From 056365635b9c0550b36825aa6667b8bd1c69a2e7 Mon Sep 17 00:00:00 2001 From: David Simansky Date: Tue, 7 Jun 2022 05:35:11 +0200 Subject: [PATCH] [release-v1.2.0] Fix kn-plugin-func inlining (#1072) --- go.sum | 36 + pkg/kn/root/plugin_register.go | 2 +- .../github.com/AlecAivazis/survey/v2/LICENSE | 21 + .../survey/v2/terminal/LICENSE.txt | 22 + .../github.com/BurntSushi/toml/COPYING | 21 + .../github.com/Masterminds/semver/LICENSE.txt | 19 + .../github.com/apex/log/LICENSE | 22 + .../github.com/buildpacks/imgutil/LICENSE | 201 + .../github.com/buildpacks/lifecycle/LICENSE | 202 + .../github.com/buildpacks/pack/LICENSE | 201 + .../github.com/containerd/containerd/LICENSE | 191 + .../github.com/containerd/containerd/NOTICE | 16 + .../stargz-snapshotter/estargz}/LICENSE | 0 .../github.com/containers/image/v5/LICENSE | 189 + .../github.com/containers/storage/pkg/LICENSE | 191 + .../github.com/containers/storage/pkg/NOTICE | 19 + .../github.com/dgraph-io/ristretto/LICENSE | 176 + .../github.com/dgraph-io/ristretto/z/LICENSE | 64 + .../github.com/docker/cli/cli/LICENSE | 191 + .../github.com/docker/cli/cli/NOTICE | 19 + .../github.com/docker/distribution/LICENSE | 202 + .../docker/docker-credential-helpers/LICENSE | 20 + .../github.com/docker/docker/LICENSE | 191 + .../github.com/docker/docker/NOTICE | 19 + .../github.com/docker/go-connections/LICENSE | 191 + .../github.com/docker/go-units/LICENSE | 191 + .../github.com/dustin/go-humanize/LICENSE | 21 + .../github.com/emicklei/go-restful/LICENSE | 22 + .../github.com/fatih/color/LICENSE.md | 20 + .../github.com/gdamore/encoding/LICENSE | 202 + .../github.com/gdamore/tcell/v2/LICENSE | 202 + .../github.com/golang/glog/LICENSE | 191 + .../github.com/google/cel-go/LICENSE | 233 + .../{pkg/name => }/LICENSE | 0 .../github.com/hako/durafmt/LICENSE | 21 + .../github.com/hashicorp/errwrap/LICENSE | 354 + .../github.com/hashicorp/errwrap/README.md | 89 + .../github.com/hashicorp/errwrap/errwrap.go | 178 + .../github.com/hashicorp/errwrap/go.mod | 1 + .../hashicorp/go-multierror/LICENSE | 353 + .../hashicorp/go-multierror/Makefile | 31 + .../hashicorp/go-multierror/README.md | 150 + .../hashicorp/go-multierror/append.go | 43 + .../hashicorp/go-multierror/flatten.go | 26 + .../hashicorp/go-multierror/format.go | 27 + .../github.com/hashicorp/go-multierror/go.mod | 5 + .../github.com/hashicorp/go-multierror/go.sum | 2 + .../hashicorp/go-multierror/group.go | 38 + .../hashicorp/go-multierror/multierror.go | 121 + .../hashicorp/go-multierror/prefix.go | 37 + .../hashicorp/go-multierror/sort.go | 16 + .../github.com/heroku/color/LICENSE | 21 + .../github.com/jonboulle/clockwork/LICENSE | 201 + .../github.com/kballard/go-shellquote/LICENSE | 19 + .../lucasb-eyer/go-colorful/LICENSE | 7 + .../github.com/mattn/go-colorable/LICENSE | 21 + .../github.com/mattn/go-isatty/LICENSE | 9 + .../github.com/mattn/go-runewidth/LICENSE | 21 + .../github.com/mgutz/ansi/LICENSE | 9 + .../github.com/mitchellh/ioprogress/LICENSE | 21 + .../github.com/moby/spdystream/LICENSE | 202 + .../github.com/moby/spdystream/NOTICE | 5 + .../github.com/moby/sys/mount/LICENSE | 202 + .../github.com/moby/sys/mountinfo/LICENSE | 202 + .../github.com/moby/term/LICENSE | 191 + .../github.com/morikuni/aec/LICENSE | 21 + .../opencontainers/go-digest/LICENSE | 192 + .../image-spec/specs-go/LICENSE | 191 + .../runc/libcontainer/user/LICENSE | 191 + .../runc/libcontainer/user/NOTICE | 17 + .../runtime-spec/specs-go/LICENSE | 191 + .../github.com/opencontainers/selinux/LICENSE | 201 + .../openshift/source-to-image/pkg/LICENSE | 202 + .../github.com/ory/viper/LICENSE | 21 + .../github.com/rivo/tview/LICENSE.txt | 21 + .../github.com/rivo/uniseg/LICENSE.txt | 21 + .../github.com/sabhiram/go-gitignore/LICENSE | 22 + .../github.com/sirupsen/logrus/LICENSE | 21 + .../github.com/src-d/gcfg/LICENSE | 28 + .../github.com/stoewer/go-strcase/LICENSE | 21 + .../syndtr/gocapability/capability/LICENSE | 24 + .../github.com/tektoncd/cli/pkg/LICENSE | 202 + .../github.com/tektoncd/pipeline/pkg/LICENSE | 202 + .../github.com/tektoncd/triggers/pkg/LICENSE | 201 + .../vbatts/tar-split/archive/tar/LICENSE | 28 + .../golang.org/x/mod/semver/LICENSE | 27 + .../gopkg.in/src-d/go-billy.v4/LICENSE | 201 + .../gopkg.in/src-d/go-git.v4/LICENSE | 201 + .../k8s.io/kube-openapi/pkg/LICENSE | 202 + .../AlecAivazis/survey/v2/.travis.yml | 22 + .../AlecAivazis/survey/v2/CONTRIBUTING.md | 77 + .../AlecAivazis/survey/v2/Gopkg.lock | 78 + .../AlecAivazis/survey/v2/Gopkg.toml | 54 + .../github.com/AlecAivazis/survey/v2/LICENSE | 21 + .../AlecAivazis/survey/v2/README.md | 483 + .../AlecAivazis/survey/v2/_tasks.hcl | 19 + .../AlecAivazis/survey/v2/confirm.go | 153 + .../AlecAivazis/survey/v2/core/template.go | 91 + .../AlecAivazis/survey/v2/core/write.go | 374 + .../AlecAivazis/survey/v2/editor.go | 222 + .../AlecAivazis/survey/v2/filter.go | 1 + .../github.com/AlecAivazis/survey/v2/go.mod | 19 + .../github.com/AlecAivazis/survey/v2/go.sum | 35 + .../github.com/AlecAivazis/survey/v2/input.go | 225 + .../AlecAivazis/survey/v2/multiline.go | 110 + .../AlecAivazis/survey/v2/multiselect.go | 346 + .../AlecAivazis/survey/v2/password.go | 101 + .../AlecAivazis/survey/v2/renderer.go | 192 + .../AlecAivazis/survey/v2/select.go | 347 + .../AlecAivazis/survey/v2/survey.go | 454 + .../survey/v2/terminal/LICENSE.txt | 22 + .../AlecAivazis/survey/v2/terminal/README.md | 3 + .../survey/v2/terminal/buffered_reader.go | 22 + .../AlecAivazis/survey/v2/terminal/cursor.go | 190 + .../survey/v2/terminal/cursor_windows.go | 138 + .../AlecAivazis/survey/v2/terminal/display.go | 9 + .../survey/v2/terminal/display_posix.go | 11 + .../survey/v2/terminal/display_windows.go | 27 + .../AlecAivazis/survey/v2/terminal/error.go | 9 + .../AlecAivazis/survey/v2/terminal/output.go | 19 + .../survey/v2/terminal/output_windows.go | 227 + .../survey/v2/terminal/runereader.go | 373 + .../survey/v2/terminal/runereader_bsd.go | 13 + .../survey/v2/terminal/runereader_linux.go | 13 + .../survey/v2/terminal/runereader_posix.go | 126 + .../survey/v2/terminal/runereader_ppc64le.go | 7 + .../survey/v2/terminal/runereader_windows.go | 142 + .../survey/v2/terminal/sequences.go | 31 + .../AlecAivazis/survey/v2/terminal/stdio.go | 24 + .../survey/v2/terminal/syscall_windows.go | 39 + .../survey/v2/terminal/terminal.go | 8 + .../AlecAivazis/survey/v2/transform.go | 79 + .../AlecAivazis/survey/v2/validate.go | 127 + vendor/github.com/Azure/go-ansiterm/LICENSE | 21 + vendor/github.com/Azure/go-ansiterm/README.md | 12 + .../github.com/Azure/go-ansiterm/constants.go | 188 + .../github.com/Azure/go-ansiterm/context.go | 7 + .../Azure/go-ansiterm/csi_entry_state.go | 49 + .../Azure/go-ansiterm/csi_param_state.go | 38 + .../go-ansiterm/escape_intermediate_state.go | 36 + .../Azure/go-ansiterm/escape_state.go | 47 + .../Azure/go-ansiterm/event_handler.go | 90 + vendor/github.com/Azure/go-ansiterm/go.mod | 5 + vendor/github.com/Azure/go-ansiterm/go.sum | 2 + .../Azure/go-ansiterm/ground_state.go | 24 + .../Azure/go-ansiterm/osc_string_state.go | 31 + vendor/github.com/Azure/go-ansiterm/parser.go | 151 + .../go-ansiterm/parser_action_helpers.go | 99 + .../Azure/go-ansiterm/parser_actions.go | 119 + vendor/github.com/Azure/go-ansiterm/states.go | 71 + .../github.com/Azure/go-ansiterm/utilities.go | 21 + .../Azure/go-ansiterm/winterm/ansi.go | 196 + .../Azure/go-ansiterm/winterm/api.go | 327 + .../go-ansiterm/winterm/attr_translation.go | 100 + .../go-ansiterm/winterm/cursor_helpers.go | 101 + .../go-ansiterm/winterm/erase_helpers.go | 84 + .../go-ansiterm/winterm/scroll_helper.go | 118 + .../Azure/go-ansiterm/winterm/utilities.go | 9 + .../go-ansiterm/winterm/win_event_handler.go | 743 + vendor/github.com/BurntSushi/toml/.gitignore | 2 + vendor/github.com/BurntSushi/toml/COMPATIBLE | 1 + vendor/github.com/BurntSushi/toml/COPYING | 21 + vendor/github.com/BurntSushi/toml/README.md | 211 + vendor/github.com/BurntSushi/toml/decode.go | 560 + .../BurntSushi/toml/decode_go116.go | 19 + .../github.com/BurntSushi/toml/deprecated.go | 21 + vendor/github.com/BurntSushi/toml/doc.go | 13 + vendor/github.com/BurntSushi/toml/encode.go | 694 + vendor/github.com/BurntSushi/toml/error.go | 229 + vendor/github.com/BurntSushi/toml/go.mod | 3 + .../github.com/BurntSushi/toml/internal/tz.go | 36 + vendor/github.com/BurntSushi/toml/lex.go | 1219 ++ vendor/github.com/BurntSushi/toml/meta.go | 120 + vendor/github.com/BurntSushi/toml/parse.go | 763 ++ .../github.com/BurntSushi/toml/type_fields.go | 242 + .../github.com/BurntSushi/toml/type_toml.go | 70 + .../github.com/Masterminds/semver/.travis.yml | 29 + .../Masterminds/semver/CHANGELOG.md | 109 + .../github.com/Masterminds/semver/LICENSE.txt | 19 + vendor/github.com/Masterminds/semver/Makefile | 36 + .../github.com/Masterminds/semver/README.md | 194 + .../Masterminds/semver/appveyor.yml | 44 + .../Masterminds/semver/collection.go | 24 + .../Masterminds/semver/constraints.go | 423 + vendor/github.com/Masterminds/semver/doc.go | 115 + .../github.com/Masterminds/semver/version.go | 425 + .../Masterminds/semver/version_fuzz.go | 10 + .../pkg/security/grantvmgroupaccess.go | 160 + .../go-winio/pkg/security/syscall_windows.go | 7 + .../go-winio/pkg/security/zsyscall_windows.go | 70 + .../github.com/Microsoft/go-winio/vhd/vhd.go | 350 + .../Microsoft/go-winio/vhd/zvhd_windows.go | 106 + .../Microsoft/hcsshim/.gitattributes | 1 + .../github.com/Microsoft/hcsshim/.gitignore | 38 + .../Microsoft/hcsshim/.golangci.yml | 99 + .../github.com/Microsoft/hcsshim/CODEOWNERS | 1 + vendor/github.com/Microsoft/hcsshim/LICENSE | 21 + vendor/github.com/Microsoft/hcsshim/Makefile | 87 + .../Microsoft/hcsshim/Protobuild.toml | 49 + vendor/github.com/Microsoft/hcsshim/README.md | 120 + .../hcsshim/computestorage/attach.go | 38 + .../hcsshim/computestorage/destroy.go | 26 + .../hcsshim/computestorage/detach.go | 26 + .../hcsshim/computestorage/export.go | 46 + .../hcsshim/computestorage/format.go | 26 + .../hcsshim/computestorage/helpers.go | 193 + .../hcsshim/computestorage/import.go | 41 + .../hcsshim/computestorage/initialize.go | 38 + .../Microsoft/hcsshim/computestorage/mount.go | 27 + .../Microsoft/hcsshim/computestorage/setup.go | 74 + .../hcsshim/computestorage/storage.go | 50 + .../computestorage/zsyscall_windows.go | 319 + .../github.com/Microsoft/hcsshim/container.go | 223 + vendor/github.com/Microsoft/hcsshim/errors.go | 245 + .../Microsoft/hcsshim/functional_tests.ps1 | 12 + vendor/github.com/Microsoft/hcsshim/go.mod | 39 + vendor/github.com/Microsoft/hcsshim/go.sum | 993 ++ .../github.com/Microsoft/hcsshim/hcsshim.go | 28 + .../Microsoft/hcsshim/hnsendpoint.go | 118 + .../Microsoft/hcsshim/hnsglobals.go | 16 + .../Microsoft/hcsshim/hnsnetwork.go | 36 + .../github.com/Microsoft/hcsshim/hnspolicy.go | 60 + .../Microsoft/hcsshim/hnspolicylist.go | 47 + .../Microsoft/hcsshim/hnssupport.go | 13 + .../github.com/Microsoft/hcsshim/interface.go | 114 + .../Microsoft/hcsshim/internal/cow/cow.go | 91 + .../hcsshim/internal/hcs/callback.go | 161 + .../Microsoft/hcsshim/internal/hcs/errors.go | 343 + .../Microsoft/hcsshim/internal/hcs/process.go | 557 + .../hcsshim/internal/hcs/schema1/schema1.go | 250 + .../internal/hcs/schema2/attachment.go | 36 + .../hcsshim/internal/hcs/schema2/battery.go | 13 + .../hcs/schema2/cache_query_stats_response.go | 18 + .../hcsshim/internal/hcs/schema2/chipset.go | 27 + .../internal/hcs/schema2/close_handle.go | 14 + .../hcsshim/internal/hcs/schema2/com_port.go | 17 + .../internal/hcs/schema2/compute_system.go | 26 + .../internal/hcs/schema2/configuration.go | 72 + .../internal/hcs/schema2/console_size.go | 16 + .../hcsshim/internal/hcs/schema2/container.go | 36 + ...r_credential_guard_add_instance_request.go | 16 + ...edential_guard_hv_socket_service_config.go | 15 + .../container_credential_guard_instance.go | 16 + ...ainer_credential_guard_modify_operation.go | 17 + ...iner_credential_guard_operation_request.go | 15 + ...redential_guard_remove_instance_request.go | 14 + .../container_credential_guard_state.go | 25 + .../container_credential_guard_system_info.go | 14 + .../schema2/container_memory_information.go | 25 + .../hcsshim/internal/hcs/schema2/cpu_group.go | 15 + .../hcs/schema2/cpu_group_affinity.go | 15 + .../internal/hcs/schema2/cpu_group_config.go | 18 + .../hcs/schema2/cpu_group_configurations.go | 15 + .../hcs/schema2/cpu_group_operations.go | 18 + .../hcs/schema2/cpu_group_property.go | 15 + .../hcs/schema2/create_group_operation.go | 17 + .../hcs/schema2/delete_group_operation.go | 15 + .../hcsshim/internal/hcs/schema2/device.go | 27 + .../hcsshim/internal/hcs/schema2/devices.go | 46 + .../hcs/schema2/enhanced_mode_video.go | 14 + .../hcs/schema2/flexible_io_device.go | 18 + .../internal/hcs/schema2/guest_connection.go | 19 + .../hcs/schema2/guest_connection_info.go | 21 + .../hcs/schema2/guest_crash_reporting.go | 14 + .../hcsshim/internal/hcs/schema2/guest_os.go | 14 + .../internal/hcs/schema2/guest_state.go | 22 + .../schema2/host_processor_modify_request.go | 16 + .../internal/hcs/schema2/hosted_system.go | 16 + .../hcsshim/internal/hcs/schema2/hv_socket.go | 16 + .../internal/hcs/schema2/hv_socket_2.go | 15 + .../internal/hcs/schema2/hv_socket_address.go | 17 + .../hcs/schema2/hv_socket_service_config.go | 28 + .../hcs/schema2/hv_socket_system_config.go | 22 + .../hcs/schema2/interrupt_moderation_mode.go | 42 + .../internal/hcs/schema2/iov_settings.go | 22 + .../hcsshim/internal/hcs/schema2/keyboard.go | 13 + .../hcsshim/internal/hcs/schema2/layer.go | 21 + .../hcs/schema2/linux_kernel_direct.go | 18 + .../internal/hcs/schema2/logical_processor.go | 18 + .../internal/hcs/schema2/mapped_directory.go | 20 + .../internal/hcs/schema2/mapped_pipe.go | 18 + .../hcsshim/internal/hcs/schema2/memory.go | 14 + .../hcsshim/internal/hcs/schema2/memory_2.go | 49 + .../hcs/schema2/memory_information_for_vm.go | 18 + .../internal/hcs/schema2/memory_stats.go | 19 + .../model_container_definition_device.go | 14 + .../hcs/schema2/model_device_category.go | 15 + .../hcs/schema2/model_device_extension.go | 15 + .../hcs/schema2/model_device_instance.go | 17 + .../hcs/schema2/model_device_namespace.go | 16 + .../hcs/schema2/model_interface_class.go | 16 + .../internal/hcs/schema2/model_namespace.go | 15 + .../hcs/schema2/model_object_directory.go | 18 + .../hcs/schema2/model_object_namespace.go | 16 + .../hcs/schema2/model_object_symlink.go | 18 + .../hcs/schema2/modification_request.go | 15 + .../hcs/schema2/modify_setting_request.go | 20 + .../hcsshim/internal/hcs/schema2/mouse.go | 13 + .../internal/hcs/schema2/network_adapter.go | 17 + .../internal/hcs/schema2/networking.go | 23 + .../hcs/schema2/pause_notification.go | 15 + .../internal/hcs/schema2/pause_options.go | 17 + .../hcsshim/internal/hcs/schema2/plan9.go | 14 + .../internal/hcs/schema2/plan9_share.go | 34 + .../internal/hcs/schema2/process_details.go | 33 + .../hcs/schema2/process_modify_request.go | 19 + .../hcs/schema2/process_parameters.go | 46 + .../internal/hcs/schema2/process_status.go | 21 + .../hcsshim/internal/hcs/schema2/processor.go | 18 + .../internal/hcs/schema2/processor_2.go | 23 + .../internal/hcs/schema2/processor_stats.go | 19 + .../hcs/schema2/processor_topology.go | 15 + .../internal/hcs/schema2/properties.go | 54 + .../internal/hcs/schema2/property_query.go | 15 + .../internal/hcs/schema2/property_type.go | 26 + .../hcs/schema2/rdp_connection_options.go | 16 + .../internal/hcs/schema2/registry_changes.go | 16 + .../internal/hcs/schema2/registry_key.go | 18 + .../internal/hcs/schema2/registry_value.go | 30 + .../internal/hcs/schema2/restore_state.go | 19 + .../internal/hcs/schema2/save_options.go | 19 + .../hcsshim/internal/hcs/schema2/scsi.go | 16 + .../hcs/schema2/service_properties.go | 18 + .../schema2/shared_memory_configuration.go | 14 + .../hcs/schema2/shared_memory_region.go | 22 + .../hcs/schema2/shared_memory_region_info.go | 16 + .../internal/hcs/schema2/silo_properties.go | 17 + .../internal/hcs/schema2/statistics.go | 29 + .../hcsshim/internal/hcs/schema2/storage.go | 21 + .../internal/hcs/schema2/storage_qo_s.go | 16 + .../internal/hcs/schema2/storage_stats.go | 21 + .../hcsshim/internal/hcs/schema2/topology.go | 16 + .../hcsshim/internal/hcs/schema2/uefi.go | 20 + .../internal/hcs/schema2/uefi_boot_entry.go | 22 + .../hcsshim/internal/hcs/schema2/version.go | 16 + .../internal/hcs/schema2/video_monitor.go | 18 + .../internal/hcs/schema2/virtual_machine.go | 32 + .../internal/hcs/schema2/virtual_node_info.go | 20 + .../hcs/schema2/virtual_p_mem_controller.go | 20 + .../hcs/schema2/virtual_p_mem_device.go | 18 + .../hcs/schema2/virtual_p_mem_mapping.go | 15 + .../hcs/schema2/virtual_pci_device.go | 16 + .../hcs/schema2/virtual_pci_function.go | 18 + .../internal/hcs/schema2/virtual_smb.go | 16 + .../internal/hcs/schema2/virtual_smb_share.go | 20 + .../hcs/schema2/virtual_smb_share_options.go | 62 + .../hcsshim/internal/hcs/schema2/vm_memory.go | 26 + .../hcs/schema2/vm_processor_limits.go | 22 + .../hcs/schema2/windows_crash_reporting.go | 16 + .../Microsoft/hcsshim/internal/hcs/service.go | 49 + .../Microsoft/hcsshim/internal/hcs/system.go | 637 + .../Microsoft/hcsshim/internal/hcs/utils.go | 62 + .../hcsshim/internal/hcs/waithelper.go | 68 + .../hcsshim/internal/hcserror/hcserror.go | 47 + .../Microsoft/hcsshim/internal/hns/hns.go | 23 + .../hcsshim/internal/hns/hnsendpoint.go | 338 + .../hcsshim/internal/hns/hnsfuncs.go | 49 + .../hcsshim/internal/hns/hnsglobals.go | 28 + .../hcsshim/internal/hns/hnsnetwork.go | 141 + .../hcsshim/internal/hns/hnspolicy.go | 109 + .../hcsshim/internal/hns/hnspolicylist.go | 201 + .../hcsshim/internal/hns/hnssupport.go | 49 + .../hcsshim/internal/hns/namespace.go | 111 + .../hcsshim/internal/hns/zsyscall_windows.go | 76 + .../hcsshim/internal/interop/interop.go | 23 + .../internal/interop/zsyscall_windows.go | 48 + .../Microsoft/hcsshim/internal/log/g.go | 23 + .../hcsshim/internal/logfields/fields.go | 32 + .../hcsshim/internal/longpath/longpath.go | 24 + .../hcsshim/internal/mergemaps/merge.go | 52 + .../Microsoft/hcsshim/internal/oc/exporter.go | 43 + .../Microsoft/hcsshim/internal/oc/span.go | 17 + .../hcsshim/internal/safefile/safeopen.go | 375 + .../hcsshim/internal/timeout/timeout.go | 74 + .../hcsshim/internal/vmcompute/vmcompute.go | 610 + .../internal/vmcompute/zsyscall_windows.go | 581 + .../hcsshim/internal/wclayer/activatelayer.go | 27 + .../hcsshim/internal/wclayer/baselayer.go | 182 + .../hcsshim/internal/wclayer/createlayer.go | 27 + .../internal/wclayer/createscratchlayer.go | 34 + .../internal/wclayer/deactivatelayer.go | 24 + .../hcsshim/internal/wclayer/destroylayer.go | 25 + .../internal/wclayer/expandscratchsize.go | 140 + .../hcsshim/internal/wclayer/exportlayer.go | 94 + .../internal/wclayer/getlayermountpath.go | 50 + .../internal/wclayer/getsharedbaseimages.go | 29 + .../hcsshim/internal/wclayer/grantvmaccess.go | 26 + .../hcsshim/internal/wclayer/importlayer.go | 166 + .../hcsshim/internal/wclayer/layerexists.go | 28 + .../hcsshim/internal/wclayer/layerid.go | 22 + .../hcsshim/internal/wclayer/layerutils.go | 97 + .../hcsshim/internal/wclayer/legacy.go | 811 ++ .../hcsshim/internal/wclayer/nametoguid.go | 29 + .../hcsshim/internal/wclayer/preparelayer.go | 44 + .../hcsshim/internal/wclayer/processimage.go | 41 + .../internal/wclayer/unpreparelayer.go | 25 + .../hcsshim/internal/wclayer/wclayer.go | 35 + .../internal/wclayer/zsyscall_windows.go | 569 + .../hcsshim/internal/winapi/console.go | 44 + .../hcsshim/internal/winapi/devices.go | 13 + .../hcsshim/internal/winapi/errors.go | 15 + .../hcsshim/internal/winapi/filesystem.go | 110 + .../Microsoft/hcsshim/internal/winapi/iocp.go | 3 + .../hcsshim/internal/winapi/jobobject.go | 215 + .../hcsshim/internal/winapi/logon.go | 30 + .../hcsshim/internal/winapi/memory.go | 4 + .../Microsoft/hcsshim/internal/winapi/net.go | 3 + .../Microsoft/hcsshim/internal/winapi/path.go | 11 + .../hcsshim/internal/winapi/process.go | 8 + .../hcsshim/internal/winapi/processor.go | 7 + .../hcsshim/internal/winapi/system.go | 52 + .../hcsshim/internal/winapi/thread.go | 12 + .../hcsshim/internal/winapi/utils.go | 80 + .../hcsshim/internal/winapi/winapi.go | 5 + .../internal/winapi/zsyscall_windows.go | 360 + vendor/github.com/Microsoft/hcsshim/layer.go | 107 + .../hcsshim/osversion/osversion_windows.go | 50 + .../hcsshim/osversion/windowsbuilds.go | 50 + .../github.com/Microsoft/hcsshim/process.go | 98 + .../Microsoft/hcsshim/zsyscall_windows.go | 54 + vendor/github.com/apex/log/.gitignore | 1 + vendor/github.com/apex/log/History.md | 75 + vendor/github.com/apex/log/LICENSE | 22 + vendor/github.com/apex/log/Makefile | 2 + vendor/github.com/apex/log/Readme.md | 61 + vendor/github.com/apex/log/context.go | 19 + vendor/github.com/apex/log/default.go | 45 + vendor/github.com/apex/log/doc.go | 10 + vendor/github.com/apex/log/entry.go | 182 + vendor/github.com/apex/log/go.mod | 32 + vendor/github.com/apex/log/go.sum | 117 + vendor/github.com/apex/log/interface.go | 22 + vendor/github.com/apex/log/levels.go | 81 + vendor/github.com/apex/log/logger.go | 156 + vendor/github.com/apex/log/pkg.go | 108 + vendor/github.com/apex/log/stack.go | 8 + .../github.com/buildpacks/imgutil/.gitignore | 18 + .../github.com/buildpacks/imgutil/CODEOWNERS | 1 + vendor/github.com/buildpacks/imgutil/LICENSE | 201 + vendor/github.com/buildpacks/imgutil/Makefile | 42 + .../github.com/buildpacks/imgutil/README.md | 19 + vendor/github.com/buildpacks/imgutil/go.mod | 19 + vendor/github.com/buildpacks/imgutil/go.sum | 1276 ++ .../buildpacks/imgutil/golangci.yaml | 32 + vendor/github.com/buildpacks/imgutil/image.go | 74 + .../imgutil/layer/bcdhive_generated.go | 31 + .../imgutil/layer/pax_permissions.go | 12 + .../imgutil/layer/windows_baselayer.go | 84 + .../imgutil/layer/windows_writer.go | 130 + .../buildpacks/imgutil/local/identifier.go | 9 + .../buildpacks/imgutil/local/local.go | 885 ++ .../buildpacks/imgutil/remote/identifier.go | 13 + .../buildpacks/imgutil/remote/remote.go | 734 + .../buildpacks/lifecycle/.dockerignore | 1 + .../buildpacks/lifecycle/.gitignore | 10 + .../buildpacks/lifecycle/.gitpod.yml | 19 + .../buildpacks/lifecycle/CODEOWNERS | 1 + .../buildpacks/lifecycle/CONTRIBUTING.md | 42 + .../buildpacks/lifecycle/DEVELOPMENT.md | 92 + .../github.com/buildpacks/lifecycle/IMAGE.md | 39 + .../github.com/buildpacks/lifecycle/LICENSE | 202 + .../github.com/buildpacks/lifecycle/Makefile | 315 + .../github.com/buildpacks/lifecycle/README.md | 64 + .../buildpacks/lifecycle/RELEASE.md | 22 + .../buildpacks/lifecycle/analyzer.go | 126 + .../buildpacks/lifecycle/api/apis.go | 100 + .../buildpacks/lifecycle/api/version.go | 129 + .../buildpacks/lifecycle/archive/archive.go | 72 + .../buildpacks/lifecycle/archive/extract.go | 117 + .../buildpacks/lifecycle/archive/reader.go | 77 + .../buildpacks/lifecycle/archive/tar_unix.go | 22 + .../lifecycle/archive/tar_windows.go | 56 + .../buildpacks/lifecycle/archive/writer.go | 71 + .../buildpacks/lifecycle/auth/env_keychain.go | 196 + .../buildpacks/lifecycle/builder.go | 281 + .../buildpacks/lifecycle/buildpack/bom.go | 114 + .../buildpacks/lifecycle/buildpack/bomfile.go | 173 + .../buildpacks/lifecycle/buildpack/build.go | 393 + .../lifecycle/buildpack/descriptor.go | 89 + .../buildpacks/lifecycle/buildpack/detect.go | 117 + .../buildpacks/lifecycle/buildpack/error.go | 26 + .../buildpacks/lifecycle/buildpack/files.go | 205 + .../lifecycle/buildpack/layermetadata.go | 136 + .../buildpacks/lifecycle/buildpack/layers.go | 199 + .../buildpacks/lifecycle/buildpack/store.go | 38 + .../github.com/buildpacks/lifecycle/cache.go | 146 + .../buildpacks/lifecycle/cmd/command.go | 62 + .../buildpacks/lifecycle/cmd/exit.go | 67 + .../buildpacks/lifecycle/cmd/flags.go | 262 + .../buildpacks/lifecycle/cmd/flags_unix.go | 9 + .../buildpacks/lifecycle/cmd/flags_windows.go | 6 + .../buildpacks/lifecycle/cmd/logs.go | 87 + .../buildpacks/lifecycle/cmd/version.go | 106 + .../buildpacks/lifecycle/codecov.yml | 18 + .../buildpacks/lifecycle/cosign.pub | 5 + .../buildpacks/lifecycle/detector.go | 415 + .../buildpacks/lifecycle/env/build.go | 79 + .../buildpacks/lifecycle/env/env.go | 195 + .../buildpacks/lifecycle/env/launch.go | 51 + .../buildpacks/lifecycle/env/vars.go | 59 + .../buildpacks/lifecycle/exporter.go | 506 + vendor/github.com/buildpacks/lifecycle/go.mod | 18 + vendor/github.com/buildpacks/lifecycle/go.sum | 1430 ++ .../buildpacks/lifecycle/golangci.yaml | 45 + .../buildpacks/lifecycle/image/image.go | 69 + .../buildpacks/lifecycle/image/utils.go | 64 + .../lifecycle/internal/encoding/utils.go | 20 + .../buildpacks/lifecycle/internal/io/utils.go | 27 + .../lifecycle/internal/layer/logger.go | 15 + .../internal/layer/metadata_restorer.go | 190 + .../lifecycle/internal/layer/sbom_restorer.go | 131 + .../lifecycle/internal/str/utils.go | 33 + .../buildpacks/lifecycle/launch/bash.go | 59 + .../buildpacks/lifecycle/launch/cmd.go | 25 + .../buildpacks/lifecycle/launch/exec_d.go | 64 + .../lifecycle/launch/exec_d_unix.go | 14 + .../lifecycle/launch/exec_d_windows.go | 21 + .../buildpacks/lifecycle/launch/launch.go | 53 + .../buildpacks/lifecycle/launch/launcher.go | 209 + .../lifecycle/launch/launcher_unix.go | 17 + .../lifecycle/launch/launcher_windows.go | 25 + .../buildpacks/lifecycle/launch/process.go | 77 + .../buildpacks/lifecycle/launch/shell.go | 126 + .../buildpacks/lifecycle/layers/digest.go | 46 + .../buildpacks/lifecycle/layers/dir.go | 29 + .../buildpacks/lifecycle/layers/extract.go | 33 + .../buildpacks/lifecycle/layers/factory.go | 99 + .../buildpacks/lifecycle/layers/launcher.go | 125 + .../buildpacks/lifecycle/layers/slices.go | 251 + .../buildpacks/lifecycle/layers/writer.go | 46 + .../buildpacks/lifecycle/lifecycle.toml | 16 + .../github.com/buildpacks/lifecycle/logger.go | 15 + .../buildpacks/lifecycle/platform/cache.go | 17 + .../buildpacks/lifecycle/platform/exit.go | 93 + .../buildpacks/lifecycle/platform/files.go | 234 + .../buildpacks/lifecycle/platform/labels.go | 9 + .../buildpacks/lifecycle/platform/platform.go | 27 + .../buildpacks/lifecycle/rebaser.go | 128 + .../buildpacks/lifecycle/restorer.go | 135 + .../github.com/buildpacks/lifecycle/save.go | 94 + .../github.com/buildpacks/lifecycle/utils.go | 58 + vendor/github.com/buildpacks/pack/.gitignore | 16 + vendor/github.com/buildpacks/pack/.gitpod.yml | 18 + vendor/github.com/buildpacks/pack/CODEOWNERS | 1 + .../buildpacks/pack/CONTRIBUTING.md | 84 + .../github.com/buildpacks/pack/DEVELOPMENT.md | 114 + vendor/github.com/buildpacks/pack/LICENSE | 201 + vendor/github.com/buildpacks/pack/Makefile | 158 + vendor/github.com/buildpacks/pack/README.md | 45 + vendor/github.com/buildpacks/pack/RELEASE.md | 66 + .../buildpacks/pack/builder/config_reader.go | 113 + .../pack/builder/detection_order.go | 18 + .../github.com/buildpacks/pack/buildpack.yml | 9 + .../pack/buildpackage/config_reader.go | 116 + vendor/github.com/buildpacks/pack/codecov.yml | 18 + vendor/github.com/buildpacks/pack/go.mod | 93 + vendor/github.com/buildpacks/pack/go.sum | 1541 +++ .../github.com/buildpacks/pack/golangci.yaml | 32 + .../pack/internal/build/container_ops.go | 315 + .../internal/build/lifecycle_execution.go | 603 + .../pack/internal/build/lifecycle_executor.go | 113 + .../pack/internal/build/mount_paths.go | 63 + .../buildpacks/pack/internal/build/phase.go | 69 + .../internal/build/phase_config_provider.go | 252 + .../pack/internal/build/phase_factory.go | 40 + .../pack/internal/builder/builder.go | 832 ++ .../pack/internal/builder/descriptor.go | 111 + .../builder/detection_order_calculator.go | 106 + .../internal/builder/image_fetcher_wrapper.go | 34 + .../pack/internal/builder/inspect.go | 158 + .../pack/internal/builder/label_manager.go | 91 + .../builder/label_manager_provider.go | 11 + .../pack/internal/builder/lifecycle.go | 121 + .../pack/internal/builder/metadata.go | 36 + .../internal/builder/suggested_builder.go | 35 + .../pack/internal/builder/version.go | 47 + .../buildpacks/pack/internal/cache/consts.go | 8 + .../pack/internal/cache/image_cache.go | 39 + .../pack/internal/cache/volume_cache.go | 52 + .../buildpacks/pack/internal/config/config.go | 131 + .../pack/internal/config/config_helpers.go | 36 + .../buildpacks/pack/internal/container/run.go | 67 + .../buildpacks/pack/internal/layer/layer.go | 11 + .../pack/internal/layer/writer_factory.go | 32 + .../buildpacks/pack/internal/name/name.go | 49 + .../buildpacks/pack/internal/paths/paths.go | 135 + .../pack/internal/registry/buildpack.go | 45 + .../buildpacks/pack/internal/registry/git.go | 31 + .../pack/internal/registry/github.go | 73 + .../pack/internal/registry/index.go | 57 + .../pack/internal/registry/registry_cache.go | 357 + .../buildpacks/pack/internal/stack/merge.go | 62 + .../buildpacks/pack/internal/stack/mixins.go | 55 + .../pack/internal/stringset/stringset.go | 37 + .../buildpacks/pack/internal/style/style.go | 62 + .../buildpacks/pack/internal/term/term.go | 25 + .../buildpacks/pack/internal/termui/branch.go | 41 + .../pack/internal/termui/dashboard.go | 212 + .../buildpacks/pack/internal/termui/detect.go | 123 + .../buildpacks/pack/internal/termui/dive.go | 291 + .../buildpacks/pack/internal/termui/logger.go | 45 + .../buildpacks/pack/internal/termui/termui.go | 211 + .../buildpacks/pack/pkg/archive/archive.go | 382 + .../pack/pkg/archive/tar_builder.go | 94 + .../buildpacks/pack/pkg/blob/blob.go | 80 + .../buildpacks/pack/pkg/blob/downloader.go | 196 + .../buildpacks/pack/pkg/buildpack/builder.go | 295 + .../pack/pkg/buildpack/buildpack.go | 242 + .../pack/pkg/buildpack/buildpackage.go | 13 + .../pack/pkg/buildpack/downloader.go | 175 + .../pack/pkg/buildpack/locator_type.go | 147 + .../pack/pkg/buildpack/oci_layout_package.go | 193 + .../buildpacks/pack/pkg/buildpack/package.go | 87 + .../pack/pkg/buildpack/parse_name.go | 60 + .../buildpacks/pack/pkg/client/build.go | 938 ++ .../buildpacks/pack/pkg/client/client.go | 275 + .../buildpacks/pack/pkg/client/common.go | 131 + .../pack/pkg/client/create_builder.go | 272 + .../pack/pkg/client/download_sbom.go | 63 + .../buildpacks/pack/pkg/client/errors.go | 25 + .../pack/pkg/client/inspect_builder.go | 106 + .../pack/pkg/client/inspect_buildpack.go | 167 + .../pack/pkg/client/inspect_image.go | 175 + .../pack/pkg/client/new_buildpack.go | 137 + .../pack/pkg/client/package_buildpack.go | 157 + .../pack/pkg/client/pull_buildpack.go | 65 + .../buildpacks/pack/pkg/client/rebase.go | 95 + .../pack/pkg/client/register_buildpack.go | 119 + .../buildpacks/pack/pkg/client/version.go | 6 + .../pack/pkg/client/yank_buildpack.go | 56 + .../buildpacks/pack/pkg/dist/buildpack.go | 39 + .../pack/pkg/dist/buildpack_descriptor.go | 60 + .../buildpacks/pack/pkg/dist/dist.go | 69 + .../buildpacks/pack/pkg/dist/distribution.go | 3 + .../buildpacks/pack/pkg/dist/image.go | 42 + .../buildpacks/pack/pkg/dist/layers.go | 45 + .../buildpacks/pack/pkg/image/fetcher.go | 187 + .../buildpacks/pack/pkg/image/pull_policy.go | 41 + .../pack/pkg/logging/logger_simple.go | 66 + .../pack/pkg/logging/logger_writers.go | 215 + .../buildpacks/pack/pkg/logging/logging.go | 66 + .../pack/pkg/logging/prefix_writer.go | 126 + .../pack/pkg/project/types/types.go | 50 + .../github.com/buildpacks/pack/project.toml | 3 + vendor/github.com/buildpacks/pack/version.go | 6 + vendor/github.com/containerd/cgroups/LICENSE | 201 + .../containerd/cgroups/stats/v1/doc.go | 17 + .../containerd/cgroups/stats/v1/metrics.pb.go | 6125 +++++++++ .../cgroups/stats/v1/metrics.pb.txt | 790 ++ .../containerd/cgroups/stats/v1/metrics.proto | 158 + .../github.com/containerd/containerd/LICENSE | 191 + .../github.com/containerd/containerd/NOTICE | 16 + .../containerd/containerd/errdefs/errors.go | 92 + .../containerd/containerd/errdefs/grpc.go | 147 + .../containerd/containerd/log/context.go | 69 + .../containerd/pkg/userns/userns_linux.go | 62 + .../pkg/userns/userns_unsupported.go | 26 + .../containerd/platforms/compare.go | 203 + .../containerd/platforms/cpuinfo.go | 131 + .../containerd/platforms/database.go | 116 + .../containerd/platforms/defaults.go | 27 + .../containerd/platforms/defaults_darwin.go | 45 + .../containerd/platforms/defaults_unix.go | 41 + .../containerd/platforms/defaults_windows.go | 91 + .../containerd/platforms/platforms.go | 261 + .../containerd/containerd/sys/epoll.go | 34 + .../containerd/containerd/sys/fds.go | 35 + .../containerd/containerd/sys/filesys_unix.go | 32 + .../containerd/sys/filesys_windows.go | 345 + .../containerd/containerd/sys/oom_linux.go | 82 + .../containerd/sys/oom_unsupported.go | 49 + .../containerd/containerd/sys/socket_unix.go | 81 + .../containerd/sys/socket_windows.go | 30 + .../containerd/sys/userns_deprecated.go | 23 + .../stargz-snapshotter/estargz/LICENSE | 202 + .../stargz-snapshotter/estargz/build.go | 628 + .../estargz/errorutil/errors.go | 40 + .../stargz-snapshotter/estargz/estargz.go | 1042 ++ .../stargz-snapshotter/estargz/go.mod | 10 + .../stargz-snapshotter/estargz/go.sum | 20 + .../stargz-snapshotter/estargz/gzip.go | 237 + .../stargz-snapshotter/estargz/testutil.go | 2009 +++ .../stargz-snapshotter/estargz/types.go | 316 + vendor/github.com/containers/image/v5/LICENSE | 189 + .../image/v5/docker/reference/README.md | 2 + .../image/v5/docker/reference/helpers.go | 42 + .../image/v5/docker/reference/normalize.go | 181 + .../image/v5/docker/reference/reference.go | 433 + .../image/v5/docker/reference/regexp.go | 143 + .../image/v5/internal/pkg/keyctl/key.go | 74 + .../image/v5/internal/pkg/keyctl/keyring.go | 118 + .../image/v5/internal/pkg/keyctl/perm.go | 34 + .../image/v5/internal/pkg/keyctl/sys_linux.go | 26 + .../image/v5/internal/rootless/rootless.go | 25 + .../v5/pkg/compression/internal/types.go | 65 + .../image/v5/pkg/compression/types/types.go | 41 + .../image/v5/pkg/docker/config/config.go | 802 ++ .../v5/pkg/docker/config/config_linux.go | 119 + .../pkg/docker/config/config_unsupported.go | 21 + .../v5/pkg/sysregistriesv2/shortnames.go | 347 + .../sysregistriesv2/system_registries_v2.go | 1000 ++ .../containers/image/v5/types/types.go | 694 + vendor/github.com/containers/storage/AUTHORS | 1523 ++ vendor/github.com/containers/storage/LICENSE | 191 + vendor/github.com/containers/storage/NOTICE | 19 + .../containers/storage/pkg/homedir/homedir.go | 52 + .../storage/pkg/homedir/homedir_others.go | 20 + .../storage/pkg/homedir/homedir_unix.go | 95 + .../storage/pkg/homedir/homedir_windows.go | 32 + .../containers/storage/pkg/idtools/idtools.go | 358 + .../storage/pkg/idtools/idtools_supported.go | 71 + .../storage/pkg/idtools/idtools_unix.go | 213 + .../pkg/idtools/idtools_unsupported.go | 11 + .../storage/pkg/idtools/idtools_windows.go | 23 + .../containers/storage/pkg/idtools/parser.go | 59 + .../storage/pkg/idtools/usergroupadd_linux.go | 164 + .../pkg/idtools/usergroupadd_unsupported.go | 12 + .../storage/pkg/idtools/utils_unix.go | 32 + .../storage/pkg/lockfile/lockfile.go | 107 + .../storage/pkg/lockfile/lockfile_unix.go | 262 + .../storage/pkg/lockfile/lockfile_windows.go | 75 + .../containers/storage/pkg/mount/flags.go | 149 + .../storage/pkg/mount/flags_linux.go | 87 + .../storage/pkg/mount/flags_unsupported.go | 31 + .../containers/storage/pkg/mount/mount.go | 110 + .../storage/pkg/mount/mounter_freebsd.go | 54 + .../storage/pkg/mount/mounter_linux.go | 74 + .../storage/pkg/mount/mounter_unsupported.go | 7 + .../containers/storage/pkg/mount/mountinfo.go | 13 + .../storage/pkg/mount/mountinfo_linux.go | 5 + .../storage/pkg/mount/sharedsubtree_linux.go | 64 + .../storage/pkg/mount/unmount_unix.go | 22 + .../storage/pkg/mount/unmount_unsupported.go | 7 + .../containers/storage/pkg/reexec/README.md | 5 + .../storage/pkg/reexec/command_linux.go | 34 + .../storage/pkg/reexec/command_unix.go | 32 + .../storage/pkg/reexec/command_unsupported.go | 20 + .../storage/pkg/reexec/command_windows.go | 34 + .../containers/storage/pkg/reexec/reexec.go | 66 + .../containers/storage/pkg/stringid/README.md | 1 + .../storage/pkg/stringid/stringid.go | 99 + .../containers/storage/pkg/system/chmod.go | 17 + .../containers/storage/pkg/system/chtimes.go | 35 + .../storage/pkg/system/chtimes_unix.go | 14 + .../storage/pkg/system/chtimes_windows.go | 28 + .../containers/storage/pkg/system/errors.go | 10 + .../containers/storage/pkg/system/exitcode.go | 33 + .../containers/storage/pkg/system/init.go | 22 + .../storage/pkg/system/init_windows.go | 17 + .../containers/storage/pkg/system/lchown.go | 20 + .../storage/pkg/system/lcow_unix.go | 8 + .../storage/pkg/system/lcow_windows.go | 6 + .../storage/pkg/system/lstat_unix.go | 20 + .../storage/pkg/system/lstat_windows.go | 14 + .../containers/storage/pkg/system/meminfo.go | 17 + .../storage/pkg/system/meminfo_linux.go | 65 + .../storage/pkg/system/meminfo_solaris.go | 129 + .../storage/pkg/system/meminfo_unsupported.go | 8 + .../storage/pkg/system/meminfo_windows.go | 45 + .../containers/storage/pkg/system/mknod.go | 22 + .../storage/pkg/system/mknod_freebsd.go | 22 + .../storage/pkg/system/mknod_windows.go | 13 + .../containers/storage/pkg/system/path.go | 21 + .../storage/pkg/system/path_unix.go | 9 + .../storage/pkg/system/path_windows.go | 33 + .../storage/pkg/system/process_unix.go | 24 + .../containers/storage/pkg/system/rm.go | 79 + .../storage/pkg/system/stat_darwin.go | 13 + .../storage/pkg/system/stat_freebsd.go | 13 + .../storage/pkg/system/stat_linux.go | 19 + .../storage/pkg/system/stat_openbsd.go | 13 + .../storage/pkg/system/stat_solaris.go | 13 + .../storage/pkg/system/stat_unix.go | 74 + .../storage/pkg/system/stat_windows.go | 63 + .../storage/pkg/system/syscall_unix.go | 25 + .../storage/pkg/system/syscall_windows.go | 127 + .../containers/storage/pkg/system/umask.go | 13 + .../storage/pkg/system/umask_windows.go | 9 + .../storage/pkg/system/utimes_freebsd.go | 24 + .../storage/pkg/system/utimes_linux.go | 25 + .../storage/pkg/system/utimes_unsupported.go | 10 + .../storage/pkg/system/xattrs_linux.go | 84 + .../storage/pkg/system/xattrs_unsupported.go | 28 + .../storage/pkg/unshare/getenv_linux_cgo.go | 22 + .../storage/pkg/unshare/getenv_linux_nocgo.go | 11 + .../containers/storage/pkg/unshare/unshare.c | 378 + .../containers/storage/pkg/unshare/unshare.go | 56 + .../storage/pkg/unshare/unshare_cgo.go | 10 + .../storage/pkg/unshare/unshare_gccgo.go | 25 + .../storage/pkg/unshare/unshare_linux.go | 605 + .../pkg/unshare/unshare_unsupported.go | 45 + .../pkg/unshare/unshare_unsupported_cgo.go | 10 + .../dgraph-io/ristretto/.deepsource.toml | 17 + .../dgraph-io/ristretto/CHANGELOG.md | 172 + vendor/github.com/dgraph-io/ristretto/LICENSE | 176 + .../github.com/dgraph-io/ristretto/README.md | 220 + .../github.com/dgraph-io/ristretto/cache.go | 719 + vendor/github.com/dgraph-io/ristretto/go.mod | 14 + vendor/github.com/dgraph-io/ristretto/go.sum | 24 + .../github.com/dgraph-io/ristretto/policy.go | 423 + vendor/github.com/dgraph-io/ristretto/ring.go | 91 + .../github.com/dgraph-io/ristretto/sketch.go | 155 + .../github.com/dgraph-io/ristretto/store.go | 242 + vendor/github.com/dgraph-io/ristretto/test.sh | 20 + vendor/github.com/dgraph-io/ristretto/ttl.go | 147 + .../github.com/dgraph-io/ristretto/z/LICENSE | 64 + .../dgraph-io/ristretto/z/README.md | 129 + .../dgraph-io/ristretto/z/allocator.go | 403 + .../dgraph-io/ristretto/z/bbloom.go | 211 + .../github.com/dgraph-io/ristretto/z/btree.go | 710 + .../dgraph-io/ristretto/z/buffer.go | 544 + .../dgraph-io/ristretto/z/calloc.go | 42 + .../dgraph-io/ristretto/z/calloc_32bit.go | 14 + .../dgraph-io/ristretto/z/calloc_64bit.go | 14 + .../dgraph-io/ristretto/z/calloc_jemalloc.go | 172 + .../ristretto/z/calloc_nojemalloc.go | 37 + .../github.com/dgraph-io/ristretto/z/file.go | 217 + .../dgraph-io/ristretto/z/file_default.go | 39 + .../dgraph-io/ristretto/z/file_linux.go | 37 + .../github.com/dgraph-io/ristretto/z/flags.go | 311 + .../dgraph-io/ristretto/z/histogram.go | 205 + .../github.com/dgraph-io/ristretto/z/mmap.go | 44 + .../dgraph-io/ristretto/z/mmap_darwin.go | 59 + .../dgraph-io/ristretto/z/mmap_linux.go | 101 + .../dgraph-io/ristretto/z/mmap_plan9.go | 44 + .../dgraph-io/ristretto/z/mmap_unix.go | 55 + .../dgraph-io/ristretto/z/mmap_windows.go | 96 + .../dgraph-io/ristretto/z/rtutil.go | 75 + .../github.com/dgraph-io/ristretto/z/rtutil.s | 0 .../dgraph-io/ristretto/z/simd/baseline.go | 127 + .../dgraph-io/ristretto/z/simd/search.go | 51 + .../dgraph-io/ristretto/z/simd/search_amd64.s | 60 + .../ristretto/z/simd/stub_search_amd64.go | 6 + vendor/github.com/dgraph-io/ristretto/z/z.go | 151 + vendor/github.com/docker/cli/AUTHORS | 771 ++ vendor/github.com/docker/cli/LICENSE | 191 + vendor/github.com/docker/cli/NOTICE | 19 + .../docker/cli/cli/config/config.go | 163 + .../docker/cli/cli/config/configfile/file.go | 415 + .../cli/cli/config/configfile/file_unix.go | 35 + .../cli/cli/config/configfile/file_windows.go | 5 + .../cli/cli/config/credentials/credentials.go | 17 + .../cli/config/credentials/default_store.go | 21 + .../credentials/default_store_darwin.go | 5 + .../config/credentials/default_store_linux.go | 13 + .../credentials/default_store_unsupported.go | 7 + .../credentials/default_store_windows.go | 5 + .../cli/cli/config/credentials/file_store.go | 81 + .../cli/config/credentials/native_store.go | 143 + .../docker/cli/cli/config/types/authconfig.go | 22 + .../cli/connhelper/commandconn/commandconn.go | 283 + .../connhelper/commandconn/pdeathsig_linux.go | 10 + .../commandconn/pdeathsig_nolinux.go | 10 + .../connhelper/commandconn/session_unix.go | 13 + .../connhelper/commandconn/session_windows.go | 8 + .../docker/cli/cli/connhelper/connhelper.go | 68 + .../docker/cli/cli/connhelper/ssh/ssh.go | 64 + vendor/github.com/docker/distribution/LICENSE | 202 + .../docker/distribution/digestset/set.go | 247 + .../docker/distribution/reference/helpers.go | 42 + .../distribution/reference/normalize.go | 199 + .../distribution/reference/reference.go | 433 + .../docker/distribution/reference/regexp.go | 143 + .../registry/api/errcode/errors.go | 267 + .../registry/api/errcode/handler.go | 40 + .../registry/api/errcode/register.go | 138 + .../registry/client/auth/challenge/addr.go | 27 + .../client/auth/challenge/authchallenge.go | 237 + .../docker/docker-credential-helpers/LICENSE | 20 + .../client/client.go | 121 + .../client/command.go | 57 + .../credentials/credentials.go | 186 + .../credentials/error.go | 102 + .../credentials/helper.go | 14 + .../credentials/version.go | 4 + vendor/github.com/docker/docker/AUTHORS | 2175 +++ vendor/github.com/docker/docker/LICENSE | 191 + vendor/github.com/docker/docker/NOTICE | 19 + vendor/github.com/docker/docker/api/README.md | 42 + vendor/github.com/docker/docker/api/common.go | 11 + .../docker/docker/api/common_unix.go | 6 + .../docker/docker/api/common_windows.go | 8 + .../docker/docker/api/swagger-gen.yaml | 12 + .../github.com/docker/docker/api/swagger.yaml | 11425 ++++++++++++++++ .../docker/docker/api/types/auth.go | 22 + .../docker/docker/api/types/blkiodev/blkio.go | 23 + .../docker/docker/api/types/client.go | 419 + .../docker/docker/api/types/configs.go | 66 + .../docker/api/types/container/config.go | 69 + .../api/types/container/container_changes.go | 20 + .../api/types/container/container_create.go | 20 + .../api/types/container/container_top.go | 22 + .../api/types/container/container_update.go | 16 + .../api/types/container/container_wait.go | 28 + .../docker/api/types/container/host_config.go | 447 + .../api/types/container/hostconfig_unix.go | 41 + .../api/types/container/hostconfig_windows.go | 40 + .../api/types/container/waitcondition.go | 22 + .../docker/docker/api/types/error_response.go | 13 + .../docker/api/types/error_response_ext.go | 6 + .../docker/docker/api/types/events/events.go | 54 + .../docker/docker/api/types/filters/parse.go | 324 + .../docker/api/types/graph_driver_data.go | 17 + .../docker/docker/api/types/id_response.go | 13 + .../docker/api/types/image/image_history.go | 36 + .../api/types/image_delete_response_item.go | 15 + .../docker/docker/api/types/image_summary.go | 49 + .../docker/docker/api/types/mount/mount.go | 131 + .../docker/api/types/network/network.go | 126 + .../docker/docker/api/types/plugin.go | 203 + .../docker/docker/api/types/plugin_device.go | 25 + .../docker/docker/api/types/plugin_env.go | 25 + .../docker/api/types/plugin_interface_type.go | 21 + .../docker/docker/api/types/plugin_mount.go | 37 + .../docker/api/types/plugin_responses.go | 71 + .../docker/docker/api/types/port.go | 23 + .../docker/api/types/registry/authenticate.go | 21 + .../docker/api/types/registry/registry.go | 119 + .../api/types/service_update_response.go | 12 + .../docker/docker/api/types/stats.go | 181 + .../docker/api/types/strslice/strslice.go | 30 + .../docker/docker/api/types/swarm/common.go | 40 + .../docker/docker/api/types/swarm/config.go | 40 + .../docker/api/types/swarm/container.go | 80 + .../docker/docker/api/types/swarm/network.go | 121 + .../docker/docker/api/types/swarm/node.go | 115 + .../docker/docker/api/types/swarm/runtime.go | 27 + .../docker/api/types/swarm/runtime/gen.go | 3 + .../api/types/swarm/runtime/plugin.pb.go | 754 + .../api/types/swarm/runtime/plugin.proto | 21 + .../docker/docker/api/types/swarm/secret.go | 36 + .../docker/docker/api/types/swarm/service.go | 202 + .../docker/docker/api/types/swarm/swarm.go | 227 + .../docker/docker/api/types/swarm/task.go | 206 + .../docker/api/types/time/duration_convert.go | 12 + .../docker/docker/api/types/time/timestamp.go | 129 + .../docker/docker/api/types/types.go | 635 + .../docker/api/types/versions/README.md | 14 + .../docker/api/types/versions/compare.go | 62 + .../docker/docker/api/types/volume.go | 72 + .../docker/api/types/volume/volume_create.go | 31 + .../docker/api/types/volume/volume_list.go | 23 + .../docker/docker/cli/config/configdir.go | 27 + .../github.com/docker/docker/client/README.md | 35 + .../docker/docker/client/build_cancel.go | 16 + .../docker/docker/client/build_prune.go | 45 + .../docker/docker/client/checkpoint_create.go | 14 + .../docker/docker/client/checkpoint_delete.go | 20 + .../docker/docker/client/checkpoint_list.go | 28 + .../github.com/docker/docker/client/client.go | 310 + .../docker/docker/client/client_deprecated.go | 23 + .../docker/docker/client/client_unix.go | 9 + .../docker/docker/client/client_windows.go | 7 + .../docker/docker/client/config_create.go | 25 + .../docker/docker/client/config_inspect.go | 36 + .../docker/docker/client/config_list.go | 38 + .../docker/docker/client/config_remove.go | 13 + .../docker/docker/client/config_update.go | 21 + .../docker/docker/client/container_attach.go | 57 + .../docker/docker/client/container_commit.go | 55 + .../docker/docker/client/container_copy.go | 103 + .../docker/docker/client/container_create.go | 63 + .../docker/docker/client/container_diff.go | 23 + .../docker/docker/client/container_exec.go | 54 + .../docker/docker/client/container_export.go | 19 + .../docker/docker/client/container_inspect.go | 53 + .../docker/docker/client/container_kill.go | 16 + .../docker/docker/client/container_list.go | 57 + .../docker/docker/client/container_logs.go | 80 + .../docker/docker/client/container_pause.go | 10 + .../docker/docker/client/container_prune.go | 36 + .../docker/docker/client/container_remove.go | 27 + .../docker/docker/client/container_rename.go | 15 + .../docker/docker/client/container_resize.go | 29 + .../docker/docker/client/container_restart.go | 22 + .../docker/docker/client/container_start.go | 23 + .../docker/docker/client/container_stats.go | 42 + .../docker/docker/client/container_stop.go | 26 + .../docker/docker/client/container_top.go | 28 + .../docker/docker/client/container_unpause.go | 10 + .../docker/docker/client/container_update.go | 21 + .../docker/docker/client/container_wait.go | 83 + .../docker/docker/client/disk_usage.go | 26 + .../docker/client/distribution_inspect.go | 38 + .../github.com/docker/docker/client/errors.go | 138 + .../github.com/docker/docker/client/events.go | 102 + .../github.com/docker/docker/client/hijack.go | 145 + .../docker/docker/client/image_build.go | 146 + .../docker/docker/client/image_create.go | 37 + .../docker/docker/client/image_history.go | 22 + .../docker/docker/client/image_import.go | 40 + .../docker/docker/client/image_inspect.go | 32 + .../docker/docker/client/image_list.go | 46 + .../docker/docker/client/image_load.go | 29 + .../docker/docker/client/image_prune.go | 36 + .../docker/docker/client/image_pull.go | 64 + .../docker/docker/client/image_push.go | 54 + .../docker/docker/client/image_remove.go | 31 + .../docker/docker/client/image_save.go | 21 + .../docker/docker/client/image_search.go | 51 + .../docker/docker/client/image_tag.go | 37 + .../github.com/docker/docker/client/info.go | 26 + .../docker/docker/client/interface.go | 201 + .../docker/client/interface_experimental.go | 18 + .../docker/docker/client/interface_stable.go | 10 + .../github.com/docker/docker/client/login.go | 25 + .../docker/docker/client/network_connect.go | 19 + .../docker/docker/client/network_create.go | 25 + .../docker/client/network_disconnect.go | 15 + .../docker/docker/client/network_inspect.go | 49 + .../docker/docker/client/network_list.go | 32 + .../docker/docker/client/network_prune.go | 36 + .../docker/docker/client/network_remove.go | 10 + .../docker/docker/client/node_inspect.go | 32 + .../docker/docker/client/node_list.go | 36 + .../docker/docker/client/node_remove.go | 20 + .../docker/docker/client/node_update.go | 18 + .../docker/docker/client/options.go | 172 + .../github.com/docker/docker/client/ping.go | 66 + .../docker/docker/client/plugin_create.go | 23 + .../docker/docker/client/plugin_disable.go | 19 + .../docker/docker/client/plugin_enable.go | 19 + .../docker/docker/client/plugin_inspect.go | 31 + .../docker/docker/client/plugin_install.go | 113 + .../docker/docker/client/plugin_list.go | 33 + .../docker/docker/client/plugin_push.go | 16 + .../docker/docker/client/plugin_remove.go | 20 + .../docker/docker/client/plugin_set.go | 12 + .../docker/docker/client/plugin_upgrade.go | 39 + .../docker/docker/client/request.go | 269 + .../docker/docker/client/secret_create.go | 25 + .../docker/docker/client/secret_inspect.go | 36 + .../docker/docker/client/secret_list.go | 38 + .../docker/docker/client/secret_remove.go | 13 + .../docker/docker/client/secret_update.go | 21 + .../docker/docker/client/service_create.go | 178 + .../docker/docker/client/service_inspect.go | 37 + .../docker/docker/client/service_list.go | 39 + .../docker/docker/client/service_logs.go | 52 + .../docker/docker/client/service_remove.go | 10 + .../docker/docker/client/service_update.go | 75 + .../docker/client/swarm_get_unlock_key.go | 21 + .../docker/docker/client/swarm_init.go | 21 + .../docker/docker/client/swarm_inspect.go | 21 + .../docker/docker/client/swarm_join.go | 14 + .../docker/docker/client/swarm_leave.go | 17 + .../docker/docker/client/swarm_unlock.go | 14 + .../docker/docker/client/swarm_update.go | 22 + .../docker/docker/client/task_inspect.go | 32 + .../docker/docker/client/task_list.go | 35 + .../docker/docker/client/task_logs.go | 51 + .../docker/docker/client/transport.go | 17 + .../github.com/docker/docker/client/utils.go | 34 + .../docker/docker/client/version.go | 21 + .../docker/docker/client/volume_create.go | 21 + .../docker/docker/client/volume_inspect.go | 38 + .../docker/docker/client/volume_list.go | 33 + .../docker/docker/client/volume_prune.go | 36 + .../docker/docker/client/volume_remove.go | 21 + .../github.com/docker/docker/errdefs/defs.go | 69 + .../github.com/docker/docker/errdefs/doc.go | 8 + .../docker/docker/errdefs/helpers.go | 279 + .../docker/docker/errdefs/http_helpers.go | 191 + vendor/github.com/docker/docker/errdefs/is.go | 107 + .../docker/docker/pkg/archive/README.md | 1 + .../docker/docker/pkg/archive/archive.go | 1322 ++ .../docker/pkg/archive/archive_linux.go | 100 + .../docker/pkg/archive/archive_other.go | 7 + .../docker/docker/pkg/archive/archive_unix.go | 115 + .../docker/pkg/archive/archive_windows.go | 67 + .../docker/docker/pkg/archive/changes.go | 445 + .../docker/pkg/archive/changes_linux.go | 286 + .../docker/pkg/archive/changes_other.go | 97 + .../docker/docker/pkg/archive/changes_unix.go | 43 + .../docker/pkg/archive/changes_windows.go | 34 + .../docker/docker/pkg/archive/copy.go | 480 + .../docker/docker/pkg/archive/copy_unix.go | 11 + .../docker/docker/pkg/archive/copy_windows.go | 9 + .../docker/docker/pkg/archive/diff.go | 260 + .../docker/docker/pkg/archive/time_linux.go | 16 + .../docker/pkg/archive/time_unsupported.go | 16 + .../docker/docker/pkg/archive/whiteouts.go | 23 + .../docker/docker/pkg/archive/wrap.go | 59 + .../docker/docker/pkg/fileutils/fileutils.go | 298 + .../docker/pkg/fileutils/fileutils_darwin.go | 27 + .../docker/pkg/fileutils/fileutils_unix.go | 22 + .../docker/pkg/fileutils/fileutils_windows.go | 7 + .../docker/pkg/homedir/homedir_linux.go | 93 + .../docker/pkg/homedir/homedir_others.go | 27 + .../docker/docker/pkg/homedir/homedir_unix.go | 38 + .../docker/pkg/homedir/homedir_windows.go | 24 + .../docker/docker/pkg/idtools/idtools.go | 241 + .../docker/docker/pkg/idtools/idtools_unix.go | 295 + .../docker/pkg/idtools/idtools_windows.go | 25 + .../docker/pkg/idtools/usergroupadd_linux.go | 164 + .../pkg/idtools/usergroupadd_unsupported.go | 12 + .../docker/docker/pkg/idtools/utils_unix.go | 31 + .../docker/docker/pkg/ioutils/buffer.go | 51 + .../docker/docker/pkg/ioutils/bytespipe.go | 187 + .../docker/docker/pkg/ioutils/fswriters.go | 162 + .../docker/docker/pkg/ioutils/readers.go | 157 + .../docker/docker/pkg/ioutils/temp_unix.go | 10 + .../docker/docker/pkg/ioutils/temp_windows.go | 16 + .../docker/docker/pkg/ioutils/writeflusher.go | 92 + .../docker/docker/pkg/ioutils/writers.go | 66 + .../docker/pkg/jsonmessage/jsonmessage.go | 283 + .../docker/docker/pkg/longpath/longpath.go | 26 + .../docker/docker/pkg/pools/pools.go | 137 + .../docker/docker/pkg/stdcopy/stdcopy.go | 190 + .../docker/docker/pkg/stringid/README.md | 1 + .../docker/docker/pkg/stringid/stringid.go | 63 + .../docker/docker/pkg/system/args_windows.go | 16 + .../docker/docker/pkg/system/chtimes.go | 31 + .../docker/pkg/system/chtimes_nowindows.go | 14 + .../docker/pkg/system/chtimes_windows.go | 26 + .../docker/docker/pkg/system/errors.go | 13 + .../docker/docker/pkg/system/exitcode.go | 19 + .../docker/docker/pkg/system/filesys_unix.go | 67 + .../docker/pkg/system/filesys_windows.go | 292 + .../docker/docker/pkg/system/init.go | 22 + .../docker/docker/pkg/system/init_windows.go | 29 + .../docker/docker/pkg/system/lcow.go | 48 + .../docker/pkg/system/lcow_unsupported.go | 28 + .../docker/docker/pkg/system/lstat_unix.go | 20 + .../docker/docker/pkg/system/lstat_windows.go | 14 + .../docker/docker/pkg/system/meminfo.go | 17 + .../docker/docker/pkg/system/meminfo_linux.go | 71 + .../docker/pkg/system/meminfo_unsupported.go | 8 + .../docker/pkg/system/meminfo_windows.go | 45 + .../docker/docker/pkg/system/mknod.go | 22 + .../docker/docker/pkg/system/mknod_windows.go | 11 + .../docker/docker/pkg/system/path.go | 64 + .../docker/docker/pkg/system/path_unix.go | 10 + .../docker/docker/pkg/system/path_windows.go | 27 + .../docker/docker/pkg/system/process_unix.go | 44 + .../docker/pkg/system/process_windows.go | 18 + .../github.com/docker/docker/pkg/system/rm.go | 78 + .../docker/docker/pkg/system/rm_windows.go | 6 + .../docker/docker/pkg/system/stat_bsd.go | 15 + .../docker/docker/pkg/system/stat_darwin.go | 13 + .../docker/docker/pkg/system/stat_linux.go | 20 + .../docker/docker/pkg/system/stat_openbsd.go | 13 + .../docker/docker/pkg/system/stat_solaris.go | 13 + .../docker/docker/pkg/system/stat_unix.go | 66 + .../docker/docker/pkg/system/stat_windows.go | 49 + .../docker/docker/pkg/system/syscall_unix.go | 11 + .../docker/pkg/system/syscall_windows.go | 136 + .../docker/docker/pkg/system/umask.go | 13 + .../docker/docker/pkg/system/umask_windows.go | 7 + .../docker/docker/pkg/system/utimes_unix.go | 24 + .../docker/pkg/system/utimes_unsupported.go | 10 + .../docker/docker/pkg/system/xattrs_linux.go | 37 + .../docker/pkg/system/xattrs_unsupported.go | 13 + .../docker/volume/mounts/lcow_parser.go | 34 + .../docker/volume/mounts/linux_parser.go | 423 + .../docker/docker/volume/mounts/mounts.go | 181 + .../docker/docker/volume/mounts/parser.go | 47 + .../docker/docker/volume/mounts/validate.go | 28 + .../docker/volume/mounts/volume_copy.go | 23 + .../docker/volume/mounts/volume_unix.go | 18 + .../docker/volume/mounts/volume_windows.go | 8 + .../docker/volume/mounts/windows_parser.go | 456 + .../github.com/docker/docker/volume/volume.go | 69 + .../github.com/docker/go-connections/LICENSE | 191 + .../docker/go-connections/nat/nat.go | 242 + .../docker/go-connections/nat/parse.go | 57 + .../docker/go-connections/nat/sort.go | 96 + .../docker/go-connections/sockets/README.md | 0 .../go-connections/sockets/inmem_socket.go | 81 + .../docker/go-connections/sockets/proxy.go | 51 + .../docker/go-connections/sockets/sockets.go | 38 + .../go-connections/sockets/sockets_unix.go | 35 + .../go-connections/sockets/sockets_windows.go | 27 + .../go-connections/sockets/tcp_socket.go | 22 + .../go-connections/sockets/unix_socket.go | 32 + .../go-connections/tlsconfig/certpool_go17.go | 18 + .../tlsconfig/certpool_other.go | 13 + .../docker/go-connections/tlsconfig/config.go | 254 + .../tlsconfig/config_client_ciphers.go | 17 + .../tlsconfig/config_legacy_client_ciphers.go | 15 + .../docker/go-units/CONTRIBUTING.md | 67 + vendor/github.com/docker/go-units/LICENSE | 191 + vendor/github.com/docker/go-units/MAINTAINERS | 46 + vendor/github.com/docker/go-units/README.md | 16 + vendor/github.com/docker/go-units/circle.yml | 11 + vendor/github.com/docker/go-units/duration.go | 35 + vendor/github.com/docker/go-units/size.go | 108 + vendor/github.com/docker/go-units/ulimit.go | 123 + .../github.com/dustin/go-humanize/.travis.yml | 21 + vendor/github.com/dustin/go-humanize/LICENSE | 21 + .../dustin/go-humanize/README.markdown | 124 + vendor/github.com/dustin/go-humanize/big.go | 31 + .../github.com/dustin/go-humanize/bigbytes.go | 173 + vendor/github.com/dustin/go-humanize/bytes.go | 143 + vendor/github.com/dustin/go-humanize/comma.go | 116 + .../github.com/dustin/go-humanize/commaf.go | 40 + vendor/github.com/dustin/go-humanize/ftoa.go | 46 + .../github.com/dustin/go-humanize/humanize.go | 8 + .../github.com/dustin/go-humanize/number.go | 192 + .../github.com/dustin/go-humanize/ordinals.go | 25 + vendor/github.com/dustin/go-humanize/si.go | 123 + vendor/github.com/dustin/go-humanize/times.go | 117 + vendor/github.com/fatih/color/LICENSE.md | 20 + vendor/github.com/fatih/color/README.md | 178 + vendor/github.com/fatih/color/color.go | 618 + vendor/github.com/fatih/color/doc.go | 135 + vendor/github.com/fatih/color/go.mod | 8 + vendor/github.com/fatih/color/go.sum | 9 + .../github.com/gdamore/encoding/.appveyor.yml | 13 + .../github.com/gdamore/encoding/.travis.yml | 7 + vendor/github.com/gdamore/encoding/LICENSE | 202 + vendor/github.com/gdamore/encoding/README.md | 19 + vendor/github.com/gdamore/encoding/ascii.go | 36 + vendor/github.com/gdamore/encoding/charmap.go | 196 + vendor/github.com/gdamore/encoding/doc.go | 17 + vendor/github.com/gdamore/encoding/ebcdic.go | 273 + vendor/github.com/gdamore/encoding/go.mod | 5 + vendor/github.com/gdamore/encoding/go.sum | 2 + vendor/github.com/gdamore/encoding/latin1.go | 33 + vendor/github.com/gdamore/encoding/latin5.go | 35 + vendor/github.com/gdamore/encoding/utf8.go | 35 + .../github.com/gdamore/tcell/v2/.appveyor.yml | 13 + vendor/github.com/gdamore/tcell/v2/.gitignore | 1 + .../github.com/gdamore/tcell/v2/.travis.yml | 18 + vendor/github.com/gdamore/tcell/v2/AUTHORS | 4 + .../github.com/gdamore/tcell/v2/CHANGESv2.md | 82 + vendor/github.com/gdamore/tcell/v2/LICENSE | 202 + vendor/github.com/gdamore/tcell/v2/README.md | 272 + .../github.com/gdamore/tcell/v2/TUTORIAL.md | 293 + vendor/github.com/gdamore/tcell/v2/attr.go | 33 + vendor/github.com/gdamore/tcell/v2/cell.go | 177 + .../gdamore/tcell/v2/charset_stub.go | 21 + .../gdamore/tcell/v2/charset_unix.go | 49 + .../gdamore/tcell/v2/charset_windows.go | 21 + vendor/github.com/gdamore/tcell/v2/color.go | 1081 ++ .../github.com/gdamore/tcell/v2/colorfit.go | 52 + .../gdamore/tcell/v2/console_stub.go | 23 + .../gdamore/tcell/v2/console_win.go | 1269 ++ vendor/github.com/gdamore/tcell/v2/doc.go | 48 + .../github.com/gdamore/tcell/v2/encoding.go | 139 + vendor/github.com/gdamore/tcell/v2/errors.go | 73 + vendor/github.com/gdamore/tcell/v2/event.go | 53 + vendor/github.com/gdamore/tcell/v2/go.mod | 12 + vendor/github.com/gdamore/tcell/v2/go.sum | 14 + .../github.com/gdamore/tcell/v2/interrupt.go | 41 + vendor/github.com/gdamore/tcell/v2/key.go | 470 + vendor/github.com/gdamore/tcell/v2/mouse.go | 103 + .../gdamore/tcell/v2/nonblock_bsd.go | 42 + .../gdamore/tcell/v2/nonblock_unix.go | 40 + vendor/github.com/gdamore/tcell/v2/paste.go | 48 + vendor/github.com/gdamore/tcell/v2/resize.go | 42 + vendor/github.com/gdamore/tcell/v2/runes.go | 111 + vendor/github.com/gdamore/tcell/v2/screen.go | 260 + .../github.com/gdamore/tcell/v2/simulation.go | 554 + .../github.com/gdamore/tcell/v2/stdin_unix.go | 177 + vendor/github.com/gdamore/tcell/v2/style.go | 137 + .../gdamore/tcell/v2/terminfo/.gitignore | 1 + .../gdamore/tcell/v2/terminfo/README.md | 25 + .../gdamore/tcell/v2/terminfo/TERMINALS.md | 7 + .../tcell/v2/terminfo/a/aixterm/term.go | 83 + .../tcell/v2/terminfo/a/alacritty/term.go | 69 + .../gdamore/tcell/v2/terminfo/a/ansi/term.go | 43 + .../tcell/v2/terminfo/b/beterm/term.go | 57 + .../gdamore/tcell/v2/terminfo/base/base.go | 32 + .../tcell/v2/terminfo/c/cygwin/term.go | 66 + .../tcell/v2/terminfo/d/dtterm/term.go | 69 + .../tcell/v2/terminfo/dynamic/dynamic.go | 427 + .../gdamore/tcell/v2/terminfo/e/emacs/term.go | 63 + .../tcell/v2/terminfo/extended/extended.go | 58 + .../gdamore/tcell/v2/terminfo/f/foot/foot.go | 69 + .../gdamore/tcell/v2/terminfo/g/gnome/term.go | 130 + .../gdamore/tcell/v2/terminfo/gen.sh | 18 + .../tcell/v2/terminfo/h/hpterm/term.go | 51 + .../tcell/v2/terminfo/k/konsole/term.go | 130 + .../gdamore/tcell/v2/terminfo/k/kterm/term.go | 68 + .../gdamore/tcell/v2/terminfo/l/linux/term.go | 71 + .../gdamore/tcell/v2/terminfo/models.txt | 31 + .../tcell/v2/terminfo/p/pcansi/term.go | 41 + .../gdamore/tcell/v2/terminfo/r/rxvt/term.go | 485 + .../tcell/v2/terminfo/s/screen/term.go | 128 + .../tcell/v2/terminfo/s/simpleterm/term.go | 136 + .../gdamore/tcell/v2/terminfo/s/sun/term.go | 112 + .../tcell/v2/terminfo/t/termite/term.go | 67 + .../gdamore/tcell/v2/terminfo/t/tmux/term.go | 71 + .../gdamore/tcell/v2/terminfo/terminfo.go | 827 ++ .../gdamore/tcell/v2/terminfo/v/vt100/term.go | 49 + .../gdamore/tcell/v2/terminfo/v/vt102/term.go | 48 + .../gdamore/tcell/v2/terminfo/v/vt220/term.go | 59 + .../gdamore/tcell/v2/terminfo/v/vt320/term.go | 64 + .../gdamore/tcell/v2/terminfo/v/vt400/term.go | 48 + .../gdamore/tcell/v2/terminfo/v/vt420/term.go | 54 + .../gdamore/tcell/v2/terminfo/v/vt52/term.go | 29 + .../gdamore/tcell/v2/terminfo/w/wy50/term.go | 60 + .../gdamore/tcell/v2/terminfo/w/wy60/term.go | 64 + .../tcell/v2/terminfo/w/wy99_ansi/term.go | 116 + .../gdamore/tcell/v2/terminfo/x/xfce/term.go | 67 + .../tcell/v2/terminfo/x/xterm/direct.go | 92 + .../gdamore/tcell/v2/terminfo/x/xterm/term.go | 192 + .../tcell/v2/terminfo/x/xterm_kitty/term.go | 69 + .../tcell/v2/terminfo/x/xterm_termite/term.go | 66 + .../gdamore/tcell/v2/terms_default.go | 23 + .../gdamore/tcell/v2/terms_dynamic.go | 37 + .../gdamore/tcell/v2/terms_static.go | 27 + vendor/github.com/gdamore/tcell/v2/tscreen.go | 1727 +++ .../gdamore/tcell/v2/tscreen_stub.go | 25 + .../gdamore/tcell/v2/tscreen_unix.go | 31 + vendor/github.com/gdamore/tcell/v2/tty.go | 56 + .../github.com/gdamore/tcell/v2/tty_unix.go | 190 + .../gogo/protobuf/gogoproto/Makefile | 37 + .../github.com/gogo/protobuf/gogoproto/doc.go | 169 + .../gogo/protobuf/gogoproto/gogo.pb.go | 874 ++ .../gogo/protobuf/gogoproto/gogo.pb.golden | 45 + .../gogo/protobuf/gogoproto/gogo.proto | 144 + .../gogo/protobuf/gogoproto/helper.go | 415 + .../protoc-gen-gogo/descriptor/Makefile | 36 + .../protoc-gen-gogo/descriptor/descriptor.go | 118 + .../descriptor/descriptor.pb.go | 2865 ++++ .../descriptor/descriptor_gostring.gen.go | 752 + .../protoc-gen-gogo/descriptor/helper.go | 390 + vendor/github.com/golang/glog/LICENSE | 191 + vendor/github.com/golang/glog/README.md | 36 + vendor/github.com/golang/glog/glog.go | 1180 ++ vendor/github.com/golang/glog/glog_file.go | 124 + vendor/github.com/golang/glog/go.mod | 3 + vendor/github.com/google/cel-go/LICENSE | 233 + vendor/github.com/google/cel-go/cel/cel.go | 19 + vendor/github.com/google/cel-go/cel/env.go | 550 + vendor/github.com/google/cel-go/cel/io.go | 280 + .../github.com/google/cel-go/cel/library.go | 77 + .../github.com/google/cel-go/cel/options.go | 528 + .../github.com/google/cel-go/cel/program.go | 552 + .../google/cel-go/checker/checker.go | 646 + .../github.com/google/cel-go/checker/cost.go | 601 + .../google/cel-go/checker/decls/decls.go | 231 + .../google/cel-go/checker/decls/scopes.go | 145 + .../github.com/google/cel-go/checker/env.go | 394 + .../google/cel-go/checker/errors.go | 96 + .../google/cel-go/checker/mapping.go | 49 + .../google/cel-go/checker/options.go | 53 + .../google/cel-go/checker/printer.go | 71 + .../google/cel-go/checker/standard.go | 492 + .../github.com/google/cel-go/checker/types.go | 485 + .../cel-go/common/containers/container.go | 316 + .../github.com/google/cel-go/common/cost.go | 40 + .../google/cel-go/common/debug/debug.go | 305 + vendor/github.com/google/cel-go/common/doc.go | 17 + .../github.com/google/cel-go/common/error.go | 70 + .../github.com/google/cel-go/common/errors.go | 74 + .../google/cel-go/common/location.go | 51 + .../cel-go/common/operators/operators.go | 143 + .../cel-go/common/overloads/overloads.go | 317 + .../google/cel-go/common/runes/buffer.go | 194 + .../github.com/google/cel-go/common/source.go | 186 + .../google/cel-go/common/types/any_value.go | 24 + .../google/cel-go/common/types/bool.go | 140 + .../google/cel-go/common/types/bytes.go | 132 + .../google/cel-go/common/types/compare.go | 97 + .../google/cel-go/common/types/doc.go | 17 + .../google/cel-go/common/types/double.go | 216 + .../google/cel-go/common/types/duration.go | 199 + .../google/cel-go/common/types/err.go | 133 + .../google/cel-go/common/types/int.go | 297 + .../google/cel-go/common/types/iterator.go | 55 + .../google/cel-go/common/types/json_value.go | 28 + .../google/cel-go/common/types/list.go | 489 + .../google/cel-go/common/types/map.go | 832 ++ .../google/cel-go/common/types/null.go | 97 + .../google/cel-go/common/types/object.go | 156 + .../google/cel-go/common/types/overflow.go | 389 + .../google/cel-go/common/types/pb/checked.go | 93 + .../google/cel-go/common/types/pb/enum.go | 44 + .../google/cel-go/common/types/pb/equal.go | 205 + .../google/cel-go/common/types/pb/file.go | 141 + .../google/cel-go/common/types/pb/pb.go | 196 + .../google/cel-go/common/types/pb/type.go | 532 + .../google/cel-go/common/types/provider.go | 533 + .../cel-go/common/types/ref/provider.go | 103 + .../cel-go/common/types/ref/reference.go | 59 + .../google/cel-go/common/types/string.go | 218 + .../google/cel-go/common/types/timestamp.go | 316 + .../cel-go/common/types/traits/comparer.go | 33 + .../cel-go/common/types/traits/container.go | 23 + .../common/types/traits/field_tester.go | 30 + .../cel-go/common/types/traits/indexer.go | 25 + .../cel-go/common/types/traits/iterator.go | 36 + .../cel-go/common/types/traits/lister.go | 33 + .../cel-go/common/types/traits/mapper.go | 33 + .../cel-go/common/types/traits/matcher.go | 23 + .../google/cel-go/common/types/traits/math.go | 62 + .../cel-go/common/types/traits/receiver.go | 24 + .../cel-go/common/types/traits/sizer.go | 25 + .../cel-go/common/types/traits/traits.go | 64 + .../google/cel-go/common/types/type.go | 102 + .../google/cel-go/common/types/uint.go | 249 + .../google/cel-go/common/types/unknown.go | 66 + .../google/cel-go/common/types/util.go | 48 + .../google/cel-go/interpreter/activation.go | 201 + .../cel-go/interpreter/attribute_patterns.go | 404 + .../google/cel-go/interpreter/attributes.go | 1051 ++ .../google/cel-go/interpreter/coster.go | 35 + .../google/cel-go/interpreter/decorators.go | 269 + .../google/cel-go/interpreter/dispatcher.go | 100 + .../google/cel-go/interpreter/evalstate.go | 75 + .../cel-go/interpreter/functions/functions.go | 58 + .../cel-go/interpreter/functions/standard.go | 270 + .../cel-go/interpreter/interpretable.go | 1220 ++ .../google/cel-go/interpreter/interpreter.go | 217 + .../cel-go/interpreter/optimizations.go | 46 + .../google/cel-go/interpreter/planner.go | 774 ++ .../google/cel-go/interpreter/prune.go | 391 + .../google/cel-go/interpreter/runtimecost.go | 241 + .../github.com/google/cel-go/parser/errors.go | 30 + .../google/cel-go/parser/gen/CEL.g4 | 186 + .../google/cel-go/parser/gen/CEL.tokens | 64 + .../google/cel-go/parser/gen/CELLexer.tokens | 64 + .../cel-go/parser/gen/cel_base_listener.go | 195 + .../cel-go/parser/gen/cel_base_visitor.go | 124 + .../google/cel-go/parser/gen/cel_lexer.go | 324 + .../google/cel-go/parser/gen/cel_listener.go | 183 + .../google/cel-go/parser/gen/cel_parser.go | 4324 ++++++ .../google/cel-go/parser/gen/cel_visitor.go | 96 + .../google/cel-go/parser/gen/doc.go | 16 + .../github.com/google/cel-go/parser/helper.go | 473 + .../github.com/google/cel-go/parser/input.go | 128 + .../github.com/google/cel-go/parser/macro.go | 386 + .../google/cel-go/parser/options.go | 104 + .../github.com/google/cel-go/parser/parser.go | 897 ++ .../google/cel-go/parser/unescape.go | 237 + .../google/cel-go/parser/unparser.go | 446 + .../internal/and/and_closer.go | 48 + .../internal/estargz/estargz.go | 55 + .../go-containerregistry/internal/gzip/zip.go | 146 + .../internal/redact/redact.go | 35 + .../internal/retry/retry.go | 77 + .../wait/kubernetes_apimachinery_wait.go | 123 + .../internal/verify/verify.go | 122 + .../go-containerregistry/pkg/authn/README.md | 322 + .../go-containerregistry/pkg/authn/anon.go | 26 + .../go-containerregistry/pkg/authn/auth.go | 30 + .../go-containerregistry/pkg/authn/authn.go | 115 + .../go-containerregistry/pkg/authn/basic.go | 29 + .../go-containerregistry/pkg/authn/bearer.go | 27 + .../go-containerregistry/pkg/authn/doc.go | 17 + .../pkg/authn/keychain.go | 174 + .../pkg/authn/multikeychain.go | 41 + .../go-containerregistry/pkg/logs/logs.go | 39 + .../go-containerregistry/pkg/v1/config.go | 133 + .../pkg/v1/daemon/README.md | 11 + .../go-containerregistry/pkg/v1/daemon/doc.go | 17 + .../pkg/v1/daemon/image.go | 204 + .../pkg/v1/daemon/options.go | 103 + .../pkg/v1/daemon/write.go | 61 + .../google/go-containerregistry/pkg/v1/doc.go | 18 + .../pkg/v1/empty/README.md | 8 + .../go-containerregistry/pkg/v1/empty/doc.go | 16 + .../pkg/v1/empty/image.go | 52 + .../pkg/v1/empty/index.go | 63 + .../go-containerregistry/pkg/v1/hash.go | 123 + .../go-containerregistry/pkg/v1/image.go | 59 + .../go-containerregistry/pkg/v1/index.go | 43 + .../go-containerregistry/pkg/v1/layer.go | 42 + .../pkg/v1/layout/README.md | 5 + .../pkg/v1/layout/blob.go | 38 + .../go-containerregistry/pkg/v1/layout/doc.go | 19 + .../pkg/v1/layout/image.go | 139 + .../pkg/v1/layout/index.go | 161 + .../pkg/v1/layout/layoutpath.go | 25 + .../pkg/v1/layout/options.go | 57 + .../pkg/v1/layout/read.go | 32 + .../pkg/v1/layout/write.go | 511 + .../go-containerregistry/pkg/v1/manifest.go | 68 + .../pkg/v1/match/match.go | 90 + .../pkg/v1/mutate/README.md | 56 + .../go-containerregistry/pkg/v1/mutate/doc.go | 16 + .../pkg/v1/mutate/image.go | 285 + .../pkg/v1/mutate/index.go | 202 + .../pkg/v1/mutate/mutate.go | 483 + .../pkg/v1/mutate/rebase.go | 144 + .../pkg/v1/partial/README.md | 82 + .../pkg/v1/partial/compressed.go | 181 + .../pkg/v1/partial/doc.go | 17 + .../pkg/v1/partial/image.go | 28 + .../pkg/v1/partial/index.go | 85 + .../pkg/v1/partial/uncompressed.go | 223 + .../pkg/v1/partial/with.go | 389 + .../go-containerregistry/pkg/v1/platform.go | 108 + .../go-containerregistry/pkg/v1/progress.go | 25 + .../pkg/v1/remote/README.md | 117 + .../pkg/v1/remote/catalog.go | 154 + .../pkg/v1/remote/check.go | 59 + .../pkg/v1/remote/delete.go | 57 + .../pkg/v1/remote/descriptor.go | 430 + .../go-containerregistry/pkg/v1/remote/doc.go | 17 + .../pkg/v1/remote/image.go | 248 + .../pkg/v1/remote/index.go | 307 + .../pkg/v1/remote/layer.go | 94 + .../pkg/v1/remote/list.go | 141 + .../pkg/v1/remote/mount.go | 95 + .../pkg/v1/remote/multi_write.go | 303 + .../pkg/v1/remote/options.go | 292 + .../pkg/v1/remote/transport/README.md | 129 + .../pkg/v1/remote/transport/basic.go | 62 + .../pkg/v1/remote/transport/bearer.go | 311 + .../pkg/v1/remote/transport/doc.go | 18 + .../pkg/v1/remote/transport/error.go | 205 + .../pkg/v1/remote/transport/logger.go | 91 + .../pkg/v1/remote/transport/ping.go | 180 + .../pkg/v1/remote/transport/retry.go | 91 + .../pkg/v1/remote/transport/schemer.go | 44 + .../pkg/v1/remote/transport/scope.go | 24 + .../pkg/v1/remote/transport/transport.go | 112 + .../pkg/v1/remote/transport/useragent.go | 94 + .../pkg/v1/remote/write.go | 892 ++ .../pkg/v1/stream/README.md | 68 + .../pkg/v1/stream/layer.go | 263 + .../pkg/v1/tarball/README.md | 280 + .../pkg/v1/tarball/doc.go | 17 + .../pkg/v1/tarball/image.go | 423 + .../pkg/v1/tarball/layer.go | 296 + .../pkg/v1/tarball/write.go | 453 + .../pkg/v1/types/types.go | 71 + .../pkg/v1/zz_deepcopy_generated.go | 324 + vendor/github.com/hako/durafmt/.gitignore | 24 + vendor/github.com/hako/durafmt/.travis.yml | 21 + .../hako/durafmt/CODE_OF_CONDUCT.md | 46 + .../github.com/hako/durafmt/CONTRIBUTING.md | 9 + vendor/github.com/hako/durafmt/LICENSE | 21 + vendor/github.com/hako/durafmt/README.md | 154 + vendor/github.com/hako/durafmt/durafmt.go | 330 + vendor/github.com/hako/durafmt/go.mod | 3 + vendor/github.com/hako/durafmt/unit.go | 108 + vendor/github.com/hashicorp/errwrap/LICENSE | 354 + vendor/github.com/hashicorp/errwrap/README.md | 89 + .../github.com/hashicorp/errwrap/errwrap.go | 178 + vendor/github.com/hashicorp/errwrap/go.mod | 1 + .../hashicorp/go-multierror/LICENSE | 353 + .../hashicorp/go-multierror/Makefile | 31 + .../hashicorp/go-multierror/README.md | 150 + .../hashicorp/go-multierror/append.go | 43 + .../hashicorp/go-multierror/flatten.go | 26 + .../hashicorp/go-multierror/format.go | 27 + .../github.com/hashicorp/go-multierror/go.mod | 5 + .../github.com/hashicorp/go-multierror/go.sum | 2 + .../hashicorp/go-multierror/group.go | 38 + .../hashicorp/go-multierror/multierror.go | 121 + .../hashicorp/go-multierror/prefix.go | 37 + .../hashicorp/go-multierror/sort.go | 16 + vendor/github.com/heroku/color/.gitignore | 16 + vendor/github.com/heroku/color/.golangcli.yml | 26 + vendor/github.com/heroku/color/LICENSE | 21 + vendor/github.com/heroku/color/Makefile | 21 + vendor/github.com/heroku/color/README.md | 109 + vendor/github.com/heroku/color/attributes.go | 175 + vendor/github.com/heroku/color/cache.go | 53 + vendor/github.com/heroku/color/color.go | 271 + vendor/github.com/heroku/color/console.go | 167 + vendor/github.com/heroku/color/go.mod | 5 + vendor/github.com/heroku/color/go.sum | 6 + .../jonboulle/clockwork/.editorconfig | 12 + .../github.com/jonboulle/clockwork/.gitignore | 27 + vendor/github.com/jonboulle/clockwork/LICENSE | 201 + .../github.com/jonboulle/clockwork/README.md | 80 + .../jonboulle/clockwork/clockwork.go | 195 + vendor/github.com/jonboulle/clockwork/go.mod | 3 + .../github.com/jonboulle/clockwork/ticker.go | 72 + .../github.com/kballard/go-shellquote/LICENSE | 19 + .../github.com/kballard/go-shellquote/README | 36 + .../github.com/kballard/go-shellquote/doc.go | 3 + .../kballard/go-shellquote/quote.go | 102 + .../kballard/go-shellquote/unquote.go | 156 + .../lucasb-eyer/go-colorful/.gitignore | 101 + .../lucasb-eyer/go-colorful/CHANGELOG.md | 42 + .../lucasb-eyer/go-colorful/LICENSE | 7 + .../lucasb-eyer/go-colorful/README.md | 482 + .../lucasb-eyer/go-colorful/colorgens.go | 55 + .../lucasb-eyer/go-colorful/colors.go | 979 ++ .../github.com/lucasb-eyer/go-colorful/go.mod | 3 + .../go-colorful/happy_palettegen.go | 25 + .../lucasb-eyer/go-colorful/hexcolor.go | 67 + .../go-colorful/hsluv-snapshot-rev4.json | 1 + .../lucasb-eyer/go-colorful/hsluv.go | 207 + .../go-colorful/soft_palettegen.go | 185 + .../go-colorful/warm_palettegen.go | 25 + vendor/github.com/mattn/go-colorable/LICENSE | 21 + .../github.com/mattn/go-colorable/README.md | 48 + .../mattn/go-colorable/colorable_appengine.go | 38 + .../mattn/go-colorable/colorable_others.go | 38 + .../mattn/go-colorable/colorable_windows.go | 1047 ++ vendor/github.com/mattn/go-colorable/go.mod | 8 + vendor/github.com/mattn/go-colorable/go.sum | 5 + .../github.com/mattn/go-colorable/go.test.sh | 12 + .../mattn/go-colorable/noncolorable.go | 57 + vendor/github.com/mattn/go-isatty/LICENSE | 9 + vendor/github.com/mattn/go-isatty/README.md | 50 + vendor/github.com/mattn/go-isatty/doc.go | 2 + vendor/github.com/mattn/go-isatty/go.mod | 5 + vendor/github.com/mattn/go-isatty/go.sum | 2 + vendor/github.com/mattn/go-isatty/go.test.sh | 12 + .../github.com/mattn/go-isatty/isatty_bsd.go | 19 + .../mattn/go-isatty/isatty_others.go | 16 + .../mattn/go-isatty/isatty_plan9.go | 23 + .../mattn/go-isatty/isatty_solaris.go | 21 + .../mattn/go-isatty/isatty_tcgets.go | 19 + .../mattn/go-isatty/isatty_windows.go | 125 + .../github.com/mattn/go-runewidth/.travis.yml | 16 + vendor/github.com/mattn/go-runewidth/LICENSE | 21 + .../github.com/mattn/go-runewidth/README.md | 27 + vendor/github.com/mattn/go-runewidth/go.mod | 5 + vendor/github.com/mattn/go-runewidth/go.sum | 2 + .../github.com/mattn/go-runewidth/go.test.sh | 12 + .../mattn/go-runewidth/runewidth.go | 273 + .../mattn/go-runewidth/runewidth_appengine.go | 8 + .../mattn/go-runewidth/runewidth_js.go | 9 + .../mattn/go-runewidth/runewidth_posix.go | 82 + .../mattn/go-runewidth/runewidth_table.go | 439 + .../mattn/go-runewidth/runewidth_windows.go | 28 + vendor/github.com/mgutz/ansi/.gitignore | 1 + vendor/github.com/mgutz/ansi/LICENSE | 9 + vendor/github.com/mgutz/ansi/README.md | 121 + vendor/github.com/mgutz/ansi/ansi.go | 285 + vendor/github.com/mgutz/ansi/doc.go | 65 + vendor/github.com/mgutz/ansi/print.go | 57 + .../github.com/mitchellh/ioprogress/LICENSE | 21 + .../github.com/mitchellh/ioprogress/README.md | 42 + .../github.com/mitchellh/ioprogress/draw.go | 104 + .../github.com/mitchellh/ioprogress/reader.go | 107 + .../moby/spdystream/CONTRIBUTING.md | 13 + vendor/github.com/moby/spdystream/LICENSE | 202 + vendor/github.com/moby/spdystream/MAINTAINERS | 40 + vendor/github.com/moby/spdystream/NOTICE | 5 + vendor/github.com/moby/spdystream/README.md | 77 + .../github.com/moby/spdystream/connection.go | 972 ++ vendor/github.com/moby/spdystream/go.mod | 5 + vendor/github.com/moby/spdystream/go.sum | 2 + vendor/github.com/moby/spdystream/handlers.go | 52 + vendor/github.com/moby/spdystream/priority.go | 114 + .../moby/spdystream/spdy/dictionary.go | 203 + .../github.com/moby/spdystream/spdy/read.go | 364 + .../github.com/moby/spdystream/spdy/types.go | 291 + .../github.com/moby/spdystream/spdy/write.go | 334 + vendor/github.com/moby/spdystream/stream.go | 343 + vendor/github.com/moby/spdystream/utils.go | 32 + vendor/github.com/moby/sys/mount/LICENSE | 202 + vendor/github.com/moby/sys/mount/doc.go | 4 + vendor/github.com/moby/sys/mount/flags_bsd.go | 45 + .../github.com/moby/sys/mount/flags_linux.go | 87 + .../github.com/moby/sys/mount/flags_unix.go | 139 + vendor/github.com/moby/sys/mount/go.mod | 8 + vendor/github.com/moby/sys/mount/go.sum | 5 + .../github.com/moby/sys/mount/mount_errors.go | 46 + .../github.com/moby/sys/mount/mount_unix.go | 87 + .../github.com/moby/sys/mount/mounter_bsd.go | 61 + .../moby/sys/mount/mounter_linux.go | 73 + .../moby/sys/mount/mounter_unsupported.go | 7 + .../moby/sys/mount/sharedsubtree_linux.go | 73 + vendor/github.com/moby/sys/mountinfo/LICENSE | 202 + vendor/github.com/moby/sys/mountinfo/doc.go | 44 + vendor/github.com/moby/sys/mountinfo/go.mod | 5 + vendor/github.com/moby/sys/mountinfo/go.sum | 2 + .../moby/sys/mountinfo/mounted_linux.go | 57 + .../moby/sys/mountinfo/mounted_unix.go | 54 + .../moby/sys/mountinfo/mountinfo.go | 67 + .../moby/sys/mountinfo/mountinfo_bsd.go | 72 + .../moby/sys/mountinfo/mountinfo_filters.go | 63 + .../moby/sys/mountinfo/mountinfo_linux.go | 214 + .../sys/mountinfo/mountinfo_unsupported.go | 19 + .../moby/sys/mountinfo/mountinfo_windows.go | 10 + vendor/github.com/moby/term/.gitignore | 8 + vendor/github.com/moby/term/LICENSE | 191 + vendor/github.com/moby/term/README.md | 36 + vendor/github.com/moby/term/ascii.go | 66 + vendor/github.com/moby/term/go.mod | 12 + vendor/github.com/moby/term/go.sum | 23 + vendor/github.com/moby/term/proxy.go | 88 + vendor/github.com/moby/term/tc.go | 19 + vendor/github.com/moby/term/term.go | 120 + vendor/github.com/moby/term/term_windows.go | 231 + vendor/github.com/moby/term/termios.go | 35 + vendor/github.com/moby/term/termios_bsd.go | 12 + vendor/github.com/moby/term/termios_nonbsd.go | 12 + .../moby/term/windows/ansi_reader.go | 252 + .../moby/term/windows/ansi_writer.go | 56 + .../github.com/moby/term/windows/console.go | 39 + vendor/github.com/moby/term/windows/doc.go | 5 + vendor/github.com/moby/term/winsize.go | 20 + vendor/github.com/morikuni/aec/LICENSE | 21 + vendor/github.com/morikuni/aec/README.md | 178 + vendor/github.com/morikuni/aec/aec.go | 137 + vendor/github.com/morikuni/aec/ansi.go | 59 + vendor/github.com/morikuni/aec/builder.go | 388 + vendor/github.com/morikuni/aec/sample.gif | Bin 0 -> 12548 bytes vendor/github.com/morikuni/aec/sgr.go | 202 + .../opencontainers/go-digest/.mailmap | 4 + .../opencontainers/go-digest/.pullapprove.yml | 28 + .../opencontainers/go-digest/.travis.yml | 5 + .../opencontainers/go-digest/CONTRIBUTING.md | 72 + .../opencontainers/go-digest/LICENSE | 192 + .../opencontainers/go-digest/LICENSE.docs | 425 + .../opencontainers/go-digest/MAINTAINERS | 5 + .../opencontainers/go-digest/README.md | 96 + .../opencontainers/go-digest/algorithm.go | 193 + .../opencontainers/go-digest/digest.go | 157 + .../opencontainers/go-digest/digester.go | 40 + .../opencontainers/go-digest/doc.go | 62 + .../opencontainers/go-digest/go.mod | 3 + .../opencontainers/go-digest/verifiers.go | 46 + .../opencontainers/image-spec/LICENSE | 191 + .../image-spec/specs-go/v1/annotations.go | 62 + .../image-spec/specs-go/v1/config.go | 114 + .../image-spec/specs-go/v1/descriptor.go | 64 + .../image-spec/specs-go/v1/index.go | 32 + .../image-spec/specs-go/v1/layout.go | 28 + .../image-spec/specs-go/v1/manifest.go | 35 + .../image-spec/specs-go/v1/mediatype.go | 57 + .../image-spec/specs-go/version.go | 32 + .../image-spec/specs-go/versioned.go | 23 + vendor/github.com/opencontainers/runc/LICENSE | 191 + vendor/github.com/opencontainers/runc/NOTICE | 17 + .../runc/libcontainer/user/lookup_unix.go | 157 + .../runc/libcontainer/user/user.go | 605 + .../runc/libcontainer/user/user_fuzzer.go | 43 + .../opencontainers/runtime-spec/LICENSE | 191 + .../runtime-spec/specs-go/config.go | 700 + .../runtime-spec/specs-go/state.go | 56 + .../runtime-spec/specs-go/version.go | 18 + .../github.com/opencontainers/selinux/LICENSE | 201 + .../opencontainers/selinux/go-selinux/doc.go | 14 + .../selinux/go-selinux/label/label.go | 97 + .../selinux/go-selinux/label/label_linux.go | 196 + .../selinux/go-selinux/label/label_stub.go | 49 + .../selinux/go-selinux/rchcon.go | 22 + .../selinux/go-selinux/rchcon_go115.go | 21 + .../selinux/go-selinux/selinux.go | 304 + .../selinux/go-selinux/selinux_linux.go | 1262 ++ .../selinux/go-selinux/selinux_stub.go | 164 + .../selinux/go-selinux/xattrs_linux.go | 71 + .../selinux/pkg/pwalk/README.md | 48 + .../opencontainers/selinux/pkg/pwalk/pwalk.go | 115 + .../selinux/pkg/pwalkdir/README.md | 54 + .../selinux/pkg/pwalkdir/pwalkdir.go | 116 + .../openshift/source-to-image/AUTHORS | 10 + .../openshift/source-to-image/LICENSE | 202 + .../source-to-image/pkg/api/constants/doc.go | 2 + .../source-to-image/pkg/api/constants/env.go | 13 + .../pkg/api/constants/labels.go | 78 + .../pkg/api/constants/scripts.go | 53 + .../openshift/source-to-image/pkg/api/doc.go | 2 + .../source-to-image/pkg/api/helpers.go | 42 + .../source-to-image/pkg/api/types.go | 638 + .../pkg/api/validation/validation.go | 116 + .../source-to-image/pkg/build/cleanup.go | 47 + .../source-to-image/pkg/build/config.go | 59 + .../source-to-image/pkg/build/interfaces.go | 73 + .../pkg/build/strategies/doc.go | 1 + .../build/strategies/dockerfile/dockerfile.go | 494 + .../pkg/build/strategies/layered/layered.go | 227 + .../build/strategies/onbuild/entrypoint.go | 60 + .../pkg/build/strategies/onbuild/onbuild.go | 195 + .../build/strategies/sti/postexecutorstep.go | 574 + .../pkg/build/strategies/sti/sti.go | 822 ++ .../pkg/build/strategies/sti/usage.go | 52 + .../pkg/build/strategies/strategies.go | 82 + .../source-to-image/pkg/docker/doc.go | 3 + .../source-to-image/pkg/docker/docker.go | 1149 ++ .../source-to-image/pkg/docker/fake_docker.go | 229 + .../source-to-image/pkg/docker/util.go | 465 + .../source-to-image/pkg/errors/doc.go | 3 + .../source-to-image/pkg/errors/errors.go | 304 + .../source-to-image/pkg/ignore/doc.go | 3 + .../source-to-image/pkg/ignore/ignore.go | 136 + .../openshift/source-to-image/pkg/scm/doc.go | 1 + .../pkg/scm/downloaders/empty/noop.go | 21 + .../pkg/scm/downloaders/file/download.go | 69 + .../pkg/scm/downloaders/git/clone.go | 94 + .../source-to-image/pkg/scm/git/git.go | 316 + .../pkg/scm/git/testhelpers.go | 86 + .../source-to-image/pkg/scm/git/types.go | 51 + .../source-to-image/pkg/scm/git/url.go | 194 + .../openshift/source-to-image/pkg/scm/scm.go | 53 + .../source-to-image/pkg/scripts/doc.go | 3 + .../source-to-image/pkg/scripts/download.go | 151 + .../pkg/scripts/environment.go | 77 + .../source-to-image/pkg/scripts/install.go | 285 + .../openshift/source-to-image/pkg/tar/doc.go | 3 + .../openshift/source-to-image/pkg/tar/tar.go | 494 + .../source-to-image/pkg/util/callback.go | 70 + .../source-to-image/pkg/util/cmd/cmd.go | 84 + .../pkg/util/cygpath/cygpath.go | 40 + .../openshift/source-to-image/pkg/util/doc.go | 4 + .../openshift/source-to-image/pkg/util/env.go | 76 + .../source-to-image/pkg/util/fs/fs.go | 407 + .../source-to-image/pkg/util/injection.go | 156 + .../pkg/util/interrupt/interrupt.go | 104 + .../source-to-image/pkg/util/labels.go | 96 + .../source-to-image/pkg/util/log/log.go | 213 + .../pkg/util/status/build_status.go | 131 + .../source-to-image/pkg/util/status/doc.go | 3 + .../source-to-image/pkg/util/strings.go | 21 + .../source-to-image/pkg/util/timeout.go | 52 + .../source-to-image/pkg/util/user/range.go | 170 + .../pkg/util/user/rangelist.go | 87 + .../source-to-image/pkg/util/util.go | 19 + vendor/github.com/ory/viper/.editorconfig | 15 + vendor/github.com/ory/viper/.gitignore | 4 + vendor/github.com/ory/viper/.golangci.yml | 27 + vendor/github.com/ory/viper/.travis.yml | 31 + vendor/github.com/ory/viper/LICENSE | 21 + vendor/github.com/ory/viper/Makefile | 76 + vendor/github.com/ory/viper/README.md | 808 ++ vendor/github.com/ory/viper/flags.go | 57 + vendor/github.com/ory/viper/go.mod | 27 + vendor/github.com/ory/viper/go.sum | 83 + vendor/github.com/ory/viper/util.go | 246 + vendor/github.com/ory/viper/viper.go | 2277 +++ .../github.com/rivo/tview/CODE_OF_CONDUCT.md | 73 + vendor/github.com/rivo/tview/CONTRIBUTING.md | 35 + vendor/github.com/rivo/tview/LICENSE.txt | 21 + vendor/github.com/rivo/tview/README.md | 116 + vendor/github.com/rivo/tview/ansi.go | 258 + vendor/github.com/rivo/tview/application.go | 766 ++ vendor/github.com/rivo/tview/borders.go | 45 + vendor/github.com/rivo/tview/box.go | 414 + vendor/github.com/rivo/tview/button.go | 157 + vendor/github.com/rivo/tview/checkbox.go | 240 + vendor/github.com/rivo/tview/doc.go | 179 + vendor/github.com/rivo/tview/dropdown.go | 564 + vendor/github.com/rivo/tview/flex.go | 239 + vendor/github.com/rivo/tview/form.go | 679 + vendor/github.com/rivo/tview/frame.go | 192 + vendor/github.com/rivo/tview/go.mod | 13 + vendor/github.com/rivo/tview/go.sum | 26 + vendor/github.com/rivo/tview/grid.go | 693 + vendor/github.com/rivo/tview/inputfield.go | 646 + vendor/github.com/rivo/tview/list.go | 705 + vendor/github.com/rivo/tview/modal.go | 201 + vendor/github.com/rivo/tview/pages.go | 315 + vendor/github.com/rivo/tview/primitive.go | 58 + vendor/github.com/rivo/tview/semigraphics.go | 294 + vendor/github.com/rivo/tview/styles.go | 35 + vendor/github.com/rivo/tview/table.go | 1298 ++ vendor/github.com/rivo/tview/textview.go | 1261 ++ vendor/github.com/rivo/tview/treeview.go | 826 ++ vendor/github.com/rivo/tview/tview.gif | Bin 0 -> 2226085 bytes vendor/github.com/rivo/tview/util.go | 658 + vendor/github.com/rivo/uniseg/LICENSE.txt | 21 + vendor/github.com/rivo/uniseg/README.md | 62 + vendor/github.com/rivo/uniseg/doc.go | 8 + vendor/github.com/rivo/uniseg/go.mod | 3 + vendor/github.com/rivo/uniseg/grapheme.go | 268 + vendor/github.com/rivo/uniseg/properties.go | 1658 +++ .../sabhiram/go-gitignore/.gitignore | 28 + .../sabhiram/go-gitignore/.travis.yml | 18 + .../github.com/sabhiram/go-gitignore/LICENSE | 22 + .../sabhiram/go-gitignore/README.md | 15 + .../sabhiram/go-gitignore/ignore.go | 225 + .../sabhiram/go-gitignore/version_gen.go | 12 + vendor/github.com/sirupsen/logrus/.gitignore | 4 + .../github.com/sirupsen/logrus/.golangci.yml | 40 + vendor/github.com/sirupsen/logrus/.travis.yml | 15 + .../github.com/sirupsen/logrus/CHANGELOG.md | 259 + vendor/github.com/sirupsen/logrus/LICENSE | 21 + vendor/github.com/sirupsen/logrus/README.md | 513 + vendor/github.com/sirupsen/logrus/alt_exit.go | 76 + .../github.com/sirupsen/logrus/appveyor.yml | 14 + .../github.com/sirupsen/logrus/buffer_pool.go | 52 + vendor/github.com/sirupsen/logrus/doc.go | 26 + vendor/github.com/sirupsen/logrus/entry.go | 431 + vendor/github.com/sirupsen/logrus/exported.go | 270 + .../github.com/sirupsen/logrus/formatter.go | 78 + vendor/github.com/sirupsen/logrus/go.mod | 10 + vendor/github.com/sirupsen/logrus/go.sum | 8 + vendor/github.com/sirupsen/logrus/hooks.go | 34 + .../sirupsen/logrus/json_formatter.go | 128 + vendor/github.com/sirupsen/logrus/logger.go | 404 + vendor/github.com/sirupsen/logrus/logrus.go | 186 + .../logrus/terminal_check_appengine.go | 11 + .../sirupsen/logrus/terminal_check_bsd.go | 13 + .../sirupsen/logrus/terminal_check_js.go | 7 + .../logrus/terminal_check_no_terminal.go | 11 + .../logrus/terminal_check_notappengine.go | 17 + .../sirupsen/logrus/terminal_check_solaris.go | 11 + .../sirupsen/logrus/terminal_check_unix.go | 13 + .../sirupsen/logrus/terminal_check_windows.go | 27 + .../sirupsen/logrus/text_formatter.go | 339 + vendor/github.com/sirupsen/logrus/writer.go | 70 + vendor/github.com/src-d/gcfg/LICENSE | 28 + vendor/github.com/src-d/gcfg/README | 4 + vendor/github.com/src-d/gcfg/doc.go | 145 + vendor/github.com/src-d/gcfg/errors.go | 41 + vendor/github.com/src-d/gcfg/go1_0.go | 7 + vendor/github.com/src-d/gcfg/go1_2.go | 9 + vendor/github.com/src-d/gcfg/read.go | 273 + .../github.com/src-d/gcfg/scanner/errors.go | 121 + .../github.com/src-d/gcfg/scanner/scanner.go | 342 + vendor/github.com/src-d/gcfg/set.go | 332 + .../github.com/src-d/gcfg/token/position.go | 435 + .../github.com/src-d/gcfg/token/serialize.go | 56 + vendor/github.com/src-d/gcfg/token/token.go | 83 + vendor/github.com/src-d/gcfg/types/bool.go | 23 + vendor/github.com/src-d/gcfg/types/doc.go | 4 + vendor/github.com/src-d/gcfg/types/enum.go | 44 + vendor/github.com/src-d/gcfg/types/int.go | 86 + vendor/github.com/src-d/gcfg/types/scan.go | 23 + .../github.com/stoewer/go-strcase/.gitignore | 17 + .../stoewer/go-strcase/.golangci.yml | 26 + vendor/github.com/stoewer/go-strcase/LICENSE | 21 + .../github.com/stoewer/go-strcase/README.md | 50 + vendor/github.com/stoewer/go-strcase/camel.go | 37 + vendor/github.com/stoewer/go-strcase/doc.go | 8 + vendor/github.com/stoewer/go-strcase/go.mod | 5 + vendor/github.com/stoewer/go-strcase/go.sum | 11 + .../github.com/stoewer/go-strcase/helper.go | 71 + vendor/github.com/stoewer/go-strcase/kebab.go | 14 + vendor/github.com/stoewer/go-strcase/snake.go | 58 + vendor/github.com/syndtr/gocapability/LICENSE | 24 + .../gocapability/capability/capability.go | 133 + .../capability/capability_linux.go | 642 + .../capability/capability_noop.go | 19 + .../syndtr/gocapability/capability/enum.go | 309 + .../gocapability/capability/enum_gen.go | 138 + .../gocapability/capability/syscall_linux.go | 154 + vendor/github.com/tektoncd/cli/LICENSE | 202 + .../tektoncd/cli/pkg/actions/create.go | 36 + .../tektoncd/cli/pkg/actions/delete.go | 38 + .../tektoncd/cli/pkg/actions/get.go | 51 + .../tektoncd/cli/pkg/actions/gvr.go | 55 + .../tektoncd/cli/pkg/actions/list.go | 51 + .../tektoncd/cli/pkg/actions/patch.go | 38 + .../tektoncd/cli/pkg/actions/watch.go | 38 + .../tektoncd/cli/pkg/cli/interface.go | 65 + .../github.com/tektoncd/cli/pkg/cli/params.go | 201 + .../tektoncd/cli/pkg/formatted/address.go | 23 + .../tektoncd/cli/pkg/formatted/color.go | 176 + .../tektoncd/cli/pkg/formatted/completion.go | 31 + .../tektoncd/cli/pkg/formatted/conditions.go | 33 + .../tektoncd/cli/pkg/formatted/description.go | 24 + .../tektoncd/cli/pkg/formatted/k8s.go | 83 + .../tektoncd/cli/pkg/formatted/param.go | 91 + .../tektoncd/cli/pkg/formatted/results.go | 23 + .../tektoncd/cli/pkg/formatted/task.go | 26 + .../tektoncd/cli/pkg/formatted/time.go | 47 + .../tektoncd/cli/pkg/formatted/version.go | 22 + .../tektoncd/cli/pkg/formatted/workspace.go | 91 + .../cli/pkg/pipelinerun/pipelinerun.go | 211 + .../cli/pkg/pipelinerun/sort/by_namespace.go | 48 + .../cli/pkg/pipelinerun/sort/by_start_time.go | 39 + .../tektoncd/cli/pkg/pipelinerun/tracker.go | 166 + .../tektoncd/cli/pkg/printer/printer.go | 30 + .../github.com/tektoncd/cli/pkg/task/task.go | 211 + .../tektoncd/cli/pkg/task/tasklastrun.go | 74 + .../tektoncd/cli/pkg/taskrun/create.go | 127 + .../tektoncd/cli/pkg/taskrun/get.go | 77 + .../tektoncd/cli/pkg/taskrun/list/list.go | 89 + .../tektoncd/cli/pkg/taskrun/patch.go | 52 + .../cli/pkg/taskrun/sort/by_namespace.go | 48 + .../cli/pkg/taskrun/sort/by_start_time.go | 39 + .../tektoncd/cli/pkg/taskrun/taskrun.go | 109 + .../tektoncd/cli/pkg/taskrun/watch.go | 17 + vendor/github.com/tektoncd/pipeline/LICENSE | 202 + .../pkg/apis/config/artifact_bucket.go | 112 + .../pipeline/pkg/apis/config/artifact_pvc.go | 86 + .../pipeline/pkg/apis/config/default.go | 134 + .../tektoncd/pipeline/pkg/apis/config/doc.go | 17 + .../pipeline/pkg/apis/config/feature_flags.go | 163 + .../pipeline/pkg/apis/config/metrics.go | 144 + .../pipeline/pkg/apis/config/store.go | 133 + .../pkg/apis/config/zz_generated.deepcopy.go | 111 + .../pipeline/pkg/apis/pipeline/controller.go | 39 + .../pipeline/pkg/apis/pipeline/images.go | 77 + .../pipeline/pkg/apis/pipeline/options.go | 23 + .../pipeline/pkg/apis/pipeline/paths.go | 31 + .../pipeline/pkg/apis/pipeline/pod/doc.go | 20 + .../pkg/apis/pipeline/pod/template.go | 125 + .../pipeline/pod/zz_generated.deepcopy.go | 115 + .../pipeline/pkg/apis/pipeline/register.go | 99 + .../pkg/apis/pipeline/storagetypes.go | 25 + .../v1alpha1/cluster_task_conversion.go | 49 + .../v1alpha1/cluster_task_defaults.go | 30 + .../pipeline/v1alpha1/cluster_task_types.go | 64 + .../v1alpha1/cluster_task_validation.go | 37 + .../pipeline/v1alpha1/condition_defaults.go | 37 + .../apis/pipeline/v1alpha1/condition_types.go | 101 + .../pipeline/v1alpha1/condition_validation.go | 59 + .../v1alpha1/container_replacements.go | 74 + .../pipeline/v1alpha1/conversion_error.go | 31 + .../pkg/apis/pipeline/v1alpha1/doc.go | 23 + .../pkg/apis/pipeline/v1alpha1/param_types.go | 49 + .../pipeline/v1alpha1/pipeline_conversion.go | 177 + .../pipeline/v1alpha1/pipeline_defaults.go | 47 + .../pipeline/v1alpha1/pipeline_interface.go | 26 + .../v1alpha1/pipeline_resource_types.go | 74 + .../apis/pipeline/v1alpha1/pipeline_types.go | 259 + .../pipeline/v1alpha1/pipeline_validation.go | 335 + .../v1alpha1/pipelinerun_conversion.go | 116 + .../pipeline/v1alpha1/pipelinerun_defaults.go | 55 + .../pipeline/v1alpha1/pipelinerun_types.go | 223 + .../v1alpha1/pipelinerun_validation.go | 85 + .../pkg/apis/pipeline/v1alpha1/pod.go | 8 + .../pkg/apis/pipeline/v1alpha1/register.go | 68 + .../apis/pipeline/v1alpha1/resource_paths.go | 40 + .../apis/pipeline/v1alpha1/resource_types.go | 109 + .../apis/pipeline/v1alpha1/run_defaults.go | 45 + .../pkg/apis/pipeline/v1alpha1/run_types.go | 234 + .../apis/pipeline/v1alpha1/run_validation.go | 74 + .../pipeline/v1alpha1/step_replacements.go | 27 + .../pipeline/v1alpha1/storage_resource.go | 24 + .../apis/pipeline/v1alpha1/task_conversion.go | 127 + .../apis/pipeline/v1alpha1/task_defaults.go | 47 + .../apis/pipeline/v1alpha1/task_interface.go | 26 + .../pkg/apis/pipeline/v1alpha1/task_types.go | 143 + .../apis/pipeline/v1alpha1/task_validation.go | 437 + .../pipeline/v1alpha1/taskrun_conversion.go | 149 + .../pipeline/v1alpha1/taskrun_defaults.go | 74 + .../apis/pipeline/v1alpha1/taskrun_types.go | 266 + .../pipeline/v1alpha1/taskrun_validation.go | 174 + .../apis/pipeline/v1alpha1/workspace_types.go | 35 + .../pipeline/v1alpha1/workspace_validation.go | 17 + .../v1alpha1/zz_generated.deepcopy.go | 1024 ++ .../v1beta1/cluster_task_conversion.go | 36 + .../pipeline/v1beta1/cluster_task_defaults.go | 30 + .../pipeline/v1beta1/cluster_task_types.go | 75 + .../v1beta1/cluster_task_validation.go | 35 + .../apis/pipeline/v1beta1/condition_types.go | 75 + .../v1beta1/container_replacements.go | 74 + .../apis/pipeline/v1beta1/conversion_error.go | 28 + .../pipeline/pkg/apis/pipeline/v1beta1/doc.go | 23 + .../pkg/apis/pipeline/v1beta1/merge.go | 173 + .../pipeline/v1beta1/openapi_generated.go | 4938 +++++++ .../apis/pipeline/v1beta1/param_context.go | 168 + .../pkg/apis/pipeline/v1beta1/param_types.go | 189 + .../pipeline/v1beta1/pipeline_conversion.go | 36 + .../pipeline/v1beta1/pipeline_defaults.go | 89 + .../pipeline/v1beta1/pipeline_interface.go | 30 + .../apis/pipeline/v1beta1/pipeline_types.go | 611 + .../pipeline/v1beta1/pipeline_validation.go | 489 + .../v1beta1/pipelineref_validation.go | 88 + .../v1beta1/pipelinerun_conversion.go | 36 + .../pipeline/v1beta1/pipelinerun_defaults.go | 60 + .../pipeline/v1beta1/pipelinerun_types.go | 542 + .../v1beta1/pipelinerun_validation.go | 214 + .../pipeline/pkg/apis/pipeline/v1beta1/pod.go | 22 + .../pkg/apis/pipeline/v1beta1/register.go | 65 + .../apis/pipeline/v1beta1/resolver_types.go | 47 + .../apis/pipeline/v1beta1/resource_paths.go | 40 + .../apis/pipeline/v1beta1/resource_types.go | 281 + .../v1beta1/resource_types_validation.go | 102 + .../pkg/apis/pipeline/v1beta1/resultref.go | 155 + .../pipeline/v1beta1/sidecar_replacements.go | 27 + .../pipeline/v1beta1/step_replacements.go | 27 + .../pkg/apis/pipeline/v1beta1/swagger.json | 2757 ++++ .../apis/pipeline/v1beta1/task_conversion.go | 36 + .../apis/pipeline/v1beta1/task_defaults.go | 44 + .../apis/pipeline/v1beta1/task_interface.go | 30 + .../pkg/apis/pipeline/v1beta1/task_types.go | 227 + .../apis/pipeline/v1beta1/task_validation.go | 400 + .../pipeline/v1beta1/taskref_validation.go | 88 + .../pipeline/v1beta1/taskrun_conversion.go | 36 + .../apis/pipeline/v1beta1/taskrun_defaults.go | 135 + .../apis/pipeline/v1beta1/taskrun_types.go | 467 + .../pipeline/v1beta1/taskrun_validation.go | 165 + .../pipeline/v1beta1/version_validation.go | 38 + .../pkg/apis/pipeline/v1beta1/when_types.go | 109 + .../apis/pipeline/v1beta1/when_validation.go | 92 + .../apis/pipeline/v1beta1/workspace_types.go | 123 + .../pipeline/v1beta1/workspace_validation.go | 92 + .../pipeline/v1beta1/zz_generated.deepcopy.go | 2103 +++ .../pkg/apis/resource/v1alpha1/doc.go | 23 + .../v1alpha1/pipeline_resource_defaults.go | 34 + .../v1alpha1/pipeline_resource_types.go | 153 + .../v1alpha1/pipelineresource_validation.go | 157 + .../pkg/apis/resource/v1alpha1/register.go | 54 + .../v1alpha1/zz_generated.deepcopy.go | 181 + .../pkg/apis/run/v1alpha1/runstatus_types.go | 148 + .../run/v1alpha1/zz_generated.deepcopy.go | 77 + .../pipeline/pkg/apis/validate/metadata.go | 48 + .../client/clientset/versioned/clientset.go | 111 + .../pkg/client/clientset/versioned/doc.go | 20 + .../client/clientset/versioned/scheme/doc.go | 20 + .../clientset/versioned/scheme/register.go | 58 + .../typed/pipeline/v1alpha1/clustertask.go | 168 + .../typed/pipeline/v1alpha1/condition.go | 178 + .../versioned/typed/pipeline/v1alpha1/doc.go | 20 + .../pipeline/v1alpha1/generated_expansion.go | 33 + .../typed/pipeline/v1alpha1/pipeline.go | 178 + .../pipeline/v1alpha1/pipeline_client.go | 119 + .../typed/pipeline/v1alpha1/pipelinerun.go | 195 + .../versioned/typed/pipeline/v1alpha1/run.go | 195 + .../versioned/typed/pipeline/v1alpha1/task.go | 178 + .../typed/pipeline/v1alpha1/taskrun.go | 195 + .../typed/pipeline/v1beta1/clustertask.go | 168 + .../versioned/typed/pipeline/v1beta1/doc.go | 20 + .../pipeline/v1beta1/generated_expansion.go | 29 + .../typed/pipeline/v1beta1/pipeline.go | 178 + .../typed/pipeline/v1beta1/pipeline_client.go | 109 + .../typed/pipeline/v1beta1/pipelinerun.go | 195 + .../versioned/typed/pipeline/v1beta1/task.go | 178 + .../typed/pipeline/v1beta1/taskrun.go | 195 + .../informers/externalversions/factory.go | 180 + .../informers/externalversions/generic.go | 87 + .../internalinterfaces/factory_interfaces.go | 40 + .../externalversions/pipeline/interface.go | 54 + .../pipeline/v1alpha1/clustertask.go | 89 + .../pipeline/v1alpha1/condition.go | 90 + .../pipeline/v1alpha1/interface.go | 87 + .../pipeline/v1alpha1/pipeline.go | 90 + .../pipeline/v1alpha1/pipelinerun.go | 90 + .../externalversions/pipeline/v1alpha1/run.go | 90 + .../pipeline/v1alpha1/task.go | 90 + .../pipeline/v1alpha1/taskrun.go | 90 + .../pipeline/v1beta1/clustertask.go | 89 + .../pipeline/v1beta1/interface.go | 73 + .../pipeline/v1beta1/pipeline.go | 90 + .../pipeline/v1beta1/pipelinerun.go | 90 + .../externalversions/pipeline/v1beta1/task.go | 90 + .../pipeline/v1beta1/taskrun.go | 90 + .../listers/pipeline/v1alpha1/clustertask.go | 68 + .../listers/pipeline/v1alpha1/condition.go | 99 + .../pipeline/v1alpha1/expansion_generated.go | 71 + .../listers/pipeline/v1alpha1/pipeline.go | 99 + .../listers/pipeline/v1alpha1/pipelinerun.go | 99 + .../client/listers/pipeline/v1alpha1/run.go | 99 + .../client/listers/pipeline/v1alpha1/task.go | 99 + .../listers/pipeline/v1alpha1/taskrun.go | 99 + .../listers/pipeline/v1beta1/clustertask.go | 68 + .../pipeline/v1beta1/expansion_generated.go | 55 + .../listers/pipeline/v1beta1/pipeline.go | 99 + .../listers/pipeline/v1beta1/pipelinerun.go | 99 + .../client/listers/pipeline/v1beta1/task.go | 99 + .../listers/pipeline/v1beta1/taskrun.go | 99 + .../resource/clientset/versioned/clientset.go | 97 + .../resource/clientset/versioned/doc.go | 20 + .../clientset/versioned/scheme/doc.go | 20 + .../clientset/versioned/scheme/register.go | 56 + .../versioned/typed/resource/v1alpha1/doc.go | 20 + .../resource/v1alpha1/generated_expansion.go | 21 + .../resource/v1alpha1/pipelineresource.go | 178 + .../resource/v1alpha1/resource_client.go | 89 + .../tektoncd/pipeline/pkg/list/diff.go | 52 + .../pkg/reconciler/pipeline/dag/dag.go | 218 + .../pipeline/pkg/substitution/substitution.go | 209 + vendor/github.com/tektoncd/triggers/LICENSE | 201 + .../triggers/pkg/apis/config/allowed_types.go | 30 + .../triggers/pkg/apis/config/default.go | 74 + .../tektoncd/triggers/pkg/apis/config/doc.go | 17 + .../triggers/pkg/apis/config/feature_flags.go | 103 + .../triggers/pkg/apis/config/store.go | 106 + .../pkg/apis/config/zz_generated.deepcopy.go | 54 + .../pkg/apis/triggers/contexts/contexts.go | 38 + .../triggers/pkg/apis/triggers/register.go | 18 + .../v1alpha1/cluster_interceptor_defaults.go | 34 + .../v1alpha1/cluster_interceptor_types.go | 125 + .../cluster_interceptor_validation.go | 46 + .../cluster_trigger_binding_defaults.go | 24 + .../v1alpha1/cluster_trigger_binding_types.go | 69 + .../cluster_trigger_binding_validation.go | 34 + .../pkg/apis/triggers/v1alpha1/doc.go | 23 + .../v1alpha1/event_listener_defaults.go | 55 + .../triggers/v1alpha1/event_listener_types.go | 324 + .../v1alpha1/event_listener_validation.go | 299 + .../triggers/v1alpha1/interceptor_types.go | 88 + .../pkg/apis/triggers/v1alpha1/param.go | 21 + .../pkg/apis/triggers/v1alpha1/register.go | 64 + .../v1alpha1/trigger_binding_defaults.go | 24 + .../v1alpha1/trigger_binding_interface.go | 26 + .../v1alpha1/trigger_binding_types.go | 77 + .../v1alpha1/trigger_binding_validation.go | 98 + .../triggers/v1alpha1/trigger_defaults.go | 54 + .../v1alpha1/trigger_template_defaults.go | 24 + .../v1alpha1/trigger_template_types.go | 69 + .../v1alpha1/trigger_template_validation.go | 109 + .../apis/triggers/v1alpha1/trigger_types.go | 292 + .../v1alpha1/trigger_types_convert.go | 41 + .../triggers/v1alpha1/trigger_validation.go | 171 + .../v1alpha1/zz_generated.deepcopy.go | 1194 ++ .../cluster_trigger_binding_defaults.go | 24 + .../v1beta1/cluster_trigger_binding_types.go | 69 + .../cluster_trigger_binding_validation.go | 34 + .../triggers/pkg/apis/triggers/v1beta1/doc.go | 23 + .../v1beta1/event_listener_defaults.go | 49 + .../triggers/v1beta1/event_listener_types.go | 355 + .../v1beta1/event_listener_validation.go | 329 + .../triggers/v1beta1/interceptor_types.go | 88 + .../triggers/v1beta1/openapi_generated.go | 1937 +++ .../pkg/apis/triggers/v1beta1/param.go | 21 + .../pkg/apis/triggers/v1beta1/register.go | 62 + .../pkg/apis/triggers/v1beta1/swagger.json | 2749 ++++ .../v1beta1/trigger_binding_defaults.go | 24 + .../v1beta1/trigger_binding_interface.go | 26 + .../triggers/v1beta1/trigger_binding_types.go | 77 + .../v1beta1/trigger_binding_validation.go | 97 + .../apis/triggers/v1beta1/trigger_defaults.go | 47 + .../v1beta1/trigger_template_defaults.go | 24 + .../v1beta1/trigger_template_types.go | 69 + .../v1beta1/trigger_template_validation.go | 109 + .../apis/triggers/v1beta1/trigger_types.go | 201 + .../triggers/v1beta1/trigger_types_convert.go | 41 + .../triggers/v1beta1/trigger_validation.go | 136 + .../triggers/v1beta1/version_validation.go | 38 + .../triggers/v1beta1/zz_generated.deepcopy.go | 1123 ++ .../triggers/pkg/apis/triggers/validation.go | 39 + .../client/clientset/versioned/clientset.go | 111 + .../pkg/client/clientset/versioned/doc.go | 20 + .../client/clientset/versioned/scheme/doc.go | 20 + .../clientset/versioned/scheme/register.go | 58 + .../triggers/v1alpha1/clusterinterceptor.go | 184 + .../v1alpha1/clustertriggerbinding.go | 184 + .../versioned/typed/triggers/v1alpha1/doc.go | 20 + .../typed/triggers/v1alpha1/eventlistener.go | 195 + .../triggers/v1alpha1/generated_expansion.go | 31 + .../typed/triggers/v1alpha1/trigger.go | 178 + .../typed/triggers/v1alpha1/triggerbinding.go | 195 + .../triggers/v1alpha1/triggers_client.go | 114 + .../triggers/v1alpha1/triggertemplate.go | 195 + .../triggers/v1beta1/clustertriggerbinding.go | 184 + .../versioned/typed/triggers/v1beta1/doc.go | 20 + .../typed/triggers/v1beta1/eventlistener.go | 195 + .../triggers/v1beta1/generated_expansion.go | 29 + .../typed/triggers/v1beta1/trigger.go | 178 + .../typed/triggers/v1beta1/triggerbinding.go | 195 + .../typed/triggers/v1beta1/triggers_client.go | 109 + .../typed/triggers/v1beta1/triggertemplate.go | 195 + vendor/github.com/vbatts/tar-split/LICENSE | 28 + .../vbatts/tar-split/archive/tar/common.go | 723 + .../vbatts/tar-split/archive/tar/format.go | 303 + .../vbatts/tar-split/archive/tar/reader.go | 923 ++ .../tar-split/archive/tar/stat_actime1.go | 20 + .../tar-split/archive/tar/stat_actime2.go | 20 + .../vbatts/tar-split/archive/tar/stat_unix.go | 96 + .../vbatts/tar-split/archive/tar/strconv.go | 326 + .../vbatts/tar-split/archive/tar/writer.go | 653 + .../x/crypto/openpgp/armor/armor.go | 230 + .../x/crypto/openpgp/armor/encode.go | 160 + .../x/crypto/openpgp/canonical_text.go | 59 + .../x/crypto/openpgp/elgamal/elgamal.go | 130 + .../x/crypto/openpgp/errors/errors.go | 78 + vendor/golang.org/x/crypto/openpgp/keys.go | 693 + .../x/crypto/openpgp/packet/compressed.go | 123 + .../x/crypto/openpgp/packet/config.go | 91 + .../x/crypto/openpgp/packet/encrypted_key.go | 208 + .../x/crypto/openpgp/packet/literal.go | 89 + .../x/crypto/openpgp/packet/ocfb.go | 143 + .../openpgp/packet/one_pass_signature.go | 73 + .../x/crypto/openpgp/packet/opaque.go | 162 + .../x/crypto/openpgp/packet/packet.go | 590 + .../x/crypto/openpgp/packet/private_key.go | 385 + .../x/crypto/openpgp/packet/public_key.go | 753 + .../x/crypto/openpgp/packet/public_key_v3.go | 279 + .../x/crypto/openpgp/packet/reader.go | 76 + .../x/crypto/openpgp/packet/signature.go | 731 + .../x/crypto/openpgp/packet/signature_v3.go | 146 + .../openpgp/packet/symmetric_key_encrypted.go | 155 + .../openpgp/packet/symmetrically_encrypted.go | 290 + .../x/crypto/openpgp/packet/userattribute.go | 91 + .../x/crypto/openpgp/packet/userid.go | 160 + vendor/golang.org/x/crypto/openpgp/read.go | 448 + vendor/golang.org/x/crypto/openpgp/s2k/s2k.go | 279 + vendor/golang.org/x/crypto/openpgp/write.go | 418 + .../api/expr/v1alpha1/checked.pb.go | 1657 +++ .../googleapis/api/expr/v1alpha1/eval.pb.go | 579 + .../api/expr/v1alpha1/explain.pb.go | 275 + .../googleapis/api/expr/v1alpha1/syntax.pb.go | 1685 +++ .../googleapis/api/expr/v1alpha1/value.pb.go | 720 + .../protobuf/types/dynamicpb/dynamic.go | 713 + .../protobuf/types/known/emptypb/empty.pb.go | 168 + .../types/known/structpb/struct.pb.go | 810 ++ vendor/gopkg.in/natefinch/npipe.v2/.gitignore | 22 + .../gopkg.in/natefinch/npipe.v2/LICENSE.txt | 8 + vendor/gopkg.in/natefinch/npipe.v2/README.md | 308 + vendor/gopkg.in/natefinch/npipe.v2/doc.go | 50 + .../natefinch/npipe.v2/npipe_windows.go | 531 + .../natefinch/npipe.v2/znpipe_windows_386.go | 124 + .../npipe.v2/znpipe_windows_amd64.go | 124 + vendor/gopkg.in/src-d/go-billy.v4/.gitignore | 4 + vendor/gopkg.in/src-d/go-billy.v4/.travis.yml | 17 + vendor/gopkg.in/src-d/go-billy.v4/DCO | 25 + vendor/gopkg.in/src-d/go-billy.v4/LICENSE | 201 + vendor/gopkg.in/src-d/go-billy.v4/MAINTAINERS | 1 + vendor/gopkg.in/src-d/go-billy.v4/Makefile | 25 + vendor/gopkg.in/src-d/go-billy.v4/README.md | 72 + .../gopkg.in/src-d/go-billy.v4/appveyor.yml | 15 + vendor/gopkg.in/src-d/go-billy.v4/fs.go | 202 + vendor/gopkg.in/src-d/go-billy.v4/go.mod | 8 + vendor/gopkg.in/src-d/go-billy.v4/go.sum | 12 + .../src-d/go-billy.v4/helper/chroot/chroot.go | 242 + .../go-billy.v4/helper/polyfill/polyfill.go | 105 + vendor/gopkg.in/src-d/go-billy.v4/osfs/os.go | 139 + .../src-d/go-billy.v4/osfs/os_posix.go | 21 + .../src-d/go-billy.v4/osfs/os_windows.go | 57 + .../gopkg.in/src-d/go-billy.v4/util/glob.go | 111 + .../gopkg.in/src-d/go-billy.v4/util/util.go | 224 + vendor/gopkg.in/src-d/go-git.v4/.gitignore | 4 + vendor/gopkg.in/src-d/go-git.v4/.travis.yml | 37 + .../src-d/go-git.v4/CODE_OF_CONDUCT.md | 74 + .../gopkg.in/src-d/go-git.v4/COMPATIBILITY.md | 111 + .../gopkg.in/src-d/go-git.v4/CONTRIBUTING.md | 59 + vendor/gopkg.in/src-d/go-git.v4/DCO | 36 + vendor/gopkg.in/src-d/go-git.v4/LICENSE | 201 + vendor/gopkg.in/src-d/go-git.v4/MAINTAINERS | 3 + vendor/gopkg.in/src-d/go-git.v4/Makefile | 52 + vendor/gopkg.in/src-d/go-git.v4/README.md | 123 + vendor/gopkg.in/src-d/go-git.v4/appveyor.yml | 21 + vendor/gopkg.in/src-d/go-git.v4/blame.go | 302 + vendor/gopkg.in/src-d/go-git.v4/common.go | 22 + .../gopkg.in/src-d/go-git.v4/config/branch.go | 90 + .../gopkg.in/src-d/go-git.v4/config/config.go | 407 + .../src-d/go-git.v4/config/modules.go | 139 + .../src-d/go-git.v4/config/refspec.go | 150 + vendor/gopkg.in/src-d/go-git.v4/doc.go | 10 + vendor/gopkg.in/src-d/go-git.v4/go.mod | 29 + vendor/gopkg.in/src-d/go-git.v4/go.sum | 92 + .../go-git.v4/internal/revision/parser.go | 622 + .../go-git.v4/internal/revision/scanner.go | 117 + .../go-git.v4/internal/revision/token.go | 28 + .../src-d/go-git.v4/internal/url/url.go | 37 + .../gopkg.in/src-d/go-git.v4/object_walker.go | 104 + vendor/gopkg.in/src-d/go-git.v4/options.go | 492 + .../go-git.v4/plumbing/cache/buffer_lru.go | 98 + .../src-d/go-git.v4/plumbing/cache/common.go | 39 + .../go-git.v4/plumbing/cache/object_lru.go | 101 + .../src-d/go-git.v4/plumbing/error.go | 35 + .../go-git.v4/plumbing/filemode/filemode.go | 188 + .../plumbing/format/config/common.go | 99 + .../plumbing/format/config/decoder.go | 37 + .../go-git.v4/plumbing/format/config/doc.go | 122 + .../plumbing/format/config/encoder.go | 77 + .../plumbing/format/config/option.go | 117 + .../plumbing/format/config/section.go | 146 + .../go-git.v4/plumbing/format/diff/patch.go | 58 + .../plumbing/format/diff/unified_encoder.go | 360 + .../plumbing/format/gitignore/dir.go | 136 + .../plumbing/format/gitignore/doc.go | 70 + .../plumbing/format/gitignore/matcher.go | 30 + .../plumbing/format/gitignore/pattern.go | 153 + .../plumbing/format/idxfile/decoder.go | 177 + .../go-git.v4/plumbing/format/idxfile/doc.go | 128 + .../plumbing/format/idxfile/encoder.go | 142 + .../plumbing/format/idxfile/idxfile.go | 346 + .../plumbing/format/idxfile/writer.go | 186 + .../plumbing/format/index/decoder.go | 477 + .../go-git.v4/plumbing/format/index/doc.go | 360 + .../plumbing/format/index/encoder.go | 150 + .../go-git.v4/plumbing/format/index/index.go | 213 + .../go-git.v4/plumbing/format/index/match.go | 186 + .../go-git.v4/plumbing/format/objfile/doc.go | 2 + .../plumbing/format/objfile/reader.go | 114 + .../plumbing/format/objfile/writer.go | 109 + .../plumbing/format/packfile/common.go | 78 + .../plumbing/format/packfile/delta_index.go | 297 + .../format/packfile/delta_selector.go | 369 + .../plumbing/format/packfile/diff_delta.go | 200 + .../go-git.v4/plumbing/format/packfile/doc.go | 39 + .../plumbing/format/packfile/encoder.go | 219 + .../plumbing/format/packfile/error.go | 30 + .../plumbing/format/packfile/fsobject.go | 116 + .../plumbing/format/packfile/object_pack.go | 164 + .../plumbing/format/packfile/packfile.go | 562 + .../plumbing/format/packfile/parser.go | 483 + .../plumbing/format/packfile/patch_delta.go | 229 + .../plumbing/format/packfile/scanner.go | 466 + .../plumbing/format/pktline/encoder.go | 122 + .../plumbing/format/pktline/scanner.go | 134 + .../gopkg.in/src-d/go-git.v4/plumbing/hash.go | 73 + .../src-d/go-git.v4/plumbing/memory.go | 61 + .../src-d/go-git.v4/plumbing/object.go | 111 + .../src-d/go-git.v4/plumbing/object/blob.go | 144 + .../src-d/go-git.v4/plumbing/object/change.go | 157 + .../plumbing/object/change_adaptor.go | 61 + .../src-d/go-git.v4/plumbing/object/commit.go | 430 + .../plumbing/object/commit_walker.go | 327 + .../plumbing/object/commit_walker_bfs.go | 100 + .../object/commit_walker_bfs_filtered.go | 176 + .../plumbing/object/commit_walker_ctime.go | 103 + .../plumbing/object/commit_walker_file.go | 145 + .../src-d/go-git.v4/plumbing/object/common.go | 12 + .../go-git.v4/plumbing/object/difftree.go | 37 + .../src-d/go-git.v4/plumbing/object/file.go | 137 + .../go-git.v4/plumbing/object/merge_base.go | 210 + .../src-d/go-git.v4/plumbing/object/object.go | 237 + .../src-d/go-git.v4/plumbing/object/patch.go | 346 + .../src-d/go-git.v4/plumbing/object/tag.go | 357 + .../src-d/go-git.v4/plumbing/object/tree.go | 520 + .../go-git.v4/plumbing/object/treenoder.go | 136 + .../plumbing/protocol/packp/advrefs.go | 203 + .../plumbing/protocol/packp/advrefs_decode.go | 288 + .../plumbing/protocol/packp/advrefs_encode.go | 176 + .../protocol/packp/capability/capability.go | 252 + .../protocol/packp/capability/list.go | 196 + .../plumbing/protocol/packp/common.go | 70 + .../go-git.v4/plumbing/protocol/packp/doc.go | 724 + .../plumbing/protocol/packp/report_status.go | 165 + .../plumbing/protocol/packp/shallowupd.go | 92 + .../protocol/packp/sideband/common.go | 33 + .../plumbing/protocol/packp/sideband/demux.go | 148 + .../plumbing/protocol/packp/sideband/doc.go | 31 + .../plumbing/protocol/packp/sideband/muxer.go | 65 + .../plumbing/protocol/packp/srvresp.go | 127 + .../plumbing/protocol/packp/ulreq.go | 168 + .../plumbing/protocol/packp/ulreq_decode.go | 257 + .../plumbing/protocol/packp/ulreq_encode.go | 145 + .../plumbing/protocol/packp/updreq.go | 122 + .../plumbing/protocol/packp/updreq_decode.go | 250 + .../plumbing/protocol/packp/updreq_encode.go | 75 + .../plumbing/protocol/packp/uppackreq.go | 98 + .../plumbing/protocol/packp/uppackresp.go | 109 + .../src-d/go-git.v4/plumbing/reference.go | 209 + .../src-d/go-git.v4/plumbing/revision.go | 11 + .../go-git.v4/plumbing/revlist/revlist.go | 230 + .../src-d/go-git.v4/plumbing/storer/doc.go | 2 + .../src-d/go-git.v4/plumbing/storer/index.go | 9 + .../src-d/go-git.v4/plumbing/storer/object.go | 288 + .../go-git.v4/plumbing/storer/reference.go | 240 + .../go-git.v4/plumbing/storer/shallow.go | 10 + .../src-d/go-git.v4/plumbing/storer/storer.go | 15 + .../plumbing/transport/client/client.go | 48 + .../go-git.v4/plumbing/transport/common.go | 274 + .../plumbing/transport/file/client.go | 156 + .../plumbing/transport/file/server.go | 53 + .../plumbing/transport/git/common.go | 109 + .../plumbing/transport/http/common.go | 281 + .../plumbing/transport/http/receive_pack.go | 106 + .../plumbing/transport/http/upload_pack.go | 123 + .../transport/internal/common/common.go | 467 + .../transport/internal/common/server.go | 73 + .../plumbing/transport/server/loader.go | 64 + .../plumbing/transport/server/server.go | 422 + .../plumbing/transport/ssh/auth_method.go | 322 + .../plumbing/transport/ssh/common.go | 228 + vendor/gopkg.in/src-d/go-git.v4/prune.go | 66 + vendor/gopkg.in/src-d/go-git.v4/references.go | 264 + vendor/gopkg.in/src-d/go-git.v4/remote.go | 1114 ++ vendor/gopkg.in/src-d/go-git.v4/repository.go | 1545 +++ vendor/gopkg.in/src-d/go-git.v4/status.go | 79 + .../go-git.v4/storage/filesystem/config.go | 61 + .../storage/filesystem/deltaobject.go | 37 + .../storage/filesystem/dotgit/dotgit.go | 1099 ++ .../dotgit/dotgit_rewrite_packed_refs.go | 81 + .../filesystem/dotgit/dotgit_setref.go | 90 + .../storage/filesystem/dotgit/writers.go | 284 + .../go-git.v4/storage/filesystem/index.go | 54 + .../go-git.v4/storage/filesystem/module.go | 20 + .../go-git.v4/storage/filesystem/object.go | 815 ++ .../go-git.v4/storage/filesystem/reference.go | 44 + .../go-git.v4/storage/filesystem/shallow.go | 54 + .../go-git.v4/storage/filesystem/storage.go | 73 + .../src-d/go-git.v4/storage/memory/storage.go | 320 + .../src-d/go-git.v4/storage/storer.go | 30 + vendor/gopkg.in/src-d/go-git.v4/submodule.go | 357 + .../src-d/go-git.v4/utils/binary/read.go | 180 + .../src-d/go-git.v4/utils/binary/write.go | 50 + .../src-d/go-git.v4/utils/diff/diff.go | 61 + .../src-d/go-git.v4/utils/ioutil/common.go | 170 + .../go-git.v4/utils/merkletrie/change.go | 149 + .../go-git.v4/utils/merkletrie/difftree.go | 424 + .../src-d/go-git.v4/utils/merkletrie/doc.go | 34 + .../go-git.v4/utils/merkletrie/doubleiter.go | 187 + .../utils/merkletrie/filesystem/node.go | 196 + .../go-git.v4/utils/merkletrie/index/node.go | 90 + .../utils/merkletrie/internal/frame/frame.go | 91 + .../src-d/go-git.v4/utils/merkletrie/iter.go | 216 + .../go-git.v4/utils/merkletrie/noder/noder.go | 59 + .../go-git.v4/utils/merkletrie/noder/path.go | 90 + vendor/gopkg.in/src-d/go-git.v4/worktree.go | 954 ++ .../gopkg.in/src-d/go-git.v4/worktree_bsd.go | 26 + .../src-d/go-git.v4/worktree_commit.go | 228 + .../src-d/go-git.v4/worktree_linux.go | 26 + .../src-d/go-git.v4/worktree_status.go | 660 + .../src-d/go-git.v4/worktree_unix_other.go | 26 + .../src-d/go-git.v4/worktree_windows.go | 35 + .../apimachinery/pkg/util/httpstream/doc.go | 19 + .../pkg/util/httpstream/httpstream.go | 159 + .../pkg/util/httpstream/spdy/connection.go | 204 + .../pkg/util/httpstream/spdy/roundtripper.go | 372 + .../pkg/util/httpstream/spdy/upgrade.go | 120 + .../k8s.io/apimachinery/pkg/util/rand/rand.go | 127 + .../pkg/util/remotecommand/constants.go | 53 + .../third_party/forked/golang/netutil/addr.go | 27 + .../client-go/tools/remotecommand/doc.go | 20 + .../tools/remotecommand/errorstream.go | 55 + .../client-go/tools/remotecommand/reader.go | 41 + .../tools/remotecommand/remotecommand.go | 142 + .../client-go/tools/remotecommand/resize.go | 33 + .../client-go/tools/remotecommand/v1.go | 160 + .../client-go/tools/remotecommand/v2.go | 200 + .../client-go/tools/remotecommand/v3.go | 111 + .../client-go/tools/remotecommand/v4.go | 119 + .../k8s.io/client-go/transport/spdy/spdy.go | 105 + vendor/k8s.io/client-go/util/exec/exec.go | 52 + .../kn-plugin-func/buildpacks/builder.go | 168 + .../knative.dev/kn-plugin-func/cmd/build.go | 261 + .../knative.dev/kn-plugin-func/cmd/client.go | 142 + .../kn-plugin-func/cmd/completion.go | 48 + .../kn-plugin-func/cmd/completion_util.go | 148 + .../knative.dev/kn-plugin-func/cmd/config.go | 171 + .../kn-plugin-func/cmd/config_envs.go | 442 + .../kn-plugin-func/cmd/config_labels.go | 294 + .../kn-plugin-func/cmd/config_volumes.go | 260 + .../knative.dev/kn-plugin-func/cmd/create.go | 586 + .../knative.dev/kn-plugin-func/cmd/delete.go | 160 + .../knative.dev/kn-plugin-func/cmd/deploy.go | 444 + .../knative.dev/kn-plugin-func/cmd/format.go | 54 + vendor/knative.dev/kn-plugin-func/cmd/info.go | 168 + .../knative.dev/kn-plugin-func/cmd/invoke.go | 379 + vendor/knative.dev/kn-plugin-func/cmd/list.go | 154 + .../kn-plugin-func/cmd/repository.go | 643 + vendor/knative.dev/kn-plugin-func/cmd/root.go | 459 + vendor/knative.dev/kn-plugin-func/cmd/run.go | 146 + .../knative.dev/kn-plugin-func/cmd/version.go | 51 + .../docker/creds/credentials.go | 460 + .../kn-plugin-func/docker/docker_client.go | 162 + .../kn-plugin-func/docker/pusher.go | 300 + .../kn-plugin-func/docker/runner.go | 246 + .../kn-plugin-func/http/transport.go | 194 + .../knative.dev/kn-plugin-func/k8s/client.go | 46 + .../kn-plugin-func/k8s/configmaps.go | 72 + .../knative.dev/kn-plugin-func/k8s/dialer.go | 397 + .../kn-plugin-func/k8s/labels/labels.go | 13 + vendor/knative.dev/kn-plugin-func/k8s/logs.go | 35 + .../kn-plugin-func/k8s/persistent_volumes.go | 52 + .../kn-plugin-func/k8s/role_bidings.go | 46 + .../knative.dev/kn-plugin-func/k8s/secrets.go | 175 + .../kn-plugin-func/k8s/service_accounts.go | 42 + .../kn-plugin-func/knative/client.go | 51 + .../kn-plugin-func/knative/deployer.go | 762 ++ .../kn-plugin-func/knative/describer.go | 105 + .../kn-plugin-func/knative/lister.go | 97 + .../kn-plugin-func/knative/remover.go | 44 + .../kn-plugin-func/openshift/openshift.go | 161 + .../kn-plugin-func/pipelines/tekton/client.go | 48 + .../pipelines/tekton/defaults.go | 7 + .../pipelines/tekton/pipeplines_provider.go | 281 + .../pipelines/tekton/resources.go | 168 + .../kn-plugin-func/pipelines/tekton/tasks.go | 79 + .../kn-plugin-func/plugin/plugin.go | 70 + .../kn-plugin-func/progress/progress.go | 252 + .../knative.dev/kn-plugin-func/s2i/builder.go | 174 + .../s2i/docker_client_wrapper.go | 62 + .../kn-plugin-func/ssh/ssh_agent_conf.go | 12 + .../ssh/ssh_agent_conf_windows.go | 17 + .../kn-plugin-func/ssh/ssh_dialer.go | 455 + .../kn-plugin-func/ssh/terminal.go | 121 + vendor/modules.txt | 489 + 2541 files changed, 370699 insertions(+), 1 deletion(-) create mode 100644 third_party/VENDOR-LICENSE/github.com/AlecAivazis/survey/v2/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/AlecAivazis/survey/v2/terminal/LICENSE.txt create mode 100644 third_party/VENDOR-LICENSE/github.com/BurntSushi/toml/COPYING create mode 100644 third_party/VENDOR-LICENSE/github.com/Masterminds/semver/LICENSE.txt create mode 100644 third_party/VENDOR-LICENSE/github.com/apex/log/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/buildpacks/imgutil/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/buildpacks/lifecycle/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/buildpacks/pack/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/containerd/containerd/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/containerd/containerd/NOTICE rename third_party/VENDOR-LICENSE/{k8s.io/kube-openapi/pkg/util/proto => github.com/containerd/stargz-snapshotter/estargz}/LICENSE (100%) create mode 100644 third_party/VENDOR-LICENSE/github.com/containers/image/v5/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/containers/storage/pkg/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/containers/storage/pkg/NOTICE create mode 100644 third_party/VENDOR-LICENSE/github.com/dgraph-io/ristretto/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/dgraph-io/ristretto/z/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/docker/cli/cli/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/docker/cli/cli/NOTICE create mode 100644 third_party/VENDOR-LICENSE/github.com/docker/distribution/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/docker/docker-credential-helpers/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/docker/docker/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/docker/docker/NOTICE create mode 100644 third_party/VENDOR-LICENSE/github.com/docker/go-connections/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/docker/go-units/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/dustin/go-humanize/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/emicklei/go-restful/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/fatih/color/LICENSE.md create mode 100644 third_party/VENDOR-LICENSE/github.com/gdamore/encoding/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/gdamore/tcell/v2/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/golang/glog/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/google/cel-go/LICENSE rename third_party/VENDOR-LICENSE/github.com/google/go-containerregistry/{pkg/name => }/LICENSE (100%) create mode 100644 third_party/VENDOR-LICENSE/github.com/hako/durafmt/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/hashicorp/errwrap/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/hashicorp/errwrap/README.md create mode 100644 third_party/VENDOR-LICENSE/github.com/hashicorp/errwrap/errwrap.go create mode 100644 third_party/VENDOR-LICENSE/github.com/hashicorp/errwrap/go.mod create mode 100644 third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/Makefile create mode 100644 third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/README.md create mode 100644 third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/append.go create mode 100644 third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/flatten.go create mode 100644 third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/format.go create mode 100644 third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/go.mod create mode 100644 third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/go.sum create mode 100644 third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/group.go create mode 100644 third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/multierror.go create mode 100644 third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/prefix.go create mode 100644 third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/sort.go create mode 100644 third_party/VENDOR-LICENSE/github.com/heroku/color/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/jonboulle/clockwork/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/kballard/go-shellquote/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/lucasb-eyer/go-colorful/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/mattn/go-colorable/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/mattn/go-isatty/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/mattn/go-runewidth/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/mgutz/ansi/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/mitchellh/ioprogress/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/moby/spdystream/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/moby/spdystream/NOTICE create mode 100644 third_party/VENDOR-LICENSE/github.com/moby/sys/mount/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/moby/sys/mountinfo/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/moby/term/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/morikuni/aec/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/opencontainers/go-digest/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/opencontainers/image-spec/specs-go/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/opencontainers/runc/libcontainer/user/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/opencontainers/runc/libcontainer/user/NOTICE create mode 100644 third_party/VENDOR-LICENSE/github.com/opencontainers/runtime-spec/specs-go/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/opencontainers/selinux/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/openshift/source-to-image/pkg/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/ory/viper/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/rivo/tview/LICENSE.txt create mode 100644 third_party/VENDOR-LICENSE/github.com/rivo/uniseg/LICENSE.txt create mode 100644 third_party/VENDOR-LICENSE/github.com/sabhiram/go-gitignore/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/sirupsen/logrus/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/src-d/gcfg/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/stoewer/go-strcase/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/syndtr/gocapability/capability/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/tektoncd/cli/pkg/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/tektoncd/pipeline/pkg/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/tektoncd/triggers/pkg/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/vbatts/tar-split/archive/tar/LICENSE create mode 100644 third_party/VENDOR-LICENSE/golang.org/x/mod/semver/LICENSE create mode 100644 third_party/VENDOR-LICENSE/gopkg.in/src-d/go-billy.v4/LICENSE create mode 100644 third_party/VENDOR-LICENSE/gopkg.in/src-d/go-git.v4/LICENSE create mode 100644 third_party/VENDOR-LICENSE/k8s.io/kube-openapi/pkg/LICENSE create mode 100644 vendor/github.com/AlecAivazis/survey/v2/.travis.yml create mode 100644 vendor/github.com/AlecAivazis/survey/v2/CONTRIBUTING.md create mode 100644 vendor/github.com/AlecAivazis/survey/v2/Gopkg.lock create mode 100644 vendor/github.com/AlecAivazis/survey/v2/Gopkg.toml create mode 100644 vendor/github.com/AlecAivazis/survey/v2/LICENSE create mode 100644 vendor/github.com/AlecAivazis/survey/v2/README.md create mode 100644 vendor/github.com/AlecAivazis/survey/v2/_tasks.hcl create mode 100644 vendor/github.com/AlecAivazis/survey/v2/confirm.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/core/template.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/core/write.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/editor.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/filter.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/go.mod create mode 100644 vendor/github.com/AlecAivazis/survey/v2/go.sum create mode 100644 vendor/github.com/AlecAivazis/survey/v2/input.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/multiline.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/multiselect.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/password.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/renderer.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/select.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/survey.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/LICENSE.txt create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/README.md create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/buffered_reader.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/cursor.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/cursor_windows.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/display.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/display_posix.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/display_windows.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/error.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/output.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/output_windows.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/runereader.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_bsd.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_linux.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_posix.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_ppc64le.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_windows.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/sequences.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/stdio.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/syscall_windows.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/terminal/terminal.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/transform.go create mode 100644 vendor/github.com/AlecAivazis/survey/v2/validate.go create mode 100644 vendor/github.com/Azure/go-ansiterm/LICENSE create mode 100644 vendor/github.com/Azure/go-ansiterm/README.md create mode 100644 vendor/github.com/Azure/go-ansiterm/constants.go create mode 100644 vendor/github.com/Azure/go-ansiterm/context.go create mode 100644 vendor/github.com/Azure/go-ansiterm/csi_entry_state.go create mode 100644 vendor/github.com/Azure/go-ansiterm/csi_param_state.go create mode 100644 vendor/github.com/Azure/go-ansiterm/escape_intermediate_state.go create mode 100644 vendor/github.com/Azure/go-ansiterm/escape_state.go create mode 100644 vendor/github.com/Azure/go-ansiterm/event_handler.go create mode 100644 vendor/github.com/Azure/go-ansiterm/go.mod create mode 100644 vendor/github.com/Azure/go-ansiterm/go.sum create mode 100644 vendor/github.com/Azure/go-ansiterm/ground_state.go create mode 100644 vendor/github.com/Azure/go-ansiterm/osc_string_state.go create mode 100644 vendor/github.com/Azure/go-ansiterm/parser.go create mode 100644 vendor/github.com/Azure/go-ansiterm/parser_action_helpers.go create mode 100644 vendor/github.com/Azure/go-ansiterm/parser_actions.go create mode 100644 vendor/github.com/Azure/go-ansiterm/states.go create mode 100644 vendor/github.com/Azure/go-ansiterm/utilities.go create mode 100644 vendor/github.com/Azure/go-ansiterm/winterm/ansi.go create mode 100644 vendor/github.com/Azure/go-ansiterm/winterm/api.go create mode 100644 vendor/github.com/Azure/go-ansiterm/winterm/attr_translation.go create mode 100644 vendor/github.com/Azure/go-ansiterm/winterm/cursor_helpers.go create mode 100644 vendor/github.com/Azure/go-ansiterm/winterm/erase_helpers.go create mode 100644 vendor/github.com/Azure/go-ansiterm/winterm/scroll_helper.go create mode 100644 vendor/github.com/Azure/go-ansiterm/winterm/utilities.go create mode 100644 vendor/github.com/Azure/go-ansiterm/winterm/win_event_handler.go create mode 100644 vendor/github.com/BurntSushi/toml/.gitignore create mode 100644 vendor/github.com/BurntSushi/toml/COMPATIBLE create mode 100644 vendor/github.com/BurntSushi/toml/COPYING create mode 100644 vendor/github.com/BurntSushi/toml/README.md create mode 100644 vendor/github.com/BurntSushi/toml/decode.go create mode 100644 vendor/github.com/BurntSushi/toml/decode_go116.go create mode 100644 vendor/github.com/BurntSushi/toml/deprecated.go create mode 100644 vendor/github.com/BurntSushi/toml/doc.go create mode 100644 vendor/github.com/BurntSushi/toml/encode.go create mode 100644 vendor/github.com/BurntSushi/toml/error.go create mode 100644 vendor/github.com/BurntSushi/toml/go.mod create mode 100644 vendor/github.com/BurntSushi/toml/internal/tz.go create mode 100644 vendor/github.com/BurntSushi/toml/lex.go create mode 100644 vendor/github.com/BurntSushi/toml/meta.go create mode 100644 vendor/github.com/BurntSushi/toml/parse.go create mode 100644 vendor/github.com/BurntSushi/toml/type_fields.go create mode 100644 vendor/github.com/BurntSushi/toml/type_toml.go create mode 100644 vendor/github.com/Masterminds/semver/.travis.yml create mode 100644 vendor/github.com/Masterminds/semver/CHANGELOG.md create mode 100644 vendor/github.com/Masterminds/semver/LICENSE.txt create mode 100644 vendor/github.com/Masterminds/semver/Makefile create mode 100644 vendor/github.com/Masterminds/semver/README.md create mode 100644 vendor/github.com/Masterminds/semver/appveyor.yml create mode 100644 vendor/github.com/Masterminds/semver/collection.go create mode 100644 vendor/github.com/Masterminds/semver/constraints.go create mode 100644 vendor/github.com/Masterminds/semver/doc.go create mode 100644 vendor/github.com/Masterminds/semver/version.go create mode 100644 vendor/github.com/Masterminds/semver/version_fuzz.go create mode 100644 vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go create mode 100644 vendor/github.com/Microsoft/go-winio/pkg/security/syscall_windows.go create mode 100644 vendor/github.com/Microsoft/go-winio/pkg/security/zsyscall_windows.go create mode 100644 vendor/github.com/Microsoft/go-winio/vhd/vhd.go create mode 100644 vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go create mode 100644 vendor/github.com/Microsoft/hcsshim/.gitattributes create mode 100644 vendor/github.com/Microsoft/hcsshim/.gitignore create mode 100644 vendor/github.com/Microsoft/hcsshim/.golangci.yml create mode 100644 vendor/github.com/Microsoft/hcsshim/CODEOWNERS create mode 100644 vendor/github.com/Microsoft/hcsshim/LICENSE create mode 100644 vendor/github.com/Microsoft/hcsshim/Makefile create mode 100644 vendor/github.com/Microsoft/hcsshim/Protobuild.toml create mode 100644 vendor/github.com/Microsoft/hcsshim/README.md create mode 100644 vendor/github.com/Microsoft/hcsshim/computestorage/attach.go create mode 100644 vendor/github.com/Microsoft/hcsshim/computestorage/destroy.go create mode 100644 vendor/github.com/Microsoft/hcsshim/computestorage/detach.go create mode 100644 vendor/github.com/Microsoft/hcsshim/computestorage/export.go create mode 100644 vendor/github.com/Microsoft/hcsshim/computestorage/format.go create mode 100644 vendor/github.com/Microsoft/hcsshim/computestorage/helpers.go create mode 100644 vendor/github.com/Microsoft/hcsshim/computestorage/import.go create mode 100644 vendor/github.com/Microsoft/hcsshim/computestorage/initialize.go create mode 100644 vendor/github.com/Microsoft/hcsshim/computestorage/mount.go create mode 100644 vendor/github.com/Microsoft/hcsshim/computestorage/setup.go create mode 100644 vendor/github.com/Microsoft/hcsshim/computestorage/storage.go create mode 100644 vendor/github.com/Microsoft/hcsshim/computestorage/zsyscall_windows.go create mode 100644 vendor/github.com/Microsoft/hcsshim/container.go create mode 100644 vendor/github.com/Microsoft/hcsshim/errors.go create mode 100644 vendor/github.com/Microsoft/hcsshim/functional_tests.ps1 create mode 100644 vendor/github.com/Microsoft/hcsshim/go.mod create mode 100644 vendor/github.com/Microsoft/hcsshim/go.sum create mode 100644 vendor/github.com/Microsoft/hcsshim/hcsshim.go create mode 100644 vendor/github.com/Microsoft/hcsshim/hnsendpoint.go create mode 100644 vendor/github.com/Microsoft/hcsshim/hnsglobals.go create mode 100644 vendor/github.com/Microsoft/hcsshim/hnsnetwork.go create mode 100644 vendor/github.com/Microsoft/hcsshim/hnspolicy.go create mode 100644 vendor/github.com/Microsoft/hcsshim/hnspolicylist.go create mode 100644 vendor/github.com/Microsoft/hcsshim/hnssupport.go create mode 100644 vendor/github.com/Microsoft/hcsshim/interface.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/cow/cow.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema1/schema1.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/attachment.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/battery.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cache_query_stats_response.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/chipset.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/close_handle.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/com_port.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/compute_system.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/configuration.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/console_size.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_add_instance_request.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_hv_socket_service_config.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_instance.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_modify_operation.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_operation_request.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_remove_instance_request.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_state.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_system_info.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_memory_information.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_affinity.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_config.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_configurations.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_operations.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_property.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/create_group_operation.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/delete_group_operation.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/device.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/devices.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/enhanced_mode_video.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/flexible_io_device.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_connection.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_connection_info.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_crash_reporting.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_os.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_state.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/host_processor_modify_request.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hosted_system.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_2.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_address.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_service_config.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_system_config.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/interrupt_moderation_mode.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/iov_settings.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/keyboard.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/layer.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/linux_kernel_direct.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/logical_processor.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mapped_directory.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mapped_pipe.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_2.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_information_for_vm.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_stats.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_container_definition_device.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_category.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_extension.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_instance.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_namespace.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_interface_class.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_namespace.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_directory.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_namespace.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_symlink.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/modification_request.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/modify_setting_request.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mouse.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/network_adapter.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/networking.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/pause_notification.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/pause_options.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/plan9.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/plan9_share.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_details.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_modify_request.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_parameters.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_status.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_2.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_stats.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_topology.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/properties.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/property_query.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/property_type.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/rdp_connection_options.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_changes.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_key.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_value.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/restore_state.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/save_options.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/scsi.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/service_properties.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_configuration.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_region.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_region_info.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/silo_properties.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/statistics.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage_qo_s.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage_stats.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/topology.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/uefi.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/uefi_boot_entry.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/version.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/video_monitor.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_machine.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_node_info.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_controller.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_device.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_mapping.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_pci_device.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_pci_function.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb_share.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb_share_options.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/vm_memory.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/vm_processor_limits.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/windows_crash_reporting.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/service.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/utils.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/waithelper.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hns/hns.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hns/hnsfuncs.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hns/hnsglobals.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hns/hnsnetwork.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hns/hnssupport.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hns/namespace.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/log/g.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/logfields/fields.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/longpath/longpath.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/mergemaps/merge.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/oc/exporter.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/oc/span.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/safefile/safeopen.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/timeout/timeout.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/vmcompute/vmcompute.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/vmcompute/zsyscall_windows.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/baselayer.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerid.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/processimage.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/wclayer.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/wclayer/zsyscall_windows.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/console.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/devices.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/errors.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/filesystem.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/iocp.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/jobobject.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/logon.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/memory.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/net.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/path.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/process.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/processor.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/system.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/thread.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/utils.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/winapi.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go create mode 100644 vendor/github.com/Microsoft/hcsshim/layer.go create mode 100644 vendor/github.com/Microsoft/hcsshim/osversion/osversion_windows.go create mode 100644 vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go create mode 100644 vendor/github.com/Microsoft/hcsshim/process.go create mode 100644 vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go create mode 100644 vendor/github.com/apex/log/.gitignore create mode 100644 vendor/github.com/apex/log/History.md create mode 100644 vendor/github.com/apex/log/LICENSE create mode 100644 vendor/github.com/apex/log/Makefile create mode 100644 vendor/github.com/apex/log/Readme.md create mode 100644 vendor/github.com/apex/log/context.go create mode 100644 vendor/github.com/apex/log/default.go create mode 100644 vendor/github.com/apex/log/doc.go create mode 100644 vendor/github.com/apex/log/entry.go create mode 100644 vendor/github.com/apex/log/go.mod create mode 100644 vendor/github.com/apex/log/go.sum create mode 100644 vendor/github.com/apex/log/interface.go create mode 100644 vendor/github.com/apex/log/levels.go create mode 100644 vendor/github.com/apex/log/logger.go create mode 100644 vendor/github.com/apex/log/pkg.go create mode 100644 vendor/github.com/apex/log/stack.go create mode 100644 vendor/github.com/buildpacks/imgutil/.gitignore create mode 100644 vendor/github.com/buildpacks/imgutil/CODEOWNERS create mode 100644 vendor/github.com/buildpacks/imgutil/LICENSE create mode 100644 vendor/github.com/buildpacks/imgutil/Makefile create mode 100644 vendor/github.com/buildpacks/imgutil/README.md create mode 100644 vendor/github.com/buildpacks/imgutil/go.mod create mode 100644 vendor/github.com/buildpacks/imgutil/go.sum create mode 100644 vendor/github.com/buildpacks/imgutil/golangci.yaml create mode 100644 vendor/github.com/buildpacks/imgutil/image.go create mode 100644 vendor/github.com/buildpacks/imgutil/layer/bcdhive_generated.go create mode 100644 vendor/github.com/buildpacks/imgutil/layer/pax_permissions.go create mode 100644 vendor/github.com/buildpacks/imgutil/layer/windows_baselayer.go create mode 100644 vendor/github.com/buildpacks/imgutil/layer/windows_writer.go create mode 100644 vendor/github.com/buildpacks/imgutil/local/identifier.go create mode 100644 vendor/github.com/buildpacks/imgutil/local/local.go create mode 100644 vendor/github.com/buildpacks/imgutil/remote/identifier.go create mode 100644 vendor/github.com/buildpacks/imgutil/remote/remote.go create mode 100644 vendor/github.com/buildpacks/lifecycle/.dockerignore create mode 100644 vendor/github.com/buildpacks/lifecycle/.gitignore create mode 100644 vendor/github.com/buildpacks/lifecycle/.gitpod.yml create mode 100644 vendor/github.com/buildpacks/lifecycle/CODEOWNERS create mode 100644 vendor/github.com/buildpacks/lifecycle/CONTRIBUTING.md create mode 100644 vendor/github.com/buildpacks/lifecycle/DEVELOPMENT.md create mode 100644 vendor/github.com/buildpacks/lifecycle/IMAGE.md create mode 100644 vendor/github.com/buildpacks/lifecycle/LICENSE create mode 100644 vendor/github.com/buildpacks/lifecycle/Makefile create mode 100644 vendor/github.com/buildpacks/lifecycle/README.md create mode 100644 vendor/github.com/buildpacks/lifecycle/RELEASE.md create mode 100644 vendor/github.com/buildpacks/lifecycle/analyzer.go create mode 100644 vendor/github.com/buildpacks/lifecycle/api/apis.go create mode 100644 vendor/github.com/buildpacks/lifecycle/api/version.go create mode 100644 vendor/github.com/buildpacks/lifecycle/archive/archive.go create mode 100644 vendor/github.com/buildpacks/lifecycle/archive/extract.go create mode 100644 vendor/github.com/buildpacks/lifecycle/archive/reader.go create mode 100644 vendor/github.com/buildpacks/lifecycle/archive/tar_unix.go create mode 100644 vendor/github.com/buildpacks/lifecycle/archive/tar_windows.go create mode 100644 vendor/github.com/buildpacks/lifecycle/archive/writer.go create mode 100644 vendor/github.com/buildpacks/lifecycle/auth/env_keychain.go create mode 100644 vendor/github.com/buildpacks/lifecycle/builder.go create mode 100644 vendor/github.com/buildpacks/lifecycle/buildpack/bom.go create mode 100644 vendor/github.com/buildpacks/lifecycle/buildpack/bomfile.go create mode 100644 vendor/github.com/buildpacks/lifecycle/buildpack/build.go create mode 100644 vendor/github.com/buildpacks/lifecycle/buildpack/descriptor.go create mode 100644 vendor/github.com/buildpacks/lifecycle/buildpack/detect.go create mode 100644 vendor/github.com/buildpacks/lifecycle/buildpack/error.go create mode 100644 vendor/github.com/buildpacks/lifecycle/buildpack/files.go create mode 100644 vendor/github.com/buildpacks/lifecycle/buildpack/layermetadata.go create mode 100644 vendor/github.com/buildpacks/lifecycle/buildpack/layers.go create mode 100644 vendor/github.com/buildpacks/lifecycle/buildpack/store.go create mode 100644 vendor/github.com/buildpacks/lifecycle/cache.go create mode 100644 vendor/github.com/buildpacks/lifecycle/cmd/command.go create mode 100644 vendor/github.com/buildpacks/lifecycle/cmd/exit.go create mode 100644 vendor/github.com/buildpacks/lifecycle/cmd/flags.go create mode 100644 vendor/github.com/buildpacks/lifecycle/cmd/flags_unix.go create mode 100644 vendor/github.com/buildpacks/lifecycle/cmd/flags_windows.go create mode 100644 vendor/github.com/buildpacks/lifecycle/cmd/logs.go create mode 100644 vendor/github.com/buildpacks/lifecycle/cmd/version.go create mode 100644 vendor/github.com/buildpacks/lifecycle/codecov.yml create mode 100644 vendor/github.com/buildpacks/lifecycle/cosign.pub create mode 100644 vendor/github.com/buildpacks/lifecycle/detector.go create mode 100644 vendor/github.com/buildpacks/lifecycle/env/build.go create mode 100644 vendor/github.com/buildpacks/lifecycle/env/env.go create mode 100644 vendor/github.com/buildpacks/lifecycle/env/launch.go create mode 100644 vendor/github.com/buildpacks/lifecycle/env/vars.go create mode 100644 vendor/github.com/buildpacks/lifecycle/exporter.go create mode 100644 vendor/github.com/buildpacks/lifecycle/go.mod create mode 100644 vendor/github.com/buildpacks/lifecycle/go.sum create mode 100644 vendor/github.com/buildpacks/lifecycle/golangci.yaml create mode 100644 vendor/github.com/buildpacks/lifecycle/image/image.go create mode 100644 vendor/github.com/buildpacks/lifecycle/image/utils.go create mode 100644 vendor/github.com/buildpacks/lifecycle/internal/encoding/utils.go create mode 100644 vendor/github.com/buildpacks/lifecycle/internal/io/utils.go create mode 100644 vendor/github.com/buildpacks/lifecycle/internal/layer/logger.go create mode 100644 vendor/github.com/buildpacks/lifecycle/internal/layer/metadata_restorer.go create mode 100644 vendor/github.com/buildpacks/lifecycle/internal/layer/sbom_restorer.go create mode 100644 vendor/github.com/buildpacks/lifecycle/internal/str/utils.go create mode 100644 vendor/github.com/buildpacks/lifecycle/launch/bash.go create mode 100644 vendor/github.com/buildpacks/lifecycle/launch/cmd.go create mode 100644 vendor/github.com/buildpacks/lifecycle/launch/exec_d.go create mode 100644 vendor/github.com/buildpacks/lifecycle/launch/exec_d_unix.go create mode 100644 vendor/github.com/buildpacks/lifecycle/launch/exec_d_windows.go create mode 100644 vendor/github.com/buildpacks/lifecycle/launch/launch.go create mode 100644 vendor/github.com/buildpacks/lifecycle/launch/launcher.go create mode 100644 vendor/github.com/buildpacks/lifecycle/launch/launcher_unix.go create mode 100644 vendor/github.com/buildpacks/lifecycle/launch/launcher_windows.go create mode 100644 vendor/github.com/buildpacks/lifecycle/launch/process.go create mode 100644 vendor/github.com/buildpacks/lifecycle/launch/shell.go create mode 100644 vendor/github.com/buildpacks/lifecycle/layers/digest.go create mode 100644 vendor/github.com/buildpacks/lifecycle/layers/dir.go create mode 100644 vendor/github.com/buildpacks/lifecycle/layers/extract.go create mode 100644 vendor/github.com/buildpacks/lifecycle/layers/factory.go create mode 100644 vendor/github.com/buildpacks/lifecycle/layers/launcher.go create mode 100644 vendor/github.com/buildpacks/lifecycle/layers/slices.go create mode 100644 vendor/github.com/buildpacks/lifecycle/layers/writer.go create mode 100644 vendor/github.com/buildpacks/lifecycle/lifecycle.toml create mode 100644 vendor/github.com/buildpacks/lifecycle/logger.go create mode 100644 vendor/github.com/buildpacks/lifecycle/platform/cache.go create mode 100644 vendor/github.com/buildpacks/lifecycle/platform/exit.go create mode 100644 vendor/github.com/buildpacks/lifecycle/platform/files.go create mode 100644 vendor/github.com/buildpacks/lifecycle/platform/labels.go create mode 100644 vendor/github.com/buildpacks/lifecycle/platform/platform.go create mode 100644 vendor/github.com/buildpacks/lifecycle/rebaser.go create mode 100644 vendor/github.com/buildpacks/lifecycle/restorer.go create mode 100644 vendor/github.com/buildpacks/lifecycle/save.go create mode 100644 vendor/github.com/buildpacks/lifecycle/utils.go create mode 100644 vendor/github.com/buildpacks/pack/.gitignore create mode 100644 vendor/github.com/buildpacks/pack/.gitpod.yml create mode 100644 vendor/github.com/buildpacks/pack/CODEOWNERS create mode 100644 vendor/github.com/buildpacks/pack/CONTRIBUTING.md create mode 100644 vendor/github.com/buildpacks/pack/DEVELOPMENT.md create mode 100644 vendor/github.com/buildpacks/pack/LICENSE create mode 100644 vendor/github.com/buildpacks/pack/Makefile create mode 100644 vendor/github.com/buildpacks/pack/README.md create mode 100644 vendor/github.com/buildpacks/pack/RELEASE.md create mode 100644 vendor/github.com/buildpacks/pack/builder/config_reader.go create mode 100644 vendor/github.com/buildpacks/pack/builder/detection_order.go create mode 100644 vendor/github.com/buildpacks/pack/buildpack.yml create mode 100644 vendor/github.com/buildpacks/pack/buildpackage/config_reader.go create mode 100644 vendor/github.com/buildpacks/pack/codecov.yml create mode 100644 vendor/github.com/buildpacks/pack/go.mod create mode 100644 vendor/github.com/buildpacks/pack/go.sum create mode 100644 vendor/github.com/buildpacks/pack/golangci.yaml create mode 100644 vendor/github.com/buildpacks/pack/internal/build/container_ops.go create mode 100644 vendor/github.com/buildpacks/pack/internal/build/lifecycle_execution.go create mode 100644 vendor/github.com/buildpacks/pack/internal/build/lifecycle_executor.go create mode 100644 vendor/github.com/buildpacks/pack/internal/build/mount_paths.go create mode 100644 vendor/github.com/buildpacks/pack/internal/build/phase.go create mode 100644 vendor/github.com/buildpacks/pack/internal/build/phase_config_provider.go create mode 100644 vendor/github.com/buildpacks/pack/internal/build/phase_factory.go create mode 100644 vendor/github.com/buildpacks/pack/internal/builder/builder.go create mode 100644 vendor/github.com/buildpacks/pack/internal/builder/descriptor.go create mode 100644 vendor/github.com/buildpacks/pack/internal/builder/detection_order_calculator.go create mode 100644 vendor/github.com/buildpacks/pack/internal/builder/image_fetcher_wrapper.go create mode 100644 vendor/github.com/buildpacks/pack/internal/builder/inspect.go create mode 100644 vendor/github.com/buildpacks/pack/internal/builder/label_manager.go create mode 100644 vendor/github.com/buildpacks/pack/internal/builder/label_manager_provider.go create mode 100644 vendor/github.com/buildpacks/pack/internal/builder/lifecycle.go create mode 100644 vendor/github.com/buildpacks/pack/internal/builder/metadata.go create mode 100644 vendor/github.com/buildpacks/pack/internal/builder/suggested_builder.go create mode 100644 vendor/github.com/buildpacks/pack/internal/builder/version.go create mode 100644 vendor/github.com/buildpacks/pack/internal/cache/consts.go create mode 100644 vendor/github.com/buildpacks/pack/internal/cache/image_cache.go create mode 100644 vendor/github.com/buildpacks/pack/internal/cache/volume_cache.go create mode 100644 vendor/github.com/buildpacks/pack/internal/config/config.go create mode 100644 vendor/github.com/buildpacks/pack/internal/config/config_helpers.go create mode 100644 vendor/github.com/buildpacks/pack/internal/container/run.go create mode 100644 vendor/github.com/buildpacks/pack/internal/layer/layer.go create mode 100644 vendor/github.com/buildpacks/pack/internal/layer/writer_factory.go create mode 100644 vendor/github.com/buildpacks/pack/internal/name/name.go create mode 100644 vendor/github.com/buildpacks/pack/internal/paths/paths.go create mode 100644 vendor/github.com/buildpacks/pack/internal/registry/buildpack.go create mode 100644 vendor/github.com/buildpacks/pack/internal/registry/git.go create mode 100644 vendor/github.com/buildpacks/pack/internal/registry/github.go create mode 100644 vendor/github.com/buildpacks/pack/internal/registry/index.go create mode 100644 vendor/github.com/buildpacks/pack/internal/registry/registry_cache.go create mode 100644 vendor/github.com/buildpacks/pack/internal/stack/merge.go create mode 100644 vendor/github.com/buildpacks/pack/internal/stack/mixins.go create mode 100644 vendor/github.com/buildpacks/pack/internal/stringset/stringset.go create mode 100644 vendor/github.com/buildpacks/pack/internal/style/style.go create mode 100644 vendor/github.com/buildpacks/pack/internal/term/term.go create mode 100644 vendor/github.com/buildpacks/pack/internal/termui/branch.go create mode 100644 vendor/github.com/buildpacks/pack/internal/termui/dashboard.go create mode 100644 vendor/github.com/buildpacks/pack/internal/termui/detect.go create mode 100644 vendor/github.com/buildpacks/pack/internal/termui/dive.go create mode 100644 vendor/github.com/buildpacks/pack/internal/termui/logger.go create mode 100644 vendor/github.com/buildpacks/pack/internal/termui/termui.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/archive/archive.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/archive/tar_builder.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/blob/blob.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/blob/downloader.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/buildpack/builder.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/buildpack/buildpack.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/buildpack/buildpackage.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/buildpack/downloader.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/buildpack/locator_type.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/buildpack/oci_layout_package.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/buildpack/package.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/buildpack/parse_name.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/client/build.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/client/client.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/client/common.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/client/create_builder.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/client/download_sbom.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/client/errors.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/client/inspect_builder.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/client/inspect_buildpack.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/client/inspect_image.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/client/new_buildpack.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/client/package_buildpack.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/client/pull_buildpack.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/client/rebase.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/client/register_buildpack.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/client/version.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/client/yank_buildpack.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/dist/buildpack.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/dist/buildpack_descriptor.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/dist/dist.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/dist/distribution.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/dist/image.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/dist/layers.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/image/fetcher.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/image/pull_policy.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/logging/logger_simple.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/logging/logger_writers.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/logging/logging.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/logging/prefix_writer.go create mode 100644 vendor/github.com/buildpacks/pack/pkg/project/types/types.go create mode 100644 vendor/github.com/buildpacks/pack/project.toml create mode 100644 vendor/github.com/buildpacks/pack/version.go create mode 100644 vendor/github.com/containerd/cgroups/LICENSE create mode 100644 vendor/github.com/containerd/cgroups/stats/v1/doc.go create mode 100644 vendor/github.com/containerd/cgroups/stats/v1/metrics.pb.go create mode 100644 vendor/github.com/containerd/cgroups/stats/v1/metrics.pb.txt create mode 100644 vendor/github.com/containerd/cgroups/stats/v1/metrics.proto create mode 100644 vendor/github.com/containerd/containerd/LICENSE create mode 100644 vendor/github.com/containerd/containerd/NOTICE create mode 100644 vendor/github.com/containerd/containerd/errdefs/errors.go create mode 100644 vendor/github.com/containerd/containerd/errdefs/grpc.go create mode 100644 vendor/github.com/containerd/containerd/log/context.go create mode 100644 vendor/github.com/containerd/containerd/pkg/userns/userns_linux.go create mode 100644 vendor/github.com/containerd/containerd/pkg/userns/userns_unsupported.go create mode 100644 vendor/github.com/containerd/containerd/platforms/compare.go create mode 100644 vendor/github.com/containerd/containerd/platforms/cpuinfo.go create mode 100644 vendor/github.com/containerd/containerd/platforms/database.go create mode 100644 vendor/github.com/containerd/containerd/platforms/defaults.go create mode 100644 vendor/github.com/containerd/containerd/platforms/defaults_darwin.go create mode 100644 vendor/github.com/containerd/containerd/platforms/defaults_unix.go create mode 100644 vendor/github.com/containerd/containerd/platforms/defaults_windows.go create mode 100644 vendor/github.com/containerd/containerd/platforms/platforms.go create mode 100644 vendor/github.com/containerd/containerd/sys/epoll.go create mode 100644 vendor/github.com/containerd/containerd/sys/fds.go create mode 100644 vendor/github.com/containerd/containerd/sys/filesys_unix.go create mode 100644 vendor/github.com/containerd/containerd/sys/filesys_windows.go create mode 100644 vendor/github.com/containerd/containerd/sys/oom_linux.go create mode 100644 vendor/github.com/containerd/containerd/sys/oom_unsupported.go create mode 100644 vendor/github.com/containerd/containerd/sys/socket_unix.go create mode 100644 vendor/github.com/containerd/containerd/sys/socket_windows.go create mode 100644 vendor/github.com/containerd/containerd/sys/userns_deprecated.go create mode 100644 vendor/github.com/containerd/stargz-snapshotter/estargz/LICENSE create mode 100644 vendor/github.com/containerd/stargz-snapshotter/estargz/build.go create mode 100644 vendor/github.com/containerd/stargz-snapshotter/estargz/errorutil/errors.go create mode 100644 vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go create mode 100644 vendor/github.com/containerd/stargz-snapshotter/estargz/go.mod create mode 100644 vendor/github.com/containerd/stargz-snapshotter/estargz/go.sum create mode 100644 vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go create mode 100644 vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go create mode 100644 vendor/github.com/containerd/stargz-snapshotter/estargz/types.go create mode 100644 vendor/github.com/containers/image/v5/LICENSE create mode 100644 vendor/github.com/containers/image/v5/docker/reference/README.md create mode 100644 vendor/github.com/containers/image/v5/docker/reference/helpers.go create mode 100644 vendor/github.com/containers/image/v5/docker/reference/normalize.go create mode 100644 vendor/github.com/containers/image/v5/docker/reference/reference.go create mode 100644 vendor/github.com/containers/image/v5/docker/reference/regexp.go create mode 100644 vendor/github.com/containers/image/v5/internal/pkg/keyctl/key.go create mode 100644 vendor/github.com/containers/image/v5/internal/pkg/keyctl/keyring.go create mode 100644 vendor/github.com/containers/image/v5/internal/pkg/keyctl/perm.go create mode 100644 vendor/github.com/containers/image/v5/internal/pkg/keyctl/sys_linux.go create mode 100644 vendor/github.com/containers/image/v5/internal/rootless/rootless.go create mode 100644 vendor/github.com/containers/image/v5/pkg/compression/internal/types.go create mode 100644 vendor/github.com/containers/image/v5/pkg/compression/types/types.go create mode 100644 vendor/github.com/containers/image/v5/pkg/docker/config/config.go create mode 100644 vendor/github.com/containers/image/v5/pkg/docker/config/config_linux.go create mode 100644 vendor/github.com/containers/image/v5/pkg/docker/config/config_unsupported.go create mode 100644 vendor/github.com/containers/image/v5/pkg/sysregistriesv2/shortnames.go create mode 100644 vendor/github.com/containers/image/v5/pkg/sysregistriesv2/system_registries_v2.go create mode 100644 vendor/github.com/containers/image/v5/types/types.go create mode 100644 vendor/github.com/containers/storage/AUTHORS create mode 100644 vendor/github.com/containers/storage/LICENSE create mode 100644 vendor/github.com/containers/storage/NOTICE create mode 100644 vendor/github.com/containers/storage/pkg/homedir/homedir.go create mode 100644 vendor/github.com/containers/storage/pkg/homedir/homedir_others.go create mode 100644 vendor/github.com/containers/storage/pkg/homedir/homedir_unix.go create mode 100644 vendor/github.com/containers/storage/pkg/homedir/homedir_windows.go create mode 100644 vendor/github.com/containers/storage/pkg/idtools/idtools.go create mode 100644 vendor/github.com/containers/storage/pkg/idtools/idtools_supported.go create mode 100644 vendor/github.com/containers/storage/pkg/idtools/idtools_unix.go create mode 100644 vendor/github.com/containers/storage/pkg/idtools/idtools_unsupported.go create mode 100644 vendor/github.com/containers/storage/pkg/idtools/idtools_windows.go create mode 100644 vendor/github.com/containers/storage/pkg/idtools/parser.go create mode 100644 vendor/github.com/containers/storage/pkg/idtools/usergroupadd_linux.go create mode 100644 vendor/github.com/containers/storage/pkg/idtools/usergroupadd_unsupported.go create mode 100644 vendor/github.com/containers/storage/pkg/idtools/utils_unix.go create mode 100644 vendor/github.com/containers/storage/pkg/lockfile/lockfile.go create mode 100644 vendor/github.com/containers/storage/pkg/lockfile/lockfile_unix.go create mode 100644 vendor/github.com/containers/storage/pkg/lockfile/lockfile_windows.go create mode 100644 vendor/github.com/containers/storage/pkg/mount/flags.go create mode 100644 vendor/github.com/containers/storage/pkg/mount/flags_linux.go create mode 100644 vendor/github.com/containers/storage/pkg/mount/flags_unsupported.go create mode 100644 vendor/github.com/containers/storage/pkg/mount/mount.go create mode 100644 vendor/github.com/containers/storage/pkg/mount/mounter_freebsd.go create mode 100644 vendor/github.com/containers/storage/pkg/mount/mounter_linux.go create mode 100644 vendor/github.com/containers/storage/pkg/mount/mounter_unsupported.go create mode 100644 vendor/github.com/containers/storage/pkg/mount/mountinfo.go create mode 100644 vendor/github.com/containers/storage/pkg/mount/mountinfo_linux.go create mode 100644 vendor/github.com/containers/storage/pkg/mount/sharedsubtree_linux.go create mode 100644 vendor/github.com/containers/storage/pkg/mount/unmount_unix.go create mode 100644 vendor/github.com/containers/storage/pkg/mount/unmount_unsupported.go create mode 100644 vendor/github.com/containers/storage/pkg/reexec/README.md create mode 100644 vendor/github.com/containers/storage/pkg/reexec/command_linux.go create mode 100644 vendor/github.com/containers/storage/pkg/reexec/command_unix.go create mode 100644 vendor/github.com/containers/storage/pkg/reexec/command_unsupported.go create mode 100644 vendor/github.com/containers/storage/pkg/reexec/command_windows.go create mode 100644 vendor/github.com/containers/storage/pkg/reexec/reexec.go create mode 100644 vendor/github.com/containers/storage/pkg/stringid/README.md create mode 100644 vendor/github.com/containers/storage/pkg/stringid/stringid.go create mode 100644 vendor/github.com/containers/storage/pkg/system/chmod.go create mode 100644 vendor/github.com/containers/storage/pkg/system/chtimes.go create mode 100644 vendor/github.com/containers/storage/pkg/system/chtimes_unix.go create mode 100644 vendor/github.com/containers/storage/pkg/system/chtimes_windows.go create mode 100644 vendor/github.com/containers/storage/pkg/system/errors.go create mode 100644 vendor/github.com/containers/storage/pkg/system/exitcode.go create mode 100644 vendor/github.com/containers/storage/pkg/system/init.go create mode 100644 vendor/github.com/containers/storage/pkg/system/init_windows.go create mode 100644 vendor/github.com/containers/storage/pkg/system/lchown.go create mode 100644 vendor/github.com/containers/storage/pkg/system/lcow_unix.go create mode 100644 vendor/github.com/containers/storage/pkg/system/lcow_windows.go create mode 100644 vendor/github.com/containers/storage/pkg/system/lstat_unix.go create mode 100644 vendor/github.com/containers/storage/pkg/system/lstat_windows.go create mode 100644 vendor/github.com/containers/storage/pkg/system/meminfo.go create mode 100644 vendor/github.com/containers/storage/pkg/system/meminfo_linux.go create mode 100644 vendor/github.com/containers/storage/pkg/system/meminfo_solaris.go create mode 100644 vendor/github.com/containers/storage/pkg/system/meminfo_unsupported.go create mode 100644 vendor/github.com/containers/storage/pkg/system/meminfo_windows.go create mode 100644 vendor/github.com/containers/storage/pkg/system/mknod.go create mode 100644 vendor/github.com/containers/storage/pkg/system/mknod_freebsd.go create mode 100644 vendor/github.com/containers/storage/pkg/system/mknod_windows.go create mode 100644 vendor/github.com/containers/storage/pkg/system/path.go create mode 100644 vendor/github.com/containers/storage/pkg/system/path_unix.go create mode 100644 vendor/github.com/containers/storage/pkg/system/path_windows.go create mode 100644 vendor/github.com/containers/storage/pkg/system/process_unix.go create mode 100644 vendor/github.com/containers/storage/pkg/system/rm.go create mode 100644 vendor/github.com/containers/storage/pkg/system/stat_darwin.go create mode 100644 vendor/github.com/containers/storage/pkg/system/stat_freebsd.go create mode 100644 vendor/github.com/containers/storage/pkg/system/stat_linux.go create mode 100644 vendor/github.com/containers/storage/pkg/system/stat_openbsd.go create mode 100644 vendor/github.com/containers/storage/pkg/system/stat_solaris.go create mode 100644 vendor/github.com/containers/storage/pkg/system/stat_unix.go create mode 100644 vendor/github.com/containers/storage/pkg/system/stat_windows.go create mode 100644 vendor/github.com/containers/storage/pkg/system/syscall_unix.go create mode 100644 vendor/github.com/containers/storage/pkg/system/syscall_windows.go create mode 100644 vendor/github.com/containers/storage/pkg/system/umask.go create mode 100644 vendor/github.com/containers/storage/pkg/system/umask_windows.go create mode 100644 vendor/github.com/containers/storage/pkg/system/utimes_freebsd.go create mode 100644 vendor/github.com/containers/storage/pkg/system/utimes_linux.go create mode 100644 vendor/github.com/containers/storage/pkg/system/utimes_unsupported.go create mode 100644 vendor/github.com/containers/storage/pkg/system/xattrs_linux.go create mode 100644 vendor/github.com/containers/storage/pkg/system/xattrs_unsupported.go create mode 100644 vendor/github.com/containers/storage/pkg/unshare/getenv_linux_cgo.go create mode 100644 vendor/github.com/containers/storage/pkg/unshare/getenv_linux_nocgo.go create mode 100644 vendor/github.com/containers/storage/pkg/unshare/unshare.c create mode 100644 vendor/github.com/containers/storage/pkg/unshare/unshare.go create mode 100644 vendor/github.com/containers/storage/pkg/unshare/unshare_cgo.go create mode 100644 vendor/github.com/containers/storage/pkg/unshare/unshare_gccgo.go create mode 100644 vendor/github.com/containers/storage/pkg/unshare/unshare_linux.go create mode 100644 vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported.go create mode 100644 vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported_cgo.go create mode 100644 vendor/github.com/dgraph-io/ristretto/.deepsource.toml create mode 100644 vendor/github.com/dgraph-io/ristretto/CHANGELOG.md create mode 100644 vendor/github.com/dgraph-io/ristretto/LICENSE create mode 100644 vendor/github.com/dgraph-io/ristretto/README.md create mode 100644 vendor/github.com/dgraph-io/ristretto/cache.go create mode 100644 vendor/github.com/dgraph-io/ristretto/go.mod create mode 100644 vendor/github.com/dgraph-io/ristretto/go.sum create mode 100644 vendor/github.com/dgraph-io/ristretto/policy.go create mode 100644 vendor/github.com/dgraph-io/ristretto/ring.go create mode 100644 vendor/github.com/dgraph-io/ristretto/sketch.go create mode 100644 vendor/github.com/dgraph-io/ristretto/store.go create mode 100644 vendor/github.com/dgraph-io/ristretto/test.sh create mode 100644 vendor/github.com/dgraph-io/ristretto/ttl.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/LICENSE create mode 100644 vendor/github.com/dgraph-io/ristretto/z/README.md create mode 100644 vendor/github.com/dgraph-io/ristretto/z/allocator.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/bbloom.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/btree.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/buffer.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/calloc.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/calloc_32bit.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/calloc_64bit.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/calloc_jemalloc.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/calloc_nojemalloc.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/file.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/file_default.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/file_linux.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/flags.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/histogram.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/mmap.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/mmap_darwin.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/mmap_linux.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/mmap_plan9.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/mmap_unix.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/mmap_windows.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/rtutil.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/rtutil.s create mode 100644 vendor/github.com/dgraph-io/ristretto/z/simd/baseline.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/simd/search.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/simd/search_amd64.s create mode 100644 vendor/github.com/dgraph-io/ristretto/z/simd/stub_search_amd64.go create mode 100644 vendor/github.com/dgraph-io/ristretto/z/z.go create mode 100644 vendor/github.com/docker/cli/AUTHORS create mode 100644 vendor/github.com/docker/cli/LICENSE create mode 100644 vendor/github.com/docker/cli/NOTICE create mode 100644 vendor/github.com/docker/cli/cli/config/config.go create mode 100644 vendor/github.com/docker/cli/cli/config/configfile/file.go create mode 100644 vendor/github.com/docker/cli/cli/config/configfile/file_unix.go create mode 100644 vendor/github.com/docker/cli/cli/config/configfile/file_windows.go create mode 100644 vendor/github.com/docker/cli/cli/config/credentials/credentials.go create mode 100644 vendor/github.com/docker/cli/cli/config/credentials/default_store.go create mode 100644 vendor/github.com/docker/cli/cli/config/credentials/default_store_darwin.go create mode 100644 vendor/github.com/docker/cli/cli/config/credentials/default_store_linux.go create mode 100644 vendor/github.com/docker/cli/cli/config/credentials/default_store_unsupported.go create mode 100644 vendor/github.com/docker/cli/cli/config/credentials/default_store_windows.go create mode 100644 vendor/github.com/docker/cli/cli/config/credentials/file_store.go create mode 100644 vendor/github.com/docker/cli/cli/config/credentials/native_store.go create mode 100644 vendor/github.com/docker/cli/cli/config/types/authconfig.go create mode 100644 vendor/github.com/docker/cli/cli/connhelper/commandconn/commandconn.go create mode 100644 vendor/github.com/docker/cli/cli/connhelper/commandconn/pdeathsig_linux.go create mode 100644 vendor/github.com/docker/cli/cli/connhelper/commandconn/pdeathsig_nolinux.go create mode 100644 vendor/github.com/docker/cli/cli/connhelper/commandconn/session_unix.go create mode 100644 vendor/github.com/docker/cli/cli/connhelper/commandconn/session_windows.go create mode 100644 vendor/github.com/docker/cli/cli/connhelper/connhelper.go create mode 100644 vendor/github.com/docker/cli/cli/connhelper/ssh/ssh.go create mode 100644 vendor/github.com/docker/distribution/LICENSE create mode 100644 vendor/github.com/docker/distribution/digestset/set.go create mode 100644 vendor/github.com/docker/distribution/reference/helpers.go create mode 100644 vendor/github.com/docker/distribution/reference/normalize.go create mode 100644 vendor/github.com/docker/distribution/reference/reference.go create mode 100644 vendor/github.com/docker/distribution/reference/regexp.go create mode 100644 vendor/github.com/docker/distribution/registry/api/errcode/errors.go create mode 100644 vendor/github.com/docker/distribution/registry/api/errcode/handler.go create mode 100644 vendor/github.com/docker/distribution/registry/api/errcode/register.go create mode 100644 vendor/github.com/docker/distribution/registry/client/auth/challenge/addr.go create mode 100644 vendor/github.com/docker/distribution/registry/client/auth/challenge/authchallenge.go create mode 100644 vendor/github.com/docker/docker-credential-helpers/LICENSE create mode 100644 vendor/github.com/docker/docker-credential-helpers/client/client.go create mode 100644 vendor/github.com/docker/docker-credential-helpers/client/command.go create mode 100644 vendor/github.com/docker/docker-credential-helpers/credentials/credentials.go create mode 100644 vendor/github.com/docker/docker-credential-helpers/credentials/error.go create mode 100644 vendor/github.com/docker/docker-credential-helpers/credentials/helper.go create mode 100644 vendor/github.com/docker/docker-credential-helpers/credentials/version.go create mode 100644 vendor/github.com/docker/docker/AUTHORS create mode 100644 vendor/github.com/docker/docker/LICENSE create mode 100644 vendor/github.com/docker/docker/NOTICE create mode 100644 vendor/github.com/docker/docker/api/README.md create mode 100644 vendor/github.com/docker/docker/api/common.go create mode 100644 vendor/github.com/docker/docker/api/common_unix.go create mode 100644 vendor/github.com/docker/docker/api/common_windows.go create mode 100644 vendor/github.com/docker/docker/api/swagger-gen.yaml create mode 100644 vendor/github.com/docker/docker/api/swagger.yaml create mode 100644 vendor/github.com/docker/docker/api/types/auth.go create mode 100644 vendor/github.com/docker/docker/api/types/blkiodev/blkio.go create mode 100644 vendor/github.com/docker/docker/api/types/client.go create mode 100644 vendor/github.com/docker/docker/api/types/configs.go create mode 100644 vendor/github.com/docker/docker/api/types/container/config.go create mode 100644 vendor/github.com/docker/docker/api/types/container/container_changes.go create mode 100644 vendor/github.com/docker/docker/api/types/container/container_create.go create mode 100644 vendor/github.com/docker/docker/api/types/container/container_top.go create mode 100644 vendor/github.com/docker/docker/api/types/container/container_update.go create mode 100644 vendor/github.com/docker/docker/api/types/container/container_wait.go create mode 100644 vendor/github.com/docker/docker/api/types/container/host_config.go create mode 100644 vendor/github.com/docker/docker/api/types/container/hostconfig_unix.go create mode 100644 vendor/github.com/docker/docker/api/types/container/hostconfig_windows.go create mode 100644 vendor/github.com/docker/docker/api/types/container/waitcondition.go create mode 100644 vendor/github.com/docker/docker/api/types/error_response.go create mode 100644 vendor/github.com/docker/docker/api/types/error_response_ext.go create mode 100644 vendor/github.com/docker/docker/api/types/events/events.go create mode 100644 vendor/github.com/docker/docker/api/types/filters/parse.go create mode 100644 vendor/github.com/docker/docker/api/types/graph_driver_data.go create mode 100644 vendor/github.com/docker/docker/api/types/id_response.go create mode 100644 vendor/github.com/docker/docker/api/types/image/image_history.go create mode 100644 vendor/github.com/docker/docker/api/types/image_delete_response_item.go create mode 100644 vendor/github.com/docker/docker/api/types/image_summary.go create mode 100644 vendor/github.com/docker/docker/api/types/mount/mount.go create mode 100644 vendor/github.com/docker/docker/api/types/network/network.go create mode 100644 vendor/github.com/docker/docker/api/types/plugin.go create mode 100644 vendor/github.com/docker/docker/api/types/plugin_device.go create mode 100644 vendor/github.com/docker/docker/api/types/plugin_env.go create mode 100644 vendor/github.com/docker/docker/api/types/plugin_interface_type.go create mode 100644 vendor/github.com/docker/docker/api/types/plugin_mount.go create mode 100644 vendor/github.com/docker/docker/api/types/plugin_responses.go create mode 100644 vendor/github.com/docker/docker/api/types/port.go create mode 100644 vendor/github.com/docker/docker/api/types/registry/authenticate.go create mode 100644 vendor/github.com/docker/docker/api/types/registry/registry.go create mode 100644 vendor/github.com/docker/docker/api/types/service_update_response.go create mode 100644 vendor/github.com/docker/docker/api/types/stats.go create mode 100644 vendor/github.com/docker/docker/api/types/strslice/strslice.go create mode 100644 vendor/github.com/docker/docker/api/types/swarm/common.go create mode 100644 vendor/github.com/docker/docker/api/types/swarm/config.go create mode 100644 vendor/github.com/docker/docker/api/types/swarm/container.go create mode 100644 vendor/github.com/docker/docker/api/types/swarm/network.go create mode 100644 vendor/github.com/docker/docker/api/types/swarm/node.go create mode 100644 vendor/github.com/docker/docker/api/types/swarm/runtime.go create mode 100644 vendor/github.com/docker/docker/api/types/swarm/runtime/gen.go create mode 100644 vendor/github.com/docker/docker/api/types/swarm/runtime/plugin.pb.go create mode 100644 vendor/github.com/docker/docker/api/types/swarm/runtime/plugin.proto create mode 100644 vendor/github.com/docker/docker/api/types/swarm/secret.go create mode 100644 vendor/github.com/docker/docker/api/types/swarm/service.go create mode 100644 vendor/github.com/docker/docker/api/types/swarm/swarm.go create mode 100644 vendor/github.com/docker/docker/api/types/swarm/task.go create mode 100644 vendor/github.com/docker/docker/api/types/time/duration_convert.go create mode 100644 vendor/github.com/docker/docker/api/types/time/timestamp.go create mode 100644 vendor/github.com/docker/docker/api/types/types.go create mode 100644 vendor/github.com/docker/docker/api/types/versions/README.md create mode 100644 vendor/github.com/docker/docker/api/types/versions/compare.go create mode 100644 vendor/github.com/docker/docker/api/types/volume.go create mode 100644 vendor/github.com/docker/docker/api/types/volume/volume_create.go create mode 100644 vendor/github.com/docker/docker/api/types/volume/volume_list.go create mode 100644 vendor/github.com/docker/docker/cli/config/configdir.go create mode 100644 vendor/github.com/docker/docker/client/README.md create mode 100644 vendor/github.com/docker/docker/client/build_cancel.go create mode 100644 vendor/github.com/docker/docker/client/build_prune.go create mode 100644 vendor/github.com/docker/docker/client/checkpoint_create.go create mode 100644 vendor/github.com/docker/docker/client/checkpoint_delete.go create mode 100644 vendor/github.com/docker/docker/client/checkpoint_list.go create mode 100644 vendor/github.com/docker/docker/client/client.go create mode 100644 vendor/github.com/docker/docker/client/client_deprecated.go create mode 100644 vendor/github.com/docker/docker/client/client_unix.go create mode 100644 vendor/github.com/docker/docker/client/client_windows.go create mode 100644 vendor/github.com/docker/docker/client/config_create.go create mode 100644 vendor/github.com/docker/docker/client/config_inspect.go create mode 100644 vendor/github.com/docker/docker/client/config_list.go create mode 100644 vendor/github.com/docker/docker/client/config_remove.go create mode 100644 vendor/github.com/docker/docker/client/config_update.go create mode 100644 vendor/github.com/docker/docker/client/container_attach.go create mode 100644 vendor/github.com/docker/docker/client/container_commit.go create mode 100644 vendor/github.com/docker/docker/client/container_copy.go create mode 100644 vendor/github.com/docker/docker/client/container_create.go create mode 100644 vendor/github.com/docker/docker/client/container_diff.go create mode 100644 vendor/github.com/docker/docker/client/container_exec.go create mode 100644 vendor/github.com/docker/docker/client/container_export.go create mode 100644 vendor/github.com/docker/docker/client/container_inspect.go create mode 100644 vendor/github.com/docker/docker/client/container_kill.go create mode 100644 vendor/github.com/docker/docker/client/container_list.go create mode 100644 vendor/github.com/docker/docker/client/container_logs.go create mode 100644 vendor/github.com/docker/docker/client/container_pause.go create mode 100644 vendor/github.com/docker/docker/client/container_prune.go create mode 100644 vendor/github.com/docker/docker/client/container_remove.go create mode 100644 vendor/github.com/docker/docker/client/container_rename.go create mode 100644 vendor/github.com/docker/docker/client/container_resize.go create mode 100644 vendor/github.com/docker/docker/client/container_restart.go create mode 100644 vendor/github.com/docker/docker/client/container_start.go create mode 100644 vendor/github.com/docker/docker/client/container_stats.go create mode 100644 vendor/github.com/docker/docker/client/container_stop.go create mode 100644 vendor/github.com/docker/docker/client/container_top.go create mode 100644 vendor/github.com/docker/docker/client/container_unpause.go create mode 100644 vendor/github.com/docker/docker/client/container_update.go create mode 100644 vendor/github.com/docker/docker/client/container_wait.go create mode 100644 vendor/github.com/docker/docker/client/disk_usage.go create mode 100644 vendor/github.com/docker/docker/client/distribution_inspect.go create mode 100644 vendor/github.com/docker/docker/client/errors.go create mode 100644 vendor/github.com/docker/docker/client/events.go create mode 100644 vendor/github.com/docker/docker/client/hijack.go create mode 100644 vendor/github.com/docker/docker/client/image_build.go create mode 100644 vendor/github.com/docker/docker/client/image_create.go create mode 100644 vendor/github.com/docker/docker/client/image_history.go create mode 100644 vendor/github.com/docker/docker/client/image_import.go create mode 100644 vendor/github.com/docker/docker/client/image_inspect.go create mode 100644 vendor/github.com/docker/docker/client/image_list.go create mode 100644 vendor/github.com/docker/docker/client/image_load.go create mode 100644 vendor/github.com/docker/docker/client/image_prune.go create mode 100644 vendor/github.com/docker/docker/client/image_pull.go create mode 100644 vendor/github.com/docker/docker/client/image_push.go create mode 100644 vendor/github.com/docker/docker/client/image_remove.go create mode 100644 vendor/github.com/docker/docker/client/image_save.go create mode 100644 vendor/github.com/docker/docker/client/image_search.go create mode 100644 vendor/github.com/docker/docker/client/image_tag.go create mode 100644 vendor/github.com/docker/docker/client/info.go create mode 100644 vendor/github.com/docker/docker/client/interface.go create mode 100644 vendor/github.com/docker/docker/client/interface_experimental.go create mode 100644 vendor/github.com/docker/docker/client/interface_stable.go create mode 100644 vendor/github.com/docker/docker/client/login.go create mode 100644 vendor/github.com/docker/docker/client/network_connect.go create mode 100644 vendor/github.com/docker/docker/client/network_create.go create mode 100644 vendor/github.com/docker/docker/client/network_disconnect.go create mode 100644 vendor/github.com/docker/docker/client/network_inspect.go create mode 100644 vendor/github.com/docker/docker/client/network_list.go create mode 100644 vendor/github.com/docker/docker/client/network_prune.go create mode 100644 vendor/github.com/docker/docker/client/network_remove.go create mode 100644 vendor/github.com/docker/docker/client/node_inspect.go create mode 100644 vendor/github.com/docker/docker/client/node_list.go create mode 100644 vendor/github.com/docker/docker/client/node_remove.go create mode 100644 vendor/github.com/docker/docker/client/node_update.go create mode 100644 vendor/github.com/docker/docker/client/options.go create mode 100644 vendor/github.com/docker/docker/client/ping.go create mode 100644 vendor/github.com/docker/docker/client/plugin_create.go create mode 100644 vendor/github.com/docker/docker/client/plugin_disable.go create mode 100644 vendor/github.com/docker/docker/client/plugin_enable.go create mode 100644 vendor/github.com/docker/docker/client/plugin_inspect.go create mode 100644 vendor/github.com/docker/docker/client/plugin_install.go create mode 100644 vendor/github.com/docker/docker/client/plugin_list.go create mode 100644 vendor/github.com/docker/docker/client/plugin_push.go create mode 100644 vendor/github.com/docker/docker/client/plugin_remove.go create mode 100644 vendor/github.com/docker/docker/client/plugin_set.go create mode 100644 vendor/github.com/docker/docker/client/plugin_upgrade.go create mode 100644 vendor/github.com/docker/docker/client/request.go create mode 100644 vendor/github.com/docker/docker/client/secret_create.go create mode 100644 vendor/github.com/docker/docker/client/secret_inspect.go create mode 100644 vendor/github.com/docker/docker/client/secret_list.go create mode 100644 vendor/github.com/docker/docker/client/secret_remove.go create mode 100644 vendor/github.com/docker/docker/client/secret_update.go create mode 100644 vendor/github.com/docker/docker/client/service_create.go create mode 100644 vendor/github.com/docker/docker/client/service_inspect.go create mode 100644 vendor/github.com/docker/docker/client/service_list.go create mode 100644 vendor/github.com/docker/docker/client/service_logs.go create mode 100644 vendor/github.com/docker/docker/client/service_remove.go create mode 100644 vendor/github.com/docker/docker/client/service_update.go create mode 100644 vendor/github.com/docker/docker/client/swarm_get_unlock_key.go create mode 100644 vendor/github.com/docker/docker/client/swarm_init.go create mode 100644 vendor/github.com/docker/docker/client/swarm_inspect.go create mode 100644 vendor/github.com/docker/docker/client/swarm_join.go create mode 100644 vendor/github.com/docker/docker/client/swarm_leave.go create mode 100644 vendor/github.com/docker/docker/client/swarm_unlock.go create mode 100644 vendor/github.com/docker/docker/client/swarm_update.go create mode 100644 vendor/github.com/docker/docker/client/task_inspect.go create mode 100644 vendor/github.com/docker/docker/client/task_list.go create mode 100644 vendor/github.com/docker/docker/client/task_logs.go create mode 100644 vendor/github.com/docker/docker/client/transport.go create mode 100644 vendor/github.com/docker/docker/client/utils.go create mode 100644 vendor/github.com/docker/docker/client/version.go create mode 100644 vendor/github.com/docker/docker/client/volume_create.go create mode 100644 vendor/github.com/docker/docker/client/volume_inspect.go create mode 100644 vendor/github.com/docker/docker/client/volume_list.go create mode 100644 vendor/github.com/docker/docker/client/volume_prune.go create mode 100644 vendor/github.com/docker/docker/client/volume_remove.go create mode 100644 vendor/github.com/docker/docker/errdefs/defs.go create mode 100644 vendor/github.com/docker/docker/errdefs/doc.go create mode 100644 vendor/github.com/docker/docker/errdefs/helpers.go create mode 100644 vendor/github.com/docker/docker/errdefs/http_helpers.go create mode 100644 vendor/github.com/docker/docker/errdefs/is.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/README.md create mode 100644 vendor/github.com/docker/docker/pkg/archive/archive.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/archive_linux.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/archive_other.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/archive_unix.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/archive_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/changes.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/changes_linux.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/changes_other.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/changes_unix.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/changes_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/copy.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/copy_unix.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/copy_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/diff.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/time_linux.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/time_unsupported.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/whiteouts.go create mode 100644 vendor/github.com/docker/docker/pkg/archive/wrap.go create mode 100644 vendor/github.com/docker/docker/pkg/fileutils/fileutils.go create mode 100644 vendor/github.com/docker/docker/pkg/fileutils/fileutils_darwin.go create mode 100644 vendor/github.com/docker/docker/pkg/fileutils/fileutils_unix.go create mode 100644 vendor/github.com/docker/docker/pkg/fileutils/fileutils_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/homedir/homedir_linux.go create mode 100644 vendor/github.com/docker/docker/pkg/homedir/homedir_others.go create mode 100644 vendor/github.com/docker/docker/pkg/homedir/homedir_unix.go create mode 100644 vendor/github.com/docker/docker/pkg/homedir/homedir_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/idtools/idtools.go create mode 100644 vendor/github.com/docker/docker/pkg/idtools/idtools_unix.go create mode 100644 vendor/github.com/docker/docker/pkg/idtools/idtools_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/idtools/usergroupadd_linux.go create mode 100644 vendor/github.com/docker/docker/pkg/idtools/usergroupadd_unsupported.go create mode 100644 vendor/github.com/docker/docker/pkg/idtools/utils_unix.go create mode 100644 vendor/github.com/docker/docker/pkg/ioutils/buffer.go create mode 100644 vendor/github.com/docker/docker/pkg/ioutils/bytespipe.go create mode 100644 vendor/github.com/docker/docker/pkg/ioutils/fswriters.go create mode 100644 vendor/github.com/docker/docker/pkg/ioutils/readers.go create mode 100644 vendor/github.com/docker/docker/pkg/ioutils/temp_unix.go create mode 100644 vendor/github.com/docker/docker/pkg/ioutils/temp_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/ioutils/writeflusher.go create mode 100644 vendor/github.com/docker/docker/pkg/ioutils/writers.go create mode 100644 vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go create mode 100644 vendor/github.com/docker/docker/pkg/longpath/longpath.go create mode 100644 vendor/github.com/docker/docker/pkg/pools/pools.go create mode 100644 vendor/github.com/docker/docker/pkg/stdcopy/stdcopy.go create mode 100644 vendor/github.com/docker/docker/pkg/stringid/README.md create mode 100644 vendor/github.com/docker/docker/pkg/stringid/stringid.go create mode 100644 vendor/github.com/docker/docker/pkg/system/args_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/system/chtimes.go create mode 100644 vendor/github.com/docker/docker/pkg/system/chtimes_nowindows.go create mode 100644 vendor/github.com/docker/docker/pkg/system/chtimes_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/system/errors.go create mode 100644 vendor/github.com/docker/docker/pkg/system/exitcode.go create mode 100644 vendor/github.com/docker/docker/pkg/system/filesys_unix.go create mode 100644 vendor/github.com/docker/docker/pkg/system/filesys_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/system/init.go create mode 100644 vendor/github.com/docker/docker/pkg/system/init_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/system/lcow.go create mode 100644 vendor/github.com/docker/docker/pkg/system/lcow_unsupported.go create mode 100644 vendor/github.com/docker/docker/pkg/system/lstat_unix.go create mode 100644 vendor/github.com/docker/docker/pkg/system/lstat_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/system/meminfo.go create mode 100644 vendor/github.com/docker/docker/pkg/system/meminfo_linux.go create mode 100644 vendor/github.com/docker/docker/pkg/system/meminfo_unsupported.go create mode 100644 vendor/github.com/docker/docker/pkg/system/meminfo_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/system/mknod.go create mode 100644 vendor/github.com/docker/docker/pkg/system/mknod_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/system/path.go create mode 100644 vendor/github.com/docker/docker/pkg/system/path_unix.go create mode 100644 vendor/github.com/docker/docker/pkg/system/path_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/system/process_unix.go create mode 100644 vendor/github.com/docker/docker/pkg/system/process_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/system/rm.go create mode 100644 vendor/github.com/docker/docker/pkg/system/rm_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/system/stat_bsd.go create mode 100644 vendor/github.com/docker/docker/pkg/system/stat_darwin.go create mode 100644 vendor/github.com/docker/docker/pkg/system/stat_linux.go create mode 100644 vendor/github.com/docker/docker/pkg/system/stat_openbsd.go create mode 100644 vendor/github.com/docker/docker/pkg/system/stat_solaris.go create mode 100644 vendor/github.com/docker/docker/pkg/system/stat_unix.go create mode 100644 vendor/github.com/docker/docker/pkg/system/stat_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/system/syscall_unix.go create mode 100644 vendor/github.com/docker/docker/pkg/system/syscall_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/system/umask.go create mode 100644 vendor/github.com/docker/docker/pkg/system/umask_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/system/utimes_unix.go create mode 100644 vendor/github.com/docker/docker/pkg/system/utimes_unsupported.go create mode 100644 vendor/github.com/docker/docker/pkg/system/xattrs_linux.go create mode 100644 vendor/github.com/docker/docker/pkg/system/xattrs_unsupported.go create mode 100644 vendor/github.com/docker/docker/volume/mounts/lcow_parser.go create mode 100644 vendor/github.com/docker/docker/volume/mounts/linux_parser.go create mode 100644 vendor/github.com/docker/docker/volume/mounts/mounts.go create mode 100644 vendor/github.com/docker/docker/volume/mounts/parser.go create mode 100644 vendor/github.com/docker/docker/volume/mounts/validate.go create mode 100644 vendor/github.com/docker/docker/volume/mounts/volume_copy.go create mode 100644 vendor/github.com/docker/docker/volume/mounts/volume_unix.go create mode 100644 vendor/github.com/docker/docker/volume/mounts/volume_windows.go create mode 100644 vendor/github.com/docker/docker/volume/mounts/windows_parser.go create mode 100644 vendor/github.com/docker/docker/volume/volume.go create mode 100644 vendor/github.com/docker/go-connections/LICENSE create mode 100644 vendor/github.com/docker/go-connections/nat/nat.go create mode 100644 vendor/github.com/docker/go-connections/nat/parse.go create mode 100644 vendor/github.com/docker/go-connections/nat/sort.go create mode 100644 vendor/github.com/docker/go-connections/sockets/README.md create mode 100644 vendor/github.com/docker/go-connections/sockets/inmem_socket.go create mode 100644 vendor/github.com/docker/go-connections/sockets/proxy.go create mode 100644 vendor/github.com/docker/go-connections/sockets/sockets.go create mode 100644 vendor/github.com/docker/go-connections/sockets/sockets_unix.go create mode 100644 vendor/github.com/docker/go-connections/sockets/sockets_windows.go create mode 100644 vendor/github.com/docker/go-connections/sockets/tcp_socket.go create mode 100644 vendor/github.com/docker/go-connections/sockets/unix_socket.go create mode 100644 vendor/github.com/docker/go-connections/tlsconfig/certpool_go17.go create mode 100644 vendor/github.com/docker/go-connections/tlsconfig/certpool_other.go create mode 100644 vendor/github.com/docker/go-connections/tlsconfig/config.go create mode 100644 vendor/github.com/docker/go-connections/tlsconfig/config_client_ciphers.go create mode 100644 vendor/github.com/docker/go-connections/tlsconfig/config_legacy_client_ciphers.go create mode 100644 vendor/github.com/docker/go-units/CONTRIBUTING.md create mode 100644 vendor/github.com/docker/go-units/LICENSE create mode 100644 vendor/github.com/docker/go-units/MAINTAINERS create mode 100644 vendor/github.com/docker/go-units/README.md create mode 100644 vendor/github.com/docker/go-units/circle.yml create mode 100644 vendor/github.com/docker/go-units/duration.go create mode 100644 vendor/github.com/docker/go-units/size.go create mode 100644 vendor/github.com/docker/go-units/ulimit.go create mode 100644 vendor/github.com/dustin/go-humanize/.travis.yml create mode 100644 vendor/github.com/dustin/go-humanize/LICENSE create mode 100644 vendor/github.com/dustin/go-humanize/README.markdown create mode 100644 vendor/github.com/dustin/go-humanize/big.go create mode 100644 vendor/github.com/dustin/go-humanize/bigbytes.go create mode 100644 vendor/github.com/dustin/go-humanize/bytes.go create mode 100644 vendor/github.com/dustin/go-humanize/comma.go create mode 100644 vendor/github.com/dustin/go-humanize/commaf.go create mode 100644 vendor/github.com/dustin/go-humanize/ftoa.go create mode 100644 vendor/github.com/dustin/go-humanize/humanize.go create mode 100644 vendor/github.com/dustin/go-humanize/number.go create mode 100644 vendor/github.com/dustin/go-humanize/ordinals.go create mode 100644 vendor/github.com/dustin/go-humanize/si.go create mode 100644 vendor/github.com/dustin/go-humanize/times.go create mode 100644 vendor/github.com/fatih/color/LICENSE.md create mode 100644 vendor/github.com/fatih/color/README.md create mode 100644 vendor/github.com/fatih/color/color.go create mode 100644 vendor/github.com/fatih/color/doc.go create mode 100644 vendor/github.com/fatih/color/go.mod create mode 100644 vendor/github.com/fatih/color/go.sum create mode 100644 vendor/github.com/gdamore/encoding/.appveyor.yml create mode 100644 vendor/github.com/gdamore/encoding/.travis.yml create mode 100644 vendor/github.com/gdamore/encoding/LICENSE create mode 100644 vendor/github.com/gdamore/encoding/README.md create mode 100644 vendor/github.com/gdamore/encoding/ascii.go create mode 100644 vendor/github.com/gdamore/encoding/charmap.go create mode 100644 vendor/github.com/gdamore/encoding/doc.go create mode 100644 vendor/github.com/gdamore/encoding/ebcdic.go create mode 100644 vendor/github.com/gdamore/encoding/go.mod create mode 100644 vendor/github.com/gdamore/encoding/go.sum create mode 100644 vendor/github.com/gdamore/encoding/latin1.go create mode 100644 vendor/github.com/gdamore/encoding/latin5.go create mode 100644 vendor/github.com/gdamore/encoding/utf8.go create mode 100644 vendor/github.com/gdamore/tcell/v2/.appveyor.yml create mode 100644 vendor/github.com/gdamore/tcell/v2/.gitignore create mode 100644 vendor/github.com/gdamore/tcell/v2/.travis.yml create mode 100644 vendor/github.com/gdamore/tcell/v2/AUTHORS create mode 100644 vendor/github.com/gdamore/tcell/v2/CHANGESv2.md create mode 100644 vendor/github.com/gdamore/tcell/v2/LICENSE create mode 100644 vendor/github.com/gdamore/tcell/v2/README.md create mode 100644 vendor/github.com/gdamore/tcell/v2/TUTORIAL.md create mode 100644 vendor/github.com/gdamore/tcell/v2/attr.go create mode 100644 vendor/github.com/gdamore/tcell/v2/cell.go create mode 100644 vendor/github.com/gdamore/tcell/v2/charset_stub.go create mode 100644 vendor/github.com/gdamore/tcell/v2/charset_unix.go create mode 100644 vendor/github.com/gdamore/tcell/v2/charset_windows.go create mode 100644 vendor/github.com/gdamore/tcell/v2/color.go create mode 100644 vendor/github.com/gdamore/tcell/v2/colorfit.go create mode 100644 vendor/github.com/gdamore/tcell/v2/console_stub.go create mode 100644 vendor/github.com/gdamore/tcell/v2/console_win.go create mode 100644 vendor/github.com/gdamore/tcell/v2/doc.go create mode 100644 vendor/github.com/gdamore/tcell/v2/encoding.go create mode 100644 vendor/github.com/gdamore/tcell/v2/errors.go create mode 100644 vendor/github.com/gdamore/tcell/v2/event.go create mode 100644 vendor/github.com/gdamore/tcell/v2/go.mod create mode 100644 vendor/github.com/gdamore/tcell/v2/go.sum create mode 100644 vendor/github.com/gdamore/tcell/v2/interrupt.go create mode 100644 vendor/github.com/gdamore/tcell/v2/key.go create mode 100644 vendor/github.com/gdamore/tcell/v2/mouse.go create mode 100644 vendor/github.com/gdamore/tcell/v2/nonblock_bsd.go create mode 100644 vendor/github.com/gdamore/tcell/v2/nonblock_unix.go create mode 100644 vendor/github.com/gdamore/tcell/v2/paste.go create mode 100644 vendor/github.com/gdamore/tcell/v2/resize.go create mode 100644 vendor/github.com/gdamore/tcell/v2/runes.go create mode 100644 vendor/github.com/gdamore/tcell/v2/screen.go create mode 100644 vendor/github.com/gdamore/tcell/v2/simulation.go create mode 100644 vendor/github.com/gdamore/tcell/v2/stdin_unix.go create mode 100644 vendor/github.com/gdamore/tcell/v2/style.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/.gitignore create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/README.md create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/TERMINALS.md create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/a/aixterm/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/a/alacritty/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/a/ansi/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/b/beterm/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/base/base.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/c/cygwin/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/d/dtterm/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/dynamic/dynamic.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/e/emacs/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/extended/extended.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/f/foot/foot.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/g/gnome/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/gen.sh create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/h/hpterm/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/k/konsole/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/k/kterm/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/l/linux/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/models.txt create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/p/pcansi/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/r/rxvt/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/s/screen/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/s/simpleterm/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/s/sun/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/t/termite/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/t/tmux/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/terminfo.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/v/vt100/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/v/vt102/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/v/vt220/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/v/vt320/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/v/vt400/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/v/vt420/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/v/vt52/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/w/wy50/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/w/wy60/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/w/wy99_ansi/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/x/xfce/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/x/xterm/direct.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/x/xterm/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/x/xterm_kitty/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terminfo/x/xterm_termite/term.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terms_default.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terms_dynamic.go create mode 100644 vendor/github.com/gdamore/tcell/v2/terms_static.go create mode 100644 vendor/github.com/gdamore/tcell/v2/tscreen.go create mode 100644 vendor/github.com/gdamore/tcell/v2/tscreen_stub.go create mode 100644 vendor/github.com/gdamore/tcell/v2/tscreen_unix.go create mode 100644 vendor/github.com/gdamore/tcell/v2/tty.go create mode 100644 vendor/github.com/gdamore/tcell/v2/tty_unix.go create mode 100644 vendor/github.com/gogo/protobuf/gogoproto/Makefile create mode 100644 vendor/github.com/gogo/protobuf/gogoproto/doc.go create mode 100644 vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.go create mode 100644 vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.golden create mode 100644 vendor/github.com/gogo/protobuf/gogoproto/gogo.proto create mode 100644 vendor/github.com/gogo/protobuf/gogoproto/helper.go create mode 100644 vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/Makefile create mode 100644 vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor.go create mode 100644 vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor.pb.go create mode 100644 vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor_gostring.gen.go create mode 100644 vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/helper.go create mode 100644 vendor/github.com/golang/glog/LICENSE create mode 100644 vendor/github.com/golang/glog/README.md create mode 100644 vendor/github.com/golang/glog/glog.go create mode 100644 vendor/github.com/golang/glog/glog_file.go create mode 100644 vendor/github.com/golang/glog/go.mod create mode 100644 vendor/github.com/google/cel-go/LICENSE create mode 100644 vendor/github.com/google/cel-go/cel/cel.go create mode 100644 vendor/github.com/google/cel-go/cel/env.go create mode 100644 vendor/github.com/google/cel-go/cel/io.go create mode 100644 vendor/github.com/google/cel-go/cel/library.go create mode 100644 vendor/github.com/google/cel-go/cel/options.go create mode 100644 vendor/github.com/google/cel-go/cel/program.go create mode 100644 vendor/github.com/google/cel-go/checker/checker.go create mode 100644 vendor/github.com/google/cel-go/checker/cost.go create mode 100644 vendor/github.com/google/cel-go/checker/decls/decls.go create mode 100644 vendor/github.com/google/cel-go/checker/decls/scopes.go create mode 100644 vendor/github.com/google/cel-go/checker/env.go create mode 100644 vendor/github.com/google/cel-go/checker/errors.go create mode 100644 vendor/github.com/google/cel-go/checker/mapping.go create mode 100644 vendor/github.com/google/cel-go/checker/options.go create mode 100644 vendor/github.com/google/cel-go/checker/printer.go create mode 100644 vendor/github.com/google/cel-go/checker/standard.go create mode 100644 vendor/github.com/google/cel-go/checker/types.go create mode 100644 vendor/github.com/google/cel-go/common/containers/container.go create mode 100644 vendor/github.com/google/cel-go/common/cost.go create mode 100644 vendor/github.com/google/cel-go/common/debug/debug.go create mode 100644 vendor/github.com/google/cel-go/common/doc.go create mode 100644 vendor/github.com/google/cel-go/common/error.go create mode 100644 vendor/github.com/google/cel-go/common/errors.go create mode 100644 vendor/github.com/google/cel-go/common/location.go create mode 100644 vendor/github.com/google/cel-go/common/operators/operators.go create mode 100644 vendor/github.com/google/cel-go/common/overloads/overloads.go create mode 100644 vendor/github.com/google/cel-go/common/runes/buffer.go create mode 100644 vendor/github.com/google/cel-go/common/source.go create mode 100644 vendor/github.com/google/cel-go/common/types/any_value.go create mode 100644 vendor/github.com/google/cel-go/common/types/bool.go create mode 100644 vendor/github.com/google/cel-go/common/types/bytes.go create mode 100644 vendor/github.com/google/cel-go/common/types/compare.go create mode 100644 vendor/github.com/google/cel-go/common/types/doc.go create mode 100644 vendor/github.com/google/cel-go/common/types/double.go create mode 100644 vendor/github.com/google/cel-go/common/types/duration.go create mode 100644 vendor/github.com/google/cel-go/common/types/err.go create mode 100644 vendor/github.com/google/cel-go/common/types/int.go create mode 100644 vendor/github.com/google/cel-go/common/types/iterator.go create mode 100644 vendor/github.com/google/cel-go/common/types/json_value.go create mode 100644 vendor/github.com/google/cel-go/common/types/list.go create mode 100644 vendor/github.com/google/cel-go/common/types/map.go create mode 100644 vendor/github.com/google/cel-go/common/types/null.go create mode 100644 vendor/github.com/google/cel-go/common/types/object.go create mode 100644 vendor/github.com/google/cel-go/common/types/overflow.go create mode 100644 vendor/github.com/google/cel-go/common/types/pb/checked.go create mode 100644 vendor/github.com/google/cel-go/common/types/pb/enum.go create mode 100644 vendor/github.com/google/cel-go/common/types/pb/equal.go create mode 100644 vendor/github.com/google/cel-go/common/types/pb/file.go create mode 100644 vendor/github.com/google/cel-go/common/types/pb/pb.go create mode 100644 vendor/github.com/google/cel-go/common/types/pb/type.go create mode 100644 vendor/github.com/google/cel-go/common/types/provider.go create mode 100644 vendor/github.com/google/cel-go/common/types/ref/provider.go create mode 100644 vendor/github.com/google/cel-go/common/types/ref/reference.go create mode 100644 vendor/github.com/google/cel-go/common/types/string.go create mode 100644 vendor/github.com/google/cel-go/common/types/timestamp.go create mode 100644 vendor/github.com/google/cel-go/common/types/traits/comparer.go create mode 100644 vendor/github.com/google/cel-go/common/types/traits/container.go create mode 100644 vendor/github.com/google/cel-go/common/types/traits/field_tester.go create mode 100644 vendor/github.com/google/cel-go/common/types/traits/indexer.go create mode 100644 vendor/github.com/google/cel-go/common/types/traits/iterator.go create mode 100644 vendor/github.com/google/cel-go/common/types/traits/lister.go create mode 100644 vendor/github.com/google/cel-go/common/types/traits/mapper.go create mode 100644 vendor/github.com/google/cel-go/common/types/traits/matcher.go create mode 100644 vendor/github.com/google/cel-go/common/types/traits/math.go create mode 100644 vendor/github.com/google/cel-go/common/types/traits/receiver.go create mode 100644 vendor/github.com/google/cel-go/common/types/traits/sizer.go create mode 100644 vendor/github.com/google/cel-go/common/types/traits/traits.go create mode 100644 vendor/github.com/google/cel-go/common/types/type.go create mode 100644 vendor/github.com/google/cel-go/common/types/uint.go create mode 100644 vendor/github.com/google/cel-go/common/types/unknown.go create mode 100644 vendor/github.com/google/cel-go/common/types/util.go create mode 100644 vendor/github.com/google/cel-go/interpreter/activation.go create mode 100644 vendor/github.com/google/cel-go/interpreter/attribute_patterns.go create mode 100644 vendor/github.com/google/cel-go/interpreter/attributes.go create mode 100644 vendor/github.com/google/cel-go/interpreter/coster.go create mode 100644 vendor/github.com/google/cel-go/interpreter/decorators.go create mode 100644 vendor/github.com/google/cel-go/interpreter/dispatcher.go create mode 100644 vendor/github.com/google/cel-go/interpreter/evalstate.go create mode 100644 vendor/github.com/google/cel-go/interpreter/functions/functions.go create mode 100644 vendor/github.com/google/cel-go/interpreter/functions/standard.go create mode 100644 vendor/github.com/google/cel-go/interpreter/interpretable.go create mode 100644 vendor/github.com/google/cel-go/interpreter/interpreter.go create mode 100644 vendor/github.com/google/cel-go/interpreter/optimizations.go create mode 100644 vendor/github.com/google/cel-go/interpreter/planner.go create mode 100644 vendor/github.com/google/cel-go/interpreter/prune.go create mode 100644 vendor/github.com/google/cel-go/interpreter/runtimecost.go create mode 100644 vendor/github.com/google/cel-go/parser/errors.go create mode 100644 vendor/github.com/google/cel-go/parser/gen/CEL.g4 create mode 100644 vendor/github.com/google/cel-go/parser/gen/CEL.tokens create mode 100644 vendor/github.com/google/cel-go/parser/gen/CELLexer.tokens create mode 100644 vendor/github.com/google/cel-go/parser/gen/cel_base_listener.go create mode 100644 vendor/github.com/google/cel-go/parser/gen/cel_base_visitor.go create mode 100644 vendor/github.com/google/cel-go/parser/gen/cel_lexer.go create mode 100644 vendor/github.com/google/cel-go/parser/gen/cel_listener.go create mode 100644 vendor/github.com/google/cel-go/parser/gen/cel_parser.go create mode 100644 vendor/github.com/google/cel-go/parser/gen/cel_visitor.go create mode 100644 vendor/github.com/google/cel-go/parser/gen/doc.go create mode 100644 vendor/github.com/google/cel-go/parser/helper.go create mode 100644 vendor/github.com/google/cel-go/parser/input.go create mode 100644 vendor/github.com/google/cel-go/parser/macro.go create mode 100644 vendor/github.com/google/cel-go/parser/options.go create mode 100644 vendor/github.com/google/cel-go/parser/parser.go create mode 100644 vendor/github.com/google/cel-go/parser/unescape.go create mode 100644 vendor/github.com/google/cel-go/parser/unparser.go create mode 100644 vendor/github.com/google/go-containerregistry/internal/and/and_closer.go create mode 100644 vendor/github.com/google/go-containerregistry/internal/estargz/estargz.go create mode 100644 vendor/github.com/google/go-containerregistry/internal/gzip/zip.go create mode 100644 vendor/github.com/google/go-containerregistry/internal/redact/redact.go create mode 100644 vendor/github.com/google/go-containerregistry/internal/retry/retry.go create mode 100644 vendor/github.com/google/go-containerregistry/internal/retry/wait/kubernetes_apimachinery_wait.go create mode 100644 vendor/github.com/google/go-containerregistry/internal/verify/verify.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/authn/README.md create mode 100644 vendor/github.com/google/go-containerregistry/pkg/authn/anon.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/authn/auth.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/authn/authn.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/authn/basic.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/authn/bearer.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/authn/doc.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/authn/keychain.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/authn/multikeychain.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/logs/logs.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/config.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/daemon/README.md create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/daemon/doc.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/daemon/image.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/daemon/options.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/daemon/write.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/doc.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/empty/README.md create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/empty/doc.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/empty/image.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/empty/index.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/hash.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/image.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/index.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/layer.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/layout/README.md create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/layout/blob.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/layout/doc.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/layout/image.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/layout/index.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/layout/layoutpath.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/layout/options.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/layout/read.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/layout/write.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/manifest.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/match/match.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/mutate/README.md create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/mutate/doc.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/mutate/image.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/mutate/index.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/mutate/mutate.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/mutate/rebase.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/partial/README.md create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/partial/compressed.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/partial/doc.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/partial/image.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/partial/index.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/partial/uncompressed.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/partial/with.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/platform.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/progress.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/README.md create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/catalog.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/check.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/delete.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/descriptor.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/doc.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/image.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/index.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/layer.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/list.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/mount.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/multi_write.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/options.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/README.md create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/basic.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/bearer.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/doc.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/error.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/logger.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/ping.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/retry.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/schemer.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/scope.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/transport.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/useragent.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/remote/write.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/stream/README.md create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/stream/layer.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/tarball/README.md create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/tarball/doc.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/tarball/image.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/tarball/layer.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/tarball/write.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/types/types.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/zz_deepcopy_generated.go create mode 100644 vendor/github.com/hako/durafmt/.gitignore create mode 100644 vendor/github.com/hako/durafmt/.travis.yml create mode 100644 vendor/github.com/hako/durafmt/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/hako/durafmt/CONTRIBUTING.md create mode 100644 vendor/github.com/hako/durafmt/LICENSE create mode 100644 vendor/github.com/hako/durafmt/README.md create mode 100644 vendor/github.com/hako/durafmt/durafmt.go create mode 100644 vendor/github.com/hako/durafmt/go.mod create mode 100644 vendor/github.com/hako/durafmt/unit.go create mode 100644 vendor/github.com/hashicorp/errwrap/LICENSE create mode 100644 vendor/github.com/hashicorp/errwrap/README.md create mode 100644 vendor/github.com/hashicorp/errwrap/errwrap.go create mode 100644 vendor/github.com/hashicorp/errwrap/go.mod create mode 100644 vendor/github.com/hashicorp/go-multierror/LICENSE create mode 100644 vendor/github.com/hashicorp/go-multierror/Makefile create mode 100644 vendor/github.com/hashicorp/go-multierror/README.md create mode 100644 vendor/github.com/hashicorp/go-multierror/append.go create mode 100644 vendor/github.com/hashicorp/go-multierror/flatten.go create mode 100644 vendor/github.com/hashicorp/go-multierror/format.go create mode 100644 vendor/github.com/hashicorp/go-multierror/go.mod create mode 100644 vendor/github.com/hashicorp/go-multierror/go.sum create mode 100644 vendor/github.com/hashicorp/go-multierror/group.go create mode 100644 vendor/github.com/hashicorp/go-multierror/multierror.go create mode 100644 vendor/github.com/hashicorp/go-multierror/prefix.go create mode 100644 vendor/github.com/hashicorp/go-multierror/sort.go create mode 100644 vendor/github.com/heroku/color/.gitignore create mode 100644 vendor/github.com/heroku/color/.golangcli.yml create mode 100644 vendor/github.com/heroku/color/LICENSE create mode 100644 vendor/github.com/heroku/color/Makefile create mode 100644 vendor/github.com/heroku/color/README.md create mode 100644 vendor/github.com/heroku/color/attributes.go create mode 100644 vendor/github.com/heroku/color/cache.go create mode 100644 vendor/github.com/heroku/color/color.go create mode 100644 vendor/github.com/heroku/color/console.go create mode 100644 vendor/github.com/heroku/color/go.mod create mode 100644 vendor/github.com/heroku/color/go.sum create mode 100644 vendor/github.com/jonboulle/clockwork/.editorconfig create mode 100644 vendor/github.com/jonboulle/clockwork/.gitignore create mode 100644 vendor/github.com/jonboulle/clockwork/LICENSE create mode 100644 vendor/github.com/jonboulle/clockwork/README.md create mode 100644 vendor/github.com/jonboulle/clockwork/clockwork.go create mode 100644 vendor/github.com/jonboulle/clockwork/go.mod create mode 100644 vendor/github.com/jonboulle/clockwork/ticker.go create mode 100644 vendor/github.com/kballard/go-shellquote/LICENSE create mode 100644 vendor/github.com/kballard/go-shellquote/README create mode 100644 vendor/github.com/kballard/go-shellquote/doc.go create mode 100644 vendor/github.com/kballard/go-shellquote/quote.go create mode 100644 vendor/github.com/kballard/go-shellquote/unquote.go create mode 100644 vendor/github.com/lucasb-eyer/go-colorful/.gitignore create mode 100644 vendor/github.com/lucasb-eyer/go-colorful/CHANGELOG.md create mode 100644 vendor/github.com/lucasb-eyer/go-colorful/LICENSE create mode 100644 vendor/github.com/lucasb-eyer/go-colorful/README.md create mode 100644 vendor/github.com/lucasb-eyer/go-colorful/colorgens.go create mode 100644 vendor/github.com/lucasb-eyer/go-colorful/colors.go create mode 100644 vendor/github.com/lucasb-eyer/go-colorful/go.mod create mode 100644 vendor/github.com/lucasb-eyer/go-colorful/happy_palettegen.go create mode 100644 vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go create mode 100644 vendor/github.com/lucasb-eyer/go-colorful/hsluv-snapshot-rev4.json create mode 100644 vendor/github.com/lucasb-eyer/go-colorful/hsluv.go create mode 100644 vendor/github.com/lucasb-eyer/go-colorful/soft_palettegen.go create mode 100644 vendor/github.com/lucasb-eyer/go-colorful/warm_palettegen.go create mode 100644 vendor/github.com/mattn/go-colorable/LICENSE create mode 100644 vendor/github.com/mattn/go-colorable/README.md create mode 100644 vendor/github.com/mattn/go-colorable/colorable_appengine.go create mode 100644 vendor/github.com/mattn/go-colorable/colorable_others.go create mode 100644 vendor/github.com/mattn/go-colorable/colorable_windows.go create mode 100644 vendor/github.com/mattn/go-colorable/go.mod create mode 100644 vendor/github.com/mattn/go-colorable/go.sum create mode 100644 vendor/github.com/mattn/go-colorable/go.test.sh create mode 100644 vendor/github.com/mattn/go-colorable/noncolorable.go create mode 100644 vendor/github.com/mattn/go-isatty/LICENSE create mode 100644 vendor/github.com/mattn/go-isatty/README.md create mode 100644 vendor/github.com/mattn/go-isatty/doc.go create mode 100644 vendor/github.com/mattn/go-isatty/go.mod create mode 100644 vendor/github.com/mattn/go-isatty/go.sum create mode 100644 vendor/github.com/mattn/go-isatty/go.test.sh create mode 100644 vendor/github.com/mattn/go-isatty/isatty_bsd.go create mode 100644 vendor/github.com/mattn/go-isatty/isatty_others.go create mode 100644 vendor/github.com/mattn/go-isatty/isatty_plan9.go create mode 100644 vendor/github.com/mattn/go-isatty/isatty_solaris.go create mode 100644 vendor/github.com/mattn/go-isatty/isatty_tcgets.go create mode 100644 vendor/github.com/mattn/go-isatty/isatty_windows.go create mode 100644 vendor/github.com/mattn/go-runewidth/.travis.yml create mode 100644 vendor/github.com/mattn/go-runewidth/LICENSE create mode 100644 vendor/github.com/mattn/go-runewidth/README.md create mode 100644 vendor/github.com/mattn/go-runewidth/go.mod create mode 100644 vendor/github.com/mattn/go-runewidth/go.sum create mode 100644 vendor/github.com/mattn/go-runewidth/go.test.sh create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth.go create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_appengine.go create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_js.go create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_posix.go create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_table.go create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_windows.go create mode 100644 vendor/github.com/mgutz/ansi/.gitignore create mode 100644 vendor/github.com/mgutz/ansi/LICENSE create mode 100644 vendor/github.com/mgutz/ansi/README.md create mode 100644 vendor/github.com/mgutz/ansi/ansi.go create mode 100644 vendor/github.com/mgutz/ansi/doc.go create mode 100644 vendor/github.com/mgutz/ansi/print.go create mode 100644 vendor/github.com/mitchellh/ioprogress/LICENSE create mode 100644 vendor/github.com/mitchellh/ioprogress/README.md create mode 100644 vendor/github.com/mitchellh/ioprogress/draw.go create mode 100644 vendor/github.com/mitchellh/ioprogress/reader.go create mode 100644 vendor/github.com/moby/spdystream/CONTRIBUTING.md create mode 100644 vendor/github.com/moby/spdystream/LICENSE create mode 100644 vendor/github.com/moby/spdystream/MAINTAINERS create mode 100644 vendor/github.com/moby/spdystream/NOTICE create mode 100644 vendor/github.com/moby/spdystream/README.md create mode 100644 vendor/github.com/moby/spdystream/connection.go create mode 100644 vendor/github.com/moby/spdystream/go.mod create mode 100644 vendor/github.com/moby/spdystream/go.sum create mode 100644 vendor/github.com/moby/spdystream/handlers.go create mode 100644 vendor/github.com/moby/spdystream/priority.go create mode 100644 vendor/github.com/moby/spdystream/spdy/dictionary.go create mode 100644 vendor/github.com/moby/spdystream/spdy/read.go create mode 100644 vendor/github.com/moby/spdystream/spdy/types.go create mode 100644 vendor/github.com/moby/spdystream/spdy/write.go create mode 100644 vendor/github.com/moby/spdystream/stream.go create mode 100644 vendor/github.com/moby/spdystream/utils.go create mode 100644 vendor/github.com/moby/sys/mount/LICENSE create mode 100644 vendor/github.com/moby/sys/mount/doc.go create mode 100644 vendor/github.com/moby/sys/mount/flags_bsd.go create mode 100644 vendor/github.com/moby/sys/mount/flags_linux.go create mode 100644 vendor/github.com/moby/sys/mount/flags_unix.go create mode 100644 vendor/github.com/moby/sys/mount/go.mod create mode 100644 vendor/github.com/moby/sys/mount/go.sum create mode 100644 vendor/github.com/moby/sys/mount/mount_errors.go create mode 100644 vendor/github.com/moby/sys/mount/mount_unix.go create mode 100644 vendor/github.com/moby/sys/mount/mounter_bsd.go create mode 100644 vendor/github.com/moby/sys/mount/mounter_linux.go create mode 100644 vendor/github.com/moby/sys/mount/mounter_unsupported.go create mode 100644 vendor/github.com/moby/sys/mount/sharedsubtree_linux.go create mode 100644 vendor/github.com/moby/sys/mountinfo/LICENSE create mode 100644 vendor/github.com/moby/sys/mountinfo/doc.go create mode 100644 vendor/github.com/moby/sys/mountinfo/go.mod create mode 100644 vendor/github.com/moby/sys/mountinfo/go.sum create mode 100644 vendor/github.com/moby/sys/mountinfo/mounted_linux.go create mode 100644 vendor/github.com/moby/sys/mountinfo/mounted_unix.go create mode 100644 vendor/github.com/moby/sys/mountinfo/mountinfo.go create mode 100644 vendor/github.com/moby/sys/mountinfo/mountinfo_bsd.go create mode 100644 vendor/github.com/moby/sys/mountinfo/mountinfo_filters.go create mode 100644 vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go create mode 100644 vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go create mode 100644 vendor/github.com/moby/sys/mountinfo/mountinfo_windows.go create mode 100644 vendor/github.com/moby/term/.gitignore create mode 100644 vendor/github.com/moby/term/LICENSE create mode 100644 vendor/github.com/moby/term/README.md create mode 100644 vendor/github.com/moby/term/ascii.go create mode 100644 vendor/github.com/moby/term/go.mod create mode 100644 vendor/github.com/moby/term/go.sum create mode 100644 vendor/github.com/moby/term/proxy.go create mode 100644 vendor/github.com/moby/term/tc.go create mode 100644 vendor/github.com/moby/term/term.go create mode 100644 vendor/github.com/moby/term/term_windows.go create mode 100644 vendor/github.com/moby/term/termios.go create mode 100644 vendor/github.com/moby/term/termios_bsd.go create mode 100644 vendor/github.com/moby/term/termios_nonbsd.go create mode 100644 vendor/github.com/moby/term/windows/ansi_reader.go create mode 100644 vendor/github.com/moby/term/windows/ansi_writer.go create mode 100644 vendor/github.com/moby/term/windows/console.go create mode 100644 vendor/github.com/moby/term/windows/doc.go create mode 100644 vendor/github.com/moby/term/winsize.go create mode 100644 vendor/github.com/morikuni/aec/LICENSE create mode 100644 vendor/github.com/morikuni/aec/README.md create mode 100644 vendor/github.com/morikuni/aec/aec.go create mode 100644 vendor/github.com/morikuni/aec/ansi.go create mode 100644 vendor/github.com/morikuni/aec/builder.go create mode 100644 vendor/github.com/morikuni/aec/sample.gif create mode 100644 vendor/github.com/morikuni/aec/sgr.go create mode 100644 vendor/github.com/opencontainers/go-digest/.mailmap create mode 100644 vendor/github.com/opencontainers/go-digest/.pullapprove.yml create mode 100644 vendor/github.com/opencontainers/go-digest/.travis.yml create mode 100644 vendor/github.com/opencontainers/go-digest/CONTRIBUTING.md create mode 100644 vendor/github.com/opencontainers/go-digest/LICENSE create mode 100644 vendor/github.com/opencontainers/go-digest/LICENSE.docs create mode 100644 vendor/github.com/opencontainers/go-digest/MAINTAINERS create mode 100644 vendor/github.com/opencontainers/go-digest/README.md create mode 100644 vendor/github.com/opencontainers/go-digest/algorithm.go create mode 100644 vendor/github.com/opencontainers/go-digest/digest.go create mode 100644 vendor/github.com/opencontainers/go-digest/digester.go create mode 100644 vendor/github.com/opencontainers/go-digest/doc.go create mode 100644 vendor/github.com/opencontainers/go-digest/go.mod create mode 100644 vendor/github.com/opencontainers/go-digest/verifiers.go create mode 100644 vendor/github.com/opencontainers/image-spec/LICENSE create mode 100644 vendor/github.com/opencontainers/image-spec/specs-go/v1/annotations.go create mode 100644 vendor/github.com/opencontainers/image-spec/specs-go/v1/config.go create mode 100644 vendor/github.com/opencontainers/image-spec/specs-go/v1/descriptor.go create mode 100644 vendor/github.com/opencontainers/image-spec/specs-go/v1/index.go create mode 100644 vendor/github.com/opencontainers/image-spec/specs-go/v1/layout.go create mode 100644 vendor/github.com/opencontainers/image-spec/specs-go/v1/manifest.go create mode 100644 vendor/github.com/opencontainers/image-spec/specs-go/v1/mediatype.go create mode 100644 vendor/github.com/opencontainers/image-spec/specs-go/version.go create mode 100644 vendor/github.com/opencontainers/image-spec/specs-go/versioned.go create mode 100644 vendor/github.com/opencontainers/runc/LICENSE create mode 100644 vendor/github.com/opencontainers/runc/NOTICE create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/user/user.go create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/user/user_fuzzer.go create mode 100644 vendor/github.com/opencontainers/runtime-spec/LICENSE create mode 100644 vendor/github.com/opencontainers/runtime-spec/specs-go/config.go create mode 100644 vendor/github.com/opencontainers/runtime-spec/specs-go/state.go create mode 100644 vendor/github.com/opencontainers/runtime-spec/specs-go/version.go create mode 100644 vendor/github.com/opencontainers/selinux/LICENSE create mode 100644 vendor/github.com/opencontainers/selinux/go-selinux/doc.go create mode 100644 vendor/github.com/opencontainers/selinux/go-selinux/label/label.go create mode 100644 vendor/github.com/opencontainers/selinux/go-selinux/label/label_linux.go create mode 100644 vendor/github.com/opencontainers/selinux/go-selinux/label/label_stub.go create mode 100644 vendor/github.com/opencontainers/selinux/go-selinux/rchcon.go create mode 100644 vendor/github.com/opencontainers/selinux/go-selinux/rchcon_go115.go create mode 100644 vendor/github.com/opencontainers/selinux/go-selinux/selinux.go create mode 100644 vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go create mode 100644 vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go create mode 100644 vendor/github.com/opencontainers/selinux/go-selinux/xattrs_linux.go create mode 100644 vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md create mode 100644 vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go create mode 100644 vendor/github.com/opencontainers/selinux/pkg/pwalkdir/README.md create mode 100644 vendor/github.com/opencontainers/selinux/pkg/pwalkdir/pwalkdir.go create mode 100644 vendor/github.com/openshift/source-to-image/AUTHORS create mode 100644 vendor/github.com/openshift/source-to-image/LICENSE create mode 100644 vendor/github.com/openshift/source-to-image/pkg/api/constants/doc.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/api/constants/env.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/api/constants/labels.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/api/constants/scripts.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/api/doc.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/api/helpers.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/api/types.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/api/validation/validation.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/build/cleanup.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/build/config.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/build/interfaces.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/build/strategies/doc.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/build/strategies/dockerfile/dockerfile.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/build/strategies/layered/layered.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/build/strategies/onbuild/entrypoint.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/build/strategies/onbuild/onbuild.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/build/strategies/sti/postexecutorstep.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/build/strategies/sti/sti.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/build/strategies/sti/usage.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/build/strategies/strategies.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/docker/doc.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/docker/docker.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/docker/fake_docker.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/docker/util.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/errors/doc.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/errors/errors.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/ignore/doc.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/ignore/ignore.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/scm/doc.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/scm/downloaders/empty/noop.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/scm/downloaders/file/download.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/scm/downloaders/git/clone.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/scm/git/git.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/scm/git/testhelpers.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/scm/git/types.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/scm/git/url.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/scm/scm.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/scripts/doc.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/scripts/download.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/scripts/environment.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/scripts/install.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/tar/doc.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/tar/tar.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/callback.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/cmd/cmd.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/cygpath/cygpath.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/doc.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/env.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/fs/fs.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/injection.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/interrupt/interrupt.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/labels.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/log/log.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/status/build_status.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/status/doc.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/strings.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/timeout.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/user/range.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/user/rangelist.go create mode 100644 vendor/github.com/openshift/source-to-image/pkg/util/util.go create mode 100644 vendor/github.com/ory/viper/.editorconfig create mode 100644 vendor/github.com/ory/viper/.gitignore create mode 100644 vendor/github.com/ory/viper/.golangci.yml create mode 100644 vendor/github.com/ory/viper/.travis.yml create mode 100644 vendor/github.com/ory/viper/LICENSE create mode 100644 vendor/github.com/ory/viper/Makefile create mode 100644 vendor/github.com/ory/viper/README.md create mode 100644 vendor/github.com/ory/viper/flags.go create mode 100644 vendor/github.com/ory/viper/go.mod create mode 100644 vendor/github.com/ory/viper/go.sum create mode 100644 vendor/github.com/ory/viper/util.go create mode 100644 vendor/github.com/ory/viper/viper.go create mode 100644 vendor/github.com/rivo/tview/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/rivo/tview/CONTRIBUTING.md create mode 100644 vendor/github.com/rivo/tview/LICENSE.txt create mode 100644 vendor/github.com/rivo/tview/README.md create mode 100644 vendor/github.com/rivo/tview/ansi.go create mode 100644 vendor/github.com/rivo/tview/application.go create mode 100644 vendor/github.com/rivo/tview/borders.go create mode 100644 vendor/github.com/rivo/tview/box.go create mode 100644 vendor/github.com/rivo/tview/button.go create mode 100644 vendor/github.com/rivo/tview/checkbox.go create mode 100644 vendor/github.com/rivo/tview/doc.go create mode 100644 vendor/github.com/rivo/tview/dropdown.go create mode 100644 vendor/github.com/rivo/tview/flex.go create mode 100644 vendor/github.com/rivo/tview/form.go create mode 100644 vendor/github.com/rivo/tview/frame.go create mode 100644 vendor/github.com/rivo/tview/go.mod create mode 100644 vendor/github.com/rivo/tview/go.sum create mode 100644 vendor/github.com/rivo/tview/grid.go create mode 100644 vendor/github.com/rivo/tview/inputfield.go create mode 100644 vendor/github.com/rivo/tview/list.go create mode 100644 vendor/github.com/rivo/tview/modal.go create mode 100644 vendor/github.com/rivo/tview/pages.go create mode 100644 vendor/github.com/rivo/tview/primitive.go create mode 100644 vendor/github.com/rivo/tview/semigraphics.go create mode 100644 vendor/github.com/rivo/tview/styles.go create mode 100644 vendor/github.com/rivo/tview/table.go create mode 100644 vendor/github.com/rivo/tview/textview.go create mode 100644 vendor/github.com/rivo/tview/treeview.go create mode 100644 vendor/github.com/rivo/tview/tview.gif create mode 100644 vendor/github.com/rivo/tview/util.go create mode 100644 vendor/github.com/rivo/uniseg/LICENSE.txt create mode 100644 vendor/github.com/rivo/uniseg/README.md create mode 100644 vendor/github.com/rivo/uniseg/doc.go create mode 100644 vendor/github.com/rivo/uniseg/go.mod create mode 100644 vendor/github.com/rivo/uniseg/grapheme.go create mode 100644 vendor/github.com/rivo/uniseg/properties.go create mode 100644 vendor/github.com/sabhiram/go-gitignore/.gitignore create mode 100644 vendor/github.com/sabhiram/go-gitignore/.travis.yml create mode 100644 vendor/github.com/sabhiram/go-gitignore/LICENSE create mode 100644 vendor/github.com/sabhiram/go-gitignore/README.md create mode 100644 vendor/github.com/sabhiram/go-gitignore/ignore.go create mode 100644 vendor/github.com/sabhiram/go-gitignore/version_gen.go create mode 100644 vendor/github.com/sirupsen/logrus/.gitignore create mode 100644 vendor/github.com/sirupsen/logrus/.golangci.yml create mode 100644 vendor/github.com/sirupsen/logrus/.travis.yml create mode 100644 vendor/github.com/sirupsen/logrus/CHANGELOG.md create mode 100644 vendor/github.com/sirupsen/logrus/LICENSE create mode 100644 vendor/github.com/sirupsen/logrus/README.md create mode 100644 vendor/github.com/sirupsen/logrus/alt_exit.go create mode 100644 vendor/github.com/sirupsen/logrus/appveyor.yml create mode 100644 vendor/github.com/sirupsen/logrus/buffer_pool.go create mode 100644 vendor/github.com/sirupsen/logrus/doc.go create mode 100644 vendor/github.com/sirupsen/logrus/entry.go create mode 100644 vendor/github.com/sirupsen/logrus/exported.go create mode 100644 vendor/github.com/sirupsen/logrus/formatter.go create mode 100644 vendor/github.com/sirupsen/logrus/go.mod create mode 100644 vendor/github.com/sirupsen/logrus/go.sum create mode 100644 vendor/github.com/sirupsen/logrus/hooks.go create mode 100644 vendor/github.com/sirupsen/logrus/json_formatter.go create mode 100644 vendor/github.com/sirupsen/logrus/logger.go create mode 100644 vendor/github.com/sirupsen/logrus/logrus.go create mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_appengine.go create mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_bsd.go create mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_js.go create mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go create mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go create mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_solaris.go create mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_unix.go create mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_windows.go create mode 100644 vendor/github.com/sirupsen/logrus/text_formatter.go create mode 100644 vendor/github.com/sirupsen/logrus/writer.go create mode 100644 vendor/github.com/src-d/gcfg/LICENSE create mode 100644 vendor/github.com/src-d/gcfg/README create mode 100644 vendor/github.com/src-d/gcfg/doc.go create mode 100644 vendor/github.com/src-d/gcfg/errors.go create mode 100644 vendor/github.com/src-d/gcfg/go1_0.go create mode 100644 vendor/github.com/src-d/gcfg/go1_2.go create mode 100644 vendor/github.com/src-d/gcfg/read.go create mode 100644 vendor/github.com/src-d/gcfg/scanner/errors.go create mode 100644 vendor/github.com/src-d/gcfg/scanner/scanner.go create mode 100644 vendor/github.com/src-d/gcfg/set.go create mode 100644 vendor/github.com/src-d/gcfg/token/position.go create mode 100644 vendor/github.com/src-d/gcfg/token/serialize.go create mode 100644 vendor/github.com/src-d/gcfg/token/token.go create mode 100644 vendor/github.com/src-d/gcfg/types/bool.go create mode 100644 vendor/github.com/src-d/gcfg/types/doc.go create mode 100644 vendor/github.com/src-d/gcfg/types/enum.go create mode 100644 vendor/github.com/src-d/gcfg/types/int.go create mode 100644 vendor/github.com/src-d/gcfg/types/scan.go create mode 100644 vendor/github.com/stoewer/go-strcase/.gitignore create mode 100644 vendor/github.com/stoewer/go-strcase/.golangci.yml create mode 100644 vendor/github.com/stoewer/go-strcase/LICENSE create mode 100644 vendor/github.com/stoewer/go-strcase/README.md create mode 100644 vendor/github.com/stoewer/go-strcase/camel.go create mode 100644 vendor/github.com/stoewer/go-strcase/doc.go create mode 100644 vendor/github.com/stoewer/go-strcase/go.mod create mode 100644 vendor/github.com/stoewer/go-strcase/go.sum create mode 100644 vendor/github.com/stoewer/go-strcase/helper.go create mode 100644 vendor/github.com/stoewer/go-strcase/kebab.go create mode 100644 vendor/github.com/stoewer/go-strcase/snake.go create mode 100644 vendor/github.com/syndtr/gocapability/LICENSE create mode 100644 vendor/github.com/syndtr/gocapability/capability/capability.go create mode 100644 vendor/github.com/syndtr/gocapability/capability/capability_linux.go create mode 100644 vendor/github.com/syndtr/gocapability/capability/capability_noop.go create mode 100644 vendor/github.com/syndtr/gocapability/capability/enum.go create mode 100644 vendor/github.com/syndtr/gocapability/capability/enum_gen.go create mode 100644 vendor/github.com/syndtr/gocapability/capability/syscall_linux.go create mode 100644 vendor/github.com/tektoncd/cli/LICENSE create mode 100644 vendor/github.com/tektoncd/cli/pkg/actions/create.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/actions/delete.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/actions/get.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/actions/gvr.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/actions/list.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/actions/patch.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/actions/watch.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/cli/interface.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/cli/params.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/formatted/address.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/formatted/color.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/formatted/completion.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/formatted/conditions.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/formatted/description.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/formatted/k8s.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/formatted/param.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/formatted/results.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/formatted/task.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/formatted/time.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/formatted/version.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/formatted/workspace.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/pipelinerun/pipelinerun.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/pipelinerun/sort/by_namespace.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/pipelinerun/sort/by_start_time.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/pipelinerun/tracker.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/printer/printer.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/task/task.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/task/tasklastrun.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/taskrun/create.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/taskrun/get.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/taskrun/list/list.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/taskrun/patch.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/taskrun/sort/by_namespace.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/taskrun/sort/by_start_time.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/taskrun/taskrun.go create mode 100644 vendor/github.com/tektoncd/cli/pkg/taskrun/watch.go create mode 100644 vendor/github.com/tektoncd/pipeline/LICENSE create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/config/artifact_bucket.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/config/artifact_pvc.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/config/default.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/config/doc.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/config/feature_flags.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/config/metrics.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/config/store.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/config/zz_generated.deepcopy.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/controller.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/images.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/options.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/paths.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/pod/doc.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/pod/template.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/pod/zz_generated.deepcopy.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/register.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/storagetypes.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/cluster_task_conversion.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/cluster_task_defaults.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/cluster_task_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/cluster_task_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/condition_defaults.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/condition_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/condition_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/container_replacements.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/conversion_error.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/doc.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/param_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_conversion.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_defaults.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_interface.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_resource_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipelinerun_conversion.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipelinerun_defaults.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipelinerun_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipelinerun_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pod.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/register.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/resource_paths.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/resource_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/run_defaults.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/run_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/run_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/step_replacements.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/storage_resource.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_conversion.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_defaults.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_interface.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/taskrun_conversion.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/taskrun_defaults.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/taskrun_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/taskrun_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/workspace_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/workspace_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/cluster_task_conversion.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/cluster_task_defaults.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/cluster_task_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/cluster_task_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/condition_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/container_replacements.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/conversion_error.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/doc.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/merge.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/openapi_generated.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/param_context.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/param_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_conversion.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_defaults.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_interface.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelineref_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelinerun_conversion.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelinerun_defaults.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelinerun_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelinerun_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pod.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/register.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resolver_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resource_paths.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resource_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resource_types_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resultref.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/sidecar_replacements.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/step_replacements.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/swagger.json create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_conversion.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_defaults.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_interface.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskref_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskrun_conversion.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskrun_defaults.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskrun_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskrun_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/version_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/when_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/when_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/workspace_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/workspace_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/zz_generated.deepcopy.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/doc.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/pipeline_resource_defaults.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/pipeline_resource_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/pipelineresource_validation.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/register.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/zz_generated.deepcopy.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/run/v1alpha1/runstatus_types.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/run/v1alpha1/zz_generated.deepcopy.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/apis/validate/metadata.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/clientset.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/doc.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme/doc.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme/register.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/clustertask.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/condition.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/doc.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/generated_expansion.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipeline.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipeline_client.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipelinerun.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/run.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/task.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/taskrun.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/clustertask.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/doc.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/generated_expansion.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/pipeline.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/pipeline_client.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/pipelinerun.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/task.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/taskrun.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/factory.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/generic.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/interface.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/clustertask.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/condition.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/interface.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/pipeline.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/pipelinerun.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/run.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/task.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/taskrun.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/clustertask.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/interface.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/pipeline.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/pipelinerun.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/task.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/taskrun.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/clustertask.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/condition.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/expansion_generated.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/pipeline.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/pipelinerun.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/run.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/task.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/taskrun.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/clustertask.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/expansion_generated.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/pipeline.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/pipelinerun.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/task.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/taskrun.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/clientset.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/doc.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/scheme/doc.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/scheme/register.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1/doc.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1/generated_expansion.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1/pipelineresource.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1/resource_client.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/list/diff.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/reconciler/pipeline/dag/dag.go create mode 100644 vendor/github.com/tektoncd/pipeline/pkg/substitution/substitution.go create mode 100644 vendor/github.com/tektoncd/triggers/LICENSE create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/config/allowed_types.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/config/default.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/config/doc.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/config/feature_flags.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/config/store.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/config/zz_generated.deepcopy.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/contexts/contexts.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/register.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_interceptor_defaults.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_interceptor_types.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_interceptor_validation.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_trigger_binding_defaults.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_trigger_binding_types.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_trigger_binding_validation.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/doc.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/event_listener_defaults.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/event_listener_types.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/event_listener_validation.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/interceptor_types.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/param.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/register.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_binding_defaults.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_binding_interface.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_binding_types.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_binding_validation.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_defaults.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_template_defaults.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_template_types.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_template_validation.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_types.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_types_convert.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_validation.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/zz_generated.deepcopy.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/cluster_trigger_binding_defaults.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/cluster_trigger_binding_types.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/cluster_trigger_binding_validation.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/doc.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/event_listener_defaults.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/event_listener_types.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/event_listener_validation.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/interceptor_types.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/openapi_generated.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/param.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/register.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/swagger.json create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_binding_defaults.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_binding_interface.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_binding_types.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_binding_validation.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_defaults.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_template_defaults.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_template_types.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_template_validation.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_types.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_types_convert.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_validation.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/version_validation.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/zz_generated.deepcopy.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/apis/triggers/validation.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/clientset.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/doc.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme/doc.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme/register.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/clusterinterceptor.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/clustertriggerbinding.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/doc.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/eventlistener.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/generated_expansion.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/trigger.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/triggerbinding.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/triggers_client.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/triggertemplate.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/clustertriggerbinding.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/doc.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/eventlistener.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/generated_expansion.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/trigger.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/triggerbinding.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/triggers_client.go create mode 100644 vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/triggertemplate.go create mode 100644 vendor/github.com/vbatts/tar-split/LICENSE create mode 100644 vendor/github.com/vbatts/tar-split/archive/tar/common.go create mode 100644 vendor/github.com/vbatts/tar-split/archive/tar/format.go create mode 100644 vendor/github.com/vbatts/tar-split/archive/tar/reader.go create mode 100644 vendor/github.com/vbatts/tar-split/archive/tar/stat_actime1.go create mode 100644 vendor/github.com/vbatts/tar-split/archive/tar/stat_actime2.go create mode 100644 vendor/github.com/vbatts/tar-split/archive/tar/stat_unix.go create mode 100644 vendor/github.com/vbatts/tar-split/archive/tar/strconv.go create mode 100644 vendor/github.com/vbatts/tar-split/archive/tar/writer.go create mode 100644 vendor/golang.org/x/crypto/openpgp/armor/armor.go create mode 100644 vendor/golang.org/x/crypto/openpgp/armor/encode.go create mode 100644 vendor/golang.org/x/crypto/openpgp/canonical_text.go create mode 100644 vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go create mode 100644 vendor/golang.org/x/crypto/openpgp/errors/errors.go create mode 100644 vendor/golang.org/x/crypto/openpgp/keys.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/compressed.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/config.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/encrypted_key.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/literal.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/ocfb.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/one_pass_signature.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/opaque.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/packet.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/private_key.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/public_key.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/public_key_v3.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/reader.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/signature.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/signature_v3.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/symmetric_key_encrypted.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/symmetrically_encrypted.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/userattribute.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/userid.go create mode 100644 vendor/golang.org/x/crypto/openpgp/read.go create mode 100644 vendor/golang.org/x/crypto/openpgp/s2k/s2k.go create mode 100644 vendor/golang.org/x/crypto/openpgp/write.go create mode 100644 vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/checked.pb.go create mode 100644 vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/eval.pb.go create mode 100644 vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/explain.pb.go create mode 100644 vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/syntax.pb.go create mode 100644 vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/value.pb.go create mode 100644 vendor/google.golang.org/protobuf/types/dynamicpb/dynamic.go create mode 100644 vendor/google.golang.org/protobuf/types/known/emptypb/empty.pb.go create mode 100644 vendor/google.golang.org/protobuf/types/known/structpb/struct.pb.go create mode 100644 vendor/gopkg.in/natefinch/npipe.v2/.gitignore create mode 100644 vendor/gopkg.in/natefinch/npipe.v2/LICENSE.txt create mode 100644 vendor/gopkg.in/natefinch/npipe.v2/README.md create mode 100644 vendor/gopkg.in/natefinch/npipe.v2/doc.go create mode 100644 vendor/gopkg.in/natefinch/npipe.v2/npipe_windows.go create mode 100644 vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_386.go create mode 100644 vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_amd64.go create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/.gitignore create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/.travis.yml create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/DCO create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/LICENSE create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/MAINTAINERS create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/Makefile create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/README.md create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/appveyor.yml create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/fs.go create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/go.mod create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/go.sum create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/helper/chroot/chroot.go create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/helper/polyfill/polyfill.go create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/osfs/os.go create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/osfs/os_posix.go create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/osfs/os_windows.go create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/util/glob.go create mode 100644 vendor/gopkg.in/src-d/go-billy.v4/util/util.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/.gitignore create mode 100644 vendor/gopkg.in/src-d/go-git.v4/.travis.yml create mode 100644 vendor/gopkg.in/src-d/go-git.v4/CODE_OF_CONDUCT.md create mode 100644 vendor/gopkg.in/src-d/go-git.v4/COMPATIBILITY.md create mode 100644 vendor/gopkg.in/src-d/go-git.v4/CONTRIBUTING.md create mode 100644 vendor/gopkg.in/src-d/go-git.v4/DCO create mode 100644 vendor/gopkg.in/src-d/go-git.v4/LICENSE create mode 100644 vendor/gopkg.in/src-d/go-git.v4/MAINTAINERS create mode 100644 vendor/gopkg.in/src-d/go-git.v4/Makefile create mode 100644 vendor/gopkg.in/src-d/go-git.v4/README.md create mode 100644 vendor/gopkg.in/src-d/go-git.v4/appveyor.yml create mode 100644 vendor/gopkg.in/src-d/go-git.v4/blame.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/common.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/config/branch.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/config/config.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/config/modules.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/config/refspec.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/doc.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/go.mod create mode 100644 vendor/gopkg.in/src-d/go-git.v4/go.sum create mode 100644 vendor/gopkg.in/src-d/go-git.v4/internal/revision/parser.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/internal/revision/scanner.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/internal/revision/token.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/internal/url/url.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/object_walker.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/options.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/cache/buffer_lru.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/cache/common.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/cache/object_lru.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/error.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/filemode/filemode.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/common.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/decoder.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/doc.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/encoder.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/option.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/section.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/diff/patch.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/diff/unified_encoder.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/gitignore/dir.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/gitignore/doc.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/gitignore/matcher.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/gitignore/pattern.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/decoder.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/doc.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/encoder.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/idxfile.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/writer.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/decoder.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/doc.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/encoder.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/index.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/match.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/objfile/doc.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/objfile/reader.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/objfile/writer.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/common.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/delta_index.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/delta_selector.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/diff_delta.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/doc.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/encoder.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/error.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/fsobject.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/object_pack.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/packfile.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/parser.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/patch_delta.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/scanner.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/pktline/encoder.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/format/pktline/scanner.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/hash.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/memory.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/blob.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/change.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/change_adaptor.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker_bfs.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker_bfs_filtered.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker_ctime.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker_file.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/common.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/difftree.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/file.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/merge_base.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/object.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/patch.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/tag.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/tree.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/object/treenoder.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/advrefs.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/advrefs_decode.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/advrefs_encode.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability/capability.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability/list.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/common.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/doc.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/report_status.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/shallowupd.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband/common.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband/demux.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband/doc.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband/muxer.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/srvresp.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/ulreq.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/ulreq_decode.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/ulreq_encode.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/updreq.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/updreq_decode.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/updreq_encode.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/uppackreq.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/uppackresp.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/reference.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/revision.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/revlist/revlist.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/storer/doc.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/storer/index.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/storer/object.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/storer/reference.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/storer/shallow.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/storer/storer.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/client/client.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/common.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/file/client.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/file/server.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/git/common.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/http/common.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/http/receive_pack.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/http/upload_pack.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/internal/common/common.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/internal/common/server.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/server/loader.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/server/server.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/ssh/auth_method.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/ssh/common.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/prune.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/references.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/remote.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/repository.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/status.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/storage/filesystem/config.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/storage/filesystem/deltaobject.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/storage/filesystem/dotgit/dotgit.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/storage/filesystem/dotgit/dotgit_rewrite_packed_refs.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/storage/filesystem/dotgit/dotgit_setref.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/storage/filesystem/dotgit/writers.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/storage/filesystem/index.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/storage/filesystem/module.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/storage/filesystem/object.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/storage/filesystem/reference.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/storage/filesystem/shallow.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/storage/filesystem/storage.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/storage/memory/storage.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/storage/storer.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/submodule.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/utils/binary/read.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/utils/binary/write.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/utils/diff/diff.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/utils/ioutil/common.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/utils/merkletrie/change.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/utils/merkletrie/difftree.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/utils/merkletrie/doc.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/utils/merkletrie/doubleiter.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/utils/merkletrie/filesystem/node.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/utils/merkletrie/index/node.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/utils/merkletrie/internal/frame/frame.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/utils/merkletrie/iter.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/utils/merkletrie/noder/noder.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/utils/merkletrie/noder/path.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/worktree.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/worktree_bsd.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/worktree_commit.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/worktree_linux.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/worktree_status.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/worktree_unix_other.go create mode 100644 vendor/gopkg.in/src-d/go-git.v4/worktree_windows.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/httpstream/doc.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/httpstream/httpstream.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/connection.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/upgrade.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/rand/rand.go create mode 100644 vendor/k8s.io/apimachinery/pkg/util/remotecommand/constants.go create mode 100644 vendor/k8s.io/apimachinery/third_party/forked/golang/netutil/addr.go create mode 100644 vendor/k8s.io/client-go/tools/remotecommand/doc.go create mode 100644 vendor/k8s.io/client-go/tools/remotecommand/errorstream.go create mode 100644 vendor/k8s.io/client-go/tools/remotecommand/reader.go create mode 100644 vendor/k8s.io/client-go/tools/remotecommand/remotecommand.go create mode 100644 vendor/k8s.io/client-go/tools/remotecommand/resize.go create mode 100644 vendor/k8s.io/client-go/tools/remotecommand/v1.go create mode 100644 vendor/k8s.io/client-go/tools/remotecommand/v2.go create mode 100644 vendor/k8s.io/client-go/tools/remotecommand/v3.go create mode 100644 vendor/k8s.io/client-go/tools/remotecommand/v4.go create mode 100644 vendor/k8s.io/client-go/transport/spdy/spdy.go create mode 100644 vendor/k8s.io/client-go/util/exec/exec.go create mode 100644 vendor/knative.dev/kn-plugin-func/buildpacks/builder.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/build.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/client.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/completion.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/completion_util.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/config.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/config_envs.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/config_labels.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/config_volumes.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/create.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/delete.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/deploy.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/format.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/info.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/invoke.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/list.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/repository.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/root.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/run.go create mode 100644 vendor/knative.dev/kn-plugin-func/cmd/version.go create mode 100644 vendor/knative.dev/kn-plugin-func/docker/creds/credentials.go create mode 100644 vendor/knative.dev/kn-plugin-func/docker/docker_client.go create mode 100644 vendor/knative.dev/kn-plugin-func/docker/pusher.go create mode 100644 vendor/knative.dev/kn-plugin-func/docker/runner.go create mode 100644 vendor/knative.dev/kn-plugin-func/http/transport.go create mode 100644 vendor/knative.dev/kn-plugin-func/k8s/client.go create mode 100644 vendor/knative.dev/kn-plugin-func/k8s/configmaps.go create mode 100644 vendor/knative.dev/kn-plugin-func/k8s/dialer.go create mode 100644 vendor/knative.dev/kn-plugin-func/k8s/labels/labels.go create mode 100644 vendor/knative.dev/kn-plugin-func/k8s/logs.go create mode 100644 vendor/knative.dev/kn-plugin-func/k8s/persistent_volumes.go create mode 100644 vendor/knative.dev/kn-plugin-func/k8s/role_bidings.go create mode 100644 vendor/knative.dev/kn-plugin-func/k8s/secrets.go create mode 100644 vendor/knative.dev/kn-plugin-func/k8s/service_accounts.go create mode 100644 vendor/knative.dev/kn-plugin-func/knative/client.go create mode 100644 vendor/knative.dev/kn-plugin-func/knative/deployer.go create mode 100644 vendor/knative.dev/kn-plugin-func/knative/describer.go create mode 100644 vendor/knative.dev/kn-plugin-func/knative/lister.go create mode 100644 vendor/knative.dev/kn-plugin-func/knative/remover.go create mode 100644 vendor/knative.dev/kn-plugin-func/openshift/openshift.go create mode 100644 vendor/knative.dev/kn-plugin-func/pipelines/tekton/client.go create mode 100644 vendor/knative.dev/kn-plugin-func/pipelines/tekton/defaults.go create mode 100644 vendor/knative.dev/kn-plugin-func/pipelines/tekton/pipeplines_provider.go create mode 100644 vendor/knative.dev/kn-plugin-func/pipelines/tekton/resources.go create mode 100644 vendor/knative.dev/kn-plugin-func/pipelines/tekton/tasks.go create mode 100644 vendor/knative.dev/kn-plugin-func/plugin/plugin.go create mode 100644 vendor/knative.dev/kn-plugin-func/progress/progress.go create mode 100644 vendor/knative.dev/kn-plugin-func/s2i/builder.go create mode 100644 vendor/knative.dev/kn-plugin-func/s2i/docker_client_wrapper.go create mode 100644 vendor/knative.dev/kn-plugin-func/ssh/ssh_agent_conf.go create mode 100644 vendor/knative.dev/kn-plugin-func/ssh/ssh_agent_conf_windows.go create mode 100644 vendor/knative.dev/kn-plugin-func/ssh/ssh_dialer.go create mode 100644 vendor/knative.dev/kn-plugin-func/ssh/terminal.go diff --git a/go.sum b/go.sum index 2d9a2f09d6..5e0fb7cd17 100644 --- a/go.sum +++ b/go.sum @@ -125,6 +125,7 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h github.com/AdaLogics/go-fuzz-headers v0.0.0-20211102141018-f7be0cbad29c/go.mod h1:WpB7kf89yJUETZxQnP1kgYPNwlT2jjdDYUCoxVggM3g= github.com/AkihiroSuda/containerd-fuse-overlayfs v1.0.0/go.mod h1:0mMDvQFeLbbn1Wy8P2j3hwFhqBq+FKn8OZPno8WLmp8= github.com/AlecAivazis/survey/v2 v2.2.12/go.mod h1:6d4saEvBsfSHXeN1a5OA5m2+HJ2LuVokllnC77pAIKI= +github.com/AlecAivazis/survey/v2 v2.3.2 h1:TqTB+aDDCLYhf9/bD2TwSO8u8jDSmMUd2SUVO4gCnU8= github.com/AlecAivazis/survey/v2 v2.3.2/go.mod h1:TH2kPCDU3Kqq7pLbnCWwZXDBjnhZtmsCle5EiYDJ2fg= github.com/Antonboom/errname v0.1.5/go.mod h1:DugbBstvPFQbv/5uLcRRzfrNqKE9tVdVCqWCLp6Cifo= github.com/Antonboom/nilnil v0.1.0/go.mod h1:PhHLvRPSghY5Y7mX4TW+BHZQYo1A8flE5H20D3IPZBo= @@ -306,6 +307,7 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0 github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/Netflix/go-expect v0.0.0-20200312175327-da48e75238e2/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= +github.com/Netflix/go-expect v0.0.0-20210722184520-ef0bf57d82b3 h1:DM0Olh3jQEm4gVPLF0mI49+fm1+M1fXDtumX/fN/G4A= github.com/Netflix/go-expect v0.0.0-20210722184520-ef0bf57d82b3/go.mod h1:68ORG0HSEWDuH5Eh73AFbYWZ1zT4Y+b0vhOa+vZRUdI= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= @@ -352,6 +354,7 @@ github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/ahmetb/gen-crd-api-reference-docs v0.3.1-0.20210420163308-c1402a70e2f1/go.mod h1:TdjdkYhlOifCQWPs1UdTma97kQQMozf5h26hTuG70u8= github.com/ahmetb/gen-crd-api-reference-docs v0.3.1-0.20210609063737-0067dc6dcea2/go.mod h1:TdjdkYhlOifCQWPs1UdTma97kQQMozf5h26hTuG70u8= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alecthomas/jsonschema v0.0.0-20180308105923-f2c93856175a/go.mod h1:qpebaTNSsyUn5rPSJMsfqEtDw71TTggXM6stUDI16HA= github.com/alecthomas/jsonschema v0.0.0-20210526225647-edb03dcab7bc/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60= @@ -543,6 +546,7 @@ github.com/bombsimon/wsl/v2 v2.2.0/go.mod h1:Azh8c3XGEJl9LyX0/sFC+CKMc7Ssgua0g+6 github.com/bombsimon/wsl/v3 v3.0.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/bombsimon/wsl/v3 v3.1.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= +github.com/boson-project/source-to-image v1.3.2 h1:jy0MQCbc6Ix/SrK9YUeuYUVP5XRFos15I5flTXC6GlY= github.com/boson-project/source-to-image v1.3.2/go.mod h1:uT4SRJAhYbi9VbTIL0Os+0TTwTgqKwNydy7LoXrQ7v4= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= @@ -688,6 +692,7 @@ github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cE github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= +github.com/containerd/continuity v0.2.2 h1:QSqfxcn8c+12slxwu00AtzXrsami0MJb/MQs9lOLHLA= github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk= github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= @@ -736,6 +741,7 @@ github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Ev github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= +github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY7aY= github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= @@ -749,12 +755,14 @@ github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtr github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE= +github.com/containers/image/v5 v5.19.1 h1:g4/+XIuh1kRoRn2MfLDhfHhkNOIO9JtqhSyo55tjpfE= github.com/containers/image/v5 v5.19.1/go.mod h1:ewoo3u+TpJvGmsz64XgzbyTHwHtM94q7mgK/pX+v2SE= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= +github.com/containers/storage v1.38.2 h1:8bAIxnVBGKzMw5EWCivVj24bztQT6IkDp4uHiyhnzwE= github.com/containers/storage v1.38.2/go.mod h1:INP0RPLHWBxx+pTsO5uiHlDUGHDFvWZPWprAbAlQWPQ= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -788,6 +796,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKY github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyberphone/json-canonicalization v0.0.0-20210303052042-6bc126869bf4/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= github.com/cyberphone/json-canonicalization v0.0.0-20210823021906-dc406ceaf94b/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= @@ -814,10 +823,12 @@ github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8l github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M= github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE= +github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-gk v0.0.0-20140819190930-201884a44051/go.mod h1:qm+vckxRlDt0aOla0RYJJVeqHZlWfOm2UIxHaqPB46E= github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654/go.mod h1:qm+vckxRlDt0aOla0RYJJVeqHZlWfOm2UIxHaqPB46E= @@ -891,6 +902,7 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1 github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -930,6 +942,7 @@ github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2Vvl github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= @@ -1238,6 +1251,7 @@ github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -1256,6 +1270,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -1320,6 +1335,7 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/cel-go v0.11.2 h1:o16cOggWWtH1a3ZHQ8uWqt8nd255vDrEK1mDE1cFRSQ= github.com/google/cel-go v0.11.2/go.mod h1:drz+knCRsctDZ180KZHwIEEUb9IdK/nxPoyhxi+O1K0= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErVRrD+nfy23RCzyl7eqVCEmlT1Zs= @@ -1480,6 +1496,7 @@ github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1/go.mod h1:YeAe0gNeiNT5hoiZRI4yiOky6jVdNvfO2N6Kav/HmxY= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= @@ -1525,6 +1542,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b h1:wDUNC2eKiL35DbLvsDhiblTUXHxcOPwQSCzi7xpQUN4= github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0= github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok= github.com/hanwen/go-fuse/v2 v2.0.3/go.mod h1:0EQM6aH2ctVpvZ6a+onrQ/vaykxh2GH7hy3e13vzTUY= @@ -1539,6 +1557,7 @@ github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -1556,6 +1575,7 @@ github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iP github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= @@ -1614,6 +1634,7 @@ github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLa github.com/heroku/color v0.0.6 h1:UTFFMrmMLFcL3OweqP1lAdp8i1y/9oHqkeHjQ/b/Ny0= github.com/heroku/color v0.0.6/go.mod h1:ZBvOcx7cTF2QKOv4LbmoBtNl5uB17qWxGuzZrsi1wLU= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= +github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c h1:kp3AxgXgDOmIJFR7bIwqFhwJ2qWar8tEQSE5XXhCfVk= github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -1741,6 +1762,7 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -1767,6 +1789,7 @@ github.com/julz/importas v0.0.0-20210419104244-841f0c0fe66d/go.mod h1:oSFU2R4XK/ github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= @@ -1812,6 +1835,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -1938,6 +1962,7 @@ github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aks github.com/mediocregopher/radix/v4 v4.0.0/go.mod h1:ajchozX/6ELmydxWeWM6xCFHVpZ4+67LXHOTOVR0nCE= github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= github.com/mgechev/revive v1.1.2/go.mod h1:bnXsMr+ZTH09V5rssEI+jHAZ4z+ZdyhgO/zsy3EhK+0= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.17/go.mod h1:WgzbA6oji13JREwiNsRDNfl7jYdPnmz+VEuLrA+/48M= @@ -1984,6 +2009,7 @@ github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/buildkit v0.8.1/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlKZIaiQ= +github.com/moby/buildkit v0.9.3 h1:0JmMLY45KIKFogJXv4LyWo+KmIMuvhit5TDrwBlxDp0= github.com/moby/buildkit v0.9.3/go.mod h1:5dZQUHg9STw/Fhl4zZiusDJKn8uje/0x952Nce4a8cg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= @@ -2136,6 +2162,7 @@ github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.m github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= @@ -2163,6 +2190,7 @@ github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= github.com/openzipkin/zipkin-go v0.3.0 h1:XtuXmOLIXLjiU2XduuWREDT0LOKtSgos/g7i7RYyoZQ= github.com/openzipkin/zipkin-go v0.3.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ= +github.com/ory/viper v1.7.5 h1:+xVdq7SU3e1vNaCsk/ixsfxE4zylk1TJUiJrY647jUE= github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM= github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= @@ -2369,6 +2397,7 @@ github.com/sassoftware/relic v0.0.0-20210427151427-dfb082b79b74/go.mod h1:YlB8wF github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= +github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8= github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= @@ -2486,6 +2515,7 @@ github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -2518,6 +2548,7 @@ github.com/sylabs/sif/v2 v2.3.1/go.mod h1:NnvveH62GiibimL00MrI6YYcZfb7DnZMcRo/40 github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= @@ -2526,13 +2557,16 @@ github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= github.com/tektoncd/chains v0.8.0/go.mod h1:54qsO4wJjmIkSt4BAv1FMwrh+S2HhuXiw4FNGgrxkLE= +github.com/tektoncd/cli v0.23.1 h1:EpT6kF66ADpg7pjnl8TIt36Kn43e1n94UJiUS5l3ZLA= github.com/tektoncd/cli v0.23.1/go.mod h1:vyHxuzvbdQQ0/HBAko20r6rZY9mWDh6994sVfoM/HII= github.com/tektoncd/hub v1.7.0/go.mod h1:c3lL6qU1nFD9TCCrFuEnygB3KcfNn5Tbi27nZ3H6nxA= github.com/tektoncd/pipeline v0.31.1-0.20220105002759-3e137645be61/go.mod h1:dO84qW4sTq7S7Jv5G0PRmbTjvxnUAuDDxD1NBrsQX9w= github.com/tektoncd/pipeline v0.33.0/go.mod h1:FAxcjW5FRSBUDmNVXxv1Lq4YlT6Qjgh85TC9JKcxdX4= github.com/tektoncd/pipeline v0.33.1/go.mod h1:5Re8byhcUd3afN6m6IKn0SfNvbJCRmQRyEOl7fz0OXo= +github.com/tektoncd/pipeline v0.34.1 h1:foL7McnPbmn9AMU8XAz0XdBZWh+puPANG3ca03i6bEA= github.com/tektoncd/pipeline v0.34.1/go.mod h1:ZdUe14VvTODQFAxQRHrDyuTEvnM4pMY8dPI+RJufw7s= github.com/tektoncd/plumbing v0.0.0-20211012143332-c7cc43d9bc0c/go.mod h1:b9esRuV1absBvaPzKkjYdKXjC5Tgs8/vh1sz++RiTdc= +github.com/tektoncd/triggers v0.19.0 h1:/vIizEZCxc86/nM6/RSmSATaUOLqO8D9P0++nzaNxu0= github.com/tektoncd/triggers v0.19.0/go.mod h1:o1LZztX+exY/MbwRJbgbeTv1exn9rDnepX2vM2CD2q0= github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= @@ -2557,6 +2591,7 @@ github.com/tilinna/clock v1.1.0/go.mod h1:ZsP7BcY7sEEz7ktc0IVy8Us6boDrK8VradlKRU github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= +github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc= github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= @@ -3751,6 +3786,7 @@ gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76 gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= +gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg= gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE= gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= diff --git a/pkg/kn/root/plugin_register.go b/pkg/kn/root/plugin_register.go index f490ce2530..6100004f3a 100644 --- a/pkg/kn/root/plugin_register.go +++ b/pkg/kn/root/plugin_register.go @@ -19,7 +19,7 @@ package root import ( // Add #plugins# import here. Don't remove this line, it triggers an automatic replacement. _ "knative.dev/kn-plugin-event/pkg/plugin" - _ "knative.dev/kn-plugin-func" + _ "knative.dev/kn-plugin-func/plugin" _ "knative.dev/kn-plugin-source-kafka/plugin" ) diff --git a/third_party/VENDOR-LICENSE/github.com/AlecAivazis/survey/v2/LICENSE b/third_party/VENDOR-LICENSE/github.com/AlecAivazis/survey/v2/LICENSE new file mode 100644 index 0000000000..07a709ae28 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/AlecAivazis/survey/v2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Alec Aivazis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/AlecAivazis/survey/v2/terminal/LICENSE.txt b/third_party/VENDOR-LICENSE/github.com/AlecAivazis/survey/v2/terminal/LICENSE.txt new file mode 100644 index 0000000000..ade5fef6d0 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/AlecAivazis/survey/v2/terminal/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2014 Takashi Kokubun + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/BurntSushi/toml/COPYING b/third_party/VENDOR-LICENSE/github.com/BurntSushi/toml/COPYING new file mode 100644 index 0000000000..01b5743200 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/BurntSushi/toml/COPYING @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 TOML authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/Masterminds/semver/LICENSE.txt b/third_party/VENDOR-LICENSE/github.com/Masterminds/semver/LICENSE.txt new file mode 100644 index 0000000000..9ff7da9c48 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/Masterminds/semver/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (C) 2014-2019, Matt Butcher and Matt Farina + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/apex/log/LICENSE b/third_party/VENDOR-LICENSE/github.com/apex/log/LICENSE new file mode 100644 index 0000000000..af71800684 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/apex/log/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2015 TJ Holowaychuk tj@tjholowaychuk.com + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/buildpacks/imgutil/LICENSE b/third_party/VENDOR-LICENSE/github.com/buildpacks/imgutil/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/buildpacks/imgutil/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/third_party/VENDOR-LICENSE/github.com/buildpacks/lifecycle/LICENSE b/third_party/VENDOR-LICENSE/github.com/buildpacks/lifecycle/LICENSE new file mode 100644 index 0000000000..a83b632218 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/buildpacks/lifecycle/LICENSE @@ -0,0 +1,202 @@ + + 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 2018 Stephen Levine + + 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/third_party/VENDOR-LICENSE/github.com/buildpacks/pack/LICENSE b/third_party/VENDOR-LICENSE/github.com/buildpacks/pack/LICENSE new file mode 100644 index 0000000000..44eae60e00 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/buildpacks/pack/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 2018 The Cloud Native Buildpacks Authors + + 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/third_party/VENDOR-LICENSE/github.com/containerd/containerd/LICENSE b/third_party/VENDOR-LICENSE/github.com/containerd/containerd/LICENSE new file mode 100644 index 0000000000..584149b6ee --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/containerd/containerd/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright The containerd Authors + + 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 + + https://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/third_party/VENDOR-LICENSE/github.com/containerd/containerd/NOTICE b/third_party/VENDOR-LICENSE/github.com/containerd/containerd/NOTICE new file mode 100644 index 0000000000..8915f02773 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/containerd/containerd/NOTICE @@ -0,0 +1,16 @@ +Docker +Copyright 2012-2015 Docker, Inc. + +This product includes software developed at Docker, Inc. (https://www.docker.com). + +The following is courtesy of our legal counsel: + + +Use and transfer of Docker may be subject to certain restrictions by the +United States and other governments. +It is your responsibility to ensure that your use and/or transfer does not +violate applicable laws. + +For more information, please see https://www.bis.doc.gov + +See also https://www.apache.org/dev/crypto.html and/or seek legal counsel. diff --git a/third_party/VENDOR-LICENSE/k8s.io/kube-openapi/pkg/util/proto/LICENSE b/third_party/VENDOR-LICENSE/github.com/containerd/stargz-snapshotter/estargz/LICENSE similarity index 100% rename from third_party/VENDOR-LICENSE/k8s.io/kube-openapi/pkg/util/proto/LICENSE rename to third_party/VENDOR-LICENSE/github.com/containerd/stargz-snapshotter/estargz/LICENSE diff --git a/third_party/VENDOR-LICENSE/github.com/containers/image/v5/LICENSE b/third_party/VENDOR-LICENSE/github.com/containers/image/v5/LICENSE new file mode 100644 index 0000000000..9535635306 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/containers/image/v5/LICENSE @@ -0,0 +1,189 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + 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 + + https://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/third_party/VENDOR-LICENSE/github.com/containers/storage/pkg/LICENSE b/third_party/VENDOR-LICENSE/github.com/containers/storage/pkg/LICENSE new file mode 100644 index 0000000000..8f3fee627a --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/containers/storage/pkg/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright 2013-2016 Docker, Inc. + + 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 + + https://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/third_party/VENDOR-LICENSE/github.com/containers/storage/pkg/NOTICE b/third_party/VENDOR-LICENSE/github.com/containers/storage/pkg/NOTICE new file mode 100644 index 0000000000..8a37c1c7bc --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/containers/storage/pkg/NOTICE @@ -0,0 +1,19 @@ +Docker +Copyright 2012-2016 Docker, Inc. + +This product includes software developed at Docker, Inc. (https://www.docker.com). + +This product contains software (https://github.com/kr/pty) developed +by Keith Rarick, licensed under the MIT License. + +The following is courtesy of our legal counsel: + + +Use and transfer of Docker may be subject to certain restrictions by the +United States and other governments. +It is your responsibility to ensure that your use and/or transfer does not +violate applicable laws. + +For more information, please see https://www.bis.doc.gov + +See also https://www.apache.org/dev/crypto.html and/or seek legal counsel. diff --git a/third_party/VENDOR-LICENSE/github.com/dgraph-io/ristretto/LICENSE b/third_party/VENDOR-LICENSE/github.com/dgraph-io/ristretto/LICENSE new file mode 100644 index 0000000000..d9a10c0d8e --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/dgraph-io/ristretto/LICENSE @@ -0,0 +1,176 @@ + 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 diff --git a/third_party/VENDOR-LICENSE/github.com/dgraph-io/ristretto/z/LICENSE b/third_party/VENDOR-LICENSE/github.com/dgraph-io/ristretto/z/LICENSE new file mode 100644 index 0000000000..0860cbfe85 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/dgraph-io/ristretto/z/LICENSE @@ -0,0 +1,64 @@ +bbloom.go + +// The MIT License (MIT) +// Copyright (c) 2014 Andreas Briese, eduToolbox@Bri-C GmbH, Sarstedt + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +rtutil.go + +// MIT License + +// Copyright (c) 2019 Ewan Chou + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +Modifications: + +/* + * Copyright 2019 Dgraph Labs, Inc. and Contributors + * + * 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/third_party/VENDOR-LICENSE/github.com/docker/cli/cli/LICENSE b/third_party/VENDOR-LICENSE/github.com/docker/cli/cli/LICENSE new file mode 100644 index 0000000000..9c8e20ab85 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/docker/cli/cli/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright 2013-2017 Docker, Inc. + + 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 + + https://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/third_party/VENDOR-LICENSE/github.com/docker/cli/cli/NOTICE b/third_party/VENDOR-LICENSE/github.com/docker/cli/cli/NOTICE new file mode 100644 index 0000000000..58b19b6d15 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/docker/cli/cli/NOTICE @@ -0,0 +1,19 @@ +Docker +Copyright 2012-2017 Docker, Inc. + +This product includes software developed at Docker, Inc. (https://www.docker.com). + +This product contains software (https://github.com/creack/pty) developed +by Keith Rarick, licensed under the MIT License. + +The following is courtesy of our legal counsel: + + +Use and transfer of Docker may be subject to certain restrictions by the +United States and other governments. +It is your responsibility to ensure that your use and/or transfer does not +violate applicable laws. + +For more information, please see https://www.bis.doc.gov + +See also https://www.apache.org/dev/crypto.html and/or seek legal counsel. diff --git a/third_party/VENDOR-LICENSE/github.com/docker/distribution/LICENSE b/third_party/VENDOR-LICENSE/github.com/docker/distribution/LICENSE new file mode 100644 index 0000000000..e06d208186 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/docker/distribution/LICENSE @@ -0,0 +1,202 @@ +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/third_party/VENDOR-LICENSE/github.com/docker/docker-credential-helpers/LICENSE b/third_party/VENDOR-LICENSE/github.com/docker/docker-credential-helpers/LICENSE new file mode 100644 index 0000000000..1ea555e2af --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/docker/docker-credential-helpers/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2016 David Calavera + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/docker/docker/LICENSE b/third_party/VENDOR-LICENSE/github.com/docker/docker/LICENSE new file mode 100644 index 0000000000..6d8d58fb67 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/docker/docker/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright 2013-2018 Docker, Inc. + + 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 + + https://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/third_party/VENDOR-LICENSE/github.com/docker/docker/NOTICE b/third_party/VENDOR-LICENSE/github.com/docker/docker/NOTICE new file mode 100644 index 0000000000..58b19b6d15 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/docker/docker/NOTICE @@ -0,0 +1,19 @@ +Docker +Copyright 2012-2017 Docker, Inc. + +This product includes software developed at Docker, Inc. (https://www.docker.com). + +This product contains software (https://github.com/creack/pty) developed +by Keith Rarick, licensed under the MIT License. + +The following is courtesy of our legal counsel: + + +Use and transfer of Docker may be subject to certain restrictions by the +United States and other governments. +It is your responsibility to ensure that your use and/or transfer does not +violate applicable laws. + +For more information, please see https://www.bis.doc.gov + +See also https://www.apache.org/dev/crypto.html and/or seek legal counsel. diff --git a/third_party/VENDOR-LICENSE/github.com/docker/go-connections/LICENSE b/third_party/VENDOR-LICENSE/github.com/docker/go-connections/LICENSE new file mode 100644 index 0000000000..b55b37bc31 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/docker/go-connections/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright 2015 Docker, Inc. + + 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 + + https://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/third_party/VENDOR-LICENSE/github.com/docker/go-units/LICENSE b/third_party/VENDOR-LICENSE/github.com/docker/go-units/LICENSE new file mode 100644 index 0000000000..b55b37bc31 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/docker/go-units/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright 2015 Docker, Inc. + + 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 + + https://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/third_party/VENDOR-LICENSE/github.com/dustin/go-humanize/LICENSE b/third_party/VENDOR-LICENSE/github.com/dustin/go-humanize/LICENSE new file mode 100644 index 0000000000..8d9a94a906 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/dustin/go-humanize/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2005-2008 Dustin Sallings + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + diff --git a/third_party/VENDOR-LICENSE/github.com/emicklei/go-restful/LICENSE b/third_party/VENDOR-LICENSE/github.com/emicklei/go-restful/LICENSE new file mode 100644 index 0000000000..ece7ec61ef --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/emicklei/go-restful/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2012,2013 Ernest Micklei + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/third_party/VENDOR-LICENSE/github.com/fatih/color/LICENSE.md b/third_party/VENDOR-LICENSE/github.com/fatih/color/LICENSE.md new file mode 100644 index 0000000000..25fdaf639d --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/fatih/color/LICENSE.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Fatih Arslan + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/gdamore/encoding/LICENSE b/third_party/VENDOR-LICENSE/github.com/gdamore/encoding/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/gdamore/encoding/LICENSE @@ -0,0 +1,202 @@ + + 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/third_party/VENDOR-LICENSE/github.com/gdamore/tcell/v2/LICENSE b/third_party/VENDOR-LICENSE/github.com/gdamore/tcell/v2/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/gdamore/tcell/v2/LICENSE @@ -0,0 +1,202 @@ + + 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/third_party/VENDOR-LICENSE/github.com/golang/glog/LICENSE b/third_party/VENDOR-LICENSE/github.com/golang/glog/LICENSE new file mode 100644 index 0000000000..37ec93a14f --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/golang/glog/LICENSE @@ -0,0 +1,191 @@ +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: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +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 +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/third_party/VENDOR-LICENSE/github.com/google/cel-go/LICENSE b/third_party/VENDOR-LICENSE/github.com/google/cel-go/LICENSE new file mode 100644 index 0000000000..2493ed2eb4 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/google/cel-go/LICENSE @@ -0,0 +1,233 @@ + + 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. + +=========================================================================== +The common/types/pb/equal.go modification of proto.Equal logic +=========================================================================== +Copyright (c) 2018 The Go Authors. 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. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +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 +OWNER 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/third_party/VENDOR-LICENSE/github.com/google/go-containerregistry/pkg/name/LICENSE b/third_party/VENDOR-LICENSE/github.com/google/go-containerregistry/LICENSE similarity index 100% rename from third_party/VENDOR-LICENSE/github.com/google/go-containerregistry/pkg/name/LICENSE rename to third_party/VENDOR-LICENSE/github.com/google/go-containerregistry/LICENSE diff --git a/third_party/VENDOR-LICENSE/github.com/hako/durafmt/LICENSE b/third_party/VENDOR-LICENSE/github.com/hako/durafmt/LICENSE new file mode 100644 index 0000000000..ccb595049d --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hako/durafmt/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Wesley Hill + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/errwrap/LICENSE b/third_party/VENDOR-LICENSE/github.com/hashicorp/errwrap/LICENSE new file mode 100644 index 0000000000..c33dcc7c92 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/errwrap/LICENSE @@ -0,0 +1,354 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. + diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/errwrap/README.md b/third_party/VENDOR-LICENSE/github.com/hashicorp/errwrap/README.md new file mode 100644 index 0000000000..444df08f8e --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/errwrap/README.md @@ -0,0 +1,89 @@ +# errwrap + +`errwrap` is a package for Go that formalizes the pattern of wrapping errors +and checking if an error contains another error. + +There is a common pattern in Go of taking a returned `error` value and +then wrapping it (such as with `fmt.Errorf`) before returning it. The problem +with this pattern is that you completely lose the original `error` structure. + +Arguably the _correct_ approach is that you should make a custom structure +implementing the `error` interface, and have the original error as a field +on that structure, such [as this example](http://golang.org/pkg/os/#PathError). +This is a good approach, but you have to know the entire chain of possible +rewrapping that happens, when you might just care about one. + +`errwrap` formalizes this pattern (it doesn't matter what approach you use +above) by giving a single interface for wrapping errors, checking if a specific +error is wrapped, and extracting that error. + +## Installation and Docs + +Install using `go get github.com/hashicorp/errwrap`. + +Full documentation is available at +http://godoc.org/github.com/hashicorp/errwrap + +## Usage + +#### Basic Usage + +Below is a very basic example of its usage: + +```go +// A function that always returns an error, but wraps it, like a real +// function might. +func tryOpen() error { + _, err := os.Open("/i/dont/exist") + if err != nil { + return errwrap.Wrapf("Doesn't exist: {{err}}", err) + } + + return nil +} + +func main() { + err := tryOpen() + + // We can use the Contains helpers to check if an error contains + // another error. It is safe to do this with a nil error, or with + // an error that doesn't even use the errwrap package. + if errwrap.Contains(err, "does not exist") { + // Do something + } + if errwrap.ContainsType(err, new(os.PathError)) { + // Do something + } + + // Or we can use the associated `Get` functions to just extract + // a specific error. This would return nil if that specific error doesn't + // exist. + perr := errwrap.GetType(err, new(os.PathError)) +} +``` + +#### Custom Types + +If you're already making custom types that properly wrap errors, then +you can get all the functionality of `errwraps.Contains` and such by +implementing the `Wrapper` interface with just one function. Example: + +```go +type AppError { + Code ErrorCode + Err error +} + +func (e *AppError) WrappedErrors() []error { + return []error{e.Err} +} +``` + +Now this works: + +```go +err := &AppError{Err: fmt.Errorf("an error")} +if errwrap.ContainsType(err, fmt.Errorf("")) { + // This will work! +} +``` diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/errwrap/errwrap.go b/third_party/VENDOR-LICENSE/github.com/hashicorp/errwrap/errwrap.go new file mode 100644 index 0000000000..44e368e569 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/errwrap/errwrap.go @@ -0,0 +1,178 @@ +// Package errwrap implements methods to formalize error wrapping in Go. +// +// All of the top-level functions that take an `error` are built to be able +// to take any error, not just wrapped errors. This allows you to use errwrap +// without having to type-check and type-cast everywhere. +package errwrap + +import ( + "errors" + "reflect" + "strings" +) + +// WalkFunc is the callback called for Walk. +type WalkFunc func(error) + +// Wrapper is an interface that can be implemented by custom types to +// have all the Contains, Get, etc. functions in errwrap work. +// +// When Walk reaches a Wrapper, it will call the callback for every +// wrapped error in addition to the wrapper itself. Since all the top-level +// functions in errwrap use Walk, this means that all those functions work +// with your custom type. +type Wrapper interface { + WrappedErrors() []error +} + +// Wrap defines that outer wraps inner, returning an error type that +// can be cleanly used with the other methods in this package, such as +// Contains, GetAll, etc. +// +// This function won't modify the error message at all (the outer message +// will be used). +func Wrap(outer, inner error) error { + return &wrappedError{ + Outer: outer, + Inner: inner, + } +} + +// Wrapf wraps an error with a formatting message. This is similar to using +// `fmt.Errorf` to wrap an error. If you're using `fmt.Errorf` to wrap +// errors, you should replace it with this. +// +// format is the format of the error message. The string '{{err}}' will +// be replaced with the original error message. +// +// Deprecated: Use fmt.Errorf() +func Wrapf(format string, err error) error { + outerMsg := "" + if err != nil { + outerMsg = err.Error() + } + + outer := errors.New(strings.Replace( + format, "{{err}}", outerMsg, -1)) + + return Wrap(outer, err) +} + +// Contains checks if the given error contains an error with the +// message msg. If err is not a wrapped error, this will always return +// false unless the error itself happens to match this msg. +func Contains(err error, msg string) bool { + return len(GetAll(err, msg)) > 0 +} + +// ContainsType checks if the given error contains an error with +// the same concrete type as v. If err is not a wrapped error, this will +// check the err itself. +func ContainsType(err error, v interface{}) bool { + return len(GetAllType(err, v)) > 0 +} + +// Get is the same as GetAll but returns the deepest matching error. +func Get(err error, msg string) error { + es := GetAll(err, msg) + if len(es) > 0 { + return es[len(es)-1] + } + + return nil +} + +// GetType is the same as GetAllType but returns the deepest matching error. +func GetType(err error, v interface{}) error { + es := GetAllType(err, v) + if len(es) > 0 { + return es[len(es)-1] + } + + return nil +} + +// GetAll gets all the errors that might be wrapped in err with the +// given message. The order of the errors is such that the outermost +// matching error (the most recent wrap) is index zero, and so on. +func GetAll(err error, msg string) []error { + var result []error + + Walk(err, func(err error) { + if err.Error() == msg { + result = append(result, err) + } + }) + + return result +} + +// GetAllType gets all the errors that are the same type as v. +// +// The order of the return value is the same as described in GetAll. +func GetAllType(err error, v interface{}) []error { + var result []error + + var search string + if v != nil { + search = reflect.TypeOf(v).String() + } + Walk(err, func(err error) { + var needle string + if err != nil { + needle = reflect.TypeOf(err).String() + } + + if needle == search { + result = append(result, err) + } + }) + + return result +} + +// Walk walks all the wrapped errors in err and calls the callback. If +// err isn't a wrapped error, this will be called once for err. If err +// is a wrapped error, the callback will be called for both the wrapper +// that implements error as well as the wrapped error itself. +func Walk(err error, cb WalkFunc) { + if err == nil { + return + } + + switch e := err.(type) { + case *wrappedError: + cb(e.Outer) + Walk(e.Inner, cb) + case Wrapper: + cb(err) + + for _, err := range e.WrappedErrors() { + Walk(err, cb) + } + case interface{ Unwrap() error }: + cb(err) + Walk(e.Unwrap(), cb) + default: + cb(err) + } +} + +// wrappedError is an implementation of error that has both the +// outer and inner errors. +type wrappedError struct { + Outer error + Inner error +} + +func (w *wrappedError) Error() string { + return w.Outer.Error() +} + +func (w *wrappedError) WrappedErrors() []error { + return []error{w.Outer, w.Inner} +} + +func (w *wrappedError) Unwrap() error { + return w.Inner +} diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/errwrap/go.mod b/third_party/VENDOR-LICENSE/github.com/hashicorp/errwrap/go.mod new file mode 100644 index 0000000000..c9b84022cf --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/errwrap/go.mod @@ -0,0 +1 @@ +module github.com/hashicorp/errwrap diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/LICENSE b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/LICENSE new file mode 100644 index 0000000000..82b4de97c7 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/LICENSE @@ -0,0 +1,353 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/Makefile b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/Makefile new file mode 100644 index 0000000000..b97cd6ed02 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/Makefile @@ -0,0 +1,31 @@ +TEST?=./... + +default: test + +# test runs the test suite and vets the code. +test: generate + @echo "==> Running tests..." + @go list $(TEST) \ + | grep -v "/vendor/" \ + | xargs -n1 go test -timeout=60s -parallel=10 ${TESTARGS} + +# testrace runs the race checker +testrace: generate + @echo "==> Running tests (race)..." + @go list $(TEST) \ + | grep -v "/vendor/" \ + | xargs -n1 go test -timeout=60s -race ${TESTARGS} + +# updatedeps installs all the dependencies needed to run and build. +updatedeps: + @sh -c "'${CURDIR}/scripts/deps.sh' '${NAME}'" + +# generate runs `go generate` to build the dynamically generated source files. +generate: + @echo "==> Generating..." + @find . -type f -name '.DS_Store' -delete + @go list ./... \ + | grep -v "/vendor/" \ + | xargs -n1 go generate + +.PHONY: default test testrace updatedeps generate diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/README.md b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/README.md new file mode 100644 index 0000000000..71dd308ed8 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/README.md @@ -0,0 +1,150 @@ +# go-multierror + +[![CircleCI](https://img.shields.io/circleci/build/github/hashicorp/go-multierror/master)](https://circleci.com/gh/hashicorp/go-multierror) +[![Go Reference](https://pkg.go.dev/badge/github.com/hashicorp/go-multierror.svg)](https://pkg.go.dev/github.com/hashicorp/go-multierror) +![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/hashicorp/go-multierror) + +[circleci]: https://app.circleci.com/pipelines/github/hashicorp/go-multierror +[godocs]: https://pkg.go.dev/github.com/hashicorp/go-multierror + +`go-multierror` is a package for Go that provides a mechanism for +representing a list of `error` values as a single `error`. + +This allows a function in Go to return an `error` that might actually +be a list of errors. If the caller knows this, they can unwrap the +list and access the errors. If the caller doesn't know, the error +formats to a nice human-readable format. + +`go-multierror` is fully compatible with the Go standard library +[errors](https://golang.org/pkg/errors/) package, including the +functions `As`, `Is`, and `Unwrap`. This provides a standardized approach +for introspecting on error values. + +## Installation and Docs + +Install using `go get github.com/hashicorp/go-multierror`. + +Full documentation is available at +https://pkg.go.dev/github.com/hashicorp/go-multierror + +### Requires go version 1.13 or newer + +`go-multierror` requires go version 1.13 or newer. Go 1.13 introduced +[error wrapping](https://golang.org/doc/go1.13#error_wrapping), which +this library takes advantage of. + +If you need to use an earlier version of go, you can use the +[v1.0.0](https://github.com/hashicorp/go-multierror/tree/v1.0.0) +tag, which doesn't rely on features in go 1.13. + +If you see compile errors that look like the below, it's likely that +you're on an older version of go: + +``` +/go/src/github.com/hashicorp/go-multierror/multierror.go:112:9: undefined: errors.As +/go/src/github.com/hashicorp/go-multierror/multierror.go:117:9: undefined: errors.Is +``` + +## Usage + +go-multierror is easy to use and purposely built to be unobtrusive in +existing Go applications/libraries that may not be aware of it. + +**Building a list of errors** + +The `Append` function is used to create a list of errors. This function +behaves a lot like the Go built-in `append` function: it doesn't matter +if the first argument is nil, a `multierror.Error`, or any other `error`, +the function behaves as you would expect. + +```go +var result error + +if err := step1(); err != nil { + result = multierror.Append(result, err) +} +if err := step2(); err != nil { + result = multierror.Append(result, err) +} + +return result +``` + +**Customizing the formatting of the errors** + +By specifying a custom `ErrorFormat`, you can customize the format +of the `Error() string` function: + +```go +var result *multierror.Error + +// ... accumulate errors here, maybe using Append + +if result != nil { + result.ErrorFormat = func([]error) string { + return "errors!" + } +} +``` + +**Accessing the list of errors** + +`multierror.Error` implements `error` so if the caller doesn't know about +multierror, it will work just fine. But if you're aware a multierror might +be returned, you can use type switches to access the list of errors: + +```go +if err := something(); err != nil { + if merr, ok := err.(*multierror.Error); ok { + // Use merr.Errors + } +} +``` + +You can also use the standard [`errors.Unwrap`](https://golang.org/pkg/errors/#Unwrap) +function. This will continue to unwrap into subsequent errors until none exist. + +**Extracting an error** + +The standard library [`errors.As`](https://golang.org/pkg/errors/#As) +function can be used directly with a multierror to extract a specific error: + +```go +// Assume err is a multierror value +err := somefunc() + +// We want to know if "err" has a "RichErrorType" in it and extract it. +var errRich RichErrorType +if errors.As(err, &errRich) { + // It has it, and now errRich is populated. +} +``` + +**Checking for an exact error value** + +Some errors are returned as exact errors such as the [`ErrNotExist`](https://golang.org/pkg/os/#pkg-variables) +error in the `os` package. You can check if this error is present by using +the standard [`errors.Is`](https://golang.org/pkg/errors/#Is) function. + +```go +// Assume err is a multierror value +err := somefunc() +if errors.Is(err, os.ErrNotExist) { + // err contains os.ErrNotExist +} +``` + +**Returning a multierror only if there are errors** + +If you build a `multierror.Error`, you can use the `ErrorOrNil` function +to return an `error` implementation only if there are errors to return: + +```go +var result *multierror.Error + +// ... accumulate errors here + +// Return the `error` only if errors were added to the multierror, otherwise +// return nil since there are no errors. +return result.ErrorOrNil() +``` diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/append.go b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/append.go new file mode 100644 index 0000000000..3e2589bfde --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/append.go @@ -0,0 +1,43 @@ +package multierror + +// Append is a helper function that will append more errors +// onto an Error in order to create a larger multi-error. +// +// If err is not a multierror.Error, then it will be turned into +// one. If any of the errs are multierr.Error, they will be flattened +// one level into err. +// Any nil errors within errs will be ignored. If err is nil, a new +// *Error will be returned. +func Append(err error, errs ...error) *Error { + switch err := err.(type) { + case *Error: + // Typed nils can reach here, so initialize if we are nil + if err == nil { + err = new(Error) + } + + // Go through each error and flatten + for _, e := range errs { + switch e := e.(type) { + case *Error: + if e != nil { + err.Errors = append(err.Errors, e.Errors...) + } + default: + if e != nil { + err.Errors = append(err.Errors, e) + } + } + } + + return err + default: + newErrs := make([]error, 0, len(errs)+1) + if err != nil { + newErrs = append(newErrs, err) + } + newErrs = append(newErrs, errs...) + + return Append(&Error{}, newErrs...) + } +} diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/flatten.go b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/flatten.go new file mode 100644 index 0000000000..aab8e9abec --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/flatten.go @@ -0,0 +1,26 @@ +package multierror + +// Flatten flattens the given error, merging any *Errors together into +// a single *Error. +func Flatten(err error) error { + // If it isn't an *Error, just return the error as-is + if _, ok := err.(*Error); !ok { + return err + } + + // Otherwise, make the result and flatten away! + flatErr := new(Error) + flatten(err, flatErr) + return flatErr +} + +func flatten(err error, flatErr *Error) { + switch err := err.(type) { + case *Error: + for _, e := range err.Errors { + flatten(e, flatErr) + } + default: + flatErr.Errors = append(flatErr.Errors, err) + } +} diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/format.go b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/format.go new file mode 100644 index 0000000000..47f13c49a6 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/format.go @@ -0,0 +1,27 @@ +package multierror + +import ( + "fmt" + "strings" +) + +// ErrorFormatFunc is a function callback that is called by Error to +// turn the list of errors into a string. +type ErrorFormatFunc func([]error) string + +// ListFormatFunc is a basic formatter that outputs the number of errors +// that occurred along with a bullet point list of the errors. +func ListFormatFunc(es []error) string { + if len(es) == 1 { + return fmt.Sprintf("1 error occurred:\n\t* %s\n\n", es[0]) + } + + points := make([]string, len(es)) + for i, err := range es { + points[i] = fmt.Sprintf("* %s", err) + } + + return fmt.Sprintf( + "%d errors occurred:\n\t%s\n\n", + len(es), strings.Join(points, "\n\t")) +} diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/go.mod b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/go.mod new file mode 100644 index 0000000000..141cc4ccb2 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/go.mod @@ -0,0 +1,5 @@ +module github.com/hashicorp/go-multierror + +go 1.13 + +require github.com/hashicorp/errwrap v1.0.0 diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/go.sum b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/go.sum new file mode 100644 index 0000000000..e8238e9ec9 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/go.sum @@ -0,0 +1,2 @@ +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/group.go b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/group.go new file mode 100644 index 0000000000..9c29efb7f8 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/group.go @@ -0,0 +1,38 @@ +package multierror + +import "sync" + +// Group is a collection of goroutines which return errors that need to be +// coalesced. +type Group struct { + mutex sync.Mutex + err *Error + wg sync.WaitGroup +} + +// Go calls the given function in a new goroutine. +// +// If the function returns an error it is added to the group multierror which +// is returned by Wait. +func (g *Group) Go(f func() error) { + g.wg.Add(1) + + go func() { + defer g.wg.Done() + + if err := f(); err != nil { + g.mutex.Lock() + g.err = Append(g.err, err) + g.mutex.Unlock() + } + }() +} + +// Wait blocks until all function calls from the Go method have returned, then +// returns the multierror. +func (g *Group) Wait() *Error { + g.wg.Wait() + g.mutex.Lock() + defer g.mutex.Unlock() + return g.err +} diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/multierror.go b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/multierror.go new file mode 100644 index 0000000000..f545743264 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/multierror.go @@ -0,0 +1,121 @@ +package multierror + +import ( + "errors" + "fmt" +) + +// Error is an error type to track multiple errors. This is used to +// accumulate errors in cases and return them as a single "error". +type Error struct { + Errors []error + ErrorFormat ErrorFormatFunc +} + +func (e *Error) Error() string { + fn := e.ErrorFormat + if fn == nil { + fn = ListFormatFunc + } + + return fn(e.Errors) +} + +// ErrorOrNil returns an error interface if this Error represents +// a list of errors, or returns nil if the list of errors is empty. This +// function is useful at the end of accumulation to make sure that the value +// returned represents the existence of errors. +func (e *Error) ErrorOrNil() error { + if e == nil { + return nil + } + if len(e.Errors) == 0 { + return nil + } + + return e +} + +func (e *Error) GoString() string { + return fmt.Sprintf("*%#v", *e) +} + +// WrappedErrors returns the list of errors that this Error is wrapping. It is +// an implementation of the errwrap.Wrapper interface so that multierror.Error +// can be used with that library. +// +// This method is not safe to be called concurrently. Unlike accessing the +// Errors field directly, this function also checks if the multierror is nil to +// prevent a null-pointer panic. It satisfies the errwrap.Wrapper interface. +func (e *Error) WrappedErrors() []error { + if e == nil { + return nil + } + return e.Errors +} + +// Unwrap returns an error from Error (or nil if there are no errors). +// This error returned will further support Unwrap to get the next error, +// etc. The order will match the order of Errors in the multierror.Error +// at the time of calling. +// +// The resulting error supports errors.As/Is/Unwrap so you can continue +// to use the stdlib errors package to introspect further. +// +// This will perform a shallow copy of the errors slice. Any errors appended +// to this error after calling Unwrap will not be available until a new +// Unwrap is called on the multierror.Error. +func (e *Error) Unwrap() error { + // If we have no errors then we do nothing + if e == nil || len(e.Errors) == 0 { + return nil + } + + // If we have exactly one error, we can just return that directly. + if len(e.Errors) == 1 { + return e.Errors[0] + } + + // Shallow copy the slice + errs := make([]error, len(e.Errors)) + copy(errs, e.Errors) + return chain(errs) +} + +// chain implements the interfaces necessary for errors.Is/As/Unwrap to +// work in a deterministic way with multierror. A chain tracks a list of +// errors while accounting for the current represented error. This lets +// Is/As be meaningful. +// +// Unwrap returns the next error. In the cleanest form, Unwrap would return +// the wrapped error here but we can't do that if we want to properly +// get access to all the errors. Instead, users are recommended to use +// Is/As to get the correct error type out. +// +// Precondition: []error is non-empty (len > 0) +type chain []error + +// Error implements the error interface +func (e chain) Error() string { + return e[0].Error() +} + +// Unwrap implements errors.Unwrap by returning the next error in the +// chain or nil if there are no more errors. +func (e chain) Unwrap() error { + if len(e) == 1 { + return nil + } + + return e[1:] +} + +// As implements errors.As by attempting to map to the current value. +func (e chain) As(target interface{}) bool { + return errors.As(e[0], target) +} + +// Is implements errors.Is by comparing the current value directly. +func (e chain) Is(target error) bool { + return errors.Is(e[0], target) +} diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/prefix.go b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/prefix.go new file mode 100644 index 0000000000..5c477abe44 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/prefix.go @@ -0,0 +1,37 @@ +package multierror + +import ( + "fmt" + + "github.com/hashicorp/errwrap" +) + +// Prefix is a helper function that will prefix some text +// to the given error. If the error is a multierror.Error, then +// it will be prefixed to each wrapped error. +// +// This is useful to use when appending multiple multierrors +// together in order to give better scoping. +func Prefix(err error, prefix string) error { + if err == nil { + return nil + } + + format := fmt.Sprintf("%s {{err}}", prefix) + switch err := err.(type) { + case *Error: + // Typed nils can reach here, so initialize if we are nil + if err == nil { + err = new(Error) + } + + // Wrap each of the errors + for i, e := range err.Errors { + err.Errors[i] = errwrap.Wrapf(format, e) + } + + return err + default: + return errwrap.Wrapf(format, err) + } +} diff --git a/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/sort.go b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/sort.go new file mode 100644 index 0000000000..fecb14e81c --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/hashicorp/go-multierror/sort.go @@ -0,0 +1,16 @@ +package multierror + +// Len implements sort.Interface function for length +func (err Error) Len() int { + return len(err.Errors) +} + +// Swap implements sort.Interface function for swapping elements +func (err Error) Swap(i, j int) { + err.Errors[i], err.Errors[j] = err.Errors[j], err.Errors[i] +} + +// Less implements sort.Interface function for determining order +func (err Error) Less(i, j int) bool { + return err.Errors[i].Error() < err.Errors[j].Error() +} diff --git a/third_party/VENDOR-LICENSE/github.com/heroku/color/LICENSE b/third_party/VENDOR-LICENSE/github.com/heroku/color/LICENSE new file mode 100644 index 0000000000..d23abe7400 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/heroku/color/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Heroku + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/jonboulle/clockwork/LICENSE b/third_party/VENDOR-LICENSE/github.com/jonboulle/clockwork/LICENSE new file mode 100644 index 0000000000..5c304d1a4a --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/jonboulle/clockwork/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/third_party/VENDOR-LICENSE/github.com/kballard/go-shellquote/LICENSE b/third_party/VENDOR-LICENSE/github.com/kballard/go-shellquote/LICENSE new file mode 100644 index 0000000000..a6d77312e1 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/kballard/go-shellquote/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2014 Kevin Ballard + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/lucasb-eyer/go-colorful/LICENSE b/third_party/VENDOR-LICENSE/github.com/lucasb-eyer/go-colorful/LICENSE new file mode 100644 index 0000000000..4e402a00e5 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/lucasb-eyer/go-colorful/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2013 Lucas Beyer + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/mattn/go-colorable/LICENSE b/third_party/VENDOR-LICENSE/github.com/mattn/go-colorable/LICENSE new file mode 100644 index 0000000000..91b5cef30e --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/mattn/go-colorable/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Yasuhiro Matsumoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/mattn/go-isatty/LICENSE b/third_party/VENDOR-LICENSE/github.com/mattn/go-isatty/LICENSE new file mode 100644 index 0000000000..65dc692b6b --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/mattn/go-isatty/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) Yasuhiro MATSUMOTO + +MIT License (Expat) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/mattn/go-runewidth/LICENSE b/third_party/VENDOR-LICENSE/github.com/mattn/go-runewidth/LICENSE new file mode 100644 index 0000000000..91b5cef30e --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/mattn/go-runewidth/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Yasuhiro Matsumoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/mgutz/ansi/LICENSE b/third_party/VENDOR-LICENSE/github.com/mgutz/ansi/LICENSE new file mode 100644 index 0000000000..06ce0c3b51 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/mgutz/ansi/LICENSE @@ -0,0 +1,9 @@ +The MIT License (MIT) +Copyright (c) 2013 Mario L. Gutierrez + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/third_party/VENDOR-LICENSE/github.com/mitchellh/ioprogress/LICENSE b/third_party/VENDOR-LICENSE/github.com/mitchellh/ioprogress/LICENSE new file mode 100644 index 0000000000..2298515904 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/mitchellh/ioprogress/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Mitchell Hashimoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/moby/spdystream/LICENSE b/third_party/VENDOR-LICENSE/github.com/moby/spdystream/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/moby/spdystream/LICENSE @@ -0,0 +1,202 @@ + + 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/third_party/VENDOR-LICENSE/github.com/moby/spdystream/NOTICE b/third_party/VENDOR-LICENSE/github.com/moby/spdystream/NOTICE new file mode 100644 index 0000000000..b9b11c9ab7 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/moby/spdystream/NOTICE @@ -0,0 +1,5 @@ +SpdyStream +Copyright 2014-2021 Docker Inc. + +This product includes software developed at +Docker Inc. (https://www.docker.com/). diff --git a/third_party/VENDOR-LICENSE/github.com/moby/sys/mount/LICENSE b/third_party/VENDOR-LICENSE/github.com/moby/sys/mount/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/moby/sys/mount/LICENSE @@ -0,0 +1,202 @@ + + 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/third_party/VENDOR-LICENSE/github.com/moby/sys/mountinfo/LICENSE b/third_party/VENDOR-LICENSE/github.com/moby/sys/mountinfo/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/moby/sys/mountinfo/LICENSE @@ -0,0 +1,202 @@ + + 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/third_party/VENDOR-LICENSE/github.com/moby/term/LICENSE b/third_party/VENDOR-LICENSE/github.com/moby/term/LICENSE new file mode 100644 index 0000000000..6d8d58fb67 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/moby/term/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright 2013-2018 Docker, Inc. + + 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 + + https://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/third_party/VENDOR-LICENSE/github.com/morikuni/aec/LICENSE b/third_party/VENDOR-LICENSE/github.com/morikuni/aec/LICENSE new file mode 100644 index 0000000000..1c26401641 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/morikuni/aec/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Taihei Morikuni + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/opencontainers/go-digest/LICENSE b/third_party/VENDOR-LICENSE/github.com/opencontainers/go-digest/LICENSE new file mode 100644 index 0000000000..3ac8ab6487 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/opencontainers/go-digest/LICENSE @@ -0,0 +1,192 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright 2019, 2020 OCI Contributors + Copyright 2016 Docker, Inc. + + 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 + + https://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/third_party/VENDOR-LICENSE/github.com/opencontainers/image-spec/specs-go/LICENSE b/third_party/VENDOR-LICENSE/github.com/opencontainers/image-spec/specs-go/LICENSE new file mode 100644 index 0000000000..9fdc20fdb6 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/opencontainers/image-spec/specs-go/LICENSE @@ -0,0 +1,191 @@ + + 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 + + Copyright 2016 The Linux Foundation. + + 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/third_party/VENDOR-LICENSE/github.com/opencontainers/runc/libcontainer/user/LICENSE b/third_party/VENDOR-LICENSE/github.com/opencontainers/runc/libcontainer/user/LICENSE new file mode 100644 index 0000000000..27448585ad --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/opencontainers/runc/libcontainer/user/LICENSE @@ -0,0 +1,191 @@ + + 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 + + Copyright 2014 Docker, Inc. + + 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/third_party/VENDOR-LICENSE/github.com/opencontainers/runc/libcontainer/user/NOTICE b/third_party/VENDOR-LICENSE/github.com/opencontainers/runc/libcontainer/user/NOTICE new file mode 100644 index 0000000000..5c97abce4b --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/opencontainers/runc/libcontainer/user/NOTICE @@ -0,0 +1,17 @@ +runc + +Copyright 2012-2015 Docker, Inc. + +This product includes software developed at Docker, Inc. (http://www.docker.com). + +The following is courtesy of our legal counsel: + + +Use and transfer of Docker may be subject to certain restrictions by the +United States and other governments. +It is your responsibility to ensure that your use and/or transfer does not +violate applicable laws. + +For more information, please see http://www.bis.doc.gov + +See also http://www.apache.org/dev/crypto.html and/or seek legal counsel. diff --git a/third_party/VENDOR-LICENSE/github.com/opencontainers/runtime-spec/specs-go/LICENSE b/third_party/VENDOR-LICENSE/github.com/opencontainers/runtime-spec/specs-go/LICENSE new file mode 100644 index 0000000000..bdc403653e --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/opencontainers/runtime-spec/specs-go/LICENSE @@ -0,0 +1,191 @@ + + 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 + + Copyright 2015 The Linux Foundation. + + 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/third_party/VENDOR-LICENSE/github.com/opencontainers/selinux/LICENSE b/third_party/VENDOR-LICENSE/github.com/opencontainers/selinux/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/opencontainers/selinux/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/third_party/VENDOR-LICENSE/github.com/openshift/source-to-image/pkg/LICENSE b/third_party/VENDOR-LICENSE/github.com/openshift/source-to-image/pkg/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/openshift/source-to-image/pkg/LICENSE @@ -0,0 +1,202 @@ + + 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/third_party/VENDOR-LICENSE/github.com/ory/viper/LICENSE b/third_party/VENDOR-LICENSE/github.com/ory/viper/LICENSE new file mode 100644 index 0000000000..4527efb9c0 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/ory/viper/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Steve Francia + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/third_party/VENDOR-LICENSE/github.com/rivo/tview/LICENSE.txt b/third_party/VENDOR-LICENSE/github.com/rivo/tview/LICENSE.txt new file mode 100644 index 0000000000..9d6943073c --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/rivo/tview/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Oliver Kuederle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/rivo/uniseg/LICENSE.txt b/third_party/VENDOR-LICENSE/github.com/rivo/uniseg/LICENSE.txt new file mode 100644 index 0000000000..5040f1ef80 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/rivo/uniseg/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Oliver Kuederle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/sabhiram/go-gitignore/LICENSE b/third_party/VENDOR-LICENSE/github.com/sabhiram/go-gitignore/LICENSE new file mode 100644 index 0000000000..c606f49e5c --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/sabhiram/go-gitignore/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Shaba Abhiram + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/third_party/VENDOR-LICENSE/github.com/sirupsen/logrus/LICENSE b/third_party/VENDOR-LICENSE/github.com/sirupsen/logrus/LICENSE new file mode 100644 index 0000000000..f090cb42f3 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/sirupsen/logrus/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Simon Eskildsen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/src-d/gcfg/LICENSE b/third_party/VENDOR-LICENSE/github.com/src-d/gcfg/LICENSE new file mode 100644 index 0000000000..87a5cede33 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/src-d/gcfg/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2012 Péter Surányi. Portions Copyright (c) 2009 The Go +Authors. 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. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +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 +OWNER 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/third_party/VENDOR-LICENSE/github.com/stoewer/go-strcase/LICENSE b/third_party/VENDOR-LICENSE/github.com/stoewer/go-strcase/LICENSE new file mode 100644 index 0000000000..a105a3819a --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/stoewer/go-strcase/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017, Adrian Stoewer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/VENDOR-LICENSE/github.com/syndtr/gocapability/capability/LICENSE b/third_party/VENDOR-LICENSE/github.com/syndtr/gocapability/capability/LICENSE new file mode 100644 index 0000000000..80dd96de77 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/syndtr/gocapability/capability/LICENSE @@ -0,0 +1,24 @@ +Copyright 2013 Suryandaru Triandana +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/third_party/VENDOR-LICENSE/github.com/tektoncd/cli/pkg/LICENSE b/third_party/VENDOR-LICENSE/github.com/tektoncd/cli/pkg/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/tektoncd/cli/pkg/LICENSE @@ -0,0 +1,202 @@ + + 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/third_party/VENDOR-LICENSE/github.com/tektoncd/pipeline/pkg/LICENSE b/third_party/VENDOR-LICENSE/github.com/tektoncd/pipeline/pkg/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/tektoncd/pipeline/pkg/LICENSE @@ -0,0 +1,202 @@ + + 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/third_party/VENDOR-LICENSE/github.com/tektoncd/triggers/pkg/LICENSE b/third_party/VENDOR-LICENSE/github.com/tektoncd/triggers/pkg/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/tektoncd/triggers/pkg/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/third_party/VENDOR-LICENSE/github.com/vbatts/tar-split/archive/tar/LICENSE b/third_party/VENDOR-LICENSE/github.com/vbatts/tar-split/archive/tar/LICENSE new file mode 100644 index 0000000000..ca03685b15 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/vbatts/tar-split/archive/tar/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2015 Vincent Batts, Raleigh, NC, USA + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. 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. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +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/third_party/VENDOR-LICENSE/golang.org/x/mod/semver/LICENSE b/third_party/VENDOR-LICENSE/golang.org/x/mod/semver/LICENSE new file mode 100644 index 0000000000..6a66aea5ea --- /dev/null +++ b/third_party/VENDOR-LICENSE/golang.org/x/mod/semver/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. 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. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +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 +OWNER 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/third_party/VENDOR-LICENSE/gopkg.in/src-d/go-billy.v4/LICENSE b/third_party/VENDOR-LICENSE/gopkg.in/src-d/go-billy.v4/LICENSE new file mode 100644 index 0000000000..9d60756894 --- /dev/null +++ b/third_party/VENDOR-LICENSE/gopkg.in/src-d/go-billy.v4/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 2017 Sourced Technologies S.L. + + 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/third_party/VENDOR-LICENSE/gopkg.in/src-d/go-git.v4/LICENSE b/third_party/VENDOR-LICENSE/gopkg.in/src-d/go-git.v4/LICENSE new file mode 100644 index 0000000000..8aa3d854cf --- /dev/null +++ b/third_party/VENDOR-LICENSE/gopkg.in/src-d/go-git.v4/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 2018 Sourced Technologies, S.L. + + 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/third_party/VENDOR-LICENSE/k8s.io/kube-openapi/pkg/LICENSE b/third_party/VENDOR-LICENSE/k8s.io/kube-openapi/pkg/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/third_party/VENDOR-LICENSE/k8s.io/kube-openapi/pkg/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/AlecAivazis/survey/v2/.travis.yml b/vendor/github.com/AlecAivazis/survey/v2/.travis.yml new file mode 100644 index 0000000000..f96db1ac0e --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/.travis.yml @@ -0,0 +1,22 @@ +language: go + +go: + - 1.12 + +os: + - linux + - linux-ppc64le + - osx + - windows + +go_import_path: github.com/AlecAivazis/survey/v2 + +before_install: + - go get github.com/alecaivazis/run + +install: + - run install-deps + +script: + - run tests + # - run autoplay-tests diff --git a/vendor/github.com/AlecAivazis/survey/v2/CONTRIBUTING.md b/vendor/github.com/AlecAivazis/survey/v2/CONTRIBUTING.md new file mode 100644 index 0000000000..a84e06061e --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/CONTRIBUTING.md @@ -0,0 +1,77 @@ +# Contributing to Survey + +🎉🎉 First off, thanks for the interest in contributing to `survey`! 🎉🎉 + +The following is a set of guidelines to follow when contributing to this package. These are not hard rules, please use common sense and feel free to propose changes to this document in a pull request. + +## Table of Contents + +1. [Code of Conduct](#code-of-conduct) +1. [Getting Help](#getting-help) +1. [Filing a Bug Report](#how-to-file-a-bug-report) +1. [Suggesting an API change](#suggesting-an-api-change) +1. [Submitting a Contribution](#submitting-a-contribution) +1. [Writing and Running Tests](#writing-and-running-tests) + +## Code of Conduct + +This project and its contibutors are expected to uphold the [Go Community Code of Conduct](https://golang.org/conduct). By participating, you are expected to follow these guidelines. + +## Getting help + +Feel free to [open up an issue](https://github.com/AlecAivazis/survey/v2/issues/new) on GitHub when asking a question so others will be able to find it. Please remember to tag the issue with the `Question` label so the maintainers can get to your question as soon as possible. If the question is urgent, feel free to reach out to `@AlecAivazis` directly in the gophers slack channel. + +## How to file a bug report + +Bugs are tracked using the Github Issue tracker. When filing a bug, please remember to label the issue as a `Bug` and answer/provide the following: + +1. What operating system and terminal are you using? +1. An example that showcases the bug. +1. What did you expect to see? +1. What did you see instead? + +## Suggesting an API change + +If you have an idea, I'm more than happy to discuss it. Please open an issue and we can work through it. In order to maintain some sense of stability, additions to the top-level API are taken just as seriously as changes that break it. Adding stuff is much easier than removing it. + +## Submitting a contribution + +In order to maintain stability, most features get fully integrated in more than one PR. This allows for more opportunity to think through each API change without amassing large amounts of tech debt and API changes at once. If your feature can be broken into separate chunks, it will be able to be reviewed much quicker. For example, if the PR that implemented the `Validate` field was submitted in a PR separately from one that included `survey.Required`, it would be able to get merge without having to decide how many different `Validators` we want to provide as part of `survey`'s API. + +When submitting a contribution, + +- Provide a description of the feature or change +- Reference the ticket addressed by the PR if there is one +- Following community standards, add comments for all exported members so that all necessary information is available on godocs +- Remember to update the project README.md with changes to the high-level API +- Include both positive and negative unit tests (when applicable) +- Contributions with visual ramifications or interaction changes should be accompanied with the appropriate `go-expect` tests. For more information on writing these tests, see [Writing and Running Tests](#writing-and-running-tests) + +## Writing and running tests + +When submitting features, please add as many units tests as necessary to test both positive and negative cases. + +Integration tests for survey uses [go-expect](https://github.com/Netflix/go-expect) to expect a match on stdout and respond on stdin. Since `os.Stdout` in a `go test` process is not a TTY, you need a way to interpret terminal / ANSI escape sequences for things like `CursorLocation`. The stdin/stdout handled by `go-expect` is also multiplexed to a [virtual terminal](https://github.com/hinshun/vt10x). + +For example, you can extend the tests for Input by specifying the following test case: + +```go +{ + "Test Input prompt interaction", // Name of the test. + &Input{ // An implementation of the survey.Prompt interface. + Message: "What is your name?", + }, + func(c *expect.Console) { // An expect procedure. You can expect strings / regexps and + c.ExpectString("What is your name?") // write back strings / bytes to its psuedoterminal for survey. + c.SendLine("Johnny Appleseed") + c.ExpectEOF() // Nothing is read from the tty without an expect, and once an + // expectation is met, no further bytes are read. End your + // procedure with `c.ExpectEOF()` to read until survey finishes. + }, + "Johnny Appleseed", // The expected result. +} +``` + +If you want to write your own `go-expect` test from scratch, you'll need to instantiate a virtual terminal, +multiplex it into an `*expect.Console`, and hook up its tty with survey's optional stdio. Please see `go-expect` +[documentation](https://godoc.org/github.com/Netflix/go-expect) for more detail. diff --git a/vendor/github.com/AlecAivazis/survey/v2/Gopkg.lock b/vendor/github.com/AlecAivazis/survey/v2/Gopkg.lock new file mode 100644 index 0000000000..b764f30811 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/Gopkg.lock @@ -0,0 +1,78 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "github.com/Netflix/go-expect" + packages = ["."] + revision = "c93bf25de8e869da25cf26bcd2932b36141f61ae" + +[[projects]] + name = "github.com/davecgh/go-spew" + packages = ["spew"] + revision = "346938d642f2ec3594ed81d874461961cd0faa76" + version = "v1.1.0" + +[[projects]] + branch = "master" + name = "github.com/hinshun/vt10x" + packages = ["."] + revision = "1954e646417484a2a687ea344edade2c2b6523c8" + +[[projects]] + branch = "master" + name = "github.com/kballard/go-shellquote" + packages = ["."] + revision = "95032a82bc518f77982ea72343cc1ade730072f0" + +[[projects]] + name = "github.com/kr/pty" + packages = ["."] + revision = "282ce0e5322c82529687d609ee670fac7c7d917c" + version = "v1.1.1" + +[[projects]] + name = "github.com/mattn/go-colorable" + packages = ["."] + revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072" + version = "v0.0.9" + +[[projects]] + name = "github.com/mattn/go-isatty" + packages = ["."] + revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39" + version = "v0.0.3" + +[[projects]] + branch = "master" + name = "github.com/mgutz/ansi" + packages = ["."] + revision = "9520e82c474b0a04dd04f8a40959027271bab992" + +[[projects]] + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + revision = "792786c7400a136282c1664665ae0a8db921c6c2" + version = "v1.0.0" + +[[projects]] + name = "github.com/stretchr/testify" + packages = [ + "assert", + "require" + ] + revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" + version = "v1.2.1" + +[[projects]] + branch = "master" + name = "golang.org/x/sys" + packages = ["unix"] + revision = "9527bec2660bd847c050fda93a0f0c6dee0800bb" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "371508ebad4798adc38a118f858b5c17a65b58594203548f9feb74cb781dd907" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/vendor/github.com/AlecAivazis/survey/v2/Gopkg.toml b/vendor/github.com/AlecAivazis/survey/v2/Gopkg.toml new file mode 100644 index 0000000000..8acb825aac --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/Gopkg.toml @@ -0,0 +1,54 @@ +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + + +[[constraint]] + branch = "master" + name = "github.com/Netflix/go-expect" + +[[constraint]] + branch = "master" + name = "github.com/hinshun/vt10x" + +[[constraint]] + name = "github.com/mattn/go-isatty" + version = "0.0.3" + +[[constraint]] + branch = "master" + name = "github.com/mgutz/ansi" + +[[constraint]] + name = "github.com/stretchr/testify" + version = "1.2.1" + +[prune] + go-tests = true + unused-packages = true + +[[constraint]] + branch = "master" + name = "github.com/kballard/go-shellquote" diff --git a/vendor/github.com/AlecAivazis/survey/v2/LICENSE b/vendor/github.com/AlecAivazis/survey/v2/LICENSE new file mode 100644 index 0000000000..07a709ae28 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Alec Aivazis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/AlecAivazis/survey/v2/README.md b/vendor/github.com/AlecAivazis/survey/v2/README.md new file mode 100644 index 0000000000..5ab410e30d --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/README.md @@ -0,0 +1,483 @@ +# Survey + +[![Build Status](https://travis-ci.org/AlecAivazis/survey.svg?branch=feature%2Fpretty)](https://travis-ci.org/AlecAivazis/survey) +[![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg)](https://pkg.go.dev/github.com/AlecAivazis/survey/v2) + +A library for building interactive and accessible prompts on terminals supporting ANSI escape sequences. + + + +```go +package main + +import ( + "fmt" + "github.com/AlecAivazis/survey/v2" +) + +// the questions to ask +var qs = []*survey.Question{ + { + Name: "name", + Prompt: &survey.Input{Message: "What is your name?"}, + Validate: survey.Required, + Transform: survey.Title, + }, + { + Name: "color", + Prompt: &survey.Select{ + Message: "Choose a color:", + Options: []string{"red", "blue", "green"}, + Default: "red", + }, + }, + { + Name: "age", + Prompt: &survey.Input{Message: "How old are you?"}, + }, +} + +func main() { + // the answers will be written to this struct + answers := struct { + Name string // survey will match the question and field names + FavoriteColor string `survey:"color"` // or you can tag fields to match a specific name + Age int // if the types don't match, survey will convert it + }{} + + // perform the questions + err := survey.Ask(qs, &answers) + if err != nil { + fmt.Println(err.Error()) + return + } + + fmt.Printf("%s chose %s.", answers.Name, answers.FavoriteColor) +} +``` + +## Table of Contents + +1. [Examples](#examples) +1. [Running the Prompts](#running-the-prompts) +1. [Prompts](#prompts) + 1. [Input](#input) + 1. [Suggestion Options](#suggestion-options) + 1. [Multiline](#multiline) + 1. [Password](#password) + 1. [Confirm](#confirm) + 1. [Select](#select) + 1. [MultiSelect](#multiselect) + 1. [Editor](#editor) +1. [Filtering Options](#filtering-options) +1. [Validation](#validation) + 1. [Built-in Validators](#built-in-validators) +1. [Help Text](#help-text) + 1. [Changing the input rune](#changing-the-input-rune) +1. [Changing the Icons ](#changing-the-icons) +1. [Custom Types](#custom-types) +1. [Testing](#testing) +1. [FAQ](#faq) + +## Examples + +Examples can be found in the `examples/` directory. Run them +to see basic behavior: + +```bash +go get github.com/AlecAivazis/survey/v2 + +cd $GOPATH/src/github.com/AlecAivazis/survey + +go run examples/simple.go +go run examples/validation.go +``` + +## Running the Prompts + +There are two primary ways to execute prompts and start collecting information from your users: `Ask` and +`AskOne`. The primary difference is whether you are interested in collecting a single piece of information +or if you have a list of questions to ask whose answers should be collected in a single struct. +For most basic usecases, `Ask` should be enough. However, for surveys with complicated branching logic, +we recommend that you break out your questions into multiple calls to both of these functions to fit your needs. + +### Configuring the Prompts + +Most prompts take fine-grained configuration through fields on the structs you instantiate. It is also +possible to change survey's default behaviors by passing `AskOpts` to either `Ask` or `AskOne`. Examples +in this document will do both interchangeably: + +```golang +prompt := &Select{ + Message: "Choose a color:", + Options: []string{"red", "blue", "green"}, + // can pass a validator directly + Validate: survey.Required, +} + +// or define a default for the single call to `AskOne` +// the answer will get written to the color variable +survey.AskOne(prompt, &color, survey.WithValidator(survey.Required)) + +// or define a default for every entry in a list of questions +// the answer will get copied into the matching field of the struct as shown above +survey.Ask(questions, &answers, survey.WithValidator(survey.Required)) +``` + +## Prompts + +### Input + + + +```golang +name := "" +prompt := &survey.Input{ + Message: "ping", +} +survey.AskOne(prompt, &name) +``` + +#### Suggestion Options + + + +```golang +file := "" +prompt := &survey.Input{ + Message: "inform a file to save:", + Suggest: func (toComplete string) []string { + files, _ := filepath.Glob(toComplete + "*") + return files + }, +} +} +survey.AskOne(prompt, &file) +``` + +### Multiline + + + +```golang +text := "" +prompt := &survey.Multiline{ + Message: "ping", +} +survey.AskOne(prompt, &text) +``` + +### Password + + + +```golang +password := "" +prompt := &survey.Password{ + Message: "Please type your password", +} +survey.AskOne(prompt, &password) +``` + +### Confirm + + + +```golang +name := false +prompt := &survey.Confirm{ + Message: "Do you like pie?", +} +survey.AskOne(prompt, &name) +``` + +### Select + + + +```golang +color := "" +prompt := &survey.Select{ + Message: "Choose a color:", + Options: []string{"red", "blue", "green"}, +} +survey.AskOne(prompt, &color) +``` + +Fields and values that come from a `Select` prompt can be one of two different things. If you pass an `int` +the field will have the value of the selected index. If you instead pass a string, the string value selected +will be written to the field. + +The user can also press `esc` to toggle the ability cycle through the options with the j and k keys to do down and up respectively. + +By default, the select prompt is limited to showing 7 options at a time +and will paginate lists of options longer than that. This can be changed a number of ways: + +```golang +// as a field on a single select +prompt := &survey.MultiSelect{..., PageSize: 10} + +// or as an option to Ask or AskOne +survey.AskOne(prompt, &days, survey.WithPageSize(10)) +``` + +### MultiSelect + +![Example](img/multi-select-all-none.gif) + +```golang +days := []string{} +prompt := &survey.MultiSelect{ + Message: "What days do you prefer:", + Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, +} +survey.AskOne(prompt, &days) +``` + +Fields and values that come from a `MultiSelect` prompt can be one of two different things. If you pass an `int` +the field will have a slice of the selected indices. If you instead pass a string, a slice of the string values +selected will be written to the field. + +The user can also press `esc` to toggle the ability cycle through the options with the j and k keys to do down and up respectively. + +By default, the MultiSelect prompt is limited to showing 7 options at a time +and will paginate lists of options longer than that. This can be changed a number of ways: + +```golang +// as a field on a single select +prompt := &survey.MultiSelect{..., PageSize: 10} + +// or as an option to Ask or AskOne +survey.AskOne(prompt, &days, survey.WithPageSize(10)) +``` + +### Editor + +Launches the user's preferred editor (defined by the \$VISUAL or \$EDITOR environment variables) on a +temporary file. Once the user exits their editor, the contents of the temporary file are read in as +the result. If neither of those are present, notepad (on Windows) or vim (Linux or Mac) is used. + +You can also specify a [pattern](https://golang.org/pkg/io/ioutil/#TempFile) for the name of the temporary file. This +can be useful for ensuring syntax highlighting matches your usecase. + +```golang +prompt := &survey.Editor{ + Message: "Shell code snippet", + FileName: "*.sh", +} + +survey.AskOne(prompt, &content) +``` + +## Filtering Options + +By default, the user can filter for options in Select and MultiSelects by typing while the prompt +is active. This will filter out all options that don't contain the typed string anywhere in their name, ignoring case. + +A custom filter function can also be provided to change this behavior: + +```golang +func myFilter(filterValue string, optValue string, optIndex int) bool { + // only include the option if it includes the filter and has length greater than 5 + return strings.Contains(optValue, filterValue) && len(optValue) >= 5 +} + +// configure it for a specific prompt +&Select{ + Message: "Choose a color:", + Options: []string{"red", "blue", "green"}, + Filter: myFilter, +} + +// or define a default for all of the questions +survey.AskOne(prompt, &color, survey.WithFilter(myFilter)) +``` + +## Keeping the filter active + +By default the filter will disappear if the user selects one of the filtered elements. Once the user selects one element the filter setting is gone. + +However the user can prevent this from happening and keep the filter active for multiple selections in a e.g. MultiSelect: + +```golang +// configure it for a specific prompt +&Select{ + Message: "Choose a color:", + Options: []string{"light-green", "green", "dark-green", "red"}, + KeepFilter: true, +} + +// or define a default for all of the questions +survey.AskOne(prompt, &color, survey.WithKeepFilter(true)) +``` + +## Validation + +Validating individual responses for a particular question can be done by defining a +`Validate` field on the `survey.Question` to be validated. This function takes an +`interface{}` type and returns an error to show to the user, prompting them for another +response. Like usual, validators can be provided directly to the prompt or with `survey.WithValidator`: + +```golang +q := &survey.Question{ + Prompt: &survey.Input{Message: "Hello world validation"}, + Validate: func (val interface{}) error { + // since we are validating an Input, the assertion will always succeed + if str, ok := val.(string) ; !ok || len(str) > 10 { + return errors.New("This response cannot be longer than 10 characters.") + } + return nil + }, +} + +color := "" +prompt := &survey.Input{ Message: "Whats your name?" } + +// you can pass multiple validators here and survey will make sure each one passes +survey.AskOne(prompt, &color, survey.WithValidator(survey.Required)) +``` + +### Built-in Validators + +`survey` comes prepackaged with a few validators to fit common situations. Currently these +validators include: + +| name | valid types | description | notes | +| ------------ | -------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------- | +| Required | any | Rejects zero values of the response type | Boolean values pass straight through since the zero value (false) is a valid response | +| MinLength(n) | string | Enforces that a response is at least the given length | | +| MaxLength(n) | string | Enforces that a response is no longer than the given length | | +| MaxItems(n) | []OptionAnswer | Enforces that a response has no more selections of the indicated | | +| MinItems(n) | []OptionAnswer | Enforces that a response has no less selections of the indicated | | + +## Help Text + +All of the prompts have a `Help` field which can be defined to provide more information to your users: + + + +```golang +&survey.Input{ + Message: "What is your phone number:", + Help: "Phone number should include the area code", +} +``` + +### Changing the input rune + +In some situations, `?` is a perfectly valid response. To handle this, you can change the rune that survey +looks for with `WithHelpInput`: + +```golang +import ( + "github.com/AlecAivazis/survey/v2" +) + +number := "" +prompt := &survey.Input{ + Message: "If you have this need, please give me a reasonable message.", + Help: "I couldn't come up with one.", +} + +survey.AskOne(prompt, &number, survey.WithHelpInput('^')) +``` + +## Changing the Icons + +Changing the icons and their color/format can be done by passing the `WithIcons` option. The format +follows the patterns outlined [here](https://github.com/mgutz/ansi#style-format). For example: + +```golang +import ( + "github.com/AlecAivazis/survey/v2" +) + +number := "" +prompt := &survey.Input{ + Message: "If you have this need, please give me a reasonable message.", + Help: "I couldn't come up with one.", +} + +survey.AskOne(prompt, &number, survey.WithIcons(func(icons *survey.IconSet) { + // you can set any icons + icons.Question.Text = "⁇" + // for more information on formatting the icons, see here: https://github.com/mgutz/ansi#style-format + icons.Question.Format = "yellow+hb" +})) +``` + +The icons and their default text and format are summarized below: + +| name | text | format | description | +| -------------- | ---- | ---------- | ------------------------------------------------------------- | +| Error | X | red | Before an error | +| Help | i | cyan | Before help text | +| Question | ? | green+hb | Before the message of a prompt | +| SelectFocus | > | green | Marks the current focus in `Select` and `MultiSelect` prompts | +| UnmarkedOption | [ ] | default+hb | Marks an unselected option in a `MultiSelect` prompt | +| MarkedOption | [x] | cyan+b | Marks a chosen selection in a `MultiSelect` prompt | + +## Custom Types + +survey will assign prompt answers to your custom types if they implement this interface: + +```golang +type Settable interface { + WriteAnswer(field string, value interface{}) error +} +``` + +Here is an example how to use them: + +```golang +type MyValue struct { + value string +} +func (my *MyValue) WriteAnswer(name string, value interface{}) error { + my.value = value.(string) +} + +myval := MyValue{} +survey.AskOne( + &survey.Input{ + Message: "Enter something:", + }, + &myval +) +``` + +## Testing + +You can test your program's interactive prompts using [go-expect](https://github.com/Netflix/go-expect). The library +can be used to expect a match on stdout and respond on stdin. Since `os.Stdout` in a `go test` process is not a TTY, +if you are manipulating the cursor or using `survey`, you will need a way to interpret terminal / ANSI escape sequences +for things like `CursorLocation`. `vt10x.NewVT10XConsole` will create a `go-expect` console that also multiplexes +stdio to an in-memory [virtual terminal](https://github.com/hinshun/vt10x). + +For some examples, you can see any of the tests in this repo. + +## FAQ + +### What kinds of IO are supported by `survey`? + +survey aims to support most terminal emulators; it expects support for ANSI escape sequences. +This means that reading from piped stdin or writing to piped stdout is **not supported**, +and likely to break your application in these situations. See [#337](https://github.com/AlecAivazis/survey/pull/337#issue-581351617) + +### Why isn't Ctrl-C working? + +Ordinarily, when you type Ctrl-C, the terminal recognizes this as the QUIT button and delivers a SIGINT signal to the process, which terminates it. +However, Survey temporarily configures the terminal to deliver control codes as ordinary input bytes. +When Survey reads a ^C byte (ASCII \x03, "end of text"), it interrupts the current survey and returns a +`github.com/AlecAivazis/survey/v2/terminal.InterruptErr` from `Ask` or `AskOne`. +If you want to stop the process, handle the returned error in your code: + +```go +err := survey.AskOne(prompt, &myVar) +if err != nil { + if err == terminal.InterruptErr { + log.Fatal("interrupted") + } + ... +} +``` diff --git a/vendor/github.com/AlecAivazis/survey/v2/_tasks.hcl b/vendor/github.com/AlecAivazis/survey/v2/_tasks.hcl new file mode 100644 index 0000000000..0eefcb5486 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/_tasks.hcl @@ -0,0 +1,19 @@ +task "install-deps" { + description = "Install all of package dependencies" + pipeline = [ + "go get -v {{.files}}", + ] +} + +task "tests" { + description = "Run the test suite" + command = "go test -v {{.files}}" + environment = { + GOFLAGS = "-mod=vendor" + } +} + +variables = { + files = "$(go list -v ./... | grep -iEv \"tests|examples\")" +} + diff --git a/vendor/github.com/AlecAivazis/survey/v2/confirm.go b/vendor/github.com/AlecAivazis/survey/v2/confirm.go new file mode 100644 index 0000000000..8e5d7efaaf --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/confirm.go @@ -0,0 +1,153 @@ +package survey + +import ( + "fmt" + "regexp" +) + +// Confirm is a regular text input that accept yes/no answers. Response type is a bool. +type Confirm struct { + Renderer + Message string + Default bool + Help string +} + +// data available to the templates when processing +type ConfirmTemplateData struct { + Confirm + Answer string + ShowHelp bool + Config *PromptConfig +} + +// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format +var ConfirmQuestionTemplate = ` +{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}} +{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}} +{{- color "default+hb"}}{{ .Message }} {{color "reset"}} +{{- if .Answer}} + {{- color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}} +{{- else }} + {{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ .Config.HelpInput }} for help]{{color "reset"}} {{end}} + {{- color "white"}}{{if .Default}}(Y/n) {{else}}(y/N) {{end}}{{color "reset"}} +{{- end}}` + +// the regex for answers +var ( + yesRx = regexp.MustCompile("^(?i:y(?:es)?)$") + noRx = regexp.MustCompile("^(?i:n(?:o)?)$") +) + +func yesNo(t bool) string { + if t { + return "Yes" + } + return "No" +} + +func (c *Confirm) getBool(showHelp bool, config *PromptConfig) (bool, error) { + cursor := c.NewCursor() + rr := c.NewRuneReader() + rr.SetTermMode() + defer rr.RestoreTermMode() + + // start waiting for input + for { + line, err := rr.ReadLine(0) + if err != nil { + return false, err + } + // move back up a line to compensate for the \n echoed from terminal + cursor.PreviousLine(1) + val := string(line) + + // get the answer that matches the + var answer bool + switch { + case yesRx.Match([]byte(val)): + answer = true + case noRx.Match([]byte(val)): + answer = false + case val == "": + answer = c.Default + case val == config.HelpInput && c.Help != "": + err := c.Render( + ConfirmQuestionTemplate, + ConfirmTemplateData{ + Confirm: *c, + ShowHelp: true, + Config: config, + }, + ) + if err != nil { + // use the default value and bubble up + return c.Default, err + } + showHelp = true + continue + default: + // we didnt get a valid answer, so print error and prompt again + if err := c.Error(config, fmt.Errorf("%q is not a valid answer, please try again.", val)); err != nil { + return c.Default, err + } + err := c.Render( + ConfirmQuestionTemplate, + ConfirmTemplateData{ + Confirm: *c, + ShowHelp: showHelp, + Config: config, + }, + ) + if err != nil { + // use the default value and bubble up + return c.Default, err + } + continue + } + return answer, nil + } + // should not get here + return c.Default, nil +} + +/* +Prompt prompts the user with a simple text field and expects a reply followed +by a carriage return. + + likesPie := false + prompt := &survey.Confirm{ Message: "What is your name?" } + survey.AskOne(prompt, &likesPie) +*/ +func (c *Confirm) Prompt(config *PromptConfig) (interface{}, error) { + // render the question template + err := c.Render( + ConfirmQuestionTemplate, + ConfirmTemplateData{ + Confirm: *c, + Config: config, + }, + ) + if err != nil { + return "", err + } + + // get input and return + return c.getBool(false, config) +} + +// Cleanup overwrite the line with the finalized formatted version +func (c *Confirm) Cleanup(config *PromptConfig, val interface{}) error { + // if the value was previously true + ans := yesNo(val.(bool)) + + // render the template + return c.Render( + ConfirmQuestionTemplate, + ConfirmTemplateData{ + Confirm: *c, + Answer: ans, + Config: config, + }, + ) +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/core/template.go b/vendor/github.com/AlecAivazis/survey/v2/core/template.go new file mode 100644 index 0000000000..6b3d20cdc7 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/core/template.go @@ -0,0 +1,91 @@ +package core + +import ( + "bytes" + "sync" + "text/template" + + "github.com/mgutz/ansi" +) + +// DisableColor can be used to make testing reliable +var DisableColor = false + +var TemplateFuncsWithColor = map[string]interface{}{ + // Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format + "color": ansi.ColorCode, +} + +var TemplateFuncsNoColor = map[string]interface{}{ + // Templates without Color formatting. For layout/ testing. + "color": func(color string) string { + return "" + }, +} + +//RunTemplate returns two formatted strings given a template and +//the data it requires. The first string returned is generated for +//user-facing output and may or may not contain ANSI escape codes +//for colored output. The second string does not contain escape codes +//and can be used by the renderer for layout purposes. +func RunTemplate(tmpl string, data interface{}) (string, string, error) { + tPair, err := GetTemplatePair(tmpl) + if err != nil { + return "", "", err + } + userBuf := bytes.NewBufferString("") + err = tPair[0].Execute(userBuf, data) + if err != nil { + return "", "", err + } + layoutBuf := bytes.NewBufferString("") + err = tPair[1].Execute(layoutBuf, data) + if err != nil { + return userBuf.String(), "", err + } + return userBuf.String(), layoutBuf.String(), err +} + +var ( + memoizedGetTemplate = map[string][2]*template.Template{} + + memoMutex = &sync.RWMutex{} +) + +//GetTemplatePair returns a pair of compiled templates where the +//first template is generated for user-facing output and the +//second is generated for use by the renderer. The second +//template does not contain any color escape codes, whereas +//the first template may or may not depending on DisableColor. +func GetTemplatePair(tmpl string) ([2]*template.Template, error) { + memoMutex.RLock() + if t, ok := memoizedGetTemplate[tmpl]; ok { + memoMutex.RUnlock() + return t, nil + } + memoMutex.RUnlock() + + templatePair := [2]*template.Template{nil, nil} + + templateNoColor, err := template.New("prompt").Funcs(TemplateFuncsNoColor).Parse(tmpl) + if err != nil { + return [2]*template.Template{}, err + } + + templatePair[1] = templateNoColor + + if DisableColor { + templatePair[0] = templatePair[1] + } else { + templateWithColor, err := template.New("prompt").Funcs(TemplateFuncsWithColor).Parse(tmpl) + templatePair[0] = templateWithColor + if err != nil { + return [2]*template.Template{}, err + } + } + + memoMutex.Lock() + memoizedGetTemplate[tmpl] = templatePair + memoMutex.Unlock() + return templatePair, nil +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/core/write.go b/vendor/github.com/AlecAivazis/survey/v2/core/write.go new file mode 100644 index 0000000000..dfe9b49995 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/core/write.go @@ -0,0 +1,374 @@ +package core + +import ( + "errors" + "fmt" + "reflect" + "strconv" + "strings" + "time" +) + +// the tag used to denote the name of the question +const tagName = "survey" + +// Settable allow for configuration when assigning answers +type Settable interface { + WriteAnswer(field string, value interface{}) error +} + +// OptionAnswer is the return type of Selects/MultiSelects that lets the appropriate information +// get copied to the user's struct +type OptionAnswer struct { + Value string + Index int +} + +type reflectField struct { + value reflect.Value + fieldType reflect.StructField +} + +func OptionAnswerList(incoming []string) []OptionAnswer { + list := []OptionAnswer{} + for i, opt := range incoming { + list = append(list, OptionAnswer{Value: opt, Index: i}) + } + return list +} + +func WriteAnswer(t interface{}, name string, v interface{}) (err error) { + // if the field is a custom type + if s, ok := t.(Settable); ok { + // use the interface method + return s.WriteAnswer(name, v) + } + + // the target to write to + target := reflect.ValueOf(t) + // the value to write from + value := reflect.ValueOf(v) + + // make sure we are writing to a pointer + if target.Kind() != reflect.Ptr { + return errors.New("you must pass a pointer as the target of a Write operation") + } + // the object "inside" of the target pointer + elem := target.Elem() + + // handle the special types + switch elem.Kind() { + // if we are writing to a struct + case reflect.Struct: + // if we are writing to an option answer than we want to treat + // it like a single thing and not a place to deposit answers + if elem.Type().Name() == "OptionAnswer" { + // copy the value over to the normal struct + return copy(elem, value) + } + + // get the name of the field that matches the string we were given + field, _, err := findField(elem, name) + // if something went wrong + if err != nil { + // bubble up + return err + } + // handle references to the Settable interface aswell + if s, ok := field.Interface().(Settable); ok { + // use the interface method + return s.WriteAnswer(name, v) + } + if field.CanAddr() { + if s, ok := field.Addr().Interface().(Settable); ok { + // use the interface method + return s.WriteAnswer(name, v) + } + } + + // copy the value over to the normal struct + return copy(field, value) + case reflect.Map: + mapType := reflect.TypeOf(t).Elem() + if mapType.Key().Kind() != reflect.String { + return errors.New("answer maps key must be of type string") + } + + // copy only string value/index value to map if, + // map is not of type interface and is 'OptionAnswer' + if value.Type().Name() == "OptionAnswer" { + if kval := mapType.Elem().Kind(); kval == reflect.String { + mt := *t.(*map[string]string) + mt[name] = value.FieldByName("Value").String() + return nil + } else if kval == reflect.Int { + mt := *t.(*map[string]int) + mt[name] = int(value.FieldByName("Index").Int()) + return nil + } + } + + if mapType.Elem().Kind() != reflect.Interface { + return errors.New("answer maps must be of type map[string]interface") + } + mt := *t.(*map[string]interface{}) + mt[name] = value.Interface() + return nil + } + // otherwise just copy the value to the target + return copy(elem, value) +} + +type errFieldNotMatch struct { + questionName string +} + +func (err errFieldNotMatch) Error() string { + return fmt.Sprintf("could not find field matching %v", err.questionName) +} + +func (err errFieldNotMatch) Is(target error) bool { // implements the dynamic errors.Is interface. + if target != nil { + if name, ok := IsFieldNotMatch(target); ok { + // if have a filled questionName then perform "deeper" comparison. + return name == "" || err.questionName == "" || name == err.questionName + } + } + + return false +} + +// IsFieldNotMatch reports whether an "err" is caused by a non matching field. +// It returns the Question.Name that couldn't be matched with a destination field. +// +// Usage: +// err := survey.Ask(qs, &v); +// if err != nil { +// if name, ok := core.IsFieldNotMatch(err); ok { +// [...name is the not matched question name] +// } +// } +func IsFieldNotMatch(err error) (string, bool) { + if err != nil { + if v, ok := err.(errFieldNotMatch); ok { + return v.questionName, true + } + } + + return "", false +} + +// BUG(AlecAivazis): the current implementation might cause weird conflicts if there are +// two fields with same name that only differ by casing. +func findField(s reflect.Value, name string) (reflect.Value, reflect.StructField, error) { + + fields := flattenFields(s) + + // first look for matching tags so we can overwrite matching field names + for _, f := range fields { + // the value of the survey tag + tag := f.fieldType.Tag.Get(tagName) + // if the tag matches the name we are looking for + if tag != "" && tag == name { + // then we found our index + return f.value, f.fieldType, nil + } + } + + // then look for matching names + for _, f := range fields { + // if the name of the field matches what we're looking for + if strings.ToLower(f.fieldType.Name) == strings.ToLower(name) { + return f.value, f.fieldType, nil + } + } + + // we didn't find the field + return reflect.Value{}, reflect.StructField{}, errFieldNotMatch{name} +} + +func flattenFields(s reflect.Value) []reflectField { + sType := s.Type() + numField := sType.NumField() + fields := make([]reflectField, 0, numField) + for i := 0; i < numField; i++ { + fieldType := sType.Field(i) + field := s.Field(i) + + if field.Kind() == reflect.Struct && fieldType.Anonymous { + // field is a promoted structure + for _, f := range flattenFields(field) { + fields = append(fields, f) + } + continue + } + fields = append(fields, reflectField{field, fieldType}) + } + return fields +} + +// isList returns true if the element is something we can Len() +func isList(v reflect.Value) bool { + switch v.Type().Kind() { + case reflect.Array, reflect.Slice: + return true + default: + return false + } +} + +// Write takes a value and copies it to the target +func copy(t reflect.Value, v reflect.Value) (err error) { + // if something ends up panicing we need to catch it in a deferred func + defer func() { + if r := recover(); r != nil { + // if we paniced with an error + if _, ok := r.(error); ok { + // cast the result to an error object + err = r.(error) + } else if _, ok := r.(string); ok { + // otherwise we could have paniced with a string so wrap it in an error + err = errors.New(r.(string)) + } + } + }() + + // if we are copying from a string result to something else + if v.Kind() == reflect.String && v.Type() != t.Type() { + var castVal interface{} + var casterr error + vString := v.Interface().(string) + + switch t.Kind() { + case reflect.Bool: + castVal, casterr = strconv.ParseBool(vString) + case reflect.Int: + castVal, casterr = strconv.Atoi(vString) + case reflect.Int8: + var val64 int64 + val64, casterr = strconv.ParseInt(vString, 10, 8) + if casterr == nil { + castVal = int8(val64) + } + case reflect.Int16: + var val64 int64 + val64, casterr = strconv.ParseInt(vString, 10, 16) + if casterr == nil { + castVal = int16(val64) + } + case reflect.Int32: + var val64 int64 + val64, casterr = strconv.ParseInt(vString, 10, 32) + if casterr == nil { + castVal = int32(val64) + } + case reflect.Int64: + if t.Type() == reflect.TypeOf(time.Duration(0)) { + castVal, casterr = time.ParseDuration(vString) + } else { + castVal, casterr = strconv.ParseInt(vString, 10, 64) + } + case reflect.Uint: + var val64 uint64 + val64, casterr = strconv.ParseUint(vString, 10, 8) + if casterr == nil { + castVal = uint(val64) + } + case reflect.Uint8: + var val64 uint64 + val64, casterr = strconv.ParseUint(vString, 10, 8) + if casterr == nil { + castVal = uint8(val64) + } + case reflect.Uint16: + var val64 uint64 + val64, casterr = strconv.ParseUint(vString, 10, 16) + if casterr == nil { + castVal = uint16(val64) + } + case reflect.Uint32: + var val64 uint64 + val64, casterr = strconv.ParseUint(vString, 10, 32) + if casterr == nil { + castVal = uint32(val64) + } + case reflect.Uint64: + castVal, casterr = strconv.ParseUint(vString, 10, 64) + case reflect.Float32: + var val64 float64 + val64, casterr = strconv.ParseFloat(vString, 32) + if casterr == nil { + castVal = float32(val64) + } + case reflect.Float64: + castVal, casterr = strconv.ParseFloat(vString, 64) + default: + return fmt.Errorf("Unable to convert from string to type %s", t.Kind()) + } + + if casterr != nil { + return casterr + } + + t.Set(reflect.ValueOf(castVal)) + return + } + + // if we are copying from an OptionAnswer to something + if v.Type().Name() == "OptionAnswer" { + // copying an option answer to a string + if t.Kind() == reflect.String { + // copies the Value field of the struct + t.Set(reflect.ValueOf(v.FieldByName("Value").Interface())) + return + } + + // copying an option answer to an int + if t.Kind() == reflect.Int { + // copies the Index field of the struct + t.Set(reflect.ValueOf(v.FieldByName("Index").Interface())) + return + } + + // copying an OptionAnswer to an OptionAnswer + if t.Type().Name() == "OptionAnswer" { + t.Set(v) + return + } + + // we're copying an option answer to an incorrect type + return fmt.Errorf("Unable to convert from OptionAnswer to type %s", t.Kind()) + } + + // if we are copying from one slice or array to another + if isList(v) && isList(t) { + // loop over every item in the desired value + for i := 0; i < v.Len(); i++ { + // write to the target given its kind + switch t.Kind() { + // if its a slice + case reflect.Slice: + // an object of the correct type + obj := reflect.Indirect(reflect.New(t.Type().Elem())) + + // write the appropriate value to the obj and catch any errors + if err := copy(obj, v.Index(i)); err != nil { + return err + } + + // just append the value to the end + t.Set(reflect.Append(t, obj)) + // otherwise it could be an array + case reflect.Array: + // set the index to the appropriate value + copy(t.Slice(i, i+1).Index(0), v.Index(i)) + } + } + } else { + // set the value to the target + t.Set(v) + } + + // we're done + return +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/editor.go b/vendor/github.com/AlecAivazis/survey/v2/editor.go new file mode 100644 index 0000000000..ec6a141782 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/editor.go @@ -0,0 +1,222 @@ +package survey + +import ( + "bytes" + "io/ioutil" + "os" + "os/exec" + "runtime" + + "github.com/AlecAivazis/survey/v2/terminal" + shellquote "github.com/kballard/go-shellquote" +) + +/* +Editor launches an instance of the users preferred editor on a temporary file. +The editor to use is determined by reading the $VISUAL or $EDITOR environment +variables. If neither of those are present, notepad (on Windows) or vim +(others) is used. +The launch of the editor is triggered by the enter key. Since the response may +be long, it will not be echoed as Input does, instead, it print . +Response type is a string. + + message := "" + prompt := &survey.Editor{ Message: "What is your commit message?" } + survey.AskOne(prompt, &message) +*/ +type Editor struct { + Renderer + Message string + Default string + Help string + Editor string + HideDefault bool + AppendDefault bool + FileName string +} + +// data available to the templates when processing +type EditorTemplateData struct { + Editor + Answer string + ShowAnswer bool + ShowHelp bool + Config *PromptConfig +} + +// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format +var EditorQuestionTemplate = ` +{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}} +{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}} +{{- color "default+hb"}}{{ .Message }} {{color "reset"}} +{{- if .ShowAnswer}} + {{- color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}} +{{- else }} + {{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ .Config.HelpInput }} for help]{{color "reset"}} {{end}} + {{- if and .Default (not .HideDefault)}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}} + {{- color "cyan"}}[Enter to launch editor] {{color "reset"}} +{{- end}}` + +var ( + bom = []byte{0xef, 0xbb, 0xbf} + editor = "vim" +) + +func init() { + if runtime.GOOS == "windows" { + editor = "notepad" + } + if v := os.Getenv("VISUAL"); v != "" { + editor = v + } else if e := os.Getenv("EDITOR"); e != "" { + editor = e + } +} + +func (e *Editor) PromptAgain(config *PromptConfig, invalid interface{}, err error) (interface{}, error) { + initialValue := invalid.(string) + return e.prompt(initialValue, config) +} + +func (e *Editor) Prompt(config *PromptConfig) (interface{}, error) { + initialValue := "" + if e.Default != "" && e.AppendDefault { + initialValue = e.Default + } + return e.prompt(initialValue, config) +} + +func (e *Editor) prompt(initialValue string, config *PromptConfig) (interface{}, error) { + // render the template + err := e.Render( + EditorQuestionTemplate, + EditorTemplateData{ + Editor: *e, + Config: config, + }, + ) + if err != nil { + return "", err + } + + // start reading runes from the standard in + rr := e.NewRuneReader() + rr.SetTermMode() + defer rr.RestoreTermMode() + + cursor := e.NewCursor() + cursor.Hide() + defer cursor.Show() + + for { + r, _, err := rr.ReadRune() + if err != nil { + return "", err + } + if r == '\r' || r == '\n' { + break + } + if r == terminal.KeyInterrupt { + return "", terminal.InterruptErr + } + if r == terminal.KeyEndTransmission { + break + } + if string(r) == config.HelpInput && e.Help != "" { + err = e.Render( + EditorQuestionTemplate, + EditorTemplateData{ + Editor: *e, + ShowHelp: true, + Config: config, + }, + ) + if err != nil { + return "", err + } + } + continue + } + + // prepare the temp file + pattern := e.FileName + if pattern == "" { + pattern = "survey*.txt" + } + f, err := ioutil.TempFile("", pattern) + if err != nil { + return "", err + } + defer os.Remove(f.Name()) + + // write utf8 BOM header + // The reason why we do this is because notepad.exe on Windows determines the + // encoding of an "empty" text file by the locale, for example, GBK in China, + // while golang string only handles utf8 well. However, a text file with utf8 + // BOM header is not considered "empty" on Windows, and the encoding will then + // be determined utf8 by notepad.exe, instead of GBK or other encodings. + if _, err := f.Write(bom); err != nil { + return "", err + } + + // write initial value + if _, err := f.WriteString(initialValue); err != nil { + return "", err + } + + // close the fd to prevent the editor unable to save file + if err := f.Close(); err != nil { + return "", err + } + + // check is input editor exist + if e.Editor != "" { + editor = e.Editor + } + + stdio := e.Stdio() + + args, err := shellquote.Split(editor) + if err != nil { + return "", err + } + args = append(args, f.Name()) + + // open the editor + cmd := exec.Command(args[0], args[1:]...) + cmd.Stdin = stdio.In + cmd.Stdout = stdio.Out + cmd.Stderr = stdio.Err + cursor.Show() + if err := cmd.Run(); err != nil { + return "", err + } + + // raw is a BOM-unstripped UTF8 byte slice + raw, err := ioutil.ReadFile(f.Name()) + if err != nil { + return "", err + } + + // strip BOM header + text := string(bytes.TrimPrefix(raw, bom)) + + // check length, return default value on empty + if len(text) == 0 && !e.AppendDefault { + return e.Default, nil + } + + return text, nil +} + +func (e *Editor) Cleanup(config *PromptConfig, val interface{}) error { + return e.Render( + EditorQuestionTemplate, + EditorTemplateData{ + Editor: *e, + Answer: "", + ShowAnswer: true, + Config: config, + }, + ) +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/filter.go b/vendor/github.com/AlecAivazis/survey/v2/filter.go new file mode 100644 index 0000000000..56f702678f --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/filter.go @@ -0,0 +1 @@ +package survey diff --git a/vendor/github.com/AlecAivazis/survey/v2/go.mod b/vendor/github.com/AlecAivazis/survey/v2/go.mod new file mode 100644 index 0000000000..de87fb9f55 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/go.mod @@ -0,0 +1,19 @@ +module github.com/AlecAivazis/survey/v2 + +require ( + github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 + github.com/kr/pty v1.1.4 + github.com/mattn/go-colorable v0.1.2 // indirect + github.com/mattn/go-isatty v0.0.8 + github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.2.1 + golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 // indirect + golang.org/x/term v0.0.0-20210503060354-a79de5458b56 + golang.org/x/text v0.3.3 +) + +go 1.13 diff --git a/vendor/github.com/AlecAivazis/survey/v2/go.sum b/vendor/github.com/AlecAivazis/survey/v2/go.sum new file mode 100644 index 0000000000..93dd6e647b --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/go.sum @@ -0,0 +1,35 @@ +github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= +github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= +github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ= +github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.2.1 h1:52QO5WkIUcHGIR7EnGagH88x1bUzqGXTC5/1bDTUQ7U= +github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w= +golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/vendor/github.com/AlecAivazis/survey/v2/input.go b/vendor/github.com/AlecAivazis/survey/v2/input.go new file mode 100644 index 0000000000..a1abab1456 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/input.go @@ -0,0 +1,225 @@ +package survey + +import ( + "errors" + + "github.com/AlecAivazis/survey/v2/core" + "github.com/AlecAivazis/survey/v2/terminal" +) + +/* +Input is a regular text input that prints each character the user types on the screen +and accepts the input with the enter key. Response type is a string. + + name := "" + prompt := &survey.Input{ Message: "What is your name?" } + survey.AskOne(prompt, &name) +*/ +type Input struct { + Renderer + Message string + Default string + Help string + Suggest func(toComplete string) []string + answer string + typedAnswer string + options []core.OptionAnswer + selectedIndex int + showingHelp bool +} + +// data available to the templates when processing +type InputTemplateData struct { + Input + ShowAnswer bool + ShowHelp bool + Answer string + PageEntries []core.OptionAnswer + SelectedIndex int + Config *PromptConfig +} + +// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format +var InputQuestionTemplate = ` +{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}} +{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}} +{{- color "default+hb"}}{{ .Message }} {{color "reset"}} +{{- if .ShowAnswer}} + {{- color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}} +{{- else if .PageEntries -}} + {{- .Answer}} [Use arrows to move, enter to select, type to continue] + {{- "\n"}} + {{- range $ix, $choice := .PageEntries}} + {{- if eq $ix $.SelectedIndex }}{{color $.Config.Icons.SelectFocus.Format }}{{ $.Config.Icons.SelectFocus.Text }} {{else}}{{color "default"}} {{end}} + {{- $choice.Value}} + {{- color "reset"}}{{"\n"}} + {{- end}} +{{- else }} + {{- if or (and .Help (not .ShowHelp)) .Suggest }}{{color "cyan"}}[ + {{- if and .Help (not .ShowHelp)}}{{ print .Config.HelpInput }} for help {{- if and .Suggest}}, {{end}}{{end -}} + {{- if and .Suggest }}{{color "cyan"}}{{ print .Config.SuggestInput }} for suggestions{{end -}} + ]{{color "reset"}} {{end}} + {{- if .Default}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}} +{{- end}}` + +func (i *Input) onRune(config *PromptConfig) terminal.OnRuneFn { + return terminal.OnRuneFn(func(key rune, line []rune) ([]rune, bool, error) { + if i.options != nil && (key == terminal.KeyEnter || key == '\n') { + return []rune(i.answer), true, nil + } else if i.options != nil && key == terminal.KeyEscape { + i.answer = i.typedAnswer + i.options = nil + } else if key == terminal.KeyArrowUp && len(i.options) > 0 { + if i.selectedIndex == 0 { + i.selectedIndex = len(i.options) - 1 + } else { + i.selectedIndex-- + } + i.answer = i.options[i.selectedIndex].Value + } else if (key == terminal.KeyArrowDown || key == terminal.KeyTab) && len(i.options) > 0 { + if i.selectedIndex == len(i.options)-1 { + i.selectedIndex = 0 + } else { + i.selectedIndex++ + } + i.answer = i.options[i.selectedIndex].Value + } else if key == terminal.KeyTab && i.Suggest != nil { + i.answer = string(line) + i.typedAnswer = i.answer + options := i.Suggest(i.answer) + i.selectedIndex = 0 + if len(options) == 0 { + return line, false, nil + } + + i.answer = options[0] + if len(options) == 1 { + i.typedAnswer = i.answer + i.options = nil + } else { + i.options = core.OptionAnswerList(options) + } + } else { + if i.options == nil { + return line, false, nil + } + + if key >= terminal.KeySpace { + i.answer += string(key) + } + i.typedAnswer = i.answer + + i.options = nil + } + + pageSize := config.PageSize + opts, idx := paginate(pageSize, i.options, i.selectedIndex) + err := i.Render( + InputQuestionTemplate, + InputTemplateData{ + Input: *i, + Answer: i.answer, + ShowHelp: i.showingHelp, + SelectedIndex: idx, + PageEntries: opts, + Config: config, + }, + ) + + if err == nil { + err = readLineAgain + } + + return []rune(i.typedAnswer), true, err + }) +} + +var readLineAgain = errors.New("read line again") + +func (i *Input) Prompt(config *PromptConfig) (interface{}, error) { + // render the template + err := i.Render( + InputQuestionTemplate, + InputTemplateData{ + Input: *i, + Config: config, + ShowHelp: i.showingHelp, + }, + ) + if err != nil { + return "", err + } + + // start reading runes from the standard in + rr := i.NewRuneReader() + rr.SetTermMode() + defer rr.RestoreTermMode() + + cursor := i.NewCursor() + if !config.ShowCursor { + cursor.Hide() // hide the cursor + defer cursor.Show() // show the cursor when we're done + } + + var line []rune + + for { + if i.options != nil { + line = []rune{} + } + + line, err = rr.ReadLineWithDefault(0, line, i.onRune(config)) + if err == readLineAgain { + continue + } + + if err != nil { + return "", err + } + + break + } + + i.answer = string(line) + // readline print an empty line, go up before we render the follow up + cursor.Up(1) + + // if we ran into the help string + if i.answer == config.HelpInput && i.Help != "" { + // show the help and prompt again + i.showingHelp = true + return i.Prompt(config) + } + + // if the line is empty + if len(i.answer) == 0 { + // use the default value + return i.Default, err + } + + lineStr := i.answer + + i.AppendRenderedText(lineStr) + + // we're done + return lineStr, err +} + +func (i *Input) Cleanup(config *PromptConfig, val interface{}) error { + // use the default answer when cleaning up the prompt if necessary + ans := i.answer + if ans == "" && i.Default != "" { + ans = i.Default + } + + // render the cleanup + return i.Render( + InputQuestionTemplate, + InputTemplateData{ + Input: *i, + ShowAnswer: true, + Config: config, + Answer: ans, + }, + ) +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/multiline.go b/vendor/github.com/AlecAivazis/survey/v2/multiline.go new file mode 100644 index 0000000000..d0523b7eb4 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/multiline.go @@ -0,0 +1,110 @@ +package survey + +import ( + "strings" + + "github.com/AlecAivazis/survey/v2/terminal" +) + +type Multiline struct { + Renderer + Message string + Default string + Help string +} + +// data available to the templates when processing +type MultilineTemplateData struct { + Multiline + Answer string + ShowAnswer bool + ShowHelp bool + Config *PromptConfig +} + +// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format +var MultilineQuestionTemplate = ` +{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}} +{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}} +{{- color "default+hb"}}{{ .Message }} {{color "reset"}} +{{- if .ShowAnswer}} + {{- "\n"}}{{color "cyan"}}{{.Answer}}{{color "reset"}} + {{- if .Answer }}{{ "\n" }}{{ end }} +{{- else }} + {{- if .Default}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}} + {{- color "cyan"}}[Enter 2 empty lines to finish]{{color "reset"}} +{{- end}}` + +func (i *Multiline) Prompt(config *PromptConfig) (interface{}, error) { + // render the template + err := i.Render( + MultilineQuestionTemplate, + MultilineTemplateData{ + Multiline: *i, + Config: config, + }, + ) + if err != nil { + return "", err + } + + // start reading runes from the standard in + rr := i.NewRuneReader() + rr.SetTermMode() + defer rr.RestoreTermMode() + + cursor := i.NewCursor() + + multiline := make([]string, 0) + + emptyOnce := false + // get the next line + for { + line := []rune{} + line, err = rr.ReadLine(0) + if err != nil { + return string(line), err + } + + if string(line) == "" { + if emptyOnce { + numLines := len(multiline) + 2 + cursor.PreviousLine(numLines) + for j := 0; j < numLines; j++ { + terminal.EraseLine(i.Stdio().Out, terminal.ERASE_LINE_ALL) + cursor.NextLine(1) + } + cursor.PreviousLine(numLines) + break + } + emptyOnce = true + } else { + emptyOnce = false + } + multiline = append(multiline, string(line)) + } + + val := strings.Join(multiline, "\n") + val = strings.TrimSpace(val) + + // if the line is empty + if len(val) == 0 { + // use the default value + return i.Default, err + } + + i.AppendRenderedText(val) + return val, err +} + +func (i *Multiline) Cleanup(config *PromptConfig, val interface{}) error { + return i.Render( + MultilineQuestionTemplate, + MultilineTemplateData{ + Multiline: *i, + Answer: val.(string), + ShowAnswer: true, + Config: config, + }, + ) +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/multiselect.go b/vendor/github.com/AlecAivazis/survey/v2/multiselect.go new file mode 100644 index 0000000000..1e94f80f1c --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/multiselect.go @@ -0,0 +1,346 @@ +package survey + +import ( + "errors" + "fmt" + + "github.com/AlecAivazis/survey/v2/core" + "github.com/AlecAivazis/survey/v2/terminal" +) + +/* +MultiSelect is a prompt that presents a list of various options to the user +for them to select using the arrow keys and enter. Response type is a slice of strings. + + days := []string{} + prompt := &survey.MultiSelect{ + Message: "What days do you prefer:", + Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, + } + survey.AskOne(prompt, &days) +*/ +type MultiSelect struct { + Renderer + Message string + Options []string + Default interface{} + Help string + PageSize int + VimMode bool + FilterMessage string + Filter func(filter string, value string, index int) bool + filter string + selectedIndex int + checked map[int]bool + showingHelp bool +} + +// data available to the templates when processing +type MultiSelectTemplateData struct { + MultiSelect + Answer string + ShowAnswer bool + Checked map[int]bool + SelectedIndex int + ShowHelp bool + PageEntries []core.OptionAnswer + Config *PromptConfig + + // These fields are used when rendering an individual option + CurrentOpt core.OptionAnswer + CurrentIndex int +} + +// IterateOption sets CurrentOpt and CurrentIndex appropriately so a multiselect option can be rendered individually +func (m MultiSelectTemplateData) IterateOption(ix int, opt core.OptionAnswer) interface{} { + copy := m + copy.CurrentIndex = ix + copy.CurrentOpt = opt + return copy +} + +var MultiSelectQuestionTemplate = ` +{{- define "option"}} + {{- if eq .SelectedIndex .CurrentIndex }}{{color .Config.Icons.SelectFocus.Format }}{{ .Config.Icons.SelectFocus.Text }}{{color "reset"}}{{else}} {{end}} + {{- if index .Checked .CurrentOpt.Index }}{{color .Config.Icons.MarkedOption.Format }} {{ .Config.Icons.MarkedOption.Text }} {{else}}{{color .Config.Icons.UnmarkedOption.Format }} {{ .Config.Icons.UnmarkedOption.Text }} {{end}} + {{- color "reset"}} + {{- " "}}{{- .CurrentOpt.Value}} +{{end}} +{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}} +{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}} +{{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}} +{{- if .ShowAnswer}}{{color "cyan"}} {{.Answer}}{{color "reset"}}{{"\n"}} +{{- else }} + {{- " "}}{{- color "cyan"}}[Use arrows to move, space to select, to all, to none, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}} + {{- "\n"}} + {{- range $ix, $option := .PageEntries}} + {{- template "option" $.IterateOption $ix $option}} + {{- end}} +{{- end}}` + +// OnChange is called on every keypress. +func (m *MultiSelect) OnChange(key rune, config *PromptConfig) { + options := m.filterOptions(config) + oldFilter := m.filter + + if key == terminal.KeyArrowUp || (m.VimMode && key == 'k') { + // if we are at the top of the list + if m.selectedIndex == 0 { + // go to the bottom + m.selectedIndex = len(options) - 1 + } else { + // decrement the selected index + m.selectedIndex-- + } + } else if key == terminal.KeyTab || key == terminal.KeyArrowDown || (m.VimMode && key == 'j') { + // if we are at the bottom of the list + if m.selectedIndex == len(options)-1 { + // start at the top + m.selectedIndex = 0 + } else { + // increment the selected index + m.selectedIndex++ + } + // if the user pressed down and there is room to move + } else if key == terminal.KeySpace { + // the option they have selected + if m.selectedIndex < len(options) { + selectedOpt := options[m.selectedIndex] + + // if we haven't seen this index before + if old, ok := m.checked[selectedOpt.Index]; !ok { + // set the value to true + m.checked[selectedOpt.Index] = true + } else { + // otherwise just invert the current value + m.checked[selectedOpt.Index] = !old + } + if !config.KeepFilter { + m.filter = "" + } + } + // only show the help message if we have one to show + } else if string(key) == config.HelpInput && m.Help != "" { + m.showingHelp = true + } else if key == terminal.KeyEscape { + m.VimMode = !m.VimMode + } else if key == terminal.KeyDeleteWord || key == terminal.KeyDeleteLine { + m.filter = "" + } else if key == terminal.KeyDelete || key == terminal.KeyBackspace { + if m.filter != "" { + runeFilter := []rune(m.filter) + m.filter = string(runeFilter[0 : len(runeFilter)-1]) + } + } else if key >= terminal.KeySpace { + m.filter += string(key) + m.VimMode = false + } else if key == terminal.KeyArrowRight { + for _, v := range options { + m.checked[v.Index] = true + } + if !config.KeepFilter { + m.filter = "" + } + } else if key == terminal.KeyArrowLeft { + for _, v := range options { + m.checked[v.Index] = false + } + if !config.KeepFilter { + m.filter = "" + } + } + + m.FilterMessage = "" + if m.filter != "" { + m.FilterMessage = " " + m.filter + } + if oldFilter != m.filter { + // filter changed + options = m.filterOptions(config) + if len(options) > 0 && len(options) <= m.selectedIndex { + m.selectedIndex = len(options) - 1 + } + } + // paginate the options + // figure out the page size + pageSize := m.PageSize + // if we dont have a specific one + if pageSize == 0 { + // grab the global value + pageSize = config.PageSize + } + + // TODO if we have started filtering and were looking at the end of a list + // and we have modified the filter then we should move the page back! + opts, idx := paginate(pageSize, options, m.selectedIndex) + + tmplData := MultiSelectTemplateData{ + MultiSelect: *m, + SelectedIndex: idx, + Checked: m.checked, + ShowHelp: m.showingHelp, + PageEntries: opts, + Config: config, + } + + // render the options + m.RenderWithCursorOffset(MultiSelectQuestionTemplate, tmplData, opts, idx) +} + +func (m *MultiSelect) filterOptions(config *PromptConfig) []core.OptionAnswer { + // the filtered list + answers := []core.OptionAnswer{} + + // if there is no filter applied + if m.filter == "" { + // return all of the options + return core.OptionAnswerList(m.Options) + } + + // the filter to apply + filter := m.Filter + if filter == nil { + filter = config.Filter + } + + // apply the filter to each option + for i, opt := range m.Options { + // i the filter says to include the option + if filter(m.filter, opt, i) { + answers = append(answers, core.OptionAnswer{ + Index: i, + Value: opt, + }) + } + } + + // we're done here + return answers +} + +func (m *MultiSelect) Prompt(config *PromptConfig) (interface{}, error) { + // compute the default state + m.checked = make(map[int]bool) + // if there is a default + if m.Default != nil { + // if the default is string values + if defaultValues, ok := m.Default.([]string); ok { + for _, dflt := range defaultValues { + for i, opt := range m.Options { + // if the option corresponds to the default + if opt == dflt { + // we found our initial value + m.checked[i] = true + // stop looking + break + } + } + } + // if the default value is index values + } else if defaultIndices, ok := m.Default.([]int); ok { + // go over every index we need to enable by default + for _, idx := range defaultIndices { + // and enable it + m.checked[idx] = true + } + } + } + + // if there are no options to render + if len(m.Options) == 0 { + // we failed + return "", errors.New("please provide options to select from") + } + + // figure out the page size + pageSize := m.PageSize + // if we dont have a specific one + if pageSize == 0 { + // grab the global value + pageSize = config.PageSize + } + // paginate the options + // build up a list of option answers + opts, idx := paginate(pageSize, core.OptionAnswerList(m.Options), m.selectedIndex) + + cursor := m.NewCursor() + cursor.Save() // for proper cursor placement during selection + cursor.Hide() // hide the cursor + defer cursor.Show() // show the cursor when we're done + defer cursor.Restore() // clear any accessibility offsetting on exit + + tmplData := MultiSelectTemplateData{ + MultiSelect: *m, + SelectedIndex: idx, + Checked: m.checked, + PageEntries: opts, + Config: config, + } + + // ask the question + err := m.RenderWithCursorOffset(MultiSelectQuestionTemplate, tmplData, opts, idx) + if err != nil { + return "", err + } + + rr := m.NewRuneReader() + rr.SetTermMode() + defer rr.RestoreTermMode() + + // start waiting for input + for { + r, _, err := rr.ReadRune() + if err != nil { + return "", err + } + if r == '\r' || r == '\n' { + break + } + if r == terminal.KeyInterrupt { + return "", terminal.InterruptErr + } + if r == terminal.KeyEndTransmission { + break + } + m.OnChange(r, config) + } + m.filter = "" + m.FilterMessage = "" + + answers := []core.OptionAnswer{} + for i, option := range m.Options { + if val, ok := m.checked[i]; ok && val { + answers = append(answers, core.OptionAnswer{Value: option, Index: i}) + } + } + + return answers, nil +} + +// Cleanup removes the options section, and renders the ask like a normal question. +func (m *MultiSelect) Cleanup(config *PromptConfig, val interface{}) error { + // the answer to show + answer := "" + for _, ans := range val.([]core.OptionAnswer) { + answer = fmt.Sprintf("%s, %s", answer, ans.Value) + } + + // if we answered anything + if len(answer) > 2 { + // remove the precending commas + answer = answer[2:] + } + + // execute the output summary template with the answer + return m.Render( + MultiSelectQuestionTemplate, + MultiSelectTemplateData{ + MultiSelect: *m, + SelectedIndex: m.selectedIndex, + Checked: m.checked, + Answer: answer, + ShowAnswer: true, + Config: config, + }, + ) +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/password.go b/vendor/github.com/AlecAivazis/survey/v2/password.go new file mode 100644 index 0000000000..285bff5e75 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/password.go @@ -0,0 +1,101 @@ +package survey + +import ( + "fmt" + "strings" + + "github.com/AlecAivazis/survey/v2/core" + "github.com/AlecAivazis/survey/v2/terminal" +) + +/* +Password is like a normal Input but the text shows up as *'s and there is no default. Response +type is a string. + + password := "" + prompt := &survey.Password{ Message: "Please type your password" } + survey.AskOne(prompt, &password) +*/ +type Password struct { + Renderer + Message string + Help string +} + +type PasswordTemplateData struct { + Password + ShowHelp bool + Config *PromptConfig +} + +// PasswordQuestionTemplate is a template with color formatting. See Documentation: https://github.com/mgutz/ansi#style-format +var PasswordQuestionTemplate = ` +{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}} +{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}} +{{- color "default+hb"}}{{ .Message }} {{color "reset"}} +{{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ .Config.HelpInput }} for help]{{color "reset"}} {{end}}` + +func (p *Password) Prompt(config *PromptConfig) (interface{}, error) { + // render the question template + userOut, _, err := core.RunTemplate( + PasswordQuestionTemplate, + PasswordTemplateData{ + Password: *p, + Config: config, + }, + ) + fmt.Fprint(terminal.NewAnsiStdout(p.Stdio().Out), userOut) + if err != nil { + return "", err + } + + rr := p.NewRuneReader() + rr.SetTermMode() + defer rr.RestoreTermMode() + + // no help msg? Just return any response + if p.Help == "" { + line, err := rr.ReadLine('*') + return string(line), err + } + + cursor := p.NewCursor() + + line := []rune{} + // process answers looking for help prompt answer + for { + line, err = rr.ReadLine('*') + if err != nil { + return string(line), err + } + + if string(line) == config.HelpInput { + // terminal will echo the \n so we need to jump back up one row + cursor.PreviousLine(1) + + err = p.Render( + PasswordQuestionTemplate, + PasswordTemplateData{ + Password: *p, + ShowHelp: true, + Config: config, + }, + ) + if err != nil { + return "", err + } + continue + } + + break + } + + lineStr := string(line) + p.AppendRenderedText(strings.Repeat("*", len(lineStr))) + return lineStr, err +} + +// Cleanup hides the string with a fixed number of characters. +func (prompt *Password) Cleanup(config *PromptConfig, val interface{}) error { + return nil +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/renderer.go b/vendor/github.com/AlecAivazis/survey/v2/renderer.go new file mode 100644 index 0000000000..5503d81e7e --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/renderer.go @@ -0,0 +1,192 @@ +package survey + +import ( + "bytes" + "fmt" + "unicode/utf8" + + "github.com/AlecAivazis/survey/v2/core" + "github.com/AlecAivazis/survey/v2/terminal" + "golang.org/x/term" +) + +type Renderer struct { + stdio terminal.Stdio + renderedErrors bytes.Buffer + renderedText bytes.Buffer +} + +type ErrorTemplateData struct { + Error error + Icon Icon +} + +var ErrorTemplate = `{{color .Icon.Format }}{{ .Icon.Text }} Sorry, your reply was invalid: {{ .Error.Error }}{{color "reset"}} +` + +func (r *Renderer) WithStdio(stdio terminal.Stdio) { + r.stdio = stdio +} + +func (r *Renderer) Stdio() terminal.Stdio { + return r.stdio +} + +func (r *Renderer) NewRuneReader() *terminal.RuneReader { + return terminal.NewRuneReader(r.stdio) +} + +func (r *Renderer) NewCursor() *terminal.Cursor { + return &terminal.Cursor{ + In: r.stdio.In, + Out: r.stdio.Out, + } +} + +func (r *Renderer) Error(config *PromptConfig, invalid error) error { + // cleanup the currently rendered errors + r.resetPrompt(r.countLines(r.renderedErrors)) + r.renderedErrors.Reset() + + // cleanup the rest of the prompt + r.resetPrompt(r.countLines(r.renderedText)) + r.renderedText.Reset() + + userOut, layoutOut, err := core.RunTemplate(ErrorTemplate, &ErrorTemplateData{ + Error: invalid, + Icon: config.Icons.Error, + }) + if err != nil { + return err + } + + // send the message to the user + fmt.Fprint(terminal.NewAnsiStdout(r.stdio.Out), userOut) + + // add the printed text to the rendered error buffer so we can cleanup later + r.appendRenderedError(layoutOut) + + return nil +} + +func (r *Renderer) OffsetCursor(offset int) { + cursor := r.NewCursor() + for offset > 0 { + cursor.PreviousLine(1) + offset-- + } +} + +func (r *Renderer) Render(tmpl string, data interface{}) error { + // cleanup the currently rendered text + lineCount := r.countLines(r.renderedText) + r.resetPrompt(lineCount) + r.renderedText.Reset() + + // render the template summarizing the current state + userOut, layoutOut, err := core.RunTemplate(tmpl, data) + if err != nil { + return err + } + + // print the summary + fmt.Fprint(terminal.NewAnsiStdout(r.stdio.Out), userOut) + + // add the printed text to the rendered text buffer so we can cleanup later + r.AppendRenderedText(layoutOut) + + // nothing went wrong + return nil +} + +func (r *Renderer) RenderWithCursorOffset(tmpl string, data IterableOpts, opts []core.OptionAnswer, idx int) error { + cursor := r.NewCursor() + cursor.Restore() // clear any accessibility offsetting + + if err := r.Render(tmpl, data); err != nil { + return err + } + cursor.Save() + + offset := computeCursorOffset(MultiSelectQuestionTemplate, data, opts, idx, r.termWidthSafe()) + r.OffsetCursor(offset) + + return nil +} + +// appendRenderedError appends text to the renderer's error buffer +// which is used to track what has been printed. It is not exported +// as errors should only be displayed via Error(config, error). +func (r *Renderer) appendRenderedError(text string) { + r.renderedErrors.WriteString(text) +} + +// AppendRenderedText appends text to the renderer's text buffer +// which is used to track of what has been printed. The buffer is used +// to calculate how many lines to erase before updating the prompt. +func (r *Renderer) AppendRenderedText(text string) { + r.renderedText.WriteString(text) +} + +func (r *Renderer) resetPrompt(lines int) { + // clean out current line in case tmpl didnt end in newline + cursor := r.NewCursor() + cursor.HorizontalAbsolute(0) + terminal.EraseLine(r.stdio.Out, terminal.ERASE_LINE_ALL) + // clean up what we left behind last time + for i := 0; i < lines; i++ { + cursor.PreviousLine(1) + terminal.EraseLine(r.stdio.Out, terminal.ERASE_LINE_ALL) + } +} + +func (r *Renderer) termWidth() (int, error) { + fd := int(r.stdio.Out.Fd()) + termWidth, _, err := term.GetSize(fd) + return termWidth, err +} + +func (r *Renderer) termWidthSafe() int { + w, err := r.termWidth() + if err != nil || w == 0 { + // if we got an error due to terminal.GetSize not being supported + // on current platform then just assume a very wide terminal + w = 10000 + } + return w +} + +// countLines will return the count of `\n` with the addition of any +// lines that have wrapped due to narrow terminal width +func (r *Renderer) countLines(buf bytes.Buffer) int { + w := r.termWidthSafe() + + bufBytes := buf.Bytes() + + count := 0 + curr := 0 + delim := -1 + for curr < len(bufBytes) { + // read until the next newline or the end of the string + relDelim := bytes.IndexRune(bufBytes[curr:], '\n') + if relDelim != -1 { + count += 1 // new line found, add it to the count + delim = curr + relDelim + } else { + delim = len(bufBytes) // no new line found, read rest of text + } + + if lineWidth := utf8.RuneCount(bufBytes[curr:delim]); lineWidth > w { + // account for word wrapping + count += lineWidth / w + if (lineWidth % w) == 0 { + // content whose width is exactly a multiplier of available width should not + // count as having wrapped on the last line + count -= 1 + } + } + curr = delim + 1 + } + + return count +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/select.go b/vendor/github.com/AlecAivazis/survey/v2/select.go new file mode 100644 index 0000000000..915f34bcb3 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/select.go @@ -0,0 +1,347 @@ +package survey + +import ( + "errors" + + "github.com/AlecAivazis/survey/v2/core" + "github.com/AlecAivazis/survey/v2/terminal" +) + +/* +Select is a prompt that presents a list of various options to the user +for them to select using the arrow keys and enter. Response type is a string. + + color := "" + prompt := &survey.Select{ + Message: "Choose a color:", + Options: []string{"red", "blue", "green"}, + } + survey.AskOne(prompt, &color) +*/ +type Select struct { + Renderer + Message string + Options []string + Default interface{} + Help string + PageSize int + VimMode bool + FilterMessage string + Filter func(filter string, value string, index int) bool + filter string + selectedIndex int + useDefault bool + showingHelp bool +} + +// SelectTemplateData is the data available to the templates when processing +type SelectTemplateData struct { + Select + PageEntries []core.OptionAnswer + SelectedIndex int + Answer string + ShowAnswer bool + ShowHelp bool + Config *PromptConfig + + // These fields are used when rendering an individual option + CurrentOpt core.OptionAnswer + CurrentIndex int +} + +// IterateOption sets CurrentOpt and CurrentIndex appropriately so a select option can be rendered individually +func (s SelectTemplateData) IterateOption(ix int, opt core.OptionAnswer) interface{} { + copy := s + copy.CurrentIndex = ix + copy.CurrentOpt = opt + return copy +} + +var SelectQuestionTemplate = ` +{{- define "option"}} + {{- if eq .SelectedIndex .CurrentIndex }}{{color .Config.Icons.SelectFocus.Format }}{{ .Config.Icons.SelectFocus.Text }} {{else}}{{color "default"}} {{end}} + {{- .CurrentOpt.Value}} + {{- color "reset"}} +{{end}} +{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}} +{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}} +{{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}} +{{- if .ShowAnswer}}{{color "cyan"}} {{.Answer}}{{color "reset"}}{{"\n"}} +{{- else}} + {{- " "}}{{- color "cyan"}}[Use arrows to move, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}} + {{- "\n"}} + {{- range $ix, $option := .PageEntries}} + {{- template "option" $.IterateOption $ix $option}} + {{- end}} +{{- end}}` + +// OnChange is called on every keypress. +func (s *Select) OnChange(key rune, config *PromptConfig) bool { + options := s.filterOptions(config) + oldFilter := s.filter + + // if the user pressed the enter key and the index is a valid option + if key == terminal.KeyEnter || key == '\n' { + // if the selected index is a valid option + if len(options) > 0 && s.selectedIndex < len(options) { + + // we're done (stop prompting the user) + return true + } + + // we're not done (keep prompting) + return false + + // if the user pressed the up arrow or 'k' to emulate vim + } else if (key == terminal.KeyArrowUp || (s.VimMode && key == 'k')) && len(options) > 0 { + s.useDefault = false + + // if we are at the top of the list + if s.selectedIndex == 0 { + // start from the button + s.selectedIndex = len(options) - 1 + } else { + // otherwise we are not at the top of the list so decrement the selected index + s.selectedIndex-- + } + + // if the user pressed down or 'j' to emulate vim + } else if (key == terminal.KeyTab || key == terminal.KeyArrowDown || (s.VimMode && key == 'j')) && len(options) > 0 { + s.useDefault = false + // if we are at the bottom of the list + if s.selectedIndex == len(options)-1 { + // start from the top + s.selectedIndex = 0 + } else { + // increment the selected index + s.selectedIndex++ + } + // only show the help message if we have one + } else if string(key) == config.HelpInput && s.Help != "" { + s.showingHelp = true + // if the user wants to toggle vim mode on/off + } else if key == terminal.KeyEscape { + s.VimMode = !s.VimMode + // if the user hits any of the keys that clear the filter + } else if key == terminal.KeyDeleteWord || key == terminal.KeyDeleteLine { + s.filter = "" + // if the user is deleting a character in the filter + } else if key == terminal.KeyDelete || key == terminal.KeyBackspace { + // if there is content in the filter to delete + if s.filter != "" { + runeFilter := []rune(s.filter) + // subtract a line from the current filter + s.filter = string(runeFilter[0 : len(runeFilter)-1]) + // we removed the last value in the filter + } + } else if key >= terminal.KeySpace { + s.filter += string(key) + // make sure vim mode is disabled + s.VimMode = false + // make sure that we use the current value in the filtered list + s.useDefault = false + } + + s.FilterMessage = "" + if s.filter != "" { + s.FilterMessage = " " + s.filter + } + if oldFilter != s.filter { + // filter changed + options = s.filterOptions(config) + if len(options) > 0 && len(options) <= s.selectedIndex { + s.selectedIndex = len(options) - 1 + } + } + + // figure out the options and index to render + // figure out the page size + pageSize := s.PageSize + // if we dont have a specific one + if pageSize == 0 { + // grab the global value + pageSize = config.PageSize + } + + // TODO if we have started filtering and were looking at the end of a list + // and we have modified the filter then we should move the page back! + opts, idx := paginate(pageSize, options, s.selectedIndex) + + tmplData := SelectTemplateData{ + Select: *s, + SelectedIndex: idx, + ShowHelp: s.showingHelp, + PageEntries: opts, + Config: config, + } + + // render the options + s.RenderWithCursorOffset(SelectQuestionTemplate, tmplData, opts, idx) + + // keep prompting + return false +} + +func (s *Select) filterOptions(config *PromptConfig) []core.OptionAnswer { + // the filtered list + answers := []core.OptionAnswer{} + + // if there is no filter applied + if s.filter == "" { + return core.OptionAnswerList(s.Options) + } + + // the filter to apply + filter := s.Filter + if filter == nil { + filter = config.Filter + } + + // + for i, opt := range s.Options { + // i the filter says to include the option + if filter(s.filter, opt, i) { + answers = append(answers, core.OptionAnswer{ + Index: i, + Value: opt, + }) + } + } + + // return the list of answers + return answers +} + +func (s *Select) Prompt(config *PromptConfig) (interface{}, error) { + // if there are no options to render + if len(s.Options) == 0 { + // we failed + return "", errors.New("please provide options to select from") + } + + // start off with the first option selected + sel := 0 + // if there is a default + if s.Default != "" { + // find the choice + for i, opt := range s.Options { + // if the option corresponds to the default + if opt == s.Default { + // we found our initial value + sel = i + // stop looking + break + } + } + } + // save the selected index + s.selectedIndex = sel + + // figure out the page size + pageSize := s.PageSize + // if we dont have a specific one + if pageSize == 0 { + // grab the global value + pageSize = config.PageSize + } + + // figure out the options and index to render + opts, idx := paginate(pageSize, core.OptionAnswerList(s.Options), sel) + + cursor := s.NewCursor() + cursor.Save() // for proper cursor placement during selection + cursor.Hide() // hide the cursor + defer cursor.Show() // show the cursor when we're done + defer cursor.Restore() // clear any accessibility offsetting on exit + + tmplData := SelectTemplateData{ + Select: *s, + SelectedIndex: idx, + ShowHelp: s.showingHelp, + PageEntries: opts, + Config: config, + } + + // ask the question + err := s.RenderWithCursorOffset(SelectQuestionTemplate, tmplData, opts, idx) + if err != nil { + return "", err + } + + // by default, use the default value + s.useDefault = true + + rr := s.NewRuneReader() + rr.SetTermMode() + defer rr.RestoreTermMode() + + // start waiting for input + for { + r, _, err := rr.ReadRune() + if err != nil { + return "", err + } + if r == terminal.KeyInterrupt { + return "", terminal.InterruptErr + } + if r == terminal.KeyEndTransmission { + break + } + if s.OnChange(r, config) { + break + } + } + options := s.filterOptions(config) + s.filter = "" + s.FilterMessage = "" + + // the index to report + var val string + // if we are supposed to use the default value + if s.useDefault || s.selectedIndex >= len(options) { + // if there is a default value + if s.Default != nil { + // if the default is a string + if defaultString, ok := s.Default.(string); ok { + // use the default value + val = defaultString + // the default value could also be an interpret which is interpretted as the index + } else if defaultIndex, ok := s.Default.(int); ok { + val = s.Options[defaultIndex] + } else { + return val, errors.New("default value of select must be an int or string") + } + } else if len(options) > 0 { + // there is no default value so use the first + val = options[0].Value + } + // otherwise the selected index points to the value + } else if s.selectedIndex < len(options) { + // the + val = options[s.selectedIndex].Value + } + + // now that we have the value lets go hunt down the right index to return + idx = -1 + for i, optionValue := range s.Options { + if optionValue == val { + idx = i + } + } + + return core.OptionAnswer{Value: val, Index: idx}, err +} + +func (s *Select) Cleanup(config *PromptConfig, val interface{}) error { + cursor := s.NewCursor() + cursor.Restore() + return s.Render( + SelectQuestionTemplate, + SelectTemplateData{ + Select: *s, + Answer: val.(core.OptionAnswer).Value, + ShowAnswer: true, + Config: config, + }, + ) +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/survey.go b/vendor/github.com/AlecAivazis/survey/v2/survey.go new file mode 100644 index 0000000000..1aae459ef3 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/survey.go @@ -0,0 +1,454 @@ +package survey + +import ( + "bytes" + "errors" + "io" + "os" + "strings" + "unicode/utf8" + + "github.com/AlecAivazis/survey/v2/core" + "github.com/AlecAivazis/survey/v2/terminal" +) + +// DefaultAskOptions is the default options on ask, using the OS stdio. +func defaultAskOptions() *AskOptions { + return &AskOptions{ + Stdio: terminal.Stdio{ + In: os.Stdin, + Out: os.Stdout, + Err: os.Stderr, + }, + PromptConfig: PromptConfig{ + PageSize: 7, + HelpInput: "?", + SuggestInput: "tab", + Icons: IconSet{ + Error: Icon{ + Text: "X", + Format: "red", + }, + Help: Icon{ + Text: "?", + Format: "cyan", + }, + Question: Icon{ + Text: "?", + Format: "green+hb", + }, + MarkedOption: Icon{ + Text: "[x]", + Format: "green", + }, + UnmarkedOption: Icon{ + Text: "[ ]", + Format: "default+hb", + }, + SelectFocus: Icon{ + Text: ">", + Format: "cyan+b", + }, + }, + Filter: func(filter string, value string, index int) (include bool) { + filter = strings.ToLower(filter) + + // include this option if it matches + return strings.Contains(strings.ToLower(value), filter) + }, + KeepFilter: false, + ShowCursor: false, + }, + } +} +func defaultPromptConfig() *PromptConfig { + return &defaultAskOptions().PromptConfig +} + +func defaultIcons() *IconSet { + return &defaultPromptConfig().Icons +} + +// OptionAnswer is an ergonomic alias for core.OptionAnswer +type OptionAnswer = core.OptionAnswer + +// Icon holds the text and format to show for a particular icon +type Icon struct { + Text string + Format string +} + +// IconSet holds the icons to use for various prompts +type IconSet struct { + HelpInput Icon + Error Icon + Help Icon + Question Icon + MarkedOption Icon + UnmarkedOption Icon + SelectFocus Icon +} + +// Validator is a function passed to a Question after a user has provided a response. +// If the function returns an error, then the user will be prompted again for another +// response. +type Validator func(ans interface{}) error + +// Transformer is a function passed to a Question after a user has provided a response. +// The function can be used to implement a custom logic that will result to return +// a different representation of the given answer. +// +// Look `TransformString`, `ToLower` `Title` and `ComposeTransformers` for more. +type Transformer func(ans interface{}) (newAns interface{}) + +// Question is the core data structure for a survey questionnaire. +type Question struct { + Name string + Prompt Prompt + Validate Validator + Transform Transformer +} + +// PromptConfig holds the global configuration for a prompt +type PromptConfig struct { + PageSize int + Icons IconSet + HelpInput string + SuggestInput string + Filter func(filter string, option string, index int) bool + KeepFilter bool + ShowCursor bool +} + +// Prompt is the primary interface for the objects that can take user input +// and return a response. +type Prompt interface { + Prompt(config *PromptConfig) (interface{}, error) + Cleanup(*PromptConfig, interface{}) error + Error(*PromptConfig, error) error +} + +// PromptAgainer Interface for Prompts that support prompting again after invalid input +type PromptAgainer interface { + PromptAgain(config *PromptConfig, invalid interface{}, err error) (interface{}, error) +} + +// AskOpt allows setting optional ask options. +type AskOpt func(options *AskOptions) error + +// AskOptions provides additional options on ask. +type AskOptions struct { + Stdio terminal.Stdio + Validators []Validator + PromptConfig PromptConfig +} + +// WithStdio specifies the standard input, output and error files survey +// interacts with. By default, these are os.Stdin, os.Stdout, and os.Stderr. +func WithStdio(in terminal.FileReader, out terminal.FileWriter, err io.Writer) AskOpt { + return func(options *AskOptions) error { + options.Stdio.In = in + options.Stdio.Out = out + options.Stdio.Err = err + return nil + } +} + +// WithFilter specifies the default filter to use when asking questions. +func WithFilter(filter func(filter string, value string, index int) (include bool)) AskOpt { + return func(options *AskOptions) error { + // save the filter internally + options.PromptConfig.Filter = filter + + return nil + } +} + +// WithKeepFilter sets the if the filter is kept after selections +func WithKeepFilter(KeepFilter bool) AskOpt { + return func(options *AskOptions) error { + // set the page size + options.PromptConfig.KeepFilter = KeepFilter + + // nothing went wrong + return nil + } +} + +// WithValidator specifies a validator to use while prompting the user +func WithValidator(v Validator) AskOpt { + return func(options *AskOptions) error { + // add the provided validator to the list + options.Validators = append(options.Validators, v) + + // nothing went wrong + return nil + } +} + +type wantsStdio interface { + WithStdio(terminal.Stdio) +} + +// WithPageSize sets the default page size used by prompts +func WithPageSize(pageSize int) AskOpt { + return func(options *AskOptions) error { + // set the page size + options.PromptConfig.PageSize = pageSize + + // nothing went wrong + return nil + } +} + +// WithHelpInput changes the character that prompts look for to give the user helpful information. +func WithHelpInput(r rune) AskOpt { + return func(options *AskOptions) error { + // set the input character + options.PromptConfig.HelpInput = string(r) + + // nothing went wrong + return nil + } +} + +// WithIcons sets the icons that will be used when prompting the user +func WithIcons(setIcons func(*IconSet)) AskOpt { + return func(options *AskOptions) error { + // update the default icons with whatever the user says + setIcons(&options.PromptConfig.Icons) + + // nothing went wrong + return nil + } +} + +// WithShowCursor sets the show cursor behavior when prompting the user +func WithShowCursor(ShowCursor bool) AskOpt { + return func(options *AskOptions) error { + // set the page size + options.PromptConfig.ShowCursor = ShowCursor + + // nothing went wrong + return nil + } +} + +/* +AskOne performs the prompt for a single prompt and asks for validation if required. +Response types should be something that can be casted from the response type designated +in the documentation. For example: + + name := "" + prompt := &survey.Input{ + Message: "name", + } + + survey.AskOne(prompt, &name) + +*/ +func AskOne(p Prompt, response interface{}, opts ...AskOpt) error { + err := Ask([]*Question{{Prompt: p}}, response, opts...) + if err != nil { + return err + } + + return nil +} + +/* +Ask performs the prompt loop, asking for validation when appropriate. The response +type can be one of two options. If a struct is passed, the answer will be written to +the field whose name matches the Name field on the corresponding question. Field types +should be something that can be casted from the response type designated in the +documentation. Note, a survey tag can also be used to identify a Otherwise, a +map[string]interface{} can be passed, responses will be written to the key with the +matching name. For example: + + qs := []*survey.Question{ + { + Name: "name", + Prompt: &survey.Input{Message: "What is your name?"}, + Validate: survey.Required, + Transform: survey.Title, + }, + } + + answers := struct{ Name string }{} + + + err := survey.Ask(qs, &answers) +*/ +func Ask(qs []*Question, response interface{}, opts ...AskOpt) error { + // build up the configuration options + options := defaultAskOptions() + for _, opt := range opts { + if opt == nil { + continue + } + if err := opt(options); err != nil { + return err + } + } + + // if we weren't passed a place to record the answers + if response == nil { + // we can't go any further + return errors.New("cannot call Ask() with a nil reference to record the answers") + } + + // go over every question + for _, q := range qs { + // If Prompt implements controllable stdio, pass in specified stdio. + if p, ok := q.Prompt.(wantsStdio); ok { + p.WithStdio(options.Stdio) + } + + // grab the user input and save it + ans, err := q.Prompt.Prompt(&options.PromptConfig) + // if there was a problem + if err != nil { + return err + } + + // build up a list of validators that we have to apply to this question + validators := []Validator{} + + // make sure to include the question specific one + if q.Validate != nil { + validators = append(validators, q.Validate) + } + // add any "global" validators + for _, validator := range options.Validators { + validators = append(validators, validator) + } + + // apply every validator to thte response + for _, validator := range validators { + // wait for a valid response + for invalid := validator(ans); invalid != nil; invalid = validator(ans) { + err := q.Prompt.Error(&options.PromptConfig, invalid) + // if there was a problem + if err != nil { + return err + } + + // ask for more input + if promptAgainer, ok := q.Prompt.(PromptAgainer); ok { + ans, err = promptAgainer.PromptAgain(&options.PromptConfig, ans, invalid) + } else { + ans, err = q.Prompt.Prompt(&options.PromptConfig) + } + // if there was a problem + if err != nil { + return err + } + } + } + + if q.Transform != nil { + // check if we have a transformer available, if so + // then try to acquire the new representation of the + // answer, if the resulting answer is not nil. + if newAns := q.Transform(ans); newAns != nil { + ans = newAns + } + } + + // tell the prompt to cleanup with the validated value + q.Prompt.Cleanup(&options.PromptConfig, ans) + + // if something went wrong + if err != nil { + // stop listening + return err + } + + // add it to the map + err = core.WriteAnswer(response, q.Name, ans) + // if something went wrong + if err != nil { + return err + } + + } + + // return the response + return nil +} + +// paginate returns a single page of choices given the page size, the total list of +// possible choices, and the current selected index in the total list. +func paginate(pageSize int, choices []core.OptionAnswer, sel int) ([]core.OptionAnswer, int) { + var start, end, cursor int + + if len(choices) < pageSize { + // if we dont have enough options to fill a page + start = 0 + end = len(choices) + cursor = sel + + } else if sel < pageSize/2 { + // if we are in the first half page + start = 0 + end = pageSize + cursor = sel + + } else if len(choices)-sel-1 < pageSize/2 { + // if we are in the last half page + start = len(choices) - pageSize + end = len(choices) + cursor = sel - start + + } else { + // somewhere in the middle + above := pageSize / 2 + below := pageSize - above + + cursor = pageSize / 2 + start = sel - above + end = sel + below + } + + // return the subset we care about and the index + return choices[start:end], cursor +} + +type IterableOpts interface { + IterateOption(int, core.OptionAnswer) interface{} +} + +func computeCursorOffset(tmpl string, data IterableOpts, opts []core.OptionAnswer, idx, tWidth int) int { + tmpls, err := core.GetTemplatePair(tmpl) + + if err != nil { + return 0 + } + + t := tmpls[0] + + renderOpt := func(ix int, opt core.OptionAnswer) string { + buf := bytes.NewBufferString("") + t.ExecuteTemplate(buf, "option", data.IterateOption(ix, opt)) + return buf.String() + } + + offset := len(opts) - idx + + for i, o := range opts { + if i < idx { + continue + } + renderedOpt := renderOpt(i, o) + valWidth := utf8.RuneCount([]byte(renderedOpt)) + if valWidth > tWidth { + splitCount := valWidth / tWidth + if valWidth%tWidth == 0 { + splitCount -= 1 + } + offset += splitCount + } + } + + return offset +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/LICENSE.txt b/vendor/github.com/AlecAivazis/survey/v2/terminal/LICENSE.txt new file mode 100644 index 0000000000..ade5fef6d0 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2014 Takashi Kokubun + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/README.md b/vendor/github.com/AlecAivazis/survey/v2/terminal/README.md new file mode 100644 index 0000000000..d0b9db0fd4 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/README.md @@ -0,0 +1,3 @@ +# survey/terminal + +This package started as a copy of [kokuban/go-ansi](http://github.com/k0kubun/go-ansi) but has since been modified to fit survey's specific needs. diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/buffered_reader.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/buffered_reader.go new file mode 100644 index 0000000000..55eac15222 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/buffered_reader.go @@ -0,0 +1,22 @@ +package terminal + +import ( + "bytes" + "io" +) + +type BufferedReader struct { + In io.Reader + Buffer *bytes.Buffer +} + +func (br *BufferedReader) Read(p []byte) (int, error) { + n, err := br.Buffer.Read(p) + if err != nil && err != io.EOF { + return n, err + } else if err == nil { + return n, nil + } + + return br.In.Read(p[n:]) +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/cursor.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/cursor.go new file mode 100644 index 0000000000..1ac74fd6f5 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/cursor.go @@ -0,0 +1,190 @@ +// +build !windows + +package terminal + +import ( + "bufio" + "bytes" + "fmt" + "io" + "regexp" + "strconv" +) + +var COORDINATE_SYSTEM_BEGIN Short = 1 + +var dsrPattern = regexp.MustCompile(`\x1b\[(\d+);(\d+)R$`) + +type Cursor struct { + In FileReader + Out FileWriter +} + +// Up moves the cursor n cells to up. +func (c *Cursor) Up(n int) { + fmt.Fprintf(c.Out, "\x1b[%dA", n) +} + +// Down moves the cursor n cells to down. +func (c *Cursor) Down(n int) { + fmt.Fprintf(c.Out, "\x1b[%dB", n) +} + +// Forward moves the cursor n cells to right. +func (c *Cursor) Forward(n int) { + fmt.Fprintf(c.Out, "\x1b[%dC", n) +} + +// Back moves the cursor n cells to left. +func (c *Cursor) Back(n int) { + fmt.Fprintf(c.Out, "\x1b[%dD", n) +} + +// NextLine moves cursor to beginning of the line n lines down. +func (c *Cursor) NextLine(n int) { + c.Down(1) + c.HorizontalAbsolute(0) +} + +// PreviousLine moves cursor to beginning of the line n lines up. +func (c *Cursor) PreviousLine(n int) { + c.Up(1) + c.HorizontalAbsolute(0) +} + +// HorizontalAbsolute moves cursor horizontally to x. +func (c *Cursor) HorizontalAbsolute(x int) { + fmt.Fprintf(c.Out, "\x1b[%dG", x) +} + +// Show shows the cursor. +func (c *Cursor) Show() { + fmt.Fprint(c.Out, "\x1b[?25h") +} + +// Hide hide the cursor. +func (c *Cursor) Hide() { + fmt.Fprint(c.Out, "\x1b[?25l") +} + +// Move moves the cursor to a specific x,y location. +func (c *Cursor) Move(x int, y int) { + fmt.Fprintf(c.Out, "\x1b[%d;%df", x, y) +} + +// Save saves the current position +func (c *Cursor) Save() { + fmt.Fprint(c.Out, "\x1b7") +} + +// Restore restores the saved position of the cursor +func (c *Cursor) Restore() { + fmt.Fprint(c.Out, "\x1b8") +} + +// for comparability purposes between windows +// in unix we need to print out a new line on some terminals +func (c *Cursor) MoveNextLine(cur *Coord, terminalSize *Coord) { + if cur.Y == terminalSize.Y { + fmt.Fprintln(c.Out) + } + c.NextLine(1) +} + +// Location returns the current location of the cursor in the terminal +func (c *Cursor) Location(buf *bytes.Buffer) (*Coord, error) { + // ANSI escape sequence for DSR - Device Status Report + // https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences + fmt.Fprint(c.Out, "\x1b[6n") + + // There may be input in Stdin prior to CursorLocation so make sure we don't + // drop those bytes. + var loc []int + var match string + for loc == nil { + // Reports the cursor position (CPR) to the application as (as though typed at + // the keyboard) ESC[n;mR, where n is the row and m is the column. + reader := bufio.NewReader(c.In) + text, err := reader.ReadSlice(byte('R')) + if err != nil { + return nil, err + } + + loc = dsrPattern.FindStringIndex(string(text)) + if loc == nil { + // After reading slice to byte 'R', the bufio Reader may have read more + // bytes into its internal buffer which will be discarded on next ReadSlice. + // We create a temporary buffer to read the remaining buffered slice and + // write them to output buffer. + buffered := make([]byte, reader.Buffered()) + _, err = io.ReadFull(reader, buffered) + if err != nil { + return nil, err + } + + // Stdin contains R that doesn't match DSR, so pass the bytes along to + // output buffer. + buf.Write(text) + buf.Write(buffered) + } else { + // Write the non-matching leading bytes to output buffer. + buf.Write(text[:loc[0]]) + + // Save the matching bytes to extract the row and column of the cursor. + match = string(text[loc[0]:loc[1]]) + } + } + + matches := dsrPattern.FindStringSubmatch(string(match)) + if len(matches) != 3 { + return nil, fmt.Errorf("incorrect number of matches: %d", len(matches)) + } + + col, err := strconv.Atoi(matches[2]) + if err != nil { + return nil, err + } + + row, err := strconv.Atoi(matches[1]) + if err != nil { + return nil, err + } + + return &Coord{Short(col), Short(row)}, nil +} + +func (cur Coord) CursorIsAtLineEnd(size *Coord) bool { + return cur.X == size.X +} + +func (cur Coord) CursorIsAtLineBegin() bool { + return cur.X == COORDINATE_SYSTEM_BEGIN +} + +// Size returns the height and width of the terminal. +func (c *Cursor) Size(buf *bytes.Buffer) (*Coord, error) { + // the general approach here is to move the cursor to the very bottom + // of the terminal, ask for the current location and then move the + // cursor back where we started + + // hide the cursor (so it doesn't blink when getting the size of the terminal) + c.Hide() + defer c.Show() + + // save the current location of the cursor + c.Save() + defer c.Restore() + + // move the cursor to the very bottom of the terminal + c.Move(999, 999) + + // ask for the current location + bottom, err := c.Location(buf) + if err != nil { + return nil, err + } + + // since the bottom was calculated in the lower right corner, it + // is the dimensions we are looking for + return bottom, nil +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/cursor_windows.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/cursor_windows.go new file mode 100644 index 0000000000..e24440e7c5 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/cursor_windows.go @@ -0,0 +1,138 @@ +package terminal + +import ( + "bytes" + "syscall" + "unsafe" +) + +var COORDINATE_SYSTEM_BEGIN Short = 0 + +// shared variable to save the cursor location from CursorSave() +var cursorLoc Coord + +type Cursor struct { + In FileReader + Out FileWriter +} + +func (c *Cursor) Up(n int) { + c.cursorMove(0, n) +} + +func (c *Cursor) Down(n int) { + c.cursorMove(0, -1*n) +} + +func (c *Cursor) Forward(n int) { + c.cursorMove(n, 0) +} + +func (c *Cursor) Back(n int) { + c.cursorMove(-1*n, 0) +} + +// save the cursor location +func (c *Cursor) Save() { + cursorLoc, _ = c.Location(nil) +} + +func (c *Cursor) Restore() { + handle := syscall.Handle(c.Out.Fd()) + // restore it to the original position + procSetConsoleCursorPosition.Call(uintptr(handle), uintptr(*(*int32)(unsafe.Pointer(&cursorLoc)))) +} + +func (cur Coord) CursorIsAtLineEnd(size *Coord) bool { + return cur.X == size.X +} + +func (cur Coord) CursorIsAtLineBegin() bool { + return cur.X == 0 +} + +func (c *Cursor) cursorMove(x int, y int) { + handle := syscall.Handle(c.Out.Fd()) + + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + + var cursor Coord + cursor.X = csbi.cursorPosition.X + Short(x) + cursor.Y = csbi.cursorPosition.Y + Short(y) + + procSetConsoleCursorPosition.Call(uintptr(handle), uintptr(*(*int32)(unsafe.Pointer(&cursor)))) +} + +func (c *Cursor) NextLine(n int) { + c.Up(n) + c.HorizontalAbsolute(0) +} + +func (c *Cursor) PreviousLine(n int) { + c.Down(n) + c.HorizontalAbsolute(0) +} + +// for comparability purposes between windows +// in windows we don't have to print out a new line +func (c *Cursor) MoveNextLine(cur Coord, terminalSize *Coord) { + c.NextLine(1) +} + +func (c *Cursor) HorizontalAbsolute(x int) { + handle := syscall.Handle(c.Out.Fd()) + + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + + var cursor Coord + cursor.X = Short(x) + cursor.Y = csbi.cursorPosition.Y + + if csbi.size.X < cursor.X { + cursor.X = csbi.size.X + } + + procSetConsoleCursorPosition.Call(uintptr(handle), uintptr(*(*int32)(unsafe.Pointer(&cursor)))) +} + +func (c *Cursor) Show() { + handle := syscall.Handle(c.Out.Fd()) + + var cci consoleCursorInfo + procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))) + cci.visible = 1 + + procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))) +} + +func (c *Cursor) Hide() { + handle := syscall.Handle(c.Out.Fd()) + + var cci consoleCursorInfo + procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))) + cci.visible = 0 + + procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))) +} + +func (c *Cursor) Location(buf *bytes.Buffer) (Coord, error) { + handle := syscall.Handle(c.Out.Fd()) + + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + + return csbi.cursorPosition, nil +} + +func (c *Cursor) Size(buf *bytes.Buffer) (*Coord, error) { + handle := syscall.Handle(c.Out.Fd()) + + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + // windows' coordinate system begins at (0, 0) + csbi.size.X-- + csbi.size.Y-- + return &csbi.size, nil +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/display.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/display.go new file mode 100644 index 0000000000..0f014b1353 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/display.go @@ -0,0 +1,9 @@ +package terminal + +type EraseLineMode int + +const ( + ERASE_LINE_END EraseLineMode = iota + ERASE_LINE_START + ERASE_LINE_ALL +) diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/display_posix.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/display_posix.go new file mode 100644 index 0000000000..838dd6646e --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/display_posix.go @@ -0,0 +1,11 @@ +// +build !windows + +package terminal + +import ( + "fmt" +) + +func EraseLine(out FileWriter, mode EraseLineMode) { + fmt.Fprintf(out, "\x1b[%dK", mode) +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/display_windows.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/display_windows.go new file mode 100644 index 0000000000..0adc1ded38 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/display_windows.go @@ -0,0 +1,27 @@ +package terminal + +import ( + "syscall" + "unsafe" +) + +func EraseLine(out FileWriter, mode EraseLineMode) { + handle := syscall.Handle(out.Fd()) + + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + + var w uint32 + var x Short + cursor := csbi.cursorPosition + switch mode { + case ERASE_LINE_END: + x = csbi.size.X + case ERASE_LINE_START: + x = 0 + case ERASE_LINE_ALL: + cursor.X = 0 + x = csbi.size.X + } + procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(x), uintptr(*(*int32)(unsafe.Pointer(&cursor))), uintptr(unsafe.Pointer(&w))) +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/error.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/error.go new file mode 100644 index 0000000000..710c361406 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/error.go @@ -0,0 +1,9 @@ +package terminal + +import ( + "errors" +) + +var ( + InterruptErr = errors.New("interrupt") +) diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/output.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/output.go new file mode 100644 index 0000000000..6fe11c089f --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/output.go @@ -0,0 +1,19 @@ +// +build !windows + +package terminal + +import ( + "io" +) + +// NewAnsiStdout returns special stdout, which converts escape sequences to Windows API calls +// on Windows environment. +func NewAnsiStdout(out FileWriter) io.Writer { + return out +} + +// NewAnsiStderr returns special stderr, which converts escape sequences to Windows API calls +// on Windows environment. +func NewAnsiStderr(out FileWriter) io.Writer { + return out +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/output_windows.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/output_windows.go new file mode 100644 index 0000000000..6622690f32 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/output_windows.go @@ -0,0 +1,227 @@ +package terminal + +import ( + "bytes" + "fmt" + "io" + "strconv" + "strings" + "syscall" + "unsafe" + + "github.com/mattn/go-isatty" +) + +var ( + cursorFunctions = map[rune]func(c *Cursor) func(int){ + 'A': func(c *Cursor) func(int) { return c.Up }, + 'B': func(c *Cursor) func(int) { return c.Down }, + 'C': func(c *Cursor) func(int) { return c.Forward }, + 'D': func(c *Cursor) func(int) { return c.Back }, + 'E': func(c *Cursor) func(int) { return c.NextLine }, + 'F': func(c *Cursor) func(int) { return c.PreviousLine }, + 'G': func(c *Cursor) func(int) { return c.HorizontalAbsolute }, + } +) + +const ( + foregroundBlue = 0x1 + foregroundGreen = 0x2 + foregroundRed = 0x4 + foregroundIntensity = 0x8 + foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) + backgroundBlue = 0x10 + backgroundGreen = 0x20 + backgroundRed = 0x40 + backgroundIntensity = 0x80 + backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) +) + +type Writer struct { + out FileWriter + handle syscall.Handle + orgAttr word +} + +func NewAnsiStdout(out FileWriter) io.Writer { + var csbi consoleScreenBufferInfo + if !isatty.IsTerminal(out.Fd()) { + return out + } + handle := syscall.Handle(out.Fd()) + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + return &Writer{out: out, handle: handle, orgAttr: csbi.attributes} +} + +func NewAnsiStderr(out FileWriter) io.Writer { + var csbi consoleScreenBufferInfo + if !isatty.IsTerminal(out.Fd()) { + return out + } + handle := syscall.Handle(out.Fd()) + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + return &Writer{out: out, handle: handle, orgAttr: csbi.attributes} +} + +func (w *Writer) Write(data []byte) (n int, err error) { + r := bytes.NewReader(data) + + for { + ch, size, err := r.ReadRune() + if err != nil { + break + } + n += size + + switch ch { + case '\x1b': + size, err = w.handleEscape(r) + n += size + if err != nil { + break + } + default: + fmt.Fprint(w.out, string(ch)) + } + } + return +} + +func (w *Writer) handleEscape(r *bytes.Reader) (n int, err error) { + buf := make([]byte, 0, 10) + buf = append(buf, "\x1b"...) + + // Check '[' continues after \x1b + ch, size, err := r.ReadRune() + if err != nil { + fmt.Fprint(w.out, string(buf)) + return + } + n += size + if ch != '[' { + fmt.Fprint(w.out, string(buf)) + return + } + + // Parse escape code + var code rune + argBuf := make([]byte, 0, 10) + for { + ch, size, err = r.ReadRune() + if err != nil { + fmt.Fprint(w.out, string(buf)) + return + } + n += size + if ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') { + code = ch + break + } + argBuf = append(argBuf, string(ch)...) + } + + w.applyEscapeCode(buf, string(argBuf), code) + return +} + +func (w *Writer) applyEscapeCode(buf []byte, arg string, code rune) { + c := &Cursor{Out: w.out} + + switch arg + string(code) { + case "?25h": + c.Show() + return + case "?25l": + c.Hide() + return + } + + if f, ok := cursorFunctions[code]; ok { + if n, err := strconv.Atoi(arg); err == nil { + f(c)(n) + return + } + } + + switch code { + case 'm': + w.applySelectGraphicRendition(arg) + default: + buf = append(buf, string(code)...) + fmt.Fprint(w.out, string(buf)) + } +} + +// Original implementation: https://github.com/mattn/go-colorable +func (w *Writer) applySelectGraphicRendition(arg string) { + if arg == "" { + procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.orgAttr)) + return + } + + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + attr := csbi.attributes + + for _, param := range strings.Split(arg, ";") { + n, err := strconv.Atoi(param) + if err != nil { + continue + } + + switch { + case n == 0 || n == 100: + attr = w.orgAttr + case 1 <= n && n <= 5: + attr |= foregroundIntensity + case 30 <= n && n <= 37: + attr = (attr & backgroundMask) + if (n-30)&1 != 0 { + attr |= foregroundRed + } + if (n-30)&2 != 0 { + attr |= foregroundGreen + } + if (n-30)&4 != 0 { + attr |= foregroundBlue + } + case 40 <= n && n <= 47: + attr = (attr & foregroundMask) + if (n-40)&1 != 0 { + attr |= backgroundRed + } + if (n-40)&2 != 0 { + attr |= backgroundGreen + } + if (n-40)&4 != 0 { + attr |= backgroundBlue + } + case 90 <= n && n <= 97: + attr = (attr & backgroundMask) + attr |= foregroundIntensity + if (n-90)&1 != 0 { + attr |= foregroundRed + } + if (n-90)&2 != 0 { + attr |= foregroundGreen + } + if (n-90)&4 != 0 { + attr |= foregroundBlue + } + case 100 <= n && n <= 107: + attr = (attr & foregroundMask) + attr |= backgroundIntensity + if (n-100)&1 != 0 { + attr |= backgroundRed + } + if (n-100)&2 != 0 { + attr |= backgroundGreen + } + if (n-100)&4 != 0 { + attr |= backgroundBlue + } + } + } + + procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr)) +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader.go new file mode 100644 index 0000000000..94c915c5de --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader.go @@ -0,0 +1,373 @@ +package terminal + +import ( + "fmt" + "unicode" + + "golang.org/x/text/width" +) + +type RuneReader struct { + stdio Stdio + cursor *Cursor + state runeReaderState +} + +func NewRuneReader(stdio Stdio) *RuneReader { + return &RuneReader{ + stdio: stdio, + state: newRuneReaderState(stdio.In), + } +} + +func (rr *RuneReader) printChar(char rune, mask rune) { + // if we don't need to mask the input + if mask == 0 { + // just print the character the user pressed + fmt.Fprintf(rr.stdio.Out, "%c", char) + } else { + // otherwise print the mask we were given + fmt.Fprintf(rr.stdio.Out, "%c", mask) + } +} + +type OnRuneFn func(rune, []rune) ([]rune, bool, error) + +func (rr *RuneReader) ReadLine(mask rune, onRunes ...OnRuneFn) ([]rune, error) { + return rr.ReadLineWithDefault(mask, []rune{}, onRunes...) +} + +func (rr *RuneReader) ReadLineWithDefault(mask rune, d []rune, onRunes ...OnRuneFn) ([]rune, error) { + line := []rune{} + // we only care about horizontal displacements from the origin so start counting at 0 + index := 0 + + cursor := &Cursor{ + In: rr.stdio.In, + Out: rr.stdio.Out, + } + + onRune := func(r rune, line []rune) ([]rune, bool, error) { + return line, false, nil + } + + // if the user pressed a key the caller was interested in capturing + if len(onRunes) > 0 { + onRune = onRunes[0] + } + + // we get the terminal width and height (if resized after this point the property might become invalid) + terminalSize, _ := cursor.Size(rr.Buffer()) + // we set the current location of the cursor once + cursorCurrent, _ := cursor.Location(rr.Buffer()) + + increment := func() { + if cursorCurrent.CursorIsAtLineEnd(terminalSize) { + cursorCurrent.X = COORDINATE_SYSTEM_BEGIN + cursorCurrent.Y++ + } else { + cursorCurrent.X++ + } + } + decrement := func() { + if cursorCurrent.CursorIsAtLineBegin() { + cursorCurrent.X = terminalSize.X + cursorCurrent.Y-- + } else { + cursorCurrent.X-- + } + } + + if len(d) > 0 { + index = len(d) + fmt.Fprint(rr.stdio.Out, string(d)) + line = d + for range d { + increment() + } + } + + for { + // wait for some input + r, _, err := rr.ReadRune() + if err != nil { + return line, err + } + + if l, stop, err := onRune(r, line); stop || err != nil { + return l, err + } + + // if the user pressed enter or some other newline/termination like ctrl+d + if r == '\r' || r == '\n' || r == KeyEndTransmission { + // delete what's printed out on the console screen (cleanup) + for index > 0 { + if cursorCurrent.CursorIsAtLineBegin() { + EraseLine(rr.stdio.Out, ERASE_LINE_END) + cursor.PreviousLine(1) + cursor.Forward(int(terminalSize.X)) + } else { + cursor.Back(1) + } + decrement() + index-- + } + // move the cursor the a new line + cursor.MoveNextLine(cursorCurrent, terminalSize) + + // we're done processing the input + return line, nil + } + // if the user interrupts (ie with ctrl+c) + if r == KeyInterrupt { + // go to the beginning of the next line + fmt.Fprint(rr.stdio.Out, "\r\n") + + // we're done processing the input, and treat interrupt like an error + return line, InterruptErr + } + + // allow for backspace/delete editing of inputs + if r == KeyBackspace || r == KeyDelete { + // and we're not at the beginning of the line + if index > 0 && len(line) > 0 { + // if we are at the end of the word + if index == len(line) { + // just remove the last letter from the internal representation + // also count the number of cells the rune before the cursor occupied + cells := runeWidth(line[len(line)-1]) + line = line[:len(line)-1] + // go back one + if cursorCurrent.X == 1 { + cursor.PreviousLine(1) + cursor.Forward(int(terminalSize.X)) + } else { + cursor.Back(cells) + } + + // clear the rest of the line + EraseLine(rr.stdio.Out, ERASE_LINE_END) + } else { + // we need to remove a character from the middle of the word + + cells := runeWidth(line[index-1]) + + // remove the current index from the list + line = append(line[:index-1], line[index:]...) + + // save the current position of the cursor, as we have to move the cursor one back to erase the current symbol + // and then move the cursor for each symbol in line[index-1:] to print it out, afterwards we want to restore + // the cursor to its previous location. + cursor.Save() + + // clear the rest of the line + cursor.Back(cells) + + // print what comes after + for _, char := range line[index-1:] { + //Erase symbols which are left over from older print + EraseLine(rr.stdio.Out, ERASE_LINE_END) + // print characters to the new line appropriately + rr.printChar(char, mask) + + } + // erase what's left over from last print + if cursorCurrent.Y < terminalSize.Y { + cursor.NextLine(1) + EraseLine(rr.stdio.Out, ERASE_LINE_END) + } + // restore cursor + cursor.Restore() + if cursorCurrent.CursorIsAtLineBegin() { + cursor.PreviousLine(1) + cursor.Forward(int(terminalSize.X)) + } else { + cursor.Back(cells) + } + } + + // decrement the index + index-- + decrement() + } else { + // otherwise the user pressed backspace while at the beginning of the line + soundBell(rr.stdio.Out) + } + + // we're done processing this key + continue + } + + // if the left arrow is pressed + if r == KeyArrowLeft { + // if we have space to the left + if index > 0 { + //move the cursor to the prev line if necessary + if cursorCurrent.CursorIsAtLineBegin() { + cursor.PreviousLine(1) + cursor.Forward(int(terminalSize.X)) + } else { + cursor.Back(runeWidth(line[index-1])) + } + //decrement the index + index-- + decrement() + + } else { + // otherwise we are at the beginning of where we started reading lines + // sound the bell + soundBell(rr.stdio.Out) + } + + // we're done processing this key press + continue + } + + // if the right arrow is pressed + if r == KeyArrowRight { + // if we have space to the right + if index < len(line) { + // move the cursor to the next line if necessary + if cursorCurrent.CursorIsAtLineEnd(terminalSize) { + cursor.NextLine(1) + } else { + cursor.Forward(runeWidth(line[index])) + } + index++ + increment() + + } else { + // otherwise we are at the end of the word and can't go past + // sound the bell + soundBell(rr.stdio.Out) + } + + // we're done processing this key press + continue + } + // the user pressed one of the special keys + if r == SpecialKeyHome { + for index > 0 { + if cursorCurrent.CursorIsAtLineBegin() { + cursor.PreviousLine(1) + cursor.Forward(int(terminalSize.X)) + cursorCurrent.Y-- + cursorCurrent.X = terminalSize.X + } else { + cursor.Back(runeWidth(line[index-1])) + cursorCurrent.X -= Short(runeWidth(line[index-1])) + } + index-- + } + continue + // user pressed end + } else if r == SpecialKeyEnd { + for index != len(line) { + if cursorCurrent.CursorIsAtLineEnd(terminalSize) { + cursor.NextLine(1) + cursorCurrent.Y++ + cursorCurrent.X = COORDINATE_SYSTEM_BEGIN + } else { + cursor.Forward(runeWidth(line[index])) + cursorCurrent.X += Short(runeWidth(line[index])) + } + index++ + } + continue + // user pressed forward delete key + } else if r == SpecialKeyDelete { + // if index at the end of the line nothing to delete + if index != len(line) { + // save the current position of the cursor, as we have to erase the current symbol + // and then move the cursor for each symbol in line[index:] to print it out, afterwards we want to restore + // the cursor to its previous location. + cursor.Save() + // remove the symbol after the cursor + line = append(line[:index], line[index+1:]...) + // print the updated line + for _, char := range line[index:] { + EraseLine(rr.stdio.Out, ERASE_LINE_END) + // print out the character + rr.printChar(char, mask) + } + // erase what's left on last line + if cursorCurrent.Y < terminalSize.Y { + cursor.NextLine(1) + EraseLine(rr.stdio.Out, ERASE_LINE_END) + } + // restore cursor + cursor.Restore() + if len(line) == 0 || index == len(line) { + EraseLine(rr.stdio.Out, ERASE_LINE_END) + } + } + continue + } + + // if the letter is another escape sequence + if unicode.IsControl(r) || r == IgnoreKey { + // ignore it + continue + } + + // the user pressed a regular key + + // if we are at the end of the line + if index == len(line) { + // just append the character at the end of the line + line = append(line, r) + // save the location of the cursor + index++ + increment() + // print out the character + rr.printChar(r, mask) + } else { + // we are in the middle of the word so we need to insert the character the user pressed + line = append(line[:index], append([]rune{r}, line[index:]...)...) + // save the current position of the cursor, as we have to move the cursor back to erase the current symbol + // and then move for each symbol in line[index:] to print it out, afterwards we want to restore + // cursor's location to its previous one. + cursor.Save() + EraseLine(rr.stdio.Out, ERASE_LINE_END) + // remove the symbol after the cursor + // print the updated line + for _, char := range line[index:] { + EraseLine(rr.stdio.Out, ERASE_LINE_END) + // print out the character + rr.printChar(char, mask) + increment() + } + // if we are at the last line, we want to visually insert a new line and append to it. + if cursorCurrent.CursorIsAtLineEnd(terminalSize) && cursorCurrent.Y == terminalSize.Y { + // add a new line to the terminal + fmt.Fprintln(rr.stdio.Out) + // restore the position of the cursor horizontally + cursor.Restore() + // restore the position of the cursor vertically + cursor.PreviousLine(1) + } else { + // restore cursor + cursor.Restore() + } + // check if cursor needs to move to next line + cursorCurrent, _ = cursor.Location(rr.Buffer()) + if cursorCurrent.CursorIsAtLineEnd(terminalSize) { + cursor.NextLine(1) + } else { + cursor.Forward(runeWidth(r)) + } + // increment the index + index++ + increment() + + } + } +} + +func runeWidth(r rune) int { + switch width.LookupRune(r).Kind() { + case width.EastAsianWide, width.EastAsianFullwidth: + return 2 + } + return 1 +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_bsd.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_bsd.go new file mode 100644 index 0000000000..6ea340923a --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_bsd.go @@ -0,0 +1,13 @@ +// copied from: https://github.com/golang/crypto/blob/master/ssh/terminal/util_bsd.go +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +package terminal + +import "syscall" + +const ioctlReadTermios = syscall.TIOCGETA +const ioctlWriteTermios = syscall.TIOCSETA diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_linux.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_linux.go new file mode 100644 index 0000000000..6dd60ea697 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_linux.go @@ -0,0 +1,13 @@ +// copied from https://github.com/golang/crypto/blob/master/ssh/terminal/util_linux.go +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +build linux,!ppc64le + +package terminal + +// These constants are declared here, rather than importing +// them from the syscall package as some syscall packages, even +// on linux, for example gccgo, do not declare them. +const ioctlReadTermios = 0x5401 // syscall.TCGETS +const ioctlWriteTermios = 0x5402 // syscall.TCSETS diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_posix.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_posix.go new file mode 100644 index 0000000000..99cbf4345b --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_posix.go @@ -0,0 +1,126 @@ +// +build !windows + +// The terminal mode manipulation code is derived heavily from: +// https://github.com/golang/crypto/blob/master/ssh/terminal/util.go: +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package terminal + +import ( + "bufio" + "bytes" + "fmt" + "syscall" + "unsafe" +) + +const ( + normalKeypad = '[' + applicationKeypad = 'O' +) + +type runeReaderState struct { + term syscall.Termios + reader *bufio.Reader + buf *bytes.Buffer +} + +func newRuneReaderState(input FileReader) runeReaderState { + buf := new(bytes.Buffer) + return runeReaderState{ + reader: bufio.NewReader(&BufferedReader{ + In: input, + Buffer: buf, + }), + buf: buf, + } +} + +func (rr *RuneReader) Buffer() *bytes.Buffer { + return rr.state.buf +} + +// For reading runes we just want to disable echo. +func (rr *RuneReader) SetTermMode() error { + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(rr.stdio.In.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&rr.state.term)), 0, 0, 0); err != 0 { + return err + } + + newState := rr.state.term + newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG + + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(rr.stdio.In.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { + return err + } + + return nil +} + +func (rr *RuneReader) RestoreTermMode() error { + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(rr.stdio.In.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&rr.state.term)), 0, 0, 0); err != 0 { + return err + } + return nil +} + +// ReadRune Parse escape sequences such as ESC [ A for arrow keys. +// See https://vt100.net/docs/vt102-ug/appendixc.html +func (rr *RuneReader) ReadRune() (rune, int, error) { + r, size, err := rr.state.reader.ReadRune() + if err != nil { + return r, size, err + } + + if r != KeyEscape { + return r, size, err + } + + if rr.state.reader.Buffered() == 0 { + // no more characters so must be `Esc` key + return KeyEscape, 1, nil + } + + r, size, err = rr.state.reader.ReadRune() + if err != nil { + return r, size, err + } + + // ESC O ... or ESC [ ...? + if r != normalKeypad && r != applicationKeypad { + return r, size, fmt.Errorf("unexpected escape sequence from terminal: %q", []rune{KeyEscape, r}) + } + + keypad := r + + r, size, err = rr.state.reader.ReadRune() + if err != nil { + return r, size, err + } + + switch r { + case 'A': // ESC [ A or ESC O A + return KeyArrowUp, 1, nil + case 'B': // ESC [ B or ESC O B + return KeyArrowDown, 1, nil + case 'C': // ESC [ C or ESC O C + return KeyArrowRight, 1, nil + case 'D': // ESC [ D or ESC O D + return KeyArrowLeft, 1, nil + case 'F': // ESC [ F or ESC O F + return SpecialKeyEnd, 1, nil + case 'H': // ESC [ H or ESC O H + return SpecialKeyHome, 1, nil + case '3': // ESC [ 3 + if keypad == normalKeypad { + // discard the following '~' key from buffer + _, _ = rr.state.reader.Discard(1) + return SpecialKeyDelete, 1, nil + } + } + + // discard the following '~' key from buffer + _, _ = rr.state.reader.Discard(1) + return IgnoreKey, 1, nil +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_ppc64le.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_ppc64le.go new file mode 100644 index 0000000000..ae4eb09737 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_ppc64le.go @@ -0,0 +1,7 @@ +// +build ppc64le,linux + +package terminal + +// Used syscall numbers from https://github.com/golang/go/blob/master/src/syscall/ztypes_linux_ppc64le.go +const ioctlReadTermios = 0x402c7413 // syscall.TCGETS +const ioctlWriteTermios = 0x802c7414 // syscall.TCSETS diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_windows.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_windows.go new file mode 100644 index 0000000000..791092f531 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_windows.go @@ -0,0 +1,142 @@ +package terminal + +import ( + "bytes" + "syscall" + "unsafe" +) + +var ( + dll = syscall.NewLazyDLL("kernel32.dll") + setConsoleMode = dll.NewProc("SetConsoleMode") + getConsoleMode = dll.NewProc("GetConsoleMode") + readConsoleInput = dll.NewProc("ReadConsoleInputW") +) + +const ( + EVENT_KEY = 0x0001 + + // key codes for arrow keys + // https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx + VK_DELETE = 0x2E + VK_END = 0x23 + VK_HOME = 0x24 + VK_LEFT = 0x25 + VK_UP = 0x26 + VK_RIGHT = 0x27 + VK_DOWN = 0x28 + + RIGHT_CTRL_PRESSED = 0x0004 + LEFT_CTRL_PRESSED = 0x0008 + + ENABLE_ECHO_INPUT uint32 = 0x0004 + ENABLE_LINE_INPUT uint32 = 0x0002 + ENABLE_PROCESSED_INPUT uint32 = 0x0001 +) + +type inputRecord struct { + eventType uint16 + padding uint16 + event [16]byte +} + +type keyEventRecord struct { + bKeyDown int32 + wRepeatCount uint16 + wVirtualKeyCode uint16 + wVirtualScanCode uint16 + unicodeChar uint16 + wdControlKeyState uint32 +} + +type runeReaderState struct { + term uint32 +} + +func newRuneReaderState(input FileReader) runeReaderState { + return runeReaderState{} +} + +func (rr *RuneReader) Buffer() *bytes.Buffer { + return nil +} + +func (rr *RuneReader) SetTermMode() error { + r, _, err := getConsoleMode.Call(uintptr(rr.stdio.In.Fd()), uintptr(unsafe.Pointer(&rr.state.term))) + // windows return 0 on error + if r == 0 { + return err + } + + newState := rr.state.term + newState &^= ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT + r, _, err = setConsoleMode.Call(uintptr(rr.stdio.In.Fd()), uintptr(newState)) + // windows return 0 on error + if r == 0 { + return err + } + return nil +} + +func (rr *RuneReader) RestoreTermMode() error { + r, _, err := setConsoleMode.Call(uintptr(rr.stdio.In.Fd()), uintptr(rr.state.term)) + // windows return 0 on error + if r == 0 { + return err + } + return nil +} + +func (rr *RuneReader) ReadRune() (rune, int, error) { + ir := &inputRecord{} + bytesRead := 0 + for { + rv, _, e := readConsoleInput.Call(rr.stdio.In.Fd(), uintptr(unsafe.Pointer(ir)), 1, uintptr(unsafe.Pointer(&bytesRead))) + // windows returns non-zero to indicate success + if rv == 0 && e != nil { + return 0, 0, e + } + + if ir.eventType != EVENT_KEY { + continue + } + + // the event data is really a c struct union, so here we have to do an usafe + // cast to put the data into the keyEventRecord (since we have already verified + // above that this event does correspond to a key event + key := (*keyEventRecord)(unsafe.Pointer(&ir.event[0])) + // we only care about key down events + if key.bKeyDown == 0 { + continue + } + if key.wdControlKeyState&(LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED) != 0 && key.unicodeChar == 'C' { + return KeyInterrupt, bytesRead, nil + } + // not a normal character so look up the input sequence from the + // virtual key code mappings (VK_*) + if key.unicodeChar == 0 { + switch key.wVirtualKeyCode { + case VK_DOWN: + return KeyArrowDown, bytesRead, nil + case VK_LEFT: + return KeyArrowLeft, bytesRead, nil + case VK_RIGHT: + return KeyArrowRight, bytesRead, nil + case VK_UP: + return KeyArrowUp, bytesRead, nil + case VK_DELETE: + return SpecialKeyDelete, bytesRead, nil + case VK_HOME: + return SpecialKeyHome, bytesRead, nil + case VK_END: + return SpecialKeyEnd, bytesRead, nil + default: + // not a virtual key that we care about so just continue on to + // the next input key + continue + } + } + r := rune(key.unicodeChar) + return r, bytesRead, nil + } +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/sequences.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/sequences.go new file mode 100644 index 0000000000..6d9e87755b --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/sequences.go @@ -0,0 +1,31 @@ +package terminal + +import ( + "fmt" + "io" +) + +const ( + KeyArrowLeft = '\x02' + KeyArrowRight = '\x06' + KeyArrowUp = '\x10' + KeyArrowDown = '\x0e' + KeySpace = ' ' + KeyEnter = '\r' + KeyBackspace = '\b' + KeyDelete = '\x7f' + KeyInterrupt = '\x03' + KeyEndTransmission = '\x04' + KeyEscape = '\x1b' + KeyDeleteWord = '\x17' // Ctrl+W + KeyDeleteLine = '\x18' // Ctrl+X + SpecialKeyHome = '\x01' + SpecialKeyEnd = '\x11' + SpecialKeyDelete = '\x12' + IgnoreKey = '\000' + KeyTab = '\t' +) + +func soundBell(out io.Writer) { + fmt.Fprint(out, "\a") +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/stdio.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/stdio.go new file mode 100644 index 0000000000..88bf7c4ea7 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/stdio.go @@ -0,0 +1,24 @@ +package terminal + +import ( + "io" +) + +// Stdio is the standard input/output the terminal reads/writes with. +type Stdio struct { + In FileReader + Out FileWriter + Err io.Writer +} + +// FileWriter provides a minimal interface for Stdin. +type FileWriter interface { + io.Writer + Fd() uintptr +} + +// FileReader provides a minimal interface for Stdout. +type FileReader interface { + io.Reader + Fd() uintptr +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/syscall_windows.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/syscall_windows.go new file mode 100644 index 0000000000..63b85d4c39 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/syscall_windows.go @@ -0,0 +1,39 @@ +package terminal + +import ( + "syscall" +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") + procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") + procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") + procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") + procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") +) + +type wchar uint16 +type dword uint32 +type word uint16 + +type smallRect struct { + left Short + top Short + right Short + bottom Short +} + +type consoleScreenBufferInfo struct { + size Coord + cursorPosition Coord + attributes word + window smallRect + maximumWindowSize Coord +} + +type consoleCursorInfo struct { + size dword + visible int32 +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/terminal/terminal.go b/vendor/github.com/AlecAivazis/survey/v2/terminal/terminal.go new file mode 100644 index 0000000000..4b59062c7d --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/terminal/terminal.go @@ -0,0 +1,8 @@ +package terminal + +type Short int16 + +type Coord struct { + X Short + Y Short +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/transform.go b/vendor/github.com/AlecAivazis/survey/v2/transform.go new file mode 100644 index 0000000000..58d5193b03 --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/transform.go @@ -0,0 +1,79 @@ +package survey + +import ( + "reflect" + "strings" +) + +// TransformString returns a `Transformer` based on the "f" +// function which accepts a string representation of the answer +// and returns a new one, transformed, answer. +// Take for example the functions inside the std `strings` package, +// they can be converted to a compatible `Transformer` by using this function, +// i.e: `TransformString(strings.Title)`, `TransformString(strings.ToUpper)`. +// +// Note that `TransformString` is just a helper, `Transformer` can be used +// to transform any type of answer. +func TransformString(f func(s string) string) Transformer { + return func(ans interface{}) interface{} { + // if the answer value passed in is the zero value of the appropriate type + if isZero(reflect.ValueOf(ans)) { + // skip this `Transformer` by returning a zero value of string. + // The original answer will be not affected, + // see survey.go#L125. + // A zero value of string should be returned to be handled by + // next Transformer in a composed Tranformer, + // see tranform.go#L75 + return "" + } + + // "ans" is never nil here, so we don't have to check that + // see survey.go#L338 for more. + // Make sure that the the answer's value was a typeof string. + s, ok := ans.(string) + if !ok { + return "" + } + + return f(s) + } +} + +// ToLower is a `Transformer`. +// It receives an answer value +// and returns a copy of the "ans" +// with all Unicode letters mapped to their lower case. +// +// Note that if "ans" is not a string then it will +// return a nil value, meaning that the above answer +// will not be affected by this call at all. +func ToLower(ans interface{}) interface{} { + transformer := TransformString(strings.ToLower) + return transformer(ans) +} + +// Title is a `Transformer`. +// It receives an answer value +// and returns a copy of the "ans" +// with all Unicode letters that begin words +// mapped to their title case. +// +// Note that if "ans" is not a string then it will +// return a nil value, meaning that the above answer +// will not be affected by this call at all. +func Title(ans interface{}) interface{} { + transformer := TransformString(strings.Title) + return transformer(ans) +} + +// ComposeTransformers is a variadic function used to create one transformer from many. +func ComposeTransformers(transformers ...Transformer) Transformer { + // return a transformer that calls each one sequentially + return func(ans interface{}) interface{} { + // execute each transformer + for _, t := range transformers { + ans = t(ans) + } + return ans + } +} diff --git a/vendor/github.com/AlecAivazis/survey/v2/validate.go b/vendor/github.com/AlecAivazis/survey/v2/validate.go new file mode 100644 index 0000000000..462dc5e75c --- /dev/null +++ b/vendor/github.com/AlecAivazis/survey/v2/validate.go @@ -0,0 +1,127 @@ +package survey + +import ( + "errors" + "fmt" + "reflect" + + "github.com/AlecAivazis/survey/v2/core" +) + +// Required does not allow an empty value +func Required(val interface{}) error { + // the reflect value of the result + value := reflect.ValueOf(val) + + // if the value passed in is the zero value of the appropriate type + if isZero(value) && value.Kind() != reflect.Bool { + return errors.New("Value is required") + } + return nil +} + +// MaxLength requires that the string is no longer than the specified value +func MaxLength(length int) Validator { + // return a validator that checks the length of the string + return func(val interface{}) error { + if str, ok := val.(string); ok { + // if the string is longer than the given value + if len([]rune(str)) > length { + // yell loudly + return fmt.Errorf("value is too long. Max length is %v", length) + } + } else { + // otherwise we cannot convert the value into a string and cannot enforce length + return fmt.Errorf("cannot enforce length on response of type %v", reflect.TypeOf(val).Name()) + } + + // the input is fine + return nil + } +} + +// MinLength requires that the string is longer or equal in length to the specified value +func MinLength(length int) Validator { + // return a validator that checks the length of the string + return func(val interface{}) error { + if str, ok := val.(string); ok { + // if the string is shorter than the given value + if len([]rune(str)) < length { + // yell loudly + return fmt.Errorf("value is too short. Min length is %v", length) + } + } else { + // otherwise we cannot convert the value into a string and cannot enforce length + return fmt.Errorf("cannot enforce length on response of type %v", reflect.TypeOf(val).Name()) + } + + // the input is fine + return nil + } +} + +// MaxItems requires that the list is no longer than the specified value +func MaxItems(numberItems int) Validator { + // return a validator that checks the length of the list + return func(val interface{}) error { + if list, ok := val.([]core.OptionAnswer); ok { + // if the list is longer than the given value + if len(list) > numberItems { + // yell loudly + return fmt.Errorf("value is too long. Max items is %v", numberItems) + } + } else { + // otherwise we cannot convert the value into a list of answer and cannot enforce length + return fmt.Errorf("cannot impose the length on something other than a list of answers") + } + // the input is fine + return nil + } +} + +// MinItems requires that the list is longer or equal in length to the specified value +func MinItems(numberItems int) Validator { + // return a validator that checks the length of the list + return func(val interface{}) error { + if list, ok := val.([]core.OptionAnswer); ok { + // if the list is shorter than the given value + if len(list) < numberItems { + // yell loudly + return fmt.Errorf("value is too long. Min items is %v", numberItems) + } + } else { + // otherwise we cannot convert the value into a list of answer and cannot enforce length + return fmt.Errorf("cannot impose the length on something other than a list of answers") + } + // the input is fine + return nil + } +} + +// ComposeValidators is a variadic function used to create one validator from many. +func ComposeValidators(validators ...Validator) Validator { + // return a validator that calls each one sequentially + return func(val interface{}) error { + // execute each validator + for _, validator := range validators { + // if the answer's value is not valid + if err := validator(val); err != nil { + // return the error + return err + } + } + // we passed all validators, the answer is valid + return nil + } +} + +// isZero returns true if the passed value is the zero object +func isZero(v reflect.Value) bool { + switch v.Kind() { + case reflect.Slice, reflect.Map: + return v.Len() == 0 + } + + // compare the types directly with more general coverage + return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) +} diff --git a/vendor/github.com/Azure/go-ansiterm/LICENSE b/vendor/github.com/Azure/go-ansiterm/LICENSE new file mode 100644 index 0000000000..e3d9a64d1d --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/Azure/go-ansiterm/README.md b/vendor/github.com/Azure/go-ansiterm/README.md new file mode 100644 index 0000000000..261c041e7a --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/README.md @@ -0,0 +1,12 @@ +# go-ansiterm + +This is a cross platform Ansi Terminal Emulation library. It reads a stream of Ansi characters and produces the appropriate function calls. The results of the function calls are platform dependent. + +For example the parser might receive "ESC, [, A" as a stream of three characters. This is the code for Cursor Up (http://www.vt100.net/docs/vt510-rm/CUU). The parser then calls the cursor up function (CUU()) on an event handler. The event handler determines what platform specific work must be done to cause the cursor to move up one position. + +The parser (parser.go) is a partial implementation of this state machine (http://vt100.net/emu/vt500_parser.png). There are also two event handler implementations, one for tests (test_event_handler.go) to validate that the expected events are being produced and called, the other is a Windows implementation (winterm/win_event_handler.go). + +See parser_test.go for examples exercising the state machine and generating appropriate function calls. + +----- +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/vendor/github.com/Azure/go-ansiterm/constants.go b/vendor/github.com/Azure/go-ansiterm/constants.go new file mode 100644 index 0000000000..96504a33bc --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/constants.go @@ -0,0 +1,188 @@ +package ansiterm + +const LogEnv = "DEBUG_TERMINAL" + +// ANSI constants +// References: +// -- http://www.ecma-international.org/publications/standards/Ecma-048.htm +// -- http://man7.org/linux/man-pages/man4/console_codes.4.html +// -- http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html +// -- http://en.wikipedia.org/wiki/ANSI_escape_code +// -- http://vt100.net/emu/dec_ansi_parser +// -- http://vt100.net/emu/vt500_parser.svg +// -- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html +// -- http://www.inwap.com/pdp10/ansicode.txt +const ( + // ECMA-48 Set Graphics Rendition + // Note: + // -- Constants leading with an underscore (e.g., _ANSI_xxx) are unsupported or reserved + // -- Fonts could possibly be supported via SetCurrentConsoleFontEx + // -- Windows does not expose the per-window cursor (i.e., caret) blink times + ANSI_SGR_RESET = 0 + ANSI_SGR_BOLD = 1 + ANSI_SGR_DIM = 2 + _ANSI_SGR_ITALIC = 3 + ANSI_SGR_UNDERLINE = 4 + _ANSI_SGR_BLINKSLOW = 5 + _ANSI_SGR_BLINKFAST = 6 + ANSI_SGR_REVERSE = 7 + _ANSI_SGR_INVISIBLE = 8 + _ANSI_SGR_LINETHROUGH = 9 + _ANSI_SGR_FONT_00 = 10 + _ANSI_SGR_FONT_01 = 11 + _ANSI_SGR_FONT_02 = 12 + _ANSI_SGR_FONT_03 = 13 + _ANSI_SGR_FONT_04 = 14 + _ANSI_SGR_FONT_05 = 15 + _ANSI_SGR_FONT_06 = 16 + _ANSI_SGR_FONT_07 = 17 + _ANSI_SGR_FONT_08 = 18 + _ANSI_SGR_FONT_09 = 19 + _ANSI_SGR_FONT_10 = 20 + _ANSI_SGR_DOUBLEUNDERLINE = 21 + ANSI_SGR_BOLD_DIM_OFF = 22 + _ANSI_SGR_ITALIC_OFF = 23 + ANSI_SGR_UNDERLINE_OFF = 24 + _ANSI_SGR_BLINK_OFF = 25 + _ANSI_SGR_RESERVED_00 = 26 + ANSI_SGR_REVERSE_OFF = 27 + _ANSI_SGR_INVISIBLE_OFF = 28 + _ANSI_SGR_LINETHROUGH_OFF = 29 + ANSI_SGR_FOREGROUND_BLACK = 30 + ANSI_SGR_FOREGROUND_RED = 31 + ANSI_SGR_FOREGROUND_GREEN = 32 + ANSI_SGR_FOREGROUND_YELLOW = 33 + ANSI_SGR_FOREGROUND_BLUE = 34 + ANSI_SGR_FOREGROUND_MAGENTA = 35 + ANSI_SGR_FOREGROUND_CYAN = 36 + ANSI_SGR_FOREGROUND_WHITE = 37 + _ANSI_SGR_RESERVED_01 = 38 + ANSI_SGR_FOREGROUND_DEFAULT = 39 + ANSI_SGR_BACKGROUND_BLACK = 40 + ANSI_SGR_BACKGROUND_RED = 41 + ANSI_SGR_BACKGROUND_GREEN = 42 + ANSI_SGR_BACKGROUND_YELLOW = 43 + ANSI_SGR_BACKGROUND_BLUE = 44 + ANSI_SGR_BACKGROUND_MAGENTA = 45 + ANSI_SGR_BACKGROUND_CYAN = 46 + ANSI_SGR_BACKGROUND_WHITE = 47 + _ANSI_SGR_RESERVED_02 = 48 + ANSI_SGR_BACKGROUND_DEFAULT = 49 + // 50 - 65: Unsupported + + ANSI_MAX_CMD_LENGTH = 4096 + + MAX_INPUT_EVENTS = 128 + DEFAULT_WIDTH = 80 + DEFAULT_HEIGHT = 24 + + ANSI_BEL = 0x07 + ANSI_BACKSPACE = 0x08 + ANSI_TAB = 0x09 + ANSI_LINE_FEED = 0x0A + ANSI_VERTICAL_TAB = 0x0B + ANSI_FORM_FEED = 0x0C + ANSI_CARRIAGE_RETURN = 0x0D + ANSI_ESCAPE_PRIMARY = 0x1B + ANSI_ESCAPE_SECONDARY = 0x5B + ANSI_OSC_STRING_ENTRY = 0x5D + ANSI_COMMAND_FIRST = 0x40 + ANSI_COMMAND_LAST = 0x7E + DCS_ENTRY = 0x90 + CSI_ENTRY = 0x9B + OSC_STRING = 0x9D + ANSI_PARAMETER_SEP = ";" + ANSI_CMD_G0 = '(' + ANSI_CMD_G1 = ')' + ANSI_CMD_G2 = '*' + ANSI_CMD_G3 = '+' + ANSI_CMD_DECPNM = '>' + ANSI_CMD_DECPAM = '=' + ANSI_CMD_OSC = ']' + ANSI_CMD_STR_TERM = '\\' + + KEY_CONTROL_PARAM_2 = ";2" + KEY_CONTROL_PARAM_3 = ";3" + KEY_CONTROL_PARAM_4 = ";4" + KEY_CONTROL_PARAM_5 = ";5" + KEY_CONTROL_PARAM_6 = ";6" + KEY_CONTROL_PARAM_7 = ";7" + KEY_CONTROL_PARAM_8 = ";8" + KEY_ESC_CSI = "\x1B[" + KEY_ESC_N = "\x1BN" + KEY_ESC_O = "\x1BO" + + FILL_CHARACTER = ' ' +) + +func getByteRange(start byte, end byte) []byte { + bytes := make([]byte, 0, 32) + for i := start; i <= end; i++ { + bytes = append(bytes, byte(i)) + } + + return bytes +} + +var toGroundBytes = getToGroundBytes() +var executors = getExecuteBytes() + +// SPACE 20+A0 hex Always and everywhere a blank space +// Intermediate 20-2F hex !"#$%&'()*+,-./ +var intermeds = getByteRange(0x20, 0x2F) + +// Parameters 30-3F hex 0123456789:;<=>? +// CSI Parameters 30-39, 3B hex 0123456789; +var csiParams = getByteRange(0x30, 0x3F) + +var csiCollectables = append(getByteRange(0x30, 0x39), getByteRange(0x3B, 0x3F)...) + +// Uppercase 40-5F hex @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ +var upperCase = getByteRange(0x40, 0x5F) + +// Lowercase 60-7E hex `abcdefghijlkmnopqrstuvwxyz{|}~ +var lowerCase = getByteRange(0x60, 0x7E) + +// Alphabetics 40-7E hex (all of upper and lower case) +var alphabetics = append(upperCase, lowerCase...) + +var printables = getByteRange(0x20, 0x7F) + +var escapeIntermediateToGroundBytes = getByteRange(0x30, 0x7E) +var escapeToGroundBytes = getEscapeToGroundBytes() + +// See http://www.vt100.net/emu/vt500_parser.png for description of the complex +// byte ranges below + +func getEscapeToGroundBytes() []byte { + escapeToGroundBytes := getByteRange(0x30, 0x4F) + escapeToGroundBytes = append(escapeToGroundBytes, getByteRange(0x51, 0x57)...) + escapeToGroundBytes = append(escapeToGroundBytes, 0x59) + escapeToGroundBytes = append(escapeToGroundBytes, 0x5A) + escapeToGroundBytes = append(escapeToGroundBytes, 0x5C) + escapeToGroundBytes = append(escapeToGroundBytes, getByteRange(0x60, 0x7E)...) + return escapeToGroundBytes +} + +func getExecuteBytes() []byte { + executeBytes := getByteRange(0x00, 0x17) + executeBytes = append(executeBytes, 0x19) + executeBytes = append(executeBytes, getByteRange(0x1C, 0x1F)...) + return executeBytes +} + +func getToGroundBytes() []byte { + groundBytes := []byte{0x18} + groundBytes = append(groundBytes, 0x1A) + groundBytes = append(groundBytes, getByteRange(0x80, 0x8F)...) + groundBytes = append(groundBytes, getByteRange(0x91, 0x97)...) + groundBytes = append(groundBytes, 0x99) + groundBytes = append(groundBytes, 0x9A) + groundBytes = append(groundBytes, 0x9C) + return groundBytes +} + +// Delete 7F hex Always and everywhere ignored +// C1 Control 80-9F hex 32 additional control characters +// G1 Displayable A1-FE hex 94 additional displayable characters +// Special A0+FF hex Same as SPACE and DELETE diff --git a/vendor/github.com/Azure/go-ansiterm/context.go b/vendor/github.com/Azure/go-ansiterm/context.go new file mode 100644 index 0000000000..8d66e777c0 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/context.go @@ -0,0 +1,7 @@ +package ansiterm + +type ansiContext struct { + currentChar byte + paramBuffer []byte + interBuffer []byte +} diff --git a/vendor/github.com/Azure/go-ansiterm/csi_entry_state.go b/vendor/github.com/Azure/go-ansiterm/csi_entry_state.go new file mode 100644 index 0000000000..bcbe00d0c5 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/csi_entry_state.go @@ -0,0 +1,49 @@ +package ansiterm + +type csiEntryState struct { + baseState +} + +func (csiState csiEntryState) Handle(b byte) (s state, e error) { + csiState.parser.logf("CsiEntry::Handle %#x", b) + + nextState, err := csiState.baseState.Handle(b) + if nextState != nil || err != nil { + return nextState, err + } + + switch { + case sliceContains(alphabetics, b): + return csiState.parser.ground, nil + case sliceContains(csiCollectables, b): + return csiState.parser.csiParam, nil + case sliceContains(executors, b): + return csiState, csiState.parser.execute() + } + + return csiState, nil +} + +func (csiState csiEntryState) Transition(s state) error { + csiState.parser.logf("CsiEntry::Transition %s --> %s", csiState.Name(), s.Name()) + csiState.baseState.Transition(s) + + switch s { + case csiState.parser.ground: + return csiState.parser.csiDispatch() + case csiState.parser.csiParam: + switch { + case sliceContains(csiParams, csiState.parser.context.currentChar): + csiState.parser.collectParam() + case sliceContains(intermeds, csiState.parser.context.currentChar): + csiState.parser.collectInter() + } + } + + return nil +} + +func (csiState csiEntryState) Enter() error { + csiState.parser.clear() + return nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/csi_param_state.go b/vendor/github.com/Azure/go-ansiterm/csi_param_state.go new file mode 100644 index 0000000000..7ed5e01c34 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/csi_param_state.go @@ -0,0 +1,38 @@ +package ansiterm + +type csiParamState struct { + baseState +} + +func (csiState csiParamState) Handle(b byte) (s state, e error) { + csiState.parser.logf("CsiParam::Handle %#x", b) + + nextState, err := csiState.baseState.Handle(b) + if nextState != nil || err != nil { + return nextState, err + } + + switch { + case sliceContains(alphabetics, b): + return csiState.parser.ground, nil + case sliceContains(csiCollectables, b): + csiState.parser.collectParam() + return csiState, nil + case sliceContains(executors, b): + return csiState, csiState.parser.execute() + } + + return csiState, nil +} + +func (csiState csiParamState) Transition(s state) error { + csiState.parser.logf("CsiParam::Transition %s --> %s", csiState.Name(), s.Name()) + csiState.baseState.Transition(s) + + switch s { + case csiState.parser.ground: + return csiState.parser.csiDispatch() + } + + return nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/escape_intermediate_state.go b/vendor/github.com/Azure/go-ansiterm/escape_intermediate_state.go new file mode 100644 index 0000000000..1c719db9e4 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/escape_intermediate_state.go @@ -0,0 +1,36 @@ +package ansiterm + +type escapeIntermediateState struct { + baseState +} + +func (escState escapeIntermediateState) Handle(b byte) (s state, e error) { + escState.parser.logf("escapeIntermediateState::Handle %#x", b) + nextState, err := escState.baseState.Handle(b) + if nextState != nil || err != nil { + return nextState, err + } + + switch { + case sliceContains(intermeds, b): + return escState, escState.parser.collectInter() + case sliceContains(executors, b): + return escState, escState.parser.execute() + case sliceContains(escapeIntermediateToGroundBytes, b): + return escState.parser.ground, nil + } + + return escState, nil +} + +func (escState escapeIntermediateState) Transition(s state) error { + escState.parser.logf("escapeIntermediateState::Transition %s --> %s", escState.Name(), s.Name()) + escState.baseState.Transition(s) + + switch s { + case escState.parser.ground: + return escState.parser.escDispatch() + } + + return nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/escape_state.go b/vendor/github.com/Azure/go-ansiterm/escape_state.go new file mode 100644 index 0000000000..6390abd231 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/escape_state.go @@ -0,0 +1,47 @@ +package ansiterm + +type escapeState struct { + baseState +} + +func (escState escapeState) Handle(b byte) (s state, e error) { + escState.parser.logf("escapeState::Handle %#x", b) + nextState, err := escState.baseState.Handle(b) + if nextState != nil || err != nil { + return nextState, err + } + + switch { + case b == ANSI_ESCAPE_SECONDARY: + return escState.parser.csiEntry, nil + case b == ANSI_OSC_STRING_ENTRY: + return escState.parser.oscString, nil + case sliceContains(executors, b): + return escState, escState.parser.execute() + case sliceContains(escapeToGroundBytes, b): + return escState.parser.ground, nil + case sliceContains(intermeds, b): + return escState.parser.escapeIntermediate, nil + } + + return escState, nil +} + +func (escState escapeState) Transition(s state) error { + escState.parser.logf("Escape::Transition %s --> %s", escState.Name(), s.Name()) + escState.baseState.Transition(s) + + switch s { + case escState.parser.ground: + return escState.parser.escDispatch() + case escState.parser.escapeIntermediate: + return escState.parser.collectInter() + } + + return nil +} + +func (escState escapeState) Enter() error { + escState.parser.clear() + return nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/event_handler.go b/vendor/github.com/Azure/go-ansiterm/event_handler.go new file mode 100644 index 0000000000..98087b38c2 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/event_handler.go @@ -0,0 +1,90 @@ +package ansiterm + +type AnsiEventHandler interface { + // Print + Print(b byte) error + + // Execute C0 commands + Execute(b byte) error + + // CUrsor Up + CUU(int) error + + // CUrsor Down + CUD(int) error + + // CUrsor Forward + CUF(int) error + + // CUrsor Backward + CUB(int) error + + // Cursor to Next Line + CNL(int) error + + // Cursor to Previous Line + CPL(int) error + + // Cursor Horizontal position Absolute + CHA(int) error + + // Vertical line Position Absolute + VPA(int) error + + // CUrsor Position + CUP(int, int) error + + // Horizontal and Vertical Position (depends on PUM) + HVP(int, int) error + + // Text Cursor Enable Mode + DECTCEM(bool) error + + // Origin Mode + DECOM(bool) error + + // 132 Column Mode + DECCOLM(bool) error + + // Erase in Display + ED(int) error + + // Erase in Line + EL(int) error + + // Insert Line + IL(int) error + + // Delete Line + DL(int) error + + // Insert Character + ICH(int) error + + // Delete Character + DCH(int) error + + // Set Graphics Rendition + SGR([]int) error + + // Pan Down + SU(int) error + + // Pan Up + SD(int) error + + // Device Attributes + DA([]string) error + + // Set Top and Bottom Margins + DECSTBM(int, int) error + + // Index + IND() error + + // Reverse Index + RI() error + + // Flush updates from previous commands + Flush() error +} diff --git a/vendor/github.com/Azure/go-ansiterm/go.mod b/vendor/github.com/Azure/go-ansiterm/go.mod new file mode 100644 index 0000000000..965cb8120e --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/go.mod @@ -0,0 +1,5 @@ +module github.com/Azure/go-ansiterm + +go 1.16 + +require golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 diff --git a/vendor/github.com/Azure/go-ansiterm/go.sum b/vendor/github.com/Azure/go-ansiterm/go.sum new file mode 100644 index 0000000000..9f05d9d3ed --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/vendor/github.com/Azure/go-ansiterm/ground_state.go b/vendor/github.com/Azure/go-ansiterm/ground_state.go new file mode 100644 index 0000000000..52451e9469 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/ground_state.go @@ -0,0 +1,24 @@ +package ansiterm + +type groundState struct { + baseState +} + +func (gs groundState) Handle(b byte) (s state, e error) { + gs.parser.context.currentChar = b + + nextState, err := gs.baseState.Handle(b) + if nextState != nil || err != nil { + return nextState, err + } + + switch { + case sliceContains(printables, b): + return gs, gs.parser.print() + + case sliceContains(executors, b): + return gs, gs.parser.execute() + } + + return gs, nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/osc_string_state.go b/vendor/github.com/Azure/go-ansiterm/osc_string_state.go new file mode 100644 index 0000000000..593b10ab69 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/osc_string_state.go @@ -0,0 +1,31 @@ +package ansiterm + +type oscStringState struct { + baseState +} + +func (oscState oscStringState) Handle(b byte) (s state, e error) { + oscState.parser.logf("OscString::Handle %#x", b) + nextState, err := oscState.baseState.Handle(b) + if nextState != nil || err != nil { + return nextState, err + } + + switch { + case isOscStringTerminator(b): + return oscState.parser.ground, nil + } + + return oscState, nil +} + +// See below for OSC string terminators for linux +// http://man7.org/linux/man-pages/man4/console_codes.4.html +func isOscStringTerminator(b byte) bool { + + if b == ANSI_BEL || b == 0x5C { + return true + } + + return false +} diff --git a/vendor/github.com/Azure/go-ansiterm/parser.go b/vendor/github.com/Azure/go-ansiterm/parser.go new file mode 100644 index 0000000000..03cec7ada6 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/parser.go @@ -0,0 +1,151 @@ +package ansiterm + +import ( + "errors" + "log" + "os" +) + +type AnsiParser struct { + currState state + eventHandler AnsiEventHandler + context *ansiContext + csiEntry state + csiParam state + dcsEntry state + escape state + escapeIntermediate state + error state + ground state + oscString state + stateMap []state + + logf func(string, ...interface{}) +} + +type Option func(*AnsiParser) + +func WithLogf(f func(string, ...interface{})) Option { + return func(ap *AnsiParser) { + ap.logf = f + } +} + +func CreateParser(initialState string, evtHandler AnsiEventHandler, opts ...Option) *AnsiParser { + ap := &AnsiParser{ + eventHandler: evtHandler, + context: &ansiContext{}, + } + for _, o := range opts { + o(ap) + } + + if isDebugEnv := os.Getenv(LogEnv); isDebugEnv == "1" { + logFile, _ := os.Create("ansiParser.log") + logger := log.New(logFile, "", log.LstdFlags) + if ap.logf != nil { + l := ap.logf + ap.logf = func(s string, v ...interface{}) { + l(s, v...) + logger.Printf(s, v...) + } + } else { + ap.logf = logger.Printf + } + } + + if ap.logf == nil { + ap.logf = func(string, ...interface{}) {} + } + + ap.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: ap}} + ap.csiParam = csiParamState{baseState{name: "CsiParam", parser: ap}} + ap.dcsEntry = dcsEntryState{baseState{name: "DcsEntry", parser: ap}} + ap.escape = escapeState{baseState{name: "Escape", parser: ap}} + ap.escapeIntermediate = escapeIntermediateState{baseState{name: "EscapeIntermediate", parser: ap}} + ap.error = errorState{baseState{name: "Error", parser: ap}} + ap.ground = groundState{baseState{name: "Ground", parser: ap}} + ap.oscString = oscStringState{baseState{name: "OscString", parser: ap}} + + ap.stateMap = []state{ + ap.csiEntry, + ap.csiParam, + ap.dcsEntry, + ap.escape, + ap.escapeIntermediate, + ap.error, + ap.ground, + ap.oscString, + } + + ap.currState = getState(initialState, ap.stateMap) + + ap.logf("CreateParser: parser %p", ap) + return ap +} + +func getState(name string, states []state) state { + for _, el := range states { + if el.Name() == name { + return el + } + } + + return nil +} + +func (ap *AnsiParser) Parse(bytes []byte) (int, error) { + for i, b := range bytes { + if err := ap.handle(b); err != nil { + return i, err + } + } + + return len(bytes), ap.eventHandler.Flush() +} + +func (ap *AnsiParser) handle(b byte) error { + ap.context.currentChar = b + newState, err := ap.currState.Handle(b) + if err != nil { + return err + } + + if newState == nil { + ap.logf("WARNING: newState is nil") + return errors.New("New state of 'nil' is invalid.") + } + + if newState != ap.currState { + if err := ap.changeState(newState); err != nil { + return err + } + } + + return nil +} + +func (ap *AnsiParser) changeState(newState state) error { + ap.logf("ChangeState %s --> %s", ap.currState.Name(), newState.Name()) + + // Exit old state + if err := ap.currState.Exit(); err != nil { + ap.logf("Exit state '%s' failed with : '%v'", ap.currState.Name(), err) + return err + } + + // Perform transition action + if err := ap.currState.Transition(newState); err != nil { + ap.logf("Transition from '%s' to '%s' failed with: '%v'", ap.currState.Name(), newState.Name, err) + return err + } + + // Enter new state + if err := newState.Enter(); err != nil { + ap.logf("Enter state '%s' failed with: '%v'", newState.Name(), err) + return err + } + + ap.currState = newState + return nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/parser_action_helpers.go b/vendor/github.com/Azure/go-ansiterm/parser_action_helpers.go new file mode 100644 index 0000000000..de0a1f9cde --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/parser_action_helpers.go @@ -0,0 +1,99 @@ +package ansiterm + +import ( + "strconv" +) + +func parseParams(bytes []byte) ([]string, error) { + paramBuff := make([]byte, 0, 0) + params := []string{} + + for _, v := range bytes { + if v == ';' { + if len(paramBuff) > 0 { + // Completed parameter, append it to the list + s := string(paramBuff) + params = append(params, s) + paramBuff = make([]byte, 0, 0) + } + } else { + paramBuff = append(paramBuff, v) + } + } + + // Last parameter may not be terminated with ';' + if len(paramBuff) > 0 { + s := string(paramBuff) + params = append(params, s) + } + + return params, nil +} + +func parseCmd(context ansiContext) (string, error) { + return string(context.currentChar), nil +} + +func getInt(params []string, dflt int) int { + i := getInts(params, 1, dflt)[0] + return i +} + +func getInts(params []string, minCount int, dflt int) []int { + ints := []int{} + + for _, v := range params { + i, _ := strconv.Atoi(v) + // Zero is mapped to the default value in VT100. + if i == 0 { + i = dflt + } + ints = append(ints, i) + } + + if len(ints) < minCount { + remaining := minCount - len(ints) + for i := 0; i < remaining; i++ { + ints = append(ints, dflt) + } + } + + return ints +} + +func (ap *AnsiParser) modeDispatch(param string, set bool) error { + switch param { + case "?3": + return ap.eventHandler.DECCOLM(set) + case "?6": + return ap.eventHandler.DECOM(set) + case "?25": + return ap.eventHandler.DECTCEM(set) + } + return nil +} + +func (ap *AnsiParser) hDispatch(params []string) error { + if len(params) == 1 { + return ap.modeDispatch(params[0], true) + } + + return nil +} + +func (ap *AnsiParser) lDispatch(params []string) error { + if len(params) == 1 { + return ap.modeDispatch(params[0], false) + } + + return nil +} + +func getEraseParam(params []string) int { + param := getInt(params, 0) + if param < 0 || 3 < param { + param = 0 + } + + return param +} diff --git a/vendor/github.com/Azure/go-ansiterm/parser_actions.go b/vendor/github.com/Azure/go-ansiterm/parser_actions.go new file mode 100644 index 0000000000..0bb5e51e9a --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/parser_actions.go @@ -0,0 +1,119 @@ +package ansiterm + +func (ap *AnsiParser) collectParam() error { + currChar := ap.context.currentChar + ap.logf("collectParam %#x", currChar) + ap.context.paramBuffer = append(ap.context.paramBuffer, currChar) + return nil +} + +func (ap *AnsiParser) collectInter() error { + currChar := ap.context.currentChar + ap.logf("collectInter %#x", currChar) + ap.context.paramBuffer = append(ap.context.interBuffer, currChar) + return nil +} + +func (ap *AnsiParser) escDispatch() error { + cmd, _ := parseCmd(*ap.context) + intermeds := ap.context.interBuffer + ap.logf("escDispatch currentChar: %#x", ap.context.currentChar) + ap.logf("escDispatch: %v(%v)", cmd, intermeds) + + switch cmd { + case "D": // IND + return ap.eventHandler.IND() + case "E": // NEL, equivalent to CRLF + err := ap.eventHandler.Execute(ANSI_CARRIAGE_RETURN) + if err == nil { + err = ap.eventHandler.Execute(ANSI_LINE_FEED) + } + return err + case "M": // RI + return ap.eventHandler.RI() + } + + return nil +} + +func (ap *AnsiParser) csiDispatch() error { + cmd, _ := parseCmd(*ap.context) + params, _ := parseParams(ap.context.paramBuffer) + ap.logf("Parsed params: %v with length: %d", params, len(params)) + + ap.logf("csiDispatch: %v(%v)", cmd, params) + + switch cmd { + case "@": + return ap.eventHandler.ICH(getInt(params, 1)) + case "A": + return ap.eventHandler.CUU(getInt(params, 1)) + case "B": + return ap.eventHandler.CUD(getInt(params, 1)) + case "C": + return ap.eventHandler.CUF(getInt(params, 1)) + case "D": + return ap.eventHandler.CUB(getInt(params, 1)) + case "E": + return ap.eventHandler.CNL(getInt(params, 1)) + case "F": + return ap.eventHandler.CPL(getInt(params, 1)) + case "G": + return ap.eventHandler.CHA(getInt(params, 1)) + case "H": + ints := getInts(params, 2, 1) + x, y := ints[0], ints[1] + return ap.eventHandler.CUP(x, y) + case "J": + param := getEraseParam(params) + return ap.eventHandler.ED(param) + case "K": + param := getEraseParam(params) + return ap.eventHandler.EL(param) + case "L": + return ap.eventHandler.IL(getInt(params, 1)) + case "M": + return ap.eventHandler.DL(getInt(params, 1)) + case "P": + return ap.eventHandler.DCH(getInt(params, 1)) + case "S": + return ap.eventHandler.SU(getInt(params, 1)) + case "T": + return ap.eventHandler.SD(getInt(params, 1)) + case "c": + return ap.eventHandler.DA(params) + case "d": + return ap.eventHandler.VPA(getInt(params, 1)) + case "f": + ints := getInts(params, 2, 1) + x, y := ints[0], ints[1] + return ap.eventHandler.HVP(x, y) + case "h": + return ap.hDispatch(params) + case "l": + return ap.lDispatch(params) + case "m": + return ap.eventHandler.SGR(getInts(params, 1, 0)) + case "r": + ints := getInts(params, 2, 1) + top, bottom := ints[0], ints[1] + return ap.eventHandler.DECSTBM(top, bottom) + default: + ap.logf("ERROR: Unsupported CSI command: '%s', with full context: %v", cmd, ap.context) + return nil + } + +} + +func (ap *AnsiParser) print() error { + return ap.eventHandler.Print(ap.context.currentChar) +} + +func (ap *AnsiParser) clear() error { + ap.context = &ansiContext{} + return nil +} + +func (ap *AnsiParser) execute() error { + return ap.eventHandler.Execute(ap.context.currentChar) +} diff --git a/vendor/github.com/Azure/go-ansiterm/states.go b/vendor/github.com/Azure/go-ansiterm/states.go new file mode 100644 index 0000000000..f2ea1fcd12 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/states.go @@ -0,0 +1,71 @@ +package ansiterm + +type stateID int + +type state interface { + Enter() error + Exit() error + Handle(byte) (state, error) + Name() string + Transition(state) error +} + +type baseState struct { + name string + parser *AnsiParser +} + +func (base baseState) Enter() error { + return nil +} + +func (base baseState) Exit() error { + return nil +} + +func (base baseState) Handle(b byte) (s state, e error) { + + switch { + case b == CSI_ENTRY: + return base.parser.csiEntry, nil + case b == DCS_ENTRY: + return base.parser.dcsEntry, nil + case b == ANSI_ESCAPE_PRIMARY: + return base.parser.escape, nil + case b == OSC_STRING: + return base.parser.oscString, nil + case sliceContains(toGroundBytes, b): + return base.parser.ground, nil + } + + return nil, nil +} + +func (base baseState) Name() string { + return base.name +} + +func (base baseState) Transition(s state) error { + if s == base.parser.ground { + execBytes := []byte{0x18} + execBytes = append(execBytes, 0x1A) + execBytes = append(execBytes, getByteRange(0x80, 0x8F)...) + execBytes = append(execBytes, getByteRange(0x91, 0x97)...) + execBytes = append(execBytes, 0x99) + execBytes = append(execBytes, 0x9A) + + if sliceContains(execBytes, base.parser.context.currentChar) { + return base.parser.execute() + } + } + + return nil +} + +type dcsEntryState struct { + baseState +} + +type errorState struct { + baseState +} diff --git a/vendor/github.com/Azure/go-ansiterm/utilities.go b/vendor/github.com/Azure/go-ansiterm/utilities.go new file mode 100644 index 0000000000..392114493a --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/utilities.go @@ -0,0 +1,21 @@ +package ansiterm + +import ( + "strconv" +) + +func sliceContains(bytes []byte, b byte) bool { + for _, v := range bytes { + if v == b { + return true + } + } + + return false +} + +func convertBytesToInteger(bytes []byte) int { + s := string(bytes) + i, _ := strconv.Atoi(s) + return i +} diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/ansi.go b/vendor/github.com/Azure/go-ansiterm/winterm/ansi.go new file mode 100644 index 0000000000..5599082ae9 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/winterm/ansi.go @@ -0,0 +1,196 @@ +// +build windows + +package winterm + +import ( + "fmt" + "os" + "strconv" + "strings" + "syscall" + + "github.com/Azure/go-ansiterm" + windows "golang.org/x/sys/windows" +) + +// Windows keyboard constants +// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx. +const ( + VK_PRIOR = 0x21 // PAGE UP key + VK_NEXT = 0x22 // PAGE DOWN key + VK_END = 0x23 // END key + VK_HOME = 0x24 // HOME key + VK_LEFT = 0x25 // LEFT ARROW key + VK_UP = 0x26 // UP ARROW key + VK_RIGHT = 0x27 // RIGHT ARROW key + VK_DOWN = 0x28 // DOWN ARROW key + VK_SELECT = 0x29 // SELECT key + VK_PRINT = 0x2A // PRINT key + VK_EXECUTE = 0x2B // EXECUTE key + VK_SNAPSHOT = 0x2C // PRINT SCREEN key + VK_INSERT = 0x2D // INS key + VK_DELETE = 0x2E // DEL key + VK_HELP = 0x2F // HELP key + VK_F1 = 0x70 // F1 key + VK_F2 = 0x71 // F2 key + VK_F3 = 0x72 // F3 key + VK_F4 = 0x73 // F4 key + VK_F5 = 0x74 // F5 key + VK_F6 = 0x75 // F6 key + VK_F7 = 0x76 // F7 key + VK_F8 = 0x77 // F8 key + VK_F9 = 0x78 // F9 key + VK_F10 = 0x79 // F10 key + VK_F11 = 0x7A // F11 key + VK_F12 = 0x7B // F12 key + + RIGHT_ALT_PRESSED = 0x0001 + LEFT_ALT_PRESSED = 0x0002 + RIGHT_CTRL_PRESSED = 0x0004 + LEFT_CTRL_PRESSED = 0x0008 + SHIFT_PRESSED = 0x0010 + NUMLOCK_ON = 0x0020 + SCROLLLOCK_ON = 0x0040 + CAPSLOCK_ON = 0x0080 + ENHANCED_KEY = 0x0100 +) + +type ansiCommand struct { + CommandBytes []byte + Command string + Parameters []string + IsSpecial bool +} + +func newAnsiCommand(command []byte) *ansiCommand { + + if isCharacterSelectionCmdChar(command[1]) { + // Is Character Set Selection commands + return &ansiCommand{ + CommandBytes: command, + Command: string(command), + IsSpecial: true, + } + } + + // last char is command character + lastCharIndex := len(command) - 1 + + ac := &ansiCommand{ + CommandBytes: command, + Command: string(command[lastCharIndex]), + IsSpecial: false, + } + + // more than a single escape + if lastCharIndex != 0 { + start := 1 + // skip if double char escape sequence + if command[0] == ansiterm.ANSI_ESCAPE_PRIMARY && command[1] == ansiterm.ANSI_ESCAPE_SECONDARY { + start++ + } + // convert this to GetNextParam method + ac.Parameters = strings.Split(string(command[start:lastCharIndex]), ansiterm.ANSI_PARAMETER_SEP) + } + + return ac +} + +func (ac *ansiCommand) paramAsSHORT(index int, defaultValue int16) int16 { + if index < 0 || index >= len(ac.Parameters) { + return defaultValue + } + + param, err := strconv.ParseInt(ac.Parameters[index], 10, 16) + if err != nil { + return defaultValue + } + + return int16(param) +} + +func (ac *ansiCommand) String() string { + return fmt.Sprintf("0x%v \"%v\" (\"%v\")", + bytesToHex(ac.CommandBytes), + ac.Command, + strings.Join(ac.Parameters, "\",\"")) +} + +// isAnsiCommandChar returns true if the passed byte falls within the range of ANSI commands. +// See http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html. +func isAnsiCommandChar(b byte) bool { + switch { + case ansiterm.ANSI_COMMAND_FIRST <= b && b <= ansiterm.ANSI_COMMAND_LAST && b != ansiterm.ANSI_ESCAPE_SECONDARY: + return true + case b == ansiterm.ANSI_CMD_G1 || b == ansiterm.ANSI_CMD_OSC || b == ansiterm.ANSI_CMD_DECPAM || b == ansiterm.ANSI_CMD_DECPNM: + // non-CSI escape sequence terminator + return true + case b == ansiterm.ANSI_CMD_STR_TERM || b == ansiterm.ANSI_BEL: + // String escape sequence terminator + return true + } + return false +} + +func isXtermOscSequence(command []byte, current byte) bool { + return (len(command) >= 2 && command[0] == ansiterm.ANSI_ESCAPE_PRIMARY && command[1] == ansiterm.ANSI_CMD_OSC && current != ansiterm.ANSI_BEL) +} + +func isCharacterSelectionCmdChar(b byte) bool { + return (b == ansiterm.ANSI_CMD_G0 || b == ansiterm.ANSI_CMD_G1 || b == ansiterm.ANSI_CMD_G2 || b == ansiterm.ANSI_CMD_G3) +} + +// bytesToHex converts a slice of bytes to a human-readable string. +func bytesToHex(b []byte) string { + hex := make([]string, len(b)) + for i, ch := range b { + hex[i] = fmt.Sprintf("%X", ch) + } + return strings.Join(hex, "") +} + +// ensureInRange adjusts the passed value, if necessary, to ensure it is within +// the passed min / max range. +func ensureInRange(n int16, min int16, max int16) int16 { + if n < min { + return min + } else if n > max { + return max + } else { + return n + } +} + +func GetStdFile(nFile int) (*os.File, uintptr) { + var file *os.File + + // syscall uses negative numbers + // windows package uses very big uint32 + // Keep these switches split so we don't have to convert ints too much. + switch uint32(nFile) { + case windows.STD_INPUT_HANDLE: + file = os.Stdin + case windows.STD_OUTPUT_HANDLE: + file = os.Stdout + case windows.STD_ERROR_HANDLE: + file = os.Stderr + default: + switch nFile { + case syscall.STD_INPUT_HANDLE: + file = os.Stdin + case syscall.STD_OUTPUT_HANDLE: + file = os.Stdout + case syscall.STD_ERROR_HANDLE: + file = os.Stderr + default: + panic(fmt.Errorf("Invalid standard handle identifier: %v", nFile)) + } + } + + fd, err := syscall.GetStdHandle(nFile) + if err != nil { + panic(fmt.Errorf("Invalid standard handle identifier: %v -- %v", nFile, err)) + } + + return file, uintptr(fd) +} diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/api.go b/vendor/github.com/Azure/go-ansiterm/winterm/api.go new file mode 100644 index 0000000000..6055e33b91 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/winterm/api.go @@ -0,0 +1,327 @@ +// +build windows + +package winterm + +import ( + "fmt" + "syscall" + "unsafe" +) + +//=========================================================================================================== +// IMPORTANT NOTE: +// +// The methods below make extensive use of the "unsafe" package to obtain the required pointers. +// Beginning in Go 1.3, the garbage collector may release local variables (e.g., incoming arguments, stack +// variables) the pointers reference *before* the API completes. +// +// As a result, in those cases, the code must hint that the variables remain in active by invoking the +// dummy method "use" (see below). Newer versions of Go are planned to change the mechanism to no longer +// require unsafe pointers. +// +// If you add or modify methods, ENSURE protection of local variables through the "use" builtin to inform +// the garbage collector the variables remain in use if: +// +// -- The value is not a pointer (e.g., int32, struct) +// -- The value is not referenced by the method after passing the pointer to Windows +// +// See http://golang.org/doc/go1.3. +//=========================================================================================================== + +var ( + kernel32DLL = syscall.NewLazyDLL("kernel32.dll") + + getConsoleCursorInfoProc = kernel32DLL.NewProc("GetConsoleCursorInfo") + setConsoleCursorInfoProc = kernel32DLL.NewProc("SetConsoleCursorInfo") + setConsoleCursorPositionProc = kernel32DLL.NewProc("SetConsoleCursorPosition") + setConsoleModeProc = kernel32DLL.NewProc("SetConsoleMode") + getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo") + setConsoleScreenBufferSizeProc = kernel32DLL.NewProc("SetConsoleScreenBufferSize") + scrollConsoleScreenBufferProc = kernel32DLL.NewProc("ScrollConsoleScreenBufferA") + setConsoleTextAttributeProc = kernel32DLL.NewProc("SetConsoleTextAttribute") + setConsoleWindowInfoProc = kernel32DLL.NewProc("SetConsoleWindowInfo") + writeConsoleOutputProc = kernel32DLL.NewProc("WriteConsoleOutputW") + readConsoleInputProc = kernel32DLL.NewProc("ReadConsoleInputW") + waitForSingleObjectProc = kernel32DLL.NewProc("WaitForSingleObject") +) + +// Windows Console constants +const ( + // Console modes + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx. + ENABLE_PROCESSED_INPUT = 0x0001 + ENABLE_LINE_INPUT = 0x0002 + ENABLE_ECHO_INPUT = 0x0004 + ENABLE_WINDOW_INPUT = 0x0008 + ENABLE_MOUSE_INPUT = 0x0010 + ENABLE_INSERT_MODE = 0x0020 + ENABLE_QUICK_EDIT_MODE = 0x0040 + ENABLE_EXTENDED_FLAGS = 0x0080 + ENABLE_AUTO_POSITION = 0x0100 + ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200 + + ENABLE_PROCESSED_OUTPUT = 0x0001 + ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002 + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + DISABLE_NEWLINE_AUTO_RETURN = 0x0008 + ENABLE_LVB_GRID_WORLDWIDE = 0x0010 + + // Character attributes + // Note: + // -- The attributes are combined to produce various colors (e.g., Blue + Green will create Cyan). + // Clearing all foreground or background colors results in black; setting all creates white. + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes. + FOREGROUND_BLUE uint16 = 0x0001 + FOREGROUND_GREEN uint16 = 0x0002 + FOREGROUND_RED uint16 = 0x0004 + FOREGROUND_INTENSITY uint16 = 0x0008 + FOREGROUND_MASK uint16 = 0x000F + + BACKGROUND_BLUE uint16 = 0x0010 + BACKGROUND_GREEN uint16 = 0x0020 + BACKGROUND_RED uint16 = 0x0040 + BACKGROUND_INTENSITY uint16 = 0x0080 + BACKGROUND_MASK uint16 = 0x00F0 + + COMMON_LVB_MASK uint16 = 0xFF00 + COMMON_LVB_REVERSE_VIDEO uint16 = 0x4000 + COMMON_LVB_UNDERSCORE uint16 = 0x8000 + + // Input event types + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx. + KEY_EVENT = 0x0001 + MOUSE_EVENT = 0x0002 + WINDOW_BUFFER_SIZE_EVENT = 0x0004 + MENU_EVENT = 0x0008 + FOCUS_EVENT = 0x0010 + + // WaitForSingleObject return codes + WAIT_ABANDONED = 0x00000080 + WAIT_FAILED = 0xFFFFFFFF + WAIT_SIGNALED = 0x0000000 + WAIT_TIMEOUT = 0x00000102 + + // WaitForSingleObject wait duration + WAIT_INFINITE = 0xFFFFFFFF + WAIT_ONE_SECOND = 1000 + WAIT_HALF_SECOND = 500 + WAIT_QUARTER_SECOND = 250 +) + +// Windows API Console types +// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682101(v=vs.85).aspx for Console specific types (e.g., COORD) +// -- See https://msdn.microsoft.com/en-us/library/aa296569(v=vs.60).aspx for comments on alignment +type ( + CHAR_INFO struct { + UnicodeChar uint16 + Attributes uint16 + } + + CONSOLE_CURSOR_INFO struct { + Size uint32 + Visible int32 + } + + CONSOLE_SCREEN_BUFFER_INFO struct { + Size COORD + CursorPosition COORD + Attributes uint16 + Window SMALL_RECT + MaximumWindowSize COORD + } + + COORD struct { + X int16 + Y int16 + } + + SMALL_RECT struct { + Left int16 + Top int16 + Right int16 + Bottom int16 + } + + // INPUT_RECORD is a C/C++ union of which KEY_EVENT_RECORD is one case, it is also the largest + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx. + INPUT_RECORD struct { + EventType uint16 + KeyEvent KEY_EVENT_RECORD + } + + KEY_EVENT_RECORD struct { + KeyDown int32 + RepeatCount uint16 + VirtualKeyCode uint16 + VirtualScanCode uint16 + UnicodeChar uint16 + ControlKeyState uint32 + } + + WINDOW_BUFFER_SIZE struct { + Size COORD + } +) + +// boolToBOOL converts a Go bool into a Windows int32. +func boolToBOOL(f bool) int32 { + if f { + return int32(1) + } else { + return int32(0) + } +} + +// GetConsoleCursorInfo retrieves information about the size and visiblity of the console cursor. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683163(v=vs.85).aspx. +func GetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error { + r1, r2, err := getConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0) + return checkError(r1, r2, err) +} + +// SetConsoleCursorInfo sets the size and visiblity of the console cursor. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686019(v=vs.85).aspx. +func SetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error { + r1, r2, err := setConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0) + return checkError(r1, r2, err) +} + +// SetConsoleCursorPosition location of the console cursor. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx. +func SetConsoleCursorPosition(handle uintptr, coord COORD) error { + r1, r2, err := setConsoleCursorPositionProc.Call(handle, coordToPointer(coord)) + use(coord) + return checkError(r1, r2, err) +} + +// GetConsoleMode gets the console mode for given file descriptor +// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx. +func GetConsoleMode(handle uintptr) (mode uint32, err error) { + err = syscall.GetConsoleMode(syscall.Handle(handle), &mode) + return mode, err +} + +// SetConsoleMode sets the console mode for given file descriptor +// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx. +func SetConsoleMode(handle uintptr, mode uint32) error { + r1, r2, err := setConsoleModeProc.Call(handle, uintptr(mode), 0) + use(mode) + return checkError(r1, r2, err) +} + +// GetConsoleScreenBufferInfo retrieves information about the specified console screen buffer. +// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx. +func GetConsoleScreenBufferInfo(handle uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) { + info := CONSOLE_SCREEN_BUFFER_INFO{} + err := checkError(getConsoleScreenBufferInfoProc.Call(handle, uintptr(unsafe.Pointer(&info)), 0)) + if err != nil { + return nil, err + } + return &info, nil +} + +func ScrollConsoleScreenBuffer(handle uintptr, scrollRect SMALL_RECT, clipRect SMALL_RECT, destOrigin COORD, char CHAR_INFO) error { + r1, r2, err := scrollConsoleScreenBufferProc.Call(handle, uintptr(unsafe.Pointer(&scrollRect)), uintptr(unsafe.Pointer(&clipRect)), coordToPointer(destOrigin), uintptr(unsafe.Pointer(&char))) + use(scrollRect) + use(clipRect) + use(destOrigin) + use(char) + return checkError(r1, r2, err) +} + +// SetConsoleScreenBufferSize sets the size of the console screen buffer. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686044(v=vs.85).aspx. +func SetConsoleScreenBufferSize(handle uintptr, coord COORD) error { + r1, r2, err := setConsoleScreenBufferSizeProc.Call(handle, coordToPointer(coord)) + use(coord) + return checkError(r1, r2, err) +} + +// SetConsoleTextAttribute sets the attributes of characters written to the +// console screen buffer by the WriteFile or WriteConsole function. +// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx. +func SetConsoleTextAttribute(handle uintptr, attribute uint16) error { + r1, r2, err := setConsoleTextAttributeProc.Call(handle, uintptr(attribute), 0) + use(attribute) + return checkError(r1, r2, err) +} + +// SetConsoleWindowInfo sets the size and position of the console screen buffer's window. +// Note that the size and location must be within and no larger than the backing console screen buffer. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686125(v=vs.85).aspx. +func SetConsoleWindowInfo(handle uintptr, isAbsolute bool, rect SMALL_RECT) error { + r1, r2, err := setConsoleWindowInfoProc.Call(handle, uintptr(boolToBOOL(isAbsolute)), uintptr(unsafe.Pointer(&rect))) + use(isAbsolute) + use(rect) + return checkError(r1, r2, err) +} + +// WriteConsoleOutput writes the CHAR_INFOs from the provided buffer to the active console buffer. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687404(v=vs.85).aspx. +func WriteConsoleOutput(handle uintptr, buffer []CHAR_INFO, bufferSize COORD, bufferCoord COORD, writeRegion *SMALL_RECT) error { + r1, r2, err := writeConsoleOutputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), coordToPointer(bufferSize), coordToPointer(bufferCoord), uintptr(unsafe.Pointer(writeRegion))) + use(buffer) + use(bufferSize) + use(bufferCoord) + return checkError(r1, r2, err) +} + +// ReadConsoleInput reads (and removes) data from the console input buffer. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx. +func ReadConsoleInput(handle uintptr, buffer []INPUT_RECORD, count *uint32) error { + r1, r2, err := readConsoleInputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)), uintptr(unsafe.Pointer(count))) + use(buffer) + return checkError(r1, r2, err) +} + +// WaitForSingleObject waits for the passed handle to be signaled. +// It returns true if the handle was signaled; false otherwise. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx. +func WaitForSingleObject(handle uintptr, msWait uint32) (bool, error) { + r1, _, err := waitForSingleObjectProc.Call(handle, uintptr(uint32(msWait))) + switch r1 { + case WAIT_ABANDONED, WAIT_TIMEOUT: + return false, nil + case WAIT_SIGNALED: + return true, nil + } + use(msWait) + return false, err +} + +// String helpers +func (info CONSOLE_SCREEN_BUFFER_INFO) String() string { + return fmt.Sprintf("Size(%v) Cursor(%v) Window(%v) Max(%v)", info.Size, info.CursorPosition, info.Window, info.MaximumWindowSize) +} + +func (coord COORD) String() string { + return fmt.Sprintf("%v,%v", coord.X, coord.Y) +} + +func (rect SMALL_RECT) String() string { + return fmt.Sprintf("(%v,%v),(%v,%v)", rect.Left, rect.Top, rect.Right, rect.Bottom) +} + +// checkError evaluates the results of a Windows API call and returns the error if it failed. +func checkError(r1, r2 uintptr, err error) error { + // Windows APIs return non-zero to indicate success + if r1 != 0 { + return nil + } + + // Return the error if provided, otherwise default to EINVAL + if err != nil { + return err + } + return syscall.EINVAL +} + +// coordToPointer converts a COORD into a uintptr (by fooling the type system). +func coordToPointer(c COORD) uintptr { + // Note: This code assumes the two SHORTs are correctly laid out; the "cast" to uint32 is just to get a pointer to pass. + return uintptr(*((*uint32)(unsafe.Pointer(&c)))) +} + +// use is a no-op, but the compiler cannot see that it is. +// Calling use(p) ensures that p is kept live until that point. +func use(p interface{}) {} diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/attr_translation.go b/vendor/github.com/Azure/go-ansiterm/winterm/attr_translation.go new file mode 100644 index 0000000000..cbec8f728f --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/winterm/attr_translation.go @@ -0,0 +1,100 @@ +// +build windows + +package winterm + +import "github.com/Azure/go-ansiterm" + +const ( + FOREGROUND_COLOR_MASK = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE + BACKGROUND_COLOR_MASK = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE +) + +// collectAnsiIntoWindowsAttributes modifies the passed Windows text mode flags to reflect the +// request represented by the passed ANSI mode. +func collectAnsiIntoWindowsAttributes(windowsMode uint16, inverted bool, baseMode uint16, ansiMode int16) (uint16, bool) { + switch ansiMode { + + // Mode styles + case ansiterm.ANSI_SGR_BOLD: + windowsMode = windowsMode | FOREGROUND_INTENSITY + + case ansiterm.ANSI_SGR_DIM, ansiterm.ANSI_SGR_BOLD_DIM_OFF: + windowsMode &^= FOREGROUND_INTENSITY + + case ansiterm.ANSI_SGR_UNDERLINE: + windowsMode = windowsMode | COMMON_LVB_UNDERSCORE + + case ansiterm.ANSI_SGR_REVERSE: + inverted = true + + case ansiterm.ANSI_SGR_REVERSE_OFF: + inverted = false + + case ansiterm.ANSI_SGR_UNDERLINE_OFF: + windowsMode &^= COMMON_LVB_UNDERSCORE + + // Foreground colors + case ansiterm.ANSI_SGR_FOREGROUND_DEFAULT: + windowsMode = (windowsMode &^ FOREGROUND_MASK) | (baseMode & FOREGROUND_MASK) + + case ansiterm.ANSI_SGR_FOREGROUND_BLACK: + windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) + + case ansiterm.ANSI_SGR_FOREGROUND_RED: + windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED + + case ansiterm.ANSI_SGR_FOREGROUND_GREEN: + windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_GREEN + + case ansiterm.ANSI_SGR_FOREGROUND_YELLOW: + windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_GREEN + + case ansiterm.ANSI_SGR_FOREGROUND_BLUE: + windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_BLUE + + case ansiterm.ANSI_SGR_FOREGROUND_MAGENTA: + windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_BLUE + + case ansiterm.ANSI_SGR_FOREGROUND_CYAN: + windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_GREEN | FOREGROUND_BLUE + + case ansiterm.ANSI_SGR_FOREGROUND_WHITE: + windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE + + // Background colors + case ansiterm.ANSI_SGR_BACKGROUND_DEFAULT: + // Black with no intensity + windowsMode = (windowsMode &^ BACKGROUND_MASK) | (baseMode & BACKGROUND_MASK) + + case ansiterm.ANSI_SGR_BACKGROUND_BLACK: + windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) + + case ansiterm.ANSI_SGR_BACKGROUND_RED: + windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED + + case ansiterm.ANSI_SGR_BACKGROUND_GREEN: + windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_GREEN + + case ansiterm.ANSI_SGR_BACKGROUND_YELLOW: + windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN + + case ansiterm.ANSI_SGR_BACKGROUND_BLUE: + windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_BLUE + + case ansiterm.ANSI_SGR_BACKGROUND_MAGENTA: + windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_BLUE + + case ansiterm.ANSI_SGR_BACKGROUND_CYAN: + windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_GREEN | BACKGROUND_BLUE + + case ansiterm.ANSI_SGR_BACKGROUND_WHITE: + windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE + } + + return windowsMode, inverted +} + +// invertAttributes inverts the foreground and background colors of a Windows attributes value +func invertAttributes(windowsMode uint16) uint16 { + return (COMMON_LVB_MASK & windowsMode) | ((FOREGROUND_MASK & windowsMode) << 4) | ((BACKGROUND_MASK & windowsMode) >> 4) +} diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/cursor_helpers.go b/vendor/github.com/Azure/go-ansiterm/winterm/cursor_helpers.go new file mode 100644 index 0000000000..3ee06ea728 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/winterm/cursor_helpers.go @@ -0,0 +1,101 @@ +// +build windows + +package winterm + +const ( + horizontal = iota + vertical +) + +func (h *windowsAnsiEventHandler) getCursorWindow(info *CONSOLE_SCREEN_BUFFER_INFO) SMALL_RECT { + if h.originMode { + sr := h.effectiveSr(info.Window) + return SMALL_RECT{ + Top: sr.top, + Bottom: sr.bottom, + Left: 0, + Right: info.Size.X - 1, + } + } else { + return SMALL_RECT{ + Top: info.Window.Top, + Bottom: info.Window.Bottom, + Left: 0, + Right: info.Size.X - 1, + } + } +} + +// setCursorPosition sets the cursor to the specified position, bounded to the screen size +func (h *windowsAnsiEventHandler) setCursorPosition(position COORD, window SMALL_RECT) error { + position.X = ensureInRange(position.X, window.Left, window.Right) + position.Y = ensureInRange(position.Y, window.Top, window.Bottom) + err := SetConsoleCursorPosition(h.fd, position) + if err != nil { + return err + } + h.logf("Cursor position set: (%d, %d)", position.X, position.Y) + return err +} + +func (h *windowsAnsiEventHandler) moveCursorVertical(param int) error { + return h.moveCursor(vertical, param) +} + +func (h *windowsAnsiEventHandler) moveCursorHorizontal(param int) error { + return h.moveCursor(horizontal, param) +} + +func (h *windowsAnsiEventHandler) moveCursor(moveMode int, param int) error { + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + position := info.CursorPosition + switch moveMode { + case horizontal: + position.X += int16(param) + case vertical: + position.Y += int16(param) + } + + if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil { + return err + } + + return nil +} + +func (h *windowsAnsiEventHandler) moveCursorLine(param int) error { + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + position := info.CursorPosition + position.X = 0 + position.Y += int16(param) + + if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil { + return err + } + + return nil +} + +func (h *windowsAnsiEventHandler) moveCursorColumn(param int) error { + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + position := info.CursorPosition + position.X = int16(param) - 1 + + if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/erase_helpers.go b/vendor/github.com/Azure/go-ansiterm/winterm/erase_helpers.go new file mode 100644 index 0000000000..244b5fa25e --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/winterm/erase_helpers.go @@ -0,0 +1,84 @@ +// +build windows + +package winterm + +import "github.com/Azure/go-ansiterm" + +func (h *windowsAnsiEventHandler) clearRange(attributes uint16, fromCoord COORD, toCoord COORD) error { + // Ignore an invalid (negative area) request + if toCoord.Y < fromCoord.Y { + return nil + } + + var err error + + var coordStart = COORD{} + var coordEnd = COORD{} + + xCurrent, yCurrent := fromCoord.X, fromCoord.Y + xEnd, yEnd := toCoord.X, toCoord.Y + + // Clear any partial initial line + if xCurrent > 0 { + coordStart.X, coordStart.Y = xCurrent, yCurrent + coordEnd.X, coordEnd.Y = xEnd, yCurrent + + err = h.clearRect(attributes, coordStart, coordEnd) + if err != nil { + return err + } + + xCurrent = 0 + yCurrent += 1 + } + + // Clear intervening rectangular section + if yCurrent < yEnd { + coordStart.X, coordStart.Y = xCurrent, yCurrent + coordEnd.X, coordEnd.Y = xEnd, yEnd-1 + + err = h.clearRect(attributes, coordStart, coordEnd) + if err != nil { + return err + } + + xCurrent = 0 + yCurrent = yEnd + } + + // Clear remaining partial ending line + coordStart.X, coordStart.Y = xCurrent, yCurrent + coordEnd.X, coordEnd.Y = xEnd, yEnd + + err = h.clearRect(attributes, coordStart, coordEnd) + if err != nil { + return err + } + + return nil +} + +func (h *windowsAnsiEventHandler) clearRect(attributes uint16, fromCoord COORD, toCoord COORD) error { + region := SMALL_RECT{Top: fromCoord.Y, Left: fromCoord.X, Bottom: toCoord.Y, Right: toCoord.X} + width := toCoord.X - fromCoord.X + 1 + height := toCoord.Y - fromCoord.Y + 1 + size := uint32(width) * uint32(height) + + if size <= 0 { + return nil + } + + buffer := make([]CHAR_INFO, size) + + char := CHAR_INFO{ansiterm.FILL_CHARACTER, attributes} + for i := 0; i < int(size); i++ { + buffer[i] = char + } + + err := WriteConsoleOutput(h.fd, buffer, COORD{X: width, Y: height}, COORD{X: 0, Y: 0}, ®ion) + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/scroll_helper.go b/vendor/github.com/Azure/go-ansiterm/winterm/scroll_helper.go new file mode 100644 index 0000000000..2d27fa1d02 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/winterm/scroll_helper.go @@ -0,0 +1,118 @@ +// +build windows + +package winterm + +// effectiveSr gets the current effective scroll region in buffer coordinates +func (h *windowsAnsiEventHandler) effectiveSr(window SMALL_RECT) scrollRegion { + top := addInRange(window.Top, h.sr.top, window.Top, window.Bottom) + bottom := addInRange(window.Top, h.sr.bottom, window.Top, window.Bottom) + if top >= bottom { + top = window.Top + bottom = window.Bottom + } + return scrollRegion{top: top, bottom: bottom} +} + +func (h *windowsAnsiEventHandler) scrollUp(param int) error { + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + sr := h.effectiveSr(info.Window) + return h.scroll(param, sr, info) +} + +func (h *windowsAnsiEventHandler) scrollDown(param int) error { + return h.scrollUp(-param) +} + +func (h *windowsAnsiEventHandler) deleteLines(param int) error { + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + start := info.CursorPosition.Y + sr := h.effectiveSr(info.Window) + // Lines cannot be inserted or deleted outside the scrolling region. + if start >= sr.top && start <= sr.bottom { + sr.top = start + return h.scroll(param, sr, info) + } else { + return nil + } +} + +func (h *windowsAnsiEventHandler) insertLines(param int) error { + return h.deleteLines(-param) +} + +// scroll scrolls the provided scroll region by param lines. The scroll region is in buffer coordinates. +func (h *windowsAnsiEventHandler) scroll(param int, sr scrollRegion, info *CONSOLE_SCREEN_BUFFER_INFO) error { + h.logf("scroll: scrollTop: %d, scrollBottom: %d", sr.top, sr.bottom) + h.logf("scroll: windowTop: %d, windowBottom: %d", info.Window.Top, info.Window.Bottom) + + // Copy from and clip to the scroll region (full buffer width) + scrollRect := SMALL_RECT{ + Top: sr.top, + Bottom: sr.bottom, + Left: 0, + Right: info.Size.X - 1, + } + + // Origin to which area should be copied + destOrigin := COORD{ + X: 0, + Y: sr.top - int16(param), + } + + char := CHAR_INFO{ + UnicodeChar: ' ', + Attributes: h.attributes, + } + + if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char); err != nil { + return err + } + return nil +} + +func (h *windowsAnsiEventHandler) deleteCharacters(param int) error { + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + return h.scrollLine(param, info.CursorPosition, info) +} + +func (h *windowsAnsiEventHandler) insertCharacters(param int) error { + return h.deleteCharacters(-param) +} + +// scrollLine scrolls a line horizontally starting at the provided position by a number of columns. +func (h *windowsAnsiEventHandler) scrollLine(columns int, position COORD, info *CONSOLE_SCREEN_BUFFER_INFO) error { + // Copy from and clip to the scroll region (full buffer width) + scrollRect := SMALL_RECT{ + Top: position.Y, + Bottom: position.Y, + Left: position.X, + Right: info.Size.X - 1, + } + + // Origin to which area should be copied + destOrigin := COORD{ + X: position.X - int16(columns), + Y: position.Y, + } + + char := CHAR_INFO{ + UnicodeChar: ' ', + Attributes: h.attributes, + } + + if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/utilities.go b/vendor/github.com/Azure/go-ansiterm/winterm/utilities.go new file mode 100644 index 0000000000..afa7635d77 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/winterm/utilities.go @@ -0,0 +1,9 @@ +// +build windows + +package winterm + +// AddInRange increments a value by the passed quantity while ensuring the values +// always remain within the supplied min / max range. +func addInRange(n int16, increment int16, min int16, max int16) int16 { + return ensureInRange(n+increment, min, max) +} diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/win_event_handler.go b/vendor/github.com/Azure/go-ansiterm/winterm/win_event_handler.go new file mode 100644 index 0000000000..2d40fb75ad --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/winterm/win_event_handler.go @@ -0,0 +1,743 @@ +// +build windows + +package winterm + +import ( + "bytes" + "log" + "os" + "strconv" + + "github.com/Azure/go-ansiterm" +) + +type windowsAnsiEventHandler struct { + fd uintptr + file *os.File + infoReset *CONSOLE_SCREEN_BUFFER_INFO + sr scrollRegion + buffer bytes.Buffer + attributes uint16 + inverted bool + wrapNext bool + drewMarginByte bool + originMode bool + marginByte byte + curInfo *CONSOLE_SCREEN_BUFFER_INFO + curPos COORD + logf func(string, ...interface{}) +} + +type Option func(*windowsAnsiEventHandler) + +func WithLogf(f func(string, ...interface{})) Option { + return func(w *windowsAnsiEventHandler) { + w.logf = f + } +} + +func CreateWinEventHandler(fd uintptr, file *os.File, opts ...Option) ansiterm.AnsiEventHandler { + infoReset, err := GetConsoleScreenBufferInfo(fd) + if err != nil { + return nil + } + + h := &windowsAnsiEventHandler{ + fd: fd, + file: file, + infoReset: infoReset, + attributes: infoReset.Attributes, + } + for _, o := range opts { + o(h) + } + + if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" { + logFile, _ := os.Create("winEventHandler.log") + logger := log.New(logFile, "", log.LstdFlags) + if h.logf != nil { + l := h.logf + h.logf = func(s string, v ...interface{}) { + l(s, v...) + logger.Printf(s, v...) + } + } else { + h.logf = logger.Printf + } + } + + if h.logf == nil { + h.logf = func(string, ...interface{}) {} + } + + return h +} + +type scrollRegion struct { + top int16 + bottom int16 +} + +// simulateLF simulates a LF or CR+LF by scrolling if necessary to handle the +// current cursor position and scroll region settings, in which case it returns +// true. If no special handling is necessary, then it does nothing and returns +// false. +// +// In the false case, the caller should ensure that a carriage return +// and line feed are inserted or that the text is otherwise wrapped. +func (h *windowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) { + if h.wrapNext { + if err := h.Flush(); err != nil { + return false, err + } + h.clearWrap() + } + pos, info, err := h.getCurrentInfo() + if err != nil { + return false, err + } + sr := h.effectiveSr(info.Window) + if pos.Y == sr.bottom { + // Scrolling is necessary. Let Windows automatically scroll if the scrolling region + // is the full window. + if sr.top == info.Window.Top && sr.bottom == info.Window.Bottom { + if includeCR { + pos.X = 0 + h.updatePos(pos) + } + return false, nil + } + + // A custom scroll region is active. Scroll the window manually to simulate + // the LF. + if err := h.Flush(); err != nil { + return false, err + } + h.logf("Simulating LF inside scroll region") + if err := h.scrollUp(1); err != nil { + return false, err + } + if includeCR { + pos.X = 0 + if err := SetConsoleCursorPosition(h.fd, pos); err != nil { + return false, err + } + } + return true, nil + + } else if pos.Y < info.Window.Bottom { + // Let Windows handle the LF. + pos.Y++ + if includeCR { + pos.X = 0 + } + h.updatePos(pos) + return false, nil + } else { + // The cursor is at the bottom of the screen but outside the scroll + // region. Skip the LF. + h.logf("Simulating LF outside scroll region") + if includeCR { + if err := h.Flush(); err != nil { + return false, err + } + pos.X = 0 + if err := SetConsoleCursorPosition(h.fd, pos); err != nil { + return false, err + } + } + return true, nil + } +} + +// executeLF executes a LF without a CR. +func (h *windowsAnsiEventHandler) executeLF() error { + handled, err := h.simulateLF(false) + if err != nil { + return err + } + if !handled { + // Windows LF will reset the cursor column position. Write the LF + // and restore the cursor position. + pos, _, err := h.getCurrentInfo() + if err != nil { + return err + } + h.buffer.WriteByte(ansiterm.ANSI_LINE_FEED) + if pos.X != 0 { + if err := h.Flush(); err != nil { + return err + } + h.logf("Resetting cursor position for LF without CR") + if err := SetConsoleCursorPosition(h.fd, pos); err != nil { + return err + } + } + } + return nil +} + +func (h *windowsAnsiEventHandler) Print(b byte) error { + if h.wrapNext { + h.buffer.WriteByte(h.marginByte) + h.clearWrap() + if _, err := h.simulateLF(true); err != nil { + return err + } + } + pos, info, err := h.getCurrentInfo() + if err != nil { + return err + } + if pos.X == info.Size.X-1 { + h.wrapNext = true + h.marginByte = b + } else { + pos.X++ + h.updatePos(pos) + h.buffer.WriteByte(b) + } + return nil +} + +func (h *windowsAnsiEventHandler) Execute(b byte) error { + switch b { + case ansiterm.ANSI_TAB: + h.logf("Execute(TAB)") + // Move to the next tab stop, but preserve auto-wrap if already set. + if !h.wrapNext { + pos, info, err := h.getCurrentInfo() + if err != nil { + return err + } + pos.X = (pos.X + 8) - pos.X%8 + if pos.X >= info.Size.X { + pos.X = info.Size.X - 1 + } + if err := h.Flush(); err != nil { + return err + } + if err := SetConsoleCursorPosition(h.fd, pos); err != nil { + return err + } + } + return nil + + case ansiterm.ANSI_BEL: + h.buffer.WriteByte(ansiterm.ANSI_BEL) + return nil + + case ansiterm.ANSI_BACKSPACE: + if h.wrapNext { + if err := h.Flush(); err != nil { + return err + } + h.clearWrap() + } + pos, _, err := h.getCurrentInfo() + if err != nil { + return err + } + if pos.X > 0 { + pos.X-- + h.updatePos(pos) + h.buffer.WriteByte(ansiterm.ANSI_BACKSPACE) + } + return nil + + case ansiterm.ANSI_VERTICAL_TAB, ansiterm.ANSI_FORM_FEED: + // Treat as true LF. + return h.executeLF() + + case ansiterm.ANSI_LINE_FEED: + // Simulate a CR and LF for now since there is no way in go-ansiterm + // to tell if the LF should include CR (and more things break when it's + // missing than when it's incorrectly added). + handled, err := h.simulateLF(true) + if handled || err != nil { + return err + } + return h.buffer.WriteByte(ansiterm.ANSI_LINE_FEED) + + case ansiterm.ANSI_CARRIAGE_RETURN: + if h.wrapNext { + if err := h.Flush(); err != nil { + return err + } + h.clearWrap() + } + pos, _, err := h.getCurrentInfo() + if err != nil { + return err + } + if pos.X != 0 { + pos.X = 0 + h.updatePos(pos) + h.buffer.WriteByte(ansiterm.ANSI_CARRIAGE_RETURN) + } + return nil + + default: + return nil + } +} + +func (h *windowsAnsiEventHandler) CUU(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("CUU: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.moveCursorVertical(-param) +} + +func (h *windowsAnsiEventHandler) CUD(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("CUD: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.moveCursorVertical(param) +} + +func (h *windowsAnsiEventHandler) CUF(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("CUF: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.moveCursorHorizontal(param) +} + +func (h *windowsAnsiEventHandler) CUB(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("CUB: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.moveCursorHorizontal(-param) +} + +func (h *windowsAnsiEventHandler) CNL(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("CNL: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.moveCursorLine(param) +} + +func (h *windowsAnsiEventHandler) CPL(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("CPL: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.moveCursorLine(-param) +} + +func (h *windowsAnsiEventHandler) CHA(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("CHA: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.moveCursorColumn(param) +} + +func (h *windowsAnsiEventHandler) VPA(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("VPA: [[%d]]", param) + h.clearWrap() + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + window := h.getCursorWindow(info) + position := info.CursorPosition + position.Y = window.Top + int16(param) - 1 + return h.setCursorPosition(position, window) +} + +func (h *windowsAnsiEventHandler) CUP(row int, col int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("CUP: [[%d %d]]", row, col) + h.clearWrap() + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + window := h.getCursorWindow(info) + position := COORD{window.Left + int16(col) - 1, window.Top + int16(row) - 1} + return h.setCursorPosition(position, window) +} + +func (h *windowsAnsiEventHandler) HVP(row int, col int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("HVP: [[%d %d]]", row, col) + h.clearWrap() + return h.CUP(row, col) +} + +func (h *windowsAnsiEventHandler) DECTCEM(visible bool) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("DECTCEM: [%v]", []string{strconv.FormatBool(visible)}) + h.clearWrap() + return nil +} + +func (h *windowsAnsiEventHandler) DECOM(enable bool) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("DECOM: [%v]", []string{strconv.FormatBool(enable)}) + h.clearWrap() + h.originMode = enable + return h.CUP(1, 1) +} + +func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("DECCOLM: [%v]", []string{strconv.FormatBool(use132)}) + h.clearWrap() + if err := h.ED(2); err != nil { + return err + } + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + targetWidth := int16(80) + if use132 { + targetWidth = 132 + } + if info.Size.X < targetWidth { + if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil { + h.logf("set buffer failed: %v", err) + return err + } + } + window := info.Window + window.Left = 0 + window.Right = targetWidth - 1 + if err := SetConsoleWindowInfo(h.fd, true, window); err != nil { + h.logf("set window failed: %v", err) + return err + } + if info.Size.X > targetWidth { + if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil { + h.logf("set buffer failed: %v", err) + return err + } + } + return SetConsoleCursorPosition(h.fd, COORD{0, 0}) +} + +func (h *windowsAnsiEventHandler) ED(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("ED: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + + // [J -- Erases from the cursor to the end of the screen, including the cursor position. + // [1J -- Erases from the beginning of the screen to the cursor, including the cursor position. + // [2J -- Erases the complete display. The cursor does not move. + // Notes: + // -- Clearing the entire buffer, versus just the Window, works best for Windows Consoles + + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + var start COORD + var end COORD + + switch param { + case 0: + start = info.CursorPosition + end = COORD{info.Size.X - 1, info.Size.Y - 1} + + case 1: + start = COORD{0, 0} + end = info.CursorPosition + + case 2: + start = COORD{0, 0} + end = COORD{info.Size.X - 1, info.Size.Y - 1} + } + + err = h.clearRange(h.attributes, start, end) + if err != nil { + return err + } + + // If the whole buffer was cleared, move the window to the top while preserving + // the window-relative cursor position. + if param == 2 { + pos := info.CursorPosition + window := info.Window + pos.Y -= window.Top + window.Bottom -= window.Top + window.Top = 0 + if err := SetConsoleCursorPosition(h.fd, pos); err != nil { + return err + } + if err := SetConsoleWindowInfo(h.fd, true, window); err != nil { + return err + } + } + + return nil +} + +func (h *windowsAnsiEventHandler) EL(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("EL: [%v]", strconv.Itoa(param)) + h.clearWrap() + + // [K -- Erases from the cursor to the end of the line, including the cursor position. + // [1K -- Erases from the beginning of the line to the cursor, including the cursor position. + // [2K -- Erases the complete line. + + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + var start COORD + var end COORD + + switch param { + case 0: + start = info.CursorPosition + end = COORD{info.Size.X, info.CursorPosition.Y} + + case 1: + start = COORD{0, info.CursorPosition.Y} + end = info.CursorPosition + + case 2: + start = COORD{0, info.CursorPosition.Y} + end = COORD{info.Size.X, info.CursorPosition.Y} + } + + err = h.clearRange(h.attributes, start, end) + if err != nil { + return err + } + + return nil +} + +func (h *windowsAnsiEventHandler) IL(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("IL: [%v]", strconv.Itoa(param)) + h.clearWrap() + return h.insertLines(param) +} + +func (h *windowsAnsiEventHandler) DL(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("DL: [%v]", strconv.Itoa(param)) + h.clearWrap() + return h.deleteLines(param) +} + +func (h *windowsAnsiEventHandler) ICH(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("ICH: [%v]", strconv.Itoa(param)) + h.clearWrap() + return h.insertCharacters(param) +} + +func (h *windowsAnsiEventHandler) DCH(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("DCH: [%v]", strconv.Itoa(param)) + h.clearWrap() + return h.deleteCharacters(param) +} + +func (h *windowsAnsiEventHandler) SGR(params []int) error { + if err := h.Flush(); err != nil { + return err + } + strings := []string{} + for _, v := range params { + strings = append(strings, strconv.Itoa(v)) + } + + h.logf("SGR: [%v]", strings) + + if len(params) <= 0 { + h.attributes = h.infoReset.Attributes + h.inverted = false + } else { + for _, attr := range params { + + if attr == ansiterm.ANSI_SGR_RESET { + h.attributes = h.infoReset.Attributes + h.inverted = false + continue + } + + h.attributes, h.inverted = collectAnsiIntoWindowsAttributes(h.attributes, h.inverted, h.infoReset.Attributes, int16(attr)) + } + } + + attributes := h.attributes + if h.inverted { + attributes = invertAttributes(attributes) + } + err := SetConsoleTextAttribute(h.fd, attributes) + if err != nil { + return err + } + + return nil +} + +func (h *windowsAnsiEventHandler) SU(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("SU: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.scrollUp(param) +} + +func (h *windowsAnsiEventHandler) SD(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("SD: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.scrollDown(param) +} + +func (h *windowsAnsiEventHandler) DA(params []string) error { + h.logf("DA: [%v]", params) + // DA cannot be implemented because it must send data on the VT100 input stream, + // which is not available to go-ansiterm. + return nil +} + +func (h *windowsAnsiEventHandler) DECSTBM(top int, bottom int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("DECSTBM: [%d, %d]", top, bottom) + + // Windows is 0 indexed, Linux is 1 indexed + h.sr.top = int16(top - 1) + h.sr.bottom = int16(bottom - 1) + + // This command also moves the cursor to the origin. + h.clearWrap() + return h.CUP(1, 1) +} + +func (h *windowsAnsiEventHandler) RI() error { + if err := h.Flush(); err != nil { + return err + } + h.logf("RI: []") + h.clearWrap() + + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + sr := h.effectiveSr(info.Window) + if info.CursorPosition.Y == sr.top { + return h.scrollDown(1) + } + + return h.moveCursorVertical(-1) +} + +func (h *windowsAnsiEventHandler) IND() error { + h.logf("IND: []") + return h.executeLF() +} + +func (h *windowsAnsiEventHandler) Flush() error { + h.curInfo = nil + if h.buffer.Len() > 0 { + h.logf("Flush: [%s]", h.buffer.Bytes()) + if _, err := h.buffer.WriteTo(h.file); err != nil { + return err + } + } + + if h.wrapNext && !h.drewMarginByte { + h.logf("Flush: drawing margin byte '%c'", h.marginByte) + + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + charInfo := []CHAR_INFO{{UnicodeChar: uint16(h.marginByte), Attributes: info.Attributes}} + size := COORD{1, 1} + position := COORD{0, 0} + region := SMALL_RECT{Left: info.CursorPosition.X, Top: info.CursorPosition.Y, Right: info.CursorPosition.X, Bottom: info.CursorPosition.Y} + if err := WriteConsoleOutput(h.fd, charInfo, size, position, ®ion); err != nil { + return err + } + h.drewMarginByte = true + } + return nil +} + +// cacheConsoleInfo ensures that the current console screen information has been queried +// since the last call to Flush(). It must be called before accessing h.curInfo or h.curPos. +func (h *windowsAnsiEventHandler) getCurrentInfo() (COORD, *CONSOLE_SCREEN_BUFFER_INFO, error) { + if h.curInfo == nil { + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return COORD{}, nil, err + } + h.curInfo = info + h.curPos = info.CursorPosition + } + return h.curPos, h.curInfo, nil +} + +func (h *windowsAnsiEventHandler) updatePos(pos COORD) { + if h.curInfo == nil { + panic("failed to call getCurrentInfo before calling updatePos") + } + h.curPos = pos +} + +// clearWrap clears the state where the cursor is in the margin +// waiting for the next character before wrapping the line. This must +// be done before most operations that act on the cursor. +func (h *windowsAnsiEventHandler) clearWrap() { + h.wrapNext = false + h.drewMarginByte = false +} diff --git a/vendor/github.com/BurntSushi/toml/.gitignore b/vendor/github.com/BurntSushi/toml/.gitignore new file mode 100644 index 0000000000..cd11be9653 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/.gitignore @@ -0,0 +1,2 @@ +toml.test +/toml-test diff --git a/vendor/github.com/BurntSushi/toml/COMPATIBLE b/vendor/github.com/BurntSushi/toml/COMPATIBLE new file mode 100644 index 0000000000..f621b01196 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/COMPATIBLE @@ -0,0 +1 @@ +Compatible with TOML version [v1.0.0](https://toml.io/en/v1.0.0). diff --git a/vendor/github.com/BurntSushi/toml/COPYING b/vendor/github.com/BurntSushi/toml/COPYING new file mode 100644 index 0000000000..01b5743200 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/COPYING @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 TOML authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/BurntSushi/toml/README.md b/vendor/github.com/BurntSushi/toml/README.md new file mode 100644 index 0000000000..cc13f8667f --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/README.md @@ -0,0 +1,211 @@ +TOML stands for Tom's Obvious, Minimal Language. This Go package provides a +reflection interface similar to Go's standard library `json` and `xml` +packages. + +Compatible with TOML version [v1.0.0](https://toml.io/en/v1.0.0). + +Documentation: https://godocs.io/github.com/BurntSushi/toml + +See the [releases page](https://github.com/BurntSushi/toml/releases) for a +changelog; this information is also in the git tag annotations (e.g. `git show +v0.4.0`). + +This library requires Go 1.13 or newer; install it with: + + % go get github.com/BurntSushi/toml@latest + +It also comes with a TOML validator CLI tool: + + % go install github.com/BurntSushi/toml/cmd/tomlv@latest + % tomlv some-toml-file.toml + +### Testing +This package passes all tests in [toml-test] for both the decoder and the +encoder. + +[toml-test]: https://github.com/BurntSushi/toml-test + +### Examples +This package works similar to how the Go standard library handles XML and JSON. +Namely, data is loaded into Go values via reflection. + +For the simplest example, consider some TOML file as just a list of keys and +values: + +```toml +Age = 25 +Cats = [ "Cauchy", "Plato" ] +Pi = 3.14 +Perfection = [ 6, 28, 496, 8128 ] +DOB = 1987-07-05T05:45:00Z +``` + +Which could be defined in Go as: + +```go +type Config struct { + Age int + Cats []string + Pi float64 + Perfection []int + DOB time.Time // requires `import time` +} +``` + +And then decoded with: + +```go +var conf Config +err := toml.Decode(tomlData, &conf) +// handle error +``` + +You can also use struct tags if your struct field name doesn't map to a TOML +key value directly: + +```toml +some_key_NAME = "wat" +``` + +```go +type TOML struct { + ObscureKey string `toml:"some_key_NAME"` +} +``` + +Beware that like other most other decoders **only exported fields** are +considered when encoding and decoding; private fields are silently ignored. + +### Using the `Marshaler` and `encoding.TextUnmarshaler` interfaces +Here's an example that automatically parses duration strings into +`time.Duration` values: + +```toml +[[song]] +name = "Thunder Road" +duration = "4m49s" + +[[song]] +name = "Stairway to Heaven" +duration = "8m03s" +``` + +Which can be decoded with: + +```go +type song struct { + Name string + Duration duration +} +type songs struct { + Song []song +} +var favorites songs +if _, err := toml.Decode(blob, &favorites); err != nil { + log.Fatal(err) +} + +for _, s := range favorites.Song { + fmt.Printf("%s (%s)\n", s.Name, s.Duration) +} +``` + +And you'll also need a `duration` type that satisfies the +`encoding.TextUnmarshaler` interface: + +```go +type duration struct { + time.Duration +} + +func (d *duration) UnmarshalText(text []byte) error { + var err error + d.Duration, err = time.ParseDuration(string(text)) + return err +} +``` + +To target TOML specifically you can implement `UnmarshalTOML` TOML interface in +a similar way. + +### More complex usage +Here's an example of how to load the example from the official spec page: + +```toml +# This is a TOML document. Boom. + +title = "TOML Example" + +[owner] +name = "Tom Preston-Werner" +organization = "GitHub" +bio = "GitHub Cofounder & CEO\nLikes tater tots and beer." +dob = 1979-05-27T07:32:00Z # First class dates? Why not? + +[database] +server = "192.168.1.1" +ports = [ 8001, 8001, 8002 ] +connection_max = 5000 +enabled = true + +[servers] + + # You can indent as you please. Tabs or spaces. TOML don't care. + [servers.alpha] + ip = "10.0.0.1" + dc = "eqdc10" + + [servers.beta] + ip = "10.0.0.2" + dc = "eqdc10" + +[clients] +data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it + +# Line breaks are OK when inside arrays +hosts = [ + "alpha", + "omega" +] +``` + +And the corresponding Go types are: + +```go +type tomlConfig struct { + Title string + Owner ownerInfo + DB database `toml:"database"` + Servers map[string]server + Clients clients +} + +type ownerInfo struct { + Name string + Org string `toml:"organization"` + Bio string + DOB time.Time +} + +type database struct { + Server string + Ports []int + ConnMax int `toml:"connection_max"` + Enabled bool +} + +type server struct { + IP string + DC string +} + +type clients struct { + Data [][]interface{} + Hosts []string +} +``` + +Note that a case insensitive match will be tried if an exact match can't be +found. + +A working example of the above can be found in `_example/example.{go,toml}`. diff --git a/vendor/github.com/BurntSushi/toml/decode.go b/vendor/github.com/BurntSushi/toml/decode.go new file mode 100644 index 0000000000..e24f0c5d5c --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/decode.go @@ -0,0 +1,560 @@ +package toml + +import ( + "encoding" + "fmt" + "io" + "io/ioutil" + "math" + "os" + "reflect" + "strings" +) + +// Unmarshaler is the interface implemented by objects that can unmarshal a +// TOML description of themselves. +type Unmarshaler interface { + UnmarshalTOML(interface{}) error +} + +// Unmarshal decodes the contents of `p` in TOML format into a pointer `v`. +func Unmarshal(p []byte, v interface{}) error { + _, err := Decode(string(p), v) + return err +} + +// Primitive is a TOML value that hasn't been decoded into a Go value. +// +// This type can be used for any value, which will cause decoding to be delayed. +// You can use the PrimitiveDecode() function to "manually" decode these values. +// +// NOTE: The underlying representation of a `Primitive` value is subject to +// change. Do not rely on it. +// +// NOTE: Primitive values are still parsed, so using them will only avoid the +// overhead of reflection. They can be useful when you don't know the exact type +// of TOML data until runtime. +type Primitive struct { + undecoded interface{} + context Key +} + +// The significand precision for float32 and float64 is 24 and 53 bits; this is +// the range a natural number can be stored in a float without loss of data. +const ( + maxSafeFloat32Int = 16777215 // 2^24-1 + maxSafeFloat64Int = 9007199254740991 // 2^53-1 +) + +// PrimitiveDecode is just like the other `Decode*` functions, except it +// decodes a TOML value that has already been parsed. Valid primitive values +// can *only* be obtained from values filled by the decoder functions, +// including this method. (i.e., `v` may contain more `Primitive` +// values.) +// +// Meta data for primitive values is included in the meta data returned by +// the `Decode*` functions with one exception: keys returned by the Undecoded +// method will only reflect keys that were decoded. Namely, any keys hidden +// behind a Primitive will be considered undecoded. Executing this method will +// update the undecoded keys in the meta data. (See the example.) +func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error { + md.context = primValue.context + defer func() { md.context = nil }() + return md.unify(primValue.undecoded, rvalue(v)) +} + +// Decoder decodes TOML data. +// +// TOML tables correspond to Go structs or maps (dealer's choice – they can be +// used interchangeably). +// +// TOML table arrays correspond to either a slice of structs or a slice of maps. +// +// TOML datetimes correspond to Go time.Time values. Local datetimes are parsed +// in the local timezone. +// +// All other TOML types (float, string, int, bool and array) correspond to the +// obvious Go types. +// +// An exception to the above rules is if a type implements the TextUnmarshaler +// interface, in which case any primitive TOML value (floats, strings, integers, +// booleans, datetimes) will be converted to a []byte and given to the value's +// UnmarshalText method. See the Unmarshaler example for a demonstration with +// time duration strings. +// +// Key mapping +// +// TOML keys can map to either keys in a Go map or field names in a Go struct. +// The special `toml` struct tag can be used to map TOML keys to struct fields +// that don't match the key name exactly (see the example). A case insensitive +// match to struct names will be tried if an exact match can't be found. +// +// The mapping between TOML values and Go values is loose. That is, there may +// exist TOML values that cannot be placed into your representation, and there +// may be parts of your representation that do not correspond to TOML values. +// This loose mapping can be made stricter by using the IsDefined and/or +// Undecoded methods on the MetaData returned. +// +// This decoder does not handle cyclic types. Decode will not terminate if a +// cyclic type is passed. +type Decoder struct { + r io.Reader +} + +// NewDecoder creates a new Decoder. +func NewDecoder(r io.Reader) *Decoder { + return &Decoder{r: r} +} + +var ( + unmarshalToml = reflect.TypeOf((*Unmarshaler)(nil)).Elem() + unmarshalText = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() +) + +// Decode TOML data in to the pointer `v`. +func (dec *Decoder) Decode(v interface{}) (MetaData, error) { + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Ptr { + s := "%q" + if reflect.TypeOf(v) == nil { + s = "%v" + } + + return MetaData{}, e("cannot decode to non-pointer "+s, reflect.TypeOf(v)) + } + if rv.IsNil() { + return MetaData{}, e("cannot decode to nil value of %q", reflect.TypeOf(v)) + } + + // Check if this is a supported type: struct, map, interface{}, or something + // that implements UnmarshalTOML or UnmarshalText. + rv = indirect(rv) + rt := rv.Type() + if rv.Kind() != reflect.Struct && rv.Kind() != reflect.Map && + !(rv.Kind() == reflect.Interface && rv.NumMethod() == 0) && + !rt.Implements(unmarshalToml) && !rt.Implements(unmarshalText) { + return MetaData{}, e("cannot decode to type %s", rt) + } + + // TODO: parser should read from io.Reader? Or at the very least, make it + // read from []byte rather than string + data, err := ioutil.ReadAll(dec.r) + if err != nil { + return MetaData{}, err + } + + p, err := parse(string(data)) + if err != nil { + return MetaData{}, err + } + + md := MetaData{ + mapping: p.mapping, + types: p.types, + keys: p.ordered, + decoded: make(map[string]struct{}, len(p.ordered)), + context: nil, + } + return md, md.unify(p.mapping, rv) +} + +// Decode the TOML data in to the pointer v. +// +// See the documentation on Decoder for a description of the decoding process. +func Decode(data string, v interface{}) (MetaData, error) { + return NewDecoder(strings.NewReader(data)).Decode(v) +} + +// DecodeFile is just like Decode, except it will automatically read the +// contents of the file at path and decode it for you. +func DecodeFile(path string, v interface{}) (MetaData, error) { + fp, err := os.Open(path) + if err != nil { + return MetaData{}, err + } + defer fp.Close() + return NewDecoder(fp).Decode(v) +} + +// unify performs a sort of type unification based on the structure of `rv`, +// which is the client representation. +// +// Any type mismatch produces an error. Finding a type that we don't know +// how to handle produces an unsupported type error. +func (md *MetaData) unify(data interface{}, rv reflect.Value) error { + // Special case. Look for a `Primitive` value. + // TODO: #76 would make this superfluous after implemented. + if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() { + // Save the undecoded data and the key context into the primitive + // value. + context := make(Key, len(md.context)) + copy(context, md.context) + rv.Set(reflect.ValueOf(Primitive{ + undecoded: data, + context: context, + })) + return nil + } + + // Special case. Unmarshaler Interface support. + if rv.CanAddr() { + if v, ok := rv.Addr().Interface().(Unmarshaler); ok { + return v.UnmarshalTOML(data) + } + } + + // Special case. Look for a value satisfying the TextUnmarshaler interface. + if v, ok := rv.Interface().(encoding.TextUnmarshaler); ok { + return md.unifyText(data, v) + } + // TODO: + // The behavior here is incorrect whenever a Go type satisfies the + // encoding.TextUnmarshaler interface but also corresponds to a TOML hash or + // array. In particular, the unmarshaler should only be applied to primitive + // TOML values. But at this point, it will be applied to all kinds of values + // and produce an incorrect error whenever those values are hashes or arrays + // (including arrays of tables). + + k := rv.Kind() + + // laziness + if k >= reflect.Int && k <= reflect.Uint64 { + return md.unifyInt(data, rv) + } + switch k { + case reflect.Ptr: + elem := reflect.New(rv.Type().Elem()) + err := md.unify(data, reflect.Indirect(elem)) + if err != nil { + return err + } + rv.Set(elem) + return nil + case reflect.Struct: + return md.unifyStruct(data, rv) + case reflect.Map: + return md.unifyMap(data, rv) + case reflect.Array: + return md.unifyArray(data, rv) + case reflect.Slice: + return md.unifySlice(data, rv) + case reflect.String: + return md.unifyString(data, rv) + case reflect.Bool: + return md.unifyBool(data, rv) + case reflect.Interface: + // we only support empty interfaces. + if rv.NumMethod() > 0 { + return e("unsupported type %s", rv.Type()) + } + return md.unifyAnything(data, rv) + case reflect.Float32, reflect.Float64: + return md.unifyFloat64(data, rv) + } + return e("unsupported type %s", rv.Kind()) +} + +func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error { + tmap, ok := mapping.(map[string]interface{}) + if !ok { + if mapping == nil { + return nil + } + return e("type mismatch for %s: expected table but found %T", + rv.Type().String(), mapping) + } + + for key, datum := range tmap { + var f *field + fields := cachedTypeFields(rv.Type()) + for i := range fields { + ff := &fields[i] + if ff.name == key { + f = ff + break + } + if f == nil && strings.EqualFold(ff.name, key) { + f = ff + } + } + if f != nil { + subv := rv + for _, i := range f.index { + subv = indirect(subv.Field(i)) + } + + if isUnifiable(subv) { + md.decoded[md.context.add(key).String()] = struct{}{} + md.context = append(md.context, key) + err := md.unify(datum, subv) + if err != nil { + return err + } + md.context = md.context[0 : len(md.context)-1] + } else if f.name != "" { + return e("cannot write unexported field %s.%s", rv.Type().String(), f.name) + } + } + } + return nil +} + +func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error { + if k := rv.Type().Key().Kind(); k != reflect.String { + return fmt.Errorf( + "toml: cannot decode to a map with non-string key type (%s in %q)", + k, rv.Type()) + } + + tmap, ok := mapping.(map[string]interface{}) + if !ok { + if tmap == nil { + return nil + } + return md.badtype("map", mapping) + } + if rv.IsNil() { + rv.Set(reflect.MakeMap(rv.Type())) + } + for k, v := range tmap { + md.decoded[md.context.add(k).String()] = struct{}{} + md.context = append(md.context, k) + + rvval := reflect.Indirect(reflect.New(rv.Type().Elem())) + if err := md.unify(v, rvval); err != nil { + return err + } + md.context = md.context[0 : len(md.context)-1] + + rvkey := indirect(reflect.New(rv.Type().Key())) + rvkey.SetString(k) + rv.SetMapIndex(rvkey, rvval) + } + return nil +} + +func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error { + datav := reflect.ValueOf(data) + if datav.Kind() != reflect.Slice { + if !datav.IsValid() { + return nil + } + return md.badtype("slice", data) + } + if l := datav.Len(); l != rv.Len() { + return e("expected array length %d; got TOML array of length %d", rv.Len(), l) + } + return md.unifySliceArray(datav, rv) +} + +func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error { + datav := reflect.ValueOf(data) + if datav.Kind() != reflect.Slice { + if !datav.IsValid() { + return nil + } + return md.badtype("slice", data) + } + n := datav.Len() + if rv.IsNil() || rv.Cap() < n { + rv.Set(reflect.MakeSlice(rv.Type(), n, n)) + } + rv.SetLen(n) + return md.unifySliceArray(datav, rv) +} + +func (md *MetaData) unifySliceArray(data, rv reflect.Value) error { + l := data.Len() + for i := 0; i < l; i++ { + err := md.unify(data.Index(i).Interface(), indirect(rv.Index(i))) + if err != nil { + return err + } + } + return nil +} + +func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error { + if s, ok := data.(string); ok { + rv.SetString(s) + return nil + } + return md.badtype("string", data) +} + +func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error { + if num, ok := data.(float64); ok { + switch rv.Kind() { + case reflect.Float32: + if num < -math.MaxFloat32 || num > math.MaxFloat32 { + return e("value %f is out of range for float32", num) + } + fallthrough + case reflect.Float64: + rv.SetFloat(num) + default: + panic("bug") + } + return nil + } + + if num, ok := data.(int64); ok { + switch rv.Kind() { + case reflect.Float32: + if num < -maxSafeFloat32Int || num > maxSafeFloat32Int { + return e("value %d is out of range for float32", num) + } + fallthrough + case reflect.Float64: + if num < -maxSafeFloat64Int || num > maxSafeFloat64Int { + return e("value %d is out of range for float64", num) + } + rv.SetFloat(float64(num)) + default: + panic("bug") + } + return nil + } + + return md.badtype("float", data) +} + +func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error { + if num, ok := data.(int64); ok { + if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 { + switch rv.Kind() { + case reflect.Int, reflect.Int64: + // No bounds checking necessary. + case reflect.Int8: + if num < math.MinInt8 || num > math.MaxInt8 { + return e("value %d is out of range for int8", num) + } + case reflect.Int16: + if num < math.MinInt16 || num > math.MaxInt16 { + return e("value %d is out of range for int16", num) + } + case reflect.Int32: + if num < math.MinInt32 || num > math.MaxInt32 { + return e("value %d is out of range for int32", num) + } + } + rv.SetInt(num) + } else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 { + unum := uint64(num) + switch rv.Kind() { + case reflect.Uint, reflect.Uint64: + // No bounds checking necessary. + case reflect.Uint8: + if num < 0 || unum > math.MaxUint8 { + return e("value %d is out of range for uint8", num) + } + case reflect.Uint16: + if num < 0 || unum > math.MaxUint16 { + return e("value %d is out of range for uint16", num) + } + case reflect.Uint32: + if num < 0 || unum > math.MaxUint32 { + return e("value %d is out of range for uint32", num) + } + } + rv.SetUint(unum) + } else { + panic("unreachable") + } + return nil + } + return md.badtype("integer", data) +} + +func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error { + if b, ok := data.(bool); ok { + rv.SetBool(b) + return nil + } + return md.badtype("boolean", data) +} + +func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error { + rv.Set(reflect.ValueOf(data)) + return nil +} + +func (md *MetaData) unifyText(data interface{}, v encoding.TextUnmarshaler) error { + var s string + switch sdata := data.(type) { + case Marshaler: + text, err := sdata.MarshalTOML() + if err != nil { + return err + } + s = string(text) + case TextMarshaler: + text, err := sdata.MarshalText() + if err != nil { + return err + } + s = string(text) + case fmt.Stringer: + s = sdata.String() + case string: + s = sdata + case bool: + s = fmt.Sprintf("%v", sdata) + case int64: + s = fmt.Sprintf("%d", sdata) + case float64: + s = fmt.Sprintf("%f", sdata) + default: + return md.badtype("primitive (string-like)", data) + } + if err := v.UnmarshalText([]byte(s)); err != nil { + return err + } + return nil +} + +func (md *MetaData) badtype(dst string, data interface{}) error { + return e("incompatible types: TOML key %q has type %T; destination has type %s", md.context, data, dst) +} + +// rvalue returns a reflect.Value of `v`. All pointers are resolved. +func rvalue(v interface{}) reflect.Value { + return indirect(reflect.ValueOf(v)) +} + +// indirect returns the value pointed to by a pointer. +// +// Pointers are followed until the value is not a pointer. New values are +// allocated for each nil pointer. +// +// An exception to this rule is if the value satisfies an interface of interest +// to us (like encoding.TextUnmarshaler). +func indirect(v reflect.Value) reflect.Value { + if v.Kind() != reflect.Ptr { + if v.CanSet() { + pv := v.Addr() + if _, ok := pv.Interface().(encoding.TextUnmarshaler); ok { + return pv + } + } + return v + } + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + return indirect(reflect.Indirect(v)) +} + +func isUnifiable(rv reflect.Value) bool { + if rv.CanSet() { + return true + } + if _, ok := rv.Interface().(encoding.TextUnmarshaler); ok { + return true + } + return false +} + +func e(format string, args ...interface{}) error { + return fmt.Errorf("toml: "+format, args...) +} diff --git a/vendor/github.com/BurntSushi/toml/decode_go116.go b/vendor/github.com/BurntSushi/toml/decode_go116.go new file mode 100644 index 0000000000..eddfb641b8 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/decode_go116.go @@ -0,0 +1,19 @@ +//go:build go1.16 +// +build go1.16 + +package toml + +import ( + "io/fs" +) + +// DecodeFS is just like Decode, except it will automatically read the contents +// of the file at `path` from a fs.FS instance. +func DecodeFS(fsys fs.FS, path string, v interface{}) (MetaData, error) { + fp, err := fsys.Open(path) + if err != nil { + return MetaData{}, err + } + defer fp.Close() + return NewDecoder(fp).Decode(v) +} diff --git a/vendor/github.com/BurntSushi/toml/deprecated.go b/vendor/github.com/BurntSushi/toml/deprecated.go new file mode 100644 index 0000000000..c6af3f239d --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/deprecated.go @@ -0,0 +1,21 @@ +package toml + +import ( + "encoding" + "io" +) + +// Deprecated: use encoding.TextMarshaler +type TextMarshaler encoding.TextMarshaler + +// Deprecated: use encoding.TextUnmarshaler +type TextUnmarshaler encoding.TextUnmarshaler + +// Deprecated: use MetaData.PrimitiveDecode. +func PrimitiveDecode(primValue Primitive, v interface{}) error { + md := MetaData{decoded: make(map[string]struct{})} + return md.unify(primValue.undecoded, rvalue(v)) +} + +// Deprecated: use NewDecoder(reader).Decode(&value). +func DecodeReader(r io.Reader, v interface{}) (MetaData, error) { return NewDecoder(r).Decode(v) } diff --git a/vendor/github.com/BurntSushi/toml/doc.go b/vendor/github.com/BurntSushi/toml/doc.go new file mode 100644 index 0000000000..099c4a77d2 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/doc.go @@ -0,0 +1,13 @@ +/* +Package toml implements decoding and encoding of TOML files. + +This package supports TOML v1.0.0, as listed on https://toml.io + +There is also support for delaying decoding with the Primitive type, and +querying the set of keys in a TOML document with the MetaData type. + +The github.com/BurntSushi/toml/cmd/tomlv package implements a TOML validator, +and can be used to verify if TOML document is valid. It can also be used to +print the type of each key. +*/ +package toml diff --git a/vendor/github.com/BurntSushi/toml/encode.go b/vendor/github.com/BurntSushi/toml/encode.go new file mode 100644 index 0000000000..dee4e6d319 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/encode.go @@ -0,0 +1,694 @@ +package toml + +import ( + "bufio" + "encoding" + "errors" + "fmt" + "io" + "math" + "reflect" + "sort" + "strconv" + "strings" + "time" + + "github.com/BurntSushi/toml/internal" +) + +type tomlEncodeError struct{ error } + +var ( + errArrayNilElement = errors.New("toml: cannot encode array with nil element") + errNonString = errors.New("toml: cannot encode a map with non-string key type") + errNoKey = errors.New("toml: top-level values must be Go maps or structs") + errAnything = errors.New("") // used in testing +) + +var dblQuotedReplacer = strings.NewReplacer( + "\"", "\\\"", + "\\", "\\\\", + "\x00", `\u0000`, + "\x01", `\u0001`, + "\x02", `\u0002`, + "\x03", `\u0003`, + "\x04", `\u0004`, + "\x05", `\u0005`, + "\x06", `\u0006`, + "\x07", `\u0007`, + "\b", `\b`, + "\t", `\t`, + "\n", `\n`, + "\x0b", `\u000b`, + "\f", `\f`, + "\r", `\r`, + "\x0e", `\u000e`, + "\x0f", `\u000f`, + "\x10", `\u0010`, + "\x11", `\u0011`, + "\x12", `\u0012`, + "\x13", `\u0013`, + "\x14", `\u0014`, + "\x15", `\u0015`, + "\x16", `\u0016`, + "\x17", `\u0017`, + "\x18", `\u0018`, + "\x19", `\u0019`, + "\x1a", `\u001a`, + "\x1b", `\u001b`, + "\x1c", `\u001c`, + "\x1d", `\u001d`, + "\x1e", `\u001e`, + "\x1f", `\u001f`, + "\x7f", `\u007f`, +) + +// Marshaler is the interface implemented by types that can marshal themselves +// into valid TOML. +type Marshaler interface { + MarshalTOML() ([]byte, error) +} + +// Encoder encodes a Go to a TOML document. +// +// The mapping between Go values and TOML values should be precisely the same as +// for the Decode* functions. +// +// The toml.Marshaler and encoder.TextMarshaler interfaces are supported to +// encoding the value as custom TOML. +// +// If you want to write arbitrary binary data then you will need to use +// something like base64 since TOML does not have any binary types. +// +// When encoding TOML hashes (Go maps or structs), keys without any sub-hashes +// are encoded first. +// +// Go maps will be sorted alphabetically by key for deterministic output. +// +// Encoding Go values without a corresponding TOML representation will return an +// error. Examples of this includes maps with non-string keys, slices with nil +// elements, embedded non-struct types, and nested slices containing maps or +// structs. (e.g. [][]map[string]string is not allowed but []map[string]string +// is okay, as is []map[string][]string). +// +// NOTE: only exported keys are encoded due to the use of reflection. Unexported +// keys are silently discarded. +type Encoder struct { + // String to use for a single indentation level; default is two spaces. + Indent string + + w *bufio.Writer + hasWritten bool // written any output to w yet? +} + +// NewEncoder create a new Encoder. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + w: bufio.NewWriter(w), + Indent: " ", + } +} + +// Encode writes a TOML representation of the Go value to the Encoder's writer. +// +// An error is returned if the value given cannot be encoded to a valid TOML +// document. +func (enc *Encoder) Encode(v interface{}) error { + rv := eindirect(reflect.ValueOf(v)) + if err := enc.safeEncode(Key([]string{}), rv); err != nil { + return err + } + return enc.w.Flush() +} + +func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) { + defer func() { + if r := recover(); r != nil { + if terr, ok := r.(tomlEncodeError); ok { + err = terr.error + return + } + panic(r) + } + }() + enc.encode(key, rv) + return nil +} + +func (enc *Encoder) encode(key Key, rv reflect.Value) { + // Special case: time needs to be in ISO8601 format. + // + // Special case: if we can marshal the type to text, then we used that. This + // prevents the encoder for handling these types as generic structs (or + // whatever the underlying type of a TextMarshaler is). + switch t := rv.Interface().(type) { + case time.Time, encoding.TextMarshaler, Marshaler: + enc.writeKeyValue(key, rv, false) + return + // TODO: #76 would make this superfluous after implemented. + case Primitive: + enc.encode(key, reflect.ValueOf(t.undecoded)) + return + } + + k := rv.Kind() + switch k { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64, + reflect.Float32, reflect.Float64, reflect.String, reflect.Bool: + enc.writeKeyValue(key, rv, false) + case reflect.Array, reflect.Slice: + if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) { + enc.eArrayOfTables(key, rv) + } else { + enc.writeKeyValue(key, rv, false) + } + case reflect.Interface: + if rv.IsNil() { + return + } + enc.encode(key, rv.Elem()) + case reflect.Map: + if rv.IsNil() { + return + } + enc.eTable(key, rv) + case reflect.Ptr: + if rv.IsNil() { + return + } + enc.encode(key, rv.Elem()) + case reflect.Struct: + enc.eTable(key, rv) + default: + encPanic(fmt.Errorf("unsupported type for key '%s': %s", key, k)) + } +} + +// eElement encodes any value that can be an array element. +func (enc *Encoder) eElement(rv reflect.Value) { + switch v := rv.Interface().(type) { + case time.Time: // Using TextMarshaler adds extra quotes, which we don't want. + format := time.RFC3339Nano + switch v.Location() { + case internal.LocalDatetime: + format = "2006-01-02T15:04:05.999999999" + case internal.LocalDate: + format = "2006-01-02" + case internal.LocalTime: + format = "15:04:05.999999999" + } + switch v.Location() { + default: + enc.wf(v.Format(format)) + case internal.LocalDatetime, internal.LocalDate, internal.LocalTime: + enc.wf(v.In(time.UTC).Format(format)) + } + return + case Marshaler: + s, err := v.MarshalTOML() + if err != nil { + encPanic(err) + } + enc.writeQuoted(string(s)) + return + case encoding.TextMarshaler: + s, err := v.MarshalText() + if err != nil { + encPanic(err) + } + enc.writeQuoted(string(s)) + return + } + + switch rv.Kind() { + case reflect.String: + enc.writeQuoted(rv.String()) + case reflect.Bool: + enc.wf(strconv.FormatBool(rv.Bool())) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + enc.wf(strconv.FormatInt(rv.Int(), 10)) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + enc.wf(strconv.FormatUint(rv.Uint(), 10)) + case reflect.Float32: + f := rv.Float() + if math.IsNaN(f) { + enc.wf("nan") + } else if math.IsInf(f, 0) { + enc.wf("%cinf", map[bool]byte{true: '-', false: '+'}[math.Signbit(f)]) + } else { + enc.wf(floatAddDecimal(strconv.FormatFloat(f, 'f', -1, 32))) + } + case reflect.Float64: + f := rv.Float() + if math.IsNaN(f) { + enc.wf("nan") + } else if math.IsInf(f, 0) { + enc.wf("%cinf", map[bool]byte{true: '-', false: '+'}[math.Signbit(f)]) + } else { + enc.wf(floatAddDecimal(strconv.FormatFloat(f, 'f', -1, 64))) + } + case reflect.Array, reflect.Slice: + enc.eArrayOrSliceElement(rv) + case reflect.Struct: + enc.eStruct(nil, rv, true) + case reflect.Map: + enc.eMap(nil, rv, true) + case reflect.Interface: + enc.eElement(rv.Elem()) + default: + encPanic(fmt.Errorf("unexpected primitive type: %T", rv.Interface())) + } +} + +// By the TOML spec, all floats must have a decimal with at least one number on +// either side. +func floatAddDecimal(fstr string) string { + if !strings.Contains(fstr, ".") { + return fstr + ".0" + } + return fstr +} + +func (enc *Encoder) writeQuoted(s string) { + enc.wf("\"%s\"", dblQuotedReplacer.Replace(s)) +} + +func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) { + length := rv.Len() + enc.wf("[") + for i := 0; i < length; i++ { + elem := rv.Index(i) + enc.eElement(elem) + if i != length-1 { + enc.wf(", ") + } + } + enc.wf("]") +} + +func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) { + if len(key) == 0 { + encPanic(errNoKey) + } + for i := 0; i < rv.Len(); i++ { + trv := rv.Index(i) + if isNil(trv) { + continue + } + enc.newline() + enc.wf("%s[[%s]]", enc.indentStr(key), key) + enc.newline() + enc.eMapOrStruct(key, trv, false) + } +} + +func (enc *Encoder) eTable(key Key, rv reflect.Value) { + if len(key) == 1 { + // Output an extra newline between top-level tables. + // (The newline isn't written if nothing else has been written though.) + enc.newline() + } + if len(key) > 0 { + enc.wf("%s[%s]", enc.indentStr(key), key) + enc.newline() + } + enc.eMapOrStruct(key, rv, false) +} + +func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value, inline bool) { + switch rv := eindirect(rv); rv.Kind() { + case reflect.Map: + enc.eMap(key, rv, inline) + case reflect.Struct: + enc.eStruct(key, rv, inline) + default: + // Should never happen? + panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String()) + } +} + +func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) { + rt := rv.Type() + if rt.Key().Kind() != reflect.String { + encPanic(errNonString) + } + + // Sort keys so that we have deterministic output. And write keys directly + // underneath this key first, before writing sub-structs or sub-maps. + var mapKeysDirect, mapKeysSub []string + for _, mapKey := range rv.MapKeys() { + k := mapKey.String() + if typeIsTable(tomlTypeOfGo(rv.MapIndex(mapKey))) { + mapKeysSub = append(mapKeysSub, k) + } else { + mapKeysDirect = append(mapKeysDirect, k) + } + } + + var writeMapKeys = func(mapKeys []string, trailC bool) { + sort.Strings(mapKeys) + for i, mapKey := range mapKeys { + val := rv.MapIndex(reflect.ValueOf(mapKey)) + if isNil(val) { + continue + } + + if inline { + enc.writeKeyValue(Key{mapKey}, val, true) + if trailC || i != len(mapKeys)-1 { + enc.wf(", ") + } + } else { + enc.encode(key.add(mapKey), val) + } + } + } + + if inline { + enc.wf("{") + } + writeMapKeys(mapKeysDirect, len(mapKeysSub) > 0) + writeMapKeys(mapKeysSub, false) + if inline { + enc.wf("}") + } +} + +const is32Bit = (32 << (^uint(0) >> 63)) == 32 + +func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) { + // Write keys for fields directly under this key first, because if we write + // a field that creates a new table then all keys under it will be in that + // table (not the one we're writing here). + // + // Fields is a [][]int: for fieldsDirect this always has one entry (the + // struct index). For fieldsSub it contains two entries: the parent field + // index from tv, and the field indexes for the fields of the sub. + var ( + rt = rv.Type() + fieldsDirect, fieldsSub [][]int + addFields func(rt reflect.Type, rv reflect.Value, start []int) + ) + addFields = func(rt reflect.Type, rv reflect.Value, start []int) { + for i := 0; i < rt.NumField(); i++ { + f := rt.Field(i) + if f.PkgPath != "" && !f.Anonymous { /// Skip unexported fields. + continue + } + + frv := rv.Field(i) + + // Treat anonymous struct fields with tag names as though they are + // not anonymous, like encoding/json does. + // + // Non-struct anonymous fields use the normal encoding logic. + if f.Anonymous { + t := f.Type + switch t.Kind() { + case reflect.Struct: + if getOptions(f.Tag).name == "" { + addFields(t, frv, append(start, f.Index...)) + continue + } + case reflect.Ptr: + if t.Elem().Kind() == reflect.Struct && getOptions(f.Tag).name == "" { + if !frv.IsNil() { + addFields(t.Elem(), frv.Elem(), append(start, f.Index...)) + } + continue + } + } + } + + if typeIsTable(tomlTypeOfGo(frv)) { + fieldsSub = append(fieldsSub, append(start, f.Index...)) + } else { + // Copy so it works correct on 32bit archs; not clear why this + // is needed. See #314, and https://www.reddit.com/r/golang/comments/pnx8v4 + // This also works fine on 64bit, but 32bit archs are somewhat + // rare and this is a wee bit faster. + if is32Bit { + copyStart := make([]int, len(start)) + copy(copyStart, start) + fieldsDirect = append(fieldsDirect, append(copyStart, f.Index...)) + } else { + fieldsDirect = append(fieldsDirect, append(start, f.Index...)) + } + } + } + } + addFields(rt, rv, nil) + + writeFields := func(fields [][]int) { + for _, fieldIndex := range fields { + fieldType := rt.FieldByIndex(fieldIndex) + fieldVal := rv.FieldByIndex(fieldIndex) + + if isNil(fieldVal) { /// Don't write anything for nil fields. + continue + } + + opts := getOptions(fieldType.Tag) + if opts.skip { + continue + } + keyName := fieldType.Name + if opts.name != "" { + keyName = opts.name + } + if opts.omitempty && isEmpty(fieldVal) { + continue + } + if opts.omitzero && isZero(fieldVal) { + continue + } + + if inline { + enc.writeKeyValue(Key{keyName}, fieldVal, true) + if fieldIndex[0] != len(fields)-1 { + enc.wf(", ") + } + } else { + enc.encode(key.add(keyName), fieldVal) + } + } + } + + if inline { + enc.wf("{") + } + writeFields(fieldsDirect) + writeFields(fieldsSub) + if inline { + enc.wf("}") + } +} + +// tomlTypeOfGo returns the TOML type name of the Go value's type. +// +// It is used to determine whether the types of array elements are mixed (which +// is forbidden). If the Go value is nil, then it is illegal for it to be an +// array element, and valueIsNil is returned as true. +// +// The type may be `nil`, which means no concrete TOML type could be found. +func tomlTypeOfGo(rv reflect.Value) tomlType { + if isNil(rv) || !rv.IsValid() { + return nil + } + switch rv.Kind() { + case reflect.Bool: + return tomlBool + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64: + return tomlInteger + case reflect.Float32, reflect.Float64: + return tomlFloat + case reflect.Array, reflect.Slice: + if typeEqual(tomlHash, tomlArrayType(rv)) { + return tomlArrayHash + } + return tomlArray + case reflect.Ptr, reflect.Interface: + return tomlTypeOfGo(rv.Elem()) + case reflect.String: + return tomlString + case reflect.Map: + return tomlHash + case reflect.Struct: + if _, ok := rv.Interface().(time.Time); ok { + return tomlDatetime + } + if isMarshaler(rv) { + return tomlString + } + return tomlHash + default: + if isMarshaler(rv) { + return tomlString + } + + encPanic(errors.New("unsupported type: " + rv.Kind().String())) + panic("unreachable") + } +} + +func isMarshaler(rv reflect.Value) bool { + switch rv.Interface().(type) { + case encoding.TextMarshaler: + return true + case Marshaler: + return true + } + + // Someone used a pointer receiver: we can make it work for pointer values. + if rv.CanAddr() { + if _, ok := rv.Addr().Interface().(encoding.TextMarshaler); ok { + return true + } + if _, ok := rv.Addr().Interface().(Marshaler); ok { + return true + } + } + return false +} + +// tomlArrayType returns the element type of a TOML array. The type returned +// may be nil if it cannot be determined (e.g., a nil slice or a zero length +// slize). This function may also panic if it finds a type that cannot be +// expressed in TOML (such as nil elements, heterogeneous arrays or directly +// nested arrays of tables). +func tomlArrayType(rv reflect.Value) tomlType { + if isNil(rv) || !rv.IsValid() || rv.Len() == 0 { + return nil + } + + /// Don't allow nil. + rvlen := rv.Len() + for i := 1; i < rvlen; i++ { + if tomlTypeOfGo(rv.Index(i)) == nil { + encPanic(errArrayNilElement) + } + } + + firstType := tomlTypeOfGo(rv.Index(0)) + if firstType == nil { + encPanic(errArrayNilElement) + } + return firstType +} + +type tagOptions struct { + skip bool // "-" + name string + omitempty bool + omitzero bool +} + +func getOptions(tag reflect.StructTag) tagOptions { + t := tag.Get("toml") + if t == "-" { + return tagOptions{skip: true} + } + var opts tagOptions + parts := strings.Split(t, ",") + opts.name = parts[0] + for _, s := range parts[1:] { + switch s { + case "omitempty": + opts.omitempty = true + case "omitzero": + opts.omitzero = true + } + } + return opts +} + +func isZero(rv reflect.Value) bool { + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return rv.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return rv.Uint() == 0 + case reflect.Float32, reflect.Float64: + return rv.Float() == 0.0 + } + return false +} + +func isEmpty(rv reflect.Value) bool { + switch rv.Kind() { + case reflect.Array, reflect.Slice, reflect.Map, reflect.String: + return rv.Len() == 0 + case reflect.Bool: + return !rv.Bool() + } + return false +} + +func (enc *Encoder) newline() { + if enc.hasWritten { + enc.wf("\n") + } +} + +// Write a key/value pair: +// +// key = +// +// This is also used for "k = v" in inline tables; so something like this will +// be written in three calls: +// +// ┌────────────────────┐ +// │ ┌───┐ ┌─────┐│ +// v v v v vv +// key = {k = v, k2 = v2} +// +func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) { + if len(key) == 0 { + encPanic(errNoKey) + } + enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1)) + enc.eElement(val) + if !inline { + enc.newline() + } +} + +func (enc *Encoder) wf(format string, v ...interface{}) { + _, err := fmt.Fprintf(enc.w, format, v...) + if err != nil { + encPanic(err) + } + enc.hasWritten = true +} + +func (enc *Encoder) indentStr(key Key) string { + return strings.Repeat(enc.Indent, len(key)-1) +} + +func encPanic(err error) { + panic(tomlEncodeError{err}) +} + +func eindirect(v reflect.Value) reflect.Value { + switch v.Kind() { + case reflect.Ptr, reflect.Interface: + return eindirect(v.Elem()) + default: + return v + } +} + +func isNil(rv reflect.Value) bool { + switch rv.Kind() { + case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return rv.IsNil() + default: + return false + } +} diff --git a/vendor/github.com/BurntSushi/toml/error.go b/vendor/github.com/BurntSushi/toml/error.go new file mode 100644 index 0000000000..36edc46554 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/error.go @@ -0,0 +1,229 @@ +package toml + +import ( + "fmt" + "strings" +) + +// ParseError is returned when there is an error parsing the TOML syntax. +// +// For example invalid syntax, duplicate keys, etc. +// +// In addition to the error message itself, you can also print detailed location +// information with context by using ErrorWithLocation(): +// +// toml: error: Key 'fruit' was already created and cannot be used as an array. +// +// At line 4, column 2-7: +// +// 2 | fruit = [] +// 3 | +// 4 | [[fruit]] # Not allowed +// ^^^^^ +// +// Furthermore, the ErrorWithUsage() can be used to print the above with some +// more detailed usage guidance: +// +// toml: error: newlines not allowed within inline tables +// +// At line 1, column 18: +// +// 1 | x = [{ key = 42 # +// ^ +// +// Error help: +// +// Inline tables must always be on a single line: +// +// table = {key = 42, second = 43} +// +// It is invalid to split them over multiple lines like so: +// +// # INVALID +// table = { +// key = 42, +// second = 43 +// } +// +// Use regular for this: +// +// [table] +// key = 42 +// second = 43 +type ParseError struct { + Message string // Short technical message. + Usage string // Longer message with usage guidance; may be blank. + Position Position // Position of the error + LastKey string // Last parsed key, may be blank. + Line int // Line the error occurred. Deprecated: use Position. + + err error + input string +} + +// Position of an error. +type Position struct { + Line int // Line number, starting at 1. + Start int // Start of error, as byte offset starting at 0. + Len int // Lenght in bytes. +} + +func (pe ParseError) Error() string { + msg := pe.Message + if msg == "" { // Error from errorf() + msg = pe.err.Error() + } + + if pe.LastKey == "" { + return fmt.Sprintf("toml: line %d: %s", pe.Position.Line, msg) + } + return fmt.Sprintf("toml: line %d (last key %q): %s", + pe.Position.Line, pe.LastKey, msg) +} + +// ErrorWithUsage() returns the error with detailed location context. +// +// See the documentation on ParseError. +func (pe ParseError) ErrorWithPosition() string { + if pe.input == "" { // Should never happen, but just in case. + return pe.Error() + } + + var ( + lines = strings.Split(pe.input, "\n") + col = pe.column(lines) + b = new(strings.Builder) + ) + + msg := pe.Message + if msg == "" { + msg = pe.err.Error() + } + + // TODO: don't show control characters as literals? This may not show up + // well everywhere. + + if pe.Position.Len == 1 { + fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d:\n\n", + msg, pe.Position.Line, col+1) + } else { + fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d-%d:\n\n", + msg, pe.Position.Line, col, col+pe.Position.Len) + } + if pe.Position.Line > 2 { + fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-2, lines[pe.Position.Line-3]) + } + if pe.Position.Line > 1 { + fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-1, lines[pe.Position.Line-2]) + } + fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line, lines[pe.Position.Line-1]) + fmt.Fprintf(b, "% 10s%s%s\n", "", strings.Repeat(" ", col), strings.Repeat("^", pe.Position.Len)) + return b.String() +} + +// ErrorWithUsage() returns the error with detailed location context and usage +// guidance. +// +// See the documentation on ParseError. +func (pe ParseError) ErrorWithUsage() string { + m := pe.ErrorWithPosition() + if u, ok := pe.err.(interface{ Usage() string }); ok && u.Usage() != "" { + return m + "Error help:\n\n " + + strings.ReplaceAll(strings.TrimSpace(u.Usage()), "\n", "\n ") + + "\n" + } + return m +} + +func (pe ParseError) column(lines []string) int { + var pos, col int + for i := range lines { + ll := len(lines[i]) + 1 // +1 for the removed newline + if pos+ll >= pe.Position.Start { + col = pe.Position.Start - pos + if col < 0 { // Should never happen, but just in case. + col = 0 + } + break + } + pos += ll + } + + return col +} + +type ( + errLexControl struct{ r rune } + errLexEscape struct{ r rune } + errLexUTF8 struct{ b byte } + errLexInvalidNum struct{ v string } + errLexInvalidDate struct{ v string } + errLexInlineTableNL struct{} + errLexStringNL struct{} +) + +func (e errLexControl) Error() string { + return fmt.Sprintf("TOML files cannot contain control characters: '0x%02x'", e.r) +} +func (e errLexControl) Usage() string { return "" } + +func (e errLexEscape) Error() string { return fmt.Sprintf(`invalid escape in string '\%c'`, e.r) } +func (e errLexEscape) Usage() string { return usageEscape } +func (e errLexUTF8) Error() string { return fmt.Sprintf("invalid UTF-8 byte: 0x%02x", e.b) } +func (e errLexUTF8) Usage() string { return "" } +func (e errLexInvalidNum) Error() string { return fmt.Sprintf("invalid number: %q", e.v) } +func (e errLexInvalidNum) Usage() string { return "" } +func (e errLexInvalidDate) Error() string { return fmt.Sprintf("invalid date: %q", e.v) } +func (e errLexInvalidDate) Usage() string { return "" } +func (e errLexInlineTableNL) Error() string { return "newlines not allowed within inline tables" } +func (e errLexInlineTableNL) Usage() string { return usageInlineNewline } +func (e errLexStringNL) Error() string { return "strings cannot contain newlines" } +func (e errLexStringNL) Usage() string { return usageStringNewline } + +const usageEscape = ` +A '\' inside a "-delimited string is interpreted as an escape character. + +The following escape sequences are supported: +\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX + +To prevent a '\' from being recognized as an escape character, use either: + +- a ' or '''-delimited string; escape characters aren't processed in them; or +- write two backslashes to get a single backslash: '\\'. + +If you're trying to add a Windows path (e.g. "C:\Users\martin") then using '/' +instead of '\' will usually also work: "C:/Users/martin". +` + +const usageInlineNewline = ` +Inline tables must always be on a single line: + + table = {key = 42, second = 43} + +It is invalid to split them over multiple lines like so: + + # INVALID + table = { + key = 42, + second = 43 + } + +Use regular for this: + + [table] + key = 42 + second = 43 +` + +const usageStringNewline = ` +Strings must always be on a single line, and cannot span more than one line: + + # INVALID + string = "Hello, + world!" + +Instead use """ or ''' to split strings over multiple lines: + + string = """Hello, + world!""" +` diff --git a/vendor/github.com/BurntSushi/toml/go.mod b/vendor/github.com/BurntSushi/toml/go.mod new file mode 100644 index 0000000000..82989481df --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/go.mod @@ -0,0 +1,3 @@ +module github.com/BurntSushi/toml + +go 1.16 diff --git a/vendor/github.com/BurntSushi/toml/internal/tz.go b/vendor/github.com/BurntSushi/toml/internal/tz.go new file mode 100644 index 0000000000..022f15bc2b --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/internal/tz.go @@ -0,0 +1,36 @@ +package internal + +import "time" + +// Timezones used for local datetime, date, and time TOML types. +// +// The exact way times and dates without a timezone should be interpreted is not +// well-defined in the TOML specification and left to the implementation. These +// defaults to current local timezone offset of the computer, but this can be +// changed by changing these variables before decoding. +// +// TODO: +// Ideally we'd like to offer people the ability to configure the used timezone +// by setting Decoder.Timezone and Encoder.Timezone; however, this is a bit +// tricky: the reason we use three different variables for this is to support +// round-tripping – without these specific TZ names we wouldn't know which +// format to use. +// +// There isn't a good way to encode this right now though, and passing this sort +// of information also ties in to various related issues such as string format +// encoding, encoding of comments, etc. +// +// So, for the time being, just put this in internal until we can write a good +// comprehensive API for doing all of this. +// +// The reason they're exported is because they're referred from in e.g. +// internal/tag. +// +// Note that this behaviour is valid according to the TOML spec as the exact +// behaviour is left up to implementations. +var ( + localOffset = func() int { _, o := time.Now().Zone(); return o }() + LocalDatetime = time.FixedZone("datetime-local", localOffset) + LocalDate = time.FixedZone("date-local", localOffset) + LocalTime = time.FixedZone("time-local", localOffset) +) diff --git a/vendor/github.com/BurntSushi/toml/lex.go b/vendor/github.com/BurntSushi/toml/lex.go new file mode 100644 index 0000000000..63ef20f474 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/lex.go @@ -0,0 +1,1219 @@ +package toml + +import ( + "fmt" + "reflect" + "runtime" + "strings" + "unicode" + "unicode/utf8" +) + +type itemType int + +const ( + itemError itemType = iota + itemNIL // used in the parser to indicate no type + itemEOF + itemText + itemString + itemRawString + itemMultilineString + itemRawMultilineString + itemBool + itemInteger + itemFloat + itemDatetime + itemArray // the start of an array + itemArrayEnd + itemTableStart + itemTableEnd + itemArrayTableStart + itemArrayTableEnd + itemKeyStart + itemKeyEnd + itemCommentStart + itemInlineTableStart + itemInlineTableEnd +) + +const eof = 0 + +type stateFn func(lx *lexer) stateFn + +func (p Position) String() string { + return fmt.Sprintf("at line %d; start %d; length %d", p.Line, p.Start, p.Len) +} + +type lexer struct { + input string + start int + pos int + line int + state stateFn + items chan item + + // Allow for backing up up to 4 runes. This is necessary because TOML + // contains 3-rune tokens (""" and '''). + prevWidths [4]int + nprev int // how many of prevWidths are in use + atEOF bool // If we emit an eof, we can still back up, but it is not OK to call next again. + + // A stack of state functions used to maintain context. + // + // The idea is to reuse parts of the state machine in various places. For + // example, values can appear at the top level or within arbitrarily nested + // arrays. The last state on the stack is used after a value has been lexed. + // Similarly for comments. + stack []stateFn +} + +type item struct { + typ itemType + val string + err error + pos Position +} + +func (lx *lexer) nextItem() item { + for { + select { + case item := <-lx.items: + return item + default: + lx.state = lx.state(lx) + //fmt.Printf(" STATE %-24s current: %-10q stack: %s\n", lx.state, lx.current(), lx.stack) + } + } +} + +func lex(input string) *lexer { + lx := &lexer{ + input: input, + state: lexTop, + items: make(chan item, 10), + stack: make([]stateFn, 0, 10), + line: 1, + } + return lx +} + +func (lx *lexer) push(state stateFn) { + lx.stack = append(lx.stack, state) +} + +func (lx *lexer) pop() stateFn { + if len(lx.stack) == 0 { + return lx.errorf("BUG in lexer: no states to pop") + } + last := lx.stack[len(lx.stack)-1] + lx.stack = lx.stack[0 : len(lx.stack)-1] + return last +} + +func (lx *lexer) current() string { + return lx.input[lx.start:lx.pos] +} + +func (lx lexer) getPos() Position { + p := Position{ + Line: lx.line, + Start: lx.start, + Len: lx.pos - lx.start, + } + if p.Len <= 0 { + p.Len = 1 + } + return p +} + +func (lx *lexer) emit(typ itemType) { + lx.items <- item{typ: typ, pos: lx.getPos(), val: lx.current()} + lx.start = lx.pos +} + +func (lx *lexer) emitTrim(typ itemType) { + lx.items <- item{typ: typ, pos: lx.getPos(), val: strings.TrimSpace(lx.current())} + lx.start = lx.pos +} + +func (lx *lexer) next() (r rune) { + if lx.atEOF { + panic("BUG in lexer: next called after EOF") + } + if lx.pos >= len(lx.input) { + lx.atEOF = true + return eof + } + + if lx.input[lx.pos] == '\n' { + lx.line++ + } + lx.prevWidths[3] = lx.prevWidths[2] + lx.prevWidths[2] = lx.prevWidths[1] + lx.prevWidths[1] = lx.prevWidths[0] + if lx.nprev < 4 { + lx.nprev++ + } + + r, w := utf8.DecodeRuneInString(lx.input[lx.pos:]) + if r == utf8.RuneError { + lx.error(errLexUTF8{lx.input[lx.pos]}) + return utf8.RuneError + } + + // Note: don't use peek() here, as this calls next(). + if isControl(r) || (r == '\r' && (len(lx.input)-1 == lx.pos || lx.input[lx.pos+1] != '\n')) { + lx.errorControlChar(r) + return utf8.RuneError + } + + lx.prevWidths[0] = w + lx.pos += w + return r +} + +// ignore skips over the pending input before this point. +func (lx *lexer) ignore() { + lx.start = lx.pos +} + +// backup steps back one rune. Can be called 4 times between calls to next. +func (lx *lexer) backup() { + if lx.atEOF { + lx.atEOF = false + return + } + if lx.nprev < 1 { + panic("BUG in lexer: backed up too far") + } + w := lx.prevWidths[0] + lx.prevWidths[0] = lx.prevWidths[1] + lx.prevWidths[1] = lx.prevWidths[2] + lx.prevWidths[2] = lx.prevWidths[3] + lx.nprev-- + + lx.pos -= w + if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' { + lx.line-- + } +} + +// accept consumes the next rune if it's equal to `valid`. +func (lx *lexer) accept(valid rune) bool { + if lx.next() == valid { + return true + } + lx.backup() + return false +} + +// peek returns but does not consume the next rune in the input. +func (lx *lexer) peek() rune { + r := lx.next() + lx.backup() + return r +} + +// skip ignores all input that matches the given predicate. +func (lx *lexer) skip(pred func(rune) bool) { + for { + r := lx.next() + if pred(r) { + continue + } + lx.backup() + lx.ignore() + return + } +} + +// error stops all lexing by emitting an error and returning `nil`. +// +// Note that any value that is a character is escaped if it's a special +// character (newlines, tabs, etc.). +func (lx *lexer) error(err error) stateFn { + if lx.atEOF { + return lx.errorPrevLine(err) + } + lx.items <- item{typ: itemError, pos: lx.getPos(), err: err} + return nil +} + +// errorfPrevline is like error(), but sets the position to the last column of +// the previous line. +// +// This is so that unexpected EOF or NL errors don't show on a new blank line. +func (lx *lexer) errorPrevLine(err error) stateFn { + pos := lx.getPos() + pos.Line-- + pos.Len = 1 + pos.Start = lx.pos - 1 + lx.items <- item{typ: itemError, pos: pos, err: err} + return nil +} + +// errorPos is like error(), but allows explicitly setting the position. +func (lx *lexer) errorPos(start, length int, err error) stateFn { + pos := lx.getPos() + pos.Start = start + pos.Len = length + lx.items <- item{typ: itemError, pos: pos, err: err} + return nil +} + +// errorf is like error, and creates a new error. +func (lx *lexer) errorf(format string, values ...interface{}) stateFn { + if lx.atEOF { + pos := lx.getPos() + pos.Line-- + pos.Len = 1 + pos.Start = lx.pos - 1 + lx.items <- item{typ: itemError, pos: pos, err: fmt.Errorf(format, values...)} + return nil + } + lx.items <- item{typ: itemError, pos: lx.getPos(), err: fmt.Errorf(format, values...)} + return nil +} + +func (lx *lexer) errorControlChar(cc rune) stateFn { + return lx.errorPos(lx.pos-1, 1, errLexControl{cc}) +} + +// lexTop consumes elements at the top level of TOML data. +func lexTop(lx *lexer) stateFn { + r := lx.next() + if isWhitespace(r) || isNL(r) { + return lexSkip(lx, lexTop) + } + switch r { + case '#': + lx.push(lexTop) + return lexCommentStart + case '[': + return lexTableStart + case eof: + if lx.pos > lx.start { + return lx.errorf("unexpected EOF") + } + lx.emit(itemEOF) + return nil + } + + // At this point, the only valid item can be a key, so we back up + // and let the key lexer do the rest. + lx.backup() + lx.push(lexTopEnd) + return lexKeyStart +} + +// lexTopEnd is entered whenever a top-level item has been consumed. (A value +// or a table.) It must see only whitespace, and will turn back to lexTop +// upon a newline. If it sees EOF, it will quit the lexer successfully. +func lexTopEnd(lx *lexer) stateFn { + r := lx.next() + switch { + case r == '#': + // a comment will read to a newline for us. + lx.push(lexTop) + return lexCommentStart + case isWhitespace(r): + return lexTopEnd + case isNL(r): + lx.ignore() + return lexTop + case r == eof: + lx.emit(itemEOF) + return nil + } + return lx.errorf( + "expected a top-level item to end with a newline, comment, or EOF, but got %q instead", + r) +} + +// lexTable lexes the beginning of a table. Namely, it makes sure that +// it starts with a character other than '.' and ']'. +// It assumes that '[' has already been consumed. +// It also handles the case that this is an item in an array of tables. +// e.g., '[[name]]'. +func lexTableStart(lx *lexer) stateFn { + if lx.peek() == '[' { + lx.next() + lx.emit(itemArrayTableStart) + lx.push(lexArrayTableEnd) + } else { + lx.emit(itemTableStart) + lx.push(lexTableEnd) + } + return lexTableNameStart +} + +func lexTableEnd(lx *lexer) stateFn { + lx.emit(itemTableEnd) + return lexTopEnd +} + +func lexArrayTableEnd(lx *lexer) stateFn { + if r := lx.next(); r != ']' { + return lx.errorf("expected end of table array name delimiter ']', but got %q instead", r) + } + lx.emit(itemArrayTableEnd) + return lexTopEnd +} + +func lexTableNameStart(lx *lexer) stateFn { + lx.skip(isWhitespace) + switch r := lx.peek(); { + case r == ']' || r == eof: + return lx.errorf("unexpected end of table name (table names cannot be empty)") + case r == '.': + return lx.errorf("unexpected table separator (table names cannot be empty)") + case r == '"' || r == '\'': + lx.ignore() + lx.push(lexTableNameEnd) + return lexQuotedName + default: + lx.push(lexTableNameEnd) + return lexBareName + } +} + +// lexTableNameEnd reads the end of a piece of a table name, optionally +// consuming whitespace. +func lexTableNameEnd(lx *lexer) stateFn { + lx.skip(isWhitespace) + switch r := lx.next(); { + case isWhitespace(r): + return lexTableNameEnd + case r == '.': + lx.ignore() + return lexTableNameStart + case r == ']': + return lx.pop() + default: + return lx.errorf("expected '.' or ']' to end table name, but got %q instead", r) + } +} + +// lexBareName lexes one part of a key or table. +// +// It assumes that at least one valid character for the table has already been +// read. +// +// Lexes only one part, e.g. only 'a' inside 'a.b'. +func lexBareName(lx *lexer) stateFn { + r := lx.next() + if isBareKeyChar(r) { + return lexBareName + } + lx.backup() + lx.emit(itemText) + return lx.pop() +} + +// lexBareName lexes one part of a key or table. +// +// It assumes that at least one valid character for the table has already been +// read. +// +// Lexes only one part, e.g. only '"a"' inside '"a".b'. +func lexQuotedName(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r): + return lexSkip(lx, lexValue) + case r == '"': + lx.ignore() // ignore the '"' + return lexString + case r == '\'': + lx.ignore() // ignore the "'" + return lexRawString + case r == eof: + return lx.errorf("unexpected EOF; expected value") + default: + return lx.errorf("expected value but found %q instead", r) + } +} + +// lexKeyStart consumes all key parts until a '='. +func lexKeyStart(lx *lexer) stateFn { + lx.skip(isWhitespace) + switch r := lx.peek(); { + case r == '=' || r == eof: + return lx.errorf("unexpected '=': key name appears blank") + case r == '.': + return lx.errorf("unexpected '.': keys cannot start with a '.'") + case r == '"' || r == '\'': + lx.ignore() + fallthrough + default: // Bare key + lx.emit(itemKeyStart) + return lexKeyNameStart + } +} + +func lexKeyNameStart(lx *lexer) stateFn { + lx.skip(isWhitespace) + switch r := lx.peek(); { + case r == '=' || r == eof: + return lx.errorf("unexpected '='") + case r == '.': + return lx.errorf("unexpected '.'") + case r == '"' || r == '\'': + lx.ignore() + lx.push(lexKeyEnd) + return lexQuotedName + default: + lx.push(lexKeyEnd) + return lexBareName + } +} + +// lexKeyEnd consumes the end of a key and trims whitespace (up to the key +// separator). +func lexKeyEnd(lx *lexer) stateFn { + lx.skip(isWhitespace) + switch r := lx.next(); { + case isWhitespace(r): + return lexSkip(lx, lexKeyEnd) + case r == eof: + return lx.errorf("unexpected EOF; expected key separator '='") + case r == '.': + lx.ignore() + return lexKeyNameStart + case r == '=': + lx.emit(itemKeyEnd) + return lexSkip(lx, lexValue) + default: + return lx.errorf("expected '.' or '=', but got %q instead", r) + } +} + +// lexValue starts the consumption of a value anywhere a value is expected. +// lexValue will ignore whitespace. +// After a value is lexed, the last state on the next is popped and returned. +func lexValue(lx *lexer) stateFn { + // We allow whitespace to precede a value, but NOT newlines. + // In array syntax, the array states are responsible for ignoring newlines. + r := lx.next() + switch { + case isWhitespace(r): + return lexSkip(lx, lexValue) + case isDigit(r): + lx.backup() // avoid an extra state and use the same as above + return lexNumberOrDateStart + } + switch r { + case '[': + lx.ignore() + lx.emit(itemArray) + return lexArrayValue + case '{': + lx.ignore() + lx.emit(itemInlineTableStart) + return lexInlineTableValue + case '"': + if lx.accept('"') { + if lx.accept('"') { + lx.ignore() // Ignore """ + return lexMultilineString + } + lx.backup() + } + lx.ignore() // ignore the '"' + return lexString + case '\'': + if lx.accept('\'') { + if lx.accept('\'') { + lx.ignore() // Ignore """ + return lexMultilineRawString + } + lx.backup() + } + lx.ignore() // ignore the "'" + return lexRawString + case '.': // special error case, be kind to users + return lx.errorf("floats must start with a digit, not '.'") + case 'i', 'n': + if (lx.accept('n') && lx.accept('f')) || (lx.accept('a') && lx.accept('n')) { + lx.emit(itemFloat) + return lx.pop() + } + case '-', '+': + return lexDecimalNumberStart + } + if unicode.IsLetter(r) { + // Be permissive here; lexBool will give a nice error if the + // user wrote something like + // x = foo + // (i.e. not 'true' or 'false' but is something else word-like.) + lx.backup() + return lexBool + } + if r == eof { + return lx.errorf("unexpected EOF; expected value") + } + return lx.errorf("expected value but found %q instead", r) +} + +// lexArrayValue consumes one value in an array. It assumes that '[' or ',' +// have already been consumed. All whitespace and newlines are ignored. +func lexArrayValue(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r) || isNL(r): + return lexSkip(lx, lexArrayValue) + case r == '#': + lx.push(lexArrayValue) + return lexCommentStart + case r == ',': + return lx.errorf("unexpected comma") + case r == ']': + return lexArrayEnd + } + + lx.backup() + lx.push(lexArrayValueEnd) + return lexValue +} + +// lexArrayValueEnd consumes everything between the end of an array value and +// the next value (or the end of the array): it ignores whitespace and newlines +// and expects either a ',' or a ']'. +func lexArrayValueEnd(lx *lexer) stateFn { + switch r := lx.next(); { + case isWhitespace(r) || isNL(r): + return lexSkip(lx, lexArrayValueEnd) + case r == '#': + lx.push(lexArrayValueEnd) + return lexCommentStart + case r == ',': + lx.ignore() + return lexArrayValue // move on to the next value + case r == ']': + return lexArrayEnd + default: + return lx.errorf("expected a comma (',') or array terminator (']'), but got %s", runeOrEOF(r)) + } +} + +// lexArrayEnd finishes the lexing of an array. +// It assumes that a ']' has just been consumed. +func lexArrayEnd(lx *lexer) stateFn { + lx.ignore() + lx.emit(itemArrayEnd) + return lx.pop() +} + +// lexInlineTableValue consumes one key/value pair in an inline table. +// It assumes that '{' or ',' have already been consumed. Whitespace is ignored. +func lexInlineTableValue(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r): + return lexSkip(lx, lexInlineTableValue) + case isNL(r): + return lx.errorPrevLine(errLexInlineTableNL{}) + case r == '#': + lx.push(lexInlineTableValue) + return lexCommentStart + case r == ',': + return lx.errorf("unexpected comma") + case r == '}': + return lexInlineTableEnd + } + lx.backup() + lx.push(lexInlineTableValueEnd) + return lexKeyStart +} + +// lexInlineTableValueEnd consumes everything between the end of an inline table +// key/value pair and the next pair (or the end of the table): +// it ignores whitespace and expects either a ',' or a '}'. +func lexInlineTableValueEnd(lx *lexer) stateFn { + switch r := lx.next(); { + case isWhitespace(r): + return lexSkip(lx, lexInlineTableValueEnd) + case isNL(r): + return lx.errorPrevLine(errLexInlineTableNL{}) + case r == '#': + lx.push(lexInlineTableValueEnd) + return lexCommentStart + case r == ',': + lx.ignore() + lx.skip(isWhitespace) + if lx.peek() == '}' { + return lx.errorf("trailing comma not allowed in inline tables") + } + return lexInlineTableValue + case r == '}': + return lexInlineTableEnd + default: + return lx.errorf("expected a comma or an inline table terminator '}', but got %s instead", runeOrEOF(r)) + } +} + +func runeOrEOF(r rune) string { + if r == eof { + return "end of file" + } + return "'" + string(r) + "'" +} + +// lexInlineTableEnd finishes the lexing of an inline table. +// It assumes that a '}' has just been consumed. +func lexInlineTableEnd(lx *lexer) stateFn { + lx.ignore() + lx.emit(itemInlineTableEnd) + return lx.pop() +} + +// lexString consumes the inner contents of a string. It assumes that the +// beginning '"' has already been consumed and ignored. +func lexString(lx *lexer) stateFn { + r := lx.next() + switch { + case r == eof: + return lx.errorf(`unexpected EOF; expected '"'`) + case isNL(r): + return lx.errorPrevLine(errLexStringNL{}) + case r == '\\': + lx.push(lexString) + return lexStringEscape + case r == '"': + lx.backup() + lx.emit(itemString) + lx.next() + lx.ignore() + return lx.pop() + } + return lexString +} + +// lexMultilineString consumes the inner contents of a string. It assumes that +// the beginning '"""' has already been consumed and ignored. +func lexMultilineString(lx *lexer) stateFn { + r := lx.next() + switch r { + default: + return lexMultilineString + case eof: + return lx.errorf(`unexpected EOF; expected '"""'`) + case '\\': + return lexMultilineStringEscape + case '"': + /// Found " → try to read two more "". + if lx.accept('"') { + if lx.accept('"') { + /// Peek ahead: the string can contain " and "", including at the + /// end: """str""""" + /// 6 or more at the end, however, is an error. + if lx.peek() == '"' { + /// Check if we already lexed 5 's; if so we have 6 now, and + /// that's just too many man! + if strings.HasSuffix(lx.current(), `"""""`) { + return lx.errorf(`unexpected '""""""'`) + } + lx.backup() + lx.backup() + return lexMultilineString + } + + lx.backup() /// backup: don't include the """ in the item. + lx.backup() + lx.backup() + lx.emit(itemMultilineString) + lx.next() /// Read over ''' again and discard it. + lx.next() + lx.next() + lx.ignore() + return lx.pop() + } + lx.backup() + } + return lexMultilineString + } +} + +// lexRawString consumes a raw string. Nothing can be escaped in such a string. +// It assumes that the beginning "'" has already been consumed and ignored. +func lexRawString(lx *lexer) stateFn { + r := lx.next() + switch { + default: + return lexRawString + case r == eof: + return lx.errorf(`unexpected EOF; expected "'"`) + case isNL(r): + return lx.errorPrevLine(errLexStringNL{}) + case r == '\'': + lx.backup() + lx.emit(itemRawString) + lx.next() + lx.ignore() + return lx.pop() + } +} + +// lexMultilineRawString consumes a raw string. Nothing can be escaped in such +// a string. It assumes that the beginning "'''" has already been consumed and +// ignored. +func lexMultilineRawString(lx *lexer) stateFn { + r := lx.next() + switch r { + default: + return lexMultilineRawString + case eof: + return lx.errorf(`unexpected EOF; expected "'''"`) + case '\'': + /// Found ' → try to read two more ''. + if lx.accept('\'') { + if lx.accept('\'') { + /// Peek ahead: the string can contain ' and '', including at the + /// end: '''str''''' + /// 6 or more at the end, however, is an error. + if lx.peek() == '\'' { + /// Check if we already lexed 5 's; if so we have 6 now, and + /// that's just too many man! + if strings.HasSuffix(lx.current(), "'''''") { + return lx.errorf(`unexpected "''''''"`) + } + lx.backup() + lx.backup() + return lexMultilineRawString + } + + lx.backup() /// backup: don't include the ''' in the item. + lx.backup() + lx.backup() + lx.emit(itemRawMultilineString) + lx.next() /// Read over ''' again and discard it. + lx.next() + lx.next() + lx.ignore() + return lx.pop() + } + lx.backup() + } + return lexMultilineRawString + } +} + +// lexMultilineStringEscape consumes an escaped character. It assumes that the +// preceding '\\' has already been consumed. +func lexMultilineStringEscape(lx *lexer) stateFn { + // Handle the special case first: + if isNL(lx.next()) { + return lexMultilineString + } + lx.backup() + lx.push(lexMultilineString) + return lexStringEscape(lx) +} + +func lexStringEscape(lx *lexer) stateFn { + r := lx.next() + switch r { + case 'b': + fallthrough + case 't': + fallthrough + case 'n': + fallthrough + case 'f': + fallthrough + case 'r': + fallthrough + case '"': + fallthrough + case ' ', '\t': + // Inside """ .. """ strings you can use \ to escape newlines, and any + // amount of whitespace can be between the \ and \n. + fallthrough + case '\\': + return lx.pop() + case 'u': + return lexShortUnicodeEscape + case 'U': + return lexLongUnicodeEscape + } + return lx.error(errLexEscape{r}) +} + +func lexShortUnicodeEscape(lx *lexer) stateFn { + var r rune + for i := 0; i < 4; i++ { + r = lx.next() + if !isHexadecimal(r) { + return lx.errorf( + `expected four hexadecimal digits after '\u', but got %q instead`, + lx.current()) + } + } + return lx.pop() +} + +func lexLongUnicodeEscape(lx *lexer) stateFn { + var r rune + for i := 0; i < 8; i++ { + r = lx.next() + if !isHexadecimal(r) { + return lx.errorf( + `expected eight hexadecimal digits after '\U', but got %q instead`, + lx.current()) + } + } + return lx.pop() +} + +// lexNumberOrDateStart processes the first character of a value which begins +// with a digit. It exists to catch values starting with '0', so that +// lexBaseNumberOrDate can differentiate base prefixed integers from other +// types. +func lexNumberOrDateStart(lx *lexer) stateFn { + r := lx.next() + switch r { + case '0': + return lexBaseNumberOrDate + } + + if !isDigit(r) { + // The only way to reach this state is if the value starts + // with a digit, so specifically treat anything else as an + // error. + return lx.errorf("expected a digit but got %q", r) + } + + return lexNumberOrDate +} + +// lexNumberOrDate consumes either an integer, float or datetime. +func lexNumberOrDate(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexNumberOrDate + } + switch r { + case '-', ':': + return lexDatetime + case '_': + return lexDecimalNumber + case '.', 'e', 'E': + return lexFloat + } + + lx.backup() + lx.emit(itemInteger) + return lx.pop() +} + +// lexDatetime consumes a Datetime, to a first approximation. +// The parser validates that it matches one of the accepted formats. +func lexDatetime(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexDatetime + } + switch r { + case '-', ':', 'T', 't', ' ', '.', 'Z', 'z', '+': + return lexDatetime + } + + lx.backup() + lx.emitTrim(itemDatetime) + return lx.pop() +} + +// lexHexInteger consumes a hexadecimal integer after seeing the '0x' prefix. +func lexHexInteger(lx *lexer) stateFn { + r := lx.next() + if isHexadecimal(r) { + return lexHexInteger + } + switch r { + case '_': + return lexHexInteger + } + + lx.backup() + lx.emit(itemInteger) + return lx.pop() +} + +// lexOctalInteger consumes an octal integer after seeing the '0o' prefix. +func lexOctalInteger(lx *lexer) stateFn { + r := lx.next() + if isOctal(r) { + return lexOctalInteger + } + switch r { + case '_': + return lexOctalInteger + } + + lx.backup() + lx.emit(itemInteger) + return lx.pop() +} + +// lexBinaryInteger consumes a binary integer after seeing the '0b' prefix. +func lexBinaryInteger(lx *lexer) stateFn { + r := lx.next() + if isBinary(r) { + return lexBinaryInteger + } + switch r { + case '_': + return lexBinaryInteger + } + + lx.backup() + lx.emit(itemInteger) + return lx.pop() +} + +// lexDecimalNumber consumes a decimal float or integer. +func lexDecimalNumber(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexDecimalNumber + } + switch r { + case '.', 'e', 'E': + return lexFloat + case '_': + return lexDecimalNumber + } + + lx.backup() + lx.emit(itemInteger) + return lx.pop() +} + +// lexDecimalNumber consumes the first digit of a number beginning with a sign. +// It assumes the sign has already been consumed. Values which start with a sign +// are only allowed to be decimal integers or floats. +// +// The special "nan" and "inf" values are also recognized. +func lexDecimalNumberStart(lx *lexer) stateFn { + r := lx.next() + + // Special error cases to give users better error messages + switch r { + case 'i': + if !lx.accept('n') || !lx.accept('f') { + return lx.errorf("invalid float: '%s'", lx.current()) + } + lx.emit(itemFloat) + return lx.pop() + case 'n': + if !lx.accept('a') || !lx.accept('n') { + return lx.errorf("invalid float: '%s'", lx.current()) + } + lx.emit(itemFloat) + return lx.pop() + case '0': + p := lx.peek() + switch p { + case 'b', 'o', 'x': + return lx.errorf("cannot use sign with non-decimal numbers: '%s%c'", lx.current(), p) + } + case '.': + return lx.errorf("floats must start with a digit, not '.'") + } + + if isDigit(r) { + return lexDecimalNumber + } + + return lx.errorf("expected a digit but got %q", r) +} + +// lexBaseNumberOrDate differentiates between the possible values which +// start with '0'. It assumes that before reaching this state, the initial '0' +// has been consumed. +func lexBaseNumberOrDate(lx *lexer) stateFn { + r := lx.next() + // Note: All datetimes start with at least two digits, so we don't + // handle date characters (':', '-', etc.) here. + if isDigit(r) { + return lexNumberOrDate + } + switch r { + case '_': + // Can only be decimal, because there can't be an underscore + // between the '0' and the base designator, and dates can't + // contain underscores. + return lexDecimalNumber + case '.', 'e', 'E': + return lexFloat + case 'b': + r = lx.peek() + if !isBinary(r) { + lx.errorf("not a binary number: '%s%c'", lx.current(), r) + } + return lexBinaryInteger + case 'o': + r = lx.peek() + if !isOctal(r) { + lx.errorf("not an octal number: '%s%c'", lx.current(), r) + } + return lexOctalInteger + case 'x': + r = lx.peek() + if !isHexadecimal(r) { + lx.errorf("not a hexidecimal number: '%s%c'", lx.current(), r) + } + return lexHexInteger + } + + lx.backup() + lx.emit(itemInteger) + return lx.pop() +} + +// lexFloat consumes the elements of a float. It allows any sequence of +// float-like characters, so floats emitted by the lexer are only a first +// approximation and must be validated by the parser. +func lexFloat(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexFloat + } + switch r { + case '_', '.', '-', '+', 'e', 'E': + return lexFloat + } + + lx.backup() + lx.emit(itemFloat) + return lx.pop() +} + +// lexBool consumes a bool string: 'true' or 'false. +func lexBool(lx *lexer) stateFn { + var rs []rune + for { + r := lx.next() + if !unicode.IsLetter(r) { + lx.backup() + break + } + rs = append(rs, r) + } + s := string(rs) + switch s { + case "true", "false": + lx.emit(itemBool) + return lx.pop() + } + return lx.errorf("expected value but found %q instead", s) +} + +// lexCommentStart begins the lexing of a comment. It will emit +// itemCommentStart and consume no characters, passing control to lexComment. +func lexCommentStart(lx *lexer) stateFn { + lx.ignore() + lx.emit(itemCommentStart) + return lexComment +} + +// lexComment lexes an entire comment. It assumes that '#' has been consumed. +// It will consume *up to* the first newline character, and pass control +// back to the last state on the stack. +func lexComment(lx *lexer) stateFn { + switch r := lx.next(); { + case isNL(r) || r == eof: + lx.backup() + lx.emit(itemText) + return lx.pop() + default: + return lexComment + } +} + +// lexSkip ignores all slurped input and moves on to the next state. +func lexSkip(lx *lexer, nextState stateFn) stateFn { + lx.ignore() + return nextState +} + +func (s stateFn) String() string { + name := runtime.FuncForPC(reflect.ValueOf(s).Pointer()).Name() + if i := strings.LastIndexByte(name, '.'); i > -1 { + name = name[i+1:] + } + if s == nil { + name = "" + } + return name + "()" +} + +func (itype itemType) String() string { + switch itype { + case itemError: + return "Error" + case itemNIL: + return "NIL" + case itemEOF: + return "EOF" + case itemText: + return "Text" + case itemString, itemRawString, itemMultilineString, itemRawMultilineString: + return "String" + case itemBool: + return "Bool" + case itemInteger: + return "Integer" + case itemFloat: + return "Float" + case itemDatetime: + return "DateTime" + case itemTableStart: + return "TableStart" + case itemTableEnd: + return "TableEnd" + case itemKeyStart: + return "KeyStart" + case itemKeyEnd: + return "KeyEnd" + case itemArray: + return "Array" + case itemArrayEnd: + return "ArrayEnd" + case itemCommentStart: + return "CommentStart" + case itemInlineTableStart: + return "InlineTableStart" + case itemInlineTableEnd: + return "InlineTableEnd" + } + panic(fmt.Sprintf("BUG: Unknown type '%d'.", int(itype))) +} + +func (item item) String() string { + return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val) +} + +func isWhitespace(r rune) bool { return r == '\t' || r == ' ' } +func isNL(r rune) bool { return r == '\n' || r == '\r' } +func isControl(r rune) bool { // Control characters except \t, \r, \n + switch r { + case '\t', '\r', '\n': + return false + default: + return (r >= 0x00 && r <= 0x1f) || r == 0x7f + } +} +func isDigit(r rune) bool { return r >= '0' && r <= '9' } +func isBinary(r rune) bool { return r == '0' || r == '1' } +func isOctal(r rune) bool { return r >= '0' && r <= '7' } +func isHexadecimal(r rune) bool { + return (r >= '0' && r <= '9') || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F') +} +func isBareKeyChar(r rune) bool { + return (r >= 'A' && r <= 'Z') || + (r >= 'a' && r <= 'z') || + (r >= '0' && r <= '9') || + r == '_' || r == '-' +} diff --git a/vendor/github.com/BurntSushi/toml/meta.go b/vendor/github.com/BurntSushi/toml/meta.go new file mode 100644 index 0000000000..868619fb97 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/meta.go @@ -0,0 +1,120 @@ +package toml + +import ( + "strings" +) + +// MetaData allows access to meta information about TOML data that's not +// accessible otherwise. +// +// It allows checking if a key is defined in the TOML data, whether any keys +// were undecoded, and the TOML type of a key. +type MetaData struct { + context Key // Used only during decoding. + + mapping map[string]interface{} + types map[string]tomlType + keys []Key + decoded map[string]struct{} +} + +// IsDefined reports if the key exists in the TOML data. +// +// The key should be specified hierarchically, for example to access the TOML +// key "a.b.c" you would use IsDefined("a", "b", "c"). Keys are case sensitive. +// +// Returns false for an empty key. +func (md *MetaData) IsDefined(key ...string) bool { + if len(key) == 0 { + return false + } + + var ( + hash map[string]interface{} + ok bool + hashOrVal interface{} = md.mapping + ) + for _, k := range key { + if hash, ok = hashOrVal.(map[string]interface{}); !ok { + return false + } + if hashOrVal, ok = hash[k]; !ok { + return false + } + } + return true +} + +// Type returns a string representation of the type of the key specified. +// +// Type will return the empty string if given an empty key or a key that does +// not exist. Keys are case sensitive. +func (md *MetaData) Type(key ...string) string { + if typ, ok := md.types[Key(key).String()]; ok { + return typ.typeString() + } + return "" +} + +// Keys returns a slice of every key in the TOML data, including key groups. +// +// Each key is itself a slice, where the first element is the top of the +// hierarchy and the last is the most specific. The list will have the same +// order as the keys appeared in the TOML data. +// +// All keys returned are non-empty. +func (md *MetaData) Keys() []Key { + return md.keys +} + +// Undecoded returns all keys that have not been decoded in the order in which +// they appear in the original TOML document. +// +// This includes keys that haven't been decoded because of a Primitive value. +// Once the Primitive value is decoded, the keys will be considered decoded. +// +// Also note that decoding into an empty interface will result in no decoding, +// and so no keys will be considered decoded. +// +// In this sense, the Undecoded keys correspond to keys in the TOML document +// that do not have a concrete type in your representation. +func (md *MetaData) Undecoded() []Key { + undecoded := make([]Key, 0, len(md.keys)) + for _, key := range md.keys { + if _, ok := md.decoded[key.String()]; !ok { + undecoded = append(undecoded, key) + } + } + return undecoded +} + +// Key represents any TOML key, including key groups. Use (MetaData).Keys to get +// values of this type. +type Key []string + +func (k Key) String() string { + ss := make([]string, len(k)) + for i := range k { + ss[i] = k.maybeQuoted(i) + } + return strings.Join(ss, ".") +} + +func (k Key) maybeQuoted(i int) string { + if k[i] == "" { + return `""` + } + for _, c := range k[i] { + if !isBareKeyChar(c) { + return `"` + dblQuotedReplacer.Replace(k[i]) + `"` + } + } + return k[i] +} + +func (k Key) add(piece string) Key { + newKey := make(Key, len(k)+1) + copy(newKey, k) + newKey[len(k)] = piece + return newKey +} diff --git a/vendor/github.com/BurntSushi/toml/parse.go b/vendor/github.com/BurntSushi/toml/parse.go new file mode 100644 index 0000000000..8269cca170 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/parse.go @@ -0,0 +1,763 @@ +package toml + +import ( + "fmt" + "strconv" + "strings" + "time" + "unicode/utf8" + + "github.com/BurntSushi/toml/internal" +) + +type parser struct { + lx *lexer + context Key // Full key for the current hash in scope. + currentKey string // Base key name for everything except hashes. + pos Position // Current position in the TOML file. + + ordered []Key // List of keys in the order that they appear in the TOML data. + mapping map[string]interface{} // Map keyname → key value. + types map[string]tomlType // Map keyname → TOML type. + implicits map[string]struct{} // Record implicit keys (e.g. "key.group.names"). +} + +func parse(data string) (p *parser, err error) { + defer func() { + if r := recover(); r != nil { + if pErr, ok := r.(ParseError); ok { + pErr.input = data + err = pErr + return + } + panic(r) + } + }() + + // Read over BOM; do this here as the lexer calls utf8.DecodeRuneInString() + // which mangles stuff. + if strings.HasPrefix(data, "\xff\xfe") || strings.HasPrefix(data, "\xfe\xff") { + data = data[2:] + } + + // Examine first few bytes for NULL bytes; this probably means it's a UTF-16 + // file (second byte in surrogate pair being NULL). Again, do this here to + // avoid having to deal with UTF-8/16 stuff in the lexer. + ex := 6 + if len(data) < 6 { + ex = len(data) + } + if i := strings.IndexRune(data[:ex], 0); i > -1 { + return nil, ParseError{ + Message: "files cannot contain NULL bytes; probably using UTF-16; TOML files must be UTF-8", + Position: Position{Line: 1, Start: i, Len: 1}, + Line: 1, + input: data, + } + } + + p = &parser{ + mapping: make(map[string]interface{}), + types: make(map[string]tomlType), + lx: lex(data), + ordered: make([]Key, 0), + implicits: make(map[string]struct{}), + } + for { + item := p.next() + if item.typ == itemEOF { + break + } + p.topLevel(item) + } + + return p, nil +} + +func (p *parser) panicItemf(it item, format string, v ...interface{}) { + panic(ParseError{ + Message: fmt.Sprintf(format, v...), + Position: it.pos, + Line: it.pos.Len, + LastKey: p.current(), + }) +} + +func (p *parser) panicf(format string, v ...interface{}) { + panic(ParseError{ + Message: fmt.Sprintf(format, v...), + Position: p.pos, + Line: p.pos.Line, + LastKey: p.current(), + }) +} + +func (p *parser) next() item { + it := p.lx.nextItem() + //fmt.Printf("ITEM %-18s line %-3d │ %q\n", it.typ, it.line, it.val) + if it.typ == itemError { + if it.err != nil { + panic(ParseError{ + Position: it.pos, + Line: it.pos.Line, + LastKey: p.current(), + err: it.err, + }) + } + + p.panicItemf(it, "%s", it.val) + } + return it +} + +func (p *parser) nextPos() item { + it := p.next() + p.pos = it.pos + return it +} + +func (p *parser) bug(format string, v ...interface{}) { + panic(fmt.Sprintf("BUG: "+format+"\n\n", v...)) +} + +func (p *parser) expect(typ itemType) item { + it := p.next() + p.assertEqual(typ, it.typ) + return it +} + +func (p *parser) assertEqual(expected, got itemType) { + if expected != got { + p.bug("Expected '%s' but got '%s'.", expected, got) + } +} + +func (p *parser) topLevel(item item) { + switch item.typ { + case itemCommentStart: // # .. + p.expect(itemText) + case itemTableStart: // [ .. ] + name := p.nextPos() + + var key Key + for ; name.typ != itemTableEnd && name.typ != itemEOF; name = p.next() { + key = append(key, p.keyString(name)) + } + p.assertEqual(itemTableEnd, name.typ) + + p.addContext(key, false) + p.setType("", tomlHash) + p.ordered = append(p.ordered, key) + case itemArrayTableStart: // [[ .. ]] + name := p.nextPos() + + var key Key + for ; name.typ != itemArrayTableEnd && name.typ != itemEOF; name = p.next() { + key = append(key, p.keyString(name)) + } + p.assertEqual(itemArrayTableEnd, name.typ) + + p.addContext(key, true) + p.setType("", tomlArrayHash) + p.ordered = append(p.ordered, key) + case itemKeyStart: // key = .. + outerContext := p.context + /// Read all the key parts (e.g. 'a' and 'b' in 'a.b') + k := p.nextPos() + var key Key + for ; k.typ != itemKeyEnd && k.typ != itemEOF; k = p.next() { + key = append(key, p.keyString(k)) + } + p.assertEqual(itemKeyEnd, k.typ) + + /// The current key is the last part. + p.currentKey = key[len(key)-1] + + /// All the other parts (if any) are the context; need to set each part + /// as implicit. + context := key[:len(key)-1] + for i := range context { + p.addImplicitContext(append(p.context, context[i:i+1]...)) + } + + /// Set value. + val, typ := p.value(p.next(), false) + p.set(p.currentKey, val, typ) + p.ordered = append(p.ordered, p.context.add(p.currentKey)) + + /// Remove the context we added (preserving any context from [tbl] lines). + p.context = outerContext + p.currentKey = "" + default: + p.bug("Unexpected type at top level: %s", item.typ) + } +} + +// Gets a string for a key (or part of a key in a table name). +func (p *parser) keyString(it item) string { + switch it.typ { + case itemText: + return it.val + case itemString, itemMultilineString, + itemRawString, itemRawMultilineString: + s, _ := p.value(it, false) + return s.(string) + default: + p.bug("Unexpected key type: %s", it.typ) + } + panic("unreachable") +} + +var datetimeRepl = strings.NewReplacer( + "z", "Z", + "t", "T", + " ", "T") + +// value translates an expected value from the lexer into a Go value wrapped +// as an empty interface. +func (p *parser) value(it item, parentIsArray bool) (interface{}, tomlType) { + switch it.typ { + case itemString: + return p.replaceEscapes(it, it.val), p.typeOfPrimitive(it) + case itemMultilineString: + return p.replaceEscapes(it, stripFirstNewline(stripEscapedNewlines(it.val))), p.typeOfPrimitive(it) + case itemRawString: + return it.val, p.typeOfPrimitive(it) + case itemRawMultilineString: + return stripFirstNewline(it.val), p.typeOfPrimitive(it) + case itemInteger: + return p.valueInteger(it) + case itemFloat: + return p.valueFloat(it) + case itemBool: + switch it.val { + case "true": + return true, p.typeOfPrimitive(it) + case "false": + return false, p.typeOfPrimitive(it) + default: + p.bug("Expected boolean value, but got '%s'.", it.val) + } + case itemDatetime: + return p.valueDatetime(it) + case itemArray: + return p.valueArray(it) + case itemInlineTableStart: + return p.valueInlineTable(it, parentIsArray) + default: + p.bug("Unexpected value type: %s", it.typ) + } + panic("unreachable") +} + +func (p *parser) valueInteger(it item) (interface{}, tomlType) { + if !numUnderscoresOK(it.val) { + p.panicItemf(it, "Invalid integer %q: underscores must be surrounded by digits", it.val) + } + if numHasLeadingZero(it.val) { + p.panicItemf(it, "Invalid integer %q: cannot have leading zeroes", it.val) + } + + num, err := strconv.ParseInt(it.val, 0, 64) + if err != nil { + // Distinguish integer values. Normally, it'd be a bug if the lexer + // provides an invalid integer, but it's possible that the number is + // out of range of valid values (which the lexer cannot determine). + // So mark the former as a bug but the latter as a legitimate user + // error. + if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange { + p.panicItemf(it, "Integer '%s' is out of the range of 64-bit signed integers.", it.val) + } else { + p.bug("Expected integer value, but got '%s'.", it.val) + } + } + return num, p.typeOfPrimitive(it) +} + +func (p *parser) valueFloat(it item) (interface{}, tomlType) { + parts := strings.FieldsFunc(it.val, func(r rune) bool { + switch r { + case '.', 'e', 'E': + return true + } + return false + }) + for _, part := range parts { + if !numUnderscoresOK(part) { + p.panicItemf(it, "Invalid float %q: underscores must be surrounded by digits", it.val) + } + } + if len(parts) > 0 && numHasLeadingZero(parts[0]) { + p.panicItemf(it, "Invalid float %q: cannot have leading zeroes", it.val) + } + if !numPeriodsOK(it.val) { + // As a special case, numbers like '123.' or '1.e2', + // which are valid as far as Go/strconv are concerned, + // must be rejected because TOML says that a fractional + // part consists of '.' followed by 1+ digits. + p.panicItemf(it, "Invalid float %q: '.' must be followed by one or more digits", it.val) + } + val := strings.Replace(it.val, "_", "", -1) + if val == "+nan" || val == "-nan" { // Go doesn't support this, but TOML spec does. + val = "nan" + } + num, err := strconv.ParseFloat(val, 64) + if err != nil { + if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange { + p.panicItemf(it, "Float '%s' is out of the range of 64-bit IEEE-754 floating-point numbers.", it.val) + } else { + p.panicItemf(it, "Invalid float value: %q", it.val) + } + } + return num, p.typeOfPrimitive(it) +} + +var dtTypes = []struct { + fmt string + zone *time.Location +}{ + {time.RFC3339Nano, time.Local}, + {"2006-01-02T15:04:05.999999999", internal.LocalDatetime}, + {"2006-01-02", internal.LocalDate}, + {"15:04:05.999999999", internal.LocalTime}, +} + +func (p *parser) valueDatetime(it item) (interface{}, tomlType) { + it.val = datetimeRepl.Replace(it.val) + var ( + t time.Time + ok bool + err error + ) + for _, dt := range dtTypes { + t, err = time.ParseInLocation(dt.fmt, it.val, dt.zone) + if err == nil { + ok = true + break + } + } + if !ok { + p.panicItemf(it, "Invalid TOML Datetime: %q.", it.val) + } + return t, p.typeOfPrimitive(it) +} + +func (p *parser) valueArray(it item) (interface{}, tomlType) { + p.setType(p.currentKey, tomlArray) + + // p.setType(p.currentKey, typ) + var ( + types []tomlType + + // Initialize to a non-nil empty slice. This makes it consistent with + // how S = [] decodes into a non-nil slice inside something like struct + // { S []string }. See #338 + array = []interface{}{} + ) + for it = p.next(); it.typ != itemArrayEnd; it = p.next() { + if it.typ == itemCommentStart { + p.expect(itemText) + continue + } + + val, typ := p.value(it, true) + array = append(array, val) + types = append(types, typ) + + // XXX: types isn't used here, we need it to record the accurate type + // information. + // + // Not entirely sure how to best store this; could use "key[0]", + // "key[1]" notation, or maybe store it on the Array type? + } + return array, tomlArray +} + +func (p *parser) valueInlineTable(it item, parentIsArray bool) (interface{}, tomlType) { + var ( + hash = make(map[string]interface{}) + outerContext = p.context + outerKey = p.currentKey + ) + + p.context = append(p.context, p.currentKey) + prevContext := p.context + p.currentKey = "" + + p.addImplicit(p.context) + p.addContext(p.context, parentIsArray) + + /// Loop over all table key/value pairs. + for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() { + if it.typ == itemCommentStart { + p.expect(itemText) + continue + } + + /// Read all key parts. + k := p.nextPos() + var key Key + for ; k.typ != itemKeyEnd && k.typ != itemEOF; k = p.next() { + key = append(key, p.keyString(k)) + } + p.assertEqual(itemKeyEnd, k.typ) + + /// The current key is the last part. + p.currentKey = key[len(key)-1] + + /// All the other parts (if any) are the context; need to set each part + /// as implicit. + context := key[:len(key)-1] + for i := range context { + p.addImplicitContext(append(p.context, context[i:i+1]...)) + } + + /// Set the value. + val, typ := p.value(p.next(), false) + p.set(p.currentKey, val, typ) + p.ordered = append(p.ordered, p.context.add(p.currentKey)) + hash[p.currentKey] = val + + /// Restore context. + p.context = prevContext + } + p.context = outerContext + p.currentKey = outerKey + return hash, tomlHash +} + +// numHasLeadingZero checks if this number has leading zeroes, allowing for '0', +// +/- signs, and base prefixes. +func numHasLeadingZero(s string) bool { + if len(s) > 1 && s[0] == '0' && !(s[1] == 'b' || s[1] == 'o' || s[1] == 'x') { // Allow 0b, 0o, 0x + return true + } + if len(s) > 2 && (s[0] == '-' || s[0] == '+') && s[1] == '0' { + return true + } + return false +} + +// numUnderscoresOK checks whether each underscore in s is surrounded by +// characters that are not underscores. +func numUnderscoresOK(s string) bool { + switch s { + case "nan", "+nan", "-nan", "inf", "-inf", "+inf": + return true + } + accept := false + for _, r := range s { + if r == '_' { + if !accept { + return false + } + } + + // isHexadecimal is a superset of all the permissable characters + // surrounding an underscore. + accept = isHexadecimal(r) + } + return accept +} + +// numPeriodsOK checks whether every period in s is followed by a digit. +func numPeriodsOK(s string) bool { + period := false + for _, r := range s { + if period && !isDigit(r) { + return false + } + period = r == '.' + } + return !period +} + +// Set the current context of the parser, where the context is either a hash or +// an array of hashes, depending on the value of the `array` parameter. +// +// Establishing the context also makes sure that the key isn't a duplicate, and +// will create implicit hashes automatically. +func (p *parser) addContext(key Key, array bool) { + var ok bool + + // Always start at the top level and drill down for our context. + hashContext := p.mapping + keyContext := make(Key, 0) + + // We only need implicit hashes for key[0:-1] + for _, k := range key[0 : len(key)-1] { + _, ok = hashContext[k] + keyContext = append(keyContext, k) + + // No key? Make an implicit hash and move on. + if !ok { + p.addImplicit(keyContext) + hashContext[k] = make(map[string]interface{}) + } + + // If the hash context is actually an array of tables, then set + // the hash context to the last element in that array. + // + // Otherwise, it better be a table, since this MUST be a key group (by + // virtue of it not being the last element in a key). + switch t := hashContext[k].(type) { + case []map[string]interface{}: + hashContext = t[len(t)-1] + case map[string]interface{}: + hashContext = t + default: + p.panicf("Key '%s' was already created as a hash.", keyContext) + } + } + + p.context = keyContext + if array { + // If this is the first element for this array, then allocate a new + // list of tables for it. + k := key[len(key)-1] + if _, ok := hashContext[k]; !ok { + hashContext[k] = make([]map[string]interface{}, 0, 4) + } + + // Add a new table. But make sure the key hasn't already been used + // for something else. + if hash, ok := hashContext[k].([]map[string]interface{}); ok { + hashContext[k] = append(hash, make(map[string]interface{})) + } else { + p.panicf("Key '%s' was already created and cannot be used as an array.", key) + } + } else { + p.setValue(key[len(key)-1], make(map[string]interface{})) + } + p.context = append(p.context, key[len(key)-1]) +} + +// set calls setValue and setType. +func (p *parser) set(key string, val interface{}, typ tomlType) { + p.setValue(key, val) + p.setType(key, typ) +} + +// setValue sets the given key to the given value in the current context. +// It will make sure that the key hasn't already been defined, account for +// implicit key groups. +func (p *parser) setValue(key string, value interface{}) { + var ( + tmpHash interface{} + ok bool + hash = p.mapping + keyContext Key + ) + for _, k := range p.context { + keyContext = append(keyContext, k) + if tmpHash, ok = hash[k]; !ok { + p.bug("Context for key '%s' has not been established.", keyContext) + } + switch t := tmpHash.(type) { + case []map[string]interface{}: + // The context is a table of hashes. Pick the most recent table + // defined as the current hash. + hash = t[len(t)-1] + case map[string]interface{}: + hash = t + default: + p.panicf("Key '%s' has already been defined.", keyContext) + } + } + keyContext = append(keyContext, key) + + if _, ok := hash[key]; ok { + // Normally redefining keys isn't allowed, but the key could have been + // defined implicitly and it's allowed to be redefined concretely. (See + // the `valid/implicit-and-explicit-after.toml` in toml-test) + // + // But we have to make sure to stop marking it as an implicit. (So that + // another redefinition provokes an error.) + // + // Note that since it has already been defined (as a hash), we don't + // want to overwrite it. So our business is done. + if p.isArray(keyContext) { + p.removeImplicit(keyContext) + hash[key] = value + return + } + if p.isImplicit(keyContext) { + p.removeImplicit(keyContext) + return + } + + // Otherwise, we have a concrete key trying to override a previous + // key, which is *always* wrong. + p.panicf("Key '%s' has already been defined.", keyContext) + } + + hash[key] = value +} + +// setType sets the type of a particular value at a given key. It should be +// called immediately AFTER setValue. +// +// Note that if `key` is empty, then the type given will be applied to the +// current context (which is either a table or an array of tables). +func (p *parser) setType(key string, typ tomlType) { + keyContext := make(Key, 0, len(p.context)+1) + keyContext = append(keyContext, p.context...) + if len(key) > 0 { // allow type setting for hashes + keyContext = append(keyContext, key) + } + // Special case to make empty keys ("" = 1) work. + // Without it it will set "" rather than `""`. + // TODO: why is this needed? And why is this only needed here? + if len(keyContext) == 0 { + keyContext = Key{""} + } + p.types[keyContext.String()] = typ +} + +// Implicit keys need to be created when tables are implied in "a.b.c.d = 1" and +// "[a.b.c]" (the "a", "b", and "c" hashes are never created explicitly). +func (p *parser) addImplicit(key Key) { p.implicits[key.String()] = struct{}{} } +func (p *parser) removeImplicit(key Key) { delete(p.implicits, key.String()) } +func (p *parser) isImplicit(key Key) bool { _, ok := p.implicits[key.String()]; return ok } +func (p *parser) isArray(key Key) bool { return p.types[key.String()] == tomlArray } +func (p *parser) addImplicitContext(key Key) { + p.addImplicit(key) + p.addContext(key, false) +} + +// current returns the full key name of the current context. +func (p *parser) current() string { + if len(p.currentKey) == 0 { + return p.context.String() + } + if len(p.context) == 0 { + return p.currentKey + } + return fmt.Sprintf("%s.%s", p.context, p.currentKey) +} + +func stripFirstNewline(s string) string { + if len(s) > 0 && s[0] == '\n' { + return s[1:] + } + if len(s) > 1 && s[0] == '\r' && s[1] == '\n' { + return s[2:] + } + return s +} + +// Remove newlines inside triple-quoted strings if a line ends with "\". +func stripEscapedNewlines(s string) string { + split := strings.Split(s, "\n") + if len(split) < 1 { + return s + } + + escNL := false // Keep track of the last non-blank line was escaped. + for i, line := range split { + line = strings.TrimRight(line, " \t\r") + + if len(line) == 0 || line[len(line)-1] != '\\' { + split[i] = strings.TrimRight(split[i], "\r") + if !escNL && i != len(split)-1 { + split[i] += "\n" + } + continue + } + + escBS := true + for j := len(line) - 1; j >= 0 && line[j] == '\\'; j-- { + escBS = !escBS + } + if escNL { + line = strings.TrimLeft(line, " \t\r") + } + escNL = !escBS + + if escBS { + split[i] += "\n" + continue + } + + split[i] = line[:len(line)-1] // Remove \ + if len(split)-1 > i { + split[i+1] = strings.TrimLeft(split[i+1], " \t\r") + } + } + return strings.Join(split, "") +} + +func (p *parser) replaceEscapes(it item, str string) string { + replaced := make([]rune, 0, len(str)) + s := []byte(str) + r := 0 + for r < len(s) { + if s[r] != '\\' { + c, size := utf8.DecodeRune(s[r:]) + r += size + replaced = append(replaced, c) + continue + } + r += 1 + if r >= len(s) { + p.bug("Escape sequence at end of string.") + return "" + } + switch s[r] { + default: + p.bug("Expected valid escape code after \\, but got %q.", s[r]) + return "" + case ' ', '\t': + p.panicItemf(it, "invalid escape: '\\%c'", s[r]) + return "" + case 'b': + replaced = append(replaced, rune(0x0008)) + r += 1 + case 't': + replaced = append(replaced, rune(0x0009)) + r += 1 + case 'n': + replaced = append(replaced, rune(0x000A)) + r += 1 + case 'f': + replaced = append(replaced, rune(0x000C)) + r += 1 + case 'r': + replaced = append(replaced, rune(0x000D)) + r += 1 + case '"': + replaced = append(replaced, rune(0x0022)) + r += 1 + case '\\': + replaced = append(replaced, rune(0x005C)) + r += 1 + case 'u': + // At this point, we know we have a Unicode escape of the form + // `uXXXX` at [r, r+5). (Because the lexer guarantees this + // for us.) + escaped := p.asciiEscapeToUnicode(it, s[r+1:r+5]) + replaced = append(replaced, escaped) + r += 5 + case 'U': + // At this point, we know we have a Unicode escape of the form + // `uXXXX` at [r, r+9). (Because the lexer guarantees this + // for us.) + escaped := p.asciiEscapeToUnicode(it, s[r+1:r+9]) + replaced = append(replaced, escaped) + r += 9 + } + } + return string(replaced) +} + +func (p *parser) asciiEscapeToUnicode(it item, bs []byte) rune { + s := string(bs) + hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32) + if err != nil { + p.bug("Could not parse '%s' as a hexadecimal number, but the lexer claims it's OK: %s", s, err) + } + if !utf8.ValidRune(rune(hex)) { + p.panicItemf(it, "Escaped character '\\u%s' is not valid UTF-8.", s) + } + return rune(hex) +} diff --git a/vendor/github.com/BurntSushi/toml/type_fields.go b/vendor/github.com/BurntSushi/toml/type_fields.go new file mode 100644 index 0000000000..254ca82e54 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/type_fields.go @@ -0,0 +1,242 @@ +package toml + +// Struct field handling is adapted from code in encoding/json: +// +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the Go distribution. + +import ( + "reflect" + "sort" + "sync" +) + +// A field represents a single field found in a struct. +type field struct { + name string // the name of the field (`toml` tag included) + tag bool // whether field has a `toml` tag + index []int // represents the depth of an anonymous field + typ reflect.Type // the type of the field +} + +// byName sorts field by name, breaking ties with depth, +// then breaking ties with "name came from toml tag", then +// breaking ties with index sequence. +type byName []field + +func (x byName) Len() int { return len(x) } + +func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byName) Less(i, j int) bool { + if x[i].name != x[j].name { + return x[i].name < x[j].name + } + if len(x[i].index) != len(x[j].index) { + return len(x[i].index) < len(x[j].index) + } + if x[i].tag != x[j].tag { + return x[i].tag + } + return byIndex(x).Less(i, j) +} + +// byIndex sorts field by index sequence. +type byIndex []field + +func (x byIndex) Len() int { return len(x) } + +func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byIndex) Less(i, j int) bool { + for k, xik := range x[i].index { + if k >= len(x[j].index) { + return false + } + if xik != x[j].index[k] { + return xik < x[j].index[k] + } + } + return len(x[i].index) < len(x[j].index) +} + +// typeFields returns a list of fields that TOML should recognize for the given +// type. The algorithm is breadth-first search over the set of structs to +// include - the top struct and then any reachable anonymous structs. +func typeFields(t reflect.Type) []field { + // Anonymous fields to explore at the current level and the next. + current := []field{} + next := []field{{typ: t}} + + // Count of queued names for current level and the next. + var count map[reflect.Type]int + var nextCount map[reflect.Type]int + + // Types already visited at an earlier level. + visited := map[reflect.Type]bool{} + + // Fields found. + var fields []field + + for len(next) > 0 { + current, next = next, current[:0] + count, nextCount = nextCount, map[reflect.Type]int{} + + for _, f := range current { + if visited[f.typ] { + continue + } + visited[f.typ] = true + + // Scan f.typ for fields to include. + for i := 0; i < f.typ.NumField(); i++ { + sf := f.typ.Field(i) + if sf.PkgPath != "" && !sf.Anonymous { // unexported + continue + } + opts := getOptions(sf.Tag) + if opts.skip { + continue + } + index := make([]int, len(f.index)+1) + copy(index, f.index) + index[len(f.index)] = i + + ft := sf.Type + if ft.Name() == "" && ft.Kind() == reflect.Ptr { + // Follow pointer. + ft = ft.Elem() + } + + // Record found field and index sequence. + if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { + tagged := opts.name != "" + name := opts.name + if name == "" { + name = sf.Name + } + fields = append(fields, field{name, tagged, index, ft}) + if count[f.typ] > 1 { + // If there were multiple instances, add a second, + // so that the annihilation code will see a duplicate. + // It only cares about the distinction between 1 or 2, + // so don't bother generating any more copies. + fields = append(fields, fields[len(fields)-1]) + } + continue + } + + // Record new anonymous struct to explore in next round. + nextCount[ft]++ + if nextCount[ft] == 1 { + f := field{name: ft.Name(), index: index, typ: ft} + next = append(next, f) + } + } + } + } + + sort.Sort(byName(fields)) + + // Delete all fields that are hidden by the Go rules for embedded fields, + // except that fields with TOML tags are promoted. + + // The fields are sorted in primary order of name, secondary order + // of field index length. Loop over names; for each name, delete + // hidden fields by choosing the one dominant field that survives. + out := fields[:0] + for advance, i := 0, 0; i < len(fields); i += advance { + // One iteration per name. + // Find the sequence of fields with the name of this first field. + fi := fields[i] + name := fi.name + for advance = 1; i+advance < len(fields); advance++ { + fj := fields[i+advance] + if fj.name != name { + break + } + } + if advance == 1 { // Only one field with this name + out = append(out, fi) + continue + } + dominant, ok := dominantField(fields[i : i+advance]) + if ok { + out = append(out, dominant) + } + } + + fields = out + sort.Sort(byIndex(fields)) + + return fields +} + +// dominantField looks through the fields, all of which are known to +// have the same name, to find the single field that dominates the +// others using Go's embedding rules, modified by the presence of +// TOML tags. If there are multiple top-level fields, the boolean +// will be false: This condition is an error in Go and we skip all +// the fields. +func dominantField(fields []field) (field, bool) { + // The fields are sorted in increasing index-length order. The winner + // must therefore be one with the shortest index length. Drop all + // longer entries, which is easy: just truncate the slice. + length := len(fields[0].index) + tagged := -1 // Index of first tagged field. + for i, f := range fields { + if len(f.index) > length { + fields = fields[:i] + break + } + if f.tag { + if tagged >= 0 { + // Multiple tagged fields at the same level: conflict. + // Return no field. + return field{}, false + } + tagged = i + } + } + if tagged >= 0 { + return fields[tagged], true + } + // All remaining fields have the same length. If there's more than one, + // we have a conflict (two fields named "X" at the same level) and we + // return no field. + if len(fields) > 1 { + return field{}, false + } + return fields[0], true +} + +var fieldCache struct { + sync.RWMutex + m map[reflect.Type][]field +} + +// cachedTypeFields is like typeFields but uses a cache to avoid repeated work. +func cachedTypeFields(t reflect.Type) []field { + fieldCache.RLock() + f := fieldCache.m[t] + fieldCache.RUnlock() + if f != nil { + return f + } + + // Compute fields without lock. + // Might duplicate effort but won't hold other computations back. + f = typeFields(t) + if f == nil { + f = []field{} + } + + fieldCache.Lock() + if fieldCache.m == nil { + fieldCache.m = map[reflect.Type][]field{} + } + fieldCache.m[t] = f + fieldCache.Unlock() + return f +} diff --git a/vendor/github.com/BurntSushi/toml/type_toml.go b/vendor/github.com/BurntSushi/toml/type_toml.go new file mode 100644 index 0000000000..4e90d77373 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/type_toml.go @@ -0,0 +1,70 @@ +package toml + +// tomlType represents any Go type that corresponds to a TOML type. +// While the first draft of the TOML spec has a simplistic type system that +// probably doesn't need this level of sophistication, we seem to be militating +// toward adding real composite types. +type tomlType interface { + typeString() string +} + +// typeEqual accepts any two types and returns true if they are equal. +func typeEqual(t1, t2 tomlType) bool { + if t1 == nil || t2 == nil { + return false + } + return t1.typeString() == t2.typeString() +} + +func typeIsTable(t tomlType) bool { + return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash) +} + +type tomlBaseType string + +func (btype tomlBaseType) typeString() string { + return string(btype) +} + +func (btype tomlBaseType) String() string { + return btype.typeString() +} + +var ( + tomlInteger tomlBaseType = "Integer" + tomlFloat tomlBaseType = "Float" + tomlDatetime tomlBaseType = "Datetime" + tomlString tomlBaseType = "String" + tomlBool tomlBaseType = "Bool" + tomlArray tomlBaseType = "Array" + tomlHash tomlBaseType = "Hash" + tomlArrayHash tomlBaseType = "ArrayHash" +) + +// typeOfPrimitive returns a tomlType of any primitive value in TOML. +// Primitive values are: Integer, Float, Datetime, String and Bool. +// +// Passing a lexer item other than the following will cause a BUG message +// to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime. +func (p *parser) typeOfPrimitive(lexItem item) tomlType { + switch lexItem.typ { + case itemInteger: + return tomlInteger + case itemFloat: + return tomlFloat + case itemDatetime: + return tomlDatetime + case itemString: + return tomlString + case itemMultilineString: + return tomlString + case itemRawString: + return tomlString + case itemRawMultilineString: + return tomlString + case itemBool: + return tomlBool + } + p.bug("Cannot infer primitive type of lex item '%s'.", lexItem) + panic("unreachable") +} diff --git a/vendor/github.com/Masterminds/semver/.travis.yml b/vendor/github.com/Masterminds/semver/.travis.yml new file mode 100644 index 0000000000..096369d44d --- /dev/null +++ b/vendor/github.com/Masterminds/semver/.travis.yml @@ -0,0 +1,29 @@ +language: go + +go: + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x + - 1.11.x + - 1.12.x + - tip + +# Setting sudo access to false will let Travis CI use containers rather than +# VMs to run the tests. For more details see: +# - http://docs.travis-ci.com/user/workers/container-based-infrastructure/ +# - http://docs.travis-ci.com/user/workers/standard-infrastructure/ +sudo: false + +script: + - make setup + - make test + +notifications: + webhooks: + urls: + - https://webhooks.gitter.im/e/06e3328629952dabe3e0 + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: never # options: [always|never|change] default: always diff --git a/vendor/github.com/Masterminds/semver/CHANGELOG.md b/vendor/github.com/Masterminds/semver/CHANGELOG.md new file mode 100644 index 0000000000..e405c9a84d --- /dev/null +++ b/vendor/github.com/Masterminds/semver/CHANGELOG.md @@ -0,0 +1,109 @@ +# 1.5.0 (2019-09-11) + +## Added + +- #103: Add basic fuzzing for `NewVersion()` (thanks @jesse-c) + +## Changed + +- #82: Clarify wildcard meaning in range constraints and update tests for it (thanks @greysteil) +- #83: Clarify caret operator range for pre-1.0.0 dependencies (thanks @greysteil) +- #72: Adding docs comment pointing to vert for a cli +- #71: Update the docs on pre-release comparator handling +- #89: Test with new go versions (thanks @thedevsaddam) +- #87: Added $ to ValidPrerelease for better validation (thanks @jeremycarroll) + +## Fixed + +- #78: Fix unchecked error in example code (thanks @ravron) +- #70: Fix the handling of pre-releases and the 0.0.0 release edge case +- #97: Fixed copyright file for proper display on GitHub +- #107: Fix handling prerelease when sorting alphanum and num +- #109: Fixed where Validate sometimes returns wrong message on error + +# 1.4.2 (2018-04-10) + +## Changed +- #72: Updated the docs to point to vert for a console appliaction +- #71: Update the docs on pre-release comparator handling + +## Fixed +- #70: Fix the handling of pre-releases and the 0.0.0 release edge case + +# 1.4.1 (2018-04-02) + +## Fixed +- Fixed #64: Fix pre-release precedence issue (thanks @uudashr) + +# 1.4.0 (2017-10-04) + +## Changed +- #61: Update NewVersion to parse ints with a 64bit int size (thanks @zknill) + +# 1.3.1 (2017-07-10) + +## Fixed +- Fixed #57: number comparisons in prerelease sometimes inaccurate + +# 1.3.0 (2017-05-02) + +## Added +- #45: Added json (un)marshaling support (thanks @mh-cbon) +- Stability marker. See https://masterminds.github.io/stability/ + +## Fixed +- #51: Fix handling of single digit tilde constraint (thanks @dgodd) + +## Changed +- #55: The godoc icon moved from png to svg + +# 1.2.3 (2017-04-03) + +## Fixed +- #46: Fixed 0.x.x and 0.0.x in constraints being treated as * + +# Release 1.2.2 (2016-12-13) + +## Fixed +- #34: Fixed issue where hyphen range was not working with pre-release parsing. + +# Release 1.2.1 (2016-11-28) + +## Fixed +- #24: Fixed edge case issue where constraint "> 0" does not handle "0.0.1-alpha" + properly. + +# Release 1.2.0 (2016-11-04) + +## Added +- #20: Added MustParse function for versions (thanks @adamreese) +- #15: Added increment methods on versions (thanks @mh-cbon) + +## Fixed +- Issue #21: Per the SemVer spec (section 9) a pre-release is unstable and + might not satisfy the intended compatibility. The change here ignores pre-releases + on constraint checks (e.g., ~ or ^) when a pre-release is not part of the + constraint. For example, `^1.2.3` will ignore pre-releases while + `^1.2.3-alpha` will include them. + +# Release 1.1.1 (2016-06-30) + +## Changed +- Issue #9: Speed up version comparison performance (thanks @sdboyer) +- Issue #8: Added benchmarks (thanks @sdboyer) +- Updated Go Report Card URL to new location +- Updated Readme to add code snippet formatting (thanks @mh-cbon) +- Updating tagging to v[SemVer] structure for compatibility with other tools. + +# Release 1.1.0 (2016-03-11) + +- Issue #2: Implemented validation to provide reasons a versions failed a + constraint. + +# Release 1.0.1 (2015-12-31) + +- Fixed #1: * constraint failing on valid versions. + +# Release 1.0.0 (2015-10-20) + +- Initial release diff --git a/vendor/github.com/Masterminds/semver/LICENSE.txt b/vendor/github.com/Masterminds/semver/LICENSE.txt new file mode 100644 index 0000000000..9ff7da9c48 --- /dev/null +++ b/vendor/github.com/Masterminds/semver/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (C) 2014-2019, Matt Butcher and Matt Farina + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/Masterminds/semver/Makefile b/vendor/github.com/Masterminds/semver/Makefile new file mode 100644 index 0000000000..a7a1b4e36d --- /dev/null +++ b/vendor/github.com/Masterminds/semver/Makefile @@ -0,0 +1,36 @@ +.PHONY: setup +setup: + go get -u gopkg.in/alecthomas/gometalinter.v1 + gometalinter.v1 --install + +.PHONY: test +test: validate lint + @echo "==> Running tests" + go test -v + +.PHONY: validate +validate: + @echo "==> Running static validations" + @gometalinter.v1 \ + --disable-all \ + --enable deadcode \ + --severity deadcode:error \ + --enable gofmt \ + --enable gosimple \ + --enable ineffassign \ + --enable misspell \ + --enable vet \ + --tests \ + --vendor \ + --deadline 60s \ + ./... || exit_code=1 + +.PHONY: lint +lint: + @echo "==> Running linters" + @gometalinter.v1 \ + --disable-all \ + --enable golint \ + --vendor \ + --deadline 60s \ + ./... || : diff --git a/vendor/github.com/Masterminds/semver/README.md b/vendor/github.com/Masterminds/semver/README.md new file mode 100644 index 0000000000..1b52d2f436 --- /dev/null +++ b/vendor/github.com/Masterminds/semver/README.md @@ -0,0 +1,194 @@ +# SemVer + +The `semver` package provides the ability to work with [Semantic Versions](http://semver.org) in Go. Specifically it provides the ability to: + +* Parse semantic versions +* Sort semantic versions +* Check if a semantic version fits within a set of constraints +* Optionally work with a `v` prefix + +[![Stability: +Active](https://masterminds.github.io/stability/active.svg)](https://masterminds.github.io/stability/active.html) +[![Build Status](https://travis-ci.org/Masterminds/semver.svg)](https://travis-ci.org/Masterminds/semver) [![Build status](https://ci.appveyor.com/api/projects/status/jfk66lib7hb985k8/branch/master?svg=true&passingText=windows%20build%20passing&failingText=windows%20build%20failing)](https://ci.appveyor.com/project/mattfarina/semver/branch/master) [![GoDoc](https://godoc.org/github.com/Masterminds/semver?status.svg)](https://godoc.org/github.com/Masterminds/semver) [![Go Report Card](https://goreportcard.com/badge/github.com/Masterminds/semver)](https://goreportcard.com/report/github.com/Masterminds/semver) + +If you are looking for a command line tool for version comparisons please see +[vert](https://github.com/Masterminds/vert) which uses this library. + +## Parsing Semantic Versions + +To parse a semantic version use the `NewVersion` function. For example, + +```go + v, err := semver.NewVersion("1.2.3-beta.1+build345") +``` + +If there is an error the version wasn't parseable. The version object has methods +to get the parts of the version, compare it to other versions, convert the +version back into a string, and get the original string. For more details +please see the [documentation](https://godoc.org/github.com/Masterminds/semver). + +## Sorting Semantic Versions + +A set of versions can be sorted using the [`sort`](https://golang.org/pkg/sort/) +package from the standard library. For example, + +```go + raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",} + vs := make([]*semver.Version, len(raw)) + for i, r := range raw { + v, err := semver.NewVersion(r) + if err != nil { + t.Errorf("Error parsing version: %s", err) + } + + vs[i] = v + } + + sort.Sort(semver.Collection(vs)) +``` + +## Checking Version Constraints + +Checking a version against version constraints is one of the most featureful +parts of the package. + +```go + c, err := semver.NewConstraint(">= 1.2.3") + if err != nil { + // Handle constraint not being parseable. + } + + v, _ := semver.NewVersion("1.3") + if err != nil { + // Handle version not being parseable. + } + // Check if the version meets the constraints. The a variable will be true. + a := c.Check(v) +``` + +## Basic Comparisons + +There are two elements to the comparisons. First, a comparison string is a list +of comma separated and comparisons. These are then separated by || separated or +comparisons. For example, `">= 1.2, < 3.0.0 || >= 4.2.3"` is looking for a +comparison that's greater than or equal to 1.2 and less than 3.0.0 or is +greater than or equal to 4.2.3. + +The basic comparisons are: + +* `=`: equal (aliased to no operator) +* `!=`: not equal +* `>`: greater than +* `<`: less than +* `>=`: greater than or equal to +* `<=`: less than or equal to + +## Working With Pre-release Versions + +Pre-releases, for those not familiar with them, are used for software releases +prior to stable or generally available releases. Examples of pre-releases include +development, alpha, beta, and release candidate releases. A pre-release may be +a version such as `1.2.3-beta.1` while the stable release would be `1.2.3`. In the +order of precidence, pre-releases come before their associated releases. In this +example `1.2.3-beta.1 < 1.2.3`. + +According to the Semantic Version specification pre-releases may not be +API compliant with their release counterpart. It says, + +> A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version. + +SemVer comparisons without a pre-release comparator will skip pre-release versions. +For example, `>=1.2.3` will skip pre-releases when looking at a list of releases +while `>=1.2.3-0` will evaluate and find pre-releases. + +The reason for the `0` as a pre-release version in the example comparison is +because pre-releases can only contain ASCII alphanumerics and hyphens (along with +`.` separators), per the spec. Sorting happens in ASCII sort order, again per the spec. The lowest character is a `0` in ASCII sort order (see an [ASCII Table](http://www.asciitable.com/)) + +Understanding ASCII sort ordering is important because A-Z comes before a-z. That +means `>=1.2.3-BETA` will return `1.2.3-alpha`. What you might expect from case +sensitivity doesn't apply here. This is due to ASCII sort ordering which is what +the spec specifies. + +## Hyphen Range Comparisons + +There are multiple methods to handle ranges and the first is hyphens ranges. +These look like: + +* `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5` +* `2.3.4 - 4.5` which is equivalent to `>= 2.3.4, <= 4.5` + +## Wildcards In Comparisons + +The `x`, `X`, and `*` characters can be used as a wildcard character. This works +for all comparison operators. When used on the `=` operator it falls +back to the pack level comparison (see tilde below). For example, + +* `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` +* `>= 1.2.x` is equivalent to `>= 1.2.0` +* `<= 2.x` is equivalent to `< 3` +* `*` is equivalent to `>= 0.0.0` + +## Tilde Range Comparisons (Patch) + +The tilde (`~`) comparison operator is for patch level ranges when a minor +version is specified and major level changes when the minor number is missing. +For example, + +* `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0` +* `~1` is equivalent to `>= 1, < 2` +* `~2.3` is equivalent to `>= 2.3, < 2.4` +* `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` +* `~1.x` is equivalent to `>= 1, < 2` + +## Caret Range Comparisons (Major) + +The caret (`^`) comparison operator is for major level changes. This is useful +when comparisons of API versions as a major change is API breaking. For example, + +* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0` +* `^0.0.1` is equivalent to `>= 0.0.1, < 1.0.0` +* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0` +* `^2.3` is equivalent to `>= 2.3, < 3` +* `^2.x` is equivalent to `>= 2.0.0, < 3` + +# Validation + +In addition to testing a version against a constraint, a version can be validated +against a constraint. When validation fails a slice of errors containing why a +version didn't meet the constraint is returned. For example, + +```go + c, err := semver.NewConstraint("<= 1.2.3, >= 1.4") + if err != nil { + // Handle constraint not being parseable. + } + + v, _ := semver.NewVersion("1.3") + if err != nil { + // Handle version not being parseable. + } + + // Validate a version against a constraint. + a, msgs := c.Validate(v) + // a is false + for _, m := range msgs { + fmt.Println(m) + + // Loops over the errors which would read + // "1.3 is greater than 1.2.3" + // "1.3 is less than 1.4" + } +``` + +# Fuzzing + + [dvyukov/go-fuzz](https://github.com/dvyukov/go-fuzz) is used for fuzzing. + +1. `go-fuzz-build` +2. `go-fuzz -workdir=fuzz` + +# Contribute + +If you find an issue or want to contribute please file an [issue](https://github.com/Masterminds/semver/issues) +or [create a pull request](https://github.com/Masterminds/semver/pulls). diff --git a/vendor/github.com/Masterminds/semver/appveyor.yml b/vendor/github.com/Masterminds/semver/appveyor.yml new file mode 100644 index 0000000000..b2778df15a --- /dev/null +++ b/vendor/github.com/Masterminds/semver/appveyor.yml @@ -0,0 +1,44 @@ +version: build-{build}.{branch} + +clone_folder: C:\gopath\src\github.com\Masterminds\semver +shallow_clone: true + +environment: + GOPATH: C:\gopath + +platform: + - x64 + +install: + - go version + - go env + - go get -u gopkg.in/alecthomas/gometalinter.v1 + - set PATH=%PATH%;%GOPATH%\bin + - gometalinter.v1.exe --install + +build_script: + - go install -v ./... + +test_script: + - "gometalinter.v1 \ + --disable-all \ + --enable deadcode \ + --severity deadcode:error \ + --enable gofmt \ + --enable gosimple \ + --enable ineffassign \ + --enable misspell \ + --enable vet \ + --tests \ + --vendor \ + --deadline 60s \ + ./... || exit_code=1" + - "gometalinter.v1 \ + --disable-all \ + --enable golint \ + --vendor \ + --deadline 60s \ + ./... || :" + - go test -v + +deploy: off diff --git a/vendor/github.com/Masterminds/semver/collection.go b/vendor/github.com/Masterminds/semver/collection.go new file mode 100644 index 0000000000..a78235895f --- /dev/null +++ b/vendor/github.com/Masterminds/semver/collection.go @@ -0,0 +1,24 @@ +package semver + +// Collection is a collection of Version instances and implements the sort +// interface. See the sort package for more details. +// https://golang.org/pkg/sort/ +type Collection []*Version + +// Len returns the length of a collection. The number of Version instances +// on the slice. +func (c Collection) Len() int { + return len(c) +} + +// Less is needed for the sort interface to compare two Version objects on the +// slice. If checks if one is less than the other. +func (c Collection) Less(i, j int) bool { + return c[i].LessThan(c[j]) +} + +// Swap is needed for the sort interface to replace the Version objects +// at two different positions in the slice. +func (c Collection) Swap(i, j int) { + c[i], c[j] = c[j], c[i] +} diff --git a/vendor/github.com/Masterminds/semver/constraints.go b/vendor/github.com/Masterminds/semver/constraints.go new file mode 100644 index 0000000000..b94b93413f --- /dev/null +++ b/vendor/github.com/Masterminds/semver/constraints.go @@ -0,0 +1,423 @@ +package semver + +import ( + "errors" + "fmt" + "regexp" + "strings" +) + +// Constraints is one or more constraint that a semantic version can be +// checked against. +type Constraints struct { + constraints [][]*constraint +} + +// NewConstraint returns a Constraints instance that a Version instance can +// be checked against. If there is a parse error it will be returned. +func NewConstraint(c string) (*Constraints, error) { + + // Rewrite - ranges into a comparison operation. + c = rewriteRange(c) + + ors := strings.Split(c, "||") + or := make([][]*constraint, len(ors)) + for k, v := range ors { + cs := strings.Split(v, ",") + result := make([]*constraint, len(cs)) + for i, s := range cs { + pc, err := parseConstraint(s) + if err != nil { + return nil, err + } + + result[i] = pc + } + or[k] = result + } + + o := &Constraints{constraints: or} + return o, nil +} + +// Check tests if a version satisfies the constraints. +func (cs Constraints) Check(v *Version) bool { + // loop over the ORs and check the inner ANDs + for _, o := range cs.constraints { + joy := true + for _, c := range o { + if !c.check(v) { + joy = false + break + } + } + + if joy { + return true + } + } + + return false +} + +// Validate checks if a version satisfies a constraint. If not a slice of +// reasons for the failure are returned in addition to a bool. +func (cs Constraints) Validate(v *Version) (bool, []error) { + // loop over the ORs and check the inner ANDs + var e []error + + // Capture the prerelease message only once. When it happens the first time + // this var is marked + var prerelesase bool + for _, o := range cs.constraints { + joy := true + for _, c := range o { + // Before running the check handle the case there the version is + // a prerelease and the check is not searching for prereleases. + if c.con.pre == "" && v.pre != "" { + if !prerelesase { + em := fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + e = append(e, em) + prerelesase = true + } + joy = false + + } else { + + if !c.check(v) { + em := fmt.Errorf(c.msg, v, c.orig) + e = append(e, em) + joy = false + } + } + } + + if joy { + return true, []error{} + } + } + + return false, e +} + +var constraintOps map[string]cfunc +var constraintMsg map[string]string +var constraintRegex *regexp.Regexp + +func init() { + constraintOps = map[string]cfunc{ + "": constraintTildeOrEqual, + "=": constraintTildeOrEqual, + "!=": constraintNotEqual, + ">": constraintGreaterThan, + "<": constraintLessThan, + ">=": constraintGreaterThanEqual, + "=>": constraintGreaterThanEqual, + "<=": constraintLessThanEqual, + "=<": constraintLessThanEqual, + "~": constraintTilde, + "~>": constraintTilde, + "^": constraintCaret, + } + + constraintMsg = map[string]string{ + "": "%s is not equal to %s", + "=": "%s is not equal to %s", + "!=": "%s is equal to %s", + ">": "%s is less than or equal to %s", + "<": "%s is greater than or equal to %s", + ">=": "%s is less than %s", + "=>": "%s is less than %s", + "<=": "%s is greater than %s", + "=<": "%s is greater than %s", + "~": "%s does not have same major and minor version as %s", + "~>": "%s does not have same major and minor version as %s", + "^": "%s does not have same major version as %s", + } + + ops := make([]string, 0, len(constraintOps)) + for k := range constraintOps { + ops = append(ops, regexp.QuoteMeta(k)) + } + + constraintRegex = regexp.MustCompile(fmt.Sprintf( + `^\s*(%s)\s*(%s)\s*$`, + strings.Join(ops, "|"), + cvRegex)) + + constraintRangeRegex = regexp.MustCompile(fmt.Sprintf( + `\s*(%s)\s+-\s+(%s)\s*`, + cvRegex, cvRegex)) +} + +// An individual constraint +type constraint struct { + // The callback function for the restraint. It performs the logic for + // the constraint. + function cfunc + + msg string + + // The version used in the constraint check. For example, if a constraint + // is '<= 2.0.0' the con a version instance representing 2.0.0. + con *Version + + // The original parsed version (e.g., 4.x from != 4.x) + orig string + + // When an x is used as part of the version (e.g., 1.x) + minorDirty bool + dirty bool + patchDirty bool +} + +// Check if a version meets the constraint +func (c *constraint) check(v *Version) bool { + return c.function(v, c) +} + +type cfunc func(v *Version, c *constraint) bool + +func parseConstraint(c string) (*constraint, error) { + m := constraintRegex.FindStringSubmatch(c) + if m == nil { + return nil, fmt.Errorf("improper constraint: %s", c) + } + + ver := m[2] + orig := ver + minorDirty := false + patchDirty := false + dirty := false + if isX(m[3]) { + ver = "0.0.0" + dirty = true + } else if isX(strings.TrimPrefix(m[4], ".")) || m[4] == "" { + minorDirty = true + dirty = true + ver = fmt.Sprintf("%s.0.0%s", m[3], m[6]) + } else if isX(strings.TrimPrefix(m[5], ".")) { + dirty = true + patchDirty = true + ver = fmt.Sprintf("%s%s.0%s", m[3], m[4], m[6]) + } + + con, err := NewVersion(ver) + if err != nil { + + // The constraintRegex should catch any regex parsing errors. So, + // we should never get here. + return nil, errors.New("constraint Parser Error") + } + + cs := &constraint{ + function: constraintOps[m[1]], + msg: constraintMsg[m[1]], + con: con, + orig: orig, + minorDirty: minorDirty, + patchDirty: patchDirty, + dirty: dirty, + } + return cs, nil +} + +// Constraint functions +func constraintNotEqual(v *Version, c *constraint) bool { + if c.dirty { + + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false + } + + if c.con.Major() != v.Major() { + return true + } + if c.con.Minor() != v.Minor() && !c.minorDirty { + return true + } else if c.minorDirty { + return false + } + + return false + } + + return !v.Equal(c.con) +} + +func constraintGreaterThan(v *Version, c *constraint) bool { + + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false + } + + return v.Compare(c.con) == 1 +} + +func constraintLessThan(v *Version, c *constraint) bool { + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false + } + + if !c.dirty { + return v.Compare(c.con) < 0 + } + + if v.Major() > c.con.Major() { + return false + } else if v.Minor() > c.con.Minor() && !c.minorDirty { + return false + } + + return true +} + +func constraintGreaterThanEqual(v *Version, c *constraint) bool { + + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false + } + + return v.Compare(c.con) >= 0 +} + +func constraintLessThanEqual(v *Version, c *constraint) bool { + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false + } + + if !c.dirty { + return v.Compare(c.con) <= 0 + } + + if v.Major() > c.con.Major() { + return false + } else if v.Minor() > c.con.Minor() && !c.minorDirty { + return false + } + + return true +} + +// ~*, ~>* --> >= 0.0.0 (any) +// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0, <3.0.0 +// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0, <2.1.0 +// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0, <1.3.0 +// ~1.2.3, ~>1.2.3 --> >=1.2.3, <1.3.0 +// ~1.2.0, ~>1.2.0 --> >=1.2.0, <1.3.0 +func constraintTilde(v *Version, c *constraint) bool { + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false + } + + if v.LessThan(c.con) { + return false + } + + // ~0.0.0 is a special case where all constraints are accepted. It's + // equivalent to >= 0.0.0. + if c.con.Major() == 0 && c.con.Minor() == 0 && c.con.Patch() == 0 && + !c.minorDirty && !c.patchDirty { + return true + } + + if v.Major() != c.con.Major() { + return false + } + + if v.Minor() != c.con.Minor() && !c.minorDirty { + return false + } + + return true +} + +// When there is a .x (dirty) status it automatically opts in to ~. Otherwise +// it's a straight = +func constraintTildeOrEqual(v *Version, c *constraint) bool { + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false + } + + if c.dirty { + c.msg = constraintMsg["~"] + return constraintTilde(v, c) + } + + return v.Equal(c.con) +} + +// ^* --> (any) +// ^2, ^2.x, ^2.x.x --> >=2.0.0, <3.0.0 +// ^2.0, ^2.0.x --> >=2.0.0, <3.0.0 +// ^1.2, ^1.2.x --> >=1.2.0, <2.0.0 +// ^1.2.3 --> >=1.2.3, <2.0.0 +// ^1.2.0 --> >=1.2.0, <2.0.0 +func constraintCaret(v *Version, c *constraint) bool { + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false + } + + if v.LessThan(c.con) { + return false + } + + if v.Major() != c.con.Major() { + return false + } + + return true +} + +var constraintRangeRegex *regexp.Regexp + +const cvRegex string = `v?([0-9|x|X|\*]+)(\.[0-9|x|X|\*]+)?(\.[0-9|x|X|\*]+)?` + + `(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + + `(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + +func isX(x string) bool { + switch x { + case "x", "*", "X": + return true + default: + return false + } +} + +func rewriteRange(i string) string { + m := constraintRangeRegex.FindAllStringSubmatch(i, -1) + if m == nil { + return i + } + o := i + for _, v := range m { + t := fmt.Sprintf(">= %s, <= %s", v[1], v[11]) + o = strings.Replace(o, v[0], t, 1) + } + + return o +} diff --git a/vendor/github.com/Masterminds/semver/doc.go b/vendor/github.com/Masterminds/semver/doc.go new file mode 100644 index 0000000000..6a6c24c6d6 --- /dev/null +++ b/vendor/github.com/Masterminds/semver/doc.go @@ -0,0 +1,115 @@ +/* +Package semver provides the ability to work with Semantic Versions (http://semver.org) in Go. + +Specifically it provides the ability to: + + * Parse semantic versions + * Sort semantic versions + * Check if a semantic version fits within a set of constraints + * Optionally work with a `v` prefix + +Parsing Semantic Versions + +To parse a semantic version use the `NewVersion` function. For example, + + v, err := semver.NewVersion("1.2.3-beta.1+build345") + +If there is an error the version wasn't parseable. The version object has methods +to get the parts of the version, compare it to other versions, convert the +version back into a string, and get the original string. For more details +please see the documentation at https://godoc.org/github.com/Masterminds/semver. + +Sorting Semantic Versions + +A set of versions can be sorted using the `sort` package from the standard library. +For example, + + raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",} + vs := make([]*semver.Version, len(raw)) + for i, r := range raw { + v, err := semver.NewVersion(r) + if err != nil { + t.Errorf("Error parsing version: %s", err) + } + + vs[i] = v + } + + sort.Sort(semver.Collection(vs)) + +Checking Version Constraints + +Checking a version against version constraints is one of the most featureful +parts of the package. + + c, err := semver.NewConstraint(">= 1.2.3") + if err != nil { + // Handle constraint not being parseable. + } + + v, err := semver.NewVersion("1.3") + if err != nil { + // Handle version not being parseable. + } + // Check if the version meets the constraints. The a variable will be true. + a := c.Check(v) + +Basic Comparisons + +There are two elements to the comparisons. First, a comparison string is a list +of comma separated and comparisons. These are then separated by || separated or +comparisons. For example, `">= 1.2, < 3.0.0 || >= 4.2.3"` is looking for a +comparison that's greater than or equal to 1.2 and less than 3.0.0 or is +greater than or equal to 4.2.3. + +The basic comparisons are: + + * `=`: equal (aliased to no operator) + * `!=`: not equal + * `>`: greater than + * `<`: less than + * `>=`: greater than or equal to + * `<=`: less than or equal to + +Hyphen Range Comparisons + +There are multiple methods to handle ranges and the first is hyphens ranges. +These look like: + + * `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5` + * `2.3.4 - 4.5` which is equivalent to `>= 2.3.4, <= 4.5` + +Wildcards In Comparisons + +The `x`, `X`, and `*` characters can be used as a wildcard character. This works +for all comparison operators. When used on the `=` operator it falls +back to the pack level comparison (see tilde below). For example, + + * `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` + * `>= 1.2.x` is equivalent to `>= 1.2.0` + * `<= 2.x` is equivalent to `<= 3` + * `*` is equivalent to `>= 0.0.0` + +Tilde Range Comparisons (Patch) + +The tilde (`~`) comparison operator is for patch level ranges when a minor +version is specified and major level changes when the minor number is missing. +For example, + + * `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0` + * `~1` is equivalent to `>= 1, < 2` + * `~2.3` is equivalent to `>= 2.3, < 2.4` + * `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` + * `~1.x` is equivalent to `>= 1, < 2` + +Caret Range Comparisons (Major) + +The caret (`^`) comparison operator is for major level changes. This is useful +when comparisons of API versions as a major change is API breaking. For example, + + * `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0` + * `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0` + * `^2.3` is equivalent to `>= 2.3, < 3` + * `^2.x` is equivalent to `>= 2.0.0, < 3` +*/ +package semver diff --git a/vendor/github.com/Masterminds/semver/version.go b/vendor/github.com/Masterminds/semver/version.go new file mode 100644 index 0000000000..400d4f9341 --- /dev/null +++ b/vendor/github.com/Masterminds/semver/version.go @@ -0,0 +1,425 @@ +package semver + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "regexp" + "strconv" + "strings" +) + +// The compiled version of the regex created at init() is cached here so it +// only needs to be created once. +var versionRegex *regexp.Regexp +var validPrereleaseRegex *regexp.Regexp + +var ( + // ErrInvalidSemVer is returned a version is found to be invalid when + // being parsed. + ErrInvalidSemVer = errors.New("Invalid Semantic Version") + + // ErrInvalidMetadata is returned when the metadata is an invalid format + ErrInvalidMetadata = errors.New("Invalid Metadata string") + + // ErrInvalidPrerelease is returned when the pre-release is an invalid format + ErrInvalidPrerelease = errors.New("Invalid Prerelease string") +) + +// SemVerRegex is the regular expression used to parse a semantic version. +const SemVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` + + `(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + + `(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + +// ValidPrerelease is the regular expression which validates +// both prerelease and metadata values. +const ValidPrerelease string = `^([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*)$` + +// Version represents a single semantic version. +type Version struct { + major, minor, patch int64 + pre string + metadata string + original string +} + +func init() { + versionRegex = regexp.MustCompile("^" + SemVerRegex + "$") + validPrereleaseRegex = regexp.MustCompile(ValidPrerelease) +} + +// NewVersion parses a given version and returns an instance of Version or +// an error if unable to parse the version. +func NewVersion(v string) (*Version, error) { + m := versionRegex.FindStringSubmatch(v) + if m == nil { + return nil, ErrInvalidSemVer + } + + sv := &Version{ + metadata: m[8], + pre: m[5], + original: v, + } + + var temp int64 + temp, err := strconv.ParseInt(m[1], 10, 64) + if err != nil { + return nil, fmt.Errorf("Error parsing version segment: %s", err) + } + sv.major = temp + + if m[2] != "" { + temp, err = strconv.ParseInt(strings.TrimPrefix(m[2], "."), 10, 64) + if err != nil { + return nil, fmt.Errorf("Error parsing version segment: %s", err) + } + sv.minor = temp + } else { + sv.minor = 0 + } + + if m[3] != "" { + temp, err = strconv.ParseInt(strings.TrimPrefix(m[3], "."), 10, 64) + if err != nil { + return nil, fmt.Errorf("Error parsing version segment: %s", err) + } + sv.patch = temp + } else { + sv.patch = 0 + } + + return sv, nil +} + +// MustParse parses a given version and panics on error. +func MustParse(v string) *Version { + sv, err := NewVersion(v) + if err != nil { + panic(err) + } + return sv +} + +// String converts a Version object to a string. +// Note, if the original version contained a leading v this version will not. +// See the Original() method to retrieve the original value. Semantic Versions +// don't contain a leading v per the spec. Instead it's optional on +// implementation. +func (v *Version) String() string { + var buf bytes.Buffer + + fmt.Fprintf(&buf, "%d.%d.%d", v.major, v.minor, v.patch) + if v.pre != "" { + fmt.Fprintf(&buf, "-%s", v.pre) + } + if v.metadata != "" { + fmt.Fprintf(&buf, "+%s", v.metadata) + } + + return buf.String() +} + +// Original returns the original value passed in to be parsed. +func (v *Version) Original() string { + return v.original +} + +// Major returns the major version. +func (v *Version) Major() int64 { + return v.major +} + +// Minor returns the minor version. +func (v *Version) Minor() int64 { + return v.minor +} + +// Patch returns the patch version. +func (v *Version) Patch() int64 { + return v.patch +} + +// Prerelease returns the pre-release version. +func (v *Version) Prerelease() string { + return v.pre +} + +// Metadata returns the metadata on the version. +func (v *Version) Metadata() string { + return v.metadata +} + +// originalVPrefix returns the original 'v' prefix if any. +func (v *Version) originalVPrefix() string { + + // Note, only lowercase v is supported as a prefix by the parser. + if v.original != "" && v.original[:1] == "v" { + return v.original[:1] + } + return "" +} + +// IncPatch produces the next patch version. +// If the current version does not have prerelease/metadata information, +// it unsets metadata and prerelease values, increments patch number. +// If the current version has any of prerelease or metadata information, +// it unsets both values and keeps curent patch value +func (v Version) IncPatch() Version { + vNext := v + // according to http://semver.org/#spec-item-9 + // Pre-release versions have a lower precedence than the associated normal version. + // according to http://semver.org/#spec-item-10 + // Build metadata SHOULD be ignored when determining version precedence. + if v.pre != "" { + vNext.metadata = "" + vNext.pre = "" + } else { + vNext.metadata = "" + vNext.pre = "" + vNext.patch = v.patch + 1 + } + vNext.original = v.originalVPrefix() + "" + vNext.String() + return vNext +} + +// IncMinor produces the next minor version. +// Sets patch to 0. +// Increments minor number. +// Unsets metadata. +// Unsets prerelease status. +func (v Version) IncMinor() Version { + vNext := v + vNext.metadata = "" + vNext.pre = "" + vNext.patch = 0 + vNext.minor = v.minor + 1 + vNext.original = v.originalVPrefix() + "" + vNext.String() + return vNext +} + +// IncMajor produces the next major version. +// Sets patch to 0. +// Sets minor to 0. +// Increments major number. +// Unsets metadata. +// Unsets prerelease status. +func (v Version) IncMajor() Version { + vNext := v + vNext.metadata = "" + vNext.pre = "" + vNext.patch = 0 + vNext.minor = 0 + vNext.major = v.major + 1 + vNext.original = v.originalVPrefix() + "" + vNext.String() + return vNext +} + +// SetPrerelease defines the prerelease value. +// Value must not include the required 'hypen' prefix. +func (v Version) SetPrerelease(prerelease string) (Version, error) { + vNext := v + if len(prerelease) > 0 && !validPrereleaseRegex.MatchString(prerelease) { + return vNext, ErrInvalidPrerelease + } + vNext.pre = prerelease + vNext.original = v.originalVPrefix() + "" + vNext.String() + return vNext, nil +} + +// SetMetadata defines metadata value. +// Value must not include the required 'plus' prefix. +func (v Version) SetMetadata(metadata string) (Version, error) { + vNext := v + if len(metadata) > 0 && !validPrereleaseRegex.MatchString(metadata) { + return vNext, ErrInvalidMetadata + } + vNext.metadata = metadata + vNext.original = v.originalVPrefix() + "" + vNext.String() + return vNext, nil +} + +// LessThan tests if one version is less than another one. +func (v *Version) LessThan(o *Version) bool { + return v.Compare(o) < 0 +} + +// GreaterThan tests if one version is greater than another one. +func (v *Version) GreaterThan(o *Version) bool { + return v.Compare(o) > 0 +} + +// Equal tests if two versions are equal to each other. +// Note, versions can be equal with different metadata since metadata +// is not considered part of the comparable version. +func (v *Version) Equal(o *Version) bool { + return v.Compare(o) == 0 +} + +// Compare compares this version to another one. It returns -1, 0, or 1 if +// the version smaller, equal, or larger than the other version. +// +// Versions are compared by X.Y.Z. Build metadata is ignored. Prerelease is +// lower than the version without a prerelease. +func (v *Version) Compare(o *Version) int { + // Compare the major, minor, and patch version for differences. If a + // difference is found return the comparison. + if d := compareSegment(v.Major(), o.Major()); d != 0 { + return d + } + if d := compareSegment(v.Minor(), o.Minor()); d != 0 { + return d + } + if d := compareSegment(v.Patch(), o.Patch()); d != 0 { + return d + } + + // At this point the major, minor, and patch versions are the same. + ps := v.pre + po := o.Prerelease() + + if ps == "" && po == "" { + return 0 + } + if ps == "" { + return 1 + } + if po == "" { + return -1 + } + + return comparePrerelease(ps, po) +} + +// UnmarshalJSON implements JSON.Unmarshaler interface. +func (v *Version) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + temp, err := NewVersion(s) + if err != nil { + return err + } + v.major = temp.major + v.minor = temp.minor + v.patch = temp.patch + v.pre = temp.pre + v.metadata = temp.metadata + v.original = temp.original + temp = nil + return nil +} + +// MarshalJSON implements JSON.Marshaler interface. +func (v *Version) MarshalJSON() ([]byte, error) { + return json.Marshal(v.String()) +} + +func compareSegment(v, o int64) int { + if v < o { + return -1 + } + if v > o { + return 1 + } + + return 0 +} + +func comparePrerelease(v, o string) int { + + // split the prelease versions by their part. The separator, per the spec, + // is a . + sparts := strings.Split(v, ".") + oparts := strings.Split(o, ".") + + // Find the longer length of the parts to know how many loop iterations to + // go through. + slen := len(sparts) + olen := len(oparts) + + l := slen + if olen > slen { + l = olen + } + + // Iterate over each part of the prereleases to compare the differences. + for i := 0; i < l; i++ { + // Since the lentgh of the parts can be different we need to create + // a placeholder. This is to avoid out of bounds issues. + stemp := "" + if i < slen { + stemp = sparts[i] + } + + otemp := "" + if i < olen { + otemp = oparts[i] + } + + d := comparePrePart(stemp, otemp) + if d != 0 { + return d + } + } + + // Reaching here means two versions are of equal value but have different + // metadata (the part following a +). They are not identical in string form + // but the version comparison finds them to be equal. + return 0 +} + +func comparePrePart(s, o string) int { + // Fastpath if they are equal + if s == o { + return 0 + } + + // When s or o are empty we can use the other in an attempt to determine + // the response. + if s == "" { + if o != "" { + return -1 + } + return 1 + } + + if o == "" { + if s != "" { + return 1 + } + return -1 + } + + // When comparing strings "99" is greater than "103". To handle + // cases like this we need to detect numbers and compare them. According + // to the semver spec, numbers are always positive. If there is a - at the + // start like -99 this is to be evaluated as an alphanum. numbers always + // have precedence over alphanum. Parsing as Uints because negative numbers + // are ignored. + + oi, n1 := strconv.ParseUint(o, 10, 64) + si, n2 := strconv.ParseUint(s, 10, 64) + + // The case where both are strings compare the strings + if n1 != nil && n2 != nil { + if s > o { + return 1 + } + return -1 + } else if n1 != nil { + // o is a string and s is a number + return -1 + } else if n2 != nil { + // s is a string and o is a number + return 1 + } + // Both are numbers + if si > oi { + return 1 + } + return -1 + +} diff --git a/vendor/github.com/Masterminds/semver/version_fuzz.go b/vendor/github.com/Masterminds/semver/version_fuzz.go new file mode 100644 index 0000000000..b42bcd62b9 --- /dev/null +++ b/vendor/github.com/Masterminds/semver/version_fuzz.go @@ -0,0 +1,10 @@ +// +build gofuzz + +package semver + +func Fuzz(data []byte) int { + if _, err := NewVersion(string(data)); err != nil { + return 0 + } + return 1 +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go b/vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go new file mode 100644 index 0000000000..602920786c --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go @@ -0,0 +1,160 @@ +// +build windows + +package security + +import ( + "fmt" + "os" + "syscall" + "unsafe" +) + +type ( + accessMask uint32 + accessMode uint32 + desiredAccess uint32 + inheritMode uint32 + objectType uint32 + shareMode uint32 + securityInformation uint32 + trusteeForm uint32 + trusteeType uint32 + + explicitAccess struct { + accessPermissions accessMask + accessMode accessMode + inheritance inheritMode + trustee trustee + } + + trustee struct { + multipleTrustee *trustee + multipleTrusteeOperation int32 + trusteeForm trusteeForm + trusteeType trusteeType + name uintptr + } +) + +const ( + accessMaskDesiredPermission accessMask = 1 << 31 // GENERIC_READ + + accessModeGrant accessMode = 1 + + desiredAccessReadControl desiredAccess = 0x20000 + desiredAccessWriteDac desiredAccess = 0x40000 + + gvmga = "GrantVmGroupAccess:" + + inheritModeNoInheritance inheritMode = 0x0 + inheritModeSubContainersAndObjectsInherit inheritMode = 0x3 + + objectTypeFileObject objectType = 0x1 + + securityInformationDACL securityInformation = 0x4 + + shareModeRead shareMode = 0x1 + shareModeWrite shareMode = 0x2 + + sidVmGroup = "S-1-5-83-0" + + trusteeFormIsSid trusteeForm = 0 + + trusteeTypeWellKnownGroup trusteeType = 5 +) + +// GrantVMGroupAccess sets the DACL for a specified file or directory to +// include Grant ACE entries for the VM Group SID. This is a golang re- +// implementation of the same function in vmcompute, just not exported in +// RS5. Which kind of sucks. Sucks a lot :/ +func GrantVmGroupAccess(name string) error { + // Stat (to determine if `name` is a directory). + s, err := os.Stat(name) + if err != nil { + return fmt.Errorf("%s os.Stat %s: %w", gvmga, name, err) + } + + // Get a handle to the file/directory. Must defer Close on success. + fd, err := createFile(name, s.IsDir()) + if err != nil { + return err // Already wrapped + } + defer syscall.CloseHandle(fd) + + // Get the current DACL and Security Descriptor. Must defer LocalFree on success. + ot := objectTypeFileObject + si := securityInformationDACL + sd := uintptr(0) + origDACL := uintptr(0) + if err := getSecurityInfo(fd, uint32(ot), uint32(si), nil, nil, &origDACL, nil, &sd); err != nil { + return fmt.Errorf("%s GetSecurityInfo %s: %w", gvmga, name, err) + } + defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(sd))) + + // Generate a new DACL which is the current DACL with the required ACEs added. + // Must defer LocalFree on success. + newDACL, err := generateDACLWithAcesAdded(name, s.IsDir(), origDACL) + if err != nil { + return err // Already wrapped + } + defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newDACL))) + + // And finally use SetSecurityInfo to apply the updated DACL. + if err := setSecurityInfo(fd, uint32(ot), uint32(si), uintptr(0), uintptr(0), newDACL, uintptr(0)); err != nil { + return fmt.Errorf("%s SetSecurityInfo %s: %w", gvmga, name, err) + } + + return nil +} + +// createFile is a helper function to call [Nt]CreateFile to get a handle to +// the file or directory. +func createFile(name string, isDir bool) (syscall.Handle, error) { + namep := syscall.StringToUTF16(name) + da := uint32(desiredAccessReadControl | desiredAccessWriteDac) + sm := uint32(shareModeRead | shareModeWrite) + fa := uint32(syscall.FILE_ATTRIBUTE_NORMAL) + if isDir { + fa = uint32(fa | syscall.FILE_FLAG_BACKUP_SEMANTICS) + } + fd, err := syscall.CreateFile(&namep[0], da, sm, nil, syscall.OPEN_EXISTING, fa, 0) + if err != nil { + return 0, fmt.Errorf("%s syscall.CreateFile %s: %w", gvmga, name, err) + } + return fd, nil +} + +// generateDACLWithAcesAdded generates a new DACL with the two needed ACEs added. +// The caller is responsible for LocalFree of the returned DACL on success. +func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintptr, error) { + // Generate pointers to the SIDs based on the string SIDs + sid, err := syscall.StringToSid(sidVmGroup) + if err != nil { + return 0, fmt.Errorf("%s syscall.StringToSid %s %s: %w", gvmga, name, sidVmGroup, err) + } + + inheritance := inheritModeNoInheritance + if isDir { + inheritance = inheritModeSubContainersAndObjectsInherit + } + + eaArray := []explicitAccess{ + explicitAccess{ + accessPermissions: accessMaskDesiredPermission, + accessMode: accessModeGrant, + inheritance: inheritance, + trustee: trustee{ + trusteeForm: trusteeFormIsSid, + trusteeType: trusteeTypeWellKnownGroup, + name: uintptr(unsafe.Pointer(sid)), + }, + }, + } + + modifiedDACL := uintptr(0) + if err := setEntriesInAcl(uintptr(uint32(1)), uintptr(unsafe.Pointer(&eaArray[0])), origDACL, &modifiedDACL); err != nil { + return 0, fmt.Errorf("%s SetEntriesInAcl %s: %w", gvmga, name, err) + } + + return modifiedDACL, nil +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/security/syscall_windows.go b/vendor/github.com/Microsoft/go-winio/pkg/security/syscall_windows.go new file mode 100644 index 0000000000..d7096716ce --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/security/syscall_windows.go @@ -0,0 +1,7 @@ +package security + +//go:generate go run mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go + +//sys getSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, ppsidOwner **uintptr, ppsidGroup **uintptr, ppDacl *uintptr, ppSacl *uintptr, ppSecurityDescriptor *uintptr) (win32err error) = advapi32.GetSecurityInfo +//sys setSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, psidOwner uintptr, psidGroup uintptr, pDacl uintptr, pSacl uintptr) (win32err error) = advapi32.SetSecurityInfo +//sys setEntriesInAcl(count uintptr, pListOfEEs uintptr, oldAcl uintptr, newAcl *uintptr) (win32err error) = advapi32.SetEntriesInAclW diff --git a/vendor/github.com/Microsoft/go-winio/pkg/security/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/pkg/security/zsyscall_windows.go new file mode 100644 index 0000000000..4084680e0f --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/security/zsyscall_windows.go @@ -0,0 +1,70 @@ +// Code generated by 'go generate'; DO NOT EDIT. + +package security + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") + + procGetSecurityInfo = modadvapi32.NewProc("GetSecurityInfo") + procSetEntriesInAclW = modadvapi32.NewProc("SetEntriesInAclW") + procSetSecurityInfo = modadvapi32.NewProc("SetSecurityInfo") +) + +func getSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, ppsidOwner **uintptr, ppsidGroup **uintptr, ppDacl *uintptr, ppSacl *uintptr, ppSecurityDescriptor *uintptr) (win32err error) { + r0, _, _ := syscall.Syscall9(procGetSecurityInfo.Addr(), 8, uintptr(handle), uintptr(objectType), uintptr(si), uintptr(unsafe.Pointer(ppsidOwner)), uintptr(unsafe.Pointer(ppsidGroup)), uintptr(unsafe.Pointer(ppDacl)), uintptr(unsafe.Pointer(ppSacl)), uintptr(unsafe.Pointer(ppSecurityDescriptor)), 0) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func setEntriesInAcl(count uintptr, pListOfEEs uintptr, oldAcl uintptr, newAcl *uintptr) (win32err error) { + r0, _, _ := syscall.Syscall6(procSetEntriesInAclW.Addr(), 4, uintptr(count), uintptr(pListOfEEs), uintptr(oldAcl), uintptr(unsafe.Pointer(newAcl)), 0, 0) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func setSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, psidOwner uintptr, psidGroup uintptr, pDacl uintptr, pSacl uintptr) (win32err error) { + r0, _, _ := syscall.Syscall9(procSetSecurityInfo.Addr(), 7, uintptr(handle), uintptr(objectType), uintptr(si), uintptr(psidOwner), uintptr(psidGroup), uintptr(pDacl), uintptr(pSacl), 0, 0) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} diff --git a/vendor/github.com/Microsoft/go-winio/vhd/vhd.go b/vendor/github.com/Microsoft/go-winio/vhd/vhd.go new file mode 100644 index 0000000000..f7f78fc230 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/vhd/vhd.go @@ -0,0 +1,350 @@ +//go:build windows +// +build windows + +package vhd + +import ( + "fmt" + "syscall" + + "github.com/Microsoft/go-winio/pkg/guid" + "golang.org/x/sys/windows" +) + +//go:generate go run mksyscall_windows.go -output zvhd_windows.go vhd.go + +//sys createVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (win32err error) = virtdisk.CreateVirtualDisk +//sys openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (win32err error) = virtdisk.OpenVirtualDisk +//sys attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (win32err error) = virtdisk.AttachVirtualDisk +//sys detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, providerSpecificFlags uint32) (win32err error) = virtdisk.DetachVirtualDisk +//sys getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (win32err error) = virtdisk.GetVirtualDiskPhysicalPath + +type ( + CreateVirtualDiskFlag uint32 + VirtualDiskFlag uint32 + AttachVirtualDiskFlag uint32 + DetachVirtualDiskFlag uint32 + VirtualDiskAccessMask uint32 +) + +type VirtualStorageType struct { + DeviceID uint32 + VendorID guid.GUID +} + +type CreateVersion2 struct { + UniqueID guid.GUID + MaximumSize uint64 + BlockSizeInBytes uint32 + SectorSizeInBytes uint32 + PhysicalSectorSizeInByte uint32 + ParentPath *uint16 // string + SourcePath *uint16 // string + OpenFlags uint32 + ParentVirtualStorageType VirtualStorageType + SourceVirtualStorageType VirtualStorageType + ResiliencyGUID guid.GUID +} + +type CreateVirtualDiskParameters struct { + Version uint32 // Must always be set to 2 + Version2 CreateVersion2 +} + +type OpenVersion2 struct { + GetInfoOnly bool + ReadOnly bool + ResiliencyGUID guid.GUID +} + +type OpenVirtualDiskParameters struct { + Version uint32 // Must always be set to 2 + Version2 OpenVersion2 +} + +// The higher level `OpenVersion2` struct uses bools to refer to `GetInfoOnly` and `ReadOnly` for ease of use. However, +// the internal windows structure uses `BOOLS` aka int32s for these types. `openVersion2` is used for translating +// `OpenVersion2` fields to the correct windows internal field types on the `Open____` methods. +type openVersion2 struct { + getInfoOnly int32 + readOnly int32 + resiliencyGUID guid.GUID +} + +type openVirtualDiskParameters struct { + version uint32 + version2 openVersion2 +} + +type AttachVersion2 struct { + RestrictedOffset uint64 + RestrictedLength uint64 +} + +type AttachVirtualDiskParameters struct { + Version uint32 + Version2 AttachVersion2 +} + +const ( + VIRTUAL_STORAGE_TYPE_DEVICE_VHDX = 0x3 + + // Access Mask for opening a VHD + VirtualDiskAccessNone VirtualDiskAccessMask = 0x00000000 + VirtualDiskAccessAttachRO VirtualDiskAccessMask = 0x00010000 + VirtualDiskAccessAttachRW VirtualDiskAccessMask = 0x00020000 + VirtualDiskAccessDetach VirtualDiskAccessMask = 0x00040000 + VirtualDiskAccessGetInfo VirtualDiskAccessMask = 0x00080000 + VirtualDiskAccessCreate VirtualDiskAccessMask = 0x00100000 + VirtualDiskAccessMetaOps VirtualDiskAccessMask = 0x00200000 + VirtualDiskAccessRead VirtualDiskAccessMask = 0x000d0000 + VirtualDiskAccessAll VirtualDiskAccessMask = 0x003f0000 + VirtualDiskAccessWritable VirtualDiskAccessMask = 0x00320000 + + // Flags for creating a VHD + CreateVirtualDiskFlagNone CreateVirtualDiskFlag = 0x0 + CreateVirtualDiskFlagFullPhysicalAllocation CreateVirtualDiskFlag = 0x1 + CreateVirtualDiskFlagPreventWritesToSourceDisk CreateVirtualDiskFlag = 0x2 + CreateVirtualDiskFlagDoNotCopyMetadataFromParent CreateVirtualDiskFlag = 0x4 + CreateVirtualDiskFlagCreateBackingStorage CreateVirtualDiskFlag = 0x8 + CreateVirtualDiskFlagUseChangeTrackingSourceLimit CreateVirtualDiskFlag = 0x10 + CreateVirtualDiskFlagPreserveParentChangeTrackingState CreateVirtualDiskFlag = 0x20 + CreateVirtualDiskFlagVhdSetUseOriginalBackingStorage CreateVirtualDiskFlag = 0x40 + CreateVirtualDiskFlagSparseFile CreateVirtualDiskFlag = 0x80 + CreateVirtualDiskFlagPmemCompatible CreateVirtualDiskFlag = 0x100 + CreateVirtualDiskFlagSupportCompressedVolumes CreateVirtualDiskFlag = 0x200 + + // Flags for opening a VHD + OpenVirtualDiskFlagNone VirtualDiskFlag = 0x00000000 + OpenVirtualDiskFlagNoParents VirtualDiskFlag = 0x00000001 + OpenVirtualDiskFlagBlankFile VirtualDiskFlag = 0x00000002 + OpenVirtualDiskFlagBootDrive VirtualDiskFlag = 0x00000004 + OpenVirtualDiskFlagCachedIO VirtualDiskFlag = 0x00000008 + OpenVirtualDiskFlagCustomDiffChain VirtualDiskFlag = 0x00000010 + OpenVirtualDiskFlagParentCachedIO VirtualDiskFlag = 0x00000020 + OpenVirtualDiskFlagVhdsetFileOnly VirtualDiskFlag = 0x00000040 + OpenVirtualDiskFlagIgnoreRelativeParentLocator VirtualDiskFlag = 0x00000080 + OpenVirtualDiskFlagNoWriteHardening VirtualDiskFlag = 0x00000100 + OpenVirtualDiskFlagSupportCompressedVolumes VirtualDiskFlag = 0x00000200 + + // Flags for attaching a VHD + AttachVirtualDiskFlagNone AttachVirtualDiskFlag = 0x00000000 + AttachVirtualDiskFlagReadOnly AttachVirtualDiskFlag = 0x00000001 + AttachVirtualDiskFlagNoDriveLetter AttachVirtualDiskFlag = 0x00000002 + AttachVirtualDiskFlagPermanentLifetime AttachVirtualDiskFlag = 0x00000004 + AttachVirtualDiskFlagNoLocalHost AttachVirtualDiskFlag = 0x00000008 + AttachVirtualDiskFlagNoSecurityDescriptor AttachVirtualDiskFlag = 0x00000010 + AttachVirtualDiskFlagBypassDefaultEncryptionPolicy AttachVirtualDiskFlag = 0x00000020 + AttachVirtualDiskFlagNonPnp AttachVirtualDiskFlag = 0x00000040 + AttachVirtualDiskFlagRestrictedRange AttachVirtualDiskFlag = 0x00000080 + AttachVirtualDiskFlagSinglePartition AttachVirtualDiskFlag = 0x00000100 + AttachVirtualDiskFlagRegisterVolume AttachVirtualDiskFlag = 0x00000200 + + // Flags for detaching a VHD + DetachVirtualDiskFlagNone DetachVirtualDiskFlag = 0x0 +) + +// CreateVhdx is a helper function to create a simple vhdx file at the given path using +// default values. +func CreateVhdx(path string, maxSizeInGb, blockSizeInMb uint32) error { + params := CreateVirtualDiskParameters{ + Version: 2, + Version2: CreateVersion2{ + MaximumSize: uint64(maxSizeInGb) * 1024 * 1024 * 1024, + BlockSizeInBytes: blockSizeInMb * 1024 * 1024, + }, + } + + handle, err := CreateVirtualDisk(path, VirtualDiskAccessNone, CreateVirtualDiskFlagNone, ¶ms) + if err != nil { + return err + } + + return syscall.CloseHandle(handle) +} + +// DetachVirtualDisk detaches a virtual hard disk by handle. +func DetachVirtualDisk(handle syscall.Handle) (err error) { + if err := detachVirtualDisk(handle, 0, 0); err != nil { + return fmt.Errorf("failed to detach virtual disk: %w", err) + } + return nil +} + +// DetachVhd detaches a vhd found at `path`. +func DetachVhd(path string) error { + handle, err := OpenVirtualDisk( + path, + VirtualDiskAccessNone, + OpenVirtualDiskFlagCachedIO|OpenVirtualDiskFlagIgnoreRelativeParentLocator, + ) + if err != nil { + return err + } + defer syscall.CloseHandle(handle) + return DetachVirtualDisk(handle) +} + +// AttachVirtualDisk attaches a virtual hard disk for use. +func AttachVirtualDisk(handle syscall.Handle, attachVirtualDiskFlag AttachVirtualDiskFlag, parameters *AttachVirtualDiskParameters) (err error) { + // Supports both version 1 and 2 of the attach parameters as version 2 wasn't present in RS5. + if err := attachVirtualDisk( + handle, + nil, + uint32(attachVirtualDiskFlag), + 0, + parameters, + nil, + ); err != nil { + return fmt.Errorf("failed to attach virtual disk: %w", err) + } + return nil +} + +// AttachVhd attaches a virtual hard disk at `path` for use. Attaches using version 2 +// of the ATTACH_VIRTUAL_DISK_PARAMETERS. +func AttachVhd(path string) (err error) { + handle, err := OpenVirtualDisk( + path, + VirtualDiskAccessNone, + OpenVirtualDiskFlagCachedIO|OpenVirtualDiskFlagIgnoreRelativeParentLocator, + ) + if err != nil { + return err + } + + defer syscall.CloseHandle(handle) + params := AttachVirtualDiskParameters{Version: 2} + if err := AttachVirtualDisk( + handle, + AttachVirtualDiskFlagNone, + ¶ms, + ); err != nil { + return fmt.Errorf("failed to attach virtual disk: %w", err) + } + return nil +} + +// OpenVirtualDisk obtains a handle to a VHD opened with supplied access mask and flags. +func OpenVirtualDisk(vhdPath string, virtualDiskAccessMask VirtualDiskAccessMask, openVirtualDiskFlags VirtualDiskFlag) (syscall.Handle, error) { + parameters := OpenVirtualDiskParameters{Version: 2} + handle, err := OpenVirtualDiskWithParameters( + vhdPath, + virtualDiskAccessMask, + openVirtualDiskFlags, + ¶meters, + ) + if err != nil { + return 0, err + } + return handle, nil +} + +// OpenVirtualDiskWithParameters obtains a handle to a VHD opened with supplied access mask, flags and parameters. +func OpenVirtualDiskWithParameters(vhdPath string, virtualDiskAccessMask VirtualDiskAccessMask, openVirtualDiskFlags VirtualDiskFlag, parameters *OpenVirtualDiskParameters) (syscall.Handle, error) { + var ( + handle syscall.Handle + defaultType VirtualStorageType + getInfoOnly int32 + readOnly int32 + ) + if parameters.Version != 2 { + return handle, fmt.Errorf("only version 2 VHDs are supported, found version: %d", parameters.Version) + } + if parameters.Version2.GetInfoOnly { + getInfoOnly = 1 + } + if parameters.Version2.ReadOnly { + readOnly = 1 + } + params := &openVirtualDiskParameters{ + version: parameters.Version, + version2: openVersion2{ + getInfoOnly, + readOnly, + parameters.Version2.ResiliencyGUID, + }, + } + if err := openVirtualDisk( + &defaultType, + vhdPath, + uint32(virtualDiskAccessMask), + uint32(openVirtualDiskFlags), + params, + &handle, + ); err != nil { + return 0, fmt.Errorf("failed to open virtual disk: %w", err) + } + return handle, nil +} + +// CreateVirtualDisk creates a virtual harddisk and returns a handle to the disk. +func CreateVirtualDisk(path string, virtualDiskAccessMask VirtualDiskAccessMask, createVirtualDiskFlags CreateVirtualDiskFlag, parameters *CreateVirtualDiskParameters) (syscall.Handle, error) { + var ( + handle syscall.Handle + defaultType VirtualStorageType + ) + if parameters.Version != 2 { + return handle, fmt.Errorf("only version 2 VHDs are supported, found version: %d", parameters.Version) + } + + if err := createVirtualDisk( + &defaultType, + path, + uint32(virtualDiskAccessMask), + nil, + uint32(createVirtualDiskFlags), + 0, + parameters, + nil, + &handle, + ); err != nil { + return handle, fmt.Errorf("failed to create virtual disk: %w", err) + } + return handle, nil +} + +// GetVirtualDiskPhysicalPath takes a handle to a virtual hard disk and returns the physical +// path of the disk on the machine. This path is in the form \\.\PhysicalDriveX where X is an integer +// that represents the particular enumeration of the physical disk on the caller's system. +func GetVirtualDiskPhysicalPath(handle syscall.Handle) (_ string, err error) { + var ( + diskPathSizeInBytes uint32 = 256 * 2 // max path length 256 wide chars + diskPhysicalPathBuf [256]uint16 + ) + if err := getVirtualDiskPhysicalPath( + handle, + &diskPathSizeInBytes, + &diskPhysicalPathBuf[0], + ); err != nil { + return "", fmt.Errorf("failed to get disk physical path: %w", err) + } + return windows.UTF16ToString(diskPhysicalPathBuf[:]), nil +} + +// CreateDiffVhd is a helper function to create a differencing virtual disk. +func CreateDiffVhd(diffVhdPath, baseVhdPath string, blockSizeInMB uint32) error { + // Setting `ParentPath` is how to signal to create a differencing disk. + createParams := &CreateVirtualDiskParameters{ + Version: 2, + Version2: CreateVersion2{ + ParentPath: windows.StringToUTF16Ptr(baseVhdPath), + BlockSizeInBytes: blockSizeInMB * 1024 * 1024, + OpenFlags: uint32(OpenVirtualDiskFlagCachedIO), + }, + } + + vhdHandle, err := CreateVirtualDisk( + diffVhdPath, + VirtualDiskAccessNone, + CreateVirtualDiskFlagNone, + createParams, + ) + if err != nil { + return fmt.Errorf("failed to create differencing vhd: %w", err) + } + if err := syscall.CloseHandle(vhdHandle); err != nil { + return fmt.Errorf("failed to close differencing vhd handle: %w", err) + } + return nil +} diff --git a/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go b/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go new file mode 100644 index 0000000000..1d7498db3b --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go @@ -0,0 +1,106 @@ +// Code generated by 'go generate'; DO NOT EDIT. + +package vhd + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modvirtdisk = windows.NewLazySystemDLL("virtdisk.dll") + + procAttachVirtualDisk = modvirtdisk.NewProc("AttachVirtualDisk") + procCreateVirtualDisk = modvirtdisk.NewProc("CreateVirtualDisk") + procDetachVirtualDisk = modvirtdisk.NewProc("DetachVirtualDisk") + procGetVirtualDiskPhysicalPath = modvirtdisk.NewProc("GetVirtualDiskPhysicalPath") + procOpenVirtualDisk = modvirtdisk.NewProc("OpenVirtualDisk") +) + +func attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (win32err error) { + r0, _, _ := syscall.Syscall6(procAttachVirtualDisk.Addr(), 6, uintptr(handle), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(attachVirtualDiskFlag), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(overlapped))) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func createVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (win32err error) { + var _p0 *uint16 + _p0, win32err = syscall.UTF16PtrFromString(path) + if win32err != nil { + return + } + return _createVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, securityDescriptor, createVirtualDiskFlags, providerSpecificFlags, parameters, overlapped, handle) +} + +func _createVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (win32err error) { + r0, _, _ := syscall.Syscall9(procCreateVirtualDisk.Addr(), 9, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(createVirtualDiskFlags), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(handle))) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, providerSpecificFlags uint32) (win32err error) { + r0, _, _ := syscall.Syscall(procDetachVirtualDisk.Addr(), 3, uintptr(handle), uintptr(detachVirtualDiskFlags), uintptr(providerSpecificFlags)) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (win32err error) { + r0, _, _ := syscall.Syscall(procGetVirtualDiskPhysicalPath.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(diskPathSizeInBytes)), uintptr(unsafe.Pointer(buffer))) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (win32err error) { + var _p0 *uint16 + _p0, win32err = syscall.UTF16PtrFromString(path) + if win32err != nil { + return + } + return _openVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, openVirtualDiskFlags, parameters, handle) +} + +func _openVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (win32err error) { + r0, _, _ := syscall.Syscall6(procOpenVirtualDisk.Addr(), 6, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(openVirtualDiskFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(handle))) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} diff --git a/vendor/github.com/Microsoft/hcsshim/.gitattributes b/vendor/github.com/Microsoft/hcsshim/.gitattributes new file mode 100644 index 0000000000..94f480de94 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file diff --git a/vendor/github.com/Microsoft/hcsshim/.gitignore b/vendor/github.com/Microsoft/hcsshim/.gitignore new file mode 100644 index 0000000000..54ed6f06c9 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/.gitignore @@ -0,0 +1,38 @@ +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Ignore vscode setting files +.vscode/ + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ + +# Ignore gcs bin directory +service/bin/ +service/pkg/ + +*.img +*.vhd +*.tar.gz + +# Make stuff +.rootfs-done +bin/* +rootfs/* +*.o +/build/ + +deps/* +out/* + +.idea/ +.vscode/ \ No newline at end of file diff --git a/vendor/github.com/Microsoft/hcsshim/.golangci.yml b/vendor/github.com/Microsoft/hcsshim/.golangci.yml new file mode 100644 index 0000000000..2400e7f1e0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/.golangci.yml @@ -0,0 +1,99 @@ +run: + timeout: 8m + +linters: + enable: + - stylecheck + +linters-settings: + stylecheck: + # https://staticcheck.io/docs/checks + checks: ["all"] + + +issues: + # This repo has a LOT of generated schema files, operating system bindings, and other things that ST1003 from stylecheck won't like + # (screaming case Windows api constants for example). There's also some structs that we *could* change the initialisms to be Go + # friendly (Id -> ID) but they're exported and it would be a breaking change. This makes it so that most new code, code that isn't + # supposed to be a pretty faithful mapping to an OS call/constants, or non-generated code still checks if we're following idioms, + # while ignoring the things that are just noise or would be more of a hassle than it'd be worth to change. + exclude-rules: + - path: layer.go + linters: + - stylecheck + Text: "ST1003:" + + - path: hcsshim.go + linters: + - stylecheck + Text: "ST1003:" + + - path: internal\\hcs\\schema2\\ + linters: + - stylecheck + Text: "ST1003:" + + - path: internal\\wclayer\\ + linters: + - stylecheck + Text: "ST1003:" + + - path: hcn\\ + linters: + - stylecheck + Text: "ST1003:" + + - path: internal\\hcs\\schema1\\ + linters: + - stylecheck + Text: "ST1003:" + + - path: internal\\hns\\ + linters: + - stylecheck + Text: "ST1003:" + + - path: ext4\\internal\\compactext4\\ + linters: + - stylecheck + Text: "ST1003:" + + - path: ext4\\internal\\format\\ + linters: + - stylecheck + Text: "ST1003:" + + - path: internal\\guestrequest\\ + linters: + - stylecheck + Text: "ST1003:" + + - path: internal\\guest\\prot\\ + linters: + - stylecheck + Text: "ST1003:" + + - path: internal\\windevice\\ + linters: + - stylecheck + Text: "ST1003:" + + - path: internal\\winapi\\ + linters: + - stylecheck + Text: "ST1003:" + + - path: internal\\vmcompute\\ + linters: + - stylecheck + Text: "ST1003:" + + - path: internal\\regstate\\ + linters: + - stylecheck + Text: "ST1003:" + + - path: internal\\hcserror\\ + linters: + - stylecheck + Text: "ST1003:" \ No newline at end of file diff --git a/vendor/github.com/Microsoft/hcsshim/CODEOWNERS b/vendor/github.com/Microsoft/hcsshim/CODEOWNERS new file mode 100644 index 0000000000..f4c5a07d14 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/CODEOWNERS @@ -0,0 +1 @@ +* @microsoft/containerplat \ No newline at end of file diff --git a/vendor/github.com/Microsoft/hcsshim/LICENSE b/vendor/github.com/Microsoft/hcsshim/LICENSE new file mode 100644 index 0000000000..49d21669ae --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/Microsoft/hcsshim/Makefile b/vendor/github.com/Microsoft/hcsshim/Makefile new file mode 100644 index 0000000000..a8f5516cd0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/Makefile @@ -0,0 +1,87 @@ +BASE:=base.tar.gz + +GO:=go +GO_FLAGS:=-ldflags "-s -w" # strip Go binaries +CGO_ENABLED:=0 +GOMODVENDOR:= + +CFLAGS:=-O2 -Wall +LDFLAGS:=-static -s # strip C binaries + +GO_FLAGS_EXTRA:= +ifeq "$(GOMODVENDOR)" "1" +GO_FLAGS_EXTRA += -mod=vendor +endif +GO_BUILD:=CGO_ENABLED=$(CGO_ENABLED) $(GO) build $(GO_FLAGS) $(GO_FLAGS_EXTRA) + +SRCROOT=$(dir $(abspath $(firstword $(MAKEFILE_LIST)))) + +# The link aliases for gcstools +GCS_TOOLS=\ + generichook + +.PHONY: all always rootfs test + +all: out/initrd.img out/rootfs.tar.gz + +clean: + find -name '*.o' -print0 | xargs -0 -r rm + rm -rf bin deps rootfs out + +test: + cd $(SRCROOT) && go test -v ./internal/guest/... + +out/delta.tar.gz: bin/init bin/vsockexec bin/cmd/gcs bin/cmd/gcstools Makefile + @mkdir -p out + rm -rf rootfs + mkdir -p rootfs/bin/ + cp bin/init rootfs/ + cp bin/vsockexec rootfs/bin/ + cp bin/cmd/gcs rootfs/bin/ + cp bin/cmd/gcstools rootfs/bin/ + for tool in $(GCS_TOOLS); do ln -s gcstools rootfs/bin/$$tool; done + git -C $(SRCROOT) rev-parse HEAD > rootfs/gcs.commit && \ + git -C $(SRCROOT) rev-parse --abbrev-ref HEAD > rootfs/gcs.branch + tar -zcf $@ -C rootfs . + rm -rf rootfs + +out/rootfs.tar.gz: out/initrd.img + rm -rf rootfs-conv + mkdir rootfs-conv + gunzip -c out/initrd.img | (cd rootfs-conv && cpio -imd) + tar -zcf $@ -C rootfs-conv . + rm -rf rootfs-conv + +out/initrd.img: $(BASE) out/delta.tar.gz $(SRCROOT)/hack/catcpio.sh + $(SRCROOT)/hack/catcpio.sh "$(BASE)" out/delta.tar.gz > out/initrd.img.uncompressed + gzip -c out/initrd.img.uncompressed > $@ + rm out/initrd.img.uncompressed + +-include deps/cmd/gcs.gomake +-include deps/cmd/gcstools.gomake + +# Implicit rule for includes that define Go targets. +%.gomake: $(SRCROOT)/Makefile + @mkdir -p $(dir $@) + @/bin/echo $(@:deps/%.gomake=bin/%): $(SRCROOT)/hack/gomakedeps.sh > $@.new + @/bin/echo -e '\t@mkdir -p $$(dir $$@) $(dir $@)' >> $@.new + @/bin/echo -e '\t$$(GO_BUILD) -o $$@.new $$(SRCROOT)/$$(@:bin/%=%)' >> $@.new + @/bin/echo -e '\tGO="$(GO)" $$(SRCROOT)/hack/gomakedeps.sh $$@ $$(SRCROOT)/$$(@:bin/%=%) $$(GO_FLAGS) $$(GO_FLAGS_EXTRA) > $(@:%.gomake=%.godeps).new' >> $@.new + @/bin/echo -e '\tmv $(@:%.gomake=%.godeps).new $(@:%.gomake=%.godeps)' >> $@.new + @/bin/echo -e '\tmv $$@.new $$@' >> $@.new + @/bin/echo -e '-include $(@:%.gomake=%.godeps)' >> $@.new + mv $@.new $@ + +VPATH=$(SRCROOT) + +bin/vsockexec: vsockexec/vsockexec.o vsockexec/vsock.o + @mkdir -p bin + $(CC) $(LDFLAGS) -o $@ $^ + +bin/init: init/init.o vsockexec/vsock.o + @mkdir -p bin + $(CC) $(LDFLAGS) -o $@ $^ + +%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< diff --git a/vendor/github.com/Microsoft/hcsshim/Protobuild.toml b/vendor/github.com/Microsoft/hcsshim/Protobuild.toml new file mode 100644 index 0000000000..ee18671aa6 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/Protobuild.toml @@ -0,0 +1,49 @@ +version = "unstable" +generator = "gogoctrd" +plugins = ["grpc", "fieldpath"] + +# Control protoc include paths. Below are usually some good defaults, but feel +# free to try it without them if it works for your project. +[includes] + # Include paths that will be added before all others. Typically, you want to + # treat the root of the project as an include, but this may not be necessary. + before = ["./protobuf"] + + # Paths that should be treated as include roots in relation to the vendor + # directory. These will be calculated with the vendor directory nearest the + # target package. + packages = ["github.com/gogo/protobuf"] + + # Paths that will be added untouched to the end of the includes. We use + # `/usr/local/include` to pickup the common install location of protobuf. + # This is the default. + after = ["/usr/local/include"] + +# This section maps protobuf imports to Go packages. These will become +# `-M` directives in the call to the go protobuf generator. +[packages] + "gogoproto/gogo.proto" = "github.com/gogo/protobuf/gogoproto" + "google/protobuf/any.proto" = "github.com/gogo/protobuf/types" + "google/protobuf/empty.proto" = "github.com/gogo/protobuf/types" + "google/protobuf/struct.proto" = "github.com/gogo/protobuf/types" + "google/protobuf/descriptor.proto" = "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" + "google/protobuf/field_mask.proto" = "github.com/gogo/protobuf/types" + "google/protobuf/timestamp.proto" = "github.com/gogo/protobuf/types" + "google/protobuf/duration.proto" = "github.com/gogo/protobuf/types" + "github/containerd/cgroups/stats/v1/metrics.proto" = "github.com/containerd/cgroups/stats/v1" + +[[overrides]] +prefixes = ["github.com/Microsoft/hcsshim/internal/shimdiag"] +plugins = ["ttrpc"] + +[[overrides]] +prefixes = ["github.com/Microsoft/hcsshim/internal/computeagent"] +plugins = ["ttrpc"] + +[[overrides]] +prefixes = ["github.com/Microsoft/hcsshim/internal/ncproxyttrpc"] +plugins = ["ttrpc"] + +[[overrides]] +prefixes = ["github.com/Microsoft/hcsshim/internal/vmservice"] +plugins = ["ttrpc"] \ No newline at end of file diff --git a/vendor/github.com/Microsoft/hcsshim/README.md b/vendor/github.com/Microsoft/hcsshim/README.md new file mode 100644 index 0000000000..b8ca926a9d --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/README.md @@ -0,0 +1,120 @@ +# hcsshim + +[![Build status](https://github.com/microsoft/hcsshim/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/microsoft/hcsshim/actions?query=branch%3Amaster) + +This package contains the Golang interface for using the Windows [Host Compute Service](https://techcommunity.microsoft.com/t5/containers/introducing-the-host-compute-service-hcs/ba-p/382332) (HCS) to launch and manage [Windows Containers](https://docs.microsoft.com/en-us/virtualization/windowscontainers/about/). It also contains other helpers and functions for managing Windows Containers such as the Golang interface for the Host Network Service (HNS), as well as code for the [guest agent](./internal/guest/README.md) (commonly referred to as the GCS or Guest Compute Service in the codebase) used to support running Linux Hyper-V containers. + +It is primarily used in the [Moby](https://github.com/moby/moby) and [Containerd](https://github.com/containerd/containerd) projects, but it can be freely used by other projects as well. + +## Building + +While this repository can be used as a library of sorts to call the HCS apis, there are a couple binaries built out of the repository as well. The main ones being the Linux guest agent, and an implementation of the [runtime v2 containerd shim api](https://github.com/containerd/containerd/blob/master/runtime/v2/README.md). +### Linux Hyper-V Container Guest Agent + +To build the Linux guest agent itself all that's needed is to set your GOOS to "Linux" and build out of ./cmd/gcs. +```powershell +C:\> $env:GOOS="linux" +C:\> go build .\cmd\gcs\ +``` + +or on a Linux machine +```sh +> go build ./cmd/gcs +``` + +If you want it to be packaged inside of a rootfs to boot with alongside all of the other tools then you'll need to provide a rootfs that it can be packaged inside of. An easy way is to export the rootfs of a container. + +```sh +docker pull busybox +docker run --name base_image_container busybox +docker export base_image_container | gzip > base.tar.gz +BASE=./base.tar.gz +make all +``` + +If the build is successful, in the `./out` folder you should see: +```sh +> ls ./out/ +delta.tar.gz initrd.img rootfs.tar.gz +``` + +### Containerd Shim +For info on the Runtime V2 API: https://github.com/containerd/containerd/blob/master/runtime/v2/README.md. + +Contrary to the typical Linux architecture of shim -> runc, the runhcs shim is used both to launch and manage the lifetime of containers. + +```powershell +C:\> $env:GOOS="windows" +C:\> go build .\cmd\containerd-shim-runhcs-v1 +``` + +Then place the binary in the same directory that Containerd is located at in your environment. A default Containerd configuration file can be generated by running: +```powershell +.\containerd.exe config default | Out-File "C:\Program Files\containerd\config.toml" -Encoding ascii +``` + +This config file will already have the shim set as the default runtime for cri interactions. + +To trial using the shim out with ctr.exe: +```powershell +C:\> ctr.exe run --runtime io.containerd.runhcs.v1 --rm mcr.microsoft.com/windows/nanoserver:2004 windows-test cmd /c "echo Hello World!" +``` + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a +Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us +the rights to use your contribution. For details, visit https://cla.microsoft.com. + +When you submit a pull request, a CLA-bot will automatically determine whether you need to provide +a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions +provided by the bot. You will only need to do this once across all repos using our CLA. + +We also require that contributors [sign their commits](https://git-scm.com/docs/git-commit) using `git commit -s` or `git commit --signoff` to +certify they either authored the work themselves or otherwise have permission to use it in this project. Please see https://developercertificate.org/ for +more info, as well as to make sure that you can attest to the rules listed. Our CI uses the [DCO Github app](https://github.com/apps/dco) to ensure +that all commits in a given PR are signed-off. + +### Test Directory (Important to note) + +This project has tried to trim some dependencies from the root Go modules file that would be cumbersome to get transitively included if this +project is being vendored/used as a library. Some of these dependencies were only being used for tests, so the /test directory in this project also has +its own go.mod file where these are now included to get around this issue. Our tests rely on the code in this project to run, so the test Go modules file +has a relative path replace directive to pull in the latest hcsshim code that the tests actually touch from this project +(which is the repo itself on your disk). + +``` +replace ( + github.com/Microsoft/hcsshim => ../ +) +``` + +Because of this, for most code changes you may need to run `go mod vendor` + `go mod tidy` in the /test directory in this repository, as the +CI in this project will check if the files are out of date and will fail if this is true. + + +## Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## Dependencies + +This project requires Golang 1.9 or newer to build. + +For system requirements to run this project, see the Microsoft docs on [Windows Container requirements](https://docs.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/system-requirements). + +## Reporting Security Issues + +Security issues and bugs should be reported privately, via email, to the Microsoft Security +Response Center (MSRC) at [secure@microsoft.com](mailto:secure@microsoft.com). You should +receive a response within 24 hours. If for some reason you do not, please follow up via +email to ensure we received your original message. Further information, including the +[MSRC PGP](https://technet.microsoft.com/en-us/security/dn606155) key, can be found in +the [Security TechCenter](https://technet.microsoft.com/en-us/security/default). + +For additional details, see [Report a Computer Security Vulnerability](https://technet.microsoft.com/en-us/security/ff852094.aspx) on Technet + +--------------- +Copyright (c) 2018 Microsoft Corp. All rights reserved. diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/attach.go b/vendor/github.com/Microsoft/hcsshim/computestorage/attach.go new file mode 100644 index 0000000000..7f1f2823dd --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/attach.go @@ -0,0 +1,38 @@ +package computestorage + +import ( + "context" + "encoding/json" + + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/pkg/errors" + "go.opencensus.io/trace" +) + +// AttachLayerStorageFilter sets up the layer storage filter on a writable +// container layer. +// +// `layerPath` is a path to a directory the writable layer is mounted. If the +// path does not end in a `\` the platform will append it automatically. +// +// `layerData` is the parent read-only layer data. +func AttachLayerStorageFilter(ctx context.Context, layerPath string, layerData LayerData) (err error) { + title := "hcsshim.AttachLayerStorageFilter" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("layerPath", layerPath), + ) + + bytes, err := json.Marshal(layerData) + if err != nil { + return err + } + + err = hcsAttachLayerStorageFilter(layerPath, string(bytes)) + if err != nil { + return errors.Wrap(err, "failed to attach layer storage filter") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/destroy.go b/vendor/github.com/Microsoft/hcsshim/computestorage/destroy.go new file mode 100644 index 0000000000..8e28e6c504 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/destroy.go @@ -0,0 +1,26 @@ +package computestorage + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/pkg/errors" + "go.opencensus.io/trace" +) + +// DestroyLayer deletes a container layer. +// +// `layerPath` is a path to a directory containing the layer to export. +func DestroyLayer(ctx context.Context, layerPath string) (err error) { + title := "hcsshim.DestroyLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("layerPath", layerPath)) + + err = hcsDestroyLayer(layerPath) + if err != nil { + return errors.Wrap(err, "failed to destroy layer") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/detach.go b/vendor/github.com/Microsoft/hcsshim/computestorage/detach.go new file mode 100644 index 0000000000..435473257e --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/detach.go @@ -0,0 +1,26 @@ +package computestorage + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/pkg/errors" + "go.opencensus.io/trace" +) + +// DetachLayerStorageFilter detaches the layer storage filter on a writable container layer. +// +// `layerPath` is a path to a directory containing the layer to export. +func DetachLayerStorageFilter(ctx context.Context, layerPath string) (err error) { + title := "hcsshim.DetachLayerStorageFilter" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("layerPath", layerPath)) + + err = hcsDetachLayerStorageFilter(layerPath) + if err != nil { + return errors.Wrap(err, "failed to detach layer storage filter") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/export.go b/vendor/github.com/Microsoft/hcsshim/computestorage/export.go new file mode 100644 index 0000000000..a1b12dd129 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/export.go @@ -0,0 +1,46 @@ +package computestorage + +import ( + "context" + "encoding/json" + + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/pkg/errors" + "go.opencensus.io/trace" +) + +// ExportLayer exports a container layer. +// +// `layerPath` is a path to a directory containing the layer to export. +// +// `exportFolderPath` is a pre-existing folder to export the layer to. +// +// `layerData` is the parent layer data. +// +// `options` are the export options applied to the exported layer. +func ExportLayer(ctx context.Context, layerPath, exportFolderPath string, layerData LayerData, options ExportLayerOptions) (err error) { + title := "hcsshim.ExportLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("layerPath", layerPath), + trace.StringAttribute("exportFolderPath", exportFolderPath), + ) + + ldbytes, err := json.Marshal(layerData) + if err != nil { + return err + } + + obytes, err := json.Marshal(options) + if err != nil { + return err + } + + err = hcsExportLayer(layerPath, exportFolderPath, string(ldbytes), string(obytes)) + if err != nil { + return errors.Wrap(err, "failed to export layer") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/format.go b/vendor/github.com/Microsoft/hcsshim/computestorage/format.go new file mode 100644 index 0000000000..83c0fa33f0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/format.go @@ -0,0 +1,26 @@ +package computestorage + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/pkg/errors" + "go.opencensus.io/trace" + "golang.org/x/sys/windows" +) + +// FormatWritableLayerVhd formats a virtual disk for use as a writable container layer. +// +// If the VHD is not mounted it will be temporarily mounted. +func FormatWritableLayerVhd(ctx context.Context, vhdHandle windows.Handle) (err error) { + title := "hcsshim.FormatWritableLayerVhd" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + + err = hcsFormatWritableLayerVhd(vhdHandle) + if err != nil { + return errors.Wrap(err, "failed to format writable layer vhd") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/helpers.go b/vendor/github.com/Microsoft/hcsshim/computestorage/helpers.go new file mode 100644 index 0000000000..87fee452cd --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/helpers.go @@ -0,0 +1,193 @@ +package computestorage + +import ( + "context" + "os" + "path/filepath" + "syscall" + + "github.com/Microsoft/go-winio/pkg/security" + "github.com/Microsoft/go-winio/vhd" + "github.com/pkg/errors" + "golang.org/x/sys/windows" +) + +const defaultVHDXBlockSizeInMB = 1 + +// SetupContainerBaseLayer is a helper to setup a containers scratch. It +// will create and format the vhdx's inside and the size is configurable with the sizeInGB +// parameter. +// +// `layerPath` is the path to the base container layer on disk. +// +// `baseVhdPath` is the path to where the base vhdx for the base layer should be created. +// +// `diffVhdPath` is the path where the differencing disk for the base layer should be created. +// +// `sizeInGB` is the size in gigabytes to make the base vhdx. +func SetupContainerBaseLayer(ctx context.Context, layerPath, baseVhdPath, diffVhdPath string, sizeInGB uint64) (err error) { + var ( + hivesPath = filepath.Join(layerPath, "Hives") + layoutPath = filepath.Join(layerPath, "Layout") + ) + + // We need to remove the hives directory and layout file as `SetupBaseOSLayer` fails if these files + // already exist. `SetupBaseOSLayer` will create these files internally. We also remove the base and + // differencing disks if they exist in case we're asking for a different size. + if _, err := os.Stat(hivesPath); err == nil { + if err := os.RemoveAll(hivesPath); err != nil { + return errors.Wrap(err, "failed to remove prexisting hives directory") + } + } + if _, err := os.Stat(layoutPath); err == nil { + if err := os.RemoveAll(layoutPath); err != nil { + return errors.Wrap(err, "failed to remove prexisting layout file") + } + } + + if _, err := os.Stat(baseVhdPath); err == nil { + if err := os.RemoveAll(baseVhdPath); err != nil { + return errors.Wrap(err, "failed to remove base vhdx path") + } + } + if _, err := os.Stat(diffVhdPath); err == nil { + if err := os.RemoveAll(diffVhdPath); err != nil { + return errors.Wrap(err, "failed to remove differencing vhdx") + } + } + + createParams := &vhd.CreateVirtualDiskParameters{ + Version: 2, + Version2: vhd.CreateVersion2{ + MaximumSize: sizeInGB * 1024 * 1024 * 1024, + BlockSizeInBytes: defaultVHDXBlockSizeInMB * 1024 * 1024, + }, + } + handle, err := vhd.CreateVirtualDisk(baseVhdPath, vhd.VirtualDiskAccessNone, vhd.CreateVirtualDiskFlagNone, createParams) + if err != nil { + return errors.Wrap(err, "failed to create vhdx") + } + + defer func() { + if err != nil { + _ = syscall.CloseHandle(handle) + os.RemoveAll(baseVhdPath) + os.RemoveAll(diffVhdPath) + } + }() + + if err = FormatWritableLayerVhd(ctx, windows.Handle(handle)); err != nil { + return err + } + // Base vhd handle must be closed before calling SetupBaseLayer in case of Container layer + if err = syscall.CloseHandle(handle); err != nil { + return errors.Wrap(err, "failed to close vhdx handle") + } + + options := OsLayerOptions{ + Type: OsLayerTypeContainer, + } + + // SetupBaseOSLayer expects an empty vhd handle for a container layer and will + // error out otherwise. + if err = SetupBaseOSLayer(ctx, layerPath, 0, options); err != nil { + return err + } + // Create the differencing disk that will be what's copied for the final rw layer + // for a container. + if err = vhd.CreateDiffVhd(diffVhdPath, baseVhdPath, defaultVHDXBlockSizeInMB); err != nil { + return errors.Wrap(err, "failed to create differencing disk") + } + + if err = security.GrantVmGroupAccess(baseVhdPath); err != nil { + return errors.Wrapf(err, "failed to grant vm group access to %s", baseVhdPath) + } + if err = security.GrantVmGroupAccess(diffVhdPath); err != nil { + return errors.Wrapf(err, "failed to grant vm group access to %s", diffVhdPath) + } + return nil +} + +// SetupUtilityVMBaseLayer is a helper to setup a UVMs scratch space. It will create and format +// the vhdx inside and the size is configurable by the sizeInGB parameter. +// +// `uvmPath` is the path to the UtilityVM filesystem. +// +// `baseVhdPath` is the path to where the base vhdx for the UVM should be created. +// +// `diffVhdPath` is the path where the differencing disk for the UVM should be created. +// +// `sizeInGB` specifies the size in gigabytes to make the base vhdx. +func SetupUtilityVMBaseLayer(ctx context.Context, uvmPath, baseVhdPath, diffVhdPath string, sizeInGB uint64) (err error) { + // Remove the base and differencing disks if they exist in case we're asking for a different size. + if _, err := os.Stat(baseVhdPath); err == nil { + if err := os.RemoveAll(baseVhdPath); err != nil { + return errors.Wrap(err, "failed to remove base vhdx") + } + } + if _, err := os.Stat(diffVhdPath); err == nil { + if err := os.RemoveAll(diffVhdPath); err != nil { + return errors.Wrap(err, "failed to remove differencing vhdx") + } + } + + // Just create the vhdx for utilityVM layer, no need to format it. + createParams := &vhd.CreateVirtualDiskParameters{ + Version: 2, + Version2: vhd.CreateVersion2{ + MaximumSize: sizeInGB * 1024 * 1024 * 1024, + BlockSizeInBytes: defaultVHDXBlockSizeInMB * 1024 * 1024, + }, + } + handle, err := vhd.CreateVirtualDisk(baseVhdPath, vhd.VirtualDiskAccessNone, vhd.CreateVirtualDiskFlagNone, createParams) + if err != nil { + return errors.Wrap(err, "failed to create vhdx") + } + + defer func() { + if err != nil { + _ = syscall.CloseHandle(handle) + os.RemoveAll(baseVhdPath) + os.RemoveAll(diffVhdPath) + } + }() + + // If it is a UtilityVM layer then the base vhdx must be attached when calling + // `SetupBaseOSLayer` + attachParams := &vhd.AttachVirtualDiskParameters{ + Version: 2, + } + if err := vhd.AttachVirtualDisk(handle, vhd.AttachVirtualDiskFlagNone, attachParams); err != nil { + return errors.Wrapf(err, "failed to attach virtual disk") + } + + options := OsLayerOptions{ + Type: OsLayerTypeVM, + } + if err := SetupBaseOSLayer(ctx, uvmPath, windows.Handle(handle), options); err != nil { + return err + } + + // Detach and close the handle after setting up the layer as we don't need the handle + // for anything else and we no longer need to be attached either. + if err = vhd.DetachVirtualDisk(handle); err != nil { + return errors.Wrap(err, "failed to detach vhdx") + } + if err = syscall.CloseHandle(handle); err != nil { + return errors.Wrap(err, "failed to close vhdx handle") + } + + // Create the differencing disk that will be what's copied for the final rw layer + // for a container. + if err = vhd.CreateDiffVhd(diffVhdPath, baseVhdPath, defaultVHDXBlockSizeInMB); err != nil { + return errors.Wrap(err, "failed to create differencing disk") + } + + if err := security.GrantVmGroupAccess(baseVhdPath); err != nil { + return errors.Wrapf(err, "failed to grant vm group access to %s", baseVhdPath) + } + if err := security.GrantVmGroupAccess(diffVhdPath); err != nil { + return errors.Wrapf(err, "failed to grant vm group access to %s", diffVhdPath) + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/import.go b/vendor/github.com/Microsoft/hcsshim/computestorage/import.go new file mode 100644 index 0000000000..0c61dab329 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/import.go @@ -0,0 +1,41 @@ +package computestorage + +import ( + "context" + "encoding/json" + + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/pkg/errors" + "go.opencensus.io/trace" +) + +// ImportLayer imports a container layer. +// +// `layerPath` is a path to a directory to import the layer to. If the directory +// does not exist it will be automatically created. +// +// `sourceFolderpath` is a pre-existing folder that contains the layer to +// import. +// +// `layerData` is the parent layer data. +func ImportLayer(ctx context.Context, layerPath, sourceFolderPath string, layerData LayerData) (err error) { + title := "hcsshim.ImportLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("layerPath", layerPath), + trace.StringAttribute("sourceFolderPath", sourceFolderPath), + ) + + bytes, err := json.Marshal(layerData) + if err != nil { + return err + } + + err = hcsImportLayer(layerPath, sourceFolderPath, string(bytes)) + if err != nil { + return errors.Wrap(err, "failed to import layer") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/initialize.go b/vendor/github.com/Microsoft/hcsshim/computestorage/initialize.go new file mode 100644 index 0000000000..53ed8ea6ed --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/initialize.go @@ -0,0 +1,38 @@ +package computestorage + +import ( + "context" + "encoding/json" + + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/pkg/errors" + "go.opencensus.io/trace" +) + +// InitializeWritableLayer initializes a writable layer for a container. +// +// `layerPath` is a path to a directory the layer is mounted. If the +// path does not end in a `\` the platform will append it automatically. +// +// `layerData` is the parent read-only layer data. +func InitializeWritableLayer(ctx context.Context, layerPath string, layerData LayerData) (err error) { + title := "hcsshim.InitializeWritableLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("layerPath", layerPath), + ) + + bytes, err := json.Marshal(layerData) + if err != nil { + return err + } + + // Options are not used in the platform as of RS5 + err = hcsInitializeWritableLayer(layerPath, string(bytes), "") + if err != nil { + return errors.Wrap(err, "failed to intitialize container layer") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/mount.go b/vendor/github.com/Microsoft/hcsshim/computestorage/mount.go new file mode 100644 index 0000000000..fcdbbef814 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/mount.go @@ -0,0 +1,27 @@ +package computestorage + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/pkg/errors" + "go.opencensus.io/trace" + "golang.org/x/sys/windows" +) + +// GetLayerVhdMountPath returns the volume path for a virtual disk of a writable container layer. +func GetLayerVhdMountPath(ctx context.Context, vhdHandle windows.Handle) (path string, err error) { + title := "hcsshim.GetLayerVhdMountPath" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + + var mountPath *uint16 + err = hcsGetLayerVhdMountPath(vhdHandle, &mountPath) + if err != nil { + return "", errors.Wrap(err, "failed to get vhd mount path") + } + path = interop.ConvertAndFreeCoTaskMemString(mountPath) + return path, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/setup.go b/vendor/github.com/Microsoft/hcsshim/computestorage/setup.go new file mode 100644 index 0000000000..06aaf841e8 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/setup.go @@ -0,0 +1,74 @@ +package computestorage + +import ( + "context" + "encoding/json" + + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/Microsoft/hcsshim/osversion" + "github.com/pkg/errors" + "go.opencensus.io/trace" + "golang.org/x/sys/windows" +) + +// SetupBaseOSLayer sets up a layer that contains a base OS for a container. +// +// `layerPath` is a path to a directory containing the layer. +// +// `vhdHandle` is an empty file handle of `options.Type == OsLayerTypeContainer` +// or else it is a file handle to the 'SystemTemplateBase.vhdx' if `options.Type +// == OsLayerTypeVm`. +// +// `options` are the options applied while processing the layer. +func SetupBaseOSLayer(ctx context.Context, layerPath string, vhdHandle windows.Handle, options OsLayerOptions) (err error) { + title := "hcsshim.SetupBaseOSLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("layerPath", layerPath), + ) + + bytes, err := json.Marshal(options) + if err != nil { + return err + } + + err = hcsSetupBaseOSLayer(layerPath, vhdHandle, string(bytes)) + if err != nil { + return errors.Wrap(err, "failed to setup base OS layer") + } + return nil +} + +// SetupBaseOSVolume sets up a volume that contains a base OS for a container. +// +// `layerPath` is a path to a directory containing the layer. +// +// `volumePath` is the path to the volume to be used for setup. +// +// `options` are the options applied while processing the layer. +func SetupBaseOSVolume(ctx context.Context, layerPath, volumePath string, options OsLayerOptions) (err error) { + if osversion.Build() < 19645 { + return errors.New("SetupBaseOSVolume is not present on builds older than 19645") + } + title := "hcsshim.SetupBaseOSVolume" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("layerPath", layerPath), + trace.StringAttribute("volumePath", volumePath), + ) + + bytes, err := json.Marshal(options) + if err != nil { + return err + } + + err = hcsSetupBaseOSVolume(layerPath, volumePath, string(bytes)) + if err != nil { + return errors.Wrap(err, "failed to setup base OS layer") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/storage.go b/vendor/github.com/Microsoft/hcsshim/computestorage/storage.go new file mode 100644 index 0000000000..95aff9c184 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/storage.go @@ -0,0 +1,50 @@ +// Package computestorage is a wrapper around the HCS storage APIs. These are new storage APIs introduced +// separate from the original graphdriver calls intended to give more freedom around creating +// and managing container layers and scratch spaces. +package computestorage + +import ( + hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2" +) + +//go:generate go run ../mksyscall_windows.go -output zsyscall_windows.go storage.go + +//sys hcsImportLayer(layerPath string, sourceFolderPath string, layerData string) (hr error) = computestorage.HcsImportLayer? +//sys hcsExportLayer(layerPath string, exportFolderPath string, layerData string, options string) (hr error) = computestorage.HcsExportLayer? +//sys hcsDestroyLayer(layerPath string) (hr error) = computestorage.HcsDestoryLayer? +//sys hcsSetupBaseOSLayer(layerPath string, handle windows.Handle, options string) (hr error) = computestorage.HcsSetupBaseOSLayer? +//sys hcsInitializeWritableLayer(writableLayerPath string, layerData string, options string) (hr error) = computestorage.HcsInitializeWritableLayer? +//sys hcsAttachLayerStorageFilter(layerPath string, layerData string) (hr error) = computestorage.HcsAttachLayerStorageFilter? +//sys hcsDetachLayerStorageFilter(layerPath string) (hr error) = computestorage.HcsDetachLayerStorageFilter? +//sys hcsFormatWritableLayerVhd(handle windows.Handle) (hr error) = computestorage.HcsFormatWritableLayerVhd? +//sys hcsGetLayerVhdMountPath(vhdHandle windows.Handle, mountPath **uint16) (hr error) = computestorage.HcsGetLayerVhdMountPath? +//sys hcsSetupBaseOSVolume(layerPath string, volumePath string, options string) (hr error) = computestorage.HcsSetupBaseOSVolume? + +// LayerData is the data used to describe parent layer information. +type LayerData struct { + SchemaVersion hcsschema.Version `json:"SchemaVersion,omitempty"` + Layers []hcsschema.Layer `json:"Layers,omitempty"` +} + +// ExportLayerOptions are the set of options that are used with the `computestorage.HcsExportLayer` syscall. +type ExportLayerOptions struct { + IsWritableLayer bool `json:"IsWritableLayer,omitempty"` +} + +// OsLayerType is the type of layer being operated on. +type OsLayerType string + +const ( + // OsLayerTypeContainer is a container layer. + OsLayerTypeContainer OsLayerType = "Container" + // OsLayerTypeVM is a virtual machine layer. + OsLayerTypeVM OsLayerType = "Vm" +) + +// OsLayerOptions are the set of options that are used with the `SetupBaseOSLayer` and +// `SetupBaseOSVolume` calls. +type OsLayerOptions struct { + Type OsLayerType `json:"Type,omitempty"` + DisableCiCacheOptimization bool `json:"DisableCiCacheOptimization,omitempty"` + SkipUpdateBcdForBoot bool `json:"SkipUpdateBcdForBoot,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/computestorage/zsyscall_windows.go new file mode 100644 index 0000000000..4f95180674 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/zsyscall_windows.go @@ -0,0 +1,319 @@ +// Code generated mksyscall_windows.exe DO NOT EDIT + +package computestorage + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modcomputestorage = windows.NewLazySystemDLL("computestorage.dll") + + procHcsImportLayer = modcomputestorage.NewProc("HcsImportLayer") + procHcsExportLayer = modcomputestorage.NewProc("HcsExportLayer") + procHcsDestoryLayer = modcomputestorage.NewProc("HcsDestoryLayer") + procHcsSetupBaseOSLayer = modcomputestorage.NewProc("HcsSetupBaseOSLayer") + procHcsInitializeWritableLayer = modcomputestorage.NewProc("HcsInitializeWritableLayer") + procHcsAttachLayerStorageFilter = modcomputestorage.NewProc("HcsAttachLayerStorageFilter") + procHcsDetachLayerStorageFilter = modcomputestorage.NewProc("HcsDetachLayerStorageFilter") + procHcsFormatWritableLayerVhd = modcomputestorage.NewProc("HcsFormatWritableLayerVhd") + procHcsGetLayerVhdMountPath = modcomputestorage.NewProc("HcsGetLayerVhdMountPath") + procHcsSetupBaseOSVolume = modcomputestorage.NewProc("HcsSetupBaseOSVolume") +) + +func hcsImportLayer(layerPath string, sourceFolderPath string, layerData string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(layerPath) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(sourceFolderPath) + if hr != nil { + return + } + var _p2 *uint16 + _p2, hr = syscall.UTF16PtrFromString(layerData) + if hr != nil { + return + } + return _hcsImportLayer(_p0, _p1, _p2) +} + +func _hcsImportLayer(layerPath *uint16, sourceFolderPath *uint16, layerData *uint16) (hr error) { + if hr = procHcsImportLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsImportLayer.Addr(), 3, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(sourceFolderPath)), uintptr(unsafe.Pointer(layerData))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsExportLayer(layerPath string, exportFolderPath string, layerData string, options string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(layerPath) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(exportFolderPath) + if hr != nil { + return + } + var _p2 *uint16 + _p2, hr = syscall.UTF16PtrFromString(layerData) + if hr != nil { + return + } + var _p3 *uint16 + _p3, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsExportLayer(_p0, _p1, _p2, _p3) +} + +func _hcsExportLayer(layerPath *uint16, exportFolderPath *uint16, layerData *uint16, options *uint16) (hr error) { + if hr = procHcsExportLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsExportLayer.Addr(), 4, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(exportFolderPath)), uintptr(unsafe.Pointer(layerData)), uintptr(unsafe.Pointer(options)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsDestroyLayer(layerPath string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(layerPath) + if hr != nil { + return + } + return _hcsDestroyLayer(_p0) +} + +func _hcsDestroyLayer(layerPath *uint16) (hr error) { + if hr = procHcsDestoryLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsDestoryLayer.Addr(), 1, uintptr(unsafe.Pointer(layerPath)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsSetupBaseOSLayer(layerPath string, handle windows.Handle, options string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(layerPath) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsSetupBaseOSLayer(_p0, handle, _p1) +} + +func _hcsSetupBaseOSLayer(layerPath *uint16, handle windows.Handle, options *uint16) (hr error) { + if hr = procHcsSetupBaseOSLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsSetupBaseOSLayer.Addr(), 3, uintptr(unsafe.Pointer(layerPath)), uintptr(handle), uintptr(unsafe.Pointer(options))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsInitializeWritableLayer(writableLayerPath string, layerData string, options string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(writableLayerPath) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(layerData) + if hr != nil { + return + } + var _p2 *uint16 + _p2, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsInitializeWritableLayer(_p0, _p1, _p2) +} + +func _hcsInitializeWritableLayer(writableLayerPath *uint16, layerData *uint16, options *uint16) (hr error) { + if hr = procHcsInitializeWritableLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsInitializeWritableLayer.Addr(), 3, uintptr(unsafe.Pointer(writableLayerPath)), uintptr(unsafe.Pointer(layerData)), uintptr(unsafe.Pointer(options))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsAttachLayerStorageFilter(layerPath string, layerData string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(layerPath) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(layerData) + if hr != nil { + return + } + return _hcsAttachLayerStorageFilter(_p0, _p1) +} + +func _hcsAttachLayerStorageFilter(layerPath *uint16, layerData *uint16) (hr error) { + if hr = procHcsAttachLayerStorageFilter.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsAttachLayerStorageFilter.Addr(), 2, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(layerData)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsDetachLayerStorageFilter(layerPath string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(layerPath) + if hr != nil { + return + } + return _hcsDetachLayerStorageFilter(_p0) +} + +func _hcsDetachLayerStorageFilter(layerPath *uint16) (hr error) { + if hr = procHcsDetachLayerStorageFilter.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsDetachLayerStorageFilter.Addr(), 1, uintptr(unsafe.Pointer(layerPath)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsFormatWritableLayerVhd(handle windows.Handle) (hr error) { + if hr = procHcsFormatWritableLayerVhd.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsFormatWritableLayerVhd.Addr(), 1, uintptr(handle), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsGetLayerVhdMountPath(vhdHandle windows.Handle, mountPath **uint16) (hr error) { + if hr = procHcsGetLayerVhdMountPath.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsGetLayerVhdMountPath.Addr(), 2, uintptr(vhdHandle), uintptr(unsafe.Pointer(mountPath)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsSetupBaseOSVolume(layerPath string, volumePath string, options string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(layerPath) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(volumePath) + if hr != nil { + return + } + var _p2 *uint16 + _p2, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsSetupBaseOSVolume(_p0, _p1, _p2) +} + +func _hcsSetupBaseOSVolume(layerPath *uint16, volumePath *uint16, options *uint16) (hr error) { + if hr = procHcsSetupBaseOSVolume.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsSetupBaseOSVolume.Addr(), 3, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(volumePath)), uintptr(unsafe.Pointer(options))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} diff --git a/vendor/github.com/Microsoft/hcsshim/container.go b/vendor/github.com/Microsoft/hcsshim/container.go new file mode 100644 index 0000000000..bfd722898e --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/container.go @@ -0,0 +1,223 @@ +package hcsshim + +import ( + "context" + "fmt" + "os" + "sync" + "time" + + "github.com/Microsoft/hcsshim/internal/hcs" + "github.com/Microsoft/hcsshim/internal/hcs/schema1" + "github.com/Microsoft/hcsshim/internal/mergemaps" +) + +// ContainerProperties holds the properties for a container and the processes running in that container +type ContainerProperties = schema1.ContainerProperties + +// MemoryStats holds the memory statistics for a container +type MemoryStats = schema1.MemoryStats + +// ProcessorStats holds the processor statistics for a container +type ProcessorStats = schema1.ProcessorStats + +// StorageStats holds the storage statistics for a container +type StorageStats = schema1.StorageStats + +// NetworkStats holds the network statistics for a container +type NetworkStats = schema1.NetworkStats + +// Statistics is the structure returned by a statistics call on a container +type Statistics = schema1.Statistics + +// ProcessList is the structure of an item returned by a ProcessList call on a container +type ProcessListItem = schema1.ProcessListItem + +// MappedVirtualDiskController is the structure of an item returned by a MappedVirtualDiskList call on a container +type MappedVirtualDiskController = schema1.MappedVirtualDiskController + +// Type of Request Support in ModifySystem +type RequestType = schema1.RequestType + +// Type of Resource Support in ModifySystem +type ResourceType = schema1.ResourceType + +// RequestType const +const ( + Add = schema1.Add + Remove = schema1.Remove + Network = schema1.Network +) + +// ResourceModificationRequestResponse is the structure used to send request to the container to modify the system +// Supported resource types are Network and Request Types are Add/Remove +type ResourceModificationRequestResponse = schema1.ResourceModificationRequestResponse + +type container struct { + system *hcs.System + waitOnce sync.Once + waitErr error + waitCh chan struct{} +} + +// createComputeSystemAdditionalJSON is read from the environment at initialisation +// time. It allows an environment variable to define additional JSON which +// is merged in the CreateComputeSystem call to HCS. +var createContainerAdditionalJSON []byte + +func init() { + createContainerAdditionalJSON = ([]byte)(os.Getenv("HCSSHIM_CREATECONTAINER_ADDITIONALJSON")) +} + +// CreateContainer creates a new container with the given configuration but does not start it. +func CreateContainer(id string, c *ContainerConfig) (Container, error) { + fullConfig, err := mergemaps.MergeJSON(c, createContainerAdditionalJSON) + if err != nil { + return nil, fmt.Errorf("failed to merge additional JSON '%s': %s", createContainerAdditionalJSON, err) + } + + system, err := hcs.CreateComputeSystem(context.Background(), id, fullConfig) + if err != nil { + return nil, err + } + return &container{system: system}, err +} + +// OpenContainer opens an existing container by ID. +func OpenContainer(id string) (Container, error) { + system, err := hcs.OpenComputeSystem(context.Background(), id) + if err != nil { + return nil, err + } + return &container{system: system}, err +} + +// GetContainers gets a list of the containers on the system that match the query +func GetContainers(q ComputeSystemQuery) ([]ContainerProperties, error) { + return hcs.GetComputeSystems(context.Background(), q) +} + +// Start synchronously starts the container. +func (container *container) Start() error { + return convertSystemError(container.system.Start(context.Background()), container) +} + +// Shutdown requests a container shutdown, but it may not actually be shutdown until Wait() succeeds. +func (container *container) Shutdown() error { + err := container.system.Shutdown(context.Background()) + if err != nil { + return convertSystemError(err, container) + } + return &ContainerError{Container: container, Err: ErrVmcomputeOperationPending, Operation: "hcsshim::ComputeSystem::Shutdown"} +} + +// Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds. +func (container *container) Terminate() error { + err := container.system.Terminate(context.Background()) + if err != nil { + return convertSystemError(err, container) + } + return &ContainerError{Container: container, Err: ErrVmcomputeOperationPending, Operation: "hcsshim::ComputeSystem::Terminate"} +} + +// Waits synchronously waits for the container to shutdown or terminate. +func (container *container) Wait() error { + err := container.system.Wait() + if err == nil { + err = container.system.ExitError() + } + return convertSystemError(err, container) +} + +// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It +// returns false if timeout occurs. +func (container *container) WaitTimeout(timeout time.Duration) error { + container.waitOnce.Do(func() { + container.waitCh = make(chan struct{}) + go func() { + container.waitErr = container.Wait() + close(container.waitCh) + }() + }) + t := time.NewTimer(timeout) + defer t.Stop() + select { + case <-t.C: + return &ContainerError{Container: container, Err: ErrTimeout, Operation: "hcsshim::ComputeSystem::Wait"} + case <-container.waitCh: + return container.waitErr + } +} + +// Pause pauses the execution of a container. +func (container *container) Pause() error { + return convertSystemError(container.system.Pause(context.Background()), container) +} + +// Resume resumes the execution of a container. +func (container *container) Resume() error { + return convertSystemError(container.system.Resume(context.Background()), container) +} + +// HasPendingUpdates returns true if the container has updates pending to install +func (container *container) HasPendingUpdates() (bool, error) { + return false, nil +} + +// Statistics returns statistics for the container. This is a legacy v1 call +func (container *container) Statistics() (Statistics, error) { + properties, err := container.system.Properties(context.Background(), schema1.PropertyTypeStatistics) + if err != nil { + return Statistics{}, convertSystemError(err, container) + } + + return properties.Statistics, nil +} + +// ProcessList returns an array of ProcessListItems for the container. This is a legacy v1 call +func (container *container) ProcessList() ([]ProcessListItem, error) { + properties, err := container.system.Properties(context.Background(), schema1.PropertyTypeProcessList) + if err != nil { + return nil, convertSystemError(err, container) + } + + return properties.ProcessList, nil +} + +// This is a legacy v1 call +func (container *container) MappedVirtualDisks() (map[int]MappedVirtualDiskController, error) { + properties, err := container.system.Properties(context.Background(), schema1.PropertyTypeMappedVirtualDisk) + if err != nil { + return nil, convertSystemError(err, container) + } + + return properties.MappedVirtualDiskControllers, nil +} + +// CreateProcess launches a new process within the container. +func (container *container) CreateProcess(c *ProcessConfig) (Process, error) { + p, err := container.system.CreateProcess(context.Background(), c) + if err != nil { + return nil, convertSystemError(err, container) + } + return &process{p: p.(*hcs.Process)}, nil +} + +// OpenProcess gets an interface to an existing process within the container. +func (container *container) OpenProcess(pid int) (Process, error) { + p, err := container.system.OpenProcess(context.Background(), pid) + if err != nil { + return nil, convertSystemError(err, container) + } + return &process{p: p}, nil +} + +// Close cleans up any state associated with the container but does not terminate or wait for it. +func (container *container) Close() error { + return convertSystemError(container.system.Close(), container) +} + +// Modify the System +func (container *container) Modify(config *ResourceModificationRequestResponse) error { + return convertSystemError(container.system.Modify(context.Background(), config), container) +} diff --git a/vendor/github.com/Microsoft/hcsshim/errors.go b/vendor/github.com/Microsoft/hcsshim/errors.go new file mode 100644 index 0000000000..f367022e71 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/errors.go @@ -0,0 +1,245 @@ +package hcsshim + +import ( + "fmt" + "syscall" + + "github.com/Microsoft/hcsshim/internal/hns" + + "github.com/Microsoft/hcsshim/internal/hcs" + "github.com/Microsoft/hcsshim/internal/hcserror" +) + +var ( + // ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists = hcs.exist + ErrComputeSystemDoesNotExist = hcs.ErrComputeSystemDoesNotExist + + // ErrElementNotFound is an error encountered when the object being referenced does not exist + ErrElementNotFound = hcs.ErrElementNotFound + + // ErrElementNotFound is an error encountered when the object being referenced does not exist + ErrNotSupported = hcs.ErrNotSupported + + // ErrInvalidData is an error encountered when the request being sent to hcs is invalid/unsupported + // decimal -2147024883 / hex 0x8007000d + ErrInvalidData = hcs.ErrInvalidData + + // ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed + ErrHandleClose = hcs.ErrHandleClose + + // ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method + ErrAlreadyClosed = hcs.ErrAlreadyClosed + + // ErrInvalidNotificationType is an error encountered when an invalid notification type is used + ErrInvalidNotificationType = hcs.ErrInvalidNotificationType + + // ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation + ErrInvalidProcessState = hcs.ErrInvalidProcessState + + // ErrTimeout is an error encountered when waiting on a notification times out + ErrTimeout = hcs.ErrTimeout + + // ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for + // a different expected notification + ErrUnexpectedContainerExit = hcs.ErrUnexpectedContainerExit + + // ErrUnexpectedProcessAbort is the error encountered when communication with the compute service + // is lost while waiting for a notification + ErrUnexpectedProcessAbort = hcs.ErrUnexpectedProcessAbort + + // ErrUnexpectedValue is an error encountered when hcs returns an invalid value + ErrUnexpectedValue = hcs.ErrUnexpectedValue + + // ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container + ErrVmcomputeAlreadyStopped = hcs.ErrVmcomputeAlreadyStopped + + // ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously + ErrVmcomputeOperationPending = hcs.ErrVmcomputeOperationPending + + // ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation + ErrVmcomputeOperationInvalidState = hcs.ErrVmcomputeOperationInvalidState + + // ErrProcNotFound is an error encountered when a procedure look up fails. + ErrProcNotFound = hcs.ErrProcNotFound + + // ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2 + // builds when the underlying silo might be in the process of terminating. HCS was fixed in RS3. + ErrVmcomputeOperationAccessIsDenied = hcs.ErrVmcomputeOperationAccessIsDenied + + // ErrVmcomputeInvalidJSON is an error encountered when the compute system does not support/understand the messages sent by management + ErrVmcomputeInvalidJSON = hcs.ErrVmcomputeInvalidJSON + + // ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message + ErrVmcomputeUnknownMessage = hcs.ErrVmcomputeUnknownMessage + + // ErrNotSupported is an error encountered when hcs doesn't support the request + ErrPlatformNotSupported = hcs.ErrPlatformNotSupported +) + +type EndpointNotFoundError = hns.EndpointNotFoundError +type NetworkNotFoundError = hns.NetworkNotFoundError + +// ProcessError is an error encountered in HCS during an operation on a Process object +type ProcessError struct { + Process *process + Operation string + Err error + Events []hcs.ErrorEvent +} + +// ContainerError is an error encountered in HCS during an operation on a Container object +type ContainerError struct { + Container *container + Operation string + Err error + Events []hcs.ErrorEvent +} + +func (e *ContainerError) Error() string { + if e == nil { + return "" + } + + if e.Container == nil { + return "unexpected nil container for error: " + e.Err.Error() + } + + s := "container " + e.Container.system.ID() + + if e.Operation != "" { + s += " encountered an error during " + e.Operation + } + + switch e.Err.(type) { + case nil: + break + case syscall.Errno: + s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, hcserror.Win32FromError(e.Err)) + default: + s += fmt.Sprintf(": %s", e.Err.Error()) + } + + for _, ev := range e.Events { + s += "\n" + ev.String() + } + + return s +} + +func (e *ProcessError) Error() string { + if e == nil { + return "" + } + + if e.Process == nil { + return "Unexpected nil process for error: " + e.Err.Error() + } + + s := fmt.Sprintf("process %d in container %s", e.Process.p.Pid(), e.Process.p.SystemID()) + if e.Operation != "" { + s += " encountered an error during " + e.Operation + } + + switch e.Err.(type) { + case nil: + break + case syscall.Errno: + s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, hcserror.Win32FromError(e.Err)) + default: + s += fmt.Sprintf(": %s", e.Err.Error()) + } + + for _, ev := range e.Events { + s += "\n" + ev.String() + } + + return s +} + +// IsNotExist checks if an error is caused by the Container or Process not existing. +// Note: Currently, ErrElementNotFound can mean that a Process has either +// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist +// will currently return true when the error is ErrElementNotFound. +func IsNotExist(err error) bool { + if _, ok := err.(EndpointNotFoundError); ok { + return true + } + if _, ok := err.(NetworkNotFoundError); ok { + return true + } + return hcs.IsNotExist(getInnerError(err)) +} + +// IsAlreadyClosed checks if an error is caused by the Container or Process having been +// already closed by a call to the Close() method. +func IsAlreadyClosed(err error) bool { + return hcs.IsAlreadyClosed(getInnerError(err)) +} + +// IsPending returns a boolean indicating whether the error is that +// the requested operation is being completed in the background. +func IsPending(err error) bool { + return hcs.IsPending(getInnerError(err)) +} + +// IsTimeout returns a boolean indicating whether the error is caused by +// a timeout waiting for the operation to complete. +func IsTimeout(err error) bool { + return hcs.IsTimeout(getInnerError(err)) +} + +// IsAlreadyStopped returns a boolean indicating whether the error is caused by +// a Container or Process being already stopped. +// Note: Currently, ErrElementNotFound can mean that a Process has either +// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist +// will currently return true when the error is ErrElementNotFound. +func IsAlreadyStopped(err error) bool { + return hcs.IsAlreadyStopped(getInnerError(err)) +} + +// IsNotSupported returns a boolean indicating whether the error is caused by +// unsupported platform requests +// Note: Currently Unsupported platform requests can be mean either +// ErrVmcomputeInvalidJSON, ErrInvalidData, ErrNotSupported or ErrVmcomputeUnknownMessage +// is thrown from the Platform +func IsNotSupported(err error) bool { + return hcs.IsNotSupported(getInnerError(err)) +} + +// IsOperationInvalidState returns true when err is caused by +// `ErrVmcomputeOperationInvalidState`. +func IsOperationInvalidState(err error) bool { + return hcs.IsOperationInvalidState(getInnerError(err)) +} + +// IsAccessIsDenied returns true when err is caused by +// `ErrVmcomputeOperationAccessIsDenied`. +func IsAccessIsDenied(err error) bool { + return hcs.IsAccessIsDenied(getInnerError(err)) +} + +func getInnerError(err error) error { + switch pe := err.(type) { + case nil: + return nil + case *ContainerError: + err = pe.Err + case *ProcessError: + err = pe.Err + } + return err +} + +func convertSystemError(err error, c *container) error { + if serr, ok := err.(*hcs.SystemError); ok { + return &ContainerError{Container: c, Operation: serr.Op, Err: serr.Err, Events: serr.Events} + } + return err +} + +func convertProcessError(err error, p *process) error { + if perr, ok := err.(*hcs.ProcessError); ok { + return &ProcessError{Process: p, Operation: perr.Op, Err: perr.Err, Events: perr.Events} + } + return err +} diff --git a/vendor/github.com/Microsoft/hcsshim/functional_tests.ps1 b/vendor/github.com/Microsoft/hcsshim/functional_tests.ps1 new file mode 100644 index 0000000000..ce6edbcf32 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/functional_tests.ps1 @@ -0,0 +1,12 @@ +# Requirements so far: +# dockerd running +# - image microsoft/nanoserver (matching host base image) docker load -i c:\baseimages\nanoserver.tar +# - image alpine (linux) docker pull --platform=linux alpine + + +# TODO: Add this a parameter for debugging. ie "functional-tests -debug=$true" +#$env:HCSSHIM_FUNCTIONAL_TESTS_DEBUG="yes please" + +#pushd uvm +go test -v -tags "functional uvmcreate uvmscratch uvmscsi uvmvpmem uvmvsmb uvmp9" ./... +#popd \ No newline at end of file diff --git a/vendor/github.com/Microsoft/hcsshim/go.mod b/vendor/github.com/Microsoft/hcsshim/go.mod new file mode 100644 index 0000000000..9c60dd3025 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/go.mod @@ -0,0 +1,39 @@ +module github.com/Microsoft/hcsshim + +go 1.13 + +require ( + github.com/BurntSushi/toml v0.3.1 + github.com/Microsoft/go-winio v0.4.17 + github.com/cenkalti/backoff/v4 v4.1.1 + github.com/containerd/cgroups v1.0.1 + github.com/containerd/console v1.0.2 + github.com/containerd/containerd v1.5.7 + github.com/containerd/go-runc v1.0.0 + github.com/containerd/ttrpc v1.1.0 + github.com/containerd/typeurl v1.0.2 + github.com/gogo/protobuf v1.3.2 + github.com/golang/mock v1.6.0 + github.com/google/go-cmp v0.5.6 + github.com/google/go-containerregistry v0.5.1 + github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3 + github.com/mattn/go-shellwords v1.0.6 + github.com/opencontainers/runc v1.0.2 + github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 + github.com/pkg/errors v0.9.1 + github.com/sirupsen/logrus v1.8.1 + github.com/urfave/cli v1.22.2 + github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852 + github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae + go.etcd.io/bbolt v1.3.6 + go.opencensus.io v0.22.3 + golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e + google.golang.org/grpc v1.40.0 +) + +replace ( + google.golang.org/genproto => google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63 + google.golang.org/grpc => google.golang.org/grpc v1.27.1 +) diff --git a/vendor/github.com/Microsoft/hcsshim/go.sum b/vendor/github.com/Microsoft/hcsshim/go.sum new file mode 100644 index 0000000000..93c37657f3 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/go.sum @@ -0,0 +1,993 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17 h1:iT12IBVClFevaf8PuVyi3UmZOVh4OqnaLxDTW2O6j3w= +github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= +github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= +github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= +github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= +github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= +github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= +github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= +github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= +github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= +github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= +github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= +github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= +github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.0.1 h1:iJnMvco9XGvKUvNQkv88bE4uJXxRQH18efbKo9w5vHQ= +github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/containerd/console v1.0.2 h1:Pi6D+aZXM+oUw1czuKgH5IJ+y0jhYcwBJfx5/Ghn9dE= +github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= +github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= +github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= +github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= +github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= +github.com/containerd/containerd v1.5.7 h1:rQyoYtj4KddB3bxG6SAqd4+08gePNyJjRqvOIfV3rkM= +github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= +github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= +github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= +github.com/containerd/continuity v0.1.0 h1:UFRRY5JemiAhPZrr/uE0n8fMTLcZsUvySPr1+D7pgr8= +github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= +github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/fifo v1.0.0 h1:6PirWBr9/L7GDamKr+XM0IeUFXu5mf3M/BPpH9gaLBU= +github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= +github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= +github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/go-runc v1.0.0 h1:oU+lLv1ULm5taqgV/CJivypVODI4SUz1znWjv3nNYS0= +github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= +github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= +github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= +github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= +github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= +github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/stargz-snapshotter/estargz v0.4.1 h1:5e7heayhB7CcgdTkqfZqrNaNv15gABwr3Q2jBTbLlt4= +github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= +github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.1.0 h1:GbtyLRxb0gOLR0TYQWt3O6B0NvT8tMdorEHqIQo/lWI= +github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= +github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= +github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY7aY= +github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= +github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= +github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= +github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= +github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= +github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= +github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= +github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= +github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= +github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= +github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017 h1:2HQmlpI3yI9deH18Q6xiSOIjXD4sLI55Y/gfpa8/558= +github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= +github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7 h1:Cvj7S8I4Xpx78KAl6TwTmMHuHlZ/0SM60NUneGJQ7IE= +github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= +github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= +github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-containerregistry v0.5.1 h1:/+mFTs4AlwsJ/mJe8NDtKb7BxLtbZFpcn8vDsneEkwQ= +github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3 h1:jUp75lepDg0phMUJBCmvaeFDldD2N3S1lBuPwUTszio= +github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/mattn/go-shellwords v1.0.6 h1:9Jok5pILi5S1MnDirGVTufYGtksUs/V2BWUP3ZkeUUI= +github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= +github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM= +github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= +github.com/opencontainers/runc v1.0.2 h1:opHZMaswlyxz1OuGpBE53Dwe4/xF7EZTY0A2L/FpCOg= +github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= +github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= +github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= +github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= +github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852 h1:cPXZWzzG0NllBLdjWoD1nDfaqu98YMv+OneaKc8sPOA= +github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw= +golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63 h1:YzfoEYWbODU5Fbt37+h7X16BWQbad7Q4S6gclTKFXM8= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= +k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= +k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= +k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= +k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= +k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= +k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= +k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= +k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= +k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= +k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= +k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= +k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= +k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= +k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= +k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/vendor/github.com/Microsoft/hcsshim/hcsshim.go b/vendor/github.com/Microsoft/hcsshim/hcsshim.go new file mode 100644 index 0000000000..ceb3ac85ee --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/hcsshim.go @@ -0,0 +1,28 @@ +// Shim for the Host Compute Service (HCS) to manage Windows Server +// containers and Hyper-V containers. + +package hcsshim + +import ( + "syscall" + + "github.com/Microsoft/hcsshim/internal/hcserror" +) + +//go:generate go run mksyscall_windows.go -output zsyscall_windows.go hcsshim.go + +//sys SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) = iphlpapi.SetCurrentThreadCompartmentId + +const ( + // Specific user-visible exit codes + WaitErrExecFailed = 32767 + + ERROR_GEN_FAILURE = hcserror.ERROR_GEN_FAILURE + ERROR_SHUTDOWN_IN_PROGRESS = syscall.Errno(1115) + WSAEINVAL = syscall.Errno(10022) + + // Timeout on wait calls + TimeoutInfinite = 0xFFFFFFFF +) + +type HcsError = hcserror.HcsError diff --git a/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go b/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go new file mode 100644 index 0000000000..9e0059447d --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go @@ -0,0 +1,118 @@ +package hcsshim + +import ( + "github.com/Microsoft/hcsshim/internal/hns" +) + +// HNSEndpoint represents a network endpoint in HNS +type HNSEndpoint = hns.HNSEndpoint + +// HNSEndpointStats represent the stats for an networkendpoint in HNS +type HNSEndpointStats = hns.EndpointStats + +// Namespace represents a Compartment. +type Namespace = hns.Namespace + +//SystemType represents the type of the system on which actions are done +type SystemType string + +// SystemType const +const ( + ContainerType SystemType = "Container" + VirtualMachineType SystemType = "VirtualMachine" + HostType SystemType = "Host" +) + +// EndpointAttachDetachRequest is the structure used to send request to the container to modify the system +// Supported resource types are Network and Request Types are Add/Remove +type EndpointAttachDetachRequest = hns.EndpointAttachDetachRequest + +// EndpointResquestResponse is object to get the endpoint request response +type EndpointResquestResponse = hns.EndpointResquestResponse + +// HNSEndpointRequest makes a HNS call to modify/query a network endpoint +func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) { + return hns.HNSEndpointRequest(method, path, request) +} + +// HNSListEndpointRequest makes a HNS call to query the list of available endpoints +func HNSListEndpointRequest() ([]HNSEndpoint, error) { + return hns.HNSListEndpointRequest() +} + +// HotAttachEndpoint makes a HCS Call to attach the endpoint to the container +func HotAttachEndpoint(containerID string, endpointID string) error { + endpoint, err := GetHNSEndpointByID(endpointID) + if err != nil { + return err + } + isAttached, err := endpoint.IsAttached(containerID) + if isAttached { + return err + } + return modifyNetworkEndpoint(containerID, endpointID, Add) +} + +// HotDetachEndpoint makes a HCS Call to detach the endpoint from the container +func HotDetachEndpoint(containerID string, endpointID string) error { + endpoint, err := GetHNSEndpointByID(endpointID) + if err != nil { + return err + } + isAttached, err := endpoint.IsAttached(containerID) + if !isAttached { + return err + } + return modifyNetworkEndpoint(containerID, endpointID, Remove) +} + +// ModifyContainer corresponding to the container id, by sending a request +func modifyContainer(id string, request *ResourceModificationRequestResponse) error { + container, err := OpenContainer(id) + if err != nil { + if IsNotExist(err) { + return ErrComputeSystemDoesNotExist + } + return getInnerError(err) + } + defer container.Close() + err = container.Modify(request) + if err != nil { + if IsNotSupported(err) { + return ErrPlatformNotSupported + } + return getInnerError(err) + } + + return nil +} + +func modifyNetworkEndpoint(containerID string, endpointID string, request RequestType) error { + requestMessage := &ResourceModificationRequestResponse{ + Resource: Network, + Request: request, + Data: endpointID, + } + err := modifyContainer(containerID, requestMessage) + + if err != nil { + return err + } + + return nil +} + +// GetHNSEndpointByID get the Endpoint by ID +func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) { + return hns.GetHNSEndpointByID(endpointID) +} + +// GetHNSEndpointByName gets the endpoint filtered by Name +func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) { + return hns.GetHNSEndpointByName(endpointName) +} + +// GetHNSEndpointStats gets the endpoint stats by ID +func GetHNSEndpointStats(endpointName string) (*HNSEndpointStats, error) { + return hns.GetHNSEndpointStats(endpointName) +} diff --git a/vendor/github.com/Microsoft/hcsshim/hnsglobals.go b/vendor/github.com/Microsoft/hcsshim/hnsglobals.go new file mode 100644 index 0000000000..2b53819047 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/hnsglobals.go @@ -0,0 +1,16 @@ +package hcsshim + +import ( + "github.com/Microsoft/hcsshim/internal/hns" +) + +type HNSGlobals = hns.HNSGlobals +type HNSVersion = hns.HNSVersion + +var ( + HNSVersion1803 = hns.HNSVersion1803 +) + +func GetHNSGlobals() (*HNSGlobals, error) { + return hns.GetHNSGlobals() +} diff --git a/vendor/github.com/Microsoft/hcsshim/hnsnetwork.go b/vendor/github.com/Microsoft/hcsshim/hnsnetwork.go new file mode 100644 index 0000000000..f775fa1d07 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/hnsnetwork.go @@ -0,0 +1,36 @@ +package hcsshim + +import ( + "github.com/Microsoft/hcsshim/internal/hns" +) + +// Subnet is assoicated with a network and represents a list +// of subnets available to the network +type Subnet = hns.Subnet + +// MacPool is assoicated with a network and represents a list +// of macaddresses available to the network +type MacPool = hns.MacPool + +// HNSNetwork represents a network in HNS +type HNSNetwork = hns.HNSNetwork + +// HNSNetworkRequest makes a call into HNS to update/query a single network +func HNSNetworkRequest(method, path, request string) (*HNSNetwork, error) { + return hns.HNSNetworkRequest(method, path, request) +} + +// HNSListNetworkRequest makes a HNS call to query the list of available networks +func HNSListNetworkRequest(method, path, request string) ([]HNSNetwork, error) { + return hns.HNSListNetworkRequest(method, path, request) +} + +// GetHNSNetworkByID +func GetHNSNetworkByID(networkID string) (*HNSNetwork, error) { + return hns.GetHNSNetworkByID(networkID) +} + +// GetHNSNetworkName filtered by Name +func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) { + return hns.GetHNSNetworkByName(networkName) +} diff --git a/vendor/github.com/Microsoft/hcsshim/hnspolicy.go b/vendor/github.com/Microsoft/hcsshim/hnspolicy.go new file mode 100644 index 0000000000..00ab263644 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/hnspolicy.go @@ -0,0 +1,60 @@ +package hcsshim + +import ( + "github.com/Microsoft/hcsshim/internal/hns" +) + +// Type of Request Support in ModifySystem +type PolicyType = hns.PolicyType + +// RequestType const +const ( + Nat = hns.Nat + ACL = hns.ACL + PA = hns.PA + VLAN = hns.VLAN + VSID = hns.VSID + VNet = hns.VNet + L2Driver = hns.L2Driver + Isolation = hns.Isolation + QOS = hns.QOS + OutboundNat = hns.OutboundNat + ExternalLoadBalancer = hns.ExternalLoadBalancer + Route = hns.Route + Proxy = hns.Proxy +) + +type ProxyPolicy = hns.ProxyPolicy + +type NatPolicy = hns.NatPolicy + +type QosPolicy = hns.QosPolicy + +type IsolationPolicy = hns.IsolationPolicy + +type VlanPolicy = hns.VlanPolicy + +type VsidPolicy = hns.VsidPolicy + +type PaPolicy = hns.PaPolicy + +type OutboundNatPolicy = hns.OutboundNatPolicy + +type ActionType = hns.ActionType +type DirectionType = hns.DirectionType +type RuleType = hns.RuleType + +const ( + Allow = hns.Allow + Block = hns.Block + + In = hns.In + Out = hns.Out + + Host = hns.Host + Switch = hns.Switch +) + +type ACLPolicy = hns.ACLPolicy + +type Policy = hns.Policy diff --git a/vendor/github.com/Microsoft/hcsshim/hnspolicylist.go b/vendor/github.com/Microsoft/hcsshim/hnspolicylist.go new file mode 100644 index 0000000000..55aaa4a50e --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/hnspolicylist.go @@ -0,0 +1,47 @@ +package hcsshim + +import ( + "github.com/Microsoft/hcsshim/internal/hns" +) + +// RoutePolicy is a structure defining schema for Route based Policy +type RoutePolicy = hns.RoutePolicy + +// ELBPolicy is a structure defining schema for ELB LoadBalancing based Policy +type ELBPolicy = hns.ELBPolicy + +// LBPolicy is a structure defining schema for LoadBalancing based Policy +type LBPolicy = hns.LBPolicy + +// PolicyList is a structure defining schema for Policy list request +type PolicyList = hns.PolicyList + +// HNSPolicyListRequest makes a call into HNS to update/query a single network +func HNSPolicyListRequest(method, path, request string) (*PolicyList, error) { + return hns.HNSPolicyListRequest(method, path, request) +} + +// HNSListPolicyListRequest gets all the policy list +func HNSListPolicyListRequest() ([]PolicyList, error) { + return hns.HNSListPolicyListRequest() +} + +// PolicyListRequest makes a HNS call to modify/query a network policy list +func PolicyListRequest(method, path, request string) (*PolicyList, error) { + return hns.PolicyListRequest(method, path, request) +} + +// GetPolicyListByID get the policy list by ID +func GetPolicyListByID(policyListID string) (*PolicyList, error) { + return hns.GetPolicyListByID(policyListID) +} + +// AddLoadBalancer policy list for the specified endpoints +func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) { + return hns.AddLoadBalancer(endpoints, isILB, sourceVIP, vip, protocol, internalPort, externalPort) +} + +// AddRoute adds route policy list for the specified endpoints +func AddRoute(endpoints []HNSEndpoint, destinationPrefix string, nextHop string, encapEnabled bool) (*PolicyList, error) { + return hns.AddRoute(endpoints, destinationPrefix, nextHop, encapEnabled) +} diff --git a/vendor/github.com/Microsoft/hcsshim/hnssupport.go b/vendor/github.com/Microsoft/hcsshim/hnssupport.go new file mode 100644 index 0000000000..69405244b6 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/hnssupport.go @@ -0,0 +1,13 @@ +package hcsshim + +import ( + "github.com/Microsoft/hcsshim/internal/hns" +) + +type HNSSupportedFeatures = hns.HNSSupportedFeatures + +type HNSAclFeatures = hns.HNSAclFeatures + +func GetHNSSupportedFeatures() HNSSupportedFeatures { + return hns.GetHNSSupportedFeatures() +} diff --git a/vendor/github.com/Microsoft/hcsshim/interface.go b/vendor/github.com/Microsoft/hcsshim/interface.go new file mode 100644 index 0000000000..300eb59966 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/interface.go @@ -0,0 +1,114 @@ +package hcsshim + +import ( + "io" + "time" + + "github.com/Microsoft/hcsshim/internal/hcs/schema1" +) + +// ProcessConfig is used as both the input of Container.CreateProcess +// and to convert the parameters to JSON for passing onto the HCS +type ProcessConfig = schema1.ProcessConfig + +type Layer = schema1.Layer +type MappedDir = schema1.MappedDir +type MappedPipe = schema1.MappedPipe +type HvRuntime = schema1.HvRuntime +type MappedVirtualDisk = schema1.MappedVirtualDisk + +// AssignedDevice represents a device that has been directly assigned to a container +// +// NOTE: Support added in RS5 +type AssignedDevice = schema1.AssignedDevice + +// ContainerConfig is used as both the input of CreateContainer +// and to convert the parameters to JSON for passing onto the HCS +type ContainerConfig = schema1.ContainerConfig + +type ComputeSystemQuery = schema1.ComputeSystemQuery + +// Container represents a created (but not necessarily running) container. +type Container interface { + // Start synchronously starts the container. + Start() error + + // Shutdown requests a container shutdown, but it may not actually be shutdown until Wait() succeeds. + Shutdown() error + + // Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds. + Terminate() error + + // Waits synchronously waits for the container to shutdown or terminate. + Wait() error + + // WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It + // returns false if timeout occurs. + WaitTimeout(time.Duration) error + + // Pause pauses the execution of a container. + Pause() error + + // Resume resumes the execution of a container. + Resume() error + + // HasPendingUpdates returns true if the container has updates pending to install. + HasPendingUpdates() (bool, error) + + // Statistics returns statistics for a container. + Statistics() (Statistics, error) + + // ProcessList returns details for the processes in a container. + ProcessList() ([]ProcessListItem, error) + + // MappedVirtualDisks returns virtual disks mapped to a utility VM, indexed by controller + MappedVirtualDisks() (map[int]MappedVirtualDiskController, error) + + // CreateProcess launches a new process within the container. + CreateProcess(c *ProcessConfig) (Process, error) + + // OpenProcess gets an interface to an existing process within the container. + OpenProcess(pid int) (Process, error) + + // Close cleans up any state associated with the container but does not terminate or wait for it. + Close() error + + // Modify the System + Modify(config *ResourceModificationRequestResponse) error +} + +// Process represents a running or exited process. +type Process interface { + // Pid returns the process ID of the process within the container. + Pid() int + + // Kill signals the process to terminate but does not wait for it to finish terminating. + Kill() error + + // Wait waits for the process to exit. + Wait() error + + // WaitTimeout waits for the process to exit or the duration to elapse. It returns + // false if timeout occurs. + WaitTimeout(time.Duration) error + + // ExitCode returns the exit code of the process. The process must have + // already terminated. + ExitCode() (int, error) + + // ResizeConsole resizes the console of the process. + ResizeConsole(width, height uint16) error + + // Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing + // these pipes does not close the underlying pipes; it should be possible to + // call this multiple times to get multiple interfaces. + Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) + + // CloseStdin closes the write side of the stdin pipe so that the process is + // notified on the read side that there is no more data in stdin. + CloseStdin() error + + // Close cleans up any state associated with the process but does not kill + // or wait on it. + Close() error +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/cow/cow.go b/vendor/github.com/Microsoft/hcsshim/internal/cow/cow.go new file mode 100644 index 0000000000..27a62a7238 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/cow/cow.go @@ -0,0 +1,91 @@ +package cow + +import ( + "context" + "io" + + "github.com/Microsoft/hcsshim/internal/hcs/schema1" + hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2" +) + +// Process is the interface for an OS process running in a container or utility VM. +type Process interface { + // Close releases resources associated with the process and closes the + // writer and readers returned by Stdio. Depending on the implementation, + // this may also terminate the process. + Close() error + // CloseStdin causes the process's stdin handle to receive EOF/EPIPE/whatever + // is appropriate to indicate that no more data is available. + CloseStdin(ctx context.Context) error + // CloseStdout closes the stdout connection to the process. It is used to indicate + // that we are done receiving output on the shim side. + CloseStdout(ctx context.Context) error + // CloseStderr closes the stderr connection to the process. It is used to indicate + // that we are done receiving output on the shim side. + CloseStderr(ctx context.Context) error + // Pid returns the process ID. + Pid() int + // Stdio returns the stdio streams for a process. These may be nil if a stream + // was not requested during CreateProcess. + Stdio() (_ io.Writer, _ io.Reader, _ io.Reader) + // ResizeConsole resizes the virtual terminal associated with the process. + ResizeConsole(ctx context.Context, width, height uint16) error + // Kill sends a SIGKILL or equivalent signal to the process and returns whether + // the signal was delivered. It does not wait for the process to terminate. + Kill(ctx context.Context) (bool, error) + // Signal sends a signal to the process and returns whether the signal was + // delivered. The input is OS specific (either + // guestrequest.SignalProcessOptionsWCOW or + // guestrequest.SignalProcessOptionsLCOW). It does not wait for the process + // to terminate. + Signal(ctx context.Context, options interface{}) (bool, error) + // Wait waits for the process to complete, or for a connection to the process to be + // terminated by some error condition (including calling Close). + Wait() error + // ExitCode returns the exit code of the process. Returns an error if the process is + // not running. + ExitCode() (int, error) +} + +// ProcessHost is the interface for creating processes. +type ProcessHost interface { + // CreateProcess creates a process. The configuration is host specific + // (either hcsschema.ProcessParameters or lcow.ProcessParameters). + CreateProcess(ctx context.Context, config interface{}) (Process, error) + // OS returns the host's operating system, "linux" or "windows". + OS() string + // IsOCI specifies whether this is an OCI-compliant process host. If true, + // then the configuration passed to CreateProcess should have an OCI process + // spec (or nil if this is the initial process in an OCI container). + // Otherwise, it should have the HCS-specific process parameters. + IsOCI() bool +} + +// Container is the interface for container objects, either running on the host or +// in a utility VM. +type Container interface { + ProcessHost + // Close releases the resources associated with the container. Depending on + // the implementation, this may also terminate the container. + Close() error + // ID returns the container ID. + ID() string + // Properties returns the requested container properties targeting a V1 schema container. + Properties(ctx context.Context, types ...schema1.PropertyType) (*schema1.ContainerProperties, error) + // PropertiesV2 returns the requested container properties targeting a V2 schema container. + PropertiesV2(ctx context.Context, types ...hcsschema.PropertyType) (*hcsschema.Properties, error) + // Start starts a container. + Start(ctx context.Context) error + // Shutdown sends a shutdown request to the container (but does not wait for + // the shutdown to complete). + Shutdown(ctx context.Context) error + // Terminate sends a terminate request to the container (but does not wait + // for the terminate to complete). + Terminate(ctx context.Context) error + // Wait waits for the container to terminate, or for the connection to the + // container to be terminated by some error condition (including calling + // Close). + Wait() error + // Modify sends a request to modify container resources + Modify(ctx context.Context, config interface{}) error +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go new file mode 100644 index 0000000000..d13772b030 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go @@ -0,0 +1,161 @@ +package hcs + +import ( + "fmt" + "sync" + "syscall" + + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/Microsoft/hcsshim/internal/logfields" + "github.com/Microsoft/hcsshim/internal/vmcompute" + "github.com/sirupsen/logrus" +) + +var ( + nextCallback uintptr + callbackMap = map[uintptr]*notificationWatcherContext{} + callbackMapLock = sync.RWMutex{} + + notificationWatcherCallback = syscall.NewCallback(notificationWatcher) + + // Notifications for HCS_SYSTEM handles + hcsNotificationSystemExited hcsNotification = 0x00000001 + hcsNotificationSystemCreateCompleted hcsNotification = 0x00000002 + hcsNotificationSystemStartCompleted hcsNotification = 0x00000003 + hcsNotificationSystemPauseCompleted hcsNotification = 0x00000004 + hcsNotificationSystemResumeCompleted hcsNotification = 0x00000005 + hcsNotificationSystemCrashReport hcsNotification = 0x00000006 + hcsNotificationSystemSiloJobCreated hcsNotification = 0x00000007 + hcsNotificationSystemSaveCompleted hcsNotification = 0x00000008 + hcsNotificationSystemRdpEnhancedModeStateChanged hcsNotification = 0x00000009 + hcsNotificationSystemShutdownFailed hcsNotification = 0x0000000A + hcsNotificationSystemGetPropertiesCompleted hcsNotification = 0x0000000B + hcsNotificationSystemModifyCompleted hcsNotification = 0x0000000C + hcsNotificationSystemCrashInitiated hcsNotification = 0x0000000D + hcsNotificationSystemGuestConnectionClosed hcsNotification = 0x0000000E + + // Notifications for HCS_PROCESS handles + hcsNotificationProcessExited hcsNotification = 0x00010000 + + // Common notifications + hcsNotificationInvalid hcsNotification = 0x00000000 + hcsNotificationServiceDisconnect hcsNotification = 0x01000000 +) + +type hcsNotification uint32 + +func (hn hcsNotification) String() string { + switch hn { + case hcsNotificationSystemExited: + return "SystemExited" + case hcsNotificationSystemCreateCompleted: + return "SystemCreateCompleted" + case hcsNotificationSystemStartCompleted: + return "SystemStartCompleted" + case hcsNotificationSystemPauseCompleted: + return "SystemPauseCompleted" + case hcsNotificationSystemResumeCompleted: + return "SystemResumeCompleted" + case hcsNotificationSystemCrashReport: + return "SystemCrashReport" + case hcsNotificationSystemSiloJobCreated: + return "SystemSiloJobCreated" + case hcsNotificationSystemSaveCompleted: + return "SystemSaveCompleted" + case hcsNotificationSystemRdpEnhancedModeStateChanged: + return "SystemRdpEnhancedModeStateChanged" + case hcsNotificationSystemShutdownFailed: + return "SystemShutdownFailed" + case hcsNotificationSystemGetPropertiesCompleted: + return "SystemGetPropertiesCompleted" + case hcsNotificationSystemModifyCompleted: + return "SystemModifyCompleted" + case hcsNotificationSystemCrashInitiated: + return "SystemCrashInitiated" + case hcsNotificationSystemGuestConnectionClosed: + return "SystemGuestConnectionClosed" + case hcsNotificationProcessExited: + return "ProcessExited" + case hcsNotificationInvalid: + return "Invalid" + case hcsNotificationServiceDisconnect: + return "ServiceDisconnect" + default: + return fmt.Sprintf("Unknown: %d", hn) + } +} + +type notificationChannel chan error + +type notificationWatcherContext struct { + channels notificationChannels + handle vmcompute.HcsCallback + + systemID string + processID int +} + +type notificationChannels map[hcsNotification]notificationChannel + +func newSystemChannels() notificationChannels { + channels := make(notificationChannels) + for _, notif := range []hcsNotification{ + hcsNotificationServiceDisconnect, + hcsNotificationSystemExited, + hcsNotificationSystemCreateCompleted, + hcsNotificationSystemStartCompleted, + hcsNotificationSystemPauseCompleted, + hcsNotificationSystemResumeCompleted, + hcsNotificationSystemSaveCompleted, + } { + channels[notif] = make(notificationChannel, 1) + } + return channels +} + +func newProcessChannels() notificationChannels { + channels := make(notificationChannels) + for _, notif := range []hcsNotification{ + hcsNotificationServiceDisconnect, + hcsNotificationProcessExited, + } { + channels[notif] = make(notificationChannel, 1) + } + return channels +} + +func closeChannels(channels notificationChannels) { + for _, c := range channels { + close(c) + } +} + +func notificationWatcher(notificationType hcsNotification, callbackNumber uintptr, notificationStatus uintptr, notificationData *uint16) uintptr { + var result error + if int32(notificationStatus) < 0 { + result = interop.Win32FromHresult(notificationStatus) + } + + callbackMapLock.RLock() + context := callbackMap[callbackNumber] + callbackMapLock.RUnlock() + + if context == nil { + return 0 + } + + log := logrus.WithFields(logrus.Fields{ + "notification-type": notificationType.String(), + "system-id": context.systemID, + }) + if context.processID != 0 { + log.Data[logfields.ProcessID] = context.processID + } + log.Debug("HCS notification") + + if channel, ok := context.channels[notificationType]; ok { + channel <- result + } + + return 0 +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go new file mode 100644 index 0000000000..e21354ffd6 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go @@ -0,0 +1,343 @@ +package hcs + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net" + "syscall" + + "github.com/Microsoft/hcsshim/internal/log" +) + +var ( + // ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists + ErrComputeSystemDoesNotExist = syscall.Errno(0xc037010e) + + // ErrElementNotFound is an error encountered when the object being referenced does not exist + ErrElementNotFound = syscall.Errno(0x490) + + // ErrElementNotFound is an error encountered when the object being referenced does not exist + ErrNotSupported = syscall.Errno(0x32) + + // ErrInvalidData is an error encountered when the request being sent to hcs is invalid/unsupported + // decimal -2147024883 / hex 0x8007000d + ErrInvalidData = syscall.Errno(0xd) + + // ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed + ErrHandleClose = errors.New("hcsshim: the handle generating this notification has been closed") + + // ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method + ErrAlreadyClosed = errors.New("hcsshim: the handle has already been closed") + + // ErrInvalidNotificationType is an error encountered when an invalid notification type is used + ErrInvalidNotificationType = errors.New("hcsshim: invalid notification type") + + // ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation + ErrInvalidProcessState = errors.New("the process is in an invalid state for the attempted operation") + + // ErrTimeout is an error encountered when waiting on a notification times out + ErrTimeout = errors.New("hcsshim: timeout waiting for notification") + + // ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for + // a different expected notification + ErrUnexpectedContainerExit = errors.New("unexpected container exit") + + // ErrUnexpectedProcessAbort is the error encountered when communication with the compute service + // is lost while waiting for a notification + ErrUnexpectedProcessAbort = errors.New("lost communication with compute service") + + // ErrUnexpectedValue is an error encountered when hcs returns an invalid value + ErrUnexpectedValue = errors.New("unexpected value returned from hcs") + + // ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container + ErrVmcomputeAlreadyStopped = syscall.Errno(0xc0370110) + + // ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously + ErrVmcomputeOperationPending = syscall.Errno(0xC0370103) + + // ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation + ErrVmcomputeOperationInvalidState = syscall.Errno(0xc0370105) + + // ErrProcNotFound is an error encountered when a procedure look up fails. + ErrProcNotFound = syscall.Errno(0x7f) + + // ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2 + // builds when the underlying silo might be in the process of terminating. HCS was fixed in RS3. + ErrVmcomputeOperationAccessIsDenied = syscall.Errno(0x5) + + // ErrVmcomputeInvalidJSON is an error encountered when the compute system does not support/understand the messages sent by management + ErrVmcomputeInvalidJSON = syscall.Errno(0xc037010d) + + // ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message + ErrVmcomputeUnknownMessage = syscall.Errno(0xc037010b) + + // ErrVmcomputeUnexpectedExit is an error encountered when the compute system terminates unexpectedly + ErrVmcomputeUnexpectedExit = syscall.Errno(0xC0370106) + + // ErrNotSupported is an error encountered when hcs doesn't support the request + ErrPlatformNotSupported = errors.New("unsupported platform request") + + // ErrProcessAlreadyStopped is returned by hcs if the process we're trying to kill has already been stopped. + ErrProcessAlreadyStopped = syscall.Errno(0x8037011f) + + // ErrInvalidHandle is an error that can be encountrered when querying the properties of a compute system when the handle to that + // compute system has already been closed. + ErrInvalidHandle = syscall.Errno(0x6) +) + +type ErrorEvent struct { + Message string `json:"Message,omitempty"` // Fully formated error message + StackTrace string `json:"StackTrace,omitempty"` // Stack trace in string form + Provider string `json:"Provider,omitempty"` + EventID uint16 `json:"EventId,omitempty"` + Flags uint32 `json:"Flags,omitempty"` + Source string `json:"Source,omitempty"` + //Data []EventData `json:"Data,omitempty"` // Omit this as HCS doesn't encode this well. It's more confusing to include. It is however logged in debug mode (see processHcsResult function) +} + +type hcsResult struct { + Error int32 + ErrorMessage string + ErrorEvents []ErrorEvent `json:"ErrorEvents,omitempty"` +} + +func (ev *ErrorEvent) String() string { + evs := "[Event Detail: " + ev.Message + if ev.StackTrace != "" { + evs += " Stack Trace: " + ev.StackTrace + } + if ev.Provider != "" { + evs += " Provider: " + ev.Provider + } + if ev.EventID != 0 { + evs = fmt.Sprintf("%s EventID: %d", evs, ev.EventID) + } + if ev.Flags != 0 { + evs = fmt.Sprintf("%s flags: %d", evs, ev.Flags) + } + if ev.Source != "" { + evs += " Source: " + ev.Source + } + evs += "]" + return evs +} + +func processHcsResult(ctx context.Context, resultJSON string) []ErrorEvent { + if resultJSON != "" { + result := &hcsResult{} + if err := json.Unmarshal([]byte(resultJSON), result); err != nil { + log.G(ctx).WithError(err).Warning("Could not unmarshal HCS result") + return nil + } + return result.ErrorEvents + } + return nil +} + +type HcsError struct { + Op string + Err error + Events []ErrorEvent +} + +var _ net.Error = &HcsError{} + +func (e *HcsError) Error() string { + s := e.Op + ": " + e.Err.Error() + for _, ev := range e.Events { + s += "\n" + ev.String() + } + return s +} + +func (e *HcsError) Temporary() bool { + err, ok := e.Err.(net.Error) + return ok && err.Temporary() +} + +func (e *HcsError) Timeout() bool { + err, ok := e.Err.(net.Error) + return ok && err.Timeout() +} + +// ProcessError is an error encountered in HCS during an operation on a Process object +type ProcessError struct { + SystemID string + Pid int + Op string + Err error + Events []ErrorEvent +} + +var _ net.Error = &ProcessError{} + +// SystemError is an error encountered in HCS during an operation on a Container object +type SystemError struct { + ID string + Op string + Err error + Events []ErrorEvent +} + +var _ net.Error = &SystemError{} + +func (e *SystemError) Error() string { + s := e.Op + " " + e.ID + ": " + e.Err.Error() + for _, ev := range e.Events { + s += "\n" + ev.String() + } + return s +} + +func (e *SystemError) Temporary() bool { + err, ok := e.Err.(net.Error) + return ok && err.Temporary() +} + +func (e *SystemError) Timeout() bool { + err, ok := e.Err.(net.Error) + return ok && err.Timeout() +} + +func makeSystemError(system *System, op string, err error, events []ErrorEvent) error { + // Don't double wrap errors + if _, ok := err.(*SystemError); ok { + return err + } + return &SystemError{ + ID: system.ID(), + Op: op, + Err: err, + Events: events, + } +} + +func (e *ProcessError) Error() string { + s := fmt.Sprintf("%s %s:%d: %s", e.Op, e.SystemID, e.Pid, e.Err.Error()) + for _, ev := range e.Events { + s += "\n" + ev.String() + } + return s +} + +func (e *ProcessError) Temporary() bool { + err, ok := e.Err.(net.Error) + return ok && err.Temporary() +} + +func (e *ProcessError) Timeout() bool { + err, ok := e.Err.(net.Error) + return ok && err.Timeout() +} + +func makeProcessError(process *Process, op string, err error, events []ErrorEvent) error { + // Don't double wrap errors + if _, ok := err.(*ProcessError); ok { + return err + } + return &ProcessError{ + Pid: process.Pid(), + SystemID: process.SystemID(), + Op: op, + Err: err, + Events: events, + } +} + +// IsNotExist checks if an error is caused by the Container or Process not existing. +// Note: Currently, ErrElementNotFound can mean that a Process has either +// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist +// will currently return true when the error is ErrElementNotFound. +func IsNotExist(err error) bool { + err = getInnerError(err) + return err == ErrComputeSystemDoesNotExist || + err == ErrElementNotFound +} + +// IsErrorInvalidHandle checks whether the error is the result of an operation carried +// out on a handle that is invalid/closed. This error popped up while trying to query +// stats on a container in the process of being stopped. +func IsErrorInvalidHandle(err error) bool { + err = getInnerError(err) + return err == ErrInvalidHandle +} + +// IsAlreadyClosed checks if an error is caused by the Container or Process having been +// already closed by a call to the Close() method. +func IsAlreadyClosed(err error) bool { + err = getInnerError(err) + return err == ErrAlreadyClosed +} + +// IsPending returns a boolean indicating whether the error is that +// the requested operation is being completed in the background. +func IsPending(err error) bool { + err = getInnerError(err) + return err == ErrVmcomputeOperationPending +} + +// IsTimeout returns a boolean indicating whether the error is caused by +// a timeout waiting for the operation to complete. +func IsTimeout(err error) bool { + if err, ok := err.(net.Error); ok && err.Timeout() { + return true + } + err = getInnerError(err) + return err == ErrTimeout +} + +// IsAlreadyStopped returns a boolean indicating whether the error is caused by +// a Container or Process being already stopped. +// Note: Currently, ErrElementNotFound can mean that a Process has either +// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist +// will currently return true when the error is ErrElementNotFound. +func IsAlreadyStopped(err error) bool { + err = getInnerError(err) + return err == ErrVmcomputeAlreadyStopped || + err == ErrProcessAlreadyStopped || + err == ErrElementNotFound +} + +// IsNotSupported returns a boolean indicating whether the error is caused by +// unsupported platform requests +// Note: Currently Unsupported platform requests can be mean either +// ErrVmcomputeInvalidJSON, ErrInvalidData, ErrNotSupported or ErrVmcomputeUnknownMessage +// is thrown from the Platform +func IsNotSupported(err error) bool { + err = getInnerError(err) + // If Platform doesn't recognize or support the request sent, below errors are seen + return err == ErrVmcomputeInvalidJSON || + err == ErrInvalidData || + err == ErrNotSupported || + err == ErrVmcomputeUnknownMessage +} + +// IsOperationInvalidState returns true when err is caused by +// `ErrVmcomputeOperationInvalidState`. +func IsOperationInvalidState(err error) bool { + err = getInnerError(err) + return err == ErrVmcomputeOperationInvalidState +} + +// IsAccessIsDenied returns true when err is caused by +// `ErrVmcomputeOperationAccessIsDenied`. +func IsAccessIsDenied(err error) bool { + err = getInnerError(err) + return err == ErrVmcomputeOperationAccessIsDenied +} + +func getInnerError(err error) error { + switch pe := err.(type) { + case nil: + return nil + case *HcsError: + err = pe.Err + case *SystemError: + err = pe.Err + case *ProcessError: + err = pe.Err + } + return err +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go new file mode 100644 index 0000000000..f4605922ab --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go @@ -0,0 +1,557 @@ +package hcs + +import ( + "context" + "encoding/json" + "errors" + "io" + "os" + "sync" + "syscall" + "time" + + "github.com/Microsoft/hcsshim/internal/log" + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/Microsoft/hcsshim/internal/vmcompute" + "go.opencensus.io/trace" +) + +// ContainerError is an error encountered in HCS +type Process struct { + handleLock sync.RWMutex + handle vmcompute.HcsProcess + processID int + system *System + hasCachedStdio bool + stdioLock sync.Mutex + stdin io.WriteCloser + stdout io.ReadCloser + stderr io.ReadCloser + callbackNumber uintptr + killSignalDelivered bool + + closedWaitOnce sync.Once + waitBlock chan struct{} + exitCode int + waitError error +} + +func newProcess(process vmcompute.HcsProcess, processID int, computeSystem *System) *Process { + return &Process{ + handle: process, + processID: processID, + system: computeSystem, + waitBlock: make(chan struct{}), + } +} + +type processModifyRequest struct { + Operation string + ConsoleSize *consoleSize `json:",omitempty"` + CloseHandle *closeHandle `json:",omitempty"` +} + +type consoleSize struct { + Height uint16 + Width uint16 +} + +type closeHandle struct { + Handle string +} + +type processStatus struct { + ProcessID uint32 + Exited bool + ExitCode uint32 + LastWaitResult int32 +} + +const stdIn string = "StdIn" + +const ( + modifyConsoleSize string = "ConsoleSize" + modifyCloseHandle string = "CloseHandle" +) + +// Pid returns the process ID of the process within the container. +func (process *Process) Pid() int { + return process.processID +} + +// SystemID returns the ID of the process's compute system. +func (process *Process) SystemID() string { + return process.system.ID() +} + +func (process *Process) processSignalResult(ctx context.Context, err error) (bool, error) { + switch err { + case nil: + return true, nil + case ErrVmcomputeOperationInvalidState, ErrComputeSystemDoesNotExist, ErrElementNotFound: + select { + case <-process.waitBlock: + // The process exit notification has already arrived. + default: + // The process should be gone, but we have not received the notification. + // After a second, force unblock the process wait to work around a possible + // deadlock in the HCS. + go func() { + time.Sleep(time.Second) + process.closedWaitOnce.Do(func() { + log.G(ctx).WithError(err).Warn("force unblocking process waits") + process.exitCode = -1 + process.waitError = err + close(process.waitBlock) + }) + }() + } + return false, nil + default: + return false, err + } +} + +// Signal signals the process with `options`. +// +// For LCOW `guestrequest.SignalProcessOptionsLCOW`. +// +// For WCOW `guestrequest.SignalProcessOptionsWCOW`. +func (process *Process) Signal(ctx context.Context, options interface{}) (bool, error) { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + + operation := "hcs::Process::Signal" + + if process.handle == 0 { + return false, makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + optionsb, err := json.Marshal(options) + if err != nil { + return false, err + } + + resultJSON, err := vmcompute.HcsSignalProcess(ctx, process.handle, string(optionsb)) + events := processHcsResult(ctx, resultJSON) + delivered, err := process.processSignalResult(ctx, err) + if err != nil { + err = makeProcessError(process, operation, err, events) + } + return delivered, err +} + +// Kill signals the process to terminate but does not wait for it to finish terminating. +func (process *Process) Kill(ctx context.Context) (bool, error) { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + + operation := "hcs::Process::Kill" + + if process.handle == 0 { + return false, makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + if process.killSignalDelivered { + // A kill signal has already been sent to this process. Sending a second + // one offers no real benefit, as processes cannot stop themselves from + // being terminated, once a TerminateProcess has been issued. Sending a + // second kill may result in a number of errors (two of which detailed bellow) + // and which we can avoid handling. + return true, nil + } + + resultJSON, err := vmcompute.HcsTerminateProcess(ctx, process.handle) + if err != nil { + // We still need to check these two cases, as processes may still be killed by an + // external actor (human operator, OOM, random script etc). + if errors.Is(err, os.ErrPermission) || IsAlreadyStopped(err) { + // There are two cases where it should be safe to ignore an error returned + // by HcsTerminateProcess. The first one is cause by the fact that + // HcsTerminateProcess ends up calling TerminateProcess in the context + // of a container. According to the TerminateProcess documentation: + // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess#remarks + // After a process has terminated, call to TerminateProcess with open + // handles to the process fails with ERROR_ACCESS_DENIED (5) error code. + // It's safe to ignore this error here. HCS should always have permissions + // to kill processes inside any container. So an ERROR_ACCESS_DENIED + // is unlikely to be anything else than what the ending remarks in the + // documentation states. + // + // The second case is generated by hcs itself, if for any reason HcsTerminateProcess + // is called twice in a very short amount of time. In such cases, hcs may return + // HCS_E_PROCESS_ALREADY_STOPPED. + return true, nil + } + } + events := processHcsResult(ctx, resultJSON) + delivered, err := process.processSignalResult(ctx, err) + if err != nil { + err = makeProcessError(process, operation, err, events) + } + + process.killSignalDelivered = delivered + return delivered, err +} + +// waitBackground waits for the process exit notification. Once received sets +// `process.waitError` (if any) and unblocks all `Wait` calls. +// +// This MUST be called exactly once per `process.handle` but `Wait` is safe to +// call multiple times. +func (process *Process) waitBackground() { + operation := "hcs::Process::waitBackground" + ctx, span := trace.StartSpan(context.Background(), operation) + defer span.End() + span.AddAttributes( + trace.StringAttribute("cid", process.SystemID()), + trace.Int64Attribute("pid", int64(process.processID))) + + var ( + err error + exitCode = -1 + propertiesJSON string + resultJSON string + ) + + err = waitForNotification(ctx, process.callbackNumber, hcsNotificationProcessExited, nil) + if err != nil { + err = makeProcessError(process, operation, err, nil) + log.G(ctx).WithError(err).Error("failed wait") + } else { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + + // Make sure we didnt race with Close() here + if process.handle != 0 { + propertiesJSON, resultJSON, err = vmcompute.HcsGetProcessProperties(ctx, process.handle) + events := processHcsResult(ctx, resultJSON) + if err != nil { + err = makeProcessError(process, operation, err, events) //nolint:ineffassign + } else { + properties := &processStatus{} + err = json.Unmarshal([]byte(propertiesJSON), properties) + if err != nil { + err = makeProcessError(process, operation, err, nil) //nolint:ineffassign + } else { + if properties.LastWaitResult != 0 { + log.G(ctx).WithField("wait-result", properties.LastWaitResult).Warning("non-zero last wait result") + } else { + exitCode = int(properties.ExitCode) + } + } + } + } + } + log.G(ctx).WithField("exitCode", exitCode).Debug("process exited") + + process.closedWaitOnce.Do(func() { + process.exitCode = exitCode + process.waitError = err + close(process.waitBlock) + }) + oc.SetSpanStatus(span, err) +} + +// Wait waits for the process to exit. If the process has already exited returns +// the pervious error (if any). +func (process *Process) Wait() error { + <-process.waitBlock + return process.waitError +} + +// ResizeConsole resizes the console of the process. +func (process *Process) ResizeConsole(ctx context.Context, width, height uint16) error { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + + operation := "hcs::Process::ResizeConsole" + + if process.handle == 0 { + return makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + modifyRequest := processModifyRequest{ + Operation: modifyConsoleSize, + ConsoleSize: &consoleSize{ + Height: height, + Width: width, + }, + } + + modifyRequestb, err := json.Marshal(modifyRequest) + if err != nil { + return err + } + + resultJSON, err := vmcompute.HcsModifyProcess(ctx, process.handle, string(modifyRequestb)) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return makeProcessError(process, operation, err, events) + } + + return nil +} + +// ExitCode returns the exit code of the process. The process must have +// already terminated. +func (process *Process) ExitCode() (int, error) { + select { + case <-process.waitBlock: + if process.waitError != nil { + return -1, process.waitError + } + return process.exitCode, nil + default: + return -1, makeProcessError(process, "hcs::Process::ExitCode", ErrInvalidProcessState, nil) + } +} + +// StdioLegacy returns the stdin, stdout, and stderr pipes, respectively. Closing +// these pipes does not close the underlying pipes. Once returned, these pipes +// are the responsibility of the caller to close. +func (process *Process) StdioLegacy() (_ io.WriteCloser, _ io.ReadCloser, _ io.ReadCloser, err error) { + operation := "hcs::Process::StdioLegacy" + ctx, span := trace.StartSpan(context.Background(), operation) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("cid", process.SystemID()), + trace.Int64Attribute("pid", int64(process.processID))) + + process.handleLock.RLock() + defer process.handleLock.RUnlock() + + if process.handle == 0 { + return nil, nil, nil, makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + process.stdioLock.Lock() + defer process.stdioLock.Unlock() + if process.hasCachedStdio { + stdin, stdout, stderr := process.stdin, process.stdout, process.stderr + process.stdin, process.stdout, process.stderr = nil, nil, nil + process.hasCachedStdio = false + return stdin, stdout, stderr, nil + } + + processInfo, resultJSON, err := vmcompute.HcsGetProcessInfo(ctx, process.handle) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return nil, nil, nil, makeProcessError(process, operation, err, events) + } + + pipes, err := makeOpenFiles([]syscall.Handle{processInfo.StdInput, processInfo.StdOutput, processInfo.StdError}) + if err != nil { + return nil, nil, nil, makeProcessError(process, operation, err, nil) + } + + return pipes[0], pipes[1], pipes[2], nil +} + +// Stdio returns the stdin, stdout, and stderr pipes, respectively. +// To close them, close the process handle. +func (process *Process) Stdio() (stdin io.Writer, stdout, stderr io.Reader) { + process.stdioLock.Lock() + defer process.stdioLock.Unlock() + return process.stdin, process.stdout, process.stderr +} + +// CloseStdin closes the write side of the stdin pipe so that the process is +// notified on the read side that there is no more data in stdin. +func (process *Process) CloseStdin(ctx context.Context) error { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + + operation := "hcs::Process::CloseStdin" + + if process.handle == 0 { + return makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + modifyRequest := processModifyRequest{ + Operation: modifyCloseHandle, + CloseHandle: &closeHandle{ + Handle: stdIn, + }, + } + + modifyRequestb, err := json.Marshal(modifyRequest) + if err != nil { + return err + } + + resultJSON, err := vmcompute.HcsModifyProcess(ctx, process.handle, string(modifyRequestb)) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return makeProcessError(process, operation, err, events) + } + + process.stdioLock.Lock() + if process.stdin != nil { + process.stdin.Close() + process.stdin = nil + } + process.stdioLock.Unlock() + + return nil +} + +func (process *Process) CloseStdout(ctx context.Context) (err error) { + ctx, span := trace.StartSpan(ctx, "hcs::Process::CloseStdout") //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("cid", process.SystemID()), + trace.Int64Attribute("pid", int64(process.processID))) + + process.handleLock.Lock() + defer process.handleLock.Unlock() + + if process.handle == 0 { + return nil + } + + process.stdioLock.Lock() + defer process.stdioLock.Unlock() + if process.stdout != nil { + process.stdout.Close() + process.stdout = nil + } + return nil +} + +func (process *Process) CloseStderr(ctx context.Context) (err error) { + ctx, span := trace.StartSpan(ctx, "hcs::Process::CloseStderr") //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("cid", process.SystemID()), + trace.Int64Attribute("pid", int64(process.processID))) + + process.handleLock.Lock() + defer process.handleLock.Unlock() + + if process.handle == 0 { + return nil + } + + process.stdioLock.Lock() + defer process.stdioLock.Unlock() + if process.stderr != nil { + process.stderr.Close() + process.stderr = nil + + } + return nil +} + +// Close cleans up any state associated with the process but does not kill +// or wait on it. +func (process *Process) Close() (err error) { + operation := "hcs::Process::Close" + ctx, span := trace.StartSpan(context.Background(), operation) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("cid", process.SystemID()), + trace.Int64Attribute("pid", int64(process.processID))) + + process.handleLock.Lock() + defer process.handleLock.Unlock() + + // Don't double free this + if process.handle == 0 { + return nil + } + + process.stdioLock.Lock() + if process.stdin != nil { + process.stdin.Close() + process.stdin = nil + } + if process.stdout != nil { + process.stdout.Close() + process.stdout = nil + } + if process.stderr != nil { + process.stderr.Close() + process.stderr = nil + } + process.stdioLock.Unlock() + + if err = process.unregisterCallback(ctx); err != nil { + return makeProcessError(process, operation, err, nil) + } + + if err = vmcompute.HcsCloseProcess(ctx, process.handle); err != nil { + return makeProcessError(process, operation, err, nil) + } + + process.handle = 0 + process.closedWaitOnce.Do(func() { + process.exitCode = -1 + process.waitError = ErrAlreadyClosed + close(process.waitBlock) + }) + + return nil +} + +func (process *Process) registerCallback(ctx context.Context) error { + callbackContext := ¬ificationWatcherContext{ + channels: newProcessChannels(), + systemID: process.SystemID(), + processID: process.processID, + } + + callbackMapLock.Lock() + callbackNumber := nextCallback + nextCallback++ + callbackMap[callbackNumber] = callbackContext + callbackMapLock.Unlock() + + callbackHandle, err := vmcompute.HcsRegisterProcessCallback(ctx, process.handle, notificationWatcherCallback, callbackNumber) + if err != nil { + return err + } + callbackContext.handle = callbackHandle + process.callbackNumber = callbackNumber + + return nil +} + +func (process *Process) unregisterCallback(ctx context.Context) error { + callbackNumber := process.callbackNumber + + callbackMapLock.RLock() + callbackContext := callbackMap[callbackNumber] + callbackMapLock.RUnlock() + + if callbackContext == nil { + return nil + } + + handle := callbackContext.handle + + if handle == 0 { + return nil + } + + // vmcompute.HcsUnregisterProcessCallback has its own synchronization to + // wait for all callbacks to complete. We must NOT hold the callbackMapLock. + err := vmcompute.HcsUnregisterProcessCallback(ctx, handle) + if err != nil { + return err + } + + closeChannels(callbackContext.channels) + + callbackMapLock.Lock() + delete(callbackMap, callbackNumber) + callbackMapLock.Unlock() + + handle = 0 //nolint:ineffassign + + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema1/schema1.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema1/schema1.go new file mode 100644 index 0000000000..b621c55938 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema1/schema1.go @@ -0,0 +1,250 @@ +package schema1 + +import ( + "encoding/json" + "time" + + "github.com/Microsoft/go-winio/pkg/guid" + hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2" +) + +// ProcessConfig is used as both the input of Container.CreateProcess +// and to convert the parameters to JSON for passing onto the HCS +type ProcessConfig struct { + ApplicationName string `json:",omitempty"` + CommandLine string `json:",omitempty"` + CommandArgs []string `json:",omitempty"` // Used by Linux Containers on Windows + User string `json:",omitempty"` + WorkingDirectory string `json:",omitempty"` + Environment map[string]string `json:",omitempty"` + EmulateConsole bool `json:",omitempty"` + CreateStdInPipe bool `json:",omitempty"` + CreateStdOutPipe bool `json:",omitempty"` + CreateStdErrPipe bool `json:",omitempty"` + ConsoleSize [2]uint `json:",omitempty"` + CreateInUtilityVm bool `json:",omitempty"` // Used by Linux Containers on Windows + OCISpecification *json.RawMessage `json:",omitempty"` // Used by Linux Containers on Windows +} + +type Layer struct { + ID string + Path string +} + +type MappedDir struct { + HostPath string + ContainerPath string + ReadOnly bool + BandwidthMaximum uint64 + IOPSMaximum uint64 + CreateInUtilityVM bool + // LinuxMetadata - Support added in 1803/RS4+. + LinuxMetadata bool `json:",omitempty"` +} + +type MappedPipe struct { + HostPath string + ContainerPipeName string +} + +type HvRuntime struct { + ImagePath string `json:",omitempty"` + SkipTemplate bool `json:",omitempty"` + LinuxInitrdFile string `json:",omitempty"` // File under ImagePath on host containing an initrd image for starting a Linux utility VM + LinuxKernelFile string `json:",omitempty"` // File under ImagePath on host containing a kernel for starting a Linux utility VM + LinuxBootParameters string `json:",omitempty"` // Additional boot parameters for starting a Linux Utility VM in initrd mode + BootSource string `json:",omitempty"` // "Vhd" for Linux Utility VM booting from VHD + WritableBootSource bool `json:",omitempty"` // Linux Utility VM booting from VHD +} + +type MappedVirtualDisk struct { + HostPath string `json:",omitempty"` // Path to VHD on the host + ContainerPath string // Platform-specific mount point path in the container + CreateInUtilityVM bool `json:",omitempty"` + ReadOnly bool `json:",omitempty"` + Cache string `json:",omitempty"` // "" (Unspecified); "Disabled"; "Enabled"; "Private"; "PrivateAllowSharing" + AttachOnly bool `json:",omitempty"` +} + +// AssignedDevice represents a device that has been directly assigned to a container +// +// NOTE: Support added in RS5 +type AssignedDevice struct { + // InterfaceClassGUID of the device to assign to container. + InterfaceClassGUID string `json:"InterfaceClassGuid,omitempty"` +} + +// ContainerConfig is used as both the input of CreateContainer +// and to convert the parameters to JSON for passing onto the HCS +type ContainerConfig struct { + SystemType string // HCS requires this to be hard-coded to "Container" + Name string // Name of the container. We use the docker ID. + Owner string `json:",omitempty"` // The management platform that created this container + VolumePath string `json:",omitempty"` // Windows volume path for scratch space. Used by Windows Server Containers only. Format \\?\\Volume{GUID} + IgnoreFlushesDuringBoot bool `json:",omitempty"` // Optimization hint for container startup in Windows + LayerFolderPath string `json:",omitempty"` // Where the layer folders are located. Used by Windows Server Containers only. Format %root%\windowsfilter\containerID + Layers []Layer // List of storage layers. Required for Windows Server and Hyper-V Containers. Format ID=GUID;Path=%root%\windowsfilter\layerID + Credentials string `json:",omitempty"` // Credentials information + ProcessorCount uint32 `json:",omitempty"` // Number of processors to assign to the container. + ProcessorWeight uint64 `json:",omitempty"` // CPU shares (relative weight to other containers with cpu shares). Range is from 1 to 10000. A value of 0 results in default shares. + ProcessorMaximum int64 `json:",omitempty"` // Specifies the portion of processor cycles that this container can use as a percentage times 100. Range is from 1 to 10000. A value of 0 results in no limit. + StorageIOPSMaximum uint64 `json:",omitempty"` // Maximum Storage IOPS + StorageBandwidthMaximum uint64 `json:",omitempty"` // Maximum Storage Bandwidth in bytes per second + StorageSandboxSize uint64 `json:",omitempty"` // Size in bytes that the container system drive should be expanded to if smaller + MemoryMaximumInMB int64 `json:",omitempty"` // Maximum memory available to the container in Megabytes + HostName string `json:",omitempty"` // Hostname + MappedDirectories []MappedDir `json:",omitempty"` // List of mapped directories (volumes/mounts) + MappedPipes []MappedPipe `json:",omitempty"` // List of mapped Windows named pipes + HvPartition bool // True if it a Hyper-V Container + NetworkSharedContainerName string `json:",omitempty"` // Name (ID) of the container that we will share the network stack with. + EndpointList []string `json:",omitempty"` // List of networking endpoints to be attached to container + HvRuntime *HvRuntime `json:",omitempty"` // Hyper-V container settings. Used by Hyper-V containers only. Format ImagePath=%root%\BaseLayerID\UtilityVM + Servicing bool `json:",omitempty"` // True if this container is for servicing + AllowUnqualifiedDNSQuery bool `json:",omitempty"` // True to allow unqualified DNS name resolution + DNSSearchList string `json:",omitempty"` // Comma seperated list of DNS suffixes to use for name resolution + ContainerType string `json:",omitempty"` // "Linux" for Linux containers on Windows. Omitted otherwise. + TerminateOnLastHandleClosed bool `json:",omitempty"` // Should HCS terminate the container once all handles have been closed + MappedVirtualDisks []MappedVirtualDisk `json:",omitempty"` // Array of virtual disks to mount at start + AssignedDevices []AssignedDevice `json:",omitempty"` // Array of devices to assign. NOTE: Support added in RS5 +} + +type ComputeSystemQuery struct { + IDs []string `json:"Ids,omitempty"` + Types []string `json:",omitempty"` + Names []string `json:",omitempty"` + Owners []string `json:",omitempty"` +} + +type PropertyType string + +const ( + PropertyTypeStatistics PropertyType = "Statistics" // V1 and V2 + PropertyTypeProcessList PropertyType = "ProcessList" // V1 and V2 + PropertyTypeMappedVirtualDisk PropertyType = "MappedVirtualDisk" // Not supported in V2 schema call + PropertyTypeGuestConnection PropertyType = "GuestConnection" // V1 and V2. Nil return from HCS before RS5 +) + +type PropertyQuery struct { + PropertyTypes []PropertyType `json:",omitempty"` +} + +// ContainerProperties holds the properties for a container and the processes running in that container +type ContainerProperties struct { + ID string `json:"Id"` + State string + Name string + SystemType string + RuntimeOSType string `json:"RuntimeOsType,omitempty"` + Owner string + SiloGUID string `json:"SiloGuid,omitempty"` + RuntimeID guid.GUID `json:"RuntimeId,omitempty"` + IsRuntimeTemplate bool `json:",omitempty"` + RuntimeImagePath string `json:",omitempty"` + Stopped bool `json:",omitempty"` + ExitType string `json:",omitempty"` + AreUpdatesPending bool `json:",omitempty"` + ObRoot string `json:",omitempty"` + Statistics Statistics `json:",omitempty"` + ProcessList []ProcessListItem `json:",omitempty"` + MappedVirtualDiskControllers map[int]MappedVirtualDiskController `json:",omitempty"` + GuestConnectionInfo GuestConnectionInfo `json:",omitempty"` +} + +// MemoryStats holds the memory statistics for a container +type MemoryStats struct { + UsageCommitBytes uint64 `json:"MemoryUsageCommitBytes,omitempty"` + UsageCommitPeakBytes uint64 `json:"MemoryUsageCommitPeakBytes,omitempty"` + UsagePrivateWorkingSetBytes uint64 `json:"MemoryUsagePrivateWorkingSetBytes,omitempty"` +} + +// ProcessorStats holds the processor statistics for a container +type ProcessorStats struct { + TotalRuntime100ns uint64 `json:",omitempty"` + RuntimeUser100ns uint64 `json:",omitempty"` + RuntimeKernel100ns uint64 `json:",omitempty"` +} + +// StorageStats holds the storage statistics for a container +type StorageStats struct { + ReadCountNormalized uint64 `json:",omitempty"` + ReadSizeBytes uint64 `json:",omitempty"` + WriteCountNormalized uint64 `json:",omitempty"` + WriteSizeBytes uint64 `json:",omitempty"` +} + +// NetworkStats holds the network statistics for a container +type NetworkStats struct { + BytesReceived uint64 `json:",omitempty"` + BytesSent uint64 `json:",omitempty"` + PacketsReceived uint64 `json:",omitempty"` + PacketsSent uint64 `json:",omitempty"` + DroppedPacketsIncoming uint64 `json:",omitempty"` + DroppedPacketsOutgoing uint64 `json:",omitempty"` + EndpointId string `json:",omitempty"` + InstanceId string `json:",omitempty"` +} + +// Statistics is the structure returned by a statistics call on a container +type Statistics struct { + Timestamp time.Time `json:",omitempty"` + ContainerStartTime time.Time `json:",omitempty"` + Uptime100ns uint64 `json:",omitempty"` + Memory MemoryStats `json:",omitempty"` + Processor ProcessorStats `json:",omitempty"` + Storage StorageStats `json:",omitempty"` + Network []NetworkStats `json:",omitempty"` +} + +// ProcessList is the structure of an item returned by a ProcessList call on a container +type ProcessListItem struct { + CreateTimestamp time.Time `json:",omitempty"` + ImageName string `json:",omitempty"` + KernelTime100ns uint64 `json:",omitempty"` + MemoryCommitBytes uint64 `json:",omitempty"` + MemoryWorkingSetPrivateBytes uint64 `json:",omitempty"` + MemoryWorkingSetSharedBytes uint64 `json:",omitempty"` + ProcessId uint32 `json:",omitempty"` + UserTime100ns uint64 `json:",omitempty"` +} + +// MappedVirtualDiskController is the structure of an item returned by a MappedVirtualDiskList call on a container +type MappedVirtualDiskController struct { + MappedVirtualDisks map[int]MappedVirtualDisk `json:",omitempty"` +} + +// GuestDefinedCapabilities is part of the GuestConnectionInfo returned by a GuestConnection call on a utility VM +type GuestDefinedCapabilities struct { + NamespaceAddRequestSupported bool `json:",omitempty"` + SignalProcessSupported bool `json:",omitempty"` + DumpStacksSupported bool `json:",omitempty"` + DeleteContainerStateSupported bool `json:",omitempty"` + UpdateContainerSupported bool `json:",omitempty"` +} + +// GuestConnectionInfo is the structure of an iterm return by a GuestConnection call on a utility VM +type GuestConnectionInfo struct { + SupportedSchemaVersions []hcsschema.Version `json:",omitempty"` + ProtocolVersion uint32 `json:",omitempty"` + GuestDefinedCapabilities GuestDefinedCapabilities `json:",omitempty"` +} + +// Type of Request Support in ModifySystem +type RequestType string + +// Type of Resource Support in ModifySystem +type ResourceType string + +// RequestType const +const ( + Add RequestType = "Add" + Remove RequestType = "Remove" + Network ResourceType = "Network" +) + +// ResourceModificationRequestResponse is the structure used to send request to the container to modify the system +// Supported resource types are Network and Request Types are Add/Remove +type ResourceModificationRequestResponse struct { + Resource ResourceType `json:"ResourceType"` + Data interface{} `json:"Settings"` + Request RequestType `json:"RequestType,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/attachment.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/attachment.go new file mode 100644 index 0000000000..70884aad75 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/attachment.go @@ -0,0 +1,36 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Attachment struct { + Type_ string `json:"Type,omitempty"` + + Path string `json:"Path,omitempty"` + + IgnoreFlushes bool `json:"IgnoreFlushes,omitempty"` + + CachingMode string `json:"CachingMode,omitempty"` + + NoWriteHardening bool `json:"NoWriteHardening,omitempty"` + + DisableExpansionOptimization bool `json:"DisableExpansionOptimization,omitempty"` + + IgnoreRelativeLocator bool `json:"IgnoreRelativeLocator,omitempty"` + + CaptureIoAttributionContext bool `json:"CaptureIoAttributionContext,omitempty"` + + ReadOnly bool `json:"ReadOnly,omitempty"` + + SupportCompressedVolumes bool `json:"SupportCompressedVolumes,omitempty"` + + AlwaysAllowSparseFiles bool `json:"AlwaysAllowSparseFiles,omitempty"` + + ExtensibleVirtualDiskType string `json:"ExtensibleVirtualDiskType,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/battery.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/battery.go new file mode 100644 index 0000000000..ecbbed4c23 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/battery.go @@ -0,0 +1,13 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Battery struct { +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cache_query_stats_response.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cache_query_stats_response.go new file mode 100644 index 0000000000..c1ea3953b5 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cache_query_stats_response.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type CacheQueryStatsResponse struct { + L3OccupancyBytes int32 `json:"L3OccupancyBytes,omitempty"` + + L3TotalBwBytes int32 `json:"L3TotalBwBytes,omitempty"` + + L3LocalBwBytes int32 `json:"L3LocalBwBytes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/chipset.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/chipset.go new file mode 100644 index 0000000000..ca75277a3f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/chipset.go @@ -0,0 +1,27 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Chipset struct { + Uefi *Uefi `json:"Uefi,omitempty"` + + IsNumLockDisabled bool `json:"IsNumLockDisabled,omitempty"` + + BaseBoardSerialNumber string `json:"BaseBoardSerialNumber,omitempty"` + + ChassisSerialNumber string `json:"ChassisSerialNumber,omitempty"` + + ChassisAssetTag string `json:"ChassisAssetTag,omitempty"` + + UseUtc bool `json:"UseUtc,omitempty"` + + // LinuxKernelDirect - Added in v2.2 Builds >=181117 + LinuxKernelDirect *LinuxKernelDirect `json:"LinuxKernelDirect,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/close_handle.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/close_handle.go new file mode 100644 index 0000000000..b4f9c315b0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/close_handle.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type CloseHandle struct { + Handle string `json:"Handle,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/com_port.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/com_port.go new file mode 100644 index 0000000000..8bf8cab60e --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/com_port.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// ComPort specifies the named pipe that will be used for the port, with empty string indicating a disconnected port. +type ComPort struct { + NamedPipe string `json:"NamedPipe,omitempty"` + + OptimizeForDebugger bool `json:"OptimizeForDebugger,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/compute_system.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/compute_system.go new file mode 100644 index 0000000000..10cea67e04 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/compute_system.go @@ -0,0 +1,26 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ComputeSystem struct { + Owner string `json:"Owner,omitempty"` + + SchemaVersion *Version `json:"SchemaVersion,omitempty"` + + HostingSystemId string `json:"HostingSystemId,omitempty"` + + HostedSystem interface{} `json:"HostedSystem,omitempty"` + + Container *Container `json:"Container,omitempty"` + + VirtualMachine *VirtualMachine `json:"VirtualMachine,omitempty"` + + ShouldTerminateOnLastHandleClosed bool `json:"ShouldTerminateOnLastHandleClosed,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/configuration.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/configuration.go new file mode 100644 index 0000000000..1d5dfe68ad --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/configuration.go @@ -0,0 +1,72 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +import ( + "net/http" +) + +// contextKeys are used to identify the type of value in the context. +// Since these are string, it is possible to get a short description of the +// context key for logging and debugging using key.String(). + +type contextKey string + +func (c contextKey) String() string { + return "auth " + string(c) +} + +var ( + // ContextOAuth2 takes a oauth2.TokenSource as authentication for the request. + ContextOAuth2 = contextKey("token") + + // ContextBasicAuth takes BasicAuth as authentication for the request. + ContextBasicAuth = contextKey("basic") + + // ContextAccessToken takes a string oauth2 access token as authentication for the request. + ContextAccessToken = contextKey("accesstoken") + + // ContextAPIKey takes an APIKey as authentication for the request + ContextAPIKey = contextKey("apikey") +) + +// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth +type BasicAuth struct { + UserName string `json:"userName,omitempty"` + Password string `json:"password,omitempty"` +} + +// APIKey provides API key based authentication to a request passed via context using ContextAPIKey +type APIKey struct { + Key string + Prefix string +} + +type Configuration struct { + BasePath string `json:"basePath,omitempty"` + Host string `json:"host,omitempty"` + Scheme string `json:"scheme,omitempty"` + DefaultHeader map[string]string `json:"defaultHeader,omitempty"` + UserAgent string `json:"userAgent,omitempty"` + HTTPClient *http.Client +} + +func NewConfiguration() *Configuration { + cfg := &Configuration{ + BasePath: "https://localhost", + DefaultHeader: make(map[string]string), + UserAgent: "Swagger-Codegen/2.1.0/go", + } + return cfg +} + +func (c *Configuration) AddDefaultHeader(key string, value string) { + c.DefaultHeader[key] = value +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/console_size.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/console_size.go new file mode 100644 index 0000000000..68aa04a573 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/console_size.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ConsoleSize struct { + Height int32 `json:"Height,omitempty"` + + Width int32 `json:"Width,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container.go new file mode 100644 index 0000000000..39a54432c0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container.go @@ -0,0 +1,36 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Container struct { + GuestOs *GuestOs `json:"GuestOs,omitempty"` + + Storage *Storage `json:"Storage,omitempty"` + + MappedDirectories []MappedDirectory `json:"MappedDirectories,omitempty"` + + MappedPipes []MappedPipe `json:"MappedPipes,omitempty"` + + Memory *Memory `json:"Memory,omitempty"` + + Processor *Processor `json:"Processor,omitempty"` + + Networking *Networking `json:"Networking,omitempty"` + + HvSocket *HvSocket `json:"HvSocket,omitempty"` + + ContainerCredentialGuard *ContainerCredentialGuardState `json:"ContainerCredentialGuard,omitempty"` + + RegistryChanges *RegistryChanges `json:"RegistryChanges,omitempty"` + + AssignedDevices []Device `json:"AssignedDevices,omitempty"` + + AdditionalDeviceNamespace *ContainerDefinitionDevice `json:"AdditionalDeviceNamespace,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_add_instance_request.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_add_instance_request.go new file mode 100644 index 0000000000..495c6ebc8f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_add_instance_request.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardAddInstanceRequest struct { + Id string `json:"Id,omitempty"` + CredentialSpec string `json:"CredentialSpec,omitempty"` + Transport string `json:"Transport,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_hv_socket_service_config.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_hv_socket_service_config.go new file mode 100644 index 0000000000..1ed4c008f2 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_hv_socket_service_config.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardHvSocketServiceConfig struct { + ServiceId string `json:"ServiceId,omitempty"` + ServiceConfig *HvSocketServiceConfig `json:"ServiceConfig,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_instance.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_instance.go new file mode 100644 index 0000000000..d7ebd0fcca --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_instance.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardInstance struct { + Id string `json:"Id,omitempty"` + CredentialGuard *ContainerCredentialGuardState `json:"CredentialGuard,omitempty"` + HvSocketConfig *ContainerCredentialGuardHvSocketServiceConfig `json:"HvSocketConfig,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_modify_operation.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_modify_operation.go new file mode 100644 index 0000000000..71005b090b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_modify_operation.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardModifyOperation string + +const ( + AddInstance ContainerCredentialGuardModifyOperation = "AddInstance" + RemoveInstance ContainerCredentialGuardModifyOperation = "RemoveInstance" +) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_operation_request.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_operation_request.go new file mode 100644 index 0000000000..952cda4965 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_operation_request.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardOperationRequest struct { + Operation ContainerCredentialGuardModifyOperation `json:"Operation,omitempty"` + OperationDetails interface{} `json:"OperationDetails,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_remove_instance_request.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_remove_instance_request.go new file mode 100644 index 0000000000..32e5a3beed --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_remove_instance_request.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardRemoveInstanceRequest struct { + Id string `json:"Id,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_state.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_state.go new file mode 100644 index 0000000000..0f8f644379 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_state.go @@ -0,0 +1,25 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardState struct { + + // Authentication cookie for calls to a Container Credential Guard instance. + Cookie string `json:"Cookie,omitempty"` + + // Name of the RPC endpoint of the Container Credential Guard instance. + RpcEndpoint string `json:"RpcEndpoint,omitempty"` + + // Transport used for the configured Container Credential Guard instance. + Transport string `json:"Transport,omitempty"` + + // Credential spec used for the configured Container Credential Guard instance. + CredentialSpec string `json:"CredentialSpec,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_system_info.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_system_info.go new file mode 100644 index 0000000000..ea306fa21a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_system_info.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardSystemInfo struct { + Instances []ContainerCredentialGuardInstance `json:"Instances,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_memory_information.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_memory_information.go new file mode 100644 index 0000000000..1fd7ca5d56 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_memory_information.go @@ -0,0 +1,25 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// memory usage as viewed from within the container +type ContainerMemoryInformation struct { + TotalPhysicalBytes int32 `json:"TotalPhysicalBytes,omitempty"` + + TotalUsage int32 `json:"TotalUsage,omitempty"` + + CommittedBytes int32 `json:"CommittedBytes,omitempty"` + + SharedCommittedBytes int32 `json:"SharedCommittedBytes,omitempty"` + + CommitLimitBytes int32 `json:"CommitLimitBytes,omitempty"` + + PeakCommitmentBytes int32 `json:"PeakCommitmentBytes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group.go new file mode 100644 index 0000000000..90332a5190 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// CPU groups allow Hyper-V administrators to better manage and allocate the host's CPU resources across guest virtual machines +type CpuGroup struct { + Id string `json:"Id,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_affinity.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_affinity.go new file mode 100644 index 0000000000..8794961bf5 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_affinity.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type CpuGroupAffinity struct { + LogicalProcessorCount int32 `json:"LogicalProcessorCount,omitempty"` + LogicalProcessors []int32 `json:"LogicalProcessors,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_config.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_config.go new file mode 100644 index 0000000000..0be0475d41 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_config.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type CpuGroupConfig struct { + GroupId string `json:"GroupId,omitempty"` + Affinity *CpuGroupAffinity `json:"Affinity,omitempty"` + GroupProperties []CpuGroupProperty `json:"GroupProperties,omitempty"` + // Hypervisor CPU group IDs exposed to clients + HypervisorGroupId uint64 `json:"HypervisorGroupId,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_configurations.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_configurations.go new file mode 100644 index 0000000000..3ace0ccc3b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_configurations.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Structure used to return cpu groups for a Service property query +type CpuGroupConfigurations struct { + CpuGroups []CpuGroupConfig `json:"CpuGroups,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_operations.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_operations.go new file mode 100644 index 0000000000..7d89780701 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_operations.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type CPUGroupOperation string + +const ( + CreateGroup CPUGroupOperation = "CreateGroup" + DeleteGroup CPUGroupOperation = "DeleteGroup" + SetProperty CPUGroupOperation = "SetProperty" +) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_property.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_property.go new file mode 100644 index 0000000000..bbad6a2c45 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_property.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type CpuGroupProperty struct { + PropertyCode uint32 `json:"PropertyCode,omitempty"` + PropertyValue uint32 `json:"PropertyValue,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/create_group_operation.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/create_group_operation.go new file mode 100644 index 0000000000..91a8278fe3 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/create_group_operation.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Create group operation settings +type CreateGroupOperation struct { + GroupId string `json:"GroupId,omitempty"` + LogicalProcessorCount uint32 `json:"LogicalProcessorCount,omitempty"` + LogicalProcessors []uint32 `json:"LogicalProcessors,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/delete_group_operation.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/delete_group_operation.go new file mode 100644 index 0000000000..134bd98817 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/delete_group_operation.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Delete group operation settings +type DeleteGroupOperation struct { + GroupId string `json:"GroupId,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/device.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/device.go new file mode 100644 index 0000000000..31c4538aff --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/device.go @@ -0,0 +1,27 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type DeviceType string + +const ( + ClassGUID DeviceType = "ClassGuid" + DeviceInstanceID DeviceType = "DeviceInstance" + GPUMirror DeviceType = "GpuMirror" +) + +type Device struct { + // The type of device to assign to the container. + Type DeviceType `json:"Type,omitempty"` + // The interface class guid of the device interfaces to assign to the container. Only used when Type is ClassGuid. + InterfaceClassGuid string `json:"InterfaceClassGuid,omitempty"` + // The location path of the device to assign to the container. Only used when Type is DeviceInstanceID. + LocationPath string `json:"LocationPath,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/devices.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/devices.go new file mode 100644 index 0000000000..e985d96d22 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/devices.go @@ -0,0 +1,46 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Devices struct { + ComPorts map[string]ComPort `json:"ComPorts,omitempty"` + + Scsi map[string]Scsi `json:"Scsi,omitempty"` + + VirtualPMem *VirtualPMemController `json:"VirtualPMem,omitempty"` + + NetworkAdapters map[string]NetworkAdapter `json:"NetworkAdapters,omitempty"` + + VideoMonitor *VideoMonitor `json:"VideoMonitor,omitempty"` + + Keyboard *Keyboard `json:"Keyboard,omitempty"` + + Mouse *Mouse `json:"Mouse,omitempty"` + + HvSocket *HvSocket2 `json:"HvSocket,omitempty"` + + EnhancedModeVideo *EnhancedModeVideo `json:"EnhancedModeVideo,omitempty"` + + GuestCrashReporting *GuestCrashReporting `json:"GuestCrashReporting,omitempty"` + + VirtualSmb *VirtualSmb `json:"VirtualSmb,omitempty"` + + Plan9 *Plan9 `json:"Plan9,omitempty"` + + Battery *Battery `json:"Battery,omitempty"` + + FlexibleIov map[string]FlexibleIoDevice `json:"FlexibleIov,omitempty"` + + SharedMemory *SharedMemoryConfiguration `json:"SharedMemory,omitempty"` + + // TODO: This is pre-release support in schema 2.3. Need to add build number + // docs when a public build with this is out. + VirtualPci map[string]VirtualPciDevice `json:",omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/enhanced_mode_video.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/enhanced_mode_video.go new file mode 100644 index 0000000000..85450c41e1 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/enhanced_mode_video.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type EnhancedModeVideo struct { + ConnectionOptions *RdpConnectionOptions `json:"ConnectionOptions,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/flexible_io_device.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/flexible_io_device.go new file mode 100644 index 0000000000..fe86cab655 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/flexible_io_device.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type FlexibleIoDevice struct { + EmulatorId string `json:"EmulatorId,omitempty"` + + HostingModel string `json:"HostingModel,omitempty"` + + Configuration []string `json:"Configuration,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_connection.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_connection.go new file mode 100644 index 0000000000..7db29495b3 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_connection.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type GuestConnection struct { + + // Use Vsock rather than Hyper-V sockets to communicate with the guest service. + UseVsock bool `json:"UseVsock,omitempty"` + + // Don't disconnect the guest connection when pausing the virtual machine. + UseConnectedSuspend bool `json:"UseConnectedSuspend,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_connection_info.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_connection_info.go new file mode 100644 index 0000000000..8a369bab71 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_connection_info.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Information about the guest. +type GuestConnectionInfo struct { + + // Each schema version x.y stands for the range of versions a.b where a==x and b<=y. This list comes from the SupportedSchemaVersions field in GcsCapabilities. + SupportedSchemaVersions []Version `json:"SupportedSchemaVersions,omitempty"` + + ProtocolVersion int32 `json:"ProtocolVersion,omitempty"` + + GuestDefinedCapabilities *interface{} `json:"GuestDefinedCapabilities,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_crash_reporting.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_crash_reporting.go new file mode 100644 index 0000000000..af82800483 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_crash_reporting.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type GuestCrashReporting struct { + WindowsCrashSettings *WindowsCrashReporting `json:"WindowsCrashSettings,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_os.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_os.go new file mode 100644 index 0000000000..8838519a39 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_os.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type GuestOs struct { + HostName string `json:"HostName,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_state.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_state.go new file mode 100644 index 0000000000..ef1eec8865 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_state.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type GuestState struct { + + // The path to an existing file uses for persistent guest state storage. An empty string indicates the system should initialize new transient, in-memory guest state. + GuestStateFilePath string `json:"GuestStateFilePath,omitempty"` + + // The path to an existing file for persistent runtime state storage. An empty string indicates the system should initialize new transient, in-memory runtime state. + RuntimeStateFilePath string `json:"RuntimeStateFilePath,omitempty"` + + // If true, the guest state and runtime state files will be used as templates to populate transient, in-memory state instead of using the files as persistent backing store. + ForceTransientState bool `json:"ForceTransientState,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/host_processor_modify_request.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/host_processor_modify_request.go new file mode 100644 index 0000000000..2238ce5306 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/host_processor_modify_request.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Structure used to request a service processor modification +type HostProcessorModificationRequest struct { + Operation CPUGroupOperation `json:"Operation,omitempty"` + OperationDetails interface{} `json:"OperationDetails,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hosted_system.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hosted_system.go new file mode 100644 index 0000000000..ea3084bca7 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hosted_system.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type HostedSystem struct { + SchemaVersion *Version `json:"SchemaVersion,omitempty"` + + Container *Container `json:"Container,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket.go new file mode 100644 index 0000000000..23b2ee9e7d --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type HvSocket struct { + Config *HvSocketSystemConfig `json:"Config,omitempty"` + + EnablePowerShellDirect bool `json:"EnablePowerShellDirect,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_2.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_2.go new file mode 100644 index 0000000000..a017691f02 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_2.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// HvSocket configuration for a VM +type HvSocket2 struct { + HvSocketConfig *HvSocketSystemConfig `json:"HvSocketConfig,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_address.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_address.go new file mode 100644 index 0000000000..84c11b93ee --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_address.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// This class defines address settings applied to a VM +// by the GCS every time a VM starts or restores. +type HvSocketAddress struct { + LocalAddress string `json:"LocalAddress,omitempty"` + ParentAddress string `json:"ParentAddress,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_service_config.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_service_config.go new file mode 100644 index 0000000000..ecd9f7fbac --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_service_config.go @@ -0,0 +1,28 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type HvSocketServiceConfig struct { + + // SDDL string that HvSocket will check before allowing a host process to bind to this specific service. If not specified, defaults to the system DefaultBindSecurityDescriptor, defined in HvSocketSystemWpConfig in V1. + BindSecurityDescriptor string `json:"BindSecurityDescriptor,omitempty"` + + // SDDL string that HvSocket will check before allowing a host process to connect to this specific service. If not specified, defaults to the system DefaultConnectSecurityDescriptor, defined in HvSocketSystemWpConfig in V1. + ConnectSecurityDescriptor string `json:"ConnectSecurityDescriptor,omitempty"` + + // If true, HvSocket will process wildcard binds for this service/system combination. Wildcard binds are secured in the registry at SOFTWARE/Microsoft/Windows NT/CurrentVersion/Virtualization/HvSocket/WildcardDescriptors + AllowWildcardBinds bool `json:"AllowWildcardBinds,omitempty"` + + // Disabled controls whether the HvSocket service is accepting connection requests. + // This set to true will make the service refuse all incoming connections as well as cancel + // any connections already established. The service itself will still be active however + // and can be re-enabled at a future time. + Disabled bool `json:"Disabled,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_system_config.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_system_config.go new file mode 100644 index 0000000000..69f4f9d39b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_system_config.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// This is the HCS Schema version of the HvSocket configuration. The VMWP version is located in Config.Devices.IC in V1. +type HvSocketSystemConfig struct { + + // SDDL string that HvSocket will check before allowing a host process to bind to an unlisted service for this specific container/VM (not wildcard binds). + DefaultBindSecurityDescriptor string `json:"DefaultBindSecurityDescriptor,omitempty"` + + // SDDL string that HvSocket will check before allowing a host process to connect to an unlisted service in the VM/container. + DefaultConnectSecurityDescriptor string `json:"DefaultConnectSecurityDescriptor,omitempty"` + + ServiceTable map[string]HvSocketServiceConfig `json:"ServiceTable,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/interrupt_moderation_mode.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/interrupt_moderation_mode.go new file mode 100644 index 0000000000..a614d63bd7 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/interrupt_moderation_mode.go @@ -0,0 +1,42 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type InterruptModerationName string + +// The valid interrupt moderation modes for I/O virtualization (IOV) offloading. +const ( + DefaultName InterruptModerationName = "Default" + AdaptiveName InterruptModerationName = "Adaptive" + OffName InterruptModerationName = "Off" + LowName InterruptModerationName = "Low" + MediumName InterruptModerationName = "Medium" + HighName InterruptModerationName = "High" +) + +type InterruptModerationValue uint32 + +const ( + DefaultValue InterruptModerationValue = iota + AdaptiveValue + OffValue + LowValue InterruptModerationValue = 100 + MediumValue InterruptModerationValue = 200 + HighValue InterruptModerationValue = 300 +) + +var InterruptModerationValueToName = map[InterruptModerationValue]InterruptModerationName{ + DefaultValue: DefaultName, + AdaptiveValue: AdaptiveName, + OffValue: OffName, + LowValue: LowName, + MediumValue: MediumName, + HighValue: HighName, +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/iov_settings.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/iov_settings.go new file mode 100644 index 0000000000..2a55cc37cd --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/iov_settings.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type IovSettings struct { + // The weight assigned to this port for I/O virtualization (IOV) offloading. + // Setting this to 0 disables IOV offloading. + OffloadWeight *uint32 `json:"OffloadWeight,omitempty"` + + // The number of queue pairs requested for this port for I/O virtualization (IOV) offloading. + QueuePairsRequested *uint32 `json:"QueuePairsRequested,omitempty"` + + // The interrupt moderation mode for I/O virtualization (IOV) offloading. + InterruptModeration *InterruptModerationName `json:"InterruptModeration,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/keyboard.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/keyboard.go new file mode 100644 index 0000000000..3d3fa3b1c7 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/keyboard.go @@ -0,0 +1,13 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Keyboard struct { +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/layer.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/layer.go new file mode 100644 index 0000000000..176c49d495 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/layer.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Layer struct { + Id string `json:"Id,omitempty"` + + Path string `json:"Path,omitempty"` + + PathType string `json:"PathType,omitempty"` + + // Unspecified defaults to Enabled + Cache string `json:"Cache,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/linux_kernel_direct.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/linux_kernel_direct.go new file mode 100644 index 0000000000..0ab6c280fc --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/linux_kernel_direct.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.2 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type LinuxKernelDirect struct { + KernelFilePath string `json:"KernelFilePath,omitempty"` + + InitRdPath string `json:"InitRdPath,omitempty"` + + KernelCmdLine string `json:"KernelCmdLine,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/logical_processor.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/logical_processor.go new file mode 100644 index 0000000000..2e3aa5e175 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/logical_processor.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type LogicalProcessor struct { + LpIndex uint32 `json:"LpIndex,omitempty"` + NodeNumber uint8 `json:"NodeNumber,omitempty"` + PackageId uint32 `json:"PackageId,omitempty"` + CoreId uint32 `json:"CoreId,omitempty"` + RootVpIndex int32 `json:"RootVpIndex,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mapped_directory.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mapped_directory.go new file mode 100644 index 0000000000..9b86a40457 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mapped_directory.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type MappedDirectory struct { + HostPath string `json:"HostPath,omitempty"` + + HostPathType string `json:"HostPathType,omitempty"` + + ContainerPath string `json:"ContainerPath,omitempty"` + + ReadOnly bool `json:"ReadOnly,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mapped_pipe.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mapped_pipe.go new file mode 100644 index 0000000000..208074e9a2 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mapped_pipe.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type MappedPipe struct { + ContainerPipeName string `json:"ContainerPipeName,omitempty"` + + HostPath string `json:"HostPath,omitempty"` + + HostPathType string `json:"HostPathType,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory.go new file mode 100644 index 0000000000..30749c6724 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Memory struct { + SizeInMB uint64 `json:"SizeInMB,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_2.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_2.go new file mode 100644 index 0000000000..71224c75b9 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_2.go @@ -0,0 +1,49 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Memory2 struct { + SizeInMB uint64 `json:"SizeInMB,omitempty"` + + AllowOvercommit bool `json:"AllowOvercommit,omitempty"` + + EnableHotHint bool `json:"EnableHotHint,omitempty"` + + EnableColdHint bool `json:"EnableColdHint,omitempty"` + + EnableEpf bool `json:"EnableEpf,omitempty"` + + // EnableDeferredCommit is private in the schema. If regenerated need to add back. + EnableDeferredCommit bool `json:"EnableDeferredCommit,omitempty"` + + // EnableColdDiscardHint if enabled, then the memory cold discard hint feature is exposed + // to the VM, allowing it to trim non-zeroed pages from the working set (if supported by + // the guest operating system). + EnableColdDiscardHint bool `json:"EnableColdDiscardHint,omitempty"` + + // LowMmioGapInMB is the low MMIO region allocated below 4GB. + // + // TODO: This is pre-release support in schema 2.3. Need to add build number + // docs when a public build with this is out. + LowMMIOGapInMB uint64 `json:"LowMmioGapInMB,omitempty"` + + // HighMmioBaseInMB is the high MMIO region allocated above 4GB (base and + // size). + // + // TODO: This is pre-release support in schema 2.3. Need to add build number + // docs when a public build with this is out. + HighMMIOBaseInMB uint64 `json:"HighMmioBaseInMB,omitempty"` + + // HighMmioGapInMB is the high MMIO region. + // + // TODO: This is pre-release support in schema 2.3. Need to add build number + // docs when a public build with this is out. + HighMMIOGapInMB uint64 `json:"HighMmioGapInMB,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_information_for_vm.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_information_for_vm.go new file mode 100644 index 0000000000..811779b04b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_information_for_vm.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type MemoryInformationForVm struct { + VirtualNodeCount uint32 `json:"VirtualNodeCount,omitempty"` + + VirtualMachineMemory *VmMemory `json:"VirtualMachineMemory,omitempty"` + + VirtualNodes []VirtualNodeInfo `json:"VirtualNodes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_stats.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_stats.go new file mode 100644 index 0000000000..906ba597f9 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_stats.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Memory runtime statistics +type MemoryStats struct { + MemoryUsageCommitBytes uint64 `json:"MemoryUsageCommitBytes,omitempty"` + + MemoryUsageCommitPeakBytes uint64 `json:"MemoryUsageCommitPeakBytes,omitempty"` + + MemoryUsagePrivateWorkingSetBytes uint64 `json:"MemoryUsagePrivateWorkingSetBytes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_container_definition_device.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_container_definition_device.go new file mode 100644 index 0000000000..8dbe40b3be --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_container_definition_device.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerDefinitionDevice struct { + DeviceExtension []DeviceExtension `json:"device_extension,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_category.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_category.go new file mode 100644 index 0000000000..8fe89f9274 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_category.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type DeviceCategory struct { + Name string `json:"name,omitempty"` + InterfaceClass []InterfaceClass `json:"interface_class,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_extension.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_extension.go new file mode 100644 index 0000000000..a62568d892 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_extension.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type DeviceExtension struct { + DeviceCategory *DeviceCategory `json:"device_category,omitempty"` + Namespace *DeviceExtensionNamespace `json:"namespace,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_instance.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_instance.go new file mode 100644 index 0000000000..a7410febd6 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_instance.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type DeviceInstance struct { + Id string `json:"id,omitempty"` + LocationPath string `json:"location_path,omitempty"` + PortName string `json:"port_name,omitempty"` + InterfaceClass []InterfaceClass `json:"interface_class,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_namespace.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_namespace.go new file mode 100644 index 0000000000..3553640647 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_namespace.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type DeviceNamespace struct { + RequiresDriverstore bool `json:"requires_driverstore,omitempty"` + DeviceCategory []DeviceCategory `json:"device_category,omitempty"` + DeviceInstance []DeviceInstance `json:"device_instance,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_interface_class.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_interface_class.go new file mode 100644 index 0000000000..7be98b5410 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_interface_class.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type InterfaceClass struct { + Type_ string `json:"type,omitempty"` + Identifier string `json:"identifier,omitempty"` + Recurse bool `json:"recurse,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_namespace.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_namespace.go new file mode 100644 index 0000000000..3ab9cf1ecf --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_namespace.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type DeviceExtensionNamespace struct { + Ob *ObjectNamespace `json:"ob,omitempty"` + Device *DeviceNamespace `json:"device,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_directory.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_directory.go new file mode 100644 index 0000000000..d2f51b3b53 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_directory.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ObjectDirectory struct { + Name string `json:"name,omitempty"` + Clonesd string `json:"clonesd,omitempty"` + Shadow string `json:"shadow,omitempty"` + Symlink []ObjectSymlink `json:"symlink,omitempty"` + Objdir []ObjectDirectory `json:"objdir,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_namespace.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_namespace.go new file mode 100644 index 0000000000..47dfb55bfa --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_namespace.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ObjectNamespace struct { + Shadow string `json:"shadow,omitempty"` + Symlink []ObjectSymlink `json:"symlink,omitempty"` + Objdir []ObjectDirectory `json:"objdir,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_symlink.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_symlink.go new file mode 100644 index 0000000000..8867ebe5f0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_symlink.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ObjectSymlink struct { + Name string `json:"name,omitempty"` + Path string `json:"path,omitempty"` + Scope string `json:"scope,omitempty"` + Pathtoclone string `json:"pathtoclone,omitempty"` + AccessMask int32 `json:"access_mask,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/modification_request.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/modification_request.go new file mode 100644 index 0000000000..1384ed8882 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/modification_request.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ModificationRequest struct { + PropertyType PropertyType `json:"PropertyType,omitempty"` + Settings interface{} `json:"Settings,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/modify_setting_request.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/modify_setting_request.go new file mode 100644 index 0000000000..d29455a3e4 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/modify_setting_request.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ModifySettingRequest struct { + ResourcePath string `json:"ResourcePath,omitempty"` + + RequestType string `json:"RequestType,omitempty"` + + Settings interface{} `json:"Settings,omitempty"` // NOTE: Swagger generated as *interface{}. Locally updated + + GuestRequest interface{} `json:"GuestRequest,omitempty"` // NOTE: Swagger generated as *interface{}. Locally updated +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mouse.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mouse.go new file mode 100644 index 0000000000..ccf8b938f3 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mouse.go @@ -0,0 +1,13 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Mouse struct { +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/network_adapter.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/network_adapter.go new file mode 100644 index 0000000000..7408abd317 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/network_adapter.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type NetworkAdapter struct { + EndpointId string `json:"EndpointId,omitempty"` + MacAddress string `json:"MacAddress,omitempty"` + // The I/O virtualization (IOV) offloading configuration. + IovSettings *IovSettings `json:"IovSettings,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/networking.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/networking.go new file mode 100644 index 0000000000..e5ea187a29 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/networking.go @@ -0,0 +1,23 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Networking struct { + AllowUnqualifiedDnsQuery bool `json:"AllowUnqualifiedDnsQuery,omitempty"` + + DnsSearchList string `json:"DnsSearchList,omitempty"` + + NetworkSharedContainerName string `json:"NetworkSharedContainerName,omitempty"` + + // Guid in windows; string in linux + Namespace string `json:"Namespace,omitempty"` + + NetworkAdapters []string `json:"NetworkAdapters,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/pause_notification.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/pause_notification.go new file mode 100644 index 0000000000..d96c9501f3 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/pause_notification.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Notification data that is indicated to components running in the Virtual Machine. +type PauseNotification struct { + Reason string `json:"Reason,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/pause_options.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/pause_options.go new file mode 100644 index 0000000000..21707a88eb --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/pause_options.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Options for HcsPauseComputeSystem +type PauseOptions struct { + SuspensionLevel string `json:"SuspensionLevel,omitempty"` + + HostedNotification *PauseNotification `json:"HostedNotification,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/plan9.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/plan9.go new file mode 100644 index 0000000000..29d8c8012f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/plan9.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Plan9 struct { + Shares []Plan9Share `json:"Shares,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/plan9_share.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/plan9_share.go new file mode 100644 index 0000000000..41f8fdea02 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/plan9_share.go @@ -0,0 +1,34 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Plan9Share struct { + Name string `json:"Name,omitempty"` + + // The name by which the guest operation system can access this share, via the aname parameter in the Plan9 protocol. + AccessName string `json:"AccessName,omitempty"` + + Path string `json:"Path,omitempty"` + + Port int32 `json:"Port,omitempty"` + + // Flags are marked private. Until they are exported correctly + // + // ReadOnly 0x00000001 + // LinuxMetadata 0x00000004 + // CaseSensitive 0x00000008 + Flags int32 `json:"Flags,omitempty"` + + ReadOnly bool `json:"ReadOnly,omitempty"` + + UseShareRootIdentity bool `json:"UseShareRootIdentity,omitempty"` + + AllowedFiles []string `json:"AllowedFiles,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_details.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_details.go new file mode 100644 index 0000000000..e9a662dd59 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_details.go @@ -0,0 +1,33 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +import ( + "time" +) + +// Information about a process running in a container +type ProcessDetails struct { + ProcessId int32 `json:"ProcessId,omitempty"` + + ImageName string `json:"ImageName,omitempty"` + + CreateTimestamp time.Time `json:"CreateTimestamp,omitempty"` + + UserTime100ns int32 `json:"UserTime100ns,omitempty"` + + KernelTime100ns int32 `json:"KernelTime100ns,omitempty"` + + MemoryCommitBytes int32 `json:"MemoryCommitBytes,omitempty"` + + MemoryWorkingSetPrivateBytes int32 `json:"MemoryWorkingSetPrivateBytes,omitempty"` + + MemoryWorkingSetSharedBytes int32 `json:"MemoryWorkingSetSharedBytes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_modify_request.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_modify_request.go new file mode 100644 index 0000000000..e4ed095c7b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_modify_request.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Passed to HcsRpc_ModifyProcess +type ProcessModifyRequest struct { + Operation string `json:"Operation,omitempty"` + + ConsoleSize *ConsoleSize `json:"ConsoleSize,omitempty"` + + CloseHandle *CloseHandle `json:"CloseHandle,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_parameters.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_parameters.go new file mode 100644 index 0000000000..82b0d0532b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_parameters.go @@ -0,0 +1,46 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ProcessParameters struct { + ApplicationName string `json:"ApplicationName,omitempty"` + + CommandLine string `json:"CommandLine,omitempty"` + + // optional alternative to CommandLine, currently only supported by Linux GCS + CommandArgs []string `json:"CommandArgs,omitempty"` + + User string `json:"User,omitempty"` + + WorkingDirectory string `json:"WorkingDirectory,omitempty"` + + Environment map[string]string `json:"Environment,omitempty"` + + // if set, will run as low-privilege process + RestrictedToken bool `json:"RestrictedToken,omitempty"` + + // if set, ignore StdErrPipe + EmulateConsole bool `json:"EmulateConsole,omitempty"` + + CreateStdInPipe bool `json:"CreateStdInPipe,omitempty"` + + CreateStdOutPipe bool `json:"CreateStdOutPipe,omitempty"` + + CreateStdErrPipe bool `json:"CreateStdErrPipe,omitempty"` + + // height then width + ConsoleSize []int32 `json:"ConsoleSize,omitempty"` + + // if set, find an existing session for the user and create the process in it + UseExistingLogin bool `json:"UseExistingLogin,omitempty"` + + // if set, use the legacy console instead of conhost + UseLegacyConsole bool `json:"UseLegacyConsole,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_status.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_status.go new file mode 100644 index 0000000000..ad9a4fa9ad --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_status.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Status of a process running in a container +type ProcessStatus struct { + ProcessId int32 `json:"ProcessId,omitempty"` + + Exited bool `json:"Exited,omitempty"` + + ExitCode int32 `json:"ExitCode,omitempty"` + + LastWaitResult int32 `json:"LastWaitResult,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor.go new file mode 100644 index 0000000000..bb24e88da1 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Processor struct { + Count int32 `json:"Count,omitempty"` + + Maximum int32 `json:"Maximum,omitempty"` + + Weight int32 `json:"Weight,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_2.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_2.go new file mode 100644 index 0000000000..c64f335ec7 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_2.go @@ -0,0 +1,23 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.5 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Processor2 struct { + Count int32 `json:"Count,omitempty"` + + Limit int32 `json:"Limit,omitempty"` + + Weight int32 `json:"Weight,omitempty"` + + ExposeVirtualizationExtensions bool `json:"ExposeVirtualizationExtensions,omitempty"` + + // An optional object that configures the CPU Group to which a Virtual Machine is going to bind to. + CpuGroup *CpuGroup `json:"CpuGroup,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_stats.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_stats.go new file mode 100644 index 0000000000..6157e25225 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_stats.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// CPU runtime statistics +type ProcessorStats struct { + TotalRuntime100ns uint64 `json:"TotalRuntime100ns,omitempty"` + + RuntimeUser100ns uint64 `json:"RuntimeUser100ns,omitempty"` + + RuntimeKernel100ns uint64 `json:"RuntimeKernel100ns,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_topology.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_topology.go new file mode 100644 index 0000000000..885156e77f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_topology.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ProcessorTopology struct { + LogicalProcessorCount uint32 `json:"LogicalProcessorCount,omitempty"` + LogicalProcessors []LogicalProcessor `json:"LogicalProcessors,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/properties.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/properties.go new file mode 100644 index 0000000000..17558cba0f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/properties.go @@ -0,0 +1,54 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +import ( + v1 "github.com/containerd/cgroups/stats/v1" +) + +type Properties struct { + Id string `json:"Id,omitempty"` + + SystemType string `json:"SystemType,omitempty"` + + RuntimeOsType string `json:"RuntimeOsType,omitempty"` + + Name string `json:"Name,omitempty"` + + Owner string `json:"Owner,omitempty"` + + RuntimeId string `json:"RuntimeId,omitempty"` + + RuntimeTemplateId string `json:"RuntimeTemplateId,omitempty"` + + State string `json:"State,omitempty"` + + Stopped bool `json:"Stopped,omitempty"` + + ExitType string `json:"ExitType,omitempty"` + + Memory *MemoryInformationForVm `json:"Memory,omitempty"` + + Statistics *Statistics `json:"Statistics,omitempty"` + + ProcessList []ProcessDetails `json:"ProcessList,omitempty"` + + TerminateOnLastHandleClosed bool `json:"TerminateOnLastHandleClosed,omitempty"` + + HostingSystemId string `json:"HostingSystemId,omitempty"` + + SharedMemoryRegionInfo []SharedMemoryRegionInfo `json:"SharedMemoryRegionInfo,omitempty"` + + GuestConnectionInfo *GuestConnectionInfo `json:"GuestConnectionInfo,omitempty"` + + // Metrics is not part of the API for HCS but this is used for LCOW v2 to + // return the full cgroup metrics from the guest. + Metrics *v1.Metrics `json:"LCOWMetrics,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/property_query.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/property_query.go new file mode 100644 index 0000000000..d6d80df131 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/property_query.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// By default the basic properties will be returned. This query provides a way to request specific properties. +type PropertyQuery struct { + PropertyTypes []PropertyType `json:"PropertyTypes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/property_type.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/property_type.go new file mode 100644 index 0000000000..98f2c96edb --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/property_type.go @@ -0,0 +1,26 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type PropertyType string + +const ( + PTMemory PropertyType = "Memory" + PTGuestMemory PropertyType = "GuestMemory" + PTStatistics PropertyType = "Statistics" + PTProcessList PropertyType = "ProcessList" + PTTerminateOnLastHandleClosed PropertyType = "TerminateOnLastHandleClosed" + PTSharedMemoryRegion PropertyType = "SharedMemoryRegion" + PTContainerCredentialGuard PropertyType = "ContainerCredentialGuard" // This field is not generated by swagger. This was added manually. + PTGuestConnection PropertyType = "GuestConnection" + PTICHeartbeatStatus PropertyType = "ICHeartbeatStatus" + PTProcessorTopology PropertyType = "ProcessorTopology" + PTCPUGroup PropertyType = "CpuGroup" +) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/rdp_connection_options.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/rdp_connection_options.go new file mode 100644 index 0000000000..8d5f5c1719 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/rdp_connection_options.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RdpConnectionOptions struct { + AccessSids []string `json:"AccessSids,omitempty"` + + NamedPipe string `json:"NamedPipe,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_changes.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_changes.go new file mode 100644 index 0000000000..006906f6e2 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_changes.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RegistryChanges struct { + AddValues []RegistryValue `json:"AddValues,omitempty"` + + DeleteKeys []RegistryKey `json:"DeleteKeys,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_key.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_key.go new file mode 100644 index 0000000000..26fde99c74 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_key.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RegistryKey struct { + Hive string `json:"Hive,omitempty"` + + Name string `json:"Name,omitempty"` + + Volatile bool `json:"Volatile,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_value.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_value.go new file mode 100644 index 0000000000..3f203176c3 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_value.go @@ -0,0 +1,30 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RegistryValue struct { + Key *RegistryKey `json:"Key,omitempty"` + + Name string `json:"Name,omitempty"` + + Type_ string `json:"Type,omitempty"` + + // One and only one value type must be set. + StringValue string `json:"StringValue,omitempty"` + + BinaryValue string `json:"BinaryValue,omitempty"` + + DWordValue int32 `json:"DWordValue,omitempty"` + + QWordValue int32 `json:"QWordValue,omitempty"` + + // Only used if RegistryValueType is CustomType The data is in BinaryValue + CustomType int32 `json:"CustomType,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/restore_state.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/restore_state.go new file mode 100644 index 0000000000..778ff58735 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/restore_state.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RestoreState struct { + + // The path to the save state file to restore the system from. + SaveStateFilePath string `json:"SaveStateFilePath,omitempty"` + + // The ID of the template system to clone this new system off of. An empty string indicates the system should not be cloned from a template. + TemplateSystemId string `json:"TemplateSystemId,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/save_options.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/save_options.go new file mode 100644 index 0000000000..e55fa1d98a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/save_options.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type SaveOptions struct { + + // The type of save operation to be performed. + SaveType string `json:"SaveType,omitempty"` + + // The path to the file that will container the saved state. + SaveStateFilePath string `json:"SaveStateFilePath,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/scsi.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/scsi.go new file mode 100644 index 0000000000..bf253a470b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/scsi.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Scsi struct { + + // Map of attachments, where the key is the integer LUN number on the controller. + Attachments map[string]Attachment `json:"Attachments,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/service_properties.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/service_properties.go new file mode 100644 index 0000000000..b8142ca6a6 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/service_properties.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +import "encoding/json" + +type ServiceProperties struct { + // Changed Properties field to []json.RawMessage from []interface{} to avoid having to + // remarshal sp.Properties[n] and unmarshal into the type(s) we want. + Properties []json.RawMessage `json:"Properties,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_configuration.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_configuration.go new file mode 100644 index 0000000000..df9baa9219 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_configuration.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type SharedMemoryConfiguration struct { + Regions []SharedMemoryRegion `json:"Regions,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_region.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_region.go new file mode 100644 index 0000000000..825b71865d --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_region.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type SharedMemoryRegion struct { + SectionName string `json:"SectionName,omitempty"` + + StartOffset int32 `json:"StartOffset,omitempty"` + + Length int32 `json:"Length,omitempty"` + + AllowGuestWrite bool `json:"AllowGuestWrite,omitempty"` + + HiddenFromGuest bool `json:"HiddenFromGuest,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_region_info.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_region_info.go new file mode 100644 index 0000000000..f67b08eb57 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_region_info.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type SharedMemoryRegionInfo struct { + SectionName string `json:"SectionName,omitempty"` + + GuestPhysicalAddress int32 `json:"GuestPhysicalAddress,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/silo_properties.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/silo_properties.go new file mode 100644 index 0000000000..5eaf6a7f4a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/silo_properties.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Silo job information +type SiloProperties struct { + Enabled bool `json:"Enabled,omitempty"` + + JobName string `json:"JobName,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/statistics.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/statistics.go new file mode 100644 index 0000000000..ba7a6b3963 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/statistics.go @@ -0,0 +1,29 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +import ( + "time" +) + +// Runtime statistics for a container +type Statistics struct { + Timestamp time.Time `json:"Timestamp,omitempty"` + + ContainerStartTime time.Time `json:"ContainerStartTime,omitempty"` + + Uptime100ns uint64 `json:"Uptime100ns,omitempty"` + + Processor *ProcessorStats `json:"Processor,omitempty"` + + Memory *MemoryStats `json:"Memory,omitempty"` + + Storage *StorageStats `json:"Storage,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage.go new file mode 100644 index 0000000000..2627af9132 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Storage struct { + + // List of layers that describe the parent hierarchy for a container's storage. These layers combined together, presented as a disposable and/or committable working storage, are used by the container to record all changes done to the parent layers. + Layers []Layer `json:"Layers,omitempty"` + + // Path that points to the scratch space of a container, where parent layers are combined together to present a new disposable and/or committable layer with the changes done during its runtime. + Path string `json:"Path,omitempty"` + + QoS *StorageQoS `json:"QoS,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage_qo_s.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage_qo_s.go new file mode 100644 index 0000000000..9c5e6eb532 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage_qo_s.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type StorageQoS struct { + IopsMaximum int32 `json:"IopsMaximum,omitempty"` + + BandwidthMaximum int32 `json:"BandwidthMaximum,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage_stats.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage_stats.go new file mode 100644 index 0000000000..4f042ffd93 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage_stats.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Storage runtime statistics +type StorageStats struct { + ReadCountNormalized uint64 `json:"ReadCountNormalized,omitempty"` + + ReadSizeBytes uint64 `json:"ReadSizeBytes,omitempty"` + + WriteCountNormalized uint64 `json:"WriteCountNormalized,omitempty"` + + WriteSizeBytes uint64 `json:"WriteSizeBytes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/topology.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/topology.go new file mode 100644 index 0000000000..8348699403 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/topology.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Topology struct { + Memory *Memory2 `json:"Memory,omitempty"` + + Processor *Processor2 `json:"Processor,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/uefi.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/uefi.go new file mode 100644 index 0000000000..0e48ece500 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/uefi.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Uefi struct { + EnableDebugger bool `json:"EnableDebugger,omitempty"` + + SecureBootTemplateId string `json:"SecureBootTemplateId,omitempty"` + + BootThis *UefiBootEntry `json:"BootThis,omitempty"` + + Console string `json:"Console,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/uefi_boot_entry.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/uefi_boot_entry.go new file mode 100644 index 0000000000..3ab409d825 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/uefi_boot_entry.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type UefiBootEntry struct { + DeviceType string `json:"DeviceType,omitempty"` + + DevicePath string `json:"DevicePath,omitempty"` + + DiskNumber int32 `json:"DiskNumber,omitempty"` + + OptionalData string `json:"OptionalData,omitempty"` + + VmbFsRootPath string `json:"VmbFsRootPath,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/version.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/version.go new file mode 100644 index 0000000000..2abfccca31 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/version.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Version struct { + Major int32 `json:"Major,omitempty"` + + Minor int32 `json:"Minor,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/video_monitor.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/video_monitor.go new file mode 100644 index 0000000000..ec5d0fb936 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/video_monitor.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VideoMonitor struct { + HorizontalResolution int32 `json:"HorizontalResolution,omitempty"` + + VerticalResolution int32 `json:"VerticalResolution,omitempty"` + + ConnectionOptions *RdpConnectionOptions `json:"ConnectionOptions,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_machine.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_machine.go new file mode 100644 index 0000000000..2d22b1bcb0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_machine.go @@ -0,0 +1,32 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualMachine struct { + + // StopOnReset is private in the schema. If regenerated need to put back. + StopOnReset bool `json:"StopOnReset,omitempty"` + + Chipset *Chipset `json:"Chipset,omitempty"` + + ComputeTopology *Topology `json:"ComputeTopology,omitempty"` + + Devices *Devices `json:"Devices,omitempty"` + + GuestState *GuestState `json:"GuestState,omitempty"` + + RestoreState *RestoreState `json:"RestoreState,omitempty"` + + RegistryChanges *RegistryChanges `json:"RegistryChanges,omitempty"` + + StorageQoS *StorageQoS `json:"StorageQoS,omitempty"` + + GuestConnection *GuestConnection `json:"GuestConnection,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_node_info.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_node_info.go new file mode 100644 index 0000000000..91a3c83d4f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_node_info.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualNodeInfo struct { + VirtualNodeIndex int32 `json:"VirtualNodeIndex,omitempty"` + + PhysicalNodeNumber int32 `json:"PhysicalNodeNumber,omitempty"` + + VirtualProcessorCount int32 `json:"VirtualProcessorCount,omitempty"` + + MemoryUsageInPages int32 `json:"MemoryUsageInPages,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_controller.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_controller.go new file mode 100644 index 0000000000..f5b7f3e38c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_controller.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualPMemController struct { + Devices map[string]VirtualPMemDevice `json:"Devices,omitempty"` + + MaximumCount uint32 `json:"MaximumCount,omitempty"` + + MaximumSizeBytes uint64 `json:"MaximumSizeBytes,omitempty"` + + Backing string `json:"Backing,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_device.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_device.go new file mode 100644 index 0000000000..70cf2d90de --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_device.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualPMemDevice struct { + HostPath string `json:"HostPath,omitempty"` + + ReadOnly bool `json:"ReadOnly,omitempty"` + + ImageFormat string `json:"ImageFormat,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_mapping.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_mapping.go new file mode 100644 index 0000000000..9ef322f615 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_mapping.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualPMemMapping struct { + HostPath string `json:"HostPath,omitempty"` + ImageFormat string `json:"ImageFormat,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_pci_device.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_pci_device.go new file mode 100644 index 0000000000..f5e05903c5 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_pci_device.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.3 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// TODO: This is pre-release support in schema 2.3. Need to add build number +// docs when a public build with this is out. +type VirtualPciDevice struct { + Functions []VirtualPciFunction `json:",omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_pci_function.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_pci_function.go new file mode 100644 index 0000000000..cedb7d18bc --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_pci_function.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.3 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// TODO: This is pre-release support in schema 2.3. Need to add build number +// docs when a public build with this is out. +type VirtualPciFunction struct { + DeviceInstancePath string `json:",omitempty"` + + VirtualFunction uint16 `json:",omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb.go new file mode 100644 index 0000000000..362df363e1 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualSmb struct { + Shares []VirtualSmbShare `json:"Shares,omitempty"` + + DirectFileMappingInMB int64 `json:"DirectFileMappingInMB,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb_share.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb_share.go new file mode 100644 index 0000000000..915e9b6386 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb_share.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualSmbShare struct { + Name string `json:"Name,omitempty"` + + Path string `json:"Path,omitempty"` + + AllowedFiles []string `json:"AllowedFiles,omitempty"` + + Options *VirtualSmbShareOptions `json:"Options,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb_share_options.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb_share_options.go new file mode 100644 index 0000000000..75196bd8c8 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb_share_options.go @@ -0,0 +1,62 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualSmbShareOptions struct { + ReadOnly bool `json:"ReadOnly,omitempty"` + + // convert exclusive access to shared read access + ShareRead bool `json:"ShareRead,omitempty"` + + // all opens will use cached I/O + CacheIo bool `json:"CacheIo,omitempty"` + + // disable oplock support + NoOplocks bool `json:"NoOplocks,omitempty"` + + // Acquire the backup privilege when attempting to open + TakeBackupPrivilege bool `json:"TakeBackupPrivilege,omitempty"` + + // Use the identity of the share root when opening + UseShareRootIdentity bool `json:"UseShareRootIdentity,omitempty"` + + // disable Direct Mapping + NoDirectmap bool `json:"NoDirectmap,omitempty"` + + // disable Byterange locks + NoLocks bool `json:"NoLocks,omitempty"` + + // disable Directory CHange Notifications + NoDirnotify bool `json:"NoDirnotify,omitempty"` + + // share is use for VM shared memory + VmSharedMemory bool `json:"VmSharedMemory,omitempty"` + + // allow access only to the files specified in AllowedFiles + RestrictFileAccess bool `json:"RestrictFileAccess,omitempty"` + + // disable all oplocks except Level II + ForceLevelIIOplocks bool `json:"ForceLevelIIOplocks,omitempty"` + + // Allow the host to reparse this base layer + ReparseBaseLayer bool `json:"ReparseBaseLayer,omitempty"` + + // Enable pseudo-oplocks + PseudoOplocks bool `json:"PseudoOplocks,omitempty"` + + // All opens will use non-cached IO + NonCacheIo bool `json:"NonCacheIo,omitempty"` + + // Enable pseudo directory change notifications + PseudoDirnotify bool `json:"PseudoDirnotify,omitempty"` + + // Block directory enumeration, renames, and deletes. + SingleFileMapping bool `json:"SingleFileMapping,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/vm_memory.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/vm_memory.go new file mode 100644 index 0000000000..8e1836dd6b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/vm_memory.go @@ -0,0 +1,26 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VmMemory struct { + AvailableMemory int32 `json:"AvailableMemory,omitempty"` + + AvailableMemoryBuffer int32 `json:"AvailableMemoryBuffer,omitempty"` + + ReservedMemory uint64 `json:"ReservedMemory,omitempty"` + + AssignedMemory uint64 `json:"AssignedMemory,omitempty"` + + SlpActive bool `json:"SlpActive,omitempty"` + + BalancingEnabled bool `json:"BalancingEnabled,omitempty"` + + DmOperationInProgress bool `json:"DmOperationInProgress,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/vm_processor_limits.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/vm_processor_limits.go new file mode 100644 index 0000000000..de1b9cf1ae --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/vm_processor_limits.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// ProcessorLimits is used when modifying processor scheduling limits of a virtual machine. +type ProcessorLimits struct { + // Maximum amount of host CPU resources that the virtual machine can use. + Limit uint64 `json:"Limit,omitempty"` + // Value describing the relative priority of this virtual machine compared to other virtual machines. + Weight uint64 `json:"Weight,omitempty"` + // Minimum amount of host CPU resources that the virtual machine is guaranteed. + Reservation uint64 `json:"Reservation,omitempty"` + // Provides the target maximum CPU frequency, in MHz, for a virtual machine. + MaximumFrequencyMHz uint32 `json:"MaximumFrequencyMHz,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/windows_crash_reporting.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/windows_crash_reporting.go new file mode 100644 index 0000000000..8ed7e566d6 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/windows_crash_reporting.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type WindowsCrashReporting struct { + DumpFileName string `json:"DumpFileName,omitempty"` + + MaxDumpSize int64 `json:"MaxDumpSize,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/service.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/service.go new file mode 100644 index 0000000000..a634dfc151 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/service.go @@ -0,0 +1,49 @@ +package hcs + +import ( + "context" + "encoding/json" + + hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2" + "github.com/Microsoft/hcsshim/internal/vmcompute" +) + +// GetServiceProperties returns properties of the host compute service. +func GetServiceProperties(ctx context.Context, q hcsschema.PropertyQuery) (*hcsschema.ServiceProperties, error) { + operation := "hcs::GetServiceProperties" + + queryb, err := json.Marshal(q) + if err != nil { + return nil, err + } + propertiesJSON, resultJSON, err := vmcompute.HcsGetServiceProperties(ctx, string(queryb)) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return nil, &HcsError{Op: operation, Err: err, Events: events} + } + + if propertiesJSON == "" { + return nil, ErrUnexpectedValue + } + properties := &hcsschema.ServiceProperties{} + if err := json.Unmarshal([]byte(propertiesJSON), properties); err != nil { + return nil, err + } + return properties, nil +} + +// ModifyServiceSettings modifies settings of the host compute service. +func ModifyServiceSettings(ctx context.Context, settings hcsschema.ModificationRequest) error { + operation := "hcs::ModifyServiceSettings" + + settingsJSON, err := json.Marshal(settings) + if err != nil { + return err + } + resultJSON, err := vmcompute.HcsModifyServiceSettings(ctx, string(settingsJSON)) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return &HcsError{Op: operation, Err: err, Events: events} + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go new file mode 100644 index 0000000000..75499c967f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go @@ -0,0 +1,637 @@ +package hcs + +import ( + "context" + "encoding/json" + "errors" + "strings" + "sync" + "syscall" + + "github.com/Microsoft/hcsshim/internal/cow" + "github.com/Microsoft/hcsshim/internal/hcs/schema1" + hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2" + "github.com/Microsoft/hcsshim/internal/log" + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/Microsoft/hcsshim/internal/timeout" + "github.com/Microsoft/hcsshim/internal/vmcompute" + "go.opencensus.io/trace" +) + +type System struct { + handleLock sync.RWMutex + handle vmcompute.HcsSystem + id string + callbackNumber uintptr + + closedWaitOnce sync.Once + waitBlock chan struct{} + waitError error + exitError error + os, typ string +} + +func newSystem(id string) *System { + return &System{ + id: id, + waitBlock: make(chan struct{}), + } +} + +// CreateComputeSystem creates a new compute system with the given configuration but does not start it. +func CreateComputeSystem(ctx context.Context, id string, hcsDocumentInterface interface{}) (_ *System, err error) { + operation := "hcs::CreateComputeSystem" + + // hcsCreateComputeSystemContext is an async operation. Start the outer span + // here to measure the full create time. + ctx, span := trace.StartSpan(ctx, operation) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("cid", id)) + + computeSystem := newSystem(id) + + hcsDocumentB, err := json.Marshal(hcsDocumentInterface) + if err != nil { + return nil, err + } + + hcsDocument := string(hcsDocumentB) + + var ( + identity syscall.Handle + resultJSON string + createError error + ) + computeSystem.handle, resultJSON, createError = vmcompute.HcsCreateComputeSystem(ctx, id, hcsDocument, identity) + if createError == nil || IsPending(createError) { + defer func() { + if err != nil { + computeSystem.Close() + } + }() + if err = computeSystem.registerCallback(ctx); err != nil { + // Terminate the compute system if it still exists. We're okay to + // ignore a failure here. + _ = computeSystem.Terminate(ctx) + return nil, makeSystemError(computeSystem, operation, err, nil) + } + } + + events, err := processAsyncHcsResult(ctx, createError, resultJSON, computeSystem.callbackNumber, hcsNotificationSystemCreateCompleted, &timeout.SystemCreate) + if err != nil { + if err == ErrTimeout { + // Terminate the compute system if it still exists. We're okay to + // ignore a failure here. + _ = computeSystem.Terminate(ctx) + } + return nil, makeSystemError(computeSystem, operation, err, events) + } + go computeSystem.waitBackground() + if err = computeSystem.getCachedProperties(ctx); err != nil { + return nil, err + } + return computeSystem, nil +} + +// OpenComputeSystem opens an existing compute system by ID. +func OpenComputeSystem(ctx context.Context, id string) (*System, error) { + operation := "hcs::OpenComputeSystem" + + computeSystem := newSystem(id) + handle, resultJSON, err := vmcompute.HcsOpenComputeSystem(ctx, id) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return nil, makeSystemError(computeSystem, operation, err, events) + } + computeSystem.handle = handle + defer func() { + if err != nil { + computeSystem.Close() + } + }() + if err = computeSystem.registerCallback(ctx); err != nil { + return nil, makeSystemError(computeSystem, operation, err, nil) + } + go computeSystem.waitBackground() + if err = computeSystem.getCachedProperties(ctx); err != nil { + return nil, err + } + return computeSystem, nil +} + +func (computeSystem *System) getCachedProperties(ctx context.Context) error { + props, err := computeSystem.Properties(ctx) + if err != nil { + return err + } + computeSystem.typ = strings.ToLower(props.SystemType) + computeSystem.os = strings.ToLower(props.RuntimeOSType) + if computeSystem.os == "" && computeSystem.typ == "container" { + // Pre-RS5 HCS did not return the OS, but it only supported containers + // that ran Windows. + computeSystem.os = "windows" + } + return nil +} + +// OS returns the operating system of the compute system, "linux" or "windows". +func (computeSystem *System) OS() string { + return computeSystem.os +} + +// IsOCI returns whether processes in the compute system should be created via +// OCI. +func (computeSystem *System) IsOCI() bool { + return computeSystem.os == "linux" && computeSystem.typ == "container" +} + +// GetComputeSystems gets a list of the compute systems on the system that match the query +func GetComputeSystems(ctx context.Context, q schema1.ComputeSystemQuery) ([]schema1.ContainerProperties, error) { + operation := "hcs::GetComputeSystems" + + queryb, err := json.Marshal(q) + if err != nil { + return nil, err + } + + computeSystemsJSON, resultJSON, err := vmcompute.HcsEnumerateComputeSystems(ctx, string(queryb)) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return nil, &HcsError{Op: operation, Err: err, Events: events} + } + + if computeSystemsJSON == "" { + return nil, ErrUnexpectedValue + } + computeSystems := []schema1.ContainerProperties{} + if err = json.Unmarshal([]byte(computeSystemsJSON), &computeSystems); err != nil { + return nil, err + } + + return computeSystems, nil +} + +// Start synchronously starts the computeSystem. +func (computeSystem *System) Start(ctx context.Context) (err error) { + operation := "hcs::System::Start" + + // hcsStartComputeSystemContext is an async operation. Start the outer span + // here to measure the full start time. + ctx, span := trace.StartSpan(ctx, operation) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) + + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) + } + + resultJSON, err := vmcompute.HcsStartComputeSystem(ctx, computeSystem.handle, "") + events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber, hcsNotificationSystemStartCompleted, &timeout.SystemStart) + if err != nil { + return makeSystemError(computeSystem, operation, err, events) + } + + return nil +} + +// ID returns the compute system's identifier. +func (computeSystem *System) ID() string { + return computeSystem.id +} + +// Shutdown requests a compute system shutdown. +func (computeSystem *System) Shutdown(ctx context.Context) error { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + operation := "hcs::System::Shutdown" + + if computeSystem.handle == 0 { + return nil + } + + resultJSON, err := vmcompute.HcsShutdownComputeSystem(ctx, computeSystem.handle, "") + events := processHcsResult(ctx, resultJSON) + switch err { + case nil, ErrVmcomputeAlreadyStopped, ErrComputeSystemDoesNotExist, ErrVmcomputeOperationPending: + default: + return makeSystemError(computeSystem, operation, err, events) + } + return nil +} + +// Terminate requests a compute system terminate. +func (computeSystem *System) Terminate(ctx context.Context) error { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + operation := "hcs::System::Terminate" + + if computeSystem.handle == 0 { + return nil + } + + resultJSON, err := vmcompute.HcsTerminateComputeSystem(ctx, computeSystem.handle, "") + events := processHcsResult(ctx, resultJSON) + switch err { + case nil, ErrVmcomputeAlreadyStopped, ErrComputeSystemDoesNotExist, ErrVmcomputeOperationPending: + default: + return makeSystemError(computeSystem, operation, err, events) + } + return nil +} + +// waitBackground waits for the compute system exit notification. Once received +// sets `computeSystem.waitError` (if any) and unblocks all `Wait` calls. +// +// This MUST be called exactly once per `computeSystem.handle` but `Wait` is +// safe to call multiple times. +func (computeSystem *System) waitBackground() { + operation := "hcs::System::waitBackground" + ctx, span := trace.StartSpan(context.Background(), operation) + defer span.End() + span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) + + err := waitForNotification(ctx, computeSystem.callbackNumber, hcsNotificationSystemExited, nil) + switch err { + case nil: + log.G(ctx).Debug("system exited") + case ErrVmcomputeUnexpectedExit: + log.G(ctx).Debug("unexpected system exit") + computeSystem.exitError = makeSystemError(computeSystem, operation, err, nil) + err = nil + default: + err = makeSystemError(computeSystem, operation, err, nil) + } + computeSystem.closedWaitOnce.Do(func() { + computeSystem.waitError = err + close(computeSystem.waitBlock) + }) + oc.SetSpanStatus(span, err) +} + +// Wait synchronously waits for the compute system to shutdown or terminate. If +// the compute system has already exited returns the previous error (if any). +func (computeSystem *System) Wait() error { + <-computeSystem.waitBlock + return computeSystem.waitError +} + +// ExitError returns an error describing the reason the compute system terminated. +func (computeSystem *System) ExitError() error { + select { + case <-computeSystem.waitBlock: + if computeSystem.waitError != nil { + return computeSystem.waitError + } + return computeSystem.exitError + default: + return errors.New("container not exited") + } +} + +// Properties returns the requested container properties targeting a V1 schema container. +func (computeSystem *System) Properties(ctx context.Context, types ...schema1.PropertyType) (*schema1.ContainerProperties, error) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + operation := "hcs::System::Properties" + + queryBytes, err := json.Marshal(schema1.PropertyQuery{PropertyTypes: types}) + if err != nil { + return nil, makeSystemError(computeSystem, operation, err, nil) + } + + propertiesJSON, resultJSON, err := vmcompute.HcsGetComputeSystemProperties(ctx, computeSystem.handle, string(queryBytes)) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return nil, makeSystemError(computeSystem, operation, err, events) + } + + if propertiesJSON == "" { + return nil, ErrUnexpectedValue + } + properties := &schema1.ContainerProperties{} + if err := json.Unmarshal([]byte(propertiesJSON), properties); err != nil { + return nil, makeSystemError(computeSystem, operation, err, nil) + } + + return properties, nil +} + +// PropertiesV2 returns the requested container properties targeting a V2 schema container. +func (computeSystem *System) PropertiesV2(ctx context.Context, types ...hcsschema.PropertyType) (*hcsschema.Properties, error) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + operation := "hcs::System::PropertiesV2" + + queryBytes, err := json.Marshal(hcsschema.PropertyQuery{PropertyTypes: types}) + if err != nil { + return nil, makeSystemError(computeSystem, operation, err, nil) + } + + propertiesJSON, resultJSON, err := vmcompute.HcsGetComputeSystemProperties(ctx, computeSystem.handle, string(queryBytes)) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return nil, makeSystemError(computeSystem, operation, err, events) + } + + if propertiesJSON == "" { + return nil, ErrUnexpectedValue + } + properties := &hcsschema.Properties{} + if err := json.Unmarshal([]byte(propertiesJSON), properties); err != nil { + return nil, makeSystemError(computeSystem, operation, err, nil) + } + + return properties, nil +} + +// Pause pauses the execution of the computeSystem. This feature is not enabled in TP5. +func (computeSystem *System) Pause(ctx context.Context) (err error) { + operation := "hcs::System::Pause" + + // hcsPauseComputeSystemContext is an async peration. Start the outer span + // here to measure the full pause time. + ctx, span := trace.StartSpan(ctx, operation) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) + + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) + } + + resultJSON, err := vmcompute.HcsPauseComputeSystem(ctx, computeSystem.handle, "") + events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber, hcsNotificationSystemPauseCompleted, &timeout.SystemPause) + if err != nil { + return makeSystemError(computeSystem, operation, err, events) + } + + return nil +} + +// Resume resumes the execution of the computeSystem. This feature is not enabled in TP5. +func (computeSystem *System) Resume(ctx context.Context) (err error) { + operation := "hcs::System::Resume" + + // hcsResumeComputeSystemContext is an async operation. Start the outer span + // here to measure the full restore time. + ctx, span := trace.StartSpan(ctx, operation) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) + + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) + } + + resultJSON, err := vmcompute.HcsResumeComputeSystem(ctx, computeSystem.handle, "") + events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber, hcsNotificationSystemResumeCompleted, &timeout.SystemResume) + if err != nil { + return makeSystemError(computeSystem, operation, err, events) + } + + return nil +} + +// Save the compute system +func (computeSystem *System) Save(ctx context.Context, options interface{}) (err error) { + operation := "hcs::System::Save" + + // hcsSaveComputeSystemContext is an async peration. Start the outer span + // here to measure the full save time. + ctx, span := trace.StartSpan(ctx, operation) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) + + saveOptions, err := json.Marshal(options) + if err != nil { + return err + } + + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) + } + + result, err := vmcompute.HcsSaveComputeSystem(ctx, computeSystem.handle, string(saveOptions)) + events, err := processAsyncHcsResult(ctx, err, result, computeSystem.callbackNumber, hcsNotificationSystemSaveCompleted, &timeout.SystemSave) + if err != nil { + return makeSystemError(computeSystem, operation, err, events) + } + + return nil +} + +func (computeSystem *System) createProcess(ctx context.Context, operation string, c interface{}) (*Process, *vmcompute.HcsProcessInformation, error) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + if computeSystem.handle == 0 { + return nil, nil, makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) + } + + configurationb, err := json.Marshal(c) + if err != nil { + return nil, nil, makeSystemError(computeSystem, operation, err, nil) + } + + configuration := string(configurationb) + processInfo, processHandle, resultJSON, err := vmcompute.HcsCreateProcess(ctx, computeSystem.handle, configuration) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return nil, nil, makeSystemError(computeSystem, operation, err, events) + } + + log.G(ctx).WithField("pid", processInfo.ProcessId).Debug("created process pid") + return newProcess(processHandle, int(processInfo.ProcessId), computeSystem), &processInfo, nil +} + +// CreateProcess launches a new process within the computeSystem. +func (computeSystem *System) CreateProcess(ctx context.Context, c interface{}) (cow.Process, error) { + operation := "hcs::System::CreateProcess" + process, processInfo, err := computeSystem.createProcess(ctx, operation, c) + if err != nil { + return nil, err + } + defer func() { + if err != nil { + process.Close() + } + }() + + pipes, err := makeOpenFiles([]syscall.Handle{processInfo.StdInput, processInfo.StdOutput, processInfo.StdError}) + if err != nil { + return nil, makeSystemError(computeSystem, operation, err, nil) + } + process.stdin = pipes[0] + process.stdout = pipes[1] + process.stderr = pipes[2] + process.hasCachedStdio = true + + if err = process.registerCallback(ctx); err != nil { + return nil, makeSystemError(computeSystem, operation, err, nil) + } + go process.waitBackground() + + return process, nil +} + +// OpenProcess gets an interface to an existing process within the computeSystem. +func (computeSystem *System) OpenProcess(ctx context.Context, pid int) (*Process, error) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + operation := "hcs::System::OpenProcess" + + if computeSystem.handle == 0 { + return nil, makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) + } + + processHandle, resultJSON, err := vmcompute.HcsOpenProcess(ctx, computeSystem.handle, uint32(pid)) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return nil, makeSystemError(computeSystem, operation, err, events) + } + + process := newProcess(processHandle, pid, computeSystem) + if err = process.registerCallback(ctx); err != nil { + return nil, makeSystemError(computeSystem, operation, err, nil) + } + go process.waitBackground() + + return process, nil +} + +// Close cleans up any state associated with the compute system but does not terminate or wait for it. +func (computeSystem *System) Close() (err error) { + operation := "hcs::System::Close" + ctx, span := trace.StartSpan(context.Background(), operation) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) + + computeSystem.handleLock.Lock() + defer computeSystem.handleLock.Unlock() + + // Don't double free this + if computeSystem.handle == 0 { + return nil + } + + if err = computeSystem.unregisterCallback(ctx); err != nil { + return makeSystemError(computeSystem, operation, err, nil) + } + + err = vmcompute.HcsCloseComputeSystem(ctx, computeSystem.handle) + if err != nil { + return makeSystemError(computeSystem, operation, err, nil) + } + + computeSystem.handle = 0 + computeSystem.closedWaitOnce.Do(func() { + computeSystem.waitError = ErrAlreadyClosed + close(computeSystem.waitBlock) + }) + + return nil +} + +func (computeSystem *System) registerCallback(ctx context.Context) error { + callbackContext := ¬ificationWatcherContext{ + channels: newSystemChannels(), + systemID: computeSystem.id, + } + + callbackMapLock.Lock() + callbackNumber := nextCallback + nextCallback++ + callbackMap[callbackNumber] = callbackContext + callbackMapLock.Unlock() + + callbackHandle, err := vmcompute.HcsRegisterComputeSystemCallback(ctx, computeSystem.handle, notificationWatcherCallback, callbackNumber) + if err != nil { + return err + } + callbackContext.handle = callbackHandle + computeSystem.callbackNumber = callbackNumber + + return nil +} + +func (computeSystem *System) unregisterCallback(ctx context.Context) error { + callbackNumber := computeSystem.callbackNumber + + callbackMapLock.RLock() + callbackContext := callbackMap[callbackNumber] + callbackMapLock.RUnlock() + + if callbackContext == nil { + return nil + } + + handle := callbackContext.handle + + if handle == 0 { + return nil + } + + // hcsUnregisterComputeSystemCallback has its own syncronization + // to wait for all callbacks to complete. We must NOT hold the callbackMapLock. + err := vmcompute.HcsUnregisterComputeSystemCallback(ctx, handle) + if err != nil { + return err + } + + closeChannels(callbackContext.channels) + + callbackMapLock.Lock() + delete(callbackMap, callbackNumber) + callbackMapLock.Unlock() + + handle = 0 //nolint:ineffassign + + return nil +} + +// Modify the System by sending a request to HCS +func (computeSystem *System) Modify(ctx context.Context, config interface{}) error { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + operation := "hcs::System::Modify" + + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) + } + + requestBytes, err := json.Marshal(config) + if err != nil { + return err + } + + requestJSON := string(requestBytes) + resultJSON, err := vmcompute.HcsModifyComputeSystem(ctx, computeSystem.handle, requestJSON) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return makeSystemError(computeSystem, operation, err, events) + } + + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/utils.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/utils.go new file mode 100644 index 0000000000..3342e5bb94 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/utils.go @@ -0,0 +1,62 @@ +package hcs + +import ( + "context" + "io" + "syscall" + + "github.com/Microsoft/go-winio" + diskutil "github.com/Microsoft/go-winio/vhd" + "github.com/Microsoft/hcsshim/computestorage" + "github.com/pkg/errors" + "golang.org/x/sys/windows" +) + +// makeOpenFiles calls winio.MakeOpenFile for each handle in a slice but closes all the handles +// if there is an error. +func makeOpenFiles(hs []syscall.Handle) (_ []io.ReadWriteCloser, err error) { + fs := make([]io.ReadWriteCloser, len(hs)) + for i, h := range hs { + if h != syscall.Handle(0) { + if err == nil { + fs[i], err = winio.MakeOpenFile(h) + } + if err != nil { + syscall.Close(h) + } + } + } + if err != nil { + for _, f := range fs { + if f != nil { + f.Close() + } + } + return nil, err + } + return fs, nil +} + +// CreateNTFSVHD creates a VHD formatted with NTFS of size `sizeGB` at the given `vhdPath`. +func CreateNTFSVHD(ctx context.Context, vhdPath string, sizeGB uint32) (err error) { + if err := diskutil.CreateVhdx(vhdPath, sizeGB, 1); err != nil { + return errors.Wrap(err, "failed to create VHD") + } + + vhd, err := diskutil.OpenVirtualDisk(vhdPath, diskutil.VirtualDiskAccessNone, diskutil.OpenVirtualDiskFlagNone) + if err != nil { + return errors.Wrap(err, "failed to open VHD") + } + defer func() { + err2 := windows.CloseHandle(windows.Handle(vhd)) + if err == nil { + err = errors.Wrap(err2, "failed to close VHD") + } + }() + + if err := computestorage.FormatWritableLayerVhd(ctx, windows.Handle(vhd)); err != nil { + return errors.Wrap(err, "failed to format VHD") + } + + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/waithelper.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/waithelper.go new file mode 100644 index 0000000000..db4e14fdfb --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/waithelper.go @@ -0,0 +1,68 @@ +package hcs + +import ( + "context" + "time" + + "github.com/Microsoft/hcsshim/internal/log" +) + +func processAsyncHcsResult(ctx context.Context, err error, resultJSON string, callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) ([]ErrorEvent, error) { + events := processHcsResult(ctx, resultJSON) + if IsPending(err) { + return nil, waitForNotification(ctx, callbackNumber, expectedNotification, timeout) + } + + return events, err +} + +func waitForNotification(ctx context.Context, callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) error { + callbackMapLock.RLock() + if _, ok := callbackMap[callbackNumber]; !ok { + callbackMapLock.RUnlock() + log.G(ctx).WithField("callbackNumber", callbackNumber).Error("failed to waitForNotification: callbackNumber does not exist in callbackMap") + return ErrHandleClose + } + channels := callbackMap[callbackNumber].channels + callbackMapLock.RUnlock() + + expectedChannel := channels[expectedNotification] + if expectedChannel == nil { + log.G(ctx).WithField("type", expectedNotification).Error("unknown notification type in waitForNotification") + return ErrInvalidNotificationType + } + + var c <-chan time.Time + if timeout != nil { + timer := time.NewTimer(*timeout) + c = timer.C + defer timer.Stop() + } + + select { + case err, ok := <-expectedChannel: + if !ok { + return ErrHandleClose + } + return err + case err, ok := <-channels[hcsNotificationSystemExited]: + if !ok { + return ErrHandleClose + } + // If the expected notification is hcsNotificationSystemExited which of the two selects + // chosen is random. Return the raw error if hcsNotificationSystemExited is expected + if channels[hcsNotificationSystemExited] == expectedChannel { + return err + } + return ErrUnexpectedContainerExit + case _, ok := <-channels[hcsNotificationServiceDisconnect]: + if !ok { + return ErrHandleClose + } + // hcsNotificationServiceDisconnect should never be an expected notification + // it does not need the same handling as hcsNotificationSystemExited + return ErrUnexpectedProcessAbort + case <-c: + return ErrTimeout + } +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go b/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go new file mode 100644 index 0000000000..921c2c8556 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go @@ -0,0 +1,47 @@ +package hcserror + +import ( + "fmt" + "syscall" +) + +const ERROR_GEN_FAILURE = syscall.Errno(31) + +type HcsError struct { + title string + rest string + Err error +} + +func (e *HcsError) Error() string { + s := e.title + if len(s) > 0 && s[len(s)-1] != ' ' { + s += " " + } + s += fmt.Sprintf("failed in Win32: %s (0x%x)", e.Err, Win32FromError(e.Err)) + if e.rest != "" { + if e.rest[0] != ' ' { + s += " " + } + s += e.rest + } + return s +} + +func New(err error, title, rest string) error { + // Pass through DLL errors directly since they do not originate from HCS. + if _, ok := err.(*syscall.DLLError); ok { + return err + } + return &HcsError{title, rest, err} +} + +func Win32FromError(err error) uint32 { + if herr, ok := err.(*HcsError); ok { + return Win32FromError(herr.Err) + } + if code, ok := err.(syscall.Errno); ok { + return uint32(code) + } + return uint32(ERROR_GEN_FAILURE) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hns.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hns.go new file mode 100644 index 0000000000..b2e475f53c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hns.go @@ -0,0 +1,23 @@ +package hns + +import "fmt" + +//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go hns.go + +//sys _hnsCall(method string, path string, object string, response **uint16) (hr error) = vmcompute.HNSCall? + +type EndpointNotFoundError struct { + EndpointName string +} + +func (e EndpointNotFoundError) Error() string { + return fmt.Sprintf("Endpoint %s not found", e.EndpointName) +} + +type NetworkNotFoundError struct { + NetworkName string +} + +func (e NetworkNotFoundError) Error() string { + return fmt.Sprintf("Network %s not found", e.NetworkName) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go new file mode 100644 index 0000000000..7cf954c7b2 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go @@ -0,0 +1,338 @@ +package hns + +import ( + "encoding/json" + "net" + "strings" + + "github.com/sirupsen/logrus" +) + +// HNSEndpoint represents a network endpoint in HNS +type HNSEndpoint struct { + Id string `json:"ID,omitempty"` + Name string `json:",omitempty"` + VirtualNetwork string `json:",omitempty"` + VirtualNetworkName string `json:",omitempty"` + Policies []json.RawMessage `json:",omitempty"` + MacAddress string `json:",omitempty"` + IPAddress net.IP `json:",omitempty"` + IPv6Address net.IP `json:",omitempty"` + DNSSuffix string `json:",omitempty"` + DNSServerList string `json:",omitempty"` + DNSDomain string `json:",omitempty"` + GatewayAddress string `json:",omitempty"` + GatewayAddressV6 string `json:",omitempty"` + EnableInternalDNS bool `json:",omitempty"` + DisableICC bool `json:",omitempty"` + PrefixLength uint8 `json:",omitempty"` + IPv6PrefixLength uint8 `json:",omitempty"` + IsRemoteEndpoint bool `json:",omitempty"` + EnableLowMetric bool `json:",omitempty"` + Namespace *Namespace `json:",omitempty"` + EncapOverhead uint16 `json:",omitempty"` + SharedContainers []string `json:",omitempty"` +} + +//SystemType represents the type of the system on which actions are done +type SystemType string + +// SystemType const +const ( + ContainerType SystemType = "Container" + VirtualMachineType SystemType = "VirtualMachine" + HostType SystemType = "Host" +) + +// EndpointAttachDetachRequest is the structure used to send request to the container to modify the system +// Supported resource types are Network and Request Types are Add/Remove +type EndpointAttachDetachRequest struct { + ContainerID string `json:"ContainerId,omitempty"` + SystemType SystemType `json:"SystemType"` + CompartmentID uint16 `json:"CompartmentId,omitempty"` + VirtualNICName string `json:"VirtualNicName,omitempty"` +} + +// EndpointResquestResponse is object to get the endpoint request response +type EndpointResquestResponse struct { + Success bool + Error string +} + +// EndpointStats is the object that has stats for a given endpoint +type EndpointStats struct { + BytesReceived uint64 `json:"BytesReceived"` + BytesSent uint64 `json:"BytesSent"` + DroppedPacketsIncoming uint64 `json:"DroppedPacketsIncoming"` + DroppedPacketsOutgoing uint64 `json:"DroppedPacketsOutgoing"` + EndpointID string `json:"EndpointId"` + InstanceID string `json:"InstanceId"` + PacketsReceived uint64 `json:"PacketsReceived"` + PacketsSent uint64 `json:"PacketsSent"` +} + +// HNSEndpointRequest makes a HNS call to modify/query a network endpoint +func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) { + endpoint := &HNSEndpoint{} + err := hnsCall(method, "/endpoints/"+path, request, &endpoint) + if err != nil { + return nil, err + } + + return endpoint, nil +} + +// HNSListEndpointRequest makes a HNS call to query the list of available endpoints +func HNSListEndpointRequest() ([]HNSEndpoint, error) { + var endpoint []HNSEndpoint + err := hnsCall("GET", "/endpoints/", "", &endpoint) + if err != nil { + return nil, err + } + + return endpoint, nil +} + +// hnsEndpointStatsRequest makes a HNS call to query the stats for a given endpoint ID +func hnsEndpointStatsRequest(id string) (*EndpointStats, error) { + var stats EndpointStats + err := hnsCall("GET", "/endpointstats/"+id, "", &stats) + if err != nil { + return nil, err + } + + return &stats, nil +} + +// GetHNSEndpointByID get the Endpoint by ID +func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) { + return HNSEndpointRequest("GET", endpointID, "") +} + +// GetHNSEndpointStats get the stats for a n Endpoint by ID +func GetHNSEndpointStats(endpointID string) (*EndpointStats, error) { + return hnsEndpointStatsRequest(endpointID) +} + +// GetHNSEndpointByName gets the endpoint filtered by Name +func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) { + hnsResponse, err := HNSListEndpointRequest() + if err != nil { + return nil, err + } + for _, hnsEndpoint := range hnsResponse { + if hnsEndpoint.Name == endpointName { + return &hnsEndpoint, nil + } + } + return nil, EndpointNotFoundError{EndpointName: endpointName} +} + +type endpointAttachInfo struct { + SharedContainers json.RawMessage `json:",omitempty"` +} + +func (endpoint *HNSEndpoint) IsAttached(vID string) (bool, error) { + attachInfo := endpointAttachInfo{} + err := hnsCall("GET", "/endpoints/"+endpoint.Id, "", &attachInfo) + + // Return false allows us to just return the err + if err != nil { + return false, err + } + + if strings.Contains(strings.ToLower(string(attachInfo.SharedContainers)), strings.ToLower(vID)) { + return true, nil + } + + return false, nil + +} + +// Create Endpoint by sending EndpointRequest to HNS. TODO: Create a separate HNS interface to place all these methods +func (endpoint *HNSEndpoint) Create() (*HNSEndpoint, error) { + operation := "Create" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + jsonString, err := json.Marshal(endpoint) + if err != nil { + return nil, err + } + return HNSEndpointRequest("POST", "", string(jsonString)) +} + +// Delete Endpoint by sending EndpointRequest to HNS +func (endpoint *HNSEndpoint) Delete() (*HNSEndpoint, error) { + operation := "Delete" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + return HNSEndpointRequest("DELETE", endpoint.Id, "") +} + +// Update Endpoint +func (endpoint *HNSEndpoint) Update() (*HNSEndpoint, error) { + operation := "Update" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + jsonString, err := json.Marshal(endpoint) + if err != nil { + return nil, err + } + err = hnsCall("POST", "/endpoints/"+endpoint.Id, string(jsonString), &endpoint) + + return endpoint, err +} + +// ApplyACLPolicy applies a set of ACL Policies on the Endpoint +func (endpoint *HNSEndpoint) ApplyACLPolicy(policies ...*ACLPolicy) error { + operation := "ApplyACLPolicy" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + for _, policy := range policies { + if policy == nil { + continue + } + jsonString, err := json.Marshal(policy) + if err != nil { + return err + } + endpoint.Policies = append(endpoint.Policies, jsonString) + } + + _, err := endpoint.Update() + return err +} + +// ApplyProxyPolicy applies a set of Proxy Policies on the Endpoint +func (endpoint *HNSEndpoint) ApplyProxyPolicy(policies ...*ProxyPolicy) error { + operation := "ApplyProxyPolicy" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + for _, policy := range policies { + if policy == nil { + continue + } + jsonString, err := json.Marshal(policy) + if err != nil { + return err + } + endpoint.Policies = append(endpoint.Policies, jsonString) + } + + _, err := endpoint.Update() + return err +} + +// ContainerAttach attaches an endpoint to container +func (endpoint *HNSEndpoint) ContainerAttach(containerID string, compartmentID uint16) error { + operation := "ContainerAttach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + requestMessage := &EndpointAttachDetachRequest{ + ContainerID: containerID, + CompartmentID: compartmentID, + SystemType: ContainerType, + } + response := &EndpointResquestResponse{} + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response) +} + +// ContainerDetach detaches an endpoint from container +func (endpoint *HNSEndpoint) ContainerDetach(containerID string) error { + operation := "ContainerDetach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + requestMessage := &EndpointAttachDetachRequest{ + ContainerID: containerID, + SystemType: ContainerType, + } + response := &EndpointResquestResponse{} + + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response) +} + +// HostAttach attaches a nic on the host +func (endpoint *HNSEndpoint) HostAttach(compartmentID uint16) error { + operation := "HostAttach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + requestMessage := &EndpointAttachDetachRequest{ + CompartmentID: compartmentID, + SystemType: HostType, + } + response := &EndpointResquestResponse{} + + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response) + +} + +// HostDetach detaches a nic on the host +func (endpoint *HNSEndpoint) HostDetach() error { + operation := "HostDetach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + requestMessage := &EndpointAttachDetachRequest{ + SystemType: HostType, + } + response := &EndpointResquestResponse{} + + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response) +} + +// VirtualMachineNICAttach attaches a endpoint to a virtual machine +func (endpoint *HNSEndpoint) VirtualMachineNICAttach(virtualMachineNICName string) error { + operation := "VirtualMachineNicAttach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + requestMessage := &EndpointAttachDetachRequest{ + VirtualNICName: virtualMachineNICName, + SystemType: VirtualMachineType, + } + response := &EndpointResquestResponse{} + + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response) +} + +// VirtualMachineNICDetach detaches a endpoint from a virtual machine +func (endpoint *HNSEndpoint) VirtualMachineNICDetach() error { + operation := "VirtualMachineNicDetach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + requestMessage := &EndpointAttachDetachRequest{ + SystemType: VirtualMachineType, + } + response := &EndpointResquestResponse{} + + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsfuncs.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsfuncs.go new file mode 100644 index 0000000000..2df4a57f56 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsfuncs.go @@ -0,0 +1,49 @@ +package hns + +import ( + "encoding/json" + "fmt" + + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/sirupsen/logrus" +) + +func hnsCallRawResponse(method, path, request string) (*hnsResponse, error) { + var responseBuffer *uint16 + logrus.Debugf("[%s]=>[%s] Request : %s", method, path, request) + + err := _hnsCall(method, path, request, &responseBuffer) + if err != nil { + return nil, hcserror.New(err, "hnsCall ", "") + } + response := interop.ConvertAndFreeCoTaskMemString(responseBuffer) + + hnsresponse := &hnsResponse{} + if err = json.Unmarshal([]byte(response), &hnsresponse); err != nil { + return nil, err + } + return hnsresponse, nil +} + +func hnsCall(method, path, request string, returnResponse interface{}) error { + hnsresponse, err := hnsCallRawResponse(method, path, request) + if err != nil { + return fmt.Errorf("failed during hnsCallRawResponse: %v", err) + } + if !hnsresponse.Success { + return fmt.Errorf("hns failed with error : %s", hnsresponse.Error) + } + + if len(hnsresponse.Output) == 0 { + return nil + } + + logrus.Debugf("Network Response : %s", hnsresponse.Output) + err = json.Unmarshal(hnsresponse.Output, returnResponse) + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsglobals.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsglobals.go new file mode 100644 index 0000000000..a8d8cc56ae --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsglobals.go @@ -0,0 +1,28 @@ +package hns + +type HNSGlobals struct { + Version HNSVersion `json:"Version"` +} + +type HNSVersion struct { + Major int `json:"Major"` + Minor int `json:"Minor"` +} + +var ( + HNSVersion1803 = HNSVersion{Major: 7, Minor: 2} +) + +func GetHNSGlobals() (*HNSGlobals, error) { + var version HNSVersion + err := hnsCall("GET", "/globals/version", "", &version) + if err != nil { + return nil, err + } + + globals := &HNSGlobals{ + Version: version, + } + + return globals, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsnetwork.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsnetwork.go new file mode 100644 index 0000000000..f12d3ab041 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsnetwork.go @@ -0,0 +1,141 @@ +package hns + +import ( + "encoding/json" + "errors" + "github.com/sirupsen/logrus" + "net" +) + +// Subnet is assoicated with a network and represents a list +// of subnets available to the network +type Subnet struct { + AddressPrefix string `json:",omitempty"` + GatewayAddress string `json:",omitempty"` + Policies []json.RawMessage `json:",omitempty"` +} + +// MacPool is assoicated with a network and represents a list +// of macaddresses available to the network +type MacPool struct { + StartMacAddress string `json:",omitempty"` + EndMacAddress string `json:",omitempty"` +} + +// HNSNetwork represents a network in HNS +type HNSNetwork struct { + Id string `json:"ID,omitempty"` + Name string `json:",omitempty"` + Type string `json:",omitempty"` + NetworkAdapterName string `json:",omitempty"` + SourceMac string `json:",omitempty"` + Policies []json.RawMessage `json:",omitempty"` + MacPools []MacPool `json:",omitempty"` + Subnets []Subnet `json:",omitempty"` + DNSSuffix string `json:",omitempty"` + DNSServerList string `json:",omitempty"` + DNSServerCompartment uint32 `json:",omitempty"` + ManagementIP string `json:",omitempty"` + AutomaticDNS bool `json:",omitempty"` +} + +type hnsResponse struct { + Success bool + Error string + Output json.RawMessage +} + +// HNSNetworkRequest makes a call into HNS to update/query a single network +func HNSNetworkRequest(method, path, request string) (*HNSNetwork, error) { + var network HNSNetwork + err := hnsCall(method, "/networks/"+path, request, &network) + if err != nil { + return nil, err + } + + return &network, nil +} + +// HNSListNetworkRequest makes a HNS call to query the list of available networks +func HNSListNetworkRequest(method, path, request string) ([]HNSNetwork, error) { + var network []HNSNetwork + err := hnsCall(method, "/networks/"+path, request, &network) + if err != nil { + return nil, err + } + + return network, nil +} + +// GetHNSNetworkByID +func GetHNSNetworkByID(networkID string) (*HNSNetwork, error) { + return HNSNetworkRequest("GET", networkID, "") +} + +// GetHNSNetworkName filtered by Name +func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) { + hsnnetworks, err := HNSListNetworkRequest("GET", "", "") + if err != nil { + return nil, err + } + for _, hnsnetwork := range hsnnetworks { + if hnsnetwork.Name == networkName { + return &hnsnetwork, nil + } + } + return nil, NetworkNotFoundError{NetworkName: networkName} +} + +// Create Network by sending NetworkRequest to HNS. +func (network *HNSNetwork) Create() (*HNSNetwork, error) { + operation := "Create" + title := "hcsshim::HNSNetwork::" + operation + logrus.Debugf(title+" id=%s", network.Id) + + for _, subnet := range network.Subnets { + if (subnet.AddressPrefix != "") && (subnet.GatewayAddress == "") { + return nil, errors.New("network create error, subnet has address prefix but no gateway specified") + } + } + + jsonString, err := json.Marshal(network) + if err != nil { + return nil, err + } + return HNSNetworkRequest("POST", "", string(jsonString)) +} + +// Delete Network by sending NetworkRequest to HNS +func (network *HNSNetwork) Delete() (*HNSNetwork, error) { + operation := "Delete" + title := "hcsshim::HNSNetwork::" + operation + logrus.Debugf(title+" id=%s", network.Id) + + return HNSNetworkRequest("DELETE", network.Id, "") +} + +// Creates an endpoint on the Network. +func (network *HNSNetwork) NewEndpoint(ipAddress net.IP, macAddress net.HardwareAddr) *HNSEndpoint { + return &HNSEndpoint{ + VirtualNetwork: network.Id, + IPAddress: ipAddress, + MacAddress: string(macAddress), + } +} + +func (network *HNSNetwork) CreateEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) { + operation := "CreateEndpoint" + title := "hcsshim::HNSNetwork::" + operation + logrus.Debugf(title+" id=%s, endpointId=%s", network.Id, endpoint.Id) + + endpoint.VirtualNetwork = network.Id + return endpoint.Create() +} + +func (network *HNSNetwork) CreateRemoteEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) { + operation := "CreateRemoteEndpoint" + title := "hcsshim::HNSNetwork::" + operation + logrus.Debugf(title+" id=%s", network.Id) + endpoint.IsRemoteEndpoint = true + return network.CreateEndpoint(endpoint) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go new file mode 100644 index 0000000000..591a2631e4 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go @@ -0,0 +1,109 @@ +package hns + +// Type of Request Support in ModifySystem +type PolicyType string + +// RequestType const +const ( + Nat PolicyType = "NAT" + ACL PolicyType = "ACL" + PA PolicyType = "PA" + VLAN PolicyType = "VLAN" + VSID PolicyType = "VSID" + VNet PolicyType = "VNET" + L2Driver PolicyType = "L2Driver" + Isolation PolicyType = "Isolation" + QOS PolicyType = "QOS" + OutboundNat PolicyType = "OutBoundNAT" + ExternalLoadBalancer PolicyType = "ELB" + Route PolicyType = "ROUTE" + Proxy PolicyType = "PROXY" +) + +type NatPolicy struct { + Type PolicyType `json:"Type"` + Protocol string `json:",omitempty"` + InternalPort uint16 `json:",omitempty"` + ExternalPort uint16 `json:",omitempty"` +} + +type QosPolicy struct { + Type PolicyType `json:"Type"` + MaximumOutgoingBandwidthInBytes uint64 +} + +type IsolationPolicy struct { + Type PolicyType `json:"Type"` + VLAN uint + VSID uint + InDefaultIsolation bool +} + +type VlanPolicy struct { + Type PolicyType `json:"Type"` + VLAN uint +} + +type VsidPolicy struct { + Type PolicyType `json:"Type"` + VSID uint +} + +type PaPolicy struct { + Type PolicyType `json:"Type"` + PA string `json:"PA"` +} + +type OutboundNatPolicy struct { + Policy + VIP string `json:"VIP,omitempty"` + Exceptions []string `json:"ExceptionList,omitempty"` + Destinations []string `json:",omitempty"` +} + +type ProxyPolicy struct { + Type PolicyType `json:"Type"` + IP string `json:",omitempty"` + Port string `json:",omitempty"` + ExceptionList []string `json:",omitempty"` + Destination string `json:",omitempty"` + OutboundNat bool `json:",omitempty"` +} + +type ActionType string +type DirectionType string +type RuleType string + +const ( + Allow ActionType = "Allow" + Block ActionType = "Block" + + In DirectionType = "In" + Out DirectionType = "Out" + + Host RuleType = "Host" + Switch RuleType = "Switch" +) + +type ACLPolicy struct { + Type PolicyType `json:"Type"` + Id string `json:"Id,omitempty"` + Protocol uint16 `json:",omitempty"` + Protocols string `json:"Protocols,omitempty"` + InternalPort uint16 `json:",omitempty"` + Action ActionType + Direction DirectionType + LocalAddresses string `json:",omitempty"` + RemoteAddresses string `json:",omitempty"` + LocalPorts string `json:"LocalPorts,omitempty"` + LocalPort uint16 `json:",omitempty"` + RemotePorts string `json:"RemotePorts,omitempty"` + RemotePort uint16 `json:",omitempty"` + RuleType RuleType `json:"RuleType,omitempty"` + Priority uint16 `json:",omitempty"` + ServiceName string `json:",omitempty"` +} + +type Policy struct { + Type PolicyType `json:"Type"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go new file mode 100644 index 0000000000..31322a6816 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go @@ -0,0 +1,201 @@ +package hns + +import ( + "encoding/json" + + "github.com/sirupsen/logrus" +) + +// RoutePolicy is a structure defining schema for Route based Policy +type RoutePolicy struct { + Policy + DestinationPrefix string `json:"DestinationPrefix,omitempty"` + NextHop string `json:"NextHop,omitempty"` + EncapEnabled bool `json:"NeedEncap,omitempty"` +} + +// ELBPolicy is a structure defining schema for ELB LoadBalancing based Policy +type ELBPolicy struct { + LBPolicy + SourceVIP string `json:"SourceVIP,omitempty"` + VIPs []string `json:"VIPs,omitempty"` + ILB bool `json:"ILB,omitempty"` + DSR bool `json:"IsDSR,omitempty"` +} + +// LBPolicy is a structure defining schema for LoadBalancing based Policy +type LBPolicy struct { + Policy + Protocol uint16 `json:"Protocol,omitempty"` + InternalPort uint16 + ExternalPort uint16 +} + +// PolicyList is a structure defining schema for Policy list request +type PolicyList struct { + ID string `json:"ID,omitempty"` + EndpointReferences []string `json:"References,omitempty"` + Policies []json.RawMessage `json:"Policies,omitempty"` +} + +// HNSPolicyListRequest makes a call into HNS to update/query a single network +func HNSPolicyListRequest(method, path, request string) (*PolicyList, error) { + var policy PolicyList + err := hnsCall(method, "/policylists/"+path, request, &policy) + if err != nil { + return nil, err + } + + return &policy, nil +} + +// HNSListPolicyListRequest gets all the policy list +func HNSListPolicyListRequest() ([]PolicyList, error) { + var plist []PolicyList + err := hnsCall("GET", "/policylists/", "", &plist) + if err != nil { + return nil, err + } + + return plist, nil +} + +// PolicyListRequest makes a HNS call to modify/query a network policy list +func PolicyListRequest(method, path, request string) (*PolicyList, error) { + policylist := &PolicyList{} + err := hnsCall(method, "/policylists/"+path, request, &policylist) + if err != nil { + return nil, err + } + + return policylist, nil +} + +// GetPolicyListByID get the policy list by ID +func GetPolicyListByID(policyListID string) (*PolicyList, error) { + return PolicyListRequest("GET", policyListID, "") +} + +// Create PolicyList by sending PolicyListRequest to HNS. +func (policylist *PolicyList) Create() (*PolicyList, error) { + operation := "Create" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" id=%s", policylist.ID) + jsonString, err := json.Marshal(policylist) + if err != nil { + return nil, err + } + return PolicyListRequest("POST", "", string(jsonString)) +} + +// Delete deletes PolicyList +func (policylist *PolicyList) Delete() (*PolicyList, error) { + operation := "Delete" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" id=%s", policylist.ID) + + return PolicyListRequest("DELETE", policylist.ID, "") +} + +// AddEndpoint add an endpoint to a Policy List +func (policylist *PolicyList) AddEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) { + operation := "AddEndpoint" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id) + + _, err := policylist.Delete() + if err != nil { + return nil, err + } + + // Add Endpoint to the Existing List + policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id) + + return policylist.Create() +} + +// RemoveEndpoint removes an endpoint from the Policy List +func (policylist *PolicyList) RemoveEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) { + operation := "RemoveEndpoint" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id) + + _, err := policylist.Delete() + if err != nil { + return nil, err + } + + elementToRemove := "/endpoints/" + endpoint.Id + + var references []string + + for _, endpointReference := range policylist.EndpointReferences { + if endpointReference == elementToRemove { + continue + } + references = append(references, endpointReference) + } + policylist.EndpointReferences = references + return policylist.Create() +} + +// AddLoadBalancer policy list for the specified endpoints +func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) { + operation := "AddLoadBalancer" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" endpointId=%v, isILB=%v, sourceVIP=%s, vip=%s, protocol=%v, internalPort=%v, externalPort=%v", endpoints, isILB, sourceVIP, vip, protocol, internalPort, externalPort) + + policylist := &PolicyList{} + + elbPolicy := &ELBPolicy{ + SourceVIP: sourceVIP, + ILB: isILB, + } + + if len(vip) > 0 { + elbPolicy.VIPs = []string{vip} + } + elbPolicy.Type = ExternalLoadBalancer + elbPolicy.Protocol = protocol + elbPolicy.InternalPort = internalPort + elbPolicy.ExternalPort = externalPort + + for _, endpoint := range endpoints { + policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id) + } + + jsonString, err := json.Marshal(elbPolicy) + if err != nil { + return nil, err + } + policylist.Policies = append(policylist.Policies, jsonString) + return policylist.Create() +} + +// AddRoute adds route policy list for the specified endpoints +func AddRoute(endpoints []HNSEndpoint, destinationPrefix string, nextHop string, encapEnabled bool) (*PolicyList, error) { + operation := "AddRoute" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" destinationPrefix:%s", destinationPrefix) + + policylist := &PolicyList{} + + rPolicy := &RoutePolicy{ + DestinationPrefix: destinationPrefix, + NextHop: nextHop, + EncapEnabled: encapEnabled, + } + rPolicy.Type = Route + + for _, endpoint := range endpoints { + policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id) + } + + jsonString, err := json.Marshal(rPolicy) + if err != nil { + return nil, err + } + + policylist.Policies = append(policylist.Policies, jsonString) + return policylist.Create() +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnssupport.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnssupport.go new file mode 100644 index 0000000000..d5efba7f28 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnssupport.go @@ -0,0 +1,49 @@ +package hns + +import ( + "github.com/sirupsen/logrus" +) + +type HNSSupportedFeatures struct { + Acl HNSAclFeatures `json:"ACL"` +} + +type HNSAclFeatures struct { + AclAddressLists bool `json:"AclAddressLists"` + AclNoHostRulePriority bool `json:"AclHostRulePriority"` + AclPortRanges bool `json:"AclPortRanges"` + AclRuleId bool `json:"AclRuleId"` +} + +func GetHNSSupportedFeatures() HNSSupportedFeatures { + var hnsFeatures HNSSupportedFeatures + + globals, err := GetHNSGlobals() + if err != nil { + // Expected on pre-1803 builds, all features will be false/unsupported + logrus.Debugf("Unable to obtain HNS globals: %s", err) + return hnsFeatures + } + + hnsFeatures.Acl = HNSAclFeatures{ + AclAddressLists: isHNSFeatureSupported(globals.Version, HNSVersion1803), + AclNoHostRulePriority: isHNSFeatureSupported(globals.Version, HNSVersion1803), + AclPortRanges: isHNSFeatureSupported(globals.Version, HNSVersion1803), + AclRuleId: isHNSFeatureSupported(globals.Version, HNSVersion1803), + } + + return hnsFeatures +} + +func isHNSFeatureSupported(currentVersion HNSVersion, minVersionSupported HNSVersion) bool { + if currentVersion.Major < minVersionSupported.Major { + return false + } + if currentVersion.Major > minVersionSupported.Major { + return true + } + if currentVersion.Minor < minVersionSupported.Minor { + return false + } + return true +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/namespace.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/namespace.go new file mode 100644 index 0000000000..d3b04eefe0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/namespace.go @@ -0,0 +1,111 @@ +package hns + +import ( + "encoding/json" + "fmt" + "os" + "path" + "strings" +) + +type namespaceRequest struct { + IsDefault bool `json:",omitempty"` +} + +type namespaceEndpointRequest struct { + ID string `json:"Id"` +} + +type NamespaceResource struct { + Type string + Data json.RawMessage +} + +type namespaceResourceRequest struct { + Type string + Data interface{} +} + +type Namespace struct { + ID string + IsDefault bool `json:",omitempty"` + ResourceList []NamespaceResource `json:",omitempty"` + CompartmentId uint32 `json:",omitempty"` +} + +func issueNamespaceRequest(id *string, method, subpath string, request interface{}) (*Namespace, error) { + var err error + hnspath := "/namespaces/" + if id != nil { + hnspath = path.Join(hnspath, *id) + } + if subpath != "" { + hnspath = path.Join(hnspath, subpath) + } + var reqJSON []byte + if request != nil { + if reqJSON, err = json.Marshal(request); err != nil { + return nil, err + } + } + var ns Namespace + err = hnsCall(method, hnspath, string(reqJSON), &ns) + if err != nil { + if strings.Contains(err.Error(), "Element not found.") { + return nil, os.ErrNotExist + } + return nil, fmt.Errorf("%s %s: %s", method, hnspath, err) + } + return &ns, err +} + +func CreateNamespace() (string, error) { + req := namespaceRequest{} + ns, err := issueNamespaceRequest(nil, "POST", "", &req) + if err != nil { + return "", err + } + return ns.ID, nil +} + +func RemoveNamespace(id string) error { + _, err := issueNamespaceRequest(&id, "DELETE", "", nil) + return err +} + +func GetNamespaceEndpoints(id string) ([]string, error) { + ns, err := issueNamespaceRequest(&id, "GET", "", nil) + if err != nil { + return nil, err + } + var endpoints []string + for _, rsrc := range ns.ResourceList { + if rsrc.Type == "Endpoint" { + var endpoint namespaceEndpointRequest + err = json.Unmarshal(rsrc.Data, &endpoint) + if err != nil { + return nil, fmt.Errorf("unmarshal endpoint: %s", err) + } + endpoints = append(endpoints, endpoint.ID) + } + } + return endpoints, nil +} + +func AddNamespaceEndpoint(id string, endpointID string) error { + resource := namespaceResourceRequest{ + Type: "Endpoint", + Data: namespaceEndpointRequest{endpointID}, + } + _, err := issueNamespaceRequest(&id, "POST", "addresource", &resource) + return err +} + +func RemoveNamespaceEndpoint(id string, endpointID string) error { + resource := namespaceResourceRequest{ + Type: "Endpoint", + Data: namespaceEndpointRequest{endpointID}, + } + _, err := issueNamespaceRequest(&id, "POST", "removeresource", &resource) + return err +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go new file mode 100644 index 0000000000..204633a488 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go @@ -0,0 +1,76 @@ +// Code generated mksyscall_windows.exe DO NOT EDIT + +package hns + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modvmcompute = windows.NewLazySystemDLL("vmcompute.dll") + + procHNSCall = modvmcompute.NewProc("HNSCall") +) + +func _hnsCall(method string, path string, object string, response **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(method) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + var _p2 *uint16 + _p2, hr = syscall.UTF16PtrFromString(object) + if hr != nil { + return + } + return __hnsCall(_p0, _p1, _p2, response) +} + +func __hnsCall(method *uint16, path *uint16, object *uint16, response **uint16) (hr error) { + if hr = procHNSCall.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHNSCall.Addr(), 4, uintptr(unsafe.Pointer(method)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(object)), uintptr(unsafe.Pointer(response)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go b/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go new file mode 100644 index 0000000000..922f7c679e --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go @@ -0,0 +1,23 @@ +package interop + +import ( + "syscall" + "unsafe" +) + +//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go interop.go + +//sys coTaskMemFree(buffer unsafe.Pointer) = api_ms_win_core_com_l1_1_0.CoTaskMemFree + +func ConvertAndFreeCoTaskMemString(buffer *uint16) string { + str := syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(buffer))[:]) + coTaskMemFree(unsafe.Pointer(buffer)) + return str +} + +func Win32FromHresult(hr uintptr) syscall.Errno { + if hr&0x1fff0000 == 0x00070000 { + return syscall.Errno(hr & 0xffff) + } + return syscall.Errno(hr) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go new file mode 100644 index 0000000000..12b0c71c5a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go @@ -0,0 +1,48 @@ +// Code generated mksyscall_windows.exe DO NOT EDIT + +package interop + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modapi_ms_win_core_com_l1_1_0 = windows.NewLazySystemDLL("api-ms-win-core-com-l1-1-0.dll") + + procCoTaskMemFree = modapi_ms_win_core_com_l1_1_0.NewProc("CoTaskMemFree") +) + +func coTaskMemFree(buffer unsafe.Pointer) { + syscall.Syscall(procCoTaskMemFree.Addr(), 1, uintptr(buffer), 0, 0) + return +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/log/g.go b/vendor/github.com/Microsoft/hcsshim/internal/log/g.go new file mode 100644 index 0000000000..ba6b1a4a53 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/log/g.go @@ -0,0 +1,23 @@ +package log + +import ( + "context" + + "github.com/sirupsen/logrus" + "go.opencensus.io/trace" +) + +// G returns a `logrus.Entry` with the `TraceID, SpanID` from `ctx` if `ctx` +// contains an OpenCensus `trace.Span`. +func G(ctx context.Context) *logrus.Entry { + span := trace.FromContext(ctx) + if span != nil { + sctx := span.SpanContext() + return logrus.WithFields(logrus.Fields{ + "traceID": sctx.TraceID.String(), + "spanID": sctx.SpanID.String(), + // "parentSpanID": TODO: JTERRY75 - Try to convince OC to export this? + }) + } + return logrus.NewEntry(logrus.StandardLogger()) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/logfields/fields.go b/vendor/github.com/Microsoft/hcsshim/internal/logfields/fields.go new file mode 100644 index 0000000000..cf2c166d9b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/logfields/fields.go @@ -0,0 +1,32 @@ +package logfields + +const ( + // Identifiers + + ContainerID = "cid" + UVMID = "uvm-id" + ProcessID = "pid" + + // Common Misc + + // Timeout represents an operation timeout. + Timeout = "timeout" + JSON = "json" + + // Keys/values + + Field = "field" + OCIAnnotation = "oci-annotation" + Value = "value" + + // Golang type's + + ExpectedType = "expected-type" + Bool = "bool" + Uint32 = "uint32" + Uint64 = "uint64" + + // runhcs + + VMShimOperation = "vmshim-op" +) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/longpath/longpath.go b/vendor/github.com/Microsoft/hcsshim/internal/longpath/longpath.go new file mode 100644 index 0000000000..e5b8b85e09 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/longpath/longpath.go @@ -0,0 +1,24 @@ +package longpath + +import ( + "path/filepath" + "strings" +) + +// LongAbs makes a path absolute and returns it in NT long path form. +func LongAbs(path string) (string, error) { + if strings.HasPrefix(path, `\\?\`) || strings.HasPrefix(path, `\\.\`) { + return path, nil + } + if !filepath.IsAbs(path) { + absPath, err := filepath.Abs(path) + if err != nil { + return "", err + } + path = absPath + } + if strings.HasPrefix(path, `\\`) { + return `\\?\UNC\` + path[2:], nil + } + return `\\?\` + path, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/mergemaps/merge.go b/vendor/github.com/Microsoft/hcsshim/internal/mergemaps/merge.go new file mode 100644 index 0000000000..7e95efb30d --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/mergemaps/merge.go @@ -0,0 +1,52 @@ +package mergemaps + +import "encoding/json" + +// Merge recursively merges map `fromMap` into map `ToMap`. Any pre-existing values +// in ToMap are overwritten. Values in fromMap are added to ToMap. +// From http://stackoverflow.com/questions/40491438/merging-two-json-strings-in-golang +func Merge(fromMap, ToMap interface{}) interface{} { + switch fromMap := fromMap.(type) { + case map[string]interface{}: + ToMap, ok := ToMap.(map[string]interface{}) + if !ok { + return fromMap + } + for keyToMap, valueToMap := range ToMap { + if valueFromMap, ok := fromMap[keyToMap]; ok { + fromMap[keyToMap] = Merge(valueFromMap, valueToMap) + } else { + fromMap[keyToMap] = valueToMap + } + } + case nil: + // merge(nil, map[string]interface{...}) -> map[string]interface{...} + ToMap, ok := ToMap.(map[string]interface{}) + if ok { + return ToMap + } + } + return fromMap +} + +// MergeJSON merges the contents of a JSON string into an object representation, +// returning a new object suitable for translating to JSON. +func MergeJSON(object interface{}, additionalJSON []byte) (interface{}, error) { + if len(additionalJSON) == 0 { + return object, nil + } + objectJSON, err := json.Marshal(object) + if err != nil { + return nil, err + } + var objectMap, newMap map[string]interface{} + err = json.Unmarshal(objectJSON, &objectMap) + if err != nil { + return nil, err + } + err = json.Unmarshal(additionalJSON, &newMap) + if err != nil { + return nil, err + } + return Merge(newMap, objectMap), nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/oc/exporter.go b/vendor/github.com/Microsoft/hcsshim/internal/oc/exporter.go new file mode 100644 index 0000000000..f428bdaf72 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/oc/exporter.go @@ -0,0 +1,43 @@ +package oc + +import ( + "github.com/sirupsen/logrus" + "go.opencensus.io/trace" +) + +var _ = (trace.Exporter)(&LogrusExporter{}) + +// LogrusExporter is an OpenCensus `trace.Exporter` that exports +// `trace.SpanData` to logrus output. +type LogrusExporter struct { +} + +// ExportSpan exports `s` based on the the following rules: +// +// 1. All output will contain `s.Attributes`, `s.TraceID`, `s.SpanID`, +// `s.ParentSpanID` for correlation +// +// 2. Any calls to .Annotate will not be supported. +// +// 3. The span itself will be written at `logrus.InfoLevel` unless +// `s.Status.Code != 0` in which case it will be written at `logrus.ErrorLevel` +// providing `s.Status.Message` as the error value. +func (le *LogrusExporter) ExportSpan(s *trace.SpanData) { + // Combine all span annotations with traceID, spanID, parentSpanID + baseEntry := logrus.WithFields(logrus.Fields(s.Attributes)) + baseEntry.Data["traceID"] = s.TraceID.String() + baseEntry.Data["spanID"] = s.SpanID.String() + baseEntry.Data["parentSpanID"] = s.ParentSpanID.String() + baseEntry.Data["startTime"] = s.StartTime + baseEntry.Data["endTime"] = s.EndTime + baseEntry.Data["duration"] = s.EndTime.Sub(s.StartTime).String() + baseEntry.Data["name"] = s.Name + baseEntry.Time = s.StartTime + + level := logrus.InfoLevel + if s.Status.Code != 0 { + level = logrus.ErrorLevel + baseEntry.Data[logrus.ErrorKey] = s.Status.Message + } + baseEntry.Log(level, "Span") +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/oc/span.go b/vendor/github.com/Microsoft/hcsshim/internal/oc/span.go new file mode 100644 index 0000000000..fee4765cbc --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/oc/span.go @@ -0,0 +1,17 @@ +package oc + +import ( + "go.opencensus.io/trace" +) + +// SetSpanStatus sets `span.SetStatus` to the proper status depending on `err`. If +// `err` is `nil` assumes `trace.StatusCodeOk`. +func SetSpanStatus(span *trace.Span, err error) { + status := trace.Status{} + if err != nil { + // TODO: JTERRY75 - Handle errors in a non-generic way + status.Code = trace.StatusCodeUnknown + status.Message = err.Error() + } + span.SetStatus(status) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/safefile/safeopen.go b/vendor/github.com/Microsoft/hcsshim/internal/safefile/safeopen.go new file mode 100644 index 0000000000..66b8d7e035 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/safefile/safeopen.go @@ -0,0 +1,375 @@ +package safefile + +import ( + "errors" + "io" + "os" + "path/filepath" + "strings" + "syscall" + "unicode/utf16" + "unsafe" + + "github.com/Microsoft/hcsshim/internal/longpath" + "github.com/Microsoft/hcsshim/internal/winapi" + + winio "github.com/Microsoft/go-winio" +) + +func OpenRoot(path string) (*os.File, error) { + longpath, err := longpath.LongAbs(path) + if err != nil { + return nil, err + } + return winio.OpenForBackup(longpath, syscall.GENERIC_READ, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, syscall.OPEN_EXISTING) +} + +func cleanGoStringRelativePath(path string) (string, error) { + path = filepath.Clean(path) + if strings.Contains(path, ":") { + // Since alternate data streams must follow the file they + // are attached to, finding one here (out of order) is invalid. + return "", errors.New("path contains invalid character `:`") + } + fspath := filepath.FromSlash(path) + if len(fspath) > 0 && fspath[0] == '\\' { + return "", errors.New("expected relative path") + } + return fspath, nil +} + +func ntRelativePath(path string) ([]uint16, error) { + fspath, err := cleanGoStringRelativePath(path) + if err != nil { + return nil, err + } + + path16 := utf16.Encode(([]rune)(fspath)) + if len(path16) > 32767 { + return nil, syscall.ENAMETOOLONG + } + + return path16, nil +} + +// openRelativeInternal opens a relative path from the given root, failing if +// any of the intermediate path components are reparse points. +func openRelativeInternal(path string, root *os.File, accessMask uint32, shareFlags uint32, createDisposition uint32, flags uint32) (*os.File, error) { + var ( + h uintptr + iosb winapi.IOStatusBlock + oa winapi.ObjectAttributes + ) + + cleanRelativePath, err := cleanGoStringRelativePath(path) + if err != nil { + return nil, err + } + + if root == nil || root.Fd() == 0 { + return nil, errors.New("missing root directory") + } + + pathUnicode, err := winapi.NewUnicodeString(cleanRelativePath) + if err != nil { + return nil, err + } + + oa.Length = unsafe.Sizeof(oa) + oa.ObjectName = pathUnicode + oa.RootDirectory = uintptr(root.Fd()) + oa.Attributes = winapi.OBJ_DONT_REPARSE + status := winapi.NtCreateFile( + &h, + accessMask|syscall.SYNCHRONIZE, + &oa, + &iosb, + nil, + 0, + shareFlags, + createDisposition, + winapi.FILE_OPEN_FOR_BACKUP_INTENT|winapi.FILE_SYNCHRONOUS_IO_NONALERT|flags, + nil, + 0, + ) + if status != 0 { + return nil, winapi.RtlNtStatusToDosError(status) + } + + fullPath, err := longpath.LongAbs(filepath.Join(root.Name(), path)) + if err != nil { + syscall.Close(syscall.Handle(h)) + return nil, err + } + + return os.NewFile(h, fullPath), nil +} + +// OpenRelative opens a relative path from the given root, failing if +// any of the intermediate path components are reparse points. +func OpenRelative(path string, root *os.File, accessMask uint32, shareFlags uint32, createDisposition uint32, flags uint32) (*os.File, error) { + f, err := openRelativeInternal(path, root, accessMask, shareFlags, createDisposition, flags) + if err != nil { + err = &os.PathError{Op: "open", Path: filepath.Join(root.Name(), path), Err: err} + } + return f, err +} + +// LinkRelative creates a hard link from oldname to newname (relative to oldroot +// and newroot), failing if any of the intermediate path components are reparse +// points. +func LinkRelative(oldname string, oldroot *os.File, newname string, newroot *os.File) error { + // Open the old file. + oldf, err := openRelativeInternal( + oldname, + oldroot, + syscall.FILE_WRITE_ATTRIBUTES, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + winapi.FILE_OPEN, + 0, + ) + if err != nil { + return &os.LinkError{Op: "link", Old: filepath.Join(oldroot.Name(), oldname), New: filepath.Join(newroot.Name(), newname), Err: err} + } + defer oldf.Close() + + // Open the parent of the new file. + var parent *os.File + parentPath := filepath.Dir(newname) + if parentPath != "." { + parent, err = openRelativeInternal( + parentPath, + newroot, + syscall.GENERIC_READ, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + winapi.FILE_OPEN, + winapi.FILE_DIRECTORY_FILE) + if err != nil { + return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: err} + } + defer parent.Close() + + fi, err := winio.GetFileBasicInfo(parent) + if err != nil { + return err + } + if (fi.FileAttributes & syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 { + return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: winapi.RtlNtStatusToDosError(winapi.STATUS_REPARSE_POINT_ENCOUNTERED)} + } + + } else { + parent = newroot + } + + // Issue an NT call to create the link. This will be safe because NT will + // not open any more directories to create the link, so it cannot walk any + // more reparse points. + newbase := filepath.Base(newname) + newbase16, err := ntRelativePath(newbase) + if err != nil { + return err + } + + size := int(unsafe.Offsetof(winapi.FileLinkInformation{}.FileName)) + len(newbase16)*2 + linkinfoBuffer := winapi.LocalAlloc(0, size) + defer winapi.LocalFree(linkinfoBuffer) + + linkinfo := (*winapi.FileLinkInformation)(unsafe.Pointer(linkinfoBuffer)) + linkinfo.RootDirectory = parent.Fd() + linkinfo.FileNameLength = uint32(len(newbase16) * 2) + copy(winapi.Uint16BufferToSlice(&linkinfo.FileName[0], len(newbase16)), newbase16) + + var iosb winapi.IOStatusBlock + status := winapi.NtSetInformationFile( + oldf.Fd(), + &iosb, + linkinfoBuffer, + uint32(size), + winapi.FileLinkInformationClass, + ) + if status != 0 { + return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(parent.Name(), newbase), Err: winapi.RtlNtStatusToDosError(status)} + } + + return nil +} + +// deleteOnClose marks a file to be deleted when the handle is closed. +func deleteOnClose(f *os.File) error { + disposition := winapi.FileDispositionInformationEx{Flags: winapi.FILE_DISPOSITION_DELETE} + var iosb winapi.IOStatusBlock + status := winapi.NtSetInformationFile( + f.Fd(), + &iosb, + uintptr(unsafe.Pointer(&disposition)), + uint32(unsafe.Sizeof(disposition)), + winapi.FileDispositionInformationExClass, + ) + if status != 0 { + return winapi.RtlNtStatusToDosError(status) + } + return nil +} + +// clearReadOnly clears the readonly attribute on a file. +func clearReadOnly(f *os.File) error { + bi, err := winio.GetFileBasicInfo(f) + if err != nil { + return err + } + if bi.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY == 0 { + return nil + } + sbi := winio.FileBasicInfo{ + FileAttributes: bi.FileAttributes &^ syscall.FILE_ATTRIBUTE_READONLY, + } + if sbi.FileAttributes == 0 { + sbi.FileAttributes = syscall.FILE_ATTRIBUTE_NORMAL + } + return winio.SetFileBasicInfo(f, &sbi) +} + +// RemoveRelative removes a file or directory relative to a root, failing if any +// intermediate path components are reparse points. +func RemoveRelative(path string, root *os.File) error { + f, err := openRelativeInternal( + path, + root, + winapi.FILE_READ_ATTRIBUTES|winapi.FILE_WRITE_ATTRIBUTES|winapi.DELETE, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + winapi.FILE_OPEN, + winapi.FILE_OPEN_REPARSE_POINT) + if err == nil { + defer f.Close() + err = deleteOnClose(f) + if err == syscall.ERROR_ACCESS_DENIED { + // Maybe the file is marked readonly. Clear the bit and retry. + _ = clearReadOnly(f) + err = deleteOnClose(f) + } + } + if err != nil { + return &os.PathError{Op: "remove", Path: filepath.Join(root.Name(), path), Err: err} + } + return nil +} + +// RemoveAllRelative removes a directory tree relative to a root, failing if any +// intermediate path components are reparse points. +func RemoveAllRelative(path string, root *os.File) error { + fi, err := LstatRelative(path, root) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + fileAttributes := fi.Sys().(*syscall.Win32FileAttributeData).FileAttributes + if fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 || fileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 { + // If this is a reparse point, it can't have children. Simple remove will do. + err := RemoveRelative(path, root) + if err == nil || os.IsNotExist(err) { + return nil + } + return err + } + + // It is necessary to use os.Open as Readdirnames does not work with + // OpenRelative. This is safe because the above lstatrelative fails + // if the target is outside the root, and we know this is not a + // symlink from the above FILE_ATTRIBUTE_REPARSE_POINT check. + fd, err := os.Open(filepath.Join(root.Name(), path)) + if err != nil { + if os.IsNotExist(err) { + // Race. It was deleted between the Lstat and Open. + // Return nil per RemoveAll's docs. + return nil + } + return err + } + + // Remove contents & return first error. + for { + names, err1 := fd.Readdirnames(100) + for _, name := range names { + err1 := RemoveAllRelative(path+string(os.PathSeparator)+name, root) + if err == nil { + err = err1 + } + } + if err1 == io.EOF { + break + } + // If Readdirnames returned an error, use it. + if err == nil { + err = err1 + } + if len(names) == 0 { + break + } + } + fd.Close() + + // Remove directory. + err1 := RemoveRelative(path, root) + if err1 == nil || os.IsNotExist(err1) { + return nil + } + if err == nil { + err = err1 + } + return err +} + +// MkdirRelative creates a directory relative to a root, failing if any +// intermediate path components are reparse points. +func MkdirRelative(path string, root *os.File) error { + f, err := openRelativeInternal( + path, + root, + 0, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + winapi.FILE_CREATE, + winapi.FILE_DIRECTORY_FILE) + if err == nil { + f.Close() + } else { + err = &os.PathError{Op: "mkdir", Path: filepath.Join(root.Name(), path), Err: err} + } + return err +} + +// LstatRelative performs a stat operation on a file relative to a root, failing +// if any intermediate path components are reparse points. +func LstatRelative(path string, root *os.File) (os.FileInfo, error) { + f, err := openRelativeInternal( + path, + root, + winapi.FILE_READ_ATTRIBUTES, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + winapi.FILE_OPEN, + winapi.FILE_OPEN_REPARSE_POINT) + if err != nil { + return nil, &os.PathError{Op: "stat", Path: filepath.Join(root.Name(), path), Err: err} + } + defer f.Close() + return f.Stat() +} + +// EnsureNotReparsePointRelative validates that a given file (relative to a +// root) and all intermediate path components are not a reparse points. +func EnsureNotReparsePointRelative(path string, root *os.File) error { + // Perform an open with OBJ_DONT_REPARSE but without specifying FILE_OPEN_REPARSE_POINT. + f, err := OpenRelative( + path, + root, + 0, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + winapi.FILE_OPEN, + 0) + if err != nil { + return err + } + f.Close() + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/timeout/timeout.go b/vendor/github.com/Microsoft/hcsshim/internal/timeout/timeout.go new file mode 100644 index 0000000000..eaf39fa513 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/timeout/timeout.go @@ -0,0 +1,74 @@ +package timeout + +import ( + "os" + "strconv" + "time" +) + +var ( + // defaultTimeout is the timeout for most operations that is not overridden. + defaultTimeout = 4 * time.Minute + + // defaultTimeoutTestdRetry is the retry loop timeout for testd to respond + // for a disk to come online in LCOW. + defaultTimeoutTestdRetry = 5 * time.Second +) + +// External variables for HCSShim consumers to use. +var ( + // SystemCreate is the timeout for creating a compute system + SystemCreate time.Duration = defaultTimeout + + // SystemStart is the timeout for starting a compute system + SystemStart time.Duration = defaultTimeout + + // SystemPause is the timeout for pausing a compute system + SystemPause time.Duration = defaultTimeout + + // SystemResume is the timeout for resuming a compute system + SystemResume time.Duration = defaultTimeout + + // SystemSave is the timeout for saving a compute system + SystemSave time.Duration = defaultTimeout + + // SyscallWatcher is the timeout before warning of a potential stuck platform syscall. + SyscallWatcher time.Duration = defaultTimeout + + // Tar2VHD is the timeout for the tar2vhd operation to complete + Tar2VHD time.Duration = defaultTimeout + + // ExternalCommandToStart is the timeout for external commands to start + ExternalCommandToStart = defaultTimeout + + // ExternalCommandToComplete is the timeout for external commands to complete. + // Generally this means copying data from their stdio pipes. + ExternalCommandToComplete = defaultTimeout + + // TestDRetryLoop is the timeout for testd retry loop when onlining a SCSI disk in LCOW + TestDRetryLoop = defaultTimeoutTestdRetry +) + +func init() { + SystemCreate = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMCREATE", SystemCreate) + SystemStart = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMSTART", SystemStart) + SystemPause = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMPAUSE", SystemPause) + SystemResume = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMRESUME", SystemResume) + SystemSave = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMSAVE", SystemSave) + SyscallWatcher = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSCALLWATCHER", SyscallWatcher) + Tar2VHD = durationFromEnvironment("HCSSHIM_TIMEOUT_TAR2VHD", Tar2VHD) + ExternalCommandToStart = durationFromEnvironment("HCSSHIM_TIMEOUT_EXTERNALCOMMANDSTART", ExternalCommandToStart) + ExternalCommandToComplete = durationFromEnvironment("HCSSHIM_TIMEOUT_EXTERNALCOMMANDCOMPLETE", ExternalCommandToComplete) + TestDRetryLoop = durationFromEnvironment("HCSSHIM_TIMEOUT_TESTDRETRYLOOP", TestDRetryLoop) +} + +func durationFromEnvironment(env string, defaultValue time.Duration) time.Duration { + envTimeout := os.Getenv(env) + if len(envTimeout) > 0 { + e, err := strconv.Atoi(envTimeout) + if err == nil && e > 0 { + return time.Second * time.Duration(e) + } + } + return defaultValue +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/vmcompute/vmcompute.go b/vendor/github.com/Microsoft/hcsshim/internal/vmcompute/vmcompute.go new file mode 100644 index 0000000000..e7f114b67a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/vmcompute/vmcompute.go @@ -0,0 +1,610 @@ +package vmcompute + +import ( + gcontext "context" + "syscall" + "time" + + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/Microsoft/hcsshim/internal/log" + "github.com/Microsoft/hcsshim/internal/logfields" + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/Microsoft/hcsshim/internal/timeout" + "go.opencensus.io/trace" +) + +//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go vmcompute.go + +//sys hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) = vmcompute.HcsEnumerateComputeSystems? +//sys hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *HcsSystem, result **uint16) (hr error) = vmcompute.HcsCreateComputeSystem? +//sys hcsOpenComputeSystem(id string, computeSystem *HcsSystem, result **uint16) (hr error) = vmcompute.HcsOpenComputeSystem? +//sys hcsCloseComputeSystem(computeSystem HcsSystem) (hr error) = vmcompute.HcsCloseComputeSystem? +//sys hcsStartComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsStartComputeSystem? +//sys hcsShutdownComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsShutdownComputeSystem? +//sys hcsTerminateComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsTerminateComputeSystem? +//sys hcsPauseComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsPauseComputeSystem? +//sys hcsResumeComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsResumeComputeSystem? +//sys hcsGetComputeSystemProperties(computeSystem HcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetComputeSystemProperties? +//sys hcsModifyComputeSystem(computeSystem HcsSystem, configuration string, result **uint16) (hr error) = vmcompute.HcsModifyComputeSystem? +//sys hcsModifyServiceSettings(settings string, result **uint16) (hr error) = vmcompute.HcsModifyServiceSettings? +//sys hcsRegisterComputeSystemCallback(computeSystem HcsSystem, callback uintptr, context uintptr, callbackHandle *HcsCallback) (hr error) = vmcompute.HcsRegisterComputeSystemCallback? +//sys hcsUnregisterComputeSystemCallback(callbackHandle HcsCallback) (hr error) = vmcompute.HcsUnregisterComputeSystemCallback? +//sys hcsSaveComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsSaveComputeSystem? + +//sys hcsCreateProcess(computeSystem HcsSystem, processParameters string, processInformation *HcsProcessInformation, process *HcsProcess, result **uint16) (hr error) = vmcompute.HcsCreateProcess? +//sys hcsOpenProcess(computeSystem HcsSystem, pid uint32, process *HcsProcess, result **uint16) (hr error) = vmcompute.HcsOpenProcess? +//sys hcsCloseProcess(process HcsProcess) (hr error) = vmcompute.HcsCloseProcess? +//sys hcsTerminateProcess(process HcsProcess, result **uint16) (hr error) = vmcompute.HcsTerminateProcess? +//sys hcsSignalProcess(process HcsProcess, options string, result **uint16) (hr error) = vmcompute.HcsSignalProcess? +//sys hcsGetProcessInfo(process HcsProcess, processInformation *HcsProcessInformation, result **uint16) (hr error) = vmcompute.HcsGetProcessInfo? +//sys hcsGetProcessProperties(process HcsProcess, processProperties **uint16, result **uint16) (hr error) = vmcompute.HcsGetProcessProperties? +//sys hcsModifyProcess(process HcsProcess, settings string, result **uint16) (hr error) = vmcompute.HcsModifyProcess? +//sys hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetServiceProperties? +//sys hcsRegisterProcessCallback(process HcsProcess, callback uintptr, context uintptr, callbackHandle *HcsCallback) (hr error) = vmcompute.HcsRegisterProcessCallback? +//sys hcsUnregisterProcessCallback(callbackHandle HcsCallback) (hr error) = vmcompute.HcsUnregisterProcessCallback? + +// errVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously +const errVmcomputeOperationPending = syscall.Errno(0xC0370103) + +// HcsSystem is the handle associated with a created compute system. +type HcsSystem syscall.Handle + +// HcsProcess is the handle associated with a created process in a compute +// system. +type HcsProcess syscall.Handle + +// HcsCallback is the handle associated with the function to call when events +// occur. +type HcsCallback syscall.Handle + +// HcsProcessInformation is the structure used when creating or getting process +// info. +type HcsProcessInformation struct { + // ProcessId is the pid of the created process. + ProcessId uint32 + reserved uint32 //nolint:structcheck + // StdInput is the handle associated with the stdin of the process. + StdInput syscall.Handle + // StdOutput is the handle associated with the stdout of the process. + StdOutput syscall.Handle + // StdError is the handle associated with the stderr of the process. + StdError syscall.Handle +} + +func execute(ctx gcontext.Context, timeout time.Duration, f func() error) error { + if timeout > 0 { + var cancel gcontext.CancelFunc + ctx, cancel = gcontext.WithTimeout(ctx, timeout) + defer cancel() + } + + done := make(chan error, 1) + go func() { + done <- f() + }() + select { + case <-ctx.Done(): + if ctx.Err() == gcontext.DeadlineExceeded { + log.G(ctx).WithField(logfields.Timeout, timeout). + Warning("Syscall did not complete within operation timeout. This may indicate a platform issue. If it appears to be making no forward progress, obtain the stacks and see if there is a syscall stuck in the platform API for a significant length of time.") + } + return ctx.Err() + case err := <-done: + return err + } +} + +func HcsEnumerateComputeSystems(ctx gcontext.Context, query string) (computeSystems, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsEnumerateComputeSystems") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.StringAttribute("query", query)) + + return computeSystems, result, execute(ctx, timeout.SyscallWatcher, func() error { + var ( + computeSystemsp *uint16 + resultp *uint16 + ) + err := hcsEnumerateComputeSystems(query, &computeSystemsp, &resultp) + if computeSystemsp != nil { + computeSystems = interop.ConvertAndFreeCoTaskMemString(computeSystemsp) + } + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsCreateComputeSystem(ctx gcontext.Context, id string, configuration string, identity syscall.Handle) (computeSystem HcsSystem, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsCreateComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + if hr != errVmcomputeOperationPending { + oc.SetSpanStatus(span, hr) + } + }() + span.AddAttributes( + trace.StringAttribute("id", id), + trace.StringAttribute("configuration", configuration)) + + return computeSystem, result, execute(ctx, timeout.SystemCreate, func() error { + var resultp *uint16 + err := hcsCreateComputeSystem(id, configuration, identity, &computeSystem, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsOpenComputeSystem(ctx gcontext.Context, id string) (computeSystem HcsSystem, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsOpenComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + + return computeSystem, result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsOpenComputeSystem(id, &computeSystem, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsCloseComputeSystem(ctx gcontext.Context, computeSystem HcsSystem) (hr error) { + ctx, span := trace.StartSpan(ctx, "HcsCloseComputeSystem") + defer span.End() + defer func() { oc.SetSpanStatus(span, hr) }() + + return execute(ctx, timeout.SyscallWatcher, func() error { + return hcsCloseComputeSystem(computeSystem) + }) +} + +func HcsStartComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsStartComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + if hr != errVmcomputeOperationPending { + oc.SetSpanStatus(span, hr) + } + }() + span.AddAttributes(trace.StringAttribute("options", options)) + + return result, execute(ctx, timeout.SystemStart, func() error { + var resultp *uint16 + err := hcsStartComputeSystem(computeSystem, options, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsShutdownComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsShutdownComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + if hr != errVmcomputeOperationPending { + oc.SetSpanStatus(span, hr) + } + }() + span.AddAttributes(trace.StringAttribute("options", options)) + + return result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsShutdownComputeSystem(computeSystem, options, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsTerminateComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsTerminateComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + if hr != errVmcomputeOperationPending { + oc.SetSpanStatus(span, hr) + } + }() + span.AddAttributes(trace.StringAttribute("options", options)) + + return result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsTerminateComputeSystem(computeSystem, options, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsPauseComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsPauseComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + if hr != errVmcomputeOperationPending { + oc.SetSpanStatus(span, hr) + } + }() + span.AddAttributes(trace.StringAttribute("options", options)) + + return result, execute(ctx, timeout.SystemPause, func() error { + var resultp *uint16 + err := hcsPauseComputeSystem(computeSystem, options, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsResumeComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsResumeComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + if hr != errVmcomputeOperationPending { + oc.SetSpanStatus(span, hr) + } + }() + span.AddAttributes(trace.StringAttribute("options", options)) + + return result, execute(ctx, timeout.SystemResume, func() error { + var resultp *uint16 + err := hcsResumeComputeSystem(computeSystem, options, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsGetComputeSystemProperties(ctx gcontext.Context, computeSystem HcsSystem, propertyQuery string) (properties, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsGetComputeSystemProperties") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.StringAttribute("propertyQuery", propertyQuery)) + + return properties, result, execute(ctx, timeout.SyscallWatcher, func() error { + var ( + propertiesp *uint16 + resultp *uint16 + ) + err := hcsGetComputeSystemProperties(computeSystem, propertyQuery, &propertiesp, &resultp) + if propertiesp != nil { + properties = interop.ConvertAndFreeCoTaskMemString(propertiesp) + } + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsModifyComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, configuration string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsModifyComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.StringAttribute("configuration", configuration)) + + return result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsModifyComputeSystem(computeSystem, configuration, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsModifyServiceSettings(ctx gcontext.Context, settings string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsModifyServiceSettings") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.StringAttribute("settings", settings)) + + return result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsModifyServiceSettings(settings, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsRegisterComputeSystemCallback(ctx gcontext.Context, computeSystem HcsSystem, callback uintptr, context uintptr) (callbackHandle HcsCallback, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsRegisterComputeSystemCallback") + defer span.End() + defer func() { oc.SetSpanStatus(span, hr) }() + + return callbackHandle, execute(ctx, timeout.SyscallWatcher, func() error { + return hcsRegisterComputeSystemCallback(computeSystem, callback, context, &callbackHandle) + }) +} + +func HcsUnregisterComputeSystemCallback(ctx gcontext.Context, callbackHandle HcsCallback) (hr error) { + ctx, span := trace.StartSpan(ctx, "HcsUnregisterComputeSystemCallback") + defer span.End() + defer func() { oc.SetSpanStatus(span, hr) }() + + return execute(ctx, timeout.SyscallWatcher, func() error { + return hcsUnregisterComputeSystemCallback(callbackHandle) + }) +} + +func HcsCreateProcess(ctx gcontext.Context, computeSystem HcsSystem, processParameters string) (processInformation HcsProcessInformation, process HcsProcess, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsCreateProcess") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.StringAttribute("processParameters", processParameters)) + + return processInformation, process, result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsCreateProcess(computeSystem, processParameters, &processInformation, &process, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsOpenProcess(ctx gcontext.Context, computeSystem HcsSystem, pid uint32) (process HcsProcess, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsOpenProcess") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.Int64Attribute("pid", int64(pid))) + + return process, result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsOpenProcess(computeSystem, pid, &process, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsCloseProcess(ctx gcontext.Context, process HcsProcess) (hr error) { + ctx, span := trace.StartSpan(ctx, "HcsCloseProcess") + defer span.End() + defer func() { oc.SetSpanStatus(span, hr) }() + + return execute(ctx, timeout.SyscallWatcher, func() error { + return hcsCloseProcess(process) + }) +} + +func HcsTerminateProcess(ctx gcontext.Context, process HcsProcess) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsTerminateProcess") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + + return result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsTerminateProcess(process, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsSignalProcess(ctx gcontext.Context, process HcsProcess, options string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsSignalProcess") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.StringAttribute("options", options)) + + return result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsSignalProcess(process, options, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsGetProcessInfo(ctx gcontext.Context, process HcsProcess) (processInformation HcsProcessInformation, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsGetProcessInfo") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + + return processInformation, result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsGetProcessInfo(process, &processInformation, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsGetProcessProperties(ctx gcontext.Context, process HcsProcess) (processProperties, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsGetProcessProperties") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + + return processProperties, result, execute(ctx, timeout.SyscallWatcher, func() error { + var ( + processPropertiesp *uint16 + resultp *uint16 + ) + err := hcsGetProcessProperties(process, &processPropertiesp, &resultp) + if processPropertiesp != nil { + processProperties = interop.ConvertAndFreeCoTaskMemString(processPropertiesp) + } + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsModifyProcess(ctx gcontext.Context, process HcsProcess, settings string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsModifyProcess") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.StringAttribute("settings", settings)) + + return result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsModifyProcess(process, settings, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsGetServiceProperties(ctx gcontext.Context, propertyQuery string) (properties, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsGetServiceProperties") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.StringAttribute("propertyQuery", propertyQuery)) + + return properties, result, execute(ctx, timeout.SyscallWatcher, func() error { + var ( + propertiesp *uint16 + resultp *uint16 + ) + err := hcsGetServiceProperties(propertyQuery, &propertiesp, &resultp) + if propertiesp != nil { + properties = interop.ConvertAndFreeCoTaskMemString(propertiesp) + } + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsRegisterProcessCallback(ctx gcontext.Context, process HcsProcess, callback uintptr, context uintptr) (callbackHandle HcsCallback, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsRegisterProcessCallback") + defer span.End() + defer func() { oc.SetSpanStatus(span, hr) }() + + return callbackHandle, execute(ctx, timeout.SyscallWatcher, func() error { + return hcsRegisterProcessCallback(process, callback, context, &callbackHandle) + }) +} + +func HcsUnregisterProcessCallback(ctx gcontext.Context, callbackHandle HcsCallback) (hr error) { + ctx, span := trace.StartSpan(ctx, "HcsUnregisterProcessCallback") + defer span.End() + defer func() { oc.SetSpanStatus(span, hr) }() + + return execute(ctx, timeout.SyscallWatcher, func() error { + return hcsUnregisterProcessCallback(callbackHandle) + }) +} + +func HcsSaveComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsSaveComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + if hr != errVmcomputeOperationPending { + oc.SetSpanStatus(span, hr) + } + }() + + return result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsSaveComputeSystem(computeSystem, options, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/vmcompute/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/vmcompute/zsyscall_windows.go new file mode 100644 index 0000000000..cae55058de --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/vmcompute/zsyscall_windows.go @@ -0,0 +1,581 @@ +// Code generated mksyscall_windows.exe DO NOT EDIT + +package vmcompute + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modvmcompute = windows.NewLazySystemDLL("vmcompute.dll") + + procHcsEnumerateComputeSystems = modvmcompute.NewProc("HcsEnumerateComputeSystems") + procHcsCreateComputeSystem = modvmcompute.NewProc("HcsCreateComputeSystem") + procHcsOpenComputeSystem = modvmcompute.NewProc("HcsOpenComputeSystem") + procHcsCloseComputeSystem = modvmcompute.NewProc("HcsCloseComputeSystem") + procHcsStartComputeSystem = modvmcompute.NewProc("HcsStartComputeSystem") + procHcsShutdownComputeSystem = modvmcompute.NewProc("HcsShutdownComputeSystem") + procHcsTerminateComputeSystem = modvmcompute.NewProc("HcsTerminateComputeSystem") + procHcsPauseComputeSystem = modvmcompute.NewProc("HcsPauseComputeSystem") + procHcsResumeComputeSystem = modvmcompute.NewProc("HcsResumeComputeSystem") + procHcsGetComputeSystemProperties = modvmcompute.NewProc("HcsGetComputeSystemProperties") + procHcsModifyComputeSystem = modvmcompute.NewProc("HcsModifyComputeSystem") + procHcsModifyServiceSettings = modvmcompute.NewProc("HcsModifyServiceSettings") + procHcsRegisterComputeSystemCallback = modvmcompute.NewProc("HcsRegisterComputeSystemCallback") + procHcsUnregisterComputeSystemCallback = modvmcompute.NewProc("HcsUnregisterComputeSystemCallback") + procHcsSaveComputeSystem = modvmcompute.NewProc("HcsSaveComputeSystem") + procHcsCreateProcess = modvmcompute.NewProc("HcsCreateProcess") + procHcsOpenProcess = modvmcompute.NewProc("HcsOpenProcess") + procHcsCloseProcess = modvmcompute.NewProc("HcsCloseProcess") + procHcsTerminateProcess = modvmcompute.NewProc("HcsTerminateProcess") + procHcsSignalProcess = modvmcompute.NewProc("HcsSignalProcess") + procHcsGetProcessInfo = modvmcompute.NewProc("HcsGetProcessInfo") + procHcsGetProcessProperties = modvmcompute.NewProc("HcsGetProcessProperties") + procHcsModifyProcess = modvmcompute.NewProc("HcsModifyProcess") + procHcsGetServiceProperties = modvmcompute.NewProc("HcsGetServiceProperties") + procHcsRegisterProcessCallback = modvmcompute.NewProc("HcsRegisterProcessCallback") + procHcsUnregisterProcessCallback = modvmcompute.NewProc("HcsUnregisterProcessCallback") +) + +func hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(query) + if hr != nil { + return + } + return _hcsEnumerateComputeSystems(_p0, computeSystems, result) +} + +func _hcsEnumerateComputeSystems(query *uint16, computeSystems **uint16, result **uint16) (hr error) { + if hr = procHcsEnumerateComputeSystems.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsEnumerateComputeSystems.Addr(), 3, uintptr(unsafe.Pointer(query)), uintptr(unsafe.Pointer(computeSystems)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *HcsSystem, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(configuration) + if hr != nil { + return + } + return _hcsCreateComputeSystem(_p0, _p1, identity, computeSystem, result) +} + +func _hcsCreateComputeSystem(id *uint16, configuration *uint16, identity syscall.Handle, computeSystem *HcsSystem, result **uint16) (hr error) { + if hr = procHcsCreateComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsCreateComputeSystem.Addr(), 5, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(configuration)), uintptr(identity), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsOpenComputeSystem(id string, computeSystem *HcsSystem, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _hcsOpenComputeSystem(_p0, computeSystem, result) +} + +func _hcsOpenComputeSystem(id *uint16, computeSystem *HcsSystem, result **uint16) (hr error) { + if hr = procHcsOpenComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsOpenComputeSystem.Addr(), 3, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsCloseComputeSystem(computeSystem HcsSystem) (hr error) { + if hr = procHcsCloseComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsCloseComputeSystem.Addr(), 1, uintptr(computeSystem), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsStartComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsStartComputeSystem(computeSystem, _p0, result) +} + +func _hcsStartComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsStartComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsStartComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsShutdownComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsShutdownComputeSystem(computeSystem, _p0, result) +} + +func _hcsShutdownComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsShutdownComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsShutdownComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsTerminateComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsTerminateComputeSystem(computeSystem, _p0, result) +} + +func _hcsTerminateComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsTerminateComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsTerminateComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsPauseComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsPauseComputeSystem(computeSystem, _p0, result) +} + +func _hcsPauseComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsPauseComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsPauseComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsResumeComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsResumeComputeSystem(computeSystem, _p0, result) +} + +func _hcsResumeComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsResumeComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsResumeComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsGetComputeSystemProperties(computeSystem HcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(propertyQuery) + if hr != nil { + return + } + return _hcsGetComputeSystemProperties(computeSystem, _p0, properties, result) +} + +func _hcsGetComputeSystemProperties(computeSystem HcsSystem, propertyQuery *uint16, properties **uint16, result **uint16) (hr error) { + if hr = procHcsGetComputeSystemProperties.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsGetComputeSystemProperties.Addr(), 4, uintptr(computeSystem), uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsModifyComputeSystem(computeSystem HcsSystem, configuration string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(configuration) + if hr != nil { + return + } + return _hcsModifyComputeSystem(computeSystem, _p0, result) +} + +func _hcsModifyComputeSystem(computeSystem HcsSystem, configuration *uint16, result **uint16) (hr error) { + if hr = procHcsModifyComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsModifyComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(configuration)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsModifyServiceSettings(settings string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(settings) + if hr != nil { + return + } + return _hcsModifyServiceSettings(_p0, result) +} + +func _hcsModifyServiceSettings(settings *uint16, result **uint16) (hr error) { + if hr = procHcsModifyServiceSettings.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsModifyServiceSettings.Addr(), 2, uintptr(unsafe.Pointer(settings)), uintptr(unsafe.Pointer(result)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsRegisterComputeSystemCallback(computeSystem HcsSystem, callback uintptr, context uintptr, callbackHandle *HcsCallback) (hr error) { + if hr = procHcsRegisterComputeSystemCallback.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsRegisterComputeSystemCallback.Addr(), 4, uintptr(computeSystem), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsUnregisterComputeSystemCallback(callbackHandle HcsCallback) (hr error) { + if hr = procHcsUnregisterComputeSystemCallback.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsUnregisterComputeSystemCallback.Addr(), 1, uintptr(callbackHandle), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsSaveComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsSaveComputeSystem(computeSystem, _p0, result) +} + +func _hcsSaveComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsSaveComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsSaveComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsCreateProcess(computeSystem HcsSystem, processParameters string, processInformation *HcsProcessInformation, process *HcsProcess, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(processParameters) + if hr != nil { + return + } + return _hcsCreateProcess(computeSystem, _p0, processInformation, process, result) +} + +func _hcsCreateProcess(computeSystem HcsSystem, processParameters *uint16, processInformation *HcsProcessInformation, process *HcsProcess, result **uint16) (hr error) { + if hr = procHcsCreateProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsCreateProcess.Addr(), 5, uintptr(computeSystem), uintptr(unsafe.Pointer(processParameters)), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(process)), uintptr(unsafe.Pointer(result)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsOpenProcess(computeSystem HcsSystem, pid uint32, process *HcsProcess, result **uint16) (hr error) { + if hr = procHcsOpenProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsOpenProcess.Addr(), 4, uintptr(computeSystem), uintptr(pid), uintptr(unsafe.Pointer(process)), uintptr(unsafe.Pointer(result)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsCloseProcess(process HcsProcess) (hr error) { + if hr = procHcsCloseProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsCloseProcess.Addr(), 1, uintptr(process), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsTerminateProcess(process HcsProcess, result **uint16) (hr error) { + if hr = procHcsTerminateProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsTerminateProcess.Addr(), 2, uintptr(process), uintptr(unsafe.Pointer(result)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsSignalProcess(process HcsProcess, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsSignalProcess(process, _p0, result) +} + +func _hcsSignalProcess(process HcsProcess, options *uint16, result **uint16) (hr error) { + if hr = procHcsSignalProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsSignalProcess.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsGetProcessInfo(process HcsProcess, processInformation *HcsProcessInformation, result **uint16) (hr error) { + if hr = procHcsGetProcessInfo.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsGetProcessInfo.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsGetProcessProperties(process HcsProcess, processProperties **uint16, result **uint16) (hr error) { + if hr = procHcsGetProcessProperties.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsGetProcessProperties.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processProperties)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsModifyProcess(process HcsProcess, settings string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(settings) + if hr != nil { + return + } + return _hcsModifyProcess(process, _p0, result) +} + +func _hcsModifyProcess(process HcsProcess, settings *uint16, result **uint16) (hr error) { + if hr = procHcsModifyProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsModifyProcess.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(settings)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(propertyQuery) + if hr != nil { + return + } + return _hcsGetServiceProperties(_p0, properties, result) +} + +func _hcsGetServiceProperties(propertyQuery *uint16, properties **uint16, result **uint16) (hr error) { + if hr = procHcsGetServiceProperties.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsGetServiceProperties.Addr(), 3, uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsRegisterProcessCallback(process HcsProcess, callback uintptr, context uintptr, callbackHandle *HcsCallback) (hr error) { + if hr = procHcsRegisterProcessCallback.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsRegisterProcessCallback.Addr(), 4, uintptr(process), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsUnregisterProcessCallback(callbackHandle HcsCallback) (hr error) { + if hr = procHcsUnregisterProcessCallback.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsUnregisterProcessCallback.Addr(), 1, uintptr(callbackHandle), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go new file mode 100644 index 0000000000..5debe974d4 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go @@ -0,0 +1,27 @@ +package wclayer + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" +) + +// ActivateLayer will find the layer with the given id and mount it's filesystem. +// For a read/write layer, the mounted filesystem will appear as a volume on the +// host, while a read-only layer is generally expected to be a no-op. +// An activated layer must later be deactivated via DeactivateLayer. +func ActivateLayer(ctx context.Context, path string) (err error) { + title := "hcsshim::ActivateLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) + + err = activateLayer(&stdDriverInfo, path) + if err != nil { + return hcserror.New(err, title, "") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/baselayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/baselayer.go new file mode 100644 index 0000000000..3ec708d1ed --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/baselayer.go @@ -0,0 +1,182 @@ +package wclayer + +import ( + "context" + "errors" + "os" + "path/filepath" + "syscall" + + "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/Microsoft/hcsshim/internal/safefile" + "github.com/Microsoft/hcsshim/internal/winapi" + "go.opencensus.io/trace" +) + +type baseLayerWriter struct { + ctx context.Context + s *trace.Span + + root *os.File + f *os.File + bw *winio.BackupFileWriter + err error + hasUtilityVM bool + dirInfo []dirInfo +} + +type dirInfo struct { + path string + fileInfo winio.FileBasicInfo +} + +// reapplyDirectoryTimes reapplies directory modification, creation, etc. times +// after processing of the directory tree has completed. The times are expected +// to be ordered such that parent directories come before child directories. +func reapplyDirectoryTimes(root *os.File, dis []dirInfo) error { + for i := range dis { + di := &dis[len(dis)-i-1] // reverse order: process child directories first + f, err := safefile.OpenRelative(di.path, root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, winapi.FILE_OPEN, winapi.FILE_DIRECTORY_FILE|syscall.FILE_FLAG_OPEN_REPARSE_POINT) + if err != nil { + return err + } + + err = winio.SetFileBasicInfo(f, &di.fileInfo) + f.Close() + if err != nil { + return err + } + + } + return nil +} + +func (w *baseLayerWriter) closeCurrentFile() error { + if w.f != nil { + err := w.bw.Close() + err2 := w.f.Close() + w.f = nil + w.bw = nil + if err != nil { + return err + } + if err2 != nil { + return err2 + } + } + return nil +} + +func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err error) { + defer func() { + if err != nil { + w.err = err + } + }() + + err = w.closeCurrentFile() + if err != nil { + return err + } + + if filepath.ToSlash(name) == `UtilityVM/Files` { + w.hasUtilityVM = true + } + + var f *os.File + defer func() { + if f != nil { + f.Close() + } + }() + + extraFlags := uint32(0) + if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { + extraFlags |= winapi.FILE_DIRECTORY_FILE + w.dirInfo = append(w.dirInfo, dirInfo{name, *fileInfo}) + } + + mode := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | winio.WRITE_DAC | winio.WRITE_OWNER | winio.ACCESS_SYSTEM_SECURITY) + f, err = safefile.OpenRelative(name, w.root, mode, syscall.FILE_SHARE_READ, winapi.FILE_CREATE, extraFlags) + if err != nil { + return hcserror.New(err, "Failed to safefile.OpenRelative", name) + } + + err = winio.SetFileBasicInfo(f, fileInfo) + if err != nil { + return hcserror.New(err, "Failed to SetFileBasicInfo", name) + } + + w.f = f + w.bw = winio.NewBackupFileWriter(f, true) + f = nil + return nil +} + +func (w *baseLayerWriter) AddLink(name string, target string) (err error) { + defer func() { + if err != nil { + w.err = err + } + }() + + err = w.closeCurrentFile() + if err != nil { + return err + } + + return safefile.LinkRelative(target, w.root, name, w.root) +} + +func (w *baseLayerWriter) Remove(name string) error { + return errors.New("base layer cannot have tombstones") +} + +func (w *baseLayerWriter) Write(b []byte) (int, error) { + n, err := w.bw.Write(b) + if err != nil { + w.err = err + } + return n, err +} + +func (w *baseLayerWriter) Close() (err error) { + defer w.s.End() + defer func() { oc.SetSpanStatus(w.s, err) }() + defer func() { + w.root.Close() + w.root = nil + }() + + err = w.closeCurrentFile() + if err != nil { + return err + } + if w.err == nil { + // Restore the file times of all the directories, since they may have + // been modified by creating child directories. + err = reapplyDirectoryTimes(w.root, w.dirInfo) + if err != nil { + return err + } + + err = ProcessBaseLayer(w.ctx, w.root.Name()) + if err != nil { + return err + } + + if w.hasUtilityVM { + err := safefile.EnsureNotReparsePointRelative("UtilityVM", w.root) + if err != nil { + return err + } + err = ProcessUtilityVMImage(w.ctx, filepath.Join(w.root.Name(), "UtilityVM")) + if err != nil { + return err + } + } + } + return w.err +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go new file mode 100644 index 0000000000..480aee8725 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go @@ -0,0 +1,27 @@ +package wclayer + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" +) + +// CreateLayer creates a new, empty, read-only layer on the filesystem based on +// the parent layer provided. +func CreateLayer(ctx context.Context, path, parent string) (err error) { + title := "hcsshim::CreateLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("path", path), + trace.StringAttribute("parent", parent)) + + err = createLayer(&stdDriverInfo, path, parent) + if err != nil { + return hcserror.New(err, title, "") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go new file mode 100644 index 0000000000..131aa94f14 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go @@ -0,0 +1,34 @@ +package wclayer + +import ( + "context" + "strings" + + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" +) + +// CreateScratchLayer creates and populates new read-write layer for use by a container. +// This requires the full list of paths to all parent layers up to the base +func CreateScratchLayer(ctx context.Context, path string, parentLayerPaths []string) (err error) { + title := "hcsshim::CreateScratchLayer" + ctx, span := trace.StartSpan(ctx, title) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("path", path), + trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", "))) + + // Generate layer descriptors + layers, err := layerPathsToDescriptors(ctx, parentLayerPaths) + if err != nil { + return err + } + + err = createSandboxLayer(&stdDriverInfo, path, 0, layers) + if err != nil { + return hcserror.New(err, title, "") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go new file mode 100644 index 0000000000..d5bf2f5bdc --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go @@ -0,0 +1,24 @@ +package wclayer + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" +) + +// DeactivateLayer will dismount a layer that was mounted via ActivateLayer. +func DeactivateLayer(ctx context.Context, path string) (err error) { + title := "hcsshim::DeactivateLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) + + err = deactivateLayer(&stdDriverInfo, path) + if err != nil { + return hcserror.New(err, title+"- failed", "") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go new file mode 100644 index 0000000000..424467ac33 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go @@ -0,0 +1,25 @@ +package wclayer + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" +) + +// DestroyLayer will remove the on-disk files representing the layer with the given +// path, including that layer's containing folder, if any. +func DestroyLayer(ctx context.Context, path string) (err error) { + title := "hcsshim::DestroyLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) + + err = destroyLayer(&stdDriverInfo, path) + if err != nil { + return hcserror.New(err, title, "") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go new file mode 100644 index 0000000000..035c9041e6 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go @@ -0,0 +1,140 @@ +package wclayer + +import ( + "context" + "os" + "path/filepath" + "syscall" + "unsafe" + + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/Microsoft/hcsshim/osversion" + "go.opencensus.io/trace" +) + +// ExpandScratchSize expands the size of a layer to at least size bytes. +func ExpandScratchSize(ctx context.Context, path string, size uint64) (err error) { + title := "hcsshim::ExpandScratchSize" + ctx, span := trace.StartSpan(ctx, title) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("path", path), + trace.Int64Attribute("size", int64(size))) + + err = expandSandboxSize(&stdDriverInfo, path, size) + if err != nil { + return hcserror.New(err, title, "") + } + + // Manually expand the volume now in order to work around bugs in 19H1 and + // prerelease versions of Vb. Remove once this is fixed in Windows. + if build := osversion.Build(); build >= osversion.V19H1 && build < 19020 { + err = expandSandboxVolume(ctx, path) + if err != nil { + return err + } + } + return nil +} + +type virtualStorageType struct { + DeviceID uint32 + VendorID [16]byte +} + +type openVersion2 struct { + GetInfoOnly int32 // bool but 4-byte aligned + ReadOnly int32 // bool but 4-byte aligned + ResiliencyGUID [16]byte // GUID +} + +type openVirtualDiskParameters struct { + Version uint32 // Must always be set to 2 + Version2 openVersion2 +} + +func attachVhd(path string) (syscall.Handle, error) { + var ( + defaultType virtualStorageType + handle syscall.Handle + ) + parameters := openVirtualDiskParameters{Version: 2} + err := openVirtualDisk( + &defaultType, + path, + 0, + 0, + ¶meters, + &handle) + if err != nil { + return 0, &os.PathError{Op: "OpenVirtualDisk", Path: path, Err: err} + } + err = attachVirtualDisk(handle, 0, 0, 0, 0, 0) + if err != nil { + syscall.Close(handle) + return 0, &os.PathError{Op: "AttachVirtualDisk", Path: path, Err: err} + } + return handle, nil +} + +func expandSandboxVolume(ctx context.Context, path string) error { + // Mount the sandbox VHD temporarily. + vhdPath := filepath.Join(path, "sandbox.vhdx") + vhd, err := attachVhd(vhdPath) + if err != nil { + return &os.PathError{Op: "OpenVirtualDisk", Path: vhdPath, Err: err} + } + defer syscall.Close(vhd) + + // Open the volume. + volumePath, err := GetLayerMountPath(ctx, path) + if err != nil { + return err + } + if volumePath[len(volumePath)-1] == '\\' { + volumePath = volumePath[:len(volumePath)-1] + } + volume, err := os.OpenFile(volumePath, os.O_RDWR, 0) + if err != nil { + return err + } + defer volume.Close() + + // Get the volume's underlying partition size in NTFS clusters. + var ( + partitionSize int64 + bytes uint32 + ) + const _IOCTL_DISK_GET_LENGTH_INFO = 0x0007405C + err = syscall.DeviceIoControl(syscall.Handle(volume.Fd()), _IOCTL_DISK_GET_LENGTH_INFO, nil, 0, (*byte)(unsafe.Pointer(&partitionSize)), 8, &bytes, nil) + if err != nil { + return &os.PathError{Op: "IOCTL_DISK_GET_LENGTH_INFO", Path: volume.Name(), Err: err} + } + const ( + clusterSize = 4096 + sectorSize = 512 + ) + targetClusters := partitionSize / clusterSize + + // Get the volume's current size in NTFS clusters. + var volumeSize int64 + err = getDiskFreeSpaceEx(volume.Name()+"\\", nil, &volumeSize, nil) + if err != nil { + return &os.PathError{Op: "GetDiskFreeSpaceEx", Path: volume.Name(), Err: err} + } + volumeClusters := volumeSize / clusterSize + + // Only resize the volume if there is space to grow, otherwise this will + // fail with invalid parameter. NTFS reserves one cluster. + if volumeClusters+1 < targetClusters { + targetSectors := targetClusters * (clusterSize / sectorSize) + const _FSCTL_EXTEND_VOLUME = 0x000900F0 + err = syscall.DeviceIoControl(syscall.Handle(volume.Fd()), _FSCTL_EXTEND_VOLUME, (*byte)(unsafe.Pointer(&targetSectors)), 8, nil, 0, &bytes, nil) + if err != nil { + return &os.PathError{Op: "FSCTL_EXTEND_VOLUME", Path: volume.Name(), Err: err} + } + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go new file mode 100644 index 0000000000..97b27eb7d6 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go @@ -0,0 +1,94 @@ +package wclayer + +import ( + "context" + "io/ioutil" + "os" + "strings" + + "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" +) + +// ExportLayer will create a folder at exportFolderPath and fill that folder with +// the transport format version of the layer identified by layerId. This transport +// format includes any metadata required for later importing the layer (using +// ImportLayer), and requires the full list of parent layer paths in order to +// perform the export. +func ExportLayer(ctx context.Context, path string, exportFolderPath string, parentLayerPaths []string) (err error) { + title := "hcsshim::ExportLayer" + ctx, span := trace.StartSpan(ctx, title) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("path", path), + trace.StringAttribute("exportFolderPath", exportFolderPath), + trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", "))) + + // Generate layer descriptors + layers, err := layerPathsToDescriptors(ctx, parentLayerPaths) + if err != nil { + return err + } + + err = exportLayer(&stdDriverInfo, path, exportFolderPath, layers) + if err != nil { + return hcserror.New(err, title, "") + } + return nil +} + +type LayerReader interface { + Next() (string, int64, *winio.FileBasicInfo, error) + Read(b []byte) (int, error) + Close() error +} + +// NewLayerReader returns a new layer reader for reading the contents of an on-disk layer. +// The caller must have taken the SeBackupPrivilege privilege +// to call this and any methods on the resulting LayerReader. +func NewLayerReader(ctx context.Context, path string, parentLayerPaths []string) (_ LayerReader, err error) { + ctx, span := trace.StartSpan(ctx, "hcsshim::NewLayerReader") + defer func() { + if err != nil { + oc.SetSpanStatus(span, err) + span.End() + } + }() + span.AddAttributes( + trace.StringAttribute("path", path), + trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", "))) + + exportPath, err := ioutil.TempDir("", "hcs") + if err != nil { + return nil, err + } + err = ExportLayer(ctx, path, exportPath, parentLayerPaths) + if err != nil { + os.RemoveAll(exportPath) + return nil, err + } + return &legacyLayerReaderWrapper{ + ctx: ctx, + s: span, + legacyLayerReader: newLegacyLayerReader(exportPath), + }, nil +} + +type legacyLayerReaderWrapper struct { + ctx context.Context + s *trace.Span + + *legacyLayerReader +} + +func (r *legacyLayerReaderWrapper) Close() (err error) { + defer r.s.End() + defer func() { oc.SetSpanStatus(r.s, err) }() + + err = r.legacyLayerReader.Close() + os.RemoveAll(r.root) + return err +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go new file mode 100644 index 0000000000..8d213f5871 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go @@ -0,0 +1,50 @@ +package wclayer + +import ( + "context" + "syscall" + + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/log" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" +) + +// GetLayerMountPath will look for a mounted layer with the given path and return +// the path at which that layer can be accessed. This path may be a volume path +// if the layer is a mounted read-write layer, otherwise it is expected to be the +// folder path at which the layer is stored. +func GetLayerMountPath(ctx context.Context, path string) (_ string, err error) { + title := "hcsshim::GetLayerMountPath" + ctx, span := trace.StartSpan(ctx, title) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) + + var mountPathLength uintptr = 0 + + // Call the procedure itself. + log.G(ctx).Debug("Calling proc (1)") + err = getLayerMountPath(&stdDriverInfo, path, &mountPathLength, nil) + if err != nil { + return "", hcserror.New(err, title, "(first call)") + } + + // Allocate a mount path of the returned length. + if mountPathLength == 0 { + return "", nil + } + mountPathp := make([]uint16, mountPathLength) + mountPathp[0] = 0 + + // Call the procedure again + log.G(ctx).Debug("Calling proc (2)") + err = getLayerMountPath(&stdDriverInfo, path, &mountPathLength, &mountPathp[0]) + if err != nil { + return "", hcserror.New(err, title, "(second call)") + } + + mountPath := syscall.UTF16ToString(mountPathp[0:]) + span.AddAttributes(trace.StringAttribute("mountPath", mountPath)) + return mountPath, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go new file mode 100644 index 0000000000..ae1fff8403 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go @@ -0,0 +1,29 @@ +package wclayer + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" +) + +// GetSharedBaseImages will enumerate the images stored in the common central +// image store and return descriptive info about those images for the purpose +// of registering them with the graphdriver, graph, and tagstore. +func GetSharedBaseImages(ctx context.Context) (_ string, err error) { + title := "hcsshim::GetSharedBaseImages" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + + var buffer *uint16 + err = getBaseImages(&buffer) + if err != nil { + return "", hcserror.New(err, title, "") + } + imageData := interop.ConvertAndFreeCoTaskMemString(buffer) + span.AddAttributes(trace.StringAttribute("imageData", imageData)) + return imageData, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go new file mode 100644 index 0000000000..4b282fef9d --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go @@ -0,0 +1,26 @@ +package wclayer + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" +) + +// GrantVmAccess adds access to a file for a given VM +func GrantVmAccess(ctx context.Context, vmid string, filepath string) (err error) { + title := "hcsshim::GrantVmAccess" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("vm-id", vmid), + trace.StringAttribute("path", filepath)) + + err = grantVmAccess(vmid, filepath) + if err != nil { + return hcserror.New(err, title, "") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go new file mode 100644 index 0000000000..687550f0be --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go @@ -0,0 +1,166 @@ +package wclayer + +import ( + "context" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/Microsoft/hcsshim/internal/safefile" + "go.opencensus.io/trace" +) + +// ImportLayer will take the contents of the folder at importFolderPath and import +// that into a layer with the id layerId. Note that in order to correctly populate +// the layer and interperet the transport format, all parent layers must already +// be present on the system at the paths provided in parentLayerPaths. +func ImportLayer(ctx context.Context, path string, importFolderPath string, parentLayerPaths []string) (err error) { + title := "hcsshim::ImportLayer" + ctx, span := trace.StartSpan(ctx, title) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("path", path), + trace.StringAttribute("importFolderPath", importFolderPath), + trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", "))) + + // Generate layer descriptors + layers, err := layerPathsToDescriptors(ctx, parentLayerPaths) + if err != nil { + return err + } + + err = importLayer(&stdDriverInfo, path, importFolderPath, layers) + if err != nil { + return hcserror.New(err, title, "") + } + return nil +} + +// LayerWriter is an interface that supports writing a new container image layer. +type LayerWriter interface { + // Add adds a file to the layer with given metadata. + Add(name string, fileInfo *winio.FileBasicInfo) error + // AddLink adds a hard link to the layer. The target must already have been added. + AddLink(name string, target string) error + // Remove removes a file that was present in a parent layer from the layer. + Remove(name string) error + // Write writes data to the current file. The data must be in the format of a Win32 + // backup stream. + Write(b []byte) (int, error) + // Close finishes the layer writing process and releases any resources. + Close() error +} + +type legacyLayerWriterWrapper struct { + ctx context.Context + s *trace.Span + + *legacyLayerWriter + path string + parentLayerPaths []string +} + +func (r *legacyLayerWriterWrapper) Close() (err error) { + defer r.s.End() + defer func() { oc.SetSpanStatus(r.s, err) }() + defer os.RemoveAll(r.root.Name()) + defer r.legacyLayerWriter.CloseRoots() + + err = r.legacyLayerWriter.Close() + if err != nil { + return err + } + + if err = ImportLayer(r.ctx, r.destRoot.Name(), r.path, r.parentLayerPaths); err != nil { + return err + } + for _, name := range r.Tombstones { + if err = safefile.RemoveRelative(name, r.destRoot); err != nil && !os.IsNotExist(err) { + return err + } + } + // Add any hard links that were collected. + for _, lnk := range r.PendingLinks { + if err = safefile.RemoveRelative(lnk.Path, r.destRoot); err != nil && !os.IsNotExist(err) { + return err + } + if err = safefile.LinkRelative(lnk.Target, lnk.TargetRoot, lnk.Path, r.destRoot); err != nil { + return err + } + } + + // The reapplyDirectoryTimes must be called AFTER we are done with Tombstone + // deletion and hard link creation. This is because Tombstone deletion and hard link + // creation updates the directory last write timestamps so that will change the + // timestamps added by the `Add` call. Some container applications depend on the + // correctness of these timestamps and so we should change the timestamps back to + // the original value (i.e the value provided in the Add call) after this + // processing is done. + err = reapplyDirectoryTimes(r.destRoot, r.changedDi) + if err != nil { + return err + } + + // Prepare the utility VM for use if one is present in the layer. + if r.HasUtilityVM { + err := safefile.EnsureNotReparsePointRelative("UtilityVM", r.destRoot) + if err != nil { + return err + } + err = ProcessUtilityVMImage(r.ctx, filepath.Join(r.destRoot.Name(), "UtilityVM")) + if err != nil { + return err + } + } + return nil +} + +// NewLayerWriter returns a new layer writer for creating a layer on disk. +// The caller must have taken the SeBackupPrivilege and SeRestorePrivilege privileges +// to call this and any methods on the resulting LayerWriter. +func NewLayerWriter(ctx context.Context, path string, parentLayerPaths []string) (_ LayerWriter, err error) { + ctx, span := trace.StartSpan(ctx, "hcsshim::NewLayerWriter") + defer func() { + if err != nil { + oc.SetSpanStatus(span, err) + span.End() + } + }() + span.AddAttributes( + trace.StringAttribute("path", path), + trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", "))) + + if len(parentLayerPaths) == 0 { + // This is a base layer. It gets imported differently. + f, err := safefile.OpenRoot(path) + if err != nil { + return nil, err + } + return &baseLayerWriter{ + ctx: ctx, + s: span, + root: f, + }, nil + } + + importPath, err := ioutil.TempDir("", "hcs") + if err != nil { + return nil, err + } + w, err := newLegacyLayerWriter(importPath, parentLayerPaths, path) + if err != nil { + return nil, err + } + return &legacyLayerWriterWrapper{ + ctx: ctx, + s: span, + legacyLayerWriter: w, + path: importPath, + parentLayerPaths: parentLayerPaths, + }, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go new file mode 100644 index 0000000000..01e6723393 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go @@ -0,0 +1,28 @@ +package wclayer + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" +) + +// LayerExists will return true if a layer with the given id exists and is known +// to the system. +func LayerExists(ctx context.Context, path string) (_ bool, err error) { + title := "hcsshim::LayerExists" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) + + // Call the procedure itself. + var exists uint32 + err = layerExists(&stdDriverInfo, path, &exists) + if err != nil { + return false, hcserror.New(err, title, "") + } + span.AddAttributes(trace.BoolAttribute("layer-exists", exists != 0)) + return exists != 0, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerid.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerid.go new file mode 100644 index 0000000000..0ce34a30f8 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerid.go @@ -0,0 +1,22 @@ +package wclayer + +import ( + "context" + "path/filepath" + + "github.com/Microsoft/go-winio/pkg/guid" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" +) + +// LayerID returns the layer ID of a layer on disk. +func LayerID(ctx context.Context, path string) (_ guid.GUID, err error) { + title := "hcsshim::LayerID" + ctx, span := trace.StartSpan(ctx, title) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) + + _, file := filepath.Split(path) + return NameToGuid(ctx, file) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go new file mode 100644 index 0000000000..1ec893c6af --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go @@ -0,0 +1,97 @@ +package wclayer + +// This file contains utility functions to support storage (graph) related +// functionality. + +import ( + "context" + "syscall" + + "github.com/Microsoft/go-winio/pkg/guid" + "github.com/sirupsen/logrus" +) + +/* To pass into syscall, we need a struct matching the following: +enum GraphDriverType +{ + DiffDriver, + FilterDriver +}; + +struct DriverInfo { + GraphDriverType Flavour; + LPCWSTR HomeDir; +}; +*/ + +type driverInfo struct { + Flavour int + HomeDirp *uint16 +} + +var ( + utf16EmptyString uint16 + stdDriverInfo = driverInfo{1, &utf16EmptyString} +) + +/* To pass into syscall, we need a struct matching the following: +typedef struct _WC_LAYER_DESCRIPTOR { + + // + // The ID of the layer + // + + GUID LayerId; + + // + // Additional flags + // + + union { + struct { + ULONG Reserved : 31; + ULONG Dirty : 1; // Created from sandbox as a result of snapshot + }; + ULONG Value; + } Flags; + + // + // Path to the layer root directory, null-terminated + // + + PCWSTR Path; + +} WC_LAYER_DESCRIPTOR, *PWC_LAYER_DESCRIPTOR; +*/ +type WC_LAYER_DESCRIPTOR struct { + LayerId guid.GUID + Flags uint32 + Pathp *uint16 +} + +func layerPathsToDescriptors(ctx context.Context, parentLayerPaths []string) ([]WC_LAYER_DESCRIPTOR, error) { + // Array of descriptors that gets constructed. + var layers []WC_LAYER_DESCRIPTOR + + for i := 0; i < len(parentLayerPaths); i++ { + g, err := LayerID(ctx, parentLayerPaths[i]) + if err != nil { + logrus.WithError(err).Debug("Failed to convert name to guid") + return nil, err + } + + p, err := syscall.UTF16PtrFromString(parentLayerPaths[i]) + if err != nil { + logrus.WithError(err).Debug("Failed conversion of parentLayerPath to pointer") + return nil, err + } + + layers = append(layers, WC_LAYER_DESCRIPTOR{ + LayerId: g, + Flags: 0, + Pathp: p, + }) + } + + return layers, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go new file mode 100644 index 0000000000..b7f3064f26 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go @@ -0,0 +1,811 @@ +package wclayer + +import ( + "bufio" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "syscall" + + "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim/internal/longpath" + "github.com/Microsoft/hcsshim/internal/safefile" + "github.com/Microsoft/hcsshim/internal/winapi" +) + +var errorIterationCanceled = errors.New("") + +var mutatedUtilityVMFiles = map[string]bool{ + `EFI\Microsoft\Boot\BCD`: true, + `EFI\Microsoft\Boot\BCD.LOG`: true, + `EFI\Microsoft\Boot\BCD.LOG1`: true, + `EFI\Microsoft\Boot\BCD.LOG2`: true, +} + +const ( + filesPath = `Files` + hivesPath = `Hives` + utilityVMPath = `UtilityVM` + utilityVMFilesPath = `UtilityVM\Files` +) + +func openFileOrDir(path string, mode uint32, createDisposition uint32) (file *os.File, err error) { + return winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createDisposition) +} + +func hasPathPrefix(p, prefix string) bool { + return strings.HasPrefix(p, prefix) && len(p) > len(prefix) && p[len(prefix)] == '\\' +} + +type fileEntry struct { + path string + fi os.FileInfo + err error +} + +type legacyLayerReader struct { + root string + result chan *fileEntry + proceed chan bool + currentFile *os.File + backupReader *winio.BackupFileReader +} + +// newLegacyLayerReader returns a new LayerReader that can read the Windows +// container layer transport format from disk. +func newLegacyLayerReader(root string) *legacyLayerReader { + r := &legacyLayerReader{ + root: root, + result: make(chan *fileEntry), + proceed: make(chan bool), + } + go r.walk() + return r +} + +func readTombstones(path string) (map[string]([]string), error) { + tf, err := os.Open(filepath.Join(path, "tombstones.txt")) + if err != nil { + return nil, err + } + defer tf.Close() + s := bufio.NewScanner(tf) + if !s.Scan() || s.Text() != "\xef\xbb\xbfVersion 1.0" { + return nil, errors.New("invalid tombstones file") + } + + ts := make(map[string]([]string)) + for s.Scan() { + t := filepath.Join(filesPath, s.Text()[1:]) // skip leading `\` + dir := filepath.Dir(t) + ts[dir] = append(ts[dir], t) + } + if err = s.Err(); err != nil { + return nil, err + } + + return ts, nil +} + +func (r *legacyLayerReader) walkUntilCancelled() error { + root, err := longpath.LongAbs(r.root) + if err != nil { + return err + } + + r.root = root + ts, err := readTombstones(r.root) + if err != nil { + return err + } + + err = filepath.Walk(r.root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + // Indirect fix for https://github.com/moby/moby/issues/32838#issuecomment-343610048. + // Handle failure from what may be a golang bug in the conversion of + // UTF16 to UTF8 in files which are left in the recycle bin. Os.Lstat + // which is called by filepath.Walk will fail when a filename contains + // unicode characters. Skip the recycle bin regardless which is goodness. + if strings.EqualFold(path, filepath.Join(r.root, `Files\$Recycle.Bin`)) && info.IsDir() { + return filepath.SkipDir + } + + if path == r.root || path == filepath.Join(r.root, "tombstones.txt") || strings.HasSuffix(path, ".$wcidirs$") { + return nil + } + + r.result <- &fileEntry{path, info, nil} + if !<-r.proceed { + return errorIterationCanceled + } + + // List all the tombstones. + if info.IsDir() { + relPath, err := filepath.Rel(r.root, path) + if err != nil { + return err + } + if dts, ok := ts[relPath]; ok { + for _, t := range dts { + r.result <- &fileEntry{filepath.Join(r.root, t), nil, nil} + if !<-r.proceed { + return errorIterationCanceled + } + } + } + } + return nil + }) + if err == errorIterationCanceled { + return nil + } + if err == nil { + return io.EOF + } + return err +} + +func (r *legacyLayerReader) walk() { + defer close(r.result) + if !<-r.proceed { + return + } + + err := r.walkUntilCancelled() + if err != nil { + for { + r.result <- &fileEntry{err: err} + if !<-r.proceed { + return + } + } + } +} + +func (r *legacyLayerReader) reset() { + if r.backupReader != nil { + r.backupReader.Close() + r.backupReader = nil + } + if r.currentFile != nil { + r.currentFile.Close() + r.currentFile = nil + } +} + +func findBackupStreamSize(r io.Reader) (int64, error) { + br := winio.NewBackupStreamReader(r) + for { + hdr, err := br.Next() + if err != nil { + if err == io.EOF { + err = nil + } + return 0, err + } + if hdr.Id == winio.BackupData { + return hdr.Size, nil + } + } +} + +func (r *legacyLayerReader) Next() (path string, size int64, fileInfo *winio.FileBasicInfo, err error) { + r.reset() + r.proceed <- true + fe := <-r.result + if fe == nil { + err = errors.New("LegacyLayerReader closed") + return + } + if fe.err != nil { + err = fe.err + return + } + + path, err = filepath.Rel(r.root, fe.path) + if err != nil { + return + } + + if fe.fi == nil { + // This is a tombstone. Return a nil fileInfo. + return + } + + if fe.fi.IsDir() && hasPathPrefix(path, filesPath) { + fe.path += ".$wcidirs$" + } + + f, err := openFileOrDir(fe.path, syscall.GENERIC_READ, syscall.OPEN_EXISTING) + if err != nil { + return + } + defer func() { + if f != nil { + f.Close() + } + }() + + fileInfo, err = winio.GetFileBasicInfo(f) + if err != nil { + return + } + + if !hasPathPrefix(path, filesPath) { + size = fe.fi.Size() + r.backupReader = winio.NewBackupFileReader(f, false) + if path == hivesPath || path == filesPath { + // The Hives directory has a non-deterministic file time because of the + // nature of the import process. Use the times from System_Delta. + var g *os.File + g, err = os.Open(filepath.Join(r.root, hivesPath, `System_Delta`)) + if err != nil { + return + } + attr := fileInfo.FileAttributes + fileInfo, err = winio.GetFileBasicInfo(g) + g.Close() + if err != nil { + return + } + fileInfo.FileAttributes = attr + } + + // The creation time and access time get reset for files outside of the Files path. + fileInfo.CreationTime = fileInfo.LastWriteTime + fileInfo.LastAccessTime = fileInfo.LastWriteTime + + } else { + // The file attributes are written before the backup stream. + var attr uint32 + err = binary.Read(f, binary.LittleEndian, &attr) + if err != nil { + return + } + fileInfo.FileAttributes = attr + beginning := int64(4) + + // Find the accurate file size. + if !fe.fi.IsDir() { + size, err = findBackupStreamSize(f) + if err != nil { + err = &os.PathError{Op: "findBackupStreamSize", Path: fe.path, Err: err} + return + } + } + + // Return back to the beginning of the backup stream. + _, err = f.Seek(beginning, 0) + if err != nil { + return + } + } + + r.currentFile = f + f = nil + return +} + +func (r *legacyLayerReader) Read(b []byte) (int, error) { + if r.backupReader == nil { + if r.currentFile == nil { + return 0, io.EOF + } + return r.currentFile.Read(b) + } + return r.backupReader.Read(b) +} + +func (r *legacyLayerReader) Seek(offset int64, whence int) (int64, error) { + if r.backupReader == nil { + if r.currentFile == nil { + return 0, errors.New("no current file") + } + return r.currentFile.Seek(offset, whence) + } + return 0, errors.New("seek not supported on this stream") +} + +func (r *legacyLayerReader) Close() error { + r.proceed <- false + <-r.result + r.reset() + return nil +} + +type pendingLink struct { + Path, Target string + TargetRoot *os.File +} + +type pendingDir struct { + Path string + Root *os.File +} + +type legacyLayerWriter struct { + root *os.File + destRoot *os.File + parentRoots []*os.File + currentFile *os.File + bufWriter *bufio.Writer + currentFileName string + currentFileRoot *os.File + backupWriter *winio.BackupFileWriter + Tombstones []string + HasUtilityVM bool + changedDi []dirInfo + addedFiles map[string]bool + PendingLinks []pendingLink + pendingDirs []pendingDir + currentIsDir bool +} + +// newLegacyLayerWriter returns a LayerWriter that can write the contaler layer +// transport format to disk. +func newLegacyLayerWriter(root string, parentRoots []string, destRoot string) (w *legacyLayerWriter, err error) { + w = &legacyLayerWriter{ + addedFiles: make(map[string]bool), + } + defer func() { + if err != nil { + w.CloseRoots() + w = nil + } + }() + w.root, err = safefile.OpenRoot(root) + if err != nil { + return + } + w.destRoot, err = safefile.OpenRoot(destRoot) + if err != nil { + return + } + for _, r := range parentRoots { + f, err := safefile.OpenRoot(r) + if err != nil { + return w, err + } + w.parentRoots = append(w.parentRoots, f) + } + w.bufWriter = bufio.NewWriterSize(ioutil.Discard, 65536) + return +} + +func (w *legacyLayerWriter) CloseRoots() { + if w.root != nil { + w.root.Close() + w.root = nil + } + if w.destRoot != nil { + w.destRoot.Close() + w.destRoot = nil + } + for i := range w.parentRoots { + _ = w.parentRoots[i].Close() + } + w.parentRoots = nil +} + +func (w *legacyLayerWriter) initUtilityVM() error { + if !w.HasUtilityVM { + err := safefile.MkdirRelative(utilityVMPath, w.destRoot) + if err != nil { + return err + } + // Server 2016 does not support multiple layers for the utility VM, so + // clone the utility VM from the parent layer into this layer. Use hard + // links to avoid unnecessary copying, since most of the files are + // immutable. + err = cloneTree(w.parentRoots[0], w.destRoot, utilityVMFilesPath, mutatedUtilityVMFiles) + if err != nil { + return fmt.Errorf("cloning the parent utility VM image failed: %s", err) + } + w.HasUtilityVM = true + } + return nil +} + +func (w *legacyLayerWriter) reset() error { + err := w.bufWriter.Flush() + if err != nil { + return err + } + w.bufWriter.Reset(ioutil.Discard) + if w.currentIsDir { + r := w.currentFile + br := winio.NewBackupStreamReader(r) + // Seek to the beginning of the backup stream, skipping the fileattrs + if _, err := r.Seek(4, io.SeekStart); err != nil { + return err + } + + for { + bhdr, err := br.Next() + if err == io.EOF { + // end of backupstream data + break + } + if err != nil { + return err + } + switch bhdr.Id { + case winio.BackupReparseData: + // The current file is a `.$wcidirs$` metadata file that + // describes a directory reparse point. Delete the placeholder + // directory to prevent future files being added into the + // destination of the reparse point during the ImportLayer call + if err := safefile.RemoveRelative(w.currentFileName, w.currentFileRoot); err != nil { + return err + } + w.pendingDirs = append(w.pendingDirs, pendingDir{Path: w.currentFileName, Root: w.currentFileRoot}) + default: + // ignore all other stream types, as we only care about directory reparse points + } + } + w.currentIsDir = false + } + if w.backupWriter != nil { + w.backupWriter.Close() + w.backupWriter = nil + } + if w.currentFile != nil { + w.currentFile.Close() + w.currentFile = nil + w.currentFileName = "" + w.currentFileRoot = nil + } + return nil +} + +// copyFileWithMetadata copies a file using the backup/restore APIs in order to preserve metadata +func copyFileWithMetadata(srcRoot, destRoot *os.File, subPath string, isDir bool) (fileInfo *winio.FileBasicInfo, err error) { + src, err := safefile.OpenRelative( + subPath, + srcRoot, + syscall.GENERIC_READ|winio.ACCESS_SYSTEM_SECURITY, + syscall.FILE_SHARE_READ, + winapi.FILE_OPEN, + winapi.FILE_OPEN_REPARSE_POINT) + if err != nil { + return nil, err + } + defer src.Close() + srcr := winio.NewBackupFileReader(src, true) + defer srcr.Close() + + fileInfo, err = winio.GetFileBasicInfo(src) + if err != nil { + return nil, err + } + + extraFlags := uint32(0) + if isDir { + extraFlags |= winapi.FILE_DIRECTORY_FILE + } + dest, err := safefile.OpenRelative( + subPath, + destRoot, + syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY, + syscall.FILE_SHARE_READ, + winapi.FILE_CREATE, + extraFlags) + if err != nil { + return nil, err + } + defer dest.Close() + + err = winio.SetFileBasicInfo(dest, fileInfo) + if err != nil { + return nil, err + } + + destw := winio.NewBackupFileWriter(dest, true) + defer func() { + cerr := destw.Close() + if err == nil { + err = cerr + } + }() + + _, err = io.Copy(destw, srcr) + if err != nil { + return nil, err + } + + return fileInfo, nil +} + +// cloneTree clones a directory tree using hard links. It skips hard links for +// the file names in the provided map and just copies those files. +func cloneTree(srcRoot *os.File, destRoot *os.File, subPath string, mutatedFiles map[string]bool) error { + var di []dirInfo + err := safefile.EnsureNotReparsePointRelative(subPath, srcRoot) + if err != nil { + return err + } + err = filepath.Walk(filepath.Join(srcRoot.Name(), subPath), func(srcFilePath string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + relPath, err := filepath.Rel(srcRoot.Name(), srcFilePath) + if err != nil { + return err + } + + fileAttributes := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes + // Directories, reparse points, and files that will be mutated during + // utility VM import must be copied. All other files can be hard linked. + isReparsePoint := fileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 + // In go1.9, FileInfo.IsDir() returns false if the directory is also a symlink. + // See: https://github.com/golang/go/commit/1989921aef60c83e6f9127a8448fb5ede10e9acc + // Fixes the problem by checking syscall.FILE_ATTRIBUTE_DIRECTORY directly + isDir := fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 + + if isDir || isReparsePoint || mutatedFiles[relPath] { + fi, err := copyFileWithMetadata(srcRoot, destRoot, relPath, isDir) + if err != nil { + return err + } + if isDir { + di = append(di, dirInfo{path: relPath, fileInfo: *fi}) + } + } else { + err = safefile.LinkRelative(relPath, srcRoot, relPath, destRoot) + if err != nil { + return err + } + } + + return nil + }) + if err != nil { + return err + } + + return reapplyDirectoryTimes(destRoot, di) +} + +func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error { + if err := w.reset(); err != nil { + return err + } + + if name == utilityVMPath { + return w.initUtilityVM() + } + + if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 { + w.changedDi = append(w.changedDi, dirInfo{path: name, fileInfo: *fileInfo}) + } + + name = filepath.Clean(name) + if hasPathPrefix(name, utilityVMPath) { + if !w.HasUtilityVM { + return errors.New("missing UtilityVM directory") + } + if !hasPathPrefix(name, utilityVMFilesPath) && name != utilityVMFilesPath { + return errors.New("invalid UtilityVM layer") + } + createDisposition := uint32(winapi.FILE_OPEN) + if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 { + st, err := safefile.LstatRelative(name, w.destRoot) + if err != nil && !os.IsNotExist(err) { + return err + } + if st != nil { + // Delete the existing file/directory if it is not the same type as this directory. + existingAttr := st.Sys().(*syscall.Win32FileAttributeData).FileAttributes + if (uint32(fileInfo.FileAttributes)^existingAttr)&(syscall.FILE_ATTRIBUTE_DIRECTORY|syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 { + if err = safefile.RemoveAllRelative(name, w.destRoot); err != nil { + return err + } + st = nil + } + } + if st == nil { + if err = safefile.MkdirRelative(name, w.destRoot); err != nil { + return err + } + } + } else { + // Overwrite any existing hard link. + err := safefile.RemoveRelative(name, w.destRoot) + if err != nil && !os.IsNotExist(err) { + return err + } + createDisposition = winapi.FILE_CREATE + } + + f, err := safefile.OpenRelative( + name, + w.destRoot, + syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY, + syscall.FILE_SHARE_READ, + createDisposition, + winapi.FILE_OPEN_REPARSE_POINT, + ) + if err != nil { + return err + } + defer func() { + if f != nil { + f.Close() + _ = safefile.RemoveRelative(name, w.destRoot) + } + }() + + err = winio.SetFileBasicInfo(f, fileInfo) + if err != nil { + return err + } + + w.backupWriter = winio.NewBackupFileWriter(f, true) + w.bufWriter.Reset(w.backupWriter) + w.currentFile = f + w.currentFileName = name + w.currentFileRoot = w.destRoot + w.addedFiles[name] = true + f = nil + return nil + } + + fname := name + if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 { + err := safefile.MkdirRelative(name, w.root) + if err != nil { + return err + } + fname += ".$wcidirs$" + w.currentIsDir = true + } + + f, err := safefile.OpenRelative(fname, w.root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, winapi.FILE_CREATE, 0) + if err != nil { + return err + } + defer func() { + if f != nil { + f.Close() + _ = safefile.RemoveRelative(fname, w.root) + } + }() + + strippedFi := *fileInfo + strippedFi.FileAttributes = 0 + err = winio.SetFileBasicInfo(f, &strippedFi) + if err != nil { + return err + } + + if hasPathPrefix(name, hivesPath) { + w.backupWriter = winio.NewBackupFileWriter(f, false) + w.bufWriter.Reset(w.backupWriter) + } else { + w.bufWriter.Reset(f) + // The file attributes are written before the stream. + err = binary.Write(w.bufWriter, binary.LittleEndian, uint32(fileInfo.FileAttributes)) + if err != nil { + w.bufWriter.Reset(ioutil.Discard) + return err + } + } + + w.currentFile = f + w.currentFileName = name + w.currentFileRoot = w.root + w.addedFiles[name] = true + f = nil + return nil +} + +func (w *legacyLayerWriter) AddLink(name string, target string) error { + if err := w.reset(); err != nil { + return err + } + + target = filepath.Clean(target) + var roots []*os.File + if hasPathPrefix(target, filesPath) { + // Look for cross-layer hard link targets in the parent layers, since + // nothing is in the destination path yet. + roots = w.parentRoots + } else if hasPathPrefix(target, utilityVMFilesPath) { + // Since the utility VM is fully cloned into the destination path + // already, look for cross-layer hard link targets directly in the + // destination path. + roots = []*os.File{w.destRoot} + } + + if roots == nil || (!hasPathPrefix(name, filesPath) && !hasPathPrefix(name, utilityVMFilesPath)) { + return errors.New("invalid hard link in layer") + } + + // Find to try the target of the link in a previously added file. If that + // fails, search in parent layers. + var selectedRoot *os.File + if _, ok := w.addedFiles[target]; ok { + selectedRoot = w.destRoot + } else { + for _, r := range roots { + if _, err := safefile.LstatRelative(target, r); err != nil { + if !os.IsNotExist(err) { + return err + } + } else { + selectedRoot = r + break + } + } + if selectedRoot == nil { + return fmt.Errorf("failed to find link target for '%s' -> '%s'", name, target) + } + } + + // The link can't be written until after the ImportLayer call. + w.PendingLinks = append(w.PendingLinks, pendingLink{ + Path: name, + Target: target, + TargetRoot: selectedRoot, + }) + w.addedFiles[name] = true + return nil +} + +func (w *legacyLayerWriter) Remove(name string) error { + name = filepath.Clean(name) + if hasPathPrefix(name, filesPath) { + w.Tombstones = append(w.Tombstones, name) + } else if hasPathPrefix(name, utilityVMFilesPath) { + err := w.initUtilityVM() + if err != nil { + return err + } + // Make sure the path exists; os.RemoveAll will not fail if the file is + // already gone, and this needs to be a fatal error for diagnostics + // purposes. + if _, err := safefile.LstatRelative(name, w.destRoot); err != nil { + return err + } + err = safefile.RemoveAllRelative(name, w.destRoot) + if err != nil { + return err + } + } else { + return fmt.Errorf("invalid tombstone %s", name) + } + + return nil +} + +func (w *legacyLayerWriter) Write(b []byte) (int, error) { + if w.backupWriter == nil && w.currentFile == nil { + return 0, errors.New("closed") + } + return w.bufWriter.Write(b) +} + +func (w *legacyLayerWriter) Close() error { + if err := w.reset(); err != nil { + return err + } + if err := safefile.RemoveRelative("tombstones.txt", w.root); err != nil && !os.IsNotExist(err) { + return err + } + for _, pd := range w.pendingDirs { + err := safefile.MkdirRelative(pd.Path, pd.Root) + if err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go new file mode 100644 index 0000000000..09950297ce --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go @@ -0,0 +1,29 @@ +package wclayer + +import ( + "context" + + "github.com/Microsoft/go-winio/pkg/guid" + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" +) + +// NameToGuid converts the given string into a GUID using the algorithm in the +// Host Compute Service, ensuring GUIDs generated with the same string are common +// across all clients. +func NameToGuid(ctx context.Context, name string) (_ guid.GUID, err error) { + title := "hcsshim::NameToGuid" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("objectName", name)) + + var id guid.GUID + err = nameToGuid(name, &id) + if err != nil { + return guid.GUID{}, hcserror.New(err, title, "") + } + span.AddAttributes(trace.StringAttribute("guid", id.String())) + return id, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go new file mode 100644 index 0000000000..90129faefb --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go @@ -0,0 +1,44 @@ +package wclayer + +import ( + "context" + "strings" + "sync" + + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" +) + +var prepareLayerLock sync.Mutex + +// PrepareLayer finds a mounted read-write layer matching path and enables the +// the filesystem filter for use on that layer. This requires the paths to all +// parent layers, and is necessary in order to view or interact with the layer +// as an actual filesystem (reading and writing files, creating directories, etc). +// Disabling the filter must be done via UnprepareLayer. +func PrepareLayer(ctx context.Context, path string, parentLayerPaths []string) (err error) { + title := "hcsshim::PrepareLayer" + ctx, span := trace.StartSpan(ctx, title) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("path", path), + trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", "))) + + // Generate layer descriptors + layers, err := layerPathsToDescriptors(ctx, parentLayerPaths) + if err != nil { + return err + } + + // This lock is a temporary workaround for a Windows bug. Only allowing one + // call to prepareLayer at a time vastly reduces the chance of a timeout. + prepareLayerLock.Lock() + defer prepareLayerLock.Unlock() + err = prepareLayer(&stdDriverInfo, path, layers) + if err != nil { + return hcserror.New(err, title, "") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/processimage.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/processimage.go new file mode 100644 index 0000000000..30bcdff5f5 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/processimage.go @@ -0,0 +1,41 @@ +package wclayer + +import ( + "context" + "os" + + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" +) + +// ProcessBaseLayer post-processes a base layer that has had its files extracted. +// The files should have been extracted to \Files. +func ProcessBaseLayer(ctx context.Context, path string) (err error) { + title := "hcsshim::ProcessBaseLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) + + err = processBaseImage(path) + if err != nil { + return &os.PathError{Op: title, Path: path, Err: err} + } + return nil +} + +// ProcessUtilityVMImage post-processes a utility VM image that has had its files extracted. +// The files should have been extracted to \Files. +func ProcessUtilityVMImage(ctx context.Context, path string) (err error) { + title := "hcsshim::ProcessUtilityVMImage" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) + + err = processUtilityImage(path) + if err != nil { + return &os.PathError{Op: title, Path: path, Err: err} + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go new file mode 100644 index 0000000000..71b130c525 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go @@ -0,0 +1,25 @@ +package wclayer + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" +) + +// UnprepareLayer disables the filesystem filter for the read-write layer with +// the given id. +func UnprepareLayer(ctx context.Context, path string) (err error) { + title := "hcsshim::UnprepareLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) + + err = unprepareLayer(&stdDriverInfo, path) + if err != nil { + return hcserror.New(err, title, "") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/wclayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/wclayer.go new file mode 100644 index 0000000000..9b1e06d50c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/wclayer.go @@ -0,0 +1,35 @@ +// Package wclayer provides bindings to HCS's legacy layer management API and +// provides a higher level interface around these calls for container layer +// management. +package wclayer + +import "github.com/Microsoft/go-winio/pkg/guid" + +//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go wclayer.go + +//sys activateLayer(info *driverInfo, id string) (hr error) = vmcompute.ActivateLayer? +//sys copyLayer(info *driverInfo, srcId string, dstId string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.CopyLayer? +//sys createLayer(info *driverInfo, id string, parent string) (hr error) = vmcompute.CreateLayer? +//sys createSandboxLayer(info *driverInfo, id string, parent uintptr, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.CreateSandboxLayer? +//sys expandSandboxSize(info *driverInfo, id string, size uint64) (hr error) = vmcompute.ExpandSandboxSize? +//sys deactivateLayer(info *driverInfo, id string) (hr error) = vmcompute.DeactivateLayer? +//sys destroyLayer(info *driverInfo, id string) (hr error) = vmcompute.DestroyLayer? +//sys exportLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.ExportLayer? +//sys getLayerMountPath(info *driverInfo, id string, length *uintptr, buffer *uint16) (hr error) = vmcompute.GetLayerMountPath? +//sys getBaseImages(buffer **uint16) (hr error) = vmcompute.GetBaseImages? +//sys importLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.ImportLayer? +//sys layerExists(info *driverInfo, id string, exists *uint32) (hr error) = vmcompute.LayerExists? +//sys nameToGuid(name string, guid *_guid) (hr error) = vmcompute.NameToGuid? +//sys prepareLayer(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.PrepareLayer? +//sys unprepareLayer(info *driverInfo, id string) (hr error) = vmcompute.UnprepareLayer? +//sys processBaseImage(path string) (hr error) = vmcompute.ProcessBaseImage? +//sys processUtilityImage(path string) (hr error) = vmcompute.ProcessUtilityImage? + +//sys grantVmAccess(vmid string, filepath string) (hr error) = vmcompute.GrantVmAccess? + +//sys openVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, flags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (err error) [failretval != 0] = virtdisk.OpenVirtualDisk +//sys attachVirtualDisk(handle syscall.Handle, sd uintptr, flags uint32, providerFlags uint32, params uintptr, overlapped uintptr) (err error) [failretval != 0] = virtdisk.AttachVirtualDisk + +//sys getDiskFreeSpaceEx(directoryName string, freeBytesAvailableToCaller *int64, totalNumberOfBytes *int64, totalNumberOfFreeBytes *int64) (err error) = GetDiskFreeSpaceExW + +type _guid = guid.GUID diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/zsyscall_windows.go new file mode 100644 index 0000000000..67f917f07e --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/zsyscall_windows.go @@ -0,0 +1,569 @@ +// Code generated mksyscall_windows.exe DO NOT EDIT + +package wclayer + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modvmcompute = windows.NewLazySystemDLL("vmcompute.dll") + modvirtdisk = windows.NewLazySystemDLL("virtdisk.dll") + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + + procActivateLayer = modvmcompute.NewProc("ActivateLayer") + procCopyLayer = modvmcompute.NewProc("CopyLayer") + procCreateLayer = modvmcompute.NewProc("CreateLayer") + procCreateSandboxLayer = modvmcompute.NewProc("CreateSandboxLayer") + procExpandSandboxSize = modvmcompute.NewProc("ExpandSandboxSize") + procDeactivateLayer = modvmcompute.NewProc("DeactivateLayer") + procDestroyLayer = modvmcompute.NewProc("DestroyLayer") + procExportLayer = modvmcompute.NewProc("ExportLayer") + procGetLayerMountPath = modvmcompute.NewProc("GetLayerMountPath") + procGetBaseImages = modvmcompute.NewProc("GetBaseImages") + procImportLayer = modvmcompute.NewProc("ImportLayer") + procLayerExists = modvmcompute.NewProc("LayerExists") + procNameToGuid = modvmcompute.NewProc("NameToGuid") + procPrepareLayer = modvmcompute.NewProc("PrepareLayer") + procUnprepareLayer = modvmcompute.NewProc("UnprepareLayer") + procProcessBaseImage = modvmcompute.NewProc("ProcessBaseImage") + procProcessUtilityImage = modvmcompute.NewProc("ProcessUtilityImage") + procGrantVmAccess = modvmcompute.NewProc("GrantVmAccess") + procOpenVirtualDisk = modvirtdisk.NewProc("OpenVirtualDisk") + procAttachVirtualDisk = modvirtdisk.NewProc("AttachVirtualDisk") + procGetDiskFreeSpaceExW = modkernel32.NewProc("GetDiskFreeSpaceExW") +) + +func activateLayer(info *driverInfo, id string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _activateLayer(info, _p0) +} + +func _activateLayer(info *driverInfo, id *uint16) (hr error) { + if hr = procActivateLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procActivateLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func copyLayer(info *driverInfo, srcId string, dstId string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(srcId) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(dstId) + if hr != nil { + return + } + return _copyLayer(info, _p0, _p1, descriptors) +} + +func _copyLayer(info *driverInfo, srcId *uint16, dstId *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p2 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p2 = &descriptors[0] + } + if hr = procCopyLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procCopyLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(srcId)), uintptr(unsafe.Pointer(dstId)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func createLayer(info *driverInfo, id string, parent string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(parent) + if hr != nil { + return + } + return _createLayer(info, _p0, _p1) +} + +func _createLayer(info *driverInfo, id *uint16, parent *uint16) (hr error) { + if hr = procCreateLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procCreateLayer.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(parent))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func createSandboxLayer(info *driverInfo, id string, parent uintptr, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _createSandboxLayer(info, _p0, parent, descriptors) +} + +func _createSandboxLayer(info *driverInfo, id *uint16, parent uintptr, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p1 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p1 = &descriptors[0] + } + if hr = procCreateSandboxLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procCreateSandboxLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(parent), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func expandSandboxSize(info *driverInfo, id string, size uint64) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _expandSandboxSize(info, _p0, size) +} + +func _expandSandboxSize(info *driverInfo, id *uint16, size uint64) (hr error) { + if hr = procExpandSandboxSize.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procExpandSandboxSize.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(size)) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func deactivateLayer(info *driverInfo, id string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _deactivateLayer(info, _p0) +} + +func _deactivateLayer(info *driverInfo, id *uint16) (hr error) { + if hr = procDeactivateLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procDeactivateLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func destroyLayer(info *driverInfo, id string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _destroyLayer(info, _p0) +} + +func _destroyLayer(info *driverInfo, id *uint16) (hr error) { + if hr = procDestroyLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procDestroyLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func exportLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + return _exportLayer(info, _p0, _p1, descriptors) +} + +func _exportLayer(info *driverInfo, id *uint16, path *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p2 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p2 = &descriptors[0] + } + if hr = procExportLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procExportLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func getLayerMountPath(info *driverInfo, id string, length *uintptr, buffer *uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _getLayerMountPath(info, _p0, length, buffer) +} + +func _getLayerMountPath(info *driverInfo, id *uint16, length *uintptr, buffer *uint16) (hr error) { + if hr = procGetLayerMountPath.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procGetLayerMountPath.Addr(), 4, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(length)), uintptr(unsafe.Pointer(buffer)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func getBaseImages(buffer **uint16) (hr error) { + if hr = procGetBaseImages.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procGetBaseImages.Addr(), 1, uintptr(unsafe.Pointer(buffer)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func importLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + return _importLayer(info, _p0, _p1, descriptors) +} + +func _importLayer(info *driverInfo, id *uint16, path *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p2 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p2 = &descriptors[0] + } + if hr = procImportLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procImportLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func layerExists(info *driverInfo, id string, exists *uint32) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _layerExists(info, _p0, exists) +} + +func _layerExists(info *driverInfo, id *uint16, exists *uint32) (hr error) { + if hr = procLayerExists.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procLayerExists.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(exists))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func nameToGuid(name string, guid *_guid) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(name) + if hr != nil { + return + } + return _nameToGuid(_p0, guid) +} + +func _nameToGuid(name *uint16, guid *_guid) (hr error) { + if hr = procNameToGuid.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procNameToGuid.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(guid)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func prepareLayer(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _prepareLayer(info, _p0, descriptors) +} + +func _prepareLayer(info *driverInfo, id *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p1 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p1 = &descriptors[0] + } + if hr = procPrepareLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procPrepareLayer.Addr(), 4, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func unprepareLayer(info *driverInfo, id string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _unprepareLayer(info, _p0) +} + +func _unprepareLayer(info *driverInfo, id *uint16) (hr error) { + if hr = procUnprepareLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procUnprepareLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func processBaseImage(path string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + return _processBaseImage(_p0) +} + +func _processBaseImage(path *uint16) (hr error) { + if hr = procProcessBaseImage.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procProcessBaseImage.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func processUtilityImage(path string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + return _processUtilityImage(_p0) +} + +func _processUtilityImage(path *uint16) (hr error) { + if hr = procProcessUtilityImage.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procProcessUtilityImage.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func grantVmAccess(vmid string, filepath string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(vmid) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(filepath) + if hr != nil { + return + } + return _grantVmAccess(_p0, _p1) +} + +func _grantVmAccess(vmid *uint16, filepath *uint16) (hr error) { + if hr = procGrantVmAccess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procGrantVmAccess.Addr(), 2, uintptr(unsafe.Pointer(vmid)), uintptr(unsafe.Pointer(filepath)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func openVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, flags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(path) + if err != nil { + return + } + return _openVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, flags, parameters, handle) +} + +func _openVirtualDisk(virtualStorageType *virtualStorageType, path *uint16, virtualDiskAccessMask uint32, flags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (err error) { + r1, _, e1 := syscall.Syscall6(procOpenVirtualDisk.Addr(), 6, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(flags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(handle))) + if r1 != 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func attachVirtualDisk(handle syscall.Handle, sd uintptr, flags uint32, providerFlags uint32, params uintptr, overlapped uintptr) (err error) { + r1, _, e1 := syscall.Syscall6(procAttachVirtualDisk.Addr(), 6, uintptr(handle), uintptr(sd), uintptr(flags), uintptr(providerFlags), uintptr(params), uintptr(overlapped)) + if r1 != 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getDiskFreeSpaceEx(directoryName string, freeBytesAvailableToCaller *int64, totalNumberOfBytes *int64, totalNumberOfFreeBytes *int64) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(directoryName) + if err != nil { + return + } + return _getDiskFreeSpaceEx(_p0, freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes) +} + +func _getDiskFreeSpaceEx(directoryName *uint16, freeBytesAvailableToCaller *int64, totalNumberOfBytes *int64, totalNumberOfFreeBytes *int64) (err error) { + r1, _, e1 := syscall.Syscall6(procGetDiskFreeSpaceExW.Addr(), 4, uintptr(unsafe.Pointer(directoryName)), uintptr(unsafe.Pointer(freeBytesAvailableToCaller)), uintptr(unsafe.Pointer(totalNumberOfBytes)), uintptr(unsafe.Pointer(totalNumberOfFreeBytes)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/console.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/console.go new file mode 100644 index 0000000000..def9525417 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/console.go @@ -0,0 +1,44 @@ +package winapi + +import ( + "unsafe" + + "golang.org/x/sys/windows" +) + +const PSEUDOCONSOLE_INHERIT_CURSOR = 0x1 + +// CreatePseudoConsole creates a windows pseudo console. +func CreatePseudoConsole(size windows.Coord, hInput windows.Handle, hOutput windows.Handle, dwFlags uint32, hpcon *windows.Handle) error { + // We need this wrapper as the function takes a COORD struct and not a pointer to one, so we need to cast to something beforehand. + return createPseudoConsole(*((*uint32)(unsafe.Pointer(&size))), hInput, hOutput, 0, hpcon) +} + +// ResizePseudoConsole resizes the internal buffers of the pseudo console to the width and height specified in `size`. +func ResizePseudoConsole(hpcon windows.Handle, size windows.Coord) error { + // We need this wrapper as the function takes a COORD struct and not a pointer to one, so we need to cast to something beforehand. + return resizePseudoConsole(hpcon, *((*uint32)(unsafe.Pointer(&size)))) +} + +// HRESULT WINAPI CreatePseudoConsole( +// _In_ COORD size, +// _In_ HANDLE hInput, +// _In_ HANDLE hOutput, +// _In_ DWORD dwFlags, +// _Out_ HPCON* phPC +// ); +// +//sys createPseudoConsole(size uint32, hInput windows.Handle, hOutput windows.Handle, dwFlags uint32, hpcon *windows.Handle) (hr error) = kernel32.CreatePseudoConsole + +// void WINAPI ClosePseudoConsole( +// _In_ HPCON hPC +// ); +// +//sys ClosePseudoConsole(hpc windows.Handle) = kernel32.ClosePseudoConsole + +// HRESULT WINAPI ResizePseudoConsole( +// _In_ HPCON hPC , +// _In_ COORD size +// ); +// +//sys resizePseudoConsole(hPc windows.Handle, size uint32) (hr error) = kernel32.ResizePseudoConsole diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/devices.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/devices.go new file mode 100644 index 0000000000..df28ea2421 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/devices.go @@ -0,0 +1,13 @@ +package winapi + +import "github.com/Microsoft/go-winio/pkg/guid" + +//sys CMGetDeviceIDListSize(pulLen *uint32, pszFilter *byte, uFlags uint32) (hr error) = cfgmgr32.CM_Get_Device_ID_List_SizeA +//sys CMGetDeviceIDList(pszFilter *byte, buffer *byte, bufferLen uint32, uFlags uint32) (hr error)= cfgmgr32.CM_Get_Device_ID_ListA +//sys CMLocateDevNode(pdnDevInst *uint32, pDeviceID string, uFlags uint32) (hr error) = cfgmgr32.CM_Locate_DevNodeW +//sys CMGetDevNodeProperty(dnDevInst uint32, propertyKey *DevPropKey, propertyType *uint32, propertyBuffer *uint16, propertyBufferSize *uint32, uFlags uint32) (hr error) = cfgmgr32.CM_Get_DevNode_PropertyW + +type DevPropKey struct { + Fmtid guid.GUID + Pid uint32 +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/errors.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/errors.go new file mode 100644 index 0000000000..4e80ef68c9 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/errors.go @@ -0,0 +1,15 @@ +package winapi + +import "syscall" + +//sys RtlNtStatusToDosError(status uint32) (winerr error) = ntdll.RtlNtStatusToDosError + +const ( + STATUS_REPARSE_POINT_ENCOUNTERED = 0xC000050B + ERROR_NO_MORE_ITEMS = 0x103 + ERROR_MORE_DATA syscall.Errno = 234 +) + +func NTSuccess(status uint32) bool { + return status == 0 +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/filesystem.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/filesystem.go new file mode 100644 index 0000000000..7ce52afd5e --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/filesystem.go @@ -0,0 +1,110 @@ +package winapi + +//sys NtCreateFile(handle *uintptr, accessMask uint32, oa *ObjectAttributes, iosb *IOStatusBlock, allocationSize *uint64, fileAttributes uint32, shareAccess uint32, createDisposition uint32, createOptions uint32, eaBuffer *byte, eaLength uint32) (status uint32) = ntdll.NtCreateFile +//sys NtSetInformationFile(handle uintptr, iosb *IOStatusBlock, information uintptr, length uint32, class uint32) (status uint32) = ntdll.NtSetInformationFile + +//sys NtOpenDirectoryObject(handle *uintptr, accessMask uint32, oa *ObjectAttributes) (status uint32) = ntdll.NtOpenDirectoryObject +//sys NtQueryDirectoryObject(handle uintptr, buffer *byte, length uint32, singleEntry bool, restartScan bool, context *uint32, returnLength *uint32)(status uint32) = ntdll.NtQueryDirectoryObject + +const ( + FileLinkInformationClass = 11 + FileDispositionInformationExClass = 64 + + FILE_READ_ATTRIBUTES = 0x0080 + FILE_WRITE_ATTRIBUTES = 0x0100 + DELETE = 0x10000 + + FILE_OPEN = 1 + FILE_CREATE = 2 + + FILE_LIST_DIRECTORY = 0x00000001 + FILE_DIRECTORY_FILE = 0x00000001 + FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020 + FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000 + FILE_OPEN_REPARSE_POINT = 0x00200000 + + FILE_DISPOSITION_DELETE = 0x00000001 + + OBJ_DONT_REPARSE = 0x1000 + + STATUS_MORE_ENTRIES = 0x105 + STATUS_NO_MORE_ENTRIES = 0x8000001a +) + +// Select entries from FILE_INFO_BY_HANDLE_CLASS. +// +// C declaration: +// typedef enum _FILE_INFO_BY_HANDLE_CLASS { +// FileBasicInfo, +// FileStandardInfo, +// FileNameInfo, +// FileRenameInfo, +// FileDispositionInfo, +// FileAllocationInfo, +// FileEndOfFileInfo, +// FileStreamInfo, +// FileCompressionInfo, +// FileAttributeTagInfo, +// FileIdBothDirectoryInfo, +// FileIdBothDirectoryRestartInfo, +// FileIoPriorityHintInfo, +// FileRemoteProtocolInfo, +// FileFullDirectoryInfo, +// FileFullDirectoryRestartInfo, +// FileStorageInfo, +// FileAlignmentInfo, +// FileIdInfo, +// FileIdExtdDirectoryInfo, +// FileIdExtdDirectoryRestartInfo, +// FileDispositionInfoEx, +// FileRenameInfoEx, +// FileCaseSensitiveInfo, +// FileNormalizedNameInfo, +// MaximumFileInfoByHandleClass +// } FILE_INFO_BY_HANDLE_CLASS, *PFILE_INFO_BY_HANDLE_CLASS; +// +// Documentation: https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ne-minwinbase-file_info_by_handle_class +const ( + FileIdInfo = 18 +) + +type FileDispositionInformationEx struct { + Flags uintptr +} + +type IOStatusBlock struct { + Status, Information uintptr +} + +type ObjectAttributes struct { + Length uintptr + RootDirectory uintptr + ObjectName *UnicodeString + Attributes uintptr + SecurityDescriptor uintptr + SecurityQoS uintptr +} + +type ObjectDirectoryInformation struct { + Name UnicodeString + TypeName UnicodeString +} + +type FileLinkInformation struct { + ReplaceIfExists bool + RootDirectory uintptr + FileNameLength uint32 + FileName [1]uint16 +} + +// C declaration: +// typedef struct _FILE_ID_INFO { +// ULONGLONG VolumeSerialNumber; +// FILE_ID_128 FileId; +// } FILE_ID_INFO, *PFILE_ID_INFO; +// +// Documentation: https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_info +type FILE_ID_INFO struct { + VolumeSerialNumber uint64 + FileID [16]byte +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/iocp.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/iocp.go new file mode 100644 index 0000000000..4e609cbf1c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/iocp.go @@ -0,0 +1,3 @@ +package winapi + +//sys GetQueuedCompletionStatus(cphandle windows.Handle, qty *uint32, key *uintptr, overlapped **windows.Overlapped, timeout uint32) (err error) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/jobobject.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/jobobject.go new file mode 100644 index 0000000000..ba12b1ad92 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/jobobject.go @@ -0,0 +1,215 @@ +package winapi + +import ( + "unsafe" + + "golang.org/x/sys/windows" +) + +// Messages that can be received from an assigned io completion port. +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_associate_completion_port +const ( + JOB_OBJECT_MSG_END_OF_JOB_TIME uint32 = 1 + JOB_OBJECT_MSG_END_OF_PROCESS_TIME uint32 = 2 + JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT uint32 = 3 + JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO uint32 = 4 + JOB_OBJECT_MSG_NEW_PROCESS uint32 = 6 + JOB_OBJECT_MSG_EXIT_PROCESS uint32 = 7 + JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS uint32 = 8 + JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT uint32 = 9 + JOB_OBJECT_MSG_JOB_MEMORY_LIMIT uint32 = 10 + JOB_OBJECT_MSG_NOTIFICATION_LIMIT uint32 = 11 +) + +// Access rights for creating or opening job objects. +// +// https://docs.microsoft.com/en-us/windows/win32/procthread/job-object-security-and-access-rights +const JOB_OBJECT_ALL_ACCESS = 0x1F001F + +// IO limit flags +// +// https://docs.microsoft.com/en-us/windows/win32/api/jobapi2/ns-jobapi2-jobobject_io_rate_control_information +const JOB_OBJECT_IO_RATE_CONTROL_ENABLE = 0x1 + +const JOBOBJECT_IO_ATTRIBUTION_CONTROL_ENABLE uint32 = 0x1 + +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_cpu_rate_control_information +const ( + JOB_OBJECT_CPU_RATE_CONTROL_ENABLE uint32 = 1 << iota + JOB_OBJECT_CPU_RATE_CONTROL_WEIGHT_BASED + JOB_OBJECT_CPU_RATE_CONTROL_HARD_CAP + JOB_OBJECT_CPU_RATE_CONTROL_NOTIFY + JOB_OBJECT_CPU_RATE_CONTROL_MIN_MAX_RATE +) + +// JobObjectInformationClass values. Used for a call to QueryInformationJobObject +// +// https://docs.microsoft.com/en-us/windows/win32/api/jobapi2/nf-jobapi2-queryinformationjobobject +const ( + JobObjectBasicAccountingInformation uint32 = 1 + JobObjectBasicProcessIdList uint32 = 3 + JobObjectBasicAndIoAccountingInformation uint32 = 8 + JobObjectLimitViolationInformation uint32 = 13 + JobObjectMemoryUsageInformation uint32 = 28 + JobObjectNotificationLimitInformation2 uint32 = 33 + JobObjectIoAttribution uint32 = 42 +) + +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_basic_limit_information +type JOBOBJECT_BASIC_LIMIT_INFORMATION struct { + PerProcessUserTimeLimit int64 + PerJobUserTimeLimit int64 + LimitFlags uint32 + MinimumWorkingSetSize uintptr + MaximumWorkingSetSize uintptr + ActiveProcessLimit uint32 + Affinity uintptr + PriorityClass uint32 + SchedulingClass uint32 +} + +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_cpu_rate_control_information +type JOBOBJECT_CPU_RATE_CONTROL_INFORMATION struct { + ControlFlags uint32 + Value uint32 +} + +// https://docs.microsoft.com/en-us/windows/win32/api/jobapi2/ns-jobapi2-jobobject_io_rate_control_information +type JOBOBJECT_IO_RATE_CONTROL_INFORMATION struct { + MaxIops int64 + MaxBandwidth int64 + ReservationIops int64 + BaseIOSize uint32 + VolumeName string + ControlFlags uint32 +} + +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_basic_process_id_list +type JOBOBJECT_BASIC_PROCESS_ID_LIST struct { + NumberOfAssignedProcesses uint32 + NumberOfProcessIdsInList uint32 + ProcessIdList [1]uintptr +} + +// AllPids returns all the process Ids in the job object. +func (p *JOBOBJECT_BASIC_PROCESS_ID_LIST) AllPids() []uintptr { + return (*[(1 << 27) - 1]uintptr)(unsafe.Pointer(&p.ProcessIdList[0]))[:p.NumberOfProcessIdsInList] +} + +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_basic_accounting_information +type JOBOBJECT_BASIC_ACCOUNTING_INFORMATION struct { + TotalUserTime int64 + TotalKernelTime int64 + ThisPeriodTotalUserTime int64 + ThisPeriodTotalKernelTime int64 + TotalPageFaultCount uint32 + TotalProcesses uint32 + ActiveProcesses uint32 + TotalTerminateProcesses uint32 +} + +//https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_basic_and_io_accounting_information +type JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION struct { + BasicInfo JOBOBJECT_BASIC_ACCOUNTING_INFORMATION + IoInfo windows.IO_COUNTERS +} + +// typedef struct _JOBOBJECT_MEMORY_USAGE_INFORMATION { +// ULONG64 JobMemory; +// ULONG64 PeakJobMemoryUsed; +// } JOBOBJECT_MEMORY_USAGE_INFORMATION, *PJOBOBJECT_MEMORY_USAGE_INFORMATION; +// +type JOBOBJECT_MEMORY_USAGE_INFORMATION struct { + JobMemory uint64 + PeakJobMemoryUsed uint64 +} + +// typedef struct _JOBOBJECT_IO_ATTRIBUTION_STATS { +// ULONG_PTR IoCount; +// ULONGLONG TotalNonOverlappedQueueTime; +// ULONGLONG TotalNonOverlappedServiceTime; +// ULONGLONG TotalSize; +// } JOBOBJECT_IO_ATTRIBUTION_STATS, *PJOBOBJECT_IO_ATTRIBUTION_STATS; +// +type JOBOBJECT_IO_ATTRIBUTION_STATS struct { + IoCount uintptr + TotalNonOverlappedQueueTime uint64 + TotalNonOverlappedServiceTime uint64 + TotalSize uint64 +} + +// typedef struct _JOBOBJECT_IO_ATTRIBUTION_INFORMATION { +// ULONG ControlFlags; +// JOBOBJECT_IO_ATTRIBUTION_STATS ReadStats; +// JOBOBJECT_IO_ATTRIBUTION_STATS WriteStats; +// } JOBOBJECT_IO_ATTRIBUTION_INFORMATION, *PJOBOBJECT_IO_ATTRIBUTION_INFORMATION; +// +type JOBOBJECT_IO_ATTRIBUTION_INFORMATION struct { + ControlFlags uint32 + ReadStats JOBOBJECT_IO_ATTRIBUTION_STATS + WriteStats JOBOBJECT_IO_ATTRIBUTION_STATS +} + +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_associate_completion_port +type JOBOBJECT_ASSOCIATE_COMPLETION_PORT struct { + CompletionKey windows.Handle + CompletionPort windows.Handle +} + +// BOOL IsProcessInJob( +// HANDLE ProcessHandle, +// HANDLE JobHandle, +// PBOOL Result +// ); +// +//sys IsProcessInJob(procHandle windows.Handle, jobHandle windows.Handle, result *bool) (err error) = kernel32.IsProcessInJob + +// BOOL QueryInformationJobObject( +// HANDLE hJob, +// JOBOBJECTINFOCLASS JobObjectInformationClass, +// LPVOID lpJobObjectInformation, +// DWORD cbJobObjectInformationLength, +// LPDWORD lpReturnLength +// ); +// +//sys QueryInformationJobObject(jobHandle windows.Handle, infoClass uint32, jobObjectInfo uintptr, jobObjectInformationLength uint32, lpReturnLength *uint32) (err error) = kernel32.QueryInformationJobObject + +// HANDLE OpenJobObjectW( +// DWORD dwDesiredAccess, +// BOOL bInheritHandle, +// LPCWSTR lpName +// ); +// +//sys OpenJobObject(desiredAccess uint32, inheritHandle bool, lpName *uint16) (handle windows.Handle, err error) = kernel32.OpenJobObjectW + +// DWORD SetIoRateControlInformationJobObject( +// HANDLE hJob, +// JOBOBJECT_IO_RATE_CONTROL_INFORMATION *IoRateControlInfo +// ); +// +//sys SetIoRateControlInformationJobObject(jobHandle windows.Handle, ioRateControlInfo *JOBOBJECT_IO_RATE_CONTROL_INFORMATION) (ret uint32, err error) = kernel32.SetIoRateControlInformationJobObject + +// DWORD QueryIoRateControlInformationJobObject( +// HANDLE hJob, +// PCWSTR VolumeName, +// JOBOBJECT_IO_RATE_CONTROL_INFORMATION **InfoBlocks, +// ULONG *InfoBlockCount +// ); +//sys QueryIoRateControlInformationJobObject(jobHandle windows.Handle, volumeName *uint16, ioRateControlInfo **JOBOBJECT_IO_RATE_CONTROL_INFORMATION, infoBlockCount *uint32) (ret uint32, err error) = kernel32.QueryIoRateControlInformationJobObject + +// NTSTATUS +// NtOpenJobObject ( +// _Out_ PHANDLE JobHandle, +// _In_ ACCESS_MASK DesiredAccess, +// _In_ POBJECT_ATTRIBUTES ObjectAttributes +// ); +//sys NtOpenJobObject(jobHandle *windows.Handle, desiredAccess uint32, objAttributes *ObjectAttributes) (status uint32) = ntdll.NtOpenJobObject + +// NTSTATUS +// NTAPI +// NtCreateJobObject ( +// _Out_ PHANDLE JobHandle, +// _In_ ACCESS_MASK DesiredAccess, +// _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes +// ); +//sys NtCreateJobObject(jobHandle *windows.Handle, desiredAccess uint32, objAttributes *ObjectAttributes) (status uint32) = ntdll.NtCreateJobObject diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/logon.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/logon.go new file mode 100644 index 0000000000..b6e7cfd460 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/logon.go @@ -0,0 +1,30 @@ +package winapi + +// BOOL LogonUserA( +// LPCWSTR lpszUsername, +// LPCWSTR lpszDomain, +// LPCWSTR lpszPassword, +// DWORD dwLogonType, +// DWORD dwLogonProvider, +// PHANDLE phToken +// ); +// +//sys LogonUser(username *uint16, domain *uint16, password *uint16, logonType uint32, logonProvider uint32, token *windows.Token) (err error) = advapi32.LogonUserW + +// Logon types +const ( + LOGON32_LOGON_INTERACTIVE uint32 = 2 + LOGON32_LOGON_NETWORK uint32 = 3 + LOGON32_LOGON_BATCH uint32 = 4 + LOGON32_LOGON_SERVICE uint32 = 5 + LOGON32_LOGON_UNLOCK uint32 = 7 + LOGON32_LOGON_NETWORK_CLEARTEXT uint32 = 8 + LOGON32_LOGON_NEW_CREDENTIALS uint32 = 9 +) + +// Logon providers +const ( + LOGON32_PROVIDER_DEFAULT uint32 = 0 + LOGON32_PROVIDER_WINNT40 uint32 = 2 + LOGON32_PROVIDER_WINNT50 uint32 = 3 +) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/memory.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/memory.go new file mode 100644 index 0000000000..53f62948c9 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/memory.go @@ -0,0 +1,4 @@ +package winapi + +//sys LocalAlloc(flags uint32, size int) (ptr uintptr) = kernel32.LocalAlloc +//sys LocalFree(ptr uintptr) = kernel32.LocalFree diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/net.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/net.go new file mode 100644 index 0000000000..f37910024f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/net.go @@ -0,0 +1,3 @@ +package winapi + +//sys SetJobCompartmentId(handle windows.Handle, compartmentId uint32) (win32Err error) = iphlpapi.SetJobCompartmentId diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/path.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/path.go new file mode 100644 index 0000000000..908920e872 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/path.go @@ -0,0 +1,11 @@ +package winapi + +// DWORD SearchPathW( +// LPCWSTR lpPath, +// LPCWSTR lpFileName, +// LPCWSTR lpExtension, +// DWORD nBufferLength, +// LPWSTR lpBuffer, +// LPWSTR *lpFilePart +// ); +//sys SearchPath(lpPath *uint16, lpFileName *uint16, lpExtension *uint16, nBufferLength uint32, lpBuffer *uint16, lpFilePath *uint16) (size uint32, err error) = kernel32.SearchPathW diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/process.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/process.go new file mode 100644 index 0000000000..37839435b9 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/process.go @@ -0,0 +1,8 @@ +package winapi + +const PROCESS_ALL_ACCESS uint32 = 2097151 + +const ( + PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE = 0x20016 + PROC_THREAD_ATTRIBUTE_JOB_LIST = 0x2000D +) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/processor.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/processor.go new file mode 100644 index 0000000000..ce79ac2cdb --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/processor.go @@ -0,0 +1,7 @@ +package winapi + +// Get count from all processor groups. +// https://docs.microsoft.com/en-us/windows/win32/procthread/processor-groups +const ALL_PROCESSOR_GROUPS = 0xFFFF + +//sys GetActiveProcessorCount(groupNumber uint16) (amount uint32) = kernel32.GetActiveProcessorCount diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/system.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/system.go new file mode 100644 index 0000000000..327f57d7c2 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/system.go @@ -0,0 +1,52 @@ +package winapi + +import "golang.org/x/sys/windows" + +const SystemProcessInformation = 5 + +const STATUS_INFO_LENGTH_MISMATCH = 0xC0000004 + +// __kernel_entry NTSTATUS NtQuerySystemInformation( +// SYSTEM_INFORMATION_CLASS SystemInformationClass, +// PVOID SystemInformation, +// ULONG SystemInformationLength, +// PULONG ReturnLength +// ); +//sys NtQuerySystemInformation(systemInfoClass int, systemInformation uintptr, systemInfoLength uint32, returnLength *uint32) (status uint32) = ntdll.NtQuerySystemInformation + +type SYSTEM_PROCESS_INFORMATION struct { + NextEntryOffset uint32 // ULONG + NumberOfThreads uint32 // ULONG + WorkingSetPrivateSize int64 // LARGE_INTEGER + HardFaultCount uint32 // ULONG + NumberOfThreadsHighWatermark uint32 // ULONG + CycleTime uint64 // ULONGLONG + CreateTime int64 // LARGE_INTEGER + UserTime int64 // LARGE_INTEGER + KernelTime int64 // LARGE_INTEGER + ImageName UnicodeString // UNICODE_STRING + BasePriority int32 // KPRIORITY + UniqueProcessID windows.Handle // HANDLE + InheritedFromUniqueProcessID windows.Handle // HANDLE + HandleCount uint32 // ULONG + SessionID uint32 // ULONG + UniqueProcessKey *uint32 // ULONG_PTR + PeakVirtualSize uintptr // SIZE_T + VirtualSize uintptr // SIZE_T + PageFaultCount uint32 // ULONG + PeakWorkingSetSize uintptr // SIZE_T + WorkingSetSize uintptr // SIZE_T + QuotaPeakPagedPoolUsage uintptr // SIZE_T + QuotaPagedPoolUsage uintptr // SIZE_T + QuotaPeakNonPagedPoolUsage uintptr // SIZE_T + QuotaNonPagedPoolUsage uintptr // SIZE_T + PagefileUsage uintptr // SIZE_T + PeakPagefileUsage uintptr // SIZE_T + PrivatePageCount uintptr // SIZE_T + ReadOperationCount int64 // LARGE_INTEGER + WriteOperationCount int64 // LARGE_INTEGER + OtherOperationCount int64 // LARGE_INTEGER + ReadTransferCount int64 // LARGE_INTEGER + WriteTransferCount int64 // LARGE_INTEGER + OtherTransferCount int64 // LARGE_INTEGER +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/thread.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/thread.go new file mode 100644 index 0000000000..4724713e3e --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/thread.go @@ -0,0 +1,12 @@ +package winapi + +// HANDLE CreateRemoteThread( +// HANDLE hProcess, +// LPSECURITY_ATTRIBUTES lpThreadAttributes, +// SIZE_T dwStackSize, +// LPTHREAD_START_ROUTINE lpStartAddress, +// LPVOID lpParameter, +// DWORD dwCreationFlags, +// LPDWORD lpThreadId +// ); +//sys CreateRemoteThread(process windows.Handle, sa *windows.SecurityAttributes, stackSize uint32, startAddr uintptr, parameter uintptr, creationFlags uint32, threadID *uint32) (handle windows.Handle, err error) = kernel32.CreateRemoteThread diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/utils.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/utils.go new file mode 100644 index 0000000000..859b753c24 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/utils.go @@ -0,0 +1,80 @@ +package winapi + +import ( + "errors" + "reflect" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +// Uint16BufferToSlice wraps a uint16 pointer-and-length into a slice +// for easier interop with Go APIs +func Uint16BufferToSlice(buffer *uint16, bufferLength int) (result []uint16) { + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&result)) + hdr.Data = uintptr(unsafe.Pointer(buffer)) + hdr.Cap = bufferLength + hdr.Len = bufferLength + + return +} + +// UnicodeString corresponds to UNICODE_STRING win32 struct defined here +// https://docs.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_unicode_string +type UnicodeString struct { + Length uint16 + MaximumLength uint16 + Buffer *uint16 +} + +// NTSTRSAFE_UNICODE_STRING_MAX_CCH is a constant defined in ntstrsafe.h. This value +// denotes the maximum number of wide chars a path can have. +const NTSTRSAFE_UNICODE_STRING_MAX_CCH = 32767 + +//String converts a UnicodeString to a golang string +func (uni UnicodeString) String() string { + // UnicodeString is not guaranteed to be null terminated, therefore + // use the UnicodeString's Length field + return windows.UTF16ToString(Uint16BufferToSlice(uni.Buffer, int(uni.Length/2))) +} + +// NewUnicodeString allocates a new UnicodeString and copies `s` into +// the buffer of the new UnicodeString. +func NewUnicodeString(s string) (*UnicodeString, error) { + buf, err := windows.UTF16FromString(s) + if err != nil { + return nil, err + } + + if len(buf) > NTSTRSAFE_UNICODE_STRING_MAX_CCH { + return nil, syscall.ENAMETOOLONG + } + + uni := &UnicodeString{ + // The length is in bytes and should not include the trailing null character. + Length: uint16((len(buf) - 1) * 2), + MaximumLength: uint16((len(buf) - 1) * 2), + Buffer: &buf[0], + } + return uni, nil +} + +// ConvertStringSetToSlice is a helper function used to convert the contents of +// `buf` into a string slice. `buf` contains a set of null terminated strings +// with an additional null at the end to indicate the end of the set. +func ConvertStringSetToSlice(buf []byte) ([]string, error) { + var results []string + prev := 0 + for i := range buf { + if buf[i] == 0 { + if prev == i { + // found two null characters in a row, return result + return results, nil + } + results = append(results, string(buf[prev:i])) + prev = i + 1 + } + } + return nil, errors.New("string set malformed: missing null terminator at end of buffer") +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/winapi.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/winapi.go new file mode 100644 index 0000000000..1d4ba3c4f8 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/winapi.go @@ -0,0 +1,5 @@ +// Package winapi contains various low-level bindings to Windows APIs. It can +// be thought of as an extension to golang.org/x/sys/windows. +package winapi + +//go:generate go run ..\..\mksyscall_windows.go -output zsyscall_windows.go console.go system.go net.go path.go thread.go iocp.go jobobject.go logon.go memory.go process.go processor.go devices.go filesystem.go errors.go diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go new file mode 100644 index 0000000000..4eb64b4c0c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go @@ -0,0 +1,360 @@ +// Code generated mksyscall_windows.exe DO NOT EDIT + +package winapi + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + modntdll = windows.NewLazySystemDLL("ntdll.dll") + modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") + modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") + modcfgmgr32 = windows.NewLazySystemDLL("cfgmgr32.dll") + + procCreatePseudoConsole = modkernel32.NewProc("CreatePseudoConsole") + procClosePseudoConsole = modkernel32.NewProc("ClosePseudoConsole") + procResizePseudoConsole = modkernel32.NewProc("ResizePseudoConsole") + procNtQuerySystemInformation = modntdll.NewProc("NtQuerySystemInformation") + procSetJobCompartmentId = modiphlpapi.NewProc("SetJobCompartmentId") + procSearchPathW = modkernel32.NewProc("SearchPathW") + procCreateRemoteThread = modkernel32.NewProc("CreateRemoteThread") + procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") + procIsProcessInJob = modkernel32.NewProc("IsProcessInJob") + procQueryInformationJobObject = modkernel32.NewProc("QueryInformationJobObject") + procOpenJobObjectW = modkernel32.NewProc("OpenJobObjectW") + procSetIoRateControlInformationJobObject = modkernel32.NewProc("SetIoRateControlInformationJobObject") + procQueryIoRateControlInformationJobObject = modkernel32.NewProc("QueryIoRateControlInformationJobObject") + procNtOpenJobObject = modntdll.NewProc("NtOpenJobObject") + procNtCreateJobObject = modntdll.NewProc("NtCreateJobObject") + procLogonUserW = modadvapi32.NewProc("LogonUserW") + procLocalAlloc = modkernel32.NewProc("LocalAlloc") + procLocalFree = modkernel32.NewProc("LocalFree") + procGetActiveProcessorCount = modkernel32.NewProc("GetActiveProcessorCount") + procCM_Get_Device_ID_List_SizeA = modcfgmgr32.NewProc("CM_Get_Device_ID_List_SizeA") + procCM_Get_Device_ID_ListA = modcfgmgr32.NewProc("CM_Get_Device_ID_ListA") + procCM_Locate_DevNodeW = modcfgmgr32.NewProc("CM_Locate_DevNodeW") + procCM_Get_DevNode_PropertyW = modcfgmgr32.NewProc("CM_Get_DevNode_PropertyW") + procNtCreateFile = modntdll.NewProc("NtCreateFile") + procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile") + procNtOpenDirectoryObject = modntdll.NewProc("NtOpenDirectoryObject") + procNtQueryDirectoryObject = modntdll.NewProc("NtQueryDirectoryObject") + procRtlNtStatusToDosError = modntdll.NewProc("RtlNtStatusToDosError") +) + +func createPseudoConsole(size uint32, hInput windows.Handle, hOutput windows.Handle, dwFlags uint32, hpcon *windows.Handle) (hr error) { + r0, _, _ := syscall.Syscall6(procCreatePseudoConsole.Addr(), 5, uintptr(size), uintptr(hInput), uintptr(hOutput), uintptr(dwFlags), uintptr(unsafe.Pointer(hpcon)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func ClosePseudoConsole(hpc windows.Handle) { + syscall.Syscall(procClosePseudoConsole.Addr(), 1, uintptr(hpc), 0, 0) + return +} + +func resizePseudoConsole(hPc windows.Handle, size uint32) (hr error) { + r0, _, _ := syscall.Syscall(procResizePseudoConsole.Addr(), 2, uintptr(hPc), uintptr(size), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func NtQuerySystemInformation(systemInfoClass int, systemInformation uintptr, systemInfoLength uint32, returnLength *uint32) (status uint32) { + r0, _, _ := syscall.Syscall6(procNtQuerySystemInformation.Addr(), 4, uintptr(systemInfoClass), uintptr(systemInformation), uintptr(systemInfoLength), uintptr(unsafe.Pointer(returnLength)), 0, 0) + status = uint32(r0) + return +} + +func SetJobCompartmentId(handle windows.Handle, compartmentId uint32) (win32Err error) { + r0, _, _ := syscall.Syscall(procSetJobCompartmentId.Addr(), 2, uintptr(handle), uintptr(compartmentId), 0) + if r0 != 0 { + win32Err = syscall.Errno(r0) + } + return +} + +func SearchPath(lpPath *uint16, lpFileName *uint16, lpExtension *uint16, nBufferLength uint32, lpBuffer *uint16, lpFilePath *uint16) (size uint32, err error) { + r0, _, e1 := syscall.Syscall6(procSearchPathW.Addr(), 6, uintptr(unsafe.Pointer(lpPath)), uintptr(unsafe.Pointer(lpFileName)), uintptr(unsafe.Pointer(lpExtension)), uintptr(nBufferLength), uintptr(unsafe.Pointer(lpBuffer)), uintptr(unsafe.Pointer(lpFilePath))) + size = uint32(r0) + if size == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func CreateRemoteThread(process windows.Handle, sa *windows.SecurityAttributes, stackSize uint32, startAddr uintptr, parameter uintptr, creationFlags uint32, threadID *uint32) (handle windows.Handle, err error) { + r0, _, e1 := syscall.Syscall9(procCreateRemoteThread.Addr(), 7, uintptr(process), uintptr(unsafe.Pointer(sa)), uintptr(stackSize), uintptr(startAddr), uintptr(parameter), uintptr(creationFlags), uintptr(unsafe.Pointer(threadID)), 0, 0) + handle = windows.Handle(r0) + if handle == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func GetQueuedCompletionStatus(cphandle windows.Handle, qty *uint32, key *uintptr, overlapped **windows.Overlapped, timeout uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(cphandle), uintptr(unsafe.Pointer(qty)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(overlapped)), uintptr(timeout), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func IsProcessInJob(procHandle windows.Handle, jobHandle windows.Handle, result *bool) (err error) { + r1, _, e1 := syscall.Syscall(procIsProcessInJob.Addr(), 3, uintptr(procHandle), uintptr(jobHandle), uintptr(unsafe.Pointer(result))) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func QueryInformationJobObject(jobHandle windows.Handle, infoClass uint32, jobObjectInfo uintptr, jobObjectInformationLength uint32, lpReturnLength *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procQueryInformationJobObject.Addr(), 5, uintptr(jobHandle), uintptr(infoClass), uintptr(jobObjectInfo), uintptr(jobObjectInformationLength), uintptr(unsafe.Pointer(lpReturnLength)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func OpenJobObject(desiredAccess uint32, inheritHandle bool, lpName *uint16) (handle windows.Handle, err error) { + var _p0 uint32 + if inheritHandle { + _p0 = 1 + } else { + _p0 = 0 + } + r0, _, e1 := syscall.Syscall(procOpenJobObjectW.Addr(), 3, uintptr(desiredAccess), uintptr(_p0), uintptr(unsafe.Pointer(lpName))) + handle = windows.Handle(r0) + if handle == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func SetIoRateControlInformationJobObject(jobHandle windows.Handle, ioRateControlInfo *JOBOBJECT_IO_RATE_CONTROL_INFORMATION) (ret uint32, err error) { + r0, _, e1 := syscall.Syscall(procSetIoRateControlInformationJobObject.Addr(), 2, uintptr(jobHandle), uintptr(unsafe.Pointer(ioRateControlInfo)), 0) + ret = uint32(r0) + if ret == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func QueryIoRateControlInformationJobObject(jobHandle windows.Handle, volumeName *uint16, ioRateControlInfo **JOBOBJECT_IO_RATE_CONTROL_INFORMATION, infoBlockCount *uint32) (ret uint32, err error) { + r0, _, e1 := syscall.Syscall6(procQueryIoRateControlInformationJobObject.Addr(), 4, uintptr(jobHandle), uintptr(unsafe.Pointer(volumeName)), uintptr(unsafe.Pointer(ioRateControlInfo)), uintptr(unsafe.Pointer(infoBlockCount)), 0, 0) + ret = uint32(r0) + if ret == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func NtOpenJobObject(jobHandle *windows.Handle, desiredAccess uint32, objAttributes *ObjectAttributes) (status uint32) { + r0, _, _ := syscall.Syscall(procNtOpenJobObject.Addr(), 3, uintptr(unsafe.Pointer(jobHandle)), uintptr(desiredAccess), uintptr(unsafe.Pointer(objAttributes))) + status = uint32(r0) + return +} + +func NtCreateJobObject(jobHandle *windows.Handle, desiredAccess uint32, objAttributes *ObjectAttributes) (status uint32) { + r0, _, _ := syscall.Syscall(procNtCreateJobObject.Addr(), 3, uintptr(unsafe.Pointer(jobHandle)), uintptr(desiredAccess), uintptr(unsafe.Pointer(objAttributes))) + status = uint32(r0) + return +} + +func LogonUser(username *uint16, domain *uint16, password *uint16, logonType uint32, logonProvider uint32, token *windows.Token) (err error) { + r1, _, e1 := syscall.Syscall6(procLogonUserW.Addr(), 6, uintptr(unsafe.Pointer(username)), uintptr(unsafe.Pointer(domain)), uintptr(unsafe.Pointer(password)), uintptr(logonType), uintptr(logonProvider), uintptr(unsafe.Pointer(token))) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func LocalAlloc(flags uint32, size int) (ptr uintptr) { + r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(flags), uintptr(size), 0) + ptr = uintptr(r0) + return +} + +func LocalFree(ptr uintptr) { + syscall.Syscall(procLocalFree.Addr(), 1, uintptr(ptr), 0, 0) + return +} + +func GetActiveProcessorCount(groupNumber uint16) (amount uint32) { + r0, _, _ := syscall.Syscall(procGetActiveProcessorCount.Addr(), 1, uintptr(groupNumber), 0, 0) + amount = uint32(r0) + return +} + +func CMGetDeviceIDListSize(pulLen *uint32, pszFilter *byte, uFlags uint32) (hr error) { + r0, _, _ := syscall.Syscall(procCM_Get_Device_ID_List_SizeA.Addr(), 3, uintptr(unsafe.Pointer(pulLen)), uintptr(unsafe.Pointer(pszFilter)), uintptr(uFlags)) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func CMGetDeviceIDList(pszFilter *byte, buffer *byte, bufferLen uint32, uFlags uint32) (hr error) { + r0, _, _ := syscall.Syscall6(procCM_Get_Device_ID_ListA.Addr(), 4, uintptr(unsafe.Pointer(pszFilter)), uintptr(unsafe.Pointer(buffer)), uintptr(bufferLen), uintptr(uFlags), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func CMLocateDevNode(pdnDevInst *uint32, pDeviceID string, uFlags uint32) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(pDeviceID) + if hr != nil { + return + } + return _CMLocateDevNode(pdnDevInst, _p0, uFlags) +} + +func _CMLocateDevNode(pdnDevInst *uint32, pDeviceID *uint16, uFlags uint32) (hr error) { + r0, _, _ := syscall.Syscall(procCM_Locate_DevNodeW.Addr(), 3, uintptr(unsafe.Pointer(pdnDevInst)), uintptr(unsafe.Pointer(pDeviceID)), uintptr(uFlags)) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func CMGetDevNodeProperty(dnDevInst uint32, propertyKey *DevPropKey, propertyType *uint32, propertyBuffer *uint16, propertyBufferSize *uint32, uFlags uint32) (hr error) { + r0, _, _ := syscall.Syscall6(procCM_Get_DevNode_PropertyW.Addr(), 6, uintptr(dnDevInst), uintptr(unsafe.Pointer(propertyKey)), uintptr(unsafe.Pointer(propertyType)), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(unsafe.Pointer(propertyBufferSize)), uintptr(uFlags)) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func NtCreateFile(handle *uintptr, accessMask uint32, oa *ObjectAttributes, iosb *IOStatusBlock, allocationSize *uint64, fileAttributes uint32, shareAccess uint32, createDisposition uint32, createOptions uint32, eaBuffer *byte, eaLength uint32) (status uint32) { + r0, _, _ := syscall.Syscall12(procNtCreateFile.Addr(), 11, uintptr(unsafe.Pointer(handle)), uintptr(accessMask), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(unsafe.Pointer(allocationSize)), uintptr(fileAttributes), uintptr(shareAccess), uintptr(createDisposition), uintptr(createOptions), uintptr(unsafe.Pointer(eaBuffer)), uintptr(eaLength), 0) + status = uint32(r0) + return +} + +func NtSetInformationFile(handle uintptr, iosb *IOStatusBlock, information uintptr, length uint32, class uint32) (status uint32) { + r0, _, _ := syscall.Syscall6(procNtSetInformationFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(iosb)), uintptr(information), uintptr(length), uintptr(class), 0) + status = uint32(r0) + return +} + +func NtOpenDirectoryObject(handle *uintptr, accessMask uint32, oa *ObjectAttributes) (status uint32) { + r0, _, _ := syscall.Syscall(procNtOpenDirectoryObject.Addr(), 3, uintptr(unsafe.Pointer(handle)), uintptr(accessMask), uintptr(unsafe.Pointer(oa))) + status = uint32(r0) + return +} + +func NtQueryDirectoryObject(handle uintptr, buffer *byte, length uint32, singleEntry bool, restartScan bool, context *uint32, returnLength *uint32) (status uint32) { + var _p0 uint32 + if singleEntry { + _p0 = 1 + } else { + _p0 = 0 + } + var _p1 uint32 + if restartScan { + _p1 = 1 + } else { + _p1 = 0 + } + r0, _, _ := syscall.Syscall9(procNtQueryDirectoryObject.Addr(), 7, uintptr(handle), uintptr(unsafe.Pointer(buffer)), uintptr(length), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(context)), uintptr(unsafe.Pointer(returnLength)), 0, 0) + status = uint32(r0) + return +} + +func RtlNtStatusToDosError(status uint32) (winerr error) { + r0, _, _ := syscall.Syscall(procRtlNtStatusToDosError.Addr(), 1, uintptr(status), 0, 0) + if r0 != 0 { + winerr = syscall.Errno(r0) + } + return +} diff --git a/vendor/github.com/Microsoft/hcsshim/layer.go b/vendor/github.com/Microsoft/hcsshim/layer.go new file mode 100644 index 0000000000..8916163706 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/layer.go @@ -0,0 +1,107 @@ +package hcsshim + +import ( + "context" + "crypto/sha1" + "path/filepath" + + "github.com/Microsoft/go-winio/pkg/guid" + "github.com/Microsoft/hcsshim/internal/wclayer" +) + +func layerPath(info *DriverInfo, id string) string { + return filepath.Join(info.HomeDir, id) +} + +func ActivateLayer(info DriverInfo, id string) error { + return wclayer.ActivateLayer(context.Background(), layerPath(&info, id)) +} +func CreateLayer(info DriverInfo, id, parent string) error { + return wclayer.CreateLayer(context.Background(), layerPath(&info, id), parent) +} + +// New clients should use CreateScratchLayer instead. Kept in to preserve API compatibility. +func CreateSandboxLayer(info DriverInfo, layerId, parentId string, parentLayerPaths []string) error { + return wclayer.CreateScratchLayer(context.Background(), layerPath(&info, layerId), parentLayerPaths) +} +func CreateScratchLayer(info DriverInfo, layerId, parentId string, parentLayerPaths []string) error { + return wclayer.CreateScratchLayer(context.Background(), layerPath(&info, layerId), parentLayerPaths) +} +func DeactivateLayer(info DriverInfo, id string) error { + return wclayer.DeactivateLayer(context.Background(), layerPath(&info, id)) +} +func DestroyLayer(info DriverInfo, id string) error { + return wclayer.DestroyLayer(context.Background(), layerPath(&info, id)) +} + +// New clients should use ExpandScratchSize instead. Kept in to preserve API compatibility. +func ExpandSandboxSize(info DriverInfo, layerId string, size uint64) error { + return wclayer.ExpandScratchSize(context.Background(), layerPath(&info, layerId), size) +} +func ExpandScratchSize(info DriverInfo, layerId string, size uint64) error { + return wclayer.ExpandScratchSize(context.Background(), layerPath(&info, layerId), size) +} +func ExportLayer(info DriverInfo, layerId string, exportFolderPath string, parentLayerPaths []string) error { + return wclayer.ExportLayer(context.Background(), layerPath(&info, layerId), exportFolderPath, parentLayerPaths) +} +func GetLayerMountPath(info DriverInfo, id string) (string, error) { + return wclayer.GetLayerMountPath(context.Background(), layerPath(&info, id)) +} +func GetSharedBaseImages() (imageData string, err error) { + return wclayer.GetSharedBaseImages(context.Background()) +} +func ImportLayer(info DriverInfo, layerID string, importFolderPath string, parentLayerPaths []string) error { + return wclayer.ImportLayer(context.Background(), layerPath(&info, layerID), importFolderPath, parentLayerPaths) +} +func LayerExists(info DriverInfo, id string) (bool, error) { + return wclayer.LayerExists(context.Background(), layerPath(&info, id)) +} +func PrepareLayer(info DriverInfo, layerId string, parentLayerPaths []string) error { + return wclayer.PrepareLayer(context.Background(), layerPath(&info, layerId), parentLayerPaths) +} +func ProcessBaseLayer(path string) error { + return wclayer.ProcessBaseLayer(context.Background(), path) +} +func ProcessUtilityVMImage(path string) error { + return wclayer.ProcessUtilityVMImage(context.Background(), path) +} +func UnprepareLayer(info DriverInfo, layerId string) error { + return wclayer.UnprepareLayer(context.Background(), layerPath(&info, layerId)) +} + +type DriverInfo struct { + Flavour int + HomeDir string +} + +type GUID [16]byte + +func NameToGuid(name string) (id GUID, err error) { + g, err := wclayer.NameToGuid(context.Background(), name) + return g.ToWindowsArray(), err +} + +func NewGUID(source string) *GUID { + h := sha1.Sum([]byte(source)) + var g GUID + copy(g[0:], h[0:16]) + return &g +} + +func (g *GUID) ToString() string { + return guid.FromWindowsArray(*g).String() +} + +type LayerReader = wclayer.LayerReader + +func NewLayerReader(info DriverInfo, layerID string, parentLayerPaths []string) (LayerReader, error) { + return wclayer.NewLayerReader(context.Background(), layerPath(&info, layerID), parentLayerPaths) +} + +type LayerWriter = wclayer.LayerWriter + +func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string) (LayerWriter, error) { + return wclayer.NewLayerWriter(context.Background(), layerPath(&info, layerID), parentLayerPaths) +} + +type WC_LAYER_DESCRIPTOR = wclayer.WC_LAYER_DESCRIPTOR diff --git a/vendor/github.com/Microsoft/hcsshim/osversion/osversion_windows.go b/vendor/github.com/Microsoft/hcsshim/osversion/osversion_windows.go new file mode 100644 index 0000000000..3ab3bcd89a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/osversion/osversion_windows.go @@ -0,0 +1,50 @@ +package osversion + +import ( + "fmt" + "sync" + + "golang.org/x/sys/windows" +) + +// OSVersion is a wrapper for Windows version information +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx +type OSVersion struct { + Version uint32 + MajorVersion uint8 + MinorVersion uint8 + Build uint16 +} + +var ( + osv OSVersion + once sync.Once +) + +// Get gets the operating system version on Windows. +// The calling application must be manifested to get the correct version information. +func Get() OSVersion { + once.Do(func() { + var err error + osv = OSVersion{} + osv.Version, err = windows.GetVersion() + if err != nil { + // GetVersion never fails. + panic(err) + } + osv.MajorVersion = uint8(osv.Version & 0xFF) + osv.MinorVersion = uint8(osv.Version >> 8 & 0xFF) + osv.Build = uint16(osv.Version >> 16) + }) + return osv +} + +// Build gets the build-number on Windows +// The calling application must be manifested to get the correct version information. +func Build() uint16 { + return Get().Build +} + +func (osv OSVersion) ToString() string { + return fmt.Sprintf("%d.%d.%d", osv.MajorVersion, osv.MinorVersion, osv.Build) +} diff --git a/vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go b/vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go new file mode 100644 index 0000000000..75dce5d821 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go @@ -0,0 +1,50 @@ +package osversion + +const ( + // RS1 (version 1607, codename "Redstone 1") corresponds to Windows Server + // 2016 (ltsc2016) and Windows 10 (Anniversary Update). + RS1 = 14393 + + // RS2 (version 1703, codename "Redstone 2") was a client-only update, and + // corresponds to Windows 10 (Creators Update). + RS2 = 15063 + + // RS3 (version 1709, codename "Redstone 3") corresponds to Windows Server + // 1709 (Semi-Annual Channel (SAC)), and Windows 10 (Fall Creators Update). + RS3 = 16299 + + // RS4 (version 1803, codename "Redstone 4") corresponds to Windows Server + // 1803 (Semi-Annual Channel (SAC)), and Windows 10 (April 2018 Update). + RS4 = 17134 + + // RS5 (version 1809, codename "Redstone 5") corresponds to Windows Server + // 2019 (ltsc2019), and Windows 10 (October 2018 Update). + RS5 = 17763 + + // V19H1 (version 1903) corresponds to Windows Server 1903 (semi-annual + // channel). + V19H1 = 18362 + + // V19H2 (version 1909) corresponds to Windows Server 1909 (semi-annual + // channel). + V19H2 = 18363 + + // V20H1 (version 2004) corresponds to Windows Server 2004 (semi-annual + // channel). + V20H1 = 19041 + + // V20H2 corresponds to Windows Server 20H2 (semi-annual channel). + V20H2 = 19042 + + // V21H1 corresponds to Windows Server 21H1 (semi-annual channel). + V21H1 = 19043 + + // V21H2Win10 corresponds to Windows 10 (November 2021 Update). + V21H2Win10 = 19044 + + // V21H2Server corresponds to Windows Server 2022 (ltsc2022). + V21H2Server = 20348 + + // V21H2Win11 corresponds to Windows 11 (original release). + V21H2Win11 = 22000 +) diff --git a/vendor/github.com/Microsoft/hcsshim/process.go b/vendor/github.com/Microsoft/hcsshim/process.go new file mode 100644 index 0000000000..3362c68335 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/process.go @@ -0,0 +1,98 @@ +package hcsshim + +import ( + "context" + "io" + "sync" + "time" + + "github.com/Microsoft/hcsshim/internal/hcs" +) + +// ContainerError is an error encountered in HCS +type process struct { + p *hcs.Process + waitOnce sync.Once + waitCh chan struct{} + waitErr error +} + +// Pid returns the process ID of the process within the container. +func (process *process) Pid() int { + return process.p.Pid() +} + +// Kill signals the process to terminate but does not wait for it to finish terminating. +func (process *process) Kill() error { + found, err := process.p.Kill(context.Background()) + if err != nil { + return convertProcessError(err, process) + } + if !found { + return &ProcessError{Process: process, Err: ErrElementNotFound, Operation: "hcsshim::Process::Kill"} + } + return nil +} + +// Wait waits for the process to exit. +func (process *process) Wait() error { + return convertProcessError(process.p.Wait(), process) +} + +// WaitTimeout waits for the process to exit or the duration to elapse. It returns +// false if timeout occurs. +func (process *process) WaitTimeout(timeout time.Duration) error { + process.waitOnce.Do(func() { + process.waitCh = make(chan struct{}) + go func() { + process.waitErr = process.Wait() + close(process.waitCh) + }() + }) + t := time.NewTimer(timeout) + defer t.Stop() + select { + case <-t.C: + return &ProcessError{Process: process, Err: ErrTimeout, Operation: "hcsshim::Process::Wait"} + case <-process.waitCh: + return process.waitErr + } +} + +// ExitCode returns the exit code of the process. The process must have +// already terminated. +func (process *process) ExitCode() (int, error) { + code, err := process.p.ExitCode() + if err != nil { + err = convertProcessError(err, process) + } + return code, err +} + +// ResizeConsole resizes the console of the process. +func (process *process) ResizeConsole(width, height uint16) error { + return convertProcessError(process.p.ResizeConsole(context.Background(), width, height), process) +} + +// Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing +// these pipes does not close the underlying pipes; it should be possible to +// call this multiple times to get multiple interfaces. +func (process *process) Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) { + stdin, stdout, stderr, err := process.p.StdioLegacy() + if err != nil { + err = convertProcessError(err, process) + } + return stdin, stdout, stderr, err +} + +// CloseStdin closes the write side of the stdin pipe so that the process is +// notified on the read side that there is no more data in stdin. +func (process *process) CloseStdin() error { + return convertProcessError(process.p.CloseStdin(context.Background()), process) +} + +// Close cleans up any state associated with the process but does not kill +// or wait on it. +func (process *process) Close() error { + return convertProcessError(process.p.Close(), process) +} diff --git a/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go new file mode 100644 index 0000000000..8bed848573 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go @@ -0,0 +1,54 @@ +// Code generated mksyscall_windows.exe DO NOT EDIT + +package hcsshim + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") + + procSetCurrentThreadCompartmentId = modiphlpapi.NewProc("SetCurrentThreadCompartmentId") +) + +func SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) { + r0, _, _ := syscall.Syscall(procSetCurrentThreadCompartmentId.Addr(), 1, uintptr(compartmentId), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} diff --git a/vendor/github.com/apex/log/.gitignore b/vendor/github.com/apex/log/.gitignore new file mode 100644 index 0000000000..7a6353d673 --- /dev/null +++ b/vendor/github.com/apex/log/.gitignore @@ -0,0 +1 @@ +.envrc diff --git a/vendor/github.com/apex/log/History.md b/vendor/github.com/apex/log/History.md new file mode 100644 index 0000000000..a303177b3c --- /dev/null +++ b/vendor/github.com/apex/log/History.md @@ -0,0 +1,75 @@ + +v1.9.0 / 2020-08-18 +=================== + + * add `WithDuration()` method to record a duration as milliseconds + * add: ignore nil errors in `WithError()` + * change trace duration to milliseconds (arguably a breaking change) + +v1.8.0 / 2020-08-05 +=================== + + * refactor apexlogs handler to not make the AddEvents() call if there are no events to flush + +v1.7.1 / 2020-08-05 +=================== + + * fix potential nil panic in apexlogs handler + +v1.7.0 / 2020-08-03 +=================== + + * add FlushSync() to apexlogs handler + +v1.6.0 / 2020-07-13 +=================== + + * update apex/logs dep to v1.0.0 + * docs: mention that Flush() is non-blocking now, use Close() + +v1.5.0 / 2020-07-11 +=================== + + * add buffering to Apex Logs handler + +v1.4.0 / 2020-06-16 +=================== + + * add AuthToken to apexlogs handler + +v1.3.0 / 2020-05-26 +=================== + + * change FromContext() to always return a logger + +v1.2.0 / 2020-05-26 +=================== + + * add log.NewContext() and log.FromContext(). Closes #78 + +v1.1.4 / 2020-04-22 +=================== + + * add apexlogs HTTPClient support + +v1.1.3 / 2020-04-22 +=================== + + * add events len check before flushing to apexlogs handler + +v1.1.2 / 2020-01-29 +=================== + + * refactor apexlogs handler to use github.com/apex/logs client + +v1.1.1 / 2019-06-24 +=================== + + * add go.mod + * add rough pass at apexlogs handler + +v1.1.0 / 2018-10-11 +=================== + + * fix: cli handler to show non-string fields appropriately + * fix: cli using fatih/color to better support windows diff --git a/vendor/github.com/apex/log/LICENSE b/vendor/github.com/apex/log/LICENSE new file mode 100644 index 0000000000..af71800684 --- /dev/null +++ b/vendor/github.com/apex/log/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2015 TJ Holowaychuk tj@tjholowaychuk.com + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/apex/log/Makefile b/vendor/github.com/apex/log/Makefile new file mode 100644 index 0000000000..f948e88ec1 --- /dev/null +++ b/vendor/github.com/apex/log/Makefile @@ -0,0 +1,2 @@ + +include github.com/tj/make/golang diff --git a/vendor/github.com/apex/log/Readme.md b/vendor/github.com/apex/log/Readme.md new file mode 100644 index 0000000000..aa5c621f5c --- /dev/null +++ b/vendor/github.com/apex/log/Readme.md @@ -0,0 +1,61 @@ + +![Structured logging for golang](assets/title.png) + +Package log implements a simple structured logging API inspired by Logrus, designed with centralization in mind. Read more on [Medium](https://medium.com/@tjholowaychuk/apex-log-e8d9627f4a9a#.rav8yhkud). + +## Handlers + +- __apexlogs__ – handler for [Apex Logs](https://apex.sh/logs/) +- __cli__ – human-friendly CLI output +- __discard__ – discards all logs +- __es__ – Elasticsearch handler +- __graylog__ – Graylog handler +- __json__ – JSON output handler +- __kinesis__ – AWS Kinesis handler +- __level__ – level filter handler +- __logfmt__ – logfmt plain-text formatter +- __memory__ – in-memory handler for tests +- __multi__ – fan-out to multiple handlers +- __papertrail__ – Papertrail handler +- __text__ – human-friendly colored output +- __delta__ – outputs the delta between log calls and spinner + +## Example + +Example using the [Apex Logs](https://apex.sh/logs/) handler. + +```go +package main + +import ( + "errors" + "time" + + "github.com/apex/log" +) + +func main() { + ctx := log.WithFields(log.Fields{ + "file": "something.png", + "type": "image/png", + "user": "tobi", + }) + + for range time.Tick(time.Millisecond * 200) { + ctx.Info("upload") + ctx.Info("upload complete") + ctx.Warn("upload retry") + ctx.WithError(errors.New("unauthorized")).Error("upload failed") + ctx.Errorf("failed to upload %s", "img.png") + } +} +``` + +--- + +[![Build Status](https://semaphoreci.com/api/v1/projects/d8a8b1c0-45b0-4b89-b066-99d788d0b94c/642077/badge.svg)](https://semaphoreci.com/tj/log) +[![GoDoc](https://godoc.org/github.com/apex/log?status.svg)](https://godoc.org/github.com/apex/log) +![](https://img.shields.io/badge/license-MIT-blue.svg) +![](https://img.shields.io/badge/status-stable-green.svg) + + diff --git a/vendor/github.com/apex/log/context.go b/vendor/github.com/apex/log/context.go new file mode 100644 index 0000000000..290ae41431 --- /dev/null +++ b/vendor/github.com/apex/log/context.go @@ -0,0 +1,19 @@ +package log + +import "context" + +// logKey is a private context key. +type logKey struct{} + +// NewContext returns a new context with logger. +func NewContext(ctx context.Context, v Interface) context.Context { + return context.WithValue(ctx, logKey{}, v) +} + +// FromContext returns the logger from context, or log.Log. +func FromContext(ctx context.Context) Interface { + if v, ok := ctx.Value(logKey{}).(Interface); ok { + return v + } + return Log +} diff --git a/vendor/github.com/apex/log/default.go b/vendor/github.com/apex/log/default.go new file mode 100644 index 0000000000..2213486238 --- /dev/null +++ b/vendor/github.com/apex/log/default.go @@ -0,0 +1,45 @@ +package log + +import ( + "bytes" + "fmt" + "log" + "sort" +) + +// field used for sorting. +type field struct { + Name string + Value interface{} +} + +// by sorts fields by name. +type byName []field + +func (a byName) Len() int { return len(a) } +func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a byName) Less(i, j int) bool { return a[i].Name < a[j].Name } + +// handleStdLog outpouts to the stlib log. +func handleStdLog(e *Entry) error { + level := levelNames[e.Level] + + var fields []field + + for k, v := range e.Fields { + fields = append(fields, field{k, v}) + } + + sort.Sort(byName(fields)) + + var b bytes.Buffer + fmt.Fprintf(&b, "%5s %-25s", level, e.Message) + + for _, f := range fields { + fmt.Fprintf(&b, " %s=%v", f.Name, f.Value) + } + + log.Println(b.String()) + + return nil +} diff --git a/vendor/github.com/apex/log/doc.go b/vendor/github.com/apex/log/doc.go new file mode 100644 index 0000000000..0331e8e163 --- /dev/null +++ b/vendor/github.com/apex/log/doc.go @@ -0,0 +1,10 @@ +/* +Package log implements a simple structured logging API designed with few assumptions. Designed for +centralized logging solutions such as Kinesis which require encoding and decoding before fanning-out +to handlers. + +You may use this package with inline handlers, much like Logrus, however a centralized solution +is recommended so that apps do not need to be re-deployed to add or remove logging service +providers. +*/ +package log diff --git a/vendor/github.com/apex/log/entry.go b/vendor/github.com/apex/log/entry.go new file mode 100644 index 0000000000..d8982956a3 --- /dev/null +++ b/vendor/github.com/apex/log/entry.go @@ -0,0 +1,182 @@ +package log + +import ( + "fmt" + "os" + "strings" + "time" +) + +// assert interface compliance. +var _ Interface = (*Entry)(nil) + +// Now returns the current time. +var Now = time.Now + +// Entry represents a single log entry. +type Entry struct { + Logger *Logger `json:"-"` + Fields Fields `json:"fields"` + Level Level `json:"level"` + Timestamp time.Time `json:"timestamp"` + Message string `json:"message"` + start time.Time + fields []Fields +} + +// NewEntry returns a new entry for `log`. +func NewEntry(log *Logger) *Entry { + return &Entry{ + Logger: log, + } +} + +// WithFields returns a new entry with `fields` set. +func (e *Entry) WithFields(fields Fielder) *Entry { + f := []Fields{} + f = append(f, e.fields...) + f = append(f, fields.Fields()) + return &Entry{ + Logger: e.Logger, + fields: f, + } +} + +// WithField returns a new entry with the `key` and `value` set. +func (e *Entry) WithField(key string, value interface{}) *Entry { + return e.WithFields(Fields{key: value}) +} + +// WithDuration returns a new entry with the "duration" field set +// to the given duration in milliseconds. +func (e *Entry) WithDuration(d time.Duration) *Entry { + return e.WithField("duration", d.Milliseconds()) +} + +// WithError returns a new entry with the "error" set to `err`. +// +// The given error may implement .Fielder, if it does the method +// will add all its `.Fields()` into the returned entry. +func (e *Entry) WithError(err error) *Entry { + if err == nil { + return e + } + + ctx := e.WithField("error", err.Error()) + + if s, ok := err.(stackTracer); ok { + frame := s.StackTrace()[0] + + name := fmt.Sprintf("%n", frame) + file := fmt.Sprintf("%+s", frame) + line := fmt.Sprintf("%d", frame) + + parts := strings.Split(file, "\n\t") + if len(parts) > 1 { + file = parts[1] + } + + ctx = ctx.WithField("source", fmt.Sprintf("%s: %s:%s", name, file, line)) + } + + if f, ok := err.(Fielder); ok { + ctx = ctx.WithFields(f.Fields()) + } + + return ctx +} + +// Debug level message. +func (e *Entry) Debug(msg string) { + e.Logger.log(DebugLevel, e, msg) +} + +// Info level message. +func (e *Entry) Info(msg string) { + e.Logger.log(InfoLevel, e, msg) +} + +// Warn level message. +func (e *Entry) Warn(msg string) { + e.Logger.log(WarnLevel, e, msg) +} + +// Error level message. +func (e *Entry) Error(msg string) { + e.Logger.log(ErrorLevel, e, msg) +} + +// Fatal level message, followed by an exit. +func (e *Entry) Fatal(msg string) { + e.Logger.log(FatalLevel, e, msg) + os.Exit(1) +} + +// Debugf level formatted message. +func (e *Entry) Debugf(msg string, v ...interface{}) { + e.Debug(fmt.Sprintf(msg, v...)) +} + +// Infof level formatted message. +func (e *Entry) Infof(msg string, v ...interface{}) { + e.Info(fmt.Sprintf(msg, v...)) +} + +// Warnf level formatted message. +func (e *Entry) Warnf(msg string, v ...interface{}) { + e.Warn(fmt.Sprintf(msg, v...)) +} + +// Errorf level formatted message. +func (e *Entry) Errorf(msg string, v ...interface{}) { + e.Error(fmt.Sprintf(msg, v...)) +} + +// Fatalf level formatted message, followed by an exit. +func (e *Entry) Fatalf(msg string, v ...interface{}) { + e.Fatal(fmt.Sprintf(msg, v...)) +} + +// Trace returns a new entry with a Stop method to fire off +// a corresponding completion log, useful with defer. +func (e *Entry) Trace(msg string) *Entry { + e.Info(msg) + v := e.WithFields(e.Fields) + v.Message = msg + v.start = time.Now() + return v +} + +// Stop should be used with Trace, to fire off the completion message. When +// an `err` is passed the "error" field is set, and the log level is error. +func (e *Entry) Stop(err *error) { + if err == nil || *err == nil { + e.WithDuration(time.Since(e.start)).Info(e.Message) + } else { + e.WithDuration(time.Since(e.start)).WithError(*err).Error(e.Message) + } +} + +// mergedFields returns the fields list collapsed into a single map. +func (e *Entry) mergedFields() Fields { + f := Fields{} + + for _, fields := range e.fields { + for k, v := range fields { + f[k] = v + } + } + + return f +} + +// finalize returns a copy of the Entry with Fields merged. +func (e *Entry) finalize(level Level, msg string) *Entry { + return &Entry{ + Logger: e.Logger, + Fields: e.mergedFields(), + Level: level, + Message: msg, + Timestamp: Now(), + } +} diff --git a/vendor/github.com/apex/log/go.mod b/vendor/github.com/apex/log/go.mod new file mode 100644 index 0000000000..870b30dbc6 --- /dev/null +++ b/vendor/github.com/apex/log/go.mod @@ -0,0 +1,32 @@ +module github.com/apex/log + +go 1.12 + +require ( + github.com/apex/logs v1.0.0 + github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a + github.com/aphistic/sweet v0.2.0 // indirect + github.com/aws/aws-sdk-go v1.20.6 + github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 + github.com/fatih/color v1.7.0 + github.com/go-logfmt/logfmt v0.4.0 + github.com/golang/protobuf v1.3.1 // indirect + github.com/google/uuid v1.1.1 // indirect + github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 // indirect + github.com/kr/pretty v0.2.0 // indirect + github.com/mattn/go-colorable v0.1.2 + github.com/pkg/errors v0.8.1 + github.com/rogpeppe/fastuuid v1.1.0 + github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect + github.com/smartystreets/gunit v1.0.0 // indirect + github.com/stretchr/testify v1.6.1 + github.com/tj/assert v0.0.3 + github.com/tj/go-buffer v1.1.0 + github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2 + github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b + github.com/tj/go-spin v1.1.0 + golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect + golang.org/x/text v0.3.2 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + gopkg.in/yaml.v2 v2.2.2 // indirect +) diff --git a/vendor/github.com/apex/log/go.sum b/vendor/github.com/apex/log/go.sum new file mode 100644 index 0000000000..c491698db4 --- /dev/null +++ b/vendor/github.com/apex/log/go.sum @@ -0,0 +1,117 @@ +github.com/apex/logs v1.0.0 h1:adOwhOTeXzZTnVuEK13wuJNBFutP0sOfutRS8NY+G6A= +github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= +github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a h1:2KLQMJ8msqoPHIPDufkxVcoTtcmE5+1sL9950m4R9Pk= +github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= +github.com/aphistic/sweet v0.2.0 h1:I4z+fAUqvKfvZV/CHi5dV0QuwbmIvYYFDjG0Ss5QpAs= +github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= +github.com/aws/aws-sdk-go v1.20.6 h1:kmy4Gvdlyez1fV4kw5RYxZzWKVyuHZHgPWeU/YvRsV4= +github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo= +github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 h1:K//n/AqR5HjG3qxbrBCL4vJPW0MVFSs9CPK1OOJdRME= +github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +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= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/fastuuid v1.1.0 h1:INyGLmTCMGFr6OVIb977ghJvABML2CMVjPoRfNDdYDo= +github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8= +github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 h1:hp2CYQUINdZMHdvTdXtPOY2ainKl4IoMcpAXEf2xj3Q= +github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= +github.com/smartystreets/gunit v1.0.0 h1:RyPDUFcJbvtXlhJPk7v+wnxZRY2EUokhEYl2EJOPToI= +github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tj/assert v0.0.0-20171129193455-018094318fb0 h1:Rw8kxzWo1mr6FSaYXjQELRe88y2KdfynXdnK72rdjtA= +github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= +github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= +github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= +github.com/tj/go-buffer v1.1.0 h1:Lo2OsPHlIxXF24zApe15AbK3bJLAOvkkxEA6Ux4c47M= +github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc= +github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2 h1:eGaGNxrtoZf/mBURsnNQKDR7u50Klgcf2eFDQEnc8Bc= +github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= +github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b h1:m74UWYy+HBs+jMFR9mdZU6shPewugMyH5+GV6LNgW8w= +github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= +github.com/tj/go-spin v1.1.0 h1:lhdWZsvImxvZ3q1C5OIB7d72DuOwP4O2NdBg9PyzNds= +github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/vendor/github.com/apex/log/interface.go b/vendor/github.com/apex/log/interface.go new file mode 100644 index 0000000000..9daa046532 --- /dev/null +++ b/vendor/github.com/apex/log/interface.go @@ -0,0 +1,22 @@ +package log + +import "time" + +// Interface represents the API of both Logger and Entry. +type Interface interface { + WithFields(Fielder) *Entry + WithField(string, interface{}) *Entry + WithDuration(time.Duration) *Entry + WithError(error) *Entry + Debug(string) + Info(string) + Warn(string) + Error(string) + Fatal(string) + Debugf(string, ...interface{}) + Infof(string, ...interface{}) + Warnf(string, ...interface{}) + Errorf(string, ...interface{}) + Fatalf(string, ...interface{}) + Trace(string) *Entry +} diff --git a/vendor/github.com/apex/log/levels.go b/vendor/github.com/apex/log/levels.go new file mode 100644 index 0000000000..7d43a43609 --- /dev/null +++ b/vendor/github.com/apex/log/levels.go @@ -0,0 +1,81 @@ +package log + +import ( + "bytes" + "errors" + "strings" +) + +// ErrInvalidLevel is returned if the severity level is invalid. +var ErrInvalidLevel = errors.New("invalid level") + +// Level of severity. +type Level int + +// Log levels. +const ( + InvalidLevel Level = iota - 1 + DebugLevel + InfoLevel + WarnLevel + ErrorLevel + FatalLevel +) + +var levelNames = [...]string{ + DebugLevel: "debug", + InfoLevel: "info", + WarnLevel: "warn", + ErrorLevel: "error", + FatalLevel: "fatal", +} + +var levelStrings = map[string]Level{ + "debug": DebugLevel, + "info": InfoLevel, + "warn": WarnLevel, + "warning": WarnLevel, + "error": ErrorLevel, + "fatal": FatalLevel, +} + +// String implementation. +func (l Level) String() string { + return levelNames[l] +} + +// MarshalJSON implementation. +func (l Level) MarshalJSON() ([]byte, error) { + return []byte(`"` + l.String() + `"`), nil +} + +// UnmarshalJSON implementation. +func (l *Level) UnmarshalJSON(b []byte) error { + v, err := ParseLevel(string(bytes.Trim(b, `"`))) + if err != nil { + return err + } + + *l = v + return nil +} + +// ParseLevel parses level string. +func ParseLevel(s string) (Level, error) { + l, ok := levelStrings[strings.ToLower(s)] + if !ok { + return InvalidLevel, ErrInvalidLevel + } + + return l, nil +} + +// MustParseLevel parses level string or panics. +func MustParseLevel(s string) Level { + l, err := ParseLevel(s) + if err != nil { + panic("invalid log level") + } + + return l +} diff --git a/vendor/github.com/apex/log/logger.go b/vendor/github.com/apex/log/logger.go new file mode 100644 index 0000000000..c7d9b730be --- /dev/null +++ b/vendor/github.com/apex/log/logger.go @@ -0,0 +1,156 @@ +package log + +import ( + stdlog "log" + "sort" + "time" +) + +// assert interface compliance. +var _ Interface = (*Logger)(nil) + +// Fielder is an interface for providing fields to custom types. +type Fielder interface { + Fields() Fields +} + +// Fields represents a map of entry level data used for structured logging. +type Fields map[string]interface{} + +// Fields implements Fielder. +func (f Fields) Fields() Fields { + return f +} + +// Get field value by name. +func (f Fields) Get(name string) interface{} { + return f[name] +} + +// Names returns field names sorted. +func (f Fields) Names() (v []string) { + for k := range f { + v = append(v, k) + } + + sort.Strings(v) + return +} + +// The HandlerFunc type is an adapter to allow the use of ordinary functions as +// log handlers. If f is a function with the appropriate signature, +// HandlerFunc(f) is a Handler object that calls f. +type HandlerFunc func(*Entry) error + +// HandleLog calls f(e). +func (f HandlerFunc) HandleLog(e *Entry) error { + return f(e) +} + +// Handler is used to handle log events, outputting them to +// stdio or sending them to remote services. See the "handlers" +// directory for implementations. +// +// It is left up to Handlers to implement thread-safety. +type Handler interface { + HandleLog(*Entry) error +} + +// Logger represents a logger with configurable Level and Handler. +type Logger struct { + Handler Handler + Level Level +} + +// WithFields returns a new entry with `fields` set. +func (l *Logger) WithFields(fields Fielder) *Entry { + return NewEntry(l).WithFields(fields.Fields()) +} + +// WithField returns a new entry with the `key` and `value` set. +// +// Note that the `key` should not have spaces in it - use camel +// case or underscores +func (l *Logger) WithField(key string, value interface{}) *Entry { + return NewEntry(l).WithField(key, value) +} + +// WithDuration returns a new entry with the "duration" field set +// to the given duration in milliseconds. +func (l *Logger) WithDuration(d time.Duration) *Entry { + return NewEntry(l).WithDuration(d) +} + +// WithError returns a new entry with the "error" set to `err`. +func (l *Logger) WithError(err error) *Entry { + return NewEntry(l).WithError(err) +} + +// Debug level message. +func (l *Logger) Debug(msg string) { + NewEntry(l).Debug(msg) +} + +// Info level message. +func (l *Logger) Info(msg string) { + NewEntry(l).Info(msg) +} + +// Warn level message. +func (l *Logger) Warn(msg string) { + NewEntry(l).Warn(msg) +} + +// Error level message. +func (l *Logger) Error(msg string) { + NewEntry(l).Error(msg) +} + +// Fatal level message, followed by an exit. +func (l *Logger) Fatal(msg string) { + NewEntry(l).Fatal(msg) +} + +// Debugf level formatted message. +func (l *Logger) Debugf(msg string, v ...interface{}) { + NewEntry(l).Debugf(msg, v...) +} + +// Infof level formatted message. +func (l *Logger) Infof(msg string, v ...interface{}) { + NewEntry(l).Infof(msg, v...) +} + +// Warnf level formatted message. +func (l *Logger) Warnf(msg string, v ...interface{}) { + NewEntry(l).Warnf(msg, v...) +} + +// Errorf level formatted message. +func (l *Logger) Errorf(msg string, v ...interface{}) { + NewEntry(l).Errorf(msg, v...) +} + +// Fatalf level formatted message, followed by an exit. +func (l *Logger) Fatalf(msg string, v ...interface{}) { + NewEntry(l).Fatalf(msg, v...) +} + +// Trace returns a new entry with a Stop method to fire off +// a corresponding completion log, useful with defer. +func (l *Logger) Trace(msg string) *Entry { + return NewEntry(l).Trace(msg) +} + +// log the message, invoking the handler. We clone the entry here +// to bypass the overhead in Entry methods when the level is not +// met. +func (l *Logger) log(level Level, e *Entry, msg string) { + if level < l.Level { + return + } + + if err := l.Handler.HandleLog(e.finalize(level, msg)); err != nil { + stdlog.Printf("error logging: %s", err) + } +} diff --git a/vendor/github.com/apex/log/pkg.go b/vendor/github.com/apex/log/pkg.go new file mode 100644 index 0000000000..872eae6c27 --- /dev/null +++ b/vendor/github.com/apex/log/pkg.go @@ -0,0 +1,108 @@ +package log + +import "time" + +// singletons ftw? +var Log Interface = &Logger{ + Handler: HandlerFunc(handleStdLog), + Level: InfoLevel, +} + +// SetHandler sets the handler. This is not thread-safe. +// The default handler outputs to the stdlib log. +func SetHandler(h Handler) { + if logger, ok := Log.(*Logger); ok { + logger.Handler = h + } +} + +// SetLevel sets the log level. This is not thread-safe. +func SetLevel(l Level) { + if logger, ok := Log.(*Logger); ok { + logger.Level = l + } +} + +// SetLevelFromString sets the log level from a string, panicing when invalid. This is not thread-safe. +func SetLevelFromString(s string) { + if logger, ok := Log.(*Logger); ok { + logger.Level = MustParseLevel(s) + } +} + +// WithFields returns a new entry with `fields` set. +func WithFields(fields Fielder) *Entry { + return Log.WithFields(fields) +} + +// WithField returns a new entry with the `key` and `value` set. +func WithField(key string, value interface{}) *Entry { + return Log.WithField(key, value) +} + +// WithDuration returns a new entry with the "duration" field set +// to the given duration in milliseconds. +func WithDuration(d time.Duration) *Entry { + return Log.WithDuration(d) +} + +// WithError returns a new entry with the "error" set to `err`. +func WithError(err error) *Entry { + return Log.WithError(err) +} + +// Debug level message. +func Debug(msg string) { + Log.Debug(msg) +} + +// Info level message. +func Info(msg string) { + Log.Info(msg) +} + +// Warn level message. +func Warn(msg string) { + Log.Warn(msg) +} + +// Error level message. +func Error(msg string) { + Log.Error(msg) +} + +// Fatal level message, followed by an exit. +func Fatal(msg string) { + Log.Fatal(msg) +} + +// Debugf level formatted message. +func Debugf(msg string, v ...interface{}) { + Log.Debugf(msg, v...) +} + +// Infof level formatted message. +func Infof(msg string, v ...interface{}) { + Log.Infof(msg, v...) +} + +// Warnf level formatted message. +func Warnf(msg string, v ...interface{}) { + Log.Warnf(msg, v...) +} + +// Errorf level formatted message. +func Errorf(msg string, v ...interface{}) { + Log.Errorf(msg, v...) +} + +// Fatalf level formatted message, followed by an exit. +func Fatalf(msg string, v ...interface{}) { + Log.Fatalf(msg, v...) +} + +// Trace returns a new entry with a Stop method to fire off +// a corresponding completion log, useful with defer. +func Trace(msg string) *Entry { + return Log.Trace(msg) +} diff --git a/vendor/github.com/apex/log/stack.go b/vendor/github.com/apex/log/stack.go new file mode 100644 index 0000000000..57efe3262e --- /dev/null +++ b/vendor/github.com/apex/log/stack.go @@ -0,0 +1,8 @@ +package log + +import "github.com/pkg/errors" + +// stackTracer interface. +type stackTracer interface { + StackTrace() errors.StackTrace +} diff --git a/vendor/github.com/buildpacks/imgutil/.gitignore b/vendor/github.com/buildpacks/imgutil/.gitignore new file mode 100644 index 0000000000..415f6b6ed7 --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/.gitignore @@ -0,0 +1,18 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# IDEs +.idea/ + +# Build timestamp +tools/bcdhive_generator/.build diff --git a/vendor/github.com/buildpacks/imgutil/CODEOWNERS b/vendor/github.com/buildpacks/imgutil/CODEOWNERS new file mode 100644 index 0000000000..d3a11dc1d0 --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/CODEOWNERS @@ -0,0 +1 @@ +* @buildpacks/implementation-maintainers diff --git a/vendor/github.com/buildpacks/imgutil/LICENSE b/vendor/github.com/buildpacks/imgutil/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/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/vendor/github.com/buildpacks/imgutil/Makefile b/vendor/github.com/buildpacks/imgutil/Makefile new file mode 100644 index 0000000000..f27f0d16af --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/Makefile @@ -0,0 +1,42 @@ +# Go parameters +GOCMD?=go +GO_VERSION=$(shell go list -m -f "{{.GoVersion}}") +PACKAGE_BASE=github.com/buildpacks/imgutil + +all: test + +install-goimports: + @echo "> Installing goimports..." + cd tools && $(GOCMD) install golang.org/x/tools/cmd/goimports + +format: install-goimports + @echo "> Formating code..." + @goimports -l -w -local ${PACKAGE_BASE} . + +install-golangci-lint: + @echo "> Installing golangci-lint..." + cd tools && $(GOCMD) install github.com/golangci/golangci-lint/cmd/golangci-lint + +lint: install-golangci-lint + @echo "> Linting code..." + @golangci-lint run -c golangci.yaml + +tools/bcdhive_generator/.build: $(wildcard tools/bcdhive_generator/*) +ifneq ($(OS),Windows_NT) + @echo "> Building bcdhive-generator in Docker using current golang version" + docker build tools/bcdhive_generator --tag bcdhive-generator --build-arg go_version=$(GO_VERSION) + + @touch tools/bcdhive_generator/.build +else + @echo "> Cannot generate on Windows" +endif + +layer/bcdhive_generated.go: layer/windows_baselayer.go tools/bcdhive_generator/.build +ifneq ($(OS),Windows_NT) + $(GOCMD) generate ./... +else + @echo "> Cannot generate on Windows" +endif + +test: layer/bcdhive_generated.go format lint + $(GOCMD) test -parallel=1 -count=1 -v ./... diff --git a/vendor/github.com/buildpacks/imgutil/README.md b/vendor/github.com/buildpacks/imgutil/README.md new file mode 100644 index 0000000000..79d2eb44c2 --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/README.md @@ -0,0 +1,19 @@ +# imgutil + +[![Build results](https://github.com/buildpacks/imgutil/workflows/test/badge.svg)](https://github.com/buildpacks/imgutil/actions) + +Helpful utilities for working with images + +## Development + +To format: + +```bash +$ make format +``` + +To run tests: + +```bash +$ make test +``` diff --git a/vendor/github.com/buildpacks/imgutil/go.mod b/vendor/github.com/buildpacks/imgutil/go.mod new file mode 100644 index 0000000000..84285d3529 --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/go.mod @@ -0,0 +1,19 @@ +module github.com/buildpacks/imgutil + +require ( + github.com/containerd/containerd v1.5.8 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.10.1 // indirect + github.com/docker/cli v20.10.11+incompatible // indirect + github.com/docker/docker v20.10.11+incompatible + github.com/google/go-cmp v0.5.6 + github.com/google/go-containerregistry v0.7.0 + github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect + github.com/opencontainers/image-spec v1.0.2 // indirect + github.com/pkg/errors v0.9.1 + github.com/sclevine/spec v1.4.0 + golang.org/x/net v0.0.0-20211203184738-4852103109b8 // indirect + golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect + google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12 // indirect +) + +go 1.14 diff --git a/vendor/github.com/buildpacks/imgutil/go.sum b/vendor/github.com/buildpacks/imgutil/go.sum new file mode 100644 index 0000000000..a8a0778991 --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/go.sum @@ -0,0 +1,1276 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= +github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= +github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= +github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= +github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= +github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= +github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= +github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= +github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= +github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= +github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= +github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= +github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= +github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= +github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= +github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= +github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= +github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= +github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= +github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= +github.com/containerd/containerd v1.5.8 h1:NmkCC1/QxyZFBny8JogwLpOy2f+VEbO/f6bV2Mqtwuw= +github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= +github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= +github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= +github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= +github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= +github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= +github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= +github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= +github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= +github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= +github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= +github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/stargz-snapshotter/estargz v0.10.0/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= +github.com/containerd/stargz-snapshotter/estargz v0.10.1 h1:hd1EoVjI2Ax8Cr64tdYqnJ4i4pZU49FkEf5kU8KxQng= +github.com/containerd/stargz-snapshotter/estargz v0.10.1/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= +github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= +github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= +github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= +github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= +github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= +github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= +github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= +github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= +github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= +github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= +github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= +github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= +github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/cli v20.10.10+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.11+incompatible h1:tXU1ezXcruZQRrMP8RN2z9N91h+6egZTS1gsPsKantc= +github.com/docker/cli v20.10.11+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= +github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.11+incompatible h1:OqzI/g/W54LczvhnccGqniFoQghHx3pklbLuhfXpqGo= +github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= +github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= +github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-containerregistry v0.7.0 h1:u0onUUOcyoCDHEiJoyR1R1gx5er1+r06V5DBhUU5ndk= +github.com/google/go-containerregistry v0.7.0/go.mod h1:2zaoelrL0d08gGbpdP3LqyUuBmhWbpD6IOe2s9nLS2k= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2-0.20210730191737-8e42a01fb1b7/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= +github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= +github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= +github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8= +github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= +github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= +github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= +github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211203184738-4852103109b8 h1:PFkPt/jI9Del3hmFplBtRp8tDhSRpFu7CyRs7VmEC0M= +golang.org/x/net v0.0.0-20211203184738-4852103109b8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211111162719-482062a4217b/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12 h1:DN5b3HU13J4sMd/QjDx34U6afpaexKTDdop+26pdjdk= +google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= +k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= +k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= +k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= +k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= +k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= +k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= +k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= +k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= +k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= +k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= +k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= +k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= +k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= +k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/vendor/github.com/buildpacks/imgutil/golangci.yaml b/vendor/github.com/buildpacks/imgutil/golangci.yaml new file mode 100644 index 0000000000..e033922b0e --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/golangci.yaml @@ -0,0 +1,32 @@ +run: + timeout: 6m + +linters: + disable-all: true + enable: + - bodyclose + - deadcode + - dogsled + - gocritic + - goimports + - golint + - gosec + - gosimple + - govet + - ineffassign + - maligned + - misspell + - nakedret + - scopelint + - staticcheck + - structcheck + - stylecheck + - typecheck + - unconvert + - unused + - varcheck + - whitespace + +linters-settings: + goimports: + local-prefixes: github.com/buildpacks/imgutil \ No newline at end of file diff --git a/vendor/github.com/buildpacks/imgutil/image.go b/vendor/github.com/buildpacks/imgutil/image.go new file mode 100644 index 0000000000..ba99143ab7 --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/image.go @@ -0,0 +1,74 @@ +package imgutil + +import ( + "fmt" + "io" + "strings" + "time" +) + +var NormalizedDateTime = time.Date(1980, time.January, 1, 0, 0, 1, 0, time.UTC) + +type SaveDiagnostic struct { + ImageName string + Cause error +} + +type SaveError struct { + Errors []SaveDiagnostic +} + +func (e SaveError) Error() string { + var errors []string + for _, d := range e.Errors { + errors = append(errors, fmt.Sprintf("[%s: %s]", d.ImageName, d.Cause.Error())) + } + return fmt.Sprintf("failed to write image to the following tags: %s", strings.Join(errors, ",")) +} + +// Platform represents the target arch/os/os_version for an image construction and querying. +type Platform struct { + Architecture string + OS string + OSVersion string +} + +type Image interface { + Name() string + Rename(name string) + Label(string) (string, error) + Labels() (map[string]string, error) + SetLabel(string, string) error + RemoveLabel(string) error + Env(key string) (string, error) + Entrypoint() ([]string, error) + SetEnv(string, string) error + SetEntrypoint(...string) error + SetWorkingDir(string) error + SetCmd(...string) error + SetOS(string) error + SetOSVersion(string) error + SetArchitecture(string) error + Rebase(string, Image) error + AddLayer(path string) error + AddLayerWithDiffID(path, diffID string) error + ReuseLayer(diffID string) error + // TopLayer returns the diff id for the top layer + TopLayer() (string, error) + // Save saves the image as `Name()` and any additional names provided to this method. + Save(additionalNames ...string) error + // Found tells whether the image exists in the repository by `Name()`. + Found() bool + // GetLayer retrieves layer by diff id. Returns a reader of the uncompressed contents of the layer. + GetLayer(diffID string) (io.ReadCloser, error) + Delete() error + CreatedAt() (time.Time, error) + Identifier() (Identifier, error) + OS() (string, error) + OSVersion() (string, error) + Architecture() (string, error) + // ManifestSize returns the size of the manifest. If a manifest doesn't exist, it returns 0. + ManifestSize() (int64, error) +} + +type Identifier fmt.Stringer diff --git a/vendor/github.com/buildpacks/imgutil/layer/bcdhive_generated.go b/vendor/github.com/buildpacks/imgutil/layer/bcdhive_generated.go new file mode 100644 index 0000000000..657517721e --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/layer/bcdhive_generated.go @@ -0,0 +1,31 @@ +package layer + +// Code generated by github.com/buildpacks/imgutil/tools/bcdhive_generator DO NOT EDIT + +import ( + "bytes" + "compress/gzip" + "encoding/base64" + "io/ioutil" +) + +const encodedBytes = "H4sIAAAAAAAC/+yaPWwbZRjH/5cmJAQoF/tcBanDoUSwcJXPvri+KaJN2gAlQZBWDBm4j9fkajuxbAOtqqCMGTuwMCB5YOhCxcYICxJiQB4Zu9EBoUgsgYEXPfeR88dZbRFDpT6/6HyP3/d5/T73PH/Feh+5LT6uKQpA14Oj/lcvff0zmZhBBNk6oheyd7CKVezgOjZxGTvooo0AHuqh3UQLO1jDOq7gTVzHNWyDeVr5Pqj+zVlgGIZhGIZhGIZ5Nth1g73QUNOxpA9Adk9KuVd/A/rrl75Mxn6YBaaS9QuAlFKSTffD+H4uY68XASwvL7/3/tb21oWNt27Q2E//SNmpA7SOrhcAnFcABdNfQAFUJVqrgfb8HfPhu0W8gikoURAzOoBXyZ5fpPeHY+MZ/ksT/Jdi/1W8POSvp/7q2Di0yD+KdSZHr/Na5Ds4NvFzh3zPhH2XEZ/hzwE2zrJ2GYZhGIZhGIZhmCc8/6vD5/9BovO/PnT+1wfmZQwdV++q0Rk+6QckvouxTWf7NdHx2kGrG+ynbYfGrgJdBfzyj78cSyl7KtCP4+lLKT+tq5imczpA9/B3CFeCdvMzpy3e3feDWiD8095F6D8b7nmsRr50vSNubzpNEc5fwmWsoTjyF3FvwvMm/Y4NLbvfcS0jd7MAttybwut28FBK2didOn1OdQH4Y/3+20cZ+9Fcsh/yj99feQ3AnYpT8cya6Rortm8Zpikcw7bKVcO2iiVRcVzftqsHad4PF4AP/lz9K6vONDdaZ5q4lRuv81xcm0fVeS4XPf8JPUsuqdd0WN8z0NWkvtu3W2KS/iiuuC2EWxPykyVninG9IZpir9tJxpK6JHH1coB9/7d7Wfv2ctn5OMqP5+MsgPMD+5qlImEl88fhvgoW88DSN899R/m4m0/1S2u/zaf6jeMO5118ggAN+GjBCX9708EFBNinWB6hpw8LT6gn2xdOtVLyjBXP9w1LXCwajueZRq1ccsuW5VsX7ZWDNI+Jnvp54Orn3atZeaS5rDz2tf+uq4+0VFe/asO6moL6WLrq51Nd9Qv/j65O49Im6+pEy87Hg0K2rjYH9i2VQ12VR3XVKgAn8zduUj4eFlJd0dq5c+O6ov97d1CBgwo8mKjBhAsDK7Dhw4IBEyYEHBiwYaGMamwVUYIIV7rwYcNGFQfp98fz/B3LMAzDMAzzNPFvAAAA///Odx8+ADAAAA==" + +func BaseLayerBCD() ([]byte, error) { + gzipBytes, err := base64.StdEncoding.DecodeString(encodedBytes) + if err != nil { + return nil, err + } + + gzipReader, err := gzip.NewReader(bytes.NewBuffer(gzipBytes)) + if err != nil { + return nil, err + } + + decodedBytes, err := ioutil.ReadAll(gzipReader) + if err != nil { + return nil, err + } + + return decodedBytes, nil +} diff --git a/vendor/github.com/buildpacks/imgutil/layer/pax_permissions.go b/vendor/github.com/buildpacks/imgutil/layer/pax_permissions.go new file mode 100644 index 0000000000..7bbe5f7ca2 --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/layer/pax_permissions.go @@ -0,0 +1,12 @@ +package layer + +// $sddl = (ConvertFrom-SddlString $sddlValue) +// $sddlBytes = [byte[]]::New($sddl.RawDescriptor.BinaryLength) +// $sddl.RawDescriptor.GetBinaryForm($sddlBytes, 0) +// [Convert]::ToBase64String($sddlBytes) + +// owner: BUILTIN/Administrators group: BUILTIN/Administrators ($sddlValue="O:BAG:BA") +const AdministratratorOwnerAndGroupSID = "AQAAgBQAAAAkAAAAAAAAAAAAAAABAgAAAAAABSAAAAAgAgAAAQIAAAAAAAUgAAAAIAIAAA==" + +// owner: BUILTIN/Users group: BUILTIN/Users ($sddlValue="O:BUG:BU") +const UserOwnerAndGroupSID = "AQAAgBQAAAAkAAAAAAAAAAAAAAABAgAAAAAABSAAAAAhAgAAAQIAAAAAAAUgAAAAIQIAAA==" diff --git a/vendor/github.com/buildpacks/imgutil/layer/windows_baselayer.go b/vendor/github.com/buildpacks/imgutil/layer/windows_baselayer.go new file mode 100644 index 0000000000..d973bb74cc --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/layer/windows_baselayer.go @@ -0,0 +1,84 @@ +package layer + +import ( + "archive/tar" + "bytes" + "io" +) + +// Generate using `make generate` +//go:generate docker run --rm -v $PWD:/out/ bcdhive-generator -file=/out/layer/bcdhive_generated.go -package=layer -func=BaseLayerBCD + +// Windows base layers must follow this pattern: +// \-> UtilityVM/Files/EFI/Microsoft/Boot/BCD (file must exist and a valid BCD format - from bcdhive_gen) +// \-> Files/Windows/System32/config/DEFAULT (file and must exist but can be empty) +// \-> Files/Windows/System32/config/SAM (file must exist but can be empty) +// \-> Files/Windows/System32/config/SECURITY (file must exist but can be empty) +// \-> Files/Windows/System32/config/SOFTWARE (file must exist but can be empty) +// \-> Files/Windows/System32/config/SYSTEM (file must exist but can be empty) +// Refs: +// https://github.com/microsoft/hcsshim/blob/master/internal/wclayer/legacy.go +// https://github.com/moby/moby/blob/master/daemon/graphdriver/windows/windows.go#L48 +func WindowsBaseLayer() (io.Reader, error) { + bcdBytes, err := BaseLayerBCD() + if err != nil { + return nil, err + } + + layerBuffer := &bytes.Buffer{} + tw := tar.NewWriter(layerBuffer) + + if err := tw.WriteHeader(&tar.Header{Name: "UtilityVM", Typeflag: tar.TypeDir}); err != nil { + return nil, err + } + if err := tw.WriteHeader(&tar.Header{Name: "UtilityVM/Files", Typeflag: tar.TypeDir}); err != nil { + return nil, err + } + if err := tw.WriteHeader(&tar.Header{Name: "UtilityVM/Files/EFI", Typeflag: tar.TypeDir}); err != nil { + return nil, err + } + if err := tw.WriteHeader(&tar.Header{Name: "UtilityVM/Files/EFI/Microsoft", Typeflag: tar.TypeDir}); err != nil { + return nil, err + } + if err := tw.WriteHeader(&tar.Header{Name: "UtilityVM/Files/EFI/Microsoft/Boot", Typeflag: tar.TypeDir}); err != nil { + return nil, err + } + + if err := tw.WriteHeader(&tar.Header{Name: "UtilityVM/Files/EFI/Microsoft/Boot/BCD", Size: int64(len(bcdBytes)), Mode: 0644}); err != nil { + return nil, err + } + if _, err := tw.Write(bcdBytes); err != nil { + return nil, err + } + + if err := tw.WriteHeader(&tar.Header{Name: "Files", Typeflag: tar.TypeDir}); err != nil { + return nil, err + } + if err := tw.WriteHeader(&tar.Header{Name: "Files/Windows", Typeflag: tar.TypeDir}); err != nil { + return nil, err + } + if err := tw.WriteHeader(&tar.Header{Name: "Files/Windows/System32", Typeflag: tar.TypeDir}); err != nil { + return nil, err + } + if err := tw.WriteHeader(&tar.Header{Name: "Files/Windows/System32/config", Typeflag: tar.TypeDir}); err != nil { + return nil, err + } + + if err := tw.WriteHeader(&tar.Header{Name: "Files/Windows/System32/config/DEFAULT", Size: 0, Mode: 0644}); err != nil { + return nil, err + } + if err := tw.WriteHeader(&tar.Header{Name: "Files/Windows/System32/config/SAM", Size: 0, Mode: 0644}); err != nil { + return nil, err + } + if err := tw.WriteHeader(&tar.Header{Name: "Files/Windows/System32/config/SECURITY", Size: 0, Mode: 0644}); err != nil { + return nil, err + } + if err := tw.WriteHeader(&tar.Header{Name: "Files/Windows/System32/config/SOFTWARE", Size: 0, Mode: 0644}); err != nil { + return nil, err + } + if err := tw.WriteHeader(&tar.Header{Name: "Files/Windows/System32/config/SYSTEM", Size: 0, Mode: 0644}); err != nil { + return nil, err + } + + return layerBuffer, nil +} diff --git a/vendor/github.com/buildpacks/imgutil/layer/windows_writer.go b/vendor/github.com/buildpacks/imgutil/layer/windows_writer.go new file mode 100644 index 0000000000..8f87f0934f --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/layer/windows_writer.go @@ -0,0 +1,130 @@ +package layer + +import ( + "archive/tar" + "fmt" + "io" + "path" + "strings" +) + +type WindowsWriter struct { + tarWriter *tar.Writer + writtenParentPaths map[string]bool +} + +func NewWindowsWriter(fileWriter io.Writer) *WindowsWriter { + return &WindowsWriter{ + tarWriter: tar.NewWriter(fileWriter), + writtenParentPaths: map[string]bool{}, + } +} + +func (w *WindowsWriter) Write(content []byte) (int, error) { + return w.tarWriter.Write(content) +} + +func (w *WindowsWriter) WriteHeader(header *tar.Header) error { + if err := w.initializeLayer(); err != nil { + return err + } + + if err := w.validateHeader(header); err != nil { + return err + } + header.Name = layerFilesPath(header.Name) + + err := w.writeParentPaths(header.Name) + if err != nil { + return err + } + + header.Format = tar.FormatPAX + if header.PAXRecords == nil { + header.PAXRecords = map[string]string{} + } + ensureSecurityDescriptor(header) + + if header.Typeflag == tar.TypeDir { + return w.writeDirHeader(header) + } + return w.tarWriter.WriteHeader(header) +} + +func ensureSecurityDescriptor(header *tar.Header) { + if _, ok := header.PAXRecords["MSWINDOWS.rawsd"]; !ok { + if header.Uid == 0 && header.Gid == 0 { + header.PAXRecords["MSWINDOWS.rawsd"] = AdministratratorOwnerAndGroupSID + } else { + header.PAXRecords["MSWINDOWS.rawsd"] = UserOwnerAndGroupSID + } + } +} + +func (w *WindowsWriter) Close() (err error) { + defer func() { + closeErr := w.tarWriter.Close() + if err == nil { + err = closeErr + } + }() + return w.initializeLayer() +} + +func (w *WindowsWriter) Flush() error { + return w.tarWriter.Flush() +} + +func (w *WindowsWriter) writeParentPaths(childPath string) error { + var parentDir string + for _, pathPart := range strings.Split(path.Dir(childPath), "/") { + parentDir = path.Join(parentDir, pathPart) + + if err := w.writeDirHeader(&tar.Header{ + Name: parentDir, + Typeflag: tar.TypeDir, + }); err != nil { + return err + } + } + return nil +} + +func layerFilesPath(origPath string) string { + return path.Join("Files", origPath) +} + +func (w *WindowsWriter) initializeLayer() error { + if err := w.writeDirHeader(&tar.Header{ + Name: "Files", + Typeflag: tar.TypeDir, + }); err != nil { + return err + } + if err := w.writeDirHeader(&tar.Header{ + Name: "Hives", + Typeflag: tar.TypeDir, + }); err != nil { + return err + } + return nil +} + +func (w *WindowsWriter) writeDirHeader(header *tar.Header) error { + if w.writtenParentPaths[header.Name] { + return nil + } + + if err := w.tarWriter.WriteHeader(header); err != nil { + return err + } + w.writtenParentPaths[header.Name] = true + return nil +} + +func (w *WindowsWriter) validateHeader(header *tar.Header) error { + if !path.IsAbs(header.Name) { + return fmt.Errorf("invalid header name: must be absolute, posix path: %s", header.Name) + } + return nil +} diff --git a/vendor/github.com/buildpacks/imgutil/local/identifier.go b/vendor/github.com/buildpacks/imgutil/local/identifier.go new file mode 100644 index 0000000000..dd24fba1bb --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/local/identifier.go @@ -0,0 +1,9 @@ +package local + +type IDIdentifier struct { + ImageID string +} + +func (i IDIdentifier) String() string { + return i.ImageID +} diff --git a/vendor/github.com/buildpacks/imgutil/local/local.go b/vendor/github.com/buildpacks/imgutil/local/local.go new file mode 100644 index 0000000000..5751a5c839 --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/local/local.go @@ -0,0 +1,885 @@ +package local + +import ( + "archive/tar" + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/buildpacks/imgutil/layer" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/client" + "github.com/docker/docker/pkg/jsonmessage" + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/pkg/errors" + + "github.com/buildpacks/imgutil" +) + +type Image struct { + docker client.CommonAPIClient + repoName string + inspect types.ImageInspect + layerPaths []string + prevImage *Image // reused layers will be fetched from prevImage + downloadBaseOnce *sync.Once +} + +type ImageOption func(*options) error + +type options struct { + platform imgutil.Platform + baseImageRepoName string + prevImageRepoName string +} + +//WithPreviousImage loads an existing image as a source for reusable layers. +//Use with ReuseLayer(). +//Ignored if image is not found. +func WithPreviousImage(imageName string) ImageOption { + return func(i *options) error { + i.prevImageRepoName = imageName + return nil + } +} + +//FromBaseImage loads an existing image as the config and layers for the new image. +//Ignored if image is not found. +func FromBaseImage(imageName string) ImageOption { + return func(i *options) error { + i.baseImageRepoName = imageName + return nil + } +} + +//WithDefaultPlatform provides Architecture/OS/OSVersion defaults for the new image. +//Defaults for a new image are ignored when FromBaseImage returns an image. +func WithDefaultPlatform(platform imgutil.Platform) ImageOption { + return func(i *options) error { + i.platform = platform + return nil + } +} + +//NewImage returns a new Image that can be modified and saved to a registry. +func NewImage(repoName string, dockerClient client.CommonAPIClient, ops ...ImageOption) (*Image, error) { + imageOpts := &options{} + for _, op := range ops { + if err := op(imageOpts); err != nil { + return nil, err + } + } + + platform, err := defaultPlatform(dockerClient) + if err != nil { + return nil, err + } + + if (imageOpts.platform != imgutil.Platform{}) { + if err := validatePlatformOption(platform, imageOpts.platform); err != nil { + return nil, err + } + platform = imageOpts.platform + } + + inspect := defaultInspect(platform) + + image := &Image{ + docker: dockerClient, + repoName: repoName, + inspect: inspect, + layerPaths: make([]string, len(inspect.RootFS.Layers)), + downloadBaseOnce: &sync.Once{}, + } + + if imageOpts.prevImageRepoName != "" { + if err := processPreviousImageOption(image, imageOpts.prevImageRepoName, platform, dockerClient); err != nil { + return nil, err + } + } + + if imageOpts.baseImageRepoName != "" { + if err := processBaseImageOption(image, imageOpts.baseImageRepoName, platform, dockerClient); err != nil { + return nil, err + } + } + + if image.inspect.Os == "windows" { + if err := prepareNewWindowsImage(image); err != nil { + return nil, err + } + } + + return image, nil +} + +func validatePlatformOption(defaultPlatform imgutil.Platform, optionPlatform imgutil.Platform) error { + if optionPlatform.OS != "" && optionPlatform.OS != defaultPlatform.OS { + return fmt.Errorf("invalid os: platform os %q must match the daemon os %q", optionPlatform.OS, defaultPlatform.OS) + } + + return nil +} + +func processPreviousImageOption(image *Image, prevImageRepoName string, platform imgutil.Platform, dockerClient client.CommonAPIClient) error { + if _, err := inspectOptionalImage(dockerClient, prevImageRepoName, platform); err != nil { + return err + } + + prevImage, err := NewImage(prevImageRepoName, dockerClient, FromBaseImage(prevImageRepoName)) + if err != nil { + return errors.Wrapf(err, "getting previous image %q", prevImageRepoName) + } + + image.prevImage = prevImage + + return nil +} + +func processBaseImageOption(image *Image, baseImageRepoName string, platform imgutil.Platform, dockerClient client.CommonAPIClient) error { + inspect, err := inspectOptionalImage(dockerClient, baseImageRepoName, platform) + if err != nil { + return err + } + + image.inspect = inspect + image.layerPaths = make([]string, len(image.inspect.RootFS.Layers)) + + return nil +} + +func prepareNewWindowsImage(image *Image) error { + // only append base layer to empty image + if len(image.inspect.RootFS.Layers) > 0 { + return nil + } + + layerReader, err := layer.WindowsBaseLayer() + if err != nil { + return err + } + + layerFile, err := ioutil.TempFile("", "imgutil.local.image.windowsbaselayer") + if err != nil { + return errors.Wrap(err, "creating temp file") + } + defer layerFile.Close() + + hasher := sha256.New() + + multiWriter := io.MultiWriter(layerFile, hasher) + + if _, err := io.Copy(multiWriter, layerReader); err != nil { + return errors.Wrap(err, "copying base layer") + } + + diffID := "sha256:" + hex.EncodeToString(hasher.Sum(nil)) + + if err := image.AddLayerWithDiffID(layerFile.Name(), diffID); err != nil { + return errors.Wrap(err, "adding base layer to image") + } + + return nil +} + +func (i *Image) Label(key string) (string, error) { + labels := i.inspect.Config.Labels + return labels[key], nil +} + +func (i *Image) Labels() (map[string]string, error) { + copiedLabels := make(map[string]string) + for i, l := range i.inspect.Config.Labels { + copiedLabels[i] = l + } + return copiedLabels, nil +} + +func (i *Image) Env(key string) (string, error) { + for _, envVar := range i.inspect.Config.Env { + parts := strings.Split(envVar, "=") + if parts[0] == key { + return parts[1], nil + } + } + return "", nil +} + +func (i *Image) Entrypoint() ([]string, error) { + return i.inspect.Config.Entrypoint, nil +} + +func (i *Image) OS() (string, error) { + return i.inspect.Os, nil +} + +func (i *Image) OSVersion() (string, error) { + return i.inspect.OsVersion, nil +} + +func (i *Image) Architecture() (string, error) { + return i.inspect.Architecture, nil +} + +func (i *Image) Rename(name string) { + i.repoName = name +} + +func (i *Image) Name() string { + return i.repoName +} + +func (i *Image) Found() bool { + return i.inspect.ID != "" +} + +func (i *Image) Identifier() (imgutil.Identifier, error) { + return IDIdentifier{ + ImageID: strings.TrimPrefix(i.inspect.ID, "sha256:"), + }, nil +} + +func (i *Image) CreatedAt() (time.Time, error) { + createdAtTime := i.inspect.Created + createdTime, err := time.Parse(time.RFC3339Nano, createdAtTime) + + if err != nil { + return time.Time{}, err + } + return createdTime, nil +} + +func (i *Image) Rebase(baseTopLayer string, newBase imgutil.Image) error { + ctx := context.Background() + + // FIND TOP LAYER + var keepLayersIdx int + for idx, diffID := range i.inspect.RootFS.Layers { + if diffID == baseTopLayer { + keepLayersIdx = idx + 1 + break + } + } + if keepLayersIdx == 0 { + return fmt.Errorf("%q not found in %q during rebase", baseTopLayer, i.repoName) + } + + // DOWNLOAD IMAGE + if err := i.downloadBaseLayersOnce(); err != nil { + return err + } + + // SWITCH BASE LAYERS + newBaseInspect, _, err := i.docker.ImageInspectWithRaw(ctx, newBase.Name()) + if err != nil { + return errors.Wrapf(err, "read config for new base image %q", newBase) + } + i.inspect.ID = newBaseInspect.ID + i.downloadBaseOnce = &sync.Once{} + i.inspect.RootFS.Layers = append(newBaseInspect.RootFS.Layers, i.inspect.RootFS.Layers[keepLayersIdx:]...) + i.layerPaths = append(make([]string, len(newBaseInspect.RootFS.Layers)), i.layerPaths[keepLayersIdx:]...) + return nil +} + +func (i *Image) SetLabel(key, val string) error { + if i.inspect.Config.Labels == nil { + i.inspect.Config.Labels = map[string]string{} + } + + i.inspect.Config.Labels[key] = val + return nil +} + +func (i *Image) SetOS(osVal string) error { + if osVal != i.inspect.Os { + return fmt.Errorf("invalid os: must match the daemon: %q", i.inspect.Os) + } + return nil +} + +func (i *Image) SetOSVersion(osVersion string) error { + i.inspect.OsVersion = osVersion + return nil +} + +func (i *Image) SetArchitecture(architecture string) error { + i.inspect.Architecture = architecture + return nil +} + +func (i *Image) RemoveLabel(key string) error { + delete(i.inspect.Config.Labels, key) + return nil +} + +func (i *Image) SetEnv(key, val string) error { + ignoreCase := i.inspect.Os == "windows" + for idx, kv := range i.inspect.Config.Env { + parts := strings.SplitN(kv, "=", 2) + foundKey := parts[0] + searchKey := key + if ignoreCase { + foundKey = strings.ToUpper(foundKey) + searchKey = strings.ToUpper(searchKey) + } + if foundKey == searchKey { + i.inspect.Config.Env[idx] = fmt.Sprintf("%s=%s", key, val) + return nil + } + } + i.inspect.Config.Env = append(i.inspect.Config.Env, fmt.Sprintf("%s=%s", key, val)) + return nil +} + +func (i *Image) SetWorkingDir(dir string) error { + i.inspect.Config.WorkingDir = dir + return nil +} + +func (i *Image) SetEntrypoint(ep ...string) error { + i.inspect.Config.Entrypoint = ep + return nil +} + +func (i *Image) SetCmd(cmd ...string) error { + i.inspect.Config.Cmd = cmd + return nil +} + +func (i *Image) TopLayer() (string, error) { + all := i.inspect.RootFS.Layers + + if len(all) == 0 { + return "", fmt.Errorf("image %q has no layers", i.repoName) + } + + topLayer := all[len(all)-1] + return topLayer, nil +} + +func (i *Image) GetLayer(diffID string) (io.ReadCloser, error) { + for l := range i.inspect.RootFS.Layers { + if i.inspect.RootFS.Layers[l] != diffID { + continue + } + if i.layerPaths[l] == "" { + if err := i.downloadBaseLayersOnce(); err != nil { + return nil, err + } + if i.layerPaths[l] == "" { + return nil, fmt.Errorf("fetching layer %q from daemon", diffID) + } + } + return os.Open(i.layerPaths[l]) + } + + return nil, fmt.Errorf("image %q does not contain layer with diff ID %q", i.repoName, diffID) +} + +func (i *Image) AddLayer(path string) error { + f, err := os.Open(filepath.Clean(path)) + if err != nil { + return errors.Wrapf(err, "AddLayer: open layer: %s", path) + } + defer f.Close() + hasher := sha256.New() + if _, err := io.Copy(hasher, f); err != nil { + return errors.Wrapf(err, "AddLayer: calculate checksum: %s", path) + } + diffID := "sha256:" + hex.EncodeToString(hasher.Sum(make([]byte, 0, hasher.Size()))) + return i.AddLayerWithDiffID(path, diffID) +} + +func (i *Image) AddLayerWithDiffID(path, diffID string) error { + i.inspect.RootFS.Layers = append(i.inspect.RootFS.Layers, diffID) + i.layerPaths = append(i.layerPaths, path) + return nil +} + +func (i *Image) ReuseLayer(diffID string) error { + if i.prevImage == nil { + return errors.New("failed to reuse layer because no previous image was provided") + } + if !i.prevImage.Found() { + return fmt.Errorf("failed to reuse layer because previous image %q was not found in daemon", i.prevImage.repoName) + } + + if err := i.prevImage.downloadBaseLayersOnce(); err != nil { + return err + } + + for l := range i.prevImage.inspect.RootFS.Layers { + if i.prevImage.inspect.RootFS.Layers[l] == diffID { + return i.AddLayerWithDiffID(i.prevImage.layerPaths[l], diffID) + } + } + return fmt.Errorf("SHA %s was not found in %s", diffID, i.prevImage.Name()) +} + +func (i *Image) Save(additionalNames ...string) error { + // during the first save attempt some layers may be excluded. The docker daemon allows this if the given set + // of layers already exists in the daemon in the given order + inspect, err := i.doSave() + if err != nil { + // populate all layer paths and try again without the above performance optimization. + if err := i.downloadBaseLayersOnce(); err != nil { + return err + } + + inspect, err = i.doSave() + if err != nil { + saveErr := imgutil.SaveError{} + for _, n := range append([]string{i.Name()}, additionalNames...) { + saveErr.Errors = append(saveErr.Errors, imgutil.SaveDiagnostic{ImageName: n, Cause: err}) + } + return saveErr + } + } + i.inspect = inspect + + var errs []imgutil.SaveDiagnostic + for _, n := range append([]string{i.Name()}, additionalNames...) { + if err := i.docker.ImageTag(context.Background(), i.inspect.ID, n); err != nil { + errs = append(errs, imgutil.SaveDiagnostic{ImageName: n, Cause: err}) + } + } + + if len(errs) > 0 { + return imgutil.SaveError{Errors: errs} + } + + return nil +} + +func (i *Image) doSave() (types.ImageInspect, error) { + ctx := context.Background() + done := make(chan error) + + t, err := name.NewTag(i.repoName, name.WeakValidation) + if err != nil { + return types.ImageInspect{}, err + } + + // returns valid 'name:tag' appending 'latest', if missing tag + repoName := t.Name() + + pr, pw := io.Pipe() + defer pw.Close() + go func() { + res, err := i.docker.ImageLoad(ctx, pr, true) + if err != nil { + done <- err + return + } + + // only return response error after response is drained and closed + responseErr := checkResponseError(res.Body) + drainCloseErr := ensureReaderClosed(res.Body) + if responseErr != nil { + done <- responseErr + return + } + if drainCloseErr != nil { + done <- drainCloseErr + } + + done <- nil + }() + + tw := tar.NewWriter(pw) + defer tw.Close() + + configFile, err := i.newConfigFile() + if err != nil { + return types.ImageInspect{}, errors.Wrap(err, "generating config file") + } + + id := fmt.Sprintf("%x", sha256.Sum256(configFile)) + if err := addTextToTar(tw, id+".json", configFile); err != nil { + return types.ImageInspect{}, err + } + + var blankIdx int + var layerPaths []string + for _, path := range i.layerPaths { + if path == "" { + layerName := fmt.Sprintf("blank_%d", blankIdx) + blankIdx++ + hdr := &tar.Header{Name: layerName, Mode: 0644, Size: 0} + if err := tw.WriteHeader(hdr); err != nil { + return types.ImageInspect{}, err + } + layerPaths = append(layerPaths, layerName) + } else { + layerName := fmt.Sprintf("/%x.tar", sha256.Sum256([]byte(path))) + f, err := os.Open(filepath.Clean(path)) + if err != nil { + return types.ImageInspect{}, err + } + defer f.Close() + if err := addFileToTar(tw, layerName, f); err != nil { + return types.ImageInspect{}, err + } + f.Close() + layerPaths = append(layerPaths, layerName) + } + } + + manifest, err := json.Marshal([]map[string]interface{}{ + { + "Config": id + ".json", + "RepoTags": []string{repoName}, + "Layers": layerPaths, + }, + }) + if err != nil { + return types.ImageInspect{}, err + } + + if err := addTextToTar(tw, "manifest.json", manifest); err != nil { + return types.ImageInspect{}, err + } + + tw.Close() + pw.Close() + err = <-done + if err != nil { + return types.ImageInspect{}, errors.Wrapf(err, "loading image %q. first error", i.repoName) + } + + inspect, _, err := i.docker.ImageInspectWithRaw(context.Background(), id) + if err != nil { + if client.IsErrNotFound(err) { + return types.ImageInspect{}, errors.Wrapf(err, "saving image %q", i.repoName) + } + return types.ImageInspect{}, err + } + + return inspect, nil +} + +func (i *Image) newConfigFile() ([]byte, error) { + cfg, err := v1Config(i.inspect) + if err != nil { + return nil, err + } + return json.Marshal(cfg) +} + +func (i *Image) Delete() error { + if !i.Found() { + return nil + } + options := types.ImageRemoveOptions{ + Force: true, + PruneChildren: true, + } + _, err := i.docker.ImageRemove(context.Background(), i.inspect.ID, options) + return err +} + +func (i *Image) ManifestSize() (int64, error) { + return 0, nil +} + +// downloadBaseLayersOnce exports the base image from the daemon and populates layerPaths the first time it is called. +// subsequent calls do nothing. +func (i *Image) downloadBaseLayersOnce() error { + var err error + if !i.Found() { + return nil + } + i.downloadBaseOnce.Do(func() { + err = i.downloadBaseLayers() + }) + if err != nil { + return errors.Wrap(err, "fetching base layers") + } + return err +} + +func (i *Image) downloadBaseLayers() error { + ctx := context.Background() + + imageReader, err := i.docker.ImageSave(ctx, []string{i.inspect.ID}) + if err != nil { + return errors.Wrapf(err, "saving base image with ID %q from the docker daemon", i.inspect.ID) + } + defer ensureReaderClosed(imageReader) + + tmpDir, err := ioutil.TempDir("", "imgutil.local.image.") + if err != nil { + return errors.Wrap(err, "failed to create temp dir") + } + + err = untar(imageReader, tmpDir) + if err != nil { + return err + } + + mf, err := os.Open(filepath.Clean(filepath.Join(tmpDir, "manifest.json"))) + if err != nil { + return err + } + defer mf.Close() + + var manifest []struct { + Config string + Layers []string + } + if err := json.NewDecoder(mf).Decode(&manifest); err != nil { + return err + } + + if len(manifest) != 1 { + return fmt.Errorf("manifest.json had unexpected number of entries: %d", len(manifest)) + } + + df, err := os.Open(filepath.Clean(filepath.Join(tmpDir, manifest[0].Config))) + if err != nil { + return err + } + defer df.Close() + + var details struct { + RootFS struct { + DiffIDs []string `json:"diff_ids"` + } `json:"rootfs"` + } + + if err = json.NewDecoder(df).Decode(&details); err != nil { + return err + } + + for l := range details.RootFS.DiffIDs { + i.layerPaths[l] = filepath.Join(tmpDir, manifest[0].Layers[l]) + } + + for l := range i.layerPaths { + if i.layerPaths[l] == "" { + return errors.New("failed to download all base layers from daemon") + } + } + + return nil +} + +func addTextToTar(tw *tar.Writer, name string, contents []byte) error { + hdr := &tar.Header{Name: name, Mode: 0644, Size: int64(len(contents))} + if err := tw.WriteHeader(hdr); err != nil { + return err + } + _, err := tw.Write(contents) + return err +} + +func addFileToTar(tw *tar.Writer, name string, contents *os.File) error { + fi, err := contents.Stat() + if err != nil { + return err + } + hdr := &tar.Header{Name: name, Mode: 0644, Size: fi.Size()} + if err := tw.WriteHeader(hdr); err != nil { + return err + } + _, err = io.Copy(tw, contents) + return err +} + +func untar(r io.Reader, dest string) error { + tr := tar.NewReader(r) + for { + hdr, err := tr.Next() + if err == io.EOF { + // end of tar archive + return nil + } + if err != nil { + return err + } + + path := filepath.Join(dest, hdr.Name) + + switch hdr.Typeflag { + case tar.TypeDir: + if err := os.MkdirAll(path, hdr.FileInfo().Mode()); err != nil { + return err + } + case tar.TypeReg, tar.TypeRegA: + _, err := os.Stat(filepath.Dir(path)) + if os.IsNotExist(err) { + if err := os.MkdirAll(filepath.Dir(path), 0750); err != nil { + return err + } + } + + fh, err := os.OpenFile(filepath.Clean(path), os.O_CREATE|os.O_WRONLY, hdr.FileInfo().Mode()) + if err != nil { + return err + } + if _, err := io.Copy(fh, tr); err != nil { + fh.Close() + return err + } + fh.Close() + case tar.TypeSymlink: + _, err := os.Stat(filepath.Dir(path)) + if os.IsNotExist(err) { + if err := os.MkdirAll(filepath.Dir(path), 0750); err != nil { + return err + } + } + + if err := os.Symlink(hdr.Linkname, path); err != nil { + return err + } + default: + return fmt.Errorf("unknown file type in tar %d", hdr.Typeflag) + } + } +} + +func inspectOptionalImage(docker client.CommonAPIClient, imageName string, platform imgutil.Platform) (types.ImageInspect, error) { + var ( + err error + inspect types.ImageInspect + ) + + if inspect, _, err = docker.ImageInspectWithRaw(context.Background(), imageName); err != nil { + if client.IsErrNotFound(err) { + return defaultInspect(platform), nil + } + + return types.ImageInspect{}, errors.Wrapf(err, "verifying image %q", imageName) + } + + return inspect, nil +} + +func defaultInspect(platform imgutil.Platform) types.ImageInspect { + return types.ImageInspect{ + Os: platform.OS, + Architecture: platform.Architecture, + OsVersion: platform.OSVersion, + Config: &container.Config{}, + } +} + +func defaultPlatform(dockerClient client.CommonAPIClient) (imgutil.Platform, error) { + daemonInfo, err := dockerClient.Info(context.Background()) + if err != nil { + return imgutil.Platform{}, err + } + + return imgutil.Platform{ + OS: daemonInfo.OSType, + Architecture: "amd64", + }, nil +} + +func v1Config(inspect types.ImageInspect) (v1.ConfigFile, error) { + history := make([]v1.History, len(inspect.RootFS.Layers)) + for i := range history { + // zero history + history[i] = v1.History{ + Created: v1.Time{Time: imgutil.NormalizedDateTime}, + } + } + diffIDs := make([]v1.Hash, len(inspect.RootFS.Layers)) + for i, layer := range inspect.RootFS.Layers { + hash, err := v1.NewHash(layer) + if err != nil { + return v1.ConfigFile{}, err + } + diffIDs[i] = hash + } + exposedPorts := make(map[string]struct{}, len(inspect.Config.ExposedPorts)) + for key, val := range inspect.Config.ExposedPorts { + exposedPorts[string(key)] = val + } + var config v1.Config + if inspect.Config != nil { + var healthcheck *v1.HealthConfig + if inspect.Config.Healthcheck != nil { + healthcheck = &v1.HealthConfig{ + Test: inspect.Config.Healthcheck.Test, + Interval: inspect.Config.Healthcheck.Interval, + Timeout: inspect.Config.Healthcheck.Timeout, + StartPeriod: inspect.Config.Healthcheck.StartPeriod, + Retries: inspect.Config.Healthcheck.Retries, + } + } + config = v1.Config{ + AttachStderr: inspect.Config.AttachStderr, + AttachStdin: inspect.Config.AttachStdin, + AttachStdout: inspect.Config.AttachStdout, + Cmd: inspect.Config.Cmd, + Healthcheck: healthcheck, + Domainname: inspect.Config.Domainname, + Entrypoint: inspect.Config.Entrypoint, + Env: inspect.Config.Env, + Hostname: inspect.Config.Hostname, + Image: inspect.Config.Image, + Labels: inspect.Config.Labels, + OnBuild: inspect.Config.OnBuild, + OpenStdin: inspect.Config.OpenStdin, + StdinOnce: inspect.Config.StdinOnce, + Tty: inspect.Config.Tty, + User: inspect.Config.User, + Volumes: inspect.Config.Volumes, + WorkingDir: inspect.Config.WorkingDir, + ExposedPorts: exposedPorts, + ArgsEscaped: inspect.Config.ArgsEscaped, + NetworkDisabled: inspect.Config.NetworkDisabled, + MacAddress: inspect.Config.MacAddress, + StopSignal: inspect.Config.StopSignal, + Shell: inspect.Config.Shell, + } + } + return v1.ConfigFile{ + Architecture: inspect.Architecture, + Created: v1.Time{Time: imgutil.NormalizedDateTime}, + History: history, + OS: inspect.Os, + OSVersion: inspect.OsVersion, + RootFS: v1.RootFS{ + Type: "layers", + DiffIDs: diffIDs, + }, + Config: config, + }, nil +} + +func checkResponseError(r io.Reader) error { + decoder := json.NewDecoder(r) + var jsonMessage jsonmessage.JSONMessage + if err := decoder.Decode(&jsonMessage); err != nil { + return errors.Wrapf(err, "parsing daemon response") + } + + if jsonMessage.Error != nil { + return errors.Wrap(jsonMessage.Error, "embedded daemon response") + } + return nil +} + +// ensureReaderClosed drains and closes and reader, returning the first error +func ensureReaderClosed(r io.ReadCloser) error { + _, err := io.Copy(ioutil.Discard, r) + if closeErr := r.Close(); closeErr != nil && err == nil { + err = closeErr + } + return err +} diff --git a/vendor/github.com/buildpacks/imgutil/remote/identifier.go b/vendor/github.com/buildpacks/imgutil/remote/identifier.go new file mode 100644 index 0000000000..5638905357 --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/remote/identifier.go @@ -0,0 +1,13 @@ +package remote + +import ( + "github.com/google/go-containerregistry/pkg/name" +) + +type DigestIdentifier struct { + Digest name.Digest +} + +func (d DigestIdentifier) String() string { + return d.Digest.String() +} diff --git a/vendor/github.com/buildpacks/imgutil/remote/remote.go b/vendor/github.com/buildpacks/imgutil/remote/remote.go new file mode 100644 index 0000000000..d861925aea --- /dev/null +++ b/vendor/github.com/buildpacks/imgutil/remote/remote.go @@ -0,0 +1,734 @@ +package remote + +import ( + "fmt" + "io" + "net/http" + "strings" + "time" + + "github.com/buildpacks/imgutil/layer" + + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/empty" + "github.com/google/go-containerregistry/pkg/v1/mutate" + "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" + "github.com/google/go-containerregistry/pkg/v1/tarball" + "github.com/google/go-containerregistry/pkg/v1/types" + "github.com/pkg/errors" + + "github.com/buildpacks/imgutil" +) + +const maxRetries = 2 + +type Image struct { + keychain authn.Keychain + repoName string + image v1.Image + prevLayers []v1.Layer +} + +type options struct { + platform imgutil.Platform + baseImageRepoName string + prevImageRepoName string +} + +type ImageOption func(*options) error + +//WithPreviousImage loads an existing image as a source for reusable layers. +//Use with ReuseLayer(). +//Ignored if image is not found. +func WithPreviousImage(imageName string) ImageOption { + return func(opts *options) error { + opts.prevImageRepoName = imageName + return nil + } +} + +//FromBaseImage loads an existing image as the config and layers for the new image. +//Ignored if image is not found. +func FromBaseImage(imageName string) ImageOption { + return func(opts *options) error { + opts.baseImageRepoName = imageName + return nil + } +} + +//WithDefaultPlatform provides Architecture/OS/OSVersion defaults for the new image. +//Defaults for a new image are ignored when FromBaseImage returns an image. +//FromBaseImage and WithPreviousImage will use the platform to choose an image from a manifest list. +func WithDefaultPlatform(platform imgutil.Platform) ImageOption { + return func(opts *options) error { + opts.platform = platform + return nil + } +} + +//NewImage returns a new Image that can be modified and saved to a Docker daemon. +func NewImage(repoName string, keychain authn.Keychain, ops ...ImageOption) (*Image, error) { + imageOpts := &options{} + for _, op := range ops { + if err := op(imageOpts); err != nil { + return nil, err + } + } + + platform := defaultPlatform() + if (imageOpts.platform != imgutil.Platform{}) { + platform = imageOpts.platform + } + + image, err := emptyImage(platform) + if err != nil { + return nil, err + } + + ri := &Image{ + keychain: keychain, + repoName: repoName, + image: image, + } + + if imageOpts.prevImageRepoName != "" { + if err := processPreviousImageOption(ri, imageOpts.prevImageRepoName, platform); err != nil { + return nil, err + } + } + + if imageOpts.baseImageRepoName != "" { + if err := processBaseImageOption(ri, imageOpts.baseImageRepoName, platform); err != nil { + return nil, err + } + } + + imgOS, err := ri.OS() + if err != nil { + return nil, err + } + if imgOS == "windows" { + if err := prepareNewWindowsImage(ri); err != nil { + return nil, err + } + } + + return ri, nil +} + +func processPreviousImageOption(ri *Image, prevImageRepoName string, platform imgutil.Platform) error { + prevImage, err := newV1Image(ri.keychain, prevImageRepoName, platform) + if err != nil { + return err + } + + prevLayers, err := prevImage.Layers() + if err != nil { + return errors.Wrapf(err, "getting layers for previous image with repo name %q", prevImageRepoName) + } + + ri.prevLayers = prevLayers + + return nil +} + +func processBaseImageOption(ri *Image, baseImageRepoName string, platform imgutil.Platform) error { + baseImage, err := newV1Image(ri.keychain, baseImageRepoName, platform) + if err != nil { + return err + } + + ri.image = baseImage + + return nil +} + +func prepareNewWindowsImage(ri *Image) error { + // only append base layer to empty image + cfgFile, err := ri.image.ConfigFile() + if err != nil { + return err + } + if len(cfgFile.RootFS.DiffIDs) > 0 { + return nil + } + + layerBytes, err := layer.WindowsBaseLayer() + if err != nil { + return err + } + + windowsBaseLayer, err := tarball.LayerFromReader(layerBytes) + if err != nil { + return err + } + + image, err := mutate.AppendLayers(ri.image, windowsBaseLayer) + if err != nil { + return err + } + + ri.image = image + + return nil +} + +func newV1Image(keychain authn.Keychain, repoName string, platform imgutil.Platform) (v1.Image, error) { + ref, auth, err := referenceForRepoName(keychain, repoName) + if err != nil { + return nil, err + } + + v1Platform := v1.Platform{ + Architecture: platform.Architecture, + OS: platform.OS, + OSVersion: platform.OSVersion, + } + + var image v1.Image + for i := 0; i <= maxRetries; i++ { + time.Sleep(100 * time.Duration(i) * time.Millisecond) // wait if retrying + image, err = remote.Image(ref, remote.WithAuth(auth), remote.WithTransport(http.DefaultTransport), remote.WithPlatform(v1Platform)) + if err != nil { + if err == io.EOF && i != maxRetries { + continue // retry if EOF + } + if transportErr, ok := err.(*transport.Error); ok && len(transportErr.Errors) > 0 { + switch transportErr.StatusCode { + case http.StatusNotFound, http.StatusUnauthorized: + return emptyImage(platform) + } + } + if strings.Contains(err.Error(), "no child with platform") { + return emptyImage(platform) + } + return nil, errors.Wrapf(err, "connect to repo store %q", repoName) + } + break + } + + return image, nil +} + +func emptyImage(platform imgutil.Platform) (v1.Image, error) { + cfg := &v1.ConfigFile{ + Architecture: platform.Architecture, + OS: platform.OS, + OSVersion: platform.OSVersion, + RootFS: v1.RootFS{ + Type: "layers", + DiffIDs: []v1.Hash{}, + }, + } + + return mutate.ConfigFile(empty.Image, cfg) +} + +func defaultPlatform() imgutil.Platform { + return imgutil.Platform{ + OS: "linux", + Architecture: "amd64", + } +} + +func referenceForRepoName(keychain authn.Keychain, ref string) (name.Reference, authn.Authenticator, error) { + var auth authn.Authenticator + r, err := name.ParseReference(ref, name.WeakValidation) + if err != nil { + return nil, nil, err + } + + auth, err = keychain.Resolve(r.Context().Registry) + if err != nil { + return nil, nil, err + } + return r, auth, nil +} + +func (i *Image) Label(key string) (string, error) { + cfg, err := i.image.ConfigFile() + if err != nil { + return "", errors.Wrapf(err, "getting config file for image %q", i.repoName) + } + if cfg == nil { + return "", fmt.Errorf("missing config for image %q", i.repoName) + } + labels := cfg.Config.Labels + return labels[key], nil +} + +func (i *Image) Labels() (map[string]string, error) { + cfg, err := i.image.ConfigFile() + if err != nil { + return nil, errors.Wrapf(err, "getting config file for image %q", i.repoName) + } + if cfg == nil { + return nil, fmt.Errorf("missing config for image %q", i.repoName) + } + return cfg.Config.Labels, nil +} + +func (i *Image) Env(key string) (string, error) { + cfg, err := i.image.ConfigFile() + if err != nil { + return "", errors.Wrapf(err, "getting config file for image %q", i.repoName) + } + if cfg == nil { + return "", fmt.Errorf("missing config for image %q", i.repoName) + } + for _, envVar := range cfg.Config.Env { + parts := strings.Split(envVar, "=") + if parts[0] == key { + return parts[1], nil + } + } + return "", nil +} + +func (i *Image) Entrypoint() ([]string, error) { + cfg, err := i.image.ConfigFile() + if err != nil { + return nil, errors.Wrapf(err, "getting config file for image %q", i.repoName) + } + if cfg == nil { + return nil, fmt.Errorf("missing config for image %q", i.repoName) + } + return cfg.Config.Entrypoint, nil +} + +func (i *Image) OS() (string, error) { + cfg, err := i.image.ConfigFile() + if err != nil { + return "", errors.Wrapf(err, "getting config file for image %q", i.repoName) + } + if cfg == nil { + return "", fmt.Errorf("missing config for image %q", i.repoName) + } + if cfg.OS == "" { + return "", fmt.Errorf("missing OS for image %q", i.repoName) + } + return cfg.OS, nil +} + +func (i *Image) OSVersion() (string, error) { + cfg, err := i.image.ConfigFile() + if err != nil { + return "", errors.Wrapf(err, "getting config file for image %q", i.repoName) + } + if cfg == nil { + return "", fmt.Errorf("missing config for image %q", i.repoName) + } + return cfg.OSVersion, nil +} + +func (i *Image) Architecture() (string, error) { + cfg, err := i.image.ConfigFile() + if err != nil { + return "", errors.Wrapf(err, "getting config file for image %q", i.repoName) + } + if cfg == nil { + return "", fmt.Errorf("missing config for image %q", i.repoName) + } + if cfg.Architecture == "" { + return "", fmt.Errorf("missing Architecture for image %q", i.repoName) + } + return cfg.Architecture, nil +} + +func (i *Image) Rename(name string) { + i.repoName = name +} + +func (i *Image) Name() string { + return i.repoName +} + +func (i *Image) Found() bool { + _, err := i.found() + return err == nil +} + +func (i *Image) CheckReadWriteAccess() bool { + ref, _, err := referenceForRepoName(i.keychain, i.repoName) + if err != nil { + return false + } + return i.CheckReadAccess() && remote.CheckPushPermission(ref, i.keychain, http.DefaultTransport) == nil +} + +func (i *Image) CheckReadAccess() bool { + _, err := i.found() + if err != nil { + if transportErr, ok := err.(*transport.Error); ok { + return transportErr.StatusCode != http.StatusUnauthorized && + transportErr.StatusCode != http.StatusForbidden + } + return false + } + return true +} + +func (i *Image) found() (*v1.Descriptor, error) { + ref, auth, err := referenceForRepoName(i.keychain, i.repoName) + if err != nil { + return nil, err + } + return remote.Head(ref, remote.WithAuth(auth), remote.WithTransport(http.DefaultTransport)) +} + +func (i *Image) Identifier() (imgutil.Identifier, error) { + ref, err := name.ParseReference(i.repoName, name.WeakValidation) + if err != nil { + return nil, errors.Wrapf(err, "parsing reference for image %q", i.repoName) + } + + hash, err := i.image.Digest() + if err != nil { + return nil, errors.Wrapf(err, "getting digest for image %q", i.repoName) + } + + digestRef, err := name.NewDigest(fmt.Sprintf("%s@%s", ref.Context().Name(), hash.String()), name.WeakValidation) + if err != nil { + return nil, errors.Wrap(err, "creating digest reference") + } + + return DigestIdentifier{ + Digest: digestRef, + }, nil +} + +func (i *Image) CreatedAt() (time.Time, error) { + configFile, err := i.image.ConfigFile() + if err != nil { + return time.Time{}, errors.Wrapf(err, "getting createdAt time for image %q", i.repoName) + } + return configFile.Created.UTC(), nil +} + +func (i *Image) Rebase(baseTopLayer string, newBase imgutil.Image) error { + newBaseRemote, ok := newBase.(*Image) + if !ok { + return errors.New("expected new base to be a remote image") + } + + newImage, err := mutate.Rebase(i.image, &subImage{img: i.image, topDiffID: baseTopLayer}, newBaseRemote.image) + if err != nil { + return errors.Wrap(err, "rebase") + } + + newImageConfig, err := newImage.ConfigFile() + if err != nil { + return err + } + + newBaseRemoteConfig, err := newBaseRemote.image.ConfigFile() + if err != nil { + return err + } + + newImageConfig.Architecture = newBaseRemoteConfig.Architecture + newImageConfig.OS = newBaseRemoteConfig.OS + newImageConfig.OSVersion = newBaseRemoteConfig.OSVersion + + newImage, err = mutate.ConfigFile(newImage, newImageConfig) + if err != nil { + return err + } + + i.image = newImage + return nil +} + +func (i *Image) SetLabel(key, val string) error { + configFile, err := i.image.ConfigFile() + if err != nil { + return err + } + config := *configFile.Config.DeepCopy() + if config.Labels == nil { + config.Labels = map[string]string{} + } + config.Labels[key] = val + i.image, err = mutate.Config(i.image, config) + return err +} + +func (i *Image) RemoveLabel(key string) error { + cfg, err := i.image.ConfigFile() + if err != nil { + return errors.Wrapf(err, "getting config file for image %q", i.repoName) + } + if cfg == nil { + return fmt.Errorf("missing config for image %q", i.repoName) + } + config := *cfg.Config.DeepCopy() + delete(config.Labels, key) + i.image, err = mutate.Config(i.image, config) + return err +} + +func (i *Image) SetEnv(key, val string) error { + configFile, err := i.image.ConfigFile() + if err != nil { + return err + } + config := *configFile.Config.DeepCopy() + ignoreCase := configFile.OS == "windows" + for idx, e := range config.Env { + parts := strings.Split(e, "=") + foundKey := parts[0] + searchKey := key + if ignoreCase { + foundKey = strings.ToUpper(foundKey) + searchKey = strings.ToUpper(searchKey) + } + if foundKey == searchKey { + config.Env[idx] = fmt.Sprintf("%s=%s", key, val) + i.image, err = mutate.Config(i.image, config) + return err + } + } + config.Env = append(config.Env, fmt.Sprintf("%s=%s", key, val)) + i.image, err = mutate.Config(i.image, config) + return err +} + +func (i *Image) SetWorkingDir(dir string) error { + configFile, err := i.image.ConfigFile() + if err != nil { + return err + } + config := *configFile.Config.DeepCopy() + config.WorkingDir = dir + i.image, err = mutate.Config(i.image, config) + return err +} + +func (i *Image) SetEntrypoint(ep ...string) error { + configFile, err := i.image.ConfigFile() + if err != nil { + return err + } + config := *configFile.Config.DeepCopy() + config.Entrypoint = ep + i.image, err = mutate.Config(i.image, config) + return err +} + +func (i *Image) SetCmd(cmd ...string) error { + configFile, err := i.image.ConfigFile() + if err != nil { + return err + } + config := *configFile.Config.DeepCopy() + config.Cmd = cmd + i.image, err = mutate.Config(i.image, config) + return err +} + +func (i *Image) SetOS(osVal string) error { + configFile, err := i.image.ConfigFile() + if err != nil { + return err + } + configFile.OS = osVal + i.image, err = mutate.ConfigFile(i.image, configFile) + return err +} + +func (i *Image) SetOSVersion(osVersion string) error { + configFile, err := i.image.ConfigFile() + if err != nil { + return err + } + configFile.OSVersion = osVersion + i.image, err = mutate.ConfigFile(i.image, configFile) + return err +} + +func (i *Image) SetArchitecture(architecture string) error { + configFile, err := i.image.ConfigFile() + if err != nil { + return err + } + configFile.Architecture = architecture + i.image, err = mutate.ConfigFile(i.image, configFile) + return err +} + +func (i *Image) TopLayer() (string, error) { + all, err := i.image.Layers() + if err != nil { + return "", err + } + if len(all) == 0 { + return "", fmt.Errorf("image %q has no layers", i.Name()) + } + topLayer := all[len(all)-1] + hex, err := topLayer.DiffID() + if err != nil { + return "", err + } + return hex.String(), nil +} + +func (i *Image) GetLayer(sha string) (io.ReadCloser, error) { + layers, err := i.image.Layers() + if err != nil { + return nil, err + } + + layer, err := findLayerWithSha(layers, sha) + if err != nil { + return nil, err + } + + return layer.Uncompressed() +} + +func (i *Image) AddLayer(path string) error { + layer, err := tarball.LayerFromFile(path) + if err != nil { + return err + } + i.image, err = mutate.AppendLayers(i.image, layer) + if err != nil { + return errors.Wrap(err, "add layer") + } + return nil +} + +func (i *Image) AddLayerWithDiffID(path, diffID string) error { + // this is equivalent to AddLayer in the remote case + // it exists to provide optimize performance for local images + return i.AddLayer(path) +} + +func (i *Image) ReuseLayer(sha string) error { + layer, err := findLayerWithSha(i.prevLayers, sha) + if err != nil { + return err + } + i.image, err = mutate.AppendLayers(i.image, layer) + return err +} + +func findLayerWithSha(layers []v1.Layer, diffID string) (v1.Layer, error) { + for _, layer := range layers { + dID, err := layer.DiffID() + if err != nil { + return nil, errors.Wrap(err, "get diff ID for previous image layer") + } + if diffID == dID.String() { + return layer, nil + } + } + return nil, fmt.Errorf("previous image did not have layer with diff id %q", diffID) +} + +func (i *Image) Save(additionalNames ...string) error { + var err error + + allNames := append([]string{i.repoName}, additionalNames...) + + i.image, err = mutate.CreatedAt(i.image, v1.Time{Time: imgutil.NormalizedDateTime}) + if err != nil { + return errors.Wrap(err, "set creation time") + } + + cfg, err := i.image.ConfigFile() + if err != nil { + return errors.Wrap(err, "get image config") + } + cfg = cfg.DeepCopy() + + layers, err := i.image.Layers() + if err != nil { + return errors.Wrap(err, "get image layers") + } + cfg.History = make([]v1.History, len(layers)) + for i := range cfg.History { + cfg.History[i] = v1.History{ + Created: v1.Time{Time: imgutil.NormalizedDateTime}, + } + } + + cfg.DockerVersion = "" + cfg.Container = "" + i.image, err = mutate.ConfigFile(i.image, cfg) + if err != nil { + return errors.Wrap(err, "zeroing history") + } + + var diagnostics []imgutil.SaveDiagnostic + for _, n := range allNames { + if err := i.doSave(n); err != nil { + diagnostics = append(diagnostics, imgutil.SaveDiagnostic{ImageName: n, Cause: err}) + } + } + if len(diagnostics) > 0 { + return imgutil.SaveError{Errors: diagnostics} + } + + return nil +} + +func (i *Image) doSave(imageName string) error { + ref, auth, err := referenceForRepoName(i.keychain, imageName) + if err != nil { + return err + } + return remote.Write(ref, i.image, remote.WithAuth(auth)) +} + +func (i *Image) Delete() error { + id, err := i.Identifier() + if err != nil { + return err + } + ref, auth, err := referenceForRepoName(i.keychain, id.String()) + if err != nil { + return err + } + return remote.Delete(ref, remote.WithAuth(auth)) +} + +func (i *Image) ManifestSize() (int64, error) { + return i.image.Size() +} + +type subImage struct { + img v1.Image + topDiffID string +} + +func (si *subImage) Layers() ([]v1.Layer, error) { + all, err := si.img.Layers() + if err != nil { + return nil, err + } + for i, l := range all { + d, err := l.DiffID() + if err != nil { + return nil, err + } + if d.String() == si.topDiffID { + return all[0 : i+1], nil + } + } + return nil, errors.New("could not find base layer in image") +} +func (si *subImage) ConfigFile() (*v1.ConfigFile, error) { return si.img.ConfigFile() } +func (si *subImage) BlobSet() (map[v1.Hash]struct{}, error) { panic("Not Implemented") } +func (si *subImage) MediaType() (types.MediaType, error) { panic("Not Implemented") } +func (si *subImage) ConfigName() (v1.Hash, error) { panic("Not Implemented") } +func (si *subImage) RawConfigFile() ([]byte, error) { panic("Not Implemented") } +func (si *subImage) Digest() (v1.Hash, error) { panic("Not Implemented") } +func (si *subImage) Manifest() (*v1.Manifest, error) { panic("Not Implemented") } +func (si *subImage) RawManifest() ([]byte, error) { panic("Not Implemented") } +func (si *subImage) LayerByDigest(v1.Hash) (v1.Layer, error) { panic("Not Implemented") } +func (si *subImage) LayerByDiffID(v1.Hash) (v1.Layer, error) { panic("Not Implemented") } +func (si *subImage) Size() (int64, error) { panic("Not Implemented") } diff --git a/vendor/github.com/buildpacks/lifecycle/.dockerignore b/vendor/github.com/buildpacks/lifecycle/.dockerignore new file mode 100644 index 0000000000..1fcb1529f8 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/.dockerignore @@ -0,0 +1 @@ +out diff --git a/vendor/github.com/buildpacks/lifecycle/.gitignore b/vendor/github.com/buildpacks/lifecycle/.gitignore new file mode 100644 index 0000000000..ebc79ed6d5 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +.idea +*.coverprofile +*.test +*~ +/out + +acceptance/testdata/*/**/container/cnb/lifecycle/* +acceptance/testdata/*/**/container/docker-config/* +acceptance/testdata/exporter/container/layers/*analyzed.toml diff --git a/vendor/github.com/buildpacks/lifecycle/.gitpod.yml b/vendor/github.com/buildpacks/lifecycle/.gitpod.yml new file mode 100644 index 0000000000..098b5ca3e6 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/.gitpod.yml @@ -0,0 +1,19 @@ + +tasks: + # allow socket to be writable by all + # (necessary for acceptance tests / calls from within container) + - init: chmod ugo+w /var/run/docker.sock + # build linux to install dependencies + - init: make tidy build-linux +github: + prebuilds: + master: true + branches: true + pullRequests: true + pullRequestsFromForks: true + addCheck: true + +vscode: + extensions: + - golang.go + - ms-azuretools.vscode-docker diff --git a/vendor/github.com/buildpacks/lifecycle/CODEOWNERS b/vendor/github.com/buildpacks/lifecycle/CODEOWNERS new file mode 100644 index 0000000000..d3a11dc1d0 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/CODEOWNERS @@ -0,0 +1 @@ +* @buildpacks/implementation-maintainers diff --git a/vendor/github.com/buildpacks/lifecycle/CONTRIBUTING.md b/vendor/github.com/buildpacks/lifecycle/CONTRIBUTING.md new file mode 100644 index 0000000000..cf795ca2e0 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/CONTRIBUTING.md @@ -0,0 +1,42 @@ +## Policies + +This repository adheres to the following project policies: + +- [Code of Conduct][code-of-conduct] - How we should act with each other. +- [Contributing][contributing] - General contributing standards. +- [Security][security] - Reporting security concerns. +- [Support][support] - Getting support. + +## Contributing to this repository + +### Welcome + +We welcome contributions to this repository! To get a sense of what the team is currently focusing on, check out our [milestones](https://github.com/buildpacks/lifecycle/milestones). Issues labeled [good first issue](https://github.com/buildpacks/lifecycle/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) and issues in our [docs repo](https://github.com/buildpacks/docs/issues?q=is%3Aissue+is%3Aopen+label%3Ateam%2Fimplementations) are great places to get started, but you are welcome to work on any issue that interests you. For issues requiring a greater degree of coordination, such as those labeled `status/needs-discussion` or that are part of larger epics, please reach out in the #implementation channel in [Slack](https://slack.buildpacks.io/). + +### Development + +Aside from the policies above, you may find [DEVELOPMENT.md](DEVELOPMENT.md) helpful in developing in this repository. + +### Background + +Here are some topics that might be helpful in further understanding the lifecycle: + +* Cloud Native Buildpacks platform api spec + * Example platforms: [pack CLI](https://github.com/buildpack/pack), [Tekton](https://github.com/tektoncd/catalog/blob/master/task/buildpacks/0.1/README.md) +* Cloud Native Buildpacks buildpack api spec + * Example buildpack providers: [Google](https://github.com/GoogleCloudPlatform/buildpacks), [Heroku](https://www.heroku.com/), [Paketo](https://paketo.io/) +* The Open Container Initiative (OCI) and [OCI image spec](https://github.com/opencontainers/image-spec) +* Questions to deepen understanding: + * What are the different [lifecycle phases](https://buildpacks.io/docs/concepts/components/lifecycle/)? What is the purpose of each phase? + * What is a [builder](https://buildpacks.io/docs/concepts/components/builder/)? Is it required to run the lifecycle? + * What is the [untrusted builder workflow](https://medium.com/buildpacks/faster-more-secure-builds-with-pack-0-11-0-4d0c633ca619)? Why do we have this flow? + * What is the [launcher](https://github.com/buildpacks/spec/blob/main/platform.md#launch)? Why do we have a launcher? + * What does a [buildpack](https://buildpacks.io/docs/concepts/components/buildpack/) do? Where does it write data? How does it communicate with the lifecycle? + * What does a [platform](https://buildpacks.io/docs/concepts/components/platform/) do? What things does it know about that the lifecycle does not? How does it communicate with the lifecycle? + * What is a [stack](https://buildpacks.io/docs/concepts/components/stack/)? Who produces stacks? Why is the stack concept important for the lifecycle? + +[code-of-conduct]: https://github.com/buildpacks/.github/blob/main/CODE_OF_CONDUCT.md +[contributing]: https://github.com/buildpacks/.github/blob/main/CONTRIBUTING.md +[security]: https://github.com/buildpacks/.github/blob/main/SECURITY.md +[support]: https://github.com/buildpacks/.github/blob/main/SUPPORT.md +[pull-request-process]: https://github.com/buildpacks/.github/blob/main/CONTRIBUTIONS.md#pull-request-process diff --git a/vendor/github.com/buildpacks/lifecycle/DEVELOPMENT.md b/vendor/github.com/buildpacks/lifecycle/DEVELOPMENT.md new file mode 100644 index 0000000000..56cd50d784 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/DEVELOPMENT.md @@ -0,0 +1,92 @@ +# Development + +## Prerequisites + +* [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) + * macOS: _(built-in)_ + * Windows: + * `choco install git -y` + * `git config --global core.autocrlf false` +* [Go](https://golang.org/doc/install) + * macOS: `brew install go` + * Windows: `choco install golang -y` +* [Docker](https://www.docker.com/products/docker-desktop) +* Make (and build tools) + * macOS: `xcode-select --install` + * Windows: + * `choco install cygwin make -y` + * `[Environment]::SetEnvironmentVariable("PATH", "C:\tools\cygwin\bin;$ENV:PATH", "MACHINE")` + +### Caveats + +* The acceptance tests require the docker daemon to be able to communicate with a local containerized insecure registry. On Docker Desktop 3.3.x, this may result in failures such as: `Expected nil: push response: : Get http://localhost:/v2/: dial tcp [::1]:: connect: connection refused`. To fix these failures, it may be necessary to add the following to the Docker Desktop Engine config: + * macOS: Docker > Preferences > Docker Engine: +``` + "insecure-registries": [ + "/32" + ] +``` + +### Testing GitHub actions on forks + +The lifecycle release process involves chaining a series of GitHub actions together such that: +* The "build" workflow creates the artifacts + * .tgz files containing the lifecycle binaries, shasums for the .tgz files, a cosign public key, an SBOM, etc. + * OCI images containing the lifecycle binaries, tagged with their commit sha (for more information, see RELEASE.md) +* The "draft-release" workflow finds the artifacts and downloads them, creating the draft release +* The "post-release" workflow re-tags the OCI images that were created during the "build" workflow with the release version + +It can be rather cumbersome to test changes to these workflows, as they are heavily intertwined. Thus we recommend forking the buildpacks/lifecycle repository in GitHub and running through the entire release process end-to-end. +For the fork, it is necessary to add the following secrets: +* COSIGN_PASSWORD (see [cosign](https://github.com/sigstore/cosign#generate-a-keypair)) +* COSIGN_PRIVATE_KEY +* DOCKER_PASSWORD (if not using ghcr.io) +* DOCKER_USERNAME (if not using ghcr.io) + +The tools/test-fork.sh script can be used to update the source code to reflect the state of the fork. +It can be invoked like so: `./tools/test-fork.sh ` + +## Tasks + +To test, build, and package binaries into an archive, simply run: + +```bash +$ make all +``` +This will create archives at `out/lifecycle-+linux.x86-64.tgz` and `out/lifecycle-+windows.x86-64.tgz`. + +`LIFECYCLE_VERSION` defaults to the value returned by `git describe --tags` if not on a release branch (for more information about the release process, see [RELEASE](RELEASE.md)). It can be changed by prepending `LIFECYCLE_VERSION=` to the +`make` command. For example: + +```bash +$ LIFECYCLE_VERSION=1.2.3 make all +``` + +Steps can also be run individually as shown below. + +### Test + +Formats, vets, and tests the code. + +```bash +$ make test +``` + +### Build + +Builds binaries to `out/linux/lifecycle/` and `out/windows/lifecycle/`. + +```bash +$ make build +``` + +> To clean the `out/` directory, run `make clean`. + +### Package + +Creates archives at `out/lifecycle-+linux.x86-64.tgz` and `out/lifecycle-+windows.x86-64.tgz`, using the contents of the +`out/linux/lifecycle/` directory, for the given (or default) `LIFECYCLE_VERSION`. + +```bash +$ make package +``` diff --git a/vendor/github.com/buildpacks/lifecycle/IMAGE.md b/vendor/github.com/buildpacks/lifecycle/IMAGE.md new file mode 100644 index 0000000000..d3ab324c29 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/IMAGE.md @@ -0,0 +1,39 @@ +# Quick reference + +This image is maintained by the [Cloud Native Buildpacks project](https://buildpacks.io/). The maintainers can be contacted via the [Cloud Native Buildpacks Slack](https://slack.buildpacks.io/), or by opening an issue on the `buildpacks/lifecycle` [GitHub repo](https://github.com/buildpacks/lifecycle). + +# Supported tags + +Supported tags are semver-versioned manifest lists - e.g., `0.12.0` or `0.12.0-rc.1`, pointing to one of the following os/architectures: +* `linux/amd64` +* `linux/arm64` +* `windows/amd64` + +# About this image + +Images are built in [GitHub actions](https://github.com/buildpacks/lifecycle/actions) and signed with [`cosign`](https://github.com/sigstore/cosign). To verify: +* Locate the public key `lifecycle-v-cosign.pub` on the [releases page](https://github.com/buildpacks/lifecycle/releases) +* Run: +``` +cosign verify -key lifecycle-v-cosign.pub buildpacksio/lifecycle: +``` + +A CycloneDX SBOM is "attached" to the image and signed with [`cosign`](https://github.com/sigstore/cosign). To verify: +* Locate the public key `lifecycle-v-cosign.pub` on the [releases page](https://github.com/buildpacks/lifecycle/releases) +* Run: +``` +cosign version # must be at least 1.2.0 +cosign verify -key cosign.pub -a tag= -attachment sbom buildpacksio/lifecycle: +cosign download sbom buildpacksio/lifecycle: +``` + +# Using this image + +With [pack](https://github.com/buildpack/pack): +* `pack build --lifecycle-image buildpacksio/lifecycle:` + +With [tekton](https://github.com/tektoncd/catalog/tree/main/task/buildpacks-phases/0.2): +* Provide as param `LIFECYCLE_IMAGE` in taskrun + +*** +[Source](https://github.com/buildpacks/lifecycle/blob/main/IMAGE.md) for this page \ No newline at end of file diff --git a/vendor/github.com/buildpacks/lifecycle/LICENSE b/vendor/github.com/buildpacks/lifecycle/LICENSE new file mode 100644 index 0000000000..a83b632218 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/LICENSE @@ -0,0 +1,202 @@ + + 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 2018 Stephen Levine + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/buildpacks/lifecycle/Makefile b/vendor/github.com/buildpacks/lifecycle/Makefile new file mode 100644 index 0000000000..5f8587511c --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/Makefile @@ -0,0 +1,315 @@ +ifeq ($(OS),Windows_NT) +SHELL:=cmd.exe +PWD?=$(subst /,\,${CURDIR}) +LDFLAGS=-s -w +BLANK:= +/:=\$(BLANK) +else +/:=/ +endif + +PARSED_COMMIT:=$(shell git rev-parse --short HEAD) + +ifeq ($(LIFECYCLE_VERSION),) +LIFECYCLE_VERSION:=$(shell go run tools/version/main.go) +LIFECYCLE_IMAGE_TAG?=$(PARSED_COMMIT) +else +LIFECYCLE_IMAGE_TAG?=$(LIFECYCLE_VERSION) +endif + +ACCEPTANCE_TIMEOUT?=2400s +GOCMD?=go +GOENV=GOARCH=$(GOARCH) CGO_ENABLED=0 +LIFECYCLE_DESCRIPTOR_PATH?=lifecycle.toml +SCM_REPO?=github.com/buildpacks/lifecycle +SCM_COMMIT?=$(PARSED_COMMIT) +LDFLAGS=-s -w +LDFLAGS+=-X 'github.com/buildpacks/lifecycle/cmd.SCMRepository=$(SCM_REPO)' +LDFLAGS+=-X 'github.com/buildpacks/lifecycle/cmd.SCMCommit=$(SCM_COMMIT)' +LDFLAGS+=-X 'github.com/buildpacks/lifecycle/cmd.Version=$(LIFECYCLE_VERSION)' +GOBUILD:=go build $(GOFLAGS) -ldflags "$(LDFLAGS)" +GOTEST=$(GOCMD) test $(GOFLAGS) +BUILD_DIR?=$(PWD)$/out +LINUX_COMPILATION_IMAGE?=golang:1.16-alpine +WINDOWS_COMPILATION_IMAGE?=golang:1.16-windowsservercore-1809 +SOURCE_COMPILATION_IMAGE?=lifecycle-img +BUILD_CTR?=lifecycle-ctr +DOCKER_CMD?=make test + +GOFILES := $(shell $(GOCMD) run tools$/lister$/main.go) + +all: test build package + +build: build-linux-amd64 build-linux-arm64 build-windows-amd64 + +build-linux-amd64: build-linux-amd64-lifecycle build-linux-amd64-symlinks build-linux-amd64-launcher +build-linux-arm64: build-linux-arm64-lifecycle build-linux-arm64-symlinks build-linux-arm64-launcher +build-windows-amd64: build-windows-amd64-lifecycle build-windows-amd64-symlinks build-windows-amd64-launcher + +build-image-linux-amd64: build-linux-amd64 package-linux-amd64 +build-image-linux-amd64: ARCHIVE_PATH=$(BUILD_DIR)/lifecycle-v$(LIFECYCLE_VERSION)+linux.x86-64.tgz +build-image-linux-amd64: + $(GOCMD) run ./tools/image/main.go -daemon -lifecyclePath $(ARCHIVE_PATH) -os linux -arch amd64 -tag lifecycle:$(LIFECYCLE_IMAGE_TAG) + +build-image-linux-arm64: build-linux-arm64 package-linux-arm64 +build-image-linux-arm64: ARCHIVE_PATH=$(BUILD_DIR)/lifecycle-v$(LIFECYCLE_VERSION)+linux.arm64.tgz +build-image-linux-arm64: + $(GOCMD) run ./tools/image/main.go -daemon -lifecyclePath $(ARCHIVE_PATH) -os linux -arch arm64 -tag lifecycle:$(LIFECYCLE_IMAGE_TAG) + +build-image-windows-amd64: build-windows-amd64 package-windows-amd64 +build-image-windows-amd64: ARCHIVE_PATH=$(BUILD_DIR)/lifecycle-v$(LIFECYCLE_VERSION)+windows.x86-64.tgz +build-image-windows-amd64: + $(GOCMD) run ./tools/image/main.go -daemon -lifecyclePath $(ARCHIVE_PATH) -os windows -arch amd64 -tag lifecycle:$(LIFECYCLE_IMAGE_TAG) + +build-linux-amd64-lifecycle: $(BUILD_DIR)/linux-amd64/lifecycle/lifecycle + +build-linux-arm64-lifecycle: $(BUILD_DIR)/linux-arm64/lifecycle/lifecycle + +$(BUILD_DIR)/linux-amd64/lifecycle/lifecycle: export GOOS:=linux +$(BUILD_DIR)/linux-amd64/lifecycle/lifecycle: export GOARCH:=amd64 +$(BUILD_DIR)/linux-amd64/lifecycle/lifecycle: OUT_DIR?=$(BUILD_DIR)/$(GOOS)-$(GOARCH)/lifecycle +$(BUILD_DIR)/linux-amd64/lifecycle/lifecycle: $(GOFILES) +$(BUILD_DIR)/linux-amd64/lifecycle/lifecycle: + @echo "> Building lifecycle/lifecycle for $(GOOS)/$(GOARCH)..." + mkdir -p $(OUT_DIR) + $(GOENV) $(GOBUILD) -o $(OUT_DIR)/lifecycle -a ./cmd/lifecycle + +$(BUILD_DIR)/linux-arm64/lifecycle/lifecycle: export GOOS:=linux +$(BUILD_DIR)/linux-arm64/lifecycle/lifecycle: export GOARCH:=arm64 +$(BUILD_DIR)/linux-arm64/lifecycle/lifecycle: OUT_DIR?=$(BUILD_DIR)/$(GOOS)-$(GOARCH)/lifecycle +$(BUILD_DIR)/linux-arm64/lifecycle/lifecycle: $(GOFILES) +$(BUILD_DIR)/linux-arm64/lifecycle/lifecycle: + @echo "> Building lifecycle/lifecycle for $(GOOS)/$(GOARCH)..." + mkdir -p $(OUT_DIR) + $(GOENV) $(GOBUILD) -o $(OUT_DIR)/lifecycle -a ./cmd/lifecycle + +build-linux-amd64-launcher: $(BUILD_DIR)/linux-amd64/lifecycle/launcher + +$(BUILD_DIR)/linux-amd64/lifecycle/launcher: export GOOS:=linux +$(BUILD_DIR)/linux-amd64/lifecycle/launcher: export GOARCH:=amd64 +$(BUILD_DIR)/linux-amd64/lifecycle/launcher: OUT_DIR?=$(BUILD_DIR)/$(GOOS)-$(GOARCH)/lifecycle +$(BUILD_DIR)/linux-amd64/lifecycle/launcher: $(GOFILES) +$(BUILD_DIR)/linux-amd64/lifecycle/launcher: + @echo "> Building lifecycle/launcher for $(GOOS)/$(GOARCH)..." + mkdir -p $(OUT_DIR) + $(GOENV) $(GOBUILD) -o $(OUT_DIR)/launcher -a ./cmd/launcher + test $$(du -m $(OUT_DIR)/launcher|cut -f 1) -le 3 + +build-linux-arm64-launcher: $(BUILD_DIR)/linux-arm64/lifecycle/launcher + +$(BUILD_DIR)/linux-arm64/lifecycle/launcher: export GOOS:=linux +$(BUILD_DIR)/linux-arm64/lifecycle/launcher: export GOARCH:=arm64 +$(BUILD_DIR)/linux-arm64/lifecycle/launcher: OUT_DIR?=$(BUILD_DIR)/$(GOOS)-$(GOARCH)/lifecycle +$(BUILD_DIR)/linux-arm64/lifecycle/launcher: $(GOFILES) +$(BUILD_DIR)/linux-arm64/lifecycle/launcher: + @echo "> Building lifecycle/launcher for $(GOOS)/$(GOARCH)..." + mkdir -p $(OUT_DIR) + $(GOENV) $(GOBUILD) -o $(OUT_DIR)/launcher -a ./cmd/launcher + test $$(du -m $(OUT_DIR)/launcher|cut -f 1) -le 3 + +build-linux-amd64-symlinks: export GOOS:=linux +build-linux-amd64-symlinks: export GOARCH:=amd64 +build-linux-amd64-symlinks: OUT_DIR?=$(BUILD_DIR)/$(GOOS)-$(GOARCH)/lifecycle +build-linux-amd64-symlinks: + @echo "> Creating phase symlinks for $(GOOS)/$(GOARCH)..." + ln -sf lifecycle $(OUT_DIR)/detector + ln -sf lifecycle $(OUT_DIR)/analyzer + ln -sf lifecycle $(OUT_DIR)/restorer + ln -sf lifecycle $(OUT_DIR)/builder + ln -sf lifecycle $(OUT_DIR)/exporter + ln -sf lifecycle $(OUT_DIR)/rebaser + ln -sf lifecycle $(OUT_DIR)/creator + +build-linux-arm64-symlinks: export GOOS:=linux +build-linux-arm64-symlinks: export GOARCH:=arm64 +build-linux-arm64-symlinks: OUT_DIR?=$(BUILD_DIR)/$(GOOS)-$(GOARCH)/lifecycle +build-linux-arm64-symlinks: + @echo "> Creating phase symlinks for $(GOOS)/$(GOARCH)..." + ln -sf lifecycle $(OUT_DIR)/detector + ln -sf lifecycle $(OUT_DIR)/analyzer + ln -sf lifecycle $(OUT_DIR)/restorer + ln -sf lifecycle $(OUT_DIR)/builder + ln -sf lifecycle $(OUT_DIR)/exporter + ln -sf lifecycle $(OUT_DIR)/rebaser + ln -sf lifecycle $(OUT_DIR)/creator + +build-windows-amd64-lifecycle: $(BUILD_DIR)/windows-amd64/lifecycle/lifecycle.exe + +$(BUILD_DIR)/windows-amd64/lifecycle/lifecycle.exe: export GOOS:=windows +$(BUILD_DIR)/windows-amd64/lifecycle/lifecycle.exe: export GOARCH:=amd64 +$(BUILD_DIR)/windows-amd64/lifecycle/lifecycle.exe: OUT_DIR?=$(BUILD_DIR)$/$(GOOS)-$(GOARCH)$/lifecycle +$(BUILD_DIR)/windows-amd64/lifecycle/lifecycle.exe: $(GOFILES) +$(BUILD_DIR)/windows-amd64/lifecycle/lifecycle.exe: + @echo "> Building lifecycle/lifecycle for $(GOOS)/$(GOARCH)..." + $(GOBUILD) -o $(OUT_DIR)$/lifecycle.exe -a .$/cmd$/lifecycle + +build-windows-amd64-launcher: $(BUILD_DIR)/windows-amd64/lifecycle/launcher.exe + +$(BUILD_DIR)/windows-amd64/lifecycle/launcher.exe: export GOOS:=windows +$(BUILD_DIR)/windows-amd64/lifecycle/launcher.exe: export GOARCH:=amd64 +$(BUILD_DIR)/windows-amd64/lifecycle/launcher.exe: OUT_DIR?=$(BUILD_DIR)$/$(GOOS)-$(GOARCH)$/lifecycle +$(BUILD_DIR)/windows-amd64/lifecycle/launcher.exe: $(GOFILES) +$(BUILD_DIR)/windows-amd64/lifecycle/launcher.exe: + @echo "> Building lifecycle/launcher for $(GOOS)/$(GOARCH)..." + $(GOBUILD) -o $(OUT_DIR)$/launcher.exe -a .$/cmd$/launcher + +build-windows-amd64-symlinks: export GOOS:=windows +build-windows-amd64-symlinks: export GOARCH:=amd64 +build-windows-amd64-symlinks: OUT_DIR?=$(BUILD_DIR)$/$(GOOS)-$(GOARCH)$/lifecycle +build-windows-amd64-symlinks: + @echo "> Creating phase symlinks for Windows..." +ifeq ($(OS),Windows_NT) + call del $(OUT_DIR)$/detector.exe + call del $(OUT_DIR)$/analyzer.exe + call del $(OUT_DIR)$/restorer.exe + call del $(OUT_DIR)$/builder.exe + call del $(OUT_DIR)$/exporter.exe + call del $(OUT_DIR)$/rebaser.exe + call del $(OUT_DIR)$/creator.exe + call mklink $(OUT_DIR)$/detector.exe lifecycle.exe + call mklink $(OUT_DIR)$/analyzer.exe lifecycle.exe + call mklink $(OUT_DIR)$/restorer.exe lifecycle.exe + call mklink $(OUT_DIR)$/builder.exe lifecycle.exe + call mklink $(OUT_DIR)$/exporter.exe lifecycle.exe + call mklink $(OUT_DIR)$/rebaser.exe lifecycle.exe + call mklink $(OUT_DIR)$/creator.exe lifecycle.exe +else + ln -sf lifecycle.exe $(OUT_DIR)$/detector.exe + ln -sf lifecycle.exe $(OUT_DIR)$/analyzer.exe + ln -sf lifecycle.exe $(OUT_DIR)$/restorer.exe + ln -sf lifecycle.exe $(OUT_DIR)$/builder.exe + ln -sf lifecycle.exe $(OUT_DIR)$/exporter.exe + ln -sf lifecycle.exe $(OUT_DIR)$/rebaser.exe + ln -sf lifecycle.exe $(OUT_DIR)$/creator.exe +endif + +build-darwin-amd64: build-darwin-amd64-lifecycle build-darwin-amd64-launcher + +build-darwin-amd64-lifecycle: $(BUILD_DIR)/darwin-amd64/lifecycle/lifecycle +$(BUILD_DIR)/darwin-amd64/lifecycle/lifecycle: export GOOS:=darwin +$(BUILD_DIR)/darwin-amd64/lifecycle/lifecycle: export GOARCH:=amd64 +$(BUILD_DIR)/darwin-amd64/lifecycle/lifecycle: OUT_DIR:=$(BUILD_DIR)/$(GOOS)-$(GOARCH)/lifecycle +$(BUILD_DIR)/darwin-amd64/lifecycle/lifecycle: $(GOFILES) +$(BUILD_DIR)/darwin-amd64/lifecycle/lifecycle: + @echo "> Building lifecycle for darwin/amd64..." + $(GOENV) $(GOBUILD) -o $(OUT_DIR)/lifecycle -a ./cmd/lifecycle + @echo "> Creating lifecycle symlinks for darwin/amd64..." + ln -sf lifecycle $(OUT_DIR)/detector + ln -sf lifecycle $(OUT_DIR)/analyzer + ln -sf lifecycle $(OUT_DIR)/restorer + ln -sf lifecycle $(OUT_DIR)/builder + ln -sf lifecycle $(OUT_DIR)/exporter + ln -sf lifecycle $(OUT_DIR)/rebaser + +build-darwin-amd64-launcher: $(BUILD_DIR)/darwin-amd64/lifecycle/launcher +$(BUILD_DIR)/darwin-amd64/lifecycle/launcher: export GOOS:=darwin +$(BUILD_DIR)/darwin-amd64/lifecycle/launcher: export GOARCH:=amd64 +$(BUILD_DIR)/darwin-amd64/lifecycle/launcher: OUT_DIR:=$(BUILD_DIR)/$(GOOS)-$(GOARCH)/lifecycle +$(BUILD_DIR)/darwin-amd64/lifecycle/launcher: $(GOFILES) +$(BUILD_DIR)/darwin-amd64/lifecycle/launcher: + @echo "> Building launcher for darwin/amd64..." + mkdir -p $(OUT_DIR) + $(GOENV) $(GOBUILD) -o $(OUT_DIR)/launcher -a ./cmd/launcher + test $$(du -m $(OUT_DIR)/launcher|cut -f 1) -le 4 + +install-goimports: + @echo "> Installing goimports..." + $(GOCMD) install golang.org/x/tools/cmd/goimports@v0.1.2 + +install-yj: + @echo "> Installing yj..." + $(GOCMD) install github.com/sclevine/yj@v0.0.0-20210612025309-737bdf40a5d1 + +install-mockgen: + @echo "> Installing mockgen..." + $(GOCMD) install github.com/golang/mock/mockgen@v1.5.0 + +install-golangci-lint: + @echo "> Installing golangci-lint..." + $(GOCMD) install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.42.1 + +lint: install-golangci-lint + @echo "> Linting code..." + @golangci-lint run -c golangci.yaml + +generate: install-mockgen + @echo "> Generating..." + $(GOCMD) generate + $(GOCMD) generate ./launch + +format: install-goimports + @echo "> Formating code..." + $(if $(shell goimports -l -w -local github.com/buildpacks/lifecycle .), @echo Fixed formatting errors. Re-run && exit 1) + +tidy: + @echo "> Tidying modules..." + $(GOCMD) mod tidy + cd tools && $(GOCMD) mod tidy + +test: unit acceptance + +# append coverage arguments +ifeq ($(TEST_COVERAGE), 1) +unit: GOTESTFLAGS:=$(GOTESTFLAGS) -coverprofile=./out/tests/coverage-unit.txt -covermode=atomic +endif +unit: out +unit: UNIT_PACKAGES=$(shell $(GOCMD) list ./... | grep -v acceptance) +unit: format lint tidy install-yj + @echo "> Running unit tests..." + $(GOTEST) $(GOTESTFLAGS) -v -count=1 $(UNIT_PACKAGES) + +out: + @mkdir out || (exit 0) + mkdir out$/tests || (exit 0) + +acceptance: format tidy + @echo "> Running acceptance tests..." + $(GOTEST) -v -count=1 -tags=acceptance -timeout=$(ACCEPTANCE_TIMEOUT) ./acceptance/... + +clean: + @echo "> Cleaning workspace..." + rm -rf $(BUILD_DIR) + +package: package-linux-amd64 package-linux-arm64 package-windows-amd64 + +package-linux-amd64: GOOS:=linux +package-linux-amd64: GOARCH:=amd64 +package-linux-amd64: INPUT_DIR:=$(BUILD_DIR)/$(GOOS)-$(GOARCH)/lifecycle +package-linux-amd64: ARCHIVE_PATH=$(BUILD_DIR)/lifecycle-v$(LIFECYCLE_VERSION)+$(GOOS).x86-64.tgz +package-linux-amd64: PACKAGER=./tools/packager/main.go +package-linux-amd64: + @echo "> Packaging lifecycle for $(GOOS)/$(GOARCH)..." + $(GOCMD) run $(PACKAGER) --inputDir $(INPUT_DIR) -archivePath $(ARCHIVE_PATH) -descriptorPath $(LIFECYCLE_DESCRIPTOR_PATH) -version $(LIFECYCLE_VERSION) + +package-linux-arm64: GOOS:=linux +package-linux-arm64: GOARCH:=arm64 +package-linux-arm64: INPUT_DIR:=$(BUILD_DIR)/$(GOOS)-$(GOARCH)/lifecycle +package-linux-arm64: ARCHIVE_PATH=$(BUILD_DIR)/lifecycle-v$(LIFECYCLE_VERSION)+$(GOOS).arm64.tgz +package-linux-arm64: PACKAGER=./tools/packager/main.go +package-linux-arm64: + @echo "> Packaging lifecycle for $(GOOS)/$(GOARCH)..." + $(GOCMD) run $(PACKAGER) --inputDir $(INPUT_DIR) -archivePath $(ARCHIVE_PATH) -descriptorPath $(LIFECYCLE_DESCRIPTOR_PATH) -version $(LIFECYCLE_VERSION) + +package-windows-amd64: GOOS:=windows +package-windows-amd64: GOARCH:=amd64 +package-windows-amd64: INPUT_DIR:=$(BUILD_DIR)$/$(GOOS)-$(GOARCH)$/lifecycle +package-windows-amd64: ARCHIVE_PATH=$(BUILD_DIR)$/lifecycle-v$(LIFECYCLE_VERSION)+$(GOOS).x86-64.tgz +package-windows-amd64: PACKAGER=.$/tools$/packager$/main.go +package-windows-amd64: + @echo "> Packaging lifecycle for $(GOOS)/$(GOARCH)..." + $(GOCMD) run $(PACKAGER) --inputDir $(INPUT_DIR) -archivePath $(ARCHIVE_PATH) -descriptorPath $(LIFECYCLE_DESCRIPTOR_PATH) -version $(LIFECYCLE_VERSION) + +# Ensure workdir is clean and build image from .git +docker-build-source-image-windows: $(GOFILES) +docker-build-source-image-windows: + $(if $(shell git status --short), @echo Uncommitted changes. Refusing to run. && exit 1) + docker build .git -f tools/Dockerfile.windows --tag $(SOURCE_COMPILATION_IMAGE) --build-arg image_tag=$(WINDOWS_COMPILATION_IMAGE) --cache-from=$(SOURCE_COMPILATION_IMAGE) --isolation=process --compress + +docker-run-windows: docker-build-source-image-windows +docker-run-windows: + @echo "> Running '$(DOCKER_CMD)' in docker windows..." + @docker volume rm -f lifecycle-out + docker run -v lifecycle-out:c:/lifecycle/out -e LIFECYCLE_VERSION -e PLATFORM_API -e BUILDPACK_API -v gopathcache:c:/gopath -v '\\.\pipe\docker_engine:\\.\pipe\docker_engine' --isolation=process --interactive --tty --rm $(SOURCE_COMPILATION_IMAGE) $(DOCKER_CMD) + docker run -v lifecycle-out:c:/lifecycle/out --rm $(SOURCE_COMPILATION_IMAGE) tar -cf- out | tar -xf- + @docker volume rm -f lifecycle-out + diff --git a/vendor/github.com/buildpacks/lifecycle/README.md b/vendor/github.com/buildpacks/lifecycle/README.md new file mode 100644 index 0000000000..2321ed5a6a --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/README.md @@ -0,0 +1,64 @@ +# Lifecycle + +[![Build Status](https://github.com/buildpacks/lifecycle/workflows/build/badge.svg)](https://github.com/buildpacks/lifecycle/actions) +[![GoDoc](https://godoc.org/github.com/buildpacks/lifecycle?status.svg)](https://godoc.org/github.com/buildpacks/lifecycle) +[![codecov](https://codecov.io/gh/buildpacks/pack/branch/main/graph/badge.svg)](https://codecov.io/gh/buildpacks/pack) +[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/4748/badge)](https://bestpractices.coreinfrastructure.org/projects/4748) + [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/buildpacks/lifecycle) + +A reference implementation of the [Cloud Native Buildpacks specification](https://github.com/buildpacks/spec). + +## Supported APIs +Lifecycle Version | Platform APIs | Buildpack APIs | +------------------|------------------------------------------------------------------------------------|----------------| +0.13.x | [0.3][p/0.3], [0.4][p/0.4], [0.5][p/0.5], [0.6][p/0.6], [0.7][p/0.7], [0.8][p/0.8] | [0.2][b/0.2], [0.3][b/0.3], [0.4][b/0.4], [0.5][b/0.5], [0.6][b/0.6], [0.7][b/0.7] +0.12.x | [0.3][p/0.3], [0.4][p/0.4], [0.5][p/0.5], [0.6][p/0.6], [0.7][p/0.7] | [0.2][b/0.2], [0.3][b/0.3], [0.4][b/0.4], [0.5][b/0.5], [0.6][b/0.6] +0.11.x | [0.3][p/0.3], [0.4][p/0.4], [0.5][p/0.5], [0.6][p/0.6] | [0.2][b/0.2], [0.3][b/0.3], [0.4][b/0.4], [0.5][b/0.5], [0.6][b/0.6] +0.10.x | [0.3][p/0.3], [0.4][p/0.4], [0.5][p/0.5] | [0.2][b/0.2], [0.3][b/0.3], [0.4][b/0.4], [0.5][b/0.5] +0.9.x | [0.3][p/0.3], [0.4][p/0.4] | [0.2][b/0.2], [0.3][b/0.3], [0.4][b/0.4] +0.8.x | [0.3][p/0.3] | [0.2][b/0.2] +0.7.x | [0.2][p/0.2] | [0.2][b/0.2] +0.6.x | [0.2][p/0.2] | [0.2][b/0.2] + +[b/0.2]: https://github.com/buildpacks/spec/blob/buildpack/v0.2/buildpack.md +[b/0.3]: https://github.com/buildpacks/spec/tree/buildpack/v0.3/buildpack.md +[b/0.4]: https://github.com/buildpacks/spec/tree/buildpack/v0.4/buildpack.md +[b/0.5]: https://github.com/buildpacks/spec/tree/buildpack/v0.5/buildpack.md +[b/0.6]: https://github.com/buildpacks/spec/tree/buildpack/v0.6/buildpack.md +[b/0.7]: https://github.com/buildpacks/spec/tree/buildpack/v0.7/buildpack.md +[p/0.2]: https://github.com/buildpacks/spec/blob/platform/v0.2/platform.md +[p/0.3]: https://github.com/buildpacks/spec/blob/platform/v0.3/platform.md +[p/0.4]: https://github.com/buildpacks/spec/blob/platform/v0.4/platform.md +[p/0.5]: https://github.com/buildpacks/spec/blob/platform/v0.5/platform.md +[p/0.6]: https://github.com/buildpacks/spec/blob/platform/v0.6/platform.md +[p/0.7]: https://github.com/buildpacks/spec/blob/platform/v0.7/platform.md +[p/0.8]: https://github.com/buildpacks/spec/blob/platform/v0.8/platform.md + +\* denotes unreleased version + +## Usage + +### Build + +Either: +* `detector` - Chooses buildpacks (via `/bin/detect`) and produces a build plan. +* `analyzer` - Restores layer metadata from the previous image and from the cache. +* `restorer` - Restores cached layers. +* `builder` - Executes buildpacks (via `/bin/build`). +* `exporter` - Creates an image and caches layers. + +Or: +* `creator` - Runs the five phases listed above in order. + +### Run + +* `launcher` - Invokes a chosen process. + +### Rebase + +* `rebaser` - Creates an image from a previous image with updated base layers. + +## Contributing +- [CONTRIBUTING](CONTRIBUTING.md) - Information on how to contribute and grow your understanding of the lifecycle. +- [DEVELOPMENT](DEVELOPMENT.md) - Further detail to help you during the development process. +- [RELEASE](RELEASE.md) - Further details about our release process. diff --git a/vendor/github.com/buildpacks/lifecycle/RELEASE.md b/vendor/github.com/buildpacks/lifecycle/RELEASE.md new file mode 100644 index 0000000000..46c07122e2 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/RELEASE.md @@ -0,0 +1,22 @@ +## Release Finalization + +To cut a pre-release: +1. If applicable, ensure the README is updated with the latest supported apis (example PR: https://github.com/buildpacks/lifecycle/pull/550). +1. Create a release branch in the format `release/0.99.0-rc.1`. New commits to this branch will trigger the `build` workflow and produce a lifecycle image: `buildpacksio/lifecycle:`. +1. When ready to cut the release, manually trigger the `draft-release` workflow: Actions -> draft-release -> Run workflow -> Use workflow from branch: `release/0.99.0-rc.1`. This will create a draft release on GitHub using the artifacts from the `build` workflow run for the latest commit on the release branch. +1. Edit the release notes as necessary. +1. Perform any manual validation of the artifacts (see below). +1. When ready to publish the release, edit the release page and click "Publish release". This will trigger the `post-release` workflow that will re-tag the lifecycle image from `buildpacksio/lifecycle:` to `buildpacksio/lifecycle:0.99.0` but will NOT update the `latest` tag. + +To cut a release: +1. Ensure the relevant spec APIs have been released. +1. Ensure the `lifecycle/0.99.0` milestone on the [docs repo](https://github.com/buildpacks/docs/blob/main/RELEASE.md#lump-changes) is complete, such that every new feature in the lifecycle is fully explained in the `release/lifecycle/0.99` branch on the docs repo, and [migration guides](https://github.com/buildpacks/docs/tree/main/content/docs/reference/spec/migration) (if relevant) are included. +1. Create a release branch in the format `release/0.99.0`. New commits to this branch will trigger the `build` workflow and produce a lifecycle image: `buildpacksio/lifecycle:`. +1. If applicable, ensure the README is updated with the latest supported apis (example PR: https://github.com/buildpacks/lifecycle/pull/550) and remove the pre-release note for the latest apis. +1. When ready to cut the release, manually trigger the `draft-release` workflow: Actions -> draft-release -> Run workflow -> Use workflow from branch: `release/0.99.0`. This will create a draft release on GitHub using the artifacts from the `build` workflow run for the latest commit on the release branch. +1. Edit the release notes as necessary. +1. Perform any manual validation of the artifacts. +1. When ready to publish the release, edit the release page and click "Publish release". This will trigger the `post-release` workflow that will re-tag the lifecycle image from `buildpacksio/lifecycle:` to `buildpacksio/lifecycle:0.99.0` and `buildpacksio/lifecycle:latest`. +1. Once released +- Update the `main` branch to remove the pre-release note in [README.md](https://github.com/buildpacks/lifecycle/blob/main/README.md) and/or merge `release/0.99.0` into `main`. +- Ask the learning team to merge the `release/lifecycle/0.99` branch into `main` on the docs repo. diff --git a/vendor/github.com/buildpacks/lifecycle/analyzer.go b/vendor/github.com/buildpacks/lifecycle/analyzer.go new file mode 100644 index 0000000000..87d8ee3c5f --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/analyzer.go @@ -0,0 +1,126 @@ +package lifecycle + +import ( + "github.com/buildpacks/imgutil" + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/api" + "github.com/buildpacks/lifecycle/buildpack" + "github.com/buildpacks/lifecycle/image" + "github.com/buildpacks/lifecycle/internal/layer" + "github.com/buildpacks/lifecycle/platform" +) + +type Platform interface { + API() *api.Version +} + +type Analyzer struct { + PreviousImage imgutil.Image + RunImage imgutil.Image + Logger Logger + Platform Platform + SBOMRestorer layer.SBOMRestorer + + // Platform API < 0.7 + Buildpacks []buildpack.GroupBuildpack + Cache Cache + LayerMetadataRestorer layer.MetadataRestorer +} + +// Analyze fetches the layers metadata from the previous image and writes analyzed.toml. +func (a *Analyzer) Analyze() (platform.AnalyzedMetadata, error) { + var ( + appMeta platform.LayersMetadata + cacheMeta platform.CacheMetadata + previousImageID *platform.ImageIdentifier + runImageID *platform.ImageIdentifier + err error + ) + + if a.PreviousImage != nil { // Previous image is optional in Platform API >= 0.7 + previousImageID, err = a.getImageIdentifier(a.PreviousImage) + if err != nil { + return platform.AnalyzedMetadata{}, errors.Wrap(err, "retrieving image identifier") + } + + // continue even if the label cannot be decoded + if err := image.DecodeLabel(a.PreviousImage, platform.LayerMetadataLabel, &appMeta); err != nil { + appMeta = platform.LayersMetadata{} + } + + if a.Platform.API().AtLeast("0.8") { + if appMeta.BOM != nil && appMeta.BOM.SHA != "" { + a.Logger.Infof("Restoring data for sbom from previous image") + if err := a.SBOMRestorer.RestoreFromPrevious(a.PreviousImage, appMeta.BOM.SHA); err != nil { + return platform.AnalyzedMetadata{}, errors.Wrap(err, "retrieving launch sBOM layer") + } + } + } + } else { + appMeta = platform.LayersMetadata{} + } + + if a.RunImage != nil { + runImageID, err = a.getImageIdentifier(a.RunImage) + if err != nil { + return platform.AnalyzedMetadata{}, errors.Wrap(err, "retrieving image identifier") + } + } + + if a.restoresLayerMetadata() { + cacheMeta, err = retrieveCacheMetadata(a.Cache, a.Logger) + if err != nil { + return platform.AnalyzedMetadata{}, err + } + + useShaFiles := true + if err := a.LayerMetadataRestorer.Restore(a.Buildpacks, appMeta, cacheMeta, layer.NewSHAStore(useShaFiles)); err != nil { + return platform.AnalyzedMetadata{}, err + } + } + + return platform.AnalyzedMetadata{ + PreviousImage: previousImageID, + RunImage: runImageID, + Metadata: appMeta, + }, nil +} + +func (a *Analyzer) restoresLayerMetadata() bool { + return a.Platform.API().LessThan("0.7") +} + +func (a *Analyzer) getImageIdentifier(image imgutil.Image) (*platform.ImageIdentifier, error) { + if !image.Found() { + a.Logger.Infof("Previous image with name %q not found", image.Name()) + return nil, nil + } + identifier, err := image.Identifier() + if err != nil { + return nil, err + } + a.Logger.Debugf("Analyzing image %q", identifier.String()) + return &platform.ImageIdentifier{ + Reference: identifier.String(), + }, nil +} + +func retrieveCacheMetadata(cache Cache, logger Logger) (platform.CacheMetadata, error) { + // Create empty cache metadata in case a usable cache is not provided. + var cacheMeta platform.CacheMetadata + if cache != nil { + var err error + if !cache.Exists() { + logger.Info("Layer cache not found") + } + cacheMeta, err = cache.RetrieveMetadata() + if err != nil { + return cacheMeta, errors.Wrap(err, "retrieving cache metadata") + } + } else { + logger.Debug("Usable cache not provided, using empty cache metadata") + } + + return cacheMeta, nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/api/apis.go b/vendor/github.com/buildpacks/lifecycle/api/apis.go new file mode 100644 index 0000000000..0b903722a2 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/api/apis.go @@ -0,0 +1,100 @@ +package api + +import ( + "fmt" + "strings" + + "github.com/pkg/errors" +) + +var ( + Platform = newApisMustParse([]string{"0.3", "0.4", "0.5", "0.6", "0.7", "0.8"}, nil) + Buildpack = newApisMustParse([]string{"0.2", "0.3", "0.4", "0.5", "0.6", "0.7"}, nil) +) + +type APIs struct { + Supported List + Deprecated List +} + +type List []*Version + +func (l List) String() string { + var els []string + for _, el := range l { + els = append(els, fmt.Sprintf("%q", el.String())) + } + return "[" + strings.Join(els, ", ") + "]" +} + +// newApisMustParse calls NewApis and panics on error +func newApisMustParse(supported []string, deprecated []string) APIs { + apis, err := NewAPIs(supported, deprecated) + if err != nil { + panic(err) + } + return apis +} + +// NewApis constructs an instance of APIs +// supported must be a superset of deprecated +// deprecated APIs greater than 1.0 should should not include minor versions +// supported APIs should always include minor versions +// Examples: +// deprecated API 1 implies all 1.x APIs are deprecated +// supported API 1 implies only 1.0 is supported +func NewAPIs(supported []string, deprecated []string) (APIs, error) { + apis := APIs{} + for _, api := range supported { + apis.Supported = append(apis.Supported, MustParse(api)) + } + for _, d := range deprecated { + dAPI := MustParse(d) + if err := validateDeprecated(apis, dAPI); err != nil { + return APIs{}, errors.Wrapf(err, "invalid deprecated API '%s'", d) + } + apis.Deprecated = append(apis.Deprecated, dAPI) + } + return apis, nil +} + +func validateDeprecated(apis APIs, deprecated *Version) error { + if !apis.IsSupported(deprecated) { + return errors.New("all deprecated APIs must also be supported") + } + if deprecated.Major != 0 && deprecated.Minor != 0 { + return errors.New("deprecated APIs may only contain 0.x or a major version") + } + return nil +} + +// IsSupported returns true or false depending on whether the target API is supported +func (a APIs) IsSupported(target *Version) bool { + for _, sAPI := range a.Supported { + if sAPI.IsSupersetOf(target) { + return true + } + } + return false +} + +// IsDeprecated returns true or false depending on whether the target API is deprecated +func (a APIs) IsDeprecated(target *Version) bool { + for _, dAPI := range a.Deprecated { + if target.IsSupersetOf(dAPI) { + return true + } + } + return false +} + +// Latest returns the latest API that is supported +func (a APIs) Latest() *Version { + latest := a.Supported[0] + for _, sAPI := range a.Supported { + if sAPI.Compare(latest) > 0 { + latest = sAPI + } + } + return latest +} diff --git a/vendor/github.com/buildpacks/lifecycle/api/version.go b/vendor/github.com/buildpacks/lifecycle/api/version.go new file mode 100644 index 0000000000..b7727a5ad2 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/api/version.go @@ -0,0 +1,129 @@ +package api + +import ( + "fmt" + "regexp" + "strconv" + + "github.com/pkg/errors" +) + +var regex = regexp.MustCompile(`^v?(\d+)\.?(\d*)$`) + +type Version struct { + Major, + Minor uint64 +} + +func MustParse(v string) *Version { + version, err := NewVersion(v) + if err != nil { + panic(err) + } + + return version +} + +func NewVersion(v string) (*Version, error) { + matches := regex.FindAllStringSubmatch(v, -1) + if len(matches) == 0 { + return nil, errors.Errorf("could not parse '%s' as version", v) + } + + var ( + major, minor uint64 + err error + ) + if len(matches[0]) == 3 { + major, err = strconv.ParseUint(matches[0][1], 10, 64) + if err != nil { + return nil, errors.Wrapf(err, "parsing Major '%s'", matches[0][1]) + } + + if matches[0][2] == "" { + minor = 0 + } else { + minor, err = strconv.ParseUint(matches[0][2], 10, 64) + if err != nil { + return nil, errors.Wrapf(err, "parsing Minor '%s'", matches[0][2]) + } + } + } else { + return nil, errors.Errorf("could not parse version '%s'", v) + } + + return &Version{Major: major, Minor: minor}, nil +} + +func (v *Version) String() string { + return fmt.Sprintf("%d.%d", v.Major, v.Minor) +} + +// MarshalText makes Version satisfy the encoding.TextMarshaler interface. +func (v *Version) MarshalText() ([]byte, error) { + return []byte(v.String()), nil +} + +// UnmarshalText makes Version satisfy the encoding.TextUnmarshaler interface. +func (v *Version) UnmarshalText(text []byte) error { + s := string(text) + + parsedVersion, err := NewVersion(s) + if err != nil { + return errors.Wrapf(err, "invalid api version '%s'", s) + } + + v.Major = parsedVersion.Major + v.Minor = parsedVersion.Minor + + return nil +} + +func (v *Version) Equal(o *Version) bool { + return v.Compare(o) == 0 +} + +// Compare returns one of the following results +// -1 is less than *Version o +// 0 is equal to *Version o +// 1 is greater than *Version o +func (v *Version) Compare(o *Version) int { + if v.Major != o.Major { + if v.Major < o.Major { + return -1 + } + + if v.Major > o.Major { + return 1 + } + } + + if v.Minor != o.Minor { + if v.Minor < o.Minor { + return -1 + } + + if v.Minor > o.Minor { + return 1 + } + } + + return 0 +} + +func (v *Version) IsSupersetOf(o *Version) bool { + if v.Major == 0 { + return v.Equal(o) + } + return v.Major == o.Major && v.Minor >= o.Minor +} + +func (v *Version) LessThan(other string) bool { + otherVersion := MustParse(other) + return v.Compare(otherVersion) < 0 +} + +func (v *Version) AtLeast(other string) bool { + otherVersion := MustParse(other) + return v.Compare(otherVersion) >= 0 +} diff --git a/vendor/github.com/buildpacks/lifecycle/archive/archive.go b/vendor/github.com/buildpacks/lifecycle/archive/archive.go new file mode 100644 index 0000000000..136190edc8 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/archive/archive.go @@ -0,0 +1,72 @@ +package archive + +import ( + "archive/tar" + "io" + "os" + "path/filepath" +) + +// PathInfo associates a path with an os.FileInfo +type PathInfo struct { + Path string + Info os.FileInfo +} + +// AddFilesToArchive writes entries describing all files to the provided TarWriter +func AddFilesToArchive(tw TarWriter, files []PathInfo) error { + for _, file := range files { + if err := AddFileToArchive(tw, file.Path, file.Info); err != nil { + return err + } + } + return nil +} + +// AddFileToArchive writes an entry describing the file at path with the given os.FileInfo to the provided TarWriter +func AddFileToArchive(tw TarWriter, path string, fi os.FileInfo) error { + if fi.Mode()&os.ModeSocket != 0 { + return nil + } + header, err := tar.FileInfoHeader(fi, "") + if err != nil { + return err + } + header.Name = path + + if fi.Mode()&os.ModeSymlink != 0 { + var err error + target, err := os.Readlink(path) + if err != nil { + return err + } + header.Linkname = target + } + addSysAttributes(header, fi) + if err := tw.WriteHeader(header); err != nil { + return err + } + if fi.Mode().IsRegular() { + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + if _, err := io.Copy(tw, f); err != nil { + return err + } + } + return nil +} + +// AddDirToArchive walks dir writes entries describing dir and all of its children files to the provided TarWriter +func AddDirToArchive(tw TarWriter, dir string) error { + dir = filepath.Clean(dir) + + return filepath.Walk(dir, func(file string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + return AddFileToArchive(tw, file, fi) + }) +} diff --git a/vendor/github.com/buildpacks/lifecycle/archive/extract.go b/vendor/github.com/buildpacks/lifecycle/archive/extract.go new file mode 100644 index 0000000000..56de8a4942 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/archive/extract.go @@ -0,0 +1,117 @@ +package archive + +import ( + "archive/tar" + "fmt" + "io" + "os" + "path/filepath" + "sync" + + "github.com/pkg/errors" +) + +type PathMode struct { + Path string + Mode os.FileMode +} + +var ( + umaskLock sync.Mutex + extractCounter int + originalUmask int +) + +func setUmaskIfNeeded() { + umaskLock.Lock() + defer umaskLock.Unlock() + extractCounter++ + if extractCounter == 1 { + originalUmask = setUmask(0) + } +} + +func unsetUmaskIfNeeded() { + umaskLock.Lock() + defer umaskLock.Unlock() + extractCounter-- + if extractCounter == 0 { + _ = setUmask(originalUmask) + } +} + +// Extract reads all entries from TarReader and extracts them to the filesystem. +func Extract(tr TarReader) error { + setUmaskIfNeeded() + defer unsetUmaskIfNeeded() + + buf := make([]byte, 32*32*1024) + dirsFound := make(map[string]bool) + + var pathModes []PathMode + for { + hdr, err := tr.Next() + if err == io.EOF { + for _, pathMode := range pathModes { // directories that are newly created and for which there is a header in the tar should have the right permissions + if err := os.Chmod(pathMode.Path, pathMode.Mode); err != nil { + return err + } + } + return nil + } + if err != nil { + return errors.Wrap(err, "error extracting from archive") + } + + switch hdr.Typeflag { + case tar.TypeDir: + if _, err := os.Stat(hdr.Name); os.IsNotExist(err) { + pathMode := PathMode{hdr.Name, hdr.FileInfo().Mode()} + pathModes = append(pathModes, pathMode) + } + if err := os.MkdirAll(hdr.Name, os.ModePerm); err != nil { + return errors.Wrapf(err, "failed to create directory %q", hdr.Name) + } + dirsFound[hdr.Name] = true + + case tar.TypeReg, tar.TypeRegA: + dirPath := filepath.Dir(hdr.Name) + if !dirsFound[dirPath] { + if _, err := os.Stat(dirPath); os.IsNotExist(err) { + if err := os.MkdirAll(dirPath, applyUmask(os.ModePerm, originalUmask)); err != nil { // if there is no header for the parent directory in the tar, apply the provided umask + return errors.Wrapf(err, "failed to create parent dir %q for file %q", dirPath, hdr.Name) + } + dirsFound[dirPath] = true + } + } + + if err := writeFile(tr, hdr.Name, hdr.FileInfo().Mode(), buf); err != nil { + return errors.Wrapf(err, "failed to write file %q", hdr.Name) + } + case tar.TypeSymlink: + if err := createSymlink(hdr); err != nil { + return errors.Wrapf(err, "failed to create symlink %q with target %q", hdr.Name, hdr.Linkname) + } + default: + return fmt.Errorf("unknown file type in tar %d", hdr.Typeflag) + } + } +} + +func applyUmask(mode os.FileMode, umask int) os.FileMode { + return os.FileMode(int(mode) &^ umask) +} + +func writeFile(in io.Reader, path string, mode os.FileMode, buf []byte) (err error) { + fh, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode) + if err != nil { + return err + } + defer func() { + if closeErr := fh.Close(); err == nil { + err = closeErr + } + }() + _, err = io.CopyBuffer(fh, in, buf) + return err +} diff --git a/vendor/github.com/buildpacks/lifecycle/archive/reader.go b/vendor/github.com/buildpacks/lifecycle/archive/reader.go new file mode 100644 index 0000000000..1af7f94eb1 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/archive/reader.go @@ -0,0 +1,77 @@ +package archive + +import ( + "archive/tar" + "path/filepath" + "strings" +) + +type TarReader interface { + Next() (*tar.Header, error) + Read(b []byte) (int, error) +} + +// NormalizingTarReader read from the wrapped TarReader normalizes header before passing them through to the caller +// NormalizingTarReader always normalizes header.Name so that path separators match the runtime OS +// Other modifications can be enabled by invoking options on the NormalizingTarReader +type NormalizingTarReader struct { + TarReader + headerOpts []HeaderOpt + excludedPaths []string +} + +// Strip removes leading directories for any subsequently read *tar.Header +func (tr *NormalizingTarReader) Strip(prefix string) { + tr.headerOpts = append(tr.headerOpts, func(header *tar.Header) *tar.Header { + header.Name = strings.TrimPrefix(header.Name, prefix) + return header + }) +} + +// ExcludedPaths configures an array of paths to be skipped on subsequent calls to Next +// Children of the configured paths will also be skipped +// paths should match the unmodified Name of the *tar.Header returned by the wrapped TarReader, not the normalized headers +func (tr *NormalizingTarReader) ExcludePaths(paths []string) { + tr.excludedPaths = paths +} + +// PrependDir will set the Name of any subsequently read *tar.Header the result of filepath.Join of dir and the +// original Name +func (tr *NormalizingTarReader) PrependDir(dir string) { + tr.headerOpts = append(tr.headerOpts, func(hdr *tar.Header) *tar.Header { + // Suppress gosec check for zip slip vulnerability, as we set dir in our code. + // #nosec G305 + hdr.Name = filepath.Join(dir, hdr.Name) + return hdr + }) +} + +// NewNormalizingTarReader creates a NormalizingTarReaders that wraps the provided TarReader +func NewNormalizingTarReader(tr TarReader) *NormalizingTarReader { + return &NormalizingTarReader{TarReader: tr} +} + +// Next calls Next on the wrapped TarReader and applies modifications before returning the *tar.Header +// If the wrapped TarReader returns a *tar.Header matching an excluded path Next will proceed to the next entry, +// returning the first non-excluded entry +// Modification options will be apply in the order the options were invoked. +// Standard modifications (path separators normalization) are applied last. +func (tr *NormalizingTarReader) Next() (*tar.Header, error) { + hdr, err := tr.TarReader.Next() + if err != nil { + return nil, err + } + for _, excluded := range tr.excludedPaths { + if strings.HasPrefix(hdr.Name, excluded) { + return tr.Next() // If path is excluded move on to the next entry + } + } + for _, opt := range tr.headerOpts { + hdr = opt(hdr) + } + if hdr.Name == "" { + return tr.Next() // If entire path is stripped move on to the next entry + } + hdr.Name = filepath.FromSlash(hdr.Name) + return hdr, nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/archive/tar_unix.go b/vendor/github.com/buildpacks/lifecycle/archive/tar_unix.go new file mode 100644 index 0000000000..a140903f23 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/archive/tar_unix.go @@ -0,0 +1,22 @@ +//go:build linux || darwin +// +build linux darwin + +package archive + +import ( + "archive/tar" + "os" + + "golang.org/x/sys/unix" +) + +func setUmask(newMask int) (oldMask int) { + return unix.Umask(newMask) +} + +func createSymlink(hdr *tar.Header) error { + return os.Symlink(hdr.Linkname, hdr.Name) +} + +func addSysAttributes(hdr *tar.Header, fi os.FileInfo) { +} diff --git a/vendor/github.com/buildpacks/lifecycle/archive/tar_windows.go b/vendor/github.com/buildpacks/lifecycle/archive/tar_windows.go new file mode 100644 index 0000000000..61f3cc8e3d --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/archive/tar_windows.go @@ -0,0 +1,56 @@ +package archive + +import ( + "archive/tar" + "os" + "strconv" + "syscall" + + "github.com/pkg/errors" +) + +const ( + symbolicLinkFlagAllowUnprivilegedCreate = 0x2 + + // MSWINDOWS pax vendor extensions + hdrMSWindowsPrefix = "MSWINDOWS." + hdrFileAttributes = hdrMSWindowsPrefix + "fileattr" +) + +func setUmask(newMask int) (oldMask int) { + // Not implemented on Windows + return 0 +} + +// createSymlink uses the file attributes in the PAX records to decide whether to make a directory or file type symlink. +// We must use the syscall because we often create symlinks when the target does not exist and os.Symlink uses the mode +// of the target to create the appropriate type of symlink on windows https://github.com/golang/go/issues/39183 +func createSymlink(hdr *tar.Header) error { + var flags uint32 = symbolicLinkFlagAllowUnprivilegedCreate + if attrStr, ok := hdr.PAXRecords[hdrFileAttributes]; ok { + attr, err := strconv.ParseUint(attrStr, 10, 32) + if err != nil { + return errors.Wrapf(err, "failed to parse file attributes for header %q", hdr.Name) + } + if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { + flags |= syscall.SYMBOLIC_LINK_FLAG_DIRECTORY + } + } + + name, err := syscall.UTF16PtrFromString(hdr.Name) + if err != nil { + return err + } + target, err := syscall.UTF16PtrFromString(hdr.Linkname) + if err != nil { + return err + } + return syscall.CreateSymbolicLink(name, target, flags) +} + +// addSysAttributes adds PAXRecords containing file attributes +func addSysAttributes(hdr *tar.Header, fi os.FileInfo) { + attrs := fi.Sys().(*syscall.Win32FileAttributeData).FileAttributes + hdr.PAXRecords = map[string]string{} + hdr.PAXRecords[hdrFileAttributes] = strconv.FormatUint(uint64(attrs), 10) +} diff --git a/vendor/github.com/buildpacks/lifecycle/archive/writer.go b/vendor/github.com/buildpacks/lifecycle/archive/writer.go new file mode 100644 index 0000000000..431fec7bce --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/archive/writer.go @@ -0,0 +1,71 @@ +package archive + +import ( + "archive/tar" + "path/filepath" + "strings" + "time" +) + +var ( + // NormalizedModTime provides a valid "zero" value for ModTime + NormalizedModTime = time.Date(1980, time.January, 1, 0, 0, 1, 0, time.UTC) +) + +type TarWriter interface { + WriteHeader(hdr *tar.Header) error + Write(b []byte) (int, error) + Close() error +} + +// NormalizingTarWriter normalizes any written *tar.Header before passing it through to the wrapped TarWriter +// NormalizingTarWriter always normalizes ModTime, Uname, and Gname +// Other modifications can be enabled by invoking options on the NormalizingTarWriter +type NormalizingTarWriter struct { + TarWriter + headerOpts []HeaderOpt +} + +type HeaderOpt func(header *tar.Header) *tar.Header + +// WithUID sets Uid of any subsequently written *tar.Header to uid +func (tw *NormalizingTarWriter) WithUID(uid int) { + tw.headerOpts = append(tw.headerOpts, func(hdr *tar.Header) *tar.Header { + hdr.Uid = uid + return hdr + }) +} + +// WithGID sets Gid of any subsequently written *tar.Header to gid +func (tw *NormalizingTarWriter) WithGID(gid int) { + tw.headerOpts = append(tw.headerOpts, func(hdr *tar.Header) *tar.Header { + hdr.Gid = gid + return hdr + }) +} + +// WithModTime sets the ModTime of any subsequently written *tar.Header to modTime +func (tw *NormalizingTarWriter) WithModTime(modTime time.Time) { + tw.headerOpts = append(tw.headerOpts, func(hdr *tar.Header) *tar.Header { + hdr.ModTime = modTime + return hdr + }) +} + +// NewNormalizingTarWriter creates a NormalizingTarWriter that wraps the provided TarWriter +func NewNormalizingTarWriter(tw TarWriter) *NormalizingTarWriter { + return &NormalizingTarWriter{tw, []HeaderOpt{}} +} + +// WriteHeader writes the header to the wrapped TarWriter after applying standard and configured modifications +// Modification options will be apply in the order the options were invoked. +// Standard modification (ModTime, Uname, and Gname) are applied last. +func (tw *NormalizingTarWriter) WriteHeader(hdr *tar.Header) error { + for _, opt := range tw.headerOpts { + hdr = opt(hdr) + } + hdr.Name = filepath.ToSlash(strings.TrimPrefix(hdr.Name, filepath.VolumeName(hdr.Name))) + hdr.Uname = "" + hdr.Gname = "" + return tw.TarWriter.WriteHeader(hdr) +} diff --git a/vendor/github.com/buildpacks/lifecycle/auth/env_keychain.go b/vendor/github.com/buildpacks/lifecycle/auth/env_keychain.go new file mode 100644 index 0000000000..d2f341f27c --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/auth/env_keychain.go @@ -0,0 +1,196 @@ +package auth + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "os" + "regexp" + + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + "github.com/pkg/errors" +) + +const EnvRegistryAuth = "CNB_REGISTRY_AUTH" + +// DefaultKeychain returns a keychain containing authentication configuration for the given images +// from the following sources, if they exist, in order of precedence: +// the provided environment variable +// the docker config.json file +func DefaultKeychain(images ...string) (authn.Keychain, error) { + envKeychain, err := EnvKeychain(EnvRegistryAuth) + if err != nil { + return nil, err + } + + return authn.NewMultiKeychain( + envKeychain, + InMemoryKeychain(authn.DefaultKeychain, images...), + ), nil +} + +// ResolvedKeychain is an implementation of authn.Keychain that stores credentials in memory. +type ResolvedKeychain struct { + Auths map[string]string +} + +// EnvKeychain returns an authn.Keychain that uses the provided environment variable as a source of credentials. +// The value of the environment variable should be a JSON object that maps OCI registry hostnames to Authorization headers. +func EnvKeychain(envVar string) (authn.Keychain, error) { + authHeaders, err := ReadEnvVar(envVar) + if err != nil { + return nil, errors.Wrap(err, "reading auth env var") + } + return &ResolvedKeychain{Auths: authHeaders}, nil +} + +// InMemoryKeychain resolves credentials for the given images from the given keychain and returns a new keychain +// that stores the pre-resolved credentials in memory and returns them on demand. This is useful in cases where the +// backing credential store may become inaccessible in the the future. +func InMemoryKeychain(keychain authn.Keychain, images ...string) authn.Keychain { + return &ResolvedKeychain{ + Auths: buildAuthMap(keychain, images...), + } +} + +func (k *ResolvedKeychain) Resolve(resource authn.Resource) (authn.Authenticator, error) { + header, ok := k.Auths[resource.RegistryStr()] + if ok { + authConfig, err := authHeaderToConfig(header) + if err != nil { + return nil, errors.Wrap(err, "parsing auth header") + } + + return &providedAuth{config: authConfig}, nil + } + + return authn.Anonymous, nil +} + +type providedAuth struct { + config *authn.AuthConfig +} + +func (p *providedAuth) Authorization() (*authn.AuthConfig, error) { + return p.config, nil +} + +// ReadEnvVar parses an environment variable to produce a map of 'registry url' to 'authorization header'. +// +// Complementary to `BuildEnvVar`. +// +// Example Input: +// {"gcr.io": "Bearer asdf=", "docker.io": "Basic qwerty="} +// +// Example Output: +// gcr.io -> Bearer asdf= +// docker.io -> Basic qwerty= +func ReadEnvVar(envVar string) (map[string]string, error) { + authMap := map[string]string{} + + env := os.Getenv(envVar) + if env != "" { + err := json.Unmarshal([]byte(env), &authMap) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse %s value", envVar) + } + } + + return authMap, nil +} + +func buildAuthMap(keychain authn.Keychain, images ...string) map[string]string { + registryAuths := map[string]string{} + + for _, image := range images { + reference, authenticator, err := ReferenceForRepoName(keychain, image) + if err != nil { + continue + } + + if authenticator == authn.Anonymous { + continue + } + + authConfig, err := authenticator.Authorization() + if err != nil { + continue + } + + header, err := authConfigToHeader(authConfig) + if err != nil { + continue + } + registryAuths[reference.Context().Registry.Name()] = header + } + + return registryAuths +} + +// BuildEnvVar creates the contents to use for authentication environment variable. +// +// Complementary to `ReadEnvVar`. +func BuildEnvVar(keychain authn.Keychain, images ...string) (string, error) { + registryAuths := buildAuthMap(keychain, images...) + + authData, err := json.Marshal(registryAuths) + if err != nil { + return "", err + } + return string(authData), nil +} + +func authConfigToHeader(config *authn.AuthConfig) (string, error) { + if config.Auth != "" { + return fmt.Sprintf("Basic %s", config.Auth), nil + } + + if config.RegistryToken != "" { + return fmt.Sprintf("Bearer %s", config.RegistryToken), nil + } + + if config.Username != "" && config.Password != "" { + delimited := fmt.Sprintf("%s:%s", config.Username, config.Password) + encoded := base64.StdEncoding.EncodeToString([]byte(delimited)) + return fmt.Sprintf("Basic %s", encoded), nil + } + + return "", nil +} + +var ( + basicAuthRegExp = regexp.MustCompile("(?i)^basic (.*)$") + bearerAuthRegExp = regexp.MustCompile("(?i)^bearer (.*)$") +) + +func authHeaderToConfig(header string) (*authn.AuthConfig, error) { + if matches := basicAuthRegExp.FindAllStringSubmatch(header, -1); len(matches) != 0 { + return &authn.AuthConfig{ + Auth: matches[0][1], + }, nil + } + + if matches := bearerAuthRegExp.FindAllStringSubmatch(header, -1); len(matches) != 0 { + return &authn.AuthConfig{ + RegistryToken: matches[0][1], + }, nil + } + + return nil, errors.New("unknown auth type from header") +} + +// ReferenceForRepoName returns a reference and an authenticator for a given image name and keychain. +func ReferenceForRepoName(keychain authn.Keychain, ref string) (name.Reference, authn.Authenticator, error) { + var auth authn.Authenticator + r, err := name.ParseReference(ref, name.WeakValidation) + if err != nil { + return nil, nil, err + } + + auth, err = keychain.Resolve(r.Context().Registry) + if err != nil { + return nil, nil, err + } + return r, auth, nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/builder.go b/vendor/github.com/buildpacks/lifecycle/builder.go new file mode 100644 index 0000000000..ec24f45b0a --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/builder.go @@ -0,0 +1,281 @@ +package lifecycle + +import ( + "fmt" + "io" + "os" + "path/filepath" + "sort" + + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/api" + "github.com/buildpacks/lifecycle/buildpack" + "github.com/buildpacks/lifecycle/env" + io2 "github.com/buildpacks/lifecycle/internal/io" + "github.com/buildpacks/lifecycle/launch" + "github.com/buildpacks/lifecycle/layers" + "github.com/buildpacks/lifecycle/platform" +) + +type BuildEnv interface { + AddRootDir(baseDir string) error + AddEnvDir(envDir string, defaultAction env.ActionType) error + WithPlatform(platformDir string) ([]string, error) + List() []string +} + +type BuildpackStore interface { + Lookup(bpID, bpVersion string) (buildpack.Buildpack, error) +} + +type Buildpack interface { + Build(bpPlan buildpack.Plan, config buildpack.BuildConfig, bpEnv buildpack.BuildEnv) (buildpack.BuildResult, error) + ConfigFile() *buildpack.Descriptor + Detect(config *buildpack.DetectConfig, bpEnv buildpack.BuildEnv) buildpack.DetectRun +} + +type Builder struct { + AppDir string + LayersDir string + PlatformDir string + Platform Platform + Group buildpack.Group + Plan platform.BuildPlan + Out, Err io.Writer + Logger Logger + BuildpackStore BuildpackStore +} + +func (b *Builder) Build() (*platform.BuildMetadata, error) { + b.Logger.Debug("Starting build") + + // ensure layers sbom directory is removed + if err := os.RemoveAll(filepath.Join(b.LayersDir, "sbom")); err != nil { + return nil, errors.Wrap(err, "cleaning layers sbom directory") + } + + config, err := b.BuildConfig() + if err != nil { + return nil, err + } + + processMap := newProcessMap() + plan := b.Plan + var bom []buildpack.BOMEntry + var bomFiles []buildpack.BOMFile + var slices []layers.Slice + var labels []buildpack.Label + + bpEnv := env.NewBuildEnv(os.Environ()) + + for _, bp := range b.Group.Group { + b.Logger.Debugf("Running build for buildpack %s", bp) + + b.Logger.Debug("Looking up buildpack") + bpTOML, err := b.BuildpackStore.Lookup(bp.ID, bp.Version) + if err != nil { + return nil, err + } + + b.Logger.Debug("Finding plan") + bpPlan := plan.Find(bp.ID) + + br, err := bpTOML.Build(bpPlan, config, bpEnv) + if err != nil { + return nil, err + } + + b.Logger.Debug("Updating buildpack processes") + updateDefaultProcesses(br.Processes, api.MustParse(bp.API), b.Platform.API()) + + bom = append(bom, br.BOM...) + bomFiles = append(bomFiles, br.BOMFiles...) + labels = append(labels, br.Labels...) + plan = plan.Filter(br.MetRequires) + + b.Logger.Debug("Updating process list") + warning := processMap.add(br.Processes) + if warning != "" { + b.Logger.Warn(warning) + } + + slices = append(slices, br.Slices...) + + b.Logger.Debugf("Finished running build for buildpack %s", bp) + } + + if b.Platform.API().LessThan("0.4") { + config.Logger.Debug("Updating BOM entries") + for i := range bom { + bom[i].ConvertMetadataToVersion() + } + } + + if b.Platform.API().AtLeast("0.8") { + b.Logger.Debug("Copying sBOM files") + err = b.copyBOMFiles(config.LayersDir, bomFiles) + if err != nil { + return nil, err + } + } + + b.Logger.Debug("Listing processes") + procList := processMap.list() + + b.Logger.Debug("Finished build") + return &platform.BuildMetadata{ + BOM: bom, + Buildpacks: b.Group.Group, + Labels: labels, + Processes: procList, + Slices: slices, + BuildpackDefaultProcessType: processMap.defaultType, + }, nil +} + +// copyBOMFiles() copies any BOM files written by buildpacks during the Build() process +// to their appropriate locations, in preparation for its final application layer. +// This function handles both BOMs that are associated with a layer directory and BOMs that are not +// associated with a layer directory, since "bomFile.LayerName" will be "" in the latter case. +// +// Before: +// /layers +// └── buildpack.id +// ├── A +// │ └── ... +// ├── A.sbom.cdx.json +// └── launch.sbom.cdx.json +// +// After: +// /layers +// └── sbom +// └── launch +// └── buildpack.id +// ├── A +// │ └── sbom.cdx.json +// └── sbom.cdx.json +func (b *Builder) copyBOMFiles(layersDir string, bomFiles []buildpack.BOMFile) error { + var ( + buildSBOMDir = filepath.Join(layersDir, "sbom", "build") + cacheSBOMDir = filepath.Join(layersDir, "sbom", "cache") + launchSBOMDir = filepath.Join(layersDir, "sbom", "launch") + copyBOMFileTo = func(bomFile buildpack.BOMFile, sbomDir string) error { + targetDir := filepath.Join(sbomDir, launch.EscapeID(bomFile.BuildpackID), bomFile.LayerName) + err := os.MkdirAll(targetDir, os.ModePerm) + if err != nil { + return err + } + + name, err := bomFile.Name() + if err != nil { + return err + } + + return io2.Copy(bomFile.Path, filepath.Join(targetDir, name)) + } + ) + + for _, bomFile := range bomFiles { + switch bomFile.LayerType { + case buildpack.LayerTypeBuild: + if err := copyBOMFileTo(bomFile, buildSBOMDir); err != nil { + return err + } + case buildpack.LayerTypeCache: + if err := copyBOMFileTo(bomFile, cacheSBOMDir); err != nil { + return err + } + case buildpack.LayerTypeLaunch: + if err := copyBOMFileTo(bomFile, launchSBOMDir); err != nil { + return err + } + } + } + + return nil +} + +// we set default = true for web processes when platformAPI >= 0.6 and buildpackAPI < 0.6 +func updateDefaultProcesses(processes []launch.Process, buildpackAPI *api.Version, platformAPI *api.Version) { + if platformAPI.LessThan("0.6") || buildpackAPI.AtLeast("0.6") { + return + } + + for i := range processes { + if processes[i].Type == "web" { + processes[i].Default = true + } + } +} + +func (b *Builder) BuildConfig() (buildpack.BuildConfig, error) { + appDir, err := filepath.Abs(b.AppDir) + if err != nil { + return buildpack.BuildConfig{}, err + } + platformDir, err := filepath.Abs(b.PlatformDir) + if err != nil { + return buildpack.BuildConfig{}, err + } + layersDir, err := filepath.Abs(b.LayersDir) + if err != nil { + return buildpack.BuildConfig{}, err + } + + return buildpack.BuildConfig{ + AppDir: appDir, + PlatformDir: platformDir, + LayersDir: layersDir, + Out: b.Out, + Err: b.Err, + Logger: b.Logger, + }, nil +} + +type processMap struct { + typeToProcess map[string]launch.Process + defaultType string +} + +func newProcessMap() processMap { + return processMap{ + typeToProcess: make(map[string]launch.Process), + defaultType: "", + } +} + +// This function adds the processes from listToAdd to processMap +// it sets m.defaultType to the last default process +// if a non-default process overrides a default process, it returns a warning and unset m.defaultType +func (m *processMap) add(listToAdd []launch.Process) string { + warning := "" + for _, procToAdd := range listToAdd { + if procToAdd.Default { + m.defaultType = procToAdd.Type + warning = "" + } else if procToAdd.Type == m.defaultType { + // non-default process overrides a default process + m.defaultType = "" + warning = fmt.Sprintf("Warning: redefining the following default process type with a process not marked as default: %s\n", procToAdd.Type) + } + m.typeToProcess[procToAdd.Type] = procToAdd + } + return warning +} + +// list returns a sorted array of processes. +// The array is sorted based on the process types. +// The list is sorted for reproducibility. +func (m processMap) list() []launch.Process { + var keys []string + for proc := range m.typeToProcess { + keys = append(keys, proc) + } + sort.Strings(keys) + result := []launch.Process{} + for _, key := range keys { + result = append(result, m.typeToProcess[key].NoDefault()) // we set the default to false so it won't be part of metadata.toml + } + return result +} diff --git a/vendor/github.com/buildpacks/lifecycle/buildpack/bom.go b/vendor/github.com/buildpacks/lifecycle/buildpack/bom.go new file mode 100644 index 0000000000..d5b64a2f62 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/buildpack/bom.go @@ -0,0 +1,114 @@ +package buildpack + +import ( + "errors" + "fmt" + + "github.com/buildpacks/lifecycle/api" +) + +type BOMValidator interface { + ValidateBOM(GroupBuildpack, []BOMEntry) ([]BOMEntry, error) +} + +func NewBOMValidator(bpAPI string, layersDir string, logger Logger) BOMValidator { + switch { + case api.MustParse(bpAPI).LessThan("0.5"): + return &legacyBOMValidator{} + case api.MustParse(bpAPI).LessThan("0.7"): + return &v05To06BOMValidator{} + default: + return &defaultBOMValidator{logger: logger, layersDir: layersDir} + } +} + +type defaultBOMValidator struct { + logger Logger + layersDir string +} + +func (v *defaultBOMValidator) ValidateBOM(bp GroupBuildpack, bom []BOMEntry) ([]BOMEntry, error) { + if err := v.validateBOM(bom); err != nil { + return []BOMEntry{}, err + } + return v.processBOM(bp, bom), nil +} + +func (v *defaultBOMValidator) validateBOM(bom []BOMEntry) error { + sbomMatches, err := sbomGlob(v.layersDir) + if err != nil { + return err + } + + switch { + case len(bom) > 0 && len(sbomMatches) > 0: + // no-op: Don't show a warning here. + // This code path represents buildpack authors providing a + // migration path from old BOM to new SBoM. + case len(bom) > 0: + v.logger.Warn("BOM table is deprecated in this buildpack api version, though it remains supported for backwards compatibility. Buildpack authors should write BOM information to .sbom., launch.sbom., or build.sbom..") + } + + for _, entry := range bom { + if entry.Version != "" { + return fmt.Errorf("bom entry '%s' has a top level version which is not allowed. The buildpack should instead set metadata.version", entry.Name) + } + } + + return nil +} + +func (v *defaultBOMValidator) processBOM(buildpack GroupBuildpack, bom []BOMEntry) []BOMEntry { + return WithBuildpack(buildpack, bom) +} + +type v05To06BOMValidator struct{} + +func (v *v05To06BOMValidator) ValidateBOM(bp GroupBuildpack, bom []BOMEntry) ([]BOMEntry, error) { + if err := v.validateBOM(bom); err != nil { + return []BOMEntry{}, err + } + return v.processBOM(bp, bom), nil +} + +func (v *v05To06BOMValidator) validateBOM(bom []BOMEntry) error { + for _, entry := range bom { + if entry.Version != "" { + return fmt.Errorf("bom entry '%s' has a top level version which is not allowed. The buildpack should instead set metadata.version", entry.Name) + } + } + return nil +} + +func (v *v05To06BOMValidator) processBOM(buildpack GroupBuildpack, bom []BOMEntry) []BOMEntry { + return WithBuildpack(buildpack, bom) +} + +type legacyBOMValidator struct{} + +func (v *legacyBOMValidator) ValidateBOM(bp GroupBuildpack, bom []BOMEntry) ([]BOMEntry, error) { + if err := v.validateBOM(bom); err != nil { + return []BOMEntry{}, err + } + return v.processBOM(bp, bom), nil +} + +func (v *legacyBOMValidator) validateBOM(bom []BOMEntry) error { + for _, entry := range bom { + if version, ok := entry.Metadata["version"]; ok { + metadataVersion := fmt.Sprintf("%v", version) + if entry.Version != "" && entry.Version != metadataVersion { + return errors.New("top level version does not match metadata version") + } + } + } + return nil +} + +func (v *legacyBOMValidator) processBOM(buildpack GroupBuildpack, bom []BOMEntry) []BOMEntry { + bom = WithBuildpack(buildpack, bom) + for i := range bom { + bom[i].convertVersionToMetadata() + } + return bom +} diff --git a/vendor/github.com/buildpacks/lifecycle/buildpack/bomfile.go b/vendor/github.com/buildpacks/lifecycle/buildpack/bomfile.go new file mode 100644 index 0000000000..5afa926523 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/buildpack/bomfile.go @@ -0,0 +1,173 @@ +package buildpack + +import ( + "path/filepath" + "strings" + + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/api" +) + +const ( + LayerTypeBuild LayerType = iota + LayerTypeCache + LayerTypeLaunch +) + +const ( + mediaTypeCycloneDX = "application/vnd.cyclonedx+json" + mediaTypeSPDX = "application/spdx+json" + mediaTypeSyft = "application/vnd.syft+json" + mediaTypeUnsupported = "unsupported" +) + +type LayerType int + +type BOMFile struct { + BuildpackID string + LayerName string + LayerType LayerType + Path string +} + +// Name() returns the destination filename for a given BOM file +// cdx files should be renamed to "sbom.cdx.json" +// spdx files should be renamed to "sbom.spdx.json" +// syft files should be renamed to "sbom.syft.json" +// If the BOM is neither cdx, spdx, nor syft, the 2nd return argument +// will return an error to indicate an unsupported format +func (b *BOMFile) Name() (string, error) { + switch b.mediaType() { + case mediaTypeCycloneDX: + return "sbom.cdx.json", nil + case mediaTypeSPDX: + return "sbom.spdx.json", nil + case mediaTypeSyft: + return "sbom.syft.json", nil + default: + return "", errors.Errorf("unsupported sbom format: '%s'", b.Path) + } +} + +func (b *BOMFile) mediaType() string { + name := filepath.Base(b.Path) + + switch { + case strings.HasSuffix(name, ".sbom.cdx.json"): + return mediaTypeCycloneDX + case strings.HasSuffix(name, ".sbom.spdx.json"): + return mediaTypeSPDX + case strings.HasSuffix(name, ".sbom.syft.json"): + return mediaTypeSyft + default: + return mediaTypeUnsupported + } +} + +func validateMediaTypes(bp GroupBuildpack, bomfiles []BOMFile, sbomMediaTypes []string) error { + contains := func(vs []string, t string) bool { + for _, v := range vs { + if v == t { + return true + } + } + return false + } + + for _, bomFile := range bomfiles { + mediaType := bomFile.mediaType() + switch mediaType { + case mediaTypeUnsupported: + return errors.Errorf("unsupported sbom format: '%s'", bomFile.Path) + default: + if !contains(sbomMediaTypes, mediaType) { + return errors.Errorf("sbom type '%s' not declared for buildpack: '%s'", mediaType, bp.String()) + } + } + } + + return nil +} + +func sbomGlob(layersDir string) (matches []string, err error) { + layerGlob := filepath.Join(layersDir, "*.sbom.*.json") + matches, err = filepath.Glob(layerGlob) + return +} + +func (b *Descriptor) processBOMFiles(layersDir string, bp GroupBuildpack, bpLayers map[string]LayerMetadataFile, logger Logger) ([]BOMFile, error) { + var ( + files []BOMFile + ) + + matches, err := sbomGlob(layersDir) + if err != nil { + return nil, err + } + + if api.MustParse(b.API).LessThan("0.7") { + if len(matches) != 0 { + logger.Warnf("the following SBoM files will be ignored for buildpack api version < 0.7 [%s]", strings.Join(matches, ", ")) + } + + return nil, nil + } + + for _, m := range matches { + layerDir, file := filepath.Split(m) + layerName := strings.SplitN(file, ".", 2)[0] + + if layerName == "launch" { + files = append(files, BOMFile{ + BuildpackID: bp.ID, + LayerType: LayerTypeLaunch, + Path: m, + }) + + continue + } + + if layerName == "build" { + files = append(files, BOMFile{ + BuildpackID: bp.ID, + LayerType: LayerTypeBuild, + Path: m, + }) + + continue + } + + meta, ok := bpLayers[filepath.Join(layerDir, layerName)] + if !ok { + continue + } + + if meta.Launch { + files = append(files, BOMFile{ + BuildpackID: bp.ID, + LayerName: layerName, + LayerType: LayerTypeLaunch, + Path: m, + }) + } else { + files = append(files, BOMFile{ + BuildpackID: bp.ID, + LayerName: layerName, + LayerType: LayerTypeBuild, + Path: m, + }) + } + + if meta.Cache { + files = append(files, BOMFile{ + BuildpackID: bp.ID, + LayerName: layerName, + LayerType: LayerTypeCache, + Path: m, + }) + } + } + + return files, validateMediaTypes(bp, files, b.Buildpack.SBOM) +} diff --git a/vendor/github.com/buildpacks/lifecycle/buildpack/build.go b/vendor/github.com/buildpacks/lifecycle/buildpack/build.go new file mode 100644 index 0000000000..867ab9b08d --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/buildpack/build.go @@ -0,0 +1,393 @@ +package buildpack + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/BurntSushi/toml" + + "github.com/buildpacks/lifecycle/api" + "github.com/buildpacks/lifecycle/env" + "github.com/buildpacks/lifecycle/internal/encoding" + "github.com/buildpacks/lifecycle/launch" + "github.com/buildpacks/lifecycle/layers" +) + +type BuildEnv interface { + AddRootDir(baseDir string) error + AddEnvDir(envDir string, defaultAction env.ActionType) error + WithPlatform(platformDir string) ([]string, error) + List() []string +} + +type BuildConfig struct { + AppDir string + PlatformDir string + LayersDir string + Out io.Writer + Err io.Writer + Logger Logger +} + +type BuildResult struct { + BOM []BOMEntry + BOMFiles []BOMFile + Labels []Label + MetRequires []string + Processes []launch.Process + Slices []layers.Slice +} + +func (bom *BOMEntry) ConvertMetadataToVersion() { + if version, ok := bom.Metadata["version"]; ok { + metadataVersion := fmt.Sprintf("%v", version) + bom.Version = metadataVersion + } +} + +func (bom *BOMEntry) convertVersionToMetadata() { + if bom.Version != "" { + if bom.Metadata == nil { + bom.Metadata = make(map[string]interface{}) + } + bom.Metadata["version"] = bom.Version + bom.Version = "" + } +} + +func (b *Descriptor) Build(bpPlan Plan, config BuildConfig, bpEnv BuildEnv) (BuildResult, error) { + config.Logger.Debugf("Running build for buildpack %s", b) + + if api.MustParse(b.API).Equal(api.MustParse("0.2")) { + config.Logger.Debug("Updating buildpack plan entries") + + for i := range bpPlan.Entries { + bpPlan.Entries[i].convertMetadataToVersion() + } + } + + config.Logger.Debug("Creating plan directory") + planDir, err := ioutil.TempDir("", launch.EscapeID(b.Buildpack.ID)+"-") + if err != nil { + return BuildResult{}, err + } + defer os.RemoveAll(planDir) + + config.Logger.Debug("Preparing paths") + bpLayersDir, bpPlanPath, err := preparePaths(b.Buildpack.ID, bpPlan, config.LayersDir, planDir) + if err != nil { + return BuildResult{}, err + } + + config.Logger.Debug("Running build command") + if err := b.runBuildCmd(bpLayersDir, bpPlanPath, config, bpEnv); err != nil { + return BuildResult{}, err + } + + config.Logger.Debug("Processing layers") + bpLayers, err := b.processLayers(bpLayersDir, config.Logger) + if err != nil { + return BuildResult{}, err + } + + config.Logger.Debug("Updating environment") + if err := b.setupEnv(bpLayers, bpEnv); err != nil { + return BuildResult{}, err + } + + config.Logger.Debug("Reading output files") + return b.readOutputFiles(bpLayersDir, bpPlanPath, bpPlan, bpLayers, config.Logger) +} + +func renameLayerDirIfNeeded(layerMetadataFile LayerMetadataFile, layerDir string) error { + // rename / to /.ignore if buildpack API >= 0.6 and all of the types flags are set to false + if !layerMetadataFile.Launch && !layerMetadataFile.Cache && !layerMetadataFile.Build { + if err := os.Rename(layerDir, layerDir+".ignore"); err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + } + return nil +} + +func (b *Descriptor) processLayers(layersDir string, logger Logger) (map[string]LayerMetadataFile, error) { + if api.MustParse(b.API).LessThan("0.6") { + return eachLayer(layersDir, b.API, func(path, buildpackAPI string) (LayerMetadataFile, error) { + layerMetadataFile, msg, err := DecodeLayerMetadataFile(path+".toml", buildpackAPI) + if err != nil { + return LayerMetadataFile{}, err + } + if msg != "" { + logger.Warn(msg) + } + return layerMetadataFile, nil + }) + } + return eachLayer(layersDir, b.API, func(path, buildpackAPI string) (LayerMetadataFile, error) { + layerMetadataFile, msg, err := DecodeLayerMetadataFile(path+".toml", buildpackAPI) + if err != nil { + return LayerMetadataFile{}, err + } + if msg != "" { + return LayerMetadataFile{}, errors.New(msg) + } + if err := renameLayerDirIfNeeded(layerMetadataFile, path); err != nil { + return LayerMetadataFile{}, err + } + return layerMetadataFile, nil + }) +} + +func preparePaths(bpID string, bpPlan Plan, layersDir, planDir string) (string, string, error) { + bpDirName := launch.EscapeID(bpID) + bpLayersDir := filepath.Join(layersDir, bpDirName) + bpPlanDir := filepath.Join(planDir, bpDirName) + if err := os.MkdirAll(bpLayersDir, 0777); err != nil { + return "", "", err + } + if err := os.MkdirAll(bpPlanDir, 0777); err != nil { + return "", "", err + } + bpPlanPath := filepath.Join(bpPlanDir, "plan.toml") + if err := encoding.WriteTOML(bpPlanPath, bpPlan); err != nil { + return "", "", err + } + + return bpLayersDir, bpPlanPath, nil +} + +func (b *Descriptor) runBuildCmd(bpLayersDir, bpPlanPath string, config BuildConfig, bpEnv BuildEnv) error { + cmd := exec.Command( + filepath.Join(b.Dir, "bin", "build"), + bpLayersDir, + config.PlatformDir, + bpPlanPath, + ) // #nosec G204 + cmd.Dir = config.AppDir + cmd.Stdout = config.Out + cmd.Stderr = config.Err + + var err error + if b.Buildpack.ClearEnv { + cmd.Env = bpEnv.List() + } else { + cmd.Env, err = bpEnv.WithPlatform(config.PlatformDir) + if err != nil { + return err + } + } + cmd.Env = append(cmd.Env, EnvBuildpackDir+"="+b.Dir) + + if err := cmd.Run(); err != nil { + return NewError(err, ErrTypeBuildpack) + } + return nil +} + +func (b *Descriptor) setupEnv(bpLayers map[string]LayerMetadataFile, buildEnv BuildEnv) error { + bpAPI := api.MustParse(b.API) + for path, layerMetadataFile := range bpLayers { + if !layerMetadataFile.Build { + continue + } + if err := buildEnv.AddRootDir(path); err != nil { + return err + } + if err := buildEnv.AddEnvDir(filepath.Join(path, "env"), env.DefaultActionType(bpAPI)); err != nil { + return err + } + if err := buildEnv.AddEnvDir(filepath.Join(path, "env.build"), env.DefaultActionType(bpAPI)); err != nil { + return err + } + } + return nil +} + +func eachLayer(bpLayersDir, buildpackAPI string, fn func(path, api string) (LayerMetadataFile, error)) (map[string]LayerMetadataFile, error) { + files, err := ioutil.ReadDir(bpLayersDir) + if os.IsNotExist(err) { + return map[string]LayerMetadataFile{}, nil + } else if err != nil { + return map[string]LayerMetadataFile{}, err + } + bpLayers := map[string]LayerMetadataFile{} + for _, f := range files { + if f.IsDir() || !strings.HasSuffix(f.Name(), ".toml") { + continue + } + path := filepath.Join(bpLayersDir, strings.TrimSuffix(f.Name(), ".toml")) + layerMetadataFile, err := fn(path, buildpackAPI) + if err != nil { + return map[string]LayerMetadataFile{}, err + } + bpLayers[path] = layerMetadataFile + } + return bpLayers, nil +} + +func (b *Descriptor) readOutputFiles(bpLayersDir, bpPlanPath string, bpPlanIn Plan, bpLayers map[string]LayerMetadataFile, logger Logger) (BuildResult, error) { + br := BuildResult{} + bpFromBpInfo := GroupBuildpack{ID: b.Buildpack.ID, Version: b.Buildpack.Version} + + // setup launch.toml + var launchTOML LaunchTOML + launchPath := filepath.Join(bpLayersDir, "launch.toml") + + bomValidator := NewBOMValidator(b.API, bpLayersDir, logger) + + var err error + if api.MustParse(b.API).LessThan("0.5") { + // read buildpack plan + var bpPlanOut Plan + if _, err := toml.DecodeFile(bpPlanPath, &bpPlanOut); err != nil { + return BuildResult{}, err + } + + // set BOM and MetRequires + br.BOM, err = bomValidator.ValidateBOM(bpFromBpInfo, bpPlanOut.toBOM()) + if err != nil { + return BuildResult{}, err + } + br.MetRequires = names(bpPlanOut.Entries) + + // set BOM files + br.BOMFiles, err = b.processBOMFiles(bpLayersDir, bpFromBpInfo, bpLayers, logger) + if err != nil { + return BuildResult{}, err + } + + // read launch.toml, return if not exists + if _, err := toml.DecodeFile(launchPath, &launchTOML); os.IsNotExist(err) { + return br, nil + } else if err != nil { + return BuildResult{}, err + } + } else { + // read build.toml + var bpBuild BuildTOML + buildPath := filepath.Join(bpLayersDir, "build.toml") + if _, err := toml.DecodeFile(buildPath, &bpBuild); err != nil && !os.IsNotExist(err) { + return BuildResult{}, err + } + if _, err := bomValidator.ValidateBOM(bpFromBpInfo, bpBuild.BOM); err != nil { + return BuildResult{}, err + } + + // set MetRequires + if err := validateUnmet(bpBuild.Unmet, bpPlanIn); err != nil { + return BuildResult{}, err + } + br.MetRequires = names(bpPlanIn.filter(bpBuild.Unmet).Entries) + + // set BOM files + br.BOMFiles, err = b.processBOMFiles(bpLayersDir, bpFromBpInfo, bpLayers, logger) + if err != nil { + return BuildResult{}, err + } + + // read launch.toml, return if not exists + if _, err := toml.DecodeFile(launchPath, &launchTOML); os.IsNotExist(err) { + return br, nil + } else if err != nil { + return BuildResult{}, err + } + + // set BOM + br.BOM, err = bomValidator.ValidateBOM(bpFromBpInfo, launchTOML.BOM) + if err != nil { + return BuildResult{}, err + } + } + + if err := overrideDefaultForOldBuildpacks(launchTOML.Processes, b.API, logger); err != nil { + return BuildResult{}, err + } + + if err := validateNoMultipleDefaults(launchTOML.Processes); err != nil { + return BuildResult{}, err + } + + // set data from launch.toml + br.Labels = append([]Label{}, launchTOML.Labels...) + for i := range launchTOML.Processes { + launchTOML.Processes[i].BuildpackID = b.Buildpack.ID + } + br.Processes = append([]launch.Process{}, launchTOML.Processes...) + br.Slices = append([]layers.Slice{}, launchTOML.Slices...) + + return br, nil +} + +func overrideDefaultForOldBuildpacks(processes []launch.Process, bpAPI string, logger Logger) error { + if api.MustParse(bpAPI).AtLeast("0.6") { + return nil + } + replacedDefaults := []string{} + for i := range processes { + if processes[i].Default { + replacedDefaults = append(replacedDefaults, processes[i].Type) + } + processes[i].Default = false + } + if len(replacedDefaults) > 0 { + logger.Warn(fmt.Sprintf("Warning: default processes aren't supported in this buildpack api version. Overriding the default value to false for the following processes: [%s]", strings.Join(replacedDefaults, ", "))) + } + return nil +} + +func validateNoMultipleDefaults(processes []launch.Process) error { + defaultType := "" + for _, process := range processes { + if process.Default && defaultType != "" { + return fmt.Errorf("multiple default process types aren't allowed") + } + if process.Default { + defaultType = process.Type + } + } + return nil +} + +func validateUnmet(unmet []Unmet, bpPlan Plan) error { + for _, unmet := range unmet { + if unmet.Name == "" { + return errors.New("unmet.name is required") + } + found := false + for _, req := range bpPlan.Entries { + if unmet.Name == req.Name { + found = true + break + } + } + if !found { + return fmt.Errorf("unmet.name '%s' must match a requested dependency", unmet.Name) + } + } + return nil +} + +func names(requires []Require) []string { + var out []string + for _, req := range requires { + out = append(out, req.Name) + } + return out +} + +func WithBuildpack(bp GroupBuildpack, bom []BOMEntry) []BOMEntry { + var out []BOMEntry + for _, entry := range bom { + entry.Buildpack = bp.NoAPI().NoHomepage() + out = append(out, entry) + } + return out +} diff --git a/vendor/github.com/buildpacks/lifecycle/buildpack/descriptor.go b/vendor/github.com/buildpacks/lifecycle/buildpack/descriptor.go new file mode 100644 index 0000000000..0349dd7641 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/buildpack/descriptor.go @@ -0,0 +1,89 @@ +// Buildpack descriptor file (https://github.com/buildpacks/spec/blob/main/buildpack.md#buildpacktoml-toml). + +package buildpack + +import "github.com/BurntSushi/toml" + +type Descriptor struct { + API string `toml:"api"` + Buildpack Info `toml:"buildpack"` + Order Order `toml:"order"` + Dir string `toml:"-"` +} + +func (b *Descriptor) ConfigFile() *Descriptor { + return b +} + +func (b *Descriptor) IsMetaBuildpack() bool { + return b.Order != nil +} + +func (b *Descriptor) String() string { + return b.Buildpack.Name + " " + b.Buildpack.Version +} + +type Info struct { + ClearEnv bool `toml:"clear-env,omitempty"` + Homepage string `toml:"homepage,omitempty"` + ID string `toml:"id"` + Name string `toml:"name"` + Version string `toml:"version"` + SBOM []string `toml:"sbom-formats,omitempty" json:"sbom-formats,omitempty"` +} + +type Order []Group + +type Group struct { + Group []GroupBuildpack `toml:"group"` +} + +func ReadGroup(path string) (Group, error) { + var group Group + _, err := toml.DecodeFile(path, &group) + return group, err +} + +func ReadOrder(path string) (Order, error) { + var order struct { + Order Order `toml:"order"` + } + _, err := toml.DecodeFile(path, &order) + return order.Order, err +} + +func (bg Group) Append(group ...Group) Group { + for _, g := range group { + bg.Group = append(bg.Group, g.Group...) + } + return bg +} + +// A GroupBuildpack represents a buildpack referenced in a buildpack.toml's [[order.group]]. +// It may be a regular buildpack, or a meta buildpack. +type GroupBuildpack struct { + API string `toml:"api,omitempty" json:"-"` + Homepage string `toml:"homepage,omitempty" json:"homepage,omitempty"` + ID string `toml:"id" json:"id"` + Optional bool `toml:"optional,omitempty" json:"optional,omitempty"` + Version string `toml:"version" json:"version"` +} + +func (bp GroupBuildpack) String() string { + return bp.ID + "@" + bp.Version +} + +func (bp GroupBuildpack) NoOpt() GroupBuildpack { + bp.Optional = false + return bp +} + +func (bp GroupBuildpack) NoAPI() GroupBuildpack { + bp.API = "" + return bp +} + +func (bp GroupBuildpack) NoHomepage() GroupBuildpack { + bp.Homepage = "" + return bp +} diff --git a/vendor/github.com/buildpacks/lifecycle/buildpack/detect.go b/vendor/github.com/buildpacks/lifecycle/buildpack/detect.go new file mode 100644 index 0000000000..53adcaf3ae --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/buildpack/detect.go @@ -0,0 +1,117 @@ +package buildpack + +import ( + "bytes" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "syscall" + + "github.com/BurntSushi/toml" + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/api" +) + +const EnvBuildpackDir = "CNB_BUILDPACK_DIR" + +type Logger interface { + Debug(msg string) + Debugf(fmt string, v ...interface{}) + + Info(msg string) + Infof(fmt string, v ...interface{}) + + Warn(msg string) + Warnf(fmt string, v ...interface{}) + + Error(msg string) + Errorf(fmt string, v ...interface{}) +} + +type DetectConfig struct { + AppDir string + PlatformDir string + Logger Logger +} + +func (b *Descriptor) Detect(config *DetectConfig, bpEnv BuildEnv) DetectRun { + appDir, err := filepath.Abs(config.AppDir) + if err != nil { + return DetectRun{Code: -1, Err: err} + } + platformDir, err := filepath.Abs(config.PlatformDir) + if err != nil { + return DetectRun{Code: -1, Err: err} + } + planDir, err := ioutil.TempDir("", "plan.") + if err != nil { + return DetectRun{Code: -1, Err: err} + } + defer os.RemoveAll(planDir) + + planPath := filepath.Join(planDir, "plan.toml") + if err := ioutil.WriteFile(planPath, nil, 0600); err != nil { + return DetectRun{Code: -1, Err: err} + } + + out := &bytes.Buffer{} + cmd := exec.Command( + filepath.Join(b.Dir, "bin", "detect"), + platformDir, + planPath, + ) // #nosec G204 + cmd.Dir = appDir + cmd.Stdout = out + cmd.Stderr = out + + if b.Buildpack.ClearEnv { + cmd.Env = bpEnv.List() + } else { + cmd.Env, err = bpEnv.WithPlatform(config.PlatformDir) + if err != nil { + return DetectRun{Code: -1, Err: err} + } + } + cmd.Env = append(cmd.Env, EnvBuildpackDir+"="+b.Dir) + + if err := cmd.Run(); err != nil { + if err, ok := err.(*exec.ExitError); ok { + if status, ok := err.Sys().(syscall.WaitStatus); ok { + return DetectRun{Code: status.ExitStatus(), Output: out.Bytes()} + } + } + return DetectRun{Code: -1, Err: err, Output: out.Bytes()} + } + var t DetectRun + if _, err := toml.DecodeFile(planPath, &t); err != nil { + return DetectRun{Code: -1, Err: err, Output: out.Bytes()} + } + if api.MustParse(b.API).Equal(api.MustParse("0.2")) { + if t.hasInconsistentVersions() || t.Or.hasInconsistentVersions() { + t.Err = errors.Errorf(`buildpack %s has a "version" key that does not match "metadata.version"`, b.Buildpack.ID) + t.Code = -1 + } + } + if api.MustParse(b.API).AtLeast("0.3") { + if t.hasDoublySpecifiedVersions() || t.Or.hasDoublySpecifiedVersions() { + t.Err = errors.Errorf(`buildpack %s has a "version" key and a "metadata.version" which cannot be specified together. "metadata.version" should be used instead`, b.Buildpack.ID) + t.Code = -1 + } + } + if api.MustParse(b.API).AtLeast("0.3") { + if t.hasTopLevelVersions() || t.Or.hasTopLevelVersions() { + config.Logger.Warnf(`Warning: buildpack %s has a "version" key. This key is deprecated in build plan requirements in buildpack API 0.3. "metadata.version" should be used instead`, b.Buildpack.ID) + } + } + t.Output = out.Bytes() + return t +} + +type DetectRun struct { + BuildPlan + Output []byte `toml:"-"` + Code int `toml:"-"` + Err error `toml:"-"` +} diff --git a/vendor/github.com/buildpacks/lifecycle/buildpack/error.go b/vendor/github.com/buildpacks/lifecycle/buildpack/error.go new file mode 100644 index 0000000000..c0c310246d --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/buildpack/error.go @@ -0,0 +1,26 @@ +package buildpack + +type ErrorType string + +const ErrTypeBuildpack ErrorType = "ERR_BUILDPACK" +const ErrTypeFailedDetection ErrorType = "ERR_FAILED_DETECTION" + +type Error struct { + RootError error + Type ErrorType +} + +func (le *Error) Error() string { + if le.Cause() != nil { + return le.Cause().Error() + } + return string(le.Type) +} + +func (le *Error) Cause() error { + return le.RootError +} + +func NewError(cause error, errType ErrorType) *Error { + return &Error{RootError: cause, Type: errType} +} diff --git a/vendor/github.com/buildpacks/lifecycle/buildpack/files.go b/vendor/github.com/buildpacks/lifecycle/buildpack/files.go new file mode 100644 index 0000000000..4f8afae792 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/buildpack/files.go @@ -0,0 +1,205 @@ +// Data Format Files for the buildpack api spec (https://github.com/buildpacks/spec/blob/main/buildpack.md#data-format). + +package buildpack + +import ( + "fmt" + + "github.com/buildpacks/lifecycle/launch" + "github.com/buildpacks/lifecycle/layers" +) + +// launch.toml + +type LaunchTOML struct { + BOM []BOMEntry + Labels []Label + Processes []launch.Process `toml:"processes"` + Slices []layers.Slice `toml:"slices"` +} + +type BOMEntry struct { + Require + Buildpack GroupBuildpack `toml:"buildpack" json:"buildpack"` +} + +type Require struct { + Name string `toml:"name" json:"name"` + Version string `toml:"version,omitempty" json:"version,omitempty"` + Metadata map[string]interface{} `toml:"metadata" json:"metadata"` +} + +func (r *Require) convertMetadataToVersion() { + if version, ok := r.Metadata["version"]; ok { + r.Version = fmt.Sprintf("%v", version) + } +} + +func (r *Require) ConvertVersionToMetadata() { + if r.Version != "" { + if r.Metadata == nil { + r.Metadata = make(map[string]interface{}) + } + r.Metadata["version"] = r.Version + r.Version = "" + } +} + +func (r *Require) hasDoublySpecifiedVersions() bool { + if _, ok := r.Metadata["version"]; ok { + return r.Version != "" + } + return false +} + +func (r *Require) hasInconsistentVersions() bool { + if version, ok := r.Metadata["version"]; ok { + return r.Version != "" && r.Version != version + } + return false +} + +func (r *Require) hasTopLevelVersions() bool { + return r.Version != "" +} + +type Label struct { + Key string `toml:"key"` + Value string `toml:"value"` +} + +// build.toml + +type BuildTOML struct { + BOM []BOMEntry `toml:"bom"` + Unmet []Unmet `toml:"unmet"` +} + +type Unmet struct { + Name string `toml:"name"` +} + +// store.toml + +type StoreTOML struct { + Data map[string]interface{} `json:"metadata" toml:"metadata"` +} + +// build plan + +type BuildPlan struct { + PlanSections + Or planSectionsList `toml:"or"` +} + +func (p *PlanSections) hasInconsistentVersions() bool { + for _, req := range p.Requires { + if req.hasInconsistentVersions() { + return true + } + } + return false +} + +func (p *PlanSections) hasDoublySpecifiedVersions() bool { + for _, req := range p.Requires { + if req.hasDoublySpecifiedVersions() { + return true + } + } + return false +} + +func (p *PlanSections) hasTopLevelVersions() bool { + for _, req := range p.Requires { + if req.hasTopLevelVersions() { + return true + } + } + return false +} + +type planSectionsList []PlanSections + +func (p *planSectionsList) hasInconsistentVersions() bool { + for _, planSection := range *p { + if planSection.hasInconsistentVersions() { + return true + } + } + return false +} + +func (p *planSectionsList) hasDoublySpecifiedVersions() bool { + for _, planSection := range *p { + if planSection.hasDoublySpecifiedVersions() { + return true + } + } + return false +} + +func (p *planSectionsList) hasTopLevelVersions() bool { + for _, planSection := range *p { + if planSection.hasTopLevelVersions() { + return true + } + } + return false +} + +type PlanSections struct { + Requires []Require `toml:"requires"` + Provides []Provide `toml:"provides"` +} + +type Provide struct { + Name string `toml:"name"` +} + +// buildpack plan + +type Plan struct { + Entries []Require `toml:"entries"` +} + +func (p Plan) filter(unmet []Unmet) Plan { + var out []Require + for _, entry := range p.Entries { + if !containsName(unmet, entry.Name) { + out = append(out, entry) + } + } + return Plan{Entries: out} +} + +func (p Plan) toBOM() []BOMEntry { + var bom []BOMEntry + for _, entry := range p.Entries { + bom = append(bom, BOMEntry{Require: entry}) + } + return bom +} + +func containsName(unmet []Unmet, name string) bool { + for _, u := range unmet { + if u.Name == name { + return true + } + } + return false +} + +// layer content metadata + +type LayersMetadata struct { + ID string `json:"key" toml:"key"` + Version string `json:"version" toml:"version"` + Layers map[string]LayerMetadata `json:"layers" toml:"layers"` + Store *StoreTOML `json:"store,omitempty" toml:"store"` +} + +type LayerMetadata struct { + SHA string `json:"sha" toml:"sha"` + LayerMetadataFile +} diff --git a/vendor/github.com/buildpacks/lifecycle/buildpack/layermetadata.go b/vendor/github.com/buildpacks/lifecycle/buildpack/layermetadata.go new file mode 100644 index 0000000000..21c2580651 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/buildpack/layermetadata.go @@ -0,0 +1,136 @@ +package buildpack + +import ( + "errors" + "fmt" + "os" + + "github.com/BurntSushi/toml" + + "github.com/buildpacks/lifecycle/api" +) + +type LayerMetadataFile struct { + Data interface{} `json:"data" toml:"metadata"` + Build bool `json:"build" toml:"build"` + Launch bool `json:"launch" toml:"launch"` + Cache bool `json:"cache" toml:"cache"` +} + +func EncodeLayerMetadataFile(lmf LayerMetadataFile, path, buildpackAPI string) error { + fh, err := os.Create(path) + if err != nil { + return err + } + defer fh.Close() + + encoders := supportedEncoderDecoders() + + for _, encoder := range encoders { + if encoder.IsSupported(buildpackAPI) { + return encoder.Encode(fh, lmf) + } + } + return errors.New("couldn't find an encoder") +} + +func DecodeLayerMetadataFile(path, buildpackAPI string) (LayerMetadataFile, string, error) { // TODO: pass the logger and print the warning inside (instead of returning a message) + fh, err := os.Open(path) + if os.IsNotExist(err) { + return LayerMetadataFile{}, "", nil + } else if err != nil { + return LayerMetadataFile{}, "", err + } + defer fh.Close() + + decoders := supportedEncoderDecoders() + + for _, decoder := range decoders { + if decoder.IsSupported(buildpackAPI) { + return decoder.Decode(path) + } + } + return LayerMetadataFile{}, "", errors.New("couldn't find a decoder") +} + +type encoderDecoder interface { + IsSupported(buildpackAPI string) bool + Encode(file *os.File, lmf LayerMetadataFile) error + Decode(path string) (LayerMetadataFile, string, error) +} + +func supportedEncoderDecoders() []encoderDecoder { + return []encoderDecoder{ + &defaultEncoderDecoder{}, + &legacyEncoderDecoder{}, + } +} + +type defaultEncoderDecoder struct{} + +func (d *defaultEncoderDecoder) IsSupported(buildpackAPI string) bool { + return api.MustParse(buildpackAPI).AtLeast("0.6") +} + +func (d *defaultEncoderDecoder) Encode(file *os.File, lmf LayerMetadataFile) error { + // omit the types table - all the flags are set to false + type dataTomlFile struct { + Data interface{} `toml:"metadata"` + } + dtf := dataTomlFile{Data: lmf.Data} + return toml.NewEncoder(file).Encode(dtf) +} + +func (d *defaultEncoderDecoder) Decode(path string) (LayerMetadataFile, string, error) { + type typesTable struct { + Build bool `toml:"build"` + Launch bool `toml:"launch"` + Cache bool `toml:"cache"` + } + type layerMetadataTomlFile struct { + Data interface{} `toml:"metadata"` + Types typesTable `toml:"types"` + } + + var lmtf layerMetadataTomlFile + md, err := toml.DecodeFile(path, &lmtf) + if err != nil { + return LayerMetadataFile{}, "", err + } + msg := "" + if isWrongFormat := typesInTopLevel(md); isWrongFormat { + msg = fmt.Sprintf("the launch, cache and build flags should be in the types table of %s", path) + } + return LayerMetadataFile{Data: lmtf.Data, Build: lmtf.Types.Build, Launch: lmtf.Types.Launch, Cache: lmtf.Types.Cache}, msg, nil +} + +func typesInTopLevel(md toml.MetaData) bool { + return md.IsDefined("build") || md.IsDefined("launch") || md.IsDefined("cache") +} + +type legacyEncoderDecoder struct{} + +func (d *legacyEncoderDecoder) IsSupported(buildpackAPI string) bool { + return api.MustParse(buildpackAPI).LessThan("0.6") +} + +func (d *legacyEncoderDecoder) Encode(file *os.File, lmf LayerMetadataFile) error { + return toml.NewEncoder(file).Encode(lmf) +} + +func (d *legacyEncoderDecoder) Decode(path string) (LayerMetadataFile, string, error) { + var lmf LayerMetadataFile + md, err := toml.DecodeFile(path, &lmf) + if err != nil { + return LayerMetadataFile{}, "", err + } + msg := "" + if isWrongFormat := typesInTypesTable(md); isWrongFormat { + msg = "Types table isn't supported in this buildpack api version. The launch, build and cache flags should be in the top level. Ignoring the values in the types table." + } + return lmf, msg, nil +} + +func typesInTypesTable(md toml.MetaData) bool { + return md.IsDefined("types") +} diff --git a/vendor/github.com/buildpacks/lifecycle/buildpack/layers.go b/vendor/github.com/buildpacks/lifecycle/buildpack/layers.go new file mode 100644 index 0000000000..447a2cac20 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/buildpack/layers.go @@ -0,0 +1,199 @@ +package buildpack + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + + "github.com/BurntSushi/toml" + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/api" + "github.com/buildpacks/lifecycle/launch" +) + +type LayersDir struct { + Path string + layers []Layer + name string + Buildpack GroupBuildpack + Store *StoreTOML +} + +func ReadLayersDir(layersDir string, bp GroupBuildpack, logger Logger) (LayersDir, error) { + path := filepath.Join(layersDir, launch.EscapeID(bp.ID)) + logger.Debugf("Reading buildpack directory: %s", path) + bpDir := LayersDir{ + name: bp.ID, + Path: path, + layers: []Layer{}, + Buildpack: bp, + } + + fis, err := ioutil.ReadDir(path) + if err != nil && !os.IsNotExist(err) { + return LayersDir{}, err + } + + names := map[string]struct{}{} + var tomls []string + for _, fi := range fis { + logger.Debugf("Reading buildpack directory item: %s", fi.Name()) + if fi.IsDir() { + bpDir.layers = append(bpDir.layers, *bpDir.NewLayer(fi.Name(), bp.API, logger)) + names[fi.Name()] = struct{}{} + continue + } + if strings.HasSuffix(fi.Name(), ".toml") { + tomls = append(tomls, filepath.Join(path, fi.Name())) + } + } + + for _, tf := range tomls { + name := strings.TrimSuffix(filepath.Base(tf), ".toml") + if name == "store" { + var bpStore StoreTOML + _, err := toml.DecodeFile(tf, &bpStore) + if err != nil { + return LayersDir{}, errors.Wrapf(err, "failed decoding store.toml for buildpack %q", bp.ID) + } + bpDir.Store = &bpStore + continue + } + if name == "launch" { + // don't treat launch.toml as a layer + continue + } + if name == "build" && api.MustParse(bp.API).AtLeast("0.5") { + // if the buildpack API supports build.toml don't treat it as a layer + continue + } + if _, ok := names[name]; !ok { + bpDir.layers = append(bpDir.layers, *bpDir.NewLayer(name, bp.API, logger)) + } + } + sort.Slice(bpDir.layers, func(i, j int) bool { + return bpDir.layers[i].identifier < bpDir.layers[j].identifier + }) + return bpDir, nil +} + +func (d *LayersDir) FindLayers(f func(layer Layer) bool) []Layer { + var selectedLayers []Layer + for _, l := range d.layers { + if f(l) { + selectedLayers = append(selectedLayers, l) + } + } + return selectedLayers +} + +func MadeLaunch(l Layer) bool { + md, err := l.Read() + return err == nil && md.Launch +} + +func MadeCached(l Layer) bool { + md, err := l.Read() + return err == nil && md.Cache +} + +func Malformed(l Layer) bool { + _, err := l.Read() + return err != nil +} + +func (d *LayersDir) NewLayer(name, buildpackAPI string, logger Logger) *Layer { + return &Layer{ + layerDir: layerDir{ + path: filepath.Join(d.Path, name), + identifier: fmt.Sprintf("%s:%s", d.Buildpack.ID, name), + }, + api: buildpackAPI, + logger: logger, + } +} + +type Layer struct { // TODO: need to refactor so api and logger won't be part of this struct + layerDir + api string + logger Logger +} + +type layerDir struct { + identifier string + path string +} + +func (l *Layer) Name() string { + return filepath.Base(l.path) +} + +func (l *Layer) HasLocalContents() bool { + _, err := ioutil.ReadDir(l.path) + + return !os.IsNotExist(err) +} + +func (l *Layer) Identifier() string { + return l.identifier +} + +func (l *Layer) Path() string { + return l.path +} + +func (l *Layer) Read() (LayerMetadata, error) { + tomlPath := l.Path() + ".toml" + layerMetadataFile, msg, err := DecodeLayerMetadataFile(tomlPath, l.api) + if err != nil { + return LayerMetadata{}, err + } + if msg != "" { + if api.MustParse(l.api).LessThan("0.6") { + l.logger.Warn(msg) + } else { + return LayerMetadata{}, errors.New(msg) + } + } + var sha string + shaBytes, err := ioutil.ReadFile(l.Path() + ".sha") + if err != nil && !os.IsNotExist(err) { // if the sha file doesn't exist, an empty sha will be returned + return LayerMetadata{}, err + } + if err == nil { + sha = string(shaBytes) + } + return LayerMetadata{SHA: sha, LayerMetadataFile: layerMetadataFile}, nil +} + +func (l *Layer) Remove() error { + if err := os.RemoveAll(l.path); err != nil && !os.IsNotExist(err) { + return err + } + if err := os.Remove(l.path + ".sha"); err != nil && !os.IsNotExist(err) { + return err + } + if err := os.Remove(l.path + ".toml"); err != nil && !os.IsNotExist(err) { + return err + } + return nil +} + +func (l *Layer) WriteMetadata(metadata LayerMetadataFile) error { + path := l.path + ".toml" + if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil { + return err + } + return EncodeLayerMetadataFile(metadata, path, l.api) +} + +func (l *Layer) WriteSha(sha string) error { + if err := ioutil.WriteFile(l.path+".sha", []byte(sha), 0666); err != nil { + return err + } // #nosec G306 + return nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/buildpack/store.go b/vendor/github.com/buildpacks/lifecycle/buildpack/store.go new file mode 100644 index 0000000000..e1aef4c34b --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/buildpack/store.go @@ -0,0 +1,38 @@ +package buildpack + +import ( + "path/filepath" + + "github.com/BurntSushi/toml" + + "github.com/buildpacks/lifecycle/launch" +) + +type Buildpack interface { + Build(bpPlan Plan, config BuildConfig, bpEnv BuildEnv) (BuildResult, error) + ConfigFile() *Descriptor + Detect(config *DetectConfig, bpEnv BuildEnv) DetectRun +} + +type DirBuildpackStore struct { + Dir string +} + +func NewBuildpackStore(dir string) (*DirBuildpackStore, error) { + dir, err := filepath.Abs(dir) + if err != nil { + return nil, err + } + return &DirBuildpackStore{Dir: dir}, nil +} + +func (f *DirBuildpackStore) Lookup(bpID, bpVersion string) (Buildpack, error) { + bpTOML := Descriptor{} + bpPath := filepath.Join(f.Dir, launch.EscapeID(bpID), bpVersion) + tomlPath := filepath.Join(bpPath, "buildpack.toml") + if _, err := toml.DecodeFile(tomlPath, &bpTOML); err != nil { + return nil, err + } + bpTOML.Dir = bpPath + return &bpTOML, nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/cache.go b/vendor/github.com/buildpacks/lifecycle/cache.go new file mode 100644 index 0000000000..9d24f97bb0 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/cache.go @@ -0,0 +1,146 @@ +package lifecycle + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/buildpack" + "github.com/buildpacks/lifecycle/platform" +) + +type LayerDir interface { + Identifier() string + Path() string +} + +func (e *Exporter) Cache(layersDir string, cacheStore Cache) error { + var err error + if !cacheStore.Exists() { + e.Logger.Info("Layer cache not found") + } + origMeta, err := cacheStore.RetrieveMetadata() + if err != nil { + return errors.Wrap(err, "metadata for previous cache") + } + meta := platform.CacheMetadata{} + + for _, bp := range e.Buildpacks { + bpDir, err := buildpack.ReadLayersDir(layersDir, bp, e.Logger) + if err != nil { + return errors.Wrapf(err, "reading layers for buildpack '%s'", bp.ID) + } + + bpMD := buildpack.LayersMetadata{ + ID: bp.ID, + Version: bp.Version, + Layers: map[string]buildpack.LayerMetadata{}, + } + for _, layer := range bpDir.FindLayers(buildpack.MadeCached) { + layer := layer + if !layer.HasLocalContents() { + e.Logger.Warnf("Failed to cache layer '%s' because it has no contents", layer.Identifier()) + continue + } + lmd, err := layer.Read() + if err != nil { + e.Logger.Warnf("Failed to cache layer '%s' because of error reading metadata: %s", layer.Identifier(), err) + continue + } + origLayerMetadata := origMeta.MetadataForBuildpack(bp.ID).Layers[layer.Name()] + if lmd.SHA, err = e.addOrReuseCacheLayer(cacheStore, &layer, origLayerMetadata.SHA); err != nil { + e.Logger.Warnf("Failed to cache layer '%s': %s", layer.Identifier(), err) + continue + } + bpMD.Layers[layer.Name()] = lmd + } + meta.Buildpacks = append(meta.Buildpacks, bpMD) + } + + if e.PlatformAPI.AtLeast("0.8") { + if err := e.addSBOMCacheLayer(layersDir, cacheStore, origMeta, &meta); err != nil { + return err + } + } + + if err := cacheStore.SetMetadata(meta); err != nil { + return errors.Wrap(err, "setting cache metadata") + } + if err := cacheStore.Commit(); err != nil { + return errors.Wrap(err, "committing cache") + } + + return nil +} + +type layerDir struct { + path string + identifier string +} + +func (l *layerDir) Identifier() string { + return l.identifier +} + +func (l *layerDir) Path() string { + return l.path +} + +func (e *Exporter) addOrReuseCacheLayer(cache Cache, layerDir LayerDir, previousSHA string) (string, error) { + layer, err := e.LayerFactory.DirLayer(layerDir.Identifier(), layerDir.Path()) + if err != nil { + return "", errors.Wrapf(err, "creating layer '%s'", layerDir.Identifier()) + } + if layer.Digest == previousSHA { + e.Logger.Infof("Reusing cache layer '%s'\n", layer.ID) + e.Logger.Debugf("Layer '%s' SHA: %s\n", layer.ID, layer.Digest) + return layer.Digest, cache.ReuseLayer(previousSHA) + } + e.Logger.Infof("Adding cache layer '%s'\n", layer.ID) + e.Logger.Debugf("Layer '%s' SHA: %s\n", layer.ID, layer.Digest) + return layer.Digest, cache.AddLayerFile(layer.TarPath, layer.Digest) +} + +func (e *Exporter) addSBOMCacheLayer(layersDir string, cacheStore Cache, origMetadata platform.CacheMetadata, meta *platform.CacheMetadata) error { + sbomCacheDir, err := readLayersSBOM(layersDir, "cache", e.Logger) + if err != nil { + return errors.Wrap(err, "failed to read layers config sbom") + } + + if sbomCacheDir != nil { + l, err := e.LayerFactory.DirLayer(sbomCacheDir.Identifier(), sbomCacheDir.Path()) + if err != nil { + return errors.Wrapf(err, "creating layer '%s', path: '%s'", sbomCacheDir.Identifier(), sbomCacheDir.Path()) + } + + lyr := &layerDir{path: l.TarPath, identifier: l.ID} + + meta.BOM.SHA, err = e.addOrReuseCacheLayer(cacheStore, lyr, origMetadata.BOM.SHA) + if err != nil { + return err + } + } + + return nil +} + +func readLayersSBOM(layersDir string, bomType string, logger Logger) (LayerDir, error) { + path := filepath.Join(layersDir, "sbom", bomType) + _, err := ioutil.ReadDir(path) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + + return nil, err + } + + logger.Debugf("Found BOM of type %s for at %s", bomType, path) + return &layerDir{ + path: path, + identifier: fmt.Sprintf("%s.sbom", bomType), + }, nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/cmd/command.go b/vendor/github.com/buildpacks/lifecycle/cmd/command.go new file mode 100644 index 0000000000..50082776ab --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/cmd/command.go @@ -0,0 +1,62 @@ +package cmd + +import ( + "io/ioutil" + "log" + "os" +) + +// Command defines the interface for running the lifecycle phases +type Command interface { + // DefineFlags defines the flags that are considered valid and reads their values (if provided) + DefineFlags() + + // Args validates arguments and flags, and fills in default values + Args(nargs int, args []string) error + + // Privileges validates the needed privileges + Privileges() error + + // Exec executes the command + Exec() error +} + +func Run(c Command, asSubcommand bool) { + var ( + printVersion bool + logLevel string + noColor bool + ) + + log.SetOutput(ioutil.Discard) + FlagVersion(&printVersion) + FlagLogLevel(&logLevel) + FlagNoColor(&noColor) + c.DefineFlags() + if asSubcommand { + if err := flagSet.Parse(os.Args[2:]); err != nil { + // flagSet exits on error, we shouldn't get here + Exit(err) + } + } else { + if err := flagSet.Parse(os.Args[1:]); err != nil { + // flagSet exits on error, we shouldn't get here + Exit(err) + } + } + DisableColor(noColor) + + if printVersion { + ExitWithVersion() + } + if err := SetLogLevel(logLevel); err != nil { + Exit(err) + } + if err := c.Args(flagSet.NArg(), flagSet.Args()); err != nil { + Exit(err) + } + if err := c.Privileges(); err != nil { + Exit(err) + } + Exit(c.Exec()) +} diff --git a/vendor/github.com/buildpacks/lifecycle/cmd/exit.go b/vendor/github.com/buildpacks/lifecycle/cmd/exit.go new file mode 100644 index 0000000000..405251e906 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/cmd/exit.go @@ -0,0 +1,67 @@ +package cmd + +import ( + "fmt" + "os" + "strings" +) + +const ( + // lifecycle errors not specific to any phase: 1-99 + CodeFailed = 1 // CodeFailed indicates generic lifecycle error + // 2: reserved + CodeInvalidArgs = 3 + // 4: CodeInvalidEnv + // 5: CodeNotFound + // 9: CodeFailedUpdate + + // API errors + CodeIncompatiblePlatformAPI = 11 + CodeIncompatibleBuildpackAPI = 12 +) + +type ErrorFail struct { + Err error + Code int + Action []string +} + +func (e *ErrorFail) Error() string { + message := "failed to " + strings.Join(e.Action, " ") + if e.Err == nil { + return message + } + return fmt.Sprintf("%s: %s", message, e.Err) +} + +func FailCode(code int, action ...string) *ErrorFail { + return FailErrCode(nil, code, action...) +} + +func FailErr(err error, action ...string) *ErrorFail { + code := CodeFailed + if err, ok := err.(*ErrorFail); ok { + code = err.Code + } + return FailErrCode(err, code, action...) +} + +func FailErrCode(err error, code int, action ...string) *ErrorFail { + return &ErrorFail{Err: err, Code: code, Action: action} +} + +func Exit(err error) { + if err == nil { + os.Exit(0) + } + DefaultLogger.Errorf("%s\n", err) + if err, ok := err.(*ErrorFail); ok { + os.Exit(err.Code) + } + os.Exit(CodeFailed) +} + +func ExitWithVersion() { + DefaultLogger.Infof(buildVersion()) + os.Exit(0) +} diff --git a/vendor/github.com/buildpacks/lifecycle/cmd/flags.go b/vendor/github.com/buildpacks/lifecycle/cmd/flags.go new file mode 100644 index 0000000000..43fbc89aa1 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/cmd/flags.go @@ -0,0 +1,262 @@ +package cmd + +import ( + "flag" + "fmt" + "os" + "path/filepath" + "strconv" + + "github.com/buildpacks/lifecycle/api" +) + +var ( + DefaultAppDir = filepath.Join(rootDir, "workspace") + DefaultBuildpacksDir = filepath.Join(rootDir, "cnb", "buildpacks") + DefaultDeprecationMode = DeprecationModeWarn + DefaultLauncherPath = filepath.Join(rootDir, "cnb", "lifecycle", "launcher"+execExt) + DefaultLayersDir = filepath.Join(rootDir, "layers") + DefaultLogLevel = "info" + DefaultPlatformAPI = "0.3" + DefaultPlatformDir = filepath.Join(rootDir, "platform") + DefaultProcessType = "web" + DefaultStackPath = filepath.Join(rootDir, "cnb", "stack.toml") + + DefaultAnalyzedFile = "analyzed.toml" + DefaultGroupFile = "group.toml" + DefaultOrderFile = "order.toml" + DefaultPlanFile = "plan.toml" + DefaultProjectMetadataFile = "project-metadata.toml" + DefaultReportFile = "report.toml" + + PlaceholderAnalyzedPath = filepath.Join("", DefaultAnalyzedFile) + PlaceholderGroupPath = filepath.Join("", DefaultGroupFile) + PlaceholderPlanPath = filepath.Join("", DefaultPlanFile) + PlaceholderProjectMetadataPath = filepath.Join("", DefaultProjectMetadataFile) + PlaceholderReportPath = filepath.Join("", DefaultReportFile) + PlaceholderOrderPath = filepath.Join("", DefaultOrderFile) +) + +const ( + EnvAnalyzedPath = "CNB_ANALYZED_PATH" + EnvAppDir = "CNB_APP_DIR" + EnvBuildpacksDir = "CNB_BUILDPACKS_DIR" + EnvCacheDir = "CNB_CACHE_DIR" + EnvCacheImage = "CNB_CACHE_IMAGE" + EnvDeprecationMode = "CNB_DEPRECATION_MODE" + EnvGID = "CNB_GROUP_ID" + EnvGroupPath = "CNB_GROUP_PATH" + EnvLaunchCacheDir = "CNB_LAUNCH_CACHE_DIR" + EnvLayersDir = "CNB_LAYERS_DIR" + EnvLogLevel = "CNB_LOG_LEVEL" + EnvNoColor = "CNB_NO_COLOR" // defaults to false + EnvOrderPath = "CNB_ORDER_PATH" + EnvPlanPath = "CNB_PLAN_PATH" + EnvPlatformAPI = "CNB_PLATFORM_API" + EnvPlatformDir = "CNB_PLATFORM_DIR" + EnvPreviousImage = "CNB_PREVIOUS_IMAGE" + EnvProcessType = "CNB_PROCESS_TYPE" + EnvProjectMetadataPath = "CNB_PROJECT_METADATA_PATH" + EnvReportPath = "CNB_REPORT_PATH" + EnvRunImage = "CNB_RUN_IMAGE" + EnvSkipLayers = "CNB_ANALYZE_SKIP_LAYERS" // defaults to false + EnvSkipRestore = "CNB_SKIP_RESTORE" // defaults to false + EnvStackPath = "CNB_STACK_PATH" + EnvUID = "CNB_USER_ID" + EnvUseDaemon = "CNB_USE_DAEMON" // defaults to false +) + +var flagSet = flag.NewFlagSet("lifecycle", flag.ExitOnError) + +func FlagAnalyzedPath(analyzedPath *string) { + flagSet.StringVar(analyzedPath, "analyzed", EnvOrDefault(EnvAnalyzedPath, PlaceholderAnalyzedPath), "path to analyzed.toml") +} + +func DefaultAnalyzedPath(platformAPI, layersDir string) string { + return defaultPath(DefaultAnalyzedFile, platformAPI, layersDir) +} + +func FlagAppDir(appDir *string) { + flagSet.StringVar(appDir, "app", EnvOrDefault(EnvAppDir, DefaultAppDir), "path to app directory") +} + +func FlagBuildpacksDir(buildpacksDir *string) { + flagSet.StringVar(buildpacksDir, "buildpacks", EnvOrDefault(EnvBuildpacksDir, DefaultBuildpacksDir), "path to buildpacks directory") +} + +func FlagCacheDir(cacheDir *string) { + flagSet.StringVar(cacheDir, "cache-dir", os.Getenv(EnvCacheDir), "path to cache directory") +} + +func FlagCacheImage(cacheImage *string) { + flagSet.StringVar(cacheImage, "cache-image", os.Getenv(EnvCacheImage), "cache image tag name") +} + +func FlagGID(gid *int) { + flagSet.IntVar(gid, "gid", intEnv(EnvGID), "GID of user's group in the stack's build and run images") +} + +func FlagGroupPath(groupPath *string) { + flagSet.StringVar(groupPath, "group", EnvOrDefault(EnvGroupPath, PlaceholderGroupPath), "path to group.toml") +} + +func DefaultGroupPath(platformAPI, layersDir string) string { + return defaultPath(DefaultGroupFile, platformAPI, layersDir) +} + +func FlagLaunchCacheDir(launchCacheDir *string) { + flagSet.StringVar(launchCacheDir, "launch-cache", os.Getenv(EnvLaunchCacheDir), "path to launch cache directory") +} + +func FlagLauncherPath(launcherPath *string) { + flagSet.StringVar(launcherPath, "launcher", DefaultLauncherPath, "path to launcher binary") +} + +func FlagLayersDir(layersDir *string) { + flagSet.StringVar(layersDir, "layers", EnvOrDefault(EnvLayersDir, DefaultLayersDir), "path to layers directory") +} + +func FlagNoColor(skip *bool) { + flagSet.BoolVar(skip, "no-color", BoolEnv(EnvNoColor), "disable color output") +} + +func FlagOrderPath(orderPath *string) { + flagSet.StringVar(orderPath, "order", EnvOrDefault(EnvOrderPath, PlaceholderOrderPath), "path to order.toml") +} + +func DefaultOrderPath(platformAPI, layersDir string) string { + cnbOrderPath := filepath.Join(rootDir, "cnb", "order.toml") + + // prior to Platform API 0.6, the default is /cnb/order.toml + if api.MustParse(platformAPI).LessThan("0.6") { + return cnbOrderPath + } + + // the default is //order.toml or /cnb/order.toml if not present + layersOrderPath := filepath.Join(layersDir, "order.toml") + if _, err := os.Stat(layersOrderPath); os.IsNotExist(err) { + return cnbOrderPath + } + return layersOrderPath +} + +func FlagPlanPath(planPath *string) { + flagSet.StringVar(planPath, "plan", EnvOrDefault(EnvPlanPath, PlaceholderPlanPath), "path to plan.toml") +} + +func DefaultPlanPath(platformAPI, layersDir string) string { + return defaultPath(DefaultPlanFile, platformAPI, layersDir) +} + +func FlagPlatformDir(platformDir *string) { + flagSet.StringVar(platformDir, "platform", EnvOrDefault(EnvPlatformDir, DefaultPlatformDir), "path to platform directory") +} + +func FlagPreviousImage(image *string) { + flagSet.StringVar(image, "previous-image", os.Getenv(EnvPreviousImage), "reference to previous image") +} + +func FlagReportPath(reportPath *string) { + flagSet.StringVar(reportPath, "report", EnvOrDefault(EnvReportPath, PlaceholderReportPath), "path to report.toml") +} + +func DefaultReportPath(platformAPI, layersDir string) string { + return defaultPath(DefaultReportFile, platformAPI, layersDir) +} + +func FlagRunImage(runImage *string) { + flagSet.StringVar(runImage, "run-image", os.Getenv(EnvRunImage), "reference to run image") +} + +func FlagSkipLayers(skip *bool) { + flagSet.BoolVar(skip, "skip-layers", BoolEnv(EnvSkipLayers), "do not provide layer metadata to buildpacks") +} + +func FlagSkipRestore(skip *bool) { + flagSet.BoolVar(skip, "skip-restore", BoolEnv(EnvSkipRestore), "do not restore layers or layer metadata") +} + +func FlagStackPath(stackPath *string) { + flagSet.StringVar(stackPath, "stack", EnvOrDefault(EnvStackPath, DefaultStackPath), "path to stack.toml") +} + +func FlagTags(tags *StringSlice) { + flagSet.Var(tags, "tag", "additional tags") +} + +func FlagUID(uid *int) { + flagSet.IntVar(uid, "uid", intEnv(EnvUID), "UID of user in the stack's build and run images") +} + +func FlagUseDaemon(use *bool) { + flagSet.BoolVar(use, "daemon", BoolEnv(EnvUseDaemon), "export to docker daemon") +} + +func FlagVersion(version *bool) { + flagSet.BoolVar(version, "version", false, "show version") +} + +func FlagLogLevel(level *string) { + flagSet.StringVar(level, "log-level", EnvOrDefault(EnvLogLevel, DefaultLogLevel), "logging level") +} + +func FlagProjectMetadataPath(projectMetadataPath *string) { + flagSet.StringVar(projectMetadataPath, "project-metadata", EnvOrDefault(EnvProjectMetadataPath, PlaceholderProjectMetadataPath), "path to project-metadata.toml") +} + +func DefaultProjectMetadataPath(platformAPI, layersDir string) string { + return defaultPath(DefaultProjectMetadataFile, platformAPI, layersDir) +} + +func FlagProcessType(processType *string) { + flagSet.StringVar(processType, "process-type", os.Getenv(EnvProcessType), "default process type") +} + +func DeprecatedFlagRunImage(image *string) { + flagSet.StringVar(image, "image", "", "reference to run image") +} + +type StringSlice []string + +func (s *StringSlice) String() string { + return fmt.Sprintf("%+v", *s) +} + +func (s *StringSlice) Set(value string) error { + *s = append(*s, value) + return nil +} + +func intEnv(k string) int { + v := os.Getenv(k) + d, err := strconv.Atoi(v) + if err != nil { + return 0 + } + return d +} + +func BoolEnv(k string) bool { + v := os.Getenv(k) + b, err := strconv.ParseBool(v) + if err != nil { + return false + } + return b +} + +func EnvOrDefault(key string, defaultVal string) string { + if envVal := os.Getenv(key); envVal != "" { + return envVal + } + return defaultVal +} + +func defaultPath(fileName, platformAPI, layersDir string) string { + if (api.MustParse(platformAPI).LessThan("0.5")) || (layersDir == "") { + // prior to platform api 0.5, the default directory was the working dir. + // layersDir is unset when this call comes from the rebaser - will be fixed as part of https://github.com/buildpacks/spec/issues/156 + return filepath.Join(".", fileName) + } + return filepath.Join(layersDir, fileName) // starting from platform api 0.5, the default directory is the layers dir. +} diff --git a/vendor/github.com/buildpacks/lifecycle/cmd/flags_unix.go b/vendor/github.com/buildpacks/lifecycle/cmd/flags_unix.go new file mode 100644 index 0000000000..83d5bfdc69 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/cmd/flags_unix.go @@ -0,0 +1,9 @@ +//go:build linux || darwin +// +build linux darwin + +package cmd + +const ( + rootDir = "/" + execExt = "" +) diff --git a/vendor/github.com/buildpacks/lifecycle/cmd/flags_windows.go b/vendor/github.com/buildpacks/lifecycle/cmd/flags_windows.go new file mode 100644 index 0000000000..b426acdc1b --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/cmd/flags_windows.go @@ -0,0 +1,6 @@ +package cmd + +const ( + rootDir = `c:\` + execExt = ".exe" +) diff --git a/vendor/github.com/buildpacks/lifecycle/cmd/logs.go b/vendor/github.com/buildpacks/lifecycle/cmd/logs.go new file mode 100644 index 0000000000..f090448b23 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/cmd/logs.go @@ -0,0 +1,87 @@ +package cmd + +import ( + "io" + "os" + "sync" + + "github.com/apex/log" + "github.com/heroku/color" +) + +const ( + errorLevelText = "ERROR: " + warnLevelText = "Warning: " +) + +func init() { + // TODO: uncomment when buildpacks/pack#493 (lifecycle containers with a tty) is implemented + // color.Disable(!terminal.IsTerminal(int(os.Stdout.Fd()))) +} + +var ( + DefaultLogger = &Logger{ + &log.Logger{ + Handler: &handler{ + writer: Stdout, + }, + }, + } + Stdout = color.NewConsole(os.Stdout) + Stderr = color.NewConsole(os.Stderr) + warnStyle = color.New(color.FgYellow, color.Bold).SprintfFunc() + errorStyle = color.New(color.FgRed, color.Bold).SprintfFunc() + phaseStyle = color.New(color.FgCyan).SprintfFunc() +) + +type Logger struct { + *log.Logger +} + +func (l *Logger) Phase(name string) { + l.Infof(phaseStyle("===> %s", name)) +} + +func SetLogLevel(level string) *ErrorFail { + var err error + DefaultLogger.Level, err = log.ParseLevel(level) + if err != nil { + return FailErrCode(err, CodeInvalidArgs, "parse log level") + } + + return nil +} + +func DisableColor(noColor bool) { + Stdout.DisableColors(noColor) + Stderr.DisableColors(noColor) +} + +type handler struct { + mu sync.Mutex + writer io.Writer +} + +func (h *handler) HandleLog(entry *log.Entry) error { + h.mu.Lock() + defer h.mu.Unlock() + + var err error + switch entry.Level { + case log.WarnLevel: + _, err = h.writer.Write([]byte(warnStyle(warnLevelText) + appendMissingLineFeed(entry.Message))) + case log.ErrorLevel: + _, err = h.writer.Write([]byte(errorStyle(errorLevelText) + appendMissingLineFeed(entry.Message))) + default: + _, err = h.writer.Write([]byte(appendMissingLineFeed(entry.Message))) + } + return err +} + +func appendMissingLineFeed(msg string) string { + buff := []byte(msg) + if buff[len(buff)-1] != '\n' { + buff = append(buff, '\n') + } + return string(buff) +} diff --git a/vendor/github.com/buildpacks/lifecycle/cmd/version.go b/vendor/github.com/buildpacks/lifecycle/cmd/version.go new file mode 100644 index 0000000000..a17dcb7020 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/cmd/version.go @@ -0,0 +1,106 @@ +package cmd + +import ( + "fmt" + "strings" + + "github.com/buildpacks/lifecycle/api" +) + +// The following variables are injected at compile time. +var ( + // Version is the version of the lifecycle and all produced binaries. + Version = "0.0.0" + // SCMCommit is the commit information provided by SCM. + SCMCommit = "" + // SCMRepository is the source repository. + SCMRepository = "" + + DeprecationMode = EnvOrDefault(EnvDeprecationMode, DefaultDeprecationMode) +) + +const ( + DeprecationModeQuiet = "quiet" + DeprecationModeWarn = "warn" + DeprecationModeError = "error" +) + +// buildVersion is a display format of the version and build metadata in compliance with semver. +func buildVersion() string { + // noinspection GoBoolExpressions + if SCMCommit == "" || strings.Contains(Version, SCMCommit) { + return Version + } + + return fmt.Sprintf("%s+%s", Version, SCMCommit) +} + +func VerifyPlatformAPI(requested string) error { + requestedAPI, err := api.NewVersion(requested) + if err != nil { + return FailErrCode( + fmt.Errorf("parse platform API '%s'", requested), + CodeIncompatiblePlatformAPI, + ) + } + if api.Platform.IsSupported(requestedAPI) { + if api.Platform.IsDeprecated(requestedAPI) { + switch DeprecationMode { + case DeprecationModeQuiet: + break + case DeprecationModeError: + DefaultLogger.Errorf("Platform requested deprecated API '%s'", requested) + DefaultLogger.Errorf("Deprecated APIs are disable by %s=%s", EnvDeprecationMode, DeprecationModeError) + return platformAPIError(requested) + case DeprecationModeWarn: + DefaultLogger.Warnf("Platform requested deprecated API '%s'", requested) + default: + DefaultLogger.Warnf("Platform requested deprecated API '%s'", requested) + } + } + return nil + } + return platformAPIError(requested) +} + +func VerifyBuildpackAPI(bp string, requested string) error { + requestedAPI, err := api.NewVersion(requested) + if err != nil { + return FailErrCode( + fmt.Errorf("parse buildpack API '%s' for buildpack '%s'", requestedAPI, bp), + CodeIncompatibleBuildpackAPI, + ) + } + if api.Buildpack.IsSupported(requestedAPI) { + if api.Buildpack.IsDeprecated(requestedAPI) { + switch DeprecationMode { + case DeprecationModeQuiet: + break + case DeprecationModeError: + DefaultLogger.Errorf("Buildpack '%s' requests deprecated API '%s'", bp, requested) + DefaultLogger.Errorf("Deprecated APIs are disable by %s=%s", EnvDeprecationMode, DeprecationModeError) + return buildpackAPIError(bp, requested) + case DeprecationModeWarn: + DefaultLogger.Warnf("Buildpack '%s' requests deprecated API '%s'", bp, requested) + default: + DefaultLogger.Warnf("Buildpack '%s' requests deprecated API '%s'", bp, requested) + } + } + return nil + } + return buildpackAPIError(bp, requested) +} + +func platformAPIError(requested string) error { + return FailErrCode( + fmt.Errorf("set platform API: platform API version '%s' is incompatible with the lifecycle", requested), + CodeIncompatiblePlatformAPI, + ) +} + +func buildpackAPIError(bp string, requested string) error { + return FailErrCode( + fmt.Errorf("set API for buildpack '%s': buildpack API version '%s' is incompatible with the lifecycle", bp, requested), + CodeIncompatibleBuildpackAPI, + ) +} diff --git a/vendor/github.com/buildpacks/lifecycle/codecov.yml b/vendor/github.com/buildpacks/lifecycle/codecov.yml new file mode 100644 index 0000000000..d1cc37613e --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/codecov.yml @@ -0,0 +1,18 @@ +codecov: + notify: + after_n_builds: 2 + +coverage: + round: up + status: + project: + default: + threshold: 1% + patch: + default: + threshold: 10% + +comment: + layout: "reach,diff,flags" + require_changes: yes + after_n_builds: 2 diff --git a/vendor/github.com/buildpacks/lifecycle/cosign.pub b/vendor/github.com/buildpacks/lifecycle/cosign.pub new file mode 100644 index 0000000000..32522007e1 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/cosign.pub @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVHkU6XfpwHxNLpqvPOWrGBvwQuN5 +YftzwwYnvdxnu5aoCnkfOpLks+3r+tQQRbcjHsgZr/HjSFFiia5WAwNq4Q== +-----END PUBLIC KEY----- + diff --git a/vendor/github.com/buildpacks/lifecycle/detector.go b/vendor/github.com/buildpacks/lifecycle/detector.go new file mode 100644 index 0000000000..7bc41b3f38 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/detector.go @@ -0,0 +1,415 @@ +package lifecycle + +import ( + "fmt" + "os" + "sync" + + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/buildpack" + "github.com/buildpacks/lifecycle/env" + "github.com/buildpacks/lifecycle/platform" +) + +const ( + CodeDetectPass = 0 + CodeDetectFail = 100 +) + +var ( + ErrFailedDetection = errors.New("no buildpacks participating") + ErrBuildpack = errors.New("buildpack(s) failed with err") +) + +type Resolver interface { + Resolve(done []buildpack.GroupBuildpack, detectRuns *sync.Map) ([]buildpack.GroupBuildpack, []platform.BuildPlanEntry, error) +} + +type Detector struct { + buildpack.DetectConfig + Platform Platform + Resolver Resolver + Runs *sync.Map + Store BuildpackStore +} + +func NewDetector(config buildpack.DetectConfig, buildpacksDir string, platform Platform) (*Detector, error) { + resolver := &DefaultResolver{ + Logger: config.Logger, + } + store, err := buildpack.NewBuildpackStore(buildpacksDir) + if err != nil { + return nil, err + } + return &Detector{ + DetectConfig: config, + Platform: platform, + Resolver: resolver, + Runs: &sync.Map{}, + Store: store, + }, nil +} + +func (d *Detector) Detect(order buildpack.Order) (buildpack.Group, platform.BuildPlan, error) { + return d.DetectOrder(order) +} + +func (d *Detector) DetectOrder(order buildpack.Order) (buildpack.Group, platform.BuildPlan, error) { + bps, entries, err := d.detectOrder(order, nil, nil, false, &sync.WaitGroup{}) + if err == ErrBuildpack { + err = buildpack.NewError(err, buildpack.ErrTypeBuildpack) + } else if err == ErrFailedDetection { + err = buildpack.NewError(err, buildpack.ErrTypeFailedDetection) + } + for i := range entries { + for j := range entries[i].Requires { + entries[i].Requires[j].ConvertVersionToMetadata() + } + } + return buildpack.Group{Group: bps}, platform.BuildPlan{Entries: entries}, err +} + +func (d *Detector) detectOrder(order buildpack.Order, done, next []buildpack.GroupBuildpack, optional bool, wg *sync.WaitGroup) ([]buildpack.GroupBuildpack, []platform.BuildPlanEntry, error) { + ngroup := buildpack.Group{Group: next} + buildpackErr := false + for _, group := range order { + // FIXME: double-check slice safety here + found, plan, err := d.detectGroup(group.Append(ngroup), done, wg) + if err == ErrBuildpack { + buildpackErr = true + } + if err == ErrFailedDetection || err == ErrBuildpack { + wg = &sync.WaitGroup{} + continue + } + return found, plan, err + } + if optional { + return d.detectGroup(ngroup, done, wg) + } + + if buildpackErr { + return nil, nil, ErrBuildpack + } + return nil, nil, ErrFailedDetection +} + +func (d *Detector) detectGroup(group buildpack.Group, done []buildpack.GroupBuildpack, wg *sync.WaitGroup) ([]buildpack.GroupBuildpack, []platform.BuildPlanEntry, error) { + for i, groupBp := range group.Group { + key := groupBp.String() + if hasID(done, groupBp.ID) { + continue + } + + bp, err := d.Store.Lookup(groupBp.ID, groupBp.Version) + if err != nil { + return nil, nil, err + } + + bpDesc := bp.ConfigFile() + groupBp.API = bpDesc.API + groupBp.Homepage = bpDesc.Buildpack.Homepage + + if bpDesc.IsMetaBuildpack() { + // TODO: double-check slice safety here + // FIXME: cyclical references lead to infinite recursion + return d.detectOrder(bpDesc.Order, done, group.Group[i+1:], groupBp.Optional, wg) + } + + bpEnv := env.NewBuildEnv(os.Environ()) + + done = append(done, groupBp) + wg.Add(1) + go func(key string, bp Buildpack) { + if _, ok := d.Runs.Load(key); !ok { + d.Runs.Store(key, bp.Detect(&d.DetectConfig, bpEnv)) + } + wg.Done() + }(key, bp) + } + + wg.Wait() + + return d.Resolver.Resolve(done, d.Runs) +} + +func hasID(bps []buildpack.GroupBuildpack, id string) bool { + for _, bp := range bps { + if bp.ID == id { + return true + } + } + return false +} + +type DefaultResolver struct { + Logger Logger +} + +// Resolve aggregates the detect output for a group of buildpacks and tries to resolve a build plan for the group. +// If any required buildpack in the group failed detection or a build plan cannot be resolved, it returns an error. +func (r *DefaultResolver) Resolve(done []buildpack.GroupBuildpack, detectRuns *sync.Map) ([]buildpack.GroupBuildpack, []platform.BuildPlanEntry, error) { + var groupRuns []buildpack.DetectRun + for _, bp := range done { + t, ok := detectRuns.Load(bp.String()) + if !ok { + return nil, nil, errors.Errorf("missing detection of '%s'", bp) + } + run := t.(buildpack.DetectRun) + outputLogf := r.Logger.Debugf + + switch run.Code { + case CodeDetectPass, CodeDetectFail: + default: + outputLogf = r.Logger.Infof + } + + if len(run.Output) > 0 { + outputLogf("======== Output: %s ========", bp) + outputLogf(string(run.Output)) + } + if run.Err != nil { + outputLogf("======== Error: %s ========", bp) + outputLogf(run.Err.Error()) + } + groupRuns = append(groupRuns, run) + } + + r.Logger.Debugf("======== Results ========") + + results := detectResults{} + detected := true + buildpackErr := false + for i, bp := range done { + run := groupRuns[i] + switch run.Code { + case CodeDetectPass: + r.Logger.Debugf("pass: %s", bp) + results = append(results, detectResult{bp, run}) + case CodeDetectFail: + if bp.Optional { + r.Logger.Debugf("skip: %s", bp) + } else { + r.Logger.Debugf("fail: %s", bp) + } + detected = detected && bp.Optional + case -1: + r.Logger.Infof("err: %s", bp) + buildpackErr = true + detected = detected && bp.Optional + default: + r.Logger.Infof("err: %s (%d)", bp, run.Code) + buildpackErr = true + detected = detected && bp.Optional + } + } + if !detected { + if buildpackErr { + return nil, nil, ErrBuildpack + } + return nil, nil, ErrFailedDetection + } + + i := 0 + deps, trial, err := results.runTrials(func(trial detectTrial) (depMap, detectTrial, error) { + i++ + return r.runTrial(i, trial) + }) + if err != nil { + return nil, nil, err + } + + if len(done) != len(trial) { + r.Logger.Infof("%d of %d buildpacks participating", len(trial), len(done)) + } + + maxLength := 0 + for _, t := range trial { + l := len(t.ID) + if l > maxLength { + maxLength = l + } + } + + f := fmt.Sprintf("%%-%ds %%s", maxLength) + + for _, t := range trial { + r.Logger.Infof(f, t.ID, t.Version) + } + + var found []buildpack.GroupBuildpack + for _, r := range trial { + found = append(found, r.GroupBuildpack.NoOpt()) + } + var plan []platform.BuildPlanEntry + for _, dep := range deps { + plan = append(plan, dep.BuildPlanEntry.NoOpt()) + } + return found, plan, nil +} + +func (r *DefaultResolver) runTrial(i int, trial detectTrial) (depMap, detectTrial, error) { + r.Logger.Debugf("Resolving plan... (try #%d)", i) + + var deps depMap + retry := true + for retry { + retry = false + deps = newDepMap(trial) + + if err := deps.eachUnmetRequire(func(name string, bp buildpack.GroupBuildpack) error { + retry = true + if !bp.Optional { + r.Logger.Debugf("fail: %s requires %s", bp, name) + return ErrFailedDetection + } + r.Logger.Debugf("skip: %s requires %s", bp, name) + trial = trial.remove(bp) + return nil + }); err != nil { + return nil, nil, err + } + + if err := deps.eachUnmetProvide(func(name string, bp buildpack.GroupBuildpack) error { + retry = true + if !bp.Optional { + r.Logger.Debugf("fail: %s provides unused %s", bp, name) + return ErrFailedDetection + } + r.Logger.Debugf("skip: %s provides unused %s", bp, name) + trial = trial.remove(bp) + return nil + }); err != nil { + return nil, nil, err + } + } + + if len(trial) == 0 { + r.Logger.Debugf("fail: no viable buildpacks in group") + return nil, nil, ErrFailedDetection + } + return deps, trial, nil +} + +type detectResult struct { + buildpack.GroupBuildpack + buildpack.DetectRun +} + +func (r *detectResult) options() []detectOption { + var out []detectOption + for i, sections := range append([]buildpack.PlanSections{r.PlanSections}, r.Or...) { + bp := r.GroupBuildpack + bp.Optional = bp.Optional && i == len(r.Or) + out = append(out, detectOption{bp, sections}) + } + return out +} + +type detectResults []detectResult +type trialFunc func(detectTrial) (depMap, detectTrial, error) + +func (rs detectResults) runTrials(f trialFunc) (depMap, detectTrial, error) { + return rs.runTrialsFrom(nil, f) +} + +func (rs detectResults) runTrialsFrom(prefix detectTrial, f trialFunc) (depMap, detectTrial, error) { + if len(rs) == 0 { + deps, trial, err := f(prefix) + return deps, trial, err + } + + var lastErr error + for _, option := range rs[0].options() { + deps, trial, err := rs[1:].runTrialsFrom(append(prefix, option), f) + if err == nil { + return deps, trial, nil + } + lastErr = err + } + return nil, nil, lastErr +} + +type detectOption struct { + buildpack.GroupBuildpack + buildpack.PlanSections +} + +type detectTrial []detectOption + +func (ts detectTrial) remove(bp buildpack.GroupBuildpack) detectTrial { + var out detectTrial + for _, t := range ts { + if t.GroupBuildpack != bp { + out = append(out, t) + } + } + return out +} + +type depEntry struct { + platform.BuildPlanEntry + earlyRequires []buildpack.GroupBuildpack + extraProvides []buildpack.GroupBuildpack +} + +type depMap map[string]depEntry + +func newDepMap(trial detectTrial) depMap { + m := depMap{} + for _, option := range trial { + for _, p := range option.Provides { + m.provide(option.GroupBuildpack, p) + } + for _, r := range option.Requires { + m.require(option.GroupBuildpack, r) + } + } + return m +} + +func (m depMap) provide(bp buildpack.GroupBuildpack, provide buildpack.Provide) { + entry := m[provide.Name] + entry.extraProvides = append(entry.extraProvides, bp) + m[provide.Name] = entry +} + +func (m depMap) require(bp buildpack.GroupBuildpack, require buildpack.Require) { + entry := m[require.Name] + entry.Providers = append(entry.Providers, entry.extraProvides...) + entry.extraProvides = nil + + if len(entry.Providers) == 0 { + entry.earlyRequires = append(entry.earlyRequires, bp) + } else { + entry.Requires = append(entry.Requires, require) + } + m[require.Name] = entry +} + +func (m depMap) eachUnmetProvide(f func(name string, bp buildpack.GroupBuildpack) error) error { + for name, entry := range m { + if len(entry.extraProvides) != 0 { + for _, bp := range entry.extraProvides { + if err := f(name, bp); err != nil { + return err + } + } + } + } + return nil +} + +func (m depMap) eachUnmetRequire(f func(name string, bp buildpack.GroupBuildpack) error) error { + for name, entry := range m { + if len(entry.earlyRequires) != 0 { + for _, bp := range entry.earlyRequires { + if err := f(name, bp); err != nil { + return err + } + } + } + } + return nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/env/build.go b/vendor/github.com/buildpacks/lifecycle/env/build.go new file mode 100644 index 0000000000..efa26795f1 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/env/build.go @@ -0,0 +1,79 @@ +package env + +import ( + "runtime" + "strings" +) + +var BuildEnvIncludelist = []string{ + "CNB_STACK_ID", + "HOSTNAME", + "HOME", + "HTTPS_PROXY", + "https_proxy", + "HTTP_PROXY", + "http_proxy", + "NO_PROXY", + "no_proxy", +} + +var ignoreEnvVarCase = runtime.GOOS == "windows" + +// NewBuildEnv returns a build-time Env from the given environment. +// +// Keys in the BuildEnvIncludelist will be added to the Environment. +func NewBuildEnv(environ []string) *Env { + envFilter := isNotMember(BuildEnvIncludelist, flattenMap(POSIXBuildEnv)) + + return &Env{ + RootDirMap: POSIXBuildEnv, + Vars: varsFromEnv(environ, ignoreEnvVarCase, envFilter), + } +} + +func matches(k1, k2 string) bool { + if ignoreEnvVarCase { + k1 = strings.ToUpper(k1) + k2 = strings.ToUpper(k2) + } + return k1 == k2 +} + +var POSIXBuildEnv = map[string][]string{ + "bin": { + "PATH", + }, + "lib": { + "LD_LIBRARY_PATH", + "LIBRARY_PATH", + }, + "include": { + "CPATH", + }, + "pkgconfig": { + "PKG_CONFIG_PATH", + }, +} + +func isNotMember(lists ...[]string) func(string) bool { + return func(key string) bool { + for _, list := range lists { + for _, wk := range list { + if matches(wk, key) { + // keep in env + return false + } + } + } + return true + } +} + +func flattenMap(m map[string][]string) []string { + result := make([]string, 0) + for _, subList := range m { + result = append(result, subList...) + } + + return result +} diff --git a/vendor/github.com/buildpacks/lifecycle/env/env.go b/vendor/github.com/buildpacks/lifecycle/env/env.go new file mode 100644 index 0000000000..ff99551d2f --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/env/env.go @@ -0,0 +1,195 @@ +package env + +import ( + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/api" +) + +// Env is used to modify and return environment variables +type Env struct { + // RootDirMap maps directories in a posix root filesystem to a slice of environment variables that + RootDirMap map[string][]string + Vars *Vars +} + +// AddRootDir modifies the environment given a root dir. If the root dir contains a directory that matches a key in +// the Env RooDirMap, the absolute path to the keyed directory will be prepended to all the associated environment variables +// using the OS path list separator as a delimiter. +func (p *Env) AddRootDir(dir string) error { + absDir, err := filepath.Abs(dir) + if err != nil { + return err + } + for dir, vars := range p.RootDirMap { + childDir := filepath.Join(absDir, dir) + if _, err := os.Stat(childDir); os.IsNotExist(err) { + continue + } else if err != nil { + return err + } + for _, key := range vars { + p.Vars.Set(key, childDir+prefix(p.Vars.Get(key), os.PathListSeparator)) + } + } + return nil +} + +func (p *Env) isRootEnv(name string) bool { + for _, m := range p.RootDirMap { + for _, k := range m { + if k == name { + return true + } + } + } + return false +} + +type ActionType string + +const ( + ActionTypePrepend ActionType = "prepend" + ActionTypeAppend ActionType = "append" + ActionTypeOverride ActionType = "override" + ActionTypeDefault ActionType = "default" + ActionTypePrependPath ActionType = "" +) + +// DefaultActionType returns the default action to perform for an unsuffixed env file as specified for the given +// buildpack API +func DefaultActionType(bpAPI *api.Version) ActionType { + if bpAPI != nil && bpAPI.LessThan("0.5") { + return ActionTypePrependPath + } + return ActionTypeOverride +} + +// AddEnvDir modified the Env given a directory containing env files. For each file in the envDir, if the file has +// a period delimited suffix, the action matching the given suffix will be performed. If the file has no suffix, +// the default action will be performed. If the suffix does not match a known type, AddEnvDir will ignore the file. +func (p *Env) AddEnvDir(envDir string, defaultAction ActionType) error { + if err := eachEnvFile(envDir, func(k, v string) error { + parts := strings.SplitN(k, ".", 2) + name := parts[0] + var action ActionType + if len(parts) > 1 { + action = ActionType(parts[1]) + } else { + action = defaultAction + } + switch action { + case ActionTypePrepend: + p.Vars.Set(name, v+prefix(p.Vars.Get(name), delim(envDir, name)...)) + case ActionTypeAppend: + p.Vars.Set(name, suffix(p.Vars.Get(name), delim(envDir, name)...)+v) + case ActionTypeOverride: + p.Vars.Set(name, v) + case ActionTypeDefault: + if p.Vars.Get(name) != "" { + return nil + } + p.Vars.Set(name, v) + case ActionTypePrependPath: + p.Vars.Set(name, v+prefix(p.Vars.Get(name), delim(envDir, name, os.PathListSeparator)...)) + } + return nil + }); err != nil { + return errors.Wrapf(err, "apply env files from dir '%s'", envDir) + } + return nil +} + +// Set sets the environment variable with the given name to the given value. +func (p *Env) Set(name, v string) { + p.Vars.Set(name, v) +} + +// WithPlatform returns the environment after applying modifications from the given platform dir. +// For each file in the platformDir, if the name of the file does not match an environment variable name in the +// RootDirMap, the given variable will be set to the contents of the file. If the name does match an environment +// variable name in the RootDirMap, the contents of the file will be prepended to the environment variable value +// using the OS path list separator as a delimiter. +func (p *Env) WithPlatform(platformDir string) (out []string, err error) { + vars := NewVars(p.Vars.vals, p.Vars.ignoreCase) + + if err := eachEnvFile(filepath.Join(platformDir, "env"), func(k, v string) error { + if p.isRootEnv(k) { + vars.Set(k, v+prefix(vars.Get(k), os.PathListSeparator)) + return nil + } + vars.Set(k, v) + return nil + }); err != nil { + return nil, err + } + return vars.List(), nil +} + +func prefix(s string, prefix ...byte) string { + if s == "" { + return "" + } + return string(prefix) + s +} + +func suffix(s string, suffix ...byte) string { + if s == "" { + return "" + } + return s + string(suffix) +} + +func delim(dir, name string, def ...byte) []byte { + value, err := ioutil.ReadFile(filepath.Join(dir, name+".delim")) + if err != nil { + return def + } + return value +} + +func eachEnvFile(dir string, fn func(k, v string) error) error { + files, err := ioutil.ReadDir(dir) + if os.IsNotExist(err) { + return nil + } else if err != nil { + return err + } + for _, f := range files { + if f.IsDir() { + continue + } + if f.Mode()&os.ModeSymlink != 0 { + lnFile, err := os.Stat(filepath.Join(dir, f.Name())) + if err != nil { + return err + } + if lnFile.IsDir() { + continue + } + } + value, err := ioutil.ReadFile(filepath.Join(dir, f.Name())) + if err != nil { + return err + } + if err := fn(f.Name(), string(value)); err != nil { + return err + } + } + return nil +} + +// List returns the environment +func (p *Env) List() []string { + return p.Vars.List() +} + +// Get returns the value for the given key +func (p *Env) Get(k string) string { + return p.Vars.Get(k) +} diff --git a/vendor/github.com/buildpacks/lifecycle/env/launch.go b/vendor/github.com/buildpacks/lifecycle/env/launch.go new file mode 100644 index 0000000000..c4d255acc2 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/env/launch.go @@ -0,0 +1,51 @@ +package env + +import ( + "os" + "strings" +) + +var LaunchEnvExcludelist = []string{ + "CNB_LAYERS_DIR", + "CNB_APP_DIR", + "CNB_PROCESS_TYPE", + "CNB_PLATFORM_API", + "CNB_DEPRECATION_MODE", +} + +// NewLaunchEnv returns an Env for process launch from the given environ. +// +// Keys in the LaunchEnvExcludelist shall be removed. +// processDir will be removed from the beginning of PATH if present. +func NewLaunchEnv(environ []string, processDir string, lifecycleDir string) *Env { + vars := varsFromEnv(environ, ignoreEnvVarCase, isExcluded) + if path, ok := vars.vals["PATH"]; ok { + parts := strings.Split(path, string(os.PathListSeparator)) + var stripped []string + for _, part := range parts { + if part == processDir || part == lifecycleDir { + continue + } + stripped = append(stripped, part) + } + vars.vals["PATH"] = strings.Join(stripped, string(os.PathListSeparator)) + } + return &Env{ + RootDirMap: POSIXLaunchEnv, + Vars: vars, + } +} + +func isExcluded(k string) bool { + for _, wk := range LaunchEnvExcludelist { + if matches(wk, k) { + return true + } + } + return false +} + +var POSIXLaunchEnv = map[string][]string{ + "bin": {"PATH"}, + "lib": {"LD_LIBRARY_PATH"}, +} diff --git a/vendor/github.com/buildpacks/lifecycle/env/vars.go b/vendor/github.com/buildpacks/lifecycle/env/vars.go new file mode 100644 index 0000000000..d3ebac2277 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/env/vars.go @@ -0,0 +1,59 @@ +package env + +import ( + "strings" +) + +type Vars struct { + vals map[string]string + ignoreCase bool +} + +func varsFromEnv(env []string, ignoreCase bool, removeKey func(string) bool) *Vars { + vars := NewVars(nil, ignoreCase) + for _, kv := range env { + parts := strings.SplitN(kv, "=", 2) + if len(parts) != 2 { + continue + } + if removeKey(parts[0]) { + continue + } + vars.Set(parts[0], parts[1]) + } + return vars +} + +func NewVars(vars map[string]string, ignoreCase bool) *Vars { + s := &Vars{ + vals: map[string]string{}, + ignoreCase: ignoreCase, + } + for k, v := range vars { + s.Set(k, v) + } + return s +} + +func (s *Vars) Get(key string) string { + return s.vals[s.key(key)] +} + +func (s *Vars) Set(key, value string) { + s.vals[s.key(key)] = value +} + +func (s *Vars) key(k string) string { + if s.ignoreCase { + return strings.ToUpper(k) + } + return k +} + +func (s *Vars) List() []string { + var result []string + for k, v := range s.vals { + result = append(result, k+"="+v) + } + return result +} diff --git a/vendor/github.com/buildpacks/lifecycle/exporter.go b/vendor/github.com/buildpacks/lifecycle/exporter.go new file mode 100644 index 0000000000..e75db1e39a --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/exporter.go @@ -0,0 +1,506 @@ +package lifecycle + +import ( + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "github.com/BurntSushi/toml" + "github.com/buildpacks/imgutil" + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/api" + "github.com/buildpacks/lifecycle/buildpack" + "github.com/buildpacks/lifecycle/cmd" + "github.com/buildpacks/lifecycle/launch" + "github.com/buildpacks/lifecycle/layers" + "github.com/buildpacks/lifecycle/platform" +) + +type Cache interface { + Exists() bool + Name() string + SetMetadata(metadata platform.CacheMetadata) error + RetrieveMetadata() (platform.CacheMetadata, error) + AddLayerFile(tarPath string, sha string) error + ReuseLayer(sha string) error + RetrieveLayer(sha string) (io.ReadCloser, error) + Commit() error +} + +type Exporter struct { + Buildpacks []buildpack.GroupBuildpack + LayerFactory LayerFactory + Logger Logger + PlatformAPI *api.Version +} + +//go:generate mockgen -package testmock -destination testmock/layer_factory.go github.com/buildpacks/lifecycle LayerFactory +type LayerFactory interface { + DirLayer(id string, dir string) (layers.Layer, error) + LauncherLayer(path string) (layers.Layer, error) + ProcessTypesLayer(metadata launch.Metadata) (layers.Layer, error) + SliceLayers(dir string, slices []layers.Slice) ([]layers.Layer, error) +} + +type LauncherConfig struct { + Path string + Metadata platform.LauncherMetadata +} + +type ExportOptions struct { + LayersDir string + AppDir string + WorkingImage imgutil.Image + RunImageRef string + OrigMetadata platform.LayersMetadata + AdditionalNames []string + LauncherConfig LauncherConfig + Stack platform.StackMetadata + Project platform.ProjectMetadata + DefaultProcessType string +} + +func (e *Exporter) Export(opts ExportOptions) (platform.ExportReport, error) { + var err error + + opts.LayersDir, err = filepath.Abs(opts.LayersDir) + if err != nil { + return platform.ExportReport{}, errors.Wrapf(err, "layers dir absolute path") + } + + opts.AppDir, err = filepath.Abs(opts.AppDir) + if err != nil { + return platform.ExportReport{}, errors.Wrapf(err, "app dir absolute path") + } + + meta := platform.LayersMetadata{} + meta.RunImage.TopLayer, err = opts.WorkingImage.TopLayer() + if err != nil { + return platform.ExportReport{}, errors.Wrap(err, "get run image top layer SHA") + } + + meta.RunImage.Reference = opts.RunImageRef + meta.Stack = opts.Stack + + buildMD := &platform.BuildMetadata{} + if _, err := toml.DecodeFile(launch.GetMetadataFilePath(opts.LayersDir), buildMD); err != nil { + return platform.ExportReport{}, errors.Wrap(err, "read build metadata") + } + + // buildpack-provided layers + if err := e.addBuildpackLayers(opts, &meta); err != nil { + return platform.ExportReport{}, err + } + + if e.PlatformAPI.AtLeast("0.8") { + if err := e.addSBOMLaunchLayer(opts, &meta); err != nil { + return platform.ExportReport{}, err + } + } + + // app layers (split into 1 or more slices) + if err := e.addAppLayers(opts, buildMD.Slices, &meta); err != nil { + return platform.ExportReport{}, errors.Wrap(err, "exporting app layers") + } + + // launcher layers (launcher binary, launcher config, process symlinks) + if err := e.addLauncherLayers(opts, buildMD, &meta); err != nil { + return platform.ExportReport{}, err + } + + if err := e.setLabels(opts, meta, buildMD); err != nil { + return platform.ExportReport{}, err + } + + if err := e.setEnv(opts, buildMD.ToLaunchMD()); err != nil { + return platform.ExportReport{}, err + } + + if e.PlatformAPI.AtLeast("0.6") { + e.Logger.Debugf("Setting WORKDIR: '%s'", opts.AppDir) + if err := e.setWorkingDir(opts); err != nil { + return platform.ExportReport{}, errors.Wrap(err, "setting workdir") + } + } + + entrypoint, err := e.entrypoint(buildMD.ToLaunchMD(), opts.DefaultProcessType, buildMD.BuildpackDefaultProcessType) + if err != nil { + return platform.ExportReport{}, errors.Wrap(err, "determining entrypoint") + } + e.Logger.Debugf("Setting ENTRYPOINT: '%s'", entrypoint) + if err = opts.WorkingImage.SetEntrypoint(entrypoint); err != nil { + return platform.ExportReport{}, errors.Wrap(err, "setting entrypoint") + } + + if err = opts.WorkingImage.SetCmd(); err != nil { // Note: Command intentionally empty + return platform.ExportReport{}, errors.Wrap(err, "setting cmd") + } + + report := platform.ExportReport{} + report.Build, err = e.makeBuildReport(opts.LayersDir) + if err != nil { + return platform.ExportReport{}, err + } + report.Image, err = saveImage(opts.WorkingImage, opts.AdditionalNames, e.Logger) + if err != nil { + return platform.ExportReport{}, err + } + if !e.supportsManifestSize() { + // unset manifest size in report.toml for old platform API versions + report.Image.ManifestSize = 0 + } + + return report, nil +} + +func (e *Exporter) addBuildpackLayers(opts ExportOptions, meta *platform.LayersMetadata) error { + for _, bp := range e.Buildpacks { + bpDir, err := buildpack.ReadLayersDir(opts.LayersDir, bp, e.Logger) + e.Logger.Debugf("Processing buildpack directory: %s", bpDir.Path) + if err != nil { + return errors.Wrapf(err, "reading layers for buildpack '%s'", bp.ID) + } + bpMD := buildpack.LayersMetadata{ + ID: bp.ID, + Version: bp.Version, + Layers: map[string]buildpack.LayerMetadata{}, + Store: bpDir.Store, + } + for _, fsLayer := range bpDir.FindLayers(buildpack.MadeLaunch) { + fsLayer := fsLayer + e.Logger.Debugf("Processing launch layer: %s", fsLayer.Path()) + lmd, err := fsLayer.Read() + if err != nil { + return errors.Wrapf(err, "reading '%s' metadata", fsLayer.Identifier()) + } + + if fsLayer.HasLocalContents() { + layer, err := e.LayerFactory.DirLayer(fsLayer.Identifier(), fsLayer.Path()) + if err != nil { + return errors.Wrapf(err, "creating layer") + } + origLayerMetadata := opts.OrigMetadata.MetadataForBuildpack(bp.ID).Layers[fsLayer.Name()] + lmd.SHA, err = e.addOrReuseLayer(opts.WorkingImage, layer, origLayerMetadata.SHA) + if err != nil { + return err + } + } else { + if lmd.Cache { + return fmt.Errorf("layer '%s' is cache=true but has no contents", fsLayer.Identifier()) + } + origLayerMetadata, ok := opts.OrigMetadata.MetadataForBuildpack(bp.ID).Layers[fsLayer.Name()] + if !ok { + return fmt.Errorf("cannot reuse '%s', previous image has no metadata for layer '%s'", fsLayer.Identifier(), fsLayer.Identifier()) + } + + e.Logger.Infof("Reusing layer '%s'\n", fsLayer.Identifier()) + e.Logger.Debugf("Layer '%s' SHA: %s\n", fsLayer.Identifier(), origLayerMetadata.SHA) + if err := opts.WorkingImage.ReuseLayer(origLayerMetadata.SHA); err != nil { + return errors.Wrapf(err, "reusing layer: '%s'", fsLayer.Identifier()) + } + lmd.SHA = origLayerMetadata.SHA + } + bpMD.Layers[fsLayer.Name()] = lmd + } + meta.Buildpacks = append(meta.Buildpacks, bpMD) + + if malformedLayers := bpDir.FindLayers(buildpack.Malformed); len(malformedLayers) > 0 { + ids := make([]string, 0, len(malformedLayers)) + for _, ml := range malformedLayers { + ids = append(ids, ml.Identifier()) + } + return fmt.Errorf("failed to parse metadata for layers '%s'", ids) + } + } + return nil +} + +func (e *Exporter) addLauncherLayers(opts ExportOptions, buildMD *platform.BuildMetadata, meta *platform.LayersMetadata) error { + launcherLayer, err := e.LayerFactory.LauncherLayer(opts.LauncherConfig.Path) + if err != nil { + return errors.Wrap(err, "creating launcher layers") + } + meta.Launcher.SHA, err = e.addOrReuseLayer(opts.WorkingImage, launcherLayer, opts.OrigMetadata.Launcher.SHA) + if err != nil { + return errors.Wrap(err, "exporting launcher configLayer") + } + configLayer, err := e.LayerFactory.DirLayer("config", filepath.Join(opts.LayersDir, "config")) + if err != nil { + return errors.Wrapf(err, "creating layer '%s'", configLayer.ID) + } + meta.Config.SHA, err = e.addOrReuseLayer(opts.WorkingImage, configLayer, opts.OrigMetadata.Config.SHA) + if err != nil { + return errors.Wrap(err, "exporting config layer") + } + + if err := e.launcherConfig(opts, buildMD, meta); err != nil { + return err + } + return nil +} + +func (e *Exporter) addAppLayers(opts ExportOptions, slices []layers.Slice, meta *platform.LayersMetadata) error { + // creating app layers (slices + app dir) + sliceLayers, err := e.LayerFactory.SliceLayers(opts.AppDir, slices) + if err != nil { + return errors.Wrap(err, "creating app layers") + } + + var numberOfReusedLayers int + for _, slice := range sliceLayers { + var err error + + found := false + for _, previous := range opts.OrigMetadata.App { + if slice.Digest == previous.SHA { + found = true + break + } + } + if found { + err = opts.WorkingImage.ReuseLayer(slice.Digest) + numberOfReusedLayers++ + } else { + err = opts.WorkingImage.AddLayerWithDiffID(slice.TarPath, slice.Digest) + } + if err != nil { + return err + } + e.Logger.Debugf("Layer '%s' SHA: %s\n", slice.ID, slice.Digest) + meta.App = append(meta.App, platform.LayerMetadata{SHA: slice.Digest}) + } + + delta := len(sliceLayers) - numberOfReusedLayers + if numberOfReusedLayers > 0 { + e.Logger.Infof("Reusing %d/%d app layer(s)\n", numberOfReusedLayers, len(sliceLayers)) + } + if delta != 0 { + e.Logger.Infof("Adding %d/%d app layer(s)\n", delta, len(sliceLayers)) + } + return nil +} + +func (e *Exporter) setLabels(opts ExportOptions, meta platform.LayersMetadata, buildMD *platform.BuildMetadata) error { + data, err := json.Marshal(meta) + if err != nil { + return errors.Wrap(err, "marshall metadata") + } + + e.Logger.Infof("Adding label '%s'", platform.LayerMetadataLabel) + if err = opts.WorkingImage.SetLabel(platform.LayerMetadataLabel, string(data)); err != nil { + return errors.Wrap(err, "set app image metadata label") + } + + buildMD.Launcher = opts.LauncherConfig.Metadata + buildJSON, err := json.Marshal(buildMD) + if err != nil { + return errors.Wrap(err, "parse build metadata") + } + + e.Logger.Infof("Adding label '%s'", platform.BuildMetadataLabel) + if err := opts.WorkingImage.SetLabel(platform.BuildMetadataLabel, string(buildJSON)); err != nil { + return errors.Wrap(err, "set build image metadata label") + } + + projectJSON, err := json.Marshal(opts.Project) + if err != nil { + return errors.Wrap(err, "parse project metadata") + } + + e.Logger.Infof("Adding label '%s'", platform.ProjectMetadataLabel) + if err := opts.WorkingImage.SetLabel(platform.ProjectMetadataLabel, string(projectJSON)); err != nil { + return errors.Wrap(err, "set project metadata label") + } + + for _, label := range buildMD.Labels { + e.Logger.Infof("Adding label '%s'", label.Key) + if err := opts.WorkingImage.SetLabel(label.Key, label.Value); err != nil { + return errors.Wrapf(err, "set buildpack-provided label '%s'", label.Key) + } + } + return nil +} + +func (e *Exporter) setEnv(opts ExportOptions, launchMD launch.Metadata) error { + e.Logger.Debugf("Setting %s=%s", cmd.EnvLayersDir, opts.LayersDir) + if err := opts.WorkingImage.SetEnv(cmd.EnvLayersDir, opts.LayersDir); err != nil { + return errors.Wrapf(err, "set app image env %s", cmd.EnvLayersDir) + } + + e.Logger.Debugf("Setting %s=%s", cmd.EnvAppDir, opts.AppDir) + if err := opts.WorkingImage.SetEnv(cmd.EnvAppDir, opts.AppDir); err != nil { + return errors.Wrapf(err, "set app image env %s", cmd.EnvAppDir) + } + + e.Logger.Debugf("Setting %s=%s", cmd.EnvPlatformAPI, e.PlatformAPI.String()) + if err := opts.WorkingImage.SetEnv(cmd.EnvPlatformAPI, e.PlatformAPI.String()); err != nil { + return errors.Wrapf(err, "set app image env %s", cmd.EnvAppDir) + } + + e.Logger.Debugf("Setting %s=%s", cmd.EnvDeprecationMode, cmd.DeprecationModeQuiet) + if err := opts.WorkingImage.SetEnv(cmd.EnvDeprecationMode, cmd.DeprecationModeQuiet); err != nil { + return errors.Wrapf(err, "set app image env %s", cmd.EnvAppDir) + } + + if e.supportsMulticallLauncher() { + path, err := opts.WorkingImage.Env("PATH") + if err != nil { + return errors.Wrap(err, "failed to get PATH from app image") + } + path = strings.Join([]string{launch.ProcessDir, launch.LifecycleDir, path}, string(os.PathListSeparator)) + e.Logger.Debugf("Prepending %s and %s to PATH", launch.ProcessDir, launch.LifecycleDir) + if err := opts.WorkingImage.SetEnv("PATH", path); err != nil { + return errors.Wrap(err, "set app image env PATH") + } + } else if opts.DefaultProcessType != "" { + if _, ok := launchMD.FindProcessType(opts.DefaultProcessType); !ok { + return processTypeError(launchMD, opts.DefaultProcessType) + } + e.Logger.Debugf("Setting %s=%s", cmd.EnvProcessType, opts.DefaultProcessType) + if err := opts.WorkingImage.SetEnv(cmd.EnvProcessType, opts.DefaultProcessType); err != nil { + return errors.Wrapf(err, "set app image env %s", cmd.EnvProcessType) + } + } + return nil +} + +func (e *Exporter) setWorkingDir(opts ExportOptions) error { + return opts.WorkingImage.SetWorkingDir(opts.AppDir) +} + +func (e *Exporter) entrypoint(launchMD launch.Metadata, userDefaultProcessType, buildpackDefaultProcessType string) (string, error) { + if !e.supportsMulticallLauncher() { + return launch.LauncherPath, nil + } + + if userDefaultProcessType == "" && e.PlatformAPI.LessThan("0.6") && len(launchMD.Processes) == 1 { + // if there is only one process, we set it to the default for platform API < 0.6 + e.Logger.Infof("Setting default process type '%s'", launchMD.Processes[0].Type) + return launch.ProcessPath(launchMD.Processes[0].Type), nil + } + + if userDefaultProcessType != "" { + defaultProcess, ok := launchMD.FindProcessType(userDefaultProcessType) + if !ok { + if e.PlatformAPI.LessThan("0.6") { + e.Logger.Warn(processTypeWarning(launchMD, userDefaultProcessType)) + return launch.LauncherPath, nil + } + return "", fmt.Errorf("tried to set %s to default but it doesn't exist", userDefaultProcessType) + } + e.Logger.Infof("Setting default process type '%s'", defaultProcess.Type) + return launch.ProcessPath(defaultProcess.Type), nil + } + if buildpackDefaultProcessType == "" { + e.Logger.Info("no default process type") + return launch.LauncherPath, nil + } + e.Logger.Infof("Setting default process type '%s'", buildpackDefaultProcessType) + return launch.ProcessPath(buildpackDefaultProcessType), nil +} + +// processTypes adds +func (e *Exporter) launcherConfig(opts ExportOptions, buildMD *platform.BuildMetadata, meta *platform.LayersMetadata) error { + if e.supportsMulticallLauncher() { + launchMD := launch.Metadata{ + Processes: buildMD.Processes, + } + if len(buildMD.Processes) > 0 { + processTypesLayer, err := e.LayerFactory.ProcessTypesLayer(launchMD) + if err != nil { + return errors.Wrapf(err, "creating layer '%s'", processTypesLayer.ID) + } + meta.ProcessTypes.SHA, err = e.addOrReuseLayer(opts.WorkingImage, processTypesLayer, opts.OrigMetadata.ProcessTypes.SHA) + if err != nil { + return errors.Wrapf(err, "exporting layer '%s'", processTypesLayer.ID) + } + } + } + return nil +} + +func (e *Exporter) supportsMulticallLauncher() bool { + return e.PlatformAPI.AtLeast("0.4") +} + +func (e *Exporter) supportsManifestSize() bool { + return e.PlatformAPI.AtLeast("0.6") +} + +func processTypeError(launchMD launch.Metadata, defaultProcessType string) error { + return fmt.Errorf(processTypeWarning(launchMD, defaultProcessType)) +} + +func processTypeWarning(launchMD launch.Metadata, defaultProcessType string) string { + var typeList []string + for _, p := range launchMD.Processes { + typeList = append(typeList, p.Type) + } + return fmt.Sprintf("default process type '%s' not present in list %+v", defaultProcessType, typeList) +} + +func (e *Exporter) addOrReuseLayer(image imgutil.Image, layer layers.Layer, previousSHA string) (string, error) { + layer, err := e.LayerFactory.DirLayer(layer.ID, layer.TarPath) + if err != nil { + return "", errors.Wrapf(err, "creating layer '%s'", layer.ID) + } + if layer.Digest == previousSHA { + e.Logger.Infof("Reusing layer '%s'\n", layer.ID) + e.Logger.Debugf("Layer '%s' SHA: %s\n", layer.ID, layer.Digest) + return layer.Digest, image.ReuseLayer(previousSHA) + } + e.Logger.Infof("Adding layer '%s'\n", layer.ID) + e.Logger.Debugf("Layer '%s' SHA: %s\n", layer.ID, layer.Digest) + return layer.Digest, image.AddLayerWithDiffID(layer.TarPath, layer.Digest) +} + +func (e *Exporter) makeBuildReport(layersDir string) (platform.BuildReport, error) { + if e.PlatformAPI.LessThan("0.5") { + return platform.BuildReport{}, nil + } + var out []buildpack.BOMEntry + for _, bp := range e.Buildpacks { + if api.MustParse(bp.API).LessThan("0.5") { + continue + } + var bpBuildReport platform.BuildReport + bpBuildTOML := filepath.Join(layersDir, launch.EscapeID(bp.ID), "build.toml") + if _, err := toml.DecodeFile(bpBuildTOML, &bpBuildReport); err != nil && !os.IsNotExist(err) { + return platform.BuildReport{}, err + } + out = append(out, buildpack.WithBuildpack(bp, bpBuildReport.BOM)...) + } + return platform.BuildReport{BOM: out}, nil +} + +func (e *Exporter) addSBOMLaunchLayer(opts ExportOptions, meta *platform.LayersMetadata) error { + sbomLaunchDir, err := readLayersSBOM(opts.LayersDir, "launch", e.Logger) + if err != nil { + return errors.Wrap(err, "failed to read layers config sbom") + } + + if sbomLaunchDir != nil { + layer, err := e.LayerFactory.DirLayer(sbomLaunchDir.Identifier(), sbomLaunchDir.Path()) + if err != nil { + return errors.Wrapf(err, "creating layer") + } + + var originalSHA string + if opts.OrigMetadata.BOM != nil { + originalSHA = opts.OrigMetadata.BOM.SHA + } + + sha, err := e.addOrReuseLayer(opts.WorkingImage, layer, originalSHA) + if err != nil { + return errors.Wrapf(err, "exporting layer '%s'", layer.ID) + } + + meta.BOM = &platform.LayerMetadata{SHA: sha} + } + + return nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/go.mod b/vendor/github.com/buildpacks/lifecycle/go.mod new file mode 100644 index 0000000000..6c6101cd3a --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/go.mod @@ -0,0 +1,18 @@ +module github.com/buildpacks/lifecycle + +require ( + github.com/BurntSushi/toml v1.0.0 + github.com/apex/log v1.9.0 + github.com/buildpacks/imgutil v0.0.0-20211203200417-76206845baac + github.com/docker/docker v20.10.12+incompatible + github.com/golang/mock v1.6.0 + github.com/google/go-cmp v0.5.7 + github.com/google/go-containerregistry v0.8.0 + github.com/heroku/color v0.0.6 + github.com/pkg/errors v0.9.1 + github.com/sclevine/spec v1.4.0 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e +) + +go 1.16 diff --git a/vendor/github.com/buildpacks/lifecycle/go.sum b/vendor/github.com/buildpacks/lifecycle/go.sum new file mode 100644 index 0000000000..c3d8382521 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/go.sum @@ -0,0 +1,1430 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= +github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= +github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= +github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= +github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= +github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= +github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= +github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= +github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= +github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0= +github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA= +github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= +github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= +github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= +github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/buildpacks/imgutil v0.0.0-20211203200417-76206845baac h1:XrKr6axRUBHEQdyyo7uffYDwWurOdeyH8MpNRJuBdIw= +github.com/buildpacks/imgutil v0.0.0-20211203200417-76206845baac/go.mod h1:YZReWjuSxwyvuN92Vlcul+WgaCXylpecgFn7T3rNang= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= +github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= +github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= +github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= +github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= +github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= +github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= +github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= +github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= +github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= +github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= +github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= +github.com/containerd/containerd v1.5.8 h1:NmkCC1/QxyZFBny8JogwLpOy2f+VEbO/f6bV2Mqtwuw= +github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= +github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= +github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= +github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= +github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= +github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= +github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= +github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= +github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= +github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= +github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= +github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/stargz-snapshotter/estargz v0.10.0/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= +github.com/containerd/stargz-snapshotter/estargz v0.10.1 h1:hd1EoVjI2Ax8Cr64tdYqnJ4i4pZU49FkEf5kU8KxQng= +github.com/containerd/stargz-snapshotter/estargz v0.10.1/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= +github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= +github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= +github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= +github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= +github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= +github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= +github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= +github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= +github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= +github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= +github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= +github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= +github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/cli v20.10.10+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.11+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.12+incompatible h1:lZlz0uzG+GH+c0plStMUdF/qk3ppmgnswpR5EbqzVGA= +github.com/docker/cli v20.10.12+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= +github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.12+incompatible h1:CEeNmFM0QZIsJCZKMkZx0ZcahTiewkrgiwfYD+dfl1U= +github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= +github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= +github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-containerregistry v0.7.0/go.mod h1:2zaoelrL0d08gGbpdP3LqyUuBmhWbpD6IOe2s9nLS2k= +github.com/google/go-containerregistry v0.8.0 h1:mtR24eN6rapCN+shds82qFEIWWmg64NPMuyCNT7/Ogc= +github.com/google/go-containerregistry v0.8.0/go.mod h1:wW5v71NHGnQyb4k+gSshjxidrC7lN33MdWEn+Mz9TsI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/heroku/color v0.0.6 h1:UTFFMrmMLFcL3OweqP1lAdp8i1y/9oHqkeHjQ/b/Ny0= +github.com/heroku/color v0.0.6/go.mod h1:ZBvOcx7cTF2QKOv4LbmoBtNl5uB17qWxGuzZrsi1wLU= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2-0.20210730191737-8e42a01fb1b7/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= +github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= +github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= +github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8= +github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= +github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= +github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= +github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= +github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= +github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc= +github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= +github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= +github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= +github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= +github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211203184738-4852103109b8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= +golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211111162719-482062a4217b/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM= +google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= +k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= +k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= +k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= +k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= +k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= +k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= +k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= +k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= +k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= +k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= +k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= +k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= +k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= +k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/vendor/github.com/buildpacks/lifecycle/golangci.yaml b/vendor/github.com/buildpacks/lifecycle/golangci.yaml new file mode 100644 index 0000000000..2c5188c797 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/golangci.yaml @@ -0,0 +1,45 @@ +run: + timeout: 6m + +linters: + disable-all: true + enable: + - bodyclose + - deadcode + - dogsled + - errcheck + - exportloopref + - gocritic + - goimports + - gosec + - gosimple + - govet + - ineffassign + - misspell + - nakedret + - revive + - staticcheck + - structcheck + - stylecheck + - typecheck + - unconvert + - unused + - varcheck + - whitespace + + +linters-settings: + goimports: + local-prefixes: github.com/buildpacks/lifecycle + govet: + enable: + - fieldalignment + + +issues: + exclude-rules: + # Ignore this minor optimization. + # See https://github.com/golang/go/issues/44877#issuecomment-794565908 + - linters: + - govet + text: "pointer bytes could be" \ No newline at end of file diff --git a/vendor/github.com/buildpacks/lifecycle/image/image.go b/vendor/github.com/buildpacks/lifecycle/image/image.go new file mode 100644 index 0000000000..1ea3cdf2b7 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/image/image.go @@ -0,0 +1,69 @@ +package image + +import ( + "github.com/buildpacks/imgutil/remote" + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + "github.com/pkg/errors" +) + +type RegistryInputs interface { + ReadableRegistryImages() []string + WriteableRegistryImages() []string +} + +// ValidateDestinationTags ensures all tags are valid +// daemon - when false (exporting to a registry), ensures all tags are on the same registry +func ValidateDestinationTags(daemon bool, repoNames ...string) error { + var ( + reg string + registries = map[string]struct{}{} + ) + + for _, repoName := range repoNames { + ref, err := name.ParseReference(repoName, name.WeakValidation) + if err != nil { + return err + } + reg = ref.Context().RegistryStr() + registries[reg] = struct{}{} + } + + if !daemon && len(registries) != 1 { + return errors.New("writing to multiple registries is unsupported") + } + + return nil +} + +func VerifyRegistryAccess(regInputs RegistryInputs, keychain authn.Keychain) error { + for _, imageRef := range regInputs.ReadableRegistryImages() { + err := verifyReadAccess(imageRef, keychain) + if err != nil { + return err + } + } + for _, imageRef := range regInputs.WriteableRegistryImages() { + err := verifyReadWriteAccess(imageRef, keychain) + if err != nil { + return err + } + } + return nil +} + +func verifyReadAccess(imageRef string, keychain authn.Keychain) error { + img, _ := remote.NewImage(imageRef, keychain) + if !img.CheckReadAccess() { + return errors.Errorf("ensure registry read access to %s", imageRef) + } + return nil +} + +func verifyReadWriteAccess(imageRef string, keychain authn.Keychain) error { + img, _ := remote.NewImage(imageRef, keychain) + if !img.CheckReadWriteAccess() { + return errors.Errorf("ensure registry read/write access to %s", imageRef) + } + return nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/image/utils.go b/vendor/github.com/buildpacks/lifecycle/image/utils.go new file mode 100644 index 0000000000..7243a38d52 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/image/utils.go @@ -0,0 +1,64 @@ +package image + +import ( + "encoding/json" + + "github.com/buildpacks/imgutil" + "github.com/pkg/errors" +) + +func DecodeLabel(image imgutil.Image, label string, v interface{}) error { + if !image.Found() { + return nil + } + contents, err := image.Label(label) + if err != nil { + return errors.Wrapf(err, "retrieving label '%s' for image '%s'", label, image.Name()) + } + if contents == "" { + return nil + } + if err := json.Unmarshal([]byte(contents), v); err != nil { + return errors.Wrapf(err, "failed to unmarshal context of label '%s'", label) + } + return nil +} + +func SyncLabels(sourceImg imgutil.Image, destImage imgutil.Image, test func(string) bool) error { + if err := removeLabels(destImage, test); err != nil { + return err + } + return copyLabels(sourceImg, destImage, test) +} + +func removeLabels(image imgutil.Image, test func(string) bool) error { + labels, err := image.Labels() + if err != nil { + return err + } + + for label := range labels { + if test(label) { + if err := image.RemoveLabel(label); err != nil { + return errors.Wrapf(err, "failed to remove label '%s'", label) + } + } + } + return nil +} + +func copyLabels(fromImage imgutil.Image, destImage imgutil.Image, test func(string) bool) error { + fromLabels, err := fromImage.Labels() + if err != nil { + return err + } + + for label, labelValue := range fromLabels { + if test(label) { + if err := destImage.SetLabel(label, labelValue); err != nil { + return err + } + } + } + return nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/internal/encoding/utils.go b/vendor/github.com/buildpacks/lifecycle/internal/encoding/utils.go new file mode 100644 index 0000000000..4718f3a684 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/internal/encoding/utils.go @@ -0,0 +1,20 @@ +package encoding + +import ( + "os" + "path/filepath" + + "github.com/BurntSushi/toml" +) + +func WriteTOML(path string, data interface{}) error { + if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil { + return err + } + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + return toml.NewEncoder(f).Encode(data) +} diff --git a/vendor/github.com/buildpacks/lifecycle/internal/io/utils.go b/vendor/github.com/buildpacks/lifecycle/internal/io/utils.go new file mode 100644 index 0000000000..990627bb13 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/internal/io/utils.go @@ -0,0 +1,27 @@ +package io + +import ( + "io" + "os" +) + +func Copy(src, dst string) error { + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + + out, err := os.Create(dst) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, in) + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/internal/layer/logger.go b/vendor/github.com/buildpacks/lifecycle/internal/layer/logger.go new file mode 100644 index 0000000000..dbcf31b13b --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/internal/layer/logger.go @@ -0,0 +1,15 @@ +package layer + +type Logger interface { + Debug(msg string) + Debugf(fmt string, v ...interface{}) + + Info(msg string) + Infof(fmt string, v ...interface{}) + + Warn(msg string) + Warnf(fmt string, v ...interface{}) + + Error(msg string) + Errorf(fmt string, v ...interface{}) +} diff --git a/vendor/github.com/buildpacks/lifecycle/internal/layer/metadata_restorer.go b/vendor/github.com/buildpacks/lifecycle/internal/layer/metadata_restorer.go new file mode 100644 index 0000000000..985292d46c --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/internal/layer/metadata_restorer.go @@ -0,0 +1,190 @@ +package layer + +import ( + "fmt" + "path/filepath" + + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/buildpack" + "github.com/buildpacks/lifecycle/internal/encoding" + "github.com/buildpacks/lifecycle/launch" + "github.com/buildpacks/lifecycle/platform" +) + +//go:generate mockgen -package testmock -destination testmock/metadata_restorer.go github.com/buildpacks/lifecycle/internal/layer MetadataRestorer +type MetadataRestorer interface { + Restore(buildpacks []buildpack.GroupBuildpack, appMeta platform.LayersMetadata, cacheMeta platform.CacheMetadata, layerSHAStore SHAStore) error +} + +func NewMetadataRestorer(logger Logger, layersDir string, skipLayers bool) MetadataRestorer { + return &DefaultMetadataRestorer{ + logger: logger, + layersDir: layersDir, + skipLayers: skipLayers, + } +} + +type DefaultMetadataRestorer struct { + logger Logger + layersDir string + skipLayers bool +} + +func (r *DefaultMetadataRestorer) Restore(buildpacks []buildpack.GroupBuildpack, appMeta platform.LayersMetadata, cacheMeta platform.CacheMetadata, layerSHAStore SHAStore) error { + if err := r.restoreStoreTOML(appMeta, buildpacks); err != nil { + return err + } + + if err := r.restoreLayerMetadata(layerSHAStore, appMeta, cacheMeta, buildpacks); err != nil { + return err + } + + return nil +} + +func (r *DefaultMetadataRestorer) restoreStoreTOML(appMeta platform.LayersMetadata, buildpacks []buildpack.GroupBuildpack) error { + for _, bp := range buildpacks { + if store := appMeta.MetadataForBuildpack(bp.ID).Store; store != nil { + if err := encoding.WriteTOML(filepath.Join(r.layersDir, launch.EscapeID(bp.ID), "store.toml"), store); err != nil { + return err + } + } + } + return nil +} + +func (r *DefaultMetadataRestorer) restoreLayerMetadata(layerSHAStore SHAStore, appMeta platform.LayersMetadata, cacheMeta platform.CacheMetadata, buildpacks []buildpack.GroupBuildpack) error { + if r.skipLayers { + r.logger.Infof("Skipping buildpack layer analysis") + return nil + } + + for _, bp := range buildpacks { + buildpackDir, err := buildpack.ReadLayersDir(r.layersDir, bp, r.logger) + if err != nil { + return errors.Wrap(err, "reading buildpack layer directory") + } + + // Restore metadata for launch=true layers. + // The restorer step will restore the layer data for cache=true layers if possible or delete the layer. + appLayers := appMeta.MetadataForBuildpack(bp.ID).Layers + cachedLayers := cacheMeta.MetadataForBuildpack(bp.ID).Layers + for layerName, layer := range appLayers { + identifier := fmt.Sprintf("%s:%s", bp.ID, layerName) + if !layer.Launch { + r.logger.Debugf("Not restoring metadata for %q, marked as launch=false", identifier) + continue + } + if layer.Build && !layer.Cache { + // layer is launch=true, build=true. Because build=true, the layer contents must be present in the build container. + // There is no reason to restore the metadata file, because the buildpack will always recreate the layer. + r.logger.Debugf("Not restoring metadata for %q, marked as build=true, cache=false", identifier) + continue + } + if layer.Cache { + if cacheLayer, ok := cachedLayers[layerName]; !ok || !cacheLayer.Cache { + // The layer is not cache=true in the cache metadata and will not be restored. + // Do not write the metadata file so that it is clear to the buildpack that it needs to recreate the layer. + // Although a launch=true (only) layer still needs a metadata file, the restorer will remove the file anyway when it does its cleanup (for buildpack apis < 0.6). + r.logger.Debugf("Not restoring metadata for %q, marked as cache=true, but not found in cache", identifier) + continue + } + } + r.logger.Infof("Restoring metadata for %q from app image", identifier) + if err := r.writeLayerMetadata(layerSHAStore, buildpackDir, layerName, layer, bp.ID); err != nil { + return err + } + } + + // Restore metadata for cache=true layers. + // The restorer step will restore the layer data if possible or delete the layer. + for layerName, layer := range cachedLayers { + identifier := fmt.Sprintf("%s:%s", bp.ID, layerName) + if !layer.Cache { + r.logger.Debugf("Not restoring %q from cache, marked as cache=false", identifier) + continue + } + // If launch=true, the metadata was restored from the app image or the layer is stale. + if layer.Launch { + r.logger.Debugf("Not restoring %q from cache, marked as launch=true", identifier) + continue + } + r.logger.Infof("Restoring metadata for %q from cache", identifier) + if err := r.writeLayerMetadata(layerSHAStore, buildpackDir, layerName, layer, bp.ID); err != nil { + return err + } + } + } + return nil +} + +func (r *DefaultMetadataRestorer) writeLayerMetadata(layerSHAStore SHAStore, buildpackDir buildpack.LayersDir, layerName string, metadata buildpack.LayerMetadata, buildpackID string) error { + layer := buildpackDir.NewLayer(layerName, buildpackDir.Buildpack.API, r.logger) + r.logger.Debugf("Writing layer metadata for %q", layer.Identifier()) + if err := layer.WriteMetadata(metadata.LayerMetadataFile); err != nil { + return err + } + return layerSHAStore.add(buildpackID, metadata.SHA, layer) +} + +type SHAStore interface { + add(buildpackID, sha string, layer *buildpack.Layer) error + Get(buildpackID string, layer buildpack.Layer) (string, error) +} + +func NewSHAStore(useShaFiles bool) SHAStore { + if useShaFiles { + return &fileStore{} + } + return &memoryStore{make(map[string]layerToSha)} +} + +type fileStore struct{} + +func (fs *fileStore) add(_, sha string, layer *buildpack.Layer) error { + return layer.WriteSha(sha) +} + +func (fs *fileStore) Get(_ string, layer buildpack.Layer) (string, error) { + data, err := layer.Read() + if err != nil { + return "", errors.Wrapf(err, "reading layer") + } + return data.SHA, nil +} + +type memoryStore struct { + buildpacksToLayersShaMap map[string]layerToSha +} + +type layerToSha struct { + layerToShaMap map[string]string +} + +func (ms *memoryStore) add(buildpackID, sha string, layer *buildpack.Layer) error { + ms.addLayerToMap(buildpackID, layer.Name(), sha) + return nil +} + +func (ms *memoryStore) Get(buildpackID string, layer buildpack.Layer) (string, error) { + return ms.getShaByBuildpackLayer(buildpackID, layer.Name()), nil +} + +func (ms *memoryStore) addLayerToMap(buildpackID, layerName, sha string) { + _, exists := ms.buildpacksToLayersShaMap[buildpackID] + if !exists { + ms.buildpacksToLayersShaMap[buildpackID] = layerToSha{make(map[string]string)} + } + ms.buildpacksToLayersShaMap[buildpackID].layerToShaMap[layerName] = sha +} + +// if the layer exists for the buildpack ID, its SHA will be returned. Otherwise, an empty string will be returned. +func (ms *memoryStore) getShaByBuildpackLayer(buildpackID, layerName string) string { + if layerToSha, buildpackExists := ms.buildpacksToLayersShaMap[buildpackID]; buildpackExists { + if sha, layerExists := layerToSha.layerToShaMap[layerName]; layerExists { + return sha + } + } + return "" +} diff --git a/vendor/github.com/buildpacks/lifecycle/internal/layer/sbom_restorer.go b/vendor/github.com/buildpacks/lifecycle/internal/layer/sbom_restorer.go new file mode 100644 index 0000000000..964688818b --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/internal/layer/sbom_restorer.go @@ -0,0 +1,131 @@ +package layer + +import ( + "fmt" + "io" + "io/fs" + "os" + "path/filepath" + "regexp" + "runtime" + + "github.com/buildpacks/imgutil" + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/buildpack" + io2 "github.com/buildpacks/lifecycle/internal/io" + "github.com/buildpacks/lifecycle/launch" + "github.com/buildpacks/lifecycle/layers" +) + +//go:generate mockgen -package testmock -destination testmock/sbom_restorer.go github.com/buildpacks/lifecycle/internal/layer SBOMRestorer +type SBOMRestorer interface { + RestoreFromPrevious(image imgutil.Image, layerDigest string) error + RestoreFromCache(cache Cache, layerDigest string) error + RestoreToBuildpackLayers(detectedBps []buildpack.GroupBuildpack) error +} + +type Cache interface { + RetrieveLayer(sha string) (io.ReadCloser, error) +} + +func NewSBOMRestorer(layersDir string, logger Logger) SBOMRestorer { + return &DefaultSBOMRestorer{ + layersDir: layersDir, + logger: logger, + } +} + +type DefaultSBOMRestorer struct { + layersDir string + logger Logger +} + +func (r *DefaultSBOMRestorer) RestoreFromPrevious(image imgutil.Image, layerDigest string) error { + // Sanity check to prevent panic. + if image == nil { + return errors.Errorf("restoring layer: previous image not found for %q", layerDigest) + } + r.logger.Debugf("Retrieving previous image sbom layer for %q", layerDigest) + + rc, err := image.GetLayer(layerDigest) + if err != nil { + return err + } + defer rc.Close() + + return layers.Extract(rc, "") +} + +func (r *DefaultSBOMRestorer) RestoreFromCache(cache Cache, layerDigest string) error { + // Sanity check to prevent panic. + if cache == nil { + return errors.New("restoring layer: cache not provided") + } + r.logger.Debugf("Retrieving sbom layer data for %q", layerDigest) + + rc, err := cache.RetrieveLayer(layerDigest) + if err != nil { + return err + } + defer rc.Close() + + return layers.Extract(rc, "") +} + +func (r *DefaultSBOMRestorer) RestoreToBuildpackLayers(detectedBps []buildpack.GroupBuildpack) error { + var ( + cacheDir = filepath.Join(r.layersDir, "sbom", "cache") + launchDir = filepath.Join(r.layersDir, "sbom", "launch") + ) + defer os.RemoveAll(filepath.Join(r.layersDir, "sbom")) + + if err := filepath.Walk(cacheDir, r.restoreSBOMFunc(detectedBps, "cache")); err != nil { + return err + } + + return filepath.Walk(launchDir, r.restoreSBOMFunc(detectedBps, "launch")) +} + +func (r *DefaultSBOMRestorer) restoreSBOMFunc(detectedBps []buildpack.GroupBuildpack, bomType string) func(path string, info fs.FileInfo, err error) error { + var bomRegex *regexp.Regexp + + if runtime.GOOS == "windows" { + bomRegex = regexp.MustCompile(fmt.Sprintf(`%s\\(.+)\\(.+)\\(sbom.+json)`, bomType)) + } else { + bomRegex = regexp.MustCompile(fmt.Sprintf(`%s/(.+)/(.+)/(sbom.+json)`, bomType)) + } + + return func(path string, info fs.FileInfo, err error) error { + if info == nil || !info.Mode().IsRegular() { + return nil + } + + matches := bomRegex.FindStringSubmatch(path) + if len(matches) != 4 { + return nil + } + + var ( + bpID = matches[1] + layerName = matches[2] + fileName = matches[3] + dest = filepath.Join(r.layersDir, bpID, fmt.Sprintf("%s.%s", layerName, fileName)) + ) + + if !r.contains(detectedBps, bpID) { + return nil + } + + return io2.Copy(path, dest) + } +} + +func (r *DefaultSBOMRestorer) contains(detectedBps []buildpack.GroupBuildpack, id string) bool { + for _, bp := range detectedBps { + if launch.EscapeID(bp.ID) == id { + return true + } + } + return false +} diff --git a/vendor/github.com/buildpacks/lifecycle/internal/str/utils.go b/vendor/github.com/buildpacks/lifecycle/internal/str/utils.go new file mode 100644 index 0000000000..8a7d00cc24 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/internal/str/utils.go @@ -0,0 +1,33 @@ +package str + +// compare performs a set comparison between two slices. `extra` represents elements present in +// `strings1` but not `strings2`. `missing` represents elements present in `strings2` that are +// missing from `strings1`. `common` represents elements present in both slices. Since the input +// slices are treated as sets, duplicates will be removed in any outputs. +// from: https://github.com/buildpacks/pack/blob/main/internal/stringset/stringset.go +func Compare(strings1, strings2 []string) (extra []string, missing []string, common []string) { + set1 := map[string]struct{}{} + set2 := map[string]struct{}{} + for _, s := range strings1 { + set1[s] = struct{}{} + } + for _, s := range strings2 { + set2[s] = struct{}{} + } + + for s := range set1 { + if _, ok := set2[s]; !ok { + extra = append(extra, s) + continue + } + common = append(common, s) + } + + for s := range set2 { + if _, ok := set1[s]; !ok { + missing = append(missing, s) + } + } + + return extra, missing, common +} diff --git a/vendor/github.com/buildpacks/lifecycle/launch/bash.go b/vendor/github.com/buildpacks/lifecycle/launch/bash.go new file mode 100644 index 0000000000..653acf523d --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/launch/bash.go @@ -0,0 +1,59 @@ +package launch + +import ( + "fmt" + + "github.com/pkg/errors" +) + +var ( + bashCommandWithScript = `exec bash -c "$@"` // for processes w/o arguments +) + +type BashShell struct { + Exec ExecFunc +} + +// Launch launches the given ShellProcess with Bash +// +// It shall execute a Bash command that sources profile scripts and then executes the process in a nested Bash command +// When ShellProcess.Script is true nested Bash script shall be proc.Command with proc.Args provided as argument to Bash +// When ShellProcess.Script is false a Bash command shall be contructed from proc.Command and proc.Args +func (b *BashShell) Launch(proc ShellProcess) error { + launcher := "" + for _, profile := range proc.Profiles { + launcher += fmt.Sprintf("source \"%s\"\n", profile) + } + var bashCommand string + if proc.Script { + bashCommand = bashCommandWithScript + } else { + bashCommand = bashCommandWithTokens(len(proc.Args) + 1) + } + launcher += bashCommand + if err := b.Exec("/bin/bash", append([]string{ + "bash", "-c", + launcher, proc.Caller, proc.Command, + }, proc.Args...), proc.Env); err != nil { + return errors.Wrap(err, "bash exec") + } + return nil +} + +// bashCommandWithTokens returns a bash script that should be executed with nTokens number of bash arguments +// Each argument to bash is evaluated by the shell before becoming a token in the resulting script +// Example: +// Given nTokens=2 the returned script will contain `"$(eval echo \"$0\")" "$(eval echo \"$1\")"` +// and should be evaluated with `bash -c '"$(eval echo \"$0\")" "$(eval echo \"$1\")"' ' +// Token evaluation example: +// "$(eval echo \"$0\"`)" // given $0='$FOO' and $FOO='arg with spaces" && quotes' +// -> "$(eval echo \"'$FOO'\")" +// -> "$(echo \"'arg with spaces" && quotes'\")" +// -> "arg with spaces\" && quotes" // this is an evaluated and properly quoted token +func bashCommandWithTokens(nTokens int) string { + commandScript := `"$(eval echo \"$0\")"` + for i := 1; i < nTokens; i++ { + commandScript += fmt.Sprintf(` "$(eval echo \"${%d}\")"`, i) + } + return fmt.Sprintf(`exec bash -c '%s' "${@:1}"`, commandScript) +} diff --git a/vendor/github.com/buildpacks/lifecycle/launch/cmd.go b/vendor/github.com/buildpacks/lifecycle/launch/cmd.go new file mode 100644 index 0000000000..8fbfe242c5 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/launch/cmd.go @@ -0,0 +1,25 @@ +package launch + +import ( + "github.com/pkg/errors" +) + +type CmdShell struct { + Exec ExecFunc +} + +// Launch launches the given ShellProcess with cmd +func (c *CmdShell) Launch(proc ShellProcess) error { + var commandTokens []string + for _, profile := range proc.Profiles { + commandTokens = append(commandTokens, "call", profile, "&&") + } + commandTokens = append(commandTokens, proc.Command) + commandTokens = append(commandTokens, proc.Args...) + if err := c.Exec("cmd", + append([]string{"cmd", "/q", "/v:on", "/s", "/c"}, commandTokens...), proc.Env, + ); err != nil { + return errors.Wrap(err, "cmd execute") + } + return nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/launch/exec_d.go b/vendor/github.com/buildpacks/lifecycle/launch/exec_d.go new file mode 100644 index 0000000000..3bceb66e64 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/launch/exec_d.go @@ -0,0 +1,64 @@ +package launch + +import ( + "io" + "io/ioutil" + "os" + "os/exec" + + "github.com/BurntSushi/toml" + "github.com/pkg/errors" +) + +// ExecDRunner is responsible for running ExecD binaries. +type ExecDRunner struct { + Out, Err io.Writer // Out and Err can be used to configure Stdout and Stderr processes run by ExecDRunner. +} + +// NewExecDRunner creates an ExecDRunner with Out and Err set to stdout and stderr +func NewExecDRunner() *ExecDRunner { + return &ExecDRunner{ + Out: os.Stdout, + Err: os.Stderr, + } +} + +// ExecD executes the executable file at path and sets the returned variables in env. The executable at path +// should implement the ExecD interface in the buildpack specification https://github.com/buildpacks/spec/blob/main/buildpack.md#execd +func (e *ExecDRunner) ExecD(path string, env Env) error { + pr, pw, err := os.Pipe() + if err != nil { + return errors.Wrap(err, "failed to create pipe") + } + errChan := make(chan error, 1) + go func() { + defer pw.Close() + cmd := exec.Command(path) + cmd.Stdout = e.Out + cmd.Stderr = e.Err + cmd.Env = env.List() + if err := setHandle(cmd, pw); err != nil { + errChan <- err + } else { + errChan <- cmd.Run() + } + }() + + out, err := ioutil.ReadAll(pr) + if cmdErr := <-errChan; cmdErr != nil { + // prefer the error from the command + return errors.Wrapf(cmdErr, "failed to execute exec.d file at path '%s'", path) + } else if err != nil { + // return the read error only if the command succeeded + return errors.Wrapf(err, "failed to read output from exec.d file at path '%s'", path) + } + + envVars := map[string]string{} + if _, err := toml.Decode(string(out), &envVars); err != nil { + return errors.Wrapf(err, "failed to decode output from exec.d file at path '%s'", path) + } + for k, v := range envVars { + env.Set(k, v) + } + return nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/launch/exec_d_unix.go b/vendor/github.com/buildpacks/lifecycle/launch/exec_d_unix.go new file mode 100644 index 0000000000..a71af4a243 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/launch/exec_d_unix.go @@ -0,0 +1,14 @@ +//go:build linux || darwin +// +build linux darwin + +package launch + +import ( + "os" + "os/exec" +) + +func setHandle(cmd *exec.Cmd, f *os.File) error { + cmd.ExtraFiles = []*os.File{f} + return nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/launch/exec_d_windows.go b/vendor/github.com/buildpacks/lifecycle/launch/exec_d_windows.go new file mode 100644 index 0000000000..9789c6c5da --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/launch/exec_d_windows.go @@ -0,0 +1,21 @@ +package launch + +import ( + "fmt" + "os" + "os/exec" + + "golang.org/x/sys/windows" +) + +const EnvExecDHandle = "CNB_EXEC_D_HANDLE" + +func setHandle(cmd *exec.Cmd, f *os.File) error { + handle := f.Fd() + if err := windows.SetHandleInformation(windows.Handle(handle), windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT); err != nil { + return err + } + + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%#x", EnvExecDHandle, handle)) + return nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/launch/launch.go b/vendor/github.com/buildpacks/lifecycle/launch/launch.go new file mode 100644 index 0000000000..8ad99c45e9 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/launch/launch.go @@ -0,0 +1,53 @@ +package launch + +import ( + "path" + "path/filepath" + "strings" +) + +type Process struct { + Type string `toml:"type" json:"type"` + Command string `toml:"command" json:"command"` + Args []string `toml:"args" json:"args"` + Direct bool `toml:"direct" json:"direct"` + Default bool `toml:"default,omitempty" json:"default,omitempty"` + BuildpackID string `toml:"buildpack-id" json:"buildpackID"` +} + +func (p Process) NoDefault() Process { + p.Default = false + return p +} + +// ProcessPath returns the absolute path to the symlink for a given process type +func ProcessPath(pType string) string { + return filepath.Join(ProcessDir, pType+exe) +} + +type Metadata struct { + Processes []Process `toml:"processes" json:"processes"` + Buildpacks []Buildpack `toml:"buildpacks" json:"buildpacks"` +} + +func (m Metadata) FindProcessType(pType string) (Process, bool) { + for _, p := range m.Processes { + if p.Type == pType { + return p, true + } + } + return Process{}, false +} + +type Buildpack struct { + API string `toml:"api"` + ID string `toml:"id"` +} + +func EscapeID(id string) string { + return strings.ReplaceAll(id, "/", "_") +} + +func GetMetadataFilePath(layersDir string) string { + return path.Join(layersDir, "config", "metadata.toml") +} diff --git a/vendor/github.com/buildpacks/lifecycle/launch/launcher.go b/vendor/github.com/buildpacks/lifecycle/launch/launcher.go new file mode 100644 index 0000000000..c2ac4f182c --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/launch/launcher.go @@ -0,0 +1,209 @@ +package launch + +import ( + "io/ioutil" + "os" + "os/exec" + "path/filepath" + + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/api" + "github.com/buildpacks/lifecycle/env" +) + +var ( + LifecycleDir = filepath.Join(CNBDir, "lifecycle") + ProcessDir = filepath.Join(CNBDir, "process") + LauncherPath = filepath.Join(LifecycleDir, "launcher"+exe) +) + +type Launcher struct { + AppDir string + Buildpacks []Buildpack + DefaultProcessType string + Env Env + Exec ExecFunc + ExecD ExecD + Shell Shell + LayersDir string + PlatformAPI *api.Version + Processes []Process + Setenv func(string, string) error +} + +type ExecFunc func(argv0 string, argv []string, envv []string) error + +type ExecD interface { + ExecD(path string, env Env) error +} + +type Env interface { + AddEnvDir(envDir string, defaultAction env.ActionType) error + AddRootDir(baseDir string) error + Get(string) string + List() []string + Set(name, k string) +} + +// Launch uses cmd to select a process and launches that process. +// For direct=false processes, self is used to set argv0 during profile script execution +func (l *Launcher) Launch(self string, cmd []string) error { + proc, err := l.ProcessFor(cmd) + if err != nil { + return errors.Wrap(err, "determine start command") + } + return l.LaunchProcess(self, proc) +} + +// LaunchProcess launches the provided process. +// For direct=false processes, self is used to set argv0 during profile script execution +func (l *Launcher) LaunchProcess(self string, proc Process) error { + if err := os.Chdir(l.AppDir); err != nil { + return errors.Wrap(err, "change to app directory") + } + if err := l.doEnv(proc.Type); err != nil { + return errors.Wrap(err, "modify env") + } + if err := l.doExecD(proc.Type); err != nil { + return errors.Wrap(err, "exec.d") + } + + if proc.Direct { + return l.launchDirect(proc) + } + return l.launchWithShell(self, proc) +} + +func (l *Launcher) launchDirect(proc Process) error { + if err := l.Setenv("PATH", l.Env.Get("PATH")); err != nil { + return errors.Wrap(err, "set path") + } + binary, err := exec.LookPath(proc.Command) + if err != nil { + return errors.Wrap(err, "path lookup") + } + + if err := l.Exec(binary, + append([]string{proc.Command}, proc.Args...), + l.Env.List(), + ); err != nil { + return errors.Wrap(err, "direct exec") + } + return nil +} + +func (l *Launcher) doEnv(procType string) error { + return l.eachBuildpack(func(bpAPI *api.Version, bpDir string) error { + if err := eachLayer(bpDir, l.doLayerRoot()); err != nil { + return errors.Wrap(err, "add layer root") + } + if err := eachLayer(bpDir, l.doLayerEnvFiles(procType, env.DefaultActionType(bpAPI))); err != nil { + return errors.Wrap(err, "add layer env") + } + return nil + }) +} + +func (l *Launcher) doExecD(procType string) error { + return l.eachBuildpack(func(bpAPI *api.Version, bpDir string) error { + if !supportsExecD(bpAPI) { + return nil + } + return eachLayer(bpDir, l.doLayerExecD(procType)) + }) +} + +func supportsExecD(bpAPI *api.Version) bool { + return bpAPI.AtLeast("0.5") +} + +type bpAction func(bpAPI *api.Version, bpDir string) error +type dirAction func(layerDir string) error + +func (l *Launcher) eachBuildpack(fn bpAction) error { + for _, bp := range l.Buildpacks { + dir := filepath.Join(l.LayersDir, EscapeID(bp.ID)) + if _, err := os.Stat(dir); os.IsNotExist(err) { + continue + } else if err != nil { + return errors.Wrap(err, "find buildpack directory") + } + bpAPI, err := api.NewVersion(bp.API) + if err != nil { + return err + } + if err := fn(bpAPI, dir); err != nil { + return err + } + } + return nil +} + +func (l *Launcher) doLayerRoot() dirAction { + return func(path string) error { + return l.Env.AddRootDir(path) + } +} + +func (l *Launcher) doLayerEnvFiles(procType string, defaultAction env.ActionType) dirAction { + return func(path string) error { + if err := l.Env.AddEnvDir(filepath.Join(path, "env"), defaultAction); err != nil { + return err + } + if err := l.Env.AddEnvDir(filepath.Join(path, "env.launch"), defaultAction); err != nil { + return err + } + if procType == "" { + return nil + } + return l.Env.AddEnvDir(filepath.Join(path, "env.launch", procType), defaultAction) + } +} + +func (l *Launcher) doLayerExecD(procType string) dirAction { + return func(path string) error { + if err := eachFile(filepath.Join(path, "exec.d"), func(path string) error { + return l.ExecD.ExecD(path, l.Env) + }); err != nil { + return err + } + if procType == "" { + return nil + } + return eachFile(filepath.Join(path, "exec.d", procType), func(path string) error { + return l.ExecD.ExecD(path, l.Env) + }) + } +} + +func eachLayer(bpDir string, action dirAction) error { + return eachInDir(bpDir, action, func(fi os.FileInfo) bool { + return fi.IsDir() + }) +} + +func eachFile(dir string, action dirAction) error { + return eachInDir(dir, action, func(fi os.FileInfo) bool { + return !fi.IsDir() + }) +} + +func eachInDir(dir string, action dirAction, predicate func(fi os.FileInfo) bool) error { + fis, err := ioutil.ReadDir(dir) + if os.IsNotExist(err) { + return nil + } + if err != nil { + return errors.Wrapf(err, "failed to list files in dir '%s'", dir) + } + for _, fi := range fis { + if !predicate(fi) { + continue + } + if err := action(filepath.Join(dir, fi.Name())); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/launch/launcher_unix.go b/vendor/github.com/buildpacks/lifecycle/launch/launcher_unix.go new file mode 100644 index 0000000000..b547a9a40e --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/launch/launcher_unix.go @@ -0,0 +1,17 @@ +//go:build linux || darwin +// +build linux darwin + +package launch + +import "syscall" + +const ( + CNBDir = `/cnb` + exe = "" + appProfile = ".profile" +) + +var ( + OSExecFunc = syscall.Exec + DefaultShell = &BashShell{Exec: OSExecFunc} +) diff --git a/vendor/github.com/buildpacks/lifecycle/launch/launcher_windows.go b/vendor/github.com/buildpacks/lifecycle/launch/launcher_windows.go new file mode 100644 index 0000000000..57b4a358e5 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/launch/launcher_windows.go @@ -0,0 +1,25 @@ +package launch + +import ( + "os" + "os/exec" +) + +const ( + CNBDir = `c:\cnb` + exe = ".exe" + appProfile = ".profile.bat" +) + +var ( + DefaultShell = &CmdShell{Exec: OSExecFunc} +) + +func OSExecFunc(argv0 string, argv []string, envv []string) error { + c := exec.Command(argv[0], argv[1:]...) // #nosec G204 + c.Env = envv + c.Stdin = os.Stdin + c.Stdout = os.Stdout + c.Stderr = os.Stderr + return c.Run() +} diff --git a/vendor/github.com/buildpacks/lifecycle/launch/process.go b/vendor/github.com/buildpacks/lifecycle/launch/process.go new file mode 100644 index 0000000000..0ed53aae32 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/launch/process.go @@ -0,0 +1,77 @@ +package launch + +import ( + "fmt" + + "github.com/pkg/errors" +) + +// ProcessFor creates a process from container cmd +// If the Platform API if 0.4 or greater and DefaultProcess is set: +// * The default process is returned with `cmd` appended to the process args +// If the Platform API is less than 0.4 +// * If there is exactly one argument and it matches a process type, it returns that process. +// * If cmd is empty, it returns the default process +// Else +// * it constructs a new process from cmd +// * If the first element in cmd is `cmd` the process shall be direct +func (l *Launcher) ProcessFor(cmd []string) (Process, error) { + if l.PlatformAPI.LessThan("0.4") { + return l.processForLegacy(cmd) + } + + if l.DefaultProcessType == "" { + process, err := l.userProvidedProcess(cmd) + if err != nil { + return Process{}, err + } + return process, nil + } + + process, ok := l.findProcessType(l.DefaultProcessType) + if !ok { + return Process{}, fmt.Errorf("process type %s was not found", l.DefaultProcessType) + } + process.Args = append(process.Args, cmd...) + + return process, nil +} + +func (l *Launcher) processForLegacy(cmd []string) (Process, error) { + if len(cmd) == 0 { + if process, ok := l.findProcessType(l.DefaultProcessType); ok { + return process, nil + } + + return Process{}, fmt.Errorf("process type %s was not found", l.DefaultProcessType) + } + + if len(cmd) == 1 { + if process, ok := l.findProcessType(cmd[0]); ok { + return process, nil + } + } + + return l.userProvidedProcess(cmd) +} + +func (l *Launcher) findProcessType(pType string) (Process, bool) { + for _, p := range l.Processes { + if p.Type == pType { + return p, true + } + } + + return Process{}, false +} + +func (l *Launcher) userProvidedProcess(cmd []string) (Process, error) { + if len(cmd) == 0 { + return Process{}, errors.New("when there is no default process a command is required") + } + if len(cmd) > 1 && cmd[0] == "--" { + return Process{Command: cmd[1], Args: cmd[2:], Direct: true}, nil + } + + return Process{Command: cmd[0], Args: cmd[1:]}, nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/launch/shell.go b/vendor/github.com/buildpacks/lifecycle/launch/shell.go new file mode 100644 index 0000000000..62bf350e03 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/launch/shell.go @@ -0,0 +1,126 @@ +package launch + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/api" +) + +type Shell interface { + Launch(ShellProcess) error +} + +type ShellProcess struct { + Script bool // Script indicates whether Command is a script or should be a token in a generated script + Args []string + Command string + Caller string // Caller used to set argv0 for Bash profile scripts and is ignored in Cmd + Profiles []string + Env []string +} + +func (l *Launcher) launchWithShell(self string, proc Process) error { + profs, err := l.getProfiles(proc.Type) + if err != nil { + return errors.Wrap(err, "find profiles") + } + script, err := l.isScript(proc) + if err != nil { + return err + } + return l.Shell.Launch(ShellProcess{ + Script: script, + Caller: self, + Command: proc.Command, + Args: proc.Args, + Profiles: profs, + Env: l.Env.List(), + }) +} + +func (l *Launcher) getProfiles(procType string) ([]string, error) { + var profiles []string + if err := l.eachBuildpack(func(_ *api.Version, bpDir string) error { + return eachLayer(bpDir, l.populateLayerProfiles(procType, &profiles)) + }); err != nil { + return nil, err + } + + fi, err := os.Stat(filepath.Join(l.AppDir, appProfile)) + if os.IsNotExist(err) { + return profiles, nil + } else if err != nil { + return nil, errors.Wrapf(err, "failed to determine if app profile script exists at path '%s'", filepath.Join(l.AppDir, appProfile)) + } + if !fi.IsDir() { + profiles = append(profiles, appProfile) + } + + return profiles, nil +} + +func (l *Launcher) populateLayerProfiles(procType string, profiles *[]string) dirAction { + return func(layerDir string) error { + if err := eachFile(filepath.Join(layerDir, "profile.d"), func(path string) error { + *profiles = append(*profiles, path) + return nil + }); err != nil { + return err + } + if procType == "" { + return nil + } + return eachFile(filepath.Join(layerDir, "profile.d", procType), func(path string) error { + *profiles = append(*profiles, path) + return nil + }) + } +} + +func (l *Launcher) isScript(proc Process) (bool, error) { + if runtime.GOOS == "windows" { + // Windows does not support script commands + return false, nil + } + if len(proc.Args) == 0 { + return true, nil + } + bpAPI, err := l.buildpackAPI(proc) + if err != nil { + return false, err + } + if bpAPI == nil { + return false, err + } + if isLegacyProcess(bpAPI) { + return true, nil + } + return false, nil +} + +// buildpackAPI returns the API of the buildpack that contributed the process and true if the process was contributed +// by a buildpack. If the process was not provided by a buildpack it returns nil. +func (l *Launcher) buildpackAPI(proc Process) (*api.Version, error) { + if proc.BuildpackID == "" { + return nil, nil + } + for _, bp := range l.Buildpacks { + if bp.ID == proc.BuildpackID { + api, err := api.NewVersion(bp.API) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse api '%s' of buildpack '%s'", bp.API, bp.ID) + } + return api, nil + } + } + return nil, fmt.Errorf("process type '%s' provided by unknown buildpack '%s'", proc.Type, proc.BuildpackID) +} + +func isLegacyProcess(bpAPI *api.Version) bool { + return bpAPI.LessThan("0.4") +} diff --git a/vendor/github.com/buildpacks/lifecycle/layers/digest.go b/vendor/github.com/buildpacks/lifecycle/layers/digest.go new file mode 100644 index 0000000000..9e086a6039 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/layers/digest.go @@ -0,0 +1,46 @@ +package layers + +import ( + "hash" + "sync" +) + +// concurrentHasher wraps a hash.Hash so that writes to it +// happen on a separate go routine. +type concurrentHasher struct { + hash hash.Hash + wg sync.WaitGroup + buffers chan []byte +} + +func newConcurrentHasher(h hash.Hash) *concurrentHasher { + ch := &concurrentHasher{ + hash: h, + buffers: make(chan []byte, 10), + } + + go func() { + for b := range ch.buffers { + _, _ = ch.hash.Write(b) + ch.wg.Done() + } + }() + + return ch +} + +func (ch *concurrentHasher) Write(p []byte) (int, error) { + cp := make([]byte, len(p)) + copy(cp, p) + + ch.wg.Add(1) + ch.buffers <- cp + + return len(p), nil +} + +func (ch *concurrentHasher) Sum(b []byte) []byte { + ch.wg.Wait() + close(ch.buffers) + return ch.hash.Sum(b) +} diff --git a/vendor/github.com/buildpacks/lifecycle/layers/dir.go b/vendor/github.com/buildpacks/lifecycle/layers/dir.go new file mode 100644 index 0000000000..7b4f47edd6 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/layers/dir.go @@ -0,0 +1,29 @@ +package layers + +import ( + "path/filepath" + + "github.com/buildpacks/lifecycle/archive" +) + +// DirLayer creates a layer from the given directory +// DirLayer will set the UID and GID of entries describing dir and its children (but not its parents) +// to Factory.UID and Factory.GID +func (f *Factory) DirLayer(id string, dir string) (layer Layer, err error) { + dir, err = filepath.Abs(dir) + if err != nil { + return Layer{}, err + } + parents, err := parents(dir) + if err != nil { + return Layer{}, err + } + return f.writeLayer(id, func(tw *archive.NormalizingTarWriter) error { + if err := archive.AddFilesToArchive(tw, parents); err != nil { + return err + } + tw.WithUID(f.UID) + tw.WithGID(f.GID) + return archive.AddDirToArchive(tw, dir) + }) +} diff --git a/vendor/github.com/buildpacks/lifecycle/layers/extract.go b/vendor/github.com/buildpacks/lifecycle/layers/extract.go new file mode 100644 index 0000000000..7d0e9f4b88 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/layers/extract.go @@ -0,0 +1,33 @@ +package layers + +import ( + "archive/tar" + "io" + "runtime" + + "github.com/buildpacks/lifecycle/archive" +) + +// Extract extracts entries from r to the dest directory +// Contents of r should be an OCI layer. +// If dest is an empty string files with be extracted to `/` or `c:\` on unix and windows filesystems respectively. +func Extract(r io.Reader, dest string) error { + tr := tarReader(r, dest) + return archive.Extract(tr) +} + +func tarReader(r io.Reader, dest string) archive.TarReader { + tr := archive.NewNormalizingTarReader(tar.NewReader(r)) + if runtime.GOOS == "windows" { + tr.ExcludePaths([]string{"Hives"}) + tr.Strip(`Files/`) + if dest == "" { + dest = `c:\` + } + } + if dest == "" { + dest = `/` + } + tr.PrependDir(dest) + return tr +} diff --git a/vendor/github.com/buildpacks/lifecycle/layers/factory.go b/vendor/github.com/buildpacks/lifecycle/layers/factory.go new file mode 100644 index 0000000000..ae9cf5ee27 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/layers/factory.go @@ -0,0 +1,99 @@ +package layers + +import ( + "os" + "path/filepath" + "strings" + + "github.com/buildpacks/lifecycle/archive" +) + +type Factory struct { + ArtifactsDir string // ArtifactsDir is the directory where layer files are written + UID, GID int // UID and GID are used to normalize layer entries + Logger Logger + + tarHashes map[string]string // tarHases Stores hashes of layer tarballs for reuse between the export and cache steps. +} + +type Layer struct { + ID string + TarPath string + Digest string +} + +type Logger interface { + Debug(msg string) + Debugf(fmt string, v ...interface{}) + + Info(msg string) + Infof(fmt string, v ...interface{}) + + Warn(msg string) + Warnf(fmt string, v ...interface{}) + + Error(msg string) + Errorf(fmt string, v ...interface{}) +} + +func (f *Factory) writeLayer(id string, addEntries func(tw *archive.NormalizingTarWriter) error) (layer Layer, err error) { + tarPath := filepath.Join(f.ArtifactsDir, escape(id)+".tar") + if f.tarHashes == nil { + f.tarHashes = make(map[string]string) + } + if sha, ok := f.tarHashes[tarPath]; ok { + f.Logger.Debugf("Reusing tarball for layer %q with SHA: %s\n", id, sha) + return Layer{ + ID: id, + TarPath: tarPath, + Digest: sha, + }, nil + } + lw, err := newFileLayerWriter(tarPath) + if err != nil { + return Layer{}, err + } + defer func() { + if closeErr := lw.Close(); err == nil { + err = closeErr + } + }() + tw := tarWriter(lw) + if err := addEntries(tw); err != nil { + return Layer{}, err + } + + if err := tw.Close(); err != nil { + return Layer{}, err + } + digest := lw.Digest() + f.tarHashes[tarPath] = digest + return Layer{ + ID: id, + Digest: digest, + TarPath: tarPath, + }, err +} + +func escape(id string) string { + return strings.ReplaceAll(id, "/", "_") +} + +func parents(file string) ([]archive.PathInfo, error) { + parent := filepath.Dir(file) + if parent == filepath.VolumeName(file)+`\` || parent == "/" { + return []archive.PathInfo{}, nil + } + fi, err := os.Stat(parent) + if err != nil { + return nil, err + } + parentDirs, err := parents(parent) + if err != nil { + return nil, err + } + return append(parentDirs, archive.PathInfo{ + Path: parent, + Info: fi, + }), nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/layers/launcher.go b/vendor/github.com/buildpacks/lifecycle/layers/launcher.go new file mode 100644 index 0000000000..67124fb45d --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/layers/launcher.go @@ -0,0 +1,125 @@ +package layers + +import ( + "archive/tar" + "fmt" + "io" + "os" + "runtime" + "strings" + + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/archive" + "github.com/buildpacks/lifecycle/launch" +) + +// LauncherLayer creates a Layer containing the launcher at path +func (f *Factory) LauncherLayer(path string) (layer Layer, err error) { + parents := []*tar.Header{ + rootOwnedDir(launch.CNBDir), + rootOwnedDir("/cnb/lifecycle"), + } + fi, err := os.Stat(path) + if err != nil { + return Layer{}, fmt.Errorf("failed to stat launcher at path '%s': %w", path, err) + } + hdr, err := tar.FileInfoHeader(fi, "") + if err != nil { + return Layer{}, fmt.Errorf("failed create TAR header for launcher at path '%s': %w", path, err) + } + hdr.Name = launch.LauncherPath + hdr.Uid = 0 + hdr.Gid = 0 + if runtime.GOOS == "windows" { + hdr.Mode = 0777 + } else { + hdr.Mode = 0755 + } + + return f.writeLayer("launcher", func(tw *archive.NormalizingTarWriter) error { + for _, dir := range parents { + if err := tw.WriteHeader(dir); err != nil { + return err + } + } + if err := tw.WriteHeader(hdr); err != nil { + return errors.Wrap(err, "failed to write header for launcher") + } + + lf, err := os.Open(path) + if err != nil { + return fmt.Errorf("failed to open launcher at path '%s': %w", path, err) + } + defer lf.Close() + if _, err := io.Copy(tw, lf); err != nil { + return errors.Wrap(err, "failed to write launcher to layer") + } + return nil + }) +} + +// ProcessTypesLayer creates a Layer containing symlinks pointing to target where: +// * any parents of the symlink files will also be added to the layer +// * symlinks and their parent directories shall be root owned and world readable +func (f *Factory) ProcessTypesLayer(config launch.Metadata) (layer Layer, err error) { + hdrs := []*tar.Header{ + rootOwnedDir(launch.CNBDir), + rootOwnedDir(launch.ProcessDir), + } + for _, proc := range config.Processes { + if len(proc.Type) == 0 { + return Layer{}, errors.New("type is required for all processes") + } + if err := validateProcessType(proc.Type); err != nil { + return Layer{}, errors.Wrapf(err, "invalid process type '%s'", proc.Type) + } + hdrs = append(hdrs, typeSymlink(launch.ProcessPath(proc.Type))) + } + + return f.writeLayer("process-types", func(tw *archive.NormalizingTarWriter) error { + for _, hdr := range hdrs { + if err := tw.WriteHeader(hdr); err != nil { + return err + } + } + return nil + }) +} + +func validateProcessType(pType string) error { + forbiddenCharacters := `/><:|&\` + if strings.ContainsAny(pType, forbiddenCharacters) { + return fmt.Errorf(`type may not contain characters '%s'`, forbiddenCharacters) + } + return nil +} + +func rootOwnedDir(path string) *tar.Header { + var modePerm int64 + if runtime.GOOS == "windows" { + modePerm = 0777 + } else { + modePerm = 0755 + } + return &tar.Header{ + Typeflag: tar.TypeDir, + Name: path, + Mode: modePerm, + } +} + +func typeSymlink(path string) *tar.Header { + var modePerm int64 + if runtime.GOOS == "windows" { + modePerm = 0777 + } else { + modePerm = 0755 + } + return &tar.Header{ + Typeflag: tar.TypeSymlink, + Name: path, + Linkname: launch.LauncherPath, + Mode: modePerm, + } +} diff --git a/vendor/github.com/buildpacks/lifecycle/layers/slices.go b/vendor/github.com/buildpacks/lifecycle/layers/slices.go new file mode 100644 index 0000000000..f095535a9d --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/layers/slices.go @@ -0,0 +1,251 @@ +package layers + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/archive" +) + +type Slice struct { + Paths []string `toml:"paths"` +} + +// SliceLayers divides dir into layers using slices using the following process: +// * Given n slices SliceLayers will return n+1 layers +// * The first n layers will contain files matched by the any Path in the nth Slice +// * The final layer will contain any files in dir that were not included in a previous layer +// Some layers may be empty +func (f *Factory) SliceLayers(dir string, slices []Slice) ([]Layer, error) { + var sliceLayers []Layer + dir, err := filepath.Abs(dir) + if err != nil { + return nil, err + } + sdir, err := newSlicableDir(dir) + if err != nil { + return nil, err + } + + // add one layer per slice + for i, slice := range slices { + layerID := fmt.Sprintf("slice-%d", i+1) + layer, err := f.createLayerFromSlice(slice, sdir, layerID) + if err != nil { + return nil, err + } + sliceLayers = append(sliceLayers, layer) + } + + // add remaining files in a single layer + layerID := fmt.Sprintf("slice-%d", len(slices)+1) + finalLayer, err := f.createLayerFromFiles(layerID, sdir, sdir.remainingFiles()) + if err != nil { + return nil, err + } + return append(sliceLayers, finalLayer), nil +} + +func (f *Factory) createLayerFromSlice(slice Slice, sdir *sliceableDir, layerID string) (Layer, error) { + var matches []string + for _, path := range slice.Paths { + globMatches, err := glob(sdir, path) + if err != nil { + return Layer{}, err + } + matches = append(matches, globMatches...) + } + return f.createLayerFromFiles(layerID, sdir, sdir.sliceFiles(matches)) +} + +func glob(sdir *sliceableDir, pattern string) ([]string, error) { + pattern = filepath.Clean(pattern) + var matches []string + if err := filepath.Walk(sdir.path, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if path == sdir.path { + return nil + } + relPath, err := filepath.Rel(sdir.path, path) + if err != nil { + return err + } + match, err := filepath.Match(pattern, relPath) + if err != nil { + return errors.Wrapf(err, "failed to check if '%s' matches '%s'", relPath, pattern) + } + if match { + matches = append(matches, path) + } + return nil + }); err != nil { + return nil, err + } + return matches, nil +} + +func (f *Factory) createLayerFromFiles(layerID string, sdir *sliceableDir, files []archive.PathInfo) (layer Layer, err error) { + sort.SliceStable(files, func(i, j int) bool { + return files[i].Path < files[j].Path + }) + return f.writeLayer(layerID, func(tw *archive.NormalizingTarWriter) error { + if len(files) != 0 { + if err := archive.AddFilesToArchive(tw, sdir.parentDirs); err != nil { + return err + } + tw.WithUID(f.UID) + tw.WithGID(f.GID) + return archive.AddFilesToArchive(tw, files) + } + return nil + }) +} + +type sliceableDir struct { + path string // path to slicableDir + slicedFiles map[string]bool // map showing which paths are already sliced + pathInfos map[string]os.FileInfo // map of path to file info + subDirs map[string][]string // map dirs to children + parentDirs []archive.PathInfo // parents of the slicableDir +} + +func newSlicableDir(appDir string) (*sliceableDir, error) { + sdir := &sliceableDir{ + path: appDir, + slicedFiles: map[string]bool{}, + pathInfos: map[string]os.FileInfo{}, + subDirs: map[string][]string{}, + } + if err := filepath.Walk(appDir, func(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + sdir.slicedFiles[path] = false + sdir.pathInfos[path] = fi + if fi.IsDir() { + children, err := ioutil.ReadDir(path) + if err != nil { + return err + } + for _, child := range children { + sdir.subDirs[path] = append(sdir.subDirs[path], filepath.Join(path, child.Name())) + } + } + return nil + }); err != nil { + return nil, err + } + parentDirs, err := parents(appDir) + if err != nil { + return nil, err + } + sdir.parentDirs = parentDirs + return sdir, nil +} + +func (sd *sliceableDir) sliceFiles(paths []string) []archive.PathInfo { + slicedFiles := map[string]os.FileInfo{} + for _, match := range paths { + sd.addMatchedFiles(slicedFiles, match) + } + return sd.fillInMissingParents(slicedFiles) +} + +func (sd *sliceableDir) addMatchedFiles(matchedFiles map[string]os.FileInfo, match string) { + if added, ok := sd.slicedFiles[match]; !ok || added { + // don't add files that live outside the app dir + // don't add files were already added + return + } + if children, ok := sd.subDirs[match]; ok { + for _, child := range children { + sd.addMatchedFiles(matchedFiles, child) + } + } + matchedFiles[match] = sd.pathInfos[match] + sd.slicedFiles[match] = true +} + +func (sd *sliceableDir) fillInMissingParents(matchedFiles map[string]os.FileInfo) []archive.PathInfo { + // add parent dirs for matched files if they are missing + var parentToCheck []string + var files []archive.PathInfo + addedParents := map[string]struct{}{} + for path, info := range matchedFiles { + files = append(files, archive.PathInfo{ + Path: path, + Info: info, + }) + parents := sd.fileParents(path) + for _, parent := range parents { + if _, ok := matchedFiles[parent.Path]; ok { + // don't add if it was slices as part of a match + continue + } + if _, ok := addedParents[parent.Path]; ok { + // don't add if it was already added + continue + } + parentToCheck = append(parentToCheck, parent.Path) + addedParents[parent.Path] = struct{}{} + files = append(files, parent) + } + } + + // sort the dirs by their path depth (deepest -> most shallow) so we always process children first + sort.SliceStable(parentToCheck, func(i, j int) bool { + return len(strings.Split(parentToCheck[i], string(os.PathSeparator))) > len(strings.Split(parentToCheck[j], string(os.PathSeparator))) + }) + + // if all children are slices, mark the dir as sliced + for _, dir := range parentToCheck { + allChildrenAdded := true + if children, ok := sd.subDirs[dir]; ok { + for _, child := range children { + if added, ok := sd.slicedFiles[child]; ok && !added { + allChildrenAdded = false + break + } + } + } + sd.slicedFiles[dir] = allChildrenAdded + } + return files +} + +func (sd *sliceableDir) remainingFiles() []archive.PathInfo { + var files []archive.PathInfo + for path, info := range sd.pathInfos { + if added, ok := sd.slicedFiles[path]; !ok || added { + continue + } + files = append(files, archive.PathInfo{ + Path: path, + Info: info, + }) + sd.slicedFiles[path] = true + } + return files +} + +// return parents within the sliceableDir +func (sd *sliceableDir) fileParents(file string) []archive.PathInfo { + parent := filepath.Dir(file) + if parent == sd.path { + return []archive.PathInfo{ + {Path: sd.path, Info: sd.pathInfos[sd.path]}, + } + } + return append(sd.fileParents(parent), archive.PathInfo{ + Path: parent, + Info: sd.pathInfos[parent], + }) +} diff --git a/vendor/github.com/buildpacks/lifecycle/layers/writer.go b/vendor/github.com/buildpacks/lifecycle/layers/writer.go new file mode 100644 index 0000000000..cc6df8dab5 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/layers/writer.go @@ -0,0 +1,46 @@ +package layers + +import ( + "archive/tar" + "crypto/sha256" + "fmt" + "io" + "os" + "runtime" + + "github.com/buildpacks/imgutil/layer" + + "github.com/buildpacks/lifecycle/archive" +) + +type layerWriter struct { + io.Writer + io.Closer + hasher *concurrentHasher + path string +} + +func newFileLayerWriter(dest string) (*layerWriter, error) { + hasher := newConcurrentHasher(sha256.New()) + file, err := os.Create(dest) + if err != nil { + return nil, err + } + w := io.MultiWriter(hasher, file) + return &layerWriter{w, file, hasher, dest}, nil +} + +func (lw *layerWriter) Digest() string { + return fmt.Sprintf("sha256:%x", lw.hasher.Sum(nil)) +} + +func tarWriter(lw *layerWriter) *archive.NormalizingTarWriter { + var tw *archive.NormalizingTarWriter + if runtime.GOOS == "windows" { + tw = archive.NewNormalizingTarWriter(layer.NewWindowsWriter(lw)) + } else { + tw = archive.NewNormalizingTarWriter(tar.NewWriter(lw)) + } + tw.WithModTime(archive.NormalizedModTime) + return tw +} diff --git a/vendor/github.com/buildpacks/lifecycle/lifecycle.toml b/vendor/github.com/buildpacks/lifecycle/lifecycle.toml new file mode 100644 index 0000000000..5fa1d556c4 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/lifecycle.toml @@ -0,0 +1,16 @@ +[apis] +[apis.buildpack] + deprecated = {{.apis_buildpack_deprecated}} + supported = {{.apis_buildpack_supported}} +[apis.platform] + deprecated = {{.apis_platform_deprecated}} + supported = {{.apis_platform_supported}} + +# For backwards compatibility the minimum supported APIs are provided in RFC-0011 format +# https://github.com/buildpacks/rfcs/blob/main/text/0011-lifecycle-descriptor.md +[api] + platform = "0.3" + buildpack = "0.2" + +[lifecycle] + version = "{{.lifecycle_version}}" diff --git a/vendor/github.com/buildpacks/lifecycle/logger.go b/vendor/github.com/buildpacks/lifecycle/logger.go new file mode 100644 index 0000000000..6daaf01902 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/logger.go @@ -0,0 +1,15 @@ +package lifecycle + +type Logger interface { + Debug(msg string) + Debugf(fmt string, v ...interface{}) + + Info(msg string) + Infof(fmt string, v ...interface{}) + + Warn(msg string) + Warnf(fmt string, v ...interface{}) + + Error(msg string) + Errorf(fmt string, v ...interface{}) +} diff --git a/vendor/github.com/buildpacks/lifecycle/platform/cache.go b/vendor/github.com/buildpacks/lifecycle/platform/cache.go new file mode 100644 index 0000000000..8f747314ee --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/platform/cache.go @@ -0,0 +1,17 @@ +package platform + +import "github.com/buildpacks/lifecycle/buildpack" + +type CacheMetadata struct { + BOM LayerMetadata `json:"sbom"` + Buildpacks []buildpack.LayersMetadata `json:"buildpacks"` +} + +func (cm *CacheMetadata) MetadataForBuildpack(id string) buildpack.LayersMetadata { + for _, bpMD := range cm.Buildpacks { + if bpMD.ID == id { + return bpMD + } + } + return buildpack.LayersMetadata{} +} diff --git a/vendor/github.com/buildpacks/lifecycle/platform/exit.go b/vendor/github.com/buildpacks/lifecycle/platform/exit.go new file mode 100644 index 0000000000..816d9e938b --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/platform/exit.go @@ -0,0 +1,93 @@ +package platform + +type LifecycleExitError int + +const CodeFailed = 1 + +const ( + FailedDetect LifecycleExitError = iota + FailedDetectWithErrors // no buildpacks detected + DetectError // no buildpacks detected and at least one errored + AnalyzeError // generic analyze error + RestoreError // generic restore error + FailedBuildWithErrors // buildpack error during /bin/build + BuildError // generic build error + ExportError // generic export error + RebaseError // generic rebase error + LaunchError // generic launch error +) + +type Exiter interface { + CodeFor(errType LifecycleExitError) int +} + +type DefaultExiter struct{} + +var defaultExitCodes = map[LifecycleExitError]int{ + // detect phase errors: 20-29 + FailedDetect: 20, // FailedDetect indicates that no buildpacks detected + FailedDetectWithErrors: 21, // FailedDetectWithErrors indicated that no buildpacks detected and at least one errored + DetectError: 22, // DetectError indicates generic detect error + + // analyze phase errors: 30-39 + AnalyzeError: 32, // AnalyzeError indicates generic analyze error + + // restore phase errors: 40-49 + RestoreError: 42, // RestoreError indicates generic restore error + + // build phase errors: 50-59 + FailedBuildWithErrors: 51, // FailedBuildWithErrors indicates buildpack error during /bin/build + BuildError: 52, // BuildError indicates generic build error + + // export phase errors: 60-69 + ExportError: 62, // ExportError indicates generic export error + + // rebase phase errors: 70-79 + RebaseError: 72, // RebaseError indicates generic rebase error + + // launch phase errors: 80-89 + LaunchError: 82, // LaunchError indicates generic launch error +} + +func (e *DefaultExiter) CodeFor(errType LifecycleExitError) int { + return codeFor(errType, defaultExitCodes) +} + +type LegacyExiter struct{} + +var legacyExitCodes = map[LifecycleExitError]int{ + // detect phase errors: 100-199 + FailedDetect: 100, // FailedDetect indicates that no buildpacks detected + FailedDetectWithErrors: 101, // FailedDetectWithErrors indicated that no buildpacks detected and at least one errored + DetectError: 102, // DetectError indicates generic detect error + + // analyze phase errors: 200-299 + AnalyzeError: 202, // AnalyzeError indicates generic analyze error + + // restore phase errors: 300-399 + RestoreError: 302, // RestoreError indicates generic restore error + + // build phase errors: 400-499 + FailedBuildWithErrors: 401, // FailedBuildWithErrors indicates buildpack error during /bin/build + BuildError: 402, // BuildError indicates generic build error + + // export phase errors: 500-599 + ExportError: 502, // ExportError indicates generic export error + + // rebase phase errors: 600-699 + RebaseError: 602, // RebaseError indicates generic rebase error + + // launch phase errors: 700-799 + LaunchError: 702, // LaunchError indicates generic launch error +} + +func (e *LegacyExiter) CodeFor(errType LifecycleExitError) int { + return codeFor(errType, legacyExitCodes) +} + +func codeFor(errType LifecycleExitError, exitCodes map[LifecycleExitError]int) int { + if code, ok := exitCodes[errType]; ok { + return code + } + return CodeFailed +} diff --git a/vendor/github.com/buildpacks/lifecycle/platform/files.go b/vendor/github.com/buildpacks/lifecycle/platform/files.go new file mode 100644 index 0000000000..2c4de1f283 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/platform/files.go @@ -0,0 +1,234 @@ +// Data Format Files for the platform api spec (https://github.com/buildpacks/spec/blob/main/platform.md#data-format). + +package platform + +import ( + "github.com/google/go-containerregistry/pkg/name" + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/buildpack" + "github.com/buildpacks/lifecycle/launch" + "github.com/buildpacks/lifecycle/layers" +) + +// analyzed.toml + +type AnalyzedMetadata struct { + PreviousImage *ImageIdentifier `toml:"image"` + Metadata LayersMetadata `toml:"metadata"` + RunImage *ImageIdentifier `toml:"run-image,omitempty"` +} + +// FIXME: fix key names to be accurate in the daemon case +type ImageIdentifier struct { + Reference string `toml:"reference"` +} + +// NOTE: This struct MUST be kept in sync with `LayersMetadataCompat` +type LayersMetadata struct { + App []LayerMetadata `json:"app" toml:"app"` + BOM *LayerMetadata `json:"sbom,omitempty" toml:"sbom,omitempty"` + Buildpacks []buildpack.LayersMetadata `json:"buildpacks" toml:"buildpacks"` + Config LayerMetadata `json:"config" toml:"config"` + Launcher LayerMetadata `json:"launcher" toml:"launcher"` + ProcessTypes LayerMetadata `json:"process-types" toml:"process-types"` + RunImage RunImageMetadata `json:"runImage" toml:"run-image"` + Stack StackMetadata `json:"stack" toml:"stack"` +} + +// NOTE: This struct MUST be kept in sync with `LayersMetadata`. +// It exists for situations where the `App` field type cannot be +// guaranteed, yet the original struct data must be maintained. +type LayersMetadataCompat struct { + App interface{} `json:"app" toml:"app"` + BOM *LayerMetadata `json:"sbom,omitempty" toml:"sbom,omitempty"` + Buildpacks []buildpack.LayersMetadata `json:"buildpacks" toml:"buildpacks"` + Config LayerMetadata `json:"config" toml:"config"` + Launcher LayerMetadata `json:"launcher" toml:"launcher"` + ProcessTypes LayerMetadata `json:"process-types" toml:"process-types"` + RunImage RunImageMetadata `json:"runImage" toml:"run-image"` + Stack StackMetadata `json:"stack" toml:"stack"` +} + +func (m *LayersMetadata) MetadataForBuildpack(id string) buildpack.LayersMetadata { + for _, bpMD := range m.Buildpacks { + if bpMD.ID == id { + return bpMD + } + } + return buildpack.LayersMetadata{} +} + +type LayerMetadata struct { + SHA string `json:"sha" toml:"sha"` +} + +type RunImageMetadata struct { + TopLayer string `json:"topLayer" toml:"top-layer"` + Reference string `json:"reference" toml:"reference"` +} + +// metadata.toml + +type BuildMetadata struct { + BOM []buildpack.BOMEntry `toml:"bom" json:"bom"` + Buildpacks []buildpack.GroupBuildpack `toml:"buildpacks" json:"buildpacks"` + Labels []buildpack.Label `toml:"labels" json:"-"` + Launcher LauncherMetadata `toml:"-" json:"launcher"` + Processes []launch.Process `toml:"processes" json:"processes"` + Slices []layers.Slice `toml:"slices" json:"-"` + BuildpackDefaultProcessType string `toml:"buildpack-default-process-type,omitempty" json:"buildpack-default-process-type,omitempty"` +} + +type LauncherMetadata struct { + Version string `json:"version"` + Source SourceMetadata `json:"source"` +} + +type SourceMetadata struct { + Git GitMetadata `json:"git"` +} + +type GitMetadata struct { + Repository string `json:"repository"` + Commit string `json:"commit"` +} + +func (md BuildMetadata) ToLaunchMD() launch.Metadata { + lmd := launch.Metadata{ + Processes: md.Processes, + } + for _, bp := range md.Buildpacks { + lmd.Buildpacks = append(lmd.Buildpacks, launch.Buildpack{ + API: bp.API, + ID: bp.ID, + }) + } + return lmd +} + +// plan.toml + +type BuildPlan struct { + Entries []BuildPlanEntry `toml:"entries"` +} + +func (p BuildPlan) Find(bpID string) buildpack.Plan { + var out []buildpack.Require + for _, entry := range p.Entries { + for _, provider := range entry.Providers { + if provider.ID == bpID { + out = append(out, entry.Requires...) + break + } + } + } + return buildpack.Plan{Entries: out} +} + +// TODO: ensure at least one claimed entry of each name is provided by the BP +func (p BuildPlan) Filter(metRequires []string) BuildPlan { + var out []BuildPlanEntry + for _, planEntry := range p.Entries { + if !containsEntry(metRequires, planEntry) { + out = append(out, planEntry) + } + } + return BuildPlan{Entries: out} +} + +func containsEntry(metRequires []string, entry BuildPlanEntry) bool { + for _, met := range metRequires { + for _, planReq := range entry.Requires { + if met == planReq.Name { + return true + } + } + } + return false +} + +type BuildPlanEntry struct { + Providers []buildpack.GroupBuildpack `toml:"providers"` + Requires []buildpack.Require `toml:"requires"` +} + +func (be BuildPlanEntry) NoOpt() BuildPlanEntry { + var out []buildpack.GroupBuildpack + for _, p := range be.Providers { + out = append(out, p.NoOpt().NoAPI().NoHomepage()) + } + be.Providers = out + return be +} + +// project-metadata.toml + +type ProjectMetadata struct { + Source *ProjectSource `toml:"source" json:"source,omitempty"` +} + +type ProjectSource struct { + Type string `toml:"type" json:"type,omitempty"` + Version map[string]interface{} `toml:"version" json:"version,omitempty"` + Metadata map[string]interface{} `toml:"metadata" json:"metadata,omitempty"` +} + +// report.toml + +type ExportReport struct { + Build BuildReport `toml:"build,omitempty"` + Image ImageReport `toml:"image"` +} + +type BuildReport struct { + BOM []buildpack.BOMEntry `toml:"bom"` +} + +type ImageReport struct { + Tags []string `toml:"tags"` + ImageID string `toml:"image-id,omitempty"` + Digest string `toml:"digest,omitempty"` + ManifestSize int64 `toml:"manifest-size,omitzero"` +} + +// stack.toml + +type StackMetadata struct { + RunImage StackRunImageMetadata `json:"runImage" toml:"run-image"` +} + +type StackRunImageMetadata struct { + Image string `toml:"image" json:"image"` + Mirrors []string `toml:"mirrors" json:"mirrors,omitempty"` +} + +func (sm *StackMetadata) BestRunImageMirror(registry string) (string, error) { + if sm.RunImage.Image == "" { + return "", errors.New("missing run-image metadata") + } + runImageMirrors := []string{sm.RunImage.Image} + runImageMirrors = append(runImageMirrors, sm.RunImage.Mirrors...) + runImageRef, err := byRegistry(registry, runImageMirrors) + if err != nil { + return "", errors.Wrap(err, "failed to find run-image") + } + return runImageRef, nil +} + +func byRegistry(reg string, imgs []string) (string, error) { + if len(imgs) < 1 { + return "", errors.New("no images provided to search") + } + + for _, img := range imgs { + ref, err := name.ParseReference(img, name.WeakValidation) + if err != nil { + continue + } + if reg == ref.Context().RegistryStr() { + return img, nil + } + } + return imgs[0], nil +} diff --git a/vendor/github.com/buildpacks/lifecycle/platform/labels.go b/vendor/github.com/buildpacks/lifecycle/platform/labels.go new file mode 100644 index 0000000000..63de14bf06 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/platform/labels.go @@ -0,0 +1,9 @@ +package platform + +const ( + BuildMetadataLabel = "io.buildpacks.build.metadata" + LayerMetadataLabel = "io.buildpacks.lifecycle.metadata" + ProjectMetadataLabel = "io.buildpacks.project.metadata" + StackIDLabel = "io.buildpacks.stack.id" + MixinsLabel = "io.buildpacks.stack.mixins" +) diff --git a/vendor/github.com/buildpacks/lifecycle/platform/platform.go b/vendor/github.com/buildpacks/lifecycle/platform/platform.go new file mode 100644 index 0000000000..8df91bdb44 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/platform/platform.go @@ -0,0 +1,27 @@ +package platform + +import ( + "github.com/buildpacks/lifecycle/api" +) + +type Platform struct { + Exiter + api *api.Version +} + +func NewPlatform(apiStr string) *Platform { + platform := Platform{ + api: api.MustParse(apiStr), + } + switch apiStr { + case "0.3", "0.4", "0.5": + platform.Exiter = &LegacyExiter{} + default: + platform.Exiter = &DefaultExiter{} + } + return &platform +} + +func (p *Platform) API() *api.Version { + return p.api +} diff --git a/vendor/github.com/buildpacks/lifecycle/rebaser.go b/vendor/github.com/buildpacks/lifecycle/rebaser.go new file mode 100644 index 0000000000..3afd853288 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/rebaser.go @@ -0,0 +1,128 @@ +package lifecycle + +import ( + "encoding/json" + "fmt" + "sort" + "strings" + + "github.com/buildpacks/imgutil" + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/api" + "github.com/buildpacks/lifecycle/image" + "github.com/buildpacks/lifecycle/internal/str" + "github.com/buildpacks/lifecycle/platform" +) + +type Rebaser struct { + Logger Logger + PlatformAPI *api.Version +} + +type RebaseReport struct { + Image platform.ImageReport `toml:"image"` +} + +func (r *Rebaser) Rebase(appImage imgutil.Image, newBaseImage imgutil.Image, additionalNames []string) (RebaseReport, error) { + var origMetadata platform.LayersMetadataCompat + if err := image.DecodeLabel(appImage, platform.LayerMetadataLabel, &origMetadata); err != nil { + return RebaseReport{}, errors.Wrap(err, "get image metadata") + } + + appStackID, err := appImage.Label(platform.StackIDLabel) + if err != nil { + return RebaseReport{}, errors.Wrap(err, "get app image stack") + } + + newBaseStackID, err := newBaseImage.Label(platform.StackIDLabel) + if err != nil { + return RebaseReport{}, errors.Wrap(err, "get new base image stack") + } + + if appStackID == "" { + return RebaseReport{}, errors.New("stack not defined on app image") + } + + if newBaseStackID == "" { + return RebaseReport{}, errors.New("stack not defined on new base image") + } + + if appStackID != newBaseStackID { + return RebaseReport{}, fmt.Errorf("incompatible stack: '%s' is not compatible with '%s'", newBaseStackID, appStackID) + } + + if err := validateMixins(appImage, newBaseImage); err != nil { + return RebaseReport{}, err + } + + if err := appImage.Rebase(origMetadata.RunImage.TopLayer, newBaseImage); err != nil { + return RebaseReport{}, errors.Wrap(err, "rebase app image") + } + + origMetadata.RunImage.TopLayer, err = newBaseImage.TopLayer() + if err != nil { + return RebaseReport{}, errors.Wrap(err, "get rebase run image top layer SHA") + } + + identifier, err := newBaseImage.Identifier() + if err != nil { + return RebaseReport{}, errors.Wrap(err, "get run image id or digest") + } + origMetadata.RunImage.Reference = identifier.String() + + data, err := json.Marshal(origMetadata) + if err != nil { + return RebaseReport{}, errors.Wrap(err, "marshall metadata") + } + + if err := appImage.SetLabel(platform.LayerMetadataLabel, string(data)); err != nil { + return RebaseReport{}, errors.Wrap(err, "set app image metadata label") + } + + hasPrefix := func(l string) bool { return strings.HasPrefix(l, "io.buildpacks.stack.") } + if err := image.SyncLabels(newBaseImage, appImage, hasPrefix); err != nil { + return RebaseReport{}, errors.Wrap(err, "set stack labels") + } + + report := RebaseReport{} + report.Image, err = saveImage(appImage, additionalNames, r.Logger) + if err != nil { + return RebaseReport{}, err + } + if !r.supportsManifestSize() { + // unset manifest size in report.toml for old platform API versions + report.Image.ManifestSize = 0 + } + + return report, err +} + +func validateMixins(appImg, newBaseImg imgutil.Image) error { + var appImageMixins []string + var newBaseImageMixins []string + + if err := image.DecodeLabel(appImg, platform.MixinsLabel, &appImageMixins); err != nil { + return errors.Wrap(err, "get app image mixins") + } + + if err := image.DecodeLabel(newBaseImg, platform.MixinsLabel, &newBaseImageMixins); err != nil { + return errors.Wrap(err, "get run image mixins") + } + + appImageMixins = removeStagePrefixes(appImageMixins) + newBaseImageMixins = removeStagePrefixes(newBaseImageMixins) + + _, missing, _ := str.Compare(newBaseImageMixins, appImageMixins) + + if len(missing) > 0 { + sort.Strings(missing) + return fmt.Errorf("missing required mixin(s): %s", strings.Join(missing, ", ")) + } + + return nil +} + +func (r *Rebaser) supportsManifestSize() bool { + return r.PlatformAPI.AtLeast("0.6") +} diff --git a/vendor/github.com/buildpacks/lifecycle/restorer.go b/vendor/github.com/buildpacks/lifecycle/restorer.go new file mode 100644 index 0000000000..c1583a1396 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/restorer.go @@ -0,0 +1,135 @@ +package lifecycle + +import ( + "path/filepath" + + "github.com/pkg/errors" + "golang.org/x/sync/errgroup" + + "github.com/buildpacks/lifecycle/api" + "github.com/buildpacks/lifecycle/buildpack" + "github.com/buildpacks/lifecycle/internal/layer" + "github.com/buildpacks/lifecycle/layers" + "github.com/buildpacks/lifecycle/platform" +) + +type Restorer struct { + LayersDir string + Logger Logger + + Buildpacks []buildpack.GroupBuildpack + LayerMetadataRestorer layer.MetadataRestorer // Platform API >= 0.7 + LayersMetadata platform.LayersMetadata // Platform API >= 0.7 + Platform Platform + SBOMRestorer layer.SBOMRestorer +} + +// Restore restores metadata for launch and cache layers into the layers directory and attempts to restore layer data for cache=true layers, removing the layer when unsuccessful. +// If a usable cache is not provided, Restore will not restore any cache=true layer metadata. +func (r *Restorer) Restore(cache Cache) error { + cacheMeta, err := retrieveCacheMetadata(cache, r.Logger) + if err != nil { + return err + } + + useShaFiles := !r.restoresLayerMetadata() + layerSHAStore := layer.NewSHAStore(useShaFiles) + if r.restoresLayerMetadata() { + if err := r.LayerMetadataRestorer.Restore(r.Buildpacks, r.LayersMetadata, cacheMeta, layerSHAStore); err != nil { + return err + } + } + + var g errgroup.Group + for _, bp := range r.Buildpacks { + cachedLayers := cacheMeta.MetadataForBuildpack(bp.ID).Layers + + var cachedFn func(buildpack.Layer) bool + if api.MustParse(bp.API).AtLeast("0.6") { + // On Buildpack API 0.6+, the .toml file never contains layer types information. + // The cache metadata is the only way to identify cache=true layers. + cachedFn = func(l buildpack.Layer) bool { + bpLayer, ok := cachedLayers[filepath.Base(l.Path())] + return ok && bpLayer.Cache + } + } else { + // On Buildpack API < 0.6, the .toml file contains layer types information. + // Prefer .toml file to cache metadata in case the cache was cleared between builds and + // the analyzer that wrote the files is on a previous version of the lifecycle, that doesn't cross-reference the cache metadata when writing the files. + // This allows the restorer to cleanup .toml files for layers that are not actually in the cache. + cachedFn = buildpack.MadeCached + } + + buildpackDir, err := buildpack.ReadLayersDir(r.LayersDir, bp, r.Logger) + if err != nil { + return errors.Wrapf(err, "reading buildpack layer directory") + } + foundLayers := buildpackDir.FindLayers(cachedFn) + + for _, bpLayer := range foundLayers { + cachedLayer, exists := cachedLayers[bpLayer.Name()] + if !exists { + r.Logger.Infof("Removing %q, not in cache", bpLayer.Identifier()) + if err := bpLayer.Remove(); err != nil { + return errors.Wrapf(err, "removing layer") + } + continue + } + + layerSha, err := layerSHAStore.Get(bp.ID, bpLayer) + if err != nil { + return err + } + + if layerSha != cachedLayer.SHA { + r.Logger.Infof("Removing %q, wrong sha", bpLayer.Identifier()) + r.Logger.Debugf("Layer sha: %q, cache sha: %q", layerSha, cachedLayer.SHA) + if err := bpLayer.Remove(); err != nil { + return errors.Wrapf(err, "removing layer") + } + } else { + r.Logger.Infof("Restoring data for %q from cache", bpLayer.Identifier()) + g.Go(func() error { + return r.restoreCacheLayer(cache, cachedLayer.SHA) + }) + } + } + } + + if r.Platform.API().AtLeast("0.8") { + g.Go(func() error { + if cacheMeta.BOM.SHA != "" { + r.Logger.Infof("Restoring data for sbom from cache") + if err := r.SBOMRestorer.RestoreFromCache(cache, cacheMeta.BOM.SHA); err != nil { + return err + } + } + return r.SBOMRestorer.RestoreToBuildpackLayers(r.Buildpacks) + }) + } + + if err := g.Wait(); err != nil { + return errors.Wrap(err, "restoring data") + } + + return nil +} + +func (r *Restorer) restoresLayerMetadata() bool { + return r.Platform.API().AtLeast("0.7") +} + +func (r *Restorer) restoreCacheLayer(cache Cache, sha string) error { + // Sanity check to prevent panic. + if cache == nil { + return errors.New("restoring layer: cache not provided") + } + r.Logger.Debugf("Retrieving data for %q", sha) + rc, err := cache.RetrieveLayer(sha) + if err != nil { + return err + } + defer rc.Close() + + return layers.Extract(rc, "") +} diff --git a/vendor/github.com/buildpacks/lifecycle/save.go b/vendor/github.com/buildpacks/lifecycle/save.go new file mode 100644 index 0000000000..787a41d352 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/save.go @@ -0,0 +1,94 @@ +package lifecycle + +import ( + "fmt" + + "github.com/buildpacks/imgutil" + "github.com/buildpacks/imgutil/local" + "github.com/buildpacks/imgutil/remote" + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/platform" +) + +func saveImage(image imgutil.Image, additionalNames []string, logger Logger) (platform.ImageReport, error) { + var saveErr error + imageReport := platform.ImageReport{} + logger.Infof("Saving %s...\n", image.Name()) + if err := image.Save(additionalNames...); err != nil { + var ok bool + if saveErr, ok = err.(imgutil.SaveError); !ok { + return platform.ImageReport{}, errors.Wrap(err, "saving image") + } + } + + id, idErr := image.Identifier() + if idErr != nil { + if saveErr != nil { + return platform.ImageReport{}, &MultiError{Errors: []error{idErr, saveErr}} + } + return platform.ImageReport{}, idErr + } + + logger.Infof("*** Images (%s):\n", shortID(id)) + for _, n := range append([]string{image.Name()}, additionalNames...) { + if ok, message := getSaveStatus(saveErr, n); !ok { + logger.Infof(" %s - %s\n", n, message) + } else { + logger.Infof(" %s\n", n) + imageReport.Tags = append(imageReport.Tags, n) + } + } + switch v := id.(type) { + case local.IDIdentifier: + imageReport.ImageID = v.String() + logger.Debugf("\n*** Image ID: %s\n", v.String()) + case remote.DigestIdentifier: + imageReport.Digest = v.Digest.DigestStr() + logger.Debugf("\n*** Digest: %s\n", v.Digest.DigestStr()) + default: + } + + manifestSize, sizeErr := image.ManifestSize() + if sizeErr != nil { + // ignore the manifest size if it's unavailable + logger.Infof("*** Manifest size is unavailable: %s\n", sizeErr.Error()) + } else if manifestSize != 0 { + imageReport.ManifestSize = manifestSize + logger.Debugf("\n*** Manifest Size: %d\n", manifestSize) + } + + return imageReport, saveErr +} + +type MultiError struct { + Errors []error +} + +func (me *MultiError) Error() string { + return fmt.Sprintf("failed with multiple errors %+v", me.Errors) +} + +func shortID(identifier imgutil.Identifier) string { + switch v := identifier.(type) { + case local.IDIdentifier: + return TruncateSha(v.String()) + case remote.DigestIdentifier: + return v.Digest.DigestStr() + default: + return v.String() + } +} + +func getSaveStatus(err error, imageName string) (bool, string) { + if err != nil { + if saveErr, ok := err.(imgutil.SaveError); ok { + for _, d := range saveErr.Errors { + if d.ImageName == imageName { + return false, d.Cause.Error() + } + } + } + } + return true, "" +} diff --git a/vendor/github.com/buildpacks/lifecycle/utils.go b/vendor/github.com/buildpacks/lifecycle/utils.go new file mode 100644 index 0000000000..90a513fd14 --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/utils.go @@ -0,0 +1,58 @@ +package lifecycle + +import ( + "os" + "path/filepath" + "strings" + + "github.com/BurntSushi/toml" + + "github.com/buildpacks/lifecycle/buildpack" +) + +func WriteTOML(path string, data interface{}) error { + if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil { + return err + } + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + return toml.NewEncoder(f).Encode(data) +} + +func ReadGroup(path string) (buildpack.Group, error) { + var group buildpack.Group + _, err := toml.DecodeFile(path, &group) + return group, err +} + +func ReadOrder(path string) (buildpack.Order, error) { + var order struct { + Order buildpack.Order `toml:"order"` + } + _, err := toml.DecodeFile(path, &order) + return order.Order, err +} + +func TruncateSha(sha string) string { + rawSha := strings.TrimPrefix(sha, "sha256:") + if len(sha) > 12 { + return rawSha[0:12] + } + return rawSha +} + +func removeStagePrefixes(mixins []string) []string { + var result []string + for _, m := range mixins { + s := strings.SplitN(m, ":", 2) + if len(s) == 1 { + result = append(result, s[0]) + } else { + result = append(result, s[1]) + } + } + return result +} diff --git a/vendor/github.com/buildpacks/pack/.gitignore b/vendor/github.com/buildpacks/pack/.gitignore new file mode 100644 index 0000000000..30eb369432 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/.gitignore @@ -0,0 +1,16 @@ +.pack/ +/pack +out/ +benchmarks.test +*.out + +# Jetbrains Goland +.idea/ + +# Build outputs +artifacts/ +.DS_Store + +# Travis unencrypted file +.travis/key.pem + diff --git a/vendor/github.com/buildpacks/pack/.gitpod.yml b/vendor/github.com/buildpacks/pack/.gitpod.yml new file mode 100644 index 0000000000..b716e56cd5 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/.gitpod.yml @@ -0,0 +1,18 @@ + +tasks: + - name: Setup + before: chmod ugo+w /var/run/docker.sock + init: make build + command: chmod ugo+w /var/run/docker.sock + +github: + prebuilds: + master: true + branches: true + pullRequests: true + pullRequestsFromForks: true + addCheck: true + +vscode: + extensions: + - golang.go diff --git a/vendor/github.com/buildpacks/pack/CODEOWNERS b/vendor/github.com/buildpacks/pack/CODEOWNERS new file mode 100644 index 0000000000..3daccc65fa --- /dev/null +++ b/vendor/github.com/buildpacks/pack/CODEOWNERS @@ -0,0 +1 @@ +* @buildpacks/platform-maintainers diff --git a/vendor/github.com/buildpacks/pack/CONTRIBUTING.md b/vendor/github.com/buildpacks/pack/CONTRIBUTING.md new file mode 100644 index 0000000000..1cdbc6aef1 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/CONTRIBUTING.md @@ -0,0 +1,84 @@ +We're glad you are interested in contributing to this project. We hope that this +document helps you get started. + +## Policies + +This repository adheres to the following project policies: + +- [Code of Conduct][code-of-conduct] - How we should act with each other. +- [Contributing][contributing] - General contributing standards. +- [Security][security] - Reporting security concerns. +- [Support][support] - Getting support. + +## Contributing to this repository + +### Development + +Aside from the policies above, you may find [DEVELOPMENT.md](DEVELOPMENT.md) to provide specific helpful detail +to assist you while developing in this repository. + +We welcome any and all contributions! The issues we are prioritizing are visible in the repository [milestones](https://github.com/buildpacks/pack/milestones), and we especially welcome contributions to that. One good place to start contributing is in our [documentation](https://github.com/buildpacks/docs/issues), though you are welcome to start in this repository! + +#### Preparing for a Pull Request + +After making all the changes but before creating a [Pull Request][pull-request-process], you should run +`make prepare-for-pr`. This command runs a set of other tasks that resolve or report any simple issues that would +otherwise arise during the pull request review process. + +### User Acceptance on a Pull Request + +Running user acceptance on a pull request is just as critical as reviewing the code changes. It allows you, a contributor and user, direct insight into how a feature works and allows for you to provide feedback into what could be improved. + +#### Downloading PR binaries + +1. On GitHub's Pull Request view, click on the **Checks** tab. +2. On the top-right, click **Artifacts**. +3. Click on the zip file for the platform you are running. + +#### Setup + +1. Unzip binary: + ```shell + unzip pack-{{PLATFORM}}.zip + ``` +2. Enable execution of binary _(macOS/Linux only)_: + ```shell + chmod +x ./pack + ``` + + > For macOS, you might need to allow your terminal to be able to execute applications from unverified developers. See [Apple Support](https://support.apple.com/en-us/HT202491). + > + > A quick solution is to add exception to the downloaded pack binary: `sudo spctl --add -v ./pack` +3. You should now be able to execute pack via: + - macOS: `./pack` + - Linux: `./pack` + - Windows: `pack.exe` + + +#### Writing Feedback + +When providing feedback please provide a succinct title, a summary of the observation, what you expected, and some output or screenshots. + +Here's a simple template you can use: + +```text + +#### + + + +###### Expected + + + +###### Output + + +``` + + +[code-of-conduct]: https://github.com/buildpacks/.github/blob/main/CODE_OF_CONDUCT.md +[contributing]: https://github.com/buildpacks/.github/blob/main/CONTRIBUTING.md +[security]: https://github.com/buildpacks/.github/blob/main/SECURITY.md +[support]: https://github.com/buildpacks/.github/blob/main/SUPPORT.md +[pull-request-process]: https://github.com/buildpacks/.github/blob/main/CONTRIBUTIONS.md#pull-request-process diff --git a/vendor/github.com/buildpacks/pack/DEVELOPMENT.md b/vendor/github.com/buildpacks/pack/DEVELOPMENT.md new file mode 100644 index 0000000000..16e3e3548a --- /dev/null +++ b/vendor/github.com/buildpacks/pack/DEVELOPMENT.md @@ -0,0 +1,114 @@ +# Development + +## Prerequisites + +* [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) + * macOS: _(built-in)_ + * Windows: + * `choco install git -y` + * `git config --global core.autocrlf false` +* [Go](https://golang.org/doc/install) + * macOS: `brew install go` + * Windows: `choco install golang -y` +* [Docker](https://www.docker.com/products/docker-desktop) +* Make (and build tools) + * macOS: `xcode-select --install` + * Windows: + * `choco install cygwin make -y` + * `[Environment]::SetEnvironmentVariable("PATH", "C:\tools\cygwin\bin;$ENV:PATH", "MACHINE")` + +Alternatively, you can use Gitpod to run pre-configured dev envrionment in the cloud right from your browser + +[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/buildpacks/pack) + + +### Windows Caveats + +* Symlinks - Some of our tests attempt to create symlinks. On Windows, this requires the [permission to be provided](https://stackoverflow.com/a/24353758). + +## Tasks + +### Building + +To build pack: +``` +make build +``` + +This will output the binary to the directory `out/`. + +Options: + +| ENV_VAR | Description | Default | +|--------------|------------------------------------------------------------------------|---------| +| GOCMD | Change the `go` executable. For example, [richgo][rgo] for testing. | go | +| PACK_BIN | Change the name or location of the binary relative to `out/`. | pack | +| PACK_VERSION | Tell `pack` what version to consider itself | `dev` | + +[rgo]: https://github.com/kyoh86/richgo + +_NOTE: This project uses [go modules](https://github.com/golang/go/wiki/Modules) for dependency management._ + +### Testing + +To run unit and integration tests: +```shell +make unit +``` + +To run acceptance tests: +```shell +make acceptance +``` + +Alternately, to run all tests: +```shell +make test +``` + +To run our full acceptance suite (including cross-compatibility for n-1 `pack` and `lifecycl`): +```shell +make acceptance-all +``` + +### Tidy + +To format the code: +```shell +make format +``` + +To tidy up the codebase and dependencies: +```shell +make tidy +``` + +### Verification + +To verify formatting and code quality: +```shell +make verify +``` + +### Prepare for PR + +Runs various checks to ensure compliance: +```shell +make prepare-for-pr +``` + +### Acceptance Tests +Some options users can provide to our acceptance tests are: + +| ENV_VAR | Description | Default | +|--------------|------------------------------------------------------------------------|---------| +| ACCEPTANCE_SUITE_CONFIG | A set of configurations for how to run the acceptance tests, describing the version of `pack` used for testing, the version of `pack` used to create the builders used in the test, and the version of `lifecycle` binaries used to test with Github | `[{"pack": "current", "pack_create_builder": "current", "lifecycle": "default"}]'` | +| COMPILE_PACK_WITH_VERSION | Tell `pack` what version to consider itself | `dev` | +| GITHUB_TOKEN | A Github Token, used when downloading `pack` and `lifecycle` releases from Github during the test setup | "" | +| LIFECYCLE_IMAGE | Image reference to be used in untrusted builder workflows | buildpacksio/lifecycle: | +| LIFECYCLE_PATH | Path to a `.tgz` file filled with a set of `lifecycle` binaries | The Github release for the default version of lifecycle in `pack` | +| PACK_PATH | Path to a `pack` executable. | A compiled version of the current branch | +| PREVIOUS_LIFECYCLE_IMAGE | Image reference to be used in untrusted builder workflows, used to test compatibility of `pack` with the n-1 version of the `lifecycle` | buildpacksio/lifecycle:, buildpacksio/lifecycle: | +| PREVIOUS_LIFECYCLE_PATH | Path to a `.tgz` file filled with a set of `lifecycle` binaries, used to test compatibility of `pack` with the n-1 version of the `lifecycle` | The Github release for n-1 release of `lifecycle` | +| PREVIOUS_PACK_FIXTURES_PATH | Path to a set of fixtures, used to override the most up-to-date fixtures, in case of changed functionality | `acceptance/testdata/pack_previous_fixtures_overrides` | +| PREVIOUS_PACK_PATH | Path to a `pack` executable, used to test compatibility with n-1 version of `pack` | The most recent release from `pack`'s Github release | diff --git a/vendor/github.com/buildpacks/pack/LICENSE b/vendor/github.com/buildpacks/pack/LICENSE new file mode 100644 index 0000000000..44eae60e00 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/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 2018 The Cloud Native Buildpacks Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/buildpacks/pack/Makefile b/vendor/github.com/buildpacks/pack/Makefile new file mode 100644 index 0000000000..707a27e633 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/Makefile @@ -0,0 +1,158 @@ +ifeq ($(OS),Windows_NT) +SHELL:=cmd.exe + +# Need BLANK due to makefile parsing of `\` +# (see: https://stackoverflow.com/questions/54733231/how-to-escape-a-backslash-in-the-end-to-mean-literal-backslash-in-makefile/54733416#54733416) +BLANK:= + +# Define variable named `/` to represent OS path separator (usable as `$/` in this file) +/:=\$(BLANK) +CAT=type +RMRF=rmdir /q /s +SRC=$(shell dir /q /s /b *.go | findstr /v $/out$/) +GOIMPORTS_DIFF_OPTION="-l" # Windows can't do diff-mode because it's missing the "diff" binary +PACK_BIN?=pack.exe +else +/:=/ +CAT=cat +RMRF=rm -rf +SRC=$(shell find . -type f -name '*.go' -not -path "*/out/*") +GOIMPORTS_DIFF_OPTION:="-d" +PACK_BIN?=pack +endif + +ACCEPTANCE_TIMEOUT?=$(TEST_TIMEOUT) +ARCHIVE_NAME=pack-$(PACK_VERSION) +GOCMD?=go +GOFLAGS?= +GOTESTFLAGS?=-v -count=1 -parallel=1 +PACKAGE_BASE=github.com/buildpacks/pack +PACK_GITSHA1=$(shell git rev-parse --short=7 HEAD) +PACK_VERSION?=0.0.0 +TEST_TIMEOUT?=1200s +UNIT_TIMEOUT?=$(TEST_TIMEOUT) +NO_DOCKER?= + +clean_build := $(strip ${PACK_BUILD}) +clean_sha := $(strip ${PACK_GITSHA1}) + +# append build number and git sha to version, if not-empty +ifneq ($(and $(clean_build),$(clean_sha)),) +PACK_VERSION:=${PACK_VERSION}+git-${clean_sha}.build-${clean_build} +else ifneq ($(clean_build),) +PACK_VERSION:=${PACK_VERSION}+build-${clean_build} +else ifneq ($(clean_sha),) +PACK_VERSION:=${PACK_VERSION}+git-${clean_sha} +endif + +export GOFLAGS:=$(GOFLAGS) +export CGO_ENABLED=0 + +BINDIR:=/usr/bin/ + +# this target must be listed first in order for it to be a defualt target, +# so that ubuntu_ppa's may be constructed using default build tools. +build: out + @echo "> Building..." + $(GOCMD) build -ldflags "-s -w -X 'github.com/buildpacks/pack.Version=${PACK_VERSION}' -extldflags ${LDFLAGS}" -trimpath -o ./out/$(PACK_BIN) -a ./cmd/pack + +all: clean verify test build + +# used by apt-get install when installing ubuntu ppa. +# move pack binary onto a path location. +install: + mkdir -p ${DESTDIR}${BINDIR} + cp ./out/$(PACK_BIN) ${DESTDIR}${BINDIR}/ + +mod-tidy: + $(GOCMD) mod tidy + cd tools && $(GOCMD) mod tidy + +tidy: mod-tidy format + +package: out + tar czf .$/out$/$(ARCHIVE_NAME).tgz -C .$/out$/ $(PACK_BIN) + +install-mockgen: + @echo "> Installing mockgen..." + cd tools && $(GOCMD) install github.com/golang/mock/mockgen + +install-goimports: + @echo "> Installing goimports..." + cd tools && $(GOCMD) install golang.org/x/tools/cmd/goimports + +format: install-goimports + @echo "> Formating code..." + @goimports -l -w -local ${PACKAGE_BASE} ${SRC} + @go run tools/pedantic_imports/main.go ${PACKAGE_BASE} ${SRC} + +install-golangci-lint: + @echo "> Installing golangci-lint..." + cd tools && $(GOCMD) install github.com/golangci/golangci-lint/cmd/golangci-lint + +lint: install-golangci-lint + @echo "> Linting code..." + @golangci-lint run -c golangci.yaml + +test: unit acceptance + +# append coverage arguments +ifeq ($(TEST_COVERAGE), 1) +unit: GOTESTFLAGS:=$(GOTESTFLAGS) -coverprofile=./out/tests/coverage-unit.txt -covermode=atomic +endif +ifeq ($(NO_DOCKER),) +unit: GOTESTFLAGS:=$(GOTESTFLAGS) --tags=example +endif +unit: out + @echo "> Running unit/integration tests..." + $(GOCMD) test $(GOTESTFLAGS) -timeout=$(UNIT_TIMEOUT) ./... + +acceptance: out + @echo "> Running acceptance tests..." + $(GOCMD) test $(GOTESTFLAGS) -timeout=$(ACCEPTANCE_TIMEOUT) -tags=acceptance ./acceptance + +acceptance-all: export ACCEPTANCE_SUITE_CONFIG:=$(shell $(CAT) .$/acceptance$/testconfig$/all.json) +acceptance-all: + @echo "> Running acceptance tests..." + $(GOCMD) test $(GOTESTFLAGS) -timeout=$(ACCEPTANCE_TIMEOUT) -tags=acceptance ./acceptance + +clean: + @echo "> Cleaning workspace..." + @$(RMRF) .$/out benchmarks.test || (exit 0) + +verify: verify-format lint + +generate: install-mockgen + @echo "> Generating mocks..." + $(GOCMD) generate ./... + +verify-format: install-goimports + @echo "> Verifying format..." + $(if $(shell goimports -l -local ${PACKAGE_BASE} ${SRC}), @echo ERROR: Format verification failed! && goimports ${GOIMPORTS_DIFF_OPTION} -local ${PACKAGE_BASE} ${SRC} && exit 1) + +prepare-for-pr: tidy verify test + @git diff-index --quiet HEAD -- ||\ + (echo "-----------------" &&\ + echo "NOTICE: There are some files that have not been committed." &&\ + echo "-----------------\n" &&\ + git status &&\ + echo "\n-----------------" &&\ + echo "NOTICE: There are some files that have not been committed." &&\ + echo "-----------------\n" &&\ + exit 0) + +benchmark: out + @echo "> Running Benchmarks" + $(GOCMD) test -run=^$ -bench=. -benchtime=1s -benchmem -memprofile=./out/bench_mem.out -cpuprofile=./out/bench_cpu.out -tags=benchmarks ./benchmarks/ -v +# NOTE: You can analyze the results, using go tool pprof. For instance, you can start a server to see a graph of the cpu usage by running +# go tool pprof -http=":8082" out/bench_cpu.out. Alternatively, you can run go tool pprof, and in the ensuing cli, run +# commands like top10 or web to dig down into the cpu and memory usage +# For more, see https://blog.golang.org/pprof + +# NOTE: Windows doesn't support `-p` +out: + @mkdir out || (exit 0) + mkdir out$/tests || (exit 0) + + +.PHONY: clean build format imports lint test unit acceptance prepare-for-pr verify verify-format benchmark diff --git a/vendor/github.com/buildpacks/pack/README.md b/vendor/github.com/buildpacks/pack/README.md new file mode 100644 index 0000000000..495172845d --- /dev/null +++ b/vendor/github.com/buildpacks/pack/README.md @@ -0,0 +1,45 @@ +# pack - Buildpack CLI + +[![Build results](https://github.com/buildpacks/pack/workflows/build/badge.svg)](https://github.com/buildpacks/pack/actions) +[![Go Report Card](https://goreportcard.com/badge/github.com/buildpacks/pack)](https://goreportcard.com/report/github.com/buildpacks/pack) +[![codecov](https://codecov.io/gh/buildpacks/pack/branch/main/graph/badge.svg)](https://codecov.io/gh/buildpacks/pack) +[![GoDoc](https://godoc.org/github.com/buildpacks/pack?status.svg)](https://godoc.org/github.com/buildpacks/pack) +[![GitHub license](https://img.shields.io/github/license/buildpacks/pack)](https://github.com/buildpacks/pack/blob/main/LICENSE) +[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/4748/badge)](https://bestpractices.coreinfrastructure.org/projects/4748) +[![Slack](https://img.shields.io/badge/slack-join-ff69b4.svg?logo=slack)](https://slack.buildpacks.io/) +[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/buildpacks/pack) + +`pack` makes it easy for... +- [**App Developers**][app-dev] to use buildpacks to convert code into runnable images. +- [**Buildpack Authors**][bp-author] to develop and package buildpacks for distribution. +- [**Operators**][operator] to package buildpacks for distribution and maintain applications. + +## Usage + + + +## Getting Started +Get started by running through our tutorial: [An App’s Brief Journey from Source to Image][getting-started] + +## Contributing +- [CONTRIBUTING](CONTRIBUTING.md) - Information on how to contribute, including the pull request process. +- [DEVELOPMENT](DEVELOPMENT.md) - Further detail to help you during the development process. +- [RELEASE](RELEASE.md) - Further details about our release process. + +## Documentation +Check out the command line documentation [here][pack-docs] + +## Specifications +`pack` is a CLI implementation of the [Platform Interface Specification][platform-spec] for [Cloud Native Buildpacks][buildpacks.io]. + +To learn more about the details, check out the [specs repository][specs]. + +[app-dev]: https://buildpacks.io/docs/app-developer-guide/ +[bp-author]: https://buildpacks.io/docs/buildpack-author-guide/ +[operator]: https://buildpacks.io/docs/operator-guide/ +[buildpacks.io]: https://buildpacks.io/ +[install-pack]: https://buildpacks.io/docs/install-pack/ +[getting-started]: https://buildpacks.io/docs/app-journey +[specs]: https://github.com/buildpacks/spec/ +[platform-spec]: https://github.com/buildpacks/spec/blob/main/platform.md +[pack-docs]: https://buildpacks.io/docs/tools/pack/cli/pack/ diff --git a/vendor/github.com/buildpacks/pack/RELEASE.md b/vendor/github.com/buildpacks/pack/RELEASE.md new file mode 100644 index 0000000000..387ddcac03 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/RELEASE.md @@ -0,0 +1,66 @@ +# Release Process + +Pack follows a 6 week release cadence, composed of 3 phases: + - [Development](#development) + - [Feature Complete](#feature-complete) + - [Release Finalization](#release-finalization) + +## Roles + +#### Release Manager + +One of the [maintainers][maintainers] is designated as the release manager. They communicate the release status to the working group meetings, schedule additional meetings with the `pack` [maintainers][maintainers] as needed, and finalize the release. They also take care of whatever release needs may arise. + +## Phases + +### Development + +Our development flow is detailed in [Development](DEVELOPMENT.md). + +### Feature Complete + +5 business days prior to a scheduled release, we enter `feature complete`. + +During this period, a **Release Candidate** (RC) is published and used for further User Acceptance testing (`UAT`). Furthermore, additional RCs may be published based on assessment by the `pack` [maintainers][maintainers] of the **impact**, **effort** and **risk** of including the changes in the upcoming release. Any other changes may be merged into the `main` branch through the normal process, and will make it into the next release. + +To produce the release candidate the [release manager](#release-manager) will: +- Create a new release branch in form `release/-rc` yielding a draft GitHub release to be published. +- Publish the [GitHub release][release]: + - Tag release branch as `v-rc`. + - Release should be marked as "pre-release". + - The GitHub release will contain the following: + - **artifacts** + - **release notes** + - The release notes should be edited and cleaned +- Merge the release branch into `main`. + +### Release Finalization + +The [release manager](#release-manager) will: +- Create a new release branch in form `release/` yielding a draft GitHub release to be published. +- Publish the [GitHub release][release] while tagging the release branch as `v`. + - Tag release branch as `v`. + - The GitHub release will contain the following: + - **artifacts** + - **release notes** + - **migration guide** (if necessary) +- Merge the release branch into `main`. +- Send out release notifications, if deemed necessary, on + - The [cncf-buildpacks mailing list](https://lists.cncf.io/g/cncf-buildpacks) + - Twitter +- Post release, you should be able to remove any acceptance test constraints (in [acceptance/invoke/pack.go](acceptance/invoke/pack.go)) in the `featureTests` struct. Create a PR removing them, in order to ensure our acceptance tests are clean. + +And with that, you're done! + +## Manual Releasing + +We release pack to a number of systems, including `homebrew`, `docker`, and `archlinux`. All of our delivery pipelines +have workflow_dispatch triggers, if a maintainer needs to manually trigger them. To activate it, go to the +[actions page](https://github.com/buildpacks/pack/actions), and select the desired workflow. Run it by providing the pack +version to release, in the format `v`. + +_For more information, see the [release process RFC][release-process]_ + +[maintainers]: https://github.com/buildpacks/community/blob/main/TEAMS.md#platform-team +[release-process]: https://github.com/buildpacks/rfcs/blob/main/text/0039-release-process.md#change-control-board +[release]: https://github.com/buildpacks/pack/releases diff --git a/vendor/github.com/buildpacks/pack/builder/config_reader.go b/vendor/github.com/buildpacks/pack/builder/config_reader.go new file mode 100644 index 0000000000..39ffc4fba0 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/builder/config_reader.go @@ -0,0 +1,113 @@ +package builder + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/BurntSushi/toml" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/config" + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/dist" +) + +// Config is a builder configuration file +type Config struct { + Description string `toml:"description"` + Buildpacks BuildpackCollection `toml:"buildpacks"` + Order dist.Order `toml:"order"` + Stack StackConfig `toml:"stack"` + Lifecycle LifecycleConfig `toml:"lifecycle"` +} + +// BuildpackCollection is a list of BuildpackConfigs +type BuildpackCollection []BuildpackConfig + +// BuildpackConfig details the configuration of a Buildpack +type BuildpackConfig struct { + dist.BuildpackInfo + dist.ImageOrURI +} + +func (c *BuildpackConfig) DisplayString() string { + if c.BuildpackInfo.FullName() != "" { + return c.BuildpackInfo.FullName() + } + + return c.ImageOrURI.DisplayString() +} + +// StackConfig details the configuration of a Stack +type StackConfig struct { + ID string `toml:"id"` + BuildImage string `toml:"build-image"` + RunImage string `toml:"run-image"` + RunImageMirrors []string `toml:"run-image-mirrors,omitempty"` +} + +// LifecycleConfig details the configuration of the Lifecycle +type LifecycleConfig struct { + URI string `toml:"uri"` + Version string `toml:"version"` +} + +// ReadConfig reads a builder configuration from the file path provided and returns the +// configuration along with any warnings encountered while parsing +func ReadConfig(path string) (config Config, warnings []string, err error) { + file, err := os.Open(filepath.Clean(path)) + if err != nil { + return Config{}, nil, errors.Wrap(err, "opening config file") + } + defer file.Close() + + config, err = parseConfig(file) + if err != nil { + return Config{}, nil, errors.Wrapf(err, "parse contents of '%s'", path) + } + + if len(config.Order) == 0 { + warnings = append(warnings, fmt.Sprintf("empty %s definition", style.Symbol("order"))) + } + + return config, warnings, nil +} + +// ValidateConfig validates the config +func ValidateConfig(c Config) error { + if c.Stack.ID == "" { + return errors.New("stack.id is required") + } + + if c.Stack.BuildImage == "" { + return errors.New("stack.build-image is required") + } + + if c.Stack.RunImage == "" { + return errors.New("stack.run-image is required") + } + + return nil +} + +// parseConfig reads a builder configuration from file +func parseConfig(file *os.File) (Config, error) { + builderConfig := Config{} + tomlMetadata, err := toml.NewDecoder(file).Decode(&builderConfig) + if err != nil { + return Config{}, errors.Wrap(err, "decoding toml contents") + } + + undecodedKeys := tomlMetadata.Undecoded() + if len(undecodedKeys) > 0 { + unknownElementsMsg := config.FormatUndecodedKeys(undecodedKeys) + + return Config{}, errors.Errorf("%s in %s", + unknownElementsMsg, + style.Symbol(file.Name()), + ) + } + + return builderConfig, nil +} diff --git a/vendor/github.com/buildpacks/pack/builder/detection_order.go b/vendor/github.com/buildpacks/pack/builder/detection_order.go new file mode 100644 index 0000000000..2063f02645 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/builder/detection_order.go @@ -0,0 +1,18 @@ +package builder + +import ( + "github.com/buildpacks/pack/pkg/dist" +) + +type DetectionOrderEntry struct { + dist.BuildpackRef `yaml:",inline"` + Cyclical bool `json:"cyclic,omitempty" yaml:"cyclic,omitempty" toml:"cyclic,omitempty"` + GroupDetectionOrder DetectionOrder `json:"buildpacks,omitempty" yaml:"buildpacks,omitempty" toml:"buildpacks,omitempty"` +} + +type DetectionOrder []DetectionOrderEntry + +const ( + OrderDetectionMaxDepth = -1 + OrderDetectionNone = 0 +) diff --git a/vendor/github.com/buildpacks/pack/buildpack.yml b/vendor/github.com/buildpacks/pack/buildpack.yml new file mode 100644 index 0000000000..4a48bef943 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/buildpack.yml @@ -0,0 +1,9 @@ +go: + targets: + - ./cmd/pack + + build: + # The go.build.flags property allows you to override the default build + # flags when compiling your program. + flags: + - -ldflags="-s -w -X 'github.com/buildpacks/pack.Version=#{PACK_VERSION}#'" diff --git a/vendor/github.com/buildpacks/pack/buildpackage/config_reader.go b/vendor/github.com/buildpacks/pack/buildpackage/config_reader.go new file mode 100644 index 0000000000..8dbbafa276 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/buildpackage/config_reader.go @@ -0,0 +1,116 @@ +package buildpackage + +import ( + "path/filepath" + + "github.com/BurntSushi/toml" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/config" + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/buildpack" + "github.com/buildpacks/pack/pkg/dist" +) + +const defaultOS = "linux" + +// Config encapsulates the possible configuration options for buildpackage creation. +type Config struct { + Buildpack dist.BuildpackURI `toml:"buildpack"` + Dependencies []dist.ImageOrURI `toml:"dependencies"` + Platform dist.Platform `toml:"platform"` +} + +func DefaultConfig() Config { + return Config{ + Buildpack: dist.BuildpackURI{ + URI: ".", + }, + Platform: dist.Platform{ + OS: defaultOS, + }, + } +} + +// NewConfigReader returns an instance of ConfigReader. It does not take any parameters. +func NewConfigReader() *ConfigReader { + return &ConfigReader{} +} + +// ConfigReader implements a Read method for buildpackage configuration which parses and validates buildpackage +// configuration from a toml file. +type ConfigReader struct{} + +// Read reads and validates a buildpackage configuration from the file path provided and returns the +// configuration and any error that occurred during reading or validation. +func (r *ConfigReader) Read(path string) (Config, error) { + packageConfig := Config{} + + tomlMetadata, err := toml.DecodeFile(path, &packageConfig) + if err != nil { + return packageConfig, errors.Wrap(err, "decoding toml") + } + + undecodedKeys := tomlMetadata.Undecoded() + if len(undecodedKeys) > 0 { + unknownElementsMsg := config.FormatUndecodedKeys(undecodedKeys) + + return packageConfig, errors.Errorf("%s in %s", + unknownElementsMsg, + style.Symbol(path), + ) + } + + if packageConfig.Buildpack.URI == "" { + return packageConfig, errors.Errorf("missing %s configuration", style.Symbol("buildpack.uri")) + } + + if packageConfig.Platform.OS == "" { + packageConfig.Platform.OS = defaultOS + } + + if packageConfig.Platform.OS != "linux" && packageConfig.Platform.OS != "windows" { + return packageConfig, errors.Errorf("invalid %s configuration: only [%s, %s] is permitted, found %s", + style.Symbol("platform.os"), style.Symbol("linux"), style.Symbol("windows"), style.Symbol(packageConfig.Platform.OS)) + } + + configDir, err := filepath.Abs(filepath.Dir(path)) + if err != nil { + return packageConfig, err + } + + if err := validateURI(packageConfig.Buildpack.URI, configDir); err != nil { + return packageConfig, err + } + + for _, dep := range packageConfig.Dependencies { + if dep.URI != "" && dep.ImageName != "" { + return packageConfig, errors.Errorf( + "dependency configured with both %s and %s", + style.Symbol("uri"), + style.Symbol("image"), + ) + } + + if dep.URI != "" { + if err := validateURI(dep.URI, configDir); err != nil { + return packageConfig, err + } + } + } + + return packageConfig, nil +} + +func validateURI(uri, relativeBaseDir string) error { + locatorType, err := buildpack.GetLocatorType(uri, relativeBaseDir, nil) + if err != nil { + return err + } + + if locatorType == buildpack.InvalidLocator { + return errors.Errorf("invalid locator %s", style.Symbol(uri)) + } + + return nil +} diff --git a/vendor/github.com/buildpacks/pack/codecov.yml b/vendor/github.com/buildpacks/pack/codecov.yml new file mode 100644 index 0000000000..c77a18bf3a --- /dev/null +++ b/vendor/github.com/buildpacks/pack/codecov.yml @@ -0,0 +1,18 @@ +codecov: + notify: + after_n_builds: 4 + +coverage: + round: up + status: + project: + default: + threshold: 1% + patch: + default: + threshold: 10% + +comment: + layout: "reach,diff,flags" + require_changes: yes + after_n_builds: 4 \ No newline at end of file diff --git a/vendor/github.com/buildpacks/pack/go.mod b/vendor/github.com/buildpacks/pack/go.mod new file mode 100644 index 0000000000..910a1d3c4d --- /dev/null +++ b/vendor/github.com/buildpacks/pack/go.mod @@ -0,0 +1,93 @@ +module github.com/buildpacks/pack + +require ( + github.com/BurntSushi/toml v1.0.0 + github.com/Masterminds/semver v1.5.0 + github.com/apex/log v1.9.0 + github.com/buildpacks/imgutil v0.0.0-20211203200417-76206845baac + github.com/buildpacks/lifecycle v0.13.3 + github.com/docker/cli v20.10.12+incompatible + github.com/docker/docker v20.10.12+incompatible + github.com/docker/go-connections v0.4.0 + github.com/dustin/go-humanize v1.0.0 + github.com/gdamore/tcell/v2 v2.4.0 + github.com/ghodss/yaml v1.0.0 + github.com/golang/mock v1.6.0 + github.com/google/go-cmp v0.5.7 + github.com/google/go-containerregistry v0.8.0 + github.com/google/go-github/v30 v30.1.0 + github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 + github.com/heroku/color v0.0.6 + github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e + github.com/onsi/gomega v1.18.1 + github.com/opencontainers/image-spec v1.0.2 + github.com/pelletier/go-toml v1.9.4 + github.com/pkg/errors v0.9.1 + github.com/rivo/tview v0.0.0-20210624165335-29d673af0ce2 + github.com/sabhiram/go-gitignore v0.0.0-20201211074657-223ce5d391b0 + github.com/sclevine/spec v1.4.0 + github.com/spf13/cobra v1.3.0 + golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 + golang.org/x/mod v0.5.1 + golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 + golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce + gopkg.in/src-d/go-git.v4 v4.13.1 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b +) + +require ( + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Microsoft/go-winio v0.5.1 // indirect + github.com/Microsoft/hcsshim v0.8.23 // indirect + github.com/bits-and-blooms/bitset v1.2.0 // indirect + github.com/containerd/cgroups v1.0.1 // indirect + github.com/containerd/containerd v1.5.8 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.10.1 // indirect + github.com/docker/distribution v2.7.1+incompatible // indirect + github.com/docker/docker-credential-helpers v0.6.4 // indirect + github.com/docker/go-units v0.4.0 // indirect + github.com/emirpasic/gods v1.12.0 // indirect + github.com/gdamore/encoding v1.0.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-querystring v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect + github.com/klauspost/compress v1.13.6 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.10 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/moby/sys/mount v0.2.0 // indirect + github.com/moby/sys/mountinfo v0.4.1 // indirect + github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/runc v1.0.2 // indirect + github.com/opencontainers/selinux v1.8.2 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/sergi/go-diff v1.1.0 // indirect + github.com/sirupsen/logrus v1.8.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/src-d/gcfg v1.4.0 // indirect + github.com/vbatts/tar-split v0.11.2 // indirect + github.com/xanzy/ssh-agent v0.3.0 // indirect + go.opencensus.io v0.23.0 // indirect + golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect + golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect + google.golang.org/grpc v1.43.0 // indirect + google.golang.org/protobuf v1.27.1 // indirect + gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) + +go 1.17 diff --git a/vendor/github.com/buildpacks/pack/go.sum b/vendor/github.com/buildpacks/pack/go.sum new file mode 100644 index 0000000000..c344d76713 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/go.sum @@ -0,0 +1,1541 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= +github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= +github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= +github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= +github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= +github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= +github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= +github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim v0.8.23 h1:47MSwtKGXet80aIn+7h4YI6fwPmwIghAnsx2aOUrG2M= +github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= +github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= +github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0= +github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA= +github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= +github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= +github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= +github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/buildpacks/imgutil v0.0.0-20211203200417-76206845baac h1:XrKr6axRUBHEQdyyo7uffYDwWurOdeyH8MpNRJuBdIw= +github.com/buildpacks/imgutil v0.0.0-20211203200417-76206845baac/go.mod h1:YZReWjuSxwyvuN92Vlcul+WgaCXylpecgFn7T3rNang= +github.com/buildpacks/lifecycle v0.13.3 h1:vV2DGTPVQOELtrCSYpop8W9OF0m+l5gwxWDPmL9ZcOw= +github.com/buildpacks/lifecycle v0.13.3/go.mod h1:4Kv6HljeDJ1ibUcRijvvC1/AHXMCpNddIqH2KYnboks= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= +github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= +github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= +github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= +github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= +github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= +github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.0.1 h1:iJnMvco9XGvKUvNQkv88bE4uJXxRQH18efbKo9w5vHQ= +github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= +github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= +github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= +github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= +github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= +github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= +github.com/containerd/containerd v1.5.8 h1:NmkCC1/QxyZFBny8JogwLpOy2f+VEbO/f6bV2Mqtwuw= +github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= +github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= +github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= +github.com/containerd/continuity v0.1.0 h1:UFRRY5JemiAhPZrr/uE0n8fMTLcZsUvySPr1+D7pgr8= +github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= +github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= +github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= +github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= +github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= +github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= +github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= +github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= +github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/stargz-snapshotter/estargz v0.10.0/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= +github.com/containerd/stargz-snapshotter/estargz v0.10.1 h1:hd1EoVjI2Ax8Cr64tdYqnJ4i4pZU49FkEf5kU8KxQng= +github.com/containerd/stargz-snapshotter/estargz v0.10.1/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= +github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= +github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= +github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= +github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= +github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= +github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= +github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= +github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= +github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= +github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= +github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= +github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= +github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/cli v20.10.10+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.11+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.12+incompatible h1:lZlz0uzG+GH+c0plStMUdF/qk3ppmgnswpR5EbqzVGA= +github.com/docker/cli v20.10.12+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= +github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.12+incompatible h1:CEeNmFM0QZIsJCZKMkZx0ZcahTiewkrgiwfYD+dfl1U= +github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= +github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell/v2 v2.3.3/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= +github.com/gdamore/tcell/v2 v2.4.0 h1:W6dxJEmaxYvhICFoTY3WrLLEXsQ11SaFnKGVEXW57KM= +github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= +github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-containerregistry v0.7.0/go.mod h1:2zaoelrL0d08gGbpdP3LqyUuBmhWbpD6IOe2s9nLS2k= +github.com/google/go-containerregistry v0.8.0 h1:mtR24eN6rapCN+shds82qFEIWWmg64NPMuyCNT7/Ogc= +github.com/google/go-containerregistry v0.8.0/go.mod h1:wW5v71NHGnQyb4k+gSshjxidrC7lN33MdWEn+Mz9TsI= +github.com/google/go-github/v30 v30.1.0 h1:VLDx+UolQICEOKu2m4uAoMti1SxuEBAl7RSEG16L+Oo= +github.com/google/go-github/v30 v30.1.0/go.mod h1:n8jBpHl45a/rlBUtRJMOG4GhNADUQFEufcolZ95JfU8= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= +github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/heroku/color v0.0.6 h1:UTFFMrmMLFcL3OweqP1lAdp8i1y/9oHqkeHjQ/b/Ny0= +github.com/heroku/color v0.0.6/go.mod h1:ZBvOcx7cTF2QKOv4LbmoBtNl5uB17qWxGuzZrsi1wLU= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e h1:Qa6dnn8DlasdXRnacluu8HzPts0S1I9zvvUPDbBnXFI= +github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e/go.mod h1:waEya8ee1Ro/lgxpVhkJI4BVASzkm3UZqkx/cFJiYHM= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM= +github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM= +github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM= +github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.0.0 h1:CcuG/HvWNkkaqCUpJifQY8z7qEMBJya6aLPx6ftGyjQ= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= +github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2-0.20210730191737-8e42a01fb1b7/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= +github.com/opencontainers/runc v1.0.2 h1:opHZMaswlyxz1OuGpBE53Dwe4/xF7EZTY0A2L/FpCOg= +github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= +github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= +github.com/opencontainers/selinux v1.8.2 h1:c4ca10UMgRcvZ6h0K4HtS15UaVSBEaE+iln2LVpAuGc= +github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= +github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rivo/tview v0.0.0-20210624165335-29d673af0ce2 h1:I5N0WNMgPSq5NKUFspB4jMJ6n2P0ipz5FlOlB4BXviQ= +github.com/rivo/tview v0.0.0-20210624165335-29d673af0ce2/go.mod h1:IxQujbYMAh4trWr0Dwa8jfciForjVmxyHpskZX6aydQ= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sabhiram/go-gitignore v0.0.0-20201211074657-223ce5d391b0 h1:4Q/TASkyjpqyR5DL5+6c2FGSDpHM5bTMSspcXr7J6R8= +github.com/sabhiram/go-gitignore v0.0.0-20201211074657-223ce5d391b0/go.mod h1:b18R55ulyQ/h3RaWyloPyER7fWQVZvimKKhnI5OfrJQ= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8= +github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0= +github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= +github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= +github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= +github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= +github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= +github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= +github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= +github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc= +github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= +github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= +github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= +github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= +github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= +github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211203184738-4852103109b8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211111162719-482062a4217b/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM= +google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= +gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= +gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg= +gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= +gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE= +gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= +k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= +k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= +k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= +k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= +k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= +k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= +k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= +k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= +k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= +k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= +k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= +k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= +k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= +k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/vendor/github.com/buildpacks/pack/golangci.yaml b/vendor/github.com/buildpacks/pack/golangci.yaml new file mode 100644 index 0000000000..b44e01cd9c --- /dev/null +++ b/vendor/github.com/buildpacks/pack/golangci.yaml @@ -0,0 +1,32 @@ +run: + timeout: 6m + +linters: + disable-all: true + enable: + - bodyclose + - deadcode + - dogsled + - gocritic + - goimports + - golint + - gosimple + - govet + - ineffassign + - maligned + - misspell + - nakedret + - rowserrcheck + - scopelint + - staticcheck + - structcheck + - stylecheck + - typecheck + - unconvert + - unused + - varcheck + - whitespace + +linters-settings: + goimports: + local-prefixes: github.com/buildpacks/pack diff --git a/vendor/github.com/buildpacks/pack/internal/build/container_ops.go b/vendor/github.com/buildpacks/pack/internal/build/container_ops.go new file mode 100644 index 0000000000..a5fbf41e80 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/build/container_ops.go @@ -0,0 +1,315 @@ +package build + +import ( + "bytes" + "context" + "fmt" + "io" + "io/ioutil" + "os" + "runtime" + + "github.com/BurntSushi/toml" + "github.com/buildpacks/lifecycle/platform" + "github.com/docker/docker/api/types" + dcontainer "github.com/docker/docker/api/types/container" + "github.com/docker/docker/client" + darchive "github.com/docker/docker/pkg/archive" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/builder" + "github.com/buildpacks/pack/internal/container" + "github.com/buildpacks/pack/internal/paths" + "github.com/buildpacks/pack/pkg/archive" +) + +type ContainerOperation func(ctrClient client.CommonAPIClient, ctx context.Context, containerID string, stdout, stderr io.Writer) error + +// CopyOut copies container directories to a handler function. The handler is responsible for closing the Reader. +func CopyOut(handler func(closer io.ReadCloser) error, srcs ...string) ContainerOperation { + return func(ctrClient client.CommonAPIClient, ctx context.Context, containerID string, stdout, stderr io.Writer) error { + for _, src := range srcs { + reader, _, err := ctrClient.CopyFromContainer(ctx, containerID, src) + if err != nil { + return err + } + + err = handler(reader) + if err != nil { + return err + } + } + + return nil + } +} + +func CopyOutTo(src, dest string) ContainerOperation { + return CopyOut(func(reader io.ReadCloser) error { + info := darchive.CopyInfo{ + Path: src, + IsDir: true, + } + + defer reader.Close() + return darchive.CopyTo(reader, info, dest) + }, src) +} + +// CopyDir copies a local directory (src) to the destination on the container while filtering files and changing it's UID/GID. +// if includeRoot is set the UID/GID will be set on the dst directory. +func CopyDir(src, dst string, uid, gid int, os string, includeRoot bool, fileFilter func(string) bool) ContainerOperation { + return func(ctrClient client.CommonAPIClient, ctx context.Context, containerID string, stdout, stderr io.Writer) error { + tarPath := dst + if os == "windows" { + tarPath = paths.WindowsToSlash(dst) + } + + reader, err := createReader(src, tarPath, uid, gid, includeRoot, fileFilter) + if err != nil { + return errors.Wrapf(err, "create tar archive from '%s'", src) + } + defer reader.Close() + + if os == "windows" { + return copyDirWindows(ctx, ctrClient, containerID, reader, dst, stdout, stderr) + } + return copyDir(ctx, ctrClient, containerID, reader) + } +} + +func copyDir(ctx context.Context, ctrClient client.CommonAPIClient, containerID string, appReader io.Reader) error { + var clientErr, err error + + doneChan := make(chan interface{}) + pr, pw := io.Pipe() + go func() { + clientErr = ctrClient.CopyToContainer(ctx, containerID, "/", pr, types.CopyToContainerOptions{}) + close(doneChan) + }() + func() { + defer pw.Close() + _, err = io.Copy(pw, appReader) + }() + + <-doneChan + if err == nil { + err = clientErr + } + + return err +} + +// copyDirWindows provides an alternate, Windows container-specific implementation of copyDir. +// This implementation is needed because copying directly to a mounted volume is currently buggy +// for Windows containers and does not work. Instead, we perform the copy from inside a container +// using xcopy. +// See: https://github.com/moby/moby/issues/40771 +func copyDirWindows(ctx context.Context, ctrClient client.CommonAPIClient, containerID string, reader io.Reader, dst string, stdout, stderr io.Writer) error { + info, err := ctrClient.ContainerInspect(ctx, containerID) + if err != nil { + return err + } + + baseName := paths.WindowsBasename(dst) + + mnt, err := findMount(info, dst) + if err != nil { + return err + } + + ctr, err := ctrClient.ContainerCreate(ctx, + &dcontainer.Config{ + Image: info.Image, + Cmd: []string{ + "cmd", + "/c", + + // xcopy args + // e - recursively create subdirectories + // h - copy hidden and system files + // b - copy symlinks, do not dereference + // x - copy attributes + // y - suppress prompting + fmt.Sprintf(`xcopy c:\windows\%s %s /e /h /b /x /y`, baseName, dst), + }, + WorkingDir: "/", + User: windowsContainerAdmin, + }, + &dcontainer.HostConfig{ + Binds: []string{fmt.Sprintf("%s:%s", mnt.Name, mnt.Destination)}, + Isolation: dcontainer.IsolationProcess, + }, + nil, nil, "", + ) + if err != nil { + return errors.Wrapf(err, "creating prep container") + } + defer ctrClient.ContainerRemove(context.Background(), ctr.ID, types.ContainerRemoveOptions{Force: true}) + + err = ctrClient.CopyToContainer(ctx, ctr.ID, "/windows", reader, types.CopyToContainerOptions{}) + if err != nil { + return errors.Wrap(err, "copy app to container") + } + + return container.RunWithHandler( + ctx, + ctrClient, + ctr.ID, + container.DefaultHandler( + ioutil.Discard, // Suppress xcopy output + stderr, + ), + ) +} + +func findMount(info types.ContainerJSON, dst string) (types.MountPoint, error) { + for _, m := range info.Mounts { + if m.Destination == dst { + return m, nil + } + } + return types.MountPoint{}, fmt.Errorf("no matching mount found for %s", dst) +} + +// WriteProjectMetadata +func WriteProjectMetadata(p string, metadata platform.ProjectMetadata, os string) ContainerOperation { + return func(ctrClient client.CommonAPIClient, ctx context.Context, containerID string, stdout, stderr io.Writer) error { + buf := &bytes.Buffer{} + err := toml.NewEncoder(buf).Encode(metadata) + if err != nil { + return errors.Wrap(err, "marshaling project metadata") + } + + tarBuilder := archive.TarBuilder{} + + tarPath := p + if os == "windows" { + tarPath = paths.WindowsToSlash(p) + } + + tarBuilder.AddFile(tarPath, 0755, archive.NormalizedDateTime, buf.Bytes()) + reader := tarBuilder.Reader(archive.DefaultTarWriterFactory()) + defer reader.Close() + + if os == "windows" { + dirName := paths.WindowsDir(p) + return copyDirWindows(ctx, ctrClient, containerID, reader, dirName, stdout, stderr) + } + + return ctrClient.CopyToContainer(ctx, containerID, "/", reader, types.CopyToContainerOptions{}) + } +} + +// WriteStackToml writes a `stack.toml` based on the StackMetadata provided to the destination path. +func WriteStackToml(dstPath string, stack builder.StackMetadata, os string) ContainerOperation { + return func(ctrClient client.CommonAPIClient, ctx context.Context, containerID string, stdout, stderr io.Writer) error { + buf := &bytes.Buffer{} + err := toml.NewEncoder(buf).Encode(stack) + if err != nil { + return errors.Wrap(err, "marshaling stack metadata") + } + + tarBuilder := archive.TarBuilder{} + + tarPath := dstPath + if os == "windows" { + tarPath = paths.WindowsToSlash(dstPath) + } + + tarBuilder.AddFile(tarPath, 0755, archive.NormalizedDateTime, buf.Bytes()) + reader := tarBuilder.Reader(archive.DefaultTarWriterFactory()) + defer reader.Close() + + if os == "windows" { + dirName := paths.WindowsDir(dstPath) + return copyDirWindows(ctx, ctrClient, containerID, reader, dirName, stdout, stderr) + } + + return ctrClient.CopyToContainer(ctx, containerID, "/", reader, types.CopyToContainerOptions{}) + } +} + +func createReader(src, dst string, uid, gid int, includeRoot bool, fileFilter func(string) bool) (io.ReadCloser, error) { + fi, err := os.Stat(src) + if err != nil { + return nil, err + } + + if fi.IsDir() { + var mode int64 = -1 + if runtime.GOOS == "windows" { + mode = 0777 + } + + return archive.ReadDirAsTar(src, dst, uid, gid, mode, false, includeRoot, fileFilter), nil + } + + return archive.ReadZipAsTar(src, dst, uid, gid, -1, false, fileFilter), nil +} + +// EnsureVolumeAccess grants full access permissions to volumes for UID/GID-based user +// When UID/GID are 0 it grants explicit full access to BUILTIN\Administrators and any other UID/GID grants full access to BUILTIN\Users +// Changing permissions on volumes through stopped containers does not work on Docker for Windows so we start the container and make change using icacls +// See: https://github.com/moby/moby/issues/40771 +func EnsureVolumeAccess(uid, gid int, os string, volumeNames ...string) ContainerOperation { + return func(ctrClient client.CommonAPIClient, ctx context.Context, containerID string, stdout, stderr io.Writer) error { + if os != "windows" { + return nil + } + + containerInfo, err := ctrClient.ContainerInspect(ctx, containerID) + if err != nil { + return err + } + + cmd := "" + binds := []string{} + for i, volumeName := range volumeNames { + containerPath := fmt.Sprintf("c:/volume-mnt-%d", i) + binds = append(binds, fmt.Sprintf("%s:%s", volumeName, containerPath)) + + if cmd != "" { + cmd += "&&" + } + + // icacls args + // /grant - add new permissions instead of replacing + // (OI) - object inherit + // (CI) - container inherit + // F - full access + // /t - recursively apply + // /l - perform on a symbolic link itself versus its target + // /q - suppress success messages + cmd += fmt.Sprintf(`icacls %s /grant *%s:(OI)(CI)F /t /l /q`, containerPath, paths.WindowsPathSID(uid, gid)) + } + + ctr, err := ctrClient.ContainerCreate(ctx, + &dcontainer.Config{ + Image: containerInfo.Image, + Cmd: []string{"cmd", "/c", cmd}, + WorkingDir: "/", + User: windowsContainerAdmin, + }, + &dcontainer.HostConfig{ + Binds: binds, + Isolation: dcontainer.IsolationProcess, + }, + nil, nil, "", + ) + if err != nil { + return err + } + defer ctrClient.ContainerRemove(context.Background(), ctr.ID, types.ContainerRemoveOptions{Force: true}) + + return container.RunWithHandler( + ctx, + ctrClient, + ctr.ID, + container.DefaultHandler( + ioutil.Discard, // Suppress icacls output + stderr, + ), + ) + } +} diff --git a/vendor/github.com/buildpacks/pack/internal/build/lifecycle_execution.go b/vendor/github.com/buildpacks/pack/internal/build/lifecycle_execution.go new file mode 100644 index 0000000000..251da2e2b2 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/build/lifecycle_execution.go @@ -0,0 +1,603 @@ +package build + +import ( + "context" + "fmt" + "math/rand" + "strconv" + + "github.com/buildpacks/lifecycle/api" + "github.com/buildpacks/lifecycle/auth" + "github.com/docker/docker/client" + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/builder" + "github.com/buildpacks/pack/internal/cache" + "github.com/buildpacks/pack/internal/paths" + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/logging" +) + +const ( + defaultProcessType = "web" + overrideGID = 0 +) + +type LifecycleExecution struct { + logger logging.Logger + docker client.CommonAPIClient + platformAPI *api.Version + layersVolume string + appVolume string + os string + mountPaths mountPaths + opts LifecycleOptions +} + +func NewLifecycleExecution(logger logging.Logger, docker client.CommonAPIClient, opts LifecycleOptions) (*LifecycleExecution, error) { + latestSupportedPlatformAPI, err := findLatestSupported(append( + opts.Builder.LifecycleDescriptor().APIs.Platform.Deprecated, + opts.Builder.LifecycleDescriptor().APIs.Platform.Supported..., + )) + if err != nil { + return nil, err + } + + osType, err := opts.Builder.Image().OS() + if err != nil { + return nil, err + } + + exec := &LifecycleExecution{ + logger: logger, + docker: docker, + layersVolume: paths.FilterReservedNames("pack-layers-" + randString(10)), + appVolume: paths.FilterReservedNames("pack-app-" + randString(10)), + platformAPI: latestSupportedPlatformAPI, + opts: opts, + os: osType, + mountPaths: mountPathsForOS(osType, opts.Workspace), + } + + if opts.Interactive { + exec.logger = opts.Termui + } + + return exec, nil +} + +func findLatestSupported(apis []*api.Version) (*api.Version, error) { + for i := len(SupportedPlatformAPIVersions) - 1; i >= 0; i-- { + for _, version := range apis { + if SupportedPlatformAPIVersions[i].Equal(version) { + return version, nil + } + } + } + + return nil, errors.New("unable to find a supported Platform API version") +} + +func randString(n int) string { + b := make([]byte, n) + for i := range b { + b[i] = 'a' + byte(rand.Intn(26)) + } + return string(b) +} + +func (l *LifecycleExecution) Builder() Builder { + return l.opts.Builder +} + +func (l *LifecycleExecution) AppPath() string { + return l.opts.AppPath +} + +func (l LifecycleExecution) AppDir() string { + return l.mountPaths.appDir() +} + +func (l *LifecycleExecution) AppVolume() string { + return l.appVolume +} + +func (l *LifecycleExecution) LayersVolume() string { + return l.layersVolume +} + +func (l *LifecycleExecution) PlatformAPI() *api.Version { + return l.platformAPI +} + +func (l *LifecycleExecution) ImageName() name.Reference { + return l.opts.Image +} + +func (l *LifecycleExecution) PrevImageName() string { + return l.opts.PreviousImage +} + +func (l *LifecycleExecution) Run(ctx context.Context, phaseFactoryCreator PhaseFactoryCreator) error { + phaseFactory := phaseFactoryCreator(l) + var buildCache Cache + if l.opts.CacheImage != "" { + cacheImage, err := name.ParseReference(l.opts.CacheImage, name.WeakValidation) + if err != nil { + return fmt.Errorf("invalid cache image name: %s", err) + } + buildCache = cache.NewImageCache(cacheImage, l.docker) + } else { + buildCache = cache.NewVolumeCache(l.opts.Image, "build", l.docker) + } + + l.logger.Debugf("Using build cache volume %s", style.Symbol(buildCache.Name())) + if l.opts.ClearCache { + if err := buildCache.Clear(ctx); err != nil { + return errors.Wrap(err, "clearing build cache") + } + l.logger.Debugf("Build cache %s cleared", style.Symbol(buildCache.Name())) + } + + launchCache := cache.NewVolumeCache(l.opts.Image, "launch", l.docker) + + if !l.opts.UseCreator { + if l.platformAPI.LessThan("0.7") { + l.logger.Info(style.Step("DETECTING")) + if err := l.Detect(ctx, l.opts.Network, l.opts.Volumes, phaseFactory); err != nil { + return err + } + + l.logger.Info(style.Step("ANALYZING")) + if err := l.Analyze(ctx, l.opts.Image.String(), l.opts.Network, l.opts.Publish, l.opts.DockerHost, l.opts.ClearCache, l.opts.RunImage, l.opts.AdditionalTags, buildCache, phaseFactory); err != nil { + return err + } + } else { + l.logger.Info(style.Step("ANALYZING")) + if err := l.Analyze(ctx, l.opts.Image.String(), l.opts.Network, l.opts.Publish, l.opts.DockerHost, l.opts.ClearCache, l.opts.RunImage, l.opts.AdditionalTags, buildCache, phaseFactory); err != nil { + return err + } + + l.logger.Info(style.Step("DETECTING")) + if err := l.Detect(ctx, l.opts.Network, l.opts.Volumes, phaseFactory); err != nil { + return err + } + } + l.logger.Info(style.Step("RESTORING")) + if l.opts.ClearCache { + l.logger.Info("Skipping 'restore' due to clearing cache") + } else if err := l.Restore(ctx, l.opts.Network, buildCache, phaseFactory); err != nil { + return err + } + + l.logger.Info(style.Step("BUILDING")) + + if err := l.Build(ctx, l.opts.Network, l.opts.Volumes, phaseFactory); err != nil { + return err + } + + l.logger.Info(style.Step("EXPORTING")) + return l.Export(ctx, l.opts.Image.String(), l.opts.RunImage, l.opts.Publish, l.opts.DockerHost, l.opts.Network, buildCache, launchCache, l.opts.AdditionalTags, phaseFactory) + } + + return l.Create(ctx, l.opts.Publish, l.opts.DockerHost, l.opts.ClearCache, l.opts.RunImage, l.opts.Image.String(), l.opts.Network, buildCache, launchCache, l.opts.AdditionalTags, l.opts.Volumes, phaseFactory) +} + +func (l *LifecycleExecution) Cleanup() error { + var reterr error + if err := l.docker.VolumeRemove(context.Background(), l.layersVolume, true); err != nil { + reterr = errors.Wrapf(err, "failed to clean up layers volume %s", l.layersVolume) + } + if err := l.docker.VolumeRemove(context.Background(), l.appVolume, true); err != nil { + reterr = errors.Wrapf(err, "failed to clean up app volume %s", l.appVolume) + } + return reterr +} + +func (l *LifecycleExecution) Create(ctx context.Context, publish bool, dockerHost string, clearCache bool, runImage, repoName, networkMode string, buildCache, launchCache Cache, additionalTags, volumes []string, phaseFactory PhaseFactory) error { + flags := addTags([]string{ + "-app", l.mountPaths.appDir(), + "-cache-dir", l.mountPaths.cacheDir(), + "-run-image", runImage, + }, additionalTags) + + if clearCache { + flags = append(flags, "-skip-restore") + } + + if l.opts.GID >= overrideGID { + flags = append(flags, "-gid", strconv.Itoa(l.opts.GID)) + } + + if l.opts.PreviousImage != "" { + if l.opts.Image == nil { + return errors.New("image can't be nil") + } + + image, err := name.ParseReference(l.opts.Image.Name(), name.WeakValidation) + if err != nil { + return fmt.Errorf("invalid image name: %s", err) + } + + prevImage, err := name.ParseReference(l.opts.PreviousImage, name.WeakValidation) + if err != nil { + return fmt.Errorf("invalid previous image name: %s", err) + } + if publish { + if image.Context().RegistryStr() != prevImage.Context().RegistryStr() { + return fmt.Errorf(`when --publish is used, must be in the same image registry as + image registry = %s + previous-image registry = %s`, image.Context().RegistryStr(), prevImage.Context().RegistryStr()) + } + } + + flags = append(flags, "-previous-image", l.opts.PreviousImage) + } + + processType := determineDefaultProcessType(l.platformAPI, l.opts.DefaultProcessType) + if processType != "" { + flags = append(flags, "-process-type", processType) + } + + var cacheOpts PhaseConfigProviderOperation + switch buildCache.Type() { + case cache.Image: + flags = append(flags, "-cache-image", buildCache.Name()) + cacheOpts = WithBinds(volumes...) + case cache.Volume: + cacheOpts = WithBinds(append(volumes, fmt.Sprintf("%s:%s", buildCache.Name(), l.mountPaths.cacheDir()))...) + } + + opts := []PhaseConfigProviderOperation{ + WithFlags(l.withLogLevel(flags...)...), + WithArgs(repoName), + WithNetwork(networkMode), + cacheOpts, + WithContainerOperations(WriteProjectMetadata(l.mountPaths.projectPath(), l.opts.ProjectMetadata, l.os)), + WithContainerOperations(CopyDir(l.opts.AppPath, l.mountPaths.appDir(), l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, true, l.opts.FileFilter)), + If(l.opts.SBOMDestinationDir != "", WithPostContainerRunOperations( + EnsureVolumeAccess(l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, l.layersVolume, l.appVolume), + CopyOutTo(l.mountPaths.sbomDir(), l.opts.SBOMDestinationDir))), + If(l.opts.Interactive, WithPostContainerRunOperations( + EnsureVolumeAccess(l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, l.layersVolume, l.appVolume), + CopyOut(l.opts.Termui.ReadLayers, l.mountPaths.layersDir(), l.mountPaths.appDir()))), + } + + if publish { + authConfig, err := auth.BuildEnvVar(authn.DefaultKeychain, repoName) + if err != nil { + return err + } + + opts = append(opts, WithRoot(), WithRegistryAccess(authConfig)) + } else { + opts = append(opts, + WithDaemonAccess(dockerHost), + WithFlags("-daemon", "-launch-cache", l.mountPaths.launchCacheDir()), + WithBinds(fmt.Sprintf("%s:%s", launchCache.Name(), l.mountPaths.launchCacheDir())), + ) + } + + create := phaseFactory.New(NewPhaseConfigProvider("creator", l, opts...)) + defer create.Cleanup() + return create.Run(ctx) +} + +func (l *LifecycleExecution) Detect(ctx context.Context, networkMode string, volumes []string, phaseFactory PhaseFactory) error { + flags := []string{"-app", l.mountPaths.appDir()} + configProvider := NewPhaseConfigProvider( + "detector", + l, + WithLogPrefix("detector"), + WithArgs( + l.withLogLevel()..., + ), + WithNetwork(networkMode), + WithBinds(volumes...), + WithContainerOperations( + EnsureVolumeAccess(l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, l.layersVolume, l.appVolume), + CopyDir(l.opts.AppPath, l.mountPaths.appDir(), l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, true, l.opts.FileFilter), + ), + WithFlags(flags...), + ) + + detect := phaseFactory.New(configProvider) + defer detect.Cleanup() + return detect.Run(ctx) +} + +func (l *LifecycleExecution) Restore(ctx context.Context, networkMode string, buildCache Cache, phaseFactory PhaseFactory) error { + flagsOpt := NullOp() + cacheOpt := NullOp() + switch buildCache.Type() { + case cache.Image: + flagsOpt = WithFlags("-cache-image", buildCache.Name()) + case cache.Volume: + cacheOpt = WithBinds(fmt.Sprintf("%s:%s", buildCache.Name(), l.mountPaths.cacheDir())) + } + if l.opts.GID >= overrideGID { + flagsOpt = WithFlags("-gid", strconv.Itoa(l.opts.GID)) + } + + configProvider := NewPhaseConfigProvider( + "restorer", + l, + WithLogPrefix("restorer"), + WithImage(l.opts.LifecycleImage), + WithEnv(fmt.Sprintf("%s=%d", builder.EnvUID, l.opts.Builder.UID()), fmt.Sprintf("%s=%d", builder.EnvGID, l.opts.Builder.GID())), + WithRoot(), // remove after platform API 0.2 is no longer supported + WithArgs( + l.withLogLevel( + "-cache-dir", l.mountPaths.cacheDir(), + )..., + ), + WithNetwork(networkMode), + flagsOpt, + cacheOpt, + ) + + restore := phaseFactory.New(configProvider) + defer restore.Cleanup() + return restore.Run(ctx) +} + +func (l *LifecycleExecution) Analyze(ctx context.Context, repoName, networkMode string, publish bool, dockerHost string, clearCache bool, runImage string, additionalTags []string, cache Cache, phaseFactory PhaseFactory) error { + analyze, err := l.newAnalyze(repoName, networkMode, publish, dockerHost, clearCache, runImage, additionalTags, cache, phaseFactory) + if err != nil { + return err + } + defer analyze.Cleanup() + return analyze.Run(ctx) +} + +func (l *LifecycleExecution) newAnalyze(repoName, networkMode string, publish bool, dockerHost string, clearCache bool, runImage string, additionalTags []string, buildCache Cache, phaseFactory PhaseFactory) (RunnerCleaner, error) { + args := []string{ + repoName, + } + platformAPILessThan07 := l.platformAPI.LessThan("0.7") + if platformAPILessThan07 { + if clearCache { + args = prependArg("-skip-layers", args) + } else { + args = append([]string{"-cache-dir", l.mountPaths.cacheDir()}, args...) + } + } + + cacheOpt := NullOp() + flagsOpt := NullOp() + switch buildCache.Type() { + case cache.Image: + if !clearCache { + flagsOpt = WithFlags("-cache-image", buildCache.Name()) + } + case cache.Volume: + if platformAPILessThan07 { + cacheOpt = WithBinds(fmt.Sprintf("%s:%s", buildCache.Name(), l.mountPaths.cacheDir())) + } + } + + if l.opts.GID >= overrideGID { + flagsOpt = WithFlags("-gid", strconv.Itoa(l.opts.GID)) + } + + if l.opts.PreviousImage != "" { + if l.opts.Image == nil { + return nil, errors.New("image can't be nil") + } + + image, err := name.ParseReference(l.opts.Image.Name(), name.WeakValidation) + if err != nil { + return nil, fmt.Errorf("invalid image name: %s", err) + } + + prevImage, err := name.ParseReference(l.opts.PreviousImage, name.WeakValidation) + if err != nil { + return nil, fmt.Errorf("invalid previous image name: %s", err) + } + if publish { + if image.Context().RegistryStr() != prevImage.Context().RegistryStr() { + return nil, fmt.Errorf(`when --publish is used, must be in the same image registry as + image registry = %s + previous-image registry = %s`, image.Context().RegistryStr(), prevImage.Context().RegistryStr()) + } + } + if platformAPILessThan07 { + l.opts.Image = prevImage + } else { + args = append([]string{"-previous-image", l.opts.PreviousImage}, args...) + } + } + stackOpts := NullOp() + if !platformAPILessThan07 { + for _, tag := range additionalTags { + args = append([]string{"-tag", tag}, args...) + } + if runImage != "" { + args = append([]string{"-run-image", runImage}, args...) + } + args = append([]string{"-stack", l.mountPaths.stackPath()}, args...) + stackOpts = WithContainerOperations(WriteStackToml(l.mountPaths.stackPath(), l.opts.Builder.Stack(), l.os)) + } + + if publish { + authConfig, err := auth.BuildEnvVar(authn.DefaultKeychain, repoName) + if err != nil { + return nil, err + } + + configProvider := NewPhaseConfigProvider( + "analyzer", + l, + WithLogPrefix("analyzer"), + WithImage(l.opts.LifecycleImage), + WithEnv(fmt.Sprintf("%s=%d", builder.EnvUID, l.opts.Builder.UID()), fmt.Sprintf("%s=%d", builder.EnvGID, l.opts.Builder.GID())), + WithRegistryAccess(authConfig), + WithRoot(), + WithArgs(l.withLogLevel(args...)...), + WithNetwork(networkMode), + flagsOpt, + cacheOpt, + stackOpts, + ) + + return phaseFactory.New(configProvider), nil + } + + // TODO: when platform API 0.2 is no longer supported we can delete this code: https://github.com/buildpacks/pack/issues/629. + configProvider := NewPhaseConfigProvider( + "analyzer", + l, + WithLogPrefix("analyzer"), + WithImage(l.opts.LifecycleImage), + WithEnv( + fmt.Sprintf("%s=%d", builder.EnvUID, l.opts.Builder.UID()), + fmt.Sprintf("%s=%d", builder.EnvGID, l.opts.Builder.GID()), + ), + WithDaemonAccess(dockerHost), + WithArgs( + l.withLogLevel( + prependArg( + "-daemon", + args, + )..., + )..., + ), + flagsOpt, + WithNetwork(networkMode), + cacheOpt, + stackOpts, + ) + + return phaseFactory.New(configProvider), nil +} + +func (l *LifecycleExecution) Build(ctx context.Context, networkMode string, volumes []string, phaseFactory PhaseFactory) error { + flags := []string{"-app", l.mountPaths.appDir()} + configProvider := NewPhaseConfigProvider( + "builder", + l, + WithLogPrefix("builder"), + WithArgs(l.withLogLevel()...), + WithNetwork(networkMode), + WithBinds(volumes...), + WithFlags(flags...), + ) + + build := phaseFactory.New(configProvider) + defer build.Cleanup() + return build.Run(ctx) +} + +func determineDefaultProcessType(platformAPI *api.Version, providedValue string) string { + shouldSetForceDefault := platformAPI.Compare(api.MustParse("0.4")) >= 0 && + platformAPI.Compare(api.MustParse("0.6")) < 0 + if providedValue == "" && shouldSetForceDefault { + return defaultProcessType + } + + return providedValue +} + +func (l *LifecycleExecution) newExport(repoName, runImage string, publish bool, dockerHost, networkMode string, buildCache, launchCache Cache, additionalTags []string, phaseFactory PhaseFactory) (RunnerCleaner, error) { + flags := []string{ + "-app", l.mountPaths.appDir(), + "-cache-dir", l.mountPaths.cacheDir(), + "-stack", l.mountPaths.stackPath(), + } + + if l.platformAPI.LessThan("0.7") { + flags = append(flags, + "-run-image", runImage, + ) + } + processType := determineDefaultProcessType(l.platformAPI, l.opts.DefaultProcessType) + if processType != "" { + flags = append(flags, "-process-type", processType) + } + if l.opts.GID >= overrideGID { + flags = append(flags, "-gid", strconv.Itoa(l.opts.GID)) + } + + cacheOpt := NullOp() + switch buildCache.Type() { + case cache.Image: + flags = append(flags, "-cache-image", buildCache.Name()) + case cache.Volume: + cacheOpt = WithBinds(fmt.Sprintf("%s:%s", buildCache.Name(), l.mountPaths.cacheDir())) + } + + opts := []PhaseConfigProviderOperation{ + WithLogPrefix("exporter"), + WithImage(l.opts.LifecycleImage), + WithEnv( + fmt.Sprintf("%s=%d", builder.EnvUID, l.opts.Builder.UID()), + fmt.Sprintf("%s=%d", builder.EnvGID, l.opts.Builder.GID()), + ), + WithFlags( + l.withLogLevel(flags...)..., + ), + WithArgs(append([]string{repoName}, additionalTags...)...), + WithRoot(), + WithNetwork(networkMode), + cacheOpt, + WithContainerOperations(WriteStackToml(l.mountPaths.stackPath(), l.opts.Builder.Stack(), l.os)), + WithContainerOperations(WriteProjectMetadata(l.mountPaths.projectPath(), l.opts.ProjectMetadata, l.os)), + If(l.opts.SBOMDestinationDir != "", WithPostContainerRunOperations( + EnsureVolumeAccess(l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, l.layersVolume, l.appVolume), + CopyOutTo(l.mountPaths.sbomDir(), l.opts.SBOMDestinationDir))), + If(l.opts.Interactive, WithPostContainerRunOperations( + EnsureVolumeAccess(l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, l.layersVolume, l.appVolume), + CopyOut(l.opts.Termui.ReadLayers, l.mountPaths.layersDir(), l.mountPaths.appDir()))), + } + + if publish { + authConfig, err := auth.BuildEnvVar(authn.DefaultKeychain, repoName, runImage) + if err != nil { + return nil, err + } + + opts = append( + opts, + WithRegistryAccess(authConfig), + WithRoot(), + ) + } else { + opts = append( + opts, + WithDaemonAccess(dockerHost), + WithFlags("-daemon", "-launch-cache", l.mountPaths.launchCacheDir()), + WithBinds(fmt.Sprintf("%s:%s", launchCache.Name(), l.mountPaths.launchCacheDir())), + ) + } + + return phaseFactory.New(NewPhaseConfigProvider("exporter", l, opts...)), nil +} + +func (l *LifecycleExecution) Export(ctx context.Context, repoName, runImage string, publish bool, dockerHost, networkMode string, buildCache, launchCache Cache, additionalTags []string, phaseFactory PhaseFactory) error { + export, err := l.newExport(repoName, runImage, publish, dockerHost, networkMode, buildCache, launchCache, additionalTags, phaseFactory) + if err != nil { + return err + } + defer export.Cleanup() + return export.Run(ctx) +} + +func (l *LifecycleExecution) withLogLevel(args ...string) []string { + if l.logger.IsVerbose() { + return append([]string{"-log-level", "debug"}, args...) + } + return args +} + +func prependArg(arg string, args []string) []string { + return append([]string{arg}, args...) +} + +func addTags(flags, additionalTags []string) []string { + for _, tag := range additionalTags { + flags = append(flags, "-tag", tag) + } + return flags +} diff --git a/vendor/github.com/buildpacks/pack/internal/build/lifecycle_executor.go b/vendor/github.com/buildpacks/pack/internal/build/lifecycle_executor.go new file mode 100644 index 0000000000..b0c5973b9b --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/build/lifecycle_executor.go @@ -0,0 +1,113 @@ +package build + +import ( + "context" + "io" + "math/rand" + "time" + + "github.com/buildpacks/imgutil" + "github.com/buildpacks/lifecycle/api" + "github.com/buildpacks/lifecycle/platform" + "github.com/docker/docker/client" + "github.com/google/go-containerregistry/pkg/name" + + "github.com/buildpacks/pack/internal/builder" + "github.com/buildpacks/pack/internal/cache" + "github.com/buildpacks/pack/internal/container" + "github.com/buildpacks/pack/pkg/logging" +) + +var ( + // SupportedPlatformAPIVersions lists the Platform API versions pack supports listed from earliest to latest + SupportedPlatformAPIVersions = builder.APISet{ + api.MustParse("0.3"), + api.MustParse("0.4"), + api.MustParse("0.5"), + api.MustParse("0.6"), + api.MustParse("0.7"), + api.MustParse("0.8"), + } +) + +type Builder interface { + Name() string + UID() int + GID() int + LifecycleDescriptor() builder.LifecycleDescriptor + Stack() builder.StackMetadata + Image() imgutil.Image +} + +type LifecycleExecutor struct { + logger logging.Logger + docker client.CommonAPIClient +} + +type Cache interface { + Name() string + Clear(context.Context) error + Type() cache.Type +} + +type Termui interface { + logging.Logger + + Run(funk func()) error + Handler() container.Handler + ReadLayers(reader io.ReadCloser) error +} + +func init() { + rand.Seed(time.Now().UTC().UnixNano()) +} + +type LifecycleOptions struct { + AppPath string + Image name.Reference + Builder Builder + LifecycleImage string + RunImage string + ProjectMetadata platform.ProjectMetadata + ClearCache bool + Publish bool + TrustBuilder bool + UseCreator bool + Interactive bool + Termui Termui + DockerHost string + CacheImage string + HTTPProxy string + HTTPSProxy string + NoProxy string + Network string + AdditionalTags []string + Volumes []string + DefaultProcessType string + FileFilter func(string) bool + Workspace string + GID int + PreviousImage string + SBOMDestinationDir string +} + +func NewLifecycleExecutor(logger logging.Logger, docker client.CommonAPIClient) *LifecycleExecutor { + return &LifecycleExecutor{logger: logger, docker: docker} +} + +func (l *LifecycleExecutor) Execute(ctx context.Context, opts LifecycleOptions) error { + lifecycleExec, err := NewLifecycleExecution(l.logger, l.docker, opts) + if err != nil { + return err + } + + if !opts.Interactive { + defer lifecycleExec.Cleanup() + return lifecycleExec.Run(ctx, NewDefaultPhaseFactory) + } + + return opts.Termui.Run(func() { + defer lifecycleExec.Cleanup() + lifecycleExec.Run(ctx, NewDefaultPhaseFactory) + }) +} diff --git a/vendor/github.com/buildpacks/pack/internal/build/mount_paths.go b/vendor/github.com/buildpacks/pack/internal/build/mount_paths.go new file mode 100644 index 0000000000..133919ee7f --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/build/mount_paths.go @@ -0,0 +1,63 @@ +package build + +import "strings" + +type mountPaths struct { + volume string + separator string + workspace string +} + +func mountPathsForOS(os, workspace string) mountPaths { + if workspace == "" { + workspace = "workspace" + } + if os == "windows" { + return mountPaths{ + volume: `c:`, + separator: `\`, + workspace: workspace, + } + } + return mountPaths{ + volume: "", + separator: "/", + workspace: workspace, + } +} + +func (m mountPaths) join(parts ...string) string { + return strings.Join(parts, m.separator) +} + +func (m mountPaths) layersDir() string { + return m.join(m.volume, "layers") +} + +func (m mountPaths) stackPath() string { + return m.join(m.layersDir(), "stack.toml") +} + +func (m mountPaths) projectPath() string { + return m.join(m.layersDir(), "project-metadata.toml") +} + +func (m mountPaths) appDirName() string { + return m.workspace +} + +func (m mountPaths) appDir() string { + return m.join(m.volume, m.appDirName()) +} + +func (m mountPaths) cacheDir() string { + return m.join(m.volume, "cache") +} + +func (m mountPaths) launchCacheDir() string { + return m.join(m.volume, "launch-cache") +} + +func (m mountPaths) sbomDir() string { + return m.join(m.volume, "layers", "sbom") +} diff --git a/vendor/github.com/buildpacks/pack/internal/build/phase.go b/vendor/github.com/buildpacks/pack/internal/build/phase.go new file mode 100644 index 0000000000..f1930d8cc1 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/build/phase.go @@ -0,0 +1,69 @@ +package build + +import ( + "context" + "io" + + "github.com/docker/docker/api/types" + dcontainer "github.com/docker/docker/api/types/container" + "github.com/docker/docker/client" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/container" +) + +type Phase struct { + name string + infoWriter io.Writer + errorWriter io.Writer + docker client.CommonAPIClient + handler container.Handler + ctrConf *dcontainer.Config + hostConf *dcontainer.HostConfig + ctr dcontainer.ContainerCreateCreatedBody + uid, gid int + appPath string + containerOps []ContainerOperation + postContainerRunOps []ContainerOperation + fileFilter func(string) bool +} + +func (p *Phase) Run(ctx context.Context) error { + var err error + p.ctr, err = p.docker.ContainerCreate(ctx, p.ctrConf, p.hostConf, nil, nil, "") + if err != nil { + return errors.Wrapf(err, "failed to create '%s' container", p.name) + } + + for _, containerOp := range p.containerOps { + if err := containerOp(p.docker, ctx, p.ctr.ID, p.infoWriter, p.errorWriter); err != nil { + return err + } + } + + handler := container.DefaultHandler(p.infoWriter, p.errorWriter) + if p.handler != nil { + handler = p.handler + } + + err = container.RunWithHandler( + ctx, + p.docker, + p.ctr.ID, + handler) + if err != nil { + return err + } + + for _, containerOp := range p.postContainerRunOps { + if err := containerOp(p.docker, ctx, p.ctr.ID, p.infoWriter, p.errorWriter); err != nil { + return err + } + } + + return nil +} + +func (p *Phase) Cleanup() error { + return p.docker.ContainerRemove(context.Background(), p.ctr.ID, types.ContainerRemoveOptions{Force: true}) +} diff --git a/vendor/github.com/buildpacks/pack/internal/build/phase_config_provider.go b/vendor/github.com/buildpacks/pack/internal/build/phase_config_provider.go new file mode 100644 index 0000000000..be248ccec6 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/build/phase_config_provider.go @@ -0,0 +1,252 @@ +package build + +import ( + "fmt" + "io" + "os" + "strings" + + "github.com/docker/docker/api/types/container" + + pcontainer "github.com/buildpacks/pack/internal/container" + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/logging" +) + +const ( + linuxContainerAdmin = "root" + windowsContainerAdmin = "ContainerAdministrator" + platformAPIEnvVar = "CNB_PLATFORM_API" +) + +type PhaseConfigProviderOperation func(*PhaseConfigProvider) + +type PhaseConfigProvider struct { + ctrConf *container.Config + hostConf *container.HostConfig + name string + os string + containerOps []ContainerOperation + postContainerRunOps []ContainerOperation + infoWriter io.Writer + errorWriter io.Writer + handler pcontainer.Handler +} + +func NewPhaseConfigProvider(name string, lifecycleExec *LifecycleExecution, ops ...PhaseConfigProviderOperation) *PhaseConfigProvider { + provider := &PhaseConfigProvider{ + ctrConf: new(container.Config), + hostConf: new(container.HostConfig), + name: name, + os: lifecycleExec.os, + infoWriter: logging.GetWriterForLevel(lifecycleExec.logger, logging.InfoLevel), + errorWriter: logging.GetWriterForLevel(lifecycleExec.logger, logging.ErrorLevel), + } + + provider.ctrConf.Image = lifecycleExec.opts.Builder.Name() + provider.ctrConf.Labels = map[string]string{"author": "pack"} + + if lifecycleExec.os == "windows" { + provider.hostConf.Isolation = container.IsolationProcess + } + + ops = append(ops, + WithEnv(fmt.Sprintf("%s=%s", platformAPIEnvVar, lifecycleExec.platformAPI.String())), + WithLifecycleProxy(lifecycleExec), + WithBinds([]string{ + fmt.Sprintf("%s:%s", lifecycleExec.layersVolume, lifecycleExec.mountPaths.layersDir()), + fmt.Sprintf("%s:%s", lifecycleExec.appVolume, lifecycleExec.mountPaths.appDir()), + }...), + ) + + for _, op := range ops { + op(provider) + } + + provider.ctrConf.Cmd = append([]string{"/cnb/lifecycle/" + name}, provider.ctrConf.Cmd...) + + lifecycleExec.logger.Debugf("Running the %s on OS %s with:", style.Symbol(provider.Name()), style.Symbol(provider.os)) + lifecycleExec.logger.Debug("Container Settings:") + lifecycleExec.logger.Debugf(" Args: %s", style.Symbol(strings.Join(provider.ctrConf.Cmd, " "))) + lifecycleExec.logger.Debugf(" System Envs: %s", style.Symbol(strings.Join(provider.ctrConf.Env, " "))) + lifecycleExec.logger.Debugf(" Image: %s", style.Symbol(provider.ctrConf.Image)) + lifecycleExec.logger.Debugf(" User: %s", style.Symbol(provider.ctrConf.User)) + lifecycleExec.logger.Debugf(" Labels: %s", style.Symbol(fmt.Sprintf("%s", provider.ctrConf.Labels))) + + lifecycleExec.logger.Debug("Host Settings:") + lifecycleExec.logger.Debugf(" Binds: %s", style.Symbol(strings.Join(provider.hostConf.Binds, " "))) + lifecycleExec.logger.Debugf(" Network Mode: %s", style.Symbol(string(provider.hostConf.NetworkMode))) + + if lifecycleExec.opts.Interactive { + provider.handler = lifecycleExec.opts.Termui.Handler() + } + + return provider +} + +func (p *PhaseConfigProvider) ContainerConfig() *container.Config { + return p.ctrConf +} + +func (p *PhaseConfigProvider) ContainerOps() []ContainerOperation { + return p.containerOps +} + +func (p *PhaseConfigProvider) PostContainerRunOps() []ContainerOperation { + return p.postContainerRunOps +} + +func (p *PhaseConfigProvider) HostConfig() *container.HostConfig { + return p.hostConf +} + +func (p *PhaseConfigProvider) Handler() pcontainer.Handler { + return p.handler +} + +func (p *PhaseConfigProvider) Name() string { + return p.name +} + +func (p *PhaseConfigProvider) ErrorWriter() io.Writer { + return p.errorWriter +} + +func (p *PhaseConfigProvider) InfoWriter() io.Writer { + return p.infoWriter +} + +func NullOp() PhaseConfigProviderOperation { + return func(provider *PhaseConfigProvider) {} +} + +func WithArgs(args ...string) PhaseConfigProviderOperation { + return func(provider *PhaseConfigProvider) { + provider.ctrConf.Cmd = append(provider.ctrConf.Cmd, args...) + } +} + +// WithFlags differs from WithArgs as flags are always prepended +func WithFlags(flags ...string) PhaseConfigProviderOperation { + return func(provider *PhaseConfigProvider) { + provider.ctrConf.Cmd = append(flags, provider.ctrConf.Cmd...) + } +} + +func WithBinds(binds ...string) PhaseConfigProviderOperation { + return func(provider *PhaseConfigProvider) { + provider.hostConf.Binds = append(provider.hostConf.Binds, binds...) + } +} + +func WithDaemonAccess(dockerHost string) PhaseConfigProviderOperation { + return func(provider *PhaseConfigProvider) { + WithRoot()(provider) + if dockerHost == "inherit" { + dockerHost = os.Getenv("DOCKER_HOST") + } + var bind string + if dockerHost == "" { + bind = "/var/run/docker.sock:/var/run/docker.sock" + if provider.os == "windows" { + bind = `\\.\pipe\docker_engine:\\.\pipe\docker_engine` + } + } else { + switch { + case strings.HasPrefix(dockerHost, "unix://"): + bind = fmt.Sprintf("%s:/var/run/docker.sock", strings.TrimPrefix(dockerHost, "unix://")) + case strings.HasPrefix(dockerHost, "npipe://") || strings.HasPrefix(dockerHost, `npipe:\\`): + sub := ([]rune(dockerHost))[8:] + bind = fmt.Sprintf(`%s:\\.\pipe\docker_engine`, string(sub)) + default: + provider.ctrConf.Env = append(provider.ctrConf.Env, fmt.Sprintf(`DOCKER_HOST=%s`, dockerHost)) + } + } + if bind != "" { + provider.hostConf.Binds = append(provider.hostConf.Binds, bind) + } + if provider.os != "windows" { + provider.hostConf.SecurityOpt = []string{"label=disable"} + } + } +} + +func WithEnv(envs ...string) PhaseConfigProviderOperation { + return func(provider *PhaseConfigProvider) { + provider.ctrConf.Env = append(provider.ctrConf.Env, envs...) + } +} + +func WithImage(image string) PhaseConfigProviderOperation { + return func(provider *PhaseConfigProvider) { + provider.ctrConf.Image = image + } +} + +// WithLogPrefix sets a prefix for logs produced by this phase +func WithLogPrefix(prefix string) PhaseConfigProviderOperation { + return func(provider *PhaseConfigProvider) { + if prefix != "" { + provider.infoWriter = logging.NewPrefixWriter(provider.infoWriter, prefix) + provider.errorWriter = logging.NewPrefixWriter(provider.errorWriter, prefix) + } + } +} + +func WithLifecycleProxy(lifecycleExec *LifecycleExecution) PhaseConfigProviderOperation { + return func(provider *PhaseConfigProvider) { + if lifecycleExec.opts.HTTPProxy != "" { + provider.ctrConf.Env = append(provider.ctrConf.Env, "HTTP_PROXY="+lifecycleExec.opts.HTTPProxy, "http_proxy="+lifecycleExec.opts.HTTPProxy) + } + + if lifecycleExec.opts.HTTPSProxy != "" { + provider.ctrConf.Env = append(provider.ctrConf.Env, "HTTPS_PROXY="+lifecycleExec.opts.HTTPSProxy, "https_proxy="+lifecycleExec.opts.HTTPSProxy) + } + + if lifecycleExec.opts.NoProxy != "" { + provider.ctrConf.Env = append(provider.ctrConf.Env, "NO_PROXY="+lifecycleExec.opts.NoProxy, "no_proxy="+lifecycleExec.opts.NoProxy) + } + } +} + +func WithNetwork(networkMode string) PhaseConfigProviderOperation { + return func(provider *PhaseConfigProvider) { + provider.hostConf.NetworkMode = container.NetworkMode(networkMode) + } +} + +func WithRegistryAccess(authConfig string) PhaseConfigProviderOperation { + return func(provider *PhaseConfigProvider) { + provider.ctrConf.Env = append(provider.ctrConf.Env, fmt.Sprintf(`CNB_REGISTRY_AUTH=%s`, authConfig)) + } +} + +func WithRoot() PhaseConfigProviderOperation { + return func(provider *PhaseConfigProvider) { + if provider.os == "windows" { + provider.ctrConf.User = windowsContainerAdmin + } else { + provider.ctrConf.User = linuxContainerAdmin + } + } +} + +func WithContainerOperations(operations ...ContainerOperation) PhaseConfigProviderOperation { + return func(provider *PhaseConfigProvider) { + provider.containerOps = append(provider.containerOps, operations...) + } +} + +func WithPostContainerRunOperations(operations ...ContainerOperation) PhaseConfigProviderOperation { + return func(provider *PhaseConfigProvider) { + provider.postContainerRunOps = append(provider.postContainerRunOps, operations...) + } +} + +func If(expression bool, operation PhaseConfigProviderOperation) PhaseConfigProviderOperation { + if expression { + return operation + } + + return NullOp() +} diff --git a/vendor/github.com/buildpacks/pack/internal/build/phase_factory.go b/vendor/github.com/buildpacks/pack/internal/build/phase_factory.go new file mode 100644 index 0000000000..97ae788349 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/build/phase_factory.go @@ -0,0 +1,40 @@ +package build + +import "context" + +type RunnerCleaner interface { + Run(ctx context.Context) error + Cleanup() error +} + +type PhaseFactory interface { + New(provider *PhaseConfigProvider) RunnerCleaner +} + +type DefaultPhaseFactory struct { + lifecycleExec *LifecycleExecution +} + +type PhaseFactoryCreator func(*LifecycleExecution) PhaseFactory + +func NewDefaultPhaseFactory(lifecycleExec *LifecycleExecution) PhaseFactory { + return &DefaultPhaseFactory{lifecycleExec: lifecycleExec} +} + +func (m *DefaultPhaseFactory) New(provider *PhaseConfigProvider) RunnerCleaner { + return &Phase{ + ctrConf: provider.ContainerConfig(), + hostConf: provider.HostConfig(), + name: provider.Name(), + docker: m.lifecycleExec.docker, + infoWriter: provider.InfoWriter(), + errorWriter: provider.ErrorWriter(), + handler: provider.handler, + uid: m.lifecycleExec.opts.Builder.UID(), + gid: m.lifecycleExec.opts.Builder.GID(), + appPath: m.lifecycleExec.opts.AppPath, + containerOps: provider.containerOps, + postContainerRunOps: provider.postContainerRunOps, + fileFilter: m.lifecycleExec.opts.FileFilter, + } +} diff --git a/vendor/github.com/buildpacks/pack/internal/builder/builder.go b/vendor/github.com/buildpacks/pack/internal/builder/builder.go new file mode 100644 index 0000000000..9bf14b233b --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/builder/builder.go @@ -0,0 +1,832 @@ +package builder + +import ( + "archive/tar" + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path" + "path/filepath" + "regexp" + "strconv" + "strings" + "time" + + "github.com/BurntSushi/toml" + "github.com/buildpacks/imgutil" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/builder" + "github.com/buildpacks/pack/internal/layer" + "github.com/buildpacks/pack/internal/stack" + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/archive" + "github.com/buildpacks/pack/pkg/buildpack" + "github.com/buildpacks/pack/pkg/dist" + "github.com/buildpacks/pack/pkg/logging" +) + +const ( + packName = "Pack CLI" + + cnbDir = "/cnb" + buildpacksDir = "/cnb/buildpacks" + + orderPath = "/cnb/order.toml" + stackPath = "/cnb/stack.toml" + platformDir = "/platform" + lifecycleDir = "/cnb/lifecycle" + compatLifecycleDir = "/lifecycle" + workspaceDir = "/workspace" + layersDir = "/layers" + + metadataLabel = "io.buildpacks.builder.metadata" + stackLabel = "io.buildpacks.stack.id" + + EnvUID = "CNB_USER_ID" + EnvGID = "CNB_GROUP_ID" + + BuildpackOnBuilderMessage = `buildpack %s already exists on builder and will be overwritten + - existing diffID: %s + - new diffID: %s` + + BuildpackPreviouslyDefinedMessage = `buildpack %s was previously defined with different contents and will be overwritten + - previous diffID: %s + - using diffID: %s` +) + +// Builder represents a pack builder, used to build images +type Builder struct { + baseImageName string + image imgutil.Image + layerWriterFactory archive.TarWriterFactory + lifecycle Lifecycle + lifecycleDescriptor LifecycleDescriptor + additionalBuildpacks []buildpack.Buildpack + metadata Metadata + mixins []string + env map[string]string + uid, gid int + StackID string + replaceOrder bool + order dist.Order +} + +type orderTOML struct { + Order dist.Order `toml:"order"` +} + +// FromImage constructs a builder from a builder image +func FromImage(img imgutil.Image) (*Builder, error) { + var metadata Metadata + if ok, err := dist.GetLabel(img, metadataLabel, &metadata); err != nil { + return nil, errors.Wrapf(err, "getting label %s", metadataLabel) + } else if !ok { + return nil, fmt.Errorf("builder %s missing label %s -- try recreating builder", style.Symbol(img.Name()), style.Symbol(metadataLabel)) + } + return constructBuilder(img, "", metadata) +} + +// New constructs a new builder from a base image +func New(baseImage imgutil.Image, name string) (*Builder, error) { + var metadata Metadata + if _, err := dist.GetLabel(baseImage, metadataLabel, &metadata); err != nil { + return nil, errors.Wrapf(err, "getting label %s", metadataLabel) + } + return constructBuilder(baseImage, name, metadata) +} + +func constructBuilder(img imgutil.Image, newName string, metadata Metadata) (*Builder, error) { + imageOS, err := img.OS() + if err != nil { + return nil, errors.Wrap(err, "getting image OS") + } + layerWriterFactory, err := layer.NewWriterFactory(imageOS) + if err != nil { + return nil, err + } + + bldr := &Builder{ + baseImageName: img.Name(), + image: img, + layerWriterFactory: layerWriterFactory, + metadata: metadata, + lifecycleDescriptor: constructLifecycleDescriptor(metadata), + env: map[string]string{}, + } + + if err := addImgLabelsToBuildr(bldr); err != nil { + return nil, errors.Wrap(err, "adding image labels to builder") + } + + if newName != "" && img.Name() != newName { + img.Rename(newName) + } + + return bldr, nil +} + +func constructLifecycleDescriptor(metadata Metadata) LifecycleDescriptor { + return CompatDescriptor(LifecycleDescriptor{ + Info: LifecycleInfo{ + Version: metadata.Lifecycle.Version, + }, + API: metadata.Lifecycle.API, + APIs: metadata.Lifecycle.APIs, + }) +} + +func addImgLabelsToBuildr(bldr *Builder) error { + var err error + bldr.uid, bldr.gid, err = userAndGroupIDs(bldr.image) + if err != nil { + return err + } + + bldr.StackID, err = bldr.image.Label(stackLabel) + if err != nil { + return errors.Wrapf(err, "get label %s from image %s", style.Symbol(stackLabel), style.Symbol(bldr.image.Name())) + } + if bldr.StackID == "" { + return fmt.Errorf("image %s missing label %s", style.Symbol(bldr.image.Name()), style.Symbol(stackLabel)) + } + + if _, err = dist.GetLabel(bldr.image, stack.MixinsLabel, &bldr.mixins); err != nil { + return errors.Wrapf(err, "getting label %s", stack.MixinsLabel) + } + + if _, err = dist.GetLabel(bldr.image, OrderLabel, &bldr.order); err != nil { + return errors.Wrapf(err, "getting label %s", OrderLabel) + } + + return nil +} + +// Getters + +// Description returns the builder description +func (b *Builder) Description() string { + return b.metadata.Description +} + +// LifecycleDescriptor returns the LifecycleDescriptor +func (b *Builder) LifecycleDescriptor() LifecycleDescriptor { + return b.lifecycleDescriptor +} + +// Buildpacks returns the buildpack list +func (b *Builder) Buildpacks() []dist.BuildpackInfo { + return b.metadata.Buildpacks +} + +// CreatedBy returns metadata around the creation of the builder +func (b *Builder) CreatedBy() CreatorMetadata { + return b.metadata.CreatedBy +} + +// Order returns the order +func (b *Builder) Order() dist.Order { + return b.order +} + +// BaseImageName returns the name of the builder base image +func (b *Builder) BaseImageName() string { + return b.baseImageName +} + +// Name returns the name of the builder +func (b *Builder) Name() string { + return b.image.Name() +} + +// Image returns the base image +func (b *Builder) Image() imgutil.Image { + return b.image +} + +// Stack returns the stack metadata +func (b *Builder) Stack() StackMetadata { + return b.metadata.Stack +} + +// Mixins returns the mixins of the builder +func (b *Builder) Mixins() []string { + return b.mixins +} + +// UID returns the UID of the builder +func (b *Builder) UID() int { + return b.uid +} + +// GID returns the GID of the builder +func (b *Builder) GID() int { + return b.gid +} + +// Setters + +// AddBuildpack adds a buildpack to the builder +func (b *Builder) AddBuildpack(bp buildpack.Buildpack) { + b.additionalBuildpacks = append(b.additionalBuildpacks, bp) + b.metadata.Buildpacks = append(b.metadata.Buildpacks, bp.Descriptor().Info) +} + +// SetLifecycle sets the lifecycle of the builder +func (b *Builder) SetLifecycle(lifecycle Lifecycle) { + b.lifecycle = lifecycle + b.lifecycleDescriptor = lifecycle.Descriptor() +} + +// SetEnv sets an environment variable to a value +func (b *Builder) SetEnv(env map[string]string) { + b.env = env +} + +// SetOrder sets the order of the builder +func (b *Builder) SetOrder(order dist.Order) { + b.order = order + b.replaceOrder = true +} + +// SetDescription sets the description of the builder +func (b *Builder) SetDescription(description string) { + b.metadata.Description = description +} + +// SetStack sets the stack of the builder +func (b *Builder) SetStack(stackConfig builder.StackConfig) { + b.metadata.Stack = StackMetadata{ + RunImage: RunImageMetadata{ + Image: stackConfig.RunImage, + Mirrors: stackConfig.RunImageMirrors, + }, + } +} + +// Save saves the builder +func (b *Builder) Save(logger logging.Logger, creatorMetadata CreatorMetadata) error { + logger.Debugf("Creating builder with the following buildpacks:") + for _, bpInfo := range b.metadata.Buildpacks { + logger.Debugf("-> %s", style.Symbol(bpInfo.FullName())) + } + + resolvedOrder, err := processOrder(b.metadata.Buildpacks, b.order) + if err != nil { + return errors.Wrap(err, "processing order") + } + + tmpDir, err := ioutil.TempDir("", "create-builder-scratch") + if err != nil { + return err + } + defer os.RemoveAll(tmpDir) + + dirsTar, err := b.defaultDirsLayer(tmpDir) + if err != nil { + return err + } + if err := b.image.AddLayer(dirsTar); err != nil { + return errors.Wrap(err, "adding default dirs layer") + } + + if b.lifecycle != nil { + lifecycleDescriptor := b.lifecycle.Descriptor() + b.metadata.Lifecycle.LifecycleInfo = lifecycleDescriptor.Info + b.metadata.Lifecycle.API = lifecycleDescriptor.API + b.metadata.Lifecycle.APIs = lifecycleDescriptor.APIs + lifecycleTar, err := b.lifecycleLayer(tmpDir) + if err != nil { + return err + } + if err := b.image.AddLayer(lifecycleTar); err != nil { + return errors.Wrap(err, "adding lifecycle layer") + } + } + + if err := validateBuildpacks(b.StackID, b.Mixins(), b.LifecycleDescriptor(), b.Buildpacks(), b.additionalBuildpacks); err != nil { + return errors.Wrap(err, "validating buildpacks") + } + + bpLayers := dist.BuildpackLayers{} + if _, err := dist.GetLabel(b.image, dist.BuildpackLayersLabel, &bpLayers); err != nil { + return errors.Wrapf(err, "getting label %s", dist.BuildpackLayersLabel) + } + + err = b.addBuildpacks(logger, tmpDir, b.image, b.additionalBuildpacks, bpLayers) + if err != nil { + return err + } + + if err := dist.SetLabel(b.image, dist.BuildpackLayersLabel, bpLayers); err != nil { + return err + } + + if b.replaceOrder { + orderTar, err := b.orderLayer(resolvedOrder, tmpDir) + if err != nil { + return err + } + if err := b.image.AddLayer(orderTar); err != nil { + return errors.Wrap(err, "adding order.tar layer") + } + + if err := dist.SetLabel(b.image, OrderLabel, b.order); err != nil { + return err + } + } + + stackTar, err := b.stackLayer(tmpDir) + if err != nil { + return err + } + if err := b.image.AddLayer(stackTar); err != nil { + return errors.Wrap(err, "adding stack.tar layer") + } + + if len(b.env) > 0 { + logger.Debugf("Provided Environment Variables\n %s", style.Map(b.env, " ", "\n")) + } + + envTar, err := b.envLayer(tmpDir, b.env) + if err != nil { + return err + } + + if err := b.image.AddLayer(envTar); err != nil { + return errors.Wrap(err, "adding env layer") + } + + if creatorMetadata.Name == "" { + creatorMetadata.Name = packName + } + + b.metadata.CreatedBy = creatorMetadata + + if err := dist.SetLabel(b.image, metadataLabel, b.metadata); err != nil { + return err + } + + if err := dist.SetLabel(b.image, stack.MixinsLabel, b.mixins); err != nil { + return err + } + + if err := b.image.SetWorkingDir(layersDir); err != nil { + return errors.Wrap(err, "failed to set working dir") + } + + return b.image.Save() +} + +// Helpers + +func (b *Builder) addBuildpacks(logger logging.Logger, tmpDir string, image imgutil.Image, additionalBuildpacks []buildpack.Buildpack, bpLayers dist.BuildpackLayers) error { + type buildpackToAdd struct { + tarPath string + diffID string + buildpack buildpack.Buildpack + } + + buildpacksToAdd := map[string]buildpackToAdd{} + for i, bp := range additionalBuildpacks { + // create buildpack directory + bpTmpDir := filepath.Join(tmpDir, strconv.Itoa(i)) + if err := os.MkdirAll(bpTmpDir, os.ModePerm); err != nil { + return errors.Wrap(err, "creating buildpack temp dir") + } + + // create tar file + bpLayerTar, err := buildpack.ToLayerTar(bpTmpDir, bp) + if err != nil { + return err + } + + // generate diff id + diffID, err := dist.LayerDiffID(bpLayerTar) + if err != nil { + return errors.Wrapf(err, + "getting content hashes for buildpack %s", + style.Symbol(bp.Descriptor().Info.FullName()), + ) + } + + bpInfo := bp.Descriptor().Info + // check against builder layers + if existingBPInfo, ok := bpLayers[bpInfo.ID][bpInfo.Version]; ok { + if existingBPInfo.LayerDiffID == diffID.String() { + logger.Debugf("Buildpack %s already exists on builder with same contents, skipping...", style.Symbol(bpInfo.FullName())) + continue + } else { + whiteoutsTar, err := b.whiteoutLayer(tmpDir, i, bpInfo) + if err != nil { + return err + } + + if err := image.AddLayer(whiteoutsTar); err != nil { + return errors.Wrap(err, "adding whiteout layer tar") + } + } + + logger.Debugf(BuildpackOnBuilderMessage, style.Symbol(bpInfo.FullName()), style.Symbol(existingBPInfo.LayerDiffID), style.Symbol(diffID.String())) + } + + // check against other buildpacks to be added + if otherAdditionalBP, ok := buildpacksToAdd[bp.Descriptor().Info.FullName()]; ok { + if otherAdditionalBP.diffID == diffID.String() { + logger.Debugf("Buildpack %s with same contents is already being added, skipping...", style.Symbol(bpInfo.FullName())) + continue + } + + logger.Debugf(BuildpackPreviouslyDefinedMessage, style.Symbol(bpInfo.FullName()), style.Symbol(otherAdditionalBP.diffID), style.Symbol(diffID.String())) + } + + // note: if same id@version is in additionalBuildpacks, last one wins (see warnings above) + buildpacksToAdd[bp.Descriptor().Info.FullName()] = buildpackToAdd{ + tarPath: bpLayerTar, + diffID: diffID.String(), + buildpack: bp, + } + } + + for _, bp := range buildpacksToAdd { + logger.Debugf("Adding buildpack %s (diffID=%s)", style.Symbol(bp.buildpack.Descriptor().Info.FullName()), bp.diffID) + if err := image.AddLayerWithDiffID(bp.tarPath, bp.diffID); err != nil { + return errors.Wrapf(err, + "adding layer tar for buildpack %s", + style.Symbol(bp.buildpack.Descriptor().Info.FullName()), + ) + } + + dist.AddBuildpackToLayersMD(bpLayers, bp.buildpack.Descriptor(), bp.diffID) + } + + return nil +} + +func processOrder(buildpacks []dist.BuildpackInfo, order dist.Order) (dist.Order, error) { + resolvedOrder := dist.Order{} + + for gi, g := range order { + resolvedOrder = append(resolvedOrder, dist.OrderEntry{}) + + for _, bpRef := range g.Group { + var matchingBps []dist.BuildpackInfo + for _, bp := range buildpacks { + if bpRef.ID == bp.ID { + matchingBps = append(matchingBps, bp) + } + } + + if len(matchingBps) == 0 { + return dist.Order{}, fmt.Errorf("no versions of buildpack %s were found on the builder", style.Symbol(bpRef.ID)) + } + + if bpRef.Version == "" { + if len(uniqueVersions(matchingBps)) > 1 { + return dist.Order{}, fmt.Errorf("unable to resolve version: multiple versions of %s - must specify an explicit version", style.Symbol(bpRef.ID)) + } + + bpRef.Version = matchingBps[0].Version + } + + if !hasBuildpackWithVersion(matchingBps, bpRef.Version) { + return dist.Order{}, fmt.Errorf("buildpack %s with version %s was not found on the builder", style.Symbol(bpRef.ID), style.Symbol(bpRef.Version)) + } + + resolvedOrder[gi].Group = append(resolvedOrder[gi].Group, bpRef) + } + } + + return resolvedOrder, nil +} + +func hasBuildpackWithVersion(bps []dist.BuildpackInfo, version string) bool { + for _, bp := range bps { + if bp.Version == version { + return true + } + } + return false +} + +func validateBuildpacks(stackID string, mixins []string, lifecycleDescriptor LifecycleDescriptor, allBuildpacks []dist.BuildpackInfo, bpsToValidate []buildpack.Buildpack) error { + bpLookup := map[string]interface{}{} + + for _, bp := range allBuildpacks { + bpLookup[bp.FullName()] = nil + } + + for _, bp := range bpsToValidate { + bpd := bp.Descriptor() + + // TODO: Warn when Buildpack API is deprecated - https://github.com/buildpacks/pack/issues/788 + compatible := false + for _, version := range append(lifecycleDescriptor.APIs.Buildpack.Supported, lifecycleDescriptor.APIs.Buildpack.Deprecated...) { + compatible = version.Compare(bpd.API) == 0 + if compatible { + break + } + } + + if !compatible { + return fmt.Errorf( + "buildpack %s (Buildpack API %s) is incompatible with lifecycle %s (Buildpack API(s) %s)", + style.Symbol(bpd.Info.FullName()), + bpd.API.String(), + style.Symbol(lifecycleDescriptor.Info.Version.String()), + strings.Join(lifecycleDescriptor.APIs.Buildpack.Supported.AsStrings(), ", "), + ) + } + + if len(bpd.Stacks) >= 1 { // standard buildpack + if err := bpd.EnsureStackSupport(stackID, mixins, false); err != nil { + return err + } + } else { // order buildpack + for _, g := range bpd.Order { + for _, r := range g.Group { + if _, ok := bpLookup[r.FullName()]; !ok { + return fmt.Errorf( + "buildpack %s not found on the builder", + style.Symbol(r.FullName()), + ) + } + } + } + } + } + + return nil +} + +func userAndGroupIDs(img imgutil.Image) (int, int, error) { + sUID, err := img.Env(EnvUID) + if err != nil { + return 0, 0, errors.Wrap(err, "reading builder env variables") + } else if sUID == "" { + return 0, 0, fmt.Errorf("image %s missing required env var %s", style.Symbol(img.Name()), style.Symbol(EnvUID)) + } + + sGID, err := img.Env(EnvGID) + if err != nil { + return 0, 0, errors.Wrap(err, "reading builder env variables") + } else if sGID == "" { + return 0, 0, fmt.Errorf("image %s missing required env var %s", style.Symbol(img.Name()), style.Symbol(EnvGID)) + } + + var uid, gid int + uid, err = strconv.Atoi(sUID) + if err != nil { + return 0, 0, fmt.Errorf("failed to parse %s, value %s should be an integer", style.Symbol(EnvUID), style.Symbol(sUID)) + } + + gid, err = strconv.Atoi(sGID) + if err != nil { + return 0, 0, fmt.Errorf("failed to parse %s, value %s should be an integer", style.Symbol(EnvGID), style.Symbol(sGID)) + } + + return uid, gid, nil +} + +func uniqueVersions(buildpacks []dist.BuildpackInfo) []string { + results := []string{} + set := map[string]interface{}{} + for _, bpInfo := range buildpacks { + _, ok := set[bpInfo.Version] + if !ok { + results = append(results, bpInfo.Version) + set[bpInfo.Version] = true + } + } + return results +} + +func (b *Builder) defaultDirsLayer(dest string) (string, error) { + fh, err := os.Create(filepath.Join(dest, "dirs.tar")) + if err != nil { + return "", err + } + defer fh.Close() + + lw := b.layerWriterFactory.NewWriter(fh) + defer lw.Close() + + ts := archive.NormalizedDateTime + + for _, path := range []string{workspaceDir, layersDir} { + if err := lw.WriteHeader(b.packOwnedDir(path, ts)); err != nil { + return "", errors.Wrapf(err, "creating %s dir in layer", style.Symbol(path)) + } + } + + // can't use filepath.Join(), to ensure Windows doesn't transform it to Windows join + for _, path := range []string{cnbDir, dist.BuildpacksDir, platformDir, platformDir + "/env"} { + if err := lw.WriteHeader(b.rootOwnedDir(path, ts)); err != nil { + return "", errors.Wrapf(err, "creating %s dir in layer", style.Symbol(path)) + } + } + + return fh.Name(), nil +} + +func (b *Builder) packOwnedDir(path string, time time.Time) *tar.Header { + return &tar.Header{ + Typeflag: tar.TypeDir, + Name: path, + Mode: 0755, + ModTime: time, + Uid: b.uid, + Gid: b.gid, + } +} + +func (b *Builder) rootOwnedDir(path string, time time.Time) *tar.Header { + return &tar.Header{ + Typeflag: tar.TypeDir, + Name: path, + Mode: 0755, + ModTime: time, + } +} + +func (b *Builder) lifecycleLayer(dest string) (string, error) { + fh, err := os.Create(filepath.Join(dest, "lifecycle.tar")) + if err != nil { + return "", err + } + defer fh.Close() + + lw := b.layerWriterFactory.NewWriter(fh) + defer lw.Close() + + if err := lw.WriteHeader(&tar.Header{ + Typeflag: tar.TypeDir, + Name: lifecycleDir, + Mode: 0755, + ModTime: archive.NormalizedDateTime, + }); err != nil { + return "", err + } + + err = b.embedLifecycleTar(lw) + if err != nil { + return "", errors.Wrap(err, "embedding lifecycle tar") + } + + if err := lw.WriteHeader(&tar.Header{ + Name: compatLifecycleDir, + Linkname: lifecycleDir, + Typeflag: tar.TypeSymlink, + Mode: 0644, + ModTime: archive.NormalizedDateTime, + }); err != nil { + return "", errors.Wrapf(err, "creating %s symlink", style.Symbol(compatLifecycleDir)) + } + + return fh.Name(), nil +} + +func (b *Builder) embedLifecycleTar(tw archive.TarWriter) error { + var regex = regexp.MustCompile(`^[^/]+/([^/]+)$`) + + lr, err := b.lifecycle.Open() + if err != nil { + return errors.Wrap(err, "failed to open lifecycle") + } + defer lr.Close() + tr := tar.NewReader(lr) + for { + header, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return errors.Wrap(err, "failed to get next tar entry") + } + + pathMatches := regex.FindStringSubmatch(path.Clean(header.Name)) + if pathMatches != nil { + binaryName := pathMatches[1] + + header.Name = lifecycleDir + "/" + binaryName + err = tw.WriteHeader(header) + if err != nil { + return errors.Wrapf(err, "failed to write header for '%s'", header.Name) + } + + buf, err := ioutil.ReadAll(tr) + if err != nil { + return errors.Wrapf(err, "failed to read contents of '%s'", header.Name) + } + + _, err = tw.Write(buf) + if err != nil { + return errors.Wrapf(err, "failed to write contents to '%s'", header.Name) + } + } + } + + return nil +} + +func (b *Builder) orderLayer(order dist.Order, dest string) (string, error) { + contents, err := orderFileContents(order) + if err != nil { + return "", err + } + + layerTar := filepath.Join(dest, "order.tar") + err = layer.CreateSingleFileTar(layerTar, orderPath, contents, b.layerWriterFactory) + if err != nil { + return "", errors.Wrapf(err, "failed to create order.toml layer tar") + } + + return layerTar, nil +} + +func orderFileContents(order dist.Order) (string, error) { + buf := &bytes.Buffer{} + + tomlData := orderTOML{Order: order} + if err := toml.NewEncoder(buf).Encode(tomlData); err != nil { + return "", errors.Wrapf(err, "failed to marshal order.toml") + } + return buf.String(), nil +} + +func (b *Builder) stackLayer(dest string) (string, error) { + buf := &bytes.Buffer{} + err := toml.NewEncoder(buf).Encode(b.metadata.Stack) + if err != nil { + return "", errors.Wrapf(err, "failed to marshal stack.toml") + } + + layerTar := filepath.Join(dest, "stack.tar") + err = layer.CreateSingleFileTar(layerTar, stackPath, buf.String(), b.layerWriterFactory) + if err != nil { + return "", errors.Wrapf(err, "failed to create stack.toml layer tar") + } + + return layerTar, nil +} + +func (b *Builder) envLayer(dest string, env map[string]string) (string, error) { + fh, err := os.Create(filepath.Join(dest, "env.tar")) + if err != nil { + return "", err + } + defer fh.Close() + + lw := b.layerWriterFactory.NewWriter(fh) + defer lw.Close() + + for k, v := range env { + if err := lw.WriteHeader(&tar.Header{ + Name: path.Join(platformDir, "env", k), + Size: int64(len(v)), + Mode: 0644, + ModTime: archive.NormalizedDateTime, + }); err != nil { + return "", err + } + if _, err := lw.Write([]byte(v)); err != nil { + return "", err + } + } + + return fh.Name(), nil +} + +func (b *Builder) whiteoutLayer(tmpDir string, i int, bpInfo dist.BuildpackInfo) (string, error) { + bpWhiteoutsTmpDir := filepath.Join(tmpDir, strconv.Itoa(i)+"_whiteouts") + if err := os.MkdirAll(bpWhiteoutsTmpDir, os.ModePerm); err != nil { + return "", errors.Wrap(err, "creating buildpack whiteouts temp dir") + } + + fh, err := os.Create(filepath.Join(bpWhiteoutsTmpDir, "whiteouts.tar")) + if err != nil { + return "", err + } + defer fh.Close() + + lw := b.layerWriterFactory.NewWriter(fh) + defer lw.Close() + + if err := lw.WriteHeader(&tar.Header{ + Name: path.Join(buildpacksDir, strings.ReplaceAll(bpInfo.ID, "/", "_"), fmt.Sprintf(".wh.%s", bpInfo.Version)), + Size: int64(0), + Mode: 0644, + }); err != nil { + return "", err + } + if _, err := lw.Write([]byte("")); err != nil { + return "", errors.Wrapf(err, + "creating whiteout layers' tarfile for buildpack %s", + style.Symbol(bpInfo.FullName()), + ) + } + + return fh.Name(), nil +} diff --git a/vendor/github.com/buildpacks/pack/internal/builder/descriptor.go b/vendor/github.com/buildpacks/pack/internal/builder/descriptor.go new file mode 100644 index 0000000000..22017db2e7 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/builder/descriptor.go @@ -0,0 +1,111 @@ +package builder + +import ( + "github.com/BurntSushi/toml" + "github.com/buildpacks/lifecycle/api" + "github.com/pkg/errors" +) + +// LifecycleDescriptor contains information described in the lifecycle.toml +type LifecycleDescriptor struct { + Info LifecycleInfo `toml:"lifecycle"` + // Deprecated: Use `LifecycleAPIs` instead + API LifecycleAPI `toml:"api"` + APIs LifecycleAPIs `toml:"apis"` +} + +// LifecycleInfo contains information about the lifecycle +type LifecycleInfo struct { + Version *Version `toml:"version" json:"version" yaml:"version"` +} + +// LifecycleAPI describes which API versions the lifecycle satisfies +type LifecycleAPI struct { + BuildpackVersion *api.Version `toml:"buildpack" json:"buildpack"` + PlatformVersion *api.Version `toml:"platform" json:"platform"` +} + +// LifecycleAPIs describes the supported API versions per specification +type LifecycleAPIs struct { + Buildpack APIVersions `toml:"buildpack" json:"buildpack"` + Platform APIVersions `toml:"platform" json:"platform"` +} + +type APISet []*api.Version + +func (a APISet) search(comp func(prevMatch, value *api.Version) bool) *api.Version { + var match *api.Version + for _, version := range a { + switch { + case version == nil: + continue + case match == nil: + match = version + case comp(match, version): + match = version + } + } + + return match +} + +func (a APISet) Earliest() *api.Version { + return a.search(func(prevMatch, value *api.Version) bool { return value.Compare(prevMatch) < 0 }) +} + +func (a APISet) Latest() *api.Version { + return a.search(func(prevMatch, value *api.Version) bool { return value.Compare(prevMatch) > 0 }) +} + +func (a APISet) AsStrings() []string { + verStrings := make([]string, len(a)) + for i, version := range a { + verStrings[i] = version.String() + } + + return verStrings +} + +// APIVersions describes the supported API versions +type APIVersions struct { + Deprecated APISet `toml:"deprecated" json:"deprecated" yaml:"deprecated"` + Supported APISet `toml:"supported" json:"supported" yaml:"supported"` +} + +// ParseDescriptor parses LifecycleDescriptor from toml formatted string. +func ParseDescriptor(contents string) (LifecycleDescriptor, error) { + descriptor := LifecycleDescriptor{} + _, err := toml.Decode(contents, &descriptor) + if err != nil { + return descriptor, errors.Wrap(err, "decoding descriptor") + } + + return descriptor, nil +} + +// CompatDescriptor provides compatibility by mapping new fields to old and vice-versa +func CompatDescriptor(descriptor LifecycleDescriptor) LifecycleDescriptor { + if len(descriptor.APIs.Buildpack.Supported) != 0 || len(descriptor.APIs.Platform.Supported) != 0 { + // select earliest value for deprecated parameters + if len(descriptor.APIs.Buildpack.Supported) != 0 { + descriptor.API.BuildpackVersion = + append(descriptor.APIs.Buildpack.Deprecated, descriptor.APIs.Buildpack.Supported...).Earliest() + } + if len(descriptor.APIs.Platform.Supported) != 0 { + descriptor.API.PlatformVersion = + append(descriptor.APIs.Platform.Deprecated, descriptor.APIs.Platform.Supported...).Earliest() + } + } else if descriptor.API.BuildpackVersion != nil && descriptor.API.PlatformVersion != nil { + // fill supported with deprecated field + descriptor.APIs = LifecycleAPIs{ + Buildpack: APIVersions{ + Supported: APISet{descriptor.API.BuildpackVersion}, + }, + Platform: APIVersions{ + Supported: APISet{descriptor.API.PlatformVersion}, + }, + } + } + + return descriptor +} diff --git a/vendor/github.com/buildpacks/pack/internal/builder/detection_order_calculator.go b/vendor/github.com/buildpacks/pack/internal/builder/detection_order_calculator.go new file mode 100644 index 0000000000..d62ad49057 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/builder/detection_order_calculator.go @@ -0,0 +1,106 @@ +package builder + +import ( + pubbldr "github.com/buildpacks/pack/builder" + "github.com/buildpacks/pack/pkg/dist" +) + +type DetectionOrderCalculator struct{} + +func NewDetectionOrderCalculator() *DetectionOrderCalculator { + return &DetectionOrderCalculator{} +} + +type detectionOrderRecurser struct { + layers dist.BuildpackLayers + maxDepth int +} + +func newDetectionOrderRecurser(layers dist.BuildpackLayers, maxDepth int) *detectionOrderRecurser { + return &detectionOrderRecurser{ + layers: layers, + maxDepth: maxDepth, + } +} + +func (c *DetectionOrderCalculator) Order( + order dist.Order, + layers dist.BuildpackLayers, + maxDepth int, +) (pubbldr.DetectionOrder, error) { + recurser := newDetectionOrderRecurser(layers, maxDepth) + + return recurser.detectionOrderFromOrder(order, dist.BuildpackRef{}, 0, map[string]interface{}{}), nil +} + +func (r *detectionOrderRecurser) detectionOrderFromOrder( + order dist.Order, + parentBuildpack dist.BuildpackRef, + currentDepth int, + visited map[string]interface{}, +) pubbldr.DetectionOrder { + var detectionOrder pubbldr.DetectionOrder + for _, orderEntry := range order { + visitedCopy := copyMap(visited) + groupDetectionOrder := r.detectionOrderFromGroup(orderEntry.Group, currentDepth, visitedCopy) + + detectionOrderEntry := pubbldr.DetectionOrderEntry{ + BuildpackRef: parentBuildpack, + GroupDetectionOrder: groupDetectionOrder, + } + + detectionOrder = append(detectionOrder, detectionOrderEntry) + } + + return detectionOrder +} + +func (r *detectionOrderRecurser) detectionOrderFromGroup( + group []dist.BuildpackRef, + currentDepth int, + visited map[string]interface{}, +) pubbldr.DetectionOrder { + var groupDetectionOrder pubbldr.DetectionOrder + + for _, bp := range group { + _, bpSeen := visited[bp.FullName()] + if !bpSeen { + visited[bp.FullName()] = true + } + + layer, ok := r.layers.Get(bp.ID, bp.Version) + if ok && len(layer.Order) > 0 && r.shouldGoDeeper(currentDepth) && !bpSeen { + groupOrder := r.detectionOrderFromOrder(layer.Order, bp, currentDepth+1, visited) + groupDetectionOrder = append(groupDetectionOrder, groupOrder...) + } else { + groupDetectionOrderEntry := pubbldr.DetectionOrderEntry{ + BuildpackRef: bp, + Cyclical: bpSeen, + } + groupDetectionOrder = append(groupDetectionOrder, groupDetectionOrderEntry) + } + } + + return groupDetectionOrder +} + +func (r *detectionOrderRecurser) shouldGoDeeper(currentDepth int) bool { + if r.maxDepth == pubbldr.OrderDetectionMaxDepth { + return true + } + + if currentDepth < r.maxDepth { + return true + } + + return false +} + +func copyMap(toCopy map[string]interface{}) map[string]interface{} { + result := make(map[string]interface{}, len(toCopy)) + for key := range toCopy { + result[key] = true + } + + return result +} diff --git a/vendor/github.com/buildpacks/pack/internal/builder/image_fetcher_wrapper.go b/vendor/github.com/buildpacks/pack/internal/builder/image_fetcher_wrapper.go new file mode 100644 index 0000000000..dfb0de4a83 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/builder/image_fetcher_wrapper.go @@ -0,0 +1,34 @@ +package builder + +import ( + "context" + + "github.com/buildpacks/imgutil" + + "github.com/buildpacks/pack/pkg/image" +) + +type ImageFetcher interface { + // Fetch fetches an image by resolving it both remotely and locally depending on provided parameters. + // If daemon is true, it will look return a `local.Image`. Pull, applicable only when daemon is true, will + // attempt to pull a remote image first. + Fetch(ctx context.Context, name string, options image.FetchOptions) (imgutil.Image, error) +} + +type ImageFetcherWrapper struct { + fetcher ImageFetcher +} + +func NewImageFetcherWrapper(fetcher ImageFetcher) *ImageFetcherWrapper { + return &ImageFetcherWrapper{ + fetcher: fetcher, + } +} + +func (w *ImageFetcherWrapper) Fetch( + ctx context.Context, + name string, + options image.FetchOptions, +) (Inspectable, error) { + return w.fetcher.Fetch(ctx, name, options) +} diff --git a/vendor/github.com/buildpacks/pack/internal/builder/inspect.go b/vendor/github.com/buildpacks/pack/internal/builder/inspect.go new file mode 100644 index 0000000000..7a3cc6ca09 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/builder/inspect.go @@ -0,0 +1,158 @@ +package builder + +import ( + "context" + "fmt" + "sort" + "strings" + + pubbldr "github.com/buildpacks/pack/builder" + "github.com/buildpacks/pack/pkg/dist" + "github.com/buildpacks/pack/pkg/image" +) + +type Info struct { + Description string + StackID string + Mixins []string + RunImage string + RunImageMirrors []string + Buildpacks []dist.BuildpackInfo + Order pubbldr.DetectionOrder + BuildpackLayers dist.BuildpackLayers + Lifecycle LifecycleDescriptor + CreatedBy CreatorMetadata +} + +type Inspectable interface { + Label(name string) (string, error) +} + +type InspectableFetcher interface { + Fetch(ctx context.Context, name string, options image.FetchOptions) (Inspectable, error) +} + +type LabelManagerFactory interface { + BuilderLabelManager(inspectable Inspectable) LabelInspector +} + +type LabelInspector interface { + Metadata() (Metadata, error) + StackID() (string, error) + Mixins() ([]string, error) + Order() (dist.Order, error) + BuildpackLayers() (dist.BuildpackLayers, error) +} + +type DetectionCalculator interface { + Order(topOrder dist.Order, layers dist.BuildpackLayers, depth int) (pubbldr.DetectionOrder, error) +} + +type Inspector struct { + imageFetcher InspectableFetcher + labelManagerFactory LabelManagerFactory + detectionOrderCalculator DetectionCalculator +} + +func NewInspector(fetcher InspectableFetcher, factory LabelManagerFactory, calculator DetectionCalculator) *Inspector { + return &Inspector{ + imageFetcher: fetcher, + labelManagerFactory: factory, + detectionOrderCalculator: calculator, + } +} + +func (i *Inspector) Inspect(name string, daemon bool, orderDetectionDepth int) (Info, error) { + inspectable, err := i.imageFetcher.Fetch(context.Background(), name, image.FetchOptions{Daemon: daemon, PullPolicy: image.PullNever}) + if err != nil { + return Info{}, fmt.Errorf("fetching builder image: %w", err) + } + + labelManager := i.labelManagerFactory.BuilderLabelManager(inspectable) + + metadata, err := labelManager.Metadata() + if err != nil { + return Info{}, fmt.Errorf("reading image metadata: %w", err) + } + + stackID, err := labelManager.StackID() + if err != nil { + return Info{}, fmt.Errorf("reading image stack id: %w", err) + } + + mixins, err := labelManager.Mixins() + if err != nil { + return Info{}, fmt.Errorf("reading image mixins: %w", err) + } + + var commonMixins, buildMixins []string + commonMixins = []string{} + for _, mixin := range mixins { + if strings.HasPrefix(mixin, "build:") { + buildMixins = append(buildMixins, mixin) + } else { + commonMixins = append(commonMixins, mixin) + } + } + + order, err := labelManager.Order() + if err != nil { + return Info{}, fmt.Errorf("reading image order: %w", err) + } + + layers, err := labelManager.BuildpackLayers() + if err != nil { + return Info{}, fmt.Errorf("reading image buildpack layers: %w", err) + } + + detectionOrder, err := i.detectionOrderCalculator.Order(order, layers, orderDetectionDepth) + if err != nil { + return Info{}, fmt.Errorf("calculating detection order: %w", err) + } + + lifecycle := CompatDescriptor(LifecycleDescriptor{ + Info: LifecycleInfo{Version: metadata.Lifecycle.Version}, + API: metadata.Lifecycle.API, + APIs: metadata.Lifecycle.APIs, + }) + + return Info{ + Description: metadata.Description, + StackID: stackID, + Mixins: append(commonMixins, buildMixins...), + RunImage: metadata.Stack.RunImage.Image, + RunImageMirrors: metadata.Stack.RunImage.Mirrors, + Buildpacks: sortBuildPacksByID(uniqueBuildpacks(metadata.Buildpacks)), + Order: detectionOrder, + BuildpackLayers: layers, + Lifecycle: lifecycle, + CreatedBy: metadata.CreatedBy, + }, nil +} + +func uniqueBuildpacks(buildpacks []dist.BuildpackInfo) []dist.BuildpackInfo { + foundBuildpacks := map[string]interface{}{} + var uniqueBuildpacks []dist.BuildpackInfo + + for _, bp := range buildpacks { + _, ok := foundBuildpacks[bp.FullName()] + if !ok { + uniqueBuildpacks = append(uniqueBuildpacks, bp) + foundBuildpacks[bp.FullName()] = true + } + } + + return uniqueBuildpacks +} + +func sortBuildPacksByID(buildpacks []dist.BuildpackInfo) []dist.BuildpackInfo { + sort.Slice(buildpacks, func(i int, j int) bool { + if buildpacks[i].ID == buildpacks[j].ID { + return buildpacks[i].Version < buildpacks[j].Version + } + + return buildpacks[i].ID < buildpacks[j].ID + }) + + return buildpacks +} diff --git a/vendor/github.com/buildpacks/pack/internal/builder/label_manager.go b/vendor/github.com/buildpacks/pack/internal/builder/label_manager.go new file mode 100644 index 0000000000..cdbfe760fd --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/builder/label_manager.go @@ -0,0 +1,91 @@ +package builder + +import ( + "encoding/json" + "fmt" + + "github.com/buildpacks/pack/pkg/dist" + + "github.com/buildpacks/pack/internal/stack" +) + +type LabelManager struct { + inspectable Inspectable +} + +func NewLabelManager(inspectable Inspectable) *LabelManager { + return &LabelManager{inspectable: inspectable} +} + +func (m *LabelManager) Metadata() (Metadata, error) { + var parsedMetadata Metadata + err := m.labelJSON(metadataLabel, &parsedMetadata) + return parsedMetadata, err +} + +func (m *LabelManager) StackID() (string, error) { + return m.labelContent(stackLabel) +} + +func (m *LabelManager) Mixins() ([]string, error) { + parsedMixins := []string{} + err := m.labelJSONDefaultEmpty(stack.MixinsLabel, &parsedMixins) + return parsedMixins, err +} + +func (m *LabelManager) Order() (dist.Order, error) { + parsedOrder := dist.Order{} + err := m.labelJSONDefaultEmpty(OrderLabel, &parsedOrder) + return parsedOrder, err +} + +func (m *LabelManager) BuildpackLayers() (dist.BuildpackLayers, error) { + parsedLayers := dist.BuildpackLayers{} + err := m.labelJSONDefaultEmpty(dist.BuildpackLayersLabel, &parsedLayers) + return parsedLayers, err +} + +func (m *LabelManager) labelContent(labelName string) (string, error) { + content, err := m.inspectable.Label(labelName) + if err != nil { + return "", fmt.Errorf("getting label %s: %w", labelName, err) + } + + if content == "" { + return "", fmt.Errorf("builder missing label %s -- try recreating builder", labelName) + } + + return content, nil +} + +func (m *LabelManager) labelJSON(labelName string, targetObject interface{}) error { + rawContent, err := m.labelContent(labelName) + if err != nil { + return err + } + + err = json.Unmarshal([]byte(rawContent), targetObject) + if err != nil { + return fmt.Errorf("parsing label content for %s: %w", labelName, err) + } + + return nil +} + +func (m *LabelManager) labelJSONDefaultEmpty(labelName string, targetObject interface{}) error { + rawContent, err := m.inspectable.Label(labelName) + if err != nil { + return fmt.Errorf("getting label %s: %w", labelName, err) + } + + if rawContent == "" { + return nil + } + + err = json.Unmarshal([]byte(rawContent), targetObject) + if err != nil { + return fmt.Errorf("parsing label content for %s: %w", labelName, err) + } + + return nil +} diff --git a/vendor/github.com/buildpacks/pack/internal/builder/label_manager_provider.go b/vendor/github.com/buildpacks/pack/internal/builder/label_manager_provider.go new file mode 100644 index 0000000000..4c701eda26 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/builder/label_manager_provider.go @@ -0,0 +1,11 @@ +package builder + +type LabelManagerProvider struct{} + +func NewLabelManagerProvider() *LabelManagerProvider { + return &LabelManagerProvider{} +} + +func (p *LabelManagerProvider) BuilderLabelManager(inspectable Inspectable) LabelInspector { + return NewLabelManager(inspectable) +} diff --git a/vendor/github.com/buildpacks/pack/internal/builder/lifecycle.go b/vendor/github.com/buildpacks/pack/internal/builder/lifecycle.go new file mode 100644 index 0000000000..151dcc0bfb --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/builder/lifecycle.go @@ -0,0 +1,121 @@ +package builder + +import ( + "archive/tar" + "fmt" + "io" + "path" + "regexp" + + "github.com/pkg/errors" + + "github.com/buildpacks/pack/pkg/archive" +) + +// A snapshot of the latest tested lifecycle version values +const ( + DefaultLifecycleVersion = "0.13.3" + DefaultBuildpackAPIVersion = "0.2" +) + +// Blob is an interface to wrap opening blobs +type Blob interface { + Open() (io.ReadCloser, error) +} + +// Lifecycle is an implementation of the CNB Lifecycle spec +//go:generate mockgen -package testmocks -destination testmocks/mock_lifecycle.go github.com/buildpacks/pack/internal/builder Lifecycle +type Lifecycle interface { + Blob + Descriptor() LifecycleDescriptor +} + +type lifecycle struct { + descriptor LifecycleDescriptor + Blob +} + +// NewLifecycle creates a Lifecycle from a Blob +func NewLifecycle(blob Blob) (Lifecycle, error) { + var err error + + br, err := blob.Open() + if err != nil { + return nil, errors.Wrap(err, "open lifecycle blob") + } + defer br.Close() + + _, buf, err := archive.ReadTarEntry(br, "lifecycle.toml") + if err != nil && errors.Cause(err) == archive.ErrEntryNotExist { + return nil, err + } else if err != nil { + return nil, errors.Wrap(err, "reading lifecycle descriptor") + } + + lifecycleDescriptor, err := ParseDescriptor(string(buf)) + if err != nil { + return nil, err + } + + lifecycle := &lifecycle{Blob: blob, descriptor: CompatDescriptor(lifecycleDescriptor)} + + if err = lifecycle.validateBinaries(); err != nil { + return nil, errors.Wrap(err, "validating binaries") + } + + return lifecycle, nil +} + +// Descriptor returns the LifecycleDescriptor +func (l *lifecycle) Descriptor() LifecycleDescriptor { + return l.descriptor +} + +func (l *lifecycle) validateBinaries() error { + rc, err := l.Open() + if err != nil { + return errors.Wrap(err, "create lifecycle blob reader") + } + defer rc.Close() + regex := regexp.MustCompile(`^[^/]+/([^/]+)$`) + headers := map[string]bool{} + tr := tar.NewReader(rc) + for { + header, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return errors.Wrap(err, "failed to get next tar entry") + } + + pathMatches := regex.FindStringSubmatch(path.Clean(header.Name)) + if pathMatches != nil { + headers[pathMatches[1]] = true + } + } + for _, p := range l.binaries() { + _, found := headers[p] + if !found { + _, found = headers[p+".exe"] + if !found { + return fmt.Errorf("did not find '%s' in tar", p) + } + } + } + return nil +} + +// Binaries returns a list of all binaries contained in the lifecycle. +func (l *lifecycle) binaries() []string { + binaries := []string{ + "detector", + "restorer", + "analyzer", + "builder", + "exporter", + "launcher", + "creator", + } + return binaries +} diff --git a/vendor/github.com/buildpacks/pack/internal/builder/metadata.go b/vendor/github.com/buildpacks/pack/internal/builder/metadata.go new file mode 100644 index 0000000000..111ec4ef83 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/builder/metadata.go @@ -0,0 +1,36 @@ +package builder + +import "github.com/buildpacks/pack/pkg/dist" + +const ( + OrderLabel = "io.buildpacks.buildpack.order" +) + +type Metadata struct { + Description string `json:"description"` + Buildpacks []dist.BuildpackInfo `json:"buildpacks"` + Stack StackMetadata `json:"stack"` + Lifecycle LifecycleMetadata `json:"lifecycle"` + CreatedBy CreatorMetadata `json:"createdBy"` +} + +type CreatorMetadata struct { + Name string `json:"name" yaml:"name"` + Version string `json:"version" yaml:"version"` +} + +type LifecycleMetadata struct { + LifecycleInfo + // Deprecated: use APIs instead + API LifecycleAPI `json:"api"` + APIs LifecycleAPIs `json:"apis"` +} + +type StackMetadata struct { + RunImage RunImageMetadata `json:"runImage" toml:"run-image"` +} + +type RunImageMetadata struct { + Image string `json:"image" toml:"image"` + Mirrors []string `json:"mirrors" toml:"mirrors"` +} diff --git a/vendor/github.com/buildpacks/pack/internal/builder/suggested_builder.go b/vendor/github.com/buildpacks/pack/internal/builder/suggested_builder.go new file mode 100644 index 0000000000..378ad72e0e --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/builder/suggested_builder.go @@ -0,0 +1,35 @@ +package builder + +type SuggestedBuilder struct { + Vendor string + Image string + DefaultDescription string +} + +var SuggestedBuilders = []SuggestedBuilder{ + { + Vendor: "Google", + Image: "gcr.io/buildpacks/builder:v1", + DefaultDescription: "GCP Builder for all runtimes", + }, + { + Vendor: "Heroku", + Image: "heroku/buildpacks:20", + DefaultDescription: "heroku-20 base image with buildpacks for Ruby, Java, Node.js, Python, Golang, & PHP", + }, + { + Vendor: "Paketo Buildpacks", + Image: "paketobuildpacks/builder:base", + DefaultDescription: "Small base image with buildpacks for Java, Node.js, Golang, & .NET Core", + }, + { + Vendor: "Paketo Buildpacks", + Image: "paketobuildpacks/builder:full", + DefaultDescription: "Larger base image with buildpacks for Java, Node.js, Golang, .NET Core, & PHP", + }, + { + Vendor: "Paketo Buildpacks", + Image: "paketobuildpacks/builder:tiny", + DefaultDescription: "Tiny base image (bionic build image, distroless run image) with buildpacks for Golang", + }, +} diff --git a/vendor/github.com/buildpacks/pack/internal/builder/version.go b/vendor/github.com/buildpacks/pack/internal/builder/version.go new file mode 100644 index 0000000000..f926419bed --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/builder/version.go @@ -0,0 +1,47 @@ +package builder + +import ( + "github.com/Masterminds/semver" + "github.com/pkg/errors" +) + +// Version is an extension to semver.Version to make it marshalable. +type Version struct { + semver.Version +} + +// VersionMustParse parses a string into a Version +func VersionMustParse(v string) *Version { + return &Version{Version: *semver.MustParse(v)} +} + +// String returns the string value of the Version +func (v *Version) String() string { + return v.Version.String() +} + +// Equal compares two Versions +func (v *Version) Equal(other *Version) bool { + if other != nil { + return v.Version.Equal(&other.Version) + } + + return false +} + +// MarshalText makes Version satisfy the encoding.TextMarshaler interface. +func (v *Version) MarshalText() ([]byte, error) { + return []byte(v.Version.Original()), nil +} + +// UnmarshalText makes Version satisfy the encoding.TextUnmarshaler interface. +func (v *Version) UnmarshalText(text []byte) error { + s := string(text) + w, err := semver.NewVersion(s) + if err != nil { + return errors.Wrapf(err, "invalid semantic version %s", s) + } + + v.Version = *w + return nil +} diff --git a/vendor/github.com/buildpacks/pack/internal/cache/consts.go b/vendor/github.com/buildpacks/pack/internal/cache/consts.go new file mode 100644 index 0000000000..80ae0ef1ce --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/cache/consts.go @@ -0,0 +1,8 @@ +package cache + +const ( + Image Type = iota + Volume +) + +type Type int diff --git a/vendor/github.com/buildpacks/pack/internal/cache/image_cache.go b/vendor/github.com/buildpacks/pack/internal/cache/image_cache.go new file mode 100644 index 0000000000..158c00a027 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/cache/image_cache.go @@ -0,0 +1,39 @@ +package cache + +import ( + "context" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" + "github.com/google/go-containerregistry/pkg/name" +) + +type ImageCache struct { + docker client.CommonAPIClient + image string +} + +func NewImageCache(imageRef name.Reference, dockerClient client.CommonAPIClient) *ImageCache { + return &ImageCache{ + image: imageRef.Name(), + docker: dockerClient, + } +} + +func (c *ImageCache) Name() string { + return c.image +} + +func (c *ImageCache) Clear(ctx context.Context) error { + _, err := c.docker.ImageRemove(ctx, c.Name(), types.ImageRemoveOptions{ + Force: true, + }) + if err != nil && !client.IsErrNotFound(err) { + return err + } + return nil +} + +func (c *ImageCache) Type() Type { + return Image +} diff --git a/vendor/github.com/buildpacks/pack/internal/cache/volume_cache.go b/vendor/github.com/buildpacks/pack/internal/cache/volume_cache.go new file mode 100644 index 0000000000..fa0124e50a --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/cache/volume_cache.go @@ -0,0 +1,52 @@ +package cache + +import ( + "context" + "crypto/sha256" + "fmt" + "strings" + + "github.com/docker/docker/client" + "github.com/google/go-containerregistry/pkg/name" + + "github.com/buildpacks/pack/internal/paths" +) + +type VolumeCache struct { + docker client.CommonAPIClient + volume string +} + +func NewVolumeCache(imageRef name.Reference, suffix string, dockerClient client.CommonAPIClient) *VolumeCache { + sum := sha256.Sum256([]byte(imageRef.Name())) + + vol := paths.FilterReservedNames(fmt.Sprintf("%s-%x", sanitizedRef(imageRef), sum[:6])) + return &VolumeCache{ + volume: fmt.Sprintf("pack-cache-%s.%s", vol, suffix), + docker: dockerClient, + } +} + +func (c *VolumeCache) Name() string { + return c.volume +} + +func (c *VolumeCache) Clear(ctx context.Context) error { + err := c.docker.VolumeRemove(ctx, c.Name(), true) + if err != nil && !client.IsErrNotFound(err) { + return err + } + return nil +} + +func (c *VolumeCache) Type() Type { + return Volume +} + +// note image names and volume names are validated using the same restrictions: +// see https://github.com/moby/moby/blob/f266f13965d5bfb1825afa181fe6c32f3a597fa3/daemon/names/names.go#L5 +func sanitizedRef(ref name.Reference) string { + result := strings.TrimPrefix(ref.Context().String(), ref.Context().RegistryStr()+"/") + result = strings.ReplaceAll(result, "/", "_") + return fmt.Sprintf("%s_%s", result, ref.Identifier()) +} diff --git a/vendor/github.com/buildpacks/pack/internal/config/config.go b/vendor/github.com/buildpacks/pack/internal/config/config.go new file mode 100644 index 0000000000..5af064f5f9 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/config/config.go @@ -0,0 +1,131 @@ +package config + +import ( + "os" + "path/filepath" + + "github.com/BurntSushi/toml" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/style" +) + +type Config struct { + // Deprecated: Use DefaultRegistryName instead. See https://github.com/buildpacks/pack/issues/747. + DefaultRegistry string `toml:"default-registry-url,omitempty"` + DefaultRegistryName string `toml:"default-registry,omitempty"` + DefaultBuilder string `toml:"default-builder-image,omitempty"` + PullPolicy string `toml:"pull-policy,omitempty"` + Experimental bool `toml:"experimental,omitempty"` + RunImages []RunImage `toml:"run-images"` + TrustedBuilders []TrustedBuilder `toml:"trusted-builders,omitempty"` + Registries []Registry `toml:"registries,omitempty"` + LifecycleImage string `toml:"lifecycle-image,omitempty"` + RegistryMirrors map[string]string `toml:"registry-mirrors,omitempty"` +} + +type Registry struct { + Name string `toml:"name"` + Type string `toml:"type"` + URL string `toml:"url"` +} + +type RunImage struct { + Image string `toml:"image"` + Mirrors []string `toml:"mirrors"` +} + +type TrustedBuilder struct { + Name string `toml:"name"` +} + +const OfficialRegistryName = "official" + +func DefaultRegistry() Registry { + return Registry{ + OfficialRegistryName, + "github", + "https://github.com/buildpacks/registry-index", + } +} + +func DefaultConfigPath() (string, error) { + home, err := PackHome() + if err != nil { + return "", errors.Wrap(err, "getting pack home") + } + return filepath.Join(home, "config.toml"), nil +} + +func PackHome() (string, error) { + packHome := os.Getenv("PACK_HOME") + if packHome == "" { + home, err := os.UserHomeDir() + if err != nil { + return "", errors.Wrap(err, "getting user home") + } + packHome = filepath.Join(home, ".pack") + } + return packHome, nil +} + +func Read(path string) (Config, error) { + cfg := Config{} + _, err := toml.DecodeFile(path, &cfg) + if err != nil && !os.IsNotExist(err) { + return Config{}, errors.Wrapf(err, "failed to read config file at path %s", path) + } + + return cfg, nil +} + +func Write(cfg Config, path string) error { + if err := MkdirAll(filepath.Dir(path)); err != nil { + return err + } + w, err := os.Create(path) + if err != nil { + return err + } + defer w.Close() + + return toml.NewEncoder(w).Encode(cfg) +} + +func MkdirAll(path string) error { + return os.MkdirAll(path, 0750) +} + +func SetRunImageMirrors(cfg Config, image string, mirrors []string) Config { + for i := range cfg.RunImages { + if cfg.RunImages[i].Image == image { + cfg.RunImages[i].Mirrors = mirrors + return cfg + } + } + cfg.RunImages = append(cfg.RunImages, RunImage{Image: image, Mirrors: mirrors}) + return cfg +} + +func GetRegistries(cfg Config) []Registry { + return append(cfg.Registries, DefaultRegistry()) +} + +func GetRegistry(cfg Config, registryName string) (Registry, error) { + if registryName == "" && cfg.DefaultRegistryName != "" { + registryName = cfg.DefaultRegistryName + } + if registryName == "" && cfg.DefaultRegistryName == "" { + registryName = OfficialRegistryName + } + if registryName != "" { + for _, registry := range GetRegistries(cfg) { + if registry.Name == registryName { + return registry, nil + } + } + } + return Registry{}, errors.Errorf("registry %s is not defined in your config file", style.Symbol(registryName)) +} + +const DefaultLifecycleImageRepo = "buildpacksio/lifecycle" diff --git a/vendor/github.com/buildpacks/pack/internal/config/config_helpers.go b/vendor/github.com/buildpacks/pack/internal/config/config_helpers.go new file mode 100644 index 0000000000..9ed17a3a78 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/config/config_helpers.go @@ -0,0 +1,36 @@ +package config + +import ( + "fmt" + "strings" + + "github.com/BurntSushi/toml" + + "github.com/buildpacks/pack/internal/style" +) + +func FormatUndecodedKeys(undecodedKeys []toml.Key) string { + unusedKeys := map[string]interface{}{} + for _, key := range undecodedKeys { + keyName := key.String() + + parent := strings.Split(keyName, ".")[0] + + if _, ok := unusedKeys[parent]; !ok { + unusedKeys[keyName] = nil + } + } + + var errorKeys []string + for errorKey := range unusedKeys { + errorKeys = append(errorKeys, style.Symbol(errorKey)) + } + + pluralizedElement := "element" + if len(errorKeys) > 1 { + pluralizedElement += "s" + } + elements := strings.Join(errorKeys, ", ") + + return fmt.Sprintf("unknown configuration %s %s", pluralizedElement, elements) +} diff --git a/vendor/github.com/buildpacks/pack/internal/container/run.go b/vendor/github.com/buildpacks/pack/internal/container/run.go new file mode 100644 index 0000000000..e39f8fbb23 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/container/run.go @@ -0,0 +1,67 @@ +package container + +import ( + "context" + "fmt" + "io" + + "github.com/docker/docker/api/types" + dcontainer "github.com/docker/docker/api/types/container" + "github.com/docker/docker/client" + "github.com/docker/docker/pkg/stdcopy" + "github.com/pkg/errors" +) + +type Handler func(bodyChan <-chan dcontainer.ContainerWaitOKBody, errChan <-chan error, reader io.Reader) error + +func RunWithHandler(ctx context.Context, docker client.CommonAPIClient, ctrID string, handler Handler) error { + bodyChan, errChan := docker.ContainerWait(ctx, ctrID, dcontainer.WaitConditionNextExit) + + resp, err := docker.ContainerAttach(ctx, ctrID, types.ContainerAttachOptions{ + Stream: true, + Stdout: true, + Stderr: true, + }) + if err != nil { + return err + } + defer resp.Close() + + if err := docker.ContainerStart(ctx, ctrID, types.ContainerStartOptions{}); err != nil { + return errors.Wrap(err, "container start") + } + + return handler(bodyChan, errChan, resp.Reader) +} + +func DefaultHandler(out, errOut io.Writer) Handler { + return func(bodyChan <-chan dcontainer.ContainerWaitOKBody, errChan <-chan error, reader io.Reader) error { + copyErr := make(chan error) + go func() { + _, err := stdcopy.StdCopy(out, errOut, reader) + defer optionallyCloseWriter(out) + defer optionallyCloseWriter(errOut) + + copyErr <- err + }() + + select { + case body := <-bodyChan: + if body.StatusCode != 0 { + return fmt.Errorf("failed with status code: %d", body.StatusCode) + } + case err := <-errChan: + return err + } + + return <-copyErr + } +} + +func optionallyCloseWriter(writer io.Writer) error { + if closer, ok := writer.(io.Closer); ok { + return closer.Close() + } + + return nil +} diff --git a/vendor/github.com/buildpacks/pack/internal/layer/layer.go b/vendor/github.com/buildpacks/pack/internal/layer/layer.go new file mode 100644 index 0000000000..2a66559cf9 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/layer/layer.go @@ -0,0 +1,11 @@ +package layer + +import ( + "github.com/buildpacks/pack/pkg/archive" +) + +func CreateSingleFileTar(tarFile, path, txt string, twf archive.TarWriterFactory) error { + tarBuilder := archive.TarBuilder{} + tarBuilder.AddFile(path, 0644, archive.NormalizedDateTime, []byte(txt)) + return tarBuilder.WriteToPath(tarFile, twf) +} diff --git a/vendor/github.com/buildpacks/pack/internal/layer/writer_factory.go b/vendor/github.com/buildpacks/pack/internal/layer/writer_factory.go new file mode 100644 index 0000000000..24f9502f82 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/layer/writer_factory.go @@ -0,0 +1,32 @@ +package layer + +import ( + "archive/tar" + "fmt" + "io" + + ilayer "github.com/buildpacks/imgutil/layer" + + "github.com/buildpacks/pack/pkg/archive" +) + +type WriterFactory struct { + os string +} + +func NewWriterFactory(imageOS string) (*WriterFactory, error) { + if imageOS != "linux" && imageOS != "windows" { + return nil, fmt.Errorf("provided image OS '%s' must be either 'linux' or 'windows'", imageOS) + } + + return &WriterFactory{os: imageOS}, nil +} + +func (f *WriterFactory) NewWriter(fileWriter io.Writer) archive.TarWriter { + if f.os == "windows" { + return ilayer.NewWindowsWriter(fileWriter) + } + + // Linux images use tar.Writer + return tar.NewWriter(fileWriter) +} diff --git a/vendor/github.com/buildpacks/pack/internal/name/name.go b/vendor/github.com/buildpacks/pack/internal/name/name.go new file mode 100644 index 0000000000..c840da5fe6 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/name/name.go @@ -0,0 +1,49 @@ +package name + +import ( + "fmt" + + gname "github.com/google/go-containerregistry/pkg/name" + + "github.com/buildpacks/pack/internal/style" +) + +type Logger interface { + Infof(fmt string, v ...interface{}) +} + +func TranslateRegistry(name string, registryMirrors map[string]string, logger Logger) (string, error) { + if registryMirrors == nil { + return name, nil + } + + srcRef, err := gname.ParseReference(name, gname.WeakValidation) + if err != nil { + return "", err + } + + srcContext := srcRef.Context() + registryMirror, ok := getMirror(srcContext, registryMirrors) + if !ok { + return name, nil + } + + refName := fmt.Sprintf("%s/%s:%s", registryMirror, srcContext.RepositoryStr(), srcRef.Identifier()) + _, err = gname.ParseReference(refName, gname.WeakValidation) + if err != nil { + return "", err + } + + logger.Infof("Using mirror %s for %s", style.Symbol(refName), name) + return refName, nil +} + +func getMirror(repo gname.Repository, registryMirrors map[string]string) (string, bool) { + mirror, ok := registryMirrors["*"] + if ok { + return mirror, ok + } + + mirror, ok = registryMirrors[repo.RegistryStr()] + return mirror, ok +} diff --git a/vendor/github.com/buildpacks/pack/internal/paths/paths.go b/vendor/github.com/buildpacks/pack/internal/paths/paths.go new file mode 100644 index 0000000000..f78695622a --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/paths/paths.go @@ -0,0 +1,135 @@ +package paths + +import ( + "net/url" + "os" + "path/filepath" + "regexp" + "runtime" + "strings" +) + +var schemeRegexp = regexp.MustCompile(`^.+:/.*`) + +func IsURI(ref string) bool { + return schemeRegexp.MatchString(ref) +} + +func IsDir(p string) (bool, error) { + fileInfo, err := os.Stat(p) + if err != nil { + return false, err + } + + return fileInfo.IsDir(), nil +} + +// FilePathToURI converts a filepath to URI. If relativeTo is provided not empty and path is +// a relative path it will be made absolute based on the provided value. Otherwise, the +// current working directory is used. +func FilePathToURI(path, relativeTo string) (string, error) { + if IsURI(path) { + return path, nil + } + + if !filepath.IsAbs(path) { + var err error + path, err = filepath.Abs(filepath.Join(relativeTo, path)) + if err != nil { + return "", err + } + } + + if runtime.GOOS == "windows" { + if strings.HasPrefix(path, `\\`) { + return "file://" + filepath.ToSlash(strings.TrimPrefix(path, `\\`)), nil + } + return "file:///" + filepath.ToSlash(path), nil + } + return "file://" + path, nil +} + +// examples: +// +// - unix file: file://laptop/some%20dir/file.tgz +// +// - windows drive: file:///C:/Documents%20and%20Settings/file.tgz +// +// - windows share: file://laptop/My%20Documents/file.tgz +// +func URIToFilePath(uri string) (string, error) { + var ( + osPath string + err error + ) + + osPath = filepath.FromSlash(strings.TrimPrefix(uri, "file://")) + + if osPath, err = url.PathUnescape(osPath); err != nil { + return "", nil + } + + if runtime.GOOS == "windows" { + if strings.HasPrefix(osPath, `\`) { + return strings.TrimPrefix(osPath, `\`), nil + } + return `\\` + osPath, nil + } + return osPath, nil +} + +func FilterReservedNames(p string) string { + // The following keys are reserved on Windows + // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN#win32-file-namespaces + reservedNameConversions := map[string]string{ + "aux": "a_u_x", + "com": "c_o_m", + "con": "c_o_n", + "lpt": "l_p_t", + "nul": "n_u_l", + "prn": "p_r_n", + } + for k, v := range reservedNameConversions { + p = strings.ReplaceAll(p, k, v) + } + + return p +} + +// WindowsDir is equivalent to path.Dir or filepath.Dir but always for Windows paths +// reproduced because Windows implementation is not exported +func WindowsDir(p string) string { + pathElements := strings.Split(p, `\`) + + dirName := strings.Join(pathElements[:len(pathElements)-1], `\`) + + return dirName +} + +// WindowsBasename is equivalent to path.Basename or filepath.Basename but always for Windows paths +// reproduced because Windows implementation is not exported +func WindowsBasename(p string) string { + pathElements := strings.Split(p, `\`) + + return pathElements[len(pathElements)-1] +} + +// WindowsToSlash is equivalent to path.ToSlash or filepath.ToSlash but always for Windows paths +// reproduced because Windows implementation is not exported +func WindowsToSlash(p string) string { + slashPath := strings.ReplaceAll(p, `\`, "/") // convert slashes + if len(slashPath) < 2 { + return "" + } + + return slashPath[2:] // strip volume +} + +// WindowsPathSID returns the appropriate SID for a given UID and GID +// This the basic logic for path permissions in Pack and Lifecycle +func WindowsPathSID(uid, gid int) string { + if uid == 0 && gid == 0 { + return "S-1-5-32-544" // BUILTIN\Administrators + } + return "S-1-5-32-545" // BUILTIN\Users +} diff --git a/vendor/github.com/buildpacks/pack/internal/registry/buildpack.go b/vendor/github.com/buildpacks/pack/internal/registry/buildpack.go new file mode 100644 index 0000000000..e5d75431a9 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/registry/buildpack.go @@ -0,0 +1,45 @@ +package registry + +import ( + "fmt" + "strings" + + ggcrname "github.com/google/go-containerregistry/pkg/name" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/style" +) + +// Buildpack contains information about a buildpack stored in a Registry +type Buildpack struct { + Namespace string `json:"ns"` + Name string `json:"name"` + Version string `json:"version"` + Yanked bool `json:"yanked"` + Address string `json:"addr,omitempty"` +} + +// Validate that a buildpack reference contains required information +func Validate(b Buildpack) error { + if b.Address == "" { + return errors.New("invalid entry: address is a required field") + } + _, err := ggcrname.NewDigest(b.Address) + if err != nil { + return fmt.Errorf("invalid entry: '%s' is not a digest reference", b.Address) + } + + return nil +} + +// ParseNamespaceName parses a buildpack ID into Namespace and Name +func ParseNamespaceName(id string) (ns string, name string, err error) { + parts := strings.Split(id, "/") + if len(parts) < 2 { + return "", "", fmt.Errorf("invalid id %s does not contain a namespace", style.Symbol(id)) + } else if len(parts) > 2 { + return "", "", fmt.Errorf("invalid id %s contains unexpected characters", style.Symbol(id)) + } + + return parts[0], parts[1], nil +} diff --git a/vendor/github.com/buildpacks/pack/internal/registry/git.go b/vendor/github.com/buildpacks/pack/internal/registry/git.go new file mode 100644 index 0000000000..9a90b56377 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/registry/git.go @@ -0,0 +1,31 @@ +package registry + +import ( + "bytes" + "text/template" + + "github.com/pkg/errors" +) + +// GitCommit commits a Buildpack to a registry Cache. +func GitCommit(b Buildpack, username string, registryCache Cache) error { + if err := registryCache.Initialize(); err != nil { + return err + } + + commitTemplate, err := template.New("buildpack").Parse(GitCommitTemplate) + if err != nil { + return err + } + + var commit bytes.Buffer + if err := commitTemplate.Execute(&commit, b); err != nil { + return errors.Wrap(err, "creating template") + } + + if err := registryCache.Commit(b, username, commit.String()); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/buildpacks/pack/internal/registry/github.go b/vendor/github.com/buildpacks/pack/internal/registry/github.go new file mode 100644 index 0000000000..329de29d52 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/registry/github.go @@ -0,0 +1,73 @@ +package registry + +import ( + "bytes" + "fmt" + "net/url" + "os/exec" + "strings" + "text/template" + + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/style" +) + +type GithubIssue struct { + Title string + Body string +} + +func CreateGithubIssue(b Buildpack) (GithubIssue, error) { + titleTemplate, err := template.New("buildpack").Parse(GithubIssueTitleTemplate) + if err != nil { + return GithubIssue{}, err + } + + bodyTemplate, err := template.New("buildpack").Parse(GithubIssueBodyTemplate) + if err != nil { + return GithubIssue{}, err + } + + var title bytes.Buffer + err = titleTemplate.Execute(&title, b) + if err != nil { + return GithubIssue{}, err + } + + var body bytes.Buffer + err = bodyTemplate.Execute(&body, b) + if err != nil { + return GithubIssue{}, err + } + + return GithubIssue{ + title.String(), + body.String(), + }, nil +} + +func CreateBrowserCmd(browserURL, os string) (*exec.Cmd, error) { + _, err := url.ParseRequestURI(browserURL) + if err != nil { + return nil, errors.Wrapf(err, "invalid URL %s", style.Symbol(browserURL)) + } + + switch os { + case "linux": + return exec.Command("xdg-open", browserURL), nil + case "windows": + return exec.Command("rundll32", "url.dll,FileProtocolHandler", browserURL), nil + case "darwin": + return exec.Command("open", browserURL), nil + default: + return nil, fmt.Errorf("unsupported platform %s", style.Symbol(os)) + } +} + +func GetIssueURL(githubURL string) (*url.URL, error) { + if githubURL == "" { + return nil, errors.New("missing github URL") + } + return url.Parse(fmt.Sprintf("%s/issues/new", strings.TrimSuffix(githubURL, "/"))) +} diff --git a/vendor/github.com/buildpacks/pack/internal/registry/index.go b/vendor/github.com/buildpacks/pack/internal/registry/index.go new file mode 100644 index 0000000000..928c3b07f8 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/registry/index.go @@ -0,0 +1,57 @@ +package registry + +import ( + "fmt" + "path/filepath" + "regexp" + + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/style" +) + +var ( + validCharsPattern = "[a-z0-9\\-.]+" + validCharsRegexp = regexp.MustCompile(fmt.Sprintf("^%s$", validCharsPattern)) +) + +// IndexPath resolves the path for a specific namespace and name of buildpack +func IndexPath(rootDir, ns, name string) (string, error) { + if err := validateField("namespace", ns); err != nil { + return "", err + } + + if err := validateField("name", name); err != nil { + return "", err + } + + var indexDir string + switch { + case len(name) == 1: + indexDir = "1" + case len(name) == 2: + indexDir = "2" + case len(name) == 3: + indexDir = filepath.Join("3", name[:2]) + default: + indexDir = filepath.Join(name[:2], name[2:4]) + } + + return filepath.Join(rootDir, indexDir, fmt.Sprintf("%s_%s", ns, name)), nil +} + +func validateField(field, value string) error { + length := len(value) + switch { + case length == 0: + return errors.Errorf("%s cannot be empty", style.Symbol(field)) + case length > 253: + return errors.Errorf("%s too long (max 253 chars)", style.Symbol(field)) + } + + if !validCharsRegexp.MatchString(value) { + return errors.Errorf("%s contains illegal characters (must match %s)", style.Symbol(field), style.Symbol(validCharsPattern)) + } + + return nil +} diff --git a/vendor/github.com/buildpacks/pack/internal/registry/registry_cache.go b/vendor/github.com/buildpacks/pack/internal/registry/registry_cache.go new file mode 100644 index 0000000000..95c9862c61 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/registry/registry_cache.go @@ -0,0 +1,357 @@ +package registry + +import ( + "bufio" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "io/ioutil" + "net/url" + "os" + "path/filepath" + "runtime" + "time" + + "github.com/pkg/errors" + "golang.org/x/mod/semver" + "gopkg.in/src-d/go-git.v4" + "gopkg.in/src-d/go-git.v4/plumbing/object" + + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/buildpack" + "github.com/buildpacks/pack/pkg/logging" +) + +const DefaultRegistryURL = "https://github.com/buildpacks/registry-index" +const DefaultRegistryName = "official" +const defaultRegistryDir = "registry" + +// Cache is a RegistryCache +type Cache struct { + logger logging.Logger + url *url.URL + Root string + RegistryDir string +} + +const GithubIssueTitleTemplate = "{{ if .Yanked }}YANK{{ else }}ADD{{ end }} {{.Namespace}}/{{.Name}}@{{.Version}}" +const GithubIssueBodyTemplate = ` +id = "{{.Namespace}}/{{.Name}}" +version = "{{.Version}}" +{{ if .Yanked }}{{ else if .Address }}addr = "{{.Address}}"{{ end }} +` +const GitCommitTemplate = `{{ if .Yanked }}YANK{{else}}ADD{{end}} {{.Namespace}}/{{.Name}}@{{.Version}}` + +// Entry is a list of buildpacks stored in a registry +type Entry struct { + Buildpacks []Buildpack `json:"buildpacks"` +} + +// NewDefaultRegistryCache creates a new registry cache with default options +func NewDefaultRegistryCache(logger logging.Logger, home string) (Cache, error) { + return NewRegistryCache(logger, home, DefaultRegistryURL) +} + +// NewRegistryCache creates a new registry cache +func NewRegistryCache(logger logging.Logger, home, registryURL string) (Cache, error) { + if _, err := os.Stat(home); err != nil { + return Cache{}, errors.Wrapf(err, "finding home %s", home) + } + + normalizedURL, err := url.Parse(registryURL) + if err != nil { + return Cache{}, errors.Wrapf(err, "parsing registry url %s", registryURL) + } + + key := sha256.New() + key.Write([]byte(normalizedURL.String())) + cacheDir := fmt.Sprintf("%s-%s", defaultRegistryDir, hex.EncodeToString(key.Sum(nil))) + + return Cache{ + url: normalizedURL, + logger: logger, + Root: filepath.Join(home, cacheDir), + }, nil +} + +// LocateBuildpack stored in registry +func (r *Cache) LocateBuildpack(bp string) (Buildpack, error) { + err := r.Refresh() + if err != nil { + return Buildpack{}, errors.Wrap(err, "refreshing cache") + } + + ns, name, version, err := buildpack.ParseRegistryID(bp) + if err != nil { + return Buildpack{}, errors.Wrap(err, "parsing buildpacks registry id") + } + + entry, err := r.readEntry(ns, name) + if err != nil { + return Buildpack{}, errors.Wrap(err, "reading entry") + } + + if len(entry.Buildpacks) > 0 { + if version == "" { + highestVersion := entry.Buildpacks[0] + if len(entry.Buildpacks) > 1 { + for _, bp := range entry.Buildpacks[1:] { + if semver.Compare(fmt.Sprintf("v%s", bp.Version), fmt.Sprintf("v%s", highestVersion.Version)) > 0 { + highestVersion = bp + } + } + } + return highestVersion, Validate(highestVersion) + } + + for _, bpIndex := range entry.Buildpacks { + if bpIndex.Version == version { + return bpIndex, Validate(bpIndex) + } + } + return Buildpack{}, fmt.Errorf("could not find version for buildpack: %s", bp) + } + + return Buildpack{}, fmt.Errorf("no entries for buildpack: %s", bp) +} + +// Refresh local Registry Cache +func (r *Cache) Refresh() error { + r.logger.Debugf("Refreshing registry cache for %s/%s", r.url.Host, r.url.Path) + + if err := r.Initialize(); err != nil { + return errors.Wrapf(err, "initializing (%s)", r.Root) + } + + repository, err := git.PlainOpen(r.Root) + if err != nil { + return errors.Wrapf(err, "opening (%s)", r.Root) + } + + w, err := repository.Worktree() + if err != nil { + return errors.Wrapf(err, "reading (%s)", r.Root) + } + + err = w.Pull(&git.PullOptions{RemoteName: "origin"}) + if err == git.NoErrAlreadyUpToDate { + return nil + } + return err +} + +// Initialize a local Registry Cache +func (r *Cache) Initialize() error { + _, err := os.Stat(r.Root) + if err != nil { + if os.IsNotExist(err) { + err = r.CreateCache() + if err != nil { + return errors.Wrap(err, "creating registry cache") + } + } + } + + if err := r.validateCache(); err != nil { + err = os.RemoveAll(r.Root) + if err != nil { + return errors.Wrap(err, "resetting registry cache") + } + err = r.CreateCache() + if err != nil { + return errors.Wrap(err, "rebuilding registry cache") + } + } + + return nil +} + +// CreateCache creates the cache on the filesystem +func (r *Cache) CreateCache() error { + r.logger.Debugf("Creating registry cache for %s/%s", r.url.Host, r.url.Path) + + registryDir, err := ioutil.TempDir(filepath.Dir(r.Root), "registry") + if err != nil { + return err + } + + r.RegistryDir = registryDir + + repository, err := git.PlainClone(r.RegistryDir, false, &git.CloneOptions{ + URL: r.url.String(), + }) + if err != nil { + return errors.Wrap(err, "cloning remote registry") + } + + w, err := repository.Worktree() + if err != nil { + return err + } + + err = os.Rename(w.Filesystem.Root(), r.Root) + if err != nil { + if err == os.ErrExist { + // If pack is run concurrently, this action might have already occurred + return nil + } + return err + } + return nil +} + +func (r *Cache) validateCache() error { + r.logger.Debugf("Validating registry cache for %s/%s", r.url.Host, r.url.Path) + + repository, err := git.PlainOpen(r.Root) + if err != nil { + return errors.Wrap(err, "opening registry cache") + } + + remotes, err := repository.Remotes() + if err != nil { + return errors.Wrap(err, "accessing registry cache") + } + + for _, remote := range remotes { + if remote.Config().Name == "origin" && remotes[0].Config().URLs[0] != r.url.String() { + return nil + } + } + return errors.New("invalid registry cache remote") +} + +// Commit a Buildpack change +func (r *Cache) Commit(b Buildpack, username, msg string) error { + r.logger.Debugf("Creating commit in registry cache") + + if msg == "" { + return errors.New("invalid commit message") + } + + repository, err := git.PlainOpen(r.Root) + if err != nil { + return errors.Wrap(err, "opening registry cache") + } + + w, err := repository.Worktree() + if err != nil { + return errors.Wrapf(err, "reading %s", style.Symbol(r.Root)) + } + + index, err := r.writeEntry(b) + if err != nil { + return errors.Wrapf(err, "writing %s", style.Symbol(index)) + } + + relativeIndexFile, err := filepath.Rel(r.Root, index) + if err != nil { + return errors.Wrap(err, "resolving relative path") + } + + if _, err := w.Add(relativeIndexFile); err != nil { + return errors.Wrapf(err, "adding %s", style.Symbol(index)) + } + + if _, err := w.Commit(msg, &git.CommitOptions{ + Author: &object.Signature{ + Name: username, + Email: "", + When: time.Now(), + }, + }); err != nil { + return errors.Wrapf(err, "committing") + } + + return nil +} + +func (r *Cache) writeEntry(b Buildpack) (string, error) { + var ns = b.Namespace + var name = b.Name + + index, err := IndexPath(r.Root, ns, name) + if err != nil { + return "", err + } + + if _, err := os.Stat(index); os.IsNotExist(err) { + if err := os.MkdirAll(filepath.Dir(index), 0750); err != nil { + return "", errors.Wrapf(err, "creating directory structure for: %s/%s", ns, name) + } + } else { + if _, err := os.Stat(index); err == nil { + entry, err := r.readEntry(ns, name) + if err != nil { + return "", errors.Wrapf(err, "reading existing buildpack entries") + } + + availableBuildpacks := entry.Buildpacks + + if len(availableBuildpacks) != 0 { + if availableBuildpacks[len(availableBuildpacks)-1].Version == b.Version { + return "", errors.Wrapf(err, "same version exists, upgrade the version to add") + } + } + } + } + + f, err := os.OpenFile(filepath.Clean(index), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644) + if err != nil { + return "", errors.Wrapf(err, "creating buildpack file: %s/%s", ns, name) + } + defer f.Close() + + newline := "\n" + if runtime.GOOS == "windows" { + newline = "\r\n" + } + + fileContents, err := json.Marshal(b) + if err != nil { + return "", errors.Wrapf(err, "converting buildpack file to json: %s/%s", ns, name) + } + + fileContentsFormatted := string(fileContents) + newline + if _, err := f.WriteString(fileContentsFormatted); err != nil { + return "", errors.Wrapf(err, "writing buildpack to file: %s/%s", ns, name) + } + + return index, nil +} + +func (r *Cache) readEntry(ns, name string) (Entry, error) { + index, err := IndexPath(r.Root, ns, name) + if err != nil { + return Entry{}, err + } + + if _, err := os.Stat(index); err != nil { + return Entry{}, errors.Wrapf(err, "finding buildpack: %s/%s", ns, name) + } + + file, err := os.Open(filepath.Clean(index)) + if err != nil { + return Entry{}, errors.Wrapf(err, "opening index for buildpack: %s/%s", ns, name) + } + defer file.Close() + + entry := Entry{} + scanner := bufio.NewScanner(file) + for scanner.Scan() { + var bp Buildpack + err = json.Unmarshal([]byte(scanner.Text()), &bp) + if err != nil { + return Entry{}, errors.Wrapf(err, "parsing index for buildpack: %s/%s", ns, name) + } + + entry.Buildpacks = append(entry.Buildpacks, bp) + } + + if err := scanner.Err(); err != nil { + return entry, errors.Wrapf(err, "reading index for buildpack: %s/%s", ns, name) + } + + return entry, nil +} diff --git a/vendor/github.com/buildpacks/pack/internal/stack/merge.go b/vendor/github.com/buildpacks/pack/internal/stack/merge.go new file mode 100644 index 0000000000..854e96362f --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/stack/merge.go @@ -0,0 +1,62 @@ +package stack + +import ( + "sort" + + "github.com/buildpacks/pack/internal/stringset" + "github.com/buildpacks/pack/pkg/dist" +) + +// MergeCompatible determines the allowable set of stacks that a combination of buildpacks may run on, given each +// buildpack's set of stacks. Compatibility between the two sets of buildpack stacks is defined by the following rules: +// +// 1. The stack must be supported by both buildpacks. That is, any resulting stack ID must appear in both input sets. +// 2. For each supported stack ID, all required mixins for all buildpacks must be provided by the result. That is, +// mixins for the stack ID in both input sets are unioned. +// +// --- +// +// Examples: +// +// stacksA = [{ID: "stack1", mixins: ["build:mixinA", "mixinB", "run:mixinC"]}}] +// stacksB = [{ID: "stack1", mixins: ["build:mixinA", "run:mixinC"]}}] +// result = [{ID: "stack1", mixins: ["build:mixinA", "mixinB", "run:mixinC"]}}] +// +// stacksA = [{ID: "stack1", mixins: ["build:mixinA"]}}, {ID: "stack2", mixins: ["mixinA"]}}] +// stacksB = [{ID: "stack1", mixins: ["run:mixinC"]}}, {ID: "stack2", mixins: ["mixinA"]}}] +// result = [{ID: "stack1", mixins: ["build:mixinA", "run:mixinC"]}}, {ID: "stack2", mixins: ["mixinA"]}}] +// +// stacksA = [{ID: "stack1", mixins: ["build:mixinA"]}}, {ID: "stack2", mixins: ["mixinA"]}}] +// stacksB = [{ID: "stack2", mixins: ["mixinA", "run:mixinB"]}}] +// result = [{ID: "stack2", mixins: ["mixinA", "run:mixinB"]}}] +// +// stacksA = [{ID: "stack1", mixins: ["build:mixinA"]}}] +// stacksB = [{ID: "stack2", mixins: ["mixinA", "run:mixinB"]}}] +// result = [] +// +func MergeCompatible(stacksA []dist.Stack, stacksB []dist.Stack) []dist.Stack { + set := map[string][]string{} + + for _, s := range stacksA { + set[s.ID] = s.Mixins + } + + var results []dist.Stack + for _, s := range stacksB { + if stackMixins, ok := set[s.ID]; ok { + mixinsSet := stringset.FromSlice(append(stackMixins, s.Mixins...)) + var mixins []string + for s := range mixinsSet { + mixins = append(mixins, s) + } + sort.Strings(mixins) + + results = append(results, dist.Stack{ + ID: s.ID, + Mixins: mixins, + }) + } + } + + return results +} diff --git a/vendor/github.com/buildpacks/pack/internal/stack/mixins.go b/vendor/github.com/buildpacks/pack/internal/stack/mixins.go new file mode 100644 index 0000000000..b3e77c2827 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/stack/mixins.go @@ -0,0 +1,55 @@ +package stack + +import ( + "fmt" + "sort" + "strings" + + "github.com/buildpacks/pack/internal/stringset" + "github.com/buildpacks/pack/internal/style" +) + +const MixinsLabel = "io.buildpacks.stack.mixins" + +func ValidateMixins(buildImageName string, buildImageMixins []string, runImageName string, runImageMixins []string) error { + if invalid := FindStageMixins(buildImageMixins, "run"); len(invalid) > 0 { + sort.Strings(invalid) + return fmt.Errorf("%s contains run-only mixin(s): %s", style.Symbol(buildImageName), strings.Join(invalid, ", ")) + } + + if invalid := FindStageMixins(runImageMixins, "build"); len(invalid) > 0 { + sort.Strings(invalid) + return fmt.Errorf("%s contains build-only mixin(s): %s", style.Symbol(runImageName), strings.Join(invalid, ", ")) + } + + buildImageMixins = removeStageMixins(buildImageMixins, "build") + runImageMixins = removeStageMixins(runImageMixins, "run") + + _, missing, _ := stringset.Compare(runImageMixins, buildImageMixins) + + if len(missing) > 0 { + sort.Strings(missing) + return fmt.Errorf("%s missing required mixin(s): %s", style.Symbol(runImageName), strings.Join(missing, ", ")) + } + return nil +} + +func FindStageMixins(mixins []string, stage string) []string { + var found []string + for _, m := range mixins { + if strings.HasPrefix(m, stage+":") { + found = append(found, m) + } + } + return found +} + +func removeStageMixins(mixins []string, stage string) []string { + var filtered []string + for _, m := range mixins { + if !strings.HasPrefix(m, stage+":") { + filtered = append(filtered, m) + } + } + return filtered +} diff --git a/vendor/github.com/buildpacks/pack/internal/stringset/stringset.go b/vendor/github.com/buildpacks/pack/internal/stringset/stringset.go new file mode 100644 index 0000000000..feb7842e7a --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/stringset/stringset.go @@ -0,0 +1,37 @@ +package stringset + +// FromSlice converts the given slice to a set in the form of unique keys in a map. +// The value associated with each key should not be relied upon. A value is present +// in the set if its key is present in the map, regardless of the key's value. +func FromSlice(strings []string) map[string]interface{} { + set := map[string]interface{}{} + for _, s := range strings { + set[s] = nil + } + return set +} + +// Compare performs a set comparison between two slices. `extra` represents elements present in +// `strings1` but not `strings2`. `missing` represents elements present in `strings2` that are +// missing from `strings1`. `common` represents elements present in both slices. Since the input +// slices are treated as sets, duplicates will be removed in any outputs. +func Compare(strings1, strings2 []string) (extra []string, missing []string, common []string) { + set1 := FromSlice(strings1) + set2 := FromSlice(strings2) + + for s := range set1 { + if _, ok := set2[s]; !ok { + extra = append(extra, s) + continue + } + common = append(common, s) + } + + for s := range set2 { + if _, ok := set1[s]; !ok { + missing = append(missing, s) + } + } + + return extra, missing, common +} diff --git a/vendor/github.com/buildpacks/pack/internal/style/style.go b/vendor/github.com/buildpacks/pack/internal/style/style.go new file mode 100644 index 0000000000..0ce59a5bca --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/style/style.go @@ -0,0 +1,62 @@ +package style + +import ( + "fmt" + "sort" + "strings" + + "github.com/heroku/color" +) + +var Symbol = func(value string) string { + if color.Enabled() { + return Key(value) + } + return "'" + value + "'" +} + +var Map = func(value map[string]string, prefix, separator string) string { + result := "" + + var keys []string + + for key := range value { + keys = append(keys, key) + } + + sort.Strings(keys) + + for _, key := range keys { + result += fmt.Sprintf("%s%s=%s%s", prefix, key, value[key], separator) + } + + if color.Enabled() { + return Key(strings.TrimSpace(result)) + } + return "'" + strings.TrimSpace(result) + "'" +} + +var SymbolF = func(format string, a ...interface{}) string { + if color.Enabled() { + return Key(format, a...) + } + return "'" + fmt.Sprintf(format, a...) + "'" +} + +var Key = color.HiBlueString + +var Tip = color.New(color.FgGreen, color.Bold).SprintfFunc() + +var Warn = color.New(color.FgYellow, color.Bold).SprintfFunc() + +var Error = color.New(color.FgRed, color.Bold).SprintfFunc() + +var Step = func(format string, a ...interface{}) string { + return color.CyanString("===> "+format, a...) +} + +var Prefix = color.CyanString +var Waiting = color.HiBlackString +var Working = color.HiBlueString +var Complete = color.GreenString +var ProgressBar = color.HiBlueString diff --git a/vendor/github.com/buildpacks/pack/internal/term/term.go b/vendor/github.com/buildpacks/pack/internal/term/term.go new file mode 100644 index 0000000000..04a246e783 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/term/term.go @@ -0,0 +1,25 @@ +package term + +import ( + "io" + + "golang.org/x/term" +) + +// InvalidFileDescriptor based on https://golang.org/src/os/file_unix.go?s=2183:2210#L57 +const InvalidFileDescriptor = ^(uintptr(0)) + +// IsTerminal returns whether a writer is a terminal +func IsTerminal(w io.Writer) (uintptr, bool) { + if f, ok := w.(hasDescriptor); ok { + termFd := f.Fd() + isTerm := term.IsTerminal(int(termFd)) + return termFd, isTerm + } + + return InvalidFileDescriptor, false +} + +type hasDescriptor interface { + Fd() uintptr +} diff --git a/vendor/github.com/buildpacks/pack/internal/termui/branch.go b/vendor/github.com/buildpacks/pack/internal/termui/branch.go new file mode 100644 index 0000000000..008b900595 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/termui/branch.go @@ -0,0 +1,41 @@ +package termui + +type Symbol int + +func (s Symbol) String() string { + switch s { + case NoBranchSymbol: + return " " + case BranchSymbol: + return "│ " + case MiddleBranchSymbol: + return "├── " + case LastBranchSymbol: + return "└── " + default: + return "" + } +} + +const ( + NoBranchSymbol Symbol = iota + BranchSymbol + MiddleBranchSymbol + LastBranchSymbol +) + +type Branches []Symbol + +func (b Branches) Add(s Symbol) Branches { + bCopy := make(Branches, len(b)) + copy(bCopy, b) + return append(bCopy, s) +} + +func (b Branches) String() string { + var s string + for _, branch := range b[:len(b)-1] { + s += branch.String() + } + return s +} diff --git a/vendor/github.com/buildpacks/pack/internal/termui/dashboard.go b/vendor/github.com/buildpacks/pack/internal/termui/dashboard.go new file mode 100644 index 0000000000..a0e10310fc --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/termui/dashboard.go @@ -0,0 +1,212 @@ +package termui + +import ( + "fmt" + + "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" + + "github.com/buildpacks/pack/pkg/dist" +) + +type Dashboard struct { + app app + buildpackInfo []dist.BuildpackInfo + appTree *tview.TreeView + builderTree *tview.TreeView + planList *tview.List + logsView *tview.TextView + screen *tview.Flex + leftPane *tview.Flex + nodes map[string]*tview.TreeNode + + logs string +} + +func NewDashboard(app app, appName string, bldr buildr, runImageName string, buildpackInfo []dist.BuildpackInfo, logs []string) *Dashboard { + d := &Dashboard{} + + appTree, builderTree := initTrees(appName, bldr, runImageName) + + planList, logsView := d.initDashboard(buildpackInfo) + + imagesView := tview.NewFlex(). + SetDirection(tview.FlexRow). + AddItem(appTree, 0, 1, false). + AddItem(builderTree, 0, 1, true) + + imagesView. + SetBorder(true). + SetTitleAlign(tview.AlignLeft). + SetTitle("| [::b]images[::-] |"). + SetBackgroundColor(backgroundColor) + + leftPane := tview.NewFlex(). + SetDirection(tview.FlexRow). + AddItem(imagesView, 11, 0, false). + AddItem(planList, 0, 1, true) + + screen := tview.NewFlex(). + SetDirection(tview.FlexColumn). + AddItem(leftPane, 0, 1, true). + AddItem(logsView, 0, 1, false) + + d.app = app + d.buildpackInfo = buildpackInfo + d.appTree = appTree + d.builderTree = builderTree + d.planList = planList + d.leftPane = leftPane + d.logsView = logsView + d.screen = screen + + for _, txt := range logs { + d.logs = d.logs + txt + "\n" + } + + d.handleToggle() + d.setScreen() + return d +} + +func (d *Dashboard) Handle(txt string) { + d.app.QueueUpdateDraw(func() { + d.logs = d.logs + txt + "\n" + d.logsView.SetText(tview.TranslateANSI(d.logs)) + }) +} + +func (d *Dashboard) Stop() { + // no-op + // This method is a side effect of the ill-fitting 'page interface' + // Trying to create a cleaner interface between the main termui controller + // and child pages like this one is currently a work-in-progress +} + +func (d *Dashboard) SetNodes(nodes map[string]*tview.TreeNode) { + d.nodes = nodes + + // activate plan list buttons + d.planList.SetMainTextColor(tcell.ColorMediumTurquoise). + SetSelectedTextColor(tcell.ColorMediumTurquoise) + + idx := d.planList.GetCurrentItem() + d.planList.Clear() + for _, buildpackInfo := range d.buildpackInfo { + bp := buildpackInfo + + d.planList.AddItem( + bp.FullName(), + info(bp), + '✔', + func() { + NewDive(d.app, d.buildpackInfo, bp, d.nodes, func() { + d.setScreen() + }) + }, + ) + } + d.planList.SetCurrentItem(idx) + d.app.Draw() +} + +func (d *Dashboard) handleToggle() { + d.planList.SetDoneFunc(func() { + screen := tview.NewFlex(). + SetDirection(tview.FlexColumn). + AddItem(d.leftPane, 0, 1, false). + AddItem(d.logsView, 0, 1, true) + d.app.SetRoot(screen, true) + }) + + d.logsView.SetDoneFunc(func(key tcell.Key) { + screen := tview.NewFlex(). + SetDirection(tview.FlexColumn). + AddItem(d.leftPane, 0, 1, true). + AddItem(d.logsView, 0, 1, false) + d.app.SetRoot(screen, true) + }) +} + +func (d *Dashboard) setScreen() { + d.app.SetRoot(d.screen, true) +} + +func (d *Dashboard) initDashboard(buildpackInfos []dist.BuildpackInfo) (*tview.List, *tview.TextView) { + planList := tview.NewList() + planList.SetMainTextColor(tcell.ColorDarkGrey). + SetSelectedTextColor(tcell.ColorDarkGrey). + SetSelectedBackgroundColor(tcell.ColorDarkSlateGray). + SetSecondaryTextColor(tcell.ColorDimGray). + SetBorder(true). + SetBorderPadding(1, 1, 1, 1). + SetTitle("| [::b]plan[::-] |"). + SetTitleAlign(tview.AlignLeft). + SetBackgroundColor(backgroundColor) + + for _, buildpackInfo := range buildpackInfos { + bp := buildpackInfo + + planList.AddItem( + bp.FullName(), + info(bp), + ' ', + func() {}, + ) + } + + logsView := tview.NewTextView() + logsView.SetDynamicColors(true). + SetTextAlign(tview.AlignLeft). + SetBorderPadding(1, 1, 3, 1). + SetTitleAlign(tview.AlignLeft). + SetBackgroundColor(backgroundColor) + + return planList, logsView +} + +func initTrees(appName string, bldr buildr, runImageName string) (*tview.TreeView, *tview.TreeView) { + var ( + appImage = tview.NewTreeNode(fmt.Sprintf("app: [white::b]%s", appName)).SetColor(tcell.ColorDimGray) + appRunImage = tview.NewTreeNode(fmt.Sprintf(" run: [white::b]%s", runImageName)).SetColor(tcell.ColorDimGray) + builderImage = tview.NewTreeNode(fmt.Sprintf("builder: [white::b]%s", bldr.BaseImageName())).SetColor(tcell.ColorDimGray) + lifecycle = tview.NewTreeNode(fmt.Sprintf(" lifecycle: [white::b]%s", bldr.LifecycleDescriptor().Info.Version.String())).SetColor(tcell.ColorDimGray) + runImage = tview.NewTreeNode(fmt.Sprintf(" run: [white::b]%s", bldr.Stack().RunImage.Image)).SetColor(tcell.ColorDimGray) + buildpacks = tview.NewTreeNode(" [mediumturquoise::b]buildpacks") + ) + + appImage.AddChild(appRunImage) + builderImage.AddChild(lifecycle) + builderImage.AddChild(runImage) + builderImage.AddChild(buildpacks) + + appTree := tview.NewTreeView() + appTree. + SetRoot(appImage). + SetGraphics(true). + SetGraphicsColor(tcell.ColorMediumTurquoise). + SetBorderPadding(1, 0, 4, 0). + SetBackgroundColor(backgroundColor) + + builderTree := tview.NewTreeView() + builderTree. + SetRoot(builderImage). + SetGraphics(true). + SetGraphicsColor(tcell.ColorMediumTurquoise). + SetBorderPadding(0, 0, 4, 0). + SetBackgroundColor(backgroundColor) + + return appTree, builderTree +} + +func info(buildpackInfo dist.BuildpackInfo) string { + if buildpackInfo.Description != "" { + return buildpackInfo.Description + } + + if buildpackInfo.Homepage != "" { + return buildpackInfo.Homepage + } + + return "-" +} diff --git a/vendor/github.com/buildpacks/pack/internal/termui/detect.go b/vendor/github.com/buildpacks/pack/internal/termui/detect.go new file mode 100644 index 0000000000..0b72d3cf9d --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/termui/detect.go @@ -0,0 +1,123 @@ +package termui + +import ( + "regexp" + "time" + + "github.com/rivo/tview" + + "github.com/buildpacks/pack/pkg/dist" +) + +type Detect struct { + app app + bldr buildr + + textView *tview.TextView + buildpackRegex *regexp.Regexp + buildpackChan chan dist.BuildpackInfo + doneChan chan bool +} + +func NewDetect(app app, buildpackChan chan dist.BuildpackInfo, bldr buildr) *Detect { + d := &Detect{ + app: app, + textView: detectStatusTV(), + buildpackRegex: regexp.MustCompile(`^(\S+)\s+([\d\.]+)$`), + buildpackChan: buildpackChan, + doneChan: make(chan bool, 1), + bldr: bldr, + } + + go d.start() + grid := centered(d.textView) + d.app.SetRoot(grid, true) + return d +} + +func (d *Detect) Handle(txt string) { + m := d.buildpackRegex.FindStringSubmatch(txt) + if len(m) == 3 { + d.buildpackChan <- d.find(m[1], m[2]) + } +} + +func (d *Detect) Stop() { + d.doneChan <- true +} + +func (d *Detect) SetNodes(map[string]*tview.TreeNode) { + // no-op + // This method is a side effect of the ill-fitting 'page interface' + // Trying to create a cleaner interface between the main termui controller + // and child pages like this one is currently a work-in-progress +} + +func (d *Detect) start() { + var ( + i = 0 + ticker = time.NewTicker(250 * time.Millisecond) + doneText = "⌛️ Detected!" + texts = []string{ + "⏳️ Detecting", + "⏳️ Detecting.", + "⏳️ Detecting..", + "⏳️ Detecting...", + } + ) + + for { + select { + case <-ticker.C: + d.app.QueueUpdateDraw(func() { + d.textView.SetText(texts[i]) + }) + + i++ + if i == len(texts) { + i = 0 + } + case <-d.doneChan: + ticker.Stop() + + d.app.QueueUpdateDraw(func() { + d.textView.SetText(doneText) + }) + return + } + } +} + +func (d *Detect) find(buildpackID, buildpackVersion string) dist.BuildpackInfo { + for _, buildpack := range d.bldr.Buildpacks() { + if buildpack.ID == buildpackID && buildpack.Version == buildpackVersion { + return buildpack + } + } + + return dist.BuildpackInfo{ + ID: buildpackID, + Version: buildpackVersion, + } +} + +func detectStatusTV() *tview.TextView { + tv := tview.NewTextView() + tv.SetBackgroundColor(backgroundColor) + return tv +} + +func centered(p tview.Primitive) tview.Primitive { + return tview.NewGrid(). + SetColumns(0, 20, 0). + SetRows(0, 1, 0). + AddItem(tview.NewBox().SetBackgroundColor(backgroundColor), 0, 0, 1, 1, 0, 0, true). + AddItem(tview.NewBox().SetBackgroundColor(backgroundColor), 0, 1, 1, 1, 0, 0, true). + AddItem(tview.NewBox().SetBackgroundColor(backgroundColor), 0, 2, 1, 1, 0, 0, true). + AddItem(tview.NewBox().SetBackgroundColor(backgroundColor), 1, 0, 1, 1, 0, 0, true). + AddItem(p, 1, 1, 1, 1, 0, 0, true). + AddItem(tview.NewBox().SetBackgroundColor(backgroundColor), 1, 2, 1, 1, 0, 0, true). + AddItem(tview.NewBox().SetBackgroundColor(backgroundColor), 2, 0, 1, 1, 0, 0, true). + AddItem(tview.NewBox().SetBackgroundColor(backgroundColor), 2, 1, 1, 1, 0, 0, true). + AddItem(tview.NewBox().SetBackgroundColor(backgroundColor), 2, 2, 1, 1, 0, 0, true) +} diff --git a/vendor/github.com/buildpacks/pack/internal/termui/dive.go b/vendor/github.com/buildpacks/pack/internal/termui/dive.go new file mode 100644 index 0000000000..16e2ee5142 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/termui/dive.go @@ -0,0 +1,291 @@ +package termui + +import ( + "archive/tar" + "fmt" + "strings" + + "github.com/dustin/go-humanize" + "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" + + "github.com/buildpacks/pack/pkg/dist" +) + +type Dive struct { + app app + menuTable *tview.Table + fileExplorerTable *tview.Table + buildpackInfo []dist.BuildpackInfo + buildpacksTreeMap map[string]*tview.TreeNode + escHandler func() +} + +func NewDive(app app, buildpackInfo []dist.BuildpackInfo, selectedBuildpack dist.BuildpackInfo, nodes map[string]*tview.TreeNode, escHandler func()) *Dive { + menu := initMenu(buildpackInfo, nodes) + fileExplorerTable := initFileExplorer() + + screen := tview.NewFlex(). + SetDirection(tview.FlexColumn). + AddItem(menu, 0, 1, true). + AddItem(fileExplorerTable, 0, 2, false) + + d := &Dive{ + app: app, + menuTable: menu, + fileExplorerTable: fileExplorerTable, + buildpackInfo: buildpackInfo, + buildpacksTreeMap: nodes, + escHandler: escHandler, + } + + d.handle() + + for row := 0; row < d.menuTable.GetRowCount(); row++ { + if strings.Contains(d.menuTable.GetCell(row, 0).Text, selectedBuildpack.FullName()) { + d.menuTable.Select(row, 0) + } + } + + d.app.SetRoot(screen, true) + return d +} + +func (d *Dive) handle() { + selectionFunc := func(nodeKey string) func(row, column int) { + return func(row, column int) { + node := d.fileExplorerTable.GetCell(row, 3).GetReference().(*tview.TreeNode) + + if !node.GetReference().(*tar.Header).FileInfo().IsDir() { + return + } + + if node.IsExpanded() { + node.Collapse() + } else { + node.Expand() + } + + d.loadFileExplorerData(nodeKey) + } + } + + d.menuTable.SetSelectionChangedFunc(func(row, column int) { + // protect from panic + if row < 0 { + return + } + + // if SBOM + if row == d.menuTable.GetRowCount()-1 { + nodeKey := "layers/sbom" + + d.loadFileExplorerData(nodeKey) + + d.fileExplorerTable.ScrollToBeginning() + + d.fileExplorerTable.SetSelectedFunc(selectionFunc(nodeKey)) + return + } + + // if buildpack + selectedBuildpack := d.buildpackInfo[row-4] + nodeKey := "layers/" + strings.ReplaceAll(selectedBuildpack.ID, "/", "_") + + d.loadFileExplorerData(nodeKey) + + d.fileExplorerTable.ScrollToBeginning() + + d.fileExplorerTable.SetSelectedFunc(selectionFunc(nodeKey)) + }) + + d.menuTable.SetDoneFunc(func(key tcell.Key) { + switch key { + case tcell.KeyEscape: + d.escHandler() + case tcell.KeyTab: + screen := tview.NewFlex(). + SetDirection(tview.FlexColumn). + AddItem(d.menuTable, 0, 1, false). + AddItem(d.fileExplorerTable, 0, 2, true) + d.app.SetRoot(screen, true) + } + }) + + d.fileExplorerTable.SetDoneFunc(func(key tcell.Key) { + switch key { + case tcell.KeyEscape: + d.escHandler() + case tcell.KeyTab: + screen := tview.NewFlex(). + SetDirection(tview.FlexColumn). + AddItem(d.menuTable, 0, 1, true). + AddItem(d.fileExplorerTable, 0, 2, false) + d.app.SetRoot(screen, true) + } + }) +} + +func (d *Dive) loadFileExplorerData(nodeKey string) { + // Configure tree + root := tview.NewTreeNode("[::b]Filetree[::-]") + for _, child := range d.buildpacksTreeMap[nodeKey].GetChildren() { + root.AddChild(child) + } + + d.fileExplorerTable.Clear() + + // Configure Table + d.fileExplorerTable.SetCell(0, 0, tview.NewTableCell("[::b]Permission[::-]").SetSelectable(false)) + d.fileExplorerTable.SetCell(0, 1, tview.NewTableCell("[::b]UID:GID[::-]").SetSelectable(false). + SetAlign(tview.AlignRight)) + d.fileExplorerTable.SetCell(0, 2, tview.NewTableCell("[::b]Size[::-] ").SetSelectable(false). + SetAlign(tview.AlignRight)) + d.fileExplorerTable.SetCell(0, 3, tview.NewTableCell("[::b]Filetree[::-]").SetSelectable(false)) + + branchesMapping := map[*tview.TreeNode]Branches{ + root: {}, + } + + root.Walk(func(node, parent *tview.TreeNode) bool { + if node == root { + return true + } + + childCount := len(parent.GetChildren()) + isLast := parent.GetChildren()[childCount-1] == node + + if isLast { + branchesMapping[node] = branchesMapping[parent].Add(NoBranchSymbol) + } else { + branchesMapping[node] = branchesMapping[parent].Add(BranchSymbol) + } + + return true + }) + + var tableRow = 0 + root.Walk(func(node, parent *tview.TreeNode) bool { + if node == root { + tableRow++ + return true + } + + ref := node.GetReference().(*tar.Header) + + collapseIcon := "" + if !node.IsExpanded() { + collapseIcon = " ⬥ " + } + + size := "-" + if ref.Typeflag != tar.TypeDir { + size = humanize.Bytes(uint64(ref.Size)) + } + + color := "" + switch { + case ref.FileInfo().IsDir(): + color = "[mediumturquoise::b]" + case ref.Typeflag == tar.TypeSymlink, ref.Typeflag == tar.TypeLink: + color = "[purple]" + case ref.FileInfo().Mode().Perm()&0111 != 0: + color = "[yellow]" + } + + childCount := len(parent.GetChildren()) + isLast := parent.GetChildren()[childCount-1] == node + branches := branchesMapping[node].String() + + currentBranch := MiddleBranchSymbol.String() + if isLast { + currentBranch = LastBranchSymbol.String() + } + + withLink := "" + if ref.Typeflag == tar.TypeSymlink || ref.Typeflag == tar.TypeLink { + withLink = "[-:-:-] → " + ref.Linkname + } + + d.fileExplorerTable.SetCell(tableRow, 0, tview.NewTableCell(color+ref.FileInfo().Mode().String())) + d.fileExplorerTable.SetCell(tableRow, 1, tview.NewTableCell(color+fmt.Sprintf("%d:%d", ref.Uid, ref.Gid)). + SetAlign(tview.AlignRight)) + d.fileExplorerTable.SetCell(tableRow, 2, tview.NewTableCell(color+size+" "). + SetAlign(tview.AlignRight)) + d.fileExplorerTable.SetCell(tableRow, 3, tview.NewTableCell(branches+currentBranch+color+ref.FileInfo().Name()+withLink+collapseIcon). + SetReference(node)) + + tableRow++ + return node.IsExpanded() + }) +} + +func initMenu(buildpackInfos []dist.BuildpackInfo, nodes map[string]*tview.TreeNode) *tview.Table { + style := tcell.StyleDefault. + Foreground(tcell.ColorMediumTurquoise). + Background(tcell.ColorDarkSlateGray). + Attributes(tcell.AttrBold) + + table := tview.NewTable() + table. + SetSelectable(true, false). + SetSelectedStyle(style). + SetBorder(true). + SetBorderPadding(1, 1, 2, 1). + SetTitle("| [::b]phases[::-] |"). + SetTitleAlign(tview.AlignLeft). + SetBackgroundColor(backgroundColor) + + var i int + for _, phase := range []string{"ANALYZE", "DETECT", "RESTORE", "BUILD"} { + table.SetCell(i, 0, + tview.NewTableCell(phase). + SetTextColor(tcell.ColorDarkGray). + SetSelectable(false)) + i++ + } + + for _, buildpackInfo := range buildpackInfos { + table.SetCell(i, 0, + tview.NewTableCell(" ↳ "+buildpackInfo.FullName()). + SetTextColor(tcell.ColorMediumTurquoise). + SetSelectable(true)) + i++ + } + + table.SetCell(i, 0, + tview.NewTableCell("EXPORT"). + SetTextColor(tcell.ColorDarkGray). + SetSelectable(false)) + + // set spacing + i++ + i++ + sbomTextColor := tcell.ColorMediumTurquoise + sbomSelectable := true + if _, ok := nodes["layers/sbom"]; !ok { + sbomTextColor = tcell.ColorDarkGray + sbomSelectable = false + } + + table.SetCell(i, 0, + tview.NewTableCell("SBOM"). + SetTextColor(sbomTextColor). + SetSelectable(sbomSelectable)) + return table +} + +func initFileExplorer() *tview.Table { + style := tcell.StyleDefault. + Foreground(tcell.ColorMediumTurquoise). + Background(tcell.ColorDarkSlateGray). + Attributes(tcell.AttrBold) + + tbl := tview.NewTable() + tbl.SetFixed(1, 0). + SetSelectedStyle(style). + SetSelectable(true, false). + SetBackgroundColor(backgroundColor). + SetBorderPadding(1, 1, 4, 0) + return tbl +} diff --git a/vendor/github.com/buildpacks/pack/internal/termui/logger.go b/vendor/github.com/buildpacks/pack/internal/termui/logger.go new file mode 100644 index 0000000000..0f07791da3 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/termui/logger.go @@ -0,0 +1,45 @@ +package termui + +import "io" + +func (s *Termui) Debug(msg string) { + // not implemented +} + +func (s *Termui) Debugf(fmt string, v ...interface{}) { + // not implemented +} + +func (s *Termui) Info(msg string) { + s.textChan <- msg +} + +func (s *Termui) Infof(fmt string, v ...interface{}) { + // not implemented +} + +func (s *Termui) Warn(msg string) { + // not implemented +} + +func (s *Termui) Warnf(fmt string, v ...interface{}) { + // not implemented +} + +func (s *Termui) Error(msg string) { + // not implemented +} + +func (s *Termui) Errorf(fmt string, v ...interface{}) { + // not implemented +} + +func (s *Termui) Writer() io.Writer { + // not implemented + return nil +} + +func (s *Termui) IsVerbose() bool { + // not implemented + return false +} diff --git a/vendor/github.com/buildpacks/pack/internal/termui/termui.go b/vendor/github.com/buildpacks/pack/internal/termui/termui.go new file mode 100644 index 0000000000..938a585fcc --- /dev/null +++ b/vendor/github.com/buildpacks/pack/internal/termui/termui.go @@ -0,0 +1,211 @@ +package termui + +import ( + "archive/tar" + "bufio" + "io" + "io/ioutil" + "path" + "path/filepath" + "strings" + + dcontainer "github.com/docker/docker/api/types/container" + "github.com/docker/docker/pkg/stdcopy" + "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" + + "github.com/buildpacks/pack/internal/builder" + "github.com/buildpacks/pack/internal/container" + "github.com/buildpacks/pack/pkg/dist" +) + +var ( + backgroundColor = tcell.NewRGBColor(5, 30, 40) +) + +type app interface { + SetRoot(root tview.Primitive, fullscreen bool) *tview.Application + Draw() *tview.Application + QueueUpdateDraw(f func()) *tview.Application + Run() error +} + +type buildr interface { + BaseImageName() string + Buildpacks() []dist.BuildpackInfo + LifecycleDescriptor() builder.LifecycleDescriptor + Stack() builder.StackMetadata +} + +type page interface { + Handle(txt string) + Stop() + SetNodes(nodes map[string]*tview.TreeNode) +} + +type Termui struct { + app app + bldr buildr + currentPage page + + appName string + runImageName string + exitCode int64 + textChan chan string + buildpackChan chan dist.BuildpackInfo + nodes map[string]*tview.TreeNode +} + +func NewTermui(appName string, bldr *builder.Builder, runImageName string) *Termui { + return &Termui{ + appName: appName, + bldr: bldr, + runImageName: runImageName, + app: tview.NewApplication(), + buildpackChan: make(chan dist.BuildpackInfo, 50), + textChan: make(chan string, 50), + nodes: map[string]*tview.TreeNode{}, + } +} + +// Run starts the terminal UI process in the foreground +// and the passed in function in the background +func (s *Termui) Run(funk func()) error { + go func() { + funk() + s.showBuildStatus() + }() + go s.handle() + defer s.stop() + + s.currentPage = NewDetect(s.app, s.buildpackChan, s.bldr) + return s.app.Run() +} + +func (s *Termui) stop() { + close(s.textChan) +} + +func (s *Termui) handle() { + var detectLogs []string + + for txt := range s.textChan { + switch { + // We need a line that signals when detect phase is completed. + // Since the phase order is: analyze -> detect -> restore -> build -> ... + // "===> RESTORING" would be the best option. But since restore is optional, + // "===> BUILDING" serves as the next best option. + case strings.Contains(txt, "===> BUILDING"): + s.currentPage.Stop() + + s.currentPage = NewDashboard(s.app, s.appName, s.bldr, s.runImageName, collect(s.buildpackChan), detectLogs) + s.currentPage.Handle(txt) + default: + detectLogs = append(detectLogs, txt) + s.currentPage.Handle(txt) + } + } +} + +func (s *Termui) Handler() container.Handler { + return func(bodyChan <-chan dcontainer.ContainerWaitOKBody, errChan <-chan error, reader io.Reader) error { + var ( + copyErr = make(chan error) + r, w = io.Pipe() + scanner = bufio.NewScanner(r) + ) + + go func() { + defer w.Close() + + _, err := stdcopy.StdCopy(w, ioutil.Discard, reader) + if err != nil { + copyErr <- err + } + }() + + for { + select { + //TODO: errors should show up on screen + // instead of halting loop + //See: https://github.com/buildpacks/pack/issues/1262 + case err := <-copyErr: + return err + case err := <-errChan: + return err + case body := <-bodyChan: + s.exitCode = body.StatusCode + return nil + default: + if scanner.Scan() { + s.textChan <- scanner.Text() + continue + } + + if err := scanner.Err(); err != nil { + return err + } + } + } + } +} + +func (s *Termui) ReadLayers(reader io.ReadCloser) error { + defer reader.Close() + + tr := tar.NewReader(reader) + + for { + header, err := tr.Next() + + switch { + // if no more files are found return + case err == io.EOF: + if s.currentPage != nil { + s.currentPage.SetNodes(s.nodes) + } + return nil + + // return any other error + case err != nil: + return err + + // if the header is nil, just skip it (not sure how this happens) + case header == nil: + continue + + default: + name := path.Clean(header.Name) + dir, base := filepath.Split(name) + dir = strings.TrimSuffix(dir, "/") + + if s.nodes[dir] == nil { + s.nodes[dir] = tview.NewTreeNode(dir) + } + + node := tview.NewTreeNode(base).SetReference(header) + s.nodes[name] = node + s.nodes[dir].AddChild(node) + } + } +} + +func (s *Termui) showBuildStatus() { + if s.exitCode == 0 { + s.textChan <- "[green::b]\n\nBUILD SUCCEEDED" + return + } + + s.textChan <- "[red::b]\n\nBUILD FAILED" +} + +func collect(buildpackChan chan dist.BuildpackInfo) []dist.BuildpackInfo { + close(buildpackChan) + + var result []dist.BuildpackInfo + for txt := range buildpackChan { + result = append(result, txt) + } + + return result +} diff --git a/vendor/github.com/buildpacks/pack/pkg/archive/archive.go b/vendor/github.com/buildpacks/pack/pkg/archive/archive.go new file mode 100644 index 0000000000..cf0fa09537 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/archive/archive.go @@ -0,0 +1,382 @@ +// Package archive defines a set of functions for reading and writing directories and files in a number of tar formats. +package archive // import "github.com/buildpacks/pack/pkg/archive" + +import ( + "archive/tar" + "archive/zip" + "io" + "io/ioutil" + "os" + "path" + "path/filepath" + "time" + + "github.com/docker/docker/pkg/ioutils" + "github.com/pkg/errors" +) + +var NormalizedDateTime time.Time + +func init() { + NormalizedDateTime = time.Date(1980, time.January, 1, 0, 0, 1, 0, time.UTC) +} + +type TarWriter interface { + WriteHeader(hdr *tar.Header) error + Write(b []byte) (int, error) + Close() error +} + +type TarWriterFactory interface { + NewWriter(io.Writer) TarWriter +} + +type defaultTarWriterFactory struct{} + +func DefaultTarWriterFactory() TarWriterFactory { + return defaultTarWriterFactory{} +} + +func (defaultTarWriterFactory) NewWriter(w io.Writer) TarWriter { + return tar.NewWriter(w) +} + +func ReadDirAsTar(srcDir, basePath string, uid, gid int, mode int64, normalizeModTime, includeRoot bool, fileFilter func(string) bool) io.ReadCloser { + return GenerateTar(func(tw TarWriter) error { + return WriteDirToTar(tw, srcDir, basePath, uid, gid, mode, normalizeModTime, includeRoot, fileFilter) + }) +} + +func ReadZipAsTar(srcPath, basePath string, uid, gid int, mode int64, normalizeModTime bool, fileFilter func(string) bool) io.ReadCloser { + return GenerateTar(func(tw TarWriter) error { + return WriteZipToTar(tw, srcPath, basePath, uid, gid, mode, normalizeModTime, fileFilter) + }) +} + +func GenerateTar(genFn func(TarWriter) error) io.ReadCloser { + return GenerateTarWithWriter(genFn, DefaultTarWriterFactory()) +} + +// GenerateTarWithTar returns a reader to a tar from a generator function using a writer from the provided factory. +// Note that the generator will not fully execute until the reader is fully read from. Any errors returned by the +// generator will be returned when reading the reader. +func GenerateTarWithWriter(genFn func(TarWriter) error, twf TarWriterFactory) io.ReadCloser { + errChan := make(chan error) + pr, pw := io.Pipe() + + go func() { + tw := twf.NewWriter(pw) + defer func() { + if r := recover(); r != nil { + tw.Close() + pw.CloseWithError(errors.Errorf("panic: %v", r)) + } + }() + + err := genFn(tw) + + closeErr := tw.Close() + closeErr = aggregateError(closeErr, pw.CloseWithError(err)) + + errChan <- closeErr + }() + + closed := false + return ioutils.NewReadCloserWrapper(pr, func() error { + if closed { + return errors.New("reader already closed") + } + + var completeErr error + + // closing the reader ensures that if anything attempts + // further reading it doesn't block waiting for content + if err := pr.Close(); err != nil { + completeErr = aggregateError(completeErr, err) + } + + // wait until everything closes properly + if err := <-errChan; err != nil { + completeErr = aggregateError(completeErr, err) + } + + closed = true + return completeErr + }) +} + +func aggregateError(base, addition error) error { + if addition == nil { + return base + } + + if base == nil { + return addition + } + + return errors.Wrap(addition, base.Error()) +} + +func CreateSingleFileTarReader(path, txt string) io.ReadCloser { + tarBuilder := TarBuilder{} + tarBuilder.AddFile(path, 0644, NormalizedDateTime, []byte(txt)) + return tarBuilder.Reader(DefaultTarWriterFactory()) +} + +func CreateSingleFileTar(tarFile, path, txt string) error { + tarBuilder := TarBuilder{} + tarBuilder.AddFile(path, 0644, NormalizedDateTime, []byte(txt)) + return tarBuilder.WriteToPath(tarFile, DefaultTarWriterFactory()) +} + +// ErrEntryNotExist is an error returned if an entry path doesn't exist +var ErrEntryNotExist = errors.New("not exist") + +// IsEntryNotExist detects whether a given error is of type ErrEntryNotExist +func IsEntryNotExist(err error) bool { + return err == ErrEntryNotExist || errors.Cause(err) == ErrEntryNotExist +} + +// ReadTarEntry reads and returns a tar file +func ReadTarEntry(rc io.Reader, entryPath string) (*tar.Header, []byte, error) { + tr := tar.NewReader(rc) + for { + header, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return nil, nil, errors.Wrap(err, "failed to get next tar entry") + } + + if path.Clean(header.Name) == entryPath { + buf, err := ioutil.ReadAll(tr) + if err != nil { + return nil, nil, errors.Wrapf(err, "failed to read contents of '%s'", entryPath) + } + + return header, buf, nil + } + } + + return nil, nil, errors.Wrapf(ErrEntryNotExist, "could not find entry path '%s'", entryPath) +} + +// WriteDirToTar writes the contents of a directory to a tar writer. `basePath` is the "location" in the tar the +// contents will be placed. The includeRoot param sets the permissions and metadata on the root file. +func WriteDirToTar(tw TarWriter, srcDir, basePath string, uid, gid int, mode int64, normalizeModTime, includeRoot bool, fileFilter func(string) bool) error { + if includeRoot { + rootHeader := &tar.Header{ + Typeflag: tar.TypeDir, + Name: basePath, + Mode: mode, + } + finalizeHeader(rootHeader, uid, gid, mode, normalizeModTime) + if err := tw.WriteHeader(rootHeader); err != nil { + return err + } + } + + return filepath.Walk(srcDir, func(file string, fi os.FileInfo, err error) error { + var relPath string + if fileFilter != nil { + relPath, err = filepath.Rel(srcDir, file) + if err != nil { + return err + } + if !fileFilter(relPath) { + return nil + } + } + if err != nil { + return err + } + + if fi.Mode()&os.ModeSocket != 0 { + return nil + } + + var header *tar.Header + if fi.Mode()&os.ModeSymlink != 0 { + target, err := os.Readlink(file) + if err != nil { + return err + } + + // Ensure that symlinks have Linux link names, independent of source OS + header, err = tar.FileInfoHeader(fi, filepath.ToSlash(target)) + if err != nil { + return err + } + } else { + header, err = tar.FileInfoHeader(fi, fi.Name()) + if err != nil { + return err + } + } + + if relPath == "" { + relPath, err = filepath.Rel(srcDir, file) + if err != nil { + return err + } + } + if relPath == "." { + return nil + } + + header.Name = filepath.ToSlash(filepath.Join(basePath, relPath)) + finalizeHeader(header, uid, gid, mode, normalizeModTime) + + if err := tw.WriteHeader(header); err != nil { + return err + } + + if fi.Mode().IsRegular() { + f, err := os.Open(filepath.Clean(file)) + if err != nil { + return err + } + defer f.Close() + + if _, err := io.Copy(tw, f); err != nil { + return err + } + } + + return nil + }) +} + +// WriteZipToTar writes the contents of a zip file to a tar writer. +func WriteZipToTar(tw TarWriter, srcZip, basePath string, uid, gid int, mode int64, normalizeModTime bool, fileFilter func(string) bool) error { + zipReader, err := zip.OpenReader(srcZip) + if err != nil { + return err + } + defer zipReader.Close() + + var fileMode int64 + for _, f := range zipReader.File { + if fileFilter != nil && !fileFilter(f.Name) { + continue + } + + fileMode = mode + if isFatFile(f.FileHeader) { + fileMode = 0777 + } + + var header *tar.Header + if f.Mode()&os.ModeSymlink != 0 { + target, err := func() (string, error) { + r, err := f.Open() + if err != nil { + return "", nil + } + defer r.Close() + + // contents is the target of the symlink + target, err := ioutil.ReadAll(r) + if err != nil { + return "", err + } + + return string(target), nil + }() + + if err != nil { + return err + } + + header, err = tar.FileInfoHeader(f.FileInfo(), target) + if err != nil { + return err + } + } else { + header, err = tar.FileInfoHeader(f.FileInfo(), f.Name) + if err != nil { + return err + } + } + + header.Name = filepath.ToSlash(filepath.Join(basePath, f.Name)) + finalizeHeader(header, uid, gid, fileMode, normalizeModTime) + + if err := tw.WriteHeader(header); err != nil { + return err + } + + if f.Mode().IsRegular() { + err := func() error { + fi, err := f.Open() + if err != nil { + return err + } + defer fi.Close() + + _, err = io.Copy(tw, fi) + return err + }() + + if err != nil { + return err + } + } + } + + return nil +} + +func isFatFile(header zip.FileHeader) bool { + var ( + creatorFAT uint16 = 0 + creatorVFAT uint16 = 14 + ) + + // This identifies FAT files, based on the `zip` source: https://golang.org/src/archive/zip/struct.go + firstByte := header.CreatorVersion >> 8 + return firstByte == creatorFAT || firstByte == creatorVFAT +} + +func finalizeHeader(header *tar.Header, uid, gid int, mode int64, normalizeModTime bool) { + NormalizeHeader(header, normalizeModTime) + if mode != -1 { + header.Mode = mode + } + header.Uid = uid + header.Gid = gid +} + +// NormalizeHeader normalizes a tar.Header +// +// Normalizes the following: +// - ModTime +// - GID +// - UID +// - User Name +// - Group Name +func NormalizeHeader(header *tar.Header, normalizeModTime bool) { + if normalizeModTime { + header.ModTime = NormalizedDateTime + } + header.Uid = 0 + header.Gid = 0 + header.Uname = "" + header.Gname = "" +} + +// IsZip detects whether or not a File is a zip directory +func IsZip(path string) (bool, error) { + r, err := zip.OpenReader(path) + + switch { + case err == nil: + r.Close() + return true, nil + case err == zip.ErrFormat: + return false, nil + default: + return false, err + } +} diff --git a/vendor/github.com/buildpacks/pack/pkg/archive/tar_builder.go b/vendor/github.com/buildpacks/pack/pkg/archive/tar_builder.go new file mode 100644 index 0000000000..9659d3c39d --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/archive/tar_builder.go @@ -0,0 +1,94 @@ +package archive + +import ( + "archive/tar" + "io" + "os" + "time" + + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/style" +) + +type TarBuilder struct { + files []fileEntry +} + +type fileEntry struct { + typeFlag byte + path string + mode int64 + modTime time.Time + contents []byte +} + +func (t *TarBuilder) AddFile(path string, mode int64, modTime time.Time, contents []byte) { + t.files = append(t.files, fileEntry{ + typeFlag: tar.TypeReg, + path: path, + mode: mode, + modTime: modTime, + contents: contents, + }) +} + +func (t *TarBuilder) AddDir(path string, mode int64, modTime time.Time) { + t.files = append(t.files, fileEntry{ + typeFlag: tar.TypeDir, + path: path, + mode: mode, + modTime: modTime, + }) +} + +func (t *TarBuilder) Reader(twf TarWriterFactory) io.ReadCloser { + pr, pw := io.Pipe() + go func() { + var err error + defer func() { + pw.CloseWithError(err) + }() + _, err = t.WriteTo(pw, twf) + }() + + return pr +} + +func (t *TarBuilder) WriteToPath(path string, twf TarWriterFactory) error { + fh, err := os.Create(path) + if err != nil { + return errors.Wrapf(err, "create file for tar: %s", style.Symbol(path)) + } + defer fh.Close() + + _, err = t.WriteTo(fh, twf) + return err +} + +func (t *TarBuilder) WriteTo(w io.Writer, twf TarWriterFactory) (int64, error) { + var written int64 + tw := twf.NewWriter(w) + defer tw.Close() + + for _, f := range t.files { + if err := tw.WriteHeader(&tar.Header{ + Typeflag: f.typeFlag, + Name: f.path, + Size: int64(len(f.contents)), + Mode: f.mode, + ModTime: f.modTime, + }); err != nil { + return written, err + } + + n, err := tw.Write(f.contents) + if err != nil { + return written, err + } + + written += int64(n) + } + + return written, nil +} diff --git a/vendor/github.com/buildpacks/pack/pkg/blob/blob.go b/vendor/github.com/buildpacks/pack/pkg/blob/blob.go new file mode 100644 index 0000000000..1846435c4f --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/blob/blob.go @@ -0,0 +1,80 @@ +package blob + +import ( + "bytes" + "compress/gzip" + "io" + "os" + + "github.com/docker/docker/pkg/ioutils" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/pkg/archive" +) + +type Blob interface { + Open() (io.ReadCloser, error) +} + +type blob struct { + path string +} + +func NewBlob(path string) Blob { + return &blob{path: path} +} + +// Open returns an io.ReadCloser whose contents are in tar archive format +func (b blob) Open() (r io.ReadCloser, err error) { + fi, err := os.Stat(b.path) + if err != nil { + return nil, errors.Wrapf(err, "read blob at path '%s'", b.path) + } + if fi.IsDir() { + return archive.ReadDirAsTar(b.path, ".", 0, 0, -1, true, false, nil), nil + } + + fh, err := os.Open(b.path) + if err != nil { + return nil, errors.Wrap(err, "open buildpack archive") + } + defer func() { + if err != nil { + fh.Close() + } + }() + + if ok, err := isGZip(fh); err != nil { + return nil, errors.Wrap(err, "check header") + } else if !ok { + return fh, nil + } + gzr, err := gzip.NewReader(fh) + if err != nil { + return nil, errors.Wrap(err, "create gzip reader") + } + + rc := ioutils.NewReadCloserWrapper(gzr, func() error { + defer fh.Close() + return gzr.Close() + }) + + return rc, nil +} + +func isGZip(file io.ReadSeeker) (bool, error) { + b := make([]byte, 3) + if _, err := file.Seek(0, 0); err != nil { + return false, err + } + _, err := file.Read(b) + if err != nil && err != io.EOF { + return false, err + } else if err == io.EOF { + return false, nil + } + if _, err := file.Seek(0, 0); err != nil { + return false, err + } + return bytes.Equal(b, []byte("\x1f\x8b\x08")), nil +} diff --git a/vendor/github.com/buildpacks/pack/pkg/blob/downloader.go b/vendor/github.com/buildpacks/pack/pkg/blob/downloader.go new file mode 100644 index 0000000000..1f5e9e5918 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/blob/downloader.go @@ -0,0 +1,196 @@ +package blob + +import ( + "context" + "crypto/sha256" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "os" + "path/filepath" + + "github.com/mitchellh/ioprogress" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/paths" + "github.com/buildpacks/pack/internal/style" +) + +const ( + cacheDirPrefix = "c" + cacheVersion = "2" +) + +type Logger interface { + Debugf(fmt string, v ...interface{}) + Infof(fmt string, v ...interface{}) + Writer() io.Writer +} + +type Downloader interface { + Download(ctx context.Context, pathOrURI string) (Blob, error) +} + +type downloader struct { + logger Logger + baseCacheDir string +} + +func NewDownloader(logger Logger, baseCacheDir string) Downloader { + return &downloader{ + logger: logger, + baseCacheDir: baseCacheDir, + } +} + +func (d *downloader) Download(ctx context.Context, pathOrURI string) (Blob, error) { + if paths.IsURI(pathOrURI) { + parsedURL, err := url.Parse(pathOrURI) + if err != nil { + return nil, errors.Wrapf(err, "parsing path/uri %s", style.Symbol(pathOrURI)) + } + + var path string + switch parsedURL.Scheme { + case "file": + path, err = paths.URIToFilePath(pathOrURI) + case "http", "https": + path, err = d.handleHTTP(ctx, pathOrURI) + default: + err = fmt.Errorf("unsupported protocol %s in URI %s", style.Symbol(parsedURL.Scheme), style.Symbol(pathOrURI)) + } + if err != nil { + return nil, err + } + + return &blob{path: path}, nil + } + + path := d.handleFile(pathOrURI) + + return &blob{path: path}, nil +} + +func (d *downloader) handleFile(path string) string { + path, err := filepath.Abs(path) + if err != nil { + return "" + } + + return path +} + +func (d *downloader) handleHTTP(ctx context.Context, uri string) (string, error) { + cacheDir := d.versionedCacheDir() + + if err := os.MkdirAll(cacheDir, 0750); err != nil { + return "", err + } + + cachePath := filepath.Join(cacheDir, fmt.Sprintf("%x", sha256.Sum256([]byte(uri)))) + + etagFile := cachePath + ".etag" + etagExists, err := fileExists(etagFile) + if err != nil { + return "", err + } + + etag := "" + if etagExists { + bytes, err := ioutil.ReadFile(filepath.Clean(etagFile)) + if err != nil { + return "", err + } + etag = string(bytes) + } + + reader, etag, err := d.downloadAsStream(ctx, uri, etag) + if err != nil { + return "", err + } else if reader == nil { + return cachePath, nil + } + defer reader.Close() + + fh, err := os.Create(cachePath) + if err != nil { + return "", errors.Wrapf(err, "create cache path %s", style.Symbol(cachePath)) + } + defer fh.Close() + + _, err = io.Copy(fh, reader) + if err != nil { + return "", errors.Wrap(err, "writing cache") + } + + if err = ioutil.WriteFile(etagFile, []byte(etag), 0744); err != nil { + return "", errors.Wrap(err, "writing etag") + } + + return cachePath, nil +} + +func (d *downloader) downloadAsStream(ctx context.Context, uri string, etag string) (io.ReadCloser, string, error) { + req, err := http.NewRequest("GET", uri, nil) + if err != nil { + return nil, "", err + } + req = req.WithContext(ctx) + + if etag != "" { + req.Header.Set("If-None-Match", etag) + } + + resp, err := (&http.Client{}).Do(req) //nolint:bodyclose + if err != nil { + return nil, "", err + } + + if resp.StatusCode >= 200 && resp.StatusCode < 300 { + d.logger.Infof("Downloading from %s", style.Symbol(uri)) + return withProgress(d.logger.Writer(), resp.Body, resp.ContentLength), resp.Header.Get("Etag"), nil + } + + if resp.StatusCode == 304 { + d.logger.Debugf("Using cached version of %s", style.Symbol(uri)) + return nil, etag, nil + } + + return nil, "", fmt.Errorf( + "could not download from %s, code http status %s", + style.Symbol(uri), style.SymbolF("%d", resp.StatusCode), + ) +} + +func withProgress(writer io.Writer, rc io.ReadCloser, length int64) io.ReadCloser { + return &progressReader{ + Closer: rc, + Reader: &ioprogress.Reader{ + Reader: rc, + Size: length, + DrawFunc: ioprogress.DrawTerminalf(writer, ioprogress.DrawTextFormatBytes), + }, + } +} + +type progressReader struct { + *ioprogress.Reader + io.Closer +} + +func (d *downloader) versionedCacheDir() string { + return filepath.Join(d.baseCacheDir, cacheDirPrefix+cacheVersion) +} + +func fileExists(file string) (bool, error) { + _, err := os.Stat(file) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, err + } + return true, nil +} diff --git a/vendor/github.com/buildpacks/pack/pkg/buildpack/builder.go b/vendor/github.com/buildpacks/pack/pkg/buildpack/builder.go new file mode 100644 index 0000000000..da0f8226c5 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/buildpack/builder.go @@ -0,0 +1,295 @@ +package buildpack + +import ( + "archive/tar" + "compress/gzip" + "io/ioutil" + "os" + + "github.com/buildpacks/imgutil/layer" + + "github.com/buildpacks/imgutil" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/empty" + "github.com/google/go-containerregistry/pkg/v1/layout" + "github.com/google/go-containerregistry/pkg/v1/mutate" + "github.com/google/go-containerregistry/pkg/v1/tarball" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/stack" + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/archive" + "github.com/buildpacks/pack/pkg/dist" +) + +type ImageFactory interface { + NewImage(repoName string, local bool, imageOS string) (imgutil.Image, error) +} + +type WorkableImage interface { + SetLabel(string, string) error + AddLayerWithDiffID(path, diffID string) error +} + +type layoutImage struct { + v1.Image +} + +func (i *layoutImage) SetLabel(key string, val string) error { + configFile, err := i.ConfigFile() + if err != nil { + return err + } + config := *configFile.Config.DeepCopy() + if config.Labels == nil { + config.Labels = map[string]string{} + } + config.Labels[key] = val + i.Image, err = mutate.Config(i.Image, config) + return err +} + +func (i *layoutImage) AddLayerWithDiffID(path, _ string) error { + tarLayer, err := tarball.LayerFromFile(path, tarball.WithCompressionLevel(gzip.DefaultCompression)) + if err != nil { + return err + } + i.Image, err = mutate.AppendLayers(i.Image, tarLayer) + if err != nil { + return errors.Wrap(err, "add layer") + } + return nil +} + +type PackageBuilder struct { + buildpack Buildpack + dependencies []Buildpack + imageFactory ImageFactory +} + +// TODO: Rename to PackageBuilder +func NewBuilder(imageFactory ImageFactory) *PackageBuilder { + return &PackageBuilder{ + imageFactory: imageFactory, + } +} + +func (b *PackageBuilder) SetBuildpack(buildpack Buildpack) { + b.buildpack = buildpack +} + +func (b *PackageBuilder) AddDependency(buildpack Buildpack) { + b.dependencies = append(b.dependencies, buildpack) +} + +func (b *PackageBuilder) finalizeImage(image WorkableImage, tmpDir string) error { + if err := dist.SetLabel(image, MetadataLabel, &Metadata{ + BuildpackInfo: b.buildpack.Descriptor().Info, + Stacks: b.resolvedStacks(), + }); err != nil { + return err + } + + bpLayers := dist.BuildpackLayers{} + for _, bp := range append(b.dependencies, b.buildpack) { + bpLayerTar, err := ToLayerTar(tmpDir, bp) + if err != nil { + return err + } + + diffID, err := dist.LayerDiffID(bpLayerTar) + if err != nil { + return errors.Wrapf(err, + "getting content hashes for buildpack %s", + style.Symbol(bp.Descriptor().Info.FullName()), + ) + } + + if err := image.AddLayerWithDiffID(bpLayerTar, diffID.String()); err != nil { + return errors.Wrapf(err, "adding layer tar for buildpack %s", style.Symbol(bp.Descriptor().Info.FullName())) + } + + dist.AddBuildpackToLayersMD(bpLayers, bp.Descriptor(), diffID.String()) + } + + if err := dist.SetLabel(image, dist.BuildpackLayersLabel, bpLayers); err != nil { + return err + } + + return nil +} + +func (b *PackageBuilder) validate() error { + if b.buildpack == nil { + return errors.New("buildpack must be set") + } + + if err := validateBuildpacks(b.buildpack, b.dependencies); err != nil { + return err + } + + if len(b.resolvedStacks()) == 0 { + return errors.Errorf("no compatible stacks among provided buildpacks") + } + + return nil +} + +func (b *PackageBuilder) resolvedStacks() []dist.Stack { + stacks := b.buildpack.Descriptor().Stacks + for _, bp := range b.dependencies { + bpd := bp.Descriptor() + + if len(stacks) == 0 { + stacks = bpd.Stacks + } else if len(bpd.Stacks) > 0 { // skip over "meta-buildpacks" + stacks = stack.MergeCompatible(stacks, bpd.Stacks) + } + } + + return stacks +} + +func (b *PackageBuilder) SaveAsFile(path, imageOS string) error { + if err := b.validate(); err != nil { + return err + } + + layoutImage, err := newLayoutImage(imageOS) + if err != nil { + return errors.Wrap(err, "creating layout image") + } + + tmpDir, err := ioutil.TempDir("", "package-buildpack") + if err != nil { + return err + } + defer os.RemoveAll(tmpDir) + + if err := b.finalizeImage(layoutImage, tmpDir); err != nil { + return err + } + + layoutDir, err := ioutil.TempDir(tmpDir, "oci-layout") + if err != nil { + return errors.Wrap(err, "creating oci-layout temp dir") + } + + p, err := layout.Write(layoutDir, empty.Index) + if err != nil { + return errors.Wrap(err, "writing index") + } + + if err := p.AppendImage(layoutImage); err != nil { + return errors.Wrap(err, "writing layout") + } + + outputFile, err := os.Create(path) + if err != nil { + return errors.Wrap(err, "creating output file") + } + defer outputFile.Close() + + tw := tar.NewWriter(outputFile) + defer tw.Close() + + return archive.WriteDirToTar(tw, layoutDir, "/", 0, 0, 0755, true, false, nil) +} + +func newLayoutImage(imageOS string) (*layoutImage, error) { + i := empty.Image + + configFile, err := i.ConfigFile() + if err != nil { + return nil, err + } + + configFile.OS = imageOS + i, err = mutate.ConfigFile(i, configFile) + if err != nil { + return nil, err + } + + if imageOS == "windows" { + baseLayerReader, err := layer.WindowsBaseLayer() + if err != nil { + return nil, err + } + + baseLayer, err := tarball.LayerFromReader(baseLayerReader, tarball.WithCompressionLevel(gzip.DefaultCompression)) + if err != nil { + return nil, err + } + + i, err = mutate.AppendLayers(i, baseLayer) + if err != nil { + return nil, err + } + } + + return &layoutImage{Image: i}, nil +} + +func (b *PackageBuilder) SaveAsImage(repoName string, publish bool, imageOS string) (imgutil.Image, error) { + if err := b.validate(); err != nil { + return nil, err + } + + image, err := b.imageFactory.NewImage(repoName, !publish, imageOS) + if err != nil { + return nil, errors.Wrapf(err, "creating image") + } + + tmpDir, err := ioutil.TempDir("", "package-buildpack") + if err != nil { + return nil, err + } + defer os.RemoveAll(tmpDir) + + if err := b.finalizeImage(image, tmpDir); err != nil { + return nil, err + } + + if err := image.Save(); err != nil { + return nil, err + } + + return image, nil +} + +func validateBuildpacks(mainBP Buildpack, depBPs []Buildpack) error { + depsWithRefs := map[string][]dist.BuildpackInfo{} + + for _, bp := range depBPs { + depsWithRefs[bp.Descriptor().Info.FullName()] = nil + } + + for _, bp := range append([]Buildpack{mainBP}, depBPs...) { // List of everything + bpd := bp.Descriptor() + for _, orderEntry := range bpd.Order { + for _, groupEntry := range orderEntry.Group { + if _, ok := depsWithRefs[groupEntry.BuildpackInfo.FullName()]; !ok { + return errors.Errorf( + "buildpack %s references buildpack %s which is not present", + style.Symbol(bpd.Info.FullName()), + style.Symbol(groupEntry.FullName()), + ) + } + + depsWithRefs[groupEntry.BuildpackInfo.FullName()] = append(depsWithRefs[groupEntry.BuildpackInfo.FullName()], bpd.Info) + } + } + } + + for bp, refs := range depsWithRefs { + if len(refs) == 0 { + return errors.Errorf( + "buildpack %s is not used by buildpack %s", + style.Symbol(bp), + style.Symbol(mainBP.Descriptor().Info.FullName()), + ) + } + } + + return nil +} diff --git a/vendor/github.com/buildpacks/pack/pkg/buildpack/buildpack.go b/vendor/github.com/buildpacks/pack/pkg/buildpack/buildpack.go new file mode 100644 index 0000000000..d3713b6cf8 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/buildpack/buildpack.go @@ -0,0 +1,242 @@ +package buildpack + +import ( + "archive/tar" + "fmt" + "io" + "os" + "path" + "path/filepath" + + "github.com/BurntSushi/toml" + "github.com/buildpacks/lifecycle/api" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/archive" + "github.com/buildpacks/pack/pkg/dist" +) + +type Blob interface { + // Open returns a io.ReadCloser for the contents of the Blob in tar format. + Open() (io.ReadCloser, error) +} + +//go:generate mockgen -package testmocks -destination ../testmocks/mock_buildpack.go github.com/buildpacks/pack/pkg/buildpack Buildpack + +type Buildpack interface { + // Open returns a reader to a tar with contents structured as per the distribution spec + // (currently '/cnbs/buildpacks/{ID}/{version}/*', all entries with a zeroed-out + // timestamp and root UID/GID). + Open() (io.ReadCloser, error) + Descriptor() dist.BuildpackDescriptor +} + +type buildpack struct { + descriptor dist.BuildpackDescriptor + Blob `toml:"-"` +} + +func (b *buildpack) Descriptor() dist.BuildpackDescriptor { + return b.descriptor +} + +// FromBlob constructs a buildpack from a blob. It is assumed that the buildpack +// contents are structured as per the distribution spec (currently '/cnbs/buildpacks/{ID}/{version}/*'). +func FromBlob(bpd dist.BuildpackDescriptor, blob Blob) Buildpack { + return &buildpack{ + Blob: blob, + descriptor: bpd, + } +} + +// FromRootBlob constructs a buildpack from a blob. It is assumed that the buildpack contents reside at the +// root of the blob. The constructed buildpack contents will be structured as per the distribution spec (currently +// a tar with contents under '/cnbs/buildpacks/{ID}/{version}/*'). +func FromRootBlob(blob Blob, layerWriterFactory archive.TarWriterFactory) (Buildpack, error) { + bpd := dist.BuildpackDescriptor{} + rc, err := blob.Open() + if err != nil { + return nil, errors.Wrap(err, "open buildpack") + } + defer rc.Close() + + _, buf, err := archive.ReadTarEntry(rc, "buildpack.toml") + if err != nil { + return nil, errors.Wrap(err, "reading buildpack.toml") + } + + bpd.API = api.MustParse(dist.AssumedBuildpackAPIVersion) + _, err = toml.Decode(string(buf), &bpd) + if err != nil { + return nil, errors.Wrap(err, "decoding buildpack.toml") + } + + err = validateDescriptor(bpd) + if err != nil { + return nil, errors.Wrap(err, "invalid buildpack.toml") + } + + return &buildpack{ + descriptor: bpd, + Blob: &distBlob{ + openFn: func() io.ReadCloser { + return archive.GenerateTarWithWriter( + func(tw archive.TarWriter) error { + return toDistTar(tw, bpd, blob) + }, + layerWriterFactory, + ) + }, + }, + }, nil +} + +type distBlob struct { + openFn func() io.ReadCloser +} + +func (b *distBlob) Open() (io.ReadCloser, error) { + return b.openFn(), nil +} + +func toDistTar(tw archive.TarWriter, bpd dist.BuildpackDescriptor, blob Blob) error { + ts := archive.NormalizedDateTime + + if err := tw.WriteHeader(&tar.Header{ + Typeflag: tar.TypeDir, + Name: path.Join(dist.BuildpacksDir, bpd.EscapedID()), + Mode: 0755, + ModTime: ts, + }); err != nil { + return errors.Wrapf(err, "writing buildpack id dir header") + } + + baseTarDir := path.Join(dist.BuildpacksDir, bpd.EscapedID(), bpd.Info.Version) + if err := tw.WriteHeader(&tar.Header{ + Typeflag: tar.TypeDir, + Name: baseTarDir, + Mode: 0755, + ModTime: ts, + }); err != nil { + return errors.Wrapf(err, "writing buildpack version dir header") + } + + rc, err := blob.Open() + if err != nil { + return errors.Wrap(err, "reading buildpack blob") + } + defer rc.Close() + + tr := tar.NewReader(rc) + for { + header, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return errors.Wrap(err, "failed to get next tar entry") + } + + archive.NormalizeHeader(header, true) + header.Name = path.Clean(header.Name) + if header.Name == "." || header.Name == "/" { + continue + } + + header.Mode = calcFileMode(header) + header.Name = path.Join(baseTarDir, header.Name) + err = tw.WriteHeader(header) + if err != nil { + return errors.Wrapf(err, "failed to write header for '%s'", header.Name) + } + + _, err = io.Copy(tw, tr) + if err != nil { + return errors.Wrapf(err, "failed to write contents to '%s'", header.Name) + } + } + + return nil +} + +func calcFileMode(header *tar.Header) int64 { + switch { + case header.Typeflag == tar.TypeDir: + return 0755 + case nameOneOf(header.Name, + path.Join("bin", "detect"), + path.Join("bin", "build"), + ): + return 0755 + case anyExecBit(header.Mode): + return 0755 + } + + return 0644 +} + +func nameOneOf(name string, paths ...string) bool { + for _, p := range paths { + if name == p { + return true + } + } + return false +} + +func anyExecBit(mode int64) bool { + return mode&0111 != 0 +} + +func validateDescriptor(bpd dist.BuildpackDescriptor) error { + if bpd.Info.ID == "" { + return errors.Errorf("%s is required", style.Symbol("buildpack.id")) + } + + if bpd.Info.Version == "" { + return errors.Errorf("%s is required", style.Symbol("buildpack.version")) + } + + if len(bpd.Order) == 0 && len(bpd.Stacks) == 0 { + return errors.Errorf( + "buildpack %s: must have either %s or an %s defined", + style.Symbol(bpd.Info.FullName()), + style.Symbol("stacks"), + style.Symbol("order"), + ) + } + + if len(bpd.Order) >= 1 && len(bpd.Stacks) >= 1 { + return errors.Errorf( + "buildpack %s: cannot have both %s and an %s defined", + style.Symbol(bpd.Info.FullName()), + style.Symbol("stacks"), + style.Symbol("order"), + ) + } + + return nil +} + +func ToLayerTar(dest string, bp Buildpack) (string, error) { + bpd := bp.Descriptor() + bpReader, err := bp.Open() + if err != nil { + return "", errors.Wrap(err, "opening buildpack blob") + } + defer bpReader.Close() + + layerTar := filepath.Join(dest, fmt.Sprintf("%s.%s.tar", bpd.EscapedID(), bpd.Info.Version)) + fh, err := os.Create(layerTar) + if err != nil { + return "", errors.Wrap(err, "create file for tar") + } + defer fh.Close() + + if _, err := io.Copy(fh, bpReader); err != nil { + return "", errors.Wrap(err, "writing buildpack blob to tar") + } + + return layerTar, nil +} diff --git a/vendor/github.com/buildpacks/pack/pkg/buildpack/buildpackage.go b/vendor/github.com/buildpacks/pack/pkg/buildpack/buildpackage.go new file mode 100644 index 0000000000..df40107355 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/buildpack/buildpackage.go @@ -0,0 +1,13 @@ +package buildpack + +import ( + "github.com/buildpacks/pack/pkg/dist" +) + +// TODO: Move to dist +const MetadataLabel = "io.buildpacks.buildpackage.metadata" + +type Metadata struct { + dist.BuildpackInfo + Stacks []dist.Stack `toml:"stacks" json:"stacks"` +} diff --git a/vendor/github.com/buildpacks/pack/pkg/buildpack/downloader.go b/vendor/github.com/buildpacks/pack/pkg/buildpack/downloader.go new file mode 100644 index 0000000000..82f41a8eba --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/buildpack/downloader.go @@ -0,0 +1,175 @@ +package buildpack + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + + "github.com/buildpacks/imgutil" + + "github.com/buildpacks/pack/internal/layer" + "github.com/buildpacks/pack/internal/paths" + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/blob" + "github.com/buildpacks/pack/pkg/dist" + "github.com/buildpacks/pack/pkg/image" +) + +type Logger interface { + Debug(msg string) + Debugf(fmt string, v ...interface{}) + Info(msg string) + Infof(fmt string, v ...interface{}) + Warn(msg string) + Warnf(fmt string, v ...interface{}) + Error(msg string) + Errorf(fmt string, v ...interface{}) +} + +type ImageFetcher interface { + Fetch(ctx context.Context, name string, options image.FetchOptions) (imgutil.Image, error) +} + +type Downloader interface { + Download(ctx context.Context, pathOrURI string) (blob.Blob, error) +} + +//go:generate mockgen -package testmocks -destination ../testmocks/mock_registry_resolver.go github.com/buildpacks/pack/pkg/buildpack RegistryResolver + +type RegistryResolver interface { + Resolve(registryName, bpURI string) (string, error) +} + +type buildpackDownloader struct { + logger Logger + imageFetcher ImageFetcher + downloader Downloader + registryResolver RegistryResolver +} + +func NewDownloader(logger Logger, imageFetcher ImageFetcher, downloader Downloader, registryResolver RegistryResolver) *buildpackDownloader { //nolint:golint,gosimple + return &buildpackDownloader{ + logger: logger, + imageFetcher: imageFetcher, + downloader: downloader, + registryResolver: registryResolver, + } +} + +type DownloadOptions struct { + // Buildpack registry name. Defines where all registry buildpacks will be pulled from. + RegistryName string + + // The base directory to use to resolve relative assets + RelativeBaseDir string + + // The OS of the builder image + ImageOS string + + // Deprecated: the older alternative to buildpack URI + ImageName string + + Daemon bool + + PullPolicy image.PullPolicy +} + +func (c *buildpackDownloader) Download(ctx context.Context, buildpackURI string, opts DownloadOptions) (Buildpack, []Buildpack, error) { + var err error + var locatorType LocatorType + if buildpackURI == "" && opts.ImageName != "" { + c.logger.Warn("The 'image' key is deprecated. Use 'uri=\"docker://...\"' instead.") + buildpackURI = opts.ImageName + locatorType = PackageLocator + } else { + locatorType, err = GetLocatorType(buildpackURI, opts.RelativeBaseDir, []dist.BuildpackInfo{}) + if err != nil { + return nil, nil, err + } + } + + var mainBP Buildpack + var depBPs []Buildpack + switch locatorType { + case PackageLocator: + imageName := ParsePackageLocator(buildpackURI) + c.logger.Debugf("Downloading buildpack from image: %s", style.Symbol(imageName)) + mainBP, depBPs, err = extractPackagedBuildpacks(ctx, imageName, c.imageFetcher, image.FetchOptions{Daemon: opts.Daemon, PullPolicy: opts.PullPolicy}) + if err != nil { + return nil, nil, errors.Wrapf(err, "extracting from registry %s", style.Symbol(buildpackURI)) + } + case RegistryLocator: + c.logger.Debugf("Downloading buildpack from registry: %s", style.Symbol(buildpackURI)) + address, err := c.registryResolver.Resolve(opts.RegistryName, buildpackURI) + if err != nil { + return nil, nil, errors.Wrapf(err, "locating in registry: %s", style.Symbol(buildpackURI)) + } + + mainBP, depBPs, err = extractPackagedBuildpacks(ctx, address, c.imageFetcher, image.FetchOptions{Daemon: opts.Daemon, PullPolicy: opts.PullPolicy}) + if err != nil { + return nil, nil, errors.Wrapf(err, "extracting from registry %s", style.Symbol(buildpackURI)) + } + case URILocator: + buildpackURI, err = paths.FilePathToURI(buildpackURI, opts.RelativeBaseDir) + if err != nil { + return nil, nil, errors.Wrapf(err, "making absolute: %s", style.Symbol(buildpackURI)) + } + + c.logger.Debugf("Downloading buildpack from URI: %s", style.Symbol(buildpackURI)) + + blob, err := c.downloader.Download(ctx, buildpackURI) + if err != nil { + return nil, nil, errors.Wrapf(err, "downloading buildpack from %s", style.Symbol(buildpackURI)) + } + + mainBP, depBPs, err = decomposeBuildpack(blob, opts.ImageOS) + if err != nil { + return nil, nil, errors.Wrapf(err, "extracting from %s", style.Symbol(buildpackURI)) + } + default: + return nil, nil, fmt.Errorf("error reading %s: invalid locator: %s", buildpackURI, locatorType) + } + return mainBP, depBPs, nil +} + +// decomposeBuildpack decomposes a buildpack blob into the main builder (order buildpack) and it's dependencies buildpacks. +func decomposeBuildpack(blob blob.Blob, imageOS string) (mainBP Buildpack, depBPs []Buildpack, err error) { + isOCILayout, err := IsOCILayoutBlob(blob) + if err != nil { + return mainBP, depBPs, errors.Wrap(err, "inspecting buildpack blob") + } + + if isOCILayout { + mainBP, depBPs, err = BuildpacksFromOCILayoutBlob(blob) + if err != nil { + return mainBP, depBPs, errors.Wrap(err, "extracting buildpacks") + } + } else { + layerWriterFactory, err := layer.NewWriterFactory(imageOS) + if err != nil { + return mainBP, depBPs, errors.Wrapf(err, "get tar writer factory for OS %s", style.Symbol(imageOS)) + } + + mainBP, err = FromRootBlob(blob, layerWriterFactory) + if err != nil { + return mainBP, depBPs, errors.Wrap(err, "reading buildpack") + } + } + + return mainBP, depBPs, nil +} + +func extractPackagedBuildpacks(ctx context.Context, pkgImageRef string, fetcher ImageFetcher, fetchOptions image.FetchOptions) (mainBP Buildpack, depBPs []Buildpack, err error) { + pkgImage, err := fetcher.Fetch(ctx, pkgImageRef, fetchOptions) + if err != nil { + return nil, nil, errors.Wrapf(err, "fetching image") + } + + mainBP, depBPs, err = ExtractBuildpacks(pkgImage) + if err != nil { + return nil, nil, errors.Wrapf(err, "extracting buildpacks from %s", style.Symbol(pkgImageRef)) + } + + return mainBP, depBPs, nil +} diff --git a/vendor/github.com/buildpacks/pack/pkg/buildpack/locator_type.go b/vendor/github.com/buildpacks/pack/pkg/buildpack/locator_type.go new file mode 100644 index 0000000000..2f1a74bb5b --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/buildpack/locator_type.go @@ -0,0 +1,147 @@ +package buildpack + +import ( + "fmt" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/google/go-containerregistry/pkg/name" + + "github.com/buildpacks/pack/internal/paths" + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/dist" +) + +type LocatorType int + +const ( + InvalidLocator LocatorType = iota + FromBuilderLocator + URILocator + IDLocator + PackageLocator + RegistryLocator + // added entries here should also be added to `String()` +) + +const ( + fromBuilderPrefix = "urn:cnb:builder" + deprecatedFromBuilderPrefix = "from=builder" + fromRegistryPrefix = "urn:cnb:registry" + fromDockerPrefix = "docker:/" +) + +var ( + // https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string + semverPattern = `(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?` + registryPattern = regexp.MustCompile(`^[a-z0-9\-\.]+\/[a-z0-9\-\.]+(?:@` + semverPattern + `)?$`) +) + +func (l LocatorType) String() string { + return []string{ + "InvalidLocator", + "FromBuilderLocator", + "URILocator", + "IDLocator", + "PackageLocator", + "RegistryLocator", + }[l] +} + +// GetLocatorType determines which type of locator is designated by the given input. +// If a type cannot be determined, `INVALID_LOCATOR` will be returned. If an error +// is encountered, it will be returned. +func GetLocatorType(locator string, relativeBaseDir string, buildpacksFromBuilder []dist.BuildpackInfo) (LocatorType, error) { + if locator == deprecatedFromBuilderPrefix { + return FromBuilderLocator, nil + } + + if strings.HasPrefix(locator, fromBuilderPrefix+":") || strings.HasPrefix(locator, deprecatedFromBuilderPrefix+":") { + if !isFoundInBuilder(locator, buildpacksFromBuilder) { + return InvalidLocator, fmt.Errorf("%s is not a valid identifier", style.Symbol(locator)) + } + return IDLocator, nil + } + + if strings.HasPrefix(locator, fromRegistryPrefix+":") { + return RegistryLocator, nil + } + + if paths.IsURI(locator) { + if HasDockerLocator(locator) { + if _, err := name.ParseReference(locator); err == nil { + return PackageLocator, nil + } + } + return URILocator, nil + } + + return parseNakedLocator(locator, relativeBaseDir, buildpacksFromBuilder), nil +} + +func HasDockerLocator(locator string) bool { + return strings.HasPrefix(locator, fromDockerPrefix) +} + +func parseNakedLocator(locator, relativeBaseDir string, buildpacksFromBuilder []dist.BuildpackInfo) LocatorType { + // from here on, we're dealing with a naked locator, and we try to figure out what it is. To do this we check + // the following characteristics in order: + // 1. Does it match a path on the file system + // 2. Does it match a buildpack ID in the builder + // 3. Does it look like a Buildpack Registry ID + // 4. Does it look like a Docker ref + + if isLocalFile(locator, relativeBaseDir) { + return URILocator + } + + if isFoundInBuilder(locator, buildpacksFromBuilder) { + return IDLocator + } + + if canBeRegistryRef(locator) { + return RegistryLocator + } + + if canBePackageRef(locator) { + return PackageLocator + } + + return InvalidLocator +} + +func canBePackageRef(locator string) bool { + if _, err := name.ParseReference(locator); err == nil { + return true + } + + return false +} + +func canBeRegistryRef(locator string) bool { + return registryPattern.MatchString(locator) +} + +func isFoundInBuilder(locator string, candidates []dist.BuildpackInfo) bool { + id, version := ParseIDLocator(locator) + for _, c := range candidates { + if id == c.ID && (version == "" || version == c.Version) { + return true + } + } + return false +} + +func isLocalFile(locator, relativeBaseDir string) bool { + if !filepath.IsAbs(locator) { + locator = filepath.Join(relativeBaseDir, locator) + } + + if _, err := os.Stat(locator); err == nil { + return true + } + + return false +} diff --git a/vendor/github.com/buildpacks/pack/pkg/buildpack/oci_layout_package.go b/vendor/github.com/buildpacks/pack/pkg/buildpack/oci_layout_package.go new file mode 100644 index 0000000000..4be4ac6882 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/buildpack/oci_layout_package.go @@ -0,0 +1,193 @@ +package buildpack + +import ( + "archive/tar" + "compress/gzip" + "encoding/json" + "io" + "path" + "strings" + + "github.com/docker/docker/pkg/ioutils" + v1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/archive" + blob2 "github.com/buildpacks/pack/pkg/blob" + "github.com/buildpacks/pack/pkg/dist" +) + +// IsOCILayoutBlob checks whether a blob is in OCI layout format. +func IsOCILayoutBlob(blob blob2.Blob) (bool, error) { + readCloser, err := blob.Open() + if err != nil { + return false, err + } + defer readCloser.Close() + + _, _, err = archive.ReadTarEntry(readCloser, "/oci-layout") + if err != nil { + if archive.IsEntryNotExist(err) { + return false, nil + } + + return false, err + } + + return true, nil +} + +// BuildpackFromOCILayoutBlob constructs buildpacks from a blob in OCI layout format. +func BuildpacksFromOCILayoutBlob(blob Blob) (mainBP Buildpack, dependencies []Buildpack, err error) { + layoutPackage, err := newOCILayoutPackage(blob) + if err != nil { + return nil, nil, err + } + + return ExtractBuildpacks(layoutPackage) +} + +func ConfigFromOCILayoutBlob(blob Blob) (config v1.ImageConfig, err error) { + layoutPackage, err := newOCILayoutPackage(blob) + if err != nil { + return v1.ImageConfig{}, err + } + return layoutPackage.imageInfo.Config, nil +} + +type ociLayoutPackage struct { + imageInfo v1.Image + manifest v1.Manifest + blob Blob +} + +func newOCILayoutPackage(blob Blob) (*ociLayoutPackage, error) { + index := &v1.Index{} + + if err := unmarshalJSONFromBlob(blob, "/index.json", index); err != nil { + return nil, err + } + + var manifestDescriptor *v1.Descriptor + for _, m := range index.Manifests { + if m.MediaType == "application/vnd.docker.distribution.manifest.v2+json" { + manifestDescriptor = &m // nolint:scopelint + break + } + } + + if manifestDescriptor == nil { + return nil, errors.New("unable to find manifest") + } + + manifest := &v1.Manifest{} + if err := unmarshalJSONFromBlob(blob, pathFromDescriptor(*manifestDescriptor), manifest); err != nil { + return nil, err + } + + imageInfo := &v1.Image{} + if err := unmarshalJSONFromBlob(blob, pathFromDescriptor(manifest.Config), imageInfo); err != nil { + return nil, err + } + + layersLabel := imageInfo.Config.Labels[dist.BuildpackLayersLabel] + if layersLabel == "" { + return nil, errors.Errorf("label %s not found", style.Symbol(dist.BuildpackLayersLabel)) + } + + bpLayers := dist.BuildpackLayers{} + if err := json.Unmarshal([]byte(layersLabel), &bpLayers); err != nil { + return nil, errors.Wrap(err, "unmarshaling layers label") + } + + return &ociLayoutPackage{ + imageInfo: *imageInfo, + manifest: *manifest, + blob: blob, + }, nil +} + +func (o *ociLayoutPackage) Label(name string) (value string, err error) { + return o.imageInfo.Config.Labels[name], nil +} + +func (o *ociLayoutPackage) GetLayer(diffID string) (io.ReadCloser, error) { + index := -1 + for i, dID := range o.imageInfo.RootFS.DiffIDs { + if dID.String() == diffID { + index = i + break + } + } + if index == -1 { + return nil, errors.Errorf("layer %s not found in rootfs", style.Symbol(diffID)) + } + + layerDescriptor := o.manifest.Layers[index] + layerPath := pathFromDescriptor(layerDescriptor) + + blobReader, err := o.blob.Open() + if err != nil { + return nil, err + } + + tr := tar.NewReader(blobReader) + for { + header, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return nil, errors.Wrap(err, "failed to get next tar entry") + } + + if path.Clean(header.Name) == path.Clean(layerPath) { + finalReader := blobReader + + if strings.HasSuffix(layerDescriptor.MediaType, ".gzip") { + finalReader, err = gzip.NewReader(tr) + if err != nil { + return nil, err + } + } + + return ioutils.NewReadCloserWrapper(finalReader, func() error { + if err := finalReader.Close(); err != nil { + return err + } + + return blobReader.Close() + }), nil + } + } + + if err := blobReader.Close(); err != nil { + return nil, err + } + + return nil, errors.Errorf("layer blob %s not found", style.Symbol(layerPath)) +} + +func pathFromDescriptor(descriptor v1.Descriptor) string { + return path.Join("/blobs", descriptor.Digest.Algorithm().String(), descriptor.Digest.Encoded()) +} + +func unmarshalJSONFromBlob(blob Blob, path string, obj interface{}) error { + reader, err := blob.Open() + if err != nil { + return err + } + defer reader.Close() + + _, contents, err := archive.ReadTarEntry(reader, path) + if err != nil { + return err + } + + if err = json.Unmarshal(contents, obj); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/buildpacks/pack/pkg/buildpack/package.go b/vendor/github.com/buildpacks/pack/pkg/buildpack/package.go new file mode 100644 index 0000000000..c8ae73fb8c --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/buildpack/package.go @@ -0,0 +1,87 @@ +package buildpack + +import ( + "io" + + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/dist" +) + +type Package interface { + Label(name string) (value string, err error) + GetLayer(diffID string) (io.ReadCloser, error) +} + +func ExtractBuildpacks(pkg Package) (mainBP Buildpack, depBPs []Buildpack, err error) { + md := &Metadata{} + if found, err := dist.GetLabel(pkg, MetadataLabel, md); err != nil { + return nil, nil, err + } else if !found { + return nil, nil, errors.Errorf( + "could not find label %s", + style.Symbol(MetadataLabel), + ) + } + + bpLayers := dist.BuildpackLayers{} + ok, err := dist.GetLabel(pkg, dist.BuildpackLayersLabel, &bpLayers) + if err != nil { + return nil, nil, err + } + + if !ok { + return nil, nil, errors.Errorf( + "could not find label %s", + style.Symbol(dist.BuildpackLayersLabel), + ) + } + + for bpID, v := range bpLayers { + for bpVersion, bpInfo := range v { + desc := dist.BuildpackDescriptor{ + API: bpInfo.API, + Info: dist.BuildpackInfo{ + ID: bpID, + Version: bpVersion, + Homepage: bpInfo.Homepage, + Name: bpInfo.Name, + }, + Stacks: bpInfo.Stacks, + Order: bpInfo.Order, + } + + diffID := bpInfo.LayerDiffID // Allow use in closure + b := &openerBlob{ + opener: func() (io.ReadCloser, error) { + rc, err := pkg.GetLayer(diffID) + if err != nil { + return nil, errors.Wrapf(err, + "extracting buildpack %s layer (diffID %s)", + style.Symbol(desc.Info.FullName()), + style.Symbol(diffID), + ) + } + return rc, nil + }, + } + + if desc.Info.Match(md.BuildpackInfo) { // This is the order buildpack of the package + mainBP = FromBlob(desc, b) + } else { + depBPs = append(depBPs, FromBlob(desc, b)) + } + } + } + + return mainBP, depBPs, nil +} + +type openerBlob struct { + opener func() (io.ReadCloser, error) +} + +func (b *openerBlob) Open() (io.ReadCloser, error) { + return b.opener() +} diff --git a/vendor/github.com/buildpacks/pack/pkg/buildpack/parse_name.go b/vendor/github.com/buildpacks/pack/pkg/buildpack/parse_name.go new file mode 100644 index 0000000000..db00972241 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/buildpack/parse_name.go @@ -0,0 +1,60 @@ +package buildpack + +import ( + "fmt" + "strings" +) + +// ParseIDLocator parses a buildpack locator in the following formats into its ID and version. +// +// - [@] +// - urn:cnb:builder:[@] +// - urn:cnb:registry:[@] +// - from=builder:[@] (deprecated) +// +// If version is omitted, the version returned will be empty. Any "from=builder:" or "urn:cnb" prefix will be ignored. +func ParseIDLocator(locator string) (id string, version string) { + nakedLocator := parseRegistryLocator(parseBuilderLocator(locator)) + + parts := strings.Split(nakedLocator, "@") + if len(parts) == 2 { + return parts[0], parts[1] + } + return parts[0], "" +} + +// ParsePackageLocator parses a locator (in format `[docker://][/][:⏐@]`) to image name (`[/][:⏐@]`) +func ParsePackageLocator(locator string) (imageName string) { + return strings.TrimPrefix( + strings.TrimPrefix( + strings.TrimPrefix(locator, fromDockerPrefix+"//"), + fromDockerPrefix+"/"), + fromDockerPrefix) +} + +// ParseRegistryID parses a registry id (ie. `/@`) into namespace, name and version components. +// +// Supported formats: +// - /[@] +// - urn:cnb:registry:/[@] +// +func ParseRegistryID(registryID string) (namespace string, name string, version string, err error) { + id, version := ParseIDLocator(registryID) + + parts := strings.Split(id, "/") + if len(parts) != 2 { + return "", "", "", fmt.Errorf("invalid registry ID: %s", registryID) + } + + return parts[0], parts[1], version, nil +} + +func parseRegistryLocator(locator string) (path string) { + return strings.TrimPrefix(locator, fromRegistryPrefix+":") +} + +func parseBuilderLocator(locator string) (path string) { + return strings.TrimPrefix( + strings.TrimPrefix(locator, deprecatedFromBuilderPrefix+":"), + fromBuilderPrefix+":") +} diff --git a/vendor/github.com/buildpacks/pack/pkg/client/build.go b/vendor/github.com/buildpacks/pack/pkg/client/build.go new file mode 100644 index 0000000000..2f1be611bd --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/client/build.go @@ -0,0 +1,938 @@ +package client + +import ( + "context" + "crypto/rand" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + + "github.com/Masterminds/semver" + "github.com/buildpacks/imgutil" + "github.com/buildpacks/imgutil/local" + "github.com/buildpacks/imgutil/remote" + "github.com/buildpacks/lifecycle/platform" + "github.com/docker/docker/api/types" + "github.com/docker/docker/volume/mounts" + "github.com/google/go-containerregistry/pkg/name" + "github.com/pkg/errors" + ignore "github.com/sabhiram/go-gitignore" + + "github.com/buildpacks/pack/internal/build" + "github.com/buildpacks/pack/internal/builder" + internalConfig "github.com/buildpacks/pack/internal/config" + pname "github.com/buildpacks/pack/internal/name" + "github.com/buildpacks/pack/internal/stack" + "github.com/buildpacks/pack/internal/stringset" + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/internal/termui" + "github.com/buildpacks/pack/pkg/archive" + "github.com/buildpacks/pack/pkg/buildpack" + "github.com/buildpacks/pack/pkg/dist" + "github.com/buildpacks/pack/pkg/image" + "github.com/buildpacks/pack/pkg/logging" + projectTypes "github.com/buildpacks/pack/pkg/project/types" +) + +const ( + minLifecycleVersionSupportingCreator = "0.7.4" + prevLifecycleVersionSupportingImage = "0.6.1" + minLifecycleVersionSupportingImage = "0.7.5" +) + +// LifecycleExecutor executes the lifecycle which satisfies the Cloud Native Buildpacks Lifecycle specification. +// Implementations of the Lifecycle must execute the following phases by calling the +// phase-specific lifecycle binary in order: +// +// Detection: /cnb/lifecycle/detector +// Analysis: /cnb/lifecycle/analyzer +// Cache Restoration: /cnb/lifecycle/restorer +// Build: /cnb/lifecycle/builder +// Export: /cnb/lifecycle/exporter +// +// or invoke the single creator binary: +// +// Creator: /cnb/lifecycle/creator +// +type LifecycleExecutor interface { + // Execute is responsible for invoking each of these binaries + // with the desired configuration. + Execute(ctx context.Context, opts build.LifecycleOptions) error +} + +type IsTrustedBuilder func(string) bool + +// BuildOptions defines configuration settings for a Build. +type BuildOptions struct { + // The base directory to use to resolve relative assets + RelativeBaseDir string + + // required. Name of output image. + Image string + + // required. Builder image name. + Builder string + + // Name of the buildpack registry. Used to + // add buildpacks to a build. + Registry string + + // AppPath is the path to application bits. + // If unset it defaults to current working directory. + AppPath string + + // Specify the run image the Image will be + // built atop. + RunImage string + + // Address of docker daemon exposed to build container + // e.g. tcp://example.com:1234, unix:///run/user/1000/podman/podman.sock + DockerHost string + + // Used to determine a run-image mirror if Run Image is empty. + // Used in combination with Builder metadata to determine to the the 'best' mirror. + // 'best' is defined as: + // - if Publish is true, the best mirror matches registry we are publishing to. + // - if Publish is false, the best mirror matches a registry specified in Image. + // - otherwise if both of the above did not match, use mirror specified in + // the builder metadata + AdditionalMirrors map[string][]string + + // User provided environment variables to the buildpacks. + // Buildpacks may both read and overwrite these values. + Env map[string]string + + // Option only valid if Publish is true + // Create an additional image that contains cache=true layers and push it to the registry. + CacheImage string + + // Option passed directly to the lifecycle. + // If true, publishes Image directly to a registry. + // Assumes Image contains a valid registry with credentials + // provided by the docker client. + Publish bool + + // Clear the build cache from previous builds. + ClearCache bool + + // Launch a terminal UI to depict the build process + Interactive bool + + // List of buildpack images or archives to add to a builder. + // These buildpacks may overwrite those on the builder if they + // share both an ID and Version with a buildpack on the builder. + Buildpacks []string + + // Additional image tags to push to, each will contain contents identical to Image + AdditionalTags []string + + // Configure the proxy environment variables, + // These variables will only be set in the build image + // and will not be used if proxy env vars are already set. + ProxyConfig *ProxyConfig + + // Configure network and volume mounts for the build containers. + ContainerConfig ContainerConfig + + // Process type that will be used when setting container start command. + DefaultProcessType string + + // Strategy for updating local images before a build. + PullPolicy image.PullPolicy + + // ProjectDescriptorBaseDir is the base directory to find relative resources referenced by the ProjectDescriptor + ProjectDescriptorBaseDir string + + // ProjectDescriptor describes the project and any configuration specific to the project + ProjectDescriptor projectTypes.Descriptor + + // The lifecycle image that will be used for the analysis, restore and export phases + // when using an untrusted builder. + LifecycleImage string + + // The location at which to mount the AppDir in the build image. + Workspace string + + // User's group id used to build the image + GroupID int + + // A previous image to set to a particular tag reference, digest reference, or (when performing a daemon build) image ID; + PreviousImage string + + // TrustBuilder when true optimizes builds by running + // all lifecycle phases in a single container. + // This places registry credentials on the builder's build image. + // Only trust builders from reputable sources. + TrustBuilder IsTrustedBuilder + + // Directory to output any SBOM artifacts + SBOMDestinationDir string +} + +// ProxyConfig specifies proxy setting to be set as environment variables in a container. +type ProxyConfig struct { + HTTPProxy string // Used to set HTTP_PROXY env var. + HTTPSProxy string // Used to set HTTPS_PROXY env var. + NoProxy string // Used to set NO_PROXY env var. +} + +// ContainerConfig is additional configuration of the docker container that all build steps +// occur within. +type ContainerConfig struct { + // Configure network settings of the build containers. + // The value of Network is handed directly to the docker client. + // For valid values of this field see: + // https://docs.docker.com/network/#network-drivers + Network string + + // Volumes are accessible during both detect build phases + // should have the form: /path/in/host:/path/in/container. + // For more about volume mounts, and their permissions see: + // https://docs.docker.com/storage/volumes/ + // + // It is strongly recommended you do not override any of the + // paths with volume mounts at the following locations: + // - /cnb + // - /layers + // - anything below /cnb/** + Volumes []string +} + +var IsSuggestedBuilderFunc = func(b string) bool { + for _, suggestedBuilder := range builder.SuggestedBuilders { + if b == suggestedBuilder.Image { + return true + } + } + return false +} + +// Build configures settings for the build container(s) and lifecycle. +// It then invokes the lifecycle to build an app image. +// If any configuration is deemed invalid, or if any lifecycle phases fail, +// an error will be returned and no image produced. +func (c *Client) Build(ctx context.Context, opts BuildOptions) error { + imageRef, err := c.parseTagReference(opts.Image) + if err != nil { + return errors.Wrapf(err, "invalid image name '%s'", opts.Image) + } + + appPath, err := c.processAppPath(opts.AppPath) + if err != nil { + return errors.Wrapf(err, "invalid app path '%s'", opts.AppPath) + } + + proxyConfig := c.processProxyConfig(opts.ProxyConfig) + + builderRef, err := c.processBuilderName(opts.Builder) + if err != nil { + return errors.Wrapf(err, "invalid builder '%s'", opts.Builder) + } + + rawBuilderImage, err := c.imageFetcher.Fetch(ctx, builderRef.Name(), image.FetchOptions{Daemon: true, PullPolicy: opts.PullPolicy}) + if err != nil { + return errors.Wrapf(err, "failed to fetch builder image '%s'", builderRef.Name()) + } + + bldr, err := c.getBuilder(rawBuilderImage) + if err != nil { + return errors.Wrapf(err, "invalid builder %s", style.Symbol(opts.Builder)) + } + + runImageName := c.resolveRunImage(opts.RunImage, imageRef.Context().RegistryStr(), builderRef.Context().RegistryStr(), bldr.Stack(), opts.AdditionalMirrors, opts.Publish) + runImage, err := c.validateRunImage(ctx, runImageName, opts.PullPolicy, opts.Publish, bldr.StackID) + if err != nil { + return errors.Wrapf(err, "invalid run-image '%s'", runImageName) + } + + var runMixins []string + if _, err := dist.GetLabel(runImage, stack.MixinsLabel, &runMixins); err != nil { + return err + } + + fetchedBPs, order, err := c.processBuildpacks(ctx, bldr.Image(), bldr.Buildpacks(), bldr.Order(), bldr.StackID, opts) + if err != nil { + return err + } + + if err := c.validateMixins(fetchedBPs, bldr, runImageName, runMixins); err != nil { + return errors.Wrap(err, "validating stack mixins") + } + + buildEnvs := map[string]string{} + for _, envVar := range opts.ProjectDescriptor.Build.Env { + buildEnvs[envVar.Name] = envVar.Value + } + + for k, v := range opts.Env { + buildEnvs[k] = v + } + + ephemeralBuilder, err := c.createEphemeralBuilder(rawBuilderImage, buildEnvs, order, fetchedBPs) + if err != nil { + return err + } + defer c.docker.ImageRemove(context.Background(), ephemeralBuilder.Name(), types.ImageRemoveOptions{Force: true}) + + var builderPlatformAPIs builder.APISet + builderPlatformAPIs = append(builderPlatformAPIs, ephemeralBuilder.LifecycleDescriptor().APIs.Platform.Deprecated...) + builderPlatformAPIs = append(builderPlatformAPIs, ephemeralBuilder.LifecycleDescriptor().APIs.Platform.Supported...) + + if !supportsPlatformAPI(builderPlatformAPIs) { + c.logger.Debugf("pack %s supports Platform API(s): %s", c.version, strings.Join(build.SupportedPlatformAPIVersions.AsStrings(), ", ")) + c.logger.Debugf("Builder %s supports Platform API(s): %s", style.Symbol(opts.Builder), strings.Join(builderPlatformAPIs.AsStrings(), ", ")) + return errors.Errorf("Builder %s is incompatible with this version of pack", style.Symbol(opts.Builder)) + } + + imgOS, err := rawBuilderImage.OS() + if err != nil { + return errors.Wrapf(err, "getting builder OS") + } + + processedVolumes, warnings, err := processVolumes(imgOS, opts.ContainerConfig.Volumes) + if err != nil { + return err + } + + for _, warning := range warnings { + c.logger.Warn(warning) + } + + fileFilter, err := getFileFilter(opts.ProjectDescriptor) + if err != nil { + return err + } + + runImageName, err = pname.TranslateRegistry(runImageName, c.registryMirrors, c.logger) + if err != nil { + return err + } + + projectMetadata := platform.ProjectMetadata{} + if c.experimental { + version := opts.ProjectDescriptor.Project.Version + sourceURL := opts.ProjectDescriptor.Project.SourceURL + if version != "" || sourceURL != "" { + projectMetadata.Source = &platform.ProjectSource{ + Type: "project", + Version: map[string]interface{}{"declared": version}, + Metadata: map[string]interface{}{"url": sourceURL}, + } + } + } + + // Default mode: if the TrustBuilder option is not set, trust the suggested builders. + if opts.TrustBuilder == nil { + opts.TrustBuilder = IsSuggestedBuilderFunc + } + + lifecycleOpts := build.LifecycleOptions{ + AppPath: appPath, + Image: imageRef, + Builder: ephemeralBuilder, + LifecycleImage: ephemeralBuilder.Name(), + RunImage: runImageName, + ProjectMetadata: projectMetadata, + ClearCache: opts.ClearCache, + Publish: opts.Publish, + TrustBuilder: opts.TrustBuilder(opts.Builder), + UseCreator: false, + DockerHost: opts.DockerHost, + CacheImage: opts.CacheImage, + HTTPProxy: proxyConfig.HTTPProxy, + HTTPSProxy: proxyConfig.HTTPSProxy, + NoProxy: proxyConfig.NoProxy, + Network: opts.ContainerConfig.Network, + AdditionalTags: opts.AdditionalTags, + Volumes: processedVolumes, + DefaultProcessType: opts.DefaultProcessType, + FileFilter: fileFilter, + Workspace: opts.Workspace, + GID: opts.GroupID, + PreviousImage: opts.PreviousImage, + Interactive: opts.Interactive, + Termui: termui.NewTermui(imageRef.Name(), ephemeralBuilder, runImageName), + SBOMDestinationDir: opts.SBOMDestinationDir, + } + + lifecycleVersion := ephemeralBuilder.LifecycleDescriptor().Info.Version + // Technically the creator is supported as of platform API version 0.3 (lifecycle version 0.7.0+) but earlier versions + // have bugs that make using the creator problematic. + lifecycleSupportsCreator := !lifecycleVersion.LessThan(semver.MustParse(minLifecycleVersionSupportingCreator)) + + if lifecycleSupportsCreator && opts.TrustBuilder(opts.Builder) { + lifecycleOpts.UseCreator = true + // no need to fetch a lifecycle image, it won't be used + if err := c.lifecycleExecutor.Execute(ctx, lifecycleOpts); err != nil { + return errors.Wrap(err, "executing lifecycle") + } + + return c.logImageNameAndSha(ctx, opts.Publish, imageRef) + } + + if !opts.TrustBuilder(opts.Builder) { + if lifecycleImageSupported(imgOS, lifecycleVersion) { + lifecycleImageName := opts.LifecycleImage + if lifecycleImageName == "" { + lifecycleImageName = fmt.Sprintf("%s:%s", internalConfig.DefaultLifecycleImageRepo, lifecycleVersion.String()) + } + + imgArch, err := rawBuilderImage.Architecture() + if err != nil { + return errors.Wrapf(err, "getting builder architecture") + } + + lifecycleImage, err := c.imageFetcher.Fetch( + ctx, + lifecycleImageName, + image.FetchOptions{Daemon: true, PullPolicy: opts.PullPolicy, Platform: fmt.Sprintf("%s/%s", imgOS, imgArch)}, + ) + if err != nil { + return errors.Wrap(err, "fetching lifecycle image") + } + + lifecycleOpts.LifecycleImage = lifecycleImage.Name() + } else { + return errors.Errorf("Lifecycle %s does not have an associated lifecycle image. Builder must be trusted.", lifecycleVersion.String()) + } + } + + if err := c.lifecycleExecutor.Execute(ctx, lifecycleOpts); err != nil { + return errors.Wrap(err, "executing lifecycle. This may be the result of using an untrusted builder") + } + + return c.logImageNameAndSha(ctx, opts.Publish, imageRef) +} + +func getFileFilter(descriptor projectTypes.Descriptor) (func(string) bool, error) { + if len(descriptor.Build.Exclude) > 0 { + excludes := ignore.CompileIgnoreLines(descriptor.Build.Exclude...) + return func(fileName string) bool { + return !excludes.MatchesPath(fileName) + }, nil + } + if len(descriptor.Build.Include) > 0 { + includes := ignore.CompileIgnoreLines(descriptor.Build.Include...) + return includes.MatchesPath, nil + } + + return nil, nil +} + +func lifecycleImageSupported(builderOS string, lifecycleVersion *builder.Version) bool { + return lifecycleVersion.Equal(builder.VersionMustParse(prevLifecycleVersionSupportingImage)) || + !lifecycleVersion.LessThan(semver.MustParse(minLifecycleVersionSupportingImage)) +} + +// supportsPlatformAPI determines whether pack can build using the builder based on the builder's supported Platform API versions. +func supportsPlatformAPI(builderPlatformAPIs builder.APISet) bool { + for _, packSupportedAPI := range build.SupportedPlatformAPIVersions { + for _, builderSupportedAPI := range builderPlatformAPIs { + supportsPlatform := packSupportedAPI.Compare(builderSupportedAPI) == 0 + if supportsPlatform { + return true + } + } + } + + return false +} + +func (c *Client) processBuilderName(builderName string) (name.Reference, error) { + if builderName == "" { + return nil, errors.New("builder is a required parameter if the client has no default builder") + } + return name.ParseReference(builderName, name.WeakValidation) +} + +func (c *Client) getBuilder(img imgutil.Image) (*builder.Builder, error) { + bldr, err := builder.FromImage(img) + if err != nil { + return nil, err + } + if bldr.Stack().RunImage.Image == "" { + return nil, errors.New("builder metadata is missing run-image") + } + + lifecycleDescriptor := bldr.LifecycleDescriptor() + if lifecycleDescriptor.Info.Version == nil { + return nil, errors.New("lifecycle version must be specified in builder") + } + if len(lifecycleDescriptor.APIs.Buildpack.Supported) == 0 { + return nil, errors.New("supported Lifecycle Buildpack APIs not specified") + } + if len(lifecycleDescriptor.APIs.Platform.Supported) == 0 { + return nil, errors.New("supported Lifecycle Platform APIs not specified") + } + + return bldr, nil +} + +func (c *Client) validateRunImage(context context.Context, name string, pullPolicy image.PullPolicy, publish bool, expectedStack string) (imgutil.Image, error) { + if name == "" { + return nil, errors.New("run image must be specified") + } + img, err := c.imageFetcher.Fetch(context, name, image.FetchOptions{Daemon: !publish, PullPolicy: pullPolicy}) + if err != nil { + return nil, err + } + stackID, err := img.Label("io.buildpacks.stack.id") + if err != nil { + return nil, err + } + if stackID != expectedStack { + return nil, fmt.Errorf("run-image stack id '%s' does not match builder stack '%s'", stackID, expectedStack) + } + return img, nil +} + +func (c *Client) validateMixins(additionalBuildpacks []buildpack.Buildpack, bldr *builder.Builder, runImageName string, runMixins []string) error { + if err := stack.ValidateMixins(bldr.Image().Name(), bldr.Mixins(), runImageName, runMixins); err != nil { + return err + } + + bps, err := allBuildpacks(bldr.Image(), additionalBuildpacks) + if err != nil { + return err + } + mixins := assembleAvailableMixins(bldr.Mixins(), runMixins) + + for _, bp := range bps { + if err := bp.EnsureStackSupport(bldr.StackID, mixins, true); err != nil { + return err + } + } + return nil +} + +// assembleAvailableMixins returns the set of mixins that are common between the two provided sets, plus build-only mixins and run-only mixins. +func assembleAvailableMixins(buildMixins, runMixins []string) []string { + // NOTE: We cannot simply union the two mixin sets, as this could introduce a mixin that is only present on one stack + // image but not the other. A buildpack that happens to require the mixin would fail to run properly, even though validation + // would pass. + // + // For example: + // + // Incorrect: + // Run image mixins: [A, B] + // Build image mixins: [A] + // Merged: [A, B] + // Buildpack requires: [A, B] + // Match? Yes + // + // Correct: + // Run image mixins: [A, B] + // Build image mixins: [A] + // Merged: [A] + // Buildpack requires: [A, B] + // Match? No + + buildOnly := stack.FindStageMixins(buildMixins, "build") + runOnly := stack.FindStageMixins(runMixins, "run") + _, _, common := stringset.Compare(buildMixins, runMixins) + + return append(common, append(buildOnly, runOnly...)...) +} + +// allBuildpacks aggregates all buildpacks declared on the image with additional buildpacks passed in. They are sorted +// by ID then Version. +func allBuildpacks(builderImage imgutil.Image, additionalBuildpacks []buildpack.Buildpack) ([]dist.BuildpackDescriptor, error) { + var all []dist.BuildpackDescriptor + var bpLayers dist.BuildpackLayers + if _, err := dist.GetLabel(builderImage, dist.BuildpackLayersLabel, &bpLayers); err != nil { + return nil, err + } + for id, bps := range bpLayers { + for ver, bp := range bps { + desc := dist.BuildpackDescriptor{ + Info: dist.BuildpackInfo{ + ID: id, + Version: ver, + }, + Stacks: bp.Stacks, + Order: bp.Order, + } + all = append(all, desc) + } + } + for _, bp := range additionalBuildpacks { + all = append(all, bp.Descriptor()) + } + + sort.Slice(all, func(i, j int) bool { + if all[i].Info.ID != all[j].Info.ID { + return all[i].Info.ID < all[j].Info.ID + } + return all[i].Info.Version < all[j].Info.Version + }) + + return all, nil +} + +func (c *Client) processAppPath(appPath string) (string, error) { + var ( + resolvedAppPath string + err error + ) + + if appPath == "" { + if appPath, err = os.Getwd(); err != nil { + return "", errors.Wrap(err, "get working dir") + } + } + + if resolvedAppPath, err = filepath.EvalSymlinks(appPath); err != nil { + return "", errors.Wrap(err, "evaluate symlink") + } + + if resolvedAppPath, err = filepath.Abs(resolvedAppPath); err != nil { + return "", errors.Wrap(err, "resolve absolute path") + } + + fi, err := os.Stat(resolvedAppPath) + if err != nil { + return "", errors.Wrap(err, "stat file") + } + + if !fi.IsDir() { + isZip, err := archive.IsZip(filepath.Clean(resolvedAppPath)) + if err != nil { + return "", errors.Wrap(err, "check zip") + } + + if !isZip { + return "", errors.New("app path must be a directory or zip") + } + } + + return resolvedAppPath, nil +} + +func (c *Client) processProxyConfig(config *ProxyConfig) ProxyConfig { + var ( + httpProxy, httpsProxy, noProxy string + ok bool + ) + if config != nil { + return *config + } + if httpProxy, ok = os.LookupEnv("HTTP_PROXY"); !ok { + httpProxy = os.Getenv("http_proxy") + } + if httpsProxy, ok = os.LookupEnv("HTTPS_PROXY"); !ok { + httpsProxy = os.Getenv("https_proxy") + } + if noProxy, ok = os.LookupEnv("NO_PROXY"); !ok { + noProxy = os.Getenv("no_proxy") + } + return ProxyConfig{ + HTTPProxy: httpProxy, + HTTPSProxy: httpsProxy, + NoProxy: noProxy, + } +} + +// processBuildpacks computes an order group based on the existing builder order and declared buildpacks. Additionally, +// it returns buildpacks that should be added to the builder. +// +// Visual examples: +// +// BUILDER ORDER +// ---------- +// - group: +// - A +// - B +// - group: +// - A +// +// WITH DECLARED: "from=builder", X +// ---------- +// - group: +// - A +// - B +// - X +// - group: +// - A +// - X +// +// WITH DECLARED: X, "from=builder", Y +// ---------- +// - group: +// - X +// - A +// - B +// - Y +// - group: +// - X +// - A +// - Y +// +// WITH DECLARED: X +// ---------- +// - group: +// - X +// +// WITH DECLARED: A +// ---------- +// - group: +// - A +func (c *Client) processBuildpacks(ctx context.Context, builderImage imgutil.Image, builderBPs []dist.BuildpackInfo, builderOrder dist.Order, stackID string, opts BuildOptions) (fetchedBPs []buildpack.Buildpack, order dist.Order, err error) { + pullPolicy := opts.PullPolicy + publish := opts.Publish + registry := opts.Registry + relativeBaseDir := opts.RelativeBaseDir + declaredBPs := opts.Buildpacks + + // declare buildpacks provided by project descriptor when no buildpacks are declared + if len(declaredBPs) == 0 && len(opts.ProjectDescriptor.Build.Buildpacks) != 0 { + relativeBaseDir = opts.ProjectDescriptorBaseDir + + for _, bp := range opts.ProjectDescriptor.Build.Buildpacks { + switch { + case bp.ID != "" && bp.Script.Inline != "" && bp.URI == "": + if bp.Script.API == "" { + return nil, nil, errors.New("Missing API version for inline buildpack") + } + + pathToInlineBuildpack, err := createInlineBuildpack(bp, stackID) + if err != nil { + return nil, nil, errors.Wrap(err, "Could not create temporary inline buildpack") + } + declaredBPs = append(declaredBPs, pathToInlineBuildpack) + case bp.URI != "": + declaredBPs = append(declaredBPs, bp.URI) + case bp.ID != "" && bp.Version != "": + declaredBPs = append(declaredBPs, fmt.Sprintf("%s@%s", bp.ID, bp.Version)) + default: + return nil, nil, errors.New("Invalid buildpack defined in project descriptor") + } + } + } + + order = dist.Order{{Group: []dist.BuildpackRef{}}} + for _, bp := range declaredBPs { + locatorType, err := buildpack.GetLocatorType(bp, relativeBaseDir, builderBPs) + if err != nil { + return nil, nil, err + } + + switch locatorType { + case buildpack.FromBuilderLocator: + switch { + case len(order) == 0 || len(order[0].Group) == 0: + order = builderOrder + case len(order) > 1: + // This should only ever be possible if they are using from=builder twice which we don't allow + return nil, nil, errors.New("buildpacks from builder can only be defined once") + default: + newOrder := dist.Order{} + groupToAdd := order[0].Group + for _, bOrderEntry := range builderOrder { + newEntry := dist.OrderEntry{Group: append(groupToAdd, bOrderEntry.Group...)} + newOrder = append(newOrder, newEntry) + } + + order = newOrder + } + case buildpack.IDLocator: + id, version := buildpack.ParseIDLocator(bp) + order = appendBuildpackToOrder(order, dist.BuildpackInfo{ + ID: id, + Version: version, + }) + default: + imageOS, err := builderImage.OS() + if err != nil { + return fetchedBPs, order, errors.Wrapf(err, "getting OS from %s", style.Symbol(builderImage.Name())) + } + mainBP, depBPs, err := c.buildpackDownloader.Download(ctx, bp, buildpack.DownloadOptions{ + RegistryName: registry, + ImageOS: imageOS, + RelativeBaseDir: relativeBaseDir, + Daemon: !publish, + PullPolicy: pullPolicy, + }) + if err != nil { + return fetchedBPs, order, errors.Wrap(err, "downloading buildpack") + } + fetchedBPs = append(append(fetchedBPs, mainBP), depBPs...) + order = appendBuildpackToOrder(order, mainBP.Descriptor().Info) + } + } + + return fetchedBPs, order, nil +} + +func appendBuildpackToOrder(order dist.Order, bpInfo dist.BuildpackInfo) (newOrder dist.Order) { + for _, orderEntry := range order { + newEntry := orderEntry + newEntry.Group = append(newEntry.Group, dist.BuildpackRef{ + BuildpackInfo: bpInfo, + Optional: false, + }) + newOrder = append(newOrder, newEntry) + } + + return newOrder +} + +func (c *Client) createEphemeralBuilder(rawBuilderImage imgutil.Image, env map[string]string, order dist.Order, buildpacks []buildpack.Buildpack) (*builder.Builder, error) { + origBuilderName := rawBuilderImage.Name() + bldr, err := builder.New(rawBuilderImage, fmt.Sprintf("pack.local/builder/%x:latest", randString(10))) + if err != nil { + return nil, errors.Wrapf(err, "invalid builder %s", style.Symbol(origBuilderName)) + } + + bldr.SetEnv(env) + for _, bp := range buildpacks { + bpInfo := bp.Descriptor().Info + c.logger.Debugf("Adding buildpack %s version %s to builder", style.Symbol(bpInfo.ID), style.Symbol(bpInfo.Version)) + bldr.AddBuildpack(bp) + } + if len(order) > 0 && len(order[0].Group) > 0 { + c.logger.Debug("Setting custom order") + bldr.SetOrder(order) + } + + if err := bldr.Save(c.logger, builder.CreatorMetadata{Version: c.version}); err != nil { + return nil, err + } + return bldr, nil +} + +// Returns a string iwith lowercase a-z, of length n +func randString(n int) string { + b := make([]byte, n) + _, err := rand.Read(b) + if err != nil { + panic(err) + } + for i := range b { + b[i] = 'a' + (b[i] % 26) + } + return string(b) +} + +func processVolumes(imgOS string, volumes []string) (processed []string, warnings []string, err error) { + parserOS := mounts.OSLinux + if imgOS == "windows" { + parserOS = mounts.OSWindows + } + parser := mounts.NewParser(parserOS) + for _, v := range volumes { + volume, err := parser.ParseMountRaw(v, "") + if err != nil { + return nil, nil, errors.Wrapf(err, "platform volume %q has invalid format", v) + } + + sensitiveDirs := []string{"/cnb", "/layers"} + if imgOS == "windows" { + sensitiveDirs = []string{`c:/cnb`, `c:\cnb`, `c:/layers`, `c:\layers`} + } + for _, p := range sensitiveDirs { + if strings.HasPrefix(strings.ToLower(volume.Spec.Target), p) { + warnings = append(warnings, fmt.Sprintf("Mounting to a sensitive directory %s", style.Symbol(volume.Spec.Target))) + } + } + + processed = append(processed, fmt.Sprintf("%s:%s:%s", volume.Spec.Source, volume.Spec.Target, processMode(volume.Mode))) + } + return processed, warnings, nil +} + +func processMode(mode string) string { + if mode == "" { + return "ro" + } + + return mode +} + +func (c *Client) logImageNameAndSha(ctx context.Context, publish bool, imageRef name.Reference) error { + // The image name and sha are printed in the lifecycle logs, and there is no need to print it again, unless output is suppressed. + if !logging.IsQuiet(c.logger) { + return nil + } + + img, err := c.imageFetcher.Fetch(ctx, imageRef.Name(), image.FetchOptions{Daemon: !publish, PullPolicy: image.PullNever}) + if err != nil { + return errors.Wrap(err, "fetching built image") + } + + id, err := img.Identifier() + if err != nil { + return errors.Wrap(err, "reading image sha") + } + + // Remove tag, if it exists, from the image name + imgName := strings.TrimSuffix(imageRef.String(), imageRef.Identifier()) + imgNameAndSha := fmt.Sprintf("%s@%s\n", imgName, parseDigestFromImageID(id)) + + // Access the logger's Writer directly to bypass ReportSuccessfulQuietBuild mode + _, err = c.logger.Writer().Write([]byte(imgNameAndSha)) + return err +} + +func parseDigestFromImageID(id imgutil.Identifier) string { + var digest string + switch v := id.(type) { + case local.IDIdentifier: + digest = v.String() + case remote.DigestIdentifier: + digest = v.Digest.DigestStr() + } + + digest = strings.TrimPrefix(digest, "sha256:") + return fmt.Sprintf("sha256:%s", digest) +} + +func createInlineBuildpack(bp projectTypes.Buildpack, stackID string) (string, error) { + pathToInlineBuilpack, err := ioutil.TempDir("", "inline-cnb") + if err != nil { + return pathToInlineBuilpack, err + } + + if bp.Version == "" { + bp.Version = "0.0.0" + } + + if err = createBuildpackTOML(pathToInlineBuilpack, bp.ID, bp.Version, bp.Script.API, []dist.Stack{{ID: stackID}}, nil); err != nil { + return pathToInlineBuilpack, err + } + + shell := bp.Script.Shell + if shell == "" { + shell = "/bin/sh" + } + + binBuild := fmt.Sprintf(`#!%s + +%s +`, shell, bp.Script.Inline) + + binDetect := fmt.Sprintf(`#!%s + +exit 0 +`, shell) + + if err = createBinScript(pathToInlineBuilpack, "build", binBuild, nil); err != nil { + return pathToInlineBuilpack, err + } + + if err = createBinScript(pathToInlineBuilpack, "build.bat", bp.Script.Inline, nil); err != nil { + return pathToInlineBuilpack, err + } + + if err = createBinScript(pathToInlineBuilpack, "detect", binDetect, nil); err != nil { + return pathToInlineBuilpack, err + } + + if err = createBinScript(pathToInlineBuilpack, "detect.bat", bp.Script.Inline, nil); err != nil { + return pathToInlineBuilpack, err + } + + return pathToInlineBuilpack, nil +} diff --git a/vendor/github.com/buildpacks/pack/pkg/client/client.go b/vendor/github.com/buildpacks/pack/pkg/client/client.go new file mode 100644 index 0000000000..0dc7b311f5 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/client/client.go @@ -0,0 +1,275 @@ +/* +Package client provides all the functionally provided by pack as a library through a go api. + +Prerequisites + +In order to use most functionality, you will need an OCI runtime such as Docker or podman installed. + +References + +This package provides functionality to create and manipulate all artifacts outlined in the Cloud Native Buildpacks specification. +An introduction to these artifacts and their usage can be found at https://buildpacks.io/docs/. + +The formal specification of the pack platform provides can be found at: https://github.com/buildpacks/spec. +*/ +package client + +import ( + "context" + "os" + "path/filepath" + + "github.com/buildpacks/imgutil" + "github.com/buildpacks/imgutil/local" + "github.com/buildpacks/imgutil/remote" + dockerClient "github.com/docker/docker/client" + "github.com/google/go-containerregistry/pkg/authn" + "github.com/pkg/errors" + + "github.com/buildpacks/pack" + "github.com/buildpacks/pack/internal/build" + iconfig "github.com/buildpacks/pack/internal/config" + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/blob" + "github.com/buildpacks/pack/pkg/buildpack" + "github.com/buildpacks/pack/pkg/image" + "github.com/buildpacks/pack/pkg/logging" +) + +//go:generate mockgen -package testmocks -destination ../testmocks/mock_docker_client.go github.com/docker/docker/client CommonAPIClient + +//go:generate mockgen -package testmocks -destination ../testmocks/mock_image_fetcher.go github.com/buildpacks/pack/pkg/client ImageFetcher + +// ImageFetcher is an interface representing the ability to fetch local and images. +type ImageFetcher interface { + // Fetch fetches an image by resolving it both remotely and locally depending on provided parameters. + // The pull behavior is dictated by the pullPolicy, which can have the following behavior + // - PullNever: try to use the daemon to return a `local.Image`. + // - PullIfNotPResent: try look to use the daemon to return a `local.Image`, if none is found fetch a remote image. + // - PullAlways: it will only try to fetch a remote image. + // + // These PullPolicies that these interact with the daemon argument. + // PullIfNotPresent and daemon = false, gives us the same behavior as PullAlways. + // There is a single invalid configuration, PullNever and daemon = false, this will always fail. + Fetch(ctx context.Context, name string, options image.FetchOptions) (imgutil.Image, error) +} + +//go:generate mockgen -package testmocks -destination ../testmocks/mock_blob_downloader.go github.com/buildpacks/pack/pkg/client BlobDownloader + +// BlobDownloader is an interface for collecting both remote and local assets as blobs. +type BlobDownloader interface { + // Download collects both local and remote assets and provides a blob object + // used to read asset contents. + Download(ctx context.Context, pathOrURI string) (blob.Blob, error) +} + +//go:generate mockgen -package testmocks -destination ../testmocks/mock_image_factory.go github.com/buildpacks/pack/pkg/client ImageFactory + +// ImageFactory is an interface representing the ability to create a new OCI image. +type ImageFactory interface { + // NewImage initializes an image object with required settings so that it + // can be written either locally or to a registry. + NewImage(repoName string, local bool, imageOS string) (imgutil.Image, error) +} + +//go:generate mockgen -package testmocks -destination ../testmocks/mock_buildpack_downloader.go github.com/buildpacks/pack/pkg/client BuildpackDownloader + +// BuildpackDownloader is an interface for downloading and extracting buildpacks from various sources +type BuildpackDownloader interface { + // Download parses a buildpack URI and downloads the buildpack and any dependencies buildpacks from the appropriate source + Download(ctx context.Context, buildpackURI string, opts buildpack.DownloadOptions) (buildpack.Buildpack, []buildpack.Buildpack, error) +} + +// Client is an orchestration object, it contains all parameters needed to +// build an app image using Cloud Native Buildpacks. +// All settings on this object should be changed through ClientOption functions. +type Client struct { + logger logging.Logger + docker dockerClient.CommonAPIClient + + keychain authn.Keychain + imageFactory ImageFactory + imageFetcher ImageFetcher + downloader BlobDownloader + lifecycleExecutor LifecycleExecutor + buildpackDownloader BuildpackDownloader + + experimental bool + registryMirrors map[string]string + version string +} + +// Option is a type of function that mutate settings on the client. +// Values in these functions are set through currying. +type Option func(c *Client) + +// WithLogger supply your own logger. +func WithLogger(l logging.Logger) Option { + return func(c *Client) { + c.logger = l + } +} + +// WithImageFactory supply your own image factory. +func WithImageFactory(f ImageFactory) Option { + return func(c *Client) { + c.imageFactory = f + } +} + +// WithFetcher supply your own Fetcher. +// A Fetcher retrieves both local and remote images to make them available. +func WithFetcher(f ImageFetcher) Option { + return func(c *Client) { + c.imageFetcher = f + } +} + +// WithDownloader supply your own downloader. +// A Downloader is used to gather buildpacks from both remote urls, or local sources. +func WithDownloader(d BlobDownloader) Option { + return func(c *Client) { + c.downloader = d + } +} + +// WithBuildpackDownloader supply your own BuildpackDownloader. +// A BuildpackDownloader is used to gather buildpacks from both remote urls, or local sources. +func WithBuildpackDownloader(d BuildpackDownloader) Option { + return func(c *Client) { + c.buildpackDownloader = d + } +} + +// Deprecated: use WithDownloader instead. +// +// WithCacheDir supply your own cache directory. +func WithCacheDir(path string) Option { + return func(c *Client) { + c.downloader = blob.NewDownloader(c.logger, path) + } +} + +// WithDockerClient supply your own docker client. +func WithDockerClient(docker dockerClient.CommonAPIClient) Option { + return func(c *Client) { + c.docker = docker + } +} + +// WithExperimental sets whether experimental features should be enabled. +func WithExperimental(experimental bool) Option { + return func(c *Client) { + c.experimental = experimental + } +} + +// WithRegistryMirrors sets mirrors to pull images from. +func WithRegistryMirrors(registryMirrors map[string]string) Option { + return func(c *Client) { + c.registryMirrors = registryMirrors + } +} + +// WithKeychain sets keychain of credentials to image registries +func WithKeychain(keychain authn.Keychain) Option { + return func(c *Client) { + c.keychain = keychain + } +} + +const DockerAPIVersion = "1.38" + +// NewClient allocates and returns a Client configured with the specified options. +func NewClient(opts ...Option) (*Client, error) { + client := &Client{ + version: pack.Version, + keychain: authn.DefaultKeychain, + } + + for _, opt := range opts { + opt(client) + } + + if client.logger == nil { + client.logger = logging.NewSimpleLogger(os.Stderr) + } + + if client.docker == nil { + var err error + client.docker, err = dockerClient.NewClientWithOpts( + dockerClient.FromEnv, + dockerClient.WithVersion(DockerAPIVersion), + ) + if err != nil { + return nil, errors.Wrap(err, "creating docker client") + } + } + + if client.downloader == nil { + packHome, err := iconfig.PackHome() + if err != nil { + return nil, errors.Wrap(err, "getting pack home") + } + client.downloader = blob.NewDownloader(client.logger, filepath.Join(packHome, "download-cache")) + } + + if client.imageFetcher == nil { + client.imageFetcher = image.NewFetcher(client.logger, client.docker, image.WithRegistryMirrors(client.registryMirrors)) + } + + if client.imageFactory == nil { + client.imageFactory = &imageFactory{ + dockerClient: client.docker, + keychain: client.keychain, + } + } + + if client.buildpackDownloader == nil { + client.buildpackDownloader = buildpack.NewDownloader( + client.logger, + client.imageFetcher, + client.downloader, + ®istryResolver{ + logger: client.logger, + }, + ) + } + + client.lifecycleExecutor = build.NewLifecycleExecutor(client.logger, client.docker) + + return client, nil +} + +type registryResolver struct { + logger logging.Logger +} + +func (r *registryResolver) Resolve(registryName, bpName string) (string, error) { + cache, err := getRegistry(r.logger, registryName) + if err != nil { + return "", errors.Wrapf(err, "lookup registry %s", style.Symbol(registryName)) + } + + regBuildpack, err := cache.LocateBuildpack(bpName) + if err != nil { + return "", errors.Wrapf(err, "lookup buildpack %s", style.Symbol(bpName)) + } + + return regBuildpack.Address, nil +} + +type imageFactory struct { + dockerClient dockerClient.CommonAPIClient + keychain authn.Keychain +} + +func (f *imageFactory) NewImage(repoName string, daemon bool, imageOS string) (imgutil.Image, error) { + platform := imgutil.Platform{OS: imageOS} + + if daemon { + return local.NewImage(repoName, f.dockerClient, local.WithDefaultPlatform(platform)) + } + + return remote.NewImage(repoName, f.keychain, remote.WithDefaultPlatform(platform)) +} diff --git a/vendor/github.com/buildpacks/pack/pkg/client/common.go b/vendor/github.com/buildpacks/pack/pkg/client/common.go new file mode 100644 index 0000000000..b6e0e8ec29 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/client/common.go @@ -0,0 +1,131 @@ +package client + +import ( + "errors" + "fmt" + + "github.com/google/go-containerregistry/pkg/name" + + "github.com/buildpacks/pack/internal/builder" + "github.com/buildpacks/pack/internal/config" + "github.com/buildpacks/pack/internal/registry" + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/logging" +) + +func (c *Client) parseTagReference(imageName string) (name.Reference, error) { + if imageName == "" { + return nil, errors.New("image is a required parameter") + } + if _, err := name.ParseReference(imageName, name.WeakValidation); err != nil { + return nil, err + } + ref, err := name.NewTag(imageName, name.WeakValidation) + if err != nil { + return nil, fmt.Errorf("'%s' is not a tag reference", imageName) + } + + return ref, nil +} + +func (c *Client) resolveRunImage(runImage, imgRegistry, bldrRegistry string, stackInfo builder.StackMetadata, additionalMirrors map[string][]string, publish bool) string { + if runImage != "" { + c.logger.Debugf("Using provided run-image %s", style.Symbol(runImage)) + return runImage + } + + preferredRegistry := bldrRegistry + if publish || bldrRegistry == "" { + preferredRegistry = imgRegistry + } + + runImageName := getBestRunMirror( + preferredRegistry, + stackInfo.RunImage.Image, + stackInfo.RunImage.Mirrors, + additionalMirrors[stackInfo.RunImage.Image], + ) + + switch { + case runImageName == stackInfo.RunImage.Image: + c.logger.Debugf("Selected run image %s", style.Symbol(runImageName)) + case contains(stackInfo.RunImage.Mirrors, runImageName): + c.logger.Debugf("Selected run image mirror %s", style.Symbol(runImageName)) + default: + c.logger.Debugf("Selected run image mirror %s from local config", style.Symbol(runImageName)) + } + return runImageName +} + +func getRegistry(logger logging.Logger, registryName string) (registry.Cache, error) { + home, err := config.PackHome() + if err != nil { + return registry.Cache{}, err + } + + if err := config.MkdirAll(home); err != nil { + return registry.Cache{}, err + } + + cfg, err := getConfig() + if err != nil { + return registry.Cache{}, err + } + + if registryName == "" { + return registry.NewDefaultRegistryCache(logger, home) + } + + for _, reg := range config.GetRegistries(cfg) { + if reg.Name == registryName { + return registry.NewRegistryCache(logger, home, reg.URL) + } + } + + return registry.Cache{}, fmt.Errorf("registry %s is not defined in your config file", style.Symbol(registryName)) +} + +func getConfig() (config.Config, error) { + path, err := config.DefaultConfigPath() + if err != nil { + return config.Config{}, err + } + + cfg, err := config.Read(path) + if err != nil { + return config.Config{}, err + } + return cfg, nil +} + +func contains(slc []string, v string) bool { + for _, s := range slc { + if s == v { + return true + } + } + return false +} + +func getBestRunMirror(registry string, runImage string, mirrors []string, preferredMirrors []string) string { + var runImageList []string + runImageList = append(runImageList, preferredMirrors...) + runImageList = append(runImageList, runImage) + runImageList = append(runImageList, mirrors...) + + for _, img := range runImageList { + ref, err := name.ParseReference(img, name.WeakValidation) + if err != nil { + continue + } + if ref.Context().RegistryStr() == registry { + return img + } + } + + if len(preferredMirrors) > 0 { + return preferredMirrors[0] + } + + return runImage +} diff --git a/vendor/github.com/buildpacks/pack/pkg/client/create_builder.go b/vendor/github.com/buildpacks/pack/pkg/client/create_builder.go new file mode 100644 index 0000000000..34bca4b5ee --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/client/create_builder.go @@ -0,0 +1,272 @@ +package client + +import ( + "context" + "fmt" + + "github.com/Masterminds/semver" + "github.com/buildpacks/imgutil" + "github.com/pkg/errors" + + pubbldr "github.com/buildpacks/pack/builder" + "github.com/buildpacks/pack/internal/builder" + "github.com/buildpacks/pack/internal/paths" + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/buildpack" + "github.com/buildpacks/pack/pkg/image" +) + +// CreateBuilderOptions is a configuration object used to change the behavior of +// CreateBuilder. +type CreateBuilderOptions struct { + // The base directory to use to resolve relative assets + RelativeBaseDir string + + // Name of the builder. + BuilderName string + + // Configuration that defines the functionality a builder provides. + Config pubbldr.Config + + // Skip building image locally, directly publish to a registry. + // Requires BuilderName to be a valid registry location. + Publish bool + + // Buildpack registry name. Defines where all registry buildpacks will be pulled from. + Registry string + + // Strategy for updating images before a build. + PullPolicy image.PullPolicy +} + +// CreateBuilder creates and saves a builder image to a registry with the provided options. +// If any configuration is invalid, it will error and exit without creating any images. +func (c *Client) CreateBuilder(ctx context.Context, opts CreateBuilderOptions) error { + if err := c.validateConfig(ctx, opts); err != nil { + return err + } + + bldr, err := c.createBaseBuilder(ctx, opts) + if err != nil { + return errors.Wrap(err, "failed to create builder") + } + + if err := c.addBuildpacksToBuilder(ctx, opts, bldr); err != nil { + return errors.Wrap(err, "failed to add buildpacks to builder") + } + + bldr.SetOrder(opts.Config.Order) + bldr.SetStack(opts.Config.Stack) + + return bldr.Save(c.logger, builder.CreatorMetadata{Version: c.version}) +} + +func (c *Client) validateConfig(ctx context.Context, opts CreateBuilderOptions) error { + if err := pubbldr.ValidateConfig(opts.Config); err != nil { + return errors.Wrap(err, "invalid builder config") + } + + if err := c.validateRunImageConfig(ctx, opts); err != nil { + return errors.Wrap(err, "invalid run image config") + } + + return nil +} + +func (c *Client) validateRunImageConfig(ctx context.Context, opts CreateBuilderOptions) error { + var runImages []imgutil.Image + for _, i := range append([]string{opts.Config.Stack.RunImage}, opts.Config.Stack.RunImageMirrors...) { + if !opts.Publish { + img, err := c.imageFetcher.Fetch(ctx, i, image.FetchOptions{Daemon: true, PullPolicy: opts.PullPolicy}) + if err != nil { + if errors.Cause(err) != image.ErrNotFound { + return errors.Wrap(err, "failed to fetch image") + } + } else { + runImages = append(runImages, img) + continue + } + } + + img, err := c.imageFetcher.Fetch(ctx, i, image.FetchOptions{Daemon: false, PullPolicy: opts.PullPolicy}) + if err != nil { + if errors.Cause(err) != image.ErrNotFound { + return errors.Wrap(err, "failed to fetch image") + } + c.logger.Warnf("run image %s is not accessible", style.Symbol(i)) + } else { + runImages = append(runImages, img) + } + } + + for _, img := range runImages { + stackID, err := img.Label("io.buildpacks.stack.id") + if err != nil { + return errors.Wrap(err, "failed to label image") + } + + if stackID != opts.Config.Stack.ID { + return fmt.Errorf( + "stack %s from builder config is incompatible with stack %s from run image %s", + style.Symbol(opts.Config.Stack.ID), + style.Symbol(stackID), + style.Symbol(img.Name()), + ) + } + } + + return nil +} + +func (c *Client) createBaseBuilder(ctx context.Context, opts CreateBuilderOptions) (*builder.Builder, error) { + baseImage, err := c.imageFetcher.Fetch(ctx, opts.Config.Stack.BuildImage, image.FetchOptions{Daemon: !opts.Publish, PullPolicy: opts.PullPolicy}) + if err != nil { + return nil, errors.Wrap(err, "fetch build image") + } + + c.logger.Debugf("Creating builder %s from build-image %s", style.Symbol(opts.BuilderName), style.Symbol(baseImage.Name())) + bldr, err := builder.New(baseImage, opts.BuilderName) + if err != nil { + return nil, errors.Wrap(err, "invalid build-image") + } + + os, err := baseImage.OS() + if err != nil { + return nil, errors.Wrap(err, "lookup image OS") + } + + if os == "windows" && !c.experimental { + return nil, NewExperimentError("Windows containers support is currently experimental.") + } + + bldr.SetDescription(opts.Config.Description) + + if bldr.StackID != opts.Config.Stack.ID { + return nil, fmt.Errorf( + "stack %s from builder config is incompatible with stack %s from build image", + style.Symbol(opts.Config.Stack.ID), + style.Symbol(bldr.StackID), + ) + } + + lifecycle, err := c.fetchLifecycle(ctx, opts.Config.Lifecycle, opts.RelativeBaseDir, os) + if err != nil { + return nil, errors.Wrap(err, "fetch lifecycle") + } + + bldr.SetLifecycle(lifecycle) + + return bldr, nil +} + +func (c *Client) fetchLifecycle(ctx context.Context, config pubbldr.LifecycleConfig, relativeBaseDir, os string) (builder.Lifecycle, error) { + if config.Version != "" && config.URI != "" { + return nil, errors.Errorf( + "%s can only declare %s or %s, not both", + style.Symbol("lifecycle"), style.Symbol("version"), style.Symbol("uri"), + ) + } + + var uri string + var err error + switch { + case config.Version != "": + v, err := semver.NewVersion(config.Version) + if err != nil { + return nil, errors.Wrapf(err, "%s must be a valid semver", style.Symbol("lifecycle.version")) + } + + uri = uriFromLifecycleVersion(*v, os) + case config.URI != "": + uri, err = paths.FilePathToURI(config.URI, relativeBaseDir) + if err != nil { + return nil, err + } + default: + uri = uriFromLifecycleVersion(*semver.MustParse(builder.DefaultLifecycleVersion), os) + } + + blob, err := c.downloader.Download(ctx, uri) + if err != nil { + return nil, errors.Wrap(err, "downloading lifecycle") + } + + lifecycle, err := builder.NewLifecycle(blob) + if err != nil { + return nil, errors.Wrap(err, "invalid lifecycle") + } + + return lifecycle, nil +} + +func (c *Client) addBuildpacksToBuilder(ctx context.Context, opts CreateBuilderOptions, bldr *builder.Builder) error { + for _, b := range opts.Config.Buildpacks { + c.logger.Debugf("Looking up buildpack %s", style.Symbol(b.DisplayString())) + + imageOS, err := bldr.Image().OS() + if err != nil { + return errors.Wrapf(err, "getting OS from %s", style.Symbol(bldr.Image().Name())) + } + + mainBP, depBPs, err := c.buildpackDownloader.Download(ctx, b.URI, buildpack.DownloadOptions{ + RegistryName: opts.Registry, + ImageOS: imageOS, + RelativeBaseDir: opts.RelativeBaseDir, + Daemon: !opts.Publish, + PullPolicy: opts.PullPolicy, + ImageName: b.ImageName, + }) + if err != nil { + return errors.Wrap(err, "downloading buildpack") + } + + err = validateBuildpack(mainBP, b.URI, b.ID, b.Version) + if err != nil { + return errors.Wrap(err, "invalid buildpack") + } + + bpDesc := mainBP.Descriptor() + for _, deprecatedAPI := range bldr.LifecycleDescriptor().APIs.Buildpack.Deprecated { + if deprecatedAPI.Equal(bpDesc.API) { + c.logger.Warnf("Buildpack %s is using deprecated Buildpacks API version %s", style.Symbol(bpDesc.Info.FullName()), style.Symbol(bpDesc.API.String())) + break + } + } + + for _, bp := range append([]buildpack.Buildpack{mainBP}, depBPs...) { + bldr.AddBuildpack(bp) + } + } + + return nil +} + +func validateBuildpack(bp buildpack.Buildpack, source, expectedID, expectedBPVersion string) error { + if expectedID != "" && bp.Descriptor().Info.ID != expectedID { + return fmt.Errorf( + "buildpack from URI %s has ID %s which does not match ID %s from builder config", + style.Symbol(source), + style.Symbol(bp.Descriptor().Info.ID), + style.Symbol(expectedID), + ) + } + + if expectedBPVersion != "" && bp.Descriptor().Info.Version != expectedBPVersion { + return fmt.Errorf( + "buildpack from URI %s has version %s which does not match version %s from builder config", + style.Symbol(source), + style.Symbol(bp.Descriptor().Info.Version), + style.Symbol(expectedBPVersion), + ) + } + + return nil +} + +func uriFromLifecycleVersion(version semver.Version, os string) string { + if os == "windows" { + return fmt.Sprintf("https://github.com/buildpacks/lifecycle/releases/download/v%s/lifecycle-v%s+windows.x86-64.tgz", version.String(), version.String()) + } + + return fmt.Sprintf("https://github.com/buildpacks/lifecycle/releases/download/v%s/lifecycle-v%s+linux.x86-64.tgz", version.String(), version.String()) +} diff --git a/vendor/github.com/buildpacks/pack/pkg/client/download_sbom.go b/vendor/github.com/buildpacks/pack/pkg/client/download_sbom.go new file mode 100644 index 0000000000..26a4a8e09a --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/client/download_sbom.go @@ -0,0 +1,63 @@ +package client + +import ( + "context" + + "github.com/buildpacks/lifecycle/layers" + "github.com/buildpacks/lifecycle/platform" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/pkg/dist" + "github.com/buildpacks/pack/pkg/image" +) + +type DownloadSBOMOptions struct { + Daemon bool + DestinationDir string +} + +// Deserialize just the subset of fields we need to avoid breaking changes +type sbomMetadata struct { + BOM *platform.LayerMetadata `json:"sbom" toml:"sbom"` +} + +func (s *sbomMetadata) isMissing() bool { + return s == nil || + s.BOM == nil || + s.BOM.SHA == "" +} + +const ( + Local = iota + Remote +) + +// DownloadSBOM pulls SBOM layer from an image. +// It reads the SBOM metadata of an image then +// pulls the corresponding diffId, if it exists +func (c *Client) DownloadSBOM(name string, options DownloadSBOMOptions) error { + img, err := c.imageFetcher.Fetch(context.Background(), name, image.FetchOptions{Daemon: options.Daemon, PullPolicy: image.PullNever}) + if err != nil { + if errors.Cause(err) == image.ErrNotFound { + return errors.Wrapf(image.ErrNotFound, "image '%s' cannot be found", name) + } + return err + } + + var sbomMD sbomMetadata + if _, err := dist.GetLabel(img, platform.LayerMetadataLabel, &sbomMD); err != nil { + return err + } + + if sbomMD.isMissing() { + return errors.Errorf("could not find SBoM information on '%s'", name) + } + + rc, err := img.GetLayer(sbomMD.BOM.SHA) + if err != nil { + return err + } + defer rc.Close() + + return layers.Extract(rc, options.DestinationDir) +} diff --git a/vendor/github.com/buildpacks/pack/pkg/client/errors.go b/vendor/github.com/buildpacks/pack/pkg/client/errors.go new file mode 100644 index 0000000000..ef4092154c --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/client/errors.go @@ -0,0 +1,25 @@ +package client + +// ExperimentError denotes that an experimental feature was trying to be used without experimental features enabled. +type ExperimentError struct { + msg string +} + +func NewExperimentError(msg string) ExperimentError { + return ExperimentError{msg} +} + +func (ee ExperimentError) Error() string { + return ee.msg +} + +// SoftError is an error that is not intended to be displayed. +type SoftError struct{} + +func NewSoftError() SoftError { + return SoftError{} +} + +func (se SoftError) Error() string { + return "" +} diff --git a/vendor/github.com/buildpacks/pack/pkg/client/inspect_builder.go b/vendor/github.com/buildpacks/pack/pkg/client/inspect_builder.go new file mode 100644 index 0000000000..8a22249f3d --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/client/inspect_builder.go @@ -0,0 +1,106 @@ +package client + +import ( + "errors" + + pubbldr "github.com/buildpacks/pack/builder" + + "github.com/buildpacks/pack/internal/builder" + "github.com/buildpacks/pack/pkg/dist" + "github.com/buildpacks/pack/pkg/image" +) + +// BuilderInfo is a collection of metadata describing a builder created using client. +type BuilderInfo struct { + // Human readable, description of a builder. + Description string + + // Stack name used by the builder. + Stack string + + // List of Stack mixins, this information is provided by Stack variable. + Mixins []string + + // RunImage provided by the builder. + RunImage string + + // List of all run image mirrors a builder will use to provide + // the RunImage. + RunImageMirrors []string + + // All buildpacks included within the builder. + Buildpacks []dist.BuildpackInfo + + // Detailed ordering of buildpacks and nested buildpacks where depth is specified. + Order pubbldr.DetectionOrder + + // Listing of all buildpack layers in a builder. + // All elements in the Buildpacks variable are represented in this + // object. + BuildpackLayers dist.BuildpackLayers + + // Lifecycle provides the following API versioning information for a builder: + // - Lifecycle Version used in this builder, + // - Platform API, + // - Buildpack API. + Lifecycle builder.LifecycleDescriptor + + // Name and Version information from tooling used + // to produce this builder. + CreatedBy builder.CreatorMetadata +} + +// BuildpackInfoKey contains all information needed to determine buildpack equivalence. +type BuildpackInfoKey struct { + ID string + Version string +} + +type BuilderInspectionConfig struct { + OrderDetectionDepth int +} + +type BuilderInspectionModifier func(config *BuilderInspectionConfig) + +func WithDetectionOrderDepth(depth int) BuilderInspectionModifier { + return func(config *BuilderInspectionConfig) { + config.OrderDetectionDepth = depth + } +} + +// InspectBuilder reads label metadata of a local or remote builder image. It initializes a BuilderInfo +// object with this metadata, and returns it. This method will error if the name image cannot be found +// both locally and remotely, or if the found image does not contain the proper labels. +func (c *Client) InspectBuilder(name string, daemon bool, modifiers ...BuilderInspectionModifier) (*BuilderInfo, error) { + inspector := builder.NewInspector( + builder.NewImageFetcherWrapper(c.imageFetcher), + builder.NewLabelManagerProvider(), + builder.NewDetectionOrderCalculator(), + ) + + inspectionConfig := BuilderInspectionConfig{OrderDetectionDepth: pubbldr.OrderDetectionNone} + for _, mod := range modifiers { + mod(&inspectionConfig) + } + + info, err := inspector.Inspect(name, daemon, inspectionConfig.OrderDetectionDepth) + if err != nil { + if errors.Is(err, image.ErrNotFound) { + return nil, nil + } + return nil, err + } + + return &BuilderInfo{ + Description: info.Description, + Stack: info.StackID, + Mixins: info.Mixins, + RunImage: info.RunImage, + RunImageMirrors: info.RunImageMirrors, + Buildpacks: info.Buildpacks, + Order: info.Order, + BuildpackLayers: info.BuildpackLayers, + Lifecycle: info.Lifecycle, + CreatedBy: info.CreatedBy, + }, nil +} diff --git a/vendor/github.com/buildpacks/pack/pkg/client/inspect_buildpack.go b/vendor/github.com/buildpacks/pack/pkg/client/inspect_buildpack.go new file mode 100644 index 0000000000..84f89ce813 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/client/inspect_buildpack.go @@ -0,0 +1,167 @@ +package client + +import ( + "context" + "fmt" + "sort" + + v1 "github.com/opencontainers/image-spec/specs-go/v1" + + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/buildpack" + "github.com/buildpacks/pack/pkg/dist" + "github.com/buildpacks/pack/pkg/image" +) + +type BuildpackInfo struct { + BuildpackMetadata buildpack.Metadata + Buildpacks []dist.BuildpackInfo + Order dist.Order + BuildpackLayers dist.BuildpackLayers + Location buildpack.LocatorType +} + +type InspectBuildpackOptions struct { + BuildpackName string + Daemon bool + Registry string +} + +type ImgWrapper struct { + v1.ImageConfig +} + +func (iw ImgWrapper) Label(name string) (string, error) { + return iw.Labels[name], nil +} + +func (c *Client) InspectBuildpack(opts InspectBuildpackOptions) (*BuildpackInfo, error) { + locatorType, err := buildpack.GetLocatorType(opts.BuildpackName, "", []dist.BuildpackInfo{}) + if err != nil { + return nil, err + } + var layersMd dist.BuildpackLayers + var buildpackMd buildpack.Metadata + + switch locatorType { + case buildpack.RegistryLocator: + buildpackMd, layersMd, err = metadataFromRegistry(c, opts.BuildpackName, opts.Registry) + case buildpack.PackageLocator: + buildpackMd, layersMd, err = metadataFromImage(c, opts.BuildpackName, opts.Daemon) + case buildpack.URILocator: + buildpackMd, layersMd, err = metadataFromArchive(c.downloader, opts.BuildpackName) + default: + return nil, fmt.Errorf("unable to handle locator %q: for buildpack %q", locatorType, opts.BuildpackName) + } + if err != nil { + return nil, err + } + + return &BuildpackInfo{ + BuildpackMetadata: buildpackMd, + BuildpackLayers: layersMd, + Order: extractOrder(buildpackMd), + Buildpacks: extractBuildpacks(layersMd), + Location: locatorType, + }, nil +} + +func metadataFromRegistry(client *Client, name, registry string) (buildpackMd buildpack.Metadata, layersMd dist.BuildpackLayers, err error) { + registryCache, err := getRegistry(client.logger, registry) + if err != nil { + return buildpack.Metadata{}, dist.BuildpackLayers{}, fmt.Errorf("invalid registry %s: %q", registry, err) + } + + registryBp, err := registryCache.LocateBuildpack(name) + if err != nil { + return buildpack.Metadata{}, dist.BuildpackLayers{}, fmt.Errorf("unable to find %s in registry: %q", style.Symbol(name), err) + } + buildpackMd, layersMd, err = metadataFromImage(client, registryBp.Address, false) + if err != nil { + return buildpack.Metadata{}, dist.BuildpackLayers{}, fmt.Errorf("error pulling registry specified image: %s", err) + } + return buildpackMd, layersMd, nil +} + +func metadataFromArchive(downloader BlobDownloader, path string) (buildpackMd buildpack.Metadata, layersMd dist.BuildpackLayers, err error) { + imgBlob, err := downloader.Download(context.Background(), path) + if err != nil { + return buildpack.Metadata{}, dist.BuildpackLayers{}, fmt.Errorf("unable to download archive: %q", err) + } + + config, err := buildpack.ConfigFromOCILayoutBlob(imgBlob) + if err != nil { + return buildpack.Metadata{}, dist.BuildpackLayers{}, fmt.Errorf("unable to fetch config from buildpack blob: %q", err) + } + wrapper := ImgWrapper{config} + + if _, err := dist.GetLabel(wrapper, dist.BuildpackLayersLabel, &layersMd); err != nil { + return buildpack.Metadata{}, dist.BuildpackLayers{}, err + } + + if _, err := dist.GetLabel(wrapper, buildpack.MetadataLabel, &buildpackMd); err != nil { + return buildpack.Metadata{}, dist.BuildpackLayers{}, err + } + return buildpackMd, layersMd, nil +} + +func metadataFromImage(client *Client, name string, daemon bool) (buildpackMd buildpack.Metadata, layersMd dist.BuildpackLayers, err error) { + imageName := buildpack.ParsePackageLocator(name) + img, err := client.imageFetcher.Fetch(context.Background(), imageName, image.FetchOptions{Daemon: daemon, PullPolicy: image.PullNever}) + if err != nil { + return buildpack.Metadata{}, dist.BuildpackLayers{}, err + } + if _, err := dist.GetLabel(img, dist.BuildpackLayersLabel, &layersMd); err != nil { + return buildpack.Metadata{}, dist.BuildpackLayers{}, fmt.Errorf("unable to get image label %s: %q", dist.BuildpackLayersLabel, err) + } + + if _, err := dist.GetLabel(img, buildpack.MetadataLabel, &buildpackMd); err != nil { + return buildpack.Metadata{}, dist.BuildpackLayers{}, fmt.Errorf("unable to get image label %s: %q", buildpack.MetadataLabel, err) + } + return buildpackMd, layersMd, nil +} + +func extractOrder(buildpackMd buildpack.Metadata) dist.Order { + return dist.Order{ + { + Group: []dist.BuildpackRef{ + { + BuildpackInfo: buildpackMd.BuildpackInfo, + }, + }, + }, + } +} + +func extractBuildpacks(layersMd dist.BuildpackLayers) []dist.BuildpackInfo { + result := []dist.BuildpackInfo{} + buildpackSet := map[*dist.BuildpackInfo]bool{} + + for buildpackID, buildpackMap := range layersMd { + for version, layerInfo := range buildpackMap { + bp := dist.BuildpackInfo{ + ID: buildpackID, + Name: layerInfo.Name, + Version: version, + Homepage: layerInfo.Homepage, + } + buildpackSet[&bp] = true + } + } + + for currentBuildpack := range buildpackSet { + result = append(result, *currentBuildpack) + } + + sort.Slice(result, func(i int, j int) bool { + switch { + case result[i].ID < result[j].ID: + return true + case result[i].ID == result[j].ID: + return result[i].Version < result[j].Version + default: + return false + } + }) + return result +} diff --git a/vendor/github.com/buildpacks/pack/pkg/client/inspect_image.go b/vendor/github.com/buildpacks/pack/pkg/client/inspect_image.go new file mode 100644 index 0000000000..c881dc20a3 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/client/inspect_image.go @@ -0,0 +1,175 @@ +package client + +import ( + "context" + "strings" + + "github.com/Masterminds/semver" + "github.com/buildpacks/lifecycle/buildpack" + "github.com/buildpacks/lifecycle/launch" + "github.com/buildpacks/lifecycle/platform" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/pkg/dist" + "github.com/buildpacks/pack/pkg/image" +) + +// ImageInfo is a collection of metadata describing +// an app image built using Cloud Native Buildpacks. +type ImageInfo struct { + // Stack Identifier used when building this image + StackID string + + // List of buildpacks that passed detection, ran their build + // phases and made a contribution to this image. + Buildpacks []buildpack.GroupBuildpack + + // Base includes two references to the run image, + // - the Run Image ID, + // - the hash of the last layer in the app image that belongs to the run image. + // A way to visualize this is given an image with n layers: + // + // last layer in run image + // v + // [1, ..., k, k+1, ..., n] + // ^ + // first layer added by buildpacks + // + // the first 1 to k layers all belong to the run image, + // the last k+1 to n layers are added by buildpacks. + // the sum of all of these is our app image. + Base platform.RunImageMetadata + + // BOM or Bill of materials, contains dependency and + // version information provided by each buildpack. + BOM []buildpack.BOMEntry + + // Stack includes the run image name, and a list of image mirrors, + // where the run image is hosted. + Stack platform.StackMetadata + + // Processes lists all processes contributed by buildpacks. + Processes ProcessDetails +} + +// ProcessDetails is a collection of all start command metadata +// on an image. +type ProcessDetails struct { + // An Images default start command. + DefaultProcess *launch.Process + + // List of all start commands contributed by buildpacks. + OtherProcesses []launch.Process +} + +// Deserialize just the subset of fields we need to avoid breaking changes +type layersMetadata struct { + RunImage platform.RunImageMetadata `json:"runImage" toml:"run-image"` + Stack platform.StackMetadata `json:"stack" toml:"stack"` +} + +const ( + platformAPIEnv = "CNB_PLATFORM_API" + cnbProcessEnv = "CNB_PROCESS_TYPE" + launcherEntrypoint = "/cnb/lifecycle/launcher" + windowsLauncherEntrypoint = `c:\cnb\lifecycle\launcher.exe` + entrypointPrefix = "/cnb/process/" + windowsEntrypointPrefix = `c:\cnb\process\` + defaultProcess = "web" + fallbackPlatformAPI = "0.3" + windowsPrefix = "c:" +) + +// InspectImage reads the Label metadata of an image. It initializes a ImageInfo object +// using this metadata, and returns it. +// If daemon is true, first the local registry will be searched for the image. +// Otherwise it assumes the image is remote. +func (c *Client) InspectImage(name string, daemon bool) (*ImageInfo, error) { + img, err := c.imageFetcher.Fetch(context.Background(), name, image.FetchOptions{Daemon: daemon, PullPolicy: image.PullNever}) + if err != nil { + if errors.Cause(err) == image.ErrNotFound { + return nil, nil + } + return nil, err + } + + var layersMd layersMetadata + if _, err := dist.GetLabel(img, platform.LayerMetadataLabel, &layersMd); err != nil { + return nil, err + } + + var buildMD platform.BuildMetadata + if _, err := dist.GetLabel(img, platform.BuildMetadataLabel, &buildMD); err != nil { + return nil, err + } + + minimumBaseImageReferenceVersion := semver.MustParse("0.5.0") + actualLauncherVersion, err := semver.NewVersion(buildMD.Launcher.Version) + + if err == nil && actualLauncherVersion.LessThan(minimumBaseImageReferenceVersion) { + layersMd.RunImage.Reference = "" + } + + stackID, err := img.Label(platform.StackIDLabel) + if err != nil { + return nil, err + } + + platformAPI, err := img.Env(platformAPIEnv) + if err != nil { + return nil, errors.Wrap(err, "reading platform api") + } + + if platformAPI == "" { + platformAPI = fallbackPlatformAPI + } + + platformAPIVersion, err := semver.NewVersion(platformAPI) + if err != nil { + return nil, errors.Wrap(err, "parsing platform api version") + } + + var defaultProcessType string + if platformAPIVersion.LessThan(semver.MustParse("0.4")) { + defaultProcessType, err = img.Env(cnbProcessEnv) + if err != nil || defaultProcessType == "" { + defaultProcessType = defaultProcess + } + } else { + entrypoint, err := img.Entrypoint() + if err != nil { + return nil, errors.Wrap(err, "reading entrypoint") + } + + if len(entrypoint) > 0 && entrypoint[0] != launcherEntrypoint && entrypoint[0] != windowsLauncherEntrypoint { + process := entrypoint[0] + if strings.HasPrefix(process, windowsPrefix) { + process = strings.TrimPrefix(process, windowsEntrypointPrefix) + process = strings.TrimSuffix(process, ".exe") // Trim .exe for Windows support + } else { + process = strings.TrimPrefix(process, entrypointPrefix) + } + + defaultProcessType = process + } + } + + var processDetails ProcessDetails + for _, proc := range buildMD.Processes { + proc := proc + if proc.Type == defaultProcessType { + processDetails.DefaultProcess = &proc + continue + } + processDetails.OtherProcesses = append(processDetails.OtherProcesses, proc) + } + + return &ImageInfo{ + StackID: stackID, + Stack: layersMd.Stack, + Base: layersMd.RunImage, + BOM: buildMD.BOM, + Buildpacks: buildMD.Buildpacks, + Processes: processDetails, + }, nil +} diff --git a/vendor/github.com/buildpacks/pack/pkg/client/new_buildpack.go b/vendor/github.com/buildpacks/pack/pkg/client/new_buildpack.go new file mode 100644 index 0000000000..df85eb0168 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/client/new_buildpack.go @@ -0,0 +1,137 @@ +package client + +import ( + "context" + "io/ioutil" + "os" + "path/filepath" + + "github.com/BurntSushi/toml" + + "github.com/buildpacks/lifecycle/api" + + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/dist" +) + +var ( + bashBinBuild = `#!/usr/bin/env bash + +set -euo pipefail + +layers_dir="$1" +env_dir="$2/env" +plan_path="$3" + +exit 0 +` + bashBinDetect = `#!/usr/bin/env bash + +exit 0 +` +) + +type NewBuildpackOptions struct { + // api compat version of the output buildpack artifact. + API string + + // The base directory to generate assets + Path string + + // The ID of the output buildpack artifact. + ID string + + // version of the output buildpack artifact. + Version string + + // The stacks this buildpack will work with + Stacks []dist.Stack +} + +func (c *Client) NewBuildpack(ctx context.Context, opts NewBuildpackOptions) error { + err := createBuildpackTOML(opts.Path, opts.ID, opts.Version, opts.API, opts.Stacks, c) + if err != nil { + return err + } + return createBashBuildpack(opts.Path, c) +} + +func createBashBuildpack(path string, c *Client) error { + if err := createBinScript(path, "build", bashBinBuild, c); err != nil { + return err + } + + if err := createBinScript(path, "detect", bashBinDetect, c); err != nil { + return err + } + + return nil +} + +func createBinScript(path, name, contents string, c *Client) error { + binDir := filepath.Join(path, "bin") + binFile := filepath.Join(binDir, name) + + _, err := os.Stat(binFile) + if os.IsNotExist(err) { + // The following line's comment is for gosec, it will ignore rule 301 in this case + // G301: Expect directory permissions to be 0750 or less + /* #nosec G301 */ + if err := os.MkdirAll(binDir, 0755); err != nil { + return err + } + // The following line's comment is for gosec, it will ignore rule 306 in this case + // G306: Expect WriteFile permissions to be 0600 or less + /* #nosec G306 */ + err = ioutil.WriteFile(binFile, []byte(contents), 0755) + if err != nil { + return err + } + + if c != nil { + c.logger.Infof(" %s bin/%s", style.Symbol("create"), name) + } + } + return nil +} + +func createBuildpackTOML(path, id, version, apiStr string, stacks []dist.Stack, c *Client) error { + api, err := api.NewVersion(apiStr) + if err != nil { + return err + } + + buildpackTOML := dist.BuildpackDescriptor{ + API: api, + Stacks: stacks, + Info: dist.BuildpackInfo{ + ID: id, + Version: version, + }, + } + + // The following line's comment is for gosec, it will ignore rule 301 in this case + // G301: Expect directory permissions to be 0750 or less + /* #nosec G301 */ + if err := os.MkdirAll(path, 0755); err != nil { + return err + } + + buildpackTOMLPath := filepath.Join(path, "buildpack.toml") + _, err = os.Stat(buildpackTOMLPath) + if os.IsNotExist(err) { + f, err := os.Create(buildpackTOMLPath) + if err != nil { + return err + } + if err := toml.NewEncoder(f).Encode(buildpackTOML); err != nil { + return err + } + defer f.Close() + if c != nil { + c.logger.Infof(" %s buildpack.toml", style.Symbol("create")) + } + } + + return nil +} diff --git a/vendor/github.com/buildpacks/pack/pkg/client/package_buildpack.go b/vendor/github.com/buildpacks/pack/pkg/client/package_buildpack.go new file mode 100644 index 0000000000..c26d27623c --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/client/package_buildpack.go @@ -0,0 +1,157 @@ +package client + +import ( + "context" + + "github.com/pkg/errors" + + pubbldpkg "github.com/buildpacks/pack/buildpackage" + "github.com/buildpacks/pack/internal/layer" + "github.com/buildpacks/pack/internal/paths" + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/blob" + "github.com/buildpacks/pack/pkg/buildpack" + "github.com/buildpacks/pack/pkg/image" +) + +const ( + // Packaging indicator that format of inputs/outputs will be an OCI image on the registry. + FormatImage = "image" + + // Packaging indicator that format of output will be a file on the host filesystem. + FormatFile = "file" + + // CNBExtension is the file extension for a cloud native buildpack tar archive + CNBExtension = ".cnb" +) + +// PackageBuildpackOptions is a configuration object used to define +// the behavior of PackageBuildpack. +type PackageBuildpackOptions struct { + // The base director to resolve relative assest from + RelativeBaseDir string + + // The name of the output buildpack artifact. + Name string + + // Type of output format, The options are the either the const FormatImage, or FormatFile. + Format string + + // Defines the Buildpacks configuration. + Config pubbldpkg.Config + + // Push resulting builder image up to a registry + // specified in the Name variable. + Publish bool + + // Strategy for updating images before packaging. + PullPolicy image.PullPolicy + + // Name of the buildpack registry. Used to + // add buildpacks to a package. + Registry string +} + +// PackageBuildpack packages buildpack(s) into either an image or file. +func (c *Client) PackageBuildpack(ctx context.Context, opts PackageBuildpackOptions) error { + if opts.Format == "" { + opts.Format = FormatImage + } + + if opts.Config.Platform.OS == "windows" && !c.experimental { + return NewExperimentError("Windows buildpackage support is currently experimental.") + } + + err := c.validateOSPlatform(ctx, opts.Config.Platform.OS, opts.Publish, opts.Format) + if err != nil { + return err + } + + writerFactory, err := layer.NewWriterFactory(opts.Config.Platform.OS) + if err != nil { + return errors.Wrap(err, "creating layer writer factory") + } + + packageBuilder := buildpack.NewBuilder(c.imageFactory) + + bpURI := opts.Config.Buildpack.URI + if bpURI == "" { + return errors.New("buildpack URI must be provided") + } + + mainBlob, err := c.downloadBuildpackFromURI(ctx, bpURI, opts.RelativeBaseDir) + if err != nil { + return err + } + + bp, err := buildpack.FromRootBlob(mainBlob, writerFactory) + if err != nil { + return errors.Wrapf(err, "creating buildpack from %s", style.Symbol(bpURI)) + } + + packageBuilder.SetBuildpack(bp) + + for _, dep := range opts.Config.Dependencies { + var depBPs []buildpack.Buildpack + mainBP, deps, err := c.buildpackDownloader.Download(ctx, dep.URI, buildpack.DownloadOptions{ + RegistryName: opts.Registry, + RelativeBaseDir: opts.RelativeBaseDir, + ImageOS: opts.Config.Platform.OS, + ImageName: dep.ImageName, + Daemon: !opts.Publish, + PullPolicy: opts.PullPolicy, + }) + + if err != nil { + return errors.Wrapf(err, "packaging dependencies (uri=%s,image=%s)", style.Symbol(dep.URI), style.Symbol(dep.ImageName)) + } + + depBPs = append([]buildpack.Buildpack{mainBP}, deps...) + for _, depBP := range depBPs { + packageBuilder.AddDependency(depBP) + } + } + + switch opts.Format { + case FormatFile: + return packageBuilder.SaveAsFile(opts.Name, opts.Config.Platform.OS) + case FormatImage: + _, err = packageBuilder.SaveAsImage(opts.Name, opts.Publish, opts.Config.Platform.OS) + return errors.Wrapf(err, "saving image") + default: + return errors.Errorf("unknown format: %s", style.Symbol(opts.Format)) + } +} + +func (c *Client) downloadBuildpackFromURI(ctx context.Context, uri, relativeBaseDir string) (blob.Blob, error) { + absPath, err := paths.FilePathToURI(uri, relativeBaseDir) + if err != nil { + return nil, errors.Wrapf(err, "making absolute: %s", style.Symbol(uri)) + } + uri = absPath + + c.logger.Debugf("Downloading buildpack from URI: %s", style.Symbol(uri)) + blob, err := c.downloader.Download(ctx, uri) + if err != nil { + return nil, errors.Wrapf(err, "downloading buildpack from %s", style.Symbol(uri)) + } + + return blob, nil +} + +func (c *Client) validateOSPlatform(ctx context.Context, os string, publish bool, format string) error { + if publish || format == FormatFile { + return nil + } + + info, err := c.docker.Info(ctx) + if err != nil { + return err + } + + if info.OSType != os { + return errors.Errorf("invalid %s specified: DOCKER_OS is %s", style.Symbol("platform.os"), style.Symbol(info.OSType)) + } + + return nil +} diff --git a/vendor/github.com/buildpacks/pack/pkg/client/pull_buildpack.go b/vendor/github.com/buildpacks/pack/pkg/client/pull_buildpack.go new file mode 100644 index 0000000000..d3942fbcfb --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/client/pull_buildpack.go @@ -0,0 +1,65 @@ +package client + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/buildpack" + "github.com/buildpacks/pack/pkg/dist" + "github.com/buildpacks/pack/pkg/image" +) + +// PullBuildpackOptions are options available for PullBuildpack +type PullBuildpackOptions struct { + // URI of the buildpack to retrieve. + URI string + // RegistryName to search for buildpacks from. + RegistryName string + // RelativeBaseDir to resolve relative assests from. + RelativeBaseDir string +} + +// PullBuildpack pulls given buildpack to be stored locally +func (c *Client) PullBuildpack(ctx context.Context, opts PullBuildpackOptions) error { + locatorType, err := buildpack.GetLocatorType(opts.URI, "", []dist.BuildpackInfo{}) + if err != nil { + return err + } + + switch locatorType { + case buildpack.PackageLocator: + imageName := buildpack.ParsePackageLocator(opts.URI) + c.logger.Debugf("Pulling buildpack from image: %s", imageName) + + _, err = c.imageFetcher.Fetch(ctx, imageName, image.FetchOptions{Daemon: true, PullPolicy: image.PullAlways}) + if err != nil { + return errors.Wrapf(err, "fetching image %s", style.Symbol(opts.URI)) + } + case buildpack.RegistryLocator: + c.logger.Debugf("Pulling buildpack from registry: %s", style.Symbol(opts.URI)) + registryCache, err := getRegistry(c.logger, opts.RegistryName) + + if err != nil { + return errors.Wrapf(err, "invalid registry '%s'", opts.RegistryName) + } + + registryBp, err := registryCache.LocateBuildpack(opts.URI) + if err != nil { + return errors.Wrapf(err, "locating in registry %s", style.Symbol(opts.URI)) + } + + _, err = c.imageFetcher.Fetch(ctx, registryBp.Address, image.FetchOptions{Daemon: true, PullPolicy: image.PullAlways}) + if err != nil { + return errors.Wrapf(err, "fetching image %s", style.Symbol(opts.URI)) + } + case buildpack.InvalidLocator: + return fmt.Errorf("invalid buildpack URI %s", style.Symbol(opts.URI)) + default: + return fmt.Errorf("unsupported buildpack URI type: %s", style.Symbol(locatorType.String())) + } + + return nil +} diff --git a/vendor/github.com/buildpacks/pack/pkg/client/rebase.go b/vendor/github.com/buildpacks/pack/pkg/client/rebase.go new file mode 100644 index 0000000000..a244c9fc67 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/client/rebase.go @@ -0,0 +1,95 @@ +package client + +import ( + "context" + + "github.com/buildpacks/lifecycle" + "github.com/buildpacks/lifecycle/platform" + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/build" + "github.com/buildpacks/pack/internal/builder" + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/pkg/dist" + "github.com/buildpacks/pack/pkg/image" +) + +// RebaseOptions is a configuration struct that controls image rebase behavior. +type RebaseOptions struct { + // Name of image we wish to rebase. + RepoName string + + // Flag to publish image to remote registry after rebase completion. + Publish bool + + // Strategy for pulling images during rebase. + PullPolicy image.PullPolicy + + // Image to rebase against. This image must have + // the same StackID as the previous run image. + RunImage string + + // A mapping from StackID to an array of mirrors. + // This mapping used only if both RunImage is omitted and Publish is true. + // AdditionalMirrors gives us inputs to recalculate the 'best' run image + // based on the registry we are publishing to. + AdditionalMirrors map[string][]string +} + +// Rebase updates the run image layers in an app image. +// This operation mutates the image specified in opts. +func (c *Client) Rebase(ctx context.Context, opts RebaseOptions) error { + imageRef, err := c.parseTagReference(opts.RepoName) + if err != nil { + return errors.Wrapf(err, "invalid image name '%s'", opts.RepoName) + } + + appImage, err := c.imageFetcher.Fetch(ctx, opts.RepoName, image.FetchOptions{Daemon: !opts.Publish, PullPolicy: opts.PullPolicy}) + if err != nil { + return err + } + + var md platform.LayersMetadataCompat + if ok, err := dist.GetLabel(appImage, platform.LayerMetadataLabel, &md); err != nil { + return err + } else if !ok { + return errors.Errorf("could not find label %s on image", style.Symbol(platform.LayerMetadataLabel)) + } + + runImageName := c.resolveRunImage( + opts.RunImage, + imageRef.Context().RegistryStr(), + "", + builder.StackMetadata{ + RunImage: builder.RunImageMetadata{ + Image: md.Stack.RunImage.Image, + Mirrors: md.Stack.RunImage.Mirrors, + }, + }, + opts.AdditionalMirrors, + opts.Publish) + + if runImageName == "" { + return errors.New("run image must be specified") + } + + baseImage, err := c.imageFetcher.Fetch(ctx, runImageName, image.FetchOptions{Daemon: !opts.Publish, PullPolicy: opts.PullPolicy}) + if err != nil { + return err + } + + c.logger.Infof("Rebasing %s on run image %s", style.Symbol(appImage.Name()), style.Symbol(baseImage.Name())) + rebaser := &lifecycle.Rebaser{Logger: c.logger, PlatformAPI: build.SupportedPlatformAPIVersions.Latest()} + _, err = rebaser.Rebase(appImage, baseImage, nil) + if err != nil { + return err + } + + appImageIdentifier, err := appImage.Identifier() + if err != nil { + return err + } + + c.logger.Infof("Rebased Image: %s", style.Symbol(appImageIdentifier.String())) + return nil +} diff --git a/vendor/github.com/buildpacks/pack/pkg/client/register_buildpack.go b/vendor/github.com/buildpacks/pack/pkg/client/register_buildpack.go new file mode 100644 index 0000000000..1bae8e5d96 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/client/register_buildpack.go @@ -0,0 +1,119 @@ +package client + +import ( + "context" + "errors" + "net/url" + "runtime" + "strings" + + "github.com/buildpacks/pack/internal/registry" + "github.com/buildpacks/pack/pkg/buildpack" + "github.com/buildpacks/pack/pkg/dist" + "github.com/buildpacks/pack/pkg/image" +) + +// RegisterBuildpackOptions is a configuration struct that controls the +// behavior of the RegisterBuildpack function. +type RegisterBuildpackOptions struct { + ImageName string + Type string + URL string + Name string +} + +// RegisterBuildpack updates the Buildpack Registry with to include a new buildpack specified in +// the opts argument +func (c *Client) RegisterBuildpack(ctx context.Context, opts RegisterBuildpackOptions) error { + appImage, err := c.imageFetcher.Fetch(ctx, opts.ImageName, image.FetchOptions{Daemon: false, PullPolicy: image.PullAlways}) + if err != nil { + return err + } + + var buildpackInfo dist.BuildpackInfo + if _, err := dist.GetLabel(appImage, buildpack.MetadataLabel, &buildpackInfo); err != nil { + return err + } + + namespace, name, err := parseID(buildpackInfo.ID) + if err != nil { + return err + } + + id, err := appImage.Identifier() + if err != nil { + return err + } + + buildpack := registry.Buildpack{ + Namespace: namespace, + Name: name, + Version: buildpackInfo.Version, + Address: id.String(), + Yanked: false, + } + + if opts.Type == "github" { + issueURL, err := registry.GetIssueURL(opts.URL) + if err != nil { + return err + } + + issue, err := registry.CreateGithubIssue(buildpack) + if err != nil { + return err + } + + params := url.Values{} + params.Add("title", issue.Title) + params.Add("body", issue.Body) + issueURL.RawQuery = params.Encode() + + c.logger.Debugf("Open URL in browser: %s", issueURL) + cmd, err := registry.CreateBrowserCmd(issueURL.String(), runtime.GOOS) + if err != nil { + return err + } + + return cmd.Start() + } else if opts.Type == "git" { + registryCache, err := getRegistry(c.logger, opts.Name) + if err != nil { + return err + } + + username, err := parseUsernameFromURL(opts.URL) + if err != nil { + return err + } + + if err := registry.GitCommit(buildpack, username, registryCache); err != nil { + return err + } + } + + return nil +} + +func parseUsernameFromURL(url string) (string, error) { + parts := strings.Split(url, "/") + if len(parts) < 3 { + return "", errors.New("invalid url: cannot parse username from url") + } + if parts[3] == "" { + return "", errors.New("invalid url: username is empty") + } + + return parts[3], nil +} + +func parseID(id string) (string, string, error) { + parts := strings.Split(id, "/") + if len(parts) < 2 { + return "", "", errors.New("invalid id: does not contain a namespace") + } else if len(parts) > 2 { + return "", "", errors.New("invalid id: contains unexpected characters") + } + + return parts[0], parts[1], nil +} diff --git a/vendor/github.com/buildpacks/pack/pkg/client/version.go b/vendor/github.com/buildpacks/pack/pkg/client/version.go new file mode 100644 index 0000000000..a3533e8866 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/client/version.go @@ -0,0 +1,6 @@ +package client + +// Version returns the version of the client +func (c *Client) Version() string { + return c.version +} diff --git a/vendor/github.com/buildpacks/pack/pkg/client/yank_buildpack.go b/vendor/github.com/buildpacks/pack/pkg/client/yank_buildpack.go new file mode 100644 index 0000000000..173f22902c --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/client/yank_buildpack.go @@ -0,0 +1,56 @@ +package client + +import ( + "net/url" + "runtime" + + "github.com/buildpacks/pack/internal/registry" +) + +// YankBuildpackOptions is a configuration struct that controls the Yanking a buildpack +// from the Buildpack Registry. +type YankBuildpackOptions struct { + ID string + Version string + Type string + URL string + Yank bool +} + +// YankBuildpack marks a buildpack on the Buildpack Registry as 'yanked'. This forbids future +// builds from using it. +func (c *Client) YankBuildpack(opts YankBuildpackOptions) error { + namespace, name, err := registry.ParseNamespaceName(opts.ID) + if err != nil { + return err + } + issueURL, err := registry.GetIssueURL(opts.URL) + if err != nil { + return err + } + + buildpack := registry.Buildpack{ + Namespace: namespace, + Name: name, + Version: opts.Version, + Yanked: opts.Yank, + } + + issue, err := registry.CreateGithubIssue(buildpack) + if err != nil { + return err + } + + params := url.Values{} + params.Add("title", issue.Title) + params.Add("body", issue.Body) + issueURL.RawQuery = params.Encode() + + c.logger.Debugf("Open URL in browser: %s", issueURL) + cmd, err := registry.CreateBrowserCmd(issueURL.String(), runtime.GOOS) + if err != nil { + return err + } + + return cmd.Start() +} diff --git a/vendor/github.com/buildpacks/pack/pkg/dist/buildpack.go b/vendor/github.com/buildpacks/pack/pkg/dist/buildpack.go new file mode 100644 index 0000000000..4042c717d7 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/dist/buildpack.go @@ -0,0 +1,39 @@ +package dist + +const AssumedBuildpackAPIVersion = "0.1" +const BuildpacksDir = "/cnb/buildpacks" + +type BuildpackInfo struct { + ID string `toml:"id,omitempty" json:"id,omitempty" yaml:"id,omitempty"` + Name string `toml:"name,omitempty" json:"name,omitempty" yaml:"name,omitempty"` + Version string `toml:"version,omitempty" json:"version,omitempty" yaml:"version,omitempty"` + Description string `toml:"description,omitempty" json:"description,omitempty" yaml:"description,omitempty"` + Homepage string `toml:"homepage,omitempty" json:"homepage,omitempty" yaml:"homepage,omitempty"` + Keywords []string `toml:"keywords,omitempty" json:"keywords,omitempty" yaml:"keywords,omitempty"` + Licenses []License `toml:"licenses,omitempty" json:"licenses,omitempty" yaml:"licenses,omitempty"` +} + +func (b BuildpackInfo) FullName() string { + if b.Version != "" { + return b.ID + "@" + b.Version + } + return b.ID +} + +// Satisfy stringer +func (b BuildpackInfo) String() string { return b.FullName() } + +// Match compares two buildpacks by ID and Version +func (b BuildpackInfo) Match(o BuildpackInfo) bool { + return b.ID == o.ID && b.Version == o.Version +} + +type License struct { + Type string `toml:"type"` + URI string `toml:"uri"` +} + +type Stack struct { + ID string `json:"id" toml:"id"` + Mixins []string `json:"mixins,omitempty" toml:"mixins,omitempty"` +} diff --git a/vendor/github.com/buildpacks/pack/pkg/dist/buildpack_descriptor.go b/vendor/github.com/buildpacks/pack/pkg/dist/buildpack_descriptor.go new file mode 100644 index 0000000000..fea7f4816f --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/dist/buildpack_descriptor.go @@ -0,0 +1,60 @@ +package dist + +import ( + "fmt" + "sort" + "strings" + + "github.com/buildpacks/lifecycle/api" + + "github.com/buildpacks/pack/internal/stringset" + "github.com/buildpacks/pack/internal/style" +) + +type BuildpackDescriptor struct { + API *api.Version `toml:"api"` + Info BuildpackInfo `toml:"buildpack"` + Stacks []Stack `toml:"stacks"` + Order Order `toml:"order"` +} + +func (b *BuildpackDescriptor) EscapedID() string { + return strings.ReplaceAll(b.Info.ID, "/", "_") +} + +func (b *BuildpackDescriptor) EnsureStackSupport(stackID string, providedMixins []string, validateRunStageMixins bool) error { + if len(b.Stacks) == 0 { + return nil // Order buildpack, no validation required + } + + bpMixins, err := b.findMixinsForStack(stackID) + if err != nil { + return err + } + + if !validateRunStageMixins { + var filtered []string + for _, m := range bpMixins { + if !strings.HasPrefix(m, "run:") { + filtered = append(filtered, m) + } + } + bpMixins = filtered + } + + _, missing, _ := stringset.Compare(providedMixins, bpMixins) + if len(missing) > 0 { + sort.Strings(missing) + return fmt.Errorf("buildpack %s requires missing mixin(s): %s", style.Symbol(b.Info.FullName()), strings.Join(missing, ", ")) + } + return nil +} + +func (b *BuildpackDescriptor) findMixinsForStack(stackID string) ([]string, error) { + for _, s := range b.Stacks { + if s.ID == stackID || s.ID == "*" { + return s.Mixins, nil + } + } + return nil, fmt.Errorf("buildpack %s does not support stack %s", style.Symbol(b.Info.FullName()), style.Symbol(stackID)) +} diff --git a/vendor/github.com/buildpacks/pack/pkg/dist/dist.go b/vendor/github.com/buildpacks/pack/pkg/dist/dist.go new file mode 100644 index 0000000000..6ccee5bcc9 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/dist/dist.go @@ -0,0 +1,69 @@ +package dist + +import ( + "github.com/buildpacks/lifecycle/api" +) + +const BuildpackLayersLabel = "io.buildpacks.buildpack.layers" + +type BuildpackURI struct { + URI string `toml:"uri"` +} + +type ImageRef struct { + ImageName string `toml:"image"` +} + +type ImageOrURI struct { + BuildpackURI + ImageRef +} + +func (c *ImageOrURI) DisplayString() string { + if c.BuildpackURI.URI != "" { + return c.BuildpackURI.URI + } + + return c.ImageRef.ImageName +} + +type Platform struct { + OS string `toml:"os"` +} + +type Order []OrderEntry + +type OrderEntry struct { + Group []BuildpackRef `toml:"group" json:"group"` +} + +type BuildpackRef struct { + BuildpackInfo `yaml:"buildpackinfo,inline"` + Optional bool `toml:"optional,omitempty" json:"optional,omitempty" yaml:"optional,omitempty"` +} + +type BuildpackLayers map[string]map[string]BuildpackLayerInfo + +type BuildpackLayerInfo struct { + API *api.Version `json:"api"` + Stacks []Stack `json:"stacks,omitempty"` + Order Order `json:"order,omitempty"` + LayerDiffID string `json:"layerDiffID"` + Homepage string `json:"homepage,omitempty"` + Name string `json:"name,omitempty"` +} + +func (b BuildpackLayers) Get(id, version string) (BuildpackLayerInfo, bool) { + buildpackLayerEntries, ok := b[id] + if !ok { + return BuildpackLayerInfo{}, false + } + if len(buildpackLayerEntries) == 1 && version == "" { + for key := range buildpackLayerEntries { + version = key + } + } + + result, ok := buildpackLayerEntries[version] + return result, ok +} diff --git a/vendor/github.com/buildpacks/pack/pkg/dist/distribution.go b/vendor/github.com/buildpacks/pack/pkg/dist/distribution.go new file mode 100644 index 0000000000..b9232771b8 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/dist/distribution.go @@ -0,0 +1,3 @@ +// Package dist is responsible for cataloging all data types in relation +// to distributing Cloud Native Buildpack components. +package dist diff --git a/vendor/github.com/buildpacks/pack/pkg/dist/image.go b/vendor/github.com/buildpacks/pack/pkg/dist/image.go new file mode 100644 index 0000000000..6032e57cdd --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/dist/image.go @@ -0,0 +1,42 @@ +package dist + +import ( + "encoding/json" + + "github.com/pkg/errors" + + "github.com/buildpacks/pack/internal/style" +) + +type Labeled interface { + Label(name string) (value string, err error) +} + +type Labelable interface { + SetLabel(name string, value string) error +} + +func SetLabel(labelable Labelable, label string, data interface{}) error { + dataBytes, err := json.Marshal(data) + if err != nil { + return errors.Wrapf(err, "marshalling data to JSON for label %s", style.Symbol(label)) + } + if err := labelable.SetLabel(label, string(dataBytes)); err != nil { + return errors.Wrapf(err, "setting label %s", style.Symbol(label)) + } + return nil +} + +func GetLabel(labeled Labeled, label string, obj interface{}) (ok bool, err error) { + labelData, err := labeled.Label(label) + if err != nil { + return false, errors.Wrapf(err, "retrieving label %s", style.Symbol(label)) + } + if labelData != "" { + if err := json.Unmarshal([]byte(labelData), obj); err != nil { + return false, errors.Wrapf(err, "unmarshalling label %s", style.Symbol(label)) + } + return true, nil + } + return false, nil +} diff --git a/vendor/github.com/buildpacks/pack/pkg/dist/layers.go b/vendor/github.com/buildpacks/pack/pkg/dist/layers.go new file mode 100644 index 0000000000..873f86ca4f --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/dist/layers.go @@ -0,0 +1,45 @@ +package dist + +import ( + "os" + "path/filepath" + + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/tarball" + "github.com/pkg/errors" +) + +func LayerDiffID(layerTarPath string) (v1.Hash, error) { + fh, err := os.Open(filepath.Clean(layerTarPath)) + if err != nil { + return v1.Hash{}, errors.Wrap(err, "opening tar file") + } + defer fh.Close() + + layer, err := tarball.LayerFromFile(layerTarPath) + if err != nil { + return v1.Hash{}, errors.Wrap(err, "reading layer tar") + } + + hash, err := layer.DiffID() + if err != nil { + return v1.Hash{}, errors.Wrap(err, "generating diff id") + } + + return hash, nil +} + +func AddBuildpackToLayersMD(layerMD BuildpackLayers, descriptor BuildpackDescriptor, diffID string) { + bpInfo := descriptor.Info + if _, ok := layerMD[bpInfo.ID]; !ok { + layerMD[bpInfo.ID] = map[string]BuildpackLayerInfo{} + } + layerMD[bpInfo.ID][bpInfo.Version] = BuildpackLayerInfo{ + API: descriptor.API, + Stacks: descriptor.Stacks, + Order: descriptor.Order, + LayerDiffID: diffID, + Homepage: bpInfo.Homepage, + Name: bpInfo.Name, + } +} diff --git a/vendor/github.com/buildpacks/pack/pkg/image/fetcher.go b/vendor/github.com/buildpacks/pack/pkg/image/fetcher.go new file mode 100644 index 0000000000..09990517af --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/image/fetcher.go @@ -0,0 +1,187 @@ +package image + +import ( + "context" + "encoding/base64" + "encoding/json" + "io" + "strings" + + "github.com/buildpacks/imgutil" + "github.com/buildpacks/imgutil/local" + "github.com/buildpacks/imgutil/remote" + "github.com/buildpacks/lifecycle/auth" + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" + "github.com/docker/docker/pkg/jsonmessage" + "github.com/google/go-containerregistry/pkg/authn" + "github.com/pkg/errors" + + pname "github.com/buildpacks/pack/internal/name" + "github.com/buildpacks/pack/internal/style" + "github.com/buildpacks/pack/internal/term" + "github.com/buildpacks/pack/pkg/logging" +) + +// FetcherOption is a type of function that mutate settings on the client. +// Values in these functions are set through currying. +type FetcherOption func(c *Fetcher) + +// WithRegistryMirrors supply your own mirrors for registry. +func WithRegistryMirrors(registryMirrors map[string]string) FetcherOption { + return func(c *Fetcher) { + c.registryMirrors = registryMirrors + } +} + +type Fetcher struct { + docker client.CommonAPIClient + logger logging.Logger + registryMirrors map[string]string +} + +type FetchOptions struct { + Daemon bool + Platform string + PullPolicy PullPolicy +} + +func NewFetcher(logger logging.Logger, docker client.CommonAPIClient, opts ...FetcherOption) *Fetcher { + var fetcher = &Fetcher{ + logger: logger, + docker: docker, + } + + for _, opt := range opts { + opt(fetcher) + } + + return fetcher +} + +var ErrNotFound = errors.New("not found") + +func (f *Fetcher) Fetch(ctx context.Context, name string, options FetchOptions) (imgutil.Image, error) { + name, err := pname.TranslateRegistry(name, f.registryMirrors, f.logger) + if err != nil { + return nil, err + } + + if !options.Daemon { + return f.fetchRemoteImage(name) + } + + switch options.PullPolicy { + case PullNever: + img, err := f.fetchDaemonImage(name) + return img, err + case PullIfNotPresent: + img, err := f.fetchDaemonImage(name) + if err == nil || !errors.Is(err, ErrNotFound) { + return img, err + } + } + + f.logger.Debugf("Pulling image %s", style.Symbol(name)) + err = f.pullImage(ctx, name, options.Platform) + if err != nil && !errors.Is(err, ErrNotFound) { + return nil, err + } + + return f.fetchDaemonImage(name) +} + +func (f *Fetcher) fetchDaemonImage(name string) (imgutil.Image, error) { + image, err := local.NewImage(name, f.docker, local.FromBaseImage(name)) + if err != nil { + return nil, err + } + + if !image.Found() { + return nil, errors.Wrapf(ErrNotFound, "image %s does not exist on the daemon", style.Symbol(name)) + } + + return image, nil +} + +func (f *Fetcher) fetchRemoteImage(name string) (imgutil.Image, error) { + image, err := remote.NewImage(name, authn.DefaultKeychain, remote.FromBaseImage(name)) + if err != nil { + return nil, err + } + + if !image.Found() { + return nil, errors.Wrapf(ErrNotFound, "image %s does not exist in registry", style.Symbol(name)) + } + + return image, nil +} + +func (f *Fetcher) pullImage(ctx context.Context, imageID string, platform string) error { + regAuth, err := registryAuth(imageID) + if err != nil { + return err + } + + rc, err := f.docker.ImagePull(ctx, imageID, types.ImagePullOptions{RegistryAuth: regAuth, Platform: platform}) + if err != nil { + if client.IsErrNotFound(err) { + return errors.Wrapf(ErrNotFound, "image %s does not exist on the daemon", style.Symbol(imageID)) + } + + return err + } + + writer := logging.GetWriterForLevel(f.logger, logging.InfoLevel) + termFd, isTerm := term.IsTerminal(writer) + + err = jsonmessage.DisplayJSONMessagesStream(rc, &colorizedWriter{writer}, termFd, isTerm, nil) + if err != nil { + return err + } + + return rc.Close() +} + +func registryAuth(ref string) (string, error) { + _, a, err := auth.ReferenceForRepoName(authn.DefaultKeychain, ref) + if err != nil { + return "", errors.Wrapf(err, "resolve auth for ref %s", ref) + } + authConfig, err := a.Authorization() + if err != nil { + return "", err + } + + dataJSON, err := json.Marshal(authConfig) + if err != nil { + return "", err + } + + return base64.StdEncoding.EncodeToString(dataJSON), nil +} + +type colorizedWriter struct { + writer io.Writer +} + +type colorFunc = func(string, ...interface{}) string + +func (w *colorizedWriter) Write(p []byte) (n int, err error) { + msg := string(p) + colorizers := map[string]colorFunc{ + "Waiting": style.Waiting, + "Pulling fs layer": style.Waiting, + "Downloading": style.Working, + "Download complete": style.Working, + "Extracting": style.Working, + "Pull complete": style.Complete, + "Already exists": style.Complete, + "=": style.ProgressBar, + ">": style.ProgressBar, + } + for pattern, colorize := range colorizers { + msg = strings.ReplaceAll(msg, pattern, colorize(pattern)) + } + return w.writer.Write([]byte(msg)) +} diff --git a/vendor/github.com/buildpacks/pack/pkg/image/pull_policy.go b/vendor/github.com/buildpacks/pack/pkg/image/pull_policy.go new file mode 100644 index 0000000000..f08678ac5a --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/image/pull_policy.go @@ -0,0 +1,41 @@ +package image + +import ( + "github.com/pkg/errors" +) + +// PullPolicy defines a policy for how to manage images +type PullPolicy int + +const ( + // PullAlways images, even if they are present + PullAlways PullPolicy = iota + // PullNever images, even if they are not present + PullNever + // PullIfNotPresent pulls images if they aren't present + PullIfNotPresent +) + +var nameMap = map[string]PullPolicy{"always": PullAlways, "never": PullNever, "if-not-present": PullIfNotPresent, "": PullAlways} + +// ParsePullPolicy from string +func ParsePullPolicy(policy string) (PullPolicy, error) { + if val, ok := nameMap[policy]; ok { + return val, nil + } + + return PullAlways, errors.Errorf("invalid pull policy %s", policy) +} + +func (p PullPolicy) String() string { + switch p { + case PullAlways: + return "always" + case PullNever: + return "never" + case PullIfNotPresent: + return "if-not-present" + } + + return "" +} diff --git a/vendor/github.com/buildpacks/pack/pkg/logging/logger_simple.go b/vendor/github.com/buildpacks/pack/pkg/logging/logger_simple.go new file mode 100644 index 0000000000..beb18e72e6 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/logging/logger_simple.go @@ -0,0 +1,66 @@ +package logging + +import ( + "fmt" + "io" + "log" +) + +// NewSimpleLogger creates a simple logger for the pack library. +func NewSimpleLogger(w io.Writer) Logger { + return &simpleLogger{ + out: log.New(w, "", log.LstdFlags|log.Lmicroseconds), + } +} + +type simpleLogger struct { + out *log.Logger +} + +const ( + debugPrefix = "DEBUG:" + infoPrefix = "INFO:" + warnPrefix = "WARN:" + errorPrefix = "ERROR:" + prefixFmt = "%-7s %s" +) + +func (l *simpleLogger) Debug(msg string) { + l.out.Printf(prefixFmt, debugPrefix, msg) +} + +func (l *simpleLogger) Debugf(format string, v ...interface{}) { + l.out.Printf(prefixFmt, debugPrefix, fmt.Sprintf(format, v...)) +} + +func (l *simpleLogger) Info(msg string) { + l.out.Printf(prefixFmt, infoPrefix, msg) +} + +func (l *simpleLogger) Infof(format string, v ...interface{}) { + l.out.Printf(prefixFmt, infoPrefix, fmt.Sprintf(format, v...)) +} + +func (l *simpleLogger) Warn(msg string) { + l.out.Printf(prefixFmt, warnPrefix, msg) +} + +func (l *simpleLogger) Warnf(format string, v ...interface{}) { + l.out.Printf(prefixFmt, warnPrefix, fmt.Sprintf(format, v...)) +} + +func (l *simpleLogger) Error(msg string) { + l.out.Printf(prefixFmt, errorPrefix, msg) +} + +func (l *simpleLogger) Errorf(format string, v ...interface{}) { + l.out.Printf(prefixFmt, errorPrefix, fmt.Sprintf(format, v...)) +} + +func (l *simpleLogger) Writer() io.Writer { + return l.out.Writer() +} + +func (l *simpleLogger) IsVerbose() bool { + return false +} diff --git a/vendor/github.com/buildpacks/pack/pkg/logging/logger_writers.go b/vendor/github.com/buildpacks/pack/pkg/logging/logger_writers.go new file mode 100644 index 0000000000..dceb66c462 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/logging/logger_writers.go @@ -0,0 +1,215 @@ +// Package logging implements the logger for the pack CLI. +package logging + +import ( + "fmt" + "io" + "io/ioutil" + "regexp" + "sync" + "time" + + "github.com/apex/log" + "github.com/heroku/color" + + "github.com/buildpacks/pack/internal/style" +) + +const ( + errorLevelText = "ERROR: " + warnLevelText = "Warning: " + lineFeed = '\n' + // log level to use when quiet is true + quietLevel = log.WarnLevel + // log level to use when debug is true + verboseLevel = log.DebugLevel + // time format the out logging uses + timeFmt = "2006/01/02 15:04:05.000000" + // InvalidFileDescriptor based on https://golang.org/src/os/file_unix.go?s=2183:2210#L57 + InvalidFileDescriptor = ^(uintptr(0)) +) + +var colorCodeMatcher = regexp.MustCompile(`\x1b\[[0-9;]*m`) + +var _ Logger = (*LogWithWriters)(nil) + +// LogWithWriters is a logger used with the pack CLI, allowing users to print logs for various levels, including Info, Debug and Error +type LogWithWriters struct { + sync.Mutex + log.Logger + wantTime bool + clock func() time.Time + out io.Writer + errOut io.Writer +} + +// NewLogWithWriters creates a logger to be used with pack CLI. +func NewLogWithWriters(stdout, stderr io.Writer, opts ...func(*LogWithWriters)) *LogWithWriters { + lw := &LogWithWriters{ + Logger: log.Logger{ + Level: log.InfoLevel, + }, + wantTime: false, + clock: time.Now, + out: stdout, + errOut: stderr, + } + lw.Logger.Handler = lw + + for _, opt := range opts { + opt(lw) + } + + return lw +} + +// WithClock is an option used to initialize a LogWithWriters with a given clock function +func WithClock(clock func() time.Time) func(writers *LogWithWriters) { + return func(logger *LogWithWriters) { + logger.clock = clock + } +} + +// WithVerbose is an option used to initialize a LogWithWriters with Verbose turned on +func WithVerbose() func(writers *LogWithWriters) { + return func(logger *LogWithWriters) { + logger.Level = log.DebugLevel + } +} + +// HandleLog handles log events, printing entries appropriately +func (lw *LogWithWriters) HandleLog(e *log.Entry) error { + lw.Lock() + defer lw.Unlock() + + writer := lw.WriterForLevel(Level(e.Level)) + _, err := fmt.Fprint(writer, appendMissingLineFeed(fmt.Sprintf("%s%s", formatLevel(e.Level), e.Message))) + + return err +} + +// WriterForLevel returns a Writer for the given Level +func (lw *LogWithWriters) WriterForLevel(level Level) io.Writer { + if lw.Level > log.Level(level) { + return ioutil.Discard + } + + if level == ErrorLevel { + return newLogWriter(lw.errOut, lw.clock, lw.wantTime) + } + + return newLogWriter(lw.out, lw.clock, lw.wantTime) +} + +// Writer returns the base Writer for the LogWithWriters +func (lw *LogWithWriters) Writer() io.Writer { + return lw.out +} + +// WantTime turns timestamps on in log entries +func (lw *LogWithWriters) WantTime(f bool) { + lw.wantTime = f +} + +// WantQuiet reduces the number of logs returned +func (lw *LogWithWriters) WantQuiet(f bool) { + if f { + lw.Level = quietLevel + } +} + +// WantVerbose increases the number of logs returned +func (lw *LogWithWriters) WantVerbose(f bool) { + if f { + lw.Level = verboseLevel + } +} + +// IsVerbose returns whether verbose logging is on +func (lw *LogWithWriters) IsVerbose() bool { + return lw.Level == log.DebugLevel +} + +func formatLevel(ll log.Level) string { + switch ll { + case log.ErrorLevel: + return style.Error(errorLevelText) + case log.WarnLevel: + return style.Warn(warnLevelText) + } + + return "" +} + +// Preserve behavior of other loggers +func appendMissingLineFeed(msg string) string { + buff := []byte(msg) + if len(buff) == 0 || buff[len(buff)-1] != lineFeed { + buff = append(buff, lineFeed) + } + return string(buff) +} + +// logWriter is a writer used for logs +type logWriter struct { + sync.Mutex + out io.Writer + clock func() time.Time + wantTime bool + wantNoColor bool +} + +func newLogWriter(writer io.Writer, clock func() time.Time, wantTime bool) *logWriter { + wantNoColor := !color.Enabled() + return &logWriter{ + out: writer, + clock: clock, + wantTime: wantTime, + wantNoColor: wantNoColor, + } +} + +// Write writes a message prepended by the time to the set io.Writer +func (lw *logWriter) Write(buf []byte) (n int, err error) { + lw.Lock() + defer lw.Unlock() + + length := len(buf) + if lw.wantNoColor { + buf = stripColor(buf) + } + + prefix := "" + if lw.wantTime { + prefix = fmt.Sprintf("%s ", lw.clock().Format(timeFmt)) + } + + _, err = fmt.Fprintf(lw.out, "%s%s", prefix, buf) + return length, err +} + +// Writer returns the base Writer for the logWriter +func (lw *logWriter) Writer() io.Writer { + return lw.out +} + +// Fd returns the file descriptor of the writer. This is used to ensure it is a Console, and can therefore display streams of text +func (lw *logWriter) Fd() uintptr { + lw.Lock() + defer lw.Unlock() + + if file, ok := lw.out.(hasDescriptor); ok { + return file.Fd() + } + + return InvalidFileDescriptor +} + +// Remove all ANSI color information. +func stripColor(b []byte) []byte { + return colorCodeMatcher.ReplaceAll(b, []byte("")) +} + +type hasDescriptor interface { + Fd() uintptr +} diff --git a/vendor/github.com/buildpacks/pack/pkg/logging/logging.go b/vendor/github.com/buildpacks/pack/pkg/logging/logging.go new file mode 100644 index 0000000000..6d9a026950 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/logging/logging.go @@ -0,0 +1,66 @@ +// Package logging defines the minimal interface that loggers must support to be used by client. +package logging + +import ( + "io" + "io/ioutil" + + "github.com/buildpacks/pack/internal/style" +) + +type Level int + +const ( + DebugLevel Level = iota + InfoLevel + WarnLevel + ErrorLevel +) + +// Logger defines behavior required by a logging package used by pack libraries +type Logger interface { + Debug(msg string) + Debugf(fmt string, v ...interface{}) + + Info(msg string) + Infof(fmt string, v ...interface{}) + + Warn(msg string) + Warnf(fmt string, v ...interface{}) + + Error(msg string) + Errorf(fmt string, v ...interface{}) + + Writer() io.Writer + + IsVerbose() bool +} + +type isSelectableWriter interface { + WriterForLevel(level Level) io.Writer +} + +// GetWriterForLevel retrieves the appropriate Writer for the log level provided. +// +// See isSelectableWriter +func GetWriterForLevel(logger Logger, level Level) io.Writer { + if w, ok := logger.(isSelectableWriter); ok { + return w.WriterForLevel(level) + } + + return logger.Writer() +} + +// IsQuiet defines whether a pack logger is set to quiet mode +func IsQuiet(logger Logger) bool { + if writer := GetWriterForLevel(logger, InfoLevel); writer == ioutil.Discard { + return true + } + + return false +} + +// Tip logs a tip. +func Tip(l Logger, format string, v ...interface{}) { + l.Infof(style.Tip("Tip: ")+format, v...) +} diff --git a/vendor/github.com/buildpacks/pack/pkg/logging/prefix_writer.go b/vendor/github.com/buildpacks/pack/pkg/logging/prefix_writer.go new file mode 100644 index 0000000000..cce4ee6772 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/logging/prefix_writer.go @@ -0,0 +1,126 @@ +package logging + +import ( + "bufio" + "bytes" + "fmt" + "io" + + "github.com/buildpacks/pack/internal/style" +) + +// PrefixWriter is a buffering writer that prefixes each new line. Close should be called to properly flush the buffer. +type PrefixWriter struct { + out io.Writer + buf *bytes.Buffer + prefix string + readerFactory func(data []byte) io.Reader +} + +type PrefixWriterOption func(c *PrefixWriter) + +func WithReaderFactory(factory func(data []byte) io.Reader) PrefixWriterOption { + return func(writer *PrefixWriter) { + writer.readerFactory = factory + } +} + +// NewPrefixWriter writes by w will be prefixed +func NewPrefixWriter(w io.Writer, prefix string, opts ...PrefixWriterOption) *PrefixWriter { + writer := &PrefixWriter{ + out: w, + prefix: fmt.Sprintf("[%s] ", style.Prefix(prefix)), + buf: &bytes.Buffer{}, + readerFactory: func(data []byte) io.Reader { + return bytes.NewReader(data) + }, + } + + for _, opt := range opts { + opt(writer) + } + + return writer +} + +// Write writes bytes to the embedded log function +func (w *PrefixWriter) Write(data []byte) (int, error) { + scanner := bufio.NewScanner(w.readerFactory(data)) + scanner.Split(ScanLinesKeepNewLine) + for scanner.Scan() { + newBits := scanner.Bytes() + if len(newBits) > 0 && newBits[len(newBits)-1] != '\n' { // just append if we don't have a new line + _, err := w.buf.Write(newBits) + if err != nil { + return 0, err + } + } else { // write our complete message + _, err := w.buf.Write(bytes.TrimRight(newBits, "\n")) + if err != nil { + return 0, err + } + + err = w.flush() + if err != nil { + return 0, err + } + } + } + + if err := scanner.Err(); err != nil { + return 0, err + } + + return len(data), nil +} + +// Close writes any pending data in the buffer +func (w *PrefixWriter) Close() error { + if w.buf.Len() > 0 { + return w.flush() + } + + return nil +} + +func (w *PrefixWriter) flush() error { + bits := w.buf.Bytes() + w.buf.Reset() + + // process any CR in message + if i := bytes.LastIndexByte(bits, '\r'); i >= 0 { + bits = bits[i+1:] + } + + _, err := fmt.Fprint(w.out, w.prefix+string(bits)+"\n") + return err +} + +// A customized implementation of bufio.ScanLines that preserves new line characters. +func ScanLinesKeepNewLine(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + + // first we'll split by LF (\n) + // then remove any preceding CR (\r) [due to CR+LF] + if i := bytes.IndexByte(data, '\n'); i >= 0 { + // We have a full newline-terminated line. + return i + 1, append(dropCR(data[0:i]), '\n'), nil + } + + // If we're at EOF, we have a final, non-terminated line. Return it. + if atEOF { + return len(data), data, nil + } + // Request more data. + return 0, nil, nil +} + +// dropCR drops a terminal \r from the data. +func dropCR(data []byte) []byte { + if len(data) > 0 && data[len(data)-1] == '\r' { + return data[0 : len(data)-1] + } + return data +} diff --git a/vendor/github.com/buildpacks/pack/pkg/project/types/types.go b/vendor/github.com/buildpacks/pack/pkg/project/types/types.go new file mode 100644 index 0000000000..575adcc83f --- /dev/null +++ b/vendor/github.com/buildpacks/pack/pkg/project/types/types.go @@ -0,0 +1,50 @@ +package types + +import ( + "github.com/buildpacks/lifecycle/api" +) + +type Script struct { + API string `toml:"api"` + Inline string `toml:"inline"` + Shell string `toml:"shell"` +} + +type Buildpack struct { + ID string `toml:"id"` + Version string `toml:"version"` + URI string `toml:"uri"` + Script Script `toml:"script"` +} + +type EnvVar struct { + Name string `toml:"name"` + Value string `toml:"value"` +} + +type Build struct { + Include []string `toml:"include"` + Exclude []string `toml:"exclude"` + Buildpacks []Buildpack `toml:"buildpacks"` + Env []EnvVar `toml:"env"` + Builder string `toml:"builder"` +} + +type Project struct { + Name string `toml:"name"` + Version string `toml:"version"` + SourceURL string `toml:"source-url"` + Licenses []License `toml:"licenses"` +} + +type License struct { + Type string `toml:"type"` + URI string `toml:"uri"` +} + +type Descriptor struct { + Project Project `toml:"project"` + Build Build `toml:"build"` + Metadata map[string]interface{} `toml:"metadata"` + SchemaVersion *api.Version +} diff --git a/vendor/github.com/buildpacks/pack/project.toml b/vendor/github.com/buildpacks/pack/project.toml new file mode 100644 index 0000000000..e8c7bb4495 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/project.toml @@ -0,0 +1,3 @@ +[project] +version = "1.0.2" +source-url = "https://github.com/buildpacks/pack" diff --git a/vendor/github.com/buildpacks/pack/version.go b/vendor/github.com/buildpacks/pack/version.go new file mode 100644 index 0000000000..6d858250f9 --- /dev/null +++ b/vendor/github.com/buildpacks/pack/version.go @@ -0,0 +1,6 @@ +package pack + +var ( + // Version is the version of `pack`. It is injected at compile time. + Version = "0.0.0" +) diff --git a/vendor/github.com/containerd/cgroups/LICENSE b/vendor/github.com/containerd/cgroups/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/vendor/github.com/containerd/cgroups/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/vendor/github.com/containerd/cgroups/stats/v1/doc.go b/vendor/github.com/containerd/cgroups/stats/v1/doc.go new file mode 100644 index 0000000000..23f3cdd4b3 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/stats/v1/doc.go @@ -0,0 +1,17 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package v1 diff --git a/vendor/github.com/containerd/cgroups/stats/v1/metrics.pb.go b/vendor/github.com/containerd/cgroups/stats/v1/metrics.pb.go new file mode 100644 index 0000000000..6d2d41770b --- /dev/null +++ b/vendor/github.com/containerd/cgroups/stats/v1/metrics.pb.go @@ -0,0 +1,6125 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/cgroups/stats/v1/metrics.proto + +package v1 + +import ( + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Metrics struct { + Hugetlb []*HugetlbStat `protobuf:"bytes,1,rep,name=hugetlb,proto3" json:"hugetlb,omitempty"` + Pids *PidsStat `protobuf:"bytes,2,opt,name=pids,proto3" json:"pids,omitempty"` + CPU *CPUStat `protobuf:"bytes,3,opt,name=cpu,proto3" json:"cpu,omitempty"` + Memory *MemoryStat `protobuf:"bytes,4,opt,name=memory,proto3" json:"memory,omitempty"` + Blkio *BlkIOStat `protobuf:"bytes,5,opt,name=blkio,proto3" json:"blkio,omitempty"` + Rdma *RdmaStat `protobuf:"bytes,6,opt,name=rdma,proto3" json:"rdma,omitempty"` + Network []*NetworkStat `protobuf:"bytes,7,rep,name=network,proto3" json:"network,omitempty"` + CgroupStats *CgroupStats `protobuf:"bytes,8,opt,name=cgroup_stats,json=cgroupStats,proto3" json:"cgroup_stats,omitempty"` + MemoryOomControl *MemoryOomControl `protobuf:"bytes,9,opt,name=memory_oom_control,json=memoryOomControl,proto3" json:"memory_oom_control,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Metrics) Reset() { *m = Metrics{} } +func (*Metrics) ProtoMessage() {} +func (*Metrics) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{0} +} +func (m *Metrics) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Metrics) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Metrics.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Metrics) XXX_Merge(src proto.Message) { + xxx_messageInfo_Metrics.Merge(m, src) +} +func (m *Metrics) XXX_Size() int { + return m.Size() +} +func (m *Metrics) XXX_DiscardUnknown() { + xxx_messageInfo_Metrics.DiscardUnknown(m) +} + +var xxx_messageInfo_Metrics proto.InternalMessageInfo + +type HugetlbStat struct { + Usage uint64 `protobuf:"varint,1,opt,name=usage,proto3" json:"usage,omitempty"` + Max uint64 `protobuf:"varint,2,opt,name=max,proto3" json:"max,omitempty"` + Failcnt uint64 `protobuf:"varint,3,opt,name=failcnt,proto3" json:"failcnt,omitempty"` + Pagesize string `protobuf:"bytes,4,opt,name=pagesize,proto3" json:"pagesize,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *HugetlbStat) Reset() { *m = HugetlbStat{} } +func (*HugetlbStat) ProtoMessage() {} +func (*HugetlbStat) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{1} +} +func (m *HugetlbStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HugetlbStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HugetlbStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *HugetlbStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_HugetlbStat.Merge(m, src) +} +func (m *HugetlbStat) XXX_Size() int { + return m.Size() +} +func (m *HugetlbStat) XXX_DiscardUnknown() { + xxx_messageInfo_HugetlbStat.DiscardUnknown(m) +} + +var xxx_messageInfo_HugetlbStat proto.InternalMessageInfo + +type PidsStat struct { + Current uint64 `protobuf:"varint,1,opt,name=current,proto3" json:"current,omitempty"` + Limit uint64 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PidsStat) Reset() { *m = PidsStat{} } +func (*PidsStat) ProtoMessage() {} +func (*PidsStat) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{2} +} +func (m *PidsStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PidsStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PidsStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PidsStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_PidsStat.Merge(m, src) +} +func (m *PidsStat) XXX_Size() int { + return m.Size() +} +func (m *PidsStat) XXX_DiscardUnknown() { + xxx_messageInfo_PidsStat.DiscardUnknown(m) +} + +var xxx_messageInfo_PidsStat proto.InternalMessageInfo + +type CPUStat struct { + Usage *CPUUsage `protobuf:"bytes,1,opt,name=usage,proto3" json:"usage,omitempty"` + Throttling *Throttle `protobuf:"bytes,2,opt,name=throttling,proto3" json:"throttling,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CPUStat) Reset() { *m = CPUStat{} } +func (*CPUStat) ProtoMessage() {} +func (*CPUStat) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{3} +} +func (m *CPUStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CPUStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CPUStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CPUStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_CPUStat.Merge(m, src) +} +func (m *CPUStat) XXX_Size() int { + return m.Size() +} +func (m *CPUStat) XXX_DiscardUnknown() { + xxx_messageInfo_CPUStat.DiscardUnknown(m) +} + +var xxx_messageInfo_CPUStat proto.InternalMessageInfo + +type CPUUsage struct { + // values in nanoseconds + Total uint64 `protobuf:"varint,1,opt,name=total,proto3" json:"total,omitempty"` + Kernel uint64 `protobuf:"varint,2,opt,name=kernel,proto3" json:"kernel,omitempty"` + User uint64 `protobuf:"varint,3,opt,name=user,proto3" json:"user,omitempty"` + PerCPU []uint64 `protobuf:"varint,4,rep,packed,name=per_cpu,json=perCpu,proto3" json:"per_cpu,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CPUUsage) Reset() { *m = CPUUsage{} } +func (*CPUUsage) ProtoMessage() {} +func (*CPUUsage) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{4} +} +func (m *CPUUsage) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CPUUsage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CPUUsage.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CPUUsage) XXX_Merge(src proto.Message) { + xxx_messageInfo_CPUUsage.Merge(m, src) +} +func (m *CPUUsage) XXX_Size() int { + return m.Size() +} +func (m *CPUUsage) XXX_DiscardUnknown() { + xxx_messageInfo_CPUUsage.DiscardUnknown(m) +} + +var xxx_messageInfo_CPUUsage proto.InternalMessageInfo + +type Throttle struct { + Periods uint64 `protobuf:"varint,1,opt,name=periods,proto3" json:"periods,omitempty"` + ThrottledPeriods uint64 `protobuf:"varint,2,opt,name=throttled_periods,json=throttledPeriods,proto3" json:"throttled_periods,omitempty"` + ThrottledTime uint64 `protobuf:"varint,3,opt,name=throttled_time,json=throttledTime,proto3" json:"throttled_time,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Throttle) Reset() { *m = Throttle{} } +func (*Throttle) ProtoMessage() {} +func (*Throttle) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{5} +} +func (m *Throttle) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Throttle) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Throttle.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Throttle) XXX_Merge(src proto.Message) { + xxx_messageInfo_Throttle.Merge(m, src) +} +func (m *Throttle) XXX_Size() int { + return m.Size() +} +func (m *Throttle) XXX_DiscardUnknown() { + xxx_messageInfo_Throttle.DiscardUnknown(m) +} + +var xxx_messageInfo_Throttle proto.InternalMessageInfo + +type MemoryStat struct { + Cache uint64 `protobuf:"varint,1,opt,name=cache,proto3" json:"cache,omitempty"` + RSS uint64 `protobuf:"varint,2,opt,name=rss,proto3" json:"rss,omitempty"` + RSSHuge uint64 `protobuf:"varint,3,opt,name=rss_huge,json=rssHuge,proto3" json:"rss_huge,omitempty"` + MappedFile uint64 `protobuf:"varint,4,opt,name=mapped_file,json=mappedFile,proto3" json:"mapped_file,omitempty"` + Dirty uint64 `protobuf:"varint,5,opt,name=dirty,proto3" json:"dirty,omitempty"` + Writeback uint64 `protobuf:"varint,6,opt,name=writeback,proto3" json:"writeback,omitempty"` + PgPgIn uint64 `protobuf:"varint,7,opt,name=pg_pg_in,json=pgPgIn,proto3" json:"pg_pg_in,omitempty"` + PgPgOut uint64 `protobuf:"varint,8,opt,name=pg_pg_out,json=pgPgOut,proto3" json:"pg_pg_out,omitempty"` + PgFault uint64 `protobuf:"varint,9,opt,name=pg_fault,json=pgFault,proto3" json:"pg_fault,omitempty"` + PgMajFault uint64 `protobuf:"varint,10,opt,name=pg_maj_fault,json=pgMajFault,proto3" json:"pg_maj_fault,omitempty"` + InactiveAnon uint64 `protobuf:"varint,11,opt,name=inactive_anon,json=inactiveAnon,proto3" json:"inactive_anon,omitempty"` + ActiveAnon uint64 `protobuf:"varint,12,opt,name=active_anon,json=activeAnon,proto3" json:"active_anon,omitempty"` + InactiveFile uint64 `protobuf:"varint,13,opt,name=inactive_file,json=inactiveFile,proto3" json:"inactive_file,omitempty"` + ActiveFile uint64 `protobuf:"varint,14,opt,name=active_file,json=activeFile,proto3" json:"active_file,omitempty"` + Unevictable uint64 `protobuf:"varint,15,opt,name=unevictable,proto3" json:"unevictable,omitempty"` + HierarchicalMemoryLimit uint64 `protobuf:"varint,16,opt,name=hierarchical_memory_limit,json=hierarchicalMemoryLimit,proto3" json:"hierarchical_memory_limit,omitempty"` + HierarchicalSwapLimit uint64 `protobuf:"varint,17,opt,name=hierarchical_swap_limit,json=hierarchicalSwapLimit,proto3" json:"hierarchical_swap_limit,omitempty"` + TotalCache uint64 `protobuf:"varint,18,opt,name=total_cache,json=totalCache,proto3" json:"total_cache,omitempty"` + TotalRSS uint64 `protobuf:"varint,19,opt,name=total_rss,json=totalRss,proto3" json:"total_rss,omitempty"` + TotalRSSHuge uint64 `protobuf:"varint,20,opt,name=total_rss_huge,json=totalRssHuge,proto3" json:"total_rss_huge,omitempty"` + TotalMappedFile uint64 `protobuf:"varint,21,opt,name=total_mapped_file,json=totalMappedFile,proto3" json:"total_mapped_file,omitempty"` + TotalDirty uint64 `protobuf:"varint,22,opt,name=total_dirty,json=totalDirty,proto3" json:"total_dirty,omitempty"` + TotalWriteback uint64 `protobuf:"varint,23,opt,name=total_writeback,json=totalWriteback,proto3" json:"total_writeback,omitempty"` + TotalPgPgIn uint64 `protobuf:"varint,24,opt,name=total_pg_pg_in,json=totalPgPgIn,proto3" json:"total_pg_pg_in,omitempty"` + TotalPgPgOut uint64 `protobuf:"varint,25,opt,name=total_pg_pg_out,json=totalPgPgOut,proto3" json:"total_pg_pg_out,omitempty"` + TotalPgFault uint64 `protobuf:"varint,26,opt,name=total_pg_fault,json=totalPgFault,proto3" json:"total_pg_fault,omitempty"` + TotalPgMajFault uint64 `protobuf:"varint,27,opt,name=total_pg_maj_fault,json=totalPgMajFault,proto3" json:"total_pg_maj_fault,omitempty"` + TotalInactiveAnon uint64 `protobuf:"varint,28,opt,name=total_inactive_anon,json=totalInactiveAnon,proto3" json:"total_inactive_anon,omitempty"` + TotalActiveAnon uint64 `protobuf:"varint,29,opt,name=total_active_anon,json=totalActiveAnon,proto3" json:"total_active_anon,omitempty"` + TotalInactiveFile uint64 `protobuf:"varint,30,opt,name=total_inactive_file,json=totalInactiveFile,proto3" json:"total_inactive_file,omitempty"` + TotalActiveFile uint64 `protobuf:"varint,31,opt,name=total_active_file,json=totalActiveFile,proto3" json:"total_active_file,omitempty"` + TotalUnevictable uint64 `protobuf:"varint,32,opt,name=total_unevictable,json=totalUnevictable,proto3" json:"total_unevictable,omitempty"` + Usage *MemoryEntry `protobuf:"bytes,33,opt,name=usage,proto3" json:"usage,omitempty"` + Swap *MemoryEntry `protobuf:"bytes,34,opt,name=swap,proto3" json:"swap,omitempty"` + Kernel *MemoryEntry `protobuf:"bytes,35,opt,name=kernel,proto3" json:"kernel,omitempty"` + KernelTCP *MemoryEntry `protobuf:"bytes,36,opt,name=kernel_tcp,json=kernelTcp,proto3" json:"kernel_tcp,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MemoryStat) Reset() { *m = MemoryStat{} } +func (*MemoryStat) ProtoMessage() {} +func (*MemoryStat) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{6} +} +func (m *MemoryStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MemoryStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MemoryStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MemoryStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_MemoryStat.Merge(m, src) +} +func (m *MemoryStat) XXX_Size() int { + return m.Size() +} +func (m *MemoryStat) XXX_DiscardUnknown() { + xxx_messageInfo_MemoryStat.DiscardUnknown(m) +} + +var xxx_messageInfo_MemoryStat proto.InternalMessageInfo + +type MemoryEntry struct { + Limit uint64 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"` + Usage uint64 `protobuf:"varint,2,opt,name=usage,proto3" json:"usage,omitempty"` + Max uint64 `protobuf:"varint,3,opt,name=max,proto3" json:"max,omitempty"` + Failcnt uint64 `protobuf:"varint,4,opt,name=failcnt,proto3" json:"failcnt,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MemoryEntry) Reset() { *m = MemoryEntry{} } +func (*MemoryEntry) ProtoMessage() {} +func (*MemoryEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{7} +} +func (m *MemoryEntry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MemoryEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MemoryEntry.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MemoryEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_MemoryEntry.Merge(m, src) +} +func (m *MemoryEntry) XXX_Size() int { + return m.Size() +} +func (m *MemoryEntry) XXX_DiscardUnknown() { + xxx_messageInfo_MemoryEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_MemoryEntry proto.InternalMessageInfo + +type MemoryOomControl struct { + OomKillDisable uint64 `protobuf:"varint,1,opt,name=oom_kill_disable,json=oomKillDisable,proto3" json:"oom_kill_disable,omitempty"` + UnderOom uint64 `protobuf:"varint,2,opt,name=under_oom,json=underOom,proto3" json:"under_oom,omitempty"` + OomKill uint64 `protobuf:"varint,3,opt,name=oom_kill,json=oomKill,proto3" json:"oom_kill,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MemoryOomControl) Reset() { *m = MemoryOomControl{} } +func (*MemoryOomControl) ProtoMessage() {} +func (*MemoryOomControl) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{8} +} +func (m *MemoryOomControl) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MemoryOomControl) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MemoryOomControl.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MemoryOomControl) XXX_Merge(src proto.Message) { + xxx_messageInfo_MemoryOomControl.Merge(m, src) +} +func (m *MemoryOomControl) XXX_Size() int { + return m.Size() +} +func (m *MemoryOomControl) XXX_DiscardUnknown() { + xxx_messageInfo_MemoryOomControl.DiscardUnknown(m) +} + +var xxx_messageInfo_MemoryOomControl proto.InternalMessageInfo + +type BlkIOStat struct { + IoServiceBytesRecursive []*BlkIOEntry `protobuf:"bytes,1,rep,name=io_service_bytes_recursive,json=ioServiceBytesRecursive,proto3" json:"io_service_bytes_recursive,omitempty"` + IoServicedRecursive []*BlkIOEntry `protobuf:"bytes,2,rep,name=io_serviced_recursive,json=ioServicedRecursive,proto3" json:"io_serviced_recursive,omitempty"` + IoQueuedRecursive []*BlkIOEntry `protobuf:"bytes,3,rep,name=io_queued_recursive,json=ioQueuedRecursive,proto3" json:"io_queued_recursive,omitempty"` + IoServiceTimeRecursive []*BlkIOEntry `protobuf:"bytes,4,rep,name=io_service_time_recursive,json=ioServiceTimeRecursive,proto3" json:"io_service_time_recursive,omitempty"` + IoWaitTimeRecursive []*BlkIOEntry `protobuf:"bytes,5,rep,name=io_wait_time_recursive,json=ioWaitTimeRecursive,proto3" json:"io_wait_time_recursive,omitempty"` + IoMergedRecursive []*BlkIOEntry `protobuf:"bytes,6,rep,name=io_merged_recursive,json=ioMergedRecursive,proto3" json:"io_merged_recursive,omitempty"` + IoTimeRecursive []*BlkIOEntry `protobuf:"bytes,7,rep,name=io_time_recursive,json=ioTimeRecursive,proto3" json:"io_time_recursive,omitempty"` + SectorsRecursive []*BlkIOEntry `protobuf:"bytes,8,rep,name=sectors_recursive,json=sectorsRecursive,proto3" json:"sectors_recursive,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *BlkIOStat) Reset() { *m = BlkIOStat{} } +func (*BlkIOStat) ProtoMessage() {} +func (*BlkIOStat) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{9} +} +func (m *BlkIOStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BlkIOStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BlkIOStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *BlkIOStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_BlkIOStat.Merge(m, src) +} +func (m *BlkIOStat) XXX_Size() int { + return m.Size() +} +func (m *BlkIOStat) XXX_DiscardUnknown() { + xxx_messageInfo_BlkIOStat.DiscardUnknown(m) +} + +var xxx_messageInfo_BlkIOStat proto.InternalMessageInfo + +type BlkIOEntry struct { + Op string `protobuf:"bytes,1,opt,name=op,proto3" json:"op,omitempty"` + Device string `protobuf:"bytes,2,opt,name=device,proto3" json:"device,omitempty"` + Major uint64 `protobuf:"varint,3,opt,name=major,proto3" json:"major,omitempty"` + Minor uint64 `protobuf:"varint,4,opt,name=minor,proto3" json:"minor,omitempty"` + Value uint64 `protobuf:"varint,5,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *BlkIOEntry) Reset() { *m = BlkIOEntry{} } +func (*BlkIOEntry) ProtoMessage() {} +func (*BlkIOEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{10} +} +func (m *BlkIOEntry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BlkIOEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BlkIOEntry.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *BlkIOEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_BlkIOEntry.Merge(m, src) +} +func (m *BlkIOEntry) XXX_Size() int { + return m.Size() +} +func (m *BlkIOEntry) XXX_DiscardUnknown() { + xxx_messageInfo_BlkIOEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_BlkIOEntry proto.InternalMessageInfo + +type RdmaStat struct { + Current []*RdmaEntry `protobuf:"bytes,1,rep,name=current,proto3" json:"current,omitempty"` + Limit []*RdmaEntry `protobuf:"bytes,2,rep,name=limit,proto3" json:"limit,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RdmaStat) Reset() { *m = RdmaStat{} } +func (*RdmaStat) ProtoMessage() {} +func (*RdmaStat) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{11} +} +func (m *RdmaStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RdmaStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RdmaStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RdmaStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_RdmaStat.Merge(m, src) +} +func (m *RdmaStat) XXX_Size() int { + return m.Size() +} +func (m *RdmaStat) XXX_DiscardUnknown() { + xxx_messageInfo_RdmaStat.DiscardUnknown(m) +} + +var xxx_messageInfo_RdmaStat proto.InternalMessageInfo + +type RdmaEntry struct { + Device string `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"` + HcaHandles uint32 `protobuf:"varint,2,opt,name=hca_handles,json=hcaHandles,proto3" json:"hca_handles,omitempty"` + HcaObjects uint32 `protobuf:"varint,3,opt,name=hca_objects,json=hcaObjects,proto3" json:"hca_objects,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RdmaEntry) Reset() { *m = RdmaEntry{} } +func (*RdmaEntry) ProtoMessage() {} +func (*RdmaEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{12} +} +func (m *RdmaEntry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RdmaEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RdmaEntry.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RdmaEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_RdmaEntry.Merge(m, src) +} +func (m *RdmaEntry) XXX_Size() int { + return m.Size() +} +func (m *RdmaEntry) XXX_DiscardUnknown() { + xxx_messageInfo_RdmaEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_RdmaEntry proto.InternalMessageInfo + +type NetworkStat struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + RxBytes uint64 `protobuf:"varint,2,opt,name=rx_bytes,json=rxBytes,proto3" json:"rx_bytes,omitempty"` + RxPackets uint64 `protobuf:"varint,3,opt,name=rx_packets,json=rxPackets,proto3" json:"rx_packets,omitempty"` + RxErrors uint64 `protobuf:"varint,4,opt,name=rx_errors,json=rxErrors,proto3" json:"rx_errors,omitempty"` + RxDropped uint64 `protobuf:"varint,5,opt,name=rx_dropped,json=rxDropped,proto3" json:"rx_dropped,omitempty"` + TxBytes uint64 `protobuf:"varint,6,opt,name=tx_bytes,json=txBytes,proto3" json:"tx_bytes,omitempty"` + TxPackets uint64 `protobuf:"varint,7,opt,name=tx_packets,json=txPackets,proto3" json:"tx_packets,omitempty"` + TxErrors uint64 `protobuf:"varint,8,opt,name=tx_errors,json=txErrors,proto3" json:"tx_errors,omitempty"` + TxDropped uint64 `protobuf:"varint,9,opt,name=tx_dropped,json=txDropped,proto3" json:"tx_dropped,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *NetworkStat) Reset() { *m = NetworkStat{} } +func (*NetworkStat) ProtoMessage() {} +func (*NetworkStat) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{13} +} +func (m *NetworkStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NetworkStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NetworkStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NetworkStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_NetworkStat.Merge(m, src) +} +func (m *NetworkStat) XXX_Size() int { + return m.Size() +} +func (m *NetworkStat) XXX_DiscardUnknown() { + xxx_messageInfo_NetworkStat.DiscardUnknown(m) +} + +var xxx_messageInfo_NetworkStat proto.InternalMessageInfo + +// CgroupStats exports per-cgroup statistics. +type CgroupStats struct { + // number of tasks sleeping + NrSleeping uint64 `protobuf:"varint,1,opt,name=nr_sleeping,json=nrSleeping,proto3" json:"nr_sleeping,omitempty"` + // number of tasks running + NrRunning uint64 `protobuf:"varint,2,opt,name=nr_running,json=nrRunning,proto3" json:"nr_running,omitempty"` + // number of tasks in stopped state + NrStopped uint64 `protobuf:"varint,3,opt,name=nr_stopped,json=nrStopped,proto3" json:"nr_stopped,omitempty"` + // number of tasks in uninterruptible state + NrUninterruptible uint64 `protobuf:"varint,4,opt,name=nr_uninterruptible,json=nrUninterruptible,proto3" json:"nr_uninterruptible,omitempty"` + // number of tasks waiting on IO + NrIoWait uint64 `protobuf:"varint,5,opt,name=nr_io_wait,json=nrIoWait,proto3" json:"nr_io_wait,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CgroupStats) Reset() { *m = CgroupStats{} } +func (*CgroupStats) ProtoMessage() {} +func (*CgroupStats) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{14} +} +func (m *CgroupStats) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CgroupStats) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CgroupStats.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CgroupStats) XXX_Merge(src proto.Message) { + xxx_messageInfo_CgroupStats.Merge(m, src) +} +func (m *CgroupStats) XXX_Size() int { + return m.Size() +} +func (m *CgroupStats) XXX_DiscardUnknown() { + xxx_messageInfo_CgroupStats.DiscardUnknown(m) +} + +var xxx_messageInfo_CgroupStats proto.InternalMessageInfo + +func init() { + proto.RegisterType((*Metrics)(nil), "io.containerd.cgroups.v1.Metrics") + proto.RegisterType((*HugetlbStat)(nil), "io.containerd.cgroups.v1.HugetlbStat") + proto.RegisterType((*PidsStat)(nil), "io.containerd.cgroups.v1.PidsStat") + proto.RegisterType((*CPUStat)(nil), "io.containerd.cgroups.v1.CPUStat") + proto.RegisterType((*CPUUsage)(nil), "io.containerd.cgroups.v1.CPUUsage") + proto.RegisterType((*Throttle)(nil), "io.containerd.cgroups.v1.Throttle") + proto.RegisterType((*MemoryStat)(nil), "io.containerd.cgroups.v1.MemoryStat") + proto.RegisterType((*MemoryEntry)(nil), "io.containerd.cgroups.v1.MemoryEntry") + proto.RegisterType((*MemoryOomControl)(nil), "io.containerd.cgroups.v1.MemoryOomControl") + proto.RegisterType((*BlkIOStat)(nil), "io.containerd.cgroups.v1.BlkIOStat") + proto.RegisterType((*BlkIOEntry)(nil), "io.containerd.cgroups.v1.BlkIOEntry") + proto.RegisterType((*RdmaStat)(nil), "io.containerd.cgroups.v1.RdmaStat") + proto.RegisterType((*RdmaEntry)(nil), "io.containerd.cgroups.v1.RdmaEntry") + proto.RegisterType((*NetworkStat)(nil), "io.containerd.cgroups.v1.NetworkStat") + proto.RegisterType((*CgroupStats)(nil), "io.containerd.cgroups.v1.CgroupStats") +} + +func init() { + proto.RegisterFile("github.com/containerd/cgroups/stats/v1/metrics.proto", fileDescriptor_a17b2d87c332bfaa) +} + +var fileDescriptor_a17b2d87c332bfaa = []byte{ + // 1749 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x58, 0xcd, 0x72, 0xe3, 0xc6, + 0x11, 0x36, 0x45, 0x48, 0x24, 0x9a, 0x92, 0x56, 0x9a, 0xfd, 0x83, 0xe4, 0xb5, 0x28, 0x53, 0xbb, + 0x89, 0xe2, 0xad, 0x48, 0x65, 0x27, 0xb5, 0x95, 0x75, 0xec, 0x4a, 0x59, 0x5a, 0xbb, 0x76, 0xcb, + 0x51, 0x44, 0x83, 0x52, 0xd9, 0x39, 0xa1, 0x40, 0x70, 0x16, 0x9c, 0x15, 0x80, 0x81, 0x07, 0x03, + 0x89, 0xca, 0x29, 0x87, 0x54, 0xe5, 0x94, 0x07, 0xca, 0x1b, 0xf8, 0x98, 0x4b, 0x52, 0xc9, 0x45, + 0x15, 0xf3, 0x49, 0x52, 0x33, 0x3d, 0xf8, 0xa1, 0xbc, 0x5a, 0x85, 0x37, 0x76, 0xcf, 0xd7, 0x5f, + 0xf7, 0x34, 0xbe, 0x19, 0x34, 0x08, 0xbf, 0x0e, 0x99, 0x1c, 0xe7, 0xc3, 0xbd, 0x80, 0xc7, 0xfb, + 0x01, 0x4f, 0xa4, 0xcf, 0x12, 0x2a, 0x46, 0xfb, 0x41, 0x28, 0x78, 0x9e, 0x66, 0xfb, 0x99, 0xf4, + 0x65, 0xb6, 0x7f, 0xfe, 0xf1, 0x7e, 0x4c, 0xa5, 0x60, 0x41, 0xb6, 0x97, 0x0a, 0x2e, 0x39, 0x71, + 0x18, 0xdf, 0xab, 0xd0, 0x7b, 0x06, 0xbd, 0x77, 0xfe, 0xf1, 0xe6, 0xbd, 0x90, 0x87, 0x5c, 0x83, + 0xf6, 0xd5, 0x2f, 0xc4, 0xf7, 0xfe, 0x65, 0x41, 0xeb, 0x08, 0x19, 0xc8, 0xef, 0xa0, 0x35, 0xce, + 0x43, 0x2a, 0xa3, 0xa1, 0xd3, 0xd8, 0x6e, 0xee, 0x76, 0x3e, 0x79, 0xb2, 0x77, 0x13, 0xdb, 0xde, + 0x4b, 0x04, 0x0e, 0xa4, 0x2f, 0xdd, 0x22, 0x8a, 0x3c, 0x03, 0x2b, 0x65, 0xa3, 0xcc, 0x59, 0xd8, + 0x6e, 0xec, 0x76, 0x3e, 0xe9, 0xdd, 0x1c, 0xdd, 0x67, 0xa3, 0x4c, 0x87, 0x6a, 0x3c, 0xf9, 0x0c, + 0x9a, 0x41, 0x9a, 0x3b, 0x4d, 0x1d, 0xf6, 0xe1, 0xcd, 0x61, 0x87, 0xfd, 0x53, 0x15, 0x75, 0xd0, + 0x9a, 0x5e, 0x75, 0x9b, 0x87, 0xfd, 0x53, 0x57, 0x85, 0x91, 0xcf, 0x60, 0x29, 0xa6, 0x31, 0x17, + 0x97, 0x8e, 0xa5, 0x09, 0x1e, 0xdf, 0x4c, 0x70, 0xa4, 0x71, 0x3a, 0xb3, 0x89, 0x21, 0xcf, 0x61, + 0x71, 0x18, 0x9d, 0x31, 0xee, 0x2c, 0xea, 0xe0, 0x9d, 0x9b, 0x83, 0x0f, 0xa2, 0xb3, 0x57, 0xc7, + 0x3a, 0x16, 0x23, 0xd4, 0x76, 0xc5, 0x28, 0xf6, 0x9d, 0xa5, 0xdb, 0xb6, 0xeb, 0x8e, 0x62, 0x1f, + 0xb7, 0xab, 0xf0, 0xaa, 0xcf, 0x09, 0x95, 0x17, 0x5c, 0x9c, 0x39, 0xad, 0xdb, 0xfa, 0xfc, 0x07, + 0x04, 0x62, 0x9f, 0x4d, 0x14, 0x79, 0x09, 0xcb, 0x08, 0xf1, 0xb4, 0x0a, 0x9c, 0xb6, 0x2e, 0xe0, + 0x1d, 0x2c, 0x87, 0xfa, 0xa7, 0x22, 0xc9, 0xdc, 0x4e, 0x50, 0x19, 0xe4, 0x3b, 0x20, 0xd8, 0x07, + 0x8f, 0xf3, 0xd8, 0x53, 0xc1, 0x82, 0x47, 0x8e, 0xad, 0xf9, 0x3e, 0xba, 0xad, 0x8f, 0xc7, 0x3c, + 0x3e, 0xc4, 0x08, 0x77, 0x2d, 0xbe, 0xe6, 0xe9, 0x9d, 0x41, 0xa7, 0xa6, 0x11, 0x72, 0x0f, 0x16, + 0xf3, 0xcc, 0x0f, 0xa9, 0xd3, 0xd8, 0x6e, 0xec, 0x5a, 0x2e, 0x1a, 0x64, 0x0d, 0x9a, 0xb1, 0x3f, + 0xd1, 0x7a, 0xb1, 0x5c, 0xf5, 0x93, 0x38, 0xd0, 0x7a, 0xed, 0xb3, 0x28, 0x48, 0xa4, 0x96, 0x83, + 0xe5, 0x16, 0x26, 0xd9, 0x84, 0x76, 0xea, 0x87, 0x34, 0x63, 0x7f, 0xa2, 0xfa, 0x41, 0xdb, 0x6e, + 0x69, 0xf7, 0x3e, 0x85, 0x76, 0x21, 0x29, 0xc5, 0x10, 0xe4, 0x42, 0xd0, 0x44, 0x9a, 0x5c, 0x85, + 0xa9, 0x6a, 0x88, 0x58, 0xcc, 0xa4, 0xc9, 0x87, 0x46, 0xef, 0xaf, 0x0d, 0x68, 0x19, 0x61, 0x91, + 0xdf, 0xd4, 0xab, 0x7c, 0xe7, 0x23, 0x3d, 0xec, 0x9f, 0x9e, 0x2a, 0x64, 0xb1, 0x93, 0x03, 0x00, + 0x39, 0x16, 0x5c, 0xca, 0x88, 0x25, 0xe1, 0xed, 0x07, 0xe0, 0x04, 0xb1, 0xd4, 0xad, 0x45, 0xf5, + 0xbe, 0x87, 0x76, 0x41, 0xab, 0x6a, 0x95, 0x5c, 0xfa, 0x51, 0xd1, 0x2f, 0x6d, 0x90, 0x07, 0xb0, + 0x74, 0x46, 0x45, 0x42, 0x23, 0xb3, 0x05, 0x63, 0x11, 0x02, 0x56, 0x9e, 0x51, 0x61, 0x5a, 0xa6, + 0x7f, 0x93, 0x1d, 0x68, 0xa5, 0x54, 0x78, 0xea, 0x60, 0x59, 0xdb, 0xcd, 0x5d, 0xeb, 0x00, 0xa6, + 0x57, 0xdd, 0xa5, 0x3e, 0x15, 0xea, 0xe0, 0x2c, 0xa5, 0x54, 0x1c, 0xa6, 0x79, 0x6f, 0x02, 0xed, + 0xa2, 0x14, 0xd5, 0xb8, 0x94, 0x0a, 0xc6, 0x47, 0x59, 0xd1, 0x38, 0x63, 0x92, 0xa7, 0xb0, 0x6e, + 0xca, 0xa4, 0x23, 0xaf, 0xc0, 0x60, 0x05, 0x6b, 0xe5, 0x42, 0xdf, 0x80, 0x9f, 0xc0, 0x6a, 0x05, + 0x96, 0x2c, 0xa6, 0xa6, 0xaa, 0x95, 0xd2, 0x7b, 0xc2, 0x62, 0xda, 0xfb, 0x4f, 0x07, 0xa0, 0x3a, + 0x8e, 0x6a, 0xbf, 0x81, 0x1f, 0x8c, 0x4b, 0x7d, 0x68, 0x83, 0x6c, 0x40, 0x53, 0x64, 0x26, 0x15, + 0x9e, 0x7a, 0x77, 0x30, 0x70, 0x95, 0x8f, 0xfc, 0x0c, 0xda, 0x22, 0xcb, 0x3c, 0x75, 0xf5, 0x60, + 0x82, 0x83, 0xce, 0xf4, 0xaa, 0xdb, 0x72, 0x07, 0x03, 0x25, 0x3b, 0xb7, 0x25, 0xb2, 0x4c, 0xfd, + 0x20, 0x5d, 0xe8, 0xc4, 0x7e, 0x9a, 0xd2, 0x91, 0xf7, 0x9a, 0x45, 0xa8, 0x1c, 0xcb, 0x05, 0x74, + 0x7d, 0xc5, 0x22, 0xdd, 0xe9, 0x11, 0x13, 0xf2, 0x52, 0x5f, 0x00, 0x96, 0x8b, 0x06, 0x79, 0x04, + 0xf6, 0x85, 0x60, 0x92, 0x0e, 0xfd, 0xe0, 0x4c, 0x1f, 0x70, 0xcb, 0xad, 0x1c, 0xc4, 0x81, 0x76, + 0x1a, 0x7a, 0x69, 0xe8, 0xb1, 0xc4, 0x69, 0xe1, 0x93, 0x48, 0xc3, 0x7e, 0xf8, 0x2a, 0x21, 0x9b, + 0x60, 0xe3, 0x0a, 0xcf, 0xa5, 0x3e, 0x97, 0xaa, 0x8d, 0x61, 0x3f, 0x3c, 0xce, 0x25, 0xd9, 0xd0, + 0x51, 0xaf, 0xfd, 0x3c, 0x92, 0xfa, 0x88, 0xe9, 0xa5, 0xaf, 0x94, 0x49, 0xb6, 0x61, 0x39, 0x0d, + 0xbd, 0xd8, 0x7f, 0x63, 0x96, 0x01, 0xcb, 0x4c, 0xc3, 0x23, 0xff, 0x0d, 0x22, 0x76, 0x60, 0x85, + 0x25, 0x7e, 0x20, 0xd9, 0x39, 0xf5, 0xfc, 0x84, 0x27, 0x4e, 0x47, 0x43, 0x96, 0x0b, 0xe7, 0x17, + 0x09, 0x4f, 0xd4, 0x66, 0xeb, 0x90, 0x65, 0x64, 0xa9, 0x01, 0xea, 0x2c, 0xba, 0x1f, 0x2b, 0xb3, + 0x2c, 0xba, 0x23, 0x15, 0x8b, 0x86, 0xac, 0xd6, 0x59, 0x34, 0x60, 0x1b, 0x3a, 0x79, 0x42, 0xcf, + 0x59, 0x20, 0xfd, 0x61, 0x44, 0x9d, 0x3b, 0x1a, 0x50, 0x77, 0x91, 0x4f, 0x61, 0x63, 0xcc, 0xa8, + 0xf0, 0x45, 0x30, 0x66, 0x81, 0x1f, 0x79, 0xe6, 0x92, 0xc1, 0xe3, 0xb7, 0xa6, 0xf1, 0x0f, 0xeb, + 0x00, 0x54, 0xc2, 0xef, 0xd5, 0x32, 0x79, 0x06, 0x33, 0x4b, 0x5e, 0x76, 0xe1, 0xa7, 0x26, 0x72, + 0x5d, 0x47, 0xde, 0xaf, 0x2f, 0x0f, 0x2e, 0xfc, 0x14, 0xe3, 0xba, 0xd0, 0xd1, 0xa7, 0xc4, 0x43, + 0x21, 0x11, 0x2c, 0x5b, 0xbb, 0x0e, 0xb5, 0x9a, 0x7e, 0x01, 0x36, 0x02, 0x94, 0xa6, 0xee, 0x6a, + 0xcd, 0x2c, 0x4f, 0xaf, 0xba, 0xed, 0x13, 0xe5, 0x54, 0xc2, 0x6a, 0xeb, 0x65, 0x37, 0xcb, 0xc8, + 0x33, 0x58, 0x2d, 0xa1, 0xa8, 0xb1, 0x7b, 0x1a, 0xbf, 0x36, 0xbd, 0xea, 0x2e, 0x17, 0x78, 0x2d, + 0xb4, 0xe5, 0x22, 0x46, 0xab, 0xed, 0x23, 0x58, 0xc7, 0xb8, 0xba, 0xe6, 0xee, 0xeb, 0x4a, 0xee, + 0xe8, 0x85, 0xa3, 0x4a, 0x78, 0x65, 0xbd, 0x28, 0xbf, 0x07, 0xb5, 0x7a, 0x5f, 0x68, 0x0d, 0xfe, + 0x1c, 0x30, 0xc6, 0xab, 0x94, 0xf8, 0x50, 0x83, 0xb0, 0xb6, 0x6f, 0x4b, 0x39, 0xee, 0x14, 0xd5, + 0x96, 0xa2, 0x74, 0xf0, 0x91, 0x68, 0x6f, 0x1f, 0x95, 0xf9, 0xa4, 0x60, 0xab, 0xf4, 0xb9, 0x81, + 0x0f, 0xbf, 0x44, 0x29, 0x91, 0x3e, 0xae, 0x71, 0xa1, 0x16, 0x37, 0x67, 0x50, 0xa8, 0xc6, 0xa7, + 0x40, 0x4a, 0x54, 0xa5, 0xda, 0xf7, 0x6b, 0x1b, 0xed, 0x57, 0xd2, 0xdd, 0x83, 0xbb, 0x08, 0x9e, + 0x15, 0xf0, 0x23, 0x8d, 0xc6, 0x7e, 0xbd, 0xaa, 0xab, 0xb8, 0x6c, 0x62, 0x1d, 0xfd, 0x41, 0x8d, + 0xfb, 0x8b, 0x0a, 0xfb, 0x53, 0x6e, 0xdd, 0xf2, 0xad, 0xb7, 0x70, 0xeb, 0xa6, 0x5f, 0xe7, 0xd6, + 0xe8, 0xee, 0x4f, 0xb8, 0x35, 0xf6, 0x69, 0x81, 0xad, 0x8b, 0x7d, 0xdb, 0x5c, 0x7b, 0x6a, 0xe1, + 0xb4, 0xa6, 0xf8, 0xdf, 0x16, 0xaf, 0x8e, 0x0f, 0x6f, 0x7b, 0x19, 0xa3, 0xd6, 0xbf, 0x4c, 0xa4, + 0xb8, 0x2c, 0xde, 0x1e, 0xcf, 0xc1, 0x52, 0x2a, 0x77, 0x7a, 0xf3, 0xc4, 0xea, 0x10, 0xf2, 0x79, + 0xf9, 0x4a, 0xd8, 0x99, 0x27, 0xb8, 0x78, 0x73, 0x0c, 0x00, 0xf0, 0x97, 0x27, 0x83, 0xd4, 0x79, + 0x3c, 0x07, 0xc5, 0xc1, 0xca, 0xf4, 0xaa, 0x6b, 0x7f, 0xad, 0x83, 0x4f, 0x0e, 0xfb, 0xae, 0x8d, + 0x3c, 0x27, 0x41, 0xda, 0xa3, 0xd0, 0xa9, 0x01, 0xab, 0xf7, 0x6e, 0xa3, 0xf6, 0xde, 0xad, 0x26, + 0x82, 0x85, 0xb7, 0x4c, 0x04, 0xcd, 0xb7, 0x4e, 0x04, 0xd6, 0xcc, 0x44, 0xd0, 0x93, 0xb0, 0x76, + 0x7d, 0x10, 0x21, 0xbb, 0xb0, 0xa6, 0x26, 0x99, 0x33, 0x16, 0xa9, 0x73, 0x95, 0xe9, 0x47, 0x86, + 0x69, 0x57, 0x39, 0x8f, 0xbf, 0x66, 0x51, 0xf4, 0x02, 0xbd, 0xe4, 0x7d, 0xb0, 0xf3, 0x64, 0x44, + 0x85, 0x9a, 0x7c, 0x4c, 0x0d, 0x6d, 0xed, 0x38, 0xe6, 0xb1, 0xba, 0xaa, 0x0b, 0x9a, 0x62, 0x0e, + 0x31, 0xe1, 0xbd, 0x7f, 0x2e, 0x82, 0x5d, 0x8e, 0x82, 0xc4, 0x87, 0x4d, 0xc6, 0xbd, 0x8c, 0x8a, + 0x73, 0x16, 0x50, 0x6f, 0x78, 0x29, 0x69, 0xe6, 0x09, 0x1a, 0xe4, 0x22, 0x63, 0xe7, 0xd4, 0x8c, + 0xd1, 0x8f, 0x6f, 0x99, 0x29, 0xf1, 0x89, 0x3c, 0x64, 0x7c, 0x80, 0x34, 0x07, 0x8a, 0xc5, 0x2d, + 0x48, 0xc8, 0x77, 0x70, 0xbf, 0x4a, 0x31, 0xaa, 0xb1, 0x2f, 0xcc, 0xc1, 0x7e, 0xb7, 0x64, 0x1f, + 0x55, 0xcc, 0x27, 0x70, 0x97, 0x71, 0xef, 0xfb, 0x9c, 0xe6, 0x33, 0xbc, 0xcd, 0x39, 0x78, 0xd7, + 0x19, 0xff, 0x46, 0xc7, 0x57, 0xac, 0x1e, 0x6c, 0xd4, 0x5a, 0xa2, 0x26, 0x80, 0x1a, 0xb7, 0x35, + 0x07, 0xf7, 0x83, 0xb2, 0x66, 0x35, 0x31, 0x54, 0x09, 0xfe, 0x08, 0x0f, 0x18, 0xf7, 0x2e, 0x7c, + 0x26, 0xaf, 0xb3, 0x2f, 0xce, 0xd7, 0x91, 0x6f, 0x7d, 0x26, 0x67, 0xa9, 0xb1, 0x23, 0x31, 0x15, + 0xe1, 0x4c, 0x47, 0x96, 0xe6, 0xeb, 0xc8, 0x91, 0x8e, 0xaf, 0x58, 0xfb, 0xb0, 0xce, 0xf8, 0xf5, + 0x5a, 0x5b, 0x73, 0x70, 0xde, 0x61, 0x7c, 0xb6, 0xce, 0x6f, 0x60, 0x3d, 0xa3, 0x81, 0xe4, 0xa2, + 0xae, 0xb6, 0xf6, 0x1c, 0x8c, 0x6b, 0x26, 0xbc, 0xa4, 0xec, 0x9d, 0x03, 0x54, 0xeb, 0x64, 0x15, + 0x16, 0x78, 0xaa, 0x4f, 0x8e, 0xed, 0x2e, 0xf0, 0x54, 0x4d, 0x9e, 0x23, 0x75, 0xd9, 0xe1, 0x71, + 0xb5, 0x5d, 0x63, 0xa9, 0x53, 0x1c, 0xfb, 0x6f, 0x78, 0x31, 0x7a, 0xa2, 0xa1, 0xbd, 0x2c, 0xe1, + 0xc2, 0x9c, 0x58, 0x34, 0x94, 0xf7, 0xdc, 0x8f, 0x72, 0x5a, 0x4c, 0x5a, 0xda, 0xe8, 0xfd, 0xa5, + 0x01, 0xed, 0xe2, 0x03, 0x89, 0x7c, 0x5e, 0x1f, 0xde, 0x9b, 0xef, 0xfe, 0x1e, 0x53, 0x41, 0xb8, + 0x99, 0x72, 0xc2, 0x7f, 0x5e, 0x4d, 0xf8, 0xff, 0x77, 0xb0, 0xf9, 0x0c, 0xa0, 0x60, 0x97, 0xbe, + 0xda, 0x6e, 0x1b, 0x33, 0xbb, 0xed, 0x42, 0x67, 0x1c, 0xf8, 0xde, 0xd8, 0x4f, 0x46, 0x11, 0xc5, + 0xb9, 0x74, 0xc5, 0x85, 0x71, 0xe0, 0xbf, 0x44, 0x4f, 0x01, 0xe0, 0xc3, 0x37, 0x34, 0x90, 0x99, + 0x6e, 0x0a, 0x02, 0x8e, 0xd1, 0xd3, 0xfb, 0xdb, 0x02, 0x74, 0x6a, 0xdf, 0x74, 0x6a, 0x72, 0x4f, + 0xfc, 0xb8, 0xc8, 0xa3, 0x7f, 0xab, 0xcb, 0x47, 0x4c, 0xf0, 0x2e, 0x31, 0x17, 0x53, 0x4b, 0x4c, + 0xf4, 0xa5, 0x40, 0x3e, 0x00, 0x10, 0x13, 0x2f, 0xf5, 0x83, 0x33, 0x6a, 0xe8, 0x2d, 0xd7, 0x16, + 0x93, 0x3e, 0x3a, 0xd4, 0x9d, 0x26, 0x26, 0x1e, 0x15, 0x82, 0x8b, 0xcc, 0xf4, 0xbe, 0x2d, 0x26, + 0x5f, 0x6a, 0xdb, 0xc4, 0x8e, 0x04, 0x57, 0x13, 0x88, 0x79, 0x06, 0xb6, 0x98, 0xbc, 0x40, 0x87, + 0xca, 0x2a, 0x8b, 0xac, 0x38, 0xf0, 0xb6, 0x64, 0x95, 0x55, 0x56, 0x59, 0x71, 0xe0, 0xb5, 0x65, + 0x3d, 0xab, 0x2c, 0xb3, 0xe2, 0xcc, 0xdb, 0x96, 0xb5, 0xac, 0xb2, 0xca, 0x6a, 0x17, 0xb1, 0x26, + 0x6b, 0xef, 0xef, 0x0d, 0xe8, 0xd4, 0xbe, 0x4e, 0x55, 0x03, 0x13, 0xe1, 0x65, 0x11, 0xa5, 0xa9, + 0xfa, 0x90, 0xc2, 0xab, 0x1b, 0x12, 0x31, 0x30, 0x1e, 0xc5, 0x97, 0x08, 0x4f, 0xe4, 0x49, 0x52, + 0x7c, 0x68, 0x59, 0xae, 0x9d, 0x08, 0x17, 0x1d, 0x66, 0x39, 0x93, 0x98, 0xae, 0x59, 0x2c, 0x0f, + 0xd0, 0x41, 0x7e, 0x09, 0x24, 0x11, 0x5e, 0x9e, 0xb0, 0x44, 0x52, 0x21, 0xf2, 0x54, 0xb2, 0x61, + 0xf9, 0x51, 0xb0, 0x9e, 0x88, 0xd3, 0xd9, 0x05, 0xf2, 0x48, 0xb3, 0x99, 0xcb, 0xc6, 0xb4, 0xac, + 0x9d, 0x88, 0x57, 0xfa, 0xe6, 0x38, 0x70, 0x7e, 0xf8, 0x71, 0xeb, 0xbd, 0x7f, 0xff, 0xb8, 0xf5, + 0xde, 0x9f, 0xa7, 0x5b, 0x8d, 0x1f, 0xa6, 0x5b, 0x8d, 0x7f, 0x4c, 0xb7, 0x1a, 0xff, 0x9d, 0x6e, + 0x35, 0x86, 0x4b, 0xfa, 0xcf, 0x95, 0x5f, 0xfd, 0x2f, 0x00, 0x00, 0xff, 0xff, 0xc4, 0x4e, 0x24, + 0x22, 0xc4, 0x11, 0x00, 0x00, +} + +func (m *Metrics) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Metrics) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Metrics) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.MemoryOomControl != nil { + { + size, err := m.MemoryOomControl.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + if m.CgroupStats != nil { + { + size, err := m.CgroupStats.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + if len(m.Network) > 0 { + for iNdEx := len(m.Network) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Network[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + if m.Rdma != nil { + { + size, err := m.Rdma.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if m.Blkio != nil { + { + size, err := m.Blkio.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if m.Memory != nil { + { + size, err := m.Memory.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.CPU != nil { + { + size, err := m.CPU.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Pids != nil { + { + size, err := m.Pids.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Hugetlb) > 0 { + for iNdEx := len(m.Hugetlb) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Hugetlb[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *HugetlbStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *HugetlbStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HugetlbStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Pagesize) > 0 { + i -= len(m.Pagesize) + copy(dAtA[i:], m.Pagesize) + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Pagesize))) + i-- + dAtA[i] = 0x22 + } + if m.Failcnt != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Failcnt)) + i-- + dAtA[i] = 0x18 + } + if m.Max != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Max)) + i-- + dAtA[i] = 0x10 + } + if m.Usage != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Usage)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *PidsStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PidsStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PidsStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Limit != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Limit)) + i-- + dAtA[i] = 0x10 + } + if m.Current != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Current)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *CPUStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CPUStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CPUStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Throttling != nil { + { + size, err := m.Throttling.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Usage != nil { + { + size, err := m.Usage.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CPUUsage) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CPUUsage) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CPUUsage) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.PerCPU) > 0 { + dAtA11 := make([]byte, len(m.PerCPU)*10) + var j10 int + for _, num := range m.PerCPU { + for num >= 1<<7 { + dAtA11[j10] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j10++ + } + dAtA11[j10] = uint8(num) + j10++ + } + i -= j10 + copy(dAtA[i:], dAtA11[:j10]) + i = encodeVarintMetrics(dAtA, i, uint64(j10)) + i-- + dAtA[i] = 0x22 + } + if m.User != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.User)) + i-- + dAtA[i] = 0x18 + } + if m.Kernel != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Kernel)) + i-- + dAtA[i] = 0x10 + } + if m.Total != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Total)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Throttle) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Throttle) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Throttle) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.ThrottledTime != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.ThrottledTime)) + i-- + dAtA[i] = 0x18 + } + if m.ThrottledPeriods != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.ThrottledPeriods)) + i-- + dAtA[i] = 0x10 + } + if m.Periods != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Periods)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MemoryStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MemoryStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MemoryStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.KernelTCP != nil { + { + size, err := m.KernelTCP.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0xa2 + } + if m.Kernel != nil { + { + size, err := m.Kernel.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0x9a + } + if m.Swap != nil { + { + size, err := m.Swap.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0x92 + } + if m.Usage != nil { + { + size, err := m.Usage.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0x8a + } + if m.TotalUnevictable != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalUnevictable)) + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0x80 + } + if m.TotalActiveFile != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalActiveFile)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xf8 + } + if m.TotalInactiveFile != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalInactiveFile)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xf0 + } + if m.TotalActiveAnon != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalActiveAnon)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xe8 + } + if m.TotalInactiveAnon != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalInactiveAnon)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xe0 + } + if m.TotalPgMajFault != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalPgMajFault)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xd8 + } + if m.TotalPgFault != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalPgFault)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xd0 + } + if m.TotalPgPgOut != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalPgPgOut)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xc8 + } + if m.TotalPgPgIn != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalPgPgIn)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xc0 + } + if m.TotalWriteback != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalWriteback)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xb8 + } + if m.TotalDirty != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalDirty)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xb0 + } + if m.TotalMappedFile != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalMappedFile)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa8 + } + if m.TotalRSSHuge != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalRSSHuge)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa0 + } + if m.TotalRSS != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalRSS)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x98 + } + if m.TotalCache != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalCache)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x90 + } + if m.HierarchicalSwapLimit != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.HierarchicalSwapLimit)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x88 + } + if m.HierarchicalMemoryLimit != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.HierarchicalMemoryLimit)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x80 + } + if m.Unevictable != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Unevictable)) + i-- + dAtA[i] = 0x78 + } + if m.ActiveFile != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.ActiveFile)) + i-- + dAtA[i] = 0x70 + } + if m.InactiveFile != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.InactiveFile)) + i-- + dAtA[i] = 0x68 + } + if m.ActiveAnon != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.ActiveAnon)) + i-- + dAtA[i] = 0x60 + } + if m.InactiveAnon != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.InactiveAnon)) + i-- + dAtA[i] = 0x58 + } + if m.PgMajFault != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.PgMajFault)) + i-- + dAtA[i] = 0x50 + } + if m.PgFault != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.PgFault)) + i-- + dAtA[i] = 0x48 + } + if m.PgPgOut != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.PgPgOut)) + i-- + dAtA[i] = 0x40 + } + if m.PgPgIn != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.PgPgIn)) + i-- + dAtA[i] = 0x38 + } + if m.Writeback != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Writeback)) + i-- + dAtA[i] = 0x30 + } + if m.Dirty != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Dirty)) + i-- + dAtA[i] = 0x28 + } + if m.MappedFile != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.MappedFile)) + i-- + dAtA[i] = 0x20 + } + if m.RSSHuge != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.RSSHuge)) + i-- + dAtA[i] = 0x18 + } + if m.RSS != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.RSS)) + i-- + dAtA[i] = 0x10 + } + if m.Cache != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Cache)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MemoryEntry) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MemoryEntry) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MemoryEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Failcnt != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Failcnt)) + i-- + dAtA[i] = 0x20 + } + if m.Max != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Max)) + i-- + dAtA[i] = 0x18 + } + if m.Usage != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Usage)) + i-- + dAtA[i] = 0x10 + } + if m.Limit != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Limit)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MemoryOomControl) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MemoryOomControl) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MemoryOomControl) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.OomKill != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.OomKill)) + i-- + dAtA[i] = 0x18 + } + if m.UnderOom != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.UnderOom)) + i-- + dAtA[i] = 0x10 + } + if m.OomKillDisable != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.OomKillDisable)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *BlkIOStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BlkIOStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BlkIOStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.SectorsRecursive) > 0 { + for iNdEx := len(m.SectorsRecursive) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SectorsRecursive[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + } + if len(m.IoTimeRecursive) > 0 { + for iNdEx := len(m.IoTimeRecursive) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IoTimeRecursive[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + if len(m.IoMergedRecursive) > 0 { + for iNdEx := len(m.IoMergedRecursive) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IoMergedRecursive[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + if len(m.IoWaitTimeRecursive) > 0 { + for iNdEx := len(m.IoWaitTimeRecursive) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IoWaitTimeRecursive[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.IoServiceTimeRecursive) > 0 { + for iNdEx := len(m.IoServiceTimeRecursive) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IoServiceTimeRecursive[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.IoQueuedRecursive) > 0 { + for iNdEx := len(m.IoQueuedRecursive) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IoQueuedRecursive[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.IoServicedRecursive) > 0 { + for iNdEx := len(m.IoServicedRecursive) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IoServicedRecursive[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.IoServiceBytesRecursive) > 0 { + for iNdEx := len(m.IoServiceBytesRecursive) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IoServiceBytesRecursive[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *BlkIOEntry) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BlkIOEntry) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BlkIOEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Value != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Value)) + i-- + dAtA[i] = 0x28 + } + if m.Minor != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Minor)) + i-- + dAtA[i] = 0x20 + } + if m.Major != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Major)) + i-- + dAtA[i] = 0x18 + } + if len(m.Device) > 0 { + i -= len(m.Device) + copy(dAtA[i:], m.Device) + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Device))) + i-- + dAtA[i] = 0x12 + } + if len(m.Op) > 0 { + i -= len(m.Op) + copy(dAtA[i:], m.Op) + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Op))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RdmaStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RdmaStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RdmaStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Limit) > 0 { + for iNdEx := len(m.Limit) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Limit[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Current) > 0 { + for iNdEx := len(m.Current) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Current[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *RdmaEntry) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RdmaEntry) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RdmaEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.HcaObjects != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.HcaObjects)) + i-- + dAtA[i] = 0x18 + } + if m.HcaHandles != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.HcaHandles)) + i-- + dAtA[i] = 0x10 + } + if len(m.Device) > 0 { + i -= len(m.Device) + copy(dAtA[i:], m.Device) + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Device))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *NetworkStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NetworkStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NetworkStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.TxDropped != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TxDropped)) + i-- + dAtA[i] = 0x48 + } + if m.TxErrors != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TxErrors)) + i-- + dAtA[i] = 0x40 + } + if m.TxPackets != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TxPackets)) + i-- + dAtA[i] = 0x38 + } + if m.TxBytes != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TxBytes)) + i-- + dAtA[i] = 0x30 + } + if m.RxDropped != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.RxDropped)) + i-- + dAtA[i] = 0x28 + } + if m.RxErrors != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.RxErrors)) + i-- + dAtA[i] = 0x20 + } + if m.RxPackets != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.RxPackets)) + i-- + dAtA[i] = 0x18 + } + if m.RxBytes != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.RxBytes)) + i-- + dAtA[i] = 0x10 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CgroupStats) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CgroupStats) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CgroupStats) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.NrIoWait != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.NrIoWait)) + i-- + dAtA[i] = 0x28 + } + if m.NrUninterruptible != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.NrUninterruptible)) + i-- + dAtA[i] = 0x20 + } + if m.NrStopped != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.NrStopped)) + i-- + dAtA[i] = 0x18 + } + if m.NrRunning != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.NrRunning)) + i-- + dAtA[i] = 0x10 + } + if m.NrSleeping != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.NrSleeping)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintMetrics(dAtA []byte, offset int, v uint64) int { + offset -= sovMetrics(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Metrics) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Hugetlb) > 0 { + for _, e := range m.Hugetlb { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if m.Pids != nil { + l = m.Pids.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.CPU != nil { + l = m.CPU.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Memory != nil { + l = m.Memory.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Blkio != nil { + l = m.Blkio.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Rdma != nil { + l = m.Rdma.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if len(m.Network) > 0 { + for _, e := range m.Network { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if m.CgroupStats != nil { + l = m.CgroupStats.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.MemoryOomControl != nil { + l = m.MemoryOomControl.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *HugetlbStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Usage != 0 { + n += 1 + sovMetrics(uint64(m.Usage)) + } + if m.Max != 0 { + n += 1 + sovMetrics(uint64(m.Max)) + } + if m.Failcnt != 0 { + n += 1 + sovMetrics(uint64(m.Failcnt)) + } + l = len(m.Pagesize) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *PidsStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Current != 0 { + n += 1 + sovMetrics(uint64(m.Current)) + } + if m.Limit != 0 { + n += 1 + sovMetrics(uint64(m.Limit)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *CPUStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Usage != nil { + l = m.Usage.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Throttling != nil { + l = m.Throttling.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *CPUUsage) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Total != 0 { + n += 1 + sovMetrics(uint64(m.Total)) + } + if m.Kernel != 0 { + n += 1 + sovMetrics(uint64(m.Kernel)) + } + if m.User != 0 { + n += 1 + sovMetrics(uint64(m.User)) + } + if len(m.PerCPU) > 0 { + l = 0 + for _, e := range m.PerCPU { + l += sovMetrics(uint64(e)) + } + n += 1 + sovMetrics(uint64(l)) + l + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *Throttle) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Periods != 0 { + n += 1 + sovMetrics(uint64(m.Periods)) + } + if m.ThrottledPeriods != 0 { + n += 1 + sovMetrics(uint64(m.ThrottledPeriods)) + } + if m.ThrottledTime != 0 { + n += 1 + sovMetrics(uint64(m.ThrottledTime)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *MemoryStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Cache != 0 { + n += 1 + sovMetrics(uint64(m.Cache)) + } + if m.RSS != 0 { + n += 1 + sovMetrics(uint64(m.RSS)) + } + if m.RSSHuge != 0 { + n += 1 + sovMetrics(uint64(m.RSSHuge)) + } + if m.MappedFile != 0 { + n += 1 + sovMetrics(uint64(m.MappedFile)) + } + if m.Dirty != 0 { + n += 1 + sovMetrics(uint64(m.Dirty)) + } + if m.Writeback != 0 { + n += 1 + sovMetrics(uint64(m.Writeback)) + } + if m.PgPgIn != 0 { + n += 1 + sovMetrics(uint64(m.PgPgIn)) + } + if m.PgPgOut != 0 { + n += 1 + sovMetrics(uint64(m.PgPgOut)) + } + if m.PgFault != 0 { + n += 1 + sovMetrics(uint64(m.PgFault)) + } + if m.PgMajFault != 0 { + n += 1 + sovMetrics(uint64(m.PgMajFault)) + } + if m.InactiveAnon != 0 { + n += 1 + sovMetrics(uint64(m.InactiveAnon)) + } + if m.ActiveAnon != 0 { + n += 1 + sovMetrics(uint64(m.ActiveAnon)) + } + if m.InactiveFile != 0 { + n += 1 + sovMetrics(uint64(m.InactiveFile)) + } + if m.ActiveFile != 0 { + n += 1 + sovMetrics(uint64(m.ActiveFile)) + } + if m.Unevictable != 0 { + n += 1 + sovMetrics(uint64(m.Unevictable)) + } + if m.HierarchicalMemoryLimit != 0 { + n += 2 + sovMetrics(uint64(m.HierarchicalMemoryLimit)) + } + if m.HierarchicalSwapLimit != 0 { + n += 2 + sovMetrics(uint64(m.HierarchicalSwapLimit)) + } + if m.TotalCache != 0 { + n += 2 + sovMetrics(uint64(m.TotalCache)) + } + if m.TotalRSS != 0 { + n += 2 + sovMetrics(uint64(m.TotalRSS)) + } + if m.TotalRSSHuge != 0 { + n += 2 + sovMetrics(uint64(m.TotalRSSHuge)) + } + if m.TotalMappedFile != 0 { + n += 2 + sovMetrics(uint64(m.TotalMappedFile)) + } + if m.TotalDirty != 0 { + n += 2 + sovMetrics(uint64(m.TotalDirty)) + } + if m.TotalWriteback != 0 { + n += 2 + sovMetrics(uint64(m.TotalWriteback)) + } + if m.TotalPgPgIn != 0 { + n += 2 + sovMetrics(uint64(m.TotalPgPgIn)) + } + if m.TotalPgPgOut != 0 { + n += 2 + sovMetrics(uint64(m.TotalPgPgOut)) + } + if m.TotalPgFault != 0 { + n += 2 + sovMetrics(uint64(m.TotalPgFault)) + } + if m.TotalPgMajFault != 0 { + n += 2 + sovMetrics(uint64(m.TotalPgMajFault)) + } + if m.TotalInactiveAnon != 0 { + n += 2 + sovMetrics(uint64(m.TotalInactiveAnon)) + } + if m.TotalActiveAnon != 0 { + n += 2 + sovMetrics(uint64(m.TotalActiveAnon)) + } + if m.TotalInactiveFile != 0 { + n += 2 + sovMetrics(uint64(m.TotalInactiveFile)) + } + if m.TotalActiveFile != 0 { + n += 2 + sovMetrics(uint64(m.TotalActiveFile)) + } + if m.TotalUnevictable != 0 { + n += 2 + sovMetrics(uint64(m.TotalUnevictable)) + } + if m.Usage != nil { + l = m.Usage.Size() + n += 2 + l + sovMetrics(uint64(l)) + } + if m.Swap != nil { + l = m.Swap.Size() + n += 2 + l + sovMetrics(uint64(l)) + } + if m.Kernel != nil { + l = m.Kernel.Size() + n += 2 + l + sovMetrics(uint64(l)) + } + if m.KernelTCP != nil { + l = m.KernelTCP.Size() + n += 2 + l + sovMetrics(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *MemoryEntry) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Limit != 0 { + n += 1 + sovMetrics(uint64(m.Limit)) + } + if m.Usage != 0 { + n += 1 + sovMetrics(uint64(m.Usage)) + } + if m.Max != 0 { + n += 1 + sovMetrics(uint64(m.Max)) + } + if m.Failcnt != 0 { + n += 1 + sovMetrics(uint64(m.Failcnt)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *MemoryOomControl) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.OomKillDisable != 0 { + n += 1 + sovMetrics(uint64(m.OomKillDisable)) + } + if m.UnderOom != 0 { + n += 1 + sovMetrics(uint64(m.UnderOom)) + } + if m.OomKill != 0 { + n += 1 + sovMetrics(uint64(m.OomKill)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *BlkIOStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.IoServiceBytesRecursive) > 0 { + for _, e := range m.IoServiceBytesRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoServicedRecursive) > 0 { + for _, e := range m.IoServicedRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoQueuedRecursive) > 0 { + for _, e := range m.IoQueuedRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoServiceTimeRecursive) > 0 { + for _, e := range m.IoServiceTimeRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoWaitTimeRecursive) > 0 { + for _, e := range m.IoWaitTimeRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoMergedRecursive) > 0 { + for _, e := range m.IoMergedRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoTimeRecursive) > 0 { + for _, e := range m.IoTimeRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.SectorsRecursive) > 0 { + for _, e := range m.SectorsRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *BlkIOEntry) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Op) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + l = len(m.Device) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Major != 0 { + n += 1 + sovMetrics(uint64(m.Major)) + } + if m.Minor != 0 { + n += 1 + sovMetrics(uint64(m.Minor)) + } + if m.Value != 0 { + n += 1 + sovMetrics(uint64(m.Value)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *RdmaStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Current) > 0 { + for _, e := range m.Current { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.Limit) > 0 { + for _, e := range m.Limit { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *RdmaEntry) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Device) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + if m.HcaHandles != 0 { + n += 1 + sovMetrics(uint64(m.HcaHandles)) + } + if m.HcaObjects != 0 { + n += 1 + sovMetrics(uint64(m.HcaObjects)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *NetworkStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + if m.RxBytes != 0 { + n += 1 + sovMetrics(uint64(m.RxBytes)) + } + if m.RxPackets != 0 { + n += 1 + sovMetrics(uint64(m.RxPackets)) + } + if m.RxErrors != 0 { + n += 1 + sovMetrics(uint64(m.RxErrors)) + } + if m.RxDropped != 0 { + n += 1 + sovMetrics(uint64(m.RxDropped)) + } + if m.TxBytes != 0 { + n += 1 + sovMetrics(uint64(m.TxBytes)) + } + if m.TxPackets != 0 { + n += 1 + sovMetrics(uint64(m.TxPackets)) + } + if m.TxErrors != 0 { + n += 1 + sovMetrics(uint64(m.TxErrors)) + } + if m.TxDropped != 0 { + n += 1 + sovMetrics(uint64(m.TxDropped)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *CgroupStats) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.NrSleeping != 0 { + n += 1 + sovMetrics(uint64(m.NrSleeping)) + } + if m.NrRunning != 0 { + n += 1 + sovMetrics(uint64(m.NrRunning)) + } + if m.NrStopped != 0 { + n += 1 + sovMetrics(uint64(m.NrStopped)) + } + if m.NrUninterruptible != 0 { + n += 1 + sovMetrics(uint64(m.NrUninterruptible)) + } + if m.NrIoWait != 0 { + n += 1 + sovMetrics(uint64(m.NrIoWait)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovMetrics(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozMetrics(x uint64) (n int) { + return sovMetrics(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Metrics) String() string { + if this == nil { + return "nil" + } + repeatedStringForHugetlb := "[]*HugetlbStat{" + for _, f := range this.Hugetlb { + repeatedStringForHugetlb += strings.Replace(f.String(), "HugetlbStat", "HugetlbStat", 1) + "," + } + repeatedStringForHugetlb += "}" + repeatedStringForNetwork := "[]*NetworkStat{" + for _, f := range this.Network { + repeatedStringForNetwork += strings.Replace(f.String(), "NetworkStat", "NetworkStat", 1) + "," + } + repeatedStringForNetwork += "}" + s := strings.Join([]string{`&Metrics{`, + `Hugetlb:` + repeatedStringForHugetlb + `,`, + `Pids:` + strings.Replace(this.Pids.String(), "PidsStat", "PidsStat", 1) + `,`, + `CPU:` + strings.Replace(this.CPU.String(), "CPUStat", "CPUStat", 1) + `,`, + `Memory:` + strings.Replace(this.Memory.String(), "MemoryStat", "MemoryStat", 1) + `,`, + `Blkio:` + strings.Replace(this.Blkio.String(), "BlkIOStat", "BlkIOStat", 1) + `,`, + `Rdma:` + strings.Replace(this.Rdma.String(), "RdmaStat", "RdmaStat", 1) + `,`, + `Network:` + repeatedStringForNetwork + `,`, + `CgroupStats:` + strings.Replace(this.CgroupStats.String(), "CgroupStats", "CgroupStats", 1) + `,`, + `MemoryOomControl:` + strings.Replace(this.MemoryOomControl.String(), "MemoryOomControl", "MemoryOomControl", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *HugetlbStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&HugetlbStat{`, + `Usage:` + fmt.Sprintf("%v", this.Usage) + `,`, + `Max:` + fmt.Sprintf("%v", this.Max) + `,`, + `Failcnt:` + fmt.Sprintf("%v", this.Failcnt) + `,`, + `Pagesize:` + fmt.Sprintf("%v", this.Pagesize) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *PidsStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&PidsStat{`, + `Current:` + fmt.Sprintf("%v", this.Current) + `,`, + `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *CPUStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CPUStat{`, + `Usage:` + strings.Replace(this.Usage.String(), "CPUUsage", "CPUUsage", 1) + `,`, + `Throttling:` + strings.Replace(this.Throttling.String(), "Throttle", "Throttle", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *CPUUsage) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CPUUsage{`, + `Total:` + fmt.Sprintf("%v", this.Total) + `,`, + `Kernel:` + fmt.Sprintf("%v", this.Kernel) + `,`, + `User:` + fmt.Sprintf("%v", this.User) + `,`, + `PerCPU:` + fmt.Sprintf("%v", this.PerCPU) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *Throttle) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Throttle{`, + `Periods:` + fmt.Sprintf("%v", this.Periods) + `,`, + `ThrottledPeriods:` + fmt.Sprintf("%v", this.ThrottledPeriods) + `,`, + `ThrottledTime:` + fmt.Sprintf("%v", this.ThrottledTime) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *MemoryStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&MemoryStat{`, + `Cache:` + fmt.Sprintf("%v", this.Cache) + `,`, + `RSS:` + fmt.Sprintf("%v", this.RSS) + `,`, + `RSSHuge:` + fmt.Sprintf("%v", this.RSSHuge) + `,`, + `MappedFile:` + fmt.Sprintf("%v", this.MappedFile) + `,`, + `Dirty:` + fmt.Sprintf("%v", this.Dirty) + `,`, + `Writeback:` + fmt.Sprintf("%v", this.Writeback) + `,`, + `PgPgIn:` + fmt.Sprintf("%v", this.PgPgIn) + `,`, + `PgPgOut:` + fmt.Sprintf("%v", this.PgPgOut) + `,`, + `PgFault:` + fmt.Sprintf("%v", this.PgFault) + `,`, + `PgMajFault:` + fmt.Sprintf("%v", this.PgMajFault) + `,`, + `InactiveAnon:` + fmt.Sprintf("%v", this.InactiveAnon) + `,`, + `ActiveAnon:` + fmt.Sprintf("%v", this.ActiveAnon) + `,`, + `InactiveFile:` + fmt.Sprintf("%v", this.InactiveFile) + `,`, + `ActiveFile:` + fmt.Sprintf("%v", this.ActiveFile) + `,`, + `Unevictable:` + fmt.Sprintf("%v", this.Unevictable) + `,`, + `HierarchicalMemoryLimit:` + fmt.Sprintf("%v", this.HierarchicalMemoryLimit) + `,`, + `HierarchicalSwapLimit:` + fmt.Sprintf("%v", this.HierarchicalSwapLimit) + `,`, + `TotalCache:` + fmt.Sprintf("%v", this.TotalCache) + `,`, + `TotalRSS:` + fmt.Sprintf("%v", this.TotalRSS) + `,`, + `TotalRSSHuge:` + fmt.Sprintf("%v", this.TotalRSSHuge) + `,`, + `TotalMappedFile:` + fmt.Sprintf("%v", this.TotalMappedFile) + `,`, + `TotalDirty:` + fmt.Sprintf("%v", this.TotalDirty) + `,`, + `TotalWriteback:` + fmt.Sprintf("%v", this.TotalWriteback) + `,`, + `TotalPgPgIn:` + fmt.Sprintf("%v", this.TotalPgPgIn) + `,`, + `TotalPgPgOut:` + fmt.Sprintf("%v", this.TotalPgPgOut) + `,`, + `TotalPgFault:` + fmt.Sprintf("%v", this.TotalPgFault) + `,`, + `TotalPgMajFault:` + fmt.Sprintf("%v", this.TotalPgMajFault) + `,`, + `TotalInactiveAnon:` + fmt.Sprintf("%v", this.TotalInactiveAnon) + `,`, + `TotalActiveAnon:` + fmt.Sprintf("%v", this.TotalActiveAnon) + `,`, + `TotalInactiveFile:` + fmt.Sprintf("%v", this.TotalInactiveFile) + `,`, + `TotalActiveFile:` + fmt.Sprintf("%v", this.TotalActiveFile) + `,`, + `TotalUnevictable:` + fmt.Sprintf("%v", this.TotalUnevictable) + `,`, + `Usage:` + strings.Replace(this.Usage.String(), "MemoryEntry", "MemoryEntry", 1) + `,`, + `Swap:` + strings.Replace(this.Swap.String(), "MemoryEntry", "MemoryEntry", 1) + `,`, + `Kernel:` + strings.Replace(this.Kernel.String(), "MemoryEntry", "MemoryEntry", 1) + `,`, + `KernelTCP:` + strings.Replace(this.KernelTCP.String(), "MemoryEntry", "MemoryEntry", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *MemoryEntry) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&MemoryEntry{`, + `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, + `Usage:` + fmt.Sprintf("%v", this.Usage) + `,`, + `Max:` + fmt.Sprintf("%v", this.Max) + `,`, + `Failcnt:` + fmt.Sprintf("%v", this.Failcnt) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *MemoryOomControl) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&MemoryOomControl{`, + `OomKillDisable:` + fmt.Sprintf("%v", this.OomKillDisable) + `,`, + `UnderOom:` + fmt.Sprintf("%v", this.UnderOom) + `,`, + `OomKill:` + fmt.Sprintf("%v", this.OomKill) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *BlkIOStat) String() string { + if this == nil { + return "nil" + } + repeatedStringForIoServiceBytesRecursive := "[]*BlkIOEntry{" + for _, f := range this.IoServiceBytesRecursive { + repeatedStringForIoServiceBytesRecursive += strings.Replace(f.String(), "BlkIOEntry", "BlkIOEntry", 1) + "," + } + repeatedStringForIoServiceBytesRecursive += "}" + repeatedStringForIoServicedRecursive := "[]*BlkIOEntry{" + for _, f := range this.IoServicedRecursive { + repeatedStringForIoServicedRecursive += strings.Replace(f.String(), "BlkIOEntry", "BlkIOEntry", 1) + "," + } + repeatedStringForIoServicedRecursive += "}" + repeatedStringForIoQueuedRecursive := "[]*BlkIOEntry{" + for _, f := range this.IoQueuedRecursive { + repeatedStringForIoQueuedRecursive += strings.Replace(f.String(), "BlkIOEntry", "BlkIOEntry", 1) + "," + } + repeatedStringForIoQueuedRecursive += "}" + repeatedStringForIoServiceTimeRecursive := "[]*BlkIOEntry{" + for _, f := range this.IoServiceTimeRecursive { + repeatedStringForIoServiceTimeRecursive += strings.Replace(f.String(), "BlkIOEntry", "BlkIOEntry", 1) + "," + } + repeatedStringForIoServiceTimeRecursive += "}" + repeatedStringForIoWaitTimeRecursive := "[]*BlkIOEntry{" + for _, f := range this.IoWaitTimeRecursive { + repeatedStringForIoWaitTimeRecursive += strings.Replace(f.String(), "BlkIOEntry", "BlkIOEntry", 1) + "," + } + repeatedStringForIoWaitTimeRecursive += "}" + repeatedStringForIoMergedRecursive := "[]*BlkIOEntry{" + for _, f := range this.IoMergedRecursive { + repeatedStringForIoMergedRecursive += strings.Replace(f.String(), "BlkIOEntry", "BlkIOEntry", 1) + "," + } + repeatedStringForIoMergedRecursive += "}" + repeatedStringForIoTimeRecursive := "[]*BlkIOEntry{" + for _, f := range this.IoTimeRecursive { + repeatedStringForIoTimeRecursive += strings.Replace(f.String(), "BlkIOEntry", "BlkIOEntry", 1) + "," + } + repeatedStringForIoTimeRecursive += "}" + repeatedStringForSectorsRecursive := "[]*BlkIOEntry{" + for _, f := range this.SectorsRecursive { + repeatedStringForSectorsRecursive += strings.Replace(f.String(), "BlkIOEntry", "BlkIOEntry", 1) + "," + } + repeatedStringForSectorsRecursive += "}" + s := strings.Join([]string{`&BlkIOStat{`, + `IoServiceBytesRecursive:` + repeatedStringForIoServiceBytesRecursive + `,`, + `IoServicedRecursive:` + repeatedStringForIoServicedRecursive + `,`, + `IoQueuedRecursive:` + repeatedStringForIoQueuedRecursive + `,`, + `IoServiceTimeRecursive:` + repeatedStringForIoServiceTimeRecursive + `,`, + `IoWaitTimeRecursive:` + repeatedStringForIoWaitTimeRecursive + `,`, + `IoMergedRecursive:` + repeatedStringForIoMergedRecursive + `,`, + `IoTimeRecursive:` + repeatedStringForIoTimeRecursive + `,`, + `SectorsRecursive:` + repeatedStringForSectorsRecursive + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *BlkIOEntry) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&BlkIOEntry{`, + `Op:` + fmt.Sprintf("%v", this.Op) + `,`, + `Device:` + fmt.Sprintf("%v", this.Device) + `,`, + `Major:` + fmt.Sprintf("%v", this.Major) + `,`, + `Minor:` + fmt.Sprintf("%v", this.Minor) + `,`, + `Value:` + fmt.Sprintf("%v", this.Value) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *RdmaStat) String() string { + if this == nil { + return "nil" + } + repeatedStringForCurrent := "[]*RdmaEntry{" + for _, f := range this.Current { + repeatedStringForCurrent += strings.Replace(f.String(), "RdmaEntry", "RdmaEntry", 1) + "," + } + repeatedStringForCurrent += "}" + repeatedStringForLimit := "[]*RdmaEntry{" + for _, f := range this.Limit { + repeatedStringForLimit += strings.Replace(f.String(), "RdmaEntry", "RdmaEntry", 1) + "," + } + repeatedStringForLimit += "}" + s := strings.Join([]string{`&RdmaStat{`, + `Current:` + repeatedStringForCurrent + `,`, + `Limit:` + repeatedStringForLimit + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *RdmaEntry) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RdmaEntry{`, + `Device:` + fmt.Sprintf("%v", this.Device) + `,`, + `HcaHandles:` + fmt.Sprintf("%v", this.HcaHandles) + `,`, + `HcaObjects:` + fmt.Sprintf("%v", this.HcaObjects) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *NetworkStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&NetworkStat{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `RxBytes:` + fmt.Sprintf("%v", this.RxBytes) + `,`, + `RxPackets:` + fmt.Sprintf("%v", this.RxPackets) + `,`, + `RxErrors:` + fmt.Sprintf("%v", this.RxErrors) + `,`, + `RxDropped:` + fmt.Sprintf("%v", this.RxDropped) + `,`, + `TxBytes:` + fmt.Sprintf("%v", this.TxBytes) + `,`, + `TxPackets:` + fmt.Sprintf("%v", this.TxPackets) + `,`, + `TxErrors:` + fmt.Sprintf("%v", this.TxErrors) + `,`, + `TxDropped:` + fmt.Sprintf("%v", this.TxDropped) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *CgroupStats) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CgroupStats{`, + `NrSleeping:` + fmt.Sprintf("%v", this.NrSleeping) + `,`, + `NrRunning:` + fmt.Sprintf("%v", this.NrRunning) + `,`, + `NrStopped:` + fmt.Sprintf("%v", this.NrStopped) + `,`, + `NrUninterruptible:` + fmt.Sprintf("%v", this.NrUninterruptible) + `,`, + `NrIoWait:` + fmt.Sprintf("%v", this.NrIoWait) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func valueToStringMetrics(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Metrics) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Metrics: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Metrics: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hugetlb", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hugetlb = append(m.Hugetlb, &HugetlbStat{}) + if err := m.Hugetlb[len(m.Hugetlb)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pids", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pids == nil { + m.Pids = &PidsStat{} + } + if err := m.Pids.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CPU", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CPU == nil { + m.CPU = &CPUStat{} + } + if err := m.CPU.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Memory", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Memory == nil { + m.Memory = &MemoryStat{} + } + if err := m.Memory.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Blkio", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Blkio == nil { + m.Blkio = &BlkIOStat{} + } + if err := m.Blkio.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rdma", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Rdma == nil { + m.Rdma = &RdmaStat{} + } + if err := m.Rdma.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Network", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Network = append(m.Network, &NetworkStat{}) + if err := m.Network[len(m.Network)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CgroupStats", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CgroupStats == nil { + m.CgroupStats = &CgroupStats{} + } + if err := m.CgroupStats.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MemoryOomControl", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.MemoryOomControl == nil { + m.MemoryOomControl = &MemoryOomControl{} + } + if err := m.MemoryOomControl.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *HugetlbStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: HugetlbStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HugetlbStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) + } + m.Usage = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Usage |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Max", wireType) + } + m.Max = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Max |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Failcnt", wireType) + } + m.Failcnt = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Failcnt |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagesize", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Pagesize = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PidsStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PidsStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PidsStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Current", wireType) + } + m.Current = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Current |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CPUStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CPUStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CPUStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Usage == nil { + m.Usage = &CPUUsage{} + } + if err := m.Usage.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Throttling", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Throttling == nil { + m.Throttling = &Throttle{} + } + if err := m.Throttling.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CPUUsage) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CPUUsage: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CPUUsage: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Total", wireType) + } + m.Total = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Total |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Kernel", wireType) + } + m.Kernel = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Kernel |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) + } + m.User = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.User |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.PerCPU = append(m.PerCPU, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.PerCPU) == 0 { + m.PerCPU = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.PerCPU = append(m.PerCPU, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field PerCPU", wireType) + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Throttle) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Throttle: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Throttle: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Periods", wireType) + } + m.Periods = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Periods |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ThrottledPeriods", wireType) + } + m.ThrottledPeriods = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ThrottledPeriods |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ThrottledTime", wireType) + } + m.ThrottledTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ThrottledTime |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MemoryStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MemoryStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MemoryStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cache", wireType) + } + m.Cache = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Cache |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RSS", wireType) + } + m.RSS = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RSS |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RSSHuge", wireType) + } + m.RSSHuge = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RSSHuge |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MappedFile", wireType) + } + m.MappedFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MappedFile |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Dirty", wireType) + } + m.Dirty = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Dirty |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Writeback", wireType) + } + m.Writeback = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Writeback |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PgPgIn", wireType) + } + m.PgPgIn = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PgPgIn |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PgPgOut", wireType) + } + m.PgPgOut = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PgPgOut |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PgFault", wireType) + } + m.PgFault = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PgFault |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PgMajFault", wireType) + } + m.PgMajFault = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PgMajFault |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InactiveAnon", wireType) + } + m.InactiveAnon = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.InactiveAnon |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ActiveAnon", wireType) + } + m.ActiveAnon = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ActiveAnon |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InactiveFile", wireType) + } + m.InactiveFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.InactiveFile |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 14: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ActiveFile", wireType) + } + m.ActiveFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ActiveFile |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 15: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Unevictable", wireType) + } + m.Unevictable = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Unevictable |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 16: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HierarchicalMemoryLimit", wireType) + } + m.HierarchicalMemoryLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HierarchicalMemoryLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 17: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HierarchicalSwapLimit", wireType) + } + m.HierarchicalSwapLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HierarchicalSwapLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 18: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalCache", wireType) + } + m.TotalCache = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalCache |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 19: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalRSS", wireType) + } + m.TotalRSS = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalRSS |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 20: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalRSSHuge", wireType) + } + m.TotalRSSHuge = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalRSSHuge |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 21: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalMappedFile", wireType) + } + m.TotalMappedFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalMappedFile |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 22: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalDirty", wireType) + } + m.TotalDirty = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalDirty |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 23: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalWriteback", wireType) + } + m.TotalWriteback = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalWriteback |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 24: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalPgPgIn", wireType) + } + m.TotalPgPgIn = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalPgPgIn |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 25: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalPgPgOut", wireType) + } + m.TotalPgPgOut = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalPgPgOut |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 26: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalPgFault", wireType) + } + m.TotalPgFault = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalPgFault |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 27: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalPgMajFault", wireType) + } + m.TotalPgMajFault = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalPgMajFault |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 28: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalInactiveAnon", wireType) + } + m.TotalInactiveAnon = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalInactiveAnon |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 29: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalActiveAnon", wireType) + } + m.TotalActiveAnon = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalActiveAnon |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 30: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalInactiveFile", wireType) + } + m.TotalInactiveFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalInactiveFile |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 31: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalActiveFile", wireType) + } + m.TotalActiveFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalActiveFile |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 32: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalUnevictable", wireType) + } + m.TotalUnevictable = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalUnevictable |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 33: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Usage == nil { + m.Usage = &MemoryEntry{} + } + if err := m.Usage.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 34: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Swap", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Swap == nil { + m.Swap = &MemoryEntry{} + } + if err := m.Swap.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 35: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Kernel", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Kernel == nil { + m.Kernel = &MemoryEntry{} + } + if err := m.Kernel.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 36: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field KernelTCP", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.KernelTCP == nil { + m.KernelTCP = &MemoryEntry{} + } + if err := m.KernelTCP.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MemoryEntry) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MemoryEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MemoryEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) + } + m.Usage = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Usage |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Max", wireType) + } + m.Max = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Max |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Failcnt", wireType) + } + m.Failcnt = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Failcnt |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MemoryOomControl) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MemoryOomControl: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MemoryOomControl: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OomKillDisable", wireType) + } + m.OomKillDisable = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OomKillDisable |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UnderOom", wireType) + } + m.UnderOom = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UnderOom |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OomKill", wireType) + } + m.OomKill = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OomKill |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BlkIOStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BlkIOStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BlkIOStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoServiceBytesRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoServiceBytesRecursive = append(m.IoServiceBytesRecursive, &BlkIOEntry{}) + if err := m.IoServiceBytesRecursive[len(m.IoServiceBytesRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoServicedRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoServicedRecursive = append(m.IoServicedRecursive, &BlkIOEntry{}) + if err := m.IoServicedRecursive[len(m.IoServicedRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoQueuedRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoQueuedRecursive = append(m.IoQueuedRecursive, &BlkIOEntry{}) + if err := m.IoQueuedRecursive[len(m.IoQueuedRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoServiceTimeRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoServiceTimeRecursive = append(m.IoServiceTimeRecursive, &BlkIOEntry{}) + if err := m.IoServiceTimeRecursive[len(m.IoServiceTimeRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoWaitTimeRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoWaitTimeRecursive = append(m.IoWaitTimeRecursive, &BlkIOEntry{}) + if err := m.IoWaitTimeRecursive[len(m.IoWaitTimeRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoMergedRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoMergedRecursive = append(m.IoMergedRecursive, &BlkIOEntry{}) + if err := m.IoMergedRecursive[len(m.IoMergedRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoTimeRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoTimeRecursive = append(m.IoTimeRecursive, &BlkIOEntry{}) + if err := m.IoTimeRecursive[len(m.IoTimeRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SectorsRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SectorsRecursive = append(m.SectorsRecursive, &BlkIOEntry{}) + if err := m.SectorsRecursive[len(m.SectorsRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BlkIOEntry) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BlkIOEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BlkIOEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Op", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Op = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Device = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Major", wireType) + } + m.Major = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Major |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Minor", wireType) + } + m.Minor = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Minor |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + m.Value = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Value |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RdmaStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RdmaStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RdmaStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Current", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Current = append(m.Current, &RdmaEntry{}) + if err := m.Current[len(m.Current)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Limit = append(m.Limit, &RdmaEntry{}) + if err := m.Limit[len(m.Limit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RdmaEntry) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RdmaEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RdmaEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Device = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HcaHandles", wireType) + } + m.HcaHandles = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HcaHandles |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HcaObjects", wireType) + } + m.HcaObjects = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HcaObjects |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NetworkStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NetworkStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NetworkStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RxBytes", wireType) + } + m.RxBytes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RxBytes |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RxPackets", wireType) + } + m.RxPackets = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RxPackets |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RxErrors", wireType) + } + m.RxErrors = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RxErrors |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RxDropped", wireType) + } + m.RxDropped = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RxDropped |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxBytes", wireType) + } + m.TxBytes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxBytes |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxPackets", wireType) + } + m.TxPackets = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxPackets |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxErrors", wireType) + } + m.TxErrors = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxErrors |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxDropped", wireType) + } + m.TxDropped = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxDropped |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CgroupStats) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CgroupStats: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CgroupStats: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NrSleeping", wireType) + } + m.NrSleeping = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NrSleeping |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NrRunning", wireType) + } + m.NrRunning = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NrRunning |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NrStopped", wireType) + } + m.NrStopped = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NrStopped |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NrUninterruptible", wireType) + } + m.NrUninterruptible = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NrUninterruptible |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NrIoWait", wireType) + } + m.NrIoWait = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NrIoWait |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMetrics(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthMetrics + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMetrics + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthMetrics + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthMetrics = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMetrics = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMetrics = fmt.Errorf("proto: unexpected end of group") +) diff --git a/vendor/github.com/containerd/cgroups/stats/v1/metrics.pb.txt b/vendor/github.com/containerd/cgroups/stats/v1/metrics.pb.txt new file mode 100644 index 0000000000..e476cea641 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/stats/v1/metrics.pb.txt @@ -0,0 +1,790 @@ +file { + name: "github.com/containerd/cgroups/stats/v1/metrics.proto" + package: "io.containerd.cgroups.v1" + dependency: "gogoproto/gogo.proto" + message_type { + name: "Metrics" + field { + name: "hugetlb" + number: 1 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.HugetlbStat" + json_name: "hugetlb" + } + field { + name: "pids" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.PidsStat" + json_name: "pids" + } + field { + name: "cpu" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.CPUStat" + options { + 65004: "CPU" + } + json_name: "cpu" + } + field { + name: "memory" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.MemoryStat" + json_name: "memory" + } + field { + name: "blkio" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOStat" + json_name: "blkio" + } + field { + name: "rdma" + number: 6 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.RdmaStat" + json_name: "rdma" + } + field { + name: "network" + number: 7 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.NetworkStat" + json_name: "network" + } + field { + name: "cgroup_stats" + number: 8 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.CgroupStats" + json_name: "cgroupStats" + } + field { + name: "memory_oom_control" + number: 9 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.MemoryOomControl" + json_name: "memoryOomControl" + } + } + message_type { + name: "HugetlbStat" + field { + name: "usage" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "usage" + } + field { + name: "max" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "max" + } + field { + name: "failcnt" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "failcnt" + } + field { + name: "pagesize" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "pagesize" + } + } + message_type { + name: "PidsStat" + field { + name: "current" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "current" + } + field { + name: "limit" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "limit" + } + } + message_type { + name: "CPUStat" + field { + name: "usage" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.CPUUsage" + json_name: "usage" + } + field { + name: "throttling" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.Throttle" + json_name: "throttling" + } + } + message_type { + name: "CPUUsage" + field { + name: "total" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "total" + } + field { + name: "kernel" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "kernel" + } + field { + name: "user" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "user" + } + field { + name: "per_cpu" + number: 4 + label: LABEL_REPEATED + type: TYPE_UINT64 + options { + 65004: "PerCPU" + } + json_name: "perCpu" + } + } + message_type { + name: "Throttle" + field { + name: "periods" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "periods" + } + field { + name: "throttled_periods" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "throttledPeriods" + } + field { + name: "throttled_time" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "throttledTime" + } + } + message_type { + name: "MemoryStat" + field { + name: "cache" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "cache" + } + field { + name: "rss" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + options { + 65004: "RSS" + } + json_name: "rss" + } + field { + name: "rss_huge" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + options { + 65004: "RSSHuge" + } + json_name: "rssHuge" + } + field { + name: "mapped_file" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "mappedFile" + } + field { + name: "dirty" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "dirty" + } + field { + name: "writeback" + number: 6 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "writeback" + } + field { + name: "pg_pg_in" + number: 7 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "pgPgIn" + } + field { + name: "pg_pg_out" + number: 8 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "pgPgOut" + } + field { + name: "pg_fault" + number: 9 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "pgFault" + } + field { + name: "pg_maj_fault" + number: 10 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "pgMajFault" + } + field { + name: "inactive_anon" + number: 11 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "inactiveAnon" + } + field { + name: "active_anon" + number: 12 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "activeAnon" + } + field { + name: "inactive_file" + number: 13 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "inactiveFile" + } + field { + name: "active_file" + number: 14 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "activeFile" + } + field { + name: "unevictable" + number: 15 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "unevictable" + } + field { + name: "hierarchical_memory_limit" + number: 16 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "hierarchicalMemoryLimit" + } + field { + name: "hierarchical_swap_limit" + number: 17 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "hierarchicalSwapLimit" + } + field { + name: "total_cache" + number: 18 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalCache" + } + field { + name: "total_rss" + number: 19 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + options { + 65004: "TotalRSS" + } + json_name: "totalRss" + } + field { + name: "total_rss_huge" + number: 20 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + options { + 65004: "TotalRSSHuge" + } + json_name: "totalRssHuge" + } + field { + name: "total_mapped_file" + number: 21 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalMappedFile" + } + field { + name: "total_dirty" + number: 22 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalDirty" + } + field { + name: "total_writeback" + number: 23 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalWriteback" + } + field { + name: "total_pg_pg_in" + number: 24 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalPgPgIn" + } + field { + name: "total_pg_pg_out" + number: 25 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalPgPgOut" + } + field { + name: "total_pg_fault" + number: 26 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalPgFault" + } + field { + name: "total_pg_maj_fault" + number: 27 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalPgMajFault" + } + field { + name: "total_inactive_anon" + number: 28 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalInactiveAnon" + } + field { + name: "total_active_anon" + number: 29 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalActiveAnon" + } + field { + name: "total_inactive_file" + number: 30 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalInactiveFile" + } + field { + name: "total_active_file" + number: 31 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalActiveFile" + } + field { + name: "total_unevictable" + number: 32 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalUnevictable" + } + field { + name: "usage" + number: 33 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.MemoryEntry" + json_name: "usage" + } + field { + name: "swap" + number: 34 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.MemoryEntry" + json_name: "swap" + } + field { + name: "kernel" + number: 35 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.MemoryEntry" + json_name: "kernel" + } + field { + name: "kernel_tcp" + number: 36 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.MemoryEntry" + options { + 65004: "KernelTCP" + } + json_name: "kernelTcp" + } + } + message_type { + name: "MemoryEntry" + field { + name: "limit" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "limit" + } + field { + name: "usage" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "usage" + } + field { + name: "max" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "max" + } + field { + name: "failcnt" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "failcnt" + } + } + message_type { + name: "MemoryOomControl" + field { + name: "oom_kill_disable" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "oomKillDisable" + } + field { + name: "under_oom" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "underOom" + } + field { + name: "oom_kill" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "oomKill" + } + } + message_type { + name: "BlkIOStat" + field { + name: "io_service_bytes_recursive" + number: 1 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOEntry" + json_name: "ioServiceBytesRecursive" + } + field { + name: "io_serviced_recursive" + number: 2 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOEntry" + json_name: "ioServicedRecursive" + } + field { + name: "io_queued_recursive" + number: 3 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOEntry" + json_name: "ioQueuedRecursive" + } + field { + name: "io_service_time_recursive" + number: 4 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOEntry" + json_name: "ioServiceTimeRecursive" + } + field { + name: "io_wait_time_recursive" + number: 5 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOEntry" + json_name: "ioWaitTimeRecursive" + } + field { + name: "io_merged_recursive" + number: 6 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOEntry" + json_name: "ioMergedRecursive" + } + field { + name: "io_time_recursive" + number: 7 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOEntry" + json_name: "ioTimeRecursive" + } + field { + name: "sectors_recursive" + number: 8 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOEntry" + json_name: "sectorsRecursive" + } + } + message_type { + name: "BlkIOEntry" + field { + name: "op" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "op" + } + field { + name: "device" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "device" + } + field { + name: "major" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "major" + } + field { + name: "minor" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "minor" + } + field { + name: "value" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "value" + } + } + message_type { + name: "RdmaStat" + field { + name: "current" + number: 1 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.RdmaEntry" + json_name: "current" + } + field { + name: "limit" + number: 2 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.RdmaEntry" + json_name: "limit" + } + } + message_type { + name: "RdmaEntry" + field { + name: "device" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "device" + } + field { + name: "hca_handles" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT32 + json_name: "hcaHandles" + } + field { + name: "hca_objects" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT32 + json_name: "hcaObjects" + } + } + message_type { + name: "NetworkStat" + field { + name: "name" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "name" + } + field { + name: "rx_bytes" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "rxBytes" + } + field { + name: "rx_packets" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "rxPackets" + } + field { + name: "rx_errors" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "rxErrors" + } + field { + name: "rx_dropped" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "rxDropped" + } + field { + name: "tx_bytes" + number: 6 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "txBytes" + } + field { + name: "tx_packets" + number: 7 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "txPackets" + } + field { + name: "tx_errors" + number: 8 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "txErrors" + } + field { + name: "tx_dropped" + number: 9 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "txDropped" + } + } + message_type { + name: "CgroupStats" + field { + name: "nr_sleeping" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "nrSleeping" + } + field { + name: "nr_running" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "nrRunning" + } + field { + name: "nr_stopped" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "nrStopped" + } + field { + name: "nr_uninterruptible" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "nrUninterruptible" + } + field { + name: "nr_io_wait" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "nrIoWait" + } + } + syntax: "proto3" +} diff --git a/vendor/github.com/containerd/cgroups/stats/v1/metrics.proto b/vendor/github.com/containerd/cgroups/stats/v1/metrics.proto new file mode 100644 index 0000000000..b3f6cc37d8 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/stats/v1/metrics.proto @@ -0,0 +1,158 @@ +syntax = "proto3"; + +package io.containerd.cgroups.v1; + +import "gogoproto/gogo.proto"; + +message Metrics { + repeated HugetlbStat hugetlb = 1; + PidsStat pids = 2; + CPUStat cpu = 3 [(gogoproto.customname) = "CPU"]; + MemoryStat memory = 4; + BlkIOStat blkio = 5; + RdmaStat rdma = 6; + repeated NetworkStat network = 7; + CgroupStats cgroup_stats = 8; + MemoryOomControl memory_oom_control = 9; +} + +message HugetlbStat { + uint64 usage = 1; + uint64 max = 2; + uint64 failcnt = 3; + string pagesize = 4; +} + +message PidsStat { + uint64 current = 1; + uint64 limit = 2; +} + +message CPUStat { + CPUUsage usage = 1; + Throttle throttling = 2; +} + +message CPUUsage { + // values in nanoseconds + uint64 total = 1; + uint64 kernel = 2; + uint64 user = 3; + repeated uint64 per_cpu = 4 [(gogoproto.customname) = "PerCPU"]; + +} + +message Throttle { + uint64 periods = 1; + uint64 throttled_periods = 2; + uint64 throttled_time = 3; +} + +message MemoryStat { + uint64 cache = 1; + uint64 rss = 2 [(gogoproto.customname) = "RSS"]; + uint64 rss_huge = 3 [(gogoproto.customname) = "RSSHuge"]; + uint64 mapped_file = 4; + uint64 dirty = 5; + uint64 writeback = 6; + uint64 pg_pg_in = 7; + uint64 pg_pg_out = 8; + uint64 pg_fault = 9; + uint64 pg_maj_fault = 10; + uint64 inactive_anon = 11; + uint64 active_anon = 12; + uint64 inactive_file = 13; + uint64 active_file = 14; + uint64 unevictable = 15; + uint64 hierarchical_memory_limit = 16; + uint64 hierarchical_swap_limit = 17; + uint64 total_cache = 18; + uint64 total_rss = 19 [(gogoproto.customname) = "TotalRSS"]; + uint64 total_rss_huge = 20 [(gogoproto.customname) = "TotalRSSHuge"]; + uint64 total_mapped_file = 21; + uint64 total_dirty = 22; + uint64 total_writeback = 23; + uint64 total_pg_pg_in = 24; + uint64 total_pg_pg_out = 25; + uint64 total_pg_fault = 26; + uint64 total_pg_maj_fault = 27; + uint64 total_inactive_anon = 28; + uint64 total_active_anon = 29; + uint64 total_inactive_file = 30; + uint64 total_active_file = 31; + uint64 total_unevictable = 32; + MemoryEntry usage = 33; + MemoryEntry swap = 34; + MemoryEntry kernel = 35; + MemoryEntry kernel_tcp = 36 [(gogoproto.customname) = "KernelTCP"]; + +} + +message MemoryEntry { + uint64 limit = 1; + uint64 usage = 2; + uint64 max = 3; + uint64 failcnt = 4; +} + +message MemoryOomControl { + uint64 oom_kill_disable = 1; + uint64 under_oom = 2; + uint64 oom_kill = 3; +} + +message BlkIOStat { + repeated BlkIOEntry io_service_bytes_recursive = 1; + repeated BlkIOEntry io_serviced_recursive = 2; + repeated BlkIOEntry io_queued_recursive = 3; + repeated BlkIOEntry io_service_time_recursive = 4; + repeated BlkIOEntry io_wait_time_recursive = 5; + repeated BlkIOEntry io_merged_recursive = 6; + repeated BlkIOEntry io_time_recursive = 7; + repeated BlkIOEntry sectors_recursive = 8; +} + +message BlkIOEntry { + string op = 1; + string device = 2; + uint64 major = 3; + uint64 minor = 4; + uint64 value = 5; +} + +message RdmaStat { + repeated RdmaEntry current = 1; + repeated RdmaEntry limit = 2; +} + +message RdmaEntry { + string device = 1; + uint32 hca_handles = 2; + uint32 hca_objects = 3; +} + +message NetworkStat { + string name = 1; + uint64 rx_bytes = 2; + uint64 rx_packets = 3; + uint64 rx_errors = 4; + uint64 rx_dropped = 5; + uint64 tx_bytes = 6; + uint64 tx_packets = 7; + uint64 tx_errors = 8; + uint64 tx_dropped = 9; +} + +// CgroupStats exports per-cgroup statistics. +message CgroupStats { + // number of tasks sleeping + uint64 nr_sleeping = 1; + // number of tasks running + uint64 nr_running = 2; + // number of tasks in stopped state + uint64 nr_stopped = 3; + // number of tasks in uninterruptible state + uint64 nr_uninterruptible = 4; + // number of tasks waiting on IO + uint64 nr_io_wait = 5; +} diff --git a/vendor/github.com/containerd/containerd/LICENSE b/vendor/github.com/containerd/containerd/LICENSE new file mode 100644 index 0000000000..584149b6ee --- /dev/null +++ b/vendor/github.com/containerd/containerd/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright The containerd Authors + + 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 + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/containerd/containerd/NOTICE b/vendor/github.com/containerd/containerd/NOTICE new file mode 100644 index 0000000000..8915f02773 --- /dev/null +++ b/vendor/github.com/containerd/containerd/NOTICE @@ -0,0 +1,16 @@ +Docker +Copyright 2012-2015 Docker, Inc. + +This product includes software developed at Docker, Inc. (https://www.docker.com). + +The following is courtesy of our legal counsel: + + +Use and transfer of Docker may be subject to certain restrictions by the +United States and other governments. +It is your responsibility to ensure that your use and/or transfer does not +violate applicable laws. + +For more information, please see https://www.bis.doc.gov + +See also https://www.apache.org/dev/crypto.html and/or seek legal counsel. diff --git a/vendor/github.com/containerd/containerd/errdefs/errors.go b/vendor/github.com/containerd/containerd/errdefs/errors.go new file mode 100644 index 0000000000..8762255970 --- /dev/null +++ b/vendor/github.com/containerd/containerd/errdefs/errors.go @@ -0,0 +1,92 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +// Package errdefs defines the common errors used throughout containerd +// packages. +// +// Use with fmt.Errorf to add context to an error. +// +// To detect an error class, use the IsXXX functions to tell whether an error +// is of a certain type. +// +// The functions ToGRPC and FromGRPC can be used to map server-side and +// client-side errors to the correct types. +package errdefs + +import ( + "context" + "errors" +) + +// Definitions of common error types used throughout containerd. All containerd +// errors returned by most packages will map into one of these errors classes. +// Packages should return errors of these types when they want to instruct a +// client to take a particular action. +// +// For the most part, we just try to provide local grpc errors. Most conditions +// map very well to those defined by grpc. +var ( + ErrUnknown = errors.New("unknown") // used internally to represent a missed mapping. + ErrInvalidArgument = errors.New("invalid argument") + ErrNotFound = errors.New("not found") + ErrAlreadyExists = errors.New("already exists") + ErrFailedPrecondition = errors.New("failed precondition") + ErrUnavailable = errors.New("unavailable") + ErrNotImplemented = errors.New("not implemented") // represents not supported and unimplemented +) + +// IsInvalidArgument returns true if the error is due to an invalid argument +func IsInvalidArgument(err error) bool { + return errors.Is(err, ErrInvalidArgument) +} + +// IsNotFound returns true if the error is due to a missing object +func IsNotFound(err error) bool { + return errors.Is(err, ErrNotFound) +} + +// IsAlreadyExists returns true if the error is due to an already existing +// metadata item +func IsAlreadyExists(err error) bool { + return errors.Is(err, ErrAlreadyExists) +} + +// IsFailedPrecondition returns true if an operation could not proceed to the +// lack of a particular condition +func IsFailedPrecondition(err error) bool { + return errors.Is(err, ErrFailedPrecondition) +} + +// IsUnavailable returns true if the error is due to a resource being unavailable +func IsUnavailable(err error) bool { + return errors.Is(err, ErrUnavailable) +} + +// IsNotImplemented returns true if the error is due to not being implemented +func IsNotImplemented(err error) bool { + return errors.Is(err, ErrNotImplemented) +} + +// IsCanceled returns true if the error is due to `context.Canceled`. +func IsCanceled(err error) bool { + return errors.Is(err, context.Canceled) +} + +// IsDeadlineExceeded returns true if the error is due to +// `context.DeadlineExceeded`. +func IsDeadlineExceeded(err error) bool { + return errors.Is(err, context.DeadlineExceeded) +} diff --git a/vendor/github.com/containerd/containerd/errdefs/grpc.go b/vendor/github.com/containerd/containerd/errdefs/grpc.go new file mode 100644 index 0000000000..7a9b33e05a --- /dev/null +++ b/vendor/github.com/containerd/containerd/errdefs/grpc.go @@ -0,0 +1,147 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package errdefs + +import ( + "context" + "fmt" + "strings" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// ToGRPC will attempt to map the backend containerd error into a grpc error, +// using the original error message as a description. +// +// Further information may be extracted from certain errors depending on their +// type. +// +// If the error is unmapped, the original error will be returned to be handled +// by the regular grpc error handling stack. +func ToGRPC(err error) error { + if err == nil { + return nil + } + + if isGRPCError(err) { + // error has already been mapped to grpc + return err + } + + switch { + case IsInvalidArgument(err): + return status.Errorf(codes.InvalidArgument, err.Error()) + case IsNotFound(err): + return status.Errorf(codes.NotFound, err.Error()) + case IsAlreadyExists(err): + return status.Errorf(codes.AlreadyExists, err.Error()) + case IsFailedPrecondition(err): + return status.Errorf(codes.FailedPrecondition, err.Error()) + case IsUnavailable(err): + return status.Errorf(codes.Unavailable, err.Error()) + case IsNotImplemented(err): + return status.Errorf(codes.Unimplemented, err.Error()) + case IsCanceled(err): + return status.Errorf(codes.Canceled, err.Error()) + case IsDeadlineExceeded(err): + return status.Errorf(codes.DeadlineExceeded, err.Error()) + } + + return err +} + +// ToGRPCf maps the error to grpc error codes, assembling the formatting string +// and combining it with the target error string. +// +// This is equivalent to errdefs.ToGRPC(fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err)) +func ToGRPCf(err error, format string, args ...interface{}) error { + return ToGRPC(fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err)) +} + +// FromGRPC returns the underlying error from a grpc service based on the grpc error code +func FromGRPC(err error) error { + if err == nil { + return nil + } + + var cls error // divide these into error classes, becomes the cause + + switch code(err) { + case codes.InvalidArgument: + cls = ErrInvalidArgument + case codes.AlreadyExists: + cls = ErrAlreadyExists + case codes.NotFound: + cls = ErrNotFound + case codes.Unavailable: + cls = ErrUnavailable + case codes.FailedPrecondition: + cls = ErrFailedPrecondition + case codes.Unimplemented: + cls = ErrNotImplemented + case codes.Canceled: + cls = context.Canceled + case codes.DeadlineExceeded: + cls = context.DeadlineExceeded + default: + cls = ErrUnknown + } + + msg := rebaseMessage(cls, err) + if msg != "" { + err = fmt.Errorf("%s: %w", msg, cls) + } else { + err = cls + } + + return err +} + +// rebaseMessage removes the repeats for an error at the end of an error +// string. This will happen when taking an error over grpc then remapping it. +// +// Effectively, we just remove the string of cls from the end of err if it +// appears there. +func rebaseMessage(cls error, err error) string { + desc := errDesc(err) + clss := cls.Error() + if desc == clss { + return "" + } + + return strings.TrimSuffix(desc, ": "+clss) +} + +func isGRPCError(err error) bool { + _, ok := status.FromError(err) + return ok +} + +func code(err error) codes.Code { + if s, ok := status.FromError(err); ok { + return s.Code() + } + return codes.Unknown +} + +func errDesc(err error) string { + if s, ok := status.FromError(err); ok { + return s.Message() + } + return err.Error() +} diff --git a/vendor/github.com/containerd/containerd/log/context.go b/vendor/github.com/containerd/containerd/log/context.go new file mode 100644 index 0000000000..0db9562b82 --- /dev/null +++ b/vendor/github.com/containerd/containerd/log/context.go @@ -0,0 +1,69 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package log + +import ( + "context" + + "github.com/sirupsen/logrus" +) + +var ( + // G is an alias for GetLogger. + // + // We may want to define this locally to a package to get package tagged log + // messages. + G = GetLogger + + // L is an alias for the standard logger. + L = logrus.NewEntry(logrus.StandardLogger()) +) + +type ( + loggerKey struct{} +) + +const ( + // RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to + // ensure the formatted time is always the same number of characters. + RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00" + + // TextFormat represents the text logging format + TextFormat = "text" + + // JSONFormat represents the JSON logging format + JSONFormat = "json" +) + +// WithLogger returns a new context with the provided logger. Use in +// combination with logger.WithField(s) for great effect. +func WithLogger(ctx context.Context, logger *logrus.Entry) context.Context { + e := logger.WithContext(ctx) + return context.WithValue(ctx, loggerKey{}, e) +} + +// GetLogger retrieves the current logger from the context. If no logger is +// available, the default logger is returned. +func GetLogger(ctx context.Context) *logrus.Entry { + logger := ctx.Value(loggerKey{}) + + if logger == nil { + return L.WithContext(ctx) + } + + return logger.(*logrus.Entry) +} diff --git a/vendor/github.com/containerd/containerd/pkg/userns/userns_linux.go b/vendor/github.com/containerd/containerd/pkg/userns/userns_linux.go new file mode 100644 index 0000000000..6656465efb --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/userns/userns_linux.go @@ -0,0 +1,62 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package userns + +import ( + "bufio" + "fmt" + "os" + "sync" +) + +var ( + inUserNS bool + nsOnce sync.Once +) + +// RunningInUserNS detects whether we are currently running in a user namespace. +// Originally copied from github.com/lxc/lxd/shared/util.go +func RunningInUserNS() bool { + nsOnce.Do(func() { + file, err := os.Open("/proc/self/uid_map") + if err != nil { + // This kernel-provided file only exists if user namespaces are supported + return + } + defer file.Close() + + buf := bufio.NewReader(file) + l, _, err := buf.ReadLine() + if err != nil { + return + } + + line := string(l) + var a, b, c int64 + fmt.Sscanf(line, "%d %d %d", &a, &b, &c) + + /* + * We assume we are in the initial user namespace if we have a full + * range - 4294967295 uids starting at uid 0. + */ + if a == 0 && b == 0 && c == 4294967295 { + return + } + inUserNS = true + }) + return inUserNS +} diff --git a/vendor/github.com/containerd/containerd/pkg/userns/userns_unsupported.go b/vendor/github.com/containerd/containerd/pkg/userns/userns_unsupported.go new file mode 100644 index 0000000000..4f8d7dd2d5 --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/userns/userns_unsupported.go @@ -0,0 +1,26 @@ +//go:build !linux +// +build !linux + +/* + Copyright The containerd Authors. + + 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. +*/ + +package userns + +// RunningInUserNS is a stub for non-Linux systems +// Always returns false +func RunningInUserNS() bool { + return false +} diff --git a/vendor/github.com/containerd/containerd/platforms/compare.go b/vendor/github.com/containerd/containerd/platforms/compare.go new file mode 100644 index 0000000000..3913ef6637 --- /dev/null +++ b/vendor/github.com/containerd/containerd/platforms/compare.go @@ -0,0 +1,203 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package platforms + +import ( + "strconv" + "strings" + + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +// MatchComparer is able to match and compare platforms to +// filter and sort platforms. +type MatchComparer interface { + Matcher + + Less(specs.Platform, specs.Platform) bool +} + +// platformVector returns an (ordered) vector of appropriate specs.Platform +// objects to try matching for the given platform object (see platforms.Only). +func platformVector(platform specs.Platform) []specs.Platform { + vector := []specs.Platform{platform} + + switch platform.Architecture { + case "amd64": + if amd64Version, err := strconv.Atoi(strings.TrimPrefix(platform.Variant, "v")); err == nil && amd64Version > 1 { + for amd64Version--; amd64Version >= 1; amd64Version-- { + vector = append(vector, specs.Platform{ + Architecture: platform.Architecture, + OS: platform.OS, + OSVersion: platform.OSVersion, + OSFeatures: platform.OSFeatures, + Variant: "v" + strconv.Itoa(amd64Version), + }) + } + } + vector = append(vector, specs.Platform{ + Architecture: "386", + OS: platform.OS, + OSVersion: platform.OSVersion, + OSFeatures: platform.OSFeatures, + }) + case "arm": + if armVersion, err := strconv.Atoi(strings.TrimPrefix(platform.Variant, "v")); err == nil && armVersion > 5 { + for armVersion--; armVersion >= 5; armVersion-- { + vector = append(vector, specs.Platform{ + Architecture: platform.Architecture, + OS: platform.OS, + OSVersion: platform.OSVersion, + OSFeatures: platform.OSFeatures, + Variant: "v" + strconv.Itoa(armVersion), + }) + } + } + case "arm64": + variant := platform.Variant + if variant == "" { + variant = "v8" + } + vector = append(vector, platformVector(specs.Platform{ + Architecture: "arm", + OS: platform.OS, + OSVersion: platform.OSVersion, + OSFeatures: platform.OSFeatures, + Variant: variant, + })...) + } + + return vector +} + +// Only returns a match comparer for a single platform +// using default resolution logic for the platform. +// +// For arm/v8, will also match arm/v7, arm/v6 and arm/v5 +// For arm/v7, will also match arm/v6 and arm/v5 +// For arm/v6, will also match arm/v5 +// For amd64, will also match 386 +func Only(platform specs.Platform) MatchComparer { + return Ordered(platformVector(Normalize(platform))...) +} + +// OnlyStrict returns a match comparer for a single platform. +// +// Unlike Only, OnlyStrict does not match sub platforms. +// So, "arm/vN" will not match "arm/vM" where M < N, +// and "amd64" will not also match "386". +// +// OnlyStrict matches non-canonical forms. +// So, "arm64" matches "arm/64/v8". +func OnlyStrict(platform specs.Platform) MatchComparer { + return Ordered(Normalize(platform)) +} + +// Ordered returns a platform MatchComparer which matches any of the platforms +// but orders them in order they are provided. +func Ordered(platforms ...specs.Platform) MatchComparer { + matchers := make([]Matcher, len(platforms)) + for i := range platforms { + matchers[i] = NewMatcher(platforms[i]) + } + return orderedPlatformComparer{ + matchers: matchers, + } +} + +// Any returns a platform MatchComparer which matches any of the platforms +// with no preference for ordering. +func Any(platforms ...specs.Platform) MatchComparer { + matchers := make([]Matcher, len(platforms)) + for i := range platforms { + matchers[i] = NewMatcher(platforms[i]) + } + return anyPlatformComparer{ + matchers: matchers, + } +} + +// All is a platform MatchComparer which matches all platforms +// with preference for ordering. +var All MatchComparer = allPlatformComparer{} + +type orderedPlatformComparer struct { + matchers []Matcher +} + +func (c orderedPlatformComparer) Match(platform specs.Platform) bool { + for _, m := range c.matchers { + if m.Match(platform) { + return true + } + } + return false +} + +func (c orderedPlatformComparer) Less(p1 specs.Platform, p2 specs.Platform) bool { + for _, m := range c.matchers { + p1m := m.Match(p1) + p2m := m.Match(p2) + if p1m && !p2m { + return true + } + if p1m || p2m { + return false + } + } + return false +} + +type anyPlatformComparer struct { + matchers []Matcher +} + +func (c anyPlatformComparer) Match(platform specs.Platform) bool { + for _, m := range c.matchers { + if m.Match(platform) { + return true + } + } + return false +} + +func (c anyPlatformComparer) Less(p1, p2 specs.Platform) bool { + var p1m, p2m bool + for _, m := range c.matchers { + if !p1m && m.Match(p1) { + p1m = true + } + if !p2m && m.Match(p2) { + p2m = true + } + if p1m && p2m { + return false + } + } + // If one matches, and the other does, sort match first + return p1m && !p2m +} + +type allPlatformComparer struct{} + +func (allPlatformComparer) Match(specs.Platform) bool { + return true +} + +func (allPlatformComparer) Less(specs.Platform, specs.Platform) bool { + return false +} diff --git a/vendor/github.com/containerd/containerd/platforms/cpuinfo.go b/vendor/github.com/containerd/containerd/platforms/cpuinfo.go new file mode 100644 index 0000000000..046e0356d1 --- /dev/null +++ b/vendor/github.com/containerd/containerd/platforms/cpuinfo.go @@ -0,0 +1,131 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package platforms + +import ( + "bufio" + "fmt" + "os" + "runtime" + "strings" + "sync" + + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/log" +) + +// Present the ARM instruction set architecture, eg: v7, v8 +// Don't use this value directly; call cpuVariant() instead. +var cpuVariantValue string + +var cpuVariantOnce sync.Once + +func cpuVariant() string { + cpuVariantOnce.Do(func() { + if isArmArch(runtime.GOARCH) { + cpuVariantValue = getCPUVariant() + } + }) + return cpuVariantValue +} + +// For Linux, the kernel has already detected the ABI, ISA and Features. +// So we don't need to access the ARM registers to detect platform information +// by ourselves. We can just parse these information from /proc/cpuinfo +func getCPUInfo(pattern string) (info string, err error) { + if !isLinuxOS(runtime.GOOS) { + return "", fmt.Errorf("getCPUInfo for OS %s: %w", runtime.GOOS, errdefs.ErrNotImplemented) + } + + cpuinfo, err := os.Open("/proc/cpuinfo") + if err != nil { + return "", err + } + defer cpuinfo.Close() + + // Start to Parse the Cpuinfo line by line. For SMP SoC, we parse + // the first core is enough. + scanner := bufio.NewScanner(cpuinfo) + for scanner.Scan() { + newline := scanner.Text() + list := strings.Split(newline, ":") + + if len(list) > 1 && strings.EqualFold(strings.TrimSpace(list[0]), pattern) { + return strings.TrimSpace(list[1]), nil + } + } + + // Check whether the scanner encountered errors + err = scanner.Err() + if err != nil { + return "", err + } + + return "", fmt.Errorf("getCPUInfo for pattern: %s: %w", pattern, errdefs.ErrNotFound) +} + +func getCPUVariant() string { + if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { + // Windows/Darwin only supports v7 for ARM32 and v8 for ARM64 and so we can use + // runtime.GOARCH to determine the variants + var variant string + switch runtime.GOARCH { + case "arm64": + variant = "v8" + case "arm": + variant = "v7" + default: + variant = "unknown" + } + + return variant + } + + variant, err := getCPUInfo("Cpu architecture") + if err != nil { + log.L.WithError(err).Error("failure getting variant") + return "" + } + + // handle edge case for Raspberry Pi ARMv6 devices (which due to a kernel quirk, report "CPU architecture: 7") + // https://www.raspberrypi.org/forums/viewtopic.php?t=12614 + if runtime.GOARCH == "arm" && variant == "7" { + model, err := getCPUInfo("model name") + if err == nil && strings.HasPrefix(strings.ToLower(model), "armv6-compatible") { + variant = "6" + } + } + + switch strings.ToLower(variant) { + case "8", "aarch64": + variant = "v8" + case "7", "7m", "?(12)", "?(13)", "?(14)", "?(15)", "?(16)", "?(17)": + variant = "v7" + case "6", "6tej": + variant = "v6" + case "5", "5t", "5te", "5tej": + variant = "v5" + case "4", "4t": + variant = "v4" + case "3": + variant = "v3" + default: + variant = "unknown" + } + + return variant +} diff --git a/vendor/github.com/containerd/containerd/platforms/database.go b/vendor/github.com/containerd/containerd/platforms/database.go new file mode 100644 index 0000000000..dbe9957ca9 --- /dev/null +++ b/vendor/github.com/containerd/containerd/platforms/database.go @@ -0,0 +1,116 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package platforms + +import ( + "runtime" + "strings" +) + +// isLinuxOS returns true if the operating system is Linux. +// +// The OS value should be normalized before calling this function. +func isLinuxOS(os string) bool { + return os == "linux" +} + +// These function are generated from https://golang.org/src/go/build/syslist.go. +// +// We use switch statements because they are slightly faster than map lookups +// and use a little less memory. + +// isKnownOS returns true if we know about the operating system. +// +// The OS value should be normalized before calling this function. +func isKnownOS(os string) bool { + switch os { + case "aix", "android", "darwin", "dragonfly", "freebsd", "hurd", "illumos", "ios", "js", "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris", "windows", "zos": + return true + } + return false +} + +// isArmArch returns true if the architecture is ARM. +// +// The arch value should be normalized before being passed to this function. +func isArmArch(arch string) bool { + switch arch { + case "arm", "arm64": + return true + } + return false +} + +// isKnownArch returns true if we know about the architecture. +// +// The arch value should be normalized before being passed to this function. +func isKnownArch(arch string) bool { + switch arch { + case "386", "amd64", "amd64p32", "arm", "armbe", "arm64", "arm64be", "ppc64", "ppc64le", "loong64", "mips", "mipsle", "mips64", "mips64le", "mips64p32", "mips64p32le", "ppc", "riscv", "riscv64", "s390", "s390x", "sparc", "sparc64", "wasm": + return true + } + return false +} + +func normalizeOS(os string) string { + if os == "" { + return runtime.GOOS + } + os = strings.ToLower(os) + + switch os { + case "macos": + os = "darwin" + } + return os +} + +// normalizeArch normalizes the architecture. +func normalizeArch(arch, variant string) (string, string) { + arch, variant = strings.ToLower(arch), strings.ToLower(variant) + switch arch { + case "i386": + arch = "386" + variant = "" + case "x86_64", "x86-64", "amd64": + arch = "amd64" + if variant == "v1" { + variant = "" + } + case "aarch64", "arm64": + arch = "arm64" + switch variant { + case "8", "v8": + variant = "" + } + case "armhf": + arch = "arm" + variant = "v7" + case "armel": + arch = "arm" + variant = "v6" + case "arm": + switch variant { + case "", "7": + variant = "v7" + case "5", "6", "8": + variant = "v" + variant + } + } + + return arch, variant +} diff --git a/vendor/github.com/containerd/containerd/platforms/defaults.go b/vendor/github.com/containerd/containerd/platforms/defaults.go new file mode 100644 index 0000000000..cfa3ff34a1 --- /dev/null +++ b/vendor/github.com/containerd/containerd/platforms/defaults.go @@ -0,0 +1,27 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package platforms + +// DefaultString returns the default string specifier for the platform. +func DefaultString() string { + return Format(DefaultSpec()) +} + +// DefaultStrict returns strict form of Default. +func DefaultStrict() MatchComparer { + return OnlyStrict(DefaultSpec()) +} diff --git a/vendor/github.com/containerd/containerd/platforms/defaults_darwin.go b/vendor/github.com/containerd/containerd/platforms/defaults_darwin.go new file mode 100644 index 0000000000..e249fe48d3 --- /dev/null +++ b/vendor/github.com/containerd/containerd/platforms/defaults_darwin.go @@ -0,0 +1,45 @@ +//go:build darwin +// +build darwin + +/* + Copyright The containerd Authors. + + 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. +*/ + +package platforms + +import ( + "runtime" + + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +// DefaultSpec returns the current platform's default platform specification. +func DefaultSpec() specs.Platform { + return specs.Platform{ + OS: runtime.GOOS, + Architecture: runtime.GOARCH, + // The Variant field will be empty if arch != ARM. + Variant: cpuVariant(), + } +} + +// Default returns the default matcher for the platform. +func Default() MatchComparer { + return Ordered(DefaultSpec(), specs.Platform{ + // darwin runtime also supports Linux binary via runu/LKL + OS: "linux", + Architecture: runtime.GOARCH, + }) +} diff --git a/vendor/github.com/containerd/containerd/platforms/defaults_unix.go b/vendor/github.com/containerd/containerd/platforms/defaults_unix.go new file mode 100644 index 0000000000..49690f1b3e --- /dev/null +++ b/vendor/github.com/containerd/containerd/platforms/defaults_unix.go @@ -0,0 +1,41 @@ +//go:build !windows && !darwin +// +build !windows,!darwin + +/* + Copyright The containerd Authors. + + 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. +*/ + +package platforms + +import ( + "runtime" + + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +// DefaultSpec returns the current platform's default platform specification. +func DefaultSpec() specs.Platform { + return specs.Platform{ + OS: runtime.GOOS, + Architecture: runtime.GOARCH, + // The Variant field will be empty if arch != ARM. + Variant: cpuVariant(), + } +} + +// Default returns the default matcher for the platform. +func Default() MatchComparer { + return Only(DefaultSpec()) +} diff --git a/vendor/github.com/containerd/containerd/platforms/defaults_windows.go b/vendor/github.com/containerd/containerd/platforms/defaults_windows.go new file mode 100644 index 0000000000..c1aaf72ca8 --- /dev/null +++ b/vendor/github.com/containerd/containerd/platforms/defaults_windows.go @@ -0,0 +1,91 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package platforms + +import ( + "fmt" + "runtime" + "strconv" + "strings" + + imagespec "github.com/opencontainers/image-spec/specs-go/v1" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "golang.org/x/sys/windows" +) + +// DefaultSpec returns the current platform's default platform specification. +func DefaultSpec() specs.Platform { + major, minor, build := windows.RtlGetNtVersionNumbers() + return specs.Platform{ + OS: runtime.GOOS, + Architecture: runtime.GOARCH, + OSVersion: fmt.Sprintf("%d.%d.%d", major, minor, build), + // The Variant field will be empty if arch != ARM. + Variant: cpuVariant(), + } +} + +type matchComparer struct { + defaults Matcher + osVersionPrefix string +} + +// Match matches platform with the same windows major, minor +// and build version. +func (m matchComparer) Match(p imagespec.Platform) bool { + if m.defaults.Match(p) { + // TODO(windows): Figure out whether OSVersion is deprecated. + return strings.HasPrefix(p.OSVersion, m.osVersionPrefix) + } + return false +} + +// Less sorts matched platforms in front of other platforms. +// For matched platforms, it puts platforms with larger revision +// number in front. +func (m matchComparer) Less(p1, p2 imagespec.Platform) bool { + m1, m2 := m.Match(p1), m.Match(p2) + if m1 && m2 { + r1, r2 := revision(p1.OSVersion), revision(p2.OSVersion) + return r1 > r2 + } + return m1 && !m2 +} + +func revision(v string) int { + parts := strings.Split(v, ".") + if len(parts) < 4 { + return 0 + } + r, err := strconv.Atoi(parts[3]) + if err != nil { + return 0 + } + return r +} + +// Default returns the current platform's default platform specification. +func Default() MatchComparer { + major, minor, build := windows.RtlGetNtVersionNumbers() + return matchComparer{ + defaults: Ordered(DefaultSpec(), specs.Platform{ + OS: "linux", + Architecture: runtime.GOARCH, + }), + osVersionPrefix: fmt.Sprintf("%d.%d.%d", major, minor, build), + } +} diff --git a/vendor/github.com/containerd/containerd/platforms/platforms.go b/vendor/github.com/containerd/containerd/platforms/platforms.go new file mode 100644 index 0000000000..8f955d036d --- /dev/null +++ b/vendor/github.com/containerd/containerd/platforms/platforms.go @@ -0,0 +1,261 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +// Package platforms provides a toolkit for normalizing, matching and +// specifying container platforms. +// +// Centered around OCI platform specifications, we define a string-based +// specifier syntax that can be used for user input. With a specifier, users +// only need to specify the parts of the platform that are relevant to their +// context, providing an operating system or architecture or both. +// +// How do I use this package? +// +// The vast majority of use cases should simply use the match function with +// user input. The first step is to parse a specifier into a matcher: +// +// m, err := Parse("linux") +// if err != nil { ... } +// +// Once you have a matcher, use it to match against the platform declared by a +// component, typically from an image or runtime. Since extracting an images +// platform is a little more involved, we'll use an example against the +// platform default: +// +// if ok := m.Match(Default()); !ok { /* doesn't match */ } +// +// This can be composed in loops for resolving runtimes or used as a filter for +// fetch and select images. +// +// More details of the specifier syntax and platform spec follow. +// +// Declaring Platform Support +// +// Components that have strict platform requirements should use the OCI +// platform specification to declare their support. Typically, this will be +// images and runtimes that should make these declaring which platform they +// support specifically. This looks roughly as follows: +// +// type Platform struct { +// Architecture string +// OS string +// Variant string +// } +// +// Most images and runtimes should at least set Architecture and OS, according +// to their GOARCH and GOOS values, respectively (follow the OCI image +// specification when in doubt). ARM should set variant under certain +// discussions, which are outlined below. +// +// Platform Specifiers +// +// While the OCI platform specifications provide a tool for components to +// specify structured information, user input typically doesn't need the full +// context and much can be inferred. To solve this problem, we introduced +// "specifiers". A specifier has the format +// `||/[/]`. The user can provide either the +// operating system or the architecture or both. +// +// An example of a common specifier is `linux/amd64`. If the host has a default +// of runtime that matches this, the user can simply provide the component that +// matters. For example, if a image provides amd64 and arm64 support, the +// operating system, `linux` can be inferred, so they only have to provide +// `arm64` or `amd64`. Similar behavior is implemented for operating systems, +// where the architecture may be known but a runtime may support images from +// different operating systems. +// +// Normalization +// +// Because not all users are familiar with the way the Go runtime represents +// platforms, several normalizations have been provided to make this package +// easier to user. +// +// The following are performed for architectures: +// +// Value Normalized +// aarch64 arm64 +// armhf arm +// armel arm/v6 +// i386 386 +// x86_64 amd64 +// x86-64 amd64 +// +// We also normalize the operating system `macos` to `darwin`. +// +// ARM Support +// +// To qualify ARM architecture, the Variant field is used to qualify the arm +// version. The most common arm version, v7, is represented without the variant +// unless it is explicitly provided. This is treated as equivalent to armhf. A +// previous architecture, armel, will be normalized to arm/v6. +// +// While these normalizations are provided, their support on arm platforms has +// not yet been fully implemented and tested. +package platforms + +import ( + "fmt" + "path" + "regexp" + "runtime" + "strconv" + "strings" + + "github.com/containerd/containerd/errdefs" + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +var ( + specifierRe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`) +) + +// Matcher matches platforms specifications, provided by an image or runtime. +type Matcher interface { + Match(platform specs.Platform) bool +} + +// NewMatcher returns a simple matcher based on the provided platform +// specification. The returned matcher only looks for equality based on os, +// architecture and variant. +// +// One may implement their own matcher if this doesn't provide the required +// functionality. +// +// Applications should opt to use `Match` over directly parsing specifiers. +func NewMatcher(platform specs.Platform) Matcher { + return &matcher{ + Platform: Normalize(platform), + } +} + +type matcher struct { + specs.Platform +} + +func (m *matcher) Match(platform specs.Platform) bool { + normalized := Normalize(platform) + return m.OS == normalized.OS && + m.Architecture == normalized.Architecture && + m.Variant == normalized.Variant +} + +func (m *matcher) String() string { + return Format(m.Platform) +} + +// Parse parses the platform specifier syntax into a platform declaration. +// +// Platform specifiers are in the format `||/[/]`. +// The minimum required information for a platform specifier is the operating +// system or architecture. If there is only a single string (no slashes), the +// value will be matched against the known set of operating systems, then fall +// back to the known set of architectures. The missing component will be +// inferred based on the local environment. +func Parse(specifier string) (specs.Platform, error) { + if strings.Contains(specifier, "*") { + // TODO(stevvooe): need to work out exact wildcard handling + return specs.Platform{}, fmt.Errorf("%q: wildcards not yet supported: %w", specifier, errdefs.ErrInvalidArgument) + } + + parts := strings.Split(specifier, "/") + + for _, part := range parts { + if !specifierRe.MatchString(part) { + return specs.Platform{}, fmt.Errorf("%q is an invalid component of %q: platform specifier component must match %q: %w", part, specifier, specifierRe.String(), errdefs.ErrInvalidArgument) + } + } + + var p specs.Platform + switch len(parts) { + case 1: + // in this case, we will test that the value might be an OS, then look + // it up. If it is not known, we'll treat it as an architecture. Since + // we have very little information about the platform here, we are + // going to be a little more strict if we don't know about the argument + // value. + p.OS = normalizeOS(parts[0]) + if isKnownOS(p.OS) { + // picks a default architecture + p.Architecture = runtime.GOARCH + if p.Architecture == "arm" && cpuVariant() != "v7" { + p.Variant = cpuVariant() + } + + return p, nil + } + + p.Architecture, p.Variant = normalizeArch(parts[0], "") + if p.Architecture == "arm" && p.Variant == "v7" { + p.Variant = "" + } + if isKnownArch(p.Architecture) { + p.OS = runtime.GOOS + return p, nil + } + + return specs.Platform{}, fmt.Errorf("%q: unknown operating system or architecture: %w", specifier, errdefs.ErrInvalidArgument) + case 2: + // In this case, we treat as a regular os/arch pair. We don't care + // about whether or not we know of the platform. + p.OS = normalizeOS(parts[0]) + p.Architecture, p.Variant = normalizeArch(parts[1], "") + if p.Architecture == "arm" && p.Variant == "v7" { + p.Variant = "" + } + + return p, nil + case 3: + // we have a fully specified variant, this is rare + p.OS = normalizeOS(parts[0]) + p.Architecture, p.Variant = normalizeArch(parts[1], parts[2]) + if p.Architecture == "arm64" && p.Variant == "" { + p.Variant = "v8" + } + + return p, nil + } + + return specs.Platform{}, fmt.Errorf("%q: cannot parse platform specifier: %w", specifier, errdefs.ErrInvalidArgument) +} + +// MustParse is like Parses but panics if the specifier cannot be parsed. +// Simplifies initialization of global variables. +func MustParse(specifier string) specs.Platform { + p, err := Parse(specifier) + if err != nil { + panic("platform: Parse(" + strconv.Quote(specifier) + "): " + err.Error()) + } + return p +} + +// Format returns a string specifier from the provided platform specification. +func Format(platform specs.Platform) string { + if platform.OS == "" { + return "unknown" + } + + return path.Join(platform.OS, platform.Architecture, platform.Variant) +} + +// Normalize validates and translate the platform to the canonical value. +// +// For example, if "Aarch64" is encountered, we change it to "arm64" or if +// "x86_64" is encountered, it becomes "amd64". +func Normalize(platform specs.Platform) specs.Platform { + platform.OS = normalizeOS(platform.OS) + platform.Architecture, platform.Variant = normalizeArch(platform.Architecture, platform.Variant) + return platform +} diff --git a/vendor/github.com/containerd/containerd/sys/epoll.go b/vendor/github.com/containerd/containerd/sys/epoll.go new file mode 100644 index 0000000000..73a57013ff --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/epoll.go @@ -0,0 +1,34 @@ +//go:build linux +// +build linux + +/* + Copyright The containerd Authors. + + 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. +*/ + +package sys + +import "golang.org/x/sys/unix" + +// EpollCreate1 is an alias for unix.EpollCreate1 +// Deprecated: use golang.org/x/sys/unix.EpollCreate1 +var EpollCreate1 = unix.EpollCreate1 + +// EpollCtl is an alias for unix.EpollCtl +// Deprecated: use golang.org/x/sys/unix.EpollCtl +var EpollCtl = unix.EpollCtl + +// EpollWait is an alias for unix.EpollWait +// Deprecated: use golang.org/x/sys/unix.EpollWait +var EpollWait = unix.EpollWait diff --git a/vendor/github.com/containerd/containerd/sys/fds.go b/vendor/github.com/containerd/containerd/sys/fds.go new file mode 100644 index 0000000000..a71a9cd7e9 --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/fds.go @@ -0,0 +1,35 @@ +//go:build !windows && !darwin +// +build !windows,!darwin + +/* + Copyright The containerd Authors. + + 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. +*/ + +package sys + +import ( + "os" + "path/filepath" + "strconv" +) + +// GetOpenFds returns the number of open fds for the process provided by pid +func GetOpenFds(pid int) (int, error) { + dirs, err := os.ReadDir(filepath.Join("/proc", strconv.Itoa(pid), "fd")) + if err != nil { + return -1, err + } + return len(dirs), nil +} diff --git a/vendor/github.com/containerd/containerd/sys/filesys_unix.go b/vendor/github.com/containerd/containerd/sys/filesys_unix.go new file mode 100644 index 0000000000..805a7a736f --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/filesys_unix.go @@ -0,0 +1,32 @@ +//go:build !windows +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package sys + +import "os" + +// ForceRemoveAll on unix is just a wrapper for os.RemoveAll +func ForceRemoveAll(path string) error { + return os.RemoveAll(path) +} + +// MkdirAllWithACL is a wrapper for os.MkdirAll on Unix systems. +func MkdirAllWithACL(path string, perm os.FileMode) error { + return os.MkdirAll(path, perm) +} diff --git a/vendor/github.com/containerd/containerd/sys/filesys_windows.go b/vendor/github.com/containerd/containerd/sys/filesys_windows.go new file mode 100644 index 0000000000..87ebacc200 --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/filesys_windows.go @@ -0,0 +1,345 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package sys + +import ( + "fmt" + "os" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + "syscall" + "unsafe" + + "github.com/Microsoft/hcsshim" + "golang.org/x/sys/windows" +) + +const ( + // SddlAdministratorsLocalSystem is local administrators plus NT AUTHORITY\System + SddlAdministratorsLocalSystem = "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)" +) + +// MkdirAllWithACL is a wrapper for MkdirAll that creates a directory +// ACL'd for Builtin Administrators and Local System. +func MkdirAllWithACL(path string, perm os.FileMode) error { + return mkdirall(path, true) +} + +// MkdirAll implementation that is volume path aware for Windows. It can be used +// as a drop-in replacement for os.MkdirAll() +func MkdirAll(path string, _ os.FileMode) error { + return mkdirall(path, false) +} + +// mkdirall is a custom version of os.MkdirAll modified for use on Windows +// so that it is both volume path aware, and can create a directory with +// a DACL. +func mkdirall(path string, adminAndLocalSystem bool) error { + if re := regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}$`); re.MatchString(path) { + return nil + } + + // The rest of this method is largely copied from os.MkdirAll and should be kept + // as-is to ensure compatibility. + + // Fast path: if we can tell whether path is a directory or file, stop with success or error. + dir, err := os.Stat(path) + if err == nil { + if dir.IsDir() { + return nil + } + return &os.PathError{ + Op: "mkdir", + Path: path, + Err: syscall.ENOTDIR, + } + } + + // Slow path: make sure parent exists and then call Mkdir for path. + i := len(path) + for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator. + i-- + } + + j := i + for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element. + j-- + } + + if j > 1 { + // Create parent + err = mkdirall(path[0:j-1], adminAndLocalSystem) + if err != nil { + return err + } + } + + // Parent now exists; invoke os.Mkdir or mkdirWithACL and use its result. + if adminAndLocalSystem { + err = mkdirWithACL(path) + } else { + err = os.Mkdir(path, 0) + } + + if err != nil { + // Handle arguments like "foo/." by + // double-checking that directory doesn't exist. + dir, err1 := os.Lstat(path) + if err1 == nil && dir.IsDir() { + return nil + } + return err + } + return nil +} + +// mkdirWithACL creates a new directory. If there is an error, it will be of +// type *PathError. . +// +// This is a modified and combined version of os.Mkdir and windows.Mkdir +// in golang to cater for creating a directory am ACL permitting full +// access, with inheritance, to any subfolder/file for Built-in Administrators +// and Local System. +func mkdirWithACL(name string) error { + sa := windows.SecurityAttributes{Length: 0} + sd, err := windows.SecurityDescriptorFromString(SddlAdministratorsLocalSystem) + if err != nil { + return &os.PathError{Op: "mkdir", Path: name, Err: err} + } + sa.Length = uint32(unsafe.Sizeof(sa)) + sa.InheritHandle = 1 + sa.SecurityDescriptor = sd + + namep, err := windows.UTF16PtrFromString(name) + if err != nil { + return &os.PathError{Op: "mkdir", Path: name, Err: err} + } + + e := windows.CreateDirectory(namep, &sa) + if e != nil { + return &os.PathError{Op: "mkdir", Path: name, Err: e} + } + return nil +} + +// IsAbs is a platform-specific wrapper for filepath.IsAbs. On Windows, +// golang filepath.IsAbs does not consider a path \windows\system32 as absolute +// as it doesn't start with a drive-letter/colon combination. However, in +// docker we need to verify things such as WORKDIR /windows/system32 in +// a Dockerfile (which gets translated to \windows\system32 when being processed +// by the daemon. This SHOULD be treated as absolute from a docker processing +// perspective. +func IsAbs(path string) bool { + if !filepath.IsAbs(path) { + if !strings.HasPrefix(path, string(os.PathSeparator)) { + return false + } + } + return true +} + +// The origin of the functions below here are the golang OS and windows packages, +// slightly modified to only cope with files, not directories due to the +// specific use case. +// +// The alteration is to allow a file on Windows to be opened with +// FILE_FLAG_SEQUENTIAL_SCAN (particular for docker load), to avoid eating +// the standby list, particularly when accessing large files such as layer.tar. + +// CreateSequential creates the named file with mode 0666 (before umask), truncating +// it if it already exists. If successful, methods on the returned +// File can be used for I/O; the associated file descriptor has mode +// O_RDWR. +// If there is an error, it will be of type *PathError. +func CreateSequential(name string) (*os.File, error) { + return OpenFileSequential(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0) +} + +// OpenSequential opens the named file for reading. If successful, methods on +// the returned file can be used for reading; the associated file +// descriptor has mode O_RDONLY. +// If there is an error, it will be of type *PathError. +func OpenSequential(name string) (*os.File, error) { + return OpenFileSequential(name, os.O_RDONLY, 0) +} + +// OpenFileSequential is the generalized open call; most users will use Open +// or Create instead. +// If there is an error, it will be of type *PathError. +func OpenFileSequential(name string, flag int, _ os.FileMode) (*os.File, error) { + if name == "" { + return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} + } + r, errf := windowsOpenFileSequential(name, flag, 0) + if errf == nil { + return r, nil + } + return nil, &os.PathError{Op: "open", Path: name, Err: errf} +} + +func windowsOpenFileSequential(name string, flag int, _ os.FileMode) (file *os.File, err error) { + r, e := windowsOpenSequential(name, flag|windows.O_CLOEXEC, 0) + if e != nil { + return nil, e + } + return os.NewFile(uintptr(r), name), nil +} + +func makeInheritSa() *windows.SecurityAttributes { + var sa windows.SecurityAttributes + sa.Length = uint32(unsafe.Sizeof(sa)) + sa.InheritHandle = 1 + return &sa +} + +func windowsOpenSequential(path string, mode int, _ uint32) (fd windows.Handle, err error) { + if len(path) == 0 { + return windows.InvalidHandle, windows.ERROR_FILE_NOT_FOUND + } + pathp, err := windows.UTF16PtrFromString(path) + if err != nil { + return windows.InvalidHandle, err + } + var access uint32 + switch mode & (windows.O_RDONLY | windows.O_WRONLY | windows.O_RDWR) { + case windows.O_RDONLY: + access = windows.GENERIC_READ + case windows.O_WRONLY: + access = windows.GENERIC_WRITE + case windows.O_RDWR: + access = windows.GENERIC_READ | windows.GENERIC_WRITE + } + if mode&windows.O_CREAT != 0 { + access |= windows.GENERIC_WRITE + } + if mode&windows.O_APPEND != 0 { + access &^= windows.GENERIC_WRITE + access |= windows.FILE_APPEND_DATA + } + sharemode := uint32(windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE) + var sa *windows.SecurityAttributes + if mode&windows.O_CLOEXEC == 0 { + sa = makeInheritSa() + } + var createmode uint32 + switch { + case mode&(windows.O_CREAT|windows.O_EXCL) == (windows.O_CREAT | windows.O_EXCL): + createmode = windows.CREATE_NEW + case mode&(windows.O_CREAT|windows.O_TRUNC) == (windows.O_CREAT | windows.O_TRUNC): + createmode = windows.CREATE_ALWAYS + case mode&windows.O_CREAT == windows.O_CREAT: + createmode = windows.OPEN_ALWAYS + case mode&windows.O_TRUNC == windows.O_TRUNC: + createmode = windows.TRUNCATE_EXISTING + default: + createmode = windows.OPEN_EXISTING + } + // Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang. + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx + const fileFlagSequentialScan = 0x08000000 // FILE_FLAG_SEQUENTIAL_SCAN + h, e := windows.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0) + return h, e +} + +// ForceRemoveAll is the same as os.RemoveAll, but is aware of io.containerd.snapshotter.v1.windows +// and uses hcsshim to unmount and delete container layers contained therein, in the correct order, +// when passed a containerd root data directory (i.e. the `--root` directory for containerd). +func ForceRemoveAll(path string) error { + // snapshots/windows/windows.go init() + const snapshotPlugin = "io.containerd.snapshotter.v1" + "." + "windows" + // snapshots/windows/windows.go NewSnapshotter() + snapshotDir := filepath.Join(path, snapshotPlugin, "snapshots") + if stat, err := os.Stat(snapshotDir); err == nil && stat.IsDir() { + if err := cleanupWCOWLayers(snapshotDir); err != nil { + return fmt.Errorf("failed to cleanup WCOW layers in %s: %w", snapshotDir, err) + } + } + + return os.RemoveAll(path) +} + +func cleanupWCOWLayers(root string) error { + // See snapshots/windows/windows.go getSnapshotDir() + var layerNums []int + var rmLayerNums []int + if err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if path != root && info.IsDir() { + name := filepath.Base(path) + if strings.HasPrefix(name, "rm-") { + layerNum, err := strconv.Atoi(strings.TrimPrefix(name, "rm-")) + if err != nil { + return err + } + rmLayerNums = append(rmLayerNums, layerNum) + } else { + layerNum, err := strconv.Atoi(name) + if err != nil { + return err + } + layerNums = append(layerNums, layerNum) + } + return filepath.SkipDir + } + + return nil + }); err != nil { + return err + } + + sort.Sort(sort.Reverse(sort.IntSlice(rmLayerNums))) + for _, rmLayerNum := range rmLayerNums { + if err := cleanupWCOWLayer(filepath.Join(root, "rm-"+strconv.Itoa(rmLayerNum))); err != nil { + return err + } + } + + sort.Sort(sort.Reverse(sort.IntSlice(layerNums))) + for _, layerNum := range layerNums { + if err := cleanupWCOWLayer(filepath.Join(root, strconv.Itoa(layerNum))); err != nil { + return err + } + } + + return nil +} + +func cleanupWCOWLayer(layerPath string) error { + info := hcsshim.DriverInfo{ + HomeDir: filepath.Dir(layerPath), + } + + // ERROR_DEV_NOT_EXIST is returned if the layer is not currently prepared or activated. + // ERROR_FLT_INSTANCE_NOT_FOUND is returned if the layer is currently activated but not prepared. + if err := hcsshim.UnprepareLayer(info, filepath.Base(layerPath)); err != nil { + if hcserror, ok := err.(*hcsshim.HcsError); !ok || (hcserror.Err != windows.ERROR_DEV_NOT_EXIST && hcserror.Err != syscall.Errno(windows.ERROR_FLT_INSTANCE_NOT_FOUND)) { + return fmt.Errorf("failed to unprepare %s: %w", layerPath, err) + } + } + + if err := hcsshim.DeactivateLayer(info, filepath.Base(layerPath)); err != nil { + return fmt.Errorf("failed to deactivate %s: %w", layerPath, err) + } + + if err := hcsshim.DestroyLayer(info, filepath.Base(layerPath)); err != nil { + return fmt.Errorf("failed to destroy %s: %w", layerPath, err) + } + + return nil +} diff --git a/vendor/github.com/containerd/containerd/sys/oom_linux.go b/vendor/github.com/containerd/containerd/sys/oom_linux.go new file mode 100644 index 0000000000..bb2a3eafb4 --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/oom_linux.go @@ -0,0 +1,82 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package sys + +import ( + "fmt" + "os" + "strconv" + "strings" + + "github.com/containerd/containerd/pkg/userns" + "golang.org/x/sys/unix" +) + +const ( + // OOMScoreAdjMin is from OOM_SCORE_ADJ_MIN https://github.com/torvalds/linux/blob/v5.10/include/uapi/linux/oom.h#L9 + OOMScoreAdjMin = -1000 + // OOMScoreAdjMax is from OOM_SCORE_ADJ_MAX https://github.com/torvalds/linux/blob/v5.10/include/uapi/linux/oom.h#L10 + OOMScoreAdjMax = 1000 +) + +// AdjustOOMScore sets the oom score for the provided pid. If the provided score +// is out of range (-1000 - 1000), it is clipped to the min/max value. +func AdjustOOMScore(pid, score int) error { + if score > OOMScoreAdjMax { + score = OOMScoreAdjMax + } else if score < OOMScoreAdjMin { + score = OOMScoreAdjMin + } + return SetOOMScore(pid, score) +} + +// SetOOMScore sets the oom score for the provided pid +func SetOOMScore(pid, score int) error { + if score > OOMScoreAdjMax || score < OOMScoreAdjMin { + return fmt.Errorf("value out of range (%d): OOM score must be between %d and %d", score, OOMScoreAdjMin, OOMScoreAdjMax) + } + path := fmt.Sprintf("/proc/%d/oom_score_adj", pid) + f, err := os.OpenFile(path, os.O_WRONLY, 0) + if err != nil { + return err + } + defer f.Close() + if _, err = f.WriteString(strconv.Itoa(score)); err != nil { + if os.IsPermission(err) && (!runningPrivileged() || userns.RunningInUserNS()) { + return nil + } + return err + } + return nil +} + +// GetOOMScoreAdj gets the oom score for a process. It returns 0 (zero) if either +// no oom score is set, or a sore is set to 0. +func GetOOMScoreAdj(pid int) (int, error) { + path := fmt.Sprintf("/proc/%d/oom_score_adj", pid) + data, err := os.ReadFile(path) + if err != nil { + return 0, err + } + return strconv.Atoi(strings.TrimSpace(string(data))) +} + +// runningPrivileged returns true if the effective user ID of the +// calling process is 0 +func runningPrivileged() bool { + return unix.Geteuid() == 0 +} diff --git a/vendor/github.com/containerd/containerd/sys/oom_unsupported.go b/vendor/github.com/containerd/containerd/sys/oom_unsupported.go new file mode 100644 index 0000000000..fa0db5a10e --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/oom_unsupported.go @@ -0,0 +1,49 @@ +//go:build !linux +// +build !linux + +/* + Copyright The containerd Authors. + + 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. +*/ + +package sys + +const ( + // OOMScoreMaxKillable is not implemented on non Linux + OOMScoreMaxKillable = 0 + // OOMScoreAdjMax is not implemented on non Linux + OOMScoreAdjMax = 0 +) + +// AdjustOOMScore sets the oom score for the provided pid. If the provided score +// is out of range (-1000 - 1000), it is clipped to the min/max value. +// +// Not implemented on Windows +func AdjustOOMScore(pid, score int) error { + return nil +} + +// SetOOMScore sets the oom score for the process +// +// Not implemented on Windows +func SetOOMScore(pid, score int) error { + return nil +} + +// GetOOMScoreAdj gets the oom score for a process +// +// Not implemented on Windows +func GetOOMScoreAdj(pid int) (int, error) { + return 0, nil +} diff --git a/vendor/github.com/containerd/containerd/sys/socket_unix.go b/vendor/github.com/containerd/containerd/sys/socket_unix.go new file mode 100644 index 0000000000..367e19cad8 --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/socket_unix.go @@ -0,0 +1,81 @@ +//go:build !windows +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package sys + +import ( + "fmt" + "net" + "os" + "path/filepath" + + "golang.org/x/sys/unix" +) + +// CreateUnixSocket creates a unix socket and returns the listener +func CreateUnixSocket(path string) (net.Listener, error) { + // BSDs have a 104 limit + if len(path) > 104 { + return nil, fmt.Errorf("%q: unix socket path too long (> 104)", path) + } + if err := os.MkdirAll(filepath.Dir(path), 0660); err != nil { + return nil, err + } + if err := unix.Unlink(path); err != nil && !os.IsNotExist(err) { + return nil, err + } + return net.Listen("unix", path) +} + +// GetLocalListener returns a listener out of a unix socket. +func GetLocalListener(path string, uid, gid int) (net.Listener, error) { + // Ensure parent directory is created + if err := mkdirAs(filepath.Dir(path), uid, gid); err != nil { + return nil, err + } + + l, err := CreateUnixSocket(path) + if err != nil { + return l, err + } + + if err := os.Chmod(path, 0660); err != nil { + l.Close() + return nil, err + } + + if err := os.Chown(path, uid, gid); err != nil { + l.Close() + return nil, err + } + + return l, nil +} + +func mkdirAs(path string, uid, gid int) error { + if _, err := os.Stat(path); !os.IsNotExist(err) { + return err + } + + if err := os.MkdirAll(path, 0770); err != nil { + return err + } + + return os.Chown(path, uid, gid) +} diff --git a/vendor/github.com/containerd/containerd/sys/socket_windows.go b/vendor/github.com/containerd/containerd/sys/socket_windows.go new file mode 100644 index 0000000000..1ae12bc511 --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/socket_windows.go @@ -0,0 +1,30 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package sys + +import ( + "net" + + "github.com/Microsoft/go-winio" +) + +// GetLocalListener returns a Listernet out of a named pipe. +// `path` must be of the form of `\\.\pipe\` +// (see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365150) +func GetLocalListener(path string, uid, gid int) (net.Listener, error) { + return winio.ListenPipe(path, nil) +} diff --git a/vendor/github.com/containerd/containerd/sys/userns_deprecated.go b/vendor/github.com/containerd/containerd/sys/userns_deprecated.go new file mode 100644 index 0000000000..53acf55477 --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/userns_deprecated.go @@ -0,0 +1,23 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package sys + +import "github.com/containerd/containerd/pkg/userns" + +// RunningInUserNS detects whether we are currently running in a user namespace. +// Deprecated: use github.com/containerd/containerd/pkg/userns.RunningInUserNS instead. +var RunningInUserNS = userns.RunningInUserNS diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/LICENSE b/vendor/github.com/containerd/stargz-snapshotter/estargz/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/build.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/build.go new file mode 100644 index 0000000000..9ee97fc911 --- /dev/null +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/build.go @@ -0,0 +1,628 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +/* + Copyright 2019 The Go Authors. All rights reserved. + Use of this source code is governed by a BSD-style + license that can be found in the LICENSE file. +*/ + +package estargz + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path" + "runtime" + "strings" + "sync" + + "github.com/containerd/stargz-snapshotter/estargz/errorutil" + "github.com/klauspost/compress/zstd" + digest "github.com/opencontainers/go-digest" + "golang.org/x/sync/errgroup" +) + +type options struct { + chunkSize int + compressionLevel int + prioritizedFiles []string + missedPrioritizedFiles *[]string + compression Compression +} + +type Option func(o *options) error + +// WithChunkSize option specifies the chunk size of eStargz blob to build. +func WithChunkSize(chunkSize int) Option { + return func(o *options) error { + o.chunkSize = chunkSize + return nil + } +} + +// WithCompressionLevel option specifies the gzip compression level. +// The default is gzip.BestCompression. +// See also: https://godoc.org/compress/gzip#pkg-constants +func WithCompressionLevel(level int) Option { + return func(o *options) error { + o.compressionLevel = level + return nil + } +} + +// WithPrioritizedFiles option specifies the list of prioritized files. +// These files must be complete paths that are absolute or relative to "/" +// For example, all of "foo/bar", "/foo/bar", "./foo/bar" and "../foo/bar" +// are treated as "/foo/bar". +func WithPrioritizedFiles(files []string) Option { + return func(o *options) error { + o.prioritizedFiles = files + return nil + } +} + +// WithAllowPrioritizeNotFound makes Build continue the execution even if some +// of prioritized files specified by WithPrioritizedFiles option aren't found +// in the input tar. Instead, this records all missed file names to the passed +// slice. +func WithAllowPrioritizeNotFound(missedFiles *[]string) Option { + return func(o *options) error { + if missedFiles == nil { + return fmt.Errorf("WithAllowPrioritizeNotFound: slice must be passed") + } + o.missedPrioritizedFiles = missedFiles + return nil + } +} + +// WithCompression specifies compression algorithm to be used. +// Default is gzip. +func WithCompression(compression Compression) Option { + return func(o *options) error { + o.compression = compression + return nil + } +} + +// Blob is an eStargz blob. +type Blob struct { + io.ReadCloser + diffID digest.Digester + tocDigest digest.Digest +} + +// DiffID returns the digest of uncompressed blob. +// It is only valid to call DiffID after Close. +func (b *Blob) DiffID() digest.Digest { + return b.diffID.Digest() +} + +// TOCDigest returns the digest of uncompressed TOC JSON. +func (b *Blob) TOCDigest() digest.Digest { + return b.tocDigest +} + +// Build builds an eStargz blob which is an extended version of stargz, from a blob (gzip, zstd +// or plain tar) passed through the argument. If there are some prioritized files are listed in +// the option, these files are grouped as "prioritized" and can be used for runtime optimization +// (e.g. prefetch). This function builds a blob in parallel, with dividing that blob into several +// (at least the number of runtime.GOMAXPROCS(0)) sub-blobs. +func Build(tarBlob *io.SectionReader, opt ...Option) (_ *Blob, rErr error) { + var opts options + opts.compressionLevel = gzip.BestCompression // BestCompression by default + for _, o := range opt { + if err := o(&opts); err != nil { + return nil, err + } + } + if opts.compression == nil { + opts.compression = newGzipCompressionWithLevel(opts.compressionLevel) + } + layerFiles := newTempFiles() + defer func() { + if rErr != nil { + if err := layerFiles.CleanupAll(); err != nil { + rErr = fmt.Errorf("failed to cleanup tmp files: %v: %w", err, rErr) + } + } + }() + tarBlob, err := decompressBlob(tarBlob, layerFiles) + if err != nil { + return nil, err + } + entries, err := sortEntries(tarBlob, opts.prioritizedFiles, opts.missedPrioritizedFiles) + if err != nil { + return nil, err + } + tarParts := divideEntries(entries, runtime.GOMAXPROCS(0)) + writers := make([]*Writer, len(tarParts)) + payloads := make([]*os.File, len(tarParts)) + var mu sync.Mutex + var eg errgroup.Group + for i, parts := range tarParts { + i, parts := i, parts + // builds verifiable stargz sub-blobs + eg.Go(func() error { + esgzFile, err := layerFiles.TempFile("", "esgzdata") + if err != nil { + return err + } + sw := NewWriterWithCompressor(esgzFile, opts.compression) + sw.ChunkSize = opts.chunkSize + if err := sw.AppendTar(readerFromEntries(parts...)); err != nil { + return err + } + mu.Lock() + writers[i] = sw + payloads[i] = esgzFile + mu.Unlock() + return nil + }) + } + if err := eg.Wait(); err != nil { + rErr = err + return nil, err + } + tocAndFooter, tocDgst, err := closeWithCombine(opts.compressionLevel, writers...) + if err != nil { + rErr = err + return nil, err + } + var rs []io.Reader + for _, p := range payloads { + fs, err := fileSectionReader(p) + if err != nil { + return nil, err + } + rs = append(rs, fs) + } + diffID := digest.Canonical.Digester() + pr, pw := io.Pipe() + go func() { + r, err := opts.compression.Reader(io.TeeReader(io.MultiReader(append(rs, tocAndFooter)...), pw)) + if err != nil { + pw.CloseWithError(err) + return + } + defer r.Close() + if _, err := io.Copy(diffID.Hash(), r); err != nil { + pw.CloseWithError(err) + return + } + pw.Close() + }() + return &Blob{ + ReadCloser: readCloser{ + Reader: pr, + closeFunc: layerFiles.CleanupAll, + }, + tocDigest: tocDgst, + diffID: diffID, + }, nil +} + +// closeWithCombine takes unclosed Writers and close them. This also returns the +// toc that combined all Writers into. +// Writers doesn't write TOC and footer to the underlying writers so they can be +// combined into a single eStargz and tocAndFooter returned by this function can +// be appended at the tail of that combined blob. +func closeWithCombine(compressionLevel int, ws ...*Writer) (tocAndFooterR io.Reader, tocDgst digest.Digest, err error) { + if len(ws) == 0 { + return nil, "", fmt.Errorf("at least one writer must be passed") + } + for _, w := range ws { + if w.closed { + return nil, "", fmt.Errorf("writer must be unclosed") + } + defer func(w *Writer) { w.closed = true }(w) + if err := w.closeGz(); err != nil { + return nil, "", err + } + if err := w.bw.Flush(); err != nil { + return nil, "", err + } + } + var ( + mtoc = new(JTOC) + currentOffset int64 + ) + mtoc.Version = ws[0].toc.Version + for _, w := range ws { + for _, e := range w.toc.Entries { + // Recalculate Offset of non-empty files/chunks + if (e.Type == "reg" && e.Size > 0) || e.Type == "chunk" { + e.Offset += currentOffset + } + mtoc.Entries = append(mtoc.Entries, e) + } + if w.toc.Version > mtoc.Version { + mtoc.Version = w.toc.Version + } + currentOffset += w.cw.n + } + + return tocAndFooter(ws[0].compressor, mtoc, currentOffset) +} + +func tocAndFooter(compressor Compressor, toc *JTOC, offset int64) (io.Reader, digest.Digest, error) { + buf := new(bytes.Buffer) + tocDigest, err := compressor.WriteTOCAndFooter(buf, offset, toc, nil) + if err != nil { + return nil, "", err + } + return buf, tocDigest, nil +} + +// divideEntries divides passed entries to the parts at least the number specified by the +// argument. +func divideEntries(entries []*entry, minPartsNum int) (set [][]*entry) { + var estimatedSize int64 + for _, e := range entries { + estimatedSize += e.header.Size + } + unitSize := estimatedSize / int64(minPartsNum) + var ( + nextEnd = unitSize + offset int64 + ) + set = append(set, []*entry{}) + for _, e := range entries { + set[len(set)-1] = append(set[len(set)-1], e) + offset += e.header.Size + if offset > nextEnd { + set = append(set, []*entry{}) + nextEnd += unitSize + } + } + return +} + +var errNotFound = errors.New("not found") + +// sortEntries reads the specified tar blob and returns a list of tar entries. +// If some of prioritized files are specified, the list starts from these +// files with keeping the order specified by the argument. +func sortEntries(in io.ReaderAt, prioritized []string, missedPrioritized *[]string) ([]*entry, error) { + + // Import tar file. + intar, err := importTar(in) + if err != nil { + return nil, fmt.Errorf("failed to sort: %w", err) + } + + // Sort the tar file respecting to the prioritized files list. + sorted := &tarFile{} + for _, l := range prioritized { + if err := moveRec(l, intar, sorted); err != nil { + if errors.Is(err, errNotFound) && missedPrioritized != nil { + *missedPrioritized = append(*missedPrioritized, l) + continue // allow not found + } + return nil, fmt.Errorf("failed to sort tar entries: %w", err) + } + } + if len(prioritized) == 0 { + sorted.add(&entry{ + header: &tar.Header{ + Name: NoPrefetchLandmark, + Typeflag: tar.TypeReg, + Size: int64(len([]byte{landmarkContents})), + }, + payload: bytes.NewReader([]byte{landmarkContents}), + }) + } else { + sorted.add(&entry{ + header: &tar.Header{ + Name: PrefetchLandmark, + Typeflag: tar.TypeReg, + Size: int64(len([]byte{landmarkContents})), + }, + payload: bytes.NewReader([]byte{landmarkContents}), + }) + } + + // Dump all entry and concatinate them. + return append(sorted.dump(), intar.dump()...), nil +} + +// readerFromEntries returns a reader of tar archive that contains entries passed +// through the arguments. +func readerFromEntries(entries ...*entry) io.Reader { + pr, pw := io.Pipe() + go func() { + tw := tar.NewWriter(pw) + defer tw.Close() + for _, entry := range entries { + if err := tw.WriteHeader(entry.header); err != nil { + pw.CloseWithError(fmt.Errorf("Failed to write tar header: %v", err)) + return + } + if _, err := io.Copy(tw, entry.payload); err != nil { + pw.CloseWithError(fmt.Errorf("Failed to write tar payload: %v", err)) + return + } + } + pw.Close() + }() + return pr +} + +func importTar(in io.ReaderAt) (*tarFile, error) { + tf := &tarFile{} + pw, err := newCountReader(in) + if err != nil { + return nil, fmt.Errorf("failed to make position watcher: %w", err) + } + tr := tar.NewReader(pw) + + // Walk through all nodes. + for { + // Fetch and parse next header. + h, err := tr.Next() + if err != nil { + if err == io.EOF { + break + } else { + return nil, fmt.Errorf("failed to parse tar file, %w", err) + } + } + switch cleanEntryName(h.Name) { + case PrefetchLandmark, NoPrefetchLandmark: + // Ignore existing landmark + continue + } + + // Add entry. If it already exists, replace it. + if _, ok := tf.get(h.Name); ok { + tf.remove(h.Name) + } + tf.add(&entry{ + header: h, + payload: io.NewSectionReader(in, pw.currentPos(), h.Size), + }) + } + + return tf, nil +} + +func moveRec(name string, in *tarFile, out *tarFile) error { + name = cleanEntryName(name) + if name == "" { // root directory. stop recursion. + if e, ok := in.get(name); ok { + // entry of the root directory exists. we should move it as well. + // this case will occur if tar entries are prefixed with "./", "/", etc. + out.add(e) + in.remove(name) + } + return nil + } + + _, okIn := in.get(name) + _, okOut := out.get(name) + if !okIn && !okOut { + return fmt.Errorf("file: %q: %w", name, errNotFound) + } + + parent, _ := path.Split(strings.TrimSuffix(name, "/")) + if err := moveRec(parent, in, out); err != nil { + return err + } + if e, ok := in.get(name); ok && e.header.Typeflag == tar.TypeLink { + if err := moveRec(e.header.Linkname, in, out); err != nil { + return err + } + } + if e, ok := in.get(name); ok { + out.add(e) + in.remove(name) + } + return nil +} + +type entry struct { + header *tar.Header + payload io.ReadSeeker +} + +type tarFile struct { + index map[string]*entry + stream []*entry +} + +func (f *tarFile) add(e *entry) { + if f.index == nil { + f.index = make(map[string]*entry) + } + f.index[cleanEntryName(e.header.Name)] = e + f.stream = append(f.stream, e) +} + +func (f *tarFile) remove(name string) { + name = cleanEntryName(name) + if f.index != nil { + delete(f.index, name) + } + var filtered []*entry + for _, e := range f.stream { + if cleanEntryName(e.header.Name) == name { + continue + } + filtered = append(filtered, e) + } + f.stream = filtered +} + +func (f *tarFile) get(name string) (e *entry, ok bool) { + if f.index == nil { + return nil, false + } + e, ok = f.index[cleanEntryName(name)] + return +} + +func (f *tarFile) dump() []*entry { + return f.stream +} + +type readCloser struct { + io.Reader + closeFunc func() error +} + +func (rc readCloser) Close() error { + return rc.closeFunc() +} + +func fileSectionReader(file *os.File) (*io.SectionReader, error) { + info, err := file.Stat() + if err != nil { + return nil, err + } + return io.NewSectionReader(file, 0, info.Size()), nil +} + +func newTempFiles() *tempFiles { + return &tempFiles{} +} + +type tempFiles struct { + files []*os.File + filesMu sync.Mutex +} + +func (tf *tempFiles) TempFile(dir, pattern string) (*os.File, error) { + f, err := ioutil.TempFile(dir, pattern) + if err != nil { + return nil, err + } + tf.filesMu.Lock() + tf.files = append(tf.files, f) + tf.filesMu.Unlock() + return f, nil +} + +func (tf *tempFiles) CleanupAll() error { + tf.filesMu.Lock() + defer tf.filesMu.Unlock() + var allErr []error + for _, f := range tf.files { + if err := f.Close(); err != nil { + allErr = append(allErr, err) + } + if err := os.Remove(f.Name()); err != nil { + allErr = append(allErr, err) + } + } + tf.files = nil + return errorutil.Aggregate(allErr) +} + +func newCountReader(r io.ReaderAt) (*countReader, error) { + pos := int64(0) + return &countReader{r: r, cPos: &pos}, nil +} + +type countReader struct { + r io.ReaderAt + cPos *int64 + + mu sync.Mutex +} + +func (cr *countReader) Read(p []byte) (int, error) { + cr.mu.Lock() + defer cr.mu.Unlock() + + n, err := cr.r.ReadAt(p, *cr.cPos) + if err == nil { + *cr.cPos += int64(n) + } + return n, err +} + +func (cr *countReader) Seek(offset int64, whence int) (int64, error) { + cr.mu.Lock() + defer cr.mu.Unlock() + + switch whence { + default: + return 0, fmt.Errorf("Unknown whence: %v", whence) + case io.SeekStart: + case io.SeekCurrent: + offset += *cr.cPos + case io.SeekEnd: + return 0, fmt.Errorf("Unsupported whence: %v", whence) + } + + if offset < 0 { + return 0, fmt.Errorf("invalid offset") + } + *cr.cPos = offset + return offset, nil +} + +func (cr *countReader) currentPos() int64 { + cr.mu.Lock() + defer cr.mu.Unlock() + + return *cr.cPos +} + +func decompressBlob(org *io.SectionReader, tmp *tempFiles) (*io.SectionReader, error) { + if org.Size() < 4 { + return org, nil + } + src := make([]byte, 4) + if _, err := org.Read(src); err != nil && err != io.EOF { + return nil, err + } + var dR io.Reader + if bytes.Equal([]byte{0x1F, 0x8B, 0x08}, src[:3]) { + // gzip + dgR, err := gzip.NewReader(io.NewSectionReader(org, 0, org.Size())) + if err != nil { + return nil, err + } + defer dgR.Close() + dR = io.Reader(dgR) + } else if bytes.Equal([]byte{0x28, 0xb5, 0x2f, 0xfd}, src[:4]) { + // zstd + dzR, err := zstd.NewReader(io.NewSectionReader(org, 0, org.Size())) + if err != nil { + return nil, err + } + defer dzR.Close() + dR = io.Reader(dzR) + } else { + // uncompressed + return io.NewSectionReader(org, 0, org.Size()), nil + } + b, err := tmp.TempFile("", "uncompresseddata") + if err != nil { + return nil, err + } + if _, err := io.Copy(b, dR); err != nil { + return nil, err + } + return fileSectionReader(b) +} diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/errorutil/errors.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/errorutil/errors.go new file mode 100644 index 0000000000..6de78b02dc --- /dev/null +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/errorutil/errors.go @@ -0,0 +1,40 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package errorutil + +import ( + "errors" + "fmt" + "strings" +) + +// Aggregate combines a list of errors into a single new error. +func Aggregate(errs []error) error { + switch len(errs) { + case 0: + return nil + case 1: + return errs[0] + default: + points := make([]string, len(errs)+1) + points[0] = fmt.Sprintf("%d error(s) occurred:", len(errs)) + for i, err := range errs { + points[i+1] = fmt.Sprintf("* %s", err) + } + return errors.New(strings.Join(points, "\n\t")) + } +} diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go new file mode 100644 index 0000000000..4b655c1453 --- /dev/null +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go @@ -0,0 +1,1042 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +/* + Copyright 2019 The Go Authors. All rights reserved. + Use of this source code is governed by a BSD-style + license that can be found in the LICENSE file. +*/ + +package estargz + +import ( + "bufio" + "bytes" + "compress/gzip" + "crypto/sha256" + "errors" + "fmt" + "hash" + "io" + "io/ioutil" + "os" + "path" + "sort" + "strings" + "sync" + "time" + + "github.com/containerd/stargz-snapshotter/estargz/errorutil" + digest "github.com/opencontainers/go-digest" + "github.com/vbatts/tar-split/archive/tar" +) + +// A Reader permits random access reads from a stargz file. +type Reader struct { + sr *io.SectionReader + toc *JTOC + tocDigest digest.Digest + + // m stores all non-chunk entries, keyed by name. + m map[string]*TOCEntry + + // chunks stores all TOCEntry values for regular files that + // are split up. For a file with a single chunk, it's only + // stored in m. + chunks map[string][]*TOCEntry + + decompressor Decompressor +} + +type openOpts struct { + tocOffset int64 + decompressors []Decompressor + telemetry *Telemetry +} + +// OpenOption is an option used during opening the layer +type OpenOption func(o *openOpts) error + +// WithTOCOffset option specifies the offset of TOC +func WithTOCOffset(tocOffset int64) OpenOption { + return func(o *openOpts) error { + o.tocOffset = tocOffset + return nil + } +} + +// WithDecompressors option specifies decompressors to use. +// Default is gzip-based decompressor. +func WithDecompressors(decompressors ...Decompressor) OpenOption { + return func(o *openOpts) error { + o.decompressors = decompressors + return nil + } +} + +// WithTelemetry option specifies the telemetry hooks +func WithTelemetry(telemetry *Telemetry) OpenOption { + return func(o *openOpts) error { + o.telemetry = telemetry + return nil + } +} + +// MeasureLatencyHook is a func which takes start time and records the diff +type MeasureLatencyHook func(time.Time) + +// Telemetry is a struct which defines telemetry hooks. By implementing these hooks you should be able to record +// the latency metrics of the respective steps of estargz open operation. To be used with estargz.OpenWithTelemetry(...) +type Telemetry struct { + GetFooterLatency MeasureLatencyHook // measure time to get stargz footer (in milliseconds) + GetTocLatency MeasureLatencyHook // measure time to GET TOC JSON (in milliseconds) + DeserializeTocLatency MeasureLatencyHook // measure time to deserialize TOC JSON (in milliseconds) +} + +// Open opens a stargz file for reading. +// The behavior is configurable using options. +// +// Note that each entry name is normalized as the path that is relative to root. +func Open(sr *io.SectionReader, opt ...OpenOption) (*Reader, error) { + var opts openOpts + for _, o := range opt { + if err := o(&opts); err != nil { + return nil, err + } + } + + gzipCompressors := []Decompressor{new(GzipDecompressor), new(LegacyGzipDecompressor)} + decompressors := append(gzipCompressors, opts.decompressors...) + + // Determine the size to fetch. Try to fetch as many bytes as possible. + fetchSize := maxFooterSize(sr.Size(), decompressors...) + if maybeTocOffset := opts.tocOffset; maybeTocOffset > fetchSize { + if maybeTocOffset > sr.Size() { + return nil, fmt.Errorf("blob size %d is smaller than the toc offset", sr.Size()) + } + fetchSize = sr.Size() - maybeTocOffset + } + + start := time.Now() // before getting layer footer + footer := make([]byte, fetchSize) + if _, err := sr.ReadAt(footer, sr.Size()-fetchSize); err != nil { + return nil, fmt.Errorf("error reading footer: %v", err) + } + if opts.telemetry != nil && opts.telemetry.GetFooterLatency != nil { + opts.telemetry.GetFooterLatency(start) + } + + var allErr []error + var found bool + var r *Reader + for _, d := range decompressors { + fSize := d.FooterSize() + fOffset := positive(int64(len(footer)) - fSize) + maybeTocBytes := footer[:fOffset] + _, tocOffset, tocSize, err := d.ParseFooter(footer[fOffset:]) + if err != nil { + allErr = append(allErr, err) + continue + } + if tocSize <= 0 { + tocSize = sr.Size() - tocOffset - fSize + } + if tocSize < int64(len(maybeTocBytes)) { + maybeTocBytes = maybeTocBytes[:tocSize] + } + r, err = parseTOC(d, sr, tocOffset, tocSize, maybeTocBytes, opts) + if err == nil { + found = true + break + } + allErr = append(allErr, err) + } + if !found { + return nil, errorutil.Aggregate(allErr) + } + if err := r.initFields(); err != nil { + return nil, fmt.Errorf("failed to initialize fields of entries: %v", err) + } + return r, nil +} + +// OpenFooter extracts and parses footer from the given blob. +// only supports gzip-based eStargz. +func OpenFooter(sr *io.SectionReader) (tocOffset int64, footerSize int64, rErr error) { + if sr.Size() < FooterSize && sr.Size() < legacyFooterSize { + return 0, 0, fmt.Errorf("blob size %d is smaller than the footer size", sr.Size()) + } + var footer [FooterSize]byte + if _, err := sr.ReadAt(footer[:], sr.Size()-FooterSize); err != nil { + return 0, 0, fmt.Errorf("error reading footer: %v", err) + } + var allErr []error + for _, d := range []Decompressor{new(GzipDecompressor), new(LegacyGzipDecompressor)} { + fSize := d.FooterSize() + fOffset := positive(int64(len(footer)) - fSize) + _, tocOffset, _, err := d.ParseFooter(footer[fOffset:]) + if err == nil { + return tocOffset, fSize, err + } + allErr = append(allErr, err) + } + return 0, 0, errorutil.Aggregate(allErr) +} + +// initFields populates the Reader from r.toc after decoding it from +// JSON. +// +// Unexported fields are populated and TOCEntry fields that were +// implicit in the JSON are populated. +func (r *Reader) initFields() error { + r.m = make(map[string]*TOCEntry, len(r.toc.Entries)) + r.chunks = make(map[string][]*TOCEntry) + var lastPath string + uname := map[int]string{} + gname := map[int]string{} + var lastRegEnt *TOCEntry + for _, ent := range r.toc.Entries { + ent.Name = cleanEntryName(ent.Name) + if ent.Type == "reg" { + lastRegEnt = ent + } + if ent.Type == "chunk" { + ent.Name = lastPath + r.chunks[ent.Name] = append(r.chunks[ent.Name], ent) + if ent.ChunkSize == 0 && lastRegEnt != nil { + ent.ChunkSize = lastRegEnt.Size - ent.ChunkOffset + } + } else { + lastPath = ent.Name + + if ent.Uname != "" { + uname[ent.UID] = ent.Uname + } else { + ent.Uname = uname[ent.UID] + } + if ent.Gname != "" { + gname[ent.GID] = ent.Gname + } else { + ent.Gname = uname[ent.GID] + } + + ent.modTime, _ = time.Parse(time.RFC3339, ent.ModTime3339) + + if ent.Type == "dir" { + ent.NumLink++ // Parent dir links to this directory + } + r.m[ent.Name] = ent + } + if ent.Type == "reg" && ent.ChunkSize > 0 && ent.ChunkSize < ent.Size { + r.chunks[ent.Name] = make([]*TOCEntry, 0, ent.Size/ent.ChunkSize+1) + r.chunks[ent.Name] = append(r.chunks[ent.Name], ent) + } + if ent.ChunkSize == 0 && ent.Size != 0 { + ent.ChunkSize = ent.Size + } + } + + // Populate children, add implicit directories: + for _, ent := range r.toc.Entries { + if ent.Type == "chunk" { + continue + } + // add "foo/": + // add "foo" child to "" (creating "" if necessary) + // + // add "foo/bar/": + // add "bar" child to "foo" (creating "foo" if necessary) + // + // add "foo/bar.txt": + // add "bar.txt" child to "foo" (creating "foo" if necessary) + // + // add "a/b/c/d/e/f.txt": + // create "a/b/c/d/e" node + // add "f.txt" child to "e" + + name := ent.Name + pdirName := parentDir(name) + if name == pdirName { + // This entry and its parent are the same. + // Ignore this for avoiding infinite loop of the reference. + // The example case where this can occur is when tar contains the root + // directory itself (e.g. "./", "/"). + continue + } + pdir := r.getOrCreateDir(pdirName) + ent.NumLink++ // at least one name(ent.Name) references this entry. + if ent.Type == "hardlink" { + org, err := r.getSource(ent) + if err != nil { + return err + } + org.NumLink++ // original entry is referenced by this ent.Name. + ent = org + } + pdir.addChild(path.Base(name), ent) + } + + lastOffset := r.sr.Size() + for i := len(r.toc.Entries) - 1; i >= 0; i-- { + e := r.toc.Entries[i] + if e.isDataType() { + e.nextOffset = lastOffset + } + if e.Offset != 0 { + lastOffset = e.Offset + } + } + + return nil +} + +func (r *Reader) getSource(ent *TOCEntry) (_ *TOCEntry, err error) { + if ent.Type == "hardlink" { + org, ok := r.m[cleanEntryName(ent.LinkName)] + if !ok { + return nil, fmt.Errorf("%q is a hardlink but the linkname %q isn't found", ent.Name, ent.LinkName) + } + ent, err = r.getSource(org) + if err != nil { + return nil, err + } + } + return ent, nil +} + +func parentDir(p string) string { + dir, _ := path.Split(p) + return strings.TrimSuffix(dir, "/") +} + +func (r *Reader) getOrCreateDir(d string) *TOCEntry { + e, ok := r.m[d] + if !ok { + e = &TOCEntry{ + Name: d, + Type: "dir", + Mode: 0755, + NumLink: 2, // The directory itself(.) and the parent link to this directory. + } + r.m[d] = e + if d != "" { + pdir := r.getOrCreateDir(parentDir(d)) + pdir.addChild(path.Base(d), e) + } + } + return e +} + +func (r *Reader) TOCDigest() digest.Digest { + return r.tocDigest +} + +// VerifyTOC checks that the TOC JSON in the passed blob matches the +// passed digests and that the TOC JSON contains digests for all chunks +// contained in the blob. If the verification succceeds, this function +// returns TOCEntryVerifier which holds all chunk digests in the stargz blob. +func (r *Reader) VerifyTOC(tocDigest digest.Digest) (TOCEntryVerifier, error) { + // Verify the digest of TOC JSON + if r.tocDigest != tocDigest { + return nil, fmt.Errorf("invalid TOC JSON %q; want %q", r.tocDigest, tocDigest) + } + return r.Verifiers() +} + +// Verifiers returns TOCEntryVerifier of this chunk. Use VerifyTOC instead in most cases +// because this doesn't verify TOC. +func (r *Reader) Verifiers() (TOCEntryVerifier, error) { + chunkDigestMap := make(map[int64]digest.Digest) // map from chunk offset to the chunk digest + regDigestMap := make(map[int64]digest.Digest) // map from chunk offset to the reg file digest + var chunkDigestMapIncomplete bool + var regDigestMapIncomplete bool + var containsChunk bool + for _, e := range r.toc.Entries { + if e.Type != "reg" && e.Type != "chunk" { + continue + } + + // offset must be unique in stargz blob + _, dOK := chunkDigestMap[e.Offset] + _, rOK := regDigestMap[e.Offset] + if dOK || rOK { + return nil, fmt.Errorf("offset %d found twice", e.Offset) + } + + if e.Type == "reg" { + if e.Size == 0 { + continue // ignores empty file + } + + // record the digest of regular file payload + if e.Digest != "" { + d, err := digest.Parse(e.Digest) + if err != nil { + return nil, fmt.Errorf("failed to parse regular file digest %q: %w", e.Digest, err) + } + regDigestMap[e.Offset] = d + } else { + regDigestMapIncomplete = true + } + } else { + containsChunk = true // this layer contains "chunk" entries. + } + + // "reg" also can contain ChunkDigest (e.g. when "reg" is the first entry of + // chunked file) + if e.ChunkDigest != "" { + d, err := digest.Parse(e.ChunkDigest) + if err != nil { + return nil, fmt.Errorf("failed to parse chunk digest %q: %w", e.ChunkDigest, err) + } + chunkDigestMap[e.Offset] = d + } else { + chunkDigestMapIncomplete = true + } + } + + if chunkDigestMapIncomplete { + // Though some chunk digests are not found, if this layer doesn't contain + // "chunk"s and all digest of "reg" files are recorded, we can use them instead. + if !containsChunk && !regDigestMapIncomplete { + return &verifier{digestMap: regDigestMap}, nil + } + return nil, fmt.Errorf("some ChunkDigest not found in TOC JSON") + } + + return &verifier{digestMap: chunkDigestMap}, nil +} + +// verifier is an implementation of TOCEntryVerifier which holds verifiers keyed by +// offset of the chunk. +type verifier struct { + digestMap map[int64]digest.Digest + digestMapMu sync.Mutex +} + +// Verifier returns a content verifier specified by TOCEntry. +func (v *verifier) Verifier(ce *TOCEntry) (digest.Verifier, error) { + v.digestMapMu.Lock() + defer v.digestMapMu.Unlock() + d, ok := v.digestMap[ce.Offset] + if !ok { + return nil, fmt.Errorf("verifier for offset=%d,size=%d hasn't been registered", + ce.Offset, ce.ChunkSize) + } + return d.Verifier(), nil +} + +// ChunkEntryForOffset returns the TOCEntry containing the byte of the +// named file at the given offset within the file. +// Name must be absolute path or one that is relative to root. +func (r *Reader) ChunkEntryForOffset(name string, offset int64) (e *TOCEntry, ok bool) { + name = cleanEntryName(name) + e, ok = r.Lookup(name) + if !ok || !e.isDataType() { + return nil, false + } + ents := r.chunks[name] + if len(ents) < 2 { + if offset >= e.ChunkSize { + return nil, false + } + return e, true + } + i := sort.Search(len(ents), func(i int) bool { + e := ents[i] + return e.ChunkOffset >= offset || (offset > e.ChunkOffset && offset < e.ChunkOffset+e.ChunkSize) + }) + if i == len(ents) { + return nil, false + } + return ents[i], true +} + +// Lookup returns the Table of Contents entry for the given path. +// +// To get the root directory, use the empty string. +// Path must be absolute path or one that is relative to root. +func (r *Reader) Lookup(path string) (e *TOCEntry, ok bool) { + path = cleanEntryName(path) + if r == nil { + return + } + e, ok = r.m[path] + if ok && e.Type == "hardlink" { + var err error + e, err = r.getSource(e) + if err != nil { + return nil, false + } + } + return +} + +// OpenFile returns the reader of the specified file payload. +// +// Name must be absolute path or one that is relative to root. +func (r *Reader) OpenFile(name string) (*io.SectionReader, error) { + name = cleanEntryName(name) + ent, ok := r.Lookup(name) + if !ok { + // TODO: come up with some error plan. This is lazy: + return nil, &os.PathError{ + Path: name, + Op: "OpenFile", + Err: os.ErrNotExist, + } + } + if ent.Type != "reg" { + return nil, &os.PathError{ + Path: name, + Op: "OpenFile", + Err: errors.New("not a regular file"), + } + } + fr := &fileReader{ + r: r, + size: ent.Size, + ents: r.getChunks(ent), + } + return io.NewSectionReader(fr, 0, fr.size), nil +} + +func (r *Reader) getChunks(ent *TOCEntry) []*TOCEntry { + if ents, ok := r.chunks[ent.Name]; ok { + return ents + } + return []*TOCEntry{ent} +} + +type fileReader struct { + r *Reader + size int64 + ents []*TOCEntry // 1 or more reg/chunk entries +} + +func (fr *fileReader) ReadAt(p []byte, off int64) (n int, err error) { + if off >= fr.size { + return 0, io.EOF + } + if off < 0 { + return 0, errors.New("invalid offset") + } + var i int + if len(fr.ents) > 1 { + i = sort.Search(len(fr.ents), func(i int) bool { + return fr.ents[i].ChunkOffset >= off + }) + if i == len(fr.ents) { + i = len(fr.ents) - 1 + } + } + ent := fr.ents[i] + if ent.ChunkOffset > off { + if i == 0 { + return 0, errors.New("internal error; first chunk offset is non-zero") + } + ent = fr.ents[i-1] + } + + // If ent is a chunk of a large file, adjust the ReadAt + // offset by the chunk's offset. + off -= ent.ChunkOffset + + finalEnt := fr.ents[len(fr.ents)-1] + compressedOff := ent.Offset + // compressedBytesRemain is the number of compressed bytes in this + // file remaining, over 1+ chunks. + compressedBytesRemain := finalEnt.NextOffset() - compressedOff + + sr := io.NewSectionReader(fr.r.sr, compressedOff, compressedBytesRemain) + + const maxRead = 2 << 20 + var bufSize = maxRead + if compressedBytesRemain < maxRead { + bufSize = int(compressedBytesRemain) + } + + br := bufio.NewReaderSize(sr, bufSize) + if _, err := br.Peek(bufSize); err != nil { + return 0, fmt.Errorf("fileReader.ReadAt.peek: %v", err) + } + + dr, err := fr.r.decompressor.Reader(br) + if err != nil { + return 0, fmt.Errorf("fileReader.ReadAt.decompressor.Reader: %v", err) + } + defer dr.Close() + if n, err := io.CopyN(ioutil.Discard, dr, off); n != off || err != nil { + return 0, fmt.Errorf("discard of %d bytes = %v, %v", off, n, err) + } + return io.ReadFull(dr, p) +} + +// A Writer writes stargz files. +// +// Use NewWriter to create a new Writer. +type Writer struct { + bw *bufio.Writer + cw *countWriter + toc *JTOC + diffHash hash.Hash // SHA-256 of uncompressed tar + + closed bool + gz io.WriteCloser + lastUsername map[int]string + lastGroupname map[int]string + compressor Compressor + + // ChunkSize optionally controls the maximum number of bytes + // of data of a regular file that can be written in one gzip + // stream before a new gzip stream is started. + // Zero means to use a default, currently 4 MiB. + ChunkSize int +} + +// currentCompressionWriter writes to the current w.gz field, which can +// change throughout writing a tar entry. +// +// Additionally, it updates w's SHA-256 of the uncompressed bytes +// of the tar file. +type currentCompressionWriter struct{ w *Writer } + +func (ccw currentCompressionWriter) Write(p []byte) (int, error) { + ccw.w.diffHash.Write(p) + if ccw.w.gz == nil { + if err := ccw.w.condOpenGz(); err != nil { + return 0, err + } + } + return ccw.w.gz.Write(p) +} + +func (w *Writer) chunkSize() int { + if w.ChunkSize <= 0 { + return 4 << 20 + } + return w.ChunkSize +} + +// Unpack decompresses the given estargz blob and returns a ReadCloser of the tar blob. +// TOC JSON and footer are removed. +func Unpack(sr *io.SectionReader, c Decompressor) (io.ReadCloser, error) { + footerSize := c.FooterSize() + if sr.Size() < footerSize { + return nil, fmt.Errorf("blob is too small; %d < %d", sr.Size(), footerSize) + } + footerOffset := sr.Size() - footerSize + footer := make([]byte, footerSize) + if _, err := sr.ReadAt(footer, footerOffset); err != nil { + return nil, err + } + blobPayloadSize, _, _, err := c.ParseFooter(footer) + if err != nil { + return nil, fmt.Errorf("failed to parse footer: %w", err) + } + return c.Reader(io.LimitReader(sr, blobPayloadSize)) +} + +// NewWriter returns a new stargz writer (gzip-based) writing to w. +// +// The writer must be closed to write its trailing table of contents. +func NewWriter(w io.Writer) *Writer { + return NewWriterLevel(w, gzip.BestCompression) +} + +// NewWriterLevel returns a new stargz writer (gzip-based) writing to w. +// The compression level is configurable. +// +// The writer must be closed to write its trailing table of contents. +func NewWriterLevel(w io.Writer, compressionLevel int) *Writer { + return NewWriterWithCompressor(w, NewGzipCompressorWithLevel(compressionLevel)) +} + +// NewWriterWithCompressor returns a new stargz writer writing to w. +// The compression method is configurable. +// +// The writer must be closed to write its trailing table of contents. +func NewWriterWithCompressor(w io.Writer, c Compressor) *Writer { + bw := bufio.NewWriter(w) + cw := &countWriter{w: bw} + return &Writer{ + bw: bw, + cw: cw, + toc: &JTOC{Version: 1}, + diffHash: sha256.New(), + compressor: c, + } +} + +// Close writes the stargz's table of contents and flushes all the +// buffers, returning any error. +func (w *Writer) Close() (digest.Digest, error) { + if w.closed { + return "", nil + } + defer func() { w.closed = true }() + + if err := w.closeGz(); err != nil { + return "", err + } + + // Write the TOC index and footer. + tocDigest, err := w.compressor.WriteTOCAndFooter(w.cw, w.cw.n, w.toc, w.diffHash) + if err != nil { + return "", err + } + if err := w.bw.Flush(); err != nil { + return "", err + } + + return tocDigest, nil +} + +func (w *Writer) closeGz() error { + if w.closed { + return errors.New("write on closed Writer") + } + if w.gz != nil { + if err := w.gz.Close(); err != nil { + return err + } + w.gz = nil + } + return nil +} + +// nameIfChanged returns name, unless it was the already the value of (*mp)[id], +// in which case it returns the empty string. +func (w *Writer) nameIfChanged(mp *map[int]string, id int, name string) string { + if name == "" { + return "" + } + if *mp == nil { + *mp = make(map[int]string) + } + if (*mp)[id] == name { + return "" + } + (*mp)[id] = name + return name +} + +func (w *Writer) condOpenGz() (err error) { + if w.gz == nil { + w.gz, err = w.compressor.Writer(w.cw) + } + return +} + +// AppendTar reads the tar or tar.gz file from r and appends +// each of its contents to w. +// +// The input r can optionally be gzip compressed but the output will +// always be compressed by the specified compressor. +func (w *Writer) AppendTar(r io.Reader) error { + return w.appendTar(r, false) +} + +// AppendTarLossLess reads the tar or tar.gz file from r and appends +// each of its contents to w. +// +// The input r can optionally be gzip compressed but the output will +// always be compressed by the specified compressor. +// +// The difference of this func with AppendTar is that this writes +// the input tar stream into w without any modification (e.g. to header bytes). +// +// Note that if the input tar stream already contains TOC JSON, this returns +// error because w cannot overwrite the TOC JSON to the one generated by w without +// lossy modification. To avoid this error, if the input stream is known to be stargz/estargz, +// you shoud decompress it and remove TOC JSON in advance. +func (w *Writer) AppendTarLossLess(r io.Reader) error { + return w.appendTar(r, true) +} + +func (w *Writer) appendTar(r io.Reader, lossless bool) error { + var src io.Reader + br := bufio.NewReader(r) + if isGzip(br) { + zr, _ := gzip.NewReader(br) + src = zr + } else { + src = io.Reader(br) + } + dst := currentCompressionWriter{w} + var tw *tar.Writer + if !lossless { + tw = tar.NewWriter(dst) // use tar writer only when this isn't lossless mode. + } + tr := tar.NewReader(src) + if lossless { + tr.RawAccounting = true + } + for { + h, err := tr.Next() + if err == io.EOF { + if lossless { + if remain := tr.RawBytes(); len(remain) > 0 { + // Collect the remaining null bytes. + // https://github.com/vbatts/tar-split/blob/80a436fd6164c557b131f7c59ed69bd81af69761/concept/main.go#L49-L53 + if _, err := dst.Write(remain); err != nil { + return err + } + } + } + break + } + if err != nil { + return fmt.Errorf("error reading from source tar: tar.Reader.Next: %v", err) + } + if cleanEntryName(h.Name) == TOCTarName { + // It is possible for a layer to be "stargzified" twice during the + // distribution lifecycle. So we reserve "TOCTarName" here to avoid + // duplicated entries in the resulting layer. + if lossless { + // We cannot handle this in lossless way. + return fmt.Errorf("existing TOC JSON is not allowed; decompress layer before append") + } + continue + } + + xattrs := make(map[string][]byte) + const xattrPAXRecordsPrefix = "SCHILY.xattr." + if h.PAXRecords != nil { + for k, v := range h.PAXRecords { + if strings.HasPrefix(k, xattrPAXRecordsPrefix) { + xattrs[k[len(xattrPAXRecordsPrefix):]] = []byte(v) + } + } + } + ent := &TOCEntry{ + Name: h.Name, + Mode: h.Mode, + UID: h.Uid, + GID: h.Gid, + Uname: w.nameIfChanged(&w.lastUsername, h.Uid, h.Uname), + Gname: w.nameIfChanged(&w.lastGroupname, h.Gid, h.Gname), + ModTime3339: formatModtime(h.ModTime), + Xattrs: xattrs, + } + if err := w.condOpenGz(); err != nil { + return err + } + if tw != nil { + if err := tw.WriteHeader(h); err != nil { + return err + } + } else { + if _, err := dst.Write(tr.RawBytes()); err != nil { + return err + } + } + switch h.Typeflag { + case tar.TypeLink: + ent.Type = "hardlink" + ent.LinkName = h.Linkname + case tar.TypeSymlink: + ent.Type = "symlink" + ent.LinkName = h.Linkname + case tar.TypeDir: + ent.Type = "dir" + case tar.TypeReg: + ent.Type = "reg" + ent.Size = h.Size + case tar.TypeChar: + ent.Type = "char" + ent.DevMajor = int(h.Devmajor) + ent.DevMinor = int(h.Devminor) + case tar.TypeBlock: + ent.Type = "block" + ent.DevMajor = int(h.Devmajor) + ent.DevMinor = int(h.Devminor) + case tar.TypeFifo: + ent.Type = "fifo" + default: + return fmt.Errorf("unsupported input tar entry %q", h.Typeflag) + } + + // We need to keep a reference to the TOC entry for regular files, so that we + // can fill the digest later. + var regFileEntry *TOCEntry + var payloadDigest digest.Digester + if h.Typeflag == tar.TypeReg { + regFileEntry = ent + payloadDigest = digest.Canonical.Digester() + } + + if h.Typeflag == tar.TypeReg && ent.Size > 0 { + var written int64 + totalSize := ent.Size // save it before we destroy ent + tee := io.TeeReader(tr, payloadDigest.Hash()) + for written < totalSize { + if err := w.closeGz(); err != nil { + return err + } + + chunkSize := int64(w.chunkSize()) + remain := totalSize - written + if remain < chunkSize { + chunkSize = remain + } else { + ent.ChunkSize = chunkSize + } + ent.Offset = w.cw.n + ent.ChunkOffset = written + chunkDigest := digest.Canonical.Digester() + + if err := w.condOpenGz(); err != nil { + return err + } + + teeChunk := io.TeeReader(tee, chunkDigest.Hash()) + var out io.Writer + if tw != nil { + out = tw + } else { + out = dst + } + if _, err := io.CopyN(out, teeChunk, chunkSize); err != nil { + return fmt.Errorf("error copying %q: %v", h.Name, err) + } + ent.ChunkDigest = chunkDigest.Digest().String() + w.toc.Entries = append(w.toc.Entries, ent) + written += chunkSize + ent = &TOCEntry{ + Name: h.Name, + Type: "chunk", + } + } + } else { + w.toc.Entries = append(w.toc.Entries, ent) + } + if payloadDigest != nil { + regFileEntry.Digest = payloadDigest.Digest().String() + } + if tw != nil { + if err := tw.Flush(); err != nil { + return err + } + } + } + remainDest := ioutil.Discard + if lossless { + remainDest = dst // Preserve the remaining bytes in lossless mode + } + _, err := io.Copy(remainDest, src) + return err +} + +// DiffID returns the SHA-256 of the uncompressed tar bytes. +// It is only valid to call DiffID after Close. +func (w *Writer) DiffID() string { + return fmt.Sprintf("sha256:%x", w.diffHash.Sum(nil)) +} + +func maxFooterSize(blobSize int64, decompressors ...Decompressor) (res int64) { + for _, d := range decompressors { + if s := d.FooterSize(); res < s && s <= blobSize { + res = s + } + } + return +} + +func parseTOC(d Decompressor, sr *io.SectionReader, tocOff, tocSize int64, tocBytes []byte, opts openOpts) (*Reader, error) { + if len(tocBytes) > 0 { + start := time.Now() + toc, tocDgst, err := d.ParseTOC(bytes.NewReader(tocBytes)) + if err == nil { + if opts.telemetry != nil && opts.telemetry.DeserializeTocLatency != nil { + opts.telemetry.DeserializeTocLatency(start) + } + return &Reader{ + sr: sr, + toc: toc, + tocDigest: tocDgst, + decompressor: d, + }, nil + } + } + + start := time.Now() + tocBytes = make([]byte, tocSize) + if _, err := sr.ReadAt(tocBytes, tocOff); err != nil { + return nil, fmt.Errorf("error reading %d byte TOC targz: %v", len(tocBytes), err) + } + if opts.telemetry != nil && opts.telemetry.GetTocLatency != nil { + opts.telemetry.GetTocLatency(start) + } + start = time.Now() + toc, tocDgst, err := d.ParseTOC(bytes.NewReader(tocBytes)) + if err != nil { + return nil, err + } + if opts.telemetry != nil && opts.telemetry.DeserializeTocLatency != nil { + opts.telemetry.DeserializeTocLatency(start) + } + return &Reader{ + sr: sr, + toc: toc, + tocDigest: tocDgst, + decompressor: d, + }, nil +} + +func formatModtime(t time.Time) string { + if t.IsZero() || t.Unix() == 0 { + return "" + } + return t.UTC().Round(time.Second).Format(time.RFC3339) +} + +func cleanEntryName(name string) string { + // Use path.Clean to consistently deal with path separators across platforms. + return strings.TrimPrefix(path.Clean("/"+name), "/") +} + +// countWriter counts how many bytes have been written to its wrapped +// io.Writer. +type countWriter struct { + w io.Writer + n int64 +} + +func (cw *countWriter) Write(p []byte) (n int, err error) { + n, err = cw.w.Write(p) + cw.n += int64(n) + return +} + +// isGzip reports whether br is positioned right before an upcoming gzip stream. +// It does not consume any bytes from br. +func isGzip(br *bufio.Reader) bool { + const ( + gzipID1 = 0x1f + gzipID2 = 0x8b + gzipDeflate = 8 + ) + peek, _ := br.Peek(3) + return len(peek) >= 3 && peek[0] == gzipID1 && peek[1] == gzipID2 && peek[2] == gzipDeflate +} + +func positive(n int64) int64 { + if n < 0 { + return 0 + } + return n +} diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/go.mod b/vendor/github.com/containerd/stargz-snapshotter/estargz/go.mod new file mode 100644 index 0000000000..9256b36b9e --- /dev/null +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/go.mod @@ -0,0 +1,10 @@ +module github.com/containerd/stargz-snapshotter/estargz + +go 1.16 + +require ( + github.com/klauspost/compress v1.14.3 + github.com/opencontainers/go-digest v1.0.0 + github.com/vbatts/tar-split v0.11.2 + golang.org/x/sync v0.0.0-20201207232520-09787c993a3a +) diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/go.sum b/vendor/github.com/containerd/stargz-snapshotter/estargz/go.sum new file mode 100644 index 0000000000..800028d920 --- /dev/null +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/go.sum @@ -0,0 +1,20 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/klauspost/compress v1.14.3 h1:DQv1WP+iS4srNjibdnHtqu8JNWCDMluj5NzPnFJsnvk= +github.com/klauspost/compress v1.14.3/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= +github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go new file mode 100644 index 0000000000..591d7a62e1 --- /dev/null +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go @@ -0,0 +1,237 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +/* + Copyright 2019 The Go Authors. All rights reserved. + Use of this source code is governed by a BSD-style + license that can be found in the LICENSE file. +*/ + +package estargz + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "encoding/binary" + "encoding/json" + "fmt" + "hash" + "io" + "strconv" + + digest "github.com/opencontainers/go-digest" +) + +type gzipCompression struct { + *GzipCompressor + *GzipDecompressor +} + +func newGzipCompressionWithLevel(level int) Compression { + return &gzipCompression{ + &GzipCompressor{level}, + &GzipDecompressor{}, + } +} + +func NewGzipCompressor() *GzipCompressor { + return &GzipCompressor{gzip.BestCompression} +} + +func NewGzipCompressorWithLevel(level int) *GzipCompressor { + return &GzipCompressor{level} +} + +type GzipCompressor struct { + compressionLevel int +} + +func (gc *GzipCompressor) Writer(w io.Writer) (io.WriteCloser, error) { + return gzip.NewWriterLevel(w, gc.compressionLevel) +} + +func (gc *GzipCompressor) WriteTOCAndFooter(w io.Writer, off int64, toc *JTOC, diffHash hash.Hash) (digest.Digest, error) { + tocJSON, err := json.MarshalIndent(toc, "", "\t") + if err != nil { + return "", err + } + gz, _ := gzip.NewWriterLevel(w, gc.compressionLevel) + gw := io.Writer(gz) + if diffHash != nil { + gw = io.MultiWriter(gz, diffHash) + } + tw := tar.NewWriter(gw) + if err := tw.WriteHeader(&tar.Header{ + Typeflag: tar.TypeReg, + Name: TOCTarName, + Size: int64(len(tocJSON)), + }); err != nil { + return "", err + } + if _, err := tw.Write(tocJSON); err != nil { + return "", err + } + + if err := tw.Close(); err != nil { + return "", err + } + if err := gz.Close(); err != nil { + return "", err + } + if _, err := w.Write(gzipFooterBytes(off)); err != nil { + return "", err + } + return digest.FromBytes(tocJSON), nil +} + +// gzipFooterBytes returns the 51 bytes footer. +func gzipFooterBytes(tocOff int64) []byte { + buf := bytes.NewBuffer(make([]byte, 0, FooterSize)) + gz, _ := gzip.NewWriterLevel(buf, gzip.NoCompression) // MUST be NoCompression to keep 51 bytes + + // Extra header indicating the offset of TOCJSON + // https://tools.ietf.org/html/rfc1952#section-2.3.1.1 + header := make([]byte, 4) + header[0], header[1] = 'S', 'G' + subfield := fmt.Sprintf("%016xSTARGZ", tocOff) + binary.LittleEndian.PutUint16(header[2:4], uint16(len(subfield))) // little-endian per RFC1952 + gz.Header.Extra = append(header, []byte(subfield)...) + gz.Close() + if buf.Len() != FooterSize { + panic(fmt.Sprintf("footer buffer = %d, not %d", buf.Len(), FooterSize)) + } + return buf.Bytes() +} + +type GzipDecompressor struct{} + +func (gz *GzipDecompressor) Reader(r io.Reader) (io.ReadCloser, error) { + return gzip.NewReader(r) +} + +func (gz *GzipDecompressor) ParseTOC(r io.Reader) (toc *JTOC, tocDgst digest.Digest, err error) { + return parseTOCEStargz(r) +} + +func (gz *GzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOffset, tocSize int64, err error) { + if len(p) != FooterSize { + return 0, 0, 0, fmt.Errorf("invalid length %d cannot be parsed", len(p)) + } + zr, err := gzip.NewReader(bytes.NewReader(p)) + if err != nil { + return 0, 0, 0, err + } + defer zr.Close() + extra := zr.Header.Extra + si1, si2, subfieldlen, subfield := extra[0], extra[1], extra[2:4], extra[4:] + if si1 != 'S' || si2 != 'G' { + return 0, 0, 0, fmt.Errorf("invalid subfield IDs: %q, %q; want E, S", si1, si2) + } + if slen := binary.LittleEndian.Uint16(subfieldlen); slen != uint16(16+len("STARGZ")) { + return 0, 0, 0, fmt.Errorf("invalid length of subfield %d; want %d", slen, 16+len("STARGZ")) + } + if string(subfield[16:]) != "STARGZ" { + return 0, 0, 0, fmt.Errorf("STARGZ magic string must be included in the footer subfield") + } + tocOffset, err = strconv.ParseInt(string(subfield[:16]), 16, 64) + if err != nil { + return 0, 0, 0, fmt.Errorf("legacy: failed to parse toc offset: %w", err) + } + return tocOffset, tocOffset, 0, nil +} + +func (gz *GzipDecompressor) FooterSize() int64 { + return FooterSize +} + +func (gz *GzipDecompressor) DecompressTOC(r io.Reader) (tocJSON io.ReadCloser, err error) { + return decompressTOCEStargz(r) +} + +type LegacyGzipDecompressor struct{} + +func (gz *LegacyGzipDecompressor) Reader(r io.Reader) (io.ReadCloser, error) { + return gzip.NewReader(r) +} + +func (gz *LegacyGzipDecompressor) ParseTOC(r io.Reader) (toc *JTOC, tocDgst digest.Digest, err error) { + return parseTOCEStargz(r) +} + +func (gz *LegacyGzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOffset, tocSize int64, err error) { + if len(p) != legacyFooterSize { + return 0, 0, 0, fmt.Errorf("legacy: invalid length %d cannot be parsed", len(p)) + } + zr, err := gzip.NewReader(bytes.NewReader(p)) + if err != nil { + return 0, 0, 0, fmt.Errorf("legacy: failed to get footer gzip reader: %w", err) + } + defer zr.Close() + extra := zr.Header.Extra + if len(extra) != 16+len("STARGZ") { + return 0, 0, 0, fmt.Errorf("legacy: invalid stargz's extra field size") + } + if string(extra[16:]) != "STARGZ" { + return 0, 0, 0, fmt.Errorf("legacy: magic string STARGZ not found") + } + tocOffset, err = strconv.ParseInt(string(extra[:16]), 16, 64) + if err != nil { + return 0, 0, 0, fmt.Errorf("legacy: failed to parse toc offset: %w", err) + } + return tocOffset, tocOffset, 0, nil +} + +func (gz *LegacyGzipDecompressor) FooterSize() int64 { + return legacyFooterSize +} + +func (gz *LegacyGzipDecompressor) DecompressTOC(r io.Reader) (tocJSON io.ReadCloser, err error) { + return decompressTOCEStargz(r) +} + +func parseTOCEStargz(r io.Reader) (toc *JTOC, tocDgst digest.Digest, err error) { + tr, err := decompressTOCEStargz(r) + if err != nil { + return nil, "", err + } + dgstr := digest.Canonical.Digester() + toc = new(JTOC) + if err := json.NewDecoder(io.TeeReader(tr, dgstr.Hash())).Decode(&toc); err != nil { + return nil, "", fmt.Errorf("error decoding TOC JSON: %v", err) + } + if err := tr.Close(); err != nil { + return nil, "", err + } + return toc, dgstr.Digest(), nil +} + +func decompressTOCEStargz(r io.Reader) (tocJSON io.ReadCloser, err error) { + zr, err := gzip.NewReader(r) + if err != nil { + return nil, fmt.Errorf("malformed TOC gzip header: %v", err) + } + zr.Multistream(false) + tr := tar.NewReader(zr) + h, err := tr.Next() + if err != nil { + return nil, fmt.Errorf("failed to find tar header in TOC gzip stream: %v", err) + } + if h.Name != TOCTarName { + return nil, fmt.Errorf("TOC tar entry had name %q; expected %q", h.Name, TOCTarName) + } + return readCloser{tr, zr.Close}, nil +} diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go new file mode 100644 index 0000000000..1de13a4705 --- /dev/null +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go @@ -0,0 +1,2009 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +/* + Copyright 2019 The Go Authors. All rights reserved. + Use of this source code is governed by a BSD-style + license that can be found in the LICENSE file. +*/ + +package estargz + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "crypto/sha256" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "reflect" + "sort" + "strings" + "testing" + "time" + + "github.com/containerd/stargz-snapshotter/estargz/errorutil" + "github.com/klauspost/compress/zstd" + digest "github.com/opencontainers/go-digest" +) + +// TestingController is Compression with some helper methods necessary for testing. +type TestingController interface { + Compression + CountStreams(*testing.T, []byte) int + DiffIDOf(*testing.T, []byte) string + String() string +} + +// CompressionTestSuite tests this pkg with controllers can build valid eStargz blobs and parse them. +func CompressionTestSuite(t *testing.T, controllers ...TestingController) { + t.Run("testBuild", func(t *testing.T) { t.Parallel(); testBuild(t, controllers...) }) + t.Run("testDigestAndVerify", func(t *testing.T) { t.Parallel(); testDigestAndVerify(t, controllers...) }) + t.Run("testWriteAndOpen", func(t *testing.T) { t.Parallel(); testWriteAndOpen(t, controllers...) }) +} + +const ( + uncompressedType int = iota + gzipType + zstdType +) + +var srcCompressions = []int{ + uncompressedType, + gzipType, + zstdType, +} + +var allowedPrefix = [4]string{"", "./", "/", "../"} + +// testBuild tests the resulting stargz blob built by this pkg has the same +// contents as the normal stargz blob. +func testBuild(t *testing.T, controllers ...TestingController) { + tests := []struct { + name string + chunkSize int + in []tarEntry + }{ + { + name: "regfiles and directories", + chunkSize: 4, + in: tarOf( + file("foo", "test1"), + dir("foo2/"), + file("foo2/bar", "test2", xAttr(map[string]string{"test": "sample"})), + ), + }, + { + name: "empty files", + chunkSize: 4, + in: tarOf( + file("foo", "tttttt"), + file("foo_empty", ""), + file("foo2", "tttttt"), + file("foo_empty2", ""), + file("foo3", "tttttt"), + file("foo_empty3", ""), + file("foo4", "tttttt"), + file("foo_empty4", ""), + file("foo5", "tttttt"), + file("foo_empty5", ""), + file("foo6", "tttttt"), + ), + }, + { + name: "various files", + chunkSize: 4, + in: tarOf( + file("baz.txt", "bazbazbazbazbazbazbaz"), + file("foo.txt", "a"), + symlink("barlink", "test/bar.txt"), + dir("test/"), + dir("dev/"), + blockdev("dev/testblock", 3, 4), + fifo("dev/testfifo"), + chardev("dev/testchar1", 5, 6), + file("test/bar.txt", "testbartestbar", xAttr(map[string]string{"test2": "sample2"})), + dir("test2/"), + link("test2/bazlink", "baz.txt"), + chardev("dev/testchar2", 1, 2), + ), + }, + { + name: "no contents", + chunkSize: 4, + in: tarOf( + file("baz.txt", ""), + symlink("barlink", "test/bar.txt"), + dir("test/"), + dir("dev/"), + blockdev("dev/testblock", 3, 4), + fifo("dev/testfifo"), + chardev("dev/testchar1", 5, 6), + file("test/bar.txt", "", xAttr(map[string]string{"test2": "sample2"})), + dir("test2/"), + link("test2/bazlink", "baz.txt"), + chardev("dev/testchar2", 1, 2), + ), + }, + } + for _, tt := range tests { + for _, srcCompression := range srcCompressions { + srcCompression := srcCompression + for _, cl := range controllers { + cl := cl + for _, srcTarFormat := range []tar.Format{tar.FormatUSTAR, tar.FormatPAX, tar.FormatGNU} { + srcTarFormat := srcTarFormat + for _, prefix := range allowedPrefix { + prefix := prefix + t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,src=%d,format=%s", cl, prefix, srcCompression, srcTarFormat), func(t *testing.T) { + tarBlob := buildTar(t, tt.in, prefix, srcTarFormat) + // Test divideEntries() + entries, err := sortEntries(tarBlob, nil, nil) // identical order + if err != nil { + t.Fatalf("failed to parse tar: %v", err) + } + var merged []*entry + for _, part := range divideEntries(entries, 4) { + merged = append(merged, part...) + } + if !reflect.DeepEqual(entries, merged) { + for _, e := range entries { + t.Logf("Original: %v", e.header) + } + for _, e := range merged { + t.Logf("Merged: %v", e.header) + } + t.Errorf("divided entries couldn't be merged") + return + } + + // Prepare sample data + wantBuf := new(bytes.Buffer) + sw := NewWriterWithCompressor(wantBuf, cl) + sw.ChunkSize = tt.chunkSize + if err := sw.AppendTar(tarBlob); err != nil { + t.Fatalf("failed to append tar to want stargz: %v", err) + } + if _, err := sw.Close(); err != nil { + t.Fatalf("failed to prepare want stargz: %v", err) + } + wantData := wantBuf.Bytes() + want, err := Open(io.NewSectionReader( + bytes.NewReader(wantData), 0, int64(len(wantData))), + WithDecompressors(cl), + ) + if err != nil { + t.Fatalf("failed to parse the want stargz: %v", err) + } + + // Prepare testing data + rc, err := Build(compressBlob(t, tarBlob, srcCompression), + WithChunkSize(tt.chunkSize), WithCompression(cl)) + if err != nil { + t.Fatalf("failed to build stargz: %v", err) + } + defer rc.Close() + gotBuf := new(bytes.Buffer) + if _, err := io.Copy(gotBuf, rc); err != nil { + t.Fatalf("failed to copy built stargz blob: %v", err) + } + gotData := gotBuf.Bytes() + got, err := Open(io.NewSectionReader( + bytes.NewReader(gotBuf.Bytes()), 0, int64(len(gotData))), + WithDecompressors(cl), + ) + if err != nil { + t.Fatalf("failed to parse the got stargz: %v", err) + } + + // Check DiffID is properly calculated + rc.Close() + diffID := rc.DiffID() + wantDiffID := cl.DiffIDOf(t, gotData) + if diffID.String() != wantDiffID { + t.Errorf("DiffID = %q; want %q", diffID, wantDiffID) + } + + // Compare as stargz + if !isSameVersion(t, cl, wantData, gotData) { + t.Errorf("built stargz hasn't same json") + return + } + if !isSameEntries(t, want, got) { + t.Errorf("built stargz isn't same as the original") + return + } + + // Compare as tar.gz + if !isSameTarGz(t, cl, wantData, gotData) { + t.Errorf("built stargz isn't same tar.gz") + return + } + }) + } + } + } + } + } +} + +func isSameTarGz(t *testing.T, controller TestingController, a, b []byte) bool { + aGz, err := controller.Reader(bytes.NewReader(a)) + if err != nil { + t.Fatalf("failed to read A") + } + defer aGz.Close() + bGz, err := controller.Reader(bytes.NewReader(b)) + if err != nil { + t.Fatalf("failed to read B") + } + defer bGz.Close() + + // Same as tar's Next() method but ignores landmarks and TOCJSON file + next := func(r *tar.Reader) (h *tar.Header, err error) { + for { + if h, err = r.Next(); err != nil { + return + } + if h.Name != PrefetchLandmark && + h.Name != NoPrefetchLandmark && + h.Name != TOCTarName { + return + } + } + } + + aTar := tar.NewReader(aGz) + bTar := tar.NewReader(bGz) + for { + // Fetch and parse next header. + aH, aErr := next(aTar) + bH, bErr := next(bTar) + if aErr != nil || bErr != nil { + if aErr == io.EOF && bErr == io.EOF { + break + } + t.Fatalf("Failed to parse tar file: A: %v, B: %v", aErr, bErr) + } + if !reflect.DeepEqual(aH, bH) { + t.Logf("different header (A = %v; B = %v)", aH, bH) + return false + + } + aFile, err := ioutil.ReadAll(aTar) + if err != nil { + t.Fatal("failed to read tar payload of A") + } + bFile, err := ioutil.ReadAll(bTar) + if err != nil { + t.Fatal("failed to read tar payload of B") + } + if !bytes.Equal(aFile, bFile) { + t.Logf("different tar payload (A = %q; B = %q)", string(a), string(b)) + return false + } + } + + return true +} + +func isSameVersion(t *testing.T, controller TestingController, a, b []byte) bool { + aJTOC, _, err := parseStargz(io.NewSectionReader(bytes.NewReader(a), 0, int64(len(a))), controller) + if err != nil { + t.Fatalf("failed to parse A: %v", err) + } + bJTOC, _, err := parseStargz(io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))), controller) + if err != nil { + t.Fatalf("failed to parse B: %v", err) + } + t.Logf("A: TOCJSON: %v", dumpTOCJSON(t, aJTOC)) + t.Logf("B: TOCJSON: %v", dumpTOCJSON(t, bJTOC)) + return aJTOC.Version == bJTOC.Version +} + +func isSameEntries(t *testing.T, a, b *Reader) bool { + aroot, ok := a.Lookup("") + if !ok { + t.Fatalf("failed to get root of A") + } + broot, ok := b.Lookup("") + if !ok { + t.Fatalf("failed to get root of B") + } + aEntry := stargzEntry{aroot, a} + bEntry := stargzEntry{broot, b} + return contains(t, aEntry, bEntry) && contains(t, bEntry, aEntry) +} + +func compressBlob(t *testing.T, src *io.SectionReader, srcCompression int) *io.SectionReader { + buf := new(bytes.Buffer) + var w io.WriteCloser + var err error + if srcCompression == gzipType { + w = gzip.NewWriter(buf) + } else if srcCompression == zstdType { + w, err = zstd.NewWriter(buf) + if err != nil { + t.Fatalf("failed to init zstd writer: %v", err) + } + } else { + return src + } + src.Seek(0, io.SeekStart) + if _, err := io.Copy(w, src); err != nil { + t.Fatalf("failed to compress source") + } + if err := w.Close(); err != nil { + t.Fatalf("failed to finalize compress source") + } + data := buf.Bytes() + return io.NewSectionReader(bytes.NewReader(data), 0, int64(len(data))) + +} + +type stargzEntry struct { + e *TOCEntry + r *Reader +} + +// contains checks if all child entries in "b" are also contained in "a". +// This function also checks if the files/chunks contain the same contents among "a" and "b". +func contains(t *testing.T, a, b stargzEntry) bool { + ae, ar := a.e, a.r + be, br := b.e, b.r + t.Logf("Comparing: %q vs %q", ae.Name, be.Name) + if !equalEntry(ae, be) { + t.Logf("%q != %q: entry: a: %v, b: %v", ae.Name, be.Name, ae, be) + return false + } + if ae.Type == "dir" { + t.Logf("Directory: %q vs %q: %v vs %v", ae.Name, be.Name, + allChildrenName(ae), allChildrenName(be)) + iscontain := true + ae.ForeachChild(func(aBaseName string, aChild *TOCEntry) bool { + // Walk through all files on this stargz file. + + if aChild.Name == PrefetchLandmark || + aChild.Name == NoPrefetchLandmark { + return true // Ignore landmarks + } + + // Ignore a TOCEntry of "./" (formated as "" by stargz lib) on root directory + // because this points to the root directory itself. + if aChild.Name == "" && ae.Name == "" { + return true + } + + bChild, ok := be.LookupChild(aBaseName) + if !ok { + t.Logf("%q (base: %q): not found in b: %v", + ae.Name, aBaseName, allChildrenName(be)) + iscontain = false + return false + } + + childcontain := contains(t, stargzEntry{aChild, a.r}, stargzEntry{bChild, b.r}) + if !childcontain { + t.Logf("%q != %q: non-equal dir", ae.Name, be.Name) + iscontain = false + return false + } + return true + }) + return iscontain + } else if ae.Type == "reg" { + af, err := ar.OpenFile(ae.Name) + if err != nil { + t.Fatalf("failed to open file %q on A: %v", ae.Name, err) + } + bf, err := br.OpenFile(be.Name) + if err != nil { + t.Fatalf("failed to open file %q on B: %v", be.Name, err) + } + + var nr int64 + for nr < ae.Size { + abytes, anext, aok := readOffset(t, af, nr, a) + bbytes, bnext, bok := readOffset(t, bf, nr, b) + if !aok && !bok { + break + } else if !(aok && bok) || anext != bnext { + t.Logf("%q != %q (offset=%d): chunk existence a=%v vs b=%v, anext=%v vs bnext=%v", + ae.Name, be.Name, nr, aok, bok, anext, bnext) + return false + } + nr = anext + if !bytes.Equal(abytes, bbytes) { + t.Logf("%q != %q: different contents %v vs %v", + ae.Name, be.Name, string(abytes), string(bbytes)) + return false + } + } + return true + } + + return true +} + +func allChildrenName(e *TOCEntry) (children []string) { + e.ForeachChild(func(baseName string, _ *TOCEntry) bool { + children = append(children, baseName) + return true + }) + return +} + +func equalEntry(a, b *TOCEntry) bool { + // Here, we selectively compare fileds that we are interested in. + return a.Name == b.Name && + a.Type == b.Type && + a.Size == b.Size && + a.ModTime3339 == b.ModTime3339 && + a.Stat().ModTime().Equal(b.Stat().ModTime()) && // modTime time.Time + a.LinkName == b.LinkName && + a.Mode == b.Mode && + a.UID == b.UID && + a.GID == b.GID && + a.Uname == b.Uname && + a.Gname == b.Gname && + (a.Offset > 0) == (b.Offset > 0) && + (a.NextOffset() > 0) == (b.NextOffset() > 0) && + a.DevMajor == b.DevMajor && + a.DevMinor == b.DevMinor && + a.NumLink == b.NumLink && + reflect.DeepEqual(a.Xattrs, b.Xattrs) && + // chunk-related infomations aren't compared in this function. + // ChunkOffset int64 `json:"chunkOffset,omitempty"` + // ChunkSize int64 `json:"chunkSize,omitempty"` + // children map[string]*TOCEntry + a.Digest == b.Digest +} + +func readOffset(t *testing.T, r *io.SectionReader, offset int64, e stargzEntry) ([]byte, int64, bool) { + ce, ok := e.r.ChunkEntryForOffset(e.e.Name, offset) + if !ok { + return nil, 0, false + } + data := make([]byte, ce.ChunkSize) + t.Logf("Offset: %v, NextOffset: %v", ce.Offset, ce.NextOffset()) + n, err := r.ReadAt(data, ce.ChunkOffset) + if err != nil { + t.Fatalf("failed to read file payload of %q (offset:%d,size:%d): %v", + e.e.Name, ce.ChunkOffset, ce.ChunkSize, err) + } + if int64(n) != ce.ChunkSize { + t.Fatalf("unexpected copied data size %d; want %d", + n, ce.ChunkSize) + } + return data[:n], offset + ce.ChunkSize, true +} + +func dumpTOCJSON(t *testing.T, tocJSON *JTOC) string { + jtocData, err := json.Marshal(*tocJSON) + if err != nil { + t.Fatalf("failed to marshal TOC JSON: %v", err) + } + buf := new(bytes.Buffer) + if _, err := io.Copy(buf, bytes.NewReader(jtocData)); err != nil { + t.Fatalf("failed to read toc json blob: %v", err) + } + return buf.String() +} + +const chunkSize = 3 + +// type check func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, compressionLevel int) +type check func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController) + +// testDigestAndVerify runs specified checks against sample stargz blobs. +func testDigestAndVerify(t *testing.T, controllers ...TestingController) { + tests := []struct { + name string + tarInit func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) + checks []check + }{ + { + name: "no-regfile", + tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) { + return tarOf( + dir("test/"), + ) + }, + checks: []check{ + checkStargzTOC, + checkVerifyTOC, + checkVerifyInvalidStargzFail(buildTar(t, tarOf( + dir("test2/"), // modified + ), allowedPrefix[0])), + }, + }, + { + name: "small-files", + tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) { + return tarOf( + regDigest(t, "baz.txt", "", dgstMap), + regDigest(t, "foo.txt", "a", dgstMap), + dir("test/"), + regDigest(t, "test/bar.txt", "bbb", dgstMap), + ) + }, + checks: []check{ + checkStargzTOC, + checkVerifyTOC, + checkVerifyInvalidStargzFail(buildTar(t, tarOf( + file("baz.txt", ""), + file("foo.txt", "M"), // modified + dir("test/"), + file("test/bar.txt", "bbb"), + ), allowedPrefix[0])), + // checkVerifyInvalidTOCEntryFail("foo.txt"), // TODO + checkVerifyBrokenContentFail("foo.txt"), + }, + }, + { + name: "big-files", + tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) { + return tarOf( + regDigest(t, "baz.txt", "bazbazbazbazbazbazbaz", dgstMap), + regDigest(t, "foo.txt", "a", dgstMap), + dir("test/"), + regDigest(t, "test/bar.txt", "testbartestbar", dgstMap), + ) + }, + checks: []check{ + checkStargzTOC, + checkVerifyTOC, + checkVerifyInvalidStargzFail(buildTar(t, tarOf( + file("baz.txt", "bazbazbazMMMbazbazbaz"), // modified + file("foo.txt", "a"), + dir("test/"), + file("test/bar.txt", "testbartestbar"), + ), allowedPrefix[0])), + checkVerifyInvalidTOCEntryFail("test/bar.txt"), + checkVerifyBrokenContentFail("test/bar.txt"), + }, + }, + { + name: "with-non-regfiles", + tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) { + return tarOf( + regDigest(t, "baz.txt", "bazbazbazbazbazbazbaz", dgstMap), + regDigest(t, "foo.txt", "a", dgstMap), + symlink("barlink", "test/bar.txt"), + dir("test/"), + regDigest(t, "test/bar.txt", "testbartestbar", dgstMap), + dir("test2/"), + link("test2/bazlink", "baz.txt"), + ) + }, + checks: []check{ + checkStargzTOC, + checkVerifyTOC, + checkVerifyInvalidStargzFail(buildTar(t, tarOf( + file("baz.txt", "bazbazbazbazbazbazbaz"), + file("foo.txt", "a"), + symlink("barlink", "test/bar.txt"), + dir("test/"), + file("test/bar.txt", "testbartestbar"), + dir("test2/"), + link("test2/bazlink", "foo.txt"), // modified + ), allowedPrefix[0])), + checkVerifyInvalidTOCEntryFail("test/bar.txt"), + checkVerifyBrokenContentFail("test/bar.txt"), + }, + }, + } + + for _, tt := range tests { + for _, srcCompression := range srcCompressions { + srcCompression := srcCompression + for _, cl := range controllers { + cl := cl + for _, prefix := range allowedPrefix { + prefix := prefix + for _, srcTarFormat := range []tar.Format{tar.FormatUSTAR, tar.FormatPAX, tar.FormatGNU} { + srcTarFormat := srcTarFormat + t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,format=%s", cl, prefix, srcTarFormat), func(t *testing.T) { + // Get original tar file and chunk digests + dgstMap := make(map[string]digest.Digest) + tarBlob := buildTar(t, tt.tarInit(t, dgstMap), prefix, srcTarFormat) + + rc, err := Build(compressBlob(t, tarBlob, srcCompression), + WithChunkSize(chunkSize), WithCompression(cl)) + if err != nil { + t.Fatalf("failed to convert stargz: %v", err) + } + tocDigest := rc.TOCDigest() + defer rc.Close() + buf := new(bytes.Buffer) + if _, err := io.Copy(buf, rc); err != nil { + t.Fatalf("failed to copy built stargz blob: %v", err) + } + newStargz := buf.Bytes() + // NoPrefetchLandmark is added during `Bulid`, which is expected behaviour. + dgstMap[chunkID(NoPrefetchLandmark, 0, int64(len([]byte{landmarkContents})))] = digest.FromBytes([]byte{landmarkContents}) + + for _, check := range tt.checks { + check(t, newStargz, tocDigest, dgstMap, cl) + } + }) + } + } + } + } + } +} + +// checkStargzTOC checks the TOC JSON of the passed stargz has the expected +// digest and contains valid chunks. It walks all entries in the stargz and +// checks all chunk digests stored to the TOC JSON match the actual contents. +func checkStargzTOC(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController) { + sgz, err := Open( + io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))), + WithDecompressors(controller), + ) + if err != nil { + t.Errorf("failed to parse converted stargz: %v", err) + return + } + digestMapTOC, err := listDigests(io.NewSectionReader( + bytes.NewReader(sgzData), 0, int64(len(sgzData))), + controller, + ) + if err != nil { + t.Fatalf("failed to list digest: %v", err) + } + found := make(map[string]bool) + for id := range dgstMap { + found[id] = false + } + zr, err := controller.Reader(bytes.NewReader(sgzData)) + if err != nil { + t.Fatalf("failed to decompress converted stargz: %v", err) + } + defer zr.Close() + tr := tar.NewReader(zr) + for { + h, err := tr.Next() + if err != nil { + if err != io.EOF { + t.Errorf("failed to read tar entry: %v", err) + return + } + break + } + if h.Name == TOCTarName { + // Check the digest of TOC JSON based on the actual contents + // It's sure that TOC JSON exists in this archive because + // Open succeeded. + dgstr := digest.Canonical.Digester() + if _, err := io.Copy(dgstr.Hash(), tr); err != nil { + t.Fatalf("failed to calculate digest of TOC JSON: %v", + err) + } + if dgstr.Digest() != tocDigest { + t.Errorf("invalid TOC JSON %q; want %q", tocDigest, dgstr.Digest()) + } + continue + } + if _, ok := sgz.Lookup(h.Name); !ok { + t.Errorf("lost stargz entry %q in the converted TOC", h.Name) + return + } + var n int64 + for n < h.Size { + ce, ok := sgz.ChunkEntryForOffset(h.Name, n) + if !ok { + t.Errorf("lost chunk %q(offset=%d) in the converted TOC", + h.Name, n) + return + } + + // Get the original digest to make sure the file contents are kept unchanged + // from the original tar, during the whole conversion steps. + id := chunkID(h.Name, n, ce.ChunkSize) + want, ok := dgstMap[id] + if !ok { + t.Errorf("Unexpected chunk %q(offset=%d,size=%d): %v", + h.Name, n, ce.ChunkSize, dgstMap) + return + } + found[id] = true + + // Check the file contents + dgstr := digest.Canonical.Digester() + if _, err := io.CopyN(dgstr.Hash(), tr, ce.ChunkSize); err != nil { + t.Fatalf("failed to calculate digest of %q (offset=%d,size=%d)", + h.Name, n, ce.ChunkSize) + } + if want != dgstr.Digest() { + t.Errorf("Invalid contents in converted stargz %q: %q; want %q", + h.Name, dgstr.Digest(), want) + return + } + + // Check the digest stored in TOC JSON + dgstTOC, ok := digestMapTOC[ce.Offset] + if !ok { + t.Errorf("digest of %q(offset=%d,size=%d,chunkOffset=%d) isn't registered", + h.Name, ce.Offset, ce.ChunkSize, ce.ChunkOffset) + } + if want != dgstTOC { + t.Errorf("Invalid digest in TOCEntry %q: %q; want %q", + h.Name, dgstTOC, want) + return + } + + n += ce.ChunkSize + } + } + + for id, ok := range found { + if !ok { + t.Errorf("required chunk %q not found in the converted stargz: %v", id, found) + } + } +} + +// checkVerifyTOC checks the verification works for the TOC JSON of the passed +// stargz. It walks all entries in the stargz and checks the verifications for +// all chunks work. +func checkVerifyTOC(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController) { + sgz, err := Open( + io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))), + WithDecompressors(controller), + ) + if err != nil { + t.Errorf("failed to parse converted stargz: %v", err) + return + } + ev, err := sgz.VerifyTOC(tocDigest) + if err != nil { + t.Errorf("failed to verify stargz: %v", err) + return + } + + found := make(map[string]bool) + for id := range dgstMap { + found[id] = false + } + zr, err := controller.Reader(bytes.NewReader(sgzData)) + if err != nil { + t.Fatalf("failed to decompress converted stargz: %v", err) + } + defer zr.Close() + tr := tar.NewReader(zr) + for { + h, err := tr.Next() + if err != nil { + if err != io.EOF { + t.Errorf("failed to read tar entry: %v", err) + return + } + break + } + if h.Name == TOCTarName { + continue + } + if _, ok := sgz.Lookup(h.Name); !ok { + t.Errorf("lost stargz entry %q in the converted TOC", h.Name) + return + } + var n int64 + for n < h.Size { + ce, ok := sgz.ChunkEntryForOffset(h.Name, n) + if !ok { + t.Errorf("lost chunk %q(offset=%d) in the converted TOC", + h.Name, n) + return + } + + v, err := ev.Verifier(ce) + if err != nil { + t.Errorf("failed to get verifier for %q(offset=%d)", h.Name, n) + } + + found[chunkID(h.Name, n, ce.ChunkSize)] = true + + // Check the file contents + if _, err := io.CopyN(v, tr, ce.ChunkSize); err != nil { + t.Fatalf("failed to get chunk of %q (offset=%d,size=%d)", + h.Name, n, ce.ChunkSize) + } + if !v.Verified() { + t.Errorf("Invalid contents in converted stargz %q (should be succeeded)", + h.Name) + return + } + n += ce.ChunkSize + } + } + + for id, ok := range found { + if !ok { + t.Errorf("required chunk %q not found in the converted stargz: %v", id, found) + } + } +} + +// checkVerifyInvalidTOCEntryFail checks if misconfigured TOC JSON can be +// detected during the verification and the verification returns an error. +func checkVerifyInvalidTOCEntryFail(filename string) check { + return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController) { + funcs := map[string]rewriteFunc{ + "lost digest in a entry": func(t *testing.T, toc *JTOC, sgz *io.SectionReader) { + var found bool + for _, e := range toc.Entries { + if cleanEntryName(e.Name) == filename { + if e.Type != "reg" && e.Type != "chunk" { + t.Fatalf("entry %q to break must be regfile or chunk", filename) + } + if e.ChunkDigest == "" { + t.Fatalf("entry %q is already invalid", filename) + } + e.ChunkDigest = "" + found = true + } + } + if !found { + t.Fatalf("rewrite target not found") + } + }, + "duplicated entry offset": func(t *testing.T, toc *JTOC, sgz *io.SectionReader) { + var ( + sampleEntry *TOCEntry + targetEntry *TOCEntry + ) + for _, e := range toc.Entries { + if e.Type == "reg" || e.Type == "chunk" { + if cleanEntryName(e.Name) == filename { + targetEntry = e + } else { + sampleEntry = e + } + } + } + if sampleEntry == nil { + t.Fatalf("TOC must contain at least one regfile or chunk entry other than the rewrite target") + } + if targetEntry == nil { + t.Fatalf("rewrite target not found") + } + targetEntry.Offset = sampleEntry.Offset + }, + } + + for name, rFunc := range funcs { + t.Run(name, func(t *testing.T) { + newSgz, newTocDigest := rewriteTOCJSON(t, io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))), rFunc, controller) + buf := new(bytes.Buffer) + if _, err := io.Copy(buf, newSgz); err != nil { + t.Fatalf("failed to get converted stargz") + } + isgz := buf.Bytes() + + sgz, err := Open( + io.NewSectionReader(bytes.NewReader(isgz), 0, int64(len(isgz))), + WithDecompressors(controller), + ) + if err != nil { + t.Fatalf("failed to parse converted stargz: %v", err) + return + } + _, err = sgz.VerifyTOC(newTocDigest) + if err == nil { + t.Errorf("must fail for invalid TOC") + return + } + }) + } + } +} + +// checkVerifyInvalidStargzFail checks if the verification detects that the +// given stargz file doesn't match to the expected digest and returns error. +func checkVerifyInvalidStargzFail(invalid *io.SectionReader) check { + return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController) { + rc, err := Build(invalid, WithChunkSize(chunkSize), WithCompression(controller)) + if err != nil { + t.Fatalf("failed to convert stargz: %v", err) + } + defer rc.Close() + buf := new(bytes.Buffer) + if _, err := io.Copy(buf, rc); err != nil { + t.Fatalf("failed to copy built stargz blob: %v", err) + } + mStargz := buf.Bytes() + + sgz, err := Open( + io.NewSectionReader(bytes.NewReader(mStargz), 0, int64(len(mStargz))), + WithDecompressors(controller), + ) + if err != nil { + t.Fatalf("failed to parse converted stargz: %v", err) + return + } + _, err = sgz.VerifyTOC(tocDigest) + if err == nil { + t.Errorf("must fail for invalid TOC") + return + } + } +} + +// checkVerifyBrokenContentFail checks if the verifier detects broken contents +// that doesn't match to the expected digest and returns error. +func checkVerifyBrokenContentFail(filename string) check { + return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController) { + // Parse stargz file + sgz, err := Open( + io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))), + WithDecompressors(controller), + ) + if err != nil { + t.Fatalf("failed to parse converted stargz: %v", err) + return + } + ev, err := sgz.VerifyTOC(tocDigest) + if err != nil { + t.Fatalf("failed to verify stargz: %v", err) + return + } + + // Open the target file + sr, err := sgz.OpenFile(filename) + if err != nil { + t.Fatalf("failed to open file %q", filename) + } + ce, ok := sgz.ChunkEntryForOffset(filename, 0) + if !ok { + t.Fatalf("lost chunk %q(offset=%d) in the converted TOC", filename, 0) + return + } + if ce.ChunkSize == 0 { + t.Fatalf("file mustn't be empty") + return + } + data := make([]byte, ce.ChunkSize) + if _, err := sr.ReadAt(data, ce.ChunkOffset); err != nil { + t.Errorf("failed to get data of a chunk of %q(offset=%q)", + filename, ce.ChunkOffset) + } + + // Check the broken chunk (must fail) + v, err := ev.Verifier(ce) + if err != nil { + t.Fatalf("failed to get verifier for %q", filename) + } + broken := append([]byte{^data[0]}, data[1:]...) + if _, err := io.CopyN(v, bytes.NewReader(broken), ce.ChunkSize); err != nil { + t.Fatalf("failed to get chunk of %q (offset=%d,size=%d)", + filename, ce.ChunkOffset, ce.ChunkSize) + } + if v.Verified() { + t.Errorf("verification must fail for broken file chunk %q(org:%q,broken:%q)", + filename, data, broken) + } + } +} + +func chunkID(name string, offset, size int64) string { + return fmt.Sprintf("%s-%d-%d", cleanEntryName(name), offset, size) +} + +type rewriteFunc func(t *testing.T, toc *JTOC, sgz *io.SectionReader) + +func rewriteTOCJSON(t *testing.T, sgz *io.SectionReader, rewrite rewriteFunc, controller TestingController) (newSgz io.Reader, tocDigest digest.Digest) { + decodedJTOC, jtocOffset, err := parseStargz(sgz, controller) + if err != nil { + t.Fatalf("failed to extract TOC JSON: %v", err) + } + + rewrite(t, decodedJTOC, sgz) + + tocFooter, tocDigest, err := tocAndFooter(controller, decodedJTOC, jtocOffset) + if err != nil { + t.Fatalf("failed to create toc and footer: %v", err) + } + + // Reconstruct stargz file with the modified TOC JSON + if _, err := sgz.Seek(0, io.SeekStart); err != nil { + t.Fatalf("failed to reset the seek position of stargz: %v", err) + } + return io.MultiReader( + io.LimitReader(sgz, jtocOffset), // Original stargz (before TOC JSON) + tocFooter, // Rewritten TOC and footer + ), tocDigest +} + +func listDigests(sgz *io.SectionReader, controller TestingController) (map[int64]digest.Digest, error) { + decodedJTOC, _, err := parseStargz(sgz, controller) + if err != nil { + return nil, err + } + digestMap := make(map[int64]digest.Digest) + for _, e := range decodedJTOC.Entries { + if e.Type == "reg" || e.Type == "chunk" { + if e.Type == "reg" && e.Size == 0 { + continue // ignores empty file + } + if e.ChunkDigest == "" { + return nil, fmt.Errorf("ChunkDigest of %q(off=%d) not found in TOC JSON", + e.Name, e.Offset) + } + d, err := digest.Parse(e.ChunkDigest) + if err != nil { + return nil, err + } + digestMap[e.Offset] = d + } + } + return digestMap, nil +} + +func parseStargz(sgz *io.SectionReader, controller TestingController) (decodedJTOC *JTOC, jtocOffset int64, err error) { + fSize := controller.FooterSize() + footer := make([]byte, fSize) + if _, err := sgz.ReadAt(footer, sgz.Size()-fSize); err != nil { + return nil, 0, fmt.Errorf("error reading footer: %w", err) + } + _, tocOffset, _, err := controller.ParseFooter(footer[positive(int64(len(footer))-fSize):]) + if err != nil { + return nil, 0, fmt.Errorf("failed to parse footer: %w", err) + } + + // Decode the TOC JSON + tocReader := io.NewSectionReader(sgz, tocOffset, sgz.Size()-tocOffset-fSize) + decodedJTOC, _, err = controller.ParseTOC(tocReader) + if err != nil { + return nil, 0, fmt.Errorf("failed to parse TOC: %w", err) + } + return decodedJTOC, tocOffset, nil +} + +func testWriteAndOpen(t *testing.T, controllers ...TestingController) { + const content = "Some contents" + invalidUtf8 := "\xff\xfe\xfd" + + xAttrFile := xAttr{"foo": "bar", "invalid-utf8": invalidUtf8} + sampleOwner := owner{uid: 50, gid: 100} + + tests := []struct { + name string + chunkSize int + in []tarEntry + want []stargzCheck + wantNumGz int // expected number of streams + + wantNumGzLossLess int // expected number of streams (> 0) in lossless mode if it's different from wantNumGz + wantFailOnLossLess bool + }{ + { + name: "empty", + in: tarOf(), + wantNumGz: 2, // empty tar + TOC + footer + wantNumGzLossLess: 3, // empty tar + TOC + footer + want: checks( + numTOCEntries(0), + ), + }, + { + name: "1dir_1empty_file", + in: tarOf( + dir("foo/"), + file("foo/bar.txt", ""), + ), + wantNumGz: 3, // dir, TOC, footer + want: checks( + numTOCEntries(2), + hasDir("foo/"), + hasFileLen("foo/bar.txt", 0), + entryHasChildren("foo", "bar.txt"), + hasFileDigest("foo/bar.txt", digestFor("")), + ), + }, + { + name: "1dir_1file", + in: tarOf( + dir("foo/"), + file("foo/bar.txt", content, xAttrFile), + ), + wantNumGz: 4, // var dir, foo.txt alone, TOC, footer + want: checks( + numTOCEntries(2), + hasDir("foo/"), + hasFileLen("foo/bar.txt", len(content)), + hasFileDigest("foo/bar.txt", digestFor(content)), + hasFileContentsRange("foo/bar.txt", 0, content), + hasFileContentsRange("foo/bar.txt", 1, content[1:]), + entryHasChildren("", "foo"), + entryHasChildren("foo", "bar.txt"), + hasFileXattrs("foo/bar.txt", "foo", "bar"), + hasFileXattrs("foo/bar.txt", "invalid-utf8", invalidUtf8), + ), + }, + { + name: "2meta_2file", + in: tarOf( + dir("bar/", sampleOwner), + dir("foo/", sampleOwner), + file("foo/bar.txt", content, sampleOwner), + ), + wantNumGz: 4, // both dirs, foo.txt alone, TOC, footer + want: checks( + numTOCEntries(3), + hasDir("bar/"), + hasDir("foo/"), + hasFileLen("foo/bar.txt", len(content)), + entryHasChildren("", "bar", "foo"), + entryHasChildren("foo", "bar.txt"), + hasChunkEntries("foo/bar.txt", 1), + hasEntryOwner("bar/", sampleOwner), + hasEntryOwner("foo/", sampleOwner), + hasEntryOwner("foo/bar.txt", sampleOwner), + ), + }, + { + name: "3dir", + in: tarOf( + dir("bar/"), + dir("foo/"), + dir("foo/bar/"), + ), + wantNumGz: 3, // 3 dirs, TOC, footer + want: checks( + hasDirLinkCount("bar/", 2), + hasDirLinkCount("foo/", 3), + hasDirLinkCount("foo/bar/", 2), + ), + }, + { + name: "symlink", + in: tarOf( + dir("foo/"), + symlink("foo/bar", "../../x"), + ), + wantNumGz: 3, // metas + TOC + footer + want: checks( + numTOCEntries(2), + hasSymlink("foo/bar", "../../x"), + entryHasChildren("", "foo"), + entryHasChildren("foo", "bar"), + ), + }, + { + name: "chunked_file", + chunkSize: 4, + in: tarOf( + dir("foo/"), + file("foo/big.txt", "This "+"is s"+"uch "+"a bi"+"g fi"+"le"), + ), + wantNumGz: 9, + want: checks( + numTOCEntries(7), // 1 for foo dir, 6 for the foo/big.txt file + hasDir("foo/"), + hasFileLen("foo/big.txt", len("This is such a big file")), + hasFileDigest("foo/big.txt", digestFor("This is such a big file")), + hasFileContentsRange("foo/big.txt", 0, "This is such a big file"), + hasFileContentsRange("foo/big.txt", 1, "his is such a big file"), + hasFileContentsRange("foo/big.txt", 2, "is is such a big file"), + hasFileContentsRange("foo/big.txt", 3, "s is such a big file"), + hasFileContentsRange("foo/big.txt", 4, " is such a big file"), + hasFileContentsRange("foo/big.txt", 5, "is such a big file"), + hasFileContentsRange("foo/big.txt", 6, "s such a big file"), + hasFileContentsRange("foo/big.txt", 7, " such a big file"), + hasFileContentsRange("foo/big.txt", 8, "such a big file"), + hasFileContentsRange("foo/big.txt", 9, "uch a big file"), + hasFileContentsRange("foo/big.txt", 10, "ch a big file"), + hasFileContentsRange("foo/big.txt", 11, "h a big file"), + hasFileContentsRange("foo/big.txt", 12, " a big file"), + hasFileContentsRange("foo/big.txt", len("This is such a big file")-1, ""), + hasChunkEntries("foo/big.txt", 6), + ), + }, + { + name: "recursive", + in: tarOf( + dir("/", sampleOwner), + dir("bar/", sampleOwner), + dir("foo/", sampleOwner), + file("foo/bar.txt", content, sampleOwner), + ), + wantNumGz: 4, // dirs, bar.txt alone, TOC, footer + want: checks( + maxDepth(2), // 0: root directory, 1: "foo/", 2: "bar.txt" + ), + }, + { + name: "block_char_fifo", + in: tarOf( + tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { + return w.WriteHeader(&tar.Header{ + Name: prefix + "b", + Typeflag: tar.TypeBlock, + Devmajor: 123, + Devminor: 456, + Format: format, + }) + }), + tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { + return w.WriteHeader(&tar.Header{ + Name: prefix + "c", + Typeflag: tar.TypeChar, + Devmajor: 111, + Devminor: 222, + Format: format, + }) + }), + tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { + return w.WriteHeader(&tar.Header{ + Name: prefix + "f", + Typeflag: tar.TypeFifo, + Format: format, + }) + }), + ), + wantNumGz: 3, + want: checks( + lookupMatch("b", &TOCEntry{Name: "b", Type: "block", DevMajor: 123, DevMinor: 456, NumLink: 1}), + lookupMatch("c", &TOCEntry{Name: "c", Type: "char", DevMajor: 111, DevMinor: 222, NumLink: 1}), + lookupMatch("f", &TOCEntry{Name: "f", Type: "fifo", NumLink: 1}), + ), + }, + { + name: "modes", + in: tarOf( + dir("foo1/", 0755|os.ModeDir|os.ModeSetgid), + file("foo1/bar1", content, 0700|os.ModeSetuid), + file("foo1/bar2", content, 0755|os.ModeSetgid), + dir("foo2/", 0755|os.ModeDir|os.ModeSticky), + file("foo2/bar3", content, 0755|os.ModeSticky), + dir("foo3/", 0755|os.ModeDir), + file("foo3/bar4", content, os.FileMode(0700)), + file("foo3/bar5", content, os.FileMode(0755)), + ), + wantNumGz: 8, // dir, bar1 alone, bar2 alone + dir, bar3 alone + dir, bar4 alone, bar5 alone, TOC, footer + want: checks( + hasMode("foo1/", 0755|os.ModeDir|os.ModeSetgid), + hasMode("foo1/bar1", 0700|os.ModeSetuid), + hasMode("foo1/bar2", 0755|os.ModeSetgid), + hasMode("foo2/", 0755|os.ModeDir|os.ModeSticky), + hasMode("foo2/bar3", 0755|os.ModeSticky), + hasMode("foo3/", 0755|os.ModeDir), + hasMode("foo3/bar4", os.FileMode(0700)), + hasMode("foo3/bar5", os.FileMode(0755)), + ), + }, + { + name: "lossy", + in: tarOf( + dir("bar/", sampleOwner), + dir("foo/", sampleOwner), + file("foo/bar.txt", content, sampleOwner), + file(TOCTarName, "dummy"), // ignored by the writer. (lossless write returns error) + ), + wantNumGz: 4, // both dirs, foo.txt alone, TOC, footer + want: checks( + numTOCEntries(3), + hasDir("bar/"), + hasDir("foo/"), + hasFileLen("foo/bar.txt", len(content)), + entryHasChildren("", "bar", "foo"), + entryHasChildren("foo", "bar.txt"), + hasChunkEntries("foo/bar.txt", 1), + hasEntryOwner("bar/", sampleOwner), + hasEntryOwner("foo/", sampleOwner), + hasEntryOwner("foo/bar.txt", sampleOwner), + ), + wantFailOnLossLess: true, + }, + } + + for _, tt := range tests { + for _, cl := range controllers { + cl := cl + for _, prefix := range allowedPrefix { + prefix := prefix + for _, srcTarFormat := range []tar.Format{tar.FormatUSTAR, tar.FormatPAX, tar.FormatGNU} { + srcTarFormat := srcTarFormat + for _, lossless := range []bool{true, false} { + t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,lossless=%v,format=%s", cl, prefix, lossless, srcTarFormat), func(t *testing.T) { + var tr io.Reader = buildTar(t, tt.in, prefix, srcTarFormat) + origTarDgstr := digest.Canonical.Digester() + tr = io.TeeReader(tr, origTarDgstr.Hash()) + var stargzBuf bytes.Buffer + w := NewWriterWithCompressor(&stargzBuf, cl) + w.ChunkSize = tt.chunkSize + if lossless { + err := w.AppendTarLossLess(tr) + if tt.wantFailOnLossLess { + if err != nil { + return // expected to fail + } + t.Fatalf("Append wanted to fail on lossless") + } + if err != nil { + t.Fatalf("Append(lossless): %v", err) + } + } else { + if err := w.AppendTar(tr); err != nil { + t.Fatalf("Append: %v", err) + } + } + if _, err := w.Close(); err != nil { + t.Fatalf("Writer.Close: %v", err) + } + b := stargzBuf.Bytes() + + if lossless { + // Check if the result blob reserves original tar metadata + rc, err := Unpack(io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))), cl) + if err != nil { + t.Errorf("failed to decompress blob: %v", err) + return + } + defer rc.Close() + resultDgstr := digest.Canonical.Digester() + if _, err := io.Copy(resultDgstr.Hash(), rc); err != nil { + t.Errorf("failed to read result decompressed blob: %v", err) + return + } + if resultDgstr.Digest() != origTarDgstr.Digest() { + t.Errorf("lossy compression occurred: digest=%v; want %v", + resultDgstr.Digest(), origTarDgstr.Digest()) + return + } + } + + diffID := w.DiffID() + wantDiffID := cl.DiffIDOf(t, b) + if diffID != wantDiffID { + t.Errorf("DiffID = %q; want %q", diffID, wantDiffID) + } + + got := cl.CountStreams(t, b) + wantNumGz := tt.wantNumGz + if lossless && tt.wantNumGzLossLess > 0 { + wantNumGz = tt.wantNumGzLossLess + } + if got != wantNumGz { + t.Errorf("number of streams = %d; want %d", got, wantNumGz) + } + + telemetry, checkCalled := newCalledTelemetry() + r, err := Open( + io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))), + WithDecompressors(cl), + WithTelemetry(telemetry), + ) + if err != nil { + t.Fatalf("stargz.Open: %v", err) + } + if err := checkCalled(); err != nil { + t.Errorf("telemetry failure: %v", err) + } + for _, want := range tt.want { + want.check(t, r) + } + }) + } + } + } + } + } +} + +func newCalledTelemetry() (telemetry *Telemetry, check func() error) { + var getFooterLatencyCalled bool + var getTocLatencyCalled bool + var deserializeTocLatencyCalled bool + return &Telemetry{ + func(time.Time) { getFooterLatencyCalled = true }, + func(time.Time) { getTocLatencyCalled = true }, + func(time.Time) { deserializeTocLatencyCalled = true }, + }, func() error { + var allErr []error + if !getFooterLatencyCalled { + allErr = append(allErr, fmt.Errorf("metrics GetFooterLatency isn't called")) + } + if !getTocLatencyCalled { + allErr = append(allErr, fmt.Errorf("metrics GetTocLatency isn't called")) + } + if !deserializeTocLatencyCalled { + allErr = append(allErr, fmt.Errorf("metrics DeserializeTocLatency isn't called")) + } + return errorutil.Aggregate(allErr) + } +} + +func digestFor(content string) string { + sum := sha256.Sum256([]byte(content)) + return fmt.Sprintf("sha256:%x", sum) +} + +type numTOCEntries int + +func (n numTOCEntries) check(t *testing.T, r *Reader) { + if r.toc == nil { + t.Fatal("nil TOC") + } + if got, want := len(r.toc.Entries), int(n); got != want { + t.Errorf("got %d TOC entries; want %d", got, want) + } + t.Logf("got TOC entries:") + for i, ent := range r.toc.Entries { + entj, _ := json.Marshal(ent) + t.Logf(" [%d]: %s\n", i, entj) + } + if t.Failed() { + t.FailNow() + } +} + +func checks(s ...stargzCheck) []stargzCheck { return s } + +type stargzCheck interface { + check(t *testing.T, r *Reader) +} + +type stargzCheckFn func(*testing.T, *Reader) + +func (f stargzCheckFn) check(t *testing.T, r *Reader) { f(t, r) } + +func maxDepth(max int) stargzCheck { + return stargzCheckFn(func(t *testing.T, r *Reader) { + e, ok := r.Lookup("") + if !ok { + t.Fatal("root directory not found") + } + d, err := getMaxDepth(t, e, 0, 10*max) + if err != nil { + t.Errorf("failed to get max depth (wanted %d): %v", max, err) + return + } + if d != max { + t.Errorf("invalid depth %d; want %d", d, max) + return + } + }) +} + +func getMaxDepth(t *testing.T, e *TOCEntry, current, limit int) (max int, rErr error) { + if current > limit { + return -1, fmt.Errorf("walkMaxDepth: exceeds limit: current:%d > limit:%d", + current, limit) + } + max = current + e.ForeachChild(func(baseName string, ent *TOCEntry) bool { + t.Logf("%q(basename:%q) is child of %q\n", ent.Name, baseName, e.Name) + d, err := getMaxDepth(t, ent, current+1, limit) + if err != nil { + rErr = err + return false + } + if d > max { + max = d + } + return true + }) + return +} + +func hasFileLen(file string, wantLen int) stargzCheck { + return stargzCheckFn(func(t *testing.T, r *Reader) { + for _, ent := range r.toc.Entries { + if ent.Name == file { + if ent.Type != "reg" { + t.Errorf("file type of %q is %q; want \"reg\"", file, ent.Type) + } else if ent.Size != int64(wantLen) { + t.Errorf("file size of %q = %d; want %d", file, ent.Size, wantLen) + } + return + } + } + t.Errorf("file %q not found", file) + }) +} + +func hasFileXattrs(file, name, value string) stargzCheck { + return stargzCheckFn(func(t *testing.T, r *Reader) { + for _, ent := range r.toc.Entries { + if ent.Name == file { + if ent.Type != "reg" { + t.Errorf("file type of %q is %q; want \"reg\"", file, ent.Type) + } + if ent.Xattrs == nil { + t.Errorf("file %q has no xattrs", file) + return + } + valueFound, found := ent.Xattrs[name] + if !found { + t.Errorf("file %q has no xattr %q", file, name) + return + } + if string(valueFound) != value { + t.Errorf("file %q has xattr %q with value %q instead of %q", file, name, valueFound, value) + } + + return + } + } + t.Errorf("file %q not found", file) + }) +} + +func hasFileDigest(file string, digest string) stargzCheck { + return stargzCheckFn(func(t *testing.T, r *Reader) { + ent, ok := r.Lookup(file) + if !ok { + t.Fatalf("didn't find TOCEntry for file %q", file) + } + if ent.Digest != digest { + t.Fatalf("Digest(%q) = %q, want %q", file, ent.Digest, digest) + } + }) +} + +func hasFileContentsRange(file string, offset int, want string) stargzCheck { + return stargzCheckFn(func(t *testing.T, r *Reader) { + f, err := r.OpenFile(file) + if err != nil { + t.Fatal(err) + } + got := make([]byte, len(want)) + n, err := f.ReadAt(got, int64(offset)) + if err != nil { + t.Fatalf("ReadAt(len %d, offset %d) = %v, %v", len(got), offset, n, err) + } + if string(got) != want { + t.Fatalf("ReadAt(len %d, offset %d) = %q, want %q", len(got), offset, got, want) + } + }) +} + +func hasChunkEntries(file string, wantChunks int) stargzCheck { + return stargzCheckFn(func(t *testing.T, r *Reader) { + ent, ok := r.Lookup(file) + if !ok { + t.Fatalf("no file for %q", file) + } + if ent.Type != "reg" { + t.Fatalf("file %q has unexpected type %q; want reg", file, ent.Type) + } + chunks := r.getChunks(ent) + if len(chunks) != wantChunks { + t.Errorf("len(r.getChunks(%q)) = %d; want %d", file, len(chunks), wantChunks) + return + } + f := chunks[0] + + var gotChunks []*TOCEntry + var last *TOCEntry + for off := int64(0); off < f.Size; off++ { + e, ok := r.ChunkEntryForOffset(file, off) + if !ok { + t.Errorf("no ChunkEntryForOffset at %d", off) + return + } + if last != e { + gotChunks = append(gotChunks, e) + last = e + } + } + if !reflect.DeepEqual(chunks, gotChunks) { + t.Errorf("gotChunks=%d, want=%d; contents mismatch", len(gotChunks), wantChunks) + } + + // And verify the NextOffset + for i := 0; i < len(gotChunks)-1; i++ { + ci := gotChunks[i] + cnext := gotChunks[i+1] + if ci.NextOffset() != cnext.Offset { + t.Errorf("chunk %d NextOffset %d != next chunk's Offset of %d", i, ci.NextOffset(), cnext.Offset) + } + } + }) +} + +func entryHasChildren(dir string, want ...string) stargzCheck { + return stargzCheckFn(func(t *testing.T, r *Reader) { + want := append([]string(nil), want...) + var got []string + ent, ok := r.Lookup(dir) + if !ok { + t.Fatalf("didn't find TOCEntry for dir node %q", dir) + } + for baseName := range ent.children { + got = append(got, baseName) + } + sort.Strings(got) + sort.Strings(want) + if !reflect.DeepEqual(got, want) { + t.Errorf("children of %q = %q; want %q", dir, got, want) + } + }) +} + +func hasDir(file string) stargzCheck { + return stargzCheckFn(func(t *testing.T, r *Reader) { + for _, ent := range r.toc.Entries { + if ent.Name == cleanEntryName(file) { + if ent.Type != "dir" { + t.Errorf("file type of %q is %q; want \"dir\"", file, ent.Type) + } + return + } + } + t.Errorf("directory %q not found", file) + }) +} + +func hasDirLinkCount(file string, count int) stargzCheck { + return stargzCheckFn(func(t *testing.T, r *Reader) { + for _, ent := range r.toc.Entries { + if ent.Name == cleanEntryName(file) { + if ent.Type != "dir" { + t.Errorf("file type of %q is %q; want \"dir\"", file, ent.Type) + return + } + if ent.NumLink != count { + t.Errorf("link count of %q = %d; want %d", file, ent.NumLink, count) + } + return + } + } + t.Errorf("directory %q not found", file) + }) +} + +func hasMode(file string, mode os.FileMode) stargzCheck { + return stargzCheckFn(func(t *testing.T, r *Reader) { + for _, ent := range r.toc.Entries { + if ent.Name == cleanEntryName(file) { + if ent.Stat().Mode() != mode { + t.Errorf("invalid mode: got %v; want %v", ent.Stat().Mode(), mode) + return + } + return + } + } + t.Errorf("file %q not found", file) + }) +} + +func hasSymlink(file, target string) stargzCheck { + return stargzCheckFn(func(t *testing.T, r *Reader) { + for _, ent := range r.toc.Entries { + if ent.Name == file { + if ent.Type != "symlink" { + t.Errorf("file type of %q is %q; want \"symlink\"", file, ent.Type) + } else if ent.LinkName != target { + t.Errorf("link target of symlink %q is %q; want %q", file, ent.LinkName, target) + } + return + } + } + t.Errorf("symlink %q not found", file) + }) +} + +func lookupMatch(name string, want *TOCEntry) stargzCheck { + return stargzCheckFn(func(t *testing.T, r *Reader) { + e, ok := r.Lookup(name) + if !ok { + t.Fatalf("failed to Lookup entry %q", name) + } + if !reflect.DeepEqual(e, want) { + t.Errorf("entry %q mismatch.\n got: %+v\nwant: %+v\n", name, e, want) + } + + }) +} + +func hasEntryOwner(entry string, owner owner) stargzCheck { + return stargzCheckFn(func(t *testing.T, r *Reader) { + ent, ok := r.Lookup(strings.TrimSuffix(entry, "/")) + if !ok { + t.Errorf("entry %q not found", entry) + return + } + if ent.UID != owner.uid || ent.GID != owner.gid { + t.Errorf("entry %q has invalid owner (uid:%d, gid:%d) instead of (uid:%d, gid:%d)", entry, ent.UID, ent.GID, owner.uid, owner.gid) + return + } + }) +} + +func tarOf(s ...tarEntry) []tarEntry { return s } + +type tarEntry interface { + appendTar(tw *tar.Writer, prefix string, format tar.Format) error +} + +type tarEntryFunc func(*tar.Writer, string, tar.Format) error + +func (f tarEntryFunc) appendTar(tw *tar.Writer, prefix string, format tar.Format) error { + return f(tw, prefix, format) +} + +func buildTar(t *testing.T, ents []tarEntry, prefix string, opts ...interface{}) *io.SectionReader { + format := tar.FormatUnknown + for _, opt := range opts { + switch v := opt.(type) { + case tar.Format: + format = v + default: + panic(fmt.Errorf("unsupported opt for buildTar: %v", opt)) + } + } + buf := new(bytes.Buffer) + tw := tar.NewWriter(buf) + for _, ent := range ents { + if err := ent.appendTar(tw, prefix, format); err != nil { + t.Fatalf("building input tar: %v", err) + } + } + if err := tw.Close(); err != nil { + t.Errorf("closing write of input tar: %v", err) + } + data := append(buf.Bytes(), make([]byte, 100)...) // append empty bytes at the tail to see lossless works + return io.NewSectionReader(bytes.NewReader(data), 0, int64(len(data))) +} + +func dir(name string, opts ...interface{}) tarEntry { + return tarEntryFunc(func(tw *tar.Writer, prefix string, format tar.Format) error { + var o owner + mode := os.FileMode(0755) + for _, opt := range opts { + switch v := opt.(type) { + case owner: + o = v + case os.FileMode: + mode = v + default: + return errors.New("unsupported opt") + } + } + if !strings.HasSuffix(name, "/") { + panic(fmt.Sprintf("missing trailing slash in dir %q ", name)) + } + tm, err := fileModeToTarMode(mode) + if err != nil { + return err + } + return tw.WriteHeader(&tar.Header{ + Typeflag: tar.TypeDir, + Name: prefix + name, + Mode: tm, + Uid: o.uid, + Gid: o.gid, + Format: format, + }) + }) +} + +// xAttr are extended attributes to set on test files created with the file func. +type xAttr map[string]string + +// owner is owner ot set on test files and directories with the file and dir functions. +type owner struct { + uid int + gid int +} + +func file(name, contents string, opts ...interface{}) tarEntry { + return tarEntryFunc(func(tw *tar.Writer, prefix string, format tar.Format) error { + var xattrs xAttr + var o owner + mode := os.FileMode(0644) + for _, opt := range opts { + switch v := opt.(type) { + case xAttr: + xattrs = v + case owner: + o = v + case os.FileMode: + mode = v + default: + return errors.New("unsupported opt") + } + } + if strings.HasSuffix(name, "/") { + return fmt.Errorf("bogus trailing slash in file %q", name) + } + tm, err := fileModeToTarMode(mode) + if err != nil { + return err + } + if len(xattrs) > 0 { + format = tar.FormatPAX // only PAX supports xattrs + } + if err := tw.WriteHeader(&tar.Header{ + Typeflag: tar.TypeReg, + Name: prefix + name, + Mode: tm, + Xattrs: xattrs, + Size: int64(len(contents)), + Uid: o.uid, + Gid: o.gid, + Format: format, + }); err != nil { + return err + } + _, err = io.WriteString(tw, contents) + return err + }) +} + +func symlink(name, target string) tarEntry { + return tarEntryFunc(func(tw *tar.Writer, prefix string, format tar.Format) error { + return tw.WriteHeader(&tar.Header{ + Typeflag: tar.TypeSymlink, + Name: prefix + name, + Linkname: target, + Mode: 0644, + Format: format, + }) + }) +} + +func link(name string, linkname string) tarEntry { + now := time.Now() + return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { + return w.WriteHeader(&tar.Header{ + Typeflag: tar.TypeLink, + Name: prefix + name, + Linkname: linkname, + ModTime: now, + Format: format, + }) + }) +} + +func chardev(name string, major, minor int64) tarEntry { + now := time.Now() + return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { + return w.WriteHeader(&tar.Header{ + Typeflag: tar.TypeChar, + Name: prefix + name, + Devmajor: major, + Devminor: minor, + ModTime: now, + Format: format, + }) + }) +} + +func blockdev(name string, major, minor int64) tarEntry { + now := time.Now() + return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { + return w.WriteHeader(&tar.Header{ + Typeflag: tar.TypeBlock, + Name: prefix + name, + Devmajor: major, + Devminor: minor, + ModTime: now, + Format: format, + }) + }) +} +func fifo(name string) tarEntry { + now := time.Now() + return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { + return w.WriteHeader(&tar.Header{ + Typeflag: tar.TypeFifo, + Name: prefix + name, + ModTime: now, + Format: format, + }) + }) +} + +func prefetchLandmark() tarEntry { + return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { + if err := w.WriteHeader(&tar.Header{ + Name: PrefetchLandmark, + Typeflag: tar.TypeReg, + Size: int64(len([]byte{landmarkContents})), + Format: format, + }); err != nil { + return err + } + contents := []byte{landmarkContents} + if _, err := io.CopyN(w, bytes.NewReader(contents), int64(len(contents))); err != nil { + return err + } + return nil + }) +} + +func noPrefetchLandmark() tarEntry { + return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { + if err := w.WriteHeader(&tar.Header{ + Name: NoPrefetchLandmark, + Typeflag: tar.TypeReg, + Size: int64(len([]byte{landmarkContents})), + Format: format, + }); err != nil { + return err + } + contents := []byte{landmarkContents} + if _, err := io.CopyN(w, bytes.NewReader(contents), int64(len(contents))); err != nil { + return err + } + return nil + }) +} + +func regDigest(t *testing.T, name string, contentStr string, digestMap map[string]digest.Digest) tarEntry { + if digestMap == nil { + t.Fatalf("digest map mustn't be nil") + } + content := []byte(contentStr) + + var n int64 + for n < int64(len(content)) { + size := int64(chunkSize) + remain := int64(len(content)) - n + if remain < size { + size = remain + } + dgstr := digest.Canonical.Digester() + if _, err := io.CopyN(dgstr.Hash(), bytes.NewReader(content[n:n+size]), size); err != nil { + t.Fatalf("failed to calculate digest of %q (name=%q,offset=%d,size=%d)", + string(content[n:n+size]), name, n, size) + } + digestMap[chunkID(name, n, size)] = dgstr.Digest() + n += size + } + + return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { + if err := w.WriteHeader(&tar.Header{ + Typeflag: tar.TypeReg, + Name: prefix + name, + Size: int64(len(content)), + Format: format, + }); err != nil { + return err + } + if _, err := io.CopyN(w, bytes.NewReader(content), int64(len(content))); err != nil { + return err + } + return nil + }) +} + +func fileModeToTarMode(mode os.FileMode) (int64, error) { + h, err := tar.FileInfoHeader(fileInfoOnlyMode(mode), "") + if err != nil { + return 0, err + } + return h.Mode, nil +} + +// fileInfoOnlyMode is os.FileMode that populates only file mode. +type fileInfoOnlyMode os.FileMode + +func (f fileInfoOnlyMode) Name() string { return "" } +func (f fileInfoOnlyMode) Size() int64 { return 0 } +func (f fileInfoOnlyMode) Mode() os.FileMode { return os.FileMode(f) } +func (f fileInfoOnlyMode) ModTime() time.Time { return time.Now() } +func (f fileInfoOnlyMode) IsDir() bool { return os.FileMode(f).IsDir() } +func (f fileInfoOnlyMode) Sys() interface{} { return nil } diff --git a/vendor/github.com/containerd/stargz-snapshotter/estargz/types.go b/vendor/github.com/containerd/stargz-snapshotter/estargz/types.go new file mode 100644 index 0000000000..384ff7fd7f --- /dev/null +++ b/vendor/github.com/containerd/stargz-snapshotter/estargz/types.go @@ -0,0 +1,316 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +/* + Copyright 2019 The Go Authors. All rights reserved. + Use of this source code is governed by a BSD-style + license that can be found in the LICENSE file. +*/ + +package estargz + +import ( + "archive/tar" + "hash" + "io" + "os" + "path" + "time" + + digest "github.com/opencontainers/go-digest" +) + +const ( + // TOCTarName is the name of the JSON file in the tar archive in the + // table of contents gzip stream. + TOCTarName = "stargz.index.json" + + // FooterSize is the number of bytes in the footer + // + // The footer is an empty gzip stream with no compression and an Extra + // header of the form "%016xSTARGZ", where the 64 bit hex-encoded + // number is the offset to the gzip stream of JSON TOC. + // + // 51 comes from: + // + // 10 bytes gzip header + // 2 bytes XLEN (length of Extra field) = 26 (4 bytes header + 16 hex digits + len("STARGZ")) + // 2 bytes Extra: SI1 = 'S', SI2 = 'G' + // 2 bytes Extra: LEN = 22 (16 hex digits + len("STARGZ")) + // 22 bytes Extra: subfield = fmt.Sprintf("%016xSTARGZ", offsetOfTOC) + // 5 bytes flate header + // 8 bytes gzip footer + // (End of the eStargz blob) + // + // NOTE: For Extra fields, subfield IDs SI1='S' SI2='G' is used for eStargz. + FooterSize = 51 + + // legacyFooterSize is the number of bytes in the legacy stargz footer. + // + // 47 comes from: + // + // 10 byte gzip header + + // 2 byte (LE16) length of extra, encoding 22 (16 hex digits + len("STARGZ")) == "\x16\x00" + + // 22 bytes of extra (fmt.Sprintf("%016xSTARGZ", tocGzipOffset)) + // 5 byte flate header + // 8 byte gzip footer (two little endian uint32s: digest, size) + legacyFooterSize = 47 + + // TOCJSONDigestAnnotation is an annotation for an image layer. This stores the + // digest of the TOC JSON. + // This annotation is valid only when it is specified in `.[]layers.annotations` + // of an image manifest. + TOCJSONDigestAnnotation = "containerd.io/snapshot/stargz/toc.digest" + + // StoreUncompressedSizeAnnotation is an additional annotation key for eStargz to enable lazy + // pulling on containers/storage. Stargz Store is required to expose the layer's uncompressed size + // to the runtime but current OCI image doesn't ship this information by default. So we store this + // to the special annotation. + StoreUncompressedSizeAnnotation = "io.containers.estargz.uncompressed-size" + + // PrefetchLandmark is a file entry which indicates the end position of + // prefetch in the stargz file. + PrefetchLandmark = ".prefetch.landmark" + + // NoPrefetchLandmark is a file entry which indicates that no prefetch should + // occur in the stargz file. + NoPrefetchLandmark = ".no.prefetch.landmark" + + landmarkContents = 0xf +) + +// JTOC is the JSON-serialized table of contents index of the files in the stargz file. +type JTOC struct { + Version int `json:"version"` + Entries []*TOCEntry `json:"entries"` +} + +// TOCEntry is an entry in the stargz file's TOC (Table of Contents). +type TOCEntry struct { + // Name is the tar entry's name. It is the complete path + // stored in the tar file, not just the base name. + Name string `json:"name"` + + // Type is one of "dir", "reg", "symlink", "hardlink", "char", + // "block", "fifo", or "chunk". + // The "chunk" type is used for regular file data chunks past the first + // TOCEntry; the 2nd chunk and on have only Type ("chunk"), Offset, + // ChunkOffset, and ChunkSize populated. + Type string `json:"type"` + + // Size, for regular files, is the logical size of the file. + Size int64 `json:"size,omitempty"` + + // ModTime3339 is the modification time of the tar entry. Empty + // means zero or unknown. Otherwise it's in UTC RFC3339 + // format. Use the ModTime method to access the time.Time value. + ModTime3339 string `json:"modtime,omitempty"` + modTime time.Time + + // LinkName, for symlinks and hardlinks, is the link target. + LinkName string `json:"linkName,omitempty"` + + // Mode is the permission and mode bits. + Mode int64 `json:"mode,omitempty"` + + // UID is the user ID of the owner. + UID int `json:"uid,omitempty"` + + // GID is the group ID of the owner. + GID int `json:"gid,omitempty"` + + // Uname is the username of the owner. + // + // In the serialized JSON, this field may only be present for + // the first entry with the same UID. + Uname string `json:"userName,omitempty"` + + // Gname is the group name of the owner. + // + // In the serialized JSON, this field may only be present for + // the first entry with the same GID. + Gname string `json:"groupName,omitempty"` + + // Offset, for regular files, provides the offset in the + // stargz file to the file's data bytes. See ChunkOffset and + // ChunkSize. + Offset int64 `json:"offset,omitempty"` + + nextOffset int64 // the Offset of the next entry with a non-zero Offset + + // DevMajor is the major device number for "char" and "block" types. + DevMajor int `json:"devMajor,omitempty"` + + // DevMinor is the major device number for "char" and "block" types. + DevMinor int `json:"devMinor,omitempty"` + + // NumLink is the number of entry names pointing to this entry. + // Zero means one name references this entry. + NumLink int + + // Xattrs are the extended attribute for the entry. + Xattrs map[string][]byte `json:"xattrs,omitempty"` + + // Digest stores the OCI checksum for regular files payload. + // It has the form "sha256:abcdef01234....". + Digest string `json:"digest,omitempty"` + + // ChunkOffset is non-zero if this is a chunk of a large, + // regular file. If so, the Offset is where the gzip header of + // ChunkSize bytes at ChunkOffset in Name begin. + // + // In serialized form, a "chunkSize" JSON field of zero means + // that the chunk goes to the end of the file. After reading + // from the stargz TOC, though, the ChunkSize is initialized + // to a non-zero file for when Type is either "reg" or + // "chunk". + ChunkOffset int64 `json:"chunkOffset,omitempty"` + ChunkSize int64 `json:"chunkSize,omitempty"` + + // ChunkDigest stores an OCI digest of the chunk. This must be formed + // as "sha256:0123abcd...". + ChunkDigest string `json:"chunkDigest,omitempty"` + + children map[string]*TOCEntry +} + +// ModTime returns the entry's modification time. +func (e *TOCEntry) ModTime() time.Time { return e.modTime } + +// NextOffset returns the position (relative to the start of the +// stargz file) of the next gzip boundary after e.Offset. +func (e *TOCEntry) NextOffset() int64 { return e.nextOffset } + +func (e *TOCEntry) addChild(baseName string, child *TOCEntry) { + if e.children == nil { + e.children = make(map[string]*TOCEntry) + } + if child.Type == "dir" { + e.NumLink++ // Entry ".." in the subdirectory links to this directory + } + e.children[baseName] = child +} + +// isDataType reports whether TOCEntry is a regular file or chunk (something that +// contains regular file data). +func (e *TOCEntry) isDataType() bool { return e.Type == "reg" || e.Type == "chunk" } + +// Stat returns a FileInfo value representing e. +func (e *TOCEntry) Stat() os.FileInfo { return fileInfo{e} } + +// ForeachChild calls f for each child item. If f returns false, iteration ends. +// If e is not a directory, f is not called. +func (e *TOCEntry) ForeachChild(f func(baseName string, ent *TOCEntry) bool) { + for name, ent := range e.children { + if !f(name, ent) { + return + } + } +} + +// LookupChild returns the directory e's child by its base name. +func (e *TOCEntry) LookupChild(baseName string) (child *TOCEntry, ok bool) { + child, ok = e.children[baseName] + return +} + +// fileInfo implements os.FileInfo using the wrapped *TOCEntry. +type fileInfo struct{ e *TOCEntry } + +var _ os.FileInfo = fileInfo{} + +func (fi fileInfo) Name() string { return path.Base(fi.e.Name) } +func (fi fileInfo) IsDir() bool { return fi.e.Type == "dir" } +func (fi fileInfo) Size() int64 { return fi.e.Size } +func (fi fileInfo) ModTime() time.Time { return fi.e.ModTime() } +func (fi fileInfo) Sys() interface{} { return fi.e } +func (fi fileInfo) Mode() (m os.FileMode) { + // TOCEntry.Mode is tar.Header.Mode so we can understand the these bits using `tar` pkg. + m = (&tar.Header{Mode: fi.e.Mode}).FileInfo().Mode() & + (os.ModePerm | os.ModeSetuid | os.ModeSetgid | os.ModeSticky) + switch fi.e.Type { + case "dir": + m |= os.ModeDir + case "symlink": + m |= os.ModeSymlink + case "char": + m |= os.ModeDevice | os.ModeCharDevice + case "block": + m |= os.ModeDevice + case "fifo": + m |= os.ModeNamedPipe + } + return m +} + +// TOCEntryVerifier holds verifiers that are usable for verifying chunks contained +// in a eStargz blob. +type TOCEntryVerifier interface { + + // Verifier provides a content verifier that can be used for verifying the + // contents of the specified TOCEntry. + Verifier(ce *TOCEntry) (digest.Verifier, error) +} + +// Compression provides the compression helper to be used creating and parsing eStargz. +// This package provides gzip-based Compression by default, but any compression +// algorithm (e.g. zstd) can be used as long as it implements Compression. +type Compression interface { + Compressor + Decompressor +} + +// Compressor represents the helper mothods to be used for creating eStargz. +type Compressor interface { + // Writer returns WriteCloser to be used for writing a chunk to eStargz. + // Everytime a chunk is written, the WriteCloser is closed and Writer is + // called again for writing the next chunk. + Writer(w io.Writer) (io.WriteCloser, error) + + // WriteTOCAndFooter is called to write JTOC to the passed Writer. + // diffHash calculates the DiffID (uncompressed sha256 hash) of the blob + // WriteTOCAndFooter can optionally write anything that affects DiffID calculation + // (e.g. uncompressed TOC JSON). + // + // This function returns tocDgst that represents the digest of TOC that will be used + // to verify this blob when it's parsed. + WriteTOCAndFooter(w io.Writer, off int64, toc *JTOC, diffHash hash.Hash) (tocDgst digest.Digest, err error) +} + +// Decompressor represents the helper mothods to be used for parsing eStargz. +type Decompressor interface { + // Reader returns ReadCloser to be used for decompressing file payload. + Reader(r io.Reader) (io.ReadCloser, error) + + // FooterSize returns the size of the footer of this blob. + FooterSize() int64 + + // ParseFooter parses the footer and returns the offset and (compressed) size of TOC. + // payloadBlobSize is the (compressed) size of the blob payload (i.e. the size between + // the top until the TOC JSON). + // + // Here, tocSize is optional. If tocSize <= 0, it's by default the size of the range + // from tocOffset until the beginning of the footer (blob size - tocOff - FooterSize). + ParseFooter(p []byte) (blobPayloadSize, tocOffset, tocSize int64, err error) + + // ParseTOC parses TOC from the passed reader. The reader provides the partial contents + // of the underlying blob that has the range specified by ParseFooter method. + // + // This function returns tocDgst that represents the digest of TOC that will be used + // to verify this blob. This must match to the value returned from + // Compressor.WriteTOCAndFooter that is used when creating this blob. + ParseTOC(r io.Reader) (toc *JTOC, tocDgst digest.Digest, err error) +} diff --git a/vendor/github.com/containers/image/v5/LICENSE b/vendor/github.com/containers/image/v5/LICENSE new file mode 100644 index 0000000000..9535635306 --- /dev/null +++ b/vendor/github.com/containers/image/v5/LICENSE @@ -0,0 +1,189 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + 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 + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/containers/image/v5/docker/reference/README.md b/vendor/github.com/containers/image/v5/docker/reference/README.md new file mode 100644 index 0000000000..3c4d74eb4d --- /dev/null +++ b/vendor/github.com/containers/image/v5/docker/reference/README.md @@ -0,0 +1,2 @@ +This is a copy of github.com/docker/distribution/reference as of commit 3226863cbcba6dbc2f6c83a37b28126c934af3f8, +except that ParseAnyReferenceWithSet has been removed to drop the dependency on github.com/docker/distribution/digestset. \ No newline at end of file diff --git a/vendor/github.com/containers/image/v5/docker/reference/helpers.go b/vendor/github.com/containers/image/v5/docker/reference/helpers.go new file mode 100644 index 0000000000..978df7eabb --- /dev/null +++ b/vendor/github.com/containers/image/v5/docker/reference/helpers.go @@ -0,0 +1,42 @@ +package reference + +import "path" + +// IsNameOnly returns true if reference only contains a repo name. +func IsNameOnly(ref Named) bool { + if _, ok := ref.(NamedTagged); ok { + return false + } + if _, ok := ref.(Canonical); ok { + return false + } + return true +} + +// FamiliarName returns the familiar name string +// for the given named, familiarizing if needed. +func FamiliarName(ref Named) string { + if nn, ok := ref.(normalizedNamed); ok { + return nn.Familiar().Name() + } + return ref.Name() +} + +// FamiliarString returns the familiar string representation +// for the given reference, familiarizing if needed. +func FamiliarString(ref Reference) string { + if nn, ok := ref.(normalizedNamed); ok { + return nn.Familiar().String() + } + return ref.String() +} + +// FamiliarMatch reports whether ref matches the specified pattern. +// See https://godoc.org/path#Match for supported patterns. +func FamiliarMatch(pattern string, ref Reference) (bool, error) { + matched, err := path.Match(pattern, FamiliarString(ref)) + if namedRef, isNamed := ref.(Named); isNamed && !matched { + matched, _ = path.Match(pattern, FamiliarName(namedRef)) + } + return matched, err +} diff --git a/vendor/github.com/containers/image/v5/docker/reference/normalize.go b/vendor/github.com/containers/image/v5/docker/reference/normalize.go new file mode 100644 index 0000000000..6a86ec64fd --- /dev/null +++ b/vendor/github.com/containers/image/v5/docker/reference/normalize.go @@ -0,0 +1,181 @@ +package reference + +import ( + "errors" + "fmt" + "strings" + + "github.com/opencontainers/go-digest" +) + +var ( + legacyDefaultDomain = "index.docker.io" + defaultDomain = "docker.io" + officialRepoName = "library" + defaultTag = "latest" +) + +// normalizedNamed represents a name which has been +// normalized and has a familiar form. A familiar name +// is what is used in Docker UI. An example normalized +// name is "docker.io/library/ubuntu" and corresponding +// familiar name of "ubuntu". +type normalizedNamed interface { + Named + Familiar() Named +} + +// ParseNormalizedNamed parses a string into a named reference +// transforming a familiar name from Docker UI to a fully +// qualified reference. If the value may be an identifier +// use ParseAnyReference. +func ParseNormalizedNamed(s string) (Named, error) { + if ok := anchoredIdentifierRegexp.MatchString(s); ok { + return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s) + } + domain, remainder := splitDockerDomain(s) + var remoteName string + if tagSep := strings.IndexRune(remainder, ':'); tagSep > -1 { + remoteName = remainder[:tagSep] + } else { + remoteName = remainder + } + if strings.ToLower(remoteName) != remoteName { + return nil, errors.New("invalid reference format: repository name must be lowercase") + } + + ref, err := Parse(domain + "/" + remainder) + if err != nil { + return nil, err + } + named, isNamed := ref.(Named) + if !isNamed { + return nil, fmt.Errorf("reference %s has no name", ref.String()) + } + return named, nil +} + +// ParseDockerRef normalizes the image reference following the docker convention. This is added +// mainly for backward compatibility. +// The reference returned can only be either tagged or digested. For reference contains both tag +// and digest, the function returns digested reference, e.g. docker.io/library/busybox:latest@ +// sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa will be returned as +// docker.io/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa. +func ParseDockerRef(ref string) (Named, error) { + named, err := ParseNormalizedNamed(ref) + if err != nil { + return nil, err + } + if _, ok := named.(NamedTagged); ok { + if canonical, ok := named.(Canonical); ok { + // The reference is both tagged and digested, only + // return digested. + newNamed, err := WithName(canonical.Name()) + if err != nil { + return nil, err + } + newCanonical, err := WithDigest(newNamed, canonical.Digest()) + if err != nil { + return nil, err + } + return newCanonical, nil + } + } + return TagNameOnly(named), nil +} + +// splitDockerDomain splits a repository name to domain and remotename string. +// If no valid domain is found, the default domain is used. Repository name +// needs to be already validated before. +func splitDockerDomain(name string) (domain, remainder string) { + i := strings.IndexRune(name, '/') + if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") { + domain, remainder = defaultDomain, name + } else { + domain, remainder = name[:i], name[i+1:] + } + if domain == legacyDefaultDomain { + domain = defaultDomain + } + if domain == defaultDomain && !strings.ContainsRune(remainder, '/') { + remainder = officialRepoName + "/" + remainder + } + return +} + +// familiarizeName returns a shortened version of the name familiar +// to to the Docker UI. Familiar names have the default domain +// "docker.io" and "library/" repository prefix removed. +// For example, "docker.io/library/redis" will have the familiar +// name "redis" and "docker.io/dmcgowan/myapp" will be "dmcgowan/myapp". +// Returns a familiarized named only reference. +func familiarizeName(named namedRepository) repository { + repo := repository{ + domain: named.Domain(), + path: named.Path(), + } + + if repo.domain == defaultDomain { + repo.domain = "" + // Handle official repositories which have the pattern "library/" + if split := strings.Split(repo.path, "/"); len(split) == 2 && split[0] == officialRepoName { + repo.path = split[1] + } + } + return repo +} + +func (r reference) Familiar() Named { + return reference{ + namedRepository: familiarizeName(r.namedRepository), + tag: r.tag, + digest: r.digest, + } +} + +func (r repository) Familiar() Named { + return familiarizeName(r) +} + +func (t taggedReference) Familiar() Named { + return taggedReference{ + namedRepository: familiarizeName(t.namedRepository), + tag: t.tag, + } +} + +func (c canonicalReference) Familiar() Named { + return canonicalReference{ + namedRepository: familiarizeName(c.namedRepository), + digest: c.digest, + } +} + +// TagNameOnly adds the default tag "latest" to a reference if it only has +// a repo name. +func TagNameOnly(ref Named) Named { + if IsNameOnly(ref) { + namedTagged, err := WithTag(ref, defaultTag) + if err != nil { + // Default tag must be valid, to create a NamedTagged + // type with non-validated input the WithTag function + // should be used instead + panic(err) + } + return namedTagged + } + return ref +} + +// ParseAnyReference parses a reference string as a possible identifier, +// full digest, or familiar name. +func ParseAnyReference(ref string) (Reference, error) { + if ok := anchoredIdentifierRegexp.MatchString(ref); ok { + return digestReference("sha256:" + ref), nil + } + if dgst, err := digest.Parse(ref); err == nil { + return digestReference(dgst), nil + } + + return ParseNormalizedNamed(ref) +} diff --git a/vendor/github.com/containers/image/v5/docker/reference/reference.go b/vendor/github.com/containers/image/v5/docker/reference/reference.go new file mode 100644 index 0000000000..8c0c23b2fe --- /dev/null +++ b/vendor/github.com/containers/image/v5/docker/reference/reference.go @@ -0,0 +1,433 @@ +// Package reference provides a general type to represent any way of referencing images within the registry. +// Its main purpose is to abstract tags and digests (content-addressable hash). +// +// Grammar +// +// reference := name [ ":" tag ] [ "@" digest ] +// name := [domain '/'] path-component ['/' path-component]* +// domain := domain-component ['.' domain-component]* [':' port-number] +// domain-component := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/ +// port-number := /[0-9]+/ +// path-component := alpha-numeric [separator alpha-numeric]* +// alpha-numeric := /[a-z0-9]+/ +// separator := /[_.]|__|[-]*/ +// +// tag := /[\w][\w.-]{0,127}/ +// +// digest := digest-algorithm ":" digest-hex +// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]* +// digest-algorithm-separator := /[+.-_]/ +// digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/ +// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value +// +// identifier := /[a-f0-9]{64}/ +// short-identifier := /[a-f0-9]{6,64}/ +package reference + +import ( + "errors" + "fmt" + "strings" + + "github.com/opencontainers/go-digest" +) + +const ( + // NameTotalLengthMax is the maximum total number of characters in a repository name. + NameTotalLengthMax = 255 +) + +var ( + // ErrReferenceInvalidFormat represents an error while trying to parse a string as a reference. + ErrReferenceInvalidFormat = errors.New("invalid reference format") + + // ErrTagInvalidFormat represents an error while trying to parse a string as a tag. + ErrTagInvalidFormat = errors.New("invalid tag format") + + // ErrDigestInvalidFormat represents an error while trying to parse a string as a tag. + ErrDigestInvalidFormat = errors.New("invalid digest format") + + // ErrNameContainsUppercase is returned for invalid repository names that contain uppercase characters. + ErrNameContainsUppercase = errors.New("repository name must be lowercase") + + // ErrNameEmpty is returned for empty, invalid repository names. + ErrNameEmpty = errors.New("repository name must have at least one component") + + // ErrNameTooLong is returned when a repository name is longer than NameTotalLengthMax. + ErrNameTooLong = fmt.Errorf("repository name must not be more than %v characters", NameTotalLengthMax) + + // ErrNameNotCanonical is returned when a name is not canonical. + ErrNameNotCanonical = errors.New("repository name must be canonical") +) + +// Reference is an opaque object reference identifier that may include +// modifiers such as a hostname, name, tag, and digest. +type Reference interface { + // String returns the full reference + String() string +} + +// Field provides a wrapper type for resolving correct reference types when +// working with encoding. +type Field struct { + reference Reference +} + +// AsField wraps a reference in a Field for encoding. +func AsField(reference Reference) Field { + return Field{reference} +} + +// Reference unwraps the reference type from the field to +// return the Reference object. This object should be +// of the appropriate type to further check for different +// reference types. +func (f Field) Reference() Reference { + return f.reference +} + +// MarshalText serializes the field to byte text which +// is the string of the reference. +func (f Field) MarshalText() (p []byte, err error) { + return []byte(f.reference.String()), nil +} + +// UnmarshalText parses text bytes by invoking the +// reference parser to ensure the appropriately +// typed reference object is wrapped by field. +func (f *Field) UnmarshalText(p []byte) error { + r, err := Parse(string(p)) + if err != nil { + return err + } + + f.reference = r + return nil +} + +// Named is an object with a full name +type Named interface { + Reference + Name() string +} + +// Tagged is an object which has a tag +type Tagged interface { + Reference + Tag() string +} + +// NamedTagged is an object including a name and tag. +type NamedTagged interface { + Named + Tag() string +} + +// Digested is an object which has a digest +// in which it can be referenced by +type Digested interface { + Reference + Digest() digest.Digest +} + +// Canonical reference is an object with a fully unique +// name including a name with domain and digest +type Canonical interface { + Named + Digest() digest.Digest +} + +// namedRepository is a reference to a repository with a name. +// A namedRepository has both domain and path components. +type namedRepository interface { + Named + Domain() string + Path() string +} + +// Domain returns the domain part of the Named reference +func Domain(named Named) string { + if r, ok := named.(namedRepository); ok { + return r.Domain() + } + domain, _ := splitDomain(named.Name()) + return domain +} + +// Path returns the name without the domain part of the Named reference +func Path(named Named) (name string) { + if r, ok := named.(namedRepository); ok { + return r.Path() + } + _, path := splitDomain(named.Name()) + return path +} + +func splitDomain(name string) (string, string) { + match := anchoredNameRegexp.FindStringSubmatch(name) + if len(match) != 3 { + return "", name + } + return match[1], match[2] +} + +// SplitHostname splits a named reference into a +// hostname and name string. If no valid hostname is +// found, the hostname is empty and the full value +// is returned as name +// DEPRECATED: Use Domain or Path +func SplitHostname(named Named) (string, string) { + if r, ok := named.(namedRepository); ok { + return r.Domain(), r.Path() + } + return splitDomain(named.Name()) +} + +// Parse parses s and returns a syntactically valid Reference. +// If an error was encountered it is returned, along with a nil Reference. +// NOTE: Parse will not handle short digests. +func Parse(s string) (Reference, error) { + matches := ReferenceRegexp.FindStringSubmatch(s) + if matches == nil { + if s == "" { + return nil, ErrNameEmpty + } + if ReferenceRegexp.FindStringSubmatch(strings.ToLower(s)) != nil { + return nil, ErrNameContainsUppercase + } + return nil, ErrReferenceInvalidFormat + } + + if len(matches[1]) > NameTotalLengthMax { + return nil, ErrNameTooLong + } + + var repo repository + + nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1]) + if len(nameMatch) == 3 { + repo.domain = nameMatch[1] + repo.path = nameMatch[2] + } else { + repo.domain = "" + repo.path = matches[1] + } + + ref := reference{ + namedRepository: repo, + tag: matches[2], + } + if matches[3] != "" { + var err error + ref.digest, err = digest.Parse(matches[3]) + if err != nil { + return nil, err + } + } + + r := getBestReferenceType(ref) + if r == nil { + return nil, ErrNameEmpty + } + + return r, nil +} + +// ParseNamed parses s and returns a syntactically valid reference implementing +// the Named interface. The reference must have a name and be in the canonical +// form, otherwise an error is returned. +// If an error was encountered it is returned, along with a nil Reference. +// NOTE: ParseNamed will not handle short digests. +func ParseNamed(s string) (Named, error) { + named, err := ParseNormalizedNamed(s) + if err != nil { + return nil, err + } + if named.String() != s { + return nil, ErrNameNotCanonical + } + return named, nil +} + +// WithName returns a named object representing the given string. If the input +// is invalid ErrReferenceInvalidFormat will be returned. +func WithName(name string) (Named, error) { + if len(name) > NameTotalLengthMax { + return nil, ErrNameTooLong + } + + match := anchoredNameRegexp.FindStringSubmatch(name) + if match == nil || len(match) != 3 { + return nil, ErrReferenceInvalidFormat + } + return repository{ + domain: match[1], + path: match[2], + }, nil +} + +// WithTag combines the name from "name" and the tag from "tag" to form a +// reference incorporating both the name and the tag. +func WithTag(name Named, tag string) (NamedTagged, error) { + if !anchoredTagRegexp.MatchString(tag) { + return nil, ErrTagInvalidFormat + } + var repo repository + if r, ok := name.(namedRepository); ok { + repo.domain = r.Domain() + repo.path = r.Path() + } else { + repo.path = name.Name() + } + if canonical, ok := name.(Canonical); ok { + return reference{ + namedRepository: repo, + tag: tag, + digest: canonical.Digest(), + }, nil + } + return taggedReference{ + namedRepository: repo, + tag: tag, + }, nil +} + +// WithDigest combines the name from "name" and the digest from "digest" to form +// a reference incorporating both the name and the digest. +func WithDigest(name Named, digest digest.Digest) (Canonical, error) { + if !anchoredDigestRegexp.MatchString(digest.String()) { + return nil, ErrDigestInvalidFormat + } + var repo repository + if r, ok := name.(namedRepository); ok { + repo.domain = r.Domain() + repo.path = r.Path() + } else { + repo.path = name.Name() + } + if tagged, ok := name.(Tagged); ok { + return reference{ + namedRepository: repo, + tag: tagged.Tag(), + digest: digest, + }, nil + } + return canonicalReference{ + namedRepository: repo, + digest: digest, + }, nil +} + +// TrimNamed removes any tag or digest from the named reference. +func TrimNamed(ref Named) Named { + domain, path := SplitHostname(ref) + return repository{ + domain: domain, + path: path, + } +} + +func getBestReferenceType(ref reference) Reference { + if ref.Name() == "" { + // Allow digest only references + if ref.digest != "" { + return digestReference(ref.digest) + } + return nil + } + if ref.tag == "" { + if ref.digest != "" { + return canonicalReference{ + namedRepository: ref.namedRepository, + digest: ref.digest, + } + } + return ref.namedRepository + } + if ref.digest == "" { + return taggedReference{ + namedRepository: ref.namedRepository, + tag: ref.tag, + } + } + + return ref +} + +type reference struct { + namedRepository + tag string + digest digest.Digest +} + +func (r reference) String() string { + return r.Name() + ":" + r.tag + "@" + r.digest.String() +} + +func (r reference) Tag() string { + return r.tag +} + +func (r reference) Digest() digest.Digest { + return r.digest +} + +type repository struct { + domain string + path string +} + +func (r repository) String() string { + return r.Name() +} + +func (r repository) Name() string { + if r.domain == "" { + return r.path + } + return r.domain + "/" + r.path +} + +func (r repository) Domain() string { + return r.domain +} + +func (r repository) Path() string { + return r.path +} + +type digestReference digest.Digest + +func (d digestReference) String() string { + return digest.Digest(d).String() +} + +func (d digestReference) Digest() digest.Digest { + return digest.Digest(d) +} + +type taggedReference struct { + namedRepository + tag string +} + +func (t taggedReference) String() string { + return t.Name() + ":" + t.tag +} + +func (t taggedReference) Tag() string { + return t.tag +} + +type canonicalReference struct { + namedRepository + digest digest.Digest +} + +func (c canonicalReference) String() string { + return c.Name() + "@" + c.digest.String() +} + +func (c canonicalReference) Digest() digest.Digest { + return c.digest +} diff --git a/vendor/github.com/containers/image/v5/docker/reference/regexp.go b/vendor/github.com/containers/image/v5/docker/reference/regexp.go new file mode 100644 index 0000000000..7860349320 --- /dev/null +++ b/vendor/github.com/containers/image/v5/docker/reference/regexp.go @@ -0,0 +1,143 @@ +package reference + +import "regexp" + +var ( + // alphaNumericRegexp defines the alpha numeric atom, typically a + // component of names. This only allows lower case characters and digits. + alphaNumericRegexp = match(`[a-z0-9]+`) + + // separatorRegexp defines the separators allowed to be embedded in name + // components. This allow one period, one or two underscore and multiple + // dashes. + separatorRegexp = match(`(?:[._]|__|[-]*)`) + + // nameComponentRegexp restricts registry path component names to start + // with at least one letter or number, with following parts able to be + // separated by one period, one or two underscore and multiple dashes. + nameComponentRegexp = expression( + alphaNumericRegexp, + optional(repeated(separatorRegexp, alphaNumericRegexp))) + + // domainComponentRegexp restricts the registry domain component of a + // repository name to start with a component as defined by DomainRegexp + // and followed by an optional port. + domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`) + + // DomainRegexp defines the structure of potential domain components + // that may be part of image names. This is purposely a subset of what is + // allowed by DNS to ensure backwards compatibility with Docker image + // names. + DomainRegexp = expression( + domainComponentRegexp, + optional(repeated(literal(`.`), domainComponentRegexp)), + optional(literal(`:`), match(`[0-9]+`))) + + // TagRegexp matches valid tag names. From docker/docker:graph/tags.go. + TagRegexp = match(`[\w][\w.-]{0,127}`) + + // anchoredTagRegexp matches valid tag names, anchored at the start and + // end of the matched string. + anchoredTagRegexp = anchored(TagRegexp) + + // DigestRegexp matches valid digests. + DigestRegexp = match(`[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`) + + // anchoredDigestRegexp matches valid digests, anchored at the start and + // end of the matched string. + anchoredDigestRegexp = anchored(DigestRegexp) + + // NameRegexp is the format for the name component of references. The + // regexp has capturing groups for the domain and name part omitting + // the separating forward slash from either. + NameRegexp = expression( + optional(DomainRegexp, literal(`/`)), + nameComponentRegexp, + optional(repeated(literal(`/`), nameComponentRegexp))) + + // anchoredNameRegexp is used to parse a name value, capturing the + // domain and trailing components. + anchoredNameRegexp = anchored( + optional(capture(DomainRegexp), literal(`/`)), + capture(nameComponentRegexp, + optional(repeated(literal(`/`), nameComponentRegexp)))) + + // ReferenceRegexp is the full supported format of a reference. The regexp + // is anchored and has capturing groups for name, tag, and digest + // components. + ReferenceRegexp = anchored(capture(NameRegexp), + optional(literal(":"), capture(TagRegexp)), + optional(literal("@"), capture(DigestRegexp))) + + // IdentifierRegexp is the format for string identifier used as a + // content addressable identifier using sha256. These identifiers + // are like digests without the algorithm, since sha256 is used. + IdentifierRegexp = match(`([a-f0-9]{64})`) + + // ShortIdentifierRegexp is the format used to represent a prefix + // of an identifier. A prefix may be used to match a sha256 identifier + // within a list of trusted identifiers. + ShortIdentifierRegexp = match(`([a-f0-9]{6,64})`) + + // anchoredIdentifierRegexp is used to check or match an + // identifier value, anchored at start and end of string. + anchoredIdentifierRegexp = anchored(IdentifierRegexp) + + // anchoredShortIdentifierRegexp is used to check if a value + // is a possible identifier prefix, anchored at start and end + // of string. + anchoredShortIdentifierRegexp = anchored(ShortIdentifierRegexp) +) + +// match compiles the string to a regular expression. +var match = regexp.MustCompile + +// literal compiles s into a literal regular expression, escaping any regexp +// reserved characters. +func literal(s string) *regexp.Regexp { + re := match(regexp.QuoteMeta(s)) + + if _, complete := re.LiteralPrefix(); !complete { + panic("must be a literal") + } + + return re +} + +// expression defines a full expression, where each regular expression must +// follow the previous. +func expression(res ...*regexp.Regexp) *regexp.Regexp { + var s string + for _, re := range res { + s += re.String() + } + + return match(s) +} + +// optional wraps the expression in a non-capturing group and makes the +// production optional. +func optional(res ...*regexp.Regexp) *regexp.Regexp { + return match(group(expression(res...)).String() + `?`) +} + +// repeated wraps the regexp in a non-capturing group to get one or more +// matches. +func repeated(res ...*regexp.Regexp) *regexp.Regexp { + return match(group(expression(res...)).String() + `+`) +} + +// group wraps the regexp in a non-capturing group. +func group(res ...*regexp.Regexp) *regexp.Regexp { + return match(`(?:` + expression(res...).String() + `)`) +} + +// capture wraps the expression in a capturing group. +func capture(res ...*regexp.Regexp) *regexp.Regexp { + return match(`(` + expression(res...).String() + `)`) +} + +// anchored anchors the regular expression by adding start and end delimiters. +func anchored(res ...*regexp.Regexp) *regexp.Regexp { + return match(`^` + expression(res...).String() + `$`) +} diff --git a/vendor/github.com/containers/image/v5/internal/pkg/keyctl/key.go b/vendor/github.com/containers/image/v5/internal/pkg/keyctl/key.go new file mode 100644 index 0000000000..bf6cc87d42 --- /dev/null +++ b/vendor/github.com/containers/image/v5/internal/pkg/keyctl/key.go @@ -0,0 +1,74 @@ +// Copyright 2015 Jesse Sipprell. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux +// +build linux + +package keyctl + +import ( + "golang.org/x/sys/unix" +) + +// Key represents a single key linked to one or more kernel keyrings. +type Key struct { + Name string + + id, ring keyID + size int +} + +// ID returns the 32-bit kernel identifier for a specific key +func (k *Key) ID() int32 { + return int32(k.id) +} + +// Get the key's value as a byte slice +func (k *Key) Get() ([]byte, error) { + var ( + b []byte + err error + sizeRead int + ) + + if k.size == 0 { + k.size = 512 + } + + size := k.size + + b = make([]byte, int(size)) + sizeRead = size + 1 + for sizeRead > size { + r1, err := unix.KeyctlBuffer(unix.KEYCTL_READ, int(k.id), b, size) + if err != nil { + return nil, err + } + + if sizeRead = int(r1); sizeRead > size { + b = make([]byte, sizeRead) + size = sizeRead + sizeRead = size + 1 + } else { + k.size = sizeRead + } + } + return b[:k.size], err +} + +// Unlink a key from the keyring it was loaded from (or added to). If the key +// is not linked to any other keyrings, it is destroyed. +func (k *Key) Unlink() error { + _, err := unix.KeyctlInt(unix.KEYCTL_UNLINK, int(k.id), int(k.ring), 0, 0) + return err +} + +// Describe returns a string describing the attributes of a specified key +func (k *Key) Describe() (string, error) { + keyAttr, err := unix.KeyctlString(unix.KEYCTL_DESCRIBE, int(k.id)) + if err != nil { + return "", err + } + return keyAttr, nil +} diff --git a/vendor/github.com/containers/image/v5/internal/pkg/keyctl/keyring.go b/vendor/github.com/containers/image/v5/internal/pkg/keyctl/keyring.go new file mode 100644 index 0000000000..5eaad615c7 --- /dev/null +++ b/vendor/github.com/containers/image/v5/internal/pkg/keyctl/keyring.go @@ -0,0 +1,118 @@ +// Copyright 2015 Jesse Sipprell. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux +// +build linux + +// Package keyctl is a Go interface to linux kernel keyrings (keyctl interface) +package keyctl + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +// Keyring is the basic interface to a linux keyctl keyring. +type Keyring interface { + ID + Add(string, []byte) (*Key, error) + Search(string) (*Key, error) +} + +type keyring struct { + id keyID +} + +// ID is unique 32-bit serial number identifiers for all Keys and Keyrings have. +type ID interface { + ID() int32 +} + +// Add a new key to a keyring. The key can be searched for later by name. +func (kr *keyring) Add(name string, key []byte) (*Key, error) { + r, err := unix.AddKey("user", name, key, int(kr.id)) + if err == nil { + key := &Key{Name: name, id: keyID(r), ring: kr.id} + return key, nil + } + return nil, err +} + +// Search for a key by name, this also searches child keyrings linked to this +// one. The key, if found, is linked to the top keyring that Search() was called +// from. +func (kr *keyring) Search(name string) (*Key, error) { + id, err := unix.KeyctlSearch(int(kr.id), "user", name, 0) + if err == nil { + return &Key{Name: name, id: keyID(id), ring: kr.id}, nil + } + return nil, err +} + +// ID returns the 32-bit kernel identifier of a keyring +func (kr *keyring) ID() int32 { + return int32(kr.id) +} + +// SessionKeyring returns the current login session keyring +func SessionKeyring() (Keyring, error) { + return newKeyring(unix.KEY_SPEC_SESSION_KEYRING) +} + +// UserKeyring returns the keyring specific to the current user. +func UserKeyring() (Keyring, error) { + return newKeyring(unix.KEY_SPEC_USER_KEYRING) +} + +// Unlink an object from a keyring +func Unlink(parent Keyring, child ID) error { + _, err := unix.KeyctlInt(unix.KEYCTL_UNLINK, int(child.ID()), int(parent.ID()), 0, 0) + return err +} + +// Link a key into a keyring +func Link(parent Keyring, child ID) error { + _, err := unix.KeyctlInt(unix.KEYCTL_LINK, int(child.ID()), int(parent.ID()), 0, 0) + return err +} + +// ReadUserKeyring reads user keyring and returns slice of key with id(key_serial_t) representing the IDs of all the keys that are linked to it +func ReadUserKeyring() ([]*Key, error) { + var ( + b []byte + err error + sizeRead int + ) + krSize := 4 + size := krSize + b = make([]byte, size) + sizeRead = size + 1 + for sizeRead > size { + r1, err := unix.KeyctlBuffer(unix.KEYCTL_READ, unix.KEY_SPEC_USER_KEYRING, b, size) + if err != nil { + return nil, err + } + + if sizeRead = int(r1); sizeRead > size { + b = make([]byte, sizeRead) + size = sizeRead + sizeRead = size + 1 + } else { + krSize = sizeRead + } + } + keyIDs := getKeyIDsFromByte(b[:krSize]) + return keyIDs, err +} + +func getKeyIDsFromByte(byteKeyIDs []byte) []*Key { + idSize := 4 + var keys []*Key + for idx := 0; idx+idSize <= len(byteKeyIDs); idx = idx + idSize { + tempID := *(*int32)(unsafe.Pointer(&byteKeyIDs[idx])) + keys = append(keys, &Key{id: keyID(tempID)}) + } + return keys +} diff --git a/vendor/github.com/containers/image/v5/internal/pkg/keyctl/perm.go b/vendor/github.com/containers/image/v5/internal/pkg/keyctl/perm.go new file mode 100644 index 0000000000..5f4d2157ae --- /dev/null +++ b/vendor/github.com/containers/image/v5/internal/pkg/keyctl/perm.go @@ -0,0 +1,34 @@ +// Copyright 2015 Jesse Sipprell. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux +// +build linux + +package keyctl + +import ( + "golang.org/x/sys/unix" +) + +// KeyPerm represents in-kernel access control permission to keys and keyrings +// as a 32-bit integer broken up into four permission sets, one per byte. +// In MSB order, the perms are: Processor, User, Group, Other. +type KeyPerm uint32 + +const ( + // PermOtherAll sets all permission for Other + PermOtherAll KeyPerm = 0x3f << (8 * iota) + // PermGroupAll sets all permission for Group + PermGroupAll + // PermUserAll sets all permission for User + PermUserAll + // PermProcessAll sets all permission for Processor + PermProcessAll +) + +// SetPerm sets the permissions on a key or keyring. +func SetPerm(k ID, p KeyPerm) error { + err := unix.KeyctlSetperm(int(k.ID()), uint32(p)) + return err +} diff --git a/vendor/github.com/containers/image/v5/internal/pkg/keyctl/sys_linux.go b/vendor/github.com/containers/image/v5/internal/pkg/keyctl/sys_linux.go new file mode 100644 index 0000000000..f61666e42c --- /dev/null +++ b/vendor/github.com/containers/image/v5/internal/pkg/keyctl/sys_linux.go @@ -0,0 +1,26 @@ +// Copyright 2015 Jesse Sipprell. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux +// +build linux + +package keyctl + +import ( + "golang.org/x/sys/unix" +) + +type keyID int32 + +func newKeyring(id keyID) (*keyring, error) { + r1, err := unix.KeyctlGetKeyringID(int(id), true) + if err != nil { + return nil, err + } + + if id < 0 { + r1 = int(id) + } + return &keyring{id: keyID(r1)}, nil +} diff --git a/vendor/github.com/containers/image/v5/internal/rootless/rootless.go b/vendor/github.com/containers/image/v5/internal/rootless/rootless.go new file mode 100644 index 0000000000..80623bfbc1 --- /dev/null +++ b/vendor/github.com/containers/image/v5/internal/rootless/rootless.go @@ -0,0 +1,25 @@ +package rootless + +import ( + "os" + "strconv" +) + +// GetRootlessEUID returns the UID of the current user (in the parent userNS, if any) +// +// Podman and similar software, in “rootless” configuration, when run as a non-root +// user, very early switches to a user namespace, where Geteuid() == 0 (but does not +// switch to a limited mount namespace); so, code relying on Geteuid() would use +// system-wide paths in e.g. /var, when the user is actually not privileged to write to +// them, and expects state to be stored in the home directory. +// +// If Podman is setting up such a user namespace, it records the original UID in an +// environment variable, allowing us to make choices based on the actual user’s identity. +func GetRootlessEUID() int { + euidEnv := os.Getenv("_CONTAINERS_ROOTLESS_UID") + if euidEnv != "" { + euid, _ := strconv.Atoi(euidEnv) + return euid + } + return os.Geteuid() +} diff --git a/vendor/github.com/containers/image/v5/pkg/compression/internal/types.go b/vendor/github.com/containers/image/v5/pkg/compression/internal/types.go new file mode 100644 index 0000000000..fb37ca3179 --- /dev/null +++ b/vendor/github.com/containers/image/v5/pkg/compression/internal/types.go @@ -0,0 +1,65 @@ +package internal + +import "io" + +// CompressorFunc writes the compressed stream to the given writer using the specified compression level. +// The caller must call Close() on the stream (even if the input stream does not need closing!). +type CompressorFunc func(io.Writer, map[string]string, *int) (io.WriteCloser, error) + +// DecompressorFunc returns the decompressed stream, given a compressed stream. +// The caller must call Close() on the decompressed stream (even if the compressed input stream does not need closing!). +type DecompressorFunc func(io.Reader) (io.ReadCloser, error) + +// Algorithm is a compression algorithm that can be used for CompressStream. +type Algorithm struct { + name string + mime string + prefix []byte // Initial bytes of a stream compressed using this algorithm, or empty to disable detection. + decompressor DecompressorFunc + compressor CompressorFunc +} + +// NewAlgorithm creates an Algorithm instance. +// This function exists so that Algorithm instances can only be created by code that +// is allowed to import this internal subpackage. +func NewAlgorithm(name, mime string, prefix []byte, decompressor DecompressorFunc, compressor CompressorFunc) Algorithm { + return Algorithm{ + name: name, + mime: mime, + prefix: prefix, + decompressor: decompressor, + compressor: compressor, + } +} + +// Name returns the name for the compression algorithm. +func (c Algorithm) Name() string { + return c.name +} + +// InternalUnstableUndocumentedMIMEQuestionMark ??? +// DO NOT USE THIS anywhere outside of c/image until it is properly documented. +func (c Algorithm) InternalUnstableUndocumentedMIMEQuestionMark() string { + return c.mime +} + +// AlgorithmCompressor returns the compressor field of algo. +// This is a function instead of a public method so that it is only callable from by code +// that is allowed to import this internal subpackage. +func AlgorithmCompressor(algo Algorithm) CompressorFunc { + return algo.compressor +} + +// AlgorithmDecompressor returns the decompressor field of algo. +// This is a function instead of a public method so that it is only callable from by code +// that is allowed to import this internal subpackage. +func AlgorithmDecompressor(algo Algorithm) DecompressorFunc { + return algo.decompressor +} + +// AlgorithmPrefix returns the prefix field of algo. +// This is a function instead of a public method so that it is only callable from by code +// that is allowed to import this internal subpackage. +func AlgorithmPrefix(algo Algorithm) []byte { + return algo.prefix +} diff --git a/vendor/github.com/containers/image/v5/pkg/compression/types/types.go b/vendor/github.com/containers/image/v5/pkg/compression/types/types.go new file mode 100644 index 0000000000..43d03b601c --- /dev/null +++ b/vendor/github.com/containers/image/v5/pkg/compression/types/types.go @@ -0,0 +1,41 @@ +package types + +import ( + "github.com/containers/image/v5/pkg/compression/internal" +) + +// DecompressorFunc returns the decompressed stream, given a compressed stream. +// The caller must call Close() on the decompressed stream (even if the compressed input stream does not need closing!). +type DecompressorFunc = internal.DecompressorFunc + +// Algorithm is a compression algorithm provided and supported by pkg/compression. +// It can’t be supplied from the outside. +type Algorithm = internal.Algorithm + +const ( + // GzipAlgorithmName is the name used by pkg/compression.Gzip. + // NOTE: Importing only this /types package does not inherently guarantee a Gzip algorithm + // will actually be available. (In fact it is intended for this types package not to depend + // on any of the implementations.) + GzipAlgorithmName = "gzip" + // Bzip2AlgorithmName is the name used by pkg/compression.Bzip2. + // NOTE: Importing only this /types package does not inherently guarantee a Bzip2 algorithm + // will actually be available. (In fact it is intended for this types package not to depend + // on any of the implementations.) + Bzip2AlgorithmName = "bzip2" + // XzAlgorithmName is the name used by pkg/compression.Xz. + // NOTE: Importing only this /types package does not inherently guarantee a Xz algorithm + // will actually be available. (In fact it is intended for this types package not to depend + // on any of the implementations.) + XzAlgorithmName = "Xz" + // ZstdAlgorithmName is the name used by pkg/compression.Zstd. + // NOTE: Importing only this /types package does not inherently guarantee a Zstd algorithm + // will actually be available. (In fact it is intended for this types package not to depend + // on any of the implementations.) + ZstdAlgorithmName = "zstd" + // ZstdChunkedAlgorithmName is the name used by pkg/compression.ZstdChunked. + // NOTE: Importing only this /types package does not inherently guarantee a ZstdChunked algorithm + // will actually be available. (In fact it is intended for this types package not to depend + // on any of the implementations.) + ZstdChunkedAlgorithmName = "zstd:chunked" +) diff --git a/vendor/github.com/containers/image/v5/pkg/docker/config/config.go b/vendor/github.com/containers/image/v5/pkg/docker/config/config.go new file mode 100644 index 0000000000..1d73dc405e --- /dev/null +++ b/vendor/github.com/containers/image/v5/pkg/docker/config/config.go @@ -0,0 +1,802 @@ +package config + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/pkg/sysregistriesv2" + "github.com/containers/image/v5/types" + "github.com/containers/storage/pkg/homedir" + helperclient "github.com/docker/docker-credential-helpers/client" + "github.com/docker/docker-credential-helpers/credentials" + "github.com/hashicorp/go-multierror" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +type dockerAuthConfig struct { + Auth string `json:"auth,omitempty"` + IdentityToken string `json:"identitytoken,omitempty"` +} + +type dockerConfigFile struct { + AuthConfigs map[string]dockerAuthConfig `json:"auths"` + CredHelpers map[string]string `json:"credHelpers,omitempty"` +} + +type authPath struct { + path string + legacyFormat bool +} + +var ( + defaultPerUIDPathFormat = filepath.FromSlash("/run/containers/%d/auth.json") + xdgConfigHomePath = filepath.FromSlash("containers/auth.json") + xdgRuntimeDirPath = filepath.FromSlash("containers/auth.json") + dockerHomePath = filepath.FromSlash(".docker/config.json") + dockerLegacyHomePath = ".dockercfg" + nonLinuxAuthFilePath = filepath.FromSlash(".config/containers/auth.json") + + // ErrNotLoggedIn is returned for users not logged into a registry + // that they are trying to logout of + ErrNotLoggedIn = errors.New("not logged in") + // ErrNotSupported is returned for unsupported methods + ErrNotSupported = errors.New("not supported") +) + +// SetCredentials stores the username and password in a location +// appropriate for sys and the users’ configuration. +// A valid key is a repository, a namespace within a registry, or a registry hostname; +// using forms other than just a registry may fail depending on configuration. +// Returns a human-redable description of the location that was updated. +// NOTE: The return value is only intended to be read by humans; its form is not an API, +// it may change (or new forms can be added) any time. +func SetCredentials(sys *types.SystemContext, key, username, password string) (string, error) { + isNamespaced, err := validateKey(key) + if err != nil { + return "", err + } + + helpers, err := sysregistriesv2.CredentialHelpers(sys) + if err != nil { + return "", err + } + + // Make sure to collect all errors. + var multiErr error + for _, helper := range helpers { + var desc string + var err error + switch helper { + // Special-case the built-in helpers for auth files. + case sysregistriesv2.AuthenticationFileHelper: + desc, err = modifyJSON(sys, func(auths *dockerConfigFile) (bool, error) { + if ch, exists := auths.CredHelpers[key]; exists { + if isNamespaced { + return false, unsupportedNamespaceErr(ch) + } + return false, setAuthToCredHelper(ch, key, username, password) + } + creds := base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) + newCreds := dockerAuthConfig{Auth: creds} + auths.AuthConfigs[key] = newCreds + return true, nil + }) + // External helpers. + default: + if isNamespaced { + err = unsupportedNamespaceErr(helper) + } else { + desc = fmt.Sprintf("credential helper: %s", helper) + err = setAuthToCredHelper(helper, key, username, password) + } + } + if err != nil { + multiErr = multierror.Append(multiErr, err) + logrus.Debugf("Error storing credentials for %s in credential helper %s: %v", key, helper, err) + continue + } + logrus.Debugf("Stored credentials for %s in credential helper %s", key, helper) + return desc, nil + } + return "", multiErr +} + +func unsupportedNamespaceErr(helper string) error { + return errors.Errorf("namespaced key is not supported for credential helper %s", helper) +} + +// SetAuthentication stores the username and password in the credential helper or file +// See the documentation of SetCredentials for format of "key" +func SetAuthentication(sys *types.SystemContext, key, username, password string) error { + _, err := SetCredentials(sys, key, username, password) + return err +} + +// GetAllCredentials returns the registry credentials for all registries stored +// in any of the configured credential helpers. +func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthConfig, error) { + // To keep things simple, let's first extract all registries from all + // possible sources, and then call `GetCredentials` on them. That + // prevents us from having to reverse engineer the logic in + // `GetCredentials`. + allKeys := make(map[string]bool) + addKey := func(s string) { + allKeys[s] = true + } + + // To use GetCredentials, we must at least convert the URL forms into host names. + // While we're at it, we’ll also canonicalize docker.io to the standard format. + normalizedDockerIORegistry := normalizeRegistry("docker.io") + + helpers, err := sysregistriesv2.CredentialHelpers(sys) + if err != nil { + return nil, err + } + for _, helper := range helpers { + switch helper { + // Special-case the built-in helper for auth files. + case sysregistriesv2.AuthenticationFileHelper: + for _, path := range getAuthFilePaths(sys, homedir.Get()) { + // readJSONFile returns an empty map in case the path doesn't exist. + auths, err := readJSONFile(path.path, path.legacyFormat) + if err != nil { + return nil, errors.Wrapf(err, "reading JSON file %q", path.path) + } + // Credential helpers in the auth file have a + // direct mapping to a registry, so we can just + // walk the map. + for registry := range auths.CredHelpers { + addKey(registry) + } + for key := range auths.AuthConfigs { + key := normalizeAuthFileKey(key, path.legacyFormat) + if key == normalizedDockerIORegistry { + key = "docker.io" + } + addKey(key) + } + } + // External helpers. + default: + creds, err := listAuthsFromCredHelper(helper) + if err != nil { + logrus.Debugf("Error listing credentials stored in credential helper %s: %v", helper, err) + } + switch errors.Cause(err) { + case nil: + for registry := range creds { + addKey(registry) + } + case exec.ErrNotFound: + // It's okay if the helper doesn't exist. + default: + return nil, err + } + } + } + + // Now use `GetCredentials` to the specific auth configs for each + // previously listed registry. + authConfigs := make(map[string]types.DockerAuthConfig) + for key := range allKeys { + authConf, err := GetCredentials(sys, key) + if err != nil { + // Note: we rely on the logging in `GetCredentials`. + return nil, err + } + if authConf != (types.DockerAuthConfig{}) { + authConfigs[key] = authConf + } + } + + return authConfigs, nil +} + +// getAuthFilePaths returns a slice of authPaths based on the system context +// in the order they should be searched. Note that some paths may not exist. +// The homeDir parameter should always be homedir.Get(), and is only intended to be overridden +// by tests. +func getAuthFilePaths(sys *types.SystemContext, homeDir string) []authPath { + paths := []authPath{} + pathToAuth, lf, err := getPathToAuth(sys) + if err == nil { + paths = append(paths, authPath{path: pathToAuth, legacyFormat: lf}) + } else { + // Error means that the path set for XDG_RUNTIME_DIR does not exist + // but we don't want to completely fail in the case that the user is pulling a public image + // Logging the error as a warning instead and moving on to pulling the image + logrus.Warnf("%v: Trying to pull image in the event that it is a public image.", err) + } + xdgCfgHome := os.Getenv("XDG_CONFIG_HOME") + if xdgCfgHome == "" { + xdgCfgHome = filepath.Join(homeDir, ".config") + } + paths = append(paths, authPath{path: filepath.Join(xdgCfgHome, xdgConfigHomePath), legacyFormat: false}) + if dockerConfig := os.Getenv("DOCKER_CONFIG"); dockerConfig != "" { + paths = append(paths, + authPath{path: filepath.Join(dockerConfig, "config.json"), legacyFormat: false}, + ) + } else { + paths = append(paths, + authPath{path: filepath.Join(homeDir, dockerHomePath), legacyFormat: false}, + ) + } + paths = append(paths, + authPath{path: filepath.Join(homeDir, dockerLegacyHomePath), legacyFormat: true}, + ) + return paths +} + +// GetCredentials returns the registry credentials matching key, appropriate for +// sys and the users’ configuration. +// If an entry is not found, an empty struct is returned. +// A valid key is a repository, a namespace within a registry, or a registry hostname. +// +// GetCredentialsForRef should almost always be used in favor of this API. +func GetCredentials(sys *types.SystemContext, key string) (types.DockerAuthConfig, error) { + return getCredentialsWithHomeDir(sys, key, homedir.Get()) +} + +// GetCredentialsForRef returns the registry credentials necessary for +// accessing ref on the registry ref points to, +// appropriate for sys and the users’ configuration. +// If an entry is not found, an empty struct is returned. +func GetCredentialsForRef(sys *types.SystemContext, ref reference.Named) (types.DockerAuthConfig, error) { + return getCredentialsWithHomeDir(sys, ref.Name(), homedir.Get()) +} + +// getCredentialsWithHomeDir is an internal implementation detail of +// GetCredentialsForRef and GetCredentials. It exists only to allow testing it +// with an artificial home directory. +func getCredentialsWithHomeDir(sys *types.SystemContext, key, homeDir string) (types.DockerAuthConfig, error) { + _, err := validateKey(key) + if err != nil { + return types.DockerAuthConfig{}, err + } + + if sys != nil && sys.DockerAuthConfig != nil { + logrus.Debugf("Returning credentials for %s from DockerAuthConfig", key) + return *sys.DockerAuthConfig, nil + } + + var registry string // We compute this once because it is used in several places. + if firstSlash := strings.IndexRune(key, '/'); firstSlash != -1 { + registry = key[:firstSlash] + } else { + registry = key + } + + // Anonymous function to query credentials from auth files. + getCredentialsFromAuthFiles := func() (types.DockerAuthConfig, string, error) { + for _, path := range getAuthFilePaths(sys, homeDir) { + authConfig, err := findCredentialsInFile(key, registry, path.path, path.legacyFormat) + if err != nil { + return types.DockerAuthConfig{}, "", err + } + + if authConfig != (types.DockerAuthConfig{}) { + return authConfig, path.path, nil + } + } + return types.DockerAuthConfig{}, "", nil + } + + helpers, err := sysregistriesv2.CredentialHelpers(sys) + if err != nil { + return types.DockerAuthConfig{}, err + } + + var multiErr error + for _, helper := range helpers { + var ( + creds types.DockerAuthConfig + helperKey string + credHelperPath string + err error + ) + switch helper { + // Special-case the built-in helper for auth files. + case sysregistriesv2.AuthenticationFileHelper: + helperKey = key + creds, credHelperPath, err = getCredentialsFromAuthFiles() + // External helpers. + default: + // This intentionally uses "registry", not "key"; we don't support namespaced + // credentials in helpers, but a "registry" is a valid parent of "key". + helperKey = registry + creds, err = getAuthFromCredHelper(helper, registry) + } + if err != nil { + logrus.Debugf("Error looking up credentials for %s in credential helper %s: %v", helperKey, helper, err) + multiErr = multierror.Append(multiErr, err) + continue + } + if creds != (types.DockerAuthConfig{}) { + msg := fmt.Sprintf("Found credentials for %s in credential helper %s", helperKey, helper) + if credHelperPath != "" { + msg = fmt.Sprintf("%s in file %s", msg, credHelperPath) + } + logrus.Debug(msg) + return creds, nil + } + } + if multiErr != nil { + return types.DockerAuthConfig{}, multiErr + } + + logrus.Debugf("No credentials for %s found", key) + return types.DockerAuthConfig{}, nil +} + +// GetAuthentication returns the registry credentials matching key, appropriate for +// sys and the users’ configuration. +// If an entry is not found, an empty struct is returned. +// A valid key is a repository, a namespace within a registry, or a registry hostname. +// +// Deprecated: This API only has support for username and password. To get the +// support for oauth2 in container registry authentication, we added the new +// GetCredentialsForRef and GetCredentials API. The new API should be used and this API is kept to +// maintain backward compatibility. +func GetAuthentication(sys *types.SystemContext, key string) (string, string, error) { + return getAuthenticationWithHomeDir(sys, key, homedir.Get()) +} + +// getAuthenticationWithHomeDir is an internal implementation detail of GetAuthentication, +// it exists only to allow testing it with an artificial home directory. +func getAuthenticationWithHomeDir(sys *types.SystemContext, key, homeDir string) (string, string, error) { + auth, err := getCredentialsWithHomeDir(sys, key, homeDir) + if err != nil { + return "", "", err + } + if auth.IdentityToken != "" { + return "", "", errors.Wrap(ErrNotSupported, "non-empty identity token found and this API doesn't support it") + } + return auth.Username, auth.Password, nil +} + +// RemoveAuthentication removes credentials for `key` from all possible +// sources such as credential helpers and auth files. +// A valid key is a repository, a namespace within a registry, or a registry hostname; +// using forms other than just a registry may fail depending on configuration. +func RemoveAuthentication(sys *types.SystemContext, key string) error { + isNamespaced, err := validateKey(key) + if err != nil { + return err + } + + helpers, err := sysregistriesv2.CredentialHelpers(sys) + if err != nil { + return err + } + + var multiErr error + isLoggedIn := false + + removeFromCredHelper := func(helper string) { + if isNamespaced { + logrus.Debugf("Not removing credentials because namespaced keys are not supported for the credential helper: %s", helper) + return + } else { + err := deleteAuthFromCredHelper(helper, key) + if err == nil { + logrus.Debugf("Credentials for %q were deleted from credential helper %s", key, helper) + isLoggedIn = true + return + } + if credentials.IsErrCredentialsNotFoundMessage(err.Error()) { + logrus.Debugf("Not logged in to %s with credential helper %s", key, helper) + return + } + } + multiErr = multierror.Append(multiErr, errors.Wrapf(err, "removing credentials for %s from credential helper %s", key, helper)) + } + + for _, helper := range helpers { + var err error + switch helper { + // Special-case the built-in helper for auth files. + case sysregistriesv2.AuthenticationFileHelper: + _, err = modifyJSON(sys, func(auths *dockerConfigFile) (bool, error) { + if innerHelper, exists := auths.CredHelpers[key]; exists { + removeFromCredHelper(innerHelper) + } + if _, ok := auths.AuthConfigs[key]; ok { + isLoggedIn = true + delete(auths.AuthConfigs, key) + } + return true, multiErr + }) + if err != nil { + multiErr = multierror.Append(multiErr, err) + } + // External helpers. + default: + removeFromCredHelper(helper) + } + } + + if multiErr != nil { + return multiErr + } + if !isLoggedIn { + return ErrNotLoggedIn + } + + return nil +} + +// RemoveAllAuthentication deletes all the credentials stored in credential +// helpers and auth files. +func RemoveAllAuthentication(sys *types.SystemContext) error { + helpers, err := sysregistriesv2.CredentialHelpers(sys) + if err != nil { + return err + } + + var multiErr error + for _, helper := range helpers { + var err error + switch helper { + // Special-case the built-in helper for auth files. + case sysregistriesv2.AuthenticationFileHelper: + _, err = modifyJSON(sys, func(auths *dockerConfigFile) (bool, error) { + for registry, helper := range auths.CredHelpers { + // Helpers in auth files are expected + // to exist, so no special treatment + // for them. + if err := deleteAuthFromCredHelper(helper, registry); err != nil { + return false, err + } + } + auths.CredHelpers = make(map[string]string) + auths.AuthConfigs = make(map[string]dockerAuthConfig) + return true, nil + }) + // External helpers. + default: + var creds map[string]string + creds, err = listAuthsFromCredHelper(helper) + switch errors.Cause(err) { + case nil: + for registry := range creds { + err = deleteAuthFromCredHelper(helper, registry) + if err != nil { + break + } + } + case exec.ErrNotFound: + // It's okay if the helper doesn't exist. + continue + default: + // fall through + } + } + if err != nil { + logrus.Debugf("Error removing credentials from credential helper %s: %v", helper, err) + multiErr = multierror.Append(multiErr, err) + continue + } + logrus.Debugf("All credentials removed from credential helper %s", helper) + } + + return multiErr +} + +func listAuthsFromCredHelper(credHelper string) (map[string]string, error) { + helperName := fmt.Sprintf("docker-credential-%s", credHelper) + p := helperclient.NewShellProgramFunc(helperName) + return helperclient.List(p) +} + +// getPathToAuth gets the path of the auth.json file used for reading and writing credentials +// returns the path, and a bool specifies whether the file is in legacy format +func getPathToAuth(sys *types.SystemContext) (string, bool, error) { + return getPathToAuthWithOS(sys, runtime.GOOS) +} + +// getPathToAuthWithOS is an internal implementation detail of getPathToAuth, +// it exists only to allow testing it with an artificial runtime.GOOS. +func getPathToAuthWithOS(sys *types.SystemContext, goOS string) (string, bool, error) { + if sys != nil { + if sys.AuthFilePath != "" { + return sys.AuthFilePath, false, nil + } + if sys.LegacyFormatAuthFilePath != "" { + return sys.LegacyFormatAuthFilePath, true, nil + } + if sys.RootForImplicitAbsolutePaths != "" { + return filepath.Join(sys.RootForImplicitAbsolutePaths, fmt.Sprintf(defaultPerUIDPathFormat, os.Getuid())), false, nil + } + } + if goOS == "windows" || goOS == "darwin" { + return filepath.Join(homedir.Get(), nonLinuxAuthFilePath), false, nil + } + + runtimeDir := os.Getenv("XDG_RUNTIME_DIR") + if runtimeDir != "" { + // This function does not in general need to separately check that the returned path exists; that’s racy, and callers will fail accessing the file anyway. + // We are checking for os.IsNotExist here only to give the user better guidance what to do in this special case. + _, err := os.Stat(runtimeDir) + if os.IsNotExist(err) { + // This means the user set the XDG_RUNTIME_DIR variable and either forgot to create the directory + // or made a typo while setting the environment variable, + // so return an error referring to $XDG_RUNTIME_DIR instead of xdgRuntimeDirPath inside. + return "", false, errors.Wrapf(err, "%q directory set by $XDG_RUNTIME_DIR does not exist. Either create the directory or unset $XDG_RUNTIME_DIR.", runtimeDir) + } // else ignore err and let the caller fail accessing xdgRuntimeDirPath. + return filepath.Join(runtimeDir, xdgRuntimeDirPath), false, nil + } + return fmt.Sprintf(defaultPerUIDPathFormat, os.Getuid()), false, nil +} + +// readJSONFile unmarshals the authentications stored in the auth.json file and returns it +// or returns an empty dockerConfigFile data structure if auth.json does not exist +// if the file exists and is empty, readJSONFile returns an error +func readJSONFile(path string, legacyFormat bool) (dockerConfigFile, error) { + var auths dockerConfigFile + + raw, err := ioutil.ReadFile(path) + if err != nil { + if os.IsNotExist(err) { + auths.AuthConfigs = map[string]dockerAuthConfig{} + return auths, nil + } + return dockerConfigFile{}, err + } + + if legacyFormat { + if err = json.Unmarshal(raw, &auths.AuthConfigs); err != nil { + return dockerConfigFile{}, errors.Wrapf(err, "unmarshaling JSON at %q", path) + } + return auths, nil + } + + if err = json.Unmarshal(raw, &auths); err != nil { + return dockerConfigFile{}, errors.Wrapf(err, "unmarshaling JSON at %q", path) + } + + if auths.AuthConfigs == nil { + auths.AuthConfigs = map[string]dockerAuthConfig{} + } + if auths.CredHelpers == nil { + auths.CredHelpers = make(map[string]string) + } + + return auths, nil +} + +// modifyJSON finds an auth.json file, calls editor on the contents, and +// writes it back if editor returns true. +// Returns a human-redable description of the file, to be returned by SetCredentials. +func modifyJSON(sys *types.SystemContext, editor func(auths *dockerConfigFile) (bool, error)) (string, error) { + path, legacyFormat, err := getPathToAuth(sys) + if err != nil { + return "", err + } + if legacyFormat { + return "", fmt.Errorf("writes to %s using legacy format are not supported", path) + } + + dir := filepath.Dir(path) + if err = os.MkdirAll(dir, 0700); err != nil { + return "", err + } + + auths, err := readJSONFile(path, false) + if err != nil { + return "", errors.Wrapf(err, "reading JSON file %q", path) + } + + updated, err := editor(&auths) + if err != nil { + return "", errors.Wrapf(err, "updating %q", path) + } + if updated { + newData, err := json.MarshalIndent(auths, "", "\t") + if err != nil { + return "", errors.Wrapf(err, "marshaling JSON %q", path) + } + + if err = ioutil.WriteFile(path, newData, 0600); err != nil { + return "", errors.Wrapf(err, "writing to file %q", path) + } + } + + return path, nil +} + +func getAuthFromCredHelper(credHelper, registry string) (types.DockerAuthConfig, error) { + helperName := fmt.Sprintf("docker-credential-%s", credHelper) + p := helperclient.NewShellProgramFunc(helperName) + creds, err := helperclient.Get(p, registry) + if err != nil { + if credentials.IsErrCredentialsNotFoundMessage(err.Error()) { + logrus.Debugf("Not logged in to %s with credential helper %s", registry, credHelper) + err = nil + } + return types.DockerAuthConfig{}, err + } + + switch creds.Username { + case "": + return types.DockerAuthConfig{ + IdentityToken: creds.Secret, + }, nil + default: + return types.DockerAuthConfig{ + Username: creds.Username, + Password: creds.Secret, + }, nil + } +} + +func setAuthToCredHelper(credHelper, registry, username, password string) error { + helperName := fmt.Sprintf("docker-credential-%s", credHelper) + p := helperclient.NewShellProgramFunc(helperName) + creds := &credentials.Credentials{ + ServerURL: registry, + Username: username, + Secret: password, + } + return helperclient.Store(p, creds) +} + +func deleteAuthFromCredHelper(credHelper, registry string) error { + helperName := fmt.Sprintf("docker-credential-%s", credHelper) + p := helperclient.NewShellProgramFunc(helperName) + return helperclient.Erase(p, registry) +} + +// findCredentialsInFile looks for credentials matching "key" +// (which is "registry" or a namespace in "registry") in "path". +func findCredentialsInFile(key, registry, path string, legacyFormat bool) (types.DockerAuthConfig, error) { + auths, err := readJSONFile(path, legacyFormat) + if err != nil { + return types.DockerAuthConfig{}, errors.Wrapf(err, "reading JSON file %q", path) + } + + // First try cred helpers. They should always be normalized. + // This intentionally uses "registry", not "key"; we don't support namespaced + // credentials in helpers. + if ch, exists := auths.CredHelpers[registry]; exists { + logrus.Debugf("Looking up in credential helper %s based on credHelpers entry in %s", ch, path) + return getAuthFromCredHelper(ch, registry) + } + + // Support sub-registry namespaces in auth. + // (This is not a feature of ~/.docker/config.json; we support it even for + // those files as an extension.) + var keys []string + if !legacyFormat { + keys = authKeysForKey(key) + } else { + keys = []string{registry} + } + + // Repo or namespace keys are only supported as exact matches. For registry + // keys we prefer exact matches as well. + for _, key := range keys { + if val, exists := auths.AuthConfigs[key]; exists { + return decodeDockerAuth(val) + } + } + + // bad luck; let's normalize the entries first + // This primarily happens for legacyFormat, which for a time used API URLs + // (http[s:]//…/v1/) as keys. + // Secondarily, (docker login) accepted URLs with no normalization for + // several years, and matched registry hostnames against that, so support + // those entries even in non-legacyFormat ~/.docker/config.json. + // The docker.io registry still uses the /v1/ key with a special host name, + // so account for that as well. + registry = normalizeRegistry(registry) + for k, v := range auths.AuthConfigs { + if normalizeAuthFileKey(k, legacyFormat) == registry { + return decodeDockerAuth(v) + } + } + + // Only log this if we found nothing; getCredentialsWithHomeDir logs the + // source of found data. + logrus.Debugf("No credentials matching %s found in %s", key, path) + return types.DockerAuthConfig{}, nil +} + +// authKeysForKey returns the keys matching a provided auth file key, in order +// from the best match to worst. For example, +// when given a repository key "quay.io/repo/ns/image", it returns +// - quay.io/repo/ns/image +// - quay.io/repo/ns +// - quay.io/repo +// - quay.io +func authKeysForKey(key string) (res []string) { + for { + res = append(res, key) + + lastSlash := strings.LastIndex(key, "/") + if lastSlash == -1 { + break + } + key = key[:lastSlash] + } + + return res +} + +// decodeDockerAuth decodes the username and password, which is +// encoded in base64. +func decodeDockerAuth(conf dockerAuthConfig) (types.DockerAuthConfig, error) { + decoded, err := base64.StdEncoding.DecodeString(conf.Auth) + if err != nil { + return types.DockerAuthConfig{}, err + } + + parts := strings.SplitN(string(decoded), ":", 2) + if len(parts) != 2 { + // if it's invalid just skip, as docker does + return types.DockerAuthConfig{}, nil + } + + user := parts[0] + password := strings.Trim(parts[1], "\x00") + return types.DockerAuthConfig{ + Username: user, + Password: password, + IdentityToken: conf.IdentityToken, + }, nil +} + +// normalizeAuthFileKey takes a key, converts it to a host name and normalizes +// the resulting registry. +func normalizeAuthFileKey(key string, legacyFormat bool) string { + stripped := strings.TrimPrefix(key, "http://") + stripped = strings.TrimPrefix(stripped, "https://") + + if legacyFormat || stripped != key { + stripped = strings.SplitN(stripped, "/", 2)[0] + } + + return normalizeRegistry(stripped) +} + +// normalizeRegistry converts the provided registry if a known docker.io host +// is provided. +func normalizeRegistry(registry string) string { + switch registry { + case "registry-1.docker.io", "docker.io": + return "index.docker.io" + } + return registry +} + +// validateKey verifies that the input key does not have a prefix that is not +// allowed and returns an indicator if the key is namespaced. +func validateKey(key string) (bool, error) { + if strings.HasPrefix(key, "http://") || strings.HasPrefix(key, "https://") { + return false, errors.Errorf("key %s contains http[s]:// prefix", key) + } + + // Ideally this should only accept explicitly valid keys, compare + // validateIdentityRemappingPrefix. For now, just reject values that look + // like tagged or digested values. + if strings.ContainsRune(key, '@') { + return false, fmt.Errorf(`key %s contains a '@' character`, key) + } + + firstSlash := strings.IndexRune(key, '/') + isNamespaced := firstSlash != -1 + // Reject host/repo:tag, but allow localhost:5000 and localhost:5000/foo. + if isNamespaced && strings.ContainsRune(key[firstSlash+1:], ':') { + return false, fmt.Errorf(`key %s contains a ':' character after host[:port]`, key) + } + // check if the provided key contains one or more subpaths. + return isNamespaced, nil +} diff --git a/vendor/github.com/containers/image/v5/pkg/docker/config/config_linux.go b/vendor/github.com/containers/image/v5/pkg/docker/config/config_linux.go new file mode 100644 index 0000000000..0bf1612591 --- /dev/null +++ b/vendor/github.com/containers/image/v5/pkg/docker/config/config_linux.go @@ -0,0 +1,119 @@ +package config + +import ( + "fmt" + "strings" + + "github.com/containers/image/v5/internal/pkg/keyctl" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// NOTE: none of the functions here are currently used. If we ever want to +// re-enable keyring support, we should introduce a similar built-in credential +// helpers as for `sysregistriesv2.AuthenticationFileHelper`. + +const keyDescribePrefix = "container-registry-login:" //nolint:deadcode,unused + +func getAuthFromKernelKeyring(registry string) (string, string, error) { //nolint:deadcode,unused + userkeyring, err := keyctl.UserKeyring() + if err != nil { + return "", "", err + } + key, err := userkeyring.Search(genDescription(registry)) + if err != nil { + return "", "", err + } + authData, err := key.Get() + if err != nil { + return "", "", err + } + parts := strings.SplitN(string(authData), "\x00", 2) + if len(parts) != 2 { + return "", "", nil + } + return parts[0], parts[1], nil +} + +func deleteAuthFromKernelKeyring(registry string) error { //nolint:deadcode,unused + userkeyring, err := keyctl.UserKeyring() + + if err != nil { + return err + } + key, err := userkeyring.Search(genDescription(registry)) + if err != nil { + return err + } + return key.Unlink() +} + +func removeAllAuthFromKernelKeyring() error { //nolint:deadcode,unused + keys, err := keyctl.ReadUserKeyring() + if err != nil { + return err + } + + userkeyring, err := keyctl.UserKeyring() + if err != nil { + return err + } + + for _, k := range keys { + keyAttr, err := k.Describe() + if err != nil { + return err + } + // split string "type;uid;gid;perm;description" + keyAttrs := strings.SplitN(keyAttr, ";", 5) + if len(keyAttrs) < 5 { + return errors.Errorf("Key attributes of %d are not available", k.ID()) + } + keyDescribe := keyAttrs[4] + if strings.HasPrefix(keyDescribe, keyDescribePrefix) { + err := keyctl.Unlink(userkeyring, k) + if err != nil { + return errors.Wrapf(err, "unlinking key %d", k.ID()) + } + logrus.Debugf("unlinked key %d:%s", k.ID(), keyAttr) + } + } + return nil +} + +func setAuthToKernelKeyring(registry, username, password string) error { //nolint:deadcode,unused + keyring, err := keyctl.SessionKeyring() + if err != nil { + return err + } + id, err := keyring.Add(genDescription(registry), []byte(fmt.Sprintf("%s\x00%s", username, password))) + if err != nil { + return err + } + + // sets all permission(view,read,write,search,link,set attribute) for current user + // it enables the user to search the key after it linked to user keyring and unlinked from session keyring + err = keyctl.SetPerm(id, keyctl.PermUserAll) + if err != nil { + return err + } + // link the key to userKeyring + userKeyring, err := keyctl.UserKeyring() + if err != nil { + return errors.Wrapf(err, "getting user keyring") + } + err = keyctl.Link(userKeyring, id) + if err != nil { + return errors.Wrapf(err, "linking the key to user keyring") + } + // unlink the key from session keyring + err = keyctl.Unlink(keyring, id) + if err != nil { + return errors.Wrapf(err, "unlinking the key from session keyring") + } + return nil +} + +func genDescription(registry string) string { //nolint:deadcode,unused + return fmt.Sprintf("%s%s", keyDescribePrefix, registry) +} diff --git a/vendor/github.com/containers/image/v5/pkg/docker/config/config_unsupported.go b/vendor/github.com/containers/image/v5/pkg/docker/config/config_unsupported.go new file mode 100644 index 0000000000..d9827d8edb --- /dev/null +++ b/vendor/github.com/containers/image/v5/pkg/docker/config/config_unsupported.go @@ -0,0 +1,21 @@ +//go:build !linux && (!386 || !amd64) +// +build !linux +// +build !386 !amd64 + +package config + +func getAuthFromKernelKeyring(registry string) (string, string, error) { //nolint:deadcode,unused + return "", "", ErrNotSupported +} + +func deleteAuthFromKernelKeyring(registry string) error { //nolint:deadcode,unused + return ErrNotSupported +} + +func setAuthToKernelKeyring(registry, username, password string) error { //nolint:deadcode,unused + return ErrNotSupported +} + +func removeAllAuthFromKernelKeyring() error { //nolint:deadcode,unused + return ErrNotSupported +} diff --git a/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/shortnames.go b/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/shortnames.go new file mode 100644 index 0000000000..7122e869fe --- /dev/null +++ b/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/shortnames.go @@ -0,0 +1,347 @@ +package sysregistriesv2 + +import ( + "os" + "path/filepath" + "reflect" + "strings" + + "github.com/BurntSushi/toml" + "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/internal/rootless" + "github.com/containers/image/v5/types" + "github.com/containers/storage/pkg/homedir" + "github.com/containers/storage/pkg/lockfile" + "github.com/pkg/errors" +) + +// defaultShortNameMode is the default mode of registries.conf files if the +// corresponding field is left empty. +const defaultShortNameMode = types.ShortNameModePermissive + +// userShortNamesFile is the user-specific config file to store aliases. +var userShortNamesFile = filepath.FromSlash("containers/short-name-aliases.conf") + +// shortNameAliasesConfPath returns the path to the machine-generated +// short-name-aliases.conf file. +func shortNameAliasesConfPath(ctx *types.SystemContext) (string, error) { + if ctx != nil && len(ctx.UserShortNameAliasConfPath) > 0 { + return ctx.UserShortNameAliasConfPath, nil + } + + if rootless.GetRootlessEUID() == 0 { + // Root user or in a non-conforming user NS + return filepath.Join("/var/cache", userShortNamesFile), nil + } + + // Rootless user + cacheRoot, err := homedir.GetCacheHome() + if err != nil { + return "", err + } + + return filepath.Join(cacheRoot, userShortNamesFile), nil +} + +// shortNameAliasConf is a subset of the `V2RegistriesConf` format. It's used in the +// software-maintained `userShortNamesFile`. +type shortNameAliasConf struct { + // A map for aliasing short names to their fully-qualified image + // reference counter parts. + // Note that Aliases is niled after being loaded from a file. + Aliases map[string]string `toml:"aliases"` + + // If you add any field, make sure to update nonempty() below. +} + +// nonempty returns true if config contains at least one configuration entry. +func (c *shortNameAliasConf) nonempty() bool { + copy := *c // A shallow copy + if copy.Aliases != nil && len(copy.Aliases) == 0 { + copy.Aliases = nil + } + return !reflect.DeepEqual(copy, shortNameAliasConf{}) +} + +// alias combines the parsed value of an alias with the config file it has been +// specified in. The config file is crucial for an improved user experience +// such that users are able to resolve potential pull errors. +type alias struct { + // The parsed value of an alias. May be nil if set to "" in a config. + value reference.Named + // The config file the alias originates from. + configOrigin string +} + +// shortNameAliasCache is the result of parsing shortNameAliasConf, +// pre-processed for faster usage. +type shortNameAliasCache struct { + // Note that an alias value may be nil iff it's set as an empty string + // in the config. + namedAliases map[string]alias +} + +// ResolveShortNameAlias performs an alias resolution of the specified name. +// The user-specific short-name-aliases.conf has precedence over aliases in the +// assembled registries.conf. It returns the possibly resolved alias or nil, a +// human-readable description of the config where the alias is specified, and +// an error. The origin of the config file is crucial for an improved user +// experience such that users are able to resolve potential pull errors. +// Almost all callers should use pkg/shortnames instead. +// +// Note that it’s the caller’s responsibility to pass only a repository +// (reference.IsNameOnly) as the short name. +func ResolveShortNameAlias(ctx *types.SystemContext, name string) (reference.Named, string, error) { + if err := validateShortName(name); err != nil { + return nil, "", err + } + confPath, lock, err := shortNameAliasesConfPathAndLock(ctx) + if err != nil { + return nil, "", err + } + + // Acquire the lock as a reader to allow for multiple routines in the + // same process space to read simultaneously. + lock.RLock() + defer lock.Unlock() + + _, aliasCache, err := loadShortNameAliasConf(confPath) + if err != nil { + return nil, "", err + } + + // First look up the short-name-aliases.conf. Note that a value may be + // nil iff it's set as an empty string in the config. + alias, resolved := aliasCache.namedAliases[name] + if resolved { + return alias.value, alias.configOrigin, nil + } + + config, err := getConfig(ctx) + if err != nil { + return nil, "", err + } + alias, resolved = config.aliasCache.namedAliases[name] + if resolved { + return alias.value, alias.configOrigin, nil + } + return nil, "", nil +} + +// editShortNameAlias loads the aliases.conf file and changes it. If value is +// set, it adds the name-value pair as a new alias. Otherwise, it will remove +// name from the config. +func editShortNameAlias(ctx *types.SystemContext, name string, value *string) error { + if err := validateShortName(name); err != nil { + return err + } + if value != nil { + if _, err := parseShortNameValue(*value); err != nil { + return err + } + } + + confPath, lock, err := shortNameAliasesConfPathAndLock(ctx) + if err != nil { + return err + } + + // Acquire the lock as a writer to prevent data corruption. + lock.Lock() + defer lock.Unlock() + + // Load the short-name-alias.conf, add the specified name-value pair, + // and write it back to the file. + conf, _, err := loadShortNameAliasConf(confPath) + if err != nil { + return err + } + + if conf.Aliases == nil { // Ensure we have a map to update. + conf.Aliases = make(map[string]string) + } + if value != nil { + conf.Aliases[name] = *value + } else { + // If the name does not exist, throw an error. + if _, exists := conf.Aliases[name]; !exists { + return errors.Errorf("short-name alias %q not found in %q: please check registries.conf files", name, confPath) + } + + delete(conf.Aliases, name) + } + + f, err := os.OpenFile(confPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return err + } + defer f.Close() + + encoder := toml.NewEncoder(f) + return encoder.Encode(conf) +} + +// AddShortNameAlias adds the specified name-value pair as a new alias to the +// user-specific aliases.conf. It may override an existing alias for `name`. +// +// Note that it’s the caller’s responsibility to pass only a repository +// (reference.IsNameOnly) as the short name. +func AddShortNameAlias(ctx *types.SystemContext, name string, value string) error { + return editShortNameAlias(ctx, name, &value) +} + +// RemoveShortNameAlias clears the alias for the specified name. It throws an +// error in case name does not exist in the machine-generated +// short-name-alias.conf. In such case, the alias must be specified in one of +// the registries.conf files, which is the users' responsibility. +// +// Note that it’s the caller’s responsibility to pass only a repository +// (reference.IsNameOnly) as the short name. +func RemoveShortNameAlias(ctx *types.SystemContext, name string) error { + return editShortNameAlias(ctx, name, nil) +} + +// parseShortNameValue parses the specified alias into a reference.Named. The alias is +// expected to not be tagged or carry a digest and *must* include a +// domain/registry. +// +// Note that the returned reference is always normalized. +func parseShortNameValue(alias string) (reference.Named, error) { + ref, err := reference.Parse(alias) + if err != nil { + return nil, errors.Wrapf(err, "parsing alias %q", alias) + } + + if _, ok := ref.(reference.Digested); ok { + return nil, errors.Errorf("invalid alias %q: must not contain digest", alias) + } + + if _, ok := ref.(reference.Tagged); ok { + return nil, errors.Errorf("invalid alias %q: must not contain tag", alias) + } + + named, ok := ref.(reference.Named) + if !ok { + return nil, errors.Errorf("invalid alias %q: must contain registry and repository", alias) + } + + registry := reference.Domain(named) + if !(strings.ContainsAny(registry, ".:") || registry == "localhost") { + return nil, errors.Errorf("invalid alias %q: must contain registry and repository", alias) + } + + // A final parse to make sure that docker.io references are correctly + // normalized (e.g., docker.io/alpine to docker.io/library/alpine. + named, err = reference.ParseNormalizedNamed(alias) + return named, err +} + +// validateShortName parses the specified `name` of an alias (i.e., the left-hand +// side) and checks if it's a short name and does not include a tag or digest. +func validateShortName(name string) error { + repo, err := reference.Parse(name) + if err != nil { + return errors.Wrapf(err, "cannot parse short name: %q", name) + } + + if _, ok := repo.(reference.Digested); ok { + return errors.Errorf("invalid short name %q: must not contain digest", name) + } + + if _, ok := repo.(reference.Tagged); ok { + return errors.Errorf("invalid short name %q: must not contain tag", name) + } + + named, ok := repo.(reference.Named) + if !ok { + return errors.Errorf("invalid short name %q: no name", name) + } + + registry := reference.Domain(named) + if strings.ContainsAny(registry, ".:") || registry == "localhost" { + return errors.Errorf("invalid short name %q: must not contain registry", name) + } + return nil +} + +// newShortNameAliasCache parses shortNameAliasConf and returns the corresponding internal +// representation. +func newShortNameAliasCache(path string, conf *shortNameAliasConf) (*shortNameAliasCache, error) { + res := shortNameAliasCache{ + namedAliases: make(map[string]alias), + } + errs := []error{} + for name, value := range conf.Aliases { + if err := validateShortName(name); err != nil { + errs = append(errs, err) + } + + // Empty right-hand side values in config files allow to reset + // an alias in a previously loaded config. This way, drop-in + // config files from registries.conf.d can reset potentially + // malconfigured aliases. + if value == "" { + res.namedAliases[name] = alias{nil, path} + continue + } + + named, err := parseShortNameValue(value) + if err != nil { + // We want to report *all* malformed entries to avoid a + // whack-a-mole for the user. + errs = append(errs, err) + } else { + res.namedAliases[name] = alias{named, path} + } + } + if len(errs) > 0 { + err := errs[0] + for i := 1; i < len(errs); i++ { + err = errors.Wrapf(err, "%v\n", errs[i]) + } + return nil, err + } + return &res, nil +} + +// updateWithConfigurationFrom updates c with configuration from updates. +// In case of conflict, updates is preferred. +func (c *shortNameAliasCache) updateWithConfigurationFrom(updates *shortNameAliasCache) { + for name, value := range updates.namedAliases { + c.namedAliases[name] = value + } +} + +func loadShortNameAliasConf(confPath string) (*shortNameAliasConf, *shortNameAliasCache, error) { + conf := shortNameAliasConf{} + + _, err := toml.DecodeFile(confPath, &conf) + if err != nil && !os.IsNotExist(err) { + // It's okay if the config doesn't exist. Other errors are not. + return nil, nil, errors.Wrapf(err, "loading short-name aliases config file %q", confPath) + } + + // Even if we don’t always need the cache, doing so validates the machine-generated config. The + // file could still be corrupted by another process or user. + cache, err := newShortNameAliasCache(confPath, &conf) + if err != nil { + return nil, nil, errors.Wrapf(err, "loading short-name aliases config file %q", confPath) + } + + return &conf, cache, nil +} + +func shortNameAliasesConfPathAndLock(ctx *types.SystemContext) (string, lockfile.Locker, error) { + shortNameAliasesConfPath, err := shortNameAliasesConfPath(ctx) + if err != nil { + return "", nil, err + } + // Make sure the path to file exists. + if err := os.MkdirAll(filepath.Dir(shortNameAliasesConfPath), 0700); err != nil { + return "", nil, err + } + + lockPath := shortNameAliasesConfPath + ".lock" + locker, err := lockfile.GetLockfile(lockPath) + return shortNameAliasesConfPath, locker, err +} diff --git a/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/system_registries_v2.go b/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/system_registries_v2.go new file mode 100644 index 0000000000..c8a603c4ef --- /dev/null +++ b/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/system_registries_v2.go @@ -0,0 +1,1000 @@ +package sysregistriesv2 + +import ( + "fmt" + "os" + "path/filepath" + "reflect" + "regexp" + "sort" + "strings" + "sync" + + "github.com/BurntSushi/toml" + "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/types" + "github.com/containers/storage/pkg/homedir" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// systemRegistriesConfPath is the path to the system-wide registry +// configuration file and is used to add/subtract potential registries for +// obtaining images. You can override this at build time with +// -ldflags '-X github.com/containers/image/v5/sysregistries.systemRegistriesConfPath=$your_path' +var systemRegistriesConfPath = builtinRegistriesConfPath + +// builtinRegistriesConfPath is the path to the registry configuration file. +// DO NOT change this, instead see systemRegistriesConfPath above. +const builtinRegistriesConfPath = "/etc/containers/registries.conf" + +// systemRegistriesConfDirPath is the path to the system-wide registry +// configuration directory and is used to add/subtract potential registries for +// obtaining images. You can override this at build time with +// -ldflags '-X github.com/containers/image/v5/sysregistries.systemRegistriesConfDirectoryPath=$your_path' +var systemRegistriesConfDirPath = builtinRegistriesConfDirPath + +// builtinRegistriesConfDirPath is the path to the registry configuration directory. +// DO NOT change this, instead see systemRegistriesConfDirectoryPath above. +const builtinRegistriesConfDirPath = "/etc/containers/registries.conf.d" + +// AuthenticationFileHelper is a special key for credential helpers indicating +// the usage of consulting containers-auth.json files instead of a credential +// helper. +const AuthenticationFileHelper = "containers-auth.json" + +// Endpoint describes a remote location of a registry. +type Endpoint struct { + // The endpoint's remote location. Can be empty iff Prefix contains + // wildcard in the format: "*.example.com" for subdomain matching. + // Please refer to FindRegistry / PullSourcesFromReference instead + // of accessing/interpreting `Location` directly. + Location string `toml:"location,omitempty"` + // If true, certs verification will be skipped and HTTP (non-TLS) + // connections will be allowed. + Insecure bool `toml:"insecure,omitempty"` +} + +// userRegistriesFile is the path to the per user registry configuration file. +var userRegistriesFile = filepath.FromSlash(".config/containers/registries.conf") + +// userRegistriesDir is the path to the per user registry configuration file. +var userRegistriesDir = filepath.FromSlash(".config/containers/registries.conf.d") + +// rewriteReference will substitute the provided reference `prefix` to the +// endpoints `location` from the `ref` and creates a new named reference from it. +// The function errors if the newly created reference is not parsable. +func (e *Endpoint) rewriteReference(ref reference.Named, prefix string) (reference.Named, error) { + refString := ref.String() + var newNamedRef string + // refMatchingPrefix returns the length of the match. Everything that + // follows the match gets appended to registries location. + prefixLen := refMatchingPrefix(refString, prefix) + if prefixLen == -1 { + return nil, fmt.Errorf("invalid prefix '%v' for reference '%v'", prefix, refString) + } + // In the case of an empty `location` field, simply return the original + // input ref as-is. + // + // FIXME: already validated in postProcessRegistries, so check can probably + // be dropped. + // https://github.com/containers/image/pull/1191#discussion_r610621608 + if e.Location == "" { + if !strings.HasPrefix(prefix, "*.") { + return nil, fmt.Errorf("invalid prefix '%v' for empty location, should be in the format: *.example.com", prefix) + } + return ref, nil + } + newNamedRef = e.Location + refString[prefixLen:] + newParsedRef, err := reference.ParseNamed(newNamedRef) + if err != nil { + return nil, errors.Wrapf(err, "rewriting reference") + } + + return newParsedRef, nil +} + +// Registry represents a registry. +type Registry struct { + // Prefix is used for matching images, and to translate one namespace to + // another. If `Prefix="example.com/bar"`, `location="example.com/foo/bar"` + // and we pull from "example.com/bar/myimage:latest", the image will + // effectively be pulled from "example.com/foo/bar/myimage:latest". + // If no Prefix is specified, it defaults to the specified location. + // Prefix can also be in the format: "*.example.com" for matching + // subdomains. The wildcard should only be in the beginning and should also + // not contain any namespaces or special characters: "/", "@" or ":". + // Please refer to FindRegistry / PullSourcesFromReference instead + // of accessing/interpreting `Prefix` directly. + Prefix string `toml:"prefix"` + // A registry is an Endpoint too + Endpoint + // The registry's mirrors. + Mirrors []Endpoint `toml:"mirror,omitempty"` + // If true, pulling from the registry will be blocked. + Blocked bool `toml:"blocked,omitempty"` + // If true, mirrors will only be used for digest pulls. Pulling images by + // tag can potentially yield different images, depending on which endpoint + // we pull from. Forcing digest-pulls for mirrors avoids that issue. + MirrorByDigestOnly bool `toml:"mirror-by-digest-only,omitempty"` +} + +// PullSource consists of an Endpoint and a Reference. Note that the reference is +// rewritten according to the registries prefix and the Endpoint's location. +type PullSource struct { + Endpoint Endpoint + Reference reference.Named +} + +// PullSourcesFromReference returns a slice of PullSource's based on the passed +// reference. +func (r *Registry) PullSourcesFromReference(ref reference.Named) ([]PullSource, error) { + var endpoints []Endpoint + + if r.MirrorByDigestOnly { + // Only use mirrors when the reference is a digest one. + if _, isDigested := ref.(reference.Canonical); isDigested { + endpoints = append(r.Mirrors, r.Endpoint) + } else { + endpoints = []Endpoint{r.Endpoint} + } + } else { + endpoints = append(r.Mirrors, r.Endpoint) + } + + sources := []PullSource{} + for _, ep := range endpoints { + rewritten, err := ep.rewriteReference(ref, r.Prefix) + if err != nil { + return nil, err + } + sources = append(sources, PullSource{Endpoint: ep, Reference: rewritten}) + } + + return sources, nil +} + +// V1TOMLregistries is for backwards compatibility to sysregistries v1 +type V1TOMLregistries struct { + Registries []string `toml:"registries"` +} + +// V1TOMLConfig is for backwards compatibility to sysregistries v1 +type V1TOMLConfig struct { + Search V1TOMLregistries `toml:"search"` + Insecure V1TOMLregistries `toml:"insecure"` + Block V1TOMLregistries `toml:"block"` +} + +// V1RegistriesConf is the sysregistries v1 configuration format. +type V1RegistriesConf struct { + V1TOMLConfig `toml:"registries"` +} + +// Nonempty returns true if config contains at least one configuration entry. +func (config *V1RegistriesConf) Nonempty() bool { + copy := *config // A shallow copy + if copy.V1TOMLConfig.Search.Registries != nil && len(copy.V1TOMLConfig.Search.Registries) == 0 { + copy.V1TOMLConfig.Search.Registries = nil + } + if copy.V1TOMLConfig.Insecure.Registries != nil && len(copy.V1TOMLConfig.Insecure.Registries) == 0 { + copy.V1TOMLConfig.Insecure.Registries = nil + } + if copy.V1TOMLConfig.Block.Registries != nil && len(copy.V1TOMLConfig.Block.Registries) == 0 { + copy.V1TOMLConfig.Block.Registries = nil + } + return !reflect.DeepEqual(copy, V1RegistriesConf{}) +} + +// V2RegistriesConf is the sysregistries v2 configuration format. +type V2RegistriesConf struct { + Registries []Registry `toml:"registry"` + // An array of host[:port] (not prefix!) entries to use for resolving unqualified image references + UnqualifiedSearchRegistries []string `toml:"unqualified-search-registries"` + // An array of global credential helpers to use for authentication + // (e.g., ["pass", "secretservice"]). The helpers are consulted in the + // specified order. Note that "containers-auth.json" is a reserved + // value for consulting auth files as specified in + // containers-auth.json(5). + // + // If empty, CredentialHelpers defaults to ["containers-auth.json"]. + CredentialHelpers []string `toml:"credential-helpers"` + + // ShortNameMode defines how short-name resolution should be handled by + // _consumers_ of this package. Depending on the mode, the user should + // be prompted with a choice of using one of the unqualified-search + // registries when referring to a short name. + // + // Valid modes are: * "prompt": prompt if stdout is a TTY, otherwise + // use all unqualified-search registries * "enforcing": always prompt + // and error if stdout is not a TTY * "disabled": do not prompt and + // potentially use all unqualified-search registries + ShortNameMode string `toml:"short-name-mode"` + + shortNameAliasConf + + // If you add any field, make sure to update Nonempty() below. +} + +// Nonempty returns true if config contains at least one configuration entry. +func (config *V2RegistriesConf) Nonempty() bool { + copy := *config // A shallow copy + if copy.Registries != nil && len(copy.Registries) == 0 { + copy.Registries = nil + } + if copy.UnqualifiedSearchRegistries != nil && len(copy.UnqualifiedSearchRegistries) == 0 { + copy.UnqualifiedSearchRegistries = nil + } + if copy.CredentialHelpers != nil && len(copy.CredentialHelpers) == 0 { + copy.CredentialHelpers = nil + } + if !copy.shortNameAliasConf.nonempty() { + copy.shortNameAliasConf = shortNameAliasConf{} + } + return !reflect.DeepEqual(copy, V2RegistriesConf{}) +} + +// parsedConfig is the result of parsing, and possibly merging, configuration files; +// it is the boundary between the process of reading+ingesting the files, and +// later interpreting the configuration based on caller’s requests. +type parsedConfig struct { + // NOTE: Update also parsedConfig.updateWithConfigurationFrom! + + // partialV2 must continue to exist to maintain the return value of TryUpdatingCache + // for compatibility with existing callers. + // We store the authoritative Registries and UnqualifiedSearchRegistries values there as well. + partialV2 V2RegistriesConf + // Absolute path to the configuration file that set the UnqualifiedSearchRegistries. + unqualifiedSearchRegistriesOrigin string + // Result of parsing of partialV2.ShortNameMode. + // NOTE: May be ShortNameModeInvalid to represent ShortNameMode == "" in intermediate values; + // the full configuration in configCache / getConfig() always contains a valid value. + shortNameMode types.ShortNameMode + aliasCache *shortNameAliasCache +} + +// InvalidRegistries represents an invalid registry configurations. An example +// is when "registry.com" is defined multiple times in the configuration but +// with conflicting security settings. +type InvalidRegistries struct { + s string +} + +// Error returns the error string. +func (e *InvalidRegistries) Error() string { + return e.s +} + +// parseLocation parses the input string, performs some sanity checks and returns +// the sanitized input string. An error is returned if the input string is +// empty or if contains an "http{s,}://" prefix. +func parseLocation(input string) (string, error) { + trimmed := strings.TrimRight(input, "/") + + // FIXME: This check needs to exist but fails for empty Location field with + // wildcarded prefix. Removal of this check "only" allows invalid input in, + // and does not prevent correct operation. + // https://github.com/containers/image/pull/1191#discussion_r610122617 + // + // if trimmed == "" { + // return "", &InvalidRegistries{s: "invalid location: cannot be empty"} + // } + // + + if strings.HasPrefix(trimmed, "http://") || strings.HasPrefix(trimmed, "https://") { + msg := fmt.Sprintf("invalid location '%s': URI schemes are not supported", input) + return "", &InvalidRegistries{s: msg} + } + + return trimmed, nil +} + +// ConvertToV2 returns a v2 config corresponding to a v1 one. +func (config *V1RegistriesConf) ConvertToV2() (*V2RegistriesConf, error) { + regMap := make(map[string]*Registry) + // The order of the registries is not really important, but make it deterministic (the same for the same config file) + // to minimize behavior inconsistency and not contribute to difficult-to-reproduce situations. + registryOrder := []string{} + + getRegistry := func(location string) (*Registry, error) { // Note: _pointer_ to a long-lived object + var err error + location, err = parseLocation(location) + if err != nil { + return nil, err + } + reg, exists := regMap[location] + if !exists { + reg = &Registry{ + Endpoint: Endpoint{Location: location}, + Mirrors: []Endpoint{}, + Prefix: location, + } + regMap[location] = reg + registryOrder = append(registryOrder, location) + } + return reg, nil + } + + for _, blocked := range config.V1TOMLConfig.Block.Registries { + reg, err := getRegistry(blocked) + if err != nil { + return nil, err + } + reg.Blocked = true + } + for _, insecure := range config.V1TOMLConfig.Insecure.Registries { + reg, err := getRegistry(insecure) + if err != nil { + return nil, err + } + reg.Insecure = true + } + + res := &V2RegistriesConf{ + UnqualifiedSearchRegistries: config.V1TOMLConfig.Search.Registries, + } + for _, location := range registryOrder { + reg := regMap[location] + res.Registries = append(res.Registries, *reg) + } + return res, nil +} + +// anchoredDomainRegexp is an internal implementation detail of postProcess, defining the valid values of elements of UnqualifiedSearchRegistries. +var anchoredDomainRegexp = regexp.MustCompile("^" + reference.DomainRegexp.String() + "$") + +// postProcess checks the consistency of all the configuration, looks for conflicts, +// and normalizes the configuration (e.g., sets the Prefix to Location if not set). +func (config *V2RegistriesConf) postProcessRegistries() error { + regMap := make(map[string][]*Registry) + + for i := range config.Registries { + reg := &config.Registries[i] + // make sure Location and Prefix are valid + var err error + reg.Location, err = parseLocation(reg.Location) + if err != nil { + return err + } + + if reg.Prefix == "" { + if reg.Location == "" { + return &InvalidRegistries{s: "invalid condition: both location and prefix are unset"} + } + reg.Prefix = reg.Location + } else { + reg.Prefix, err = parseLocation(reg.Prefix) + if err != nil { + return err + } + // FIXME: allow config authors to always use Prefix. + // https://github.com/containers/image/pull/1191#discussion_r610622495 + if !strings.HasPrefix(reg.Prefix, "*.") && reg.Location == "" { + return &InvalidRegistries{s: "invalid condition: location is unset and prefix is not in the format: *.example.com"} + } + } + + // make sure mirrors are valid + for _, mir := range reg.Mirrors { + mir.Location, err = parseLocation(mir.Location) + if err != nil { + return err + } + + //FIXME: unqualifiedSearchRegistries now also accepts empty values + //and shouldn't + // https://github.com/containers/image/pull/1191#discussion_r610623216 + if mir.Location == "" { + return &InvalidRegistries{s: "invalid condition: mirror location is unset"} + } + } + if reg.Location == "" { + regMap[reg.Prefix] = append(regMap[reg.Prefix], reg) + } else { + regMap[reg.Location] = append(regMap[reg.Location], reg) + } + } + + // Given a registry can be mentioned multiple times (e.g., to have + // multiple prefixes backed by different mirrors), we need to make sure + // there are no conflicts among them. + // + // Note: we need to iterate over the registries array to ensure a + // deterministic behavior which is not guaranteed by maps. + for _, reg := range config.Registries { + var others []*Registry + var ok bool + if reg.Location == "" { + others, ok = regMap[reg.Prefix] + } else { + others, ok = regMap[reg.Location] + } + if !ok { + return fmt.Errorf("Internal error in V2RegistriesConf.PostProcess: entry in regMap is missing") + } + for _, other := range others { + if reg.Insecure != other.Insecure { + msg := fmt.Sprintf("registry '%s' is defined multiple times with conflicting 'insecure' setting", reg.Location) + return &InvalidRegistries{s: msg} + } + + if reg.Blocked != other.Blocked { + msg := fmt.Sprintf("registry '%s' is defined multiple times with conflicting 'blocked' setting", reg.Location) + return &InvalidRegistries{s: msg} + } + } + } + + for i := range config.UnqualifiedSearchRegistries { + registry, err := parseLocation(config.UnqualifiedSearchRegistries[i]) + if err != nil { + return err + } + if !anchoredDomainRegexp.MatchString(registry) { + return &InvalidRegistries{fmt.Sprintf("Invalid unqualified-search-registries entry %#v", registry)} + } + config.UnqualifiedSearchRegistries[i] = registry + } + + // Registries are ordered and the first longest prefix always wins, + // rendering later items with the same prefix non-existent. We cannot error + // out anymore as this might break existing users, so let's just ignore them + // to guarantee that the same prefix exists only once. + // + // As a side effect of parsedConfig.updateWithConfigurationFrom, the Registries slice + // is always sorted. To be consistent in situations where it is not called (no drop-ins), + // sort it here as well. + prefixes := []string{} + uniqueRegistries := make(map[string]Registry) + for i := range config.Registries { + // TODO: should we warn if we see the same prefix being used multiple times? + prefix := config.Registries[i].Prefix + if _, exists := uniqueRegistries[prefix]; !exists { + uniqueRegistries[prefix] = config.Registries[i] + prefixes = append(prefixes, prefix) + } + } + sort.Strings(prefixes) + config.Registries = []Registry{} + for _, prefix := range prefixes { + config.Registries = append(config.Registries, uniqueRegistries[prefix]) + } + + return nil +} + +// ConfigPath returns the path to the system-wide registry configuration file. +// Deprecated: This API implies configuration is read from files, and that there is only one. +// Please use ConfigurationSourceDescription to obtain a string usable for error messages. +func ConfigPath(ctx *types.SystemContext) string { + return newConfigWrapper(ctx).configPath +} + +// ConfigDirPath returns the path to the directory for drop-in +// registry configuration files. +// Deprecated: This API implies configuration is read from directories, and that there is only one. +// Please use ConfigurationSourceDescription to obtain a string usable for error messages. +func ConfigDirPath(ctx *types.SystemContext) string { + configWrapper := newConfigWrapper(ctx) + if configWrapper.userConfigDirPath != "" { + return configWrapper.userConfigDirPath + } + return configWrapper.configDirPath +} + +// configWrapper is used to store the paths from ConfigPath and ConfigDirPath +// and acts as a key to the internal cache. +type configWrapper struct { + // path to the registries.conf file + configPath string + // path to system-wide registries.conf.d directory, or "" if not used + configDirPath string + // path to user specified registries.conf.d directory, or "" if not used + userConfigDirPath string +} + +// newConfigWrapper returns a configWrapper for the specified SystemContext. +func newConfigWrapper(ctx *types.SystemContext) configWrapper { + return newConfigWrapperWithHomeDir(ctx, homedir.Get()) +} + +// newConfigWrapperWithHomeDir is an internal implementation detail of newConfigWrapper, +// it exists only to allow testing it with an artificial home directory. +func newConfigWrapperWithHomeDir(ctx *types.SystemContext, homeDir string) configWrapper { + var wrapper configWrapper + userRegistriesFilePath := filepath.Join(homeDir, userRegistriesFile) + userRegistriesDirPath := filepath.Join(homeDir, userRegistriesDir) + + // decide configPath using per-user path or system file + if ctx != nil && ctx.SystemRegistriesConfPath != "" { + wrapper.configPath = ctx.SystemRegistriesConfPath + } else if _, err := os.Stat(userRegistriesFilePath); err == nil { + // per-user registries.conf exists, not reading system dir + // return config dirs from ctx or per-user one + wrapper.configPath = userRegistriesFilePath + if ctx != nil && ctx.SystemRegistriesConfDirPath != "" { + wrapper.configDirPath = ctx.SystemRegistriesConfDirPath + } else { + wrapper.userConfigDirPath = userRegistriesDirPath + } + + return wrapper + } else if ctx != nil && ctx.RootForImplicitAbsolutePaths != "" { + wrapper.configPath = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemRegistriesConfPath) + } else { + wrapper.configPath = systemRegistriesConfPath + } + + // potentially use both system and per-user dirs if not using per-user config file + if ctx != nil && ctx.SystemRegistriesConfDirPath != "" { + // dir explicitly chosen: use only that one + wrapper.configDirPath = ctx.SystemRegistriesConfDirPath + } else if ctx != nil && ctx.RootForImplicitAbsolutePaths != "" { + wrapper.configDirPath = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemRegistriesConfDirPath) + wrapper.userConfigDirPath = userRegistriesDirPath + } else { + wrapper.configDirPath = systemRegistriesConfDirPath + wrapper.userConfigDirPath = userRegistriesDirPath + } + + return wrapper +} + +// ConfigurationSourceDescription returns a string containers paths of registries.conf and registries.conf.d +func ConfigurationSourceDescription(ctx *types.SystemContext) string { + wrapper := newConfigWrapper(ctx) + configSources := []string{wrapper.configPath} + if wrapper.configDirPath != "" { + configSources = append(configSources, wrapper.configDirPath) + } + if wrapper.userConfigDirPath != "" { + configSources = append(configSources, wrapper.userConfigDirPath) + } + return strings.Join(configSources, ", ") +} + +// configMutex is used to synchronize concurrent accesses to configCache. +var configMutex = sync.Mutex{} + +// configCache caches already loaded configs with config paths as keys and is +// used to avoid redundantly parsing configs. Concurrent accesses to the cache +// are synchronized via configMutex. +var configCache = make(map[configWrapper]*parsedConfig) + +// InvalidateCache invalidates the registry cache. This function is meant to be +// used for long-running processes that need to reload potential changes made to +// the cached registry config files. +func InvalidateCache() { + configMutex.Lock() + defer configMutex.Unlock() + configCache = make(map[configWrapper]*parsedConfig) +} + +// getConfig returns the config object corresponding to ctx, loading it if it is not yet cached. +func getConfig(ctx *types.SystemContext) (*parsedConfig, error) { + wrapper := newConfigWrapper(ctx) + configMutex.Lock() + if config, inCache := configCache[wrapper]; inCache { + configMutex.Unlock() + return config, nil + } + configMutex.Unlock() + + return tryUpdatingCache(ctx, wrapper) +} + +// dropInConfigs returns a slice of drop-in-configs from the registries.conf.d +// directory. +func dropInConfigs(wrapper configWrapper) ([]string, error) { + var ( + configs []string + dirPaths []string + ) + if wrapper.configDirPath != "" { + dirPaths = append(dirPaths, wrapper.configDirPath) + } + if wrapper.userConfigDirPath != "" { + dirPaths = append(dirPaths, wrapper.userConfigDirPath) + } + for _, dirPath := range dirPaths { + err := filepath.Walk(dirPath, + // WalkFunc to read additional configs + func(path string, info os.FileInfo, err error) error { + switch { + case err != nil: + // return error (could be a permission problem) + return err + case info == nil: + // this should only happen when err != nil but let's be sure + return nil + case info.IsDir(): + if path != dirPath { + // make sure to not recurse into sub-directories + return filepath.SkipDir + } + // ignore directories + return nil + default: + // only add *.conf files + if strings.HasSuffix(path, ".conf") { + configs = append(configs, path) + } + return nil + } + }, + ) + + if err != nil && !os.IsNotExist(err) { + // Ignore IsNotExist errors: most systems won't have a registries.conf.d + // directory. + return nil, errors.Wrapf(err, "reading registries.conf.d") + } + } + + return configs, nil +} + +// TryUpdatingCache loads the configuration from the provided `SystemContext` +// without using the internal cache. On success, the loaded configuration will +// be added into the internal registry cache. +// It returns the resulting configuration; this is DEPRECATED and may not correctly +// reflect any future data handled by this package. +func TryUpdatingCache(ctx *types.SystemContext) (*V2RegistriesConf, error) { + config, err := tryUpdatingCache(ctx, newConfigWrapper(ctx)) + if err != nil { + return nil, err + } + return &config.partialV2, err +} + +// tryUpdatingCache implements TryUpdatingCache with an additional configWrapper +// argument to avoid redundantly calculating the config paths. +func tryUpdatingCache(ctx *types.SystemContext, wrapper configWrapper) (*parsedConfig, error) { + configMutex.Lock() + defer configMutex.Unlock() + + // load the config + config, err := loadConfigFile(wrapper.configPath, false) + if err != nil { + // Continue with an empty []Registry if we use the default config, which + // implies that the config path of the SystemContext isn't set. + // + // Note: if ctx.SystemRegistriesConfPath points to the default config, + // we will still return an error. + if os.IsNotExist(err) && (ctx == nil || ctx.SystemRegistriesConfPath == "") { + config = &parsedConfig{} + config.partialV2 = V2RegistriesConf{Registries: []Registry{}} + config.aliasCache, err = newShortNameAliasCache("", &shortNameAliasConf{}) + if err != nil { + return nil, err // Should never happen + } + } else { + return nil, errors.Wrapf(err, "loading registries configuration %q", wrapper.configPath) + } + } + + // Load the configs from the conf directory path. + dinConfigs, err := dropInConfigs(wrapper) + if err != nil { + return nil, err + } + for _, path := range dinConfigs { + // Enforce v2 format for drop-in-configs. + dropIn, err := loadConfigFile(path, true) + if err != nil { + return nil, errors.Wrapf(err, "loading drop-in registries configuration %q", path) + } + config.updateWithConfigurationFrom(dropIn) + } + + if config.shortNameMode == types.ShortNameModeInvalid { + config.shortNameMode = defaultShortNameMode + } + + if len(config.partialV2.CredentialHelpers) == 0 { + config.partialV2.CredentialHelpers = []string{AuthenticationFileHelper} + } + + // populate the cache + configCache[wrapper] = config + return config, nil +} + +// GetRegistries has been deprecated. Use FindRegistry instead. +// +// GetRegistries loads and returns the registries specified in the config. +// Note the parsed content of registry config files is cached. For reloading, +// use `InvalidateCache` and re-call `GetRegistries`. +func GetRegistries(ctx *types.SystemContext) ([]Registry, error) { + config, err := getConfig(ctx) + if err != nil { + return nil, err + } + return config.partialV2.Registries, nil +} + +// UnqualifiedSearchRegistries returns a list of host[:port] entries to try +// for unqualified image search, in the returned order) +func UnqualifiedSearchRegistries(ctx *types.SystemContext) ([]string, error) { + registries, _, err := UnqualifiedSearchRegistriesWithOrigin(ctx) + return registries, err +} + +// UnqualifiedSearchRegistriesWithOrigin returns a list of host[:port] entries +// to try for unqualified image search, in the returned order. It also returns +// a human-readable description of where these entries are specified (e.g., a +// registries.conf file). +func UnqualifiedSearchRegistriesWithOrigin(ctx *types.SystemContext) ([]string, string, error) { + config, err := getConfig(ctx) + if err != nil { + return nil, "", err + } + return config.partialV2.UnqualifiedSearchRegistries, config.unqualifiedSearchRegistriesOrigin, nil +} + +// parseShortNameMode translates the string into well-typed +// types.ShortNameMode. +func parseShortNameMode(mode string) (types.ShortNameMode, error) { + switch mode { + case "disabled": + return types.ShortNameModeDisabled, nil + case "enforcing": + return types.ShortNameModeEnforcing, nil + case "permissive": + return types.ShortNameModePermissive, nil + default: + return types.ShortNameModeInvalid, errors.Errorf("invalid short-name mode: %q", mode) + } +} + +// GetShortNameMode returns the configured types.ShortNameMode. +func GetShortNameMode(ctx *types.SystemContext) (types.ShortNameMode, error) { + if ctx != nil && ctx.ShortNameMode != nil { + return *ctx.ShortNameMode, nil + } + config, err := getConfig(ctx) + if err != nil { + return -1, err + } + return config.shortNameMode, err +} + +// CredentialHelpers returns the global top-level credential helpers. +func CredentialHelpers(sys *types.SystemContext) ([]string, error) { + config, err := getConfig(sys) + if err != nil { + return nil, err + } + return config.partialV2.CredentialHelpers, nil +} + +// refMatchingSubdomainPrefix returns the length of ref +// iff ref, which is a registry, repository namespace, repository or image reference (as formatted by +// reference.Domain(), reference.Named.Name() or reference.Reference.String() +// — note that this requires the name to start with an explicit hostname!), +// matches a Registry.Prefix value containing wildcarded subdomains in the +// format: *.example.com. Wildcards are only accepted at the beginning, so +// other formats like example.*.com will not work. Wildcarded prefixes also +// cannot contain port numbers or namespaces in them. +func refMatchingSubdomainPrefix(ref, prefix string) int { + index := strings.Index(ref, prefix[1:]) + if index == -1 { + return -1 + } + if strings.Contains(ref[:index], "/") { + return -1 + } + index += len(prefix[1:]) + if index == len(ref) { + return index + } + switch ref[index] { + case ':', '/', '@': + return index + default: + return -1 + } +} + +// refMatchingPrefix returns the length of the prefix iff ref, +// which is a registry, repository namespace, repository or image reference (as formatted by +// reference.Domain(), reference.Named.Name() or reference.Reference.String() +// — note that this requires the name to start with an explicit hostname!), +// matches a Registry.Prefix value. +// (This is split from the caller primarily to make testing easier.) +func refMatchingPrefix(ref, prefix string) int { + switch { + case strings.HasPrefix(prefix, "*."): + return refMatchingSubdomainPrefix(ref, prefix) + case len(ref) < len(prefix): + return -1 + case len(ref) == len(prefix): + if ref == prefix { + return len(prefix) + } + return -1 + case len(ref) > len(prefix): + if !strings.HasPrefix(ref, prefix) { + return -1 + } + c := ref[len(prefix)] + // This allows "example.com:5000" to match "example.com", + // which is unintended; that will get fixed eventually, DON'T RELY + // ON THE CURRENT BEHAVIOR. + if c == ':' || c == '/' || c == '@' { + return len(prefix) + } + return -1 + default: + panic("Internal error: impossible comparison outcome") + } +} + +// FindRegistry returns the Registry with the longest prefix for ref, +// which is a registry, repository namespace repository or image reference (as formatted by +// reference.Domain(), reference.Named.Name() or reference.Reference.String() +// — note that this requires the name to start with an explicit hostname!). +// If no Registry prefixes the image, nil is returned. +func FindRegistry(ctx *types.SystemContext, ref string) (*Registry, error) { + config, err := getConfig(ctx) + if err != nil { + return nil, err + } + + return findRegistryWithParsedConfig(config, ref) +} + +// findRegistryWithParsedConfig implements `FindRegistry` with a pre-loaded +// parseConfig. +func findRegistryWithParsedConfig(config *parsedConfig, ref string) (*Registry, error) { + reg := Registry{} + prefixLen := 0 + for _, r := range config.partialV2.Registries { + if refMatchingPrefix(ref, r.Prefix) != -1 { + length := len(r.Prefix) + if length > prefixLen { + reg = r + prefixLen = length + } + } + } + if prefixLen != 0 { + return ®, nil + } + return nil, nil +} + +// loadConfigFile loads and unmarshals a single config file. +// Use forceV2 if the config must in the v2 format. +func loadConfigFile(path string, forceV2 bool) (*parsedConfig, error) { + logrus.Debugf("Loading registries configuration %q", path) + + // tomlConfig allows us to unmarshal either V1 or V2 simultaneously. + type tomlConfig struct { + V2RegistriesConf + V1RegistriesConf // for backwards compatibility with sysregistries v1 + } + + // Load the tomlConfig. Note that `DecodeFile` will overwrite set fields. + var combinedTOML tomlConfig + _, err := toml.DecodeFile(path, &combinedTOML) + if err != nil { + return nil, err + } + + if combinedTOML.V1RegistriesConf.Nonempty() { + // Enforce the v2 format if requested. + if forceV2 { + return nil, &InvalidRegistries{s: "registry must be in v2 format but is in v1"} + } + + // Convert a v1 config into a v2 config. + if combinedTOML.V2RegistriesConf.Nonempty() { + return nil, &InvalidRegistries{s: "mixing sysregistry v1/v2 is not supported"} + } + converted, err := combinedTOML.V1RegistriesConf.ConvertToV2() + if err != nil { + return nil, err + } + combinedTOML.V1RegistriesConf = V1RegistriesConf{} + combinedTOML.V2RegistriesConf = *converted + } + + res := parsedConfig{partialV2: combinedTOML.V2RegistriesConf} + + // Post process registries, set the correct prefixes, sanity checks, etc. + if err := res.partialV2.postProcessRegistries(); err != nil { + return nil, err + } + + res.unqualifiedSearchRegistriesOrigin = path + + if len(res.partialV2.ShortNameMode) > 0 { + mode, err := parseShortNameMode(res.partialV2.ShortNameMode) + if err != nil { + return nil, err + } + res.shortNameMode = mode + } else { + res.shortNameMode = types.ShortNameModeInvalid + } + + // Valid wildcarded prefixes must be in the format: *.example.com + // FIXME: Move to postProcessRegistries + // https://github.com/containers/image/pull/1191#discussion_r610623829 + for i := range res.partialV2.Registries { + prefix := res.partialV2.Registries[i].Prefix + if strings.HasPrefix(prefix, "*.") && strings.ContainsAny(prefix, "/@:") { + msg := fmt.Sprintf("Wildcarded prefix should be in the format: *.example.com. Current prefix %q is incorrectly formatted", prefix) + return nil, &InvalidRegistries{s: msg} + } + } + + // Parse and validate short-name aliases. + cache, err := newShortNameAliasCache(path, &res.partialV2.shortNameAliasConf) + if err != nil { + return nil, errors.Wrap(err, "validating short-name aliases") + } + res.aliasCache = cache + // Clear conf.partialV2.shortNameAliasConf to make it available for garbage collection and + // reduce memory consumption. We're consulting aliasCache for lookups. + res.partialV2.shortNameAliasConf = shortNameAliasConf{} + + return &res, nil +} + +// updateWithConfigurationFrom updates c with configuration from updates. +// +// Fields present in updates will typically replace already set fields in c. +// The [[registry]] and alias tables are merged. +func (c *parsedConfig) updateWithConfigurationFrom(updates *parsedConfig) { + // == Merge Registries: + registryMap := make(map[string]Registry) + for i := range c.partialV2.Registries { + registryMap[c.partialV2.Registries[i].Prefix] = c.partialV2.Registries[i] + } + // Merge the freshly loaded registries. + for i := range updates.partialV2.Registries { + registryMap[updates.partialV2.Registries[i].Prefix] = updates.partialV2.Registries[i] + } + + // Go maps have a non-deterministic order when iterating the keys, so + // we dump them in a slice and sort it to enforce some order in + // Registries slice. Some consumers of c/image (e.g., CRI-O) log the + // the configuration where a non-deterministic order could easily cause + // confusion. + prefixes := []string{} + for prefix := range registryMap { + prefixes = append(prefixes, prefix) + } + sort.Strings(prefixes) + + c.partialV2.Registries = []Registry{} + for _, prefix := range prefixes { + c.partialV2.Registries = append(c.partialV2.Registries, registryMap[prefix]) + } + + // == Merge UnqualifiedSearchRegistries: + // This depends on an subtlety of the behavior of the TOML decoder, where a missing array field + // is not modified while unmarshaling (in our case remains to nil), while an [] is unmarshaled + // as a non-nil []string{}. + if updates.partialV2.UnqualifiedSearchRegistries != nil { + c.partialV2.UnqualifiedSearchRegistries = updates.partialV2.UnqualifiedSearchRegistries + c.unqualifiedSearchRegistriesOrigin = updates.unqualifiedSearchRegistriesOrigin + } + + // == Merge credential helpers: + if updates.partialV2.CredentialHelpers != nil { + c.partialV2.CredentialHelpers = updates.partialV2.CredentialHelpers + } + + // == Merge shortNameMode: + // We don’t maintain c.partialV2.ShortNameMode. + if updates.shortNameMode != types.ShortNameModeInvalid { + c.shortNameMode = updates.shortNameMode + } + + // == Merge aliasCache: + // We don’t maintain (in fact we actively clear) c.partialV2.shortNameAliasConf. + c.aliasCache.updateWithConfigurationFrom(updates.aliasCache) +} diff --git a/vendor/github.com/containers/image/v5/types/types.go b/vendor/github.com/containers/image/v5/types/types.go new file mode 100644 index 0000000000..dcff8caf76 --- /dev/null +++ b/vendor/github.com/containers/image/v5/types/types.go @@ -0,0 +1,694 @@ +package types + +import ( + "context" + "io" + "time" + + "github.com/containers/image/v5/docker/reference" + compression "github.com/containers/image/v5/pkg/compression/types" + digest "github.com/opencontainers/go-digest" + v1 "github.com/opencontainers/image-spec/specs-go/v1" +) + +// ImageTransport is a top-level namespace for ways to to store/load an image. +// It should generally correspond to ImageSource/ImageDestination implementations. +// +// Note that ImageTransport is based on "ways the users refer to image storage", not necessarily on the underlying physical transport. +// For example, all Docker References would be used within a single "docker" transport, regardless of whether the images are pulled over HTTP or HTTPS +// (or, even, IPv4 or IPv6). +// +// OTOH all images using the same transport should (apart from versions of the image format), be interoperable. +// For example, several different ImageTransport implementations may be based on local filesystem paths, +// but using completely different formats for the contents of that path (a single tar file, a directory containing tarballs, a fully expanded container filesystem, ...) +// +// See also transports.KnownTransports. +type ImageTransport interface { + // Name returns the name of the transport, which must be unique among other transports. + Name() string + // ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an ImageReference. + ParseReference(reference string) (ImageReference, error) + // ValidatePolicyConfigurationScope checks that scope is a valid name for a signature.PolicyTransportScopes keys + // (i.e. a valid PolicyConfigurationIdentity() or PolicyConfigurationNamespaces() return value). + // It is acceptable to allow an invalid value which will never be matched, it can "only" cause user confusion. + // scope passed to this function will not be "", that value is always allowed. + ValidatePolicyConfigurationScope(scope string) error +} + +// ImageReference is an abstracted way to refer to an image location, namespaced within an ImageTransport. +// +// The object should preferably be immutable after creation, with any parsing/state-dependent resolving happening +// within an ImageTransport.ParseReference() or equivalent API creating the reference object. +// That's also why the various identification/formatting methods of this type do not support returning errors. +// +// WARNING: While this design freezes the content of the reference within this process, it can not freeze the outside +// world: paths may be replaced by symlinks elsewhere, HTTP APIs may start returning different results, and so on. +type ImageReference interface { + Transport() ImageTransport + // StringWithinTransport returns a string representation of the reference, which MUST be such that + // reference.Transport().ParseReference(reference.StringWithinTransport()) returns an equivalent reference. + // NOTE: The returned string is not promised to be equal to the original input to ParseReference; + // e.g. default attribute values omitted by the user may be filled in in the return value, or vice versa. + // WARNING: Do not use the return value in the UI to describe an image, it does not contain the Transport().Name() prefix; + // instead, see transports.ImageName(). + StringWithinTransport() string + + // DockerReference returns a Docker reference associated with this reference + // (fully explicit, i.e. !reference.IsNameOnly, but reflecting user intent, + // not e.g. after redirect or alias processing), or nil if unknown/not applicable. + DockerReference() reference.Named + + // PolicyConfigurationIdentity returns a string representation of the reference, suitable for policy lookup. + // This MUST reflect user intent, not e.g. after processing of third-party redirects or aliases; + // The value SHOULD be fully explicit about its semantics, with no hidden defaults, AND canonical + // (i.e. various references with exactly the same semantics should return the same configuration identity) + // It is fine for the return value to be equal to StringWithinTransport(), and it is desirable but + // not required/guaranteed that it will be a valid input to Transport().ParseReference(). + // Returns "" if configuration identities for these references are not supported. + PolicyConfigurationIdentity() string + + // PolicyConfigurationNamespaces returns a list of other policy configuration namespaces to search + // for if explicit configuration for PolicyConfigurationIdentity() is not set. The list will be processed + // in order, terminating on first match, and an implicit "" is always checked at the end. + // It is STRONGLY recommended for the first element, if any, to be a prefix of PolicyConfigurationIdentity(), + // and each following element to be a prefix of the element preceding it. + PolicyConfigurationNamespaces() []string + + // NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport. + // The caller must call .Close() on the returned ImageCloser. + // NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource, + // verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage. + // WARNING: This may not do the right thing for a manifest list, see image.FromSource for details. + NewImage(ctx context.Context, sys *SystemContext) (ImageCloser, error) + // NewImageSource returns a types.ImageSource for this reference. + // The caller must call .Close() on the returned ImageSource. + NewImageSource(ctx context.Context, sys *SystemContext) (ImageSource, error) + // NewImageDestination returns a types.ImageDestination for this reference. + // The caller must call .Close() on the returned ImageDestination. + NewImageDestination(ctx context.Context, sys *SystemContext) (ImageDestination, error) + + // DeleteImage deletes the named image from the registry, if supported. + DeleteImage(ctx context.Context, sys *SystemContext) error +} + +// LayerCompression indicates if layers must be compressed, decompressed or preserved +type LayerCompression int + +const ( + // PreserveOriginal indicates the layer must be preserved, ie + // no compression or decompression. + PreserveOriginal LayerCompression = iota + // Decompress indicates the layer must be decompressed + Decompress + // Compress indicates the layer must be compressed + Compress +) + +// LayerCrypto indicates if layers have been encrypted or decrypted or none +type LayerCrypto int + +const ( + // PreserveOriginalCrypto indicates the layer must be preserved, ie + // no encryption/decryption + PreserveOriginalCrypto LayerCrypto = iota + // Encrypt indicates the layer is encrypted + Encrypt + // Decrypt indicates the layer is decrypted + Decrypt +) + +// BlobInfo collects known information about a blob (layer/config). +// In some situations, some fields may be unknown, in others they may be mandatory; documenting an “unknown” value here does not override that. +type BlobInfo struct { + Digest digest.Digest // "" if unknown. + Size int64 // -1 if unknown + URLs []string + Annotations map[string]string + MediaType string + // CompressionOperation is used in Image.UpdateLayerInfos to instruct + // whether the original layer's "compressed or not" should be preserved, + // possibly while changing the compression algorithm from one to another, + // or if it should be compressed or decompressed. The field defaults to + // preserve the original layer's compressedness. + // TODO: To remove together with CryptoOperation in re-design to remove + // field out out of BlobInfo. + CompressionOperation LayerCompression + // CompressionAlgorithm is used in Image.UpdateLayerInfos to set the correct + // MIME type for compressed layers (e.g., gzip or zstd). This field MUST be + // set when `CompressionOperation == Compress` and MAY be set when + // `CompressionOperation == PreserveOriginal` and the compression type is + // being changed for an already-compressed layer. + CompressionAlgorithm *compression.Algorithm + // CryptoOperation is used in Image.UpdateLayerInfos to instruct + // whether the original layer was encrypted/decrypted + // TODO: To remove together with CompressionOperation in re-design to + // remove field out out of BlobInfo. + CryptoOperation LayerCrypto +} + +// BICTransportScope encapsulates transport-dependent representation of a “scope” where blobs are or are not present. +// BlobInfocache.RecordKnownLocations / BlobInfocache.CandidateLocations record data about blobs keyed by (scope, digest). +// The scope will typically be similar to an ImageReference, or a superset of it within which blobs are reusable. +// +// NOTE: The contents of this structure may be recorded in a persistent file, possibly shared across different +// tools which use different versions of the transport. Allow for reasonable backward/forward compatibility, +// at least by not failing hard when encountering unknown data. +type BICTransportScope struct { + Opaque string +} + +// BICLocationReference encapsulates transport-dependent representation of a blob location within a BICTransportScope. +// Each transport can store arbitrary data using BlobInfoCache.RecordKnownLocation, and ImageDestination.TryReusingBlob +// can look it up using BlobInfoCache.CandidateLocations. +// +// NOTE: The contents of this structure may be recorded in a persistent file, possibly shared across different +// tools which use different versions of the transport. Allow for reasonable backward/forward compatibility, +// at least by not failing hard when encountering unknown data. +type BICLocationReference struct { + Opaque string +} + +// BICReplacementCandidate is an item returned by BlobInfoCache.CandidateLocations. +type BICReplacementCandidate struct { + Digest digest.Digest + Location BICLocationReference +} + +// BlobInfoCache records data useful for reusing blobs, or substituting equivalent ones, to avoid unnecessary blob copies. +// +// It records two kinds of data: +// - Sets of corresponding digest vs. uncompressed digest ("DiffID") pairs: +// One of the two digests is known to be uncompressed, and a single uncompressed digest may correspond to more than one compressed digest. +// This allows matching compressed layer blobs to existing local uncompressed layers (to avoid unnecessary download and decompression), +// or uncompressed layer blobs to existing remote compressed layers (to avoid unnecessary compression and upload)/ +// +// It is allowed to record an (uncompressed digest, the same uncompressed digest) correspondence, to express that the digest is known +// to be uncompressed (i.e. that a conversion from schema1 does not have to decompress the blob to compute a DiffID value). +// +// This mapping is primarily maintained in generic copy.Image code, but transports may want to contribute more data points if they independently +// compress/decompress blobs for their own purposes. +// +// - Known blob locations, managed by individual transports: +// The transports call RecordKnownLocation when encountering a blob that could possibly be reused (typically in GetBlob/PutBlob/TryReusingBlob), +// recording transport-specific information that allows the transport to reuse the blob in the future; +// then, TryReusingBlob implementations can call CandidateLocations to look up previously recorded blob locations that could be reused. +// +// Each transport defines its own “scopes” within which blob reuse is possible (e.g. in, the docker/distribution case, blobs +// can be directly reused within a registry, or mounted across registries within a registry server.) +// +// None of the methods return an error indication: errors when neither reading from, nor writing to, the cache, should be fatal; +// users of the cache should just fall back to copying the blobs the usual way. +// +// The BlobInfoCache interface is deprecated. Consumers of this library should use one of the implementations provided by +// subpackages of the library's "pkg/blobinfocache" package in preference to implementing the interface on their own. +type BlobInfoCache interface { + // UncompressedDigest returns an uncompressed digest corresponding to anyDigest. + // May return anyDigest if it is known to be uncompressed. + // Returns "" if nothing is known about the digest (it may be compressed or uncompressed). + UncompressedDigest(anyDigest digest.Digest) digest.Digest + // RecordDigestUncompressedPair records that the uncompressed version of anyDigest is uncompressed. + // It’s allowed for anyDigest == uncompressed. + // WARNING: Only call this for LOCALLY VERIFIED data; don’t record a digest pair just because some remote author claims so (e.g. + // because a manifest/config pair exists); otherwise the cache could be poisoned and allow substituting unexpected blobs. + // (Eventually, the DiffIDs in image config could detect the substitution, but that may be too late, and not all image formats contain that data.) + RecordDigestUncompressedPair(anyDigest digest.Digest, uncompressed digest.Digest) + + // RecordKnownLocation records that a blob with the specified digest exists within the specified (transport, scope) scope, + // and can be reused given the opaque location data. + RecordKnownLocation(transport ImageTransport, scope BICTransportScope, digest digest.Digest, location BICLocationReference) + // CandidateLocations returns a prioritized, limited, number of blobs and their locations that could possibly be reused + // within the specified (transport scope) (if they still exist, which is not guaranteed). + // + // If !canSubstitute, the returned candidates will match the submitted digest exactly; if canSubstitute, + // data from previous RecordDigestUncompressedPair calls is used to also look up variants of the blob which have the same + // uncompressed digest. + CandidateLocations(transport ImageTransport, scope BICTransportScope, digest digest.Digest, canSubstitute bool) []BICReplacementCandidate +} + +// ImageSource is a service, possibly remote (= slow), to download components of a single image or a named image set (manifest list). +// This is primarily useful for copying images around; for examining their properties, Image (below) +// is usually more useful. +// Each ImageSource should eventually be closed by calling Close(). +// +// WARNING: Various methods which return an object identified by digest generally do not +// validate that the returned data actually matches that digest; this is the caller’s responsibility. +type ImageSource interface { + // Reference returns the reference used to set up this source, _as specified by the user_ + // (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image. + Reference() ImageReference + // Close removes resources associated with an initialized ImageSource, if any. + Close() error + // GetManifest returns the image's manifest along with its MIME type (which may be empty when it can't be determined but the manifest is available). + // It may use a remote (= slow) service. + // If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve (when the primary manifest is a manifest list); + // this never happens if the primary manifest is not a manifest list (e.g. if the source never returns manifest lists). + GetManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, string, error) + // GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown). + // The Digest field in BlobInfo is guaranteed to be provided, Size may be -1 and MediaType may be optionally provided. + // May update BlobInfoCache, preferably after it knows for certain that a blob truly exists at a specific location. + GetBlob(context.Context, BlobInfo, BlobInfoCache) (io.ReadCloser, int64, error) + // HasThreadSafeGetBlob indicates whether GetBlob can be executed concurrently. + HasThreadSafeGetBlob() bool + // GetSignatures returns the image's signatures. It may use a remote (= slow) service. + // If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve signatures for + // (when the primary manifest is a manifest list); this never happens if the primary manifest is not a manifest list + // (e.g. if the source never returns manifest lists). + GetSignatures(ctx context.Context, instanceDigest *digest.Digest) ([][]byte, error) + // LayerInfosForCopy returns either nil (meaning the values in the manifest are fine), or updated values for the layer + // blobsums that are listed in the image's manifest. If values are returned, they should be used when using GetBlob() + // to read the image's layers. + // If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve BlobInfos for + // (when the primary manifest is a manifest list); this never happens if the primary manifest is not a manifest list + // (e.g. if the source never returns manifest lists). + // The Digest field is guaranteed to be provided; Size may be -1. + // WARNING: The list may contain duplicates, and they are semantically relevant. + LayerInfosForCopy(ctx context.Context, instanceDigest *digest.Digest) ([]BlobInfo, error) +} + +// ImageDestination is a service, possibly remote (= slow), to store components of a single image. +// +// There is a specific required order for some of the calls: +// TryReusingBlob/PutBlob on the various blobs, if any, MUST be called before PutManifest (manifest references blobs, which may be created or compressed only at push time) +// PutSignatures, if called, MUST be called after PutManifest (signatures reference manifest contents) +// Finally, Commit MUST be called if the caller wants the image, as formed by the components saved above, to persist. +// +// Each ImageDestination should eventually be closed by calling Close(). +type ImageDestination interface { + // Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent, + // e.g. it should use the public hostname instead of the result of resolving CNAMEs or following redirects. + Reference() ImageReference + // Close removes resources associated with an initialized ImageDestination, if any. + Close() error + + // SupportedManifestMIMETypes tells which manifest mime types the destination supports + // If an empty slice or nil it's returned, then any mime type can be tried to upload + SupportedManifestMIMETypes() []string + // SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures. + // Note: It is still possible for PutSignatures to fail if SupportsSignatures returns nil. + SupportsSignatures(ctx context.Context) error + // DesiredLayerCompression indicates the kind of compression to apply on layers + DesiredLayerCompression() LayerCompression + // AcceptsForeignLayerURLs returns false iff foreign layers in manifest should be actually + // uploaded to the image destination, true otherwise. + AcceptsForeignLayerURLs() bool + // MustMatchRuntimeOS returns true iff the destination can store only images targeted for the current runtime architecture and OS. False otherwise. + MustMatchRuntimeOS() bool + // IgnoresEmbeddedDockerReference() returns true iff the destination does not care about Image.EmbeddedDockerReferenceConflicts(), + // and would prefer to receive an unmodified manifest instead of one modified for the destination. + // Does not make a difference if Reference().DockerReference() is nil. + IgnoresEmbeddedDockerReference() bool + + // PutBlob writes contents of stream and returns data representing the result. + // inputInfo.Digest can be optionally provided if known; if provided, and stream is read to the end without error, the digest MUST match the stream contents. + // inputInfo.Size is the expected length of stream, if known. + // inputInfo.MediaType describes the blob format, if known. + // May update cache. + // WARNING: The contents of stream are being verified on the fly. Until stream.Read() returns io.EOF, the contents of the data SHOULD NOT be available + // to any other readers for download using the supplied digest. + // If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far. + PutBlob(ctx context.Context, stream io.Reader, inputInfo BlobInfo, cache BlobInfoCache, isConfig bool) (BlobInfo, error) + // HasThreadSafePutBlob indicates whether PutBlob can be executed concurrently. + HasThreadSafePutBlob() bool + // TryReusingBlob checks whether the transport already contains, or can efficiently reuse, a blob, and if so, applies it to the current destination + // (e.g. if the blob is a filesystem layer, this signifies that the changes it describes need to be applied again when composing a filesystem tree). + // info.Digest must not be empty. + // If canSubstitute, TryReusingBlob can use an equivalent equivalent of the desired blob; in that case the returned info may not match the input. + // If the blob has been successfully reused, returns (true, info, nil); info must contain at least a digest and size, and may + // include CompressionOperation and CompressionAlgorithm fields to indicate that a change to the compression type should be + // reflected in the manifest that will be written. + // If the transport can not reuse the requested blob, TryReusingBlob returns (false, {}, nil); it returns a non-nil error only on an unexpected failure. + // May use and/or update cache. + TryReusingBlob(ctx context.Context, info BlobInfo, cache BlobInfoCache, canSubstitute bool) (bool, BlobInfo, error) + // PutManifest writes manifest to the destination. + // If instanceDigest is not nil, it contains a digest of the specific manifest instance to write the manifest for + // (when the primary manifest is a manifest list); this should always be nil if the primary manifest is not a manifest list. + // It is expected but not enforced that the instanceDigest, when specified, matches the digest of `manifest` as generated + // by `manifest.Digest()`. + // FIXME? This should also receive a MIME type if known, to differentiate between schema versions. + // If the destination is in principle available, refuses this manifest type (e.g. it does not recognize the schema), + // but may accept a different manifest type, the returned error must be an ManifestTypeRejectedError. + PutManifest(ctx context.Context, manifest []byte, instanceDigest *digest.Digest) error + // PutSignatures writes a set of signatures to the destination. + // If instanceDigest is not nil, it contains a digest of the specific manifest instance to write or overwrite the signatures for + // (when the primary manifest is a manifest list); this should always be nil if the primary manifest is not a manifest list. + // MUST be called after PutManifest (signatures may reference manifest contents). + PutSignatures(ctx context.Context, signatures [][]byte, instanceDigest *digest.Digest) error + // Commit marks the process of storing the image as successful and asks for the image to be persisted. + // unparsedToplevel contains data about the top-level manifest of the source (which may be a single-arch image or a manifest list + // if PutManifest was only called for the single-arch image with instanceDigest == nil), primarily to allow lookups by the + // original manifest list digest, if desired. + // WARNING: This does not have any transactional semantics: + // - Uploaded data MAY be visible to others before Commit() is called + // - Uploaded data MAY be removed or MAY remain around if Close() is called without Commit() (i.e. rollback is allowed but not guaranteed) + Commit(ctx context.Context, unparsedToplevel UnparsedImage) error +} + +// ManifestTypeRejectedError is returned by ImageDestination.PutManifest if the destination is in principle available, +// refuses specifically this manifest type, but may accept a different manifest type. +type ManifestTypeRejectedError struct { // We only use a struct to allow a type assertion, without limiting the contents of the error otherwise. + Err error +} + +func (e ManifestTypeRejectedError) Error() string { + return e.Err.Error() +} + +// UnparsedImage is an Image-to-be; until it is verified and accepted, it only caries its identity and caches manifest and signature blobs. +// Thus, an UnparsedImage can be created from an ImageSource simply by fetching blobs without interpreting them, +// allowing cryptographic signature verification to happen first, before even fetching the manifest, or parsing anything else. +// This also makes the UnparsedImage→Image conversion an explicitly visible step. +// +// An UnparsedImage is a pair of (ImageSource, instance digest); it can represent either a manifest list or a single image instance. +// +// The UnparsedImage must not be used after the underlying ImageSource is Close()d. +type UnparsedImage interface { + // Reference returns the reference used to set up this source, _as specified by the user_ + // (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image. + Reference() ImageReference + // Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need. + Manifest(ctx context.Context) ([]byte, string, error) + // Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need. + Signatures(ctx context.Context) ([][]byte, error) +} + +// Image is the primary API for inspecting properties of images. +// An Image is based on a pair of (ImageSource, instance digest); it can represent either a manifest list or a single image instance. +// +// The Image must not be used after the underlying ImageSource is Close()d. +type Image interface { + // Note that Reference may return nil in the return value of UpdatedImage! + UnparsedImage + // ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object. + // Note that the config object may not exist in the underlying storage in the return value of UpdatedImage! Use ConfigBlob() below. + ConfigInfo() BlobInfo + // ConfigBlob returns the blob described by ConfigInfo, if ConfigInfo().Digest != ""; nil otherwise. + // The result is cached; it is OK to call this however often you need. + ConfigBlob(context.Context) ([]byte, error) + // OCIConfig returns the image configuration as per OCI v1 image-spec. Information about + // layers in the resulting configuration isn't guaranteed to be returned to due how + // old image manifests work (docker v2s1 especially). + OCIConfig(context.Context) (*v1.Image, error) + // LayerInfos returns a list of BlobInfos of layers referenced by this image, in order (the root layer first, and then successive layered layers). + // The Digest field is guaranteed to be provided, Size may be -1 and MediaType may be optionally provided. + // WARNING: The list may contain duplicates, and they are semantically relevant. + LayerInfos() []BlobInfo + // LayerInfosForCopy returns either nil (meaning the values in the manifest are fine), or updated values for the layer blobsums that are listed in the image's manifest. + // The Digest field is guaranteed to be provided, Size may be -1 and MediaType may be optionally provided. + // WARNING: The list may contain duplicates, and they are semantically relevant. + LayerInfosForCopy(context.Context) ([]BlobInfo, error) + // EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref. + // It returns false if the manifest does not embed a Docker reference. + // (This embedding unfortunately happens for Docker schema1, please do not add support for this in any new formats.) + EmbeddedDockerReferenceConflicts(ref reference.Named) bool + // Inspect returns various information for (skopeo inspect) parsed from the manifest and configuration. + Inspect(context.Context) (*ImageInspectInfo, error) + // UpdatedImageNeedsLayerDiffIDs returns true iff UpdatedImage(options) needs InformationOnly.LayerDiffIDs. + // This is a horribly specific interface, but computing InformationOnly.LayerDiffIDs can be very expensive to compute + // (most importantly it forces us to download the full layers even if they are already present at the destination). + UpdatedImageNeedsLayerDiffIDs(options ManifestUpdateOptions) bool + // UpdatedImage returns a types.Image modified according to options. + // Everything in options.InformationOnly should be provided, other fields should be set only if a modification is desired. + // This does not change the state of the original Image object. + // The returned error will be a manifest.ManifestLayerCompressionIncompatibilityError if + // manifests of type options.ManifestMIMEType can not include layers that are compressed + // in accordance with the CompressionOperation and CompressionAlgorithm specified in one + // or more options.LayerInfos items, though retrying with a different + // options.ManifestMIMEType or with different CompressionOperation+CompressionAlgorithm + // values might succeed. + UpdatedImage(ctx context.Context, options ManifestUpdateOptions) (Image, error) + // SupportsEncryption returns an indicator that the image supports encryption + // + // Deprecated: Initially used to determine if a manifest can be copied from a source manifest type since + // the process of updating a manifest between different manifest types was to update then convert. + // This resulted in some fields in the update being lost. This has been fixed by: https://github.com/containers/image/pull/836 + SupportsEncryption(ctx context.Context) bool + // Size returns an approximation of the amount of disk space which is consumed by the image in its current + // location. If the size is not known, -1 will be returned. + Size() (int64, error) +} + +// ImageCloser is an Image with a Close() method which must be called by the user. +// This is returned by ImageReference.NewImage, which transparently instantiates a types.ImageSource, +// to ensure that the ImageSource is closed. +type ImageCloser interface { + Image + // Close removes resources associated with an initialized ImageCloser. + Close() error +} + +// ManifestUpdateOptions is a way to pass named optional arguments to Image.UpdatedManifest +type ManifestUpdateOptions struct { + LayerInfos []BlobInfo // Complete BlobInfos (size+digest+urls+annotations) which should replace the originals, in order (the root layer first, and then successive layered layers). BlobInfos' MediaType fields are ignored. + EmbeddedDockerReference reference.Named + ManifestMIMEType string + // The values below are NOT requests to modify the image; they provide optional context which may or may not be used. + InformationOnly ManifestUpdateInformation +} + +// ManifestUpdateInformation is a component of ManifestUpdateOptions, named here +// only to make writing struct literals possible. +type ManifestUpdateInformation struct { + Destination ImageDestination // and yes, UpdatedManifest may write to Destination (see the schema2 → schema1 conversion logic in image/docker_schema2.go) + LayerInfos []BlobInfo // Complete BlobInfos (size+digest) which have been uploaded, in order (the root layer first, and then successive layered layers) + LayerDiffIDs []digest.Digest // Digest values for the _uncompressed_ contents of the blobs which have been uploaded, in the same order. +} + +// ImageInspectInfo is a set of metadata describing Docker images, primarily their manifest and configuration. +// The Tag field is a legacy field which is here just for the Docker v2s1 manifest. It won't be supported +// for other manifest types. +type ImageInspectInfo struct { + Tag string + Created *time.Time + DockerVersion string + Labels map[string]string + Architecture string + Variant string + Os string + Layers []string + Env []string +} + +// DockerAuthConfig contains authorization information for connecting to a registry. +// the value of Username and Password can be empty for accessing the registry anonymously +type DockerAuthConfig struct { + Username string + Password string + // IdentityToken can be used as an refresh_token in place of username and + // password to obtain the bearer/access token in oauth2 flow. If identity + // token is set, password should not be set. + // Ref: https://docs.docker.com/registry/spec/auth/oauth/ + IdentityToken string +} + +// OptionalBool is a boolean with an additional undefined value, which is meant +// to be used in the context of user input to distinguish between a +// user-specified value and a default value. +type OptionalBool byte + +const ( + // OptionalBoolUndefined indicates that the OptionalBoolean hasn't been written. + OptionalBoolUndefined OptionalBool = iota + // OptionalBoolTrue represents the boolean true. + OptionalBoolTrue + // OptionalBoolFalse represents the boolean false. + OptionalBoolFalse +) + +// NewOptionalBool converts the input bool into either OptionalBoolTrue or +// OptionalBoolFalse. The function is meant to avoid boilerplate code of users. +func NewOptionalBool(b bool) OptionalBool { + o := OptionalBoolFalse + if b { + o = OptionalBoolTrue + } + return o +} + +// ShortNameMode defines the mode of short-name resolution. +// +// The use of unqualified-search registries entails an ambiguity as it's +// unclear from which registry a given image, referenced by a short name, may +// be pulled from. +// +// The ShortNameMode type defines how short names should resolve. +type ShortNameMode int + +const ( + ShortNameModeInvalid ShortNameMode = iota + // Use all configured unqualified-search registries without prompting + // the user. + ShortNameModeDisabled + // If stdout and stdin are a TTY, prompt the user to select a configured + // unqualified-search registry. Otherwise, use all configured + // unqualified-search registries. + // + // Note that if only one unqualified-search registry is set, it will be + // used without prompting. + ShortNameModePermissive + // Always prompt the user to select a configured unqualified-search + // registry. Throw an error if stdout or stdin is not a TTY as + // prompting isn't possible. + // + // Note that if only one unqualified-search registry is set, it will be + // used without prompting. + ShortNameModeEnforcing +) + +// SystemContext allows parameterizing access to implicitly-accessed resources, +// like configuration files in /etc and users' login state in their home directory. +// Various components can share the same field only if their semantics is exactly +// the same; if in doubt, add a new field. +// It is always OK to pass nil instead of a SystemContext. +type SystemContext struct { + // If not "", prefixed to any absolute paths used by default by the library (e.g. in /etc/). + // Not used for any of the more specific path overrides available in this struct. + // Not used for any paths specified by users in config files (even if the location of the config file _was_ affected by it). + // NOTE: If this is set, environment-variable overrides of paths are ignored (to keep the semantics simple: to create an /etc replacement, just set RootForImplicitAbsolutePaths . + // and there is no need to worry about the environment.) + // NOTE: This does NOT affect paths starting by $HOME. + RootForImplicitAbsolutePaths string + + // === Global configuration overrides === + // If not "", overrides the system's default path for signature.Policy configuration. + SignaturePolicyPath string + // If not "", overrides the system's default path for registries.d (Docker signature storage configuration) + RegistriesDirPath string + // Path to the system-wide registries configuration file + SystemRegistriesConfPath string + // Path to the system-wide registries configuration directory + SystemRegistriesConfDirPath string + // Path to the user-specific short-names configuration file + UserShortNameAliasConfPath string + // If set, short-name resolution in pkg/shortnames must follow the specified mode + ShortNameMode *ShortNameMode + // If set, short names will resolve in pkg/shortnames to docker.io only, and unqualified-search registries and + // short-name aliases in registries.conf are ignored. Note that this field is only intended to help enforce + // resolving to Docker Hub in the Docker-compatible REST API of Podman; it should never be used outside this + // specific context. + PodmanOnlyShortNamesIgnoreRegistriesConfAndForceDockerHub bool + // If not "", overrides the default path for the authentication file, but only new format files + AuthFilePath string + // if not "", overrides the default path for the authentication file, but with the legacy format; + // the code currently will by default look for legacy format files like .dockercfg in the $HOME dir; + // but in addition to the home dir, openshift may mount .dockercfg files (via secret mount) + // in locations other than the home dir; openshift components should then set this field in those cases; + // this field is ignored if `AuthFilePath` is set (we favor the newer format); + // only reading of this data is supported; + LegacyFormatAuthFilePath string + // If not "", overrides the use of platform.GOARCH when choosing an image or verifying architecture match. + ArchitectureChoice string + // If not "", overrides the use of platform.GOOS when choosing an image or verifying OS match. + OSChoice string + // If not "", overrides the use of detected ARM platform variant when choosing an image or verifying variant match. + VariantChoice string + // If not "", overrides the system's default directory containing a blob info cache. + BlobInfoCacheDir string + // Additional tags when creating or copying a docker-archive. + DockerArchiveAdditionalTags []reference.NamedTagged + // If not "", overrides the temporary directory to use for storing big files + BigFilesTemporaryDir string + + // === OCI.Transport overrides === + // If not "", a directory containing a CA certificate (ending with ".crt"), + // a client certificate (ending with ".cert") and a client certificate key + // (ending with ".key") used when downloading OCI image layers. + OCICertPath string + // Allow downloading OCI image layers over HTTP, or HTTPS with failed TLS verification. Note that this does not affect other TLS connections. + OCIInsecureSkipTLSVerify bool + // If not "", use a shared directory for storing blobs rather than within OCI layouts + OCISharedBlobDirPath string + // Allow UnCompress image layer for OCI image layer + OCIAcceptUncompressedLayers bool + + // === docker.Transport overrides === + // If not "", a directory containing a CA certificate (ending with ".crt"), + // a client certificate (ending with ".cert") and a client certificate key + // (ending with ".key") used when talking to a container registry. + DockerCertPath string + // If not "", overrides the system’s default path for a directory containing host[:port] subdirectories with the same structure as DockerCertPath above. + // Ignored if DockerCertPath is non-empty. + DockerPerHostCertDirPath string + // Allow contacting container registries over HTTP, or HTTPS with failed TLS verification. Note that this does not affect other TLS connections. + DockerInsecureSkipTLSVerify OptionalBool + // if nil, the library tries to parse ~/.docker/config.json to retrieve credentials + // Ignored if DockerBearerRegistryToken is non-empty. + DockerAuthConfig *DockerAuthConfig + // if not "", the library uses this registry token to authenticate to the registry + DockerBearerRegistryToken string + // if not "", an User-Agent header is added to each request when contacting a registry. + DockerRegistryUserAgent string + // if true, a V1 ping attempt isn't done to give users a better error. Default is false. + // Note that this field is used mainly to integrate containers/image into projectatomic/docker + // in order to not break any existing docker's integration tests. + DockerDisableV1Ping bool + // If true, dockerImageDestination.SupportedManifestMIMETypes will omit the Schema1 media types from the supported list + DockerDisableDestSchema1MIMETypes bool + // If true, the physical pull source of docker transport images logged as info level + DockerLogMirrorChoice bool + // Directory to use for OSTree temporary files + OSTreeTmpDirPath string + // If true, all blobs will have precomputed digests to ensure layers are not uploaded that already exist on the registry. + // Note that this requires writing blobs to temporary files, and takes more time than the default behavior, + // when the digest for a blob is unknown. + DockerRegistryPushPrecomputeDigests bool + + // === docker/daemon.Transport overrides === + // A directory containing a CA certificate (ending with ".crt"), + // a client certificate (ending with ".cert") and a client certificate key + // (ending with ".key") used when talking to a Docker daemon. + DockerDaemonCertPath string + // The hostname or IP to the Docker daemon. If not set (aka ""), client.DefaultDockerHost is assumed. + DockerDaemonHost string + // Used to skip TLS verification, off by default. To take effect DockerDaemonCertPath needs to be specified as well. + DockerDaemonInsecureSkipTLSVerify bool + + // === dir.Transport overrides === + // DirForceCompress compresses the image layers if set to true + DirForceCompress bool + // DirForceDecompress decompresses the image layers if set to true + DirForceDecompress bool + + // CompressionFormat is the format to use for the compression of the blobs + CompressionFormat *compression.Algorithm + // CompressionLevel specifies what compression level is used + CompressionLevel *int +} + +// ProgressEvent is the type of events a progress reader can produce +// Warning: new event types may be added any time. +type ProgressEvent uint + +const ( + // ProgressEventNewArtifact will be fired on progress reader setup + ProgressEventNewArtifact ProgressEvent = iota + + // ProgressEventRead indicates that the artifact download is currently in + // progress + ProgressEventRead + + // ProgressEventDone is fired when the data transfer has been finished for + // the specific artifact + ProgressEventDone + + // ProgressEventSkipped is fired when the artifact has been skipped because + // its already available at the destination + ProgressEventSkipped +) + +// ProgressProperties is used to pass information from the copy code to a monitor which +// can use the real-time information to produce output or react to changes. +type ProgressProperties struct { + // The event indicating what + Event ProgressEvent + + // The artifact which has been updated in this interval + Artifact BlobInfo + + // The currently downloaded size in bytes + // Increases from 0 to the final Artifact size + Offset uint64 + + // The additional offset which has been downloaded inside the last update + // interval. Will be reset after each ProgressEventRead event. + OffsetUpdate uint64 +} diff --git a/vendor/github.com/containers/storage/AUTHORS b/vendor/github.com/containers/storage/AUTHORS new file mode 100644 index 0000000000..129dd39692 --- /dev/null +++ b/vendor/github.com/containers/storage/AUTHORS @@ -0,0 +1,1523 @@ +# This file lists all individuals having contributed content to the repository. +# For how it is generated, see `hack/generate-authors.sh`. + +Aanand Prasad +Aaron Davidson +Aaron Feng +Aaron Huslage +Aaron Lehmann +Aaron Welch +Abel Muiño +Abhijeet Kasurde +Abhinav Ajgaonkar +Abhishek Chanda +Abin Shahab +Adam Miller +Adam Singer +Aditi Rajagopal +Aditya +Adria Casas +Adrian Mouat +Adrian Oprea +Adrien Folie +Adrien Gallouët +Ahmed Kamal +Ahmet Alp Balkan +Aidan Feldman +Aidan Hobson Sayers +AJ Bowen +Ajey Charantimath +ajneu +Akihiro Suda +Al Tobey +alambike +Alan Scherger +Alan Thompson +Albert Callarisa +Albert Zhang +Aleksa Sarai +Aleksandrs Fadins +Alena Prokharchyk +Alessandro Boch +Alessio Biancalana +Alex Chan +Alex Crawford +Alex Ellis +Alex Gaynor +Alex Samorukov +Alex Warhawk +Alexander Artemenko +Alexander Boyd +Alexander Larsson +Alexander Morozov +Alexander Shopov +Alexandre Beslic +Alexandre González +Alexandru Sfirlogea +Alexey Guskov +Alexey Kotlyarov +Alexey Shamrin +Alexis THOMAS +Ali Dehghani +Allen Madsen +Allen Sun +almoehi +Alvin Richards +amangoel +Amen Belayneh +Amit Bakshi +Amit Krishnan +Amy Lindburg +Anand Patil +AnandkumarPatel +Anatoly Borodin +Anchal Agrawal +Anders Janmyr +Andre Dublin <81dublin@gmail.com> +Andre Granovsky +Andrea Luzzardi +Andrea Turli +Andreas Köhler +Andreas Savvides +Andreas Tiefenthaler +Andrew C. Bodine +Andrew Clay Shafer +Andrew Duckworth +Andrew France +Andrew Gerrand +Andrew Guenther +Andrew Kuklewicz +Andrew Macgregor +Andrew Macpherson +Andrew Martin +Andrew Munsell +Andrew Weiss +Andrew Williams +Andrews Medina +Andrey Petrov +Andrey Stolbovsky +André Martins +andy +Andy Chambers +andy diller +Andy Goldstein +Andy Kipp +Andy Rothfusz +Andy Smith +Andy Wilson +Anes Hasicic +Anil Belur +Ankush Agarwal +Anonmily +Anthon van der Neut +Anthony Baire +Anthony Bishopric +Anthony Dahanne +Anton Löfgren +Anton Nikitin +Anton Polonskiy +Anton Tiurin +Antonio Murdaca +Antony Messerli +Anuj Bahuguna +Anusha Ragunathan +apocas +ArikaChen +Arnaud Porterie +Arthur Barr +Arthur Gautier +Artur Meyster +Arun Gupta +Asbjørn Enge +averagehuman +Avi Das +Avi Miller +ayoshitake +Azat Khuyiyakhmetov +Bardia Keyoumarsi +Barnaby Gray +Barry Allard +Bartłomiej Piotrowski +Bastiaan Bakker +bdevloed +Ben Firshman +Ben Golub +Ben Hall +Ben Sargent +Ben Severson +Ben Toews +Ben Wiklund +Benjamin Atkin +Benoit Chesneau +Bernerd Schaefer +Bert Goethals +Bharath Thiruveedula +Bhiraj Butala +Bill W +bin liu +Blake Geno +Boaz Shuster +bobby abbott +boucher +Bouke Haarsma +Boyd Hemphill +boynux +Bradley Cicenas +Bradley Wright +Brandon Liu +Brandon Philips +Brandon Rhodes +Brendan Dixon +Brent Salisbury +Brett Higgins +Brett Kochendorfer +Brian (bex) Exelbierd +Brian Bland +Brian DeHamer +Brian Dorsey +Brian Flad +Brian Goff +Brian McCallister +Brian Olsen +Brian Shumate +Brian Torres-Gil +Brian Trump +Brice Jaglin +Briehan Lombaard +Bruno Bigras +Bruno Binet +Bruno Gazzera +Bruno Renié +Bryan Bess +Bryan Boreham +Bryan Matsuo +Bryan Murphy +buddhamagnet +Burke Libbey +Byung Kang +Caleb Spare +Calen Pennington +Cameron Boehmer +Cameron Spear +Campbell Allen +Candid Dauth +Carl Henrik Lunde +Carl X. Su +Carlos Alexandro Becker +Carlos Sanchez +Carol Fager-Higgins +Cary +Casey Bisson +Cedric Davies +Cezar Sa Espinola +Chad Swenson +Chance Zibolski +Chander G +Charles Chan +Charles Hooper +Charles Law +Charles Lindsay +Charles Merriam +Charles Sarrazin +Charlie Lewis +Chase Bolt +ChaYoung You +Chen Chao +Chen Hanxiao +cheney90 +Chewey +Chia-liang Kao +chli +Cholerae Hu +Chris Alfonso +Chris Armstrong +Chris Dituri +Chris Fordham +Chris Khoo +Chris McKinnel +Chris Seto +Chris Snow +Chris St. Pierre +Chris Stivers +Chris Swan +Chris Wahl +Chris Weyl +chrismckinnel +Christian Berendt +Christian Böhme +Christian Persson +Christian Rotzoll +Christian Simon +Christian Stefanescu +ChristoperBiscardi +Christophe Mehay +Christophe Troestler +Christopher Currie +Christopher Jones +Christopher Latham +Christopher Rigor +Christy Perez +Chun Chen +Ciro S. Costa +Clayton Coleman +Clinton Kitson +Coenraad Loubser +Colin Dunklau +Colin Rice +Colin Walters +Collin Guarino +Colm Hally +companycy +Cory Forsyth +cressie176 +Cristian Staretu +cristiano balducci +Cruceru Calin-Cristian +Cyril F +Daan van Berkel +Daehyeok Mun +Dafydd Crosby +dalanlan +Damien Nadé +Damien Nozay +Damjan Georgievski +Dan Anolik +Dan Buch +Dan Cotora +Dan Griffin +Dan Hirsch +Dan Keder +Dan Levy +Dan McPherson +Dan Stine +Dan Walsh +Dan Williams +Daniel Antlinger +Daniel Exner +Daniel Farrell +Daniel Garcia +Daniel Gasienica +Daniel Hiltgen +Daniel Menet +Daniel Mizyrycki +Daniel Nephin +Daniel Norberg +Daniel Nordberg +Daniel Robinson +Daniel S +Daniel Von Fange +Daniel YC Lin +Daniel Zhang +Daniel, Dao Quang Minh +Danny Berger +Danny Yates +Darren Coxall +Darren Shepherd +Darren Stahl +Dave Barboza +Dave Henderson +Dave MacDonald +Dave Tucker +David Anderson +David Calavera +David Corking +David Cramer +David Currie +David Davis +David Gageot +David Gebler +David Lawrence +David Mackey +David Mat +David Mcanulty +David Pelaez +David R. Jenni +David Röthlisberger +David Sheets +David Sissitka +David Xia +David Young +Davide Ceretti +Dawn Chen +dcylabs +decadent +deed02392 +Deng Guangxing +Deni Bertovic +Denis Gladkikh +Denis Ollier +Dennis Docter +Derek +Derek +Derek Ch +Derek McGowan +Deric Crago +Deshi Xiao +devmeyster +Devvyn Murphy +Dharmit Shah +Dieter Reuter +Dima Stopel +Dimitri John Ledkov +Dimitry Andric +Dinesh Subhraveti +Diogo Monica +DiuDiugirl +Djibril Koné +dkumor +Dmitri Logvinenko +Dmitry Demeshchuk +Dmitry Gusev +Dmitry V. Krivenok +Dmitry Vorobev +Dolph Mathews +Dominik Finkbeiner +Dominik Honnef +Don Kirkby +Don Kjer +Don Spaulding +Donald Huang +Dong Chen +Donovan Jones +Doug Davis +Doug MacEachern +Doug Tangren +Dr Nic Williams +dragon788 +Dražen Lučanin +Dustin Sallings +Ed Costello +Edmund Wagner +Eiichi Tsukata +Eike Herzbach +Eivind Uggedal +Elan Ruusamäe +Elias Probst +Elijah Zupancic +eluck +Elvir Kuric +Emil Hernvall +Emily Maier +Emily Rose +Emir Ozer +Enguerran +Eohyung Lee +Eric Hanchrow +Eric Lee +Eric Myhre +Eric Paris +Eric Rafaloff +Eric Rosenberg +Eric Sage +Eric Windisch +Eric Yang +Eric-Olivier Lamey +Erik Bray +Erik Dubbelboer +Erik Hollensbe +Erik Inge Bolsø +Erik Kristensen +Erik Weathers +Erno Hopearuoho +Erwin van der Koogh +Euan +Eugene Yakubovich +eugenkrizo +evalle +Evan Allrich +Evan Carmi +Evan Hazlett +Evan Krall +Evan Phoenix +Evan Wies +Evgeny Vereshchagin +Ewa Czechowska +Eystein Måløy Stenberg +ezbercih +Fabiano Rosas +Fabio Falci +Fabio Rehm +Fabrizio Regini +Fabrizio Soppelsa +Faiz Khan +falmp +Fangyuan Gao <21551127@zju.edu.cn> +Fareed Dudhia +Fathi Boudra +Federico Gimenez +Felix Geisendörfer +Felix Hupfeld +Felix Rabe +Felix Schindler +Ferenc Szabo +Fernando +Fero Volar +Filipe Brandenburger +Filipe Oliveira +fl0yd +Flavio Castelli +FLGMwt +Florian +Florian Klein +Florian Maier +Florian Weingarten +Florin Asavoaie +Francesc Campoy +Francisco Carriedo +Francisco Souza +Frank Groeneveld +Frank Herrmann +Frank Macreery +Frank Rosquin +Fred Lifton +Frederick F. Kautz IV +Frederik Loeffert +Frederik Nordahl Jul Sabroe +Freek Kalter +fy2462 +Félix Baylac-Jacqué +Félix Cantournet +Gabe Rosenhouse +Gabor Nagy +Gabriel Monroy +GabrielNicolasAvellaneda +Galen Sampson +Gareth Rushgrove +Garrett Barboza +Gaurav +gautam, prasanna +GennadySpb +Geoffrey Bachelet +George MacRorie +George Xie +Georgi Hristozov +Gereon Frey +German DZ +Gert van Valkenhoef +Gianluca Borello +Gildas Cuisinier +gissehel +Giuseppe Mazzotta +Gleb Fotengauer-Malinovskiy +Gleb M Borisov +Glyn Normington +GoBella +Goffert van Gool +Gosuke Miyashita +Gou Rao +Govinda Fichtner +Grant Reaber +Graydon Hoare +Greg Fausak +Greg Thornton +grossws +grunny +gs11 +Guilhem Lettron +Guilherme Salgado +Guillaume Dufour +Guillaume J. Charmes +guoxiuyan +Gurjeet Singh +Guruprasad +gwx296173 +Günter Zöchbauer +Hans Kristian Flaatten +Hans Rødtang +Hao Shu Wei +Hao Zhang <21521210@zju.edu.cn> +Harald Albers +Harley Laue +Harold Cooper +Harry Zhang +He Simei +heartlock <21521209@zju.edu.cn> +Hector Castro +Henning Sprang +Hobofan +Hollie Teal +Hong Xu +hsinko <21551195@zju.edu.cn> +Hu Keping +Hu Tao +Huanzhong Zhang +Huayi Zhang +Hugo Duncan +Hugo Marisco <0x6875676f@gmail.com> +Hunter Blanks +huqun +Huu Nguyen +hyeongkyu.lee +hyp3rdino +Hyzhou <1187766782@qq.com> +Ian Babrou +Ian Bishop +Ian Bull +Ian Calvert +Ian Lee +Ian Main +Ian Truslove +Iavael +Icaro Seara +Igor Dolzhikov +Ilkka Laukkanen +Ilya Dmitrichenko +Ilya Gusev +ILYA Khlopotov +imre Fitos +inglesp +Ingo Gottwald +Isaac Dupree +Isabel Jimenez +Isao Jonas +Ivan Babrou +Ivan Fraixedes +Ivan Grcic +J Bruni +J. Nunn +Jack Danger Canty +Jacob Atzen +Jacob Edelman +Jake Champlin +Jake Moshenko +jakedt +James Allen +James Carey +James Carr +James DeFelice +James Harrison Fisher +James Kyburz +James Kyle +James Lal +James Mills +James Nugent +James Turnbull +Jamie Hannaford +Jamshid Afshar +Jan Keromnes +Jan Koprowski +Jan Pazdziora +Jan Toebes +Jan-Gerd Tenberge +Jan-Jaap Driessen +Jana Radhakrishnan +Januar Wayong +Jared Biel +Jared Hocutt +Jaroslaw Zabiello +jaseg +Jasmine Hegman +Jason Divock +Jason Giedymin +Jason Green +Jason Hall +Jason Heiss +Jason Livesay +Jason McVetta +Jason Plum +Jason Shepherd +Jason Smith +Jason Sommer +Jason Stangroome +jaxgeller +Jay +Jay +Jay Kamat +Jean-Baptiste Barth +Jean-Baptiste Dalido +Jean-Paul Calderone +Jean-Tiare Le Bigot +Jeff Anderson +Jeff Johnston +Jeff Lindsay +Jeff Mickey +Jeff Minard +Jeff Nickoloff +Jeff Welch +Jeffrey Bolle +Jeffrey Morgan +Jeffrey van Gogh +Jenny Gebske +Jeremy Grosser +Jeremy Price +Jeremy Qian +Jeremy Unruh +Jeroen Jacobs +Jesse Dearing +Jesse Dubay +Jessica Frazelle +Jezeniel Zapanta +jgeiger +Jhon Honce +Jian Zhang +jianbosun +Jilles Oldenbeuving +Jim Alateras +Jim Perrin +Jimmy Cuadra +Jimmy Puckett +jimmyxian +Jinsoo Park +Jiri Popelka +Jiří Župka +jjy +jmzwcn +Joe Beda +Joe Doliner +Joe Ferguson +Joe Gordon +Joe Shaw +Joe Van Dyk +Joel Friedly +Joel Handwell +Joel Hansson +Joel Wurtz +Joey Geiger +Joey Gibson +Joffrey F +Johan Euphrosine +Johan Rydberg +Johannes 'fish' Ziemke +John Costa +John Feminella +John Gardiner Myers +John Gossman +John Howard (VM) +John OBrien III +John Starks +John Tims +John Warwick +John Willis +Jon Wedaman +Jonas Pfenniger +Jonathan A. Sternberg +Jonathan Boulle +Jonathan Camp +Jonathan Dowland +Jonathan Lebon +Jonathan McCrohan +Jonathan Mueller +Jonathan Pares +Jonathan Rudenberg +Joost Cassee +Jordan +Jordan Arentsen +Jordan Sissel +Jordan Williams +Jose Diaz-Gonzalez +Joseph Anthony Pasquale Holsten +Joseph Hager +Joseph Kern +Josh +Josh Hawn +Josh Poimboeuf +Josiah Kiehl +José Tomás Albornoz +JP +jrabbit +Julian Taylor +Julien Barbier +Julien Bisconti +Julien Bordellier +Julien Dubois +Julien Pervillé +Julio Montes +Jun-Ru Chang +Jussi Nummelin +Justas Brazauskas +Justin Cormack +Justin Force +Justin Plock +Justin Simonelis +Justin Terry +Jyrki Puttonen +Jérôme Petazzoni +Jörg Thalheim +Kai Blin +Kai Qiang Wu(Kennan) +Kamil Domański +kamjar gerami +Kanstantsin Shautsou +Karan Lyons +Kareem Khazem +kargakis +Karl Grzeszczak +Karol Duleba +Katie McLaughlin +Kato Kazuyoshi +Katrina Owen +Kawsar Saiyeed +kayrus +Ke Xu +Keli Hu +Ken Cochrane +Ken ICHIKAWA +Kenfe-Mickael Laventure +Kenjiro Nakayama +Kent Johnson +Kevin "qwazerty" Houdebert +Kevin Clark +Kevin J. Lynagh +Kevin Menard +Kevin P. Kucharczyk +Kevin Shi +Kevin Wallace +Kevin Yap +kevinmeredith +Keyvan Fatehi +kies +Kim BKC Carlbacker +Kim Eik +Kimbro Staken +Kir Kolyshkin +Kiran Gangadharan +Kirill SIbirev +knappe +Kohei Tsuruta +Koichi Shiraishi +Konrad Kleine +Konstantin Pelykh +Krasimir Georgiev +Kristian Haugene +Kristina Zabunova +krrg +Kun Zhang +Kunal Kushwaha +Kyle Conroy +kyu +Lachlan Coote +Lai Jiangshan +Lajos Papp +Lakshan Perera +Lalatendu Mohanty +lalyos +Lance Chen +Lance Kinley +Lars Butler +Lars Kellogg-Stedman +Lars R. Damerow +Laszlo Meszaros +Laurent Erignoux +Laurie Voss +Leandro Siqueira +Lee, Meng-Han +leeplay +Lei Jitang +Len Weincier +Lennie +Leszek Kowalski +Levi Blackstone +Levi Gross +Lewis Marshall +Lewis Peckover +Liana Lo +Liang Mingqiang +Liang-Chi Hsieh +liaoqingwei +limsy +Lin Lu +LingFaKe +Linus Heckemann +Liran Tal +Liron Levin +Liu Bo +Liu Hua +LIZAO LI +Lloyd Dewolf +Lokesh Mandvekar +longliqiang88 <394564827@qq.com> +Lorenz Leutgeb +Lorenzo Fontana +Louis Opter +Luca Marturana +Luca Orlandi +Luca-Bogdan Grigorescu +Lucas Chan +Luis Martínez de Bartolomé Izquierdo +Lukas Waslowski +lukaspustina +Lukasz Zajaczkowski +lukemarsden +Lynda O'Leary +Lénaïc Huard +Ma Shimiao +Mabin +Madhav Puri +Madhu Venugopal +Mageee <21521230.zju.edu.cn> +Mahesh Tiyyagura +malnick +Malte Janduda +manchoz +Manfred Touron +Manfred Zabarauskas +mansinahar +Manuel Meurer +Manuel Woelker +mapk0y +Marc Abramowitz +Marc Kuo +Marc Tamsky +Marcelo Salazar +Marco Hennings +Marcus Farkas +Marcus Linke +Marcus Ramberg +Marek Goldmann +Marian Marinov +Marianna Tessel +Mario Loriedo +Marius Gundersen +Marius Sturm +Marius Voila +Mark Allen +Mark McGranaghan +Mark McKinstry +Mark West +Marko Mikulicic +Marko Tibold +Markus Fix +Martijn Dwars +Martijn van Oosterhout +Martin Honermeyer +Martin Kelly +Martin Mosegaard Amdisen +Martin Redmond +Mary Anthony +Masahito Zembutsu +Mason Malone +Mateusz Sulima +Mathias Monnerville +Mathieu Le Marec - Pasquet +Matt Apperson +Matt Bachmann +Matt Bentley +Matt Haggard +Matt McCormick +Matt Moore +Matt Robenolt +Matthew Heon +Matthew Mayer +Matthew Mueller +Matthew Riley +Matthias Klumpp +Matthias Kühnle +Matthias Rampke +Matthieu Hauglustaine +mattymo +mattyw +Mauricio Garavaglia +mauriyouth +Max Shytikov +Maxim Ivanov +Maxim Kulkin +Maxim Treskin +Maxime Petazzoni +Meaglith Ma +meejah +Megan Kostick +Mehul Kar +Mengdi Gao +Mert Yazıcıoğlu +Micah Zoltu +Michael A. Smith +Michael Bridgen +Michael Brown +Michael Chiang +Michael Crosby +Michael Currie +Michael Friis +Michael Gorsuch +Michael Grauer +Michael Holzheu +Michael Hudson-Doyle +Michael Huettermann +Michael Käufl +Michael Neale +Michael Prokop +Michael Scharf +Michael Stapelberg +Michael Steinert +Michael Thies +Michael West +Michal Fojtik +Michal Gebauer +Michal Jemala +Michal Minar +Michaël Pailloncy +Michał Czeraszkiewicz +Michiel@unhosted +Miguel Angel Fernández +Miguel Morales +Mihai Borobocea +Mihuleacc Sergiu +Mike Brown +Mike Chelen +Mike Danese +Mike Dillon +Mike Dougherty +Mike Gaffney +Mike Goelzer +Mike Leone +Mike MacCana +Mike Naberezny +Mike Snitzer +mikelinjie <294893458@qq.com> +Mikhail Sobolev +Miloslav Trmač +mingqing +Mingzhen Feng +Mitch Capper +mlarcher +Mohammad Banikazemi +Mohammed Aaqib Ansari +Mohit Soni +Morgan Bauer +Morgante Pell +Morgy93 +Morten Siebuhr +Morton Fox +Moysés Borges +mqliang +Mrunal Patel +msabansal +mschurenko +muge +Mustafa Akın +Muthukumar R +Máximo Cuadros +Médi-Rémi Hashim +Nahum Shalman +Nakul Pathak +Nalin Dahyabhai +Nan Monnand Deng +Naoki Orii +Natalie Parker +Natanael Copa +Nate Brennand +Nate Eagleson +Nate Jones +Nathan Hsieh +Nathan Kleyn +Nathan LeClaire +Nathan McCauley +Nathan Williams +Neal McBurnett +Nelson Chen +Nghia Tran +Niall O'Higgins +Nicholas E. Rabenau +Nick Irvine +Nick Parker +Nick Payne +Nick Stenning +Nick Stinemates +Nicolas Borboën +Nicolas De loof +Nicolas Dudebout +Nicolas Goy +Nicolas Kaiser +Nicolás Hock Isaza +Nigel Poulton +NikolaMandic +nikolas +Nirmal Mehta +Nishant Totla +NIWA Hideyuki +noducks +Nolan Darilek +nponeccop +Nuutti Kotivuori +nzwsch +O.S. Tezer +objectified +OddBloke +odk- +Oguz Bilgic +Oh Jinkyun +Ohad Schneider +Ole Reifschneider +Oliver Neal +Olivier Gambier +Olle Jonsson +Oriol Francès +Otto Kekäläinen +oyld +ozlerhakan +paetling +pandrew +panticz +Paolo G. Giarrusso +Pascal Borreli +Pascal Hartig +Patrick Devine +Patrick Hemmer +Patrick Stapleton +pattichen +Paul +paul +Paul Annesley +Paul Bellamy +Paul Bowsher +Paul Hammond +Paul Jimenez +Paul Lietar +Paul Liljenberg +Paul Morie +Paul Nasrat +Paul Weaver +Pavel Lobashov +Pavel Pospisil +Pavel Sutyrin +Pavel Tikhomirov +Pavlos Ratis +Peeyush Gupta +Peggy Li +Pei Su +Penghan Wang +perhapszzy@sina.com +Peter Bourgon +Peter Braden +Peter Choi +Peter Dave Hello +Peter Edge +Peter Ericson +Peter Esbensen +Peter Malmgren +Peter Salvatore +Peter Volpe +Peter Waller +Phil +Phil Estes +Phil Spitler +Philip Monroe +Philipp Wahala +Philipp Weissensteiner +Phillip Alexander +pidster +Piergiuliano Bossi +Pierre +Pierre Carrier +Pierre Wacrenier +Pierre-Alain RIVIERE +Piotr Bogdan +pixelistik +Porjo +Poul Kjeldager Sørensen +Pradeep Chhetri +Prasanna Gautam +Prayag Verma +Przemek Hejman +pysqz +qg <1373319223@qq.com> +qhuang +Qiang Huang +qq690388648 <690388648@qq.com> +Quentin Brossard +Quentin Perez +Quentin Tayssier +r0n22 +Rafal Jeczalik +Rafe Colton +Raghavendra K T +Raghuram Devarakonda +Rajat Pandit +Rajdeep Dua +Ralle +Ralph Bean +Ramkumar Ramachandra +Ramon van Alteren +Ray Tsang +ReadmeCritic +Recursive Madman +Regan McCooey +Remi Rampin +Renato Riccieri Santos Zannon +resouer +rgstephens +Rhys Hiltner +Rich Seymour +Richard +Richard Burnison +Richard Harvey +Richard Metzler +Richard Scothern +Richo Healey +Rick Bradley +Rick van de Loo +Rick Wieman +Rik Nijessen +Riku Voipio +Riley Guerin +Ritesh H Shukla +Riyaz Faizullabhoy +Rob Vesse +Robert Bachmann +Robert Bittle +Robert Obryk +Robert Stern +Robert Wallis +Roberto G. Hashioka +Robin Naundorf +Robin Schneider +Robin Speekenbrink +robpc +Rodolfo Carvalho +Rodrigo Vaz +Roel Van Nyen +Roger Peppe +Rohit Jnagal +Rohit Kadam +Roland Huß +Roland Kammerer +Roland Moriz +Roma Sokolov +Roman Strashkin +Ron Smits +root +root +root +root +Rory Hunter +Rory McCune +Ross Boucher +Rovanion Luckey +Rozhnov Alexandr +rsmoorthy +Rudolph Gottesheim +Rui Lopes +Ryan Anderson +Ryan Aslett +Ryan Belgrave +Ryan Detzel +Ryan Fowler +Ryan McLaughlin +Ryan O'Donnell +Ryan Seto +Ryan Thomas +Ryan Trauntvein +Ryan Wallner +RyanDeng +Rémy Greinhofer +s. rannou +s00318865 +Sabin Basyal +Sachin Joshi +Sagar Hani +Sainath Grandhi +Sally O'Malley +Sam Abed +Sam Alba +Sam Bailey +Sam J Sharpe +Sam Neirinck +Sam Reis +Sam Rijs +Sambuddha Basu +Sami Wagiaalla +Samuel Andaya +Samuel Dion-Girardeau +Samuel Karp +Samuel PHAN +Sankar சங்கர் +Sanket Saurav +Santhosh Manohar +sapphiredev +Satnam Singh +satoru +Satoshi Amemiya +scaleoutsean +Scott Bessler +Scott Collier +Scott Johnston +Scott Stamp +Scott Walls +sdreyesg +Sean Christopherson +Sean Cronin +Sean OMeara +Sean P. Kane +Sebastiaan van Steenis +Sebastiaan van Stijn +Senthil Kumar Selvaraj +Senthil Kumaran +SeongJae Park +Seongyeol Lim +Serge Hallyn +Sergey Alekseev +Sergey Evstifeev +Sevki Hasirci +Shane Canon +Shane da Silva +shaunol +Shawn Landden +Shawn Siefkas +Shekhar Gulati +Sheng Yang +Shengbo Song +Shih-Yuan Lee +Shijiang Wei +Shishir Mahajan +shuai-z +Shuwei Hao +Sian Lerk Lau +sidharthamani +Silas Sewell +Simei He +Simon Eskildsen +Simon Leinen +Simon Taranto +Sindhu S +Sjoerd Langkemper +Solganik Alexander +Solomon Hykes +Song Gao +Soshi Katsuta +Soulou +Spencer Brown +Spencer Smith +Sridatta Thatipamala +Sridhar Ratnakumar +Srini Brahmaroutu +srinsriv +Steeve Morin +Stefan Berger +Stefan J. Wernli +Stefan Praszalowicz +Stefan Scherer +Stefan Staudenmeyer +Stefan Weil +Stephen Crosby +Stephen Day +Stephen Rust +Steve Durrheimer +Steve Francia +Steve Koch +Steven Burgess +Steven Iveson +Steven Merrill +Steven Richards +Steven Taylor +Subhajit Ghosh +Sujith Haridasan +Suryakumar Sudar +Sven Dowideit +Swapnil Daingade +Sylvain Baubeau +Sylvain Bellemare +Sébastien +Sébastien Luttringer +Sébastien Stormacq +TAGOMORI Satoshi +tang0th +Tangi COLIN +Tatsuki Sugiura +Tatsushi Inagaki +Taylor Jones +tbonza +Ted M. Young +Tehmasp Chaudhri +Tejesh Mehta +terryding77 <550147740@qq.com> +tgic +Thatcher Peskens +theadactyl +Thell 'Bo' Fowler +Thermionix +Thijs Terlouw +Thomas Bikeev +Thomas Frössman +Thomas Gazagnaire +Thomas Grainger +Thomas Hansen +Thomas Leonard +Thomas LEVEIL +Thomas Orozco +Thomas Riccardi +Thomas Schroeter +Thomas Sjögren +Thomas Swift +Thomas Tanaka +Thomas Texier +Tianon Gravi +Tibor Vass +Tiffany Low +Tim Bosse +Tim Dettrick +Tim Düsterhus +Tim Hockin +Tim Ruffles +Tim Smith +Tim Terhorst +Tim Wang +Tim Waugh +Tim Wraight +Timothy Hobbs +tjwebb123 +tobe +Tobias Bieniek +Tobias Bradtke +Tobias Gesellchen +Tobias Klauser +Tobias Schmidt +Tobias Schwab +Todd Crane +Todd Lunter +Todd Whiteman +Toli Kuznets +Tom Barlow +Tom Denham +Tom Fotherby +Tom Howe +Tom Hulihan +Tom Maaswinkel +Tom X. Tobin +Tomas Tomecek +Tomasz Kopczynski +Tomasz Lipinski +Tomasz Nurkiewicz +Tommaso Visconti +Tomáš Hrčka +Tonis Tiigi +Tonny Xu +Tony Daws +Tony Miller +toogley +Torstein Husebø +tpng +tracylihui <793912329@qq.com> +Travis Cline +Travis Thieman +Trent Ogren +Trevor +Trevor Pounds +trishnaguha +Tristan Carel +Troy Denton +Tyler Brock +Tzu-Jung Lee +Tõnis Tiigi +Ulysse Carion +unknown +vagrant +Vaidas Jablonskis +Veres Lajos +vgeta +Victor Coisne +Victor Costan +Victor I. Wood +Victor Lyuboslavsky +Victor Marmol +Victor Palma +Victor Vieux +Victoria Bialas +Vijaya Kumar K +Viktor Stanchev +Viktor Vojnovski +VinayRaghavanKS +Vincent Batts +Vincent Bernat +Vincent Bernat +Vincent Demeester +Vincent Giersch +Vincent Mayers +Vincent Woo +Vinod Kulkarni +Vishal Doshi +Vishnu Kannan +Vitor Monteiro +Vivek Agarwal +Vivek Dasgupta +Vivek Goyal +Vladimir Bulyga +Vladimir Kirillov +Vladimir Rutsky +Vladimir Varankin +VladimirAus +Vojtech Vitek (V-Teq) +waitingkuo +Walter Leibbrandt +Walter Stanish +WANG Chao +Wang Xing +Ward Vandewege +WarheadsSE +Wayne Chang +Wei-Ting Kuo +weiyan +Weiyang Zhu +Wen Cheng Ma +Wendel Fleming +Wenxuan Zhao +Wenyu You <21551128@zju.edu.cn> +Wes Morgan +Will Dietz +Will Rouesnel +Will Weaver +willhf +William Delanoue +William Henry +William Hubbs +William Riancho +William Thurston +WiseTrem +wlan0 +Wolfgang Powisch +wonderflow +xamyzhao +XiaoBing Jiang +Xiaoxu Chen +xiekeyang +Xinzi Zhou +Xiuming Chen +xlgao-zju +xuzhaokui +Yahya +YAMADA Tsuyoshi +Yan Feng +Yang Bai +yangshukui +Yasunori Mahata +Yestin Sun +Yi EungJun +Yibai Zhang +Yihang Ho +Ying Li +Yohei Ueda +Yong Tang +Yongzhi Pan +yorkie +Youcef YEKHLEF +Yuan Sun +yuchangchun +yuchengxia +Yurii Rashkovskii +yuzou +Zac Dover +Zach Borboa +Zachary Jaffee +Zain Memon +Zaiste! +Zane DeGraffenried +Zefan Li +Zen Lin(Zhinan Lin) +Zhang Kun +Zhang Wei +Zhang Wentao +Zhenan Ye <21551168@zju.edu.cn> +Zhu Guihua +Zhuoyun Wei +Zilin Du +zimbatm +Ziming Dong +ZJUshuaizhou <21551191@zju.edu.cn> +zmarouf +Zoltan Tombol +zqh +Zuhayr Elahi +Zunayed Ali +Álex González +Álvaro Lázaro +Átila Camurça Alves +尹吉峰 +搏通 diff --git a/vendor/github.com/containers/storage/LICENSE b/vendor/github.com/containers/storage/LICENSE new file mode 100644 index 0000000000..8f3fee627a --- /dev/null +++ b/vendor/github.com/containers/storage/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright 2013-2016 Docker, Inc. + + 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 + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/containers/storage/NOTICE b/vendor/github.com/containers/storage/NOTICE new file mode 100644 index 0000000000..8a37c1c7bc --- /dev/null +++ b/vendor/github.com/containers/storage/NOTICE @@ -0,0 +1,19 @@ +Docker +Copyright 2012-2016 Docker, Inc. + +This product includes software developed at Docker, Inc. (https://www.docker.com). + +This product contains software (https://github.com/kr/pty) developed +by Keith Rarick, licensed under the MIT License. + +The following is courtesy of our legal counsel: + + +Use and transfer of Docker may be subject to certain restrictions by the +United States and other governments. +It is your responsibility to ensure that your use and/or transfer does not +violate applicable laws. + +For more information, please see https://www.bis.doc.gov + +See also https://www.apache.org/dev/crypto.html and/or seek legal counsel. diff --git a/vendor/github.com/containers/storage/pkg/homedir/homedir.go b/vendor/github.com/containers/storage/pkg/homedir/homedir.go new file mode 100644 index 0000000000..85c5e76c84 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/homedir/homedir.go @@ -0,0 +1,52 @@ +package homedir + +import ( + "errors" + "os" + "path/filepath" +) + +// GetConfigHome returns XDG_CONFIG_HOME. +// GetConfigHome returns $HOME/.config and nil error if XDG_CONFIG_HOME is not set. +// +// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html +func GetConfigHome() (string, error) { + if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" { + return xdgConfigHome, nil + } + home := Get() + if home == "" { + return "", errors.New("could not get either XDG_CONFIG_HOME or HOME") + } + return filepath.Join(home, ".config"), nil +} + +// GetDataHome returns XDG_DATA_HOME. +// GetDataHome returns $HOME/.local/share and nil error if XDG_DATA_HOME is not set. +// +// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html +func GetDataHome() (string, error) { + if xdgDataHome := os.Getenv("XDG_DATA_HOME"); xdgDataHome != "" { + return xdgDataHome, nil + } + home := Get() + if home == "" { + return "", errors.New("could not get either XDG_DATA_HOME or HOME") + } + return filepath.Join(home, ".local", "share"), nil +} + +// GetCacheHome returns XDG_CACHE_HOME. +// GetCacheHome returns $HOME/.cache and nil error if XDG_CACHE_HOME is not set. +// +// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html +func GetCacheHome() (string, error) { + if xdgCacheHome := os.Getenv("XDG_CACHE_HOME"); xdgCacheHome != "" { + return xdgCacheHome, nil + } + home := Get() + if home == "" { + return "", errors.New("could not get either XDG_CACHE_HOME or HOME") + } + return filepath.Join(home, ".cache"), nil +} diff --git a/vendor/github.com/containers/storage/pkg/homedir/homedir_others.go b/vendor/github.com/containers/storage/pkg/homedir/homedir_others.go new file mode 100644 index 0000000000..027db259c1 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/homedir/homedir_others.go @@ -0,0 +1,20 @@ +// +build !linux,!darwin,!freebsd + +package homedir + +// Copyright 2013-2018 Docker, Inc. +// NOTE: this package has originally been copied from github.com/docker/docker. + +import ( + "errors" +) + +// GetRuntimeDir is unsupported on non-linux system. +func GetRuntimeDir() (string, error) { + return "", errors.New("homedir.GetRuntimeDir() is not supported on this system") +} + +// StickRuntimeDirContents is unsupported on non-linux system. +func StickRuntimeDirContents(files []string) ([]string, error) { + return nil, errors.New("homedir.StickRuntimeDirContents() is not supported on this system") +} diff --git a/vendor/github.com/containers/storage/pkg/homedir/homedir_unix.go b/vendor/github.com/containers/storage/pkg/homedir/homedir_unix.go new file mode 100644 index 0000000000..33177bdf30 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/homedir/homedir_unix.go @@ -0,0 +1,95 @@ +// +build !windows + +package homedir + +// Copyright 2013-2018 Docker, Inc. +// NOTE: this package has originally been copied from github.com/docker/docker. + +import ( + "errors" + "os" + "path/filepath" + "strings" + + "github.com/containers/storage/pkg/unshare" +) + +// Key returns the env var name for the user's home dir based on +// the platform being run on +func Key() string { + return "HOME" +} + +// Get returns the home directory of the current user with the help of +// environment variables depending on the target operating system. +// Returned path should be used with "path/filepath" to form new paths. +// +// If linking statically with cgo enabled against glibc, ensure the +// osusergo build tag is used. +// +// If needing to do nss lookups, do not disable cgo or set osusergo. +func Get() string { + homedir, _ := unshare.HomeDir() + return homedir +} + +// GetShortcutString returns the string that is shortcut to user's home directory +// in the native shell of the platform running on. +func GetShortcutString() string { + return "~" +} + +// GetRuntimeDir returns XDG_RUNTIME_DIR. +// XDG_RUNTIME_DIR is typically configured via pam_systemd. +// GetRuntimeDir returns non-nil error if XDG_RUNTIME_DIR is not set. +// +// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html +func GetRuntimeDir() (string, error) { + if xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR"); xdgRuntimeDir != "" { + return xdgRuntimeDir, nil + } + return "", errors.New("could not get XDG_RUNTIME_DIR") +} + +// StickRuntimeDirContents sets the sticky bit on files that are under +// XDG_RUNTIME_DIR, so that the files won't be periodically removed by the system. +// +// StickyRuntimeDir returns slice of sticked files. +// StickyRuntimeDir returns nil error if XDG_RUNTIME_DIR is not set. +// +// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html +func StickRuntimeDirContents(files []string) ([]string, error) { + runtimeDir, err := GetRuntimeDir() + if err != nil { + // ignore error if runtimeDir is empty + return nil, nil + } + runtimeDir, err = filepath.Abs(runtimeDir) + if err != nil { + return nil, err + } + var sticked []string + for _, f := range files { + f, err = filepath.Abs(f) + if err != nil { + return sticked, err + } + if strings.HasPrefix(f, runtimeDir+"/") { + if err = stick(f); err != nil { + return sticked, err + } + sticked = append(sticked, f) + } + } + return sticked, nil +} + +func stick(f string) error { + st, err := os.Stat(f) + if err != nil { + return err + } + m := st.Mode() + m |= os.ModeSticky + return os.Chmod(f, m) +} diff --git a/vendor/github.com/containers/storage/pkg/homedir/homedir_windows.go b/vendor/github.com/containers/storage/pkg/homedir/homedir_windows.go new file mode 100644 index 0000000000..af65f2c03d --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/homedir/homedir_windows.go @@ -0,0 +1,32 @@ +package homedir + +// Copyright 2013-2018 Docker, Inc. +// NOTE: this package has originally been copied from github.com/docker/docker. + +import ( + "os" +) + +// Key returns the env var name for the user's home dir based on +// the platform being run on +func Key() string { + return "USERPROFILE" +} + +// Get returns the home directory of the current user with the help of +// environment variables depending on the target operating system. +// Returned path should be used with "path/filepath" to form new paths. +func Get() string { + home := os.Getenv(Key()) + if home != "" { + return home + } + home, _ = os.UserHomeDir() + return home +} + +// GetShortcutString returns the string that is shortcut to user's home directory +// in the native shell of the platform running on. +func GetShortcutString() string { + return "%USERPROFILE%" // be careful while using in format functions +} diff --git a/vendor/github.com/containers/storage/pkg/idtools/idtools.go b/vendor/github.com/containers/storage/pkg/idtools/idtools.go new file mode 100644 index 0000000000..0abe886eb2 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/idtools/idtools.go @@ -0,0 +1,358 @@ +package idtools + +import ( + "bufio" + "fmt" + "os" + "os/user" + "sort" + "strconv" + "strings" + "syscall" + + "github.com/containers/storage/pkg/system" + "github.com/pkg/errors" +) + +// IDMap contains a single entry for user namespace range remapping. An array +// of IDMap entries represents the structure that will be provided to the Linux +// kernel for creating a user namespace. +type IDMap struct { + ContainerID int `json:"container_id"` + HostID int `json:"host_id"` + Size int `json:"size"` +} + +type subIDRange struct { + Start int + Length int +} + +type ranges []subIDRange + +func (e ranges) Len() int { return len(e) } +func (e ranges) Swap(i, j int) { e[i], e[j] = e[j], e[i] } +func (e ranges) Less(i, j int) bool { return e[i].Start < e[j].Start } + +const ( + subuidFileName string = "/etc/subuid" + subgidFileName string = "/etc/subgid" +) + +// MkdirAllAs creates a directory (include any along the path) and then modifies +// ownership to the requested uid/gid. If the directory already exists, this +// function will still change ownership to the requested uid/gid pair. +// Deprecated: Use MkdirAllAndChown +func MkdirAllAs(path string, mode os.FileMode, ownerUID, ownerGID int) error { + return mkdirAs(path, mode, ownerUID, ownerGID, true, true) +} + +// MkdirAs creates a directory and then modifies ownership to the requested uid/gid. +// If the directory already exists, this function still changes ownership +// Deprecated: Use MkdirAndChown with a IDPair +func MkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int) error { + return mkdirAs(path, mode, ownerUID, ownerGID, false, true) +} + +// MkdirAllAndChown creates a directory (include any along the path) and then modifies +// ownership to the requested uid/gid. If the directory already exists, this +// function will still change ownership to the requested uid/gid pair. +func MkdirAllAndChown(path string, mode os.FileMode, ids IDPair) error { + return mkdirAs(path, mode, ids.UID, ids.GID, true, true) +} + +// MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid. +// If the directory already exists, this function still changes ownership +func MkdirAndChown(path string, mode os.FileMode, ids IDPair) error { + return mkdirAs(path, mode, ids.UID, ids.GID, false, true) +} + +// MkdirAllAndChownNew creates a directory (include any along the path) and then modifies +// ownership ONLY of newly created directories to the requested uid/gid. If the +// directories along the path exist, no change of ownership will be performed +func MkdirAllAndChownNew(path string, mode os.FileMode, ids IDPair) error { + return mkdirAs(path, mode, ids.UID, ids.GID, true, false) +} + +// GetRootUIDGID retrieves the remapped root uid/gid pair from the set of maps. +// If the maps are empty, then the root uid/gid will default to "real" 0/0 +func GetRootUIDGID(uidMap, gidMap []IDMap) (int, int, error) { + var uid, gid int + var err error + if len(uidMap) == 1 && uidMap[0].Size == 1 { + uid = uidMap[0].HostID + } else { + uid, err = RawToHost(0, uidMap) + if err != nil { + return -1, -1, err + } + } + if len(gidMap) == 1 && gidMap[0].Size == 1 { + gid = gidMap[0].HostID + } else { + gid, err = RawToHost(0, gidMap) + if err != nil { + return -1, -1, err + } + } + return uid, gid, nil +} + +// RawToContainer takes an id mapping, and uses it to translate a host ID to +// the remapped ID. If no map is provided, then the translation assumes a +// 1-to-1 mapping and returns the passed in id. +// +// If you wish to map a (uid,gid) combination you should use the corresponding +// IDMappings methods, which ensure that you are mapping the correct ID against +// the correct mapping. +func RawToContainer(hostID int, idMap []IDMap) (int, error) { + if idMap == nil { + return hostID, nil + } + for _, m := range idMap { + if (hostID >= m.HostID) && (hostID <= (m.HostID + m.Size - 1)) { + contID := m.ContainerID + (hostID - m.HostID) + return contID, nil + } + } + return -1, fmt.Errorf("Host ID %d cannot be mapped to a container ID", hostID) +} + +// RawToHost takes an id mapping and a remapped ID, and translates the ID to +// the mapped host ID. If no map is provided, then the translation assumes a +// 1-to-1 mapping and returns the passed in id. +// +// If you wish to map a (uid,gid) combination you should use the corresponding +// IDMappings methods, which ensure that you are mapping the correct ID against +// the correct mapping. +func RawToHost(contID int, idMap []IDMap) (int, error) { + if idMap == nil { + return contID, nil + } + for _, m := range idMap { + if (contID >= m.ContainerID) && (contID <= (m.ContainerID + m.Size - 1)) { + hostID := m.HostID + (contID - m.ContainerID) + return hostID, nil + } + } + return -1, fmt.Errorf("Container ID %d cannot be mapped to a host ID", contID) +} + +// IDPair is a UID and GID pair +type IDPair struct { + UID int + GID int +} + +// IDMappings contains a mappings of UIDs and GIDs +type IDMappings struct { + uids []IDMap + gids []IDMap +} + +// NewIDMappings takes a requested user and group name and +// using the data from /etc/sub{uid,gid} ranges, creates the +// proper uid and gid remapping ranges for that user/group pair +func NewIDMappings(username, groupname string) (*IDMappings, error) { + subuidRanges, err := readSubuid(username) + if err != nil { + return nil, err + } + subgidRanges, err := readSubgid(groupname) + if err != nil { + return nil, err + } + if len(subuidRanges) == 0 { + return nil, fmt.Errorf("No subuid ranges found for user %q in %s", username, subuidFileName) + } + if len(subgidRanges) == 0 { + return nil, fmt.Errorf("No subgid ranges found for group %q in %s", groupname, subgidFileName) + } + + return &IDMappings{ + uids: createIDMap(subuidRanges), + gids: createIDMap(subgidRanges), + }, nil +} + +// NewIDMappingsFromMaps creates a new mapping from two slices +// Deprecated: this is a temporary shim while transitioning to IDMapping +func NewIDMappingsFromMaps(uids []IDMap, gids []IDMap) *IDMappings { + return &IDMappings{uids: uids, gids: gids} +} + +// RootPair returns a uid and gid pair for the root user. The error is ignored +// because a root user always exists, and the defaults are correct when the uid +// and gid maps are empty. +func (i *IDMappings) RootPair() IDPair { + uid, gid, _ := GetRootUIDGID(i.uids, i.gids) + return IDPair{UID: uid, GID: gid} +} + +// ToHost returns the host UID and GID for the container uid, gid. +// Remapping is only performed if the ids aren't already the remapped root ids +func (i *IDMappings) ToHost(pair IDPair) (IDPair, error) { + var err error + var target IDPair + + target.UID, err = RawToHost(pair.UID, i.uids) + if err != nil { + return target, err + } + + target.GID, err = RawToHost(pair.GID, i.gids) + return target, err +} + +// ToContainer returns the container UID and GID for the host uid and gid +func (i *IDMappings) ToContainer(pair IDPair) (int, int, error) { + uid, err := RawToContainer(pair.UID, i.uids) + if err != nil { + return -1, -1, err + } + gid, err := RawToContainer(pair.GID, i.gids) + return uid, gid, err +} + +// Empty returns true if there are no id mappings +func (i *IDMappings) Empty() bool { + return len(i.uids) == 0 && len(i.gids) == 0 +} + +// UIDs return the UID mapping +// TODO: remove this once everything has been refactored to use pairs +func (i *IDMappings) UIDs() []IDMap { + return i.uids +} + +// GIDs return the UID mapping +// TODO: remove this once everything has been refactored to use pairs +func (i *IDMappings) GIDs() []IDMap { + return i.gids +} + +func createIDMap(subidRanges ranges) []IDMap { + idMap := []IDMap{} + + // sort the ranges by lowest ID first + sort.Sort(subidRanges) + containerID := 0 + for _, idrange := range subidRanges { + idMap = append(idMap, IDMap{ + ContainerID: containerID, + HostID: idrange.Start, + Size: idrange.Length, + }) + containerID = containerID + idrange.Length + } + return idMap +} + +// parseSubidFile will read the appropriate file (/etc/subuid or /etc/subgid) +// and return all found ranges for a specified username. If the special value +// "ALL" is supplied for username, then all ranges in the file will be returned +func parseSubidFile(path, username string) (ranges, error) { + var ( + rangeList ranges + uidstr string + ) + if u, err := user.Lookup(username); err == nil { + uidstr = u.Uid + } + + subidFile, err := os.Open(path) + if err != nil { + return rangeList, err + } + defer subidFile.Close() + + s := bufio.NewScanner(subidFile) + for s.Scan() { + if err := s.Err(); err != nil { + return rangeList, err + } + + text := strings.TrimSpace(s.Text()) + if text == "" || strings.HasPrefix(text, "#") { + continue + } + parts := strings.Split(text, ":") + if len(parts) != 3 { + return rangeList, fmt.Errorf("Cannot parse subuid/gid information: Format not correct for %s file", path) + } + if parts[0] == username || username == "ALL" || (parts[0] == uidstr && parts[0] != "") { + startid, err := strconv.Atoi(parts[1]) + if err != nil { + return rangeList, fmt.Errorf("String to int conversion failed during subuid/gid parsing of %s: %v", path, err) + } + length, err := strconv.Atoi(parts[2]) + if err != nil { + return rangeList, fmt.Errorf("String to int conversion failed during subuid/gid parsing of %s: %v", path, err) + } + rangeList = append(rangeList, subIDRange{startid, length}) + } + } + return rangeList, nil +} + +func checkChownErr(err error, name string, uid, gid int) error { + if e, ok := err.(*os.PathError); ok && e.Err == syscall.EINVAL { + return errors.Wrapf(err, "potentially insufficient UIDs or GIDs available in user namespace (requested %d:%d for %s): Check /etc/subuid and /etc/subgid if configured locally", uid, gid, name) + } + return err +} + +func SafeChown(name string, uid, gid int) error { + if stat, statErr := system.Stat(name); statErr == nil { + if stat.UID() == uint32(uid) && stat.GID() == uint32(gid) { + return nil + } + } + return checkChownErr(os.Chown(name, uid, gid), name, uid, gid) +} + +func SafeLchown(name string, uid, gid int) error { + if stat, statErr := system.Lstat(name); statErr == nil { + if stat.UID() == uint32(uid) && stat.GID() == uint32(gid) { + return nil + } + } + return checkChownErr(os.Lchown(name, uid, gid), name, uid, gid) +} + +type sortByHostID []IDMap + +func (e sortByHostID) Len() int { return len(e) } +func (e sortByHostID) Swap(i, j int) { e[i], e[j] = e[j], e[i] } +func (e sortByHostID) Less(i, j int) bool { return e[i].HostID < e[j].HostID } + +type sortByContainerID []IDMap + +func (e sortByContainerID) Len() int { return len(e) } +func (e sortByContainerID) Swap(i, j int) { e[i], e[j] = e[j], e[i] } +func (e sortByContainerID) Less(i, j int) bool { return e[i].ContainerID < e[j].ContainerID } + +// IsContiguous checks if the specified mapping is contiguous and doesn't +// have any hole. +func IsContiguous(mappings []IDMap) bool { + if len(mappings) < 2 { + return true + } + + var mh sortByHostID = mappings[:] + sort.Sort(mh) + for i := 1; i < len(mh); i++ { + if mh[i].HostID != mh[i-1].HostID+mh[i-1].Size { + return false + } + } + + var mc sortByContainerID = mappings[:] + sort.Sort(mc) + for i := 1; i < len(mc); i++ { + if mc[i].ContainerID != mc[i-1].ContainerID+mc[i-1].Size { + return false + } + } + return true +} diff --git a/vendor/github.com/containers/storage/pkg/idtools/idtools_supported.go b/vendor/github.com/containers/storage/pkg/idtools/idtools_supported.go new file mode 100644 index 0000000000..6e6e3b22bc --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/idtools/idtools_supported.go @@ -0,0 +1,71 @@ +// +build linux,cgo,libsubid + +package idtools + +import ( + "unsafe" + + "github.com/pkg/errors" +) + +/* +#cgo LDFLAGS: -l subid +#include +#include +#include +const char *Prog = "storage"; +FILE *shadow_logfd = NULL; + +struct subid_range get_range(struct subid_range *ranges, int i) +{ + shadow_logfd = stderr; + return ranges[i]; +} + +#if !defined(SUBID_ABI_MAJOR) || (SUBID_ABI_MAJOR < 4) +# define subid_get_uid_ranges get_subuid_ranges +# define subid_get_gid_ranges get_subgid_ranges +#endif + +*/ +import "C" + +func readSubid(username string, isUser bool) (ranges, error) { + var ret ranges + if username == "ALL" { + return nil, errors.New("username ALL not supported") + } + + cUsername := C.CString(username) + defer C.free(unsafe.Pointer(cUsername)) + + var nRanges C.int + var cRanges *C.struct_subid_range + if isUser { + nRanges = C.subid_get_uid_ranges(cUsername, &cRanges) + } else { + nRanges = C.subid_get_gid_ranges(cUsername, &cRanges) + } + if nRanges < 0 { + return nil, errors.New("cannot read subids") + } + defer C.free(unsafe.Pointer(cRanges)) + + for i := 0; i < int(nRanges); i++ { + r := C.get_range(cRanges, C.int(i)) + newRange := subIDRange{ + Start: int(r.start), + Length: int(r.count), + } + ret = append(ret, newRange) + } + return ret, nil +} + +func readSubuid(username string) (ranges, error) { + return readSubid(username, true) +} + +func readSubgid(username string) (ranges, error) { + return readSubid(username, false) +} diff --git a/vendor/github.com/containers/storage/pkg/idtools/idtools_unix.go b/vendor/github.com/containers/storage/pkg/idtools/idtools_unix.go new file mode 100644 index 0000000000..7f270c61f8 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/idtools/idtools_unix.go @@ -0,0 +1,213 @@ +// +build !windows + +package idtools + +import ( + "bytes" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "sync" + "syscall" + + "github.com/containers/storage/pkg/system" + "github.com/opencontainers/runc/libcontainer/user" +) + +var ( + entOnce sync.Once + getentCmd string +) + +func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chownExisting bool) error { + // make an array containing the original path asked for, plus (for mkAll == true) + // all path components leading up to the complete path that don't exist before we MkdirAll + // so that we can chown all of them properly at the end. If chownExisting is false, we won't + // chown the full directory path if it exists + var paths []string + st, err := os.Stat(path) + if err != nil && os.IsNotExist(err) { + paths = []string{path} + } else if err == nil { + if !st.IsDir() { + return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR} + } + if chownExisting { + // short-circuit--we were called with an existing directory and chown was requested + return SafeChown(path, ownerUID, ownerGID) + } + // nothing to do; directory exists and chown was NOT requested + return nil + } + + if mkAll { + // walk back to "/" looking for directories which do not exist + // and add them to the paths array for chown after creation + dirPath := path + if !filepath.IsAbs(dirPath) { + return fmt.Errorf("path: %s should be absolute", dirPath) + } + for { + dirPath = filepath.Dir(dirPath) + if dirPath == "/" { + break + } + if _, err := os.Stat(dirPath); err != nil && os.IsNotExist(err) { + paths = append(paths, dirPath) + } + } + if err := os.MkdirAll(path, mode); err != nil { + return err + } + } else { + if err := os.Mkdir(path, mode); err != nil && !os.IsExist(err) { + return err + } + } + // even if it existed, we will chown the requested path + any subpaths that + // didn't exist when we called MkdirAll + for _, pathComponent := range paths { + if err := SafeChown(pathComponent, ownerUID, ownerGID); err != nil { + return err + } + } + return nil +} + +// CanAccess takes a valid (existing) directory and a uid, gid pair and determines +// if that uid, gid pair has access (execute bit) to the directory +func CanAccess(path string, pair IDPair) bool { + statInfo, err := system.Stat(path) + if err != nil { + return false + } + fileMode := os.FileMode(statInfo.Mode()) + permBits := fileMode.Perm() + return accessible(statInfo.UID() == uint32(pair.UID), + statInfo.GID() == uint32(pair.GID), permBits) +} + +func accessible(isOwner, isGroup bool, perms os.FileMode) bool { + if isOwner && (perms&0100 == 0100) { + return true + } + if isGroup && (perms&0010 == 0010) { + return true + } + if perms&0001 == 0001 { + return true + } + return false +} + +// LookupUser uses traditional local system files lookup (from libcontainer/user) on a username, +// followed by a call to `getent` for supporting host configured non-files passwd and group dbs +func LookupUser(username string) (user.User, error) { + // first try a local system files lookup using existing capabilities + usr, err := user.LookupUser(username) + if err == nil { + return usr, nil + } + // local files lookup failed; attempt to call `getent` to query configured passwd dbs + usr, err = getentUser(fmt.Sprintf("%s %s", "passwd", username)) + if err != nil { + return user.User{}, err + } + return usr, nil +} + +// LookupUID uses traditional local system files lookup (from libcontainer/user) on a uid, +// followed by a call to `getent` for supporting host configured non-files passwd and group dbs +func LookupUID(uid int) (user.User, error) { + // first try a local system files lookup using existing capabilities + usr, err := user.LookupUid(uid) + if err == nil { + return usr, nil + } + // local files lookup failed; attempt to call `getent` to query configured passwd dbs + return getentUser(fmt.Sprintf("%s %d", "passwd", uid)) +} + +func getentUser(args string) (user.User, error) { + reader, err := callGetent(args) + if err != nil { + return user.User{}, err + } + users, err := user.ParsePasswd(reader) + if err != nil { + return user.User{}, err + } + if len(users) == 0 { + return user.User{}, fmt.Errorf("getent failed to find passwd entry for %q", strings.Split(args, " ")[1]) + } + return users[0], nil +} + +// LookupGroup uses traditional local system files lookup (from libcontainer/user) on a group name, +// followed by a call to `getent` for supporting host configured non-files passwd and group dbs +func LookupGroup(groupname string) (user.Group, error) { + // first try a local system files lookup using existing capabilities + group, err := user.LookupGroup(groupname) + if err == nil { + return group, nil + } + // local files lookup failed; attempt to call `getent` to query configured group dbs + return getentGroup(fmt.Sprintf("%s %s", "group", groupname)) +} + +// LookupGID uses traditional local system files lookup (from libcontainer/user) on a group ID, +// followed by a call to `getent` for supporting host configured non-files passwd and group dbs +func LookupGID(gid int) (user.Group, error) { + // first try a local system files lookup using existing capabilities + group, err := user.LookupGid(gid) + if err == nil { + return group, nil + } + // local files lookup failed; attempt to call `getent` to query configured group dbs + return getentGroup(fmt.Sprintf("%s %d", "group", gid)) +} + +func getentGroup(args string) (user.Group, error) { + reader, err := callGetent(args) + if err != nil { + return user.Group{}, err + } + groups, err := user.ParseGroup(reader) + if err != nil { + return user.Group{}, err + } + if len(groups) == 0 { + return user.Group{}, fmt.Errorf("getent failed to find groups entry for %q", strings.Split(args, " ")[1]) + } + return groups[0], nil +} + +func callGetent(args string) (io.Reader, error) { + entOnce.Do(func() { getentCmd, _ = resolveBinary("getent") }) + // if no `getent` command on host, can't do anything else + if getentCmd == "" { + return nil, fmt.Errorf("") + } + out, err := execCmd(getentCmd, args) + if err != nil { + exitCode, errC := system.GetExitCode(err) + if errC != nil { + return nil, err + } + switch exitCode { + case 1: + return nil, fmt.Errorf("getent reported invalid parameters/database unknown") + case 2: + terms := strings.Split(args, " ") + return nil, fmt.Errorf("getent unable to find entry %q in %s database", terms[1], terms[0]) + case 3: + return nil, fmt.Errorf("getent database doesn't support enumeration") + default: + return nil, err + } + + } + return bytes.NewReader(out), nil +} diff --git a/vendor/github.com/containers/storage/pkg/idtools/idtools_unsupported.go b/vendor/github.com/containers/storage/pkg/idtools/idtools_unsupported.go new file mode 100644 index 0000000000..84da1b764b --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/idtools/idtools_unsupported.go @@ -0,0 +1,11 @@ +// +build !linux !libsubid !cgo + +package idtools + +func readSubuid(username string) (ranges, error) { + return parseSubidFile(subuidFileName, username) +} + +func readSubgid(username string) (ranges, error) { + return parseSubidFile(subgidFileName, username) +} diff --git a/vendor/github.com/containers/storage/pkg/idtools/idtools_windows.go b/vendor/github.com/containers/storage/pkg/idtools/idtools_windows.go new file mode 100644 index 0000000000..16be94f446 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/idtools/idtools_windows.go @@ -0,0 +1,23 @@ +// +build windows + +package idtools + +import ( + "os" +) + +// Platforms such as Windows do not support the UID/GID concept. So make this +// just a wrapper around system.MkdirAll. +func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chownExisting bool) error { + if err := os.MkdirAll(path, mode); err != nil { + return err + } + return nil +} + +// CanAccess takes a valid (existing) directory and a uid, gid pair and determines +// if that uid, gid pair has access (execute bit) to the directory +// Windows does not require/support this function, so always return true +func CanAccess(path string, pair IDPair) bool { + return true +} diff --git a/vendor/github.com/containers/storage/pkg/idtools/parser.go b/vendor/github.com/containers/storage/pkg/idtools/parser.go new file mode 100644 index 0000000000..1c819a1f97 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/idtools/parser.go @@ -0,0 +1,59 @@ +package idtools + +import ( + "fmt" + "math" + "math/bits" + "strconv" + "strings" +) + +func parseTriple(spec []string) (container, host, size uint32, err error) { + cid, err := strconv.ParseUint(spec[0], 10, 32) + if err != nil { + return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[0], err) + } + hid, err := strconv.ParseUint(spec[1], 10, 32) + if err != nil { + return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[1], err) + } + sz, err := strconv.ParseUint(spec[2], 10, 32) + if err != nil { + return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[2], err) + } + return uint32(cid), uint32(hid), uint32(sz), nil +} + +// ParseIDMap parses idmap triples from string. +func ParseIDMap(mapSpec []string, mapSetting string) (idmap []IDMap, err error) { + stdErr := fmt.Errorf("error initializing ID mappings: %s setting is malformed expected [\"uint32:uint32:uint32\"]: %q", mapSetting, mapSpec) + for _, idMapSpec := range mapSpec { + if idMapSpec == "" { + continue + } + idSpec := strings.Split(idMapSpec, ":") + if len(idSpec)%3 != 0 { + return nil, stdErr + } + for i := range idSpec { + if i%3 != 0 { + continue + } + cid, hid, size, err := parseTriple(idSpec[i : i+3]) + if err != nil { + return nil, stdErr + } + // Avoid possible integer overflow on 32bit builds + if bits.UintSize == 32 && (cid > math.MaxInt32 || hid > math.MaxInt32 || size > math.MaxInt32) { + return nil, stdErr + } + mapping := IDMap{ + ContainerID: int(cid), + HostID: int(hid), + Size: int(size), + } + idmap = append(idmap, mapping) + } + } + return idmap, nil +} diff --git a/vendor/github.com/containers/storage/pkg/idtools/usergroupadd_linux.go b/vendor/github.com/containers/storage/pkg/idtools/usergroupadd_linux.go new file mode 100644 index 0000000000..3dd7bf2105 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/idtools/usergroupadd_linux.go @@ -0,0 +1,164 @@ +package idtools + +import ( + "fmt" + "regexp" + "sort" + "strconv" + "strings" + "sync" +) + +// add a user and/or group to Linux /etc/passwd, /etc/group using standard +// Linux distribution commands: +// adduser --system --shell /bin/false --disabled-login --disabled-password --no-create-home --group +// useradd -r -s /bin/false + +var ( + once sync.Once + userCommand string + + cmdTemplates = map[string]string{ + "adduser": "--system --shell /bin/false --no-create-home --disabled-login --disabled-password --group %s", + "useradd": "-r -s /bin/false %s", + "usermod": "-%s %d-%d %s", + } + + idOutRegexp = regexp.MustCompile(`uid=([0-9]+).*gid=([0-9]+)`) + // default length for a UID/GID subordinate range + defaultRangeLen = 65536 + defaultRangeStart = 100000 + userMod = "usermod" +) + +// AddNamespaceRangesUser takes a username and uses the standard system +// utility to create a system user/group pair used to hold the +// /etc/sub{uid,gid} ranges which will be used for user namespace +// mapping ranges in containers. +func AddNamespaceRangesUser(name string) (int, int, error) { + if err := addUser(name); err != nil { + return -1, -1, fmt.Errorf("Error adding user %q: %v", name, err) + } + + // Query the system for the created uid and gid pair + out, err := execCmd("id", name) + if err != nil { + return -1, -1, fmt.Errorf("Error trying to find uid/gid for new user %q: %v", name, err) + } + matches := idOutRegexp.FindStringSubmatch(strings.TrimSpace(string(out))) + if len(matches) != 3 { + return -1, -1, fmt.Errorf("Can't find uid, gid from `id` output: %q", string(out)) + } + uid, err := strconv.Atoi(matches[1]) + if err != nil { + return -1, -1, fmt.Errorf("Can't convert found uid (%s) to int: %v", matches[1], err) + } + gid, err := strconv.Atoi(matches[2]) + if err != nil { + return -1, -1, fmt.Errorf("Can't convert found gid (%s) to int: %v", matches[2], err) + } + + // Now we need to create the subuid/subgid ranges for our new user/group (system users + // do not get auto-created ranges in subuid/subgid) + + if err := createSubordinateRanges(name); err != nil { + return -1, -1, fmt.Errorf("Couldn't create subordinate ID ranges: %v", err) + } + return uid, gid, nil +} + +func addUser(userName string) error { + once.Do(func() { + // set up which commands are used for adding users/groups dependent on distro + if _, err := resolveBinary("adduser"); err == nil { + userCommand = "adduser" + } else if _, err := resolveBinary("useradd"); err == nil { + userCommand = "useradd" + } + }) + if userCommand == "" { + return fmt.Errorf("Cannot add user; no useradd/adduser binary found") + } + args := fmt.Sprintf(cmdTemplates[userCommand], userName) + out, err := execCmd(userCommand, args) + if err != nil { + return fmt.Errorf("Failed to add user with error: %v; output: %q", err, string(out)) + } + return nil +} + +func createSubordinateRanges(name string) error { + + // first, we should verify that ranges weren't automatically created + // by the distro tooling + ranges, err := readSubuid(name) + if err != nil { + return fmt.Errorf("Error while looking for subuid ranges for user %q: %v", name, err) + } + if len(ranges) == 0 { + // no UID ranges; let's create one + startID, err := findNextUIDRange() + if err != nil { + return fmt.Errorf("Can't find available subuid range: %v", err) + } + out, err := execCmd(userMod, fmt.Sprintf(cmdTemplates[userMod], "v", startID, startID+defaultRangeLen-1, name)) + if err != nil { + return fmt.Errorf("Unable to add subuid range to user: %q; output: %s, err: %v", name, out, err) + } + } + + ranges, err = readSubgid(name) + if err != nil { + return fmt.Errorf("Error while looking for subgid ranges for user %q: %v", name, err) + } + if len(ranges) == 0 { + // no GID ranges; let's create one + startID, err := findNextGIDRange() + if err != nil { + return fmt.Errorf("Can't find available subgid range: %v", err) + } + out, err := execCmd(userMod, fmt.Sprintf(cmdTemplates[userMod], "w", startID, startID+defaultRangeLen-1, name)) + if err != nil { + return fmt.Errorf("Unable to add subgid range to user: %q; output: %s, err: %v", name, out, err) + } + } + return nil +} + +func findNextUIDRange() (int, error) { + ranges, err := readSubuid("ALL") + if err != nil { + return -1, fmt.Errorf("Couldn't parse all ranges in /etc/subuid file: %v", err) + } + sort.Sort(ranges) + return findNextRangeStart(ranges) +} + +func findNextGIDRange() (int, error) { + ranges, err := readSubgid("ALL") + if err != nil { + return -1, fmt.Errorf("Couldn't parse all ranges in /etc/subgid file: %v", err) + } + sort.Sort(ranges) + return findNextRangeStart(ranges) +} + +func findNextRangeStart(rangeList ranges) (int, error) { + startID := defaultRangeStart + for _, arange := range rangeList { + if wouldOverlap(arange, startID) { + startID = arange.Start + arange.Length + } + } + return startID, nil +} + +func wouldOverlap(arange subIDRange, ID int) bool { + low := ID + high := ID + defaultRangeLen + if (low >= arange.Start && low <= arange.Start+arange.Length) || + (high <= arange.Start+arange.Length && high >= arange.Start) { + return true + } + return false +} diff --git a/vendor/github.com/containers/storage/pkg/idtools/usergroupadd_unsupported.go b/vendor/github.com/containers/storage/pkg/idtools/usergroupadd_unsupported.go new file mode 100644 index 0000000000..d98b354cbd --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/idtools/usergroupadd_unsupported.go @@ -0,0 +1,12 @@ +// +build !linux + +package idtools + +import "fmt" + +// AddNamespaceRangesUser takes a name and finds an unused uid, gid pair +// and calls the appropriate helper function to add the group and then +// the user to the group in /etc/group and /etc/passwd respectively. +func AddNamespaceRangesUser(name string) (int, int, error) { + return -1, -1, fmt.Errorf("No support for adding users or groups on this OS") +} diff --git a/vendor/github.com/containers/storage/pkg/idtools/utils_unix.go b/vendor/github.com/containers/storage/pkg/idtools/utils_unix.go new file mode 100644 index 0000000000..9703ecbd9d --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/idtools/utils_unix.go @@ -0,0 +1,32 @@ +// +build !windows + +package idtools + +import ( + "fmt" + "os/exec" + "path/filepath" + "strings" +) + +func resolveBinary(binname string) (string, error) { + binaryPath, err := exec.LookPath(binname) + if err != nil { + return "", err + } + resolvedPath, err := filepath.EvalSymlinks(binaryPath) + if err != nil { + return "", err + } + //only return no error if the final resolved binary basename + //matches what was searched for + if filepath.Base(resolvedPath) == binname { + return resolvedPath, nil + } + return "", fmt.Errorf("Binary %q does not resolve to a binary of that name in $PATH (%q)", binname, resolvedPath) +} + +func execCmd(cmd, args string) ([]byte, error) { + execCmd := exec.Command(cmd, strings.Split(args, " ")...) + return execCmd.CombinedOutput() +} diff --git a/vendor/github.com/containers/storage/pkg/lockfile/lockfile.go b/vendor/github.com/containers/storage/pkg/lockfile/lockfile.go new file mode 100644 index 0000000000..6a00141c3d --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/lockfile/lockfile.go @@ -0,0 +1,107 @@ +package lockfile + +import ( + "path/filepath" + "sync" + "time" + + "github.com/pkg/errors" +) + +// A Locker represents a file lock where the file is used to cache an +// identifier of the last party that made changes to whatever's being protected +// by the lock. +type Locker interface { + // Acquire a writer lock. + // The default unix implementation panics if: + // - opening the lockfile failed + // - tried to lock a read-only lock-file + Lock() + + // Acquire a writer lock recursively, allowing for recursive acquisitions + // within the same process space. + RecursiveLock() + + // Unlock the lock. + // The default unix implementation panics if: + // - unlocking an unlocked lock + // - if the lock counter is corrupted + Unlock() + + // Acquire a reader lock. + RLock() + + // Touch records, for others sharing the lock, that the caller was the + // last writer. It should only be called with the lock held. + Touch() error + + // Modified() checks if the most recent writer was a party other than the + // last recorded writer. It should only be called with the lock held. + Modified() (bool, error) + + // TouchedSince() checks if the most recent writer modified the file (likely using Touch()) after the specified time. + TouchedSince(when time.Time) bool + + // IsReadWrite() checks if the lock file is read-write + IsReadWrite() bool + + // Locked() checks if lock is locked for writing by a thread in this process + Locked() bool +} + +var ( + lockfiles map[string]Locker + lockfilesLock sync.Mutex +) + +// GetLockfile opens a read-write lock file, creating it if necessary. The +// Locker object may already be locked if the path has already been requested +// by the current process. +func GetLockfile(path string) (Locker, error) { + return getLockfile(path, false) +} + +// GetROLockfile opens a read-only lock file, creating it if necessary. The +// Locker object may already be locked if the path has already been requested +// by the current process. +func GetROLockfile(path string) (Locker, error) { + return getLockfile(path, true) +} + +// getLockfile returns a Locker object, possibly (depending on the platform) +// working inter-process, and associated with the specified path. +// +// If ro, the lock is a read-write lock and the returned Locker should correspond to the +// “lock for reading” (shared) operation; otherwise, the lock is either an exclusive lock, +// or a read-write lock and Locker should correspond to the “lock for writing” (exclusive) operation. +// +// WARNING: +// - The lock may or MAY NOT be inter-process. +// - There may or MAY NOT be an actual object on the filesystem created for the specified path. +// - Even if ro, the lock MAY be exclusive. +func getLockfile(path string, ro bool) (Locker, error) { + lockfilesLock.Lock() + defer lockfilesLock.Unlock() + if lockfiles == nil { + lockfiles = make(map[string]Locker) + } + cleanPath, err := filepath.Abs(path) + if err != nil { + return nil, errors.Wrapf(err, "error ensuring that path %q is an absolute path", path) + } + if locker, ok := lockfiles[cleanPath]; ok { + if ro && locker.IsReadWrite() { + return nil, errors.Errorf("lock %q is not a read-only lock", cleanPath) + } + if !ro && !locker.IsReadWrite() { + return nil, errors.Errorf("lock %q is not a read-write lock", cleanPath) + } + return locker, nil + } + locker, err := createLockerForPath(cleanPath, ro) // platform-dependent locker + if err != nil { + return nil, err + } + lockfiles[cleanPath] = locker + return locker, nil +} diff --git a/vendor/github.com/containers/storage/pkg/lockfile/lockfile_unix.go b/vendor/github.com/containers/storage/pkg/lockfile/lockfile_unix.go new file mode 100644 index 0000000000..fc080acbed --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/lockfile/lockfile_unix.go @@ -0,0 +1,262 @@ +// +build linux solaris darwin freebsd + +package lockfile + +import ( + "fmt" + "os" + "path/filepath" + "sync" + "time" + + "github.com/containers/storage/pkg/stringid" + "github.com/containers/storage/pkg/system" + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +type lockfile struct { + // rwMutex serializes concurrent reader-writer acquisitions in the same process space + rwMutex *sync.RWMutex + // stateMutex is used to synchronize concurrent accesses to the state below + stateMutex *sync.Mutex + counter int64 + file string + fd uintptr + lw string + locktype int16 + locked bool + ro bool + recursive bool +} + +// openLock opens the file at path and returns the corresponding file +// descriptor. Note that the path is opened read-only when ro is set. If ro +// is unset, openLock will open the path read-write and create the file if +// necessary. +func openLock(path string, ro bool) (fd int, err error) { + if ro { + fd, err = unix.Open(path, os.O_RDONLY|unix.O_CLOEXEC|os.O_CREATE, 0) + } else { + fd, err = unix.Open(path, + os.O_RDWR|unix.O_CLOEXEC|os.O_CREATE, + unix.S_IRUSR|unix.S_IWUSR|unix.S_IRGRP|unix.S_IROTH, + ) + } + + if err == nil { + return + } + + // the directory of the lockfile seems to be removed, try to create it + if os.IsNotExist(err) { + if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { + return fd, errors.Wrap(err, "creating locker directory") + } + + return openLock(path, ro) + } + + return +} + +// createLockerForPath returns a Locker object, possibly (depending on the platform) +// working inter-process and associated with the specified path. +// +// This function will be called at most once for each path value within a single process. +// +// If ro, the lock is a read-write lock and the returned Locker should correspond to the +// “lock for reading” (shared) operation; otherwise, the lock is either an exclusive lock, +// or a read-write lock and Locker should correspond to the “lock for writing” (exclusive) operation. +// +// WARNING: +// - The lock may or MAY NOT be inter-process. +// - There may or MAY NOT be an actual object on the filesystem created for the specified path. +// - Even if ro, the lock MAY be exclusive. +func createLockerForPath(path string, ro bool) (Locker, error) { + // Check if we can open the lock. + fd, err := openLock(path, ro) + if err != nil { + return nil, errors.Wrapf(err, "error opening %q", path) + } + unix.Close(fd) + + locktype := unix.F_WRLCK + if ro { + locktype = unix.F_RDLCK + } + return &lockfile{ + stateMutex: &sync.Mutex{}, + rwMutex: &sync.RWMutex{}, + file: path, + lw: stringid.GenerateRandomID(), + locktype: int16(locktype), + locked: false, + ro: ro}, nil +} + +// lock locks the lockfile via FCTNL(2) based on the specified type and +// command. +func (l *lockfile) lock(lType int16, recursive bool) { + lk := unix.Flock_t{ + Type: lType, + Whence: int16(os.SEEK_SET), + Start: 0, + Len: 0, + } + switch lType { + case unix.F_RDLCK: + l.rwMutex.RLock() + case unix.F_WRLCK: + if recursive { + // NOTE: that's okay as recursive is only set in RecursiveLock(), so + // there's no need to protect against hypothetical RDLCK cases. + l.rwMutex.RLock() + } else { + l.rwMutex.Lock() + } + default: + panic(fmt.Sprintf("attempted to acquire a file lock of unrecognized type %d", lType)) + } + l.stateMutex.Lock() + defer l.stateMutex.Unlock() + if l.counter == 0 { + // If we're the first reference on the lock, we need to open the file again. + fd, err := openLock(l.file, l.ro) + if err != nil { + panic(fmt.Sprintf("error opening %q: %v", l.file, err)) + } + l.fd = uintptr(fd) + + // Optimization: only use the (expensive) fcntl syscall when + // the counter is 0. In this case, we're either the first + // reader lock or a writer lock. + for unix.FcntlFlock(l.fd, unix.F_SETLKW, &lk) != nil { + time.Sleep(10 * time.Millisecond) + } + } + l.locktype = lType + l.locked = true + l.recursive = recursive + l.counter++ +} + +// Lock locks the lockfile as a writer. Panic if the lock is a read-only one. +func (l *lockfile) Lock() { + if l.ro { + panic("can't take write lock on read-only lock file") + } else { + l.lock(unix.F_WRLCK, false) + } +} + +// RecursiveLock locks the lockfile as a writer but allows for recursive +// acquisitions within the same process space. Note that RLock() will be called +// if it's a lockTypReader lock. +func (l *lockfile) RecursiveLock() { + if l.ro { + l.RLock() + } else { + l.lock(unix.F_WRLCK, true) + } +} + +// LockRead locks the lockfile as a reader. +func (l *lockfile) RLock() { + l.lock(unix.F_RDLCK, false) +} + +// Unlock unlocks the lockfile. +func (l *lockfile) Unlock() { + l.stateMutex.Lock() + if l.locked == false { + // Panic when unlocking an unlocked lock. That's a violation + // of the lock semantics and will reveal such. + panic("calling Unlock on unlocked lock") + } + l.counter-- + if l.counter < 0 { + // Panic when the counter is negative. There is no way we can + // recover from a corrupted lock and we need to protect the + // storage from corruption. + panic(fmt.Sprintf("lock %q has been unlocked too often", l.file)) + } + if l.counter == 0 { + // We should only release the lock when the counter is 0 to + // avoid releasing read-locks too early; a given process may + // acquire a read lock multiple times. + l.locked = false + // Close the file descriptor on the last unlock, releasing the + // file lock. + unix.Close(int(l.fd)) + } + if l.locktype == unix.F_RDLCK || l.recursive { + l.rwMutex.RUnlock() + } else { + l.rwMutex.Unlock() + } + l.stateMutex.Unlock() +} + +// Locked checks if lockfile is locked for writing by a thread in this process. +func (l *lockfile) Locked() bool { + l.stateMutex.Lock() + defer l.stateMutex.Unlock() + return l.locked && (l.locktype == unix.F_WRLCK) +} + +// Touch updates the lock file with the UID of the user. +func (l *lockfile) Touch() error { + l.stateMutex.Lock() + if !l.locked || (l.locktype != unix.F_WRLCK) { + panic("attempted to update last-writer in lockfile without the write lock") + } + defer l.stateMutex.Unlock() + l.lw = stringid.GenerateRandomID() + id := []byte(l.lw) + n, err := unix.Pwrite(int(l.fd), id, 0) + if err != nil { + return err + } + if n != len(id) { + return unix.ENOSPC + } + return nil +} + +// Modified indicates if the lockfile has been updated since the last time it +// was loaded. +func (l *lockfile) Modified() (bool, error) { + l.stateMutex.Lock() + id := []byte(l.lw) + if !l.locked { + panic("attempted to check last-writer in lockfile without locking it first") + } + defer l.stateMutex.Unlock() + n, err := unix.Pread(int(l.fd), id, 0) + if err != nil { + return true, err + } + if n != len(id) { + return true, nil + } + lw := l.lw + l.lw = string(id) + return l.lw != lw, nil +} + +// IsReadWriteLock indicates if the lock file is a read-write lock. +func (l *lockfile) IsReadWrite() bool { + return !l.ro +} + +// TouchedSince indicates if the lock file has been touched since the specified time +func (l *lockfile) TouchedSince(when time.Time) bool { + st, err := system.Fstat(int(l.fd)) + if err != nil { + return true + } + mtim := st.Mtim() + touched := time.Unix(mtim.Unix()) + return when.Before(touched) +} diff --git a/vendor/github.com/containers/storage/pkg/lockfile/lockfile_windows.go b/vendor/github.com/containers/storage/pkg/lockfile/lockfile_windows.go new file mode 100644 index 0000000000..82bd91db9a --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/lockfile/lockfile_windows.go @@ -0,0 +1,75 @@ +// +build windows + +package lockfile + +import ( + "os" + "sync" + "time" +) + +// createLockerForPath returns a Locker object, possibly (depending on the platform) +// working inter-process and associated with the specified path. +// +// This function will be called at most once for each path value within a single process. +// +// If ro, the lock is a read-write lock and the returned Locker should correspond to the +// “lock for reading” (shared) operation; otherwise, the lock is either an exclusive lock, +// or a read-write lock and Locker should correspond to the “lock for writing” (exclusive) operation. +// +// WARNING: +// - The lock may or MAY NOT be inter-process. +// - There may or MAY NOT be an actual object on the filesystem created for the specified path. +// - Even if ro, the lock MAY be exclusive. +func createLockerForPath(path string, ro bool) (Locker, error) { + return &lockfile{locked: false}, nil +} + +type lockfile struct { + mu sync.Mutex + file string + locked bool +} + +func (l *lockfile) Lock() { + l.mu.Lock() + l.locked = true +} + +func (l *lockfile) RecursiveLock() { + // We don't support Windows but a recursive writer-lock in one process-space + // is really a writer lock, so just panic. + panic("not supported") +} + +func (l *lockfile) RLock() { + l.mu.Lock() + l.locked = true +} + +func (l *lockfile) Unlock() { + l.locked = false + l.mu.Unlock() +} + +func (l *lockfile) Locked() bool { + return l.locked +} + +func (l *lockfile) Modified() (bool, error) { + return false, nil +} +func (l *lockfile) Touch() error { + return nil +} +func (l *lockfile) IsReadWrite() bool { + return false +} + +func (l *lockfile) TouchedSince(when time.Time) bool { + stat, err := os.Stat(l.file) + if err != nil { + return true + } + return when.Before(stat.ModTime()) +} diff --git a/vendor/github.com/containers/storage/pkg/mount/flags.go b/vendor/github.com/containers/storage/pkg/mount/flags.go new file mode 100644 index 0000000000..07a0f4847c --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/mount/flags.go @@ -0,0 +1,149 @@ +package mount + +import ( + "fmt" + "strings" +) + +var flags = map[string]struct { + clear bool + flag int +}{ + "defaults": {false, 0}, + "ro": {false, RDONLY}, + "rw": {true, RDONLY}, + "suid": {true, NOSUID}, + "nosuid": {false, NOSUID}, + "dev": {true, NODEV}, + "nodev": {false, NODEV}, + "exec": {true, NOEXEC}, + "noexec": {false, NOEXEC}, + "sync": {false, SYNCHRONOUS}, + "async": {true, SYNCHRONOUS}, + "dirsync": {false, DIRSYNC}, + "remount": {false, REMOUNT}, + "mand": {false, MANDLOCK}, + "nomand": {true, MANDLOCK}, + "atime": {true, NOATIME}, + "noatime": {false, NOATIME}, + "diratime": {true, NODIRATIME}, + "nodiratime": {false, NODIRATIME}, + "bind": {false, BIND}, + "rbind": {false, RBIND}, + "unbindable": {false, UNBINDABLE}, + "runbindable": {false, RUNBINDABLE}, + "private": {false, PRIVATE}, + "rprivate": {false, RPRIVATE}, + "shared": {false, SHARED}, + "rshared": {false, RSHARED}, + "slave": {false, SLAVE}, + "rslave": {false, RSLAVE}, + "relatime": {false, RELATIME}, + "norelatime": {true, RELATIME}, + "strictatime": {false, STRICTATIME}, + "nostrictatime": {true, STRICTATIME}, +} + +var validFlags = map[string]bool{ + "": true, + "size": true, + "mode": true, + "uid": true, + "gid": true, + "nr_inodes": true, + "nr_blocks": true, + "mpol": true, +} + +var propagationFlags = map[string]bool{ + "bind": true, + "rbind": true, + "unbindable": true, + "runbindable": true, + "private": true, + "rprivate": true, + "shared": true, + "rshared": true, + "slave": true, + "rslave": true, +} + +// MergeTmpfsOptions merge mount options to make sure there is no duplicate. +func MergeTmpfsOptions(options []string) ([]string, error) { + // We use collisions maps to remove duplicates. + // For flag, the key is the flag value (the key for propagation flag is -1) + // For data=value, the key is the data + flagCollisions := map[int]bool{} + dataCollisions := map[string]bool{} + + var newOptions []string + // We process in reverse order + for i := len(options) - 1; i >= 0; i-- { + option := options[i] + if option == "defaults" { + continue + } + if f, ok := flags[option]; ok && f.flag != 0 { + // There is only one propagation mode + key := f.flag + if propagationFlags[option] { + key = -1 + } + // Check to see if there is collision for flag + if !flagCollisions[key] { + // We prepend the option and add to collision map + newOptions = append([]string{option}, newOptions...) + flagCollisions[key] = true + } + continue + } + opt := strings.SplitN(option, "=", 2) + if len(opt) != 2 || !validFlags[opt[0]] { + return nil, fmt.Errorf("Invalid tmpfs option %q", opt) + } + if !dataCollisions[opt[0]] { + // We prepend the option and add to collision map + newOptions = append([]string{option}, newOptions...) + dataCollisions[opt[0]] = true + } + } + + return newOptions, nil +} + +// ParseOptions parses fstab type mount options into mount() flags +// and device specific data +func ParseOptions(options string) (int, string) { + var ( + flag int + data []string + ) + + for _, o := range strings.Split(options, ",") { + // If the option does not exist in the flags table or the flag + // is not supported on the platform, + // then it is a data value for a specific fs type + if f, exists := flags[o]; exists && f.flag != 0 { + if f.clear { + flag &= ^f.flag + } else { + flag |= f.flag + } + } else { + data = append(data, o) + } + } + return flag, strings.Join(data, ",") +} + +// ParseTmpfsOptions parse fstab type mount options into flags and data +func ParseTmpfsOptions(options string) (int, string, error) { + flags, data := ParseOptions(options) + for _, o := range strings.Split(data, ",") { + opt := strings.SplitN(o, "=", 2) + if !validFlags[opt[0]] { + return 0, "", fmt.Errorf("Invalid tmpfs option %q", opt) + } + } + return flags, data, nil +} diff --git a/vendor/github.com/containers/storage/pkg/mount/flags_linux.go b/vendor/github.com/containers/storage/pkg/mount/flags_linux.go new file mode 100644 index 0000000000..0425d0dd63 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/mount/flags_linux.go @@ -0,0 +1,87 @@ +package mount + +import ( + "golang.org/x/sys/unix" +) + +const ( + // RDONLY will mount the file system read-only. + RDONLY = unix.MS_RDONLY + + // NOSUID will not allow set-user-identifier or set-group-identifier bits to + // take effect. + NOSUID = unix.MS_NOSUID + + // NODEV will not interpret character or block special devices on the file + // system. + NODEV = unix.MS_NODEV + + // NOEXEC will not allow execution of any binaries on the mounted file system. + NOEXEC = unix.MS_NOEXEC + + // SYNCHRONOUS will allow I/O to the file system to be done synchronously. + SYNCHRONOUS = unix.MS_SYNCHRONOUS + + // DIRSYNC will force all directory updates within the file system to be done + // synchronously. This affects the following system calls: create, link, + // unlink, symlink, mkdir, rmdir, mknod and rename. + DIRSYNC = unix.MS_DIRSYNC + + // REMOUNT will attempt to remount an already-mounted file system. This is + // commonly used to change the mount flags for a file system, especially to + // make a readonly file system writeable. It does not change device or mount + // point. + REMOUNT = unix.MS_REMOUNT + + // MANDLOCK will force mandatory locks on a filesystem. + MANDLOCK = unix.MS_MANDLOCK + + // NOATIME will not update the file access time when reading from a file. + NOATIME = unix.MS_NOATIME + + // NODIRATIME will not update the directory access time. + NODIRATIME = unix.MS_NODIRATIME + + // BIND remounts a subtree somewhere else. + BIND = unix.MS_BIND + + // RBIND remounts a subtree and all possible submounts somewhere else. + RBIND = unix.MS_BIND | unix.MS_REC + + // UNBINDABLE creates a mount which cannot be cloned through a bind operation. + UNBINDABLE = unix.MS_UNBINDABLE + + // RUNBINDABLE marks the entire mount tree as UNBINDABLE. + RUNBINDABLE = unix.MS_UNBINDABLE | unix.MS_REC + + // PRIVATE creates a mount which carries no propagation abilities. + PRIVATE = unix.MS_PRIVATE + + // RPRIVATE marks the entire mount tree as PRIVATE. + RPRIVATE = unix.MS_PRIVATE | unix.MS_REC + + // SLAVE creates a mount which receives propagation from its master, but not + // vice versa. + SLAVE = unix.MS_SLAVE + + // RSLAVE marks the entire mount tree as SLAVE. + RSLAVE = unix.MS_SLAVE | unix.MS_REC + + // SHARED creates a mount which provides the ability to create mirrors of + // that mount such that mounts and unmounts within any of the mirrors + // propagate to the other mirrors. + SHARED = unix.MS_SHARED + + // RSHARED marks the entire mount tree as SHARED. + RSHARED = unix.MS_SHARED | unix.MS_REC + + // RELATIME updates inode access times relative to modify or change time. + RELATIME = unix.MS_RELATIME + + // STRICTATIME allows to explicitly request full atime updates. This makes + // it possible for the kernel to default to relatime or noatime but still + // allow userspace to override it. + STRICTATIME = unix.MS_STRICTATIME + + mntDetach = unix.MNT_DETACH +) diff --git a/vendor/github.com/containers/storage/pkg/mount/flags_unsupported.go b/vendor/github.com/containers/storage/pkg/mount/flags_unsupported.go new file mode 100644 index 0000000000..9afd26d4c0 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/mount/flags_unsupported.go @@ -0,0 +1,31 @@ +// +build !linux + +package mount + +// These flags are unsupported. +const ( + BIND = 0 + DIRSYNC = 0 + MANDLOCK = 0 + NOATIME = 0 + NODEV = 0 + NODIRATIME = 0 + NOEXEC = 0 + NOSUID = 0 + UNBINDABLE = 0 + RUNBINDABLE = 0 + PRIVATE = 0 + RPRIVATE = 0 + SHARED = 0 + RSHARED = 0 + SLAVE = 0 + RSLAVE = 0 + RBIND = 0 + RELATIME = 0 + RELATIVE = 0 + REMOUNT = 0 + STRICTATIME = 0 + SYNCHRONOUS = 0 + RDONLY = 0 + mntDetach = 0 +) diff --git a/vendor/github.com/containers/storage/pkg/mount/mount.go b/vendor/github.com/containers/storage/pkg/mount/mount.go new file mode 100644 index 0000000000..23c5c44ac0 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/mount/mount.go @@ -0,0 +1,110 @@ +package mount + +import ( + "sort" + "strconv" + "strings" +) + +// mountError holds an error from a mount or unmount operation +type mountError struct { + op string + source, target string + flags uintptr + data string + err error +} + +// Error returns a string representation of mountError +func (e *mountError) Error() string { + out := e.op + " " + + if e.source != "" { + out += e.source + ":" + e.target + } else { + out += e.target + } + + if e.flags != uintptr(0) { + out += ", flags: 0x" + strconv.FormatUint(uint64(e.flags), 16) + } + if e.data != "" { + out += ", data: " + e.data + } + + out += ": " + e.err.Error() + return out +} + +// Cause returns the underlying cause of the error +func (e *mountError) Cause() error { + return e.err +} + +// Unwrap returns the underlying cause of the error +func (e *mountError) Unwrap() error { + return e.err +} + +// Mount will mount filesystem according to the specified configuration, on the +// condition that the target path is *not* already mounted. Options must be +// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See +// flags.go for supported option flags. +func Mount(device, target, mType, options string) error { + flag, data := ParseOptions(options) + if flag&REMOUNT != REMOUNT { + if mounted, err := Mounted(target); err != nil || mounted { + return err + } + } + return mount(device, target, mType, uintptr(flag), data) +} + +// ForceMount will mount a filesystem according to the specified configuration, +// *regardless* if the target path is not already mounted. Options must be +// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See +// flags.go for supported option flags. +func ForceMount(device, target, mType, options string) error { + flag, data := ParseOptions(options) + return mount(device, target, mType, uintptr(flag), data) +} + +// Unmount lazily unmounts a filesystem on supported platforms, otherwise +// does a normal unmount. +func Unmount(target string) error { + return unmount(target, mntDetach) +} + +// RecursiveUnmount unmounts the target and all mounts underneath, starting with +// the deepest mount first. +func RecursiveUnmount(target string) error { + mounts, err := GetMounts() + if err != nil { + return err + } + + // Make the deepest mount be first + sort.Slice(mounts, func(i, j int) bool { + return len(mounts[i].Mountpoint) > len(mounts[j].Mountpoint) + }) + + for i, m := range mounts { + if !strings.HasPrefix(m.Mountpoint, target) { + continue + } + if err := Unmount(m.Mountpoint); err != nil && i == len(mounts)-1 { + return err + // Ignore errors for submounts and continue trying to unmount others + // The final unmount should fail if there are any submounts remaining + } + } + return nil +} + +// ForceUnmount lazily unmounts a filesystem on supported platforms, +// otherwise does a normal unmount. +// +// Deprecated: please use Unmount instead, it is identical. +func ForceUnmount(target string) error { + return unmount(target, mntDetach) +} diff --git a/vendor/github.com/containers/storage/pkg/mount/mounter_freebsd.go b/vendor/github.com/containers/storage/pkg/mount/mounter_freebsd.go new file mode 100644 index 0000000000..b31cf99d0f --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/mount/mounter_freebsd.go @@ -0,0 +1,54 @@ +package mount + +/* +#include +#include +#include +#include +#include +#include +*/ +import "C" + +import ( + "fmt" + "strings" + "unsafe" +) + +func allocateIOVecs(options []string) []C.struct_iovec { + out := make([]C.struct_iovec, len(options)) + for i, option := range options { + out[i].iov_base = unsafe.Pointer(C.CString(option)) + out[i].iov_len = C.size_t(len(option) + 1) + } + return out +} + +func mount(device, target, mType string, flag uintptr, data string) error { + isNullFS := false + + xs := strings.Split(data, ",") + for _, x := range xs { + if x == "bind" { + isNullFS = true + } + } + + options := []string{"fspath", target} + if isNullFS { + options = append(options, "fstype", "nullfs", "target", device) + } else { + options = append(options, "fstype", mType, "from", device) + } + rawOptions := allocateIOVecs(options) + for _, rawOption := range rawOptions { + defer C.free(rawOption.iov_base) + } + + if errno := C.nmount(&rawOptions[0], C.uint(len(options)), C.int(flag)); errno != 0 { + reason := C.GoString(C.strerror(*C.__error())) + return fmt.Errorf("Failed to call nmount: %s", reason) + } + return nil +} diff --git a/vendor/github.com/containers/storage/pkg/mount/mounter_linux.go b/vendor/github.com/containers/storage/pkg/mount/mounter_linux.go new file mode 100644 index 0000000000..594cd0881a --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/mount/mounter_linux.go @@ -0,0 +1,74 @@ +package mount + +import ( + "golang.org/x/sys/unix" +) + +const ( + // ptypes is the set propagation types. + ptypes = unix.MS_SHARED | unix.MS_PRIVATE | unix.MS_SLAVE | unix.MS_UNBINDABLE + + // pflags is the full set valid flags for a change propagation call. + pflags = ptypes | unix.MS_REC | unix.MS_SILENT + + // broflags is the combination of bind and read only + broflags = unix.MS_BIND | unix.MS_RDONLY + + none = "none" +) + +// isremount returns true if either device name or flags identify a remount request, false otherwise. +func isremount(device string, flags uintptr) bool { + switch { + // We treat device "" and "none" as a remount request to provide compatibility with + // requests that don't explicitly set MS_REMOUNT such as those manipulating bind mounts. + case flags&unix.MS_REMOUNT != 0, device == "", device == none: + return true + default: + return false + } +} + +func mount(device, target, mType string, flags uintptr, data string) error { + oflags := flags &^ ptypes + if !isremount(device, flags) || data != "" { + // Initial call applying all non-propagation flags for mount + // or remount with changed data + if err := unix.Mount(device, target, mType, oflags, data); err != nil { + return &mountError{ + op: "mount", + source: device, + target: target, + flags: oflags, + data: data, + err: err, + } + } + } + + if flags&ptypes != 0 { + // Change the propagation type. + if err := unix.Mount("", target, "", flags&pflags, ""); err != nil { + return &mountError{ + op: "remount", + target: target, + flags: flags & pflags, + err: err, + } + } + } + + if oflags&broflags == broflags { + // Remount the bind to apply read only. + if err := unix.Mount("", target, "", oflags|unix.MS_REMOUNT, ""); err != nil { + return &mountError{ + op: "remount-ro", + target: target, + flags: oflags | unix.MS_REMOUNT, + err: err, + } + } + } + + return nil +} diff --git a/vendor/github.com/containers/storage/pkg/mount/mounter_unsupported.go b/vendor/github.com/containers/storage/pkg/mount/mounter_unsupported.go new file mode 100644 index 0000000000..9d20cfbf86 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/mount/mounter_unsupported.go @@ -0,0 +1,7 @@ +// +build !linux,!freebsd + +package mount + +func mount(device, target, mType string, flag uintptr, data string) error { + panic("Not implemented") +} diff --git a/vendor/github.com/containers/storage/pkg/mount/mountinfo.go b/vendor/github.com/containers/storage/pkg/mount/mountinfo.go new file mode 100644 index 0000000000..bb2da474f4 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/mount/mountinfo.go @@ -0,0 +1,13 @@ +package mount + +import ( + "github.com/moby/sys/mountinfo" +) + +type Info = mountinfo.Info + +var Mounted = mountinfo.Mounted + +func GetMounts() ([]*Info, error) { + return mountinfo.GetMounts(nil) +} diff --git a/vendor/github.com/containers/storage/pkg/mount/mountinfo_linux.go b/vendor/github.com/containers/storage/pkg/mount/mountinfo_linux.go new file mode 100644 index 0000000000..cbc0299fb5 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/mount/mountinfo_linux.go @@ -0,0 +1,5 @@ +package mount + +import "github.com/moby/sys/mountinfo" + +var PidMountInfo = mountinfo.PidMountInfo diff --git a/vendor/github.com/containers/storage/pkg/mount/sharedsubtree_linux.go b/vendor/github.com/containers/storage/pkg/mount/sharedsubtree_linux.go new file mode 100644 index 0000000000..80922ad5ca --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/mount/sharedsubtree_linux.go @@ -0,0 +1,64 @@ +package mount + +// MakeShared ensures a mounted filesystem has the SHARED mount option enabled. +// See the supported options in flags.go for further reference. +func MakeShared(mountPoint string) error { + return ensureMountedAs(mountPoint, SHARED) +} + +// MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled. +// See the supported options in flags.go for further reference. +func MakeRShared(mountPoint string) error { + return ensureMountedAs(mountPoint, RSHARED) +} + +// MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled. +// See the supported options in flags.go for further reference. +func MakePrivate(mountPoint string) error { + return ensureMountedAs(mountPoint, PRIVATE) +} + +// MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option +// enabled. See the supported options in flags.go for further reference. +func MakeRPrivate(mountPoint string) error { + return ensureMountedAs(mountPoint, RPRIVATE) +} + +// MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled. +// See the supported options in flags.go for further reference. +func MakeSlave(mountPoint string) error { + return ensureMountedAs(mountPoint, SLAVE) +} + +// MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled. +// See the supported options in flags.go for further reference. +func MakeRSlave(mountPoint string) error { + return ensureMountedAs(mountPoint, RSLAVE) +} + +// MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option +// enabled. See the supported options in flags.go for further reference. +func MakeUnbindable(mountPoint string) error { + return ensureMountedAs(mountPoint, UNBINDABLE) +} + +// MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount +// option enabled. See the supported options in flags.go for further reference. +func MakeRUnbindable(mountPoint string) error { + return ensureMountedAs(mountPoint, RUNBINDABLE) +} + +func ensureMountedAs(mnt string, flags int) error { + mounted, err := Mounted(mnt) + if err != nil { + return err + } + + if !mounted { + if err := mount(mnt, mnt, "none", uintptr(BIND), ""); err != nil { + return err + } + } + + return mount("", mnt, "none", uintptr(flags), "") +} diff --git a/vendor/github.com/containers/storage/pkg/mount/unmount_unix.go b/vendor/github.com/containers/storage/pkg/mount/unmount_unix.go new file mode 100644 index 0000000000..1d1afeee2e --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/mount/unmount_unix.go @@ -0,0 +1,22 @@ +// +build !windows + +package mount + +import "golang.org/x/sys/unix" + +func unmount(target string, flags int) error { + err := unix.Unmount(target, flags) + if err == nil || err == unix.EINVAL { + // Ignore "not mounted" error here. Note the same error + // can be returned if flags are invalid, so this code + // assumes that the flags value is always correct. + return nil + } + + return &mountError{ + op: "umount", + target: target, + flags: uintptr(flags), + err: err, + } +} diff --git a/vendor/github.com/containers/storage/pkg/mount/unmount_unsupported.go b/vendor/github.com/containers/storage/pkg/mount/unmount_unsupported.go new file mode 100644 index 0000000000..eebc4ab84e --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/mount/unmount_unsupported.go @@ -0,0 +1,7 @@ +// +build windows + +package mount + +func unmount(target string, flag int) error { + panic("Not implemented") +} diff --git a/vendor/github.com/containers/storage/pkg/reexec/README.md b/vendor/github.com/containers/storage/pkg/reexec/README.md new file mode 100644 index 0000000000..6658f69b69 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/reexec/README.md @@ -0,0 +1,5 @@ +# reexec + +The `reexec` package facilitates the busybox style reexec of the docker binary that we require because +of the forking limitations of using Go. Handlers can be registered with a name and the argv 0 of +the exec of the binary will be used to find and execute custom init paths. diff --git a/vendor/github.com/containers/storage/pkg/reexec/command_linux.go b/vendor/github.com/containers/storage/pkg/reexec/command_linux.go new file mode 100644 index 0000000000..d3dd86d349 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/reexec/command_linux.go @@ -0,0 +1,34 @@ +// +build linux + +package reexec + +import ( + "context" + "os/exec" +) + +// Self returns the path to the current process's binary. +// Returns "/proc/self/exe". +func Self() string { + return "/proc/self/exe" +} + +// Command returns *exec.Cmd which has Path as current binary. +// This will use the in-memory version (/proc/self/exe) of the current binary, +// it is thus safe to delete or replace the on-disk binary (os.Args[0]). +func Command(args ...string) *exec.Cmd { + panicIfNotInitialized() + cmd := exec.Command(Self()) + cmd.Args = args + return cmd +} + +// CommandContext returns *exec.Cmd which has Path as current binary. +// This will use the in-memory version (/proc/self/exe) of the current binary, +// it is thus safe to delete or replace the on-disk binary (os.Args[0]). +func CommandContext(ctx context.Context, args ...string) *exec.Cmd { + panicIfNotInitialized() + cmd := exec.CommandContext(ctx, Self()) + cmd.Args = args + return cmd +} diff --git a/vendor/github.com/containers/storage/pkg/reexec/command_unix.go b/vendor/github.com/containers/storage/pkg/reexec/command_unix.go new file mode 100644 index 0000000000..9dd8cb9bbe --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/reexec/command_unix.go @@ -0,0 +1,32 @@ +// +build freebsd solaris darwin + +package reexec + +import ( + "context" + "os/exec" +) + +// Self returns the path to the current process's binary. +// Uses os.Args[0]. +func Self() string { + return naiveSelf() +} + +// Command returns *exec.Cmd which has Path as current binary. +// For example if current binary is "docker" at "/usr/bin/", then cmd.Path will +// be set to "/usr/bin/docker". +func Command(args ...string) *exec.Cmd { + panicIfNotInitialized() + cmd := exec.Command(Self()) + cmd.Args = args + return cmd +} + +// CommandContext returns *exec.Cmd which has Path as current binary. +func CommandContext(ctx context.Context, args ...string) *exec.Cmd { + panicIfNotInitialized() + cmd := exec.CommandContext(ctx, Self()) + cmd.Args = args + return cmd +} diff --git a/vendor/github.com/containers/storage/pkg/reexec/command_unsupported.go b/vendor/github.com/containers/storage/pkg/reexec/command_unsupported.go new file mode 100644 index 0000000000..5b3605f319 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/reexec/command_unsupported.go @@ -0,0 +1,20 @@ +// +build !linux,!windows,!freebsd,!solaris,!darwin + +package reexec + +import ( + "context" + "os/exec" +) + +// Command is unsupported on operating systems apart from Linux, Windows, Solaris and Darwin. +func Command(args ...string) *exec.Cmd { + panicIfNotInitialized() + return nil +} + +// CommandContext is unsupported on operating systems apart from Linux, Windows, Solaris and Darwin. +func CommandContext(ctx context.Context, args ...string) *exec.Cmd { + panicIfNotInitialized() + return nil +} diff --git a/vendor/github.com/containers/storage/pkg/reexec/command_windows.go b/vendor/github.com/containers/storage/pkg/reexec/command_windows.go new file mode 100644 index 0000000000..d868564767 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/reexec/command_windows.go @@ -0,0 +1,34 @@ +// +build windows + +package reexec + +import ( + "context" + "os/exec" +) + +// Self returns the path to the current process's binary. +// Uses os.Args[0]. +func Self() string { + return naiveSelf() +} + +// Command returns *exec.Cmd which has Path as current binary. +// For example if current binary is "docker.exe" at "C:\", then cmd.Path will +// be set to "C:\docker.exe". +func Command(args ...string) *exec.Cmd { + panicIfNotInitialized() + cmd := exec.Command(Self()) + cmd.Args = args + return cmd +} + +// Command returns *exec.Cmd which has Path as current binary. +// For example if current binary is "docker.exe" at "C:\", then cmd.Path will +// be set to "C:\docker.exe". +func CommandContext(ctx context.Context, args ...string) *exec.Cmd { + panicIfNotInitialized() + cmd := exec.CommandContext(ctx, Self()) + cmd.Args = args + return cmd +} diff --git a/vendor/github.com/containers/storage/pkg/reexec/reexec.go b/vendor/github.com/containers/storage/pkg/reexec/reexec.go new file mode 100644 index 0000000000..a1938cd4f3 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/reexec/reexec.go @@ -0,0 +1,66 @@ +package reexec + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" +) + +var ( + registeredInitializers = make(map[string]func()) + initWasCalled = false +) + +// Register adds an initialization func under the specified name +func Register(name string, initializer func()) { + if _, exists := registeredInitializers[name]; exists { + panic(fmt.Sprintf("reexec func already registered under name %q", name)) + } + + registeredInitializers[name] = initializer +} + +// Init is called as the first part of the exec process and returns true if an +// initialization function was called. +func Init() bool { + initializer, exists := registeredInitializers[os.Args[0]] + initWasCalled = true + if exists { + initializer() + + return true + } + return false +} + +func panicIfNotInitialized() { + if !initWasCalled { + // The reexec package is used to run subroutines in + // subprocesses which would otherwise have unacceptable side + // effects on the main thread. If you found this error, then + // your program uses a package which needs to do this. In + // order for that to work, main() should start with this + // boilerplate, or an equivalent: + // if reexec.Init() { + // return + // } + panic("a library subroutine needed to run a subprocess, but reexec.Init() was not called in main()") + } +} + +func naiveSelf() string { + name := os.Args[0] + if filepath.Base(name) == name { + if lp, err := exec.LookPath(name); err == nil { + return lp + } + } + // handle conversion of relative paths to absolute + if absName, err := filepath.Abs(name); err == nil { + return absName + } + // if we couldn't get absolute name, return original + // (NOTE: Go only errors on Abs() if os.Getwd fails) + return name +} diff --git a/vendor/github.com/containers/storage/pkg/stringid/README.md b/vendor/github.com/containers/storage/pkg/stringid/README.md new file mode 100644 index 0000000000..37a5098fd9 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/stringid/README.md @@ -0,0 +1 @@ +This package provides helper functions for dealing with string identifiers diff --git a/vendor/github.com/containers/storage/pkg/stringid/stringid.go b/vendor/github.com/containers/storage/pkg/stringid/stringid.go new file mode 100644 index 0000000000..a0c7c42a05 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/stringid/stringid.go @@ -0,0 +1,99 @@ +// Package stringid provides helper functions for dealing with string identifiers +package stringid + +import ( + cryptorand "crypto/rand" + "encoding/hex" + "fmt" + "io" + "math" + "math/big" + "math/rand" + "regexp" + "strconv" + "strings" + "time" +) + +const shortLen = 12 + +var ( + validShortID = regexp.MustCompile("^[a-f0-9]{12}$") + validHex = regexp.MustCompile(`^[a-f0-9]{64}$`) +) + +// IsShortID determines if an arbitrary string *looks like* a short ID. +func IsShortID(id string) bool { + return validShortID.MatchString(id) +} + +// TruncateID returns a shorthand version of a string identifier for convenience. +// A collision with other shorthands is very unlikely, but possible. +// In case of a collision a lookup with TruncIndex.Get() will fail, and the caller +// will need to use a longer prefix, or the full-length Id. +func TruncateID(id string) string { + if i := strings.IndexRune(id, ':'); i >= 0 { + id = id[i+1:] + } + if len(id) > shortLen { + id = id[:shortLen] + } + return id +} + +func generateID(r io.Reader) string { + b := make([]byte, 32) + for { + if _, err := io.ReadFull(r, b); err != nil { + panic(err) // This shouldn't happen + } + id := hex.EncodeToString(b) + // if we try to parse the truncated for as an int and we don't have + // an error then the value is all numeric and causes issues when + // used as a hostname. ref #3869 + if _, err := strconv.ParseInt(TruncateID(id), 10, 64); err == nil { + continue + } + return id + } +} + +// GenerateRandomID returns a unique id. +func GenerateRandomID() string { + return generateID(cryptorand.Reader) +} + +// GenerateNonCryptoID generates unique id without using cryptographically +// secure sources of random. +// It helps you to save entropy. +func GenerateNonCryptoID() string { + return generateID(readerFunc(rand.Read)) +} + +// ValidateID checks whether an ID string is a valid image ID. +func ValidateID(id string) error { + if ok := validHex.MatchString(id); !ok { + return fmt.Errorf("image ID %q is invalid", id) + } + return nil +} + +func init() { + // safely set the seed globally so we generate random ids. Tries to use a + // crypto seed before falling back to time. + var seed int64 + if cryptoseed, err := cryptorand.Int(cryptorand.Reader, big.NewInt(math.MaxInt64)); err != nil { + // This should not happen, but worst-case fallback to time-based seed. + seed = time.Now().UnixNano() + } else { + seed = cryptoseed.Int64() + } + + rand.Seed(seed) +} + +type readerFunc func(p []byte) (int, error) + +func (fn readerFunc) Read(p []byte) (int, error) { + return fn(p) +} diff --git a/vendor/github.com/containers/storage/pkg/system/chmod.go b/vendor/github.com/containers/storage/pkg/system/chmod.go new file mode 100644 index 0000000000..a01d8abfbd --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/chmod.go @@ -0,0 +1,17 @@ +package system + +import ( + "errors" + "os" + "syscall" +) + +func Chmod(name string, mode os.FileMode) error { + err := os.Chmod(name, mode) + + for err != nil && errors.Is(err, syscall.EINTR) { + err = os.Chmod(name, mode) + } + + return err +} diff --git a/vendor/github.com/containers/storage/pkg/system/chtimes.go b/vendor/github.com/containers/storage/pkg/system/chtimes.go new file mode 100644 index 0000000000..056d19954d --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/chtimes.go @@ -0,0 +1,35 @@ +package system + +import ( + "os" + "time" +) + +// Chtimes changes the access time and modified time of a file at the given path +func Chtimes(name string, atime time.Time, mtime time.Time) error { + unixMinTime := time.Unix(0, 0) + unixMaxTime := maxTime + + // If the modified time is prior to the Unix Epoch, or after the + // end of Unix Time, os.Chtimes has undefined behavior + // default to Unix Epoch in this case, just in case + + if atime.Before(unixMinTime) || atime.After(unixMaxTime) { + atime = unixMinTime + } + + if mtime.Before(unixMinTime) || mtime.After(unixMaxTime) { + mtime = unixMinTime + } + + if err := os.Chtimes(name, atime, mtime); err != nil { + return err + } + + // Take platform specific action for setting create time. + if err := setCTime(name, mtime); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/chtimes_unix.go b/vendor/github.com/containers/storage/pkg/system/chtimes_unix.go new file mode 100644 index 0000000000..09d58bcbfd --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/chtimes_unix.go @@ -0,0 +1,14 @@ +// +build !windows + +package system + +import ( + "time" +) + +//setCTime will set the create time on a file. On Unix, the create +//time is updated as a side effect of setting the modified time, so +//no action is required. +func setCTime(path string, ctime time.Time) error { + return nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/chtimes_windows.go b/vendor/github.com/containers/storage/pkg/system/chtimes_windows.go new file mode 100644 index 0000000000..45428c141c --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/chtimes_windows.go @@ -0,0 +1,28 @@ +// +build windows + +package system + +import ( + "time" + + "golang.org/x/sys/windows" +) + +//setCTime will set the create time on a file. On Windows, this requires +//calling SetFileTime and explicitly including the create time. +func setCTime(path string, ctime time.Time) error { + ctimespec := windows.NsecToTimespec(ctime.UnixNano()) + pathp, e := windows.UTF16PtrFromString(path) + if e != nil { + return e + } + h, e := windows.CreateFile(pathp, + windows.FILE_WRITE_ATTRIBUTES, windows.FILE_SHARE_WRITE, nil, + windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0) + if e != nil { + return e + } + defer windows.Close(h) + c := windows.NsecToFiletime(windows.TimespecToNsec(ctimespec)) + return windows.SetFileTime(h, &c, nil, nil) +} diff --git a/vendor/github.com/containers/storage/pkg/system/errors.go b/vendor/github.com/containers/storage/pkg/system/errors.go new file mode 100644 index 0000000000..288318985e --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/errors.go @@ -0,0 +1,10 @@ +package system + +import ( + "errors" +) + +var ( + // ErrNotSupportedPlatform means the platform is not supported. + ErrNotSupportedPlatform = errors.New("platform and architecture is not supported") +) diff --git a/vendor/github.com/containers/storage/pkg/system/exitcode.go b/vendor/github.com/containers/storage/pkg/system/exitcode.go new file mode 100644 index 0000000000..60f0514b1d --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/exitcode.go @@ -0,0 +1,33 @@ +package system + +import ( + "fmt" + "os/exec" + "syscall" +) + +// GetExitCode returns the ExitStatus of the specified error if its type is +// exec.ExitError, returns 0 and an error otherwise. +func GetExitCode(err error) (int, error) { + exitCode := 0 + if exiterr, ok := err.(*exec.ExitError); ok { + if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok { + return procExit.ExitStatus(), nil + } + } + return exitCode, fmt.Errorf("failed to get exit code") +} + +// ProcessExitCode process the specified error and returns the exit status code +// if the error was of type exec.ExitError, returns nothing otherwise. +func ProcessExitCode(err error) (exitCode int) { + if err != nil { + var exiterr error + if exitCode, exiterr = GetExitCode(err); exiterr != nil { + // TODO: Fix this so we check the error's text. + // we've failed to retrieve exit code, so we set it to 127 + exitCode = 127 + } + } + return +} diff --git a/vendor/github.com/containers/storage/pkg/system/init.go b/vendor/github.com/containers/storage/pkg/system/init.go new file mode 100644 index 0000000000..17935088de --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/init.go @@ -0,0 +1,22 @@ +package system + +import ( + "syscall" + "time" + "unsafe" +) + +// Used by chtimes +var maxTime time.Time + +func init() { + // chtimes initialization + if unsafe.Sizeof(syscall.Timespec{}.Nsec) == 8 { + // This is a 64 bit timespec + // os.Chtimes limits time to the following + maxTime = time.Unix(0, 1<<63-1) + } else { + // This is a 32 bit timespec + maxTime = time.Unix(1<<31-1, 0) + } +} diff --git a/vendor/github.com/containers/storage/pkg/system/init_windows.go b/vendor/github.com/containers/storage/pkg/system/init_windows.go new file mode 100644 index 0000000000..019c66441c --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/init_windows.go @@ -0,0 +1,17 @@ +package system + +import "os" + +// LCOWSupported determines if Linux Containers on Windows are supported. +// Note: This feature is in development (06/17) and enabled through an +// environment variable. At a future time, it will be enabled based +// on build number. @jhowardmsft +var lcowSupported = false + +func init() { + // LCOW initialization + if os.Getenv("LCOW_SUPPORTED") != "" { + lcowSupported = true + } + +} diff --git a/vendor/github.com/containers/storage/pkg/system/lchown.go b/vendor/github.com/containers/storage/pkg/system/lchown.go new file mode 100644 index 0000000000..eb2d8b464c --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/lchown.go @@ -0,0 +1,20 @@ +package system + +import ( + "os" + "syscall" +) + +func Lchown(name string, uid, gid int) error { + err := syscall.Lchown(name, uid, gid) + + for err == syscall.EINTR { + err = syscall.Lchown(name, uid, gid) + } + + if err != nil { + return &os.PathError{Op: "lchown", Path: name, Err: err} + } + + return nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/lcow_unix.go b/vendor/github.com/containers/storage/pkg/system/lcow_unix.go new file mode 100644 index 0000000000..cff33bb408 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/lcow_unix.go @@ -0,0 +1,8 @@ +// +build !windows + +package system + +// LCOWSupported returns true if Linux containers on Windows are supported. +func LCOWSupported() bool { + return false +} diff --git a/vendor/github.com/containers/storage/pkg/system/lcow_windows.go b/vendor/github.com/containers/storage/pkg/system/lcow_windows.go new file mode 100644 index 0000000000..e54d01e696 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/lcow_windows.go @@ -0,0 +1,6 @@ +package system + +// LCOWSupported returns true if Linux containers on Windows are supported. +func LCOWSupported() bool { + return lcowSupported +} diff --git a/vendor/github.com/containers/storage/pkg/system/lstat_unix.go b/vendor/github.com/containers/storage/pkg/system/lstat_unix.go new file mode 100644 index 0000000000..e9d301f090 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/lstat_unix.go @@ -0,0 +1,20 @@ +// +build !windows + +package system + +import ( + "os" + "syscall" +) + +// Lstat takes a path to a file and returns +// a system.StatT type pertaining to that file. +// +// Throws an error if the file does not exist +func Lstat(path string) (*StatT, error) { + s := &syscall.Stat_t{} + if err := syscall.Lstat(path, s); err != nil { + return nil, &os.PathError{"Lstat", path, err} + } + return fromStatT(s) +} diff --git a/vendor/github.com/containers/storage/pkg/system/lstat_windows.go b/vendor/github.com/containers/storage/pkg/system/lstat_windows.go new file mode 100644 index 0000000000..e51df0dafe --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/lstat_windows.go @@ -0,0 +1,14 @@ +package system + +import "os" + +// Lstat calls os.Lstat to get a fileinfo interface back. +// This is then copied into our own locally defined structure. +func Lstat(path string) (*StatT, error) { + fi, err := os.Lstat(path) + if err != nil { + return nil, err + } + + return fromStatT(&fi) +} diff --git a/vendor/github.com/containers/storage/pkg/system/meminfo.go b/vendor/github.com/containers/storage/pkg/system/meminfo.go new file mode 100644 index 0000000000..3b6e947e67 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/meminfo.go @@ -0,0 +1,17 @@ +package system + +// MemInfo contains memory statistics of the host system. +type MemInfo struct { + // Total usable RAM (i.e. physical RAM minus a few reserved bits and the + // kernel binary code). + MemTotal int64 + + // Amount of free memory. + MemFree int64 + + // Total amount of swap space available. + SwapTotal int64 + + // Amount of swap space that is currently unused. + SwapFree int64 +} diff --git a/vendor/github.com/containers/storage/pkg/system/meminfo_linux.go b/vendor/github.com/containers/storage/pkg/system/meminfo_linux.go new file mode 100644 index 0000000000..385f1d5e73 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/meminfo_linux.go @@ -0,0 +1,65 @@ +package system + +import ( + "bufio" + "io" + "os" + "strconv" + "strings" + + "github.com/docker/go-units" +) + +// ReadMemInfo retrieves memory statistics of the host system and returns a +// MemInfo type. +func ReadMemInfo() (*MemInfo, error) { + file, err := os.Open("/proc/meminfo") + if err != nil { + return nil, err + } + defer file.Close() + return parseMemInfo(file) +} + +// parseMemInfo parses the /proc/meminfo file into +// a MemInfo object given an io.Reader to the file. +// Throws error if there are problems reading from the file +func parseMemInfo(reader io.Reader) (*MemInfo, error) { + meminfo := &MemInfo{} + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + // Expected format: ["MemTotal:", "1234", "kB"] + parts := strings.Fields(scanner.Text()) + + // Sanity checks: Skip malformed entries. + if len(parts) < 3 || parts[2] != "kB" { + continue + } + + // Convert to bytes. + size, err := strconv.Atoi(parts[1]) + if err != nil { + continue + } + bytes := int64(size) * units.KiB + + switch parts[0] { + case "MemTotal:": + meminfo.MemTotal = bytes + case "MemFree:": + meminfo.MemFree = bytes + case "SwapTotal:": + meminfo.SwapTotal = bytes + case "SwapFree:": + meminfo.SwapFree = bytes + } + + } + + // Handle errors that may have occurred during the reading of the file. + if err := scanner.Err(); err != nil { + return nil, err + } + + return meminfo, nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/meminfo_solaris.go b/vendor/github.com/containers/storage/pkg/system/meminfo_solaris.go new file mode 100644 index 0000000000..925776e789 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/meminfo_solaris.go @@ -0,0 +1,129 @@ +// +build solaris,cgo + +package system + +import ( + "fmt" + "unsafe" +) + +// #cgo CFLAGS: -std=c99 +// #cgo LDFLAGS: -lkstat +// #include +// #include +// #include +// #include +// #include +// #include +// struct swaptable *allocSwaptable(int num) { +// struct swaptable *st; +// struct swapent *swapent; +// st = (struct swaptable *)malloc(num * sizeof(swapent_t) + sizeof (int)); +// swapent = st->swt_ent; +// for (int i = 0; i < num; i++,swapent++) { +// swapent->ste_path = (char *)malloc(MAXPATHLEN * sizeof (char)); +// } +// st->swt_n = num; +// return st; +//} +// void freeSwaptable (struct swaptable *st) { +// struct swapent *swapent = st->swt_ent; +// for (int i = 0; i < st->swt_n; i++,swapent++) { +// free(swapent->ste_path); +// } +// free(st); +// } +// swapent_t getSwapEnt(swapent_t *ent, int i) { +// return ent[i]; +// } +// int64_t getPpKernel() { +// int64_t pp_kernel = 0; +// kstat_ctl_t *ksc; +// kstat_t *ks; +// kstat_named_t *knp; +// kid_t kid; +// +// if ((ksc = kstat_open()) == NULL) { +// return -1; +// } +// if ((ks = kstat_lookup(ksc, "unix", 0, "system_pages")) == NULL) { +// return -1; +// } +// if (((kid = kstat_read(ksc, ks, NULL)) == -1) || +// ((knp = kstat_data_lookup(ks, "pp_kernel")) == NULL)) { +// return -1; +// } +// switch (knp->data_type) { +// case KSTAT_DATA_UINT64: +// pp_kernel = knp->value.ui64; +// break; +// case KSTAT_DATA_UINT32: +// pp_kernel = knp->value.ui32; +// break; +// } +// pp_kernel *= sysconf(_SC_PAGESIZE); +// return (pp_kernel > 0 ? pp_kernel : -1); +// } +import "C" + +// Get the system memory info using sysconf same as prtconf +func getTotalMem() int64 { + pagesize := C.sysconf(C._SC_PAGESIZE) + npages := C.sysconf(C._SC_PHYS_PAGES) + return int64(pagesize * npages) +} + +func getFreeMem() int64 { + pagesize := C.sysconf(C._SC_PAGESIZE) + npages := C.sysconf(C._SC_AVPHYS_PAGES) + return int64(pagesize * npages) +} + +// ReadMemInfo retrieves memory statistics of the host system and returns a +// MemInfo type. +func ReadMemInfo() (*MemInfo, error) { + + ppKernel := C.getPpKernel() + MemTotal := getTotalMem() + MemFree := getFreeMem() + SwapTotal, SwapFree, err := getSysSwap() + + if ppKernel < 0 || MemTotal < 0 || MemFree < 0 || SwapTotal < 0 || + SwapFree < 0 { + return nil, fmt.Errorf("error getting system memory info %v\n", err) + } + + meminfo := &MemInfo{} + // Total memory is total physical memory less than memory locked by kernel + meminfo.MemTotal = MemTotal - int64(ppKernel) + meminfo.MemFree = MemFree + meminfo.SwapTotal = SwapTotal + meminfo.SwapFree = SwapFree + + return meminfo, nil +} + +func getSysSwap() (int64, int64, error) { + var tSwap int64 + var fSwap int64 + var diskblksPerPage int64 + num, err := C.swapctl(C.SC_GETNSWP, nil) + if err != nil { + return -1, -1, err + } + st := C.allocSwaptable(num) + _, err = C.swapctl(C.SC_LIST, unsafe.Pointer(st)) + if err != nil { + C.freeSwaptable(st) + return -1, -1, err + } + + diskblksPerPage = int64(C.sysconf(C._SC_PAGESIZE) >> C.DEV_BSHIFT) + for i := 0; i < int(num); i++ { + swapent := C.getSwapEnt(&st.swt_ent[0], C.int(i)) + tSwap += int64(swapent.ste_pages) * diskblksPerPage + fSwap += int64(swapent.ste_free) * diskblksPerPage + } + C.freeSwaptable(st) + return tSwap, fSwap, nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/meminfo_unsupported.go b/vendor/github.com/containers/storage/pkg/system/meminfo_unsupported.go new file mode 100644 index 0000000000..3ce019dffd --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/meminfo_unsupported.go @@ -0,0 +1,8 @@ +// +build !linux,!windows,!solaris + +package system + +// ReadMemInfo is not supported on platforms other than linux and windows. +func ReadMemInfo() (*MemInfo, error) { + return nil, ErrNotSupportedPlatform +} diff --git a/vendor/github.com/containers/storage/pkg/system/meminfo_windows.go b/vendor/github.com/containers/storage/pkg/system/meminfo_windows.go new file mode 100644 index 0000000000..883944a4c5 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/meminfo_windows.go @@ -0,0 +1,45 @@ +package system + +import ( + "unsafe" + + "golang.org/x/sys/windows" +) + +var ( + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + + procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx") +) + +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366589(v=vs.85).aspx +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366770(v=vs.85).aspx +type memorystatusex struct { + dwLength uint32 + dwMemoryLoad uint32 + ullTotalPhys uint64 + ullAvailPhys uint64 + ullTotalPageFile uint64 + ullAvailPageFile uint64 + ullTotalVirtual uint64 + ullAvailVirtual uint64 + ullAvailExtendedVirtual uint64 +} + +// ReadMemInfo retrieves memory statistics of the host system and returns a +// MemInfo type. +func ReadMemInfo() (*MemInfo, error) { + msi := &memorystatusex{ + dwLength: 64, + } + r1, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(msi))) + if r1 == 0 { + return &MemInfo{}, nil + } + return &MemInfo{ + MemTotal: int64(msi.ullTotalPhys), + MemFree: int64(msi.ullAvailPhys), + SwapTotal: int64(msi.ullTotalPageFile), + SwapFree: int64(msi.ullAvailPageFile), + }, nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/mknod.go b/vendor/github.com/containers/storage/pkg/system/mknod.go new file mode 100644 index 0000000000..c276ce8e80 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/mknod.go @@ -0,0 +1,22 @@ +// +build !windows,!freebsd + +package system + +import ( + "golang.org/x/sys/unix" +) + +// Mknod creates a filesystem node (file, device special file or named pipe) named path +// with attributes specified by mode and dev. +func Mknod(path string, mode uint32, dev int) error { + return unix.Mknod(path, mode, dev) +} + +// Mkdev is used to build the value of linux devices (in /dev/) which specifies major +// and minor number of the newly created device special file. +// Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes. +// They are, from low to high: the lower 8 bits of the minor, then 12 bits of the major, +// then the top 12 bits of the minor. +func Mkdev(major int64, minor int64) uint32 { + return uint32(((minor & 0xfff00) << 12) | ((major & 0xfff) << 8) | (minor & 0xff)) +} diff --git a/vendor/github.com/containers/storage/pkg/system/mknod_freebsd.go b/vendor/github.com/containers/storage/pkg/system/mknod_freebsd.go new file mode 100644 index 0000000000..d09005589a --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/mknod_freebsd.go @@ -0,0 +1,22 @@ +// +build freebsd + +package system + +import ( + "golang.org/x/sys/unix" +) + +// Mknod creates a filesystem node (file, device special file or named pipe) named path +// with attributes specified by mode and dev. +func Mknod(path string, mode uint32, dev uint64) error { + return unix.Mknod(path, mode, dev) +} + +// Mkdev is used to build the value of linux devices (in /dev/) which specifies major +// and minor number of the newly created device special file. +// Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes. +// They are, from low to high: the lower 8 bits of the minor, then 12 bits of the major, +// then the top 12 bits of the minor. +func Mkdev(major int64, minor int64) uint32 { + return uint32(((minor & 0xfff00) << 12) | ((major & 0xfff) << 8) | (minor & 0xff)) +} diff --git a/vendor/github.com/containers/storage/pkg/system/mknod_windows.go b/vendor/github.com/containers/storage/pkg/system/mknod_windows.go new file mode 100644 index 0000000000..2e863c0215 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/mknod_windows.go @@ -0,0 +1,13 @@ +// +build windows + +package system + +// Mknod is not implemented on Windows. +func Mknod(path string, mode uint32, dev int) error { + return ErrNotSupportedPlatform +} + +// Mkdev is not implemented on Windows. +func Mkdev(major int64, minor int64) uint32 { + panic("Mkdev not implemented on Windows.") +} diff --git a/vendor/github.com/containers/storage/pkg/system/path.go b/vendor/github.com/containers/storage/pkg/system/path.go new file mode 100644 index 0000000000..f634a6be67 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/path.go @@ -0,0 +1,21 @@ +package system + +import "runtime" + +const defaultUnixPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + +// DefaultPathEnv is unix style list of directories to search for +// executables. Each directory is separated from the next by a colon +// ':' character . +func DefaultPathEnv(platform string) string { + if runtime.GOOS == "windows" { + if platform != runtime.GOOS && LCOWSupported() { + return defaultUnixPathEnv + } + // Deliberately empty on Windows containers on Windows as the default path will be set by + // the container. Docker has no context of what the default path should be. + return "" + } + return defaultUnixPathEnv + +} diff --git a/vendor/github.com/containers/storage/pkg/system/path_unix.go b/vendor/github.com/containers/storage/pkg/system/path_unix.go new file mode 100644 index 0000000000..f3762e69d3 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/path_unix.go @@ -0,0 +1,9 @@ +// +build !windows + +package system + +// CheckSystemDriveAndRemoveDriveLetter verifies that a path, if it includes a drive letter, +// is the system drive. This is a no-op on Linux. +func CheckSystemDriveAndRemoveDriveLetter(path string) (string, error) { + return path, nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/path_windows.go b/vendor/github.com/containers/storage/pkg/system/path_windows.go new file mode 100644 index 0000000000..aab891522d --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/path_windows.go @@ -0,0 +1,33 @@ +// +build windows + +package system + +import ( + "fmt" + "path/filepath" + "strings" +) + +// CheckSystemDriveAndRemoveDriveLetter verifies and manipulates a Windows path. +// This is used, for example, when validating a user provided path in docker cp. +// If a drive letter is supplied, it must be the system drive. The drive letter +// is always removed. Also, it translates it to OS semantics (IOW / to \). We +// need the path in this syntax so that it can ultimately be concatenated with +// a Windows long-path which doesn't support drive-letters. Examples: +// C: --> Fail +// C:\ --> \ +// a --> a +// /a --> \a +// d:\ --> Fail +func CheckSystemDriveAndRemoveDriveLetter(path string) (string, error) { + if len(path) == 2 && string(path[1]) == ":" { + return "", fmt.Errorf("No relative path specified in %q", path) + } + if !filepath.IsAbs(path) || len(path) < 2 { + return filepath.FromSlash(path), nil + } + if string(path[1]) == ":" && !strings.EqualFold(string(path[0]), "c") { + return "", fmt.Errorf("The specified path is not on the system drive (C:)") + } + return filepath.FromSlash(path[2:]), nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/process_unix.go b/vendor/github.com/containers/storage/pkg/system/process_unix.go new file mode 100644 index 0000000000..a9a0dd7517 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/process_unix.go @@ -0,0 +1,24 @@ +// +build linux freebsd solaris darwin + +package system + +import ( + "syscall" + + "golang.org/x/sys/unix" +) + +// IsProcessAlive returns true if process with a given pid is running. +func IsProcessAlive(pid int) bool { + err := unix.Kill(pid, syscall.Signal(0)) + if err == nil || err == unix.EPERM { + return true + } + + return false +} + +// KillProcess force-stops a process. +func KillProcess(pid int) { + _ = unix.Kill(pid, unix.SIGKILL) +} diff --git a/vendor/github.com/containers/storage/pkg/system/rm.go b/vendor/github.com/containers/storage/pkg/system/rm.go new file mode 100644 index 0000000000..510e714283 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/rm.go @@ -0,0 +1,79 @@ +package system + +import ( + "os" + "syscall" + "time" + + "github.com/containers/storage/pkg/mount" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// EnsureRemoveAll wraps `os.RemoveAll` to check for specific errors that can +// often be remedied. +// Only use `EnsureRemoveAll` if you really want to make every effort to remove +// a directory. +// +// Because of the way `os.Remove` (and by extension `os.RemoveAll`) works, there +// can be a race between reading directory entries and then actually attempting +// to remove everything in the directory. +// These types of errors do not need to be returned since it's ok for the dir to +// be gone we can just retry the remove operation. +// +// This should not return a `os.ErrNotExist` kind of error under any circumstances +func EnsureRemoveAll(dir string) error { + notExistErr := make(map[string]bool) + + // track retries + exitOnErr := make(map[string]int) + maxRetry := 100 + + // Attempt to unmount anything beneath this dir first + if err := mount.RecursiveUnmount(dir); err != nil { + logrus.Debugf("RecusiveUnmount on %s failed: %v", dir, err) + } + + for { + err := os.RemoveAll(dir) + if err == nil { + return nil + } + + pe, ok := err.(*os.PathError) + if !ok { + return err + } + + if os.IsNotExist(err) { + if notExistErr[pe.Path] { + return err + } + notExistErr[pe.Path] = true + + // There is a race where some subdir can be removed but after the parent + // dir entries have been read. + // So the path could be from `os.Remove(subdir)` + // If the reported non-existent path is not the passed in `dir` we + // should just retry, but otherwise return with no error. + if pe.Path == dir { + return nil + } + continue + } + + if pe.Err != syscall.EBUSY { + return err + } + + if e := mount.Unmount(pe.Path); e != nil { + return errors.Wrapf(e, "error while removing %s", dir) + } + + if exitOnErr[pe.Path] == maxRetry { + return err + } + exitOnErr[pe.Path]++ + time.Sleep(100 * time.Millisecond) + } +} diff --git a/vendor/github.com/containers/storage/pkg/system/stat_darwin.go b/vendor/github.com/containers/storage/pkg/system/stat_darwin.go new file mode 100644 index 0000000000..715f05b938 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/stat_darwin.go @@ -0,0 +1,13 @@ +package system + +import "syscall" + +// fromStatT converts a syscall.Stat_t type to a system.Stat_t type +func fromStatT(s *syscall.Stat_t) (*StatT, error) { + return &StatT{size: s.Size, + mode: uint32(s.Mode), + uid: s.Uid, + gid: s.Gid, + rdev: uint64(s.Rdev), + mtim: s.Mtimespec}, nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/stat_freebsd.go b/vendor/github.com/containers/storage/pkg/system/stat_freebsd.go new file mode 100644 index 0000000000..715f05b938 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/stat_freebsd.go @@ -0,0 +1,13 @@ +package system + +import "syscall" + +// fromStatT converts a syscall.Stat_t type to a system.Stat_t type +func fromStatT(s *syscall.Stat_t) (*StatT, error) { + return &StatT{size: s.Size, + mode: uint32(s.Mode), + uid: s.Uid, + gid: s.Gid, + rdev: uint64(s.Rdev), + mtim: s.Mtimespec}, nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/stat_linux.go b/vendor/github.com/containers/storage/pkg/system/stat_linux.go new file mode 100644 index 0000000000..af7af20fa4 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/stat_linux.go @@ -0,0 +1,19 @@ +package system + +import "syscall" + +// fromStatT converts a syscall.Stat_t type to a system.Stat_t type +func fromStatT(s *syscall.Stat_t) (*StatT, error) { + return &StatT{size: s.Size, + mode: s.Mode, + uid: s.Uid, + gid: s.Gid, + rdev: uint64(s.Rdev), + mtim: s.Mtim}, nil +} + +// FromStatT converts a syscall.Stat_t type to a system.Stat_t type +// This is exposed on Linux as pkg/archive/changes uses it. +func FromStatT(s *syscall.Stat_t) (*StatT, error) { + return fromStatT(s) +} diff --git a/vendor/github.com/containers/storage/pkg/system/stat_openbsd.go b/vendor/github.com/containers/storage/pkg/system/stat_openbsd.go new file mode 100644 index 0000000000..b607dea946 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/stat_openbsd.go @@ -0,0 +1,13 @@ +package system + +import "syscall" + +// fromStatT converts a syscall.Stat_t type to a system.Stat_t type +func fromStatT(s *syscall.Stat_t) (*StatT, error) { + return &StatT{size: s.Size, + mode: uint32(s.Mode), + uid: s.Uid, + gid: s.Gid, + rdev: uint64(s.Rdev), + mtim: s.Mtim}, nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/stat_solaris.go b/vendor/github.com/containers/storage/pkg/system/stat_solaris.go new file mode 100644 index 0000000000..b607dea946 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/stat_solaris.go @@ -0,0 +1,13 @@ +package system + +import "syscall" + +// fromStatT converts a syscall.Stat_t type to a system.Stat_t type +func fromStatT(s *syscall.Stat_t) (*StatT, error) { + return &StatT{size: s.Size, + mode: uint32(s.Mode), + uid: s.Uid, + gid: s.Gid, + rdev: uint64(s.Rdev), + mtim: s.Mtim}, nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/stat_unix.go b/vendor/github.com/containers/storage/pkg/system/stat_unix.go new file mode 100644 index 0000000000..2fac918bfc --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/stat_unix.go @@ -0,0 +1,74 @@ +// +build !windows + +package system + +import ( + "os" + "strconv" + "syscall" +) + +// StatT type contains status of a file. It contains metadata +// like permission, owner, group, size, etc about a file. +type StatT struct { + mode uint32 + uid uint32 + gid uint32 + rdev uint64 + size int64 + mtim syscall.Timespec +} + +// Mode returns file's permission mode. +func (s StatT) Mode() uint32 { + return s.mode +} + +// UID returns file's user id of owner. +func (s StatT) UID() uint32 { + return s.uid +} + +// GID returns file's group id of owner. +func (s StatT) GID() uint32 { + return s.gid +} + +// Rdev returns file's device ID (if it's special file). +func (s StatT) Rdev() uint64 { + return s.rdev +} + +// Size returns file's size. +func (s StatT) Size() int64 { + return s.size +} + +// Mtim returns file's last modification time. +func (s StatT) Mtim() syscall.Timespec { + return s.mtim +} + +// Stat takes a path to a file and returns +// a system.StatT type pertaining to that file. +// +// Throws an error if the file does not exist +func Stat(path string) (*StatT, error) { + s := &syscall.Stat_t{} + if err := syscall.Stat(path, s); err != nil { + return nil, &os.PathError{Op: "Stat", Path: path, Err: err} + } + return fromStatT(s) +} + +// Fstat takes an open file descriptor and returns +// a system.StatT type pertaining to that file. +// +// Throws an error if the file descriptor is invalid +func Fstat(fd int) (*StatT, error) { + s := &syscall.Stat_t{} + if err := syscall.Fstat(fd, s); err != nil { + return nil, &os.PathError{Op: "Fstat", Path: strconv.Itoa(fd), Err: err} + } + return fromStatT(s) +} diff --git a/vendor/github.com/containers/storage/pkg/system/stat_windows.go b/vendor/github.com/containers/storage/pkg/system/stat_windows.go new file mode 100644 index 0000000000..d306360520 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/stat_windows.go @@ -0,0 +1,63 @@ +package system + +import ( + "os" + "time" +) + +// StatT type contains status of a file. It contains metadata +// like permission, size, etc about a file. +type StatT struct { + mode os.FileMode + size int64 + mtim time.Time +} + +// Size returns file's size. +func (s StatT) Size() int64 { + return s.size +} + +// Mode returns file's permission mode. +func (s StatT) Mode() os.FileMode { + return os.FileMode(s.mode) +} + +// Mtim returns file's last modification time. +func (s StatT) Mtim() time.Time { + return time.Time(s.mtim) +} + +// UID returns file's user id of owner. +// +// on windows this is always 0 because there is no concept of UID +func (s StatT) UID() uint32 { + return 0 +} + +// GID returns file's group id of owner. +// +// on windows this is always 0 because there is no concept of GID +func (s StatT) GID() uint32 { + return 0 +} + +// Stat takes a path to a file and returns +// a system.StatT type pertaining to that file. +// +// Throws an error if the file does not exist +func Stat(path string) (*StatT, error) { + fi, err := os.Stat(path) + if err != nil { + return nil, err + } + return fromStatT(&fi) +} + +// fromStatT converts a os.FileInfo type to a system.StatT type +func fromStatT(fi *os.FileInfo) (*StatT, error) { + return &StatT{ + size: (*fi).Size(), + mode: (*fi).Mode(), + mtim: (*fi).ModTime()}, nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/syscall_unix.go b/vendor/github.com/containers/storage/pkg/system/syscall_unix.go new file mode 100644 index 0000000000..1bb852d11f --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/syscall_unix.go @@ -0,0 +1,25 @@ +// +build linux freebsd darwin + +package system + +import ( + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +// Unmount is a platform-specific helper function to call +// the unmount syscall. +func Unmount(dest string) error { + return unix.Unmount(dest, 0) +} + +// CommandLineToArgv should not be used on Unix. +// It simply returns commandLine in the only element in the returned array. +func CommandLineToArgv(commandLine string) ([]string, error) { + return []string{commandLine}, nil +} + +// IsEBUSY checks if the specified error is EBUSY. +func IsEBUSY(err error) bool { + return errors.Is(err, unix.EBUSY) +} diff --git a/vendor/github.com/containers/storage/pkg/system/syscall_windows.go b/vendor/github.com/containers/storage/pkg/system/syscall_windows.go new file mode 100644 index 0000000000..f4d8692cdb --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/syscall_windows.go @@ -0,0 +1,127 @@ +package system + +import ( + "unsafe" + + "github.com/sirupsen/logrus" + "golang.org/x/sys/windows" +) + +var ( + ntuserApiset = windows.NewLazyDLL("ext-ms-win-ntuser-window-l1-1-0") + procGetVersionExW = modkernel32.NewProc("GetVersionExW") + procGetProductInfo = modkernel32.NewProc("GetProductInfo") +) + +// OSVersion is a wrapper for Windows version information +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx +type OSVersion struct { + Version uint32 + MajorVersion uint8 + MinorVersion uint8 + Build uint16 +} + +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724833(v=vs.85).aspx +type osVersionInfoEx struct { + OSVersionInfoSize uint32 + MajorVersion uint32 + MinorVersion uint32 + BuildNumber uint32 + PlatformID uint32 + CSDVersion [128]uint16 + ServicePackMajor uint16 + ServicePackMinor uint16 + SuiteMask uint16 + ProductType byte + Reserve byte +} + +// GetOSVersion gets the operating system version on Windows. Note that +// docker.exe must be manifested to get the correct version information. +func GetOSVersion() OSVersion { + var err error + osv := OSVersion{} + osv.Version, err = windows.GetVersion() + if err != nil { + // GetVersion never fails. + panic(err) + } + osv.MajorVersion = uint8(osv.Version & 0xFF) + osv.MinorVersion = uint8(osv.Version >> 8 & 0xFF) + osv.Build = uint16(osv.Version >> 16) + return osv +} + +// IsWindowsClient returns true if the SKU is client +// @engine maintainers - this function should not be removed or modified as it +// is used to enforce licensing restrictions on Windows. +func IsWindowsClient() bool { + osviex := &osVersionInfoEx{OSVersionInfoSize: 284} + r1, _, err := procGetVersionExW.Call(uintptr(unsafe.Pointer(osviex))) + if r1 == 0 { + logrus.Warnf("GetVersionExW failed - assuming server SKU: %v", err) + return false + } + const verNTWorkstation = 0x00000001 + return osviex.ProductType == verNTWorkstation +} + +// IsIoTCore returns true if the currently running image is based off of +// Windows 10 IoT Core. +// @engine maintainers - this function should not be removed or modified as it +// is used to enforce licensing restrictions on Windows. +func IsIoTCore() bool { + var returnedProductType uint32 + r1, _, err := procGetProductInfo.Call(6, 1, 0, 0, uintptr(unsafe.Pointer(&returnedProductType))) + if r1 == 0 { + logrus.Warnf("GetProductInfo failed - assuming this is not IoT: %v", err) + return false + } + const productIoTUAP = 0x0000007B + const productIoTUAPCommercial = 0x00000083 + return returnedProductType == productIoTUAP || returnedProductType == productIoTUAPCommercial +} + +// Unmount is a platform-specific helper function to call +// the unmount syscall. Not supported on Windows +func Unmount(dest string) error { + return nil +} + +// CommandLineToArgv wraps the Windows syscall to turn a commandline into an argument array. +func CommandLineToArgv(commandLine string) ([]string, error) { + var argc int32 + + argsPtr, err := windows.UTF16PtrFromString(commandLine) + if err != nil { + return nil, err + } + + argv, err := windows.CommandLineToArgv(argsPtr, &argc) + if err != nil { + return nil, err + } + defer windows.LocalFree(windows.Handle(uintptr(unsafe.Pointer(argv)))) + + newArgs := make([]string, argc) + for i, v := range (*argv)[:argc] { + newArgs[i] = string(windows.UTF16ToString((*v)[:])) + } + + return newArgs, nil +} + +// HasWin32KSupport determines whether containers that depend on win32k can +// run on this machine. Win32k is the driver used to implement windowing. +func HasWin32KSupport() bool { + // For now, check for ntuser API support on the host. In the future, a host + // may support win32k in containers even if the host does not support ntuser + // APIs. + return ntuserApiset.Load() == nil +} + +// IsEBUSY checks if the specified error is EBUSY. +func IsEBUSY(err error) bool { + return false +} diff --git a/vendor/github.com/containers/storage/pkg/system/umask.go b/vendor/github.com/containers/storage/pkg/system/umask.go new file mode 100644 index 0000000000..5a10eda5af --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/umask.go @@ -0,0 +1,13 @@ +// +build !windows + +package system + +import ( + "golang.org/x/sys/unix" +) + +// Umask sets current process's file mode creation mask to newmask +// and returns oldmask. +func Umask(newmask int) (oldmask int, err error) { + return unix.Umask(newmask), nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/umask_windows.go b/vendor/github.com/containers/storage/pkg/system/umask_windows.go new file mode 100644 index 0000000000..13f1de1769 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/umask_windows.go @@ -0,0 +1,9 @@ +// +build windows + +package system + +// Umask is not supported on the windows platform. +func Umask(newmask int) (oldmask int, err error) { + // should not be called on cli code path + return 0, ErrNotSupportedPlatform +} diff --git a/vendor/github.com/containers/storage/pkg/system/utimes_freebsd.go b/vendor/github.com/containers/storage/pkg/system/utimes_freebsd.go new file mode 100644 index 0000000000..6a77524376 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/utimes_freebsd.go @@ -0,0 +1,24 @@ +package system + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +// LUtimesNano is used to change access and modification time of the specified path. +// It's used for symbol link file because unix.UtimesNano doesn't support a NOFOLLOW flag atm. +func LUtimesNano(path string, ts []syscall.Timespec) error { + var _path *byte + _path, err := unix.BytePtrFromString(path) + if err != nil { + return err + } + + if _, _, err := unix.Syscall(unix.SYS_LUTIMES, uintptr(unsafe.Pointer(_path)), uintptr(unsafe.Pointer(&ts[0])), 0); err != 0 && err != unix.ENOSYS { + return err + } + + return nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/utimes_linux.go b/vendor/github.com/containers/storage/pkg/system/utimes_linux.go new file mode 100644 index 0000000000..edc588a63f --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/utimes_linux.go @@ -0,0 +1,25 @@ +package system + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +// LUtimesNano is used to change access and modification time of the specified path. +// It's used for symbol link file because unix.UtimesNano doesn't support a NOFOLLOW flag atm. +func LUtimesNano(path string, ts []syscall.Timespec) error { + atFdCwd := unix.AT_FDCWD + + var _path *byte + _path, err := unix.BytePtrFromString(path) + if err != nil { + return err + } + if _, _, err := unix.Syscall6(unix.SYS_UTIMENSAT, uintptr(atFdCwd), uintptr(unsafe.Pointer(_path)), uintptr(unsafe.Pointer(&ts[0])), unix.AT_SYMLINK_NOFOLLOW, 0, 0); err != 0 && err != unix.ENOSYS { + return err + } + + return nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/utimes_unsupported.go b/vendor/github.com/containers/storage/pkg/system/utimes_unsupported.go new file mode 100644 index 0000000000..139714544d --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/utimes_unsupported.go @@ -0,0 +1,10 @@ +// +build !linux,!freebsd + +package system + +import "syscall" + +// LUtimesNano is only supported on linux and freebsd. +func LUtimesNano(path string, ts []syscall.Timespec) error { + return ErrNotSupportedPlatform +} diff --git a/vendor/github.com/containers/storage/pkg/system/xattrs_linux.go b/vendor/github.com/containers/storage/pkg/system/xattrs_linux.go new file mode 100644 index 0000000000..10355848bd --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/xattrs_linux.go @@ -0,0 +1,84 @@ +package system + +import ( + "bytes" + "os" + + "golang.org/x/sys/unix" +) + +const ( + // Value is larger than the maximum size allowed + E2BIG unix.Errno = unix.E2BIG + + // Operation not supported + EOPNOTSUPP unix.Errno = unix.EOPNOTSUPP +) + +// Lgetxattr retrieves the value of the extended attribute identified by attr +// and associated with the given path in the file system. +// Returns a []byte slice if the xattr is set and nil otherwise. +func Lgetxattr(path string, attr string) ([]byte, error) { + // Start with a 128 length byte array + dest := make([]byte, 128) + sz, errno := unix.Lgetxattr(path, attr, dest) + + for errno == unix.ERANGE { + // Buffer too small, use zero-sized buffer to get the actual size + sz, errno = unix.Lgetxattr(path, attr, []byte{}) + if errno != nil { + return nil, &os.PathError{Op: "lgetxattr", Path: path, Err: errno} + } + dest = make([]byte, sz) + sz, errno = unix.Lgetxattr(path, attr, dest) + } + + switch { + case errno == unix.ENODATA: + return nil, nil + case errno != nil: + return nil, &os.PathError{Op: "lgetxattr", Path: path, Err: errno} + } + + return dest[:sz], nil +} + +// Lsetxattr sets the value of the extended attribute identified by attr +// and associated with the given path in the file system. +func Lsetxattr(path string, attr string, data []byte, flags int) error { + if err := unix.Lsetxattr(path, attr, data, flags); err != nil { + return &os.PathError{Op: "lsetxattr", Path: path, Err: err} + } + + return nil +} + +// Llistxattr lists extended attributes associated with the given path +// in the file system. +func Llistxattr(path string) ([]string, error) { + dest := make([]byte, 128) + sz, errno := unix.Llistxattr(path, dest) + + for errno == unix.ERANGE { + // Buffer too small, use zero-sized buffer to get the actual size + sz, errno = unix.Llistxattr(path, []byte{}) + if errno != nil { + return nil, &os.PathError{Op: "llistxattr", Path: path, Err: errno} + } + + dest = make([]byte, sz) + sz, errno = unix.Llistxattr(path, dest) + } + if errno != nil { + return nil, &os.PathError{Op: "llistxattr", Path: path, Err: errno} + } + + var attrs []string + for _, token := range bytes.Split(dest[:sz], []byte{0}) { + if len(token) > 0 { + attrs = append(attrs, string(token)) + } + } + + return attrs, nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/xattrs_unsupported.go b/vendor/github.com/containers/storage/pkg/system/xattrs_unsupported.go new file mode 100644 index 0000000000..bc8b8e3a5f --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/xattrs_unsupported.go @@ -0,0 +1,28 @@ +// +build !linux + +package system + +import "syscall" + +const ( + // Value is larger than the maximum size allowed + E2BIG syscall.Errno = syscall.Errno(0) + + // Operation not supported + EOPNOTSUPP syscall.Errno = syscall.Errno(0) +) + +// Lgetxattr is not supported on platforms other than linux. +func Lgetxattr(path string, attr string) ([]byte, error) { + return nil, ErrNotSupportedPlatform +} + +// Lsetxattr is not supported on platforms other than linux. +func Lsetxattr(path string, attr string, data []byte, flags int) error { + return ErrNotSupportedPlatform +} + +// Llistxattr is not supported on platforms other than linux. +func Llistxattr(path string) ([]string, error) { + return nil, ErrNotSupportedPlatform +} diff --git a/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_cgo.go b/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_cgo.go new file mode 100644 index 0000000000..4f441c32c5 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_cgo.go @@ -0,0 +1,22 @@ +// +build linux,cgo + +package unshare + +import ( + "unsafe" +) + +/* +#cgo remoteclient CFLAGS: -Wall -Werror +#include +*/ +import "C" + +func getenv(name string) string { + cName := C.CString(name) + defer C.free(unsafe.Pointer(cName)) + + value := C.GoString(C.getenv(cName)) + + return value +} diff --git a/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_nocgo.go b/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_nocgo.go new file mode 100644 index 0000000000..a5005403af --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/unshare/getenv_linux_nocgo.go @@ -0,0 +1,11 @@ +// +build linux,!cgo + +package unshare + +import ( + "os" +) + +func getenv(name string) string { + return os.Getenv(name) +} diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare.c b/vendor/github.com/containers/storage/pkg/unshare/unshare.c new file mode 100644 index 0000000000..c0e359b276 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/unshare/unshare.c @@ -0,0 +1,378 @@ +#ifndef UNSHARE_NO_CODE_AT_ALL + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Open Source projects like conda-forge, want to package podman and are based + off of centos:6, Conda-force has minimal libc requirements and is lacking + the memfd.h file, so we use mmam.h +*/ +#ifndef MFD_ALLOW_SEALING +#define MFD_ALLOW_SEALING 2U +#endif +#ifndef MFD_CLOEXEC +#define MFD_CLOEXEC 1U +#endif + +#ifndef F_LINUX_SPECIFIC_BASE +#define F_LINUX_SPECIFIC_BASE 1024 +#endif +#ifndef F_ADD_SEALS +#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) +#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) +#endif +#ifndef F_SEAL_SEAL +#define F_SEAL_SEAL 0x0001LU +#endif +#ifndef F_SEAL_SHRINK +#define F_SEAL_SHRINK 0x0002LU +#endif +#ifndef F_SEAL_GROW +#define F_SEAL_GROW 0x0004LU +#endif +#ifndef F_SEAL_WRITE +#define F_SEAL_WRITE 0x0008LU +#endif + +#define BUFSTEP 1024 + +static const char *_max_user_namespaces = "/proc/sys/user/max_user_namespaces"; +static const char *_unprivileged_user_namespaces = "/proc/sys/kernel/unprivileged_userns_clone"; + +static int _containers_unshare_parse_envint(const char *envname) { + char *p, *q; + long l; + + p = getenv(envname); + if (p == NULL) { + return -1; + } + q = NULL; + l = strtol(p, &q, 10); + if ((q == NULL) || (*q != '\0')) { + fprintf(stderr, "Error parsing \"%s\"=\"%s\"!\n", envname, p); + _exit(1); + } + unsetenv(envname); + return l; +} + +static void _check_proc_sys_file(const char *path) +{ + FILE *fp; + char buf[32]; + size_t n_read; + long r; + + fp = fopen(path, "r"); + if (fp == NULL) { + if (errno != ENOENT) + fprintf(stderr, "Error reading %s: %m\n", _max_user_namespaces); + } else { + memset(buf, 0, sizeof(buf)); + n_read = fread(buf, 1, sizeof(buf) - 1, fp); + if (n_read > 0) { + r = atoi(buf); + if (r == 0) { + fprintf(stderr, "User namespaces are not enabled in %s.\n", path); + } + } else { + fprintf(stderr, "Error reading %s: no contents, should contain a number greater than 0.\n", path); + } + fclose(fp); + } +} + +static char **parse_proc_stringlist(const char *list) { + int fd, n, i, n_strings; + char *buf, *new_buf, **ret; + size_t size, new_size, used; + + fd = open(list, O_RDONLY); + if (fd == -1) { + return NULL; + } + buf = NULL; + size = 0; + used = 0; + for (;;) { + new_size = used + BUFSTEP; + new_buf = realloc(buf, new_size); + if (new_buf == NULL) { + free(buf); + fprintf(stderr, "realloc(%ld): out of memory\n", (long)(size + BUFSTEP)); + return NULL; + } + buf = new_buf; + size = new_size; + memset(buf + used, '\0', size - used); + n = read(fd, buf + used, size - used - 1); + if (n < 0) { + fprintf(stderr, "read(): %m\n"); + return NULL; + } + if (n == 0) { + break; + } + used += n; + } + close(fd); + n_strings = 0; + for (n = 0; n < used; n++) { + if ((n == 0) || (buf[n-1] == '\0')) { + n_strings++; + } + } + ret = calloc(n_strings + 1, sizeof(char *)); + if (ret == NULL) { + fprintf(stderr, "calloc(): out of memory\n"); + return NULL; + } + i = 0; + for (n = 0; n < used; n++) { + if ((n == 0) || (buf[n-1] == '\0')) { + ret[i++] = &buf[n]; + } + } + ret[i] = NULL; + return ret; +} + +/* + * Taken from the runc cloned_binary.c file + * Copyright (C) 2019 Aleksa Sarai + * Copyright (C) 2019 SUSE LLC + * + * This work is dual licensed under the following licenses. You may use, + * redistribute, and/or modify the work under the conditions of either (or + * both) licenses. + * + * === Apache-2.0 === + */ +static int try_bindfd(void) +{ + int fd, ret = -1; + char src[PATH_MAX] = {0}; + char template[64] = {0}; + + strncpy(template, "/tmp/containers.XXXXXX", sizeof(template) - 1); + + /* + * We need somewhere to mount it, mounting anything over /proc/self is a + * BAD idea on the host -- even if we do it temporarily. + */ + fd = mkstemp(template); + if (fd < 0) + return ret; + close(fd); + + ret = -EPERM; + + if (readlink("/proc/self/exe", src, sizeof (src) - 1) < 0) + goto out; + + if (mount(src, template, NULL, MS_BIND, NULL) < 0) + goto out; + if (mount(NULL, template, NULL, MS_REMOUNT | MS_BIND | MS_RDONLY, NULL) < 0) + goto out_umount; + + /* Get read-only handle that we're sure can't be made read-write. */ + ret = open(template, O_PATH | O_CLOEXEC); + +out_umount: + /* + * Make sure the MNT_DETACH works, otherwise we could get remounted + * read-write and that would be quite bad (the fd would be made read-write + * too, invalidating the protection). + */ + if (umount2(template, MNT_DETACH) < 0) { + if (ret >= 0) + close(ret); + ret = -ENOTRECOVERABLE; + } + +out: + /* + * We don't care about unlink errors, the worst that happens is that + * there's an empty file left around in STATEDIR. + */ + unlink(template); + return ret; +} + +static int copy_self_proc_exe(char **argv) { + char *exename; + int fd, mmfd, n_read, n_written; + struct stat st; + char buf[2048]; + + fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC); + if (fd == -1) { + fprintf(stderr, "open(\"/proc/self/exe\"): %m\n"); + return -1; + } + if (fstat(fd, &st) == -1) { + fprintf(stderr, "fstat(\"/proc/self/exe\"): %m\n"); + close(fd); + return -1; + } + exename = basename(argv[0]); + mmfd = syscall(SYS_memfd_create, exename, (long) MFD_ALLOW_SEALING | MFD_CLOEXEC); + if (mmfd == -1) { + fprintf(stderr, "memfd_create(): %m\n"); + goto close_fd; + } + for (;;) { + n_read = read(fd, buf, sizeof(buf)); + if (n_read < 0) { + fprintf(stderr, "read(\"/proc/self/exe\"): %m\n"); + return -1; + } + if (n_read == 0) { + break; + } + n_written = write(mmfd, buf, n_read); + if (n_written < 0) { + fprintf(stderr, "write(anonfd): %m\n"); + goto close_fd; + } + if (n_written != n_read) { + fprintf(stderr, "write(anonfd): short write (%d != %d)\n", n_written, n_read); + goto close_fd; + } + } + close(fd); + if (fcntl(mmfd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL) == -1) { + fprintf(stderr, "Close_Fd sealing memfd copy: %m\n"); + goto close_mmfd; + } + + return mmfd; + +close_fd: + close(fd); +close_mmfd: + close(mmfd); + return -1; +} +static int containers_reexec(int flags) { + char **argv; + int fd = -1; + + argv = parse_proc_stringlist("/proc/self/cmdline"); + if (argv == NULL) { + return -1; + } + + if (flags & CLONE_NEWNS) + fd = try_bindfd(); + if (fd < 0) + fd = copy_self_proc_exe(argv); + if (fd < 0) + return fd; + + if (fexecve(fd, argv, environ) == -1) { + close(fd); + fprintf(stderr, "Error during reexec(...): %m\n"); + return -1; + } + close(fd); + return 0; +} + +void _containers_unshare(void) +{ + int flags, pidfd, continuefd, n, pgrp, sid, ctty; + char buf[2048]; + + flags = _containers_unshare_parse_envint("_Containers-unshare"); + if (flags == -1) { + return; + } + if ((flags & CLONE_NEWUSER) != 0) { + if (unshare(CLONE_NEWUSER) == -1) { + fprintf(stderr, "Error during unshare(CLONE_NEWUSER): %m\n"); + _check_proc_sys_file (_max_user_namespaces); + _check_proc_sys_file (_unprivileged_user_namespaces); + _exit(1); + } + } + pidfd = _containers_unshare_parse_envint("_Containers-pid-pipe"); + if (pidfd != -1) { + snprintf(buf, sizeof(buf), "%llu", (unsigned long long) getpid()); + size_t size = write(pidfd, buf, strlen(buf)); + if (size != strlen(buf)) { + fprintf(stderr, "Error writing PID to pipe on fd %d: %m\n", pidfd); + _exit(1); + } + close(pidfd); + } + continuefd = _containers_unshare_parse_envint("_Containers-continue-pipe"); + if (continuefd != -1) { + n = read(continuefd, buf, sizeof(buf)); + if (n > 0) { + fprintf(stderr, "Error: %.*s\n", n, buf); + _exit(1); + } + close(continuefd); + } + sid = _containers_unshare_parse_envint("_Containers-setsid"); + if (sid == 1) { + if (setsid() == -1) { + fprintf(stderr, "Error during setsid: %m\n"); + _exit(1); + } + } + pgrp = _containers_unshare_parse_envint("_Containers-setpgrp"); + if (pgrp == 1) { + if (setpgrp() == -1) { + fprintf(stderr, "Error during setpgrp: %m\n"); + _exit(1); + } + } + ctty = _containers_unshare_parse_envint("_Containers-ctty"); + if (ctty != -1) { + if (ioctl(ctty, TIOCSCTTY, 0) == -1) { + fprintf(stderr, "Error while setting controlling terminal to %d: %m\n", ctty); + _exit(1); + } + } + if ((flags & CLONE_NEWUSER) != 0) { + if (setresgid(0, 0, 0) != 0) { + fprintf(stderr, "Error during setresgid(0): %m\n"); + _exit(1); + } + if (setresuid(0, 0, 0) != 0) { + fprintf(stderr, "Error during setresuid(0): %m\n"); + _exit(1); + } + } + if ((flags & ~CLONE_NEWUSER) != 0) { + if (unshare(flags & ~CLONE_NEWUSER) == -1) { + fprintf(stderr, "Error during unshare(...): %m\n"); + _exit(1); + } + } + if (containers_reexec(flags) != 0) { + _exit(1); + } + return; +} + +#endif // !UNSHARE_NO_CODE_AT_ALL diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare.go b/vendor/github.com/containers/storage/pkg/unshare/unshare.go new file mode 100644 index 0000000000..53cfeb0ec4 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/unshare/unshare.go @@ -0,0 +1,56 @@ +package unshare + +import ( + "fmt" + "os" + "os/user" + "sync" + + "github.com/pkg/errors" + "github.com/syndtr/gocapability/capability" +) + +var ( + homeDirOnce sync.Once + homeDirErr error + homeDir string + + hasCapSysAdminOnce sync.Once + hasCapSysAdminRet bool + hasCapSysAdminErr error +) + +// HomeDir returns the home directory for the current user. +func HomeDir() (string, error) { + homeDirOnce.Do(func() { + home := os.Getenv("HOME") + if home == "" { + usr, err := user.LookupId(fmt.Sprintf("%d", GetRootlessUID())) + if err != nil { + homeDir, homeDirErr = "", errors.Wrapf(err, "unable to resolve HOME directory") + return + } + homeDir, homeDirErr = usr.HomeDir, nil + return + } + homeDir, homeDirErr = home, nil + }) + return homeDir, homeDirErr +} + +// HasCapSysAdmin returns whether the current process has CAP_SYS_ADMIN. +func HasCapSysAdmin() (bool, error) { + hasCapSysAdminOnce.Do(func() { + currentCaps, err := capability.NewPid2(0) + if err != nil { + hasCapSysAdminErr = err + return + } + if err = currentCaps.Load(); err != nil { + hasCapSysAdminErr = err + return + } + hasCapSysAdminRet = currentCaps.Get(capability.EFFECTIVE, capability.CAP_SYS_ADMIN) + }) + return hasCapSysAdminRet, hasCapSysAdminErr +} diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare_cgo.go b/vendor/github.com/containers/storage/pkg/unshare/unshare_cgo.go new file mode 100644 index 0000000000..b3f8099f6a --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/unshare/unshare_cgo.go @@ -0,0 +1,10 @@ +// +build linux,cgo,!gccgo + +package unshare + +// #cgo CFLAGS: -Wall +// extern void _containers_unshare(void); +// void __attribute__((constructor)) init(void) { +// _containers_unshare(); +// } +import "C" diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare_gccgo.go b/vendor/github.com/containers/storage/pkg/unshare/unshare_gccgo.go new file mode 100644 index 0000000000..2f95da7d8e --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/unshare/unshare_gccgo.go @@ -0,0 +1,25 @@ +// +build linux,cgo,gccgo + +package unshare + +// #cgo CFLAGS: -Wall -Wextra +// extern void _containers_unshare(void); +// void __attribute__((constructor)) init(void) { +// _containers_unshare(); +// } +import "C" + +// This next bit is straight out of libcontainer. + +// AlwaysFalse is here to stay false +// (and be exported so the compiler doesn't optimize out its reference) +var AlwaysFalse bool + +func init() { + if AlwaysFalse { + // by referencing this C init() in a noop test, it will ensure the compiler + // links in the C function. + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65134 + C.init() + } +} diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare_linux.go b/vendor/github.com/containers/storage/pkg/unshare/unshare_linux.go new file mode 100644 index 0000000000..6d351ce80a --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/unshare/unshare_linux.go @@ -0,0 +1,605 @@ +// +build linux + +package unshare + +import ( + "bufio" + "bytes" + "fmt" + "io" + "os" + "os/exec" + "os/user" + "runtime" + "strconv" + "strings" + "sync" + "syscall" + + "github.com/containers/storage/pkg/idtools" + "github.com/containers/storage/pkg/reexec" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/syndtr/gocapability/capability" +) + +// Cmd wraps an exec.Cmd created by the reexec package in unshare(), and +// handles setting ID maps and other related settings by triggering +// initialization code in the child. +type Cmd struct { + *exec.Cmd + UnshareFlags int + UseNewuidmap bool + UidMappings []specs.LinuxIDMapping // nolint: golint + UseNewgidmap bool + GidMappings []specs.LinuxIDMapping // nolint: golint + GidMappingsEnableSetgroups bool + Setsid bool + Setpgrp bool + Ctty *os.File + OOMScoreAdj *int + Hook func(pid int) error +} + +// Command creates a new Cmd which can be customized. +func Command(args ...string) *Cmd { + cmd := reexec.Command(args...) + return &Cmd{ + Cmd: cmd, + } +} + +func getRootlessUID() int { + uidEnv := getenv("_CONTAINERS_ROOTLESS_UID") + if uidEnv != "" { + u, _ := strconv.Atoi(uidEnv) + return u + } + return os.Geteuid() +} + +func getRootlessGID() int { + gidEnv := getenv("_CONTAINERS_ROOTLESS_GID") + if gidEnv != "" { + u, _ := strconv.Atoi(gidEnv) + return u + } + + /* If the _CONTAINERS_ROOTLESS_UID is set, assume the gid==uid. */ + uidEnv := os.Getenv("_CONTAINERS_ROOTLESS_UID") + if uidEnv != "" { + u, _ := strconv.Atoi(uidEnv) + return u + } + return os.Getegid() +} + +func (c *Cmd) Start() error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + // Set an environment variable to tell the child to synchronize its startup. + if c.Env == nil { + c.Env = os.Environ() + } + c.Env = append(c.Env, fmt.Sprintf("_Containers-unshare=%d", c.UnshareFlags)) + + // Please the libpod "rootless" package to find the expected env variables. + if IsRootless() { + c.Env = append(c.Env, "_CONTAINERS_USERNS_CONFIGURED=done") + c.Env = append(c.Env, fmt.Sprintf("_CONTAINERS_ROOTLESS_UID=%d", getRootlessUID())) + c.Env = append(c.Env, fmt.Sprintf("_CONTAINERS_ROOTLESS_GID=%d", getRootlessGID())) + } + + // Create the pipe for reading the child's PID. + pidRead, pidWrite, err := os.Pipe() + if err != nil { + return errors.Wrapf(err, "error creating pid pipe") + } + c.Env = append(c.Env, fmt.Sprintf("_Containers-pid-pipe=%d", len(c.ExtraFiles)+3)) + c.ExtraFiles = append(c.ExtraFiles, pidWrite) + + // Create the pipe for letting the child know to proceed. + continueRead, continueWrite, err := os.Pipe() + if err != nil { + pidRead.Close() + pidWrite.Close() + return errors.Wrapf(err, "error creating pid pipe") + } + c.Env = append(c.Env, fmt.Sprintf("_Containers-continue-pipe=%d", len(c.ExtraFiles)+3)) + c.ExtraFiles = append(c.ExtraFiles, continueRead) + + // Pass along other instructions. + if c.Setsid { + c.Env = append(c.Env, "_Containers-setsid=1") + } + if c.Setpgrp { + c.Env = append(c.Env, "_Containers-setpgrp=1") + } + if c.Ctty != nil { + c.Env = append(c.Env, fmt.Sprintf("_Containers-ctty=%d", len(c.ExtraFiles)+3)) + c.ExtraFiles = append(c.ExtraFiles, c.Ctty) + } + + // Make sure we clean up our pipes. + defer func() { + if pidRead != nil { + pidRead.Close() + } + if pidWrite != nil { + pidWrite.Close() + } + if continueRead != nil { + continueRead.Close() + } + if continueWrite != nil { + continueWrite.Close() + } + }() + + // Start the new process. + err = c.Cmd.Start() + if err != nil { + return err + } + + // Close the ends of the pipes that the parent doesn't need. + continueRead.Close() + continueRead = nil + pidWrite.Close() + pidWrite = nil + + // Read the child's PID from the pipe. + pidString := "" + b := new(bytes.Buffer) + if _, err := io.Copy(b, pidRead); err != nil { + return errors.Wrapf(err, "Reading child PID") + } + pidString = b.String() + pid, err := strconv.Atoi(pidString) + if err != nil { + fmt.Fprintf(continueWrite, "error parsing PID %q: %v", pidString, err) + return errors.Wrapf(err, "error parsing PID %q", pidString) + } + pidString = fmt.Sprintf("%d", pid) + + // If we created a new user namespace, set any specified mappings. + if c.UnshareFlags&syscall.CLONE_NEWUSER != 0 { + // Always set "setgroups". + setgroups, err := os.OpenFile(fmt.Sprintf("/proc/%s/setgroups", pidString), os.O_TRUNC|os.O_WRONLY, 0) + if err != nil { + fmt.Fprintf(continueWrite, "error opening setgroups: %v", err) + return errors.Wrapf(err, "error opening /proc/%s/setgroups", pidString) + } + defer setgroups.Close() + if c.GidMappingsEnableSetgroups { + if _, err := fmt.Fprintf(setgroups, "allow"); err != nil { + fmt.Fprintf(continueWrite, "error writing \"allow\" to setgroups: %v", err) + return errors.Wrapf(err, "error opening \"allow\" to /proc/%s/setgroups", pidString) + } + } else { + if _, err := fmt.Fprintf(setgroups, "deny"); err != nil { + fmt.Fprintf(continueWrite, "error writing \"deny\" to setgroups: %v", err) + return errors.Wrapf(err, "error writing \"deny\" to /proc/%s/setgroups", pidString) + } + } + + if len(c.UidMappings) == 0 || len(c.GidMappings) == 0 { + uidmap, gidmap, err := GetHostIDMappings("") + if err != nil { + fmt.Fprintf(continueWrite, "Reading ID mappings in parent: %v", err) + return errors.Wrapf(err, "Reading ID mappings in parent") + } + if len(c.UidMappings) == 0 { + c.UidMappings = uidmap + for i := range c.UidMappings { + c.UidMappings[i].HostID = c.UidMappings[i].ContainerID + } + } + if len(c.GidMappings) == 0 { + c.GidMappings = gidmap + for i := range c.GidMappings { + c.GidMappings[i].HostID = c.GidMappings[i].ContainerID + } + } + } + + if len(c.GidMappings) > 0 { + // Build the GID map, since writing to the proc file has to be done all at once. + g := new(bytes.Buffer) + for _, m := range c.GidMappings { + fmt.Fprintf(g, "%d %d %d\n", m.ContainerID, m.HostID, m.Size) + } + gidmapSet := false + // Set the GID map. + if c.UseNewgidmap { + cmd := exec.Command("newgidmap", append([]string{pidString}, strings.Fields(strings.Replace(g.String(), "\n", " ", -1))...)...) + g.Reset() + cmd.Stdout = g + cmd.Stderr = g + err := cmd.Run() + if err == nil { + gidmapSet = true + } else { + logrus.Warnf("Error running newgidmap: %v: %s", err, g.String()) + logrus.Warnf("Falling back to single mapping") + g.Reset() + g.Write([]byte(fmt.Sprintf("0 %d 1\n", os.Getegid()))) + } + } + if !gidmapSet { + if c.UseNewgidmap { + setgroups, err := os.OpenFile(fmt.Sprintf("/proc/%s/setgroups", pidString), os.O_TRUNC|os.O_WRONLY, 0) + if err != nil { + fmt.Fprintf(continueWrite, "error opening /proc/%s/setgroups: %v", pidString, err) + return errors.Wrapf(err, "error opening /proc/%s/setgroups", pidString) + } + defer setgroups.Close() + if _, err := fmt.Fprintf(setgroups, "deny"); err != nil { + fmt.Fprintf(continueWrite, "error writing 'deny' to /proc/%s/setgroups: %v", pidString, err) + return errors.Wrapf(err, "error writing 'deny' to /proc/%s/setgroups", pidString) + } + } + gidmap, err := os.OpenFile(fmt.Sprintf("/proc/%s/gid_map", pidString), os.O_TRUNC|os.O_WRONLY, 0) + if err != nil { + fmt.Fprintf(continueWrite, "error opening /proc/%s/gid_map: %v", pidString, err) + return errors.Wrapf(err, "error opening /proc/%s/gid_map", pidString) + } + defer gidmap.Close() + if _, err := fmt.Fprintf(gidmap, "%s", g.String()); err != nil { + fmt.Fprintf(continueWrite, "error writing %q to /proc/%s/gid_map: %v", g.String(), pidString, err) + return errors.Wrapf(err, "error writing %q to /proc/%s/gid_map", g.String(), pidString) + } + } + } + + if len(c.UidMappings) > 0 { + // Build the UID map, since writing to the proc file has to be done all at once. + u := new(bytes.Buffer) + for _, m := range c.UidMappings { + fmt.Fprintf(u, "%d %d %d\n", m.ContainerID, m.HostID, m.Size) + } + uidmapSet := false + // Set the GID map. + if c.UseNewuidmap { + cmd := exec.Command("newuidmap", append([]string{pidString}, strings.Fields(strings.Replace(u.String(), "\n", " ", -1))...)...) + u.Reset() + cmd.Stdout = u + cmd.Stderr = u + err := cmd.Run() + if err == nil { + uidmapSet = true + } else { + logrus.Warnf("Error running newuidmap: %v: %s", err, u.String()) + logrus.Warnf("Falling back to single mapping") + u.Reset() + u.Write([]byte(fmt.Sprintf("0 %d 1\n", os.Geteuid()))) + } + } + if !uidmapSet { + uidmap, err := os.OpenFile(fmt.Sprintf("/proc/%s/uid_map", pidString), os.O_TRUNC|os.O_WRONLY, 0) + if err != nil { + fmt.Fprintf(continueWrite, "error opening /proc/%s/uid_map: %v", pidString, err) + return errors.Wrapf(err, "error opening /proc/%s/uid_map", pidString) + } + defer uidmap.Close() + if _, err := fmt.Fprintf(uidmap, "%s", u.String()); err != nil { + fmt.Fprintf(continueWrite, "error writing %q to /proc/%s/uid_map: %v", u.String(), pidString, err) + return errors.Wrapf(err, "error writing %q to /proc/%s/uid_map", u.String(), pidString) + } + } + } + } + + if c.OOMScoreAdj != nil { + oomScoreAdj, err := os.OpenFile(fmt.Sprintf("/proc/%s/oom_score_adj", pidString), os.O_TRUNC|os.O_WRONLY, 0) + if err != nil { + fmt.Fprintf(continueWrite, "error opening oom_score_adj: %v", err) + return errors.Wrapf(err, "error opening /proc/%s/oom_score_adj", pidString) + } + defer oomScoreAdj.Close() + if _, err := fmt.Fprintf(oomScoreAdj, "%d\n", *c.OOMScoreAdj); err != nil { + fmt.Fprintf(continueWrite, "error writing \"%d\" to oom_score_adj: %v", c.OOMScoreAdj, err) + return errors.Wrapf(err, "error writing \"%d\" to /proc/%s/oom_score_adj", c.OOMScoreAdj, pidString) + } + } + // Run any additional setup that we want to do before the child starts running proper. + if c.Hook != nil { + if err = c.Hook(pid); err != nil { + fmt.Fprintf(continueWrite, "hook error: %v", err) + return err + } + } + + return nil +} + +func (c *Cmd) Run() error { + if err := c.Start(); err != nil { + return err + } + return c.Wait() +} + +func (c *Cmd) CombinedOutput() ([]byte, error) { + return nil, errors.New("unshare: CombinedOutput() not implemented") +} + +func (c *Cmd) Output() ([]byte, error) { + return nil, errors.New("unshare: Output() not implemented") +} + +var ( + isRootlessOnce sync.Once + isRootless bool +) + +const ( + // UsernsEnvName is the environment variable, if set indicates in rootless mode + UsernsEnvName = "_CONTAINERS_USERNS_CONFIGURED" +) + +// IsRootless tells us if we are running in rootless mode +func IsRootless() bool { + isRootlessOnce.Do(func() { + isRootless = getRootlessUID() != 0 || getenv(UsernsEnvName) != "" + }) + return isRootless +} + +// GetRootlessUID returns the UID of the user in the parent userNS +func GetRootlessUID() int { + uidEnv := getenv("_CONTAINERS_ROOTLESS_UID") + if uidEnv != "" { + u, _ := strconv.Atoi(uidEnv) + return u + } + return os.Getuid() +} + +// RootlessEnv returns the environment settings for the rootless containers +func RootlessEnv() []string { + return append(os.Environ(), UsernsEnvName+"=done") +} + +type Runnable interface { + Run() error +} + +func bailOnError(err error, format string, a ...interface{}) { // nolint: golint,goprintffuncname + if err != nil { + if format != "" { + logrus.Errorf("%s: %v", fmt.Sprintf(format, a...), err) + } else { + logrus.Errorf("%v", err) + } + os.Exit(1) + } +} + +// MaybeReexecUsingUserNamespace re-exec the process in a new namespace +func MaybeReexecUsingUserNamespace(evenForRoot bool) { + // If we've already been through this once, no need to try again. + if os.Geteuid() == 0 && IsRootless() { + return + } + + var uidNum, gidNum uint64 + // Figure out who we are. + me, err := user.Current() + if !os.IsNotExist(err) { + bailOnError(err, "error determining current user") + uidNum, err = strconv.ParseUint(me.Uid, 10, 32) + bailOnError(err, "error parsing current UID %s", me.Uid) + gidNum, err = strconv.ParseUint(me.Gid, 10, 32) + bailOnError(err, "error parsing current GID %s", me.Gid) + } + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + // ID mappings to use to reexec ourselves. + var uidmap, gidmap []specs.LinuxIDMapping + if uidNum != 0 || evenForRoot { + // Read the set of ID mappings that we're allowed to use. Each + // range in /etc/subuid and /etc/subgid file is a starting host + // ID and a range size. + uidmap, gidmap, err = GetSubIDMappings(me.Username, me.Username) + if err != nil { + logrus.Warnf("Reading allowed ID mappings: %v", err) + } + if len(uidmap) == 0 { + logrus.Warnf("Found no UID ranges set aside for user %q in /etc/subuid.", me.Username) + } + if len(gidmap) == 0 { + logrus.Warnf("Found no GID ranges set aside for user %q in /etc/subgid.", me.Username) + } + // Map our UID and GID, then the subuid and subgid ranges, + // consecutively, starting at 0, to get the mappings to use for + // a copy of ourselves. + uidmap = append([]specs.LinuxIDMapping{{HostID: uint32(uidNum), ContainerID: 0, Size: 1}}, uidmap...) + gidmap = append([]specs.LinuxIDMapping{{HostID: uint32(gidNum), ContainerID: 0, Size: 1}}, gidmap...) + var rangeStart uint32 + for i := range uidmap { + uidmap[i].ContainerID = rangeStart + rangeStart += uidmap[i].Size + } + rangeStart = 0 + for i := range gidmap { + gidmap[i].ContainerID = rangeStart + rangeStart += gidmap[i].Size + } + } else { + // If we have CAP_SYS_ADMIN, then we don't need to create a new namespace in order to be able + // to use unshare(), so don't bother creating a new user namespace at this point. + capabilities, err := capability.NewPid(0) + bailOnError(err, "Reading the current capabilities sets") + if capabilities.Get(capability.EFFECTIVE, capability.CAP_SYS_ADMIN) { + return + } + // Read the set of ID mappings that we're currently using. + uidmap, gidmap, err = GetHostIDMappings("") + bailOnError(err, "Reading current ID mappings") + // Just reuse them. + for i := range uidmap { + uidmap[i].HostID = uidmap[i].ContainerID + } + for i := range gidmap { + gidmap[i].HostID = gidmap[i].ContainerID + } + } + + // Unlike most uses of reexec or unshare, we're using a name that + // _won't_ be recognized as a registered reexec handler, since we + // _want_ to fall through reexec.Init() to the normal main(). + cmd := Command(append([]string{fmt.Sprintf("%s-in-a-user-namespace", os.Args[0])}, os.Args[1:]...)...) + + // If, somehow, we don't become UID 0 in our child, indicate that the child shouldn't try again. + err = os.Setenv(UsernsEnvName, "1") + bailOnError(err, "error setting %s=1 in environment", UsernsEnvName) + + // Set the default isolation type to use the "rootless" method. + if _, present := os.LookupEnv("BUILDAH_ISOLATION"); !present { + if err = os.Setenv("BUILDAH_ISOLATION", "rootless"); err != nil { + if err := os.Setenv("BUILDAH_ISOLATION", "rootless"); err != nil { + logrus.Errorf("Setting BUILDAH_ISOLATION=rootless in environment: %v", err) + os.Exit(1) + } + } + } + + // Reuse our stdio. + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + // Set up a new user namespace with the ID mapping. + cmd.UnshareFlags = syscall.CLONE_NEWUSER | syscall.CLONE_NEWNS + cmd.UseNewuidmap = uidNum != 0 + cmd.UidMappings = uidmap + cmd.UseNewgidmap = uidNum != 0 + cmd.GidMappings = gidmap + cmd.GidMappingsEnableSetgroups = true + + // Finish up. + logrus.Debugf("Running %+v with environment %+v, UID map %+v, and GID map %+v", cmd.Cmd.Args, os.Environ(), cmd.UidMappings, cmd.GidMappings) + ExecRunnable(cmd, nil) +} + +// ExecRunnable runs the specified unshare command, captures its exit status, +// and exits with the same status. +func ExecRunnable(cmd Runnable, cleanup func()) { + exit := func(status int) { + if cleanup != nil { + cleanup() + } + os.Exit(status) + } + if err := cmd.Run(); err != nil { + if exitError, ok := errors.Cause(err).(*exec.ExitError); ok { + if exitError.ProcessState.Exited() { + if waitStatus, ok := exitError.ProcessState.Sys().(syscall.WaitStatus); ok { + if waitStatus.Exited() { + logrus.Errorf("%v", exitError) + exit(waitStatus.ExitStatus()) + } + if waitStatus.Signaled() { + logrus.Errorf("%v", exitError) + exit(int(waitStatus.Signal()) + 128) + } + } + } + } + logrus.Errorf("%v", err) + logrus.Errorf("(Unable to determine exit status)") + exit(1) + } + exit(0) +} + +// getHostIDMappings reads mappings from the named node under /proc. +func getHostIDMappings(path string) ([]specs.LinuxIDMapping, error) { + var mappings []specs.LinuxIDMapping + f, err := os.Open(path) + if err != nil { + return nil, errors.Wrapf(err, "Reading ID mappings from %q", path) + } + defer f.Close() + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Text() + fields := strings.Fields(line) + if len(fields) != 3 { + return nil, errors.Errorf("line %q from %q has %d fields, not 3", line, path, len(fields)) + } + cid, err := strconv.ParseUint(fields[0], 10, 32) + if err != nil { + return nil, errors.Wrapf(err, "error parsing container ID value %q from line %q in %q", fields[0], line, path) + } + hid, err := strconv.ParseUint(fields[1], 10, 32) + if err != nil { + return nil, errors.Wrapf(err, "error parsing host ID value %q from line %q in %q", fields[1], line, path) + } + size, err := strconv.ParseUint(fields[2], 10, 32) + if err != nil { + return nil, errors.Wrapf(err, "error parsing size value %q from line %q in %q", fields[2], line, path) + } + mappings = append(mappings, specs.LinuxIDMapping{ContainerID: uint32(cid), HostID: uint32(hid), Size: uint32(size)}) + } + return mappings, nil +} + +// GetHostIDMappings reads mappings for the specified process (or the current +// process if pid is "self" or an empty string) from the kernel. +func GetHostIDMappings(pid string) ([]specs.LinuxIDMapping, []specs.LinuxIDMapping, error) { + if pid == "" { + pid = "self" + } + uidmap, err := getHostIDMappings(fmt.Sprintf("/proc/%s/uid_map", pid)) + if err != nil { + return nil, nil, err + } + gidmap, err := getHostIDMappings(fmt.Sprintf("/proc/%s/gid_map", pid)) + if err != nil { + return nil, nil, err + } + return uidmap, gidmap, nil +} + +// GetSubIDMappings reads mappings from /etc/subuid and /etc/subgid. +func GetSubIDMappings(user, group string) ([]specs.LinuxIDMapping, []specs.LinuxIDMapping, error) { + mappings, err := idtools.NewIDMappings(user, group) + if err != nil { + return nil, nil, errors.Wrapf(err, "Reading subuid mappings for user %q and subgid mappings for group %q", user, group) + } + var uidmap, gidmap []specs.LinuxIDMapping + for _, m := range mappings.UIDs() { + uidmap = append(uidmap, specs.LinuxIDMapping{ + ContainerID: uint32(m.ContainerID), + HostID: uint32(m.HostID), + Size: uint32(m.Size), + }) + } + for _, m := range mappings.GIDs() { + gidmap = append(gidmap, specs.LinuxIDMapping{ + ContainerID: uint32(m.ContainerID), + HostID: uint32(m.HostID), + Size: uint32(m.Size), + }) + } + return uidmap, gidmap, nil +} + +// ParseIDMappings parses mapping triples. +func ParseIDMappings(uidmap, gidmap []string) ([]idtools.IDMap, []idtools.IDMap, error) { + uid, err := idtools.ParseIDMap(uidmap, "userns-uid-map") + if err != nil { + return nil, nil, err + } + gid, err := idtools.ParseIDMap(gidmap, "userns-gid-map") + if err != nil { + return nil, nil, err + } + return uid, gid, nil +} diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported.go b/vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported.go new file mode 100644 index 0000000000..bf4d567b8a --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported.go @@ -0,0 +1,45 @@ +// +build !linux + +package unshare + +import ( + "os" + + "github.com/containers/storage/pkg/idtools" + "github.com/opencontainers/runtime-spec/specs-go" +) + +const ( + // UsernsEnvName is the environment variable, if set indicates in rootless mode + UsernsEnvName = "_CONTAINERS_USERNS_CONFIGURED" +) + +// IsRootless tells us if we are running in rootless mode +func IsRootless() bool { + return false +} + +// GetRootlessUID returns the UID of the user in the parent userNS +func GetRootlessUID() int { + return os.Getuid() +} + +// RootlessEnv returns the environment settings for the rootless containers +func RootlessEnv() []string { + return append(os.Environ(), UsernsEnvName+"=") +} + +// MaybeReexecUsingUserNamespace re-exec the process in a new namespace +func MaybeReexecUsingUserNamespace(evenForRoot bool) { +} + +// GetHostIDMappings reads mappings for the specified process (or the current +// process if pid is "self" or an empty string) from the kernel. +func GetHostIDMappings(pid string) ([]specs.LinuxIDMapping, []specs.LinuxIDMapping, error) { + return nil, nil, nil +} + +// ParseIDMappings parses mapping triples. +func ParseIDMappings(uidmap, gidmap []string) ([]idtools.IDMap, []idtools.IDMap, error) { + return nil, nil, nil +} diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported_cgo.go b/vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported_cgo.go new file mode 100644 index 0000000000..d5f2d22a80 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported_cgo.go @@ -0,0 +1,10 @@ +// +build !linux,cgo + +package unshare + +// Go refuses to compile a subpackage with CGO_ENABLED=1 if there is a *.c file but no 'import "C"'. +// OTOH if we did have an 'import "C"', the Linux-only code would fail to compile. +// So, satisfy the Go compiler by using import "C" but #ifdef-ing out all of the code. + +// #cgo CPPFLAGS: -DUNSHARE_NO_CODE_AT_ALL +import "C" diff --git a/vendor/github.com/dgraph-io/ristretto/.deepsource.toml b/vendor/github.com/dgraph-io/ristretto/.deepsource.toml new file mode 100644 index 0000000000..40609eff3f --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/.deepsource.toml @@ -0,0 +1,17 @@ +version = 1 + +test_patterns = [ + '**/*_test.go' +] + +exclude_patterns = [ + +] + +[[analyzers]] +name = 'go' +enabled = true + + + [analyzers.meta] + import_path = 'github.com/dgraph-io/ristretto' diff --git a/vendor/github.com/dgraph-io/ristretto/CHANGELOG.md b/vendor/github.com/dgraph-io/ristretto/CHANGELOG.md new file mode 100644 index 0000000000..da964bc0c8 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/CHANGELOG.md @@ -0,0 +1,172 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project will adhere to [Semantic Versioning](http://semver.org/spec/v2.0.0.html) starting v1.0.0. + +## Unreleased + +## [0.1.0] - 2021-06-03 + +[0.1.0]: https://github.com/dgraph-io/ristretto/compare/v0.1.0..v0.0.3 +This release contains bug fixes and improvements to Ristretto. It also contains +major updates to the z package. The z package contains types such as Tree (B+ +tree), Buffer, Mmap file, etc. All these types are used in Badger and Dgraph to +improve performance and reduce memory requirements. + +### Changed +- Make item public. Add a new onReject call for rejected items. (#180) + +### Added +- Use z.Buffer backing for B+ tree (#268) +- expose GetTTL function (#270) +- docs(README): Ristretto is production-ready. (#267) +- Add IterateKV (#265) +- feat(super-flags): Add GetPath method in superflags (#258) +- add GetDuration to SuperFlag (#248) +- add Has, GetFloat64, and GetInt64 to SuperFlag (#247) +- move SuperFlag to Ristretto (#246) +- add SuperFlagHelp tool to generate flag help text (#251) +- allow empty defaults in SuperFlag (#254) +- add mmaped b+ tree (#207) +- Add API to allow the MaxCost of an existing cache to be updated. (#200) +- Add OnExit handler which can be used for manual memory management (#183) +- Add life expectancy histogram (#182) +- Add mechanism to wait for items to be processed. (#184) + +### Fixed +- change expiration type from int64 to time.Time (#277) +- fix(buffer): make buffer capacity atleast defaultCapacity (#273) +- Fixes for z.PersistentTree (#272) +- Initialize persistent tree correctly (#271) +- use xxhash v2 (#266) +- update comments to correctly reflect counter space usage (#189) +- enable riscv64 builds (#264) +- Switch from log to glog (#263) +- Use Fibonacci for latency numbers +- cache: fix race when clearning a cache (#261) +- Check for keys without values in superflags (#259) +- chore(perf): using tags instead of runtime callers to improve the performance of leak detection (#255) +- fix(Flags): panic on user errors (#256) +- fix SuperFlagHelp newline (#252) +- fix(arm): Fix crashing under ARMv6 due to memory mis-alignment (#239) +- Fix incorrect unit test coverage depiction (#245) +- chore(histogram): adding percentile in histogram (#241) +- fix(windows): use filepath instead of path (#244) +- fix(MmapFile): Close the fd before deleting the file (#242) +- Fixes CGO_ENABLED=0 compilation error (#240) +- fix(build): fix build on non-amd64 architectures (#238) +- fix(b+tree): Do not double the size of btree (#237) +- fix(jemalloc): Fix the stats of jemalloc (#236) +- Don't print stuff, only return strings. +- Bring memclrNoHeapPointers to z (#235) +- increase number of buffers from 32 to 64 in allocator (#234) +- Set minSize to 1MB. +- Opt(btree): Use Go memory instead of mmap files +- Opt(btree): Lightweight stats calculation +- Put padding internally to z.Buffer +- Chore(z): Add SetTmpDir API to set the temp directory (#233) +- Add a BufferFrom +- Bring z.Allocator and z.AllocatorPool back +- Fix(z.Allocator): Make Allocator use Go memory +- Updated ZeroOut to use a simple for loop. (#231) +- Add concurrency back +- Add a test to check concurrency of Allocator. +- Fix(buffer): Expose padding by z.Buffer's APIs and fix test (#222) +- AllocateSlice should Truncate if the file is not big enough (#226) +- Zero out allocations for structs now that we're reusing Allocators. +- Fix the ristretto substring +- Deal with nil z.AllocatorPool +- Create an AllocatorPool class. +- chore(btree): clean NewTree API (#225) +- fix(MmapFile): Don't error out if fileSize > sz (#224) +- feat(btree): allow option to reset btree and mmaping it to specified file. (#223) +- Use mremap on Linux instead of munmap+mmap (#221) +- Reuse pages in B+ tree (#220) +- fix(allocator): make nil allocator return go byte slice (#217) +- fix(buffer): Make padding internal to z.buffer (#216) +- chore(buffer): add a parent directory field in z.Buffer (#215) +- Make Allocator concurrent +- Fix infinite loop in allocator (#214) +- Add trim func +- Use allocator pool. Turn off freelist. +- Add freelists to Allocator to reuse. +- make DeleteBelow delete values that are less than lo (#211) +- Avoid an unnecessary Load procedure in IncrementOffset. +- Add Stats method in Btree. +- chore(script): fix local test script (#210) +- fix(btree): Increase buffer size if needed. (#209) +- chore(btree): add occupancy ratio, search benchmark and compact bug fix (#208) +- Add licenses, remove prints, and fix a bug in compact +- Add IncrementOffset API for z.buffers (#206) +- Show count when printing histogram (#201) +- Zbuffer: Add LenNoPadding and make padding 8 bytes (#204) +- Allocate Go memory in case allocator is nil. +- Add leak detection via leak build flag and fix a leak during cache.Close. +- Add some APIs for allocator and buffer +- Sync before truncation or close. +- Handle nil MmapFile for Sync. +- Public methods must not panic after Close() (#202) +- Check for RD_ONLY correctly. +- Modify MmapFile APIs +- Add a bunch of APIs around MmapFile +- Move APIs for mmapfile creation over to z package. +- Add ZeroOut func +- Add SliceOffsets +- z: Add TotalSize method on bloom filter (#197) +- Add Msync func +- Buffer: Use 256 GB mmap size instead of MaxInt64 (#198) +- Add a simple test to check next2Pow +- Improve memory performance (#195) +- Have a way to automatically mmap a growing buffer (#196) +- Introduce Mmapped buffers and Merge Sort (#194) +- Add a way to access an allocator via reference. +- Use jemalloc.a to ensure compilation with the Go binary +- Fix up a build issue with ReadMemStats +- Add ReadMemStats function (#193) +- Allocator helps allocate memory to be used by unsafe structs (#192) +- Improve histogram output +- Move Closer from y to z (#191) +- Add histogram.Mean() method (#188) +- Introduce Calloc: Manual Memory Management via jemalloc (#186) + +## [0.0.3] - 2020-07-06 + +[0.0.3]: https://github.com/dgraph-io/ristretto/compare/v0.0.2..v0.0.3 + +### Changed + +### Added + +### Fixed + +- z: use MemHashString and xxhash.Sum64String ([#153][]) +- Check conflict key before updating expiration map. ([#154][]) +- Fix race condition in Cache.Clear ([#133][]) +- Improve handling of updated items ([#168][]) +- Fix droppedSets count while updating the item ([#171][]) + +## [0.0.2] - 2020-02-24 + +[0.0.2]: https://github.com/dgraph-io/ristretto/compare/v0.0.1..v0.0.2 + +### Added + +- Sets with TTL. ([#122][]) + +### Fixed + +- Fix the way metrics are handled for deletions. ([#111][]) +- Support nil `*Cache` values in `Clear` and `Close`. ([#119][]) +- Delete item immediately. ([#113][]) +- Remove key from policy after TTL eviction. ([#130][]) + +[#111]: https://github.com/dgraph-io/ristretto/issues/111 +[#113]: https://github.com/dgraph-io/ristretto/issues/113 +[#119]: https://github.com/dgraph-io/ristretto/issues/119 +[#122]: https://github.com/dgraph-io/ristretto/issues/122 +[#130]: https://github.com/dgraph-io/ristretto/issues/130 + +## 0.0.1 + +First release. Basic cache functionality based on a LFU policy. diff --git a/vendor/github.com/dgraph-io/ristretto/LICENSE b/vendor/github.com/dgraph-io/ristretto/LICENSE new file mode 100644 index 0000000000..d9a10c0d8e --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/LICENSE @@ -0,0 +1,176 @@ + 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 diff --git a/vendor/github.com/dgraph-io/ristretto/README.md b/vendor/github.com/dgraph-io/ristretto/README.md new file mode 100644 index 0000000000..f4bb28cd75 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/README.md @@ -0,0 +1,220 @@ +# Ristretto +[![Go Doc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/dgraph-io/ristretto) +[![Go Report Card](https://img.shields.io/badge/go%20report-A%2B-brightgreen)](https://goreportcard.com/report/github.com/dgraph-io/ristretto) +[![Coverage](https://gocover.io/_badge/github.com/dgraph-io/ristretto)](https://gocover.io/github.com/dgraph-io/ristretto) +![Tests](https://github.com/dgraph-io/ristretto/workflows/tests/badge.svg) + +Ristretto is a fast, concurrent cache library built with a focus on performance and correctness. + +The motivation to build Ristretto comes from the need for a contention-free +cache in [Dgraph][]. + +**Use [Discuss Issues](https://discuss.dgraph.io/tags/c/issues/35/ristretto/40) for reporting issues about this repository.** + +[Dgraph]: https://github.com/dgraph-io/dgraph + +## Features + +* **High Hit Ratios** - with our unique admission/eviction policy pairing, Ristretto's performance is best in class. + * **Eviction: SampledLFU** - on par with exact LRU and better performance on Search and Database traces. + * **Admission: TinyLFU** - extra performance with little memory overhead (12 bits per counter). +* **Fast Throughput** - we use a variety of techniques for managing contention and the result is excellent throughput. +* **Cost-Based Eviction** - any large new item deemed valuable can evict multiple smaller items (cost could be anything). +* **Fully Concurrent** - you can use as many goroutines as you want with little throughput degradation. +* **Metrics** - optional performance metrics for throughput, hit ratios, and other stats. +* **Simple API** - just figure out your ideal `Config` values and you're off and running. + +## Status + +Ristretto is production-ready. See [Projects using Ristretto](#projects-using-ristretto). + +## Table of Contents + +* [Usage](#Usage) + * [Example](#Example) + * [Config](#Config) + * [NumCounters](#Config) + * [MaxCost](#Config) + * [BufferItems](#Config) + * [Metrics](#Config) + * [OnEvict](#Config) + * [KeyToHash](#Config) + * [Cost](#Config) +* [Benchmarks](#Benchmarks) + * [Hit Ratios](#Hit-Ratios) + * [Search](#Search) + * [Database](#Database) + * [Looping](#Looping) + * [CODASYL](#CODASYL) + * [Throughput](#Throughput) + * [Mixed](#Mixed) + * [Read](#Read) + * [Write](#Write) +* [Projects using Ristretto](#projects-using-ristretto) +* [FAQ](#FAQ) + +## Usage + +### Example + +```go +func main() { + cache, err := ristretto.NewCache(&ristretto.Config{ + NumCounters: 1e7, // number of keys to track frequency of (10M). + MaxCost: 1 << 30, // maximum cost of cache (1GB). + BufferItems: 64, // number of keys per Get buffer. + }) + if err != nil { + panic(err) + } + + // set a value with a cost of 1 + cache.Set("key", "value", 1) + + // wait for value to pass through buffers + time.Sleep(10 * time.Millisecond) + + value, found := cache.Get("key") + if !found { + panic("missing value") + } + fmt.Println(value) + cache.Del("key") +} +``` + +### Config + +The `Config` struct is passed to `NewCache` when creating Ristretto instances (see the example above). + +**NumCounters** `int64` + +NumCounters is the number of 4-bit access counters to keep for admission and eviction. We've seen good performance in setting this to 10x the number of items you expect to keep in the cache when full. + +For example, if you expect each item to have a cost of 1 and MaxCost is 100, set NumCounters to 1,000. Or, if you use variable cost values but expect the cache to hold around 10,000 items when full, set NumCounters to 100,000. The important thing is the *number of unique items* in the full cache, not necessarily the MaxCost value. + +**MaxCost** `int64` + +MaxCost is how eviction decisions are made. For example, if MaxCost is 100 and a new item with a cost of 1 increases total cache cost to 101, 1 item will be evicted. + +MaxCost can also be used to denote the max size in bytes. For example, if MaxCost is 1,000,000 (1MB) and the cache is full with 1,000 1KB items, a new item (that's accepted) would cause 5 1KB items to be evicted. + +MaxCost could be anything as long as it matches how you're using the cost values when calling Set. + +**BufferItems** `int64` + +BufferItems is the size of the Get buffers. The best value we've found for this is 64. + +If for some reason you see Get performance decreasing with lots of contention (you shouldn't), try increasing this value in increments of 64. This is a fine-tuning mechanism and you probably won't have to touch this. + +**Metrics** `bool` + +Metrics is true when you want real-time logging of a variety of stats. The reason this is a Config flag is because there's a 10% throughput performance overhead. + +**OnEvict** `func(hashes [2]uint64, value interface{}, cost int64)` + +OnEvict is called for every eviction. + +**KeyToHash** `func(key interface{}) [2]uint64` + +KeyToHash is the hashing algorithm used for every key. If this is nil, Ristretto has a variety of [defaults depending on the underlying interface type](https://github.com/dgraph-io/ristretto/blob/master/z/z.go#L19-L41). + +Note that if you want 128bit hashes you should use the full `[2]uint64`, +otherwise just fill the `uint64` at the `0` position and it will behave like +any 64bit hash. + +**Cost** `func(value interface{}) int64` + +Cost is an optional function you can pass to the Config in order to evaluate +item cost at runtime, and only for the Set calls that aren't dropped (this is +useful if calculating item cost is particularly expensive and you don't want to +waste time on items that will be dropped anyways). + +To signal to Ristretto that you'd like to use this Cost function: + +1. Set the Cost field to a non-nil function. +2. When calling Set for new items or item updates, use a `cost` of 0. + +## Benchmarks + +The benchmarks can be found in https://github.com/dgraph-io/benchmarks/tree/master/cachebench/ristretto. + +### Hit Ratios + +#### Search + +This trace is described as "disk read accesses initiated by a large commercial +search engine in response to various web search requests." + +

+ +

+ +#### Database + +This trace is described as "a database server running at a commercial site +running an ERP application on top of a commercial database." + +

+ +

+ +#### Looping + +This trace demonstrates a looping access pattern. + +

+ +

+ +#### CODASYL + +This trace is described as "references to a CODASYL database for a one hour +period." + +

+ +

+ +### Throughput + +All throughput benchmarks were ran on an Intel Core i7-8700K (3.7GHz) with 16gb +of RAM. + +#### Mixed + +

+ +

+ +#### Read + +

+ +

+ +#### Write + +

+ +

+ +## Projects Using Ristretto + +Below is a list of known projects that use Ristretto: + +- [Badger](https://github.com/dgraph-io/badger) - Embeddable key-value DB in Go +- [Dgraph](https://github.com/dgraph-io/dgraph) - Horizontally scalable and distributed GraphQL database with a graph backend +- [Vitess](https://github.com/vitessio/vitess) - database clustering system for horizontal scaling of MySQL + +## FAQ + +### How are you achieving this performance? What shortcuts are you taking? + +We go into detail in the [Ristretto blog post](https://blog.dgraph.io/post/introducing-ristretto-high-perf-go-cache/), but in short: our throughput performance can be attributed to a mix of batching and eventual consistency. Our hit ratio performance is mostly due to an excellent [admission policy](https://arxiv.org/abs/1512.00727) and SampledLFU eviction policy. + +As for "shortcuts," the only thing Ristretto does that could be construed as one is dropping some Set calls. That means a Set call for a new item (updates are guaranteed) isn't guaranteed to make it into the cache. The new item could be dropped at two points: when passing through the Set buffer or when passing through the admission policy. However, this doesn't affect hit ratios much at all as we expect the most popular items to be Set multiple times and eventually make it in the cache. + +### Is Ristretto distributed? + +No, it's just like any other Go library that you can import into your project and use in a single process. diff --git a/vendor/github.com/dgraph-io/ristretto/cache.go b/vendor/github.com/dgraph-io/ristretto/cache.go new file mode 100644 index 0000000000..7226245bcc --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/cache.go @@ -0,0 +1,719 @@ +/* + * Copyright 2019 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +// Ristretto is a fast, fixed size, in-memory cache with a dual focus on +// throughput and hit ratio performance. You can easily add Ristretto to an +// existing system and keep the most valuable data where you need it. +package ristretto + +import ( + "bytes" + "errors" + "fmt" + "sync" + "sync/atomic" + "time" + "unsafe" + + "github.com/dgraph-io/ristretto/z" +) + +var ( + // TODO: find the optimal value for this or make it configurable + setBufSize = 32 * 1024 +) + +type itemCallback func(*Item) + +const itemSize = int64(unsafe.Sizeof(storeItem{})) + +// Cache is a thread-safe implementation of a hashmap with a TinyLFU admission +// policy and a Sampled LFU eviction policy. You can use the same Cache instance +// from as many goroutines as you want. +type Cache struct { + // store is the central concurrent hashmap where key-value items are stored. + store store + // policy determines what gets let in to the cache and what gets kicked out. + policy policy + // getBuf is a custom ring buffer implementation that gets pushed to when + // keys are read. + getBuf *ringBuffer + // setBuf is a buffer allowing us to batch/drop Sets during times of high + // contention. + setBuf chan *Item + // onEvict is called for item evictions. + onEvict itemCallback + // onReject is called when an item is rejected via admission policy. + onReject itemCallback + // onExit is called whenever a value goes out of scope from the cache. + onExit (func(interface{})) + // KeyToHash function is used to customize the key hashing algorithm. + // Each key will be hashed using the provided function. If keyToHash value + // is not set, the default keyToHash function is used. + keyToHash func(interface{}) (uint64, uint64) + // stop is used to stop the processItems goroutine. + stop chan struct{} + // indicates whether cache is closed. + isClosed bool + // cost calculates cost from a value. + cost func(value interface{}) int64 + // ignoreInternalCost dictates whether to ignore the cost of internally storing + // the item in the cost calculation. + ignoreInternalCost bool + // cleanupTicker is used to periodically check for entries whose TTL has passed. + cleanupTicker *time.Ticker + // Metrics contains a running log of important statistics like hits, misses, + // and dropped items. + Metrics *Metrics +} + +// Config is passed to NewCache for creating new Cache instances. +type Config struct { + // NumCounters determines the number of counters (keys) to keep that hold + // access frequency information. It's generally a good idea to have more + // counters than the max cache capacity, as this will improve eviction + // accuracy and subsequent hit ratios. + // + // For example, if you expect your cache to hold 1,000,000 items when full, + // NumCounters should be 10,000,000 (10x). Each counter takes up roughly + // 3 bytes (4 bits for each counter * 4 copies plus about a byte per + // counter for the bloom filter). Note that the number of counters is + // internally rounded up to the nearest power of 2, so the space usage + // may be a little larger than 3 bytes * NumCounters. + NumCounters int64 + // MaxCost can be considered as the cache capacity, in whatever units you + // choose to use. + // + // For example, if you want the cache to have a max capacity of 100MB, you + // would set MaxCost to 100,000,000 and pass an item's number of bytes as + // the `cost` parameter for calls to Set. If new items are accepted, the + // eviction process will take care of making room for the new item and not + // overflowing the MaxCost value. + MaxCost int64 + // BufferItems determines the size of Get buffers. + // + // Unless you have a rare use case, using `64` as the BufferItems value + // results in good performance. + BufferItems int64 + // Metrics determines whether cache statistics are kept during the cache's + // lifetime. There *is* some overhead to keeping statistics, so you should + // only set this flag to true when testing or throughput performance isn't a + // major factor. + Metrics bool + // OnEvict is called for every eviction and passes the hashed key, value, + // and cost to the function. + OnEvict func(item *Item) + // OnReject is called for every rejection done via the policy. + OnReject func(item *Item) + // OnExit is called whenever a value is removed from cache. This can be + // used to do manual memory deallocation. Would also be called on eviction + // and rejection of the value. + OnExit func(val interface{}) + // KeyToHash function is used to customize the key hashing algorithm. + // Each key will be hashed using the provided function. If keyToHash value + // is not set, the default keyToHash function is used. + KeyToHash func(key interface{}) (uint64, uint64) + // Cost evaluates a value and outputs a corresponding cost. This function + // is ran after Set is called for a new item or an item update with a cost + // param of 0. + Cost func(value interface{}) int64 + // IgnoreInternalCost set to true indicates to the cache that the cost of + // internally storing the value should be ignored. This is useful when the + // cost passed to set is not using bytes as units. Keep in mind that setting + // this to true will increase the memory usage. + IgnoreInternalCost bool +} + +type itemFlag byte + +const ( + itemNew itemFlag = iota + itemDelete + itemUpdate +) + +// Item is passed to setBuf so items can eventually be added to the cache. +type Item struct { + flag itemFlag + Key uint64 + Conflict uint64 + Value interface{} + Cost int64 + Expiration time.Time + wg *sync.WaitGroup +} + +// NewCache returns a new Cache instance and any configuration errors, if any. +func NewCache(config *Config) (*Cache, error) { + switch { + case config.NumCounters == 0: + return nil, errors.New("NumCounters can't be zero") + case config.MaxCost == 0: + return nil, errors.New("MaxCost can't be zero") + case config.BufferItems == 0: + return nil, errors.New("BufferItems can't be zero") + } + policy := newPolicy(config.NumCounters, config.MaxCost) + cache := &Cache{ + store: newStore(), + policy: policy, + getBuf: newRingBuffer(policy, config.BufferItems), + setBuf: make(chan *Item, setBufSize), + keyToHash: config.KeyToHash, + stop: make(chan struct{}), + cost: config.Cost, + ignoreInternalCost: config.IgnoreInternalCost, + cleanupTicker: time.NewTicker(time.Duration(bucketDurationSecs) * time.Second / 2), + } + cache.onExit = func(val interface{}) { + if config.OnExit != nil && val != nil { + config.OnExit(val) + } + } + cache.onEvict = func(item *Item) { + if config.OnEvict != nil { + config.OnEvict(item) + } + cache.onExit(item.Value) + } + cache.onReject = func(item *Item) { + if config.OnReject != nil { + config.OnReject(item) + } + cache.onExit(item.Value) + } + if cache.keyToHash == nil { + cache.keyToHash = z.KeyToHash + } + if config.Metrics { + cache.collectMetrics() + } + // NOTE: benchmarks seem to show that performance decreases the more + // goroutines we have running cache.processItems(), so 1 should + // usually be sufficient + go cache.processItems() + return cache, nil +} + +func (c *Cache) Wait() { + if c == nil || c.isClosed { + return + } + wg := &sync.WaitGroup{} + wg.Add(1) + c.setBuf <- &Item{wg: wg} + wg.Wait() +} + +// Get returns the value (if any) and a boolean representing whether the +// value was found or not. The value can be nil and the boolean can be true at +// the same time. +func (c *Cache) Get(key interface{}) (interface{}, bool) { + if c == nil || c.isClosed || key == nil { + return nil, false + } + keyHash, conflictHash := c.keyToHash(key) + c.getBuf.Push(keyHash) + value, ok := c.store.Get(keyHash, conflictHash) + if ok { + c.Metrics.add(hit, keyHash, 1) + } else { + c.Metrics.add(miss, keyHash, 1) + } + return value, ok +} + +// Set attempts to add the key-value item to the cache. If it returns false, +// then the Set was dropped and the key-value item isn't added to the cache. If +// it returns true, there's still a chance it could be dropped by the policy if +// its determined that the key-value item isn't worth keeping, but otherwise the +// item will be added and other items will be evicted in order to make room. +// +// To dynamically evaluate the items cost using the Config.Coster function, set +// the cost parameter to 0 and Coster will be ran when needed in order to find +// the items true cost. +func (c *Cache) Set(key, value interface{}, cost int64) bool { + return c.SetWithTTL(key, value, cost, 0*time.Second) +} + +// SetWithTTL works like Set but adds a key-value pair to the cache that will expire +// after the specified TTL (time to live) has passed. A zero value means the value never +// expires, which is identical to calling Set. A negative value is a no-op and the value +// is discarded. +func (c *Cache) SetWithTTL(key, value interface{}, cost int64, ttl time.Duration) bool { + if c == nil || c.isClosed || key == nil { + return false + } + + var expiration time.Time + switch { + case ttl == 0: + // No expiration. + break + case ttl < 0: + // Treat this a a no-op. + return false + default: + expiration = time.Now().Add(ttl) + } + + keyHash, conflictHash := c.keyToHash(key) + i := &Item{ + flag: itemNew, + Key: keyHash, + Conflict: conflictHash, + Value: value, + Cost: cost, + Expiration: expiration, + } + // cost is eventually updated. The expiration must also be immediately updated + // to prevent items from being prematurely removed from the map. + if prev, ok := c.store.Update(i); ok { + c.onExit(prev) + i.flag = itemUpdate + } + // Attempt to send item to policy. + select { + case c.setBuf <- i: + return true + default: + if i.flag == itemUpdate { + // Return true if this was an update operation since we've already + // updated the store. For all the other operations (set/delete), we + // return false which means the item was not inserted. + return true + } + c.Metrics.add(dropSets, keyHash, 1) + return false + } +} + +// Del deletes the key-value item from the cache if it exists. +func (c *Cache) Del(key interface{}) { + if c == nil || c.isClosed || key == nil { + return + } + keyHash, conflictHash := c.keyToHash(key) + // Delete immediately. + _, prev := c.store.Del(keyHash, conflictHash) + c.onExit(prev) + // If we've set an item, it would be applied slightly later. + // So we must push the same item to `setBuf` with the deletion flag. + // This ensures that if a set is followed by a delete, it will be + // applied in the correct order. + c.setBuf <- &Item{ + flag: itemDelete, + Key: keyHash, + Conflict: conflictHash, + } +} + +// GetTTL returns the TTL for the specified key and a bool that is true if the +// item was found and is not expired. +func (c *Cache) GetTTL(key interface{}) (time.Duration, bool) { + if c == nil || key == nil { + return 0, false + } + + keyHash, conflictHash := c.keyToHash(key) + if _, ok := c.store.Get(keyHash, conflictHash); !ok { + // not found + return 0, false + } + + expiration := c.store.Expiration(keyHash) + if expiration.IsZero() { + // found but no expiration + return 0, true + } + + if time.Now().After(expiration) { + // found but expired + return 0, false + } + + return time.Until(expiration), true +} + +// Close stops all goroutines and closes all channels. +func (c *Cache) Close() { + if c == nil || c.isClosed { + return + } + c.Clear() + + // Block until processItems goroutine is returned. + c.stop <- struct{}{} + close(c.stop) + close(c.setBuf) + c.policy.Close() + c.isClosed = true +} + +// Clear empties the hashmap and zeroes all policy counters. Note that this is +// not an atomic operation (but that shouldn't be a problem as it's assumed that +// Set/Get calls won't be occurring until after this). +func (c *Cache) Clear() { + if c == nil || c.isClosed { + return + } + // Block until processItems goroutine is returned. + c.stop <- struct{}{} + + // Clear out the setBuf channel. +loop: + for { + select { + case i := <-c.setBuf: + if i.wg != nil { + i.wg.Done() + continue + } + if i.flag != itemUpdate { + // In itemUpdate, the value is already set in the store. So, no need to call + // onEvict here. + c.onEvict(i) + } + default: + break loop + } + } + + // Clear value hashmap and policy data. + c.policy.Clear() + c.store.Clear(c.onEvict) + // Only reset metrics if they're enabled. + if c.Metrics != nil { + c.Metrics.Clear() + } + // Restart processItems goroutine. + go c.processItems() +} + +// MaxCost returns the max cost of the cache. +func (c *Cache) MaxCost() int64 { + if c == nil { + return 0 + } + return c.policy.MaxCost() +} + +// UpdateMaxCost updates the maxCost of an existing cache. +func (c *Cache) UpdateMaxCost(maxCost int64) { + if c == nil { + return + } + c.policy.UpdateMaxCost(maxCost) +} + +// processItems is ran by goroutines processing the Set buffer. +func (c *Cache) processItems() { + startTs := make(map[uint64]time.Time) + numToKeep := 100000 // TODO: Make this configurable via options. + + trackAdmission := func(key uint64) { + if c.Metrics == nil { + return + } + startTs[key] = time.Now() + if len(startTs) > numToKeep { + for k := range startTs { + if len(startTs) <= numToKeep { + break + } + delete(startTs, k) + } + } + } + onEvict := func(i *Item) { + if ts, has := startTs[i.Key]; has { + c.Metrics.trackEviction(int64(time.Since(ts) / time.Second)) + delete(startTs, i.Key) + } + if c.onEvict != nil { + c.onEvict(i) + } + } + + for { + select { + case i := <-c.setBuf: + if i.wg != nil { + i.wg.Done() + continue + } + // Calculate item cost value if new or update. + if i.Cost == 0 && c.cost != nil && i.flag != itemDelete { + i.Cost = c.cost(i.Value) + } + if !c.ignoreInternalCost { + // Add the cost of internally storing the object. + i.Cost += itemSize + } + + switch i.flag { + case itemNew: + victims, added := c.policy.Add(i.Key, i.Cost) + if added { + c.store.Set(i) + c.Metrics.add(keyAdd, i.Key, 1) + trackAdmission(i.Key) + } else { + c.onReject(i) + } + for _, victim := range victims { + victim.Conflict, victim.Value = c.store.Del(victim.Key, 0) + onEvict(victim) + } + + case itemUpdate: + c.policy.Update(i.Key, i.Cost) + + case itemDelete: + c.policy.Del(i.Key) // Deals with metrics updates. + _, val := c.store.Del(i.Key, i.Conflict) + c.onExit(val) + } + case <-c.cleanupTicker.C: + c.store.Cleanup(c.policy, onEvict) + case <-c.stop: + return + } + } +} + +// collectMetrics just creates a new *Metrics instance and adds the pointers +// to the cache and policy instances. +func (c *Cache) collectMetrics() { + c.Metrics = newMetrics() + c.policy.CollectMetrics(c.Metrics) +} + +type metricType int + +const ( + // The following 2 keep track of hits and misses. + hit = iota + miss + // The following 3 keep track of number of keys added, updated and evicted. + keyAdd + keyUpdate + keyEvict + // The following 2 keep track of cost of keys added and evicted. + costAdd + costEvict + // The following keep track of how many sets were dropped or rejected later. + dropSets + rejectSets + // The following 2 keep track of how many gets were kept and dropped on the + // floor. + dropGets + keepGets + // This should be the final enum. Other enums should be set before this. + doNotUse +) + +func stringFor(t metricType) string { + switch t { + case hit: + return "hit" + case miss: + return "miss" + case keyAdd: + return "keys-added" + case keyUpdate: + return "keys-updated" + case keyEvict: + return "keys-evicted" + case costAdd: + return "cost-added" + case costEvict: + return "cost-evicted" + case dropSets: + return "sets-dropped" + case rejectSets: + return "sets-rejected" // by policy. + case dropGets: + return "gets-dropped" + case keepGets: + return "gets-kept" + default: + return "unidentified" + } +} + +// Metrics is a snapshot of performance statistics for the lifetime of a cache instance. +type Metrics struct { + all [doNotUse][]*uint64 + + mu sync.RWMutex + life *z.HistogramData // Tracks the life expectancy of a key. +} + +func newMetrics() *Metrics { + s := &Metrics{ + life: z.NewHistogramData(z.HistogramBounds(1, 16)), + } + for i := 0; i < doNotUse; i++ { + s.all[i] = make([]*uint64, 256) + slice := s.all[i] + for j := range slice { + slice[j] = new(uint64) + } + } + return s +} + +func (p *Metrics) add(t metricType, hash, delta uint64) { + if p == nil { + return + } + valp := p.all[t] + // Avoid false sharing by padding at least 64 bytes of space between two + // atomic counters which would be incremented. + idx := (hash % 25) * 10 + atomic.AddUint64(valp[idx], delta) +} + +func (p *Metrics) get(t metricType) uint64 { + if p == nil { + return 0 + } + valp := p.all[t] + var total uint64 + for i := range valp { + total += atomic.LoadUint64(valp[i]) + } + return total +} + +// Hits is the number of Get calls where a value was found for the corresponding key. +func (p *Metrics) Hits() uint64 { + return p.get(hit) +} + +// Misses is the number of Get calls where a value was not found for the corresponding key. +func (p *Metrics) Misses() uint64 { + return p.get(miss) +} + +// KeysAdded is the total number of Set calls where a new key-value item was added. +func (p *Metrics) KeysAdded() uint64 { + return p.get(keyAdd) +} + +// KeysUpdated is the total number of Set calls where the value was updated. +func (p *Metrics) KeysUpdated() uint64 { + return p.get(keyUpdate) +} + +// KeysEvicted is the total number of keys evicted. +func (p *Metrics) KeysEvicted() uint64 { + return p.get(keyEvict) +} + +// CostAdded is the sum of costs that have been added (successful Set calls). +func (p *Metrics) CostAdded() uint64 { + return p.get(costAdd) +} + +// CostEvicted is the sum of all costs that have been evicted. +func (p *Metrics) CostEvicted() uint64 { + return p.get(costEvict) +} + +// SetsDropped is the number of Set calls that don't make it into internal +// buffers (due to contention or some other reason). +func (p *Metrics) SetsDropped() uint64 { + return p.get(dropSets) +} + +// SetsRejected is the number of Set calls rejected by the policy (TinyLFU). +func (p *Metrics) SetsRejected() uint64 { + return p.get(rejectSets) +} + +// GetsDropped is the number of Get counter increments that are dropped +// internally. +func (p *Metrics) GetsDropped() uint64 { + return p.get(dropGets) +} + +// GetsKept is the number of Get counter increments that are kept. +func (p *Metrics) GetsKept() uint64 { + return p.get(keepGets) +} + +// Ratio is the number of Hits over all accesses (Hits + Misses). This is the +// percentage of successful Get calls. +func (p *Metrics) Ratio() float64 { + if p == nil { + return 0.0 + } + hits, misses := p.get(hit), p.get(miss) + if hits == 0 && misses == 0 { + return 0.0 + } + return float64(hits) / float64(hits+misses) +} + +func (p *Metrics) trackEviction(numSeconds int64) { + if p == nil { + return + } + p.mu.Lock() + defer p.mu.Unlock() + p.life.Update(numSeconds) +} + +func (p *Metrics) LifeExpectancySeconds() *z.HistogramData { + if p == nil { + return nil + } + p.mu.RLock() + defer p.mu.RUnlock() + return p.life.Copy() +} + +// Clear resets all the metrics. +func (p *Metrics) Clear() { + if p == nil { + return + } + for i := 0; i < doNotUse; i++ { + for j := range p.all[i] { + atomic.StoreUint64(p.all[i][j], 0) + } + } + p.mu.Lock() + p.life = z.NewHistogramData(z.HistogramBounds(1, 16)) + p.mu.Unlock() +} + +// String returns a string representation of the metrics. +func (p *Metrics) String() string { + if p == nil { + return "" + } + var buf bytes.Buffer + for i := 0; i < doNotUse; i++ { + t := metricType(i) + fmt.Fprintf(&buf, "%s: %d ", stringFor(t), p.get(t)) + } + fmt.Fprintf(&buf, "gets-total: %d ", p.get(hit)+p.get(miss)) + fmt.Fprintf(&buf, "hit-ratio: %.2f", p.Ratio()) + return buf.String() +} diff --git a/vendor/github.com/dgraph-io/ristretto/go.mod b/vendor/github.com/dgraph-io/ristretto/go.mod new file mode 100644 index 0000000000..f7a620c57e --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/go.mod @@ -0,0 +1,14 @@ +module github.com/dgraph-io/ristretto + +go 1.12 + +require ( + github.com/cespare/xxhash/v2 v2.1.1 + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 + github.com/dustin/go-humanize v1.0.0 + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b + github.com/pkg/errors v0.9.1 + github.com/stretchr/testify v1.4.0 + golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f +) diff --git a/vendor/github.com/dgraph-io/ristretto/go.sum b/vendor/github.com/dgraph-io/ristretto/go.sum new file mode 100644 index 0000000000..6e3d790326 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/go.sum @@ -0,0 +1,24 @@ +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/dgraph-io/ristretto/policy.go b/vendor/github.com/dgraph-io/ristretto/policy.go new file mode 100644 index 0000000000..bf23f91fd9 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/policy.go @@ -0,0 +1,423 @@ +/* + * Copyright 2020 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package ristretto + +import ( + "math" + "sync" + "sync/atomic" + + "github.com/dgraph-io/ristretto/z" +) + +const ( + // lfuSample is the number of items to sample when looking at eviction + // candidates. 5 seems to be the most optimal number [citation needed]. + lfuSample = 5 +) + +// policy is the interface encapsulating eviction/admission behavior. +// +// TODO: remove this interface and just rename defaultPolicy to policy, as we +// are probably only going to use/implement/maintain one policy. +type policy interface { + ringConsumer + // Add attempts to Add the key-cost pair to the Policy. It returns a slice + // of evicted keys and a bool denoting whether or not the key-cost pair + // was added. If it returns true, the key should be stored in cache. + Add(uint64, int64) ([]*Item, bool) + // Has returns true if the key exists in the Policy. + Has(uint64) bool + // Del deletes the key from the Policy. + Del(uint64) + // Cap returns the available capacity. + Cap() int64 + // Close stops all goroutines and closes all channels. + Close() + // Update updates the cost value for the key. + Update(uint64, int64) + // Cost returns the cost value of a key or -1 if missing. + Cost(uint64) int64 + // Optionally, set stats object to track how policy is performing. + CollectMetrics(*Metrics) + // Clear zeroes out all counters and clears hashmaps. + Clear() + // MaxCost returns the current max cost of the cache policy. + MaxCost() int64 + // UpdateMaxCost updates the max cost of the cache policy. + UpdateMaxCost(int64) +} + +func newPolicy(numCounters, maxCost int64) policy { + return newDefaultPolicy(numCounters, maxCost) +} + +type defaultPolicy struct { + sync.Mutex + admit *tinyLFU + evict *sampledLFU + itemsCh chan []uint64 + stop chan struct{} + isClosed bool + metrics *Metrics +} + +func newDefaultPolicy(numCounters, maxCost int64) *defaultPolicy { + p := &defaultPolicy{ + admit: newTinyLFU(numCounters), + evict: newSampledLFU(maxCost), + itemsCh: make(chan []uint64, 3), + stop: make(chan struct{}), + } + go p.processItems() + return p +} + +func (p *defaultPolicy) CollectMetrics(metrics *Metrics) { + p.metrics = metrics + p.evict.metrics = metrics +} + +type policyPair struct { + key uint64 + cost int64 +} + +func (p *defaultPolicy) processItems() { + for { + select { + case items := <-p.itemsCh: + p.Lock() + p.admit.Push(items) + p.Unlock() + case <-p.stop: + return + } + } +} + +func (p *defaultPolicy) Push(keys []uint64) bool { + if p.isClosed { + return false + } + + if len(keys) == 0 { + return true + } + + select { + case p.itemsCh <- keys: + p.metrics.add(keepGets, keys[0], uint64(len(keys))) + return true + default: + p.metrics.add(dropGets, keys[0], uint64(len(keys))) + return false + } +} + +// Add decides whether the item with the given key and cost should be accepted by +// the policy. It returns the list of victims that have been evicted and a boolean +// indicating whether the incoming item should be accepted. +func (p *defaultPolicy) Add(key uint64, cost int64) ([]*Item, bool) { + p.Lock() + defer p.Unlock() + + // Cannot add an item bigger than entire cache. + if cost > p.evict.getMaxCost() { + return nil, false + } + + // No need to go any further if the item is already in the cache. + if has := p.evict.updateIfHas(key, cost); has { + // An update does not count as an addition, so return false. + return nil, false + } + + // If the execution reaches this point, the key doesn't exist in the cache. + // Calculate the remaining room in the cache (usually bytes). + room := p.evict.roomLeft(cost) + if room >= 0 { + // There's enough room in the cache to store the new item without + // overflowing. Do that now and stop here. + p.evict.add(key, cost) + p.metrics.add(costAdd, key, uint64(cost)) + return nil, true + } + + // incHits is the hit count for the incoming item. + incHits := p.admit.Estimate(key) + // sample is the eviction candidate pool to be filled via random sampling. + // TODO: perhaps we should use a min heap here. Right now our time + // complexity is N for finding the min. Min heap should bring it down to + // O(lg N). + sample := make([]*policyPair, 0, lfuSample) + // As items are evicted they will be appended to victims. + victims := make([]*Item, 0) + + // Delete victims until there's enough space or a minKey is found that has + // more hits than incoming item. + for ; room < 0; room = p.evict.roomLeft(cost) { + // Fill up empty slots in sample. + sample = p.evict.fillSample(sample) + + // Find minimally used item in sample. + minKey, minHits, minId, minCost := uint64(0), int64(math.MaxInt64), 0, int64(0) + for i, pair := range sample { + // Look up hit count for sample key. + if hits := p.admit.Estimate(pair.key); hits < minHits { + minKey, minHits, minId, minCost = pair.key, hits, i, pair.cost + } + } + + // If the incoming item isn't worth keeping in the policy, reject. + if incHits < minHits { + p.metrics.add(rejectSets, key, 1) + return victims, false + } + + // Delete the victim from metadata. + p.evict.del(minKey) + + // Delete the victim from sample. + sample[minId] = sample[len(sample)-1] + sample = sample[:len(sample)-1] + // Store victim in evicted victims slice. + victims = append(victims, &Item{ + Key: minKey, + Conflict: 0, + Cost: minCost, + }) + } + + p.evict.add(key, cost) + p.metrics.add(costAdd, key, uint64(cost)) + return victims, true +} + +func (p *defaultPolicy) Has(key uint64) bool { + p.Lock() + _, exists := p.evict.keyCosts[key] + p.Unlock() + return exists +} + +func (p *defaultPolicy) Del(key uint64) { + p.Lock() + p.evict.del(key) + p.Unlock() +} + +func (p *defaultPolicy) Cap() int64 { + p.Lock() + capacity := int64(p.evict.getMaxCost() - p.evict.used) + p.Unlock() + return capacity +} + +func (p *defaultPolicy) Update(key uint64, cost int64) { + p.Lock() + p.evict.updateIfHas(key, cost) + p.Unlock() +} + +func (p *defaultPolicy) Cost(key uint64) int64 { + p.Lock() + if cost, found := p.evict.keyCosts[key]; found { + p.Unlock() + return cost + } + p.Unlock() + return -1 +} + +func (p *defaultPolicy) Clear() { + p.Lock() + p.admit.clear() + p.evict.clear() + p.Unlock() +} + +func (p *defaultPolicy) Close() { + if p.isClosed { + return + } + + // Block until the p.processItems goroutine returns. + p.stop <- struct{}{} + close(p.stop) + close(p.itemsCh) + p.isClosed = true +} + +func (p *defaultPolicy) MaxCost() int64 { + if p == nil || p.evict == nil { + return 0 + } + return p.evict.getMaxCost() +} + +func (p *defaultPolicy) UpdateMaxCost(maxCost int64) { + if p == nil || p.evict == nil { + return + } + p.evict.updateMaxCost(maxCost) +} + +// sampledLFU is an eviction helper storing key-cost pairs. +type sampledLFU struct { + // NOTE: align maxCost to 64-bit boundary for use with atomic. + // As per https://golang.org/pkg/sync/atomic/: "On ARM, x86-32, + // and 32-bit MIPS, it is the caller’s responsibility to arrange + // for 64-bit alignment of 64-bit words accessed atomically. + // The first word in a variable or in an allocated struct, array, + // or slice can be relied upon to be 64-bit aligned." + maxCost int64 + used int64 + metrics *Metrics + keyCosts map[uint64]int64 +} + +func newSampledLFU(maxCost int64) *sampledLFU { + return &sampledLFU{ + keyCosts: make(map[uint64]int64), + maxCost: maxCost, + } +} + +func (p *sampledLFU) getMaxCost() int64 { + return atomic.LoadInt64(&p.maxCost) +} + +func (p *sampledLFU) updateMaxCost(maxCost int64) { + atomic.StoreInt64(&p.maxCost, maxCost) +} + +func (p *sampledLFU) roomLeft(cost int64) int64 { + return p.getMaxCost() - (p.used + cost) +} + +func (p *sampledLFU) fillSample(in []*policyPair) []*policyPair { + if len(in) >= lfuSample { + return in + } + for key, cost := range p.keyCosts { + in = append(in, &policyPair{key, cost}) + if len(in) >= lfuSample { + return in + } + } + return in +} + +func (p *sampledLFU) del(key uint64) { + cost, ok := p.keyCosts[key] + if !ok { + return + } + p.used -= cost + delete(p.keyCosts, key) + p.metrics.add(costEvict, key, uint64(cost)) + p.metrics.add(keyEvict, key, 1) +} + +func (p *sampledLFU) add(key uint64, cost int64) { + p.keyCosts[key] = cost + p.used += cost +} + +func (p *sampledLFU) updateIfHas(key uint64, cost int64) bool { + if prev, found := p.keyCosts[key]; found { + // Update the cost of an existing key, but don't worry about evicting. + // Evictions will be handled the next time a new item is added. + p.metrics.add(keyUpdate, key, 1) + if prev > cost { + diff := prev - cost + p.metrics.add(costAdd, key, ^uint64(uint64(diff)-1)) + } else if cost > prev { + diff := cost - prev + p.metrics.add(costAdd, key, uint64(diff)) + } + p.used += cost - prev + p.keyCosts[key] = cost + return true + } + return false +} + +func (p *sampledLFU) clear() { + p.used = 0 + p.keyCosts = make(map[uint64]int64) +} + +// tinyLFU is an admission helper that keeps track of access frequency using +// tiny (4-bit) counters in the form of a count-min sketch. +// tinyLFU is NOT thread safe. +type tinyLFU struct { + freq *cmSketch + door *z.Bloom + incrs int64 + resetAt int64 +} + +func newTinyLFU(numCounters int64) *tinyLFU { + return &tinyLFU{ + freq: newCmSketch(numCounters), + door: z.NewBloomFilter(float64(numCounters), 0.01), + resetAt: numCounters, + } +} + +func (p *tinyLFU) Push(keys []uint64) { + for _, key := range keys { + p.Increment(key) + } +} + +func (p *tinyLFU) Estimate(key uint64) int64 { + hits := p.freq.Estimate(key) + if p.door.Has(key) { + hits++ + } + return hits +} + +func (p *tinyLFU) Increment(key uint64) { + // Flip doorkeeper bit if not already done. + if added := p.door.AddIfNotHas(key); !added { + // Increment count-min counter if doorkeeper bit is already set. + p.freq.Increment(key) + } + p.incrs++ + if p.incrs >= p.resetAt { + p.reset() + } +} + +func (p *tinyLFU) reset() { + // Zero out incrs. + p.incrs = 0 + // clears doorkeeper bits + p.door.Clear() + // halves count-min counters + p.freq.Reset() +} + +func (p *tinyLFU) clear() { + p.incrs = 0 + p.door.Clear() + p.freq.Clear() +} diff --git a/vendor/github.com/dgraph-io/ristretto/ring.go b/vendor/github.com/dgraph-io/ristretto/ring.go new file mode 100644 index 0000000000..5dbed4cc59 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/ring.go @@ -0,0 +1,91 @@ +/* + * Copyright 2019 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package ristretto + +import ( + "sync" +) + +// ringConsumer is the user-defined object responsible for receiving and +// processing items in batches when buffers are drained. +type ringConsumer interface { + Push([]uint64) bool +} + +// ringStripe is a singular ring buffer that is not concurrent safe. +type ringStripe struct { + cons ringConsumer + data []uint64 + capa int +} + +func newRingStripe(cons ringConsumer, capa int64) *ringStripe { + return &ringStripe{ + cons: cons, + data: make([]uint64, 0, capa), + capa: int(capa), + } +} + +// Push appends an item in the ring buffer and drains (copies items and +// sends to Consumer) if full. +func (s *ringStripe) Push(item uint64) { + s.data = append(s.data, item) + // Decide if the ring buffer should be drained. + if len(s.data) >= s.capa { + // Send elements to consumer and create a new ring stripe. + if s.cons.Push(s.data) { + s.data = make([]uint64, 0, s.capa) + } else { + s.data = s.data[:0] + } + } +} + +// ringBuffer stores multiple buffers (stripes) and distributes Pushed items +// between them to lower contention. +// +// This implements the "batching" process described in the BP-Wrapper paper +// (section III part A). +type ringBuffer struct { + pool *sync.Pool +} + +// newRingBuffer returns a striped ring buffer. The Consumer in ringConfig will +// be called when individual stripes are full and need to drain their elements. +func newRingBuffer(cons ringConsumer, capa int64) *ringBuffer { + // LOSSY buffers use a very simple sync.Pool for concurrently reusing + // stripes. We do lose some stripes due to GC (unheld items in sync.Pool + // are cleared), but the performance gains generally outweigh the small + // percentage of elements lost. The performance primarily comes from + // low-level runtime functions used in the standard library that aren't + // available to us (such as runtime_procPin()). + return &ringBuffer{ + pool: &sync.Pool{ + New: func() interface{} { return newRingStripe(cons, capa) }, + }, + } +} + +// Push adds an element to one of the internal stripes and possibly drains if +// the stripe becomes full. +func (b *ringBuffer) Push(item uint64) { + // Reuse or create a new stripe. + stripe := b.pool.Get().(*ringStripe) + stripe.Push(item) + b.pool.Put(stripe) +} diff --git a/vendor/github.com/dgraph-io/ristretto/sketch.go b/vendor/github.com/dgraph-io/ristretto/sketch.go new file mode 100644 index 0000000000..10f414689a --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/sketch.go @@ -0,0 +1,155 @@ +/* + * Copyright 2019 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +// This package includes multiple probabalistic data structures needed for +// admission/eviction metadata. Most are Counting Bloom Filter variations, but +// a caching-specific feature that is also required is a "freshness" mechanism, +// which basically serves as a "lifetime" process. This freshness mechanism +// was described in the original TinyLFU paper [1], but other mechanisms may +// be better suited for certain data distributions. +// +// [1]: https://arxiv.org/abs/1512.00727 +package ristretto + +import ( + "fmt" + "math/rand" + "time" +) + +// cmSketch is a Count-Min sketch implementation with 4-bit counters, heavily +// based on Damian Gryski's CM4 [1]. +// +// [1]: https://github.com/dgryski/go-tinylfu/blob/master/cm4.go +type cmSketch struct { + rows [cmDepth]cmRow + seed [cmDepth]uint64 + mask uint64 +} + +const ( + // cmDepth is the number of counter copies to store (think of it as rows). + cmDepth = 4 +) + +func newCmSketch(numCounters int64) *cmSketch { + if numCounters == 0 { + panic("cmSketch: bad numCounters") + } + // Get the next power of 2 for better cache performance. + numCounters = next2Power(numCounters) + sketch := &cmSketch{mask: uint64(numCounters - 1)} + // Initialize rows of counters and seeds. + source := rand.New(rand.NewSource(time.Now().UnixNano())) + for i := 0; i < cmDepth; i++ { + sketch.seed[i] = source.Uint64() + sketch.rows[i] = newCmRow(numCounters) + } + return sketch +} + +// Increment increments the count(ers) for the specified key. +func (s *cmSketch) Increment(hashed uint64) { + for i := range s.rows { + s.rows[i].increment((hashed ^ s.seed[i]) & s.mask) + } +} + +// Estimate returns the value of the specified key. +func (s *cmSketch) Estimate(hashed uint64) int64 { + min := byte(255) + for i := range s.rows { + val := s.rows[i].get((hashed ^ s.seed[i]) & s.mask) + if val < min { + min = val + } + } + return int64(min) +} + +// Reset halves all counter values. +func (s *cmSketch) Reset() { + for _, r := range s.rows { + r.reset() + } +} + +// Clear zeroes all counters. +func (s *cmSketch) Clear() { + for _, r := range s.rows { + r.clear() + } +} + +// cmRow is a row of bytes, with each byte holding two counters. +type cmRow []byte + +func newCmRow(numCounters int64) cmRow { + return make(cmRow, numCounters/2) +} + +func (r cmRow) get(n uint64) byte { + return byte(r[n/2]>>((n&1)*4)) & 0x0f +} + +func (r cmRow) increment(n uint64) { + // Index of the counter. + i := n / 2 + // Shift distance (even 0, odd 4). + s := (n & 1) * 4 + // Counter value. + v := (r[i] >> s) & 0x0f + // Only increment if not max value (overflow wrap is bad for LFU). + if v < 15 { + r[i] += 1 << s + } +} + +func (r cmRow) reset() { + // Halve each counter. + for i := range r { + r[i] = (r[i] >> 1) & 0x77 + } +} + +func (r cmRow) clear() { + // Zero each counter. + for i := range r { + r[i] = 0 + } +} + +func (r cmRow) string() string { + s := "" + for i := uint64(0); i < uint64(len(r)*2); i++ { + s += fmt.Sprintf("%02d ", (r[(i/2)]>>((i&1)*4))&0x0f) + } + s = s[:len(s)-1] + return s +} + +// next2Power rounds x up to the next power of 2, if it's not already one. +func next2Power(x int64) int64 { + x-- + x |= x >> 1 + x |= x >> 2 + x |= x >> 4 + x |= x >> 8 + x |= x >> 16 + x |= x >> 32 + x++ + return x +} diff --git a/vendor/github.com/dgraph-io/ristretto/store.go b/vendor/github.com/dgraph-io/ristretto/store.go new file mode 100644 index 0000000000..e42a98b787 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/store.go @@ -0,0 +1,242 @@ +/* + * Copyright 2019 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package ristretto + +import ( + "sync" + "time" +) + +// TODO: Do we need this to be a separate struct from Item? +type storeItem struct { + key uint64 + conflict uint64 + value interface{} + expiration time.Time +} + +// store is the interface fulfilled by all hash map implementations in this +// file. Some hash map implementations are better suited for certain data +// distributions than others, so this allows us to abstract that out for use +// in Ristretto. +// +// Every store is safe for concurrent usage. +type store interface { + // Get returns the value associated with the key parameter. + Get(uint64, uint64) (interface{}, bool) + // Expiration returns the expiration time for this key. + Expiration(uint64) time.Time + // Set adds the key-value pair to the Map or updates the value if it's + // already present. The key-value pair is passed as a pointer to an + // item object. + Set(*Item) + // Del deletes the key-value pair from the Map. + Del(uint64, uint64) (uint64, interface{}) + // Update attempts to update the key with a new value and returns true if + // successful. + Update(*Item) (interface{}, bool) + // Cleanup removes items that have an expired TTL. + Cleanup(policy policy, onEvict itemCallback) + // Clear clears all contents of the store. + Clear(onEvict itemCallback) +} + +// newStore returns the default store implementation. +func newStore() store { + return newShardedMap() +} + +const numShards uint64 = 256 + +type shardedMap struct { + shards []*lockedMap + expiryMap *expirationMap +} + +func newShardedMap() *shardedMap { + sm := &shardedMap{ + shards: make([]*lockedMap, int(numShards)), + expiryMap: newExpirationMap(), + } + for i := range sm.shards { + sm.shards[i] = newLockedMap(sm.expiryMap) + } + return sm +} + +func (sm *shardedMap) Get(key, conflict uint64) (interface{}, bool) { + return sm.shards[key%numShards].get(key, conflict) +} + +func (sm *shardedMap) Expiration(key uint64) time.Time { + return sm.shards[key%numShards].Expiration(key) +} + +func (sm *shardedMap) Set(i *Item) { + if i == nil { + // If item is nil make this Set a no-op. + return + } + + sm.shards[i.Key%numShards].Set(i) +} + +func (sm *shardedMap) Del(key, conflict uint64) (uint64, interface{}) { + return sm.shards[key%numShards].Del(key, conflict) +} + +func (sm *shardedMap) Update(newItem *Item) (interface{}, bool) { + return sm.shards[newItem.Key%numShards].Update(newItem) +} + +func (sm *shardedMap) Cleanup(policy policy, onEvict itemCallback) { + sm.expiryMap.cleanup(sm, policy, onEvict) +} + +func (sm *shardedMap) Clear(onEvict itemCallback) { + for i := uint64(0); i < numShards; i++ { + sm.shards[i].Clear(onEvict) + } +} + +type lockedMap struct { + sync.RWMutex + data map[uint64]storeItem + em *expirationMap +} + +func newLockedMap(em *expirationMap) *lockedMap { + return &lockedMap{ + data: make(map[uint64]storeItem), + em: em, + } +} + +func (m *lockedMap) get(key, conflict uint64) (interface{}, bool) { + m.RLock() + item, ok := m.data[key] + m.RUnlock() + if !ok { + return nil, false + } + if conflict != 0 && (conflict != item.conflict) { + return nil, false + } + + // Handle expired items. + if !item.expiration.IsZero() && time.Now().After(item.expiration) { + return nil, false + } + return item.value, true +} + +func (m *lockedMap) Expiration(key uint64) time.Time { + m.RLock() + defer m.RUnlock() + return m.data[key].expiration +} + +func (m *lockedMap) Set(i *Item) { + if i == nil { + // If the item is nil make this Set a no-op. + return + } + + m.Lock() + defer m.Unlock() + item, ok := m.data[i.Key] + + if ok { + // The item existed already. We need to check the conflict key and reject the + // update if they do not match. Only after that the expiration map is updated. + if i.Conflict != 0 && (i.Conflict != item.conflict) { + return + } + m.em.update(i.Key, i.Conflict, item.expiration, i.Expiration) + } else { + // The value is not in the map already. There's no need to return anything. + // Simply add the expiration map. + m.em.add(i.Key, i.Conflict, i.Expiration) + } + + m.data[i.Key] = storeItem{ + key: i.Key, + conflict: i.Conflict, + value: i.Value, + expiration: i.Expiration, + } +} + +func (m *lockedMap) Del(key, conflict uint64) (uint64, interface{}) { + m.Lock() + item, ok := m.data[key] + if !ok { + m.Unlock() + return 0, nil + } + if conflict != 0 && (conflict != item.conflict) { + m.Unlock() + return 0, nil + } + + if !item.expiration.IsZero() { + m.em.del(key, item.expiration) + } + + delete(m.data, key) + m.Unlock() + return item.conflict, item.value +} + +func (m *lockedMap) Update(newItem *Item) (interface{}, bool) { + m.Lock() + item, ok := m.data[newItem.Key] + if !ok { + m.Unlock() + return nil, false + } + if newItem.Conflict != 0 && (newItem.Conflict != item.conflict) { + m.Unlock() + return nil, false + } + + m.em.update(newItem.Key, newItem.Conflict, item.expiration, newItem.Expiration) + m.data[newItem.Key] = storeItem{ + key: newItem.Key, + conflict: newItem.Conflict, + value: newItem.Value, + expiration: newItem.Expiration, + } + + m.Unlock() + return item.value, true +} + +func (m *lockedMap) Clear(onEvict itemCallback) { + m.Lock() + i := &Item{} + if onEvict != nil { + for _, si := range m.data { + i.Key = si.key + i.Conflict = si.conflict + i.Value = si.value + onEvict(i) + } + } + m.data = make(map[uint64]storeItem) + m.Unlock() +} diff --git a/vendor/github.com/dgraph-io/ristretto/test.sh b/vendor/github.com/dgraph-io/ristretto/test.sh new file mode 100644 index 0000000000..d53b32d42a --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/test.sh @@ -0,0 +1,20 @@ +#! /bin/sh + +starttest() { + set -e + GO111MODULE=on go test -race ./... +} + +if [ -z "${TEAMCITY_VERSION}" ]; then + # running locally, so start test in a container + # TEAMCITY_VERSION=local will avoid recursive calls, when it would be running in container + docker run --rm --name ristretto-test -ti \ + -v `pwd`:/go/src/github.com/dgraph-io/ristretto \ + --workdir /go/src/github.com/dgraph-io/ristretto \ + --env TEAMCITY_VERSION=local \ + golang:1.13 \ + sh test.sh +else + # running in teamcity, since teamcity itself run this in container, let's simply run this + starttest +fi diff --git a/vendor/github.com/dgraph-io/ristretto/ttl.go b/vendor/github.com/dgraph-io/ristretto/ttl.go new file mode 100644 index 0000000000..337976ad43 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/ttl.go @@ -0,0 +1,147 @@ +/* + * Copyright 2020 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package ristretto + +import ( + "sync" + "time" +) + +var ( + // TODO: find the optimal value or make it configurable. + bucketDurationSecs = int64(5) +) + +func storageBucket(t time.Time) int64 { + return (t.Unix() / bucketDurationSecs) + 1 +} + +func cleanupBucket(t time.Time) int64 { + // The bucket to cleanup is always behind the storage bucket by one so that + // no elements in that bucket (which might not have expired yet) are deleted. + return storageBucket(t) - 1 +} + +// bucket type is a map of key to conflict. +type bucket map[uint64]uint64 + +// expirationMap is a map of bucket number to the corresponding bucket. +type expirationMap struct { + sync.RWMutex + buckets map[int64]bucket +} + +func newExpirationMap() *expirationMap { + return &expirationMap{ + buckets: make(map[int64]bucket), + } +} + +func (m *expirationMap) add(key, conflict uint64, expiration time.Time) { + if m == nil { + return + } + + // Items that don't expire don't need to be in the expiration map. + if expiration.IsZero() { + return + } + + bucketNum := storageBucket(expiration) + m.Lock() + defer m.Unlock() + + b, ok := m.buckets[bucketNum] + if !ok { + b = make(bucket) + m.buckets[bucketNum] = b + } + b[key] = conflict +} + +func (m *expirationMap) update(key, conflict uint64, oldExpTime, newExpTime time.Time) { + if m == nil { + return + } + + m.Lock() + defer m.Unlock() + + oldBucketNum := storageBucket(oldExpTime) + oldBucket, ok := m.buckets[oldBucketNum] + if ok { + delete(oldBucket, key) + } + + newBucketNum := storageBucket(newExpTime) + newBucket, ok := m.buckets[newBucketNum] + if !ok { + newBucket = make(bucket) + m.buckets[newBucketNum] = newBucket + } + newBucket[key] = conflict +} + +func (m *expirationMap) del(key uint64, expiration time.Time) { + if m == nil { + return + } + + bucketNum := storageBucket(expiration) + m.Lock() + defer m.Unlock() + _, ok := m.buckets[bucketNum] + if !ok { + return + } + delete(m.buckets[bucketNum], key) +} + +// cleanup removes all the items in the bucket that was just completed. It deletes +// those items from the store, and calls the onEvict function on those items. +// This function is meant to be called periodically. +func (m *expirationMap) cleanup(store store, policy policy, onEvict itemCallback) { + if m == nil { + return + } + + m.Lock() + now := time.Now() + bucketNum := cleanupBucket(now) + keys := m.buckets[bucketNum] + delete(m.buckets, bucketNum) + m.Unlock() + + for key, conflict := range keys { + // Sanity check. Verify that the store agrees that this key is expired. + if store.Expiration(key).After(now) { + continue + } + + cost := policy.Cost(key) + policy.Del(key) + _, value := store.Del(key, conflict) + + if onEvict != nil { + onEvict(&Item{Key: key, + Conflict: conflict, + Value: value, + Cost: cost, + }) + } + } +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/LICENSE b/vendor/github.com/dgraph-io/ristretto/z/LICENSE new file mode 100644 index 0000000000..0860cbfe85 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/LICENSE @@ -0,0 +1,64 @@ +bbloom.go + +// The MIT License (MIT) +// Copyright (c) 2014 Andreas Briese, eduToolbox@Bri-C GmbH, Sarstedt + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +rtutil.go + +// MIT License + +// Copyright (c) 2019 Ewan Chou + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +Modifications: + +/* + * Copyright 2019 Dgraph Labs, Inc. and Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + diff --git a/vendor/github.com/dgraph-io/ristretto/z/README.md b/vendor/github.com/dgraph-io/ristretto/z/README.md new file mode 100644 index 0000000000..6d77e146eb --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/README.md @@ -0,0 +1,129 @@ +## bbloom: a bitset Bloom filter for go/golang +=== + +package implements a fast bloom filter with real 'bitset' and JSONMarshal/JSONUnmarshal to store/reload the Bloom filter. + +NOTE: the package uses unsafe.Pointer to set and read the bits from the bitset. If you're uncomfortable with using the unsafe package, please consider using my bloom filter package at github.com/AndreasBriese/bloom + +=== + +changelog 11/2015: new thread safe methods AddTS(), HasTS(), AddIfNotHasTS() following a suggestion from Srdjan Marinovic (github @a-little-srdjan), who used this to code a bloomfilter cache. + +This bloom filter was developed to strengthen a website-log database and was tested and optimized for this log-entry mask: "2014/%02i/%02i %02i:%02i:%02i /info.html". +Nonetheless bbloom should work with any other form of entries. + +~~Hash function is a modified Berkeley DB sdbm hash (to optimize for smaller strings). sdbm http://www.cse.yorku.ca/~oz/hash.html~~ + +Found sipHash (SipHash-2-4, a fast short-input PRF created by Jean-Philippe Aumasson and Daniel J. Bernstein.) to be about as fast. sipHash had been ported by Dimtry Chestnyk to Go (github.com/dchest/siphash ) + +Minimum hashset size is: 512 ([4]uint64; will be set automatically). + +###install + +```sh +go get github.com/AndreasBriese/bbloom +``` + +###test ++ change to folder ../bbloom ++ create wordlist in file "words.txt" (you might use `python permut.py`) ++ run 'go test -bench=.' within the folder + +```go +go test -bench=. +``` + +~~If you've installed the GOCONVEY TDD-framework http://goconvey.co/ you can run the tests automatically.~~ + +using go's testing framework now (have in mind that the op timing is related to 65536 operations of Add, Has, AddIfNotHas respectively) + +### usage + +after installation add + +```go +import ( + ... + "github.com/AndreasBriese/bbloom" + ... + ) +``` + +at your header. In the program use + +```go +// create a bloom filter for 65536 items and 1 % wrong-positive ratio +bf := bbloom.New(float64(1<<16), float64(0.01)) + +// or +// create a bloom filter with 650000 for 65536 items and 7 locs per hash explicitly +// bf = bbloom.New(float64(650000), float64(7)) +// or +bf = bbloom.New(650000.0, 7.0) + +// add one item +bf.Add([]byte("butter")) + +// Number of elements added is exposed now +// Note: ElemNum will not be included in JSON export (for compatability to older version) +nOfElementsInFilter := bf.ElemNum + +// check if item is in the filter +isIn := bf.Has([]byte("butter")) // should be true +isNotIn := bf.Has([]byte("Butter")) // should be false + +// 'add only if item is new' to the bloomfilter +added := bf.AddIfNotHas([]byte("butter")) // should be false because 'butter' is already in the set +added = bf.AddIfNotHas([]byte("buTTer")) // should be true because 'buTTer' is new + +// thread safe versions for concurrent use: AddTS, HasTS, AddIfNotHasTS +// add one item +bf.AddTS([]byte("peanutbutter")) +// check if item is in the filter +isIn = bf.HasTS([]byte("peanutbutter")) // should be true +isNotIn = bf.HasTS([]byte("peanutButter")) // should be false +// 'add only if item is new' to the bloomfilter +added = bf.AddIfNotHasTS([]byte("butter")) // should be false because 'peanutbutter' is already in the set +added = bf.AddIfNotHasTS([]byte("peanutbuTTer")) // should be true because 'penutbuTTer' is new + +// convert to JSON ([]byte) +Json := bf.JSONMarshal() + +// bloomfilters Mutex is exposed for external un-/locking +// i.e. mutex lock while doing JSON conversion +bf.Mtx.Lock() +Json = bf.JSONMarshal() +bf.Mtx.Unlock() + +// restore a bloom filter from storage +bfNew := bbloom.JSONUnmarshal(Json) + +isInNew := bfNew.Has([]byte("butter")) // should be true +isNotInNew := bfNew.Has([]byte("Butter")) // should be false + +``` + +to work with the bloom filter. + +### why 'fast'? + +It's about 3 times faster than William Fitzgeralds bitset bloom filter https://github.com/willf/bloom . And it is about so fast as my []bool set variant for Boom filters (see https://github.com/AndreasBriese/bloom ) but having a 8times smaller memory footprint: + + + Bloom filter (filter size 524288, 7 hashlocs) + github.com/AndreasBriese/bbloom 'Add' 65536 items (10 repetitions): 6595800 ns (100 ns/op) + github.com/AndreasBriese/bbloom 'Has' 65536 items (10 repetitions): 5986600 ns (91 ns/op) + github.com/AndreasBriese/bloom 'Add' 65536 items (10 repetitions): 6304684 ns (96 ns/op) + github.com/AndreasBriese/bloom 'Has' 65536 items (10 repetitions): 6568663 ns (100 ns/op) + + github.com/willf/bloom 'Add' 65536 items (10 repetitions): 24367224 ns (371 ns/op) + github.com/willf/bloom 'Test' 65536 items (10 repetitions): 21881142 ns (333 ns/op) + github.com/dataence/bloom/standard 'Add' 65536 items (10 repetitions): 23041644 ns (351 ns/op) + github.com/dataence/bloom/standard 'Check' 65536 items (10 repetitions): 19153133 ns (292 ns/op) + github.com/cabello/bloom 'Add' 65536 items (10 repetitions): 131921507 ns (2012 ns/op) + github.com/cabello/bloom 'Contains' 65536 items (10 repetitions): 131108962 ns (2000 ns/op) + +(on MBPro15 OSX10.8.5 i7 4Core 2.4Ghz) + + +With 32bit bloom filters (bloom32) using modified sdbm, bloom32 does hashing with only 2 bit shifts, one xor and one substraction per byte. smdb is about as fast as fnv64a but gives less collisions with the dataset (see mask above). bloom.New(float64(10 * 1<<16),float64(7)) populated with 1<<16 random items from the dataset (see above) and tested against the rest results in less than 0.05% collisions. diff --git a/vendor/github.com/dgraph-io/ristretto/z/allocator.go b/vendor/github.com/dgraph-io/ristretto/z/allocator.go new file mode 100644 index 0000000000..db00ff5eca --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/allocator.go @@ -0,0 +1,403 @@ +/* + * Copyright 2020 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package z + +import ( + "bytes" + "fmt" + "math" + "math/bits" + "math/rand" + "strings" + "sync" + "sync/atomic" + "time" + "unsafe" + + "github.com/dustin/go-humanize" +) + +// Allocator amortizes the cost of small allocations by allocating memory in +// bigger chunks. Internally it uses z.Calloc to allocate memory. Once +// allocated, the memory is not moved, so it is safe to use the allocated bytes +// to unsafe cast them to Go struct pointers. Maintaining a freelist is slow. +// Instead, Allocator only allocates memory, with the idea that finally we +// would just release the entire Allocator. +type Allocator struct { + sync.Mutex + compIdx uint64 // Stores bufIdx in 32 MSBs and posIdx in 32 LSBs. + buffers [][]byte + Ref uint64 + Tag string +} + +// allocs keeps references to all Allocators, so we can safely discard them later. +var allocsMu *sync.Mutex +var allocRef uint64 +var allocs map[uint64]*Allocator +var calculatedLog2 []int + +func init() { + allocsMu = new(sync.Mutex) + allocs = make(map[uint64]*Allocator) + + // Set up a unique Ref per process. + rand.Seed(time.Now().UnixNano()) + allocRef = uint64(rand.Int63n(1<<16)) << 48 + + calculatedLog2 = make([]int, 1025) + for i := 1; i <= 1024; i++ { + calculatedLog2[i] = int(math.Log2(float64(i))) + } +} + +// NewAllocator creates an allocator starting with the given size. +func NewAllocator(sz int, tag string) *Allocator { + ref := atomic.AddUint64(&allocRef, 1) + // We should not allow a zero sized page because addBufferWithMinSize + // will run into an infinite loop trying to double the pagesize. + if sz < 512 { + sz = 512 + } + a := &Allocator{ + Ref: ref, + buffers: make([][]byte, 64), + Tag: tag, + } + l2 := uint64(log2(sz)) + if bits.OnesCount64(uint64(sz)) > 1 { + l2 += 1 + } + a.buffers[0] = Calloc(1<> 32), int(pos & 0xFFFFFFFF) +} + +// Size returns the size of the allocations so far. +func (a *Allocator) Size() int { + pos := atomic.LoadUint64(&a.compIdx) + bi, pi := parse(pos) + var sz int + for i, b := range a.buffers { + if i < bi { + sz += len(b) + continue + } + sz += pi + return sz + } + panic("Size should not reach here") +} + +func log2(sz int) int { + if sz < len(calculatedLog2) { + return calculatedLog2[sz] + } + pow := 10 + sz >>= 10 + for sz > 1 { + sz >>= 1 + pow++ + } + return pow +} + +func (a *Allocator) Allocated() uint64 { + var alloc int + for _, b := range a.buffers { + alloc += cap(b) + } + return uint64(alloc) +} + +func (a *Allocator) TrimTo(max int) { + var alloc int + for i, b := range a.buffers { + if len(b) == 0 { + break + } + alloc += len(b) + if alloc < max { + continue + } + Free(b) + a.buffers[i] = nil + } +} + +// Release would release the memory back. Remember to make this call to avoid memory leaks. +func (a *Allocator) Release() { + if a == nil { + return + } + + var alloc int + for _, b := range a.buffers { + if len(b) == 0 { + break + } + alloc += len(b) + Free(b) + } + + allocsMu.Lock() + delete(allocs, a.Ref) + allocsMu.Unlock() +} + +const maxAlloc = 1 << 30 + +func (a *Allocator) MaxAlloc() int { + return maxAlloc +} + +const nodeAlign = unsafe.Sizeof(uint64(0)) - 1 + +func (a *Allocator) AllocateAligned(sz int) []byte { + tsz := sz + int(nodeAlign) + out := a.Allocate(tsz) + // We are reusing allocators. In that case, it's important to zero out the memory allocated + // here. We don't always zero it out (in Allocate), because other functions would be immediately + // overwriting the allocated slices anyway (see Copy). + ZeroOut(out, 0, len(out)) + + addr := uintptr(unsafe.Pointer(&out[0])) + aligned := (addr + nodeAlign) & ^nodeAlign + start := int(aligned - addr) + + return out[start : start+sz] +} + +func (a *Allocator) Copy(buf []byte) []byte { + if a == nil { + return append([]byte{}, buf...) + } + out := a.Allocate(len(buf)) + copy(out, buf) + return out +} + +func (a *Allocator) addBufferAt(bufIdx, minSz int) { + for { + if bufIdx >= len(a.buffers) { + panic(fmt.Sprintf("Allocator can not allocate more than %d buffers", len(a.buffers))) + } + if len(a.buffers[bufIdx]) == 0 { + break + } + if minSz <= len(a.buffers[bufIdx]) { + // No need to do anything. We already have a buffer which can satisfy minSz. + return + } + bufIdx++ + } + assert(bufIdx > 0) + // We need to allocate a new buffer. + // Make pageSize double of the last allocation. + pageSize := 2 * len(a.buffers[bufIdx-1]) + // Ensure pageSize is bigger than sz. + for pageSize < minSz { + pageSize *= 2 + } + // If bigger than maxAlloc, trim to maxAlloc. + if pageSize > maxAlloc { + pageSize = maxAlloc + } + + buf := Calloc(pageSize, a.Tag) + assert(len(a.buffers[bufIdx]) == 0) + a.buffers[bufIdx] = buf +} + +func (a *Allocator) Allocate(sz int) []byte { + if a == nil { + return make([]byte, sz) + } + if sz > maxAlloc { + panic(fmt.Sprintf("Unable to allocate more than %d\n", maxAlloc)) + } + if sz == 0 { + return nil + } + for { + pos := atomic.AddUint64(&a.compIdx, uint64(sz)) + bufIdx, posIdx := parse(pos) + buf := a.buffers[bufIdx] + if posIdx > len(buf) { + a.Lock() + newPos := atomic.LoadUint64(&a.compIdx) + newBufIdx, _ := parse(newPos) + if newBufIdx != bufIdx { + a.Unlock() + continue + } + a.addBufferAt(bufIdx+1, sz) + atomic.StoreUint64(&a.compIdx, uint64((bufIdx+1)<<32)) + a.Unlock() + // We added a new buffer. Let's acquire slice the right way by going back to the top. + continue + } + data := buf[posIdx-sz : posIdx] + return data + } +} + +type AllocatorPool struct { + numGets int64 + allocCh chan *Allocator + closer *Closer +} + +func NewAllocatorPool(sz int) *AllocatorPool { + a := &AllocatorPool{ + allocCh: make(chan *Allocator, sz), + closer: NewCloser(1), + } + go a.freeupAllocators() + return a +} + +func (p *AllocatorPool) Get(sz int, tag string) *Allocator { + if p == nil { + return NewAllocator(sz, tag) + } + atomic.AddInt64(&p.numGets, 1) + select { + case alloc := <-p.allocCh: + alloc.Reset() + alloc.Tag = tag + return alloc + default: + return NewAllocator(sz, tag) + } +} +func (p *AllocatorPool) Return(a *Allocator) { + if a == nil { + return + } + if p == nil { + a.Release() + return + } + a.TrimTo(400 << 20) + + select { + case p.allocCh <- a: + return + default: + a.Release() + } +} + +func (p *AllocatorPool) Release() { + if p == nil { + return + } + p.closer.SignalAndWait() +} + +func (p *AllocatorPool) freeupAllocators() { + defer p.closer.Done() + + ticker := time.NewTicker(2 * time.Second) + defer ticker.Stop() + + releaseOne := func() bool { + select { + case alloc := <-p.allocCh: + alloc.Release() + return true + default: + return false + } + } + + var last int64 + for { + select { + case <-p.closer.HasBeenClosed(): + close(p.allocCh) + for alloc := range p.allocCh { + alloc.Release() + } + return + + case <-ticker.C: + gets := atomic.LoadInt64(&p.numGets) + if gets != last { + // Some retrievals were made since the last time. So, let's avoid doing a release. + last = gets + continue + } + releaseOne() + } + } +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/bbloom.go b/vendor/github.com/dgraph-io/ristretto/z/bbloom.go new file mode 100644 index 0000000000..37135b012f --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/bbloom.go @@ -0,0 +1,211 @@ +// The MIT License (MIT) +// Copyright (c) 2014 Andreas Briese, eduToolbox@Bri-C GmbH, Sarstedt + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package z + +import ( + "bytes" + "encoding/json" + "math" + "unsafe" + + "github.com/golang/glog" +) + +// helper +var mask = []uint8{1, 2, 4, 8, 16, 32, 64, 128} + +func getSize(ui64 uint64) (size uint64, exponent uint64) { + if ui64 < uint64(512) { + ui64 = uint64(512) + } + size = uint64(1) + for size < ui64 { + size <<= 1 + exponent++ + } + return size, exponent +} + +func calcSizeByWrongPositives(numEntries, wrongs float64) (uint64, uint64) { + size := -1 * numEntries * math.Log(wrongs) / math.Pow(float64(0.69314718056), 2) + locs := math.Ceil(float64(0.69314718056) * size / numEntries) + return uint64(size), uint64(locs) +} + +// NewBloomFilter returns a new bloomfilter. +func NewBloomFilter(params ...float64) (bloomfilter *Bloom) { + var entries, locs uint64 + if len(params) == 2 { + if params[1] < 1 { + entries, locs = calcSizeByWrongPositives(params[0], params[1]) + } else { + entries, locs = uint64(params[0]), uint64(params[1]) + } + } else { + glog.Fatal("usage: New(float64(number_of_entries), float64(number_of_hashlocations))" + + " i.e. New(float64(1000), float64(3)) or New(float64(number_of_entries)," + + " float64(number_of_hashlocations)) i.e. New(float64(1000), float64(0.03))") + } + size, exponent := getSize(entries) + bloomfilter = &Bloom{ + sizeExp: exponent, + size: size - 1, + setLocs: locs, + shift: 64 - exponent, + } + bloomfilter.Size(size) + return bloomfilter +} + +// Bloom filter +type Bloom struct { + bitset []uint64 + ElemNum uint64 + sizeExp uint64 + size uint64 + setLocs uint64 + shift uint64 +} + +// <--- http://www.cse.yorku.ca/~oz/hash.html +// modified Berkeley DB Hash (32bit) +// hash is casted to l, h = 16bit fragments +// func (bl Bloom) absdbm(b *[]byte) (l, h uint64) { +// hash := uint64(len(*b)) +// for _, c := range *b { +// hash = uint64(c) + (hash << 6) + (hash << bl.sizeExp) - hash +// } +// h = hash >> bl.shift +// l = hash << bl.shift >> bl.shift +// return l, h +// } + +// Add adds hash of a key to the bloomfilter. +func (bl *Bloom) Add(hash uint64) { + h := hash >> bl.shift + l := hash << bl.shift >> bl.shift + for i := uint64(0); i < bl.setLocs; i++ { + bl.Set((h + i*l) & bl.size) + bl.ElemNum++ + } +} + +// Has checks if bit(s) for entry hash is/are set, +// returns true if the hash was added to the Bloom Filter. +func (bl Bloom) Has(hash uint64) bool { + h := hash >> bl.shift + l := hash << bl.shift >> bl.shift + for i := uint64(0); i < bl.setLocs; i++ { + if !bl.IsSet((h + i*l) & bl.size) { + return false + } + } + return true +} + +// AddIfNotHas only Adds hash, if it's not present in the bloomfilter. +// Returns true if hash was added. +// Returns false if hash was already registered in the bloomfilter. +func (bl *Bloom) AddIfNotHas(hash uint64) bool { + if bl.Has(hash) { + return false + } + bl.Add(hash) + return true +} + +// TotalSize returns the total size of the bloom filter. +func (bl *Bloom) TotalSize() int { + // The bl struct has 5 members and each one is 8 byte. The bitset is a + // uint64 byte slice. + return len(bl.bitset)*8 + 5*8 +} + +// Size makes Bloom filter with as bitset of size sz. +func (bl *Bloom) Size(sz uint64) { + bl.bitset = make([]uint64, sz>>6) +} + +// Clear resets the Bloom filter. +func (bl *Bloom) Clear() { + for i := range bl.bitset { + bl.bitset[i] = 0 + } +} + +// Set sets the bit[idx] of bitset. +func (bl *Bloom) Set(idx uint64) { + ptr := unsafe.Pointer(uintptr(unsafe.Pointer(&bl.bitset[idx>>6])) + uintptr((idx%64)>>3)) + *(*uint8)(ptr) |= mask[idx%8] +} + +// IsSet checks if bit[idx] of bitset is set, returns true/false. +func (bl *Bloom) IsSet(idx uint64) bool { + ptr := unsafe.Pointer(uintptr(unsafe.Pointer(&bl.bitset[idx>>6])) + uintptr((idx%64)>>3)) + r := ((*(*uint8)(ptr)) >> (idx % 8)) & 1 + return r == 1 +} + +// bloomJSONImExport +// Im/Export structure used by JSONMarshal / JSONUnmarshal +type bloomJSONImExport struct { + FilterSet []byte + SetLocs uint64 +} + +// NewWithBoolset takes a []byte slice and number of locs per entry, +// returns the bloomfilter with a bitset populated according to the input []byte. +func newWithBoolset(bs *[]byte, locs uint64) *Bloom { + bloomfilter := NewBloomFilter(float64(len(*bs)<<3), float64(locs)) + for i, b := range *bs { + *(*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(&bloomfilter.bitset[0])) + uintptr(i))) = b + } + return bloomfilter +} + +// JSONUnmarshal takes JSON-Object (type bloomJSONImExport) as []bytes +// returns bloom32 / bloom64 object. +func JSONUnmarshal(dbData []byte) (*Bloom, error) { + bloomImEx := bloomJSONImExport{} + if err := json.Unmarshal(dbData, &bloomImEx); err != nil { + return nil, err + } + buf := bytes.NewBuffer(bloomImEx.FilterSet) + bs := buf.Bytes() + bf := newWithBoolset(&bs, bloomImEx.SetLocs) + return bf, nil +} + +// JSONMarshal returns JSON-object (type bloomJSONImExport) as []byte. +func (bl Bloom) JSONMarshal() []byte { + bloomImEx := bloomJSONImExport{} + bloomImEx.SetLocs = bl.setLocs + bloomImEx.FilterSet = make([]byte, len(bl.bitset)<<3) + for i := range bloomImEx.FilterSet { + bloomImEx.FilterSet[i] = *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&bl.bitset[0])) + + uintptr(i))) + } + data, err := json.Marshal(bloomImEx) + if err != nil { + glog.Fatal("json.Marshal failed: ", err) + } + return data +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/btree.go b/vendor/github.com/dgraph-io/ristretto/z/btree.go new file mode 100644 index 0000000000..12b735bb03 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/btree.go @@ -0,0 +1,710 @@ +/* + * Copyright 2020 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package z + +import ( + "fmt" + "math" + "os" + "reflect" + "strings" + "unsafe" + + "github.com/dgraph-io/ristretto/z/simd" +) + +var ( + pageSize = os.Getpagesize() + maxKeys = (pageSize / 16) - 1 + oneThird = int(float64(maxKeys) / 3) +) + +const ( + absoluteMax = uint64(math.MaxUint64 - 1) + minSize = 1 << 20 +) + +// Tree represents the structure for custom mmaped B+ tree. +// It supports keys in range [1, math.MaxUint64-1] and values [1, math.Uint64]. +type Tree struct { + buffer *Buffer + data []byte + nextPage uint64 + freePage uint64 + stats TreeStats +} + +func (t *Tree) initRootNode() { + // This is the root node. + t.newNode(0) + // This acts as the rightmost pointer (all the keys are <= this key). + t.Set(absoluteMax, 0) +} + +// NewTree returns an in-memory B+ tree. +func NewTree(tag string) *Tree { + const defaultTag = "tree" + if tag == "" { + tag = defaultTag + } + t := &Tree{buffer: NewBuffer(minSize, tag)} + t.Reset() + return t +} + +// NewTree returns a persistent on-disk B+ tree. +func NewTreePersistent(path string) (*Tree, error) { + t := &Tree{} + var err error + + // Open the buffer from disk and set it to the maximum allocated size. + t.buffer, err = NewBufferPersistent(path, minSize) + if err != nil { + return nil, err + } + t.buffer.offset = uint64(len(t.buffer.buf)) + t.data = t.buffer.Bytes() + + // pageID can never be 0 if the tree has been initialized. + root := t.node(1) + isInitialized := root.pageID() != 0 + + if !isInitialized { + t.nextPage = 1 + t.freePage = 0 + t.initRootNode() + } else { + t.reinit() + } + + return t, nil +} + +// reinit sets the internal variables of a Tree, which are normally stored +// in-memory, but are lost when loading from disk. +func (t *Tree) reinit() { + // Calculate t.nextPage by finding the first node whose pageID is not set. + t.nextPage = 1 + for int(t.nextPage)*pageSize < len(t.data) { + n := t.node(t.nextPage) + if n.pageID() == 0 { + break + } + t.nextPage++ + } + maxPageId := t.nextPage - 1 + + // Calculate t.freePage by finding the page to which no other page points. + // This would be the head of the page linked list. + // tailPages[i] is true if pageId i+1 is not the head of the list. + tailPages := make([]bool, maxPageId) + // Mark all pages containing nodes as tail pages. + t.Iterate(func(n node) { + i := n.pageID() - 1 + tailPages[i] = true + // If this is a leaf node, increment the stats. + if n.isLeaf() { + t.stats.NumLeafKeys += n.numKeys() + } + }) + // pointedPages is a list of page IDs that the tail pages point to. + pointedPages := make([]uint64, 0) + for i, isTail := range tailPages { + if !isTail { + pageId := uint64(i) + 1 + // Skip if nextPageId = 0, as that is equivalent to null page. + if nextPageId := t.node(pageId).uint64(0); nextPageId != 0 { + pointedPages = append(pointedPages, nextPageId) + } + t.stats.NumPagesFree++ + } + } + + // Mark all pages being pointed to as tail pages. + for _, pageId := range pointedPages { + i := pageId - 1 + tailPages[i] = true + } + // There should only be one head page left. + for i, isTail := range tailPages { + if !isTail { + pageId := uint64(i) + 1 + t.freePage = pageId + break + } + } +} + +// Reset resets the tree and truncates it to maxSz. +func (t *Tree) Reset() { + // Tree relies on uninitialized data being zeroed out, so we need to Memclr + // the data before using it again. + Memclr(t.buffer.buf) + t.buffer.Reset() + t.buffer.AllocateOffset(minSize) + t.data = t.buffer.Bytes() + t.stats = TreeStats{} + t.nextPage = 1 + t.freePage = 0 + t.initRootNode() +} + +// Close releases the memory used by the tree. +func (t *Tree) Close() error { + if t == nil { + return nil + } + return t.buffer.Release() +} + +type TreeStats struct { + Allocated int // Derived. + Bytes int // Derived. + NumLeafKeys int // Calculated. + NumPages int // Derived. + NumPagesFree int // Calculated. + Occupancy float64 // Derived. + PageSize int // Derived. +} + +// Stats returns stats about the tree. +func (t *Tree) Stats() TreeStats { + numPages := int(t.nextPage - 1) + out := TreeStats{ + Bytes: numPages * pageSize, + Allocated: len(t.data), + NumLeafKeys: t.stats.NumLeafKeys, + NumPages: numPages, + NumPagesFree: t.stats.NumPagesFree, + PageSize: pageSize, + } + out.Occupancy = 100.0 * float64(out.NumLeafKeys) / float64(maxKeys*numPages) + return out +} + +// BytesToUint64Slice converts a byte slice to a uint64 slice. +func BytesToUint64Slice(b []byte) []uint64 { + if len(b) == 0 { + return nil + } + var u64s []uint64 + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&u64s)) + hdr.Len = len(b) / 8 + hdr.Cap = hdr.Len + hdr.Data = uintptr(unsafe.Pointer(&b[0])) + return u64s +} + +func (t *Tree) newNode(bit uint64) node { + var pageId uint64 + if t.freePage > 0 { + pageId = t.freePage + t.stats.NumPagesFree-- + } else { + pageId = t.nextPage + t.nextPage++ + offset := int(pageId) * pageSize + reqSize := offset + pageSize + if reqSize > len(t.data) { + t.buffer.AllocateOffset(reqSize - len(t.data)) + t.data = t.buffer.Bytes() + } + } + n := t.node(pageId) + if t.freePage > 0 { + t.freePage = n.uint64(0) + } + zeroOut(n) + n.setBit(bit) + n.setAt(keyOffset(maxKeys), pageId) + return n +} + +func getNode(data []byte) node { + return node(BytesToUint64Slice(data)) +} + +func zeroOut(data []uint64) { + for i := 0; i < len(data); i++ { + data[i] = 0 + } +} + +func (t *Tree) node(pid uint64) node { + // page does not exist + if pid == 0 { + return nil + } + start := pageSize * int(pid) + return getNode(t.data[start : start+pageSize]) +} + +// Set sets the key-value pair in the tree. +func (t *Tree) Set(k, v uint64) { + if k == math.MaxUint64 || k == 0 { + panic("Error setting zero or MaxUint64") + } + root := t.set(1, k, v) + if root.isFull() { + right := t.split(1) + left := t.newNode(root.bits()) + // Re-read the root as the underlying buffer for tree might have changed during split. + root = t.node(1) + copy(left[:keyOffset(maxKeys)], root) + left.setNumKeys(root.numKeys()) + + // reset the root node. + zeroOut(root[:keyOffset(maxKeys)]) + root.setNumKeys(0) + + // set the pointers for left and right child in the root node. + root.set(left.maxKey(), left.pageID()) + root.set(right.maxKey(), right.pageID()) + } +} + +// For internal nodes, they contain . +// where all entries <= key are stored in the corresponding ptr. +func (t *Tree) set(pid, k, v uint64) node { + n := t.node(pid) + if n.isLeaf() { + t.stats.NumLeafKeys += n.set(k, v) + return n + } + + // This is an internal node. + idx := n.search(k) + if idx >= maxKeys { + panic("search returned index >= maxKeys") + } + // If no key at idx. + if n.key(idx) == 0 { + n.setAt(keyOffset(idx), k) + n.setNumKeys(n.numKeys() + 1) + } + child := t.node(n.val(idx)) + if child == nil { + child = t.newNode(bitLeaf) + n = t.node(pid) + n.setAt(valOffset(idx), child.pageID()) + } + child = t.set(child.pageID(), k, v) + // Re-read n as the underlying buffer for tree might have changed during set. + n = t.node(pid) + if child.isFull() { + // Just consider the left sibling for simplicity. + // if t.shareWithSibling(n, idx) { + // return n + // } + + nn := t.split(child.pageID()) + // Re-read n and child as the underlying buffer for tree might have changed during split. + n = t.node(pid) + child = t.node(n.uint64(valOffset(idx))) + // Set child pointers in the node n. + // Note that key for right node (nn) already exist in node n, but the + // pointer is updated. + n.set(child.maxKey(), child.pageID()) + n.set(nn.maxKey(), nn.pageID()) + } + return n +} + +// Get looks for key and returns the corresponding value. +// If key is not found, 0 is returned. +func (t *Tree) Get(k uint64) uint64 { + if k == math.MaxUint64 || k == 0 { + panic("Does not support getting MaxUint64/Zero") + } + root := t.node(1) + return t.get(root, k) +} + +func (t *Tree) get(n node, k uint64) uint64 { + if n.isLeaf() { + return n.get(k) + } + // This is internal node + idx := n.search(k) + if idx == n.numKeys() || n.key(idx) == 0 { + return 0 + } + child := t.node(n.uint64(valOffset(idx))) + assert(child != nil) + return t.get(child, k) +} + +// DeleteBelow deletes all keys with value under ts. +func (t *Tree) DeleteBelow(ts uint64) { + root := t.node(1) + t.stats.NumLeafKeys = 0 + t.compact(root, ts) + assert(root.numKeys() >= 1) +} + +func (t *Tree) compact(n node, ts uint64) int { + if n.isLeaf() { + numKeys := n.compact(ts) + t.stats.NumLeafKeys += n.numKeys() + return numKeys + } + // Not leaf. + N := n.numKeys() + for i := 0; i < N; i++ { + assert(n.key(i) > 0) + childID := n.uint64(valOffset(i)) + child := t.node(childID) + if rem := t.compact(child, ts); rem == 0 && i < N-1 { + // If no valid key is remaining we can drop this child. However, don't do that if this + // is the max key. + t.stats.NumLeafKeys -= child.numKeys() + child.setAt(0, t.freePage) + t.freePage = childID + n.setAt(valOffset(i), 0) + t.stats.NumPagesFree++ + } + } + // We use ts=1 here because we want to delete all the keys whose value is 0, which means they no + // longer have a valid page for that key. + return n.compact(1) +} + +func (t *Tree) iterate(n node, fn func(node)) { + fn(n) + if n.isLeaf() { + return + } + // Explore children. + for i := 0; i < maxKeys; i++ { + if n.key(i) == 0 { + return + } + childID := n.uint64(valOffset(i)) + assert(childID > 0) + + child := t.node(childID) + t.iterate(child, fn) + } +} + +// Iterate iterates over the tree and executes the fn on each node. +func (t *Tree) Iterate(fn func(node)) { + root := t.node(1) + t.iterate(root, fn) +} + +// IterateKV iterates through all keys and values in the tree. +// If newVal is non-zero, it will be set in the tree. +func (t *Tree) IterateKV(f func(key, val uint64) (newVal uint64)) { + t.Iterate(func(n node) { + // Only leaf nodes contain keys. + if !n.isLeaf() { + return + } + + for i := 0; i < n.numKeys(); i++ { + key := n.key(i) + val := n.val(i) + + // A zero value here means that this is a bogus entry. + if val == 0 { + continue + } + + newVal := f(key, val) + if newVal != 0 { + n.setAt(valOffset(i), newVal) + } + } + }) +} + +func (t *Tree) print(n node, parentID uint64) { + n.print(parentID) + if n.isLeaf() { + return + } + pid := n.pageID() + for i := 0; i < maxKeys; i++ { + if n.key(i) == 0 { + return + } + childID := n.uint64(valOffset(i)) + child := t.node(childID) + t.print(child, pid) + } +} + +// Print iterates over the tree and prints all valid KVs. +func (t *Tree) Print() { + root := t.node(1) + t.print(root, 0) +} + +// Splits the node into two. It moves right half of the keys from the original node to a newly +// created right node. It returns the right node. +func (t *Tree) split(pid uint64) node { + n := t.node(pid) + if !n.isFull() { + panic("This should be called only when n is full") + } + + // Create a new node nn, copy over half the keys from n, and set the parent to n's parent. + nn := t.newNode(n.bits()) + // Re-read n as the underlying buffer for tree might have changed during newNode. + n = t.node(pid) + rightHalf := n[keyOffset(maxKeys/2):keyOffset(maxKeys)] + copy(nn, rightHalf) + nn.setNumKeys(maxKeys - maxKeys/2) + + // Remove entries from node n. + zeroOut(rightHalf) + n.setNumKeys(maxKeys / 2) + return nn +} + +// shareWithSiblingXXX is unused for now. The idea is to move some keys to +// sibling when a node is full. But, I don't see any special benefits in our +// access pattern. It doesn't result in better occupancy ratios. +func (t *Tree) shareWithSiblingXXX(n node, idx int) bool { + if idx == 0 { + return false + } + left := t.node(n.val(idx - 1)) + ns := left.numKeys() + if ns >= maxKeys/2 { + // Sibling is already getting full. + return false + } + + right := t.node(n.val(idx)) + // Copy over keys from right child to left child. + copied := copy(left[keyOffset(ns):], right[:keyOffset(oneThird)]) + copied /= 2 // Considering that key-val constitute one key. + left.setNumKeys(ns + copied) + + // Update the max key in parent node n for the left sibling. + n.setAt(keyOffset(idx-1), left.maxKey()) + + // Now move keys to left for the right sibling. + until := copy(right, right[keyOffset(oneThird):keyOffset(maxKeys)]) + right.setNumKeys(until / 2) + zeroOut(right[until:keyOffset(maxKeys)]) + return true +} + +// Each node in the node is of size pageSize. Two kinds of nodes. Leaf nodes and internal nodes. +// Leaf nodes only contain the data. Internal nodes would contain the key and the offset to the +// child node. +// Internal node would have first entry as +// <0 offset to child>, <1000 offset>, <5000 offset>, and so on... +// Leaf nodes would just have: , , and so on... +// Last 16 bytes of the node are off limits. +// | pageID (8 bytes) | metaBits (1 byte) | 3 free bytes | numKeys (4 bytes) | +type node []uint64 + +func (n node) uint64(start int) uint64 { return n[start] } + +// func (n node) uint32(start int) uint32 { return *(*uint32)(unsafe.Pointer(&n[start])) } + +func keyOffset(i int) int { return 2 * i } +func valOffset(i int) int { return 2*i + 1 } +func (n node) numKeys() int { return int(n.uint64(valOffset(maxKeys)) & 0xFFFFFFFF) } +func (n node) pageID() uint64 { return n.uint64(keyOffset(maxKeys)) } +func (n node) key(i int) uint64 { return n.uint64(keyOffset(i)) } +func (n node) val(i int) uint64 { return n.uint64(valOffset(i)) } +func (n node) data(i int) []uint64 { return n[keyOffset(i):keyOffset(i+1)] } + +func (n node) setAt(start int, k uint64) { + n[start] = k +} + +func (n node) setNumKeys(num int) { + idx := valOffset(maxKeys) + val := n[idx] + val &= 0xFFFFFFFF00000000 + val |= uint64(num) + n[idx] = val +} + +func (n node) moveRight(lo int) { + hi := n.numKeys() + assert(hi != maxKeys) + // copy works despite of overlap in src and dst. + // See https://golang.org/pkg/builtin/#copy + copy(n[keyOffset(lo+1):keyOffset(hi+1)], n[keyOffset(lo):keyOffset(hi)]) +} + +const ( + bitLeaf = uint64(1 << 63) +) + +func (n node) setBit(b uint64) { + vo := valOffset(maxKeys) + val := n[vo] + val &= 0xFFFFFFFF + val |= b + n[vo] = val +} +func (n node) bits() uint64 { + return n.val(maxKeys) & 0xFF00000000000000 +} +func (n node) isLeaf() bool { + return n.bits()&bitLeaf > 0 +} + +// isFull checks that the node is already full. +func (n node) isFull() bool { + return n.numKeys() == maxKeys +} + +// Search returns the index of a smallest key >= k in a node. +func (n node) search(k uint64) int { + N := n.numKeys() + if N < 4 { + for i := 0; i < N; i++ { + if ki := n.key(i); ki >= k { + return i + } + } + return N + } + return int(simd.Search(n[:2*N], k)) + // lo, hi := 0, N + // // Reduce the search space using binary seach and then do linear search. + // for hi-lo > 32 { + // mid := (hi + lo) / 2 + // km := n.key(mid) + // if k == km { + // return mid + // } + // if k > km { + // // key is greater than the key at mid, so move right. + // lo = mid + 1 + // } else { + // // else move left. + // hi = mid + // } + // } + // for i := lo; i <= hi; i++ { + // if ki := n.key(i); ki >= k { + // return i + // } + // } + // return N +} +func (n node) maxKey() uint64 { + idx := n.numKeys() + // idx points to the first key which is zero. + if idx > 0 { + idx-- + } + return n.key(idx) +} + +// compacts the node i.e., remove all the kvs with value < lo. It returns the remaining number of +// keys. +func (n node) compact(lo uint64) int { + N := n.numKeys() + mk := n.maxKey() + var left, right int + for right = 0; right < N; right++ { + if n.val(right) < lo && n.key(right) < mk { + // Skip over this key. Don't copy it. + continue + } + // Valid data. Copy it from right to left. Advance left. + if left != right { + copy(n.data(left), n.data(right)) + } + left++ + } + // zero out rest of the kv pairs. + zeroOut(n[keyOffset(left):keyOffset(right)]) + n.setNumKeys(left) + + // If the only key we have is the max key, and its value is less than lo, then we can indicate + // to the caller by returning a zero that it's OK to drop the node. + if left == 1 && n.key(0) == mk && n.val(0) < lo { + return 0 + } + return left +} + +func (n node) get(k uint64) uint64 { + idx := n.search(k) + // key is not found + if idx == n.numKeys() { + return 0 + } + if ki := n.key(idx); ki == k { + return n.val(idx) + } + return 0 +} + +// set returns true if it added a new key. +func (n node) set(k, v uint64) (numAdded int) { + idx := n.search(k) + ki := n.key(idx) + if n.numKeys() == maxKeys { + // This happens during split of non-root node, when we are updating the child pointer of + // right node. Hence, the key should already exist. + assert(ki == k) + } + if ki > k { + // Found the first entry which is greater than k. So, we need to fit k + // just before it. For that, we should move the rest of the data in the + // node to the right to make space for k. + n.moveRight(idx) + } + // If the k does not exist already, increment the number of keys. + if ki != k { + n.setNumKeys(n.numKeys() + 1) + numAdded = 1 + } + if ki == 0 || ki >= k { + n.setAt(keyOffset(idx), k) + n.setAt(valOffset(idx), v) + return + } + panic("shouldn't reach here") +} + +func (n node) iterate(fn func(node, int)) { + for i := 0; i < maxKeys; i++ { + if k := n.key(i); k > 0 { + fn(n, i) + } else { + break + } + } +} + +func (n node) print(parentID uint64) { + var keys []string + n.iterate(func(n node, i int) { + keys = append(keys, fmt.Sprintf("%d", n.key(i))) + }) + if len(keys) > 8 { + copy(keys[4:], keys[len(keys)-4:]) + keys[3] = "..." + keys = keys[:8] + } + fmt.Printf("%d Child of: %d num keys: %d keys: %s\n", + n.pageID(), parentID, n.numKeys(), strings.Join(keys, " ")) +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/buffer.go b/vendor/github.com/dgraph-io/ristretto/z/buffer.go new file mode 100644 index 0000000000..5a22de8c7f --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/buffer.go @@ -0,0 +1,544 @@ +/* + * Copyright 2020 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package z + +import ( + "encoding/binary" + "fmt" + "io/ioutil" + "os" + "sort" + "sync/atomic" + + "github.com/golang/glog" + "github.com/pkg/errors" +) + +const ( + defaultCapacity = 64 + defaultTag = "buffer" +) + +// Buffer is equivalent of bytes.Buffer without the ability to read. It is NOT thread-safe. +// +// In UseCalloc mode, z.Calloc is used to allocate memory, which depending upon how the code is +// compiled could use jemalloc for allocations. +// +// In UseMmap mode, Buffer uses file mmap to allocate memory. This allows us to store big data +// structures without using physical memory. +// +// MaxSize can be set to limit the memory usage. +type Buffer struct { + padding uint64 // number of starting bytes used for padding + offset uint64 // used length of the buffer + buf []byte // backing slice for the buffer + bufType BufferType // type of the underlying buffer + curSz int // capacity of the buffer + maxSz int // causes a panic if the buffer grows beyond this size + mmapFile *MmapFile // optional mmap backing for the buffer + autoMmapAfter int // Calloc falls back to an mmaped tmpfile after crossing this size + autoMmapDir string // directory for autoMmap to create a tempfile in + persistent bool // when enabled, Release will not delete the underlying mmap file + tag string // used for jemalloc stats +} + +func NewBuffer(capacity int, tag string) *Buffer { + if capacity < defaultCapacity { + capacity = defaultCapacity + } + if tag == "" { + tag = defaultTag + } + return &Buffer{ + buf: Calloc(capacity, tag), + bufType: UseCalloc, + curSz: capacity, + offset: 8, + padding: 8, + tag: tag, + } +} + +// It is the caller's responsibility to set offset after this, because Buffer +// doesn't remember what it was. +func NewBufferPersistent(path string, capacity int) (*Buffer, error) { + file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0666) + if err != nil { + return nil, err + } + buffer, err := newBufferFile(file, capacity) + if err != nil { + return nil, err + } + buffer.persistent = true + return buffer, nil +} + +func NewBufferTmp(dir string, capacity int) (*Buffer, error) { + if dir == "" { + dir = tmpDir + } + file, err := ioutil.TempFile(dir, "buffer") + if err != nil { + return nil, err + } + return newBufferFile(file, capacity) +} + +func newBufferFile(file *os.File, capacity int) (*Buffer, error) { + if capacity < defaultCapacity { + capacity = defaultCapacity + } + mmapFile, err := OpenMmapFileUsing(file, capacity, true) + if err != nil && err != NewFile { + return nil, err + } + buf := &Buffer{ + buf: mmapFile.Data, + bufType: UseMmap, + curSz: len(mmapFile.Data), + mmapFile: mmapFile, + offset: 8, + padding: 8, + } + return buf, nil +} + +func NewBufferSlice(slice []byte) *Buffer { + return &Buffer{ + offset: uint64(len(slice)), + buf: slice, + bufType: UseInvalid, + } +} + +func (b *Buffer) WithAutoMmap(threshold int, path string) *Buffer { + if b.bufType != UseCalloc { + panic("can only autoMmap with UseCalloc") + } + b.autoMmapAfter = threshold + if path == "" { + b.autoMmapDir = tmpDir + } else { + b.autoMmapDir = path + } + return b +} + +func (b *Buffer) WithMaxSize(size int) *Buffer { + b.maxSz = size + return b +} + +func (b *Buffer) IsEmpty() bool { + return int(b.offset) == b.StartOffset() +} + +// LenWithPadding would return the number of bytes written to the buffer so far +// plus the padding at the start of the buffer. +func (b *Buffer) LenWithPadding() int { + return int(atomic.LoadUint64(&b.offset)) +} + +// LenNoPadding would return the number of bytes written to the buffer so far +// (without the padding). +func (b *Buffer) LenNoPadding() int { + return int(atomic.LoadUint64(&b.offset) - b.padding) +} + +// Bytes would return all the written bytes as a slice. +func (b *Buffer) Bytes() []byte { + off := atomic.LoadUint64(&b.offset) + return b.buf[b.padding:off] +} + +// Grow would grow the buffer to have at least n more bytes. In case the buffer is at capacity, it +// would reallocate twice the size of current capacity + n, to ensure n bytes can be written to the +// buffer without further allocation. In UseMmap mode, this might result in underlying file +// expansion. +func (b *Buffer) Grow(n int) { + if b.buf == nil { + panic("z.Buffer needs to be initialized before using") + } + if b.maxSz > 0 && int(b.offset)+n > b.maxSz { + err := fmt.Errorf( + "z.Buffer max size exceeded: %d offset: %d grow: %d", b.maxSz, b.offset, n) + panic(err) + } + if int(b.offset)+n < b.curSz { + return + } + + // Calculate new capacity. + growBy := b.curSz + n + // Don't allocate more than 1GB at a time. + if growBy > 1<<30 { + growBy = 1 << 30 + } + // Allocate at least n, even if it exceeds the 1GB limit above. + if n > growBy { + growBy = n + } + b.curSz += growBy + + switch b.bufType { + case UseCalloc: + // If autoMmap gets triggered, copy the slice over to an mmaped file. + if b.autoMmapAfter > 0 && b.curSz > b.autoMmapAfter { + b.bufType = UseMmap + file, err := ioutil.TempFile(b.autoMmapDir, "") + if err != nil { + panic(err) + } + mmapFile, err := OpenMmapFileUsing(file, b.curSz, true) + if err != nil && err != NewFile { + panic(err) + } + assert(int(b.offset) == copy(mmapFile.Data, b.buf[:b.offset])) + Free(b.buf) + b.mmapFile = mmapFile + b.buf = mmapFile.Data + break + } + + // Else, reallocate the slice. + newBuf := Calloc(b.curSz, b.tag) + assert(int(b.offset) == copy(newBuf, b.buf[:b.offset])) + Free(b.buf) + b.buf = newBuf + + case UseMmap: + // Truncate and remap the underlying file. + if err := b.mmapFile.Truncate(int64(b.curSz)); err != nil { + err = errors.Wrapf(err, + "while trying to truncate file: %s to size: %d", b.mmapFile.Fd.Name(), b.curSz) + panic(err) + } + b.buf = b.mmapFile.Data + + default: + panic("can only use Grow on UseCalloc and UseMmap buffers") + } +} + +// Allocate is a way to get a slice of size n back from the buffer. This slice can be directly +// written to. Warning: Allocate is not thread-safe. The byte slice returned MUST be used before +// further calls to Buffer. +func (b *Buffer) Allocate(n int) []byte { + b.Grow(n) + off := b.offset + b.offset += uint64(n) + return b.buf[off:int(b.offset)] +} + +// AllocateOffset works the same way as allocate, but instead of returning a byte slice, it returns +// the offset of the allocation. +func (b *Buffer) AllocateOffset(n int) int { + b.Grow(n) + b.offset += uint64(n) + return int(b.offset) - n +} + +func (b *Buffer) writeLen(sz int) { + buf := b.Allocate(4) + binary.BigEndian.PutUint32(buf, uint32(sz)) +} + +// SliceAllocate would encode the size provided into the buffer, followed by a call to Allocate, +// hence returning the slice of size sz. This can be used to allocate a lot of small buffers into +// this big buffer. +// Note that SliceAllocate should NOT be mixed with normal calls to Write. +func (b *Buffer) SliceAllocate(sz int) []byte { + b.Grow(4 + sz) + b.writeLen(sz) + return b.Allocate(sz) +} + +func (b *Buffer) StartOffset() int { + return int(b.padding) +} + +func (b *Buffer) WriteSlice(slice []byte) { + dst := b.SliceAllocate(len(slice)) + assert(len(slice) == copy(dst, slice)) +} + +func (b *Buffer) SliceIterate(f func(slice []byte) error) error { + if b.IsEmpty() { + return nil + } + slice, next := []byte{}, b.StartOffset() + for next >= 0 { + slice, next = b.Slice(next) + if len(slice) == 0 { + continue + } + if err := f(slice); err != nil { + return err + } + } + return nil +} + +const ( + UseCalloc BufferType = iota + UseMmap + UseInvalid +) + +type BufferType int + +func (t BufferType) String() string { + switch t { + case UseCalloc: + return "UseCalloc" + case UseMmap: + return "UseMmap" + default: + return "UseInvalid" + } +} + +type LessFunc func(a, b []byte) bool +type sortHelper struct { + offsets []int + b *Buffer + tmp *Buffer + less LessFunc + small []int +} + +func (s *sortHelper) sortSmall(start, end int) { + s.tmp.Reset() + s.small = s.small[:0] + next := start + for next >= 0 && next < end { + s.small = append(s.small, next) + _, next = s.b.Slice(next) + } + + // We are sorting the slices pointed to by s.small offsets, but only moving the offsets around. + sort.Slice(s.small, func(i, j int) bool { + left, _ := s.b.Slice(s.small[i]) + right, _ := s.b.Slice(s.small[j]) + return s.less(left, right) + }) + // Now we iterate over the s.small offsets and copy over the slices. The result is now in order. + for _, off := range s.small { + s.tmp.Write(rawSlice(s.b.buf[off:])) + } + assert(end-start == copy(s.b.buf[start:end], s.tmp.Bytes())) +} + +func assert(b bool) { + if !b { + glog.Fatalf("%+v", errors.Errorf("Assertion failure")) + } +} +func check(err error) { + if err != nil { + glog.Fatalf("%+v", err) + } +} +func check2(_ interface{}, err error) { + check(err) +} + +func (s *sortHelper) merge(left, right []byte, start, end int) { + if len(left) == 0 || len(right) == 0 { + return + } + s.tmp.Reset() + check2(s.tmp.Write(left)) + left = s.tmp.Bytes() + + var ls, rs []byte + + copyLeft := func() { + assert(len(ls) == copy(s.b.buf[start:], ls)) + left = left[len(ls):] + start += len(ls) + } + copyRight := func() { + assert(len(rs) == copy(s.b.buf[start:], rs)) + right = right[len(rs):] + start += len(rs) + } + + for start < end { + if len(left) == 0 { + assert(len(right) == copy(s.b.buf[start:end], right)) + return + } + if len(right) == 0 { + assert(len(left) == copy(s.b.buf[start:end], left)) + return + } + ls = rawSlice(left) + rs = rawSlice(right) + + // We skip the first 4 bytes in the rawSlice, because that stores the length. + if s.less(ls[4:], rs[4:]) { + copyLeft() + } else { + copyRight() + } + } +} + +func (s *sortHelper) sort(lo, hi int) []byte { + assert(lo <= hi) + + mid := lo + (hi-lo)/2 + loff, hoff := s.offsets[lo], s.offsets[hi] + if lo == mid { + // No need to sort, just return the buffer. + return s.b.buf[loff:hoff] + } + + // lo, mid would sort from [offset[lo], offset[mid]) . + left := s.sort(lo, mid) + // Typically we'd use mid+1, but here mid represents an offset in the buffer. Each offset + // contains a thousand entries. So, if we do mid+1, we'd skip over those entries. + right := s.sort(mid, hi) + + s.merge(left, right, loff, hoff) + return s.b.buf[loff:hoff] +} + +// SortSlice is like SortSliceBetween but sorting over the entire buffer. +func (b *Buffer) SortSlice(less func(left, right []byte) bool) { + b.SortSliceBetween(b.StartOffset(), int(b.offset), less) +} +func (b *Buffer) SortSliceBetween(start, end int, less LessFunc) { + if start >= end { + return + } + if start == 0 { + panic("start can never be zero") + } + + var offsets []int + next, count := start, 0 + for next >= 0 && next < end { + if count%1024 == 0 { + offsets = append(offsets, next) + } + _, next = b.Slice(next) + count++ + } + assert(len(offsets) > 0) + if offsets[len(offsets)-1] != end { + offsets = append(offsets, end) + } + + szTmp := int(float64((end-start)/2) * 1.1) + s := &sortHelper{ + offsets: offsets, + b: b, + less: less, + small: make([]int, 0, 1024), + tmp: NewBuffer(szTmp, b.tag), + } + defer s.tmp.Release() + + left := offsets[0] + for _, off := range offsets[1:] { + s.sortSmall(left, off) + left = off + } + s.sort(0, len(offsets)-1) +} + +func rawSlice(buf []byte) []byte { + sz := binary.BigEndian.Uint32(buf) + return buf[:4+int(sz)] +} + +// Slice would return the slice written at offset. +func (b *Buffer) Slice(offset int) ([]byte, int) { + if offset >= int(b.offset) { + return nil, -1 + } + + sz := binary.BigEndian.Uint32(b.buf[offset:]) + start := offset + 4 + next := start + int(sz) + res := b.buf[start:next] + if next >= int(b.offset) { + next = -1 + } + return res, next +} + +// SliceOffsets is an expensive function. Use sparingly. +func (b *Buffer) SliceOffsets() []int { + next := b.StartOffset() + var offsets []int + for next >= 0 { + offsets = append(offsets, next) + _, next = b.Slice(next) + } + return offsets +} + +func (b *Buffer) Data(offset int) []byte { + if offset > b.curSz { + panic("offset beyond current size") + } + return b.buf[offset:b.curSz] +} + +// Write would write p bytes to the buffer. +func (b *Buffer) Write(p []byte) (n int, err error) { + n = len(p) + b.Grow(n) + assert(n == copy(b.buf[b.offset:], p)) + b.offset += uint64(n) + return n, nil +} + +// Reset would reset the buffer to be reused. +func (b *Buffer) Reset() { + b.offset = uint64(b.StartOffset()) +} + +// Release would free up the memory allocated by the buffer. Once the usage of buffer is done, it is +// important to call Release, otherwise a memory leak can happen. +func (b *Buffer) Release() error { + if b == nil { + return nil + } + switch b.bufType { + case UseCalloc: + Free(b.buf) + case UseMmap: + if b.mmapFile == nil { + return nil + } + path := b.mmapFile.Fd.Name() + if err := b.mmapFile.Close(-1); err != nil { + return errors.Wrapf(err, "while closing file: %s", path) + } + if !b.persistent { + if err := os.Remove(path); err != nil { + return errors.Wrapf(err, "while deleting file %s", path) + } + } + } + return nil +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/calloc.go b/vendor/github.com/dgraph-io/ristretto/z/calloc.go new file mode 100644 index 0000000000..2e5d613813 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/calloc.go @@ -0,0 +1,42 @@ +package z + +import "sync/atomic" + +var numBytes int64 + +// NumAllocBytes returns the number of bytes allocated using calls to z.Calloc. The allocations +// could be happening via either Go or jemalloc, depending upon the build flags. +func NumAllocBytes() int64 { + return atomic.LoadInt64(&numBytes) +} + +// MemStats is used to fetch JE Malloc Stats. The stats are fetched from +// the mallctl namespace http://jemalloc.net/jemalloc.3.html#mallctl_namespace. +type MemStats struct { + // Total number of bytes allocated by the application. + // http://jemalloc.net/jemalloc.3.html#stats.allocated + Allocated uint64 + // Total number of bytes in active pages allocated by the application. This + // is a multiple of the page size, and greater than or equal to + // Allocated. + // http://jemalloc.net/jemalloc.3.html#stats.active + Active uint64 + // Maximum number of bytes in physically resident data pages mapped by the + // allocator, comprising all pages dedicated to allocator metadata, pages + // backing active allocations, and unused dirty pages. This is a maximum + // rather than precise because pages may not actually be physically + // resident if they correspond to demand-zeroed virtual memory that has not + // yet been touched. This is a multiple of the page size, and is larger + // than stats.active. + // http://jemalloc.net/jemalloc.3.html#stats.resident + Resident uint64 + // Total number of bytes in virtual memory mappings that were retained + // rather than being returned to the operating system via e.g. munmap(2) or + // similar. Retained virtual memory is typically untouched, decommitted, or + // purged, so it has no strongly associated physical memory (see extent + // hooks http://jemalloc.net/jemalloc.3.html#arena.i.extent_hooks for + // details). Retained memory is excluded from mapped memory statistics, + // e.g. stats.mapped (http://jemalloc.net/jemalloc.3.html#stats.mapped). + // http://jemalloc.net/jemalloc.3.html#stats.retained + Retained uint64 +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/calloc_32bit.go b/vendor/github.com/dgraph-io/ristretto/z/calloc_32bit.go new file mode 100644 index 0000000000..3a0442614f --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/calloc_32bit.go @@ -0,0 +1,14 @@ +// Copyright 2020 The LevelDB-Go and Pebble Authors. All rights reserved. Use +// of this source code is governed by a BSD-style license that can be found in +// the LICENSE file. + +// +build 386 amd64p32 arm armbe mips mipsle mips64p32 mips64p32le ppc sparc + +package z + +const ( + // MaxArrayLen is a safe maximum length for slices on this architecture. + MaxArrayLen = 1<<31 - 1 + // MaxBufferSize is the size of virtually unlimited buffer on this architecture. + MaxBufferSize = 1 << 30 +) diff --git a/vendor/github.com/dgraph-io/ristretto/z/calloc_64bit.go b/vendor/github.com/dgraph-io/ristretto/z/calloc_64bit.go new file mode 100644 index 0000000000..b898248bba --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/calloc_64bit.go @@ -0,0 +1,14 @@ +// Copyright 2020 The LevelDB-Go and Pebble Authors. All rights reserved. Use +// of this source code is governed by a BSD-style license that can be found in +// the LICENSE file. + +// +build amd64 arm64 arm64be ppc64 ppc64le mips64 mips64le riscv64 s390x sparc64 + +package z + +const ( + // MaxArrayLen is a safe maximum length for slices on this architecture. + MaxArrayLen = 1<<50 - 1 + // MaxBufferSize is the size of virtually unlimited buffer on this architecture. + MaxBufferSize = 256 << 30 +) diff --git a/vendor/github.com/dgraph-io/ristretto/z/calloc_jemalloc.go b/vendor/github.com/dgraph-io/ristretto/z/calloc_jemalloc.go new file mode 100644 index 0000000000..904d73ac57 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/calloc_jemalloc.go @@ -0,0 +1,172 @@ +// Copyright 2020 The LevelDB-Go and Pebble Authors. All rights reserved. Use +// of this source code is governed by a BSD-style license that can be found in +// the LICENSE file. + +// +build jemalloc + +package z + +/* +#cgo LDFLAGS: /usr/local/lib/libjemalloc.a -L/usr/local/lib -Wl,-rpath,/usr/local/lib -ljemalloc -lm -lstdc++ -pthread -ldl +#include +#include +*/ +import "C" +import ( + "bytes" + "fmt" + "sync" + "sync/atomic" + "unsafe" + + "github.com/dustin/go-humanize" +) + +// The go:linkname directives provides backdoor access to private functions in +// the runtime. Below we're accessing the throw function. + +//go:linkname throw runtime.throw +func throw(s string) + +// New allocates a slice of size n. The returned slice is from manually managed +// memory and MUST be released by calling Free. Failure to do so will result in +// a memory leak. +// +// Compile jemalloc with ./configure --with-jemalloc-prefix="je_" +// https://android.googlesource.com/platform/external/jemalloc_new/+/6840b22e8e11cb68b493297a5cd757d6eaa0b406/TUNING.md +// These two config options seems useful for frequent allocations and deallocations in +// multi-threaded programs (like we have). +// JE_MALLOC_CONF="background_thread:true,metadata_thp:auto" +// +// Compile Go program with `go build -tags=jemalloc` to enable this. + +type dalloc struct { + t string + sz int +} + +var dallocsMu sync.Mutex +var dallocs map[unsafe.Pointer]*dalloc + +func init() { + // By initializing dallocs, we can start tracking allocations and deallocations via z.Calloc. + dallocs = make(map[unsafe.Pointer]*dalloc) +} + +func Calloc(n int, tag string) []byte { + if n == 0 { + return make([]byte, 0) + } + // We need to be conscious of the Cgo pointer passing rules: + // + // https://golang.org/cmd/cgo/#hdr-Passing_pointers + // + // ... + // Note: the current implementation has a bug. While Go code is permitted + // to write nil or a C pointer (but not a Go pointer) to C memory, the + // current implementation may sometimes cause a runtime error if the + // contents of the C memory appear to be a Go pointer. Therefore, avoid + // passing uninitialized C memory to Go code if the Go code is going to + // store pointer values in it. Zero out the memory in C before passing it + // to Go. + + ptr := C.je_calloc(C.size_t(n), 1) + if ptr == nil { + // NB: throw is like panic, except it guarantees the process will be + // terminated. The call below is exactly what the Go runtime invokes when + // it cannot allocate memory. + throw("out of memory") + } + + uptr := unsafe.Pointer(ptr) + dallocsMu.Lock() + dallocs[uptr] = &dalloc{ + t: tag, + sz: n, + } + dallocsMu.Unlock() + atomic.AddInt64(&numBytes, int64(n)) + // Interpret the C pointer as a pointer to a Go array, then slice. + return (*[MaxArrayLen]byte)(uptr)[:n:n] +} + +// CallocNoRef does the exact same thing as Calloc with jemalloc enabled. +func CallocNoRef(n int, tag string) []byte { + return Calloc(n, tag) +} + +// Free frees the specified slice. +func Free(b []byte) { + if sz := cap(b); sz != 0 { + b = b[:cap(b)] + ptr := unsafe.Pointer(&b[0]) + C.je_free(ptr) + atomic.AddInt64(&numBytes, -int64(sz)) + dallocsMu.Lock() + delete(dallocs, ptr) + dallocsMu.Unlock() + } +} + +func Leaks() string { + if dallocs == nil { + return "Leak detection disabled. Enable with 'leak' build flag." + } + dallocsMu.Lock() + defer dallocsMu.Unlock() + if len(dallocs) == 0 { + return "NO leaks found." + } + m := make(map[string]int) + for _, da := range dallocs { + m[da.t] += da.sz + } + var buf bytes.Buffer + fmt.Fprintf(&buf, "Allocations:\n") + for f, sz := range m { + fmt.Fprintf(&buf, "%s at file: %s\n", humanize.IBytes(uint64(sz)), f) + } + return buf.String() +} + +// ReadMemStats populates stats with JE Malloc statistics. +func ReadMemStats(stats *MemStats) { + if stats == nil { + return + } + // Call an epoch mallclt to refresh the stats data as mentioned in the docs. + // http://jemalloc.net/jemalloc.3.html#epoch + // Note: This epoch mallctl is as expensive as a malloc call. It takes up the + // malloc_mutex_lock. + epoch := 1 + sz := unsafe.Sizeof(&epoch) + C.je_mallctl( + (C.CString)("epoch"), + unsafe.Pointer(&epoch), + (*C.size_t)(unsafe.Pointer(&sz)), + unsafe.Pointer(&epoch), + (C.size_t)(unsafe.Sizeof(epoch))) + stats.Allocated = fetchStat("stats.allocated") + stats.Active = fetchStat("stats.active") + stats.Resident = fetchStat("stats.resident") + stats.Retained = fetchStat("stats.retained") +} + +// fetchStat is used to read a specific attribute from je malloc stats using mallctl. +func fetchStat(s string) uint64 { + var out uint64 + sz := unsafe.Sizeof(&out) + C.je_mallctl( + (C.CString)(s), // Query: eg: stats.allocated, stats.resident, etc. + unsafe.Pointer(&out), // Variable to store the output. + (*C.size_t)(unsafe.Pointer(&sz)), // Size of the output variable. + nil, // Input variable used to set a value. + 0) // Size of the input variable. + return out +} + +func StatsPrint() { + opts := C.CString("mdablxe") + C.je_malloc_stats_print(nil, nil, opts) + C.free(unsafe.Pointer(opts)) +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/calloc_nojemalloc.go b/vendor/github.com/dgraph-io/ristretto/z/calloc_nojemalloc.go new file mode 100644 index 0000000000..93ceedf906 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/calloc_nojemalloc.go @@ -0,0 +1,37 @@ +// Copyright 2020 The LevelDB-Go and Pebble Authors. All rights reserved. Use +// of this source code is governed by a BSD-style license that can be found in +// the LICENSE file. + +// +build !jemalloc !cgo + +package z + +import ( + "fmt" +) + +// Provides versions of Calloc, CallocNoRef, etc when jemalloc is not available +// (eg: build without jemalloc tag). + +// Calloc allocates a slice of size n. +func Calloc(n int, tag string) []byte { + return make([]byte, n) +} + +// CallocNoRef will not give you memory back without jemalloc. +func CallocNoRef(n int, tag string) []byte { + // We do the add here just to stay compatible with a corresponding Free call. + return nil +} + +// Free does not do anything in this mode. +func Free(b []byte) {} + +func Leaks() string { return "Leaks: Using Go memory" } +func StatsPrint() { + fmt.Println("Using Go memory") +} + +// ReadMemStats doesn't do anything since all the memory is being managed +// by the Go runtime. +func ReadMemStats(_ *MemStats) { return } diff --git a/vendor/github.com/dgraph-io/ristretto/z/file.go b/vendor/github.com/dgraph-io/ristretto/z/file.go new file mode 100644 index 0000000000..880caf0ad9 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/file.go @@ -0,0 +1,217 @@ +/* + * Copyright 2020 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package z + +import ( + "encoding/binary" + "fmt" + "io" + "os" + "path/filepath" + + "github.com/pkg/errors" +) + +// MmapFile represents an mmapd file and includes both the buffer to the data +// and the file descriptor. +type MmapFile struct { + Data []byte + Fd *os.File +} + +var NewFile = errors.New("Create a new file") + +func OpenMmapFileUsing(fd *os.File, sz int, writable bool) (*MmapFile, error) { + filename := fd.Name() + fi, err := fd.Stat() + if err != nil { + return nil, errors.Wrapf(err, "cannot stat file: %s", filename) + } + + var rerr error + fileSize := fi.Size() + if sz > 0 && fileSize == 0 { + // If file is empty, truncate it to sz. + if err := fd.Truncate(int64(sz)); err != nil { + return nil, errors.Wrapf(err, "error while truncation") + } + fileSize = int64(sz) + rerr = NewFile + } + + // fmt.Printf("Mmaping file: %s with writable: %v filesize: %d\n", fd.Name(), writable, fileSize) + buf, err := Mmap(fd, writable, fileSize) // Mmap up to file size. + if err != nil { + return nil, errors.Wrapf(err, "while mmapping %s with size: %d", fd.Name(), fileSize) + } + + if fileSize == 0 { + dir, _ := filepath.Split(filename) + go SyncDir(dir) + } + return &MmapFile{ + Data: buf, + Fd: fd, + }, rerr +} + +// OpenMmapFile opens an existing file or creates a new file. If the file is +// created, it would truncate the file to maxSz. In both cases, it would mmap +// the file to maxSz and returned it. In case the file is created, z.NewFile is +// returned. +func OpenMmapFile(filename string, flag int, maxSz int) (*MmapFile, error) { + // fmt.Printf("opening file %s with flag: %v\n", filename, flag) + fd, err := os.OpenFile(filename, flag, 0666) + if err != nil { + return nil, errors.Wrapf(err, "unable to open: %s", filename) + } + writable := true + if flag == os.O_RDONLY { + writable = false + } + return OpenMmapFileUsing(fd, maxSz, writable) +} + +type mmapReader struct { + Data []byte + offset int +} + +func (mr *mmapReader) Read(buf []byte) (int, error) { + if mr.offset > len(mr.Data) { + return 0, io.EOF + } + n := copy(buf, mr.Data[mr.offset:]) + mr.offset += n + if n < len(buf) { + return n, io.EOF + } + return n, nil +} + +func (m *MmapFile) NewReader(offset int) io.Reader { + return &mmapReader{ + Data: m.Data, + offset: offset, + } +} + +// Bytes returns data starting from offset off of size sz. If there's not enough data, it would +// return nil slice and io.EOF. +func (m *MmapFile) Bytes(off, sz int) ([]byte, error) { + if len(m.Data[off:]) < sz { + return nil, io.EOF + } + return m.Data[off : off+sz], nil +} + +// Slice returns the slice at the given offset. +func (m *MmapFile) Slice(offset int) []byte { + sz := binary.BigEndian.Uint32(m.Data[offset:]) + start := offset + 4 + next := start + int(sz) + if next > len(m.Data) { + return []byte{} + } + res := m.Data[start:next] + return res +} + +// AllocateSlice allocates a slice of the given size at the given offset. +func (m *MmapFile) AllocateSlice(sz, offset int) ([]byte, int, error) { + start := offset + 4 + + // If the file is too small, double its size or increase it by 1GB, whichever is smaller. + if start+sz > len(m.Data) { + const oneGB = 1 << 30 + growBy := len(m.Data) + if growBy > oneGB { + growBy = oneGB + } + if growBy < sz+4 { + growBy = sz + 4 + } + if err := m.Truncate(int64(len(m.Data) + growBy)); err != nil { + return nil, 0, err + } + } + + binary.BigEndian.PutUint32(m.Data[offset:], uint32(sz)) + return m.Data[start : start+sz], start + sz, nil +} + +func (m *MmapFile) Sync() error { + if m == nil { + return nil + } + return Msync(m.Data) +} + +func (m *MmapFile) Delete() error { + // Badger can set the m.Data directly, without setting any Fd. In that case, this should be a + // NOOP. + if m.Fd == nil { + return nil + } + + if err := Munmap(m.Data); err != nil { + return fmt.Errorf("while munmap file: %s, error: %v\n", m.Fd.Name(), err) + } + m.Data = nil + if err := m.Fd.Truncate(0); err != nil { + return fmt.Errorf("while truncate file: %s, error: %v\n", m.Fd.Name(), err) + } + if err := m.Fd.Close(); err != nil { + return fmt.Errorf("while close file: %s, error: %v\n", m.Fd.Name(), err) + } + return os.Remove(m.Fd.Name()) +} + +// Close would close the file. It would also truncate the file if maxSz >= 0. +func (m *MmapFile) Close(maxSz int64) error { + // Badger can set the m.Data directly, without setting any Fd. In that case, this should be a + // NOOP. + if m.Fd == nil { + return nil + } + if err := m.Sync(); err != nil { + return fmt.Errorf("while sync file: %s, error: %v\n", m.Fd.Name(), err) + } + if err := Munmap(m.Data); err != nil { + return fmt.Errorf("while munmap file: %s, error: %v\n", m.Fd.Name(), err) + } + if maxSz >= 0 { + if err := m.Fd.Truncate(maxSz); err != nil { + return fmt.Errorf("while truncate file: %s, error: %v\n", m.Fd.Name(), err) + } + } + return m.Fd.Close() +} + +func SyncDir(dir string) error { + df, err := os.Open(dir) + if err != nil { + return errors.Wrapf(err, "while opening %s", dir) + } + if err := df.Sync(); err != nil { + return errors.Wrapf(err, "while syncing %s", dir) + } + if err := df.Close(); err != nil { + return errors.Wrapf(err, "while closing %s", dir) + } + return nil +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/file_default.go b/vendor/github.com/dgraph-io/ristretto/z/file_default.go new file mode 100644 index 0000000000..d9c0db43e7 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/file_default.go @@ -0,0 +1,39 @@ +// +build !linux + +/* + * Copyright 2020 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package z + +import "fmt" + +// Truncate would truncate the mmapped file to the given size. On Linux, we truncate +// the underlying file and then call mremap, but on other systems, we unmap first, +// then truncate, then re-map. +func (m *MmapFile) Truncate(maxSz int64) error { + if err := m.Sync(); err != nil { + return fmt.Errorf("while sync file: %s, error: %v\n", m.Fd.Name(), err) + } + if err := Munmap(m.Data); err != nil { + return fmt.Errorf("while munmap file: %s, error: %v\n", m.Fd.Name(), err) + } + if err := m.Fd.Truncate(maxSz); err != nil { + return fmt.Errorf("while truncate file: %s, error: %v\n", m.Fd.Name(), err) + } + var err error + m.Data, err = Mmap(m.Fd, true, maxSz) // Mmap up to max size. + return err +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/file_linux.go b/vendor/github.com/dgraph-io/ristretto/z/file_linux.go new file mode 100644 index 0000000000..7f670bd2cc --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/file_linux.go @@ -0,0 +1,37 @@ +/* + * Copyright 2020 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package z + +import ( + "fmt" +) + +// Truncate would truncate the mmapped file to the given size. On Linux, we truncate +// the underlying file and then call mremap, but on other systems, we unmap first, +// then truncate, then re-map. +func (m *MmapFile) Truncate(maxSz int64) error { + if err := m.Sync(); err != nil { + return fmt.Errorf("while sync file: %s, error: %v\n", m.Fd.Name(), err) + } + if err := m.Fd.Truncate(maxSz); err != nil { + return fmt.Errorf("while truncate file: %s, error: %v\n", m.Fd.Name(), err) + } + + var err error + m.Data, err = mremap(m.Data, int(maxSz)) // Mmap up to max size. + return err +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/flags.go b/vendor/github.com/dgraph-io/ristretto/z/flags.go new file mode 100644 index 0000000000..a55c474ab2 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/flags.go @@ -0,0 +1,311 @@ +package z + +import ( + "fmt" + "os" + "os/user" + "path/filepath" + "sort" + "strconv" + "strings" + "time" + + "github.com/golang/glog" + "github.com/pkg/errors" +) + +// SuperFlagHelp makes it really easy to generate command line `--help` output for a SuperFlag. For +// example: +// +// const flagDefaults = `enabled=true; path=some/path;` +// +// var help string = z.NewSuperFlagHelp(flagDefaults). +// Flag("enabled", "Turns on ."). +// Flag("path", "The path to ."). +// Flag("another", "Not present in defaults, but still included."). +// String() +// +// The `help` string would then contain: +// +// enabled=true; Turns on . +// path=some/path; The path to . +// another=; Not present in defaults, but still included. +// +// All flags are sorted alphabetically for consistent `--help` output. Flags with default values are +// placed at the top, and everything else goes under. +type SuperFlagHelp struct { + head string + defaults *SuperFlag + flags map[string]string +} + +func NewSuperFlagHelp(defaults string) *SuperFlagHelp { + return &SuperFlagHelp{ + defaults: NewSuperFlag(defaults), + flags: make(map[string]string, 0), + } +} + +func (h *SuperFlagHelp) Head(head string) *SuperFlagHelp { + h.head = head + return h +} + +func (h *SuperFlagHelp) Flag(name, description string) *SuperFlagHelp { + h.flags[name] = description + return h +} + +func (h *SuperFlagHelp) String() string { + defaultLines := make([]string, 0) + otherLines := make([]string, 0) + for name, help := range h.flags { + val, found := h.defaults.m[name] + line := fmt.Sprintf(" %s=%s; %s\n", name, val, help) + if found { + defaultLines = append(defaultLines, line) + } else { + otherLines = append(otherLines, line) + } + } + sort.Strings(defaultLines) + sort.Strings(otherLines) + dls := strings.Join(defaultLines, "") + ols := strings.Join(otherLines, "") + if len(h.defaults.m) == 0 && len(ols) == 0 { + // remove last newline + dls = dls[:len(dls)-1] + } + // remove last newline + if len(h.defaults.m) == 0 && len(ols) > 1 { + ols = ols[:len(ols)-1] + } + return h.head + "\n" + dls + ols +} + +func parseFlag(flag string) (map[string]string, error) { + kvm := make(map[string]string) + for _, kv := range strings.Split(flag, ";") { + if strings.TrimSpace(kv) == "" { + continue + } + // For a non-empty separator, 0 < len(splits) ≤ 2. + splits := strings.SplitN(kv, "=", 2) + k := strings.TrimSpace(splits[0]) + if len(splits) < 2 { + return nil, fmt.Errorf("superflag: missing value for '%s' in flag: %s", k, flag) + } + k = strings.ToLower(k) + k = strings.ReplaceAll(k, "_", "-") + kvm[k] = strings.TrimSpace(splits[1]) + } + return kvm, nil +} + +type SuperFlag struct { + m map[string]string +} + +func NewSuperFlag(flag string) *SuperFlag { + sf, err := newSuperFlagImpl(flag) + if err != nil { + glog.Fatal(err) + } + return sf +} + +func newSuperFlagImpl(flag string) (*SuperFlag, error) { + m, err := parseFlag(flag) + if err != nil { + return nil, err + } + return &SuperFlag{m}, nil +} + +func (sf *SuperFlag) String() string { + if sf == nil { + return "" + } + kvs := make([]string, 0, len(sf.m)) + for k, v := range sf.m { + kvs = append(kvs, fmt.Sprintf("%s=%s", k, v)) + } + return strings.Join(kvs, "; ") +} + +func (sf *SuperFlag) MergeAndCheckDefault(flag string) *SuperFlag { + sf, err := sf.mergeAndCheckDefaultImpl(flag) + if err != nil { + glog.Fatal(err) + } + return sf +} + +func (sf *SuperFlag) mergeAndCheckDefaultImpl(flag string) (*SuperFlag, error) { + if sf == nil { + m, err := parseFlag(flag) + if err != nil { + return nil, err + } + return &SuperFlag{m}, nil + } + + src, err := parseFlag(flag) + if err != nil { + return nil, err + } + + numKeys := len(sf.m) + for k := range src { + if _, ok := sf.m[k]; ok { + numKeys-- + } + } + if numKeys != 0 { + return nil, fmt.Errorf("superflag: found invalid options in flag: %s.\nvalid options: %v", sf, flag) + } + for k, v := range src { + if _, ok := sf.m[k]; !ok { + sf.m[k] = v + } + } + return sf, nil +} + +func (sf *SuperFlag) Has(opt string) bool { + val := sf.GetString(opt) + return val != "" +} + +func (sf *SuperFlag) GetDuration(opt string) time.Duration { + val := sf.GetString(opt) + if val == "" { + return time.Duration(0) + } + if strings.Contains(val, "d") { + val = strings.Replace(val, "d", "", 1) + days, err := strconv.ParseUint(val, 0, 64) + if err != nil { + return time.Duration(0) + } + return time.Hour * 24 * time.Duration(days) + } + d, err := time.ParseDuration(val) + if err != nil { + return time.Duration(0) + } + return d +} + +func (sf *SuperFlag) GetBool(opt string) bool { + val := sf.GetString(opt) + if val == "" { + return false + } + b, err := strconv.ParseBool(val) + if err != nil { + err = errors.Wrapf(err, + "Unable to parse %s as bool for key: %s. Options: %s\n", + val, opt, sf) + glog.Fatalf("%+v", err) + } + return b +} + +func (sf *SuperFlag) GetFloat64(opt string) float64 { + val := sf.GetString(opt) + if val == "" { + return 0 + } + f, err := strconv.ParseFloat(val, 64) + if err != nil { + err = errors.Wrapf(err, + "Unable to parse %s as float64 for key: %s. Options: %s\n", + val, opt, sf) + glog.Fatalf("%+v", err) + } + return f +} + +func (sf *SuperFlag) GetInt64(opt string) int64 { + val := sf.GetString(opt) + if val == "" { + return 0 + } + i, err := strconv.ParseInt(val, 0, 64) + if err != nil { + err = errors.Wrapf(err, + "Unable to parse %s as int64 for key: %s. Options: %s\n", + val, opt, sf) + glog.Fatalf("%+v", err) + } + return i +} + +func (sf *SuperFlag) GetUint64(opt string) uint64 { + val := sf.GetString(opt) + if val == "" { + return 0 + } + u, err := strconv.ParseUint(val, 0, 64) + if err != nil { + err = errors.Wrapf(err, + "Unable to parse %s as uint64 for key: %s. Options: %s\n", + val, opt, sf) + glog.Fatalf("%+v", err) + } + return u +} + +func (sf *SuperFlag) GetUint32(opt string) uint32 { + val := sf.GetString(opt) + if val == "" { + return 0 + } + u, err := strconv.ParseUint(val, 0, 32) + if err != nil { + err = errors.Wrapf(err, + "Unable to parse %s as uint32 for key: %s. Options: %s\n", + val, opt, sf) + glog.Fatalf("%+v", err) + } + return uint32(u) +} + +func (sf *SuperFlag) GetString(opt string) string { + if sf == nil { + return "" + } + return sf.m[opt] +} + +func (sf *SuperFlag) GetPath(opt string) string { + p := sf.GetString(opt) + path, err := expandPath(p) + if err != nil { + glog.Fatalf("Failed to get path: %+v", err) + } + return path +} + +// expandPath expands the paths containing ~ to /home/user. It also computes the absolute path +// from the relative paths. For example: ~/abc/../cef will be transformed to /home/user/cef. +func expandPath(path string) (string, error) { + if len(path) == 0 { + return "", nil + } + if path[0] == '~' && (len(path) == 1 || os.IsPathSeparator(path[1])) { + usr, err := user.Current() + if err != nil { + return "", errors.Wrap(err, "Failed to get the home directory of the user") + } + path = filepath.Join(usr.HomeDir, path[1:]) + } + + var err error + path, err = filepath.Abs(path) + if err != nil { + return "", errors.Wrap(err, "Failed to generate absolute path") + } + return path, nil +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/histogram.go b/vendor/github.com/dgraph-io/ristretto/z/histogram.go new file mode 100644 index 0000000000..4eb0c4f6c9 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/histogram.go @@ -0,0 +1,205 @@ +/* + * Copyright 2020 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package z + +import ( + "fmt" + "math" + "strings" + + "github.com/dustin/go-humanize" +) + +// Creates bounds for an histogram. The bounds are powers of two of the form +// [2^min_exponent, ..., 2^max_exponent]. +func HistogramBounds(minExponent, maxExponent uint32) []float64 { + var bounds []float64 + for i := minExponent; i <= maxExponent; i++ { + bounds = append(bounds, float64(int(1)< 4) + bounds := make([]float64, num) + bounds[0] = 1 + bounds[1] = 2 + for i := 2; i < num; i++ { + bounds[i] = bounds[i-1] + bounds[i-2] + } + return bounds +} + +// HistogramData stores the information needed to represent the sizes of the keys and values +// as a histogram. +type HistogramData struct { + Bounds []float64 + Count int64 + CountPerBucket []int64 + Min int64 + Max int64 + Sum int64 +} + +// NewHistogramData returns a new instance of HistogramData with properly initialized fields. +func NewHistogramData(bounds []float64) *HistogramData { + return &HistogramData{ + Bounds: bounds, + CountPerBucket: make([]int64, len(bounds)+1), + Max: 0, + Min: math.MaxInt64, + } +} + +func (histogram *HistogramData) Copy() *HistogramData { + if histogram == nil { + return nil + } + return &HistogramData{ + Bounds: append([]float64{}, histogram.Bounds...), + CountPerBucket: append([]int64{}, histogram.CountPerBucket...), + Count: histogram.Count, + Min: histogram.Min, + Max: histogram.Max, + Sum: histogram.Sum, + } +} + +// Update changes the Min and Max fields if value is less than or greater than the current values. +func (histogram *HistogramData) Update(value int64) { + if histogram == nil { + return + } + if value > histogram.Max { + histogram.Max = value + } + if value < histogram.Min { + histogram.Min = value + } + + histogram.Sum += value + histogram.Count++ + + for index := 0; index <= len(histogram.Bounds); index++ { + // Allocate value in the last buckets if we reached the end of the Bounds array. + if index == len(histogram.Bounds) { + histogram.CountPerBucket[index]++ + break + } + + if value < int64(histogram.Bounds[index]) { + histogram.CountPerBucket[index]++ + break + } + } +} + +// Mean returns the mean value for the histogram. +func (histogram *HistogramData) Mean() float64 { + if histogram.Count == 0 { + return 0 + } + return float64(histogram.Sum) / float64(histogram.Count) +} + +// String converts the histogram data into human-readable string. +func (histogram *HistogramData) String() string { + if histogram == nil { + return "" + } + var b strings.Builder + + b.WriteString("\n -- Histogram: \n") + b.WriteString(fmt.Sprintf("Min value: %d \n", histogram.Min)) + b.WriteString(fmt.Sprintf("Max value: %d \n", histogram.Max)) + b.WriteString(fmt.Sprintf("Count: %d \n", histogram.Count)) + b.WriteString(fmt.Sprintf("50p: %.2f \n", histogram.Percentile(0.5))) + b.WriteString(fmt.Sprintf("75p: %.2f \n", histogram.Percentile(0.75))) + b.WriteString(fmt.Sprintf("90p: %.2f \n", histogram.Percentile(0.90))) + + numBounds := len(histogram.Bounds) + var cum float64 + for index, count := range histogram.CountPerBucket { + if count == 0 { + continue + } + + // The last bucket represents the bucket that contains the range from + // the last bound up to infinity so it's processed differently than the + // other buckets. + if index == len(histogram.CountPerBucket)-1 { + lowerBound := uint64(histogram.Bounds[numBounds-1]) + page := float64(count*100) / float64(histogram.Count) + cum += page + b.WriteString(fmt.Sprintf("[%s, %s) %d %.2f%% %.2f%%\n", + humanize.IBytes(lowerBound), "infinity", count, page, cum)) + continue + } + + upperBound := uint64(histogram.Bounds[index]) + lowerBound := uint64(0) + if index > 0 { + lowerBound = uint64(histogram.Bounds[index-1]) + } + + page := float64(count*100) / float64(histogram.Count) + cum += page + b.WriteString(fmt.Sprintf("[%d, %d) %d %.2f%% %.2f%%\n", + lowerBound, upperBound, count, page, cum)) + } + b.WriteString(" --\n") + return b.String() +} + +// Percentile returns the percentile value for the histogram. +// value of p should be between [0.0-1.0] +func (histogram *HistogramData) Percentile(p float64) float64 { + if histogram == nil { + return 0 + } + + if histogram.Count == 0 { + // if no data return the minimum range + return histogram.Bounds[0] + } + pval := int64(float64(histogram.Count) * p) + for i, v := range histogram.CountPerBucket { + pval = pval - v + if pval <= 0 { + if i == len(histogram.Bounds) { + break + } + return histogram.Bounds[i] + } + } + // default return should be the max range + return histogram.Bounds[len(histogram.Bounds)-1] +} + +// Clear reset the histogram. Helpful in situations where we need to reset the metrics +func (histogram *HistogramData) Clear() { + if histogram == nil { + return + } + + histogram.Count = 0 + histogram.CountPerBucket = make([]int64, len(histogram.Bounds)+1) + histogram.Sum = 0 + histogram.Max = 0 + histogram.Min = math.MaxInt64 +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/mmap.go b/vendor/github.com/dgraph-io/ristretto/z/mmap.go new file mode 100644 index 0000000000..9b02510003 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/mmap.go @@ -0,0 +1,44 @@ +/* + * Copyright 2019 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package z + +import ( + "os" +) + +// Mmap uses the mmap system call to memory-map a file. If writable is true, +// memory protection of the pages is set so that they may be written to as well. +func Mmap(fd *os.File, writable bool, size int64) ([]byte, error) { + return mmap(fd, writable, size) +} + +// Munmap unmaps a previously mapped slice. +func Munmap(b []byte) error { + return munmap(b) +} + +// Madvise uses the madvise system call to give advise about the use of memory +// when using a slice that is memory-mapped to a file. Set the readahead flag to +// false if page references are expected in random order. +func Madvise(b []byte, readahead bool) error { + return madvise(b, readahead) +} + +// Msync would call sync on the mmapped data. +func Msync(b []byte) error { + return msync(b) +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/mmap_darwin.go b/vendor/github.com/dgraph-io/ristretto/z/mmap_darwin.go new file mode 100644 index 0000000000..4d6d74f193 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/mmap_darwin.go @@ -0,0 +1,59 @@ +/* + * Copyright 2019 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package z + +import ( + "os" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +// Mmap uses the mmap system call to memory-map a file. If writable is true, +// memory protection of the pages is set so that they may be written to as well. +func mmap(fd *os.File, writable bool, size int64) ([]byte, error) { + mtype := unix.PROT_READ + if writable { + mtype |= unix.PROT_WRITE + } + return unix.Mmap(int(fd.Fd()), 0, int(size), mtype, unix.MAP_SHARED) +} + +// Munmap unmaps a previously mapped slice. +func munmap(b []byte) error { + return unix.Munmap(b) +} + +// This is required because the unix package does not support the madvise system call on OS X. +func madvise(b []byte, readahead bool) error { + advice := unix.MADV_NORMAL + if !readahead { + advice = unix.MADV_RANDOM + } + + _, _, e1 := syscall.Syscall(syscall.SYS_MADVISE, uintptr(unsafe.Pointer(&b[0])), + uintptr(len(b)), uintptr(advice)) + if e1 != 0 { + return e1 + } + return nil +} + +func msync(b []byte) error { + return unix.Msync(b, unix.MS_SYNC) +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/mmap_linux.go b/vendor/github.com/dgraph-io/ristretto/z/mmap_linux.go new file mode 100644 index 0000000000..9cc3497a16 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/mmap_linux.go @@ -0,0 +1,101 @@ +/* + * Copyright 2020 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package z + +import ( + "fmt" + "os" + "reflect" + "unsafe" + + "golang.org/x/sys/unix" +) + +// mmap uses the mmap system call to memory-map a file. If writable is true, +// memory protection of the pages is set so that they may be written to as well. +func mmap(fd *os.File, writable bool, size int64) ([]byte, error) { + mtype := unix.PROT_READ + if writable { + mtype |= unix.PROT_WRITE + } + return unix.Mmap(int(fd.Fd()), 0, int(size), mtype, unix.MAP_SHARED) +} + +// mremap is a Linux-specific system call to remap pages in memory. This can be used in place of munmap + mmap. +func mremap(data []byte, size int) ([]byte, error) { + // taken from + const MREMAP_MAYMOVE = 0x1 + + header := (*reflect.SliceHeader)(unsafe.Pointer(&data)) + mmapAddr, mmapSize, errno := unix.Syscall6( + unix.SYS_MREMAP, + header.Data, + uintptr(header.Len), + uintptr(size), + uintptr(MREMAP_MAYMOVE), + 0, + 0, + ) + if errno != 0 { + return nil, errno + } + if mmapSize != uintptr(size) { + return nil, fmt.Errorf("mremap size mismatch: requested: %d got: %d", size, mmapSize) + } + + header.Data = mmapAddr + header.Cap = size + header.Len = size + return data, nil +} + +// munmap unmaps a previously mapped slice. +// +// unix.Munmap maintains an internal list of mmapped addresses, and only calls munmap +// if the address is present in that list. If we use mremap, this list is not updated. +// To bypass this, we call munmap ourselves. +func munmap(data []byte) error { + if len(data) == 0 || len(data) != cap(data) { + return unix.EINVAL + } + _, _, errno := unix.Syscall( + unix.SYS_MUNMAP, + uintptr(unsafe.Pointer(&data[0])), + uintptr(len(data)), + 0, + ) + if errno != 0 { + return errno + } + return nil +} + +// madvise uses the madvise system call to give advise about the use of memory +// when using a slice that is memory-mapped to a file. Set the readahead flag to +// false if page references are expected in random order. +func madvise(b []byte, readahead bool) error { + flags := unix.MADV_NORMAL + if !readahead { + flags = unix.MADV_RANDOM + } + return unix.Madvise(b, flags) +} + +// msync writes any modified data to persistent storage. +func msync(b []byte) error { + return unix.Msync(b, unix.MS_SYNC) +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/mmap_plan9.go b/vendor/github.com/dgraph-io/ristretto/z/mmap_plan9.go new file mode 100644 index 0000000000..f30729654f --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/mmap_plan9.go @@ -0,0 +1,44 @@ +/* + * Copyright 2020 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package z + +import ( + "os" + "syscall" +) + +// Mmap uses the mmap system call to memory-map a file. If writable is true, +// memory protection of the pages is set so that they may be written to as well. +func mmap(fd *os.File, writable bool, size int64) ([]byte, error) { + return nil, syscall.EPLAN9 +} + +// Munmap unmaps a previously mapped slice. +func munmap(b []byte) error { + return syscall.EPLAN9 +} + +// Madvise uses the madvise system call to give advise about the use of memory +// when using a slice that is memory-mapped to a file. Set the readahead flag to +// false if page references are expected in random order. +func madvise(b []byte, readahead bool) error { + return syscall.EPLAN9 +} + +func msync(b []byte) error { + return syscall.EPLAN9 +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/mmap_unix.go b/vendor/github.com/dgraph-io/ristretto/z/mmap_unix.go new file mode 100644 index 0000000000..e8b2699cf9 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/mmap_unix.go @@ -0,0 +1,55 @@ +// +build !windows,!darwin,!plan9,!linux + +/* + * Copyright 2019 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package z + +import ( + "os" + + "golang.org/x/sys/unix" +) + +// Mmap uses the mmap system call to memory-map a file. If writable is true, +// memory protection of the pages is set so that they may be written to as well. +func mmap(fd *os.File, writable bool, size int64) ([]byte, error) { + mtype := unix.PROT_READ + if writable { + mtype |= unix.PROT_WRITE + } + return unix.Mmap(int(fd.Fd()), 0, int(size), mtype, unix.MAP_SHARED) +} + +// Munmap unmaps a previously mapped slice. +func munmap(b []byte) error { + return unix.Munmap(b) +} + +// Madvise uses the madvise system call to give advise about the use of memory +// when using a slice that is memory-mapped to a file. Set the readahead flag to +// false if page references are expected in random order. +func madvise(b []byte, readahead bool) error { + flags := unix.MADV_NORMAL + if !readahead { + flags = unix.MADV_RANDOM + } + return unix.Madvise(b, flags) +} + +func msync(b []byte) error { + return unix.Msync(b, unix.MS_SYNC) +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/mmap_windows.go b/vendor/github.com/dgraph-io/ristretto/z/mmap_windows.go new file mode 100644 index 0000000000..171176e9fe --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/mmap_windows.go @@ -0,0 +1,96 @@ +// +build windows + +/* + * Copyright 2019 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package z + +import ( + "fmt" + "os" + "syscall" + "unsafe" +) + +func mmap(fd *os.File, write bool, size int64) ([]byte, error) { + protect := syscall.PAGE_READONLY + access := syscall.FILE_MAP_READ + + if write { + protect = syscall.PAGE_READWRITE + access = syscall.FILE_MAP_WRITE + } + fi, err := fd.Stat() + if err != nil { + return nil, err + } + + // In windows, we cannot mmap a file more than it's actual size. + // So truncate the file to the size of the mmap. + if fi.Size() < size { + if err := fd.Truncate(size); err != nil { + return nil, fmt.Errorf("truncate: %s", err) + } + } + + // Open a file mapping handle. + sizelo := uint32(size >> 32) + sizehi := uint32(size) & 0xffffffff + + handler, err := syscall.CreateFileMapping(syscall.Handle(fd.Fd()), nil, + uint32(protect), sizelo, sizehi, nil) + if err != nil { + return nil, os.NewSyscallError("CreateFileMapping", err) + } + + // Create the memory map. + addr, err := syscall.MapViewOfFile(handler, uint32(access), 0, 0, uintptr(size)) + if addr == 0 { + return nil, os.NewSyscallError("MapViewOfFile", err) + } + + // Close mapping handle. + if err := syscall.CloseHandle(syscall.Handle(handler)); err != nil { + return nil, os.NewSyscallError("CloseHandle", err) + } + + // Slice memory layout + // Copied this snippet from golang/sys package + var sl = struct { + addr uintptr + len int + cap int + }{addr, int(size), int(size)} + + // Use unsafe to turn sl into a []byte. + data := *(*[]byte)(unsafe.Pointer(&sl)) + + return data, nil +} + +func munmap(b []byte) error { + return syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&b[0]))) +} + +func madvise(b []byte, readahead bool) error { + // Do Nothing. We don’t care about this setting on Windows + return nil +} + +func msync(b []byte) error { + // TODO: Figure out how to do msync on Windows. + return nil +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/rtutil.go b/vendor/github.com/dgraph-io/ristretto/z/rtutil.go new file mode 100644 index 0000000000..8f317c80d3 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/rtutil.go @@ -0,0 +1,75 @@ +// MIT License + +// Copyright (c) 2019 Ewan Chou + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package z + +import ( + "unsafe" +) + +// NanoTime returns the current time in nanoseconds from a monotonic clock. +//go:linkname NanoTime runtime.nanotime +func NanoTime() int64 + +// CPUTicks is a faster alternative to NanoTime to measure time duration. +//go:linkname CPUTicks runtime.cputicks +func CPUTicks() int64 + +type stringStruct struct { + str unsafe.Pointer + len int +} + +//go:noescape +//go:linkname memhash runtime.memhash +func memhash(p unsafe.Pointer, h, s uintptr) uintptr + +// MemHash is the hash function used by go map, it utilizes available hardware instructions(behaves +// as aeshash if aes instruction is available). +// NOTE: The hash seed changes for every process. So, this cannot be used as a persistent hash. +func MemHash(data []byte) uint64 { + ss := (*stringStruct)(unsafe.Pointer(&data)) + return uint64(memhash(ss.str, 0, uintptr(ss.len))) +} + +// MemHashString is the hash function used by go map, it utilizes available hardware instructions +// (behaves as aeshash if aes instruction is available). +// NOTE: The hash seed changes for every process. So, this cannot be used as a persistent hash. +func MemHashString(str string) uint64 { + ss := (*stringStruct)(unsafe.Pointer(&str)) + return uint64(memhash(ss.str, 0, uintptr(ss.len))) +} + +// FastRand is a fast thread local random function. +//go:linkname FastRand runtime.fastrand +func FastRand() uint32 + +//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers +func memclrNoHeapPointers(p unsafe.Pointer, n uintptr) + +func Memclr(b []byte) { + if len(b) == 0 { + return + } + p := unsafe.Pointer(&b[0]) + memclrNoHeapPointers(p, uintptr(len(b))) +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/rtutil.s b/vendor/github.com/dgraph-io/ristretto/z/rtutil.s new file mode 100644 index 0000000000..e69de29bb2 diff --git a/vendor/github.com/dgraph-io/ristretto/z/simd/baseline.go b/vendor/github.com/dgraph-io/ristretto/z/simd/baseline.go new file mode 100644 index 0000000000..967e3a307e --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/simd/baseline.go @@ -0,0 +1,127 @@ +package simd + +import ( + "fmt" + "runtime" + "sort" + "sync" +) + +// Search finds the key using the naive way +func Naive(xs []uint64, k uint64) int16 { + var i int + for i = 0; i < len(xs); i += 2 { + x := xs[i] + if x >= k { + return int16(i / 2) + } + } + return int16(i / 2) +} + +func Clever(xs []uint64, k uint64) int16 { + if len(xs) < 8 { + return Naive(xs, k) + } + var twos, pk [4]uint64 + pk[0] = k + pk[1] = k + pk[2] = k + pk[3] = k + for i := 0; i < len(xs); i += 8 { + twos[0] = xs[i] + twos[1] = xs[i+2] + twos[2] = xs[i+4] + twos[3] = xs[i+6] + if twos[0] >= pk[0] { + return int16(i / 2) + } + if twos[1] >= pk[1] { + return int16((i + 2) / 2) + } + if twos[2] >= pk[2] { + return int16((i + 4) / 2) + } + if twos[3] >= pk[3] { + return int16((i + 6) / 2) + } + + } + return int16(len(xs) / 2) +} + +func Parallel(xs []uint64, k uint64) int16 { + cpus := runtime.NumCPU() + if cpus%2 != 0 { + panic(fmt.Sprintf("odd number of CPUs %v", cpus)) + } + sz := len(xs)/cpus + 1 + var wg sync.WaitGroup + retChan := make(chan int16, cpus) + for i := 0; i < len(xs); i += sz { + end := i + sz + if end >= len(xs) { + end = len(xs) + } + chunk := xs[i:end] + wg.Add(1) + go func(hd int16, xs []uint64, k uint64, wg *sync.WaitGroup, ch chan int16) { + for i := 0; i < len(xs); i += 2 { + if xs[i] >= k { + ch <- (int16(i) + hd) / 2 + break + } + } + wg.Done() + }(int16(i), chunk, k, &wg, retChan) + } + wg.Wait() + close(retChan) + var min int16 = (1 << 15) - 1 + for i := range retChan { + if i < min { + min = i + } + } + if min == (1<<15)-1 { + return int16(len(xs) / 2) + } + return min +} + +func Binary(keys []uint64, key uint64) int16 { + return int16(sort.Search(len(keys), func(i int) bool { + if i*2 >= len(keys) { + return true + } + return keys[i*2] >= key + })) +} + +func cmp2_native(twos, pk [2]uint64) int16 { + if twos[0] == pk[0] { + return 0 + } + if twos[1] == pk[1] { + return 1 + } + return 2 +} + +func cmp4_native(fours, pk [4]uint64) int16 { + for i := range fours { + if fours[i] >= pk[i] { + return int16(i) + } + } + return 4 +} + +func cmp8_native(a [8]uint64, pk [4]uint64) int16 { + for i := range a { + if a[i] >= pk[0] { + return int16(i) + } + } + return 8 +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/simd/search.go b/vendor/github.com/dgraph-io/ristretto/z/simd/search.go new file mode 100644 index 0000000000..0d001ee0c4 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/simd/search.go @@ -0,0 +1,51 @@ +// +build !amd64 + +/* + * Copyright 2020 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package simd + +// Search uses the Clever search to find the correct key. +func Search(xs []uint64, k uint64) int16 { + if len(xs) < 8 { + return Naive(xs, k) + } + var twos, pk [4]uint64 + pk[0] = k + pk[1] = k + pk[2] = k + pk[3] = k + for i := 0; i < len(xs); i += 8 { + twos[0] = xs[i] + twos[1] = xs[i+2] + twos[2] = xs[i+4] + twos[3] = xs[i+6] + if twos[0] >= pk[0] { + return int16(i / 2) + } + if twos[1] >= pk[1] { + return int16((i + 2) / 2) + } + if twos[2] >= pk[2] { + return int16((i + 4) / 2) + } + if twos[3] >= pk[3] { + return int16((i + 6) / 2) + } + + } + return int16(len(xs) / 2) +} diff --git a/vendor/github.com/dgraph-io/ristretto/z/simd/search_amd64.s b/vendor/github.com/dgraph-io/ristretto/z/simd/search_amd64.s new file mode 100644 index 0000000000..150c846647 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/simd/search_amd64.s @@ -0,0 +1,60 @@ +// Code generated by command: go run asm2.go -out search_amd64.s -stubs stub_search_amd64.go. DO NOT EDIT. + +#include "textflag.h" + +// func Search(xs []uint64, k uint64) int16 +TEXT ·Search(SB), NOSPLIT, $0-34 + MOVQ xs_base+0(FP), AX + MOVQ xs_len+8(FP), CX + MOVQ k+24(FP), DX + + // Save n + MOVQ CX, BX + + // Initialize idx register to zero. + XORL BP, BP + +loop: + // Unroll1 + CMPQ (AX)(BP*8), DX + JAE Found + + // Unroll2 + CMPQ 16(AX)(BP*8), DX + JAE Found2 + + // Unroll3 + CMPQ 32(AX)(BP*8), DX + JAE Found3 + + // Unroll4 + CMPQ 48(AX)(BP*8), DX + JAE Found4 + + // plus8 + ADDQ $0x08, BP + CMPQ BP, CX + JB loop + JMP NotFound + +Found2: + ADDL $0x02, BP + JMP Found + +Found3: + ADDL $0x04, BP + JMP Found + +Found4: + ADDL $0x06, BP + +Found: + MOVL BP, BX + +NotFound: + MOVL BX, BP + SHRL $0x1f, BP + ADDL BX, BP + SHRL $0x01, BP + MOVL BP, ret+32(FP) + RET diff --git a/vendor/github.com/dgraph-io/ristretto/z/simd/stub_search_amd64.go b/vendor/github.com/dgraph-io/ristretto/z/simd/stub_search_amd64.go new file mode 100644 index 0000000000..0821d38a77 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/simd/stub_search_amd64.go @@ -0,0 +1,6 @@ +// Code generated by command: go run asm2.go -out search_amd64.s -stubs stub_search_amd64.go. DO NOT EDIT. + +package simd + +// Search finds the first idx for which xs[idx] >= k in xs. +func Search(xs []uint64, k uint64) int16 diff --git a/vendor/github.com/dgraph-io/ristretto/z/z.go b/vendor/github.com/dgraph-io/ristretto/z/z.go new file mode 100644 index 0000000000..97455586a1 --- /dev/null +++ b/vendor/github.com/dgraph-io/ristretto/z/z.go @@ -0,0 +1,151 @@ +/* + * Copyright 2019 Dgraph Labs, Inc. and Contributors + * + * 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. + */ + +package z + +import ( + "context" + "sync" + + "github.com/cespare/xxhash/v2" +) + +// TODO: Figure out a way to re-use memhash for the second uint64 hash, we +// already know that appending bytes isn't reliable for generating a +// second hash (see Ristretto PR #88). +// +// We also know that while the Go runtime has a runtime memhash128 +// function, it's not possible to use it to generate [2]uint64 or +// anything resembling a 128bit hash, even though that's exactly what +// we need in this situation. +func KeyToHash(key interface{}) (uint64, uint64) { + if key == nil { + return 0, 0 + } + switch k := key.(type) { + case uint64: + return k, 0 + case string: + return MemHashString(k), xxhash.Sum64String(k) + case []byte: + return MemHash(k), xxhash.Sum64(k) + case byte: + return uint64(k), 0 + case int: + return uint64(k), 0 + case int32: + return uint64(k), 0 + case uint32: + return uint64(k), 0 + case int64: + return uint64(k), 0 + default: + panic("Key type not supported") + } +} + +var ( + dummyCloserChan <-chan struct{} + tmpDir string +) + +// Closer holds the two things we need to close a goroutine and wait for it to +// finish: a chan to tell the goroutine to shut down, and a WaitGroup with +// which to wait for it to finish shutting down. +type Closer struct { + waiting sync.WaitGroup + + ctx context.Context + cancel context.CancelFunc +} + +// SetTmpDir sets the temporary directory for the temporary buffers. +func SetTmpDir(dir string) { + tmpDir = dir +} + +// NewCloser constructs a new Closer, with an initial count on the WaitGroup. +func NewCloser(initial int) *Closer { + ret := &Closer{} + ret.ctx, ret.cancel = context.WithCancel(context.Background()) + ret.waiting.Add(initial) + return ret +} + +// AddRunning Add()'s delta to the WaitGroup. +func (lc *Closer) AddRunning(delta int) { + lc.waiting.Add(delta) +} + +// Ctx can be used to get a context, which would automatically get cancelled when Signal is called. +func (lc *Closer) Ctx() context.Context { + if lc == nil { + return context.Background() + } + return lc.ctx +} + +// Signal signals the HasBeenClosed signal. +func (lc *Closer) Signal() { + // Todo(ibrahim): Change Signal to return error on next badger breaking change. + lc.cancel() +} + +// HasBeenClosed gets signaled when Signal() is called. +func (lc *Closer) HasBeenClosed() <-chan struct{} { + if lc == nil { + return dummyCloserChan + } + return lc.ctx.Done() +} + +// Done calls Done() on the WaitGroup. +func (lc *Closer) Done() { + if lc == nil { + return + } + lc.waiting.Done() +} + +// Wait waits on the WaitGroup. (It waits for NewCloser's initial value, AddRunning, and Done +// calls to balance out.) +func (lc *Closer) Wait() { + lc.waiting.Wait() +} + +// SignalAndWait calls Signal(), then Wait(). +func (lc *Closer) SignalAndWait() { + lc.Signal() + lc.Wait() +} + +// ZeroOut zeroes out all the bytes in the range [start, end). +func ZeroOut(dst []byte, start, end int) { + if start < 0 || start >= len(dst) { + return // BAD + } + if end >= len(dst) { + end = len(dst) + } + if end-start <= 0 { + return + } + Memclr(dst[start:end]) + // b := dst[start:end] + // for i := range b { + // b[i] = 0x0 + // } +} diff --git a/vendor/github.com/docker/cli/AUTHORS b/vendor/github.com/docker/cli/AUTHORS new file mode 100644 index 0000000000..8990f85b56 --- /dev/null +++ b/vendor/github.com/docker/cli/AUTHORS @@ -0,0 +1,771 @@ +# This file lists all individuals having contributed content to the repository. +# For how it is generated, see `scripts/docs/generate-authors.sh`. + +Aanand Prasad +Aaron L. Xu +Aaron Lehmann +Aaron.L.Xu +Abdur Rehman +Abhinandan Prativadi +Abin Shahab +Abreto FU +Ace Tang +Addam Hardy +Adolfo Ochagavía +Adrian Plata +Adrien Duermael +Adrien Folie +Ahmet Alp Balkan +Aidan Feldman +Aidan Hobson Sayers +AJ Bowen +Akhil Mohan +Akihiro Suda +Akim Demaille +Alan Thompson +Albert Callarisa +Albin Kerouanton +Aleksa Sarai +Aleksander Piotrowski +Alessandro Boch +Alex Mavrogiannis +Alex Mayer +Alexander Boyd +Alexander Larsson +Alexander Morozov +Alexander Ryabov +Alexandre González +Alfred Landrum +Alicia Lauerman +Allen Sun +Alvin Deng +Amen Belayneh +Amir Goldstein +Amit Krishnan +Amit Shukla +Amy Lindburg +Anca Iordache +Anda Xu +Andrea Luzzardi +Andreas Köhler +Andrew France +Andrew Hsu +Andrew Macpherson +Andrew McDonnell +Andrew Po +Andrey Petrov +Andrii Berehuliak +André Martins +Andy Goldstein +Andy Rothfusz +Anil Madhavapeddy +Ankush Agarwal +Anne Henmi +Anton Polonskiy +Antonio Murdaca +Antonis Kalipetis +Anusha Ragunathan +Ao Li +Arash Deshmeh +Arko Dasgupta +Arnaud Porterie +Arthur Peka +Ashwini Oruganti +Azat Khuyiyakhmetov +Bardia Keyoumarsi +Barnaby Gray +Bastiaan Bakker +BastianHofmann +Ben Bonnefoy +Ben Creasy +Ben Firshman +Benjamin Boudreau +Benoit Sigoure +Bhumika Bayani +Bill Wang +Bin Liu +Bingshen Wang +Boaz Shuster +Bogdan Anton +Boris Pruessmann +Bradley Cicenas +Brandon Mitchell +Brandon Philips +Brent Salisbury +Bret Fisher +Brian (bex) Exelbierd +Brian Goff +Brian Wieder +Bryan Bess +Bryan Boreham +Bryan Murphy +bryfry +Cameron Spear +Cao Weiwei +Carlo Mion +Carlos Alexandro Becker +Carlos de Paula +Ce Gao +Cedric Davies +Cezar Sa Espinola +Chad Faragher +Chao Wang +Charles Chan +Charles Law +Charles Smith +Charlie Drage +ChaYoung You +Chen Chuanliang +Chen Hanxiao +Chen Mingjie +Chen Qiu +Chris Gavin +Chris Gibson +Chris McKinnel +Chris Snow +Chris Weyl +Christian Persson +Christian Stefanescu +Christophe Robin +Christophe Vidal +Christopher Biscardi +Christopher Crone +Christopher Jones +Christy Norman +Chun Chen +Clinton Kitson +Coenraad Loubser +Colin Hebert +Collin Guarino +Colm Hally +Comical Derskeal <27731088+derskeal@users.noreply.github.com> +Corey Farrell +Corey Quon +Craig Wilhite +Cristian Staretu +Daehyeok Mun +Dafydd Crosby +Daisuke Ito +dalanlan +Damien Nadé +Dan Cotora +Daniel Artine +Daniel Cassidy +Daniel Dao +Daniel Farrell +Daniel Gasienica +Daniel Goosen +Daniel Helfand +Daniel Hiltgen +Daniel J Walsh +Daniel Nephin +Daniel Norberg +Daniel Watkins +Daniel Zhang +Daniil Nikolenko +Danny Berger +Darren Shepherd +Darren Stahl +Dattatraya Kumbhar +Dave Goodchild +Dave Henderson +Dave Tucker +David Beitey +David Calavera +David Cramer +David Dooling +David Gageot +David Lechner +David Scott +David Sheets +David Williamson +David Xia +David Young +Deng Guangxing +Denis Defreyne +Denis Gladkikh +Denis Ollier +Dennis Docter +Derek McGowan +Deshi Xiao +Dharmit Shah +Dhawal Yogesh Bhanushali +Dieter Reuter +Dima Stopel +Dimitry Andric +Ding Fei +Diogo Monica +Djordje Lukic +Dmitry Gusev +Dmitry Smirnov +Dmitry V. Krivenok +Dominik Braun +Don Kjer +Dong Chen +Doug Davis +Drew Erny +Ed Costello +Elango Sivanandam +Eli Uriegas +Eli Uriegas +Elias Faxö +Elliot Luo <956941328@qq.com> +Eric Curtin +Eric G. Noriega +Eric Rosenberg +Eric Sage +Eric-Olivier Lamey +Erica Windisch +Erik Hollensbe +Erik St. Martin +Essam A. Hassan +Ethan Haynes +Euan Kemp +Eugene Yakubovich +Evan Allrich +Evan Hazlett +Evan Krall +Evelyn Xu +Everett Toews +Fabio Falci +Fabrizio Soppelsa +Felix Hupfeld +Felix Rabe +Filip Jareš +Flavio Crisciani +Florian Klein +Forest Johnson +Foysal Iqbal +François Scala +Fred Lifton +Frederic Hemberger +Frederick F. Kautz IV +Frederik Nordahl Jul Sabroe +Frieder Bluemle +Gabriel Nicolas Avellaneda +Gaetan de Villele +Gang Qiao +Gary Schaetz +Genki Takiuchi +George MacRorie +George Xie +Gianluca Borello +Gildas Cuisinier +Goksu Toprak +Gou Rao +Grant Reaber +Greg Pflaum +Guilhem Lettron +Guillaume J. Charmes +Guillaume Le Floch +gwx296173 +Günther Jungbluth +Hakan Özler +Hao Zhang <21521210@zju.edu.cn> +Harald Albers +Harold Cooper +Harry Zhang +He Simei +Hector S +Helen Xie +Henning Sprang +Henry N +Hernan Garcia +Hongbin Lu +Hu Keping +Huayi Zhang +Hugo Gabriel Eyherabide +huqun +Huu Nguyen +Hyzhou Zhy +Ian Campbell +Ian Philpot +Ignacio Capurro +Ilya Dmitrichenko +Ilya Khlopotov +Ilya Sotkov +Ioan Eugen Stan +Isabel Jimenez +Ivan Grcic +Ivan Markin +Jacob Atzen +Jacob Tomlinson +Jaivish Kothari +Jake Lambert +Jake Sanders +James Nesbitt +James Turnbull +Jamie Hannaford +Jan Koprowski +Jan Pazdziora +Jan-Jaap Driessen +Jana Radhakrishnan +Jared Hocutt +Jasmine Hegman +Jason Heiss +Jason Plum +Jay Kamat +Jean Rouge +Jean-Christophe Sirot +Jean-Pierre Huynh +Jeff Lindsay +Jeff Nickoloff +Jeff Silberman +Jeremy Chambers +Jeremy Unruh +Jeremy Yallop +Jeroen Franse +Jesse Adametz +Jessica Frazelle +Jezeniel Zapanta +Jian Zhang +Jie Luo +Jilles Oldenbeuving +Jim Galasyn +Jimmy Leger +Jimmy Song +jimmyxian +Jintao Zhang +Joao Fernandes +Joe Abbey +Joe Doliner +Joe Gordon +Joel Handwell +Joey Geiger +Joffrey F +Johan Euphrosine +Johannes 'fish' Ziemke +John Feminella +John Harris +John Howard +John Laswell +John Maguire +John Mulhausen +John Starks +John Stephens +John Tims +John V. Martinez +John Willis +Jon Johnson +Jonatas Baldin +Jonathan Boulle +Jonathan Lee +Jonathan Lomas +Jonathan McCrohan +Jonh Wendell +Jordan Jennings +Jose J. Escobar <53836904+jescobar-docker@users.noreply.github.com> +Joseph Kern +Josh Bodah +Josh Chorlton +Josh Hawn +Josh Horwitz +Josh Soref +Julien Barbier +Julien Kassar +Julien Maitrehenry +Justas Brazauskas +Justin Cormack +Justin Simonelis +Justyn Temme +Jyrki Puttonen +Jérémie Drouet +Jérôme Petazzoni +Jörg Thalheim +Kai Blin +Kai Qiang Wu (Kennan) +Kara Alexandra +Kareem Khazem +Karthik Nayak +Kat Samperi +Kathryn Spiers +Katie McLaughlin +Ke Xu +Kei Ohmura +Keith Hudgins +Ken Cochrane +Ken ICHIKAWA +Kenfe-Mickaël Laventure +Kevin Burke +Kevin Feyrer +Kevin Kern +Kevin Kirsche +Kevin Meredith +Kevin Richardson +Kevin Woblick +khaled souf +Kim Eik +Kir Kolyshkin +Kotaro Yoshimatsu +Krasi Georgiev +Kris-Mikael Krister +Kun Zhang +Kunal Kushwaha +Lachlan Cooper +Lai Jiangshan +Lars Kellogg-Stedman +Laura Frank +Laurent Erignoux +Lee Gaines +Lei Jitang +Lennie +Leo Gallucci +Lewis Daly +Li Yi +Li Yi +Liang-Chi Hsieh +Lifubang +Lihua Tang +Lily Guo +Lin Lu +Linus Heckemann +Liping Xue +Liron Levin +liwenqi +lixiaobing10051267 +Lloyd Dewolf +Lorenzo Fontana +Louis Opter +Luca Favatella +Luca Marturana +Lucas Chan +Luka Hartwig +Lukas Heeren +Lukasz Zajaczkowski +Lydell Manganti +Lénaïc Huard +Ma Shimiao +Mabin +Maciej Kalisz +Madhav Puri +Madhu Venugopal +Madhur Batra +Malte Janduda +Manjunath A Kumatagi +Mansi Nahar +mapk0y +Marc Bihlmaier +Marco Mariani +Marco Vedovati +Marcus Martins +Marianna Tessel +Marius Ileana +Marius Sturm +Mark Oates +Marsh Macy +Martin Mosegaard Amdisen +Mary Anthony +Mason Fish +Mason Malone +Mateusz Major +Mathieu Champlon +Matt Gucci +Matt Robenolt +Matteo Orefice +Matthew Heon +Matthieu Hauglustaine +Mauro Porras P +Max Shytikov +Maxime Petazzoni +Mei ChunTao +Micah Zoltu +Michael A. Smith +Michael Bridgen +Michael Crosby +Michael Friis +Michael Irwin +Michael Käufl +Michael Prokop +Michael Scharf +Michael Spetsiotis +Michael Steinert +Michael West +Michal Minář +Michał Czeraszkiewicz +Miguel Angel Alvarez Cabrerizo +Mihai Borobocea +Mihuleacc Sergiu +Mike Brown +Mike Casas +Mike Danese +Mike Dillon +Mike Goelzer +Mike MacCana +mikelinjie <294893458@qq.com> +Mikhail Vasin +Milind Chawre +Mindaugas Rukas +Miroslav Gula +Misty Stanley-Jones +Mohammad Banikazemi +Mohammed Aaqib Ansari +Mohini Anne Dsouza +Moorthy RS +Morgan Bauer +Morten Hekkvang +Moysés Borges +Mrunal Patel +muicoder +Muthukumar R +Máximo Cuadros +Mårten Cassel +Nace Oroz +Nahum Shalman +Nalin Dahyabhai +Nao YONASHIRO +Nassim 'Nass' Eddequiouaq +Natalie Parker +Nate Brennand +Nathan Hsieh +Nathan LeClaire +Nathan McCauley +Neil Peterson +Nick Adcock +Nico Stapelbroek +Nicola Kabar +Nicolas Borboën +Nicolas De Loof +Nikhil Chawla +Nikolas Garofil +Nikolay Milovanov +Nir Soffer +Nishant Totla +NIWA Hideyuki +Noah Treuhaft +O.S. Tezer +Odin Ugedal +ohmystack +Olle Jonsson +Olli Janatuinen +Oscar Wieman +Otto Kekäläinen +Ovidio Mallo +Pascal Borreli +Patrick Böänziger +Patrick Hemmer +Patrick Lang +Paul +Paul Kehrer +Paul Lietar +Paul Mulders +Paul Weaver +Pavel Pospisil +Paweł Szczekutowicz +Peeyush Gupta +Per Lundberg +Peter Edge +Peter Hsu +Peter Jaffe +Peter Kehl +Peter Nagy +Peter Salvatore +Peter Waller +Phil Estes +Philip Alexander Etling +Philipp Gillé +Philipp Schmied +pidster +pixelistik +Pratik Karki +Prayag Verma +Preston Cowley +Pure White +Qiang Huang +Qinglan Peng +qudongfang +Raghavendra K T +Rahul Zoldyck +Ravi Shekhar Jethani +Ray Tsang +Reficul +Remy Suen +Renaud Gaubert +Ricardo N Feliciano +Rich Moyse +Richard Mathie +Richard Scothern +Rick Wieman +Ritesh H Shukla +Riyaz Faizullabhoy +Rob Gulewich +Robert Wallis +Robin Naundorf +Robin Speekenbrink +Rodolfo Ortiz +Rogelio Canedo +Rohan Verma +Roland Kammerer +Roman Dudin +Rory Hunter +Ross Boucher +Rubens Figueiredo +Rui Cao +Ryan Belgrave +Ryan Detzel +Ryan Stelly +Ryan Wilson-Perkin +Ryan Zhang +Sainath Grandhi +Sakeven Jiang +Sally O'Malley +Sam Neirinck +Samarth Shah +Sambuddha Basu +Sami Tabet +Samuel Cochran +Samuel Karp +Santhosh Manohar +Sargun Dhillon +Saswat Bhattacharya +Scott Brenner +Scott Collier +Sean Christopherson +Sean Rodman +Sebastiaan van Stijn +Sergey Tryuber +Serhat Gülçiçek +Sevki Hasirci +Shaun Kaasten +Sheng Yang +Shijiang Wei +Shishir Mahajan +Shoubhik Bose +Shukui Yang +Sian Lerk Lau +Sidhartha Mani +sidharthamani +Silvin Lubecki +Simei He +Simon Ferquel +Simon Heimberg +Sindhu S +Slava Semushin +Solomon Hykes +Song Gao +Spencer Brown +squeegels <1674195+squeegels@users.noreply.github.com> +Srini Brahmaroutu +Stefan S. +Stefan Scherer +Stefan Weil +Stephane Jeandeaux +Stephen Day +Stephen Rust +Steve Durrheimer +Steve Richards +Steven Burgess +Subhajit Ghosh +Sun Jianbo +Sune Keller +Sungwon Han +Sunny Gogoi +Sven Dowideit +Sylvain Baubeau +Sébastien HOUZÉ +T K Sourabh +TAGOMORI Satoshi +taiji-tech +Taylor Jones +Tejaswini Duggaraju +Tengfei Wang +Teppei Fukuda +Thatcher Peskens +Thibault Coupin +Thomas Gazagnaire +Thomas Krzero +Thomas Leonard +Thomas Léveil +Thomas Riccardi +Thomas Swift +Tianon Gravi +Tianyi Wang +Tibor Vass +Tim Dettrick +Tim Hockin +Tim Sampson +Tim Smith +Tim Waugh +Tim Wraight +timfeirg +Timothy Hobbs +Tobias Bradtke +Tobias Gesellchen +Todd Whiteman +Tom Denham +Tom Fotherby +Tom Klingenberg +Tom Milligan +Tom X. Tobin +Tomas Tomecek +Tomasz Kopczynski +Tomáš Hrčka +Tony Abboud +Tõnis Tiigi +Trapier Marshall +Travis Cline +Tristan Carel +Tycho Andersen +Tycho Andersen +uhayate +Ulrich Bareth +Ulysses Souza +Umesh Yadav +Valentin Lorentz +Venkateswara Reddy Bukkasamudram +Veres Lajos +Victor Vieux +Victoria Bialas +Viktor Stanchev +Vimal Raghubir +Vincent Batts +Vincent Bernat +Vincent Demeester +Vincent Woo +Vishnu Kannan +Vivek Goyal +Wang Jie +Wang Lei +Wang Long +Wang Ping +Wang Xing +Wang Yuexiao +Wang Yumu <37442693@qq.com> +Wataru Ishida +Wayne Song +Wen Cheng Ma +Wenzhi Liang +Wes Morgan +Wewang Xiaorenfine +William Henry +Xianglin Gao +Xiaodong Liu +Xiaodong Zhang +Xiaoxi He +Xinbo Weng +Xuecong Liao +Yan Feng +Yanqiang Miao +Yassine Tijani +Yi EungJun +Ying Li +Yong Tang +Yosef Fertel +Yu Peng +Yuan Sun +Yue Zhang +Yunxiang Huang +Zachary Romero +Zander Mackie +zebrilee +Zhang Kun +Zhang Wei +Zhang Wentao +ZhangHang +zhenghenghuo +Zhou Hao +Zhoulin Xie +Zhu Guihua +Álex González +Álvaro Lázaro +Átila Camurça Alves +徐俊杰 diff --git a/vendor/github.com/docker/cli/LICENSE b/vendor/github.com/docker/cli/LICENSE new file mode 100644 index 0000000000..9c8e20ab85 --- /dev/null +++ b/vendor/github.com/docker/cli/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright 2013-2017 Docker, Inc. + + 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 + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/docker/cli/NOTICE b/vendor/github.com/docker/cli/NOTICE new file mode 100644 index 0000000000..58b19b6d15 --- /dev/null +++ b/vendor/github.com/docker/cli/NOTICE @@ -0,0 +1,19 @@ +Docker +Copyright 2012-2017 Docker, Inc. + +This product includes software developed at Docker, Inc. (https://www.docker.com). + +This product contains software (https://github.com/creack/pty) developed +by Keith Rarick, licensed under the MIT License. + +The following is courtesy of our legal counsel: + + +Use and transfer of Docker may be subject to certain restrictions by the +United States and other governments. +It is your responsibility to ensure that your use and/or transfer does not +violate applicable laws. + +For more information, please see https://www.bis.doc.gov + +See also https://www.apache.org/dev/crypto.html and/or seek legal counsel. diff --git a/vendor/github.com/docker/cli/cli/config/config.go b/vendor/github.com/docker/cli/cli/config/config.go new file mode 100644 index 0000000000..93275f3d98 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/config/config.go @@ -0,0 +1,163 @@ +package config + +import ( + "fmt" + "io" + "os" + "path/filepath" + "strings" + "sync" + + "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/cli/config/credentials" + "github.com/docker/cli/cli/config/types" + "github.com/docker/docker/pkg/homedir" + "github.com/pkg/errors" +) + +const ( + // ConfigFileName is the name of config file + ConfigFileName = "config.json" + configFileDir = ".docker" + oldConfigfile = ".dockercfg" + contextsDir = "contexts" +) + +var ( + initConfigDir = new(sync.Once) + configDir string + homeDir string +) + +// resetHomeDir is used in testing to reset the "homeDir" package variable to +// force re-lookup of the home directory between tests. +func resetHomeDir() { + homeDir = "" +} + +func getHomeDir() string { + if homeDir == "" { + homeDir = homedir.Get() + } + return homeDir +} + +// resetConfigDir is used in testing to reset the "configDir" package variable +// and its sync.Once to force re-lookup between tests. +func resetConfigDir() { + configDir = "" + initConfigDir = new(sync.Once) +} + +func setConfigDir() { + if configDir != "" { + return + } + configDir = os.Getenv("DOCKER_CONFIG") + if configDir == "" { + configDir = filepath.Join(getHomeDir(), configFileDir) + } +} + +// Dir returns the directory the configuration file is stored in +func Dir() string { + initConfigDir.Do(setConfigDir) + return configDir +} + +// ContextStoreDir returns the directory the docker contexts are stored in +func ContextStoreDir() string { + return filepath.Join(Dir(), contextsDir) +} + +// SetDir sets the directory the configuration file is stored in +func SetDir(dir string) { + configDir = filepath.Clean(dir) +} + +// Path returns the path to a file relative to the config dir +func Path(p ...string) (string, error) { + path := filepath.Join(append([]string{Dir()}, p...)...) + if !strings.HasPrefix(path, Dir()+string(filepath.Separator)) { + return "", errors.Errorf("path %q is outside of root config directory %q", path, Dir()) + } + return path, nil +} + +// LegacyLoadFromReader is a convenience function that creates a ConfigFile object from +// a non-nested reader +func LegacyLoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) { + configFile := configfile.ConfigFile{ + AuthConfigs: make(map[string]types.AuthConfig), + } + err := configFile.LegacyLoadFromReader(configData) + return &configFile, err +} + +// LoadFromReader is a convenience function that creates a ConfigFile object from +// a reader +func LoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) { + configFile := configfile.ConfigFile{ + AuthConfigs: make(map[string]types.AuthConfig), + } + err := configFile.LoadFromReader(configData) + return &configFile, err +} + +// TODO remove this temporary hack, which is used to warn about the deprecated ~/.dockercfg file +var printLegacyFileWarning bool + +// Load reads the configuration files in the given directory, and sets up +// the auth config information and returns values. +// FIXME: use the internal golang config parser +func Load(configDir string) (*configfile.ConfigFile, error) { + printLegacyFileWarning = false + + if configDir == "" { + configDir = Dir() + } + + filename := filepath.Join(configDir, ConfigFileName) + configFile := configfile.New(filename) + + // Try happy path first - latest config file + if file, err := os.Open(filename); err == nil { + defer file.Close() + err = configFile.LoadFromReader(file) + if err != nil { + err = errors.Wrap(err, filename) + } + return configFile, err + } else if !os.IsNotExist(err) { + // if file is there but we can't stat it for any reason other + // than it doesn't exist then stop + return configFile, errors.Wrap(err, filename) + } + + // Can't find latest config file so check for the old one + filename = filepath.Join(getHomeDir(), oldConfigfile) + if file, err := os.Open(filename); err == nil { + printLegacyFileWarning = true + defer file.Close() + if err := configFile.LegacyLoadFromReader(file); err != nil { + return configFile, errors.Wrap(err, filename) + } + } + return configFile, nil +} + +// LoadDefaultConfigFile attempts to load the default config file and returns +// an initialized ConfigFile struct if none is found. +func LoadDefaultConfigFile(stderr io.Writer) *configfile.ConfigFile { + configFile, err := Load(Dir()) + if err != nil { + fmt.Fprintf(stderr, "WARNING: Error loading config file: %v\n", err) + } + if printLegacyFileWarning { + _, _ = fmt.Fprintln(stderr, "WARNING: Support for the legacy ~/.dockercfg configuration file and file-format is deprecated and will be removed in an upcoming release") + } + if !configFile.ContainsAuth() { + configFile.CredentialsStore = credentials.DetectDefaultStore(configFile.CredentialsStore) + } + return configFile +} diff --git a/vendor/github.com/docker/cli/cli/config/configfile/file.go b/vendor/github.com/docker/cli/cli/config/configfile/file.go new file mode 100644 index 0000000000..dc9f39eb7e --- /dev/null +++ b/vendor/github.com/docker/cli/cli/config/configfile/file.go @@ -0,0 +1,415 @@ +package configfile + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/docker/cli/cli/config/credentials" + "github.com/docker/cli/cli/config/types" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +const ( + // This constant is only used for really old config files when the + // URL wasn't saved as part of the config file and it was just + // assumed to be this value. + defaultIndexServer = "https://index.docker.io/v1/" +) + +// ConfigFile ~/.docker/config.json file info +type ConfigFile struct { + AuthConfigs map[string]types.AuthConfig `json:"auths"` + HTTPHeaders map[string]string `json:"HttpHeaders,omitempty"` + PsFormat string `json:"psFormat,omitempty"` + ImagesFormat string `json:"imagesFormat,omitempty"` + NetworksFormat string `json:"networksFormat,omitempty"` + PluginsFormat string `json:"pluginsFormat,omitempty"` + VolumesFormat string `json:"volumesFormat,omitempty"` + StatsFormat string `json:"statsFormat,omitempty"` + DetachKeys string `json:"detachKeys,omitempty"` + CredentialsStore string `json:"credsStore,omitempty"` + CredentialHelpers map[string]string `json:"credHelpers,omitempty"` + Filename string `json:"-"` // Note: for internal use only + ServiceInspectFormat string `json:"serviceInspectFormat,omitempty"` + ServicesFormat string `json:"servicesFormat,omitempty"` + TasksFormat string `json:"tasksFormat,omitempty"` + SecretFormat string `json:"secretFormat,omitempty"` + ConfigFormat string `json:"configFormat,omitempty"` + NodesFormat string `json:"nodesFormat,omitempty"` + PruneFilters []string `json:"pruneFilters,omitempty"` + Proxies map[string]ProxyConfig `json:"proxies,omitempty"` + Experimental string `json:"experimental,omitempty"` + StackOrchestrator string `json:"stackOrchestrator,omitempty"` + Kubernetes *KubernetesConfig `json:"kubernetes,omitempty"` + CurrentContext string `json:"currentContext,omitempty"` + CLIPluginsExtraDirs []string `json:"cliPluginsExtraDirs,omitempty"` + Plugins map[string]map[string]string `json:"plugins,omitempty"` + Aliases map[string]string `json:"aliases,omitempty"` +} + +// ProxyConfig contains proxy configuration settings +type ProxyConfig struct { + HTTPProxy string `json:"httpProxy,omitempty"` + HTTPSProxy string `json:"httpsProxy,omitempty"` + NoProxy string `json:"noProxy,omitempty"` + FTPProxy string `json:"ftpProxy,omitempty"` +} + +// KubernetesConfig contains Kubernetes orchestrator settings +type KubernetesConfig struct { + AllNamespaces string `json:"allNamespaces,omitempty"` +} + +// New initializes an empty configuration file for the given filename 'fn' +func New(fn string) *ConfigFile { + return &ConfigFile{ + AuthConfigs: make(map[string]types.AuthConfig), + HTTPHeaders: make(map[string]string), + Filename: fn, + Plugins: make(map[string]map[string]string), + Aliases: make(map[string]string), + } +} + +// LegacyLoadFromReader reads the non-nested configuration data given and sets up the +// auth config information with given directory and populates the receiver object +func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error { + b, err := ioutil.ReadAll(configData) + if err != nil { + return err + } + + if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil { + arr := strings.Split(string(b), "\n") + if len(arr) < 2 { + return errors.Errorf("The Auth config file is empty") + } + authConfig := types.AuthConfig{} + origAuth := strings.Split(arr[0], " = ") + if len(origAuth) != 2 { + return errors.Errorf("Invalid Auth config file") + } + authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1]) + if err != nil { + return err + } + authConfig.ServerAddress = defaultIndexServer + configFile.AuthConfigs[defaultIndexServer] = authConfig + } else { + for k, authConfig := range configFile.AuthConfigs { + authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth) + if err != nil { + return err + } + authConfig.Auth = "" + authConfig.ServerAddress = k + configFile.AuthConfigs[k] = authConfig + } + } + return nil +} + +// LoadFromReader reads the configuration data given and sets up the auth config +// information with given directory and populates the receiver object +func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error { + if err := json.NewDecoder(configData).Decode(&configFile); err != nil && !errors.Is(err, io.EOF) { + return err + } + var err error + for addr, ac := range configFile.AuthConfigs { + if ac.Auth != "" { + ac.Username, ac.Password, err = decodeAuth(ac.Auth) + if err != nil { + return err + } + } + ac.Auth = "" + ac.ServerAddress = addr + configFile.AuthConfigs[addr] = ac + } + return checkKubernetesConfiguration(configFile.Kubernetes) +} + +// ContainsAuth returns whether there is authentication configured +// in this file or not. +func (configFile *ConfigFile) ContainsAuth() bool { + return configFile.CredentialsStore != "" || + len(configFile.CredentialHelpers) > 0 || + len(configFile.AuthConfigs) > 0 +} + +// GetAuthConfigs returns the mapping of repo to auth configuration +func (configFile *ConfigFile) GetAuthConfigs() map[string]types.AuthConfig { + return configFile.AuthConfigs +} + +// SaveToWriter encodes and writes out all the authorization information to +// the given writer +func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error { + // Encode sensitive data into a new/temp struct + tmpAuthConfigs := make(map[string]types.AuthConfig, len(configFile.AuthConfigs)) + for k, authConfig := range configFile.AuthConfigs { + authCopy := authConfig + // encode and save the authstring, while blanking out the original fields + authCopy.Auth = encodeAuth(&authCopy) + authCopy.Username = "" + authCopy.Password = "" + authCopy.ServerAddress = "" + tmpAuthConfigs[k] = authCopy + } + + saveAuthConfigs := configFile.AuthConfigs + configFile.AuthConfigs = tmpAuthConfigs + defer func() { configFile.AuthConfigs = saveAuthConfigs }() + + // User-Agent header is automatically set, and should not be stored in the configuration + for v := range configFile.HTTPHeaders { + if strings.EqualFold(v, "User-Agent") { + delete(configFile.HTTPHeaders, v) + } + } + + data, err := json.MarshalIndent(configFile, "", "\t") + if err != nil { + return err + } + _, err = writer.Write(data) + return err +} + +// Save encodes and writes out all the authorization information +func (configFile *ConfigFile) Save() (retErr error) { + if configFile.Filename == "" { + return errors.Errorf("Can't save config with empty filename") + } + + dir := filepath.Dir(configFile.Filename) + if err := os.MkdirAll(dir, 0700); err != nil { + return err + } + temp, err := ioutil.TempFile(dir, filepath.Base(configFile.Filename)) + if err != nil { + return err + } + defer func() { + temp.Close() + if retErr != nil { + if err := os.Remove(temp.Name()); err != nil { + logrus.WithError(err).WithField("file", temp.Name()).Debug("Error cleaning up temp file") + } + } + }() + + err = configFile.SaveToWriter(temp) + if err != nil { + return err + } + + if err := temp.Close(); err != nil { + return errors.Wrap(err, "error closing temp file") + } + + // Handle situation where the configfile is a symlink + cfgFile := configFile.Filename + if f, err := os.Readlink(cfgFile); err == nil { + cfgFile = f + } + + // Try copying the current config file (if any) ownership and permissions + copyFilePermissions(cfgFile, temp.Name()) + return os.Rename(temp.Name(), cfgFile) +} + +// ParseProxyConfig computes proxy configuration by retrieving the config for the provided host and +// then checking this against any environment variables provided to the container +func (configFile *ConfigFile) ParseProxyConfig(host string, runOpts map[string]*string) map[string]*string { + var cfgKey string + + if _, ok := configFile.Proxies[host]; !ok { + cfgKey = "default" + } else { + cfgKey = host + } + + config := configFile.Proxies[cfgKey] + permitted := map[string]*string{ + "HTTP_PROXY": &config.HTTPProxy, + "HTTPS_PROXY": &config.HTTPSProxy, + "NO_PROXY": &config.NoProxy, + "FTP_PROXY": &config.FTPProxy, + } + m := runOpts + if m == nil { + m = make(map[string]*string) + } + for k := range permitted { + if *permitted[k] == "" { + continue + } + if _, ok := m[k]; !ok { + m[k] = permitted[k] + } + if _, ok := m[strings.ToLower(k)]; !ok { + m[strings.ToLower(k)] = permitted[k] + } + } + return m +} + +// encodeAuth creates a base64 encoded string to containing authorization information +func encodeAuth(authConfig *types.AuthConfig) string { + if authConfig.Username == "" && authConfig.Password == "" { + return "" + } + + authStr := authConfig.Username + ":" + authConfig.Password + msg := []byte(authStr) + encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg))) + base64.StdEncoding.Encode(encoded, msg) + return string(encoded) +} + +// decodeAuth decodes a base64 encoded string and returns username and password +func decodeAuth(authStr string) (string, string, error) { + if authStr == "" { + return "", "", nil + } + + decLen := base64.StdEncoding.DecodedLen(len(authStr)) + decoded := make([]byte, decLen) + authByte := []byte(authStr) + n, err := base64.StdEncoding.Decode(decoded, authByte) + if err != nil { + return "", "", err + } + if n > decLen { + return "", "", errors.Errorf("Something went wrong decoding auth config") + } + arr := strings.SplitN(string(decoded), ":", 2) + if len(arr) != 2 { + return "", "", errors.Errorf("Invalid auth configuration file") + } + password := strings.Trim(arr[1], "\x00") + return arr[0], password, nil +} + +// GetCredentialsStore returns a new credentials store from the settings in the +// configuration file +func (configFile *ConfigFile) GetCredentialsStore(registryHostname string) credentials.Store { + if helper := getConfiguredCredentialStore(configFile, registryHostname); helper != "" { + return newNativeStore(configFile, helper) + } + return credentials.NewFileStore(configFile) +} + +// var for unit testing. +var newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store { + return credentials.NewNativeStore(configFile, helperSuffix) +} + +// GetAuthConfig for a repository from the credential store +func (configFile *ConfigFile) GetAuthConfig(registryHostname string) (types.AuthConfig, error) { + return configFile.GetCredentialsStore(registryHostname).Get(registryHostname) +} + +// getConfiguredCredentialStore returns the credential helper configured for the +// given registry, the default credsStore, or the empty string if neither are +// configured. +func getConfiguredCredentialStore(c *ConfigFile, registryHostname string) string { + if c.CredentialHelpers != nil && registryHostname != "" { + if helper, exists := c.CredentialHelpers[registryHostname]; exists { + return helper + } + } + return c.CredentialsStore +} + +// GetAllCredentials returns all of the credentials stored in all of the +// configured credential stores. +func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig, error) { + auths := make(map[string]types.AuthConfig) + addAll := func(from map[string]types.AuthConfig) { + for reg, ac := range from { + auths[reg] = ac + } + } + + defaultStore := configFile.GetCredentialsStore("") + newAuths, err := defaultStore.GetAll() + if err != nil { + return nil, err + } + addAll(newAuths) + + // Auth configs from a registry-specific helper should override those from the default store. + for registryHostname := range configFile.CredentialHelpers { + newAuth, err := configFile.GetAuthConfig(registryHostname) + if err != nil { + return nil, err + } + auths[registryHostname] = newAuth + } + return auths, nil +} + +// GetFilename returns the file name that this config file is based on. +func (configFile *ConfigFile) GetFilename() string { + return configFile.Filename +} + +// PluginConfig retrieves the requested option for the given plugin. +func (configFile *ConfigFile) PluginConfig(pluginname, option string) (string, bool) { + if configFile.Plugins == nil { + return "", false + } + pluginConfig, ok := configFile.Plugins[pluginname] + if !ok { + return "", false + } + value, ok := pluginConfig[option] + return value, ok +} + +// SetPluginConfig sets the option to the given value for the given +// plugin. Passing a value of "" will remove the option. If removing +// the final config item for a given plugin then also cleans up the +// overall plugin entry. +func (configFile *ConfigFile) SetPluginConfig(pluginname, option, value string) { + if configFile.Plugins == nil { + configFile.Plugins = make(map[string]map[string]string) + } + pluginConfig, ok := configFile.Plugins[pluginname] + if !ok { + pluginConfig = make(map[string]string) + configFile.Plugins[pluginname] = pluginConfig + } + if value != "" { + pluginConfig[option] = value + } else { + delete(pluginConfig, option) + } + if len(pluginConfig) == 0 { + delete(configFile.Plugins, pluginname) + } +} + +func checkKubernetesConfiguration(kubeConfig *KubernetesConfig) error { + if kubeConfig == nil { + return nil + } + switch kubeConfig.AllNamespaces { + case "": + case "enabled": + case "disabled": + default: + return fmt.Errorf("invalid 'kubernetes.allNamespaces' value, should be 'enabled' or 'disabled': %s", kubeConfig.AllNamespaces) + } + return nil +} diff --git a/vendor/github.com/docker/cli/cli/config/configfile/file_unix.go b/vendor/github.com/docker/cli/cli/config/configfile/file_unix.go new file mode 100644 index 0000000000..3ca65c6140 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/config/configfile/file_unix.go @@ -0,0 +1,35 @@ +// +build !windows + +package configfile + +import ( + "os" + "syscall" +) + +// copyFilePermissions copies file ownership and permissions from "src" to "dst", +// ignoring any error during the process. +func copyFilePermissions(src, dst string) { + var ( + mode os.FileMode = 0600 + uid, gid int + ) + + fi, err := os.Stat(src) + if err != nil { + return + } + if fi.Mode().IsRegular() { + mode = fi.Mode() + } + if err := os.Chmod(dst, mode); err != nil { + return + } + + uid = int(fi.Sys().(*syscall.Stat_t).Uid) + gid = int(fi.Sys().(*syscall.Stat_t).Gid) + + if uid > 0 && gid > 0 { + _ = os.Chown(dst, uid, gid) + } +} diff --git a/vendor/github.com/docker/cli/cli/config/configfile/file_windows.go b/vendor/github.com/docker/cli/cli/config/configfile/file_windows.go new file mode 100644 index 0000000000..42fffc39ad --- /dev/null +++ b/vendor/github.com/docker/cli/cli/config/configfile/file_windows.go @@ -0,0 +1,5 @@ +package configfile + +func copyFilePermissions(src, dst string) { + // TODO implement for Windows +} diff --git a/vendor/github.com/docker/cli/cli/config/credentials/credentials.go b/vendor/github.com/docker/cli/cli/config/credentials/credentials.go new file mode 100644 index 0000000000..28d58ec48d --- /dev/null +++ b/vendor/github.com/docker/cli/cli/config/credentials/credentials.go @@ -0,0 +1,17 @@ +package credentials + +import ( + "github.com/docker/cli/cli/config/types" +) + +// Store is the interface that any credentials store must implement. +type Store interface { + // Erase removes credentials from the store for a given server. + Erase(serverAddress string) error + // Get retrieves credentials from the store for a given server. + Get(serverAddress string) (types.AuthConfig, error) + // GetAll retrieves all the credentials from the store. + GetAll() (map[string]types.AuthConfig, error) + // Store saves credentials in the store. + Store(authConfig types.AuthConfig) error +} diff --git a/vendor/github.com/docker/cli/cli/config/credentials/default_store.go b/vendor/github.com/docker/cli/cli/config/credentials/default_store.go new file mode 100644 index 0000000000..402235bff0 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/config/credentials/default_store.go @@ -0,0 +1,21 @@ +package credentials + +import ( + exec "golang.org/x/sys/execabs" +) + +// DetectDefaultStore return the default credentials store for the platform if +// the store executable is available. +func DetectDefaultStore(store string) string { + platformDefault := defaultCredentialsStore() + + // user defined or no default for platform + if store != "" || platformDefault == "" { + return store + } + + if _, err := exec.LookPath(remoteCredentialsPrefix + platformDefault); err == nil { + return platformDefault + } + return "" +} diff --git a/vendor/github.com/docker/cli/cli/config/credentials/default_store_darwin.go b/vendor/github.com/docker/cli/cli/config/credentials/default_store_darwin.go new file mode 100644 index 0000000000..5d42dec622 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/config/credentials/default_store_darwin.go @@ -0,0 +1,5 @@ +package credentials + +func defaultCredentialsStore() string { + return "osxkeychain" +} diff --git a/vendor/github.com/docker/cli/cli/config/credentials/default_store_linux.go b/vendor/github.com/docker/cli/cli/config/credentials/default_store_linux.go new file mode 100644 index 0000000000..a9012c6d4a --- /dev/null +++ b/vendor/github.com/docker/cli/cli/config/credentials/default_store_linux.go @@ -0,0 +1,13 @@ +package credentials + +import ( + "os/exec" +) + +func defaultCredentialsStore() string { + if _, err := exec.LookPath("pass"); err == nil { + return "pass" + } + + return "secretservice" +} diff --git a/vendor/github.com/docker/cli/cli/config/credentials/default_store_unsupported.go b/vendor/github.com/docker/cli/cli/config/credentials/default_store_unsupported.go new file mode 100644 index 0000000000..3028168ac2 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/config/credentials/default_store_unsupported.go @@ -0,0 +1,7 @@ +// +build !windows,!darwin,!linux + +package credentials + +func defaultCredentialsStore() string { + return "" +} diff --git a/vendor/github.com/docker/cli/cli/config/credentials/default_store_windows.go b/vendor/github.com/docker/cli/cli/config/credentials/default_store_windows.go new file mode 100644 index 0000000000..bb799ca61b --- /dev/null +++ b/vendor/github.com/docker/cli/cli/config/credentials/default_store_windows.go @@ -0,0 +1,5 @@ +package credentials + +func defaultCredentialsStore() string { + return "wincred" +} diff --git a/vendor/github.com/docker/cli/cli/config/credentials/file_store.go b/vendor/github.com/docker/cli/cli/config/credentials/file_store.go new file mode 100644 index 0000000000..e509820b73 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/config/credentials/file_store.go @@ -0,0 +1,81 @@ +package credentials + +import ( + "strings" + + "github.com/docker/cli/cli/config/types" +) + +type store interface { + Save() error + GetAuthConfigs() map[string]types.AuthConfig + GetFilename() string +} + +// fileStore implements a credentials store using +// the docker configuration file to keep the credentials in plain text. +type fileStore struct { + file store +} + +// NewFileStore creates a new file credentials store. +func NewFileStore(file store) Store { + return &fileStore{file: file} +} + +// Erase removes the given credentials from the file store. +func (c *fileStore) Erase(serverAddress string) error { + delete(c.file.GetAuthConfigs(), serverAddress) + return c.file.Save() +} + +// Get retrieves credentials for a specific server from the file store. +func (c *fileStore) Get(serverAddress string) (types.AuthConfig, error) { + authConfig, ok := c.file.GetAuthConfigs()[serverAddress] + if !ok { + // Maybe they have a legacy config file, we will iterate the keys converting + // them to the new format and testing + for r, ac := range c.file.GetAuthConfigs() { + if serverAddress == ConvertToHostname(r) { + return ac, nil + } + } + + authConfig = types.AuthConfig{} + } + return authConfig, nil +} + +func (c *fileStore) GetAll() (map[string]types.AuthConfig, error) { + return c.file.GetAuthConfigs(), nil +} + +// Store saves the given credentials in the file store. +func (c *fileStore) Store(authConfig types.AuthConfig) error { + c.file.GetAuthConfigs()[authConfig.ServerAddress] = authConfig + return c.file.Save() +} + +func (c *fileStore) GetFilename() string { + return c.file.GetFilename() +} + +func (c *fileStore) IsFileStore() bool { + return true +} + +// ConvertToHostname converts a registry url which has http|https prepended +// to just an hostname. +// Copied from github.com/docker/docker/registry.ConvertToHostname to reduce dependencies. +func ConvertToHostname(url string) string { + stripped := url + if strings.HasPrefix(url, "http://") { + stripped = strings.TrimPrefix(url, "http://") + } else if strings.HasPrefix(url, "https://") { + stripped = strings.TrimPrefix(url, "https://") + } + + nameParts := strings.SplitN(stripped, "/", 2) + + return nameParts[0] +} diff --git a/vendor/github.com/docker/cli/cli/config/credentials/native_store.go b/vendor/github.com/docker/cli/cli/config/credentials/native_store.go new file mode 100644 index 0000000000..afe542cc3c --- /dev/null +++ b/vendor/github.com/docker/cli/cli/config/credentials/native_store.go @@ -0,0 +1,143 @@ +package credentials + +import ( + "github.com/docker/cli/cli/config/types" + "github.com/docker/docker-credential-helpers/client" + "github.com/docker/docker-credential-helpers/credentials" +) + +const ( + remoteCredentialsPrefix = "docker-credential-" + tokenUsername = "" +) + +// nativeStore implements a credentials store +// using native keychain to keep credentials secure. +// It piggybacks into a file store to keep users' emails. +type nativeStore struct { + programFunc client.ProgramFunc + fileStore Store +} + +// NewNativeStore creates a new native store that +// uses a remote helper program to manage credentials. +func NewNativeStore(file store, helperSuffix string) Store { + name := remoteCredentialsPrefix + helperSuffix + return &nativeStore{ + programFunc: client.NewShellProgramFunc(name), + fileStore: NewFileStore(file), + } +} + +// Erase removes the given credentials from the native store. +func (c *nativeStore) Erase(serverAddress string) error { + if err := client.Erase(c.programFunc, serverAddress); err != nil { + return err + } + + // Fallback to plain text store to remove email + return c.fileStore.Erase(serverAddress) +} + +// Get retrieves credentials for a specific server from the native store. +func (c *nativeStore) Get(serverAddress string) (types.AuthConfig, error) { + // load user email if it exist or an empty auth config. + auth, _ := c.fileStore.Get(serverAddress) + + creds, err := c.getCredentialsFromStore(serverAddress) + if err != nil { + return auth, err + } + auth.Username = creds.Username + auth.IdentityToken = creds.IdentityToken + auth.Password = creds.Password + + return auth, nil +} + +// GetAll retrieves all the credentials from the native store. +func (c *nativeStore) GetAll() (map[string]types.AuthConfig, error) { + auths, err := c.listCredentialsInStore() + if err != nil { + return nil, err + } + + // Emails are only stored in the file store. + // This call can be safely eliminated when emails are removed. + fileConfigs, _ := c.fileStore.GetAll() + + authConfigs := make(map[string]types.AuthConfig) + for registry := range auths { + creds, err := c.getCredentialsFromStore(registry) + if err != nil { + return nil, err + } + ac := fileConfigs[registry] // might contain Email + ac.Username = creds.Username + ac.Password = creds.Password + ac.IdentityToken = creds.IdentityToken + authConfigs[registry] = ac + } + + return authConfigs, nil +} + +// Store saves the given credentials in the file store. +func (c *nativeStore) Store(authConfig types.AuthConfig) error { + if err := c.storeCredentialsInStore(authConfig); err != nil { + return err + } + authConfig.Username = "" + authConfig.Password = "" + authConfig.IdentityToken = "" + + // Fallback to old credential in plain text to save only the email + return c.fileStore.Store(authConfig) +} + +// storeCredentialsInStore executes the command to store the credentials in the native store. +func (c *nativeStore) storeCredentialsInStore(config types.AuthConfig) error { + creds := &credentials.Credentials{ + ServerURL: config.ServerAddress, + Username: config.Username, + Secret: config.Password, + } + + if config.IdentityToken != "" { + creds.Username = tokenUsername + creds.Secret = config.IdentityToken + } + + return client.Store(c.programFunc, creds) +} + +// getCredentialsFromStore executes the command to get the credentials from the native store. +func (c *nativeStore) getCredentialsFromStore(serverAddress string) (types.AuthConfig, error) { + var ret types.AuthConfig + + creds, err := client.Get(c.programFunc, serverAddress) + if err != nil { + if credentials.IsErrCredentialsNotFound(err) { + // do not return an error if the credentials are not + // in the keychain. Let docker ask for new credentials. + return ret, nil + } + return ret, err + } + + if creds.Username == tokenUsername { + ret.IdentityToken = creds.Secret + } else { + ret.Password = creds.Secret + ret.Username = creds.Username + } + + ret.ServerAddress = serverAddress + return ret, nil +} + +// listCredentialsInStore returns a listing of stored credentials as a map of +// URL -> username. +func (c *nativeStore) listCredentialsInStore() (map[string]string, error) { + return client.List(c.programFunc) +} diff --git a/vendor/github.com/docker/cli/cli/config/types/authconfig.go b/vendor/github.com/docker/cli/cli/config/types/authconfig.go new file mode 100644 index 0000000000..056af6b842 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/config/types/authconfig.go @@ -0,0 +1,22 @@ +package types + +// AuthConfig contains authorization information for connecting to a Registry +type AuthConfig struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Auth string `json:"auth,omitempty"` + + // Email is an optional value associated with the username. + // This field is deprecated and will be removed in a later + // version of docker. + Email string `json:"email,omitempty"` + + ServerAddress string `json:"serveraddress,omitempty"` + + // IdentityToken is used to authenticate the user and get + // an access token for the registry. + IdentityToken string `json:"identitytoken,omitempty"` + + // RegistryToken is a bearer token to be sent to a registry + RegistryToken string `json:"registrytoken,omitempty"` +} diff --git a/vendor/github.com/docker/cli/cli/connhelper/commandconn/commandconn.go b/vendor/github.com/docker/cli/cli/connhelper/commandconn/commandconn.go new file mode 100644 index 0000000000..128da447b5 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/connhelper/commandconn/commandconn.go @@ -0,0 +1,283 @@ +// Package commandconn provides a net.Conn implementation that can be used for +// proxying (or emulating) stream via a custom command. +// +// For example, to provide an http.Client that can connect to a Docker daemon +// running in a Docker container ("DIND"): +// +// httpClient := &http.Client{ +// Transport: &http.Transport{ +// DialContext: func(ctx context.Context, _network, _addr string) (net.Conn, error) { +// return commandconn.New(ctx, "docker", "exec", "-it", containerID, "docker", "system", "dial-stdio") +// }, +// }, +// } +package commandconn + +import ( + "bytes" + "context" + "fmt" + "io" + "net" + "os" + "runtime" + "strings" + "sync" + "syscall" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + exec "golang.org/x/sys/execabs" +) + +// New returns net.Conn +func New(ctx context.Context, cmd string, args ...string) (net.Conn, error) { + var ( + c commandConn + err error + ) + c.cmd = exec.CommandContext(ctx, cmd, args...) + // we assume that args never contains sensitive information + logrus.Debugf("commandconn: starting %s with %v", cmd, args) + c.cmd.Env = os.Environ() + c.cmd.SysProcAttr = &syscall.SysProcAttr{} + setPdeathsig(c.cmd) + createSession(c.cmd) + c.stdin, err = c.cmd.StdinPipe() + if err != nil { + return nil, err + } + c.stdout, err = c.cmd.StdoutPipe() + if err != nil { + return nil, err + } + c.cmd.Stderr = &stderrWriter{ + stderrMu: &c.stderrMu, + stderr: &c.stderr, + debugPrefix: fmt.Sprintf("commandconn (%s):", cmd), + } + c.localAddr = dummyAddr{network: "dummy", s: "dummy-0"} + c.remoteAddr = dummyAddr{network: "dummy", s: "dummy-1"} + return &c, c.cmd.Start() +} + +// commandConn implements net.Conn +type commandConn struct { + cmd *exec.Cmd + cmdExited bool + cmdWaitErr error + cmdMutex sync.Mutex + stdin io.WriteCloser + stdout io.ReadCloser + stderrMu sync.Mutex + stderr bytes.Buffer + stdioClosedMu sync.Mutex // for stdinClosed and stdoutClosed + stdinClosed bool + stdoutClosed bool + localAddr net.Addr + remoteAddr net.Addr +} + +// killIfStdioClosed kills the cmd if both stdin and stdout are closed. +func (c *commandConn) killIfStdioClosed() error { + c.stdioClosedMu.Lock() + stdioClosed := c.stdoutClosed && c.stdinClosed + c.stdioClosedMu.Unlock() + if !stdioClosed { + return nil + } + return c.kill() +} + +// killAndWait tries sending SIGTERM to the process before sending SIGKILL. +func killAndWait(cmd *exec.Cmd) error { + var werr error + if runtime.GOOS != "windows" { + werrCh := make(chan error) + go func() { werrCh <- cmd.Wait() }() + cmd.Process.Signal(syscall.SIGTERM) + select { + case werr = <-werrCh: + case <-time.After(3 * time.Second): + cmd.Process.Kill() + werr = <-werrCh + } + } else { + cmd.Process.Kill() + werr = cmd.Wait() + } + return werr +} + +// kill returns nil if the command terminated, regardless to the exit status. +func (c *commandConn) kill() error { + var werr error + c.cmdMutex.Lock() + if c.cmdExited { + werr = c.cmdWaitErr + } else { + werr = killAndWait(c.cmd) + c.cmdWaitErr = werr + c.cmdExited = true + } + c.cmdMutex.Unlock() + if werr == nil { + return nil + } + wExitErr, ok := werr.(*exec.ExitError) + if ok { + if wExitErr.ProcessState.Exited() { + return nil + } + } + return errors.Wrapf(werr, "commandconn: failed to wait") +} + +func (c *commandConn) onEOF(eof error) error { + // when we got EOF, the command is going to be terminated + var werr error + c.cmdMutex.Lock() + if c.cmdExited { + werr = c.cmdWaitErr + } else { + werrCh := make(chan error) + go func() { werrCh <- c.cmd.Wait() }() + select { + case werr = <-werrCh: + c.cmdWaitErr = werr + c.cmdExited = true + case <-time.After(10 * time.Second): + c.cmdMutex.Unlock() + c.stderrMu.Lock() + stderr := c.stderr.String() + c.stderrMu.Unlock() + return errors.Errorf("command %v did not exit after %v: stderr=%q", c.cmd.Args, eof, stderr) + } + } + c.cmdMutex.Unlock() + if werr == nil { + return eof + } + c.stderrMu.Lock() + stderr := c.stderr.String() + c.stderrMu.Unlock() + return errors.Errorf("command %v has exited with %v, please make sure the URL is valid, and Docker 18.09 or later is installed on the remote host: stderr=%s", c.cmd.Args, werr, stderr) +} + +func ignorableCloseError(err error) bool { + errS := err.Error() + ss := []string{ + os.ErrClosed.Error(), + } + for _, s := range ss { + if strings.Contains(errS, s) { + return true + } + } + return false +} + +func (c *commandConn) CloseRead() error { + // NOTE: maybe already closed here + if err := c.stdout.Close(); err != nil && !ignorableCloseError(err) { + logrus.Warnf("commandConn.CloseRead: %v", err) + } + c.stdioClosedMu.Lock() + c.stdoutClosed = true + c.stdioClosedMu.Unlock() + if err := c.killIfStdioClosed(); err != nil { + logrus.Warnf("commandConn.CloseRead: %v", err) + } + return nil +} + +func (c *commandConn) Read(p []byte) (int, error) { + n, err := c.stdout.Read(p) + if err == io.EOF { + err = c.onEOF(err) + } + return n, err +} + +func (c *commandConn) CloseWrite() error { + // NOTE: maybe already closed here + if err := c.stdin.Close(); err != nil && !ignorableCloseError(err) { + logrus.Warnf("commandConn.CloseWrite: %v", err) + } + c.stdioClosedMu.Lock() + c.stdinClosed = true + c.stdioClosedMu.Unlock() + if err := c.killIfStdioClosed(); err != nil { + logrus.Warnf("commandConn.CloseWrite: %v", err) + } + return nil +} + +func (c *commandConn) Write(p []byte) (int, error) { + n, err := c.stdin.Write(p) + if err == io.EOF { + err = c.onEOF(err) + } + return n, err +} + +func (c *commandConn) Close() error { + var err error + if err = c.CloseRead(); err != nil { + logrus.Warnf("commandConn.Close: CloseRead: %v", err) + } + if err = c.CloseWrite(); err != nil { + logrus.Warnf("commandConn.Close: CloseWrite: %v", err) + } + return err +} + +func (c *commandConn) LocalAddr() net.Addr { + return c.localAddr +} +func (c *commandConn) RemoteAddr() net.Addr { + return c.remoteAddr +} +func (c *commandConn) SetDeadline(t time.Time) error { + logrus.Debugf("unimplemented call: SetDeadline(%v)", t) + return nil +} +func (c *commandConn) SetReadDeadline(t time.Time) error { + logrus.Debugf("unimplemented call: SetReadDeadline(%v)", t) + return nil +} +func (c *commandConn) SetWriteDeadline(t time.Time) error { + logrus.Debugf("unimplemented call: SetWriteDeadline(%v)", t) + return nil +} + +type dummyAddr struct { + network string + s string +} + +func (d dummyAddr) Network() string { + return d.network +} + +func (d dummyAddr) String() string { + return d.s +} + +type stderrWriter struct { + stderrMu *sync.Mutex + stderr *bytes.Buffer + debugPrefix string +} + +func (w *stderrWriter) Write(p []byte) (int, error) { + logrus.Debugf("%s%s", w.debugPrefix, string(p)) + w.stderrMu.Lock() + if w.stderr.Len() > 4096 { + w.stderr.Reset() + } + n, err := w.stderr.Write(p) + w.stderrMu.Unlock() + return n, err +} diff --git a/vendor/github.com/docker/cli/cli/connhelper/commandconn/pdeathsig_linux.go b/vendor/github.com/docker/cli/cli/connhelper/commandconn/pdeathsig_linux.go new file mode 100644 index 0000000000..1cdd788cc0 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/connhelper/commandconn/pdeathsig_linux.go @@ -0,0 +1,10 @@ +package commandconn + +import ( + "os/exec" + "syscall" +) + +func setPdeathsig(cmd *exec.Cmd) { + cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL +} diff --git a/vendor/github.com/docker/cli/cli/connhelper/commandconn/pdeathsig_nolinux.go b/vendor/github.com/docker/cli/cli/connhelper/commandconn/pdeathsig_nolinux.go new file mode 100644 index 0000000000..ab07166724 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/connhelper/commandconn/pdeathsig_nolinux.go @@ -0,0 +1,10 @@ +// +build !linux + +package commandconn + +import ( + "os/exec" +) + +func setPdeathsig(cmd *exec.Cmd) { +} diff --git a/vendor/github.com/docker/cli/cli/connhelper/commandconn/session_unix.go b/vendor/github.com/docker/cli/cli/connhelper/commandconn/session_unix.go new file mode 100644 index 0000000000..6448500d63 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/connhelper/commandconn/session_unix.go @@ -0,0 +1,13 @@ +// +build !windows + +package commandconn + +import ( + "os/exec" +) + +func createSession(cmd *exec.Cmd) { + // for supporting ssh connection helper with ProxyCommand + // https://github.com/docker/cli/issues/1707 + cmd.SysProcAttr.Setsid = true +} diff --git a/vendor/github.com/docker/cli/cli/connhelper/commandconn/session_windows.go b/vendor/github.com/docker/cli/cli/connhelper/commandconn/session_windows.go new file mode 100644 index 0000000000..b926074500 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/connhelper/commandconn/session_windows.go @@ -0,0 +1,8 @@ +package commandconn + +import ( + "os/exec" +) + +func createSession(cmd *exec.Cmd) { +} diff --git a/vendor/github.com/docker/cli/cli/connhelper/connhelper.go b/vendor/github.com/docker/cli/cli/connhelper/connhelper.go new file mode 100644 index 0000000000..9ac9d6744d --- /dev/null +++ b/vendor/github.com/docker/cli/cli/connhelper/connhelper.go @@ -0,0 +1,68 @@ +// Package connhelper provides helpers for connecting to a remote daemon host with custom logic. +package connhelper + +import ( + "context" + "net" + "net/url" + + "github.com/docker/cli/cli/connhelper/commandconn" + "github.com/docker/cli/cli/connhelper/ssh" + "github.com/pkg/errors" +) + +// ConnectionHelper allows to connect to a remote host with custom stream provider binary. +type ConnectionHelper struct { + Dialer func(ctx context.Context, network, addr string) (net.Conn, error) + Host string // dummy URL used for HTTP requests. e.g. "http://docker" +} + +// GetConnectionHelper returns Docker-specific connection helper for the given URL. +// GetConnectionHelper returns nil without error when no helper is registered for the scheme. +// +// ssh://@ URL requires Docker 18.09 or later on the remote host. +func GetConnectionHelper(daemonURL string) (*ConnectionHelper, error) { + return getConnectionHelper(daemonURL, nil) +} + +// GetConnectionHelperWithSSHOpts returns Docker-specific connection helper for +// the given URL, and accepts additional options for ssh connections. It returns +// nil without error when no helper is registered for the scheme. +// +// Requires Docker 18.09 or later on the remote host. +func GetConnectionHelperWithSSHOpts(daemonURL string, sshFlags []string) (*ConnectionHelper, error) { + return getConnectionHelper(daemonURL, sshFlags) +} + +func getConnectionHelper(daemonURL string, sshFlags []string) (*ConnectionHelper, error) { + u, err := url.Parse(daemonURL) + if err != nil { + return nil, err + } + switch scheme := u.Scheme; scheme { + case "ssh": + sp, err := ssh.ParseURL(daemonURL) + if err != nil { + return nil, errors.Wrap(err, "ssh host connection is not valid") + } + return &ConnectionHelper{ + Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) { + return commandconn.New(ctx, "ssh", append(sshFlags, sp.Args("docker", "system", "dial-stdio")...)...) + }, + Host: "http://docker.example.com", + }, nil + } + // Future version may support plugins via ~/.docker/config.json. e.g. "dind" + // See docker/cli#889 for the previous discussion. + return nil, err +} + +// GetCommandConnectionHelper returns Docker-specific connection helper constructed from an arbitrary command. +func GetCommandConnectionHelper(cmd string, flags ...string) (*ConnectionHelper, error) { + return &ConnectionHelper{ + Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) { + return commandconn.New(ctx, cmd, flags...) + }, + Host: "http://docker.example.com", + }, nil +} diff --git a/vendor/github.com/docker/cli/cli/connhelper/ssh/ssh.go b/vendor/github.com/docker/cli/cli/connhelper/ssh/ssh.go new file mode 100644 index 0000000000..bde01ae7f7 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/connhelper/ssh/ssh.go @@ -0,0 +1,64 @@ +// Package ssh provides the connection helper for ssh:// URL. +package ssh + +import ( + "net/url" + + "github.com/pkg/errors" +) + +// ParseURL parses URL +func ParseURL(daemonURL string) (*Spec, error) { + u, err := url.Parse(daemonURL) + if err != nil { + return nil, err + } + if u.Scheme != "ssh" { + return nil, errors.Errorf("expected scheme ssh, got %q", u.Scheme) + } + + var sp Spec + + if u.User != nil { + sp.User = u.User.Username() + if _, ok := u.User.Password(); ok { + return nil, errors.New("plain-text password is not supported") + } + } + sp.Host = u.Hostname() + if sp.Host == "" { + return nil, errors.Errorf("no host specified") + } + sp.Port = u.Port() + if u.Path != "" { + return nil, errors.Errorf("extra path after the host: %q", u.Path) + } + if u.RawQuery != "" { + return nil, errors.Errorf("extra query after the host: %q", u.RawQuery) + } + if u.Fragment != "" { + return nil, errors.Errorf("extra fragment after the host: %q", u.Fragment) + } + return &sp, err +} + +// Spec of SSH URL +type Spec struct { + User string + Host string + Port string +} + +// Args returns args except "ssh" itself combined with optional additional command args +func (sp *Spec) Args(add ...string) []string { + var args []string + if sp.User != "" { + args = append(args, "-l", sp.User) + } + if sp.Port != "" { + args = append(args, "-p", sp.Port) + } + args = append(args, "--", sp.Host) + args = append(args, add...) + return args +} diff --git a/vendor/github.com/docker/distribution/LICENSE b/vendor/github.com/docker/distribution/LICENSE new file mode 100644 index 0000000000..e06d208186 --- /dev/null +++ b/vendor/github.com/docker/distribution/LICENSE @@ -0,0 +1,202 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/vendor/github.com/docker/distribution/digestset/set.go b/vendor/github.com/docker/distribution/digestset/set.go new file mode 100644 index 0000000000..71327dca72 --- /dev/null +++ b/vendor/github.com/docker/distribution/digestset/set.go @@ -0,0 +1,247 @@ +package digestset + +import ( + "errors" + "sort" + "strings" + "sync" + + digest "github.com/opencontainers/go-digest" +) + +var ( + // ErrDigestNotFound is used when a matching digest + // could not be found in a set. + ErrDigestNotFound = errors.New("digest not found") + + // ErrDigestAmbiguous is used when multiple digests + // are found in a set. None of the matching digests + // should be considered valid matches. + ErrDigestAmbiguous = errors.New("ambiguous digest string") +) + +// Set is used to hold a unique set of digests which +// may be easily referenced by easily referenced by a string +// representation of the digest as well as short representation. +// The uniqueness of the short representation is based on other +// digests in the set. If digests are omitted from this set, +// collisions in a larger set may not be detected, therefore it +// is important to always do short representation lookups on +// the complete set of digests. To mitigate collisions, an +// appropriately long short code should be used. +type Set struct { + mutex sync.RWMutex + entries digestEntries +} + +// NewSet creates an empty set of digests +// which may have digests added. +func NewSet() *Set { + return &Set{ + entries: digestEntries{}, + } +} + +// checkShortMatch checks whether two digests match as either whole +// values or short values. This function does not test equality, +// rather whether the second value could match against the first +// value. +func checkShortMatch(alg digest.Algorithm, hex, shortAlg, shortHex string) bool { + if len(hex) == len(shortHex) { + if hex != shortHex { + return false + } + if len(shortAlg) > 0 && string(alg) != shortAlg { + return false + } + } else if !strings.HasPrefix(hex, shortHex) { + return false + } else if len(shortAlg) > 0 && string(alg) != shortAlg { + return false + } + return true +} + +// Lookup looks for a digest matching the given string representation. +// If no digests could be found ErrDigestNotFound will be returned +// with an empty digest value. If multiple matches are found +// ErrDigestAmbiguous will be returned with an empty digest value. +func (dst *Set) Lookup(d string) (digest.Digest, error) { + dst.mutex.RLock() + defer dst.mutex.RUnlock() + if len(dst.entries) == 0 { + return "", ErrDigestNotFound + } + var ( + searchFunc func(int) bool + alg digest.Algorithm + hex string + ) + dgst, err := digest.Parse(d) + if err == digest.ErrDigestInvalidFormat { + hex = d + searchFunc = func(i int) bool { + return dst.entries[i].val >= d + } + } else { + hex = dgst.Hex() + alg = dgst.Algorithm() + searchFunc = func(i int) bool { + if dst.entries[i].val == hex { + return dst.entries[i].alg >= alg + } + return dst.entries[i].val >= hex + } + } + idx := sort.Search(len(dst.entries), searchFunc) + if idx == len(dst.entries) || !checkShortMatch(dst.entries[idx].alg, dst.entries[idx].val, string(alg), hex) { + return "", ErrDigestNotFound + } + if dst.entries[idx].alg == alg && dst.entries[idx].val == hex { + return dst.entries[idx].digest, nil + } + if idx+1 < len(dst.entries) && checkShortMatch(dst.entries[idx+1].alg, dst.entries[idx+1].val, string(alg), hex) { + return "", ErrDigestAmbiguous + } + + return dst.entries[idx].digest, nil +} + +// Add adds the given digest to the set. An error will be returned +// if the given digest is invalid. If the digest already exists in the +// set, this operation will be a no-op. +func (dst *Set) Add(d digest.Digest) error { + if err := d.Validate(); err != nil { + return err + } + dst.mutex.Lock() + defer dst.mutex.Unlock() + entry := &digestEntry{alg: d.Algorithm(), val: d.Hex(), digest: d} + searchFunc := func(i int) bool { + if dst.entries[i].val == entry.val { + return dst.entries[i].alg >= entry.alg + } + return dst.entries[i].val >= entry.val + } + idx := sort.Search(len(dst.entries), searchFunc) + if idx == len(dst.entries) { + dst.entries = append(dst.entries, entry) + return nil + } else if dst.entries[idx].digest == d { + return nil + } + + entries := append(dst.entries, nil) + copy(entries[idx+1:], entries[idx:len(entries)-1]) + entries[idx] = entry + dst.entries = entries + return nil +} + +// Remove removes the given digest from the set. An err will be +// returned if the given digest is invalid. If the digest does +// not exist in the set, this operation will be a no-op. +func (dst *Set) Remove(d digest.Digest) error { + if err := d.Validate(); err != nil { + return err + } + dst.mutex.Lock() + defer dst.mutex.Unlock() + entry := &digestEntry{alg: d.Algorithm(), val: d.Hex(), digest: d} + searchFunc := func(i int) bool { + if dst.entries[i].val == entry.val { + return dst.entries[i].alg >= entry.alg + } + return dst.entries[i].val >= entry.val + } + idx := sort.Search(len(dst.entries), searchFunc) + // Not found if idx is after or value at idx is not digest + if idx == len(dst.entries) || dst.entries[idx].digest != d { + return nil + } + + entries := dst.entries + copy(entries[idx:], entries[idx+1:]) + entries = entries[:len(entries)-1] + dst.entries = entries + + return nil +} + +// All returns all the digests in the set +func (dst *Set) All() []digest.Digest { + dst.mutex.RLock() + defer dst.mutex.RUnlock() + retValues := make([]digest.Digest, len(dst.entries)) + for i := range dst.entries { + retValues[i] = dst.entries[i].digest + } + + return retValues +} + +// ShortCodeTable returns a map of Digest to unique short codes. The +// length represents the minimum value, the maximum length may be the +// entire value of digest if uniqueness cannot be achieved without the +// full value. This function will attempt to make short codes as short +// as possible to be unique. +func ShortCodeTable(dst *Set, length int) map[digest.Digest]string { + dst.mutex.RLock() + defer dst.mutex.RUnlock() + m := make(map[digest.Digest]string, len(dst.entries)) + l := length + resetIdx := 0 + for i := 0; i < len(dst.entries); i++ { + var short string + extended := true + for extended { + extended = false + if len(dst.entries[i].val) <= l { + short = dst.entries[i].digest.String() + } else { + short = dst.entries[i].val[:l] + for j := i + 1; j < len(dst.entries); j++ { + if checkShortMatch(dst.entries[j].alg, dst.entries[j].val, "", short) { + if j > resetIdx { + resetIdx = j + } + extended = true + } else { + break + } + } + if extended { + l++ + } + } + } + m[dst.entries[i].digest] = short + if i >= resetIdx { + l = length + } + } + return m +} + +type digestEntry struct { + alg digest.Algorithm + val string + digest digest.Digest +} + +type digestEntries []*digestEntry + +func (d digestEntries) Len() int { + return len(d) +} + +func (d digestEntries) Less(i, j int) bool { + if d[i].val != d[j].val { + return d[i].val < d[j].val + } + return d[i].alg < d[j].alg +} + +func (d digestEntries) Swap(i, j int) { + d[i], d[j] = d[j], d[i] +} diff --git a/vendor/github.com/docker/distribution/reference/helpers.go b/vendor/github.com/docker/distribution/reference/helpers.go new file mode 100644 index 0000000000..978df7eabb --- /dev/null +++ b/vendor/github.com/docker/distribution/reference/helpers.go @@ -0,0 +1,42 @@ +package reference + +import "path" + +// IsNameOnly returns true if reference only contains a repo name. +func IsNameOnly(ref Named) bool { + if _, ok := ref.(NamedTagged); ok { + return false + } + if _, ok := ref.(Canonical); ok { + return false + } + return true +} + +// FamiliarName returns the familiar name string +// for the given named, familiarizing if needed. +func FamiliarName(ref Named) string { + if nn, ok := ref.(normalizedNamed); ok { + return nn.Familiar().Name() + } + return ref.Name() +} + +// FamiliarString returns the familiar string representation +// for the given reference, familiarizing if needed. +func FamiliarString(ref Reference) string { + if nn, ok := ref.(normalizedNamed); ok { + return nn.Familiar().String() + } + return ref.String() +} + +// FamiliarMatch reports whether ref matches the specified pattern. +// See https://godoc.org/path#Match for supported patterns. +func FamiliarMatch(pattern string, ref Reference) (bool, error) { + matched, err := path.Match(pattern, FamiliarString(ref)) + if namedRef, isNamed := ref.(Named); isNamed && !matched { + matched, _ = path.Match(pattern, FamiliarName(namedRef)) + } + return matched, err +} diff --git a/vendor/github.com/docker/distribution/reference/normalize.go b/vendor/github.com/docker/distribution/reference/normalize.go new file mode 100644 index 0000000000..b3dfb7a6d7 --- /dev/null +++ b/vendor/github.com/docker/distribution/reference/normalize.go @@ -0,0 +1,199 @@ +package reference + +import ( + "errors" + "fmt" + "strings" + + "github.com/docker/distribution/digestset" + "github.com/opencontainers/go-digest" +) + +var ( + legacyDefaultDomain = "index.docker.io" + defaultDomain = "docker.io" + officialRepoName = "library" + defaultTag = "latest" +) + +// normalizedNamed represents a name which has been +// normalized and has a familiar form. A familiar name +// is what is used in Docker UI. An example normalized +// name is "docker.io/library/ubuntu" and corresponding +// familiar name of "ubuntu". +type normalizedNamed interface { + Named + Familiar() Named +} + +// ParseNormalizedNamed parses a string into a named reference +// transforming a familiar name from Docker UI to a fully +// qualified reference. If the value may be an identifier +// use ParseAnyReference. +func ParseNormalizedNamed(s string) (Named, error) { + if ok := anchoredIdentifierRegexp.MatchString(s); ok { + return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s) + } + domain, remainder := splitDockerDomain(s) + var remoteName string + if tagSep := strings.IndexRune(remainder, ':'); tagSep > -1 { + remoteName = remainder[:tagSep] + } else { + remoteName = remainder + } + if strings.ToLower(remoteName) != remoteName { + return nil, errors.New("invalid reference format: repository name must be lowercase") + } + + ref, err := Parse(domain + "/" + remainder) + if err != nil { + return nil, err + } + named, isNamed := ref.(Named) + if !isNamed { + return nil, fmt.Errorf("reference %s has no name", ref.String()) + } + return named, nil +} + +// ParseDockerRef normalizes the image reference following the docker convention. This is added +// mainly for backward compatibility. +// The reference returned can only be either tagged or digested. For reference contains both tag +// and digest, the function returns digested reference, e.g. docker.io/library/busybox:latest@ +// sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa will be returned as +// docker.io/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa. +func ParseDockerRef(ref string) (Named, error) { + named, err := ParseNormalizedNamed(ref) + if err != nil { + return nil, err + } + if _, ok := named.(NamedTagged); ok { + if canonical, ok := named.(Canonical); ok { + // The reference is both tagged and digested, only + // return digested. + newNamed, err := WithName(canonical.Name()) + if err != nil { + return nil, err + } + newCanonical, err := WithDigest(newNamed, canonical.Digest()) + if err != nil { + return nil, err + } + return newCanonical, nil + } + } + return TagNameOnly(named), nil +} + +// splitDockerDomain splits a repository name to domain and remotename string. +// If no valid domain is found, the default domain is used. Repository name +// needs to be already validated before. +func splitDockerDomain(name string) (domain, remainder string) { + i := strings.IndexRune(name, '/') + if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") { + domain, remainder = defaultDomain, name + } else { + domain, remainder = name[:i], name[i+1:] + } + if domain == legacyDefaultDomain { + domain = defaultDomain + } + if domain == defaultDomain && !strings.ContainsRune(remainder, '/') { + remainder = officialRepoName + "/" + remainder + } + return +} + +// familiarizeName returns a shortened version of the name familiar +// to to the Docker UI. Familiar names have the default domain +// "docker.io" and "library/" repository prefix removed. +// For example, "docker.io/library/redis" will have the familiar +// name "redis" and "docker.io/dmcgowan/myapp" will be "dmcgowan/myapp". +// Returns a familiarized named only reference. +func familiarizeName(named namedRepository) repository { + repo := repository{ + domain: named.Domain(), + path: named.Path(), + } + + if repo.domain == defaultDomain { + repo.domain = "" + // Handle official repositories which have the pattern "library/" + if split := strings.Split(repo.path, "/"); len(split) == 2 && split[0] == officialRepoName { + repo.path = split[1] + } + } + return repo +} + +func (r reference) Familiar() Named { + return reference{ + namedRepository: familiarizeName(r.namedRepository), + tag: r.tag, + digest: r.digest, + } +} + +func (r repository) Familiar() Named { + return familiarizeName(r) +} + +func (t taggedReference) Familiar() Named { + return taggedReference{ + namedRepository: familiarizeName(t.namedRepository), + tag: t.tag, + } +} + +func (c canonicalReference) Familiar() Named { + return canonicalReference{ + namedRepository: familiarizeName(c.namedRepository), + digest: c.digest, + } +} + +// TagNameOnly adds the default tag "latest" to a reference if it only has +// a repo name. +func TagNameOnly(ref Named) Named { + if IsNameOnly(ref) { + namedTagged, err := WithTag(ref, defaultTag) + if err != nil { + // Default tag must be valid, to create a NamedTagged + // type with non-validated input the WithTag function + // should be used instead + panic(err) + } + return namedTagged + } + return ref +} + +// ParseAnyReference parses a reference string as a possible identifier, +// full digest, or familiar name. +func ParseAnyReference(ref string) (Reference, error) { + if ok := anchoredIdentifierRegexp.MatchString(ref); ok { + return digestReference("sha256:" + ref), nil + } + if dgst, err := digest.Parse(ref); err == nil { + return digestReference(dgst), nil + } + + return ParseNormalizedNamed(ref) +} + +// ParseAnyReferenceWithSet parses a reference string as a possible short +// identifier to be matched in a digest set, a full digest, or familiar name. +func ParseAnyReferenceWithSet(ref string, ds *digestset.Set) (Reference, error) { + if ok := anchoredShortIdentifierRegexp.MatchString(ref); ok { + dgst, err := ds.Lookup(ref) + if err == nil { + return digestReference(dgst), nil + } + } else { + if dgst, err := digest.Parse(ref); err == nil { + return digestReference(dgst), nil + } + } + + return ParseNormalizedNamed(ref) +} diff --git a/vendor/github.com/docker/distribution/reference/reference.go b/vendor/github.com/docker/distribution/reference/reference.go new file mode 100644 index 0000000000..8c0c23b2fe --- /dev/null +++ b/vendor/github.com/docker/distribution/reference/reference.go @@ -0,0 +1,433 @@ +// Package reference provides a general type to represent any way of referencing images within the registry. +// Its main purpose is to abstract tags and digests (content-addressable hash). +// +// Grammar +// +// reference := name [ ":" tag ] [ "@" digest ] +// name := [domain '/'] path-component ['/' path-component]* +// domain := domain-component ['.' domain-component]* [':' port-number] +// domain-component := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/ +// port-number := /[0-9]+/ +// path-component := alpha-numeric [separator alpha-numeric]* +// alpha-numeric := /[a-z0-9]+/ +// separator := /[_.]|__|[-]*/ +// +// tag := /[\w][\w.-]{0,127}/ +// +// digest := digest-algorithm ":" digest-hex +// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]* +// digest-algorithm-separator := /[+.-_]/ +// digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/ +// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value +// +// identifier := /[a-f0-9]{64}/ +// short-identifier := /[a-f0-9]{6,64}/ +package reference + +import ( + "errors" + "fmt" + "strings" + + "github.com/opencontainers/go-digest" +) + +const ( + // NameTotalLengthMax is the maximum total number of characters in a repository name. + NameTotalLengthMax = 255 +) + +var ( + // ErrReferenceInvalidFormat represents an error while trying to parse a string as a reference. + ErrReferenceInvalidFormat = errors.New("invalid reference format") + + // ErrTagInvalidFormat represents an error while trying to parse a string as a tag. + ErrTagInvalidFormat = errors.New("invalid tag format") + + // ErrDigestInvalidFormat represents an error while trying to parse a string as a tag. + ErrDigestInvalidFormat = errors.New("invalid digest format") + + // ErrNameContainsUppercase is returned for invalid repository names that contain uppercase characters. + ErrNameContainsUppercase = errors.New("repository name must be lowercase") + + // ErrNameEmpty is returned for empty, invalid repository names. + ErrNameEmpty = errors.New("repository name must have at least one component") + + // ErrNameTooLong is returned when a repository name is longer than NameTotalLengthMax. + ErrNameTooLong = fmt.Errorf("repository name must not be more than %v characters", NameTotalLengthMax) + + // ErrNameNotCanonical is returned when a name is not canonical. + ErrNameNotCanonical = errors.New("repository name must be canonical") +) + +// Reference is an opaque object reference identifier that may include +// modifiers such as a hostname, name, tag, and digest. +type Reference interface { + // String returns the full reference + String() string +} + +// Field provides a wrapper type for resolving correct reference types when +// working with encoding. +type Field struct { + reference Reference +} + +// AsField wraps a reference in a Field for encoding. +func AsField(reference Reference) Field { + return Field{reference} +} + +// Reference unwraps the reference type from the field to +// return the Reference object. This object should be +// of the appropriate type to further check for different +// reference types. +func (f Field) Reference() Reference { + return f.reference +} + +// MarshalText serializes the field to byte text which +// is the string of the reference. +func (f Field) MarshalText() (p []byte, err error) { + return []byte(f.reference.String()), nil +} + +// UnmarshalText parses text bytes by invoking the +// reference parser to ensure the appropriately +// typed reference object is wrapped by field. +func (f *Field) UnmarshalText(p []byte) error { + r, err := Parse(string(p)) + if err != nil { + return err + } + + f.reference = r + return nil +} + +// Named is an object with a full name +type Named interface { + Reference + Name() string +} + +// Tagged is an object which has a tag +type Tagged interface { + Reference + Tag() string +} + +// NamedTagged is an object including a name and tag. +type NamedTagged interface { + Named + Tag() string +} + +// Digested is an object which has a digest +// in which it can be referenced by +type Digested interface { + Reference + Digest() digest.Digest +} + +// Canonical reference is an object with a fully unique +// name including a name with domain and digest +type Canonical interface { + Named + Digest() digest.Digest +} + +// namedRepository is a reference to a repository with a name. +// A namedRepository has both domain and path components. +type namedRepository interface { + Named + Domain() string + Path() string +} + +// Domain returns the domain part of the Named reference +func Domain(named Named) string { + if r, ok := named.(namedRepository); ok { + return r.Domain() + } + domain, _ := splitDomain(named.Name()) + return domain +} + +// Path returns the name without the domain part of the Named reference +func Path(named Named) (name string) { + if r, ok := named.(namedRepository); ok { + return r.Path() + } + _, path := splitDomain(named.Name()) + return path +} + +func splitDomain(name string) (string, string) { + match := anchoredNameRegexp.FindStringSubmatch(name) + if len(match) != 3 { + return "", name + } + return match[1], match[2] +} + +// SplitHostname splits a named reference into a +// hostname and name string. If no valid hostname is +// found, the hostname is empty and the full value +// is returned as name +// DEPRECATED: Use Domain or Path +func SplitHostname(named Named) (string, string) { + if r, ok := named.(namedRepository); ok { + return r.Domain(), r.Path() + } + return splitDomain(named.Name()) +} + +// Parse parses s and returns a syntactically valid Reference. +// If an error was encountered it is returned, along with a nil Reference. +// NOTE: Parse will not handle short digests. +func Parse(s string) (Reference, error) { + matches := ReferenceRegexp.FindStringSubmatch(s) + if matches == nil { + if s == "" { + return nil, ErrNameEmpty + } + if ReferenceRegexp.FindStringSubmatch(strings.ToLower(s)) != nil { + return nil, ErrNameContainsUppercase + } + return nil, ErrReferenceInvalidFormat + } + + if len(matches[1]) > NameTotalLengthMax { + return nil, ErrNameTooLong + } + + var repo repository + + nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1]) + if len(nameMatch) == 3 { + repo.domain = nameMatch[1] + repo.path = nameMatch[2] + } else { + repo.domain = "" + repo.path = matches[1] + } + + ref := reference{ + namedRepository: repo, + tag: matches[2], + } + if matches[3] != "" { + var err error + ref.digest, err = digest.Parse(matches[3]) + if err != nil { + return nil, err + } + } + + r := getBestReferenceType(ref) + if r == nil { + return nil, ErrNameEmpty + } + + return r, nil +} + +// ParseNamed parses s and returns a syntactically valid reference implementing +// the Named interface. The reference must have a name and be in the canonical +// form, otherwise an error is returned. +// If an error was encountered it is returned, along with a nil Reference. +// NOTE: ParseNamed will not handle short digests. +func ParseNamed(s string) (Named, error) { + named, err := ParseNormalizedNamed(s) + if err != nil { + return nil, err + } + if named.String() != s { + return nil, ErrNameNotCanonical + } + return named, nil +} + +// WithName returns a named object representing the given string. If the input +// is invalid ErrReferenceInvalidFormat will be returned. +func WithName(name string) (Named, error) { + if len(name) > NameTotalLengthMax { + return nil, ErrNameTooLong + } + + match := anchoredNameRegexp.FindStringSubmatch(name) + if match == nil || len(match) != 3 { + return nil, ErrReferenceInvalidFormat + } + return repository{ + domain: match[1], + path: match[2], + }, nil +} + +// WithTag combines the name from "name" and the tag from "tag" to form a +// reference incorporating both the name and the tag. +func WithTag(name Named, tag string) (NamedTagged, error) { + if !anchoredTagRegexp.MatchString(tag) { + return nil, ErrTagInvalidFormat + } + var repo repository + if r, ok := name.(namedRepository); ok { + repo.domain = r.Domain() + repo.path = r.Path() + } else { + repo.path = name.Name() + } + if canonical, ok := name.(Canonical); ok { + return reference{ + namedRepository: repo, + tag: tag, + digest: canonical.Digest(), + }, nil + } + return taggedReference{ + namedRepository: repo, + tag: tag, + }, nil +} + +// WithDigest combines the name from "name" and the digest from "digest" to form +// a reference incorporating both the name and the digest. +func WithDigest(name Named, digest digest.Digest) (Canonical, error) { + if !anchoredDigestRegexp.MatchString(digest.String()) { + return nil, ErrDigestInvalidFormat + } + var repo repository + if r, ok := name.(namedRepository); ok { + repo.domain = r.Domain() + repo.path = r.Path() + } else { + repo.path = name.Name() + } + if tagged, ok := name.(Tagged); ok { + return reference{ + namedRepository: repo, + tag: tagged.Tag(), + digest: digest, + }, nil + } + return canonicalReference{ + namedRepository: repo, + digest: digest, + }, nil +} + +// TrimNamed removes any tag or digest from the named reference. +func TrimNamed(ref Named) Named { + domain, path := SplitHostname(ref) + return repository{ + domain: domain, + path: path, + } +} + +func getBestReferenceType(ref reference) Reference { + if ref.Name() == "" { + // Allow digest only references + if ref.digest != "" { + return digestReference(ref.digest) + } + return nil + } + if ref.tag == "" { + if ref.digest != "" { + return canonicalReference{ + namedRepository: ref.namedRepository, + digest: ref.digest, + } + } + return ref.namedRepository + } + if ref.digest == "" { + return taggedReference{ + namedRepository: ref.namedRepository, + tag: ref.tag, + } + } + + return ref +} + +type reference struct { + namedRepository + tag string + digest digest.Digest +} + +func (r reference) String() string { + return r.Name() + ":" + r.tag + "@" + r.digest.String() +} + +func (r reference) Tag() string { + return r.tag +} + +func (r reference) Digest() digest.Digest { + return r.digest +} + +type repository struct { + domain string + path string +} + +func (r repository) String() string { + return r.Name() +} + +func (r repository) Name() string { + if r.domain == "" { + return r.path + } + return r.domain + "/" + r.path +} + +func (r repository) Domain() string { + return r.domain +} + +func (r repository) Path() string { + return r.path +} + +type digestReference digest.Digest + +func (d digestReference) String() string { + return digest.Digest(d).String() +} + +func (d digestReference) Digest() digest.Digest { + return digest.Digest(d) +} + +type taggedReference struct { + namedRepository + tag string +} + +func (t taggedReference) String() string { + return t.Name() + ":" + t.tag +} + +func (t taggedReference) Tag() string { + return t.tag +} + +type canonicalReference struct { + namedRepository + digest digest.Digest +} + +func (c canonicalReference) String() string { + return c.Name() + "@" + c.digest.String() +} + +func (c canonicalReference) Digest() digest.Digest { + return c.digest +} diff --git a/vendor/github.com/docker/distribution/reference/regexp.go b/vendor/github.com/docker/distribution/reference/regexp.go new file mode 100644 index 0000000000..7860349320 --- /dev/null +++ b/vendor/github.com/docker/distribution/reference/regexp.go @@ -0,0 +1,143 @@ +package reference + +import "regexp" + +var ( + // alphaNumericRegexp defines the alpha numeric atom, typically a + // component of names. This only allows lower case characters and digits. + alphaNumericRegexp = match(`[a-z0-9]+`) + + // separatorRegexp defines the separators allowed to be embedded in name + // components. This allow one period, one or two underscore and multiple + // dashes. + separatorRegexp = match(`(?:[._]|__|[-]*)`) + + // nameComponentRegexp restricts registry path component names to start + // with at least one letter or number, with following parts able to be + // separated by one period, one or two underscore and multiple dashes. + nameComponentRegexp = expression( + alphaNumericRegexp, + optional(repeated(separatorRegexp, alphaNumericRegexp))) + + // domainComponentRegexp restricts the registry domain component of a + // repository name to start with a component as defined by DomainRegexp + // and followed by an optional port. + domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`) + + // DomainRegexp defines the structure of potential domain components + // that may be part of image names. This is purposely a subset of what is + // allowed by DNS to ensure backwards compatibility with Docker image + // names. + DomainRegexp = expression( + domainComponentRegexp, + optional(repeated(literal(`.`), domainComponentRegexp)), + optional(literal(`:`), match(`[0-9]+`))) + + // TagRegexp matches valid tag names. From docker/docker:graph/tags.go. + TagRegexp = match(`[\w][\w.-]{0,127}`) + + // anchoredTagRegexp matches valid tag names, anchored at the start and + // end of the matched string. + anchoredTagRegexp = anchored(TagRegexp) + + // DigestRegexp matches valid digests. + DigestRegexp = match(`[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`) + + // anchoredDigestRegexp matches valid digests, anchored at the start and + // end of the matched string. + anchoredDigestRegexp = anchored(DigestRegexp) + + // NameRegexp is the format for the name component of references. The + // regexp has capturing groups for the domain and name part omitting + // the separating forward slash from either. + NameRegexp = expression( + optional(DomainRegexp, literal(`/`)), + nameComponentRegexp, + optional(repeated(literal(`/`), nameComponentRegexp))) + + // anchoredNameRegexp is used to parse a name value, capturing the + // domain and trailing components. + anchoredNameRegexp = anchored( + optional(capture(DomainRegexp), literal(`/`)), + capture(nameComponentRegexp, + optional(repeated(literal(`/`), nameComponentRegexp)))) + + // ReferenceRegexp is the full supported format of a reference. The regexp + // is anchored and has capturing groups for name, tag, and digest + // components. + ReferenceRegexp = anchored(capture(NameRegexp), + optional(literal(":"), capture(TagRegexp)), + optional(literal("@"), capture(DigestRegexp))) + + // IdentifierRegexp is the format for string identifier used as a + // content addressable identifier using sha256. These identifiers + // are like digests without the algorithm, since sha256 is used. + IdentifierRegexp = match(`([a-f0-9]{64})`) + + // ShortIdentifierRegexp is the format used to represent a prefix + // of an identifier. A prefix may be used to match a sha256 identifier + // within a list of trusted identifiers. + ShortIdentifierRegexp = match(`([a-f0-9]{6,64})`) + + // anchoredIdentifierRegexp is used to check or match an + // identifier value, anchored at start and end of string. + anchoredIdentifierRegexp = anchored(IdentifierRegexp) + + // anchoredShortIdentifierRegexp is used to check if a value + // is a possible identifier prefix, anchored at start and end + // of string. + anchoredShortIdentifierRegexp = anchored(ShortIdentifierRegexp) +) + +// match compiles the string to a regular expression. +var match = regexp.MustCompile + +// literal compiles s into a literal regular expression, escaping any regexp +// reserved characters. +func literal(s string) *regexp.Regexp { + re := match(regexp.QuoteMeta(s)) + + if _, complete := re.LiteralPrefix(); !complete { + panic("must be a literal") + } + + return re +} + +// expression defines a full expression, where each regular expression must +// follow the previous. +func expression(res ...*regexp.Regexp) *regexp.Regexp { + var s string + for _, re := range res { + s += re.String() + } + + return match(s) +} + +// optional wraps the expression in a non-capturing group and makes the +// production optional. +func optional(res ...*regexp.Regexp) *regexp.Regexp { + return match(group(expression(res...)).String() + `?`) +} + +// repeated wraps the regexp in a non-capturing group to get one or more +// matches. +func repeated(res ...*regexp.Regexp) *regexp.Regexp { + return match(group(expression(res...)).String() + `+`) +} + +// group wraps the regexp in a non-capturing group. +func group(res ...*regexp.Regexp) *regexp.Regexp { + return match(`(?:` + expression(res...).String() + `)`) +} + +// capture wraps the expression in a capturing group. +func capture(res ...*regexp.Regexp) *regexp.Regexp { + return match(`(` + expression(res...).String() + `)`) +} + +// anchored anchors the regular expression by adding start and end delimiters. +func anchored(res ...*regexp.Regexp) *regexp.Regexp { + return match(`^` + expression(res...).String() + `$`) +} diff --git a/vendor/github.com/docker/distribution/registry/api/errcode/errors.go b/vendor/github.com/docker/distribution/registry/api/errcode/errors.go new file mode 100644 index 0000000000..4c35b879af --- /dev/null +++ b/vendor/github.com/docker/distribution/registry/api/errcode/errors.go @@ -0,0 +1,267 @@ +package errcode + +import ( + "encoding/json" + "fmt" + "strings" +) + +// ErrorCoder is the base interface for ErrorCode and Error allowing +// users of each to just call ErrorCode to get the real ID of each +type ErrorCoder interface { + ErrorCode() ErrorCode +} + +// ErrorCode represents the error type. The errors are serialized via strings +// and the integer format may change and should *never* be exported. +type ErrorCode int + +var _ error = ErrorCode(0) + +// ErrorCode just returns itself +func (ec ErrorCode) ErrorCode() ErrorCode { + return ec +} + +// Error returns the ID/Value +func (ec ErrorCode) Error() string { + // NOTE(stevvooe): Cannot use message here since it may have unpopulated args. + return strings.ToLower(strings.Replace(ec.String(), "_", " ", -1)) +} + +// Descriptor returns the descriptor for the error code. +func (ec ErrorCode) Descriptor() ErrorDescriptor { + d, ok := errorCodeToDescriptors[ec] + + if !ok { + return ErrorCodeUnknown.Descriptor() + } + + return d +} + +// String returns the canonical identifier for this error code. +func (ec ErrorCode) String() string { + return ec.Descriptor().Value +} + +// Message returned the human-readable error message for this error code. +func (ec ErrorCode) Message() string { + return ec.Descriptor().Message +} + +// MarshalText encodes the receiver into UTF-8-encoded text and returns the +// result. +func (ec ErrorCode) MarshalText() (text []byte, err error) { + return []byte(ec.String()), nil +} + +// UnmarshalText decodes the form generated by MarshalText. +func (ec *ErrorCode) UnmarshalText(text []byte) error { + desc, ok := idToDescriptors[string(text)] + + if !ok { + desc = ErrorCodeUnknown.Descriptor() + } + + *ec = desc.Code + + return nil +} + +// WithMessage creates a new Error struct based on the passed-in info and +// overrides the Message property. +func (ec ErrorCode) WithMessage(message string) Error { + return Error{ + Code: ec, + Message: message, + } +} + +// WithDetail creates a new Error struct based on the passed-in info and +// set the Detail property appropriately +func (ec ErrorCode) WithDetail(detail interface{}) Error { + return Error{ + Code: ec, + Message: ec.Message(), + }.WithDetail(detail) +} + +// WithArgs creates a new Error struct and sets the Args slice +func (ec ErrorCode) WithArgs(args ...interface{}) Error { + return Error{ + Code: ec, + Message: ec.Message(), + }.WithArgs(args...) +} + +// Error provides a wrapper around ErrorCode with extra Details provided. +type Error struct { + Code ErrorCode `json:"code"` + Message string `json:"message"` + Detail interface{} `json:"detail,omitempty"` + + // TODO(duglin): See if we need an "args" property so we can do the + // variable substitution right before showing the message to the user +} + +var _ error = Error{} + +// ErrorCode returns the ID/Value of this Error +func (e Error) ErrorCode() ErrorCode { + return e.Code +} + +// Error returns a human readable representation of the error. +func (e Error) Error() string { + return fmt.Sprintf("%s: %s", e.Code.Error(), e.Message) +} + +// WithDetail will return a new Error, based on the current one, but with +// some Detail info added +func (e Error) WithDetail(detail interface{}) Error { + return Error{ + Code: e.Code, + Message: e.Message, + Detail: detail, + } +} + +// WithArgs uses the passed-in list of interface{} as the substitution +// variables in the Error's Message string, but returns a new Error +func (e Error) WithArgs(args ...interface{}) Error { + return Error{ + Code: e.Code, + Message: fmt.Sprintf(e.Code.Message(), args...), + Detail: e.Detail, + } +} + +// ErrorDescriptor provides relevant information about a given error code. +type ErrorDescriptor struct { + // Code is the error code that this descriptor describes. + Code ErrorCode + + // Value provides a unique, string key, often captilized with + // underscores, to identify the error code. This value is used as the + // keyed value when serializing api errors. + Value string + + // Message is a short, human readable decription of the error condition + // included in API responses. + Message string + + // Description provides a complete account of the errors purpose, suitable + // for use in documentation. + Description string + + // HTTPStatusCode provides the http status code that is associated with + // this error condition. + HTTPStatusCode int +} + +// ParseErrorCode returns the value by the string error code. +// `ErrorCodeUnknown` will be returned if the error is not known. +func ParseErrorCode(value string) ErrorCode { + ed, ok := idToDescriptors[value] + if ok { + return ed.Code + } + + return ErrorCodeUnknown +} + +// Errors provides the envelope for multiple errors and a few sugar methods +// for use within the application. +type Errors []error + +var _ error = Errors{} + +func (errs Errors) Error() string { + switch len(errs) { + case 0: + return "" + case 1: + return errs[0].Error() + default: + msg := "errors:\n" + for _, err := range errs { + msg += err.Error() + "\n" + } + return msg + } +} + +// Len returns the current number of errors. +func (errs Errors) Len() int { + return len(errs) +} + +// MarshalJSON converts slice of error, ErrorCode or Error into a +// slice of Error - then serializes +func (errs Errors) MarshalJSON() ([]byte, error) { + var tmpErrs struct { + Errors []Error `json:"errors,omitempty"` + } + + for _, daErr := range errs { + var err Error + + switch daErr := daErr.(type) { + case ErrorCode: + err = daErr.WithDetail(nil) + case Error: + err = daErr + default: + err = ErrorCodeUnknown.WithDetail(daErr) + + } + + // If the Error struct was setup and they forgot to set the + // Message field (meaning its "") then grab it from the ErrCode + msg := err.Message + if msg == "" { + msg = err.Code.Message() + } + + tmpErrs.Errors = append(tmpErrs.Errors, Error{ + Code: err.Code, + Message: msg, + Detail: err.Detail, + }) + } + + return json.Marshal(tmpErrs) +} + +// UnmarshalJSON deserializes []Error and then converts it into slice of +// Error or ErrorCode +func (errs *Errors) UnmarshalJSON(data []byte) error { + var tmpErrs struct { + Errors []Error + } + + if err := json.Unmarshal(data, &tmpErrs); err != nil { + return err + } + + var newErrs Errors + for _, daErr := range tmpErrs.Errors { + // If Message is empty or exactly matches the Code's message string + // then just use the Code, no need for a full Error struct + if daErr.Detail == nil && (daErr.Message == "" || daErr.Message == daErr.Code.Message()) { + // Error's w/o details get converted to ErrorCode + newErrs = append(newErrs, daErr.Code) + } else { + // Error's w/ details are untouched + newErrs = append(newErrs, Error{ + Code: daErr.Code, + Message: daErr.Message, + Detail: daErr.Detail, + }) + } + } + + *errs = newErrs + return nil +} diff --git a/vendor/github.com/docker/distribution/registry/api/errcode/handler.go b/vendor/github.com/docker/distribution/registry/api/errcode/handler.go new file mode 100644 index 0000000000..d77e70473e --- /dev/null +++ b/vendor/github.com/docker/distribution/registry/api/errcode/handler.go @@ -0,0 +1,40 @@ +package errcode + +import ( + "encoding/json" + "net/http" +) + +// ServeJSON attempts to serve the errcode in a JSON envelope. It marshals err +// and sets the content-type header to 'application/json'. It will handle +// ErrorCoder and Errors, and if necessary will create an envelope. +func ServeJSON(w http.ResponseWriter, err error) error { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + var sc int + + switch errs := err.(type) { + case Errors: + if len(errs) < 1 { + break + } + + if err, ok := errs[0].(ErrorCoder); ok { + sc = err.ErrorCode().Descriptor().HTTPStatusCode + } + case ErrorCoder: + sc = errs.ErrorCode().Descriptor().HTTPStatusCode + err = Errors{err} // create an envelope. + default: + // We just have an unhandled error type, so just place in an envelope + // and move along. + err = Errors{err} + } + + if sc == 0 { + sc = http.StatusInternalServerError + } + + w.WriteHeader(sc) + + return json.NewEncoder(w).Encode(err) +} diff --git a/vendor/github.com/docker/distribution/registry/api/errcode/register.go b/vendor/github.com/docker/distribution/registry/api/errcode/register.go new file mode 100644 index 0000000000..d1e8826c6d --- /dev/null +++ b/vendor/github.com/docker/distribution/registry/api/errcode/register.go @@ -0,0 +1,138 @@ +package errcode + +import ( + "fmt" + "net/http" + "sort" + "sync" +) + +var ( + errorCodeToDescriptors = map[ErrorCode]ErrorDescriptor{} + idToDescriptors = map[string]ErrorDescriptor{} + groupToDescriptors = map[string][]ErrorDescriptor{} +) + +var ( + // ErrorCodeUnknown is a generic error that can be used as a last + // resort if there is no situation-specific error message that can be used + ErrorCodeUnknown = Register("errcode", ErrorDescriptor{ + Value: "UNKNOWN", + Message: "unknown error", + Description: `Generic error returned when the error does not have an + API classification.`, + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeUnsupported is returned when an operation is not supported. + ErrorCodeUnsupported = Register("errcode", ErrorDescriptor{ + Value: "UNSUPPORTED", + Message: "The operation is unsupported.", + Description: `The operation was unsupported due to a missing + implementation or invalid set of parameters.`, + HTTPStatusCode: http.StatusMethodNotAllowed, + }) + + // ErrorCodeUnauthorized is returned if a request requires + // authentication. + ErrorCodeUnauthorized = Register("errcode", ErrorDescriptor{ + Value: "UNAUTHORIZED", + Message: "authentication required", + Description: `The access controller was unable to authenticate + the client. Often this will be accompanied by a + Www-Authenticate HTTP response header indicating how to + authenticate.`, + HTTPStatusCode: http.StatusUnauthorized, + }) + + // ErrorCodeDenied is returned if a client does not have sufficient + // permission to perform an action. + ErrorCodeDenied = Register("errcode", ErrorDescriptor{ + Value: "DENIED", + Message: "requested access to the resource is denied", + Description: `The access controller denied access for the + operation on a resource.`, + HTTPStatusCode: http.StatusForbidden, + }) + + // ErrorCodeUnavailable provides a common error to report unavailability + // of a service or endpoint. + ErrorCodeUnavailable = Register("errcode", ErrorDescriptor{ + Value: "UNAVAILABLE", + Message: "service unavailable", + Description: "Returned when a service is not available", + HTTPStatusCode: http.StatusServiceUnavailable, + }) + + // ErrorCodeTooManyRequests is returned if a client attempts too many + // times to contact a service endpoint. + ErrorCodeTooManyRequests = Register("errcode", ErrorDescriptor{ + Value: "TOOMANYREQUESTS", + Message: "too many requests", + Description: `Returned when a client attempts to contact a + service too many times`, + HTTPStatusCode: http.StatusTooManyRequests, + }) +) + +var nextCode = 1000 +var registerLock sync.Mutex + +// Register will make the passed-in error known to the environment and +// return a new ErrorCode +func Register(group string, descriptor ErrorDescriptor) ErrorCode { + registerLock.Lock() + defer registerLock.Unlock() + + descriptor.Code = ErrorCode(nextCode) + + if _, ok := idToDescriptors[descriptor.Value]; ok { + panic(fmt.Sprintf("ErrorValue %q is already registered", descriptor.Value)) + } + if _, ok := errorCodeToDescriptors[descriptor.Code]; ok { + panic(fmt.Sprintf("ErrorCode %v is already registered", descriptor.Code)) + } + + groupToDescriptors[group] = append(groupToDescriptors[group], descriptor) + errorCodeToDescriptors[descriptor.Code] = descriptor + idToDescriptors[descriptor.Value] = descriptor + + nextCode++ + return descriptor.Code +} + +type byValue []ErrorDescriptor + +func (a byValue) Len() int { return len(a) } +func (a byValue) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a byValue) Less(i, j int) bool { return a[i].Value < a[j].Value } + +// GetGroupNames returns the list of Error group names that are registered +func GetGroupNames() []string { + keys := []string{} + + for k := range groupToDescriptors { + keys = append(keys, k) + } + sort.Strings(keys) + return keys +} + +// GetErrorCodeGroup returns the named group of error descriptors +func GetErrorCodeGroup(name string) []ErrorDescriptor { + desc := groupToDescriptors[name] + sort.Sort(byValue(desc)) + return desc +} + +// GetErrorAllDescriptors returns a slice of all ErrorDescriptors that are +// registered, irrespective of what group they're in +func GetErrorAllDescriptors() []ErrorDescriptor { + result := []ErrorDescriptor{} + + for _, group := range GetGroupNames() { + result = append(result, GetErrorCodeGroup(group)...) + } + sort.Sort(byValue(result)) + return result +} diff --git a/vendor/github.com/docker/distribution/registry/client/auth/challenge/addr.go b/vendor/github.com/docker/distribution/registry/client/auth/challenge/addr.go new file mode 100644 index 0000000000..2c3ebe1653 --- /dev/null +++ b/vendor/github.com/docker/distribution/registry/client/auth/challenge/addr.go @@ -0,0 +1,27 @@ +package challenge + +import ( + "net/url" + "strings" +) + +// FROM: https://golang.org/src/net/http/http.go +// Given a string of the form "host", "host:port", or "[ipv6::address]:port", +// return true if the string includes a port. +func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") } + +// FROM: http://golang.org/src/net/http/transport.go +var portMap = map[string]string{ + "http": "80", + "https": "443", +} + +// canonicalAddr returns url.Host but always with a ":port" suffix +// FROM: http://golang.org/src/net/http/transport.go +func canonicalAddr(url *url.URL) string { + addr := url.Host + if !hasPort(addr) { + return addr + ":" + portMap[url.Scheme] + } + return addr +} diff --git a/vendor/github.com/docker/distribution/registry/client/auth/challenge/authchallenge.go b/vendor/github.com/docker/distribution/registry/client/auth/challenge/authchallenge.go new file mode 100644 index 0000000000..fe238210cd --- /dev/null +++ b/vendor/github.com/docker/distribution/registry/client/auth/challenge/authchallenge.go @@ -0,0 +1,237 @@ +package challenge + +import ( + "fmt" + "net/http" + "net/url" + "strings" + "sync" +) + +// Challenge carries information from a WWW-Authenticate response header. +// See RFC 2617. +type Challenge struct { + // Scheme is the auth-scheme according to RFC 2617 + Scheme string + + // Parameters are the auth-params according to RFC 2617 + Parameters map[string]string +} + +// Manager manages the challenges for endpoints. +// The challenges are pulled out of HTTP responses. Only +// responses which expect challenges should be added to +// the manager, since a non-unauthorized request will be +// viewed as not requiring challenges. +type Manager interface { + // GetChallenges returns the challenges for the given + // endpoint URL. + GetChallenges(endpoint url.URL) ([]Challenge, error) + + // AddResponse adds the response to the challenge + // manager. The challenges will be parsed out of + // the WWW-Authenicate headers and added to the + // URL which was produced the response. If the + // response was authorized, any challenges for the + // endpoint will be cleared. + AddResponse(resp *http.Response) error +} + +// NewSimpleManager returns an instance of +// Manger which only maps endpoints to challenges +// based on the responses which have been added the +// manager. The simple manager will make no attempt to +// perform requests on the endpoints or cache the responses +// to a backend. +func NewSimpleManager() Manager { + return &simpleManager{ + Challenges: make(map[string][]Challenge), + } +} + +type simpleManager struct { + sync.RWMutex + Challenges map[string][]Challenge +} + +func normalizeURL(endpoint *url.URL) { + endpoint.Host = strings.ToLower(endpoint.Host) + endpoint.Host = canonicalAddr(endpoint) +} + +func (m *simpleManager) GetChallenges(endpoint url.URL) ([]Challenge, error) { + normalizeURL(&endpoint) + + m.RLock() + defer m.RUnlock() + challenges := m.Challenges[endpoint.String()] + return challenges, nil +} + +func (m *simpleManager) AddResponse(resp *http.Response) error { + challenges := ResponseChallenges(resp) + if resp.Request == nil { + return fmt.Errorf("missing request reference") + } + urlCopy := url.URL{ + Path: resp.Request.URL.Path, + Host: resp.Request.URL.Host, + Scheme: resp.Request.URL.Scheme, + } + normalizeURL(&urlCopy) + + m.Lock() + defer m.Unlock() + m.Challenges[urlCopy.String()] = challenges + return nil +} + +// Octet types from RFC 2616. +type octetType byte + +var octetTypes [256]octetType + +const ( + isToken octetType = 1 << iota + isSpace +) + +func init() { + // OCTET = + // CHAR = + // CTL = + // CR = + // LF = + // SP = + // HT = + // <"> = + // CRLF = CR LF + // LWS = [CRLF] 1*( SP | HT ) + // TEXT = + // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> + // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT + // token = 1* + // qdtext = > + + for c := 0; c < 256; c++ { + var t octetType + isCtl := c <= 31 || c == 127 + isChar := 0 <= c && c <= 127 + isSeparator := strings.ContainsRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) + if strings.ContainsRune(" \t\r\n", rune(c)) { + t |= isSpace + } + if isChar && !isCtl && !isSeparator { + t |= isToken + } + octetTypes[c] = t + } +} + +// ResponseChallenges returns a list of authorization challenges +// for the given http Response. Challenges are only checked if +// the response status code was a 401. +func ResponseChallenges(resp *http.Response) []Challenge { + if resp.StatusCode == http.StatusUnauthorized { + // Parse the WWW-Authenticate Header and store the challenges + // on this endpoint object. + return parseAuthHeader(resp.Header) + } + + return nil +} + +func parseAuthHeader(header http.Header) []Challenge { + challenges := []Challenge{} + for _, h := range header[http.CanonicalHeaderKey("WWW-Authenticate")] { + v, p := parseValueAndParams(h) + if v != "" { + challenges = append(challenges, Challenge{Scheme: v, Parameters: p}) + } + } + return challenges +} + +func parseValueAndParams(header string) (value string, params map[string]string) { + params = make(map[string]string) + value, s := expectToken(header) + if value == "" { + return + } + value = strings.ToLower(value) + s = "," + skipSpace(s) + for strings.HasPrefix(s, ",") { + var pkey string + pkey, s = expectToken(skipSpace(s[1:])) + if pkey == "" { + return + } + if !strings.HasPrefix(s, "=") { + return + } + var pvalue string + pvalue, s = expectTokenOrQuoted(s[1:]) + if pvalue == "" { + return + } + pkey = strings.ToLower(pkey) + params[pkey] = pvalue + s = skipSpace(s) + } + return +} + +func skipSpace(s string) (rest string) { + i := 0 + for ; i < len(s); i++ { + if octetTypes[s[i]]&isSpace == 0 { + break + } + } + return s[i:] +} + +func expectToken(s string) (token, rest string) { + i := 0 + for ; i < len(s); i++ { + if octetTypes[s[i]]&isToken == 0 { + break + } + } + return s[:i], s[i:] +} + +func expectTokenOrQuoted(s string) (value string, rest string) { + if !strings.HasPrefix(s, "\"") { + return expectToken(s) + } + s = s[1:] + for i := 0; i < len(s); i++ { + switch s[i] { + case '"': + return s[:i], s[i+1:] + case '\\': + p := make([]byte, len(s)-1) + j := copy(p, s[:i]) + escape := true + for i = i + 1; i < len(s); i++ { + b := s[i] + switch { + case escape: + escape = false + p[j] = b + j++ + case b == '\\': + escape = true + case b == '"': + return string(p[:j]), s[i+1:] + default: + p[j] = b + j++ + } + } + return "", "" + } + } + return "", "" +} diff --git a/vendor/github.com/docker/docker-credential-helpers/LICENSE b/vendor/github.com/docker/docker-credential-helpers/LICENSE new file mode 100644 index 0000000000..1ea555e2af --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2016 David Calavera + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/docker/docker-credential-helpers/client/client.go b/vendor/github.com/docker/docker-credential-helpers/client/client.go new file mode 100644 index 0000000000..d1d0434cb5 --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/client/client.go @@ -0,0 +1,121 @@ +package client + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + + "github.com/docker/docker-credential-helpers/credentials" +) + +// isValidCredsMessage checks if 'msg' contains invalid credentials error message. +// It returns whether the logs are free of invalid credentials errors and the error if it isn't. +// error values can be errCredentialsMissingServerURL or errCredentialsMissingUsername. +func isValidCredsMessage(msg string) error { + if credentials.IsCredentialsMissingServerURLMessage(msg) { + return credentials.NewErrCredentialsMissingServerURL() + } + + if credentials.IsCredentialsMissingUsernameMessage(msg) { + return credentials.NewErrCredentialsMissingUsername() + } + + return nil +} + +// Store uses an external program to save credentials. +func Store(program ProgramFunc, creds *credentials.Credentials) error { + cmd := program("store") + + buffer := new(bytes.Buffer) + if err := json.NewEncoder(buffer).Encode(creds); err != nil { + return err + } + cmd.Input(buffer) + + out, err := cmd.Output() + if err != nil { + t := strings.TrimSpace(string(out)) + + if isValidErr := isValidCredsMessage(t); isValidErr != nil { + err = isValidErr + } + + return fmt.Errorf("error storing credentials - err: %v, out: `%s`", err, t) + } + + return nil +} + +// Get executes an external program to get the credentials from a native store. +func Get(program ProgramFunc, serverURL string) (*credentials.Credentials, error) { + cmd := program("get") + cmd.Input(strings.NewReader(serverURL)) + + out, err := cmd.Output() + if err != nil { + t := strings.TrimSpace(string(out)) + + if credentials.IsErrCredentialsNotFoundMessage(t) { + return nil, credentials.NewErrCredentialsNotFound() + } + + if isValidErr := isValidCredsMessage(t); isValidErr != nil { + err = isValidErr + } + + return nil, fmt.Errorf("error getting credentials - err: %v, out: `%s`", err, t) + } + + resp := &credentials.Credentials{ + ServerURL: serverURL, + } + + if err := json.NewDecoder(bytes.NewReader(out)).Decode(resp); err != nil { + return nil, err + } + + return resp, nil +} + +// Erase executes a program to remove the server credentials from the native store. +func Erase(program ProgramFunc, serverURL string) error { + cmd := program("erase") + cmd.Input(strings.NewReader(serverURL)) + out, err := cmd.Output() + if err != nil { + t := strings.TrimSpace(string(out)) + + if isValidErr := isValidCredsMessage(t); isValidErr != nil { + err = isValidErr + } + + return fmt.Errorf("error erasing credentials - err: %v, out: `%s`", err, t) + } + + return nil +} + +// List executes a program to list server credentials in the native store. +func List(program ProgramFunc) (map[string]string, error) { + cmd := program("list") + cmd.Input(strings.NewReader("unused")) + out, err := cmd.Output() + if err != nil { + t := strings.TrimSpace(string(out)) + + if isValidErr := isValidCredsMessage(t); isValidErr != nil { + err = isValidErr + } + + return nil, fmt.Errorf("error listing credentials - err: %v, out: `%s`", err, t) + } + + var resp map[string]string + if err = json.NewDecoder(bytes.NewReader(out)).Decode(&resp); err != nil { + return nil, err + } + + return resp, nil +} diff --git a/vendor/github.com/docker/docker-credential-helpers/client/command.go b/vendor/github.com/docker/docker-credential-helpers/client/command.go new file mode 100644 index 0000000000..0183c06393 --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/client/command.go @@ -0,0 +1,57 @@ +package client + +import ( + "fmt" + "io" + "os" + + exec "golang.org/x/sys/execabs" +) + +// Program is an interface to execute external programs. +type Program interface { + Output() ([]byte, error) + Input(in io.Reader) +} + +// ProgramFunc is a type of function that initializes programs based on arguments. +type ProgramFunc func(args ...string) Program + +// NewShellProgramFunc creates programs that are executed in a Shell. +func NewShellProgramFunc(name string) ProgramFunc { + return NewShellProgramFuncWithEnv(name, nil) +} + +// NewShellProgramFuncWithEnv creates programs that are executed in a Shell with environment variables +func NewShellProgramFuncWithEnv(name string, env *map[string]string) ProgramFunc { + return func(args ...string) Program { + return &Shell{cmd: createProgramCmdRedirectErr(name, args, env)} + } +} + +func createProgramCmdRedirectErr(commandName string, args []string, env *map[string]string) *exec.Cmd { + programCmd := exec.Command(commandName, args...) + programCmd.Env = os.Environ() + if env != nil { + for k, v := range *env { + programCmd.Env = append(programCmd.Env, fmt.Sprintf("%s=%s", k, v)) + } + } + programCmd.Stderr = os.Stderr + return programCmd +} + +// Shell invokes shell commands to talk with a remote credentials helper. +type Shell struct { + cmd *exec.Cmd +} + +// Output returns responses from the remote credentials helper. +func (s *Shell) Output() ([]byte, error) { + return s.cmd.Output() +} + +// Input sets the input to send to a remote credentials helper. +func (s *Shell) Input(in io.Reader) { + s.cmd.Stdin = in +} diff --git a/vendor/github.com/docker/docker-credential-helpers/credentials/credentials.go b/vendor/github.com/docker/docker-credential-helpers/credentials/credentials.go new file mode 100644 index 0000000000..da8b594e7f --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/credentials/credentials.go @@ -0,0 +1,186 @@ +package credentials + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "io" + "os" + "strings" +) + +// Credentials holds the information shared between docker and the credentials store. +type Credentials struct { + ServerURL string + Username string + Secret string +} + +// isValid checks the integrity of Credentials object such that no credentials lack +// a server URL or a username. +// It returns whether the credentials are valid and the error if it isn't. +// error values can be errCredentialsMissingServerURL or errCredentialsMissingUsername +func (c *Credentials) isValid() (bool, error) { + if len(c.ServerURL) == 0 { + return false, NewErrCredentialsMissingServerURL() + } + + if len(c.Username) == 0 { + return false, NewErrCredentialsMissingUsername() + } + + return true, nil +} + +// CredsLabel holds the way Docker credentials should be labeled as such in credentials stores that allow labelling. +// That label allows to filter out non-Docker credentials too at lookup/search in macOS keychain, +// Windows credentials manager and Linux libsecret. Default value is "Docker Credentials" +var CredsLabel = "Docker Credentials" + +// SetCredsLabel is a simple setter for CredsLabel +func SetCredsLabel(label string) { + CredsLabel = label +} + +// Serve initializes the credentials helper and parses the action argument. +// This function is designed to be called from a command line interface. +// It uses os.Args[1] as the key for the action. +// It uses os.Stdin as input and os.Stdout as output. +// This function terminates the program with os.Exit(1) if there is an error. +func Serve(helper Helper) { + var err error + if len(os.Args) != 2 { + err = fmt.Errorf("Usage: %s ", os.Args[0]) + } + + if err == nil { + err = HandleCommand(helper, os.Args[1], os.Stdin, os.Stdout) + } + + if err != nil { + fmt.Fprintf(os.Stdout, "%v\n", err) + os.Exit(1) + } +} + +// HandleCommand uses a helper and a key to run a credential action. +func HandleCommand(helper Helper, key string, in io.Reader, out io.Writer) error { + switch key { + case "store": + return Store(helper, in) + case "get": + return Get(helper, in, out) + case "erase": + return Erase(helper, in) + case "list": + return List(helper, out) + case "version": + return PrintVersion(out) + } + return fmt.Errorf("Unknown credential action `%s`", key) +} + +// Store uses a helper and an input reader to save credentials. +// The reader must contain the JSON serialization of a Credentials struct. +func Store(helper Helper, reader io.Reader) error { + scanner := bufio.NewScanner(reader) + + buffer := new(bytes.Buffer) + for scanner.Scan() { + buffer.Write(scanner.Bytes()) + } + + if err := scanner.Err(); err != nil && err != io.EOF { + return err + } + + var creds Credentials + if err := json.NewDecoder(buffer).Decode(&creds); err != nil { + return err + } + + if ok, err := creds.isValid(); !ok { + return err + } + + return helper.Add(&creds) +} + +// Get retrieves the credentials for a given server url. +// The reader must contain the server URL to search. +// The writer is used to write the JSON serialization of the credentials. +func Get(helper Helper, reader io.Reader, writer io.Writer) error { + scanner := bufio.NewScanner(reader) + + buffer := new(bytes.Buffer) + for scanner.Scan() { + buffer.Write(scanner.Bytes()) + } + + if err := scanner.Err(); err != nil && err != io.EOF { + return err + } + + serverURL := strings.TrimSpace(buffer.String()) + if len(serverURL) == 0 { + return NewErrCredentialsMissingServerURL() + } + + username, secret, err := helper.Get(serverURL) + if err != nil { + return err + } + + resp := Credentials{ + ServerURL: serverURL, + Username: username, + Secret: secret, + } + + buffer.Reset() + if err := json.NewEncoder(buffer).Encode(resp); err != nil { + return err + } + + fmt.Fprint(writer, buffer.String()) + return nil +} + +// Erase removes credentials from the store. +// The reader must contain the server URL to remove. +func Erase(helper Helper, reader io.Reader) error { + scanner := bufio.NewScanner(reader) + + buffer := new(bytes.Buffer) + for scanner.Scan() { + buffer.Write(scanner.Bytes()) + } + + if err := scanner.Err(); err != nil && err != io.EOF { + return err + } + + serverURL := strings.TrimSpace(buffer.String()) + if len(serverURL) == 0 { + return NewErrCredentialsMissingServerURL() + } + + return helper.Delete(serverURL) +} + +//List returns all the serverURLs of keys in +//the OS store as a list of strings +func List(helper Helper, writer io.Writer) error { + accts, err := helper.List() + if err != nil { + return err + } + return json.NewEncoder(writer).Encode(accts) +} + +//PrintVersion outputs the current version. +func PrintVersion(writer io.Writer) error { + fmt.Fprintln(writer, Version) + return nil +} diff --git a/vendor/github.com/docker/docker-credential-helpers/credentials/error.go b/vendor/github.com/docker/docker-credential-helpers/credentials/error.go new file mode 100644 index 0000000000..fe6a5aef45 --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/credentials/error.go @@ -0,0 +1,102 @@ +package credentials + +const ( + // ErrCredentialsNotFound standardizes the not found error, so every helper returns + // the same message and docker can handle it properly. + errCredentialsNotFoundMessage = "credentials not found in native keychain" + + // ErrCredentialsMissingServerURL and ErrCredentialsMissingUsername standardize + // invalid credentials or credentials management operations + errCredentialsMissingServerURLMessage = "no credentials server URL" + errCredentialsMissingUsernameMessage = "no credentials username" +) + +// errCredentialsNotFound represents an error +// raised when credentials are not in the store. +type errCredentialsNotFound struct{} + +// Error returns the standard error message +// for when the credentials are not in the store. +func (errCredentialsNotFound) Error() string { + return errCredentialsNotFoundMessage +} + +// NewErrCredentialsNotFound creates a new error +// for when the credentials are not in the store. +func NewErrCredentialsNotFound() error { + return errCredentialsNotFound{} +} + +// IsErrCredentialsNotFound returns true if the error +// was caused by not having a set of credentials in a store. +func IsErrCredentialsNotFound(err error) bool { + _, ok := err.(errCredentialsNotFound) + return ok +} + +// IsErrCredentialsNotFoundMessage returns true if the error +// was caused by not having a set of credentials in a store. +// +// This function helps to check messages returned by an +// external program via its standard output. +func IsErrCredentialsNotFoundMessage(err string) bool { + return err == errCredentialsNotFoundMessage +} + +// errCredentialsMissingServerURL represents an error raised +// when the credentials object has no server URL or when no +// server URL is provided to a credentials operation requiring +// one. +type errCredentialsMissingServerURL struct{} + +func (errCredentialsMissingServerURL) Error() string { + return errCredentialsMissingServerURLMessage +} + +// errCredentialsMissingUsername represents an error raised +// when the credentials object has no username or when no +// username is provided to a credentials operation requiring +// one. +type errCredentialsMissingUsername struct{} + +func (errCredentialsMissingUsername) Error() string { + return errCredentialsMissingUsernameMessage +} + +// NewErrCredentialsMissingServerURL creates a new error for +// errCredentialsMissingServerURL. +func NewErrCredentialsMissingServerURL() error { + return errCredentialsMissingServerURL{} +} + +// NewErrCredentialsMissingUsername creates a new error for +// errCredentialsMissingUsername. +func NewErrCredentialsMissingUsername() error { + return errCredentialsMissingUsername{} +} + +// IsCredentialsMissingServerURL returns true if the error +// was an errCredentialsMissingServerURL. +func IsCredentialsMissingServerURL(err error) bool { + _, ok := err.(errCredentialsMissingServerURL) + return ok +} + +// IsCredentialsMissingServerURLMessage checks for an +// errCredentialsMissingServerURL in the error message. +func IsCredentialsMissingServerURLMessage(err string) bool { + return err == errCredentialsMissingServerURLMessage +} + +// IsCredentialsMissingUsername returns true if the error +// was an errCredentialsMissingUsername. +func IsCredentialsMissingUsername(err error) bool { + _, ok := err.(errCredentialsMissingUsername) + return ok +} + +// IsCredentialsMissingUsernameMessage checks for an +// errCredentialsMissingUsername in the error message. +func IsCredentialsMissingUsernameMessage(err string) bool { + return err == errCredentialsMissingUsernameMessage +} diff --git a/vendor/github.com/docker/docker-credential-helpers/credentials/helper.go b/vendor/github.com/docker/docker-credential-helpers/credentials/helper.go new file mode 100644 index 0000000000..135acd254d --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/credentials/helper.go @@ -0,0 +1,14 @@ +package credentials + +// Helper is the interface a credentials store helper must implement. +type Helper interface { + // Add appends credentials to the store. + Add(*Credentials) error + // Delete removes credentials from the store. + Delete(serverURL string) error + // Get retrieves credentials from the store. + // It returns username and secret as strings. + Get(serverURL string) (string, string, error) + // List returns the stored serverURLs and their associated usernames. + List() (map[string]string, error) +} diff --git a/vendor/github.com/docker/docker-credential-helpers/credentials/version.go b/vendor/github.com/docker/docker-credential-helpers/credentials/version.go new file mode 100644 index 0000000000..185e367961 --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/credentials/version.go @@ -0,0 +1,4 @@ +package credentials + +// Version holds a string describing the current version +const Version = "0.6.4" diff --git a/vendor/github.com/docker/docker/AUTHORS b/vendor/github.com/docker/docker/AUTHORS new file mode 100644 index 0000000000..dffacff112 --- /dev/null +++ b/vendor/github.com/docker/docker/AUTHORS @@ -0,0 +1,2175 @@ +# This file lists all individuals having contributed content to the repository. +# For how it is generated, see `hack/generate-authors.sh`. + +Aanand Prasad +Aaron Davidson +Aaron Feng +Aaron Hnatiw +Aaron Huslage +Aaron L. Xu +Aaron Lehmann +Aaron Welch +Aaron.L.Xu +Abel Muiño +Abhijeet Kasurde +Abhinandan Prativadi +Abhinav Ajgaonkar +Abhishek Chanda +Abhishek Sharma +Abin Shahab +Adam Avilla +Adam Dobrawy +Adam Eijdenberg +Adam Kunk +Adam Miller +Adam Mills +Adam Pointer +Adam Singer +Adam Walz +Addam Hardy +Aditi Rajagopal +Aditya +Adnan Khan +Adolfo Ochagavía +Adria Casas +Adrian Moisey +Adrian Mouat +Adrian Oprea +Adrien Folie +Adrien Gallouët +Ahmed Kamal +Ahmet Alp Balkan +Aidan Feldman +Aidan Hobson Sayers +AJ Bowen +Ajey Charantimath +ajneu +Akash Gupta +Akhil Mohan +Akihiro Matsushima +Akihiro Suda +Akim Demaille +Akira Koyasu +Akshay Karle +Al Tobey +alambike +Alan Hoyle +Alan Scherger +Alan Thompson +Albert Callarisa +Albert Zhang +Albin Kerouanton +Alejandro González Hevia +Aleksa Sarai +Aleksandrs Fadins +Alena Prokharchyk +Alessandro Boch +Alessio Biancalana +Alex Chan +Alex Chen +Alex Coventry +Alex Crawford +Alex Ellis +Alex Gaynor +Alex Goodman +Alex Olshansky +Alex Samorukov +Alex Warhawk +Alexander Artemenko +Alexander Boyd +Alexander Larsson +Alexander Midlash +Alexander Morozov +Alexander Shopov +Alexandre Beslic +Alexandre Garnier +Alexandre González +Alexandre Jomin +Alexandru Sfirlogea +Alexei Margasov +Alexey Guskov +Alexey Kotlyarov +Alexey Shamrin +Alexis THOMAS +Alfred Landrum +Ali Dehghani +Alicia Lauerman +Alihan Demir +Allen Madsen +Allen Sun +almoehi +Alvaro Saurin +Alvin Deng +Alvin Richards +amangoel +Amen Belayneh +Amir Goldstein +Amit Bakshi +Amit Krishnan +Amit Shukla +Amr Gawish +Amy Lindburg +Anand Patil +AnandkumarPatel +Anatoly Borodin +Anca Iordache +Anchal Agrawal +Anda Xu +Anders Janmyr +Andre Dublin <81dublin@gmail.com> +Andre Granovsky +Andrea Denisse Gómez +Andrea Luzzardi +Andrea Turli +Andreas Elvers +Andreas Köhler +Andreas Savvides +Andreas Tiefenthaler +Andrei Gherzan +Andrei Vagin +Andrew C. Bodine +Andrew Clay Shafer +Andrew Duckworth +Andrew France +Andrew Gerrand +Andrew Guenther +Andrew He +Andrew Hsu +Andrew Kuklewicz +Andrew Macgregor +Andrew Macpherson +Andrew Martin +Andrew McDonnell +Andrew Munsell +Andrew Pennebaker +Andrew Po +Andrew Weiss +Andrew Williams +Andrews Medina +Andrey Kolomentsev +Andrey Petrov +Andrey Stolbovsky +André Martins +andy +Andy Chambers +andy diller +Andy Goldstein +Andy Kipp +Andy Rothfusz +Andy Smith +Andy Wilson +Anes Hasicic +Anil Belur +Anil Madhavapeddy +Ankit Jain +Ankush Agarwal +Anonmily +Anran Qiao +Anshul Pundir +Anthon van der Neut +Anthony Baire +Anthony Bishopric +Anthony Dahanne +Anthony Sottile +Anton Löfgren +Anton Nikitin +Anton Polonskiy +Anton Tiurin +Antonio Murdaca +Antonis Kalipetis +Antony Messerli +Anuj Bahuguna +Anusha Ragunathan +apocas +Arash Deshmeh +ArikaChen +Arko Dasgupta +Arnaud Lefebvre +Arnaud Porterie +Arnaud Rebillout +Arthur Barr +Arthur Gautier +Artur Meyster +Arun Gupta +Asad Saeeduddin +Asbjørn Enge +averagehuman +Avi Das +Avi Kivity +Avi Miller +Avi Vaid +ayoshitake +Azat Khuyiyakhmetov +Bardia Keyoumarsi +Barnaby Gray +Barry Allard +Bartłomiej Piotrowski +Bastiaan Bakker +bdevloed +Ben Bonnefoy +Ben Firshman +Ben Golub +Ben Gould +Ben Hall +Ben Sargent +Ben Severson +Ben Toews +Ben Wiklund +Benjamin Atkin +Benjamin Baker +Benjamin Boudreau +Benjamin Yolken +Benny Ng +Benoit Chesneau +Bernerd Schaefer +Bernhard M. Wiedemann +Bert Goethals +Bertrand Roussel +Bevisy Zhang +Bharath Thiruveedula +Bhiraj Butala +Bhumika Bayani +Bilal Amarni +Bill Wang +Bily Zhang +Bin Liu +Bingshen Wang +Blake Geno +Boaz Shuster +bobby abbott +Boqin Qin +Boris Pruessmann +Boshi Lian +Bouke Haarsma +Boyd Hemphill +boynux +Bradley Cicenas +Bradley Wright +Brandon Liu +Brandon Philips +Brandon Rhodes +Brendan Dixon +Brent Salisbury +Brett Higgins +Brett Kochendorfer +Brett Randall +Brian (bex) Exelbierd +Brian Bland +Brian DeHamer +Brian Dorsey +Brian Flad +Brian Goff +Brian McCallister +Brian Olsen +Brian Schwind +Brian Shumate +Brian Torres-Gil +Brian Trump +Brice Jaglin +Briehan Lombaard +Brielle Broder +Bruno Bigras +Bruno Binet +Bruno Gazzera +Bruno Renié +Bruno Tavares +Bryan Bess +Bryan Boreham +Bryan Matsuo +Bryan Murphy +Burke Libbey +Byung Kang +Caleb Spare +Calen Pennington +Cameron Boehmer +Cameron Spear +Campbell Allen +Candid Dauth +Cao Weiwei +Carl Henrik Lunde +Carl Loa Odin +Carl X. Su +Carlo Mion +Carlos Alexandro Becker +Carlos de Paula +Carlos Sanchez +Carol Fager-Higgins +Cary +Casey Bisson +Catalin Pirvu +Ce Gao +Cedric Davies +Cezar Sa Espinola +Chad Swenson +Chance Zibolski +Chander Govindarajan +Chanhun Jeong +Chao Wang +Charles Chan +Charles Hooper +Charles Law +Charles Lindsay +Charles Merriam +Charles Sarrazin +Charles Smith +Charlie Drage +Charlie Lewis +Chase Bolt +ChaYoung You +Chen Chao +Chen Chuanliang +Chen Hanxiao +Chen Min +Chen Mingjie +Chen Qiu +Cheng-mean Liu +Chengfei Shang +Chengguang Xu +chenyuzhu +Chetan Birajdar +Chewey +Chia-liang Kao +chli +Cholerae Hu +Chris Alfonso +Chris Armstrong +Chris Dias +Chris Dituri +Chris Fordham +Chris Gavin +Chris Gibson +Chris Khoo +Chris McKinnel +Chris McKinnel +Chris Price +Chris Seto +Chris Snow +Chris St. Pierre +Chris Stivers +Chris Swan +Chris Telfer +Chris Wahl +Chris Weyl +Chris White +Christian Berendt +Christian Brauner +Christian Böhme +Christian Muehlhaeuser +Christian Persson +Christian Rotzoll +Christian Simon +Christian Stefanescu +Christophe Mehay +Christophe Troestler +Christophe Vidal +Christopher Biscardi +Christopher Crone +Christopher Currie +Christopher Jones +Christopher Latham +Christopher Rigor +Christy Norman +Chun Chen +Ciro S. Costa +Clayton Coleman +Clinton Kitson +Cody Roseborough +Coenraad Loubser +Colin Dunklau +Colin Hebert +Colin Panisset +Colin Rice +Colin Walters +Collin Guarino +Colm Hally +companycy +Corbin Coleman +Corey Farrell +Cory Forsyth +cressie176 +CrimsonGlory +Cristian Ariza +Cristian Staretu +cristiano balducci +Cristina Yenyxe Gonzalez Garcia +Cruceru Calin-Cristian +CUI Wei +Cyprian Gracz +Cyril F +Daan van Berkel +Daehyeok Mun +Dafydd Crosby +dalanlan +Damian Smyth +Damien Nadé +Damien Nozay +Damjan Georgievski +Dan Anolik +Dan Buch +Dan Cotora +Dan Feldman +Dan Griffin +Dan Hirsch +Dan Keder +Dan Levy +Dan McPherson +Dan Stine +Dan Williams +Dani Hodovic +Dani Louca +Daniel Antlinger +Daniel Black +Daniel Dao +Daniel Exner +Daniel Farrell +Daniel Garcia +Daniel Gasienica +Daniel Grunwell +Daniel Helfand +Daniel Hiltgen +Daniel J Walsh +Daniel Menet +Daniel Mizyrycki +Daniel Nephin +Daniel Norberg +Daniel Nordberg +Daniel Robinson +Daniel S +Daniel Sweet +Daniel Von Fange +Daniel Watkins +Daniel X Moore +Daniel YC Lin +Daniel Zhang +Danny Berger +Danny Milosavljevic +Danny Yates +Danyal Khaliq +Darren Coxall +Darren Shepherd +Darren Stahl +Dattatraya Kumbhar +Davanum Srinivas +Dave Barboza +Dave Goodchild +Dave Henderson +Dave MacDonald +Dave Tucker +David Anderson +David Calavera +David Chung +David Corking +David Cramer +David Currie +David Davis +David Dooling +David Gageot +David Gebler +David Glasser +David Lawrence +David Lechner +David M. Karr +David Mackey +David Mat +David Mcanulty +David McKay +David P Hilton +David Pelaez +David R. Jenni +David Röthlisberger +David Sheets +David Sissitka +David Trott +David Wang <00107082@163.com> +David Williamson +David Xia +David Young +Davide Ceretti +Dawn Chen +dbdd +dcylabs +Debayan De +Deborah Gertrude Digges +deed02392 +Deep Debroy +Deng Guangxing +Deni Bertovic +Denis Defreyne +Denis Gladkikh +Denis Ollier +Dennis Chen +Dennis Chen +Dennis Docter +Derek +Derek +Derek Ch +Derek McGowan +Deric Crago +Deshi Xiao +devmeyster +Devon Estes +Devvyn Murphy +Dharmit Shah +Dhawal Yogesh Bhanushali +Diego Romero +Diego Siqueira +Dieter Reuter +Dillon Dixon +Dima Stopel +Dimitri John Ledkov +Dimitris Mandalidis +Dimitris Rozakis +Dimitry Andric +Dinesh Subhraveti +Ding Fei +Diogo Monica +DiuDiugirl +Djibril Koné +dkumor +Dmitri Logvinenko +Dmitri Shuralyov +Dmitry Demeshchuk +Dmitry Gusev +Dmitry Kononenko +Dmitry Sharshakov +Dmitry Shyshkin +Dmitry Smirnov +Dmitry V. Krivenok +Dmitry Vorobev +Dolph Mathews +Dominic Tubach +Dominic Yin +Dominik Dingel +Dominik Finkbeiner +Dominik Honnef +Don Kirkby +Don Kjer +Don Spaulding +Donald Huang +Dong Chen +Donghwa Kim +Donovan Jones +Doron Podoleanu +Doug Davis +Doug MacEachern +Doug Tangren +Douglas Curtis +Dr Nic Williams +dragon788 +Dražen Lučanin +Drew Erny +Drew Hubl +Dustin Sallings +Ed Costello +Edmund Wagner +Eiichi Tsukata +Eike Herzbach +Eivin Giske Skaaren +Eivind Uggedal +Elan Ruusamäe +Elango Sivanandam +Elena Morozova +Eli Uriegas +Elias Faxö +Elias Probst +Elijah Zupancic +eluck +Elvir Kuric +Emil Davtyan +Emil Hernvall +Emily Maier +Emily Rose +Emir Ozer +Enguerran +Eohyung Lee +epeterso +Eric Barch +Eric Curtin +Eric G. Noriega +Eric Hanchrow +Eric Lee +Eric Myhre +Eric Paris +Eric Rafaloff +Eric Rosenberg +Eric Sage +Eric Soderstrom +Eric Yang +Eric-Olivier Lamey +Erica Windisch +Erik Bray +Erik Dubbelboer +Erik Hollensbe +Erik Inge Bolsø +Erik Kristensen +Erik St. Martin +Erik Weathers +Erno Hopearuoho +Erwin van der Koogh +Ethan Bell +Ethan Mosbaugh +Euan Kemp +Eugen Krizo +Eugene Yakubovich +Evan Allrich +Evan Carmi +Evan Hazlett +Evan Krall +Evan Phoenix +Evan Wies +Evelyn Xu +Everett Toews +Evgeniy Makhrov +Evgeny Shmarnev +Evgeny Vereshchagin +Ewa Czechowska +Eystein Måløy Stenberg +ezbercih +Ezra Silvera +Fabian Kramm +Fabian Lauer +Fabian Raetz +Fabiano Rosas +Fabio Falci +Fabio Kung +Fabio Rapposelli +Fabio Rehm +Fabrizio Regini +Fabrizio Soppelsa +Faiz Khan +falmp +Fangming Fang +Fangyuan Gao <21551127@zju.edu.cn> +fanjiyun +Fareed Dudhia +Fathi Boudra +Federico Gimenez +Felipe Oliveira +Felipe Ruhland +Felix Abecassis +Felix Geisendörfer +Felix Hupfeld +Felix Rabe +Felix Ruess +Felix Schindler +Feng Yan +Fengtu Wang +Ferenc Szabo +Fernando +Fero Volar +Ferran Rodenas +Filipe Brandenburger +Filipe Oliveira +Flavio Castelli +Flavio Crisciani +Florian +Florian Klein +Florian Maier +Florian Noeding +Florian Schmaus +Florian Weingarten +Florin Asavoaie +Florin Patan +fonglh +Foysal Iqbal +Francesc Campoy +Francesco Mari +Francis Chuang +Francisco Carriedo +Francisco Souza +Frank Groeneveld +Frank Herrmann +Frank Macreery +Frank Rosquin +frankyang +Fred Lifton +Frederick F. Kautz IV +Frederik Loeffert +Frederik Nordahl Jul Sabroe +Freek Kalter +Frieder Bluemle +Fu JinLin +Félix Baylac-Jacqué +Félix Cantournet +Gabe Rosenhouse +Gabor Nagy +Gabriel Linder +Gabriel Monroy +Gabriel Nicolas Avellaneda +Gaetan de Villele +Galen Sampson +Gang Qiao +Gareth Rushgrove +Garrett Barboza +Gary Schaetz +Gaurav +Gaurav Singh +Gaël PORTAY +Genki Takiuchi +GennadySpb +Geoffrey Bachelet +Geon Kim +George Kontridze +George MacRorie +George Xie +Georgi Hristozov +Gereon Frey +German DZ +Gert van Valkenhoef +Gerwim Feiken +Ghislain Bourgeois +Giampaolo Mancini +Gianluca Borello +Gildas Cuisinier +Giovan Isa Musthofa +gissehel +Giuseppe Mazzotta +Gleb Fotengauer-Malinovskiy +Gleb M Borisov +Glyn Normington +GoBella +Goffert van Gool +Goldwyn Rodrigues +Gopikannan Venugopalsamy +Gosuke Miyashita +Gou Rao +Govinda Fichtner +Grant Millar +Grant Reaber +Graydon Hoare +Greg Fausak +Greg Pflaum +Greg Stephens +Greg Thornton +Grzegorz Jaśkiewicz +Guilhem Lettron +Guilherme Salgado +Guillaume Dufour +Guillaume J. Charmes +guoxiuyan +Guri +Gurjeet Singh +Guruprasad +Gustav Sinder +gwx296173 +Günter Zöchbauer +Haichao Yang +haikuoliu +Hakan Özler +Hamish Hutchings +Hannes Ljungberg +Hans Kristian Flaatten +Hans Rødtang +Hao Shu Wei +Hao Zhang <21521210@zju.edu.cn> +Harald Albers +Harald Niesche +Harley Laue +Harold Cooper +Harrison Turton +Harry Zhang +Harshal Patil +Harshal Patil +He Simei +He Xiaoxi +He Xin +heartlock <21521209@zju.edu.cn> +Hector Castro +Helen Xie +Henning Sprang +Hiroshi Hatake +Hiroyuki Sasagawa +Hobofan +Hollie Teal +Hong Xu +Hongbin Lu +Hongxu Jia +Honza Pokorny +Hsing-Hui Hsu +hsinko <21551195@zju.edu.cn> +Hu Keping +Hu Tao +HuanHuan Ye +Huanzhong Zhang +Huayi Zhang +Hugo Duncan +Hugo Marisco <0x6875676f@gmail.com> +Hunter Blanks +huqun +Huu Nguyen +hyeongkyu.lee +Hyzhou Zhy +Iago López Galeiras +Ian Babrou +Ian Bishop +Ian Bull +Ian Calvert +Ian Campbell +Ian Chen +Ian Lee +Ian Main +Ian Philpot +Ian Truslove +Iavael +Icaro Seara +Ignacio Capurro +Igor Dolzhikov +Igor Karpovich +Iliana Weller +Ilkka Laukkanen +Ilya Dmitrichenko +Ilya Gusev +Ilya Khlopotov +imre Fitos +inglesp +Ingo Gottwald +Innovimax +Isaac Dupree +Isabel Jimenez +Isaiah Grace +Isao Jonas +Iskander Sharipov +Ivan Babrou +Ivan Fraixedes +Ivan Grcic +Ivan Markin +J Bruni +J. Nunn +Jack Danger Canty +Jack Laxson +Jacob Atzen +Jacob Edelman +Jacob Tomlinson +Jacob Vallejo +Jacob Wen +Jaime Cepeda +Jaivish Kothari +Jake Champlin +Jake Moshenko +Jake Sanders +jakedt +James Allen +James Carey +James Carr +James DeFelice +James Harrison Fisher +James Kyburz +James Kyle +James Lal +James Mills +James Nesbitt +James Nugent +James Turnbull +James Watkins-Harvey +Jamie Hannaford +Jamshid Afshar +Jan Chren +Jan Keromnes +Jan Koprowski +Jan Pazdziora +Jan Toebes +Jan-Gerd Tenberge +Jan-Jaap Driessen +Jana Radhakrishnan +Jannick Fahlbusch +Januar Wayong +Jared Biel +Jared Hocutt +Jaroslaw Zabiello +jaseg +Jasmine Hegman +Jason A. Donenfeld +Jason Divock +Jason Giedymin +Jason Green +Jason Hall +Jason Heiss +Jason Livesay +Jason McVetta +Jason Plum +Jason Shepherd +Jason Smith +Jason Sommer +Jason Stangroome +jaxgeller +Jay +Jay +Jay Kamat +Jean Rouge +Jean-Baptiste Barth +Jean-Baptiste Dalido +Jean-Christophe Berthon +Jean-Paul Calderone +Jean-Pierre Huynh +Jean-Tiare Le Bigot +Jeeva S. Chelladhurai +Jeff Anderson +Jeff Hajewski +Jeff Johnston +Jeff Lindsay +Jeff Mickey +Jeff Minard +Jeff Nickoloff +Jeff Silberman +Jeff Welch +Jeffrey Bolle +Jeffrey Morgan +Jeffrey van Gogh +Jenny Gebske +Jeremy Chambers +Jeremy Grosser +Jeremy Price +Jeremy Qian +Jeremy Unruh +Jeremy Yallop +Jeroen Franse +Jeroen Jacobs +Jesse Dearing +Jesse Dubay +Jessica Frazelle +Jezeniel Zapanta +Jhon Honce +Ji.Zhilong +Jian Liao +Jian Zhang +Jiang Jinyang +Jie Luo +Jie Ma +Jihyun Hwang +Jilles Oldenbeuving +Jim Alateras +Jim Ehrismann +Jim Galasyn +Jim Minter +Jim Perrin +Jimmy Cuadra +Jimmy Puckett +Jimmy Song +Jinsoo Park +Jintao Zhang +Jiri Appl +Jiri Popelka +Jiuyue Ma +Jiří Župka +Joao Fernandes +Joao Trindade +Joe Beda +Joe Doliner +Joe Ferguson +Joe Gordon +Joe Shaw +Joe Van Dyk +Joel Friedly +Joel Handwell +Joel Hansson +Joel Wurtz +Joey Geiger +Joey Geiger +Joey Gibson +Joffrey F +Johan Euphrosine +Johan Rydberg +Johanan Lieberman +Johannes 'fish' Ziemke +John Costa +John Feminella +John Gardiner Myers +John Gossman +John Harris +John Howard +John Laswell +John Maguire +John Mulhausen +John OBrien III +John Starks +John Stephens +John Tims +John V. Martinez +John Warwick +John Willis +Jon Johnson +Jon Surrell +Jon Wedaman +Jonas Dohse +Jonas Heinrich +Jonas Pfenniger +Jonathan A. Schweder +Jonathan A. Sternberg +Jonathan Boulle +Jonathan Camp +Jonathan Choy +Jonathan Dowland +Jonathan Lebon +Jonathan Lomas +Jonathan McCrohan +Jonathan Mueller +Jonathan Pares +Jonathan Rudenberg +Jonathan Stoppani +Jonh Wendell +Joni Sar +Joost Cassee +Jordan Arentsen +Jordan Jennings +Jordan Sissel +Jorge Marin +Jorit Kleine-Möllhoff +Jose Diaz-Gonzalez +Joseph Anthony Pasquale Holsten +Joseph Hager +Joseph Kern +Joseph Rothrock +Josh +Josh Bodah +Josh Bonczkowski +Josh Chorlton +Josh Eveleth +Josh Hawn +Josh Horwitz +Josh Poimboeuf +Josh Soref +Josh Wilson +Josiah Kiehl +José Tomás Albornoz +Joyce Jang +JP +Julian Taylor +Julien Barbier +Julien Bisconti +Julien Bordellier +Julien Dubois +Julien Kassar +Julien Maitrehenry +Julien Pervillé +Julien Pivotto +Julio Guerra +Julio Montes +Jun-Ru Chang +Jussi Nummelin +Justas Brazauskas +Justen Martin +Justin Cormack +Justin Force +Justin Menga +Justin Plock +Justin Simonelis +Justin Terry +Justyn Temme +Jyrki Puttonen +Jérémy Leherpeur +Jérôme Petazzoni +Jörg Thalheim +K. Heller +Kai Blin +Kai Qiang Wu (Kennan) +Kamil Domański +Kamjar Gerami +Kanstantsin Shautsou +Kara Alexandra +Karan Lyons +Kareem Khazem +kargakis +Karl Grzeszczak +Karol Duleba +Karthik Karanth +Karthik Nayak +Kasper Fabæch Brandt +Kate Heddleston +Katie McLaughlin +Kato Kazuyoshi +Katrina Owen +Kawsar Saiyeed +Kay Yan +kayrus +Kazuhiro Sera +Ke Li +Ke Xu +Kei Ohmura +Keith Hudgins +Keli Hu +Ken Cochrane +Ken Herner +Ken ICHIKAWA +Ken Reese +Kenfe-Mickaël Laventure +Kenjiro Nakayama +Kent Johnson +Kenta Tada +Kevin "qwazerty" Houdebert +Kevin Burke +Kevin Clark +Kevin Feyrer +Kevin J. Lynagh +Kevin Jing Qiu +Kevin Kern +Kevin Menard +Kevin Meredith +Kevin P. Kucharczyk +Kevin Parsons +Kevin Richardson +Kevin Shi +Kevin Wallace +Kevin Yap +Keyvan Fatehi +kies +Kim BKC Carlbacker +Kim Eik +Kimbro Staken +Kir Kolyshkin +Kiran Gangadharan +Kirill SIbirev +knappe +Kohei Tsuruta +Koichi Shiraishi +Konrad Kleine +Konstantin Gribov +Konstantin L +Konstantin Pelykh +Krasi Georgiev +Krasimir Georgiev +Kris-Mikael Krister +Kristian Haugene +Kristina Zabunova +Krystian Wojcicki +Kun Zhang +Kunal Kushwaha +Kunal Tyagi +Kyle Conroy +Kyle Linden +Kyle Wuolle +kyu +Lachlan Coote +Lai Jiangshan +Lajos Papp +Lakshan Perera +Lalatendu Mohanty +Lance Chen +Lance Kinley +Lars Butler +Lars Kellogg-Stedman +Lars R. Damerow +Lars-Magnus Skog +Laszlo Meszaros +Laura Frank +Laurent Erignoux +Laurie Voss +Leandro Siqueira +Lee Chao <932819864@qq.com> +Lee, Meng-Han +leeplay +Lei Gong +Lei Jitang +Len Weincier +Lennie +Leo Gallucci +Leszek Kowalski +Levi Blackstone +Levi Gross +Lewis Daly +Lewis Marshall +Lewis Peckover +Li Yi +Liam Macgillavry +Liana Lo +Liang Mingqiang +Liang-Chi Hsieh +Liao Qingwei +Lifubang +Lihua Tang +Lily Guo +limsy +Lin Lu +LingFaKe +Linus Heckemann +Liran Tal +Liron Levin +Liu Bo +Liu Hua +liwenqi +lixiaobing10051267 +Liz Zhang +LIZAO LI +Lizzie Dixon <_@lizzie.io> +Lloyd Dewolf +Lokesh Mandvekar +longliqiang88 <394564827@qq.com> +Lorenz Leutgeb +Lorenzo Fontana +Lotus Fenn +Louis Delossantos +Louis Opter +Luca Favatella +Luca Marturana +Luca Orlandi +Luca-Bogdan Grigorescu +Lucas Chan +Lucas Chi +Lucas Molas +Lucas Silvestre +Luciano Mores +Luis Martínez de Bartolomé Izquierdo +Luiz Svoboda +Lukas Heeren +Lukas Waslowski +lukaspustina +Lukasz Zajaczkowski +Luke Marsden +Lyn +Lynda O'Leary +Lénaïc Huard +Ma Müller +Ma Shimiao +Mabin +Madhan Raj Mookkandy +Madhav Puri +Madhu Venugopal +Mageee +Mahesh Tiyyagura +malnick +Malte Janduda +Manfred Touron +Manfred Zabarauskas +Manjunath A Kumatagi +Mansi Nahar +Manuel Meurer +Manuel Rüger +Manuel Woelker +mapk0y +Marc Abramowitz +Marc Kuo +Marc Tamsky +Marcel Edmund Franke +Marcelo Horacio Fortino +Marcelo Salazar +Marco Hennings +Marcus Cobden +Marcus Farkas +Marcus Linke +Marcus Martins +Marcus Ramberg +Marek Goldmann +Marian Marinov +Marianna Tessel +Mario Loriedo +Marius Gundersen +Marius Sturm +Marius Voila +Mark Allen +Mark Jeromin +Mark McGranaghan +Mark McKinstry +Mark Milstein +Mark Oates +Mark Parker +Mark West +Markan Patel +Marko Mikulicic +Marko Tibold +Markus Fix +Markus Kortlang +Martijn Dwars +Martijn van Oosterhout +Martin Honermeyer +Martin Kelly +Martin Mosegaard Amdisen +Martin Muzatko +Martin Redmond +Mary Anthony +Masahito Zembutsu +Masato Ohba +Masayuki Morita +Mason Malone +Mateusz Sulima +Mathias Monnerville +Mathieu Champlon +Mathieu Le Marec - Pasquet +Mathieu Parent +Matt Apperson +Matt Bachmann +Matt Bentley +Matt Haggard +Matt Hoyle +Matt McCormick +Matt Moore +Matt Richardson +Matt Rickard +Matt Robenolt +Matt Schurenko +Matt Williams +Matthew Heon +Matthew Lapworth +Matthew Mayer +Matthew Mosesohn +Matthew Mueller +Matthew Riley +Matthias Klumpp +Matthias Kühnle +Matthias Rampke +Matthieu Hauglustaine +Mattias Jernberg +Mauricio Garavaglia +mauriyouth +Max Harmathy +Max Shytikov +Maxim Fedchyshyn +Maxim Ivanov +Maxim Kulkin +Maxim Treskin +Maxime Petazzoni +Maximiliano Maccanti +Maxwell +Meaglith Ma +meejah +Megan Kostick +Mehul Kar +Mei ChunTao +Mengdi Gao +Mert Yazıcıoğlu +mgniu +Micah Zoltu +Michael A. Smith +Michael Bridgen +Michael Brown +Michael Chiang +Michael Crosby +Michael Currie +Michael Friis +Michael Gorsuch +Michael Grauer +Michael Holzheu +Michael Hudson-Doyle +Michael Huettermann +Michael Irwin +Michael Käufl +Michael Neale +Michael Nussbaum +Michael Prokop +Michael Scharf +Michael Spetsiotis +Michael Stapelberg +Michael Steinert +Michael Thies +Michael West +Michael Zhao +Michal Fojtik +Michal Gebauer +Michal Jemala +Michal Minář +Michal Wieczorek +Michaël Pailloncy +Michał Czeraszkiewicz +Michał Gryko +Michiel de Jong +Mickaël Fortunato +Mickaël Remars +Miguel Angel Fernández +Miguel Morales +Mihai Borobocea +Mihuleacc Sergiu +Mike Brown +Mike Bush +Mike Casas +Mike Chelen +Mike Danese +Mike Dillon +Mike Dougherty +Mike Estes +Mike Gaffney +Mike Goelzer +Mike Leone +Mike Lundy +Mike MacCana +Mike Naberezny +Mike Snitzer +mikelinjie <294893458@qq.com> +Mikhail Sobolev +Miklos Szegedi +Milind Chawre +Miloslav Trmač +mingqing +Mingzhen Feng +Misty Stanley-Jones +Mitch Capper +Mizuki Urushida +mlarcher +Mohammad Banikazemi +Mohammad Nasirifar +Mohammed Aaqib Ansari +Mohit Soni +Moorthy RS +Morgan Bauer +Morgante Pell +Morgy93 +Morten Siebuhr +Morton Fox +Moysés Borges +mrfly +Mrunal Patel +Muayyad Alsadi +Mustafa Akın +Muthukumar R +Máximo Cuadros +Médi-Rémi Hashim +Nace Oroz +Nahum Shalman +Nakul Pathak +Nalin Dahyabhai +Nan Monnand Deng +Naoki Orii +Natalie Parker +Natanael Copa +Natasha Jarus +Nate Brennand +Nate Eagleson +Nate Jones +Nathan Hsieh +Nathan Kleyn +Nathan LeClaire +Nathan McCauley +Nathan Williams +Naveed Jamil +Neal McBurnett +Neil Horman +Neil Peterson +Nelson Chen +Neyazul Haque +Nghia Tran +Niall O'Higgins +Nicholas E. Rabenau +Nick Adcock +Nick DeCoursin +Nick Irvine +Nick Neisen +Nick Parker +Nick Payne +Nick Russo +Nick Stenning +Nick Stinemates +NickrenREN +Nicola Kabar +Nicolas Borboën +Nicolas De Loof +Nicolas Dudebout +Nicolas Goy +Nicolas Kaiser +Nicolas Sterchele +Nicolas V Castet +Nicolás Hock Isaza +Nigel Poulton +Nik Nyby +Nikhil Chawla +NikolaMandic +Nikolas Garofil +Nikolay Edigaryev +Nikolay Milovanov +Nirmal Mehta +Nishant Totla +NIWA Hideyuki +Noah Meyerhans +Noah Treuhaft +NobodyOnSE +noducks +Nolan Darilek +Noriki Nakamura +nponeccop +Nuutti Kotivuori +nzwsch +O.S. Tezer +objectified +Odin Ugedal +Oguz Bilgic +Oh Jinkyun +Ohad Schneider +ohmystack +Ole Reifschneider +Oliver Neal +Oliver Reason +Olivier Gambier +Olle Jonsson +Olli Janatuinen +Olly Pomeroy +Omri Shiv +Oriol Francès +Oskar Niburski +Otto Kekäläinen +Ouyang Liduo +Ovidio Mallo +Panagiotis Moustafellos +Paolo G. Giarrusso +Pascal +Pascal Bach +Pascal Borreli +Pascal Hartig +Patrick Böänziger +Patrick Devine +Patrick Hemmer +Patrick Stapleton +Patrik Cyvoct +pattichen +Paul +paul +Paul Annesley +Paul Bellamy +Paul Bowsher +Paul Furtado +Paul Hammond +Paul Jimenez +Paul Kehrer +Paul Lietar +Paul Liljenberg +Paul Morie +Paul Nasrat +Paul Weaver +Paulo Ribeiro +Pavel Lobashov +Pavel Matěja +Pavel Pletenev +Pavel Pospisil +Pavel Sutyrin +Pavel Tikhomirov +Pavlos Ratis +Pavol Vargovcik +Pawel Konczalski +Peeyush Gupta +Peggy Li +Pei Su +Peng Tao +Penghan Wang +Per Weijnitz +perhapszzy@sina.com +Peter Bourgon +Peter Braden +Peter Bücker +Peter Choi +Peter Dave Hello +Peter Edge +Peter Ericson +Peter Esbensen +Peter Jaffe +Peter Kang +Peter Malmgren +Peter Salvatore +Peter Volpe +Peter Waller +Petr Švihlík +Phil +Phil Estes +Phil Spitler +Philip Alexander Etling +Philip Monroe +Philipp Gillé +Philipp Wahala +Philipp Weissensteiner +Phillip Alexander +phineas +pidster +Piergiuliano Bossi +Pierre +Pierre Carrier +Pierre Dal-Pra +Pierre Wacrenier +Pierre-Alain RIVIERE +Piotr Bogdan +pixelistik +Porjo +Poul Kjeldager Sørensen +Pradeep Chhetri +Pradip Dhara +Prasanna Gautam +Pratik Karki +Prayag Verma +Priya Wadhwa +Projjol Banerji +Przemek Hejman +Pure White +pysqz +Qiang Huang +Qinglan Peng +qudongfang +Quentin Brossard +Quentin Perez +Quentin Tayssier +r0n22 +Radostin Stoyanov +Rafal Jeczalik +Rafe Colton +Raghavendra K T +Raghuram Devarakonda +Raja Sami +Rajat Pandit +Rajdeep Dua +Ralf Sippl +Ralle +Ralph Bean +Ramkumar Ramachandra +Ramon Brooker +Ramon van Alteren +RaviTeja Pothana +Ray Tsang +ReadmeCritic +Recursive Madman +Reficul +Regan McCooey +Remi Rampin +Remy Suen +Renato Riccieri Santos Zannon +Renaud Gaubert +Rhys Hiltner +Ri Xu +Ricardo N Feliciano +Rich Moyse +Rich Seymour +Richard +Richard Burnison +Richard Harvey +Richard Mathie +Richard Metzler +Richard Scothern +Richo Healey +Rick Bradley +Rick van de Loo +Rick Wieman +Rik Nijessen +Riku Voipio +Riley Guerin +Ritesh H Shukla +Riyaz Faizullabhoy +Rob Gulewich +Rob Vesse +Robert Bachmann +Robert Bittle +Robert Obryk +Robert Schneider +Robert Stern +Robert Terhaar +Robert Wallis +Robert Wang +Roberto G. Hashioka +Roberto Muñoz Fernández +Robin Naundorf +Robin Schneider +Robin Speekenbrink +Robin Thoni +robpc +Rodolfo Carvalho +Rodrigo Vaz +Roel Van Nyen +Roger Peppe +Rohit Jnagal +Rohit Kadam +Rohit Kapur +Rojin George +Roland Huß +Roland Kammerer +Roland Moriz +Roma Sokolov +Roman Dudin +Roman Mazur +Roman Strashkin +Ron Smits +Ron Williams +Rong Gao +Rong Zhang +Rongxiang Song +root +root +root +root +Rory Hunter +Rory McCune +Ross Boucher +Rovanion Luckey +Royce Remer +Rozhnov Alexandr +Rudolph Gottesheim +Rui Cao +Rui Lopes +Ruilin Li +Runshen Zhu +Russ Magee +Ryan Abrams +Ryan Anderson +Ryan Aslett +Ryan Belgrave +Ryan Detzel +Ryan Fowler +Ryan Liu +Ryan McLaughlin +Ryan O'Donnell +Ryan Seto +Ryan Simmen +Ryan Stelly +Ryan Thomas +Ryan Trauntvein +Ryan Wallner +Ryan Zhang +ryancooper7 +RyanDeng +Ryo Nakao +Rémy Greinhofer +s. rannou +s00318865 +Sabin Basyal +Sachin Joshi +Sagar Hani +Sainath Grandhi +Sakeven Jiang +Salahuddin Khan +Sally O'Malley +Sam Abed +Sam Alba +Sam Bailey +Sam J Sharpe +Sam Neirinck +Sam Reis +Sam Rijs +Sam Whited +Sambuddha Basu +Sami Wagiaalla +Samuel Andaya +Samuel Dion-Girardeau +Samuel Karp +Samuel PHAN +Sandeep Bansal +Sankar சங்கர் +Sanket Saurav +Santhosh Manohar +sapphiredev +Sargun Dhillon +Sascha Andres +Sascha Grunert +SataQiu +Satnam Singh +Satoshi Amemiya +Satoshi Tagomori +Scott Bessler +Scott Collier +Scott Johnston +Scott Stamp +Scott Walls +sdreyesg +Sean Christopherson +Sean Cronin +Sean Lee +Sean McIntyre +Sean OMeara +Sean P. Kane +Sean Rodman +Sebastiaan van Steenis +Sebastiaan van Stijn +Senthil Kumar Selvaraj +Senthil Kumaran +SeongJae Park +Seongyeol Lim +Serge Hallyn +Sergey Alekseev +Sergey Evstifeev +Sergii Kabashniuk +Sergio Lopez +Serhat Gülçiçek +SeungUkLee +Sevki Hasirci +Shane Canon +Shane da Silva +Shaun Kaasten +shaunol +Shawn Landden +Shawn Siefkas +shawnhe +Shayne Wang +Shekhar Gulati +Sheng Yang +Shengbo Song +Shev Yan +Shih-Yuan Lee +Shijiang Wei +Shijun Qin +Shishir Mahajan +Shoubhik Bose +Shourya Sarcar +Shu-Wai Chow +shuai-z +Shukui Yang +Shuwei Hao +Sian Lerk Lau +Sidhartha Mani +sidharthamani +Silas Sewell +Silvan Jegen +Simão Reis +Simei He +Simon Barendse +Simon Eskildsen +Simon Ferquel +Simon Leinen +Simon Menke +Simon Taranto +Simon Vikstrom +Sindhu S +Sjoerd Langkemper +skanehira +Solganik Alexander +Solomon Hykes +Song Gao +Soshi Katsuta +Soulou +Spencer Brown +Spencer Smith +Sridatta Thatipamala +Sridhar Ratnakumar +Srini Brahmaroutu +Srinivasan Srivatsan +Staf Wagemakers +Stanislav Bondarenko +Stanislav Levin +Steeve Morin +Stefan Berger +Stefan J. Wernli +Stefan Praszalowicz +Stefan S. +Stefan Scherer +Stefan Staudenmeyer +Stefan Weil +Stephan Spindler +Stephen Benjamin +Stephen Crosby +Stephen Day +Stephen Drake +Stephen Rust +Steve Desmond +Steve Dougherty +Steve Durrheimer +Steve Francia +Steve Koch +Steven Burgess +Steven Erenst +Steven Hartland +Steven Iveson +Steven Merrill +Steven Richards +Steven Taylor +Stig Larsson +Subhajit Ghosh +Sujith Haridasan +Sun Gengze <690388648@qq.com> +Sun Jianbo +Sune Keller +Sunny Gogoi +Suryakumar Sudar +Sven Dowideit +Swapnil Daingade +Sylvain Baubeau +Sylvain Bellemare +Sébastien +Sébastien HOUZÉ +Sébastien Luttringer +Sébastien Stormacq +Tabakhase +Tadej Janež +TAGOMORI Satoshi +tang0th +Tangi Colin +Tatsuki Sugiura +Tatsushi Inagaki +Taylan Isikdemir +Taylor Jones +Ted M. Young +Tehmasp Chaudhri +Tejaswini Duggaraju +Tejesh Mehta +terryding77 <550147740@qq.com> +tgic +Thatcher Peskens +theadactyl +Thell 'Bo' Fowler +Thermionix +Thijs Terlouw +Thomas Bikeev +Thomas Frössman +Thomas Gazagnaire +Thomas Grainger +Thomas Hansen +Thomas Leonard +Thomas Léveil +Thomas Orozco +Thomas Riccardi +Thomas Schroeter +Thomas Sjögren +Thomas Swift +Thomas Tanaka +Thomas Texier +Ti Zhou +Tianon Gravi +Tianyi Wang +Tibor Vass +Tiffany Jernigan +Tiffany Low +Till Wegmüller +Tim +Tim Bart +Tim Bosse +Tim Dettrick +Tim Düsterhus +Tim Hockin +Tim Potter +Tim Ruffles +Tim Smith +Tim Terhorst +Tim Wang +Tim Waugh +Tim Wraight +Tim Zju <21651152@zju.edu.cn> +timfeirg +Timothy Hobbs +tjwebb123 +tobe +Tobias Bieniek +Tobias Bradtke +Tobias Gesellchen +Tobias Klauser +Tobias Munk +Tobias Schmidt +Tobias Schwab +Todd Crane +Todd Lunter +Todd Whiteman +Toli Kuznets +Tom Barlow +Tom Booth +Tom Denham +Tom Fotherby +Tom Howe +Tom Hulihan +Tom Maaswinkel +Tom Sweeney +Tom Wilkie +Tom X. Tobin +Tomas Tomecek +Tomasz Kopczynski +Tomasz Lipinski +Tomasz Nurkiewicz +Tommaso Visconti +Tomáš Hrčka +Tonny Xu +Tony Abboud +Tony Daws +Tony Miller +toogley +Torstein Husebø +Tõnis Tiigi +Trace Andreason +tracylihui <793912329@qq.com> +Trapier Marshall +Travis Cline +Travis Thieman +Trent Ogren +Trevor +Trevor Pounds +Trevor Sullivan +Trishna Guha +Tristan Carel +Troy Denton +Tycho Andersen +Tyler Brock +Tyler Brown +Tzu-Jung Lee +uhayate +Ulysse Carion +Umesh Yadav +Utz Bacher +vagrant +Vaidas Jablonskis +vanderliang +Velko Ivanov +Veres Lajos +Victor Algaze +Victor Coisne +Victor Costan +Victor I. Wood +Victor Lyuboslavsky +Victor Marmol +Victor Palma +Victor Vieux +Victoria Bialas +Vijaya Kumar K +Vikram bir Singh +Viktor Stanchev +Viktor Vojnovski +VinayRaghavanKS +Vincent Batts +Vincent Bernat +Vincent Boulineau +Vincent Demeester +Vincent Giersch +Vincent Mayers +Vincent Woo +Vinod Kulkarni +Vishal Doshi +Vishnu Kannan +Vitaly Ostrosablin +Vitor Monteiro +Vivek Agarwal +Vivek Dasgupta +Vivek Goyal +Vladimir Bulyga +Vladimir Kirillov +Vladimir Pouzanov +Vladimir Rutsky +Vladimir Varankin +VladimirAus +Vlastimil Zeman +Vojtech Vitek (V-Teq) +waitingkuo +Walter Leibbrandt +Walter Stanish +Wang Chao +Wang Guoliang +Wang Jie +Wang Long +Wang Ping +Wang Xing +Wang Yuexiao +Wang Yumu <37442693@qq.com> +wanghuaiqing +Ward Vandewege +WarheadsSE +Wassim Dhif +Wayne Chang +Wayne Song +Weerasak Chongnguluam +Wei Fu +Wei Wu +Wei-Ting Kuo +weipeng +weiyan +Weiyang Zhu +Wen Cheng Ma +Wendel Fleming +Wenjun Tang +Wenkai Yin +wenlxie +Wentao Zhang +Wenxuan Zhao +Wenyu You <21551128@zju.edu.cn> +Wenzhi Liang +Wes Morgan +Wewang Xiaorenfine +Wiktor Kwapisiewicz +Will Dietz +Will Rouesnel +Will Weaver +willhf +William Delanoue +William Henry +William Hubbs +William Martin +William Riancho +William Thurston +Wilson Júnior +Wing-Kam Wong +WiseTrem +Wolfgang Powisch +Wonjun Kim +xamyzhao +Xian Chaobo +Xianglin Gao +Xianlu Bird +Xiao YongBiao +XiaoBing Jiang +Xiaodong Liu +Xiaodong Zhang +Xiaoxi He +Xiaoxu Chen +Xiaoyu Zhang +xichengliudui <1693291525@qq.com> +xiekeyang +Ximo Guanter Gonzálbez +Xinbo Weng +Xinfeng Liu +Xinzi Zhou +Xiuming Chen +Xuecong Liao +xuzhaokui +Yadnyawalkya Tale +Yahya +YAMADA Tsuyoshi +Yamasaki Masahide +Yan Feng +Yang Bai +Yang Pengfei +yangchenliang +Yanqiang Miao +Yao Zaiyong +Yash Murty +Yassine Tijani +Yasunori Mahata +Yazhong Liu +Yestin Sun +Yi EungJun +Yibai Zhang +Yihang Ho +Ying Li +Yohei Ueda +Yong Tang +Yongxin Li +Yongzhi Pan +Yosef Fertel +You-Sheng Yang (楊有勝) +youcai +Youcef YEKHLEF +Yu Changchun +Yu Chengxia +Yu Peng +Yu-Ju Hong +Yuan Sun +Yuanhong Peng +Yue Zhang +Yuhao Fang +Yuichiro Kaneko +Yunxiang Huang +Yurii Rashkovskii +Yusuf Tarık Günaydın +Yves Junqueira +Zac Dover +Zach Borboa +Zachary Jaffee +Zain Memon +Zaiste! +Zane DeGraffenried +Zefan Li +Zen Lin(Zhinan Lin) +Zhang Kun +Zhang Wei +Zhang Wentao +ZhangHang +zhangxianwei +Zhenan Ye <21551168@zju.edu.cn> +zhenghenghuo +Zhenhai Gao +Zhenkun Bi +zhipengzuo +Zhou Hao +Zhoulin Xie +Zhu Guihua +Zhu Kunjia +Zhuoyun Wei +Ziheng Liu +Zilin Du +zimbatm +Ziming Dong +ZJUshuaizhou <21551191@zju.edu.cn> +zmarouf +Zoltan Tombol +Zou Yu +zqh +Zuhayr Elahi +Zunayed Ali +Álex González +Álvaro Lázaro +Átila Camurça Alves +尹吉峰 +屈骏 +徐俊杰 +慕陶 +搏通 +黄艳红00139573 diff --git a/vendor/github.com/docker/docker/LICENSE b/vendor/github.com/docker/docker/LICENSE new file mode 100644 index 0000000000..6d8d58fb67 --- /dev/null +++ b/vendor/github.com/docker/docker/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright 2013-2018 Docker, Inc. + + 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 + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/docker/docker/NOTICE b/vendor/github.com/docker/docker/NOTICE new file mode 100644 index 0000000000..58b19b6d15 --- /dev/null +++ b/vendor/github.com/docker/docker/NOTICE @@ -0,0 +1,19 @@ +Docker +Copyright 2012-2017 Docker, Inc. + +This product includes software developed at Docker, Inc. (https://www.docker.com). + +This product contains software (https://github.com/creack/pty) developed +by Keith Rarick, licensed under the MIT License. + +The following is courtesy of our legal counsel: + + +Use and transfer of Docker may be subject to certain restrictions by the +United States and other governments. +It is your responsibility to ensure that your use and/or transfer does not +violate applicable laws. + +For more information, please see https://www.bis.doc.gov + +See also https://www.apache.org/dev/crypto.html and/or seek legal counsel. diff --git a/vendor/github.com/docker/docker/api/README.md b/vendor/github.com/docker/docker/api/README.md new file mode 100644 index 0000000000..f136c3433a --- /dev/null +++ b/vendor/github.com/docker/docker/api/README.md @@ -0,0 +1,42 @@ +# Working on the Engine API + +The Engine API is an HTTP API used by the command-line client to communicate with the daemon. It can also be used by third-party software to control the daemon. + +It consists of various components in this repository: + +- `api/swagger.yaml` A Swagger definition of the API. +- `api/types/` Types shared by both the client and server, representing various objects, options, responses, etc. Most are written manually, but some are automatically generated from the Swagger definition. See [#27919](https://github.com/docker/docker/issues/27919) for progress on this. +- `cli/` The command-line client. +- `client/` The Go client used by the command-line client. It can also be used by third-party Go programs. +- `daemon/` The daemon, which serves the API. + +## Swagger definition + +The API is defined by the [Swagger](http://swagger.io/specification/) definition in `api/swagger.yaml`. This definition can be used to: + +1. Automatically generate documentation. +2. Automatically generate the Go server and client. (A work-in-progress.) +3. Provide a machine readable version of the API for introspecting what it can do, automatically generating clients for other languages, etc. + +## Updating the API documentation + +The API documentation is generated entirely from `api/swagger.yaml`. If you make updates to the API, edit this file to represent the change in the documentation. + +The file is split into two main sections: + +- `definitions`, which defines re-usable objects used in requests and responses +- `paths`, which defines the API endpoints (and some inline objects which don't need to be reusable) + +To make an edit, first look for the endpoint you want to edit under `paths`, then make the required edits. Endpoints may reference reusable objects with `$ref`, which can be found in the `definitions` section. + +There is hopefully enough example material in the file for you to copy a similar pattern from elsewhere in the file (e.g. adding new fields or endpoints), but for the full reference, see the [Swagger specification](https://github.com/docker/docker/issues/27919). + +`swagger.yaml` is validated by `hack/validate/swagger` to ensure it is a valid Swagger definition. This is useful when making edits to ensure you are doing the right thing. + +## Viewing the API documentation + +When you make edits to `swagger.yaml`, you may want to check the generated API documentation to ensure it renders correctly. + +Run `make swagger-docs` and a preview will be running at `http://localhost`. Some of the styling may be incorrect, but you'll be able to ensure that it is generating the correct documentation. + +The production documentation is generated by vendoring `swagger.yaml` into [docker/docker.github.io](https://github.com/docker/docker.github.io). diff --git a/vendor/github.com/docker/docker/api/common.go b/vendor/github.com/docker/docker/api/common.go new file mode 100644 index 0000000000..1565e2af64 --- /dev/null +++ b/vendor/github.com/docker/docker/api/common.go @@ -0,0 +1,11 @@ +package api // import "github.com/docker/docker/api" + +// Common constants for daemon and client. +const ( + // DefaultVersion of Current REST API + DefaultVersion = "1.41" + + // NoBaseImageSpecifier is the symbol used by the FROM + // command to specify that no base image is to be used. + NoBaseImageSpecifier = "scratch" +) diff --git a/vendor/github.com/docker/docker/api/common_unix.go b/vendor/github.com/docker/docker/api/common_unix.go new file mode 100644 index 0000000000..504b0c90d7 --- /dev/null +++ b/vendor/github.com/docker/docker/api/common_unix.go @@ -0,0 +1,6 @@ +// +build !windows + +package api // import "github.com/docker/docker/api" + +// MinVersion represents Minimum REST API version supported +const MinVersion = "1.12" diff --git a/vendor/github.com/docker/docker/api/common_windows.go b/vendor/github.com/docker/docker/api/common_windows.go new file mode 100644 index 0000000000..590ba5479b --- /dev/null +++ b/vendor/github.com/docker/docker/api/common_windows.go @@ -0,0 +1,8 @@ +package api // import "github.com/docker/docker/api" + +// MinVersion represents Minimum REST API version supported +// Technically the first daemon API version released on Windows is v1.25 in +// engine version 1.13. However, some clients are explicitly using downlevel +// APIs (e.g. docker-compose v2.1 file format) and that is just too restrictive. +// Hence also allowing 1.24 on Windows. +const MinVersion string = "1.24" diff --git a/vendor/github.com/docker/docker/api/swagger-gen.yaml b/vendor/github.com/docker/docker/api/swagger-gen.yaml new file mode 100644 index 0000000000..f07a02737f --- /dev/null +++ b/vendor/github.com/docker/docker/api/swagger-gen.yaml @@ -0,0 +1,12 @@ + +layout: + models: + - name: definition + source: asset:model + target: "{{ joinFilePath .Target .ModelPackage }}" + file_name: "{{ (snakize (pascalize .Name)) }}.go" + operations: + - name: handler + source: asset:serverOperation + target: "{{ joinFilePath .Target .APIPackage .Package }}" + file_name: "{{ (snakize (pascalize .Name)) }}.go" diff --git a/vendor/github.com/docker/docker/api/swagger.yaml b/vendor/github.com/docker/docker/api/swagger.yaml new file mode 100644 index 0000000000..bada4a8e3c --- /dev/null +++ b/vendor/github.com/docker/docker/api/swagger.yaml @@ -0,0 +1,11425 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.41" +info: + title: "Docker Engine API" + version: "1.41" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the + Docker client uses to communicate with the Engine, so everything the Docker + client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` + is `GET /containers/json`). The notable exception is running containers, + which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure + of the API call. The body of the response will be JSON in the following + format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release, so API calls are versioned to + ensure that clients don't break. To lock to a specific version of the API, + you prefix the URL with its version, for example, call `/v1.30/info` to use + the v1.30 version of the `/info` endpoint. If the API version specified in + the URL is not supported by the daemon, a HTTP `400 Bad Request` error message + is returned. + + If you omit the version-prefix, the current version of the API (v1.41) is used. + For example, calling `/info` is the same as calling `/v1.41/info`. Using the + API without a version-prefix is deprecated and will be removed in a future release. + + Engine releases in the near future should support this version of the API, + so your client will continue to work even if it is talking to a newer Engine. + + The API uses an open schema model, which means server may add extra properties + to responses. Likewise, the server will ignore any extra query parameters and + request body properties. When you write clients, you need to ignore additional + properties in responses to ensure they do not break when talking to newer + daemons. + + + # Authentication + + Authentication for registries is handled client side. The client has to send + authentication details to various endpoints that need to communicate with + registries, such as `POST /images/(name)/push`. These are sent as + `X-Registry-Auth` header as a [base64url encoded](https://tools.ietf.org/html/rfc4648#section-5) + (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this + structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), + you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldy. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. + See the [networking documentation](https://docs.docker.com/network/) + for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. Refer to the + [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) + for more information. + + To exec a command in a container, you first need to create an exec instance, + then start it. These two API endpoints are wrapped up in a single command-line + command, `docker exec`. + + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. Refer to the + [swarm mode documentation](https://docs.docker.com/engine/swarm/) + for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode + must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must + be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit + of swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Secret" + x-displayName: "Secrets" + description: | + Secrets are sensitive data that can be used by services. Swarm mode must + be enabled for these endpoints to work. + - name: "Config" + x-displayName: "Configs" + description: | + Configs are application configurations that can be used by services. Swarm + mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + description: "Host IP address that the container's port is mapped to" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp", "sctp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + DeviceRequest: + type: "object" + description: "A request for devices to be sent to device drivers" + properties: + Driver: + type: "string" + example: "nvidia" + Count: + type: "integer" + example: -1 + DeviceIDs: + type: "array" + items: + type: "string" + example: + - "0" + - "1" + - "GPU-fef8089b-4820-abfc-e83e-94318197576e" + Capabilities: + description: | + A list of capabilities; an OR list of AND lists of capabilities. + type: "array" + items: + type: "array" + items: + type: "string" + example: + # gpu AND nvidia AND compute + - ["gpu", "nvidia", "compute"] + Options: + description: | + Driver-specific options, specified as a key/value pairs. These options + are passed directly to the driver. + type: "object" + additionalProperties: + type: "string" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + type: "string" + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + - `npipe` Mounts a named pipe from the host into the container. Must exist prior to creating the container. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + - "npipe" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + Consistency: + description: "The consistency requirement for the mount: `default`, `consistent`, `cached`, or `delegated`." + type: "string" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + type: "string" + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + NonRecursive: + description: "Disable recursive bind mount." + type: "boolean" + default: false + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to + restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is + added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - Empty string means not to restart + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "" + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: | + If `on-failure` is used, the number of times to retry before giving up. + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: | + An integer value representing this container's relative CPU weight + versus other containers. + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + format: "int64" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: | + Path to `cgroups` under which the container's `cgroup` is created. If + the path is not absolute, the path is considered to be relative to the + `cgroups` path of the init process. Cgroups are created if they do not + already exist. + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form: + + ``` + [{"Path": "device_path", "Weight": weight}] + ``` + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: | + Microseconds of CPU time that the container can get in a CPU period. + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: | + The length of a CPU real-time period in microseconds. Set to 0 to + allocate no time allocated to real-time tasks. + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: | + The length of a CPU real-time runtime in microseconds. Set to 0 to + allocate no time allocated to real-time tasks. + type: "integer" + format: "int64" + CpusetCpus: + description: | + CPUs in which to allow execution (e.g., `0-3`, `0,1`). + type: "string" + example: "0-3" + CpusetMems: + description: | + Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only + effective on NUMA systems. + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DeviceCgroupRules: + description: "a list of cgroup rules to apply to the container" + type: "array" + items: + type: "string" + example: "c 13:* rwm" + DeviceRequests: + description: | + A list of requests for devices to be sent to device drivers. + type: "array" + items: + $ref: "#/definitions/DeviceRequest" + KernelMemory: + description: | + Kernel memory limit in bytes. + +


+ + > **Deprecated**: This field is deprecated as the kernel 5.4 deprecated + > `kmem.limit_in_bytes`. + type: "integer" + format: "int64" + example: 209715200 + KernelMemoryTCP: + description: "Hard limit for kernel TCP buffer memory (in bytes)." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: | + Total memory limit (memory + swap). Set as `-1` to enable unlimited + swap. + type: "integer" + format: "int64" + MemorySwappiness: + description: | + Tune a container's memory swappiness behavior. Accepts an integer + between 0 and 100. + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + Init: + description: | + Run an init inside the container that forwards signals and reaps + processes. This field is omitted if empty, and the default (as + configured on the daemon) is used. + type: "boolean" + x-nullable: true + PidsLimit: + description: | + Tune a container's PIDs limit. Set `0` or `-1` for unlimited, or `null` + to not change. + type: "integer" + format: "int64" + x-nullable: true + Ulimits: + description: | + A list of resource limits to set in the container. For example: + + ``` + {"Name": "nofile", "Soft": 1024, "Hard": 2048} + ``` + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are + mutually exclusive. The order of precedence is `CPUCount` first, then + `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are + mutually exclusive. The order of precedence is `CPUCount` first, then + `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: | + Maximum IO in bytes per second for the container system drive + (Windows only). + type: "integer" + format: "int64" + + Limit: + description: | + An object describing a limit on resources which can be requested by a task. + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + example: 4000000000 + MemoryBytes: + type: "integer" + format: "int64" + example: 8272408576 + Pids: + description: | + Limits the maximum number of PIDs in the container. Set `0` for unlimited. + type: "integer" + format: "int64" + default: 0 + example: 100 + + ResourceObject: + description: | + An object describing the resources which can be advertised by a node and + requested by a task. + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + example: 4000000000 + MemoryBytes: + type: "integer" + format: "int64" + example: 8272408576 + GenericResources: + $ref: "#/definitions/GenericResources" + + GenericResources: + description: | + User-defined resources can be either Integer resources (e.g, `SSD=3`) or + String resources (e.g, `GPU=UUID1`). + type: "array" + items: + type: "object" + properties: + NamedResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "string" + DiscreteResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "integer" + format: "int64" + example: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + HealthConfig: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `[]` inherit healthcheck from image or parent image + - `["NONE"]` disable healthcheck + - `["CMD", args...]` exec arguments directly + - `["CMD-SHELL", command]` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: | + The time to wait between checks in nanoseconds. It should be 0 or at + least 1000000 (1 ms). 0 means inherit. + type: "integer" + Timeout: + description: | + The time to wait before considering the check to have hung. It should + be 0 or at least 1000000 (1 ms). 0 means inherit. + type: "integer" + Retries: + description: | + The number of consecutive failures needed to consider a container as + unhealthy. 0 means inherit. + type: "integer" + StartPeriod: + description: | + Start period for the container to initialize before starting + health-retries countdown in nanoseconds. It should be 0 or at least + 1000000 (1 ms). 0 means inherit. + type: "integer" + + Health: + description: | + Health stores information about the container's healthcheck results. + type: "object" + properties: + Status: + description: | + Status is one of `none`, `starting`, `healthy` or `unhealthy` + + - "none" Indicates there is no healthcheck + - "starting" Starting indicates that the container is not yet ready + - "healthy" Healthy indicates that the container is running correctly + - "unhealthy" Unhealthy indicates that the container has a problem + type: "string" + enum: + - "none" + - "starting" + - "healthy" + - "unhealthy" + example: "healthy" + FailingStreak: + description: "FailingStreak is the number of consecutive failures" + type: "integer" + example: 0 + Log: + type: "array" + description: | + Log contains the last few results (oldest first) + items: + x-nullable: true + $ref: "#/definitions/HealthcheckResult" + + HealthcheckResult: + description: | + HealthcheckResult stores information about a single run of a healthcheck probe + type: "object" + properties: + Start: + description: | + Date and time at which this check started in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "date-time" + example: "2020-01-04T10:44:24.496525531Z" + End: + description: | + Date and time at which this check ended in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2020-01-04T10:45:21.364524523Z" + ExitCode: + description: | + ExitCode meanings: + + - `0` healthy + - `1` unhealthy + - `2` reserved (considered unhealthy) + - other values: error running probe + type: "integer" + example: 0 + Output: + description: "Output from last check" + type: "string" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding + is a string in one of these forms: + + - `host-src:container-dest[:options]` to bind-mount a host path + into the container. Both `host-src`, and `container-dest` must + be an _absolute_ path. + - `volume-name:container-dest[:options]` to bind-mount a volume + managed by a volume driver into the container. `container-dest` + must be an _absolute_ path. + + `options` is an optional, comma-delimited list of: + + - `nocopy` disables automatic copying of data from the container + path to the volume. The `nocopy` flag only applies to named volumes. + - `[ro|rw]` mounts a volume read-only or read-write, respectively. + If omitted or set to `rw`, volumes are mounted read-write. + - `[z|Z]` applies SELinux labels to allow or deny multiple containers + to read and write to the same volume. + - `z`: a _shared_ content label is applied to the content. This + label indicates that multiple containers can share the volume + content, for both reading and writing. + - `Z`: a _private unshared_ label is applied to the content. + This label indicates that only the current container can use + a private volume. Labeling systems such as SELinux require + proper labels to be placed on volume content that is mounted + into a container. Without a label, the security system can + prevent a container's processes from using the content. By + default, the labels set by the host operating system are not + modified. + - `[[r]shared|[r]slave|[r]private]` specifies mount + [propagation behavior](https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt). + This only applies to bind-mounted volumes, not internal volumes + or named volumes. Mount propagation requires the source mount + point (the location where the source directory is mounted in the + host operating system) to have the correct propagation properties. + For shared volumes, the source mount point must be set to `shared`. + For slave volumes, the mount must be set to either `shared` or + `slave`. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: | + Network mode to use for this container. Supported standard values + are: `bridge`, `host`, `none`, and `container:`. Any + other value is taken as a custom network's name to which this + container should connect to. + PortBindings: + $ref: "#/definitions/PortMap" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: | + Automatically remove the container when the container's process + exits. This has no effect if `RestartPolicy` is set. + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: | + A list of volumes to inherit from another container, specified in + the form `[:]`. + items: + type: "string" + Mounts: + description: | + Specification for mounts to be added to the container. + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: | + A list of kernel capabilities to add to the container. Conflicts + with option 'Capabilities'. + items: + type: "string" + CapDrop: + type: "array" + description: | + A list of kernel capabilities to drop from the container. Conflicts + with option 'Capabilities'. + items: + type: "string" + CgroupnsMode: + type: "string" + enum: + - "private" + - "host" + description: | + cgroup namespace mode for the container. Possible values are: + + - `"private"`: the container runs in its own private cgroup namespace + - `"host"`: use the host system's cgroup namespace + + If not specified, the daemon default is used, which can either be `"private"` + or `"host"`, depending on daemon version, kernel support and configuration. + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` + file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: | + A list of additional groups that the container process will run as. + items: + type: "string" + IpcMode: + type: "string" + description: | + IPC sharing mode for the container. Possible values are: + + - `"none"`: own private IPC namespace, with /dev/shm not mounted + - `"private"`: own private IPC namespace + - `"shareable"`: own private IPC namespace, with a possibility to share it with other containers + - `"container:"`: join another (shareable) container's IPC namespace + - `"host"`: use the host system's IPC namespace + + If not specified, daemon default is used, which can either be `"private"` + or `"shareable"`, depending on daemon version and configuration. + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: | + A list of links for the container in the form `container_name:alias`. + items: + type: "string" + OomScoreAdj: + type: "integer" + description: | + An integer value containing the score given to the container in + order to tune OOM killer preferences. + example: 500 + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be + either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: | + Allocates an ephemeral host port for all of a container's + exposed ports. + + Ports are de-allocated when the container stops and allocated when + the container starts. The allocated port might be changed when + restarting the container. + + The port is selected from the ephemeral port range that depends on + the kernel. For example, on Linux the range is defined by + `/proc/sys/net/ipv4/ip_local_port_range`. + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs + mounts, and their corresponding mount options. For example: + + ``` + { "/run": "rw,noexec,nosuid,size=65536k" } + ``` + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: | + Sets the usernamespace mode for the container when usernamespace + remapping option is enabled. + ShmSize: + type: "integer" + description: | + Size of `/dev/shm` in bytes. If omitted, the system uses 64MB. + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. + For example: + + ``` + {"net.ipv4.ip_forward": "1"} + ``` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: | + Initial console size, as an `[height, width]` array. (Windows only) + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: | + Isolation technology of the container. (Windows only) + enum: + - "default" + - "process" + - "hyperv" + MaskedPaths: + type: "array" + description: | + The list of paths to be masked inside the container (this overrides + the default set of paths). + items: + type: "string" + ReadonlyPaths: + type: "array" + description: | + The list of paths to be set as read-only inside the container + (this overrides the default set of paths). + items: + type: "string" + + ContainerConfig: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: | + Attach standard streams to a TTY, including `stdin` if it is not closed. + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the + form `["VAR=value", ...]`. A variable without `=` is removed from the + environment, rather than to have an empty value. + type: "array" + items: + type: "string" + Cmd: + description: | + Command to run specified as a string or an array of strings. + type: "array" + items: + type: "string" + Healthcheck: + $ref: "#/definitions/HealthConfig" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: | + The name of the image to use when creating the container/ + type: "string" + Volumes: + description: | + An object mapping mount point paths inside the container to empty + objects. + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the + entry point is reset to system default (i.e., the entry point used by + docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: "array" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: | + `ONBUILD` metadata that were defined in the image's `Dockerfile`. + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: | + Signal to stop a container as a string or unsigned integer. + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: | + Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell. + type: "array" + items: + type: "string" + + NetworkingConfig: + description: | + NetworkingConfig represents the container's networking configuration for + each of its interfaces. + It is used for the networking configs specified in the `docker create` + and `docker network connect` commands. + type: "object" + properties: + EndpointsConfig: + description: | + A mapping of network name to endpoint configuration for that network. + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + # putting an example here, instead of using the example values from + # /definitions/EndpointSettings, because containers/create currently + # does not support attaching to multiple networks, so the example request + # would be confusing if it showed that multiple networks can be contained + # in the EndpointsConfig. + # TODO remove once we support multiple networks on container create (see https://github.com/moby/moby/blob/07e6b843594e061f82baa5fa23c2ff7d536c2a05/daemon/create.go#L323) + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + NetworkSettings: + description: "NetworkSettings exposes the network settings in the API" + type: "object" + properties: + Bridge: + description: Name of the network'a bridge (for example, `docker0`). + type: "string" + example: "docker0" + SandboxID: + description: SandboxID uniquely represents a container's network stack. + type: "string" + example: "9d12daf2c33f5959c8bf90aa513e4f65b561738661003029ec84830cd503a0c3" + HairpinMode: + description: | + Indicates if hairpin NAT should be enabled on the virtual interface. + type: "boolean" + example: false + LinkLocalIPv6Address: + description: IPv6 unicast address using the link-local prefix. + type: "string" + example: "fe80::42:acff:fe11:1" + LinkLocalIPv6PrefixLen: + description: Prefix length of the IPv6 unicast address. + type: "integer" + example: "64" + Ports: + $ref: "#/definitions/PortMap" + SandboxKey: + description: SandboxKey identifies the sandbox + type: "string" + example: "/var/run/docker/netns/8ab54b426c38" + + # TODO is SecondaryIPAddresses actually used? + SecondaryIPAddresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO is SecondaryIPv6Addresses actually used? + SecondaryIPv6Addresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO properties below are part of DefaultNetworkSettings, which is + # marked as deprecated since Docker 1.9 and to be removed in Docker v17.12 + EndpointID: + description: | + EndpointID uniquely represents a service endpoint in a Sandbox. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.1" + GlobalIPv6Address: + description: | + Global IPv6 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 64 + IPAddress: + description: | + IPv4 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address for this network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8:2::100" + MacAddress: + description: | + MAC address for the container on the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "02:42:ac:11:00:04" + Networks: + description: | + Information about all networks that the container is connected to. + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + + Address: + description: Address represents an IPv4 or IPv6 IP address. + type: "object" + properties: + Addr: + description: IP address. + type: "string" + PrefixLen: + description: Mask length of the IP address. + type: "integer" + + PortMap: + description: | + PortMap describes the mapping of container ports to host ports, using the + container's port-number and protocol as key in the format `/`, + for example, `80/udp`. + + If a container's port is mapped for multiple protocols, separate entries + are added to the mapping table. + type: "object" + additionalProperties: + type: "array" + x-nullable: true + items: + $ref: "#/definitions/PortBinding" + example: + "443/tcp": + - HostIp: "127.0.0.1" + HostPort: "4443" + "80/tcp": + - HostIp: "0.0.0.0" + HostPort: "80" + - HostIp: "0.0.0.0" + HostPort: "8080" + "80/udp": + - HostIp: "0.0.0.0" + HostPort: "80" + "53/udp": + - HostIp: "0.0.0.0" + HostPort: "53" + "2377/tcp": null + + PortBinding: + description: | + PortBinding represents a binding between a host IP address and a host + port. + type: "object" + properties: + HostIp: + description: "Host IP address that the container's port is mapped to." + type: "string" + example: "127.0.0.1" + HostPort: + description: "Host port number that the container's port is mapped to." + type: "string" + example: "4443" + + GraphDriverData: + description: "Information about a container's graph driver." + type: "object" + required: [Name, Data] + properties: + Name: + type: "string" + x-nullable: false + Data: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + + Image: + type: "object" + required: + - Id + - Parent + - Comment + - Created + - Container + - DockerVersion + - Author + - Architecture + - Os + - Size + - VirtualSize + - GraphDriver + - RootFS + properties: + Id: + type: "string" + x-nullable: false + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + x-nullable: false + Comment: + type: "string" + x-nullable: false + Created: + type: "string" + x-nullable: false + Container: + type: "string" + x-nullable: false + ContainerConfig: + $ref: "#/definitions/ContainerConfig" + DockerVersion: + type: "string" + x-nullable: false + Author: + type: "string" + x-nullable: false + Config: + $ref: "#/definitions/ContainerConfig" + Architecture: + type: "string" + x-nullable: false + Os: + type: "string" + x-nullable: false + OsVersion: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + VirtualSize: + type: "integer" + format: "int64" + x-nullable: false + GraphDriver: + $ref: "#/definitions/GraphDriverData" + RootFS: + type: "object" + required: [Type] + properties: + Type: + type: "string" + x-nullable: false + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + Metadata: + type: "object" + properties: + LastTagTime: + type: "string" + format: "dateTime" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + CreatedAt: + type: "string" + format: "dateTime" + description: "Date/Time the volume was created." + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: | + The level at which the volume exists. Either `global` for cluster-wide, + or `local` for machine level. + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: | + The driver specific options used when creating the volume. + additionalProperties: + type: "string" + UsageData: + type: "object" + x-nullable: true + required: [Size, RefCount] + description: | + Usage details about the volume. This information is used by the + `GET /system/df` endpoint, and omitted in other endpoints. + properties: + Size: + type: "integer" + default: -1 + description: | + Amount of disk space used by the volume (in bytes). This information + is only available for volumes created with the `"local"` volume + driver. For volumes created with other volume drivers, this field + is set to `-1` ("not available") + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: | + The number of containers referencing this volume. This field + is set to `-1` if the reference-count is not available. + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + CreatedAt: "2016-06-07T20:31:11.853781916Z" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Attachable: + type: "boolean" + Ingress: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Attachable: false + Ingress: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: | + List of IPAM configuration options, specified as a map: + + ``` + {"Subnet": , "IPRange": , "Gateway": , "AuxAddress": } + ``` + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "object" + additionalProperties: + type: "string" + + NetworkContainer: + type: "object" + properties: + Name: + type: "string" + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + aux: + $ref: "#/definitions/ImageID" + + BuildCache: + type: "object" + properties: + ID: + type: "string" + Parent: + type: "string" + Type: + type: "string" + Description: + type: "string" + InUse: + type: "boolean" + Shared: + type: "boolean" + Size: + description: | + Amount of disk space used by the build cache (in bytes). + type: "integer" + CreatedAt: + description: | + Date and time at which the build cache was created in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + LastUsedAt: + description: | + Date and time at which the build cache was last used in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + x-nullable: true + example: "2017-08-09T07:09:37.632105588Z" + UsageCount: + type: "integer" + + ImageID: + type: "object" + description: "Image ID or Digest" + properties: + ID: + type: "string" + example: + ID: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + + CreateImageInfo: + type: "object" + properties: + id: + type: "string" + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + + ProgressDetail: + type: "object" + properties: + current: + type: "integer" + total: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + # Configurations + IPAMConfig: + $ref: "#/definitions/EndpointIPAMConfig" + Links: + type: "array" + items: + type: "string" + example: + - "container_1" + - "container_2" + Aliases: + type: "array" + items: + type: "string" + example: + - "server_x" + - "server_y" + + # Operational data + NetworkID: + description: | + Unique ID of the network. + type: "string" + example: "08754567f1f40222263eab4102e1c733ae697e8e354aa9cd6e18d7402835292a" + EndpointID: + description: | + Unique ID for the service endpoint in a Sandbox. + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for this network. + type: "string" + example: "172.17.0.1" + IPAddress: + description: | + IPv4 address. + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address. + type: "string" + example: "2001:db8:2::100" + GlobalIPv6Address: + description: | + Global IPv6 address. + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + type: "integer" + format: "int64" + example: 64 + MacAddress: + description: | + MAC address for the endpoint on this network. + type: "string" + example: "02:42:ac:11:00:04" + DriverOpts: + description: | + DriverOpts is a mapping of driver options and values. These options + are passed directly to the driver and are driver specific. + type: "object" + x-nullable: true + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + + EndpointIPAMConfig: + description: | + EndpointIPAMConfig represents an endpoint's IPAM configuration. + type: "object" + x-nullable: true + properties: + IPv4Address: + type: "string" + example: "172.20.30.33" + IPv6Address: + type: "string" + example: "2001:db8:abcd::3033" + LinkLocalIPs: + type: "array" + items: + type: "string" + example: + - "169.254.34.68" + - "fe80::3468" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + example: "some-mount" + Description: + type: "string" + x-nullable: false + example: "This is a mount that's used by the plugin." + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + example: "/var/lib/docker/plugins/" + Destination: + type: "string" + x-nullable: false + example: "/mnt/state" + Type: + type: "string" + x-nullable: false + example: "bind" + Options: + type: "array" + items: + type: "string" + example: + - "rbind" + - "rw" + + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + example: "/dev/fuse" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + example: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: + type: "string" + x-nullable: false + example: "tiborvass/sample-volume-plugin" + Enabled: + description: + True if the plugin is running. False if the plugin is not running, + only installed. + type: "boolean" + x-nullable: false + example: true + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + example: + - "DEBUG=0" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + example: "localhost:5000/tiborvass/sample-volume-plugin:latest" + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PidHost + - PropagatedMount + - IpcHost + - Mounts + - Env + - Args + properties: + DockerVersion: + description: "Docker Version used to create the plugin" + type: "string" + x-nullable: false + example: "17.06.0-ce" + Description: + type: "string" + x-nullable: false + example: "A sample volume plugin for Docker" + Documentation: + type: "string" + x-nullable: false + example: "https://docs.docker.com/engine/extend/plugins/" + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + example: + - "docker.volumedriver/1.0" + Socket: + type: "string" + x-nullable: false + example: "plugins.sock" + ProtocolScheme: + type: "string" + example: "some.protocol/v1.0" + description: "Protocol to use for clients connecting to the plugin." + enum: + - "" + - "moby.plugins.http/v1" + Entrypoint: + type: "array" + items: + type: "string" + example: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: + type: "string" + x-nullable: false + example: "/bin/" + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + example: 1000 + GID: + type: "integer" + format: "uint32" + example: 1000 + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + example: "host" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + example: + - "CAP_SYS_ADMIN" + - "CAP_SYSLOG" + AllowAllDevices: + type: "boolean" + x-nullable: false + example: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + example: "/mnt/volumes" + IpcHost: + type: "boolean" + x-nullable: false + example: false + PidHost: + type: "boolean" + x-nullable: false + example: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + example: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + example: "args" + Description: + x-nullable: false + type: "string" + example: "command line arguments" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + example: "layers" + diff_ids: + type: "array" + items: + type: "string" + example: + - "sha256:675532206fbf3030b8458f88d6e26d4eb1577688a25efec97154c94e8b6b4887" + - "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + + ObjectVersion: + description: | + The version number of the object such as node, service, etc. This is needed + to avoid conflicting writes. The client must send the version number along + with the modified specification when updating these objects. + + This approach ensures safe concurrency and determinism in that the change + on the object may not be applied if the version number has changed from the + last read. In other words, if two update requests specify the same base + version, only one of the requests can succeed. As a result, two separate + update requests that happen at the same time will not unintentionally + overwrite each other. + type: "object" + properties: + Index: + type: "integer" + format: "uint64" + example: 373531 + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + example: "my-node" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + example: "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: "active" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + + Node: + type: "object" + properties: + ID: + type: "string" + example: "24ifsmvkjbyhk" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the node was added to the swarm in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the node was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + $ref: "#/definitions/NodeDescription" + Status: + $ref: "#/definitions/NodeStatus" + ManagerStatus: + $ref: "#/definitions/ManagerStatus" + + NodeDescription: + description: | + NodeDescription encapsulates the properties of the Node as reported by the + agent. + type: "object" + properties: + Hostname: + type: "string" + example: "bf3067039e47" + Platform: + $ref: "#/definitions/Platform" + Resources: + $ref: "#/definitions/ResourceObject" + Engine: + $ref: "#/definitions/EngineDescription" + TLSInfo: + $ref: "#/definitions/TLSInfo" + + Platform: + description: | + Platform represents the platform (Arch/OS). + type: "object" + properties: + Architecture: + description: | + Architecture represents the hardware architecture (for example, + `x86_64`). + type: "string" + example: "x86_64" + OS: + description: | + OS represents the Operating System (for example, `linux` or `windows`). + type: "string" + example: "linux" + + EngineDescription: + description: "EngineDescription provides information about an engine." + type: "object" + properties: + EngineVersion: + type: "string" + example: "17.06.0" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + foo: "bar" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + example: + - Type: "Log" + Name: "awslogs" + - Type: "Log" + Name: "fluentd" + - Type: "Log" + Name: "gcplogs" + - Type: "Log" + Name: "gelf" + - Type: "Log" + Name: "journald" + - Type: "Log" + Name: "json-file" + - Type: "Log" + Name: "logentries" + - Type: "Log" + Name: "splunk" + - Type: "Log" + Name: "syslog" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "host" + - Type: "Network" + Name: "ipvlan" + - Type: "Network" + Name: "macvlan" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + - Type: "Volume" + Name: "local" + - Type: "Volume" + Name: "localhost:5000/vieux/sshfs:latest" + - Type: "Volume" + Name: "vieux/sshfs:latest" + + TLSInfo: + description: | + Information about the issuer of leaf TLS certificates and the trusted root + CA certificate. + type: "object" + properties: + TrustRoot: + description: | + The root CA certificate(s) that are used to validate leaf TLS + certificates. + type: "string" + CertIssuerSubject: + description: + The base64-url-safe-encoded raw subject bytes of the issuer. + type: "string" + CertIssuerPublicKey: + description: | + The base64-url-safe-encoded raw public key bytes of the issuer. + type: "string" + example: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + + NodeStatus: + description: | + NodeStatus represents the status of a node. + + It provides the current status of the node, as seen by the manager. + type: "object" + properties: + State: + $ref: "#/definitions/NodeState" + Message: + type: "string" + example: "" + Addr: + description: "IP address of the node." + type: "string" + example: "172.17.0.2" + + NodeState: + description: "NodeState represents the state of a node." + type: "string" + enum: + - "unknown" + - "down" + - "ready" + - "disconnected" + example: "ready" + + ManagerStatus: + description: | + ManagerStatus represents the status of a manager. + + It provides the current status of a node's manager component, if the node + is a manager. + x-nullable: true + type: "object" + properties: + Leader: + type: "boolean" + default: false + example: true + Reachability: + $ref: "#/definitions/Reachability" + Addr: + description: | + The IP address and port at which the manager is reachable. + type: "string" + example: "10.0.0.46:2377" + + Reachability: + description: "Reachability represents the reachability of a node." + type: "string" + enum: + - "unknown" + - "unreachable" + - "reachable" + example: "reachable" + + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + example: "default" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.corp.type: "production" + com.example.corp.department: "engineering" + Orchestration: + description: "Orchestration configuration." + type: "object" + x-nullable: true + properties: + TaskHistoryRetentionLimit: + description: | + The number of historic tasks to keep per instance or node. If + negative, never remove completed or failed tasks. + type: "integer" + format: "int64" + example: 10 + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "uint64" + example: 10000 + KeepOldSnapshots: + description: | + The number of snapshots to keep beyond the current snapshot. + type: "integer" + format: "uint64" + LogEntriesForSlowFollowers: + description: | + The number of log entries to keep around to sync up slow followers + after a snapshot is created. + type: "integer" + format: "uint64" + example: 500 + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from + the leader before becoming a candidate and starting an election. + `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate + directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 3 + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, + the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate + directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 1 + Dispatcher: + description: "Dispatcher configuration." + type: "object" + x-nullable: true + properties: + HeartbeatPeriod: + description: | + The delay for an agent to send a heartbeat to the dispatcher. + type: "integer" + format: "int64" + example: 5000000000 + CAConfig: + description: "CA configuration." + type: "object" + x-nullable: true + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + example: 7776000000000000 + ExternalCAs: + description: | + Configuration for forwarding signing requests to an external + certificate authority. + type: "array" + items: + type: "object" + properties: + Protocol: + description: | + Protocol for communication with the external CA (currently + only `cfssl` is supported). + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: | + URL where certificate signing requests should be sent. + type: "string" + Options: + description: | + An object with key/value pairs that are interpreted as + protocol-specific options for the external CA driver. + type: "object" + additionalProperties: + type: "string" + CACert: + description: | + The root CA certificate (in PEM format) this external CA uses + to issue TLS certificates (assumed to be to the current swarm + root CA certificate if not provided). + type: "string" + SigningCACert: + description: | + The desired signing CA certificate for all swarm node TLS leaf + certificates, in PEM format. + type: "string" + SigningCAKey: + description: | + The desired signing CA key for all swarm node TLS leaf certificates, + in PEM format. + type: "string" + ForceRotate: + description: | + An integer whose purpose is to force swarm to generate a new + signing CA certificate and key, if none have been specified in + `SigningCACert` and `SigningCAKey` + format: "uint64" + type: "integer" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: | + If set, generate a key and use it to lock data stored on the + managers. + type: "boolean" + example: false + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if + unspecified by a service. + + Updating this value only affects new tasks. Existing tasks continue + to use their previously configured log driver until recreated. + type: "object" + properties: + Name: + description: | + The log driver to use as a default for new tasks. + type: "string" + example: "json-file" + Options: + description: | + Driver-specific options for the selectd log driver, specified + as key/value pairs. + type: "object" + additionalProperties: + type: "string" + example: + "max-file": "10" + "max-size": "100m" + + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + description: | + ClusterInfo represents information about the swarm as is returned by the + "/info" endpoint. Join-tokens are not included. + x-nullable: true + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + example: "abajmipo7b4xz5ip2nrla6b11" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the swarm was initialised in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the swarm was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/SwarmSpec" + TLSInfo: + $ref: "#/definitions/TLSInfo" + RootRotationInProgress: + description: | + Whether there is currently a root CA rotation in progress for the swarm + type: "boolean" + example: false + DataPathPort: + description: | + DataPathPort specifies the data path port number for data traffic. + Acceptable port range is 1024 to 49151. + If no port is set or is set to 0, the default port (4789) is used. + type: "integer" + format: "uint32" + default: 4789 + example: 4789 + DefaultAddrPool: + description: | + Default Address Pool specifies default subnet pools for global scope + networks. + type: "array" + items: + type: "string" + format: "CIDR" + example: ["10.10.0.0/16", "20.20.0.0/16"] + SubnetSize: + description: | + SubnetSize specifies the subnet size of the networks created from the + default subnet pool. + type: "integer" + format: "uint32" + maximum: 29 + default: 24 + example: 24 + + JoinTokens: + description: | + JoinTokens contains the tokens workers and managers need to join the swarm. + type: "object" + properties: + Worker: + description: | + The token workers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: + description: | + The token managers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + + Swarm: + type: "object" + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + $ref: "#/definitions/JoinTokens" + + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + PluginSpec: + type: "object" + description: | + Plugin spec for the service. *(Experimental release only.)* + +


+ + > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + > mutually exclusive. PluginSpec is only used when the Runtime field + > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + > field is set to `attachment`. + properties: + Name: + description: "The name or 'alias' to use for the plugin." + type: "string" + Remote: + description: "The plugin image reference to use." + type: "string" + Disabled: + description: "Disable the plugin once scheduled." + type: "boolean" + PluginPrivilege: + type: "array" + items: + description: | + Describes a permission accepted by the user upon installing the + plugin. + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + ContainerSpec: + type: "object" + description: | + Container spec for the service. + +


+ + > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + > mutually exclusive. PluginSpec is only used when the Runtime field + > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + > field is set to `attachment`. + properties: + Image: + description: "The image name to use for the container" + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Hostname: + description: | + The hostname to use for the container, as a valid + [RFC 1123](https://tools.ietf.org/html/rfc1123) hostname. + type: "string" + Env: + description: | + A list of environment variables in the form `VAR=value`. + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Groups: + type: "array" + description: | + A list of additional groups that the container process will run as. + items: + type: "string" + Privileges: + type: "object" + description: "Security options for the container" + properties: + CredentialSpec: + type: "object" + description: "CredentialSpec for managed service account (Windows only)" + properties: + Config: + type: "string" + example: "0bt9dmxjvjiqermk6xrop3ekq" + description: | + Load credential spec from a Swarm Config with the given ID. + The specified config must also be present in the Configs + field with the Runtime property set. + +


+ + + > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, + > and `CredentialSpec.Config` are mutually exclusive. + File: + type: "string" + example: "spec.json" + description: | + Load credential spec from this file. The file is read by + the daemon, and must be present in the `CredentialSpecs` + subdirectory in the docker data directory, which defaults + to `C:\ProgramData\Docker\` on Windows. + + For example, specifying `spec.json` loads + `C:\ProgramData\Docker\CredentialSpecs\spec.json`. + +


+ + > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, + > and `CredentialSpec.Config` are mutually exclusive. + Registry: + type: "string" + description: | + Load credential spec from this value in the Windows + registry. The specified registry value must be located in: + + `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs` + +


+ + + > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, + > and `CredentialSpec.Config` are mutually exclusive. + SELinuxContext: + type: "object" + description: "SELinux labels of the container" + properties: + Disable: + type: "boolean" + description: "Disable SELinux" + User: + type: "string" + description: "SELinux user label" + Role: + type: "string" + description: "SELinux role label" + Type: + type: "string" + description: "SELinux type label" + Level: + type: "string" + description: "SELinux level label" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + OpenStdin: + description: "Open `stdin`" + type: "boolean" + ReadOnly: + description: "Mount the container's root filesystem as read only." + type: "boolean" + Mounts: + description: | + Specification for mounts to be added to containers created as part + of the service. + type: "array" + items: + $ref: "#/definitions/Mount" + StopSignal: + description: "Signal to stop the container." + type: "string" + StopGracePeriod: + description: | + Amount of time to wait for the container to terminate before + forcefully killing it. + type: "integer" + format: "int64" + HealthCheck: + $ref: "#/definitions/HealthConfig" + Hosts: + type: "array" + description: | + A list of hostname/IP mappings to add to the container's `hosts` + file. The format of extra hosts is specified in the + [hosts(5)](http://man7.org/linux/man-pages/man5/hosts.5.html) + man page: + + IP_address canonical_hostname [aliases...] + items: + type: "string" + DNSConfig: + description: | + Specification for DNS related configurations in resolver configuration + file (`resolv.conf`). + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: | + A list of internal resolver variables to be modified (e.g., + `debug`, `ndots:3`, etc.). + type: "array" + items: + type: "string" + Secrets: + description: | + Secrets contains references to zero or more secrets that will be + exposed to the service. + type: "array" + items: + type: "object" + properties: + File: + description: | + File represents a specific target that is backed by a file. + type: "object" + properties: + Name: + description: | + Name represents the final filename in the filesystem. + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + SecretID: + description: | + SecretID represents the ID of the specific secret that we're + referencing. + type: "string" + SecretName: + description: | + SecretName is the name of the secret that this references, + but this is just provided for lookup/display purposes. The + secret in the reference will be identified by its ID. + type: "string" + Configs: + description: | + Configs contains references to zero or more configs that will be + exposed to the service. + type: "array" + items: + type: "object" + properties: + File: + description: | + File represents a specific target that is backed by a file. + +


+ + > **Note**: `Configs.File` and `Configs.Runtime` are mutually exclusive + type: "object" + properties: + Name: + description: | + Name represents the final filename in the filesystem. + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + Runtime: + description: | + Runtime represents a target that is not mounted into the + container but is used by the task + +


+ + > **Note**: `Configs.File` and `Configs.Runtime` are mutually + > exclusive + type: "object" + ConfigID: + description: | + ConfigID represents the ID of the specific config that we're + referencing. + type: "string" + ConfigName: + description: | + ConfigName is the name of the config that this references, + but this is just provided for lookup/display purposes. The + config in the reference will be identified by its ID. + type: "string" + Isolation: + type: "string" + description: | + Isolation technology of the containers running the service. + (Windows only) + enum: + - "default" + - "process" + - "hyperv" + Init: + description: | + Run an init inside the container that forwards signals and reaps + processes. This field is omitted if empty, and the default (as + configured on the daemon) is used. + type: "boolean" + x-nullable: true + Sysctls: + description: | + Set kernel namedspaced parameters (sysctls) in the container. + The Sysctls option on services accepts the same sysctls as the + are supported on containers. Note that while the same sysctls are + supported, no guarantees or checks are made about their + suitability for a clustered environment, and it's up to the user + to determine whether a given sysctl will work properly in a + Service. + type: "object" + additionalProperties: + type: "string" + # This option is not used by Windows containers + CapabilityAdd: + type: "array" + description: | + A list of kernel capabilities to add to the default set + for the container. + items: + type: "string" + example: + - "CAP_NET_RAW" + - "CAP_SYS_ADMIN" + - "CAP_SYS_CHROOT" + - "CAP_SYSLOG" + CapabilityDrop: + type: "array" + description: | + A list of kernel capabilities to drop from the default set + for the container. + items: + type: "string" + example: + - "CAP_NET_RAW" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + NetworkAttachmentSpec: + description: | + Read-only spec type for non-swarm containers attached to swarm overlay + networks. + +


+ + > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + > mutually exclusive. PluginSpec is only used when the Runtime field + > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + > field is set to `attachment`. + type: "object" + properties: + ContainerID: + description: "ID of the container represented by this task" + type: "string" + Resources: + description: | + Resource requirements which apply to each individual container created + as part of the service. + type: "object" + properties: + Limits: + description: "Define resources limits." + $ref: "#/definitions/Limit" + Reservation: + description: "Define resources reservation." + $ref: "#/definitions/ResourceObject" + RestartPolicy: + description: | + Specification for the restart policy which applies to containers + created as part of this service. + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: | + Maximum attempts to restart a given container before giving up + (default value is 0, which is ignored). + type: "integer" + format: "int64" + default: 0 + Window: + description: | + Windows is the time window used to evaluate the restart policy + (default value is 0, which is unbounded). + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: | + An array of constraint expressions to limit the set of nodes where + a task can be scheduled. Constraint expressions can either use a + _match_ (`==`) or _exclude_ (`!=`) rule. Multiple constraints find + nodes that satisfy every expression (AND match). Constraints can + match node or Docker Engine labels as follows: + + node attribute | matches | example + ---------------------|--------------------------------|----------------------------------------------- + `node.id` | Node ID | `node.id==2ivku8v2gvtg4` + `node.hostname` | Node hostname | `node.hostname!=node-2` + `node.role` | Node role (`manager`/`worker`) | `node.role==manager` + `node.platform.os` | Node operating system | `node.platform.os==windows` + `node.platform.arch` | Node architecture | `node.platform.arch==x86_64` + `node.labels` | User-defined node labels | `node.labels.security==high` + `engine.labels` | Docker Engine's labels | `engine.labels.operatingsystem==ubuntu-14.04` + + `engine.labels` apply to Docker Engine labels like operating system, + drivers, etc. Swarm administrators add `node.labels` for operational + purposes by using the [`node update endpoint`](#operation/NodeUpdate). + + type: "array" + items: + type: "string" + example: + - "node.hostname!=node3.corp.example.com" + - "node.role!=manager" + - "node.labels.type==production" + - "node.platform.os==linux" + - "node.platform.arch==x86_64" + Preferences: + description: | + Preferences provide a way to make the scheduler aware of factors + such as topology. They are provided in order from highest to + lowest precedence. + type: "array" + items: + type: "object" + properties: + Spread: + type: "object" + properties: + SpreadDescriptor: + description: | + label descriptor, such as `engine.labels.az`. + type: "string" + example: + - Spread: + SpreadDescriptor: "node.labels.datacenter" + - Spread: + SpreadDescriptor: "node.labels.rack" + MaxReplicas: + description: | + Maximum number of replicas for per node (default value is 0, which + is unlimited) + type: "integer" + format: "int64" + default: 0 + Platforms: + description: | + Platforms stores all the platforms that the service's image can + run on. This field is used in the platform filter for scheduling. + If empty, then the platform filter is off, meaning there are no + scheduling restrictions. + type: "array" + items: + $ref: "#/definitions/Platform" + ForceUpdate: + description: | + A counter that triggers an update even if no relevant parameters have + been changed. + type: "integer" + Runtime: + description: | + Runtime is the type of runtime specified for the task executor. + type: "string" + Networks: + description: "Specifies which networks the service should attach to." + type: "array" + items: + $ref: "#/definitions/NetworkAttachmentConfig" + LogDriver: + description: | + Specifies the log driver to use for tasks created from this spec. If + not present, the default one for the swarm will be used, finally + falling back to the engine default if not specified. + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + - "remove" + - "orphaned" + + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + AssignedGenericResources: + $ref: "#/definitions/GenericResources" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + JobIteration: + description: | + If the Service this Task belongs to is a job-mode service, contains + the JobIteration of the Service this Task was created for. Absent if + the Task was created for a Replicated or Global Service. + $ref: "#/definitions/ObjectVersion" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + AssignedGenericResources: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + ReplicatedJob: + description: | + The mode used for services with a finite number of tasks that run + to a completed state. + type: "object" + properties: + MaxConcurrent: + description: | + The maximum number of replicas to run simultaneously. + type: "integer" + format: "int64" + default: 1 + TotalCompletions: + description: | + The total number of replicas desired to reach the Completed + state. If unset, will default to the value of `MaxConcurrent` + type: "integer" + format: "int64" + GlobalJob: + description: | + The mode used for services which run a task to the completed state + on each valid node. + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: | + Maximum number of tasks to be updated in one iteration (0 means + unlimited parallelism). + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: | + Action to take if an updated task fails to run, or stops running + during the update. + type: "string" + enum: + - "continue" + - "pause" + - "rollback" + Monitor: + description: | + Amount of time to monitor each updated task for failures, in + nanoseconds. + type: "integer" + format: "int64" + MaxFailureRatio: + description: | + The fraction of tasks that may fail during an update before the + failure action is invoked, specified as a floating point number + between 0 and 1. + type: "number" + default: 0 + Order: + description: | + The order of operations when rolling out an updated task. Either + the old task is shut down before the new task is started, or the + new task is started before the old task is shut down. + type: "string" + enum: + - "stop-first" + - "start-first" + RollbackConfig: + description: "Specification for the rollback strategy of the service." + type: "object" + properties: + Parallelism: + description: | + Maximum number of tasks to be rolled back in one iteration (0 means + unlimited parallelism). + type: "integer" + format: "int64" + Delay: + description: | + Amount of time between rollback iterations, in nanoseconds. + type: "integer" + format: "int64" + FailureAction: + description: | + Action to take if an rolled back task fails to run, or stops + running during the rollback. + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: | + Amount of time to monitor each rolled back task for failures, in + nanoseconds. + type: "integer" + format: "int64" + MaxFailureRatio: + description: | + The fraction of tasks that may fail during a rollback before the + failure action is invoked, specified as a floating point number + between 0 and 1. + type: "number" + default: 0 + Order: + description: | + The order of operations when rolling back a task. Either the old + task is shut down before the new task is started, or the new task + is started before the old task is shut down. + type: "string" + enum: + - "stop-first" + - "start-first" + Networks: + description: "Specifies which networks the service should attach to." + type: "array" + items: + $ref: "#/definitions/NetworkAttachmentConfig" + + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + - "sctp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + PublishMode: + description: | + The mode in which port is published. + +


+ + - "ingress" makes the target port accessible on every node, + regardless of whether there is a task for the service running on + that node or not. + - "host" bypasses the routing mesh and publish the port directly on + the swarm node where that service is running. + + type: "string" + enum: + - "ingress" + - "host" + default: "ingress" + example: "ingress" + + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: | + The mode of resolution to use for internal load balancing between tasks. + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: | + List of exposed ports that this service is accessible on from the + outside. Ports can only be provided if `vip` resolution mode is used. + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + + Service: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + ServiceStatus: + description: | + The status of the service's tasks. Provided only when requested as + part of a ServiceList operation. + type: "object" + properties: + RunningTasks: + description: | + The number of tasks for the service currently in the Running state. + type: "integer" + format: "uint64" + example: 7 + DesiredTasks: + description: | + The number of tasks for the service desired to be running. + For replicated services, this is the replica count from the + service spec. For global services, this is computed by taking + count of all tasks for the service with a Desired State other + than Shutdown. + type: "integer" + format: "uint64" + example: 10 + CompletedTasks: + description: | + The number of tasks for a job that are in the Completed state. + This field must be cross-referenced with the service type, as the + value of 0 may mean the service is not in a job mode, or it may + mean the job-mode service has no tasks yet Completed. + type: "integer" + format: "uint64" + JobStatus: + description: | + The status of the service when it is in one of ReplicatedJob or + GlobalJob modes. Absent on Replicated and Global mode services. The + JobIteration is an ObjectVersion, but unlike the Service's version, + does not need to be sent with an update request. + type: "object" + properties: + JobIteration: + description: | + JobIteration is a value increased each time a Job is executed, + successfully or otherwise. "Executed", in this case, means the + job as a whole has been started, not that an individual Task has + been launched. A job is "Executed" when its ServiceSpec is + updated. JobIteration can be used to disambiguate Tasks belonging + to different executions of a job. Though JobIteration will + increase with each subsequent execution, it may not necessarily + increase by 1, and so JobIteration should not be used to + $ref: "#/definitions/ObjectVersion" + LastExecution: + description: | + The last time, as observed by the server, that this job was + started. + type: "string" + format: "dateTime" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + + ImageDeleteResponseItem: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + + Driver: + description: "Driver represents a driver (network, logging, secrets)." + type: "object" + required: [Name] + properties: + Name: + description: "Name of the driver." + type: "string" + x-nullable: false + example: "some-driver" + Options: + description: "Key/value map of driver-specific options." + type: "object" + x-nullable: false + additionalProperties: + type: "string" + example: + OptionA: "value for driver-specific option A" + OptionB: "value for driver-specific option B" + + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) + data to store as secret. + + This field is only used to _create_ a secret, and is not returned by + other endpoints. + type: "string" + example: "" + Driver: + description: | + Name of the secrets driver used to fetch the secret's value from an + external secret store. + $ref: "#/definitions/Driver" + Templating: + description: | + Templating driver, if applicable + + Templating controls whether and how to evaluate the config payload as + a template. If no driver is set, no templating is used. + $ref: "#/definitions/Driver" + + Secret: + type: "object" + properties: + ID: + type: "string" + example: "blt1owaxmitz71s9v5zh81zun" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + Spec: + $ref: "#/definitions/SecretSpec" + + ConfigSpec: + type: "object" + properties: + Name: + description: "User-defined name of the config." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) + config data. + type: "string" + Templating: + description: | + Templating driver, if applicable + + Templating controls whether and how to evaluate the config payload as + a template. If no driver is set, no templating is used. + $ref: "#/definitions/Driver" + + Config: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ConfigSpec" + + ContainerState: + description: | + ContainerState stores container's running state. It's part of ContainerJSONBase + and will be returned by the "inspect" command. + type: "object" + properties: + Status: + description: | + String representation of the container state. Can be one of "created", + "running", "paused", "restarting", "removing", "exited", or "dead". + type: "string" + enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"] + example: "running" + Running: + description: | + Whether this container is running. + + Note that a running container can be _paused_. The `Running` and `Paused` + booleans are not mutually exclusive: + + When pausing a container (on Linux), the freezer cgroup is used to suspend + all processes in the container. Freezing the process requires the process to + be running. As a result, paused containers are both `Running` _and_ `Paused`. + + Use the `Status` field instead to determine if a container's state is "running". + type: "boolean" + example: true + Paused: + description: "Whether this container is paused." + type: "boolean" + example: false + Restarting: + description: "Whether this container is restarting." + type: "boolean" + example: false + OOMKilled: + description: | + Whether this container has been killed because it ran out of memory. + type: "boolean" + example: false + Dead: + type: "boolean" + example: false + Pid: + description: "The process ID of this container" + type: "integer" + example: 1234 + ExitCode: + description: "The last exit code of this container" + type: "integer" + example: 0 + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + example: "2020-01-06T09:06:59.461876391Z" + FinishedAt: + description: "The time when this container last exited." + type: "string" + example: "2020-01-06T09:07:59.461876391Z" + Health: + x-nullable: true + $ref: "#/definitions/Health" + + SystemVersion: + type: "object" + description: | + Response of Engine API: GET "/version" + properties: + Platform: + type: "object" + required: [Name] + properties: + Name: + type: "string" + Components: + type: "array" + description: | + Information about system components + items: + type: "object" + x-go-name: ComponentVersion + required: [Name, Version] + properties: + Name: + description: | + Name of the component + type: "string" + example: "Engine" + Version: + description: | + Version of the component + type: "string" + x-nullable: false + example: "19.03.12" + Details: + description: | + Key/value pairs of strings with additional information about the + component. These values are intended for informational purposes + only, and their content is not defined, and not part of the API + specification. + + These messages can be printed by the client as information to the user. + type: "object" + x-nullable: true + Version: + description: "The version of the daemon" + type: "string" + example: "19.03.12" + ApiVersion: + description: | + The default (and highest) API version that is supported by the daemon + type: "string" + example: "1.40" + MinAPIVersion: + description: | + The minimum API version that is supported by the daemon + type: "string" + example: "1.12" + GitCommit: + description: | + The Git commit of the source code that was used to build the daemon + type: "string" + example: "48a66213fe" + GoVersion: + description: | + The version Go used to compile the daemon, and the version of the Go + runtime in use. + type: "string" + example: "go1.13.14" + Os: + description: | + The operating system that the daemon is running on ("linux" or "windows") + type: "string" + example: "linux" + Arch: + description: | + The architecture that the daemon is running on + type: "string" + example: "amd64" + KernelVersion: + description: | + The kernel version (`uname -r`) that the daemon is running on. + + This field is omitted when empty. + type: "string" + example: "4.19.76-linuxkit" + Experimental: + description: | + Indicates if the daemon is started with experimental features enabled. + + This field is omitted when empty / false. + type: "boolean" + example: true + BuildTime: + description: | + The date and time that the daemon was compiled. + type: "string" + example: "2020-06-22T15:49:27.000000000+00:00" + + + SystemInfo: + type: "object" + properties: + ID: + description: | + Unique identifier of the daemon. + +


+ + > **Note**: The format of the ID itself is not part of the API, and + > should not be considered stable. + type: "string" + example: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + Containers: + description: "Total number of containers on the host." + type: "integer" + example: 14 + ContainersRunning: + description: | + Number of containers with status `"running"`. + type: "integer" + example: 3 + ContainersPaused: + description: | + Number of containers with status `"paused"`. + type: "integer" + example: 1 + ContainersStopped: + description: | + Number of containers with status `"stopped"`. + type: "integer" + example: 10 + Images: + description: | + Total number of images on the host. + + Both _tagged_ and _untagged_ (dangling) images are counted. + type: "integer" + example: 508 + Driver: + description: "Name of the storage driver in use." + type: "string" + example: "overlay2" + DriverStatus: + description: | + Information specific to the storage driver, provided as + "label" / "value" pairs. + + This information is provided by the storage driver, and formatted + in a way consistent with the output of `docker info` on the command + line. + +


+ + > **Note**: The information returned in this field, including the + > formatting of values and labels, should not be considered stable, + > and may change without notice. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Backing Filesystem", "extfs"] + - ["Supports d_type", "true"] + - ["Native Overlay Diff", "true"] + DockerRootDir: + description: | + Root directory of persistent Docker state. + + Defaults to `/var/lib/docker` on Linux, and `C:\ProgramData\docker` + on Windows. + type: "string" + example: "/var/lib/docker" + Plugins: + $ref: "#/definitions/PluginsInfo" + MemoryLimit: + description: "Indicates if the host has memory limit support enabled." + type: "boolean" + example: true + SwapLimit: + description: "Indicates if the host has memory swap limit support enabled." + type: "boolean" + example: true + KernelMemory: + description: | + Indicates if the host has kernel memory limit support enabled. + +


+ + > **Deprecated**: This field is deprecated as the kernel 5.4 deprecated + > `kmem.limit_in_bytes`. + type: "boolean" + example: true + CpuCfsPeriod: + description: | + Indicates if CPU CFS(Completely Fair Scheduler) period is supported by + the host. + type: "boolean" + example: true + CpuCfsQuota: + description: | + Indicates if CPU CFS(Completely Fair Scheduler) quota is supported by + the host. + type: "boolean" + example: true + CPUShares: + description: | + Indicates if CPU Shares limiting is supported by the host. + type: "boolean" + example: true + CPUSet: + description: | + Indicates if CPUsets (cpuset.cpus, cpuset.mems) are supported by the host. + + See [cpuset(7)](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt) + type: "boolean" + example: true + PidsLimit: + description: "Indicates if the host kernel has PID limit support enabled." + type: "boolean" + example: true + OomKillDisable: + description: "Indicates if OOM killer disable is supported on the host." + type: "boolean" + IPv4Forwarding: + description: "Indicates IPv4 forwarding is enabled." + type: "boolean" + example: true + BridgeNfIptables: + description: "Indicates if `bridge-nf-call-iptables` is available on the host." + type: "boolean" + example: true + BridgeNfIp6tables: + description: "Indicates if `bridge-nf-call-ip6tables` is available on the host." + type: "boolean" + example: true + Debug: + description: | + Indicates if the daemon is running in debug-mode / with debug-level + logging enabled. + type: "boolean" + example: true + NFd: + description: | + The total number of file Descriptors in use by the daemon process. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 64 + NGoroutines: + description: | + The number of goroutines that currently exist. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 174 + SystemTime: + description: | + Current system-time in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) + format with nano-seconds. + type: "string" + example: "2017-08-08T20:28:29.06202363Z" + LoggingDriver: + description: | + The logging driver to use as a default for new containers. + type: "string" + CgroupDriver: + description: | + The driver to use for managing cgroups. + type: "string" + enum: ["cgroupfs", "systemd", "none"] + default: "cgroupfs" + example: "cgroupfs" + CgroupVersion: + description: | + The version of the cgroup. + type: "string" + enum: ["1", "2"] + default: "1" + example: "1" + NEventsListener: + description: "Number of event listeners subscribed." + type: "integer" + example: 30 + KernelVersion: + description: | + Kernel version of the host. + + On Linux, this information obtained from `uname`. On Windows this + information is queried from the HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ + registry value, for example _"10.0 14393 (14393.1198.amd64fre.rs1_release_sec.170427-1353)"_. + type: "string" + example: "4.9.38-moby" + OperatingSystem: + description: | + Name of the host's operating system, for example: "Ubuntu 16.04.2 LTS" + or "Windows Server 2016 Datacenter" + type: "string" + example: "Alpine Linux v3.5" + OSVersion: + description: | + Version of the host's operating system + +


+ + > **Note**: The information returned in this field, including its + > very existence, and the formatting of values, should not be considered + > stable, and may change without notice. + type: "string" + example: "16.04" + OSType: + description: | + Generic type of the operating system of the host, as returned by the + Go runtime (`GOOS`). + + Currently returned values are "linux" and "windows". A full list of + possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "linux" + Architecture: + description: | + Hardware architecture of the host, as returned by the Go runtime + (`GOARCH`). + + A full list of possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "x86_64" + NCPU: + description: | + The number of logical CPUs usable by the daemon. + + The number of available CPUs is checked by querying the operating + system when the daemon starts. Changes to operating system CPU + allocation after the daemon is started are not reflected. + type: "integer" + example: 4 + MemTotal: + description: | + Total amount of physical memory available on the host, in bytes. + type: "integer" + format: "int64" + example: 2095882240 + + IndexServerAddress: + description: | + Address / URL of the index server that is used for image search, + and as a default for user authentication for Docker Hub and Docker Cloud. + default: "https://index.docker.io/v1/" + type: "string" + example: "https://index.docker.io/v1/" + RegistryConfig: + $ref: "#/definitions/RegistryServiceConfig" + GenericResources: + $ref: "#/definitions/GenericResources" + HttpProxy: + description: | + HTTP-proxy configured for the daemon. This value is obtained from the + [`HTTP_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + Credentials ([user info component](https://tools.ietf.org/html/rfc3986#section-3.2.1)) in the proxy URL + are masked in the API response. + + Containers do not automatically inherit this configuration. + type: "string" + example: "http://xxxxx:xxxxx@proxy.corp.example.com:8080" + HttpsProxy: + description: | + HTTPS-proxy configured for the daemon. This value is obtained from the + [`HTTPS_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + Credentials ([user info component](https://tools.ietf.org/html/rfc3986#section-3.2.1)) in the proxy URL + are masked in the API response. + + Containers do not automatically inherit this configuration. + type: "string" + example: "https://xxxxx:xxxxx@proxy.corp.example.com:4443" + NoProxy: + description: | + Comma-separated list of domain extensions for which no proxy should be + used. This value is obtained from the [`NO_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) + environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "*.local, 169.254/16" + Name: + description: "Hostname of the host." + type: "string" + example: "node5.corp.example.com" + Labels: + description: | + User-defined labels (key/value metadata) as set on the daemon. + +


+ + > **Note**: When part of a Swarm, nodes can both have _daemon_ labels, + > set through the daemon configuration, and _node_ labels, set from a + > manager node in the Swarm. Node labels are not included in this + > field. Node labels can be retrieved using the `/nodes/(id)` endpoint + > on a manager node in the Swarm. + type: "array" + items: + type: "string" + example: ["storage=ssd", "production"] + ExperimentalBuild: + description: | + Indicates if experimental features are enabled on the daemon. + type: "boolean" + example: true + ServerVersion: + description: | + Version string of the daemon. + + > **Note**: the [standalone Swarm API](https://docs.docker.com/swarm/swarm-api/) + > returns the Swarm version instead of the daemon version, for example + > `swarm/1.2.8`. + type: "string" + example: "17.06.0-ce" + ClusterStore: + description: | + URL of the distributed storage backend. + + + The storage backend is used for multihost networking (to store + network and endpoint information) and by the node discovery mechanism. + +


+ + > **Deprecated**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "consul://consul.corp.example.com:8600/some/path" + ClusterAdvertise: + description: | + The network endpoint that the Engine advertises for the purpose of + node discovery. ClusterAdvertise is a `host:port` combination on which + the daemon is reachable by other hosts. + +


+ + > **Deprecated**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "node5.corp.example.com:8000" + Runtimes: + description: | + List of [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtimes configured on the daemon. Keys hold the "name" used to + reference the runtime. + + The Docker daemon relies on an OCI compliant runtime (invoked via the + `containerd` daemon) as its interface to the Linux kernel namespaces, + cgroups, and SELinux. + + The default runtime is `runc`, and automatically configured. Additional + runtimes can be configured by the user and will be listed here. + type: "object" + additionalProperties: + $ref: "#/definitions/Runtime" + default: + runc: + path: "runc" + example: + runc: + path: "runc" + runc-master: + path: "/go/bin/runc" + custom: + path: "/usr/local/bin/my-oci-runtime" + runtimeArgs: ["--debug", "--systemd-cgroup=false"] + DefaultRuntime: + description: | + Name of the default OCI runtime that is used when starting containers. + + The default can be overridden per-container at create time. + type: "string" + default: "runc" + example: "runc" + Swarm: + $ref: "#/definitions/SwarmInfo" + LiveRestoreEnabled: + description: | + Indicates if live restore is enabled. + + If enabled, containers are kept running when the daemon is shutdown + or upon daemon start if running containers are detected. + type: "boolean" + default: false + example: false + Isolation: + description: | + Represents the isolation technology to use as a default for containers. + The supported values are platform-specific. + + If no isolation value is specified on daemon start, on Windows client, + the default is `hyperv`, and on Windows server, the default is `process`. + + This option is currently not used on other platforms. + default: "default" + type: "string" + enum: + - "default" + - "hyperv" + - "process" + InitBinary: + description: | + Name and, optional, path of the `docker-init` binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "docker-init" + ContainerdCommit: + $ref: "#/definitions/Commit" + RuncCommit: + $ref: "#/definitions/Commit" + InitCommit: + $ref: "#/definitions/Commit" + SecurityOptions: + description: | + List of security features that are enabled on the daemon, such as + apparmor, seccomp, SELinux, user-namespaces (userns), and rootless. + + Additional configuration options for each security feature may + be present, and are included as a comma-separated list of key/value + pairs. + type: "array" + items: + type: "string" + example: + - "name=apparmor" + - "name=seccomp,profile=default" + - "name=selinux" + - "name=userns" + - "name=rootless" + ProductLicense: + description: | + Reports a summary of the product license on the daemon. + + If a commercial license has been applied to the daemon, information + such as number of nodes, and expiration are included. + type: "string" + example: "Community Engine" + DefaultAddressPools: + description: | + List of custom default address pools for local networks, which can be + specified in the daemon.json file or dockerd option. + + Example: a Base "10.10.0.0/16" with Size 24 will define the set of 256 + 10.10.[0-255].0/24 address pools. + type: "array" + items: + type: "object" + properties: + Base: + description: "The network address in CIDR format" + type: "string" + example: "10.10.0.0/16" + Size: + description: "The network pool size" + type: "integer" + example: "24" + Warnings: + description: | + List of warnings / informational messages about missing features, or + issues related to the daemon configuration. + + These messages can be printed by the client as information to the user. + type: "array" + items: + type: "string" + example: + - "WARNING: No memory limit support" + - "WARNING: bridge-nf-call-iptables is disabled" + - "WARNING: bridge-nf-call-ip6tables is disabled" + + + # PluginsInfo is a temp struct holding Plugins name + # registered with docker daemon. It is used by Info struct + PluginsInfo: + description: | + Available plugins per type. + +


+ + > **Note**: Only unmanaged (V1) plugins are included in this list. + > V1 plugins are "lazily" loaded, and are not returned in this list + > if there is no resource using the plugin. + type: "object" + properties: + Volume: + description: "Names of available volume-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["local"] + Network: + description: "Names of available network-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["bridge", "host", "ipvlan", "macvlan", "null", "overlay"] + Authorization: + description: "Names of available authorization plugins." + type: "array" + items: + type: "string" + example: ["img-authz-plugin", "hbm"] + Log: + description: "Names of available logging-drivers, and logging-driver plugins." + type: "array" + items: + type: "string" + example: ["awslogs", "fluentd", "gcplogs", "gelf", "journald", "json-file", "logentries", "splunk", "syslog"] + + + RegistryServiceConfig: + description: | + RegistryServiceConfig stores daemon registry services configuration. + type: "object" + x-nullable: true + properties: + AllowNondistributableArtifactsCIDRs: + description: | + List of IP ranges to which nondistributable artifacts can be pushed, + using the CIDR syntax [RFC 4632](https://tools.ietf.org/html/4632). + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior, and enables the daemon to + push nondistributable artifacts to all registries whose resolved IP + address is within the subnet described by the CIDR syntax. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + AllowNondistributableArtifactsHostnames: + description: | + List of registry hostnames to which nondistributable artifacts can be + pushed, using the format `[:]` or `[:]`. + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior for the specified + registries. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + type: "array" + items: + type: "string" + example: ["registry.internal.corp.example.com:3000", "[2001:db8:a0b:12f0::1]:443"] + InsecureRegistryCIDRs: + description: | + List of IP ranges of insecure registries, using the CIDR syntax + ([RFC 4632](https://tools.ietf.org/html/4632)). Insecure registries + accept un-encrypted (HTTP) and/or untrusted (HTTPS with certificates + from unknown CAs) communication. + + By default, local registries (`127.0.0.0/8`) are configured as + insecure. All other registries are secure. Communicating with an + insecure registry is not possible if the daemon assumes that registry + is secure. + + This configuration override this behavior, insecure communication with + registries whose resolved IP address is within the subnet described by + the CIDR syntax. + + Registries can also be marked insecure by hostname. Those registries + are listed under `IndexConfigs` and have their `Secure` field set to + `false`. + + > **Warning**: Using this option can be useful when running a local + > registry, but introduces security vulnerabilities. This option + > should therefore ONLY be used for testing purposes. For increased + > security, users should add their CA to their system's list of trusted + > CAs instead of enabling this option. + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + IndexConfigs: + type: "object" + additionalProperties: + $ref: "#/definitions/IndexInfo" + example: + "127.0.0.1:5000": + "Name": "127.0.0.1:5000" + "Mirrors": [] + "Secure": false + "Official": false + "[2001:db8:a0b:12f0::1]:80": + "Name": "[2001:db8:a0b:12f0::1]:80" + "Mirrors": [] + "Secure": false + "Official": false + "docker.io": + Name: "docker.io" + Mirrors: ["https://hub-mirror.corp.example.com:5000/"] + Secure: true + Official: true + "registry.internal.corp.example.com:3000": + Name: "registry.internal.corp.example.com:3000" + Mirrors: [] + Secure: false + Official: false + Mirrors: + description: | + List of registry URLs that act as a mirror for the official + (`docker.io`) registry. + + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://[2001:db8:a0b:12f0::1]/" + + IndexInfo: + description: + IndexInfo contains information about a registry. + type: "object" + x-nullable: true + properties: + Name: + description: | + Name of the registry, such as "docker.io". + type: "string" + example: "docker.io" + Mirrors: + description: | + List of mirrors, expressed as URIs. + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://registry-2.docker.io/" + - "https://registry-3.docker.io/" + Secure: + description: | + Indicates if the registry is part of the list of insecure + registries. + + If `false`, the registry is insecure. Insecure registries accept + un-encrypted (HTTP) and/or untrusted (HTTPS with certificates from + unknown CAs) communication. + + > **Warning**: Insecure registries can be useful when running a local + > registry. However, because its use creates security vulnerabilities + > it should ONLY be enabled for testing purposes. For increased + > security, users should add their CA to their system's list of + > trusted CAs instead of enabling this option. + type: "boolean" + example: true + Official: + description: | + Indicates whether this is an official registry (i.e., Docker Hub / docker.io) + type: "boolean" + example: true + + Runtime: + description: | + Runtime describes an [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtime. + + The runtime is invoked by the daemon via the `containerd` daemon. OCI + runtimes act as an interface to the Linux kernel namespaces, cgroups, + and SELinux. + type: "object" + properties: + path: + description: | + Name and, optional, path, of the OCI executable binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "/usr/local/bin/my-oci-runtime" + runtimeArgs: + description: | + List of command-line arguments to pass to the runtime when invoked. + type: "array" + x-nullable: true + items: + type: "string" + example: ["--debug", "--systemd-cgroup=false"] + + Commit: + description: | + Commit holds the Git-commit (SHA1) that a binary was built from, as + reported in the version-string of external tools, such as `containerd`, + or `runC`. + type: "object" + properties: + ID: + description: "Actual commit ID of external tool." + type: "string" + example: "cfb82a876ecc11b5ca0977d1733adbe58599088a" + Expected: + description: | + Commit ID of external tool expected by dockerd as set at build time. + type: "string" + example: "2d41c047c83e09a6d61d464906feb2a2f3c52aa4" + + SwarmInfo: + description: | + Represents generic information about swarm. + type: "object" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + default: "" + example: "k67qz4598weg5unwwffg6z1m1" + NodeAddr: + description: | + IP address at which this node can be reached by other nodes in the + swarm. + type: "string" + default: "" + example: "10.0.0.46" + LocalNodeState: + $ref: "#/definitions/LocalNodeState" + ControlAvailable: + type: "boolean" + default: false + example: true + Error: + type: "string" + default: "" + RemoteManagers: + description: | + List of ID's and addresses of other managers in the swarm. + type: "array" + default: null + x-nullable: true + items: + $ref: "#/definitions/PeerNode" + example: + - NodeID: "71izy0goik036k48jg985xnds" + Addr: "10.0.0.158:2377" + - NodeID: "79y6h1o4gv8n120drcprv5nmc" + Addr: "10.0.0.159:2377" + - NodeID: "k67qz4598weg5unwwffg6z1m1" + Addr: "10.0.0.46:2377" + Nodes: + description: "Total number of nodes in the swarm." + type: "integer" + x-nullable: true + example: 4 + Managers: + description: "Total number of managers in the swarm." + type: "integer" + x-nullable: true + example: 3 + Cluster: + $ref: "#/definitions/ClusterInfo" + + LocalNodeState: + description: "Current local status of this node." + type: "string" + default: "" + enum: + - "" + - "inactive" + - "pending" + - "active" + - "error" + - "locked" + example: "active" + + PeerNode: + description: "Represents a peer-node in the swarm" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + Addr: + description: | + IP address and ports at which this node can be reached. + type: "string" + + NetworkAttachmentConfig: + description: | + Specifies how a service should be attached to a particular network. + type: "object" + properties: + Target: + description: | + The target network for attachment. Must be a network name or ID. + type: "string" + Aliases: + description: | + Discoverable alternate names for the service on this network. + type: "array" + items: + type: "string" + DriverOpts: + description: | + Driver attachment options for the network target. + type: "object" + additionalProperties: + type: "string" + +paths: + /containers/json: + get: + summary: "List containers" + description: | + Returns a list of containers. For details on the format, see the + [inspect endpoint](#operation/ContainerInspect). + + Note that it uses a different, smaller representation of a container + than inspecting a single container. For example, the list of linked + containers is not propagated . + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: | + Return all containers. By default, only running containers are shown. + type: "boolean" + default: false + - name: "limit" + in: "query" + description: | + Return this number of most recently created containers, including + non-running ones. + type: "integer" + - name: "size" + in: "query" + description: | + Return the size of container as fields `SizeRw` and `SizeRootFs`. + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a + `map[string][]string`). For example, `{"status": ["paused"]}` will + only return paused containers. + + Available filters: + + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `expose`=(`[/]`|`/[]`) + - `exited=` containers with exit code of `` + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + - `id=` a container's ID + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `is-task=`(`true`|`false`) + - `label=key` or `label="key=value"` of a container label + - `name=` a container's name + - `network`=(`` or ``) + - `publish`=(`[/]`|`/[]`) + - `since`=(`` or ``) + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `volume`=(`` or ``) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: | + Assign the specified name to the container. Must match + `/?[a-zA-Z0-9][a-zA-Z0-9_.-]+`. + type: "string" + pattern: "^/?[a-zA-Z0-9][a-zA-Z0-9_.-]+$" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/ContainerConfig" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + $ref: "#/definitions/NetworkingConfig" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + DeviceRequests: + - Driver: "nvidia" + Count: -1 + DeviceIDs": ["0", "1", "GPU-fef8089b-4820-abfc-e83e-94318197576e"] + Capabilities: [["gpu", "nvidia", "compute"]] + Options: + property1: "string" + property2: "string" + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: 0 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + title: "ContainerCreateResponse" + description: "OK response to ContainerCreate operation" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "ContainerInspectResponse" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + x-nullable: true + $ref: "#/definitions/ContainerState" + Image: + description: "The container's image ID" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + Platform: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + description: "IDs of exec instances that are running in the container." + type: "array" + items: + type: "string" + x-nullable: true + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriverData" + SizeRw: + description: | + The size of files that have been created or changed by this + container. + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/ContainerConfig" + NetworkSettings: + $ref: "#/definitions/NetworkSettings" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Healthcheck: + Test: ["CMD-SHELL", "exit 0"] + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + ExecIDs: + - "b35395de42bc8abd327f9dd65d913b9ba28c74d2f0734eeeae84fa1c616a0fca" + - "3fc1232e5cd20c8de182ed81178503dc6437f4e7ef12b52cc5e8de020652f1c4" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + DeviceRequests: + - Driver: "nvidia" + Count: -1 + DeviceIDs": ["0", "1", "GPU-fef8089b-4820-abfc-e83e-94318197576e"] + Capabilities: [["gpu", "nvidia", "compute"]] + Options: + property1: "string" + property2: "string" + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + Health: + Status: "healthy" + FailingStreak: 0 + Log: + - Start: "2019-12-22T10:59:05.6385933Z" + End: "2019-12-22T10:59:05.8078452Z" + ExitCode: 0 + Output: "" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: | + On Unix systems, this is done by running the `ps` command. This endpoint + is not supported on Windows. + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "ContainerTopResponse" + description: "OK response to ContainerTop operation" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: | + Each process running in the container, where each is process + is an array of values corresponding to the titles. + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or + `journald` logging driver. + operationId: "ContainerLogs" + responses: + 200: + description: | + logs returned as a stream in response body. + For the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + Note that unlike the attach endpoint, the logs endpoint does not + upgrade the connection and does not set Content-Type. + schema: + type: "string" + format: "binary" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: "Keep connection after returning logs." + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "until" + in: "query" + description: "Only return logs before this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: | + Only return this number of log lines from the end of the logs. + Specify as an integer or `all` to output all log lines. + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, + or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: ["application/json"] + responses: + 200: + description: "The list of changes" + schema: + type: "array" + items: + type: "object" + x-go-name: "ContainerChangeResponseItem" + title: "ContainerChangeResponseItem" + description: "change item in response to ContainerChanges operation" + required: [Path, Kind] + properties: + Path: + description: "Path to file that has changed" + type: "string" + x-nullable: false + Kind: + description: "Kind of change" + type: "integer" + format: "uint8" + enum: [0, 1, 2] + x-nullable: false + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage + statistics. + + The `precpu_stats` is the CPU statistic of the *previous* read, and is + used to calculate the CPU usage percentage. It is not an exact copy + of the `cpu_stats` field. + + If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + nil then for compatibility with older daemons the length of the + corresponding `cpu_usage.percpu_usage` array should be used. + + On a cgroup v2 host, the following fields are not set + * `blkio_stats`: all fields other than `io_service_bytes_recursive` + * `cpu_stats`: `cpu_usage.percpu_usage` + * `memory_stats`: `max_usage` and `failcnt` + Also, `memory_stats.stats` fields are incompatible with cgroup v1. + + To calculate the values shown by the `stats` command of the docker cli tool + the following formulas can be used: + * used_memory = `memory_stats.usage - memory_stats.stats.cache` + * available_memory = `memory_stats.limit` + * Memory usage % = `(used_memory / available_memory) * 100.0` + * cpu_delta = `cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage` + * system_cpu_delta = `cpu_stats.system_cpu_usage - precpu_stats.system_cpu_usage` + * number_cpus = `lenght(cpu_stats.cpu_usage.percpu_usage)` or `cpu_stats.online_cpus` + * CPU usage % = `(cpu_delta / system_cpu_delta) * number_cpus * 100.0` + operationId: "ContainerStats" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: | + Stream the output. If false, the stats will be output once and then + it will disconnect. + type: "boolean" + default: true + - name: "one-shot" + in: "query" + description: | + Only get a single stat instead of waiting for 2 cycles. Must be used + with `stream=false`. + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: | + Override the key sequence for detaching a container. Format is a + single character `[a-Z]` or `ctrl-` where `` is one + of: `a-z`, `@`, `^`, `[`, `,` or `_`. + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: | + Send a POSIX signal to a container, defaulting to killing to the + container. + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is not running" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "Container d37cde0fe4ad63c3a7252023b2f9800282894247d145cb5933ddf6e52cc03a28 is not running" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: | + Change various configuration options of a container without having to + recreate it. + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + title: "ContainerUpdateResponse" + description: "OK response to ContainerUpdate operation" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the freezer cgroup to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, + which is observable by the process being suspended. With the freezer + cgroup the process is unaware, and unable to capture, that it is being + suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach + to the same container multiple times and you can reattach to containers + that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint + to do anything. + + See the [documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) + for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, + and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used + for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client + can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will + similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), + the stream over the hijacked connected is multiplexed to separate out + `stdout` and `stderr`. The stream consists of a series of frames, each + containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or + `stderr`). It also contains the size of the associated frame encoded in + the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size + encoded as big endian. + + Following the header is the payload, which is the specified number of + bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), + the stream is not multiplexed. The data exchanged over the hijacked + connection is simply the raw data from the process PTY and client's + `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: | + Override the key sequence for detaching a container.Format is a single + character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, + `@`, `^`, `[`, `,` or `_`. + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you + want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been + returned, it will seamlessly transition into streaming current + output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: | + Stream attached streams from the time the request was made onwards. + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: | + Override the key sequence for detaching a container.Format is a single + character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, + `@`, `^`, `[`, `,`, or `_`. + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + title: "ContainerWaitResponse" + description: "OK response to ContainerWait operation" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + Error: + description: "container waiting error, if any" + type: "object" + properties: + Message: + description: "Details of an error" + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "condition" + in: "query" + description: | + Wait until a container state reaches the given condition, either + 'not-running' (default), 'next-exit', or 'removed'. + type: "string" + default: "not-running" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: | + You cannot remove a running container: c2ada9df5af8. Stop the + container before attempting removal or force remove + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + - name: "link" + in: "query" + description: "Remove the specified link associated with the container." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: | + A response header `X-Docker-Container-Path-Stat` is returned, containing + a base64 - encoded JSON object with some filesystem header information + about the path. + operationId: "ContainerArchiveInfo" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: | + A base64 - encoded JSON object with some filesystem header + information about the path + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: | + The error message. Either "must specify path parameter" + (path cannot be empty) or "not a directory" (path was + asserted to be a directory but exists as a file). + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get a tar archive of a resource in the filesystem of container id." + operationId: "ContainerArchive" + produces: ["application/x-tar"] + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: | + The error message. Either "must specify path parameter" + (path cannot be empty) or "not a directory" (path was + asserted to be a directory but exists as a file). + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "PutContainerArchive" + consumes: ["application/x-tar", "application/octet-stream"] + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: | + If `1`, `true`, or `True` then it will be an error if unpacking the + given content would cause an existing directory to be replaced with + a non-directory and vice versa. + type: "string" + - name: "copyUIDGID" + in: "query" + description: | + If `1`, `true`, then it will copy UID/GID maps to the dest file or + dir + type: "string" + - name: "inputStream" + in: "body" + required: true + description: | + The input stream must be a tar archive compressed with one of the + following algorithms: `identity` (no compression), `gzip`, `bzip2`, + or `xz`. + schema: + type: "string" + format: "binary" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune containers created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune containers with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ContainerPruneResponse" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the images list. + + Available filters: + + - `before`=(`[:]`, `` or ``) + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `reference`=(`[:]`) + - `since`=(`[:]`, `` or ``) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "extrahosts" + in: "query" + description: "Extra hosts to add to /etc/hosts" + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: > + JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker + uses the buildargs as the environment context for commands run via the `Dockerfile` RUN + instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for + passing secret values. + + + For example, the build arg `FOO=bar` would become `{"FOO":"bar"}` in JSON. This would result in the + query parameter `buildargs={"FOO":"bar"}`. Note that `{"FOO":"bar"}` should be URI component encoded. + + + [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg) + type: "string" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: | + Sets the networking mode for the run commands during build. Supported + standard values are: `bridge`, `host`, `none`, and `container:`. + Any other value is taken as a custom network's name or ID to which this + container should connect to. + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/x-tar" + default: "application/x-tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + - name: "target" + in: "query" + description: "Target build stage" + type: "string" + default: "" + - name: "outputs" + in: "query" + description: "BuildKit output configuration" + type: "string" + default: "" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /build/prune: + post: + summary: "Delete builder cache" + produces: + - "application/json" + operationId: "BuildPrune" + parameters: + - name: "keep-storage" + in: "query" + description: "Amount of disk space in bytes to keep for cache" + type: "integer" + format: "int64" + - name: "all" + in: "query" + type: "boolean" + description: "Remove all types of build cache" + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the list of build cache objects. + + Available filters: + + - `until=`: duration relative to daemon's time, during which build cache was not used, in Go's duration format (e.g., '24h') + - `id=` + - `parent=` + - `type=` + - `description=` + - `inuse` + - `shared` + - `private` + responses: + 200: + description: "No error" + schema: + type: "object" + title: "BuildPruneResponse" + properties: + CachesDeleted: + type: "array" + items: + description: "ID of build cache object" + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "message" + in: "query" + description: "Set commit message for imported image." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + Data: {} + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: ["application/json"] + responses: + 200: + description: "List of image layers" + schema: + type: "array" + items: + type: "object" + x-go-name: HistoryResponseItem + title: "HistoryResponseItem" + description: "individual image layer information in response to ImageHistory operation" + required: [Id, Created, CreatedBy, Tags, Size, Comment] + properties: + Id: + type: "string" + x-nullable: false + Created: + type: "integer" + format: "int64" + x-nullable: false + CreatedBy: + type: "string" + x-nullable: false + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + Comment: + type: "string" + x-nullable: false + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must + already have a tag which references the registry. For example, + `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were + referenced by that image. + + Images can't be removed if they have descendant images, are being + used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: ["application/json"] + responses: + 200: + description: "The image was deleted successfully" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + title: "ImageSearchResponseItem" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `is-automated=(true|false)` + - `is-official=(true|false)` + - `stars=` Matches images that has at least 'number' stars. + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + - `until=` Prune images created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune images with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ImagePruneResponse" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: | + Validate credentials for a registry and, if available, get an identity + token for accessing the registry without password. + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + title: "SystemAuthResponse" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/SystemInfo" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/SystemVersion" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Builder-Version: + type: "string" + description: "Default version of docker image builder" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + Cache-Control: + type: "string" + default: "no-cache, no-store, must-revalidate" + Pragma: + type: "string" + default: "no-cache" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + headers: + Cache-Control: + type: "string" + default: "no-cache, no-store, must-revalidate" + Pragma: + type: "string" + default: "no-cache" + tags: ["System"] + head: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPingHead" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "(empty)" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Builder-Version: + type: "string" + description: "Default version of docker image builder" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + Cache-Control: + type: "string" + default: "no-cache, no-store, must-revalidate" + Pragma: + type: "string" + default: "no-cache" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/ContainerConfig" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `exec_die`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, `update`, and `prune` + + Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, `untag`, and `prune` + + Volumes report these events: `create`, `mount`, `unmount`, `destroy`, and `prune` + + Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, `remove`, and `prune` + + The Docker daemon reports these events: `reload` + + Services report these events: `create`, `update`, and `remove` + + Nodes report these events: `create`, `update`, and `remove` + + Secrets report these events: `create`, `update`, and `remove` + + Configs report these events: `create`, `update`, and `remove` + + The Builder reports `prune` events + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemEventsResponse" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `config=` config name or ID + - `container=` container name or ID + - `daemon=` daemon name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `network=` network name or ID + - `node=` node ID + - `plugin`= plugin name or ID + - `scope`= local or swarm + - `secret=` secret name or ID + - `service=` service name or ID + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service`, `secret` or `config` + - `volume=` volume name + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemDataUsageResponse" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + BuildCache: + type: "array" + items: + $ref: "#/definitions/BuildCache" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/my-volume/_data" + Labels: null + Scope: "local" + Options: null + UsageData: + Size: 10920104 + RefCount: 2 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image + repositories. + + For each value of the `names` parameter: if it is a specific name and + tag (e.g. `ubuntu:latest`), then only that image (and its parents) are + returned; if it is an image ID, similarly only that image (and its parents) + are returned and there would be no names referenced in the 'repositories' + file for this image ID. + + For details on the format, see the [export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see the [export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: | + Override the key sequence for detaching a container. Format is + a single character `[a-Z]` or `ctrl-` where `` + is one of: `a-z`, `@`, `^`, `[`, `,` or `_`. + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: | + A list of environment variables in the form `["VAR=value", ...]`. + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: | + The user, and optionally, group to run the exec process inside + the container. Format is one of: `user`, `user:group`, `uid`, + or `uid:gid`. + WorkingDir: + type: "string" + description: | + The working directory for the exec process inside the container. + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: | + Starts a previously set up exec instance. If detach is true, this endpoint + returns immediately after starting the command. Otherwise, it sets up an + interactive session with the command. + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: | + Resize the TTY session used by an exec instance. This endpoint only works + if `tty` was specified as part of creating and starting the exec instance. + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ExecInspectResponse" + properties: + CanRemove: + type: "boolean" + DetachKeys: + type: "string" + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + title: "VolumeListResponse" + description: "Volume list response" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: | + Warnings that occurred when fetching the list of volumes. + items: + type: "string" + + examples: + application/json: + Volumes: + - CreatedAt: "2017-07-19T12:00:26Z" + Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches volumes based on their driver. + - `label=` or `label=:` Matches volumes based on + the presence of a `label` alone or a `label` and a value. + - `name=` Matches all or part of a volume name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + description: "Volume configuration" + title: "VolumeConfig" + properties: + Name: + description: | + The new volume's name. If not specified, Docker generates a name. + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: | + A mapping of driver options and values. These options are + passed directly to the driver and are driver specific. + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune volumes with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "VolumePruneResponse" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + description: | + Returns a list of networks. For details on the format, see the + [network inspect endpoint](#operation/NetworkInspect). + + Note that it uses a different, smaller representation of a network than + inspecting a single network. For example, the list of containers attached + to the network is not propagated in API versions 1.28 and up. + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process + on the networks list. + + Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + networks that are not in use by a container. When set to `false` + (or `0`), only networks that are in use by one or more + containers are returned. + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `scope=["swarm"|"global"|"local"]` Filters networks by scope (`swarm`, `global`, or `local`). + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "verbose" + in: "query" + description: "Detailed inspect output for troubleshooting" + type: "boolean" + default: false + - name: "scope" + in: "query" + description: "Filter the network by scope (swarm, global, or local)" + type: "string" + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + title: "NetworkCreateResponse" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: | + Check for networks with duplicate names. Since Network is + primarily keyed based on a random ID and not on the name, and + network name is strictly a user-friendly alias to the network + which is uniquely identified using ID, there is no guaranteed + way to check for duplicates. CheckDuplicate is there to provide + a best effort checking of any networks which has the same name + but it is not guaranteed to catch all name collisions. + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + Attachable: + description: | + Globally scoped network is manually attachable by regular + containers from workers in swarm mode. + type: "boolean" + Ingress: + description: | + Ingress network is the network which provides the routing-mesh + in swarm mode. + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Attachable: false + Ingress: false + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: | + The ID or name of the container to disconnect from the network. + Force: + type: "boolean" + description: | + Force the container to disconnect from the network. + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune networks with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "NetworkPruneResponse" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the plugin list. + + Available filters: + + - `capability=` + - `enable=|` + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: | + Describes a permission the user has to accept upon installing + the plugin. + type: "object" + title: "PluginPrivilegeItem" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be + enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration to use when pulling a plugin + from a registry. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: | + Describes a permission accepted by the user upon installing the + plugin. + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "force" + in: "query" + description: | + Disable the plugin before removing. This may result in issues if the + plugin is in use by a container. + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration to use when pulling a plugin + from a registry. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: | + Describes a permission accepted by the user upon installing the + plugin. + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `node.label=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: | + The version number of the node object being updated. This is required + to avoid conflicting writes. + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Swarm" + 404: + description: "no such swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: | + Listen address used for inter-manager communication, as well + as determining the networking interface used for the VXLAN + Tunnel Endpoint (VTEP). This can either be an address/port + combination in the form `192.168.1.1:4567`, or an interface + followed by a port number, like `eth0:4567`. If the port number + is omitted, the default swarm listening port is used. + type: "string" + AdvertiseAddr: + description: | + Externally reachable address advertised to other nodes. This + can either be an address/port combination in the form + `192.168.1.1:4567`, or an interface followed by a port number, + like `eth0:4567`. If the port number is omitted, the port + number from the listen address is used. If `AdvertiseAddr` is + not specified, it will be automatically detected when possible. + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: + ``), for example, `192.168.1.1`, or an interface, + like `eth0`. If `DataPathAddr` is unspecified, the same address + as `AdvertiseAddr` is used. + + The `DataPathAddr` specifies the address that global scope + network drivers will publish towards other nodes in order to + reach the containers running on this node. Using this parameter + it is possible to separate the container data traffic from the + management traffic of the cluster. + type: "string" + DataPathPort: + description: | + DataPathPort specifies the data path port number for data traffic. + Acceptable port range is 1024 to 49151. + if no port is set or is set to 0, default port 4789 will be used. + type: "integer" + format: "uint32" + DefaultAddrPool: + description: | + Default Address Pool specifies default subnet pools for global + scope networks. + type: "array" + items: + type: "string" + example: ["10.10.0.0/16", "20.20.0.0/16"] + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + SubnetSize: + description: | + SubnetSize specifies the subnet size of the networks created + from the default subnet pool. + type: "integer" + format: "uint32" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + DataPathPort: 4789 + DefaultAddrPool: ["10.10.0.0/8", "20.20.0.0/8"] + SubnetSize: 24 + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: | + Listen address used for inter-manager communication if the node + gets promoted to manager, as well as determining the networking + interface used for the VXLAN Tunnel Endpoint (VTEP). + type: "string" + AdvertiseAddr: + description: | + Externally reachable address advertised to other nodes. This + can either be an address/port combination in the form + `192.168.1.1:4567`, or an interface followed by a port number, + like `eth0:4567`. If the port number is omitted, the port + number from the listen address is used. If `AdvertiseAddr` is + not specified, it will be automatically detected when possible. + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: + ``), for example, `192.168.1.1`, or an interface, + like `eth0`. If `DataPathAddr` is unspecified, the same addres + as `AdvertiseAddr` is used. + + The `DataPathAddr` specifies the address that global scope + network drivers will publish towards other nodes in order to + reach the containers running on this node. Using this parameter + it is possible to separate the container data traffic from the + management traffic of the cluster. + + type: "string" + RemoteAddrs: + description: | + Addresses of manager nodes already participating in the swarm. + type: "array" + items: + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: | + Force leave swarm, even if this is the last manager or that it will + break the cluster. + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: | + The version number of the swarm object being updated. This is + required to avoid conflicting writes. + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "UnlockKeyResponse" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the services list. + + Available filters: + + - `id=` + - `label=` + - `mode=["replicated"|"global"]` + - `name=` + - name: "status" + in: "query" + type: "boolean" + description: | + Include service status, with count of running and desired tasks. + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + title: "ServiceCreateResponse" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + Hosts: ["10.10.10.10 host1", "ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 host2"] + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + Secrets: + - + File: + Name: "www.example.org.key" + UID: "33" + GID: "33" + Mode: 384 + SecretID: "fpjqlhnwb19zds35k8wn80lq9" + SecretName: "example_org_domain_key" + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration for pulling from private + registries. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "insertDefaults" + in: "query" + description: "Fill empty fields with default values." + type: "boolean" + default: false + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ServiceUpdateResponse" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: | + The version number of the service object being updated. This is + required to avoid conflicting writes. + This version number should be the value as currently set on the + service *before* the update. You can find the current version by + calling `GET /services/{id}` + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + description: | + If the `X-Registry-Auth` header is not specified, this parameter + indicates where to find registry authorization credentials. + type: "string" + enum: ["spec", "previous-spec"] + default: "spec" + - name: "rollback" + in: "query" + description: | + Set to this parameter to `previous` to cause a server-side rollback + to the previous service spec. The supplied spec will be ignored in + this case. + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration for pulling from private + registries. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. See also + [`/containers/{id}/logs`](#operation/ContainerLogs). + + **Note**: This endpoint works only for services with the `local`, + `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + responses: + 200: + description: "logs returned as a stream in response body" + schema: + type: "string" + format: "binary" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such service: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the service" + type: "string" + - name: "details" + in: "query" + description: "Show service context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: "Keep connection after returning logs." + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: | + Only return this number of log lines from the end of the logs. + Specify as an integer or `all` to output all log lines. + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the tasks list. + + Available filters: + + - `desired-state=(running | shutdown | accepted)` + - `id=` + - `label=key` or `label="key=value"` + - `name=` + - `node=` + - `service=` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /tasks/{id}/logs: + get: + summary: "Get task logs" + description: | + Get `stdout` and `stderr` logs from a task. + See also [`/containers/{id}/logs`](#operation/ContainerLogs). + + **Note**: This endpoint works only for services with the `local`, + `json-file` or `journald` logging drivers. + operationId: "TaskLogs" + responses: + 200: + description: "logs returned as a stream in response body" + schema: + type: "string" + format: "binary" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such task: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID of the task" + type: "string" + - name: "details" + in: "query" + description: "Show task context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: "Keep connection after returning logs." + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: | + Only return this number of log lines from the end of the logs. + Specify as an integer or `all` to output all log lines. + type: "string" + default: "all" + tags: ["Task"] + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "blt1owaxmitz71s9v5zh81zun" + Version: + Index: 85 + CreatedAt: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: "2017-07-20T13:55:28.678958722Z" + Spec: + Name: "mysql-passwd" + Labels: + some.label: "some.value" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the secrets list. + + Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: | + The spec of the secret to update. Currently, only the Labels field + can be updated. All other fields must remain unchanged from the + [SecretInspect endpoint](#operation/SecretInspect) response values. + - name: "version" + in: "query" + description: | + The version number of the secret object being updated. This is + required to avoid conflicting writes. + type: "integer" + format: "int64" + required: true + tags: ["Secret"] + /configs: + get: + summary: "List configs" + operationId: "ConfigList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Config" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "server.conf" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the configs list. + + Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Config"] + /configs/create: + post: + summary: "Create a config" + operationId: "ConfigCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/ConfigSpec" + - type: "object" + example: + Name: "server.conf" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Config"] + /configs/{id}: + get: + summary: "Inspect a config" + operationId: "ConfigInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Config" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + delete: + summary: "Delete a config" + operationId: "ConfigDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + /configs/{id}/update: + post: + summary: "Update a Config" + operationId: "ConfigUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such config" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the config" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/ConfigSpec" + description: | + The spec of the config to update. Currently, only the Labels field + can be updated. All other fields must remain unchanged from the + [ConfigInspect endpoint](#operation/ConfigInspect) response values. + - name: "version" + in: "query" + description: | + The version number of the config object being updated. This is + required to avoid conflicting writes. + type: "integer" + format: "int64" + required: true + tags: ["Config"] + /distribution/{name}/json: + get: + summary: "Get image information from the registry" + description: | + Return image digest and platform information by contacting the registry. + operationId: "DistributionInspect" + produces: + - "application/json" + responses: + 200: + description: "descriptor and platform information" + schema: + type: "object" + x-go-name: DistributionInspect + title: "DistributionInspectResponse" + required: [Descriptor, Platforms] + properties: + Descriptor: + type: "object" + description: | + A descriptor struct containing digest, media type, and size. + properties: + MediaType: + type: "string" + Size: + type: "integer" + format: "int64" + Digest: + type: "string" + URLs: + type: "array" + items: + type: "string" + Platforms: + type: "array" + description: | + An array containing all platforms supported by the image. + items: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + OSVersion: + type: "string" + OSFeatures: + type: "array" + items: + type: "string" + Variant: + type: "string" + Features: + type: "array" + items: + type: "string" + examples: + application/json: + Descriptor: + MediaType: "application/vnd.docker.distribution.manifest.v2+json" + Digest: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" + Size: 3987495 + URLs: + - "" + Platforms: + - Architecture: "amd64" + OS: "linux" + OSVersion: "" + OSFeatures: + - "" + Variant: "" + Features: + - "" + 401: + description: "Failed authentication or no image found" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Distribution"] + /session: + post: + summary: "Initialize interactive session" + description: | + Start a new interactive session with a server. Session allows server to + call back to the client for advanced capabilities. + + ### Hijacking + + This endpoint hijacks the HTTP connection to HTTP2 transport that allows + the client to expose gPRC services on that connection. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /session HTTP/1.1 + Upgrade: h2c + Connection: Upgrade + ``` + + The Docker daemon responds with a `101 UPGRADED` response follow with + the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Connection: Upgrade + Upgrade: h2c + ``` + operationId: "Session" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hijacking successful" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Session"] diff --git a/vendor/github.com/docker/docker/api/types/auth.go b/vendor/github.com/docker/docker/api/types/auth.go new file mode 100644 index 0000000000..ddf15bb182 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/auth.go @@ -0,0 +1,22 @@ +package types // import "github.com/docker/docker/api/types" + +// AuthConfig contains authorization information for connecting to a Registry +type AuthConfig struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Auth string `json:"auth,omitempty"` + + // Email is an optional value associated with the username. + // This field is deprecated and will be removed in a later + // version of docker. + Email string `json:"email,omitempty"` + + ServerAddress string `json:"serveraddress,omitempty"` + + // IdentityToken is used to authenticate the user and get + // an access token for the registry. + IdentityToken string `json:"identitytoken,omitempty"` + + // RegistryToken is a bearer token to be sent to a registry + RegistryToken string `json:"registrytoken,omitempty"` +} diff --git a/vendor/github.com/docker/docker/api/types/blkiodev/blkio.go b/vendor/github.com/docker/docker/api/types/blkiodev/blkio.go new file mode 100644 index 0000000000..bf3463b90e --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/blkiodev/blkio.go @@ -0,0 +1,23 @@ +package blkiodev // import "github.com/docker/docker/api/types/blkiodev" + +import "fmt" + +// WeightDevice is a structure that holds device:weight pair +type WeightDevice struct { + Path string + Weight uint16 +} + +func (w *WeightDevice) String() string { + return fmt.Sprintf("%s:%d", w.Path, w.Weight) +} + +// ThrottleDevice is a structure that holds device:rate_per_second pair +type ThrottleDevice struct { + Path string + Rate uint64 +} + +func (t *ThrottleDevice) String() string { + return fmt.Sprintf("%s:%d", t.Path, t.Rate) +} diff --git a/vendor/github.com/docker/docker/api/types/client.go b/vendor/github.com/docker/docker/api/types/client.go new file mode 100644 index 0000000000..9c464b73e2 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/client.go @@ -0,0 +1,419 @@ +package types // import "github.com/docker/docker/api/types" + +import ( + "bufio" + "io" + "net" + + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/filters" + units "github.com/docker/go-units" +) + +// CheckpointCreateOptions holds parameters to create a checkpoint from a container +type CheckpointCreateOptions struct { + CheckpointID string + CheckpointDir string + Exit bool +} + +// CheckpointListOptions holds parameters to list checkpoints for a container +type CheckpointListOptions struct { + CheckpointDir string +} + +// CheckpointDeleteOptions holds parameters to delete a checkpoint from a container +type CheckpointDeleteOptions struct { + CheckpointID string + CheckpointDir string +} + +// ContainerAttachOptions holds parameters to attach to a container. +type ContainerAttachOptions struct { + Stream bool + Stdin bool + Stdout bool + Stderr bool + DetachKeys string + Logs bool +} + +// ContainerCommitOptions holds parameters to commit changes into a container. +type ContainerCommitOptions struct { + Reference string + Comment string + Author string + Changes []string + Pause bool + Config *container.Config +} + +// ContainerExecInspect holds information returned by exec inspect. +type ContainerExecInspect struct { + ExecID string `json:"ID"` + ContainerID string + Running bool + ExitCode int + Pid int +} + +// ContainerListOptions holds parameters to list containers with. +type ContainerListOptions struct { + Quiet bool + Size bool + All bool + Latest bool + Since string + Before string + Limit int + Filters filters.Args +} + +// ContainerLogsOptions holds parameters to filter logs with. +type ContainerLogsOptions struct { + ShowStdout bool + ShowStderr bool + Since string + Until string + Timestamps bool + Follow bool + Tail string + Details bool +} + +// ContainerRemoveOptions holds parameters to remove containers. +type ContainerRemoveOptions struct { + RemoveVolumes bool + RemoveLinks bool + Force bool +} + +// ContainerStartOptions holds parameters to start containers. +type ContainerStartOptions struct { + CheckpointID string + CheckpointDir string +} + +// CopyToContainerOptions holds information +// about files to copy into a container +type CopyToContainerOptions struct { + AllowOverwriteDirWithFile bool + CopyUIDGID bool +} + +// EventsOptions holds parameters to filter events with. +type EventsOptions struct { + Since string + Until string + Filters filters.Args +} + +// NetworkListOptions holds parameters to filter the list of networks with. +type NetworkListOptions struct { + Filters filters.Args +} + +// HijackedResponse holds connection information for a hijacked request. +type HijackedResponse struct { + Conn net.Conn + Reader *bufio.Reader +} + +// Close closes the hijacked connection and reader. +func (h *HijackedResponse) Close() { + h.Conn.Close() +} + +// CloseWriter is an interface that implements structs +// that close input streams to prevent from writing. +type CloseWriter interface { + CloseWrite() error +} + +// CloseWrite closes a readWriter for writing. +func (h *HijackedResponse) CloseWrite() error { + if conn, ok := h.Conn.(CloseWriter); ok { + return conn.CloseWrite() + } + return nil +} + +// ImageBuildOptions holds the information +// necessary to build images. +type ImageBuildOptions struct { + Tags []string + SuppressOutput bool + RemoteContext string + NoCache bool + Remove bool + ForceRemove bool + PullParent bool + Isolation container.Isolation + CPUSetCPUs string + CPUSetMems string + CPUShares int64 + CPUQuota int64 + CPUPeriod int64 + Memory int64 + MemorySwap int64 + CgroupParent string + NetworkMode string + ShmSize int64 + Dockerfile string + Ulimits []*units.Ulimit + // BuildArgs needs to be a *string instead of just a string so that + // we can tell the difference between "" (empty string) and no value + // at all (nil). See the parsing of buildArgs in + // api/server/router/build/build_routes.go for even more info. + BuildArgs map[string]*string + AuthConfigs map[string]AuthConfig + Context io.Reader + Labels map[string]string + // squash the resulting image's layers to the parent + // preserves the original image and creates a new one from the parent with all + // the changes applied to a single layer + Squash bool + // CacheFrom specifies images that are used for matching cache. Images + // specified here do not need to have a valid parent chain to match cache. + CacheFrom []string + SecurityOpt []string + ExtraHosts []string // List of extra hosts + Target string + SessionID string + Platform string + // Version specifies the version of the unerlying builder to use + Version BuilderVersion + // BuildID is an optional identifier that can be passed together with the + // build request. The same identifier can be used to gracefully cancel the + // build with the cancel request. + BuildID string + // Outputs defines configurations for exporting build results. Only supported + // in BuildKit mode + Outputs []ImageBuildOutput +} + +// ImageBuildOutput defines configuration for exporting a build result +type ImageBuildOutput struct { + Type string + Attrs map[string]string +} + +// BuilderVersion sets the version of underlying builder to use +type BuilderVersion string + +const ( + // BuilderV1 is the first generation builder in docker daemon + BuilderV1 BuilderVersion = "1" + // BuilderBuildKit is builder based on moby/buildkit project + BuilderBuildKit BuilderVersion = "2" +) + +// ImageBuildResponse holds information +// returned by a server after building +// an image. +type ImageBuildResponse struct { + Body io.ReadCloser + OSType string +} + +// ImageCreateOptions holds information to create images. +type ImageCreateOptions struct { + RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry. + Platform string // Platform is the target platform of the image if it needs to be pulled from the registry. +} + +// ImageImportSource holds source information for ImageImport +type ImageImportSource struct { + Source io.Reader // Source is the data to send to the server to create this image from. You must set SourceName to "-" to leverage this. + SourceName string // SourceName is the name of the image to pull. Set to "-" to leverage the Source attribute. +} + +// ImageImportOptions holds information to import images from the client host. +type ImageImportOptions struct { + Tag string // Tag is the name to tag this image with. This attribute is deprecated. + Message string // Message is the message to tag the image with + Changes []string // Changes are the raw changes to apply to this image + Platform string // Platform is the target platform of the image +} + +// ImageListOptions holds parameters to filter the list of images with. +type ImageListOptions struct { + All bool + Filters filters.Args +} + +// ImageLoadResponse returns information to the client about a load process. +type ImageLoadResponse struct { + // Body must be closed to avoid a resource leak + Body io.ReadCloser + JSON bool +} + +// ImagePullOptions holds information to pull images. +type ImagePullOptions struct { + All bool + RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry + PrivilegeFunc RequestPrivilegeFunc + Platform string +} + +// RequestPrivilegeFunc is a function interface that +// clients can supply to retry operations after +// getting an authorization error. +// This function returns the registry authentication +// header value in base 64 format, or an error +// if the privilege request fails. +type RequestPrivilegeFunc func() (string, error) + +// ImagePushOptions holds information to push images. +type ImagePushOptions ImagePullOptions + +// ImageRemoveOptions holds parameters to remove images. +type ImageRemoveOptions struct { + Force bool + PruneChildren bool +} + +// ImageSearchOptions holds parameters to search images with. +type ImageSearchOptions struct { + RegistryAuth string + PrivilegeFunc RequestPrivilegeFunc + Filters filters.Args + Limit int +} + +// ResizeOptions holds parameters to resize a tty. +// It can be used to resize container ttys and +// exec process ttys too. +type ResizeOptions struct { + Height uint + Width uint +} + +// NodeListOptions holds parameters to list nodes with. +type NodeListOptions struct { + Filters filters.Args +} + +// NodeRemoveOptions holds parameters to remove nodes with. +type NodeRemoveOptions struct { + Force bool +} + +// ServiceCreateOptions contains the options to use when creating a service. +type ServiceCreateOptions struct { + // EncodedRegistryAuth is the encoded registry authorization credentials to + // use when updating the service. + // + // This field follows the format of the X-Registry-Auth header. + EncodedRegistryAuth string + + // QueryRegistry indicates whether the service update requires + // contacting a registry. A registry may be contacted to retrieve + // the image digest and manifest, which in turn can be used to update + // platform or other information about the service. + QueryRegistry bool +} + +// ServiceCreateResponse contains the information returned to a client +// on the creation of a new service. +type ServiceCreateResponse struct { + // ID is the ID of the created service. + ID string + // Warnings is a set of non-fatal warning messages to pass on to the user. + Warnings []string `json:",omitempty"` +} + +// Values for RegistryAuthFrom in ServiceUpdateOptions +const ( + RegistryAuthFromSpec = "spec" + RegistryAuthFromPreviousSpec = "previous-spec" +) + +// ServiceUpdateOptions contains the options to be used for updating services. +type ServiceUpdateOptions struct { + // EncodedRegistryAuth is the encoded registry authorization credentials to + // use when updating the service. + // + // This field follows the format of the X-Registry-Auth header. + EncodedRegistryAuth string + + // TODO(stevvooe): Consider moving the version parameter of ServiceUpdate + // into this field. While it does open API users up to racy writes, most + // users may not need that level of consistency in practice. + + // RegistryAuthFrom specifies where to find the registry authorization + // credentials if they are not given in EncodedRegistryAuth. Valid + // values are "spec" and "previous-spec". + RegistryAuthFrom string + + // Rollback indicates whether a server-side rollback should be + // performed. When this is set, the provided spec will be ignored. + // The valid values are "previous" and "none". An empty value is the + // same as "none". + Rollback string + + // QueryRegistry indicates whether the service update requires + // contacting a registry. A registry may be contacted to retrieve + // the image digest and manifest, which in turn can be used to update + // platform or other information about the service. + QueryRegistry bool +} + +// ServiceListOptions holds parameters to list services with. +type ServiceListOptions struct { + Filters filters.Args + + // Status indicates whether the server should include the service task + // count of running and desired tasks. + Status bool +} + +// ServiceInspectOptions holds parameters related to the "service inspect" +// operation. +type ServiceInspectOptions struct { + InsertDefaults bool +} + +// TaskListOptions holds parameters to list tasks with. +type TaskListOptions struct { + Filters filters.Args +} + +// PluginRemoveOptions holds parameters to remove plugins. +type PluginRemoveOptions struct { + Force bool +} + +// PluginEnableOptions holds parameters to enable plugins. +type PluginEnableOptions struct { + Timeout int +} + +// PluginDisableOptions holds parameters to disable plugins. +type PluginDisableOptions struct { + Force bool +} + +// PluginInstallOptions holds parameters to install a plugin. +type PluginInstallOptions struct { + Disabled bool + AcceptAllPermissions bool + RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry + RemoteRef string // RemoteRef is the plugin name on the registry + PrivilegeFunc RequestPrivilegeFunc + AcceptPermissionsFunc func(PluginPrivileges) (bool, error) + Args []string +} + +// SwarmUnlockKeyResponse contains the response for Engine API: +// GET /swarm/unlockkey +type SwarmUnlockKeyResponse struct { + // UnlockKey is the unlock key in ASCII-armored format. + UnlockKey string +} + +// PluginCreateOptions hold all options to plugin create. +type PluginCreateOptions struct { + RepoName string +} diff --git a/vendor/github.com/docker/docker/api/types/configs.go b/vendor/github.com/docker/docker/api/types/configs.go new file mode 100644 index 0000000000..3dd133a3a5 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/configs.go @@ -0,0 +1,66 @@ +package types // import "github.com/docker/docker/api/types" + +import ( + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/network" + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +// configs holds structs used for internal communication between the +// frontend (such as an http server) and the backend (such as the +// docker daemon). + +// ContainerCreateConfig is the parameter set to ContainerCreate() +type ContainerCreateConfig struct { + Name string + Config *container.Config + HostConfig *container.HostConfig + NetworkingConfig *network.NetworkingConfig + Platform *specs.Platform + AdjustCPUShares bool +} + +// ContainerRmConfig holds arguments for the container remove +// operation. This struct is used to tell the backend what operations +// to perform. +type ContainerRmConfig struct { + ForceRemove, RemoveVolume, RemoveLink bool +} + +// ExecConfig is a small subset of the Config struct that holds the configuration +// for the exec feature of docker. +type ExecConfig struct { + User string // User that will run the command + Privileged bool // Is the container in privileged mode + Tty bool // Attach standard streams to a tty. + AttachStdin bool // Attach the standard input, makes possible user interaction + AttachStderr bool // Attach the standard error + AttachStdout bool // Attach the standard output + Detach bool // Execute in detach mode + DetachKeys string // Escape keys for detach + Env []string // Environment variables + WorkingDir string // Working directory + Cmd []string // Execution commands and args +} + +// PluginRmConfig holds arguments for plugin remove. +type PluginRmConfig struct { + ForceRemove bool +} + +// PluginEnableConfig holds arguments for plugin enable +type PluginEnableConfig struct { + Timeout int +} + +// PluginDisableConfig holds arguments for plugin disable. +type PluginDisableConfig struct { + ForceDisable bool +} + +// NetworkListConfig stores the options available for listing networks +type NetworkListConfig struct { + // TODO(@cpuguy83): naming is hard, this is pulled from what was being used in the router before moving here + Detailed bool + Verbose bool +} diff --git a/vendor/github.com/docker/docker/api/types/container/config.go b/vendor/github.com/docker/docker/api/types/container/config.go new file mode 100644 index 0000000000..f767195b94 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/container/config.go @@ -0,0 +1,69 @@ +package container // import "github.com/docker/docker/api/types/container" + +import ( + "time" + + "github.com/docker/docker/api/types/strslice" + "github.com/docker/go-connections/nat" +) + +// MinimumDuration puts a minimum on user configured duration. +// This is to prevent API error on time unit. For example, API may +// set 3 as healthcheck interval with intention of 3 seconds, but +// Docker interprets it as 3 nanoseconds. +const MinimumDuration = 1 * time.Millisecond + +// HealthConfig holds configuration settings for the HEALTHCHECK feature. +type HealthConfig struct { + // Test is the test to perform to check that the container is healthy. + // An empty slice means to inherit the default. + // The options are: + // {} : inherit healthcheck + // {"NONE"} : disable healthcheck + // {"CMD", args...} : exec arguments directly + // {"CMD-SHELL", command} : run command with system's default shell + Test []string `json:",omitempty"` + + // Zero means to inherit. Durations are expressed as integer nanoseconds. + Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks. + Timeout time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung. + StartPeriod time.Duration `json:",omitempty"` // The start period for the container to initialize before the retries starts to count down. + + // Retries is the number of consecutive failures needed to consider a container as unhealthy. + // Zero means inherit. + Retries int `json:",omitempty"` +} + +// Config contains the configuration data about a container. +// It should hold only portable information about the container. +// Here, "portable" means "independent from the host we are running on". +// Non-portable information *should* appear in HostConfig. +// All fields added to this struct must be marked `omitempty` to keep getting +// predictable hashes from the old `v1Compatibility` configuration. +type Config struct { + Hostname string // Hostname + Domainname string // Domainname + User string // User that will run the command(s) inside the container, also support user:group + AttachStdin bool // Attach the standard input, makes possible user interaction + AttachStdout bool // Attach the standard output + AttachStderr bool // Attach the standard error + ExposedPorts nat.PortSet `json:",omitempty"` // List of exposed ports + Tty bool // Attach standard streams to a tty, including stdin if it is not closed. + OpenStdin bool // Open stdin + StdinOnce bool // If true, close stdin after the 1 attached client disconnects. + Env []string // List of environment variable to set in the container + Cmd strslice.StrSlice // Command to run when starting the container + Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy + ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (meaning treat as a command line) (Windows specific). + Image string // Name of the image as it was passed by the operator (e.g. could be symbolic) + Volumes map[string]struct{} // List of volumes (mounts) used for the container + WorkingDir string // Current directory (PWD) in the command will be launched + Entrypoint strslice.StrSlice // Entrypoint to run when starting the container + NetworkDisabled bool `json:",omitempty"` // Is network disabled + MacAddress string `json:",omitempty"` // Mac Address of the container + OnBuild []string // ONBUILD metadata that were defined on the image Dockerfile + Labels map[string]string // List of labels set to this container + StopSignal string `json:",omitempty"` // Signal to stop a container + StopTimeout *int `json:",omitempty"` // Timeout (in seconds) to stop a container + Shell strslice.StrSlice `json:",omitempty"` // Shell for shell-form of RUN, CMD, ENTRYPOINT +} diff --git a/vendor/github.com/docker/docker/api/types/container/container_changes.go b/vendor/github.com/docker/docker/api/types/container/container_changes.go new file mode 100644 index 0000000000..16dd5019ee --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/container/container_changes.go @@ -0,0 +1,20 @@ +package container // import "github.com/docker/docker/api/types/container" + +// ---------------------------------------------------------------------------- +// Code generated by `swagger generate operation`. DO NOT EDIT. +// +// See hack/generate-swagger-api.sh +// ---------------------------------------------------------------------------- + +// ContainerChangeResponseItem change item in response to ContainerChanges operation +// swagger:model ContainerChangeResponseItem +type ContainerChangeResponseItem struct { + + // Kind of change + // Required: true + Kind uint8 `json:"Kind"` + + // Path to file that has changed + // Required: true + Path string `json:"Path"` +} diff --git a/vendor/github.com/docker/docker/api/types/container/container_create.go b/vendor/github.com/docker/docker/api/types/container/container_create.go new file mode 100644 index 0000000000..d0c852f84d --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/container/container_create.go @@ -0,0 +1,20 @@ +package container // import "github.com/docker/docker/api/types/container" + +// ---------------------------------------------------------------------------- +// Code generated by `swagger generate operation`. DO NOT EDIT. +// +// See hack/generate-swagger-api.sh +// ---------------------------------------------------------------------------- + +// ContainerCreateCreatedBody OK response to ContainerCreate operation +// swagger:model ContainerCreateCreatedBody +type ContainerCreateCreatedBody struct { + + // The ID of the created container + // Required: true + ID string `json:"Id"` + + // Warnings encountered when creating the container + // Required: true + Warnings []string `json:"Warnings"` +} diff --git a/vendor/github.com/docker/docker/api/types/container/container_top.go b/vendor/github.com/docker/docker/api/types/container/container_top.go new file mode 100644 index 0000000000..63381da367 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/container/container_top.go @@ -0,0 +1,22 @@ +package container // import "github.com/docker/docker/api/types/container" + +// ---------------------------------------------------------------------------- +// Code generated by `swagger generate operation`. DO NOT EDIT. +// +// See hack/generate-swagger-api.sh +// ---------------------------------------------------------------------------- + +// ContainerTopOKBody OK response to ContainerTop operation +// swagger:model ContainerTopOKBody +type ContainerTopOKBody struct { + + // Each process running in the container, where each is process + // is an array of values corresponding to the titles. + // + // Required: true + Processes [][]string `json:"Processes"` + + // The ps column titles + // Required: true + Titles []string `json:"Titles"` +} diff --git a/vendor/github.com/docker/docker/api/types/container/container_update.go b/vendor/github.com/docker/docker/api/types/container/container_update.go new file mode 100644 index 0000000000..c10f175ea8 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/container/container_update.go @@ -0,0 +1,16 @@ +package container // import "github.com/docker/docker/api/types/container" + +// ---------------------------------------------------------------------------- +// Code generated by `swagger generate operation`. DO NOT EDIT. +// +// See hack/generate-swagger-api.sh +// ---------------------------------------------------------------------------- + +// ContainerUpdateOKBody OK response to ContainerUpdate operation +// swagger:model ContainerUpdateOKBody +type ContainerUpdateOKBody struct { + + // warnings + // Required: true + Warnings []string `json:"Warnings"` +} diff --git a/vendor/github.com/docker/docker/api/types/container/container_wait.go b/vendor/github.com/docker/docker/api/types/container/container_wait.go new file mode 100644 index 0000000000..49e05ae669 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/container/container_wait.go @@ -0,0 +1,28 @@ +package container // import "github.com/docker/docker/api/types/container" + +// ---------------------------------------------------------------------------- +// Code generated by `swagger generate operation`. DO NOT EDIT. +// +// See hack/generate-swagger-api.sh +// ---------------------------------------------------------------------------- + +// ContainerWaitOKBodyError container waiting error, if any +// swagger:model ContainerWaitOKBodyError +type ContainerWaitOKBodyError struct { + + // Details of an error + Message string `json:"Message,omitempty"` +} + +// ContainerWaitOKBody OK response to ContainerWait operation +// swagger:model ContainerWaitOKBody +type ContainerWaitOKBody struct { + + // error + // Required: true + Error *ContainerWaitOKBodyError `json:"Error"` + + // Exit code of the container + // Required: true + StatusCode int64 `json:"StatusCode"` +} diff --git a/vendor/github.com/docker/docker/api/types/container/host_config.go b/vendor/github.com/docker/docker/api/types/container/host_config.go new file mode 100644 index 0000000000..2d1cbaa9ab --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/container/host_config.go @@ -0,0 +1,447 @@ +package container // import "github.com/docker/docker/api/types/container" + +import ( + "strings" + + "github.com/docker/docker/api/types/blkiodev" + "github.com/docker/docker/api/types/mount" + "github.com/docker/docker/api/types/strslice" + "github.com/docker/go-connections/nat" + units "github.com/docker/go-units" +) + +// CgroupnsMode represents the cgroup namespace mode of the container +type CgroupnsMode string + +// IsPrivate indicates whether the container uses its own private cgroup namespace +func (c CgroupnsMode) IsPrivate() bool { + return c == "private" +} + +// IsHost indicates whether the container shares the host's cgroup namespace +func (c CgroupnsMode) IsHost() bool { + return c == "host" +} + +// IsEmpty indicates whether the container cgroup namespace mode is unset +func (c CgroupnsMode) IsEmpty() bool { + return c == "" +} + +// Valid indicates whether the cgroup namespace mode is valid +func (c CgroupnsMode) Valid() bool { + return c.IsEmpty() || c.IsPrivate() || c.IsHost() +} + +// Isolation represents the isolation technology of a container. The supported +// values are platform specific +type Isolation string + +// IsDefault indicates the default isolation technology of a container. On Linux this +// is the native driver. On Windows, this is a Windows Server Container. +func (i Isolation) IsDefault() bool { + return strings.ToLower(string(i)) == "default" || string(i) == "" +} + +// IsHyperV indicates the use of a Hyper-V partition for isolation +func (i Isolation) IsHyperV() bool { + return strings.ToLower(string(i)) == "hyperv" +} + +// IsProcess indicates the use of process isolation +func (i Isolation) IsProcess() bool { + return strings.ToLower(string(i)) == "process" +} + +const ( + // IsolationEmpty is unspecified (same behavior as default) + IsolationEmpty = Isolation("") + // IsolationDefault is the default isolation mode on current daemon + IsolationDefault = Isolation("default") + // IsolationProcess is process isolation mode + IsolationProcess = Isolation("process") + // IsolationHyperV is HyperV isolation mode + IsolationHyperV = Isolation("hyperv") +) + +// IpcMode represents the container ipc stack. +type IpcMode string + +// IsPrivate indicates whether the container uses its own private ipc namespace which can not be shared. +func (n IpcMode) IsPrivate() bool { + return n == "private" +} + +// IsHost indicates whether the container shares the host's ipc namespace. +func (n IpcMode) IsHost() bool { + return n == "host" +} + +// IsShareable indicates whether the container's ipc namespace can be shared with another container. +func (n IpcMode) IsShareable() bool { + return n == "shareable" +} + +// IsContainer indicates whether the container uses another container's ipc namespace. +func (n IpcMode) IsContainer() bool { + parts := strings.SplitN(string(n), ":", 2) + return len(parts) > 1 && parts[0] == "container" +} + +// IsNone indicates whether container IpcMode is set to "none". +func (n IpcMode) IsNone() bool { + return n == "none" +} + +// IsEmpty indicates whether container IpcMode is empty +func (n IpcMode) IsEmpty() bool { + return n == "" +} + +// Valid indicates whether the ipc mode is valid. +func (n IpcMode) Valid() bool { + return n.IsEmpty() || n.IsNone() || n.IsPrivate() || n.IsHost() || n.IsShareable() || n.IsContainer() +} + +// Container returns the name of the container ipc stack is going to be used. +func (n IpcMode) Container() string { + parts := strings.SplitN(string(n), ":", 2) + if len(parts) > 1 && parts[0] == "container" { + return parts[1] + } + return "" +} + +// NetworkMode represents the container network stack. +type NetworkMode string + +// IsNone indicates whether container isn't using a network stack. +func (n NetworkMode) IsNone() bool { + return n == "none" +} + +// IsDefault indicates whether container uses the default network stack. +func (n NetworkMode) IsDefault() bool { + return n == "default" +} + +// IsPrivate indicates whether container uses its private network stack. +func (n NetworkMode) IsPrivate() bool { + return !(n.IsHost() || n.IsContainer()) +} + +// IsContainer indicates whether container uses a container network stack. +func (n NetworkMode) IsContainer() bool { + parts := strings.SplitN(string(n), ":", 2) + return len(parts) > 1 && parts[0] == "container" +} + +// ConnectedContainer is the id of the container which network this container is connected to. +func (n NetworkMode) ConnectedContainer() string { + parts := strings.SplitN(string(n), ":", 2) + if len(parts) > 1 { + return parts[1] + } + return "" +} + +// UserDefined indicates user-created network +func (n NetworkMode) UserDefined() string { + if n.IsUserDefined() { + return string(n) + } + return "" +} + +// UsernsMode represents userns mode in the container. +type UsernsMode string + +// IsHost indicates whether the container uses the host's userns. +func (n UsernsMode) IsHost() bool { + return n == "host" +} + +// IsPrivate indicates whether the container uses the a private userns. +func (n UsernsMode) IsPrivate() bool { + return !(n.IsHost()) +} + +// Valid indicates whether the userns is valid. +func (n UsernsMode) Valid() bool { + parts := strings.Split(string(n), ":") + switch mode := parts[0]; mode { + case "", "host": + default: + return false + } + return true +} + +// CgroupSpec represents the cgroup to use for the container. +type CgroupSpec string + +// IsContainer indicates whether the container is using another container cgroup +func (c CgroupSpec) IsContainer() bool { + parts := strings.SplitN(string(c), ":", 2) + return len(parts) > 1 && parts[0] == "container" +} + +// Valid indicates whether the cgroup spec is valid. +func (c CgroupSpec) Valid() bool { + return c.IsContainer() || c == "" +} + +// Container returns the name of the container whose cgroup will be used. +func (c CgroupSpec) Container() string { + parts := strings.SplitN(string(c), ":", 2) + if len(parts) > 1 { + return parts[1] + } + return "" +} + +// UTSMode represents the UTS namespace of the container. +type UTSMode string + +// IsPrivate indicates whether the container uses its private UTS namespace. +func (n UTSMode) IsPrivate() bool { + return !(n.IsHost()) +} + +// IsHost indicates whether the container uses the host's UTS namespace. +func (n UTSMode) IsHost() bool { + return n == "host" +} + +// Valid indicates whether the UTS namespace is valid. +func (n UTSMode) Valid() bool { + parts := strings.Split(string(n), ":") + switch mode := parts[0]; mode { + case "", "host": + default: + return false + } + return true +} + +// PidMode represents the pid namespace of the container. +type PidMode string + +// IsPrivate indicates whether the container uses its own new pid namespace. +func (n PidMode) IsPrivate() bool { + return !(n.IsHost() || n.IsContainer()) +} + +// IsHost indicates whether the container uses the host's pid namespace. +func (n PidMode) IsHost() bool { + return n == "host" +} + +// IsContainer indicates whether the container uses a container's pid namespace. +func (n PidMode) IsContainer() bool { + parts := strings.SplitN(string(n), ":", 2) + return len(parts) > 1 && parts[0] == "container" +} + +// Valid indicates whether the pid namespace is valid. +func (n PidMode) Valid() bool { + parts := strings.Split(string(n), ":") + switch mode := parts[0]; mode { + case "", "host": + case "container": + if len(parts) != 2 || parts[1] == "" { + return false + } + default: + return false + } + return true +} + +// Container returns the name of the container whose pid namespace is going to be used. +func (n PidMode) Container() string { + parts := strings.SplitN(string(n), ":", 2) + if len(parts) > 1 { + return parts[1] + } + return "" +} + +// DeviceRequest represents a request for devices from a device driver. +// Used by GPU device drivers. +type DeviceRequest struct { + Driver string // Name of device driver + Count int // Number of devices to request (-1 = All) + DeviceIDs []string // List of device IDs as recognizable by the device driver + Capabilities [][]string // An OR list of AND lists of device capabilities (e.g. "gpu") + Options map[string]string // Options to pass onto the device driver +} + +// DeviceMapping represents the device mapping between the host and the container. +type DeviceMapping struct { + PathOnHost string + PathInContainer string + CgroupPermissions string +} + +// RestartPolicy represents the restart policies of the container. +type RestartPolicy struct { + Name string + MaximumRetryCount int +} + +// IsNone indicates whether the container has the "no" restart policy. +// This means the container will not automatically restart when exiting. +func (rp *RestartPolicy) IsNone() bool { + return rp.Name == "no" || rp.Name == "" +} + +// IsAlways indicates whether the container has the "always" restart policy. +// This means the container will automatically restart regardless of the exit status. +func (rp *RestartPolicy) IsAlways() bool { + return rp.Name == "always" +} + +// IsOnFailure indicates whether the container has the "on-failure" restart policy. +// This means the container will automatically restart of exiting with a non-zero exit status. +func (rp *RestartPolicy) IsOnFailure() bool { + return rp.Name == "on-failure" +} + +// IsUnlessStopped indicates whether the container has the +// "unless-stopped" restart policy. This means the container will +// automatically restart unless user has put it to stopped state. +func (rp *RestartPolicy) IsUnlessStopped() bool { + return rp.Name == "unless-stopped" +} + +// IsSame compares two RestartPolicy to see if they are the same +func (rp *RestartPolicy) IsSame(tp *RestartPolicy) bool { + return rp.Name == tp.Name && rp.MaximumRetryCount == tp.MaximumRetryCount +} + +// LogMode is a type to define the available modes for logging +// These modes affect how logs are handled when log messages start piling up. +type LogMode string + +// Available logging modes +const ( + LogModeUnset = "" + LogModeBlocking LogMode = "blocking" + LogModeNonBlock LogMode = "non-blocking" +) + +// LogConfig represents the logging configuration of the container. +type LogConfig struct { + Type string + Config map[string]string +} + +// Resources contains container's resources (cgroups config, ulimits...) +type Resources struct { + // Applicable to all platforms + CPUShares int64 `json:"CpuShares"` // CPU shares (relative weight vs. other containers) + Memory int64 // Memory limit (in bytes) + NanoCPUs int64 `json:"NanoCpus"` // CPU quota in units of 10-9 CPUs. + + // Applicable to UNIX platforms + CgroupParent string // Parent cgroup. + BlkioWeight uint16 // Block IO weight (relative weight vs. other containers) + BlkioWeightDevice []*blkiodev.WeightDevice + BlkioDeviceReadBps []*blkiodev.ThrottleDevice + BlkioDeviceWriteBps []*blkiodev.ThrottleDevice + BlkioDeviceReadIOps []*blkiodev.ThrottleDevice + BlkioDeviceWriteIOps []*blkiodev.ThrottleDevice + CPUPeriod int64 `json:"CpuPeriod"` // CPU CFS (Completely Fair Scheduler) period + CPUQuota int64 `json:"CpuQuota"` // CPU CFS (Completely Fair Scheduler) quota + CPURealtimePeriod int64 `json:"CpuRealtimePeriod"` // CPU real-time period + CPURealtimeRuntime int64 `json:"CpuRealtimeRuntime"` // CPU real-time runtime + CpusetCpus string // CpusetCpus 0-2, 0,1 + CpusetMems string // CpusetMems 0-2, 0,1 + Devices []DeviceMapping // List of devices to map inside the container + DeviceCgroupRules []string // List of rule to be added to the device cgroup + DeviceRequests []DeviceRequest // List of device requests for device drivers + KernelMemory int64 // Kernel memory limit (in bytes), Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes + KernelMemoryTCP int64 // Hard limit for kernel TCP buffer memory (in bytes) + MemoryReservation int64 // Memory soft limit (in bytes) + MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap + MemorySwappiness *int64 // Tuning container memory swappiness behaviour + OomKillDisable *bool // Whether to disable OOM Killer or not + PidsLimit *int64 // Setting PIDs limit for a container; Set `0` or `-1` for unlimited, or `null` to not change. + Ulimits []*units.Ulimit // List of ulimits to be set in the container + + // Applicable to Windows + CPUCount int64 `json:"CpuCount"` // CPU count + CPUPercent int64 `json:"CpuPercent"` // CPU percent + IOMaximumIOps uint64 // Maximum IOps for the container system drive + IOMaximumBandwidth uint64 // Maximum IO in bytes per second for the container system drive +} + +// UpdateConfig holds the mutable attributes of a Container. +// Those attributes can be updated at runtime. +type UpdateConfig struct { + // Contains container's resources (cgroups, ulimits) + Resources + RestartPolicy RestartPolicy +} + +// HostConfig the non-portable Config structure of a container. +// Here, "non-portable" means "dependent of the host we are running on". +// Portable information *should* appear in Config. +type HostConfig struct { + // Applicable to all platforms + Binds []string // List of volume bindings for this container + ContainerIDFile string // File (path) where the containerId is written + LogConfig LogConfig // Configuration of the logs for this container + NetworkMode NetworkMode // Network mode to use for the container + PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host + RestartPolicy RestartPolicy // Restart policy to be used for the container + AutoRemove bool // Automatically remove container when it exits + VolumeDriver string // Name of the volume driver used to mount volumes + VolumesFrom []string // List of volumes to take from other container + + // Applicable to UNIX platforms + CapAdd strslice.StrSlice // List of kernel capabilities to add to the container + CapDrop strslice.StrSlice // List of kernel capabilities to remove from the container + CgroupnsMode CgroupnsMode // Cgroup namespace mode to use for the container + DNS []string `json:"Dns"` // List of DNS server to lookup + DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for + DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for + ExtraHosts []string // List of extra hosts + GroupAdd []string // List of additional groups that the container process will run as + IpcMode IpcMode // IPC namespace to use for the container + Cgroup CgroupSpec // Cgroup to use for the container + Links []string // List of links (in the name:alias form) + OomScoreAdj int // Container preference for OOM-killing + PidMode PidMode // PID namespace to use for the container + Privileged bool // Is the container in privileged mode + PublishAllPorts bool // Should docker publish all exposed port for the container + ReadonlyRootfs bool // Is the container root filesystem in read-only + SecurityOpt []string // List of string values to customize labels for MLS systems, such as SELinux. + StorageOpt map[string]string `json:",omitempty"` // Storage driver options per container. + Tmpfs map[string]string `json:",omitempty"` // List of tmpfs (mounts) used for the container + UTSMode UTSMode // UTS namespace to use for the container + UsernsMode UsernsMode // The user namespace to use for the container + ShmSize int64 // Total shm memory usage + Sysctls map[string]string `json:",omitempty"` // List of Namespaced sysctls used for the container + Runtime string `json:",omitempty"` // Runtime to use with this container + + // Applicable to Windows + ConsoleSize [2]uint // Initial console size (height,width) + Isolation Isolation // Isolation technology of the container (e.g. default, hyperv) + + // Contains container's resources (cgroups, ulimits) + Resources + + // Mounts specs used by the container + Mounts []mount.Mount `json:",omitempty"` + + // MaskedPaths is the list of paths to be masked inside the container (this overrides the default set of paths) + MaskedPaths []string + + // ReadonlyPaths is the list of paths to be set as read-only inside the container (this overrides the default set of paths) + ReadonlyPaths []string + + // Run a custom init inside the container, if null, use the daemon's configured settings + Init *bool `json:",omitempty"` +} diff --git a/vendor/github.com/docker/docker/api/types/container/hostconfig_unix.go b/vendor/github.com/docker/docker/api/types/container/hostconfig_unix.go new file mode 100644 index 0000000000..cf6fdf4402 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/container/hostconfig_unix.go @@ -0,0 +1,41 @@ +// +build !windows + +package container // import "github.com/docker/docker/api/types/container" + +// IsValid indicates if an isolation technology is valid +func (i Isolation) IsValid() bool { + return i.IsDefault() +} + +// NetworkName returns the name of the network stack. +func (n NetworkMode) NetworkName() string { + if n.IsBridge() { + return "bridge" + } else if n.IsHost() { + return "host" + } else if n.IsContainer() { + return "container" + } else if n.IsNone() { + return "none" + } else if n.IsDefault() { + return "default" + } else if n.IsUserDefined() { + return n.UserDefined() + } + return "" +} + +// IsBridge indicates whether container uses the bridge network stack +func (n NetworkMode) IsBridge() bool { + return n == "bridge" +} + +// IsHost indicates whether container uses the host network stack. +func (n NetworkMode) IsHost() bool { + return n == "host" +} + +// IsUserDefined indicates user-created network +func (n NetworkMode) IsUserDefined() bool { + return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer() +} diff --git a/vendor/github.com/docker/docker/api/types/container/hostconfig_windows.go b/vendor/github.com/docker/docker/api/types/container/hostconfig_windows.go new file mode 100644 index 0000000000..99f803a5bb --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/container/hostconfig_windows.go @@ -0,0 +1,40 @@ +package container // import "github.com/docker/docker/api/types/container" + +// IsBridge indicates whether container uses the bridge network stack +// in windows it is given the name NAT +func (n NetworkMode) IsBridge() bool { + return n == "nat" +} + +// IsHost indicates whether container uses the host network stack. +// returns false as this is not supported by windows +func (n NetworkMode) IsHost() bool { + return false +} + +// IsUserDefined indicates user-created network +func (n NetworkMode) IsUserDefined() bool { + return !n.IsDefault() && !n.IsNone() && !n.IsBridge() && !n.IsContainer() +} + +// IsValid indicates if an isolation technology is valid +func (i Isolation) IsValid() bool { + return i.IsDefault() || i.IsHyperV() || i.IsProcess() +} + +// NetworkName returns the name of the network stack. +func (n NetworkMode) NetworkName() string { + if n.IsDefault() { + return "default" + } else if n.IsBridge() { + return "nat" + } else if n.IsNone() { + return "none" + } else if n.IsContainer() { + return "container" + } else if n.IsUserDefined() { + return n.UserDefined() + } + + return "" +} diff --git a/vendor/github.com/docker/docker/api/types/container/waitcondition.go b/vendor/github.com/docker/docker/api/types/container/waitcondition.go new file mode 100644 index 0000000000..cd8311f99c --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/container/waitcondition.go @@ -0,0 +1,22 @@ +package container // import "github.com/docker/docker/api/types/container" + +// WaitCondition is a type used to specify a container state for which +// to wait. +type WaitCondition string + +// Possible WaitCondition Values. +// +// WaitConditionNotRunning (default) is used to wait for any of the non-running +// states: "created", "exited", "dead", "removing", or "removed". +// +// WaitConditionNextExit is used to wait for the next time the state changes +// to a non-running state. If the state is currently "created" or "exited", +// this would cause Wait() to block until either the container runs and exits +// or is removed. +// +// WaitConditionRemoved is used to wait for the container to be removed. +const ( + WaitConditionNotRunning WaitCondition = "not-running" + WaitConditionNextExit WaitCondition = "next-exit" + WaitConditionRemoved WaitCondition = "removed" +) diff --git a/vendor/github.com/docker/docker/api/types/error_response.go b/vendor/github.com/docker/docker/api/types/error_response.go new file mode 100644 index 0000000000..dc942d9d9e --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/error_response.go @@ -0,0 +1,13 @@ +package types + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// ErrorResponse Represents an error. +// swagger:model ErrorResponse +type ErrorResponse struct { + + // The error message. + // Required: true + Message string `json:"message"` +} diff --git a/vendor/github.com/docker/docker/api/types/error_response_ext.go b/vendor/github.com/docker/docker/api/types/error_response_ext.go new file mode 100644 index 0000000000..f84f034cd5 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/error_response_ext.go @@ -0,0 +1,6 @@ +package types + +// Error returns the error message +func (e ErrorResponse) Error() string { + return e.Message +} diff --git a/vendor/github.com/docker/docker/api/types/events/events.go b/vendor/github.com/docker/docker/api/types/events/events.go new file mode 100644 index 0000000000..aa8fba8154 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/events/events.go @@ -0,0 +1,54 @@ +package events // import "github.com/docker/docker/api/types/events" + +const ( + // BuilderEventType is the event type that the builder generates + BuilderEventType = "builder" + // ContainerEventType is the event type that containers generate + ContainerEventType = "container" + // DaemonEventType is the event type that daemon generate + DaemonEventType = "daemon" + // ImageEventType is the event type that images generate + ImageEventType = "image" + // NetworkEventType is the event type that networks generate + NetworkEventType = "network" + // PluginEventType is the event type that plugins generate + PluginEventType = "plugin" + // VolumeEventType is the event type that volumes generate + VolumeEventType = "volume" + // ServiceEventType is the event type that services generate + ServiceEventType = "service" + // NodeEventType is the event type that nodes generate + NodeEventType = "node" + // SecretEventType is the event type that secrets generate + SecretEventType = "secret" + // ConfigEventType is the event type that configs generate + ConfigEventType = "config" +) + +// Actor describes something that generates events, +// like a container, or a network, or a volume. +// It has a defined name and a set or attributes. +// The container attributes are its labels, other actors +// can generate these attributes from other properties. +type Actor struct { + ID string + Attributes map[string]string +} + +// Message represents the information an event contains +type Message struct { + // Deprecated information from JSONMessage. + // With data only in container events. + Status string `json:"status,omitempty"` + ID string `json:"id,omitempty"` + From string `json:"from,omitempty"` + + Type string + Action string + Actor Actor + // Engine events are local scope. Cluster events are swarm scope. + Scope string `json:"scope,omitempty"` + + Time int64 `json:"time,omitempty"` + TimeNano int64 `json:"timeNano,omitempty"` +} diff --git a/vendor/github.com/docker/docker/api/types/filters/parse.go b/vendor/github.com/docker/docker/api/types/filters/parse.go new file mode 100644 index 0000000000..4bc91cffd6 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/filters/parse.go @@ -0,0 +1,324 @@ +/*Package filters provides tools for encoding a mapping of keys to a set of +multiple values. +*/ +package filters // import "github.com/docker/docker/api/types/filters" + +import ( + "encoding/json" + "regexp" + "strings" + + "github.com/docker/docker/api/types/versions" +) + +// Args stores a mapping of keys to a set of multiple values. +type Args struct { + fields map[string]map[string]bool +} + +// KeyValuePair are used to initialize a new Args +type KeyValuePair struct { + Key string + Value string +} + +// Arg creates a new KeyValuePair for initializing Args +func Arg(key, value string) KeyValuePair { + return KeyValuePair{Key: key, Value: value} +} + +// NewArgs returns a new Args populated with the initial args +func NewArgs(initialArgs ...KeyValuePair) Args { + args := Args{fields: map[string]map[string]bool{}} + for _, arg := range initialArgs { + args.Add(arg.Key, arg.Value) + } + return args +} + +// Keys returns all the keys in list of Args +func (args Args) Keys() []string { + keys := make([]string, 0, len(args.fields)) + for k := range args.fields { + keys = append(keys, k) + } + return keys +} + +// MarshalJSON returns a JSON byte representation of the Args +func (args Args) MarshalJSON() ([]byte, error) { + if len(args.fields) == 0 { + return []byte{}, nil + } + return json.Marshal(args.fields) +} + +// ToJSON returns the Args as a JSON encoded string +func ToJSON(a Args) (string, error) { + if a.Len() == 0 { + return "", nil + } + buf, err := json.Marshal(a) + return string(buf), err +} + +// ToParamWithVersion encodes Args as a JSON string. If version is less than 1.22 +// then the encoded format will use an older legacy format where the values are a +// list of strings, instead of a set. +// +// Deprecated: do not use in any new code; use ToJSON instead +func ToParamWithVersion(version string, a Args) (string, error) { + if a.Len() == 0 { + return "", nil + } + + if version != "" && versions.LessThan(version, "1.22") { + buf, err := json.Marshal(convertArgsToSlice(a.fields)) + return string(buf), err + } + + return ToJSON(a) +} + +// FromJSON decodes a JSON encoded string into Args +func FromJSON(p string) (Args, error) { + args := NewArgs() + + if p == "" { + return args, nil + } + + raw := []byte(p) + err := json.Unmarshal(raw, &args) + if err == nil { + return args, nil + } + + // Fallback to parsing arguments in the legacy slice format + deprecated := map[string][]string{} + if legacyErr := json.Unmarshal(raw, &deprecated); legacyErr != nil { + return args, err + } + + args.fields = deprecatedArgs(deprecated) + return args, nil +} + +// UnmarshalJSON populates the Args from JSON encode bytes +func (args Args) UnmarshalJSON(raw []byte) error { + if len(raw) == 0 { + return nil + } + return json.Unmarshal(raw, &args.fields) +} + +// Get returns the list of values associated with the key +func (args Args) Get(key string) []string { + values := args.fields[key] + if values == nil { + return make([]string, 0) + } + slice := make([]string, 0, len(values)) + for key := range values { + slice = append(slice, key) + } + return slice +} + +// Add a new value to the set of values +func (args Args) Add(key, value string) { + if _, ok := args.fields[key]; ok { + args.fields[key][value] = true + } else { + args.fields[key] = map[string]bool{value: true} + } +} + +// Del removes a value from the set +func (args Args) Del(key, value string) { + if _, ok := args.fields[key]; ok { + delete(args.fields[key], value) + if len(args.fields[key]) == 0 { + delete(args.fields, key) + } + } +} + +// Len returns the number of keys in the mapping +func (args Args) Len() int { + return len(args.fields) +} + +// MatchKVList returns true if all the pairs in sources exist as key=value +// pairs in the mapping at key, or if there are no values at key. +func (args Args) MatchKVList(key string, sources map[string]string) bool { + fieldValues := args.fields[key] + + // do not filter if there is no filter set or cannot determine filter + if len(fieldValues) == 0 { + return true + } + + if len(sources) == 0 { + return false + } + + for value := range fieldValues { + testKV := strings.SplitN(value, "=", 2) + + v, ok := sources[testKV[0]] + if !ok { + return false + } + if len(testKV) == 2 && testKV[1] != v { + return false + } + } + + return true +} + +// Match returns true if any of the values at key match the source string +func (args Args) Match(field, source string) bool { + if args.ExactMatch(field, source) { + return true + } + + fieldValues := args.fields[field] + for name2match := range fieldValues { + match, err := regexp.MatchString(name2match, source) + if err != nil { + continue + } + if match { + return true + } + } + return false +} + +// ExactMatch returns true if the source matches exactly one of the values. +func (args Args) ExactMatch(key, source string) bool { + fieldValues, ok := args.fields[key] + // do not filter if there is no filter set or cannot determine filter + if !ok || len(fieldValues) == 0 { + return true + } + + // try to match full name value to avoid O(N) regular expression matching + return fieldValues[source] +} + +// UniqueExactMatch returns true if there is only one value and the source +// matches exactly the value. +func (args Args) UniqueExactMatch(key, source string) bool { + fieldValues := args.fields[key] + // do not filter if there is no filter set or cannot determine filter + if len(fieldValues) == 0 { + return true + } + if len(args.fields[key]) != 1 { + return false + } + + // try to match full name value to avoid O(N) regular expression matching + return fieldValues[source] +} + +// FuzzyMatch returns true if the source matches exactly one value, or the +// source has one of the values as a prefix. +func (args Args) FuzzyMatch(key, source string) bool { + if args.ExactMatch(key, source) { + return true + } + + fieldValues := args.fields[key] + for prefix := range fieldValues { + if strings.HasPrefix(source, prefix) { + return true + } + } + return false +} + +// Contains returns true if the key exists in the mapping +func (args Args) Contains(field string) bool { + _, ok := args.fields[field] + return ok +} + +type invalidFilter string + +func (e invalidFilter) Error() string { + return "Invalid filter '" + string(e) + "'" +} + +func (invalidFilter) InvalidParameter() {} + +// Validate compared the set of accepted keys against the keys in the mapping. +// An error is returned if any mapping keys are not in the accepted set. +func (args Args) Validate(accepted map[string]bool) error { + for name := range args.fields { + if !accepted[name] { + return invalidFilter(name) + } + } + return nil +} + +// WalkValues iterates over the list of values for a key in the mapping and calls +// op() for each value. If op returns an error the iteration stops and the +// error is returned. +func (args Args) WalkValues(field string, op func(value string) error) error { + if _, ok := args.fields[field]; !ok { + return nil + } + for v := range args.fields[field] { + if err := op(v); err != nil { + return err + } + } + return nil +} + +// Clone returns a copy of args. +func (args Args) Clone() (newArgs Args) { + newArgs.fields = make(map[string]map[string]bool, len(args.fields)) + for k, m := range args.fields { + var mm map[string]bool + if m != nil { + mm = make(map[string]bool, len(m)) + for kk, v := range m { + mm[kk] = v + } + } + newArgs.fields[k] = mm + } + return newArgs +} + +func deprecatedArgs(d map[string][]string) map[string]map[string]bool { + m := map[string]map[string]bool{} + for k, v := range d { + values := map[string]bool{} + for _, vv := range v { + values[vv] = true + } + m[k] = values + } + return m +} + +func convertArgsToSlice(f map[string]map[string]bool) map[string][]string { + m := map[string][]string{} + for k, v := range f { + values := []string{} + for kk := range v { + if v[kk] { + values = append(values, kk) + } + } + m[k] = values + } + return m +} diff --git a/vendor/github.com/docker/docker/api/types/graph_driver_data.go b/vendor/github.com/docker/docker/api/types/graph_driver_data.go new file mode 100644 index 0000000000..4d9bf1c62c --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/graph_driver_data.go @@ -0,0 +1,17 @@ +package types + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// GraphDriverData Information about a container's graph driver. +// swagger:model GraphDriverData +type GraphDriverData struct { + + // data + // Required: true + Data map[string]string `json:"Data"` + + // name + // Required: true + Name string `json:"Name"` +} diff --git a/vendor/github.com/docker/docker/api/types/id_response.go b/vendor/github.com/docker/docker/api/types/id_response.go new file mode 100644 index 0000000000..7592d2f8b1 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/id_response.go @@ -0,0 +1,13 @@ +package types + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// IDResponse Response to an API call that returns just an Id +// swagger:model IdResponse +type IDResponse struct { + + // The id of the newly created object. + // Required: true + ID string `json:"Id"` +} diff --git a/vendor/github.com/docker/docker/api/types/image/image_history.go b/vendor/github.com/docker/docker/api/types/image/image_history.go new file mode 100644 index 0000000000..e302bb0aeb --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/image/image_history.go @@ -0,0 +1,36 @@ +package image // import "github.com/docker/docker/api/types/image" + +// ---------------------------------------------------------------------------- +// Code generated by `swagger generate operation`. DO NOT EDIT. +// +// See hack/generate-swagger-api.sh +// ---------------------------------------------------------------------------- + +// HistoryResponseItem individual image layer information in response to ImageHistory operation +// swagger:model HistoryResponseItem +type HistoryResponseItem struct { + + // comment + // Required: true + Comment string `json:"Comment"` + + // created + // Required: true + Created int64 `json:"Created"` + + // created by + // Required: true + CreatedBy string `json:"CreatedBy"` + + // Id + // Required: true + ID string `json:"Id"` + + // size + // Required: true + Size int64 `json:"Size"` + + // tags + // Required: true + Tags []string `json:"Tags"` +} diff --git a/vendor/github.com/docker/docker/api/types/image_delete_response_item.go b/vendor/github.com/docker/docker/api/types/image_delete_response_item.go new file mode 100644 index 0000000000..b9a65a0d8e --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/image_delete_response_item.go @@ -0,0 +1,15 @@ +package types + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// ImageDeleteResponseItem image delete response item +// swagger:model ImageDeleteResponseItem +type ImageDeleteResponseItem struct { + + // The image ID of an image that was deleted + Deleted string `json:"Deleted,omitempty"` + + // The image ID of an image that was untagged + Untagged string `json:"Untagged,omitempty"` +} diff --git a/vendor/github.com/docker/docker/api/types/image_summary.go b/vendor/github.com/docker/docker/api/types/image_summary.go new file mode 100644 index 0000000000..e145b3dcfc --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/image_summary.go @@ -0,0 +1,49 @@ +package types + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// ImageSummary image summary +// swagger:model ImageSummary +type ImageSummary struct { + + // containers + // Required: true + Containers int64 `json:"Containers"` + + // created + // Required: true + Created int64 `json:"Created"` + + // Id + // Required: true + ID string `json:"Id"` + + // labels + // Required: true + Labels map[string]string `json:"Labels"` + + // parent Id + // Required: true + ParentID string `json:"ParentId"` + + // repo digests + // Required: true + RepoDigests []string `json:"RepoDigests"` + + // repo tags + // Required: true + RepoTags []string `json:"RepoTags"` + + // shared size + // Required: true + SharedSize int64 `json:"SharedSize"` + + // size + // Required: true + Size int64 `json:"Size"` + + // virtual size + // Required: true + VirtualSize int64 `json:"VirtualSize"` +} diff --git a/vendor/github.com/docker/docker/api/types/mount/mount.go b/vendor/github.com/docker/docker/api/types/mount/mount.go new file mode 100644 index 0000000000..443b8d07a9 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/mount/mount.go @@ -0,0 +1,131 @@ +package mount // import "github.com/docker/docker/api/types/mount" + +import ( + "os" +) + +// Type represents the type of a mount. +type Type string + +// Type constants +const ( + // TypeBind is the type for mounting host dir + TypeBind Type = "bind" + // TypeVolume is the type for remote storage volumes + TypeVolume Type = "volume" + // TypeTmpfs is the type for mounting tmpfs + TypeTmpfs Type = "tmpfs" + // TypeNamedPipe is the type for mounting Windows named pipes + TypeNamedPipe Type = "npipe" +) + +// Mount represents a mount (volume). +type Mount struct { + Type Type `json:",omitempty"` + // Source specifies the name of the mount. Depending on mount type, this + // may be a volume name or a host path, or even ignored. + // Source is not supported for tmpfs (must be an empty value) + Source string `json:",omitempty"` + Target string `json:",omitempty"` + ReadOnly bool `json:",omitempty"` + Consistency Consistency `json:",omitempty"` + + BindOptions *BindOptions `json:",omitempty"` + VolumeOptions *VolumeOptions `json:",omitempty"` + TmpfsOptions *TmpfsOptions `json:",omitempty"` +} + +// Propagation represents the propagation of a mount. +type Propagation string + +const ( + // PropagationRPrivate RPRIVATE + PropagationRPrivate Propagation = "rprivate" + // PropagationPrivate PRIVATE + PropagationPrivate Propagation = "private" + // PropagationRShared RSHARED + PropagationRShared Propagation = "rshared" + // PropagationShared SHARED + PropagationShared Propagation = "shared" + // PropagationRSlave RSLAVE + PropagationRSlave Propagation = "rslave" + // PropagationSlave SLAVE + PropagationSlave Propagation = "slave" +) + +// Propagations is the list of all valid mount propagations +var Propagations = []Propagation{ + PropagationRPrivate, + PropagationPrivate, + PropagationRShared, + PropagationShared, + PropagationRSlave, + PropagationSlave, +} + +// Consistency represents the consistency requirements of a mount. +type Consistency string + +const ( + // ConsistencyFull guarantees bind mount-like consistency + ConsistencyFull Consistency = "consistent" + // ConsistencyCached mounts can cache read data and FS structure + ConsistencyCached Consistency = "cached" + // ConsistencyDelegated mounts can cache read and written data and structure + ConsistencyDelegated Consistency = "delegated" + // ConsistencyDefault provides "consistent" behavior unless overridden + ConsistencyDefault Consistency = "default" +) + +// BindOptions defines options specific to mounts of type "bind". +type BindOptions struct { + Propagation Propagation `json:",omitempty"` + NonRecursive bool `json:",omitempty"` +} + +// VolumeOptions represents the options for a mount of type volume. +type VolumeOptions struct { + NoCopy bool `json:",omitempty"` + Labels map[string]string `json:",omitempty"` + DriverConfig *Driver `json:",omitempty"` +} + +// Driver represents a volume driver. +type Driver struct { + Name string `json:",omitempty"` + Options map[string]string `json:",omitempty"` +} + +// TmpfsOptions defines options specific to mounts of type "tmpfs". +type TmpfsOptions struct { + // Size sets the size of the tmpfs, in bytes. + // + // This will be converted to an operating system specific value + // depending on the host. For example, on linux, it will be converted to + // use a 'k', 'm' or 'g' syntax. BSD, though not widely supported with + // docker, uses a straight byte value. + // + // Percentages are not supported. + SizeBytes int64 `json:",omitempty"` + // Mode of the tmpfs upon creation + Mode os.FileMode `json:",omitempty"` + + // TODO(stevvooe): There are several more tmpfs flags, specified in the + // daemon, that are accepted. Only the most basic are added for now. + // + // From https://github.com/moby/sys/blob/mount/v0.1.1/mount/flags.go#L47-L56 + // + // var validFlags = map[string]bool{ + // "": true, + // "size": true, X + // "mode": true, X + // "uid": true, + // "gid": true, + // "nr_inodes": true, + // "nr_blocks": true, + // "mpol": true, + // } + // + // Some of these may be straightforward to add, but others, such as + // uid/gid have implications in a clustered system. +} diff --git a/vendor/github.com/docker/docker/api/types/network/network.go b/vendor/github.com/docker/docker/api/types/network/network.go new file mode 100644 index 0000000000..437b184c67 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/network/network.go @@ -0,0 +1,126 @@ +package network // import "github.com/docker/docker/api/types/network" +import ( + "github.com/docker/docker/api/types/filters" +) + +// Address represents an IP address +type Address struct { + Addr string + PrefixLen int +} + +// IPAM represents IP Address Management +type IPAM struct { + Driver string + Options map[string]string // Per network IPAM driver options + Config []IPAMConfig +} + +// IPAMConfig represents IPAM configurations +type IPAMConfig struct { + Subnet string `json:",omitempty"` + IPRange string `json:",omitempty"` + Gateway string `json:",omitempty"` + AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"` +} + +// EndpointIPAMConfig represents IPAM configurations for the endpoint +type EndpointIPAMConfig struct { + IPv4Address string `json:",omitempty"` + IPv6Address string `json:",omitempty"` + LinkLocalIPs []string `json:",omitempty"` +} + +// Copy makes a copy of the endpoint ipam config +func (cfg *EndpointIPAMConfig) Copy() *EndpointIPAMConfig { + cfgCopy := *cfg + cfgCopy.LinkLocalIPs = make([]string, 0, len(cfg.LinkLocalIPs)) + cfgCopy.LinkLocalIPs = append(cfgCopy.LinkLocalIPs, cfg.LinkLocalIPs...) + return &cfgCopy +} + +// PeerInfo represents one peer of an overlay network +type PeerInfo struct { + Name string + IP string +} + +// EndpointSettings stores the network endpoint details +type EndpointSettings struct { + // Configurations + IPAMConfig *EndpointIPAMConfig + Links []string + Aliases []string + // Operational data + NetworkID string + EndpointID string + Gateway string + IPAddress string + IPPrefixLen int + IPv6Gateway string + GlobalIPv6Address string + GlobalIPv6PrefixLen int + MacAddress string + DriverOpts map[string]string +} + +// Task carries the information about one backend task +type Task struct { + Name string + EndpointID string + EndpointIP string + Info map[string]string +} + +// ServiceInfo represents service parameters with the list of service's tasks +type ServiceInfo struct { + VIP string + Ports []string + LocalLBIndex int + Tasks []Task +} + +// Copy makes a deep copy of `EndpointSettings` +func (es *EndpointSettings) Copy() *EndpointSettings { + epCopy := *es + if es.IPAMConfig != nil { + epCopy.IPAMConfig = es.IPAMConfig.Copy() + } + + if es.Links != nil { + links := make([]string, 0, len(es.Links)) + epCopy.Links = append(links, es.Links...) + } + + if es.Aliases != nil { + aliases := make([]string, 0, len(es.Aliases)) + epCopy.Aliases = append(aliases, es.Aliases...) + } + return &epCopy +} + +// NetworkingConfig represents the container's networking configuration for each of its interfaces +// Carries the networking configs specified in the `docker run` and `docker network connect` commands +type NetworkingConfig struct { + EndpointsConfig map[string]*EndpointSettings // Endpoint configs for each connecting network +} + +// ConfigReference specifies the source which provides a network's configuration +type ConfigReference struct { + Network string +} + +var acceptedFilters = map[string]bool{ + "dangling": true, + "driver": true, + "id": true, + "label": true, + "name": true, + "scope": true, + "type": true, +} + +// ValidateFilters validates the list of filter args with the available filters. +func ValidateFilters(filter filters.Args) error { + return filter.Validate(acceptedFilters) +} diff --git a/vendor/github.com/docker/docker/api/types/plugin.go b/vendor/github.com/docker/docker/api/types/plugin.go new file mode 100644 index 0000000000..abae48b9ab --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/plugin.go @@ -0,0 +1,203 @@ +package types + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// Plugin A plugin for the Engine API +// swagger:model Plugin +type Plugin struct { + + // config + // Required: true + Config PluginConfig `json:"Config"` + + // True if the plugin is running. False if the plugin is not running, only installed. + // Required: true + Enabled bool `json:"Enabled"` + + // Id + ID string `json:"Id,omitempty"` + + // name + // Required: true + Name string `json:"Name"` + + // plugin remote reference used to push/pull the plugin + PluginReference string `json:"PluginReference,omitempty"` + + // settings + // Required: true + Settings PluginSettings `json:"Settings"` +} + +// PluginConfig The config of a plugin. +// swagger:model PluginConfig +type PluginConfig struct { + + // args + // Required: true + Args PluginConfigArgs `json:"Args"` + + // description + // Required: true + Description string `json:"Description"` + + // Docker Version used to create the plugin + DockerVersion string `json:"DockerVersion,omitempty"` + + // documentation + // Required: true + Documentation string `json:"Documentation"` + + // entrypoint + // Required: true + Entrypoint []string `json:"Entrypoint"` + + // env + // Required: true + Env []PluginEnv `json:"Env"` + + // interface + // Required: true + Interface PluginConfigInterface `json:"Interface"` + + // ipc host + // Required: true + IpcHost bool `json:"IpcHost"` + + // linux + // Required: true + Linux PluginConfigLinux `json:"Linux"` + + // mounts + // Required: true + Mounts []PluginMount `json:"Mounts"` + + // network + // Required: true + Network PluginConfigNetwork `json:"Network"` + + // pid host + // Required: true + PidHost bool `json:"PidHost"` + + // propagated mount + // Required: true + PropagatedMount string `json:"PropagatedMount"` + + // user + User PluginConfigUser `json:"User,omitempty"` + + // work dir + // Required: true + WorkDir string `json:"WorkDir"` + + // rootfs + Rootfs *PluginConfigRootfs `json:"rootfs,omitempty"` +} + +// PluginConfigArgs plugin config args +// swagger:model PluginConfigArgs +type PluginConfigArgs struct { + + // description + // Required: true + Description string `json:"Description"` + + // name + // Required: true + Name string `json:"Name"` + + // settable + // Required: true + Settable []string `json:"Settable"` + + // value + // Required: true + Value []string `json:"Value"` +} + +// PluginConfigInterface The interface between Docker and the plugin +// swagger:model PluginConfigInterface +type PluginConfigInterface struct { + + // Protocol to use for clients connecting to the plugin. + ProtocolScheme string `json:"ProtocolScheme,omitempty"` + + // socket + // Required: true + Socket string `json:"Socket"` + + // types + // Required: true + Types []PluginInterfaceType `json:"Types"` +} + +// PluginConfigLinux plugin config linux +// swagger:model PluginConfigLinux +type PluginConfigLinux struct { + + // allow all devices + // Required: true + AllowAllDevices bool `json:"AllowAllDevices"` + + // capabilities + // Required: true + Capabilities []string `json:"Capabilities"` + + // devices + // Required: true + Devices []PluginDevice `json:"Devices"` +} + +// PluginConfigNetwork plugin config network +// swagger:model PluginConfigNetwork +type PluginConfigNetwork struct { + + // type + // Required: true + Type string `json:"Type"` +} + +// PluginConfigRootfs plugin config rootfs +// swagger:model PluginConfigRootfs +type PluginConfigRootfs struct { + + // diff ids + DiffIds []string `json:"diff_ids"` + + // type + Type string `json:"type,omitempty"` +} + +// PluginConfigUser plugin config user +// swagger:model PluginConfigUser +type PluginConfigUser struct { + + // g ID + GID uint32 `json:"GID,omitempty"` + + // UID + UID uint32 `json:"UID,omitempty"` +} + +// PluginSettings Settings that can be modified by users. +// swagger:model PluginSettings +type PluginSettings struct { + + // args + // Required: true + Args []string `json:"Args"` + + // devices + // Required: true + Devices []PluginDevice `json:"Devices"` + + // env + // Required: true + Env []string `json:"Env"` + + // mounts + // Required: true + Mounts []PluginMount `json:"Mounts"` +} diff --git a/vendor/github.com/docker/docker/api/types/plugin_device.go b/vendor/github.com/docker/docker/api/types/plugin_device.go new file mode 100644 index 0000000000..5699010675 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/plugin_device.go @@ -0,0 +1,25 @@ +package types + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// PluginDevice plugin device +// swagger:model PluginDevice +type PluginDevice struct { + + // description + // Required: true + Description string `json:"Description"` + + // name + // Required: true + Name string `json:"Name"` + + // path + // Required: true + Path *string `json:"Path"` + + // settable + // Required: true + Settable []string `json:"Settable"` +} diff --git a/vendor/github.com/docker/docker/api/types/plugin_env.go b/vendor/github.com/docker/docker/api/types/plugin_env.go new file mode 100644 index 0000000000..32962dc2eb --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/plugin_env.go @@ -0,0 +1,25 @@ +package types + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// PluginEnv plugin env +// swagger:model PluginEnv +type PluginEnv struct { + + // description + // Required: true + Description string `json:"Description"` + + // name + // Required: true + Name string `json:"Name"` + + // settable + // Required: true + Settable []string `json:"Settable"` + + // value + // Required: true + Value *string `json:"Value"` +} diff --git a/vendor/github.com/docker/docker/api/types/plugin_interface_type.go b/vendor/github.com/docker/docker/api/types/plugin_interface_type.go new file mode 100644 index 0000000000..c82f204e87 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/plugin_interface_type.go @@ -0,0 +1,21 @@ +package types + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// PluginInterfaceType plugin interface type +// swagger:model PluginInterfaceType +type PluginInterfaceType struct { + + // capability + // Required: true + Capability string `json:"Capability"` + + // prefix + // Required: true + Prefix string `json:"Prefix"` + + // version + // Required: true + Version string `json:"Version"` +} diff --git a/vendor/github.com/docker/docker/api/types/plugin_mount.go b/vendor/github.com/docker/docker/api/types/plugin_mount.go new file mode 100644 index 0000000000..5c031cf8b5 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/plugin_mount.go @@ -0,0 +1,37 @@ +package types + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// PluginMount plugin mount +// swagger:model PluginMount +type PluginMount struct { + + // description + // Required: true + Description string `json:"Description"` + + // destination + // Required: true + Destination string `json:"Destination"` + + // name + // Required: true + Name string `json:"Name"` + + // options + // Required: true + Options []string `json:"Options"` + + // settable + // Required: true + Settable []string `json:"Settable"` + + // source + // Required: true + Source *string `json:"Source"` + + // type + // Required: true + Type string `json:"Type"` +} diff --git a/vendor/github.com/docker/docker/api/types/plugin_responses.go b/vendor/github.com/docker/docker/api/types/plugin_responses.go new file mode 100644 index 0000000000..60d1fb5ad8 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/plugin_responses.go @@ -0,0 +1,71 @@ +package types // import "github.com/docker/docker/api/types" + +import ( + "encoding/json" + "fmt" + "sort" +) + +// PluginsListResponse contains the response for the Engine API +type PluginsListResponse []*Plugin + +// UnmarshalJSON implements json.Unmarshaler for PluginInterfaceType +func (t *PluginInterfaceType) UnmarshalJSON(p []byte) error { + versionIndex := len(p) + prefixIndex := 0 + if len(p) < 2 || p[0] != '"' || p[len(p)-1] != '"' { + return fmt.Errorf("%q is not a plugin interface type", p) + } + p = p[1 : len(p)-1] +loop: + for i, b := range p { + switch b { + case '.': + prefixIndex = i + case '/': + versionIndex = i + break loop + } + } + t.Prefix = string(p[:prefixIndex]) + t.Capability = string(p[prefixIndex+1 : versionIndex]) + if versionIndex < len(p) { + t.Version = string(p[versionIndex+1:]) + } + return nil +} + +// MarshalJSON implements json.Marshaler for PluginInterfaceType +func (t *PluginInterfaceType) MarshalJSON() ([]byte, error) { + return json.Marshal(t.String()) +} + +// String implements fmt.Stringer for PluginInterfaceType +func (t PluginInterfaceType) String() string { + return fmt.Sprintf("%s.%s/%s", t.Prefix, t.Capability, t.Version) +} + +// PluginPrivilege describes a permission the user has to accept +// upon installing a plugin. +type PluginPrivilege struct { + Name string + Description string + Value []string +} + +// PluginPrivileges is a list of PluginPrivilege +type PluginPrivileges []PluginPrivilege + +func (s PluginPrivileges) Len() int { + return len(s) +} + +func (s PluginPrivileges) Less(i, j int) bool { + return s[i].Name < s[j].Name +} + +func (s PluginPrivileges) Swap(i, j int) { + sort.Strings(s[i].Value) + sort.Strings(s[j].Value) + s[i], s[j] = s[j], s[i] +} diff --git a/vendor/github.com/docker/docker/api/types/port.go b/vendor/github.com/docker/docker/api/types/port.go new file mode 100644 index 0000000000..d91234744c --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/port.go @@ -0,0 +1,23 @@ +package types + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// Port An open port on a container +// swagger:model Port +type Port struct { + + // Host IP address that the container's port is mapped to + IP string `json:"IP,omitempty"` + + // Port on the container + // Required: true + PrivatePort uint16 `json:"PrivatePort"` + + // Port exposed on the host + PublicPort uint16 `json:"PublicPort,omitempty"` + + // type + // Required: true + Type string `json:"Type"` +} diff --git a/vendor/github.com/docker/docker/api/types/registry/authenticate.go b/vendor/github.com/docker/docker/api/types/registry/authenticate.go new file mode 100644 index 0000000000..f0a2113e40 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/registry/authenticate.go @@ -0,0 +1,21 @@ +package registry // import "github.com/docker/docker/api/types/registry" + +// ---------------------------------------------------------------------------- +// DO NOT EDIT THIS FILE +// This file was generated by `swagger generate operation` +// +// See hack/generate-swagger-api.sh +// ---------------------------------------------------------------------------- + +// AuthenticateOKBody authenticate o k body +// swagger:model AuthenticateOKBody +type AuthenticateOKBody struct { + + // An opaque token used to authenticate a user after a successful login + // Required: true + IdentityToken string `json:"IdentityToken"` + + // The status of the authentication + // Required: true + Status string `json:"Status"` +} diff --git a/vendor/github.com/docker/docker/api/types/registry/registry.go b/vendor/github.com/docker/docker/api/types/registry/registry.go new file mode 100644 index 0000000000..53e47084c8 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/registry/registry.go @@ -0,0 +1,119 @@ +package registry // import "github.com/docker/docker/api/types/registry" + +import ( + "encoding/json" + "net" + + v1 "github.com/opencontainers/image-spec/specs-go/v1" +) + +// ServiceConfig stores daemon registry services configuration. +type ServiceConfig struct { + AllowNondistributableArtifactsCIDRs []*NetIPNet + AllowNondistributableArtifactsHostnames []string + InsecureRegistryCIDRs []*NetIPNet `json:"InsecureRegistryCIDRs"` + IndexConfigs map[string]*IndexInfo `json:"IndexConfigs"` + Mirrors []string +} + +// NetIPNet is the net.IPNet type, which can be marshalled and +// unmarshalled to JSON +type NetIPNet net.IPNet + +// String returns the CIDR notation of ipnet +func (ipnet *NetIPNet) String() string { + return (*net.IPNet)(ipnet).String() +} + +// MarshalJSON returns the JSON representation of the IPNet +func (ipnet *NetIPNet) MarshalJSON() ([]byte, error) { + return json.Marshal((*net.IPNet)(ipnet).String()) +} + +// UnmarshalJSON sets the IPNet from a byte array of JSON +func (ipnet *NetIPNet) UnmarshalJSON(b []byte) (err error) { + var ipnetStr string + if err = json.Unmarshal(b, &ipnetStr); err == nil { + var cidr *net.IPNet + if _, cidr, err = net.ParseCIDR(ipnetStr); err == nil { + *ipnet = NetIPNet(*cidr) + } + } + return +} + +// IndexInfo contains information about a registry +// +// RepositoryInfo Examples: +// { +// "Index" : { +// "Name" : "docker.io", +// "Mirrors" : ["https://registry-2.docker.io/v1/", "https://registry-3.docker.io/v1/"], +// "Secure" : true, +// "Official" : true, +// }, +// "RemoteName" : "library/debian", +// "LocalName" : "debian", +// "CanonicalName" : "docker.io/debian" +// "Official" : true, +// } +// +// { +// "Index" : { +// "Name" : "127.0.0.1:5000", +// "Mirrors" : [], +// "Secure" : false, +// "Official" : false, +// }, +// "RemoteName" : "user/repo", +// "LocalName" : "127.0.0.1:5000/user/repo", +// "CanonicalName" : "127.0.0.1:5000/user/repo", +// "Official" : false, +// } +type IndexInfo struct { + // Name is the name of the registry, such as "docker.io" + Name string + // Mirrors is a list of mirrors, expressed as URIs + Mirrors []string + // Secure is set to false if the registry is part of the list of + // insecure registries. Insecure registries accept HTTP and/or accept + // HTTPS with certificates from unknown CAs. + Secure bool + // Official indicates whether this is an official registry + Official bool +} + +// SearchResult describes a search result returned from a registry +type SearchResult struct { + // StarCount indicates the number of stars this repository has + StarCount int `json:"star_count"` + // IsOfficial is true if the result is from an official repository. + IsOfficial bool `json:"is_official"` + // Name is the name of the repository + Name string `json:"name"` + // IsAutomated indicates whether the result is automated + IsAutomated bool `json:"is_automated"` + // Description is a textual description of the repository + Description string `json:"description"` +} + +// SearchResults lists a collection search results returned from a registry +type SearchResults struct { + // Query contains the query string that generated the search results + Query string `json:"query"` + // NumResults indicates the number of results the query returned + NumResults int `json:"num_results"` + // Results is a slice containing the actual results for the search + Results []SearchResult `json:"results"` +} + +// DistributionInspect describes the result obtained from contacting the +// registry to retrieve image metadata +type DistributionInspect struct { + // Descriptor contains information about the manifest, including + // the content addressable digest + Descriptor v1.Descriptor + // Platforms contains the list of platforms supported by the image, + // obtained by parsing the manifest + Platforms []v1.Platform +} diff --git a/vendor/github.com/docker/docker/api/types/service_update_response.go b/vendor/github.com/docker/docker/api/types/service_update_response.go new file mode 100644 index 0000000000..74ea64b1bb --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/service_update_response.go @@ -0,0 +1,12 @@ +package types + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// ServiceUpdateResponse service update response +// swagger:model ServiceUpdateResponse +type ServiceUpdateResponse struct { + + // Optional warning messages + Warnings []string `json:"Warnings"` +} diff --git a/vendor/github.com/docker/docker/api/types/stats.go b/vendor/github.com/docker/docker/api/types/stats.go new file mode 100644 index 0000000000..20daebed14 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/stats.go @@ -0,0 +1,181 @@ +// Package types is used for API stability in the types and response to the +// consumers of the API stats endpoint. +package types // import "github.com/docker/docker/api/types" + +import "time" + +// ThrottlingData stores CPU throttling stats of one running container. +// Not used on Windows. +type ThrottlingData struct { + // Number of periods with throttling active + Periods uint64 `json:"periods"` + // Number of periods when the container hits its throttling limit. + ThrottledPeriods uint64 `json:"throttled_periods"` + // Aggregate time the container was throttled for in nanoseconds. + ThrottledTime uint64 `json:"throttled_time"` +} + +// CPUUsage stores All CPU stats aggregated since container inception. +type CPUUsage struct { + // Total CPU time consumed. + // Units: nanoseconds (Linux) + // Units: 100's of nanoseconds (Windows) + TotalUsage uint64 `json:"total_usage"` + + // Total CPU time consumed per core (Linux). Not used on Windows. + // Units: nanoseconds. + PercpuUsage []uint64 `json:"percpu_usage,omitempty"` + + // Time spent by tasks of the cgroup in kernel mode (Linux). + // Time spent by all container processes in kernel mode (Windows). + // Units: nanoseconds (Linux). + // Units: 100's of nanoseconds (Windows). Not populated for Hyper-V Containers. + UsageInKernelmode uint64 `json:"usage_in_kernelmode"` + + // Time spent by tasks of the cgroup in user mode (Linux). + // Time spent by all container processes in user mode (Windows). + // Units: nanoseconds (Linux). + // Units: 100's of nanoseconds (Windows). Not populated for Hyper-V Containers + UsageInUsermode uint64 `json:"usage_in_usermode"` +} + +// CPUStats aggregates and wraps all CPU related info of container +type CPUStats struct { + // CPU Usage. Linux and Windows. + CPUUsage CPUUsage `json:"cpu_usage"` + + // System Usage. Linux only. + SystemUsage uint64 `json:"system_cpu_usage,omitempty"` + + // Online CPUs. Linux only. + OnlineCPUs uint32 `json:"online_cpus,omitempty"` + + // Throttling Data. Linux only. + ThrottlingData ThrottlingData `json:"throttling_data,omitempty"` +} + +// MemoryStats aggregates all memory stats since container inception on Linux. +// Windows returns stats for commit and private working set only. +type MemoryStats struct { + // Linux Memory Stats + + // current res_counter usage for memory + Usage uint64 `json:"usage,omitempty"` + // maximum usage ever recorded. + MaxUsage uint64 `json:"max_usage,omitempty"` + // TODO(vishh): Export these as stronger types. + // all the stats exported via memory.stat. + Stats map[string]uint64 `json:"stats,omitempty"` + // number of times memory usage hits limits. + Failcnt uint64 `json:"failcnt,omitempty"` + Limit uint64 `json:"limit,omitempty"` + + // Windows Memory Stats + // See https://technet.microsoft.com/en-us/magazine/ff382715.aspx + + // committed bytes + Commit uint64 `json:"commitbytes,omitempty"` + // peak committed bytes + CommitPeak uint64 `json:"commitpeakbytes,omitempty"` + // private working set + PrivateWorkingSet uint64 `json:"privateworkingset,omitempty"` +} + +// BlkioStatEntry is one small entity to store a piece of Blkio stats +// Not used on Windows. +type BlkioStatEntry struct { + Major uint64 `json:"major"` + Minor uint64 `json:"minor"` + Op string `json:"op"` + Value uint64 `json:"value"` +} + +// BlkioStats stores All IO service stats for data read and write. +// This is a Linux specific structure as the differences between expressing +// block I/O on Windows and Linux are sufficiently significant to make +// little sense attempting to morph into a combined structure. +type BlkioStats struct { + // number of bytes transferred to and from the block device + IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive"` + IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive"` + IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive"` + IoServiceTimeRecursive []BlkioStatEntry `json:"io_service_time_recursive"` + IoWaitTimeRecursive []BlkioStatEntry `json:"io_wait_time_recursive"` + IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive"` + IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive"` + SectorsRecursive []BlkioStatEntry `json:"sectors_recursive"` +} + +// StorageStats is the disk I/O stats for read/write on Windows. +type StorageStats struct { + ReadCountNormalized uint64 `json:"read_count_normalized,omitempty"` + ReadSizeBytes uint64 `json:"read_size_bytes,omitempty"` + WriteCountNormalized uint64 `json:"write_count_normalized,omitempty"` + WriteSizeBytes uint64 `json:"write_size_bytes,omitempty"` +} + +// NetworkStats aggregates the network stats of one container +type NetworkStats struct { + // Bytes received. Windows and Linux. + RxBytes uint64 `json:"rx_bytes"` + // Packets received. Windows and Linux. + RxPackets uint64 `json:"rx_packets"` + // Received errors. Not used on Windows. Note that we don't `omitempty` this + // field as it is expected in the >=v1.21 API stats structure. + RxErrors uint64 `json:"rx_errors"` + // Incoming packets dropped. Windows and Linux. + RxDropped uint64 `json:"rx_dropped"` + // Bytes sent. Windows and Linux. + TxBytes uint64 `json:"tx_bytes"` + // Packets sent. Windows and Linux. + TxPackets uint64 `json:"tx_packets"` + // Sent errors. Not used on Windows. Note that we don't `omitempty` this + // field as it is expected in the >=v1.21 API stats structure. + TxErrors uint64 `json:"tx_errors"` + // Outgoing packets dropped. Windows and Linux. + TxDropped uint64 `json:"tx_dropped"` + // Endpoint ID. Not used on Linux. + EndpointID string `json:"endpoint_id,omitempty"` + // Instance ID. Not used on Linux. + InstanceID string `json:"instance_id,omitempty"` +} + +// PidsStats contains the stats of a container's pids +type PidsStats struct { + // Current is the number of pids in the cgroup + Current uint64 `json:"current,omitempty"` + // Limit is the hard limit on the number of pids in the cgroup. + // A "Limit" of 0 means that there is no limit. + Limit uint64 `json:"limit,omitempty"` +} + +// Stats is Ultimate struct aggregating all types of stats of one container +type Stats struct { + // Common stats + Read time.Time `json:"read"` + PreRead time.Time `json:"preread"` + + // Linux specific stats, not populated on Windows. + PidsStats PidsStats `json:"pids_stats,omitempty"` + BlkioStats BlkioStats `json:"blkio_stats,omitempty"` + + // Windows specific stats, not populated on Linux. + NumProcs uint32 `json:"num_procs"` + StorageStats StorageStats `json:"storage_stats,omitempty"` + + // Shared stats + CPUStats CPUStats `json:"cpu_stats,omitempty"` + PreCPUStats CPUStats `json:"precpu_stats,omitempty"` // "Pre"="Previous" + MemoryStats MemoryStats `json:"memory_stats,omitempty"` +} + +// StatsJSON is newly used Networks +type StatsJSON struct { + Stats + + Name string `json:"name,omitempty"` + ID string `json:"id,omitempty"` + + // Networks request version >=1.21 + Networks map[string]NetworkStats `json:"networks,omitempty"` +} diff --git a/vendor/github.com/docker/docker/api/types/strslice/strslice.go b/vendor/github.com/docker/docker/api/types/strslice/strslice.go new file mode 100644 index 0000000000..82921cebc1 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/strslice/strslice.go @@ -0,0 +1,30 @@ +package strslice // import "github.com/docker/docker/api/types/strslice" + +import "encoding/json" + +// StrSlice represents a string or an array of strings. +// We need to override the json decoder to accept both options. +type StrSlice []string + +// UnmarshalJSON decodes the byte slice whether it's a string or an array of +// strings. This method is needed to implement json.Unmarshaler. +func (e *StrSlice) UnmarshalJSON(b []byte) error { + if len(b) == 0 { + // With no input, we preserve the existing value by returning nil and + // leaving the target alone. This allows defining default values for + // the type. + return nil + } + + p := make([]string, 0, 1) + if err := json.Unmarshal(b, &p); err != nil { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + p = append(p, s) + } + + *e = p + return nil +} diff --git a/vendor/github.com/docker/docker/api/types/swarm/common.go b/vendor/github.com/docker/docker/api/types/swarm/common.go new file mode 100644 index 0000000000..ef020f458b --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/swarm/common.go @@ -0,0 +1,40 @@ +package swarm // import "github.com/docker/docker/api/types/swarm" + +import "time" + +// Version represents the internal object version. +type Version struct { + Index uint64 `json:",omitempty"` +} + +// Meta is a base object inherited by most of the other once. +type Meta struct { + Version Version `json:",omitempty"` + CreatedAt time.Time `json:",omitempty"` + UpdatedAt time.Time `json:",omitempty"` +} + +// Annotations represents how to describe an object. +type Annotations struct { + Name string `json:",omitempty"` + Labels map[string]string `json:"Labels"` +} + +// Driver represents a driver (network, logging, secrets backend). +type Driver struct { + Name string `json:",omitempty"` + Options map[string]string `json:",omitempty"` +} + +// TLSInfo represents the TLS information about what CA certificate is trusted, +// and who the issuer for a TLS certificate is +type TLSInfo struct { + // TrustRoot is the trusted CA root certificate in PEM format + TrustRoot string `json:",omitempty"` + + // CertIssuer is the raw subject bytes of the issuer + CertIssuerSubject []byte `json:",omitempty"` + + // CertIssuerPublicKey is the raw public key bytes of the issuer + CertIssuerPublicKey []byte `json:",omitempty"` +} diff --git a/vendor/github.com/docker/docker/api/types/swarm/config.go b/vendor/github.com/docker/docker/api/types/swarm/config.go new file mode 100644 index 0000000000..16202ccce6 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/swarm/config.go @@ -0,0 +1,40 @@ +package swarm // import "github.com/docker/docker/api/types/swarm" + +import "os" + +// Config represents a config. +type Config struct { + ID string + Meta + Spec ConfigSpec +} + +// ConfigSpec represents a config specification from a config in swarm +type ConfigSpec struct { + Annotations + Data []byte `json:",omitempty"` + + // Templating controls whether and how to evaluate the config payload as + // a template. If it is not set, no templating is used. + Templating *Driver `json:",omitempty"` +} + +// ConfigReferenceFileTarget is a file target in a config reference +type ConfigReferenceFileTarget struct { + Name string + UID string + GID string + Mode os.FileMode +} + +// ConfigReferenceRuntimeTarget is a target for a config specifying that it +// isn't mounted into the container but instead has some other purpose. +type ConfigReferenceRuntimeTarget struct{} + +// ConfigReference is a reference to a config in swarm +type ConfigReference struct { + File *ConfigReferenceFileTarget `json:",omitempty"` + Runtime *ConfigReferenceRuntimeTarget `json:",omitempty"` + ConfigID string + ConfigName string +} diff --git a/vendor/github.com/docker/docker/api/types/swarm/container.go b/vendor/github.com/docker/docker/api/types/swarm/container.go new file mode 100644 index 0000000000..af5e1c0bc2 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/swarm/container.go @@ -0,0 +1,80 @@ +package swarm // import "github.com/docker/docker/api/types/swarm" + +import ( + "time" + + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/mount" + "github.com/docker/go-units" +) + +// DNSConfig specifies DNS related configurations in resolver configuration file (resolv.conf) +// Detailed documentation is available in: +// http://man7.org/linux/man-pages/man5/resolv.conf.5.html +// `nameserver`, `search`, `options` have been supported. +// TODO: `domain` is not supported yet. +type DNSConfig struct { + // Nameservers specifies the IP addresses of the name servers + Nameservers []string `json:",omitempty"` + // Search specifies the search list for host-name lookup + Search []string `json:",omitempty"` + // Options allows certain internal resolver variables to be modified + Options []string `json:",omitempty"` +} + +// SELinuxContext contains the SELinux labels of the container. +type SELinuxContext struct { + Disable bool + + User string + Role string + Type string + Level string +} + +// CredentialSpec for managed service account (Windows only) +type CredentialSpec struct { + Config string + File string + Registry string +} + +// Privileges defines the security options for the container. +type Privileges struct { + CredentialSpec *CredentialSpec + SELinuxContext *SELinuxContext +} + +// ContainerSpec represents the spec of a container. +type ContainerSpec struct { + Image string `json:",omitempty"` + Labels map[string]string `json:",omitempty"` + Command []string `json:",omitempty"` + Args []string `json:",omitempty"` + Hostname string `json:",omitempty"` + Env []string `json:",omitempty"` + Dir string `json:",omitempty"` + User string `json:",omitempty"` + Groups []string `json:",omitempty"` + Privileges *Privileges `json:",omitempty"` + Init *bool `json:",omitempty"` + StopSignal string `json:",omitempty"` + TTY bool `json:",omitempty"` + OpenStdin bool `json:",omitempty"` + ReadOnly bool `json:",omitempty"` + Mounts []mount.Mount `json:",omitempty"` + StopGracePeriod *time.Duration `json:",omitempty"` + Healthcheck *container.HealthConfig `json:",omitempty"` + // The format of extra hosts on swarmkit is specified in: + // http://man7.org/linux/man-pages/man5/hosts.5.html + // IP_address canonical_hostname [aliases...] + Hosts []string `json:",omitempty"` + DNSConfig *DNSConfig `json:",omitempty"` + Secrets []*SecretReference `json:",omitempty"` + Configs []*ConfigReference `json:",omitempty"` + Isolation container.Isolation `json:",omitempty"` + Sysctls map[string]string `json:",omitempty"` + CapabilityAdd []string `json:",omitempty"` + CapabilityDrop []string `json:",omitempty"` + Ulimits []*units.Ulimit `json:",omitempty"` +} diff --git a/vendor/github.com/docker/docker/api/types/swarm/network.go b/vendor/github.com/docker/docker/api/types/swarm/network.go new file mode 100644 index 0000000000..98ef3284d1 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/swarm/network.go @@ -0,0 +1,121 @@ +package swarm // import "github.com/docker/docker/api/types/swarm" + +import ( + "github.com/docker/docker/api/types/network" +) + +// Endpoint represents an endpoint. +type Endpoint struct { + Spec EndpointSpec `json:",omitempty"` + Ports []PortConfig `json:",omitempty"` + VirtualIPs []EndpointVirtualIP `json:",omitempty"` +} + +// EndpointSpec represents the spec of an endpoint. +type EndpointSpec struct { + Mode ResolutionMode `json:",omitempty"` + Ports []PortConfig `json:",omitempty"` +} + +// ResolutionMode represents a resolution mode. +type ResolutionMode string + +const ( + // ResolutionModeVIP VIP + ResolutionModeVIP ResolutionMode = "vip" + // ResolutionModeDNSRR DNSRR + ResolutionModeDNSRR ResolutionMode = "dnsrr" +) + +// PortConfig represents the config of a port. +type PortConfig struct { + Name string `json:",omitempty"` + Protocol PortConfigProtocol `json:",omitempty"` + // TargetPort is the port inside the container + TargetPort uint32 `json:",omitempty"` + // PublishedPort is the port on the swarm hosts + PublishedPort uint32 `json:",omitempty"` + // PublishMode is the mode in which port is published + PublishMode PortConfigPublishMode `json:",omitempty"` +} + +// PortConfigPublishMode represents the mode in which the port is to +// be published. +type PortConfigPublishMode string + +const ( + // PortConfigPublishModeIngress is used for ports published + // for ingress load balancing using routing mesh. + PortConfigPublishModeIngress PortConfigPublishMode = "ingress" + // PortConfigPublishModeHost is used for ports published + // for direct host level access on the host where the task is running. + PortConfigPublishModeHost PortConfigPublishMode = "host" +) + +// PortConfigProtocol represents the protocol of a port. +type PortConfigProtocol string + +const ( + // TODO(stevvooe): These should be used generally, not just for PortConfig. + + // PortConfigProtocolTCP TCP + PortConfigProtocolTCP PortConfigProtocol = "tcp" + // PortConfigProtocolUDP UDP + PortConfigProtocolUDP PortConfigProtocol = "udp" + // PortConfigProtocolSCTP SCTP + PortConfigProtocolSCTP PortConfigProtocol = "sctp" +) + +// EndpointVirtualIP represents the virtual ip of a port. +type EndpointVirtualIP struct { + NetworkID string `json:",omitempty"` + Addr string `json:",omitempty"` +} + +// Network represents a network. +type Network struct { + ID string + Meta + Spec NetworkSpec `json:",omitempty"` + DriverState Driver `json:",omitempty"` + IPAMOptions *IPAMOptions `json:",omitempty"` +} + +// NetworkSpec represents the spec of a network. +type NetworkSpec struct { + Annotations + DriverConfiguration *Driver `json:",omitempty"` + IPv6Enabled bool `json:",omitempty"` + Internal bool `json:",omitempty"` + Attachable bool `json:",omitempty"` + Ingress bool `json:",omitempty"` + IPAMOptions *IPAMOptions `json:",omitempty"` + ConfigFrom *network.ConfigReference `json:",omitempty"` + Scope string `json:",omitempty"` +} + +// NetworkAttachmentConfig represents the configuration of a network attachment. +type NetworkAttachmentConfig struct { + Target string `json:",omitempty"` + Aliases []string `json:",omitempty"` + DriverOpts map[string]string `json:",omitempty"` +} + +// NetworkAttachment represents a network attachment. +type NetworkAttachment struct { + Network Network `json:",omitempty"` + Addresses []string `json:",omitempty"` +} + +// IPAMOptions represents ipam options. +type IPAMOptions struct { + Driver Driver `json:",omitempty"` + Configs []IPAMConfig `json:",omitempty"` +} + +// IPAMConfig represents ipam configuration. +type IPAMConfig struct { + Subnet string `json:",omitempty"` + Range string `json:",omitempty"` + Gateway string `json:",omitempty"` +} diff --git a/vendor/github.com/docker/docker/api/types/swarm/node.go b/vendor/github.com/docker/docker/api/types/swarm/node.go new file mode 100644 index 0000000000..1e30f5fa10 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/swarm/node.go @@ -0,0 +1,115 @@ +package swarm // import "github.com/docker/docker/api/types/swarm" + +// Node represents a node. +type Node struct { + ID string + Meta + // Spec defines the desired state of the node as specified by the user. + // The system will honor this and will *never* modify it. + Spec NodeSpec `json:",omitempty"` + // Description encapsulates the properties of the Node as reported by the + // agent. + Description NodeDescription `json:",omitempty"` + // Status provides the current status of the node, as seen by the manager. + Status NodeStatus `json:",omitempty"` + // ManagerStatus provides the current status of the node's manager + // component, if the node is a manager. + ManagerStatus *ManagerStatus `json:",omitempty"` +} + +// NodeSpec represents the spec of a node. +type NodeSpec struct { + Annotations + Role NodeRole `json:",omitempty"` + Availability NodeAvailability `json:",omitempty"` +} + +// NodeRole represents the role of a node. +type NodeRole string + +const ( + // NodeRoleWorker WORKER + NodeRoleWorker NodeRole = "worker" + // NodeRoleManager MANAGER + NodeRoleManager NodeRole = "manager" +) + +// NodeAvailability represents the availability of a node. +type NodeAvailability string + +const ( + // NodeAvailabilityActive ACTIVE + NodeAvailabilityActive NodeAvailability = "active" + // NodeAvailabilityPause PAUSE + NodeAvailabilityPause NodeAvailability = "pause" + // NodeAvailabilityDrain DRAIN + NodeAvailabilityDrain NodeAvailability = "drain" +) + +// NodeDescription represents the description of a node. +type NodeDescription struct { + Hostname string `json:",omitempty"` + Platform Platform `json:",omitempty"` + Resources Resources `json:",omitempty"` + Engine EngineDescription `json:",omitempty"` + TLSInfo TLSInfo `json:",omitempty"` +} + +// Platform represents the platform (Arch/OS). +type Platform struct { + Architecture string `json:",omitempty"` + OS string `json:",omitempty"` +} + +// EngineDescription represents the description of an engine. +type EngineDescription struct { + EngineVersion string `json:",omitempty"` + Labels map[string]string `json:",omitempty"` + Plugins []PluginDescription `json:",omitempty"` +} + +// PluginDescription represents the description of an engine plugin. +type PluginDescription struct { + Type string `json:",omitempty"` + Name string `json:",omitempty"` +} + +// NodeStatus represents the status of a node. +type NodeStatus struct { + State NodeState `json:",omitempty"` + Message string `json:",omitempty"` + Addr string `json:",omitempty"` +} + +// Reachability represents the reachability of a node. +type Reachability string + +const ( + // ReachabilityUnknown UNKNOWN + ReachabilityUnknown Reachability = "unknown" + // ReachabilityUnreachable UNREACHABLE + ReachabilityUnreachable Reachability = "unreachable" + // ReachabilityReachable REACHABLE + ReachabilityReachable Reachability = "reachable" +) + +// ManagerStatus represents the status of a manager. +type ManagerStatus struct { + Leader bool `json:",omitempty"` + Reachability Reachability `json:",omitempty"` + Addr string `json:",omitempty"` +} + +// NodeState represents the state of a node. +type NodeState string + +const ( + // NodeStateUnknown UNKNOWN + NodeStateUnknown NodeState = "unknown" + // NodeStateDown DOWN + NodeStateDown NodeState = "down" + // NodeStateReady READY + NodeStateReady NodeState = "ready" + // NodeStateDisconnected DISCONNECTED + NodeStateDisconnected NodeState = "disconnected" +) diff --git a/vendor/github.com/docker/docker/api/types/swarm/runtime.go b/vendor/github.com/docker/docker/api/types/swarm/runtime.go new file mode 100644 index 0000000000..0c77403ccf --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/swarm/runtime.go @@ -0,0 +1,27 @@ +package swarm // import "github.com/docker/docker/api/types/swarm" + +// RuntimeType is the type of runtime used for the TaskSpec +type RuntimeType string + +// RuntimeURL is the proto type url +type RuntimeURL string + +const ( + // RuntimeContainer is the container based runtime + RuntimeContainer RuntimeType = "container" + // RuntimePlugin is the plugin based runtime + RuntimePlugin RuntimeType = "plugin" + // RuntimeNetworkAttachment is the network attachment runtime + RuntimeNetworkAttachment RuntimeType = "attachment" + + // RuntimeURLContainer is the proto url for the container type + RuntimeURLContainer RuntimeURL = "types.docker.com/RuntimeContainer" + // RuntimeURLPlugin is the proto url for the plugin type + RuntimeURLPlugin RuntimeURL = "types.docker.com/RuntimePlugin" +) + +// NetworkAttachmentSpec represents the runtime spec type for network +// attachment tasks +type NetworkAttachmentSpec struct { + ContainerID string +} diff --git a/vendor/github.com/docker/docker/api/types/swarm/runtime/gen.go b/vendor/github.com/docker/docker/api/types/swarm/runtime/gen.go new file mode 100644 index 0000000000..98c2806c31 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/swarm/runtime/gen.go @@ -0,0 +1,3 @@ +//go:generate protoc -I . --gogofast_out=import_path=github.com/docker/docker/api/types/swarm/runtime:. plugin.proto + +package runtime // import "github.com/docker/docker/api/types/swarm/runtime" diff --git a/vendor/github.com/docker/docker/api/types/swarm/runtime/plugin.pb.go b/vendor/github.com/docker/docker/api/types/swarm/runtime/plugin.pb.go new file mode 100644 index 0000000000..e45045866a --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/swarm/runtime/plugin.pb.go @@ -0,0 +1,754 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: plugin.proto + +/* + Package runtime is a generated protocol buffer package. + + It is generated from these files: + plugin.proto + + It has these top-level messages: + PluginSpec + PluginPrivilege +*/ +package runtime + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +// PluginSpec defines the base payload which clients can specify for creating +// a service with the plugin runtime. +type PluginSpec struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Remote string `protobuf:"bytes,2,opt,name=remote,proto3" json:"remote,omitempty"` + Privileges []*PluginPrivilege `protobuf:"bytes,3,rep,name=privileges" json:"privileges,omitempty"` + Disabled bool `protobuf:"varint,4,opt,name=disabled,proto3" json:"disabled,omitempty"` + Env []string `protobuf:"bytes,5,rep,name=env" json:"env,omitempty"` +} + +func (m *PluginSpec) Reset() { *m = PluginSpec{} } +func (m *PluginSpec) String() string { return proto.CompactTextString(m) } +func (*PluginSpec) ProtoMessage() {} +func (*PluginSpec) Descriptor() ([]byte, []int) { return fileDescriptorPlugin, []int{0} } + +func (m *PluginSpec) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *PluginSpec) GetRemote() string { + if m != nil { + return m.Remote + } + return "" +} + +func (m *PluginSpec) GetPrivileges() []*PluginPrivilege { + if m != nil { + return m.Privileges + } + return nil +} + +func (m *PluginSpec) GetDisabled() bool { + if m != nil { + return m.Disabled + } + return false +} + +func (m *PluginSpec) GetEnv() []string { + if m != nil { + return m.Env + } + return nil +} + +// PluginPrivilege describes a permission the user has to accept +// upon installing a plugin. +type PluginPrivilege struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Value []string `protobuf:"bytes,3,rep,name=value" json:"value,omitempty"` +} + +func (m *PluginPrivilege) Reset() { *m = PluginPrivilege{} } +func (m *PluginPrivilege) String() string { return proto.CompactTextString(m) } +func (*PluginPrivilege) ProtoMessage() {} +func (*PluginPrivilege) Descriptor() ([]byte, []int) { return fileDescriptorPlugin, []int{1} } + +func (m *PluginPrivilege) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *PluginPrivilege) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func (m *PluginPrivilege) GetValue() []string { + if m != nil { + return m.Value + } + return nil +} + +func init() { + proto.RegisterType((*PluginSpec)(nil), "PluginSpec") + proto.RegisterType((*PluginPrivilege)(nil), "PluginPrivilege") +} +func (m *PluginSpec) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PluginSpec) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Name) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintPlugin(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + } + if len(m.Remote) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintPlugin(dAtA, i, uint64(len(m.Remote))) + i += copy(dAtA[i:], m.Remote) + } + if len(m.Privileges) > 0 { + for _, msg := range m.Privileges { + dAtA[i] = 0x1a + i++ + i = encodeVarintPlugin(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if m.Disabled { + dAtA[i] = 0x20 + i++ + if m.Disabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if len(m.Env) > 0 { + for _, s := range m.Env { + dAtA[i] = 0x2a + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } + return i, nil +} + +func (m *PluginPrivilege) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PluginPrivilege) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Name) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintPlugin(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + } + if len(m.Description) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintPlugin(dAtA, i, uint64(len(m.Description))) + i += copy(dAtA[i:], m.Description) + } + if len(m.Value) > 0 { + for _, s := range m.Value { + dAtA[i] = 0x1a + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } + return i, nil +} + +func encodeVarintPlugin(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *PluginSpec) Size() (n int) { + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovPlugin(uint64(l)) + } + l = len(m.Remote) + if l > 0 { + n += 1 + l + sovPlugin(uint64(l)) + } + if len(m.Privileges) > 0 { + for _, e := range m.Privileges { + l = e.Size() + n += 1 + l + sovPlugin(uint64(l)) + } + } + if m.Disabled { + n += 2 + } + if len(m.Env) > 0 { + for _, s := range m.Env { + l = len(s) + n += 1 + l + sovPlugin(uint64(l)) + } + } + return n +} + +func (m *PluginPrivilege) Size() (n int) { + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovPlugin(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovPlugin(uint64(l)) + } + if len(m.Value) > 0 { + for _, s := range m.Value { + l = len(s) + n += 1 + l + sovPlugin(uint64(l)) + } + } + return n +} + +func sovPlugin(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozPlugin(x uint64) (n int) { + return sovPlugin(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *PluginSpec) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlugin + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PluginSpec: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PluginSpec: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlugin + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlugin + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Remote", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlugin + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlugin + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Remote = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Privileges", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlugin + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPlugin + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Privileges = append(m.Privileges, &PluginPrivilege{}) + if err := m.Privileges[len(m.Privileges)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Disabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlugin + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Disabled = bool(v != 0) + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Env", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlugin + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlugin + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Env = append(m.Env, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPlugin(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthPlugin + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PluginPrivilege) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlugin + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PluginPrivilege: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PluginPrivilege: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlugin + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlugin + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlugin + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlugin + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlugin + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlugin + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = append(m.Value, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPlugin(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthPlugin + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipPlugin(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPlugin + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPlugin + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPlugin + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthPlugin + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPlugin + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipPlugin(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthPlugin = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowPlugin = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("plugin.proto", fileDescriptorPlugin) } + +var fileDescriptorPlugin = []byte{ + // 256 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x4d, 0x4b, 0xc3, 0x30, + 0x18, 0xc7, 0x89, 0xdd, 0xc6, 0xfa, 0x4c, 0x70, 0x04, 0x91, 0xe2, 0xa1, 0x94, 0x9d, 0x7a, 0x6a, + 0x45, 0x2f, 0x82, 0x37, 0x0f, 0x9e, 0x47, 0xbc, 0x09, 0x1e, 0xd2, 0xf6, 0xa1, 0x06, 0x9b, 0x17, + 0x92, 0xb4, 0xe2, 0x37, 0xf1, 0x23, 0x79, 0xf4, 0x23, 0x48, 0x3f, 0x89, 0x98, 0x75, 0x32, 0x64, + 0xa7, 0xff, 0x4b, 0xc2, 0x9f, 0x1f, 0x0f, 0x9c, 0x9a, 0xae, 0x6f, 0x85, 0x2a, 0x8c, 0xd5, 0x5e, + 0x6f, 0x3e, 0x08, 0xc0, 0x36, 0x14, 0x8f, 0x06, 0x6b, 0x4a, 0x61, 0xa6, 0xb8, 0xc4, 0x84, 0x64, + 0x24, 0x8f, 0x59, 0xf0, 0xf4, 0x02, 0x16, 0x16, 0xa5, 0xf6, 0x98, 0x9c, 0x84, 0x76, 0x4a, 0xf4, + 0x0a, 0xc0, 0x58, 0x31, 0x88, 0x0e, 0x5b, 0x74, 0x49, 0x94, 0x45, 0xf9, 0xea, 0x7a, 0x5d, 0xec, + 0xc6, 0xb6, 0xfb, 0x07, 0x76, 0xf0, 0x87, 0x5e, 0xc2, 0xb2, 0x11, 0x8e, 0x57, 0x1d, 0x36, 0xc9, + 0x2c, 0x23, 0xf9, 0x92, 0xfd, 0x65, 0xba, 0x86, 0x08, 0xd5, 0x90, 0xcc, 0xb3, 0x28, 0x8f, 0xd9, + 0xaf, 0xdd, 0x3c, 0xc3, 0xd9, 0xbf, 0xb1, 0xa3, 0x78, 0x19, 0xac, 0x1a, 0x74, 0xb5, 0x15, 0xc6, + 0x0b, 0xad, 0x26, 0xc6, 0xc3, 0x8a, 0x9e, 0xc3, 0x7c, 0xe0, 0x5d, 0x8f, 0x81, 0x31, 0x66, 0xbb, + 0x70, 0xff, 0xf0, 0x39, 0xa6, 0xe4, 0x6b, 0x4c, 0xc9, 0xf7, 0x98, 0x92, 0xa7, 0xdb, 0x56, 0xf8, + 0x97, 0xbe, 0x2a, 0x6a, 0x2d, 0xcb, 0x46, 0xd7, 0xaf, 0x68, 0xf7, 0xc2, 0x8d, 0x28, 0xfd, 0xbb, + 0x41, 0x57, 0xba, 0x37, 0x6e, 0x65, 0x69, 0x7b, 0xe5, 0x85, 0xc4, 0xbb, 0x49, 0xab, 0x45, 0x38, + 0xe4, 0xcd, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x99, 0xa8, 0xd9, 0x9b, 0x58, 0x01, 0x00, 0x00, +} diff --git a/vendor/github.com/docker/docker/api/types/swarm/runtime/plugin.proto b/vendor/github.com/docker/docker/api/types/swarm/runtime/plugin.proto new file mode 100644 index 0000000000..9ef169046b --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/swarm/runtime/plugin.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +option go_package = "github.com/docker/docker/api/types/swarm/runtime;runtime"; + +// PluginSpec defines the base payload which clients can specify for creating +// a service with the plugin runtime. +message PluginSpec { + string name = 1; + string remote = 2; + repeated PluginPrivilege privileges = 3; + bool disabled = 4; + repeated string env = 5; +} + +// PluginPrivilege describes a permission the user has to accept +// upon installing a plugin. +message PluginPrivilege { + string name = 1; + string description = 2; + repeated string value = 3; +} diff --git a/vendor/github.com/docker/docker/api/types/swarm/secret.go b/vendor/github.com/docker/docker/api/types/swarm/secret.go new file mode 100644 index 0000000000..d5213ec981 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/swarm/secret.go @@ -0,0 +1,36 @@ +package swarm // import "github.com/docker/docker/api/types/swarm" + +import "os" + +// Secret represents a secret. +type Secret struct { + ID string + Meta + Spec SecretSpec +} + +// SecretSpec represents a secret specification from a secret in swarm +type SecretSpec struct { + Annotations + Data []byte `json:",omitempty"` + Driver *Driver `json:",omitempty"` // name of the secrets driver used to fetch the secret's value from an external secret store + + // Templating controls whether and how to evaluate the secret payload as + // a template. If it is not set, no templating is used. + Templating *Driver `json:",omitempty"` +} + +// SecretReferenceFileTarget is a file target in a secret reference +type SecretReferenceFileTarget struct { + Name string + UID string + GID string + Mode os.FileMode +} + +// SecretReference is a reference to a secret in swarm +type SecretReference struct { + File *SecretReferenceFileTarget + SecretID string + SecretName string +} diff --git a/vendor/github.com/docker/docker/api/types/swarm/service.go b/vendor/github.com/docker/docker/api/types/swarm/service.go new file mode 100644 index 0000000000..6eb452d24d --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/swarm/service.go @@ -0,0 +1,202 @@ +package swarm // import "github.com/docker/docker/api/types/swarm" + +import "time" + +// Service represents a service. +type Service struct { + ID string + Meta + Spec ServiceSpec `json:",omitempty"` + PreviousSpec *ServiceSpec `json:",omitempty"` + Endpoint Endpoint `json:",omitempty"` + UpdateStatus *UpdateStatus `json:",omitempty"` + + // ServiceStatus is an optional, extra field indicating the number of + // desired and running tasks. It is provided primarily as a shortcut to + // calculating these values client-side, which otherwise would require + // listing all tasks for a service, an operation that could be + // computation and network expensive. + ServiceStatus *ServiceStatus `json:",omitempty"` + + // JobStatus is the status of a Service which is in one of ReplicatedJob or + // GlobalJob modes. It is absent on Replicated and Global services. + JobStatus *JobStatus `json:",omitempty"` +} + +// ServiceSpec represents the spec of a service. +type ServiceSpec struct { + Annotations + + // TaskTemplate defines how the service should construct new tasks when + // orchestrating this service. + TaskTemplate TaskSpec `json:",omitempty"` + Mode ServiceMode `json:",omitempty"` + UpdateConfig *UpdateConfig `json:",omitempty"` + RollbackConfig *UpdateConfig `json:",omitempty"` + + // Networks field in ServiceSpec is deprecated. The + // same field in TaskSpec should be used instead. + // This field will be removed in a future release. + Networks []NetworkAttachmentConfig `json:",omitempty"` + EndpointSpec *EndpointSpec `json:",omitempty"` +} + +// ServiceMode represents the mode of a service. +type ServiceMode struct { + Replicated *ReplicatedService `json:",omitempty"` + Global *GlobalService `json:",omitempty"` + ReplicatedJob *ReplicatedJob `json:",omitempty"` + GlobalJob *GlobalJob `json:",omitempty"` +} + +// UpdateState is the state of a service update. +type UpdateState string + +const ( + // UpdateStateUpdating is the updating state. + UpdateStateUpdating UpdateState = "updating" + // UpdateStatePaused is the paused state. + UpdateStatePaused UpdateState = "paused" + // UpdateStateCompleted is the completed state. + UpdateStateCompleted UpdateState = "completed" + // UpdateStateRollbackStarted is the state with a rollback in progress. + UpdateStateRollbackStarted UpdateState = "rollback_started" + // UpdateStateRollbackPaused is the state with a rollback in progress. + UpdateStateRollbackPaused UpdateState = "rollback_paused" + // UpdateStateRollbackCompleted is the state with a rollback in progress. + UpdateStateRollbackCompleted UpdateState = "rollback_completed" +) + +// UpdateStatus reports the status of a service update. +type UpdateStatus struct { + State UpdateState `json:",omitempty"` + StartedAt *time.Time `json:",omitempty"` + CompletedAt *time.Time `json:",omitempty"` + Message string `json:",omitempty"` +} + +// ReplicatedService is a kind of ServiceMode. +type ReplicatedService struct { + Replicas *uint64 `json:",omitempty"` +} + +// GlobalService is a kind of ServiceMode. +type GlobalService struct{} + +// ReplicatedJob is the a type of Service which executes a defined Tasks +// in parallel until the specified number of Tasks have succeeded. +type ReplicatedJob struct { + // MaxConcurrent indicates the maximum number of Tasks that should be + // executing simultaneously for this job at any given time. There may be + // fewer Tasks that MaxConcurrent executing simultaneously; for example, if + // there are fewer than MaxConcurrent tasks needed to reach + // TotalCompletions. + // + // If this field is empty, it will default to a max concurrency of 1. + MaxConcurrent *uint64 `json:",omitempty"` + + // TotalCompletions is the total number of Tasks desired to run to + // completion. + // + // If this field is empty, the value of MaxConcurrent will be used. + TotalCompletions *uint64 `json:",omitempty"` +} + +// GlobalJob is the type of a Service which executes a Task on every Node +// matching the Service's placement constraints. These tasks run to completion +// and then exit. +// +// This type is deliberately empty. +type GlobalJob struct{} + +const ( + // UpdateFailureActionPause PAUSE + UpdateFailureActionPause = "pause" + // UpdateFailureActionContinue CONTINUE + UpdateFailureActionContinue = "continue" + // UpdateFailureActionRollback ROLLBACK + UpdateFailureActionRollback = "rollback" + + // UpdateOrderStopFirst STOP_FIRST + UpdateOrderStopFirst = "stop-first" + // UpdateOrderStartFirst START_FIRST + UpdateOrderStartFirst = "start-first" +) + +// UpdateConfig represents the update configuration. +type UpdateConfig struct { + // Maximum number of tasks to be updated in one iteration. + // 0 means unlimited parallelism. + Parallelism uint64 + + // Amount of time between updates. + Delay time.Duration `json:",omitempty"` + + // FailureAction is the action to take when an update failures. + FailureAction string `json:",omitempty"` + + // Monitor indicates how long to monitor a task for failure after it is + // created. If the task fails by ending up in one of the states + // REJECTED, COMPLETED, or FAILED, within Monitor from its creation, + // this counts as a failure. If it fails after Monitor, it does not + // count as a failure. If Monitor is unspecified, a default value will + // be used. + Monitor time.Duration `json:",omitempty"` + + // MaxFailureRatio is the fraction of tasks that may fail during + // an update before the failure action is invoked. Any task created by + // the current update which ends up in one of the states REJECTED, + // COMPLETED or FAILED within Monitor from its creation counts as a + // failure. The number of failures is divided by the number of tasks + // being updated, and if this fraction is greater than + // MaxFailureRatio, the failure action is invoked. + // + // If the failure action is CONTINUE, there is no effect. + // If the failure action is PAUSE, no more tasks will be updated until + // another update is started. + MaxFailureRatio float32 + + // Order indicates the order of operations when rolling out an updated + // task. Either the old task is shut down before the new task is + // started, or the new task is started before the old task is shut down. + Order string +} + +// ServiceStatus represents the number of running tasks in a service and the +// number of tasks desired to be running. +type ServiceStatus struct { + // RunningTasks is the number of tasks for the service actually in the + // Running state + RunningTasks uint64 + + // DesiredTasks is the number of tasks desired to be running by the + // service. For replicated services, this is the replica count. For global + // services, this is computed by taking the number of tasks with desired + // state of not-Shutdown. + DesiredTasks uint64 + + // CompletedTasks is the number of tasks in the state Completed, if this + // service is in ReplicatedJob or GlobalJob mode. This field must be + // cross-referenced with the service type, because the default value of 0 + // may mean that a service is not in a job mode, or it may mean that the + // job has yet to complete any tasks. + CompletedTasks uint64 +} + +// JobStatus is the status of a job-type service. +type JobStatus struct { + // JobIteration is a value increased each time a Job is executed, + // successfully or otherwise. "Executed", in this case, means the job as a + // whole has been started, not that an individual Task has been launched. A + // job is "Executed" when its ServiceSpec is updated. JobIteration can be + // used to disambiguate Tasks belonging to different executions of a job. + // + // Though JobIteration will increase with each subsequent execution, it may + // not necessarily increase by 1, and so JobIteration should not be used to + // keep track of the number of times a job has been executed. + JobIteration Version + + // LastExecution is the time that the job was last executed, as observed by + // Swarm manager. + LastExecution time.Time `json:",omitempty"` +} diff --git a/vendor/github.com/docker/docker/api/types/swarm/swarm.go b/vendor/github.com/docker/docker/api/types/swarm/swarm.go new file mode 100644 index 0000000000..b25f999646 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/swarm/swarm.go @@ -0,0 +1,227 @@ +package swarm // import "github.com/docker/docker/api/types/swarm" + +import ( + "time" +) + +// ClusterInfo represents info about the cluster for outputting in "info" +// it contains the same information as "Swarm", but without the JoinTokens +type ClusterInfo struct { + ID string + Meta + Spec Spec + TLSInfo TLSInfo + RootRotationInProgress bool + DefaultAddrPool []string + SubnetSize uint32 + DataPathPort uint32 +} + +// Swarm represents a swarm. +type Swarm struct { + ClusterInfo + JoinTokens JoinTokens +} + +// JoinTokens contains the tokens workers and managers need to join the swarm. +type JoinTokens struct { + // Worker is the join token workers may use to join the swarm. + Worker string + // Manager is the join token managers may use to join the swarm. + Manager string +} + +// Spec represents the spec of a swarm. +type Spec struct { + Annotations + + Orchestration OrchestrationConfig `json:",omitempty"` + Raft RaftConfig `json:",omitempty"` + Dispatcher DispatcherConfig `json:",omitempty"` + CAConfig CAConfig `json:",omitempty"` + TaskDefaults TaskDefaults `json:",omitempty"` + EncryptionConfig EncryptionConfig `json:",omitempty"` +} + +// OrchestrationConfig represents orchestration configuration. +type OrchestrationConfig struct { + // TaskHistoryRetentionLimit is the number of historic tasks to keep per instance or + // node. If negative, never remove completed or failed tasks. + TaskHistoryRetentionLimit *int64 `json:",omitempty"` +} + +// TaskDefaults parameterizes cluster-level task creation with default values. +type TaskDefaults struct { + // LogDriver selects the log driver to use for tasks created in the + // orchestrator if unspecified by a service. + // + // Updating this value will only have an affect on new tasks. Old tasks + // will continue use their previously configured log driver until + // recreated. + LogDriver *Driver `json:",omitempty"` +} + +// EncryptionConfig controls at-rest encryption of data and keys. +type EncryptionConfig struct { + // AutoLockManagers specifies whether or not managers TLS keys and raft data + // should be encrypted at rest in such a way that they must be unlocked + // before the manager node starts up again. + AutoLockManagers bool +} + +// RaftConfig represents raft configuration. +type RaftConfig struct { + // SnapshotInterval is the number of log entries between snapshots. + SnapshotInterval uint64 `json:",omitempty"` + + // KeepOldSnapshots is the number of snapshots to keep beyond the + // current snapshot. + KeepOldSnapshots *uint64 `json:",omitempty"` + + // LogEntriesForSlowFollowers is the number of log entries to keep + // around to sync up slow followers after a snapshot is created. + LogEntriesForSlowFollowers uint64 `json:",omitempty"` + + // ElectionTick is the number of ticks that a follower will wait for a message + // from the leader before becoming a candidate and starting an election. + // ElectionTick must be greater than HeartbeatTick. + // + // A tick currently defaults to one second, so these translate directly to + // seconds currently, but this is NOT guaranteed. + ElectionTick int + + // HeartbeatTick is the number of ticks between heartbeats. Every + // HeartbeatTick ticks, the leader will send a heartbeat to the + // followers. + // + // A tick currently defaults to one second, so these translate directly to + // seconds currently, but this is NOT guaranteed. + HeartbeatTick int +} + +// DispatcherConfig represents dispatcher configuration. +type DispatcherConfig struct { + // HeartbeatPeriod defines how often agent should send heartbeats to + // dispatcher. + HeartbeatPeriod time.Duration `json:",omitempty"` +} + +// CAConfig represents CA configuration. +type CAConfig struct { + // NodeCertExpiry is the duration certificates should be issued for + NodeCertExpiry time.Duration `json:",omitempty"` + + // ExternalCAs is a list of CAs to which a manager node will make + // certificate signing requests for node certificates. + ExternalCAs []*ExternalCA `json:",omitempty"` + + // SigningCACert and SigningCAKey specify the desired signing root CA and + // root CA key for the swarm. When inspecting the cluster, the key will + // be redacted. + SigningCACert string `json:",omitempty"` + SigningCAKey string `json:",omitempty"` + + // If this value changes, and there is no specified signing cert and key, + // then the swarm is forced to generate a new root certificate ane key. + ForceRotate uint64 `json:",omitempty"` +} + +// ExternalCAProtocol represents type of external CA. +type ExternalCAProtocol string + +// ExternalCAProtocolCFSSL CFSSL +const ExternalCAProtocolCFSSL ExternalCAProtocol = "cfssl" + +// ExternalCA defines external CA to be used by the cluster. +type ExternalCA struct { + // Protocol is the protocol used by this external CA. + Protocol ExternalCAProtocol + + // URL is the URL where the external CA can be reached. + URL string + + // Options is a set of additional key/value pairs whose interpretation + // depends on the specified CA type. + Options map[string]string `json:",omitempty"` + + // CACert specifies which root CA is used by this external CA. This certificate must + // be in PEM format. + CACert string +} + +// InitRequest is the request used to init a swarm. +type InitRequest struct { + ListenAddr string + AdvertiseAddr string + DataPathAddr string + DataPathPort uint32 + ForceNewCluster bool + Spec Spec + AutoLockManagers bool + Availability NodeAvailability + DefaultAddrPool []string + SubnetSize uint32 +} + +// JoinRequest is the request used to join a swarm. +type JoinRequest struct { + ListenAddr string + AdvertiseAddr string + DataPathAddr string + RemoteAddrs []string + JoinToken string // accept by secret + Availability NodeAvailability +} + +// UnlockRequest is the request used to unlock a swarm. +type UnlockRequest struct { + // UnlockKey is the unlock key in ASCII-armored format. + UnlockKey string +} + +// LocalNodeState represents the state of the local node. +type LocalNodeState string + +const ( + // LocalNodeStateInactive INACTIVE + LocalNodeStateInactive LocalNodeState = "inactive" + // LocalNodeStatePending PENDING + LocalNodeStatePending LocalNodeState = "pending" + // LocalNodeStateActive ACTIVE + LocalNodeStateActive LocalNodeState = "active" + // LocalNodeStateError ERROR + LocalNodeStateError LocalNodeState = "error" + // LocalNodeStateLocked LOCKED + LocalNodeStateLocked LocalNodeState = "locked" +) + +// Info represents generic information about swarm. +type Info struct { + NodeID string + NodeAddr string + + LocalNodeState LocalNodeState + ControlAvailable bool + Error string + + RemoteManagers []Peer + Nodes int `json:",omitempty"` + Managers int `json:",omitempty"` + + Cluster *ClusterInfo `json:",omitempty"` + + Warnings []string `json:",omitempty"` +} + +// Peer represents a peer. +type Peer struct { + NodeID string + Addr string +} + +// UpdateFlags contains flags for SwarmUpdate. +type UpdateFlags struct { + RotateWorkerToken bool + RotateManagerToken bool + RotateManagerUnlockKey bool +} diff --git a/vendor/github.com/docker/docker/api/types/swarm/task.go b/vendor/github.com/docker/docker/api/types/swarm/task.go new file mode 100644 index 0000000000..a6f7ab7b5c --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/swarm/task.go @@ -0,0 +1,206 @@ +package swarm // import "github.com/docker/docker/api/types/swarm" + +import ( + "time" + + "github.com/docker/docker/api/types/swarm/runtime" +) + +// TaskState represents the state of a task. +type TaskState string + +const ( + // TaskStateNew NEW + TaskStateNew TaskState = "new" + // TaskStateAllocated ALLOCATED + TaskStateAllocated TaskState = "allocated" + // TaskStatePending PENDING + TaskStatePending TaskState = "pending" + // TaskStateAssigned ASSIGNED + TaskStateAssigned TaskState = "assigned" + // TaskStateAccepted ACCEPTED + TaskStateAccepted TaskState = "accepted" + // TaskStatePreparing PREPARING + TaskStatePreparing TaskState = "preparing" + // TaskStateReady READY + TaskStateReady TaskState = "ready" + // TaskStateStarting STARTING + TaskStateStarting TaskState = "starting" + // TaskStateRunning RUNNING + TaskStateRunning TaskState = "running" + // TaskStateComplete COMPLETE + TaskStateComplete TaskState = "complete" + // TaskStateShutdown SHUTDOWN + TaskStateShutdown TaskState = "shutdown" + // TaskStateFailed FAILED + TaskStateFailed TaskState = "failed" + // TaskStateRejected REJECTED + TaskStateRejected TaskState = "rejected" + // TaskStateRemove REMOVE + TaskStateRemove TaskState = "remove" + // TaskStateOrphaned ORPHANED + TaskStateOrphaned TaskState = "orphaned" +) + +// Task represents a task. +type Task struct { + ID string + Meta + Annotations + + Spec TaskSpec `json:",omitempty"` + ServiceID string `json:",omitempty"` + Slot int `json:",omitempty"` + NodeID string `json:",omitempty"` + Status TaskStatus `json:",omitempty"` + DesiredState TaskState `json:",omitempty"` + NetworksAttachments []NetworkAttachment `json:",omitempty"` + GenericResources []GenericResource `json:",omitempty"` + + // JobIteration is the JobIteration of the Service that this Task was + // spawned from, if the Service is a ReplicatedJob or GlobalJob. This is + // used to determine which Tasks belong to which run of the job. This field + // is absent if the Service mode is Replicated or Global. + JobIteration *Version `json:",omitempty"` +} + +// TaskSpec represents the spec of a task. +type TaskSpec struct { + // ContainerSpec, NetworkAttachmentSpec, and PluginSpec are mutually exclusive. + // PluginSpec is only used when the `Runtime` field is set to `plugin` + // NetworkAttachmentSpec is used if the `Runtime` field is set to + // `attachment`. + ContainerSpec *ContainerSpec `json:",omitempty"` + PluginSpec *runtime.PluginSpec `json:",omitempty"` + NetworkAttachmentSpec *NetworkAttachmentSpec `json:",omitempty"` + + Resources *ResourceRequirements `json:",omitempty"` + RestartPolicy *RestartPolicy `json:",omitempty"` + Placement *Placement `json:",omitempty"` + Networks []NetworkAttachmentConfig `json:",omitempty"` + + // LogDriver specifies the LogDriver to use for tasks created from this + // spec. If not present, the one on cluster default on swarm.Spec will be + // used, finally falling back to the engine default if not specified. + LogDriver *Driver `json:",omitempty"` + + // ForceUpdate is a counter that triggers an update even if no relevant + // parameters have been changed. + ForceUpdate uint64 + + Runtime RuntimeType `json:",omitempty"` +} + +// Resources represents resources (CPU/Memory) which can be advertised by a +// node and requested to be reserved for a task. +type Resources struct { + NanoCPUs int64 `json:",omitempty"` + MemoryBytes int64 `json:",omitempty"` + GenericResources []GenericResource `json:",omitempty"` +} + +// Limit describes limits on resources which can be requested by a task. +type Limit struct { + NanoCPUs int64 `json:",omitempty"` + MemoryBytes int64 `json:",omitempty"` + Pids int64 `json:",omitempty"` +} + +// GenericResource represents a "user defined" resource which can +// be either an integer (e.g: SSD=3) or a string (e.g: SSD=sda1) +type GenericResource struct { + NamedResourceSpec *NamedGenericResource `json:",omitempty"` + DiscreteResourceSpec *DiscreteGenericResource `json:",omitempty"` +} + +// NamedGenericResource represents a "user defined" resource which is defined +// as a string. +// "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...) +// Value is used to identify the resource (GPU="UUID-1", FPGA="/dev/sdb5", ...) +type NamedGenericResource struct { + Kind string `json:",omitempty"` + Value string `json:",omitempty"` +} + +// DiscreteGenericResource represents a "user defined" resource which is defined +// as an integer +// "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...) +// Value is used to count the resource (SSD=5, HDD=3, ...) +type DiscreteGenericResource struct { + Kind string `json:",omitempty"` + Value int64 `json:",omitempty"` +} + +// ResourceRequirements represents resources requirements. +type ResourceRequirements struct { + Limits *Limit `json:",omitempty"` + Reservations *Resources `json:",omitempty"` +} + +// Placement represents orchestration parameters. +type Placement struct { + Constraints []string `json:",omitempty"` + Preferences []PlacementPreference `json:",omitempty"` + MaxReplicas uint64 `json:",omitempty"` + + // Platforms stores all the platforms that the image can run on. + // This field is used in the platform filter for scheduling. If empty, + // then the platform filter is off, meaning there are no scheduling restrictions. + Platforms []Platform `json:",omitempty"` +} + +// PlacementPreference provides a way to make the scheduler aware of factors +// such as topology. +type PlacementPreference struct { + Spread *SpreadOver +} + +// SpreadOver is a scheduling preference that instructs the scheduler to spread +// tasks evenly over groups of nodes identified by labels. +type SpreadOver struct { + // label descriptor, such as engine.labels.az + SpreadDescriptor string +} + +// RestartPolicy represents the restart policy. +type RestartPolicy struct { + Condition RestartPolicyCondition `json:",omitempty"` + Delay *time.Duration `json:",omitempty"` + MaxAttempts *uint64 `json:",omitempty"` + Window *time.Duration `json:",omitempty"` +} + +// RestartPolicyCondition represents when to restart. +type RestartPolicyCondition string + +const ( + // RestartPolicyConditionNone NONE + RestartPolicyConditionNone RestartPolicyCondition = "none" + // RestartPolicyConditionOnFailure ON_FAILURE + RestartPolicyConditionOnFailure RestartPolicyCondition = "on-failure" + // RestartPolicyConditionAny ANY + RestartPolicyConditionAny RestartPolicyCondition = "any" +) + +// TaskStatus represents the status of a task. +type TaskStatus struct { + Timestamp time.Time `json:",omitempty"` + State TaskState `json:",omitempty"` + Message string `json:",omitempty"` + Err string `json:",omitempty"` + ContainerStatus *ContainerStatus `json:",omitempty"` + PortStatus PortStatus `json:",omitempty"` +} + +// ContainerStatus represents the status of a container. +type ContainerStatus struct { + ContainerID string + PID int + ExitCode int +} + +// PortStatus represents the port status of a task's host ports whose +// service has published host ports +type PortStatus struct { + Ports []PortConfig `json:",omitempty"` +} diff --git a/vendor/github.com/docker/docker/api/types/time/duration_convert.go b/vendor/github.com/docker/docker/api/types/time/duration_convert.go new file mode 100644 index 0000000000..84b6f07322 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/time/duration_convert.go @@ -0,0 +1,12 @@ +package time // import "github.com/docker/docker/api/types/time" + +import ( + "strconv" + "time" +) + +// DurationToSecondsString converts the specified duration to the number +// seconds it represents, formatted as a string. +func DurationToSecondsString(duration time.Duration) string { + return strconv.FormatFloat(duration.Seconds(), 'f', 0, 64) +} diff --git a/vendor/github.com/docker/docker/api/types/time/timestamp.go b/vendor/github.com/docker/docker/api/types/time/timestamp.go new file mode 100644 index 0000000000..ea3495efeb --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/time/timestamp.go @@ -0,0 +1,129 @@ +package time // import "github.com/docker/docker/api/types/time" + +import ( + "fmt" + "math" + "strconv" + "strings" + "time" +) + +// These are additional predefined layouts for use in Time.Format and Time.Parse +// with --since and --until parameters for `docker logs` and `docker events` +const ( + rFC3339Local = "2006-01-02T15:04:05" // RFC3339 with local timezone + rFC3339NanoLocal = "2006-01-02T15:04:05.999999999" // RFC3339Nano with local timezone + dateWithZone = "2006-01-02Z07:00" // RFC3339 with time at 00:00:00 + dateLocal = "2006-01-02" // RFC3339 with local timezone and time at 00:00:00 +) + +// GetTimestamp tries to parse given string as golang duration, +// then RFC3339 time and finally as a Unix timestamp. If +// any of these were successful, it returns a Unix timestamp +// as string otherwise returns the given value back. +// In case of duration input, the returned timestamp is computed +// as the given reference time minus the amount of the duration. +func GetTimestamp(value string, reference time.Time) (string, error) { + if d, err := time.ParseDuration(value); value != "0" && err == nil { + return strconv.FormatInt(reference.Add(-d).Unix(), 10), nil + } + + var format string + // if the string has a Z or a + or three dashes use parse otherwise use parseinlocation + parseInLocation := !(strings.ContainsAny(value, "zZ+") || strings.Count(value, "-") == 3) + + if strings.Contains(value, ".") { + if parseInLocation { + format = rFC3339NanoLocal + } else { + format = time.RFC3339Nano + } + } else if strings.Contains(value, "T") { + // we want the number of colons in the T portion of the timestamp + tcolons := strings.Count(value, ":") + // if parseInLocation is off and we have a +/- zone offset (not Z) then + // there will be an extra colon in the input for the tz offset subtract that + // colon from the tcolons count + if !parseInLocation && !strings.ContainsAny(value, "zZ") && tcolons > 0 { + tcolons-- + } + if parseInLocation { + switch tcolons { + case 0: + format = "2006-01-02T15" + case 1: + format = "2006-01-02T15:04" + default: + format = rFC3339Local + } + } else { + switch tcolons { + case 0: + format = "2006-01-02T15Z07:00" + case 1: + format = "2006-01-02T15:04Z07:00" + default: + format = time.RFC3339 + } + } + } else if parseInLocation { + format = dateLocal + } else { + format = dateWithZone + } + + var t time.Time + var err error + + if parseInLocation { + t, err = time.ParseInLocation(format, value, time.FixedZone(reference.Zone())) + } else { + t, err = time.Parse(format, value) + } + + if err != nil { + // if there is a `-` then it's an RFC3339 like timestamp + if strings.Contains(value, "-") { + return "", err // was probably an RFC3339 like timestamp but the parser failed with an error + } + if _, _, err := parseTimestamp(value); err != nil { + return "", fmt.Errorf("failed to parse value as time or duration: %q", value) + } + return value, nil // unix timestamp in and out case (meaning: the value passed at the command line is already in the right format for passing to the server) + } + + return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond())), nil +} + +// ParseTimestamps returns seconds and nanoseconds from a timestamp that has the +// format "%d.%09d", time.Unix(), int64(time.Nanosecond())) +// if the incoming nanosecond portion is longer or shorter than 9 digits it is +// converted to nanoseconds. The expectation is that the seconds and +// seconds will be used to create a time variable. For example: +// seconds, nanoseconds, err := ParseTimestamp("1136073600.000000001",0) +// if err == nil since := time.Unix(seconds, nanoseconds) +// returns seconds as def(aultSeconds) if value == "" +func ParseTimestamps(value string, def int64) (int64, int64, error) { + if value == "" { + return def, 0, nil + } + return parseTimestamp(value) +} + +func parseTimestamp(value string) (int64, int64, error) { + sa := strings.SplitN(value, ".", 2) + s, err := strconv.ParseInt(sa[0], 10, 64) + if err != nil { + return s, 0, err + } + if len(sa) != 2 { + return s, 0, nil + } + n, err := strconv.ParseInt(sa[1], 10, 64) + if err != nil { + return s, n, err + } + // should already be in nanoseconds but just in case convert n to nanoseconds + n = int64(float64(n) * math.Pow(float64(10), float64(9-len(sa[1])))) + return s, n, nil +} diff --git a/vendor/github.com/docker/docker/api/types/types.go b/vendor/github.com/docker/docker/api/types/types.go new file mode 100644 index 0000000000..e3a159912e --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/types.go @@ -0,0 +1,635 @@ +package types // import "github.com/docker/docker/api/types" + +import ( + "errors" + "fmt" + "io" + "os" + "strings" + "time" + + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/mount" + "github.com/docker/docker/api/types/network" + "github.com/docker/docker/api/types/registry" + "github.com/docker/docker/api/types/swarm" + "github.com/docker/go-connections/nat" +) + +// RootFS returns Image's RootFS description including the layer IDs. +type RootFS struct { + Type string + Layers []string `json:",omitempty"` + BaseLayer string `json:",omitempty"` +} + +// ImageInspect contains response of Engine API: +// GET "/images/{name:.*}/json" +type ImageInspect struct { + ID string `json:"Id"` + RepoTags []string + RepoDigests []string + Parent string + Comment string + Created string + Container string + ContainerConfig *container.Config + DockerVersion string + Author string + Config *container.Config + Architecture string + Variant string `json:",omitempty"` + Os string + OsVersion string `json:",omitempty"` + Size int64 + VirtualSize int64 + GraphDriver GraphDriverData + RootFS RootFS + Metadata ImageMetadata +} + +// ImageMetadata contains engine-local data about the image +type ImageMetadata struct { + LastTagTime time.Time `json:",omitempty"` +} + +// Container contains response of Engine API: +// GET "/containers/json" +type Container struct { + ID string `json:"Id"` + Names []string + Image string + ImageID string + Command string + Created int64 + Ports []Port + SizeRw int64 `json:",omitempty"` + SizeRootFs int64 `json:",omitempty"` + Labels map[string]string + State string + Status string + HostConfig struct { + NetworkMode string `json:",omitempty"` + } + NetworkSettings *SummaryNetworkSettings + Mounts []MountPoint +} + +// CopyConfig contains request body of Engine API: +// POST "/containers/"+containerID+"/copy" +type CopyConfig struct { + Resource string +} + +// ContainerPathStat is used to encode the header from +// GET "/containers/{name:.*}/archive" +// "Name" is the file or directory name. +type ContainerPathStat struct { + Name string `json:"name"` + Size int64 `json:"size"` + Mode os.FileMode `json:"mode"` + Mtime time.Time `json:"mtime"` + LinkTarget string `json:"linkTarget"` +} + +// ContainerStats contains response of Engine API: +// GET "/stats" +type ContainerStats struct { + Body io.ReadCloser `json:"body"` + OSType string `json:"ostype"` +} + +// Ping contains response of Engine API: +// GET "/_ping" +type Ping struct { + APIVersion string + OSType string + Experimental bool + BuilderVersion BuilderVersion +} + +// ComponentVersion describes the version information for a specific component. +type ComponentVersion struct { + Name string + Version string + Details map[string]string `json:",omitempty"` +} + +// Version contains response of Engine API: +// GET "/version" +type Version struct { + Platform struct{ Name string } `json:",omitempty"` + Components []ComponentVersion `json:",omitempty"` + + // The following fields are deprecated, they relate to the Engine component and are kept for backwards compatibility + + Version string + APIVersion string `json:"ApiVersion"` + MinAPIVersion string `json:"MinAPIVersion,omitempty"` + GitCommit string + GoVersion string + Os string + Arch string + KernelVersion string `json:",omitempty"` + Experimental bool `json:",omitempty"` + BuildTime string `json:",omitempty"` +} + +// Commit holds the Git-commit (SHA1) that a binary was built from, as reported +// in the version-string of external tools, such as containerd, or runC. +type Commit struct { + ID string // ID is the actual commit ID of external tool. + Expected string // Expected is the commit ID of external tool expected by dockerd as set at build time. +} + +// Info contains response of Engine API: +// GET "/info" +type Info struct { + ID string + Containers int + ContainersRunning int + ContainersPaused int + ContainersStopped int + Images int + Driver string + DriverStatus [][2]string + SystemStatus [][2]string `json:",omitempty"` // SystemStatus is only propagated by the Swarm standalone API + Plugins PluginsInfo + MemoryLimit bool + SwapLimit bool + KernelMemory bool // Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes + KernelMemoryTCP bool + CPUCfsPeriod bool `json:"CpuCfsPeriod"` + CPUCfsQuota bool `json:"CpuCfsQuota"` + CPUShares bool + CPUSet bool + PidsLimit bool + IPv4Forwarding bool + BridgeNfIptables bool + BridgeNfIP6tables bool `json:"BridgeNfIp6tables"` + Debug bool + NFd int + OomKillDisable bool + NGoroutines int + SystemTime string + LoggingDriver string + CgroupDriver string + CgroupVersion string `json:",omitempty"` + NEventsListener int + KernelVersion string + OperatingSystem string + OSVersion string + OSType string + Architecture string + IndexServerAddress string + RegistryConfig *registry.ServiceConfig + NCPU int + MemTotal int64 + GenericResources []swarm.GenericResource + DockerRootDir string + HTTPProxy string `json:"HttpProxy"` + HTTPSProxy string `json:"HttpsProxy"` + NoProxy string + Name string + Labels []string + ExperimentalBuild bool + ServerVersion string + ClusterStore string `json:",omitempty"` // Deprecated: host-discovery and overlay networks with external k/v stores are deprecated + ClusterAdvertise string `json:",omitempty"` // Deprecated: host-discovery and overlay networks with external k/v stores are deprecated + Runtimes map[string]Runtime + DefaultRuntime string + Swarm swarm.Info + // LiveRestoreEnabled determines whether containers should be kept + // running when the daemon is shutdown or upon daemon start if + // running containers are detected + LiveRestoreEnabled bool + Isolation container.Isolation + InitBinary string + ContainerdCommit Commit + RuncCommit Commit + InitCommit Commit + SecurityOptions []string + ProductLicense string `json:",omitempty"` + DefaultAddressPools []NetworkAddressPool `json:",omitempty"` + Warnings []string +} + +// KeyValue holds a key/value pair +type KeyValue struct { + Key, Value string +} + +// NetworkAddressPool is a temp struct used by Info struct +type NetworkAddressPool struct { + Base string + Size int +} + +// SecurityOpt contains the name and options of a security option +type SecurityOpt struct { + Name string + Options []KeyValue +} + +// DecodeSecurityOptions decodes a security options string slice to a type safe +// SecurityOpt +func DecodeSecurityOptions(opts []string) ([]SecurityOpt, error) { + so := []SecurityOpt{} + for _, opt := range opts { + // support output from a < 1.13 docker daemon + if !strings.Contains(opt, "=") { + so = append(so, SecurityOpt{Name: opt}) + continue + } + secopt := SecurityOpt{} + split := strings.Split(opt, ",") + for _, s := range split { + kv := strings.SplitN(s, "=", 2) + if len(kv) != 2 { + return nil, fmt.Errorf("invalid security option %q", s) + } + if kv[0] == "" || kv[1] == "" { + return nil, errors.New("invalid empty security option") + } + if kv[0] == "name" { + secopt.Name = kv[1] + continue + } + secopt.Options = append(secopt.Options, KeyValue{Key: kv[0], Value: kv[1]}) + } + so = append(so, secopt) + } + return so, nil +} + +// PluginsInfo is a temp struct holding Plugins name +// registered with docker daemon. It is used by Info struct +type PluginsInfo struct { + // List of Volume plugins registered + Volume []string + // List of Network plugins registered + Network []string + // List of Authorization plugins registered + Authorization []string + // List of Log plugins registered + Log []string +} + +// ExecStartCheck is a temp struct used by execStart +// Config fields is part of ExecConfig in runconfig package +type ExecStartCheck struct { + // ExecStart will first check if it's detached + Detach bool + // Check if there's a tty + Tty bool +} + +// HealthcheckResult stores information about a single run of a healthcheck probe +type HealthcheckResult struct { + Start time.Time // Start is the time this check started + End time.Time // End is the time this check ended + ExitCode int // ExitCode meanings: 0=healthy, 1=unhealthy, 2=reserved (considered unhealthy), else=error running probe + Output string // Output from last check +} + +// Health states +const ( + NoHealthcheck = "none" // Indicates there is no healthcheck + Starting = "starting" // Starting indicates that the container is not yet ready + Healthy = "healthy" // Healthy indicates that the container is running correctly + Unhealthy = "unhealthy" // Unhealthy indicates that the container has a problem +) + +// Health stores information about the container's healthcheck results +type Health struct { + Status string // Status is one of Starting, Healthy or Unhealthy + FailingStreak int // FailingStreak is the number of consecutive failures + Log []*HealthcheckResult // Log contains the last few results (oldest first) +} + +// ContainerState stores container's running state +// it's part of ContainerJSONBase and will return by "inspect" command +type ContainerState struct { + Status string // String representation of the container state. Can be one of "created", "running", "paused", "restarting", "removing", "exited", or "dead" + Running bool + Paused bool + Restarting bool + OOMKilled bool + Dead bool + Pid int + ExitCode int + Error string + StartedAt string + FinishedAt string + Health *Health `json:",omitempty"` +} + +// ContainerNode stores information about the node that a container +// is running on. It's only used by the Docker Swarm standalone API +type ContainerNode struct { + ID string + IPAddress string `json:"IP"` + Addr string + Name string + Cpus int + Memory int64 + Labels map[string]string +} + +// ContainerJSONBase contains response of Engine API: +// GET "/containers/{name:.*}/json" +type ContainerJSONBase struct { + ID string `json:"Id"` + Created string + Path string + Args []string + State *ContainerState + Image string + ResolvConfPath string + HostnamePath string + HostsPath string + LogPath string + Node *ContainerNode `json:",omitempty"` // Node is only propagated by Docker Swarm standalone API + Name string + RestartCount int + Driver string + Platform string + MountLabel string + ProcessLabel string + AppArmorProfile string + ExecIDs []string + HostConfig *container.HostConfig + GraphDriver GraphDriverData + SizeRw *int64 `json:",omitempty"` + SizeRootFs *int64 `json:",omitempty"` +} + +// ContainerJSON is newly used struct along with MountPoint +type ContainerJSON struct { + *ContainerJSONBase + Mounts []MountPoint + Config *container.Config + NetworkSettings *NetworkSettings +} + +// NetworkSettings exposes the network settings in the api +type NetworkSettings struct { + NetworkSettingsBase + DefaultNetworkSettings + Networks map[string]*network.EndpointSettings +} + +// SummaryNetworkSettings provides a summary of container's networks +// in /containers/json +type SummaryNetworkSettings struct { + Networks map[string]*network.EndpointSettings +} + +// NetworkSettingsBase holds basic information about networks +type NetworkSettingsBase struct { + Bridge string // Bridge is the Bridge name the network uses(e.g. `docker0`) + SandboxID string // SandboxID uniquely represents a container's network stack + HairpinMode bool // HairpinMode specifies if hairpin NAT should be enabled on the virtual interface + LinkLocalIPv6Address string // LinkLocalIPv6Address is an IPv6 unicast address using the link-local prefix + LinkLocalIPv6PrefixLen int // LinkLocalIPv6PrefixLen is the prefix length of an IPv6 unicast address + Ports nat.PortMap // Ports is a collection of PortBinding indexed by Port + SandboxKey string // SandboxKey identifies the sandbox + SecondaryIPAddresses []network.Address + SecondaryIPv6Addresses []network.Address +} + +// DefaultNetworkSettings holds network information +// during the 2 release deprecation period. +// It will be removed in Docker 1.11. +type DefaultNetworkSettings struct { + EndpointID string // EndpointID uniquely represents a service endpoint in a Sandbox + Gateway string // Gateway holds the gateway address for the network + GlobalIPv6Address string // GlobalIPv6Address holds network's global IPv6 address + GlobalIPv6PrefixLen int // GlobalIPv6PrefixLen represents mask length of network's global IPv6 address + IPAddress string // IPAddress holds the IPv4 address for the network + IPPrefixLen int // IPPrefixLen represents mask length of network's IPv4 address + IPv6Gateway string // IPv6Gateway holds gateway address specific for IPv6 + MacAddress string // MacAddress holds the MAC address for the network +} + +// MountPoint represents a mount point configuration inside the container. +// This is used for reporting the mountpoints in use by a container. +type MountPoint struct { + Type mount.Type `json:",omitempty"` + Name string `json:",omitempty"` + Source string + Destination string + Driver string `json:",omitempty"` + Mode string + RW bool + Propagation mount.Propagation +} + +// NetworkResource is the body of the "get network" http response message +type NetworkResource struct { + Name string // Name is the requested name of the network + ID string `json:"Id"` // ID uniquely identifies a network on a single machine + Created time.Time // Created is the time the network created + Scope string // Scope describes the level at which the network exists (e.g. `swarm` for cluster-wide or `local` for machine level) + Driver string // Driver is the Driver name used to create the network (e.g. `bridge`, `overlay`) + EnableIPv6 bool // EnableIPv6 represents whether to enable IPv6 + IPAM network.IPAM // IPAM is the network's IP Address Management + Internal bool // Internal represents if the network is used internal only + Attachable bool // Attachable represents if the global scope is manually attachable by regular containers from workers in swarm mode. + Ingress bool // Ingress indicates the network is providing the routing-mesh for the swarm cluster. + ConfigFrom network.ConfigReference // ConfigFrom specifies the source which will provide the configuration for this network. + ConfigOnly bool // ConfigOnly networks are place-holder networks for network configurations to be used by other networks. ConfigOnly networks cannot be used directly to run containers or services. + Containers map[string]EndpointResource // Containers contains endpoints belonging to the network + Options map[string]string // Options holds the network specific options to use for when creating the network + Labels map[string]string // Labels holds metadata specific to the network being created + Peers []network.PeerInfo `json:",omitempty"` // List of peer nodes for an overlay network + Services map[string]network.ServiceInfo `json:",omitempty"` +} + +// EndpointResource contains network resources allocated and used for a container in a network +type EndpointResource struct { + Name string + EndpointID string + MacAddress string + IPv4Address string + IPv6Address string +} + +// NetworkCreate is the expected body of the "create network" http request message +type NetworkCreate struct { + // Check for networks with duplicate names. + // Network is primarily keyed based on a random ID and not on the name. + // Network name is strictly a user-friendly alias to the network + // which is uniquely identified using ID. + // And there is no guaranteed way to check for duplicates. + // Option CheckDuplicate is there to provide a best effort checking of any networks + // which has the same name but it is not guaranteed to catch all name collisions. + CheckDuplicate bool + Driver string + Scope string + EnableIPv6 bool + IPAM *network.IPAM + Internal bool + Attachable bool + Ingress bool + ConfigOnly bool + ConfigFrom *network.ConfigReference + Options map[string]string + Labels map[string]string +} + +// NetworkCreateRequest is the request message sent to the server for network create call. +type NetworkCreateRequest struct { + NetworkCreate + Name string +} + +// NetworkCreateResponse is the response message sent by the server for network create call +type NetworkCreateResponse struct { + ID string `json:"Id"` + Warning string +} + +// NetworkConnect represents the data to be used to connect a container to the network +type NetworkConnect struct { + Container string + EndpointConfig *network.EndpointSettings `json:",omitempty"` +} + +// NetworkDisconnect represents the data to be used to disconnect a container from the network +type NetworkDisconnect struct { + Container string + Force bool +} + +// NetworkInspectOptions holds parameters to inspect network +type NetworkInspectOptions struct { + Scope string + Verbose bool +} + +// Checkpoint represents the details of a checkpoint +type Checkpoint struct { + Name string // Name is the name of the checkpoint +} + +// Runtime describes an OCI runtime +type Runtime struct { + Path string `json:"path"` + Args []string `json:"runtimeArgs,omitempty"` + + // This is exposed here only for internal use + // It is not currently supported to specify custom shim configs + Shim *ShimConfig `json:"-"` +} + +// ShimConfig is used by runtime to configure containerd shims +type ShimConfig struct { + Binary string + Opts interface{} +} + +// DiskUsage contains response of Engine API: +// GET "/system/df" +type DiskUsage struct { + LayersSize int64 + Images []*ImageSummary + Containers []*Container + Volumes []*Volume + BuildCache []*BuildCache + BuilderSize int64 // deprecated +} + +// ContainersPruneReport contains the response for Engine API: +// POST "/containers/prune" +type ContainersPruneReport struct { + ContainersDeleted []string + SpaceReclaimed uint64 +} + +// VolumesPruneReport contains the response for Engine API: +// POST "/volumes/prune" +type VolumesPruneReport struct { + VolumesDeleted []string + SpaceReclaimed uint64 +} + +// ImagesPruneReport contains the response for Engine API: +// POST "/images/prune" +type ImagesPruneReport struct { + ImagesDeleted []ImageDeleteResponseItem + SpaceReclaimed uint64 +} + +// BuildCachePruneReport contains the response for Engine API: +// POST "/build/prune" +type BuildCachePruneReport struct { + CachesDeleted []string + SpaceReclaimed uint64 +} + +// NetworksPruneReport contains the response for Engine API: +// POST "/networks/prune" +type NetworksPruneReport struct { + NetworksDeleted []string +} + +// SecretCreateResponse contains the information returned to a client +// on the creation of a new secret. +type SecretCreateResponse struct { + // ID is the id of the created secret. + ID string +} + +// SecretListOptions holds parameters to list secrets +type SecretListOptions struct { + Filters filters.Args +} + +// ConfigCreateResponse contains the information returned to a client +// on the creation of a new config. +type ConfigCreateResponse struct { + // ID is the id of the created config. + ID string +} + +// ConfigListOptions holds parameters to list configs +type ConfigListOptions struct { + Filters filters.Args +} + +// PushResult contains the tag, manifest digest, and manifest size from the +// push. It's used to signal this information to the trust code in the client +// so it can sign the manifest if necessary. +type PushResult struct { + Tag string + Digest string + Size int +} + +// BuildResult contains the image id of a successful build +type BuildResult struct { + ID string +} + +// BuildCache contains information about a build cache record +type BuildCache struct { + ID string + Parent string + Type string + Description string + InUse bool + Shared bool + Size int64 + CreatedAt time.Time + LastUsedAt *time.Time + UsageCount int +} + +// BuildCachePruneOptions hold parameters to prune the build cache +type BuildCachePruneOptions struct { + All bool + KeepStorage int64 + Filters filters.Args +} diff --git a/vendor/github.com/docker/docker/api/types/versions/README.md b/vendor/github.com/docker/docker/api/types/versions/README.md new file mode 100644 index 0000000000..1ef911edb0 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/versions/README.md @@ -0,0 +1,14 @@ +# Legacy API type versions + +This package includes types for legacy API versions. The stable version of the API types live in `api/types/*.go`. + +Consider moving a type here when you need to keep backwards compatibility in the API. This legacy types are organized by the latest API version they appear in. For instance, types in the `v1p19` package are valid for API versions below or equal `1.19`. Types in the `v1p20` package are valid for the API version `1.20`, since the versions below that will use the legacy types in `v1p19`. + +## Package name conventions + +The package name convention is to use `v` as a prefix for the version number and `p`(patch) as a separator. We use this nomenclature due to a few restrictions in the Go package name convention: + +1. We cannot use `.` because it's interpreted by the language, think of `v1.20.CallFunction`. +2. We cannot use `_` because golint complains about it. The code is actually valid, but it looks probably more weird: `v1_20.CallFunction`. + +For instance, if you want to modify a type that was available in the version `1.21` of the API but it will have different fields in the version `1.22`, you want to create a new package under `api/types/versions/v1p21`. diff --git a/vendor/github.com/docker/docker/api/types/versions/compare.go b/vendor/github.com/docker/docker/api/types/versions/compare.go new file mode 100644 index 0000000000..8ccb0aa92e --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/versions/compare.go @@ -0,0 +1,62 @@ +package versions // import "github.com/docker/docker/api/types/versions" + +import ( + "strconv" + "strings" +) + +// compare compares two version strings +// returns -1 if v1 < v2, 1 if v1 > v2, 0 otherwise. +func compare(v1, v2 string) int { + var ( + currTab = strings.Split(v1, ".") + otherTab = strings.Split(v2, ".") + ) + + max := len(currTab) + if len(otherTab) > max { + max = len(otherTab) + } + for i := 0; i < max; i++ { + var currInt, otherInt int + + if len(currTab) > i { + currInt, _ = strconv.Atoi(currTab[i]) + } + if len(otherTab) > i { + otherInt, _ = strconv.Atoi(otherTab[i]) + } + if currInt > otherInt { + return 1 + } + if otherInt > currInt { + return -1 + } + } + return 0 +} + +// LessThan checks if a version is less than another +func LessThan(v, other string) bool { + return compare(v, other) == -1 +} + +// LessThanOrEqualTo checks if a version is less than or equal to another +func LessThanOrEqualTo(v, other string) bool { + return compare(v, other) <= 0 +} + +// GreaterThan checks if a version is greater than another +func GreaterThan(v, other string) bool { + return compare(v, other) == 1 +} + +// GreaterThanOrEqualTo checks if a version is greater than or equal to another +func GreaterThanOrEqualTo(v, other string) bool { + return compare(v, other) >= 0 +} + +// Equal checks if a version is equal to another +func Equal(v, other string) bool { + return compare(v, other) == 0 +} diff --git a/vendor/github.com/docker/docker/api/types/volume.go b/vendor/github.com/docker/docker/api/types/volume.go new file mode 100644 index 0000000000..c69b08448d --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/volume.go @@ -0,0 +1,72 @@ +package types + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// Volume volume +// swagger:model Volume +type Volume struct { + + // Date/Time the volume was created. + CreatedAt string `json:"CreatedAt,omitempty"` + + // Name of the volume driver used by the volume. + // Required: true + Driver string `json:"Driver"` + + // User-defined key/value metadata. + // Required: true + Labels map[string]string `json:"Labels"` + + // Mount path of the volume on the host. + // Required: true + Mountpoint string `json:"Mountpoint"` + + // Name of the volume. + // Required: true + Name string `json:"Name"` + + // The driver specific options used when creating the volume. + // + // Required: true + Options map[string]string `json:"Options"` + + // The level at which the volume exists. Either `global` for cluster-wide, + // or `local` for machine level. + // + // Required: true + Scope string `json:"Scope"` + + // Low-level details about the volume, provided by the volume driver. + // Details are returned as a map with key/value pairs: + // `{"key":"value","key2":"value2"}`. + // + // The `Status` field is optional, and is omitted if the volume driver + // does not support this feature. + // + Status map[string]interface{} `json:"Status,omitempty"` + + // usage data + UsageData *VolumeUsageData `json:"UsageData,omitempty"` +} + +// VolumeUsageData Usage details about the volume. This information is used by the +// `GET /system/df` endpoint, and omitted in other endpoints. +// +// swagger:model VolumeUsageData +type VolumeUsageData struct { + + // The number of containers referencing this volume. This field + // is set to `-1` if the reference-count is not available. + // + // Required: true + RefCount int64 `json:"RefCount"` + + // Amount of disk space used by the volume (in bytes). This information + // is only available for volumes created with the `"local"` volume + // driver. For volumes created with other volume drivers, this field + // is set to `-1` ("not available") + // + // Required: true + Size int64 `json:"Size"` +} diff --git a/vendor/github.com/docker/docker/api/types/volume/volume_create.go b/vendor/github.com/docker/docker/api/types/volume/volume_create.go new file mode 100644 index 0000000000..8538078dd6 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/volume/volume_create.go @@ -0,0 +1,31 @@ +package volume // import "github.com/docker/docker/api/types/volume" + +// ---------------------------------------------------------------------------- +// Code generated by `swagger generate operation`. DO NOT EDIT. +// +// See hack/generate-swagger-api.sh +// ---------------------------------------------------------------------------- + +// VolumeCreateBody Volume configuration +// swagger:model VolumeCreateBody +type VolumeCreateBody struct { + + // Name of the volume driver to use. + // Required: true + Driver string `json:"Driver"` + + // A mapping of driver options and values. These options are + // passed directly to the driver and are driver specific. + // + // Required: true + DriverOpts map[string]string `json:"DriverOpts"` + + // User-defined key/value metadata. + // Required: true + Labels map[string]string `json:"Labels"` + + // The new volume's name. If not specified, Docker generates a name. + // + // Required: true + Name string `json:"Name"` +} diff --git a/vendor/github.com/docker/docker/api/types/volume/volume_list.go b/vendor/github.com/docker/docker/api/types/volume/volume_list.go new file mode 100644 index 0000000000..be06179bf4 --- /dev/null +++ b/vendor/github.com/docker/docker/api/types/volume/volume_list.go @@ -0,0 +1,23 @@ +package volume // import "github.com/docker/docker/api/types/volume" + +// ---------------------------------------------------------------------------- +// Code generated by `swagger generate operation`. DO NOT EDIT. +// +// See hack/generate-swagger-api.sh +// ---------------------------------------------------------------------------- + +import "github.com/docker/docker/api/types" + +// VolumeListOKBody Volume list response +// swagger:model VolumeListOKBody +type VolumeListOKBody struct { + + // List of volumes + // Required: true + Volumes []*types.Volume `json:"Volumes"` + + // Warnings that occurred when fetching the list of volumes. + // + // Required: true + Warnings []string `json:"Warnings"` +} diff --git a/vendor/github.com/docker/docker/cli/config/configdir.go b/vendor/github.com/docker/docker/cli/config/configdir.go new file mode 100644 index 0000000000..f0b68ee387 --- /dev/null +++ b/vendor/github.com/docker/docker/cli/config/configdir.go @@ -0,0 +1,27 @@ +package config // import "github.com/docker/docker/cli/config" + +import ( + "os" + "path/filepath" + + "github.com/docker/docker/pkg/homedir" +) + +var ( + configDir = os.Getenv("DOCKER_CONFIG") + configFileDir = ".docker" +) + +// Dir returns the path to the configuration directory as specified by the DOCKER_CONFIG environment variable. +// If DOCKER_CONFIG is unset, Dir returns ~/.docker . +// Dir ignores XDG_CONFIG_HOME (same as the docker client). +// TODO: this was copied from cli/config/configfile and should be removed once cmd/dockerd moves +func Dir() string { + return configDir +} + +func init() { + if configDir == "" { + configDir = filepath.Join(homedir.Get(), configFileDir) + } +} diff --git a/vendor/github.com/docker/docker/client/README.md b/vendor/github.com/docker/docker/client/README.md new file mode 100644 index 0000000000..992f18117d --- /dev/null +++ b/vendor/github.com/docker/docker/client/README.md @@ -0,0 +1,35 @@ +# Go client for the Docker Engine API + +The `docker` command uses this package to communicate with the daemon. It can also be used by your own Go applications to do anything the command-line interface does – running containers, pulling images, managing swarms, etc. + +For example, to list running containers (the equivalent of `docker ps`): + +```go +package main + +import ( + "context" + "fmt" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" +) + +func main() { + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + panic(err) + } + + containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{}) + if err != nil { + panic(err) + } + + for _, container := range containers { + fmt.Printf("%s %s\n", container.ID[:10], container.Image) + } +} +``` + +[Full documentation is available on GoDoc.](https://godoc.org/github.com/docker/docker/client) diff --git a/vendor/github.com/docker/docker/client/build_cancel.go b/vendor/github.com/docker/docker/client/build_cancel.go new file mode 100644 index 0000000000..3aae43e3d1 --- /dev/null +++ b/vendor/github.com/docker/docker/client/build_cancel.go @@ -0,0 +1,16 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" +) + +// BuildCancel requests the daemon to cancel ongoing build request +func (cli *Client) BuildCancel(ctx context.Context, id string) error { + query := url.Values{} + query.Set("id", id) + + serverResp, err := cli.post(ctx, "/build/cancel", query, nil, nil) + ensureReaderClosed(serverResp) + return err +} diff --git a/vendor/github.com/docker/docker/client/build_prune.go b/vendor/github.com/docker/docker/client/build_prune.go new file mode 100644 index 0000000000..397d67cdcf --- /dev/null +++ b/vendor/github.com/docker/docker/client/build_prune.go @@ -0,0 +1,45 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "fmt" + "net/url" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/pkg/errors" +) + +// BuildCachePrune requests the daemon to delete unused cache data +func (cli *Client) BuildCachePrune(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) { + if err := cli.NewVersionError("1.31", "build prune"); err != nil { + return nil, err + } + + report := types.BuildCachePruneReport{} + + query := url.Values{} + if opts.All { + query.Set("all", "1") + } + query.Set("keep-storage", fmt.Sprintf("%d", opts.KeepStorage)) + filters, err := filters.ToJSON(opts.Filters) + if err != nil { + return nil, errors.Wrap(err, "prune could not marshal filters option") + } + query.Set("filters", filters) + + serverResp, err := cli.post(ctx, "/build/prune", query, nil, nil) + defer ensureReaderClosed(serverResp) + + if err != nil { + return nil, err + } + + if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { + return nil, fmt.Errorf("Error retrieving disk usage: %v", err) + } + + return &report, nil +} diff --git a/vendor/github.com/docker/docker/client/checkpoint_create.go b/vendor/github.com/docker/docker/client/checkpoint_create.go new file mode 100644 index 0000000000..921024fe4f --- /dev/null +++ b/vendor/github.com/docker/docker/client/checkpoint_create.go @@ -0,0 +1,14 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + + "github.com/docker/docker/api/types" +) + +// CheckpointCreate creates a checkpoint from the given container with the given name +func (cli *Client) CheckpointCreate(ctx context.Context, container string, options types.CheckpointCreateOptions) error { + resp, err := cli.post(ctx, "/containers/"+container+"/checkpoints", nil, options, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/checkpoint_delete.go b/vendor/github.com/docker/docker/client/checkpoint_delete.go new file mode 100644 index 0000000000..54f55fa76e --- /dev/null +++ b/vendor/github.com/docker/docker/client/checkpoint_delete.go @@ -0,0 +1,20 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + + "github.com/docker/docker/api/types" +) + +// CheckpointDelete deletes the checkpoint with the given name from the given container +func (cli *Client) CheckpointDelete(ctx context.Context, containerID string, options types.CheckpointDeleteOptions) error { + query := url.Values{} + if options.CheckpointDir != "" { + query.Set("dir", options.CheckpointDir) + } + + resp, err := cli.delete(ctx, "/containers/"+containerID+"/checkpoints/"+options.CheckpointID, query, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/checkpoint_list.go b/vendor/github.com/docker/docker/client/checkpoint_list.go new file mode 100644 index 0000000000..66d46dd161 --- /dev/null +++ b/vendor/github.com/docker/docker/client/checkpoint_list.go @@ -0,0 +1,28 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/docker/docker/api/types" +) + +// CheckpointList returns the checkpoints of the given container in the docker host +func (cli *Client) CheckpointList(ctx context.Context, container string, options types.CheckpointListOptions) ([]types.Checkpoint, error) { + var checkpoints []types.Checkpoint + + query := url.Values{} + if options.CheckpointDir != "" { + query.Set("dir", options.CheckpointDir) + } + + resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", query, nil) + defer ensureReaderClosed(resp) + if err != nil { + return checkpoints, wrapResponseError(err, resp, "container", container) + } + + err = json.NewDecoder(resp.body).Decode(&checkpoints) + return checkpoints, err +} diff --git a/vendor/github.com/docker/docker/client/client.go b/vendor/github.com/docker/docker/client/client.go new file mode 100644 index 0000000000..21edf1fa1f --- /dev/null +++ b/vendor/github.com/docker/docker/client/client.go @@ -0,0 +1,310 @@ +/* +Package client is a Go client for the Docker Engine API. + +For more information about the Engine API, see the documentation: +https://docs.docker.com/engine/api/ + +Usage + +You use the library by creating a client object and calling methods on it. The +client can be created either from environment variables with NewClientWithOpts(client.FromEnv), +or configured manually with NewClient(). + +For example, to list running containers (the equivalent of "docker ps"): + + package main + + import ( + "context" + "fmt" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" + ) + + func main() { + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + panic(err) + } + + containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{}) + if err != nil { + panic(err) + } + + for _, container := range containers { + fmt.Printf("%s %s\n", container.ID[:10], container.Image) + } + } + +*/ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "fmt" + "net" + "net/http" + "net/url" + "path" + "strings" + + "github.com/docker/docker/api" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/versions" + "github.com/docker/go-connections/sockets" + "github.com/pkg/errors" +) + +// ErrRedirect is the error returned by checkRedirect when the request is non-GET. +var ErrRedirect = errors.New("unexpected redirect in response") + +// Client is the API client that performs all operations +// against a docker server. +type Client struct { + // scheme sets the scheme for the client + scheme string + // host holds the server address to connect to + host string + // proto holds the client protocol i.e. unix. + proto string + // addr holds the client address. + addr string + // basePath holds the path to prepend to the requests. + basePath string + // client used to send and receive http requests. + client *http.Client + // version of the server to talk to. + version string + // custom http headers configured by users. + customHTTPHeaders map[string]string + // manualOverride is set to true when the version was set by users. + manualOverride bool + + // negotiateVersion indicates if the client should automatically negotiate + // the API version to use when making requests. API version negotiation is + // performed on the first request, after which negotiated is set to "true" + // so that subsequent requests do not re-negotiate. + negotiateVersion bool + + // negotiated indicates that API version negotiation took place + negotiated bool +} + +// CheckRedirect specifies the policy for dealing with redirect responses: +// If the request is non-GET return `ErrRedirect`. Otherwise use the last response. +// +// Go 1.8 changes behavior for HTTP redirects (specifically 301, 307, and 308) in the client . +// The Docker client (and by extension docker API client) can be made to send a request +// like POST /containers//start where what would normally be in the name section of the URL is empty. +// This triggers an HTTP 301 from the daemon. +// In go 1.8 this 301 will be converted to a GET request, and ends up getting a 404 from the daemon. +// This behavior change manifests in the client in that before the 301 was not followed and +// the client did not generate an error, but now results in a message like Error response from daemon: page not found. +func CheckRedirect(req *http.Request, via []*http.Request) error { + if via[0].Method == http.MethodGet { + return http.ErrUseLastResponse + } + return ErrRedirect +} + +// NewClientWithOpts initializes a new API client with default values. It takes functors +// to modify values when creating it, like `NewClientWithOpts(WithVersion(…))` +// It also initializes the custom http headers to add to each request. +// +// It won't send any version information if the version number is empty. It is +// highly recommended that you set a version or your client may break if the +// server is upgraded. +func NewClientWithOpts(ops ...Opt) (*Client, error) { + client, err := defaultHTTPClient(DefaultDockerHost) + if err != nil { + return nil, err + } + c := &Client{ + host: DefaultDockerHost, + version: api.DefaultVersion, + client: client, + proto: defaultProto, + addr: defaultAddr, + } + + for _, op := range ops { + if err := op(c); err != nil { + return nil, err + } + } + + if _, ok := c.client.Transport.(http.RoundTripper); !ok { + return nil, fmt.Errorf("unable to verify TLS configuration, invalid transport %v", c.client.Transport) + } + if c.scheme == "" { + c.scheme = "http" + + tlsConfig := resolveTLSConfig(c.client.Transport) + if tlsConfig != nil { + // TODO(stevvooe): This isn't really the right way to write clients in Go. + // `NewClient` should probably only take an `*http.Client` and work from there. + // Unfortunately, the model of having a host-ish/url-thingy as the connection + // string has us confusing protocol and transport layers. We continue doing + // this to avoid breaking existing clients but this should be addressed. + c.scheme = "https" + } + } + + return c, nil +} + +func defaultHTTPClient(host string) (*http.Client, error) { + url, err := ParseHostURL(host) + if err != nil { + return nil, err + } + transport := new(http.Transport) + sockets.ConfigureTransport(transport, url.Scheme, url.Host) + return &http.Client{ + Transport: transport, + CheckRedirect: CheckRedirect, + }, nil +} + +// Close the transport used by the client +func (cli *Client) Close() error { + if t, ok := cli.client.Transport.(*http.Transport); ok { + t.CloseIdleConnections() + } + return nil +} + +// getAPIPath returns the versioned request path to call the api. +// It appends the query parameters to the path if they are not empty. +func (cli *Client) getAPIPath(ctx context.Context, p string, query url.Values) string { + var apiPath string + if cli.negotiateVersion && !cli.negotiated { + cli.NegotiateAPIVersion(ctx) + } + if cli.version != "" { + v := strings.TrimPrefix(cli.version, "v") + apiPath = path.Join(cli.basePath, "/v"+v, p) + } else { + apiPath = path.Join(cli.basePath, p) + } + return (&url.URL{Path: apiPath, RawQuery: query.Encode()}).String() +} + +// ClientVersion returns the API version used by this client. +func (cli *Client) ClientVersion() string { + return cli.version +} + +// NegotiateAPIVersion queries the API and updates the version to match the +// API version. Any errors are silently ignored. If a manual override is in place, +// either through the `DOCKER_API_VERSION` environment variable, or if the client +// was initialized with a fixed version (`opts.WithVersion(xx)`), no negotiation +// will be performed. +func (cli *Client) NegotiateAPIVersion(ctx context.Context) { + if !cli.manualOverride { + ping, _ := cli.Ping(ctx) + cli.negotiateAPIVersionPing(ping) + } +} + +// NegotiateAPIVersionPing updates the client version to match the Ping.APIVersion +// if the ping version is less than the default version. If a manual override is +// in place, either through the `DOCKER_API_VERSION` environment variable, or if +// the client was initialized with a fixed version (`opts.WithVersion(xx)`), no +// negotiation is performed. +func (cli *Client) NegotiateAPIVersionPing(p types.Ping) { + if !cli.manualOverride { + cli.negotiateAPIVersionPing(p) + } +} + +// negotiateAPIVersionPing queries the API and updates the version to match the +// API version. Any errors are silently ignored. +func (cli *Client) negotiateAPIVersionPing(p types.Ping) { + // try the latest version before versioning headers existed + if p.APIVersion == "" { + p.APIVersion = "1.24" + } + + // if the client is not initialized with a version, start with the latest supported version + if cli.version == "" { + cli.version = api.DefaultVersion + } + + // if server version is lower than the client version, downgrade + if versions.LessThan(p.APIVersion, cli.version) { + cli.version = p.APIVersion + } + + // Store the results, so that automatic API version negotiation (if enabled) + // won't be performed on the next request. + if cli.negotiateVersion { + cli.negotiated = true + } +} + +// DaemonHost returns the host address used by the client +func (cli *Client) DaemonHost() string { + return cli.host +} + +// HTTPClient returns a copy of the HTTP client bound to the server +func (cli *Client) HTTPClient() *http.Client { + c := *cli.client + return &c +} + +// ParseHostURL parses a url string, validates the string is a host url, and +// returns the parsed URL +func ParseHostURL(host string) (*url.URL, error) { + protoAddrParts := strings.SplitN(host, "://", 2) + if len(protoAddrParts) == 1 { + return nil, fmt.Errorf("unable to parse docker host `%s`", host) + } + + var basePath string + proto, addr := protoAddrParts[0], protoAddrParts[1] + if proto == "tcp" { + parsed, err := url.Parse("tcp://" + addr) + if err != nil { + return nil, err + } + addr = parsed.Host + basePath = parsed.Path + } + return &url.URL{ + Scheme: proto, + Host: addr, + Path: basePath, + }, nil +} + +// CustomHTTPHeaders returns the custom http headers stored by the client. +func (cli *Client) CustomHTTPHeaders() map[string]string { + m := make(map[string]string) + for k, v := range cli.customHTTPHeaders { + m[k] = v + } + return m +} + +// SetCustomHTTPHeaders that will be set on every HTTP request made by the client. +// Deprecated: use WithHTTPHeaders when creating the client. +func (cli *Client) SetCustomHTTPHeaders(headers map[string]string) { + cli.customHTTPHeaders = headers +} + +// Dialer returns a dialer for a raw stream connection, with HTTP/1.1 header, that can be used for proxying the daemon connection. +// Used by `docker dial-stdio` (docker/cli#889). +func (cli *Client) Dialer() func(context.Context) (net.Conn, error) { + return func(ctx context.Context) (net.Conn, error) { + if transport, ok := cli.client.Transport.(*http.Transport); ok { + if transport.DialContext != nil && transport.TLSClientConfig == nil { + return transport.DialContext(ctx, cli.proto, cli.addr) + } + } + return fallbackDial(cli.proto, cli.addr, resolveTLSConfig(cli.client.Transport)) + } +} diff --git a/vendor/github.com/docker/docker/client/client_deprecated.go b/vendor/github.com/docker/docker/client/client_deprecated.go new file mode 100644 index 0000000000..54cdfc29a8 --- /dev/null +++ b/vendor/github.com/docker/docker/client/client_deprecated.go @@ -0,0 +1,23 @@ +package client + +import "net/http" + +// NewClient initializes a new API client for the given host and API version. +// It uses the given http client as transport. +// It also initializes the custom http headers to add to each request. +// +// It won't send any version information if the version number is empty. It is +// highly recommended that you set a version or your client may break if the +// server is upgraded. +// Deprecated: use NewClientWithOpts +func NewClient(host string, version string, client *http.Client, httpHeaders map[string]string) (*Client, error) { + return NewClientWithOpts(WithHost(host), WithVersion(version), WithHTTPClient(client), WithHTTPHeaders(httpHeaders)) +} + +// NewEnvClient initializes a new API client based on environment variables. +// See FromEnv for a list of support environment variables. +// +// Deprecated: use NewClientWithOpts(FromEnv) +func NewEnvClient() (*Client, error) { + return NewClientWithOpts(FromEnv) +} diff --git a/vendor/github.com/docker/docker/client/client_unix.go b/vendor/github.com/docker/docker/client/client_unix.go new file mode 100644 index 0000000000..9d0f0dcbf0 --- /dev/null +++ b/vendor/github.com/docker/docker/client/client_unix.go @@ -0,0 +1,9 @@ +// +build linux freebsd openbsd netbsd darwin solaris illumos dragonfly + +package client // import "github.com/docker/docker/client" + +// DefaultDockerHost defines os specific default if DOCKER_HOST is unset +const DefaultDockerHost = "unix:///var/run/docker.sock" + +const defaultProto = "unix" +const defaultAddr = "/var/run/docker.sock" diff --git a/vendor/github.com/docker/docker/client/client_windows.go b/vendor/github.com/docker/docker/client/client_windows.go new file mode 100644 index 0000000000..c649e54412 --- /dev/null +++ b/vendor/github.com/docker/docker/client/client_windows.go @@ -0,0 +1,7 @@ +package client // import "github.com/docker/docker/client" + +// DefaultDockerHost defines os specific default if DOCKER_HOST is unset +const DefaultDockerHost = "npipe:////./pipe/docker_engine" + +const defaultProto = "npipe" +const defaultAddr = "//./pipe/docker_engine" diff --git a/vendor/github.com/docker/docker/client/config_create.go b/vendor/github.com/docker/docker/client/config_create.go new file mode 100644 index 0000000000..ee7d411df0 --- /dev/null +++ b/vendor/github.com/docker/docker/client/config_create.go @@ -0,0 +1,25 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/swarm" +) + +// ConfigCreate creates a new Config. +func (cli *Client) ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (types.ConfigCreateResponse, error) { + var response types.ConfigCreateResponse + if err := cli.NewVersionError("1.30", "config create"); err != nil { + return response, err + } + resp, err := cli.post(ctx, "/configs/create", nil, config, nil) + defer ensureReaderClosed(resp) + if err != nil { + return response, err + } + + err = json.NewDecoder(resp.body).Decode(&response) + return response, err +} diff --git a/vendor/github.com/docker/docker/client/config_inspect.go b/vendor/github.com/docker/docker/client/config_inspect.go new file mode 100644 index 0000000000..7d0ce3e11c --- /dev/null +++ b/vendor/github.com/docker/docker/client/config_inspect.go @@ -0,0 +1,36 @@ +package client // import "github.com/docker/docker/client" + +import ( + "bytes" + "context" + "encoding/json" + "io/ioutil" + + "github.com/docker/docker/api/types/swarm" +) + +// ConfigInspectWithRaw returns the config information with raw data +func (cli *Client) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.Config, []byte, error) { + if id == "" { + return swarm.Config{}, nil, objectNotFoundError{object: "config", id: id} + } + if err := cli.NewVersionError("1.30", "config inspect"); err != nil { + return swarm.Config{}, nil, err + } + resp, err := cli.get(ctx, "/configs/"+id, nil, nil) + defer ensureReaderClosed(resp) + if err != nil { + return swarm.Config{}, nil, wrapResponseError(err, resp, "config", id) + } + + body, err := ioutil.ReadAll(resp.body) + if err != nil { + return swarm.Config{}, nil, err + } + + var config swarm.Config + rdr := bytes.NewReader(body) + err = json.NewDecoder(rdr).Decode(&config) + + return config, body, err +} diff --git a/vendor/github.com/docker/docker/client/config_list.go b/vendor/github.com/docker/docker/client/config_list.go new file mode 100644 index 0000000000..565acc6e27 --- /dev/null +++ b/vendor/github.com/docker/docker/client/config_list.go @@ -0,0 +1,38 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/swarm" +) + +// ConfigList returns the list of configs. +func (cli *Client) ConfigList(ctx context.Context, options types.ConfigListOptions) ([]swarm.Config, error) { + if err := cli.NewVersionError("1.30", "config list"); err != nil { + return nil, err + } + query := url.Values{} + + if options.Filters.Len() > 0 { + filterJSON, err := filters.ToJSON(options.Filters) + if err != nil { + return nil, err + } + + query.Set("filters", filterJSON) + } + + resp, err := cli.get(ctx, "/configs", query, nil) + defer ensureReaderClosed(resp) + if err != nil { + return nil, err + } + + var configs []swarm.Config + err = json.NewDecoder(resp.body).Decode(&configs) + return configs, err +} diff --git a/vendor/github.com/docker/docker/client/config_remove.go b/vendor/github.com/docker/docker/client/config_remove.go new file mode 100644 index 0000000000..a708fcaecf --- /dev/null +++ b/vendor/github.com/docker/docker/client/config_remove.go @@ -0,0 +1,13 @@ +package client // import "github.com/docker/docker/client" + +import "context" + +// ConfigRemove removes a Config. +func (cli *Client) ConfigRemove(ctx context.Context, id string) error { + if err := cli.NewVersionError("1.30", "config remove"); err != nil { + return err + } + resp, err := cli.delete(ctx, "/configs/"+id, nil, nil) + defer ensureReaderClosed(resp) + return wrapResponseError(err, resp, "config", id) +} diff --git a/vendor/github.com/docker/docker/client/config_update.go b/vendor/github.com/docker/docker/client/config_update.go new file mode 100644 index 0000000000..39e59cf858 --- /dev/null +++ b/vendor/github.com/docker/docker/client/config_update.go @@ -0,0 +1,21 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + "strconv" + + "github.com/docker/docker/api/types/swarm" +) + +// ConfigUpdate attempts to update a Config +func (cli *Client) ConfigUpdate(ctx context.Context, id string, version swarm.Version, config swarm.ConfigSpec) error { + if err := cli.NewVersionError("1.30", "config update"); err != nil { + return err + } + query := url.Values{} + query.Set("version", strconv.FormatUint(version.Index, 10)) + resp, err := cli.post(ctx, "/configs/"+id+"/update", query, config, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/container_attach.go b/vendor/github.com/docker/docker/client/container_attach.go new file mode 100644 index 0000000000..88ba1ef639 --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_attach.go @@ -0,0 +1,57 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + + "github.com/docker/docker/api/types" +) + +// ContainerAttach attaches a connection to a container in the server. +// It returns a types.HijackedConnection with the hijacked connection +// and the a reader to get output. It's up to the called to close +// the hijacked connection by calling types.HijackedResponse.Close. +// +// The stream format on the response will be in one of two formats: +// +// If the container is using a TTY, there is only a single stream (stdout), and +// data is copied directly from the container output stream, no extra +// multiplexing or headers. +// +// If the container is *not* using a TTY, streams for stdout and stderr are +// multiplexed. +// The format of the multiplexed stream is as follows: +// +// [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}[]byte{OUTPUT} +// +// STREAM_TYPE can be 1 for stdout and 2 for stderr +// +// SIZE1, SIZE2, SIZE3, and SIZE4 are four bytes of uint32 encoded as big endian. +// This is the size of OUTPUT. +// +// You can use github.com/docker/docker/pkg/stdcopy.StdCopy to demultiplex this +// stream. +func (cli *Client) ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error) { + query := url.Values{} + if options.Stream { + query.Set("stream", "1") + } + if options.Stdin { + query.Set("stdin", "1") + } + if options.Stdout { + query.Set("stdout", "1") + } + if options.Stderr { + query.Set("stderr", "1") + } + if options.DetachKeys != "" { + query.Set("detachKeys", options.DetachKeys) + } + if options.Logs { + query.Set("logs", "1") + } + + headers := map[string][]string{"Content-Type": {"text/plain"}} + return cli.postHijacked(ctx, "/containers/"+container+"/attach", query, nil, headers) +} diff --git a/vendor/github.com/docker/docker/client/container_commit.go b/vendor/github.com/docker/docker/client/container_commit.go new file mode 100644 index 0000000000..2966e88c8e --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_commit.go @@ -0,0 +1,55 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "errors" + "net/url" + + "github.com/docker/distribution/reference" + "github.com/docker/docker/api/types" +) + +// ContainerCommit applies changes into a container and creates a new tagged image. +func (cli *Client) ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error) { + var repository, tag string + if options.Reference != "" { + ref, err := reference.ParseNormalizedNamed(options.Reference) + if err != nil { + return types.IDResponse{}, err + } + + if _, isCanonical := ref.(reference.Canonical); isCanonical { + return types.IDResponse{}, errors.New("refusing to create a tag with a digest reference") + } + ref = reference.TagNameOnly(ref) + + if tagged, ok := ref.(reference.Tagged); ok { + tag = tagged.Tag() + } + repository = reference.FamiliarName(ref) + } + + query := url.Values{} + query.Set("container", container) + query.Set("repo", repository) + query.Set("tag", tag) + query.Set("comment", options.Comment) + query.Set("author", options.Author) + for _, change := range options.Changes { + query.Add("changes", change) + } + if !options.Pause { + query.Set("pause", "0") + } + + var response types.IDResponse + resp, err := cli.post(ctx, "/commit", query, options.Config, nil) + defer ensureReaderClosed(resp) + if err != nil { + return response, err + } + + err = json.NewDecoder(resp.body).Decode(&response) + return response, err +} diff --git a/vendor/github.com/docker/docker/client/container_copy.go b/vendor/github.com/docker/docker/client/container_copy.go new file mode 100644 index 0000000000..bb278bf7f3 --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_copy.go @@ -0,0 +1,103 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "path/filepath" + "strings" + + "github.com/docker/docker/api/types" +) + +// ContainerStatPath returns Stat information about a path inside the container filesystem. +func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path string) (types.ContainerPathStat, error) { + query := url.Values{} + query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API. + + urlStr := "/containers/" + containerID + "/archive" + response, err := cli.head(ctx, urlStr, query, nil) + defer ensureReaderClosed(response) + if err != nil { + return types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+path) + } + return getContainerPathStatFromHeader(response.header) +} + +// CopyToContainer copies content into the container filesystem. +// Note that `content` must be a Reader for a TAR archive +func (cli *Client) CopyToContainer(ctx context.Context, containerID, dstPath string, content io.Reader, options types.CopyToContainerOptions) error { + query := url.Values{} + query.Set("path", filepath.ToSlash(dstPath)) // Normalize the paths used in the API. + // Do not allow for an existing directory to be overwritten by a non-directory and vice versa. + if !options.AllowOverwriteDirWithFile { + query.Set("noOverwriteDirNonDir", "true") + } + + if options.CopyUIDGID { + query.Set("copyUIDGID", "true") + } + + apiPath := "/containers/" + containerID + "/archive" + + response, err := cli.putRaw(ctx, apiPath, query, content, nil) + defer ensureReaderClosed(response) + if err != nil { + return wrapResponseError(err, response, "container:path", containerID+":"+dstPath) + } + + // TODO this code converts non-error status-codes (e.g., "204 No Content") into an error; verify if this is the desired behavior + if response.statusCode != http.StatusOK { + return fmt.Errorf("unexpected status code from daemon: %d", response.statusCode) + } + + return nil +} + +// CopyFromContainer gets the content from the container and returns it as a Reader +// for a TAR archive to manipulate it in the host. It's up to the caller to close the reader. +func (cli *Client) CopyFromContainer(ctx context.Context, containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) { + query := make(url.Values, 1) + query.Set("path", filepath.ToSlash(srcPath)) // Normalize the paths used in the API. + + apiPath := "/containers/" + containerID + "/archive" + response, err := cli.get(ctx, apiPath, query, nil) + if err != nil { + return nil, types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+srcPath) + } + + // TODO this code converts non-error status-codes (e.g., "204 No Content") into an error; verify if this is the desired behavior + if response.statusCode != http.StatusOK { + return nil, types.ContainerPathStat{}, fmt.Errorf("unexpected status code from daemon: %d", response.statusCode) + } + + // In order to get the copy behavior right, we need to know information + // about both the source and the destination. The response headers include + // stat info about the source that we can use in deciding exactly how to + // copy it locally. Along with the stat info about the local destination, + // we have everything we need to handle the multiple possibilities there + // can be when copying a file/dir from one location to another file/dir. + stat, err := getContainerPathStatFromHeader(response.header) + if err != nil { + return nil, stat, fmt.Errorf("unable to get resource stat from response: %s", err) + } + return response.body, stat, err +} + +func getContainerPathStatFromHeader(header http.Header) (types.ContainerPathStat, error) { + var stat types.ContainerPathStat + + encodedStat := header.Get("X-Docker-Container-Path-Stat") + statDecoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(encodedStat)) + + err := json.NewDecoder(statDecoder).Decode(&stat) + if err != nil { + err = fmt.Errorf("unable to decode container path stat header: %s", err) + } + + return stat, err +} diff --git a/vendor/github.com/docker/docker/client/container_create.go b/vendor/github.com/docker/docker/client/container_create.go new file mode 100644 index 0000000000..b1d5fea5bd --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_create.go @@ -0,0 +1,63 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/containerd/containerd/platforms" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/network" + "github.com/docker/docker/api/types/versions" + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +type configWrapper struct { + *container.Config + HostConfig *container.HostConfig + NetworkingConfig *network.NetworkingConfig + Platform *specs.Platform +} + +// ContainerCreate creates a new container based in the given configuration. +// It can be associated with a name, but it's not mandatory. +func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (container.ContainerCreateCreatedBody, error) { + var response container.ContainerCreateCreatedBody + + if err := cli.NewVersionError("1.25", "stop timeout"); config != nil && config.StopTimeout != nil && err != nil { + return response, err + } + + // When using API 1.24 and under, the client is responsible for removing the container + if hostConfig != nil && versions.LessThan(cli.ClientVersion(), "1.25") { + hostConfig.AutoRemove = false + } + + if err := cli.NewVersionError("1.41", "specify container image platform"); platform != nil && err != nil { + return response, err + } + + query := url.Values{} + if platform != nil { + query.Set("platform", platforms.Format(*platform)) + } + + if containerName != "" { + query.Set("name", containerName) + } + + body := configWrapper{ + Config: config, + HostConfig: hostConfig, + NetworkingConfig: networkingConfig, + } + + serverResp, err := cli.post(ctx, "/containers/create", query, body, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return response, err + } + + err = json.NewDecoder(serverResp.body).Decode(&response) + return response, err +} diff --git a/vendor/github.com/docker/docker/client/container_diff.go b/vendor/github.com/docker/docker/client/container_diff.go new file mode 100644 index 0000000000..29dac8491d --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_diff.go @@ -0,0 +1,23 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/docker/docker/api/types/container" +) + +// ContainerDiff shows differences in a container filesystem since it was started. +func (cli *Client) ContainerDiff(ctx context.Context, containerID string) ([]container.ContainerChangeResponseItem, error) { + var changes []container.ContainerChangeResponseItem + + serverResp, err := cli.get(ctx, "/containers/"+containerID+"/changes", url.Values{}, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return changes, err + } + + err = json.NewDecoder(serverResp.body).Decode(&changes) + return changes, err +} diff --git a/vendor/github.com/docker/docker/client/container_exec.go b/vendor/github.com/docker/docker/client/container_exec.go new file mode 100644 index 0000000000..e3ee755b71 --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_exec.go @@ -0,0 +1,54 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + + "github.com/docker/docker/api/types" +) + +// ContainerExecCreate creates a new exec configuration to run an exec process. +func (cli *Client) ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.IDResponse, error) { + var response types.IDResponse + + if err := cli.NewVersionError("1.25", "env"); len(config.Env) != 0 && err != nil { + return response, err + } + + resp, err := cli.post(ctx, "/containers/"+container+"/exec", nil, config, nil) + defer ensureReaderClosed(resp) + if err != nil { + return response, err + } + err = json.NewDecoder(resp.body).Decode(&response) + return response, err +} + +// ContainerExecStart starts an exec process already created in the docker host. +func (cli *Client) ContainerExecStart(ctx context.Context, execID string, config types.ExecStartCheck) error { + resp, err := cli.post(ctx, "/exec/"+execID+"/start", nil, config, nil) + ensureReaderClosed(resp) + return err +} + +// ContainerExecAttach attaches a connection to an exec process in the server. +// It returns a types.HijackedConnection with the hijacked connection +// and the a reader to get output. It's up to the called to close +// the hijacked connection by calling types.HijackedResponse.Close. +func (cli *Client) ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error) { + headers := map[string][]string{"Content-Type": {"application/json"}} + return cli.postHijacked(ctx, "/exec/"+execID+"/start", nil, config, headers) +} + +// ContainerExecInspect returns information about a specific exec process on the docker host. +func (cli *Client) ContainerExecInspect(ctx context.Context, execID string) (types.ContainerExecInspect, error) { + var response types.ContainerExecInspect + resp, err := cli.get(ctx, "/exec/"+execID+"/json", nil, nil) + if err != nil { + return response, err + } + + err = json.NewDecoder(resp.body).Decode(&response) + ensureReaderClosed(resp) + return response, err +} diff --git a/vendor/github.com/docker/docker/client/container_export.go b/vendor/github.com/docker/docker/client/container_export.go new file mode 100644 index 0000000000..d0c0a5cbad --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_export.go @@ -0,0 +1,19 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "io" + "net/url" +) + +// ContainerExport retrieves the raw contents of a container +// and returns them as an io.ReadCloser. It's up to the caller +// to close the stream. +func (cli *Client) ContainerExport(ctx context.Context, containerID string) (io.ReadCloser, error) { + serverResp, err := cli.get(ctx, "/containers/"+containerID+"/export", url.Values{}, nil) + if err != nil { + return nil, err + } + + return serverResp.body, nil +} diff --git a/vendor/github.com/docker/docker/client/container_inspect.go b/vendor/github.com/docker/docker/client/container_inspect.go new file mode 100644 index 0000000000..c496bcffea --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_inspect.go @@ -0,0 +1,53 @@ +package client // import "github.com/docker/docker/client" + +import ( + "bytes" + "context" + "encoding/json" + "io/ioutil" + "net/url" + + "github.com/docker/docker/api/types" +) + +// ContainerInspect returns the container information. +func (cli *Client) ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error) { + if containerID == "" { + return types.ContainerJSON{}, objectNotFoundError{object: "container", id: containerID} + } + serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return types.ContainerJSON{}, wrapResponseError(err, serverResp, "container", containerID) + } + + var response types.ContainerJSON + err = json.NewDecoder(serverResp.body).Decode(&response) + return response, err +} + +// ContainerInspectWithRaw returns the container information and its raw representation. +func (cli *Client) ContainerInspectWithRaw(ctx context.Context, containerID string, getSize bool) (types.ContainerJSON, []byte, error) { + if containerID == "" { + return types.ContainerJSON{}, nil, objectNotFoundError{object: "container", id: containerID} + } + query := url.Values{} + if getSize { + query.Set("size", "1") + } + serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", query, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return types.ContainerJSON{}, nil, wrapResponseError(err, serverResp, "container", containerID) + } + + body, err := ioutil.ReadAll(serverResp.body) + if err != nil { + return types.ContainerJSON{}, nil, err + } + + var response types.ContainerJSON + rdr := bytes.NewReader(body) + err = json.NewDecoder(rdr).Decode(&response) + return response, body, err +} diff --git a/vendor/github.com/docker/docker/client/container_kill.go b/vendor/github.com/docker/docker/client/container_kill.go new file mode 100644 index 0000000000..4d6f1d23da --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_kill.go @@ -0,0 +1,16 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" +) + +// ContainerKill terminates the container process but does not remove the container from the docker host. +func (cli *Client) ContainerKill(ctx context.Context, containerID, signal string) error { + query := url.Values{} + query.Set("signal", signal) + + resp, err := cli.post(ctx, "/containers/"+containerID+"/kill", query, nil, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/container_list.go b/vendor/github.com/docker/docker/client/container_list.go new file mode 100644 index 0000000000..a973de597f --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_list.go @@ -0,0 +1,57 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + "strconv" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" +) + +// ContainerList returns the list of containers in the docker host. +func (cli *Client) ContainerList(ctx context.Context, options types.ContainerListOptions) ([]types.Container, error) { + query := url.Values{} + + if options.All { + query.Set("all", "1") + } + + if options.Limit != -1 { + query.Set("limit", strconv.Itoa(options.Limit)) + } + + if options.Since != "" { + query.Set("since", options.Since) + } + + if options.Before != "" { + query.Set("before", options.Before) + } + + if options.Size { + query.Set("size", "1") + } + + if options.Filters.Len() > 0 { + //nolint:staticcheck // ignore SA1019 for old code + filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters) + + if err != nil { + return nil, err + } + + query.Set("filters", filterJSON) + } + + resp, err := cli.get(ctx, "/containers/json", query, nil) + defer ensureReaderClosed(resp) + if err != nil { + return nil, err + } + + var containers []types.Container + err = json.NewDecoder(resp.body).Decode(&containers) + return containers, err +} diff --git a/vendor/github.com/docker/docker/client/container_logs.go b/vendor/github.com/docker/docker/client/container_logs.go new file mode 100644 index 0000000000..5b6541f035 --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_logs.go @@ -0,0 +1,80 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "io" + "net/url" + "time" + + "github.com/docker/docker/api/types" + timetypes "github.com/docker/docker/api/types/time" + "github.com/pkg/errors" +) + +// ContainerLogs returns the logs generated by a container in an io.ReadCloser. +// It's up to the caller to close the stream. +// +// The stream format on the response will be in one of two formats: +// +// If the container is using a TTY, there is only a single stream (stdout), and +// data is copied directly from the container output stream, no extra +// multiplexing or headers. +// +// If the container is *not* using a TTY, streams for stdout and stderr are +// multiplexed. +// The format of the multiplexed stream is as follows: +// +// [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}[]byte{OUTPUT} +// +// STREAM_TYPE can be 1 for stdout and 2 for stderr +// +// SIZE1, SIZE2, SIZE3, and SIZE4 are four bytes of uint32 encoded as big endian. +// This is the size of OUTPUT. +// +// You can use github.com/docker/docker/pkg/stdcopy.StdCopy to demultiplex this +// stream. +func (cli *Client) ContainerLogs(ctx context.Context, container string, options types.ContainerLogsOptions) (io.ReadCloser, error) { + query := url.Values{} + if options.ShowStdout { + query.Set("stdout", "1") + } + + if options.ShowStderr { + query.Set("stderr", "1") + } + + if options.Since != "" { + ts, err := timetypes.GetTimestamp(options.Since, time.Now()) + if err != nil { + return nil, errors.Wrap(err, `invalid value for "since"`) + } + query.Set("since", ts) + } + + if options.Until != "" { + ts, err := timetypes.GetTimestamp(options.Until, time.Now()) + if err != nil { + return nil, errors.Wrap(err, `invalid value for "until"`) + } + query.Set("until", ts) + } + + if options.Timestamps { + query.Set("timestamps", "1") + } + + if options.Details { + query.Set("details", "1") + } + + if options.Follow { + query.Set("follow", "1") + } + query.Set("tail", options.Tail) + + resp, err := cli.get(ctx, "/containers/"+container+"/logs", query, nil) + if err != nil { + return nil, wrapResponseError(err, resp, "container", container) + } + return resp.body, nil +} diff --git a/vendor/github.com/docker/docker/client/container_pause.go b/vendor/github.com/docker/docker/client/container_pause.go new file mode 100644 index 0000000000..5e7271a371 --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_pause.go @@ -0,0 +1,10 @@ +package client // import "github.com/docker/docker/client" + +import "context" + +// ContainerPause pauses the main process of a given container without terminating it. +func (cli *Client) ContainerPause(ctx context.Context, containerID string) error { + resp, err := cli.post(ctx, "/containers/"+containerID+"/pause", nil, nil, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/container_prune.go b/vendor/github.com/docker/docker/client/container_prune.go new file mode 100644 index 0000000000..04383deaaf --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_prune.go @@ -0,0 +1,36 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" +) + +// ContainersPrune requests the daemon to delete unused data +func (cli *Client) ContainersPrune(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error) { + var report types.ContainersPruneReport + + if err := cli.NewVersionError("1.25", "container prune"); err != nil { + return report, err + } + + query, err := getFiltersQuery(pruneFilters) + if err != nil { + return report, err + } + + serverResp, err := cli.post(ctx, "/containers/prune", query, nil, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return report, err + } + + if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { + return report, fmt.Errorf("Error retrieving disk usage: %v", err) + } + + return report, nil +} diff --git a/vendor/github.com/docker/docker/client/container_remove.go b/vendor/github.com/docker/docker/client/container_remove.go new file mode 100644 index 0000000000..df81461b88 --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_remove.go @@ -0,0 +1,27 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + + "github.com/docker/docker/api/types" +) + +// ContainerRemove kills and removes a container from the docker host. +func (cli *Client) ContainerRemove(ctx context.Context, containerID string, options types.ContainerRemoveOptions) error { + query := url.Values{} + if options.RemoveVolumes { + query.Set("v", "1") + } + if options.RemoveLinks { + query.Set("link", "1") + } + + if options.Force { + query.Set("force", "1") + } + + resp, err := cli.delete(ctx, "/containers/"+containerID, query, nil) + defer ensureReaderClosed(resp) + return wrapResponseError(err, resp, "container", containerID) +} diff --git a/vendor/github.com/docker/docker/client/container_rename.go b/vendor/github.com/docker/docker/client/container_rename.go new file mode 100644 index 0000000000..240fdf552b --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_rename.go @@ -0,0 +1,15 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" +) + +// ContainerRename changes the name of a given container. +func (cli *Client) ContainerRename(ctx context.Context, containerID, newContainerName string) error { + query := url.Values{} + query.Set("name", newContainerName) + resp, err := cli.post(ctx, "/containers/"+containerID+"/rename", query, nil, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/container_resize.go b/vendor/github.com/docker/docker/client/container_resize.go new file mode 100644 index 0000000000..a9d4c0c79a --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_resize.go @@ -0,0 +1,29 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + "strconv" + + "github.com/docker/docker/api/types" +) + +// ContainerResize changes the size of the tty for a container. +func (cli *Client) ContainerResize(ctx context.Context, containerID string, options types.ResizeOptions) error { + return cli.resize(ctx, "/containers/"+containerID, options.Height, options.Width) +} + +// ContainerExecResize changes the size of the tty for an exec process running inside a container. +func (cli *Client) ContainerExecResize(ctx context.Context, execID string, options types.ResizeOptions) error { + return cli.resize(ctx, "/exec/"+execID, options.Height, options.Width) +} + +func (cli *Client) resize(ctx context.Context, basePath string, height, width uint) error { + query := url.Values{} + query.Set("h", strconv.Itoa(int(height))) + query.Set("w", strconv.Itoa(int(width))) + + resp, err := cli.post(ctx, basePath+"/resize", query, nil, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/container_restart.go b/vendor/github.com/docker/docker/client/container_restart.go new file mode 100644 index 0000000000..41e421969f --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_restart.go @@ -0,0 +1,22 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + "time" + + timetypes "github.com/docker/docker/api/types/time" +) + +// ContainerRestart stops and starts a container again. +// It makes the daemon to wait for the container to be up again for +// a specific amount of time, given the timeout. +func (cli *Client) ContainerRestart(ctx context.Context, containerID string, timeout *time.Duration) error { + query := url.Values{} + if timeout != nil { + query.Set("t", timetypes.DurationToSecondsString(*timeout)) + } + resp, err := cli.post(ctx, "/containers/"+containerID+"/restart", query, nil, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/container_start.go b/vendor/github.com/docker/docker/client/container_start.go new file mode 100644 index 0000000000..c2e0b15dca --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_start.go @@ -0,0 +1,23 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + + "github.com/docker/docker/api/types" +) + +// ContainerStart sends a request to the docker daemon to start a container. +func (cli *Client) ContainerStart(ctx context.Context, containerID string, options types.ContainerStartOptions) error { + query := url.Values{} + if len(options.CheckpointID) != 0 { + query.Set("checkpoint", options.CheckpointID) + } + if len(options.CheckpointDir) != 0 { + query.Set("checkpoint-dir", options.CheckpointDir) + } + + resp, err := cli.post(ctx, "/containers/"+containerID+"/start", query, nil, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/container_stats.go b/vendor/github.com/docker/docker/client/container_stats.go new file mode 100644 index 0000000000..0a6488dde8 --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_stats.go @@ -0,0 +1,42 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + + "github.com/docker/docker/api/types" +) + +// ContainerStats returns near realtime stats for a given container. +// It's up to the caller to close the io.ReadCloser returned. +func (cli *Client) ContainerStats(ctx context.Context, containerID string, stream bool) (types.ContainerStats, error) { + query := url.Values{} + query.Set("stream", "0") + if stream { + query.Set("stream", "1") + } + + resp, err := cli.get(ctx, "/containers/"+containerID+"/stats", query, nil) + if err != nil { + return types.ContainerStats{}, err + } + + osType := getDockerOS(resp.header.Get("Server")) + return types.ContainerStats{Body: resp.body, OSType: osType}, err +} + +// ContainerStatsOneShot gets a single stat entry from a container. +// It differs from `ContainerStats` in that the API should not wait to prime the stats +func (cli *Client) ContainerStatsOneShot(ctx context.Context, containerID string) (types.ContainerStats, error) { + query := url.Values{} + query.Set("stream", "0") + query.Set("one-shot", "1") + + resp, err := cli.get(ctx, "/containers/"+containerID+"/stats", query, nil) + if err != nil { + return types.ContainerStats{}, err + } + + osType := getDockerOS(resp.header.Get("Server")) + return types.ContainerStats{Body: resp.body, OSType: osType}, err +} diff --git a/vendor/github.com/docker/docker/client/container_stop.go b/vendor/github.com/docker/docker/client/container_stop.go new file mode 100644 index 0000000000..629d7ab64c --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_stop.go @@ -0,0 +1,26 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + "time" + + timetypes "github.com/docker/docker/api/types/time" +) + +// ContainerStop stops a container. In case the container fails to stop +// gracefully within a time frame specified by the timeout argument, +// it is forcefully terminated (killed). +// +// If the timeout is nil, the container's StopTimeout value is used, if set, +// otherwise the engine default. A negative timeout value can be specified, +// meaning no timeout, i.e. no forceful termination is performed. +func (cli *Client) ContainerStop(ctx context.Context, containerID string, timeout *time.Duration) error { + query := url.Values{} + if timeout != nil { + query.Set("t", timetypes.DurationToSecondsString(*timeout)) + } + resp, err := cli.post(ctx, "/containers/"+containerID+"/stop", query, nil, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/container_top.go b/vendor/github.com/docker/docker/client/container_top.go new file mode 100644 index 0000000000..a5b78999bf --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_top.go @@ -0,0 +1,28 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + "strings" + + "github.com/docker/docker/api/types/container" +) + +// ContainerTop shows process information from within a container. +func (cli *Client) ContainerTop(ctx context.Context, containerID string, arguments []string) (container.ContainerTopOKBody, error) { + var response container.ContainerTopOKBody + query := url.Values{} + if len(arguments) > 0 { + query.Set("ps_args", strings.Join(arguments, " ")) + } + + resp, err := cli.get(ctx, "/containers/"+containerID+"/top", query, nil) + defer ensureReaderClosed(resp) + if err != nil { + return response, err + } + + err = json.NewDecoder(resp.body).Decode(&response) + return response, err +} diff --git a/vendor/github.com/docker/docker/client/container_unpause.go b/vendor/github.com/docker/docker/client/container_unpause.go new file mode 100644 index 0000000000..1d8f873169 --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_unpause.go @@ -0,0 +1,10 @@ +package client // import "github.com/docker/docker/client" + +import "context" + +// ContainerUnpause resumes the process execution within a container +func (cli *Client) ContainerUnpause(ctx context.Context, containerID string) error { + resp, err := cli.post(ctx, "/containers/"+containerID+"/unpause", nil, nil, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/container_update.go b/vendor/github.com/docker/docker/client/container_update.go new file mode 100644 index 0000000000..6917cf9fb3 --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_update.go @@ -0,0 +1,21 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + + "github.com/docker/docker/api/types/container" +) + +// ContainerUpdate updates resources of a container +func (cli *Client) ContainerUpdate(ctx context.Context, containerID string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error) { + var response container.ContainerUpdateOKBody + serverResp, err := cli.post(ctx, "/containers/"+containerID+"/update", nil, updateConfig, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return response, err + } + + err = json.NewDecoder(serverResp.body).Decode(&response) + return response, err +} diff --git a/vendor/github.com/docker/docker/client/container_wait.go b/vendor/github.com/docker/docker/client/container_wait.go new file mode 100644 index 0000000000..6ab8c1da96 --- /dev/null +++ b/vendor/github.com/docker/docker/client/container_wait.go @@ -0,0 +1,83 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/versions" +) + +// ContainerWait waits until the specified container is in a certain state +// indicated by the given condition, either "not-running" (default), +// "next-exit", or "removed". +// +// If this client's API version is before 1.30, condition is ignored and +// ContainerWait will return immediately with the two channels, as the server +// will wait as if the condition were "not-running". +// +// If this client's API version is at least 1.30, ContainerWait blocks until +// the request has been acknowledged by the server (with a response header), +// then returns two channels on which the caller can wait for the exit status +// of the container or an error if there was a problem either beginning the +// wait request or in getting the response. This allows the caller to +// synchronize ContainerWait with other calls, such as specifying a +// "next-exit" condition before issuing a ContainerStart request. +func (cli *Client) ContainerWait(ctx context.Context, containerID string, condition container.WaitCondition) (<-chan container.ContainerWaitOKBody, <-chan error) { + if versions.LessThan(cli.ClientVersion(), "1.30") { + return cli.legacyContainerWait(ctx, containerID) + } + + resultC := make(chan container.ContainerWaitOKBody) + errC := make(chan error, 1) + + query := url.Values{} + query.Set("condition", string(condition)) + + resp, err := cli.post(ctx, "/containers/"+containerID+"/wait", query, nil, nil) + if err != nil { + defer ensureReaderClosed(resp) + errC <- err + return resultC, errC + } + + go func() { + defer ensureReaderClosed(resp) + var res container.ContainerWaitOKBody + if err := json.NewDecoder(resp.body).Decode(&res); err != nil { + errC <- err + return + } + + resultC <- res + }() + + return resultC, errC +} + +// legacyContainerWait returns immediately and doesn't have an option to wait +// until the container is removed. +func (cli *Client) legacyContainerWait(ctx context.Context, containerID string) (<-chan container.ContainerWaitOKBody, <-chan error) { + resultC := make(chan container.ContainerWaitOKBody) + errC := make(chan error) + + go func() { + resp, err := cli.post(ctx, "/containers/"+containerID+"/wait", nil, nil, nil) + if err != nil { + errC <- err + return + } + defer ensureReaderClosed(resp) + + var res container.ContainerWaitOKBody + if err := json.NewDecoder(resp.body).Decode(&res); err != nil { + errC <- err + return + } + + resultC <- res + }() + + return resultC, errC +} diff --git a/vendor/github.com/docker/docker/client/disk_usage.go b/vendor/github.com/docker/docker/client/disk_usage.go new file mode 100644 index 0000000000..354cd36939 --- /dev/null +++ b/vendor/github.com/docker/docker/client/disk_usage.go @@ -0,0 +1,26 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/docker/docker/api/types" +) + +// DiskUsage requests the current data usage from the daemon +func (cli *Client) DiskUsage(ctx context.Context) (types.DiskUsage, error) { + var du types.DiskUsage + + serverResp, err := cli.get(ctx, "/system/df", nil, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return du, err + } + + if err := json.NewDecoder(serverResp.body).Decode(&du); err != nil { + return du, fmt.Errorf("Error retrieving disk usage: %v", err) + } + + return du, nil +} diff --git a/vendor/github.com/docker/docker/client/distribution_inspect.go b/vendor/github.com/docker/docker/client/distribution_inspect.go new file mode 100644 index 0000000000..f4e3794cb4 --- /dev/null +++ b/vendor/github.com/docker/docker/client/distribution_inspect.go @@ -0,0 +1,38 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + registrytypes "github.com/docker/docker/api/types/registry" +) + +// DistributionInspect returns the image digest with full Manifest +func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegistryAuth string) (registrytypes.DistributionInspect, error) { + // Contact the registry to retrieve digest and platform information + var distributionInspect registrytypes.DistributionInspect + if image == "" { + return distributionInspect, objectNotFoundError{object: "distribution", id: image} + } + + if err := cli.NewVersionError("1.30", "distribution inspect"); err != nil { + return distributionInspect, err + } + var headers map[string][]string + + if encodedRegistryAuth != "" { + headers = map[string][]string{ + "X-Registry-Auth": {encodedRegistryAuth}, + } + } + + resp, err := cli.get(ctx, "/distribution/"+image+"/json", url.Values{}, headers) + defer ensureReaderClosed(resp) + if err != nil { + return distributionInspect, err + } + + err = json.NewDecoder(resp.body).Decode(&distributionInspect) + return distributionInspect, err +} diff --git a/vendor/github.com/docker/docker/client/errors.go b/vendor/github.com/docker/docker/client/errors.go new file mode 100644 index 0000000000..041bc8d49c --- /dev/null +++ b/vendor/github.com/docker/docker/client/errors.go @@ -0,0 +1,138 @@ +package client // import "github.com/docker/docker/client" + +import ( + "fmt" + "net/http" + + "github.com/docker/docker/api/types/versions" + "github.com/docker/docker/errdefs" + "github.com/pkg/errors" +) + +// errConnectionFailed implements an error returned when connection failed. +type errConnectionFailed struct { + host string +} + +// Error returns a string representation of an errConnectionFailed +func (err errConnectionFailed) Error() string { + if err.host == "" { + return "Cannot connect to the Docker daemon. Is the docker daemon running on this host?" + } + return fmt.Sprintf("Cannot connect to the Docker daemon at %s. Is the docker daemon running?", err.host) +} + +// IsErrConnectionFailed returns true if the error is caused by connection failed. +func IsErrConnectionFailed(err error) bool { + return errors.As(err, &errConnectionFailed{}) +} + +// ErrorConnectionFailed returns an error with host in the error message when connection to docker daemon failed. +func ErrorConnectionFailed(host string) error { + return errConnectionFailed{host: host} +} + +// Deprecated: use the errdefs.NotFound() interface instead. Kept for backward compatibility +type notFound interface { + error + NotFound() bool +} + +// IsErrNotFound returns true if the error is a NotFound error, which is returned +// by the API when some object is not found. +func IsErrNotFound(err error) bool { + var e notFound + if errors.As(err, &e) { + return true + } + return errdefs.IsNotFound(err) +} + +type objectNotFoundError struct { + object string + id string +} + +func (e objectNotFoundError) NotFound() {} + +func (e objectNotFoundError) Error() string { + return fmt.Sprintf("Error: No such %s: %s", e.object, e.id) +} + +func wrapResponseError(err error, resp serverResponse, object, id string) error { + switch { + case err == nil: + return nil + case resp.statusCode == http.StatusNotFound: + return objectNotFoundError{object: object, id: id} + case resp.statusCode == http.StatusNotImplemented: + return errdefs.NotImplemented(err) + default: + return err + } +} + +// unauthorizedError represents an authorization error in a remote registry. +type unauthorizedError struct { + cause error +} + +// Error returns a string representation of an unauthorizedError +func (u unauthorizedError) Error() string { + return u.cause.Error() +} + +// IsErrUnauthorized returns true if the error is caused +// when a remote registry authentication fails +func IsErrUnauthorized(err error) bool { + if _, ok := err.(unauthorizedError); ok { + return ok + } + return errdefs.IsUnauthorized(err) +} + +type pluginPermissionDenied struct { + name string +} + +func (e pluginPermissionDenied) Error() string { + return "Permission denied while installing plugin " + e.name +} + +// IsErrPluginPermissionDenied returns true if the error is caused +// when a user denies a plugin's permissions +func IsErrPluginPermissionDenied(err error) bool { + _, ok := err.(pluginPermissionDenied) + return ok +} + +type notImplementedError struct { + message string +} + +func (e notImplementedError) Error() string { + return e.message +} + +func (e notImplementedError) NotImplemented() bool { + return true +} + +// IsErrNotImplemented returns true if the error is a NotImplemented error. +// This is returned by the API when a requested feature has not been +// implemented. +func IsErrNotImplemented(err error) bool { + if _, ok := err.(notImplementedError); ok { + return ok + } + return errdefs.IsNotImplemented(err) +} + +// NewVersionError returns an error if the APIVersion required +// if less than the current supported version +func (cli *Client) NewVersionError(APIrequired, feature string) error { + if cli.version != "" && versions.LessThan(cli.version, APIrequired) { + return fmt.Errorf("%q requires API version %s, but the Docker daemon API version is %s", feature, APIrequired, cli.version) + } + return nil +} diff --git a/vendor/github.com/docker/docker/client/events.go b/vendor/github.com/docker/docker/client/events.go new file mode 100644 index 0000000000..f0dc9d9e12 --- /dev/null +++ b/vendor/github.com/docker/docker/client/events.go @@ -0,0 +1,102 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/events" + "github.com/docker/docker/api/types/filters" + timetypes "github.com/docker/docker/api/types/time" +) + +// Events returns a stream of events in the daemon. It's up to the caller to close the stream +// by cancelling the context. Once the stream has been completely read an io.EOF error will +// be sent over the error channel. If an error is sent all processing will be stopped. It's up +// to the caller to reopen the stream in the event of an error by reinvoking this method. +func (cli *Client) Events(ctx context.Context, options types.EventsOptions) (<-chan events.Message, <-chan error) { + + messages := make(chan events.Message) + errs := make(chan error, 1) + + started := make(chan struct{}) + go func() { + defer close(errs) + + query, err := buildEventsQueryParams(cli.version, options) + if err != nil { + close(started) + errs <- err + return + } + + resp, err := cli.get(ctx, "/events", query, nil) + if err != nil { + close(started) + errs <- err + return + } + defer resp.body.Close() + + decoder := json.NewDecoder(resp.body) + + close(started) + for { + select { + case <-ctx.Done(): + errs <- ctx.Err() + return + default: + var event events.Message + if err := decoder.Decode(&event); err != nil { + errs <- err + return + } + + select { + case messages <- event: + case <-ctx.Done(): + errs <- ctx.Err() + return + } + } + } + }() + <-started + + return messages, errs +} + +func buildEventsQueryParams(cliVersion string, options types.EventsOptions) (url.Values, error) { + query := url.Values{} + ref := time.Now() + + if options.Since != "" { + ts, err := timetypes.GetTimestamp(options.Since, ref) + if err != nil { + return nil, err + } + query.Set("since", ts) + } + + if options.Until != "" { + ts, err := timetypes.GetTimestamp(options.Until, ref) + if err != nil { + return nil, err + } + query.Set("until", ts) + } + + if options.Filters.Len() > 0 { + //nolint:staticcheck // ignore SA1019 for old code + filterJSON, err := filters.ToParamWithVersion(cliVersion, options.Filters) + if err != nil { + return nil, err + } + query.Set("filters", filterJSON) + } + + return query, nil +} diff --git a/vendor/github.com/docker/docker/client/hijack.go b/vendor/github.com/docker/docker/client/hijack.go new file mode 100644 index 0000000000..e1dc49ef0f --- /dev/null +++ b/vendor/github.com/docker/docker/client/hijack.go @@ -0,0 +1,145 @@ +package client // import "github.com/docker/docker/client" + +import ( + "bufio" + "context" + "crypto/tls" + "fmt" + "net" + "net/http" + "net/http/httputil" + "net/url" + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/go-connections/sockets" + "github.com/pkg/errors" +) + +// postHijacked sends a POST request and hijacks the connection. +func (cli *Client) postHijacked(ctx context.Context, path string, query url.Values, body interface{}, headers map[string][]string) (types.HijackedResponse, error) { + bodyEncoded, err := encodeData(body) + if err != nil { + return types.HijackedResponse{}, err + } + + apiPath := cli.getAPIPath(ctx, path, query) + req, err := http.NewRequest(http.MethodPost, apiPath, bodyEncoded) + if err != nil { + return types.HijackedResponse{}, err + } + req = cli.addHeaders(req, headers) + + conn, err := cli.setupHijackConn(ctx, req, "tcp") + if err != nil { + return types.HijackedResponse{}, err + } + + return types.HijackedResponse{Conn: conn, Reader: bufio.NewReader(conn)}, err +} + +// DialHijack returns a hijacked connection with negotiated protocol proto. +func (cli *Client) DialHijack(ctx context.Context, url, proto string, meta map[string][]string) (net.Conn, error) { + req, err := http.NewRequest(http.MethodPost, url, nil) + if err != nil { + return nil, err + } + req = cli.addHeaders(req, meta) + + return cli.setupHijackConn(ctx, req, proto) +} + +// fallbackDial is used when WithDialer() was not called. +// See cli.Dialer(). +func fallbackDial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) { + if tlsConfig != nil && proto != "unix" && proto != "npipe" { + return tls.Dial(proto, addr, tlsConfig) + } + if proto == "npipe" { + return sockets.DialPipe(addr, 32*time.Second) + } + return net.Dial(proto, addr) +} + +func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto string) (net.Conn, error) { + req.Host = cli.addr + req.Header.Set("Connection", "Upgrade") + req.Header.Set("Upgrade", proto) + + dialer := cli.Dialer() + conn, err := dialer(ctx) + if err != nil { + return nil, errors.Wrap(err, "cannot connect to the Docker daemon. Is 'docker daemon' running on this host?") + } + + // When we set up a TCP connection for hijack, there could be long periods + // of inactivity (a long running command with no output) that in certain + // network setups may cause ECONNTIMEOUT, leaving the client in an unknown + // state. Setting TCP KeepAlive on the socket connection will prohibit + // ECONNTIMEOUT unless the socket connection truly is broken + if tcpConn, ok := conn.(*net.TCPConn); ok { + tcpConn.SetKeepAlive(true) + tcpConn.SetKeepAlivePeriod(30 * time.Second) + } + + clientconn := httputil.NewClientConn(conn, nil) + defer clientconn.Close() + + // Server hijacks the connection, error 'connection closed' expected + resp, err := clientconn.Do(req) + + //nolint:staticcheck // ignore SA1019 for connecting to old (pre go1.8) daemons + if err != httputil.ErrPersistEOF { + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusSwitchingProtocols { + resp.Body.Close() + return nil, fmt.Errorf("unable to upgrade to %s, received %d", proto, resp.StatusCode) + } + } + + c, br := clientconn.Hijack() + if br.Buffered() > 0 { + // If there is buffered content, wrap the connection. We return an + // object that implements CloseWrite iff the underlying connection + // implements it. + if _, ok := c.(types.CloseWriter); ok { + c = &hijackedConnCloseWriter{&hijackedConn{c, br}} + } else { + c = &hijackedConn{c, br} + } + } else { + br.Reset(nil) + } + + return c, nil +} + +// hijackedConn wraps a net.Conn and is returned by setupHijackConn in the case +// that a) there was already buffered data in the http layer when Hijack() was +// called, and b) the underlying net.Conn does *not* implement CloseWrite(). +// hijackedConn does not implement CloseWrite() either. +type hijackedConn struct { + net.Conn + r *bufio.Reader +} + +func (c *hijackedConn) Read(b []byte) (int, error) { + return c.r.Read(b) +} + +// hijackedConnCloseWriter is a hijackedConn which additionally implements +// CloseWrite(). It is returned by setupHijackConn in the case that a) there +// was already buffered data in the http layer when Hijack() was called, and b) +// the underlying net.Conn *does* implement CloseWrite(). +type hijackedConnCloseWriter struct { + *hijackedConn +} + +var _ types.CloseWriter = &hijackedConnCloseWriter{} + +func (c *hijackedConnCloseWriter) CloseWrite() error { + conn := c.Conn.(types.CloseWriter) + return conn.CloseWrite() +} diff --git a/vendor/github.com/docker/docker/client/image_build.go b/vendor/github.com/docker/docker/client/image_build.go new file mode 100644 index 0000000000..8fcf995036 --- /dev/null +++ b/vendor/github.com/docker/docker/client/image_build.go @@ -0,0 +1,146 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/base64" + "encoding/json" + "io" + "net/http" + "net/url" + "strconv" + "strings" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" +) + +// ImageBuild sends request to the daemon to build images. +// The Body in the response implement an io.ReadCloser and it's up to the caller to +// close it. +func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) { + query, err := cli.imageBuildOptionsToQuery(options) + if err != nil { + return types.ImageBuildResponse{}, err + } + + headers := http.Header(make(map[string][]string)) + buf, err := json.Marshal(options.AuthConfigs) + if err != nil { + return types.ImageBuildResponse{}, err + } + headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf)) + + headers.Set("Content-Type", "application/x-tar") + + serverResp, err := cli.postRaw(ctx, "/build", query, buildContext, headers) + if err != nil { + return types.ImageBuildResponse{}, err + } + + osType := getDockerOS(serverResp.header.Get("Server")) + + return types.ImageBuildResponse{ + Body: serverResp.body, + OSType: osType, + }, nil +} + +func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (url.Values, error) { + query := url.Values{ + "t": options.Tags, + "securityopt": options.SecurityOpt, + "extrahosts": options.ExtraHosts, + } + if options.SuppressOutput { + query.Set("q", "1") + } + if options.RemoteContext != "" { + query.Set("remote", options.RemoteContext) + } + if options.NoCache { + query.Set("nocache", "1") + } + if options.Remove { + query.Set("rm", "1") + } else { + query.Set("rm", "0") + } + + if options.ForceRemove { + query.Set("forcerm", "1") + } + + if options.PullParent { + query.Set("pull", "1") + } + + if options.Squash { + if err := cli.NewVersionError("1.25", "squash"); err != nil { + return query, err + } + query.Set("squash", "1") + } + + if !container.Isolation.IsDefault(options.Isolation) { + query.Set("isolation", string(options.Isolation)) + } + + query.Set("cpusetcpus", options.CPUSetCPUs) + query.Set("networkmode", options.NetworkMode) + query.Set("cpusetmems", options.CPUSetMems) + query.Set("cpushares", strconv.FormatInt(options.CPUShares, 10)) + query.Set("cpuquota", strconv.FormatInt(options.CPUQuota, 10)) + query.Set("cpuperiod", strconv.FormatInt(options.CPUPeriod, 10)) + query.Set("memory", strconv.FormatInt(options.Memory, 10)) + query.Set("memswap", strconv.FormatInt(options.MemorySwap, 10)) + query.Set("cgroupparent", options.CgroupParent) + query.Set("shmsize", strconv.FormatInt(options.ShmSize, 10)) + query.Set("dockerfile", options.Dockerfile) + query.Set("target", options.Target) + + ulimitsJSON, err := json.Marshal(options.Ulimits) + if err != nil { + return query, err + } + query.Set("ulimits", string(ulimitsJSON)) + + buildArgsJSON, err := json.Marshal(options.BuildArgs) + if err != nil { + return query, err + } + query.Set("buildargs", string(buildArgsJSON)) + + labelsJSON, err := json.Marshal(options.Labels) + if err != nil { + return query, err + } + query.Set("labels", string(labelsJSON)) + + cacheFromJSON, err := json.Marshal(options.CacheFrom) + if err != nil { + return query, err + } + query.Set("cachefrom", string(cacheFromJSON)) + if options.SessionID != "" { + query.Set("session", options.SessionID) + } + if options.Platform != "" { + if err := cli.NewVersionError("1.32", "platform"); err != nil { + return query, err + } + query.Set("platform", strings.ToLower(options.Platform)) + } + if options.BuildID != "" { + query.Set("buildid", options.BuildID) + } + query.Set("version", string(options.Version)) + + if options.Outputs != nil { + outputsJSON, err := json.Marshal(options.Outputs) + if err != nil { + return query, err + } + query.Set("outputs", string(outputsJSON)) + } + return query, nil +} diff --git a/vendor/github.com/docker/docker/client/image_create.go b/vendor/github.com/docker/docker/client/image_create.go new file mode 100644 index 0000000000..239380474e --- /dev/null +++ b/vendor/github.com/docker/docker/client/image_create.go @@ -0,0 +1,37 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "io" + "net/url" + "strings" + + "github.com/docker/distribution/reference" + "github.com/docker/docker/api/types" +) + +// ImageCreate creates a new image based in the parent options. +// It returns the JSON content in the response body. +func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) { + ref, err := reference.ParseNormalizedNamed(parentReference) + if err != nil { + return nil, err + } + + query := url.Values{} + query.Set("fromImage", reference.FamiliarName(ref)) + query.Set("tag", getAPITagFromNamedRef(ref)) + if options.Platform != "" { + query.Set("platform", strings.ToLower(options.Platform)) + } + resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth) + if err != nil { + return nil, err + } + return resp.body, nil +} + +func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) { + headers := map[string][]string{"X-Registry-Auth": {registryAuth}} + return cli.post(ctx, "/images/create", query, nil, headers) +} diff --git a/vendor/github.com/docker/docker/client/image_history.go b/vendor/github.com/docker/docker/client/image_history.go new file mode 100644 index 0000000000..b5bea10d8f --- /dev/null +++ b/vendor/github.com/docker/docker/client/image_history.go @@ -0,0 +1,22 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/docker/docker/api/types/image" +) + +// ImageHistory returns the changes in an image in history format. +func (cli *Client) ImageHistory(ctx context.Context, imageID string) ([]image.HistoryResponseItem, error) { + var history []image.HistoryResponseItem + serverResp, err := cli.get(ctx, "/images/"+imageID+"/history", url.Values{}, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return history, err + } + + err = json.NewDecoder(serverResp.body).Decode(&history) + return history, err +} diff --git a/vendor/github.com/docker/docker/client/image_import.go b/vendor/github.com/docker/docker/client/image_import.go new file mode 100644 index 0000000000..d3336d4106 --- /dev/null +++ b/vendor/github.com/docker/docker/client/image_import.go @@ -0,0 +1,40 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "io" + "net/url" + "strings" + + "github.com/docker/distribution/reference" + "github.com/docker/docker/api/types" +) + +// ImageImport creates a new image based in the source options. +// It returns the JSON content in the response body. +func (cli *Client) ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) { + if ref != "" { + // Check if the given image name can be resolved + if _, err := reference.ParseNormalizedNamed(ref); err != nil { + return nil, err + } + } + + query := url.Values{} + query.Set("fromSrc", source.SourceName) + query.Set("repo", ref) + query.Set("tag", options.Tag) + query.Set("message", options.Message) + if options.Platform != "" { + query.Set("platform", strings.ToLower(options.Platform)) + } + for _, change := range options.Changes { + query.Add("changes", change) + } + + resp, err := cli.postRaw(ctx, "/images/create", query, source.Source, nil) + if err != nil { + return nil, err + } + return resp.body, nil +} diff --git a/vendor/github.com/docker/docker/client/image_inspect.go b/vendor/github.com/docker/docker/client/image_inspect.go new file mode 100644 index 0000000000..1eb8dce025 --- /dev/null +++ b/vendor/github.com/docker/docker/client/image_inspect.go @@ -0,0 +1,32 @@ +package client // import "github.com/docker/docker/client" + +import ( + "bytes" + "context" + "encoding/json" + "io/ioutil" + + "github.com/docker/docker/api/types" +) + +// ImageInspectWithRaw returns the image information and its raw representation. +func (cli *Client) ImageInspectWithRaw(ctx context.Context, imageID string) (types.ImageInspect, []byte, error) { + if imageID == "" { + return types.ImageInspect{}, nil, objectNotFoundError{object: "image", id: imageID} + } + serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", nil, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return types.ImageInspect{}, nil, wrapResponseError(err, serverResp, "image", imageID) + } + + body, err := ioutil.ReadAll(serverResp.body) + if err != nil { + return types.ImageInspect{}, nil, err + } + + var response types.ImageInspect + rdr := bytes.NewReader(body) + err = json.NewDecoder(rdr).Decode(&response) + return response, body, err +} diff --git a/vendor/github.com/docker/docker/client/image_list.go b/vendor/github.com/docker/docker/client/image_list.go new file mode 100644 index 0000000000..a4d7505094 --- /dev/null +++ b/vendor/github.com/docker/docker/client/image_list.go @@ -0,0 +1,46 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/versions" +) + +// ImageList returns a list of images in the docker host. +func (cli *Client) ImageList(ctx context.Context, options types.ImageListOptions) ([]types.ImageSummary, error) { + var images []types.ImageSummary + query := url.Values{} + + optionFilters := options.Filters + referenceFilters := optionFilters.Get("reference") + if versions.LessThan(cli.version, "1.25") && len(referenceFilters) > 0 { + query.Set("filter", referenceFilters[0]) + for _, filterValue := range referenceFilters { + optionFilters.Del("reference", filterValue) + } + } + if optionFilters.Len() > 0 { + //nolint:staticcheck // ignore SA1019 for old code + filterJSON, err := filters.ToParamWithVersion(cli.version, optionFilters) + if err != nil { + return images, err + } + query.Set("filters", filterJSON) + } + if options.All { + query.Set("all", "1") + } + + serverResp, err := cli.get(ctx, "/images/json", query, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return images, err + } + + err = json.NewDecoder(serverResp.body).Decode(&images) + return images, err +} diff --git a/vendor/github.com/docker/docker/client/image_load.go b/vendor/github.com/docker/docker/client/image_load.go new file mode 100644 index 0000000000..91016e493c --- /dev/null +++ b/vendor/github.com/docker/docker/client/image_load.go @@ -0,0 +1,29 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "io" + "net/url" + + "github.com/docker/docker/api/types" +) + +// ImageLoad loads an image in the docker host from the client host. +// It's up to the caller to close the io.ReadCloser in the +// ImageLoadResponse returned by this function. +func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, quiet bool) (types.ImageLoadResponse, error) { + v := url.Values{} + v.Set("quiet", "0") + if quiet { + v.Set("quiet", "1") + } + headers := map[string][]string{"Content-Type": {"application/x-tar"}} + resp, err := cli.postRaw(ctx, "/images/load", v, input, headers) + if err != nil { + return types.ImageLoadResponse{}, err + } + return types.ImageLoadResponse{ + Body: resp.body, + JSON: resp.header.Get("Content-Type") == "application/json", + }, nil +} diff --git a/vendor/github.com/docker/docker/client/image_prune.go b/vendor/github.com/docker/docker/client/image_prune.go new file mode 100644 index 0000000000..56af6d7f98 --- /dev/null +++ b/vendor/github.com/docker/docker/client/image_prune.go @@ -0,0 +1,36 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" +) + +// ImagesPrune requests the daemon to delete unused data +func (cli *Client) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (types.ImagesPruneReport, error) { + var report types.ImagesPruneReport + + if err := cli.NewVersionError("1.25", "image prune"); err != nil { + return report, err + } + + query, err := getFiltersQuery(pruneFilters) + if err != nil { + return report, err + } + + serverResp, err := cli.post(ctx, "/images/prune", query, nil, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return report, err + } + + if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { + return report, fmt.Errorf("Error retrieving disk usage: %v", err) + } + + return report, nil +} diff --git a/vendor/github.com/docker/docker/client/image_pull.go b/vendor/github.com/docker/docker/client/image_pull.go new file mode 100644 index 0000000000..a23975591b --- /dev/null +++ b/vendor/github.com/docker/docker/client/image_pull.go @@ -0,0 +1,64 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "io" + "net/url" + "strings" + + "github.com/docker/distribution/reference" + "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" +) + +// ImagePull requests the docker host to pull an image from a remote registry. +// It executes the privileged function if the operation is unauthorized +// and it tries one more time. +// It's up to the caller to handle the io.ReadCloser and close it properly. +// +// FIXME(vdemeester): there is currently used in a few way in docker/docker +// - if not in trusted content, ref is used to pass the whole reference, and tag is empty +// - if in trusted content, ref is used to pass the reference name, and tag for the digest +func (cli *Client) ImagePull(ctx context.Context, refStr string, options types.ImagePullOptions) (io.ReadCloser, error) { + ref, err := reference.ParseNormalizedNamed(refStr) + if err != nil { + return nil, err + } + + query := url.Values{} + query.Set("fromImage", reference.FamiliarName(ref)) + if !options.All { + query.Set("tag", getAPITagFromNamedRef(ref)) + } + if options.Platform != "" { + query.Set("platform", strings.ToLower(options.Platform)) + } + + resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth) + if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil { + newAuthHeader, privilegeErr := options.PrivilegeFunc() + if privilegeErr != nil { + return nil, privilegeErr + } + resp, err = cli.tryImageCreate(ctx, query, newAuthHeader) + } + if err != nil { + return nil, err + } + return resp.body, nil +} + +// getAPITagFromNamedRef returns a tag from the specified reference. +// This function is necessary as long as the docker "server" api expects +// digests to be sent as tags and makes a distinction between the name +// and tag/digest part of a reference. +func getAPITagFromNamedRef(ref reference.Named) string { + if digested, ok := ref.(reference.Digested); ok { + return digested.Digest().String() + } + ref = reference.TagNameOnly(ref) + if tagged, ok := ref.(reference.Tagged); ok { + return tagged.Tag() + } + return "" +} diff --git a/vendor/github.com/docker/docker/client/image_push.go b/vendor/github.com/docker/docker/client/image_push.go new file mode 100644 index 0000000000..845580d4a4 --- /dev/null +++ b/vendor/github.com/docker/docker/client/image_push.go @@ -0,0 +1,54 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "errors" + "io" + "net/url" + + "github.com/docker/distribution/reference" + "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" +) + +// ImagePush requests the docker host to push an image to a remote registry. +// It executes the privileged function if the operation is unauthorized +// and it tries one more time. +// It's up to the caller to handle the io.ReadCloser and close it properly. +func (cli *Client) ImagePush(ctx context.Context, image string, options types.ImagePushOptions) (io.ReadCloser, error) { + ref, err := reference.ParseNormalizedNamed(image) + if err != nil { + return nil, err + } + + if _, isCanonical := ref.(reference.Canonical); isCanonical { + return nil, errors.New("cannot push a digest reference") + } + + name := reference.FamiliarName(ref) + query := url.Values{} + if !options.All { + ref = reference.TagNameOnly(ref) + if tagged, ok := ref.(reference.Tagged); ok { + query.Set("tag", tagged.Tag()) + } + } + + resp, err := cli.tryImagePush(ctx, name, query, options.RegistryAuth) + if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil { + newAuthHeader, privilegeErr := options.PrivilegeFunc() + if privilegeErr != nil { + return nil, privilegeErr + } + resp, err = cli.tryImagePush(ctx, name, query, newAuthHeader) + } + if err != nil { + return nil, err + } + return resp.body, nil +} + +func (cli *Client) tryImagePush(ctx context.Context, imageID string, query url.Values, registryAuth string) (serverResponse, error) { + headers := map[string][]string{"X-Registry-Auth": {registryAuth}} + return cli.post(ctx, "/images/"+imageID+"/push", query, nil, headers) +} diff --git a/vendor/github.com/docker/docker/client/image_remove.go b/vendor/github.com/docker/docker/client/image_remove.go new file mode 100644 index 0000000000..84a41af0f2 --- /dev/null +++ b/vendor/github.com/docker/docker/client/image_remove.go @@ -0,0 +1,31 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/docker/docker/api/types" +) + +// ImageRemove removes an image from the docker host. +func (cli *Client) ImageRemove(ctx context.Context, imageID string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) { + query := url.Values{} + + if options.Force { + query.Set("force", "1") + } + if !options.PruneChildren { + query.Set("noprune", "1") + } + + var dels []types.ImageDeleteResponseItem + resp, err := cli.delete(ctx, "/images/"+imageID, query, nil) + defer ensureReaderClosed(resp) + if err != nil { + return dels, wrapResponseError(err, resp, "image", imageID) + } + + err = json.NewDecoder(resp.body).Decode(&dels) + return dels, err +} diff --git a/vendor/github.com/docker/docker/client/image_save.go b/vendor/github.com/docker/docker/client/image_save.go new file mode 100644 index 0000000000..d1314e4b22 --- /dev/null +++ b/vendor/github.com/docker/docker/client/image_save.go @@ -0,0 +1,21 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "io" + "net/url" +) + +// ImageSave retrieves one or more images from the docker host as an io.ReadCloser. +// It's up to the caller to store the images and close the stream. +func (cli *Client) ImageSave(ctx context.Context, imageIDs []string) (io.ReadCloser, error) { + query := url.Values{ + "names": imageIDs, + } + + resp, err := cli.get(ctx, "/images/get", query, nil) + if err != nil { + return nil, err + } + return resp.body, nil +} diff --git a/vendor/github.com/docker/docker/client/image_search.go b/vendor/github.com/docker/docker/client/image_search.go new file mode 100644 index 0000000000..82955a7477 --- /dev/null +++ b/vendor/github.com/docker/docker/client/image_search.go @@ -0,0 +1,51 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "fmt" + "net/url" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/registry" + "github.com/docker/docker/errdefs" +) + +// ImageSearch makes the docker host to search by a term in a remote registry. +// The list of results is not sorted in any fashion. +func (cli *Client) ImageSearch(ctx context.Context, term string, options types.ImageSearchOptions) ([]registry.SearchResult, error) { + var results []registry.SearchResult + query := url.Values{} + query.Set("term", term) + query.Set("limit", fmt.Sprintf("%d", options.Limit)) + + if options.Filters.Len() > 0 { + filterJSON, err := filters.ToJSON(options.Filters) + if err != nil { + return results, err + } + query.Set("filters", filterJSON) + } + + resp, err := cli.tryImageSearch(ctx, query, options.RegistryAuth) + defer ensureReaderClosed(resp) + if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil { + newAuthHeader, privilegeErr := options.PrivilegeFunc() + if privilegeErr != nil { + return results, privilegeErr + } + resp, err = cli.tryImageSearch(ctx, query, newAuthHeader) + } + if err != nil { + return results, err + } + + err = json.NewDecoder(resp.body).Decode(&results) + return results, err +} + +func (cli *Client) tryImageSearch(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) { + headers := map[string][]string{"X-Registry-Auth": {registryAuth}} + return cli.get(ctx, "/images/search", query, headers) +} diff --git a/vendor/github.com/docker/docker/client/image_tag.go b/vendor/github.com/docker/docker/client/image_tag.go new file mode 100644 index 0000000000..5652bfc252 --- /dev/null +++ b/vendor/github.com/docker/docker/client/image_tag.go @@ -0,0 +1,37 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + + "github.com/docker/distribution/reference" + "github.com/pkg/errors" +) + +// ImageTag tags an image in the docker host +func (cli *Client) ImageTag(ctx context.Context, source, target string) error { + if _, err := reference.ParseAnyReference(source); err != nil { + return errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", source) + } + + ref, err := reference.ParseNormalizedNamed(target) + if err != nil { + return errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", target) + } + + if _, isCanonical := ref.(reference.Canonical); isCanonical { + return errors.New("refusing to create a tag with a digest reference") + } + + ref = reference.TagNameOnly(ref) + + query := url.Values{} + query.Set("repo", reference.FamiliarName(ref)) + if tagged, ok := ref.(reference.Tagged); ok { + query.Set("tag", tagged.Tag()) + } + + resp, err := cli.post(ctx, "/images/"+source+"/tag", query, nil, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/info.go b/vendor/github.com/docker/docker/client/info.go new file mode 100644 index 0000000000..c856704e23 --- /dev/null +++ b/vendor/github.com/docker/docker/client/info.go @@ -0,0 +1,26 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "fmt" + "net/url" + + "github.com/docker/docker/api/types" +) + +// Info returns information about the docker server. +func (cli *Client) Info(ctx context.Context) (types.Info, error) { + var info types.Info + serverResp, err := cli.get(ctx, "/info", url.Values{}, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return info, err + } + + if err := json.NewDecoder(serverResp.body).Decode(&info); err != nil { + return info, fmt.Errorf("Error reading remote info: %v", err) + } + + return info, nil +} diff --git a/vendor/github.com/docker/docker/client/interface.go b/vendor/github.com/docker/docker/client/interface.go new file mode 100644 index 0000000000..aabad4a911 --- /dev/null +++ b/vendor/github.com/docker/docker/client/interface.go @@ -0,0 +1,201 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "io" + "net" + "net/http" + "time" + + "github.com/docker/docker/api/types" + containertypes "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/events" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/image" + networktypes "github.com/docker/docker/api/types/network" + "github.com/docker/docker/api/types/registry" + "github.com/docker/docker/api/types/swarm" + volumetypes "github.com/docker/docker/api/types/volume" + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +// CommonAPIClient is the common methods between stable and experimental versions of APIClient. +type CommonAPIClient interface { + ConfigAPIClient + ContainerAPIClient + DistributionAPIClient + ImageAPIClient + NodeAPIClient + NetworkAPIClient + PluginAPIClient + ServiceAPIClient + SwarmAPIClient + SecretAPIClient + SystemAPIClient + VolumeAPIClient + ClientVersion() string + DaemonHost() string + HTTPClient() *http.Client + ServerVersion(ctx context.Context) (types.Version, error) + NegotiateAPIVersion(ctx context.Context) + NegotiateAPIVersionPing(types.Ping) + DialHijack(ctx context.Context, url, proto string, meta map[string][]string) (net.Conn, error) + Dialer() func(context.Context) (net.Conn, error) + Close() error +} + +// ContainerAPIClient defines API client methods for the containers +type ContainerAPIClient interface { + ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error) + ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error) + ContainerCreate(ctx context.Context, config *containertypes.Config, hostConfig *containertypes.HostConfig, networkingConfig *networktypes.NetworkingConfig, platform *specs.Platform, containerName string) (containertypes.ContainerCreateCreatedBody, error) + ContainerDiff(ctx context.Context, container string) ([]containertypes.ContainerChangeResponseItem, error) + ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error) + ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.IDResponse, error) + ContainerExecInspect(ctx context.Context, execID string) (types.ContainerExecInspect, error) + ContainerExecResize(ctx context.Context, execID string, options types.ResizeOptions) error + ContainerExecStart(ctx context.Context, execID string, config types.ExecStartCheck) error + ContainerExport(ctx context.Context, container string) (io.ReadCloser, error) + ContainerInspect(ctx context.Context, container string) (types.ContainerJSON, error) + ContainerInspectWithRaw(ctx context.Context, container string, getSize bool) (types.ContainerJSON, []byte, error) + ContainerKill(ctx context.Context, container, signal string) error + ContainerList(ctx context.Context, options types.ContainerListOptions) ([]types.Container, error) + ContainerLogs(ctx context.Context, container string, options types.ContainerLogsOptions) (io.ReadCloser, error) + ContainerPause(ctx context.Context, container string) error + ContainerRemove(ctx context.Context, container string, options types.ContainerRemoveOptions) error + ContainerRename(ctx context.Context, container, newContainerName string) error + ContainerResize(ctx context.Context, container string, options types.ResizeOptions) error + ContainerRestart(ctx context.Context, container string, timeout *time.Duration) error + ContainerStatPath(ctx context.Context, container, path string) (types.ContainerPathStat, error) + ContainerStats(ctx context.Context, container string, stream bool) (types.ContainerStats, error) + ContainerStatsOneShot(ctx context.Context, container string) (types.ContainerStats, error) + ContainerStart(ctx context.Context, container string, options types.ContainerStartOptions) error + ContainerStop(ctx context.Context, container string, timeout *time.Duration) error + ContainerTop(ctx context.Context, container string, arguments []string) (containertypes.ContainerTopOKBody, error) + ContainerUnpause(ctx context.Context, container string) error + ContainerUpdate(ctx context.Context, container string, updateConfig containertypes.UpdateConfig) (containertypes.ContainerUpdateOKBody, error) + ContainerWait(ctx context.Context, container string, condition containertypes.WaitCondition) (<-chan containertypes.ContainerWaitOKBody, <-chan error) + CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) + CopyToContainer(ctx context.Context, container, path string, content io.Reader, options types.CopyToContainerOptions) error + ContainersPrune(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error) +} + +// DistributionAPIClient defines API client methods for the registry +type DistributionAPIClient interface { + DistributionInspect(ctx context.Context, image, encodedRegistryAuth string) (registry.DistributionInspect, error) +} + +// ImageAPIClient defines API client methods for the images +type ImageAPIClient interface { + ImageBuild(ctx context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) + BuildCachePrune(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) + BuildCancel(ctx context.Context, id string) error + ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) + ImageHistory(ctx context.Context, image string) ([]image.HistoryResponseItem, error) + ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) + ImageInspectWithRaw(ctx context.Context, image string) (types.ImageInspect, []byte, error) + ImageList(ctx context.Context, options types.ImageListOptions) ([]types.ImageSummary, error) + ImageLoad(ctx context.Context, input io.Reader, quiet bool) (types.ImageLoadResponse, error) + ImagePull(ctx context.Context, ref string, options types.ImagePullOptions) (io.ReadCloser, error) + ImagePush(ctx context.Context, ref string, options types.ImagePushOptions) (io.ReadCloser, error) + ImageRemove(ctx context.Context, image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) + ImageSearch(ctx context.Context, term string, options types.ImageSearchOptions) ([]registry.SearchResult, error) + ImageSave(ctx context.Context, images []string) (io.ReadCloser, error) + ImageTag(ctx context.Context, image, ref string) error + ImagesPrune(ctx context.Context, pruneFilter filters.Args) (types.ImagesPruneReport, error) +} + +// NetworkAPIClient defines API client methods for the networks +type NetworkAPIClient interface { + NetworkConnect(ctx context.Context, network, container string, config *networktypes.EndpointSettings) error + NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error) + NetworkDisconnect(ctx context.Context, network, container string, force bool) error + NetworkInspect(ctx context.Context, network string, options types.NetworkInspectOptions) (types.NetworkResource, error) + NetworkInspectWithRaw(ctx context.Context, network string, options types.NetworkInspectOptions) (types.NetworkResource, []byte, error) + NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) + NetworkRemove(ctx context.Context, network string) error + NetworksPrune(ctx context.Context, pruneFilter filters.Args) (types.NetworksPruneReport, error) +} + +// NodeAPIClient defines API client methods for the nodes +type NodeAPIClient interface { + NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) + NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) + NodeRemove(ctx context.Context, nodeID string, options types.NodeRemoveOptions) error + NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error +} + +// PluginAPIClient defines API client methods for the plugins +type PluginAPIClient interface { + PluginList(ctx context.Context, filter filters.Args) (types.PluginsListResponse, error) + PluginRemove(ctx context.Context, name string, options types.PluginRemoveOptions) error + PluginEnable(ctx context.Context, name string, options types.PluginEnableOptions) error + PluginDisable(ctx context.Context, name string, options types.PluginDisableOptions) error + PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) (io.ReadCloser, error) + PluginUpgrade(ctx context.Context, name string, options types.PluginInstallOptions) (io.ReadCloser, error) + PluginPush(ctx context.Context, name string, registryAuth string) (io.ReadCloser, error) + PluginSet(ctx context.Context, name string, args []string) error + PluginInspectWithRaw(ctx context.Context, name string) (*types.Plugin, []byte, error) + PluginCreate(ctx context.Context, createContext io.Reader, options types.PluginCreateOptions) error +} + +// ServiceAPIClient defines API client methods for the services +type ServiceAPIClient interface { + ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options types.ServiceCreateOptions) (types.ServiceCreateResponse, error) + ServiceInspectWithRaw(ctx context.Context, serviceID string, options types.ServiceInspectOptions) (swarm.Service, []byte, error) + ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) + ServiceRemove(ctx context.Context, serviceID string) error + ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) + ServiceLogs(ctx context.Context, serviceID string, options types.ContainerLogsOptions) (io.ReadCloser, error) + TaskLogs(ctx context.Context, taskID string, options types.ContainerLogsOptions) (io.ReadCloser, error) + TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error) + TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error) +} + +// SwarmAPIClient defines API client methods for the swarm +type SwarmAPIClient interface { + SwarmInit(ctx context.Context, req swarm.InitRequest) (string, error) + SwarmJoin(ctx context.Context, req swarm.JoinRequest) error + SwarmGetUnlockKey(ctx context.Context) (types.SwarmUnlockKeyResponse, error) + SwarmUnlock(ctx context.Context, req swarm.UnlockRequest) error + SwarmLeave(ctx context.Context, force bool) error + SwarmInspect(ctx context.Context) (swarm.Swarm, error) + SwarmUpdate(ctx context.Context, version swarm.Version, swarm swarm.Spec, flags swarm.UpdateFlags) error +} + +// SystemAPIClient defines API client methods for the system +type SystemAPIClient interface { + Events(ctx context.Context, options types.EventsOptions) (<-chan events.Message, <-chan error) + Info(ctx context.Context) (types.Info, error) + RegistryLogin(ctx context.Context, auth types.AuthConfig) (registry.AuthenticateOKBody, error) + DiskUsage(ctx context.Context) (types.DiskUsage, error) + Ping(ctx context.Context) (types.Ping, error) +} + +// VolumeAPIClient defines API client methods for the volumes +type VolumeAPIClient interface { + VolumeCreate(ctx context.Context, options volumetypes.VolumeCreateBody) (types.Volume, error) + VolumeInspect(ctx context.Context, volumeID string) (types.Volume, error) + VolumeInspectWithRaw(ctx context.Context, volumeID string) (types.Volume, []byte, error) + VolumeList(ctx context.Context, filter filters.Args) (volumetypes.VolumeListOKBody, error) + VolumeRemove(ctx context.Context, volumeID string, force bool) error + VolumesPrune(ctx context.Context, pruneFilter filters.Args) (types.VolumesPruneReport, error) +} + +// SecretAPIClient defines API client methods for secrets +type SecretAPIClient interface { + SecretList(ctx context.Context, options types.SecretListOptions) ([]swarm.Secret, error) + SecretCreate(ctx context.Context, secret swarm.SecretSpec) (types.SecretCreateResponse, error) + SecretRemove(ctx context.Context, id string) error + SecretInspectWithRaw(ctx context.Context, name string) (swarm.Secret, []byte, error) + SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error +} + +// ConfigAPIClient defines API client methods for configs +type ConfigAPIClient interface { + ConfigList(ctx context.Context, options types.ConfigListOptions) ([]swarm.Config, error) + ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (types.ConfigCreateResponse, error) + ConfigRemove(ctx context.Context, id string) error + ConfigInspectWithRaw(ctx context.Context, name string) (swarm.Config, []byte, error) + ConfigUpdate(ctx context.Context, id string, version swarm.Version, config swarm.ConfigSpec) error +} diff --git a/vendor/github.com/docker/docker/client/interface_experimental.go b/vendor/github.com/docker/docker/client/interface_experimental.go new file mode 100644 index 0000000000..402ffb512c --- /dev/null +++ b/vendor/github.com/docker/docker/client/interface_experimental.go @@ -0,0 +1,18 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + + "github.com/docker/docker/api/types" +) + +type apiClientExperimental interface { + CheckpointAPIClient +} + +// CheckpointAPIClient defines API client methods for the checkpoints +type CheckpointAPIClient interface { + CheckpointCreate(ctx context.Context, container string, options types.CheckpointCreateOptions) error + CheckpointDelete(ctx context.Context, container string, options types.CheckpointDeleteOptions) error + CheckpointList(ctx context.Context, container string, options types.CheckpointListOptions) ([]types.Checkpoint, error) +} diff --git a/vendor/github.com/docker/docker/client/interface_stable.go b/vendor/github.com/docker/docker/client/interface_stable.go new file mode 100644 index 0000000000..5502cd7426 --- /dev/null +++ b/vendor/github.com/docker/docker/client/interface_stable.go @@ -0,0 +1,10 @@ +package client // import "github.com/docker/docker/client" + +// APIClient is an interface that clients that talk with a docker server must implement. +type APIClient interface { + CommonAPIClient + apiClientExperimental +} + +// Ensure that Client always implements APIClient. +var _ APIClient = &Client{} diff --git a/vendor/github.com/docker/docker/client/login.go b/vendor/github.com/docker/docker/client/login.go new file mode 100644 index 0000000000..f058520638 --- /dev/null +++ b/vendor/github.com/docker/docker/client/login.go @@ -0,0 +1,25 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/registry" +) + +// RegistryLogin authenticates the docker server with a given docker registry. +// It returns unauthorizedError when the authentication fails. +func (cli *Client) RegistryLogin(ctx context.Context, auth types.AuthConfig) (registry.AuthenticateOKBody, error) { + resp, err := cli.post(ctx, "/auth", url.Values{}, auth, nil) + defer ensureReaderClosed(resp) + + if err != nil { + return registry.AuthenticateOKBody{}, err + } + + var response registry.AuthenticateOKBody + err = json.NewDecoder(resp.body).Decode(&response) + return response, err +} diff --git a/vendor/github.com/docker/docker/client/network_connect.go b/vendor/github.com/docker/docker/client/network_connect.go new file mode 100644 index 0000000000..5718946134 --- /dev/null +++ b/vendor/github.com/docker/docker/client/network_connect.go @@ -0,0 +1,19 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/network" +) + +// NetworkConnect connects a container to an existent network in the docker host. +func (cli *Client) NetworkConnect(ctx context.Context, networkID, containerID string, config *network.EndpointSettings) error { + nc := types.NetworkConnect{ + Container: containerID, + EndpointConfig: config, + } + resp, err := cli.post(ctx, "/networks/"+networkID+"/connect", nil, nc, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/network_create.go b/vendor/github.com/docker/docker/client/network_create.go new file mode 100644 index 0000000000..278d9383a8 --- /dev/null +++ b/vendor/github.com/docker/docker/client/network_create.go @@ -0,0 +1,25 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + + "github.com/docker/docker/api/types" +) + +// NetworkCreate creates a new network in the docker host. +func (cli *Client) NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error) { + networkCreateRequest := types.NetworkCreateRequest{ + NetworkCreate: options, + Name: name, + } + var response types.NetworkCreateResponse + serverResp, err := cli.post(ctx, "/networks/create", nil, networkCreateRequest, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return response, err + } + + err = json.NewDecoder(serverResp.body).Decode(&response) + return response, err +} diff --git a/vendor/github.com/docker/docker/client/network_disconnect.go b/vendor/github.com/docker/docker/client/network_disconnect.go new file mode 100644 index 0000000000..dd15676656 --- /dev/null +++ b/vendor/github.com/docker/docker/client/network_disconnect.go @@ -0,0 +1,15 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + + "github.com/docker/docker/api/types" +) + +// NetworkDisconnect disconnects a container from an existent network in the docker host. +func (cli *Client) NetworkDisconnect(ctx context.Context, networkID, containerID string, force bool) error { + nd := types.NetworkDisconnect{Container: containerID, Force: force} + resp, err := cli.post(ctx, "/networks/"+networkID+"/disconnect", nil, nd, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/network_inspect.go b/vendor/github.com/docker/docker/client/network_inspect.go new file mode 100644 index 0000000000..89a05b3021 --- /dev/null +++ b/vendor/github.com/docker/docker/client/network_inspect.go @@ -0,0 +1,49 @@ +package client // import "github.com/docker/docker/client" + +import ( + "bytes" + "context" + "encoding/json" + "io/ioutil" + "net/url" + + "github.com/docker/docker/api/types" +) + +// NetworkInspect returns the information for a specific network configured in the docker host. +func (cli *Client) NetworkInspect(ctx context.Context, networkID string, options types.NetworkInspectOptions) (types.NetworkResource, error) { + networkResource, _, err := cli.NetworkInspectWithRaw(ctx, networkID, options) + return networkResource, err +} + +// NetworkInspectWithRaw returns the information for a specific network configured in the docker host and its raw representation. +func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string, options types.NetworkInspectOptions) (types.NetworkResource, []byte, error) { + if networkID == "" { + return types.NetworkResource{}, nil, objectNotFoundError{object: "network", id: networkID} + } + var ( + networkResource types.NetworkResource + resp serverResponse + err error + ) + query := url.Values{} + if options.Verbose { + query.Set("verbose", "true") + } + if options.Scope != "" { + query.Set("scope", options.Scope) + } + resp, err = cli.get(ctx, "/networks/"+networkID, query, nil) + defer ensureReaderClosed(resp) + if err != nil { + return networkResource, nil, wrapResponseError(err, resp, "network", networkID) + } + + body, err := ioutil.ReadAll(resp.body) + if err != nil { + return networkResource, nil, err + } + rdr := bytes.NewReader(body) + err = json.NewDecoder(rdr).Decode(&networkResource) + return networkResource, body, err +} diff --git a/vendor/github.com/docker/docker/client/network_list.go b/vendor/github.com/docker/docker/client/network_list.go new file mode 100644 index 0000000000..ed2acb5571 --- /dev/null +++ b/vendor/github.com/docker/docker/client/network_list.go @@ -0,0 +1,32 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" +) + +// NetworkList returns the list of networks configured in the docker host. +func (cli *Client) NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) { + query := url.Values{} + if options.Filters.Len() > 0 { + //nolint:staticcheck // ignore SA1019 for old code + filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters) + if err != nil { + return nil, err + } + + query.Set("filters", filterJSON) + } + var networkResources []types.NetworkResource + resp, err := cli.get(ctx, "/networks", query, nil) + defer ensureReaderClosed(resp) + if err != nil { + return networkResources, err + } + err = json.NewDecoder(resp.body).Decode(&networkResources) + return networkResources, err +} diff --git a/vendor/github.com/docker/docker/client/network_prune.go b/vendor/github.com/docker/docker/client/network_prune.go new file mode 100644 index 0000000000..cebb188219 --- /dev/null +++ b/vendor/github.com/docker/docker/client/network_prune.go @@ -0,0 +1,36 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" +) + +// NetworksPrune requests the daemon to delete unused networks +func (cli *Client) NetworksPrune(ctx context.Context, pruneFilters filters.Args) (types.NetworksPruneReport, error) { + var report types.NetworksPruneReport + + if err := cli.NewVersionError("1.25", "network prune"); err != nil { + return report, err + } + + query, err := getFiltersQuery(pruneFilters) + if err != nil { + return report, err + } + + serverResp, err := cli.post(ctx, "/networks/prune", query, nil, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return report, err + } + + if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { + return report, fmt.Errorf("Error retrieving network prune report: %v", err) + } + + return report, nil +} diff --git a/vendor/github.com/docker/docker/client/network_remove.go b/vendor/github.com/docker/docker/client/network_remove.go new file mode 100644 index 0000000000..e71b16d869 --- /dev/null +++ b/vendor/github.com/docker/docker/client/network_remove.go @@ -0,0 +1,10 @@ +package client // import "github.com/docker/docker/client" + +import "context" + +// NetworkRemove removes an existent network from the docker host. +func (cli *Client) NetworkRemove(ctx context.Context, networkID string) error { + resp, err := cli.delete(ctx, "/networks/"+networkID, nil, nil) + defer ensureReaderClosed(resp) + return wrapResponseError(err, resp, "network", networkID) +} diff --git a/vendor/github.com/docker/docker/client/node_inspect.go b/vendor/github.com/docker/docker/client/node_inspect.go new file mode 100644 index 0000000000..d296c9fdde --- /dev/null +++ b/vendor/github.com/docker/docker/client/node_inspect.go @@ -0,0 +1,32 @@ +package client // import "github.com/docker/docker/client" + +import ( + "bytes" + "context" + "encoding/json" + "io/ioutil" + + "github.com/docker/docker/api/types/swarm" +) + +// NodeInspectWithRaw returns the node information. +func (cli *Client) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) { + if nodeID == "" { + return swarm.Node{}, nil, objectNotFoundError{object: "node", id: nodeID} + } + serverResp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return swarm.Node{}, nil, wrapResponseError(err, serverResp, "node", nodeID) + } + + body, err := ioutil.ReadAll(serverResp.body) + if err != nil { + return swarm.Node{}, nil, err + } + + var response swarm.Node + rdr := bytes.NewReader(body) + err = json.NewDecoder(rdr).Decode(&response) + return response, body, err +} diff --git a/vendor/github.com/docker/docker/client/node_list.go b/vendor/github.com/docker/docker/client/node_list.go new file mode 100644 index 0000000000..c212906bc7 --- /dev/null +++ b/vendor/github.com/docker/docker/client/node_list.go @@ -0,0 +1,36 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/swarm" +) + +// NodeList returns the list of nodes. +func (cli *Client) NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) { + query := url.Values{} + + if options.Filters.Len() > 0 { + filterJSON, err := filters.ToJSON(options.Filters) + + if err != nil { + return nil, err + } + + query.Set("filters", filterJSON) + } + + resp, err := cli.get(ctx, "/nodes", query, nil) + defer ensureReaderClosed(resp) + if err != nil { + return nil, err + } + + var nodes []swarm.Node + err = json.NewDecoder(resp.body).Decode(&nodes) + return nodes, err +} diff --git a/vendor/github.com/docker/docker/client/node_remove.go b/vendor/github.com/docker/docker/client/node_remove.go new file mode 100644 index 0000000000..03ab878097 --- /dev/null +++ b/vendor/github.com/docker/docker/client/node_remove.go @@ -0,0 +1,20 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + + "github.com/docker/docker/api/types" +) + +// NodeRemove removes a Node. +func (cli *Client) NodeRemove(ctx context.Context, nodeID string, options types.NodeRemoveOptions) error { + query := url.Values{} + if options.Force { + query.Set("force", "1") + } + + resp, err := cli.delete(ctx, "/nodes/"+nodeID, query, nil) + defer ensureReaderClosed(resp) + return wrapResponseError(err, resp, "node", nodeID) +} diff --git a/vendor/github.com/docker/docker/client/node_update.go b/vendor/github.com/docker/docker/client/node_update.go new file mode 100644 index 0000000000..de32a617fb --- /dev/null +++ b/vendor/github.com/docker/docker/client/node_update.go @@ -0,0 +1,18 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + "strconv" + + "github.com/docker/docker/api/types/swarm" +) + +// NodeUpdate updates a Node. +func (cli *Client) NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error { + query := url.Values{} + query.Set("version", strconv.FormatUint(version.Index, 10)) + resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, node, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/options.go b/vendor/github.com/docker/docker/client/options.go new file mode 100644 index 0000000000..6f77f0955f --- /dev/null +++ b/vendor/github.com/docker/docker/client/options.go @@ -0,0 +1,172 @@ +package client + +import ( + "context" + "net" + "net/http" + "os" + "path/filepath" + "time" + + "github.com/docker/go-connections/sockets" + "github.com/docker/go-connections/tlsconfig" + "github.com/pkg/errors" +) + +// Opt is a configuration option to initialize a client +type Opt func(*Client) error + +// FromEnv configures the client with values from environment variables. +// +// Supported environment variables: +// DOCKER_HOST to set the url to the docker server. +// DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest. +// DOCKER_CERT_PATH to load the TLS certificates from. +// DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default. +func FromEnv(c *Client) error { + if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" { + options := tlsconfig.Options{ + CAFile: filepath.Join(dockerCertPath, "ca.pem"), + CertFile: filepath.Join(dockerCertPath, "cert.pem"), + KeyFile: filepath.Join(dockerCertPath, "key.pem"), + InsecureSkipVerify: os.Getenv("DOCKER_TLS_VERIFY") == "", + } + tlsc, err := tlsconfig.Client(options) + if err != nil { + return err + } + + c.client = &http.Client{ + Transport: &http.Transport{TLSClientConfig: tlsc}, + CheckRedirect: CheckRedirect, + } + } + + if host := os.Getenv("DOCKER_HOST"); host != "" { + if err := WithHost(host)(c); err != nil { + return err + } + } + + if version := os.Getenv("DOCKER_API_VERSION"); version != "" { + if err := WithVersion(version)(c); err != nil { + return err + } + } + return nil +} + +// WithDialer applies the dialer.DialContext to the client transport. This can be +// used to set the Timeout and KeepAlive settings of the client. +// Deprecated: use WithDialContext +func WithDialer(dialer *net.Dialer) Opt { + return WithDialContext(dialer.DialContext) +} + +// WithDialContext applies the dialer to the client transport. This can be +// used to set the Timeout and KeepAlive settings of the client. +func WithDialContext(dialContext func(ctx context.Context, network, addr string) (net.Conn, error)) Opt { + return func(c *Client) error { + if transport, ok := c.client.Transport.(*http.Transport); ok { + transport.DialContext = dialContext + return nil + } + return errors.Errorf("cannot apply dialer to transport: %T", c.client.Transport) + } +} + +// WithHost overrides the client host with the specified one. +func WithHost(host string) Opt { + return func(c *Client) error { + hostURL, err := ParseHostURL(host) + if err != nil { + return err + } + c.host = host + c.proto = hostURL.Scheme + c.addr = hostURL.Host + c.basePath = hostURL.Path + if transport, ok := c.client.Transport.(*http.Transport); ok { + return sockets.ConfigureTransport(transport, c.proto, c.addr) + } + return errors.Errorf("cannot apply host to transport: %T", c.client.Transport) + } +} + +// WithHTTPClient overrides the client http client with the specified one +func WithHTTPClient(client *http.Client) Opt { + return func(c *Client) error { + if client != nil { + c.client = client + } + return nil + } +} + +// WithTimeout configures the time limit for requests made by the HTTP client +func WithTimeout(timeout time.Duration) Opt { + return func(c *Client) error { + c.client.Timeout = timeout + return nil + } +} + +// WithHTTPHeaders overrides the client default http headers +func WithHTTPHeaders(headers map[string]string) Opt { + return func(c *Client) error { + c.customHTTPHeaders = headers + return nil + } +} + +// WithScheme overrides the client scheme with the specified one +func WithScheme(scheme string) Opt { + return func(c *Client) error { + c.scheme = scheme + return nil + } +} + +// WithTLSClientConfig applies a tls config to the client transport. +func WithTLSClientConfig(cacertPath, certPath, keyPath string) Opt { + return func(c *Client) error { + opts := tlsconfig.Options{ + CAFile: cacertPath, + CertFile: certPath, + KeyFile: keyPath, + ExclusiveRootPools: true, + } + config, err := tlsconfig.Client(opts) + if err != nil { + return errors.Wrap(err, "failed to create tls config") + } + if transport, ok := c.client.Transport.(*http.Transport); ok { + transport.TLSClientConfig = config + return nil + } + return errors.Errorf("cannot apply tls config to transport: %T", c.client.Transport) + } +} + +// WithVersion overrides the client version with the specified one. If an empty +// version is specified, the value will be ignored to allow version negotiation. +func WithVersion(version string) Opt { + return func(c *Client) error { + if version != "" { + c.version = version + c.manualOverride = true + } + return nil + } +} + +// WithAPIVersionNegotiation enables automatic API version negotiation for the client. +// With this option enabled, the client automatically negotiates the API version +// to use when making requests. API version negotiation is performed on the first +// request; subsequent requests will not re-negotiate. +func WithAPIVersionNegotiation() Opt { + return func(c *Client) error { + c.negotiateVersion = true + return nil + } +} diff --git a/vendor/github.com/docker/docker/client/ping.go b/vendor/github.com/docker/docker/client/ping.go new file mode 100644 index 0000000000..a9af001ef4 --- /dev/null +++ b/vendor/github.com/docker/docker/client/ping.go @@ -0,0 +1,66 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/http" + "path" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" +) + +// Ping pings the server and returns the value of the "Docker-Experimental", +// "Builder-Version", "OS-Type" & "API-Version" headers. It attempts to use +// a HEAD request on the endpoint, but falls back to GET if HEAD is not supported +// by the daemon. +func (cli *Client) Ping(ctx context.Context) (types.Ping, error) { + var ping types.Ping + + // Using cli.buildRequest() + cli.doRequest() instead of cli.sendRequest() + // because ping requests are used during API version negotiation, so we want + // to hit the non-versioned /_ping endpoint, not /v1.xx/_ping + req, err := cli.buildRequest(http.MethodHead, path.Join(cli.basePath, "/_ping"), nil, nil) + if err != nil { + return ping, err + } + serverResp, err := cli.doRequest(ctx, req) + if err == nil { + defer ensureReaderClosed(serverResp) + switch serverResp.statusCode { + case http.StatusOK, http.StatusInternalServerError: + // Server handled the request, so parse the response + return parsePingResponse(cli, serverResp) + } + } else if IsErrConnectionFailed(err) { + return ping, err + } + + req, err = cli.buildRequest(http.MethodGet, path.Join(cli.basePath, "/_ping"), nil, nil) + if err != nil { + return ping, err + } + serverResp, err = cli.doRequest(ctx, req) + defer ensureReaderClosed(serverResp) + if err != nil { + return ping, err + } + return parsePingResponse(cli, serverResp) +} + +func parsePingResponse(cli *Client, resp serverResponse) (types.Ping, error) { + var ping types.Ping + if resp.header == nil { + err := cli.checkResponseErr(resp) + return ping, errdefs.FromStatusCode(err, resp.statusCode) + } + ping.APIVersion = resp.header.Get("API-Version") + ping.OSType = resp.header.Get("OSType") + if resp.header.Get("Docker-Experimental") == "true" { + ping.Experimental = true + } + if bv := resp.header.Get("Builder-Version"); bv != "" { + ping.BuilderVersion = types.BuilderVersion(bv) + } + err := cli.checkResponseErr(resp) + return ping, errdefs.FromStatusCode(err, resp.statusCode) +} diff --git a/vendor/github.com/docker/docker/client/plugin_create.go b/vendor/github.com/docker/docker/client/plugin_create.go new file mode 100644 index 0000000000..b95dbaf686 --- /dev/null +++ b/vendor/github.com/docker/docker/client/plugin_create.go @@ -0,0 +1,23 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "io" + "net/http" + "net/url" + + "github.com/docker/docker/api/types" +) + +// PluginCreate creates a plugin +func (cli *Client) PluginCreate(ctx context.Context, createContext io.Reader, createOptions types.PluginCreateOptions) error { + headers := http.Header(make(map[string][]string)) + headers.Set("Content-Type", "application/x-tar") + + query := url.Values{} + query.Set("name", createOptions.RepoName) + + resp, err := cli.postRaw(ctx, "/plugins/create", query, createContext, headers) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/plugin_disable.go b/vendor/github.com/docker/docker/client/plugin_disable.go new file mode 100644 index 0000000000..01f6574f95 --- /dev/null +++ b/vendor/github.com/docker/docker/client/plugin_disable.go @@ -0,0 +1,19 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + + "github.com/docker/docker/api/types" +) + +// PluginDisable disables a plugin +func (cli *Client) PluginDisable(ctx context.Context, name string, options types.PluginDisableOptions) error { + query := url.Values{} + if options.Force { + query.Set("force", "1") + } + resp, err := cli.post(ctx, "/plugins/"+name+"/disable", query, nil, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/plugin_enable.go b/vendor/github.com/docker/docker/client/plugin_enable.go new file mode 100644 index 0000000000..736da48bd1 --- /dev/null +++ b/vendor/github.com/docker/docker/client/plugin_enable.go @@ -0,0 +1,19 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + "strconv" + + "github.com/docker/docker/api/types" +) + +// PluginEnable enables a plugin +func (cli *Client) PluginEnable(ctx context.Context, name string, options types.PluginEnableOptions) error { + query := url.Values{} + query.Set("timeout", strconv.Itoa(options.Timeout)) + + resp, err := cli.post(ctx, "/plugins/"+name+"/enable", query, nil, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/plugin_inspect.go b/vendor/github.com/docker/docker/client/plugin_inspect.go new file mode 100644 index 0000000000..81b89732b0 --- /dev/null +++ b/vendor/github.com/docker/docker/client/plugin_inspect.go @@ -0,0 +1,31 @@ +package client // import "github.com/docker/docker/client" + +import ( + "bytes" + "context" + "encoding/json" + "io/ioutil" + + "github.com/docker/docker/api/types" +) + +// PluginInspectWithRaw inspects an existing plugin +func (cli *Client) PluginInspectWithRaw(ctx context.Context, name string) (*types.Plugin, []byte, error) { + if name == "" { + return nil, nil, objectNotFoundError{object: "plugin", id: name} + } + resp, err := cli.get(ctx, "/plugins/"+name+"/json", nil, nil) + defer ensureReaderClosed(resp) + if err != nil { + return nil, nil, wrapResponseError(err, resp, "plugin", name) + } + + body, err := ioutil.ReadAll(resp.body) + if err != nil { + return nil, nil, err + } + var p types.Plugin + rdr := bytes.NewReader(body) + err = json.NewDecoder(rdr).Decode(&p) + return &p, body, err +} diff --git a/vendor/github.com/docker/docker/client/plugin_install.go b/vendor/github.com/docker/docker/client/plugin_install.go new file mode 100644 index 0000000000..012afe61ca --- /dev/null +++ b/vendor/github.com/docker/docker/client/plugin_install.go @@ -0,0 +1,113 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "io" + "net/url" + + "github.com/docker/distribution/reference" + "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" + "github.com/pkg/errors" +) + +// PluginInstall installs a plugin +func (cli *Client) PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) { + query := url.Values{} + if _, err := reference.ParseNormalizedNamed(options.RemoteRef); err != nil { + return nil, errors.Wrap(err, "invalid remote reference") + } + query.Set("remote", options.RemoteRef) + + privileges, err := cli.checkPluginPermissions(ctx, query, options) + if err != nil { + return nil, err + } + + // set name for plugin pull, if empty should default to remote reference + query.Set("name", name) + + resp, err := cli.tryPluginPull(ctx, query, privileges, options.RegistryAuth) + if err != nil { + return nil, err + } + + name = resp.header.Get("Docker-Plugin-Name") + + pr, pw := io.Pipe() + go func() { // todo: the client should probably be designed more around the actual api + _, err := io.Copy(pw, resp.body) + if err != nil { + pw.CloseWithError(err) + return + } + defer func() { + if err != nil { + delResp, _ := cli.delete(ctx, "/plugins/"+name, nil, nil) + ensureReaderClosed(delResp) + } + }() + if len(options.Args) > 0 { + if err := cli.PluginSet(ctx, name, options.Args); err != nil { + pw.CloseWithError(err) + return + } + } + + if options.Disabled { + pw.Close() + return + } + + enableErr := cli.PluginEnable(ctx, name, types.PluginEnableOptions{Timeout: 0}) + pw.CloseWithError(enableErr) + }() + return pr, nil +} + +func (cli *Client) tryPluginPrivileges(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) { + headers := map[string][]string{"X-Registry-Auth": {registryAuth}} + return cli.get(ctx, "/plugins/privileges", query, headers) +} + +func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, privileges types.PluginPrivileges, registryAuth string) (serverResponse, error) { + headers := map[string][]string{"X-Registry-Auth": {registryAuth}} + return cli.post(ctx, "/plugins/pull", query, privileges, headers) +} + +func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values, options types.PluginInstallOptions) (types.PluginPrivileges, error) { + resp, err := cli.tryPluginPrivileges(ctx, query, options.RegistryAuth) + if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil { + // todo: do inspect before to check existing name before checking privileges + newAuthHeader, privilegeErr := options.PrivilegeFunc() + if privilegeErr != nil { + ensureReaderClosed(resp) + return nil, privilegeErr + } + options.RegistryAuth = newAuthHeader + resp, err = cli.tryPluginPrivileges(ctx, query, options.RegistryAuth) + } + if err != nil { + ensureReaderClosed(resp) + return nil, err + } + + var privileges types.PluginPrivileges + if err := json.NewDecoder(resp.body).Decode(&privileges); err != nil { + ensureReaderClosed(resp) + return nil, err + } + ensureReaderClosed(resp) + + if !options.AcceptAllPermissions && options.AcceptPermissionsFunc != nil && len(privileges) > 0 { + accept, err := options.AcceptPermissionsFunc(privileges) + if err != nil { + return nil, err + } + if !accept { + return nil, pluginPermissionDenied{options.RemoteRef} + } + } + return privileges, nil +} diff --git a/vendor/github.com/docker/docker/client/plugin_list.go b/vendor/github.com/docker/docker/client/plugin_list.go new file mode 100644 index 0000000000..cf1935e2f5 --- /dev/null +++ b/vendor/github.com/docker/docker/client/plugin_list.go @@ -0,0 +1,33 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" +) + +// PluginList returns the installed plugins +func (cli *Client) PluginList(ctx context.Context, filter filters.Args) (types.PluginsListResponse, error) { + var plugins types.PluginsListResponse + query := url.Values{} + + if filter.Len() > 0 { + //nolint:staticcheck // ignore SA1019 for old code + filterJSON, err := filters.ToParamWithVersion(cli.version, filter) + if err != nil { + return plugins, err + } + query.Set("filters", filterJSON) + } + resp, err := cli.get(ctx, "/plugins", query, nil) + defer ensureReaderClosed(resp) + if err != nil { + return plugins, wrapResponseError(err, resp, "plugin", "") + } + + err = json.NewDecoder(resp.body).Decode(&plugins) + return plugins, err +} diff --git a/vendor/github.com/docker/docker/client/plugin_push.go b/vendor/github.com/docker/docker/client/plugin_push.go new file mode 100644 index 0000000000..d20bfe8447 --- /dev/null +++ b/vendor/github.com/docker/docker/client/plugin_push.go @@ -0,0 +1,16 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "io" +) + +// PluginPush pushes a plugin to a registry +func (cli *Client) PluginPush(ctx context.Context, name string, registryAuth string) (io.ReadCloser, error) { + headers := map[string][]string{"X-Registry-Auth": {registryAuth}} + resp, err := cli.post(ctx, "/plugins/"+name+"/push", nil, nil, headers) + if err != nil { + return nil, err + } + return resp.body, nil +} diff --git a/vendor/github.com/docker/docker/client/plugin_remove.go b/vendor/github.com/docker/docker/client/plugin_remove.go new file mode 100644 index 0000000000..51ca1040d6 --- /dev/null +++ b/vendor/github.com/docker/docker/client/plugin_remove.go @@ -0,0 +1,20 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + + "github.com/docker/docker/api/types" +) + +// PluginRemove removes a plugin +func (cli *Client) PluginRemove(ctx context.Context, name string, options types.PluginRemoveOptions) error { + query := url.Values{} + if options.Force { + query.Set("force", "1") + } + + resp, err := cli.delete(ctx, "/plugins/"+name, query, nil) + defer ensureReaderClosed(resp) + return wrapResponseError(err, resp, "plugin", name) +} diff --git a/vendor/github.com/docker/docker/client/plugin_set.go b/vendor/github.com/docker/docker/client/plugin_set.go new file mode 100644 index 0000000000..dcf5752ca2 --- /dev/null +++ b/vendor/github.com/docker/docker/client/plugin_set.go @@ -0,0 +1,12 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" +) + +// PluginSet modifies settings for an existing plugin +func (cli *Client) PluginSet(ctx context.Context, name string, args []string) error { + resp, err := cli.post(ctx, "/plugins/"+name+"/set", nil, args, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/plugin_upgrade.go b/vendor/github.com/docker/docker/client/plugin_upgrade.go new file mode 100644 index 0000000000..115cea945b --- /dev/null +++ b/vendor/github.com/docker/docker/client/plugin_upgrade.go @@ -0,0 +1,39 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "io" + "net/url" + + "github.com/docker/distribution/reference" + "github.com/docker/docker/api/types" + "github.com/pkg/errors" +) + +// PluginUpgrade upgrades a plugin +func (cli *Client) PluginUpgrade(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) { + if err := cli.NewVersionError("1.26", "plugin upgrade"); err != nil { + return nil, err + } + query := url.Values{} + if _, err := reference.ParseNormalizedNamed(options.RemoteRef); err != nil { + return nil, errors.Wrap(err, "invalid remote reference") + } + query.Set("remote", options.RemoteRef) + + privileges, err := cli.checkPluginPermissions(ctx, query, options) + if err != nil { + return nil, err + } + + resp, err := cli.tryPluginUpgrade(ctx, query, privileges, name, options.RegistryAuth) + if err != nil { + return nil, err + } + return resp.body, nil +} + +func (cli *Client) tryPluginUpgrade(ctx context.Context, query url.Values, privileges types.PluginPrivileges, name, registryAuth string) (serverResponse, error) { + headers := map[string][]string{"X-Registry-Auth": {registryAuth}} + return cli.post(ctx, "/plugins/"+name+"/upgrade", query, privileges, headers) +} diff --git a/vendor/github.com/docker/docker/client/request.go b/vendor/github.com/docker/docker/client/request.go new file mode 100644 index 0000000000..813eac2c9e --- /dev/null +++ b/vendor/github.com/docker/docker/client/request.go @@ -0,0 +1,269 @@ +package client // import "github.com/docker/docker/client" + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "net/url" + "os" + "strings" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/versions" + "github.com/docker/docker/errdefs" + "github.com/pkg/errors" +) + +// serverResponse is a wrapper for http API responses. +type serverResponse struct { + body io.ReadCloser + header http.Header + statusCode int + reqURL *url.URL +} + +// head sends an http request to the docker API using the method HEAD. +func (cli *Client) head(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) { + return cli.sendRequest(ctx, http.MethodHead, path, query, nil, headers) +} + +// get sends an http request to the docker API using the method GET with a specific Go context. +func (cli *Client) get(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) { + return cli.sendRequest(ctx, http.MethodGet, path, query, nil, headers) +} + +// post sends an http request to the docker API using the method POST with a specific Go context. +func (cli *Client) post(ctx context.Context, path string, query url.Values, obj interface{}, headers map[string][]string) (serverResponse, error) { + body, headers, err := encodeBody(obj, headers) + if err != nil { + return serverResponse{}, err + } + return cli.sendRequest(ctx, http.MethodPost, path, query, body, headers) +} + +func (cli *Client) postRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) { + return cli.sendRequest(ctx, http.MethodPost, path, query, body, headers) +} + +// putRaw sends an http request to the docker API using the method PUT. +func (cli *Client) putRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) { + return cli.sendRequest(ctx, http.MethodPut, path, query, body, headers) +} + +// delete sends an http request to the docker API using the method DELETE. +func (cli *Client) delete(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) { + return cli.sendRequest(ctx, http.MethodDelete, path, query, nil, headers) +} + +type headers map[string][]string + +func encodeBody(obj interface{}, headers headers) (io.Reader, headers, error) { + if obj == nil { + return nil, headers, nil + } + + body, err := encodeData(obj) + if err != nil { + return nil, headers, err + } + if headers == nil { + headers = make(map[string][]string) + } + headers["Content-Type"] = []string{"application/json"} + return body, headers, nil +} + +func (cli *Client) buildRequest(method, path string, body io.Reader, headers headers) (*http.Request, error) { + expectedPayload := (method == http.MethodPost || method == http.MethodPut) + if expectedPayload && body == nil { + body = bytes.NewReader([]byte{}) + } + + req, err := http.NewRequest(method, path, body) + if err != nil { + return nil, err + } + req = cli.addHeaders(req, headers) + + if cli.proto == "unix" || cli.proto == "npipe" { + // For local communications, it doesn't matter what the host is. We just + // need a valid and meaningful host name. (See #189) + req.Host = "docker" + } + + req.URL.Host = cli.addr + req.URL.Scheme = cli.scheme + + if expectedPayload && req.Header.Get("Content-Type") == "" { + req.Header.Set("Content-Type", "text/plain") + } + return req, nil +} + +func (cli *Client) sendRequest(ctx context.Context, method, path string, query url.Values, body io.Reader, headers headers) (serverResponse, error) { + req, err := cli.buildRequest(method, cli.getAPIPath(ctx, path, query), body, headers) + if err != nil { + return serverResponse{}, err + } + resp, err := cli.doRequest(ctx, req) + if err != nil { + return resp, errdefs.FromStatusCode(err, resp.statusCode) + } + err = cli.checkResponseErr(resp) + return resp, errdefs.FromStatusCode(err, resp.statusCode) +} + +func (cli *Client) doRequest(ctx context.Context, req *http.Request) (serverResponse, error) { + serverResp := serverResponse{statusCode: -1, reqURL: req.URL} + + req = req.WithContext(ctx) + resp, err := cli.client.Do(req) + if err != nil { + if cli.scheme != "https" && strings.Contains(err.Error(), "malformed HTTP response") { + return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err) + } + + if cli.scheme == "https" && strings.Contains(err.Error(), "bad certificate") { + return serverResp, errors.Wrap(err, "The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings") + } + + // Don't decorate context sentinel errors; users may be comparing to + // them directly. + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + return serverResp, err + } + + if nErr, ok := err.(*url.Error); ok { + if nErr, ok := nErr.Err.(*net.OpError); ok { + if os.IsPermission(nErr.Err) { + return serverResp, errors.Wrapf(err, "Got permission denied while trying to connect to the Docker daemon socket at %v", cli.host) + } + } + } + + if err, ok := err.(net.Error); ok { + if err.Timeout() { + return serverResp, ErrorConnectionFailed(cli.host) + } + if !err.Temporary() { + if strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "dial unix") { + return serverResp, ErrorConnectionFailed(cli.host) + } + } + } + + // Although there's not a strongly typed error for this in go-winio, + // lots of people are using the default configuration for the docker + // daemon on Windows where the daemon is listening on a named pipe + // `//./pipe/docker_engine, and the client must be running elevated. + // Give users a clue rather than the not-overly useful message + // such as `error during connect: Get http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.26/info: + // open //./pipe/docker_engine: The system cannot find the file specified.`. + // Note we can't string compare "The system cannot find the file specified" as + // this is localised - for example in French the error would be + // `open //./pipe/docker_engine: Le fichier spécifié est introuvable.` + if strings.Contains(err.Error(), `open //./pipe/docker_engine`) { + // Checks if client is running with elevated privileges + if f, elevatedErr := os.Open("\\\\.\\PHYSICALDRIVE0"); elevatedErr == nil { + err = errors.Wrap(err, "In the default daemon configuration on Windows, the docker client must be run with elevated privileges to connect.") + } else { + f.Close() + err = errors.Wrap(err, "This error may indicate that the docker daemon is not running.") + } + } + + return serverResp, errors.Wrap(err, "error during connect") + } + + if resp != nil { + serverResp.statusCode = resp.StatusCode + serverResp.body = resp.Body + serverResp.header = resp.Header + } + return serverResp, nil +} + +func (cli *Client) checkResponseErr(serverResp serverResponse) error { + if serverResp.statusCode >= 200 && serverResp.statusCode < 400 { + return nil + } + + var body []byte + var err error + if serverResp.body != nil { + bodyMax := 1 * 1024 * 1024 // 1 MiB + bodyR := &io.LimitedReader{ + R: serverResp.body, + N: int64(bodyMax), + } + body, err = ioutil.ReadAll(bodyR) + if err != nil { + return err + } + if bodyR.N == 0 { + return fmt.Errorf("request returned %s with a message (> %d bytes) for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), bodyMax, serverResp.reqURL) + } + } + if len(body) == 0 { + return fmt.Errorf("request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), serverResp.reqURL) + } + + var ct string + if serverResp.header != nil { + ct = serverResp.header.Get("Content-Type") + } + + var errorMessage string + if (cli.version == "" || versions.GreaterThan(cli.version, "1.23")) && ct == "application/json" { + var errorResponse types.ErrorResponse + if err := json.Unmarshal(body, &errorResponse); err != nil { + return errors.Wrap(err, "Error reading JSON") + } + errorMessage = strings.TrimSpace(errorResponse.Message) + } else { + errorMessage = strings.TrimSpace(string(body)) + } + + return errors.Wrap(errors.New(errorMessage), "Error response from daemon") +} + +func (cli *Client) addHeaders(req *http.Request, headers headers) *http.Request { + // Add CLI Config's HTTP Headers BEFORE we set the Docker headers + // then the user can't change OUR headers + for k, v := range cli.customHTTPHeaders { + if versions.LessThan(cli.version, "1.25") && k == "User-Agent" { + continue + } + req.Header.Set(k, v) + } + + if headers != nil { + for k, v := range headers { + req.Header[k] = v + } + } + return req +} + +func encodeData(data interface{}) (*bytes.Buffer, error) { + params := bytes.NewBuffer(nil) + if data != nil { + if err := json.NewEncoder(params).Encode(data); err != nil { + return nil, err + } + } + return params, nil +} + +func ensureReaderClosed(response serverResponse) { + if response.body != nil { + // Drain up to 512 bytes and close the body to let the Transport reuse the connection + io.CopyN(ioutil.Discard, response.body, 512) + response.body.Close() + } +} diff --git a/vendor/github.com/docker/docker/client/secret_create.go b/vendor/github.com/docker/docker/client/secret_create.go new file mode 100644 index 0000000000..fd5b914136 --- /dev/null +++ b/vendor/github.com/docker/docker/client/secret_create.go @@ -0,0 +1,25 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/swarm" +) + +// SecretCreate creates a new Secret. +func (cli *Client) SecretCreate(ctx context.Context, secret swarm.SecretSpec) (types.SecretCreateResponse, error) { + var response types.SecretCreateResponse + if err := cli.NewVersionError("1.25", "secret create"); err != nil { + return response, err + } + resp, err := cli.post(ctx, "/secrets/create", nil, secret, nil) + defer ensureReaderClosed(resp) + if err != nil { + return response, err + } + + err = json.NewDecoder(resp.body).Decode(&response) + return response, err +} diff --git a/vendor/github.com/docker/docker/client/secret_inspect.go b/vendor/github.com/docker/docker/client/secret_inspect.go new file mode 100644 index 0000000000..d093916c9a --- /dev/null +++ b/vendor/github.com/docker/docker/client/secret_inspect.go @@ -0,0 +1,36 @@ +package client // import "github.com/docker/docker/client" + +import ( + "bytes" + "context" + "encoding/json" + "io/ioutil" + + "github.com/docker/docker/api/types/swarm" +) + +// SecretInspectWithRaw returns the secret information with raw data +func (cli *Client) SecretInspectWithRaw(ctx context.Context, id string) (swarm.Secret, []byte, error) { + if err := cli.NewVersionError("1.25", "secret inspect"); err != nil { + return swarm.Secret{}, nil, err + } + if id == "" { + return swarm.Secret{}, nil, objectNotFoundError{object: "secret", id: id} + } + resp, err := cli.get(ctx, "/secrets/"+id, nil, nil) + defer ensureReaderClosed(resp) + if err != nil { + return swarm.Secret{}, nil, wrapResponseError(err, resp, "secret", id) + } + + body, err := ioutil.ReadAll(resp.body) + if err != nil { + return swarm.Secret{}, nil, err + } + + var secret swarm.Secret + rdr := bytes.NewReader(body) + err = json.NewDecoder(rdr).Decode(&secret) + + return secret, body, err +} diff --git a/vendor/github.com/docker/docker/client/secret_list.go b/vendor/github.com/docker/docker/client/secret_list.go new file mode 100644 index 0000000000..a0289c9f44 --- /dev/null +++ b/vendor/github.com/docker/docker/client/secret_list.go @@ -0,0 +1,38 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/swarm" +) + +// SecretList returns the list of secrets. +func (cli *Client) SecretList(ctx context.Context, options types.SecretListOptions) ([]swarm.Secret, error) { + if err := cli.NewVersionError("1.25", "secret list"); err != nil { + return nil, err + } + query := url.Values{} + + if options.Filters.Len() > 0 { + filterJSON, err := filters.ToJSON(options.Filters) + if err != nil { + return nil, err + } + + query.Set("filters", filterJSON) + } + + resp, err := cli.get(ctx, "/secrets", query, nil) + defer ensureReaderClosed(resp) + if err != nil { + return nil, err + } + + var secrets []swarm.Secret + err = json.NewDecoder(resp.body).Decode(&secrets) + return secrets, err +} diff --git a/vendor/github.com/docker/docker/client/secret_remove.go b/vendor/github.com/docker/docker/client/secret_remove.go new file mode 100644 index 0000000000..c16f555804 --- /dev/null +++ b/vendor/github.com/docker/docker/client/secret_remove.go @@ -0,0 +1,13 @@ +package client // import "github.com/docker/docker/client" + +import "context" + +// SecretRemove removes a Secret. +func (cli *Client) SecretRemove(ctx context.Context, id string) error { + if err := cli.NewVersionError("1.25", "secret remove"); err != nil { + return err + } + resp, err := cli.delete(ctx, "/secrets/"+id, nil, nil) + defer ensureReaderClosed(resp) + return wrapResponseError(err, resp, "secret", id) +} diff --git a/vendor/github.com/docker/docker/client/secret_update.go b/vendor/github.com/docker/docker/client/secret_update.go new file mode 100644 index 0000000000..164256bbc1 --- /dev/null +++ b/vendor/github.com/docker/docker/client/secret_update.go @@ -0,0 +1,21 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + "strconv" + + "github.com/docker/docker/api/types/swarm" +) + +// SecretUpdate attempts to update a Secret +func (cli *Client) SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error { + if err := cli.NewVersionError("1.25", "secret update"); err != nil { + return err + } + query := url.Values{} + query.Set("version", strconv.FormatUint(version.Index, 10)) + resp, err := cli.post(ctx, "/secrets/"+id+"/update", query, secret, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/service_create.go b/vendor/github.com/docker/docker/client/service_create.go new file mode 100644 index 0000000000..e0428bf98b --- /dev/null +++ b/vendor/github.com/docker/docker/client/service_create.go @@ -0,0 +1,178 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "fmt" + "strings" + + "github.com/docker/distribution/reference" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/swarm" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" +) + +// ServiceCreate creates a new Service. +func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options types.ServiceCreateOptions) (types.ServiceCreateResponse, error) { + var response types.ServiceCreateResponse + headers := map[string][]string{ + "version": {cli.version}, + } + + if options.EncodedRegistryAuth != "" { + headers["X-Registry-Auth"] = []string{options.EncodedRegistryAuth} + } + + // Make sure containerSpec is not nil when no runtime is set or the runtime is set to container + if service.TaskTemplate.ContainerSpec == nil && (service.TaskTemplate.Runtime == "" || service.TaskTemplate.Runtime == swarm.RuntimeContainer) { + service.TaskTemplate.ContainerSpec = &swarm.ContainerSpec{} + } + + if err := validateServiceSpec(service); err != nil { + return response, err + } + + // ensure that the image is tagged + var resolveWarning string + switch { + case service.TaskTemplate.ContainerSpec != nil: + if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" { + service.TaskTemplate.ContainerSpec.Image = taggedImg + } + if options.QueryRegistry { + resolveWarning = resolveContainerSpecImage(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth) + } + case service.TaskTemplate.PluginSpec != nil: + if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" { + service.TaskTemplate.PluginSpec.Remote = taggedImg + } + if options.QueryRegistry { + resolveWarning = resolvePluginSpecRemote(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth) + } + } + + resp, err := cli.post(ctx, "/services/create", nil, service, headers) + defer ensureReaderClosed(resp) + if err != nil { + return response, err + } + + err = json.NewDecoder(resp.body).Decode(&response) + if resolveWarning != "" { + response.Warnings = append(response.Warnings, resolveWarning) + } + + return response, err +} + +func resolveContainerSpecImage(ctx context.Context, cli DistributionAPIClient, taskSpec *swarm.TaskSpec, encodedAuth string) string { + var warning string + if img, imgPlatforms, err := imageDigestAndPlatforms(ctx, cli, taskSpec.ContainerSpec.Image, encodedAuth); err != nil { + warning = digestWarning(taskSpec.ContainerSpec.Image) + } else { + taskSpec.ContainerSpec.Image = img + if len(imgPlatforms) > 0 { + if taskSpec.Placement == nil { + taskSpec.Placement = &swarm.Placement{} + } + taskSpec.Placement.Platforms = imgPlatforms + } + } + return warning +} + +func resolvePluginSpecRemote(ctx context.Context, cli DistributionAPIClient, taskSpec *swarm.TaskSpec, encodedAuth string) string { + var warning string + if img, imgPlatforms, err := imageDigestAndPlatforms(ctx, cli, taskSpec.PluginSpec.Remote, encodedAuth); err != nil { + warning = digestWarning(taskSpec.PluginSpec.Remote) + } else { + taskSpec.PluginSpec.Remote = img + if len(imgPlatforms) > 0 { + if taskSpec.Placement == nil { + taskSpec.Placement = &swarm.Placement{} + } + taskSpec.Placement.Platforms = imgPlatforms + } + } + return warning +} + +func imageDigestAndPlatforms(ctx context.Context, cli DistributionAPIClient, image, encodedAuth string) (string, []swarm.Platform, error) { + distributionInspect, err := cli.DistributionInspect(ctx, image, encodedAuth) + var platforms []swarm.Platform + if err != nil { + return "", nil, err + } + + imageWithDigest := imageWithDigestString(image, distributionInspect.Descriptor.Digest) + + if len(distributionInspect.Platforms) > 0 { + platforms = make([]swarm.Platform, 0, len(distributionInspect.Platforms)) + for _, p := range distributionInspect.Platforms { + // clear architecture field for arm. This is a temporary patch to address + // https://github.com/docker/swarmkit/issues/2294. The issue is that while + // image manifests report "arm" as the architecture, the node reports + // something like "armv7l" (includes the variant), which causes arm images + // to stop working with swarm mode. This patch removes the architecture + // constraint for arm images to ensure tasks get scheduled. + arch := p.Architecture + if strings.ToLower(arch) == "arm" { + arch = "" + } + platforms = append(platforms, swarm.Platform{ + Architecture: arch, + OS: p.OS, + }) + } + } + return imageWithDigest, platforms, err +} + +// imageWithDigestString takes an image string and a digest, and updates +// the image string if it didn't originally contain a digest. It returns +// image unmodified in other situations. +func imageWithDigestString(image string, dgst digest.Digest) string { + namedRef, err := reference.ParseNormalizedNamed(image) + if err == nil { + if _, isCanonical := namedRef.(reference.Canonical); !isCanonical { + // ensure that image gets a default tag if none is provided + img, err := reference.WithDigest(namedRef, dgst) + if err == nil { + return reference.FamiliarString(img) + } + } + } + return image +} + +// imageWithTagString takes an image string, and returns a tagged image +// string, adding a 'latest' tag if one was not provided. It returns an +// empty string if a canonical reference was provided +func imageWithTagString(image string) string { + namedRef, err := reference.ParseNormalizedNamed(image) + if err == nil { + return reference.FamiliarString(reference.TagNameOnly(namedRef)) + } + return "" +} + +// digestWarning constructs a formatted warning string using the +// image name that could not be pinned by digest. The formatting +// is hardcoded, but could me made smarter in the future +func digestWarning(image string) string { + return fmt.Sprintf("image %s could not be accessed on a registry to record\nits digest. Each node will access %s independently,\npossibly leading to different nodes running different\nversions of the image.\n", image, image) +} + +func validateServiceSpec(s swarm.ServiceSpec) error { + if s.TaskTemplate.ContainerSpec != nil && s.TaskTemplate.PluginSpec != nil { + return errors.New("must not specify both a container spec and a plugin spec in the task template") + } + if s.TaskTemplate.PluginSpec != nil && s.TaskTemplate.Runtime != swarm.RuntimePlugin { + return errors.New("mismatched runtime with plugin spec") + } + if s.TaskTemplate.ContainerSpec != nil && (s.TaskTemplate.Runtime != "" && s.TaskTemplate.Runtime != swarm.RuntimeContainer) { + return errors.New("mismatched runtime with container spec") + } + return nil +} diff --git a/vendor/github.com/docker/docker/client/service_inspect.go b/vendor/github.com/docker/docker/client/service_inspect.go new file mode 100644 index 0000000000..2801483b80 --- /dev/null +++ b/vendor/github.com/docker/docker/client/service_inspect.go @@ -0,0 +1,37 @@ +package client // import "github.com/docker/docker/client" + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/url" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/swarm" +) + +// ServiceInspectWithRaw returns the service information and the raw data. +func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string, opts types.ServiceInspectOptions) (swarm.Service, []byte, error) { + if serviceID == "" { + return swarm.Service{}, nil, objectNotFoundError{object: "service", id: serviceID} + } + query := url.Values{} + query.Set("insertDefaults", fmt.Sprintf("%v", opts.InsertDefaults)) + serverResp, err := cli.get(ctx, "/services/"+serviceID, query, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return swarm.Service{}, nil, wrapResponseError(err, serverResp, "service", serviceID) + } + + body, err := ioutil.ReadAll(serverResp.body) + if err != nil { + return swarm.Service{}, nil, err + } + + var response swarm.Service + rdr := bytes.NewReader(body) + err = json.NewDecoder(rdr).Decode(&response) + return response, body, err +} diff --git a/vendor/github.com/docker/docker/client/service_list.go b/vendor/github.com/docker/docker/client/service_list.go new file mode 100644 index 0000000000..f97ec75a5c --- /dev/null +++ b/vendor/github.com/docker/docker/client/service_list.go @@ -0,0 +1,39 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/swarm" +) + +// ServiceList returns the list of services. +func (cli *Client) ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) { + query := url.Values{} + + if options.Filters.Len() > 0 { + filterJSON, err := filters.ToJSON(options.Filters) + if err != nil { + return nil, err + } + + query.Set("filters", filterJSON) + } + + if options.Status { + query.Set("status", "true") + } + + resp, err := cli.get(ctx, "/services", query, nil) + defer ensureReaderClosed(resp) + if err != nil { + return nil, err + } + + var services []swarm.Service + err = json.NewDecoder(resp.body).Decode(&services) + return services, err +} diff --git a/vendor/github.com/docker/docker/client/service_logs.go b/vendor/github.com/docker/docker/client/service_logs.go new file mode 100644 index 0000000000..906fd4059e --- /dev/null +++ b/vendor/github.com/docker/docker/client/service_logs.go @@ -0,0 +1,52 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "io" + "net/url" + "time" + + "github.com/docker/docker/api/types" + timetypes "github.com/docker/docker/api/types/time" + "github.com/pkg/errors" +) + +// ServiceLogs returns the logs generated by a service in an io.ReadCloser. +// It's up to the caller to close the stream. +func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options types.ContainerLogsOptions) (io.ReadCloser, error) { + query := url.Values{} + if options.ShowStdout { + query.Set("stdout", "1") + } + + if options.ShowStderr { + query.Set("stderr", "1") + } + + if options.Since != "" { + ts, err := timetypes.GetTimestamp(options.Since, time.Now()) + if err != nil { + return nil, errors.Wrap(err, `invalid value for "since"`) + } + query.Set("since", ts) + } + + if options.Timestamps { + query.Set("timestamps", "1") + } + + if options.Details { + query.Set("details", "1") + } + + if options.Follow { + query.Set("follow", "1") + } + query.Set("tail", options.Tail) + + resp, err := cli.get(ctx, "/services/"+serviceID+"/logs", query, nil) + if err != nil { + return nil, err + } + return resp.body, nil +} diff --git a/vendor/github.com/docker/docker/client/service_remove.go b/vendor/github.com/docker/docker/client/service_remove.go new file mode 100644 index 0000000000..953a2adf5a --- /dev/null +++ b/vendor/github.com/docker/docker/client/service_remove.go @@ -0,0 +1,10 @@ +package client // import "github.com/docker/docker/client" + +import "context" + +// ServiceRemove kills and removes a service. +func (cli *Client) ServiceRemove(ctx context.Context, serviceID string) error { + resp, err := cli.delete(ctx, "/services/"+serviceID, nil, nil) + defer ensureReaderClosed(resp) + return wrapResponseError(err, resp, "service", serviceID) +} diff --git a/vendor/github.com/docker/docker/client/service_update.go b/vendor/github.com/docker/docker/client/service_update.go new file mode 100644 index 0000000000..c63895f74f --- /dev/null +++ b/vendor/github.com/docker/docker/client/service_update.go @@ -0,0 +1,75 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + "strconv" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/swarm" +) + +// ServiceUpdate updates a Service. The version number is required to avoid conflicting writes. +// It should be the value as set *before* the update. You can find this value in the Meta field +// of swarm.Service, which can be found using ServiceInspectWithRaw. +func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) { + var ( + query = url.Values{} + response = types.ServiceUpdateResponse{} + ) + + headers := map[string][]string{ + "version": {cli.version}, + } + + if options.EncodedRegistryAuth != "" { + headers["X-Registry-Auth"] = []string{options.EncodedRegistryAuth} + } + + if options.RegistryAuthFrom != "" { + query.Set("registryAuthFrom", options.RegistryAuthFrom) + } + + if options.Rollback != "" { + query.Set("rollback", options.Rollback) + } + + query.Set("version", strconv.FormatUint(version.Index, 10)) + + if err := validateServiceSpec(service); err != nil { + return response, err + } + + // ensure that the image is tagged + var resolveWarning string + switch { + case service.TaskTemplate.ContainerSpec != nil: + if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" { + service.TaskTemplate.ContainerSpec.Image = taggedImg + } + if options.QueryRegistry { + resolveWarning = resolveContainerSpecImage(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth) + } + case service.TaskTemplate.PluginSpec != nil: + if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" { + service.TaskTemplate.PluginSpec.Remote = taggedImg + } + if options.QueryRegistry { + resolveWarning = resolvePluginSpecRemote(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth) + } + } + + resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, headers) + defer ensureReaderClosed(resp) + if err != nil { + return response, err + } + + err = json.NewDecoder(resp.body).Decode(&response) + if resolveWarning != "" { + response.Warnings = append(response.Warnings, resolveWarning) + } + + return response, err +} diff --git a/vendor/github.com/docker/docker/client/swarm_get_unlock_key.go b/vendor/github.com/docker/docker/client/swarm_get_unlock_key.go new file mode 100644 index 0000000000..19f59dd582 --- /dev/null +++ b/vendor/github.com/docker/docker/client/swarm_get_unlock_key.go @@ -0,0 +1,21 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + + "github.com/docker/docker/api/types" +) + +// SwarmGetUnlockKey retrieves the swarm's unlock key. +func (cli *Client) SwarmGetUnlockKey(ctx context.Context) (types.SwarmUnlockKeyResponse, error) { + serverResp, err := cli.get(ctx, "/swarm/unlockkey", nil, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return types.SwarmUnlockKeyResponse{}, err + } + + var response types.SwarmUnlockKeyResponse + err = json.NewDecoder(serverResp.body).Decode(&response) + return response, err +} diff --git a/vendor/github.com/docker/docker/client/swarm_init.go b/vendor/github.com/docker/docker/client/swarm_init.go new file mode 100644 index 0000000000..da3c1637ef --- /dev/null +++ b/vendor/github.com/docker/docker/client/swarm_init.go @@ -0,0 +1,21 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + + "github.com/docker/docker/api/types/swarm" +) + +// SwarmInit initializes the swarm. +func (cli *Client) SwarmInit(ctx context.Context, req swarm.InitRequest) (string, error) { + serverResp, err := cli.post(ctx, "/swarm/init", nil, req, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return "", err + } + + var response string + err = json.NewDecoder(serverResp.body).Decode(&response) + return response, err +} diff --git a/vendor/github.com/docker/docker/client/swarm_inspect.go b/vendor/github.com/docker/docker/client/swarm_inspect.go new file mode 100644 index 0000000000..b52b67a884 --- /dev/null +++ b/vendor/github.com/docker/docker/client/swarm_inspect.go @@ -0,0 +1,21 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + + "github.com/docker/docker/api/types/swarm" +) + +// SwarmInspect inspects the swarm. +func (cli *Client) SwarmInspect(ctx context.Context) (swarm.Swarm, error) { + serverResp, err := cli.get(ctx, "/swarm", nil, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return swarm.Swarm{}, err + } + + var response swarm.Swarm + err = json.NewDecoder(serverResp.body).Decode(&response) + return response, err +} diff --git a/vendor/github.com/docker/docker/client/swarm_join.go b/vendor/github.com/docker/docker/client/swarm_join.go new file mode 100644 index 0000000000..a1cf0455d2 --- /dev/null +++ b/vendor/github.com/docker/docker/client/swarm_join.go @@ -0,0 +1,14 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + + "github.com/docker/docker/api/types/swarm" +) + +// SwarmJoin joins the swarm. +func (cli *Client) SwarmJoin(ctx context.Context, req swarm.JoinRequest) error { + resp, err := cli.post(ctx, "/swarm/join", nil, req, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/swarm_leave.go b/vendor/github.com/docker/docker/client/swarm_leave.go new file mode 100644 index 0000000000..90ca84b363 --- /dev/null +++ b/vendor/github.com/docker/docker/client/swarm_leave.go @@ -0,0 +1,17 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" +) + +// SwarmLeave leaves the swarm. +func (cli *Client) SwarmLeave(ctx context.Context, force bool) error { + query := url.Values{} + if force { + query.Set("force", "1") + } + resp, err := cli.post(ctx, "/swarm/leave", query, nil, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/swarm_unlock.go b/vendor/github.com/docker/docker/client/swarm_unlock.go new file mode 100644 index 0000000000..d2412f7d44 --- /dev/null +++ b/vendor/github.com/docker/docker/client/swarm_unlock.go @@ -0,0 +1,14 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + + "github.com/docker/docker/api/types/swarm" +) + +// SwarmUnlock unlocks locked swarm. +func (cli *Client) SwarmUnlock(ctx context.Context, req swarm.UnlockRequest) error { + serverResp, err := cli.post(ctx, "/swarm/unlock", nil, req, nil) + ensureReaderClosed(serverResp) + return err +} diff --git a/vendor/github.com/docker/docker/client/swarm_update.go b/vendor/github.com/docker/docker/client/swarm_update.go new file mode 100644 index 0000000000..56a5bea761 --- /dev/null +++ b/vendor/github.com/docker/docker/client/swarm_update.go @@ -0,0 +1,22 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "fmt" + "net/url" + "strconv" + + "github.com/docker/docker/api/types/swarm" +) + +// SwarmUpdate updates the swarm. +func (cli *Client) SwarmUpdate(ctx context.Context, version swarm.Version, swarm swarm.Spec, flags swarm.UpdateFlags) error { + query := url.Values{} + query.Set("version", strconv.FormatUint(version.Index, 10)) + query.Set("rotateWorkerToken", fmt.Sprintf("%v", flags.RotateWorkerToken)) + query.Set("rotateManagerToken", fmt.Sprintf("%v", flags.RotateManagerToken)) + query.Set("rotateManagerUnlockKey", fmt.Sprintf("%v", flags.RotateManagerUnlockKey)) + resp, err := cli.post(ctx, "/swarm/update", query, swarm, nil) + ensureReaderClosed(resp) + return err +} diff --git a/vendor/github.com/docker/docker/client/task_inspect.go b/vendor/github.com/docker/docker/client/task_inspect.go new file mode 100644 index 0000000000..44d40ba5ae --- /dev/null +++ b/vendor/github.com/docker/docker/client/task_inspect.go @@ -0,0 +1,32 @@ +package client // import "github.com/docker/docker/client" + +import ( + "bytes" + "context" + "encoding/json" + "io/ioutil" + + "github.com/docker/docker/api/types/swarm" +) + +// TaskInspectWithRaw returns the task information and its raw representation.. +func (cli *Client) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error) { + if taskID == "" { + return swarm.Task{}, nil, objectNotFoundError{object: "task", id: taskID} + } + serverResp, err := cli.get(ctx, "/tasks/"+taskID, nil, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return swarm.Task{}, nil, wrapResponseError(err, serverResp, "task", taskID) + } + + body, err := ioutil.ReadAll(serverResp.body) + if err != nil { + return swarm.Task{}, nil, err + } + + var response swarm.Task + rdr := bytes.NewReader(body) + err = json.NewDecoder(rdr).Decode(&response) + return response, body, err +} diff --git a/vendor/github.com/docker/docker/client/task_list.go b/vendor/github.com/docker/docker/client/task_list.go new file mode 100644 index 0000000000..4869b44493 --- /dev/null +++ b/vendor/github.com/docker/docker/client/task_list.go @@ -0,0 +1,35 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/swarm" +) + +// TaskList returns the list of tasks. +func (cli *Client) TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error) { + query := url.Values{} + + if options.Filters.Len() > 0 { + filterJSON, err := filters.ToJSON(options.Filters) + if err != nil { + return nil, err + } + + query.Set("filters", filterJSON) + } + + resp, err := cli.get(ctx, "/tasks", query, nil) + defer ensureReaderClosed(resp) + if err != nil { + return nil, err + } + + var tasks []swarm.Task + err = json.NewDecoder(resp.body).Decode(&tasks) + return tasks, err +} diff --git a/vendor/github.com/docker/docker/client/task_logs.go b/vendor/github.com/docker/docker/client/task_logs.go new file mode 100644 index 0000000000..6222fab577 --- /dev/null +++ b/vendor/github.com/docker/docker/client/task_logs.go @@ -0,0 +1,51 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "io" + "net/url" + "time" + + "github.com/docker/docker/api/types" + timetypes "github.com/docker/docker/api/types/time" +) + +// TaskLogs returns the logs generated by a task in an io.ReadCloser. +// It's up to the caller to close the stream. +func (cli *Client) TaskLogs(ctx context.Context, taskID string, options types.ContainerLogsOptions) (io.ReadCloser, error) { + query := url.Values{} + if options.ShowStdout { + query.Set("stdout", "1") + } + + if options.ShowStderr { + query.Set("stderr", "1") + } + + if options.Since != "" { + ts, err := timetypes.GetTimestamp(options.Since, time.Now()) + if err != nil { + return nil, err + } + query.Set("since", ts) + } + + if options.Timestamps { + query.Set("timestamps", "1") + } + + if options.Details { + query.Set("details", "1") + } + + if options.Follow { + query.Set("follow", "1") + } + query.Set("tail", options.Tail) + + resp, err := cli.get(ctx, "/tasks/"+taskID+"/logs", query, nil) + if err != nil { + return nil, err + } + return resp.body, nil +} diff --git a/vendor/github.com/docker/docker/client/transport.go b/vendor/github.com/docker/docker/client/transport.go new file mode 100644 index 0000000000..5541344366 --- /dev/null +++ b/vendor/github.com/docker/docker/client/transport.go @@ -0,0 +1,17 @@ +package client // import "github.com/docker/docker/client" + +import ( + "crypto/tls" + "net/http" +) + +// resolveTLSConfig attempts to resolve the TLS configuration from the +// RoundTripper. +func resolveTLSConfig(transport http.RoundTripper) *tls.Config { + switch tr := transport.(type) { + case *http.Transport: + return tr.TLSClientConfig + default: + return nil + } +} diff --git a/vendor/github.com/docker/docker/client/utils.go b/vendor/github.com/docker/docker/client/utils.go new file mode 100644 index 0000000000..7f3ff44eb8 --- /dev/null +++ b/vendor/github.com/docker/docker/client/utils.go @@ -0,0 +1,34 @@ +package client // import "github.com/docker/docker/client" + +import ( + "net/url" + "regexp" + + "github.com/docker/docker/api/types/filters" +) + +var headerRegexp = regexp.MustCompile(`\ADocker/.+\s\((.+)\)\z`) + +// getDockerOS returns the operating system based on the server header from the daemon. +func getDockerOS(serverHeader string) string { + var osType string + matches := headerRegexp.FindStringSubmatch(serverHeader) + if len(matches) > 0 { + osType = matches[1] + } + return osType +} + +// getFiltersQuery returns a url query with "filters" query term, based on the +// filters provided. +func getFiltersQuery(f filters.Args) (url.Values, error) { + query := url.Values{} + if f.Len() > 0 { + filterJSON, err := filters.ToJSON(f) + if err != nil { + return query, err + } + query.Set("filters", filterJSON) + } + return query, nil +} diff --git a/vendor/github.com/docker/docker/client/version.go b/vendor/github.com/docker/docker/client/version.go new file mode 100644 index 0000000000..8f17ff4e87 --- /dev/null +++ b/vendor/github.com/docker/docker/client/version.go @@ -0,0 +1,21 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + + "github.com/docker/docker/api/types" +) + +// ServerVersion returns information of the docker client and server host. +func (cli *Client) ServerVersion(ctx context.Context) (types.Version, error) { + resp, err := cli.get(ctx, "/version", nil, nil) + defer ensureReaderClosed(resp) + if err != nil { + return types.Version{}, err + } + + var server types.Version + err = json.NewDecoder(resp.body).Decode(&server) + return server, err +} diff --git a/vendor/github.com/docker/docker/client/volume_create.go b/vendor/github.com/docker/docker/client/volume_create.go new file mode 100644 index 0000000000..92761b3c63 --- /dev/null +++ b/vendor/github.com/docker/docker/client/volume_create.go @@ -0,0 +1,21 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + + "github.com/docker/docker/api/types" + volumetypes "github.com/docker/docker/api/types/volume" +) + +// VolumeCreate creates a volume in the docker host. +func (cli *Client) VolumeCreate(ctx context.Context, options volumetypes.VolumeCreateBody) (types.Volume, error) { + var volume types.Volume + resp, err := cli.post(ctx, "/volumes/create", nil, options, nil) + defer ensureReaderClosed(resp) + if err != nil { + return volume, err + } + err = json.NewDecoder(resp.body).Decode(&volume) + return volume, err +} diff --git a/vendor/github.com/docker/docker/client/volume_inspect.go b/vendor/github.com/docker/docker/client/volume_inspect.go new file mode 100644 index 0000000000..e20b2c67c7 --- /dev/null +++ b/vendor/github.com/docker/docker/client/volume_inspect.go @@ -0,0 +1,38 @@ +package client // import "github.com/docker/docker/client" + +import ( + "bytes" + "context" + "encoding/json" + "io/ioutil" + + "github.com/docker/docker/api/types" +) + +// VolumeInspect returns the information about a specific volume in the docker host. +func (cli *Client) VolumeInspect(ctx context.Context, volumeID string) (types.Volume, error) { + volume, _, err := cli.VolumeInspectWithRaw(ctx, volumeID) + return volume, err +} + +// VolumeInspectWithRaw returns the information about a specific volume in the docker host and its raw representation +func (cli *Client) VolumeInspectWithRaw(ctx context.Context, volumeID string) (types.Volume, []byte, error) { + if volumeID == "" { + return types.Volume{}, nil, objectNotFoundError{object: "volume", id: volumeID} + } + + var volume types.Volume + resp, err := cli.get(ctx, "/volumes/"+volumeID, nil, nil) + defer ensureReaderClosed(resp) + if err != nil { + return volume, nil, wrapResponseError(err, resp, "volume", volumeID) + } + + body, err := ioutil.ReadAll(resp.body) + if err != nil { + return volume, nil, err + } + rdr := bytes.NewReader(body) + err = json.NewDecoder(rdr).Decode(&volume) + return volume, body, err +} diff --git a/vendor/github.com/docker/docker/client/volume_list.go b/vendor/github.com/docker/docker/client/volume_list.go new file mode 100644 index 0000000000..942498dde2 --- /dev/null +++ b/vendor/github.com/docker/docker/client/volume_list.go @@ -0,0 +1,33 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "net/url" + + "github.com/docker/docker/api/types/filters" + volumetypes "github.com/docker/docker/api/types/volume" +) + +// VolumeList returns the volumes configured in the docker host. +func (cli *Client) VolumeList(ctx context.Context, filter filters.Args) (volumetypes.VolumeListOKBody, error) { + var volumes volumetypes.VolumeListOKBody + query := url.Values{} + + if filter.Len() > 0 { + //nolint:staticcheck // ignore SA1019 for old code + filterJSON, err := filters.ToParamWithVersion(cli.version, filter) + if err != nil { + return volumes, err + } + query.Set("filters", filterJSON) + } + resp, err := cli.get(ctx, "/volumes", query, nil) + defer ensureReaderClosed(resp) + if err != nil { + return volumes, err + } + + err = json.NewDecoder(resp.body).Decode(&volumes) + return volumes, err +} diff --git a/vendor/github.com/docker/docker/client/volume_prune.go b/vendor/github.com/docker/docker/client/volume_prune.go new file mode 100644 index 0000000000..6e324708f2 --- /dev/null +++ b/vendor/github.com/docker/docker/client/volume_prune.go @@ -0,0 +1,36 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" +) + +// VolumesPrune requests the daemon to delete unused data +func (cli *Client) VolumesPrune(ctx context.Context, pruneFilters filters.Args) (types.VolumesPruneReport, error) { + var report types.VolumesPruneReport + + if err := cli.NewVersionError("1.25", "volume prune"); err != nil { + return report, err + } + + query, err := getFiltersQuery(pruneFilters) + if err != nil { + return report, err + } + + serverResp, err := cli.post(ctx, "/volumes/prune", query, nil, nil) + defer ensureReaderClosed(serverResp) + if err != nil { + return report, err + } + + if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { + return report, fmt.Errorf("Error retrieving volume prune report: %v", err) + } + + return report, nil +} diff --git a/vendor/github.com/docker/docker/client/volume_remove.go b/vendor/github.com/docker/docker/client/volume_remove.go new file mode 100644 index 0000000000..79decdafab --- /dev/null +++ b/vendor/github.com/docker/docker/client/volume_remove.go @@ -0,0 +1,21 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" + + "github.com/docker/docker/api/types/versions" +) + +// VolumeRemove removes a volume from the docker host. +func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, force bool) error { + query := url.Values{} + if versions.GreaterThanOrEqualTo(cli.version, "1.25") { + if force { + query.Set("force", "1") + } + } + resp, err := cli.delete(ctx, "/volumes/"+volumeID, query, nil) + defer ensureReaderClosed(resp) + return wrapResponseError(err, resp, "volume", volumeID) +} diff --git a/vendor/github.com/docker/docker/errdefs/defs.go b/vendor/github.com/docker/docker/errdefs/defs.go new file mode 100644 index 0000000000..61e7456b4e --- /dev/null +++ b/vendor/github.com/docker/docker/errdefs/defs.go @@ -0,0 +1,69 @@ +package errdefs // import "github.com/docker/docker/errdefs" + +// ErrNotFound signals that the requested object doesn't exist +type ErrNotFound interface { + NotFound() +} + +// ErrInvalidParameter signals that the user input is invalid +type ErrInvalidParameter interface { + InvalidParameter() +} + +// ErrConflict signals that some internal state conflicts with the requested action and can't be performed. +// A change in state should be able to clear this error. +type ErrConflict interface { + Conflict() +} + +// ErrUnauthorized is used to signify that the user is not authorized to perform a specific action +type ErrUnauthorized interface { + Unauthorized() +} + +// ErrUnavailable signals that the requested action/subsystem is not available. +type ErrUnavailable interface { + Unavailable() +} + +// ErrForbidden signals that the requested action cannot be performed under any circumstances. +// When a ErrForbidden is returned, the caller should never retry the action. +type ErrForbidden interface { + Forbidden() +} + +// ErrSystem signals that some internal error occurred. +// An example of this would be a failed mount request. +type ErrSystem interface { + System() +} + +// ErrNotModified signals that an action can't be performed because it's already in the desired state +type ErrNotModified interface { + NotModified() +} + +// ErrNotImplemented signals that the requested action/feature is not implemented on the system as configured. +type ErrNotImplemented interface { + NotImplemented() +} + +// ErrUnknown signals that the kind of error that occurred is not known. +type ErrUnknown interface { + Unknown() +} + +// ErrCancelled signals that the action was cancelled. +type ErrCancelled interface { + Cancelled() +} + +// ErrDeadline signals that the deadline was reached before the action completed. +type ErrDeadline interface { + DeadlineExceeded() +} + +// ErrDataLoss indicates that data was lost or there is data corruption. +type ErrDataLoss interface { + DataLoss() +} diff --git a/vendor/github.com/docker/docker/errdefs/doc.go b/vendor/github.com/docker/docker/errdefs/doc.go new file mode 100644 index 0000000000..c211f174fc --- /dev/null +++ b/vendor/github.com/docker/docker/errdefs/doc.go @@ -0,0 +1,8 @@ +// Package errdefs defines a set of error interfaces that packages should use for communicating classes of errors. +// Errors that cross the package boundary should implement one (and only one) of these interfaces. +// +// Packages should not reference these interfaces directly, only implement them. +// To check if a particular error implements one of these interfaces, there are helper +// functions provided (e.g. `Is`) which can be used rather than asserting the interfaces directly. +// If you must assert on these interfaces, be sure to check the causal chain (`err.Cause()`). +package errdefs // import "github.com/docker/docker/errdefs" diff --git a/vendor/github.com/docker/docker/errdefs/helpers.go b/vendor/github.com/docker/docker/errdefs/helpers.go new file mode 100644 index 0000000000..fe06fb6f70 --- /dev/null +++ b/vendor/github.com/docker/docker/errdefs/helpers.go @@ -0,0 +1,279 @@ +package errdefs // import "github.com/docker/docker/errdefs" + +import "context" + +type errNotFound struct{ error } + +func (errNotFound) NotFound() {} + +func (e errNotFound) Cause() error { + return e.error +} + +func (e errNotFound) Unwrap() error { + return e.error +} + +// NotFound is a helper to create an error of the class with the same name from any error type +func NotFound(err error) error { + if err == nil || IsNotFound(err) { + return err + } + return errNotFound{err} +} + +type errInvalidParameter struct{ error } + +func (errInvalidParameter) InvalidParameter() {} + +func (e errInvalidParameter) Cause() error { + return e.error +} + +func (e errInvalidParameter) Unwrap() error { + return e.error +} + +// InvalidParameter is a helper to create an error of the class with the same name from any error type +func InvalidParameter(err error) error { + if err == nil || IsInvalidParameter(err) { + return err + } + return errInvalidParameter{err} +} + +type errConflict struct{ error } + +func (errConflict) Conflict() {} + +func (e errConflict) Cause() error { + return e.error +} + +func (e errConflict) Unwrap() error { + return e.error +} + +// Conflict is a helper to create an error of the class with the same name from any error type +func Conflict(err error) error { + if err == nil || IsConflict(err) { + return err + } + return errConflict{err} +} + +type errUnauthorized struct{ error } + +func (errUnauthorized) Unauthorized() {} + +func (e errUnauthorized) Cause() error { + return e.error +} + +func (e errUnauthorized) Unwrap() error { + return e.error +} + +// Unauthorized is a helper to create an error of the class with the same name from any error type +func Unauthorized(err error) error { + if err == nil || IsUnauthorized(err) { + return err + } + return errUnauthorized{err} +} + +type errUnavailable struct{ error } + +func (errUnavailable) Unavailable() {} + +func (e errUnavailable) Cause() error { + return e.error +} + +func (e errUnavailable) Unwrap() error { + return e.error +} + +// Unavailable is a helper to create an error of the class with the same name from any error type +func Unavailable(err error) error { + if err == nil || IsUnavailable(err) { + return err + } + return errUnavailable{err} +} + +type errForbidden struct{ error } + +func (errForbidden) Forbidden() {} + +func (e errForbidden) Cause() error { + return e.error +} + +func (e errForbidden) Unwrap() error { + return e.error +} + +// Forbidden is a helper to create an error of the class with the same name from any error type +func Forbidden(err error) error { + if err == nil || IsForbidden(err) { + return err + } + return errForbidden{err} +} + +type errSystem struct{ error } + +func (errSystem) System() {} + +func (e errSystem) Cause() error { + return e.error +} + +func (e errSystem) Unwrap() error { + return e.error +} + +// System is a helper to create an error of the class with the same name from any error type +func System(err error) error { + if err == nil || IsSystem(err) { + return err + } + return errSystem{err} +} + +type errNotModified struct{ error } + +func (errNotModified) NotModified() {} + +func (e errNotModified) Cause() error { + return e.error +} + +func (e errNotModified) Unwrap() error { + return e.error +} + +// NotModified is a helper to create an error of the class with the same name from any error type +func NotModified(err error) error { + if err == nil || IsNotModified(err) { + return err + } + return errNotModified{err} +} + +type errNotImplemented struct{ error } + +func (errNotImplemented) NotImplemented() {} + +func (e errNotImplemented) Cause() error { + return e.error +} + +func (e errNotImplemented) Unwrap() error { + return e.error +} + +// NotImplemented is a helper to create an error of the class with the same name from any error type +func NotImplemented(err error) error { + if err == nil || IsNotImplemented(err) { + return err + } + return errNotImplemented{err} +} + +type errUnknown struct{ error } + +func (errUnknown) Unknown() {} + +func (e errUnknown) Cause() error { + return e.error +} + +func (e errUnknown) Unwrap() error { + return e.error +} + +// Unknown is a helper to create an error of the class with the same name from any error type +func Unknown(err error) error { + if err == nil || IsUnknown(err) { + return err + } + return errUnknown{err} +} + +type errCancelled struct{ error } + +func (errCancelled) Cancelled() {} + +func (e errCancelled) Cause() error { + return e.error +} + +func (e errCancelled) Unwrap() error { + return e.error +} + +// Cancelled is a helper to create an error of the class with the same name from any error type +func Cancelled(err error) error { + if err == nil || IsCancelled(err) { + return err + } + return errCancelled{err} +} + +type errDeadline struct{ error } + +func (errDeadline) DeadlineExceeded() {} + +func (e errDeadline) Cause() error { + return e.error +} + +func (e errDeadline) Unwrap() error { + return e.error +} + +// Deadline is a helper to create an error of the class with the same name from any error type +func Deadline(err error) error { + if err == nil || IsDeadline(err) { + return err + } + return errDeadline{err} +} + +type errDataLoss struct{ error } + +func (errDataLoss) DataLoss() {} + +func (e errDataLoss) Cause() error { + return e.error +} + +func (e errDataLoss) Unwrap() error { + return e.error +} + +// DataLoss is a helper to create an error of the class with the same name from any error type +func DataLoss(err error) error { + if err == nil || IsDataLoss(err) { + return err + } + return errDataLoss{err} +} + +// FromContext returns the error class from the passed in context +func FromContext(ctx context.Context) error { + e := ctx.Err() + if e == nil { + return nil + } + + if e == context.Canceled { + return Cancelled(e) + } + if e == context.DeadlineExceeded { + return Deadline(e) + } + return Unknown(e) +} diff --git a/vendor/github.com/docker/docker/errdefs/http_helpers.go b/vendor/github.com/docker/docker/errdefs/http_helpers.go new file mode 100644 index 0000000000..07552f1cc1 --- /dev/null +++ b/vendor/github.com/docker/docker/errdefs/http_helpers.go @@ -0,0 +1,191 @@ +package errdefs // import "github.com/docker/docker/errdefs" + +import ( + "fmt" + "net/http" + + containerderrors "github.com/containerd/containerd/errdefs" + "github.com/docker/distribution/registry/api/errcode" + "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// GetHTTPErrorStatusCode retrieves status code from error message. +func GetHTTPErrorStatusCode(err error) int { + if err == nil { + logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling") + return http.StatusInternalServerError + } + + var statusCode int + + // Stop right there + // Are you sure you should be adding a new error class here? Do one of the existing ones work? + + // Note that the below functions are already checking the error causal chain for matches. + switch { + case IsNotFound(err): + statusCode = http.StatusNotFound + case IsInvalidParameter(err): + statusCode = http.StatusBadRequest + case IsConflict(err): + statusCode = http.StatusConflict + case IsUnauthorized(err): + statusCode = http.StatusUnauthorized + case IsUnavailable(err): + statusCode = http.StatusServiceUnavailable + case IsForbidden(err): + statusCode = http.StatusForbidden + case IsNotModified(err): + statusCode = http.StatusNotModified + case IsNotImplemented(err): + statusCode = http.StatusNotImplemented + case IsSystem(err) || IsUnknown(err) || IsDataLoss(err) || IsDeadline(err) || IsCancelled(err): + statusCode = http.StatusInternalServerError + default: + statusCode = statusCodeFromGRPCError(err) + if statusCode != http.StatusInternalServerError { + return statusCode + } + statusCode = statusCodeFromContainerdError(err) + if statusCode != http.StatusInternalServerError { + return statusCode + } + statusCode = statusCodeFromDistributionError(err) + if statusCode != http.StatusInternalServerError { + return statusCode + } + if e, ok := err.(causer); ok { + return GetHTTPErrorStatusCode(e.Cause()) + } + + logrus.WithFields(logrus.Fields{ + "module": "api", + "error_type": fmt.Sprintf("%T", err), + }).Debugf("FIXME: Got an API for which error does not match any expected type!!!: %+v", err) + } + + if statusCode == 0 { + statusCode = http.StatusInternalServerError + } + + return statusCode +} + +// FromStatusCode creates an errdef error, based on the provided HTTP status-code +func FromStatusCode(err error, statusCode int) error { + if err == nil { + return err + } + switch statusCode { + case http.StatusNotFound: + err = NotFound(err) + case http.StatusBadRequest: + err = InvalidParameter(err) + case http.StatusConflict: + err = Conflict(err) + case http.StatusUnauthorized: + err = Unauthorized(err) + case http.StatusServiceUnavailable: + err = Unavailable(err) + case http.StatusForbidden: + err = Forbidden(err) + case http.StatusNotModified: + err = NotModified(err) + case http.StatusNotImplemented: + err = NotImplemented(err) + case http.StatusInternalServerError: + if !IsSystem(err) && !IsUnknown(err) && !IsDataLoss(err) && !IsDeadline(err) && !IsCancelled(err) { + err = System(err) + } + default: + logrus.WithFields(logrus.Fields{ + "module": "api", + "status_code": fmt.Sprintf("%d", statusCode), + }).Debugf("FIXME: Got an status-code for which error does not match any expected type!!!: %d", statusCode) + + switch { + case statusCode >= 200 && statusCode < 400: + // it's a client error + case statusCode >= 400 && statusCode < 500: + err = InvalidParameter(err) + case statusCode >= 500 && statusCode < 600: + err = System(err) + default: + err = Unknown(err) + } + } + return err +} + +// statusCodeFromGRPCError returns status code according to gRPC error +func statusCodeFromGRPCError(err error) int { + switch status.Code(err) { + case codes.InvalidArgument: // code 3 + return http.StatusBadRequest + case codes.NotFound: // code 5 + return http.StatusNotFound + case codes.AlreadyExists: // code 6 + return http.StatusConflict + case codes.PermissionDenied: // code 7 + return http.StatusForbidden + case codes.FailedPrecondition: // code 9 + return http.StatusBadRequest + case codes.Unauthenticated: // code 16 + return http.StatusUnauthorized + case codes.OutOfRange: // code 11 + return http.StatusBadRequest + case codes.Unimplemented: // code 12 + return http.StatusNotImplemented + case codes.Unavailable: // code 14 + return http.StatusServiceUnavailable + default: + // codes.Canceled(1) + // codes.Unknown(2) + // codes.DeadlineExceeded(4) + // codes.ResourceExhausted(8) + // codes.Aborted(10) + // codes.Internal(13) + // codes.DataLoss(15) + return http.StatusInternalServerError + } +} + +// statusCodeFromDistributionError returns status code according to registry errcode +// code is loosely based on errcode.ServeJSON() in docker/distribution +func statusCodeFromDistributionError(err error) int { + switch errs := err.(type) { + case errcode.Errors: + if len(errs) < 1 { + return http.StatusInternalServerError + } + if _, ok := errs[0].(errcode.ErrorCoder); ok { + return statusCodeFromDistributionError(errs[0]) + } + case errcode.ErrorCoder: + return errs.ErrorCode().Descriptor().HTTPStatusCode + } + return http.StatusInternalServerError +} + +// statusCodeFromContainerdError returns status code for containerd errors when +// consumed directly (not through gRPC) +func statusCodeFromContainerdError(err error) int { + switch { + case containerderrors.IsInvalidArgument(err): + return http.StatusBadRequest + case containerderrors.IsNotFound(err): + return http.StatusNotFound + case containerderrors.IsAlreadyExists(err): + return http.StatusConflict + case containerderrors.IsFailedPrecondition(err): + return http.StatusPreconditionFailed + case containerderrors.IsUnavailable(err): + return http.StatusServiceUnavailable + case containerderrors.IsNotImplemented(err): + return http.StatusNotImplemented + default: + return http.StatusInternalServerError + } +} diff --git a/vendor/github.com/docker/docker/errdefs/is.go b/vendor/github.com/docker/docker/errdefs/is.go new file mode 100644 index 0000000000..3abf07d0c3 --- /dev/null +++ b/vendor/github.com/docker/docker/errdefs/is.go @@ -0,0 +1,107 @@ +package errdefs // import "github.com/docker/docker/errdefs" + +type causer interface { + Cause() error +} + +func getImplementer(err error) error { + switch e := err.(type) { + case + ErrNotFound, + ErrInvalidParameter, + ErrConflict, + ErrUnauthorized, + ErrUnavailable, + ErrForbidden, + ErrSystem, + ErrNotModified, + ErrNotImplemented, + ErrCancelled, + ErrDeadline, + ErrDataLoss, + ErrUnknown: + return err + case causer: + return getImplementer(e.Cause()) + default: + return err + } +} + +// IsNotFound returns if the passed in error is an ErrNotFound +func IsNotFound(err error) bool { + _, ok := getImplementer(err).(ErrNotFound) + return ok +} + +// IsInvalidParameter returns if the passed in error is an ErrInvalidParameter +func IsInvalidParameter(err error) bool { + _, ok := getImplementer(err).(ErrInvalidParameter) + return ok +} + +// IsConflict returns if the passed in error is an ErrConflict +func IsConflict(err error) bool { + _, ok := getImplementer(err).(ErrConflict) + return ok +} + +// IsUnauthorized returns if the passed in error is an ErrUnauthorized +func IsUnauthorized(err error) bool { + _, ok := getImplementer(err).(ErrUnauthorized) + return ok +} + +// IsUnavailable returns if the passed in error is an ErrUnavailable +func IsUnavailable(err error) bool { + _, ok := getImplementer(err).(ErrUnavailable) + return ok +} + +// IsForbidden returns if the passed in error is an ErrForbidden +func IsForbidden(err error) bool { + _, ok := getImplementer(err).(ErrForbidden) + return ok +} + +// IsSystem returns if the passed in error is an ErrSystem +func IsSystem(err error) bool { + _, ok := getImplementer(err).(ErrSystem) + return ok +} + +// IsNotModified returns if the passed in error is a NotModified error +func IsNotModified(err error) bool { + _, ok := getImplementer(err).(ErrNotModified) + return ok +} + +// IsNotImplemented returns if the passed in error is an ErrNotImplemented +func IsNotImplemented(err error) bool { + _, ok := getImplementer(err).(ErrNotImplemented) + return ok +} + +// IsUnknown returns if the passed in error is an ErrUnknown +func IsUnknown(err error) bool { + _, ok := getImplementer(err).(ErrUnknown) + return ok +} + +// IsCancelled returns if the passed in error is an ErrCancelled +func IsCancelled(err error) bool { + _, ok := getImplementer(err).(ErrCancelled) + return ok +} + +// IsDeadline returns if the passed in error is an ErrDeadline +func IsDeadline(err error) bool { + _, ok := getImplementer(err).(ErrDeadline) + return ok +} + +// IsDataLoss returns if the passed in error is an ErrDataLoss +func IsDataLoss(err error) bool { + _, ok := getImplementer(err).(ErrDataLoss) + return ok +} diff --git a/vendor/github.com/docker/docker/pkg/archive/README.md b/vendor/github.com/docker/docker/pkg/archive/README.md new file mode 100644 index 0000000000..7307d9694f --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/README.md @@ -0,0 +1 @@ +This code provides helper functions for dealing with archive files. diff --git a/vendor/github.com/docker/docker/pkg/archive/archive.go b/vendor/github.com/docker/docker/pkg/archive/archive.go new file mode 100644 index 0000000000..50b83c62c6 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/archive.go @@ -0,0 +1,1322 @@ +package archive // import "github.com/docker/docker/pkg/archive" + +import ( + "archive/tar" + "bufio" + "bytes" + "compress/bzip2" + "compress/gzip" + "context" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + "syscall" + "time" + + "github.com/docker/docker/pkg/fileutils" + "github.com/docker/docker/pkg/idtools" + "github.com/docker/docker/pkg/ioutils" + "github.com/docker/docker/pkg/pools" + "github.com/docker/docker/pkg/system" + "github.com/sirupsen/logrus" + exec "golang.org/x/sys/execabs" +) + +type ( + // Compression is the state represents if compressed or not. + Compression int + // WhiteoutFormat is the format of whiteouts unpacked + WhiteoutFormat int + + // TarOptions wraps the tar options. + TarOptions struct { + IncludeFiles []string + ExcludePatterns []string + Compression Compression + NoLchown bool + UIDMaps []idtools.IDMap + GIDMaps []idtools.IDMap + ChownOpts *idtools.Identity + IncludeSourceDir bool + // WhiteoutFormat is the expected on disk format for whiteout files. + // This format will be converted to the standard format on pack + // and from the standard format on unpack. + WhiteoutFormat WhiteoutFormat + // When unpacking, specifies whether overwriting a directory with a + // non-directory is allowed and vice versa. + NoOverwriteDirNonDir bool + // For each include when creating an archive, the included name will be + // replaced with the matching name from this map. + RebaseNames map[string]string + InUserNS bool + } +) + +// Archiver implements the Archiver interface and allows the reuse of most utility functions of +// this package with a pluggable Untar function. Also, to facilitate the passing of specific id +// mappings for untar, an Archiver can be created with maps which will then be passed to Untar operations. +type Archiver struct { + Untar func(io.Reader, string, *TarOptions) error + IDMapping *idtools.IdentityMapping +} + +// NewDefaultArchiver returns a new Archiver without any IdentityMapping +func NewDefaultArchiver() *Archiver { + return &Archiver{Untar: Untar, IDMapping: &idtools.IdentityMapping{}} +} + +// breakoutError is used to differentiate errors related to breaking out +// When testing archive breakout in the unit tests, this error is expected +// in order for the test to pass. +type breakoutError error + +const ( + // Uncompressed represents the uncompressed. + Uncompressed Compression = iota + // Bzip2 is bzip2 compression algorithm. + Bzip2 + // Gzip is gzip compression algorithm. + Gzip + // Xz is xz compression algorithm. + Xz +) + +const ( + // AUFSWhiteoutFormat is the default format for whiteouts + AUFSWhiteoutFormat WhiteoutFormat = iota + // OverlayWhiteoutFormat formats whiteout according to the overlay + // standard. + OverlayWhiteoutFormat +) + +const ( + modeISDIR = 040000 // Directory + modeISFIFO = 010000 // FIFO + modeISREG = 0100000 // Regular file + modeISLNK = 0120000 // Symbolic link + modeISBLK = 060000 // Block special file + modeISCHR = 020000 // Character special file + modeISSOCK = 0140000 // Socket +) + +// IsArchivePath checks if the (possibly compressed) file at the given path +// starts with a tar file header. +func IsArchivePath(path string) bool { + file, err := os.Open(path) + if err != nil { + return false + } + defer file.Close() + rdr, err := DecompressStream(file) + if err != nil { + return false + } + defer rdr.Close() + r := tar.NewReader(rdr) + _, err = r.Next() + return err == nil +} + +// DetectCompression detects the compression algorithm of the source. +func DetectCompression(source []byte) Compression { + for compression, m := range map[Compression][]byte{ + Bzip2: {0x42, 0x5A, 0x68}, + Gzip: {0x1F, 0x8B, 0x08}, + Xz: {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}, + } { + if len(source) < len(m) { + logrus.Debug("Len too short") + continue + } + if bytes.Equal(m, source[:len(m)]) { + return compression + } + } + return Uncompressed +} + +func xzDecompress(ctx context.Context, archive io.Reader) (io.ReadCloser, error) { + args := []string{"xz", "-d", "-c", "-q"} + + return cmdStream(exec.CommandContext(ctx, args[0], args[1:]...), archive) +} + +func gzDecompress(ctx context.Context, buf io.Reader) (io.ReadCloser, error) { + noPigzEnv := os.Getenv("MOBY_DISABLE_PIGZ") + var noPigz bool + + if noPigzEnv != "" { + var err error + noPigz, err = strconv.ParseBool(noPigzEnv) + if err != nil { + logrus.WithError(err).Warn("invalid value in MOBY_DISABLE_PIGZ env var") + } + } + + if noPigz { + logrus.Debugf("Use of pigz is disabled due to MOBY_DISABLE_PIGZ=%s", noPigzEnv) + return gzip.NewReader(buf) + } + + unpigzPath, err := exec.LookPath("unpigz") + if err != nil { + logrus.Debugf("unpigz binary not found, falling back to go gzip library") + return gzip.NewReader(buf) + } + + logrus.Debugf("Using %s to decompress", unpigzPath) + + return cmdStream(exec.CommandContext(ctx, unpigzPath, "-d", "-c"), buf) +} + +func wrapReadCloser(readBuf io.ReadCloser, cancel context.CancelFunc) io.ReadCloser { + return ioutils.NewReadCloserWrapper(readBuf, func() error { + cancel() + return readBuf.Close() + }) +} + +// DecompressStream decompresses the archive and returns a ReaderCloser with the decompressed archive. +func DecompressStream(archive io.Reader) (io.ReadCloser, error) { + p := pools.BufioReader32KPool + buf := p.Get(archive) + bs, err := buf.Peek(10) + if err != nil && err != io.EOF { + // Note: we'll ignore any io.EOF error because there are some odd + // cases where the layer.tar file will be empty (zero bytes) and + // that results in an io.EOF from the Peek() call. So, in those + // cases we'll just treat it as a non-compressed stream and + // that means just create an empty layer. + // See Issue 18170 + return nil, err + } + + compression := DetectCompression(bs) + switch compression { + case Uncompressed: + readBufWrapper := p.NewReadCloserWrapper(buf, buf) + return readBufWrapper, nil + case Gzip: + ctx, cancel := context.WithCancel(context.Background()) + + gzReader, err := gzDecompress(ctx, buf) + if err != nil { + cancel() + return nil, err + } + readBufWrapper := p.NewReadCloserWrapper(buf, gzReader) + return wrapReadCloser(readBufWrapper, cancel), nil + case Bzip2: + bz2Reader := bzip2.NewReader(buf) + readBufWrapper := p.NewReadCloserWrapper(buf, bz2Reader) + return readBufWrapper, nil + case Xz: + ctx, cancel := context.WithCancel(context.Background()) + + xzReader, err := xzDecompress(ctx, buf) + if err != nil { + cancel() + return nil, err + } + readBufWrapper := p.NewReadCloserWrapper(buf, xzReader) + return wrapReadCloser(readBufWrapper, cancel), nil + default: + return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension()) + } +} + +// CompressStream compresses the dest with specified compression algorithm. +func CompressStream(dest io.Writer, compression Compression) (io.WriteCloser, error) { + p := pools.BufioWriter32KPool + buf := p.Get(dest) + switch compression { + case Uncompressed: + writeBufWrapper := p.NewWriteCloserWrapper(buf, buf) + return writeBufWrapper, nil + case Gzip: + gzWriter := gzip.NewWriter(dest) + writeBufWrapper := p.NewWriteCloserWrapper(buf, gzWriter) + return writeBufWrapper, nil + case Bzip2, Xz: + // archive/bzip2 does not support writing, and there is no xz support at all + // However, this is not a problem as docker only currently generates gzipped tars + return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension()) + default: + return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension()) + } +} + +// TarModifierFunc is a function that can be passed to ReplaceFileTarWrapper to +// modify the contents or header of an entry in the archive. If the file already +// exists in the archive the TarModifierFunc will be called with the Header and +// a reader which will return the files content. If the file does not exist both +// header and content will be nil. +type TarModifierFunc func(path string, header *tar.Header, content io.Reader) (*tar.Header, []byte, error) + +// ReplaceFileTarWrapper converts inputTarStream to a new tar stream. Files in the +// tar stream are modified if they match any of the keys in mods. +func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModifierFunc) io.ReadCloser { + pipeReader, pipeWriter := io.Pipe() + + go func() { + tarReader := tar.NewReader(inputTarStream) + tarWriter := tar.NewWriter(pipeWriter) + defer inputTarStream.Close() + defer tarWriter.Close() + + modify := func(name string, original *tar.Header, modifier TarModifierFunc, tarReader io.Reader) error { + header, data, err := modifier(name, original, tarReader) + switch { + case err != nil: + return err + case header == nil: + return nil + } + + header.Name = name + header.Size = int64(len(data)) + if err := tarWriter.WriteHeader(header); err != nil { + return err + } + if len(data) != 0 { + if _, err := tarWriter.Write(data); err != nil { + return err + } + } + return nil + } + + var err error + var originalHeader *tar.Header + for { + originalHeader, err = tarReader.Next() + if err == io.EOF { + break + } + if err != nil { + pipeWriter.CloseWithError(err) + return + } + + modifier, ok := mods[originalHeader.Name] + if !ok { + // No modifiers for this file, copy the header and data + if err := tarWriter.WriteHeader(originalHeader); err != nil { + pipeWriter.CloseWithError(err) + return + } + if _, err := pools.Copy(tarWriter, tarReader); err != nil { + pipeWriter.CloseWithError(err) + return + } + continue + } + delete(mods, originalHeader.Name) + + if err := modify(originalHeader.Name, originalHeader, modifier, tarReader); err != nil { + pipeWriter.CloseWithError(err) + return + } + } + + // Apply the modifiers that haven't matched any files in the archive + for name, modifier := range mods { + if err := modify(name, nil, modifier, nil); err != nil { + pipeWriter.CloseWithError(err) + return + } + } + + pipeWriter.Close() + + }() + return pipeReader +} + +// Extension returns the extension of a file that uses the specified compression algorithm. +func (compression *Compression) Extension() string { + switch *compression { + case Uncompressed: + return "tar" + case Bzip2: + return "tar.bz2" + case Gzip: + return "tar.gz" + case Xz: + return "tar.xz" + } + return "" +} + +// FileInfoHeader creates a populated Header from fi. +// Compared to archive pkg this function fills in more information. +// Also, regardless of Go version, this function fills file type bits (e.g. hdr.Mode |= modeISDIR), +// which have been deleted since Go 1.9 archive/tar. +func FileInfoHeader(name string, fi os.FileInfo, link string) (*tar.Header, error) { + hdr, err := tar.FileInfoHeader(fi, link) + if err != nil { + return nil, err + } + hdr.Format = tar.FormatPAX + hdr.ModTime = hdr.ModTime.Truncate(time.Second) + hdr.AccessTime = time.Time{} + hdr.ChangeTime = time.Time{} + hdr.Mode = fillGo18FileTypeBits(int64(chmodTarEntry(os.FileMode(hdr.Mode))), fi) + hdr.Name = canonicalTarName(name, fi.IsDir()) + if err := setHeaderForSpecialDevice(hdr, name, fi.Sys()); err != nil { + return nil, err + } + return hdr, nil +} + +// fillGo18FileTypeBits fills type bits which have been removed on Go 1.9 archive/tar +// https://github.com/golang/go/commit/66b5a2f +func fillGo18FileTypeBits(mode int64, fi os.FileInfo) int64 { + fm := fi.Mode() + switch { + case fm.IsRegular(): + mode |= modeISREG + case fi.IsDir(): + mode |= modeISDIR + case fm&os.ModeSymlink != 0: + mode |= modeISLNK + case fm&os.ModeDevice != 0: + if fm&os.ModeCharDevice != 0 { + mode |= modeISCHR + } else { + mode |= modeISBLK + } + case fm&os.ModeNamedPipe != 0: + mode |= modeISFIFO + case fm&os.ModeSocket != 0: + mode |= modeISSOCK + } + return mode +} + +// ReadSecurityXattrToTarHeader reads security.capability xattr from filesystem +// to a tar header +func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error { + const ( + // Values based on linux/include/uapi/linux/capability.h + xattrCapsSz2 = 20 + versionOffset = 3 + vfsCapRevision2 = 2 + vfsCapRevision3 = 3 + ) + capability, _ := system.Lgetxattr(path, "security.capability") + if capability != nil { + length := len(capability) + if capability[versionOffset] == vfsCapRevision3 { + // Convert VFS_CAP_REVISION_3 to VFS_CAP_REVISION_2 as root UID makes no + // sense outside the user namespace the archive is built in. + capability[versionOffset] = vfsCapRevision2 + length = xattrCapsSz2 + } + hdr.Xattrs = make(map[string]string) + hdr.Xattrs["security.capability"] = string(capability[:length]) + } + return nil +} + +type tarWhiteoutConverter interface { + ConvertWrite(*tar.Header, string, os.FileInfo) (*tar.Header, error) + ConvertRead(*tar.Header, string) (bool, error) +} + +type tarAppender struct { + TarWriter *tar.Writer + Buffer *bufio.Writer + + // for hardlink mapping + SeenFiles map[uint64]string + IdentityMapping *idtools.IdentityMapping + ChownOpts *idtools.Identity + + // For packing and unpacking whiteout files in the + // non standard format. The whiteout files defined + // by the AUFS standard are used as the tar whiteout + // standard. + WhiteoutConverter tarWhiteoutConverter +} + +func newTarAppender(idMapping *idtools.IdentityMapping, writer io.Writer, chownOpts *idtools.Identity) *tarAppender { + return &tarAppender{ + SeenFiles: make(map[uint64]string), + TarWriter: tar.NewWriter(writer), + Buffer: pools.BufioWriter32KPool.Get(nil), + IdentityMapping: idMapping, + ChownOpts: chownOpts, + } +} + +// canonicalTarName provides a platform-independent and consistent posix-style +// path for files and directories to be archived regardless of the platform. +func canonicalTarName(name string, isDir bool) string { + name = CanonicalTarNameForPath(name) + + // suffix with '/' for directories + if isDir && !strings.HasSuffix(name, "/") { + name += "/" + } + return name +} + +// addTarFile adds to the tar archive a file from `path` as `name` +func (ta *tarAppender) addTarFile(path, name string) error { + fi, err := os.Lstat(path) + if err != nil { + return err + } + + var link string + if fi.Mode()&os.ModeSymlink != 0 { + var err error + link, err = os.Readlink(path) + if err != nil { + return err + } + } + + hdr, err := FileInfoHeader(name, fi, link) + if err != nil { + return err + } + if err := ReadSecurityXattrToTarHeader(path, hdr); err != nil { + return err + } + + // if it's not a directory and has more than 1 link, + // it's hard linked, so set the type flag accordingly + if !fi.IsDir() && hasHardlinks(fi) { + inode, err := getInodeFromStat(fi.Sys()) + if err != nil { + return err + } + // a link should have a name that it links too + // and that linked name should be first in the tar archive + if oldpath, ok := ta.SeenFiles[inode]; ok { + hdr.Typeflag = tar.TypeLink + hdr.Linkname = oldpath + hdr.Size = 0 // This Must be here for the writer math to add up! + } else { + ta.SeenFiles[inode] = name + } + } + + // check whether the file is overlayfs whiteout + // if yes, skip re-mapping container ID mappings. + isOverlayWhiteout := fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0 + + // handle re-mapping container ID mappings back to host ID mappings before + // writing tar headers/files. We skip whiteout files because they were written + // by the kernel and already have proper ownership relative to the host + if !isOverlayWhiteout && !strings.HasPrefix(filepath.Base(hdr.Name), WhiteoutPrefix) && !ta.IdentityMapping.Empty() { + fileIDPair, err := getFileUIDGID(fi.Sys()) + if err != nil { + return err + } + hdr.Uid, hdr.Gid, err = ta.IdentityMapping.ToContainer(fileIDPair) + if err != nil { + return err + } + } + + // explicitly override with ChownOpts + if ta.ChownOpts != nil { + hdr.Uid = ta.ChownOpts.UID + hdr.Gid = ta.ChownOpts.GID + } + + if ta.WhiteoutConverter != nil { + wo, err := ta.WhiteoutConverter.ConvertWrite(hdr, path, fi) + if err != nil { + return err + } + + // If a new whiteout file exists, write original hdr, then + // replace hdr with wo to be written after. Whiteouts should + // always be written after the original. Note the original + // hdr may have been updated to be a whiteout with returning + // a whiteout header + if wo != nil { + if err := ta.TarWriter.WriteHeader(hdr); err != nil { + return err + } + if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 { + return fmt.Errorf("tar: cannot use whiteout for non-empty file") + } + hdr = wo + } + } + + if err := ta.TarWriter.WriteHeader(hdr); err != nil { + return err + } + + if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 { + // We use system.OpenSequential to ensure we use sequential file + // access on Windows to avoid depleting the standby list. + // On Linux, this equates to a regular os.Open. + file, err := system.OpenSequential(path) + if err != nil { + return err + } + + ta.Buffer.Reset(ta.TarWriter) + defer ta.Buffer.Reset(nil) + _, err = io.Copy(ta.Buffer, file) + file.Close() + if err != nil { + return err + } + err = ta.Buffer.Flush() + if err != nil { + return err + } + } + + return nil +} + +func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *idtools.Identity, inUserns bool) error { + // hdr.Mode is in linux format, which we can use for sycalls, + // but for os.Foo() calls we need the mode converted to os.FileMode, + // so use hdrInfo.Mode() (they differ for e.g. setuid bits) + hdrInfo := hdr.FileInfo() + + switch hdr.Typeflag { + case tar.TypeDir: + // Create directory unless it exists as a directory already. + // In that case we just want to merge the two + if fi, err := os.Lstat(path); !(err == nil && fi.IsDir()) { + if err := os.Mkdir(path, hdrInfo.Mode()); err != nil { + return err + } + } + + case tar.TypeReg, tar.TypeRegA: + // Source is regular file. We use system.OpenFileSequential to use sequential + // file access to avoid depleting the standby list on Windows. + // On Linux, this equates to a regular os.OpenFile + file, err := system.OpenFileSequential(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode()) + if err != nil { + return err + } + if _, err := io.Copy(file, reader); err != nil { + file.Close() + return err + } + file.Close() + + case tar.TypeBlock, tar.TypeChar: + if inUserns { // cannot create devices in a userns + return nil + } + // Handle this is an OS-specific way + if err := handleTarTypeBlockCharFifo(hdr, path); err != nil { + return err + } + + case tar.TypeFifo: + // Handle this is an OS-specific way + if err := handleTarTypeBlockCharFifo(hdr, path); err != nil { + return err + } + + case tar.TypeLink: + targetPath := filepath.Join(extractDir, hdr.Linkname) + // check for hardlink breakout + if !strings.HasPrefix(targetPath, extractDir) { + return breakoutError(fmt.Errorf("invalid hardlink %q -> %q", targetPath, hdr.Linkname)) + } + if err := os.Link(targetPath, path); err != nil { + return err + } + + case tar.TypeSymlink: + // path -> hdr.Linkname = targetPath + // e.g. /extractDir/path/to/symlink -> ../2/file = /extractDir/path/2/file + targetPath := filepath.Join(filepath.Dir(path), hdr.Linkname) + + // the reason we don't need to check symlinks in the path (with FollowSymlinkInScope) is because + // that symlink would first have to be created, which would be caught earlier, at this very check: + if !strings.HasPrefix(targetPath, extractDir) { + return breakoutError(fmt.Errorf("invalid symlink %q -> %q", path, hdr.Linkname)) + } + if err := os.Symlink(hdr.Linkname, path); err != nil { + return err + } + + case tar.TypeXGlobalHeader: + logrus.Debug("PAX Global Extended Headers found and ignored") + return nil + + default: + return fmt.Errorf("unhandled tar header type %d", hdr.Typeflag) + } + + // Lchown is not supported on Windows. + if Lchown && runtime.GOOS != "windows" { + if chownOpts == nil { + chownOpts = &idtools.Identity{UID: hdr.Uid, GID: hdr.Gid} + } + if err := os.Lchown(path, chownOpts.UID, chownOpts.GID); err != nil { + return err + } + } + + var errors []string + for key, value := range hdr.Xattrs { + if err := system.Lsetxattr(path, key, []byte(value), 0); err != nil { + if err == syscall.ENOTSUP || err == syscall.EPERM { + // We ignore errors here because not all graphdrivers support + // xattrs *cough* old versions of AUFS *cough*. However only + // ENOTSUP should be emitted in that case, otherwise we still + // bail. + // EPERM occurs if modifying xattrs is not allowed. This can + // happen when running in userns with restrictions (ChromeOS). + errors = append(errors, err.Error()) + continue + } + return err + } + + } + + if len(errors) > 0 { + logrus.WithFields(logrus.Fields{ + "errors": errors, + }).Warn("ignored xattrs in archive: underlying filesystem doesn't support them") + } + + // There is no LChmod, so ignore mode for symlink. Also, this + // must happen after chown, as that can modify the file mode + if err := handleLChmod(hdr, path, hdrInfo); err != nil { + return err + } + + aTime := hdr.AccessTime + if aTime.Before(hdr.ModTime) { + // Last access time should never be before last modified time. + aTime = hdr.ModTime + } + + // system.Chtimes doesn't support a NOFOLLOW flag atm + if hdr.Typeflag == tar.TypeLink { + if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) { + if err := system.Chtimes(path, aTime, hdr.ModTime); err != nil { + return err + } + } + } else if hdr.Typeflag != tar.TypeSymlink { + if err := system.Chtimes(path, aTime, hdr.ModTime); err != nil { + return err + } + } else { + ts := []syscall.Timespec{timeToTimespec(aTime), timeToTimespec(hdr.ModTime)} + if err := system.LUtimesNano(path, ts); err != nil && err != system.ErrNotSupportedPlatform { + return err + } + } + return nil +} + +// Tar creates an archive from the directory at `path`, and returns it as a +// stream of bytes. +func Tar(path string, compression Compression) (io.ReadCloser, error) { + return TarWithOptions(path, &TarOptions{Compression: compression}) +} + +// TarWithOptions creates an archive from the directory at `path`, only including files whose relative +// paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`. +func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) { + + // Fix the source path to work with long path names. This is a no-op + // on platforms other than Windows. + srcPath = fixVolumePathPrefix(srcPath) + + pm, err := fileutils.NewPatternMatcher(options.ExcludePatterns) + if err != nil { + return nil, err + } + + pipeReader, pipeWriter := io.Pipe() + + compressWriter, err := CompressStream(pipeWriter, options.Compression) + if err != nil { + return nil, err + } + + whiteoutConverter, err := getWhiteoutConverter(options.WhiteoutFormat, options.InUserNS) + if err != nil { + return nil, err + } + + go func() { + ta := newTarAppender( + idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps), + compressWriter, + options.ChownOpts, + ) + ta.WhiteoutConverter = whiteoutConverter + + defer func() { + // Make sure to check the error on Close. + if err := ta.TarWriter.Close(); err != nil { + logrus.Errorf("Can't close tar writer: %s", err) + } + if err := compressWriter.Close(); err != nil { + logrus.Errorf("Can't close compress writer: %s", err) + } + if err := pipeWriter.Close(); err != nil { + logrus.Errorf("Can't close pipe writer: %s", err) + } + }() + + // this buffer is needed for the duration of this piped stream + defer pools.BufioWriter32KPool.Put(ta.Buffer) + + // In general we log errors here but ignore them because + // during e.g. a diff operation the container can continue + // mutating the filesystem and we can see transient errors + // from this + + stat, err := os.Lstat(srcPath) + if err != nil { + return + } + + if !stat.IsDir() { + // We can't later join a non-dir with any includes because the + // 'walk' will error if "file/." is stat-ed and "file" is not a + // directory. So, we must split the source path and use the + // basename as the include. + if len(options.IncludeFiles) > 0 { + logrus.Warn("Tar: Can't archive a file with includes") + } + + dir, base := SplitPathDirEntry(srcPath) + srcPath = dir + options.IncludeFiles = []string{base} + } + + if len(options.IncludeFiles) == 0 { + options.IncludeFiles = []string{"."} + } + + seen := make(map[string]bool) + + for _, include := range options.IncludeFiles { + rebaseName := options.RebaseNames[include] + + walkRoot := getWalkRoot(srcPath, include) + filepath.Walk(walkRoot, func(filePath string, f os.FileInfo, err error) error { + if err != nil { + logrus.Errorf("Tar: Can't stat file %s to tar: %s", srcPath, err) + return nil + } + + relFilePath, err := filepath.Rel(srcPath, filePath) + if err != nil || (!options.IncludeSourceDir && relFilePath == "." && f.IsDir()) { + // Error getting relative path OR we are looking + // at the source directory path. Skip in both situations. + return nil + } + + if options.IncludeSourceDir && include == "." && relFilePath != "." { + relFilePath = strings.Join([]string{".", relFilePath}, string(filepath.Separator)) + } + + skip := false + + // If "include" is an exact match for the current file + // then even if there's an "excludePatterns" pattern that + // matches it, don't skip it. IOW, assume an explicit 'include' + // is asking for that file no matter what - which is true + // for some files, like .dockerignore and Dockerfile (sometimes) + if include != relFilePath { + skip, err = pm.Matches(relFilePath) + if err != nil { + logrus.Errorf("Error matching %s: %v", relFilePath, err) + return err + } + } + + if skip { + // If we want to skip this file and its a directory + // then we should first check to see if there's an + // excludes pattern (e.g. !dir/file) that starts with this + // dir. If so then we can't skip this dir. + + // Its not a dir then so we can just return/skip. + if !f.IsDir() { + return nil + } + + // No exceptions (!...) in patterns so just skip dir + if !pm.Exclusions() { + return filepath.SkipDir + } + + dirSlash := relFilePath + string(filepath.Separator) + + for _, pat := range pm.Patterns() { + if !pat.Exclusion() { + continue + } + if strings.HasPrefix(pat.String()+string(filepath.Separator), dirSlash) { + // found a match - so can't skip this dir + return nil + } + } + + // No matching exclusion dir so just skip dir + return filepath.SkipDir + } + + if seen[relFilePath] { + return nil + } + seen[relFilePath] = true + + // Rename the base resource. + if rebaseName != "" { + var replacement string + if rebaseName != string(filepath.Separator) { + // Special case the root directory to replace with an + // empty string instead so that we don't end up with + // double slashes in the paths. + replacement = rebaseName + } + + relFilePath = strings.Replace(relFilePath, include, replacement, 1) + } + + if err := ta.addTarFile(filePath, relFilePath); err != nil { + logrus.Errorf("Can't add file %s to tar: %s", filePath, err) + // if pipe is broken, stop writing tar stream to it + if err == io.ErrClosedPipe { + return err + } + } + return nil + }) + } + }() + + return pipeReader, nil +} + +// Unpack unpacks the decompressedArchive to dest with options. +func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) error { + tr := tar.NewReader(decompressedArchive) + trBuf := pools.BufioReader32KPool.Get(nil) + defer pools.BufioReader32KPool.Put(trBuf) + + var dirs []*tar.Header + idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps) + rootIDs := idMapping.RootPair() + whiteoutConverter, err := getWhiteoutConverter(options.WhiteoutFormat, options.InUserNS) + if err != nil { + return err + } + + // Iterate through the files in the archive. +loop: + for { + hdr, err := tr.Next() + if err == io.EOF { + // end of tar archive + break + } + if err != nil { + return err + } + + // ignore XGlobalHeader early to avoid creating parent directories for them + if hdr.Typeflag == tar.TypeXGlobalHeader { + logrus.Debugf("PAX Global Extended Headers found for %s and ignored", hdr.Name) + continue + } + + // Normalize name, for safety and for a simple is-root check + // This keeps "../" as-is, but normalizes "/../" to "/". Or Windows: + // This keeps "..\" as-is, but normalizes "\..\" to "\". + hdr.Name = filepath.Clean(hdr.Name) + + for _, exclude := range options.ExcludePatterns { + if strings.HasPrefix(hdr.Name, exclude) { + continue loop + } + } + + // After calling filepath.Clean(hdr.Name) above, hdr.Name will now be in + // the filepath format for the OS on which the daemon is running. Hence + // the check for a slash-suffix MUST be done in an OS-agnostic way. + if !strings.HasSuffix(hdr.Name, string(os.PathSeparator)) { + // Not the root directory, ensure that the parent directory exists + parent := filepath.Dir(hdr.Name) + parentPath := filepath.Join(dest, parent) + if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) { + err = idtools.MkdirAllAndChownNew(parentPath, 0755, rootIDs) + if err != nil { + return err + } + } + } + + path := filepath.Join(dest, hdr.Name) + rel, err := filepath.Rel(dest, path) + if err != nil { + return err + } + if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) { + return breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest)) + } + + // If path exits we almost always just want to remove and replace it + // The only exception is when it is a directory *and* the file from + // the layer is also a directory. Then we want to merge them (i.e. + // just apply the metadata from the layer). + if fi, err := os.Lstat(path); err == nil { + if options.NoOverwriteDirNonDir && fi.IsDir() && hdr.Typeflag != tar.TypeDir { + // If NoOverwriteDirNonDir is true then we cannot replace + // an existing directory with a non-directory from the archive. + return fmt.Errorf("cannot overwrite directory %q with non-directory %q", path, dest) + } + + if options.NoOverwriteDirNonDir && !fi.IsDir() && hdr.Typeflag == tar.TypeDir { + // If NoOverwriteDirNonDir is true then we cannot replace + // an existing non-directory with a directory from the archive. + return fmt.Errorf("cannot overwrite non-directory %q with directory %q", path, dest) + } + + if fi.IsDir() && hdr.Name == "." { + continue + } + + if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) { + if err := os.RemoveAll(path); err != nil { + return err + } + } + } + trBuf.Reset(tr) + + if err := remapIDs(idMapping, hdr); err != nil { + return err + } + + if whiteoutConverter != nil { + writeFile, err := whiteoutConverter.ConvertRead(hdr, path) + if err != nil { + return err + } + if !writeFile { + continue + } + } + + if err := createTarFile(path, dest, hdr, trBuf, !options.NoLchown, options.ChownOpts, options.InUserNS); err != nil { + return err + } + + // Directory mtimes must be handled at the end to avoid further + // file creation in them to modify the directory mtime + if hdr.Typeflag == tar.TypeDir { + dirs = append(dirs, hdr) + } + } + + for _, hdr := range dirs { + path := filepath.Join(dest, hdr.Name) + + if err := system.Chtimes(path, hdr.AccessTime, hdr.ModTime); err != nil { + return err + } + } + return nil +} + +// Untar reads a stream of bytes from `archive`, parses it as a tar archive, +// and unpacks it into the directory at `dest`. +// The archive may be compressed with one of the following algorithms: +// identity (uncompressed), gzip, bzip2, xz. +// FIXME: specify behavior when target path exists vs. doesn't exist. +func Untar(tarArchive io.Reader, dest string, options *TarOptions) error { + return untarHandler(tarArchive, dest, options, true) +} + +// UntarUncompressed reads a stream of bytes from `archive`, parses it as a tar archive, +// and unpacks it into the directory at `dest`. +// The archive must be an uncompressed stream. +func UntarUncompressed(tarArchive io.Reader, dest string, options *TarOptions) error { + return untarHandler(tarArchive, dest, options, false) +} + +// Handler for teasing out the automatic decompression +func untarHandler(tarArchive io.Reader, dest string, options *TarOptions, decompress bool) error { + if tarArchive == nil { + return fmt.Errorf("Empty archive") + } + dest = filepath.Clean(dest) + if options == nil { + options = &TarOptions{} + } + if options.ExcludePatterns == nil { + options.ExcludePatterns = []string{} + } + + r := tarArchive + if decompress { + decompressedArchive, err := DecompressStream(tarArchive) + if err != nil { + return err + } + defer decompressedArchive.Close() + r = decompressedArchive + } + + return Unpack(r, dest, options) +} + +// TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other. +// If either Tar or Untar fails, TarUntar aborts and returns the error. +func (archiver *Archiver) TarUntar(src, dst string) error { + logrus.Debugf("TarUntar(%s %s)", src, dst) + archive, err := TarWithOptions(src, &TarOptions{Compression: Uncompressed}) + if err != nil { + return err + } + defer archive.Close() + options := &TarOptions{ + UIDMaps: archiver.IDMapping.UIDs(), + GIDMaps: archiver.IDMapping.GIDs(), + } + return archiver.Untar(archive, dst, options) +} + +// UntarPath untar a file from path to a destination, src is the source tar file path. +func (archiver *Archiver) UntarPath(src, dst string) error { + archive, err := os.Open(src) + if err != nil { + return err + } + defer archive.Close() + options := &TarOptions{ + UIDMaps: archiver.IDMapping.UIDs(), + GIDMaps: archiver.IDMapping.GIDs(), + } + return archiver.Untar(archive, dst, options) +} + +// CopyWithTar creates a tar archive of filesystem path `src`, and +// unpacks it at filesystem path `dst`. +// The archive is streamed directly with fixed buffering and no +// intermediary disk IO. +func (archiver *Archiver) CopyWithTar(src, dst string) error { + srcSt, err := os.Stat(src) + if err != nil { + return err + } + if !srcSt.IsDir() { + return archiver.CopyFileWithTar(src, dst) + } + + // if this Archiver is set up with ID mapping we need to create + // the new destination directory with the remapped root UID/GID pair + // as owner + rootIDs := archiver.IDMapping.RootPair() + // Create dst, copy src's content into it + logrus.Debugf("Creating dest directory: %s", dst) + if err := idtools.MkdirAllAndChownNew(dst, 0755, rootIDs); err != nil { + return err + } + logrus.Debugf("Calling TarUntar(%s, %s)", src, dst) + return archiver.TarUntar(src, dst) +} + +// CopyFileWithTar emulates the behavior of the 'cp' command-line +// for a single file. It copies a regular file from path `src` to +// path `dst`, and preserves all its metadata. +func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) { + logrus.Debugf("CopyFileWithTar(%s, %s)", src, dst) + srcSt, err := os.Stat(src) + if err != nil { + return err + } + + if srcSt.IsDir() { + return fmt.Errorf("Can't copy a directory") + } + + // Clean up the trailing slash. This must be done in an operating + // system specific manner. + if dst[len(dst)-1] == os.PathSeparator { + dst = filepath.Join(dst, filepath.Base(src)) + } + // Create the holding directory if necessary + if err := system.MkdirAll(filepath.Dir(dst), 0700); err != nil { + return err + } + + r, w := io.Pipe() + errC := make(chan error, 1) + + go func() { + defer close(errC) + + errC <- func() error { + defer w.Close() + + srcF, err := os.Open(src) + if err != nil { + return err + } + defer srcF.Close() + + hdr, err := tar.FileInfoHeader(srcSt, "") + if err != nil { + return err + } + hdr.Format = tar.FormatPAX + hdr.ModTime = hdr.ModTime.Truncate(time.Second) + hdr.AccessTime = time.Time{} + hdr.ChangeTime = time.Time{} + hdr.Name = filepath.Base(dst) + hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode))) + + if err := remapIDs(archiver.IDMapping, hdr); err != nil { + return err + } + + tw := tar.NewWriter(w) + defer tw.Close() + if err := tw.WriteHeader(hdr); err != nil { + return err + } + if _, err := io.Copy(tw, srcF); err != nil { + return err + } + return nil + }() + }() + defer func() { + if er := <-errC; err == nil && er != nil { + err = er + } + }() + + err = archiver.Untar(r, filepath.Dir(dst), nil) + if err != nil { + r.CloseWithError(err) + } + return err +} + +// IdentityMapping returns the IdentityMapping of the archiver. +func (archiver *Archiver) IdentityMapping() *idtools.IdentityMapping { + return archiver.IDMapping +} + +func remapIDs(idMapping *idtools.IdentityMapping, hdr *tar.Header) error { + ids, err := idMapping.ToHost(idtools.Identity{UID: hdr.Uid, GID: hdr.Gid}) + hdr.Uid, hdr.Gid = ids.UID, ids.GID + return err +} + +// cmdStream executes a command, and returns its stdout as a stream. +// If the command fails to run or doesn't complete successfully, an error +// will be returned, including anything written on stderr. +func cmdStream(cmd *exec.Cmd, input io.Reader) (io.ReadCloser, error) { + cmd.Stdin = input + pipeR, pipeW := io.Pipe() + cmd.Stdout = pipeW + var errBuf bytes.Buffer + cmd.Stderr = &errBuf + + // Run the command and return the pipe + if err := cmd.Start(); err != nil { + return nil, err + } + + // Ensure the command has exited before we clean anything up + done := make(chan struct{}) + + // Copy stdout to the returned pipe + go func() { + if err := cmd.Wait(); err != nil { + pipeW.CloseWithError(fmt.Errorf("%s: %s", err, errBuf.String())) + } else { + pipeW.Close() + } + close(done) + }() + + return ioutils.NewReadCloserWrapper(pipeR, func() error { + // Close pipeR, and then wait for the command to complete before returning. We have to close pipeR first, as + // cmd.Wait waits for any non-file stdout/stderr/stdin to close. + err := pipeR.Close() + <-done + return err + }), nil +} + +// NewTempArchive reads the content of src into a temporary file, and returns the contents +// of that file as an archive. The archive can only be read once - as soon as reading completes, +// the file will be deleted. +func NewTempArchive(src io.Reader, dir string) (*TempArchive, error) { + f, err := ioutil.TempFile(dir, "") + if err != nil { + return nil, err + } + if _, err := io.Copy(f, src); err != nil { + return nil, err + } + if _, err := f.Seek(0, 0); err != nil { + return nil, err + } + st, err := f.Stat() + if err != nil { + return nil, err + } + size := st.Size() + return &TempArchive{File: f, Size: size}, nil +} + +// TempArchive is a temporary archive. The archive can only be read once - as soon as reading completes, +// the file will be deleted. +type TempArchive struct { + *os.File + Size int64 // Pre-computed from Stat().Size() as a convenience + read int64 + closed bool +} + +// Close closes the underlying file if it's still open, or does a no-op +// to allow callers to try to close the TempArchive multiple times safely. +func (archive *TempArchive) Close() error { + if archive.closed { + return nil + } + + archive.closed = true + + return archive.File.Close() +} + +func (archive *TempArchive) Read(data []byte) (int, error) { + n, err := archive.File.Read(data) + archive.read += int64(n) + if err != nil || archive.read == archive.Size { + archive.Close() + os.Remove(archive.File.Name()) + } + return n, err +} diff --git a/vendor/github.com/docker/docker/pkg/archive/archive_linux.go b/vendor/github.com/docker/docker/pkg/archive/archive_linux.go new file mode 100644 index 0000000000..0a3cc1f92b --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/archive_linux.go @@ -0,0 +1,100 @@ +package archive // import "github.com/docker/docker/pkg/archive" + +import ( + "archive/tar" + "os" + "path/filepath" + "strings" + + "github.com/docker/docker/pkg/system" + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +func getWhiteoutConverter(format WhiteoutFormat, inUserNS bool) (tarWhiteoutConverter, error) { + if format == OverlayWhiteoutFormat { + if inUserNS { + return nil, errors.New("specifying OverlayWhiteoutFormat is not allowed in userns") + } + return overlayWhiteoutConverter{}, nil + } + return nil, nil +} + +type overlayWhiteoutConverter struct { +} + +func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, err error) { + // convert whiteouts to AUFS format + if fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0 { + // we just rename the file and make it normal + dir, filename := filepath.Split(hdr.Name) + hdr.Name = filepath.Join(dir, WhiteoutPrefix+filename) + hdr.Mode = 0600 + hdr.Typeflag = tar.TypeReg + hdr.Size = 0 + } + + if fi.Mode()&os.ModeDir != 0 { + // convert opaque dirs to AUFS format by writing an empty file with the prefix + opaque, err := system.Lgetxattr(path, "trusted.overlay.opaque") + if err != nil { + return nil, err + } + if len(opaque) == 1 && opaque[0] == 'y' { + if hdr.Xattrs != nil { + delete(hdr.Xattrs, "trusted.overlay.opaque") + } + + // create a header for the whiteout file + // it should inherit some properties from the parent, but be a regular file + wo = &tar.Header{ + Typeflag: tar.TypeReg, + Mode: hdr.Mode & int64(os.ModePerm), + Name: filepath.Join(hdr.Name, WhiteoutOpaqueDir), + Size: 0, + Uid: hdr.Uid, + Uname: hdr.Uname, + Gid: hdr.Gid, + Gname: hdr.Gname, + AccessTime: hdr.AccessTime, + ChangeTime: hdr.ChangeTime, + } + } + } + + return +} + +func (c overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, error) { + base := filepath.Base(path) + dir := filepath.Dir(path) + + // if a directory is marked as opaque by the AUFS special file, we need to translate that to overlay + if base == WhiteoutOpaqueDir { + err := unix.Setxattr(dir, "trusted.overlay.opaque", []byte{'y'}, 0) + if err != nil { + return false, errors.Wrapf(err, "setxattr(%q, trusted.overlay.opaque=y)", dir) + } + // don't write the file itself + return false, err + } + + // if a file was deleted and we are using overlay, we need to create a character device + if strings.HasPrefix(base, WhiteoutPrefix) { + originalBase := base[len(WhiteoutPrefix):] + originalPath := filepath.Join(dir, originalBase) + + if err := unix.Mknod(originalPath, unix.S_IFCHR, 0); err != nil { + return false, errors.Wrapf(err, "failed to mknod(%q, S_IFCHR, 0)", originalPath) + } + if err := os.Chown(originalPath, hdr.Uid, hdr.Gid); err != nil { + return false, err + } + + // don't write the file itself + return false, nil + } + + return true, nil +} diff --git a/vendor/github.com/docker/docker/pkg/archive/archive_other.go b/vendor/github.com/docker/docker/pkg/archive/archive_other.go new file mode 100644 index 0000000000..2a3dc95398 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/archive_other.go @@ -0,0 +1,7 @@ +// +build !linux + +package archive // import "github.com/docker/docker/pkg/archive" + +func getWhiteoutConverter(format WhiteoutFormat, inUserNS bool) (tarWhiteoutConverter, error) { + return nil, nil +} diff --git a/vendor/github.com/docker/docker/pkg/archive/archive_unix.go b/vendor/github.com/docker/docker/pkg/archive/archive_unix.go new file mode 100644 index 0000000000..0b92bb0f4a --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/archive_unix.go @@ -0,0 +1,115 @@ +// +build !windows + +package archive // import "github.com/docker/docker/pkg/archive" + +import ( + "archive/tar" + "errors" + "os" + "path/filepath" + "strings" + "syscall" + + "github.com/containerd/containerd/sys" + "github.com/docker/docker/pkg/idtools" + "github.com/docker/docker/pkg/system" + "golang.org/x/sys/unix" +) + +// fixVolumePathPrefix does platform specific processing to ensure that if +// the path being passed in is not in a volume path format, convert it to one. +func fixVolumePathPrefix(srcPath string) string { + return srcPath +} + +// getWalkRoot calculates the root path when performing a TarWithOptions. +// We use a separate function as this is platform specific. On Linux, we +// can't use filepath.Join(srcPath,include) because this will clean away +// a trailing "." or "/" which may be important. +func getWalkRoot(srcPath string, include string) string { + return strings.TrimSuffix(srcPath, string(filepath.Separator)) + string(filepath.Separator) + include +} + +// CanonicalTarNameForPath returns platform-specific filepath +// to canonical posix-style path for tar archival. p is relative +// path. +func CanonicalTarNameForPath(p string) string { + return p // already unix-style +} + +// chmodTarEntry is used to adjust the file permissions used in tar header based +// on the platform the archival is done. + +func chmodTarEntry(perm os.FileMode) os.FileMode { + return perm // noop for unix as golang APIs provide perm bits correctly +} + +func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) (err error) { + s, ok := stat.(*syscall.Stat_t) + + if ok { + // Currently go does not fill in the major/minors + if s.Mode&unix.S_IFBLK != 0 || + s.Mode&unix.S_IFCHR != 0 { + hdr.Devmajor = int64(unix.Major(uint64(s.Rdev))) // nolint: unconvert + hdr.Devminor = int64(unix.Minor(uint64(s.Rdev))) // nolint: unconvert + } + } + + return +} + +func getInodeFromStat(stat interface{}) (inode uint64, err error) { + s, ok := stat.(*syscall.Stat_t) + + if ok { + inode = s.Ino + } + + return +} + +func getFileUIDGID(stat interface{}) (idtools.Identity, error) { + s, ok := stat.(*syscall.Stat_t) + + if !ok { + return idtools.Identity{}, errors.New("cannot convert stat value to syscall.Stat_t") + } + return idtools.Identity{UID: int(s.Uid), GID: int(s.Gid)}, nil +} + +// handleTarTypeBlockCharFifo is an OS-specific helper function used by +// createTarFile to handle the following types of header: Block; Char; Fifo +func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error { + mode := uint32(hdr.Mode & 07777) + switch hdr.Typeflag { + case tar.TypeBlock: + mode |= unix.S_IFBLK + case tar.TypeChar: + mode |= unix.S_IFCHR + case tar.TypeFifo: + mode |= unix.S_IFIFO + } + + err := system.Mknod(path, mode, int(system.Mkdev(hdr.Devmajor, hdr.Devminor))) + if errors.Is(err, syscall.EPERM) && sys.RunningInUserNS() { + // In most cases, cannot create a device if running in user namespace + err = nil + } + return err +} + +func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error { + if hdr.Typeflag == tar.TypeLink { + if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) { + if err := os.Chmod(path, hdrInfo.Mode()); err != nil { + return err + } + } + } else if hdr.Typeflag != tar.TypeSymlink { + if err := os.Chmod(path, hdrInfo.Mode()); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/docker/docker/pkg/archive/archive_windows.go b/vendor/github.com/docker/docker/pkg/archive/archive_windows.go new file mode 100644 index 0000000000..7260174bfb --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/archive_windows.go @@ -0,0 +1,67 @@ +package archive // import "github.com/docker/docker/pkg/archive" + +import ( + "archive/tar" + "os" + "path/filepath" + + "github.com/docker/docker/pkg/idtools" + "github.com/docker/docker/pkg/longpath" +) + +// fixVolumePathPrefix does platform specific processing to ensure that if +// the path being passed in is not in a volume path format, convert it to one. +func fixVolumePathPrefix(srcPath string) string { + return longpath.AddPrefix(srcPath) +} + +// getWalkRoot calculates the root path when performing a TarWithOptions. +// We use a separate function as this is platform specific. +func getWalkRoot(srcPath string, include string) string { + return filepath.Join(srcPath, include) +} + +// CanonicalTarNameForPath returns platform-specific filepath +// to canonical posix-style path for tar archival. p is relative +// path. +func CanonicalTarNameForPath(p string) string { + return filepath.ToSlash(p) +} + +// chmodTarEntry is used to adjust the file permissions used in tar header based +// on the platform the archival is done. +func chmodTarEntry(perm os.FileMode) os.FileMode { + // perm &= 0755 // this 0-ed out tar flags (like link, regular file, directory marker etc.) + permPart := perm & os.ModePerm + noPermPart := perm &^ os.ModePerm + // Add the x bit: make everything +x from windows + permPart |= 0111 + permPart &= 0755 + + return noPermPart | permPart +} + +func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) (err error) { + // do nothing. no notion of Rdev, Nlink in stat on Windows + return +} + +func getInodeFromStat(stat interface{}) (inode uint64, err error) { + // do nothing. no notion of Inode in stat on Windows + return +} + +// handleTarTypeBlockCharFifo is an OS-specific helper function used by +// createTarFile to handle the following types of header: Block; Char; Fifo +func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error { + return nil +} + +func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error { + return nil +} + +func getFileUIDGID(stat interface{}) (idtools.Identity, error) { + // no notion of file ownership mapping yet on Windows + return idtools.Identity{UID: 0, GID: 0}, nil +} diff --git a/vendor/github.com/docker/docker/pkg/archive/changes.go b/vendor/github.com/docker/docker/pkg/archive/changes.go new file mode 100644 index 0000000000..aedb91b035 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/changes.go @@ -0,0 +1,445 @@ +package archive // import "github.com/docker/docker/pkg/archive" + +import ( + "archive/tar" + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + "syscall" + "time" + + "github.com/docker/docker/pkg/idtools" + "github.com/docker/docker/pkg/pools" + "github.com/docker/docker/pkg/system" + "github.com/sirupsen/logrus" +) + +// ChangeType represents the change type. +type ChangeType int + +const ( + // ChangeModify represents the modify operation. + ChangeModify = iota + // ChangeAdd represents the add operation. + ChangeAdd + // ChangeDelete represents the delete operation. + ChangeDelete +) + +func (c ChangeType) String() string { + switch c { + case ChangeModify: + return "C" + case ChangeAdd: + return "A" + case ChangeDelete: + return "D" + } + return "" +} + +// Change represents a change, it wraps the change type and path. +// It describes changes of the files in the path respect to the +// parent layers. The change could be modify, add, delete. +// This is used for layer diff. +type Change struct { + Path string + Kind ChangeType +} + +func (change *Change) String() string { + return fmt.Sprintf("%s %s", change.Kind, change.Path) +} + +// for sort.Sort +type changesByPath []Change + +func (c changesByPath) Less(i, j int) bool { return c[i].Path < c[j].Path } +func (c changesByPath) Len() int { return len(c) } +func (c changesByPath) Swap(i, j int) { c[j], c[i] = c[i], c[j] } + +// Gnu tar doesn't have sub-second mtime precision. The go tar +// writer (1.10+) does when using PAX format, but we round times to seconds +// to ensure archives have the same hashes for backwards compatibility. +// See https://github.com/moby/moby/pull/35739/commits/fb170206ba12752214630b269a40ac7be6115ed4. +// +// Non-sub-second is problematic when we apply changes via tar +// files. We handle this by comparing for exact times, *or* same +// second count and either a or b having exactly 0 nanoseconds +func sameFsTime(a, b time.Time) bool { + return a.Equal(b) || + (a.Unix() == b.Unix() && + (a.Nanosecond() == 0 || b.Nanosecond() == 0)) +} + +func sameFsTimeSpec(a, b syscall.Timespec) bool { + return a.Sec == b.Sec && + (a.Nsec == b.Nsec || a.Nsec == 0 || b.Nsec == 0) +} + +// Changes walks the path rw and determines changes for the files in the path, +// with respect to the parent layers +func Changes(layers []string, rw string) ([]Change, error) { + return changes(layers, rw, aufsDeletedFile, aufsMetadataSkip) +} + +func aufsMetadataSkip(path string) (skip bool, err error) { + skip, err = filepath.Match(string(os.PathSeparator)+WhiteoutMetaPrefix+"*", path) + if err != nil { + skip = true + } + return +} + +func aufsDeletedFile(root, path string, fi os.FileInfo) (string, error) { + f := filepath.Base(path) + + // If there is a whiteout, then the file was removed + if strings.HasPrefix(f, WhiteoutPrefix) { + originalFile := f[len(WhiteoutPrefix):] + return filepath.Join(filepath.Dir(path), originalFile), nil + } + + return "", nil +} + +type skipChange func(string) (bool, error) +type deleteChange func(string, string, os.FileInfo) (string, error) + +func changes(layers []string, rw string, dc deleteChange, sc skipChange) ([]Change, error) { + var ( + changes []Change + changedDirs = make(map[string]struct{}) + ) + + err := filepath.Walk(rw, func(path string, f os.FileInfo, err error) error { + if err != nil { + return err + } + + // Rebase path + path, err = filepath.Rel(rw, path) + if err != nil { + return err + } + + // As this runs on the daemon side, file paths are OS specific. + path = filepath.Join(string(os.PathSeparator), path) + + // Skip root + if path == string(os.PathSeparator) { + return nil + } + + if sc != nil { + if skip, err := sc(path); skip { + return err + } + } + + change := Change{ + Path: path, + } + + deletedFile, err := dc(rw, path, f) + if err != nil { + return err + } + + // Find out what kind of modification happened + if deletedFile != "" { + change.Path = deletedFile + change.Kind = ChangeDelete + } else { + // Otherwise, the file was added + change.Kind = ChangeAdd + + // ...Unless it already existed in a top layer, in which case, it's a modification + for _, layer := range layers { + stat, err := os.Stat(filepath.Join(layer, path)) + if err != nil && !os.IsNotExist(err) { + return err + } + if err == nil { + // The file existed in the top layer, so that's a modification + + // However, if it's a directory, maybe it wasn't actually modified. + // If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar + if stat.IsDir() && f.IsDir() { + if f.Size() == stat.Size() && f.Mode() == stat.Mode() && sameFsTime(f.ModTime(), stat.ModTime()) { + // Both directories are the same, don't record the change + return nil + } + } + change.Kind = ChangeModify + break + } + } + } + + // If /foo/bar/file.txt is modified, then /foo/bar must be part of the changed files. + // This block is here to ensure the change is recorded even if the + // modify time, mode and size of the parent directory in the rw and ro layers are all equal. + // Check https://github.com/docker/docker/pull/13590 for details. + if f.IsDir() { + changedDirs[path] = struct{}{} + } + if change.Kind == ChangeAdd || change.Kind == ChangeDelete { + parent := filepath.Dir(path) + if _, ok := changedDirs[parent]; !ok && parent != "/" { + changes = append(changes, Change{Path: parent, Kind: ChangeModify}) + changedDirs[parent] = struct{}{} + } + } + + // Record change + changes = append(changes, change) + return nil + }) + if err != nil && !os.IsNotExist(err) { + return nil, err + } + return changes, nil +} + +// FileInfo describes the information of a file. +type FileInfo struct { + parent *FileInfo + name string + stat *system.StatT + children map[string]*FileInfo + capability []byte + added bool +} + +// LookUp looks up the file information of a file. +func (info *FileInfo) LookUp(path string) *FileInfo { + // As this runs on the daemon side, file paths are OS specific. + parent := info + if path == string(os.PathSeparator) { + return info + } + + pathElements := strings.Split(path, string(os.PathSeparator)) + for _, elem := range pathElements { + if elem != "" { + child := parent.children[elem] + if child == nil { + return nil + } + parent = child + } + } + return parent +} + +func (info *FileInfo) path() string { + if info.parent == nil { + // As this runs on the daemon side, file paths are OS specific. + return string(os.PathSeparator) + } + return filepath.Join(info.parent.path(), info.name) +} + +func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) { + + sizeAtEntry := len(*changes) + + if oldInfo == nil { + // add + change := Change{ + Path: info.path(), + Kind: ChangeAdd, + } + *changes = append(*changes, change) + info.added = true + } + + // We make a copy so we can modify it to detect additions + // also, we only recurse on the old dir if the new info is a directory + // otherwise any previous delete/change is considered recursive + oldChildren := make(map[string]*FileInfo) + if oldInfo != nil && info.isDir() { + for k, v := range oldInfo.children { + oldChildren[k] = v + } + } + + for name, newChild := range info.children { + oldChild := oldChildren[name] + if oldChild != nil { + // change? + oldStat := oldChild.stat + newStat := newChild.stat + // Note: We can't compare inode or ctime or blocksize here, because these change + // when copying a file into a container. However, that is not generally a problem + // because any content change will change mtime, and any status change should + // be visible when actually comparing the stat fields. The only time this + // breaks down is if some code intentionally hides a change by setting + // back mtime + if statDifferent(oldStat, newStat) || + !bytes.Equal(oldChild.capability, newChild.capability) { + change := Change{ + Path: newChild.path(), + Kind: ChangeModify, + } + *changes = append(*changes, change) + newChild.added = true + } + + // Remove from copy so we can detect deletions + delete(oldChildren, name) + } + + newChild.addChanges(oldChild, changes) + } + for _, oldChild := range oldChildren { + // delete + change := Change{ + Path: oldChild.path(), + Kind: ChangeDelete, + } + *changes = append(*changes, change) + } + + // If there were changes inside this directory, we need to add it, even if the directory + // itself wasn't changed. This is needed to properly save and restore filesystem permissions. + // As this runs on the daemon side, file paths are OS specific. + if len(*changes) > sizeAtEntry && info.isDir() && !info.added && info.path() != string(os.PathSeparator) { + change := Change{ + Path: info.path(), + Kind: ChangeModify, + } + // Let's insert the directory entry before the recently added entries located inside this dir + *changes = append(*changes, change) // just to resize the slice, will be overwritten + copy((*changes)[sizeAtEntry+1:], (*changes)[sizeAtEntry:]) + (*changes)[sizeAtEntry] = change + } + +} + +// Changes add changes to file information. +func (info *FileInfo) Changes(oldInfo *FileInfo) []Change { + var changes []Change + + info.addChanges(oldInfo, &changes) + + return changes +} + +func newRootFileInfo() *FileInfo { + // As this runs on the daemon side, file paths are OS specific. + root := &FileInfo{ + name: string(os.PathSeparator), + children: make(map[string]*FileInfo), + } + return root +} + +// ChangesDirs compares two directories and generates an array of Change objects describing the changes. +// If oldDir is "", then all files in newDir will be Add-Changes. +func ChangesDirs(newDir, oldDir string) ([]Change, error) { + var ( + oldRoot, newRoot *FileInfo + ) + if oldDir == "" { + emptyDir, err := ioutil.TempDir("", "empty") + if err != nil { + return nil, err + } + defer os.Remove(emptyDir) + oldDir = emptyDir + } + oldRoot, newRoot, err := collectFileInfoForChanges(oldDir, newDir) + if err != nil { + return nil, err + } + + return newRoot.Changes(oldRoot), nil +} + +// ChangesSize calculates the size in bytes of the provided changes, based on newDir. +func ChangesSize(newDir string, changes []Change) int64 { + var ( + size int64 + sf = make(map[uint64]struct{}) + ) + for _, change := range changes { + if change.Kind == ChangeModify || change.Kind == ChangeAdd { + file := filepath.Join(newDir, change.Path) + fileInfo, err := os.Lstat(file) + if err != nil { + logrus.Errorf("Can not stat %q: %s", file, err) + continue + } + + if fileInfo != nil && !fileInfo.IsDir() { + if hasHardlinks(fileInfo) { + inode := getIno(fileInfo) + if _, ok := sf[inode]; !ok { + size += fileInfo.Size() + sf[inode] = struct{}{} + } + } else { + size += fileInfo.Size() + } + } + } + } + return size +} + +// ExportChanges produces an Archive from the provided changes, relative to dir. +func ExportChanges(dir string, changes []Change, uidMaps, gidMaps []idtools.IDMap) (io.ReadCloser, error) { + reader, writer := io.Pipe() + go func() { + ta := newTarAppender(idtools.NewIDMappingsFromMaps(uidMaps, gidMaps), writer, nil) + + // this buffer is needed for the duration of this piped stream + defer pools.BufioWriter32KPool.Put(ta.Buffer) + + sort.Sort(changesByPath(changes)) + + // In general we log errors here but ignore them because + // during e.g. a diff operation the container can continue + // mutating the filesystem and we can see transient errors + // from this + for _, change := range changes { + if change.Kind == ChangeDelete { + whiteOutDir := filepath.Dir(change.Path) + whiteOutBase := filepath.Base(change.Path) + whiteOut := filepath.Join(whiteOutDir, WhiteoutPrefix+whiteOutBase) + timestamp := time.Now() + hdr := &tar.Header{ + Name: whiteOut[1:], + Size: 0, + ModTime: timestamp, + AccessTime: timestamp, + ChangeTime: timestamp, + } + if err := ta.TarWriter.WriteHeader(hdr); err != nil { + logrus.Debugf("Can't write whiteout header: %s", err) + } + } else { + path := filepath.Join(dir, change.Path) + if err := ta.addTarFile(path, change.Path[1:]); err != nil { + logrus.Debugf("Can't add file %s to tar: %s", path, err) + } + } + } + + // Make sure to check the error on Close. + if err := ta.TarWriter.Close(); err != nil { + logrus.Debugf("Can't close layer: %s", err) + } + if err := writer.Close(); err != nil { + logrus.Debugf("failed close Changes writer: %s", err) + } + }() + return reader, nil +} diff --git a/vendor/github.com/docker/docker/pkg/archive/changes_linux.go b/vendor/github.com/docker/docker/pkg/archive/changes_linux.go new file mode 100644 index 0000000000..f8792b3d4e --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/changes_linux.go @@ -0,0 +1,286 @@ +package archive // import "github.com/docker/docker/pkg/archive" + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "sort" + "syscall" + "unsafe" + + "github.com/docker/docker/pkg/system" + "golang.org/x/sys/unix" +) + +// walker is used to implement collectFileInfoForChanges on linux. Where this +// method in general returns the entire contents of two directory trees, we +// optimize some FS calls out on linux. In particular, we take advantage of the +// fact that getdents(2) returns the inode of each file in the directory being +// walked, which, when walking two trees in parallel to generate a list of +// changes, can be used to prune subtrees without ever having to lstat(2) them +// directly. Eliminating stat calls in this way can save up to seconds on large +// images. +type walker struct { + dir1 string + dir2 string + root1 *FileInfo + root2 *FileInfo +} + +// collectFileInfoForChanges returns a complete representation of the trees +// rooted at dir1 and dir2, with one important exception: any subtree or +// leaf where the inode and device numbers are an exact match between dir1 +// and dir2 will be pruned from the results. This method is *only* to be used +// to generating a list of changes between the two directories, as it does not +// reflect the full contents. +func collectFileInfoForChanges(dir1, dir2 string) (*FileInfo, *FileInfo, error) { + w := &walker{ + dir1: dir1, + dir2: dir2, + root1: newRootFileInfo(), + root2: newRootFileInfo(), + } + + i1, err := os.Lstat(w.dir1) + if err != nil { + return nil, nil, err + } + i2, err := os.Lstat(w.dir2) + if err != nil { + return nil, nil, err + } + + if err := w.walk("/", i1, i2); err != nil { + return nil, nil, err + } + + return w.root1, w.root2, nil +} + +// Given a FileInfo, its path info, and a reference to the root of the tree +// being constructed, register this file with the tree. +func walkchunk(path string, fi os.FileInfo, dir string, root *FileInfo) error { + if fi == nil { + return nil + } + parent := root.LookUp(filepath.Dir(path)) + if parent == nil { + return fmt.Errorf("walkchunk: Unexpectedly no parent for %s", path) + } + info := &FileInfo{ + name: filepath.Base(path), + children: make(map[string]*FileInfo), + parent: parent, + } + cpath := filepath.Join(dir, path) + stat, err := system.FromStatT(fi.Sys().(*syscall.Stat_t)) + if err != nil { + return err + } + info.stat = stat + info.capability, _ = system.Lgetxattr(cpath, "security.capability") // lgetxattr(2): fs access + parent.children[info.name] = info + return nil +} + +// Walk a subtree rooted at the same path in both trees being iterated. For +// example, /docker/overlay/1234/a/b/c/d and /docker/overlay/8888/a/b/c/d +func (w *walker) walk(path string, i1, i2 os.FileInfo) (err error) { + // Register these nodes with the return trees, unless we're still at the + // (already-created) roots: + if path != "/" { + if err := walkchunk(path, i1, w.dir1, w.root1); err != nil { + return err + } + if err := walkchunk(path, i2, w.dir2, w.root2); err != nil { + return err + } + } + + is1Dir := i1 != nil && i1.IsDir() + is2Dir := i2 != nil && i2.IsDir() + + sameDevice := false + if i1 != nil && i2 != nil { + si1 := i1.Sys().(*syscall.Stat_t) + si2 := i2.Sys().(*syscall.Stat_t) + if si1.Dev == si2.Dev { + sameDevice = true + } + } + + // If these files are both non-existent, or leaves (non-dirs), we are done. + if !is1Dir && !is2Dir { + return nil + } + + // Fetch the names of all the files contained in both directories being walked: + var names1, names2 []nameIno + if is1Dir { + names1, err = readdirnames(filepath.Join(w.dir1, path)) // getdents(2): fs access + if err != nil { + return err + } + } + if is2Dir { + names2, err = readdirnames(filepath.Join(w.dir2, path)) // getdents(2): fs access + if err != nil { + return err + } + } + + // We have lists of the files contained in both parallel directories, sorted + // in the same order. Walk them in parallel, generating a unique merged list + // of all items present in either or both directories. + var names []string + ix1 := 0 + ix2 := 0 + + for { + if ix1 >= len(names1) { + break + } + if ix2 >= len(names2) { + break + } + + ni1 := names1[ix1] + ni2 := names2[ix2] + + switch bytes.Compare([]byte(ni1.name), []byte(ni2.name)) { + case -1: // ni1 < ni2 -- advance ni1 + // we will not encounter ni1 in names2 + names = append(names, ni1.name) + ix1++ + case 0: // ni1 == ni2 + if ni1.ino != ni2.ino || !sameDevice { + names = append(names, ni1.name) + } + ix1++ + ix2++ + case 1: // ni1 > ni2 -- advance ni2 + // we will not encounter ni2 in names1 + names = append(names, ni2.name) + ix2++ + } + } + for ix1 < len(names1) { + names = append(names, names1[ix1].name) + ix1++ + } + for ix2 < len(names2) { + names = append(names, names2[ix2].name) + ix2++ + } + + // For each of the names present in either or both of the directories being + // iterated, stat the name under each root, and recurse the pair of them: + for _, name := range names { + fname := filepath.Join(path, name) + var cInfo1, cInfo2 os.FileInfo + if is1Dir { + cInfo1, err = os.Lstat(filepath.Join(w.dir1, fname)) // lstat(2): fs access + if err != nil && !os.IsNotExist(err) { + return err + } + } + if is2Dir { + cInfo2, err = os.Lstat(filepath.Join(w.dir2, fname)) // lstat(2): fs access + if err != nil && !os.IsNotExist(err) { + return err + } + } + if err = w.walk(fname, cInfo1, cInfo2); err != nil { + return err + } + } + return nil +} + +// {name,inode} pairs used to support the early-pruning logic of the walker type +type nameIno struct { + name string + ino uint64 +} + +type nameInoSlice []nameIno + +func (s nameInoSlice) Len() int { return len(s) } +func (s nameInoSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s nameInoSlice) Less(i, j int) bool { return s[i].name < s[j].name } + +// readdirnames is a hacked-apart version of the Go stdlib code, exposing inode +// numbers further up the stack when reading directory contents. Unlike +// os.Readdirnames, which returns a list of filenames, this function returns a +// list of {filename,inode} pairs. +func readdirnames(dirname string) (names []nameIno, err error) { + var ( + size = 100 + buf = make([]byte, 4096) + nbuf int + bufp int + nb int + ) + + f, err := os.Open(dirname) + if err != nil { + return nil, err + } + defer f.Close() + + names = make([]nameIno, 0, size) // Empty with room to grow. + for { + // Refill the buffer if necessary + if bufp >= nbuf { + bufp = 0 + nbuf, err = unix.ReadDirent(int(f.Fd()), buf) // getdents on linux + if nbuf < 0 { + nbuf = 0 + } + if err != nil { + return nil, os.NewSyscallError("readdirent", err) + } + if nbuf <= 0 { + break // EOF + } + } + + // Drain the buffer + nb, names = parseDirent(buf[bufp:nbuf], names) + bufp += nb + } + + sl := nameInoSlice(names) + sort.Sort(sl) + return sl, nil +} + +// parseDirent is a minor modification of unix.ParseDirent (linux version) +// which returns {name,inode} pairs instead of just names. +func parseDirent(buf []byte, names []nameIno) (consumed int, newnames []nameIno) { + origlen := len(buf) + for len(buf) > 0 { + dirent := (*unix.Dirent)(unsafe.Pointer(&buf[0])) + buf = buf[dirent.Reclen:] + if dirent.Ino == 0 { // File absent in directory. + continue + } + bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0])) + var name = string(bytes[0:clen(bytes[:])]) + if name == "." || name == ".." { // Useless names + continue + } + names = append(names, nameIno{name, dirent.Ino}) + } + return origlen - len(buf), names +} + +func clen(n []byte) int { + for i := 0; i < len(n); i++ { + if n[i] == 0 { + return i + } + } + return len(n) +} diff --git a/vendor/github.com/docker/docker/pkg/archive/changes_other.go b/vendor/github.com/docker/docker/pkg/archive/changes_other.go new file mode 100644 index 0000000000..ba744741cd --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/changes_other.go @@ -0,0 +1,97 @@ +// +build !linux + +package archive // import "github.com/docker/docker/pkg/archive" + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/docker/docker/pkg/system" +) + +func collectFileInfoForChanges(oldDir, newDir string) (*FileInfo, *FileInfo, error) { + var ( + oldRoot, newRoot *FileInfo + err1, err2 error + errs = make(chan error, 2) + ) + go func() { + oldRoot, err1 = collectFileInfo(oldDir) + errs <- err1 + }() + go func() { + newRoot, err2 = collectFileInfo(newDir) + errs <- err2 + }() + + // block until both routines have returned + for i := 0; i < 2; i++ { + if err := <-errs; err != nil { + return nil, nil, err + } + } + + return oldRoot, newRoot, nil +} + +func collectFileInfo(sourceDir string) (*FileInfo, error) { + root := newRootFileInfo() + + err := filepath.Walk(sourceDir, func(path string, f os.FileInfo, err error) error { + if err != nil { + return err + } + + // Rebase path + relPath, err := filepath.Rel(sourceDir, path) + if err != nil { + return err + } + + // As this runs on the daemon side, file paths are OS specific. + relPath = filepath.Join(string(os.PathSeparator), relPath) + + // See https://github.com/golang/go/issues/9168 - bug in filepath.Join. + // Temporary workaround. If the returned path starts with two backslashes, + // trim it down to a single backslash. Only relevant on Windows. + if runtime.GOOS == "windows" { + if strings.HasPrefix(relPath, `\\`) { + relPath = relPath[1:] + } + } + + if relPath == string(os.PathSeparator) { + return nil + } + + parent := root.LookUp(filepath.Dir(relPath)) + if parent == nil { + return fmt.Errorf("collectFileInfo: Unexpectedly no parent for %s", relPath) + } + + info := &FileInfo{ + name: filepath.Base(relPath), + children: make(map[string]*FileInfo), + parent: parent, + } + + s, err := system.Lstat(path) + if err != nil { + return err + } + info.stat = s + + info.capability, _ = system.Lgetxattr(path, "security.capability") + + parent.children[info.name] = info + + return nil + }) + if err != nil { + return nil, err + } + return root, nil +} diff --git a/vendor/github.com/docker/docker/pkg/archive/changes_unix.go b/vendor/github.com/docker/docker/pkg/archive/changes_unix.go new file mode 100644 index 0000000000..06217b7161 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/changes_unix.go @@ -0,0 +1,43 @@ +// +build !windows + +package archive // import "github.com/docker/docker/pkg/archive" + +import ( + "os" + "syscall" + + "github.com/docker/docker/pkg/system" + "golang.org/x/sys/unix" +) + +func statDifferent(oldStat *system.StatT, newStat *system.StatT) bool { + // Don't look at size for dirs, its not a good measure of change + if oldStat.Mode() != newStat.Mode() || + oldStat.UID() != newStat.UID() || + oldStat.GID() != newStat.GID() || + oldStat.Rdev() != newStat.Rdev() || + // Don't look at size or modification time for dirs, its not a good + // measure of change. See https://github.com/moby/moby/issues/9874 + // for a description of the issue with modification time, and + // https://github.com/moby/moby/pull/11422 for the change. + // (Note that in the Windows implementation of this function, + // modification time IS taken as a change). See + // https://github.com/moby/moby/pull/37982 for more information. + (oldStat.Mode()&unix.S_IFDIR != unix.S_IFDIR && + (!sameFsTimeSpec(oldStat.Mtim(), newStat.Mtim()) || (oldStat.Size() != newStat.Size()))) { + return true + } + return false +} + +func (info *FileInfo) isDir() bool { + return info.parent == nil || info.stat.Mode()&unix.S_IFDIR != 0 +} + +func getIno(fi os.FileInfo) uint64 { + return fi.Sys().(*syscall.Stat_t).Ino +} + +func hasHardlinks(fi os.FileInfo) bool { + return fi.Sys().(*syscall.Stat_t).Nlink > 1 +} diff --git a/vendor/github.com/docker/docker/pkg/archive/changes_windows.go b/vendor/github.com/docker/docker/pkg/archive/changes_windows.go new file mode 100644 index 0000000000..9906685e4b --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/changes_windows.go @@ -0,0 +1,34 @@ +package archive // import "github.com/docker/docker/pkg/archive" + +import ( + "os" + + "github.com/docker/docker/pkg/system" +) + +func statDifferent(oldStat *system.StatT, newStat *system.StatT) bool { + // Note there is slight difference between the Linux and Windows + // implementations here. Due to https://github.com/moby/moby/issues/9874, + // and the fix at https://github.com/moby/moby/pull/11422, Linux does not + // consider a change to the directory time as a change. Windows on NTFS + // does. See https://github.com/moby/moby/pull/37982 for more information. + + if !sameFsTime(oldStat.Mtim(), newStat.Mtim()) || + oldStat.Mode() != newStat.Mode() || + oldStat.Size() != newStat.Size() && !oldStat.Mode().IsDir() { + return true + } + return false +} + +func (info *FileInfo) isDir() bool { + return info.parent == nil || info.stat.Mode().IsDir() +} + +func getIno(fi os.FileInfo) (inode uint64) { + return +} + +func hasHardlinks(fi os.FileInfo) bool { + return false +} diff --git a/vendor/github.com/docker/docker/pkg/archive/copy.go b/vendor/github.com/docker/docker/pkg/archive/copy.go new file mode 100644 index 0000000000..57fddac078 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/copy.go @@ -0,0 +1,480 @@ +package archive // import "github.com/docker/docker/pkg/archive" + +import ( + "archive/tar" + "errors" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/docker/docker/pkg/system" + "github.com/sirupsen/logrus" +) + +// Errors used or returned by this file. +var ( + ErrNotDirectory = errors.New("not a directory") + ErrDirNotExists = errors.New("no such directory") + ErrCannotCopyDir = errors.New("cannot copy directory") + ErrInvalidCopySource = errors.New("invalid copy source content") +) + +// PreserveTrailingDotOrSeparator returns the given cleaned path (after +// processing using any utility functions from the path or filepath stdlib +// packages) and appends a trailing `/.` or `/` if its corresponding original +// path (from before being processed by utility functions from the path or +// filepath stdlib packages) ends with a trailing `/.` or `/`. If the cleaned +// path already ends in a `.` path segment, then another is not added. If the +// clean path already ends in the separator, then another is not added. +func PreserveTrailingDotOrSeparator(cleanedPath string, originalPath string, sep byte) string { + // Ensure paths are in platform semantics + cleanedPath = strings.Replace(cleanedPath, "/", string(sep), -1) + originalPath = strings.Replace(originalPath, "/", string(sep), -1) + + if !specifiesCurrentDir(cleanedPath) && specifiesCurrentDir(originalPath) { + if !hasTrailingPathSeparator(cleanedPath, sep) { + // Add a separator if it doesn't already end with one (a cleaned + // path would only end in a separator if it is the root). + cleanedPath += string(sep) + } + cleanedPath += "." + } + + if !hasTrailingPathSeparator(cleanedPath, sep) && hasTrailingPathSeparator(originalPath, sep) { + cleanedPath += string(sep) + } + + return cleanedPath +} + +// assertsDirectory returns whether the given path is +// asserted to be a directory, i.e., the path ends with +// a trailing '/' or `/.`, assuming a path separator of `/`. +func assertsDirectory(path string, sep byte) bool { + return hasTrailingPathSeparator(path, sep) || specifiesCurrentDir(path) +} + +// hasTrailingPathSeparator returns whether the given +// path ends with the system's path separator character. +func hasTrailingPathSeparator(path string, sep byte) bool { + return len(path) > 0 && path[len(path)-1] == sep +} + +// specifiesCurrentDir returns whether the given path specifies +// a "current directory", i.e., the last path segment is `.`. +func specifiesCurrentDir(path string) bool { + return filepath.Base(path) == "." +} + +// SplitPathDirEntry splits the given path between its directory name and its +// basename by first cleaning the path but preserves a trailing "." if the +// original path specified the current directory. +func SplitPathDirEntry(path string) (dir, base string) { + cleanedPath := filepath.Clean(filepath.FromSlash(path)) + + if specifiesCurrentDir(path) { + cleanedPath += string(os.PathSeparator) + "." + } + + return filepath.Dir(cleanedPath), filepath.Base(cleanedPath) +} + +// TarResource archives the resource described by the given CopyInfo to a Tar +// archive. A non-nil error is returned if sourcePath does not exist or is +// asserted to be a directory but exists as another type of file. +// +// This function acts as a convenient wrapper around TarWithOptions, which +// requires a directory as the source path. TarResource accepts either a +// directory or a file path and correctly sets the Tar options. +func TarResource(sourceInfo CopyInfo) (content io.ReadCloser, err error) { + return TarResourceRebase(sourceInfo.Path, sourceInfo.RebaseName) +} + +// TarResourceRebase is like TarResource but renames the first path element of +// items in the resulting tar archive to match the given rebaseName if not "". +func TarResourceRebase(sourcePath, rebaseName string) (content io.ReadCloser, err error) { + sourcePath = normalizePath(sourcePath) + if _, err = os.Lstat(sourcePath); err != nil { + // Catches the case where the source does not exist or is not a + // directory if asserted to be a directory, as this also causes an + // error. + return + } + + // Separate the source path between its directory and + // the entry in that directory which we are archiving. + sourceDir, sourceBase := SplitPathDirEntry(sourcePath) + opts := TarResourceRebaseOpts(sourceBase, rebaseName) + + logrus.Debugf("copying %q from %q", sourceBase, sourceDir) + return TarWithOptions(sourceDir, opts) +} + +// TarResourceRebaseOpts does not preform the Tar, but instead just creates the rebase +// parameters to be sent to TarWithOptions (the TarOptions struct) +func TarResourceRebaseOpts(sourceBase string, rebaseName string) *TarOptions { + filter := []string{sourceBase} + return &TarOptions{ + Compression: Uncompressed, + IncludeFiles: filter, + IncludeSourceDir: true, + RebaseNames: map[string]string{ + sourceBase: rebaseName, + }, + } +} + +// CopyInfo holds basic info about the source +// or destination path of a copy operation. +type CopyInfo struct { + Path string + Exists bool + IsDir bool + RebaseName string +} + +// CopyInfoSourcePath stats the given path to create a CopyInfo +// struct representing that resource for the source of an archive copy +// operation. The given path should be an absolute local path. A source path +// has all symlinks evaluated that appear before the last path separator ("/" +// on Unix). As it is to be a copy source, the path must exist. +func CopyInfoSourcePath(path string, followLink bool) (CopyInfo, error) { + // normalize the file path and then evaluate the symbol link + // we will use the target file instead of the symbol link if + // followLink is set + path = normalizePath(path) + + resolvedPath, rebaseName, err := ResolveHostSourcePath(path, followLink) + if err != nil { + return CopyInfo{}, err + } + + stat, err := os.Lstat(resolvedPath) + if err != nil { + return CopyInfo{}, err + } + + return CopyInfo{ + Path: resolvedPath, + Exists: true, + IsDir: stat.IsDir(), + RebaseName: rebaseName, + }, nil +} + +// CopyInfoDestinationPath stats the given path to create a CopyInfo +// struct representing that resource for the destination of an archive copy +// operation. The given path should be an absolute local path. +func CopyInfoDestinationPath(path string) (info CopyInfo, err error) { + maxSymlinkIter := 10 // filepath.EvalSymlinks uses 255, but 10 already seems like a lot. + path = normalizePath(path) + originalPath := path + + stat, err := os.Lstat(path) + + if err == nil && stat.Mode()&os.ModeSymlink == 0 { + // The path exists and is not a symlink. + return CopyInfo{ + Path: path, + Exists: true, + IsDir: stat.IsDir(), + }, nil + } + + // While the path is a symlink. + for n := 0; err == nil && stat.Mode()&os.ModeSymlink != 0; n++ { + if n > maxSymlinkIter { + // Don't follow symlinks more than this arbitrary number of times. + return CopyInfo{}, errors.New("too many symlinks in " + originalPath) + } + + // The path is a symbolic link. We need to evaluate it so that the + // destination of the copy operation is the link target and not the + // link itself. This is notably different than CopyInfoSourcePath which + // only evaluates symlinks before the last appearing path separator. + // Also note that it is okay if the last path element is a broken + // symlink as the copy operation should create the target. + var linkTarget string + + linkTarget, err = os.Readlink(path) + if err != nil { + return CopyInfo{}, err + } + + if !system.IsAbs(linkTarget) { + // Join with the parent directory. + dstParent, _ := SplitPathDirEntry(path) + linkTarget = filepath.Join(dstParent, linkTarget) + } + + path = linkTarget + stat, err = os.Lstat(path) + } + + if err != nil { + // It's okay if the destination path doesn't exist. We can still + // continue the copy operation if the parent directory exists. + if !os.IsNotExist(err) { + return CopyInfo{}, err + } + + // Ensure destination parent dir exists. + dstParent, _ := SplitPathDirEntry(path) + + parentDirStat, err := os.Stat(dstParent) + if err != nil { + return CopyInfo{}, err + } + if !parentDirStat.IsDir() { + return CopyInfo{}, ErrNotDirectory + } + + return CopyInfo{Path: path}, nil + } + + // The path exists after resolving symlinks. + return CopyInfo{ + Path: path, + Exists: true, + IsDir: stat.IsDir(), + }, nil +} + +// PrepareArchiveCopy prepares the given srcContent archive, which should +// contain the archived resource described by srcInfo, to the destination +// described by dstInfo. Returns the possibly modified content archive along +// with the path to the destination directory which it should be extracted to. +func PrepareArchiveCopy(srcContent io.Reader, srcInfo, dstInfo CopyInfo) (dstDir string, content io.ReadCloser, err error) { + // Ensure in platform semantics + srcInfo.Path = normalizePath(srcInfo.Path) + dstInfo.Path = normalizePath(dstInfo.Path) + + // Separate the destination path between its directory and base + // components in case the source archive contents need to be rebased. + dstDir, dstBase := SplitPathDirEntry(dstInfo.Path) + _, srcBase := SplitPathDirEntry(srcInfo.Path) + + switch { + case dstInfo.Exists && dstInfo.IsDir: + // The destination exists as a directory. No alteration + // to srcContent is needed as its contents can be + // simply extracted to the destination directory. + return dstInfo.Path, ioutil.NopCloser(srcContent), nil + case dstInfo.Exists && srcInfo.IsDir: + // The destination exists as some type of file and the source + // content is a directory. This is an error condition since + // you cannot copy a directory to an existing file location. + return "", nil, ErrCannotCopyDir + case dstInfo.Exists: + // The destination exists as some type of file and the source content + // is also a file. The source content entry will have to be renamed to + // have a basename which matches the destination path's basename. + if len(srcInfo.RebaseName) != 0 { + srcBase = srcInfo.RebaseName + } + return dstDir, RebaseArchiveEntries(srcContent, srcBase, dstBase), nil + case srcInfo.IsDir: + // The destination does not exist and the source content is an archive + // of a directory. The archive should be extracted to the parent of + // the destination path instead, and when it is, the directory that is + // created as a result should take the name of the destination path. + // The source content entries will have to be renamed to have a + // basename which matches the destination path's basename. + if len(srcInfo.RebaseName) != 0 { + srcBase = srcInfo.RebaseName + } + return dstDir, RebaseArchiveEntries(srcContent, srcBase, dstBase), nil + case assertsDirectory(dstInfo.Path, os.PathSeparator): + // The destination does not exist and is asserted to be created as a + // directory, but the source content is not a directory. This is an + // error condition since you cannot create a directory from a file + // source. + return "", nil, ErrDirNotExists + default: + // The last remaining case is when the destination does not exist, is + // not asserted to be a directory, and the source content is not an + // archive of a directory. It this case, the destination file will need + // to be created when the archive is extracted and the source content + // entry will have to be renamed to have a basename which matches the + // destination path's basename. + if len(srcInfo.RebaseName) != 0 { + srcBase = srcInfo.RebaseName + } + return dstDir, RebaseArchiveEntries(srcContent, srcBase, dstBase), nil + } + +} + +// RebaseArchiveEntries rewrites the given srcContent archive replacing +// an occurrence of oldBase with newBase at the beginning of entry names. +func RebaseArchiveEntries(srcContent io.Reader, oldBase, newBase string) io.ReadCloser { + if oldBase == string(os.PathSeparator) { + // If oldBase specifies the root directory, use an empty string as + // oldBase instead so that newBase doesn't replace the path separator + // that all paths will start with. + oldBase = "" + } + + rebased, w := io.Pipe() + + go func() { + srcTar := tar.NewReader(srcContent) + rebasedTar := tar.NewWriter(w) + + for { + hdr, err := srcTar.Next() + if err == io.EOF { + // Signals end of archive. + rebasedTar.Close() + w.Close() + return + } + if err != nil { + w.CloseWithError(err) + return + } + + // srcContent tar stream, as served by TarWithOptions(), is + // definitely in PAX format, but tar.Next() mistakenly guesses it + // as USTAR, which creates a problem: if the newBase is >100 + // characters long, WriteHeader() returns an error like + // "archive/tar: cannot encode header: Format specifies USTAR; and USTAR cannot encode Name=...". + // + // To fix, set the format to PAX here. See docker/for-linux issue #484. + hdr.Format = tar.FormatPAX + hdr.Name = strings.Replace(hdr.Name, oldBase, newBase, 1) + if hdr.Typeflag == tar.TypeLink { + hdr.Linkname = strings.Replace(hdr.Linkname, oldBase, newBase, 1) + } + + if err = rebasedTar.WriteHeader(hdr); err != nil { + w.CloseWithError(err) + return + } + + if _, err = io.Copy(rebasedTar, srcTar); err != nil { + w.CloseWithError(err) + return + } + } + }() + + return rebased +} + +// TODO @gupta-ak. These might have to be changed in the future to be +// continuity driver aware as well to support LCOW. + +// CopyResource performs an archive copy from the given source path to the +// given destination path. The source path MUST exist and the destination +// path's parent directory must exist. +func CopyResource(srcPath, dstPath string, followLink bool) error { + var ( + srcInfo CopyInfo + err error + ) + + // Ensure in platform semantics + srcPath = normalizePath(srcPath) + dstPath = normalizePath(dstPath) + + // Clean the source and destination paths. + srcPath = PreserveTrailingDotOrSeparator(filepath.Clean(srcPath), srcPath, os.PathSeparator) + dstPath = PreserveTrailingDotOrSeparator(filepath.Clean(dstPath), dstPath, os.PathSeparator) + + if srcInfo, err = CopyInfoSourcePath(srcPath, followLink); err != nil { + return err + } + + content, err := TarResource(srcInfo) + if err != nil { + return err + } + defer content.Close() + + return CopyTo(content, srcInfo, dstPath) +} + +// CopyTo handles extracting the given content whose +// entries should be sourced from srcInfo to dstPath. +func CopyTo(content io.Reader, srcInfo CopyInfo, dstPath string) error { + // The destination path need not exist, but CopyInfoDestinationPath will + // ensure that at least the parent directory exists. + dstInfo, err := CopyInfoDestinationPath(normalizePath(dstPath)) + if err != nil { + return err + } + + dstDir, copyArchive, err := PrepareArchiveCopy(content, srcInfo, dstInfo) + if err != nil { + return err + } + defer copyArchive.Close() + + options := &TarOptions{ + NoLchown: true, + NoOverwriteDirNonDir: true, + } + + return Untar(copyArchive, dstDir, options) +} + +// ResolveHostSourcePath decides real path need to be copied with parameters such as +// whether to follow symbol link or not, if followLink is true, resolvedPath will return +// link target of any symbol link file, else it will only resolve symlink of directory +// but return symbol link file itself without resolving. +func ResolveHostSourcePath(path string, followLink bool) (resolvedPath, rebaseName string, err error) { + if followLink { + resolvedPath, err = filepath.EvalSymlinks(path) + if err != nil { + return + } + + resolvedPath, rebaseName = GetRebaseName(path, resolvedPath) + } else { + dirPath, basePath := filepath.Split(path) + + // if not follow symbol link, then resolve symbol link of parent dir + var resolvedDirPath string + resolvedDirPath, err = filepath.EvalSymlinks(dirPath) + if err != nil { + return + } + // resolvedDirPath will have been cleaned (no trailing path separators) so + // we can manually join it with the base path element. + resolvedPath = resolvedDirPath + string(filepath.Separator) + basePath + if hasTrailingPathSeparator(path, os.PathSeparator) && + filepath.Base(path) != filepath.Base(resolvedPath) { + rebaseName = filepath.Base(path) + } + } + return resolvedPath, rebaseName, nil +} + +// GetRebaseName normalizes and compares path and resolvedPath, +// return completed resolved path and rebased file name +func GetRebaseName(path, resolvedPath string) (string, string) { + // linkTarget will have been cleaned (no trailing path separators and dot) so + // we can manually join it with them + var rebaseName string + if specifiesCurrentDir(path) && + !specifiesCurrentDir(resolvedPath) { + resolvedPath += string(filepath.Separator) + "." + } + + if hasTrailingPathSeparator(path, os.PathSeparator) && + !hasTrailingPathSeparator(resolvedPath, os.PathSeparator) { + resolvedPath += string(filepath.Separator) + } + + if filepath.Base(path) != filepath.Base(resolvedPath) { + // In the case where the path had a trailing separator and a symlink + // evaluation has changed the last path component, we will need to + // rebase the name in the archive that is being copied to match the + // originally requested name. + rebaseName = filepath.Base(path) + } + return resolvedPath, rebaseName +} diff --git a/vendor/github.com/docker/docker/pkg/archive/copy_unix.go b/vendor/github.com/docker/docker/pkg/archive/copy_unix.go new file mode 100644 index 0000000000..3958364f5b --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/copy_unix.go @@ -0,0 +1,11 @@ +// +build !windows + +package archive // import "github.com/docker/docker/pkg/archive" + +import ( + "path/filepath" +) + +func normalizePath(path string) string { + return filepath.ToSlash(path) +} diff --git a/vendor/github.com/docker/docker/pkg/archive/copy_windows.go b/vendor/github.com/docker/docker/pkg/archive/copy_windows.go new file mode 100644 index 0000000000..a878d1bac4 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/copy_windows.go @@ -0,0 +1,9 @@ +package archive // import "github.com/docker/docker/pkg/archive" + +import ( + "path/filepath" +) + +func normalizePath(path string) string { + return filepath.FromSlash(path) +} diff --git a/vendor/github.com/docker/docker/pkg/archive/diff.go b/vendor/github.com/docker/docker/pkg/archive/diff.go new file mode 100644 index 0000000000..27897e6ab7 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/diff.go @@ -0,0 +1,260 @@ +package archive // import "github.com/docker/docker/pkg/archive" + +import ( + "archive/tar" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/docker/docker/pkg/idtools" + "github.com/docker/docker/pkg/pools" + "github.com/docker/docker/pkg/system" + "github.com/sirupsen/logrus" +) + +// UnpackLayer unpack `layer` to a `dest`. The stream `layer` can be +// compressed or uncompressed. +// Returns the size in bytes of the contents of the layer. +func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64, err error) { + tr := tar.NewReader(layer) + trBuf := pools.BufioReader32KPool.Get(tr) + defer pools.BufioReader32KPool.Put(trBuf) + + var dirs []*tar.Header + unpackedPaths := make(map[string]struct{}) + + if options == nil { + options = &TarOptions{} + } + if options.ExcludePatterns == nil { + options.ExcludePatterns = []string{} + } + idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps) + + aufsTempdir := "" + aufsHardlinks := make(map[string]*tar.Header) + + // Iterate through the files in the archive. + for { + hdr, err := tr.Next() + if err == io.EOF { + // end of tar archive + break + } + if err != nil { + return 0, err + } + + size += hdr.Size + + // Normalize name, for safety and for a simple is-root check + hdr.Name = filepath.Clean(hdr.Name) + + // Windows does not support filenames with colons in them. Ignore + // these files. This is not a problem though (although it might + // appear that it is). Let's suppose a client is running docker pull. + // The daemon it points to is Windows. Would it make sense for the + // client to be doing a docker pull Ubuntu for example (which has files + // with colons in the name under /usr/share/man/man3)? No, absolutely + // not as it would really only make sense that they were pulling a + // Windows image. However, for development, it is necessary to be able + // to pull Linux images which are in the repository. + // + // TODO Windows. Once the registry is aware of what images are Windows- + // specific or Linux-specific, this warning should be changed to an error + // to cater for the situation where someone does manage to upload a Linux + // image but have it tagged as Windows inadvertently. + if runtime.GOOS == "windows" { + if strings.Contains(hdr.Name, ":") { + logrus.Warnf("Windows: Ignoring %s (is this a Linux image?)", hdr.Name) + continue + } + } + + // Note as these operations are platform specific, so must the slash be. + if !strings.HasSuffix(hdr.Name, string(os.PathSeparator)) { + // Not the root directory, ensure that the parent directory exists. + // This happened in some tests where an image had a tarfile without any + // parent directories. + parent := filepath.Dir(hdr.Name) + parentPath := filepath.Join(dest, parent) + + if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) { + err = system.MkdirAll(parentPath, 0600) + if err != nil { + return 0, err + } + } + } + + // Skip AUFS metadata dirs + if strings.HasPrefix(hdr.Name, WhiteoutMetaPrefix) { + // Regular files inside /.wh..wh.plnk can be used as hardlink targets + // We don't want this directory, but we need the files in them so that + // such hardlinks can be resolved. + if strings.HasPrefix(hdr.Name, WhiteoutLinkDir) && hdr.Typeflag == tar.TypeReg { + basename := filepath.Base(hdr.Name) + aufsHardlinks[basename] = hdr + if aufsTempdir == "" { + if aufsTempdir, err = ioutil.TempDir("", "dockerplnk"); err != nil { + return 0, err + } + defer os.RemoveAll(aufsTempdir) + } + if err := createTarFile(filepath.Join(aufsTempdir, basename), dest, hdr, tr, true, nil, options.InUserNS); err != nil { + return 0, err + } + } + + if hdr.Name != WhiteoutOpaqueDir { + continue + } + } + path := filepath.Join(dest, hdr.Name) + rel, err := filepath.Rel(dest, path) + if err != nil { + return 0, err + } + + // Note as these operations are platform specific, so must the slash be. + if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) { + return 0, breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest)) + } + base := filepath.Base(path) + + if strings.HasPrefix(base, WhiteoutPrefix) { + dir := filepath.Dir(path) + if base == WhiteoutOpaqueDir { + _, err := os.Lstat(dir) + if err != nil { + return 0, err + } + err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + if os.IsNotExist(err) { + err = nil // parent was deleted + } + return err + } + if path == dir { + return nil + } + if _, exists := unpackedPaths[path]; !exists { + err := os.RemoveAll(path) + return err + } + return nil + }) + if err != nil { + return 0, err + } + } else { + originalBase := base[len(WhiteoutPrefix):] + originalPath := filepath.Join(dir, originalBase) + if err := os.RemoveAll(originalPath); err != nil { + return 0, err + } + } + } else { + // If path exits we almost always just want to remove and replace it. + // The only exception is when it is a directory *and* the file from + // the layer is also a directory. Then we want to merge them (i.e. + // just apply the metadata from the layer). + if fi, err := os.Lstat(path); err == nil { + if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) { + if err := os.RemoveAll(path); err != nil { + return 0, err + } + } + } + + trBuf.Reset(tr) + srcData := io.Reader(trBuf) + srcHdr := hdr + + // Hard links into /.wh..wh.plnk don't work, as we don't extract that directory, so + // we manually retarget these into the temporary files we extracted them into + if hdr.Typeflag == tar.TypeLink && strings.HasPrefix(filepath.Clean(hdr.Linkname), WhiteoutLinkDir) { + linkBasename := filepath.Base(hdr.Linkname) + srcHdr = aufsHardlinks[linkBasename] + if srcHdr == nil { + return 0, fmt.Errorf("Invalid aufs hardlink") + } + tmpFile, err := os.Open(filepath.Join(aufsTempdir, linkBasename)) + if err != nil { + return 0, err + } + defer tmpFile.Close() + srcData = tmpFile + } + + if err := remapIDs(idMapping, srcHdr); err != nil { + return 0, err + } + + if err := createTarFile(path, dest, srcHdr, srcData, !options.NoLchown, nil, options.InUserNS); err != nil { + return 0, err + } + + // Directory mtimes must be handled at the end to avoid further + // file creation in them to modify the directory mtime + if hdr.Typeflag == tar.TypeDir { + dirs = append(dirs, hdr) + } + unpackedPaths[path] = struct{}{} + } + } + + for _, hdr := range dirs { + path := filepath.Join(dest, hdr.Name) + if err := system.Chtimes(path, hdr.AccessTime, hdr.ModTime); err != nil { + return 0, err + } + } + + return size, nil +} + +// ApplyLayer parses a diff in the standard layer format from `layer`, +// and applies it to the directory `dest`. The stream `layer` can be +// compressed or uncompressed. +// Returns the size in bytes of the contents of the layer. +func ApplyLayer(dest string, layer io.Reader) (int64, error) { + return applyLayerHandler(dest, layer, &TarOptions{}, true) +} + +// ApplyUncompressedLayer parses a diff in the standard layer format from +// `layer`, and applies it to the directory `dest`. The stream `layer` +// can only be uncompressed. +// Returns the size in bytes of the contents of the layer. +func ApplyUncompressedLayer(dest string, layer io.Reader, options *TarOptions) (int64, error) { + return applyLayerHandler(dest, layer, options, false) +} + +// do the bulk load of ApplyLayer, but allow for not calling DecompressStream +func applyLayerHandler(dest string, layer io.Reader, options *TarOptions, decompress bool) (int64, error) { + dest = filepath.Clean(dest) + + // We need to be able to set any perms + if runtime.GOOS != "windows" { + oldmask, err := system.Umask(0) + if err != nil { + return 0, err + } + defer system.Umask(oldmask) + } + + if decompress { + decompLayer, err := DecompressStream(layer) + if err != nil { + return 0, err + } + defer decompLayer.Close() + layer = decompLayer + } + return UnpackLayer(dest, layer, options) +} diff --git a/vendor/github.com/docker/docker/pkg/archive/time_linux.go b/vendor/github.com/docker/docker/pkg/archive/time_linux.go new file mode 100644 index 0000000000..797143ee84 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/time_linux.go @@ -0,0 +1,16 @@ +package archive // import "github.com/docker/docker/pkg/archive" + +import ( + "syscall" + "time" +) + +func timeToTimespec(time time.Time) (ts syscall.Timespec) { + if time.IsZero() { + // Return UTIME_OMIT special value + ts.Sec = 0 + ts.Nsec = (1 << 30) - 2 + return + } + return syscall.NsecToTimespec(time.UnixNano()) +} diff --git a/vendor/github.com/docker/docker/pkg/archive/time_unsupported.go b/vendor/github.com/docker/docker/pkg/archive/time_unsupported.go new file mode 100644 index 0000000000..f58bf227fd --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/time_unsupported.go @@ -0,0 +1,16 @@ +// +build !linux + +package archive // import "github.com/docker/docker/pkg/archive" + +import ( + "syscall" + "time" +) + +func timeToTimespec(time time.Time) (ts syscall.Timespec) { + nsec := int64(0) + if !time.IsZero() { + nsec = time.UnixNano() + } + return syscall.NsecToTimespec(nsec) +} diff --git a/vendor/github.com/docker/docker/pkg/archive/whiteouts.go b/vendor/github.com/docker/docker/pkg/archive/whiteouts.go new file mode 100644 index 0000000000..4c072a87ee --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/whiteouts.go @@ -0,0 +1,23 @@ +package archive // import "github.com/docker/docker/pkg/archive" + +// Whiteouts are files with a special meaning for the layered filesystem. +// Docker uses AUFS whiteout files inside exported archives. In other +// filesystems these files are generated/handled on tar creation/extraction. + +// WhiteoutPrefix prefix means file is a whiteout. If this is followed by a +// filename this means that file has been removed from the base layer. +const WhiteoutPrefix = ".wh." + +// WhiteoutMetaPrefix prefix means whiteout has a special meaning and is not +// for removing an actual file. Normally these files are excluded from exported +// archives. +const WhiteoutMetaPrefix = WhiteoutPrefix + WhiteoutPrefix + +// WhiteoutLinkDir is a directory AUFS uses for storing hardlink links to other +// layers. Normally these should not go into exported archives and all changed +// hardlinks should be copied to the top layer. +const WhiteoutLinkDir = WhiteoutMetaPrefix + "plnk" + +// WhiteoutOpaqueDir file means directory has been made opaque - meaning +// readdir calls to this directory do not follow to lower layers. +const WhiteoutOpaqueDir = WhiteoutMetaPrefix + ".opq" diff --git a/vendor/github.com/docker/docker/pkg/archive/wrap.go b/vendor/github.com/docker/docker/pkg/archive/wrap.go new file mode 100644 index 0000000000..85435694cf --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/archive/wrap.go @@ -0,0 +1,59 @@ +package archive // import "github.com/docker/docker/pkg/archive" + +import ( + "archive/tar" + "bytes" + "io" +) + +// Generate generates a new archive from the content provided +// as input. +// +// `files` is a sequence of path/content pairs. A new file is +// added to the archive for each pair. +// If the last pair is incomplete, the file is created with an +// empty content. For example: +// +// Generate("foo.txt", "hello world", "emptyfile") +// +// The above call will return an archive with 2 files: +// * ./foo.txt with content "hello world" +// * ./empty with empty content +// +// FIXME: stream content instead of buffering +// FIXME: specify permissions and other archive metadata +func Generate(input ...string) (io.Reader, error) { + files := parseStringPairs(input...) + buf := new(bytes.Buffer) + tw := tar.NewWriter(buf) + for _, file := range files { + name, content := file[0], file[1] + hdr := &tar.Header{ + Name: name, + Size: int64(len(content)), + } + if err := tw.WriteHeader(hdr); err != nil { + return nil, err + } + if _, err := tw.Write([]byte(content)); err != nil { + return nil, err + } + } + if err := tw.Close(); err != nil { + return nil, err + } + return buf, nil +} + +func parseStringPairs(input ...string) (output [][2]string) { + output = make([][2]string, 0, len(input)/2+1) + for i := 0; i < len(input); i += 2 { + var pair [2]string + pair[0] = input[i] + if i+1 < len(input) { + pair[1] = input[i+1] + } + output = append(output, pair) + } + return +} diff --git a/vendor/github.com/docker/docker/pkg/fileutils/fileutils.go b/vendor/github.com/docker/docker/pkg/fileutils/fileutils.go new file mode 100644 index 0000000000..34f1c726fb --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/fileutils/fileutils.go @@ -0,0 +1,298 @@ +package fileutils // import "github.com/docker/docker/pkg/fileutils" + +import ( + "errors" + "fmt" + "io" + "os" + "path/filepath" + "regexp" + "strings" + "text/scanner" + + "github.com/sirupsen/logrus" +) + +// PatternMatcher allows checking paths against a list of patterns +type PatternMatcher struct { + patterns []*Pattern + exclusions bool +} + +// NewPatternMatcher creates a new matcher object for specific patterns that can +// be used later to match against patterns against paths +func NewPatternMatcher(patterns []string) (*PatternMatcher, error) { + pm := &PatternMatcher{ + patterns: make([]*Pattern, 0, len(patterns)), + } + for _, p := range patterns { + // Eliminate leading and trailing whitespace. + p = strings.TrimSpace(p) + if p == "" { + continue + } + p = filepath.Clean(p) + newp := &Pattern{} + if p[0] == '!' { + if len(p) == 1 { + return nil, errors.New("illegal exclusion pattern: \"!\"") + } + newp.exclusion = true + p = p[1:] + pm.exclusions = true + } + // Do some syntax checking on the pattern. + // filepath's Match() has some really weird rules that are inconsistent + // so instead of trying to dup their logic, just call Match() for its + // error state and if there is an error in the pattern return it. + // If this becomes an issue we can remove this since its really only + // needed in the error (syntax) case - which isn't really critical. + if _, err := filepath.Match(p, "."); err != nil { + return nil, err + } + newp.cleanedPattern = p + newp.dirs = strings.Split(p, string(os.PathSeparator)) + pm.patterns = append(pm.patterns, newp) + } + return pm, nil +} + +// Matches matches path against all the patterns. Matches is not safe to be +// called concurrently +func (pm *PatternMatcher) Matches(file string) (bool, error) { + matched := false + file = filepath.FromSlash(file) + parentPath := filepath.Dir(file) + parentPathDirs := strings.Split(parentPath, string(os.PathSeparator)) + + for _, pattern := range pm.patterns { + negative := false + + if pattern.exclusion { + negative = true + } + + match, err := pattern.match(file) + if err != nil { + return false, err + } + + if !match && parentPath != "." { + // Check to see if the pattern matches one of our parent dirs. + if len(pattern.dirs) <= len(parentPathDirs) { + match, _ = pattern.match(strings.Join(parentPathDirs[:len(pattern.dirs)], string(os.PathSeparator))) + } + } + + if match { + matched = !negative + } + } + + if matched { + logrus.Debugf("Skipping excluded path: %s", file) + } + + return matched, nil +} + +// Exclusions returns true if any of the patterns define exclusions +func (pm *PatternMatcher) Exclusions() bool { + return pm.exclusions +} + +// Patterns returns array of active patterns +func (pm *PatternMatcher) Patterns() []*Pattern { + return pm.patterns +} + +// Pattern defines a single regexp used to filter file paths. +type Pattern struct { + cleanedPattern string + dirs []string + regexp *regexp.Regexp + exclusion bool +} + +func (p *Pattern) String() string { + return p.cleanedPattern +} + +// Exclusion returns true if this pattern defines exclusion +func (p *Pattern) Exclusion() bool { + return p.exclusion +} + +func (p *Pattern) match(path string) (bool, error) { + + if p.regexp == nil { + if err := p.compile(); err != nil { + return false, filepath.ErrBadPattern + } + } + + b := p.regexp.MatchString(path) + + return b, nil +} + +func (p *Pattern) compile() error { + regStr := "^" + pattern := p.cleanedPattern + // Go through the pattern and convert it to a regexp. + // We use a scanner so we can support utf-8 chars. + var scan scanner.Scanner + scan.Init(strings.NewReader(pattern)) + + sl := string(os.PathSeparator) + escSL := sl + if sl == `\` { + escSL += `\` + } + + for scan.Peek() != scanner.EOF { + ch := scan.Next() + + if ch == '*' { + if scan.Peek() == '*' { + // is some flavor of "**" + scan.Next() + + // Treat **/ as ** so eat the "/" + if string(scan.Peek()) == sl { + scan.Next() + } + + if scan.Peek() == scanner.EOF { + // is "**EOF" - to align with .gitignore just accept all + regStr += ".*" + } else { + // is "**" + // Note that this allows for any # of /'s (even 0) because + // the .* will eat everything, even /'s + regStr += "(.*" + escSL + ")?" + } + } else { + // is "*" so map it to anything but "/" + regStr += "[^" + escSL + "]*" + } + } else if ch == '?' { + // "?" is any char except "/" + regStr += "[^" + escSL + "]" + } else if ch == '.' || ch == '$' { + // Escape some regexp special chars that have no meaning + // in golang's filepath.Match + regStr += `\` + string(ch) + } else if ch == '\\' { + // escape next char. Note that a trailing \ in the pattern + // will be left alone (but need to escape it) + if sl == `\` { + // On windows map "\" to "\\", meaning an escaped backslash, + // and then just continue because filepath.Match on + // Windows doesn't allow escaping at all + regStr += escSL + continue + } + if scan.Peek() != scanner.EOF { + regStr += `\` + string(scan.Next()) + } else { + regStr += `\` + } + } else { + regStr += string(ch) + } + } + + regStr += "$" + + re, err := regexp.Compile(regStr) + if err != nil { + return err + } + + p.regexp = re + return nil +} + +// Matches returns true if file matches any of the patterns +// and isn't excluded by any of the subsequent patterns. +func Matches(file string, patterns []string) (bool, error) { + pm, err := NewPatternMatcher(patterns) + if err != nil { + return false, err + } + file = filepath.Clean(file) + + if file == "." { + // Don't let them exclude everything, kind of silly. + return false, nil + } + + return pm.Matches(file) +} + +// CopyFile copies from src to dst until either EOF is reached +// on src or an error occurs. It verifies src exists and removes +// the dst if it exists. +func CopyFile(src, dst string) (int64, error) { + cleanSrc := filepath.Clean(src) + cleanDst := filepath.Clean(dst) + if cleanSrc == cleanDst { + return 0, nil + } + sf, err := os.Open(cleanSrc) + if err != nil { + return 0, err + } + defer sf.Close() + if err := os.Remove(cleanDst); err != nil && !os.IsNotExist(err) { + return 0, err + } + df, err := os.Create(cleanDst) + if err != nil { + return 0, err + } + defer df.Close() + return io.Copy(df, sf) +} + +// ReadSymlinkedDirectory returns the target directory of a symlink. +// The target of the symbolic link may not be a file. +func ReadSymlinkedDirectory(path string) (string, error) { + var realPath string + var err error + if realPath, err = filepath.Abs(path); err != nil { + return "", fmt.Errorf("unable to get absolute path for %s: %s", path, err) + } + if realPath, err = filepath.EvalSymlinks(realPath); err != nil { + return "", fmt.Errorf("failed to canonicalise path for %s: %s", path, err) + } + realPathInfo, err := os.Stat(realPath) + if err != nil { + return "", fmt.Errorf("failed to stat target '%s' of '%s': %s", realPath, path, err) + } + if !realPathInfo.Mode().IsDir() { + return "", fmt.Errorf("canonical path points to a file '%s'", realPath) + } + return realPath, nil +} + +// CreateIfNotExists creates a file or a directory only if it does not already exist. +func CreateIfNotExists(path string, isDir bool) error { + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + if isDir { + return os.MkdirAll(path, 0755) + } + if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { + return err + } + f, err := os.OpenFile(path, os.O_CREATE, 0755) + if err != nil { + return err + } + f.Close() + } + } + return nil +} diff --git a/vendor/github.com/docker/docker/pkg/fileutils/fileutils_darwin.go b/vendor/github.com/docker/docker/pkg/fileutils/fileutils_darwin.go new file mode 100644 index 0000000000..e40cc271b3 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/fileutils/fileutils_darwin.go @@ -0,0 +1,27 @@ +package fileutils // import "github.com/docker/docker/pkg/fileutils" + +import ( + "os" + "os/exec" + "strconv" + "strings" +) + +// GetTotalUsedFds returns the number of used File Descriptors by +// executing `lsof -p PID` +func GetTotalUsedFds() int { + pid := os.Getpid() + + cmd := exec.Command("lsof", "-p", strconv.Itoa(pid)) + + output, err := cmd.CombinedOutput() + if err != nil { + return -1 + } + + outputStr := strings.TrimSpace(string(output)) + + fds := strings.Split(outputStr, "\n") + + return len(fds) - 1 +} diff --git a/vendor/github.com/docker/docker/pkg/fileutils/fileutils_unix.go b/vendor/github.com/docker/docker/pkg/fileutils/fileutils_unix.go new file mode 100644 index 0000000000..565396f1c7 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/fileutils/fileutils_unix.go @@ -0,0 +1,22 @@ +// +build linux freebsd + +package fileutils // import "github.com/docker/docker/pkg/fileutils" + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/sirupsen/logrus" +) + +// GetTotalUsedFds Returns the number of used File Descriptors by +// reading it via /proc filesystem. +func GetTotalUsedFds() int { + if fds, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil { + logrus.Errorf("Error opening /proc/%d/fd: %s", os.Getpid(), err) + } else { + return len(fds) + } + return -1 +} diff --git a/vendor/github.com/docker/docker/pkg/fileutils/fileutils_windows.go b/vendor/github.com/docker/docker/pkg/fileutils/fileutils_windows.go new file mode 100644 index 0000000000..3f1ebb6567 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/fileutils/fileutils_windows.go @@ -0,0 +1,7 @@ +package fileutils // import "github.com/docker/docker/pkg/fileutils" + +// GetTotalUsedFds Returns the number of used File Descriptors. Not supported +// on Windows. +func GetTotalUsedFds() int { + return -1 +} diff --git a/vendor/github.com/docker/docker/pkg/homedir/homedir_linux.go b/vendor/github.com/docker/docker/pkg/homedir/homedir_linux.go new file mode 100644 index 0000000000..5e6310fdcd --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/homedir/homedir_linux.go @@ -0,0 +1,93 @@ +package homedir // import "github.com/docker/docker/pkg/homedir" + +import ( + "errors" + "os" + "path/filepath" + "strings" +) + +// GetRuntimeDir returns XDG_RUNTIME_DIR. +// XDG_RUNTIME_DIR is typically configured via pam_systemd. +// GetRuntimeDir returns non-nil error if XDG_RUNTIME_DIR is not set. +// +// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html +func GetRuntimeDir() (string, error) { + if xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR"); xdgRuntimeDir != "" { + return xdgRuntimeDir, nil + } + return "", errors.New("could not get XDG_RUNTIME_DIR") +} + +// StickRuntimeDirContents sets the sticky bit on files that are under +// XDG_RUNTIME_DIR, so that the files won't be periodically removed by the system. +// +// StickyRuntimeDir returns slice of sticked files. +// StickyRuntimeDir returns nil error if XDG_RUNTIME_DIR is not set. +// +// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html +func StickRuntimeDirContents(files []string) ([]string, error) { + runtimeDir, err := GetRuntimeDir() + if err != nil { + // ignore error if runtimeDir is empty + return nil, nil + } + runtimeDir, err = filepath.Abs(runtimeDir) + if err != nil { + return nil, err + } + var sticked []string + for _, f := range files { + f, err = filepath.Abs(f) + if err != nil { + return sticked, err + } + if strings.HasPrefix(f, runtimeDir+"/") { + if err = stick(f); err != nil { + return sticked, err + } + sticked = append(sticked, f) + } + } + return sticked, nil +} + +func stick(f string) error { + st, err := os.Stat(f) + if err != nil { + return err + } + m := st.Mode() + m |= os.ModeSticky + return os.Chmod(f, m) +} + +// GetDataHome returns XDG_DATA_HOME. +// GetDataHome returns $HOME/.local/share and nil error if XDG_DATA_HOME is not set. +// +// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html +func GetDataHome() (string, error) { + if xdgDataHome := os.Getenv("XDG_DATA_HOME"); xdgDataHome != "" { + return xdgDataHome, nil + } + home := os.Getenv("HOME") + if home == "" { + return "", errors.New("could not get either XDG_DATA_HOME or HOME") + } + return filepath.Join(home, ".local", "share"), nil +} + +// GetConfigHome returns XDG_CONFIG_HOME. +// GetConfigHome returns $HOME/.config and nil error if XDG_CONFIG_HOME is not set. +// +// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html +func GetConfigHome() (string, error) { + if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" { + return xdgConfigHome, nil + } + home := os.Getenv("HOME") + if home == "" { + return "", errors.New("could not get either XDG_CONFIG_HOME or HOME") + } + return filepath.Join(home, ".config"), nil +} diff --git a/vendor/github.com/docker/docker/pkg/homedir/homedir_others.go b/vendor/github.com/docker/docker/pkg/homedir/homedir_others.go new file mode 100644 index 0000000000..67ab9e9b31 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/homedir/homedir_others.go @@ -0,0 +1,27 @@ +// +build !linux + +package homedir // import "github.com/docker/docker/pkg/homedir" + +import ( + "errors" +) + +// GetRuntimeDir is unsupported on non-linux system. +func GetRuntimeDir() (string, error) { + return "", errors.New("homedir.GetRuntimeDir() is not supported on this system") +} + +// StickRuntimeDirContents is unsupported on non-linux system. +func StickRuntimeDirContents(files []string) ([]string, error) { + return nil, errors.New("homedir.StickRuntimeDirContents() is not supported on this system") +} + +// GetDataHome is unsupported on non-linux system. +func GetDataHome() (string, error) { + return "", errors.New("homedir.GetDataHome() is not supported on this system") +} + +// GetConfigHome is unsupported on non-linux system. +func GetConfigHome() (string, error) { + return "", errors.New("homedir.GetConfigHome() is not supported on this system") +} diff --git a/vendor/github.com/docker/docker/pkg/homedir/homedir_unix.go b/vendor/github.com/docker/docker/pkg/homedir/homedir_unix.go new file mode 100644 index 0000000000..441bd727b6 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/homedir/homedir_unix.go @@ -0,0 +1,38 @@ +// +build !windows + +package homedir // import "github.com/docker/docker/pkg/homedir" + +import ( + "os" + "os/user" +) + +// Key returns the env var name for the user's home dir based on +// the platform being run on +func Key() string { + return "HOME" +} + +// Get returns the home directory of the current user with the help of +// environment variables depending on the target operating system. +// Returned path should be used with "path/filepath" to form new paths. +// +// If linking statically with cgo enabled against glibc, ensure the +// osusergo build tag is used. +// +// If needing to do nss lookups, do not disable cgo or set osusergo. +func Get() string { + home := os.Getenv(Key()) + if home == "" { + if u, err := user.Current(); err == nil { + return u.HomeDir + } + } + return home +} + +// GetShortcutString returns the string that is shortcut to user's home directory +// in the native shell of the platform running on. +func GetShortcutString() string { + return "~" +} diff --git a/vendor/github.com/docker/docker/pkg/homedir/homedir_windows.go b/vendor/github.com/docker/docker/pkg/homedir/homedir_windows.go new file mode 100644 index 0000000000..2f81813b28 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/homedir/homedir_windows.go @@ -0,0 +1,24 @@ +package homedir // import "github.com/docker/docker/pkg/homedir" + +import ( + "os" +) + +// Key returns the env var name for the user's home dir based on +// the platform being run on +func Key() string { + return "USERPROFILE" +} + +// Get returns the home directory of the current user with the help of +// environment variables depending on the target operating system. +// Returned path should be used with "path/filepath" to form new paths. +func Get() string { + return os.Getenv(Key()) +} + +// GetShortcutString returns the string that is shortcut to user's home directory +// in the native shell of the platform running on. +func GetShortcutString() string { + return "%USERPROFILE%" // be careful while using in format functions +} diff --git a/vendor/github.com/docker/docker/pkg/idtools/idtools.go b/vendor/github.com/docker/docker/pkg/idtools/idtools.go new file mode 100644 index 0000000000..25a57b231e --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/idtools/idtools.go @@ -0,0 +1,241 @@ +package idtools // import "github.com/docker/docker/pkg/idtools" + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +// IDMap contains a single entry for user namespace range remapping. An array +// of IDMap entries represents the structure that will be provided to the Linux +// kernel for creating a user namespace. +type IDMap struct { + ContainerID int `json:"container_id"` + HostID int `json:"host_id"` + Size int `json:"size"` +} + +type subIDRange struct { + Start int + Length int +} + +type ranges []subIDRange + +func (e ranges) Len() int { return len(e) } +func (e ranges) Swap(i, j int) { e[i], e[j] = e[j], e[i] } +func (e ranges) Less(i, j int) bool { return e[i].Start < e[j].Start } + +const ( + subuidFileName = "/etc/subuid" + subgidFileName = "/etc/subgid" +) + +// MkdirAllAndChown creates a directory (include any along the path) and then modifies +// ownership to the requested uid/gid. If the directory already exists, this +// function will still change ownership and permissions. +func MkdirAllAndChown(path string, mode os.FileMode, owner Identity) error { + return mkdirAs(path, mode, owner, true, true) +} + +// MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid. +// If the directory already exists, this function still changes ownership and permissions. +// Note that unlike os.Mkdir(), this function does not return IsExist error +// in case path already exists. +func MkdirAndChown(path string, mode os.FileMode, owner Identity) error { + return mkdirAs(path, mode, owner, false, true) +} + +// MkdirAllAndChownNew creates a directory (include any along the path) and then modifies +// ownership ONLY of newly created directories to the requested uid/gid. If the +// directories along the path exist, no change of ownership or permissions will be performed +func MkdirAllAndChownNew(path string, mode os.FileMode, owner Identity) error { + return mkdirAs(path, mode, owner, true, false) +} + +// GetRootUIDGID retrieves the remapped root uid/gid pair from the set of maps. +// If the maps are empty, then the root uid/gid will default to "real" 0/0 +func GetRootUIDGID(uidMap, gidMap []IDMap) (int, int, error) { + uid, err := toHost(0, uidMap) + if err != nil { + return -1, -1, err + } + gid, err := toHost(0, gidMap) + if err != nil { + return -1, -1, err + } + return uid, gid, nil +} + +// toContainer takes an id mapping, and uses it to translate a +// host ID to the remapped ID. If no map is provided, then the translation +// assumes a 1-to-1 mapping and returns the passed in id +func toContainer(hostID int, idMap []IDMap) (int, error) { + if idMap == nil { + return hostID, nil + } + for _, m := range idMap { + if (hostID >= m.HostID) && (hostID <= (m.HostID + m.Size - 1)) { + contID := m.ContainerID + (hostID - m.HostID) + return contID, nil + } + } + return -1, fmt.Errorf("Host ID %d cannot be mapped to a container ID", hostID) +} + +// toHost takes an id mapping and a remapped ID, and translates the +// ID to the mapped host ID. If no map is provided, then the translation +// assumes a 1-to-1 mapping and returns the passed in id # +func toHost(contID int, idMap []IDMap) (int, error) { + if idMap == nil { + return contID, nil + } + for _, m := range idMap { + if (contID >= m.ContainerID) && (contID <= (m.ContainerID + m.Size - 1)) { + hostID := m.HostID + (contID - m.ContainerID) + return hostID, nil + } + } + return -1, fmt.Errorf("Container ID %d cannot be mapped to a host ID", contID) +} + +// Identity is either a UID and GID pair or a SID (but not both) +type Identity struct { + UID int + GID int + SID string +} + +// IdentityMapping contains a mappings of UIDs and GIDs +type IdentityMapping struct { + uids []IDMap + gids []IDMap +} + +// NewIDMappingsFromMaps creates a new mapping from two slices +// Deprecated: this is a temporary shim while transitioning to IDMapping +func NewIDMappingsFromMaps(uids []IDMap, gids []IDMap) *IdentityMapping { + return &IdentityMapping{uids: uids, gids: gids} +} + +// RootPair returns a uid and gid pair for the root user. The error is ignored +// because a root user always exists, and the defaults are correct when the uid +// and gid maps are empty. +func (i *IdentityMapping) RootPair() Identity { + uid, gid, _ := GetRootUIDGID(i.uids, i.gids) + return Identity{UID: uid, GID: gid} +} + +// ToHost returns the host UID and GID for the container uid, gid. +// Remapping is only performed if the ids aren't already the remapped root ids +func (i *IdentityMapping) ToHost(pair Identity) (Identity, error) { + var err error + target := i.RootPair() + + if pair.UID != target.UID { + target.UID, err = toHost(pair.UID, i.uids) + if err != nil { + return target, err + } + } + + if pair.GID != target.GID { + target.GID, err = toHost(pair.GID, i.gids) + } + return target, err +} + +// ToContainer returns the container UID and GID for the host uid and gid +func (i *IdentityMapping) ToContainer(pair Identity) (int, int, error) { + uid, err := toContainer(pair.UID, i.uids) + if err != nil { + return -1, -1, err + } + gid, err := toContainer(pair.GID, i.gids) + return uid, gid, err +} + +// Empty returns true if there are no id mappings +func (i *IdentityMapping) Empty() bool { + return len(i.uids) == 0 && len(i.gids) == 0 +} + +// UIDs return the UID mapping +// TODO: remove this once everything has been refactored to use pairs +func (i *IdentityMapping) UIDs() []IDMap { + return i.uids +} + +// GIDs return the UID mapping +// TODO: remove this once everything has been refactored to use pairs +func (i *IdentityMapping) GIDs() []IDMap { + return i.gids +} + +func createIDMap(subidRanges ranges) []IDMap { + idMap := []IDMap{} + + containerID := 0 + for _, idrange := range subidRanges { + idMap = append(idMap, IDMap{ + ContainerID: containerID, + HostID: idrange.Start, + Size: idrange.Length, + }) + containerID = containerID + idrange.Length + } + return idMap +} + +func parseSubuid(username string) (ranges, error) { + return parseSubidFile(subuidFileName, username) +} + +func parseSubgid(username string) (ranges, error) { + return parseSubidFile(subgidFileName, username) +} + +// parseSubidFile will read the appropriate file (/etc/subuid or /etc/subgid) +// and return all found ranges for a specified username. If the special value +// "ALL" is supplied for username, then all ranges in the file will be returned +func parseSubidFile(path, username string) (ranges, error) { + var rangeList ranges + + subidFile, err := os.Open(path) + if err != nil { + return rangeList, err + } + defer subidFile.Close() + + s := bufio.NewScanner(subidFile) + for s.Scan() { + text := strings.TrimSpace(s.Text()) + if text == "" || strings.HasPrefix(text, "#") { + continue + } + parts := strings.Split(text, ":") + if len(parts) != 3 { + return rangeList, fmt.Errorf("Cannot parse subuid/gid information: Format not correct for %s file", path) + } + if parts[0] == username || username == "ALL" { + startid, err := strconv.Atoi(parts[1]) + if err != nil { + return rangeList, fmt.Errorf("String to int conversion failed during subuid/gid parsing of %s: %v", path, err) + } + length, err := strconv.Atoi(parts[2]) + if err != nil { + return rangeList, fmt.Errorf("String to int conversion failed during subuid/gid parsing of %s: %v", path, err) + } + rangeList = append(rangeList, subIDRange{startid, length}) + } + } + + return rangeList, s.Err() +} + +// CurrentIdentity returns the identity of the current process +func CurrentIdentity() Identity { + return Identity{UID: os.Getuid(), GID: os.Getegid()} +} diff --git a/vendor/github.com/docker/docker/pkg/idtools/idtools_unix.go b/vendor/github.com/docker/docker/pkg/idtools/idtools_unix.go new file mode 100644 index 0000000000..e7d25ee471 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/idtools/idtools_unix.go @@ -0,0 +1,295 @@ +// +build !windows + +package idtools // import "github.com/docker/docker/pkg/idtools" + +import ( + "bytes" + "fmt" + "io" + "os" + "path/filepath" + "strconv" + "sync" + "syscall" + + "github.com/docker/docker/pkg/system" + "github.com/opencontainers/runc/libcontainer/user" + "github.com/pkg/errors" +) + +var ( + entOnce sync.Once + getentCmd string +) + +func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting bool) error { + // make an array containing the original path asked for, plus (for mkAll == true) + // all path components leading up to the complete path that don't exist before we MkdirAll + // so that we can chown all of them properly at the end. If chownExisting is false, we won't + // chown the full directory path if it exists + + var paths []string + + stat, err := system.Stat(path) + if err == nil { + if !stat.IsDir() { + return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR} + } + if !chownExisting { + return nil + } + + // short-circuit--we were called with an existing directory and chown was requested + return setPermissions(path, mode, owner.UID, owner.GID, stat) + } + + if os.IsNotExist(err) { + paths = []string{path} + } + + if mkAll { + // walk back to "/" looking for directories which do not exist + // and add them to the paths array for chown after creation + dirPath := path + for { + dirPath = filepath.Dir(dirPath) + if dirPath == "/" { + break + } + if _, err := os.Stat(dirPath); err != nil && os.IsNotExist(err) { + paths = append(paths, dirPath) + } + } + if err := system.MkdirAll(path, mode); err != nil { + return err + } + } else { + if err := os.Mkdir(path, mode); err != nil && !os.IsExist(err) { + return err + } + } + // even if it existed, we will chown the requested path + any subpaths that + // didn't exist when we called MkdirAll + for _, pathComponent := range paths { + if err := setPermissions(pathComponent, mode, owner.UID, owner.GID, nil); err != nil { + return err + } + } + return nil +} + +// CanAccess takes a valid (existing) directory and a uid, gid pair and determines +// if that uid, gid pair has access (execute bit) to the directory +func CanAccess(path string, pair Identity) bool { + statInfo, err := system.Stat(path) + if err != nil { + return false + } + fileMode := os.FileMode(statInfo.Mode()) + permBits := fileMode.Perm() + return accessible(statInfo.UID() == uint32(pair.UID), + statInfo.GID() == uint32(pair.GID), permBits) +} + +func accessible(isOwner, isGroup bool, perms os.FileMode) bool { + if isOwner && (perms&0100 == 0100) { + return true + } + if isGroup && (perms&0010 == 0010) { + return true + } + if perms&0001 == 0001 { + return true + } + return false +} + +// LookupUser uses traditional local system files lookup (from libcontainer/user) on a username, +// followed by a call to `getent` for supporting host configured non-files passwd and group dbs +func LookupUser(name string) (user.User, error) { + // first try a local system files lookup using existing capabilities + usr, err := user.LookupUser(name) + if err == nil { + return usr, nil + } + // local files lookup failed; attempt to call `getent` to query configured passwd dbs + usr, err = getentUser(name) + if err != nil { + return user.User{}, err + } + return usr, nil +} + +// LookupUID uses traditional local system files lookup (from libcontainer/user) on a uid, +// followed by a call to `getent` for supporting host configured non-files passwd and group dbs +func LookupUID(uid int) (user.User, error) { + // first try a local system files lookup using existing capabilities + usr, err := user.LookupUid(uid) + if err == nil { + return usr, nil + } + // local files lookup failed; attempt to call `getent` to query configured passwd dbs + return getentUser(strconv.Itoa(uid)) +} + +func getentUser(name string) (user.User, error) { + reader, err := callGetent("passwd", name) + if err != nil { + return user.User{}, err + } + users, err := user.ParsePasswd(reader) + if err != nil { + return user.User{}, err + } + if len(users) == 0 { + return user.User{}, fmt.Errorf("getent failed to find passwd entry for %q", name) + } + return users[0], nil +} + +// LookupGroup uses traditional local system files lookup (from libcontainer/user) on a group name, +// followed by a call to `getent` for supporting host configured non-files passwd and group dbs +func LookupGroup(name string) (user.Group, error) { + // first try a local system files lookup using existing capabilities + group, err := user.LookupGroup(name) + if err == nil { + return group, nil + } + // local files lookup failed; attempt to call `getent` to query configured group dbs + return getentGroup(name) +} + +// LookupGID uses traditional local system files lookup (from libcontainer/user) on a group ID, +// followed by a call to `getent` for supporting host configured non-files passwd and group dbs +func LookupGID(gid int) (user.Group, error) { + // first try a local system files lookup using existing capabilities + group, err := user.LookupGid(gid) + if err == nil { + return group, nil + } + // local files lookup failed; attempt to call `getent` to query configured group dbs + return getentGroup(strconv.Itoa(gid)) +} + +func getentGroup(name string) (user.Group, error) { + reader, err := callGetent("group", name) + if err != nil { + return user.Group{}, err + } + groups, err := user.ParseGroup(reader) + if err != nil { + return user.Group{}, err + } + if len(groups) == 0 { + return user.Group{}, fmt.Errorf("getent failed to find groups entry for %q", name) + } + return groups[0], nil +} + +func callGetent(database, key string) (io.Reader, error) { + entOnce.Do(func() { getentCmd, _ = resolveBinary("getent") }) + // if no `getent` command on host, can't do anything else + if getentCmd == "" { + return nil, fmt.Errorf("unable to find getent command") + } + out, err := execCmd(getentCmd, database, key) + if err != nil { + exitCode, errC := system.GetExitCode(err) + if errC != nil { + return nil, err + } + switch exitCode { + case 1: + return nil, fmt.Errorf("getent reported invalid parameters/database unknown") + case 2: + return nil, fmt.Errorf("getent unable to find entry %q in %s database", key, database) + case 3: + return nil, fmt.Errorf("getent database doesn't support enumeration") + default: + return nil, err + } + + } + return bytes.NewReader(out), nil +} + +// setPermissions performs a chown/chmod only if the uid/gid don't match what's requested +// Normally a Chown is a no-op if uid/gid match, but in some cases this can still cause an error, e.g. if the +// dir is on an NFS share, so don't call chown unless we absolutely must. +// Likewise for setting permissions. +func setPermissions(p string, mode os.FileMode, uid, gid int, stat *system.StatT) error { + if stat == nil { + var err error + stat, err = system.Stat(p) + if err != nil { + return err + } + } + if os.FileMode(stat.Mode()).Perm() != mode.Perm() { + if err := os.Chmod(p, mode.Perm()); err != nil { + return err + } + } + if stat.UID() == uint32(uid) && stat.GID() == uint32(gid) { + return nil + } + return os.Chown(p, uid, gid) +} + +// NewIdentityMapping takes a requested username and +// using the data from /etc/sub{uid,gid} ranges, creates the +// proper uid and gid remapping ranges for that user/group pair +func NewIdentityMapping(name string) (*IdentityMapping, error) { + usr, err := LookupUser(name) + if err != nil { + return nil, fmt.Errorf("Could not get user for username %s: %v", name, err) + } + + subuidRanges, err := lookupSubUIDRanges(usr) + if err != nil { + return nil, err + } + subgidRanges, err := lookupSubGIDRanges(usr) + if err != nil { + return nil, err + } + + return &IdentityMapping{ + uids: subuidRanges, + gids: subgidRanges, + }, nil +} + +func lookupSubUIDRanges(usr user.User) ([]IDMap, error) { + rangeList, err := parseSubuid(strconv.Itoa(usr.Uid)) + if err != nil { + return nil, err + } + if len(rangeList) == 0 { + rangeList, err = parseSubuid(usr.Name) + if err != nil { + return nil, err + } + } + if len(rangeList) == 0 { + return nil, errors.Errorf("no subuid ranges found for user %q", usr.Name) + } + return createIDMap(rangeList), nil +} + +func lookupSubGIDRanges(usr user.User) ([]IDMap, error) { + rangeList, err := parseSubgid(strconv.Itoa(usr.Uid)) + if err != nil { + return nil, err + } + if len(rangeList) == 0 { + rangeList, err = parseSubgid(usr.Name) + if err != nil { + return nil, err + } + } + if len(rangeList) == 0 { + return nil, errors.Errorf("no subgid ranges found for user %q", usr.Name) + } + return createIDMap(rangeList), nil +} diff --git a/vendor/github.com/docker/docker/pkg/idtools/idtools_windows.go b/vendor/github.com/docker/docker/pkg/idtools/idtools_windows.go new file mode 100644 index 0000000000..35ede0fffa --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/idtools/idtools_windows.go @@ -0,0 +1,25 @@ +package idtools // import "github.com/docker/docker/pkg/idtools" + +import ( + "os" + + "github.com/docker/docker/pkg/system" +) + +// This is currently a wrapper around MkdirAll, however, since currently +// permissions aren't set through this path, the identity isn't utilized. +// Ownership is handled elsewhere, but in the future could be support here +// too. +func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting bool) error { + if err := system.MkdirAll(path, mode); err != nil { + return err + } + return nil +} + +// CanAccess takes a valid (existing) directory and a uid, gid pair and determines +// if that uid, gid pair has access (execute bit) to the directory +// Windows does not require/support this function, so always return true +func CanAccess(path string, identity Identity) bool { + return true +} diff --git a/vendor/github.com/docker/docker/pkg/idtools/usergroupadd_linux.go b/vendor/github.com/docker/docker/pkg/idtools/usergroupadd_linux.go new file mode 100644 index 0000000000..bf7ae0564b --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/idtools/usergroupadd_linux.go @@ -0,0 +1,164 @@ +package idtools // import "github.com/docker/docker/pkg/idtools" + +import ( + "fmt" + "regexp" + "sort" + "strconv" + "strings" + "sync" +) + +// add a user and/or group to Linux /etc/passwd, /etc/group using standard +// Linux distribution commands: +// adduser --system --shell /bin/false --disabled-login --disabled-password --no-create-home --group +// useradd -r -s /bin/false + +var ( + once sync.Once + userCommand string + idOutRegexp = regexp.MustCompile(`uid=([0-9]+).*gid=([0-9]+)`) +) + +const ( + // default length for a UID/GID subordinate range + defaultRangeLen = 65536 + defaultRangeStart = 100000 +) + +// AddNamespaceRangesUser takes a username and uses the standard system +// utility to create a system user/group pair used to hold the +// /etc/sub{uid,gid} ranges which will be used for user namespace +// mapping ranges in containers. +func AddNamespaceRangesUser(name string) (int, int, error) { + if err := addUser(name); err != nil { + return -1, -1, fmt.Errorf("Error adding user %q: %v", name, err) + } + + // Query the system for the created uid and gid pair + out, err := execCmd("id", name) + if err != nil { + return -1, -1, fmt.Errorf("Error trying to find uid/gid for new user %q: %v", name, err) + } + matches := idOutRegexp.FindStringSubmatch(strings.TrimSpace(string(out))) + if len(matches) != 3 { + return -1, -1, fmt.Errorf("Can't find uid, gid from `id` output: %q", string(out)) + } + uid, err := strconv.Atoi(matches[1]) + if err != nil { + return -1, -1, fmt.Errorf("Can't convert found uid (%s) to int: %v", matches[1], err) + } + gid, err := strconv.Atoi(matches[2]) + if err != nil { + return -1, -1, fmt.Errorf("Can't convert found gid (%s) to int: %v", matches[2], err) + } + + // Now we need to create the subuid/subgid ranges for our new user/group (system users + // do not get auto-created ranges in subuid/subgid) + + if err := createSubordinateRanges(name); err != nil { + return -1, -1, fmt.Errorf("Couldn't create subordinate ID ranges: %v", err) + } + return uid, gid, nil +} + +func addUser(name string) error { + once.Do(func() { + // set up which commands are used for adding users/groups dependent on distro + if _, err := resolveBinary("adduser"); err == nil { + userCommand = "adduser" + } else if _, err := resolveBinary("useradd"); err == nil { + userCommand = "useradd" + } + }) + var args []string + switch userCommand { + case "adduser": + args = []string{"--system", "--shell", "/bin/false", "--no-create-home", "--disabled-login", "--disabled-password", "--group", name} + case "useradd": + args = []string{"-r", "-s", "/bin/false", name} + default: + return fmt.Errorf("cannot add user; no useradd/adduser binary found") + } + + if out, err := execCmd(userCommand, args...); err != nil { + return fmt.Errorf("failed to add user with error: %v; output: %q", err, string(out)) + } + return nil +} + +func createSubordinateRanges(name string) error { + + // first, we should verify that ranges weren't automatically created + // by the distro tooling + ranges, err := parseSubuid(name) + if err != nil { + return fmt.Errorf("Error while looking for subuid ranges for user %q: %v", name, err) + } + if len(ranges) == 0 { + // no UID ranges; let's create one + startID, err := findNextUIDRange() + if err != nil { + return fmt.Errorf("Can't find available subuid range: %v", err) + } + out, err := execCmd("usermod", "-v", fmt.Sprintf("%d-%d", startID, startID+defaultRangeLen-1), name) + if err != nil { + return fmt.Errorf("Unable to add subuid range to user: %q; output: %s, err: %v", name, out, err) + } + } + + ranges, err = parseSubgid(name) + if err != nil { + return fmt.Errorf("Error while looking for subgid ranges for user %q: %v", name, err) + } + if len(ranges) == 0 { + // no GID ranges; let's create one + startID, err := findNextGIDRange() + if err != nil { + return fmt.Errorf("Can't find available subgid range: %v", err) + } + out, err := execCmd("usermod", "-w", fmt.Sprintf("%d-%d", startID, startID+defaultRangeLen-1), name) + if err != nil { + return fmt.Errorf("Unable to add subgid range to user: %q; output: %s, err: %v", name, out, err) + } + } + return nil +} + +func findNextUIDRange() (int, error) { + ranges, err := parseSubuid("ALL") + if err != nil { + return -1, fmt.Errorf("Couldn't parse all ranges in /etc/subuid file: %v", err) + } + sort.Sort(ranges) + return findNextRangeStart(ranges) +} + +func findNextGIDRange() (int, error) { + ranges, err := parseSubgid("ALL") + if err != nil { + return -1, fmt.Errorf("Couldn't parse all ranges in /etc/subgid file: %v", err) + } + sort.Sort(ranges) + return findNextRangeStart(ranges) +} + +func findNextRangeStart(rangeList ranges) (int, error) { + startID := defaultRangeStart + for _, arange := range rangeList { + if wouldOverlap(arange, startID) { + startID = arange.Start + arange.Length + } + } + return startID, nil +} + +func wouldOverlap(arange subIDRange, ID int) bool { + low := ID + high := ID + defaultRangeLen + if (low >= arange.Start && low <= arange.Start+arange.Length) || + (high <= arange.Start+arange.Length && high >= arange.Start) { + return true + } + return false +} diff --git a/vendor/github.com/docker/docker/pkg/idtools/usergroupadd_unsupported.go b/vendor/github.com/docker/docker/pkg/idtools/usergroupadd_unsupported.go new file mode 100644 index 0000000000..e7c4d63118 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/idtools/usergroupadd_unsupported.go @@ -0,0 +1,12 @@ +// +build !linux + +package idtools // import "github.com/docker/docker/pkg/idtools" + +import "fmt" + +// AddNamespaceRangesUser takes a name and finds an unused uid, gid pair +// and calls the appropriate helper function to add the group and then +// the user to the group in /etc/group and /etc/passwd respectively. +func AddNamespaceRangesUser(name string) (int, int, error) { + return -1, -1, fmt.Errorf("No support for adding users or groups on this OS") +} diff --git a/vendor/github.com/docker/docker/pkg/idtools/utils_unix.go b/vendor/github.com/docker/docker/pkg/idtools/utils_unix.go new file mode 100644 index 0000000000..1e2d4a7a75 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/idtools/utils_unix.go @@ -0,0 +1,31 @@ +// +build !windows + +package idtools // import "github.com/docker/docker/pkg/idtools" + +import ( + "fmt" + "os/exec" + "path/filepath" +) + +func resolveBinary(binname string) (string, error) { + binaryPath, err := exec.LookPath(binname) + if err != nil { + return "", err + } + resolvedPath, err := filepath.EvalSymlinks(binaryPath) + if err != nil { + return "", err + } + // only return no error if the final resolved binary basename + // matches what was searched for + if filepath.Base(resolvedPath) == binname { + return resolvedPath, nil + } + return "", fmt.Errorf("Binary %q does not resolve to a binary of that name in $PATH (%q)", binname, resolvedPath) +} + +func execCmd(cmd string, arg ...string) ([]byte, error) { + execCmd := exec.Command(cmd, arg...) + return execCmd.CombinedOutput() +} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/buffer.go b/vendor/github.com/docker/docker/pkg/ioutils/buffer.go new file mode 100644 index 0000000000..466f79294b --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/ioutils/buffer.go @@ -0,0 +1,51 @@ +package ioutils // import "github.com/docker/docker/pkg/ioutils" + +import ( + "errors" + "io" +) + +var errBufferFull = errors.New("buffer is full") + +type fixedBuffer struct { + buf []byte + pos int + lastRead int +} + +func (b *fixedBuffer) Write(p []byte) (int, error) { + n := copy(b.buf[b.pos:cap(b.buf)], p) + b.pos += n + + if n < len(p) { + if b.pos == cap(b.buf) { + return n, errBufferFull + } + return n, io.ErrShortWrite + } + return n, nil +} + +func (b *fixedBuffer) Read(p []byte) (int, error) { + n := copy(p, b.buf[b.lastRead:b.pos]) + b.lastRead += n + return n, nil +} + +func (b *fixedBuffer) Len() int { + return b.pos - b.lastRead +} + +func (b *fixedBuffer) Cap() int { + return cap(b.buf) +} + +func (b *fixedBuffer) Reset() { + b.pos = 0 + b.lastRead = 0 + b.buf = b.buf[:0] +} + +func (b *fixedBuffer) String() string { + return string(b.buf[b.lastRead:b.pos]) +} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/bytespipe.go b/vendor/github.com/docker/docker/pkg/ioutils/bytespipe.go new file mode 100644 index 0000000000..87514b643d --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/ioutils/bytespipe.go @@ -0,0 +1,187 @@ +package ioutils // import "github.com/docker/docker/pkg/ioutils" + +import ( + "errors" + "io" + "sync" +) + +// maxCap is the highest capacity to use in byte slices that buffer data. +const maxCap = 1e6 + +// minCap is the lowest capacity to use in byte slices that buffer data +const minCap = 64 + +// blockThreshold is the minimum number of bytes in the buffer which will cause +// a write to BytesPipe to block when allocating a new slice. +const blockThreshold = 1e6 + +var ( + // ErrClosed is returned when Write is called on a closed BytesPipe. + ErrClosed = errors.New("write to closed BytesPipe") + + bufPools = make(map[int]*sync.Pool) + bufPoolsLock sync.Mutex +) + +// BytesPipe is io.ReadWriteCloser which works similarly to pipe(queue). +// All written data may be read at most once. Also, BytesPipe allocates +// and releases new byte slices to adjust to current needs, so the buffer +// won't be overgrown after peak loads. +type BytesPipe struct { + mu sync.Mutex + wait *sync.Cond + buf []*fixedBuffer + bufLen int + closeErr error // error to return from next Read. set to nil if not closed. +} + +// NewBytesPipe creates new BytesPipe, initialized by specified slice. +// If buf is nil, then it will be initialized with slice which cap is 64. +// buf will be adjusted in a way that len(buf) == 0, cap(buf) == cap(buf). +func NewBytesPipe() *BytesPipe { + bp := &BytesPipe{} + bp.buf = append(bp.buf, getBuffer(minCap)) + bp.wait = sync.NewCond(&bp.mu) + return bp +} + +// Write writes p to BytesPipe. +// It can allocate new []byte slices in a process of writing. +func (bp *BytesPipe) Write(p []byte) (int, error) { + bp.mu.Lock() + + written := 0 +loop0: + for { + if bp.closeErr != nil { + bp.mu.Unlock() + return written, ErrClosed + } + + if len(bp.buf) == 0 { + bp.buf = append(bp.buf, getBuffer(64)) + } + // get the last buffer + b := bp.buf[len(bp.buf)-1] + + n, err := b.Write(p) + written += n + bp.bufLen += n + + // errBufferFull is an error we expect to get if the buffer is full + if err != nil && err != errBufferFull { + bp.wait.Broadcast() + bp.mu.Unlock() + return written, err + } + + // if there was enough room to write all then break + if len(p) == n { + break + } + + // more data: write to the next slice + p = p[n:] + + // make sure the buffer doesn't grow too big from this write + for bp.bufLen >= blockThreshold { + bp.wait.Wait() + if bp.closeErr != nil { + continue loop0 + } + } + + // add new byte slice to the buffers slice and continue writing + nextCap := b.Cap() * 2 + if nextCap > maxCap { + nextCap = maxCap + } + bp.buf = append(bp.buf, getBuffer(nextCap)) + } + bp.wait.Broadcast() + bp.mu.Unlock() + return written, nil +} + +// CloseWithError causes further reads from a BytesPipe to return immediately. +func (bp *BytesPipe) CloseWithError(err error) error { + bp.mu.Lock() + if err != nil { + bp.closeErr = err + } else { + bp.closeErr = io.EOF + } + bp.wait.Broadcast() + bp.mu.Unlock() + return nil +} + +// Close causes further reads from a BytesPipe to return immediately. +func (bp *BytesPipe) Close() error { + return bp.CloseWithError(nil) +} + +// Read reads bytes from BytesPipe. +// Data could be read only once. +func (bp *BytesPipe) Read(p []byte) (n int, err error) { + bp.mu.Lock() + if bp.bufLen == 0 { + if bp.closeErr != nil { + err := bp.closeErr + bp.mu.Unlock() + return 0, err + } + bp.wait.Wait() + if bp.bufLen == 0 && bp.closeErr != nil { + err := bp.closeErr + bp.mu.Unlock() + return 0, err + } + } + + for bp.bufLen > 0 { + b := bp.buf[0] + read, _ := b.Read(p) // ignore error since fixedBuffer doesn't really return an error + n += read + bp.bufLen -= read + + if b.Len() == 0 { + // it's empty so return it to the pool and move to the next one + returnBuffer(b) + bp.buf[0] = nil + bp.buf = bp.buf[1:] + } + + if len(p) == read { + break + } + + p = p[read:] + } + + bp.wait.Broadcast() + bp.mu.Unlock() + return +} + +func returnBuffer(b *fixedBuffer) { + b.Reset() + bufPoolsLock.Lock() + pool := bufPools[b.Cap()] + bufPoolsLock.Unlock() + if pool != nil { + pool.Put(b) + } +} + +func getBuffer(size int) *fixedBuffer { + bufPoolsLock.Lock() + pool, ok := bufPools[size] + if !ok { + pool = &sync.Pool{New: func() interface{} { return &fixedBuffer{buf: make([]byte, 0, size)} }} + bufPools[size] = pool + } + bufPoolsLock.Unlock() + return pool.Get().(*fixedBuffer) +} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/fswriters.go b/vendor/github.com/docker/docker/pkg/ioutils/fswriters.go new file mode 100644 index 0000000000..534d66ac26 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/ioutils/fswriters.go @@ -0,0 +1,162 @@ +package ioutils // import "github.com/docker/docker/pkg/ioutils" + +import ( + "io" + "io/ioutil" + "os" + "path/filepath" +) + +// NewAtomicFileWriter returns WriteCloser so that writing to it writes to a +// temporary file and closing it atomically changes the temporary file to +// destination path. Writing and closing concurrently is not allowed. +func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, error) { + f, err := ioutil.TempFile(filepath.Dir(filename), ".tmp-"+filepath.Base(filename)) + if err != nil { + return nil, err + } + + abspath, err := filepath.Abs(filename) + if err != nil { + return nil, err + } + return &atomicFileWriter{ + f: f, + fn: abspath, + perm: perm, + }, nil +} + +// AtomicWriteFile atomically writes data to a file named by filename. +func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error { + f, err := NewAtomicFileWriter(filename, perm) + if err != nil { + return err + } + n, err := f.Write(data) + if err == nil && n < len(data) { + err = io.ErrShortWrite + f.(*atomicFileWriter).writeErr = err + } + if err1 := f.Close(); err == nil { + err = err1 + } + return err +} + +type atomicFileWriter struct { + f *os.File + fn string + writeErr error + perm os.FileMode +} + +func (w *atomicFileWriter) Write(dt []byte) (int, error) { + n, err := w.f.Write(dt) + if err != nil { + w.writeErr = err + } + return n, err +} + +func (w *atomicFileWriter) Close() (retErr error) { + defer func() { + if retErr != nil || w.writeErr != nil { + os.Remove(w.f.Name()) + } + }() + if err := w.f.Sync(); err != nil { + w.f.Close() + return err + } + if err := w.f.Close(); err != nil { + return err + } + if err := os.Chmod(w.f.Name(), w.perm); err != nil { + return err + } + if w.writeErr == nil { + return os.Rename(w.f.Name(), w.fn) + } + return nil +} + +// AtomicWriteSet is used to atomically write a set +// of files and ensure they are visible at the same time. +// Must be committed to a new directory. +type AtomicWriteSet struct { + root string +} + +// NewAtomicWriteSet creates a new atomic write set to +// atomically create a set of files. The given directory +// is used as the base directory for storing files before +// commit. If no temporary directory is given the system +// default is used. +func NewAtomicWriteSet(tmpDir string) (*AtomicWriteSet, error) { + td, err := ioutil.TempDir(tmpDir, "write-set-") + if err != nil { + return nil, err + } + + return &AtomicWriteSet{ + root: td, + }, nil +} + +// WriteFile writes a file to the set, guaranteeing the file +// has been synced. +func (ws *AtomicWriteSet) WriteFile(filename string, data []byte, perm os.FileMode) error { + f, err := ws.FileWriter(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) + if err != nil { + return err + } + n, err := f.Write(data) + if err == nil && n < len(data) { + err = io.ErrShortWrite + } + if err1 := f.Close(); err == nil { + err = err1 + } + return err +} + +type syncFileCloser struct { + *os.File +} + +func (w syncFileCloser) Close() error { + err := w.File.Sync() + if err1 := w.File.Close(); err == nil { + err = err1 + } + return err +} + +// FileWriter opens a file writer inside the set. The file +// should be synced and closed before calling commit. +func (ws *AtomicWriteSet) FileWriter(name string, flag int, perm os.FileMode) (io.WriteCloser, error) { + f, err := os.OpenFile(filepath.Join(ws.root, name), flag, perm) + if err != nil { + return nil, err + } + return syncFileCloser{f}, nil +} + +// Cancel cancels the set and removes all temporary data +// created in the set. +func (ws *AtomicWriteSet) Cancel() error { + return os.RemoveAll(ws.root) +} + +// Commit moves all created files to the target directory. The +// target directory must not exist and the parent of the target +// directory must exist. +func (ws *AtomicWriteSet) Commit(target string) error { + return os.Rename(ws.root, target) +} + +// String returns the location the set is writing to. +func (ws *AtomicWriteSet) String() string { + return ws.root +} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/readers.go b/vendor/github.com/docker/docker/pkg/ioutils/readers.go new file mode 100644 index 0000000000..1f657bd3dc --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/ioutils/readers.go @@ -0,0 +1,157 @@ +package ioutils // import "github.com/docker/docker/pkg/ioutils" + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "io" +) + +// ReadCloserWrapper wraps an io.Reader, and implements an io.ReadCloser +// It calls the given callback function when closed. It should be constructed +// with NewReadCloserWrapper +type ReadCloserWrapper struct { + io.Reader + closer func() error +} + +// Close calls back the passed closer function +func (r *ReadCloserWrapper) Close() error { + return r.closer() +} + +// NewReadCloserWrapper returns a new io.ReadCloser. +func NewReadCloserWrapper(r io.Reader, closer func() error) io.ReadCloser { + return &ReadCloserWrapper{ + Reader: r, + closer: closer, + } +} + +type readerErrWrapper struct { + reader io.Reader + closer func() +} + +func (r *readerErrWrapper) Read(p []byte) (int, error) { + n, err := r.reader.Read(p) + if err != nil { + r.closer() + } + return n, err +} + +// NewReaderErrWrapper returns a new io.Reader. +func NewReaderErrWrapper(r io.Reader, closer func()) io.Reader { + return &readerErrWrapper{ + reader: r, + closer: closer, + } +} + +// HashData returns the sha256 sum of src. +func HashData(src io.Reader) (string, error) { + h := sha256.New() + if _, err := io.Copy(h, src); err != nil { + return "", err + } + return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil +} + +// OnEOFReader wraps an io.ReadCloser and a function +// the function will run at the end of file or close the file. +type OnEOFReader struct { + Rc io.ReadCloser + Fn func() +} + +func (r *OnEOFReader) Read(p []byte) (n int, err error) { + n, err = r.Rc.Read(p) + if err == io.EOF { + r.runFunc() + } + return +} + +// Close closes the file and run the function. +func (r *OnEOFReader) Close() error { + err := r.Rc.Close() + r.runFunc() + return err +} + +func (r *OnEOFReader) runFunc() { + if fn := r.Fn; fn != nil { + fn() + r.Fn = nil + } +} + +// cancelReadCloser wraps an io.ReadCloser with a context for cancelling read +// operations. +type cancelReadCloser struct { + cancel func() + pR *io.PipeReader // Stream to read from + pW *io.PipeWriter +} + +// NewCancelReadCloser creates a wrapper that closes the ReadCloser when the +// context is cancelled. The returned io.ReadCloser must be closed when it is +// no longer needed. +func NewCancelReadCloser(ctx context.Context, in io.ReadCloser) io.ReadCloser { + pR, pW := io.Pipe() + + // Create a context used to signal when the pipe is closed + doneCtx, cancel := context.WithCancel(context.Background()) + + p := &cancelReadCloser{ + cancel: cancel, + pR: pR, + pW: pW, + } + + go func() { + _, err := io.Copy(pW, in) + select { + case <-ctx.Done(): + // If the context was closed, p.closeWithError + // was already called. Calling it again would + // change the error that Read returns. + default: + p.closeWithError(err) + } + in.Close() + }() + go func() { + for { + select { + case <-ctx.Done(): + p.closeWithError(ctx.Err()) + case <-doneCtx.Done(): + return + } + } + }() + + return p +} + +// Read wraps the Read method of the pipe that provides data from the wrapped +// ReadCloser. +func (p *cancelReadCloser) Read(buf []byte) (n int, err error) { + return p.pR.Read(buf) +} + +// closeWithError closes the wrapper and its underlying reader. It will +// cause future calls to Read to return err. +func (p *cancelReadCloser) closeWithError(err error) { + p.pW.CloseWithError(err) + p.cancel() +} + +// Close closes the wrapper its underlying reader. It will cause +// future calls to Read to return io.EOF. +func (p *cancelReadCloser) Close() error { + p.closeWithError(io.EOF) + return nil +} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/temp_unix.go b/vendor/github.com/docker/docker/pkg/ioutils/temp_unix.go new file mode 100644 index 0000000000..dc894f9131 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/ioutils/temp_unix.go @@ -0,0 +1,10 @@ +// +build !windows + +package ioutils // import "github.com/docker/docker/pkg/ioutils" + +import "io/ioutil" + +// TempDir on Unix systems is equivalent to ioutil.TempDir. +func TempDir(dir, prefix string) (string, error) { + return ioutil.TempDir(dir, prefix) +} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/temp_windows.go b/vendor/github.com/docker/docker/pkg/ioutils/temp_windows.go new file mode 100644 index 0000000000..ecaba2e36d --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/ioutils/temp_windows.go @@ -0,0 +1,16 @@ +package ioutils // import "github.com/docker/docker/pkg/ioutils" + +import ( + "io/ioutil" + + "github.com/docker/docker/pkg/longpath" +) + +// TempDir is the equivalent of ioutil.TempDir, except that the result is in Windows longpath format. +func TempDir(dir, prefix string) (string, error) { + tempDir, err := ioutil.TempDir(dir, prefix) + if err != nil { + return "", err + } + return longpath.AddPrefix(tempDir), nil +} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/writeflusher.go b/vendor/github.com/docker/docker/pkg/ioutils/writeflusher.go new file mode 100644 index 0000000000..91b8d18266 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/ioutils/writeflusher.go @@ -0,0 +1,92 @@ +package ioutils // import "github.com/docker/docker/pkg/ioutils" + +import ( + "io" + "sync" +) + +// WriteFlusher wraps the Write and Flush operation ensuring that every write +// is a flush. In addition, the Close method can be called to intercept +// Read/Write calls if the targets lifecycle has already ended. +type WriteFlusher struct { + w io.Writer + flusher flusher + flushed chan struct{} + flushedOnce sync.Once + closed chan struct{} + closeLock sync.Mutex +} + +type flusher interface { + Flush() +} + +var errWriteFlusherClosed = io.EOF + +func (wf *WriteFlusher) Write(b []byte) (n int, err error) { + select { + case <-wf.closed: + return 0, errWriteFlusherClosed + default: + } + + n, err = wf.w.Write(b) + wf.Flush() // every write is a flush. + return n, err +} + +// Flush the stream immediately. +func (wf *WriteFlusher) Flush() { + select { + case <-wf.closed: + return + default: + } + + wf.flushedOnce.Do(func() { + close(wf.flushed) + }) + wf.flusher.Flush() +} + +// Flushed returns the state of flushed. +// If it's flushed, return true, or else it return false. +func (wf *WriteFlusher) Flushed() bool { + // BUG(stevvooe): Remove this method. Its use is inherently racy. Seems to + // be used to detect whether or a response code has been issued or not. + // Another hook should be used instead. + var flushed bool + select { + case <-wf.flushed: + flushed = true + default: + } + return flushed +} + +// Close closes the write flusher, disallowing any further writes to the +// target. After the flusher is closed, all calls to write or flush will +// result in an error. +func (wf *WriteFlusher) Close() error { + wf.closeLock.Lock() + defer wf.closeLock.Unlock() + + select { + case <-wf.closed: + return errWriteFlusherClosed + default: + close(wf.closed) + } + return nil +} + +// NewWriteFlusher returns a new WriteFlusher. +func NewWriteFlusher(w io.Writer) *WriteFlusher { + var fl flusher + if f, ok := w.(flusher); ok { + fl = f + } else { + fl = &NopFlusher{} + } + return &WriteFlusher{w: w, flusher: fl, closed: make(chan struct{}), flushed: make(chan struct{})} +} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/writers.go b/vendor/github.com/docker/docker/pkg/ioutils/writers.go new file mode 100644 index 0000000000..61c679497d --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/ioutils/writers.go @@ -0,0 +1,66 @@ +package ioutils // import "github.com/docker/docker/pkg/ioutils" + +import "io" + +// NopWriter represents a type which write operation is nop. +type NopWriter struct{} + +func (*NopWriter) Write(buf []byte) (int, error) { + return len(buf), nil +} + +type nopWriteCloser struct { + io.Writer +} + +func (w *nopWriteCloser) Close() error { return nil } + +// NopWriteCloser returns a nopWriteCloser. +func NopWriteCloser(w io.Writer) io.WriteCloser { + return &nopWriteCloser{w} +} + +// NopFlusher represents a type which flush operation is nop. +type NopFlusher struct{} + +// Flush is a nop operation. +func (f *NopFlusher) Flush() {} + +type writeCloserWrapper struct { + io.Writer + closer func() error +} + +func (r *writeCloserWrapper) Close() error { + return r.closer() +} + +// NewWriteCloserWrapper returns a new io.WriteCloser. +func NewWriteCloserWrapper(r io.Writer, closer func() error) io.WriteCloser { + return &writeCloserWrapper{ + Writer: r, + closer: closer, + } +} + +// WriteCounter wraps a concrete io.Writer and hold a count of the number +// of bytes written to the writer during a "session". +// This can be convenient when write return is masked +// (e.g., json.Encoder.Encode()) +type WriteCounter struct { + Count int64 + Writer io.Writer +} + +// NewWriteCounter returns a new WriteCounter. +func NewWriteCounter(w io.Writer) *WriteCounter { + return &WriteCounter{ + Writer: w, + } +} + +func (wc *WriteCounter) Write(p []byte) (count int, err error) { + count, err = wc.Writer.Write(p) + wc.Count += int64(count) + return +} diff --git a/vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go b/vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go new file mode 100644 index 0000000000..cf8d04b1b2 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go @@ -0,0 +1,283 @@ +package jsonmessage // import "github.com/docker/docker/pkg/jsonmessage" + +import ( + "encoding/json" + "fmt" + "io" + "strings" + "time" + + units "github.com/docker/go-units" + "github.com/moby/term" + "github.com/morikuni/aec" +) + +// RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to +// ensure the formatted time isalways the same number of characters. +const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00" + +// JSONError wraps a concrete Code and Message, `Code` is +// is an integer error code, `Message` is the error message. +type JSONError struct { + Code int `json:"code,omitempty"` + Message string `json:"message,omitempty"` +} + +func (e *JSONError) Error() string { + return e.Message +} + +// JSONProgress describes a Progress. terminalFd is the fd of the current terminal, +// Start is the initial value for the operation. Current is the current status and +// value of the progress made towards Total. Total is the end value describing when +// we made 100% progress for an operation. +type JSONProgress struct { + terminalFd uintptr + Current int64 `json:"current,omitempty"` + Total int64 `json:"total,omitempty"` + Start int64 `json:"start,omitempty"` + // If true, don't show xB/yB + HideCounts bool `json:"hidecounts,omitempty"` + Units string `json:"units,omitempty"` + nowFunc func() time.Time + winSize int +} + +func (p *JSONProgress) String() string { + var ( + width = p.width() + pbBox string + numbersBox string + timeLeftBox string + ) + if p.Current <= 0 && p.Total <= 0 { + return "" + } + if p.Total <= 0 { + switch p.Units { + case "": + current := units.HumanSize(float64(p.Current)) + return fmt.Sprintf("%8v", current) + default: + return fmt.Sprintf("%d %s", p.Current, p.Units) + } + } + + percentage := int(float64(p.Current)/float64(p.Total)*100) / 2 + if percentage > 50 { + percentage = 50 + } + if width > 110 { + // this number can't be negative gh#7136 + numSpaces := 0 + if 50-percentage > 0 { + numSpaces = 50 - percentage + } + pbBox = fmt.Sprintf("[%s>%s] ", strings.Repeat("=", percentage), strings.Repeat(" ", numSpaces)) + } + + switch { + case p.HideCounts: + case p.Units == "": // no units, use bytes + current := units.HumanSize(float64(p.Current)) + total := units.HumanSize(float64(p.Total)) + + numbersBox = fmt.Sprintf("%8v/%v", current, total) + + if p.Current > p.Total { + // remove total display if the reported current is wonky. + numbersBox = fmt.Sprintf("%8v", current) + } + default: + numbersBox = fmt.Sprintf("%d/%d %s", p.Current, p.Total, p.Units) + + if p.Current > p.Total { + // remove total display if the reported current is wonky. + numbersBox = fmt.Sprintf("%d %s", p.Current, p.Units) + } + } + + if p.Current > 0 && p.Start > 0 && percentage < 50 { + fromStart := p.now().Sub(time.Unix(p.Start, 0)) + perEntry := fromStart / time.Duration(p.Current) + left := time.Duration(p.Total-p.Current) * perEntry + left = (left / time.Second) * time.Second + + if width > 50 { + timeLeftBox = " " + left.String() + } + } + return pbBox + numbersBox + timeLeftBox +} + +// shim for testing +func (p *JSONProgress) now() time.Time { + if p.nowFunc == nil { + p.nowFunc = func() time.Time { + return time.Now().UTC() + } + } + return p.nowFunc() +} + +// shim for testing +func (p *JSONProgress) width() int { + if p.winSize != 0 { + return p.winSize + } + ws, err := term.GetWinsize(p.terminalFd) + if err == nil { + return int(ws.Width) + } + return 200 +} + +// JSONMessage defines a message struct. It describes +// the created time, where it from, status, ID of the +// message. It's used for docker events. +type JSONMessage struct { + Stream string `json:"stream,omitempty"` + Status string `json:"status,omitempty"` + Progress *JSONProgress `json:"progressDetail,omitempty"` + ProgressMessage string `json:"progress,omitempty"` // deprecated + ID string `json:"id,omitempty"` + From string `json:"from,omitempty"` + Time int64 `json:"time,omitempty"` + TimeNano int64 `json:"timeNano,omitempty"` + Error *JSONError `json:"errorDetail,omitempty"` + ErrorMessage string `json:"error,omitempty"` // deprecated + // Aux contains out-of-band data, such as digests for push signing and image id after building. + Aux *json.RawMessage `json:"aux,omitempty"` +} + +func clearLine(out io.Writer) { + eraseMode := aec.EraseModes.All + cl := aec.EraseLine(eraseMode) + fmt.Fprint(out, cl) +} + +func cursorUp(out io.Writer, l uint) { + fmt.Fprint(out, aec.Up(l)) +} + +func cursorDown(out io.Writer, l uint) { + fmt.Fprint(out, aec.Down(l)) +} + +// Display displays the JSONMessage to `out`. If `isTerminal` is true, it will erase the +// entire current line when displaying the progressbar. +func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error { + if jm.Error != nil { + if jm.Error.Code == 401 { + return fmt.Errorf("authentication is required") + } + return jm.Error + } + var endl string + if isTerminal && jm.Stream == "" && jm.Progress != nil { + clearLine(out) + endl = "\r" + fmt.Fprint(out, endl) + } else if jm.Progress != nil && jm.Progress.String() != "" { // disable progressbar in non-terminal + return nil + } + if jm.TimeNano != 0 { + fmt.Fprintf(out, "%s ", time.Unix(0, jm.TimeNano).Format(RFC3339NanoFixed)) + } else if jm.Time != 0 { + fmt.Fprintf(out, "%s ", time.Unix(jm.Time, 0).Format(RFC3339NanoFixed)) + } + if jm.ID != "" { + fmt.Fprintf(out, "%s: ", jm.ID) + } + if jm.From != "" { + fmt.Fprintf(out, "(from %s) ", jm.From) + } + if jm.Progress != nil && isTerminal { + fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl) + } else if jm.ProgressMessage != "" { // deprecated + fmt.Fprintf(out, "%s %s%s", jm.Status, jm.ProgressMessage, endl) + } else if jm.Stream != "" { + fmt.Fprintf(out, "%s%s", jm.Stream, endl) + } else { + fmt.Fprintf(out, "%s%s\n", jm.Status, endl) + } + return nil +} + +// DisplayJSONMessagesStream displays a json message stream from `in` to `out`, `isTerminal` +// describes if `out` is a terminal. If this is the case, it will print `\n` at the end of +// each line and move the cursor while displaying. +func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(JSONMessage)) error { + var ( + dec = json.NewDecoder(in) + ids = make(map[string]uint) + ) + + for { + var diff uint + var jm JSONMessage + if err := dec.Decode(&jm); err != nil { + if err == io.EOF { + break + } + return err + } + + if jm.Aux != nil { + if auxCallback != nil { + auxCallback(jm) + } + continue + } + + if jm.Progress != nil { + jm.Progress.terminalFd = terminalFd + } + if jm.ID != "" && (jm.Progress != nil || jm.ProgressMessage != "") { + line, ok := ids[jm.ID] + if !ok { + // NOTE: This approach of using len(id) to + // figure out the number of lines of history + // only works as long as we clear the history + // when we output something that's not + // accounted for in the map, such as a line + // with no ID. + line = uint(len(ids)) + ids[jm.ID] = line + if isTerminal { + fmt.Fprintf(out, "\n") + } + } + diff = uint(len(ids)) - line + if isTerminal { + cursorUp(out, diff) + } + } else { + // When outputting something that isn't progress + // output, clear the history of previous lines. We + // don't want progress entries from some previous + // operation to be updated (for example, pull -a + // with multiple tags). + ids = make(map[string]uint) + } + err := jm.Display(out, isTerminal) + if jm.ID != "" && isTerminal { + cursorDown(out, diff) + } + if err != nil { + return err + } + } + return nil +} + +type stream interface { + io.Writer + FD() uintptr + IsTerminal() bool +} + +// DisplayJSONMessagesToStream prints json messages to the output stream +func DisplayJSONMessagesToStream(in io.Reader, stream stream, auxCallback func(JSONMessage)) error { + return DisplayJSONMessagesStream(in, stream, stream.FD(), stream.IsTerminal(), auxCallback) +} diff --git a/vendor/github.com/docker/docker/pkg/longpath/longpath.go b/vendor/github.com/docker/docker/pkg/longpath/longpath.go new file mode 100644 index 0000000000..4177affba2 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/longpath/longpath.go @@ -0,0 +1,26 @@ +// longpath introduces some constants and helper functions for handling long paths +// in Windows, which are expected to be prepended with `\\?\` and followed by either +// a drive letter, a UNC server\share, or a volume identifier. + +package longpath // import "github.com/docker/docker/pkg/longpath" + +import ( + "strings" +) + +// Prefix is the longpath prefix for Windows file paths. +const Prefix = `\\?\` + +// AddPrefix will add the Windows long path prefix to the path provided if +// it does not already have it. +func AddPrefix(path string) string { + if !strings.HasPrefix(path, Prefix) { + if strings.HasPrefix(path, `\\`) { + // This is a UNC path, so we need to add 'UNC' to the path as well. + path = Prefix + `UNC` + path[1:] + } else { + path = Prefix + path + } + } + return path +} diff --git a/vendor/github.com/docker/docker/pkg/pools/pools.go b/vendor/github.com/docker/docker/pkg/pools/pools.go new file mode 100644 index 0000000000..3792c67a9e --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/pools/pools.go @@ -0,0 +1,137 @@ +// Package pools provides a collection of pools which provide various +// data types with buffers. These can be used to lower the number of +// memory allocations and reuse buffers. +// +// New pools should be added to this package to allow them to be +// shared across packages. +// +// Utility functions which operate on pools should be added to this +// package to allow them to be reused. +package pools // import "github.com/docker/docker/pkg/pools" + +import ( + "bufio" + "io" + "sync" + + "github.com/docker/docker/pkg/ioutils" +) + +const buffer32K = 32 * 1024 + +var ( + // BufioReader32KPool is a pool which returns bufio.Reader with a 32K buffer. + BufioReader32KPool = newBufioReaderPoolWithSize(buffer32K) + // BufioWriter32KPool is a pool which returns bufio.Writer with a 32K buffer. + BufioWriter32KPool = newBufioWriterPoolWithSize(buffer32K) + buffer32KPool = newBufferPoolWithSize(buffer32K) +) + +// BufioReaderPool is a bufio reader that uses sync.Pool. +type BufioReaderPool struct { + pool sync.Pool +} + +// newBufioReaderPoolWithSize is unexported because new pools should be +// added here to be shared where required. +func newBufioReaderPoolWithSize(size int) *BufioReaderPool { + return &BufioReaderPool{ + pool: sync.Pool{ + New: func() interface{} { return bufio.NewReaderSize(nil, size) }, + }, + } +} + +// Get returns a bufio.Reader which reads from r. The buffer size is that of the pool. +func (bufPool *BufioReaderPool) Get(r io.Reader) *bufio.Reader { + buf := bufPool.pool.Get().(*bufio.Reader) + buf.Reset(r) + return buf +} + +// Put puts the bufio.Reader back into the pool. +func (bufPool *BufioReaderPool) Put(b *bufio.Reader) { + b.Reset(nil) + bufPool.pool.Put(b) +} + +type bufferPool struct { + pool sync.Pool +} + +func newBufferPoolWithSize(size int) *bufferPool { + return &bufferPool{ + pool: sync.Pool{ + New: func() interface{} { s := make([]byte, size); return &s }, + }, + } +} + +func (bp *bufferPool) Get() *[]byte { + return bp.pool.Get().(*[]byte) +} + +func (bp *bufferPool) Put(b *[]byte) { + bp.pool.Put(b) +} + +// Copy is a convenience wrapper which uses a buffer to avoid allocation in io.Copy. +func Copy(dst io.Writer, src io.Reader) (written int64, err error) { + buf := buffer32KPool.Get() + written, err = io.CopyBuffer(dst, src, *buf) + buffer32KPool.Put(buf) + return +} + +// NewReadCloserWrapper returns a wrapper which puts the bufio.Reader back +// into the pool and closes the reader if it's an io.ReadCloser. +func (bufPool *BufioReaderPool) NewReadCloserWrapper(buf *bufio.Reader, r io.Reader) io.ReadCloser { + return ioutils.NewReadCloserWrapper(r, func() error { + if readCloser, ok := r.(io.ReadCloser); ok { + readCloser.Close() + } + bufPool.Put(buf) + return nil + }) +} + +// BufioWriterPool is a bufio writer that uses sync.Pool. +type BufioWriterPool struct { + pool sync.Pool +} + +// newBufioWriterPoolWithSize is unexported because new pools should be +// added here to be shared where required. +func newBufioWriterPoolWithSize(size int) *BufioWriterPool { + return &BufioWriterPool{ + pool: sync.Pool{ + New: func() interface{} { return bufio.NewWriterSize(nil, size) }, + }, + } +} + +// Get returns a bufio.Writer which writes to w. The buffer size is that of the pool. +func (bufPool *BufioWriterPool) Get(w io.Writer) *bufio.Writer { + buf := bufPool.pool.Get().(*bufio.Writer) + buf.Reset(w) + return buf +} + +// Put puts the bufio.Writer back into the pool. +func (bufPool *BufioWriterPool) Put(b *bufio.Writer) { + b.Reset(nil) + bufPool.pool.Put(b) +} + +// NewWriteCloserWrapper returns a wrapper which puts the bufio.Writer back +// into the pool and closes the writer if it's an io.Writecloser. +func (bufPool *BufioWriterPool) NewWriteCloserWrapper(buf *bufio.Writer, w io.Writer) io.WriteCloser { + return ioutils.NewWriteCloserWrapper(w, func() error { + buf.Flush() + if writeCloser, ok := w.(io.WriteCloser); ok { + writeCloser.Close() + } + bufPool.Put(buf) + return nil + }) +} diff --git a/vendor/github.com/docker/docker/pkg/stdcopy/stdcopy.go b/vendor/github.com/docker/docker/pkg/stdcopy/stdcopy.go new file mode 100644 index 0000000000..8f6e0a737a --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/stdcopy/stdcopy.go @@ -0,0 +1,190 @@ +package stdcopy // import "github.com/docker/docker/pkg/stdcopy" + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "sync" +) + +// StdType is the type of standard stream +// a writer can multiplex to. +type StdType byte + +const ( + // Stdin represents standard input stream type. + Stdin StdType = iota + // Stdout represents standard output stream type. + Stdout + // Stderr represents standard error steam type. + Stderr + // Systemerr represents errors originating from the system that make it + // into the multiplexed stream. + Systemerr + + stdWriterPrefixLen = 8 + stdWriterFdIndex = 0 + stdWriterSizeIndex = 4 + + startingBufLen = 32*1024 + stdWriterPrefixLen + 1 +) + +var bufPool = &sync.Pool{New: func() interface{} { return bytes.NewBuffer(nil) }} + +// stdWriter is wrapper of io.Writer with extra customized info. +type stdWriter struct { + io.Writer + prefix byte +} + +// Write sends the buffer to the underneath writer. +// It inserts the prefix header before the buffer, +// so stdcopy.StdCopy knows where to multiplex the output. +// It makes stdWriter to implement io.Writer. +func (w *stdWriter) Write(p []byte) (n int, err error) { + if w == nil || w.Writer == nil { + return 0, errors.New("Writer not instantiated") + } + if p == nil { + return 0, nil + } + + header := [stdWriterPrefixLen]byte{stdWriterFdIndex: w.prefix} + binary.BigEndian.PutUint32(header[stdWriterSizeIndex:], uint32(len(p))) + buf := bufPool.Get().(*bytes.Buffer) + buf.Write(header[:]) + buf.Write(p) + + n, err = w.Writer.Write(buf.Bytes()) + n -= stdWriterPrefixLen + if n < 0 { + n = 0 + } + + buf.Reset() + bufPool.Put(buf) + return +} + +// NewStdWriter instantiates a new Writer. +// Everything written to it will be encapsulated using a custom format, +// and written to the underlying `w` stream. +// This allows multiple write streams (e.g. stdout and stderr) to be muxed into a single connection. +// `t` indicates the id of the stream to encapsulate. +// It can be stdcopy.Stdin, stdcopy.Stdout, stdcopy.Stderr. +func NewStdWriter(w io.Writer, t StdType) io.Writer { + return &stdWriter{ + Writer: w, + prefix: byte(t), + } +} + +// StdCopy is a modified version of io.Copy. +// +// StdCopy will demultiplex `src`, assuming that it contains two streams, +// previously multiplexed together using a StdWriter instance. +// As it reads from `src`, StdCopy will write to `dstout` and `dsterr`. +// +// StdCopy will read until it hits EOF on `src`. It will then return a nil error. +// In other words: if `err` is non nil, it indicates a real underlying error. +// +// `written` will hold the total number of bytes written to `dstout` and `dsterr`. +func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error) { + var ( + buf = make([]byte, startingBufLen) + bufLen = len(buf) + nr, nw int + er, ew error + out io.Writer + frameSize int + ) + + for { + // Make sure we have at least a full header + for nr < stdWriterPrefixLen { + var nr2 int + nr2, er = src.Read(buf[nr:]) + nr += nr2 + if er == io.EOF { + if nr < stdWriterPrefixLen { + return written, nil + } + break + } + if er != nil { + return 0, er + } + } + + stream := StdType(buf[stdWriterFdIndex]) + // Check the first byte to know where to write + switch stream { + case Stdin: + fallthrough + case Stdout: + // Write on stdout + out = dstout + case Stderr: + // Write on stderr + out = dsterr + case Systemerr: + // If we're on Systemerr, we won't write anywhere. + // NB: if this code changes later, make sure you don't try to write + // to outstream if Systemerr is the stream + out = nil + default: + return 0, fmt.Errorf("Unrecognized input header: %d", buf[stdWriterFdIndex]) + } + + // Retrieve the size of the frame + frameSize = int(binary.BigEndian.Uint32(buf[stdWriterSizeIndex : stdWriterSizeIndex+4])) + + // Check if the buffer is big enough to read the frame. + // Extend it if necessary. + if frameSize+stdWriterPrefixLen > bufLen { + buf = append(buf, make([]byte, frameSize+stdWriterPrefixLen-bufLen+1)...) + bufLen = len(buf) + } + + // While the amount of bytes read is less than the size of the frame + header, we keep reading + for nr < frameSize+stdWriterPrefixLen { + var nr2 int + nr2, er = src.Read(buf[nr:]) + nr += nr2 + if er == io.EOF { + if nr < frameSize+stdWriterPrefixLen { + return written, nil + } + break + } + if er != nil { + return 0, er + } + } + + // we might have an error from the source mixed up in our multiplexed + // stream. if we do, return it. + if stream == Systemerr { + return written, fmt.Errorf("error from daemon in stream: %s", string(buf[stdWriterPrefixLen:frameSize+stdWriterPrefixLen])) + } + + // Write the retrieved frame (without header) + nw, ew = out.Write(buf[stdWriterPrefixLen : frameSize+stdWriterPrefixLen]) + if ew != nil { + return 0, ew + } + + // If the frame has not been fully written: error + if nw != frameSize { + return 0, io.ErrShortWrite + } + written += int64(nw) + + // Move the rest of the buffer to the beginning + copy(buf, buf[frameSize+stdWriterPrefixLen:]) + // Move the index + nr -= frameSize + stdWriterPrefixLen + } +} diff --git a/vendor/github.com/docker/docker/pkg/stringid/README.md b/vendor/github.com/docker/docker/pkg/stringid/README.md new file mode 100644 index 0000000000..37a5098fd9 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/stringid/README.md @@ -0,0 +1 @@ +This package provides helper functions for dealing with string identifiers diff --git a/vendor/github.com/docker/docker/pkg/stringid/stringid.go b/vendor/github.com/docker/docker/pkg/stringid/stringid.go new file mode 100644 index 0000000000..5fe071d628 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/stringid/stringid.go @@ -0,0 +1,63 @@ +// Package stringid provides helper functions for dealing with string identifiers +package stringid // import "github.com/docker/docker/pkg/stringid" + +import ( + "crypto/rand" + "encoding/hex" + "fmt" + "regexp" + "strconv" + "strings" +) + +const shortLen = 12 + +var ( + validShortID = regexp.MustCompile("^[a-f0-9]{12}$") + validHex = regexp.MustCompile(`^[a-f0-9]{64}$`) +) + +// IsShortID determines if an arbitrary string *looks like* a short ID. +func IsShortID(id string) bool { + return validShortID.MatchString(id) +} + +// TruncateID returns a shorthand version of a string identifier for convenience. +// A collision with other shorthands is very unlikely, but possible. +// In case of a collision a lookup with TruncIndex.Get() will fail, and the caller +// will need to use a longer prefix, or the full-length Id. +func TruncateID(id string) string { + if i := strings.IndexRune(id, ':'); i >= 0 { + id = id[i+1:] + } + if len(id) > shortLen { + id = id[:shortLen] + } + return id +} + +// GenerateRandomID returns a unique id. +func GenerateRandomID() string { + b := make([]byte, 32) + for { + if _, err := rand.Read(b); err != nil { + panic(err) // This shouldn't happen + } + id := hex.EncodeToString(b) + // if we try to parse the truncated for as an int and we don't have + // an error then the value is all numeric and causes issues when + // used as a hostname. ref #3869 + if _, err := strconv.ParseInt(TruncateID(id), 10, 64); err == nil { + continue + } + return id + } +} + +// ValidateID checks whether an ID string is a valid image ID. +func ValidateID(id string) error { + if ok := validHex.MatchString(id); !ok { + return fmt.Errorf("image ID %q is invalid", id) + } + return nil +} diff --git a/vendor/github.com/docker/docker/pkg/system/args_windows.go b/vendor/github.com/docker/docker/pkg/system/args_windows.go new file mode 100644 index 0000000000..b7c9487a06 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/args_windows.go @@ -0,0 +1,16 @@ +package system // import "github.com/docker/docker/pkg/system" + +import ( + "strings" + + "golang.org/x/sys/windows" +) + +// EscapeArgs makes a Windows-style escaped command line from a set of arguments +func EscapeArgs(args []string) string { + escapedArgs := make([]string, len(args)) + for i, a := range args { + escapedArgs[i] = windows.EscapeArg(a) + } + return strings.Join(escapedArgs, " ") +} diff --git a/vendor/github.com/docker/docker/pkg/system/chtimes.go b/vendor/github.com/docker/docker/pkg/system/chtimes.go new file mode 100644 index 0000000000..c26a4e24b6 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/chtimes.go @@ -0,0 +1,31 @@ +package system // import "github.com/docker/docker/pkg/system" + +import ( + "os" + "time" +) + +// Chtimes changes the access time and modified time of a file at the given path +func Chtimes(name string, atime time.Time, mtime time.Time) error { + unixMinTime := time.Unix(0, 0) + unixMaxTime := maxTime + + // If the modified time is prior to the Unix Epoch, or after the + // end of Unix Time, os.Chtimes has undefined behavior + // default to Unix Epoch in this case, just in case + + if atime.Before(unixMinTime) || atime.After(unixMaxTime) { + atime = unixMinTime + } + + if mtime.Before(unixMinTime) || mtime.After(unixMaxTime) { + mtime = unixMinTime + } + + if err := os.Chtimes(name, atime, mtime); err != nil { + return err + } + + // Take platform specific action for setting create time. + return setCTime(name, mtime) +} diff --git a/vendor/github.com/docker/docker/pkg/system/chtimes_nowindows.go b/vendor/github.com/docker/docker/pkg/system/chtimes_nowindows.go new file mode 100644 index 0000000000..d5fab96f9d --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/chtimes_nowindows.go @@ -0,0 +1,14 @@ +// +build !windows + +package system // import "github.com/docker/docker/pkg/system" + +import ( + "time" +) + +// setCTime will set the create time on a file. On Unix, the create +// time is updated as a side effect of setting the modified time, so +// no action is required. +func setCTime(path string, ctime time.Time) error { + return nil +} diff --git a/vendor/github.com/docker/docker/pkg/system/chtimes_windows.go b/vendor/github.com/docker/docker/pkg/system/chtimes_windows.go new file mode 100644 index 0000000000..6664b8bcad --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/chtimes_windows.go @@ -0,0 +1,26 @@ +package system // import "github.com/docker/docker/pkg/system" + +import ( + "time" + + "golang.org/x/sys/windows" +) + +// setCTime will set the create time on a file. On Windows, this requires +// calling SetFileTime and explicitly including the create time. +func setCTime(path string, ctime time.Time) error { + ctimespec := windows.NsecToTimespec(ctime.UnixNano()) + pathp, e := windows.UTF16PtrFromString(path) + if e != nil { + return e + } + h, e := windows.CreateFile(pathp, + windows.FILE_WRITE_ATTRIBUTES, windows.FILE_SHARE_WRITE, nil, + windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0) + if e != nil { + return e + } + defer windows.Close(h) + c := windows.NsecToFiletime(windows.TimespecToNsec(ctimespec)) + return windows.SetFileTime(h, &c, nil, nil) +} diff --git a/vendor/github.com/docker/docker/pkg/system/errors.go b/vendor/github.com/docker/docker/pkg/system/errors.go new file mode 100644 index 0000000000..2573d71622 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/errors.go @@ -0,0 +1,13 @@ +package system // import "github.com/docker/docker/pkg/system" + +import ( + "errors" +) + +var ( + // ErrNotSupportedPlatform means the platform is not supported. + ErrNotSupportedPlatform = errors.New("platform and architecture is not supported") + + // ErrNotSupportedOperatingSystem means the operating system is not supported. + ErrNotSupportedOperatingSystem = errors.New("operating system is not supported") +) diff --git a/vendor/github.com/docker/docker/pkg/system/exitcode.go b/vendor/github.com/docker/docker/pkg/system/exitcode.go new file mode 100644 index 0000000000..4ba8fe35bf --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/exitcode.go @@ -0,0 +1,19 @@ +package system // import "github.com/docker/docker/pkg/system" + +import ( + "fmt" + "os/exec" + "syscall" +) + +// GetExitCode returns the ExitStatus of the specified error if its type is +// exec.ExitError, returns 0 and an error otherwise. +func GetExitCode(err error) (int, error) { + exitCode := 0 + if exiterr, ok := err.(*exec.ExitError); ok { + if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok { + return procExit.ExitStatus(), nil + } + } + return exitCode, fmt.Errorf("failed to get exit code") +} diff --git a/vendor/github.com/docker/docker/pkg/system/filesys_unix.go b/vendor/github.com/docker/docker/pkg/system/filesys_unix.go new file mode 100644 index 0000000000..dcee3e9f98 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/filesys_unix.go @@ -0,0 +1,67 @@ +// +build !windows + +package system // import "github.com/docker/docker/pkg/system" + +import ( + "io/ioutil" + "os" + "path/filepath" +) + +// MkdirAllWithACL is a wrapper for os.MkdirAll on unix systems. +func MkdirAllWithACL(path string, perm os.FileMode, sddl string) error { + return os.MkdirAll(path, perm) +} + +// MkdirAll creates a directory named path along with any necessary parents, +// with permission specified by attribute perm for all dir created. +func MkdirAll(path string, perm os.FileMode) error { + return os.MkdirAll(path, perm) +} + +// IsAbs is a platform-specific wrapper for filepath.IsAbs. +func IsAbs(path string) bool { + return filepath.IsAbs(path) +} + +// The functions below here are wrappers for the equivalents in the os and ioutils packages. +// They are passthrough on Unix platforms, and only relevant on Windows. + +// CreateSequential creates the named file with mode 0666 (before umask), truncating +// it if it already exists. If successful, methods on the returned +// File can be used for I/O; the associated file descriptor has mode +// O_RDWR. +// If there is an error, it will be of type *PathError. +func CreateSequential(name string) (*os.File, error) { + return os.Create(name) +} + +// OpenSequential opens the named file for reading. If successful, methods on +// the returned file can be used for reading; the associated file +// descriptor has mode O_RDONLY. +// If there is an error, it will be of type *PathError. +func OpenSequential(name string) (*os.File, error) { + return os.Open(name) +} + +// OpenFileSequential is the generalized open call; most users will use Open +// or Create instead. It opens the named file with specified flag +// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful, +// methods on the returned File can be used for I/O. +// If there is an error, it will be of type *PathError. +func OpenFileSequential(name string, flag int, perm os.FileMode) (*os.File, error) { + return os.OpenFile(name, flag, perm) +} + +// TempFileSequential creates a new temporary file in the directory dir +// with a name beginning with prefix, opens the file for reading +// and writing, and returns the resulting *os.File. +// If dir is the empty string, TempFile uses the default directory +// for temporary files (see os.TempDir). +// Multiple programs calling TempFile simultaneously +// will not choose the same file. The caller can use f.Name() +// to find the pathname of the file. It is the caller's responsibility +// to remove the file when no longer needed. +func TempFileSequential(dir, prefix string) (f *os.File, err error) { + return ioutil.TempFile(dir, prefix) +} diff --git a/vendor/github.com/docker/docker/pkg/system/filesys_windows.go b/vendor/github.com/docker/docker/pkg/system/filesys_windows.go new file mode 100644 index 0000000000..b4646277ab --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/filesys_windows.go @@ -0,0 +1,292 @@ +package system // import "github.com/docker/docker/pkg/system" + +import ( + "os" + "path/filepath" + "regexp" + "strconv" + "strings" + "sync" + "syscall" + "time" + "unsafe" + + "golang.org/x/sys/windows" +) + +const ( + // SddlAdministratorsLocalSystem is local administrators plus NT AUTHORITY\System + SddlAdministratorsLocalSystem = "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)" +) + +// MkdirAllWithACL is a wrapper for MkdirAll that creates a directory +// with an appropriate SDDL defined ACL. +func MkdirAllWithACL(path string, perm os.FileMode, sddl string) error { + return mkdirall(path, true, sddl) +} + +// MkdirAll implementation that is volume path aware for Windows. It can be used +// as a drop-in replacement for os.MkdirAll() +func MkdirAll(path string, _ os.FileMode) error { + return mkdirall(path, false, "") +} + +// mkdirall is a custom version of os.MkdirAll modified for use on Windows +// so that it is both volume path aware, and can create a directory with +// a DACL. +func mkdirall(path string, applyACL bool, sddl string) error { + if re := regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}$`); re.MatchString(path) { + return nil + } + + // The rest of this method is largely copied from os.MkdirAll and should be kept + // as-is to ensure compatibility. + + // Fast path: if we can tell whether path is a directory or file, stop with success or error. + dir, err := os.Stat(path) + if err == nil { + if dir.IsDir() { + return nil + } + return &os.PathError{ + Op: "mkdir", + Path: path, + Err: syscall.ENOTDIR, + } + } + + // Slow path: make sure parent exists and then call Mkdir for path. + i := len(path) + for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator. + i-- + } + + j := i + for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element. + j-- + } + + if j > 1 { + // Create parent + err = mkdirall(path[0:j-1], false, sddl) + if err != nil { + return err + } + } + + // Parent now exists; invoke os.Mkdir or mkdirWithACL and use its result. + if applyACL { + err = mkdirWithACL(path, sddl) + } else { + err = os.Mkdir(path, 0) + } + + if err != nil { + // Handle arguments like "foo/." by + // double-checking that directory doesn't exist. + dir, err1 := os.Lstat(path) + if err1 == nil && dir.IsDir() { + return nil + } + return err + } + return nil +} + +// mkdirWithACL creates a new directory. If there is an error, it will be of +// type *PathError. . +// +// This is a modified and combined version of os.Mkdir and windows.Mkdir +// in golang to cater for creating a directory am ACL permitting full +// access, with inheritance, to any subfolder/file for Built-in Administrators +// and Local System. +func mkdirWithACL(name string, sddl string) error { + sa := windows.SecurityAttributes{Length: 0} + sd, err := windows.SecurityDescriptorFromString(sddl) + if err != nil { + return &os.PathError{Op: "mkdir", Path: name, Err: err} + } + sa.Length = uint32(unsafe.Sizeof(sa)) + sa.InheritHandle = 1 + sa.SecurityDescriptor = sd + + namep, err := windows.UTF16PtrFromString(name) + if err != nil { + return &os.PathError{Op: "mkdir", Path: name, Err: err} + } + + e := windows.CreateDirectory(namep, &sa) + if e != nil { + return &os.PathError{Op: "mkdir", Path: name, Err: e} + } + return nil +} + +// IsAbs is a platform-specific wrapper for filepath.IsAbs. On Windows, +// golang filepath.IsAbs does not consider a path \windows\system32 as absolute +// as it doesn't start with a drive-letter/colon combination. However, in +// docker we need to verify things such as WORKDIR /windows/system32 in +// a Dockerfile (which gets translated to \windows\system32 when being processed +// by the daemon. This SHOULD be treated as absolute from a docker processing +// perspective. +func IsAbs(path string) bool { + if filepath.IsAbs(path) || strings.HasPrefix(path, string(os.PathSeparator)) { + return true + } + return false +} + +// The origin of the functions below here are the golang OS and windows packages, +// slightly modified to only cope with files, not directories due to the +// specific use case. +// +// The alteration is to allow a file on Windows to be opened with +// FILE_FLAG_SEQUENTIAL_SCAN (particular for docker load), to avoid eating +// the standby list, particularly when accessing large files such as layer.tar. + +// CreateSequential creates the named file with mode 0666 (before umask), truncating +// it if it already exists. If successful, methods on the returned +// File can be used for I/O; the associated file descriptor has mode +// O_RDWR. +// If there is an error, it will be of type *PathError. +func CreateSequential(name string) (*os.File, error) { + return OpenFileSequential(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0) +} + +// OpenSequential opens the named file for reading. If successful, methods on +// the returned file can be used for reading; the associated file +// descriptor has mode O_RDONLY. +// If there is an error, it will be of type *PathError. +func OpenSequential(name string) (*os.File, error) { + return OpenFileSequential(name, os.O_RDONLY, 0) +} + +// OpenFileSequential is the generalized open call; most users will use Open +// or Create instead. +// If there is an error, it will be of type *PathError. +func OpenFileSequential(name string, flag int, _ os.FileMode) (*os.File, error) { + if name == "" { + return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} + } + r, errf := windowsOpenFileSequential(name, flag, 0) + if errf == nil { + return r, nil + } + return nil, &os.PathError{Op: "open", Path: name, Err: errf} +} + +func windowsOpenFileSequential(name string, flag int, _ os.FileMode) (file *os.File, err error) { + r, e := windowsOpenSequential(name, flag|windows.O_CLOEXEC, 0) + if e != nil { + return nil, e + } + return os.NewFile(uintptr(r), name), nil +} + +func makeInheritSa() *windows.SecurityAttributes { + var sa windows.SecurityAttributes + sa.Length = uint32(unsafe.Sizeof(sa)) + sa.InheritHandle = 1 + return &sa +} + +func windowsOpenSequential(path string, mode int, _ uint32) (fd windows.Handle, err error) { + if len(path) == 0 { + return windows.InvalidHandle, windows.ERROR_FILE_NOT_FOUND + } + pathp, err := windows.UTF16PtrFromString(path) + if err != nil { + return windows.InvalidHandle, err + } + var access uint32 + switch mode & (windows.O_RDONLY | windows.O_WRONLY | windows.O_RDWR) { + case windows.O_RDONLY: + access = windows.GENERIC_READ + case windows.O_WRONLY: + access = windows.GENERIC_WRITE + case windows.O_RDWR: + access = windows.GENERIC_READ | windows.GENERIC_WRITE + } + if mode&windows.O_CREAT != 0 { + access |= windows.GENERIC_WRITE + } + if mode&windows.O_APPEND != 0 { + access &^= windows.GENERIC_WRITE + access |= windows.FILE_APPEND_DATA + } + sharemode := uint32(windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE) + var sa *windows.SecurityAttributes + if mode&windows.O_CLOEXEC == 0 { + sa = makeInheritSa() + } + var createmode uint32 + switch { + case mode&(windows.O_CREAT|windows.O_EXCL) == (windows.O_CREAT | windows.O_EXCL): + createmode = windows.CREATE_NEW + case mode&(windows.O_CREAT|windows.O_TRUNC) == (windows.O_CREAT | windows.O_TRUNC): + createmode = windows.CREATE_ALWAYS + case mode&windows.O_CREAT == windows.O_CREAT: + createmode = windows.OPEN_ALWAYS + case mode&windows.O_TRUNC == windows.O_TRUNC: + createmode = windows.TRUNCATE_EXISTING + default: + createmode = windows.OPEN_EXISTING + } + // Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang. + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx + const fileFlagSequentialScan = 0x08000000 // FILE_FLAG_SEQUENTIAL_SCAN + h, e := windows.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0) + return h, e +} + +// Helpers for TempFileSequential +var rand uint32 +var randmu sync.Mutex + +func reseed() uint32 { + return uint32(time.Now().UnixNano() + int64(os.Getpid())) +} +func nextSuffix() string { + randmu.Lock() + r := rand + if r == 0 { + r = reseed() + } + r = r*1664525 + 1013904223 // constants from Numerical Recipes + rand = r + randmu.Unlock() + return strconv.Itoa(int(1e9 + r%1e9))[1:] +} + +// TempFileSequential is a copy of ioutil.TempFile, modified to use sequential +// file access. Below is the original comment from golang: +// TempFile creates a new temporary file in the directory dir +// with a name beginning with prefix, opens the file for reading +// and writing, and returns the resulting *os.File. +// If dir is the empty string, TempFile uses the default directory +// for temporary files (see os.TempDir). +// Multiple programs calling TempFile simultaneously +// will not choose the same file. The caller can use f.Name() +// to find the pathname of the file. It is the caller's responsibility +// to remove the file when no longer needed. +func TempFileSequential(dir, prefix string) (f *os.File, err error) { + if dir == "" { + dir = os.TempDir() + } + + nconflict := 0 + for i := 0; i < 10000; i++ { + name := filepath.Join(dir, prefix+nextSuffix()) + f, err = OpenFileSequential(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) + if os.IsExist(err) { + if nconflict++; nconflict > 10 { + randmu.Lock() + rand = reseed() + randmu.Unlock() + } + continue + } + break + } + return +} diff --git a/vendor/github.com/docker/docker/pkg/system/init.go b/vendor/github.com/docker/docker/pkg/system/init.go new file mode 100644 index 0000000000..a17597aaba --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/init.go @@ -0,0 +1,22 @@ +package system // import "github.com/docker/docker/pkg/system" + +import ( + "syscall" + "time" + "unsafe" +) + +// Used by chtimes +var maxTime time.Time + +func init() { + // chtimes initialization + if unsafe.Sizeof(syscall.Timespec{}.Nsec) == 8 { + // This is a 64 bit timespec + // os.Chtimes limits time to the following + maxTime = time.Unix(0, 1<<63-1) + } else { + // This is a 32 bit timespec + maxTime = time.Unix(1<<31-1, 0) + } +} diff --git a/vendor/github.com/docker/docker/pkg/system/init_windows.go b/vendor/github.com/docker/docker/pkg/system/init_windows.go new file mode 100644 index 0000000000..a91288c60b --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/init_windows.go @@ -0,0 +1,29 @@ +package system // import "github.com/docker/docker/pkg/system" + +import ( + "os" + + "github.com/sirupsen/logrus" +) + +var ( + // containerdRuntimeSupported determines if ContainerD should be the runtime. + // As of March 2019, this is an experimental feature. + containerdRuntimeSupported = false +) + +// InitContainerdRuntime sets whether to use ContainerD for runtime +// on Windows. This is an experimental feature still in development, and +// also requires an environment variable to be set (so as not to turn the +// feature on from simply experimental which would also mean LCOW. +func InitContainerdRuntime(experimental bool, cdPath string) { + if experimental && len(cdPath) > 0 && len(os.Getenv("DOCKER_WINDOWS_CONTAINERD_RUNTIME")) > 0 { + logrus.Warnf("Using ContainerD runtime. This feature is experimental") + containerdRuntimeSupported = true + } +} + +// ContainerdRuntimeSupported returns true if the use of ContainerD runtime is supported. +func ContainerdRuntimeSupported() bool { + return containerdRuntimeSupported +} diff --git a/vendor/github.com/docker/docker/pkg/system/lcow.go b/vendor/github.com/docker/docker/pkg/system/lcow.go new file mode 100644 index 0000000000..0f00028fbd --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/lcow.go @@ -0,0 +1,48 @@ +// +build windows,!no_lcow + +package system // import "github.com/docker/docker/pkg/system" + +import ( + "strings" + + "github.com/Microsoft/hcsshim/osversion" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +var ( + // lcowSupported determines if Linux Containers on Windows are supported. + lcowSupported = false +) + +// InitLCOW sets whether LCOW is supported or not. Requires RS5+ +func InitLCOW(experimental bool) { + if experimental && osversion.Build() >= osversion.RS5 { + lcowSupported = true + } +} + +func LCOWSupported() bool { + return lcowSupported +} + +// ValidatePlatform determines if a platform structure is valid. +// TODO This is a temporary windows-only function, should be replaced by +// comparison of worker capabilities +func ValidatePlatform(platform specs.Platform) error { + if !IsOSSupported(platform.OS) { + return errors.Errorf("unsupported os %s", platform.OS) + } + return nil +} + +// IsOSSupported determines if an operating system is supported by the host +func IsOSSupported(os string) bool { + if strings.EqualFold("windows", os) { + return true + } + if LCOWSupported() && strings.EqualFold(os, "linux") { + return true + } + return false +} diff --git a/vendor/github.com/docker/docker/pkg/system/lcow_unsupported.go b/vendor/github.com/docker/docker/pkg/system/lcow_unsupported.go new file mode 100644 index 0000000000..3d3cf775a7 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/lcow_unsupported.go @@ -0,0 +1,28 @@ +// +build !windows windows,no_lcow + +package system // import "github.com/docker/docker/pkg/system" +import ( + "runtime" + "strings" + + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +// InitLCOW does nothing since LCOW is a windows only feature +func InitLCOW(_ bool) {} + +// LCOWSupported returns true if Linux containers on Windows are supported. +func LCOWSupported() bool { + return false +} + +// ValidatePlatform determines if a platform structure is valid. This function +// is used for LCOW, and is a no-op on non-windows platforms. +func ValidatePlatform(_ specs.Platform) error { + return nil +} + +// IsOSSupported determines if an operating system is supported by the host. +func IsOSSupported(os string) bool { + return strings.EqualFold(runtime.GOOS, os) +} diff --git a/vendor/github.com/docker/docker/pkg/system/lstat_unix.go b/vendor/github.com/docker/docker/pkg/system/lstat_unix.go new file mode 100644 index 0000000000..de5a1c0fb2 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/lstat_unix.go @@ -0,0 +1,20 @@ +// +build !windows + +package system // import "github.com/docker/docker/pkg/system" + +import ( + "os" + "syscall" +) + +// Lstat takes a path to a file and returns +// a system.StatT type pertaining to that file. +// +// Throws an error if the file does not exist +func Lstat(path string) (*StatT, error) { + s := &syscall.Stat_t{} + if err := syscall.Lstat(path, s); err != nil { + return nil, &os.PathError{Op: "Lstat", Path: path, Err: err} + } + return fromStatT(s) +} diff --git a/vendor/github.com/docker/docker/pkg/system/lstat_windows.go b/vendor/github.com/docker/docker/pkg/system/lstat_windows.go new file mode 100644 index 0000000000..359c791d9b --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/lstat_windows.go @@ -0,0 +1,14 @@ +package system // import "github.com/docker/docker/pkg/system" + +import "os" + +// Lstat calls os.Lstat to get a fileinfo interface back. +// This is then copied into our own locally defined structure. +func Lstat(path string) (*StatT, error) { + fi, err := os.Lstat(path) + if err != nil { + return nil, err + } + + return fromStatT(&fi) +} diff --git a/vendor/github.com/docker/docker/pkg/system/meminfo.go b/vendor/github.com/docker/docker/pkg/system/meminfo.go new file mode 100644 index 0000000000..6667eb84dc --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/meminfo.go @@ -0,0 +1,17 @@ +package system // import "github.com/docker/docker/pkg/system" + +// MemInfo contains memory statistics of the host system. +type MemInfo struct { + // Total usable RAM (i.e. physical RAM minus a few reserved bits and the + // kernel binary code). + MemTotal int64 + + // Amount of free memory. + MemFree int64 + + // Total amount of swap space available. + SwapTotal int64 + + // Amount of swap space that is currently unused. + SwapFree int64 +} diff --git a/vendor/github.com/docker/docker/pkg/system/meminfo_linux.go b/vendor/github.com/docker/docker/pkg/system/meminfo_linux.go new file mode 100644 index 0000000000..cd060eff24 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/meminfo_linux.go @@ -0,0 +1,71 @@ +package system // import "github.com/docker/docker/pkg/system" + +import ( + "bufio" + "io" + "os" + "strconv" + "strings" + + units "github.com/docker/go-units" +) + +// ReadMemInfo retrieves memory statistics of the host system and returns a +// MemInfo type. +func ReadMemInfo() (*MemInfo, error) { + file, err := os.Open("/proc/meminfo") + if err != nil { + return nil, err + } + defer file.Close() + return parseMemInfo(file) +} + +// parseMemInfo parses the /proc/meminfo file into +// a MemInfo object given an io.Reader to the file. +// Throws error if there are problems reading from the file +func parseMemInfo(reader io.Reader) (*MemInfo, error) { + meminfo := &MemInfo{} + scanner := bufio.NewScanner(reader) + memAvailable := int64(-1) + for scanner.Scan() { + // Expected format: ["MemTotal:", "1234", "kB"] + parts := strings.Fields(scanner.Text()) + + // Sanity checks: Skip malformed entries. + if len(parts) < 3 || parts[2] != "kB" { + continue + } + + // Convert to bytes. + size, err := strconv.Atoi(parts[1]) + if err != nil { + continue + } + bytes := int64(size) * units.KiB + + switch parts[0] { + case "MemTotal:": + meminfo.MemTotal = bytes + case "MemFree:": + meminfo.MemFree = bytes + case "MemAvailable:": + memAvailable = bytes + case "SwapTotal:": + meminfo.SwapTotal = bytes + case "SwapFree:": + meminfo.SwapFree = bytes + } + + } + if memAvailable != -1 { + meminfo.MemFree = memAvailable + } + + // Handle errors that may have occurred during the reading of the file. + if err := scanner.Err(); err != nil { + return nil, err + } + + return meminfo, nil +} diff --git a/vendor/github.com/docker/docker/pkg/system/meminfo_unsupported.go b/vendor/github.com/docker/docker/pkg/system/meminfo_unsupported.go new file mode 100644 index 0000000000..56f4494268 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/meminfo_unsupported.go @@ -0,0 +1,8 @@ +// +build !linux,!windows + +package system // import "github.com/docker/docker/pkg/system" + +// ReadMemInfo is not supported on platforms other than linux and windows. +func ReadMemInfo() (*MemInfo, error) { + return nil, ErrNotSupportedPlatform +} diff --git a/vendor/github.com/docker/docker/pkg/system/meminfo_windows.go b/vendor/github.com/docker/docker/pkg/system/meminfo_windows.go new file mode 100644 index 0000000000..6ed93f2fe2 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/meminfo_windows.go @@ -0,0 +1,45 @@ +package system // import "github.com/docker/docker/pkg/system" + +import ( + "unsafe" + + "golang.org/x/sys/windows" +) + +var ( + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + + procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx") +) + +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366589(v=vs.85).aspx +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366770(v=vs.85).aspx +type memorystatusex struct { + dwLength uint32 + dwMemoryLoad uint32 + ullTotalPhys uint64 + ullAvailPhys uint64 + ullTotalPageFile uint64 + ullAvailPageFile uint64 + ullTotalVirtual uint64 + ullAvailVirtual uint64 + ullAvailExtendedVirtual uint64 +} + +// ReadMemInfo retrieves memory statistics of the host system and returns a +// MemInfo type. +func ReadMemInfo() (*MemInfo, error) { + msi := &memorystatusex{ + dwLength: 64, + } + r1, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(msi))) + if r1 == 0 { + return &MemInfo{}, nil + } + return &MemInfo{ + MemTotal: int64(msi.ullTotalPhys), + MemFree: int64(msi.ullAvailPhys), + SwapTotal: int64(msi.ullTotalPageFile), + SwapFree: int64(msi.ullAvailPageFile), + }, nil +} diff --git a/vendor/github.com/docker/docker/pkg/system/mknod.go b/vendor/github.com/docker/docker/pkg/system/mknod.go new file mode 100644 index 0000000000..b132482e03 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/mknod.go @@ -0,0 +1,22 @@ +// +build !windows + +package system // import "github.com/docker/docker/pkg/system" + +import ( + "golang.org/x/sys/unix" +) + +// Mknod creates a filesystem node (file, device special file or named pipe) named path +// with attributes specified by mode and dev. +func Mknod(path string, mode uint32, dev int) error { + return unix.Mknod(path, mode, dev) +} + +// Mkdev is used to build the value of linux devices (in /dev/) which specifies major +// and minor number of the newly created device special file. +// Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes. +// They are, from low to high: the lower 8 bits of the minor, then 12 bits of the major, +// then the top 12 bits of the minor. +func Mkdev(major int64, minor int64) uint32 { + return uint32(unix.Mkdev(uint32(major), uint32(minor))) +} diff --git a/vendor/github.com/docker/docker/pkg/system/mknod_windows.go b/vendor/github.com/docker/docker/pkg/system/mknod_windows.go new file mode 100644 index 0000000000..ec89d7a15e --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/mknod_windows.go @@ -0,0 +1,11 @@ +package system // import "github.com/docker/docker/pkg/system" + +// Mknod is not implemented on Windows. +func Mknod(path string, mode uint32, dev int) error { + return ErrNotSupportedPlatform +} + +// Mkdev is not implemented on Windows. +func Mkdev(major int64, minor int64) uint32 { + panic("Mkdev not implemented on Windows.") +} diff --git a/vendor/github.com/docker/docker/pkg/system/path.go b/vendor/github.com/docker/docker/pkg/system/path.go new file mode 100644 index 0000000000..64e892289a --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/path.go @@ -0,0 +1,64 @@ +package system // import "github.com/docker/docker/pkg/system" + +import ( + "fmt" + "path/filepath" + "runtime" + "strings" +) + +const defaultUnixPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + +// DefaultPathEnv is unix style list of directories to search for +// executables. Each directory is separated from the next by a colon +// ':' character . +func DefaultPathEnv(os string) string { + if runtime.GOOS == "windows" { + if os != runtime.GOOS { + return defaultUnixPathEnv + } + // Deliberately empty on Windows containers on Windows as the default path will be set by + // the container. Docker has no context of what the default path should be. + return "" + } + return defaultUnixPathEnv + +} + +// PathVerifier defines the subset of a PathDriver that CheckSystemDriveAndRemoveDriveLetter +// actually uses in order to avoid system depending on containerd/continuity. +type PathVerifier interface { + IsAbs(string) bool +} + +// CheckSystemDriveAndRemoveDriveLetter verifies that a path, if it includes a drive letter, +// is the system drive. +// On Linux: this is a no-op. +// On Windows: this does the following> +// CheckSystemDriveAndRemoveDriveLetter verifies and manipulates a Windows path. +// This is used, for example, when validating a user provided path in docker cp. +// If a drive letter is supplied, it must be the system drive. The drive letter +// is always removed. Also, it translates it to OS semantics (IOW / to \). We +// need the path in this syntax so that it can ultimately be concatenated with +// a Windows long-path which doesn't support drive-letters. Examples: +// C: --> Fail +// C:\ --> \ +// a --> a +// /a --> \a +// d:\ --> Fail +func CheckSystemDriveAndRemoveDriveLetter(path string, driver PathVerifier) (string, error) { + if runtime.GOOS != "windows" || LCOWSupported() { + return path, nil + } + + if len(path) == 2 && string(path[1]) == ":" { + return "", fmt.Errorf("No relative path specified in %q", path) + } + if !driver.IsAbs(path) || len(path) < 2 { + return filepath.FromSlash(path), nil + } + if string(path[1]) == ":" && !strings.EqualFold(string(path[0]), "c") { + return "", fmt.Errorf("The specified path is not on the system drive (C:)") + } + return filepath.FromSlash(path[2:]), nil +} diff --git a/vendor/github.com/docker/docker/pkg/system/path_unix.go b/vendor/github.com/docker/docker/pkg/system/path_unix.go new file mode 100644 index 0000000000..b0b93196a1 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/path_unix.go @@ -0,0 +1,10 @@ +// +build !windows + +package system // import "github.com/docker/docker/pkg/system" + +// GetLongPathName converts Windows short pathnames to full pathnames. +// For example C:\Users\ADMIN~1 --> C:\Users\Administrator. +// It is a no-op on non-Windows platforms +func GetLongPathName(path string) (string, error) { + return path, nil +} diff --git a/vendor/github.com/docker/docker/pkg/system/path_windows.go b/vendor/github.com/docker/docker/pkg/system/path_windows.go new file mode 100644 index 0000000000..22a56136c8 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/path_windows.go @@ -0,0 +1,27 @@ +package system // import "github.com/docker/docker/pkg/system" + +import "golang.org/x/sys/windows" + +// GetLongPathName converts Windows short pathnames to full pathnames. +// For example C:\Users\ADMIN~1 --> C:\Users\Administrator. +// It is a no-op on non-Windows platforms +func GetLongPathName(path string) (string, error) { + // See https://groups.google.com/forum/#!topic/golang-dev/1tufzkruoTg + p, err := windows.UTF16FromString(path) + if err != nil { + return "", err + } + b := p // GetLongPathName says we can reuse buffer + n, err := windows.GetLongPathName(&p[0], &b[0], uint32(len(b))) + if err != nil { + return "", err + } + if n > uint32(len(b)) { + b = make([]uint16, n) + _, err = windows.GetLongPathName(&p[0], &b[0], uint32(len(b))) + if err != nil { + return "", err + } + } + return windows.UTF16ToString(b), nil +} diff --git a/vendor/github.com/docker/docker/pkg/system/process_unix.go b/vendor/github.com/docker/docker/pkg/system/process_unix.go new file mode 100644 index 0000000000..79aebb5272 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/process_unix.go @@ -0,0 +1,44 @@ +// +build linux freebsd darwin + +package system // import "github.com/docker/docker/pkg/system" + +import ( + "fmt" + "io/ioutil" + "strings" + "syscall" + + "golang.org/x/sys/unix" +) + +// IsProcessAlive returns true if process with a given pid is running. +func IsProcessAlive(pid int) bool { + err := unix.Kill(pid, syscall.Signal(0)) + if err == nil || err == unix.EPERM { + return true + } + + return false +} + +// KillProcess force-stops a process. +func KillProcess(pid int) { + unix.Kill(pid, unix.SIGKILL) +} + +// IsProcessZombie return true if process has a state with "Z" +// http://man7.org/linux/man-pages/man5/proc.5.html +func IsProcessZombie(pid int) (bool, error) { + statPath := fmt.Sprintf("/proc/%d/stat", pid) + dataBytes, err := ioutil.ReadFile(statPath) + if err != nil { + return false, err + } + data := string(dataBytes) + sdata := strings.SplitN(data, " ", 4) + if len(sdata) >= 3 && sdata[2] == "Z" { + return true, nil + } + + return false, nil +} diff --git a/vendor/github.com/docker/docker/pkg/system/process_windows.go b/vendor/github.com/docker/docker/pkg/system/process_windows.go new file mode 100644 index 0000000000..09bdfa0ca0 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/process_windows.go @@ -0,0 +1,18 @@ +package system // import "github.com/docker/docker/pkg/system" + +import "os" + +// IsProcessAlive returns true if process with a given pid is running. +func IsProcessAlive(pid int) bool { + _, err := os.FindProcess(pid) + + return err == nil +} + +// KillProcess force-stops a process. +func KillProcess(pid int) { + p, err := os.FindProcess(pid) + if err == nil { + _ = p.Kill() + } +} diff --git a/vendor/github.com/docker/docker/pkg/system/rm.go b/vendor/github.com/docker/docker/pkg/system/rm.go new file mode 100644 index 0000000000..c5d80ebda1 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/rm.go @@ -0,0 +1,78 @@ +// +build !darwin,!windows + +package system // import "github.com/docker/docker/pkg/system" + +import ( + "os" + "syscall" + "time" + + "github.com/moby/sys/mount" + "github.com/pkg/errors" +) + +// EnsureRemoveAll wraps `os.RemoveAll` to check for specific errors that can +// often be remedied. +// Only use `EnsureRemoveAll` if you really want to make every effort to remove +// a directory. +// +// Because of the way `os.Remove` (and by extension `os.RemoveAll`) works, there +// can be a race between reading directory entries and then actually attempting +// to remove everything in the directory. +// These types of errors do not need to be returned since it's ok for the dir to +// be gone we can just retry the remove operation. +// +// This should not return a `os.ErrNotExist` kind of error under any circumstances +func EnsureRemoveAll(dir string) error { + notExistErr := make(map[string]bool) + + // track retries + exitOnErr := make(map[string]int) + maxRetry := 50 + + // Attempt to unmount anything beneath this dir first + mount.RecursiveUnmount(dir) + + for { + err := os.RemoveAll(dir) + if err == nil { + return nil + } + + pe, ok := err.(*os.PathError) + if !ok { + return err + } + + if os.IsNotExist(err) { + if notExistErr[pe.Path] { + return err + } + notExistErr[pe.Path] = true + + // There is a race where some subdir can be removed but after the parent + // dir entries have been read. + // So the path could be from `os.Remove(subdir)` + // If the reported non-existent path is not the passed in `dir` we + // should just retry, but otherwise return with no error. + if pe.Path == dir { + return nil + } + continue + } + + if pe.Err != syscall.EBUSY { + return err + } + + if e := mount.Unmount(pe.Path); e != nil { + return errors.Wrapf(e, "error while removing %s", dir) + } + + if exitOnErr[pe.Path] == maxRetry { + return err + } + exitOnErr[pe.Path]++ + time.Sleep(100 * time.Millisecond) + } +} diff --git a/vendor/github.com/docker/docker/pkg/system/rm_windows.go b/vendor/github.com/docker/docker/pkg/system/rm_windows.go new file mode 100644 index 0000000000..ed9c5dcb8a --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/rm_windows.go @@ -0,0 +1,6 @@ +package system + +import "os" + +// EnsureRemoveAll is an alias to os.RemoveAll on Windows +var EnsureRemoveAll = os.RemoveAll diff --git a/vendor/github.com/docker/docker/pkg/system/stat_bsd.go b/vendor/github.com/docker/docker/pkg/system/stat_bsd.go new file mode 100644 index 0000000000..ea55c3dbb5 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/stat_bsd.go @@ -0,0 +1,15 @@ +// +build freebsd netbsd + +package system // import "github.com/docker/docker/pkg/system" + +import "syscall" + +// fromStatT converts a syscall.Stat_t type to a system.Stat_t type +func fromStatT(s *syscall.Stat_t) (*StatT, error) { + return &StatT{size: s.Size, + mode: uint32(s.Mode), + uid: s.Uid, + gid: s.Gid, + rdev: uint64(s.Rdev), + mtim: s.Mtimespec}, nil +} diff --git a/vendor/github.com/docker/docker/pkg/system/stat_darwin.go b/vendor/github.com/docker/docker/pkg/system/stat_darwin.go new file mode 100644 index 0000000000..c1c0ee9f38 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/stat_darwin.go @@ -0,0 +1,13 @@ +package system // import "github.com/docker/docker/pkg/system" + +import "syscall" + +// fromStatT converts a syscall.Stat_t type to a system.Stat_t type +func fromStatT(s *syscall.Stat_t) (*StatT, error) { + return &StatT{size: s.Size, + mode: uint32(s.Mode), + uid: s.Uid, + gid: s.Gid, + rdev: uint64(s.Rdev), + mtim: s.Mtimespec}, nil +} diff --git a/vendor/github.com/docker/docker/pkg/system/stat_linux.go b/vendor/github.com/docker/docker/pkg/system/stat_linux.go new file mode 100644 index 0000000000..17d5d131a3 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/stat_linux.go @@ -0,0 +1,20 @@ +package system // import "github.com/docker/docker/pkg/system" + +import "syscall" + +// fromStatT converts a syscall.Stat_t type to a system.Stat_t type +func fromStatT(s *syscall.Stat_t) (*StatT, error) { + return &StatT{size: s.Size, + mode: s.Mode, + uid: s.Uid, + gid: s.Gid, + // the type is 32bit on mips + rdev: uint64(s.Rdev), // nolint: unconvert + mtim: s.Mtim}, nil +} + +// FromStatT converts a syscall.Stat_t type to a system.Stat_t type +// This is exposed on Linux as pkg/archive/changes uses it. +func FromStatT(s *syscall.Stat_t) (*StatT, error) { + return fromStatT(s) +} diff --git a/vendor/github.com/docker/docker/pkg/system/stat_openbsd.go b/vendor/github.com/docker/docker/pkg/system/stat_openbsd.go new file mode 100644 index 0000000000..756b92d1e6 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/stat_openbsd.go @@ -0,0 +1,13 @@ +package system // import "github.com/docker/docker/pkg/system" + +import "syscall" + +// fromStatT converts a syscall.Stat_t type to a system.Stat_t type +func fromStatT(s *syscall.Stat_t) (*StatT, error) { + return &StatT{size: s.Size, + mode: uint32(s.Mode), + uid: s.Uid, + gid: s.Gid, + rdev: uint64(s.Rdev), + mtim: s.Mtim}, nil +} diff --git a/vendor/github.com/docker/docker/pkg/system/stat_solaris.go b/vendor/github.com/docker/docker/pkg/system/stat_solaris.go new file mode 100644 index 0000000000..6a51ccd642 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/stat_solaris.go @@ -0,0 +1,13 @@ +package system // import "github.com/docker/docker/pkg/system" + +import "syscall" + +// fromStatT converts a syscall.Stat_t type to a system.Stat_t type +func fromStatT(s *syscall.Stat_t) (*StatT, error) { + return &StatT{size: s.Size, + mode: s.Mode, + uid: s.Uid, + gid: s.Gid, + rdev: s.Rdev, + mtim: s.Mtim}, nil +} diff --git a/vendor/github.com/docker/docker/pkg/system/stat_unix.go b/vendor/github.com/docker/docker/pkg/system/stat_unix.go new file mode 100644 index 0000000000..86bb6dd55e --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/stat_unix.go @@ -0,0 +1,66 @@ +// +build !windows + +package system // import "github.com/docker/docker/pkg/system" + +import ( + "os" + "syscall" +) + +// StatT type contains status of a file. It contains metadata +// like permission, owner, group, size, etc about a file. +type StatT struct { + mode uint32 + uid uint32 + gid uint32 + rdev uint64 + size int64 + mtim syscall.Timespec +} + +// Mode returns file's permission mode. +func (s StatT) Mode() uint32 { + return s.mode +} + +// UID returns file's user id of owner. +func (s StatT) UID() uint32 { + return s.uid +} + +// GID returns file's group id of owner. +func (s StatT) GID() uint32 { + return s.gid +} + +// Rdev returns file's device ID (if it's special file). +func (s StatT) Rdev() uint64 { + return s.rdev +} + +// Size returns file's size. +func (s StatT) Size() int64 { + return s.size +} + +// Mtim returns file's last modification time. +func (s StatT) Mtim() syscall.Timespec { + return s.mtim +} + +// IsDir reports whether s describes a directory. +func (s StatT) IsDir() bool { + return s.mode&syscall.S_IFDIR != 0 +} + +// Stat takes a path to a file and returns +// a system.StatT type pertaining to that file. +// +// Throws an error if the file does not exist +func Stat(path string) (*StatT, error) { + s := &syscall.Stat_t{} + if err := syscall.Stat(path, s); err != nil { + return nil, &os.PathError{Op: "Stat", Path: path, Err: err} + } + return fromStatT(s) +} diff --git a/vendor/github.com/docker/docker/pkg/system/stat_windows.go b/vendor/github.com/docker/docker/pkg/system/stat_windows.go new file mode 100644 index 0000000000..b2456cb887 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/stat_windows.go @@ -0,0 +1,49 @@ +package system // import "github.com/docker/docker/pkg/system" + +import ( + "os" + "time" +) + +// StatT type contains status of a file. It contains metadata +// like permission, size, etc about a file. +type StatT struct { + mode os.FileMode + size int64 + mtim time.Time +} + +// Size returns file's size. +func (s StatT) Size() int64 { + return s.size +} + +// Mode returns file's permission mode. +func (s StatT) Mode() os.FileMode { + return os.FileMode(s.mode) +} + +// Mtim returns file's last modification time. +func (s StatT) Mtim() time.Time { + return time.Time(s.mtim) +} + +// Stat takes a path to a file and returns +// a system.StatT type pertaining to that file. +// +// Throws an error if the file does not exist +func Stat(path string) (*StatT, error) { + fi, err := os.Stat(path) + if err != nil { + return nil, err + } + return fromStatT(&fi) +} + +// fromStatT converts a os.FileInfo type to a system.StatT type +func fromStatT(fi *os.FileInfo) (*StatT, error) { + return &StatT{ + size: (*fi).Size(), + mode: (*fi).Mode(), + mtim: (*fi).ModTime()}, nil +} diff --git a/vendor/github.com/docker/docker/pkg/system/syscall_unix.go b/vendor/github.com/docker/docker/pkg/system/syscall_unix.go new file mode 100644 index 0000000000..905d10f153 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/syscall_unix.go @@ -0,0 +1,11 @@ +// +build linux freebsd + +package system // import "github.com/docker/docker/pkg/system" + +import "golang.org/x/sys/unix" + +// Unmount is a platform-specific helper function to call +// the unmount syscall. +func Unmount(dest string) error { + return unix.Unmount(dest, 0) +} diff --git a/vendor/github.com/docker/docker/pkg/system/syscall_windows.go b/vendor/github.com/docker/docker/pkg/system/syscall_windows.go new file mode 100644 index 0000000000..1588aa3ef9 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/syscall_windows.go @@ -0,0 +1,136 @@ +package system // import "github.com/docker/docker/pkg/system" + +import ( + "syscall" + "unsafe" + + "github.com/Microsoft/hcsshim/osversion" + "github.com/sirupsen/logrus" + "golang.org/x/sys/windows" +) + +const ( + OWNER_SECURITY_INFORMATION = windows.OWNER_SECURITY_INFORMATION // Deprecated: use golang.org/x/sys/windows.OWNER_SECURITY_INFORMATION + GROUP_SECURITY_INFORMATION = windows.GROUP_SECURITY_INFORMATION // Deprecated: use golang.org/x/sys/windows.GROUP_SECURITY_INFORMATION + DACL_SECURITY_INFORMATION = windows.DACL_SECURITY_INFORMATION // Deprecated: use golang.org/x/sys/windows.DACL_SECURITY_INFORMATION + SACL_SECURITY_INFORMATION = windows.SACL_SECURITY_INFORMATION // Deprecated: use golang.org/x/sys/windows.SACL_SECURITY_INFORMATION + LABEL_SECURITY_INFORMATION = windows.LABEL_SECURITY_INFORMATION // Deprecated: use golang.org/x/sys/windows.LABEL_SECURITY_INFORMATION + ATTRIBUTE_SECURITY_INFORMATION = windows.ATTRIBUTE_SECURITY_INFORMATION // Deprecated: use golang.org/x/sys/windows.ATTRIBUTE_SECURITY_INFORMATION + SCOPE_SECURITY_INFORMATION = windows.SCOPE_SECURITY_INFORMATION // Deprecated: use golang.org/x/sys/windows.SCOPE_SECURITY_INFORMATION + PROCESS_TRUST_LABEL_SECURITY_INFORMATION = 0x00000080 + ACCESS_FILTER_SECURITY_INFORMATION = 0x00000100 + BACKUP_SECURITY_INFORMATION = windows.BACKUP_SECURITY_INFORMATION // Deprecated: use golang.org/x/sys/windows.BACKUP_SECURITY_INFORMATION + PROTECTED_DACL_SECURITY_INFORMATION = windows.PROTECTED_DACL_SECURITY_INFORMATION // Deprecated: use golang.org/x/sys/windows.PROTECTED_DACL_SECURITY_INFORMATION + PROTECTED_SACL_SECURITY_INFORMATION = windows.PROTECTED_SACL_SECURITY_INFORMATION // Deprecated: use golang.org/x/sys/windows.PROTECTED_SACL_SECURITY_INFORMATION + UNPROTECTED_DACL_SECURITY_INFORMATION = windows.UNPROTECTED_DACL_SECURITY_INFORMATION // Deprecated: use golang.org/x/sys/windows.UNPROTECTED_DACL_SECURITY_INFORMATION + UNPROTECTED_SACL_SECURITY_INFORMATION = windows.UNPROTECTED_SACL_SECURITY_INFORMATION // Deprecated: use golang.org/x/sys/windows.UNPROTECTED_SACL_SECURITY_INFORMATION +) + +const ( + SE_UNKNOWN_OBJECT_TYPE = windows.SE_UNKNOWN_OBJECT_TYPE // Deprecated: use golang.org/x/sys/windows.SE_UNKNOWN_OBJECT_TYPE + SE_FILE_OBJECT = windows.SE_FILE_OBJECT // Deprecated: use golang.org/x/sys/windows.SE_FILE_OBJECT + SE_SERVICE = windows.SE_SERVICE // Deprecated: use golang.org/x/sys/windows.SE_SERVICE + SE_PRINTER = windows.SE_PRINTER // Deprecated: use golang.org/x/sys/windows.SE_PRINTER + SE_REGISTRY_KEY = windows.SE_REGISTRY_KEY // Deprecated: use golang.org/x/sys/windows.SE_REGISTRY_KEY + SE_LMSHARE = windows.SE_LMSHARE // Deprecated: use golang.org/x/sys/windows.SE_LMSHARE + SE_KERNEL_OBJECT = windows.SE_KERNEL_OBJECT // Deprecated: use golang.org/x/sys/windows.SE_KERNEL_OBJECT + SE_WINDOW_OBJECT = windows.SE_WINDOW_OBJECT // Deprecated: use golang.org/x/sys/windows.SE_WINDOW_OBJECT + SE_DS_OBJECT = windows.SE_DS_OBJECT // Deprecated: use golang.org/x/sys/windows.SE_DS_OBJECT + SE_DS_OBJECT_ALL = windows.SE_DS_OBJECT_ALL // Deprecated: use golang.org/x/sys/windows.SE_DS_OBJECT_ALL + SE_PROVIDER_DEFINED_OBJECT = windows.SE_PROVIDER_DEFINED_OBJECT // Deprecated: use golang.org/x/sys/windows.SE_PROVIDER_DEFINED_OBJECT + SE_WMIGUID_OBJECT = windows.SE_WMIGUID_OBJECT // Deprecated: use golang.org/x/sys/windows.SE_WMIGUID_OBJECT + SE_REGISTRY_WOW64_32KEY = windows.SE_REGISTRY_WOW64_32KEY // Deprecated: use golang.org/x/sys/windows.SE_REGISTRY_WOW64_32KEY +) + +const ( + SeTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege" +) + +const ( + ContainerAdministratorSidString = "S-1-5-93-2-1" + ContainerUserSidString = "S-1-5-93-2-2" +) + +var ( + ntuserApiset = windows.NewLazyDLL("ext-ms-win-ntuser-window-l1-1-0") + modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") + procGetVersionExW = modkernel32.NewProc("GetVersionExW") + procSetNamedSecurityInfo = modadvapi32.NewProc("SetNamedSecurityInfoW") + procGetSecurityDescriptorDacl = modadvapi32.NewProc("GetSecurityDescriptorDacl") +) + +// OSVersion is a wrapper for Windows version information +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx +type OSVersion = osversion.OSVersion + +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724833(v=vs.85).aspx +// TODO: use golang.org/x/sys/windows.OsVersionInfoEx (needs OSVersionInfoSize to be exported) +type osVersionInfoEx struct { + OSVersionInfoSize uint32 + MajorVersion uint32 + MinorVersion uint32 + BuildNumber uint32 + PlatformID uint32 + CSDVersion [128]uint16 + ServicePackMajor uint16 + ServicePackMinor uint16 + SuiteMask uint16 + ProductType byte + Reserve byte +} + +// GetOSVersion gets the operating system version on Windows. Note that +// dockerd.exe must be manifested to get the correct version information. +// Deprecated: use github.com/Microsoft/hcsshim/osversion.Get() instead +func GetOSVersion() OSVersion { + return osversion.Get() +} + +// IsWindowsClient returns true if the SKU is client +func IsWindowsClient() bool { + osviex := &osVersionInfoEx{OSVersionInfoSize: 284} + r1, _, err := procGetVersionExW.Call(uintptr(unsafe.Pointer(osviex))) + if r1 == 0 { + logrus.Warnf("GetVersionExW failed - assuming server SKU: %v", err) + return false + } + const verNTWorkstation = 0x00000001 + return osviex.ProductType == verNTWorkstation +} + +// Unmount is a platform-specific helper function to call +// the unmount syscall. Not supported on Windows +func Unmount(_ string) error { + return nil +} + +// HasWin32KSupport determines whether containers that depend on win32k can +// run on this machine. Win32k is the driver used to implement windowing. +func HasWin32KSupport() bool { + // For now, check for ntuser API support on the host. In the future, a host + // may support win32k in containers even if the host does not support ntuser + // APIs. + return ntuserApiset.Load() == nil +} + +// Deprecated: use golang.org/x/sys/windows.SetNamedSecurityInfo() +func SetNamedSecurityInfo(objectName *uint16, objectType uint32, securityInformation uint32, sidOwner *windows.SID, sidGroup *windows.SID, dacl *byte, sacl *byte) (result error) { + r0, _, _ := syscall.Syscall9(procSetNamedSecurityInfo.Addr(), 7, uintptr(unsafe.Pointer(objectName)), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(sidOwner)), uintptr(unsafe.Pointer(sidGroup)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), 0, 0) + if r0 != 0 { + result = syscall.Errno(r0) + } + return +} + +// Deprecated: uses golang.org/x/sys/windows.SecurityDescriptorFromString() and golang.org/x/sys/windows.SECURITY_DESCRIPTOR.DACL() +func GetSecurityDescriptorDacl(securityDescriptor *byte, daclPresent *uint32, dacl **byte, daclDefaulted *uint32) (result error) { + r1, _, e1 := syscall.Syscall6(procGetSecurityDescriptorDacl.Addr(), 4, uintptr(unsafe.Pointer(securityDescriptor)), uintptr(unsafe.Pointer(daclPresent)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(daclDefaulted)), 0, 0) + if r1 == 0 { + if e1 != 0 { + result = e1 + } else { + result = syscall.EINVAL + } + } + return +} diff --git a/vendor/github.com/docker/docker/pkg/system/umask.go b/vendor/github.com/docker/docker/pkg/system/umask.go new file mode 100644 index 0000000000..9912a2babb --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/umask.go @@ -0,0 +1,13 @@ +// +build !windows + +package system // import "github.com/docker/docker/pkg/system" + +import ( + "golang.org/x/sys/unix" +) + +// Umask sets current process's file mode creation mask to newmask +// and returns oldmask. +func Umask(newmask int) (oldmask int, err error) { + return unix.Umask(newmask), nil +} diff --git a/vendor/github.com/docker/docker/pkg/system/umask_windows.go b/vendor/github.com/docker/docker/pkg/system/umask_windows.go new file mode 100644 index 0000000000..fc62388c38 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/umask_windows.go @@ -0,0 +1,7 @@ +package system // import "github.com/docker/docker/pkg/system" + +// Umask is not supported on the windows platform. +func Umask(newmask int) (oldmask int, err error) { + // should not be called on cli code path + return 0, ErrNotSupportedPlatform +} diff --git a/vendor/github.com/docker/docker/pkg/system/utimes_unix.go b/vendor/github.com/docker/docker/pkg/system/utimes_unix.go new file mode 100644 index 0000000000..61ba8c474c --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/utimes_unix.go @@ -0,0 +1,24 @@ +// +build linux freebsd + +package system // import "github.com/docker/docker/pkg/system" + +import ( + "syscall" + + "golang.org/x/sys/unix" +) + +// LUtimesNano is used to change access and modification time of the specified path. +// It's used for symbol link file because unix.UtimesNano doesn't support a NOFOLLOW flag atm. +func LUtimesNano(path string, ts []syscall.Timespec) error { + uts := []unix.Timespec{ + unix.NsecToTimespec(syscall.TimespecToNsec(ts[0])), + unix.NsecToTimespec(syscall.TimespecToNsec(ts[1])), + } + err := unix.UtimesNanoAt(unix.AT_FDCWD, path, uts, unix.AT_SYMLINK_NOFOLLOW) + if err != nil && err != unix.ENOSYS { + return err + } + + return nil +} diff --git a/vendor/github.com/docker/docker/pkg/system/utimes_unsupported.go b/vendor/github.com/docker/docker/pkg/system/utimes_unsupported.go new file mode 100644 index 0000000000..095e072e1d --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/utimes_unsupported.go @@ -0,0 +1,10 @@ +// +build !linux,!freebsd + +package system // import "github.com/docker/docker/pkg/system" + +import "syscall" + +// LUtimesNano is only supported on linux and freebsd. +func LUtimesNano(path string, ts []syscall.Timespec) error { + return ErrNotSupportedPlatform +} diff --git a/vendor/github.com/docker/docker/pkg/system/xattrs_linux.go b/vendor/github.com/docker/docker/pkg/system/xattrs_linux.go new file mode 100644 index 0000000000..95b609fe7a --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/xattrs_linux.go @@ -0,0 +1,37 @@ +package system // import "github.com/docker/docker/pkg/system" + +import "golang.org/x/sys/unix" + +// Lgetxattr retrieves the value of the extended attribute identified by attr +// and associated with the given path in the file system. +// It will returns a nil slice and nil error if the xattr is not set. +func Lgetxattr(path string, attr string) ([]byte, error) { + // Start with a 128 length byte array + dest := make([]byte, 128) + sz, errno := unix.Lgetxattr(path, attr, dest) + + for errno == unix.ERANGE { + // Buffer too small, use zero-sized buffer to get the actual size + sz, errno = unix.Lgetxattr(path, attr, []byte{}) + if errno != nil { + return nil, errno + } + dest = make([]byte, sz) + sz, errno = unix.Lgetxattr(path, attr, dest) + } + + switch { + case errno == unix.ENODATA: + return nil, nil + case errno != nil: + return nil, errno + } + + return dest[:sz], nil +} + +// Lsetxattr sets the value of the extended attribute identified by attr +// and associated with the given path in the file system. +func Lsetxattr(path string, attr string, data []byte, flags int) error { + return unix.Lsetxattr(path, attr, data, flags) +} diff --git a/vendor/github.com/docker/docker/pkg/system/xattrs_unsupported.go b/vendor/github.com/docker/docker/pkg/system/xattrs_unsupported.go new file mode 100644 index 0000000000..d780a90cd3 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/system/xattrs_unsupported.go @@ -0,0 +1,13 @@ +// +build !linux + +package system // import "github.com/docker/docker/pkg/system" + +// Lgetxattr is not supported on platforms other than linux. +func Lgetxattr(path string, attr string) ([]byte, error) { + return nil, ErrNotSupportedPlatform +} + +// Lsetxattr is not supported on platforms other than linux. +func Lsetxattr(path string, attr string, data []byte, flags int) error { + return ErrNotSupportedPlatform +} diff --git a/vendor/github.com/docker/docker/volume/mounts/lcow_parser.go b/vendor/github.com/docker/docker/volume/mounts/lcow_parser.go new file mode 100644 index 0000000000..bafb7b07f8 --- /dev/null +++ b/vendor/github.com/docker/docker/volume/mounts/lcow_parser.go @@ -0,0 +1,34 @@ +package mounts // import "github.com/docker/docker/volume/mounts" + +import ( + "errors" + "path" + + "github.com/docker/docker/api/types/mount" +) + +var lcowSpecificValidators mountValidator = func(m *mount.Mount) error { + if path.Clean(m.Target) == "/" { + return ErrVolumeTargetIsRoot + } + if m.Type == mount.TypeNamedPipe { + return errors.New("Linux containers on Windows do not support named pipe mounts") + } + return nil +} + +type lcowParser struct { + windowsParser +} + +func (p *lcowParser) ValidateMountConfig(mnt *mount.Mount) error { + return p.validateMountConfigReg(mnt, rxLCOWDestination, lcowSpecificValidators) +} + +func (p *lcowParser) ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) { + return p.parseMountRaw(raw, volumeDriver, rxLCOWDestination, false, lcowSpecificValidators) +} + +func (p *lcowParser) ParseMountSpec(cfg mount.Mount) (*MountPoint, error) { + return p.parseMountSpec(cfg, rxLCOWDestination, false, lcowSpecificValidators) +} diff --git a/vendor/github.com/docker/docker/volume/mounts/linux_parser.go b/vendor/github.com/docker/docker/volume/mounts/linux_parser.go new file mode 100644 index 0000000000..b5ed4af6a3 --- /dev/null +++ b/vendor/github.com/docker/docker/volume/mounts/linux_parser.go @@ -0,0 +1,423 @@ +package mounts // import "github.com/docker/docker/volume/mounts" + +import ( + "errors" + "fmt" + "path" + "path/filepath" + "strings" + + "github.com/docker/docker/api/types/mount" + "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/volume" +) + +type linuxParser struct { +} + +func linuxSplitRawSpec(raw string) ([]string, error) { + if strings.Count(raw, ":") > 2 { + return nil, errInvalidSpec(raw) + } + + arr := strings.SplitN(raw, ":", 3) + if arr[0] == "" { + return nil, errInvalidSpec(raw) + } + return arr, nil +} + +func linuxValidateNotRoot(p string) error { + p = path.Clean(strings.Replace(p, `\`, `/`, -1)) + if p == "/" { + return ErrVolumeTargetIsRoot + } + return nil +} +func linuxValidateAbsolute(p string) error { + p = strings.Replace(p, `\`, `/`, -1) + if path.IsAbs(p) { + return nil + } + return fmt.Errorf("invalid mount path: '%s' mount path must be absolute", p) +} +func (p *linuxParser) ValidateMountConfig(mnt *mount.Mount) error { + // there was something looking like a bug in existing codebase: + // - validateMountConfig on linux was called with options skipping bind source existence when calling ParseMountRaw + // - but not when calling ParseMountSpec directly... nor when the unit test called it directly + return p.validateMountConfigImpl(mnt, true) +} +func (p *linuxParser) validateMountConfigImpl(mnt *mount.Mount, validateBindSourceExists bool) error { + if len(mnt.Target) == 0 { + return &errMountConfig{mnt, errMissingField("Target")} + } + + if err := linuxValidateNotRoot(mnt.Target); err != nil { + return &errMountConfig{mnt, err} + } + + if err := linuxValidateAbsolute(mnt.Target); err != nil { + return &errMountConfig{mnt, err} + } + + switch mnt.Type { + case mount.TypeBind: + if len(mnt.Source) == 0 { + return &errMountConfig{mnt, errMissingField("Source")} + } + // Don't error out just because the propagation mode is not supported on the platform + if opts := mnt.BindOptions; opts != nil { + if len(opts.Propagation) > 0 && len(linuxPropagationModes) > 0 { + if _, ok := linuxPropagationModes[opts.Propagation]; !ok { + return &errMountConfig{mnt, fmt.Errorf("invalid propagation mode: %s", opts.Propagation)} + } + } + } + if mnt.VolumeOptions != nil { + return &errMountConfig{mnt, errExtraField("VolumeOptions")} + } + + if err := linuxValidateAbsolute(mnt.Source); err != nil { + return &errMountConfig{mnt, err} + } + + if validateBindSourceExists { + exists, _, err := currentFileInfoProvider.fileInfo(mnt.Source) + if err != nil { + return &errMountConfig{mnt, err} + } + if !exists { + return &errMountConfig{mnt, errBindSourceDoesNotExist(mnt.Source)} + } + } + + case mount.TypeVolume: + if mnt.BindOptions != nil { + return &errMountConfig{mnt, errExtraField("BindOptions")} + } + + if len(mnt.Source) == 0 && mnt.ReadOnly { + return &errMountConfig{mnt, fmt.Errorf("must not set ReadOnly mode when using anonymous volumes")} + } + case mount.TypeTmpfs: + if mnt.BindOptions != nil { + return &errMountConfig{mnt, errExtraField("BindOptions")} + } + if len(mnt.Source) != 0 { + return &errMountConfig{mnt, errExtraField("Source")} + } + if _, err := p.ConvertTmpfsOptions(mnt.TmpfsOptions, mnt.ReadOnly); err != nil { + return &errMountConfig{mnt, err} + } + default: + return &errMountConfig{mnt, errors.New("mount type unknown")} + } + return nil +} + +// read-write modes +var rwModes = map[string]bool{ + "rw": true, + "ro": true, +} + +// label modes +var linuxLabelModes = map[string]bool{ + "Z": true, + "z": true, +} + +// consistency modes +var linuxConsistencyModes = map[mount.Consistency]bool{ + mount.ConsistencyFull: true, + mount.ConsistencyCached: true, + mount.ConsistencyDelegated: true, +} +var linuxPropagationModes = map[mount.Propagation]bool{ + mount.PropagationPrivate: true, + mount.PropagationRPrivate: true, + mount.PropagationSlave: true, + mount.PropagationRSlave: true, + mount.PropagationShared: true, + mount.PropagationRShared: true, +} + +const linuxDefaultPropagationMode = mount.PropagationRPrivate + +func linuxGetPropagation(mode string) mount.Propagation { + for _, o := range strings.Split(mode, ",") { + prop := mount.Propagation(o) + if linuxPropagationModes[prop] { + return prop + } + } + return linuxDefaultPropagationMode +} + +func linuxHasPropagation(mode string) bool { + for _, o := range strings.Split(mode, ",") { + if linuxPropagationModes[mount.Propagation(o)] { + return true + } + } + return false +} + +func linuxValidMountMode(mode string) bool { + if mode == "" { + return true + } + + rwModeCount := 0 + labelModeCount := 0 + propagationModeCount := 0 + copyModeCount := 0 + consistencyModeCount := 0 + + for _, o := range strings.Split(mode, ",") { + switch { + case rwModes[o]: + rwModeCount++ + case linuxLabelModes[o]: + labelModeCount++ + case linuxPropagationModes[mount.Propagation(o)]: + propagationModeCount++ + case copyModeExists(o): + copyModeCount++ + case linuxConsistencyModes[mount.Consistency(o)]: + consistencyModeCount++ + default: + return false + } + } + + // Only one string for each mode is allowed. + if rwModeCount > 1 || labelModeCount > 1 || propagationModeCount > 1 || copyModeCount > 1 || consistencyModeCount > 1 { + return false + } + return true +} + +func (p *linuxParser) ReadWrite(mode string) bool { + if !linuxValidMountMode(mode) { + return false + } + + for _, o := range strings.Split(mode, ",") { + if o == "ro" { + return false + } + } + return true +} + +func (p *linuxParser) ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) { + arr, err := linuxSplitRawSpec(raw) + if err != nil { + return nil, err + } + + var spec mount.Mount + var mode string + switch len(arr) { + case 1: + // Just a destination path in the container + spec.Target = arr[0] + case 2: + if linuxValidMountMode(arr[1]) { + // Destination + Mode is not a valid volume - volumes + // cannot include a mode. e.g. /foo:rw + return nil, errInvalidSpec(raw) + } + // Host Source Path or Name + Destination + spec.Source = arr[0] + spec.Target = arr[1] + case 3: + // HostSourcePath+DestinationPath+Mode + spec.Source = arr[0] + spec.Target = arr[1] + mode = arr[2] + default: + return nil, errInvalidSpec(raw) + } + + if !linuxValidMountMode(mode) { + return nil, errInvalidMode(mode) + } + + if path.IsAbs(spec.Source) { + spec.Type = mount.TypeBind + } else { + spec.Type = mount.TypeVolume + } + + spec.ReadOnly = !p.ReadWrite(mode) + + // cannot assume that if a volume driver is passed in that we should set it + if volumeDriver != "" && spec.Type == mount.TypeVolume { + spec.VolumeOptions = &mount.VolumeOptions{ + DriverConfig: &mount.Driver{Name: volumeDriver}, + } + } + + if copyData, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet { + if spec.VolumeOptions == nil { + spec.VolumeOptions = &mount.VolumeOptions{} + } + spec.VolumeOptions.NoCopy = !copyData + } + if linuxHasPropagation(mode) { + spec.BindOptions = &mount.BindOptions{ + Propagation: linuxGetPropagation(mode), + } + } + + mp, err := p.parseMountSpec(spec, false) + if mp != nil { + mp.Mode = mode + } + if err != nil { + err = fmt.Errorf("%v: %v", errInvalidSpec(raw), err) + } + return mp, err +} +func (p *linuxParser) ParseMountSpec(cfg mount.Mount) (*MountPoint, error) { + return p.parseMountSpec(cfg, true) +} +func (p *linuxParser) parseMountSpec(cfg mount.Mount, validateBindSourceExists bool) (*MountPoint, error) { + if err := p.validateMountConfigImpl(&cfg, validateBindSourceExists); err != nil { + return nil, err + } + mp := &MountPoint{ + RW: !cfg.ReadOnly, + Destination: path.Clean(filepath.ToSlash(cfg.Target)), + Type: cfg.Type, + Spec: cfg, + } + + switch cfg.Type { + case mount.TypeVolume: + if cfg.Source == "" { + mp.Name = stringid.GenerateRandomID() + } else { + mp.Name = cfg.Source + } + mp.CopyData = p.DefaultCopyMode() + + if cfg.VolumeOptions != nil { + if cfg.VolumeOptions.DriverConfig != nil { + mp.Driver = cfg.VolumeOptions.DriverConfig.Name + } + if cfg.VolumeOptions.NoCopy { + mp.CopyData = false + } + } + case mount.TypeBind: + mp.Source = path.Clean(filepath.ToSlash(cfg.Source)) + if cfg.BindOptions != nil && len(cfg.BindOptions.Propagation) > 0 { + mp.Propagation = cfg.BindOptions.Propagation + } else { + // If user did not specify a propagation mode, get + // default propagation mode. + mp.Propagation = linuxDefaultPropagationMode + } + case mount.TypeTmpfs: + // NOP + } + return mp, nil +} + +func (p *linuxParser) ParseVolumesFrom(spec string) (string, string, error) { + if len(spec) == 0 { + return "", "", fmt.Errorf("volumes-from specification cannot be an empty string") + } + + specParts := strings.SplitN(spec, ":", 2) + id := specParts[0] + mode := "rw" + + if len(specParts) == 2 { + mode = specParts[1] + if !linuxValidMountMode(mode) { + return "", "", errInvalidMode(mode) + } + // For now don't allow propagation properties while importing + // volumes from data container. These volumes will inherit + // the same propagation property as of the original volume + // in data container. This probably can be relaxed in future. + if linuxHasPropagation(mode) { + return "", "", errInvalidMode(mode) + } + // Do not allow copy modes on volumes-from + if _, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet { + return "", "", errInvalidMode(mode) + } + } + return id, mode, nil +} + +func (p *linuxParser) DefaultPropagationMode() mount.Propagation { + return linuxDefaultPropagationMode +} + +func (p *linuxParser) ConvertTmpfsOptions(opt *mount.TmpfsOptions, readOnly bool) (string, error) { + var rawOpts []string + if readOnly { + rawOpts = append(rawOpts, "ro") + } + + if opt != nil && opt.Mode != 0 { + rawOpts = append(rawOpts, fmt.Sprintf("mode=%o", opt.Mode)) + } + + if opt != nil && opt.SizeBytes != 0 { + // calculate suffix here, making this linux specific, but that is + // okay, since API is that way anyways. + + // we do this by finding the suffix that divides evenly into the + // value, returning the value itself, with no suffix, if it fails. + // + // For the most part, we don't enforce any semantic to this values. + // The operating system will usually align this and enforce minimum + // and maximums. + var ( + size = opt.SizeBytes + suffix string + ) + for _, r := range []struct { + suffix string + divisor int64 + }{ + {"g", 1 << 30}, + {"m", 1 << 20}, + {"k", 1 << 10}, + } { + if size%r.divisor == 0 { + size = size / r.divisor + suffix = r.suffix + break + } + } + + rawOpts = append(rawOpts, fmt.Sprintf("size=%d%s", size, suffix)) + } + return strings.Join(rawOpts, ","), nil +} + +func (p *linuxParser) DefaultCopyMode() bool { + return true +} +func (p *linuxParser) ValidateVolumeName(name string) error { + return nil +} + +func (p *linuxParser) IsBackwardCompatible(m *MountPoint) bool { + return len(m.Source) > 0 || m.Driver == volume.DefaultDriverName +} + +func (p *linuxParser) ValidateTmpfsMountDestination(dest string) error { + if err := linuxValidateNotRoot(dest); err != nil { + return err + } + return linuxValidateAbsolute(dest) +} diff --git a/vendor/github.com/docker/docker/volume/mounts/mounts.go b/vendor/github.com/docker/docker/volume/mounts/mounts.go new file mode 100644 index 0000000000..c441e51ed9 --- /dev/null +++ b/vendor/github.com/docker/docker/volume/mounts/mounts.go @@ -0,0 +1,181 @@ +package mounts // import "github.com/docker/docker/volume/mounts" + +import ( + "fmt" + "os" + "path/filepath" + "syscall" + + mounttypes "github.com/docker/docker/api/types/mount" + "github.com/docker/docker/pkg/idtools" + "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/volume" + "github.com/opencontainers/selinux/go-selinux/label" + "github.com/pkg/errors" +) + +// MountPoint is the intersection point between a volume and a container. It +// specifies which volume is to be used and where inside a container it should +// be mounted. +// +// Note that this type is embedded in `container.Container` object and persisted to disk. +// Changes to this struct need to by synced with on disk state. +type MountPoint struct { + // Source is the source path of the mount. + // E.g. `mount --bind /foo /bar`, `/foo` is the `Source`. + Source string + // Destination is the path relative to the container root (`/`) to the mount point + // It is where the `Source` is mounted to + Destination string + // RW is set to true when the mountpoint should be mounted as read-write + RW bool + // Name is the name reference to the underlying data defined by `Source` + // e.g., the volume name + Name string + // Driver is the volume driver used to create the volume (if it is a volume) + Driver string + // Type of mount to use, see `Type` definitions in github.com/docker/docker/api/types/mount + Type mounttypes.Type `json:",omitempty"` + // Volume is the volume providing data to this mountpoint. + // This is nil unless `Type` is set to `TypeVolume` + Volume volume.Volume `json:"-"` + + // Mode is the comma separated list of options supplied by the user when creating + // the bind/volume mount. + // Note Mode is not used on Windows + Mode string `json:"Relabel,omitempty"` // Originally field was `Relabel`" + + // Propagation describes how the mounts are propagated from the host into the + // mount point, and vice-versa. + // See https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt + // Note Propagation is not used on Windows + Propagation mounttypes.Propagation `json:",omitempty"` // Mount propagation string + + // Specifies if data should be copied from the container before the first mount + // Use a pointer here so we can tell if the user set this value explicitly + // This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated + CopyData bool `json:"-"` + // ID is the opaque ID used to pass to the volume driver. + // This should be set by calls to `Mount` and unset by calls to `Unmount` + ID string `json:",omitempty"` + + // Sepc is a copy of the API request that created this mount. + Spec mounttypes.Mount + + // Some bind mounts should not be automatically created. + // (Some are auto-created for backwards-compatibility) + // This is checked on the API but setting this here prevents race conditions. + // where a bind dir existed during validation was removed before reaching the setup code. + SkipMountpointCreation bool + + // Track usage of this mountpoint + // Specifically needed for containers which are running and calls to `docker cp` + // because both these actions require mounting the volumes. + active int +} + +// Cleanup frees resources used by the mountpoint +func (m *MountPoint) Cleanup() error { + if m.Volume == nil || m.ID == "" { + return nil + } + + if err := m.Volume.Unmount(m.ID); err != nil { + return errors.Wrapf(err, "error unmounting volume %s", m.Volume.Name()) + } + + m.active-- + if m.active == 0 { + m.ID = "" + } + return nil +} + +// Setup sets up a mount point by either mounting the volume if it is +// configured, or creating the source directory if supplied. +// The, optional, checkFun parameter allows doing additional checking +// before creating the source directory on the host. +func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.Identity, checkFun func(m *MountPoint) error) (path string, err error) { + if m.SkipMountpointCreation { + return m.Source, nil + } + + defer func() { + if err != nil || !label.RelabelNeeded(m.Mode) { + return + } + + var sourcePath string + sourcePath, err = filepath.EvalSymlinks(m.Source) + if err != nil { + path = "" + err = errors.Wrapf(err, "error evaluating symlinks from mount source %q", m.Source) + return + } + err = label.Relabel(sourcePath, mountLabel, label.IsShared(m.Mode)) + if errors.Is(err, syscall.ENOTSUP) { + err = nil + } + if err != nil { + path = "" + err = errors.Wrapf(err, "error setting label on mount source '%s'", sourcePath) + } + }() + + if m.Volume != nil { + id := m.ID + if id == "" { + id = stringid.GenerateRandomID() + } + path, err := m.Volume.Mount(id) + if err != nil { + return "", errors.Wrapf(err, "error while mounting volume '%s'", m.Source) + } + + m.ID = id + m.active++ + return path, nil + } + + if len(m.Source) == 0 { + return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined") + } + + if m.Type == mounttypes.TypeBind { + // Before creating the source directory on the host, invoke checkFun if it's not nil. One of + // the use case is to forbid creating the daemon socket as a directory if the daemon is in + // the process of shutting down. + if checkFun != nil { + if err := checkFun(m); err != nil { + return "", err + } + } + + // idtools.MkdirAllNewAs() produces an error if m.Source exists and is a file (not a directory) + // also, makes sure that if the directory is created, the correct remapped rootUID/rootGID will own it + if err := idtools.MkdirAllAndChownNew(m.Source, 0755, rootIDs); err != nil { + if perr, ok := err.(*os.PathError); ok { + if perr.Err != syscall.ENOTDIR { + return "", errors.Wrapf(err, "error while creating mount source path '%s'", m.Source) + } + } + } + } + return m.Source, nil +} + +// Path returns the path of a volume in a mount point. +func (m *MountPoint) Path() string { + if m.Volume != nil { + return m.Volume.Path() + } + return m.Source +} + +func errInvalidMode(mode string) error { + return errors.Errorf("invalid mode: %v", mode) +} + +func errInvalidSpec(spec string) error { + return errors.Errorf("invalid volume specification: '%s'", spec) +} diff --git a/vendor/github.com/docker/docker/volume/mounts/parser.go b/vendor/github.com/docker/docker/volume/mounts/parser.go new file mode 100644 index 0000000000..73681750ea --- /dev/null +++ b/vendor/github.com/docker/docker/volume/mounts/parser.go @@ -0,0 +1,47 @@ +package mounts // import "github.com/docker/docker/volume/mounts" + +import ( + "errors" + "runtime" + + "github.com/docker/docker/api/types/mount" +) + +const ( + // OSLinux is the same as runtime.GOOS on linux + OSLinux = "linux" + // OSWindows is the same as runtime.GOOS on windows + OSWindows = "windows" +) + +// ErrVolumeTargetIsRoot is returned when the target destination is root. +// It's used by both LCOW and Linux parsers. +var ErrVolumeTargetIsRoot = errors.New("invalid specification: destination can't be '/'") + +// Parser represents a platform specific parser for mount expressions +type Parser interface { + ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) + ParseMountSpec(cfg mount.Mount) (*MountPoint, error) + ParseVolumesFrom(spec string) (string, string, error) + DefaultPropagationMode() mount.Propagation + ConvertTmpfsOptions(opt *mount.TmpfsOptions, readOnly bool) (string, error) + DefaultCopyMode() bool + ValidateVolumeName(name string) error + ReadWrite(mode string) bool + IsBackwardCompatible(m *MountPoint) bool + HasResource(m *MountPoint, absPath string) bool + ValidateTmpfsMountDestination(dest string) error + ValidateMountConfig(mt *mount.Mount) error +} + +// NewParser creates a parser for a given container OS, depending on the current host OS (linux on a windows host will resolve to an lcowParser) +func NewParser(containerOS string) Parser { + switch containerOS { + case OSWindows: + return &windowsParser{} + } + if runtime.GOOS == OSWindows { + return &lcowParser{} + } + return &linuxParser{} +} diff --git a/vendor/github.com/docker/docker/volume/mounts/validate.go b/vendor/github.com/docker/docker/volume/mounts/validate.go new file mode 100644 index 0000000000..9fc9109021 --- /dev/null +++ b/vendor/github.com/docker/docker/volume/mounts/validate.go @@ -0,0 +1,28 @@ +package mounts // import "github.com/docker/docker/volume/mounts" + +import ( + "fmt" + + "github.com/docker/docker/api/types/mount" + "github.com/pkg/errors" +) + +type errMountConfig struct { + mount *mount.Mount + err error +} + +func (e *errMountConfig) Error() string { + return fmt.Sprintf("invalid mount config for type %q: %v", e.mount.Type, e.err.Error()) +} + +func errBindSourceDoesNotExist(path string) error { + return errors.Errorf("bind source path does not exist: %s", path) +} + +func errExtraField(name string) error { + return errors.Errorf("field %s must not be specified", name) +} +func errMissingField(name string) error { + return errors.Errorf("field %s must not be empty", name) +} diff --git a/vendor/github.com/docker/docker/volume/mounts/volume_copy.go b/vendor/github.com/docker/docker/volume/mounts/volume_copy.go new file mode 100644 index 0000000000..04056fa50a --- /dev/null +++ b/vendor/github.com/docker/docker/volume/mounts/volume_copy.go @@ -0,0 +1,23 @@ +package mounts // import "github.com/docker/docker/volume/mounts" + +import "strings" + +// {=isEnabled} +var copyModes = map[string]bool{ + "nocopy": false, +} + +func copyModeExists(mode string) bool { + _, exists := copyModes[mode] + return exists +} + +// GetCopyMode gets the copy mode from the mode string for mounts +func getCopyMode(mode string, def bool) (bool, bool) { + for _, o := range strings.Split(mode, ",") { + if isEnabled, exists := copyModes[o]; exists { + return isEnabled, true + } + } + return def, false +} diff --git a/vendor/github.com/docker/docker/volume/mounts/volume_unix.go b/vendor/github.com/docker/docker/volume/mounts/volume_unix.go new file mode 100644 index 0000000000..c6d51e0710 --- /dev/null +++ b/vendor/github.com/docker/docker/volume/mounts/volume_unix.go @@ -0,0 +1,18 @@ +// +build linux freebsd darwin + +package mounts // import "github.com/docker/docker/volume/mounts" + +import ( + "fmt" + "path/filepath" + "strings" +) + +func (p *linuxParser) HasResource(m *MountPoint, absolutePath string) bool { + relPath, err := filepath.Rel(m.Destination, absolutePath) + return err == nil && relPath != ".." && !strings.HasPrefix(relPath, fmt.Sprintf("..%c", filepath.Separator)) +} + +func (p *windowsParser) HasResource(m *MountPoint, absolutePath string) bool { + return false +} diff --git a/vendor/github.com/docker/docker/volume/mounts/volume_windows.go b/vendor/github.com/docker/docker/volume/mounts/volume_windows.go new file mode 100644 index 0000000000..773e7db88a --- /dev/null +++ b/vendor/github.com/docker/docker/volume/mounts/volume_windows.go @@ -0,0 +1,8 @@ +package mounts // import "github.com/docker/docker/volume/mounts" + +func (p *windowsParser) HasResource(m *MountPoint, absolutePath string) bool { + return false +} +func (p *linuxParser) HasResource(m *MountPoint, absolutePath string) bool { + return false +} diff --git a/vendor/github.com/docker/docker/volume/mounts/windows_parser.go b/vendor/github.com/docker/docker/volume/mounts/windows_parser.go new file mode 100644 index 0000000000..8f427d8c50 --- /dev/null +++ b/vendor/github.com/docker/docker/volume/mounts/windows_parser.go @@ -0,0 +1,456 @@ +package mounts // import "github.com/docker/docker/volume/mounts" + +import ( + "errors" + "fmt" + "os" + "regexp" + "runtime" + "strings" + + "github.com/docker/docker/api/types/mount" + "github.com/docker/docker/pkg/stringid" +) + +type windowsParser struct { +} + +const ( + // Spec should be in the format [source:]destination[:mode] + // + // Examples: c:\foo bar:d:rw + // c:\foo:d:\bar + // myname:d: + // d:\ + // + // Explanation of this regex! Thanks @thaJeztah on IRC and gist for help. See + // https://gist.github.com/thaJeztah/6185659e4978789fb2b2. A good place to + // test is https://regex-golang.appspot.com/assets/html/index.html + // + // Useful link for referencing named capturing groups: + // http://stackoverflow.com/questions/20750843/using-named-matches-from-go-regex + // + // There are three match groups: source, destination and mode. + // + + // rxHostDir is the first option of a source + rxHostDir = `(?:\\\\\?\\)?[a-z]:[\\/](?:[^\\/:*?"<>|\r\n]+[\\/]?)*` + // rxName is the second option of a source + rxName = `[^\\/:*?"<>|\r\n]+` + + // RXReservedNames are reserved names not possible on Windows + rxReservedNames = `(con)|(prn)|(nul)|(aux)|(com[1-9])|(lpt[1-9])` + + // rxPipe is a named path pipe (starts with `\\.\pipe\`, possibly with / instead of \) + rxPipe = `[/\\]{2}.[/\\]pipe[/\\][^:*?"<>|\r\n]+` + // rxSource is the combined possibilities for a source + rxSource = `((?P((` + rxHostDir + `)|(` + rxName + `)|(` + rxPipe + `))):)?` + + // Source. Can be either a host directory, a name, or omitted: + // HostDir: + // - Essentially using the folder solution from + // https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9781449327453/ch08s18.html + // but adding case insensitivity. + // - Must be an absolute path such as c:\path + // - Can include spaces such as `c:\program files` + // - And then followed by a colon which is not in the capture group + // - And can be optional + // Name: + // - Must not contain invalid NTFS filename characters (https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx) + // - And then followed by a colon which is not in the capture group + // - And can be optional + + // rxDestination is the regex expression for the mount destination + rxDestination = `(?P((?:\\\\\?\\)?([a-z]):((?:[\\/][^\\/:*?"<>\r\n]+)*[\\/]?))|(` + rxPipe + `))` + + rxLCOWDestination = `(?P/(?:[^\\/:*?"<>\r\n]+[/]?)*)` + // Destination (aka container path): + // - Variation on hostdir but can be a drive followed by colon as well + // - If a path, must be absolute. Can include spaces + // - Drive cannot be c: (explicitly checked in code, not RegEx) + + // rxMode is the regex expression for the mode of the mount + // Mode (optional): + // - Hopefully self explanatory in comparison to above regex's. + // - Colon is not in the capture group + rxMode = `(:(?P(?i)ro|rw))?` +) + +type mountValidator func(mnt *mount.Mount) error + +func windowsSplitRawSpec(raw, destRegex string) ([]string, error) { + specExp := regexp.MustCompile(`^` + rxSource + destRegex + rxMode + `$`) + match := specExp.FindStringSubmatch(strings.ToLower(raw)) + + // Must have something back + if len(match) == 0 { + return nil, errInvalidSpec(raw) + } + + var split []string + matchgroups := make(map[string]string) + // Pull out the sub expressions from the named capture groups + for i, name := range specExp.SubexpNames() { + matchgroups[name] = strings.ToLower(match[i]) + } + if source, exists := matchgroups["source"]; exists { + if source != "" { + split = append(split, source) + } + } + if destination, exists := matchgroups["destination"]; exists { + if destination != "" { + split = append(split, destination) + } + } + if mode, exists := matchgroups["mode"]; exists { + if mode != "" { + split = append(split, mode) + } + } + // Fix #26329. If the destination appears to be a file, and the source is null, + // it may be because we've fallen through the possible naming regex and hit a + // situation where the user intention was to map a file into a container through + // a local volume, but this is not supported by the platform. + if matchgroups["source"] == "" && matchgroups["destination"] != "" { + volExp := regexp.MustCompile(`^` + rxName + `$`) + reservedNameExp := regexp.MustCompile(`^` + rxReservedNames + `$`) + + if volExp.MatchString(matchgroups["destination"]) { + if reservedNameExp.MatchString(matchgroups["destination"]) { + return nil, fmt.Errorf("volume name %q cannot be a reserved word for Windows filenames", matchgroups["destination"]) + } + } else { + + exists, isDir, _ := currentFileInfoProvider.fileInfo(matchgroups["destination"]) + if exists && !isDir { + return nil, fmt.Errorf("file '%s' cannot be mapped. Only directories can be mapped on this platform", matchgroups["destination"]) + + } + } + } + return split, nil +} + +func windowsValidMountMode(mode string) bool { + if mode == "" { + return true + } + return rwModes[strings.ToLower(mode)] +} +func windowsValidateNotRoot(p string) error { + p = strings.ToLower(strings.Replace(p, `/`, `\`, -1)) + if p == "c:" || p == `c:\` { + return fmt.Errorf("destination path cannot be `c:` or `c:\\`: %v", p) + } + return nil +} + +var windowsSpecificValidators mountValidator = func(mnt *mount.Mount) error { + return windowsValidateNotRoot(mnt.Target) +} + +func windowsValidateRegex(p, r string) error { + if regexp.MustCompile(`^` + r + `$`).MatchString(strings.ToLower(p)) { + return nil + } + return fmt.Errorf("invalid mount path: '%s'", p) +} +func windowsValidateAbsolute(p string) error { + if err := windowsValidateRegex(p, rxDestination); err != nil { + return fmt.Errorf("invalid mount path: '%s' mount path must be absolute", p) + } + return nil +} + +func windowsDetectMountType(p string) mount.Type { + if strings.HasPrefix(p, `\\.\pipe\`) { + return mount.TypeNamedPipe + } else if regexp.MustCompile(`^` + rxHostDir + `$`).MatchString(p) { + return mount.TypeBind + } else { + return mount.TypeVolume + } +} + +func (p *windowsParser) ReadWrite(mode string) bool { + return strings.ToLower(mode) != "ro" +} + +// IsVolumeNameValid checks a volume name in a platform specific manner. +func (p *windowsParser) ValidateVolumeName(name string) error { + nameExp := regexp.MustCompile(`^` + rxName + `$`) + if !nameExp.MatchString(name) { + return errors.New("invalid volume name") + } + nameExp = regexp.MustCompile(`^` + rxReservedNames + `$`) + if nameExp.MatchString(name) { + return fmt.Errorf("volume name %q cannot be a reserved word for Windows filenames", name) + } + return nil +} +func (p *windowsParser) ValidateMountConfig(mnt *mount.Mount) error { + return p.validateMountConfigReg(mnt, rxDestination, windowsSpecificValidators) +} + +type fileInfoProvider interface { + fileInfo(path string) (exist, isDir bool, err error) +} + +type defaultFileInfoProvider struct { +} + +func (defaultFileInfoProvider) fileInfo(path string) (exist, isDir bool, err error) { + fi, err := os.Stat(path) + if err != nil { + if !os.IsNotExist(err) { + return false, false, err + } + return false, false, nil + } + return true, fi.IsDir(), nil +} + +var currentFileInfoProvider fileInfoProvider = defaultFileInfoProvider{} + +func (p *windowsParser) validateMountConfigReg(mnt *mount.Mount, destRegex string, additionalValidators ...mountValidator) error { + + for _, v := range additionalValidators { + if err := v(mnt); err != nil { + return &errMountConfig{mnt, err} + } + } + if len(mnt.Target) == 0 { + return &errMountConfig{mnt, errMissingField("Target")} + } + + if err := windowsValidateRegex(mnt.Target, destRegex); err != nil { + return &errMountConfig{mnt, err} + } + + switch mnt.Type { + case mount.TypeBind: + if len(mnt.Source) == 0 { + return &errMountConfig{mnt, errMissingField("Source")} + } + // Don't error out just because the propagation mode is not supported on the platform + if opts := mnt.BindOptions; opts != nil { + if len(opts.Propagation) > 0 { + return &errMountConfig{mnt, fmt.Errorf("invalid propagation mode: %s", opts.Propagation)} + } + } + if mnt.VolumeOptions != nil { + return &errMountConfig{mnt, errExtraField("VolumeOptions")} + } + + if err := windowsValidateAbsolute(mnt.Source); err != nil { + return &errMountConfig{mnt, err} + } + + exists, isdir, err := currentFileInfoProvider.fileInfo(mnt.Source) + if err != nil { + return &errMountConfig{mnt, err} + } + if !exists { + return &errMountConfig{mnt, errBindSourceDoesNotExist(mnt.Source)} + } + if !isdir { + return &errMountConfig{mnt, fmt.Errorf("source path must be a directory")} + } + + case mount.TypeVolume: + if mnt.BindOptions != nil { + return &errMountConfig{mnt, errExtraField("BindOptions")} + } + + if len(mnt.Source) == 0 && mnt.ReadOnly { + return &errMountConfig{mnt, fmt.Errorf("must not set ReadOnly mode when using anonymous volumes")} + } + + if len(mnt.Source) != 0 { + if err := p.ValidateVolumeName(mnt.Source); err != nil { + return &errMountConfig{mnt, err} + } + } + case mount.TypeNamedPipe: + if len(mnt.Source) == 0 { + return &errMountConfig{mnt, errMissingField("Source")} + } + + if mnt.BindOptions != nil { + return &errMountConfig{mnt, errExtraField("BindOptions")} + } + + if mnt.ReadOnly { + return &errMountConfig{mnt, errExtraField("ReadOnly")} + } + + if windowsDetectMountType(mnt.Source) != mount.TypeNamedPipe { + return &errMountConfig{mnt, fmt.Errorf("'%s' is not a valid pipe path", mnt.Source)} + } + + if windowsDetectMountType(mnt.Target) != mount.TypeNamedPipe { + return &errMountConfig{mnt, fmt.Errorf("'%s' is not a valid pipe path", mnt.Target)} + } + default: + return &errMountConfig{mnt, errors.New("mount type unknown")} + } + return nil +} +func (p *windowsParser) ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) { + return p.parseMountRaw(raw, volumeDriver, rxDestination, true, windowsSpecificValidators) +} + +func (p *windowsParser) parseMountRaw(raw, volumeDriver, destRegex string, convertTargetToBackslash bool, additionalValidators ...mountValidator) (*MountPoint, error) { + arr, err := windowsSplitRawSpec(raw, destRegex) + if err != nil { + return nil, err + } + + var spec mount.Mount + var mode string + switch len(arr) { + case 1: + // Just a destination path in the container + spec.Target = arr[0] + case 2: + if windowsValidMountMode(arr[1]) { + // Destination + Mode is not a valid volume - volumes + // cannot include a mode. e.g. /foo:rw + return nil, errInvalidSpec(raw) + } + // Host Source Path or Name + Destination + spec.Source = strings.Replace(arr[0], `/`, `\`, -1) + spec.Target = arr[1] + case 3: + // HostSourcePath+DestinationPath+Mode + spec.Source = strings.Replace(arr[0], `/`, `\`, -1) + spec.Target = arr[1] + mode = arr[2] + default: + return nil, errInvalidSpec(raw) + } + if convertTargetToBackslash { + spec.Target = strings.Replace(spec.Target, `/`, `\`, -1) + } + + if !windowsValidMountMode(mode) { + return nil, errInvalidMode(mode) + } + + spec.Type = windowsDetectMountType(spec.Source) + spec.ReadOnly = !p.ReadWrite(mode) + + // cannot assume that if a volume driver is passed in that we should set it + if volumeDriver != "" && spec.Type == mount.TypeVolume { + spec.VolumeOptions = &mount.VolumeOptions{ + DriverConfig: &mount.Driver{Name: volumeDriver}, + } + } + + if copyData, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet { + if spec.VolumeOptions == nil { + spec.VolumeOptions = &mount.VolumeOptions{} + } + spec.VolumeOptions.NoCopy = !copyData + } + + mp, err := p.parseMountSpec(spec, destRegex, convertTargetToBackslash, additionalValidators...) + if mp != nil { + mp.Mode = mode + } + if err != nil { + err = fmt.Errorf("%v: %v", errInvalidSpec(raw), err) + } + return mp, err +} + +func (p *windowsParser) ParseMountSpec(cfg mount.Mount) (*MountPoint, error) { + return p.parseMountSpec(cfg, rxDestination, true, windowsSpecificValidators) +} +func (p *windowsParser) parseMountSpec(cfg mount.Mount, destRegex string, convertTargetToBackslash bool, additionalValidators ...mountValidator) (*MountPoint, error) { + if err := p.validateMountConfigReg(&cfg, destRegex, additionalValidators...); err != nil { + return nil, err + } + mp := &MountPoint{ + RW: !cfg.ReadOnly, + Destination: cfg.Target, + Type: cfg.Type, + Spec: cfg, + } + if convertTargetToBackslash { + mp.Destination = strings.Replace(cfg.Target, `/`, `\`, -1) + } + + switch cfg.Type { + case mount.TypeVolume: + if cfg.Source == "" { + mp.Name = stringid.GenerateRandomID() + } else { + mp.Name = cfg.Source + } + mp.CopyData = p.DefaultCopyMode() + + if cfg.VolumeOptions != nil { + if cfg.VolumeOptions.DriverConfig != nil { + mp.Driver = cfg.VolumeOptions.DriverConfig.Name + } + if cfg.VolumeOptions.NoCopy { + mp.CopyData = false + } + } + case mount.TypeBind: + mp.Source = strings.Replace(cfg.Source, `/`, `\`, -1) + case mount.TypeNamedPipe: + mp.Source = strings.Replace(cfg.Source, `/`, `\`, -1) + } + // cleanup trailing `\` except for paths like `c:\` + if len(mp.Source) > 3 && mp.Source[len(mp.Source)-1] == '\\' { + mp.Source = mp.Source[:len(mp.Source)-1] + } + if len(mp.Destination) > 3 && mp.Destination[len(mp.Destination)-1] == '\\' { + mp.Destination = mp.Destination[:len(mp.Destination)-1] + } + return mp, nil +} + +func (p *windowsParser) ParseVolumesFrom(spec string) (string, string, error) { + if len(spec) == 0 { + return "", "", fmt.Errorf("volumes-from specification cannot be an empty string") + } + + specParts := strings.SplitN(spec, ":", 2) + id := specParts[0] + mode := "rw" + + if len(specParts) == 2 { + mode = specParts[1] + if !windowsValidMountMode(mode) { + return "", "", errInvalidMode(mode) + } + + // Do not allow copy modes on volumes-from + if _, isSet := getCopyMode(mode, p.DefaultCopyMode()); isSet { + return "", "", errInvalidMode(mode) + } + } + return id, mode, nil +} + +func (p *windowsParser) DefaultPropagationMode() mount.Propagation { + return mount.Propagation("") +} + +func (p *windowsParser) ConvertTmpfsOptions(opt *mount.TmpfsOptions, readOnly bool) (string, error) { + return "", fmt.Errorf("%s does not support tmpfs", runtime.GOOS) +} +func (p *windowsParser) DefaultCopyMode() bool { + return false +} +func (p *windowsParser) IsBackwardCompatible(m *MountPoint) bool { + return false +} + +func (p *windowsParser) ValidateTmpfsMountDestination(dest string) error { + return errors.New("Platform does not support tmpfs") +} diff --git a/vendor/github.com/docker/docker/volume/volume.go b/vendor/github.com/docker/docker/volume/volume.go new file mode 100644 index 0000000000..61c8243979 --- /dev/null +++ b/vendor/github.com/docker/docker/volume/volume.go @@ -0,0 +1,69 @@ +package volume // import "github.com/docker/docker/volume" + +import ( + "time" +) + +// DefaultDriverName is the driver name used for the driver +// implemented in the local package. +const DefaultDriverName = "local" + +// Scopes define if a volume has is cluster-wide (global) or local only. +// Scopes are returned by the volume driver when it is queried for capabilities and then set on a volume +const ( + LocalScope = "local" + GlobalScope = "global" +) + +// Driver is for creating and removing volumes. +type Driver interface { + // Name returns the name of the volume driver. + Name() string + // Create makes a new volume with the given name. + Create(name string, opts map[string]string) (Volume, error) + // Remove deletes the volume. + Remove(vol Volume) (err error) + // List lists all the volumes the driver has + List() ([]Volume, error) + // Get retrieves the volume with the requested name + Get(name string) (Volume, error) + // Scope returns the scope of the driver (e.g. `global` or `local`). + // Scope determines how the driver is handled at a cluster level + Scope() string +} + +// Capability defines a set of capabilities that a driver is able to handle. +type Capability struct { + // Scope is the scope of the driver, `global` or `local` + // A `global` scope indicates that the driver manages volumes across the cluster + // A `local` scope indicates that the driver only manages volumes resources local to the host + // Scope is declared by the driver + Scope string +} + +// Volume is a place to store data. It is backed by a specific driver, and can be mounted. +type Volume interface { + // Name returns the name of the volume + Name() string + // DriverName returns the name of the driver which owns this volume. + DriverName() string + // Path returns the absolute path to the volume. + Path() string + // Mount mounts the volume and returns the absolute path to + // where it can be consumed. + Mount(id string) (string, error) + // Unmount unmounts the volume when it is no longer in use. + Unmount(id string) error + // CreatedAt returns Volume Creation time + CreatedAt() (time.Time, error) + // Status returns low-level status information about a volume + Status() map[string]interface{} +} + +// DetailedVolume wraps a Volume with user-defined labels, options, and cluster scope (e.g., `local` or `global`) +type DetailedVolume interface { + Labels() map[string]string + Options() map[string]string + Scope() string + Volume +} diff --git a/vendor/github.com/docker/go-connections/LICENSE b/vendor/github.com/docker/go-connections/LICENSE new file mode 100644 index 0000000000..b55b37bc31 --- /dev/null +++ b/vendor/github.com/docker/go-connections/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright 2015 Docker, Inc. + + 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 + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/docker/go-connections/nat/nat.go b/vendor/github.com/docker/go-connections/nat/nat.go new file mode 100644 index 0000000000..bb7e4e3369 --- /dev/null +++ b/vendor/github.com/docker/go-connections/nat/nat.go @@ -0,0 +1,242 @@ +// Package nat is a convenience package for manipulation of strings describing network ports. +package nat + +import ( + "fmt" + "net" + "strconv" + "strings" +) + +const ( + // portSpecTemplate is the expected format for port specifications + portSpecTemplate = "ip:hostPort:containerPort" +) + +// PortBinding represents a binding between a Host IP address and a Host Port +type PortBinding struct { + // HostIP is the host IP Address + HostIP string `json:"HostIp"` + // HostPort is the host port number + HostPort string +} + +// PortMap is a collection of PortBinding indexed by Port +type PortMap map[Port][]PortBinding + +// PortSet is a collection of structs indexed by Port +type PortSet map[Port]struct{} + +// Port is a string containing port number and protocol in the format "80/tcp" +type Port string + +// NewPort creates a new instance of a Port given a protocol and port number or port range +func NewPort(proto, port string) (Port, error) { + // Check for parsing issues on "port" now so we can avoid having + // to check it later on. + + portStartInt, portEndInt, err := ParsePortRangeToInt(port) + if err != nil { + return "", err + } + + if portStartInt == portEndInt { + return Port(fmt.Sprintf("%d/%s", portStartInt, proto)), nil + } + return Port(fmt.Sprintf("%d-%d/%s", portStartInt, portEndInt, proto)), nil +} + +// ParsePort parses the port number string and returns an int +func ParsePort(rawPort string) (int, error) { + if len(rawPort) == 0 { + return 0, nil + } + port, err := strconv.ParseUint(rawPort, 10, 16) + if err != nil { + return 0, err + } + return int(port), nil +} + +// ParsePortRangeToInt parses the port range string and returns start/end ints +func ParsePortRangeToInt(rawPort string) (int, int, error) { + if len(rawPort) == 0 { + return 0, 0, nil + } + start, end, err := ParsePortRange(rawPort) + if err != nil { + return 0, 0, err + } + return int(start), int(end), nil +} + +// Proto returns the protocol of a Port +func (p Port) Proto() string { + proto, _ := SplitProtoPort(string(p)) + return proto +} + +// Port returns the port number of a Port +func (p Port) Port() string { + _, port := SplitProtoPort(string(p)) + return port +} + +// Int returns the port number of a Port as an int +func (p Port) Int() int { + portStr := p.Port() + // We don't need to check for an error because we're going to + // assume that any error would have been found, and reported, in NewPort() + port, _ := ParsePort(portStr) + return port +} + +// Range returns the start/end port numbers of a Port range as ints +func (p Port) Range() (int, int, error) { + return ParsePortRangeToInt(p.Port()) +} + +// SplitProtoPort splits a port in the format of proto/port +func SplitProtoPort(rawPort string) (string, string) { + parts := strings.Split(rawPort, "/") + l := len(parts) + if len(rawPort) == 0 || l == 0 || len(parts[0]) == 0 { + return "", "" + } + if l == 1 { + return "tcp", rawPort + } + if len(parts[1]) == 0 { + return "tcp", parts[0] + } + return parts[1], parts[0] +} + +func validateProto(proto string) bool { + for _, availableProto := range []string{"tcp", "udp", "sctp"} { + if availableProto == proto { + return true + } + } + return false +} + +// ParsePortSpecs receives port specs in the format of ip:public:private/proto and parses +// these in to the internal types +func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) { + var ( + exposedPorts = make(map[Port]struct{}, len(ports)) + bindings = make(map[Port][]PortBinding) + ) + for _, rawPort := range ports { + portMappings, err := ParsePortSpec(rawPort) + if err != nil { + return nil, nil, err + } + + for _, portMapping := range portMappings { + port := portMapping.Port + if _, exists := exposedPorts[port]; !exists { + exposedPorts[port] = struct{}{} + } + bslice, exists := bindings[port] + if !exists { + bslice = []PortBinding{} + } + bindings[port] = append(bslice, portMapping.Binding) + } + } + return exposedPorts, bindings, nil +} + +// PortMapping is a data object mapping a Port to a PortBinding +type PortMapping struct { + Port Port + Binding PortBinding +} + +func splitParts(rawport string) (string, string, string) { + parts := strings.Split(rawport, ":") + n := len(parts) + containerport := parts[n-1] + + switch n { + case 1: + return "", "", containerport + case 2: + return "", parts[0], containerport + case 3: + return parts[0], parts[1], containerport + default: + return strings.Join(parts[:n-2], ":"), parts[n-2], containerport + } +} + +// ParsePortSpec parses a port specification string into a slice of PortMappings +func ParsePortSpec(rawPort string) ([]PortMapping, error) { + var proto string + rawIP, hostPort, containerPort := splitParts(rawPort) + proto, containerPort = SplitProtoPort(containerPort) + + // Strip [] from IPV6 addresses + ip, _, err := net.SplitHostPort(rawIP + ":") + if err != nil { + return nil, fmt.Errorf("Invalid ip address %v: %s", rawIP, err) + } + if ip != "" && net.ParseIP(ip) == nil { + return nil, fmt.Errorf("Invalid ip address: %s", ip) + } + if containerPort == "" { + return nil, fmt.Errorf("No port specified: %s", rawPort) + } + + startPort, endPort, err := ParsePortRange(containerPort) + if err != nil { + return nil, fmt.Errorf("Invalid containerPort: %s", containerPort) + } + + var startHostPort, endHostPort uint64 = 0, 0 + if len(hostPort) > 0 { + startHostPort, endHostPort, err = ParsePortRange(hostPort) + if err != nil { + return nil, fmt.Errorf("Invalid hostPort: %s", hostPort) + } + } + + if hostPort != "" && (endPort-startPort) != (endHostPort-startHostPort) { + // Allow host port range iff containerPort is not a range. + // In this case, use the host port range as the dynamic + // host port range to allocate into. + if endPort != startPort { + return nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort) + } + } + + if !validateProto(strings.ToLower(proto)) { + return nil, fmt.Errorf("Invalid proto: %s", proto) + } + + ports := []PortMapping{} + for i := uint64(0); i <= (endPort - startPort); i++ { + containerPort = strconv.FormatUint(startPort+i, 10) + if len(hostPort) > 0 { + hostPort = strconv.FormatUint(startHostPort+i, 10) + } + // Set hostPort to a range only if there is a single container port + // and a dynamic host port. + if startPort == endPort && startHostPort != endHostPort { + hostPort = fmt.Sprintf("%s-%s", hostPort, strconv.FormatUint(endHostPort, 10)) + } + port, err := NewPort(strings.ToLower(proto), containerPort) + if err != nil { + return nil, err + } + + binding := PortBinding{ + HostIP: ip, + HostPort: hostPort, + } + ports = append(ports, PortMapping{Port: port, Binding: binding}) + } + return ports, nil +} diff --git a/vendor/github.com/docker/go-connections/nat/parse.go b/vendor/github.com/docker/go-connections/nat/parse.go new file mode 100644 index 0000000000..892adf8c66 --- /dev/null +++ b/vendor/github.com/docker/go-connections/nat/parse.go @@ -0,0 +1,57 @@ +package nat + +import ( + "fmt" + "strconv" + "strings" +) + +// PartParser parses and validates the specified string (data) using the specified template +// e.g. ip:public:private -> 192.168.0.1:80:8000 +// DEPRECATED: do not use, this function may be removed in a future version +func PartParser(template, data string) (map[string]string, error) { + // ip:public:private + var ( + templateParts = strings.Split(template, ":") + parts = strings.Split(data, ":") + out = make(map[string]string, len(templateParts)) + ) + if len(parts) != len(templateParts) { + return nil, fmt.Errorf("Invalid format to parse. %s should match template %s", data, template) + } + + for i, t := range templateParts { + value := "" + if len(parts) > i { + value = parts[i] + } + out[t] = value + } + return out, nil +} + +// ParsePortRange parses and validates the specified string as a port-range (8000-9000) +func ParsePortRange(ports string) (uint64, uint64, error) { + if ports == "" { + return 0, 0, fmt.Errorf("Empty string specified for ports.") + } + if !strings.Contains(ports, "-") { + start, err := strconv.ParseUint(ports, 10, 16) + end := start + return start, end, err + } + + parts := strings.Split(ports, "-") + start, err := strconv.ParseUint(parts[0], 10, 16) + if err != nil { + return 0, 0, err + } + end, err := strconv.ParseUint(parts[1], 10, 16) + if err != nil { + return 0, 0, err + } + if end < start { + return 0, 0, fmt.Errorf("Invalid range specified for the Port: %s", ports) + } + return start, end, nil +} diff --git a/vendor/github.com/docker/go-connections/nat/sort.go b/vendor/github.com/docker/go-connections/nat/sort.go new file mode 100644 index 0000000000..ce950171e3 --- /dev/null +++ b/vendor/github.com/docker/go-connections/nat/sort.go @@ -0,0 +1,96 @@ +package nat + +import ( + "sort" + "strings" +) + +type portSorter struct { + ports []Port + by func(i, j Port) bool +} + +func (s *portSorter) Len() int { + return len(s.ports) +} + +func (s *portSorter) Swap(i, j int) { + s.ports[i], s.ports[j] = s.ports[j], s.ports[i] +} + +func (s *portSorter) Less(i, j int) bool { + ip := s.ports[i] + jp := s.ports[j] + + return s.by(ip, jp) +} + +// Sort sorts a list of ports using the provided predicate +// This function should compare `i` and `j`, returning true if `i` is +// considered to be less than `j` +func Sort(ports []Port, predicate func(i, j Port) bool) { + s := &portSorter{ports, predicate} + sort.Sort(s) +} + +type portMapEntry struct { + port Port + binding PortBinding +} + +type portMapSorter []portMapEntry + +func (s portMapSorter) Len() int { return len(s) } +func (s portMapSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// sort the port so that the order is: +// 1. port with larger specified bindings +// 2. larger port +// 3. port with tcp protocol +func (s portMapSorter) Less(i, j int) bool { + pi, pj := s[i].port, s[j].port + hpi, hpj := toInt(s[i].binding.HostPort), toInt(s[j].binding.HostPort) + return hpi > hpj || pi.Int() > pj.Int() || (pi.Int() == pj.Int() && strings.ToLower(pi.Proto()) == "tcp") +} + +// SortPortMap sorts the list of ports and their respected mapping. The ports +// will explicit HostPort will be placed first. +func SortPortMap(ports []Port, bindings PortMap) { + s := portMapSorter{} + for _, p := range ports { + if binding, ok := bindings[p]; ok { + for _, b := range binding { + s = append(s, portMapEntry{port: p, binding: b}) + } + bindings[p] = []PortBinding{} + } else { + s = append(s, portMapEntry{port: p}) + } + } + + sort.Sort(s) + var ( + i int + pm = make(map[Port]struct{}) + ) + // reorder ports + for _, entry := range s { + if _, ok := pm[entry.port]; !ok { + ports[i] = entry.port + pm[entry.port] = struct{}{} + i++ + } + // reorder bindings for this port + if _, ok := bindings[entry.port]; ok { + bindings[entry.port] = append(bindings[entry.port], entry.binding) + } + } +} + +func toInt(s string) uint64 { + i, _, err := ParsePortRange(s) + if err != nil { + i = 0 + } + return i +} diff --git a/vendor/github.com/docker/go-connections/sockets/README.md b/vendor/github.com/docker/go-connections/sockets/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/vendor/github.com/docker/go-connections/sockets/inmem_socket.go b/vendor/github.com/docker/go-connections/sockets/inmem_socket.go new file mode 100644 index 0000000000..99846ffddb --- /dev/null +++ b/vendor/github.com/docker/go-connections/sockets/inmem_socket.go @@ -0,0 +1,81 @@ +package sockets + +import ( + "errors" + "net" + "sync" +) + +var errClosed = errors.New("use of closed network connection") + +// InmemSocket implements net.Listener using in-memory only connections. +type InmemSocket struct { + chConn chan net.Conn + chClose chan struct{} + addr string + mu sync.Mutex +} + +// dummyAddr is used to satisfy net.Addr for the in-mem socket +// it is just stored as a string and returns the string for all calls +type dummyAddr string + +// NewInmemSocket creates an in-memory only net.Listener +// The addr argument can be any string, but is used to satisfy the `Addr()` part +// of the net.Listener interface +func NewInmemSocket(addr string, bufSize int) *InmemSocket { + return &InmemSocket{ + chConn: make(chan net.Conn, bufSize), + chClose: make(chan struct{}), + addr: addr, + } +} + +// Addr returns the socket's addr string to satisfy net.Listener +func (s *InmemSocket) Addr() net.Addr { + return dummyAddr(s.addr) +} + +// Accept implements the Accept method in the Listener interface; it waits for the next call and returns a generic Conn. +func (s *InmemSocket) Accept() (net.Conn, error) { + select { + case conn := <-s.chConn: + return conn, nil + case <-s.chClose: + return nil, errClosed + } +} + +// Close closes the listener. It will be unavailable for use once closed. +func (s *InmemSocket) Close() error { + s.mu.Lock() + defer s.mu.Unlock() + select { + case <-s.chClose: + default: + close(s.chClose) + } + return nil +} + +// Dial is used to establish a connection with the in-mem server +func (s *InmemSocket) Dial(network, addr string) (net.Conn, error) { + srvConn, clientConn := net.Pipe() + select { + case s.chConn <- srvConn: + case <-s.chClose: + return nil, errClosed + } + + return clientConn, nil +} + +// Network returns the addr string, satisfies net.Addr +func (a dummyAddr) Network() string { + return string(a) +} + +// String returns the string form +func (a dummyAddr) String() string { + return string(a) +} diff --git a/vendor/github.com/docker/go-connections/sockets/proxy.go b/vendor/github.com/docker/go-connections/sockets/proxy.go new file mode 100644 index 0000000000..98e9a1dc61 --- /dev/null +++ b/vendor/github.com/docker/go-connections/sockets/proxy.go @@ -0,0 +1,51 @@ +package sockets + +import ( + "net" + "net/url" + "os" + "strings" + + "golang.org/x/net/proxy" +) + +// GetProxyEnv allows access to the uppercase and the lowercase forms of +// proxy-related variables. See the Go specification for details on these +// variables. https://golang.org/pkg/net/http/ +func GetProxyEnv(key string) string { + proxyValue := os.Getenv(strings.ToUpper(key)) + if proxyValue == "" { + return os.Getenv(strings.ToLower(key)) + } + return proxyValue +} + +// DialerFromEnvironment takes in a "direct" *net.Dialer and returns a +// proxy.Dialer which will route the connections through the proxy using the +// given dialer. +func DialerFromEnvironment(direct *net.Dialer) (proxy.Dialer, error) { + allProxy := GetProxyEnv("all_proxy") + if len(allProxy) == 0 { + return direct, nil + } + + proxyURL, err := url.Parse(allProxy) + if err != nil { + return direct, err + } + + proxyFromURL, err := proxy.FromURL(proxyURL, direct) + if err != nil { + return direct, err + } + + noProxy := GetProxyEnv("no_proxy") + if len(noProxy) == 0 { + return proxyFromURL, nil + } + + perHost := proxy.NewPerHost(proxyFromURL, direct) + perHost.AddFromString(noProxy) + + return perHost, nil +} diff --git a/vendor/github.com/docker/go-connections/sockets/sockets.go b/vendor/github.com/docker/go-connections/sockets/sockets.go new file mode 100644 index 0000000000..a1d7beb4d8 --- /dev/null +++ b/vendor/github.com/docker/go-connections/sockets/sockets.go @@ -0,0 +1,38 @@ +// Package sockets provides helper functions to create and configure Unix or TCP sockets. +package sockets + +import ( + "errors" + "net" + "net/http" + "time" +) + +// Why 32? See https://github.com/docker/docker/pull/8035. +const defaultTimeout = 32 * time.Second + +// ErrProtocolNotAvailable is returned when a given transport protocol is not provided by the operating system. +var ErrProtocolNotAvailable = errors.New("protocol not available") + +// ConfigureTransport configures the specified Transport according to the +// specified proto and addr. +// If the proto is unix (using a unix socket to communicate) or npipe the +// compression is disabled. +func ConfigureTransport(tr *http.Transport, proto, addr string) error { + switch proto { + case "unix": + return configureUnixTransport(tr, proto, addr) + case "npipe": + return configureNpipeTransport(tr, proto, addr) + default: + tr.Proxy = http.ProxyFromEnvironment + dialer, err := DialerFromEnvironment(&net.Dialer{ + Timeout: defaultTimeout, + }) + if err != nil { + return err + } + tr.Dial = dialer.Dial + } + return nil +} diff --git a/vendor/github.com/docker/go-connections/sockets/sockets_unix.go b/vendor/github.com/docker/go-connections/sockets/sockets_unix.go new file mode 100644 index 0000000000..386cf0dbbd --- /dev/null +++ b/vendor/github.com/docker/go-connections/sockets/sockets_unix.go @@ -0,0 +1,35 @@ +// +build !windows + +package sockets + +import ( + "fmt" + "net" + "net/http" + "syscall" + "time" +) + +const maxUnixSocketPathSize = len(syscall.RawSockaddrUnix{}.Path) + +func configureUnixTransport(tr *http.Transport, proto, addr string) error { + if len(addr) > maxUnixSocketPathSize { + return fmt.Errorf("Unix socket path %q is too long", addr) + } + // No need for compression in local communications. + tr.DisableCompression = true + tr.Dial = func(_, _ string) (net.Conn, error) { + return net.DialTimeout(proto, addr, defaultTimeout) + } + return nil +} + +func configureNpipeTransport(tr *http.Transport, proto, addr string) error { + return ErrProtocolNotAvailable +} + +// DialPipe connects to a Windows named pipe. +// This is not supported on other OSes. +func DialPipe(_ string, _ time.Duration) (net.Conn, error) { + return nil, syscall.EAFNOSUPPORT +} diff --git a/vendor/github.com/docker/go-connections/sockets/sockets_windows.go b/vendor/github.com/docker/go-connections/sockets/sockets_windows.go new file mode 100644 index 0000000000..5c21644e1f --- /dev/null +++ b/vendor/github.com/docker/go-connections/sockets/sockets_windows.go @@ -0,0 +1,27 @@ +package sockets + +import ( + "net" + "net/http" + "time" + + "github.com/Microsoft/go-winio" +) + +func configureUnixTransport(tr *http.Transport, proto, addr string) error { + return ErrProtocolNotAvailable +} + +func configureNpipeTransport(tr *http.Transport, proto, addr string) error { + // No need for compression in local communications. + tr.DisableCompression = true + tr.Dial = func(_, _ string) (net.Conn, error) { + return DialPipe(addr, defaultTimeout) + } + return nil +} + +// DialPipe connects to a Windows named pipe. +func DialPipe(addr string, timeout time.Duration) (net.Conn, error) { + return winio.DialPipe(addr, &timeout) +} diff --git a/vendor/github.com/docker/go-connections/sockets/tcp_socket.go b/vendor/github.com/docker/go-connections/sockets/tcp_socket.go new file mode 100644 index 0000000000..53cbb6c79e --- /dev/null +++ b/vendor/github.com/docker/go-connections/sockets/tcp_socket.go @@ -0,0 +1,22 @@ +// Package sockets provides helper functions to create and configure Unix or TCP sockets. +package sockets + +import ( + "crypto/tls" + "net" +) + +// NewTCPSocket creates a TCP socket listener with the specified address and +// the specified tls configuration. If TLSConfig is set, will encapsulate the +// TCP listener inside a TLS one. +func NewTCPSocket(addr string, tlsConfig *tls.Config) (net.Listener, error) { + l, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + if tlsConfig != nil { + tlsConfig.NextProtos = []string{"http/1.1"} + l = tls.NewListener(l, tlsConfig) + } + return l, nil +} diff --git a/vendor/github.com/docker/go-connections/sockets/unix_socket.go b/vendor/github.com/docker/go-connections/sockets/unix_socket.go new file mode 100644 index 0000000000..a8b5dbb6fd --- /dev/null +++ b/vendor/github.com/docker/go-connections/sockets/unix_socket.go @@ -0,0 +1,32 @@ +// +build !windows + +package sockets + +import ( + "net" + "os" + "syscall" +) + +// NewUnixSocket creates a unix socket with the specified path and group. +func NewUnixSocket(path string, gid int) (net.Listener, error) { + if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) { + return nil, err + } + mask := syscall.Umask(0777) + defer syscall.Umask(mask) + + l, err := net.Listen("unix", path) + if err != nil { + return nil, err + } + if err := os.Chown(path, 0, gid); err != nil { + l.Close() + return nil, err + } + if err := os.Chmod(path, 0660); err != nil { + l.Close() + return nil, err + } + return l, nil +} diff --git a/vendor/github.com/docker/go-connections/tlsconfig/certpool_go17.go b/vendor/github.com/docker/go-connections/tlsconfig/certpool_go17.go new file mode 100644 index 0000000000..1ca0965e06 --- /dev/null +++ b/vendor/github.com/docker/go-connections/tlsconfig/certpool_go17.go @@ -0,0 +1,18 @@ +// +build go1.7 + +package tlsconfig + +import ( + "crypto/x509" + "runtime" +) + +// SystemCertPool returns a copy of the system cert pool, +// returns an error if failed to load or empty pool on windows. +func SystemCertPool() (*x509.CertPool, error) { + certpool, err := x509.SystemCertPool() + if err != nil && runtime.GOOS == "windows" { + return x509.NewCertPool(), nil + } + return certpool, err +} diff --git a/vendor/github.com/docker/go-connections/tlsconfig/certpool_other.go b/vendor/github.com/docker/go-connections/tlsconfig/certpool_other.go new file mode 100644 index 0000000000..1ff81c333c --- /dev/null +++ b/vendor/github.com/docker/go-connections/tlsconfig/certpool_other.go @@ -0,0 +1,13 @@ +// +build !go1.7 + +package tlsconfig + +import ( + "crypto/x509" +) + +// SystemCertPool returns an new empty cert pool, +// accessing system cert pool is supported in go 1.7 +func SystemCertPool() (*x509.CertPool, error) { + return x509.NewCertPool(), nil +} diff --git a/vendor/github.com/docker/go-connections/tlsconfig/config.go b/vendor/github.com/docker/go-connections/tlsconfig/config.go new file mode 100644 index 0000000000..0ef3fdcb46 --- /dev/null +++ b/vendor/github.com/docker/go-connections/tlsconfig/config.go @@ -0,0 +1,254 @@ +// Package tlsconfig provides primitives to retrieve secure-enough TLS configurations for both clients and servers. +// +// As a reminder from https://golang.org/pkg/crypto/tls/#Config: +// A Config structure is used to configure a TLS client or server. After one has been passed to a TLS function it must not be modified. +// A Config may be reused; the tls package will also not modify it. +package tlsconfig + +import ( + "crypto/tls" + "crypto/x509" + "encoding/pem" + "fmt" + "io/ioutil" + "os" + + "github.com/pkg/errors" +) + +// Options represents the information needed to create client and server TLS configurations. +type Options struct { + CAFile string + + // If either CertFile or KeyFile is empty, Client() will not load them + // preventing the client from authenticating to the server. + // However, Server() requires them and will error out if they are empty. + CertFile string + KeyFile string + + // client-only option + InsecureSkipVerify bool + // server-only option + ClientAuth tls.ClientAuthType + // If ExclusiveRootPools is set, then if a CA file is provided, the root pool used for TLS + // creds will include exclusively the roots in that CA file. If no CA file is provided, + // the system pool will be used. + ExclusiveRootPools bool + MinVersion uint16 + // If Passphrase is set, it will be used to decrypt a TLS private key + // if the key is encrypted + Passphrase string +} + +// Extra (server-side) accepted CBC cipher suites - will phase out in the future +var acceptedCBCCiphers = []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, +} + +// DefaultServerAcceptedCiphers should be uses by code which already has a crypto/tls +// options struct but wants to use a commonly accepted set of TLS cipher suites, with +// known weak algorithms removed. +var DefaultServerAcceptedCiphers = append(clientCipherSuites, acceptedCBCCiphers...) + +// allTLSVersions lists all the TLS versions and is used by the code that validates +// a uint16 value as a TLS version. +var allTLSVersions = map[uint16]struct{}{ + tls.VersionSSL30: {}, + tls.VersionTLS10: {}, + tls.VersionTLS11: {}, + tls.VersionTLS12: {}, +} + +// ServerDefault returns a secure-enough TLS configuration for the server TLS configuration. +func ServerDefault(ops ...func(*tls.Config)) *tls.Config { + tlsconfig := &tls.Config{ + // Avoid fallback by default to SSL protocols < TLS1.2 + MinVersion: tls.VersionTLS12, + PreferServerCipherSuites: true, + CipherSuites: DefaultServerAcceptedCiphers, + } + + for _, op := range ops { + op(tlsconfig) + } + + return tlsconfig +} + +// ClientDefault returns a secure-enough TLS configuration for the client TLS configuration. +func ClientDefault(ops ...func(*tls.Config)) *tls.Config { + tlsconfig := &tls.Config{ + // Prefer TLS1.2 as the client minimum + MinVersion: tls.VersionTLS12, + CipherSuites: clientCipherSuites, + } + + for _, op := range ops { + op(tlsconfig) + } + + return tlsconfig +} + +// certPool returns an X.509 certificate pool from `caFile`, the certificate file. +func certPool(caFile string, exclusivePool bool) (*x509.CertPool, error) { + // If we should verify the server, we need to load a trusted ca + var ( + certPool *x509.CertPool + err error + ) + if exclusivePool { + certPool = x509.NewCertPool() + } else { + certPool, err = SystemCertPool() + if err != nil { + return nil, fmt.Errorf("failed to read system certificates: %v", err) + } + } + pem, err := ioutil.ReadFile(caFile) + if err != nil { + return nil, fmt.Errorf("could not read CA certificate %q: %v", caFile, err) + } + if !certPool.AppendCertsFromPEM(pem) { + return nil, fmt.Errorf("failed to append certificates from PEM file: %q", caFile) + } + return certPool, nil +} + +// isValidMinVersion checks that the input value is a valid tls minimum version +func isValidMinVersion(version uint16) bool { + _, ok := allTLSVersions[version] + return ok +} + +// adjustMinVersion sets the MinVersion on `config`, the input configuration. +// It assumes the current MinVersion on the `config` is the lowest allowed. +func adjustMinVersion(options Options, config *tls.Config) error { + if options.MinVersion > 0 { + if !isValidMinVersion(options.MinVersion) { + return fmt.Errorf("Invalid minimum TLS version: %x", options.MinVersion) + } + if options.MinVersion < config.MinVersion { + return fmt.Errorf("Requested minimum TLS version is too low. Should be at-least: %x", config.MinVersion) + } + config.MinVersion = options.MinVersion + } + + return nil +} + +// IsErrEncryptedKey returns true if the 'err' is an error of incorrect +// password when tryin to decrypt a TLS private key +func IsErrEncryptedKey(err error) bool { + return errors.Cause(err) == x509.IncorrectPasswordError +} + +// getPrivateKey returns the private key in 'keyBytes', in PEM-encoded format. +// If the private key is encrypted, 'passphrase' is used to decrypted the +// private key. +func getPrivateKey(keyBytes []byte, passphrase string) ([]byte, error) { + // this section makes some small changes to code from notary/tuf/utils/x509.go + pemBlock, _ := pem.Decode(keyBytes) + if pemBlock == nil { + return nil, fmt.Errorf("no valid private key found") + } + + var err error + if x509.IsEncryptedPEMBlock(pemBlock) { + keyBytes, err = x509.DecryptPEMBlock(pemBlock, []byte(passphrase)) + if err != nil { + return nil, errors.Wrap(err, "private key is encrypted, but could not decrypt it") + } + keyBytes = pem.EncodeToMemory(&pem.Block{Type: pemBlock.Type, Bytes: keyBytes}) + } + + return keyBytes, nil +} + +// getCert returns a Certificate from the CertFile and KeyFile in 'options', +// if the key is encrypted, the Passphrase in 'options' will be used to +// decrypt it. +func getCert(options Options) ([]tls.Certificate, error) { + if options.CertFile == "" && options.KeyFile == "" { + return nil, nil + } + + errMessage := "Could not load X509 key pair" + + cert, err := ioutil.ReadFile(options.CertFile) + if err != nil { + return nil, errors.Wrap(err, errMessage) + } + + prKeyBytes, err := ioutil.ReadFile(options.KeyFile) + if err != nil { + return nil, errors.Wrap(err, errMessage) + } + + prKeyBytes, err = getPrivateKey(prKeyBytes, options.Passphrase) + if err != nil { + return nil, errors.Wrap(err, errMessage) + } + + tlsCert, err := tls.X509KeyPair(cert, prKeyBytes) + if err != nil { + return nil, errors.Wrap(err, errMessage) + } + + return []tls.Certificate{tlsCert}, nil +} + +// Client returns a TLS configuration meant to be used by a client. +func Client(options Options) (*tls.Config, error) { + tlsConfig := ClientDefault() + tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify + if !options.InsecureSkipVerify && options.CAFile != "" { + CAs, err := certPool(options.CAFile, options.ExclusiveRootPools) + if err != nil { + return nil, err + } + tlsConfig.RootCAs = CAs + } + + tlsCerts, err := getCert(options) + if err != nil { + return nil, err + } + tlsConfig.Certificates = tlsCerts + + if err := adjustMinVersion(options, tlsConfig); err != nil { + return nil, err + } + + return tlsConfig, nil +} + +// Server returns a TLS configuration meant to be used by a server. +func Server(options Options) (*tls.Config, error) { + tlsConfig := ServerDefault() + tlsConfig.ClientAuth = options.ClientAuth + tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile) + if err != nil { + if os.IsNotExist(err) { + return nil, fmt.Errorf("Could not load X509 key pair (cert: %q, key: %q): %v", options.CertFile, options.KeyFile, err) + } + return nil, fmt.Errorf("Error reading X509 key pair (cert: %q, key: %q): %v. Make sure the key is not encrypted.", options.CertFile, options.KeyFile, err) + } + tlsConfig.Certificates = []tls.Certificate{tlsCert} + if options.ClientAuth >= tls.VerifyClientCertIfGiven && options.CAFile != "" { + CAs, err := certPool(options.CAFile, options.ExclusiveRootPools) + if err != nil { + return nil, err + } + tlsConfig.ClientCAs = CAs + } + + if err := adjustMinVersion(options, tlsConfig); err != nil { + return nil, err + } + + return tlsConfig, nil +} diff --git a/vendor/github.com/docker/go-connections/tlsconfig/config_client_ciphers.go b/vendor/github.com/docker/go-connections/tlsconfig/config_client_ciphers.go new file mode 100644 index 0000000000..6b4c6a7c0d --- /dev/null +++ b/vendor/github.com/docker/go-connections/tlsconfig/config_client_ciphers.go @@ -0,0 +1,17 @@ +// +build go1.5 + +// Package tlsconfig provides primitives to retrieve secure-enough TLS configurations for both clients and servers. +// +package tlsconfig + +import ( + "crypto/tls" +) + +// Client TLS cipher suites (dropping CBC ciphers for client preferred suite set) +var clientCipherSuites = []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, +} diff --git a/vendor/github.com/docker/go-connections/tlsconfig/config_legacy_client_ciphers.go b/vendor/github.com/docker/go-connections/tlsconfig/config_legacy_client_ciphers.go new file mode 100644 index 0000000000..ee22df47cb --- /dev/null +++ b/vendor/github.com/docker/go-connections/tlsconfig/config_legacy_client_ciphers.go @@ -0,0 +1,15 @@ +// +build !go1.5 + +// Package tlsconfig provides primitives to retrieve secure-enough TLS configurations for both clients and servers. +// +package tlsconfig + +import ( + "crypto/tls" +) + +// Client TLS cipher suites (dropping CBC ciphers for client preferred suite set) +var clientCipherSuites = []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, +} diff --git a/vendor/github.com/docker/go-units/CONTRIBUTING.md b/vendor/github.com/docker/go-units/CONTRIBUTING.md new file mode 100644 index 0000000000..9ea86d784e --- /dev/null +++ b/vendor/github.com/docker/go-units/CONTRIBUTING.md @@ -0,0 +1,67 @@ +# Contributing to go-units + +Want to hack on go-units? Awesome! Here are instructions to get you started. + +go-units is a part of the [Docker](https://www.docker.com) project, and follows +the same rules and principles. If you're already familiar with the way +Docker does things, you'll feel right at home. + +Otherwise, go read Docker's +[contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), +[issue triaging](https://github.com/docker/docker/blob/master/project/ISSUE-TRIAGE.md), +[review process](https://github.com/docker/docker/blob/master/project/REVIEWING.md) and +[branches and tags](https://github.com/docker/docker/blob/master/project/BRANCHES-AND-TAGS.md). + +### Sign your work + +The sign-off is a simple line at the end of the explanation for the patch. Your +signature certifies that you wrote the patch or otherwise have the right to pass +it on as an open-source patch. The rules are pretty simple: if you can certify +the below (from [developercertificate.org](http://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +Then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +Use your real name (sorry, no pseudonyms or anonymous contributions.) + +If you set your `user.name` and `user.email` git configs, you can sign your +commit automatically with `git commit -s`. diff --git a/vendor/github.com/docker/go-units/LICENSE b/vendor/github.com/docker/go-units/LICENSE new file mode 100644 index 0000000000..b55b37bc31 --- /dev/null +++ b/vendor/github.com/docker/go-units/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright 2015 Docker, Inc. + + 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 + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/docker/go-units/MAINTAINERS b/vendor/github.com/docker/go-units/MAINTAINERS new file mode 100644 index 0000000000..4aac7c7411 --- /dev/null +++ b/vendor/github.com/docker/go-units/MAINTAINERS @@ -0,0 +1,46 @@ +# go-units maintainers file +# +# This file describes who runs the docker/go-units project and how. +# This is a living document - if you see something out of date or missing, speak up! +# +# It is structured to be consumable by both humans and programs. +# To extract its contents programmatically, use any TOML-compliant parser. +# +# This file is compiled into the MAINTAINERS file in docker/opensource. +# +[Org] + [Org."Core maintainers"] + people = [ + "akihirosuda", + "dnephin", + "thajeztah", + "vdemeester", + ] + +[people] + +# A reference list of all people associated with the project. +# All other sections should refer to people by their canonical key +# in the people section. + + # ADD YOURSELF HERE IN ALPHABETICAL ORDER + + [people.akihirosuda] + Name = "Akihiro Suda" + Email = "akihiro.suda.cz@hco.ntt.co.jp" + GitHub = "AkihiroSuda" + + [people.dnephin] + Name = "Daniel Nephin" + Email = "dnephin@gmail.com" + GitHub = "dnephin" + + [people.thajeztah] + Name = "Sebastiaan van Stijn" + Email = "github@gone.nl" + GitHub = "thaJeztah" + + [people.vdemeester] + Name = "Vincent Demeester" + Email = "vincent@sbr.pm" + GitHub = "vdemeester" \ No newline at end of file diff --git a/vendor/github.com/docker/go-units/README.md b/vendor/github.com/docker/go-units/README.md new file mode 100644 index 0000000000..4f70a4e134 --- /dev/null +++ b/vendor/github.com/docker/go-units/README.md @@ -0,0 +1,16 @@ +[![GoDoc](https://godoc.org/github.com/docker/go-units?status.svg)](https://godoc.org/github.com/docker/go-units) + +# Introduction + +go-units is a library to transform human friendly measurements into machine friendly values. + +## Usage + +See the [docs in godoc](https://godoc.org/github.com/docker/go-units) for examples and documentation. + +## Copyright and license + +Copyright © 2015 Docker, Inc. + +go-units is licensed under the Apache License, Version 2.0. +See [LICENSE](LICENSE) for the full text of the license. diff --git a/vendor/github.com/docker/go-units/circle.yml b/vendor/github.com/docker/go-units/circle.yml new file mode 100644 index 0000000000..af9d605529 --- /dev/null +++ b/vendor/github.com/docker/go-units/circle.yml @@ -0,0 +1,11 @@ +dependencies: + post: + # install golint + - go get golang.org/x/lint/golint + +test: + pre: + # run analysis before tests + - go vet ./... + - test -z "$(golint ./... | tee /dev/stderr)" + - test -z "$(gofmt -s -l . | tee /dev/stderr)" diff --git a/vendor/github.com/docker/go-units/duration.go b/vendor/github.com/docker/go-units/duration.go new file mode 100644 index 0000000000..48dd8744d4 --- /dev/null +++ b/vendor/github.com/docker/go-units/duration.go @@ -0,0 +1,35 @@ +// Package units provides helper function to parse and print size and time units +// in human-readable format. +package units + +import ( + "fmt" + "time" +) + +// HumanDuration returns a human-readable approximation of a duration +// (eg. "About a minute", "4 hours ago", etc.). +func HumanDuration(d time.Duration) string { + if seconds := int(d.Seconds()); seconds < 1 { + return "Less than a second" + } else if seconds == 1 { + return "1 second" + } else if seconds < 60 { + return fmt.Sprintf("%d seconds", seconds) + } else if minutes := int(d.Minutes()); minutes == 1 { + return "About a minute" + } else if minutes < 60 { + return fmt.Sprintf("%d minutes", minutes) + } else if hours := int(d.Hours() + 0.5); hours == 1 { + return "About an hour" + } else if hours < 48 { + return fmt.Sprintf("%d hours", hours) + } else if hours < 24*7*2 { + return fmt.Sprintf("%d days", hours/24) + } else if hours < 24*30*2 { + return fmt.Sprintf("%d weeks", hours/24/7) + } else if hours < 24*365*2 { + return fmt.Sprintf("%d months", hours/24/30) + } + return fmt.Sprintf("%d years", int(d.Hours())/24/365) +} diff --git a/vendor/github.com/docker/go-units/size.go b/vendor/github.com/docker/go-units/size.go new file mode 100644 index 0000000000..85f6ab0715 --- /dev/null +++ b/vendor/github.com/docker/go-units/size.go @@ -0,0 +1,108 @@ +package units + +import ( + "fmt" + "regexp" + "strconv" + "strings" +) + +// See: http://en.wikipedia.org/wiki/Binary_prefix +const ( + // Decimal + + KB = 1000 + MB = 1000 * KB + GB = 1000 * MB + TB = 1000 * GB + PB = 1000 * TB + + // Binary + + KiB = 1024 + MiB = 1024 * KiB + GiB = 1024 * MiB + TiB = 1024 * GiB + PiB = 1024 * TiB +) + +type unitMap map[string]int64 + +var ( + decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB} + binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB} + sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[iI]?[bB]?$`) +) + +var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} +var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} + +func getSizeAndUnit(size float64, base float64, _map []string) (float64, string) { + i := 0 + unitsLimit := len(_map) - 1 + for size >= base && i < unitsLimit { + size = size / base + i++ + } + return size, _map[i] +} + +// CustomSize returns a human-readable approximation of a size +// using custom format. +func CustomSize(format string, size float64, base float64, _map []string) string { + size, unit := getSizeAndUnit(size, base, _map) + return fmt.Sprintf(format, size, unit) +} + +// HumanSizeWithPrecision allows the size to be in any precision, +// instead of 4 digit precision used in units.HumanSize. +func HumanSizeWithPrecision(size float64, precision int) string { + size, unit := getSizeAndUnit(size, 1000.0, decimapAbbrs) + return fmt.Sprintf("%.*g%s", precision, size, unit) +} + +// HumanSize returns a human-readable approximation of a size +// capped at 4 valid numbers (eg. "2.746 MB", "796 KB"). +func HumanSize(size float64) string { + return HumanSizeWithPrecision(size, 4) +} + +// BytesSize returns a human-readable size in bytes, kibibytes, +// mebibytes, gibibytes, or tebibytes (eg. "44kiB", "17MiB"). +func BytesSize(size float64) string { + return CustomSize("%.4g%s", size, 1024.0, binaryAbbrs) +} + +// FromHumanSize returns an integer from a human-readable specification of a +// size using SI standard (eg. "44kB", "17MB"). +func FromHumanSize(size string) (int64, error) { + return parseSize(size, decimalMap) +} + +// RAMInBytes parses a human-readable string representing an amount of RAM +// in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and +// returns the number of bytes, or -1 if the string is unparseable. +// Units are case-insensitive, and the 'b' suffix is optional. +func RAMInBytes(size string) (int64, error) { + return parseSize(size, binaryMap) +} + +// Parses the human-readable size string into the amount it represents. +func parseSize(sizeStr string, uMap unitMap) (int64, error) { + matches := sizeRegex.FindStringSubmatch(sizeStr) + if len(matches) != 4 { + return -1, fmt.Errorf("invalid size: '%s'", sizeStr) + } + + size, err := strconv.ParseFloat(matches[1], 64) + if err != nil { + return -1, err + } + + unitPrefix := strings.ToLower(matches[3]) + if mul, ok := uMap[unitPrefix]; ok { + size *= float64(mul) + } + + return int64(size), nil +} diff --git a/vendor/github.com/docker/go-units/ulimit.go b/vendor/github.com/docker/go-units/ulimit.go new file mode 100644 index 0000000000..fca0400cc8 --- /dev/null +++ b/vendor/github.com/docker/go-units/ulimit.go @@ -0,0 +1,123 @@ +package units + +import ( + "fmt" + "strconv" + "strings" +) + +// Ulimit is a human friendly version of Rlimit. +type Ulimit struct { + Name string + Hard int64 + Soft int64 +} + +// Rlimit specifies the resource limits, such as max open files. +type Rlimit struct { + Type int `json:"type,omitempty"` + Hard uint64 `json:"hard,omitempty"` + Soft uint64 `json:"soft,omitempty"` +} + +const ( + // magic numbers for making the syscall + // some of these are defined in the syscall package, but not all. + // Also since Windows client doesn't get access to the syscall package, need to + // define these here + rlimitAs = 9 + rlimitCore = 4 + rlimitCPU = 0 + rlimitData = 2 + rlimitFsize = 1 + rlimitLocks = 10 + rlimitMemlock = 8 + rlimitMsgqueue = 12 + rlimitNice = 13 + rlimitNofile = 7 + rlimitNproc = 6 + rlimitRss = 5 + rlimitRtprio = 14 + rlimitRttime = 15 + rlimitSigpending = 11 + rlimitStack = 3 +) + +var ulimitNameMapping = map[string]int{ + //"as": rlimitAs, // Disabled since this doesn't seem usable with the way Docker inits a container. + "core": rlimitCore, + "cpu": rlimitCPU, + "data": rlimitData, + "fsize": rlimitFsize, + "locks": rlimitLocks, + "memlock": rlimitMemlock, + "msgqueue": rlimitMsgqueue, + "nice": rlimitNice, + "nofile": rlimitNofile, + "nproc": rlimitNproc, + "rss": rlimitRss, + "rtprio": rlimitRtprio, + "rttime": rlimitRttime, + "sigpending": rlimitSigpending, + "stack": rlimitStack, +} + +// ParseUlimit parses and returns a Ulimit from the specified string. +func ParseUlimit(val string) (*Ulimit, error) { + parts := strings.SplitN(val, "=", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("invalid ulimit argument: %s", val) + } + + if _, exists := ulimitNameMapping[parts[0]]; !exists { + return nil, fmt.Errorf("invalid ulimit type: %s", parts[0]) + } + + var ( + soft int64 + hard = &soft // default to soft in case no hard was set + temp int64 + err error + ) + switch limitVals := strings.Split(parts[1], ":"); len(limitVals) { + case 2: + temp, err = strconv.ParseInt(limitVals[1], 10, 64) + if err != nil { + return nil, err + } + hard = &temp + fallthrough + case 1: + soft, err = strconv.ParseInt(limitVals[0], 10, 64) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("too many limit value arguments - %s, can only have up to two, `soft[:hard]`", parts[1]) + } + + if *hard != -1 { + if soft == -1 { + return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: soft: -1 (unlimited), hard: %d", *hard) + } + if soft > *hard { + return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, *hard) + } + } + + return &Ulimit{Name: parts[0], Soft: soft, Hard: *hard}, nil +} + +// GetRlimit returns the RLimit corresponding to Ulimit. +func (u *Ulimit) GetRlimit() (*Rlimit, error) { + t, exists := ulimitNameMapping[u.Name] + if !exists { + return nil, fmt.Errorf("invalid ulimit name %s", u.Name) + } + + return &Rlimit{Type: t, Soft: uint64(u.Soft), Hard: uint64(u.Hard)}, nil +} + +func (u *Ulimit) String() string { + return fmt.Sprintf("%s=%d:%d", u.Name, u.Soft, u.Hard) +} diff --git a/vendor/github.com/dustin/go-humanize/.travis.yml b/vendor/github.com/dustin/go-humanize/.travis.yml new file mode 100644 index 0000000000..ba95cdd15c --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/.travis.yml @@ -0,0 +1,21 @@ +sudo: false +language: go +go: + - 1.3.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - master +matrix: + allow_failures: + - go: master + fast_finish: true +install: + - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d -s .) + - go tool vet . + - go test -v -race ./... diff --git a/vendor/github.com/dustin/go-humanize/LICENSE b/vendor/github.com/dustin/go-humanize/LICENSE new file mode 100644 index 0000000000..8d9a94a906 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2005-2008 Dustin Sallings + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + diff --git a/vendor/github.com/dustin/go-humanize/README.markdown b/vendor/github.com/dustin/go-humanize/README.markdown new file mode 100644 index 0000000000..91b4ae5646 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/README.markdown @@ -0,0 +1,124 @@ +# Humane Units [![Build Status](https://travis-ci.org/dustin/go-humanize.svg?branch=master)](https://travis-ci.org/dustin/go-humanize) [![GoDoc](https://godoc.org/github.com/dustin/go-humanize?status.svg)](https://godoc.org/github.com/dustin/go-humanize) + +Just a few functions for helping humanize times and sizes. + +`go get` it as `github.com/dustin/go-humanize`, import it as +`"github.com/dustin/go-humanize"`, use it as `humanize`. + +See [godoc](https://godoc.org/github.com/dustin/go-humanize) for +complete documentation. + +## Sizes + +This lets you take numbers like `82854982` and convert them to useful +strings like, `83 MB` or `79 MiB` (whichever you prefer). + +Example: + +```go +fmt.Printf("That file is %s.", humanize.Bytes(82854982)) // That file is 83 MB. +``` + +## Times + +This lets you take a `time.Time` and spit it out in relative terms. +For example, `12 seconds ago` or `3 days from now`. + +Example: + +```go +fmt.Printf("This was touched %s.", humanize.Time(someTimeInstance)) // This was touched 7 hours ago. +``` + +Thanks to Kyle Lemons for the time implementation from an IRC +conversation one day. It's pretty neat. + +## Ordinals + +From a [mailing list discussion][odisc] where a user wanted to be able +to label ordinals. + + 0 -> 0th + 1 -> 1st + 2 -> 2nd + 3 -> 3rd + 4 -> 4th + [...] + +Example: + +```go +fmt.Printf("You're my %s best friend.", humanize.Ordinal(193)) // You are my 193rd best friend. +``` + +## Commas + +Want to shove commas into numbers? Be my guest. + + 0 -> 0 + 100 -> 100 + 1000 -> 1,000 + 1000000000 -> 1,000,000,000 + -100000 -> -100,000 + +Example: + +```go +fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491. +``` + +## Ftoa + +Nicer float64 formatter that removes trailing zeros. + +```go +fmt.Printf("%f", 2.24) // 2.240000 +fmt.Printf("%s", humanize.Ftoa(2.24)) // 2.24 +fmt.Printf("%f", 2.0) // 2.000000 +fmt.Printf("%s", humanize.Ftoa(2.0)) // 2 +``` + +## SI notation + +Format numbers with [SI notation][sinotation]. + +Example: + +```go +humanize.SI(0.00000000223, "M") // 2.23 nM +``` + +## English-specific functions + +The following functions are in the `humanize/english` subpackage. + +### Plurals + +Simple English pluralization + +```go +english.PluralWord(1, "object", "") // object +english.PluralWord(42, "object", "") // objects +english.PluralWord(2, "bus", "") // buses +english.PluralWord(99, "locus", "loci") // loci + +english.Plural(1, "object", "") // 1 object +english.Plural(42, "object", "") // 42 objects +english.Plural(2, "bus", "") // 2 buses +english.Plural(99, "locus", "loci") // 99 loci +``` + +### Word series + +Format comma-separated words lists with conjuctions: + +```go +english.WordSeries([]string{"foo"}, "and") // foo +english.WordSeries([]string{"foo", "bar"}, "and") // foo and bar +english.WordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar and baz + +english.OxfordWordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar, and baz +``` + +[odisc]: https://groups.google.com/d/topic/golang-nuts/l8NhI74jl-4/discussion +[sinotation]: http://en.wikipedia.org/wiki/Metric_prefix diff --git a/vendor/github.com/dustin/go-humanize/big.go b/vendor/github.com/dustin/go-humanize/big.go new file mode 100644 index 0000000000..f49dc337dc --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/big.go @@ -0,0 +1,31 @@ +package humanize + +import ( + "math/big" +) + +// order of magnitude (to a max order) +func oomm(n, b *big.Int, maxmag int) (float64, int) { + mag := 0 + m := &big.Int{} + for n.Cmp(b) >= 0 { + n.DivMod(n, b, m) + mag++ + if mag == maxmag && maxmag >= 0 { + break + } + } + return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag +} + +// total order of magnitude +// (same as above, but with no upper limit) +func oom(n, b *big.Int) (float64, int) { + mag := 0 + m := &big.Int{} + for n.Cmp(b) >= 0 { + n.DivMod(n, b, m) + mag++ + } + return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag +} diff --git a/vendor/github.com/dustin/go-humanize/bigbytes.go b/vendor/github.com/dustin/go-humanize/bigbytes.go new file mode 100644 index 0000000000..1a2bf61723 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/bigbytes.go @@ -0,0 +1,173 @@ +package humanize + +import ( + "fmt" + "math/big" + "strings" + "unicode" +) + +var ( + bigIECExp = big.NewInt(1024) + + // BigByte is one byte in bit.Ints + BigByte = big.NewInt(1) + // BigKiByte is 1,024 bytes in bit.Ints + BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp) + // BigMiByte is 1,024 k bytes in bit.Ints + BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp) + // BigGiByte is 1,024 m bytes in bit.Ints + BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp) + // BigTiByte is 1,024 g bytes in bit.Ints + BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp) + // BigPiByte is 1,024 t bytes in bit.Ints + BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp) + // BigEiByte is 1,024 p bytes in bit.Ints + BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp) + // BigZiByte is 1,024 e bytes in bit.Ints + BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp) + // BigYiByte is 1,024 z bytes in bit.Ints + BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp) +) + +var ( + bigSIExp = big.NewInt(1000) + + // BigSIByte is one SI byte in big.Ints + BigSIByte = big.NewInt(1) + // BigKByte is 1,000 SI bytes in big.Ints + BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp) + // BigMByte is 1,000 SI k bytes in big.Ints + BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp) + // BigGByte is 1,000 SI m bytes in big.Ints + BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp) + // BigTByte is 1,000 SI g bytes in big.Ints + BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp) + // BigPByte is 1,000 SI t bytes in big.Ints + BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp) + // BigEByte is 1,000 SI p bytes in big.Ints + BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp) + // BigZByte is 1,000 SI e bytes in big.Ints + BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp) + // BigYByte is 1,000 SI z bytes in big.Ints + BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp) +) + +var bigBytesSizeTable = map[string]*big.Int{ + "b": BigByte, + "kib": BigKiByte, + "kb": BigKByte, + "mib": BigMiByte, + "mb": BigMByte, + "gib": BigGiByte, + "gb": BigGByte, + "tib": BigTiByte, + "tb": BigTByte, + "pib": BigPiByte, + "pb": BigPByte, + "eib": BigEiByte, + "eb": BigEByte, + "zib": BigZiByte, + "zb": BigZByte, + "yib": BigYiByte, + "yb": BigYByte, + // Without suffix + "": BigByte, + "ki": BigKiByte, + "k": BigKByte, + "mi": BigMiByte, + "m": BigMByte, + "gi": BigGiByte, + "g": BigGByte, + "ti": BigTiByte, + "t": BigTByte, + "pi": BigPiByte, + "p": BigPByte, + "ei": BigEiByte, + "e": BigEByte, + "z": BigZByte, + "zi": BigZiByte, + "y": BigYByte, + "yi": BigYiByte, +} + +var ten = big.NewInt(10) + +func humanateBigBytes(s, base *big.Int, sizes []string) string { + if s.Cmp(ten) < 0 { + return fmt.Sprintf("%d B", s) + } + c := (&big.Int{}).Set(s) + val, mag := oomm(c, base, len(sizes)-1) + suffix := sizes[mag] + f := "%.0f %s" + if val < 10 { + f = "%.1f %s" + } + + return fmt.Sprintf(f, val, suffix) + +} + +// BigBytes produces a human readable representation of an SI size. +// +// See also: ParseBigBytes. +// +// BigBytes(82854982) -> 83 MB +func BigBytes(s *big.Int) string { + sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} + return humanateBigBytes(s, bigSIExp, sizes) +} + +// BigIBytes produces a human readable representation of an IEC size. +// +// See also: ParseBigBytes. +// +// BigIBytes(82854982) -> 79 MiB +func BigIBytes(s *big.Int) string { + sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} + return humanateBigBytes(s, bigIECExp, sizes) +} + +// ParseBigBytes parses a string representation of bytes into the number +// of bytes it represents. +// +// See also: BigBytes, BigIBytes. +// +// ParseBigBytes("42 MB") -> 42000000, nil +// ParseBigBytes("42 mib") -> 44040192, nil +func ParseBigBytes(s string) (*big.Int, error) { + lastDigit := 0 + hasComma := false + for _, r := range s { + if !(unicode.IsDigit(r) || r == '.' || r == ',') { + break + } + if r == ',' { + hasComma = true + } + lastDigit++ + } + + num := s[:lastDigit] + if hasComma { + num = strings.Replace(num, ",", "", -1) + } + + val := &big.Rat{} + _, err := fmt.Sscanf(num, "%f", val) + if err != nil { + return nil, err + } + + extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) + if m, ok := bigBytesSizeTable[extra]; ok { + mv := (&big.Rat{}).SetInt(m) + val.Mul(val, mv) + rv := &big.Int{} + rv.Div(val.Num(), val.Denom()) + return rv, nil + } + + return nil, fmt.Errorf("unhandled size name: %v", extra) +} diff --git a/vendor/github.com/dustin/go-humanize/bytes.go b/vendor/github.com/dustin/go-humanize/bytes.go new file mode 100644 index 0000000000..0b498f4885 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/bytes.go @@ -0,0 +1,143 @@ +package humanize + +import ( + "fmt" + "math" + "strconv" + "strings" + "unicode" +) + +// IEC Sizes. +// kibis of bits +const ( + Byte = 1 << (iota * 10) + KiByte + MiByte + GiByte + TiByte + PiByte + EiByte +) + +// SI Sizes. +const ( + IByte = 1 + KByte = IByte * 1000 + MByte = KByte * 1000 + GByte = MByte * 1000 + TByte = GByte * 1000 + PByte = TByte * 1000 + EByte = PByte * 1000 +) + +var bytesSizeTable = map[string]uint64{ + "b": Byte, + "kib": KiByte, + "kb": KByte, + "mib": MiByte, + "mb": MByte, + "gib": GiByte, + "gb": GByte, + "tib": TiByte, + "tb": TByte, + "pib": PiByte, + "pb": PByte, + "eib": EiByte, + "eb": EByte, + // Without suffix + "": Byte, + "ki": KiByte, + "k": KByte, + "mi": MiByte, + "m": MByte, + "gi": GiByte, + "g": GByte, + "ti": TiByte, + "t": TByte, + "pi": PiByte, + "p": PByte, + "ei": EiByte, + "e": EByte, +} + +func logn(n, b float64) float64 { + return math.Log(n) / math.Log(b) +} + +func humanateBytes(s uint64, base float64, sizes []string) string { + if s < 10 { + return fmt.Sprintf("%d B", s) + } + e := math.Floor(logn(float64(s), base)) + suffix := sizes[int(e)] + val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10 + f := "%.0f %s" + if val < 10 { + f = "%.1f %s" + } + + return fmt.Sprintf(f, val, suffix) +} + +// Bytes produces a human readable representation of an SI size. +// +// See also: ParseBytes. +// +// Bytes(82854982) -> 83 MB +func Bytes(s uint64) string { + sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"} + return humanateBytes(s, 1000, sizes) +} + +// IBytes produces a human readable representation of an IEC size. +// +// See also: ParseBytes. +// +// IBytes(82854982) -> 79 MiB +func IBytes(s uint64) string { + sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"} + return humanateBytes(s, 1024, sizes) +} + +// ParseBytes parses a string representation of bytes into the number +// of bytes it represents. +// +// See Also: Bytes, IBytes. +// +// ParseBytes("42 MB") -> 42000000, nil +// ParseBytes("42 mib") -> 44040192, nil +func ParseBytes(s string) (uint64, error) { + lastDigit := 0 + hasComma := false + for _, r := range s { + if !(unicode.IsDigit(r) || r == '.' || r == ',') { + break + } + if r == ',' { + hasComma = true + } + lastDigit++ + } + + num := s[:lastDigit] + if hasComma { + num = strings.Replace(num, ",", "", -1) + } + + f, err := strconv.ParseFloat(num, 64) + if err != nil { + return 0, err + } + + extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) + if m, ok := bytesSizeTable[extra]; ok { + f *= float64(m) + if f >= math.MaxUint64 { + return 0, fmt.Errorf("too large: %v", s) + } + return uint64(f), nil + } + + return 0, fmt.Errorf("unhandled size name: %v", extra) +} diff --git a/vendor/github.com/dustin/go-humanize/comma.go b/vendor/github.com/dustin/go-humanize/comma.go new file mode 100644 index 0000000000..520ae3e57d --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/comma.go @@ -0,0 +1,116 @@ +package humanize + +import ( + "bytes" + "math" + "math/big" + "strconv" + "strings" +) + +// Comma produces a string form of the given number in base 10 with +// commas after every three orders of magnitude. +// +// e.g. Comma(834142) -> 834,142 +func Comma(v int64) string { + sign := "" + + // Min int64 can't be negated to a usable value, so it has to be special cased. + if v == math.MinInt64 { + return "-9,223,372,036,854,775,808" + } + + if v < 0 { + sign = "-" + v = 0 - v + } + + parts := []string{"", "", "", "", "", "", ""} + j := len(parts) - 1 + + for v > 999 { + parts[j] = strconv.FormatInt(v%1000, 10) + switch len(parts[j]) { + case 2: + parts[j] = "0" + parts[j] + case 1: + parts[j] = "00" + parts[j] + } + v = v / 1000 + j-- + } + parts[j] = strconv.Itoa(int(v)) + return sign + strings.Join(parts[j:], ",") +} + +// Commaf produces a string form of the given number in base 10 with +// commas after every three orders of magnitude. +// +// e.g. Commaf(834142.32) -> 834,142.32 +func Commaf(v float64) string { + buf := &bytes.Buffer{} + if v < 0 { + buf.Write([]byte{'-'}) + v = 0 - v + } + + comma := []byte{','} + + parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".") + pos := 0 + if len(parts[0])%3 != 0 { + pos += len(parts[0]) % 3 + buf.WriteString(parts[0][:pos]) + buf.Write(comma) + } + for ; pos < len(parts[0]); pos += 3 { + buf.WriteString(parts[0][pos : pos+3]) + buf.Write(comma) + } + buf.Truncate(buf.Len() - 1) + + if len(parts) > 1 { + buf.Write([]byte{'.'}) + buf.WriteString(parts[1]) + } + return buf.String() +} + +// CommafWithDigits works like the Commaf but limits the resulting +// string to the given number of decimal places. +// +// e.g. CommafWithDigits(834142.32, 1) -> 834,142.3 +func CommafWithDigits(f float64, decimals int) string { + return stripTrailingDigits(Commaf(f), decimals) +} + +// BigComma produces a string form of the given big.Int in base 10 +// with commas after every three orders of magnitude. +func BigComma(b *big.Int) string { + sign := "" + if b.Sign() < 0 { + sign = "-" + b.Abs(b) + } + + athousand := big.NewInt(1000) + c := (&big.Int{}).Set(b) + _, m := oom(c, athousand) + parts := make([]string, m+1) + j := len(parts) - 1 + + mod := &big.Int{} + for b.Cmp(athousand) >= 0 { + b.DivMod(b, athousand, mod) + parts[j] = strconv.FormatInt(mod.Int64(), 10) + switch len(parts[j]) { + case 2: + parts[j] = "0" + parts[j] + case 1: + parts[j] = "00" + parts[j] + } + j-- + } + parts[j] = strconv.Itoa(int(b.Int64())) + return sign + strings.Join(parts[j:], ",") +} diff --git a/vendor/github.com/dustin/go-humanize/commaf.go b/vendor/github.com/dustin/go-humanize/commaf.go new file mode 100644 index 0000000000..620690dec7 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/commaf.go @@ -0,0 +1,40 @@ +// +build go1.6 + +package humanize + +import ( + "bytes" + "math/big" + "strings" +) + +// BigCommaf produces a string form of the given big.Float in base 10 +// with commas after every three orders of magnitude. +func BigCommaf(v *big.Float) string { + buf := &bytes.Buffer{} + if v.Sign() < 0 { + buf.Write([]byte{'-'}) + v.Abs(v) + } + + comma := []byte{','} + + parts := strings.Split(v.Text('f', -1), ".") + pos := 0 + if len(parts[0])%3 != 0 { + pos += len(parts[0]) % 3 + buf.WriteString(parts[0][:pos]) + buf.Write(comma) + } + for ; pos < len(parts[0]); pos += 3 { + buf.WriteString(parts[0][pos : pos+3]) + buf.Write(comma) + } + buf.Truncate(buf.Len() - 1) + + if len(parts) > 1 { + buf.Write([]byte{'.'}) + buf.WriteString(parts[1]) + } + return buf.String() +} diff --git a/vendor/github.com/dustin/go-humanize/ftoa.go b/vendor/github.com/dustin/go-humanize/ftoa.go new file mode 100644 index 0000000000..1c62b640d4 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/ftoa.go @@ -0,0 +1,46 @@ +package humanize + +import ( + "strconv" + "strings" +) + +func stripTrailingZeros(s string) string { + offset := len(s) - 1 + for offset > 0 { + if s[offset] == '.' { + offset-- + break + } + if s[offset] != '0' { + break + } + offset-- + } + return s[:offset+1] +} + +func stripTrailingDigits(s string, digits int) string { + if i := strings.Index(s, "."); i >= 0 { + if digits <= 0 { + return s[:i] + } + i++ + if i+digits >= len(s) { + return s + } + return s[:i+digits] + } + return s +} + +// Ftoa converts a float to a string with no trailing zeros. +func Ftoa(num float64) string { + return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64)) +} + +// FtoaWithDigits converts a float to a string but limits the resulting string +// to the given number of decimal places, and no trailing zeros. +func FtoaWithDigits(num float64, digits int) string { + return stripTrailingZeros(stripTrailingDigits(strconv.FormatFloat(num, 'f', 6, 64), digits)) +} diff --git a/vendor/github.com/dustin/go-humanize/humanize.go b/vendor/github.com/dustin/go-humanize/humanize.go new file mode 100644 index 0000000000..a2c2da31ef --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/humanize.go @@ -0,0 +1,8 @@ +/* +Package humanize converts boring ugly numbers to human-friendly strings and back. + +Durations can be turned into strings such as "3 days ago", numbers +representing sizes like 82854982 into useful strings like, "83 MB" or +"79 MiB" (whichever you prefer). +*/ +package humanize diff --git a/vendor/github.com/dustin/go-humanize/number.go b/vendor/github.com/dustin/go-humanize/number.go new file mode 100644 index 0000000000..dec6186599 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/number.go @@ -0,0 +1,192 @@ +package humanize + +/* +Slightly adapted from the source to fit go-humanize. + +Author: https://github.com/gorhill +Source: https://gist.github.com/gorhill/5285193 + +*/ + +import ( + "math" + "strconv" +) + +var ( + renderFloatPrecisionMultipliers = [...]float64{ + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + } + + renderFloatPrecisionRounders = [...]float64{ + 0.5, + 0.05, + 0.005, + 0.0005, + 0.00005, + 0.000005, + 0.0000005, + 0.00000005, + 0.000000005, + 0.0000000005, + } +) + +// FormatFloat produces a formatted number as string based on the following user-specified criteria: +// * thousands separator +// * decimal separator +// * decimal precision +// +// Usage: s := RenderFloat(format, n) +// The format parameter tells how to render the number n. +// +// See examples: http://play.golang.org/p/LXc1Ddm1lJ +// +// Examples of format strings, given n = 12345.6789: +// "#,###.##" => "12,345.67" +// "#,###." => "12,345" +// "#,###" => "12345,678" +// "#\u202F###,##" => "12 345,68" +// "#.###,###### => 12.345,678900 +// "" (aka default format) => 12,345.67 +// +// The highest precision allowed is 9 digits after the decimal symbol. +// There is also a version for integer number, FormatInteger(), +// which is convenient for calls within template. +func FormatFloat(format string, n float64) string { + // Special cases: + // NaN = "NaN" + // +Inf = "+Infinity" + // -Inf = "-Infinity" + if math.IsNaN(n) { + return "NaN" + } + if n > math.MaxFloat64 { + return "Infinity" + } + if n < -math.MaxFloat64 { + return "-Infinity" + } + + // default format + precision := 2 + decimalStr := "." + thousandStr := "," + positiveStr := "" + negativeStr := "-" + + if len(format) > 0 { + format := []rune(format) + + // If there is an explicit format directive, + // then default values are these: + precision = 9 + thousandStr = "" + + // collect indices of meaningful formatting directives + formatIndx := []int{} + for i, char := range format { + if char != '#' && char != '0' { + formatIndx = append(formatIndx, i) + } + } + + if len(formatIndx) > 0 { + // Directive at index 0: + // Must be a '+' + // Raise an error if not the case + // index: 0123456789 + // +0.000,000 + // +000,000.0 + // +0000.00 + // +0000 + if formatIndx[0] == 0 { + if format[formatIndx[0]] != '+' { + panic("RenderFloat(): invalid positive sign directive") + } + positiveStr = "+" + formatIndx = formatIndx[1:] + } + + // Two directives: + // First is thousands separator + // Raise an error if not followed by 3-digit + // 0123456789 + // 0.000,000 + // 000,000.00 + if len(formatIndx) == 2 { + if (formatIndx[1] - formatIndx[0]) != 4 { + panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers") + } + thousandStr = string(format[formatIndx[0]]) + formatIndx = formatIndx[1:] + } + + // One directive: + // Directive is decimal separator + // The number of digit-specifier following the separator indicates wanted precision + // 0123456789 + // 0.00 + // 000,0000 + if len(formatIndx) == 1 { + decimalStr = string(format[formatIndx[0]]) + precision = len(format) - formatIndx[0] - 1 + } + } + } + + // generate sign part + var signStr string + if n >= 0.000000001 { + signStr = positiveStr + } else if n <= -0.000000001 { + signStr = negativeStr + n = -n + } else { + signStr = "" + n = 0.0 + } + + // split number into integer and fractional parts + intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision]) + + // generate integer part string + intStr := strconv.FormatInt(int64(intf), 10) + + // add thousand separator if required + if len(thousandStr) > 0 { + for i := len(intStr); i > 3; { + i -= 3 + intStr = intStr[:i] + thousandStr + intStr[i:] + } + } + + // no fractional part, we can leave now + if precision == 0 { + return signStr + intStr + } + + // generate fractional part + fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision])) + // may need padding + if len(fracStr) < precision { + fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr + } + + return signStr + intStr + decimalStr + fracStr +} + +// FormatInteger produces a formatted number as string. +// See FormatFloat. +func FormatInteger(format string, n int) string { + return FormatFloat(format, float64(n)) +} diff --git a/vendor/github.com/dustin/go-humanize/ordinals.go b/vendor/github.com/dustin/go-humanize/ordinals.go new file mode 100644 index 0000000000..43d88a8619 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/ordinals.go @@ -0,0 +1,25 @@ +package humanize + +import "strconv" + +// Ordinal gives you the input number in a rank/ordinal format. +// +// Ordinal(3) -> 3rd +func Ordinal(x int) string { + suffix := "th" + switch x % 10 { + case 1: + if x%100 != 11 { + suffix = "st" + } + case 2: + if x%100 != 12 { + suffix = "nd" + } + case 3: + if x%100 != 13 { + suffix = "rd" + } + } + return strconv.Itoa(x) + suffix +} diff --git a/vendor/github.com/dustin/go-humanize/si.go b/vendor/github.com/dustin/go-humanize/si.go new file mode 100644 index 0000000000..ae659e0e49 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/si.go @@ -0,0 +1,123 @@ +package humanize + +import ( + "errors" + "math" + "regexp" + "strconv" +) + +var siPrefixTable = map[float64]string{ + -24: "y", // yocto + -21: "z", // zepto + -18: "a", // atto + -15: "f", // femto + -12: "p", // pico + -9: "n", // nano + -6: "µ", // micro + -3: "m", // milli + 0: "", + 3: "k", // kilo + 6: "M", // mega + 9: "G", // giga + 12: "T", // tera + 15: "P", // peta + 18: "E", // exa + 21: "Z", // zetta + 24: "Y", // yotta +} + +var revSIPrefixTable = revfmap(siPrefixTable) + +// revfmap reverses the map and precomputes the power multiplier +func revfmap(in map[float64]string) map[string]float64 { + rv := map[string]float64{} + for k, v := range in { + rv[v] = math.Pow(10, k) + } + return rv +} + +var riParseRegex *regexp.Regexp + +func init() { + ri := `^([\-0-9.]+)\s?([` + for _, v := range siPrefixTable { + ri += v + } + ri += `]?)(.*)` + + riParseRegex = regexp.MustCompile(ri) +} + +// ComputeSI finds the most appropriate SI prefix for the given number +// and returns the prefix along with the value adjusted to be within +// that prefix. +// +// See also: SI, ParseSI. +// +// e.g. ComputeSI(2.2345e-12) -> (2.2345, "p") +func ComputeSI(input float64) (float64, string) { + if input == 0 { + return 0, "" + } + mag := math.Abs(input) + exponent := math.Floor(logn(mag, 10)) + exponent = math.Floor(exponent/3) * 3 + + value := mag / math.Pow(10, exponent) + + // Handle special case where value is exactly 1000.0 + // Should return 1 M instead of 1000 k + if value == 1000.0 { + exponent += 3 + value = mag / math.Pow(10, exponent) + } + + value = math.Copysign(value, input) + + prefix := siPrefixTable[exponent] + return value, prefix +} + +// SI returns a string with default formatting. +// +// SI uses Ftoa to format float value, removing trailing zeros. +// +// See also: ComputeSI, ParseSI. +// +// e.g. SI(1000000, "B") -> 1 MB +// e.g. SI(2.2345e-12, "F") -> 2.2345 pF +func SI(input float64, unit string) string { + value, prefix := ComputeSI(input) + return Ftoa(value) + " " + prefix + unit +} + +// SIWithDigits works like SI but limits the resulting string to the +// given number of decimal places. +// +// e.g. SIWithDigits(1000000, 0, "B") -> 1 MB +// e.g. SIWithDigits(2.2345e-12, 2, "F") -> 2.23 pF +func SIWithDigits(input float64, decimals int, unit string) string { + value, prefix := ComputeSI(input) + return FtoaWithDigits(value, decimals) + " " + prefix + unit +} + +var errInvalid = errors.New("invalid input") + +// ParseSI parses an SI string back into the number and unit. +// +// See also: SI, ComputeSI. +// +// e.g. ParseSI("2.2345 pF") -> (2.2345e-12, "F", nil) +func ParseSI(input string) (float64, string, error) { + found := riParseRegex.FindStringSubmatch(input) + if len(found) != 4 { + return 0, "", errInvalid + } + mag := revSIPrefixTable[found[2]] + unit := found[3] + + base, err := strconv.ParseFloat(found[1], 64) + return base * mag, unit, err +} diff --git a/vendor/github.com/dustin/go-humanize/times.go b/vendor/github.com/dustin/go-humanize/times.go new file mode 100644 index 0000000000..dd3fbf5efc --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/times.go @@ -0,0 +1,117 @@ +package humanize + +import ( + "fmt" + "math" + "sort" + "time" +) + +// Seconds-based time units +const ( + Day = 24 * time.Hour + Week = 7 * Day + Month = 30 * Day + Year = 12 * Month + LongTime = 37 * Year +) + +// Time formats a time into a relative string. +// +// Time(someT) -> "3 weeks ago" +func Time(then time.Time) string { + return RelTime(then, time.Now(), "ago", "from now") +} + +// A RelTimeMagnitude struct contains a relative time point at which +// the relative format of time will switch to a new format string. A +// slice of these in ascending order by their "D" field is passed to +// CustomRelTime to format durations. +// +// The Format field is a string that may contain a "%s" which will be +// replaced with the appropriate signed label (e.g. "ago" or "from +// now") and a "%d" that will be replaced by the quantity. +// +// The DivBy field is the amount of time the time difference must be +// divided by in order to display correctly. +// +// e.g. if D is 2*time.Minute and you want to display "%d minutes %s" +// DivBy should be time.Minute so whatever the duration is will be +// expressed in minutes. +type RelTimeMagnitude struct { + D time.Duration + Format string + DivBy time.Duration +} + +var defaultMagnitudes = []RelTimeMagnitude{ + {time.Second, "now", time.Second}, + {2 * time.Second, "1 second %s", 1}, + {time.Minute, "%d seconds %s", time.Second}, + {2 * time.Minute, "1 minute %s", 1}, + {time.Hour, "%d minutes %s", time.Minute}, + {2 * time.Hour, "1 hour %s", 1}, + {Day, "%d hours %s", time.Hour}, + {2 * Day, "1 day %s", 1}, + {Week, "%d days %s", Day}, + {2 * Week, "1 week %s", 1}, + {Month, "%d weeks %s", Week}, + {2 * Month, "1 month %s", 1}, + {Year, "%d months %s", Month}, + {18 * Month, "1 year %s", 1}, + {2 * Year, "2 years %s", 1}, + {LongTime, "%d years %s", Year}, + {math.MaxInt64, "a long while %s", 1}, +} + +// RelTime formats a time into a relative string. +// +// It takes two times and two labels. In addition to the generic time +// delta string (e.g. 5 minutes), the labels are used applied so that +// the label corresponding to the smaller time is applied. +// +// RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier" +func RelTime(a, b time.Time, albl, blbl string) string { + return CustomRelTime(a, b, albl, blbl, defaultMagnitudes) +} + +// CustomRelTime formats a time into a relative string. +// +// It takes two times two labels and a table of relative time formats. +// In addition to the generic time delta string (e.g. 5 minutes), the +// labels are used applied so that the label corresponding to the +// smaller time is applied. +func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string { + lbl := albl + diff := b.Sub(a) + + if a.After(b) { + lbl = blbl + diff = a.Sub(b) + } + + n := sort.Search(len(magnitudes), func(i int) bool { + return magnitudes[i].D > diff + }) + + if n >= len(magnitudes) { + n = len(magnitudes) - 1 + } + mag := magnitudes[n] + args := []interface{}{} + escaped := false + for _, ch := range mag.Format { + if escaped { + switch ch { + case 's': + args = append(args, lbl) + case 'd': + args = append(args, diff/mag.DivBy) + } + escaped = false + } else { + escaped = ch == '%' + } + } + return fmt.Sprintf(mag.Format, args...) +} diff --git a/vendor/github.com/fatih/color/LICENSE.md b/vendor/github.com/fatih/color/LICENSE.md new file mode 100644 index 0000000000..25fdaf639d --- /dev/null +++ b/vendor/github.com/fatih/color/LICENSE.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Fatih Arslan + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/fatih/color/README.md b/vendor/github.com/fatih/color/README.md new file mode 100644 index 0000000000..5152bf59bf --- /dev/null +++ b/vendor/github.com/fatih/color/README.md @@ -0,0 +1,178 @@ +# color [![](https://github.com/fatih/color/workflows/build/badge.svg)](https://github.com/fatih/color/actions) [![PkgGoDev](https://pkg.go.dev/badge/github.com/fatih/color)](https://pkg.go.dev/github.com/fatih/color) + +Color lets you use colorized outputs in terms of [ANSI Escape +Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It +has support for Windows too! The API can be used in several ways, pick one that +suits you. + +![Color](https://user-images.githubusercontent.com/438920/96832689-03b3e000-13f4-11eb-9803-46f4c4de3406.jpg) + + +## Install + +```bash +go get github.com/fatih/color +``` + +## Examples + +### Standard colors + +```go +// Print with default helper functions +color.Cyan("Prints text in cyan.") + +// A newline will be appended automatically +color.Blue("Prints %s in blue.", "text") + +// These are using the default foreground colors +color.Red("We have red") +color.Magenta("And many others ..") + +``` + +### Mix and reuse colors + +```go +// Create a new color object +c := color.New(color.FgCyan).Add(color.Underline) +c.Println("Prints cyan text with an underline.") + +// Or just add them to New() +d := color.New(color.FgCyan, color.Bold) +d.Printf("This prints bold cyan %s\n", "too!.") + +// Mix up foreground and background colors, create new mixes! +red := color.New(color.FgRed) + +boldRed := red.Add(color.Bold) +boldRed.Println("This will print text in bold red.") + +whiteBackground := red.Add(color.BgWhite) +whiteBackground.Println("Red text with white background.") +``` + +### Use your own output (io.Writer) + +```go +// Use your own io.Writer output +color.New(color.FgBlue).Fprintln(myWriter, "blue color!") + +blue := color.New(color.FgBlue) +blue.Fprint(writer, "This will print text in blue.") +``` + +### Custom print functions (PrintFunc) + +```go +// Create a custom print function for convenience +red := color.New(color.FgRed).PrintfFunc() +red("Warning") +red("Error: %s", err) + +// Mix up multiple attributes +notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() +notice("Don't forget this...") +``` + +### Custom fprint functions (FprintFunc) + +```go +blue := color.New(color.FgBlue).FprintfFunc() +blue(myWriter, "important notice: %s", stars) + +// Mix up with multiple attributes +success := color.New(color.Bold, color.FgGreen).FprintlnFunc() +success(myWriter, "Don't forget this...") +``` + +### Insert into noncolor strings (SprintFunc) + +```go +// Create SprintXxx functions to mix strings with other non-colorized strings: +yellow := color.New(color.FgYellow).SprintFunc() +red := color.New(color.FgRed).SprintFunc() +fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error")) + +info := color.New(color.FgWhite, color.BgGreen).SprintFunc() +fmt.Printf("This %s rocks!\n", info("package")) + +// Use helper functions +fmt.Println("This", color.RedString("warning"), "should be not neglected.") +fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.") + +// Windows supported too! Just don't forget to change the output to color.Output +fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) +``` + +### Plug into existing code + +```go +// Use handy standard colors +color.Set(color.FgYellow) + +fmt.Println("Existing text will now be in yellow") +fmt.Printf("This one %s\n", "too") + +color.Unset() // Don't forget to unset + +// You can mix up parameters +color.Set(color.FgMagenta, color.Bold) +defer color.Unset() // Use it in your function + +fmt.Println("All text will now be bold magenta.") +``` + +### Disable/Enable color + +There might be a case where you want to explicitly disable/enable color output. the +`go-isatty` package will automatically disable color output for non-tty output streams +(for example if the output were piped directly to `less`). + +The `color` package also disables color output if the [`NO_COLOR`](https://no-color.org) environment +variable is set (regardless of its value). + +`Color` has support to disable/enable colors programatically both globally and +for single color definitions. For example suppose you have a CLI app and a +`--no-color` bool flag. You can easily disable the color output with: + +```go +var flagNoColor = flag.Bool("no-color", false, "Disable color output") + +if *flagNoColor { + color.NoColor = true // disables colorized output +} +``` + +It also has support for single color definitions (local). You can +disable/enable color output on the fly: + +```go +c := color.New(color.FgCyan) +c.Println("Prints cyan text") + +c.DisableColor() +c.Println("This is printed without any color") + +c.EnableColor() +c.Println("This prints again cyan...") +``` + +## GitHub Actions + +To output color in GitHub Actions (or other CI systems that support ANSI colors), make sure to set `color.NoColor = false` so that it bypasses the check for non-tty output streams. + +## Todo + +* Save/Return previous values +* Evaluate fmt.Formatter interface + + +## Credits + + * [Fatih Arslan](https://github.com/fatih) + * Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable) + +## License + +The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details diff --git a/vendor/github.com/fatih/color/color.go b/vendor/github.com/fatih/color/color.go new file mode 100644 index 0000000000..98a60f3c88 --- /dev/null +++ b/vendor/github.com/fatih/color/color.go @@ -0,0 +1,618 @@ +package color + +import ( + "fmt" + "io" + "os" + "strconv" + "strings" + "sync" + + "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" +) + +var ( + // NoColor defines if the output is colorized or not. It's dynamically set to + // false or true based on the stdout's file descriptor referring to a terminal + // or not. It's also set to true if the NO_COLOR environment variable is + // set (regardless of its value). This is a global option and affects all + // colors. For more control over each color block use the methods + // DisableColor() individually. + NoColor = noColorExists() || os.Getenv("TERM") == "dumb" || + (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) + + // Output defines the standard output of the print functions. By default + // os.Stdout is used. + Output = colorable.NewColorableStdout() + + // Error defines a color supporting writer for os.Stderr. + Error = colorable.NewColorableStderr() + + // colorsCache is used to reduce the count of created Color objects and + // allows to reuse already created objects with required Attribute. + colorsCache = make(map[Attribute]*Color) + colorsCacheMu sync.Mutex // protects colorsCache +) + +// noColorExists returns true if the environment variable NO_COLOR exists. +func noColorExists() bool { + _, exists := os.LookupEnv("NO_COLOR") + return exists +} + +// Color defines a custom color object which is defined by SGR parameters. +type Color struct { + params []Attribute + noColor *bool +} + +// Attribute defines a single SGR Code +type Attribute int + +const escape = "\x1b" + +// Base attributes +const ( + Reset Attribute = iota + Bold + Faint + Italic + Underline + BlinkSlow + BlinkRapid + ReverseVideo + Concealed + CrossedOut +) + +// Foreground text colors +const ( + FgBlack Attribute = iota + 30 + FgRed + FgGreen + FgYellow + FgBlue + FgMagenta + FgCyan + FgWhite +) + +// Foreground Hi-Intensity text colors +const ( + FgHiBlack Attribute = iota + 90 + FgHiRed + FgHiGreen + FgHiYellow + FgHiBlue + FgHiMagenta + FgHiCyan + FgHiWhite +) + +// Background text colors +const ( + BgBlack Attribute = iota + 40 + BgRed + BgGreen + BgYellow + BgBlue + BgMagenta + BgCyan + BgWhite +) + +// Background Hi-Intensity text colors +const ( + BgHiBlack Attribute = iota + 100 + BgHiRed + BgHiGreen + BgHiYellow + BgHiBlue + BgHiMagenta + BgHiCyan + BgHiWhite +) + +// New returns a newly created color object. +func New(value ...Attribute) *Color { + c := &Color{ + params: make([]Attribute, 0), + } + + if noColorExists() { + c.noColor = boolPtr(true) + } + + c.Add(value...) + return c +} + +// Set sets the given parameters immediately. It will change the color of +// output with the given SGR parameters until color.Unset() is called. +func Set(p ...Attribute) *Color { + c := New(p...) + c.Set() + return c +} + +// Unset resets all escape attributes and clears the output. Usually should +// be called after Set(). +func Unset() { + if NoColor { + return + } + + fmt.Fprintf(Output, "%s[%dm", escape, Reset) +} + +// Set sets the SGR sequence. +func (c *Color) Set() *Color { + if c.isNoColorSet() { + return c + } + + fmt.Fprintf(Output, c.format()) + return c +} + +func (c *Color) unset() { + if c.isNoColorSet() { + return + } + + Unset() +} + +func (c *Color) setWriter(w io.Writer) *Color { + if c.isNoColorSet() { + return c + } + + fmt.Fprintf(w, c.format()) + return c +} + +func (c *Color) unsetWriter(w io.Writer) { + if c.isNoColorSet() { + return + } + + if NoColor { + return + } + + fmt.Fprintf(w, "%s[%dm", escape, Reset) +} + +// Add is used to chain SGR parameters. Use as many as parameters to combine +// and create custom color objects. Example: Add(color.FgRed, color.Underline). +func (c *Color) Add(value ...Attribute) *Color { + c.params = append(c.params, value...) + return c +} + +func (c *Color) prepend(value Attribute) { + c.params = append(c.params, 0) + copy(c.params[1:], c.params[0:]) + c.params[0] = value +} + +// Fprint formats using the default formats for its operands and writes to w. +// Spaces are added between operands when neither is a string. +// It returns the number of bytes written and any write error encountered. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprint(w, a...) +} + +// Print formats using the default formats for its operands and writes to +// standard output. Spaces are added between operands when neither is a +// string. It returns the number of bytes written and any write error +// encountered. This is the standard fmt.Print() method wrapped with the given +// color. +func (c *Color) Print(a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprint(Output, a...) +} + +// Fprintf formats according to a format specifier and writes to w. +// It returns the number of bytes written and any write error encountered. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprintf(w, format, a...) +} + +// Printf formats according to a format specifier and writes to standard output. +// It returns the number of bytes written and any write error encountered. +// This is the standard fmt.Printf() method wrapped with the given color. +func (c *Color) Printf(format string, a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprintf(Output, format, a...) +} + +// Fprintln formats using the default formats for its operands and writes to w. +// Spaces are always added between operands and a newline is appended. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprintln(w, a...) +} + +// Println formats using the default formats for its operands and writes to +// standard output. Spaces are always added between operands and a newline is +// appended. It returns the number of bytes written and any write error +// encountered. This is the standard fmt.Print() method wrapped with the given +// color. +func (c *Color) Println(a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprintln(Output, a...) +} + +// Sprint is just like Print, but returns a string instead of printing it. +func (c *Color) Sprint(a ...interface{}) string { + return c.wrap(fmt.Sprint(a...)) +} + +// Sprintln is just like Println, but returns a string instead of printing it. +func (c *Color) Sprintln(a ...interface{}) string { + return c.wrap(fmt.Sprintln(a...)) +} + +// Sprintf is just like Printf, but returns a string instead of printing it. +func (c *Color) Sprintf(format string, a ...interface{}) string { + return c.wrap(fmt.Sprintf(format, a...)) +} + +// FprintFunc returns a new function that prints the passed arguments as +// colorized with color.Fprint(). +func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) { + return func(w io.Writer, a ...interface{}) { + c.Fprint(w, a...) + } +} + +// PrintFunc returns a new function that prints the passed arguments as +// colorized with color.Print(). +func (c *Color) PrintFunc() func(a ...interface{}) { + return func(a ...interface{}) { + c.Print(a...) + } +} + +// FprintfFunc returns a new function that prints the passed arguments as +// colorized with color.Fprintf(). +func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) { + return func(w io.Writer, format string, a ...interface{}) { + c.Fprintf(w, format, a...) + } +} + +// PrintfFunc returns a new function that prints the passed arguments as +// colorized with color.Printf(). +func (c *Color) PrintfFunc() func(format string, a ...interface{}) { + return func(format string, a ...interface{}) { + c.Printf(format, a...) + } +} + +// FprintlnFunc returns a new function that prints the passed arguments as +// colorized with color.Fprintln(). +func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) { + return func(w io.Writer, a ...interface{}) { + c.Fprintln(w, a...) + } +} + +// PrintlnFunc returns a new function that prints the passed arguments as +// colorized with color.Println(). +func (c *Color) PrintlnFunc() func(a ...interface{}) { + return func(a ...interface{}) { + c.Println(a...) + } +} + +// SprintFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprint(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output, example: +// +// put := New(FgYellow).SprintFunc() +// fmt.Fprintf(color.Output, "This is a %s", put("warning")) +func (c *Color) SprintFunc() func(a ...interface{}) string { + return func(a ...interface{}) string { + return c.wrap(fmt.Sprint(a...)) + } +} + +// SprintfFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprintf(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output. +func (c *Color) SprintfFunc() func(format string, a ...interface{}) string { + return func(format string, a ...interface{}) string { + return c.wrap(fmt.Sprintf(format, a...)) + } +} + +// SprintlnFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprintln(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output. +func (c *Color) SprintlnFunc() func(a ...interface{}) string { + return func(a ...interface{}) string { + return c.wrap(fmt.Sprintln(a...)) + } +} + +// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m" +// an example output might be: "1;36" -> bold cyan +func (c *Color) sequence() string { + format := make([]string, len(c.params)) + for i, v := range c.params { + format[i] = strconv.Itoa(int(v)) + } + + return strings.Join(format, ";") +} + +// wrap wraps the s string with the colors attributes. The string is ready to +// be printed. +func (c *Color) wrap(s string) string { + if c.isNoColorSet() { + return s + } + + return c.format() + s + c.unformat() +} + +func (c *Color) format() string { + return fmt.Sprintf("%s[%sm", escape, c.sequence()) +} + +func (c *Color) unformat() string { + return fmt.Sprintf("%s[%dm", escape, Reset) +} + +// DisableColor disables the color output. Useful to not change any existing +// code and still being able to output. Can be used for flags like +// "--no-color". To enable back use EnableColor() method. +func (c *Color) DisableColor() { + c.noColor = boolPtr(true) +} + +// EnableColor enables the color output. Use it in conjunction with +// DisableColor(). Otherwise this method has no side effects. +func (c *Color) EnableColor() { + c.noColor = boolPtr(false) +} + +func (c *Color) isNoColorSet() bool { + // check first if we have user set action + if c.noColor != nil { + return *c.noColor + } + + // if not return the global option, which is disabled by default + return NoColor +} + +// Equals returns a boolean value indicating whether two colors are equal. +func (c *Color) Equals(c2 *Color) bool { + if len(c.params) != len(c2.params) { + return false + } + + for _, attr := range c.params { + if !c2.attrExists(attr) { + return false + } + } + + return true +} + +func (c *Color) attrExists(a Attribute) bool { + for _, attr := range c.params { + if attr == a { + return true + } + } + + return false +} + +func boolPtr(v bool) *bool { + return &v +} + +func getCachedColor(p Attribute) *Color { + colorsCacheMu.Lock() + defer colorsCacheMu.Unlock() + + c, ok := colorsCache[p] + if !ok { + c = New(p) + colorsCache[p] = c + } + + return c +} + +func colorPrint(format string, p Attribute, a ...interface{}) { + c := getCachedColor(p) + + if !strings.HasSuffix(format, "\n") { + format += "\n" + } + + if len(a) == 0 { + c.Print(format) + } else { + c.Printf(format, a...) + } +} + +func colorString(format string, p Attribute, a ...interface{}) string { + c := getCachedColor(p) + + if len(a) == 0 { + return c.SprintFunc()(format) + } + + return c.SprintfFunc()(format, a...) +} + +// Black is a convenient helper function to print with black foreground. A +// newline is appended to format by default. +func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) } + +// Red is a convenient helper function to print with red foreground. A +// newline is appended to format by default. +func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) } + +// Green is a convenient helper function to print with green foreground. A +// newline is appended to format by default. +func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) } + +// Yellow is a convenient helper function to print with yellow foreground. +// A newline is appended to format by default. +func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) } + +// Blue is a convenient helper function to print with blue foreground. A +// newline is appended to format by default. +func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) } + +// Magenta is a convenient helper function to print with magenta foreground. +// A newline is appended to format by default. +func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) } + +// Cyan is a convenient helper function to print with cyan foreground. A +// newline is appended to format by default. +func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) } + +// White is a convenient helper function to print with white foreground. A +// newline is appended to format by default. +func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) } + +// BlackString is a convenient helper function to return a string with black +// foreground. +func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) } + +// RedString is a convenient helper function to return a string with red +// foreground. +func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) } + +// GreenString is a convenient helper function to return a string with green +// foreground. +func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) } + +// YellowString is a convenient helper function to return a string with yellow +// foreground. +func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) } + +// BlueString is a convenient helper function to return a string with blue +// foreground. +func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) } + +// MagentaString is a convenient helper function to return a string with magenta +// foreground. +func MagentaString(format string, a ...interface{}) string { + return colorString(format, FgMagenta, a...) +} + +// CyanString is a convenient helper function to return a string with cyan +// foreground. +func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) } + +// WhiteString is a convenient helper function to return a string with white +// foreground. +func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) } + +// HiBlack is a convenient helper function to print with hi-intensity black foreground. A +// newline is appended to format by default. +func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) } + +// HiRed is a convenient helper function to print with hi-intensity red foreground. A +// newline is appended to format by default. +func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) } + +// HiGreen is a convenient helper function to print with hi-intensity green foreground. A +// newline is appended to format by default. +func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) } + +// HiYellow is a convenient helper function to print with hi-intensity yellow foreground. +// A newline is appended to format by default. +func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) } + +// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A +// newline is appended to format by default. +func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) } + +// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground. +// A newline is appended to format by default. +func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) } + +// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A +// newline is appended to format by default. +func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) } + +// HiWhite is a convenient helper function to print with hi-intensity white foreground. A +// newline is appended to format by default. +func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) } + +// HiBlackString is a convenient helper function to return a string with hi-intensity black +// foreground. +func HiBlackString(format string, a ...interface{}) string { + return colorString(format, FgHiBlack, a...) +} + +// HiRedString is a convenient helper function to return a string with hi-intensity red +// foreground. +func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) } + +// HiGreenString is a convenient helper function to return a string with hi-intensity green +// foreground. +func HiGreenString(format string, a ...interface{}) string { + return colorString(format, FgHiGreen, a...) +} + +// HiYellowString is a convenient helper function to return a string with hi-intensity yellow +// foreground. +func HiYellowString(format string, a ...interface{}) string { + return colorString(format, FgHiYellow, a...) +} + +// HiBlueString is a convenient helper function to return a string with hi-intensity blue +// foreground. +func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) } + +// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta +// foreground. +func HiMagentaString(format string, a ...interface{}) string { + return colorString(format, FgHiMagenta, a...) +} + +// HiCyanString is a convenient helper function to return a string with hi-intensity cyan +// foreground. +func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) } + +// HiWhiteString is a convenient helper function to return a string with hi-intensity white +// foreground. +func HiWhiteString(format string, a ...interface{}) string { + return colorString(format, FgHiWhite, a...) +} diff --git a/vendor/github.com/fatih/color/doc.go b/vendor/github.com/fatih/color/doc.go new file mode 100644 index 0000000000..04541de786 --- /dev/null +++ b/vendor/github.com/fatih/color/doc.go @@ -0,0 +1,135 @@ +/* +Package color is an ANSI color package to output colorized or SGR defined +output to the standard output. The API can be used in several way, pick one +that suits you. + +Use simple and default helper functions with predefined foreground colors: + + color.Cyan("Prints text in cyan.") + + // a newline will be appended automatically + color.Blue("Prints %s in blue.", "text") + + // More default foreground colors.. + color.Red("We have red") + color.Yellow("Yellow color too!") + color.Magenta("And many others ..") + + // Hi-intensity colors + color.HiGreen("Bright green color.") + color.HiBlack("Bright black means gray..") + color.HiWhite("Shiny white color!") + +However there are times where custom color mixes are required. Below are some +examples to create custom color objects and use the print functions of each +separate color object. + + // Create a new color object + c := color.New(color.FgCyan).Add(color.Underline) + c.Println("Prints cyan text with an underline.") + + // Or just add them to New() + d := color.New(color.FgCyan, color.Bold) + d.Printf("This prints bold cyan %s\n", "too!.") + + + // Mix up foreground and background colors, create new mixes! + red := color.New(color.FgRed) + + boldRed := red.Add(color.Bold) + boldRed.Println("This will print text in bold red.") + + whiteBackground := red.Add(color.BgWhite) + whiteBackground.Println("Red text with White background.") + + // Use your own io.Writer output + color.New(color.FgBlue).Fprintln(myWriter, "blue color!") + + blue := color.New(color.FgBlue) + blue.Fprint(myWriter, "This will print text in blue.") + +You can create PrintXxx functions to simplify even more: + + // Create a custom print function for convenient + red := color.New(color.FgRed).PrintfFunc() + red("warning") + red("error: %s", err) + + // Mix up multiple attributes + notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() + notice("don't forget this...") + +You can also FprintXxx functions to pass your own io.Writer: + + blue := color.New(FgBlue).FprintfFunc() + blue(myWriter, "important notice: %s", stars) + + // Mix up with multiple attributes + success := color.New(color.Bold, color.FgGreen).FprintlnFunc() + success(myWriter, don't forget this...") + + +Or create SprintXxx functions to mix strings with other non-colorized strings: + + yellow := New(FgYellow).SprintFunc() + red := New(FgRed).SprintFunc() + + fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error")) + + info := New(FgWhite, BgGreen).SprintFunc() + fmt.Printf("this %s rocks!\n", info("package")) + +Windows support is enabled by default. All Print functions work as intended. +However only for color.SprintXXX functions, user should use fmt.FprintXXX and +set the output to color.Output: + + fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) + + info := New(FgWhite, BgGreen).SprintFunc() + fmt.Fprintf(color.Output, "this %s rocks!\n", info("package")) + +Using with existing code is possible. Just use the Set() method to set the +standard output to the given parameters. That way a rewrite of an existing +code is not required. + + // Use handy standard colors. + color.Set(color.FgYellow) + + fmt.Println("Existing text will be now in Yellow") + fmt.Printf("This one %s\n", "too") + + color.Unset() // don't forget to unset + + // You can mix up parameters + color.Set(color.FgMagenta, color.Bold) + defer color.Unset() // use it in your function + + fmt.Println("All text will be now bold magenta.") + +There might be a case where you want to disable color output (for example to +pipe the standard output of your app to somewhere else). `Color` has support to +disable colors both globally and for single color definition. For example +suppose you have a CLI app and a `--no-color` bool flag. You can easily disable +the color output with: + + var flagNoColor = flag.Bool("no-color", false, "Disable color output") + + if *flagNoColor { + color.NoColor = true // disables colorized output + } + +You can also disable the color by setting the NO_COLOR environment variable to any value. + +It also has support for single color definitions (local). You can +disable/enable color output on the fly: + + c := color.New(color.FgCyan) + c.Println("Prints cyan text") + + c.DisableColor() + c.Println("This is printed without any color") + + c.EnableColor() + c.Println("This prints again cyan...") +*/ +package color diff --git a/vendor/github.com/fatih/color/go.mod b/vendor/github.com/fatih/color/go.mod new file mode 100644 index 0000000000..c9b3cd59a2 --- /dev/null +++ b/vendor/github.com/fatih/color/go.mod @@ -0,0 +1,8 @@ +module github.com/fatih/color + +go 1.13 + +require ( + github.com/mattn/go-colorable v0.1.9 + github.com/mattn/go-isatty v0.0.14 +) diff --git a/vendor/github.com/fatih/color/go.sum b/vendor/github.com/fatih/color/go.sum new file mode 100644 index 0000000000..cbbcfb6446 --- /dev/null +++ b/vendor/github.com/fatih/color/go.sum @@ -0,0 +1,9 @@ +github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/vendor/github.com/gdamore/encoding/.appveyor.yml b/vendor/github.com/gdamore/encoding/.appveyor.yml new file mode 100644 index 0000000000..19a4c5ddf9 --- /dev/null +++ b/vendor/github.com/gdamore/encoding/.appveyor.yml @@ -0,0 +1,13 @@ +version: 1.0.{build} +clone_folder: c:\gopath\src\github.com\gdamore\encoding +environment: + GOPATH: c:\gopath +build_script: +- go version +- go env +- SET PATH=%LOCALAPPDATA%\atom\bin;%GOPATH%\bin;%PATH% +- go get -t ./... +- go build +- go install ./... +test_script: +- go test ./... diff --git a/vendor/github.com/gdamore/encoding/.travis.yml b/vendor/github.com/gdamore/encoding/.travis.yml new file mode 100644 index 0000000000..504241380e --- /dev/null +++ b/vendor/github.com/gdamore/encoding/.travis.yml @@ -0,0 +1,7 @@ +language: go + +go: + - 1.9.x + - 1.10.x + - 1.11.x + - tip diff --git a/vendor/github.com/gdamore/encoding/LICENSE b/vendor/github.com/gdamore/encoding/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/github.com/gdamore/encoding/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/gdamore/encoding/README.md b/vendor/github.com/gdamore/encoding/README.md new file mode 100644 index 0000000000..3db2b4c58a --- /dev/null +++ b/vendor/github.com/gdamore/encoding/README.md @@ -0,0 +1,19 @@ +## encoding + +[![Linux Status](https://img.shields.io/travis/gdamore/encoding.svg?label=linux)](https://travis-ci.org/gdamore/encoding) +[![Windows Status](https://img.shields.io/appveyor/ci/gdamore/encoding.svg?label=windows)](https://ci.appveyor.com/project/gdamore/encoding) +[![Apache License](https://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/gdamore/encoding/blob/master/LICENSE) +[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/gdamore/encoding) +[![Go Report Card](http://goreportcard.com/badge/gdamore/encoding)](http://goreportcard.com/report/gdamore/encoding) + +Package encoding provides a number of encodings that are missing from the +standard Go [encoding]("https://godoc.org/golang.org/x/text/encoding") package. + +We hope that we can contribute these to the standard Go library someday. It +turns out that some of these are useful for dealing with I/O streams coming +from non-UTF friendly sources. + +The UTF8 Encoder is also useful for situations where valid UTF-8 might be +carried in streams that contain non-valid UTF; in particular I use it for +helping me cope with terminals that embed escape sequences in otherwise +valid UTF-8. diff --git a/vendor/github.com/gdamore/encoding/ascii.go b/vendor/github.com/gdamore/encoding/ascii.go new file mode 100644 index 0000000000..b7321f4334 --- /dev/null +++ b/vendor/github.com/gdamore/encoding/ascii.go @@ -0,0 +1,36 @@ +// Copyright 2015 Garrett D'Amore +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package encoding + +import ( + "golang.org/x/text/encoding" +) + +// ASCII represents the 7-bit US-ASCII scheme. It decodes directly to +// UTF-8 without change, as all ASCII values are legal UTF-8. +// Unicode values less than 128 (i.e. 7 bits) map 1:1 with ASCII. +// It encodes runes outside of that to 0x1A, the ASCII substitution character. +var ASCII encoding.Encoding + +func init() { + amap := make(map[byte]rune) + for i := 128; i <= 255; i++ { + amap[byte(i)] = RuneError + } + + cm := &Charmap{Map: amap} + cm.Init() + ASCII = cm +} diff --git a/vendor/github.com/gdamore/encoding/charmap.go b/vendor/github.com/gdamore/encoding/charmap.go new file mode 100644 index 0000000000..db1c33ef7f --- /dev/null +++ b/vendor/github.com/gdamore/encoding/charmap.go @@ -0,0 +1,196 @@ +// Copyright 2015 Garrett D'Amore +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package encoding + +import ( + "sync" + "unicode/utf8" + + "golang.org/x/text/encoding" + "golang.org/x/text/transform" +) + +const ( + // RuneError is an alias for the UTF-8 replacement rune, '\uFFFD'. + RuneError = '\uFFFD' + + // RuneSelf is the rune below which UTF-8 and the Unicode values are + // identical. Its also the limit for ASCII. + RuneSelf = 0x80 + + // ASCIISub is the ASCII substitution character. + ASCIISub = '\x1a' +) + +// Charmap is a structure for setting up encodings for 8-bit character sets, +// for transforming between UTF8 and that other character set. It has some +// ideas borrowed from golang.org/x/text/encoding/charmap, but it uses a +// different implementation. This implementation uses maps, and supports +// user-defined maps. +// +// We do assume that a character map has a reasonable substitution character, +// and that valid encodings are stable (exactly a 1:1 map) and stateless +// (that is there is no shift character or anything like that.) Hence this +// approach will not work for many East Asian character sets. +// +// Measurement shows little or no measurable difference in the performance of +// the two approaches. The difference was down to a couple of nsec/op, and +// no consistent pattern as to which ran faster. With the conversion to +// UTF-8 the code takes about 25 nsec/op. The conversion in the reverse +// direction takes about 100 nsec/op. (The larger cost for conversion +// from UTF-8 is most likely due to the need to convert the UTF-8 byte stream +// to a rune before conversion. +// +type Charmap struct { + transform.NopResetter + bytes map[rune]byte + runes [256][]byte + once sync.Once + + // The map between bytes and runes. To indicate that a specific + // byte value is invalid for a charcter set, use the rune + // utf8.RuneError. Values that are absent from this map will + // be assumed to have the identity mapping -- that is the default + // is to assume ISO8859-1, where all 8-bit characters have the same + // numeric value as their Unicode runes. (Not to be confused with + // the UTF-8 values, which *will* be different for non-ASCII runes.) + // + // If no values less than RuneSelf are changed (or have non-identity + // mappings), then the character set is assumed to be an ASCII + // superset, and certain assumptions and optimizations become + // available for ASCII bytes. + Map map[byte]rune + + // The ReplacementChar is the byte value to use for substitution. + // It should normally be ASCIISub for ASCII encodings. This may be + // unset (left to zero) for mappings that are strictly ASCII supersets. + // In that case ASCIISub will be assumed instead. + ReplacementChar byte +} + +type cmapDecoder struct { + transform.NopResetter + runes [256][]byte +} + +type cmapEncoder struct { + transform.NopResetter + bytes map[rune]byte + replace byte +} + +// Init initializes internal values of a character map. This should +// be done early, to minimize the cost of allocation of transforms +// later. It is not strictly necessary however, as the allocation +// functions will arrange to call it if it has not already been done. +func (c *Charmap) Init() { + c.once.Do(c.initialize) +} + +func (c *Charmap) initialize() { + c.bytes = make(map[rune]byte) + ascii := true + + for i := 0; i < 256; i++ { + r, ok := c.Map[byte(i)] + if !ok { + r = rune(i) + } + if r < 128 && r != rune(i) { + ascii = false + } + if r != RuneError { + c.bytes[r] = byte(i) + } + utf := make([]byte, utf8.RuneLen(r)) + utf8.EncodeRune(utf, r) + c.runes[i] = utf + } + if ascii && c.ReplacementChar == '\x00' { + c.ReplacementChar = ASCIISub + } +} + +// NewDecoder returns a Decoder the converts from the 8-bit +// character set to UTF-8. Unknown mappings, if any, are mapped +// to '\uFFFD'. +func (c *Charmap) NewDecoder() *encoding.Decoder { + c.Init() + return &encoding.Decoder{Transformer: &cmapDecoder{runes: c.runes}} +} + +// NewEncoder returns a Transformer that converts from UTF8 to the +// 8-bit character set. Unknown mappings are mapped to 0x1A. +func (c *Charmap) NewEncoder() *encoding.Encoder { + c.Init() + return &encoding.Encoder{ + Transformer: &cmapEncoder{ + bytes: c.bytes, + replace: c.ReplacementChar, + }, + } +} + +func (d *cmapDecoder) Transform(dst, src []byte, atEOF bool) (int, int, error) { + var e error + var ndst, nsrc int + + for _, c := range src { + b := d.runes[c] + l := len(b) + + if ndst+l > len(dst) { + e = transform.ErrShortDst + break + } + for i := 0; i < l; i++ { + dst[ndst] = b[i] + ndst++ + } + nsrc++ + } + return ndst, nsrc, e +} + +func (d *cmapEncoder) Transform(dst, src []byte, atEOF bool) (int, int, error) { + var e error + var ndst, nsrc int + for nsrc < len(src) { + if ndst >= len(dst) { + e = transform.ErrShortDst + break + } + + r, sz := utf8.DecodeRune(src[nsrc:]) + if r == utf8.RuneError && sz == 1 { + // If its inconclusive due to insufficient data in + // in the source, report it + if !atEOF && !utf8.FullRune(src[nsrc:]) { + e = transform.ErrShortSrc + break + } + } + + if c, ok := d.bytes[r]; ok { + dst[ndst] = c + } else { + dst[ndst] = d.replace + } + nsrc += sz + ndst++ + } + + return ndst, nsrc, e +} diff --git a/vendor/github.com/gdamore/encoding/doc.go b/vendor/github.com/gdamore/encoding/doc.go new file mode 100644 index 0000000000..8a7b48d7ee --- /dev/null +++ b/vendor/github.com/gdamore/encoding/doc.go @@ -0,0 +1,17 @@ +// Copyright 2015 Garrett D'Amore +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +// Package encoding provides a few of the encoding structures that are +// missing from the Go x/text/encoding tree. +package encoding diff --git a/vendor/github.com/gdamore/encoding/ebcdic.go b/vendor/github.com/gdamore/encoding/ebcdic.go new file mode 100644 index 0000000000..8e13f1a97f --- /dev/null +++ b/vendor/github.com/gdamore/encoding/ebcdic.go @@ -0,0 +1,273 @@ +// Copyright 2015 Garrett D'Amore +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package encoding + +import ( + "golang.org/x/text/encoding" +) + +// EBCDIC represents the 8-bit EBCDIC scheme, found in some mainframe +// environments. If you don't know what this is, consider yourself lucky. +var EBCDIC encoding.Encoding + +func init() { + cm := &Charmap{ + ReplacementChar: '\x3f', + Map: map[byte]rune{ + // 0x00-0x03 match + 0x04: RuneError, + 0x05: '\t', + 0x06: RuneError, + 0x07: '\x7f', + 0x08: RuneError, + 0x09: RuneError, + 0x0a: RuneError, + // 0x0b-0x13 match + 0x14: RuneError, + 0x15: '\x85', // Not in any ISO code + 0x16: '\x08', + 0x17: RuneError, + // 0x18-0x19 match + 0x1a: RuneError, + 0x1b: RuneError, + // 0x1c-0x1f match + 0x20: RuneError, + 0x21: RuneError, + 0x22: RuneError, + 0x23: RuneError, + 0x24: RuneError, + 0x25: '\n', + 0x26: '\x17', + 0x27: '\x1b', + 0x28: RuneError, + 0x29: RuneError, + 0x2a: RuneError, + 0x2b: RuneError, + 0x2c: RuneError, + 0x2d: '\x05', + 0x2e: '\x06', + 0x2f: '\x07', + 0x30: RuneError, + 0x31: RuneError, + 0x32: '\x16', + 0x33: RuneError, + 0x34: RuneError, + 0x35: RuneError, + 0x36: RuneError, + 0x37: '\x04', + 0x38: RuneError, + 0x39: RuneError, + 0x3a: RuneError, + 0x3b: RuneError, + 0x3c: '\x14', + 0x3d: '\x15', + 0x3e: RuneError, + 0x3f: '\x1a', // also replacement char + 0x40: ' ', + 0x41: '\xa0', + 0x42: RuneError, + 0x43: RuneError, + 0x44: RuneError, + 0x45: RuneError, + 0x46: RuneError, + 0x47: RuneError, + 0x48: RuneError, + 0x49: RuneError, + 0x4a: RuneError, + 0x4b: '.', + 0x4c: '<', + 0x4d: '(', + 0x4e: '+', + 0x4f: '|', + 0x50: '&', + 0x51: RuneError, + 0x52: RuneError, + 0x53: RuneError, + 0x54: RuneError, + 0x55: RuneError, + 0x56: RuneError, + 0x57: RuneError, + 0x58: RuneError, + 0x59: RuneError, + 0x5a: '!', + 0x5b: '$', + 0x5c: '*', + 0x5d: ')', + 0x5e: ';', + 0x5f: '¬', + 0x60: '-', + 0x61: '/', + 0x62: RuneError, + 0x63: RuneError, + 0x64: RuneError, + 0x65: RuneError, + 0x66: RuneError, + 0x67: RuneError, + 0x68: RuneError, + 0x69: RuneError, + 0x6a: '¦', + 0x6b: ',', + 0x6c: '%', + 0x6d: '_', + 0x6e: '>', + 0x6f: '?', + 0x70: RuneError, + 0x71: RuneError, + 0x72: RuneError, + 0x73: RuneError, + 0x74: RuneError, + 0x75: RuneError, + 0x76: RuneError, + 0x77: RuneError, + 0x78: RuneError, + 0x79: '`', + 0x7a: ':', + 0x7b: '#', + 0x7c: '@', + 0x7d: '\'', + 0x7e: '=', + 0x7f: '"', + 0x80: RuneError, + 0x81: 'a', + 0x82: 'b', + 0x83: 'c', + 0x84: 'd', + 0x85: 'e', + 0x86: 'f', + 0x87: 'g', + 0x88: 'h', + 0x89: 'i', + 0x8a: RuneError, + 0x8b: RuneError, + 0x8c: RuneError, + 0x8d: RuneError, + 0x8e: RuneError, + 0x8f: '±', + 0x90: RuneError, + 0x91: 'j', + 0x92: 'k', + 0x93: 'l', + 0x94: 'm', + 0x95: 'n', + 0x96: 'o', + 0x97: 'p', + 0x98: 'q', + 0x99: 'r', + 0x9a: RuneError, + 0x9b: RuneError, + 0x9c: RuneError, + 0x9d: RuneError, + 0x9e: RuneError, + 0x9f: RuneError, + 0xa0: RuneError, + 0xa1: '~', + 0xa2: 's', + 0xa3: 't', + 0xa4: 'u', + 0xa5: 'v', + 0xa6: 'w', + 0xa7: 'x', + 0xa8: 'y', + 0xa9: 'z', + 0xaa: RuneError, + 0xab: RuneError, + 0xac: RuneError, + 0xad: RuneError, + 0xae: RuneError, + 0xaf: RuneError, + 0xb0: '^', + 0xb1: RuneError, + 0xb2: RuneError, + 0xb3: RuneError, + 0xb4: RuneError, + 0xb5: RuneError, + 0xb6: RuneError, + 0xb7: RuneError, + 0xb8: RuneError, + 0xb9: RuneError, + 0xba: '[', + 0xbb: ']', + 0xbc: RuneError, + 0xbd: RuneError, + 0xbe: RuneError, + 0xbf: RuneError, + 0xc0: '{', + 0xc1: 'A', + 0xc2: 'B', + 0xc3: 'C', + 0xc4: 'D', + 0xc5: 'E', + 0xc6: 'F', + 0xc7: 'G', + 0xc8: 'H', + 0xc9: 'I', + 0xca: '\xad', // NB: soft hyphen + 0xcb: RuneError, + 0xcc: RuneError, + 0xcd: RuneError, + 0xce: RuneError, + 0xcf: RuneError, + 0xd0: '}', + 0xd1: 'J', + 0xd2: 'K', + 0xd3: 'L', + 0xd4: 'M', + 0xd5: 'N', + 0xd6: 'O', + 0xd7: 'P', + 0xd8: 'Q', + 0xd9: 'R', + 0xda: RuneError, + 0xdb: RuneError, + 0xdc: RuneError, + 0xdd: RuneError, + 0xde: RuneError, + 0xdf: RuneError, + 0xe0: '\\', + 0xe1: '\u2007', // Non-breaking space + 0xe2: 'S', + 0xe3: 'T', + 0xe4: 'U', + 0xe5: 'V', + 0xe6: 'W', + 0xe7: 'X', + 0xe8: 'Y', + 0xe9: 'Z', + 0xea: RuneError, + 0xeb: RuneError, + 0xec: RuneError, + 0xed: RuneError, + 0xee: RuneError, + 0xef: RuneError, + 0xf0: '0', + 0xf1: '1', + 0xf2: '2', + 0xf3: '3', + 0xf4: '4', + 0xf5: '5', + 0xf6: '6', + 0xf7: '7', + 0xf8: '8', + 0xf9: '9', + 0xfa: RuneError, + 0xfb: RuneError, + 0xfc: RuneError, + 0xfd: RuneError, + 0xfe: RuneError, + 0xff: RuneError, + }} + cm.Init() + EBCDIC = cm +} diff --git a/vendor/github.com/gdamore/encoding/go.mod b/vendor/github.com/gdamore/encoding/go.mod new file mode 100644 index 0000000000..e91b30d5a6 --- /dev/null +++ b/vendor/github.com/gdamore/encoding/go.mod @@ -0,0 +1,5 @@ +module github.com/gdamore/encoding + +go 1.9 + +require golang.org/x/text v0.3.0 diff --git a/vendor/github.com/gdamore/encoding/go.sum b/vendor/github.com/gdamore/encoding/go.sum new file mode 100644 index 0000000000..6bad37b2a7 --- /dev/null +++ b/vendor/github.com/gdamore/encoding/go.sum @@ -0,0 +1,2 @@ +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/vendor/github.com/gdamore/encoding/latin1.go b/vendor/github.com/gdamore/encoding/latin1.go new file mode 100644 index 0000000000..226bf01d0f --- /dev/null +++ b/vendor/github.com/gdamore/encoding/latin1.go @@ -0,0 +1,33 @@ +// Copyright 2015 Garrett D'Amore +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package encoding + +import ( + "golang.org/x/text/encoding" +) + +// ISO8859_1 represents the 8-bit ISO8859-1 scheme. It decodes directly to +// UTF-8 without change, as all ISO8859-1 values are legal UTF-8. +// Unicode values less than 256 (i.e. 8 bits) map 1:1 with 8859-1. +// It encodes runes outside of that to 0x1A, the ASCII substitution character. +var ISO8859_1 encoding.Encoding + +func init() { + cm := &Charmap{} + cm.Init() + + // 8859-1 is the 8-bit identity map for Unicode. + ISO8859_1 = cm +} diff --git a/vendor/github.com/gdamore/encoding/latin5.go b/vendor/github.com/gdamore/encoding/latin5.go new file mode 100644 index 0000000000..c75ecf2753 --- /dev/null +++ b/vendor/github.com/gdamore/encoding/latin5.go @@ -0,0 +1,35 @@ +// Copyright 2015 Garrett D'Amore +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package encoding + +import ( + "golang.org/x/text/encoding" +) + +// ISO8859_9 represents the 8-bit ISO8859-9 scheme. +var ISO8859_9 encoding.Encoding + +func init() { + cm := &Charmap{Map: map[byte]rune{ + 0xD0: 'Ğ', + 0xDD: 'İ', + 0xDE: 'Ş', + 0xF0: 'ğ', + 0xFD: 'ı', + 0xFE: 'ş', + }} + cm.Init() + ISO8859_9 = cm +} diff --git a/vendor/github.com/gdamore/encoding/utf8.go b/vendor/github.com/gdamore/encoding/utf8.go new file mode 100644 index 0000000000..2d59f4b39d --- /dev/null +++ b/vendor/github.com/gdamore/encoding/utf8.go @@ -0,0 +1,35 @@ +// Copyright 2015 Garrett D'Amore +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package encoding + +import ( + "golang.org/x/text/encoding" +) + +type validUtf8 struct{} + +// UTF8 is an encoding for UTF-8. All it does is verify that the UTF-8 +// in is valid. The main reason for its existence is that it will detect +// and report ErrSrcShort or ErrDstShort, whereas the Nop encoding just +// passes every byte, blithely. +var UTF8 encoding.Encoding = validUtf8{} + +func (validUtf8) NewDecoder() *encoding.Decoder { + return &encoding.Decoder{Transformer: encoding.UTF8Validator} +} + +func (validUtf8) NewEncoder() *encoding.Encoder { + return &encoding.Encoder{Transformer: encoding.UTF8Validator} +} diff --git a/vendor/github.com/gdamore/tcell/v2/.appveyor.yml b/vendor/github.com/gdamore/tcell/v2/.appveyor.yml new file mode 100644 index 0000000000..435dfe3a83 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/.appveyor.yml @@ -0,0 +1,13 @@ +version: 1.0.{build} +clone_folder: c:\gopath\src\github.com\gdamore\tcell +environment: + GOPATH: c:\gopath +build_script: +- go version +- go env +- SET PATH=%LOCALAPPDATA%\atom\bin;%GOPATH%\bin;%PATH% +- go get -t ./... +- go build +- go install ./... +test_script: +- go test ./... diff --git a/vendor/github.com/gdamore/tcell/v2/.gitignore b/vendor/github.com/gdamore/tcell/v2/.gitignore new file mode 100644 index 0000000000..c57100a595 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/.gitignore @@ -0,0 +1 @@ +coverage.txt diff --git a/vendor/github.com/gdamore/tcell/v2/.travis.yml b/vendor/github.com/gdamore/tcell/v2/.travis.yml new file mode 100644 index 0000000000..967b5b3357 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/.travis.yml @@ -0,0 +1,18 @@ +language: go + +go: + - 1.15.x + - master + +arch: + - amd64 + - ppc64le + +before_install: + - go get -t -v ./... + +script: + - go test -race -coverprofile=coverage.txt -covermode=atomic + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/gdamore/tcell/v2/AUTHORS b/vendor/github.com/gdamore/tcell/v2/AUTHORS new file mode 100644 index 0000000000..53f87ee639 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/AUTHORS @@ -0,0 +1,4 @@ +Garrett D'Amore +Zachary Yedidia +Junegunn Choi +Staysail Systems, Inc. diff --git a/vendor/github.com/gdamore/tcell/v2/CHANGESv2.md b/vendor/github.com/gdamore/tcell/v2/CHANGESv2.md new file mode 100644 index 0000000000..ad97c11b5b --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/CHANGESv2.md @@ -0,0 +1,82 @@ +## Breaking Changes in _Tcell_ v2 + +A number of changes were made to _Tcell_ for version two, and some of these are breaking. + +### Import Path + +The import path for tcell has changed to `github.com/gdamore/tcell/v2` to reflect a new major version. + +### Style Is Not Numeric + +The type `Style` has changed to a structure, to allow us to add additional data such as flags for color setting, +more attribute bits, and so forth. +Applications that relied on this being a number will need to be updated to use the accessor methods. + +### Mouse Event Changes + +The middle mouse button was reported as button 2 on Linux, but as button 3 on Windows, +and the right mouse button was reported the reverse way. +_Tcell_ now always reports the right mouse button as button 2, and the middle button as button 3. +To help make this clearer, new symbols `ButtonPrimary`, `ButtonSecondary`, and +`ButtonMiddle` are provided. +(Note that which button is right vs. left may be impacted by user preferences. +Usually the left button will be considered the Primary, and the right will be the Secondary.) +Applications may need to adjust their handling of mouse buttons 2 and 3 accordingly. + +### Terminals Removed + +A number of terminals have been removed. +These are mostly ancient definitions unlikely to be used by anyone, such as `adm3a`. + +### High Number Function Keys + +Historically terminfo reported function keys with modifiers set as a different +function key altogether. For example, Shift-F1 was reported as F13 on XTerm. +_Tcell_ now prefers to report these using the base key (such as F1) with modifiers added. +This works on XTerm and VTE based emulators, but some emulators may not support this. +The new behavior more closely aligns with behavior on Windows platforms. + +## New Features in _Tcell_ v2 + +These features are not breaking, but are introduced in version 2. + +### Improved Modifier Support + +For terminals that appear to behave like the venerable XTerm, _tcell_ +automatically adds modifier reporting for ALT, CTRL, SHIFT, and META keys +when the terminal reports them. + +### Better Support for Palettes (Themes) + +When using a color by its name or palette entry, _Tcell_ now tries to +use that palette entry as is; this should avoid some inconsistency and respect +terminal themes correctly. + +When true fidelity to RGB values is needed, the new `TrueColor()` API can be used +to create a direct color, which bypasses the palette altogether. + +### Automatic TrueColor Detection + +For some terminals, if the `Tc` or `RGB` properties are present in terminfo, +_Tcell_ will automatically assume the terminal supports 24-bit color. + +### ColorReset + +A new color value, `ColorReset` can be used on the foreground or background +to reset the color the default used by the terminal. + +### tmux Support + +_Tcell_ now has improved support for tmux, when the `$TERM` variable is set to "tmux". + +### Strikethrough Support + +_Tcell_ has support for strikethrough when the terminal supports it, using the new `StrikeThrough()` API. + +### Bracketed Paste Support + +_Tcell_ provides the long requested capability to discriminate paste event by using the +bracketed-paste capability present in some terminals. This is automatically available on +terminals that support XTerm style mouse handling, but applications must opt-in to this +by using the new `EnablePaste()` function. A new `EventPaste` type of event will be +delivered when starting and finishing a paste operation. \ No newline at end of file diff --git a/vendor/github.com/gdamore/tcell/v2/LICENSE b/vendor/github.com/gdamore/tcell/v2/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/gdamore/tcell/v2/README.md b/vendor/github.com/gdamore/tcell/v2/README.md new file mode 100644 index 0000000000..b7727e081a --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/README.md @@ -0,0 +1,272 @@ + +# ![Tcell](logos/tcell.png) + +_Tcell_ is a _Go_ package that provides a cell based view for text terminals, like _XTerm_. +It was inspired by _termbox_, but includes many additional improvements. + +[![Linux Status](https://img.shields.io/travis/gdamore/tcell.svg?label=linux)](https://travis-ci.org/gdamore/tcell) +[![Windows Status](https://img.shields.io/appveyor/ci/gdamore/tcell.svg?label=windows)](https://ci.appveyor.com/project/gdamore/tcell) +[![Apache License](https://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/gdamore/tcell/blob/master/LICENSE) +[![Documentation](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/gdamore/tcell/v2) +[![Report Card](https://goreportcard.com/badge/gdamore/tcell)](http://goreportcard.com/report/gdamore/tcell/v2) +[![Discord](https://img.shields.io/discord/639503822733180969?label=discord)](https://discord.gg/urTTxDN) +[![Coverage](https://codecov.io/gh/gdamore/tcell/branch/master/graph/badge.svg)](https://codecov.io/gh/gdamore/tcell) + + +NOTE: This is version 2 of _Tcell_. There are breaking changes relative to version 1. +Version 1.x remains available using the import `github.com/gdamore/tcell`. + +## Tutorial + +A brief, and still somewhat rough, [tutorial](TUTORIAL.md) is available. +## Examples + +* [proxima5](https://github.com/gdamore/proxima5) - space shooter ([video](https://youtu.be/jNxKTCmY_bQ)) +* [govisor](https://github.com/gdamore/govisor) - service management UI ([screenshot](http://2.bp.blogspot.com/--OsvnfzSNow/Vf7aqMw3zXI/AAAAAAAAARo/uOMtOvw4Sbg/s1600/Screen%2BShot%2B2015-09-20%2Bat%2B9.08.41%2BAM.png)) +* mouse demo - included mouse test ([screenshot](http://2.bp.blogspot.com/-fWvW5opT0es/VhIdItdKqJI/AAAAAAAAATE/7Ojc0L1SpB0/s1600/Screen%2BShot%2B2015-10-04%2Bat%2B11.47.13%2BPM.png)) +* [gomatrix](https://github.com/gdamore/gomatrix) - converted from Termbox +* [micro](https://github.com/zyedidia/micro/) - lightweight text editor with syntax-highlighting and themes +* [godu](https://github.com/viktomas/godu) - utility to discover large files/folders +* [tview](https://github.com/rivo/tview/) - rich interactive widgets +* [cview](https://code.rocketnine.space/tslocum/cview) - user interface toolkit (fork of _tview_) +* [awsome gocui](https://github.com/awesome-gocui/gocui) - Go Console User Interface +* [gomandelbrot](https://github.com/rgm3/gomandelbrot) - Mandelbrot! +* [WTF](https://github.com/senorprogrammer/wtf) - personal information dashboard +* [browsh](https://github.com/browsh-org/browsh) - modern web browser ([video](https://www.youtube.com/watch?v=HZq86XfBoRo)) +* [go-life](https://github.com/sachaos/go-life) - Conway's Game of Life +* [gowid](https://github.com/gcla/gowid) - compositional widgets for terminal UIs, inspired by _urwid_ +* [termshark](https://termshark.io) - interface for _tshark_, inspired by Wireshark, built on _gowid_ +* [go-tetris](https://github.com/MichaelS11/go-tetris) - Go Tetris with AI option +* [fzf](https://github.com/junegunn/fzf) - command-line fuzzy finder +* [ascii-fluid](https://github.com/esimov/ascii-fluid) - fluid simulation controlled by webcam +* [cbind](https://code.rocketnine.space/tslocum/cbind) - key event encoding, decoding and handling +* [tpong](https://github.com/spinzed/tpong) - old-school Pong +* [aerc](https://git.sr.ht/~sircmpwn/aerc) - email client +* [tblogs](https://github.com/ezeoleaf/tblogs) - development blogs reader +* [spinc](https://github.com/lallassu/spinc) - _irssi_ inspired chat application for Cisco Spark/WebEx +* [gorss](https://github.com/lallassu/gorss) - RSS/Atom feed reader +* [memoryalike](https://github.com/Bios-Marcel/memoryalike) - memorization game +* [lf](https://github.com/gokcehan/lf) - file manager +* [gokeybr](https://github.com/bunyk/gokeybr) - deliberately practice your typing +* [gonano](https://github.com/jbaramidze/gonano) - editor, mimics _nano_ +* [uchess](https://github.com/tmountain/uchess) - UCI chess client +* [min](https://github.com/a-h/min) - Gemini browser +* [ov](https://github.com/noborus/ov) - file pager +* [tmux-wormhole](https://github.com/gcla/tmux-wormhole) - _tmux_ plugin to transfer files +* [gruid-tcell](https://github.com/anaseto/gruid-tcell) - driver for the grid based UI and game framework +* [aretext](https://github.com/aretext/aretext) - minimalist text editor with _vim_ key bindings + +## Pure Go Terminfo Database + +_Tcell_ includes a full parser and expander for terminfo capability strings, +so that it can avoid hard coding escape strings for formatting. It also favors +portability, and includes support for all POSIX systems. + +The database is also flexible & extensible, and can be modified by either running +a program to build the entire database, or an entry for just a single terminal. + +## More Portable + +_Tcell_ is portable to a wide variety of systems, and is pure Go, without +any need for CGO. +_Tcell_ is believed to work with mainstream systems officially supported by golang. + +## No Async IO + +_Tcell_ is able to operate without requiring `SIGIO` signals (unlike _termbox_), +or asynchronous I/O, and can instead use standard Go file objects and Go routines. +This means it should be safe, especially for +use with programs that use exec, or otherwise need to manipulate the tty streams. +This model is also much closer to idiomatic Go, leading to fewer surprises. + +## Rich Unicode & non-Unicode support + +_Tcell_ includes enhanced support for Unicode, including wide characters and +combining characters, provided your terminal can support them. +Note that +Windows terminals generally don't support the full Unicode repertoire. + +It will also convert to and from Unicode locales, so that the program +can work with UTF-8 internally, and get reasonable output in other locales. +_Tcell_ tries hard to convert to native characters on both input and output. +On output _Tcell_ even makes use of the alternate character set to facilitate +drawing certain characters. + +## More Function Keys + +_Tcell_ also has richer support for a larger number of special keys that some +terminals can send. + +## Better Color Handling + +_Tcell_ will respect your terminal's color space as specified within your terminfo entries. +For example attempts to emit color sequences on VT100 terminals +won't result in unintended consequences. + +In legacy Windows mode, _Tcell_ supports 16 colors, bold, dim, and reverse, +instead of just termbox's 8 colors with reverse. (Note that there is some +conflation with bold/dim and colors.) +Modern Windows 10 can benefit from much richer colors however. + +_Tcell_ maps 16 colors down to 8, for terminals that need it. +(The upper 8 colors are just brighter versions of the lower 8.) + +## Better Mouse Support + +_Tcell_ supports enhanced mouse tracking mode, so your application can receive +regular mouse motion events, and wheel events, if your terminal supports it. + +(Note: The Windows 10 Terminal application suffers from a flaw in this regard, +and does not support mouse interaction. The stock Windows 10 console host +fired up with cmd.exe or PowerShell works fine however.) + +## _Termbox_ Compatibility + +A compatibility layer for _termbox_ is provided in the `compat` directory. +To use it, try importing `github.com/gdamore/tcell/termbox` instead. +Most _termbox-go_ programs will probably work without further modification. + +## Working With Unicode + +Internally _Tcell_ uses UTF-8, just like Go. +However, _Tcell_ understands how to +convert to and from other character sets, using the capabilities of +the `golang.org/x/text/encoding packages`. +Your application must supply +them, as the full set of the most common ones bloats the program by about 2 MB. +If you're lazy, and want them all anyway, see the `encoding` sub-directory. + +## Wide & Combining Characters + +The `SetContent()` API takes a primary rune, and an optional list of combining runes. +If any of the runes is a wide (East Asian) rune occupying two cells, +then the library will skip output from the following cell. Care must be +taken in the application to avoid explicitly attempting to set content in the +next cell, otherwise the results are undefined. (Normally the wide character +is displayed, and the other character is not; do not depend on that behavior.) + +Older terminal applications (especially on systems like Windows 8) lack support +for advanced Unicode, and thus may not fare well. + +## Colors + +_Tcell_ assumes the ANSI/XTerm color model, including the 256 color map that +XTerm uses when it supports 256 colors. The terminfo guidance will be +honored, with respect to the number of colors supported. Also, only +terminals which expose ANSI style `setaf` and `setab` will support color; +if you have a color terminal that only has `setf` and `setb`, please submit +a ticket. + +## 24-bit Color + +_Tcell_ _supports 24-bit color!_ (That is, if your terminal can support it.) + +NOTE: Technically the approach of using 24-bit RGB values for color is more +accurately described as "direct color", but most people use the term "true color". +We follow the (inaccurate) common convention. + +There are a few ways you can enable (or disable) true color. + +* For many terminals, we can detect it automatically if your terminal +includes the `RGB` or `Tc` capabilities (or rather it did when the database +was updated.) + +* You can force this one by setting the `COLORTERM` environment variable to +`24-bit`, `truecolor` or `24bit`. This is the same method used +by most other terminal applications that support 24-bit color. + +* If you set your `TERM` environment variable to a value with the suffix `-truecolor` +then 24-bit color compatible with XTerm and ECMA-48 will be assumed. +(This feature is deprecated. +It is recommended to use one of other methods listed above.) + +* You can disable 24-bit color by setting `TCELL_TRUECOLOR=disable` in your +environment. + +When using TrueColor, programs will display the colors that the programmer +intended, overriding any "`themes`" you may have set in your terminal +emulator. (For some cases, accurate color fidelity is more important +than respecting themes. For other cases, such as typical text apps that +only use a few colors, its more desirable to respect the themes that +the user has established.) + +## Performance + +Reasonable attempts have been made to minimize sending data to terminals, +avoiding repeated sequences or drawing the same cell on refresh updates. + +## Terminfo + +(Not relevant for Windows users.) + +The Terminfo implementation operates with a built-in database. +This should satisfy most users. However, it can also (on systems +with ncurses installed), dynamically parse the output from `infocmp` +for terminals it does not already know about. + +See the `terminfo/` directory for more information about generating +new entries for the built-in database. + +_Tcell_ requires that the terminal support the `cup` mode of cursor addressing. +Ancient terminals without the ability to position the cursor directly +are not supported. +This is unlikely to be a problem; such terminals have not been mass-produced +since the early 1970s. + +## Mouse Support + +Mouse support is detected via the `kmous` terminfo variable, however, +enablement/disablement and decoding mouse events is done using hard coded +sequences based on the XTerm X11 model. All popular +terminals with mouse tracking support this model. (Full terminfo support +is not possible as terminfo sequences are not defined.) + +On Windows, the mouse works normally. + +Mouse wheel buttons on various terminals are known to work, but the support +in terminal emulators, as well as support for various buttons and +live mouse tracking, varies widely. +Modern _xterm_, macOS _Terminal_, and _iTerm_ all work well. + +## Bracketed Paste + +Terminals that appear to support the XTerm mouse model also can support +bracketed paste, for applications that opt-in. See `EnablePaste()` for details. + +## Testability + +There is a `SimulationScreen`, that can be used to simulate a real screen +for automated testing. The supplied tests do this. The simulation contains +event delivery, screen resizing support, and capabilities to inject events +and examine "`physical`" screen contents. + +## Platforms + +### POSIX (Linux, FreeBSD, macOS, Solaris, etc.) + +Everything works using pure Go on mainstream platforms. Some more esoteric +platforms (e.g., AIX) may need to be added. Pull requests are welcome! + +### Windows + +Windows console mode applications are supported. + +Modern console applications like ConEmu and the Windows 10 terminal, +support all the good features (resize, mouse tracking, etc.) + +### Plan9, WASM, and others + +These platforms won't work, but compilation stubs are supplied +for folks that want to include parts of this in software for those +platforms. The Simulation screen works, but as _Tcell_ doesn't know how to +allocate a real screen object on those platforms, `NewScreen()` will fail. + +If anyone has wisdom about how to improve support for these, +please let me know. PRs are especially welcome. + +### Commercial Support + +_Tcell_ is absolutely free, but if you want to obtain commercial, professional support, there are options. + +* [TideLift](https://tidelift.com/) subscriptions include support for _Tcell_, as well as many other open source packages. +* [Staysail Systems Inc.](mailto:info@staysail.tech) offers direct support, and custom development around _Tcell_ on an hourly basis. diff --git a/vendor/github.com/gdamore/tcell/v2/TUTORIAL.md b/vendor/github.com/gdamore/tcell/v2/TUTORIAL.md new file mode 100644 index 0000000000..8d14f33115 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/TUTORIAL.md @@ -0,0 +1,293 @@ +# _Tcell_ Tutorial + +_Tcell_ provides a low-level, portable API for building terminal-based programs. +A [terminal emulator](https://en.wikipedia.org/wiki/Terminal_emulator) +(or a real terminal such as a DEC VT-220) is used to interact with such a program. + +_Tcell_'s interface is fairly low-level. +While it provides a reasonably portable way of dealing with all the usual terminal +features, it may be easier to utilize a higher level framework. +A number of such frameworks are listed on the _Tcell_ main [README](README.md). + +This tutorial provides the details of _Tcell_, and is appropriate for developers +wishing to create their own application frameworks or needing more direct access +to the terminal capabilities. + +## Resize events + +Applications receive an event of type `EventResize` when they are first initialized and each time the terminal is resized. +The new size is available as `Size`. + +```golang +switch ev := ev.(type) { +case *tcell.EventResize: + w, h := ev.Size() + logMessage(fmt.Sprintf("Resized to %dx%d", w, h)) +} +``` + +## Key events + +When a key is pressed, applications receive an event of type `EventKey`. +This event describes the modifier keys pressed (if any) and the pressed key or rune. + +When a rune key is pressed, an event with its `Key` set to `KeyRune` is dispatched. + +When a non-rune key is pressed, it is available as the `Key` of the event. + +```golang +switch ev := ev.(type) { +case *tcell.EventKey: + mod, key, ch := ev.Mod(), ev.Key(), ev.Rune() + logMessage(fmt.Sprintf("EventKey Modifiers: %d Key: %d Rune: %d", mod, key, ch)) +} +``` + +### Key event restrictions + +Terminal-based programs have less visibility into keyboard activity than graphical applications. + +When a key is pressed and held, additional key press events are sent by the terminal emulator. +The rate of these repeated events depends on the emulator's configuration. +Key release events are not available. + +It is not possible to distinguish runes typed while holding shift and runes typed using caps lock. +Capital letters are reported without the Shift modifier. + +## Mouse events + +Applications receive an event of type `EventMouse` when the mouse moves, or a mouse button is pressed or released. +Mouse events are only delivered if +`EnableMouse` has been called. + +The mouse buttons being pressed (if any) are available as `Buttons`, and the position of the mouse is available as `Position`. + +```golang +switch ev := ev.(type) { +case *tcell.EventMouse: + mod := ev.Modifiers() + btns := ev.Buttons() + x, y := ev.Position() + logMessage(fmt.Sprintf("EventMouse Modifiers: %d Buttons: %d Position: %d,%d", mod, btns, x, y)) +} +``` + +### Mouse buttons + +Identifier | Alias | Description +-----------|-----------------|----------- +Button1 | ButtonPrimary | Left button +Button2 | ButtonSecondary | Right button +Button3 | ButtonMiddle | Middle button +Button4 | | Side button (thumb/next) +Button5 | | Side button (thumb/prev) + +## Usage + +To create a tcell application, first initialize a screen to hold it. + +```golang +s, err := tcell.NewScreen() +if err != nil { + log.Fatalf("%+v", err) +} +if err := s.Init(); err != nil { + log.Fatalf("%+v", err) +} + +// Set default text style +defStyle := tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.ColorReset) +s.SetStyle(defStyle) + +// Clear screen +s.Clear() +``` + +Text may be drawn on the screen using `SetContent`. + +```golang +s.SetContent(0, 0, 'H', nil, defStyle) +s.SetContent(1, 0, 'i', nil, defStyle) +s.SetContent(2, 0, '!', nil, defStyle) +``` + +To draw text more easily, define a render function. + +```golang +func drawText(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string) { + row := y1 + col := x1 + for _, r := range []rune(text) { + s.SetContent(col, row, r, nil, style) + col++ + if col >= x2 { + row++ + col = x1 + } + if row > y2 { + break + } + } +} +``` + +Lastly, define an event loop to handle user input and update application state. + +```golang +quit := func() { + s.Fini() + os.Exit(0) +} +for { + // Update screen + s.Show() + + // Poll event + ev := s.PollEvent() + + // Process event + switch ev := ev.(type) { + case *tcell.EventResize: + s.Sync() + case *tcell.EventKey: + if ev.Key() == tcell.KeyEscape || ev.Key() == tcell.KeyCtrlC { + quit() + } + } +} +``` + +## Demo application + +The following demonstrates how to initialize a screen, draw text/graphics and handle user input. + +```golang +package main + +import ( + "fmt" + "log" + "os" + + "github.com/gdamore/tcell/v2" +) + +func drawText(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string) { + row := y1 + col := x1 + for _, r := range []rune(text) { + s.SetContent(col, row, r, nil, style) + col++ + if col >= x2 { + row++ + col = x1 + } + if row > y2 { + break + } + } +} + +func drawBox(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string) { + if y2 < y1 { + y1, y2 = y2, y1 + } + if x2 < x1 { + x1, x2 = x2, x1 + } + + // Fill background + for row := y1; row <= y2; row++ { + for col := x1; col <= x2; col++ { + s.SetContent(col, row, ' ', nil, style) + } + } + + // Draw borders + for col := x1; col <= x2; col++ { + s.SetContent(col, y1, tcell.RuneHLine, nil, style) + s.SetContent(col, y2, tcell.RuneHLine, nil, style) + } + for row := y1 + 1; row < y2; row++ { + s.SetContent(x1, row, tcell.RuneVLine, nil, style) + s.SetContent(x2, row, tcell.RuneVLine, nil, style) + } + + // Only draw corners if necessary + if y1 != y2 && x1 != x2 { + s.SetContent(x1, y1, tcell.RuneULCorner, nil, style) + s.SetContent(x2, y1, tcell.RuneURCorner, nil, style) + s.SetContent(x1, y2, tcell.RuneLLCorner, nil, style) + s.SetContent(x2, y2, tcell.RuneLRCorner, nil, style) + } + + drawText(s, x1+1, y1+1, x2-1, y2-1, style, text) +} + +func main() { + defStyle := tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.ColorReset) + boxStyle := tcell.StyleDefault.Foreground(tcell.ColorWhite).Background(tcell.ColorPurple) + + // Initialize screen + s, err := tcell.NewScreen() + if err != nil { + log.Fatalf("%+v", err) + } + if err := s.Init(); err != nil { + log.Fatalf("%+v", err) + } + s.SetStyle(defStyle) + s.EnableMouse() + s.EnablePaste() + s.Clear() + + // Draw initial boxes + drawBox(s, 1, 1, 42, 7, boxStyle, "Click and drag to draw a box") + drawBox(s, 5, 9, 32, 14, boxStyle, "Press C to reset") + + // Event loop + ox, oy := -1, -1 + quit := func() { + s.Fini() + os.Exit(0) + } + for { + // Update screen + s.Show() + + // Poll event + ev := s.PollEvent() + + // Process event + switch ev := ev.(type) { + case *tcell.EventResize: + s.Sync() + case *tcell.EventKey: + if ev.Key() == tcell.KeyEscape || ev.Key() == tcell.KeyCtrlC { + quit() + } else if ev.Key() == tcell.KeyCtrlL { + s.Sync() + } else if ev.Rune() == 'C' || ev.Rune() == 'c' { + s.Clear() + } + case *tcell.EventMouse: + x, y := ev.Position() + button := ev.Buttons() + // Only process button events, not wheel events + button &= tcell.ButtonMask(0xff) + + if button != tcell.ButtonNone && ox < 0 { + ox, oy = x, y + } + switch ev.Buttons() { + case tcell.ButtonNone: + if ox >= 0 { + label := fmt.Sprintf("%d,%d to %d,%d", ox, oy, x, y) + drawBox(s, ox, oy, x, y, boxStyle, label) + ox, oy = -1, -1 + } + } + } + } +} +``` diff --git a/vendor/github.com/gdamore/tcell/v2/attr.go b/vendor/github.com/gdamore/tcell/v2/attr.go new file mode 100644 index 0000000000..8b1eab7758 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/attr.go @@ -0,0 +1,33 @@ +// Copyright 2020 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +// AttrMask represents a mask of text attributes, apart from color. +// Note that support for attributes may vary widely across terminals. +type AttrMask int + +// Attributes are not colors, but affect the display of text. They can +// be combined. +const ( + AttrBold AttrMask = 1 << iota + AttrBlink + AttrReverse + AttrUnderline + AttrDim + AttrItalic + AttrStrikeThrough + AttrInvalid // Mark the style or attributes invalid + AttrNone AttrMask = 0 // Just normal text. +) diff --git a/vendor/github.com/gdamore/tcell/v2/cell.go b/vendor/github.com/gdamore/tcell/v2/cell.go new file mode 100644 index 0000000000..c7f8f1ade1 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/cell.go @@ -0,0 +1,177 @@ +// Copyright 2019 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + runewidth "github.com/mattn/go-runewidth" +) + +type cell struct { + currMain rune + currComb []rune + currStyle Style + lastMain rune + lastStyle Style + lastComb []rune + width int +} + +// CellBuffer represents a two dimensional array of character cells. +// This is primarily intended for use by Screen implementors; it +// contains much of the common code they need. To create one, just +// declare a variable of its type; no explicit initialization is necessary. +// +// CellBuffer is not thread safe. +type CellBuffer struct { + w int + h int + cells []cell +} + +// SetContent sets the contents (primary rune, combining runes, +// and style) for a cell at a given location. +func (cb *CellBuffer) SetContent(x int, y int, + mainc rune, combc []rune, style Style) { + + if x >= 0 && y >= 0 && x < cb.w && y < cb.h { + c := &cb.cells[(y*cb.w)+x] + + c.currComb = append([]rune{}, combc...) + + if c.currMain != mainc { + c.width = runewidth.RuneWidth(mainc) + } + c.currMain = mainc + c.currStyle = style + } +} + +// GetContent returns the contents of a character cell, including the +// primary rune, any combining character runes (which will usually be +// nil), the style, and the display width in cells. (The width can be +// either 1, normally, or 2 for East Asian full-width characters.) +func (cb *CellBuffer) GetContent(x, y int) (rune, []rune, Style, int) { + var mainc rune + var combc []rune + var style Style + var width int + if x >= 0 && y >= 0 && x < cb.w && y < cb.h { + c := &cb.cells[(y*cb.w)+x] + mainc, combc, style = c.currMain, c.currComb, c.currStyle + if width = c.width; width == 0 || mainc < ' ' { + width = 1 + mainc = ' ' + } + } + return mainc, combc, style, width +} + +// Size returns the (width, height) in cells of the buffer. +func (cb *CellBuffer) Size() (int, int) { + return cb.w, cb.h +} + +// Invalidate marks all characters within the buffer as dirty. +func (cb *CellBuffer) Invalidate() { + for i := range cb.cells { + cb.cells[i].lastMain = rune(0) + } +} + +// Dirty checks if a character at the given location needs an +// to be refreshed on the physical display. This returns true +// if the cell content is different since the last time it was +// marked clean. +func (cb *CellBuffer) Dirty(x, y int) bool { + if x >= 0 && y >= 0 && x < cb.w && y < cb.h { + c := &cb.cells[(y*cb.w)+x] + if c.lastMain == rune(0) { + return true + } + if c.lastMain != c.currMain { + return true + } + if c.lastStyle != c.currStyle { + return true + } + if len(c.lastComb) != len(c.currComb) { + return true + } + for i := range c.lastComb { + if c.lastComb[i] != c.currComb[i] { + return true + } + } + } + return false +} + +// SetDirty is normally used to indicate that a cell has +// been displayed (in which case dirty is false), or to manually +// force a cell to be marked dirty. +func (cb *CellBuffer) SetDirty(x, y int, dirty bool) { + if x >= 0 && y >= 0 && x < cb.w && y < cb.h { + c := &cb.cells[(y*cb.w)+x] + if dirty { + c.lastMain = rune(0) + } else { + if c.currMain == rune(0) { + c.currMain = ' ' + } + c.lastMain = c.currMain + c.lastComb = c.currComb + c.lastStyle = c.currStyle + } + } +} + +// Resize is used to resize the cells array, with different dimensions, +// while preserving the original contents. The cells will be invalidated +// so that they can be redrawn. +func (cb *CellBuffer) Resize(w, h int) { + + if cb.h == h && cb.w == w { + return + } + + newc := make([]cell, w*h) + for y := 0; y < h && y < cb.h; y++ { + for x := 0; x < w && x < cb.w; x++ { + oc := &cb.cells[(y*cb.w)+x] + nc := &newc[(y*w)+x] + nc.currMain = oc.currMain + nc.currComb = oc.currComb + nc.currStyle = oc.currStyle + nc.width = oc.width + nc.lastMain = rune(0) + } + } + cb.cells = newc + cb.h = h + cb.w = w +} + +// Fill fills the entire cell buffer array with the specified character +// and style. Normally choose ' ' to clear the screen. This API doesn't +// support combining characters, or characters with a width larger than one. +func (cb *CellBuffer) Fill(r rune, style Style) { + for i := range cb.cells { + c := &cb.cells[i] + c.currMain = r + c.currComb = nil + c.currStyle = style + c.width = 1 + } +} diff --git a/vendor/github.com/gdamore/tcell/v2/charset_stub.go b/vendor/github.com/gdamore/tcell/v2/charset_stub.go new file mode 100644 index 0000000000..c1c1594c74 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/charset_stub.go @@ -0,0 +1,21 @@ +// +build plan9 nacl + +// Copyright 2015 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +func getCharset() string { + return "" +} diff --git a/vendor/github.com/gdamore/tcell/v2/charset_unix.go b/vendor/github.com/gdamore/tcell/v2/charset_unix.go new file mode 100644 index 0000000000..d9f9d8e1fe --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/charset_unix.go @@ -0,0 +1,49 @@ +// +build !windows,!nacl,!plan9 + +// Copyright 2016 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + "os" + "strings" +) + +func getCharset() string { + // Determine the character set. This can help us later. + // Per POSIX, we search for LC_ALL first, then LC_CTYPE, and + // finally LANG. First one set wins. + locale := "" + if locale = os.Getenv("LC_ALL"); locale == "" { + if locale = os.Getenv("LC_CTYPE"); locale == "" { + locale = os.Getenv("LANG") + } + } + if locale == "POSIX" || locale == "C" { + return "US-ASCII" + } + if i := strings.IndexRune(locale, '@'); i >= 0 { + locale = locale[:i] + } + if i := strings.IndexRune(locale, '.'); i >= 0 { + locale = locale[i+1:] + } else { + // Default assumption, and on Linux we can see LC_ALL + // without a character set, which we assume implies UTF-8. + return "UTF-8" + } + // XXX: add support for aliases + return locale +} diff --git a/vendor/github.com/gdamore/tcell/v2/charset_windows.go b/vendor/github.com/gdamore/tcell/v2/charset_windows.go new file mode 100644 index 0000000000..2400aa8a3f --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/charset_windows.go @@ -0,0 +1,21 @@ +// +build windows + +// Copyright 2015 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +func getCharset() string { + return "UTF-16" +} diff --git a/vendor/github.com/gdamore/tcell/v2/color.go b/vendor/github.com/gdamore/tcell/v2/color.go new file mode 100644 index 0000000000..8e50fa3022 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/color.go @@ -0,0 +1,1081 @@ +// Copyright 2020 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + ic "image/color" + "strconv" +) + +// Color represents a color. The low numeric values are the same as used +// by ECMA-48, and beyond that XTerm. A 24-bit RGB value may be used by +// adding in the ColorIsRGB flag. For Color names we use the W3C approved +// color names. +// +// We use a 64-bit integer to allow future expansion if we want to add an +// 8-bit alpha, while still leaving us some room for extra options. +// +// Note that on various terminals colors may be approximated however, or +// not supported at all. If no suitable representation for a color is known, +// the library will simply not set any color, deferring to whatever default +// attributes the terminal uses. +type Color uint64 + +const ( + // ColorDefault is used to leave the Color unchanged from whatever + // system or terminal default may exist. It's also the zero value. + ColorDefault Color = 0 + + // ColorValid is used to indicate the color value is actually + // valid (initialized). This is useful to permit the zero value + // to be treated as the default. + ColorValid Color = 1 << 32 + + // ColorIsRGB is used to indicate that the numeric value is not + // a known color constant, but rather an RGB value. The lower + // order 3 bytes are RGB. + ColorIsRGB Color = 1 << 33 + + // ColorSpecial is a flag used to indicate that the values have + // special meaning, and live outside of the color space(s). + ColorSpecial Color = 1 << 34 +) + +// Note that the order of these options is important -- it follows the +// definitions used by ECMA and XTerm. Hence any further named colors +// must begin at a value not less than 256. +const ( + ColorBlack = ColorValid + iota + ColorMaroon + ColorGreen + ColorOlive + ColorNavy + ColorPurple + ColorTeal + ColorSilver + ColorGray + ColorRed + ColorLime + ColorYellow + ColorBlue + ColorFuchsia + ColorAqua + ColorWhite + Color16 + Color17 + Color18 + Color19 + Color20 + Color21 + Color22 + Color23 + Color24 + Color25 + Color26 + Color27 + Color28 + Color29 + Color30 + Color31 + Color32 + Color33 + Color34 + Color35 + Color36 + Color37 + Color38 + Color39 + Color40 + Color41 + Color42 + Color43 + Color44 + Color45 + Color46 + Color47 + Color48 + Color49 + Color50 + Color51 + Color52 + Color53 + Color54 + Color55 + Color56 + Color57 + Color58 + Color59 + Color60 + Color61 + Color62 + Color63 + Color64 + Color65 + Color66 + Color67 + Color68 + Color69 + Color70 + Color71 + Color72 + Color73 + Color74 + Color75 + Color76 + Color77 + Color78 + Color79 + Color80 + Color81 + Color82 + Color83 + Color84 + Color85 + Color86 + Color87 + Color88 + Color89 + Color90 + Color91 + Color92 + Color93 + Color94 + Color95 + Color96 + Color97 + Color98 + Color99 + Color100 + Color101 + Color102 + Color103 + Color104 + Color105 + Color106 + Color107 + Color108 + Color109 + Color110 + Color111 + Color112 + Color113 + Color114 + Color115 + Color116 + Color117 + Color118 + Color119 + Color120 + Color121 + Color122 + Color123 + Color124 + Color125 + Color126 + Color127 + Color128 + Color129 + Color130 + Color131 + Color132 + Color133 + Color134 + Color135 + Color136 + Color137 + Color138 + Color139 + Color140 + Color141 + Color142 + Color143 + Color144 + Color145 + Color146 + Color147 + Color148 + Color149 + Color150 + Color151 + Color152 + Color153 + Color154 + Color155 + Color156 + Color157 + Color158 + Color159 + Color160 + Color161 + Color162 + Color163 + Color164 + Color165 + Color166 + Color167 + Color168 + Color169 + Color170 + Color171 + Color172 + Color173 + Color174 + Color175 + Color176 + Color177 + Color178 + Color179 + Color180 + Color181 + Color182 + Color183 + Color184 + Color185 + Color186 + Color187 + Color188 + Color189 + Color190 + Color191 + Color192 + Color193 + Color194 + Color195 + Color196 + Color197 + Color198 + Color199 + Color200 + Color201 + Color202 + Color203 + Color204 + Color205 + Color206 + Color207 + Color208 + Color209 + Color210 + Color211 + Color212 + Color213 + Color214 + Color215 + Color216 + Color217 + Color218 + Color219 + Color220 + Color221 + Color222 + Color223 + Color224 + Color225 + Color226 + Color227 + Color228 + Color229 + Color230 + Color231 + Color232 + Color233 + Color234 + Color235 + Color236 + Color237 + Color238 + Color239 + Color240 + Color241 + Color242 + Color243 + Color244 + Color245 + Color246 + Color247 + Color248 + Color249 + Color250 + Color251 + Color252 + Color253 + Color254 + Color255 + ColorAliceBlue + ColorAntiqueWhite + ColorAquaMarine + ColorAzure + ColorBeige + ColorBisque + ColorBlanchedAlmond + ColorBlueViolet + ColorBrown + ColorBurlyWood + ColorCadetBlue + ColorChartreuse + ColorChocolate + ColorCoral + ColorCornflowerBlue + ColorCornsilk + ColorCrimson + ColorDarkBlue + ColorDarkCyan + ColorDarkGoldenrod + ColorDarkGray + ColorDarkGreen + ColorDarkKhaki + ColorDarkMagenta + ColorDarkOliveGreen + ColorDarkOrange + ColorDarkOrchid + ColorDarkRed + ColorDarkSalmon + ColorDarkSeaGreen + ColorDarkSlateBlue + ColorDarkSlateGray + ColorDarkTurquoise + ColorDarkViolet + ColorDeepPink + ColorDeepSkyBlue + ColorDimGray + ColorDodgerBlue + ColorFireBrick + ColorFloralWhite + ColorForestGreen + ColorGainsboro + ColorGhostWhite + ColorGold + ColorGoldenrod + ColorGreenYellow + ColorHoneydew + ColorHotPink + ColorIndianRed + ColorIndigo + ColorIvory + ColorKhaki + ColorLavender + ColorLavenderBlush + ColorLawnGreen + ColorLemonChiffon + ColorLightBlue + ColorLightCoral + ColorLightCyan + ColorLightGoldenrodYellow + ColorLightGray + ColorLightGreen + ColorLightPink + ColorLightSalmon + ColorLightSeaGreen + ColorLightSkyBlue + ColorLightSlateGray + ColorLightSteelBlue + ColorLightYellow + ColorLimeGreen + ColorLinen + ColorMediumAquamarine + ColorMediumBlue + ColorMediumOrchid + ColorMediumPurple + ColorMediumSeaGreen + ColorMediumSlateBlue + ColorMediumSpringGreen + ColorMediumTurquoise + ColorMediumVioletRed + ColorMidnightBlue + ColorMintCream + ColorMistyRose + ColorMoccasin + ColorNavajoWhite + ColorOldLace + ColorOliveDrab + ColorOrange + ColorOrangeRed + ColorOrchid + ColorPaleGoldenrod + ColorPaleGreen + ColorPaleTurquoise + ColorPaleVioletRed + ColorPapayaWhip + ColorPeachPuff + ColorPeru + ColorPink + ColorPlum + ColorPowderBlue + ColorRebeccaPurple + ColorRosyBrown + ColorRoyalBlue + ColorSaddleBrown + ColorSalmon + ColorSandyBrown + ColorSeaGreen + ColorSeashell + ColorSienna + ColorSkyblue + ColorSlateBlue + ColorSlateGray + ColorSnow + ColorSpringGreen + ColorSteelBlue + ColorTan + ColorThistle + ColorTomato + ColorTurquoise + ColorViolet + ColorWheat + ColorWhiteSmoke + ColorYellowGreen +) + +// These are aliases for the color gray, because some of us spell +// it as grey. +const ( + ColorGrey = ColorGray + ColorDimGrey = ColorDimGray + ColorDarkGrey = ColorDarkGray + ColorDarkSlateGrey = ColorDarkSlateGray + ColorLightGrey = ColorLightGray + ColorLightSlateGrey = ColorLightSlateGray + ColorSlateGrey = ColorSlateGray +) + +// ColorValues maps color constants to their RGB values. +var ColorValues = map[Color]int32{ + ColorBlack: 0x000000, + ColorMaroon: 0x800000, + ColorGreen: 0x008000, + ColorOlive: 0x808000, + ColorNavy: 0x000080, + ColorPurple: 0x800080, + ColorTeal: 0x008080, + ColorSilver: 0xC0C0C0, + ColorGray: 0x808080, + ColorRed: 0xFF0000, + ColorLime: 0x00FF00, + ColorYellow: 0xFFFF00, + ColorBlue: 0x0000FF, + ColorFuchsia: 0xFF00FF, + ColorAqua: 0x00FFFF, + ColorWhite: 0xFFFFFF, + Color16: 0x000000, // black + Color17: 0x00005F, + Color18: 0x000087, + Color19: 0x0000AF, + Color20: 0x0000D7, + Color21: 0x0000FF, // blue + Color22: 0x005F00, + Color23: 0x005F5F, + Color24: 0x005F87, + Color25: 0x005FAF, + Color26: 0x005FD7, + Color27: 0x005FFF, + Color28: 0x008700, + Color29: 0x00875F, + Color30: 0x008787, + Color31: 0x0087Af, + Color32: 0x0087D7, + Color33: 0x0087FF, + Color34: 0x00AF00, + Color35: 0x00AF5F, + Color36: 0x00AF87, + Color37: 0x00AFAF, + Color38: 0x00AFD7, + Color39: 0x00AFFF, + Color40: 0x00D700, + Color41: 0x00D75F, + Color42: 0x00D787, + Color43: 0x00D7AF, + Color44: 0x00D7D7, + Color45: 0x00D7FF, + Color46: 0x00FF00, // lime + Color47: 0x00FF5F, + Color48: 0x00FF87, + Color49: 0x00FFAF, + Color50: 0x00FFd7, + Color51: 0x00FFFF, // aqua + Color52: 0x5F0000, + Color53: 0x5F005F, + Color54: 0x5F0087, + Color55: 0x5F00AF, + Color56: 0x5F00D7, + Color57: 0x5F00FF, + Color58: 0x5F5F00, + Color59: 0x5F5F5F, + Color60: 0x5F5F87, + Color61: 0x5F5FAF, + Color62: 0x5F5FD7, + Color63: 0x5F5FFF, + Color64: 0x5F8700, + Color65: 0x5F875F, + Color66: 0x5F8787, + Color67: 0x5F87AF, + Color68: 0x5F87D7, + Color69: 0x5F87FF, + Color70: 0x5FAF00, + Color71: 0x5FAF5F, + Color72: 0x5FAF87, + Color73: 0x5FAFAF, + Color74: 0x5FAFD7, + Color75: 0x5FAFFF, + Color76: 0x5FD700, + Color77: 0x5FD75F, + Color78: 0x5FD787, + Color79: 0x5FD7AF, + Color80: 0x5FD7D7, + Color81: 0x5FD7FF, + Color82: 0x5FFF00, + Color83: 0x5FFF5F, + Color84: 0x5FFF87, + Color85: 0x5FFFAF, + Color86: 0x5FFFD7, + Color87: 0x5FFFFF, + Color88: 0x870000, + Color89: 0x87005F, + Color90: 0x870087, + Color91: 0x8700AF, + Color92: 0x8700D7, + Color93: 0x8700FF, + Color94: 0x875F00, + Color95: 0x875F5F, + Color96: 0x875F87, + Color97: 0x875FAF, + Color98: 0x875FD7, + Color99: 0x875FFF, + Color100: 0x878700, + Color101: 0x87875F, + Color102: 0x878787, + Color103: 0x8787AF, + Color104: 0x8787D7, + Color105: 0x8787FF, + Color106: 0x87AF00, + Color107: 0x87AF5F, + Color108: 0x87AF87, + Color109: 0x87AFAF, + Color110: 0x87AFD7, + Color111: 0x87AFFF, + Color112: 0x87D700, + Color113: 0x87D75F, + Color114: 0x87D787, + Color115: 0x87D7AF, + Color116: 0x87D7D7, + Color117: 0x87D7FF, + Color118: 0x87FF00, + Color119: 0x87FF5F, + Color120: 0x87FF87, + Color121: 0x87FFAF, + Color122: 0x87FFD7, + Color123: 0x87FFFF, + Color124: 0xAF0000, + Color125: 0xAF005F, + Color126: 0xAF0087, + Color127: 0xAF00AF, + Color128: 0xAF00D7, + Color129: 0xAF00FF, + Color130: 0xAF5F00, + Color131: 0xAF5F5F, + Color132: 0xAF5F87, + Color133: 0xAF5FAF, + Color134: 0xAF5FD7, + Color135: 0xAF5FFF, + Color136: 0xAF8700, + Color137: 0xAF875F, + Color138: 0xAF8787, + Color139: 0xAF87AF, + Color140: 0xAF87D7, + Color141: 0xAF87FF, + Color142: 0xAFAF00, + Color143: 0xAFAF5F, + Color144: 0xAFAF87, + Color145: 0xAFAFAF, + Color146: 0xAFAFD7, + Color147: 0xAFAFFF, + Color148: 0xAFD700, + Color149: 0xAFD75F, + Color150: 0xAFD787, + Color151: 0xAFD7AF, + Color152: 0xAFD7D7, + Color153: 0xAFD7FF, + Color154: 0xAFFF00, + Color155: 0xAFFF5F, + Color156: 0xAFFF87, + Color157: 0xAFFFAF, + Color158: 0xAFFFD7, + Color159: 0xAFFFFF, + Color160: 0xD70000, + Color161: 0xD7005F, + Color162: 0xD70087, + Color163: 0xD700AF, + Color164: 0xD700D7, + Color165: 0xD700FF, + Color166: 0xD75F00, + Color167: 0xD75F5F, + Color168: 0xD75F87, + Color169: 0xD75FAF, + Color170: 0xD75FD7, + Color171: 0xD75FFF, + Color172: 0xD78700, + Color173: 0xD7875F, + Color174: 0xD78787, + Color175: 0xD787AF, + Color176: 0xD787D7, + Color177: 0xD787FF, + Color178: 0xD7AF00, + Color179: 0xD7AF5F, + Color180: 0xD7AF87, + Color181: 0xD7AFAF, + Color182: 0xD7AFD7, + Color183: 0xD7AFFF, + Color184: 0xD7D700, + Color185: 0xD7D75F, + Color186: 0xD7D787, + Color187: 0xD7D7AF, + Color188: 0xD7D7D7, + Color189: 0xD7D7FF, + Color190: 0xD7FF00, + Color191: 0xD7FF5F, + Color192: 0xD7FF87, + Color193: 0xD7FFAF, + Color194: 0xD7FFD7, + Color195: 0xD7FFFF, + Color196: 0xFF0000, // red + Color197: 0xFF005F, + Color198: 0xFF0087, + Color199: 0xFF00AF, + Color200: 0xFF00D7, + Color201: 0xFF00FF, // fuchsia + Color202: 0xFF5F00, + Color203: 0xFF5F5F, + Color204: 0xFF5F87, + Color205: 0xFF5FAF, + Color206: 0xFF5FD7, + Color207: 0xFF5FFF, + Color208: 0xFF8700, + Color209: 0xFF875F, + Color210: 0xFF8787, + Color211: 0xFF87AF, + Color212: 0xFF87D7, + Color213: 0xFF87FF, + Color214: 0xFFAF00, + Color215: 0xFFAF5F, + Color216: 0xFFAF87, + Color217: 0xFFAFAF, + Color218: 0xFFAFD7, + Color219: 0xFFAFFF, + Color220: 0xFFD700, + Color221: 0xFFD75F, + Color222: 0xFFD787, + Color223: 0xFFD7AF, + Color224: 0xFFD7D7, + Color225: 0xFFD7FF, + Color226: 0xFFFF00, // yellow + Color227: 0xFFFF5F, + Color228: 0xFFFF87, + Color229: 0xFFFFAF, + Color230: 0xFFFFD7, + Color231: 0xFFFFFF, // white + Color232: 0x080808, + Color233: 0x121212, + Color234: 0x1C1C1C, + Color235: 0x262626, + Color236: 0x303030, + Color237: 0x3A3A3A, + Color238: 0x444444, + Color239: 0x4E4E4E, + Color240: 0x585858, + Color241: 0x626262, + Color242: 0x6C6C6C, + Color243: 0x767676, + Color244: 0x808080, // grey + Color245: 0x8A8A8A, + Color246: 0x949494, + Color247: 0x9E9E9E, + Color248: 0xA8A8A8, + Color249: 0xB2B2B2, + Color250: 0xBCBCBC, + Color251: 0xC6C6C6, + Color252: 0xD0D0D0, + Color253: 0xDADADA, + Color254: 0xE4E4E4, + Color255: 0xEEEEEE, + ColorAliceBlue: 0xF0F8FF, + ColorAntiqueWhite: 0xFAEBD7, + ColorAquaMarine: 0x7FFFD4, + ColorAzure: 0xF0FFFF, + ColorBeige: 0xF5F5DC, + ColorBisque: 0xFFE4C4, + ColorBlanchedAlmond: 0xFFEBCD, + ColorBlueViolet: 0x8A2BE2, + ColorBrown: 0xA52A2A, + ColorBurlyWood: 0xDEB887, + ColorCadetBlue: 0x5F9EA0, + ColorChartreuse: 0x7FFF00, + ColorChocolate: 0xD2691E, + ColorCoral: 0xFF7F50, + ColorCornflowerBlue: 0x6495ED, + ColorCornsilk: 0xFFF8DC, + ColorCrimson: 0xDC143C, + ColorDarkBlue: 0x00008B, + ColorDarkCyan: 0x008B8B, + ColorDarkGoldenrod: 0xB8860B, + ColorDarkGray: 0xA9A9A9, + ColorDarkGreen: 0x006400, + ColorDarkKhaki: 0xBDB76B, + ColorDarkMagenta: 0x8B008B, + ColorDarkOliveGreen: 0x556B2F, + ColorDarkOrange: 0xFF8C00, + ColorDarkOrchid: 0x9932CC, + ColorDarkRed: 0x8B0000, + ColorDarkSalmon: 0xE9967A, + ColorDarkSeaGreen: 0x8FBC8F, + ColorDarkSlateBlue: 0x483D8B, + ColorDarkSlateGray: 0x2F4F4F, + ColorDarkTurquoise: 0x00CED1, + ColorDarkViolet: 0x9400D3, + ColorDeepPink: 0xFF1493, + ColorDeepSkyBlue: 0x00BFFF, + ColorDimGray: 0x696969, + ColorDodgerBlue: 0x1E90FF, + ColorFireBrick: 0xB22222, + ColorFloralWhite: 0xFFFAF0, + ColorForestGreen: 0x228B22, + ColorGainsboro: 0xDCDCDC, + ColorGhostWhite: 0xF8F8FF, + ColorGold: 0xFFD700, + ColorGoldenrod: 0xDAA520, + ColorGreenYellow: 0xADFF2F, + ColorHoneydew: 0xF0FFF0, + ColorHotPink: 0xFF69B4, + ColorIndianRed: 0xCD5C5C, + ColorIndigo: 0x4B0082, + ColorIvory: 0xFFFFF0, + ColorKhaki: 0xF0E68C, + ColorLavender: 0xE6E6FA, + ColorLavenderBlush: 0xFFF0F5, + ColorLawnGreen: 0x7CFC00, + ColorLemonChiffon: 0xFFFACD, + ColorLightBlue: 0xADD8E6, + ColorLightCoral: 0xF08080, + ColorLightCyan: 0xE0FFFF, + ColorLightGoldenrodYellow: 0xFAFAD2, + ColorLightGray: 0xD3D3D3, + ColorLightGreen: 0x90EE90, + ColorLightPink: 0xFFB6C1, + ColorLightSalmon: 0xFFA07A, + ColorLightSeaGreen: 0x20B2AA, + ColorLightSkyBlue: 0x87CEFA, + ColorLightSlateGray: 0x778899, + ColorLightSteelBlue: 0xB0C4DE, + ColorLightYellow: 0xFFFFE0, + ColorLimeGreen: 0x32CD32, + ColorLinen: 0xFAF0E6, + ColorMediumAquamarine: 0x66CDAA, + ColorMediumBlue: 0x0000CD, + ColorMediumOrchid: 0xBA55D3, + ColorMediumPurple: 0x9370DB, + ColorMediumSeaGreen: 0x3CB371, + ColorMediumSlateBlue: 0x7B68EE, + ColorMediumSpringGreen: 0x00FA9A, + ColorMediumTurquoise: 0x48D1CC, + ColorMediumVioletRed: 0xC71585, + ColorMidnightBlue: 0x191970, + ColorMintCream: 0xF5FFFA, + ColorMistyRose: 0xFFE4E1, + ColorMoccasin: 0xFFE4B5, + ColorNavajoWhite: 0xFFDEAD, + ColorOldLace: 0xFDF5E6, + ColorOliveDrab: 0x6B8E23, + ColorOrange: 0xFFA500, + ColorOrangeRed: 0xFF4500, + ColorOrchid: 0xDA70D6, + ColorPaleGoldenrod: 0xEEE8AA, + ColorPaleGreen: 0x98FB98, + ColorPaleTurquoise: 0xAFEEEE, + ColorPaleVioletRed: 0xDB7093, + ColorPapayaWhip: 0xFFEFD5, + ColorPeachPuff: 0xFFDAB9, + ColorPeru: 0xCD853F, + ColorPink: 0xFFC0CB, + ColorPlum: 0xDDA0DD, + ColorPowderBlue: 0xB0E0E6, + ColorRebeccaPurple: 0x663399, + ColorRosyBrown: 0xBC8F8F, + ColorRoyalBlue: 0x4169E1, + ColorSaddleBrown: 0x8B4513, + ColorSalmon: 0xFA8072, + ColorSandyBrown: 0xF4A460, + ColorSeaGreen: 0x2E8B57, + ColorSeashell: 0xFFF5EE, + ColorSienna: 0xA0522D, + ColorSkyblue: 0x87CEEB, + ColorSlateBlue: 0x6A5ACD, + ColorSlateGray: 0x708090, + ColorSnow: 0xFFFAFA, + ColorSpringGreen: 0x00FF7F, + ColorSteelBlue: 0x4682B4, + ColorTan: 0xD2B48C, + ColorThistle: 0xD8BFD8, + ColorTomato: 0xFF6347, + ColorTurquoise: 0x40E0D0, + ColorViolet: 0xEE82EE, + ColorWheat: 0xF5DEB3, + ColorWhiteSmoke: 0xF5F5F5, + ColorYellowGreen: 0x9ACD32, +} + +// Special colors. +const ( + // ColorReset is used to indicate that the color should use the + // vanilla terminal colors. (Basically go back to the defaults.) + ColorReset = ColorSpecial | iota +) + +// ColorNames holds the written names of colors. Useful to present a list of +// recognized named colors. +var ColorNames = map[string]Color{ + "black": ColorBlack, + "maroon": ColorMaroon, + "green": ColorGreen, + "olive": ColorOlive, + "navy": ColorNavy, + "purple": ColorPurple, + "teal": ColorTeal, + "silver": ColorSilver, + "gray": ColorGray, + "red": ColorRed, + "lime": ColorLime, + "yellow": ColorYellow, + "blue": ColorBlue, + "fuchsia": ColorFuchsia, + "aqua": ColorAqua, + "white": ColorWhite, + "aliceblue": ColorAliceBlue, + "antiquewhite": ColorAntiqueWhite, + "aquamarine": ColorAquaMarine, + "azure": ColorAzure, + "beige": ColorBeige, + "bisque": ColorBisque, + "blanchedalmond": ColorBlanchedAlmond, + "blueviolet": ColorBlueViolet, + "brown": ColorBrown, + "burlywood": ColorBurlyWood, + "cadetblue": ColorCadetBlue, + "chartreuse": ColorChartreuse, + "chocolate": ColorChocolate, + "coral": ColorCoral, + "cornflowerblue": ColorCornflowerBlue, + "cornsilk": ColorCornsilk, + "crimson": ColorCrimson, + "darkblue": ColorDarkBlue, + "darkcyan": ColorDarkCyan, + "darkgoldenrod": ColorDarkGoldenrod, + "darkgray": ColorDarkGray, + "darkgreen": ColorDarkGreen, + "darkkhaki": ColorDarkKhaki, + "darkmagenta": ColorDarkMagenta, + "darkolivegreen": ColorDarkOliveGreen, + "darkorange": ColorDarkOrange, + "darkorchid": ColorDarkOrchid, + "darkred": ColorDarkRed, + "darksalmon": ColorDarkSalmon, + "darkseagreen": ColorDarkSeaGreen, + "darkslateblue": ColorDarkSlateBlue, + "darkslategray": ColorDarkSlateGray, + "darkturquoise": ColorDarkTurquoise, + "darkviolet": ColorDarkViolet, + "deeppink": ColorDeepPink, + "deepskyblue": ColorDeepSkyBlue, + "dimgray": ColorDimGray, + "dodgerblue": ColorDodgerBlue, + "firebrick": ColorFireBrick, + "floralwhite": ColorFloralWhite, + "forestgreen": ColorForestGreen, + "gainsboro": ColorGainsboro, + "ghostwhite": ColorGhostWhite, + "gold": ColorGold, + "goldenrod": ColorGoldenrod, + "greenyellow": ColorGreenYellow, + "honeydew": ColorHoneydew, + "hotpink": ColorHotPink, + "indianred": ColorIndianRed, + "indigo": ColorIndigo, + "ivory": ColorIvory, + "khaki": ColorKhaki, + "lavender": ColorLavender, + "lavenderblush": ColorLavenderBlush, + "lawngreen": ColorLawnGreen, + "lemonchiffon": ColorLemonChiffon, + "lightblue": ColorLightBlue, + "lightcoral": ColorLightCoral, + "lightcyan": ColorLightCyan, + "lightgoldenrodyellow": ColorLightGoldenrodYellow, + "lightgray": ColorLightGray, + "lightgreen": ColorLightGreen, + "lightpink": ColorLightPink, + "lightsalmon": ColorLightSalmon, + "lightseagreen": ColorLightSeaGreen, + "lightskyblue": ColorLightSkyBlue, + "lightslategray": ColorLightSlateGray, + "lightsteelblue": ColorLightSteelBlue, + "lightyellow": ColorLightYellow, + "limegreen": ColorLimeGreen, + "linen": ColorLinen, + "mediumaquamarine": ColorMediumAquamarine, + "mediumblue": ColorMediumBlue, + "mediumorchid": ColorMediumOrchid, + "mediumpurple": ColorMediumPurple, + "mediumseagreen": ColorMediumSeaGreen, + "mediumslateblue": ColorMediumSlateBlue, + "mediumspringgreen": ColorMediumSpringGreen, + "mediumturquoise": ColorMediumTurquoise, + "mediumvioletred": ColorMediumVioletRed, + "midnightblue": ColorMidnightBlue, + "mintcream": ColorMintCream, + "mistyrose": ColorMistyRose, + "moccasin": ColorMoccasin, + "navajowhite": ColorNavajoWhite, + "oldlace": ColorOldLace, + "olivedrab": ColorOliveDrab, + "orange": ColorOrange, + "orangered": ColorOrangeRed, + "orchid": ColorOrchid, + "palegoldenrod": ColorPaleGoldenrod, + "palegreen": ColorPaleGreen, + "paleturquoise": ColorPaleTurquoise, + "palevioletred": ColorPaleVioletRed, + "papayawhip": ColorPapayaWhip, + "peachpuff": ColorPeachPuff, + "peru": ColorPeru, + "pink": ColorPink, + "plum": ColorPlum, + "powderblue": ColorPowderBlue, + "rebeccapurple": ColorRebeccaPurple, + "rosybrown": ColorRosyBrown, + "royalblue": ColorRoyalBlue, + "saddlebrown": ColorSaddleBrown, + "salmon": ColorSalmon, + "sandybrown": ColorSandyBrown, + "seagreen": ColorSeaGreen, + "seashell": ColorSeashell, + "sienna": ColorSienna, + "skyblue": ColorSkyblue, + "slateblue": ColorSlateBlue, + "slategray": ColorSlateGray, + "snow": ColorSnow, + "springgreen": ColorSpringGreen, + "steelblue": ColorSteelBlue, + "tan": ColorTan, + "thistle": ColorThistle, + "tomato": ColorTomato, + "turquoise": ColorTurquoise, + "violet": ColorViolet, + "wheat": ColorWheat, + "whitesmoke": ColorWhiteSmoke, + "yellowgreen": ColorYellowGreen, + "grey": ColorGray, + "dimgrey": ColorDimGray, + "darkgrey": ColorDarkGray, + "darkslategrey": ColorDarkSlateGray, + "lightgrey": ColorLightGray, + "lightslategrey": ColorLightSlateGray, + "slategrey": ColorSlateGray, +} + +// Valid indicates the color is a valid value (has been set). +func (c Color) Valid() bool { + return c&ColorValid != 0 +} + +// IsRGB is true if the color is an RGB specific value. +func (c Color) IsRGB() bool { + return c&(ColorValid|ColorIsRGB) == (ColorValid | ColorIsRGB) +} + +// Hex returns the color's hexadecimal RGB 24-bit value with each component +// consisting of a single byte, ala R << 16 | G << 8 | B. If the color +// is unknown or unset, -1 is returned. +func (c Color) Hex() int32 { + if !c.Valid() { + return -1 + } + if c&ColorIsRGB != 0 { + return int32(c) & 0xffffff + } + if v, ok := ColorValues[c]; ok { + return v + } + return -1 +} + +// RGB returns the red, green, and blue components of the color, with +// each component represented as a value 0-255. In the event that the +// color cannot be broken up (not set usually), -1 is returned for each value. +func (c Color) RGB() (int32, int32, int32) { + v := c.Hex() + if v < 0 { + return -1, -1, -1 + } + return (v >> 16) & 0xff, (v >> 8) & 0xff, v & 0xff +} + +// TrueColor returns the true color (RGB) version of the provided color. +// This is useful for ensuring color accuracy when using named colors. +// This will override terminal theme colors. +func (c Color) TrueColor() Color { + if !c.Valid() { + return ColorDefault + } + if c&ColorIsRGB != 0 { + return c + } + return Color(c.Hex()) | ColorIsRGB | ColorValid +} + +// NewRGBColor returns a new color with the given red, green, and blue values. +// Each value must be represented in the range 0-255. +func NewRGBColor(r, g, b int32) Color { + return NewHexColor(((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff)) +} + +// NewHexColor returns a color using the given 24-bit RGB value. +func NewHexColor(v int32) Color { + return ColorIsRGB | Color(v) | ColorValid +} + +// GetColor creates a Color from a color name (W3C name). A hex value may +// be supplied as a string in the format "#ffffff". +func GetColor(name string) Color { + if c, ok := ColorNames[name]; ok { + return c + } + if len(name) == 7 && name[0] == '#' { + if v, e := strconv.ParseInt(name[1:], 16, 32); e == nil { + return NewHexColor(int32(v)) + } + } + return ColorDefault +} + +// PaletteColor creates a color based on the palette index. +func PaletteColor(index int) Color { + return Color(index) | ColorValid +} + +// FromImageColor converts an image/color.Color into tcell.Color. +// The alpha value is dropped, so it should be tracked separately if it is +// needed. +func FromImageColor(imageColor ic.Color) Color { + r, g, b, _ := imageColor.RGBA() + // NOTE image/color.Color RGB values range is [0, 0xFFFF] as uint32 + return NewRGBColor(int32(r>>8), int32(g>>8), int32(b>>8)) +} diff --git a/vendor/github.com/gdamore/tcell/v2/colorfit.go b/vendor/github.com/gdamore/tcell/v2/colorfit.go new file mode 100644 index 0000000000..b7740b8ae2 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/colorfit.go @@ -0,0 +1,52 @@ +// Copyright 2016 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + "github.com/lucasb-eyer/go-colorful" + "math" +) + +// FindColor attempts to find a given color, or the best match possible for it, +// from the palette given. This is an expensive operation, so results should +// be cached by the caller. +func FindColor(c Color, palette []Color) Color { + match := ColorDefault + dist := float64(0) + r, g, b := c.RGB() + c1 := colorful.Color{ + R: float64(r) / 255.0, + G: float64(g) / 255.0, + B: float64(b) / 255.0, + } + for _, d := range palette { + r, g, b = d.RGB() + c2 := colorful.Color{ + R: float64(r) / 255.0, + G: float64(g) / 255.0, + B: float64(b) / 255.0, + } + // CIE94 is more accurate, but really really expensive. + nd := c1.DistanceCIE76(c2) + if math.IsNaN(nd) { + nd = math.Inf(1) + } + if match == ColorDefault || nd < dist { + match = d + dist = nd + } + } + return match +} diff --git a/vendor/github.com/gdamore/tcell/v2/console_stub.go b/vendor/github.com/gdamore/tcell/v2/console_stub.go new file mode 100644 index 0000000000..fda2f0926c --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/console_stub.go @@ -0,0 +1,23 @@ +// +build !windows + +// Copyright 2015 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +// NewConsoleScreen returns a console based screen. This platform +// doesn't have support for any, so it returns nil and a suitable error. +func NewConsoleScreen() (Screen, error) { + return nil, ErrNoScreen +} diff --git a/vendor/github.com/gdamore/tcell/v2/console_win.go b/vendor/github.com/gdamore/tcell/v2/console_win.go new file mode 100644 index 0000000000..a901a1255d --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/console_win.go @@ -0,0 +1,1269 @@ +// +build windows + +// Copyright 2021 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + "errors" + "fmt" + "os" + "strings" + "sync" + "syscall" + "unicode/utf16" + "unsafe" +) + +type cScreen struct { + in syscall.Handle + out syscall.Handle + cancelflag syscall.Handle + scandone chan struct{} + evch chan Event + quit chan struct{} + curx int + cury int + style Style + clear bool + fini bool + vten bool + truecolor bool + running bool + + w int + h int + + oscreen consoleInfo + ocursor cursorInfo + oimode uint32 + oomode uint32 + cells CellBuffer + + finiOnce sync.Once + + mouseEnabled bool + wg sync.WaitGroup + stopQ chan struct{} + + sync.Mutex +} + +var winLock sync.Mutex + +var winPalette = []Color{ + ColorBlack, + ColorMaroon, + ColorGreen, + ColorNavy, + ColorOlive, + ColorPurple, + ColorTeal, + ColorSilver, + ColorGray, + ColorRed, + ColorLime, + ColorBlue, + ColorYellow, + ColorFuchsia, + ColorAqua, + ColorWhite, +} + +var winColors = map[Color]Color{ + ColorBlack: ColorBlack, + ColorMaroon: ColorMaroon, + ColorGreen: ColorGreen, + ColorNavy: ColorNavy, + ColorOlive: ColorOlive, + ColorPurple: ColorPurple, + ColorTeal: ColorTeal, + ColorSilver: ColorSilver, + ColorGray: ColorGray, + ColorRed: ColorRed, + ColorLime: ColorLime, + ColorBlue: ColorBlue, + ColorYellow: ColorYellow, + ColorFuchsia: ColorFuchsia, + ColorAqua: ColorAqua, + ColorWhite: ColorWhite, +} + +var ( + k32 = syscall.NewLazyDLL("kernel32.dll") + u32 = syscall.NewLazyDLL("user32.dll") +) + +// We have to bring in the kernel32 and user32 DLLs directly, so we can get +// access to some system calls that the core Go API lacks. +// +// Note that Windows appends some functions with W to indicate that wide +// characters (Unicode) are in use. The documentation refers to them +// without this suffix, as the resolution is made via preprocessor. +var ( + procReadConsoleInput = k32.NewProc("ReadConsoleInputW") + procWaitForMultipleObjects = k32.NewProc("WaitForMultipleObjects") + procCreateEvent = k32.NewProc("CreateEventW") + procSetEvent = k32.NewProc("SetEvent") + procGetConsoleCursorInfo = k32.NewProc("GetConsoleCursorInfo") + procSetConsoleCursorInfo = k32.NewProc("SetConsoleCursorInfo") + procSetConsoleCursorPosition = k32.NewProc("SetConsoleCursorPosition") + procSetConsoleMode = k32.NewProc("SetConsoleMode") + procGetConsoleMode = k32.NewProc("GetConsoleMode") + procGetConsoleScreenBufferInfo = k32.NewProc("GetConsoleScreenBufferInfo") + procFillConsoleOutputAttribute = k32.NewProc("FillConsoleOutputAttribute") + procFillConsoleOutputCharacter = k32.NewProc("FillConsoleOutputCharacterW") + procSetConsoleWindowInfo = k32.NewProc("SetConsoleWindowInfo") + procSetConsoleScreenBufferSize = k32.NewProc("SetConsoleScreenBufferSize") + procSetConsoleTextAttribute = k32.NewProc("SetConsoleTextAttribute") + procMessageBeep = u32.NewProc("MessageBeep") +) + +const ( + w32Infinite = ^uintptr(0) + w32WaitObject0 = uintptr(0) +) + +const ( + // VT100/XTerm escapes understood by the console + vtShowCursor = "\x1b[?25h" + vtHideCursor = "\x1b[?25l" + vtCursorPos = "\x1b[%d;%dH" // Note that it is Y then X + vtSgr0 = "\x1b[0m" + vtBold = "\x1b[1m" + vtUnderline = "\x1b[4m" + vtBlink = "\x1b[5m" // Not sure this is processed + vtReverse = "\x1b[7m" + vtSetFg = "\x1b[38;5;%dm" + vtSetBg = "\x1b[48;5;%dm" + vtSetFgRGB = "\x1b[38;2;%d;%d;%dm" // RGB + vtSetBgRGB = "\x1b[48;2;%d;%d;%dm" // RGB +) + +// NewConsoleScreen returns a Screen for the Windows console associated +// with the current process. The Screen makes use of the Windows Console +// API to display content and read events. +func NewConsoleScreen() (Screen, error) { + return &cScreen{}, nil +} + +func (s *cScreen) Init() error { + s.evch = make(chan Event, 10) + s.quit = make(chan struct{}) + s.scandone = make(chan struct{}) + + in, e := syscall.Open("CONIN$", syscall.O_RDWR, 0) + if e != nil { + return e + } + s.in = in + out, e := syscall.Open("CONOUT$", syscall.O_RDWR, 0) + if e != nil { + syscall.Close(s.in) + return e + } + s.out = out + + s.truecolor = true + + // ConEmu handling of colors and scrolling when in terminal + // mode is extremely problematic at the best. The color + // palette will scroll even though characters do not, when + // emitting stuff for the last character. In the future we + // might change this to look at specific versions of ConEmu + // if they fix the bug. + if os.Getenv("ConEmuPID") != "" { + s.truecolor = false + } + switch os.Getenv("TCELL_TRUECOLOR") { + case "disable": + s.truecolor = false + case "enable": + s.truecolor = true + } + + s.Lock() + + s.curx = -1 + s.cury = -1 + s.style = StyleDefault + s.getCursorInfo(&s.ocursor) + s.getConsoleInfo(&s.oscreen) + s.getOutMode(&s.oomode) + s.getInMode(&s.oimode) + s.resize() + + s.fini = false + s.setInMode(modeResizeEn | modeExtndFlg) + + // 24-bit color is opt-in for now, because we can't figure out + // to make it work consistently. + if s.truecolor { + s.setOutMode(modeVtOutput | modeNoAutoNL | modeCookedOut) + var omode uint32 + s.getOutMode(&omode) + if omode&modeVtOutput == modeVtOutput { + s.vten = true + } else { + s.truecolor = false + s.setOutMode(0) + } + } else { + s.setOutMode(0) + } + + s.Unlock() + + return s.engage() +} + +func (s *cScreen) CharacterSet() string { + // We are always UTF-16LE on Windows + return "UTF-16LE" +} + +func (s *cScreen) EnableMouse(...MouseFlags) { + s.Lock() + s.mouseEnabled = true + s.enableMouse(true) + s.Unlock() +} + +func (s *cScreen) DisableMouse() { + s.Lock() + s.mouseEnabled = false + s.enableMouse(false) + s.Unlock() +} + +func (s *cScreen) enableMouse(on bool) { + if on { + s.setInMode(modeResizeEn | modeMouseEn | modeExtndFlg) + } else { + s.setInMode(modeResizeEn | modeExtndFlg) + } +} + +// Windows lacks bracketed paste (for now) + +func (s *cScreen) EnablePaste() {} + +func (s *cScreen) DisablePaste() {} + +func (s *cScreen) Fini() { + s.disengage() +} + +func (s *cScreen) disengage() { + s.Lock() + if !s.running { + s.Unlock() + return + } + s.running = false + stopQ := s.stopQ + procSetEvent.Call(uintptr(s.cancelflag)) + close(stopQ) + s.Unlock() + + s.wg.Wait() + + s.setInMode(s.oimode) + s.setOutMode(s.oomode) + s.setBufferSize(int(s.oscreen.size.x), int(s.oscreen.size.y)) + s.clearScreen(StyleDefault, false) + s.setCursorPos(0, 0, false) + s.setCursorInfo(&s.ocursor) + procSetConsoleTextAttribute.Call( + uintptr(s.out), + uintptr(s.mapStyle(StyleDefault))) +} + +func (s *cScreen) engage() error { + s.Lock() + defer s.Unlock() + if s.running { + return errors.New("already engaged") + } + s.stopQ = make(chan struct{}) + cf, _, e := procCreateEvent.Call( + uintptr(0), + uintptr(1), + uintptr(0), + uintptr(0)) + if cf == uintptr(0) { + return e + } + s.running = true + s.cancelflag = syscall.Handle(cf) + s.enableMouse(s.mouseEnabled) + + if s.vten { + s.setOutMode(modeVtOutput | modeNoAutoNL | modeCookedOut) + } else { + s.setOutMode(0) + } + + s.clearScreen(s.style, s.vten) + s.hideCursor() + + s.cells.Invalidate() + s.hideCursor() + s.resize() + s.draw() + s.doCursor() + + s.wg.Add(1) + go s.scanInput(s.stopQ) + return nil +} + +func (s *cScreen) PostEventWait(ev Event) { + s.evch <- ev +} + +func (s *cScreen) PostEvent(ev Event) error { + select { + case s.evch <- ev: + return nil + default: + return ErrEventQFull + } +} + +func (s *cScreen) ChannelEvents(ch chan<- Event, quit <-chan struct{}) { + defer close(ch) + for { + select { + case <-quit: + return + case <-s.stopQ: + return + case ev := <-s.evch: + select { + case <-quit: + return + case <-s.stopQ: + return + case ch <- ev: + } + } + } +} + +func (s *cScreen) PollEvent() Event { + select { + case <-s.stopQ: + return nil + case ev := <-s.evch: + return ev + } +} + +func (s *cScreen) HasPendingEvent() bool { + return len(s.evch) > 0 +} + +type cursorInfo struct { + size uint32 + visible uint32 +} + +type coord struct { + x int16 + y int16 +} + +func (c coord) uintptr() uintptr { + // little endian, put x first + return uintptr(c.x) | (uintptr(c.y) << 16) +} + +type rect struct { + left int16 + top int16 + right int16 + bottom int16 +} + +func (s *cScreen) emitVtString(vs string) { + esc := utf16.Encode([]rune(vs)) + syscall.WriteConsole(s.out, &esc[0], uint32(len(esc)), nil, nil) +} + +func (s *cScreen) showCursor() { + if s.vten { + s.emitVtString(vtShowCursor) + } else { + s.setCursorInfo(&cursorInfo{size: 100, visible: 1}) + } +} + +func (s *cScreen) hideCursor() { + if s.vten { + s.emitVtString(vtHideCursor) + } else { + s.setCursorInfo(&cursorInfo{size: 1, visible: 0}) + } +} + +func (s *cScreen) ShowCursor(x, y int) { + s.Lock() + if !s.fini { + s.curx = x + s.cury = y + } + s.doCursor() + s.Unlock() +} + +func (s *cScreen) doCursor() { + x, y := s.curx, s.cury + + if x < 0 || y < 0 || x >= s.w || y >= s.h { + s.hideCursor() + } else { + s.setCursorPos(x, y, s.vten) + s.showCursor() + } +} + +func (s *cScreen) HideCursor() { + s.ShowCursor(-1, -1) +} + +type inputRecord struct { + typ uint16 + _ uint16 + data [16]byte +} + +const ( + keyEvent uint16 = 1 + mouseEvent uint16 = 2 + resizeEvent uint16 = 4 + menuEvent uint16 = 8 // don't use + focusEvent uint16 = 16 // don't use +) + +type mouseRecord struct { + x int16 + y int16 + btns uint32 + mod uint32 + flags uint32 +} + +const ( + mouseDoubleClick uint32 = 0x2 + mouseHWheeled uint32 = 0x8 + mouseVWheeled uint32 = 0x4 + mouseMoved uint32 = 0x1 +) + +type resizeRecord struct { + x int16 + y int16 +} + +type keyRecord struct { + isdown int32 + repeat uint16 + kcode uint16 + scode uint16 + ch uint16 + mod uint32 +} + +const ( + // Constants per Microsoft. We don't put the modifiers + // here. + vkCancel = 0x03 + vkBack = 0x08 // Backspace + vkTab = 0x09 + vkClear = 0x0c + vkReturn = 0x0d + vkPause = 0x13 + vkEscape = 0x1b + vkSpace = 0x20 + vkPrior = 0x21 // PgUp + vkNext = 0x22 // PgDn + vkEnd = 0x23 + vkHome = 0x24 + vkLeft = 0x25 + vkUp = 0x26 + vkRight = 0x27 + vkDown = 0x28 + vkPrint = 0x2a + vkPrtScr = 0x2c + vkInsert = 0x2d + vkDelete = 0x2e + vkHelp = 0x2f + vkF1 = 0x70 + vkF2 = 0x71 + vkF3 = 0x72 + vkF4 = 0x73 + vkF5 = 0x74 + vkF6 = 0x75 + vkF7 = 0x76 + vkF8 = 0x77 + vkF9 = 0x78 + vkF10 = 0x79 + vkF11 = 0x7a + vkF12 = 0x7b + vkF13 = 0x7c + vkF14 = 0x7d + vkF15 = 0x7e + vkF16 = 0x7f + vkF17 = 0x80 + vkF18 = 0x81 + vkF19 = 0x82 + vkF20 = 0x83 + vkF21 = 0x84 + vkF22 = 0x85 + vkF23 = 0x86 + vkF24 = 0x87 +) + +var vkKeys = map[uint16]Key{ + vkCancel: KeyCancel, + vkBack: KeyBackspace, + vkTab: KeyTab, + vkClear: KeyClear, + vkPause: KeyPause, + vkPrint: KeyPrint, + vkPrtScr: KeyPrint, + vkPrior: KeyPgUp, + vkNext: KeyPgDn, + vkReturn: KeyEnter, + vkEnd: KeyEnd, + vkHome: KeyHome, + vkLeft: KeyLeft, + vkUp: KeyUp, + vkRight: KeyRight, + vkDown: KeyDown, + vkInsert: KeyInsert, + vkDelete: KeyDelete, + vkHelp: KeyHelp, + vkF1: KeyF1, + vkF2: KeyF2, + vkF3: KeyF3, + vkF4: KeyF4, + vkF5: KeyF5, + vkF6: KeyF6, + vkF7: KeyF7, + vkF8: KeyF8, + vkF9: KeyF9, + vkF10: KeyF10, + vkF11: KeyF11, + vkF12: KeyF12, + vkF13: KeyF13, + vkF14: KeyF14, + vkF15: KeyF15, + vkF16: KeyF16, + vkF17: KeyF17, + vkF18: KeyF18, + vkF19: KeyF19, + vkF20: KeyF20, + vkF21: KeyF21, + vkF22: KeyF22, + vkF23: KeyF23, + vkF24: KeyF24, +} + +// NB: All Windows platforms are little endian. We assume this +// never, ever change. The following code is endian safe. and does +// not use unsafe pointers. +func getu32(v []byte) uint32 { + return uint32(v[0]) + (uint32(v[1]) << 8) + (uint32(v[2]) << 16) + (uint32(v[3]) << 24) +} +func geti32(v []byte) int32 { + return int32(getu32(v)) +} +func getu16(v []byte) uint16 { + return uint16(v[0]) + (uint16(v[1]) << 8) +} +func geti16(v []byte) int16 { + return int16(getu16(v)) +} + +// Convert windows dwControlKeyState to modifier mask +func mod2mask(cks uint32) ModMask { + mm := ModNone + // Left or right control + if (cks & (0x0008 | 0x0004)) != 0 { + mm |= ModCtrl + } + // Left or right alt + if (cks & (0x0002 | 0x0001)) != 0 { + mm |= ModAlt + } + // Any shift + if (cks & 0x0010) != 0 { + mm |= ModShift + } + return mm +} + +func mrec2btns(mbtns, flags uint32) ButtonMask { + btns := ButtonNone + if mbtns&0x1 != 0 { + btns |= Button1 + } + if mbtns&0x2 != 0 { + btns |= Button2 + } + if mbtns&0x4 != 0 { + btns |= Button3 + } + if mbtns&0x8 != 0 { + btns |= Button4 + } + if mbtns&0x10 != 0 { + btns |= Button5 + } + if mbtns&0x20 != 0 { + btns |= Button6 + } + if mbtns&0x40 != 0 { + btns |= Button7 + } + if mbtns&0x80 != 0 { + btns |= Button8 + } + + if flags&mouseVWheeled != 0 { + if mbtns&0x80000000 == 0 { + btns |= WheelUp + } else { + btns |= WheelDown + } + } + if flags&mouseHWheeled != 0 { + if mbtns&0x80000000 == 0 { + btns |= WheelRight + } else { + btns |= WheelLeft + } + } + return btns +} + +func (s *cScreen) getConsoleInput() error { + // cancelFlag comes first as WaitForMultipleObjects returns the lowest index + // in the event that both events are signalled. + waitObjects := []syscall.Handle{s.cancelflag, s.in} + // As arrays are contiguous in memory, a pointer to the first object is the + // same as a pointer to the array itself. + pWaitObjects := unsafe.Pointer(&waitObjects[0]) + + rv, _, er := procWaitForMultipleObjects.Call( + uintptr(len(waitObjects)), + uintptr(pWaitObjects), + uintptr(0), + w32Infinite) + // WaitForMultipleObjects returns WAIT_OBJECT_0 + the index. + switch rv { + case w32WaitObject0: // s.cancelFlag + return errors.New("cancelled") + case w32WaitObject0 + 1: // s.in + rec := &inputRecord{} + var nrec int32 + rv, _, er := procReadConsoleInput.Call( + uintptr(s.in), + uintptr(unsafe.Pointer(rec)), + uintptr(1), + uintptr(unsafe.Pointer(&nrec))) + if rv == 0 { + return er + } + if nrec != 1 { + return nil + } + switch rec.typ { + case keyEvent: + krec := &keyRecord{} + krec.isdown = geti32(rec.data[0:]) + krec.repeat = getu16(rec.data[4:]) + krec.kcode = getu16(rec.data[6:]) + krec.scode = getu16(rec.data[8:]) + krec.ch = getu16(rec.data[10:]) + krec.mod = getu32(rec.data[12:]) + + if krec.isdown == 0 || krec.repeat < 1 { + // its a key release event, ignore it + return nil + } + if krec.ch != 0 { + // synthesized key code + for krec.repeat > 0 { + // convert shift+tab to backtab + if mod2mask(krec.mod) == ModShift && krec.ch == vkTab { + s.PostEventWait(NewEventKey(KeyBacktab, 0, + ModNone)) + } else { + s.PostEventWait(NewEventKey(KeyRune, rune(krec.ch), + mod2mask(krec.mod))) + } + krec.repeat-- + } + return nil + } + key := KeyNUL // impossible on Windows + ok := false + if key, ok = vkKeys[krec.kcode]; !ok { + return nil + } + for krec.repeat > 0 { + s.PostEventWait(NewEventKey(key, rune(krec.ch), + mod2mask(krec.mod))) + krec.repeat-- + } + + case mouseEvent: + var mrec mouseRecord + mrec.x = geti16(rec.data[0:]) + mrec.y = geti16(rec.data[2:]) + mrec.btns = getu32(rec.data[4:]) + mrec.mod = getu32(rec.data[8:]) + mrec.flags = getu32(rec.data[12:]) + btns := mrec2btns(mrec.btns, mrec.flags) + // we ignore double click, events are delivered normally + s.PostEventWait(NewEventMouse(int(mrec.x), int(mrec.y), btns, + mod2mask(mrec.mod))) + + case resizeEvent: + var rrec resizeRecord + rrec.x = geti16(rec.data[0:]) + rrec.y = geti16(rec.data[2:]) + s.PostEventWait(NewEventResize(int(rrec.x), int(rrec.y))) + + default: + } + default: + return er + } + + return nil +} + +func (s *cScreen) scanInput(stopQ chan struct{}) { + defer s.wg.Done() + for { + select { + case <-stopQ: + return + default: + } + if e := s.getConsoleInput(); e != nil { + return + } + } +} + +// Windows console can display 8 characters, in either low or high intensity +func (s *cScreen) Colors() int { + if s.vten { + return 1 << 24 + } + return 16 +} + +var vgaColors = map[Color]uint16{ + ColorBlack: 0, + ColorMaroon: 0x4, + ColorGreen: 0x2, + ColorNavy: 0x1, + ColorOlive: 0x6, + ColorPurple: 0x5, + ColorTeal: 0x3, + ColorSilver: 0x7, + ColorGrey: 0x8, + ColorRed: 0xc, + ColorLime: 0xa, + ColorBlue: 0x9, + ColorYellow: 0xe, + ColorFuchsia: 0xd, + ColorAqua: 0xb, + ColorWhite: 0xf, +} + +// Windows uses RGB signals +func mapColor2RGB(c Color) uint16 { + winLock.Lock() + if v, ok := winColors[c]; ok { + c = v + } else { + v = FindColor(c, winPalette) + winColors[c] = v + c = v + } + winLock.Unlock() + + if vc, ok := vgaColors[c]; ok { + return vc + } + return 0 +} + +// Map a tcell style to Windows attributes +func (s *cScreen) mapStyle(style Style) uint16 { + f, b, a := style.Decompose() + fa := s.oscreen.attrs & 0xf + ba := (s.oscreen.attrs) >> 4 & 0xf + if f != ColorDefault && f != ColorReset { + fa = mapColor2RGB(f) + } + if b != ColorDefault && b != ColorReset { + ba = mapColor2RGB(b) + } + var attr uint16 + // We simulate reverse by doing the color swap ourselves. + // Apparently windows cannot really do this except in DBCS + // views. + if a&AttrReverse != 0 { + attr = ba + attr |= (fa << 4) + } else { + attr = fa + attr |= (ba << 4) + } + if a&AttrBold != 0 { + attr |= 0x8 + } + if a&AttrDim != 0 { + attr &^= 0x8 + } + if a&AttrUnderline != 0 { + // Best effort -- doesn't seem to work though. + attr |= 0x8000 + } + // Blink is unsupported + return attr +} + +func (s *cScreen) SetCell(x, y int, style Style, ch ...rune) { + if len(ch) > 0 { + s.SetContent(x, y, ch[0], ch[1:], style) + } else { + s.SetContent(x, y, ' ', nil, style) + } +} + +func (s *cScreen) SetContent(x, y int, mainc rune, combc []rune, style Style) { + s.Lock() + if !s.fini { + s.cells.SetContent(x, y, mainc, combc, style) + } + s.Unlock() +} + +func (s *cScreen) GetContent(x, y int) (rune, []rune, Style, int) { + s.Lock() + mainc, combc, style, width := s.cells.GetContent(x, y) + s.Unlock() + return mainc, combc, style, width +} + +func (s *cScreen) sendVtStyle(style Style) { + esc := &strings.Builder{} + + fg, bg, attrs := style.Decompose() + + esc.WriteString(vtSgr0) + + if attrs&(AttrBold|AttrDim) == AttrBold { + esc.WriteString(vtBold) + } + if attrs&AttrBlink != 0 { + esc.WriteString(vtBlink) + } + if attrs&AttrUnderline != 0 { + esc.WriteString(vtUnderline) + } + if attrs&AttrReverse != 0 { + esc.WriteString(vtReverse) + } + if fg.IsRGB() { + r, g, b := fg.RGB() + fmt.Fprintf(esc, vtSetFgRGB, r, g, b) + } else if fg.Valid() { + fmt.Fprintf(esc, vtSetFg, fg&0xff) + } + if bg.IsRGB() { + r, g, b := bg.RGB() + fmt.Fprintf(esc, vtSetBgRGB, r, g, b) + } else if bg.Valid() { + fmt.Fprintf(esc, vtSetBg, bg&0xff) + } + s.emitVtString(esc.String()) +} + +func (s *cScreen) writeString(x, y int, style Style, ch []uint16) { + // we assume the caller has hidden the cursor + if len(ch) == 0 { + return + } + s.setCursorPos(x, y, s.vten) + + if s.vten { + s.sendVtStyle(style) + } else { + procSetConsoleTextAttribute.Call( + uintptr(s.out), + uintptr(s.mapStyle(style))) + } + syscall.WriteConsole(s.out, &ch[0], uint32(len(ch)), nil, nil) +} + +func (s *cScreen) draw() { + // allocate a scratch line bit enough for no combining chars. + // if you have combining characters, you may pay for extra allocs. + if s.clear { + s.clearScreen(s.style, s.vten) + s.clear = false + s.cells.Invalidate() + } + buf := make([]uint16, 0, s.w) + wcs := buf[:] + lstyle := styleInvalid + + lx, ly := -1, -1 + ra := make([]rune, 1) + + for y := 0; y < s.h; y++ { + for x := 0; x < s.w; x++ { + mainc, combc, style, width := s.cells.GetContent(x, y) + dirty := s.cells.Dirty(x, y) + if style == StyleDefault { + style = s.style + } + + if !dirty || style != lstyle { + // write out any data queued thus far + // because we are going to skip over some + // cells, or because we need to change styles + s.writeString(lx, ly, lstyle, wcs) + wcs = buf[0:0] + lstyle = StyleDefault + if !dirty { + continue + } + } + if x > s.w-width { + mainc = ' ' + combc = nil + width = 1 + } + if len(wcs) == 0 { + lstyle = style + lx = x + ly = y + } + ra[0] = mainc + wcs = append(wcs, utf16.Encode(ra)...) + if len(combc) != 0 { + wcs = append(wcs, utf16.Encode(combc)...) + } + for dx := 0; dx < width; dx++ { + s.cells.SetDirty(x+dx, y, false) + } + x += width - 1 + } + s.writeString(lx, ly, lstyle, wcs) + wcs = buf[0:0] + lstyle = styleInvalid + } +} + +func (s *cScreen) Show() { + s.Lock() + if !s.fini { + s.hideCursor() + s.resize() + s.draw() + s.doCursor() + } + s.Unlock() +} + +func (s *cScreen) Sync() { + s.Lock() + if !s.fini { + s.cells.Invalidate() + s.hideCursor() + s.resize() + s.draw() + s.doCursor() + } + s.Unlock() +} + +type consoleInfo struct { + size coord + pos coord + attrs uint16 + win rect + maxsz coord +} + +func (s *cScreen) getConsoleInfo(info *consoleInfo) { + procGetConsoleScreenBufferInfo.Call( + uintptr(s.out), + uintptr(unsafe.Pointer(info))) +} + +func (s *cScreen) getCursorInfo(info *cursorInfo) { + procGetConsoleCursorInfo.Call( + uintptr(s.out), + uintptr(unsafe.Pointer(info))) +} + +func (s *cScreen) setCursorInfo(info *cursorInfo) { + procSetConsoleCursorInfo.Call( + uintptr(s.out), + uintptr(unsafe.Pointer(info))) + +} + +func (s *cScreen) setCursorPos(x, y int, vtEnable bool) { + if vtEnable { + // Note that the string is Y first. Origin is 1,1. + s.emitVtString(fmt.Sprintf(vtCursorPos, y+1, x+1)) + } else { + procSetConsoleCursorPosition.Call( + uintptr(s.out), + coord{int16(x), int16(y)}.uintptr()) + } +} + +func (s *cScreen) setBufferSize(x, y int) { + procSetConsoleScreenBufferSize.Call( + uintptr(s.out), + coord{int16(x), int16(y)}.uintptr()) +} + +func (s *cScreen) Size() (int, int) { + s.Lock() + w, h := s.w, s.h + s.Unlock() + + return w, h +} + +func (s *cScreen) resize() { + info := consoleInfo{} + s.getConsoleInfo(&info) + + w := int((info.win.right - info.win.left) + 1) + h := int((info.win.bottom - info.win.top) + 1) + + if s.w == w && s.h == h { + return + } + + s.cells.Resize(w, h) + s.w = w + s.h = h + + s.setBufferSize(w, h) + + r := rect{0, 0, int16(w - 1), int16(h - 1)} + procSetConsoleWindowInfo.Call( + uintptr(s.out), + uintptr(1), + uintptr(unsafe.Pointer(&r))) + s.PostEvent(NewEventResize(w, h)) +} + +func (s *cScreen) Clear() { + s.Fill(' ', s.style) +} + +func (s *cScreen) Fill(r rune, style Style) { + s.Lock() + if !s.fini { + s.cells.Fill(r, style) + s.clear = true + } + s.Unlock() +} + +func (s *cScreen) clearScreen(style Style, vtEnable bool) { + if vtEnable { + s.sendVtStyle(style) + row := strings.Repeat(" ", s.w) + for y := 0; y < s.h; y++ { + s.setCursorPos(0, y, vtEnable) + s.emitVtString(row) + } + s.setCursorPos(0, 0, vtEnable) + + } else { + pos := coord{0, 0} + attr := s.mapStyle(style) + x, y := s.w, s.h + scratch := uint32(0) + count := uint32(x * y) + + procFillConsoleOutputAttribute.Call( + uintptr(s.out), + uintptr(attr), + uintptr(count), + pos.uintptr(), + uintptr(unsafe.Pointer(&scratch))) + procFillConsoleOutputCharacter.Call( + uintptr(s.out), + uintptr(' '), + uintptr(count), + pos.uintptr(), + uintptr(unsafe.Pointer(&scratch))) + } +} + +const ( + // Input modes + modeExtndFlg uint32 = 0x0080 + modeMouseEn = 0x0010 + modeResizeEn = 0x0008 + modeCooked = 0x0001 + modeVtInput = 0x0200 + + // Output modes + modeCookedOut uint32 = 0x0001 + modeWrapEOL = 0x0002 + modeVtOutput = 0x0004 + modeNoAutoNL = 0x0008 +) + +func (s *cScreen) setInMode(mode uint32) error { + rv, _, err := procSetConsoleMode.Call( + uintptr(s.in), + uintptr(mode)) + if rv == 0 { + return err + } + return nil +} + +func (s *cScreen) setOutMode(mode uint32) error { + rv, _, err := procSetConsoleMode.Call( + uintptr(s.out), + uintptr(mode)) + if rv == 0 { + return err + } + return nil +} + +func (s *cScreen) getInMode(v *uint32) { + procGetConsoleMode.Call( + uintptr(s.in), + uintptr(unsafe.Pointer(v))) +} + +func (s *cScreen) getOutMode(v *uint32) { + procGetConsoleMode.Call( + uintptr(s.out), + uintptr(unsafe.Pointer(v))) +} + +func (s *cScreen) SetStyle(style Style) { + s.Lock() + s.style = style + s.Unlock() +} + +// No fallback rune support, since we have Unicode. Yay! + +func (s *cScreen) RegisterRuneFallback(r rune, subst string) { +} + +func (s *cScreen) UnregisterRuneFallback(r rune) { +} + +func (s *cScreen) CanDisplay(r rune, checkFallbacks bool) bool { + // We presume we can display anything -- we're Unicode. + // (Sadly this not precisely true. Combinings are especially + // poorly supported under Windows.) + return true +} + +func (s *cScreen) HasMouse() bool { + return true +} + +func (s *cScreen) Resize(int, int, int, int) {} + +func (s *cScreen) HasKey(k Key) bool { + // Microsoft has codes for some keys, but they are unusual, + // so we don't include them. We include all the typical + // 101, 105 key layout keys. + valid := map[Key]bool{ + KeyBackspace: true, + KeyTab: true, + KeyEscape: true, + KeyPause: true, + KeyPrint: true, + KeyPgUp: true, + KeyPgDn: true, + KeyEnter: true, + KeyEnd: true, + KeyHome: true, + KeyLeft: true, + KeyUp: true, + KeyRight: true, + KeyDown: true, + KeyInsert: true, + KeyDelete: true, + KeyF1: true, + KeyF2: true, + KeyF3: true, + KeyF4: true, + KeyF5: true, + KeyF6: true, + KeyF7: true, + KeyF8: true, + KeyF9: true, + KeyF10: true, + KeyF11: true, + KeyF12: true, + KeyRune: true, + } + + return valid[k] +} + +func (s *cScreen) Beep() error { + // A simple beep. If the sound card is not available, the sound is generated + // using the speaker. + // + // Reference: + // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebeep + const simpleBeep = 0xffffffff + if rv, _, err := procMessageBeep.Call(simpleBeep); rv == 0 { + return err + } + return nil +} + +func (s *cScreen) Suspend() error { + s.disengage() + return nil +} + +func (s *cScreen) Resume() error { + return s.engage() +} diff --git a/vendor/github.com/gdamore/tcell/v2/doc.go b/vendor/github.com/gdamore/tcell/v2/doc.go new file mode 100644 index 0000000000..b671961359 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/doc.go @@ -0,0 +1,48 @@ +// Copyright 2018 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +// Package tcell provides a lower-level, portable API for building +// programs that interact with terminals or consoles. It works with +// both common (and many uncommon!) terminals or terminal emulators, +// and Windows console implementations. +// +// It provides support for up to 256 colors, text attributes, and box drawing +// elements. A database of terminals built from a real terminfo database +// is provided, along with code to generate new database entries. +// +// Tcell offers very rich support for mice, dependent upon the terminal +// of course. (Windows, XTerm, and iTerm 2 are known to work very well.) +// +// If the environment is not Unicode by default, such as an ISO8859 based +// locale or GB18030, Tcell can convert input and output, so that your +// terminal can operate in whatever locale is most convenient, while the +// application program can just assume "everything is UTF-8". Reasonable +// defaults are used for updating characters to something suitable for +// display. Unicode box drawing characters will be converted to use the +// alternate character set of your terminal, if native conversions are +// not available. If no ACS is available, then some ASCII fallbacks will +// be used. +// +// Note that support for non-UTF-8 locales (other than C) must be enabled +// by the application using RegisterEncoding() -- we don't have them all +// enabled by default to avoid bloating the application unneccessarily. +// (These days UTF-8 is good enough for almost everyone, and nobody should +// be using legacy locales anymore.) Also, actual glyphs for various code +// point will only be displayed if your terminal or emulator (or the font +// the emulator is using) supports them. +// +// A rich set of keycodes is supported, with support for up to 65 function +// keys, and various other special keys. +// +package tcell diff --git a/vendor/github.com/gdamore/tcell/v2/encoding.go b/vendor/github.com/gdamore/tcell/v2/encoding.go new file mode 100644 index 0000000000..596a6e8005 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/encoding.go @@ -0,0 +1,139 @@ +// Copyright 2015 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + "strings" + "sync" + + "golang.org/x/text/encoding" + + gencoding "github.com/gdamore/encoding" +) + +var encodings map[string]encoding.Encoding +var encodingLk sync.Mutex +var encodingFallback EncodingFallback = EncodingFallbackFail + +// RegisterEncoding may be called by the application to register an encoding. +// The presence of additional encodings will facilitate application usage with +// terminal environments where the I/O subsystem does not support Unicode. +// +// Windows systems use Unicode natively, and do not need any of the encoding +// subsystem when using Windows Console screens. +// +// Please see the Go documentation for golang.org/x/text/encoding -- most of +// the common ones exist already as stock variables. For example, ISO8859-15 +// can be registered using the following code: +// +// import "golang.org/x/text/encoding/charmap" +// +// ... +// RegisterEncoding("ISO8859-15", charmap.ISO8859_15) +// +// Aliases can be registered as well, for example "8859-15" could be an alias +// for "ISO8859-15". +// +// For POSIX systems, the tcell package will check the environment variables +// LC_ALL, LC_CTYPE, and LANG (in that order) to determine the character set. +// These are expected to have the following pattern: +// +// $language[.$codeset[@$variant] +// +// We extract only the $codeset part, which will usually be something like +// UTF-8 or ISO8859-15 or KOI8-R. Note that if the locale is either "POSIX" +// or "C", then we assume US-ASCII (the POSIX 'portable character set' +// and assume all other characters are somehow invalid.) +// +// Modern POSIX systems and terminal emulators may use UTF-8, and for those +// systems, this API is also unnecessary. For example, Darwin (MacOS X) and +// modern Linux running modern xterm generally will out of the box without +// any of this. Use of UTF-8 is recommended when possible, as it saves +// quite a lot processing overhead. +// +// Note that some encodings are quite large (for example GB18030 which is a +// superset of Unicode) and so the application size can be expected ot +// increase quite a bit as each encoding is added. The East Asian encodings +// have been seen to add 100-200K per encoding to the application size. +// +func RegisterEncoding(charset string, enc encoding.Encoding) { + encodingLk.Lock() + charset = strings.ToLower(charset) + encodings[charset] = enc + encodingLk.Unlock() +} + +// EncodingFallback describes how the system behavees when the locale +// requires a character set that we do not support. The system always +// supports UTF-8 and US-ASCII. On Windows consoles, UTF-16LE is also +// supported automatically. Other character sets must be added using the +// RegisterEncoding API. (A large group of nearly all of them can be +// added using the RegisterAll function in the encoding sub package.) +type EncodingFallback int + +const ( + // EncodingFallbackFail behavior causes GetEncoding to fail + // when it cannot find an encoding. + EncodingFallbackFail = iota + + // EncodingFallbackASCII behaviore causes GetEncoding to fall back + // to a 7-bit ASCII encoding, if no other encoding can be found. + EncodingFallbackASCII + + // EncodingFallbackUTF8 behavior causes GetEncoding to assume + // UTF8 can pass unmodified upon failure. Note that this behavior + // is not recommended, unless you are sure your terminal can cope + // with real UTF8 sequences. + EncodingFallbackUTF8 +) + +// SetEncodingFallback changes the behavior of GetEncoding when a suitable +// encoding is not found. The default is EncodingFallbackFail, which +// causes GetEncoding to simply return nil. +func SetEncodingFallback(fb EncodingFallback) { + encodingLk.Lock() + encodingFallback = fb + encodingLk.Unlock() +} + +// GetEncoding is used by Screen implementors who want to locate an encoding +// for the given character set name. Note that this will return nil for +// either the Unicode (UTF-8) or ASCII encodings, since we don't use +// encodings for them but instead have our own native methods. +func GetEncoding(charset string) encoding.Encoding { + charset = strings.ToLower(charset) + encodingLk.Lock() + defer encodingLk.Unlock() + if enc, ok := encodings[charset]; ok { + return enc + } + switch encodingFallback { + case EncodingFallbackASCII: + return gencoding.ASCII + case EncodingFallbackUTF8: + return encoding.Nop + } + return nil +} + +func init() { + // We always support UTF-8 and ASCII. + encodings = make(map[string]encoding.Encoding) + encodings["utf-8"] = gencoding.UTF8 + encodings["utf8"] = gencoding.UTF8 + encodings["us-ascii"] = gencoding.ASCII + encodings["ascii"] = gencoding.ASCII + encodings["iso646"] = gencoding.ASCII +} diff --git a/vendor/github.com/gdamore/tcell/v2/errors.go b/vendor/github.com/gdamore/tcell/v2/errors.go new file mode 100644 index 0000000000..201dff9f80 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/errors.go @@ -0,0 +1,73 @@ +// Copyright 2015 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + "errors" + "time" + + "github.com/gdamore/tcell/v2/terminfo" +) + +var ( + // ErrTermNotFound indicates that a suitable terminal entry could + // not be found. This can result from either not having TERM set, + // or from the TERM failing to support certain minimal functionality, + // in particular absolute cursor addressability (the cup capability) + // is required. For example, legacy "adm3" lacks this capability, + // whereas the slightly newer "adm3a" supports it. This failure + // occurs most often with "dumb". + ErrTermNotFound = terminfo.ErrTermNotFound + + // ErrNoScreen indicates that no suitable screen could be found. + // This may result from attempting to run on a platform where there + // is no support for either termios or console I/O (such as nacl), + // or from running in an environment where there is no access to + // a suitable console/terminal device. (For example, running on + // without a controlling TTY or with no /dev/tty on POSIX platforms.) + ErrNoScreen = errors.New("no suitable screen available") + + // ErrNoCharset indicates that the locale environment the + // program is not supported by the program, because no suitable + // encoding was found for it. This problem never occurs if + // the environment is UTF-8 or UTF-16. + ErrNoCharset = errors.New("character set not supported") + + // ErrEventQFull indicates that the event queue is full, and + // cannot accept more events. + ErrEventQFull = errors.New("event queue full") +) + +// An EventError is an event representing some sort of error, and carries +// an error payload. +type EventError struct { + t time.Time + err error +} + +// When returns the time when the event was created. +func (ev *EventError) When() time.Time { + return ev.t +} + +// Error implements the error. +func (ev *EventError) Error() string { + return ev.err.Error() +} + +// NewEventError creates an ErrorEvent with the given error payload. +func NewEventError(err error) *EventError { + return &EventError{t: time.Now(), err: err} +} diff --git a/vendor/github.com/gdamore/tcell/v2/event.go b/vendor/github.com/gdamore/tcell/v2/event.go new file mode 100644 index 0000000000..a3b770063b --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/event.go @@ -0,0 +1,53 @@ +// Copyright 2015 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + "time" +) + +// Event is a generic interface used for passing around Events. +// Concrete types follow. +type Event interface { + // When reports the time when the event was generated. + When() time.Time +} + +// EventTime is a simple base event class, suitable for easy reuse. +// It can be used to deliver actual timer events as well. +type EventTime struct { + when time.Time +} + +// When returns the time stamp when the event occurred. +func (e *EventTime) When() time.Time { + return e.when +} + +// SetEventTime sets the time of occurrence for the event. +func (e *EventTime) SetEventTime(t time.Time) { + e.when = t +} + +// SetEventNow sets the time of occurrence for the event to the current time. +func (e *EventTime) SetEventNow() { + e.SetEventTime(time.Now()) +} + +// EventHandler is anything that handles events. If the handler has +// consumed the event, it should return true. False otherwise. +type EventHandler interface { + HandleEvent(Event) bool +} diff --git a/vendor/github.com/gdamore/tcell/v2/go.mod b/vendor/github.com/gdamore/tcell/v2/go.mod new file mode 100644 index 0000000000..af97fa00bc --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/go.mod @@ -0,0 +1,12 @@ +module github.com/gdamore/tcell/v2 + +go 1.12 + +require ( + github.com/gdamore/encoding v1.0.0 + github.com/lucasb-eyer/go-colorful v1.0.3 + github.com/mattn/go-runewidth v0.0.10 + golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 + golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf + golang.org/x/text v0.3.0 +) diff --git a/vendor/github.com/gdamore/tcell/v2/go.sum b/vendor/github.com/gdamore/tcell/v2/go.sum new file mode 100644 index 0000000000..068fabd988 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/go.sum @@ -0,0 +1,14 @@ +github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/vendor/github.com/gdamore/tcell/v2/interrupt.go b/vendor/github.com/gdamore/tcell/v2/interrupt.go new file mode 100644 index 0000000000..70dddfce2f --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/interrupt.go @@ -0,0 +1,41 @@ +// Copyright 2015 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + "time" +) + +// EventInterrupt is a generic wakeup event. Its can be used to +// to request a redraw. It can carry an arbitrary payload, as well. +type EventInterrupt struct { + t time.Time + v interface{} +} + +// When returns the time when this event was created. +func (ev *EventInterrupt) When() time.Time { + return ev.t +} + +// Data is used to obtain the opaque event payload. +func (ev *EventInterrupt) Data() interface{} { + return ev.v +} + +// NewEventInterrupt creates an EventInterrupt with the given payload. +func NewEventInterrupt(data interface{}) *EventInterrupt { + return &EventInterrupt{t: time.Now(), v: data} +} diff --git a/vendor/github.com/gdamore/tcell/v2/key.go b/vendor/github.com/gdamore/tcell/v2/key.go new file mode 100644 index 0000000000..9741e699fe --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/key.go @@ -0,0 +1,470 @@ +// Copyright 2016 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + "fmt" + "strings" + "time" +) + +// EventKey represents a key press. Usually this is a key press followed +// by a key release, but since terminal programs don't have a way to report +// key release events, we usually get just one event. If a key is held down +// then the terminal may synthesize repeated key presses at some predefined +// rate. We have no control over that, nor visibility into it. +// +// In some cases, we can have a modifier key, such as ModAlt, that can be +// generated with a key press. (This usually is represented by having the +// high bit set, or in some cases, by sending an ESC prior to the rune.) +// +// If the value of Key() is KeyRune, then the actual key value will be +// available with the Rune() method. This will be the case for most keys. +// In most situations, the modifiers will not be set. For example, if the +// rune is 'A', this will be reported without the ModShift bit set, since +// really can't tell if the Shift key was pressed (it might have been CAPSLOCK, +// or a terminal that only can send capitals, or keyboard with separate +// capital letters from lower case letters). +// +// Generally, terminal applications have far less visibility into keyboard +// activity than graphical applications. Hence, they should avoid depending +// overly much on availability of modifiers, or the availability of any +// specific keys. +type EventKey struct { + t time.Time + mod ModMask + key Key + ch rune +} + +// When returns the time when this Event was created, which should closely +// match the time when the key was pressed. +func (ev *EventKey) When() time.Time { + return ev.t +} + +// Rune returns the rune corresponding to the key press, if it makes sense. +// The result is only defined if the value of Key() is KeyRune. +func (ev *EventKey) Rune() rune { + return ev.ch +} + +// Key returns a virtual key code. We use this to identify specific key +// codes, such as KeyEnter, etc. Most control and function keys are reported +// with unique Key values. Normal alphanumeric and punctuation keys will +// generally return KeyRune here; the specific key can be further decoded +// using the Rune() function. +func (ev *EventKey) Key() Key { + return ev.key +} + +// Modifiers returns the modifiers that were present with the key press. Note +// that not all platforms and terminals support this equally well, and some +// cases we will not not know for sure. Hence, applications should avoid +// using this in most circumstances. +func (ev *EventKey) Modifiers() ModMask { + return ev.mod +} + +// KeyNames holds the written names of special keys. Useful to echo back a key +// name, or to look up a key from a string value. +var KeyNames = map[Key]string{ + KeyEnter: "Enter", + KeyBackspace: "Backspace", + KeyTab: "Tab", + KeyBacktab: "Backtab", + KeyEsc: "Esc", + KeyBackspace2: "Backspace2", + KeyDelete: "Delete", + KeyInsert: "Insert", + KeyUp: "Up", + KeyDown: "Down", + KeyLeft: "Left", + KeyRight: "Right", + KeyHome: "Home", + KeyEnd: "End", + KeyUpLeft: "UpLeft", + KeyUpRight: "UpRight", + KeyDownLeft: "DownLeft", + KeyDownRight: "DownRight", + KeyCenter: "Center", + KeyPgDn: "PgDn", + KeyPgUp: "PgUp", + KeyClear: "Clear", + KeyExit: "Exit", + KeyCancel: "Cancel", + KeyPause: "Pause", + KeyPrint: "Print", + KeyF1: "F1", + KeyF2: "F2", + KeyF3: "F3", + KeyF4: "F4", + KeyF5: "F5", + KeyF6: "F6", + KeyF7: "F7", + KeyF8: "F8", + KeyF9: "F9", + KeyF10: "F10", + KeyF11: "F11", + KeyF12: "F12", + KeyF13: "F13", + KeyF14: "F14", + KeyF15: "F15", + KeyF16: "F16", + KeyF17: "F17", + KeyF18: "F18", + KeyF19: "F19", + KeyF20: "F20", + KeyF21: "F21", + KeyF22: "F22", + KeyF23: "F23", + KeyF24: "F24", + KeyF25: "F25", + KeyF26: "F26", + KeyF27: "F27", + KeyF28: "F28", + KeyF29: "F29", + KeyF30: "F30", + KeyF31: "F31", + KeyF32: "F32", + KeyF33: "F33", + KeyF34: "F34", + KeyF35: "F35", + KeyF36: "F36", + KeyF37: "F37", + KeyF38: "F38", + KeyF39: "F39", + KeyF40: "F40", + KeyF41: "F41", + KeyF42: "F42", + KeyF43: "F43", + KeyF44: "F44", + KeyF45: "F45", + KeyF46: "F46", + KeyF47: "F47", + KeyF48: "F48", + KeyF49: "F49", + KeyF50: "F50", + KeyF51: "F51", + KeyF52: "F52", + KeyF53: "F53", + KeyF54: "F54", + KeyF55: "F55", + KeyF56: "F56", + KeyF57: "F57", + KeyF58: "F58", + KeyF59: "F59", + KeyF60: "F60", + KeyF61: "F61", + KeyF62: "F62", + KeyF63: "F63", + KeyF64: "F64", + KeyCtrlA: "Ctrl-A", + KeyCtrlB: "Ctrl-B", + KeyCtrlC: "Ctrl-C", + KeyCtrlD: "Ctrl-D", + KeyCtrlE: "Ctrl-E", + KeyCtrlF: "Ctrl-F", + KeyCtrlG: "Ctrl-G", + KeyCtrlJ: "Ctrl-J", + KeyCtrlK: "Ctrl-K", + KeyCtrlL: "Ctrl-L", + KeyCtrlN: "Ctrl-N", + KeyCtrlO: "Ctrl-O", + KeyCtrlP: "Ctrl-P", + KeyCtrlQ: "Ctrl-Q", + KeyCtrlR: "Ctrl-R", + KeyCtrlS: "Ctrl-S", + KeyCtrlT: "Ctrl-T", + KeyCtrlU: "Ctrl-U", + KeyCtrlV: "Ctrl-V", + KeyCtrlW: "Ctrl-W", + KeyCtrlX: "Ctrl-X", + KeyCtrlY: "Ctrl-Y", + KeyCtrlZ: "Ctrl-Z", + KeyCtrlSpace: "Ctrl-Space", + KeyCtrlUnderscore: "Ctrl-_", + KeyCtrlRightSq: "Ctrl-]", + KeyCtrlBackslash: "Ctrl-\\", + KeyCtrlCarat: "Ctrl-^", +} + +// Name returns a printable value or the key stroke. This can be used +// when printing the event, for example. +func (ev *EventKey) Name() string { + s := "" + m := []string{} + if ev.mod&ModShift != 0 { + m = append(m, "Shift") + } + if ev.mod&ModAlt != 0 { + m = append(m, "Alt") + } + if ev.mod&ModMeta != 0 { + m = append(m, "Meta") + } + if ev.mod&ModCtrl != 0 { + m = append(m, "Ctrl") + } + + ok := false + if s, ok = KeyNames[ev.key]; !ok { + if ev.key == KeyRune { + s = "Rune[" + string(ev.ch) + "]" + } else { + s = fmt.Sprintf("Key[%d,%d]", ev.key, int(ev.ch)) + } + } + if len(m) != 0 { + if ev.mod&ModCtrl != 0 && strings.HasPrefix(s, "Ctrl-") { + s = s[5:] + } + return fmt.Sprintf("%s+%s", strings.Join(m, "+"), s) + } + return s +} + +// NewEventKey attempts to create a suitable event. It parses the various +// ASCII control sequences if KeyRune is passed for Key, but if the caller +// has more precise information it should set that specifically. Callers +// that aren't sure about modifier state (most) should just pass ModNone. +func NewEventKey(k Key, ch rune, mod ModMask) *EventKey { + if k == KeyRune && (ch < ' ' || ch == 0x7f) { + // Turn specials into proper key codes. This is for + // control characters and the DEL. + k = Key(ch) + if mod == ModNone && ch < ' ' { + switch Key(ch) { + case KeyBackspace, KeyTab, KeyEsc, KeyEnter: + // these keys are directly typeable without CTRL + default: + // most likely entered with a CTRL keypress + mod = ModCtrl + } + } + } + return &EventKey{t: time.Now(), key: k, ch: ch, mod: mod} +} + +// ModMask is a mask of modifier keys. Note that it will not always be +// possible to report modifier keys. +type ModMask int16 + +// These are the modifiers keys that can be sent either with a key press, +// or a mouse event. Note that as of now, due to the confusion associated +// with Meta, and the lack of support for it on many/most platforms, the +// current implementations never use it. Instead, they use ModAlt, even for +// events that could possibly have been distinguished from ModAlt. +const ( + ModShift ModMask = 1 << iota + ModCtrl + ModAlt + ModMeta + ModNone ModMask = 0 +) + +// Key is a generic value for representing keys, and especially special +// keys (function keys, cursor movement keys, etc.) For normal keys, like +// ASCII letters, we use KeyRune, and then expect the application to +// inspect the Rune() member of the EventKey. +type Key int16 + +// This is the list of named keys. KeyRune is special however, in that it is +// a place holder key indicating that a printable character was sent. The +// actual value of the rune will be transported in the Rune of the associated +// EventKey. +const ( + KeyRune Key = iota + 256 + KeyUp + KeyDown + KeyRight + KeyLeft + KeyUpLeft + KeyUpRight + KeyDownLeft + KeyDownRight + KeyCenter + KeyPgUp + KeyPgDn + KeyHome + KeyEnd + KeyInsert + KeyDelete + KeyHelp + KeyExit + KeyClear + KeyCancel + KeyPrint + KeyPause + KeyBacktab + KeyF1 + KeyF2 + KeyF3 + KeyF4 + KeyF5 + KeyF6 + KeyF7 + KeyF8 + KeyF9 + KeyF10 + KeyF11 + KeyF12 + KeyF13 + KeyF14 + KeyF15 + KeyF16 + KeyF17 + KeyF18 + KeyF19 + KeyF20 + KeyF21 + KeyF22 + KeyF23 + KeyF24 + KeyF25 + KeyF26 + KeyF27 + KeyF28 + KeyF29 + KeyF30 + KeyF31 + KeyF32 + KeyF33 + KeyF34 + KeyF35 + KeyF36 + KeyF37 + KeyF38 + KeyF39 + KeyF40 + KeyF41 + KeyF42 + KeyF43 + KeyF44 + KeyF45 + KeyF46 + KeyF47 + KeyF48 + KeyF49 + KeyF50 + KeyF51 + KeyF52 + KeyF53 + KeyF54 + KeyF55 + KeyF56 + KeyF57 + KeyF58 + KeyF59 + KeyF60 + KeyF61 + KeyF62 + KeyF63 + KeyF64 +) + +const ( + // These key codes are used internally, and will never appear to applications. + keyPasteStart Key = iota + 16384 + keyPasteEnd +) + +// These are the control keys. Note that they overlap with other keys, +// perhaps. For example, KeyCtrlH is the same as KeyBackspace. +const ( + KeyCtrlSpace Key = iota + KeyCtrlA + KeyCtrlB + KeyCtrlC + KeyCtrlD + KeyCtrlE + KeyCtrlF + KeyCtrlG + KeyCtrlH + KeyCtrlI + KeyCtrlJ + KeyCtrlK + KeyCtrlL + KeyCtrlM + KeyCtrlN + KeyCtrlO + KeyCtrlP + KeyCtrlQ + KeyCtrlR + KeyCtrlS + KeyCtrlT + KeyCtrlU + KeyCtrlV + KeyCtrlW + KeyCtrlX + KeyCtrlY + KeyCtrlZ + KeyCtrlLeftSq // Escape + KeyCtrlBackslash + KeyCtrlRightSq + KeyCtrlCarat + KeyCtrlUnderscore +) + +// Special values - these are fixed in an attempt to make it more likely +// that aliases will encode the same way. + +// These are the defined ASCII values for key codes. They generally match +// with KeyCtrl values. +const ( + KeyNUL Key = iota + KeySOH + KeySTX + KeyETX + KeyEOT + KeyENQ + KeyACK + KeyBEL + KeyBS + KeyTAB + KeyLF + KeyVT + KeyFF + KeyCR + KeySO + KeySI + KeyDLE + KeyDC1 + KeyDC2 + KeyDC3 + KeyDC4 + KeyNAK + KeySYN + KeyETB + KeyCAN + KeyEM + KeySUB + KeyESC + KeyFS + KeyGS + KeyRS + KeyUS + KeyDEL Key = 0x7F +) + +// These keys are aliases for other names. +const ( + KeyBackspace = KeyBS + KeyTab = KeyTAB + KeyEsc = KeyESC + KeyEscape = KeyESC + KeyEnter = KeyCR + KeyBackspace2 = KeyDEL +) diff --git a/vendor/github.com/gdamore/tcell/v2/mouse.go b/vendor/github.com/gdamore/tcell/v2/mouse.go new file mode 100644 index 0000000000..008c2e26c3 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/mouse.go @@ -0,0 +1,103 @@ +// Copyright 2020 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + "time" +) + +// EventMouse is a mouse event. It is sent on either mouse up or mouse down +// events. It is also sent on mouse motion events - if the terminal supports +// it. We make every effort to ensure that mouse release events are delivered. +// Hence, click drag can be identified by a motion event with the mouse down, +// without any intervening button release. On some terminals only the initiating +// press and terminating release event will be delivered. +// +// Mouse wheel events, when reported, may appear on their own as individual +// impulses; that is, there will normally not be a release event delivered +// for mouse wheel movements. +// +// Most terminals cannot report the state of more than one button at a time -- +// and some cannot report motion events unless a button is pressed. +// +// Applications can inspect the time between events to resolve double or +// triple clicks. +type EventMouse struct { + t time.Time + btn ButtonMask + mod ModMask + x int + y int +} + +// When returns the time when this EventMouse was created. +func (ev *EventMouse) When() time.Time { + return ev.t +} + +// Buttons returns the list of buttons that were pressed or wheel motions. +func (ev *EventMouse) Buttons() ButtonMask { + return ev.btn +} + +// Modifiers returns a list of keyboard modifiers that were pressed +// with the mouse button(s). +func (ev *EventMouse) Modifiers() ModMask { + return ev.mod +} + +// Position returns the mouse position in character cells. The origin +// 0, 0 is at the upper left corner. +func (ev *EventMouse) Position() (int, int) { + return ev.x, ev.y +} + +// NewEventMouse is used to create a new mouse event. Applications +// shouldn't need to use this; its mostly for screen implementors. +func NewEventMouse(x, y int, btn ButtonMask, mod ModMask) *EventMouse { + return &EventMouse{t: time.Now(), x: x, y: y, btn: btn, mod: mod} +} + +// ButtonMask is a mask of mouse buttons and wheel events. Mouse button presses +// are normally delivered as both press and release events. Mouse wheel events +// are normally just single impulse events. Windows supports up to eight +// separate buttons plus all four wheel directions, but XTerm can only support +// mouse buttons 1-3 and wheel up/down. Its not unheard of for terminals +// to support only one or two buttons (think Macs). Old terminals, and true +// emulations (such as vt100) won't support mice at all, of course. +type ButtonMask int16 + +// These are the actual button values. Note that tcell version 1.x reversed buttons +// two and three on *nix based terminals. We use button 1 as the primary, and +// button 2 as the secondary, and button 3 (which is often missing) as the middle. +const ( + Button1 ButtonMask = 1 << iota // Usually the left (primary) mouse button. + Button2 // Usually the right (secondary) mouse button. + Button3 // Usually the middle mouse button. + Button4 // Often a side button (thumb/next). + Button5 // Often a side button (thumb/prev). + Button6 + Button7 + Button8 + WheelUp // Wheel motion up/away from user. + WheelDown // Wheel motion down/towards user. + WheelLeft // Wheel motion to left. + WheelRight // Wheel motion to right. + ButtonNone ButtonMask = 0 // No button or wheel events. + + ButtonPrimary = Button1 + ButtonSecondary = Button2 + ButtonMiddle = Button3 +) diff --git a/vendor/github.com/gdamore/tcell/v2/nonblock_bsd.go b/vendor/github.com/gdamore/tcell/v2/nonblock_bsd.go new file mode 100644 index 0000000000..28fd4644c7 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/nonblock_bsd.go @@ -0,0 +1,42 @@ +// Copyright 2021 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +// +build darwin dragonfly freebsd netbsd openbsd + +package tcell + +import ( + "syscall" + + "golang.org/x/sys/unix" +) + +// BSD systems use TIOC style ioctls. + +// tcSetBufParams is used by the tty driver on UNIX systems to configure the +// buffering parameters (minimum character count and minimum wait time in msec.) +// This also waits for output to drain first. +func tcSetBufParams(fd int, vMin uint8, vTime uint8) error { + _ = syscall.SetNonblock(fd, true) + tio, err := unix.IoctlGetTermios(fd, unix.TIOCGETA) + if err != nil { + return err + } + tio.Cc[unix.VMIN] = vMin + tio.Cc[unix.VTIME] = vTime + if err = unix.IoctlSetTermios(fd, unix.TIOCSETAW, tio); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/gdamore/tcell/v2/nonblock_unix.go b/vendor/github.com/gdamore/tcell/v2/nonblock_unix.go new file mode 100644 index 0000000000..fe31844cf5 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/nonblock_unix.go @@ -0,0 +1,40 @@ +// Copyright 2021 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +// +build linux aix zos solaris + +package tcell + +import ( + "syscall" + + "golang.org/x/sys/unix" +) + +// tcSetBufParams is used by the tty driver on UNIX systems to configure the +// buffering parameters (minimum character count and minimum wait time in msec.) +// This also waits for output to drain first. +func tcSetBufParams(fd int, vMin uint8, vTime uint8) error { + _ = syscall.SetNonblock(fd, true) + tio, err := unix.IoctlGetTermios(fd, unix.TCGETS) + if err != nil { + return err + } + tio.Cc[unix.VMIN] = vMin + tio.Cc[unix.VTIME] = vTime + if err = unix.IoctlSetTermios(fd, unix.TCSETSW, tio); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/gdamore/tcell/v2/paste.go b/vendor/github.com/gdamore/tcell/v2/paste.go new file mode 100644 index 0000000000..71cf8b1cce --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/paste.go @@ -0,0 +1,48 @@ +// Copyright 2020 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + "time" +) + +// EventPaste is used to mark the start and end of a bracketed paste. +// An event with .Start() true will be sent to mark the start. +// Then a number of keys will be sent to indicate that the content +// is pasted in. At the end, an event with .Start() false will be sent. +type EventPaste struct { + start bool + t time.Time +} + +// When returns the time when this EventMouse was created. +func (ev *EventPaste) When() time.Time { + return ev.t +} + +// Start returns true if this is the start of a paste. +func (ev *EventPaste) Start() bool { + return ev.start +} + +// End returns true if this is the end of a paste. +func (ev *EventPaste) End() bool { + return !ev.start +} + +// NewEventPaste returns a new EventPaste. +func NewEventPaste(start bool) *EventPaste { + return &EventPaste{t: time.Now(), start: start} +} diff --git a/vendor/github.com/gdamore/tcell/v2/resize.go b/vendor/github.com/gdamore/tcell/v2/resize.go new file mode 100644 index 0000000000..0385673c83 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/resize.go @@ -0,0 +1,42 @@ +// Copyright 2015 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + "time" +) + +// EventResize is sent when the window size changes. +type EventResize struct { + t time.Time + w int + h int +} + +// NewEventResize creates an EventResize with the new updated window size, +// which is given in character cells. +func NewEventResize(width, height int) *EventResize { + return &EventResize{t: time.Now(), w: width, h: height} +} + +// When returns the time when the Event was created. +func (ev *EventResize) When() time.Time { + return ev.t +} + +// Size returns the new window size as width, height in character cells. +func (ev *EventResize) Size() (int, int) { + return ev.w, ev.h +} diff --git a/vendor/github.com/gdamore/tcell/v2/runes.go b/vendor/github.com/gdamore/tcell/v2/runes.go new file mode 100644 index 0000000000..ed9c63b5c1 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/runes.go @@ -0,0 +1,111 @@ +// Copyright 2015 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +// The names of these constants are chosen to match Terminfo names, +// modulo case, and changing the prefix from ACS_ to Rune. These are +// the runes we provide extra special handling for, with ASCII fallbacks +// for terminals that lack them. +const ( + RuneSterling = '£' + RuneDArrow = '↓' + RuneLArrow = '←' + RuneRArrow = '→' + RuneUArrow = '↑' + RuneBullet = '·' + RuneBoard = '░' + RuneCkBoard = '▒' + RuneDegree = '°' + RuneDiamond = '◆' + RuneGEqual = '≥' + RunePi = 'π' + RuneHLine = '─' + RuneLantern = '§' + RunePlus = '┼' + RuneLEqual = '≤' + RuneLLCorner = '└' + RuneLRCorner = '┘' + RuneNEqual = '≠' + RunePlMinus = '±' + RuneS1 = '⎺' + RuneS3 = '⎻' + RuneS7 = '⎼' + RuneS9 = '⎽' + RuneBlock = '█' + RuneTTee = '┬' + RuneRTee = '┤' + RuneLTee = '├' + RuneBTee = '┴' + RuneULCorner = '┌' + RuneURCorner = '┐' + RuneVLine = '│' +) + +// RuneFallbacks is the default map of fallback strings that will be +// used to replace a rune when no other more appropriate transformation +// is available, and the rune cannot be displayed directly. +// +// New entries may be added to this map over time, as it becomes clear +// that such is desirable. Characters that represent either letters or +// numbers should not be added to this list unless it is certain that +// the meaning will still convey unambiguously. +// +// As an example, it would be appropriate to add an ASCII mapping for +// the full width form of the letter 'A', but it would not be appropriate +// to do so a glyph representing the country China. +// +// Programs that desire richer fallbacks may register additional ones, +// or change or even remove these mappings with Screen.RegisterRuneFallback +// Screen.UnregisterRuneFallback methods. +// +// Note that Unicode is presumed to be able to display all glyphs. +// This is a pretty poor assumption, but there is no easy way to +// figure out which glyphs are supported in a given font. Hence, +// some care in selecting the characters you support in your application +// is still appropriate. +var RuneFallbacks = map[rune]string{ + RuneSterling: "f", + RuneDArrow: "v", + RuneLArrow: "<", + RuneRArrow: ">", + RuneUArrow: "^", + RuneBullet: "o", + RuneBoard: "#", + RuneCkBoard: ":", + RuneDegree: "\\", + RuneDiamond: "+", + RuneGEqual: ">", + RunePi: "*", + RuneHLine: "-", + RuneLantern: "#", + RunePlus: "+", + RuneLEqual: "<", + RuneLLCorner: "+", + RuneLRCorner: "+", + RuneNEqual: "!", + RunePlMinus: "#", + RuneS1: "~", + RuneS3: "-", + RuneS7: "-", + RuneS9: "_", + RuneBlock: "#", + RuneTTee: "+", + RuneRTee: "+", + RuneLTee: "+", + RuneBTee: "+", + RuneULCorner: "+", + RuneURCorner: "+", + RuneVLine: "|", +} diff --git a/vendor/github.com/gdamore/tcell/v2/screen.go b/vendor/github.com/gdamore/tcell/v2/screen.go new file mode 100644 index 0000000000..cbe505f9e4 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/screen.go @@ -0,0 +1,260 @@ +// Copyright 2021 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +// Screen represents the physical (or emulated) screen. +// This can be a terminal window or a physical console. Platforms implement +// this differently. +type Screen interface { + // Init initializes the screen for use. + Init() error + + // Fini finalizes the screen also releasing resources. + Fini() + + // Clear erases the screen. The contents of any screen buffers + // will also be cleared. This has the logical effect of + // filling the screen with spaces, using the global default style. + Clear() + + // Fill fills the screen with the given character and style. + Fill(rune, Style) + + // SetCell is an older API, and will be removed. Please use + // SetContent instead; SetCell is implemented in terms of SetContent. + SetCell(x int, y int, style Style, ch ...rune) + + // GetContent returns the contents at the given location. If the + // coordinates are out of range, then the values will be 0, nil, + // StyleDefault. Note that the contents returned are logical contents + // and may not actually be what is displayed, but rather are what will + // be displayed if Show() or Sync() is called. The width is the width + // in screen cells; most often this will be 1, but some East Asian + // characters require two cells. + GetContent(x, y int) (mainc rune, combc []rune, style Style, width int) + + // SetContent sets the contents of the given cell location. If + // the coordinates are out of range, then the operation is ignored. + // + // The first rune is the primary non-zero width rune. The array + // that follows is a possible list of combining characters to append, + // and will usually be nil (no combining characters.) + // + // The results are not displayd until Show() or Sync() is called. + // + // Note that wide (East Asian full width) runes occupy two cells, + // and attempts to place character at next cell to the right will have + // undefined effects. Wide runes that are printed in the + // last column will be replaced with a single width space on output. + SetContent(x int, y int, mainc rune, combc []rune, style Style) + + // SetStyle sets the default style to use when clearing the screen + // or when StyleDefault is specified. If it is also StyleDefault, + // then whatever system/terminal default is relevant will be used. + SetStyle(style Style) + + // ShowCursor is used to display the cursor at a given location. + // If the coordinates -1, -1 are given or are otherwise outside the + // dimensions of the screen, the cursor will be hidden. + ShowCursor(x int, y int) + + // HideCursor is used to hide the cursor. Its an alias for + // ShowCursor(-1, -1). + HideCursor() + + // Size returns the screen size as width, height. This changes in + // response to a call to Clear or Flush. + Size() (int, int) + + // ChannelEvents is an infinite loop that waits for an event and + // channels it into the user provided channel ch. Closing the + // quit channel and calling the Fini method are cancellation + // signals. When a cancellation signal is received the method + // returns after closing ch. + // + // This method should be used as a goroutine. + // + // NOTE: PollEvent should not be called while this method is running. + ChannelEvents(ch chan<- Event, quit <-chan struct{}) + + // PollEvent waits for events to arrive. Main application loops + // must spin on this to prevent the application from stalling. + // Furthermore, this will return nil if the Screen is finalized. + PollEvent() Event + + // HasPendingEvent returns true if PollEvent would return an event + // without blocking. If the screen is stopped and PollEvent would + // return nil, then the return value from this function is unspecified. + // The purpose of this function is to allow multiple events to be collected + // at once, to minimize screen redraws. + HasPendingEvent() bool + + // PostEvent tries to post an event into the event stream. This + // can fail if the event queue is full. In that case, the event + // is dropped, and ErrEventQFull is returned. + PostEvent(ev Event) error + + // Deprecated: PostEventWait is unsafe, and will be removed + // in the future. + // + // PostEventWait is like PostEvent, but if the queue is full, it + // blocks until there is space in the queue, making delivery + // reliable. However, it is VERY important that this function + // never be called from within whatever event loop is polling + // with PollEvent(), otherwise a deadlock may arise. + // + // For this reason, when using this function, the use of a + // Goroutine is recommended to ensure no deadlock can occur. + PostEventWait(ev Event) + + // EnableMouse enables the mouse. (If your terminal supports it.) + // If no flags are specified, then all events are reported, if the + // terminal supports them. + EnableMouse(...MouseFlags) + + // DisableMouse disables the mouse. + DisableMouse() + + // EnablePaste enables bracketed paste mode, if supported. + EnablePaste() + + // DisablePaste disables bracketed paste mode. + DisablePaste() + + // HasMouse returns true if the terminal (apparently) supports a + // mouse. Note that the a return value of true doesn't guarantee that + // a mouse/pointing device is present; a false return definitely + // indicates no mouse support is available. + HasMouse() bool + + // Colors returns the number of colors. All colors are assumed to + // use the ANSI color map. If a terminal is monochrome, it will + // return 0. + Colors() int + + // Show makes all the content changes made using SetContent() visible + // on the display. + // + // It does so in the most efficient and least visually disruptive + // manner possible. + Show() + + // Sync works like Show(), but it updates every visible cell on the + // physical display, assuming that it is not synchronized with any + // internal model. This may be both expensive and visually jarring, + // so it should only be used when believed to actually be necessary. + // + // Typically this is called as a result of a user-requested redraw + // (e.g. to clear up on screen corruption caused by some other program), + // or during a resize event. + Sync() + + // CharacterSet returns information about the character set. + // This isn't the full locale, but it does give us the input/output + // character set. Note that this is just for diagnostic purposes, + // we normally translate input/output to/from UTF-8, regardless of + // what the user's environment is. + CharacterSet() string + + // RegisterRuneFallback adds a fallback for runes that are not + // part of the character set -- for example one could register + // o as a fallback for ø. This should be done cautiously for + // characters that might be displayed ordinarily in language + // specific text -- characters that could change the meaning of + // of written text would be dangerous. The intention here is to + // facilitate fallback characters in pseudo-graphical applications. + // + // If the terminal has fallbacks already in place via an alternate + // character set, those are used in preference. Also, standard + // fallbacks for graphical characters in the ACSC terminfo string + // are registered implicitly. + // + // The display string should be the same width as original rune. + // This makes it possible to register two character replacements + // for full width East Asian characters, for example. + // + // It is recommended that replacement strings consist only of + // 7-bit ASCII, since other characters may not display everywhere. + RegisterRuneFallback(r rune, subst string) + + // UnregisterRuneFallback unmaps a replacement. It will unmap + // the implicit ASCII replacements for alternate characters as well. + // When an unmapped char needs to be displayed, but no suitable + // glyph is available, '?' is emitted instead. It is not possible + // to "disable" the use of alternate characters that are supported + // by your terminal except by changing the terminal database. + UnregisterRuneFallback(r rune) + + // CanDisplay returns true if the given rune can be displayed on + // this screen. Note that this is a best guess effort -- whether + // your fonts support the character or not may be questionable. + // Mostly this is for folks who work outside of Unicode. + // + // If checkFallbacks is true, then if any (possibly imperfect) + // fallbacks are registered, this will return true. This will + // also return true if the terminal can replace the glyph with + // one that is visually indistinguishable from the one requested. + CanDisplay(r rune, checkFallbacks bool) bool + + // Resize does nothing, since its generally not possible to + // ask a screen to resize, but it allows the Screen to implement + // the View interface. + Resize(int, int, int, int) + + // HasKey returns true if the keyboard is believed to have the + // key. In some cases a keyboard may have keys with this name + // but no support for them, while in others a key may be reported + // as supported but not actually be usable (such as some emulators + // that hijack certain keys). Its best not to depend to strictly + // on this function, but it can be used for hinting when building + // menus, displayed hot-keys, etc. Note that KeyRune (literal + // runes) is always true. + HasKey(Key) bool + + // Suspend pauses input and output processing. It also restores the + // terminal settings to what they were when the application started. + // This can be used to, for example, run a sub-shell. + Suspend() error + + // Resume resumes after Suspend(). + Resume() error + + // Beep attempts to sound an OS-dependent audible alert and returns an error + // when unsuccessful. + Beep() error +} + +// NewScreen returns a default Screen suitable for the user's terminal +// environment. +func NewScreen() (Screen, error) { + // Windows is happier if we try for a console screen first. + if s, _ := NewConsoleScreen(); s != nil { + return s, nil + } else if s, e := NewTerminfoScreen(); s != nil { + return s, nil + } else { + return nil, e + } +} + +// MouseFlags are options to modify the handling of mouse events. +// Actual events can be or'd together. +type MouseFlags int + +const ( + MouseButtonEvents = MouseFlags(1) // Click events only + MouseDragEvents = MouseFlags(2) // Click-drag events (includes button events) + MouseMotionEvents = MouseFlags(4) // All mouse events (includes click and drag events) +) diff --git a/vendor/github.com/gdamore/tcell/v2/simulation.go b/vendor/github.com/gdamore/tcell/v2/simulation.go new file mode 100644 index 0000000000..451460be25 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/simulation.go @@ -0,0 +1,554 @@ +// Copyright 2021 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + "sync" + "unicode/utf8" + + "golang.org/x/text/transform" +) + +// NewSimulationScreen returns a SimulationScreen. Note that +// SimulationScreen is also a Screen. +func NewSimulationScreen(charset string) SimulationScreen { + if charset == "" { + charset = "UTF-8" + } + s := &simscreen{charset: charset} + return s +} + +// SimulationScreen represents a screen simulation. This is intended to +// be a superset of normal Screens, but also adds some important interfaces +// for testing. +type SimulationScreen interface { + // InjectKeyBytes injects a stream of bytes corresponding to + // the native encoding (see charset). It turns true if the entire + // set of bytes were processed and delivered as KeyEvents, false + // if any bytes were not fully understood. Any bytes that are not + // fully converted are discarded. + InjectKeyBytes(buf []byte) bool + + // InjectKey injects a key event. The rune is a UTF-8 rune, post + // any translation. + InjectKey(key Key, r rune, mod ModMask) + + // InjectMouse injects a mouse event. + InjectMouse(x, y int, buttons ButtonMask, mod ModMask) + + // SetSize resizes the underlying physical screen. It also causes + // a resize event to be injected during the next Show() or Sync(). + // A new physical contents array will be allocated (with data from + // the old copied), so any prior value obtained with GetContents + // won't be used anymore + SetSize(width, height int) + + // GetContents returns screen contents as an array of + // cells, along with the physical width & height. Note that the + // physical contents will be used until the next time SetSize() + // is called. + GetContents() (cells []SimCell, width int, height int) + + // GetCursor returns the cursor details. + GetCursor() (x int, y int, visible bool) + + Screen +} + +// SimCell represents a simulated screen cell. The purpose of this +// is to track on screen content. +type SimCell struct { + // Bytes is the actual character bytes. Normally this is + // rune data, but it could be be data in another encoding system. + Bytes []byte + + // Style is the style used to display the data. + Style Style + + // Runes is the list of runes, unadulterated, in UTF-8. + Runes []rune +} + +type simscreen struct { + physw int + physh int + fini bool + style Style + evch chan Event + quit chan struct{} + + front []SimCell + back CellBuffer + clear bool + cursorx int + cursory int + cursorvis bool + mouse bool + paste bool + charset string + encoder transform.Transformer + decoder transform.Transformer + fillchar rune + fillstyle Style + fallback map[rune]string + + sync.Mutex +} + +func (s *simscreen) Init() error { + s.evch = make(chan Event, 10) + s.quit = make(chan struct{}) + s.fillchar = 'X' + s.fillstyle = StyleDefault + s.mouse = false + s.physw = 80 + s.physh = 25 + s.cursorx = -1 + s.cursory = -1 + s.style = StyleDefault + + if enc := GetEncoding(s.charset); enc != nil { + s.encoder = enc.NewEncoder() + s.decoder = enc.NewDecoder() + } else { + return ErrNoCharset + } + + s.front = make([]SimCell, s.physw*s.physh) + s.back.Resize(80, 25) + + // default fallbacks + s.fallback = make(map[rune]string) + for k, v := range RuneFallbacks { + s.fallback[k] = v + } + return nil +} + +func (s *simscreen) Fini() { + s.Lock() + s.fini = true + s.back.Resize(0, 0) + s.Unlock() + if s.quit != nil { + close(s.quit) + } + s.physw = 0 + s.physh = 0 + s.front = nil +} + +func (s *simscreen) SetStyle(style Style) { + s.Lock() + s.style = style + s.Unlock() +} + +func (s *simscreen) Clear() { + s.Fill(' ', s.style) +} + +func (s *simscreen) Fill(r rune, style Style) { + s.Lock() + s.back.Fill(r, style) + s.Unlock() +} + +func (s *simscreen) SetCell(x, y int, style Style, ch ...rune) { + + if len(ch) > 0 { + s.SetContent(x, y, ch[0], ch[1:], style) + } else { + s.SetContent(x, y, ' ', nil, style) + } +} + +func (s *simscreen) SetContent(x, y int, mainc rune, combc []rune, st Style) { + + s.Lock() + s.back.SetContent(x, y, mainc, combc, st) + s.Unlock() +} + +func (s *simscreen) GetContent(x, y int) (rune, []rune, Style, int) { + var mainc rune + var combc []rune + var style Style + var width int + s.Lock() + mainc, combc, style, width = s.back.GetContent(x, y) + s.Unlock() + return mainc, combc, style, width +} + +func (s *simscreen) drawCell(x, y int) int { + + mainc, combc, style, width := s.back.GetContent(x, y) + if !s.back.Dirty(x, y) { + return width + } + if x >= s.physw || y >= s.physh || x < 0 || y < 0 { + return width + } + simc := &s.front[(y*s.physw)+x] + + if style == StyleDefault { + style = s.style + } + simc.Style = style + simc.Runes = append([]rune{mainc}, combc...) + + // now emit runes - taking care to not overrun width with a + // wide character, and to ensure that we emit exactly one regular + // character followed up by any residual combing characters + + simc.Bytes = nil + + if x > s.physw-width { + simc.Runes = []rune{' '} + simc.Bytes = []byte{' '} + return width + } + + lbuf := make([]byte, 12) + ubuf := make([]byte, 12) + nout := 0 + + for _, r := range simc.Runes { + + l := utf8.EncodeRune(ubuf, r) + + nout, _, _ = s.encoder.Transform(lbuf, ubuf[:l], true) + + if nout == 0 || lbuf[0] == '\x1a' { + + // skip combining + + if subst, ok := s.fallback[r]; ok { + simc.Bytes = append(simc.Bytes, + []byte(subst)...) + + } else if r >= ' ' && r <= '~' { + simc.Bytes = append(simc.Bytes, byte(r)) + + } else if simc.Bytes == nil { + simc.Bytes = append(simc.Bytes, '?') + } + } else { + simc.Bytes = append(simc.Bytes, lbuf[:nout]...) + } + } + s.back.SetDirty(x, y, false) + return width +} + +func (s *simscreen) ShowCursor(x, y int) { + s.Lock() + s.cursorx, s.cursory = x, y + s.showCursor() + s.Unlock() +} + +func (s *simscreen) HideCursor() { + s.ShowCursor(-1, -1) +} + +func (s *simscreen) showCursor() { + + x, y := s.cursorx, s.cursory + if x < 0 || y < 0 || x >= s.physw || y >= s.physh { + s.cursorvis = false + } else { + s.cursorvis = true + } +} + +func (s *simscreen) hideCursor() { + // does not update cursor position + s.cursorvis = false +} + +func (s *simscreen) Show() { + s.Lock() + s.resize() + s.draw() + s.Unlock() +} + +func (s *simscreen) clearScreen() { + // We emulate a hardware clear by filling with a specific pattern + for i := range s.front { + s.front[i].Style = s.fillstyle + s.front[i].Runes = []rune{s.fillchar} + s.front[i].Bytes = []byte{byte(s.fillchar)} + } + s.clear = false +} + +func (s *simscreen) draw() { + s.hideCursor() + if s.clear { + s.clearScreen() + } + + w, h := s.back.Size() + for y := 0; y < h; y++ { + for x := 0; x < w; x++ { + width := s.drawCell(x, y) + x += width - 1 + } + } + s.showCursor() +} + +func (s *simscreen) EnableMouse(...MouseFlags) { + s.mouse = true +} + +func (s *simscreen) DisableMouse() { + s.mouse = false +} + +func (s *simscreen) EnablePaste() { + s.paste = true +} + +func (s *simscreen) DisablePaste() { + s.paste = false +} + +func (s *simscreen) Size() (int, int) { + s.Lock() + w, h := s.back.Size() + s.Unlock() + return w, h +} + +func (s *simscreen) resize() { + w, h := s.physw, s.physh + ow, oh := s.back.Size() + if w != ow || h != oh { + s.back.Resize(w, h) + ev := NewEventResize(w, h) + s.PostEvent(ev) + } +} + +func (s *simscreen) Colors() int { + return 256 +} + +func (s *simscreen) ChannelEvents(ch chan<- Event, quit <-chan struct{}) { + defer close(ch) + for { + select { + case <-quit: + return + case <-s.quit: + return + case ev := <-s.evch: + select { + case <-quit: + return + case <-s.quit: + return + case ch <- ev: + } + } + } +} + +func (s *simscreen) PollEvent() Event { + select { + case <-s.quit: + return nil + case ev := <-s.evch: + return ev + } +} + +func (s *simscreen) HasPendingEvent() bool { + return len(s.evch) > 0 +} + +func (s *simscreen) PostEventWait(ev Event) { + s.evch <- ev +} + +func (s *simscreen) PostEvent(ev Event) error { + select { + case s.evch <- ev: + return nil + default: + return ErrEventQFull + } +} + +func (s *simscreen) InjectMouse(x, y int, buttons ButtonMask, mod ModMask) { + ev := NewEventMouse(x, y, buttons, mod) + s.PostEvent(ev) +} + +func (s *simscreen) InjectKey(key Key, r rune, mod ModMask) { + ev := NewEventKey(key, r, mod) + s.PostEvent(ev) +} + +func (s *simscreen) InjectKeyBytes(b []byte) bool { + failed := false + +outer: + for len(b) > 0 { + if b[0] >= ' ' && b[0] <= 0x7F { + // printable ASCII easy to deal with -- no encodings + ev := NewEventKey(KeyRune, rune(b[0]), ModNone) + s.PostEvent(ev) + b = b[1:] + continue + } + + if b[0] < 0x80 { + mod := ModNone + // No encodings start with low numbered values + if Key(b[0]) >= KeyCtrlA && Key(b[0]) <= KeyCtrlZ { + mod = ModCtrl + } + ev := NewEventKey(Key(b[0]), 0, mod) + s.PostEvent(ev) + b = b[1:] + continue + } + + utfb := make([]byte, len(b)*4) // worst case + for l := 1; l < len(b); l++ { + s.decoder.Reset() + nout, nin, _ := s.decoder.Transform(utfb, b[:l], true) + + if nout != 0 { + r, _ := utf8.DecodeRune(utfb[:nout]) + if r != utf8.RuneError { + ev := NewEventKey(KeyRune, r, ModNone) + s.PostEvent(ev) + } + b = b[nin:] + continue outer + } + } + failed = true + b = b[1:] + continue + } + + return !failed +} + +func (s *simscreen) Sync() { + s.Lock() + s.clear = true + s.resize() + s.back.Invalidate() + s.draw() + s.Unlock() +} + +func (s *simscreen) CharacterSet() string { + return s.charset +} + +func (s *simscreen) SetSize(w, h int) { + s.Lock() + newc := make([]SimCell, w*h) + for row := 0; row < h && row < s.physh; row++ { + for col := 0; col < w && col < s.physw; col++ { + newc[(row*w)+col] = s.front[(row*s.physw)+col] + } + } + s.cursorx, s.cursory = -1, -1 + s.physw, s.physh = w, h + s.front = newc + s.back.Resize(w, h) + s.Unlock() +} + +func (s *simscreen) GetContents() ([]SimCell, int, int) { + s.Lock() + cells, w, h := s.front, s.physw, s.physh + s.Unlock() + return cells, w, h +} + +func (s *simscreen) GetCursor() (int, int, bool) { + s.Lock() + x, y, vis := s.cursorx, s.cursory, s.cursorvis + s.Unlock() + return x, y, vis +} + +func (s *simscreen) RegisterRuneFallback(r rune, subst string) { + s.Lock() + s.fallback[r] = subst + s.Unlock() +} + +func (s *simscreen) UnregisterRuneFallback(r rune) { + s.Lock() + delete(s.fallback, r) + s.Unlock() +} + +func (s *simscreen) CanDisplay(r rune, checkFallbacks bool) bool { + + if enc := s.encoder; enc != nil { + nb := make([]byte, 6) + ob := make([]byte, 6) + num := utf8.EncodeRune(ob, r) + + enc.Reset() + dst, _, err := enc.Transform(nb, ob[:num], true) + if dst != 0 && err == nil && nb[0] != '\x1A' { + return true + } + } + if !checkFallbacks { + return false + } + if _, ok := s.fallback[r]; ok { + return true + } + return false +} + +func (s *simscreen) HasMouse() bool { + return false +} + +func (s *simscreen) Resize(int, int, int, int) {} + +func (s *simscreen) HasKey(Key) bool { + return true +} + +func (s *simscreen) Beep() error { + return nil +} + +func (s *simscreen) Suspend() error { + return nil +} + +func (s *simscreen) Resume() error { + return nil +} diff --git a/vendor/github.com/gdamore/tcell/v2/stdin_unix.go b/vendor/github.com/gdamore/tcell/v2/stdin_unix.go new file mode 100644 index 0000000000..e3d32e9fd9 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/stdin_unix.go @@ -0,0 +1,177 @@ +// Copyright 2021 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos + +package tcell + +import ( + "errors" + "fmt" + "os" + "os/signal" + "strconv" + "sync" + "syscall" + "time" + + "golang.org/x/term" +) + +// stdIoTty is an implementation of the Tty API based upon stdin/stdout. +type stdIoTty struct { + fd int + in *os.File + out *os.File + saved *term.State + sig chan os.Signal + cb func() + stopQ chan struct{} + dev string + wg sync.WaitGroup + l sync.Mutex +} + +func (tty *stdIoTty) Read(b []byte) (int, error) { + return tty.in.Read(b) +} + +func (tty *stdIoTty) Write(b []byte) (int, error) { + return tty.out.Write(b) +} + +func (tty *stdIoTty) Close() error { + return nil +} + +func (tty *stdIoTty) Start() error { + tty.l.Lock() + defer tty.l.Unlock() + + // We open another copy of /dev/tty. This is a workaround for unusual behavior + // observed in macOS, apparently caused when a subshell (for example) closes our + // own tty device (when it exits for example). Getting a fresh new one seems to + // resolve the problem. (We believe this is a bug in the macOS tty driver that + // fails to account for dup() references to the same file before applying close() + // related behaviors to the tty.) We're also holding the original copy we opened + // since closing that might have deleterious effects as well. The upshot is that + // we will have up to two separate file handles open on /dev/tty. (Note that when + // using stdin/stdout instead of /dev/tty this problem is not observed.) + var err error + tty.in = os.Stdin + tty.out = os.Stdout + tty.fd = int(tty.in.Fd()) + + if !term.IsTerminal(tty.fd) { + return errors.New("device is not a terminal") + } + + _ = tty.in.SetReadDeadline(time.Time{}) + saved, err := term.MakeRaw(tty.fd) // also sets vMin and vTime + if err != nil { + return err + } + tty.saved = saved + + tty.stopQ = make(chan struct{}) + tty.wg.Add(1) + go func(stopQ chan struct{}) { + defer tty.wg.Done() + for { + select { + case <-tty.sig: + tty.l.Lock() + cb := tty.cb + tty.l.Unlock() + if cb != nil { + cb() + } + case <-stopQ: + return + } + } + }(tty.stopQ) + + signal.Notify(tty.sig, syscall.SIGWINCH) + return nil +} + +func (tty *stdIoTty) Drain() error { + _ = tty.in.SetReadDeadline(time.Now()) + if err := tcSetBufParams(tty.fd, 0, 0); err != nil { + return err + } + return nil +} + +func (tty *stdIoTty) Stop() error { + tty.l.Lock() + if err := term.Restore(tty.fd, tty.saved); err != nil { + tty.l.Unlock() + return err + } + _ = tty.in.SetReadDeadline(time.Now()) + + signal.Stop(tty.sig) + close(tty.stopQ) + tty.l.Unlock() + + tty.wg.Wait() + + return nil +} + +func (tty *stdIoTty) WindowSize() (int, int, error) { + w, h, err := term.GetSize(tty.fd) + if err != nil { + return 0, 0, err + } + if w == 0 { + w, _ = strconv.Atoi(os.Getenv("COLUMNS")) + } + if w == 0 { + w = 80 // default + } + if h == 0 { + h, _ = strconv.Atoi(os.Getenv("LINES")) + } + if h == 0 { + h = 25 // default + } + return w, h, nil +} + +func (tty *stdIoTty) NotifyResize(cb func()) { + tty.l.Lock() + tty.cb = cb + tty.l.Unlock() +} + +// NewStdioTty opens a tty using standard input/output. +func NewStdIoTty() (Tty, error) { + tty := &stdIoTty{ + sig: make(chan os.Signal), + in: os.Stdin, + out: os.Stdout, + } + var err error + tty.fd = int(tty.in.Fd()) + if !term.IsTerminal(tty.fd) { + return nil, errors.New("not a terminal") + } + if tty.saved, err = term.GetState(tty.fd); err != nil { + return nil, fmt.Errorf("failed to get state: %w", err) + } + return tty, nil +} diff --git a/vendor/github.com/gdamore/tcell/v2/style.go b/vendor/github.com/gdamore/tcell/v2/style.go new file mode 100644 index 0000000000..8359e28c67 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/style.go @@ -0,0 +1,137 @@ +// Copyright 2020 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +// Style represents a complete text style, including both foreground color, +// background color, and additional attributes such as "bold" or "underline". +// +// Note that not all terminals can display all colors or attributes, and +// many might have specific incompatibilities between specific attributes +// and color combinations. +// +// To use Style, just declare a variable of its type. +type Style struct { + fg Color + bg Color + attrs AttrMask +} + +// StyleDefault represents a default style, based upon the context. +// It is the zero value. +var StyleDefault Style + +// styleInvalid is just an arbitrary invalid style used internally. +var styleInvalid = Style{attrs: AttrInvalid} + +// Foreground returns a new style based on s, with the foreground color set +// as requested. ColorDefault can be used to select the global default. +func (s Style) Foreground(c Color) Style { + return Style{ + fg: c, + bg: s.bg, + attrs: s.attrs, + } +} + +// Background returns a new style based on s, with the background color set +// as requested. ColorDefault can be used to select the global default. +func (s Style) Background(c Color) Style { + return Style{ + fg: s.fg, + bg: c, + attrs: s.attrs, + } +} + +// Decompose breaks a style up, returning the foreground, background, +// and other attributes. +func (s Style) Decompose() (fg Color, bg Color, attr AttrMask) { + return s.fg, s.bg, s.attrs +} + +func (s Style) setAttrs(attrs AttrMask, on bool) Style { + if on { + return Style{ + fg: s.fg, + bg: s.bg, + attrs: s.attrs | attrs, + } + } + return Style{ + fg: s.fg, + bg: s.bg, + attrs: s.attrs &^ attrs, + } +} + +// Normal returns the style with all attributes disabled. +func (s Style) Normal() Style { + return Style{ + fg: s.fg, + bg: s.bg, + } +} + +// Bold returns a new style based on s, with the bold attribute set +// as requested. +func (s Style) Bold(on bool) Style { + return s.setAttrs(AttrBold, on) +} + +// Blink returns a new style based on s, with the blink attribute set +// as requested. +func (s Style) Blink(on bool) Style { + return s.setAttrs(AttrBlink, on) +} + +// Dim returns a new style based on s, with the dim attribute set +// as requested. +func (s Style) Dim(on bool) Style { + return s.setAttrs(AttrDim, on) +} + +// Italic returns a new style based on s, with the italic attribute set +// as requested. +func (s Style) Italic(on bool) Style { + return s.setAttrs(AttrItalic, on) +} + +// Reverse returns a new style based on s, with the reverse attribute set +// as requested. (Reverse usually changes the foreground and background +// colors.) +func (s Style) Reverse(on bool) Style { + return s.setAttrs(AttrReverse, on) +} + +// Underline returns a new style based on s, with the underline attribute set +// as requested. +func (s Style) Underline(on bool) Style { + return s.setAttrs(AttrUnderline, on) +} + +// StrikeThrough sets strikethrough mode. +func (s Style) StrikeThrough(on bool) Style { + return s.setAttrs(AttrStrikeThrough, on) +} + +// Attributes returns a new style based on s, with its attributes set as +// specified. +func (s Style) Attributes(attrs AttrMask) Style { + return Style{ + fg: s.fg, + bg: s.bg, + attrs: attrs, + } +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/.gitignore b/vendor/github.com/gdamore/tcell/v2/terminfo/.gitignore new file mode 100644 index 0000000000..74f3c04fd6 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/.gitignore @@ -0,0 +1 @@ +mkinfo diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/README.md b/vendor/github.com/gdamore/tcell/v2/terminfo/README.md new file mode 100644 index 0000000000..20ae937f38 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/README.md @@ -0,0 +1,25 @@ +This package represents the parent for all terminals. + +In older versions of tcell we had (a couple of) different +external file formats for the terminal database. Those are +now removed. All terminal definitions are supplied by +one of two methods: + +1. Compiled Go code + +2. For systems with terminfo and infocmp, dynamically + generated at runtime. + +The Go code can be generated using the mkinfo utility in +this directory. The database entry should be generated +into a package in a directory named as the first character +of the package name. (This permits us to group them all +without having a huge directory of little packages.) + +It may be desirable to add new packages to the extended +package, or -- rarely -- the base package. + +Applications which want to have the large set of terminal +descriptions built into the binary can simply import the +extended package. Otherwise a smaller reasonable default +set (the base package) will be included instead. diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/TERMINALS.md b/vendor/github.com/gdamore/tcell/v2/terminfo/TERMINALS.md new file mode 100644 index 0000000000..85c1e61c2e --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/TERMINALS.md @@ -0,0 +1,7 @@ +TERMINALS +========= + +The best way to populate terminals on Debian is to install ncurses, +ncurses-term, screen, tmux, rxvt-unicode, and dvtm. This populates the +the terminfo database so that we can have a reasonable set of starting +terminals. diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/a/aixterm/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/a/aixterm/term.go new file mode 100644 index 0000000000..503c9199ed --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/a/aixterm/term.go @@ -0,0 +1,83 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package aixterm + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // IBM Aixterm Terminal Emulator + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "aixterm", + Columns: 80, + Lines: 25, + Colors: 8, + Bell: "\a", + Clear: "\x1b[H\x1b[J", + AttrOff: "\x1b[0;10m\x1b(B", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Reverse: "\x1b[7m", + SetFg: "\x1b[3%p1%dm", + SetBg: "\x1b[4%p1%dm", + SetFgBg: "\x1b[3%p1%d;4%p2%dm", + ResetFgBg: "\x1b[32m\x1b[40m", + PadChar: "\x00", + AltChars: "jjkkllmmnnqqttuuvvwwxx", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1b[A", + KeyDown: "\x1b[B", + KeyRight: "\x1b[C", + KeyLeft: "\x1b[D", + KeyInsert: "\x1b[139q", + KeyDelete: "\x1b[P", + KeyBackspace: "\b", + KeyHome: "\x1b[H", + KeyEnd: "\x1b[146q", + KeyPgUp: "\x1b[150q", + KeyPgDn: "\x1b[154q", + KeyF1: "\x1b[001q", + KeyF2: "\x1b[002q", + KeyF3: "\x1b[003q", + KeyF4: "\x1b[004q", + KeyF5: "\x1b[005q", + KeyF6: "\x1b[006q", + KeyF7: "\x1b[007q", + KeyF8: "\x1b[008q", + KeyF9: "\x1b[009q", + KeyF10: "\x1b[010q", + KeyF11: "\x1b[011q", + KeyF12: "\x1b[012q", + KeyF13: "\x1b[013q", + KeyF14: "\x1b[014q", + KeyF15: "\x1b[015q", + KeyF16: "\x1b[016q", + KeyF17: "\x1b[017q", + KeyF18: "\x1b[018q", + KeyF19: "\x1b[019q", + KeyF20: "\x1b[020q", + KeyF21: "\x1b[021q", + KeyF22: "\x1b[022q", + KeyF23: "\x1b[023q", + KeyF24: "\x1b[024q", + KeyF25: "\x1b[025q", + KeyF26: "\x1b[026q", + KeyF27: "\x1b[027q", + KeyF28: "\x1b[028q", + KeyF29: "\x1b[029q", + KeyF30: "\x1b[030q", + KeyF31: "\x1b[031q", + KeyF32: "\x1b[032q", + KeyF33: "\x1b[033q", + KeyF34: "\x1b[034q", + KeyF35: "\x1b[035q", + KeyF36: "\x1b[036q", + KeyClear: "\x1b[144q", + KeyBacktab: "\x1b[Z", + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/a/alacritty/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/a/alacritty/term.go new file mode 100644 index 0000000000..5b97998469 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/a/alacritty/term.go @@ -0,0 +1,69 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package alacritty + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // alacritty terminal emulator + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "alacritty", + Columns: 80, + Lines: 24, + Colors: 256, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b[?1049h\x1b[22;0;0t", + ExitCA: "\x1b[?1049l\x1b[23;0;0t", + ShowCursor: "\x1b[?12l\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b(B\x1b[m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", + ResetFgBg: "\x1b[39;49m", + AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + StrikeThrough: "\x1b[9m", + Mouse: "\x1b[<", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1bOH", + KeyEnd: "\x1bOF", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/a/ansi/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/a/ansi/term.go new file mode 100644 index 0000000000..5c572fd496 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/a/ansi/term.go @@ -0,0 +1,43 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package ansi + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // ansi/pc-term compatible with color + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "ansi", + Columns: 80, + Lines: 24, + Colors: 8, + Bell: "\a", + Clear: "\x1b[H\x1b[J", + AttrOff: "\x1b[0;10m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + SetFg: "\x1b[3%p1%dm", + SetBg: "\x1b[4%p1%dm", + SetFgBg: "\x1b[3%p1%d;4%p2%dm", + ResetFgBg: "\x1b[39;49m", + PadChar: "\x00", + AltChars: "+\x10,\x11-\x18.\x190\xdb`\x04a\xb1f\xf8g\xf1h\xb0j\xd9k\xbfl\xdam\xc0n\xc5o~p\xc4q\xc4r\xc4s_t\xc3u\xb4v\xc1w\xc2x\xb3y\xf3z\xf2{\xe3|\xd8}\x9c~\xfe", + EnterAcs: "\x1b[11m", + ExitAcs: "\x1b[10m", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\x1b[D", + CursorUp1: "\x1b[A", + KeyUp: "\x1b[A", + KeyDown: "\x1b[B", + KeyRight: "\x1b[C", + KeyLeft: "\x1b[D", + KeyInsert: "\x1b[L", + KeyBackspace: "\b", + KeyHome: "\x1b[H", + KeyBacktab: "\x1b[Z", + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/b/beterm/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/b/beterm/term.go new file mode 100644 index 0000000000..e6d88838c3 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/b/beterm/term.go @@ -0,0 +1,57 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package beterm + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // BeOS Terminal + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "beterm", + Columns: 80, + Lines: 25, + Colors: 8, + Bell: "\a", + Clear: "\x1b[H\x1b[J", + AttrOff: "\x1b[0;10m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?4h", + ExitKeypad: "\x1b[?4l", + SetFg: "\x1b[3%p1%dm", + SetBg: "\x1b[4%p1%dm", + SetFgBg: "\x1b[3%p1%d;4%p2%dm", + ResetFgBg: "\x1b[m", + PadChar: "\x00", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1b[A", + KeyDown: "\x1b[B", + KeyRight: "\x1b[C", + KeyLeft: "\x1b[D", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\b", + KeyHome: "\x1b[1~", + KeyEnd: "\x1b[4~", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1b[11~", + KeyF2: "\x1b[12~", + KeyF3: "\x1b[13~", + KeyF4: "\x1b[14~", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[16~", + KeyF7: "\x1b[17~", + KeyF8: "\x1b[18~", + KeyF9: "\x1b[19~", + KeyF10: "\x1b[20~", + KeyF11: "\x1b[21~", + KeyF12: "\x1b[22~", + AutoMargin: true, + InsertChar: "\x1b[@", + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/base/base.go b/vendor/github.com/gdamore/tcell/v2/terminfo/base/base.go new file mode 100644 index 0000000000..fbecdfa93e --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/base/base.go @@ -0,0 +1,32 @@ +// Copyright 2020 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +// This is just a "minimalist" set of the base terminal descriptions. +// It should be sufficient for most applications. + +// Package base contains the base terminal descriptions that are likely +// to be needed by any stock application. It is imported by default in the +// terminfo package, so terminal types listed here will be available to any +// tcell application. +package base + +import ( + // The following imports just register themselves -- + // thse are the terminal types we aggregate in this package. + _ "github.com/gdamore/tcell/v2/terminfo/a/ansi" + _ "github.com/gdamore/tcell/v2/terminfo/v/vt100" + _ "github.com/gdamore/tcell/v2/terminfo/v/vt102" + _ "github.com/gdamore/tcell/v2/terminfo/v/vt220" + _ "github.com/gdamore/tcell/v2/terminfo/x/xterm" +) diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/c/cygwin/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/c/cygwin/term.go new file mode 100644 index 0000000000..46a0a4a3a2 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/c/cygwin/term.go @@ -0,0 +1,66 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package cygwin + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // ANSI emulation for Cygwin + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "cygwin", + Colors: 8, + Bell: "\a", + Clear: "\x1b[H\x1b[J", + EnterCA: "\x1b7\x1b[?47h", + ExitCA: "\x1b[2J\x1b[?47l\x1b8", + AttrOff: "\x1b[0;10m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Reverse: "\x1b[7m", + SetFg: "\x1b[3%p1%dm", + SetBg: "\x1b[4%p1%dm", + SetFgBg: "\x1b[3%p1%d;4%p2%dm", + ResetFgBg: "\x1b[39;49m", + PadChar: "\x00", + AltChars: "+\x10,\x11-\x18.\x190\xdb`\x04a\xb1f\xf8g\xf1h\xb0j\xd9k\xbfl\xdam\xc0n\xc5o~p\xc4q\xc4r\xc4s_t\xc3u\xb4v\xc1w\xc2x\xb3y\xf3z\xf2{\xe3|\xd8}\x9c~\xfe", + EnterAcs: "\x1b[11m", + ExitAcs: "\x1b[10m", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1b[A", + KeyDown: "\x1b[B", + KeyRight: "\x1b[C", + KeyLeft: "\x1b[D", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\b", + KeyHome: "\x1b[1~", + KeyEnd: "\x1b[4~", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1b[[A", + KeyF2: "\x1b[[B", + KeyF3: "\x1b[[C", + KeyF4: "\x1b[[D", + KeyF5: "\x1b[[E", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyF13: "\x1b[25~", + KeyF14: "\x1b[26~", + KeyF15: "\x1b[28~", + KeyF16: "\x1b[29~", + KeyF17: "\x1b[31~", + KeyF18: "\x1b[32~", + KeyF19: "\x1b[33~", + KeyF20: "\x1b[34~", + AutoMargin: true, + InsertChar: "\x1b[@", + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/d/dtterm/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/d/dtterm/term.go new file mode 100644 index 0000000000..f471c80d23 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/d/dtterm/term.go @@ -0,0 +1,69 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package dtterm + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // CDE desktop terminal + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "dtterm", + Columns: 80, + Lines: 24, + Colors: 8, + Bell: "\a", + Clear: "\x1b[H\x1b[J", + ShowCursor: "\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[m\x0f", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + SetFg: "\x1b[3%p1%dm", + SetBg: "\x1b[4%p1%dm", + SetFgBg: "\x1b[3%p1%d;4%p2%dm", + ResetFgBg: "\x1b[39;49m", + PadChar: "\x00", + AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b(B\x1b)0", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1b[A", + KeyDown: "\x1b[B", + KeyRight: "\x1b[C", + KeyLeft: "\x1b[D", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\b", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1b[11~", + KeyF2: "\x1b[12~", + KeyF3: "\x1b[13~", + KeyF4: "\x1b[14~", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyF13: "\x1b[25~", + KeyF14: "\x1b[26~", + KeyF15: "\x1b[28~", + KeyF16: "\x1b[29~", + KeyF17: "\x1b[31~", + KeyF18: "\x1b[32~", + KeyF19: "\x1b[33~", + KeyF20: "\x1b[34~", + KeyHelp: "\x1b[28~", + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/dynamic/dynamic.go b/vendor/github.com/gdamore/tcell/v2/terminfo/dynamic/dynamic.go new file mode 100644 index 0000000000..08ff248428 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/dynamic/dynamic.go @@ -0,0 +1,427 @@ +// Copyright 2021 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +// The dynamic package is used to generate a terminal description dynamically, +// using infocmp. This is really a method of last resort, as the performance +// will be slow, and it requires a working infocmp. But, the hope is that it +// will assist folks who have to deal with a terminal description that isn't +// already built in. This requires infocmp to be in the user's path, and to +// support reasonably the -1 option. + +package dynamic + +import ( + "bytes" + "errors" + "os/exec" + "regexp" + "strconv" + "strings" + + "github.com/gdamore/tcell/v2/terminfo" +) + +type termcap struct { + name string + desc string + aliases []string + bools map[string]bool + nums map[string]int + strs map[string]string +} + +func (tc *termcap) getnum(s string) int { + return (tc.nums[s]) +} + +func (tc *termcap) getflag(s string) bool { + return (tc.bools[s]) +} + +func (tc *termcap) getstr(s string) string { + return (tc.strs[s]) +} + +const ( + none = iota + control + escaped +) + +var errNotAddressable = errors.New("terminal not cursor addressable") + +func unescape(s string) string { + // Various escapes are in \x format. Control codes are + // encoded as ^M (carat followed by ASCII equivalent). + // escapes are: \e, \E - escape + // \0 NULL, \n \l \r \t \b \f \s for equivalent C escape. + buf := &bytes.Buffer{} + esc := none + + for i := 0; i < len(s); i++ { + c := s[i] + switch esc { + case none: + switch c { + case '\\': + esc = escaped + case '^': + esc = control + default: + buf.WriteByte(c) + } + case control: + buf.WriteByte(c ^ 1<<6) + esc = none + case escaped: + switch c { + case 'E', 'e': + buf.WriteByte(0x1b) + case '0', '1', '2', '3', '4', '5', '6', '7': + if i+2 < len(s) && s[i+1] >= '0' && s[i+1] <= '7' && s[i+2] >= '0' && s[i+2] <= '7' { + buf.WriteByte(((c - '0') * 64) + ((s[i+1] - '0') * 8) + (s[i+2] - '0')) + i = i + 2 + } else if c == '0' { + buf.WriteByte(0) + } + case 'n': + buf.WriteByte('\n') + case 'r': + buf.WriteByte('\r') + case 't': + buf.WriteByte('\t') + case 'b': + buf.WriteByte('\b') + case 'f': + buf.WriteByte('\f') + case 's': + buf.WriteByte(' ') + default: + buf.WriteByte(c) + } + esc = none + } + } + return (buf.String()) +} + +func (tc *termcap) setupterm(name string) error { + cmd := exec.Command("infocmp", "-1", name) + output := &bytes.Buffer{} + cmd.Stdout = output + + tc.strs = make(map[string]string) + tc.bools = make(map[string]bool) + tc.nums = make(map[string]int) + + if err := cmd.Run(); err != nil { + return err + } + + // Now parse the output. + // We get comment lines (starting with "#"), followed by + // a header line that looks like "||...|" + // then capabilities, one per line, starting with a tab and ending + // with a comma and newline. + lines := strings.Split(output.String(), "\n") + for len(lines) > 0 && strings.HasPrefix(lines[0], "#") { + lines = lines[1:] + } + + // Ditch trailing empty last line + if lines[len(lines)-1] == "" { + lines = lines[:len(lines)-1] + } + header := lines[0] + if strings.HasSuffix(header, ",") { + header = header[:len(header)-1] + } + names := strings.Split(header, "|") + tc.name = names[0] + names = names[1:] + if len(names) > 0 { + tc.desc = names[len(names)-1] + names = names[:len(names)-1] + } + tc.aliases = names + for _, val := range lines[1:] { + if (!strings.HasPrefix(val, "\t")) || + (!strings.HasSuffix(val, ",")) { + return (errors.New("malformed infocmp: " + val)) + } + + val = val[1:] + val = val[:len(val)-1] + + if k := strings.SplitN(val, "=", 2); len(k) == 2 { + tc.strs[k[0]] = unescape(k[1]) + } else if k := strings.SplitN(val, "#", 2); len(k) == 2 { + u, err := strconv.ParseUint(k[1], 0, 0) + if err != nil { + return (err) + } + tc.nums[k[0]] = int(u) + } else { + tc.bools[val] = true + } + } + return nil +} + +// LoadTerminfo creates a Terminfo by for named terminal by attempting to parse +// the output from infocmp. This returns the terminfo entry, a description of +// the terminal, and either nil or an error. +func LoadTerminfo(name string) (*terminfo.Terminfo, string, error) { + var tc termcap + if err := tc.setupterm(name); err != nil { + if err != nil { + return nil, "", err + } + } + t := &terminfo.Terminfo{} + // If this is an alias record, then just emit the alias + t.Name = tc.name + if t.Name != name { + return t, "", nil + } + t.Aliases = tc.aliases + t.Colors = tc.getnum("colors") + t.Columns = tc.getnum("cols") + t.Lines = tc.getnum("lines") + t.Bell = tc.getstr("bel") + t.Clear = tc.getstr("clear") + t.EnterCA = tc.getstr("smcup") + t.ExitCA = tc.getstr("rmcup") + t.ShowCursor = tc.getstr("cnorm") + t.HideCursor = tc.getstr("civis") + t.AttrOff = tc.getstr("sgr0") + t.Underline = tc.getstr("smul") + t.Bold = tc.getstr("bold") + t.Blink = tc.getstr("blink") + t.Dim = tc.getstr("dim") + t.Italic = tc.getstr("sitm") + t.Reverse = tc.getstr("rev") + t.EnterKeypad = tc.getstr("smkx") + t.ExitKeypad = tc.getstr("rmkx") + t.SetFg = tc.getstr("setaf") + t.SetBg = tc.getstr("setab") + t.SetCursor = tc.getstr("cup") + t.CursorBack1 = tc.getstr("cub1") + t.CursorUp1 = tc.getstr("cuu1") + t.KeyF1 = tc.getstr("kf1") + t.KeyF2 = tc.getstr("kf2") + t.KeyF3 = tc.getstr("kf3") + t.KeyF4 = tc.getstr("kf4") + t.KeyF5 = tc.getstr("kf5") + t.KeyF6 = tc.getstr("kf6") + t.KeyF7 = tc.getstr("kf7") + t.KeyF8 = tc.getstr("kf8") + t.KeyF9 = tc.getstr("kf9") + t.KeyF10 = tc.getstr("kf10") + t.KeyF11 = tc.getstr("kf11") + t.KeyF12 = tc.getstr("kf12") + t.KeyF13 = tc.getstr("kf13") + t.KeyF14 = tc.getstr("kf14") + t.KeyF15 = tc.getstr("kf15") + t.KeyF16 = tc.getstr("kf16") + t.KeyF17 = tc.getstr("kf17") + t.KeyF18 = tc.getstr("kf18") + t.KeyF19 = tc.getstr("kf19") + t.KeyF20 = tc.getstr("kf20") + t.KeyF21 = tc.getstr("kf21") + t.KeyF22 = tc.getstr("kf22") + t.KeyF23 = tc.getstr("kf23") + t.KeyF24 = tc.getstr("kf24") + t.KeyF25 = tc.getstr("kf25") + t.KeyF26 = tc.getstr("kf26") + t.KeyF27 = tc.getstr("kf27") + t.KeyF28 = tc.getstr("kf28") + t.KeyF29 = tc.getstr("kf29") + t.KeyF30 = tc.getstr("kf30") + t.KeyF31 = tc.getstr("kf31") + t.KeyF32 = tc.getstr("kf32") + t.KeyF33 = tc.getstr("kf33") + t.KeyF34 = tc.getstr("kf34") + t.KeyF35 = tc.getstr("kf35") + t.KeyF36 = tc.getstr("kf36") + t.KeyF37 = tc.getstr("kf37") + t.KeyF38 = tc.getstr("kf38") + t.KeyF39 = tc.getstr("kf39") + t.KeyF40 = tc.getstr("kf40") + t.KeyF41 = tc.getstr("kf41") + t.KeyF42 = tc.getstr("kf42") + t.KeyF43 = tc.getstr("kf43") + t.KeyF44 = tc.getstr("kf44") + t.KeyF45 = tc.getstr("kf45") + t.KeyF46 = tc.getstr("kf46") + t.KeyF47 = tc.getstr("kf47") + t.KeyF48 = tc.getstr("kf48") + t.KeyF49 = tc.getstr("kf49") + t.KeyF50 = tc.getstr("kf50") + t.KeyF51 = tc.getstr("kf51") + t.KeyF52 = tc.getstr("kf52") + t.KeyF53 = tc.getstr("kf53") + t.KeyF54 = tc.getstr("kf54") + t.KeyF55 = tc.getstr("kf55") + t.KeyF56 = tc.getstr("kf56") + t.KeyF57 = tc.getstr("kf57") + t.KeyF58 = tc.getstr("kf58") + t.KeyF59 = tc.getstr("kf59") + t.KeyF60 = tc.getstr("kf60") + t.KeyF61 = tc.getstr("kf61") + t.KeyF62 = tc.getstr("kf62") + t.KeyF63 = tc.getstr("kf63") + t.KeyF64 = tc.getstr("kf64") + t.KeyInsert = tc.getstr("kich1") + t.KeyDelete = tc.getstr("kdch1") + t.KeyBackspace = tc.getstr("kbs") + t.KeyHome = tc.getstr("khome") + t.KeyEnd = tc.getstr("kend") + t.KeyUp = tc.getstr("kcuu1") + t.KeyDown = tc.getstr("kcud1") + t.KeyRight = tc.getstr("kcuf1") + t.KeyLeft = tc.getstr("kcub1") + t.KeyPgDn = tc.getstr("knp") + t.KeyPgUp = tc.getstr("kpp") + t.KeyBacktab = tc.getstr("kcbt") + t.KeyExit = tc.getstr("kext") + t.KeyCancel = tc.getstr("kcan") + t.KeyPrint = tc.getstr("kprt") + t.KeyHelp = tc.getstr("khlp") + t.KeyClear = tc.getstr("kclr") + t.AltChars = tc.getstr("acsc") + t.EnterAcs = tc.getstr("smacs") + t.ExitAcs = tc.getstr("rmacs") + t.EnableAcs = tc.getstr("enacs") + t.Mouse = tc.getstr("kmous") + t.KeyShfRight = tc.getstr("kRIT") + t.KeyShfLeft = tc.getstr("kLFT") + t.KeyShfHome = tc.getstr("kHOM") + t.KeyShfEnd = tc.getstr("kEND") + + // Terminfo lacks descriptions for a bunch of modified keys, + // but modern XTerm and emulators often have them. Let's add them, + // if the shifted right and left arrows are defined. + if t.KeyShfRight == "\x1b[1;2C" && t.KeyShfLeft == "\x1b[1;2D" { + t.KeyShfUp = "\x1b[1;2A" + t.KeyShfDown = "\x1b[1;2B" + t.KeyMetaUp = "\x1b[1;9A" + t.KeyMetaDown = "\x1b[1;9B" + t.KeyMetaRight = "\x1b[1;9C" + t.KeyMetaLeft = "\x1b[1;9D" + t.KeyAltUp = "\x1b[1;3A" + t.KeyAltDown = "\x1b[1;3B" + t.KeyAltRight = "\x1b[1;3C" + t.KeyAltLeft = "\x1b[1;3D" + t.KeyCtrlUp = "\x1b[1;5A" + t.KeyCtrlDown = "\x1b[1;5B" + t.KeyCtrlRight = "\x1b[1;5C" + t.KeyCtrlLeft = "\x1b[1;5D" + t.KeyAltShfUp = "\x1b[1;4A" + t.KeyAltShfDown = "\x1b[1;4B" + t.KeyAltShfRight = "\x1b[1;4C" + t.KeyAltShfLeft = "\x1b[1;4D" + + t.KeyMetaShfUp = "\x1b[1;10A" + t.KeyMetaShfDown = "\x1b[1;10B" + t.KeyMetaShfRight = "\x1b[1;10C" + t.KeyMetaShfLeft = "\x1b[1;10D" + + t.KeyCtrlShfUp = "\x1b[1;6A" + t.KeyCtrlShfDown = "\x1b[1;6B" + t.KeyCtrlShfRight = "\x1b[1;6C" + t.KeyCtrlShfLeft = "\x1b[1;6D" + + t.KeyShfPgUp = "\x1b[5;2~" + t.KeyShfPgDn = "\x1b[6;2~" + } + // And also for Home and End + if t.KeyShfHome == "\x1b[1;2H" && t.KeyShfEnd == "\x1b[1;2F" { + t.KeyCtrlHome = "\x1b[1;5H" + t.KeyCtrlEnd = "\x1b[1;5F" + t.KeyAltHome = "\x1b[1;9H" + t.KeyAltEnd = "\x1b[1;9F" + t.KeyCtrlShfHome = "\x1b[1;6H" + t.KeyCtrlShfEnd = "\x1b[1;6F" + t.KeyAltShfHome = "\x1b[1;4H" + t.KeyAltShfEnd = "\x1b[1;4F" + t.KeyMetaShfHome = "\x1b[1;10H" + t.KeyMetaShfEnd = "\x1b[1;10F" + } + + // And the same thing for rxvt and workalikes (Eterm, aterm, etc.) + // It seems that urxvt at least send escaped as ALT prefix for these, + // although some places seem to indicate a separate ALT key sesquence. + if t.KeyShfRight == "\x1b[c" && t.KeyShfLeft == "\x1b[d" { + t.KeyShfUp = "\x1b[a" + t.KeyShfDown = "\x1b[b" + t.KeyCtrlUp = "\x1b[Oa" + t.KeyCtrlDown = "\x1b[Ob" + t.KeyCtrlRight = "\x1b[Oc" + t.KeyCtrlLeft = "\x1b[Od" + } + if t.KeyShfHome == "\x1b[7$" && t.KeyShfEnd == "\x1b[8$" { + t.KeyCtrlHome = "\x1b[7^" + t.KeyCtrlEnd = "\x1b[8^" + } + + // Technically the RGB flag that is provided for xterm-direct is not + // quite right. The problem is that the -direct flag that was introduced + // with ncurses 6.1 requires a parsing for the parameters that we lack. + // For this case we'll just assume it's XTerm compatible. Someday this + // may be incorrect, but right now it is correct, and nobody uses it + // anyway. + if tc.getflag("Tc") { + // This presumes XTerm 24-bit true color. + t.TrueColor = true + } else if tc.getflag("RGB") { + // This is for xterm-direct, which uses a different scheme entirely. + // (ncurses went a very different direction from everyone else, and + // so it's unlikely anything is using this definition.) + t.TrueColor = true + t.SetBg = "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m" + t.SetFg = "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m" + } + + // We only support colors in ANSI 8 or 256 color mode. + if t.Colors < 8 || t.SetFg == "" { + t.Colors = 0 + } + if t.SetCursor == "" { + return nil, "", errNotAddressable + } + + // For padding, we lookup the pad char. If that isn't present, + // and npc is *not* set, then we assume a null byte. + t.PadChar = tc.getstr("pad") + if t.PadChar == "" { + if !tc.getflag("npc") { + t.PadChar = "\u0000" + } + } + + // For terminals that use "standard" SGR sequences, lets combine the + // foreground and background together. + if strings.HasPrefix(t.SetFg, "\x1b[") && + strings.HasPrefix(t.SetBg, "\x1b[") && + strings.HasSuffix(t.SetFg, "m") && + strings.HasSuffix(t.SetBg, "m") { + fg := t.SetFg[:len(t.SetFg)-1] + r := regexp.MustCompile("%p1") + bg := r.ReplaceAllString(t.SetBg[2:], "%p2") + t.SetFgBg = fg + ";" + bg + } + + return t, tc.desc, nil +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/e/emacs/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/e/emacs/term.go new file mode 100644 index 0000000000..b0b9b4771e --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/e/emacs/term.go @@ -0,0 +1,63 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package emacs + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // gnu emacs term.el terminal emulation + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "eterm", + Columns: 80, + Lines: 24, + Bell: "\a", + Clear: "\x1b[H\x1b[J", + EnterCA: "\x1b7\x1b[?47h", + ExitCA: "\x1b[2J\x1b[?47l\x1b8", + AttrOff: "\x1b[m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Reverse: "\x1b[7m", + PadChar: "\x00", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + AutoMargin: true, + }) + + // Emacs term.el terminal emulator term-protocol-version 0.96 + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "eterm-color", + Columns: 80, + Lines: 24, + Colors: 8, + Bell: "\a", + Clear: "\x1b[H\x1b[J", + AttrOff: "\x1b[m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + SetFg: "\x1b[%p1%{30}%+%dm", + SetBg: "\x1b[%p1%'('%+%dm", + SetFgBg: "\x1b[%p1%{30}%+%d;%p2%'('%+%dm", + ResetFgBg: "\x1b[39;49m", + PadChar: "\x00", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1b[1~", + KeyEnd: "\x1b[4~", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/extended/extended.go b/vendor/github.com/gdamore/tcell/v2/terminfo/extended/extended.go new file mode 100644 index 0000000000..c69bef13d5 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/extended/extended.go @@ -0,0 +1,58 @@ +// Copyright 2020 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +// Package extended contains an extended set of terminal descriptions. +// Applications desiring to have a better chance of Just Working by +// default should include this package. This will significantly increase +// the size of the program. +package extended + +import ( + // The following imports just register themselves -- + // these are the terminal types we aggregate in this package. + _ "github.com/gdamore/tcell/v2/terminfo/a/aixterm" + _ "github.com/gdamore/tcell/v2/terminfo/a/alacritty" + _ "github.com/gdamore/tcell/v2/terminfo/a/ansi" + _ "github.com/gdamore/tcell/v2/terminfo/b/beterm" + _ "github.com/gdamore/tcell/v2/terminfo/c/cygwin" + _ "github.com/gdamore/tcell/v2/terminfo/d/dtterm" + _ "github.com/gdamore/tcell/v2/terminfo/e/emacs" + _ "github.com/gdamore/tcell/v2/terminfo/f/foot" + _ "github.com/gdamore/tcell/v2/terminfo/g/gnome" + _ "github.com/gdamore/tcell/v2/terminfo/h/hpterm" + _ "github.com/gdamore/tcell/v2/terminfo/k/konsole" + _ "github.com/gdamore/tcell/v2/terminfo/k/kterm" + _ "github.com/gdamore/tcell/v2/terminfo/l/linux" + _ "github.com/gdamore/tcell/v2/terminfo/p/pcansi" + _ "github.com/gdamore/tcell/v2/terminfo/r/rxvt" + _ "github.com/gdamore/tcell/v2/terminfo/s/screen" + _ "github.com/gdamore/tcell/v2/terminfo/s/simpleterm" + _ "github.com/gdamore/tcell/v2/terminfo/s/sun" + _ "github.com/gdamore/tcell/v2/terminfo/t/termite" + _ "github.com/gdamore/tcell/v2/terminfo/t/tmux" + _ "github.com/gdamore/tcell/v2/terminfo/v/vt100" + _ "github.com/gdamore/tcell/v2/terminfo/v/vt102" + _ "github.com/gdamore/tcell/v2/terminfo/v/vt220" + _ "github.com/gdamore/tcell/v2/terminfo/v/vt320" + _ "github.com/gdamore/tcell/v2/terminfo/v/vt400" + _ "github.com/gdamore/tcell/v2/terminfo/v/vt420" + _ "github.com/gdamore/tcell/v2/terminfo/v/vt52" + _ "github.com/gdamore/tcell/v2/terminfo/w/wy50" + _ "github.com/gdamore/tcell/v2/terminfo/w/wy60" + _ "github.com/gdamore/tcell/v2/terminfo/w/wy99_ansi" + _ "github.com/gdamore/tcell/v2/terminfo/x/xfce" + _ "github.com/gdamore/tcell/v2/terminfo/x/xterm" + _ "github.com/gdamore/tcell/v2/terminfo/x/xterm_kitty" + _ "github.com/gdamore/tcell/v2/terminfo/x/xterm_termite" +) diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/f/foot/foot.go b/vendor/github.com/gdamore/tcell/v2/terminfo/f/foot/foot.go new file mode 100644 index 0000000000..fb734cbdc2 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/f/foot/foot.go @@ -0,0 +1,69 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package foot + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // foot terminal emulator + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "foot", + Columns: 80, + Lines: 24, + Colors: 256, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b[?1049h\x1b[22;0;0t", + ExitCA: "\x1b[?1049l\x1b[23;0;0t", + ShowCursor: "\x1b[?12l\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b(B\x1b[m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38:5:%p1%d%;m", + SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48:5:%p1%d%;m", + SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38:5:%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48:5:%p2%d%;m", + ResetFgBg: "\x1b[39;49m", + AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + StrikeThrough: "\x1b[9m", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1bOH", + KeyEnd: "\x1bOF", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/g/gnome/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/g/gnome/term.go new file mode 100644 index 0000000000..e85a3a343d --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/g/gnome/term.go @@ -0,0 +1,130 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package gnome + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // GNOME Terminal + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "gnome", + Columns: 80, + Lines: 24, + Colors: 8, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b7\x1b[?47h", + ExitCA: "\x1b[2J\x1b[?47l\x1b8", + ShowCursor: "\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[0m\x0f", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[3%p1%dm", + SetBg: "\x1b[4%p1%dm", + SetFgBg: "\x1b[3%p1%d;4%p2%dm", + ResetFgBg: "\x1b[39;49m", + PadChar: "\x00", + AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b)0", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1bOH", + KeyEnd: "\x1bOF", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + AutoMargin: true, + }) + + // GNOME Terminal with xterm 256-colors + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "gnome-256color", + Columns: 80, + Lines: 24, + Colors: 256, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b7\x1b[?47h", + ExitCA: "\x1b[2J\x1b[?47l\x1b8", + ShowCursor: "\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[0m\x0f", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", + ResetFgBg: "\x1b[39;49m", + PadChar: "\x00", + AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b)0", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1bOH", + KeyEnd: "\x1bOF", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/gen.sh b/vendor/github.com/gdamore/tcell/v2/terminfo/gen.sh new file mode 100644 index 0000000000..2fc0611234 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/gen.sh @@ -0,0 +1,18 @@ +while read line +do + case "$line" in + *'|'*) + alias=${line#*|} + line=${line%|*} + ;; + *) + alias=${line%%,*} + ;; + esac + + alias=${alias//-/_} + direc=${alias:0:1} + + mkdir -p ${direc}/${alias} + go run mkinfo.go -P ${alias} -go ${direc}/${alias}/term.go ${line//,/ } +done < models.txt diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/h/hpterm/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/h/hpterm/term.go new file mode 100644 index 0000000000..123bfb9390 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/h/hpterm/term.go @@ -0,0 +1,51 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package hpterm + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // hp X11 terminal emulator + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "hpterm", + Aliases: []string{"X-hpterm"}, + Columns: 80, + Lines: 24, + Bell: "\a", + Clear: "\x1b&a0y0C\x1bJ", + AttrOff: "\x1b&d@\x0f", + Underline: "\x1b&dD", + Bold: "\x1b&dB", + Dim: "\x1b&dH", + Reverse: "\x1b&dB", + EnterKeypad: "\x1b&s1A", + ExitKeypad: "\x1b&s0A", + PadChar: "\x00", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + SetCursor: "\x1b&a%p1%dy%p2%dC", + CursorBack1: "\b", + CursorUp1: "\x1bA", + KeyUp: "\x1bA", + KeyDown: "\x1bB", + KeyRight: "\x1bC", + KeyLeft: "\x1bD", + KeyInsert: "\x1bQ", + KeyDelete: "\x1bP", + KeyBackspace: "\b", + KeyHome: "\x1bh", + KeyPgUp: "\x1bV", + KeyPgDn: "\x1bU", + KeyF1: "\x1bp", + KeyF2: "\x1bq", + KeyF3: "\x1br", + KeyF4: "\x1bs", + KeyF5: "\x1bt", + KeyF6: "\x1bu", + KeyF7: "\x1bv", + KeyF8: "\x1bw", + KeyClear: "\x1bJ", + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/k/konsole/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/k/konsole/term.go new file mode 100644 index 0000000000..236db9db2d --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/k/konsole/term.go @@ -0,0 +1,130 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package konsole + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // KDE console window + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "konsole", + Columns: 80, + Lines: 24, + Colors: 8, + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b7\x1b[?47h", + ExitCA: "\x1b[2J\x1b[?47l\x1b8", + ShowCursor: "\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[0m\x0f", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[3%p1%dm", + SetBg: "\x1b[4%p1%dm", + SetFgBg: "\x1b[3%p1%d;4%p2%dm", + ResetFgBg: "\x1b[39;49m", + AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b)0", + StrikeThrough: "\x1b[9m", + Mouse: "\x1b[<", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1bOH", + KeyEnd: "\x1bOF", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + AutoMargin: true, + }) + + // KDE console window with xterm 256-colors + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "konsole-256color", + Columns: 80, + Lines: 24, + Colors: 256, + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b7\x1b[?47h", + ExitCA: "\x1b[2J\x1b[?47l\x1b8", + ShowCursor: "\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[0m\x0f", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", + ResetFgBg: "\x1b[39;49m", + AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b)0", + StrikeThrough: "\x1b[9m", + Mouse: "\x1b[<", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1bOH", + KeyEnd: "\x1bOF", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/k/kterm/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/k/kterm/term.go new file mode 100644 index 0000000000..eedbe6de0a --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/k/kterm/term.go @@ -0,0 +1,68 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package kterm + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // kterm kanji terminal emulator (X window system) + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "kterm", + Columns: 80, + Lines: 24, + Colors: 8, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b7\x1b[?47h", + ExitCA: "\x1b[2J\x1b[?47l\x1b8", + AttrOff: "\x1b[m\x1b(B", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[3%p1%dm", + SetBg: "\x1b[4%p1%dm", + SetFgBg: "\x1b[3%p1%d;4%p2%dm", + ResetFgBg: "\x1b[39;49m", + PadChar: "\x00", + AltChars: "``aajjkkllmmnnooppqqrrssttuuvvwwxx~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1b[11~", + KeyF2: "\x1b[12~", + KeyF3: "\x1b[13~", + KeyF4: "\x1b[14~", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyF13: "\x1b[25~", + KeyF14: "\x1b[26~", + KeyF15: "\x1b[28~", + KeyF16: "\x1b[29~", + KeyF17: "\x1b[31~", + KeyF18: "\x1b[32~", + KeyF19: "\x1b[33~", + KeyF20: "\x1b[34~", + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/l/linux/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/l/linux/term.go new file mode 100644 index 0000000000..8783b4c7ff --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/l/linux/term.go @@ -0,0 +1,71 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package linux + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // linux console + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "linux", + Colors: 8, + Bell: "\a", + Clear: "\x1b[H\x1b[J", + ShowCursor: "\x1b[?25h\x1b[?0c", + HideCursor: "\x1b[?25l\x1b[?1c", + AttrOff: "\x1b[m\x0f", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + SetFg: "\x1b[3%p1%dm", + SetBg: "\x1b[4%p1%dm", + SetFgBg: "\x1b[3%p1%d;4%p2%dm", + ResetFgBg: "\x1b[39;49m", + PadChar: "\x00", + AltChars: "++,,--..00__``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}c~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b)0", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1b[A", + KeyDown: "\x1b[B", + KeyRight: "\x1b[C", + KeyLeft: "\x1b[D", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1b[1~", + KeyEnd: "\x1b[4~", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1b[[A", + KeyF2: "\x1b[[B", + KeyF3: "\x1b[[C", + KeyF4: "\x1b[[D", + KeyF5: "\x1b[[E", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyF13: "\x1b[25~", + KeyF14: "\x1b[26~", + KeyF15: "\x1b[28~", + KeyF16: "\x1b[29~", + KeyF17: "\x1b[31~", + KeyF18: "\x1b[32~", + KeyF19: "\x1b[33~", + KeyF20: "\x1b[34~", + KeyBacktab: "\x1b[Z", + AutoMargin: true, + InsertChar: "\x1b[@", + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/models.txt b/vendor/github.com/gdamore/tcell/v2/terminfo/models.txt new file mode 100644 index 0000000000..f0db81299e --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/models.txt @@ -0,0 +1,31 @@ +aixterm +alacritty +ansi +beterm +cygwin +dtterm +eterm,eterm-color|emacs +gnome,gnome-256color +hpterm +konsole,konsole-256color +kterm +linux +pcansi +rxvt,rxvt-256color,rxvt-88color,rxvt-unicode,rxvt-unicode-256color +screen,screen-256color +st,st-256color|simpleterm +termite +tmux +vt52 +vt100 +vt102 +vt220 +vt320 +vt400 +vt420 +wy50 +wy60 +wy99-ansi,wy99a-ansi +xfce +xterm,xterm-88color,xterm-256color +xterm-kitty diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/p/pcansi/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/p/pcansi/term.go new file mode 100644 index 0000000000..9e89c19772 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/p/pcansi/term.go @@ -0,0 +1,41 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package pcansi + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // ibm-pc terminal programs claiming to be ansi + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "pcansi", + Columns: 80, + Lines: 24, + Colors: 8, + Bell: "\a", + Clear: "\x1b[H\x1b[J", + AttrOff: "\x1b[0;10m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + SetFg: "\x1b[3%p1%dm", + SetBg: "\x1b[4%p1%dm", + SetFgBg: "\x1b[3%p1%d;4%p2%dm", + ResetFgBg: "\x1b[37;40m", + PadChar: "\x00", + AltChars: "+\x10,\x11-\x18.\x190\xdb`\x04a\xb1f\xf8g\xf1h\xb0j\xd9k\xbfl\xdam\xc0n\xc5o~p\xc4q\xc4r\xc4s_t\xc3u\xb4v\xc1w\xc2x\xb3y\xf3z\xf2{\xe3|\xd8}\x9c~\xfe", + EnterAcs: "\x1b[12m", + ExitAcs: "\x1b[10m", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\x1b[D", + CursorUp1: "\x1b[A", + KeyUp: "\x1b[A", + KeyDown: "\x1b[B", + KeyRight: "\x1b[C", + KeyLeft: "\x1b[D", + KeyBackspace: "\b", + KeyHome: "\x1b[H", + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/r/rxvt/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/r/rxvt/term.go new file mode 100644 index 0000000000..6fa9e7fa46 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/r/rxvt/term.go @@ -0,0 +1,485 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package rxvt + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // rxvt terminal emulator (X Window System) + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "rxvt", + Columns: 80, + Lines: 24, + Colors: 8, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b7\x1b[?47h", + ExitCA: "\x1b[2J\x1b[?47l\x1b8", + ShowCursor: "\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[m\x0f", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b=", + ExitKeypad: "\x1b>", + SetFg: "\x1b[3%p1%dm", + SetBg: "\x1b[4%p1%dm", + SetFgBg: "\x1b[3%p1%d;4%p2%dm", + ResetFgBg: "\x1b[39;49m", + PadChar: "\x00", + AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b(B\x1b)0", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1b[A", + KeyDown: "\x1b[B", + KeyRight: "\x1b[C", + KeyLeft: "\x1b[D", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1b[7~", + KeyEnd: "\x1b[8~", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1b[11~", + KeyF2: "\x1b[12~", + KeyF3: "\x1b[13~", + KeyF4: "\x1b[14~", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyF13: "\x1b[25~", + KeyF14: "\x1b[26~", + KeyF15: "\x1b[28~", + KeyF16: "\x1b[29~", + KeyF17: "\x1b[31~", + KeyF18: "\x1b[32~", + KeyF19: "\x1b[33~", + KeyF20: "\x1b[34~", + KeyF21: "\x1b[23$", + KeyF22: "\x1b[24$", + KeyF23: "\x1b[11^", + KeyF24: "\x1b[12^", + KeyF25: "\x1b[13^", + KeyF26: "\x1b[14^", + KeyF27: "\x1b[15^", + KeyF28: "\x1b[17^", + KeyF29: "\x1b[18^", + KeyF30: "\x1b[19^", + KeyF31: "\x1b[20^", + KeyF32: "\x1b[21^", + KeyF33: "\x1b[23^", + KeyF34: "\x1b[24^", + KeyF35: "\x1b[25^", + KeyF36: "\x1b[26^", + KeyF37: "\x1b[28^", + KeyF38: "\x1b[29^", + KeyF39: "\x1b[31^", + KeyF40: "\x1b[32^", + KeyF41: "\x1b[33^", + KeyF42: "\x1b[34^", + KeyF43: "\x1b[23@", + KeyF44: "\x1b[24@", + KeyBacktab: "\x1b[Z", + KeyShfLeft: "\x1b[d", + KeyShfRight: "\x1b[c", + KeyShfUp: "\x1b[a", + KeyShfDown: "\x1b[b", + KeyShfHome: "\x1b[7$", + KeyShfEnd: "\x1b[8$", + KeyShfDelete: "\x1b[3$", + KeyCtrlUp: "\x1b[Oa", + KeyCtrlDown: "\x1b[Ob", + KeyCtrlRight: "\x1b[Oc", + KeyCtrlLeft: "\x1b[Od", + KeyCtrlHome: "\x1b[7^", + KeyCtrlEnd: "\x1b[8^", + AutoMargin: true, + InsertChar: "\x1b[@", + }) + + // rxvt 2.7.9 with xterm 256-colors + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "rxvt-256color", + Columns: 80, + Lines: 24, + Colors: 256, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b7\x1b[?47h", + ExitCA: "\x1b[2J\x1b[?47l\x1b8", + ShowCursor: "\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[m\x0f", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b=", + ExitKeypad: "\x1b>", + SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", + ResetFgBg: "\x1b[39;49m", + PadChar: "\x00", + AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b(B\x1b)0", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1b[A", + KeyDown: "\x1b[B", + KeyRight: "\x1b[C", + KeyLeft: "\x1b[D", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1b[7~", + KeyEnd: "\x1b[8~", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1b[11~", + KeyF2: "\x1b[12~", + KeyF3: "\x1b[13~", + KeyF4: "\x1b[14~", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyF13: "\x1b[25~", + KeyF14: "\x1b[26~", + KeyF15: "\x1b[28~", + KeyF16: "\x1b[29~", + KeyF17: "\x1b[31~", + KeyF18: "\x1b[32~", + KeyF19: "\x1b[33~", + KeyF20: "\x1b[34~", + KeyF21: "\x1b[23$", + KeyF22: "\x1b[24$", + KeyF23: "\x1b[11^", + KeyF24: "\x1b[12^", + KeyF25: "\x1b[13^", + KeyF26: "\x1b[14^", + KeyF27: "\x1b[15^", + KeyF28: "\x1b[17^", + KeyF29: "\x1b[18^", + KeyF30: "\x1b[19^", + KeyF31: "\x1b[20^", + KeyF32: "\x1b[21^", + KeyF33: "\x1b[23^", + KeyF34: "\x1b[24^", + KeyF35: "\x1b[25^", + KeyF36: "\x1b[26^", + KeyF37: "\x1b[28^", + KeyF38: "\x1b[29^", + KeyF39: "\x1b[31^", + KeyF40: "\x1b[32^", + KeyF41: "\x1b[33^", + KeyF42: "\x1b[34^", + KeyF43: "\x1b[23@", + KeyF44: "\x1b[24@", + KeyBacktab: "\x1b[Z", + KeyShfLeft: "\x1b[d", + KeyShfRight: "\x1b[c", + KeyShfUp: "\x1b[a", + KeyShfDown: "\x1b[b", + KeyShfHome: "\x1b[7$", + KeyShfEnd: "\x1b[8$", + KeyShfDelete: "\x1b[3$", + KeyCtrlUp: "\x1b[Oa", + KeyCtrlDown: "\x1b[Ob", + KeyCtrlRight: "\x1b[Oc", + KeyCtrlLeft: "\x1b[Od", + KeyCtrlHome: "\x1b[7^", + KeyCtrlEnd: "\x1b[8^", + AutoMargin: true, + InsertChar: "\x1b[@", + }) + + // rxvt 2.7.9 with xterm 88-colors + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "rxvt-88color", + Columns: 80, + Lines: 24, + Colors: 88, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b7\x1b[?47h", + ExitCA: "\x1b[2J\x1b[?47l\x1b8", + ShowCursor: "\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[m\x0f", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b=", + ExitKeypad: "\x1b>", + SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", + ResetFgBg: "\x1b[39;49m", + PadChar: "\x00", + AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b(B\x1b)0", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1b[A", + KeyDown: "\x1b[B", + KeyRight: "\x1b[C", + KeyLeft: "\x1b[D", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1b[7~", + KeyEnd: "\x1b[8~", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1b[11~", + KeyF2: "\x1b[12~", + KeyF3: "\x1b[13~", + KeyF4: "\x1b[14~", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyF13: "\x1b[25~", + KeyF14: "\x1b[26~", + KeyF15: "\x1b[28~", + KeyF16: "\x1b[29~", + KeyF17: "\x1b[31~", + KeyF18: "\x1b[32~", + KeyF19: "\x1b[33~", + KeyF20: "\x1b[34~", + KeyF21: "\x1b[23$", + KeyF22: "\x1b[24$", + KeyF23: "\x1b[11^", + KeyF24: "\x1b[12^", + KeyF25: "\x1b[13^", + KeyF26: "\x1b[14^", + KeyF27: "\x1b[15^", + KeyF28: "\x1b[17^", + KeyF29: "\x1b[18^", + KeyF30: "\x1b[19^", + KeyF31: "\x1b[20^", + KeyF32: "\x1b[21^", + KeyF33: "\x1b[23^", + KeyF34: "\x1b[24^", + KeyF35: "\x1b[25^", + KeyF36: "\x1b[26^", + KeyF37: "\x1b[28^", + KeyF38: "\x1b[29^", + KeyF39: "\x1b[31^", + KeyF40: "\x1b[32^", + KeyF41: "\x1b[33^", + KeyF42: "\x1b[34^", + KeyF43: "\x1b[23@", + KeyF44: "\x1b[24@", + KeyBacktab: "\x1b[Z", + KeyShfLeft: "\x1b[d", + KeyShfRight: "\x1b[c", + KeyShfUp: "\x1b[a", + KeyShfDown: "\x1b[b", + KeyShfHome: "\x1b[7$", + KeyShfEnd: "\x1b[8$", + KeyShfDelete: "\x1b[3$", + KeyCtrlUp: "\x1b[Oa", + KeyCtrlDown: "\x1b[Ob", + KeyCtrlRight: "\x1b[Oc", + KeyCtrlLeft: "\x1b[Od", + KeyCtrlHome: "\x1b[7^", + KeyCtrlEnd: "\x1b[8^", + AutoMargin: true, + InsertChar: "\x1b[@", + }) + + // rxvt-unicode terminal (X Window System) + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "rxvt-unicode", + Columns: 80, + Lines: 24, + Colors: 88, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b[?1049h", + ExitCA: "\x1b[r\x1b[?1049l", + ShowCursor: "\x1b[?12l\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[m\x1b(B", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Italic: "\x1b[3m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b=", + ExitKeypad: "\x1b>", + SetFg: "\x1b[38;5;%p1%dm", + SetBg: "\x1b[48;5;%p1%dm", + SetFgBg: "\x1b[38;5;%p1%d;48;5;%p2%dm", + ResetFgBg: "\x1b[39;49m", + AltChars: "+C,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1b[A", + KeyDown: "\x1b[B", + KeyRight: "\x1b[C", + KeyLeft: "\x1b[D", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1b[7~", + KeyEnd: "\x1b[8~", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1b[11~", + KeyF2: "\x1b[12~", + KeyF3: "\x1b[13~", + KeyF4: "\x1b[14~", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyF13: "\x1b[25~", + KeyF14: "\x1b[26~", + KeyF15: "\x1b[28~", + KeyF16: "\x1b[29~", + KeyF17: "\x1b[31~", + KeyF18: "\x1b[32~", + KeyF19: "\x1b[33~", + KeyF20: "\x1b[34~", + KeyBacktab: "\x1b[Z", + KeyShfLeft: "\x1b[d", + KeyShfRight: "\x1b[c", + KeyShfUp: "\x1b[a", + KeyShfDown: "\x1b[b", + KeyShfHome: "\x1b[7$", + KeyShfEnd: "\x1b[8$", + KeyShfInsert: "\x1b[2$", + KeyShfDelete: "\x1b[3$", + KeyCtrlUp: "\x1b[Oa", + KeyCtrlDown: "\x1b[Ob", + KeyCtrlRight: "\x1b[Oc", + KeyCtrlLeft: "\x1b[Od", + KeyCtrlHome: "\x1b[7^", + KeyCtrlEnd: "\x1b[8^", + AutoMargin: true, + InsertChar: "\x1b[@", + }) + + // rxvt-unicode terminal with 256 colors (X Window System) + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "rxvt-unicode-256color", + Columns: 80, + Lines: 24, + Colors: 256, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b[?1049h", + ExitCA: "\x1b[r\x1b[?1049l", + ShowCursor: "\x1b[?12l\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[m\x1b(B", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Italic: "\x1b[3m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b=", + ExitKeypad: "\x1b>", + SetFg: "\x1b[38;5;%p1%dm", + SetBg: "\x1b[48;5;%p1%dm", + SetFgBg: "\x1b[38;5;%p1%d;48;5;%p2%dm", + ResetFgBg: "\x1b[39;49m", + AltChars: "+C,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1b[A", + KeyDown: "\x1b[B", + KeyRight: "\x1b[C", + KeyLeft: "\x1b[D", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1b[7~", + KeyEnd: "\x1b[8~", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1b[11~", + KeyF2: "\x1b[12~", + KeyF3: "\x1b[13~", + KeyF4: "\x1b[14~", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyF13: "\x1b[25~", + KeyF14: "\x1b[26~", + KeyF15: "\x1b[28~", + KeyF16: "\x1b[29~", + KeyF17: "\x1b[31~", + KeyF18: "\x1b[32~", + KeyF19: "\x1b[33~", + KeyF20: "\x1b[34~", + KeyBacktab: "\x1b[Z", + KeyShfLeft: "\x1b[d", + KeyShfRight: "\x1b[c", + KeyShfUp: "\x1b[a", + KeyShfDown: "\x1b[b", + KeyShfHome: "\x1b[7$", + KeyShfEnd: "\x1b[8$", + KeyShfInsert: "\x1b[2$", + KeyShfDelete: "\x1b[3$", + KeyCtrlUp: "\x1b[Oa", + KeyCtrlDown: "\x1b[Ob", + KeyCtrlRight: "\x1b[Oc", + KeyCtrlLeft: "\x1b[Od", + KeyCtrlHome: "\x1b[7^", + KeyCtrlEnd: "\x1b[8^", + AutoMargin: true, + InsertChar: "\x1b[@", + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/s/screen/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/s/screen/term.go new file mode 100644 index 0000000000..d95d636337 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/s/screen/term.go @@ -0,0 +1,128 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package screen + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // VT 100/ANSI X3.64 virtual terminal + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "screen", + Columns: 80, + Lines: 24, + Colors: 8, + Bell: "\a", + Clear: "\x1b[H\x1b[J", + EnterCA: "\x1b[?1049h", + ExitCA: "\x1b[?1049l", + ShowCursor: "\x1b[34h\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[m\x0f", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[3%p1%dm", + SetBg: "\x1b[4%p1%dm", + SetFgBg: "\x1b[3%p1%d;4%p2%dm", + ResetFgBg: "\x1b[39;49m", + PadChar: "\x00", + AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b(B\x1b)0", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1bM", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1b[1~", + KeyEnd: "\x1b[4~", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + AutoMargin: true, + }) + + // GNU Screen with 256 colors + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "screen-256color", + Columns: 80, + Lines: 24, + Colors: 256, + Bell: "\a", + Clear: "\x1b[H\x1b[J", + EnterCA: "\x1b[?1049h", + ExitCA: "\x1b[?1049l", + ShowCursor: "\x1b[34h\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[m\x0f", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", + ResetFgBg: "\x1b[39;49m", + PadChar: "\x00", + AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b(B\x1b)0", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1bM", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1b[1~", + KeyEnd: "\x1b[4~", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/s/simpleterm/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/s/simpleterm/term.go new file mode 100644 index 0000000000..f633f29414 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/s/simpleterm/term.go @@ -0,0 +1,136 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package simpleterm + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // simpleterm + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "st", + Columns: 80, + Lines: 24, + Colors: 8, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b[?1049h", + ExitCA: "\x1b[?1049l", + ShowCursor: "\x1b[?12l\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[0m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[3%p1%dm", + SetBg: "\x1b[4%p1%dm", + SetFgBg: "\x1b[3%p1%d;4%p2%dm", + ResetFgBg: "\x1b[39;49m", + AltChars: "+C,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + EnableAcs: "\x1b)0", + StrikeThrough: "\x1b[9m", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1b[1~", + KeyEnd: "\x1b[4~", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyClear: "\x1b[3;5~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + TrueColor: true, + AutoMargin: true, + }) + + // simpleterm with 256 colors + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "st-256color", + Columns: 80, + Lines: 24, + Colors: 256, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b[?1049h", + ExitCA: "\x1b[?1049l", + ShowCursor: "\x1b[?12l\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[0m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", + ResetFgBg: "\x1b[39;49m", + AltChars: "+C,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + EnableAcs: "\x1b)0", + StrikeThrough: "\x1b[9m", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1b[1~", + KeyEnd: "\x1b[4~", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyClear: "\x1b[3;5~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + TrueColor: true, + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/s/sun/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/s/sun/term.go new file mode 100644 index 0000000000..16cb96c20a --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/s/sun/term.go @@ -0,0 +1,112 @@ +// Copyright 2021 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +// This terminal definition is hand-coded, as the default terminfo for +// this terminal is busted with respect to color. Unlike pretty much every +// other ANSI compliant terminal, this terminal cannot combine foreground and +// background escapes. The default terminfo also only provides escapes for +// 16-bit color. + +package sun + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // Sun Microsystems Inc. workstation console + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "sun", + Aliases: []string{"sun1", "sun2"}, + Columns: 80, + Lines: 34, + Bell: "\a", + Clear: "\f", + AttrOff: "\x1b[m", + Reverse: "\x1b[7m", + PadChar: "\x00", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1b[A", + KeyDown: "\x1b[B", + KeyRight: "\x1b[C", + KeyLeft: "\x1b[D", + KeyInsert: "\x1b[247z", + KeyDelete: "\u007f", + KeyBackspace: "\b", + KeyHome: "\x1b[214z", + KeyEnd: "\x1b[220z", + KeyPgUp: "\x1b[216z", + KeyPgDn: "\x1b[222z", + KeyF1: "\x1b[224z", + KeyF2: "\x1b[225z", + KeyF3: "\x1b[226z", + KeyF4: "\x1b[227z", + KeyF5: "\x1b[228z", + KeyF6: "\x1b[229z", + KeyF7: "\x1b[230z", + KeyF8: "\x1b[231z", + KeyF9: "\x1b[232z", + KeyF10: "\x1b[233z", + KeyF11: "\x1b[234z", + KeyF12: "\x1b[235z", + AutoMargin: true, + InsertChar: "\x1b[@", + }) + + // Sun Microsystems Workstation console with color support (IA systems) + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "sun-color", + Columns: 80, + Lines: 34, + Colors: 256, + Bell: "\a", + Clear: "\f", + AttrOff: "\x1b[m", + Bold: "\x1b[1m", + Reverse: "\x1b[7m", + SetFg: "\x1b[38;5;%p1%dm", + SetBg: "\x1b[48;5;%p1%dm", + ResetFgBg: "\x1b[0m", + PadChar: "\x00", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1b[A", + KeyDown: "\x1b[B", + KeyRight: "\x1b[C", + KeyLeft: "\x1b[D", + KeyInsert: "\x1b[247z", + KeyDelete: "\u007f", + KeyBackspace: "\b", + KeyHome: "\x1b[214z", + KeyEnd: "\x1b[220z", + KeyPgUp: "\x1b[216z", + KeyPgDn: "\x1b[222z", + KeyF1: "\x1b[224z", + KeyF2: "\x1b[225z", + KeyF3: "\x1b[226z", + KeyF4: "\x1b[227z", + KeyF5: "\x1b[228z", + KeyF6: "\x1b[229z", + KeyF7: "\x1b[230z", + KeyF8: "\x1b[231z", + KeyF9: "\x1b[232z", + KeyF10: "\x1b[233z", + KeyF11: "\x1b[234z", + KeyF12: "\x1b[235z", + AutoMargin: true, + InsertChar: "\x1b[@", + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/t/termite/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/t/termite/term.go new file mode 100644 index 0000000000..593d385561 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/t/termite/term.go @@ -0,0 +1,67 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package termite + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // VTE-based terminal + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "termite", + Columns: 80, + Lines: 24, + Colors: 256, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b[?1049h", + ExitCA: "\x1b[?1049l", + ShowCursor: "\x1b[?12l\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b(B\x1b[m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Italic: "\x1b[3m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", + ResetFgBg: "\x1b[39;49m", + AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1bOH", + KeyEnd: "\x1bOF", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + AutoMargin: true, + InsertChar: "\x1b[@", + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/t/tmux/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/t/tmux/term.go new file mode 100644 index 0000000000..44975d69a4 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/t/tmux/term.go @@ -0,0 +1,71 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package tmux + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // tmux terminal multiplexer + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "tmux", + Columns: 80, + Lines: 24, + Colors: 8, + Bell: "\a", + Clear: "\x1b[H\x1b[J", + EnterCA: "\x1b[?1049h", + ExitCA: "\x1b[?1049l", + ShowCursor: "\x1b[34h\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[m\x0f", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[3%p1%dm", + SetBg: "\x1b[4%p1%dm", + SetFgBg: "\x1b[3%p1%d;4%p2%dm", + ResetFgBg: "\x1b[39;49m", + PadChar: "\x00", + AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b(B\x1b)0", + StrikeThrough: "\x1b[9m", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1bM", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1b[1~", + KeyEnd: "\x1b[4~", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/terminfo.go b/vendor/github.com/gdamore/tcell/v2/terminfo/terminfo.go new file mode 100644 index 0000000000..7e17352cc4 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/terminfo.go @@ -0,0 +1,827 @@ +// Copyright 2021 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package terminfo + +import ( + "bytes" + "errors" + "fmt" + "io" + "os" + "strconv" + "strings" + "sync" + "time" +) + +var ( + // ErrTermNotFound indicates that a suitable terminal entry could + // not be found. This can result from either not having TERM set, + // or from the TERM failing to support certain minimal functionality, + // in particular absolute cursor addressability (the cup capability) + // is required. For example, legacy "adm3" lacks this capability, + // whereas the slightly newer "adm3a" supports it. This failure + // occurs most often with "dumb". + ErrTermNotFound = errors.New("terminal entry not found") +) + +// Terminfo represents a terminfo entry. Note that we use friendly names +// in Go, but when we write out JSON, we use the same names as terminfo. +// The name, aliases and smous, rmous fields do not come from terminfo directly. +type Terminfo struct { + Name string + Aliases []string + Columns int // cols + Lines int // lines + Colors int // colors + Bell string // bell + Clear string // clear + EnterCA string // smcup + ExitCA string // rmcup + ShowCursor string // cnorm + HideCursor string // civis + AttrOff string // sgr0 + Underline string // smul + Bold string // bold + Blink string // blink + Reverse string // rev + Dim string // dim + Italic string // sitm + EnterKeypad string // smkx + ExitKeypad string // rmkx + SetFg string // setaf + SetBg string // setab + ResetFgBg string // op + SetCursor string // cup + CursorBack1 string // cub1 + CursorUp1 string // cuu1 + PadChar string // pad + KeyBackspace string // kbs + KeyF1 string // kf1 + KeyF2 string // kf2 + KeyF3 string // kf3 + KeyF4 string // kf4 + KeyF5 string // kf5 + KeyF6 string // kf6 + KeyF7 string // kf7 + KeyF8 string // kf8 + KeyF9 string // kf9 + KeyF10 string // kf10 + KeyF11 string // kf11 + KeyF12 string // kf12 + KeyF13 string // kf13 + KeyF14 string // kf14 + KeyF15 string // kf15 + KeyF16 string // kf16 + KeyF17 string // kf17 + KeyF18 string // kf18 + KeyF19 string // kf19 + KeyF20 string // kf20 + KeyF21 string // kf21 + KeyF22 string // kf22 + KeyF23 string // kf23 + KeyF24 string // kf24 + KeyF25 string // kf25 + KeyF26 string // kf26 + KeyF27 string // kf27 + KeyF28 string // kf28 + KeyF29 string // kf29 + KeyF30 string // kf30 + KeyF31 string // kf31 + KeyF32 string // kf32 + KeyF33 string // kf33 + KeyF34 string // kf34 + KeyF35 string // kf35 + KeyF36 string // kf36 + KeyF37 string // kf37 + KeyF38 string // kf38 + KeyF39 string // kf39 + KeyF40 string // kf40 + KeyF41 string // kf41 + KeyF42 string // kf42 + KeyF43 string // kf43 + KeyF44 string // kf44 + KeyF45 string // kf45 + KeyF46 string // kf46 + KeyF47 string // kf47 + KeyF48 string // kf48 + KeyF49 string // kf49 + KeyF50 string // kf50 + KeyF51 string // kf51 + KeyF52 string // kf52 + KeyF53 string // kf53 + KeyF54 string // kf54 + KeyF55 string // kf55 + KeyF56 string // kf56 + KeyF57 string // kf57 + KeyF58 string // kf58 + KeyF59 string // kf59 + KeyF60 string // kf60 + KeyF61 string // kf61 + KeyF62 string // kf62 + KeyF63 string // kf63 + KeyF64 string // kf64 + KeyInsert string // kich1 + KeyDelete string // kdch1 + KeyHome string // khome + KeyEnd string // kend + KeyHelp string // khlp + KeyPgUp string // kpp + KeyPgDn string // knp + KeyUp string // kcuu1 + KeyDown string // kcud1 + KeyLeft string // kcub1 + KeyRight string // kcuf1 + KeyBacktab string // kcbt + KeyExit string // kext + KeyClear string // kclr + KeyPrint string // kprt + KeyCancel string // kcan + Mouse string // kmous + AltChars string // acsc + EnterAcs string // smacs + ExitAcs string // rmacs + EnableAcs string // enacs + KeyShfRight string // kRIT + KeyShfLeft string // kLFT + KeyShfHome string // kHOM + KeyShfEnd string // kEND + KeyShfInsert string // kIC + KeyShfDelete string // kDC + + // These are non-standard extensions to terminfo. This includes + // true color support, and some additional keys. Its kind of bizarre + // that shifted variants of left and right exist, but not up and down. + // Terminal support for these are going to vary amongst XTerm + // emulations, so don't depend too much on them in your application. + + StrikeThrough string // smxx + SetFgBg string // setfgbg + SetFgBgRGB string // setfgbgrgb + SetFgRGB string // setfrgb + SetBgRGB string // setbrgb + KeyShfUp string // shift-up + KeyShfDown string // shift-down + KeyShfPgUp string // shift-kpp + KeyShfPgDn string // shift-knp + KeyCtrlUp string // ctrl-up + KeyCtrlDown string // ctrl-left + KeyCtrlRight string // ctrl-right + KeyCtrlLeft string // ctrl-left + KeyMetaUp string // meta-up + KeyMetaDown string // meta-left + KeyMetaRight string // meta-right + KeyMetaLeft string // meta-left + KeyAltUp string // alt-up + KeyAltDown string // alt-left + KeyAltRight string // alt-right + KeyAltLeft string // alt-left + KeyCtrlHome string + KeyCtrlEnd string + KeyMetaHome string + KeyMetaEnd string + KeyAltHome string + KeyAltEnd string + KeyAltShfUp string + KeyAltShfDown string + KeyAltShfLeft string + KeyAltShfRight string + KeyMetaShfUp string + KeyMetaShfDown string + KeyMetaShfLeft string + KeyMetaShfRight string + KeyCtrlShfUp string + KeyCtrlShfDown string + KeyCtrlShfLeft string + KeyCtrlShfRight string + KeyCtrlShfHome string + KeyCtrlShfEnd string + KeyAltShfHome string + KeyAltShfEnd string + KeyMetaShfHome string + KeyMetaShfEnd string + EnablePaste string // bracketed paste mode + DisablePaste string + PasteStart string + PasteEnd string + Modifiers int + InsertChar string // string to insert a character (ich1) + AutoMargin bool // true if writing to last cell in line advances + TrueColor bool // true if the terminal supports direct color +} + +const ( + ModifiersNone = 0 + ModifiersXTerm = 1 +) + +type stackElem struct { + s string + i int + isStr bool + isInt bool +} + +type stack []stackElem + +func (st stack) Push(v string) stack { + e := stackElem{ + s: v, + isStr: true, + } + return append(st, e) +} + +func (st stack) Pop() (string, stack) { + v := "" + if len(st) > 0 { + e := st[len(st)-1] + st = st[:len(st)-1] + if e.isStr { + v = e.s + } else { + v = strconv.Itoa(e.i) + } + } + return v, st +} + +func (st stack) PopInt() (int, stack) { + if len(st) > 0 { + e := st[len(st)-1] + st = st[:len(st)-1] + if e.isInt { + return e.i, st + } else if e.isStr { + // If the string that was pushed was the representation + // of a number e.g. '123', then return the number. If the + // conversion doesn't work, assume the string pushed was + // intended to return, as an int, the ascii representation + // of the (one and only) character. + i, err := strconv.Atoi(e.s) + if err == nil { + return i, st + } else if len(e.s) >= 1 { + return int(e.s[0]), st + } + } + } + return 0, st +} + +func (st stack) PopBool() (bool, stack) { + if len(st) > 0 { + e := st[len(st)-1] + st = st[:len(st)-1] + if e.isStr { + if e.s == "1" { + return true, st + } + return false, st + } else if e.i == 1 { + return true, st + } else { + return false, st + } + } + return false, st +} + +func (st stack) PushInt(i int) stack { + e := stackElem{ + i: i, + isInt: true, + } + return append(st, e) +} + +func (st stack) PushBool(i bool) stack { + if i { + return st.PushInt(1) + } + return st.PushInt(0) +} + +// static vars +var svars [26]string + +// paramsBuffer handles some persistent state for TParam. Technically we +// could probably dispense with this, but caching buffer arrays gives us +// a nice little performance boost. Furthermore, we know that TParam is +// rarely (never?) called re-entrantly, so we can just reuse the same +// buffers, making it thread-safe by stashing a lock. +type paramsBuffer struct { + out bytes.Buffer + buf bytes.Buffer + lk sync.Mutex +} + +// Start initializes the params buffer with the initial string data. +// It also locks the paramsBuffer. The caller must call End() when +// finished. +func (pb *paramsBuffer) Start(s string) { + pb.lk.Lock() + pb.out.Reset() + pb.buf.Reset() + pb.buf.WriteString(s) +} + +// End returns the final output from TParam, but it also releases the lock. +func (pb *paramsBuffer) End() string { + s := pb.out.String() + pb.lk.Unlock() + return s +} + +// NextCh returns the next input character to the expander. +func (pb *paramsBuffer) NextCh() (byte, error) { + return pb.buf.ReadByte() +} + +// PutCh "emits" (rather schedules for output) a single byte character. +func (pb *paramsBuffer) PutCh(ch byte) { + pb.out.WriteByte(ch) +} + +// PutString schedules a string for output. +func (pb *paramsBuffer) PutString(s string) { + pb.out.WriteString(s) +} + +var pb = ¶msBuffer{} + +// TParm takes a terminfo parameterized string, such as setaf or cup, and +// evaluates the string, and returns the result with the parameter +// applied. +func (t *Terminfo) TParm(s string, p ...int) string { + var stk stack + var a, b string + var ai, bi int + var ab bool + var dvars [26]string + var params [9]int + + pb.Start(s) + + // make sure we always have 9 parameters -- makes it easier + // later to skip checks + for i := 0; i < len(params) && i < len(p); i++ { + params[i] = p[i] + } + + nest := 0 + + for { + + ch, err := pb.NextCh() + if err != nil { + break + } + + if ch != '%' { + pb.PutCh(ch) + continue + } + + ch, err = pb.NextCh() + if err != nil { + // XXX Error + break + } + + switch ch { + case '%': // quoted % + pb.PutCh(ch) + + case 'i': // increment both parameters (ANSI cup support) + params[0]++ + params[1]++ + + case 'c', 's': + // NB: these, and 'd' below are special cased for + // efficiency. They could be handled by the richer + // format support below, less efficiently. + a, stk = stk.Pop() + pb.PutString(a) + + case 'd': + ai, stk = stk.PopInt() + pb.PutString(strconv.Itoa(ai)) + + case '0', '1', '2', '3', '4', 'x', 'X', 'o', ':': + // This is pretty suboptimal, but this is rarely used. + // None of the mainstream terminals use any of this, + // and it would surprise me if this code is ever + // executed outside of test cases. + f := "%" + if ch == ':' { + ch, _ = pb.NextCh() + } + f += string(ch) + for ch == '+' || ch == '-' || ch == '#' || ch == ' ' { + ch, _ = pb.NextCh() + f += string(ch) + } + for (ch >= '0' && ch <= '9') || ch == '.' { + ch, _ = pb.NextCh() + f += string(ch) + } + switch ch { + case 'd', 'x', 'X', 'o': + ai, stk = stk.PopInt() + pb.PutString(fmt.Sprintf(f, ai)) + case 'c', 's': + a, stk = stk.Pop() + pb.PutString(fmt.Sprintf(f, a)) + } + + case 'p': // push parameter + ch, _ = pb.NextCh() + ai = int(ch - '1') + if ai >= 0 && ai < len(params) { + stk = stk.PushInt(params[ai]) + } else { + stk = stk.PushInt(0) + } + + case 'P': // pop & store variable + ch, _ = pb.NextCh() + if ch >= 'A' && ch <= 'Z' { + svars[int(ch-'A')], stk = stk.Pop() + } else if ch >= 'a' && ch <= 'z' { + dvars[int(ch-'a')], stk = stk.Pop() + } + + case 'g': // recall & push variable + ch, _ = pb.NextCh() + if ch >= 'A' && ch <= 'Z' { + stk = stk.Push(svars[int(ch-'A')]) + } else if ch >= 'a' && ch <= 'z' { + stk = stk.Push(dvars[int(ch-'a')]) + } + + case '\'': // push(char) + ch, _ = pb.NextCh() + pb.NextCh() // must be ' but we don't check + stk = stk.Push(string(ch)) + + case '{': // push(int) + ai = 0 + ch, _ = pb.NextCh() + for ch >= '0' && ch <= '9' { + ai *= 10 + ai += int(ch - '0') + ch, _ = pb.NextCh() + } + // ch must be '}' but no verification + stk = stk.PushInt(ai) + + case 'l': // push(strlen(pop)) + a, stk = stk.Pop() + stk = stk.PushInt(len(a)) + + case '+': + bi, stk = stk.PopInt() + ai, stk = stk.PopInt() + stk = stk.PushInt(ai + bi) + + case '-': + bi, stk = stk.PopInt() + ai, stk = stk.PopInt() + stk = stk.PushInt(ai - bi) + + case '*': + bi, stk = stk.PopInt() + ai, stk = stk.PopInt() + stk = stk.PushInt(ai * bi) + + case '/': + bi, stk = stk.PopInt() + ai, stk = stk.PopInt() + if bi != 0 { + stk = stk.PushInt(ai / bi) + } else { + stk = stk.PushInt(0) + } + + case 'm': // push(pop mod pop) + bi, stk = stk.PopInt() + ai, stk = stk.PopInt() + if bi != 0 { + stk = stk.PushInt(ai % bi) + } else { + stk = stk.PushInt(0) + } + + case '&': // AND + bi, stk = stk.PopInt() + ai, stk = stk.PopInt() + stk = stk.PushInt(ai & bi) + + case '|': // OR + bi, stk = stk.PopInt() + ai, stk = stk.PopInt() + stk = stk.PushInt(ai | bi) + + case '^': // XOR + bi, stk = stk.PopInt() + ai, stk = stk.PopInt() + stk = stk.PushInt(ai ^ bi) + + case '~': // bit complement + ai, stk = stk.PopInt() + stk = stk.PushInt(ai ^ -1) + + case '!': // logical NOT + ai, stk = stk.PopInt() + stk = stk.PushBool(ai != 0) + + case '=': // numeric compare or string compare + b, stk = stk.Pop() + a, stk = stk.Pop() + stk = stk.PushBool(a == b) + + case '>': // greater than, numeric + bi, stk = stk.PopInt() + ai, stk = stk.PopInt() + stk = stk.PushBool(ai > bi) + + case '<': // less than, numeric + bi, stk = stk.PopInt() + ai, stk = stk.PopInt() + stk = stk.PushBool(ai < bi) + + case '?': // start conditional + + case 't': + ab, stk = stk.PopBool() + if ab { + // just keep going + break + } + nest = 0 + ifloop: + // this loop consumes everything until we hit our else, + // or the end of the conditional + for { + ch, err = pb.NextCh() + if err != nil { + break + } + if ch != '%' { + continue + } + ch, _ = pb.NextCh() + switch ch { + case ';': + if nest == 0 { + break ifloop + } + nest-- + case '?': + nest++ + case 'e': + if nest == 0 { + break ifloop + } + } + } + + case 'e': + // if we got here, it means we didn't use the else + // in the 't' case above, and we should skip until + // the end of the conditional + nest = 0 + elloop: + for { + ch, err = pb.NextCh() + if err != nil { + break + } + if ch != '%' { + continue + } + ch, _ = pb.NextCh() + switch ch { + case ';': + if nest == 0 { + break elloop + } + nest-- + case '?': + nest++ + } + } + + case ';': // endif + + } + } + + return pb.End() +} + +// TPuts emits the string to the writer, but expands inline padding +// indications (of the form $<[delay]> where [delay] is msec) to +// a suitable time (unless the terminfo string indicates this isn't needed +// by specifying npc - no padding). All Terminfo based strings should be +// emitted using this function. +func (t *Terminfo) TPuts(w io.Writer, s string) { + for { + beg := strings.Index(s, "$<") + if beg < 0 { + // Most strings don't need padding, which is good news! + io.WriteString(w, s) + return + } + io.WriteString(w, s[:beg]) + s = s[beg+2:] + end := strings.Index(s, ">") + if end < 0 { + // unterminated.. just emit bytes unadulterated + io.WriteString(w, "$<"+s) + return + } + val := s[:end] + s = s[end+1:] + padus := 0 + unit := time.Millisecond + dot := false + loop: + for i := range val { + switch val[i] { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + padus *= 10 + padus += int(val[i] - '0') + if dot { + unit /= 10 + } + case '.': + if !dot { + dot = true + } else { + break loop + } + default: + break loop + } + } + + // Curses historically uses padding to achieve "fine grained" + // delays. We have much better clocks these days, and so we + // do not rely on padding but simply sleep a bit. + if len(t.PadChar) > 0 { + time.Sleep(unit * time.Duration(padus)) + } + } +} + +// TGoto returns a string suitable for addressing the cursor at the given +// row and column. The origin 0, 0 is in the upper left corner of the screen. +func (t *Terminfo) TGoto(col, row int) string { + return t.TParm(t.SetCursor, row, col) +} + +// TColor returns a string corresponding to the given foreground and background +// colors. Either fg or bg can be set to -1 to elide. +func (t *Terminfo) TColor(fi, bi int) string { + rv := "" + // As a special case, we map bright colors to lower versions if the + // color table only holds 8. For the remaining 240 colors, the user + // is out of luck. Someday we could create a mapping table, but its + // not worth it. + if t.Colors == 8 { + if fi > 7 && fi < 16 { + fi -= 8 + } + if bi > 7 && bi < 16 { + bi -= 8 + } + } + if t.Colors > fi && fi >= 0 { + rv += t.TParm(t.SetFg, fi) + } + if t.Colors > bi && bi >= 0 { + rv += t.TParm(t.SetBg, bi) + } + return rv +} + +var ( + dblock sync.Mutex + terminfos = make(map[string]*Terminfo) + aliases = make(map[string]string) +) + +// AddTerminfo can be called to register a new Terminfo entry. +func AddTerminfo(t *Terminfo) { + dblock.Lock() + terminfos[t.Name] = t + for _, x := range t.Aliases { + terminfos[x] = t + } + dblock.Unlock() +} + +// LookupTerminfo attempts to find a definition for the named $TERM. +func LookupTerminfo(name string) (*Terminfo, error) { + if name == "" { + // else on windows: index out of bounds + // on the name[0] reference below + return nil, ErrTermNotFound + } + + addtruecolor := false + add256color := false + switch os.Getenv("COLORTERM") { + case "truecolor", "24bit", "24-bit": + addtruecolor = true + } + dblock.Lock() + t := terminfos[name] + dblock.Unlock() + + // If the name ends in -truecolor, then fabricate an entry + // from the corresponding -256color, -color, or bare terminal. + if t != nil && t.TrueColor { + addtruecolor = true + } else if t == nil && strings.HasSuffix(name, "-truecolor") { + + suffixes := []string{ + "-256color", + "-88color", + "-color", + "", + } + base := name[:len(name)-len("-truecolor")] + for _, s := range suffixes { + if t, _ = LookupTerminfo(base + s); t != nil { + addtruecolor = true + break + } + } + } + + // If the name ends in -256color, maybe fabricate using the xterm 256 color sequences + if t == nil && strings.HasSuffix(name, "-256color") { + suffixes := []string{ + "-88color", + "-color", + } + base := name[:len(name)-len("-256color")] + for _, s := range suffixes { + if t, _ = LookupTerminfo(base + s); t != nil { + add256color = true + break + } + } + } + + if t == nil { + return nil, ErrTermNotFound + } + + switch os.Getenv("TCELL_TRUECOLOR") { + case "": + case "disable": + addtruecolor = false + default: + addtruecolor = true + } + + // If the user has requested 24-bit color with $COLORTERM, then + // amend the value (unless already present). This means we don't + // need to have a value present. + if addtruecolor && + t.SetFgBgRGB == "" && + t.SetFgRGB == "" && + t.SetBgRGB == "" { + + // Supply vanilla ISO 8613-6:1994 24-bit color sequences. + t.SetFgRGB = "\x1b[38;2;%p1%d;%p2%d;%p3%dm" + t.SetBgRGB = "\x1b[48;2;%p1%d;%p2%d;%p3%dm" + t.SetFgBgRGB = "\x1b[38;2;%p1%d;%p2%d;%p3%d;" + + "48;2;%p4%d;%p5%d;%p6%dm" + } + + if add256color { + t.Colors = 256 + t.SetFg = "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m" + t.SetBg = "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m" + t.SetFgBg = "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m" + t.ResetFgBg = "\x1b[39;49m" + } + return t, nil +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt100/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt100/term.go new file mode 100644 index 0000000000..0ae3918aca --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt100/term.go @@ -0,0 +1,49 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package vt100 + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // dec vt100 (w/advanced video) + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "vt100", + Aliases: []string{"vt100-am"}, + Columns: 80, + Lines: 24, + Bell: "\a", + Clear: "\x1b[H\x1b[J$<50>", + AttrOff: "\x1b[m\x0f$<2>", + Underline: "\x1b[4m$<2>", + Bold: "\x1b[1m$<2>", + Blink: "\x1b[5m$<2>", + Reverse: "\x1b[7m$<2>", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + PadChar: "\x00", + AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b(B\x1b)0", + SetCursor: "\x1b[%i%p1%d;%p2%dH$<5>", + CursorBack1: "\b", + CursorUp1: "\x1b[A$<2>", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyBackspace: "\b", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1bOt", + KeyF6: "\x1bOu", + KeyF7: "\x1bOv", + KeyF8: "\x1bOl", + KeyF9: "\x1bOw", + KeyF10: "\x1bOx", + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt102/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt102/term.go new file mode 100644 index 0000000000..ec8dae2468 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt102/term.go @@ -0,0 +1,48 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package vt102 + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // dec vt102 + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "vt102", + Columns: 80, + Lines: 24, + Bell: "\a", + Clear: "\x1b[H\x1b[J$<50>", + AttrOff: "\x1b[m\x0f$<2>", + Underline: "\x1b[4m$<2>", + Bold: "\x1b[1m$<2>", + Blink: "\x1b[5m$<2>", + Reverse: "\x1b[7m$<2>", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + PadChar: "\x00", + AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b(B\x1b)0", + SetCursor: "\x1b[%i%p1%d;%p2%dH$<5>", + CursorBack1: "\b", + CursorUp1: "\x1b[A$<2>", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyBackspace: "\b", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1bOt", + KeyF6: "\x1bOu", + KeyF7: "\x1bOv", + KeyF8: "\x1bOl", + KeyF9: "\x1bOw", + KeyF10: "\x1bOx", + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt220/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt220/term.go new file mode 100644 index 0000000000..75ab9a8ca4 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt220/term.go @@ -0,0 +1,59 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package vt220 + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // dec vt220 + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "vt220", + Aliases: []string{"vt200"}, + Columns: 80, + Lines: 24, + Bell: "\a", + Clear: "\x1b[H\x1b[J", + AttrOff: "\x1b[m\x1b(B", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + PadChar: "\x00", + AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0$<2>", + ExitAcs: "\x1b(B$<4>", + EnableAcs: "\x1b)0", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1b[A", + KeyDown: "\x1b[B", + KeyRight: "\x1b[C", + KeyLeft: "\x1b[D", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\b", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyF13: "\x1b[25~", + KeyF14: "\x1b[26~", + KeyF17: "\x1b[31~", + KeyF18: "\x1b[32~", + KeyF19: "\x1b[33~", + KeyF20: "\x1b[34~", + KeyHelp: "\x1b[28~", + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt320/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt320/term.go new file mode 100644 index 0000000000..3fd3d39f0f --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt320/term.go @@ -0,0 +1,64 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package vt320 + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // dec vt320 7 bit terminal + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "vt320", + Aliases: []string{"vt300"}, + Columns: 80, + Lines: 24, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + ShowCursor: "\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[m\x1b(B", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + PadChar: "\x00", + AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1b[1~", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyF13: "\x1b[25~", + KeyF14: "\x1b[26~", + KeyF15: "\x1b[28~", + KeyF16: "\x1b[29~", + KeyF17: "\x1b[31~", + KeyF18: "\x1b[32~", + KeyF19: "\x1b[33~", + KeyF20: "\x1b[34~", + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt400/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt400/term.go new file mode 100644 index 0000000000..0c07e9d2c5 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt400/term.go @@ -0,0 +1,48 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package vt400 + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // dec vt400 24x80 column autowrap + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "vt400", + Aliases: []string{"vt400-24", "dec-vt400"}, + Columns: 80, + Lines: 24, + Clear: "\x1b[H\x1b[J$<10/>", + ShowCursor: "\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[m\x1b(B", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + PadChar: "\x00", + AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyBackspace: "\b", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + AutoMargin: true, + InsertChar: "\x1b[@", + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt420/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt420/term.go new file mode 100644 index 0000000000..094886e270 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt420/term.go @@ -0,0 +1,54 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package vt420 + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // DEC VT420 + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "vt420", + Columns: 80, + Lines: 24, + Bell: "\a", + Clear: "\x1b[H\x1b[2J$<50>", + ShowCursor: "\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[m\x1b(B$<2>", + Underline: "\x1b[4m", + Bold: "\x1b[1m$<2>", + Blink: "\x1b[5m$<2>", + Reverse: "\x1b[7m$<2>", + EnterKeypad: "\x1b=", + ExitKeypad: "\x1b>", + PadChar: "\x00", + AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0$<2>", + ExitAcs: "\x1b(B$<4>", + EnableAcs: "\x1b)0", + SetCursor: "\x1b[%i%p1%d;%p2%dH$<10>", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1b[A", + KeyDown: "\x1b[B", + KeyRight: "\x1b[C", + KeyLeft: "\x1b[D", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\b", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[17~", + KeyF6: "\x1b[18~", + KeyF7: "\x1b[19~", + KeyF8: "\x1b[20~", + KeyF9: "\x1b[21~", + KeyF10: "\x1b[29~", + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt52/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt52/term.go new file mode 100644 index 0000000000..ba49f7f5ee --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/v/vt52/term.go @@ -0,0 +1,29 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package vt52 + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // dec vt52 + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "vt52", + Columns: 80, + Lines: 24, + Bell: "\a", + Clear: "\x1bH\x1bJ", + PadChar: "\x00", + AltChars: "+h.k0affggolpnqprrss", + EnterAcs: "\x1bF", + ExitAcs: "\x1bG", + SetCursor: "\x1bY%p1%' '%+%c%p2%' '%+%c", + CursorBack1: "\x1bD", + CursorUp1: "\x1bA", + KeyUp: "\x1bA", + KeyDown: "\x1bB", + KeyRight: "\x1bC", + KeyLeft: "\x1bD", + KeyBackspace: "\b", + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/w/wy50/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/w/wy50/term.go new file mode 100644 index 0000000000..beced62d5c --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/w/wy50/term.go @@ -0,0 +1,60 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package wy50 + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // Wyse 50 + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "wy50", + Aliases: []string{"wyse50"}, + Columns: 80, + Lines: 24, + Bell: "\a", + Clear: "\x1b+$<20>", + ShowCursor: "\x1b`1", + HideCursor: "\x1b`0", + AttrOff: "\x1b(\x1bH\x03", + Dim: "\x1b`7\x1b)", + Reverse: "\x1b`6\x1b)", + PadChar: "\x00", + AltChars: "a;j5k3l2m1n8q:t4u9v=w0x6", + EnterAcs: "\x1bH\x02", + ExitAcs: "\x1bH\x03", + SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c", + CursorBack1: "\b", + CursorUp1: "\v", + KeyUp: "\v", + KeyDown: "\n", + KeyRight: "\f", + KeyLeft: "\b", + KeyInsert: "\x1bQ", + KeyDelete: "\x1bW", + KeyBackspace: "\b", + KeyHome: "\x1e", + KeyPgUp: "\x1bJ", + KeyPgDn: "\x1bK", + KeyF1: "\x01@\r", + KeyF2: "\x01A\r", + KeyF3: "\x01B\r", + KeyF4: "\x01C\r", + KeyF5: "\x01D\r", + KeyF6: "\x01E\r", + KeyF7: "\x01F\r", + KeyF8: "\x01G\r", + KeyF9: "\x01H\r", + KeyF10: "\x01I\r", + KeyF11: "\x01J\r", + KeyF12: "\x01K\r", + KeyF13: "\x01L\r", + KeyF14: "\x01M\r", + KeyF15: "\x01N\r", + KeyF16: "\x01O\r", + KeyPrint: "\x1bP", + KeyBacktab: "\x1bI", + KeyShfHome: "\x1b{", + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/w/wy60/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/w/wy60/term.go new file mode 100644 index 0000000000..5b79310a77 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/w/wy60/term.go @@ -0,0 +1,64 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package wy60 + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // Wyse 60 + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "wy60", + Aliases: []string{"wyse60"}, + Columns: 80, + Lines: 24, + Bell: "\a", + Clear: "\x1b+$<100>", + EnterCA: "\x1bw0", + ExitCA: "\x1bw1", + ShowCursor: "\x1b`1", + HideCursor: "\x1b`0", + AttrOff: "\x1b(\x1bH\x03\x1bG0\x1bcD", + Underline: "\x1bG8", + Dim: "\x1bGp", + Blink: "\x1bG2", + Reverse: "\x1bG4", + PadChar: "\x00", + AltChars: "+/,.0[a2fxgqh1ihjYk?lZm@nEqDtCu4vAwBx3yszr{c~~", + EnterAcs: "\x1bcE", + ExitAcs: "\x1bcD", + SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c", + CursorBack1: "\b", + CursorUp1: "\v", + KeyUp: "\v", + KeyDown: "\n", + KeyRight: "\f", + KeyLeft: "\b", + KeyInsert: "\x1bQ", + KeyDelete: "\x1bW", + KeyBackspace: "\b", + KeyHome: "\x1e", + KeyPgUp: "\x1bJ", + KeyPgDn: "\x1bK", + KeyF1: "\x01@\r", + KeyF2: "\x01A\r", + KeyF3: "\x01B\r", + KeyF4: "\x01C\r", + KeyF5: "\x01D\r", + KeyF6: "\x01E\r", + KeyF7: "\x01F\r", + KeyF8: "\x01G\r", + KeyF9: "\x01H\r", + KeyF10: "\x01I\r", + KeyF11: "\x01J\r", + KeyF12: "\x01K\r", + KeyF13: "\x01L\r", + KeyF14: "\x01M\r", + KeyF15: "\x01N\r", + KeyF16: "\x01O\r", + KeyPrint: "\x1bP", + KeyBacktab: "\x1bI", + KeyShfHome: "\x1b{", + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/w/wy99_ansi/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/w/wy99_ansi/term.go new file mode 100644 index 0000000000..af470e46f3 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/w/wy99_ansi/term.go @@ -0,0 +1,116 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package wy99_ansi + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // Wyse WY-99GT in ansi mode (int'l PC keyboard) + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "wy99-ansi", + Columns: 80, + Lines: 25, + Bell: "\a", + Clear: "\x1b[H\x1b[J$<200>", + ShowCursor: "\x1b[34h\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[m\x0f\x1b[\"q", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h", + ExitKeypad: "\x1b[?1l", + PadChar: "\x00", + AltChars: "``aaffggjjkkllmmnnooqqssttuuvvwwxx{{||}}~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b)0", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b$<1>", + CursorUp1: "\x1bM", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyBackspace: "\b", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[M", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyF17: "\x1b[K", + KeyF18: "\x1b[31~", + KeyF19: "\x1b[32~", + KeyF20: "\x1b[33~", + KeyF21: "\x1b[34~", + KeyF22: "\x1b[35~", + KeyF23: "\x1b[1~", + KeyF24: "\x1b[2~", + KeyBacktab: "\x1b[z", + AutoMargin: true, + }) + + // Wyse WY-99GT in ansi mode (US PC keyboard) + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "wy99a-ansi", + Columns: 80, + Lines: 25, + Bell: "\a", + Clear: "\x1b[H\x1b[J$<200>", + ShowCursor: "\x1b[34h\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[m\x0f\x1b[\"q", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h", + ExitKeypad: "\x1b[?1l", + PadChar: "\x00", + AltChars: "``aaffggjjkkllmmnnooqqssttuuvvwwxx{{||}}~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b)0", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b$<1>", + CursorUp1: "\x1bM", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyBackspace: "\b", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[M", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyF17: "\x1b[K", + KeyF18: "\x1b[31~", + KeyF19: "\x1b[32~", + KeyF20: "\x1b[33~", + KeyF21: "\x1b[34~", + KeyF22: "\x1b[35~", + KeyF23: "\x1b[1~", + KeyF24: "\x1b[2~", + KeyBacktab: "\x1b[z", + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/x/xfce/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/x/xfce/term.go new file mode 100644 index 0000000000..d70b2e910c --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/x/xfce/term.go @@ -0,0 +1,67 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package xfce + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // Xfce Terminal + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "xfce", + Columns: 80, + Lines: 24, + Colors: 8, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b7\x1b[?47h", + ExitCA: "\x1b[2J\x1b[?47l\x1b8", + ShowCursor: "\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b[0m\x0f", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[3%p1%dm", + SetBg: "\x1b[4%p1%dm", + SetFgBg: "\x1b[3%p1%d;4%p2%dm", + ResetFgBg: "\x1b[39;49m", + PadChar: "\x00", + AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x0e", + ExitAcs: "\x0f", + EnableAcs: "\x1b)0", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1bOH", + KeyEnd: "\x1bOF", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/x/xterm/direct.go b/vendor/github.com/gdamore/tcell/v2/terminfo/x/xterm/direct.go new file mode 100644 index 0000000000..358ebae91c --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/x/xterm/direct.go @@ -0,0 +1,92 @@ +// Copyright 2021 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +// This terminal definition is derived from the xterm-256color definition, but +// makes use of the RGB property these terminals have to support direct color. +// The terminfo entry for this uses a new format for the color handling introduced +// by ncurses 6.1 (and used by nobody else), so this override ensures we get +// good handling even in the face of this. + +package xterm + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // derived from xterm-256color, but adds full RGB support + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "xterm-direct", + Aliases: []string{"xterm-truecolor"}, + Columns: 80, + Lines: 24, + Colors: 256, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b[?1049h\x1b[22;0;0t", + ExitCA: "\x1b[?1049l\x1b[23;0;0t", + ShowCursor: "\x1b[?12l\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b(B\x1b[m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", + SetFgRGB: "\x1b[38;2;%p1%d;%p2%d;%p3%dm", + SetBgRGB: "\x1b[48;2;%p1%d;%p2%d;%p3%dm", + SetFgBgRGB: "\x1b[38;2;%p1%d;%p2%d;%p3%d;48;2;%p4%d;%p5%d;%p6%dm", + ResetFgBg: "\x1b[39;49m", + AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + StrikeThrough: "\x1b[9m", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1bOH", + KeyEnd: "\x1bOF", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + AutoMargin: true, + TrueColor: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/x/xterm/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/x/xterm/term.go new file mode 100644 index 0000000000..daa19c3f17 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/x/xterm/term.go @@ -0,0 +1,192 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package xterm + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // X11 terminal emulator + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "xterm", + Aliases: []string{"xterm-debian"}, + Columns: 80, + Lines: 24, + Colors: 8, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b[?1049h\x1b[22;0;0t", + ExitCA: "\x1b[?1049l\x1b[23;0;0t", + ShowCursor: "\x1b[?12l\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b(B\x1b[m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[3%p1%dm", + SetBg: "\x1b[4%p1%dm", + SetFgBg: "\x1b[3%p1%d;4%p2%dm", + ResetFgBg: "\x1b[39;49m", + AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + StrikeThrough: "\x1b[9m", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1bOH", + KeyEnd: "\x1bOF", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + AutoMargin: true, + }) + + // xterm with 88 colors + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "xterm-88color", + Columns: 80, + Lines: 24, + Colors: 88, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b[?1049h\x1b[22;0;0t", + ExitCA: "\x1b[?1049l\x1b[23;0;0t", + ShowCursor: "\x1b[?12l\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b(B\x1b[m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", + ResetFgBg: "\x1b[39;49m", + AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + StrikeThrough: "\x1b[9m", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1bOH", + KeyEnd: "\x1bOF", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + AutoMargin: true, + }) + + // xterm with 256 colors + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "xterm-256color", + Columns: 80, + Lines: 24, + Colors: 256, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b[?1049h\x1b[22;0;0t", + ExitCA: "\x1b[?1049l\x1b[23;0;0t", + ShowCursor: "\x1b[?12l\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b(B\x1b[m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", + ResetFgBg: "\x1b[39;49m", + AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + StrikeThrough: "\x1b[9m", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1bOH", + KeyEnd: "\x1bOF", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/x/xterm_kitty/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/x/xterm_kitty/term.go new file mode 100644 index 0000000000..ab50003e05 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/x/xterm_kitty/term.go @@ -0,0 +1,69 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package xterm_kitty + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // KovIdTTY + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "xterm-kitty", + Columns: 80, + Lines: 24, + Colors: 256, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b[?1049h", + ExitCA: "\x1b[?1049l", + ShowCursor: "\x1b[?12l\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b(B\x1b[m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h", + ExitKeypad: "\x1b[?1l", + SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", + ResetFgBg: "\x1b[39;49m", + AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + StrikeThrough: "\x1b[9m", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1bOH", + KeyEnd: "\x1bOF", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + TrueColor: true, + AutoMargin: true, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/x/xterm_termite/term.go b/vendor/github.com/gdamore/tcell/v2/terminfo/x/xterm_termite/term.go new file mode 100644 index 0000000000..f2d0221014 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/x/xterm_termite/term.go @@ -0,0 +1,66 @@ +// Generated automatically. DO NOT HAND-EDIT. + +package xterm_termite + +import "github.com/gdamore/tcell/v2/terminfo" + +func init() { + + // VTE-based terminal + terminfo.AddTerminfo(&terminfo.Terminfo{ + Name: "xterm-termite", + Columns: 80, + Lines: 24, + Colors: 256, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b[?1049h", + ExitCA: "\x1b[?1049l", + ShowCursor: "\x1b[?12l\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b(B\x1b[m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", + SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", + SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", + ResetFgBg: "\x1b[39;49m", + AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1bOH", + KeyEnd: "\x1bOF", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + Modifiers: 1, + }) +} diff --git a/vendor/github.com/gdamore/tcell/v2/terms_default.go b/vendor/github.com/gdamore/tcell/v2/terms_default.go new file mode 100644 index 0000000000..4ce763702c --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terms_default.go @@ -0,0 +1,23 @@ +// +build !tcell_minimal + +// Copyright 2019 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + // This imports the default terminal entries. To disable, use the + // tcell_minimal build tag. + _ "github.com/gdamore/tcell/v2/terminfo/extended" +) diff --git a/vendor/github.com/gdamore/tcell/v2/terms_dynamic.go b/vendor/github.com/gdamore/tcell/v2/terms_dynamic.go new file mode 100644 index 0000000000..8f0994a86d --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terms_dynamic.go @@ -0,0 +1,37 @@ +// +build !tcell_minimal,!nacl,!js,!zos,!plan9,!windows,!android + +// Copyright 2019 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + // This imports a dynamic version of the terminal database, which + // is built using infocmp. This relies on a working installation + // of infocmp (typically supplied with ncurses). We only do this + // for systems likely to have that -- i.e. UNIX based hosts. We + // also don't support Android here, because you really don't want + // to run external programs there. Generally the android terminals + // will be automatically included anyway. + "github.com/gdamore/tcell/v2/terminfo" + "github.com/gdamore/tcell/v2/terminfo/dynamic" +) + +func loadDynamicTerminfo(term string) (*terminfo.Terminfo, error) { + ti, _, e := dynamic.LoadTerminfo(term) + if e != nil { + return nil, e + } + return ti, nil +} diff --git a/vendor/github.com/gdamore/tcell/v2/terms_static.go b/vendor/github.com/gdamore/tcell/v2/terms_static.go new file mode 100644 index 0000000000..8b3fe12959 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/terms_static.go @@ -0,0 +1,27 @@ +// +build tcell_minimal nacl js zos plan9 windows android + +// Copyright 2019 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + "errors" + + "github.com/gdamore/tcell/v2/terminfo" +) + +func loadDynamicTerminfo(_ string) (*terminfo.Terminfo, error) { + return nil, errors.New("terminal type unsupported") +} diff --git a/vendor/github.com/gdamore/tcell/v2/tscreen.go b/vendor/github.com/gdamore/tcell/v2/tscreen.go new file mode 100644 index 0000000000..ebd61a2588 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/tscreen.go @@ -0,0 +1,1727 @@ +// Copyright 2021 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import ( + "bytes" + "errors" + "io" + "os" + "strconv" + "strings" + "sync" + "time" + "unicode/utf8" + + "golang.org/x/term" + "golang.org/x/text/transform" + + "github.com/gdamore/tcell/v2/terminfo" + + // import the stock terminals + _ "github.com/gdamore/tcell/v2/terminfo/base" +) + +// NewTerminfoScreen returns a Screen that uses the stock TTY interface +// and POSIX terminal control, combined with a terminfo description taken from +// the $TERM environment variable. It returns an error if the terminal +// is not supported for any reason. +// +// For terminals that do not support dynamic resize events, the $LINES +// $COLUMNS environment variables can be set to the actual window size, +// otherwise defaults taken from the terminal database are used. +func NewTerminfoScreen() (Screen, error) { + return NewTerminfoScreenFromTty(nil) +} + +// NewTerminfoScreenFromTty returns a Screen using a custom Tty implementation. +// If the passed in tty is nil, then a reasonable default (typically /dev/tty) +// is presumed, at least on UNIX hosts. (Windows hosts will typically fail this +// call altogether.) +func NewTerminfoScreenFromTty(tty Tty) (Screen, error) { + ti, e := terminfo.LookupTerminfo(os.Getenv("TERM")) + if e != nil { + ti, e = loadDynamicTerminfo(os.Getenv("TERM")) + if e != nil { + return nil, e + } + terminfo.AddTerminfo(ti) + } + t := &tScreen{ti: ti, tty: tty} + + t.keyexist = make(map[Key]bool) + t.keycodes = make(map[string]*tKeyCode) + if len(ti.Mouse) > 0 { + t.mouse = []byte(ti.Mouse) + } + t.prepareKeys() + t.buildAcsMap() + t.resizeQ = make(chan bool, 1) + t.fallback = make(map[rune]string) + for k, v := range RuneFallbacks { + t.fallback[k] = v + } + + return t, nil +} + +// tKeyCode represents a combination of a key code and modifiers. +type tKeyCode struct { + key Key + mod ModMask +} + +// tScreen represents a screen backed by a terminfo implementation. +type tScreen struct { + ti *terminfo.Terminfo + tty Tty + h int + w int + fini bool + cells CellBuffer + buffering bool // true if we are collecting writes to buf instead of sending directly to out + buf bytes.Buffer + curstyle Style + style Style + evch chan Event + resizeQ chan bool + quit chan struct{} + keyexist map[Key]bool + keycodes map[string]*tKeyCode + keychan chan []byte + keytimer *time.Timer + keyexpire time.Time + cx int + cy int + mouse []byte + clear bool + cursorx int + cursory int + wasbtn bool + acs map[rune]string + charset string + encoder transform.Transformer + decoder transform.Transformer + fallback map[rune]string + colors map[Color]Color + palette []Color + truecolor bool + escaped bool + buttondn bool + finiOnce sync.Once + enablePaste string + disablePaste string + saved *term.State + stopQ chan struct{} + running bool + wg sync.WaitGroup + mouseFlags MouseFlags + pasteEnabled bool + + sync.Mutex +} + +func (t *tScreen) Init() error { + if e := t.initialize(); e != nil { + return e + } + + t.evch = make(chan Event, 10) + t.keychan = make(chan []byte, 10) + t.keytimer = time.NewTimer(time.Millisecond * 50) + t.charset = "UTF-8" + + t.charset = getCharset() + if enc := GetEncoding(t.charset); enc != nil { + t.encoder = enc.NewEncoder() + t.decoder = enc.NewDecoder() + } else { + return ErrNoCharset + } + ti := t.ti + + // environment overrides + w := ti.Columns + h := ti.Lines + if i, _ := strconv.Atoi(os.Getenv("LINES")); i != 0 { + h = i + } + if i, _ := strconv.Atoi(os.Getenv("COLUMNS")); i != 0 { + w = i + } + if t.ti.SetFgBgRGB != "" || t.ti.SetFgRGB != "" || t.ti.SetBgRGB != "" { + t.truecolor = true + } + // A user who wants to have his themes honored can + // set this environment variable. + if os.Getenv("TCELL_TRUECOLOR") == "disable" { + t.truecolor = false + } + t.colors = make(map[Color]Color) + t.palette = make([]Color, t.nColors()) + for i := 0; i < t.nColors(); i++ { + t.palette[i] = Color(i) | ColorValid + // identity map for our builtin colors + t.colors[Color(i)|ColorValid] = Color(i) | ColorValid + } + + t.quit = make(chan struct{}) + + t.Lock() + t.cx = -1 + t.cy = -1 + t.style = StyleDefault + t.cells.Resize(w, h) + t.cursorx = -1 + t.cursory = -1 + t.resize() + t.Unlock() + + if err := t.engage(); err != nil { + return err + } + + return nil +} + +func (t *tScreen) prepareKeyMod(key Key, mod ModMask, val string) { + if val != "" { + // Do not override codes that already exist + if _, exist := t.keycodes[val]; !exist { + t.keyexist[key] = true + t.keycodes[val] = &tKeyCode{key: key, mod: mod} + } + } +} + +func (t *tScreen) prepareKeyModReplace(key Key, replace Key, mod ModMask, val string) { + if val != "" { + // Do not override codes that already exist + if old, exist := t.keycodes[val]; !exist || old.key == replace { + t.keyexist[key] = true + t.keycodes[val] = &tKeyCode{key: key, mod: mod} + } + } +} + +func (t *tScreen) prepareKeyModXTerm(key Key, val string) { + + if strings.HasPrefix(val, "\x1b[") && strings.HasSuffix(val, "~") { + + // Drop the trailing ~ + val = val[:len(val)-1] + + // These suffixes are calculated assuming Xterm style modifier suffixes. + // Please see https://invisible-island.net/xterm/ctlseqs/ctlseqs.pdf for + // more information (specifically "PC-Style Function Keys"). + t.prepareKeyModReplace(key, key+12, ModShift, val+";2~") + t.prepareKeyModReplace(key, key+48, ModAlt, val+";3~") + t.prepareKeyModReplace(key, key+60, ModAlt|ModShift, val+";4~") + t.prepareKeyModReplace(key, key+24, ModCtrl, val+";5~") + t.prepareKeyModReplace(key, key+36, ModCtrl|ModShift, val+";6~") + t.prepareKeyMod(key, ModAlt|ModCtrl, val+";7~") + t.prepareKeyMod(key, ModShift|ModAlt|ModCtrl, val+";8~") + t.prepareKeyMod(key, ModMeta, val+";9~") + t.prepareKeyMod(key, ModMeta|ModShift, val+";10~") + t.prepareKeyMod(key, ModMeta|ModAlt, val+";11~") + t.prepareKeyMod(key, ModMeta|ModAlt|ModShift, val+";12~") + t.prepareKeyMod(key, ModMeta|ModCtrl, val+";13~") + t.prepareKeyMod(key, ModMeta|ModCtrl|ModShift, val+";14~") + t.prepareKeyMod(key, ModMeta|ModCtrl|ModAlt, val+";15~") + t.prepareKeyMod(key, ModMeta|ModCtrl|ModAlt|ModShift, val+";16~") + } else if strings.HasPrefix(val, "\x1bO") && len(val) == 3 { + val = val[2:] + t.prepareKeyModReplace(key, key+12, ModShift, "\x1b[1;2"+val) + t.prepareKeyModReplace(key, key+48, ModAlt, "\x1b[1;3"+val) + t.prepareKeyModReplace(key, key+24, ModCtrl, "\x1b[1;5"+val) + t.prepareKeyModReplace(key, key+36, ModCtrl|ModShift, "\x1b[1;6"+val) + t.prepareKeyModReplace(key, key+60, ModAlt|ModShift, "\x1b[1;4"+val) + t.prepareKeyMod(key, ModAlt|ModCtrl, "\x1b[1;7"+val) + t.prepareKeyMod(key, ModShift|ModAlt|ModCtrl, "\x1b[1;8"+val) + t.prepareKeyMod(key, ModMeta, "\x1b[1;9"+val) + t.prepareKeyMod(key, ModMeta|ModShift, "\x1b[1;10"+val) + t.prepareKeyMod(key, ModMeta|ModAlt, "\x1b[1;11"+val) + t.prepareKeyMod(key, ModMeta|ModAlt|ModShift, "\x1b[1;12"+val) + t.prepareKeyMod(key, ModMeta|ModCtrl, "\x1b[1;13"+val) + t.prepareKeyMod(key, ModMeta|ModCtrl|ModShift, "\x1b[1;14"+val) + t.prepareKeyMod(key, ModMeta|ModCtrl|ModAlt, "\x1b[1;15"+val) + t.prepareKeyMod(key, ModMeta|ModCtrl|ModAlt|ModShift, "\x1b[1;16"+val) + } +} + +func (t *tScreen) prepareXtermModifiers() { + if t.ti.Modifiers != terminfo.ModifiersXTerm { + return + } + t.prepareKeyModXTerm(KeyRight, t.ti.KeyRight) + t.prepareKeyModXTerm(KeyLeft, t.ti.KeyLeft) + t.prepareKeyModXTerm(KeyUp, t.ti.KeyUp) + t.prepareKeyModXTerm(KeyDown, t.ti.KeyDown) + t.prepareKeyModXTerm(KeyInsert, t.ti.KeyInsert) + t.prepareKeyModXTerm(KeyDelete, t.ti.KeyDelete) + t.prepareKeyModXTerm(KeyPgUp, t.ti.KeyPgUp) + t.prepareKeyModXTerm(KeyPgDn, t.ti.KeyPgDn) + t.prepareKeyModXTerm(KeyHome, t.ti.KeyHome) + t.prepareKeyModXTerm(KeyEnd, t.ti.KeyEnd) + t.prepareKeyModXTerm(KeyF1, t.ti.KeyF1) + t.prepareKeyModXTerm(KeyF2, t.ti.KeyF2) + t.prepareKeyModXTerm(KeyF3, t.ti.KeyF3) + t.prepareKeyModXTerm(KeyF4, t.ti.KeyF4) + t.prepareKeyModXTerm(KeyF5, t.ti.KeyF5) + t.prepareKeyModXTerm(KeyF6, t.ti.KeyF6) + t.prepareKeyModXTerm(KeyF7, t.ti.KeyF7) + t.prepareKeyModXTerm(KeyF8, t.ti.KeyF8) + t.prepareKeyModXTerm(KeyF9, t.ti.KeyF9) + t.prepareKeyModXTerm(KeyF10, t.ti.KeyF10) + t.prepareKeyModXTerm(KeyF11, t.ti.KeyF11) + t.prepareKeyModXTerm(KeyF12, t.ti.KeyF12) +} + +func (t *tScreen) prepareBracketedPaste() { + // Another workaround for lack of reporting in terminfo. + // We assume if the terminal has a mouse entry, that it + // offers bracketed paste. But we allow specific overrides + // via our terminal database. + if t.ti.EnablePaste != "" { + t.enablePaste = t.ti.EnablePaste + t.disablePaste = t.ti.DisablePaste + t.prepareKey(keyPasteStart, t.ti.PasteStart) + t.prepareKey(keyPasteEnd, t.ti.PasteEnd) + } else if t.ti.Mouse != "" { + t.enablePaste = "\x1b[?2004h" + t.disablePaste = "\x1b[?2004l" + t.prepareKey(keyPasteStart, "\x1b[200~") + t.prepareKey(keyPasteEnd, "\x1b[201~") + } +} + +func (t *tScreen) prepareKey(key Key, val string) { + t.prepareKeyMod(key, ModNone, val) +} + +func (t *tScreen) prepareKeys() { + ti := t.ti + t.prepareKey(KeyBackspace, ti.KeyBackspace) + t.prepareKey(KeyF1, ti.KeyF1) + t.prepareKey(KeyF2, ti.KeyF2) + t.prepareKey(KeyF3, ti.KeyF3) + t.prepareKey(KeyF4, ti.KeyF4) + t.prepareKey(KeyF5, ti.KeyF5) + t.prepareKey(KeyF6, ti.KeyF6) + t.prepareKey(KeyF7, ti.KeyF7) + t.prepareKey(KeyF8, ti.KeyF8) + t.prepareKey(KeyF9, ti.KeyF9) + t.prepareKey(KeyF10, ti.KeyF10) + t.prepareKey(KeyF11, ti.KeyF11) + t.prepareKey(KeyF12, ti.KeyF12) + t.prepareKey(KeyF13, ti.KeyF13) + t.prepareKey(KeyF14, ti.KeyF14) + t.prepareKey(KeyF15, ti.KeyF15) + t.prepareKey(KeyF16, ti.KeyF16) + t.prepareKey(KeyF17, ti.KeyF17) + t.prepareKey(KeyF18, ti.KeyF18) + t.prepareKey(KeyF19, ti.KeyF19) + t.prepareKey(KeyF20, ti.KeyF20) + t.prepareKey(KeyF21, ti.KeyF21) + t.prepareKey(KeyF22, ti.KeyF22) + t.prepareKey(KeyF23, ti.KeyF23) + t.prepareKey(KeyF24, ti.KeyF24) + t.prepareKey(KeyF25, ti.KeyF25) + t.prepareKey(KeyF26, ti.KeyF26) + t.prepareKey(KeyF27, ti.KeyF27) + t.prepareKey(KeyF28, ti.KeyF28) + t.prepareKey(KeyF29, ti.KeyF29) + t.prepareKey(KeyF30, ti.KeyF30) + t.prepareKey(KeyF31, ti.KeyF31) + t.prepareKey(KeyF32, ti.KeyF32) + t.prepareKey(KeyF33, ti.KeyF33) + t.prepareKey(KeyF34, ti.KeyF34) + t.prepareKey(KeyF35, ti.KeyF35) + t.prepareKey(KeyF36, ti.KeyF36) + t.prepareKey(KeyF37, ti.KeyF37) + t.prepareKey(KeyF38, ti.KeyF38) + t.prepareKey(KeyF39, ti.KeyF39) + t.prepareKey(KeyF40, ti.KeyF40) + t.prepareKey(KeyF41, ti.KeyF41) + t.prepareKey(KeyF42, ti.KeyF42) + t.prepareKey(KeyF43, ti.KeyF43) + t.prepareKey(KeyF44, ti.KeyF44) + t.prepareKey(KeyF45, ti.KeyF45) + t.prepareKey(KeyF46, ti.KeyF46) + t.prepareKey(KeyF47, ti.KeyF47) + t.prepareKey(KeyF48, ti.KeyF48) + t.prepareKey(KeyF49, ti.KeyF49) + t.prepareKey(KeyF50, ti.KeyF50) + t.prepareKey(KeyF51, ti.KeyF51) + t.prepareKey(KeyF52, ti.KeyF52) + t.prepareKey(KeyF53, ti.KeyF53) + t.prepareKey(KeyF54, ti.KeyF54) + t.prepareKey(KeyF55, ti.KeyF55) + t.prepareKey(KeyF56, ti.KeyF56) + t.prepareKey(KeyF57, ti.KeyF57) + t.prepareKey(KeyF58, ti.KeyF58) + t.prepareKey(KeyF59, ti.KeyF59) + t.prepareKey(KeyF60, ti.KeyF60) + t.prepareKey(KeyF61, ti.KeyF61) + t.prepareKey(KeyF62, ti.KeyF62) + t.prepareKey(KeyF63, ti.KeyF63) + t.prepareKey(KeyF64, ti.KeyF64) + t.prepareKey(KeyInsert, ti.KeyInsert) + t.prepareKey(KeyDelete, ti.KeyDelete) + t.prepareKey(KeyHome, ti.KeyHome) + t.prepareKey(KeyEnd, ti.KeyEnd) + t.prepareKey(KeyUp, ti.KeyUp) + t.prepareKey(KeyDown, ti.KeyDown) + t.prepareKey(KeyLeft, ti.KeyLeft) + t.prepareKey(KeyRight, ti.KeyRight) + t.prepareKey(KeyPgUp, ti.KeyPgUp) + t.prepareKey(KeyPgDn, ti.KeyPgDn) + t.prepareKey(KeyHelp, ti.KeyHelp) + t.prepareKey(KeyPrint, ti.KeyPrint) + t.prepareKey(KeyCancel, ti.KeyCancel) + t.prepareKey(KeyExit, ti.KeyExit) + t.prepareKey(KeyBacktab, ti.KeyBacktab) + + t.prepareKeyMod(KeyRight, ModShift, ti.KeyShfRight) + t.prepareKeyMod(KeyLeft, ModShift, ti.KeyShfLeft) + t.prepareKeyMod(KeyUp, ModShift, ti.KeyShfUp) + t.prepareKeyMod(KeyDown, ModShift, ti.KeyShfDown) + t.prepareKeyMod(KeyHome, ModShift, ti.KeyShfHome) + t.prepareKeyMod(KeyEnd, ModShift, ti.KeyShfEnd) + t.prepareKeyMod(KeyPgUp, ModShift, ti.KeyShfPgUp) + t.prepareKeyMod(KeyPgDn, ModShift, ti.KeyShfPgDn) + + t.prepareKeyMod(KeyRight, ModCtrl, ti.KeyCtrlRight) + t.prepareKeyMod(KeyLeft, ModCtrl, ti.KeyCtrlLeft) + t.prepareKeyMod(KeyUp, ModCtrl, ti.KeyCtrlUp) + t.prepareKeyMod(KeyDown, ModCtrl, ti.KeyCtrlDown) + t.prepareKeyMod(KeyHome, ModCtrl, ti.KeyCtrlHome) + t.prepareKeyMod(KeyEnd, ModCtrl, ti.KeyCtrlEnd) + + // Sadly, xterm handling of keycodes is somewhat erratic. In + // particular, different codes are sent depending on application + // mode is in use or not, and the entries for many of these are + // simply absent from terminfo on many systems. So we insert + // a number of escape sequences if they are not already used, in + // order to have the widest correct usage. Note that prepareKey + // will not inject codes if the escape sequence is already known. + // We also only do this for terminals that have the application + // mode present. + + // Cursor mode + if ti.EnterKeypad != "" { + t.prepareKey(KeyUp, "\x1b[A") + t.prepareKey(KeyDown, "\x1b[B") + t.prepareKey(KeyRight, "\x1b[C") + t.prepareKey(KeyLeft, "\x1b[D") + t.prepareKey(KeyEnd, "\x1b[F") + t.prepareKey(KeyHome, "\x1b[H") + t.prepareKey(KeyDelete, "\x1b[3~") + t.prepareKey(KeyHome, "\x1b[1~") + t.prepareKey(KeyEnd, "\x1b[4~") + t.prepareKey(KeyPgUp, "\x1b[5~") + t.prepareKey(KeyPgDn, "\x1b[6~") + + // Application mode + t.prepareKey(KeyUp, "\x1bOA") + t.prepareKey(KeyDown, "\x1bOB") + t.prepareKey(KeyRight, "\x1bOC") + t.prepareKey(KeyLeft, "\x1bOD") + t.prepareKey(KeyHome, "\x1bOH") + } + + t.prepareKey(keyPasteStart, ti.PasteStart) + t.prepareKey(keyPasteEnd, ti.PasteEnd) + t.prepareXtermModifiers() + t.prepareBracketedPaste() + +outer: + // Add key mappings for control keys. + for i := 0; i < ' '; i++ { + // Do not insert direct key codes for ambiguous keys. + // For example, ESC is used for lots of other keys, so + // when parsing this we don't want to fast path handling + // of it, but instead wait a bit before parsing it as in + // isolation. + for esc := range t.keycodes { + if []byte(esc)[0] == byte(i) { + continue outer + } + } + + t.keyexist[Key(i)] = true + + mod := ModCtrl + switch Key(i) { + case KeyBS, KeyTAB, KeyESC, KeyCR: + // directly type-able- no control sequence + mod = ModNone + } + t.keycodes[string(rune(i))] = &tKeyCode{key: Key(i), mod: mod} + } +} + +func (t *tScreen) Fini() { + t.finiOnce.Do(t.finish) +} + +func (t *tScreen) finish() { + close(t.quit) + t.finalize() +} + +func (t *tScreen) SetStyle(style Style) { + t.Lock() + if !t.fini { + t.style = style + } + t.Unlock() +} + +func (t *tScreen) Clear() { + t.Fill(' ', t.style) +} + +func (t *tScreen) Fill(r rune, style Style) { + t.Lock() + if !t.fini { + t.cells.Fill(r, style) + } + t.Unlock() +} + +func (t *tScreen) SetContent(x, y int, mainc rune, combc []rune, style Style) { + t.Lock() + if !t.fini { + t.cells.SetContent(x, y, mainc, combc, style) + } + t.Unlock() +} + +func (t *tScreen) GetContent(x, y int) (rune, []rune, Style, int) { + t.Lock() + mainc, combc, style, width := t.cells.GetContent(x, y) + t.Unlock() + return mainc, combc, style, width +} + +func (t *tScreen) SetCell(x, y int, style Style, ch ...rune) { + if len(ch) > 0 { + t.SetContent(x, y, ch[0], ch[1:], style) + } else { + t.SetContent(x, y, ' ', nil, style) + } +} + +func (t *tScreen) encodeRune(r rune, buf []byte) []byte { + + nb := make([]byte, 6) + ob := make([]byte, 6) + num := utf8.EncodeRune(ob, r) + ob = ob[:num] + dst := 0 + var err error + if enc := t.encoder; enc != nil { + enc.Reset() + dst, _, err = enc.Transform(nb, ob, true) + } + if err != nil || dst == 0 || nb[0] == '\x1a' { + // Combining characters are elided + if len(buf) == 0 { + if acs, ok := t.acs[r]; ok { + buf = append(buf, []byte(acs)...) + } else if fb, ok := t.fallback[r]; ok { + buf = append(buf, []byte(fb)...) + } else { + buf = append(buf, '?') + } + } + } else { + buf = append(buf, nb[:dst]...) + } + + return buf +} + +func (t *tScreen) sendFgBg(fg Color, bg Color) { + ti := t.ti + if ti.Colors == 0 { + return + } + if fg == ColorReset || bg == ColorReset { + t.TPuts(ti.ResetFgBg) + } + if t.truecolor { + if ti.SetFgBgRGB != "" && fg.IsRGB() && bg.IsRGB() { + r1, g1, b1 := fg.RGB() + r2, g2, b2 := bg.RGB() + t.TPuts(ti.TParm(ti.SetFgBgRGB, + int(r1), int(g1), int(b1), + int(r2), int(g2), int(b2))) + return + } + + if fg.IsRGB() && ti.SetFgRGB != "" { + r, g, b := fg.RGB() + t.TPuts(ti.TParm(ti.SetFgRGB, int(r), int(g), int(b))) + fg = ColorDefault + } + + if bg.IsRGB() && ti.SetBgRGB != "" { + r, g, b := bg.RGB() + t.TPuts(ti.TParm(ti.SetBgRGB, + int(r), int(g), int(b))) + bg = ColorDefault + } + } + + if fg.Valid() { + if v, ok := t.colors[fg]; ok { + fg = v + } else { + v = FindColor(fg, t.palette) + t.colors[fg] = v + fg = v + } + } + + if bg.Valid() { + if v, ok := t.colors[bg]; ok { + bg = v + } else { + v = FindColor(bg, t.palette) + t.colors[bg] = v + bg = v + } + } + + if fg.Valid() && bg.Valid() && ti.SetFgBg != "" { + t.TPuts(ti.TParm(ti.SetFgBg, int(fg&0xff), int(bg&0xff))) + } else { + if fg.Valid() && ti.SetFg != "" { + t.TPuts(ti.TParm(ti.SetFg, int(fg&0xff))) + } + if bg.Valid() && ti.SetBg != "" { + t.TPuts(ti.TParm(ti.SetBg, int(bg&0xff))) + } + } +} + +func (t *tScreen) drawCell(x, y int) int { + + ti := t.ti + + mainc, combc, style, width := t.cells.GetContent(x, y) + if !t.cells.Dirty(x, y) { + return width + } + + if y == t.h-1 && x == t.w-1 && t.ti.AutoMargin && ti.InsertChar != "" { + // our solution is somewhat goofy. + // we write to the second to the last cell what we want in the last cell, then we + // insert a character at that 2nd to last position to shift the last column into + // place, then we rewrite that 2nd to last cell. Old terminals suck. + t.TPuts(ti.TGoto(x-1, y)) + defer func() { + t.TPuts(ti.TGoto(x-1, y)) + t.TPuts(ti.InsertChar) + t.cy = y + t.cx = x - 1 + t.cells.SetDirty(x-1, y, true) + _ = t.drawCell(x-1, y) + t.TPuts(t.ti.TGoto(0, 0)) + t.cy = 0 + t.cx = 0 + }() + } else if t.cy != y || t.cx != x { + t.TPuts(ti.TGoto(x, y)) + t.cx = x + t.cy = y + } + + if style == StyleDefault { + style = t.style + } + if style != t.curstyle { + fg, bg, attrs := style.Decompose() + + t.TPuts(ti.AttrOff) + + t.sendFgBg(fg, bg) + if attrs&AttrBold != 0 { + t.TPuts(ti.Bold) + } + if attrs&AttrUnderline != 0 { + t.TPuts(ti.Underline) + } + if attrs&AttrReverse != 0 { + t.TPuts(ti.Reverse) + } + if attrs&AttrBlink != 0 { + t.TPuts(ti.Blink) + } + if attrs&AttrDim != 0 { + t.TPuts(ti.Dim) + } + if attrs&AttrItalic != 0 { + t.TPuts(ti.Italic) + } + if attrs&AttrStrikeThrough != 0 { + t.TPuts(ti.StrikeThrough) + } + t.curstyle = style + } + // now emit runes - taking care to not overrun width with a + // wide character, and to ensure that we emit exactly one regular + // character followed up by any residual combing characters + + if width < 1 { + width = 1 + } + + var str string + + buf := make([]byte, 0, 6) + + buf = t.encodeRune(mainc, buf) + for _, r := range combc { + buf = t.encodeRune(r, buf) + } + + str = string(buf) + if width > 1 && str == "?" { + // No FullWidth character support + str = "? " + t.cx = -1 + } + + if x > t.w-width { + // too wide to fit; emit a single space instead + width = 1 + str = " " + } + t.writeString(str) + t.cx += width + t.cells.SetDirty(x, y, false) + if width > 1 { + t.cx = -1 + } + + return width +} + +func (t *tScreen) ShowCursor(x, y int) { + t.Lock() + t.cursorx = x + t.cursory = y + t.Unlock() +} + +func (t *tScreen) HideCursor() { + t.ShowCursor(-1, -1) +} + +func (t *tScreen) showCursor() { + + x, y := t.cursorx, t.cursory + w, h := t.cells.Size() + if x < 0 || y < 0 || x >= w || y >= h { + t.hideCursor() + return + } + t.TPuts(t.ti.TGoto(x, y)) + t.TPuts(t.ti.ShowCursor) + t.cx = x + t.cy = y +} + +// writeString sends a string to the terminal. The string is sent as-is and +// this function does not expand inline padding indications (of the form +// $<[delay]> where [delay] is msec). In order to have these expanded, use +// TPuts. If the screen is "buffering", the string is collected in a buffer, +// with the intention that the entire buffer be sent to the terminal in one +// write operation at some point later. +func (t *tScreen) writeString(s string) { + if t.buffering { + _, _ = io.WriteString(&t.buf, s) + } else { + _, _ = io.WriteString(t.tty, s) + } +} + +func (t *tScreen) TPuts(s string) { + if t.buffering { + t.ti.TPuts(&t.buf, s) + } else { + t.ti.TPuts(t.tty, s) + } +} + +func (t *tScreen) Show() { + t.Lock() + if !t.fini { + t.resize() + t.draw() + } + t.Unlock() +} + +func (t *tScreen) clearScreen() { + fg, bg, _ := t.style.Decompose() + t.sendFgBg(fg, bg) + t.TPuts(t.ti.Clear) + t.clear = false +} + +func (t *tScreen) hideCursor() { + // does not update cursor position + if t.ti.HideCursor != "" { + t.TPuts(t.ti.HideCursor) + } else { + // No way to hide cursor, stick it + // at bottom right of screen + t.cx, t.cy = t.cells.Size() + t.TPuts(t.ti.TGoto(t.cx, t.cy)) + } +} + +func (t *tScreen) draw() { + // clobber cursor position, because we're gonna change it all + t.cx = -1 + t.cy = -1 + + t.buf.Reset() + t.buffering = true + defer func() { + t.buffering = false + }() + + // hide the cursor while we move stuff around + t.hideCursor() + + if t.clear { + t.clearScreen() + } + + for y := 0; y < t.h; y++ { + for x := 0; x < t.w; x++ { + width := t.drawCell(x, y) + if width > 1 { + if x+1 < t.w { + // this is necessary so that if we ever + // go back to drawing that cell, we + // actually will *draw* it. + t.cells.SetDirty(x+1, y, true) + } + } + x += width - 1 + } + } + + // restore the cursor + t.showCursor() + + _, _ = t.buf.WriteTo(t.tty) +} + +func (t *tScreen) EnableMouse(flags ...MouseFlags) { + var f MouseFlags + flagsPresent := false + for _, flag := range flags { + f |= flag + flagsPresent = true + } + if !flagsPresent { + f = MouseMotionEvents + } + + t.Lock() + t.mouseFlags = f + t.enableMouse(f) + t.Unlock() +} + +func (t *tScreen) enableMouse(f MouseFlags) { + // Rather than using terminfo to find mouse escape sequences, we rely on the fact that + // pretty much *every* terminal that supports mouse tracking follows the + // XTerm standards (the modern ones). + if len(t.mouse) != 0 { + // start by disabling all tracking. + t.TPuts("\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l") + if f&MouseMotionEvents != 0 { + t.TPuts("\x1b[?1003h\x1b[?1006h") + } else if f&MouseDragEvents != 0 { + t.TPuts("\x1b[?1002h\x1b[?1006h") + } else if f&MouseButtonEvents != 0 { + t.TPuts("\x1b[?1000h\x1b[?1006h") + } + } +} + +func (t *tScreen) DisableMouse() { + t.Lock() + t.mouseFlags = 0 + t.enableMouse(0) + t.Unlock() +} + +func (t *tScreen) EnablePaste() { + t.Lock() + t.pasteEnabled = true + t.enablePasting(true) + t.Unlock() +} + +func (t *tScreen) DisablePaste() { + t.Lock() + t.pasteEnabled = false + t.enablePasting(false) + t.Unlock() +} + +func (t *tScreen) enablePasting(on bool) { + var s string + if on { + s = t.enablePaste + } else { + s = t.disablePaste + } + if s != "" { + t.TPuts(s) + } +} + +func (t *tScreen) Size() (int, int) { + t.Lock() + w, h := t.w, t.h + t.Unlock() + return w, h +} + +func (t *tScreen) resize() { + if w, h, e := t.tty.WindowSize(); e == nil { + if w != t.w || h != t.h { + t.cx = -1 + t.cy = -1 + + t.cells.Resize(w, h) + t.cells.Invalidate() + t.h = h + t.w = w + ev := NewEventResize(w, h) + _ = t.PostEvent(ev) + } + } +} + +func (t *tScreen) Colors() int { + // this doesn't change, no need for lock + if t.truecolor { + return 1 << 24 + } + return t.ti.Colors +} + +// nColors returns the size of the built-in palette. +// This is distinct from Colors(), as it will generally +// always be a small number. (<= 256) +func (t *tScreen) nColors() int { + return t.ti.Colors +} + +func (t *tScreen) ChannelEvents(ch chan<- Event, quit <-chan struct{}) { + defer close(ch) + for { + select { + case <-quit: + return + case <-t.quit: + return + case ev := <-t.evch: + select { + case <-quit: + return + case <-t.quit: + return + case ch <- ev: + } + } + } +} + +func (t *tScreen) PollEvent() Event { + select { + case <-t.quit: + return nil + case ev := <-t.evch: + return ev + } +} + +func (t *tScreen) HasPendingEvent() bool { + return len(t.evch) > 0 +} + +// vtACSNames is a map of bytes defined by terminfo that are used in +// the terminals Alternate Character Set to represent other glyphs. +// For example, the upper left corner of the box drawing set can be +// displayed by printing "l" while in the alternate character set. +// Its not quite that simple, since the "l" is the terminfo name, +// and it may be necessary to use a different character based on +// the terminal implementation (or the terminal may lack support for +// this altogether). See buildAcsMap below for detail. +var vtACSNames = map[byte]rune{ + '+': RuneRArrow, + ',': RuneLArrow, + '-': RuneUArrow, + '.': RuneDArrow, + '0': RuneBlock, + '`': RuneDiamond, + 'a': RuneCkBoard, + 'b': '␉', // VT100, Not defined by terminfo + 'c': '␌', // VT100, Not defined by terminfo + 'd': '␋', // VT100, Not defined by terminfo + 'e': '␊', // VT100, Not defined by terminfo + 'f': RuneDegree, + 'g': RunePlMinus, + 'h': RuneBoard, + 'i': RuneLantern, + 'j': RuneLRCorner, + 'k': RuneURCorner, + 'l': RuneULCorner, + 'm': RuneLLCorner, + 'n': RunePlus, + 'o': RuneS1, + 'p': RuneS3, + 'q': RuneHLine, + 'r': RuneS7, + 's': RuneS9, + 't': RuneLTee, + 'u': RuneRTee, + 'v': RuneBTee, + 'w': RuneTTee, + 'x': RuneVLine, + 'y': RuneLEqual, + 'z': RuneGEqual, + '{': RunePi, + '|': RuneNEqual, + '}': RuneSterling, + '~': RuneBullet, +} + +// buildAcsMap builds a map of characters that we translate from Unicode to +// alternate character encodings. To do this, we use the standard VT100 ACS +// maps. This is only done if the terminal lacks support for Unicode; we +// always prefer to emit Unicode glyphs when we are able. +func (t *tScreen) buildAcsMap() { + acsstr := t.ti.AltChars + t.acs = make(map[rune]string) + for len(acsstr) > 2 { + srcv := acsstr[0] + dstv := string(acsstr[1]) + if r, ok := vtACSNames[srcv]; ok { + t.acs[r] = t.ti.EnterAcs + dstv + t.ti.ExitAcs + } + acsstr = acsstr[2:] + } +} + +func (t *tScreen) PostEventWait(ev Event) { + t.evch <- ev +} + +func (t *tScreen) PostEvent(ev Event) error { + select { + case t.evch <- ev: + return nil + default: + return ErrEventQFull + } +} + +func (t *tScreen) clip(x, y int) (int, int) { + w, h := t.cells.Size() + if x < 0 { + x = 0 + } + if y < 0 { + y = 0 + } + if x > w-1 { + x = w - 1 + } + if y > h-1 { + y = h - 1 + } + return x, y +} + +// buildMouseEvent returns an event based on the supplied coordinates and button +// state. Note that the screen's mouse button state is updated based on the +// input to this function (i.e. it mutates the receiver). +func (t *tScreen) buildMouseEvent(x, y, btn int) *EventMouse { + + // XTerm mouse events only report at most one button at a time, + // which may include a wheel button. Wheel motion events are + // reported as single impulses, while other button events are reported + // as separate press & release events. + + button := ButtonNone + mod := ModNone + + // Mouse wheel has bit 6 set, no release events. It should be noted + // that wheel events are sometimes misdelivered as mouse button events + // during a click-drag, so we debounce these, considering them to be + // button press events unless we see an intervening release event. + switch btn & 0x43 { + case 0: + button = Button1 + t.wasbtn = true + case 1: + button = Button3 // Note we prefer to treat right as button 2 + t.wasbtn = true + case 2: + button = Button2 // And the middle button as button 3 + t.wasbtn = true + case 3: + button = ButtonNone + t.wasbtn = false + case 0x40: + if !t.wasbtn { + button = WheelUp + } else { + button = Button1 + } + case 0x41: + if !t.wasbtn { + button = WheelDown + } else { + button = Button2 + } + } + + if btn&0x4 != 0 { + mod |= ModShift + } + if btn&0x8 != 0 { + mod |= ModAlt + } + if btn&0x10 != 0 { + mod |= ModCtrl + } + + // Some terminals will report mouse coordinates outside the + // screen, especially with click-drag events. Clip the coordinates + // to the screen in that case. + x, y = t.clip(x, y) + + return NewEventMouse(x, y, button, mod) +} + +// parseSgrMouse attempts to locate an SGR mouse record at the start of the +// buffer. It returns true, true if it found one, and the associated bytes +// be removed from the buffer. It returns true, false if the buffer might +// contain such an event, but more bytes are necessary (partial match), and +// false, false if the content is definitely *not* an SGR mouse record. +func (t *tScreen) parseSgrMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) { + + b := buf.Bytes() + + var x, y, btn, state int + dig := false + neg := false + motion := false + i := 0 + val := 0 + + for i = range b { + switch b[i] { + case '\x1b': + if state != 0 { + return false, false + } + state = 1 + + case '\x9b': + if state != 0 { + return false, false + } + state = 2 + + case '[': + if state != 1 { + return false, false + } + state = 2 + + case '<': + if state != 2 { + return false, false + } + val = 0 + dig = false + neg = false + state = 3 + + case '-': + if state != 3 && state != 4 && state != 5 { + return false, false + } + if dig || neg { + return false, false + } + neg = true // stay in state + + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + if state != 3 && state != 4 && state != 5 { + return false, false + } + val *= 10 + val += int(b[i] - '0') + dig = true // stay in state + + case ';': + if neg { + val = -val + } + switch state { + case 3: + btn, val = val, 0 + neg, dig, state = false, false, 4 + case 4: + x, val = val-1, 0 + neg, dig, state = false, false, 5 + default: + return false, false + } + + case 'm', 'M': + if state != 5 { + return false, false + } + if neg { + val = -val + } + y = val - 1 + + motion = (btn & 32) != 0 + btn &^= 32 + if b[i] == 'm' { + // mouse release, clear all buttons + btn |= 3 + btn &^= 0x40 + t.buttondn = false + } else if motion { + /* + * Some broken terminals appear to send + * mouse button one motion events, instead of + * encoding 35 (no buttons) into these events. + * We resolve these by looking for a non-motion + * event first. + */ + if !t.buttondn { + btn |= 3 + btn &^= 0x40 + } + } else { + t.buttondn = true + } + // consume the event bytes + for i >= 0 { + _, _ = buf.ReadByte() + i-- + } + *evs = append(*evs, t.buildMouseEvent(x, y, btn)) + return true, true + } + } + + // incomplete & inconclusive at this point + return true, false +} + +// parseXtermMouse is like parseSgrMouse, but it parses a legacy +// X11 mouse record. +func (t *tScreen) parseXtermMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) { + + b := buf.Bytes() + + state := 0 + btn := 0 + x := 0 + y := 0 + + for i := range b { + switch state { + case 0: + switch b[i] { + case '\x1b': + state = 1 + case '\x9b': + state = 2 + default: + return false, false + } + case 1: + if b[i] != '[' { + return false, false + } + state = 2 + case 2: + if b[i] != 'M' { + return false, false + } + state++ + case 3: + btn = int(b[i]) + state++ + case 4: + x = int(b[i]) - 32 - 1 + state++ + case 5: + y = int(b[i]) - 32 - 1 + for i >= 0 { + _, _ = buf.ReadByte() + i-- + } + *evs = append(*evs, t.buildMouseEvent(x, y, btn)) + return true, true + } + } + return true, false +} + +func (t *tScreen) parseFunctionKey(buf *bytes.Buffer, evs *[]Event) (bool, bool) { + b := buf.Bytes() + partial := false + for e, k := range t.keycodes { + esc := []byte(e) + if (len(esc) == 1) && (esc[0] == '\x1b') { + continue + } + if bytes.HasPrefix(b, esc) { + // matched + var r rune + if len(esc) == 1 { + r = rune(b[0]) + } + mod := k.mod + if t.escaped { + mod |= ModAlt + t.escaped = false + } + switch k.key { + case keyPasteStart: + *evs = append(*evs, NewEventPaste(true)) + case keyPasteEnd: + *evs = append(*evs, NewEventPaste(false)) + default: + *evs = append(*evs, NewEventKey(k.key, r, mod)) + } + for i := 0; i < len(esc); i++ { + _, _ = buf.ReadByte() + } + return true, true + } + if bytes.HasPrefix(esc, b) { + partial = true + } + } + return partial, false +} + +func (t *tScreen) parseRune(buf *bytes.Buffer, evs *[]Event) (bool, bool) { + b := buf.Bytes() + if b[0] >= ' ' && b[0] <= 0x7F { + // printable ASCII easy to deal with -- no encodings + mod := ModNone + if t.escaped { + mod = ModAlt + t.escaped = false + } + *evs = append(*evs, NewEventKey(KeyRune, rune(b[0]), mod)) + _, _ = buf.ReadByte() + return true, true + } + + if b[0] < 0x80 { + // Low numbered values are control keys, not runes. + return false, false + } + + utf := make([]byte, 12) + for l := 1; l <= len(b); l++ { + t.decoder.Reset() + nOut, nIn, e := t.decoder.Transform(utf, b[:l], true) + if e == transform.ErrShortSrc { + continue + } + if nOut != 0 { + r, _ := utf8.DecodeRune(utf[:nOut]) + if r != utf8.RuneError { + mod := ModNone + if t.escaped { + mod = ModAlt + t.escaped = false + } + *evs = append(*evs, NewEventKey(KeyRune, r, mod)) + } + for nIn > 0 { + _, _ = buf.ReadByte() + nIn-- + } + return true, true + } + } + // Looks like potential escape + return true, false +} + +func (t *tScreen) scanInput(buf *bytes.Buffer, expire bool) { + evs := t.collectEventsFromInput(buf, expire) + + for _, ev := range evs { + t.PostEventWait(ev) + } +} + +// Return an array of Events extracted from the supplied buffer. This is done +// while holding the screen's lock - the events can then be queued for +// application processing with the lock released. +func (t *tScreen) collectEventsFromInput(buf *bytes.Buffer, expire bool) []Event { + + res := make([]Event, 0, 20) + + t.Lock() + defer t.Unlock() + + for { + b := buf.Bytes() + if len(b) == 0 { + buf.Reset() + return res + } + + partials := 0 + + if part, comp := t.parseRune(buf, &res); comp { + continue + } else if part { + partials++ + } + + if part, comp := t.parseFunctionKey(buf, &res); comp { + continue + } else if part { + partials++ + } + + // Only parse mouse records if this term claims to have + // mouse support + + if t.ti.Mouse != "" { + if part, comp := t.parseXtermMouse(buf, &res); comp { + continue + } else if part { + partials++ + } + + if part, comp := t.parseSgrMouse(buf, &res); comp { + continue + } else if part { + partials++ + } + } + + if partials == 0 || expire { + if b[0] == '\x1b' { + if len(b) == 1 { + res = append(res, NewEventKey(KeyEsc, 0, ModNone)) + t.escaped = false + } else { + t.escaped = true + } + _, _ = buf.ReadByte() + continue + } + // Nothing was going to match, or we timed out + // waiting for more data -- just deliver the characters + // to the app & let them sort it out. Possibly we + // should only do this for control characters like ESC. + by, _ := buf.ReadByte() + mod := ModNone + if t.escaped { + t.escaped = false + mod = ModAlt + } + res = append(res, NewEventKey(KeyRune, rune(by), mod)) + continue + } + + // well we have some partial data, wait until we get + // some more + break + } + + return res +} + +func (t *tScreen) mainLoop(stopQ chan struct{}) { + defer t.wg.Done() + buf := &bytes.Buffer{} + for { + select { + case <-stopQ: + return + case <-t.quit: + return + case <-t.resizeQ: + t.Lock() + t.cx = -1 + t.cy = -1 + t.resize() + t.cells.Invalidate() + t.draw() + t.Unlock() + continue + case <-t.keytimer.C: + // If the timer fired, and the current time + // is after the expiration of the escape sequence, + // then we assume the escape sequence reached it's + // conclusion, and process the chunk independently. + // This lets us detect conflicts such as a lone ESC. + if buf.Len() > 0 { + if time.Now().After(t.keyexpire) { + t.scanInput(buf, true) + } + } + if buf.Len() > 0 { + if !t.keytimer.Stop() { + select { + case <-t.keytimer.C: + default: + } + } + t.keytimer.Reset(time.Millisecond * 50) + } + case chunk := <-t.keychan: + buf.Write(chunk) + t.keyexpire = time.Now().Add(time.Millisecond * 50) + t.scanInput(buf, false) + if !t.keytimer.Stop() { + select { + case <-t.keytimer.C: + default: + } + } + if buf.Len() > 0 { + t.keytimer.Reset(time.Millisecond * 50) + } + } + } +} + +func (t *tScreen) inputLoop(stopQ chan struct{}) { + + defer t.wg.Done() + for { + select { + case <-stopQ: + return + default: + } + chunk := make([]byte, 128) + n, e := t.tty.Read(chunk) + switch e { + case nil: + default: + _ = t.PostEvent(NewEventError(e)) + return + } + if n > 0 { + t.keychan <- chunk[:n] + } + } +} + +func (t *tScreen) Sync() { + t.Lock() + t.cx = -1 + t.cy = -1 + if !t.fini { + t.resize() + t.clear = true + t.cells.Invalidate() + t.draw() + } + t.Unlock() +} + +func (t *tScreen) CharacterSet() string { + return t.charset +} + +func (t *tScreen) RegisterRuneFallback(orig rune, fallback string) { + t.Lock() + t.fallback[orig] = fallback + t.Unlock() +} + +func (t *tScreen) UnregisterRuneFallback(orig rune) { + t.Lock() + delete(t.fallback, orig) + t.Unlock() +} + +func (t *tScreen) CanDisplay(r rune, checkFallbacks bool) bool { + + if enc := t.encoder; enc != nil { + nb := make([]byte, 6) + ob := make([]byte, 6) + num := utf8.EncodeRune(ob, r) + + enc.Reset() + dst, _, err := enc.Transform(nb, ob[:num], true) + if dst != 0 && err == nil && nb[0] != '\x1A' { + return true + } + } + // Terminal fallbacks always permitted, since we assume they are + // basically nearly perfect renditions. + if _, ok := t.acs[r]; ok { + return true + } + if !checkFallbacks { + return false + } + if _, ok := t.fallback[r]; ok { + return true + } + return false +} + +func (t *tScreen) HasMouse() bool { + return len(t.mouse) != 0 +} + +func (t *tScreen) HasKey(k Key) bool { + if k == KeyRune { + return true + } + return t.keyexist[k] +} + +func (t *tScreen) Resize(int, int, int, int) {} + +func (t *tScreen) Suspend() error { + t.disengage() + return nil +} + +func (t *tScreen) Resume() error { + return t.engage() +} + +// engage is used to place the terminal in raw mode and establish screen size, etc. +// Thing of this is as tcell "engaging" the clutch, as it's going to be driving the +// terminal interface. +func (t *tScreen) engage() error { + t.Lock() + defer t.Unlock() + if t.tty == nil { + return ErrNoScreen + } + t.tty.NotifyResize(func() { + select { + case t.resizeQ <- true: + default: + } + }) + if t.running { + return errors.New("already engaged") + } + if err := t.tty.Start(); err != nil { + return err + } + t.running = true + if w, h, err := t.tty.WindowSize(); err == nil && w != 0 && h != 0 { + t.cells.Resize(w, h) + } + stopQ := make(chan struct{}) + t.stopQ = stopQ + t.enableMouse(t.mouseFlags) + t.enablePasting(t.pasteEnabled) + + ti := t.ti + t.TPuts(ti.EnterCA) + t.TPuts(ti.EnterKeypad) + t.TPuts(ti.HideCursor) + t.TPuts(ti.EnableAcs) + t.TPuts(ti.Clear) + + t.wg.Add(2) + go t.inputLoop(stopQ) + go t.mainLoop(stopQ) + return nil +} + +// disengage is used to release the terminal back to support from the caller. +// Think of this as tcell disengaging the clutch, so that another application +// can take over the terminal interface. This restores the TTY mode that was +// present when the application was first started. +func (t *tScreen) disengage() { + + t.Lock() + if !t.running { + t.Unlock() + return + } + t.running = false + stopQ := t.stopQ + close(stopQ) + _ = t.tty.Drain() + t.Unlock() + + t.tty.NotifyResize(nil) + // wait for everything to shut down + t.wg.Wait() + + // shutdown the screen and disable special modes (e.g. mouse and bracketed paste) + ti := t.ti + t.cells.Resize(0, 0) + t.TPuts(ti.ShowCursor) + t.TPuts(ti.ResetFgBg) + t.TPuts(ti.AttrOff) + t.TPuts(ti.Clear) + t.TPuts(ti.ExitCA) + t.TPuts(ti.ExitKeypad) + t.enableMouse(0) + t.enablePasting(false) + + _ = t.tty.Stop() +} + +// Beep emits a beep to the terminal. +func (t *tScreen) Beep() error { + t.writeString(string(byte(7))) + return nil +} + +// finalize is used to at application shutdown, and restores the terminal +// to it's initial state. It should not be called more than once. +func (t *tScreen) finalize() { + t.disengage() + _ = t.tty.Close() +} diff --git a/vendor/github.com/gdamore/tcell/v2/tscreen_stub.go b/vendor/github.com/gdamore/tcell/v2/tscreen_stub.go new file mode 100644 index 0000000000..1611cb5531 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/tscreen_stub.go @@ -0,0 +1,25 @@ +// +build js plan9 windows + +// Copyright 2021 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +// NB: We might someday wish to move Windows to this model. However, +// that would probably mean sacrificing some of the richer key reporting +// that we can obtain with the console API present on Windows. + +func (t *tScreen) initialize() error { + return ErrNoScreen +} diff --git a/vendor/github.com/gdamore/tcell/v2/tscreen_unix.go b/vendor/github.com/gdamore/tcell/v2/tscreen_unix.go new file mode 100644 index 0000000000..1eb7504a7b --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/tscreen_unix.go @@ -0,0 +1,31 @@ +// Copyright 2021 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos + +package tcell + +// initialize is used at application startup, and sets up the initial values +// including file descriptors used for terminals and saving the initial state +// so that it can be restored when the application terminates. +func (t *tScreen) initialize() error { + var err error + if t.tty == nil { + t.tty, err = NewDevTty() + if err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/gdamore/tcell/v2/tty.go b/vendor/github.com/gdamore/tcell/v2/tty.go new file mode 100644 index 0000000000..567b041876 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/tty.go @@ -0,0 +1,56 @@ +// Copyright 2021 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +package tcell + +import "io" + +// Tty is an abstraction of a tty (traditionally "teletype"). This allows applications to +// provide for alternate backends, as there are situations where the traditional /dev/tty +// does not work, or where more flexible handling is required. This interface is for use +// with the terminfo-style based API. It extends the io.ReadWriter API. It is reasonable +// that the implementation might choose to use different underlying files for the Reader +// and Writer sides of this API, as part of it's internal implementation. +type Tty interface { + // Start is used to activate the Tty for use. Upon return the terminal should be + // in raw mode, non-blocking, etc. The implementation should take care of saving + // any state that is required so that it may be restored when Stop is called. + Start() error + + // Stop is used to stop using this Tty instance. This may be a suspend, so that other + // terminal based applications can run in the foreground. Implementations should + // restore any state collected at Start(), and return to ordinary blocking mode, etc. + // Drain is called first to drain the input. Once this is called, no more Read + // or Write calls will be made until Start is called again. + Stop() error + + // Drain is called before Stop, and ensures that the reader will wake up appropriately + // if it was blocked. This workaround is required for /dev/tty on certain UNIX systems + // to ensure that Read() does not block forever. This typically arranges for the tty driver + // to send data immediately (e.g. VMIN and VTIME both set zero) and sets a deadline on input. + // Implementations may reasonably make this a no-op. There will still be control sequences + // emitted between the time this is called, and when Stop is called. + Drain() error + + // NotifyResize is used register a callback when the tty thinks the dimensions have + // changed. The standard UNIX implementation links this to a handler for SIGWINCH. + // If the supplied callback is nil, then any handler should be unregistered. + NotifyResize(cb func()) + + // WindowSize is called to determine the terminal dimensions. This might be determined + // by an ioctl or other means. + WindowSize() (width int, height int, err error) + + io.ReadWriteCloser +} \ No newline at end of file diff --git a/vendor/github.com/gdamore/tcell/v2/tty_unix.go b/vendor/github.com/gdamore/tcell/v2/tty_unix.go new file mode 100644 index 0000000000..0c6ea78498 --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/tty_unix.go @@ -0,0 +1,190 @@ +// Copyright 2021 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use 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. + +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos + +package tcell + +import ( + "errors" + "fmt" + "os" + "os/signal" + "strconv" + "sync" + "syscall" + "time" + + "golang.org/x/term" +) + +// devTty is an implementation of the Tty API based upon /dev/tty. +type devTty struct { + fd int + f *os.File + of *os.File // the first open of /dev/tty + saved *term.State + sig chan os.Signal + cb func() + stopQ chan struct{} + dev string + wg sync.WaitGroup + l sync.Mutex +} + +func (tty *devTty) Read(b []byte) (int, error) { + return tty.f.Read(b) +} + +func (tty *devTty) Write(b []byte) (int, error) { + return tty.f.Write(b) +} + +func (tty *devTty) Close() error { + return tty.f.Close() +} + +func (tty *devTty) Start() error { + tty.l.Lock() + defer tty.l.Unlock() + + // We open another copy of /dev/tty. This is a workaround for unusual behavior + // observed in macOS, apparently caused when a subshell (for example) closes our + // own tty device (when it exits for example). Getting a fresh new one seems to + // resolve the problem. (We believe this is a bug in the macOS tty driver that + // fails to account for dup() references to the same file before applying close() + // related behaviors to the tty.) We're also holding the original copy we opened + // since closing that might have deleterious effects as well. The upshot is that + // we will have up to two separate file handles open on /dev/tty. (Note that when + // using stdin/stdout instead of /dev/tty this problem is not observed.) + var err error + if tty.f, err = os.OpenFile(tty.dev, os.O_RDWR, 0); err != nil { + return err + } + tty.fd = int(tty.of.Fd()) + + if !term.IsTerminal(tty.fd) { + return errors.New("device is not a terminal") + } + + _ = tty.f.SetReadDeadline(time.Time{}) + saved, err := term.MakeRaw(tty.fd) // also sets vMin and vTime + if err != nil { + return err + } + tty.saved = saved + + tty.stopQ = make(chan struct{}) + tty.wg.Add(1) + go func(stopQ chan struct{}) { + defer tty.wg.Done() + for { + select { + case <-tty.sig: + tty.l.Lock() + cb := tty.cb + tty.l.Unlock() + if cb != nil { + cb() + } + case <-stopQ: + return + } + } + }(tty.stopQ) + + signal.Notify(tty.sig, syscall.SIGWINCH) + return nil +} + +func (tty *devTty) Drain() error { + _ = tty.f.SetReadDeadline(time.Now()) + if err := tcSetBufParams(tty.fd, 0, 0); err != nil { + return err + } + return nil +} + +func (tty *devTty) Stop() error { + tty.l.Lock() + if err := term.Restore(tty.fd, tty.saved); err != nil { + tty.l.Unlock() + return err + } + _ = tty.f.SetReadDeadline(time.Now()) + + signal.Stop(tty.sig) + close(tty.stopQ) + tty.l.Unlock() + + tty.wg.Wait() + + // close our tty device -- we'll get another one if we Start again later. + _ = tty.f.Close() + + return nil +} + +func (tty *devTty) WindowSize() (int, int, error) { + w, h, err := term.GetSize(tty.fd) + if err != nil { + return 0, 0, err + } + if w == 0 { + w, _ = strconv.Atoi(os.Getenv("COLUMNS")) + } + if w == 0 { + w = 80 // default + } + if h == 0 { + h, _ = strconv.Atoi(os.Getenv("LINES")) + } + if h == 0 { + h = 25 // default + } + return w, h, nil +} + +func (tty *devTty) NotifyResize(cb func()) { + tty.l.Lock() + tty.cb = cb + tty.l.Unlock() +} + +// NewDevTty opens a /dev/tty based Tty. +func NewDevTty() (Tty, error) { + return NewDevTtyFromDev("/dev/tty") +} + +// NewDevTtyFromDev opens a tty device given a path. This can be useful to bind to other nodes. +func NewDevTtyFromDev(dev string) (Tty, error) { + tty := &devTty{ + dev: dev, + sig: make(chan os.Signal), + } + var err error + if tty.of, err = os.OpenFile(dev, os.O_RDWR, 0); err != nil { + return nil, err + } + tty.fd = int(tty.of.Fd()) + if !term.IsTerminal(tty.fd) { + _ = tty.f.Close() + return nil, errors.New("not a terminal") + } + if tty.saved, err = term.GetState(tty.fd); err != nil { + _ = tty.f.Close() + return nil, fmt.Errorf("failed to get state: %w", err) + } + return tty, nil +} diff --git a/vendor/github.com/gogo/protobuf/gogoproto/Makefile b/vendor/github.com/gogo/protobuf/gogoproto/Makefile new file mode 100644 index 0000000000..0b4659b731 --- /dev/null +++ b/vendor/github.com/gogo/protobuf/gogoproto/Makefile @@ -0,0 +1,37 @@ +# Protocol Buffers for Go with Gadgets +# +# Copyright (c) 2013, The GoGo Authors. All rights reserved. +# http://github.com/gogo/protobuf +# +# 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 +# OWNER 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. + +regenerate: + go install github.com/gogo/protobuf/protoc-gen-gogo + protoc --gogo_out=Mgoogle/protobuf/descriptor.proto=github.com/gogo/protobuf/protoc-gen-gogo/descriptor:../../../../ --proto_path=../../../../:../protobuf/:. *.proto + +restore: + cp gogo.pb.golden gogo.pb.go + +preserve: + cp gogo.pb.go gogo.pb.golden diff --git a/vendor/github.com/gogo/protobuf/gogoproto/doc.go b/vendor/github.com/gogo/protobuf/gogoproto/doc.go new file mode 100644 index 0000000000..081c86fa8e --- /dev/null +++ b/vendor/github.com/gogo/protobuf/gogoproto/doc.go @@ -0,0 +1,169 @@ +// Protocol Buffers for Go with Gadgets +// +// Copyright (c) 2013, The GoGo Authors. All rights reserved. +// http://github.com/gogo/protobuf +// +// 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 +// OWNER 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. + +/* +Package gogoproto provides extensions for protocol buffers to achieve: + + - fast marshalling and unmarshalling. + - peace of mind by optionally generating test and benchmark code. + - more canonical Go structures. + - less typing by optionally generating extra helper code. + - goprotobuf compatibility + +More Canonical Go Structures + +A lot of time working with a goprotobuf struct will lead you to a place where you create another struct that is easier to work with and then have a function to copy the values between the two structs. +You might also find that basic structs that started their life as part of an API need to be sent over the wire. With gob, you could just send it. With goprotobuf, you need to make a parallel struct. +Gogoprotobuf tries to fix these problems with the nullable, embed, customtype and customname field extensions. + + - nullable, if false, a field is generated without a pointer (see warning below). + - embed, if true, the field is generated as an embedded field. + - customtype, It works with the Marshal and Unmarshal methods, to allow you to have your own types in your struct, but marshal to bytes. For example, custom.Uuid or custom.Fixed128 + - customname (beta), Changes the generated fieldname. This is especially useful when generated methods conflict with fieldnames. + - casttype (beta), Changes the generated fieldtype. All generated code assumes that this type is castable to the protocol buffer field type. It does not work for structs or enums. + - castkey (beta), Changes the generated fieldtype for a map key. All generated code assumes that this type is castable to the protocol buffer field type. Only supported on maps. + - castvalue (beta), Changes the generated fieldtype for a map value. All generated code assumes that this type is castable to the protocol buffer field type. Only supported on maps. + +Warning about nullable: According to the Protocol Buffer specification, you should be able to tell whether a field is set or unset. With the option nullable=false this feature is lost, since your non-nullable fields will always be set. It can be seen as a layer on top of Protocol Buffers, where before and after marshalling all non-nullable fields are set and they cannot be unset. + +Let us look at: + + github.com/gogo/protobuf/test/example/example.proto + +for a quicker overview. + +The following message: + + package test; + + import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + + message A { + optional string Description = 1 [(gogoproto.nullable) = false]; + optional int64 Number = 2 [(gogoproto.nullable) = false]; + optional bytes Id = 3 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uuid", (gogoproto.nullable) = false]; + } + +Will generate a go struct which looks a lot like this: + + type A struct { + Description string + Number int64 + Id github_com_gogo_protobuf_test_custom.Uuid + } + +You will see there are no pointers, since all fields are non-nullable. +You will also see a custom type which marshals to a string. +Be warned it is your responsibility to test your custom types thoroughly. +You should think of every possible empty and nil case for your marshaling, unmarshaling and size methods. + +Next we will embed the message A in message B. + + message B { + optional A A = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true]; + repeated bytes G = 2 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uint128", (gogoproto.nullable) = false]; + } + +See below that A is embedded in B. + + type B struct { + A + G []github_com_gogo_protobuf_test_custom.Uint128 + } + +Also see the repeated custom type. + + type Uint128 [2]uint64 + +Next we will create a custom name for one of our fields. + + message C { + optional int64 size = 1 [(gogoproto.customname) = "MySize"]; + } + +See below that the field's name is MySize and not Size. + + type C struct { + MySize *int64 + } + +The is useful when having a protocol buffer message with a field name which conflicts with a generated method. +As an example, having a field name size and using the sizer plugin to generate a Size method will cause a go compiler error. +Using customname you can fix this error without changing the field name. +This is typically useful when working with a protocol buffer that was designed before these methods and/or the go language were avialable. + +Gogoprotobuf also has some more subtle changes, these could be changed back: + + - the generated package name for imports do not have the extra /filename.pb, + but are actually the imports specified in the .proto file. + +Gogoprotobuf also has lost some features which should be brought back with time: + + - Marshalling and unmarshalling with reflect and without the unsafe package, + this requires work in pointer_reflect.go + +Why does nullable break protocol buffer specifications: + +The protocol buffer specification states, somewhere, that you should be able to tell whether a +field is set or unset. With the option nullable=false this feature is lost, +since your non-nullable fields will always be set. It can be seen as a layer on top of +protocol buffers, where before and after marshalling all non-nullable fields are set +and they cannot be unset. + +Goprotobuf Compatibility: + +Gogoprotobuf is compatible with Goprotobuf, because it is compatible with protocol buffers. +Gogoprotobuf generates the same code as goprotobuf if no extensions are used. +The enumprefix, getters and stringer extensions can be used to remove some of the unnecessary code generated by goprotobuf: + + - gogoproto_import, if false, the generated code imports github.com/golang/protobuf/proto instead of github.com/gogo/protobuf/proto. + - goproto_enum_prefix, if false, generates the enum constant names without the messagetype prefix + - goproto_enum_stringer (experimental), if false, the enum is generated without the default string method, this is useful for rather using enum_stringer, or allowing you to write your own string method. + - goproto_getters, if false, the message is generated without get methods, this is useful when you would rather want to use face + - goproto_stringer, if false, the message is generated without the default string method, this is useful for rather using stringer, or allowing you to write your own string method. + - goproto_extensions_map (beta), if false, the extensions field is generated as type []byte instead of type map[int32]proto.Extension + - goproto_unrecognized (beta), if false, XXX_unrecognized field is not generated. This is useful in conjunction with gogoproto.nullable=false, to generate structures completely devoid of pointers and reduce GC pressure at the cost of losing information about unrecognized fields. + - goproto_registration (beta), if true, the generated files will register all messages and types against both gogo/protobuf and golang/protobuf. This is necessary when using third-party packages which read registrations from golang/protobuf (such as the grpc-gateway). + +Less Typing and Peace of Mind is explained in their specific plugin folders godoc: + + - github.com/gogo/protobuf/plugin/ + +If you do not use any of these extension the code that is generated +will be the same as if goprotobuf has generated it. + +The most complete way to see examples is to look at + + github.com/gogo/protobuf/test/thetest.proto + +Gogoprototest is a seperate project, +because we want to keep gogoprotobuf independent of goprotobuf, +but we still want to test it thoroughly. + +*/ +package gogoproto diff --git a/vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.go b/vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.go new file mode 100644 index 0000000000..1e91766aee --- /dev/null +++ b/vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.go @@ -0,0 +1,874 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: gogo.proto + +package gogoproto + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +var E_GoprotoEnumPrefix = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.EnumOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 62001, + Name: "gogoproto.goproto_enum_prefix", + Tag: "varint,62001,opt,name=goproto_enum_prefix", + Filename: "gogo.proto", +} + +var E_GoprotoEnumStringer = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.EnumOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 62021, + Name: "gogoproto.goproto_enum_stringer", + Tag: "varint,62021,opt,name=goproto_enum_stringer", + Filename: "gogo.proto", +} + +var E_EnumStringer = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.EnumOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 62022, + Name: "gogoproto.enum_stringer", + Tag: "varint,62022,opt,name=enum_stringer", + Filename: "gogo.proto", +} + +var E_EnumCustomname = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.EnumOptions)(nil), + ExtensionType: (*string)(nil), + Field: 62023, + Name: "gogoproto.enum_customname", + Tag: "bytes,62023,opt,name=enum_customname", + Filename: "gogo.proto", +} + +var E_Enumdecl = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.EnumOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 62024, + Name: "gogoproto.enumdecl", + Tag: "varint,62024,opt,name=enumdecl", + Filename: "gogo.proto", +} + +var E_EnumvalueCustomname = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.EnumValueOptions)(nil), + ExtensionType: (*string)(nil), + Field: 66001, + Name: "gogoproto.enumvalue_customname", + Tag: "bytes,66001,opt,name=enumvalue_customname", + Filename: "gogo.proto", +} + +var E_GoprotoGettersAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63001, + Name: "gogoproto.goproto_getters_all", + Tag: "varint,63001,opt,name=goproto_getters_all", + Filename: "gogo.proto", +} + +var E_GoprotoEnumPrefixAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63002, + Name: "gogoproto.goproto_enum_prefix_all", + Tag: "varint,63002,opt,name=goproto_enum_prefix_all", + Filename: "gogo.proto", +} + +var E_GoprotoStringerAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63003, + Name: "gogoproto.goproto_stringer_all", + Tag: "varint,63003,opt,name=goproto_stringer_all", + Filename: "gogo.proto", +} + +var E_VerboseEqualAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63004, + Name: "gogoproto.verbose_equal_all", + Tag: "varint,63004,opt,name=verbose_equal_all", + Filename: "gogo.proto", +} + +var E_FaceAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63005, + Name: "gogoproto.face_all", + Tag: "varint,63005,opt,name=face_all", + Filename: "gogo.proto", +} + +var E_GostringAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63006, + Name: "gogoproto.gostring_all", + Tag: "varint,63006,opt,name=gostring_all", + Filename: "gogo.proto", +} + +var E_PopulateAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63007, + Name: "gogoproto.populate_all", + Tag: "varint,63007,opt,name=populate_all", + Filename: "gogo.proto", +} + +var E_StringerAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63008, + Name: "gogoproto.stringer_all", + Tag: "varint,63008,opt,name=stringer_all", + Filename: "gogo.proto", +} + +var E_OnlyoneAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63009, + Name: "gogoproto.onlyone_all", + Tag: "varint,63009,opt,name=onlyone_all", + Filename: "gogo.proto", +} + +var E_EqualAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63013, + Name: "gogoproto.equal_all", + Tag: "varint,63013,opt,name=equal_all", + Filename: "gogo.proto", +} + +var E_DescriptionAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63014, + Name: "gogoproto.description_all", + Tag: "varint,63014,opt,name=description_all", + Filename: "gogo.proto", +} + +var E_TestgenAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63015, + Name: "gogoproto.testgen_all", + Tag: "varint,63015,opt,name=testgen_all", + Filename: "gogo.proto", +} + +var E_BenchgenAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63016, + Name: "gogoproto.benchgen_all", + Tag: "varint,63016,opt,name=benchgen_all", + Filename: "gogo.proto", +} + +var E_MarshalerAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63017, + Name: "gogoproto.marshaler_all", + Tag: "varint,63017,opt,name=marshaler_all", + Filename: "gogo.proto", +} + +var E_UnmarshalerAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63018, + Name: "gogoproto.unmarshaler_all", + Tag: "varint,63018,opt,name=unmarshaler_all", + Filename: "gogo.proto", +} + +var E_StableMarshalerAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63019, + Name: "gogoproto.stable_marshaler_all", + Tag: "varint,63019,opt,name=stable_marshaler_all", + Filename: "gogo.proto", +} + +var E_SizerAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63020, + Name: "gogoproto.sizer_all", + Tag: "varint,63020,opt,name=sizer_all", + Filename: "gogo.proto", +} + +var E_GoprotoEnumStringerAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63021, + Name: "gogoproto.goproto_enum_stringer_all", + Tag: "varint,63021,opt,name=goproto_enum_stringer_all", + Filename: "gogo.proto", +} + +var E_EnumStringerAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63022, + Name: "gogoproto.enum_stringer_all", + Tag: "varint,63022,opt,name=enum_stringer_all", + Filename: "gogo.proto", +} + +var E_UnsafeMarshalerAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63023, + Name: "gogoproto.unsafe_marshaler_all", + Tag: "varint,63023,opt,name=unsafe_marshaler_all", + Filename: "gogo.proto", +} + +var E_UnsafeUnmarshalerAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63024, + Name: "gogoproto.unsafe_unmarshaler_all", + Tag: "varint,63024,opt,name=unsafe_unmarshaler_all", + Filename: "gogo.proto", +} + +var E_GoprotoExtensionsMapAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63025, + Name: "gogoproto.goproto_extensions_map_all", + Tag: "varint,63025,opt,name=goproto_extensions_map_all", + Filename: "gogo.proto", +} + +var E_GoprotoUnrecognizedAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63026, + Name: "gogoproto.goproto_unrecognized_all", + Tag: "varint,63026,opt,name=goproto_unrecognized_all", + Filename: "gogo.proto", +} + +var E_GogoprotoImport = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63027, + Name: "gogoproto.gogoproto_import", + Tag: "varint,63027,opt,name=gogoproto_import", + Filename: "gogo.proto", +} + +var E_ProtosizerAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63028, + Name: "gogoproto.protosizer_all", + Tag: "varint,63028,opt,name=protosizer_all", + Filename: "gogo.proto", +} + +var E_CompareAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63029, + Name: "gogoproto.compare_all", + Tag: "varint,63029,opt,name=compare_all", + Filename: "gogo.proto", +} + +var E_TypedeclAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63030, + Name: "gogoproto.typedecl_all", + Tag: "varint,63030,opt,name=typedecl_all", + Filename: "gogo.proto", +} + +var E_EnumdeclAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63031, + Name: "gogoproto.enumdecl_all", + Tag: "varint,63031,opt,name=enumdecl_all", + Filename: "gogo.proto", +} + +var E_GoprotoRegistration = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63032, + Name: "gogoproto.goproto_registration", + Tag: "varint,63032,opt,name=goproto_registration", + Filename: "gogo.proto", +} + +var E_MessagenameAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63033, + Name: "gogoproto.messagename_all", + Tag: "varint,63033,opt,name=messagename_all", + Filename: "gogo.proto", +} + +var E_GoprotoSizecacheAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63034, + Name: "gogoproto.goproto_sizecache_all", + Tag: "varint,63034,opt,name=goproto_sizecache_all", + Filename: "gogo.proto", +} + +var E_GoprotoUnkeyedAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63035, + Name: "gogoproto.goproto_unkeyed_all", + Tag: "varint,63035,opt,name=goproto_unkeyed_all", + Filename: "gogo.proto", +} + +var E_GoprotoGetters = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64001, + Name: "gogoproto.goproto_getters", + Tag: "varint,64001,opt,name=goproto_getters", + Filename: "gogo.proto", +} + +var E_GoprotoStringer = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64003, + Name: "gogoproto.goproto_stringer", + Tag: "varint,64003,opt,name=goproto_stringer", + Filename: "gogo.proto", +} + +var E_VerboseEqual = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64004, + Name: "gogoproto.verbose_equal", + Tag: "varint,64004,opt,name=verbose_equal", + Filename: "gogo.proto", +} + +var E_Face = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64005, + Name: "gogoproto.face", + Tag: "varint,64005,opt,name=face", + Filename: "gogo.proto", +} + +var E_Gostring = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64006, + Name: "gogoproto.gostring", + Tag: "varint,64006,opt,name=gostring", + Filename: "gogo.proto", +} + +var E_Populate = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64007, + Name: "gogoproto.populate", + Tag: "varint,64007,opt,name=populate", + Filename: "gogo.proto", +} + +var E_Stringer = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 67008, + Name: "gogoproto.stringer", + Tag: "varint,67008,opt,name=stringer", + Filename: "gogo.proto", +} + +var E_Onlyone = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64009, + Name: "gogoproto.onlyone", + Tag: "varint,64009,opt,name=onlyone", + Filename: "gogo.proto", +} + +var E_Equal = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64013, + Name: "gogoproto.equal", + Tag: "varint,64013,opt,name=equal", + Filename: "gogo.proto", +} + +var E_Description = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64014, + Name: "gogoproto.description", + Tag: "varint,64014,opt,name=description", + Filename: "gogo.proto", +} + +var E_Testgen = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64015, + Name: "gogoproto.testgen", + Tag: "varint,64015,opt,name=testgen", + Filename: "gogo.proto", +} + +var E_Benchgen = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64016, + Name: "gogoproto.benchgen", + Tag: "varint,64016,opt,name=benchgen", + Filename: "gogo.proto", +} + +var E_Marshaler = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64017, + Name: "gogoproto.marshaler", + Tag: "varint,64017,opt,name=marshaler", + Filename: "gogo.proto", +} + +var E_Unmarshaler = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64018, + Name: "gogoproto.unmarshaler", + Tag: "varint,64018,opt,name=unmarshaler", + Filename: "gogo.proto", +} + +var E_StableMarshaler = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64019, + Name: "gogoproto.stable_marshaler", + Tag: "varint,64019,opt,name=stable_marshaler", + Filename: "gogo.proto", +} + +var E_Sizer = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64020, + Name: "gogoproto.sizer", + Tag: "varint,64020,opt,name=sizer", + Filename: "gogo.proto", +} + +var E_UnsafeMarshaler = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64023, + Name: "gogoproto.unsafe_marshaler", + Tag: "varint,64023,opt,name=unsafe_marshaler", + Filename: "gogo.proto", +} + +var E_UnsafeUnmarshaler = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64024, + Name: "gogoproto.unsafe_unmarshaler", + Tag: "varint,64024,opt,name=unsafe_unmarshaler", + Filename: "gogo.proto", +} + +var E_GoprotoExtensionsMap = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64025, + Name: "gogoproto.goproto_extensions_map", + Tag: "varint,64025,opt,name=goproto_extensions_map", + Filename: "gogo.proto", +} + +var E_GoprotoUnrecognized = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64026, + Name: "gogoproto.goproto_unrecognized", + Tag: "varint,64026,opt,name=goproto_unrecognized", + Filename: "gogo.proto", +} + +var E_Protosizer = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64028, + Name: "gogoproto.protosizer", + Tag: "varint,64028,opt,name=protosizer", + Filename: "gogo.proto", +} + +var E_Compare = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64029, + Name: "gogoproto.compare", + Tag: "varint,64029,opt,name=compare", + Filename: "gogo.proto", +} + +var E_Typedecl = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64030, + Name: "gogoproto.typedecl", + Tag: "varint,64030,opt,name=typedecl", + Filename: "gogo.proto", +} + +var E_Messagename = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64033, + Name: "gogoproto.messagename", + Tag: "varint,64033,opt,name=messagename", + Filename: "gogo.proto", +} + +var E_GoprotoSizecache = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64034, + Name: "gogoproto.goproto_sizecache", + Tag: "varint,64034,opt,name=goproto_sizecache", + Filename: "gogo.proto", +} + +var E_GoprotoUnkeyed = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64035, + Name: "gogoproto.goproto_unkeyed", + Tag: "varint,64035,opt,name=goproto_unkeyed", + Filename: "gogo.proto", +} + +var E_Nullable = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 65001, + Name: "gogoproto.nullable", + Tag: "varint,65001,opt,name=nullable", + Filename: "gogo.proto", +} + +var E_Embed = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 65002, + Name: "gogoproto.embed", + Tag: "varint,65002,opt,name=embed", + Filename: "gogo.proto", +} + +var E_Customtype = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*string)(nil), + Field: 65003, + Name: "gogoproto.customtype", + Tag: "bytes,65003,opt,name=customtype", + Filename: "gogo.proto", +} + +var E_Customname = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*string)(nil), + Field: 65004, + Name: "gogoproto.customname", + Tag: "bytes,65004,opt,name=customname", + Filename: "gogo.proto", +} + +var E_Jsontag = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*string)(nil), + Field: 65005, + Name: "gogoproto.jsontag", + Tag: "bytes,65005,opt,name=jsontag", + Filename: "gogo.proto", +} + +var E_Moretags = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*string)(nil), + Field: 65006, + Name: "gogoproto.moretags", + Tag: "bytes,65006,opt,name=moretags", + Filename: "gogo.proto", +} + +var E_Casttype = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*string)(nil), + Field: 65007, + Name: "gogoproto.casttype", + Tag: "bytes,65007,opt,name=casttype", + Filename: "gogo.proto", +} + +var E_Castkey = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*string)(nil), + Field: 65008, + Name: "gogoproto.castkey", + Tag: "bytes,65008,opt,name=castkey", + Filename: "gogo.proto", +} + +var E_Castvalue = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*string)(nil), + Field: 65009, + Name: "gogoproto.castvalue", + Tag: "bytes,65009,opt,name=castvalue", + Filename: "gogo.proto", +} + +var E_Stdtime = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 65010, + Name: "gogoproto.stdtime", + Tag: "varint,65010,opt,name=stdtime", + Filename: "gogo.proto", +} + +var E_Stdduration = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 65011, + Name: "gogoproto.stdduration", + Tag: "varint,65011,opt,name=stdduration", + Filename: "gogo.proto", +} + +var E_Wktpointer = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 65012, + Name: "gogoproto.wktpointer", + Tag: "varint,65012,opt,name=wktpointer", + Filename: "gogo.proto", +} + +func init() { + proto.RegisterExtension(E_GoprotoEnumPrefix) + proto.RegisterExtension(E_GoprotoEnumStringer) + proto.RegisterExtension(E_EnumStringer) + proto.RegisterExtension(E_EnumCustomname) + proto.RegisterExtension(E_Enumdecl) + proto.RegisterExtension(E_EnumvalueCustomname) + proto.RegisterExtension(E_GoprotoGettersAll) + proto.RegisterExtension(E_GoprotoEnumPrefixAll) + proto.RegisterExtension(E_GoprotoStringerAll) + proto.RegisterExtension(E_VerboseEqualAll) + proto.RegisterExtension(E_FaceAll) + proto.RegisterExtension(E_GostringAll) + proto.RegisterExtension(E_PopulateAll) + proto.RegisterExtension(E_StringerAll) + proto.RegisterExtension(E_OnlyoneAll) + proto.RegisterExtension(E_EqualAll) + proto.RegisterExtension(E_DescriptionAll) + proto.RegisterExtension(E_TestgenAll) + proto.RegisterExtension(E_BenchgenAll) + proto.RegisterExtension(E_MarshalerAll) + proto.RegisterExtension(E_UnmarshalerAll) + proto.RegisterExtension(E_StableMarshalerAll) + proto.RegisterExtension(E_SizerAll) + proto.RegisterExtension(E_GoprotoEnumStringerAll) + proto.RegisterExtension(E_EnumStringerAll) + proto.RegisterExtension(E_UnsafeMarshalerAll) + proto.RegisterExtension(E_UnsafeUnmarshalerAll) + proto.RegisterExtension(E_GoprotoExtensionsMapAll) + proto.RegisterExtension(E_GoprotoUnrecognizedAll) + proto.RegisterExtension(E_GogoprotoImport) + proto.RegisterExtension(E_ProtosizerAll) + proto.RegisterExtension(E_CompareAll) + proto.RegisterExtension(E_TypedeclAll) + proto.RegisterExtension(E_EnumdeclAll) + proto.RegisterExtension(E_GoprotoRegistration) + proto.RegisterExtension(E_MessagenameAll) + proto.RegisterExtension(E_GoprotoSizecacheAll) + proto.RegisterExtension(E_GoprotoUnkeyedAll) + proto.RegisterExtension(E_GoprotoGetters) + proto.RegisterExtension(E_GoprotoStringer) + proto.RegisterExtension(E_VerboseEqual) + proto.RegisterExtension(E_Face) + proto.RegisterExtension(E_Gostring) + proto.RegisterExtension(E_Populate) + proto.RegisterExtension(E_Stringer) + proto.RegisterExtension(E_Onlyone) + proto.RegisterExtension(E_Equal) + proto.RegisterExtension(E_Description) + proto.RegisterExtension(E_Testgen) + proto.RegisterExtension(E_Benchgen) + proto.RegisterExtension(E_Marshaler) + proto.RegisterExtension(E_Unmarshaler) + proto.RegisterExtension(E_StableMarshaler) + proto.RegisterExtension(E_Sizer) + proto.RegisterExtension(E_UnsafeMarshaler) + proto.RegisterExtension(E_UnsafeUnmarshaler) + proto.RegisterExtension(E_GoprotoExtensionsMap) + proto.RegisterExtension(E_GoprotoUnrecognized) + proto.RegisterExtension(E_Protosizer) + proto.RegisterExtension(E_Compare) + proto.RegisterExtension(E_Typedecl) + proto.RegisterExtension(E_Messagename) + proto.RegisterExtension(E_GoprotoSizecache) + proto.RegisterExtension(E_GoprotoUnkeyed) + proto.RegisterExtension(E_Nullable) + proto.RegisterExtension(E_Embed) + proto.RegisterExtension(E_Customtype) + proto.RegisterExtension(E_Customname) + proto.RegisterExtension(E_Jsontag) + proto.RegisterExtension(E_Moretags) + proto.RegisterExtension(E_Casttype) + proto.RegisterExtension(E_Castkey) + proto.RegisterExtension(E_Castvalue) + proto.RegisterExtension(E_Stdtime) + proto.RegisterExtension(E_Stdduration) + proto.RegisterExtension(E_Wktpointer) +} + +func init() { proto.RegisterFile("gogo.proto", fileDescriptor_592445b5231bc2b9) } + +var fileDescriptor_592445b5231bc2b9 = []byte{ + // 1328 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x98, 0x49, 0x6f, 0x1c, 0x45, + 0x14, 0x80, 0x85, 0x48, 0x64, 0x4f, 0x79, 0x8b, 0xc7, 0xc6, 0x84, 0x08, 0x44, 0xe0, 0xc4, 0xc9, + 0x3e, 0x45, 0x28, 0x65, 0x45, 0x96, 0x63, 0x39, 0x56, 0x10, 0x0e, 0xc6, 0x89, 0xc3, 0x76, 0x18, + 0xf5, 0xf4, 0x94, 0xdb, 0x8d, 0xbb, 0xbb, 0x9a, 0xee, 0xea, 0x10, 0xe7, 0x86, 0xc2, 0x22, 0x84, + 0xd8, 0x91, 0x20, 0x21, 0x09, 0x04, 0xc4, 0xbe, 0x86, 0x7d, 0xb9, 0x70, 0x61, 0xb9, 0xf2, 0x1f, + 0xb8, 0x00, 0x66, 0xf7, 0xcd, 0x17, 0xf4, 0xba, 0xdf, 0xeb, 0xa9, 0x69, 0x8f, 0x54, 0x35, 0xb7, + 0xf6, 0xb8, 0xbe, 0x6f, 0xaa, 0xdf, 0xeb, 0x7a, 0xef, 0x4d, 0x33, 0xe6, 0x49, 0x4f, 0x4e, 0xc6, + 0x89, 0x54, 0xb2, 0x5e, 0x83, 0xeb, 0xfc, 0x72, 0xdf, 0x7e, 0x4f, 0x4a, 0x2f, 0x10, 0x53, 0xf9, + 0x5f, 0xcd, 0x6c, 0x75, 0xaa, 0x25, 0x52, 0x37, 0xf1, 0x63, 0x25, 0x93, 0x62, 0x31, 0x3f, 0xc6, + 0xc6, 0x70, 0x71, 0x43, 0x44, 0x59, 0xd8, 0x88, 0x13, 0xb1, 0xea, 0x9f, 0xae, 0x5f, 0x3f, 0x59, + 0x90, 0x93, 0x44, 0x4e, 0xce, 0x47, 0x59, 0x78, 0x47, 0xac, 0x7c, 0x19, 0xa5, 0x7b, 0xaf, 0xfc, + 0x72, 0xf5, 0xfe, 0xab, 0x6e, 0xe9, 0x5f, 0x1e, 0x45, 0x14, 0xfe, 0xb7, 0x94, 0x83, 0x7c, 0x99, + 0x5d, 0xd3, 0xe1, 0x4b, 0x55, 0xe2, 0x47, 0x9e, 0x48, 0x0c, 0xc6, 0xef, 0xd1, 0x38, 0xa6, 0x19, + 0x8f, 0x23, 0xca, 0xe7, 0xd8, 0x50, 0x2f, 0xae, 0x1f, 0xd0, 0x35, 0x28, 0x74, 0xc9, 0x02, 0x1b, + 0xc9, 0x25, 0x6e, 0x96, 0x2a, 0x19, 0x46, 0x4e, 0x28, 0x0c, 0x9a, 0x1f, 0x73, 0x4d, 0x6d, 0x79, + 0x18, 0xb0, 0xb9, 0x92, 0xe2, 0x9c, 0xf5, 0xc3, 0x27, 0x2d, 0xe1, 0x06, 0x06, 0xc3, 0x4f, 0xb8, + 0x91, 0x72, 0x3d, 0x3f, 0xc9, 0xc6, 0xe1, 0xfa, 0x94, 0x13, 0x64, 0x42, 0xdf, 0xc9, 0x4d, 0x5d, + 0x3d, 0x27, 0x61, 0x19, 0xc9, 0x7e, 0x3e, 0xbb, 0x2b, 0xdf, 0xce, 0x58, 0x29, 0xd0, 0xf6, 0xa4, + 0x65, 0xd1, 0x13, 0x4a, 0x89, 0x24, 0x6d, 0x38, 0x41, 0xb7, 0xed, 0x1d, 0xf1, 0x83, 0xd2, 0x78, + 0x6e, 0xb3, 0x33, 0x8b, 0x0b, 0x05, 0x39, 0x1b, 0x04, 0x7c, 0x85, 0x5d, 0xdb, 0xe5, 0xa9, 0xb0, + 0x70, 0x9e, 0x47, 0xe7, 0xf8, 0x8e, 0x27, 0x03, 0xb4, 0x4b, 0x8c, 0x3e, 0x2f, 0x73, 0x69, 0xe1, + 0x7c, 0x19, 0x9d, 0x75, 0x64, 0x29, 0xa5, 0x60, 0xbc, 0x8d, 0x8d, 0x9e, 0x12, 0x49, 0x53, 0xa6, + 0xa2, 0x21, 0x1e, 0xc8, 0x9c, 0xc0, 0x42, 0x77, 0x01, 0x75, 0x23, 0x08, 0xce, 0x03, 0x07, 0xae, + 0x83, 0xac, 0x7f, 0xd5, 0x71, 0x85, 0x85, 0xe2, 0x22, 0x2a, 0xfa, 0x60, 0x3d, 0xa0, 0xb3, 0x6c, + 0xd0, 0x93, 0xc5, 0x2d, 0x59, 0xe0, 0x97, 0x10, 0x1f, 0x20, 0x06, 0x15, 0xb1, 0x8c, 0xb3, 0xc0, + 0x51, 0x36, 0x3b, 0x78, 0x85, 0x14, 0xc4, 0xa0, 0xa2, 0x87, 0xb0, 0xbe, 0x4a, 0x8a, 0x54, 0x8b, + 0xe7, 0x0c, 0x1b, 0x90, 0x51, 0xb0, 0x21, 0x23, 0x9b, 0x4d, 0x5c, 0x46, 0x03, 0x43, 0x04, 0x04, + 0xd3, 0xac, 0x66, 0x9b, 0x88, 0x37, 0x36, 0xe9, 0x78, 0x50, 0x06, 0x16, 0xd8, 0x08, 0x15, 0x28, + 0x5f, 0x46, 0x16, 0x8a, 0x37, 0x51, 0x31, 0xac, 0x61, 0x78, 0x1b, 0x4a, 0xa4, 0xca, 0x13, 0x36, + 0x92, 0xb7, 0xe8, 0x36, 0x10, 0xc1, 0x50, 0x36, 0x45, 0xe4, 0xae, 0xd9, 0x19, 0xde, 0xa6, 0x50, + 0x12, 0x03, 0x8a, 0x39, 0x36, 0x14, 0x3a, 0x49, 0xba, 0xe6, 0x04, 0x56, 0xe9, 0x78, 0x07, 0x1d, + 0x83, 0x25, 0x84, 0x11, 0xc9, 0xa2, 0x5e, 0x34, 0xef, 0x52, 0x44, 0x34, 0x0c, 0x8f, 0x5e, 0xaa, + 0x9c, 0x66, 0x20, 0x1a, 0xbd, 0xd8, 0xde, 0xa3, 0xa3, 0x57, 0xb0, 0x8b, 0xba, 0x71, 0x9a, 0xd5, + 0x52, 0xff, 0x8c, 0x95, 0xe6, 0x7d, 0xca, 0x74, 0x0e, 0x00, 0x7c, 0x0f, 0xbb, 0xae, 0x6b, 0x9b, + 0xb0, 0x90, 0x7d, 0x80, 0xb2, 0x89, 0x2e, 0xad, 0x02, 0x4b, 0x42, 0xaf, 0xca, 0x0f, 0xa9, 0x24, + 0x88, 0x8a, 0x6b, 0x89, 0x8d, 0x67, 0x51, 0xea, 0xac, 0xf6, 0x16, 0xb5, 0x8f, 0x28, 0x6a, 0x05, + 0xdb, 0x11, 0xb5, 0x13, 0x6c, 0x02, 0x8d, 0xbd, 0xe5, 0xf5, 0x63, 0x2a, 0xac, 0x05, 0xbd, 0xd2, + 0x99, 0xdd, 0xfb, 0xd8, 0xbe, 0x32, 0x9c, 0xa7, 0x95, 0x88, 0x52, 0x60, 0x1a, 0xa1, 0x13, 0x5b, + 0x98, 0xaf, 0xa0, 0x99, 0x2a, 0xfe, 0x7c, 0x29, 0x58, 0x74, 0x62, 0x90, 0xdf, 0xcd, 0xf6, 0x92, + 0x3c, 0x8b, 0x12, 0xe1, 0x4a, 0x2f, 0xf2, 0xcf, 0x88, 0x96, 0x85, 0xfa, 0x93, 0x4a, 0xaa, 0x56, + 0x34, 0x1c, 0xcc, 0x47, 0xd9, 0x9e, 0x72, 0x56, 0x69, 0xf8, 0x61, 0x2c, 0x13, 0x65, 0x30, 0x7e, + 0x4a, 0x99, 0x2a, 0xb9, 0xa3, 0x39, 0xc6, 0xe7, 0xd9, 0x70, 0xfe, 0xa7, 0xed, 0x23, 0xf9, 0x19, + 0x8a, 0x86, 0xda, 0x14, 0x16, 0x0e, 0x57, 0x86, 0xb1, 0x93, 0xd8, 0xd4, 0xbf, 0xcf, 0xa9, 0x70, + 0x20, 0x82, 0x85, 0x43, 0x6d, 0xc4, 0x02, 0xba, 0xbd, 0x85, 0xe1, 0x0b, 0x2a, 0x1c, 0xc4, 0xa0, + 0x82, 0x06, 0x06, 0x0b, 0xc5, 0x97, 0xa4, 0x20, 0x06, 0x14, 0x77, 0xb6, 0x1b, 0x6d, 0x22, 0x3c, + 0x3f, 0x55, 0x89, 0x03, 0xab, 0x0d, 0xaa, 0xaf, 0x36, 0x3b, 0x87, 0xb0, 0x65, 0x0d, 0x85, 0x4a, + 0x14, 0x8a, 0x34, 0x75, 0x3c, 0x01, 0x13, 0x87, 0xc5, 0xc6, 0xbe, 0xa6, 0x4a, 0xa4, 0x61, 0xb0, + 0x37, 0x6d, 0x42, 0x84, 0xb0, 0xbb, 0x8e, 0xbb, 0x66, 0xa3, 0xfb, 0xa6, 0xb2, 0xb9, 0xe3, 0xc4, + 0x82, 0x53, 0x9b, 0x7f, 0xb2, 0x68, 0x5d, 0x6c, 0x58, 0x3d, 0x9d, 0xdf, 0x56, 0xe6, 0x9f, 0x95, + 0x82, 0x2c, 0x6a, 0xc8, 0x48, 0x65, 0x9e, 0xaa, 0xdf, 0xb8, 0xc3, 0xb5, 0x58, 0xdc, 0x17, 0xe9, + 0x1e, 0xda, 0xc2, 0xfb, 0xed, 0x1c, 0xa7, 0xf8, 0xed, 0xf0, 0x90, 0x77, 0x0e, 0x3d, 0x66, 0xd9, + 0xd9, 0xad, 0xf2, 0x39, 0xef, 0x98, 0x79, 0xf8, 0x11, 0x36, 0xd4, 0x31, 0xf0, 0x98, 0x55, 0x0f, + 0xa3, 0x6a, 0x50, 0x9f, 0x77, 0xf8, 0x01, 0xb6, 0x0b, 0x86, 0x17, 0x33, 0xfe, 0x08, 0xe2, 0xf9, + 0x72, 0x7e, 0x88, 0xf5, 0xd3, 0xd0, 0x62, 0x46, 0x1f, 0x45, 0xb4, 0x44, 0x00, 0xa7, 0x81, 0xc5, + 0x8c, 0x3f, 0x46, 0x38, 0x21, 0x80, 0xdb, 0x87, 0xf0, 0xbb, 0x27, 0x76, 0x61, 0xd3, 0xa1, 0xd8, + 0x4d, 0xb3, 0x3e, 0x9c, 0x54, 0xcc, 0xf4, 0xe3, 0xf8, 0xe5, 0x44, 0xf0, 0x5b, 0xd9, 0x6e, 0xcb, + 0x80, 0x3f, 0x89, 0x68, 0xb1, 0x9e, 0xcf, 0xb1, 0x01, 0x6d, 0x3a, 0x31, 0xe3, 0x4f, 0x21, 0xae, + 0x53, 0xb0, 0x75, 0x9c, 0x4e, 0xcc, 0x82, 0xa7, 0x69, 0xeb, 0x48, 0x40, 0xd8, 0x68, 0x30, 0x31, + 0xd3, 0xcf, 0x50, 0xd4, 0x09, 0xe1, 0x33, 0xac, 0x56, 0x36, 0x1b, 0x33, 0xff, 0x2c, 0xf2, 0x6d, + 0x06, 0x22, 0xa0, 0x35, 0x3b, 0xb3, 0xe2, 0x39, 0x8a, 0x80, 0x46, 0xc1, 0x31, 0xaa, 0x0e, 0x30, + 0x66, 0xd3, 0xf3, 0x74, 0x8c, 0x2a, 0xf3, 0x0b, 0x64, 0x33, 0xaf, 0xf9, 0x66, 0xc5, 0x0b, 0x94, + 0xcd, 0x7c, 0x3d, 0x6c, 0xa3, 0x3a, 0x11, 0x98, 0x1d, 0x2f, 0xd2, 0x36, 0x2a, 0x03, 0x01, 0x5f, + 0x62, 0xf5, 0x9d, 0xd3, 0x80, 0xd9, 0xf7, 0x12, 0xfa, 0x46, 0x77, 0x0c, 0x03, 0xfc, 0x2e, 0x36, + 0xd1, 0x7d, 0x12, 0x30, 0x5b, 0xcf, 0x6d, 0x55, 0x7e, 0xbb, 0xe9, 0x83, 0x00, 0x3f, 0xd1, 0x6e, + 0x29, 0xfa, 0x14, 0x60, 0xd6, 0x9e, 0xdf, 0xea, 0x2c, 0xdc, 0xfa, 0x10, 0xc0, 0x67, 0x19, 0x6b, + 0x37, 0x60, 0xb3, 0xeb, 0x02, 0xba, 0x34, 0x08, 0x8e, 0x06, 0xf6, 0x5f, 0x33, 0x7f, 0x91, 0x8e, + 0x06, 0x12, 0x70, 0x34, 0xa8, 0xf5, 0x9a, 0xe9, 0x4b, 0x74, 0x34, 0x08, 0x81, 0x27, 0x5b, 0xeb, + 0x6e, 0x66, 0xc3, 0x65, 0x7a, 0xb2, 0x35, 0x8a, 0x1f, 0x63, 0xa3, 0x3b, 0x1a, 0xa2, 0x59, 0xf5, + 0x1a, 0xaa, 0xf6, 0x54, 0xfb, 0xa1, 0xde, 0xbc, 0xb0, 0x19, 0x9a, 0x6d, 0xaf, 0x57, 0x9a, 0x17, + 0xf6, 0x42, 0x3e, 0xcd, 0xfa, 0xa3, 0x2c, 0x08, 0xe0, 0xf0, 0xd4, 0x6f, 0xe8, 0xd2, 0x4d, 0x45, + 0xd0, 0x22, 0xc5, 0xaf, 0xdb, 0x18, 0x1d, 0x02, 0xf8, 0x01, 0xb6, 0x5b, 0x84, 0x4d, 0xd1, 0x32, + 0x91, 0xbf, 0x6d, 0x53, 0xc1, 0x84, 0xd5, 0x7c, 0x86, 0xb1, 0xe2, 0xd5, 0x08, 0x84, 0xd9, 0xc4, + 0xfe, 0xbe, 0x5d, 0xbc, 0xa5, 0xd1, 0x90, 0xb6, 0x20, 0x4f, 0x8a, 0x41, 0xb0, 0xd9, 0x29, 0xc8, + 0x33, 0x72, 0x90, 0xf5, 0xdd, 0x9f, 0xca, 0x48, 0x39, 0x9e, 0x89, 0xfe, 0x03, 0x69, 0x5a, 0x0f, + 0x01, 0x0b, 0x65, 0x22, 0x94, 0xe3, 0xa5, 0x26, 0xf6, 0x4f, 0x64, 0x4b, 0x00, 0x60, 0xd7, 0x49, + 0x95, 0xcd, 0x7d, 0xff, 0x45, 0x30, 0x01, 0xb0, 0x69, 0xb8, 0x5e, 0x17, 0x1b, 0x26, 0xf6, 0x6f, + 0xda, 0x34, 0xae, 0xe7, 0x87, 0x58, 0x0d, 0x2e, 0xf3, 0xb7, 0x4a, 0x26, 0xf8, 0x1f, 0x84, 0xdb, + 0x04, 0x7c, 0x73, 0xaa, 0x5a, 0xca, 0x37, 0x07, 0xfb, 0x5f, 0xcc, 0x34, 0xad, 0xe7, 0xb3, 0x6c, + 0x20, 0x55, 0xad, 0x56, 0x86, 0xf3, 0xa9, 0x01, 0xff, 0x6f, 0xbb, 0x7c, 0x65, 0x51, 0x32, 0x90, + 0xed, 0x07, 0xd7, 0x55, 0x2c, 0xfd, 0x48, 0x89, 0xc4, 0x64, 0xd8, 0x42, 0x83, 0x86, 0x1c, 0x9e, + 0x67, 0x63, 0xae, 0x0c, 0xab, 0xdc, 0x61, 0xb6, 0x20, 0x17, 0xe4, 0x52, 0x5e, 0x67, 0xee, 0xbd, + 0xd9, 0xf3, 0xd5, 0x5a, 0xd6, 0x9c, 0x74, 0x65, 0x38, 0x05, 0xbf, 0x3c, 0xda, 0x2f, 0x54, 0xcb, + 0xdf, 0x21, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0x9c, 0xaf, 0x70, 0x4e, 0x83, 0x15, 0x00, 0x00, +} diff --git a/vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.golden b/vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.golden new file mode 100644 index 0000000000..f6502e4b90 --- /dev/null +++ b/vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.golden @@ -0,0 +1,45 @@ +// Code generated by protoc-gen-go. +// source: gogo.proto +// DO NOT EDIT! + +package gogoproto + +import proto "github.com/gogo/protobuf/proto" +import json "encoding/json" +import math "math" +import google_protobuf "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" + +// Reference proto, json, and math imports to suppress error if they are not otherwise used. +var _ = proto.Marshal +var _ = &json.SyntaxError{} +var _ = math.Inf + +var E_Nullable = &proto.ExtensionDesc{ + ExtendedType: (*google_protobuf.FieldOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 51235, + Name: "gogoproto.nullable", + Tag: "varint,51235,opt,name=nullable", +} + +var E_Embed = &proto.ExtensionDesc{ + ExtendedType: (*google_protobuf.FieldOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 51236, + Name: "gogoproto.embed", + Tag: "varint,51236,opt,name=embed", +} + +var E_Customtype = &proto.ExtensionDesc{ + ExtendedType: (*google_protobuf.FieldOptions)(nil), + ExtensionType: (*string)(nil), + Field: 51237, + Name: "gogoproto.customtype", + Tag: "bytes,51237,opt,name=customtype", +} + +func init() { + proto.RegisterExtension(E_Nullable) + proto.RegisterExtension(E_Embed) + proto.RegisterExtension(E_Customtype) +} diff --git a/vendor/github.com/gogo/protobuf/gogoproto/gogo.proto b/vendor/github.com/gogo/protobuf/gogoproto/gogo.proto new file mode 100644 index 0000000000..b80c85653f --- /dev/null +++ b/vendor/github.com/gogo/protobuf/gogoproto/gogo.proto @@ -0,0 +1,144 @@ +// Protocol Buffers for Go with Gadgets +// +// Copyright (c) 2013, The GoGo Authors. All rights reserved. +// http://github.com/gogo/protobuf +// +// 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 +// OWNER 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. + +syntax = "proto2"; +package gogoproto; + +import "google/protobuf/descriptor.proto"; + +option java_package = "com.google.protobuf"; +option java_outer_classname = "GoGoProtos"; +option go_package = "github.com/gogo/protobuf/gogoproto"; + +extend google.protobuf.EnumOptions { + optional bool goproto_enum_prefix = 62001; + optional bool goproto_enum_stringer = 62021; + optional bool enum_stringer = 62022; + optional string enum_customname = 62023; + optional bool enumdecl = 62024; +} + +extend google.protobuf.EnumValueOptions { + optional string enumvalue_customname = 66001; +} + +extend google.protobuf.FileOptions { + optional bool goproto_getters_all = 63001; + optional bool goproto_enum_prefix_all = 63002; + optional bool goproto_stringer_all = 63003; + optional bool verbose_equal_all = 63004; + optional bool face_all = 63005; + optional bool gostring_all = 63006; + optional bool populate_all = 63007; + optional bool stringer_all = 63008; + optional bool onlyone_all = 63009; + + optional bool equal_all = 63013; + optional bool description_all = 63014; + optional bool testgen_all = 63015; + optional bool benchgen_all = 63016; + optional bool marshaler_all = 63017; + optional bool unmarshaler_all = 63018; + optional bool stable_marshaler_all = 63019; + + optional bool sizer_all = 63020; + + optional bool goproto_enum_stringer_all = 63021; + optional bool enum_stringer_all = 63022; + + optional bool unsafe_marshaler_all = 63023; + optional bool unsafe_unmarshaler_all = 63024; + + optional bool goproto_extensions_map_all = 63025; + optional bool goproto_unrecognized_all = 63026; + optional bool gogoproto_import = 63027; + optional bool protosizer_all = 63028; + optional bool compare_all = 63029; + optional bool typedecl_all = 63030; + optional bool enumdecl_all = 63031; + + optional bool goproto_registration = 63032; + optional bool messagename_all = 63033; + + optional bool goproto_sizecache_all = 63034; + optional bool goproto_unkeyed_all = 63035; +} + +extend google.protobuf.MessageOptions { + optional bool goproto_getters = 64001; + optional bool goproto_stringer = 64003; + optional bool verbose_equal = 64004; + optional bool face = 64005; + optional bool gostring = 64006; + optional bool populate = 64007; + optional bool stringer = 67008; + optional bool onlyone = 64009; + + optional bool equal = 64013; + optional bool description = 64014; + optional bool testgen = 64015; + optional bool benchgen = 64016; + optional bool marshaler = 64017; + optional bool unmarshaler = 64018; + optional bool stable_marshaler = 64019; + + optional bool sizer = 64020; + + optional bool unsafe_marshaler = 64023; + optional bool unsafe_unmarshaler = 64024; + + optional bool goproto_extensions_map = 64025; + optional bool goproto_unrecognized = 64026; + + optional bool protosizer = 64028; + optional bool compare = 64029; + + optional bool typedecl = 64030; + + optional bool messagename = 64033; + + optional bool goproto_sizecache = 64034; + optional bool goproto_unkeyed = 64035; +} + +extend google.protobuf.FieldOptions { + optional bool nullable = 65001; + optional bool embed = 65002; + optional string customtype = 65003; + optional string customname = 65004; + optional string jsontag = 65005; + optional string moretags = 65006; + optional string casttype = 65007; + optional string castkey = 65008; + optional string castvalue = 65009; + + optional bool stdtime = 65010; + optional bool stdduration = 65011; + optional bool wktpointer = 65012; + +} diff --git a/vendor/github.com/gogo/protobuf/gogoproto/helper.go b/vendor/github.com/gogo/protobuf/gogoproto/helper.go new file mode 100644 index 0000000000..390d4e4be6 --- /dev/null +++ b/vendor/github.com/gogo/protobuf/gogoproto/helper.go @@ -0,0 +1,415 @@ +// Protocol Buffers for Go with Gadgets +// +// Copyright (c) 2013, The GoGo Authors. All rights reserved. +// http://github.com/gogo/protobuf +// +// 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 +// OWNER 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. + +package gogoproto + +import google_protobuf "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" +import proto "github.com/gogo/protobuf/proto" + +func IsEmbed(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Embed, false) +} + +func IsNullable(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Nullable, true) +} + +func IsStdTime(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Stdtime, false) +} + +func IsStdDuration(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Stdduration, false) +} + +func IsStdDouble(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.DoubleValue" +} + +func IsStdFloat(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.FloatValue" +} + +func IsStdInt64(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.Int64Value" +} + +func IsStdUInt64(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.UInt64Value" +} + +func IsStdInt32(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.Int32Value" +} + +func IsStdUInt32(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.UInt32Value" +} + +func IsStdBool(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.BoolValue" +} + +func IsStdString(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.StringValue" +} + +func IsStdBytes(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.BytesValue" +} + +func IsStdType(field *google_protobuf.FieldDescriptorProto) bool { + return (IsStdTime(field) || IsStdDuration(field) || + IsStdDouble(field) || IsStdFloat(field) || + IsStdInt64(field) || IsStdUInt64(field) || + IsStdInt32(field) || IsStdUInt32(field) || + IsStdBool(field) || + IsStdString(field) || IsStdBytes(field)) +} + +func IsWktPtr(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) +} + +func NeedsNilCheck(proto3 bool, field *google_protobuf.FieldDescriptorProto) bool { + nullable := IsNullable(field) + if field.IsMessage() || IsCustomType(field) { + return nullable + } + if proto3 { + return false + } + return nullable || *field.Type == google_protobuf.FieldDescriptorProto_TYPE_BYTES +} + +func IsCustomType(field *google_protobuf.FieldDescriptorProto) bool { + typ := GetCustomType(field) + if len(typ) > 0 { + return true + } + return false +} + +func IsCastType(field *google_protobuf.FieldDescriptorProto) bool { + typ := GetCastType(field) + if len(typ) > 0 { + return true + } + return false +} + +func IsCastKey(field *google_protobuf.FieldDescriptorProto) bool { + typ := GetCastKey(field) + if len(typ) > 0 { + return true + } + return false +} + +func IsCastValue(field *google_protobuf.FieldDescriptorProto) bool { + typ := GetCastValue(field) + if len(typ) > 0 { + return true + } + return false +} + +func HasEnumDecl(file *google_protobuf.FileDescriptorProto, enum *google_protobuf.EnumDescriptorProto) bool { + return proto.GetBoolExtension(enum.Options, E_Enumdecl, proto.GetBoolExtension(file.Options, E_EnumdeclAll, true)) +} + +func HasTypeDecl(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Typedecl, proto.GetBoolExtension(file.Options, E_TypedeclAll, true)) +} + +func GetCustomType(field *google_protobuf.FieldDescriptorProto) string { + if field == nil { + return "" + } + if field.Options != nil { + v, err := proto.GetExtension(field.Options, E_Customtype) + if err == nil && v.(*string) != nil { + return *(v.(*string)) + } + } + return "" +} + +func GetCastType(field *google_protobuf.FieldDescriptorProto) string { + if field == nil { + return "" + } + if field.Options != nil { + v, err := proto.GetExtension(field.Options, E_Casttype) + if err == nil && v.(*string) != nil { + return *(v.(*string)) + } + } + return "" +} + +func GetCastKey(field *google_protobuf.FieldDescriptorProto) string { + if field == nil { + return "" + } + if field.Options != nil { + v, err := proto.GetExtension(field.Options, E_Castkey) + if err == nil && v.(*string) != nil { + return *(v.(*string)) + } + } + return "" +} + +func GetCastValue(field *google_protobuf.FieldDescriptorProto) string { + if field == nil { + return "" + } + if field.Options != nil { + v, err := proto.GetExtension(field.Options, E_Castvalue) + if err == nil && v.(*string) != nil { + return *(v.(*string)) + } + } + return "" +} + +func IsCustomName(field *google_protobuf.FieldDescriptorProto) bool { + name := GetCustomName(field) + if len(name) > 0 { + return true + } + return false +} + +func IsEnumCustomName(field *google_protobuf.EnumDescriptorProto) bool { + name := GetEnumCustomName(field) + if len(name) > 0 { + return true + } + return false +} + +func IsEnumValueCustomName(field *google_protobuf.EnumValueDescriptorProto) bool { + name := GetEnumValueCustomName(field) + if len(name) > 0 { + return true + } + return false +} + +func GetCustomName(field *google_protobuf.FieldDescriptorProto) string { + if field == nil { + return "" + } + if field.Options != nil { + v, err := proto.GetExtension(field.Options, E_Customname) + if err == nil && v.(*string) != nil { + return *(v.(*string)) + } + } + return "" +} + +func GetEnumCustomName(field *google_protobuf.EnumDescriptorProto) string { + if field == nil { + return "" + } + if field.Options != nil { + v, err := proto.GetExtension(field.Options, E_EnumCustomname) + if err == nil && v.(*string) != nil { + return *(v.(*string)) + } + } + return "" +} + +func GetEnumValueCustomName(field *google_protobuf.EnumValueDescriptorProto) string { + if field == nil { + return "" + } + if field.Options != nil { + v, err := proto.GetExtension(field.Options, E_EnumvalueCustomname) + if err == nil && v.(*string) != nil { + return *(v.(*string)) + } + } + return "" +} + +func GetJsonTag(field *google_protobuf.FieldDescriptorProto) *string { + if field == nil { + return nil + } + if field.Options != nil { + v, err := proto.GetExtension(field.Options, E_Jsontag) + if err == nil && v.(*string) != nil { + return (v.(*string)) + } + } + return nil +} + +func GetMoreTags(field *google_protobuf.FieldDescriptorProto) *string { + if field == nil { + return nil + } + if field.Options != nil { + v, err := proto.GetExtension(field.Options, E_Moretags) + if err == nil && v.(*string) != nil { + return (v.(*string)) + } + } + return nil +} + +type EnableFunc func(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool + +func EnabledGoEnumPrefix(file *google_protobuf.FileDescriptorProto, enum *google_protobuf.EnumDescriptorProto) bool { + return proto.GetBoolExtension(enum.Options, E_GoprotoEnumPrefix, proto.GetBoolExtension(file.Options, E_GoprotoEnumPrefixAll, true)) +} + +func EnabledGoStringer(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_GoprotoStringer, proto.GetBoolExtension(file.Options, E_GoprotoStringerAll, true)) +} + +func HasGoGetters(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_GoprotoGetters, proto.GetBoolExtension(file.Options, E_GoprotoGettersAll, true)) +} + +func IsUnion(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Onlyone, proto.GetBoolExtension(file.Options, E_OnlyoneAll, false)) +} + +func HasGoString(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Gostring, proto.GetBoolExtension(file.Options, E_GostringAll, false)) +} + +func HasEqual(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Equal, proto.GetBoolExtension(file.Options, E_EqualAll, false)) +} + +func HasVerboseEqual(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_VerboseEqual, proto.GetBoolExtension(file.Options, E_VerboseEqualAll, false)) +} + +func IsStringer(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Stringer, proto.GetBoolExtension(file.Options, E_StringerAll, false)) +} + +func IsFace(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Face, proto.GetBoolExtension(file.Options, E_FaceAll, false)) +} + +func HasDescription(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Description, proto.GetBoolExtension(file.Options, E_DescriptionAll, false)) +} + +func HasPopulate(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Populate, proto.GetBoolExtension(file.Options, E_PopulateAll, false)) +} + +func HasTestGen(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Testgen, proto.GetBoolExtension(file.Options, E_TestgenAll, false)) +} + +func HasBenchGen(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Benchgen, proto.GetBoolExtension(file.Options, E_BenchgenAll, false)) +} + +func IsMarshaler(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Marshaler, proto.GetBoolExtension(file.Options, E_MarshalerAll, false)) +} + +func IsUnmarshaler(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Unmarshaler, proto.GetBoolExtension(file.Options, E_UnmarshalerAll, false)) +} + +func IsStableMarshaler(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_StableMarshaler, proto.GetBoolExtension(file.Options, E_StableMarshalerAll, false)) +} + +func IsSizer(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Sizer, proto.GetBoolExtension(file.Options, E_SizerAll, false)) +} + +func IsProtoSizer(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Protosizer, proto.GetBoolExtension(file.Options, E_ProtosizerAll, false)) +} + +func IsGoEnumStringer(file *google_protobuf.FileDescriptorProto, enum *google_protobuf.EnumDescriptorProto) bool { + return proto.GetBoolExtension(enum.Options, E_GoprotoEnumStringer, proto.GetBoolExtension(file.Options, E_GoprotoEnumStringerAll, true)) +} + +func IsEnumStringer(file *google_protobuf.FileDescriptorProto, enum *google_protobuf.EnumDescriptorProto) bool { + return proto.GetBoolExtension(enum.Options, E_EnumStringer, proto.GetBoolExtension(file.Options, E_EnumStringerAll, false)) +} + +func IsUnsafeMarshaler(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_UnsafeMarshaler, proto.GetBoolExtension(file.Options, E_UnsafeMarshalerAll, false)) +} + +func IsUnsafeUnmarshaler(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_UnsafeUnmarshaler, proto.GetBoolExtension(file.Options, E_UnsafeUnmarshalerAll, false)) +} + +func HasExtensionsMap(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_GoprotoExtensionsMap, proto.GetBoolExtension(file.Options, E_GoprotoExtensionsMapAll, true)) +} + +func HasUnrecognized(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_GoprotoUnrecognized, proto.GetBoolExtension(file.Options, E_GoprotoUnrecognizedAll, true)) +} + +func IsProto3(file *google_protobuf.FileDescriptorProto) bool { + return file.GetSyntax() == "proto3" +} + +func ImportsGoGoProto(file *google_protobuf.FileDescriptorProto) bool { + return proto.GetBoolExtension(file.Options, E_GogoprotoImport, true) +} + +func HasCompare(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Compare, proto.GetBoolExtension(file.Options, E_CompareAll, false)) +} + +func RegistersGolangProto(file *google_protobuf.FileDescriptorProto) bool { + return proto.GetBoolExtension(file.Options, E_GoprotoRegistration, false) +} + +func HasMessageName(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Messagename, proto.GetBoolExtension(file.Options, E_MessagenameAll, false)) +} + +func HasSizecache(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_GoprotoSizecache, proto.GetBoolExtension(file.Options, E_GoprotoSizecacheAll, true)) +} + +func HasUnkeyed(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_GoprotoUnkeyed, proto.GetBoolExtension(file.Options, E_GoprotoUnkeyedAll, true)) +} diff --git a/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/Makefile b/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/Makefile new file mode 100644 index 0000000000..3496dc99d5 --- /dev/null +++ b/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/Makefile @@ -0,0 +1,36 @@ +# Go support for Protocol Buffers - Google's data interchange format +# +# Copyright 2010 The Go Authors. All rights reserved. +# https://github.com/golang/protobuf +# +# 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. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# 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 +# OWNER 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. + +regenerate: + go install github.com/gogo/protobuf/protoc-gen-gogo + go install github.com/gogo/protobuf/protoc-gen-gostring + protoc --gogo_out=. -I=../../protobuf/google/protobuf ../../protobuf/google/protobuf/descriptor.proto + protoc --gostring_out=. -I=../../protobuf/google/protobuf ../../protobuf/google/protobuf/descriptor.proto diff --git a/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor.go b/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor.go new file mode 100644 index 0000000000..a85bf1984c --- /dev/null +++ b/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor.go @@ -0,0 +1,118 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2016 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// 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. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// 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 +// OWNER 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. + +// Package descriptor provides functions for obtaining protocol buffer +// descriptors for generated Go types. +// +// These functions cannot go in package proto because they depend on the +// generated protobuf descriptor messages, which themselves depend on proto. +package descriptor + +import ( + "bytes" + "compress/gzip" + "fmt" + "io/ioutil" + + "github.com/gogo/protobuf/proto" +) + +// extractFile extracts a FileDescriptorProto from a gzip'd buffer. +func extractFile(gz []byte) (*FileDescriptorProto, error) { + r, err := gzip.NewReader(bytes.NewReader(gz)) + if err != nil { + return nil, fmt.Errorf("failed to open gzip reader: %v", err) + } + defer r.Close() + + b, err := ioutil.ReadAll(r) + if err != nil { + return nil, fmt.Errorf("failed to uncompress descriptor: %v", err) + } + + fd := new(FileDescriptorProto) + if err := proto.Unmarshal(b, fd); err != nil { + return nil, fmt.Errorf("malformed FileDescriptorProto: %v", err) + } + + return fd, nil +} + +// Message is a proto.Message with a method to return its descriptor. +// +// Message types generated by the protocol compiler always satisfy +// the Message interface. +type Message interface { + proto.Message + Descriptor() ([]byte, []int) +} + +// ForMessage returns a FileDescriptorProto and a DescriptorProto from within it +// describing the given message. +func ForMessage(msg Message) (fd *FileDescriptorProto, md *DescriptorProto) { + gz, path := msg.Descriptor() + fd, err := extractFile(gz) + if err != nil { + panic(fmt.Sprintf("invalid FileDescriptorProto for %T: %v", msg, err)) + } + + md = fd.MessageType[path[0]] + for _, i := range path[1:] { + md = md.NestedType[i] + } + return fd, md +} + +// Is this field a scalar numeric type? +func (field *FieldDescriptorProto) IsScalar() bool { + if field.Type == nil { + return false + } + switch *field.Type { + case FieldDescriptorProto_TYPE_DOUBLE, + FieldDescriptorProto_TYPE_FLOAT, + FieldDescriptorProto_TYPE_INT64, + FieldDescriptorProto_TYPE_UINT64, + FieldDescriptorProto_TYPE_INT32, + FieldDescriptorProto_TYPE_FIXED64, + FieldDescriptorProto_TYPE_FIXED32, + FieldDescriptorProto_TYPE_BOOL, + FieldDescriptorProto_TYPE_UINT32, + FieldDescriptorProto_TYPE_ENUM, + FieldDescriptorProto_TYPE_SFIXED32, + FieldDescriptorProto_TYPE_SFIXED64, + FieldDescriptorProto_TYPE_SINT32, + FieldDescriptorProto_TYPE_SINT64: + return true + default: + return false + } +} diff --git a/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor.pb.go b/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor.pb.go new file mode 100644 index 0000000000..18b2a3318a --- /dev/null +++ b/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor.pb.go @@ -0,0 +1,2865 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: descriptor.proto + +package descriptor + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type FieldDescriptorProto_Type int32 + +const ( + // 0 is reserved for errors. + // Order is weird for historical reasons. + FieldDescriptorProto_TYPE_DOUBLE FieldDescriptorProto_Type = 1 + FieldDescriptorProto_TYPE_FLOAT FieldDescriptorProto_Type = 2 + // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if + // negative values are likely. + FieldDescriptorProto_TYPE_INT64 FieldDescriptorProto_Type = 3 + FieldDescriptorProto_TYPE_UINT64 FieldDescriptorProto_Type = 4 + // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if + // negative values are likely. + FieldDescriptorProto_TYPE_INT32 FieldDescriptorProto_Type = 5 + FieldDescriptorProto_TYPE_FIXED64 FieldDescriptorProto_Type = 6 + FieldDescriptorProto_TYPE_FIXED32 FieldDescriptorProto_Type = 7 + FieldDescriptorProto_TYPE_BOOL FieldDescriptorProto_Type = 8 + FieldDescriptorProto_TYPE_STRING FieldDescriptorProto_Type = 9 + // Tag-delimited aggregate. + // Group type is deprecated and not supported in proto3. However, Proto3 + // implementations should still be able to parse the group wire format and + // treat group fields as unknown fields. + FieldDescriptorProto_TYPE_GROUP FieldDescriptorProto_Type = 10 + FieldDescriptorProto_TYPE_MESSAGE FieldDescriptorProto_Type = 11 + // New in version 2. + FieldDescriptorProto_TYPE_BYTES FieldDescriptorProto_Type = 12 + FieldDescriptorProto_TYPE_UINT32 FieldDescriptorProto_Type = 13 + FieldDescriptorProto_TYPE_ENUM FieldDescriptorProto_Type = 14 + FieldDescriptorProto_TYPE_SFIXED32 FieldDescriptorProto_Type = 15 + FieldDescriptorProto_TYPE_SFIXED64 FieldDescriptorProto_Type = 16 + FieldDescriptorProto_TYPE_SINT32 FieldDescriptorProto_Type = 17 + FieldDescriptorProto_TYPE_SINT64 FieldDescriptorProto_Type = 18 +) + +var FieldDescriptorProto_Type_name = map[int32]string{ + 1: "TYPE_DOUBLE", + 2: "TYPE_FLOAT", + 3: "TYPE_INT64", + 4: "TYPE_UINT64", + 5: "TYPE_INT32", + 6: "TYPE_FIXED64", + 7: "TYPE_FIXED32", + 8: "TYPE_BOOL", + 9: "TYPE_STRING", + 10: "TYPE_GROUP", + 11: "TYPE_MESSAGE", + 12: "TYPE_BYTES", + 13: "TYPE_UINT32", + 14: "TYPE_ENUM", + 15: "TYPE_SFIXED32", + 16: "TYPE_SFIXED64", + 17: "TYPE_SINT32", + 18: "TYPE_SINT64", +} + +var FieldDescriptorProto_Type_value = map[string]int32{ + "TYPE_DOUBLE": 1, + "TYPE_FLOAT": 2, + "TYPE_INT64": 3, + "TYPE_UINT64": 4, + "TYPE_INT32": 5, + "TYPE_FIXED64": 6, + "TYPE_FIXED32": 7, + "TYPE_BOOL": 8, + "TYPE_STRING": 9, + "TYPE_GROUP": 10, + "TYPE_MESSAGE": 11, + "TYPE_BYTES": 12, + "TYPE_UINT32": 13, + "TYPE_ENUM": 14, + "TYPE_SFIXED32": 15, + "TYPE_SFIXED64": 16, + "TYPE_SINT32": 17, + "TYPE_SINT64": 18, +} + +func (x FieldDescriptorProto_Type) Enum() *FieldDescriptorProto_Type { + p := new(FieldDescriptorProto_Type) + *p = x + return p +} + +func (x FieldDescriptorProto_Type) String() string { + return proto.EnumName(FieldDescriptorProto_Type_name, int32(x)) +} + +func (x *FieldDescriptorProto_Type) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(FieldDescriptorProto_Type_value, data, "FieldDescriptorProto_Type") + if err != nil { + return err + } + *x = FieldDescriptorProto_Type(value) + return nil +} + +func (FieldDescriptorProto_Type) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{4, 0} +} + +type FieldDescriptorProto_Label int32 + +const ( + // 0 is reserved for errors + FieldDescriptorProto_LABEL_OPTIONAL FieldDescriptorProto_Label = 1 + FieldDescriptorProto_LABEL_REQUIRED FieldDescriptorProto_Label = 2 + FieldDescriptorProto_LABEL_REPEATED FieldDescriptorProto_Label = 3 +) + +var FieldDescriptorProto_Label_name = map[int32]string{ + 1: "LABEL_OPTIONAL", + 2: "LABEL_REQUIRED", + 3: "LABEL_REPEATED", +} + +var FieldDescriptorProto_Label_value = map[string]int32{ + "LABEL_OPTIONAL": 1, + "LABEL_REQUIRED": 2, + "LABEL_REPEATED": 3, +} + +func (x FieldDescriptorProto_Label) Enum() *FieldDescriptorProto_Label { + p := new(FieldDescriptorProto_Label) + *p = x + return p +} + +func (x FieldDescriptorProto_Label) String() string { + return proto.EnumName(FieldDescriptorProto_Label_name, int32(x)) +} + +func (x *FieldDescriptorProto_Label) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(FieldDescriptorProto_Label_value, data, "FieldDescriptorProto_Label") + if err != nil { + return err + } + *x = FieldDescriptorProto_Label(value) + return nil +} + +func (FieldDescriptorProto_Label) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{4, 1} +} + +// Generated classes can be optimized for speed or code size. +type FileOptions_OptimizeMode int32 + +const ( + FileOptions_SPEED FileOptions_OptimizeMode = 1 + // etc. + FileOptions_CODE_SIZE FileOptions_OptimizeMode = 2 + FileOptions_LITE_RUNTIME FileOptions_OptimizeMode = 3 +) + +var FileOptions_OptimizeMode_name = map[int32]string{ + 1: "SPEED", + 2: "CODE_SIZE", + 3: "LITE_RUNTIME", +} + +var FileOptions_OptimizeMode_value = map[string]int32{ + "SPEED": 1, + "CODE_SIZE": 2, + "LITE_RUNTIME": 3, +} + +func (x FileOptions_OptimizeMode) Enum() *FileOptions_OptimizeMode { + p := new(FileOptions_OptimizeMode) + *p = x + return p +} + +func (x FileOptions_OptimizeMode) String() string { + return proto.EnumName(FileOptions_OptimizeMode_name, int32(x)) +} + +func (x *FileOptions_OptimizeMode) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(FileOptions_OptimizeMode_value, data, "FileOptions_OptimizeMode") + if err != nil { + return err + } + *x = FileOptions_OptimizeMode(value) + return nil +} + +func (FileOptions_OptimizeMode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{10, 0} +} + +type FieldOptions_CType int32 + +const ( + // Default mode. + FieldOptions_STRING FieldOptions_CType = 0 + FieldOptions_CORD FieldOptions_CType = 1 + FieldOptions_STRING_PIECE FieldOptions_CType = 2 +) + +var FieldOptions_CType_name = map[int32]string{ + 0: "STRING", + 1: "CORD", + 2: "STRING_PIECE", +} + +var FieldOptions_CType_value = map[string]int32{ + "STRING": 0, + "CORD": 1, + "STRING_PIECE": 2, +} + +func (x FieldOptions_CType) Enum() *FieldOptions_CType { + p := new(FieldOptions_CType) + *p = x + return p +} + +func (x FieldOptions_CType) String() string { + return proto.EnumName(FieldOptions_CType_name, int32(x)) +} + +func (x *FieldOptions_CType) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(FieldOptions_CType_value, data, "FieldOptions_CType") + if err != nil { + return err + } + *x = FieldOptions_CType(value) + return nil +} + +func (FieldOptions_CType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{12, 0} +} + +type FieldOptions_JSType int32 + +const ( + // Use the default type. + FieldOptions_JS_NORMAL FieldOptions_JSType = 0 + // Use JavaScript strings. + FieldOptions_JS_STRING FieldOptions_JSType = 1 + // Use JavaScript numbers. + FieldOptions_JS_NUMBER FieldOptions_JSType = 2 +) + +var FieldOptions_JSType_name = map[int32]string{ + 0: "JS_NORMAL", + 1: "JS_STRING", + 2: "JS_NUMBER", +} + +var FieldOptions_JSType_value = map[string]int32{ + "JS_NORMAL": 0, + "JS_STRING": 1, + "JS_NUMBER": 2, +} + +func (x FieldOptions_JSType) Enum() *FieldOptions_JSType { + p := new(FieldOptions_JSType) + *p = x + return p +} + +func (x FieldOptions_JSType) String() string { + return proto.EnumName(FieldOptions_JSType_name, int32(x)) +} + +func (x *FieldOptions_JSType) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(FieldOptions_JSType_value, data, "FieldOptions_JSType") + if err != nil { + return err + } + *x = FieldOptions_JSType(value) + return nil +} + +func (FieldOptions_JSType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{12, 1} +} + +// Is this method side-effect-free (or safe in HTTP parlance), or idempotent, +// or neither? HTTP based RPC implementation may choose GET verb for safe +// methods, and PUT verb for idempotent methods instead of the default POST. +type MethodOptions_IdempotencyLevel int32 + +const ( + MethodOptions_IDEMPOTENCY_UNKNOWN MethodOptions_IdempotencyLevel = 0 + MethodOptions_NO_SIDE_EFFECTS MethodOptions_IdempotencyLevel = 1 + MethodOptions_IDEMPOTENT MethodOptions_IdempotencyLevel = 2 +) + +var MethodOptions_IdempotencyLevel_name = map[int32]string{ + 0: "IDEMPOTENCY_UNKNOWN", + 1: "NO_SIDE_EFFECTS", + 2: "IDEMPOTENT", +} + +var MethodOptions_IdempotencyLevel_value = map[string]int32{ + "IDEMPOTENCY_UNKNOWN": 0, + "NO_SIDE_EFFECTS": 1, + "IDEMPOTENT": 2, +} + +func (x MethodOptions_IdempotencyLevel) Enum() *MethodOptions_IdempotencyLevel { + p := new(MethodOptions_IdempotencyLevel) + *p = x + return p +} + +func (x MethodOptions_IdempotencyLevel) String() string { + return proto.EnumName(MethodOptions_IdempotencyLevel_name, int32(x)) +} + +func (x *MethodOptions_IdempotencyLevel) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(MethodOptions_IdempotencyLevel_value, data, "MethodOptions_IdempotencyLevel") + if err != nil { + return err + } + *x = MethodOptions_IdempotencyLevel(value) + return nil +} + +func (MethodOptions_IdempotencyLevel) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{17, 0} +} + +// The protocol compiler can output a FileDescriptorSet containing the .proto +// files it parses. +type FileDescriptorSet struct { + File []*FileDescriptorProto `protobuf:"bytes,1,rep,name=file" json:"file,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *FileDescriptorSet) Reset() { *m = FileDescriptorSet{} } +func (m *FileDescriptorSet) String() string { return proto.CompactTextString(m) } +func (*FileDescriptorSet) ProtoMessage() {} +func (*FileDescriptorSet) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{0} +} +func (m *FileDescriptorSet) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FileDescriptorSet.Unmarshal(m, b) +} +func (m *FileDescriptorSet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FileDescriptorSet.Marshal(b, m, deterministic) +} +func (m *FileDescriptorSet) XXX_Merge(src proto.Message) { + xxx_messageInfo_FileDescriptorSet.Merge(m, src) +} +func (m *FileDescriptorSet) XXX_Size() int { + return xxx_messageInfo_FileDescriptorSet.Size(m) +} +func (m *FileDescriptorSet) XXX_DiscardUnknown() { + xxx_messageInfo_FileDescriptorSet.DiscardUnknown(m) +} + +var xxx_messageInfo_FileDescriptorSet proto.InternalMessageInfo + +func (m *FileDescriptorSet) GetFile() []*FileDescriptorProto { + if m != nil { + return m.File + } + return nil +} + +// Describes a complete .proto file. +type FileDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Package *string `protobuf:"bytes,2,opt,name=package" json:"package,omitempty"` + // Names of files imported by this file. + Dependency []string `protobuf:"bytes,3,rep,name=dependency" json:"dependency,omitempty"` + // Indexes of the public imported files in the dependency list above. + PublicDependency []int32 `protobuf:"varint,10,rep,name=public_dependency,json=publicDependency" json:"public_dependency,omitempty"` + // Indexes of the weak imported files in the dependency list. + // For Google-internal migration only. Do not use. + WeakDependency []int32 `protobuf:"varint,11,rep,name=weak_dependency,json=weakDependency" json:"weak_dependency,omitempty"` + // All top-level definitions in this file. + MessageType []*DescriptorProto `protobuf:"bytes,4,rep,name=message_type,json=messageType" json:"message_type,omitempty"` + EnumType []*EnumDescriptorProto `protobuf:"bytes,5,rep,name=enum_type,json=enumType" json:"enum_type,omitempty"` + Service []*ServiceDescriptorProto `protobuf:"bytes,6,rep,name=service" json:"service,omitempty"` + Extension []*FieldDescriptorProto `protobuf:"bytes,7,rep,name=extension" json:"extension,omitempty"` + Options *FileOptions `protobuf:"bytes,8,opt,name=options" json:"options,omitempty"` + // This field contains optional information about the original source code. + // You may safely remove this entire field without harming runtime + // functionality of the descriptors -- the information is needed only by + // development tools. + SourceCodeInfo *SourceCodeInfo `protobuf:"bytes,9,opt,name=source_code_info,json=sourceCodeInfo" json:"source_code_info,omitempty"` + // The syntax of the proto file. + // The supported values are "proto2" and "proto3". + Syntax *string `protobuf:"bytes,12,opt,name=syntax" json:"syntax,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *FileDescriptorProto) Reset() { *m = FileDescriptorProto{} } +func (m *FileDescriptorProto) String() string { return proto.CompactTextString(m) } +func (*FileDescriptorProto) ProtoMessage() {} +func (*FileDescriptorProto) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{1} +} +func (m *FileDescriptorProto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FileDescriptorProto.Unmarshal(m, b) +} +func (m *FileDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FileDescriptorProto.Marshal(b, m, deterministic) +} +func (m *FileDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_FileDescriptorProto.Merge(m, src) +} +func (m *FileDescriptorProto) XXX_Size() int { + return xxx_messageInfo_FileDescriptorProto.Size(m) +} +func (m *FileDescriptorProto) XXX_DiscardUnknown() { + xxx_messageInfo_FileDescriptorProto.DiscardUnknown(m) +} + +var xxx_messageInfo_FileDescriptorProto proto.InternalMessageInfo + +func (m *FileDescriptorProto) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *FileDescriptorProto) GetPackage() string { + if m != nil && m.Package != nil { + return *m.Package + } + return "" +} + +func (m *FileDescriptorProto) GetDependency() []string { + if m != nil { + return m.Dependency + } + return nil +} + +func (m *FileDescriptorProto) GetPublicDependency() []int32 { + if m != nil { + return m.PublicDependency + } + return nil +} + +func (m *FileDescriptorProto) GetWeakDependency() []int32 { + if m != nil { + return m.WeakDependency + } + return nil +} + +func (m *FileDescriptorProto) GetMessageType() []*DescriptorProto { + if m != nil { + return m.MessageType + } + return nil +} + +func (m *FileDescriptorProto) GetEnumType() []*EnumDescriptorProto { + if m != nil { + return m.EnumType + } + return nil +} + +func (m *FileDescriptorProto) GetService() []*ServiceDescriptorProto { + if m != nil { + return m.Service + } + return nil +} + +func (m *FileDescriptorProto) GetExtension() []*FieldDescriptorProto { + if m != nil { + return m.Extension + } + return nil +} + +func (m *FileDescriptorProto) GetOptions() *FileOptions { + if m != nil { + return m.Options + } + return nil +} + +func (m *FileDescriptorProto) GetSourceCodeInfo() *SourceCodeInfo { + if m != nil { + return m.SourceCodeInfo + } + return nil +} + +func (m *FileDescriptorProto) GetSyntax() string { + if m != nil && m.Syntax != nil { + return *m.Syntax + } + return "" +} + +// Describes a message type. +type DescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Field []*FieldDescriptorProto `protobuf:"bytes,2,rep,name=field" json:"field,omitempty"` + Extension []*FieldDescriptorProto `protobuf:"bytes,6,rep,name=extension" json:"extension,omitempty"` + NestedType []*DescriptorProto `protobuf:"bytes,3,rep,name=nested_type,json=nestedType" json:"nested_type,omitempty"` + EnumType []*EnumDescriptorProto `protobuf:"bytes,4,rep,name=enum_type,json=enumType" json:"enum_type,omitempty"` + ExtensionRange []*DescriptorProto_ExtensionRange `protobuf:"bytes,5,rep,name=extension_range,json=extensionRange" json:"extension_range,omitempty"` + OneofDecl []*OneofDescriptorProto `protobuf:"bytes,8,rep,name=oneof_decl,json=oneofDecl" json:"oneof_decl,omitempty"` + Options *MessageOptions `protobuf:"bytes,7,opt,name=options" json:"options,omitempty"` + ReservedRange []*DescriptorProto_ReservedRange `protobuf:"bytes,9,rep,name=reserved_range,json=reservedRange" json:"reserved_range,omitempty"` + // Reserved field names, which may not be used by fields in the same message. + // A given name may only be reserved once. + ReservedName []string `protobuf:"bytes,10,rep,name=reserved_name,json=reservedName" json:"reserved_name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DescriptorProto) Reset() { *m = DescriptorProto{} } +func (m *DescriptorProto) String() string { return proto.CompactTextString(m) } +func (*DescriptorProto) ProtoMessage() {} +func (*DescriptorProto) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{2} +} +func (m *DescriptorProto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DescriptorProto.Unmarshal(m, b) +} +func (m *DescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DescriptorProto.Marshal(b, m, deterministic) +} +func (m *DescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_DescriptorProto.Merge(m, src) +} +func (m *DescriptorProto) XXX_Size() int { + return xxx_messageInfo_DescriptorProto.Size(m) +} +func (m *DescriptorProto) XXX_DiscardUnknown() { + xxx_messageInfo_DescriptorProto.DiscardUnknown(m) +} + +var xxx_messageInfo_DescriptorProto proto.InternalMessageInfo + +func (m *DescriptorProto) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *DescriptorProto) GetField() []*FieldDescriptorProto { + if m != nil { + return m.Field + } + return nil +} + +func (m *DescriptorProto) GetExtension() []*FieldDescriptorProto { + if m != nil { + return m.Extension + } + return nil +} + +func (m *DescriptorProto) GetNestedType() []*DescriptorProto { + if m != nil { + return m.NestedType + } + return nil +} + +func (m *DescriptorProto) GetEnumType() []*EnumDescriptorProto { + if m != nil { + return m.EnumType + } + return nil +} + +func (m *DescriptorProto) GetExtensionRange() []*DescriptorProto_ExtensionRange { + if m != nil { + return m.ExtensionRange + } + return nil +} + +func (m *DescriptorProto) GetOneofDecl() []*OneofDescriptorProto { + if m != nil { + return m.OneofDecl + } + return nil +} + +func (m *DescriptorProto) GetOptions() *MessageOptions { + if m != nil { + return m.Options + } + return nil +} + +func (m *DescriptorProto) GetReservedRange() []*DescriptorProto_ReservedRange { + if m != nil { + return m.ReservedRange + } + return nil +} + +func (m *DescriptorProto) GetReservedName() []string { + if m != nil { + return m.ReservedName + } + return nil +} + +type DescriptorProto_ExtensionRange struct { + Start *int32 `protobuf:"varint,1,opt,name=start" json:"start,omitempty"` + End *int32 `protobuf:"varint,2,opt,name=end" json:"end,omitempty"` + Options *ExtensionRangeOptions `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DescriptorProto_ExtensionRange) Reset() { *m = DescriptorProto_ExtensionRange{} } +func (m *DescriptorProto_ExtensionRange) String() string { return proto.CompactTextString(m) } +func (*DescriptorProto_ExtensionRange) ProtoMessage() {} +func (*DescriptorProto_ExtensionRange) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{2, 0} +} +func (m *DescriptorProto_ExtensionRange) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DescriptorProto_ExtensionRange.Unmarshal(m, b) +} +func (m *DescriptorProto_ExtensionRange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DescriptorProto_ExtensionRange.Marshal(b, m, deterministic) +} +func (m *DescriptorProto_ExtensionRange) XXX_Merge(src proto.Message) { + xxx_messageInfo_DescriptorProto_ExtensionRange.Merge(m, src) +} +func (m *DescriptorProto_ExtensionRange) XXX_Size() int { + return xxx_messageInfo_DescriptorProto_ExtensionRange.Size(m) +} +func (m *DescriptorProto_ExtensionRange) XXX_DiscardUnknown() { + xxx_messageInfo_DescriptorProto_ExtensionRange.DiscardUnknown(m) +} + +var xxx_messageInfo_DescriptorProto_ExtensionRange proto.InternalMessageInfo + +func (m *DescriptorProto_ExtensionRange) GetStart() int32 { + if m != nil && m.Start != nil { + return *m.Start + } + return 0 +} + +func (m *DescriptorProto_ExtensionRange) GetEnd() int32 { + if m != nil && m.End != nil { + return *m.End + } + return 0 +} + +func (m *DescriptorProto_ExtensionRange) GetOptions() *ExtensionRangeOptions { + if m != nil { + return m.Options + } + return nil +} + +// Range of reserved tag numbers. Reserved tag numbers may not be used by +// fields or extension ranges in the same message. Reserved ranges may +// not overlap. +type DescriptorProto_ReservedRange struct { + Start *int32 `protobuf:"varint,1,opt,name=start" json:"start,omitempty"` + End *int32 `protobuf:"varint,2,opt,name=end" json:"end,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DescriptorProto_ReservedRange) Reset() { *m = DescriptorProto_ReservedRange{} } +func (m *DescriptorProto_ReservedRange) String() string { return proto.CompactTextString(m) } +func (*DescriptorProto_ReservedRange) ProtoMessage() {} +func (*DescriptorProto_ReservedRange) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{2, 1} +} +func (m *DescriptorProto_ReservedRange) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DescriptorProto_ReservedRange.Unmarshal(m, b) +} +func (m *DescriptorProto_ReservedRange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DescriptorProto_ReservedRange.Marshal(b, m, deterministic) +} +func (m *DescriptorProto_ReservedRange) XXX_Merge(src proto.Message) { + xxx_messageInfo_DescriptorProto_ReservedRange.Merge(m, src) +} +func (m *DescriptorProto_ReservedRange) XXX_Size() int { + return xxx_messageInfo_DescriptorProto_ReservedRange.Size(m) +} +func (m *DescriptorProto_ReservedRange) XXX_DiscardUnknown() { + xxx_messageInfo_DescriptorProto_ReservedRange.DiscardUnknown(m) +} + +var xxx_messageInfo_DescriptorProto_ReservedRange proto.InternalMessageInfo + +func (m *DescriptorProto_ReservedRange) GetStart() int32 { + if m != nil && m.Start != nil { + return *m.Start + } + return 0 +} + +func (m *DescriptorProto_ReservedRange) GetEnd() int32 { + if m != nil && m.End != nil { + return *m.End + } + return 0 +} + +type ExtensionRangeOptions struct { + // The parser stores options it doesn't recognize here. See above. + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ExtensionRangeOptions) Reset() { *m = ExtensionRangeOptions{} } +func (m *ExtensionRangeOptions) String() string { return proto.CompactTextString(m) } +func (*ExtensionRangeOptions) ProtoMessage() {} +func (*ExtensionRangeOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{3} +} + +var extRange_ExtensionRangeOptions = []proto.ExtensionRange{ + {Start: 1000, End: 536870911}, +} + +func (*ExtensionRangeOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_ExtensionRangeOptions +} + +func (m *ExtensionRangeOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ExtensionRangeOptions.Unmarshal(m, b) +} +func (m *ExtensionRangeOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ExtensionRangeOptions.Marshal(b, m, deterministic) +} +func (m *ExtensionRangeOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExtensionRangeOptions.Merge(m, src) +} +func (m *ExtensionRangeOptions) XXX_Size() int { + return xxx_messageInfo_ExtensionRangeOptions.Size(m) +} +func (m *ExtensionRangeOptions) XXX_DiscardUnknown() { + xxx_messageInfo_ExtensionRangeOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_ExtensionRangeOptions proto.InternalMessageInfo + +func (m *ExtensionRangeOptions) GetUninterpretedOption() []*UninterpretedOption { + if m != nil { + return m.UninterpretedOption + } + return nil +} + +// Describes a field within a message. +type FieldDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Number *int32 `protobuf:"varint,3,opt,name=number" json:"number,omitempty"` + Label *FieldDescriptorProto_Label `protobuf:"varint,4,opt,name=label,enum=google.protobuf.FieldDescriptorProto_Label" json:"label,omitempty"` + // If type_name is set, this need not be set. If both this and type_name + // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP. + Type *FieldDescriptorProto_Type `protobuf:"varint,5,opt,name=type,enum=google.protobuf.FieldDescriptorProto_Type" json:"type,omitempty"` + // For message and enum types, this is the name of the type. If the name + // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping + // rules are used to find the type (i.e. first the nested types within this + // message are searched, then within the parent, on up to the root + // namespace). + TypeName *string `protobuf:"bytes,6,opt,name=type_name,json=typeName" json:"type_name,omitempty"` + // For extensions, this is the name of the type being extended. It is + // resolved in the same manner as type_name. + Extendee *string `protobuf:"bytes,2,opt,name=extendee" json:"extendee,omitempty"` + // For numeric types, contains the original text representation of the value. + // For booleans, "true" or "false". + // For strings, contains the default text contents (not escaped in any way). + // For bytes, contains the C escaped value. All bytes >= 128 are escaped. + // TODO(kenton): Base-64 encode? + DefaultValue *string `protobuf:"bytes,7,opt,name=default_value,json=defaultValue" json:"default_value,omitempty"` + // If set, gives the index of a oneof in the containing type's oneof_decl + // list. This field is a member of that oneof. + OneofIndex *int32 `protobuf:"varint,9,opt,name=oneof_index,json=oneofIndex" json:"oneof_index,omitempty"` + // JSON name of this field. The value is set by protocol compiler. If the + // user has set a "json_name" option on this field, that option's value + // will be used. Otherwise, it's deduced from the field's name by converting + // it to camelCase. + JsonName *string `protobuf:"bytes,10,opt,name=json_name,json=jsonName" json:"json_name,omitempty"` + Options *FieldOptions `protobuf:"bytes,8,opt,name=options" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *FieldDescriptorProto) Reset() { *m = FieldDescriptorProto{} } +func (m *FieldDescriptorProto) String() string { return proto.CompactTextString(m) } +func (*FieldDescriptorProto) ProtoMessage() {} +func (*FieldDescriptorProto) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{4} +} +func (m *FieldDescriptorProto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FieldDescriptorProto.Unmarshal(m, b) +} +func (m *FieldDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FieldDescriptorProto.Marshal(b, m, deterministic) +} +func (m *FieldDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_FieldDescriptorProto.Merge(m, src) +} +func (m *FieldDescriptorProto) XXX_Size() int { + return xxx_messageInfo_FieldDescriptorProto.Size(m) +} +func (m *FieldDescriptorProto) XXX_DiscardUnknown() { + xxx_messageInfo_FieldDescriptorProto.DiscardUnknown(m) +} + +var xxx_messageInfo_FieldDescriptorProto proto.InternalMessageInfo + +func (m *FieldDescriptorProto) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *FieldDescriptorProto) GetNumber() int32 { + if m != nil && m.Number != nil { + return *m.Number + } + return 0 +} + +func (m *FieldDescriptorProto) GetLabel() FieldDescriptorProto_Label { + if m != nil && m.Label != nil { + return *m.Label + } + return FieldDescriptorProto_LABEL_OPTIONAL +} + +func (m *FieldDescriptorProto) GetType() FieldDescriptorProto_Type { + if m != nil && m.Type != nil { + return *m.Type + } + return FieldDescriptorProto_TYPE_DOUBLE +} + +func (m *FieldDescriptorProto) GetTypeName() string { + if m != nil && m.TypeName != nil { + return *m.TypeName + } + return "" +} + +func (m *FieldDescriptorProto) GetExtendee() string { + if m != nil && m.Extendee != nil { + return *m.Extendee + } + return "" +} + +func (m *FieldDescriptorProto) GetDefaultValue() string { + if m != nil && m.DefaultValue != nil { + return *m.DefaultValue + } + return "" +} + +func (m *FieldDescriptorProto) GetOneofIndex() int32 { + if m != nil && m.OneofIndex != nil { + return *m.OneofIndex + } + return 0 +} + +func (m *FieldDescriptorProto) GetJsonName() string { + if m != nil && m.JsonName != nil { + return *m.JsonName + } + return "" +} + +func (m *FieldDescriptorProto) GetOptions() *FieldOptions { + if m != nil { + return m.Options + } + return nil +} + +// Describes a oneof. +type OneofDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Options *OneofOptions `protobuf:"bytes,2,opt,name=options" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *OneofDescriptorProto) Reset() { *m = OneofDescriptorProto{} } +func (m *OneofDescriptorProto) String() string { return proto.CompactTextString(m) } +func (*OneofDescriptorProto) ProtoMessage() {} +func (*OneofDescriptorProto) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{5} +} +func (m *OneofDescriptorProto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OneofDescriptorProto.Unmarshal(m, b) +} +func (m *OneofDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OneofDescriptorProto.Marshal(b, m, deterministic) +} +func (m *OneofDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_OneofDescriptorProto.Merge(m, src) +} +func (m *OneofDescriptorProto) XXX_Size() int { + return xxx_messageInfo_OneofDescriptorProto.Size(m) +} +func (m *OneofDescriptorProto) XXX_DiscardUnknown() { + xxx_messageInfo_OneofDescriptorProto.DiscardUnknown(m) +} + +var xxx_messageInfo_OneofDescriptorProto proto.InternalMessageInfo + +func (m *OneofDescriptorProto) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *OneofDescriptorProto) GetOptions() *OneofOptions { + if m != nil { + return m.Options + } + return nil +} + +// Describes an enum type. +type EnumDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Value []*EnumValueDescriptorProto `protobuf:"bytes,2,rep,name=value" json:"value,omitempty"` + Options *EnumOptions `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` + // Range of reserved numeric values. Reserved numeric values may not be used + // by enum values in the same enum declaration. Reserved ranges may not + // overlap. + ReservedRange []*EnumDescriptorProto_EnumReservedRange `protobuf:"bytes,4,rep,name=reserved_range,json=reservedRange" json:"reserved_range,omitempty"` + // Reserved enum value names, which may not be reused. A given name may only + // be reserved once. + ReservedName []string `protobuf:"bytes,5,rep,name=reserved_name,json=reservedName" json:"reserved_name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EnumDescriptorProto) Reset() { *m = EnumDescriptorProto{} } +func (m *EnumDescriptorProto) String() string { return proto.CompactTextString(m) } +func (*EnumDescriptorProto) ProtoMessage() {} +func (*EnumDescriptorProto) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{6} +} +func (m *EnumDescriptorProto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EnumDescriptorProto.Unmarshal(m, b) +} +func (m *EnumDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EnumDescriptorProto.Marshal(b, m, deterministic) +} +func (m *EnumDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnumDescriptorProto.Merge(m, src) +} +func (m *EnumDescriptorProto) XXX_Size() int { + return xxx_messageInfo_EnumDescriptorProto.Size(m) +} +func (m *EnumDescriptorProto) XXX_DiscardUnknown() { + xxx_messageInfo_EnumDescriptorProto.DiscardUnknown(m) +} + +var xxx_messageInfo_EnumDescriptorProto proto.InternalMessageInfo + +func (m *EnumDescriptorProto) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *EnumDescriptorProto) GetValue() []*EnumValueDescriptorProto { + if m != nil { + return m.Value + } + return nil +} + +func (m *EnumDescriptorProto) GetOptions() *EnumOptions { + if m != nil { + return m.Options + } + return nil +} + +func (m *EnumDescriptorProto) GetReservedRange() []*EnumDescriptorProto_EnumReservedRange { + if m != nil { + return m.ReservedRange + } + return nil +} + +func (m *EnumDescriptorProto) GetReservedName() []string { + if m != nil { + return m.ReservedName + } + return nil +} + +// Range of reserved numeric values. Reserved values may not be used by +// entries in the same enum. Reserved ranges may not overlap. +// +// Note that this is distinct from DescriptorProto.ReservedRange in that it +// is inclusive such that it can appropriately represent the entire int32 +// domain. +type EnumDescriptorProto_EnumReservedRange struct { + Start *int32 `protobuf:"varint,1,opt,name=start" json:"start,omitempty"` + End *int32 `protobuf:"varint,2,opt,name=end" json:"end,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EnumDescriptorProto_EnumReservedRange) Reset() { *m = EnumDescriptorProto_EnumReservedRange{} } +func (m *EnumDescriptorProto_EnumReservedRange) String() string { return proto.CompactTextString(m) } +func (*EnumDescriptorProto_EnumReservedRange) ProtoMessage() {} +func (*EnumDescriptorProto_EnumReservedRange) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{6, 0} +} +func (m *EnumDescriptorProto_EnumReservedRange) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Unmarshal(m, b) +} +func (m *EnumDescriptorProto_EnumReservedRange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Marshal(b, m, deterministic) +} +func (m *EnumDescriptorProto_EnumReservedRange) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Merge(m, src) +} +func (m *EnumDescriptorProto_EnumReservedRange) XXX_Size() int { + return xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Size(m) +} +func (m *EnumDescriptorProto_EnumReservedRange) XXX_DiscardUnknown() { + xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.DiscardUnknown(m) +} + +var xxx_messageInfo_EnumDescriptorProto_EnumReservedRange proto.InternalMessageInfo + +func (m *EnumDescriptorProto_EnumReservedRange) GetStart() int32 { + if m != nil && m.Start != nil { + return *m.Start + } + return 0 +} + +func (m *EnumDescriptorProto_EnumReservedRange) GetEnd() int32 { + if m != nil && m.End != nil { + return *m.End + } + return 0 +} + +// Describes a value within an enum. +type EnumValueDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Number *int32 `protobuf:"varint,2,opt,name=number" json:"number,omitempty"` + Options *EnumValueOptions `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EnumValueDescriptorProto) Reset() { *m = EnumValueDescriptorProto{} } +func (m *EnumValueDescriptorProto) String() string { return proto.CompactTextString(m) } +func (*EnumValueDescriptorProto) ProtoMessage() {} +func (*EnumValueDescriptorProto) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{7} +} +func (m *EnumValueDescriptorProto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EnumValueDescriptorProto.Unmarshal(m, b) +} +func (m *EnumValueDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EnumValueDescriptorProto.Marshal(b, m, deterministic) +} +func (m *EnumValueDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnumValueDescriptorProto.Merge(m, src) +} +func (m *EnumValueDescriptorProto) XXX_Size() int { + return xxx_messageInfo_EnumValueDescriptorProto.Size(m) +} +func (m *EnumValueDescriptorProto) XXX_DiscardUnknown() { + xxx_messageInfo_EnumValueDescriptorProto.DiscardUnknown(m) +} + +var xxx_messageInfo_EnumValueDescriptorProto proto.InternalMessageInfo + +func (m *EnumValueDescriptorProto) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *EnumValueDescriptorProto) GetNumber() int32 { + if m != nil && m.Number != nil { + return *m.Number + } + return 0 +} + +func (m *EnumValueDescriptorProto) GetOptions() *EnumValueOptions { + if m != nil { + return m.Options + } + return nil +} + +// Describes a service. +type ServiceDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Method []*MethodDescriptorProto `protobuf:"bytes,2,rep,name=method" json:"method,omitempty"` + Options *ServiceOptions `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ServiceDescriptorProto) Reset() { *m = ServiceDescriptorProto{} } +func (m *ServiceDescriptorProto) String() string { return proto.CompactTextString(m) } +func (*ServiceDescriptorProto) ProtoMessage() {} +func (*ServiceDescriptorProto) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{8} +} +func (m *ServiceDescriptorProto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ServiceDescriptorProto.Unmarshal(m, b) +} +func (m *ServiceDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ServiceDescriptorProto.Marshal(b, m, deterministic) +} +func (m *ServiceDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_ServiceDescriptorProto.Merge(m, src) +} +func (m *ServiceDescriptorProto) XXX_Size() int { + return xxx_messageInfo_ServiceDescriptorProto.Size(m) +} +func (m *ServiceDescriptorProto) XXX_DiscardUnknown() { + xxx_messageInfo_ServiceDescriptorProto.DiscardUnknown(m) +} + +var xxx_messageInfo_ServiceDescriptorProto proto.InternalMessageInfo + +func (m *ServiceDescriptorProto) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *ServiceDescriptorProto) GetMethod() []*MethodDescriptorProto { + if m != nil { + return m.Method + } + return nil +} + +func (m *ServiceDescriptorProto) GetOptions() *ServiceOptions { + if m != nil { + return m.Options + } + return nil +} + +// Describes a method of a service. +type MethodDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + // Input and output type names. These are resolved in the same way as + // FieldDescriptorProto.type_name, but must refer to a message type. + InputType *string `protobuf:"bytes,2,opt,name=input_type,json=inputType" json:"input_type,omitempty"` + OutputType *string `protobuf:"bytes,3,opt,name=output_type,json=outputType" json:"output_type,omitempty"` + Options *MethodOptions `protobuf:"bytes,4,opt,name=options" json:"options,omitempty"` + // Identifies if client streams multiple client messages + ClientStreaming *bool `protobuf:"varint,5,opt,name=client_streaming,json=clientStreaming,def=0" json:"client_streaming,omitempty"` + // Identifies if server streams multiple server messages + ServerStreaming *bool `protobuf:"varint,6,opt,name=server_streaming,json=serverStreaming,def=0" json:"server_streaming,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MethodDescriptorProto) Reset() { *m = MethodDescriptorProto{} } +func (m *MethodDescriptorProto) String() string { return proto.CompactTextString(m) } +func (*MethodDescriptorProto) ProtoMessage() {} +func (*MethodDescriptorProto) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{9} +} +func (m *MethodDescriptorProto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MethodDescriptorProto.Unmarshal(m, b) +} +func (m *MethodDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MethodDescriptorProto.Marshal(b, m, deterministic) +} +func (m *MethodDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_MethodDescriptorProto.Merge(m, src) +} +func (m *MethodDescriptorProto) XXX_Size() int { + return xxx_messageInfo_MethodDescriptorProto.Size(m) +} +func (m *MethodDescriptorProto) XXX_DiscardUnknown() { + xxx_messageInfo_MethodDescriptorProto.DiscardUnknown(m) +} + +var xxx_messageInfo_MethodDescriptorProto proto.InternalMessageInfo + +const Default_MethodDescriptorProto_ClientStreaming bool = false +const Default_MethodDescriptorProto_ServerStreaming bool = false + +func (m *MethodDescriptorProto) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *MethodDescriptorProto) GetInputType() string { + if m != nil && m.InputType != nil { + return *m.InputType + } + return "" +} + +func (m *MethodDescriptorProto) GetOutputType() string { + if m != nil && m.OutputType != nil { + return *m.OutputType + } + return "" +} + +func (m *MethodDescriptorProto) GetOptions() *MethodOptions { + if m != nil { + return m.Options + } + return nil +} + +func (m *MethodDescriptorProto) GetClientStreaming() bool { + if m != nil && m.ClientStreaming != nil { + return *m.ClientStreaming + } + return Default_MethodDescriptorProto_ClientStreaming +} + +func (m *MethodDescriptorProto) GetServerStreaming() bool { + if m != nil && m.ServerStreaming != nil { + return *m.ServerStreaming + } + return Default_MethodDescriptorProto_ServerStreaming +} + +type FileOptions struct { + // Sets the Java package where classes generated from this .proto will be + // placed. By default, the proto package is used, but this is often + // inappropriate because proto packages do not normally start with backwards + // domain names. + JavaPackage *string `protobuf:"bytes,1,opt,name=java_package,json=javaPackage" json:"java_package,omitempty"` + // If set, all the classes from the .proto file are wrapped in a single + // outer class with the given name. This applies to both Proto1 + // (equivalent to the old "--one_java_file" option) and Proto2 (where + // a .proto always translates to a single class, but you may want to + // explicitly choose the class name). + JavaOuterClassname *string `protobuf:"bytes,8,opt,name=java_outer_classname,json=javaOuterClassname" json:"java_outer_classname,omitempty"` + // If set true, then the Java code generator will generate a separate .java + // file for each top-level message, enum, and service defined in the .proto + // file. Thus, these types will *not* be nested inside the outer class + // named by java_outer_classname. However, the outer class will still be + // generated to contain the file's getDescriptor() method as well as any + // top-level extensions defined in the file. + JavaMultipleFiles *bool `protobuf:"varint,10,opt,name=java_multiple_files,json=javaMultipleFiles,def=0" json:"java_multiple_files,omitempty"` + // This option does nothing. + JavaGenerateEqualsAndHash *bool `protobuf:"varint,20,opt,name=java_generate_equals_and_hash,json=javaGenerateEqualsAndHash" json:"java_generate_equals_and_hash,omitempty"` // Deprecated: Do not use. + // If set true, then the Java2 code generator will generate code that + // throws an exception whenever an attempt is made to assign a non-UTF-8 + // byte sequence to a string field. + // Message reflection will do the same. + // However, an extension field still accepts non-UTF-8 byte sequences. + // This option has no effect on when used with the lite runtime. + JavaStringCheckUtf8 *bool `protobuf:"varint,27,opt,name=java_string_check_utf8,json=javaStringCheckUtf8,def=0" json:"java_string_check_utf8,omitempty"` + OptimizeFor *FileOptions_OptimizeMode `protobuf:"varint,9,opt,name=optimize_for,json=optimizeFor,enum=google.protobuf.FileOptions_OptimizeMode,def=1" json:"optimize_for,omitempty"` + // Sets the Go package where structs generated from this .proto will be + // placed. If omitted, the Go package will be derived from the following: + // - The basename of the package import path, if provided. + // - Otherwise, the package statement in the .proto file, if present. + // - Otherwise, the basename of the .proto file, without extension. + GoPackage *string `protobuf:"bytes,11,opt,name=go_package,json=goPackage" json:"go_package,omitempty"` + // Should generic services be generated in each language? "Generic" services + // are not specific to any particular RPC system. They are generated by the + // main code generators in each language (without additional plugins). + // Generic services were the only kind of service generation supported by + // early versions of google.protobuf. + // + // Generic services are now considered deprecated in favor of using plugins + // that generate code specific to your particular RPC system. Therefore, + // these default to false. Old code which depends on generic services should + // explicitly set them to true. + CcGenericServices *bool `protobuf:"varint,16,opt,name=cc_generic_services,json=ccGenericServices,def=0" json:"cc_generic_services,omitempty"` + JavaGenericServices *bool `protobuf:"varint,17,opt,name=java_generic_services,json=javaGenericServices,def=0" json:"java_generic_services,omitempty"` + PyGenericServices *bool `protobuf:"varint,18,opt,name=py_generic_services,json=pyGenericServices,def=0" json:"py_generic_services,omitempty"` + PhpGenericServices *bool `protobuf:"varint,42,opt,name=php_generic_services,json=phpGenericServices,def=0" json:"php_generic_services,omitempty"` + // Is this file deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for everything in the file, or it will be completely ignored; in the very + // least, this is a formalization for deprecating files. + Deprecated *bool `protobuf:"varint,23,opt,name=deprecated,def=0" json:"deprecated,omitempty"` + // Enables the use of arenas for the proto messages in this file. This applies + // only to generated classes for C++. + CcEnableArenas *bool `protobuf:"varint,31,opt,name=cc_enable_arenas,json=ccEnableArenas,def=0" json:"cc_enable_arenas,omitempty"` + // Sets the objective c class prefix which is prepended to all objective c + // generated classes from this .proto. There is no default. + ObjcClassPrefix *string `protobuf:"bytes,36,opt,name=objc_class_prefix,json=objcClassPrefix" json:"objc_class_prefix,omitempty"` + // Namespace for generated classes; defaults to the package. + CsharpNamespace *string `protobuf:"bytes,37,opt,name=csharp_namespace,json=csharpNamespace" json:"csharp_namespace,omitempty"` + // By default Swift generators will take the proto package and CamelCase it + // replacing '.' with underscore and use that to prefix the types/symbols + // defined. When this options is provided, they will use this value instead + // to prefix the types/symbols defined. + SwiftPrefix *string `protobuf:"bytes,39,opt,name=swift_prefix,json=swiftPrefix" json:"swift_prefix,omitempty"` + // Sets the php class prefix which is prepended to all php generated classes + // from this .proto. Default is empty. + PhpClassPrefix *string `protobuf:"bytes,40,opt,name=php_class_prefix,json=phpClassPrefix" json:"php_class_prefix,omitempty"` + // Use this option to change the namespace of php generated classes. Default + // is empty. When this option is empty, the package name will be used for + // determining the namespace. + PhpNamespace *string `protobuf:"bytes,41,opt,name=php_namespace,json=phpNamespace" json:"php_namespace,omitempty"` + // Use this option to change the namespace of php generated metadata classes. + // Default is empty. When this option is empty, the proto file name will be + // used for determining the namespace. + PhpMetadataNamespace *string `protobuf:"bytes,44,opt,name=php_metadata_namespace,json=phpMetadataNamespace" json:"php_metadata_namespace,omitempty"` + // Use this option to change the package of ruby generated classes. Default + // is empty. When this option is not set, the package name will be used for + // determining the ruby package. + RubyPackage *string `protobuf:"bytes,45,opt,name=ruby_package,json=rubyPackage" json:"ruby_package,omitempty"` + // The parser stores options it doesn't recognize here. + // See the documentation for the "Options" section above. + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *FileOptions) Reset() { *m = FileOptions{} } +func (m *FileOptions) String() string { return proto.CompactTextString(m) } +func (*FileOptions) ProtoMessage() {} +func (*FileOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{10} +} + +var extRange_FileOptions = []proto.ExtensionRange{ + {Start: 1000, End: 536870911}, +} + +func (*FileOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_FileOptions +} + +func (m *FileOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FileOptions.Unmarshal(m, b) +} +func (m *FileOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FileOptions.Marshal(b, m, deterministic) +} +func (m *FileOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_FileOptions.Merge(m, src) +} +func (m *FileOptions) XXX_Size() int { + return xxx_messageInfo_FileOptions.Size(m) +} +func (m *FileOptions) XXX_DiscardUnknown() { + xxx_messageInfo_FileOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_FileOptions proto.InternalMessageInfo + +const Default_FileOptions_JavaMultipleFiles bool = false +const Default_FileOptions_JavaStringCheckUtf8 bool = false +const Default_FileOptions_OptimizeFor FileOptions_OptimizeMode = FileOptions_SPEED +const Default_FileOptions_CcGenericServices bool = false +const Default_FileOptions_JavaGenericServices bool = false +const Default_FileOptions_PyGenericServices bool = false +const Default_FileOptions_PhpGenericServices bool = false +const Default_FileOptions_Deprecated bool = false +const Default_FileOptions_CcEnableArenas bool = false + +func (m *FileOptions) GetJavaPackage() string { + if m != nil && m.JavaPackage != nil { + return *m.JavaPackage + } + return "" +} + +func (m *FileOptions) GetJavaOuterClassname() string { + if m != nil && m.JavaOuterClassname != nil { + return *m.JavaOuterClassname + } + return "" +} + +func (m *FileOptions) GetJavaMultipleFiles() bool { + if m != nil && m.JavaMultipleFiles != nil { + return *m.JavaMultipleFiles + } + return Default_FileOptions_JavaMultipleFiles +} + +// Deprecated: Do not use. +func (m *FileOptions) GetJavaGenerateEqualsAndHash() bool { + if m != nil && m.JavaGenerateEqualsAndHash != nil { + return *m.JavaGenerateEqualsAndHash + } + return false +} + +func (m *FileOptions) GetJavaStringCheckUtf8() bool { + if m != nil && m.JavaStringCheckUtf8 != nil { + return *m.JavaStringCheckUtf8 + } + return Default_FileOptions_JavaStringCheckUtf8 +} + +func (m *FileOptions) GetOptimizeFor() FileOptions_OptimizeMode { + if m != nil && m.OptimizeFor != nil { + return *m.OptimizeFor + } + return Default_FileOptions_OptimizeFor +} + +func (m *FileOptions) GetGoPackage() string { + if m != nil && m.GoPackage != nil { + return *m.GoPackage + } + return "" +} + +func (m *FileOptions) GetCcGenericServices() bool { + if m != nil && m.CcGenericServices != nil { + return *m.CcGenericServices + } + return Default_FileOptions_CcGenericServices +} + +func (m *FileOptions) GetJavaGenericServices() bool { + if m != nil && m.JavaGenericServices != nil { + return *m.JavaGenericServices + } + return Default_FileOptions_JavaGenericServices +} + +func (m *FileOptions) GetPyGenericServices() bool { + if m != nil && m.PyGenericServices != nil { + return *m.PyGenericServices + } + return Default_FileOptions_PyGenericServices +} + +func (m *FileOptions) GetPhpGenericServices() bool { + if m != nil && m.PhpGenericServices != nil { + return *m.PhpGenericServices + } + return Default_FileOptions_PhpGenericServices +} + +func (m *FileOptions) GetDeprecated() bool { + if m != nil && m.Deprecated != nil { + return *m.Deprecated + } + return Default_FileOptions_Deprecated +} + +func (m *FileOptions) GetCcEnableArenas() bool { + if m != nil && m.CcEnableArenas != nil { + return *m.CcEnableArenas + } + return Default_FileOptions_CcEnableArenas +} + +func (m *FileOptions) GetObjcClassPrefix() string { + if m != nil && m.ObjcClassPrefix != nil { + return *m.ObjcClassPrefix + } + return "" +} + +func (m *FileOptions) GetCsharpNamespace() string { + if m != nil && m.CsharpNamespace != nil { + return *m.CsharpNamespace + } + return "" +} + +func (m *FileOptions) GetSwiftPrefix() string { + if m != nil && m.SwiftPrefix != nil { + return *m.SwiftPrefix + } + return "" +} + +func (m *FileOptions) GetPhpClassPrefix() string { + if m != nil && m.PhpClassPrefix != nil { + return *m.PhpClassPrefix + } + return "" +} + +func (m *FileOptions) GetPhpNamespace() string { + if m != nil && m.PhpNamespace != nil { + return *m.PhpNamespace + } + return "" +} + +func (m *FileOptions) GetPhpMetadataNamespace() string { + if m != nil && m.PhpMetadataNamespace != nil { + return *m.PhpMetadataNamespace + } + return "" +} + +func (m *FileOptions) GetRubyPackage() string { + if m != nil && m.RubyPackage != nil { + return *m.RubyPackage + } + return "" +} + +func (m *FileOptions) GetUninterpretedOption() []*UninterpretedOption { + if m != nil { + return m.UninterpretedOption + } + return nil +} + +type MessageOptions struct { + // Set true to use the old proto1 MessageSet wire format for extensions. + // This is provided for backwards-compatibility with the MessageSet wire + // format. You should not use this for any other reason: It's less + // efficient, has fewer features, and is more complicated. + // + // The message must be defined exactly as follows: + // message Foo { + // option message_set_wire_format = true; + // extensions 4 to max; + // } + // Note that the message cannot have any defined fields; MessageSets only + // have extensions. + // + // All extensions of your type must be singular messages; e.g. they cannot + // be int32s, enums, or repeated messages. + // + // Because this is an option, the above two restrictions are not enforced by + // the protocol compiler. + MessageSetWireFormat *bool `protobuf:"varint,1,opt,name=message_set_wire_format,json=messageSetWireFormat,def=0" json:"message_set_wire_format,omitempty"` + // Disables the generation of the standard "descriptor()" accessor, which can + // conflict with a field of the same name. This is meant to make migration + // from proto1 easier; new code should avoid fields named "descriptor". + NoStandardDescriptorAccessor *bool `protobuf:"varint,2,opt,name=no_standard_descriptor_accessor,json=noStandardDescriptorAccessor,def=0" json:"no_standard_descriptor_accessor,omitempty"` + // Is this message deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the message, or it will be completely ignored; in the very least, + // this is a formalization for deprecating messages. + Deprecated *bool `protobuf:"varint,3,opt,name=deprecated,def=0" json:"deprecated,omitempty"` + // Whether the message is an automatically generated map entry type for the + // maps field. + // + // For maps fields: + // map map_field = 1; + // The parsed descriptor looks like: + // message MapFieldEntry { + // option map_entry = true; + // optional KeyType key = 1; + // optional ValueType value = 2; + // } + // repeated MapFieldEntry map_field = 1; + // + // Implementations may choose not to generate the map_entry=true message, but + // use a native map in the target language to hold the keys and values. + // The reflection APIs in such implementations still need to work as + // if the field is a repeated message field. + // + // NOTE: Do not set the option in .proto files. Always use the maps syntax + // instead. The option should only be implicitly set by the proto compiler + // parser. + MapEntry *bool `protobuf:"varint,7,opt,name=map_entry,json=mapEntry" json:"map_entry,omitempty"` + // The parser stores options it doesn't recognize here. See above. + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MessageOptions) Reset() { *m = MessageOptions{} } +func (m *MessageOptions) String() string { return proto.CompactTextString(m) } +func (*MessageOptions) ProtoMessage() {} +func (*MessageOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{11} +} + +var extRange_MessageOptions = []proto.ExtensionRange{ + {Start: 1000, End: 536870911}, +} + +func (*MessageOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_MessageOptions +} + +func (m *MessageOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MessageOptions.Unmarshal(m, b) +} +func (m *MessageOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MessageOptions.Marshal(b, m, deterministic) +} +func (m *MessageOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_MessageOptions.Merge(m, src) +} +func (m *MessageOptions) XXX_Size() int { + return xxx_messageInfo_MessageOptions.Size(m) +} +func (m *MessageOptions) XXX_DiscardUnknown() { + xxx_messageInfo_MessageOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_MessageOptions proto.InternalMessageInfo + +const Default_MessageOptions_MessageSetWireFormat bool = false +const Default_MessageOptions_NoStandardDescriptorAccessor bool = false +const Default_MessageOptions_Deprecated bool = false + +func (m *MessageOptions) GetMessageSetWireFormat() bool { + if m != nil && m.MessageSetWireFormat != nil { + return *m.MessageSetWireFormat + } + return Default_MessageOptions_MessageSetWireFormat +} + +func (m *MessageOptions) GetNoStandardDescriptorAccessor() bool { + if m != nil && m.NoStandardDescriptorAccessor != nil { + return *m.NoStandardDescriptorAccessor + } + return Default_MessageOptions_NoStandardDescriptorAccessor +} + +func (m *MessageOptions) GetDeprecated() bool { + if m != nil && m.Deprecated != nil { + return *m.Deprecated + } + return Default_MessageOptions_Deprecated +} + +func (m *MessageOptions) GetMapEntry() bool { + if m != nil && m.MapEntry != nil { + return *m.MapEntry + } + return false +} + +func (m *MessageOptions) GetUninterpretedOption() []*UninterpretedOption { + if m != nil { + return m.UninterpretedOption + } + return nil +} + +type FieldOptions struct { + // The ctype option instructs the C++ code generator to use a different + // representation of the field than it normally would. See the specific + // options below. This option is not yet implemented in the open source + // release -- sorry, we'll try to include it in a future version! + Ctype *FieldOptions_CType `protobuf:"varint,1,opt,name=ctype,enum=google.protobuf.FieldOptions_CType,def=0" json:"ctype,omitempty"` + // The packed option can be enabled for repeated primitive fields to enable + // a more efficient representation on the wire. Rather than repeatedly + // writing the tag and type for each element, the entire array is encoded as + // a single length-delimited blob. In proto3, only explicit setting it to + // false will avoid using packed encoding. + Packed *bool `protobuf:"varint,2,opt,name=packed" json:"packed,omitempty"` + // The jstype option determines the JavaScript type used for values of the + // field. The option is permitted only for 64 bit integral and fixed types + // (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING + // is represented as JavaScript string, which avoids loss of precision that + // can happen when a large value is converted to a floating point JavaScript. + // Specifying JS_NUMBER for the jstype causes the generated JavaScript code to + // use the JavaScript "number" type. The behavior of the default option + // JS_NORMAL is implementation dependent. + // + // This option is an enum to permit additional types to be added, e.g. + // goog.math.Integer. + Jstype *FieldOptions_JSType `protobuf:"varint,6,opt,name=jstype,enum=google.protobuf.FieldOptions_JSType,def=0" json:"jstype,omitempty"` + // Should this field be parsed lazily? Lazy applies only to message-type + // fields. It means that when the outer message is initially parsed, the + // inner message's contents will not be parsed but instead stored in encoded + // form. The inner message will actually be parsed when it is first accessed. + // + // This is only a hint. Implementations are free to choose whether to use + // eager or lazy parsing regardless of the value of this option. However, + // setting this option true suggests that the protocol author believes that + // using lazy parsing on this field is worth the additional bookkeeping + // overhead typically needed to implement it. + // + // This option does not affect the public interface of any generated code; + // all method signatures remain the same. Furthermore, thread-safety of the + // interface is not affected by this option; const methods remain safe to + // call from multiple threads concurrently, while non-const methods continue + // to require exclusive access. + // + // + // Note that implementations may choose not to check required fields within + // a lazy sub-message. That is, calling IsInitialized() on the outer message + // may return true even if the inner message has missing required fields. + // This is necessary because otherwise the inner message would have to be + // parsed in order to perform the check, defeating the purpose of lazy + // parsing. An implementation which chooses not to check required fields + // must be consistent about it. That is, for any particular sub-message, the + // implementation must either *always* check its required fields, or *never* + // check its required fields, regardless of whether or not the message has + // been parsed. + Lazy *bool `protobuf:"varint,5,opt,name=lazy,def=0" json:"lazy,omitempty"` + // Is this field deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for accessors, or it will be completely ignored; in the very least, this + // is a formalization for deprecating fields. + Deprecated *bool `protobuf:"varint,3,opt,name=deprecated,def=0" json:"deprecated,omitempty"` + // For Google-internal migration only. Do not use. + Weak *bool `protobuf:"varint,10,opt,name=weak,def=0" json:"weak,omitempty"` + // The parser stores options it doesn't recognize here. See above. + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *FieldOptions) Reset() { *m = FieldOptions{} } +func (m *FieldOptions) String() string { return proto.CompactTextString(m) } +func (*FieldOptions) ProtoMessage() {} +func (*FieldOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{12} +} + +var extRange_FieldOptions = []proto.ExtensionRange{ + {Start: 1000, End: 536870911}, +} + +func (*FieldOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_FieldOptions +} + +func (m *FieldOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FieldOptions.Unmarshal(m, b) +} +func (m *FieldOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FieldOptions.Marshal(b, m, deterministic) +} +func (m *FieldOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_FieldOptions.Merge(m, src) +} +func (m *FieldOptions) XXX_Size() int { + return xxx_messageInfo_FieldOptions.Size(m) +} +func (m *FieldOptions) XXX_DiscardUnknown() { + xxx_messageInfo_FieldOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_FieldOptions proto.InternalMessageInfo + +const Default_FieldOptions_Ctype FieldOptions_CType = FieldOptions_STRING +const Default_FieldOptions_Jstype FieldOptions_JSType = FieldOptions_JS_NORMAL +const Default_FieldOptions_Lazy bool = false +const Default_FieldOptions_Deprecated bool = false +const Default_FieldOptions_Weak bool = false + +func (m *FieldOptions) GetCtype() FieldOptions_CType { + if m != nil && m.Ctype != nil { + return *m.Ctype + } + return Default_FieldOptions_Ctype +} + +func (m *FieldOptions) GetPacked() bool { + if m != nil && m.Packed != nil { + return *m.Packed + } + return false +} + +func (m *FieldOptions) GetJstype() FieldOptions_JSType { + if m != nil && m.Jstype != nil { + return *m.Jstype + } + return Default_FieldOptions_Jstype +} + +func (m *FieldOptions) GetLazy() bool { + if m != nil && m.Lazy != nil { + return *m.Lazy + } + return Default_FieldOptions_Lazy +} + +func (m *FieldOptions) GetDeprecated() bool { + if m != nil && m.Deprecated != nil { + return *m.Deprecated + } + return Default_FieldOptions_Deprecated +} + +func (m *FieldOptions) GetWeak() bool { + if m != nil && m.Weak != nil { + return *m.Weak + } + return Default_FieldOptions_Weak +} + +func (m *FieldOptions) GetUninterpretedOption() []*UninterpretedOption { + if m != nil { + return m.UninterpretedOption + } + return nil +} + +type OneofOptions struct { + // The parser stores options it doesn't recognize here. See above. + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *OneofOptions) Reset() { *m = OneofOptions{} } +func (m *OneofOptions) String() string { return proto.CompactTextString(m) } +func (*OneofOptions) ProtoMessage() {} +func (*OneofOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{13} +} + +var extRange_OneofOptions = []proto.ExtensionRange{ + {Start: 1000, End: 536870911}, +} + +func (*OneofOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_OneofOptions +} + +func (m *OneofOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OneofOptions.Unmarshal(m, b) +} +func (m *OneofOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OneofOptions.Marshal(b, m, deterministic) +} +func (m *OneofOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_OneofOptions.Merge(m, src) +} +func (m *OneofOptions) XXX_Size() int { + return xxx_messageInfo_OneofOptions.Size(m) +} +func (m *OneofOptions) XXX_DiscardUnknown() { + xxx_messageInfo_OneofOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_OneofOptions proto.InternalMessageInfo + +func (m *OneofOptions) GetUninterpretedOption() []*UninterpretedOption { + if m != nil { + return m.UninterpretedOption + } + return nil +} + +type EnumOptions struct { + // Set this option to true to allow mapping different tag names to the same + // value. + AllowAlias *bool `protobuf:"varint,2,opt,name=allow_alias,json=allowAlias" json:"allow_alias,omitempty"` + // Is this enum deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the enum, or it will be completely ignored; in the very least, this + // is a formalization for deprecating enums. + Deprecated *bool `protobuf:"varint,3,opt,name=deprecated,def=0" json:"deprecated,omitempty"` + // The parser stores options it doesn't recognize here. See above. + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EnumOptions) Reset() { *m = EnumOptions{} } +func (m *EnumOptions) String() string { return proto.CompactTextString(m) } +func (*EnumOptions) ProtoMessage() {} +func (*EnumOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{14} +} + +var extRange_EnumOptions = []proto.ExtensionRange{ + {Start: 1000, End: 536870911}, +} + +func (*EnumOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_EnumOptions +} + +func (m *EnumOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EnumOptions.Unmarshal(m, b) +} +func (m *EnumOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EnumOptions.Marshal(b, m, deterministic) +} +func (m *EnumOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnumOptions.Merge(m, src) +} +func (m *EnumOptions) XXX_Size() int { + return xxx_messageInfo_EnumOptions.Size(m) +} +func (m *EnumOptions) XXX_DiscardUnknown() { + xxx_messageInfo_EnumOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_EnumOptions proto.InternalMessageInfo + +const Default_EnumOptions_Deprecated bool = false + +func (m *EnumOptions) GetAllowAlias() bool { + if m != nil && m.AllowAlias != nil { + return *m.AllowAlias + } + return false +} + +func (m *EnumOptions) GetDeprecated() bool { + if m != nil && m.Deprecated != nil { + return *m.Deprecated + } + return Default_EnumOptions_Deprecated +} + +func (m *EnumOptions) GetUninterpretedOption() []*UninterpretedOption { + if m != nil { + return m.UninterpretedOption + } + return nil +} + +type EnumValueOptions struct { + // Is this enum value deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the enum value, or it will be completely ignored; in the very least, + // this is a formalization for deprecating enum values. + Deprecated *bool `protobuf:"varint,1,opt,name=deprecated,def=0" json:"deprecated,omitempty"` + // The parser stores options it doesn't recognize here. See above. + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EnumValueOptions) Reset() { *m = EnumValueOptions{} } +func (m *EnumValueOptions) String() string { return proto.CompactTextString(m) } +func (*EnumValueOptions) ProtoMessage() {} +func (*EnumValueOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{15} +} + +var extRange_EnumValueOptions = []proto.ExtensionRange{ + {Start: 1000, End: 536870911}, +} + +func (*EnumValueOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_EnumValueOptions +} + +func (m *EnumValueOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EnumValueOptions.Unmarshal(m, b) +} +func (m *EnumValueOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EnumValueOptions.Marshal(b, m, deterministic) +} +func (m *EnumValueOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnumValueOptions.Merge(m, src) +} +func (m *EnumValueOptions) XXX_Size() int { + return xxx_messageInfo_EnumValueOptions.Size(m) +} +func (m *EnumValueOptions) XXX_DiscardUnknown() { + xxx_messageInfo_EnumValueOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_EnumValueOptions proto.InternalMessageInfo + +const Default_EnumValueOptions_Deprecated bool = false + +func (m *EnumValueOptions) GetDeprecated() bool { + if m != nil && m.Deprecated != nil { + return *m.Deprecated + } + return Default_EnumValueOptions_Deprecated +} + +func (m *EnumValueOptions) GetUninterpretedOption() []*UninterpretedOption { + if m != nil { + return m.UninterpretedOption + } + return nil +} + +type ServiceOptions struct { + // Is this service deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the service, or it will be completely ignored; in the very least, + // this is a formalization for deprecating services. + Deprecated *bool `protobuf:"varint,33,opt,name=deprecated,def=0" json:"deprecated,omitempty"` + // The parser stores options it doesn't recognize here. See above. + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ServiceOptions) Reset() { *m = ServiceOptions{} } +func (m *ServiceOptions) String() string { return proto.CompactTextString(m) } +func (*ServiceOptions) ProtoMessage() {} +func (*ServiceOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{16} +} + +var extRange_ServiceOptions = []proto.ExtensionRange{ + {Start: 1000, End: 536870911}, +} + +func (*ServiceOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_ServiceOptions +} + +func (m *ServiceOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ServiceOptions.Unmarshal(m, b) +} +func (m *ServiceOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ServiceOptions.Marshal(b, m, deterministic) +} +func (m *ServiceOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_ServiceOptions.Merge(m, src) +} +func (m *ServiceOptions) XXX_Size() int { + return xxx_messageInfo_ServiceOptions.Size(m) +} +func (m *ServiceOptions) XXX_DiscardUnknown() { + xxx_messageInfo_ServiceOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_ServiceOptions proto.InternalMessageInfo + +const Default_ServiceOptions_Deprecated bool = false + +func (m *ServiceOptions) GetDeprecated() bool { + if m != nil && m.Deprecated != nil { + return *m.Deprecated + } + return Default_ServiceOptions_Deprecated +} + +func (m *ServiceOptions) GetUninterpretedOption() []*UninterpretedOption { + if m != nil { + return m.UninterpretedOption + } + return nil +} + +type MethodOptions struct { + // Is this method deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the method, or it will be completely ignored; in the very least, + // this is a formalization for deprecating methods. + Deprecated *bool `protobuf:"varint,33,opt,name=deprecated,def=0" json:"deprecated,omitempty"` + IdempotencyLevel *MethodOptions_IdempotencyLevel `protobuf:"varint,34,opt,name=idempotency_level,json=idempotencyLevel,enum=google.protobuf.MethodOptions_IdempotencyLevel,def=0" json:"idempotency_level,omitempty"` + // The parser stores options it doesn't recognize here. See above. + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + proto.XXX_InternalExtensions `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MethodOptions) Reset() { *m = MethodOptions{} } +func (m *MethodOptions) String() string { return proto.CompactTextString(m) } +func (*MethodOptions) ProtoMessage() {} +func (*MethodOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{17} +} + +var extRange_MethodOptions = []proto.ExtensionRange{ + {Start: 1000, End: 536870911}, +} + +func (*MethodOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_MethodOptions +} + +func (m *MethodOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MethodOptions.Unmarshal(m, b) +} +func (m *MethodOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MethodOptions.Marshal(b, m, deterministic) +} +func (m *MethodOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_MethodOptions.Merge(m, src) +} +func (m *MethodOptions) XXX_Size() int { + return xxx_messageInfo_MethodOptions.Size(m) +} +func (m *MethodOptions) XXX_DiscardUnknown() { + xxx_messageInfo_MethodOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_MethodOptions proto.InternalMessageInfo + +const Default_MethodOptions_Deprecated bool = false +const Default_MethodOptions_IdempotencyLevel MethodOptions_IdempotencyLevel = MethodOptions_IDEMPOTENCY_UNKNOWN + +func (m *MethodOptions) GetDeprecated() bool { + if m != nil && m.Deprecated != nil { + return *m.Deprecated + } + return Default_MethodOptions_Deprecated +} + +func (m *MethodOptions) GetIdempotencyLevel() MethodOptions_IdempotencyLevel { + if m != nil && m.IdempotencyLevel != nil { + return *m.IdempotencyLevel + } + return Default_MethodOptions_IdempotencyLevel +} + +func (m *MethodOptions) GetUninterpretedOption() []*UninterpretedOption { + if m != nil { + return m.UninterpretedOption + } + return nil +} + +// A message representing a option the parser does not recognize. This only +// appears in options protos created by the compiler::Parser class. +// DescriptorPool resolves these when building Descriptor objects. Therefore, +// options protos in descriptor objects (e.g. returned by Descriptor::options(), +// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions +// in them. +type UninterpretedOption struct { + Name []*UninterpretedOption_NamePart `protobuf:"bytes,2,rep,name=name" json:"name,omitempty"` + // The value of the uninterpreted option, in whatever type the tokenizer + // identified it as during parsing. Exactly one of these should be set. + IdentifierValue *string `protobuf:"bytes,3,opt,name=identifier_value,json=identifierValue" json:"identifier_value,omitempty"` + PositiveIntValue *uint64 `protobuf:"varint,4,opt,name=positive_int_value,json=positiveIntValue" json:"positive_int_value,omitempty"` + NegativeIntValue *int64 `protobuf:"varint,5,opt,name=negative_int_value,json=negativeIntValue" json:"negative_int_value,omitempty"` + DoubleValue *float64 `protobuf:"fixed64,6,opt,name=double_value,json=doubleValue" json:"double_value,omitempty"` + StringValue []byte `protobuf:"bytes,7,opt,name=string_value,json=stringValue" json:"string_value,omitempty"` + AggregateValue *string `protobuf:"bytes,8,opt,name=aggregate_value,json=aggregateValue" json:"aggregate_value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UninterpretedOption) Reset() { *m = UninterpretedOption{} } +func (m *UninterpretedOption) String() string { return proto.CompactTextString(m) } +func (*UninterpretedOption) ProtoMessage() {} +func (*UninterpretedOption) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{18} +} +func (m *UninterpretedOption) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_UninterpretedOption.Unmarshal(m, b) +} +func (m *UninterpretedOption) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_UninterpretedOption.Marshal(b, m, deterministic) +} +func (m *UninterpretedOption) XXX_Merge(src proto.Message) { + xxx_messageInfo_UninterpretedOption.Merge(m, src) +} +func (m *UninterpretedOption) XXX_Size() int { + return xxx_messageInfo_UninterpretedOption.Size(m) +} +func (m *UninterpretedOption) XXX_DiscardUnknown() { + xxx_messageInfo_UninterpretedOption.DiscardUnknown(m) +} + +var xxx_messageInfo_UninterpretedOption proto.InternalMessageInfo + +func (m *UninterpretedOption) GetName() []*UninterpretedOption_NamePart { + if m != nil { + return m.Name + } + return nil +} + +func (m *UninterpretedOption) GetIdentifierValue() string { + if m != nil && m.IdentifierValue != nil { + return *m.IdentifierValue + } + return "" +} + +func (m *UninterpretedOption) GetPositiveIntValue() uint64 { + if m != nil && m.PositiveIntValue != nil { + return *m.PositiveIntValue + } + return 0 +} + +func (m *UninterpretedOption) GetNegativeIntValue() int64 { + if m != nil && m.NegativeIntValue != nil { + return *m.NegativeIntValue + } + return 0 +} + +func (m *UninterpretedOption) GetDoubleValue() float64 { + if m != nil && m.DoubleValue != nil { + return *m.DoubleValue + } + return 0 +} + +func (m *UninterpretedOption) GetStringValue() []byte { + if m != nil { + return m.StringValue + } + return nil +} + +func (m *UninterpretedOption) GetAggregateValue() string { + if m != nil && m.AggregateValue != nil { + return *m.AggregateValue + } + return "" +} + +// The name of the uninterpreted option. Each string represents a segment in +// a dot-separated name. is_extension is true iff a segment represents an +// extension (denoted with parentheses in options specs in .proto files). +// E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents +// "foo.(bar.baz).qux". +type UninterpretedOption_NamePart struct { + NamePart *string `protobuf:"bytes,1,req,name=name_part,json=namePart" json:"name_part,omitempty"` + IsExtension *bool `protobuf:"varint,2,req,name=is_extension,json=isExtension" json:"is_extension,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UninterpretedOption_NamePart) Reset() { *m = UninterpretedOption_NamePart{} } +func (m *UninterpretedOption_NamePart) String() string { return proto.CompactTextString(m) } +func (*UninterpretedOption_NamePart) ProtoMessage() {} +func (*UninterpretedOption_NamePart) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{18, 0} +} +func (m *UninterpretedOption_NamePart) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_UninterpretedOption_NamePart.Unmarshal(m, b) +} +func (m *UninterpretedOption_NamePart) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_UninterpretedOption_NamePart.Marshal(b, m, deterministic) +} +func (m *UninterpretedOption_NamePart) XXX_Merge(src proto.Message) { + xxx_messageInfo_UninterpretedOption_NamePart.Merge(m, src) +} +func (m *UninterpretedOption_NamePart) XXX_Size() int { + return xxx_messageInfo_UninterpretedOption_NamePart.Size(m) +} +func (m *UninterpretedOption_NamePart) XXX_DiscardUnknown() { + xxx_messageInfo_UninterpretedOption_NamePart.DiscardUnknown(m) +} + +var xxx_messageInfo_UninterpretedOption_NamePart proto.InternalMessageInfo + +func (m *UninterpretedOption_NamePart) GetNamePart() string { + if m != nil && m.NamePart != nil { + return *m.NamePart + } + return "" +} + +func (m *UninterpretedOption_NamePart) GetIsExtension() bool { + if m != nil && m.IsExtension != nil { + return *m.IsExtension + } + return false +} + +// Encapsulates information about the original source file from which a +// FileDescriptorProto was generated. +type SourceCodeInfo struct { + // A Location identifies a piece of source code in a .proto file which + // corresponds to a particular definition. This information is intended + // to be useful to IDEs, code indexers, documentation generators, and similar + // tools. + // + // For example, say we have a file like: + // message Foo { + // optional string foo = 1; + // } + // Let's look at just the field definition: + // optional string foo = 1; + // ^ ^^ ^^ ^ ^^^ + // a bc de f ghi + // We have the following locations: + // span path represents + // [a,i) [ 4, 0, 2, 0 ] The whole field definition. + // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). + // [c,d) [ 4, 0, 2, 0, 5 ] The type (string). + // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). + // [g,h) [ 4, 0, 2, 0, 3 ] The number (1). + // + // Notes: + // - A location may refer to a repeated field itself (i.e. not to any + // particular index within it). This is used whenever a set of elements are + // logically enclosed in a single code segment. For example, an entire + // extend block (possibly containing multiple extension definitions) will + // have an outer location whose path refers to the "extensions" repeated + // field without an index. + // - Multiple locations may have the same path. This happens when a single + // logical declaration is spread out across multiple places. The most + // obvious example is the "extend" block again -- there may be multiple + // extend blocks in the same scope, each of which will have the same path. + // - A location's span is not always a subset of its parent's span. For + // example, the "extendee" of an extension declaration appears at the + // beginning of the "extend" block and is shared by all extensions within + // the block. + // - Just because a location's span is a subset of some other location's span + // does not mean that it is a descendant. For example, a "group" defines + // both a type and a field in a single declaration. Thus, the locations + // corresponding to the type and field and their components will overlap. + // - Code which tries to interpret locations should probably be designed to + // ignore those that it doesn't understand, as more types of locations could + // be recorded in the future. + Location []*SourceCodeInfo_Location `protobuf:"bytes,1,rep,name=location" json:"location,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SourceCodeInfo) Reset() { *m = SourceCodeInfo{} } +func (m *SourceCodeInfo) String() string { return proto.CompactTextString(m) } +func (*SourceCodeInfo) ProtoMessage() {} +func (*SourceCodeInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{19} +} +func (m *SourceCodeInfo) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SourceCodeInfo.Unmarshal(m, b) +} +func (m *SourceCodeInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SourceCodeInfo.Marshal(b, m, deterministic) +} +func (m *SourceCodeInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_SourceCodeInfo.Merge(m, src) +} +func (m *SourceCodeInfo) XXX_Size() int { + return xxx_messageInfo_SourceCodeInfo.Size(m) +} +func (m *SourceCodeInfo) XXX_DiscardUnknown() { + xxx_messageInfo_SourceCodeInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_SourceCodeInfo proto.InternalMessageInfo + +func (m *SourceCodeInfo) GetLocation() []*SourceCodeInfo_Location { + if m != nil { + return m.Location + } + return nil +} + +type SourceCodeInfo_Location struct { + // Identifies which part of the FileDescriptorProto was defined at this + // location. + // + // Each element is a field number or an index. They form a path from + // the root FileDescriptorProto to the place where the definition. For + // example, this path: + // [ 4, 3, 2, 7, 1 ] + // refers to: + // file.message_type(3) // 4, 3 + // .field(7) // 2, 7 + // .name() // 1 + // This is because FileDescriptorProto.message_type has field number 4: + // repeated DescriptorProto message_type = 4; + // and DescriptorProto.field has field number 2: + // repeated FieldDescriptorProto field = 2; + // and FieldDescriptorProto.name has field number 1: + // optional string name = 1; + // + // Thus, the above path gives the location of a field name. If we removed + // the last element: + // [ 4, 3, 2, 7 ] + // this path refers to the whole field declaration (from the beginning + // of the label to the terminating semicolon). + Path []int32 `protobuf:"varint,1,rep,packed,name=path" json:"path,omitempty"` + // Always has exactly three or four elements: start line, start column, + // end line (optional, otherwise assumed same as start line), end column. + // These are packed into a single field for efficiency. Note that line + // and column numbers are zero-based -- typically you will want to add + // 1 to each before displaying to a user. + Span []int32 `protobuf:"varint,2,rep,packed,name=span" json:"span,omitempty"` + // If this SourceCodeInfo represents a complete declaration, these are any + // comments appearing before and after the declaration which appear to be + // attached to the declaration. + // + // A series of line comments appearing on consecutive lines, with no other + // tokens appearing on those lines, will be treated as a single comment. + // + // leading_detached_comments will keep paragraphs of comments that appear + // before (but not connected to) the current element. Each paragraph, + // separated by empty lines, will be one comment element in the repeated + // field. + // + // Only the comment content is provided; comment markers (e.g. //) are + // stripped out. For block comments, leading whitespace and an asterisk + // will be stripped from the beginning of each line other than the first. + // Newlines are included in the output. + // + // Examples: + // + // optional int32 foo = 1; // Comment attached to foo. + // // Comment attached to bar. + // optional int32 bar = 2; + // + // optional string baz = 3; + // // Comment attached to baz. + // // Another line attached to baz. + // + // // Comment attached to qux. + // // + // // Another line attached to qux. + // optional double qux = 4; + // + // // Detached comment for corge. This is not leading or trailing comments + // // to qux or corge because there are blank lines separating it from + // // both. + // + // // Detached comment for corge paragraph 2. + // + // optional string corge = 5; + // /* Block comment attached + // * to corge. Leading asterisks + // * will be removed. */ + // /* Block comment attached to + // * grault. */ + // optional int32 grault = 6; + // + // // ignored detached comments. + LeadingComments *string `protobuf:"bytes,3,opt,name=leading_comments,json=leadingComments" json:"leading_comments,omitempty"` + TrailingComments *string `protobuf:"bytes,4,opt,name=trailing_comments,json=trailingComments" json:"trailing_comments,omitempty"` + LeadingDetachedComments []string `protobuf:"bytes,6,rep,name=leading_detached_comments,json=leadingDetachedComments" json:"leading_detached_comments,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SourceCodeInfo_Location) Reset() { *m = SourceCodeInfo_Location{} } +func (m *SourceCodeInfo_Location) String() string { return proto.CompactTextString(m) } +func (*SourceCodeInfo_Location) ProtoMessage() {} +func (*SourceCodeInfo_Location) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{19, 0} +} +func (m *SourceCodeInfo_Location) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SourceCodeInfo_Location.Unmarshal(m, b) +} +func (m *SourceCodeInfo_Location) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SourceCodeInfo_Location.Marshal(b, m, deterministic) +} +func (m *SourceCodeInfo_Location) XXX_Merge(src proto.Message) { + xxx_messageInfo_SourceCodeInfo_Location.Merge(m, src) +} +func (m *SourceCodeInfo_Location) XXX_Size() int { + return xxx_messageInfo_SourceCodeInfo_Location.Size(m) +} +func (m *SourceCodeInfo_Location) XXX_DiscardUnknown() { + xxx_messageInfo_SourceCodeInfo_Location.DiscardUnknown(m) +} + +var xxx_messageInfo_SourceCodeInfo_Location proto.InternalMessageInfo + +func (m *SourceCodeInfo_Location) GetPath() []int32 { + if m != nil { + return m.Path + } + return nil +} + +func (m *SourceCodeInfo_Location) GetSpan() []int32 { + if m != nil { + return m.Span + } + return nil +} + +func (m *SourceCodeInfo_Location) GetLeadingComments() string { + if m != nil && m.LeadingComments != nil { + return *m.LeadingComments + } + return "" +} + +func (m *SourceCodeInfo_Location) GetTrailingComments() string { + if m != nil && m.TrailingComments != nil { + return *m.TrailingComments + } + return "" +} + +func (m *SourceCodeInfo_Location) GetLeadingDetachedComments() []string { + if m != nil { + return m.LeadingDetachedComments + } + return nil +} + +// Describes the relationship between generated code and its original source +// file. A GeneratedCodeInfo message is associated with only one generated +// source file, but may contain references to different source .proto files. +type GeneratedCodeInfo struct { + // An Annotation connects some span of text in generated code to an element + // of its generating .proto file. + Annotation []*GeneratedCodeInfo_Annotation `protobuf:"bytes,1,rep,name=annotation" json:"annotation,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GeneratedCodeInfo) Reset() { *m = GeneratedCodeInfo{} } +func (m *GeneratedCodeInfo) String() string { return proto.CompactTextString(m) } +func (*GeneratedCodeInfo) ProtoMessage() {} +func (*GeneratedCodeInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{20} +} +func (m *GeneratedCodeInfo) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GeneratedCodeInfo.Unmarshal(m, b) +} +func (m *GeneratedCodeInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GeneratedCodeInfo.Marshal(b, m, deterministic) +} +func (m *GeneratedCodeInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_GeneratedCodeInfo.Merge(m, src) +} +func (m *GeneratedCodeInfo) XXX_Size() int { + return xxx_messageInfo_GeneratedCodeInfo.Size(m) +} +func (m *GeneratedCodeInfo) XXX_DiscardUnknown() { + xxx_messageInfo_GeneratedCodeInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_GeneratedCodeInfo proto.InternalMessageInfo + +func (m *GeneratedCodeInfo) GetAnnotation() []*GeneratedCodeInfo_Annotation { + if m != nil { + return m.Annotation + } + return nil +} + +type GeneratedCodeInfo_Annotation struct { + // Identifies the element in the original source .proto file. This field + // is formatted the same as SourceCodeInfo.Location.path. + Path []int32 `protobuf:"varint,1,rep,packed,name=path" json:"path,omitempty"` + // Identifies the filesystem path to the original source .proto. + SourceFile *string `protobuf:"bytes,2,opt,name=source_file,json=sourceFile" json:"source_file,omitempty"` + // Identifies the starting offset in bytes in the generated code + // that relates to the identified object. + Begin *int32 `protobuf:"varint,3,opt,name=begin" json:"begin,omitempty"` + // Identifies the ending offset in bytes in the generated code that + // relates to the identified offset. The end offset should be one past + // the last relevant byte (so the length of the text = end - begin). + End *int32 `protobuf:"varint,4,opt,name=end" json:"end,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GeneratedCodeInfo_Annotation) Reset() { *m = GeneratedCodeInfo_Annotation{} } +func (m *GeneratedCodeInfo_Annotation) String() string { return proto.CompactTextString(m) } +func (*GeneratedCodeInfo_Annotation) ProtoMessage() {} +func (*GeneratedCodeInfo_Annotation) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{20, 0} +} +func (m *GeneratedCodeInfo_Annotation) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GeneratedCodeInfo_Annotation.Unmarshal(m, b) +} +func (m *GeneratedCodeInfo_Annotation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GeneratedCodeInfo_Annotation.Marshal(b, m, deterministic) +} +func (m *GeneratedCodeInfo_Annotation) XXX_Merge(src proto.Message) { + xxx_messageInfo_GeneratedCodeInfo_Annotation.Merge(m, src) +} +func (m *GeneratedCodeInfo_Annotation) XXX_Size() int { + return xxx_messageInfo_GeneratedCodeInfo_Annotation.Size(m) +} +func (m *GeneratedCodeInfo_Annotation) XXX_DiscardUnknown() { + xxx_messageInfo_GeneratedCodeInfo_Annotation.DiscardUnknown(m) +} + +var xxx_messageInfo_GeneratedCodeInfo_Annotation proto.InternalMessageInfo + +func (m *GeneratedCodeInfo_Annotation) GetPath() []int32 { + if m != nil { + return m.Path + } + return nil +} + +func (m *GeneratedCodeInfo_Annotation) GetSourceFile() string { + if m != nil && m.SourceFile != nil { + return *m.SourceFile + } + return "" +} + +func (m *GeneratedCodeInfo_Annotation) GetBegin() int32 { + if m != nil && m.Begin != nil { + return *m.Begin + } + return 0 +} + +func (m *GeneratedCodeInfo_Annotation) GetEnd() int32 { + if m != nil && m.End != nil { + return *m.End + } + return 0 +} + +func init() { + proto.RegisterEnum("google.protobuf.FieldDescriptorProto_Type", FieldDescriptorProto_Type_name, FieldDescriptorProto_Type_value) + proto.RegisterEnum("google.protobuf.FieldDescriptorProto_Label", FieldDescriptorProto_Label_name, FieldDescriptorProto_Label_value) + proto.RegisterEnum("google.protobuf.FileOptions_OptimizeMode", FileOptions_OptimizeMode_name, FileOptions_OptimizeMode_value) + proto.RegisterEnum("google.protobuf.FieldOptions_CType", FieldOptions_CType_name, FieldOptions_CType_value) + proto.RegisterEnum("google.protobuf.FieldOptions_JSType", FieldOptions_JSType_name, FieldOptions_JSType_value) + proto.RegisterEnum("google.protobuf.MethodOptions_IdempotencyLevel", MethodOptions_IdempotencyLevel_name, MethodOptions_IdempotencyLevel_value) + proto.RegisterType((*FileDescriptorSet)(nil), "google.protobuf.FileDescriptorSet") + proto.RegisterType((*FileDescriptorProto)(nil), "google.protobuf.FileDescriptorProto") + proto.RegisterType((*DescriptorProto)(nil), "google.protobuf.DescriptorProto") + proto.RegisterType((*DescriptorProto_ExtensionRange)(nil), "google.protobuf.DescriptorProto.ExtensionRange") + proto.RegisterType((*DescriptorProto_ReservedRange)(nil), "google.protobuf.DescriptorProto.ReservedRange") + proto.RegisterType((*ExtensionRangeOptions)(nil), "google.protobuf.ExtensionRangeOptions") + proto.RegisterType((*FieldDescriptorProto)(nil), "google.protobuf.FieldDescriptorProto") + proto.RegisterType((*OneofDescriptorProto)(nil), "google.protobuf.OneofDescriptorProto") + proto.RegisterType((*EnumDescriptorProto)(nil), "google.protobuf.EnumDescriptorProto") + proto.RegisterType((*EnumDescriptorProto_EnumReservedRange)(nil), "google.protobuf.EnumDescriptorProto.EnumReservedRange") + proto.RegisterType((*EnumValueDescriptorProto)(nil), "google.protobuf.EnumValueDescriptorProto") + proto.RegisterType((*ServiceDescriptorProto)(nil), "google.protobuf.ServiceDescriptorProto") + proto.RegisterType((*MethodDescriptorProto)(nil), "google.protobuf.MethodDescriptorProto") + proto.RegisterType((*FileOptions)(nil), "google.protobuf.FileOptions") + proto.RegisterType((*MessageOptions)(nil), "google.protobuf.MessageOptions") + proto.RegisterType((*FieldOptions)(nil), "google.protobuf.FieldOptions") + proto.RegisterType((*OneofOptions)(nil), "google.protobuf.OneofOptions") + proto.RegisterType((*EnumOptions)(nil), "google.protobuf.EnumOptions") + proto.RegisterType((*EnumValueOptions)(nil), "google.protobuf.EnumValueOptions") + proto.RegisterType((*ServiceOptions)(nil), "google.protobuf.ServiceOptions") + proto.RegisterType((*MethodOptions)(nil), "google.protobuf.MethodOptions") + proto.RegisterType((*UninterpretedOption)(nil), "google.protobuf.UninterpretedOption") + proto.RegisterType((*UninterpretedOption_NamePart)(nil), "google.protobuf.UninterpretedOption.NamePart") + proto.RegisterType((*SourceCodeInfo)(nil), "google.protobuf.SourceCodeInfo") + proto.RegisterType((*SourceCodeInfo_Location)(nil), "google.protobuf.SourceCodeInfo.Location") + proto.RegisterType((*GeneratedCodeInfo)(nil), "google.protobuf.GeneratedCodeInfo") + proto.RegisterType((*GeneratedCodeInfo_Annotation)(nil), "google.protobuf.GeneratedCodeInfo.Annotation") +} + +func init() { proto.RegisterFile("descriptor.proto", fileDescriptor_308767df5ffe18af) } + +var fileDescriptor_308767df5ffe18af = []byte{ + // 2522 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0xcd, 0x6f, 0xdb, 0xc8, + 0x15, 0x5f, 0x7d, 0x5a, 0x7a, 0x92, 0x65, 0x7a, 0xec, 0x75, 0x18, 0xef, 0x47, 0x1c, 0xed, 0x66, + 0xe3, 0x24, 0xbb, 0xca, 0xc2, 0x49, 0x9c, 0xac, 0x53, 0x6c, 0x2b, 0x4b, 0x8c, 0x57, 0xa9, 0xbe, + 0x4a, 0xc9, 0xdd, 0x64, 0x8b, 0x82, 0x18, 0x93, 0x23, 0x89, 0x09, 0x45, 0x72, 0x49, 0x2a, 0x89, + 0x83, 0x1e, 0x02, 0xf4, 0x54, 0xa0, 0x7f, 0x40, 0x51, 0x14, 0x3d, 0xf4, 0xb2, 0x40, 0xff, 0x80, + 0x02, 0xed, 0xbd, 0xd7, 0x02, 0xbd, 0xf7, 0x50, 0xa0, 0x05, 0xda, 0x3f, 0xa1, 0xc7, 0x62, 0x66, + 0x48, 0x8a, 0xd4, 0x47, 0xe2, 0x5d, 0x20, 0xd9, 0x93, 0x3d, 0xef, 0xfd, 0xde, 0x9b, 0x37, 0x8f, + 0xbf, 0x79, 0xf3, 0x66, 0x04, 0x82, 0x46, 0x5c, 0xd5, 0xd1, 0x6d, 0xcf, 0x72, 0x2a, 0xb6, 0x63, + 0x79, 0x16, 0x5a, 0x1b, 0x5a, 0xd6, 0xd0, 0x20, 0x7c, 0x74, 0x32, 0x19, 0x94, 0x5b, 0xb0, 0x7e, + 0x4f, 0x37, 0x48, 0x3d, 0x04, 0xf6, 0x88, 0x87, 0xee, 0x40, 0x7a, 0xa0, 0x1b, 0x44, 0x4c, 0xec, + 0xa4, 0x76, 0x0b, 0x7b, 0x1f, 0x56, 0x66, 0x8c, 0x2a, 0x71, 0x8b, 0x2e, 0x15, 0xcb, 0xcc, 0xa2, + 0xfc, 0xef, 0x34, 0x6c, 0x2c, 0xd0, 0x22, 0x04, 0x69, 0x13, 0x8f, 0xa9, 0xc7, 0xc4, 0x6e, 0x5e, + 0x66, 0xff, 0x23, 0x11, 0x56, 0x6c, 0xac, 0x3e, 0xc6, 0x43, 0x22, 0x26, 0x99, 0x38, 0x18, 0xa2, + 0xf7, 0x01, 0x34, 0x62, 0x13, 0x53, 0x23, 0xa6, 0x7a, 0x2a, 0xa6, 0x76, 0x52, 0xbb, 0x79, 0x39, + 0x22, 0x41, 0xd7, 0x60, 0xdd, 0x9e, 0x9c, 0x18, 0xba, 0xaa, 0x44, 0x60, 0xb0, 0x93, 0xda, 0xcd, + 0xc8, 0x02, 0x57, 0xd4, 0xa7, 0xe0, 0xcb, 0xb0, 0xf6, 0x94, 0xe0, 0xc7, 0x51, 0x68, 0x81, 0x41, + 0x4b, 0x54, 0x1c, 0x01, 0xd6, 0xa0, 0x38, 0x26, 0xae, 0x8b, 0x87, 0x44, 0xf1, 0x4e, 0x6d, 0x22, + 0xa6, 0xd9, 0xea, 0x77, 0xe6, 0x56, 0x3f, 0xbb, 0xf2, 0x82, 0x6f, 0xd5, 0x3f, 0xb5, 0x09, 0xaa, + 0x42, 0x9e, 0x98, 0x93, 0x31, 0xf7, 0x90, 0x59, 0x92, 0x3f, 0xc9, 0x9c, 0x8c, 0x67, 0xbd, 0xe4, + 0xa8, 0x99, 0xef, 0x62, 0xc5, 0x25, 0xce, 0x13, 0x5d, 0x25, 0x62, 0x96, 0x39, 0xb8, 0x3c, 0xe7, + 0xa0, 0xc7, 0xf5, 0xb3, 0x3e, 0x02, 0x3b, 0x54, 0x83, 0x3c, 0x79, 0xe6, 0x11, 0xd3, 0xd5, 0x2d, + 0x53, 0x5c, 0x61, 0x4e, 0x2e, 0x2d, 0xf8, 0x8a, 0xc4, 0xd0, 0x66, 0x5d, 0x4c, 0xed, 0xd0, 0x3e, + 0xac, 0x58, 0xb6, 0xa7, 0x5b, 0xa6, 0x2b, 0xe6, 0x76, 0x12, 0xbb, 0x85, 0xbd, 0x77, 0x17, 0x12, + 0xa1, 0xc3, 0x31, 0x72, 0x00, 0x46, 0x0d, 0x10, 0x5c, 0x6b, 0xe2, 0xa8, 0x44, 0x51, 0x2d, 0x8d, + 0x28, 0xba, 0x39, 0xb0, 0xc4, 0x3c, 0x73, 0x70, 0x61, 0x7e, 0x21, 0x0c, 0x58, 0xb3, 0x34, 0xd2, + 0x30, 0x07, 0x96, 0x5c, 0x72, 0x63, 0x63, 0xb4, 0x05, 0x59, 0xf7, 0xd4, 0xf4, 0xf0, 0x33, 0xb1, + 0xc8, 0x18, 0xe2, 0x8f, 0xca, 0x7f, 0xce, 0xc2, 0xda, 0x59, 0x28, 0x76, 0x17, 0x32, 0x03, 0xba, + 0x4a, 0x31, 0xf9, 0x6d, 0x72, 0xc0, 0x6d, 0xe2, 0x49, 0xcc, 0x7e, 0xc7, 0x24, 0x56, 0xa1, 0x60, + 0x12, 0xd7, 0x23, 0x1a, 0x67, 0x44, 0xea, 0x8c, 0x9c, 0x02, 0x6e, 0x34, 0x4f, 0xa9, 0xf4, 0x77, + 0xa2, 0xd4, 0x03, 0x58, 0x0b, 0x43, 0x52, 0x1c, 0x6c, 0x0e, 0x03, 0x6e, 0x5e, 0x7f, 0x55, 0x24, + 0x15, 0x29, 0xb0, 0x93, 0xa9, 0x99, 0x5c, 0x22, 0xb1, 0x31, 0xaa, 0x03, 0x58, 0x26, 0xb1, 0x06, + 0x8a, 0x46, 0x54, 0x43, 0xcc, 0x2d, 0xc9, 0x52, 0x87, 0x42, 0xe6, 0xb2, 0x64, 0x71, 0xa9, 0x6a, + 0xa0, 0xcf, 0xa6, 0x54, 0x5b, 0x59, 0xc2, 0x94, 0x16, 0xdf, 0x64, 0x73, 0x6c, 0x3b, 0x86, 0x92, + 0x43, 0x28, 0xef, 0x89, 0xe6, 0xaf, 0x2c, 0xcf, 0x82, 0xa8, 0xbc, 0x72, 0x65, 0xb2, 0x6f, 0xc6, + 0x17, 0xb6, 0xea, 0x44, 0x87, 0xe8, 0x03, 0x08, 0x05, 0x0a, 0xa3, 0x15, 0xb0, 0x2a, 0x54, 0x0c, + 0x84, 0x6d, 0x3c, 0x26, 0xdb, 0xcf, 0xa1, 0x14, 0x4f, 0x0f, 0xda, 0x84, 0x8c, 0xeb, 0x61, 0xc7, + 0x63, 0x2c, 0xcc, 0xc8, 0x7c, 0x80, 0x04, 0x48, 0x11, 0x53, 0x63, 0x55, 0x2e, 0x23, 0xd3, 0x7f, + 0xd1, 0x8f, 0xa6, 0x0b, 0x4e, 0xb1, 0x05, 0x7f, 0x34, 0xff, 0x45, 0x63, 0x9e, 0x67, 0xd7, 0xbd, + 0x7d, 0x1b, 0x56, 0x63, 0x0b, 0x38, 0xeb, 0xd4, 0xe5, 0x5f, 0xc0, 0xdb, 0x0b, 0x5d, 0xa3, 0x07, + 0xb0, 0x39, 0x31, 0x75, 0xd3, 0x23, 0x8e, 0xed, 0x10, 0xca, 0x58, 0x3e, 0x95, 0xf8, 0x9f, 0x95, + 0x25, 0x9c, 0x3b, 0x8e, 0xa2, 0xb9, 0x17, 0x79, 0x63, 0x32, 0x2f, 0xbc, 0x9a, 0xcf, 0xfd, 0x77, + 0x45, 0x78, 0xf1, 0xe2, 0xc5, 0x8b, 0x64, 0xf9, 0x37, 0x59, 0xd8, 0x5c, 0xb4, 0x67, 0x16, 0x6e, + 0xdf, 0x2d, 0xc8, 0x9a, 0x93, 0xf1, 0x09, 0x71, 0x58, 0x92, 0x32, 0xb2, 0x3f, 0x42, 0x55, 0xc8, + 0x18, 0xf8, 0x84, 0x18, 0x62, 0x7a, 0x27, 0xb1, 0x5b, 0xda, 0xbb, 0x76, 0xa6, 0x5d, 0x59, 0x69, + 0x52, 0x13, 0x99, 0x5b, 0xa2, 0xcf, 0x21, 0xed, 0x97, 0x68, 0xea, 0xe1, 0xea, 0xd9, 0x3c, 0xd0, + 0xbd, 0x24, 0x33, 0x3b, 0xf4, 0x0e, 0xe4, 0xe9, 0x5f, 0xce, 0x8d, 0x2c, 0x8b, 0x39, 0x47, 0x05, + 0x94, 0x17, 0x68, 0x1b, 0x72, 0x6c, 0x9b, 0x68, 0x24, 0x38, 0xda, 0xc2, 0x31, 0x25, 0x96, 0x46, + 0x06, 0x78, 0x62, 0x78, 0xca, 0x13, 0x6c, 0x4c, 0x08, 0x23, 0x7c, 0x5e, 0x2e, 0xfa, 0xc2, 0x9f, + 0x52, 0x19, 0xba, 0x00, 0x05, 0xbe, 0xab, 0x74, 0x53, 0x23, 0xcf, 0x58, 0xf5, 0xcc, 0xc8, 0x7c, + 0xa3, 0x35, 0xa8, 0x84, 0x4e, 0xff, 0xc8, 0xb5, 0xcc, 0x80, 0x9a, 0x6c, 0x0a, 0x2a, 0x60, 0xd3, + 0xdf, 0x9e, 0x2d, 0xdc, 0xef, 0x2d, 0x5e, 0xde, 0x2c, 0xa7, 0xca, 0x7f, 0x4a, 0x42, 0x9a, 0xd5, + 0x8b, 0x35, 0x28, 0xf4, 0x1f, 0x76, 0x25, 0xa5, 0xde, 0x39, 0x3e, 0x6c, 0x4a, 0x42, 0x02, 0x95, + 0x00, 0x98, 0xe0, 0x5e, 0xb3, 0x53, 0xed, 0x0b, 0xc9, 0x70, 0xdc, 0x68, 0xf7, 0xf7, 0x6f, 0x0a, + 0xa9, 0xd0, 0xe0, 0x98, 0x0b, 0xd2, 0x51, 0xc0, 0x8d, 0x3d, 0x21, 0x83, 0x04, 0x28, 0x72, 0x07, + 0x8d, 0x07, 0x52, 0x7d, 0xff, 0xa6, 0x90, 0x8d, 0x4b, 0x6e, 0xec, 0x09, 0x2b, 0x68, 0x15, 0xf2, + 0x4c, 0x72, 0xd8, 0xe9, 0x34, 0x85, 0x5c, 0xe8, 0xb3, 0xd7, 0x97, 0x1b, 0xed, 0x23, 0x21, 0x1f, + 0xfa, 0x3c, 0x92, 0x3b, 0xc7, 0x5d, 0x01, 0x42, 0x0f, 0x2d, 0xa9, 0xd7, 0xab, 0x1e, 0x49, 0x42, + 0x21, 0x44, 0x1c, 0x3e, 0xec, 0x4b, 0x3d, 0xa1, 0x18, 0x0b, 0xeb, 0xc6, 0x9e, 0xb0, 0x1a, 0x4e, + 0x21, 0xb5, 0x8f, 0x5b, 0x42, 0x09, 0xad, 0xc3, 0x2a, 0x9f, 0x22, 0x08, 0x62, 0x6d, 0x46, 0xb4, + 0x7f, 0x53, 0x10, 0xa6, 0x81, 0x70, 0x2f, 0xeb, 0x31, 0xc1, 0xfe, 0x4d, 0x01, 0x95, 0x6b, 0x90, + 0x61, 0xec, 0x42, 0x08, 0x4a, 0xcd, 0xea, 0xa1, 0xd4, 0x54, 0x3a, 0xdd, 0x7e, 0xa3, 0xd3, 0xae, + 0x36, 0x85, 0xc4, 0x54, 0x26, 0x4b, 0x3f, 0x39, 0x6e, 0xc8, 0x52, 0x5d, 0x48, 0x46, 0x65, 0x5d, + 0xa9, 0xda, 0x97, 0xea, 0x42, 0xaa, 0xac, 0xc2, 0xe6, 0xa2, 0x3a, 0xb9, 0x70, 0x67, 0x44, 0x3e, + 0x71, 0x72, 0xc9, 0x27, 0x66, 0xbe, 0xe6, 0x3e, 0xf1, 0xbf, 0x92, 0xb0, 0xb1, 0xe0, 0xac, 0x58, + 0x38, 0xc9, 0x0f, 0x21, 0xc3, 0x29, 0xca, 0x4f, 0xcf, 0x2b, 0x0b, 0x0f, 0x1d, 0x46, 0xd8, 0xb9, + 0x13, 0x94, 0xd9, 0x45, 0x3b, 0x88, 0xd4, 0x92, 0x0e, 0x82, 0xba, 0x98, 0xab, 0xe9, 0x3f, 0x9f, + 0xab, 0xe9, 0xfc, 0xd8, 0xdb, 0x3f, 0xcb, 0xb1, 0xc7, 0x64, 0xdf, 0xae, 0xb6, 0x67, 0x16, 0xd4, + 0xf6, 0xbb, 0xb0, 0x3e, 0xe7, 0xe8, 0xcc, 0x35, 0xf6, 0x97, 0x09, 0x10, 0x97, 0x25, 0xe7, 0x15, + 0x95, 0x2e, 0x19, 0xab, 0x74, 0x77, 0x67, 0x33, 0x78, 0x71, 0xf9, 0x47, 0x98, 0xfb, 0xd6, 0xdf, + 0x24, 0x60, 0x6b, 0x71, 0xa7, 0xb8, 0x30, 0x86, 0xcf, 0x21, 0x3b, 0x26, 0xde, 0xc8, 0x0a, 0xba, + 0xa5, 0x8f, 0x16, 0x9c, 0xc1, 0x54, 0x3d, 0xfb, 0xb1, 0x7d, 0xab, 0xe8, 0x21, 0x9e, 0x5a, 0xd6, + 0xee, 0xf1, 0x68, 0xe6, 0x22, 0xfd, 0x55, 0x12, 0xde, 0x5e, 0xe8, 0x7c, 0x61, 0xa0, 0xef, 0x01, + 0xe8, 0xa6, 0x3d, 0xf1, 0x78, 0x47, 0xc4, 0x0b, 0x6c, 0x9e, 0x49, 0x58, 0xf1, 0xa2, 0xc5, 0x73, + 0xe2, 0x85, 0xfa, 0x14, 0xd3, 0x03, 0x17, 0x31, 0xc0, 0x9d, 0x69, 0xa0, 0x69, 0x16, 0xe8, 0xfb, + 0x4b, 0x56, 0x3a, 0x47, 0xcc, 0x4f, 0x41, 0x50, 0x0d, 0x9d, 0x98, 0x9e, 0xe2, 0x7a, 0x0e, 0xc1, + 0x63, 0xdd, 0x1c, 0xb2, 0x13, 0x24, 0x77, 0x90, 0x19, 0x60, 0xc3, 0x25, 0xf2, 0x1a, 0x57, 0xf7, + 0x02, 0x2d, 0xb5, 0x60, 0x04, 0x72, 0x22, 0x16, 0xd9, 0x98, 0x05, 0x57, 0x87, 0x16, 0xe5, 0x5f, + 0xe7, 0xa1, 0x10, 0xe9, 0xab, 0xd1, 0x45, 0x28, 0x3e, 0xc2, 0x4f, 0xb0, 0x12, 0xdc, 0x95, 0x78, + 0x26, 0x0a, 0x54, 0xd6, 0xf5, 0xef, 0x4b, 0x9f, 0xc2, 0x26, 0x83, 0x58, 0x13, 0x8f, 0x38, 0x8a, + 0x6a, 0x60, 0xd7, 0x65, 0x49, 0xcb, 0x31, 0x28, 0xa2, 0xba, 0x0e, 0x55, 0xd5, 0x02, 0x0d, 0xba, + 0x05, 0x1b, 0xcc, 0x62, 0x3c, 0x31, 0x3c, 0xdd, 0x36, 0x88, 0x42, 0x6f, 0x6f, 0x2e, 0x3b, 0x49, + 0xc2, 0xc8, 0xd6, 0x29, 0xa2, 0xe5, 0x03, 0x68, 0x44, 0x2e, 0xaa, 0xc3, 0x7b, 0xcc, 0x6c, 0x48, + 0x4c, 0xe2, 0x60, 0x8f, 0x28, 0xe4, 0xeb, 0x09, 0x36, 0x5c, 0x05, 0x9b, 0x9a, 0x32, 0xc2, 0xee, + 0x48, 0xdc, 0xa4, 0x0e, 0x0e, 0x93, 0x62, 0x42, 0x3e, 0x4f, 0x81, 0x47, 0x3e, 0x4e, 0x62, 0xb0, + 0xaa, 0xa9, 0x7d, 0x81, 0xdd, 0x11, 0x3a, 0x80, 0x2d, 0xe6, 0xc5, 0xf5, 0x1c, 0xdd, 0x1c, 0x2a, + 0xea, 0x88, 0xa8, 0x8f, 0x95, 0x89, 0x37, 0xb8, 0x23, 0xbe, 0x13, 0x9d, 0x9f, 0x45, 0xd8, 0x63, + 0x98, 0x1a, 0x85, 0x1c, 0x7b, 0x83, 0x3b, 0xa8, 0x07, 0x45, 0xfa, 0x31, 0xc6, 0xfa, 0x73, 0xa2, + 0x0c, 0x2c, 0x87, 0x1d, 0x8d, 0xa5, 0x05, 0xa5, 0x29, 0x92, 0xc1, 0x4a, 0xc7, 0x37, 0x68, 0x59, + 0x1a, 0x39, 0xc8, 0xf4, 0xba, 0x92, 0x54, 0x97, 0x0b, 0x81, 0x97, 0x7b, 0x96, 0x43, 0x09, 0x35, + 0xb4, 0xc2, 0x04, 0x17, 0x38, 0xa1, 0x86, 0x56, 0x90, 0xde, 0x5b, 0xb0, 0xa1, 0xaa, 0x7c, 0xcd, + 0xba, 0xaa, 0xf8, 0x77, 0x2c, 0x57, 0x14, 0x62, 0xc9, 0x52, 0xd5, 0x23, 0x0e, 0xf0, 0x39, 0xee, + 0xa2, 0xcf, 0xe0, 0xed, 0x69, 0xb2, 0xa2, 0x86, 0xeb, 0x73, 0xab, 0x9c, 0x35, 0xbd, 0x05, 0x1b, + 0xf6, 0xe9, 0xbc, 0x21, 0x8a, 0xcd, 0x68, 0x9f, 0xce, 0x9a, 0xdd, 0x86, 0x4d, 0x7b, 0x64, 0xcf, + 0xdb, 0x5d, 0x8d, 0xda, 0x21, 0x7b, 0x64, 0xcf, 0x1a, 0x5e, 0x62, 0x17, 0x6e, 0x87, 0xa8, 0xd8, + 0x23, 0x9a, 0x78, 0x2e, 0x0a, 0x8f, 0x28, 0xd0, 0x75, 0x10, 0x54, 0x55, 0x21, 0x26, 0x3e, 0x31, + 0x88, 0x82, 0x1d, 0x62, 0x62, 0x57, 0xbc, 0x10, 0x05, 0x97, 0x54, 0x55, 0x62, 0xda, 0x2a, 0x53, + 0xa2, 0xab, 0xb0, 0x6e, 0x9d, 0x3c, 0x52, 0x39, 0x25, 0x15, 0xdb, 0x21, 0x03, 0xfd, 0x99, 0xf8, + 0x21, 0xcb, 0xef, 0x1a, 0x55, 0x30, 0x42, 0x76, 0x99, 0x18, 0x5d, 0x01, 0x41, 0x75, 0x47, 0xd8, + 0xb1, 0x59, 0x4d, 0x76, 0x6d, 0xac, 0x12, 0xf1, 0x12, 0x87, 0x72, 0x79, 0x3b, 0x10, 0xd3, 0x2d, + 0xe1, 0x3e, 0xd5, 0x07, 0x5e, 0xe0, 0xf1, 0x32, 0xdf, 0x12, 0x4c, 0xe6, 0x7b, 0xdb, 0x05, 0x81, + 0xa6, 0x22, 0x36, 0xf1, 0x2e, 0x83, 0x95, 0xec, 0x91, 0x1d, 0x9d, 0xf7, 0x03, 0x58, 0xa5, 0xc8, + 0xe9, 0xa4, 0x57, 0x78, 0x43, 0x66, 0x8f, 0x22, 0x33, 0xde, 0x84, 0x2d, 0x0a, 0x1a, 0x13, 0x0f, + 0x6b, 0xd8, 0xc3, 0x11, 0xf4, 0xc7, 0x0c, 0x4d, 0xf3, 0xde, 0xf2, 0x95, 0xb1, 0x38, 0x9d, 0xc9, + 0xc9, 0x69, 0xc8, 0xac, 0x4f, 0x78, 0x9c, 0x54, 0x16, 0x70, 0xeb, 0xb5, 0x35, 0xdd, 0xe5, 0x03, + 0x28, 0x46, 0x89, 0x8f, 0xf2, 0xc0, 0xa9, 0x2f, 0x24, 0x68, 0x17, 0x54, 0xeb, 0xd4, 0x69, 0xff, + 0xf2, 0x95, 0x24, 0x24, 0x69, 0x1f, 0xd5, 0x6c, 0xf4, 0x25, 0x45, 0x3e, 0x6e, 0xf7, 0x1b, 0x2d, + 0x49, 0x48, 0x45, 0x1b, 0xf6, 0xbf, 0x26, 0xa1, 0x14, 0xbf, 0x7b, 0xa1, 0x1f, 0xc0, 0xb9, 0xe0, + 0xa1, 0xc4, 0x25, 0x9e, 0xf2, 0x54, 0x77, 0xd8, 0x5e, 0x1c, 0x63, 0x7e, 0x2e, 0x86, 0x6c, 0xd8, + 0xf4, 0x51, 0x3d, 0xe2, 0x7d, 0xa9, 0x3b, 0x74, 0xa7, 0x8d, 0xb1, 0x87, 0x9a, 0x70, 0xc1, 0xb4, + 0x14, 0xd7, 0xc3, 0xa6, 0x86, 0x1d, 0x4d, 0x99, 0x3e, 0x51, 0x29, 0x58, 0x55, 0x89, 0xeb, 0x5a, + 0xfc, 0x0c, 0x0c, 0xbd, 0xbc, 0x6b, 0x5a, 0x3d, 0x1f, 0x3c, 0x3d, 0x1c, 0xaa, 0x3e, 0x74, 0x86, + 0xb9, 0xa9, 0x65, 0xcc, 0x7d, 0x07, 0xf2, 0x63, 0x6c, 0x2b, 0xc4, 0xf4, 0x9c, 0x53, 0xd6, 0x71, + 0xe7, 0xe4, 0xdc, 0x18, 0xdb, 0x12, 0x1d, 0xbf, 0x99, 0x8b, 0xcf, 0x3f, 0x52, 0x50, 0x8c, 0x76, + 0xdd, 0xf4, 0x12, 0xa3, 0xb2, 0x03, 0x2a, 0xc1, 0x4a, 0xd8, 0x07, 0x2f, 0xed, 0xd1, 0x2b, 0x35, + 0x7a, 0x72, 0x1d, 0x64, 0x79, 0x2f, 0x2c, 0x73, 0x4b, 0xda, 0x35, 0x50, 0x6a, 0x11, 0xde, 0x7b, + 0xe4, 0x64, 0x7f, 0x84, 0x8e, 0x20, 0xfb, 0xc8, 0x65, 0xbe, 0xb3, 0xcc, 0xf7, 0x87, 0x2f, 0xf7, + 0x7d, 0xbf, 0xc7, 0x9c, 0xe7, 0xef, 0xf7, 0x94, 0x76, 0x47, 0x6e, 0x55, 0x9b, 0xb2, 0x6f, 0x8e, + 0xce, 0x43, 0xda, 0xc0, 0xcf, 0x4f, 0xe3, 0x67, 0x1c, 0x13, 0x9d, 0x35, 0xf1, 0xe7, 0x21, 0xfd, + 0x94, 0xe0, 0xc7, 0xf1, 0x93, 0x85, 0x89, 0x5e, 0x23, 0xf5, 0xaf, 0x43, 0x86, 0xe5, 0x0b, 0x01, + 0xf8, 0x19, 0x13, 0xde, 0x42, 0x39, 0x48, 0xd7, 0x3a, 0x32, 0xa5, 0xbf, 0x00, 0x45, 0x2e, 0x55, + 0xba, 0x0d, 0xa9, 0x26, 0x09, 0xc9, 0xf2, 0x2d, 0xc8, 0xf2, 0x24, 0xd0, 0xad, 0x11, 0xa6, 0x41, + 0x78, 0xcb, 0x1f, 0xfa, 0x3e, 0x12, 0x81, 0xf6, 0xb8, 0x75, 0x28, 0xc9, 0x42, 0x32, 0xfa, 0x79, + 0x5d, 0x28, 0x46, 0x1b, 0xee, 0x37, 0xc3, 0xa9, 0xbf, 0x24, 0xa0, 0x10, 0x69, 0xa0, 0x69, 0xe7, + 0x83, 0x0d, 0xc3, 0x7a, 0xaa, 0x60, 0x43, 0xc7, 0xae, 0x4f, 0x0a, 0x60, 0xa2, 0x2a, 0x95, 0x9c, + 0xf5, 0xa3, 0xbd, 0x91, 0xe0, 0x7f, 0x9f, 0x00, 0x61, 0xb6, 0x77, 0x9d, 0x09, 0x30, 0xf1, 0xbd, + 0x06, 0xf8, 0xbb, 0x04, 0x94, 0xe2, 0x0d, 0xeb, 0x4c, 0x78, 0x17, 0xbf, 0xd7, 0xf0, 0xfe, 0x99, + 0x84, 0xd5, 0x58, 0x9b, 0x7a, 0xd6, 0xe8, 0xbe, 0x86, 0x75, 0x5d, 0x23, 0x63, 0xdb, 0xf2, 0x88, + 0xa9, 0x9e, 0x2a, 0x06, 0x79, 0x42, 0x0c, 0xb1, 0xcc, 0x0a, 0xc5, 0xf5, 0x97, 0x37, 0xc2, 0x95, + 0xc6, 0xd4, 0xae, 0x49, 0xcd, 0x0e, 0x36, 0x1a, 0x75, 0xa9, 0xd5, 0xed, 0xf4, 0xa5, 0x76, 0xed, + 0xa1, 0x72, 0xdc, 0xfe, 0x71, 0xbb, 0xf3, 0x65, 0x5b, 0x16, 0xf4, 0x19, 0xd8, 0x6b, 0xdc, 0xea, + 0x5d, 0x10, 0x66, 0x83, 0x42, 0xe7, 0x60, 0x51, 0x58, 0xc2, 0x5b, 0x68, 0x03, 0xd6, 0xda, 0x1d, + 0xa5, 0xd7, 0xa8, 0x4b, 0x8a, 0x74, 0xef, 0x9e, 0x54, 0xeb, 0xf7, 0xf8, 0xd3, 0x46, 0x88, 0xee, + 0xc7, 0x37, 0xf5, 0x6f, 0x53, 0xb0, 0xb1, 0x20, 0x12, 0x54, 0xf5, 0x2f, 0x25, 0xfc, 0x9e, 0xf4, + 0xc9, 0x59, 0xa2, 0xaf, 0xd0, 0xae, 0xa0, 0x8b, 0x1d, 0xcf, 0xbf, 0xc3, 0x5c, 0x01, 0x9a, 0x25, + 0xd3, 0xd3, 0x07, 0x3a, 0x71, 0xfc, 0x97, 0x20, 0x7e, 0x53, 0x59, 0x9b, 0xca, 0xf9, 0x63, 0xd0, + 0xc7, 0x80, 0x6c, 0xcb, 0xd5, 0x3d, 0xfd, 0x09, 0x51, 0x74, 0x33, 0x78, 0x36, 0xa2, 0x37, 0x97, + 0xb4, 0x2c, 0x04, 0x9a, 0x86, 0xe9, 0x85, 0x68, 0x93, 0x0c, 0xf1, 0x0c, 0x9a, 0x16, 0xf0, 0x94, + 0x2c, 0x04, 0x9a, 0x10, 0x7d, 0x11, 0x8a, 0x9a, 0x35, 0xa1, 0xed, 0x1c, 0xc7, 0xd1, 0xf3, 0x22, + 0x21, 0x17, 0xb8, 0x2c, 0x84, 0xf8, 0x8d, 0xfa, 0xf4, 0xbd, 0xaa, 0x28, 0x17, 0xb8, 0x8c, 0x43, + 0x2e, 0xc3, 0x1a, 0x1e, 0x0e, 0x1d, 0xea, 0x3c, 0x70, 0xc4, 0xaf, 0x1e, 0xa5, 0x50, 0xcc, 0x80, + 0xdb, 0xf7, 0x21, 0x17, 0xe4, 0x81, 0x1e, 0xc9, 0x34, 0x13, 0x8a, 0xcd, 0xef, 0xd3, 0xc9, 0xdd, + 0xbc, 0x9c, 0x33, 0x03, 0xe5, 0x45, 0x28, 0xea, 0xae, 0x32, 0x7d, 0x7e, 0x4f, 0xee, 0x24, 0x77, + 0x73, 0x72, 0x41, 0x77, 0xc3, 0xa7, 0xcb, 0xf2, 0x37, 0x49, 0x28, 0xc5, 0x7f, 0x3e, 0x40, 0x75, + 0xc8, 0x19, 0x96, 0x8a, 0x19, 0xb5, 0xf8, 0x6f, 0x57, 0xbb, 0xaf, 0xf8, 0xc5, 0xa1, 0xd2, 0xf4, + 0xf1, 0x72, 0x68, 0xb9, 0xfd, 0xb7, 0x04, 0xe4, 0x02, 0x31, 0xda, 0x82, 0xb4, 0x8d, 0xbd, 0x11, + 0x73, 0x97, 0x39, 0x4c, 0x0a, 0x09, 0x99, 0x8d, 0xa9, 0xdc, 0xb5, 0xb1, 0xc9, 0x28, 0xe0, 0xcb, + 0xe9, 0x98, 0x7e, 0x57, 0x83, 0x60, 0x8d, 0xdd, 0x6b, 0xac, 0xf1, 0x98, 0x98, 0x9e, 0x1b, 0x7c, + 0x57, 0x5f, 0x5e, 0xf3, 0xc5, 0xe8, 0x1a, 0xac, 0x7b, 0x0e, 0xd6, 0x8d, 0x18, 0x36, 0xcd, 0xb0, + 0x42, 0xa0, 0x08, 0xc1, 0x07, 0x70, 0x3e, 0xf0, 0xab, 0x11, 0x0f, 0xab, 0x23, 0xa2, 0x4d, 0x8d, + 0xb2, 0xec, 0xfd, 0xe2, 0x9c, 0x0f, 0xa8, 0xfb, 0xfa, 0xc0, 0xb6, 0xfc, 0xf7, 0x04, 0xac, 0x07, + 0x37, 0x31, 0x2d, 0x4c, 0x56, 0x0b, 0x00, 0x9b, 0xa6, 0xe5, 0x45, 0xd3, 0x35, 0x4f, 0xe5, 0x39, + 0xbb, 0x4a, 0x35, 0x34, 0x92, 0x23, 0x0e, 0xb6, 0xc7, 0x00, 0x53, 0xcd, 0xd2, 0xb4, 0x5d, 0x80, + 0x82, 0xff, 0xdb, 0x10, 0xfb, 0x81, 0x91, 0xdf, 0xdd, 0x81, 0x8b, 0xe8, 0x95, 0x0d, 0x6d, 0x42, + 0xe6, 0x84, 0x0c, 0x75, 0xd3, 0x7f, 0xf1, 0xe5, 0x83, 0xe0, 0x85, 0x25, 0x1d, 0xbe, 0xb0, 0x1c, + 0xfe, 0x0c, 0x36, 0x54, 0x6b, 0x3c, 0x1b, 0xee, 0xa1, 0x30, 0xf3, 0x7e, 0xe0, 0x7e, 0x91, 0xf8, + 0x0a, 0xa6, 0x2d, 0xe6, 0xff, 0x12, 0x89, 0x3f, 0x24, 0x53, 0x47, 0xdd, 0xc3, 0x3f, 0x26, 0xb7, + 0x8f, 0xb8, 0x69, 0x37, 0x58, 0xa9, 0x4c, 0x06, 0x06, 0x51, 0x69, 0xf4, 0xff, 0x0f, 0x00, 0x00, + 0xff, 0xff, 0x88, 0x17, 0xc1, 0xbe, 0x38, 0x1d, 0x00, 0x00, +} diff --git a/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor_gostring.gen.go b/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor_gostring.gen.go new file mode 100644 index 0000000000..165b2110df --- /dev/null +++ b/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor_gostring.gen.go @@ -0,0 +1,752 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: descriptor.proto + +package descriptor + +import ( + fmt "fmt" + github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" + proto "github.com/gogo/protobuf/proto" + math "math" + reflect "reflect" + sort "sort" + strconv "strconv" + strings "strings" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +func (this *FileDescriptorSet) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 5) + s = append(s, "&descriptor.FileDescriptorSet{") + if this.File != nil { + s = append(s, "File: "+fmt.Sprintf("%#v", this.File)+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *FileDescriptorProto) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 16) + s = append(s, "&descriptor.FileDescriptorProto{") + if this.Name != nil { + s = append(s, "Name: "+valueToGoStringDescriptor(this.Name, "string")+",\n") + } + if this.Package != nil { + s = append(s, "Package: "+valueToGoStringDescriptor(this.Package, "string")+",\n") + } + if this.Dependency != nil { + s = append(s, "Dependency: "+fmt.Sprintf("%#v", this.Dependency)+",\n") + } + if this.PublicDependency != nil { + s = append(s, "PublicDependency: "+fmt.Sprintf("%#v", this.PublicDependency)+",\n") + } + if this.WeakDependency != nil { + s = append(s, "WeakDependency: "+fmt.Sprintf("%#v", this.WeakDependency)+",\n") + } + if this.MessageType != nil { + s = append(s, "MessageType: "+fmt.Sprintf("%#v", this.MessageType)+",\n") + } + if this.EnumType != nil { + s = append(s, "EnumType: "+fmt.Sprintf("%#v", this.EnumType)+",\n") + } + if this.Service != nil { + s = append(s, "Service: "+fmt.Sprintf("%#v", this.Service)+",\n") + } + if this.Extension != nil { + s = append(s, "Extension: "+fmt.Sprintf("%#v", this.Extension)+",\n") + } + if this.Options != nil { + s = append(s, "Options: "+fmt.Sprintf("%#v", this.Options)+",\n") + } + if this.SourceCodeInfo != nil { + s = append(s, "SourceCodeInfo: "+fmt.Sprintf("%#v", this.SourceCodeInfo)+",\n") + } + if this.Syntax != nil { + s = append(s, "Syntax: "+valueToGoStringDescriptor(this.Syntax, "string")+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *DescriptorProto) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 14) + s = append(s, "&descriptor.DescriptorProto{") + if this.Name != nil { + s = append(s, "Name: "+valueToGoStringDescriptor(this.Name, "string")+",\n") + } + if this.Field != nil { + s = append(s, "Field: "+fmt.Sprintf("%#v", this.Field)+",\n") + } + if this.Extension != nil { + s = append(s, "Extension: "+fmt.Sprintf("%#v", this.Extension)+",\n") + } + if this.NestedType != nil { + s = append(s, "NestedType: "+fmt.Sprintf("%#v", this.NestedType)+",\n") + } + if this.EnumType != nil { + s = append(s, "EnumType: "+fmt.Sprintf("%#v", this.EnumType)+",\n") + } + if this.ExtensionRange != nil { + s = append(s, "ExtensionRange: "+fmt.Sprintf("%#v", this.ExtensionRange)+",\n") + } + if this.OneofDecl != nil { + s = append(s, "OneofDecl: "+fmt.Sprintf("%#v", this.OneofDecl)+",\n") + } + if this.Options != nil { + s = append(s, "Options: "+fmt.Sprintf("%#v", this.Options)+",\n") + } + if this.ReservedRange != nil { + s = append(s, "ReservedRange: "+fmt.Sprintf("%#v", this.ReservedRange)+",\n") + } + if this.ReservedName != nil { + s = append(s, "ReservedName: "+fmt.Sprintf("%#v", this.ReservedName)+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *DescriptorProto_ExtensionRange) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 7) + s = append(s, "&descriptor.DescriptorProto_ExtensionRange{") + if this.Start != nil { + s = append(s, "Start: "+valueToGoStringDescriptor(this.Start, "int32")+",\n") + } + if this.End != nil { + s = append(s, "End: "+valueToGoStringDescriptor(this.End, "int32")+",\n") + } + if this.Options != nil { + s = append(s, "Options: "+fmt.Sprintf("%#v", this.Options)+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *DescriptorProto_ReservedRange) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 6) + s = append(s, "&descriptor.DescriptorProto_ReservedRange{") + if this.Start != nil { + s = append(s, "Start: "+valueToGoStringDescriptor(this.Start, "int32")+",\n") + } + if this.End != nil { + s = append(s, "End: "+valueToGoStringDescriptor(this.End, "int32")+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *ExtensionRangeOptions) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 5) + s = append(s, "&descriptor.ExtensionRangeOptions{") + if this.UninterpretedOption != nil { + s = append(s, "UninterpretedOption: "+fmt.Sprintf("%#v", this.UninterpretedOption)+",\n") + } + s = append(s, "XXX_InternalExtensions: "+extensionToGoStringDescriptor(this)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *FieldDescriptorProto) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 14) + s = append(s, "&descriptor.FieldDescriptorProto{") + if this.Name != nil { + s = append(s, "Name: "+valueToGoStringDescriptor(this.Name, "string")+",\n") + } + if this.Number != nil { + s = append(s, "Number: "+valueToGoStringDescriptor(this.Number, "int32")+",\n") + } + if this.Label != nil { + s = append(s, "Label: "+valueToGoStringDescriptor(this.Label, "FieldDescriptorProto_Label")+",\n") + } + if this.Type != nil { + s = append(s, "Type: "+valueToGoStringDescriptor(this.Type, "FieldDescriptorProto_Type")+",\n") + } + if this.TypeName != nil { + s = append(s, "TypeName: "+valueToGoStringDescriptor(this.TypeName, "string")+",\n") + } + if this.Extendee != nil { + s = append(s, "Extendee: "+valueToGoStringDescriptor(this.Extendee, "string")+",\n") + } + if this.DefaultValue != nil { + s = append(s, "DefaultValue: "+valueToGoStringDescriptor(this.DefaultValue, "string")+",\n") + } + if this.OneofIndex != nil { + s = append(s, "OneofIndex: "+valueToGoStringDescriptor(this.OneofIndex, "int32")+",\n") + } + if this.JsonName != nil { + s = append(s, "JsonName: "+valueToGoStringDescriptor(this.JsonName, "string")+",\n") + } + if this.Options != nil { + s = append(s, "Options: "+fmt.Sprintf("%#v", this.Options)+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *OneofDescriptorProto) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 6) + s = append(s, "&descriptor.OneofDescriptorProto{") + if this.Name != nil { + s = append(s, "Name: "+valueToGoStringDescriptor(this.Name, "string")+",\n") + } + if this.Options != nil { + s = append(s, "Options: "+fmt.Sprintf("%#v", this.Options)+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *EnumDescriptorProto) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 9) + s = append(s, "&descriptor.EnumDescriptorProto{") + if this.Name != nil { + s = append(s, "Name: "+valueToGoStringDescriptor(this.Name, "string")+",\n") + } + if this.Value != nil { + s = append(s, "Value: "+fmt.Sprintf("%#v", this.Value)+",\n") + } + if this.Options != nil { + s = append(s, "Options: "+fmt.Sprintf("%#v", this.Options)+",\n") + } + if this.ReservedRange != nil { + s = append(s, "ReservedRange: "+fmt.Sprintf("%#v", this.ReservedRange)+",\n") + } + if this.ReservedName != nil { + s = append(s, "ReservedName: "+fmt.Sprintf("%#v", this.ReservedName)+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *EnumDescriptorProto_EnumReservedRange) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 6) + s = append(s, "&descriptor.EnumDescriptorProto_EnumReservedRange{") + if this.Start != nil { + s = append(s, "Start: "+valueToGoStringDescriptor(this.Start, "int32")+",\n") + } + if this.End != nil { + s = append(s, "End: "+valueToGoStringDescriptor(this.End, "int32")+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *EnumValueDescriptorProto) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 7) + s = append(s, "&descriptor.EnumValueDescriptorProto{") + if this.Name != nil { + s = append(s, "Name: "+valueToGoStringDescriptor(this.Name, "string")+",\n") + } + if this.Number != nil { + s = append(s, "Number: "+valueToGoStringDescriptor(this.Number, "int32")+",\n") + } + if this.Options != nil { + s = append(s, "Options: "+fmt.Sprintf("%#v", this.Options)+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *ServiceDescriptorProto) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 7) + s = append(s, "&descriptor.ServiceDescriptorProto{") + if this.Name != nil { + s = append(s, "Name: "+valueToGoStringDescriptor(this.Name, "string")+",\n") + } + if this.Method != nil { + s = append(s, "Method: "+fmt.Sprintf("%#v", this.Method)+",\n") + } + if this.Options != nil { + s = append(s, "Options: "+fmt.Sprintf("%#v", this.Options)+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *MethodDescriptorProto) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 10) + s = append(s, "&descriptor.MethodDescriptorProto{") + if this.Name != nil { + s = append(s, "Name: "+valueToGoStringDescriptor(this.Name, "string")+",\n") + } + if this.InputType != nil { + s = append(s, "InputType: "+valueToGoStringDescriptor(this.InputType, "string")+",\n") + } + if this.OutputType != nil { + s = append(s, "OutputType: "+valueToGoStringDescriptor(this.OutputType, "string")+",\n") + } + if this.Options != nil { + s = append(s, "Options: "+fmt.Sprintf("%#v", this.Options)+",\n") + } + if this.ClientStreaming != nil { + s = append(s, "ClientStreaming: "+valueToGoStringDescriptor(this.ClientStreaming, "bool")+",\n") + } + if this.ServerStreaming != nil { + s = append(s, "ServerStreaming: "+valueToGoStringDescriptor(this.ServerStreaming, "bool")+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *FileOptions) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 25) + s = append(s, "&descriptor.FileOptions{") + if this.JavaPackage != nil { + s = append(s, "JavaPackage: "+valueToGoStringDescriptor(this.JavaPackage, "string")+",\n") + } + if this.JavaOuterClassname != nil { + s = append(s, "JavaOuterClassname: "+valueToGoStringDescriptor(this.JavaOuterClassname, "string")+",\n") + } + if this.JavaMultipleFiles != nil { + s = append(s, "JavaMultipleFiles: "+valueToGoStringDescriptor(this.JavaMultipleFiles, "bool")+",\n") + } + if this.JavaGenerateEqualsAndHash != nil { + s = append(s, "JavaGenerateEqualsAndHash: "+valueToGoStringDescriptor(this.JavaGenerateEqualsAndHash, "bool")+",\n") + } + if this.JavaStringCheckUtf8 != nil { + s = append(s, "JavaStringCheckUtf8: "+valueToGoStringDescriptor(this.JavaStringCheckUtf8, "bool")+",\n") + } + if this.OptimizeFor != nil { + s = append(s, "OptimizeFor: "+valueToGoStringDescriptor(this.OptimizeFor, "FileOptions_OptimizeMode")+",\n") + } + if this.GoPackage != nil { + s = append(s, "GoPackage: "+valueToGoStringDescriptor(this.GoPackage, "string")+",\n") + } + if this.CcGenericServices != nil { + s = append(s, "CcGenericServices: "+valueToGoStringDescriptor(this.CcGenericServices, "bool")+",\n") + } + if this.JavaGenericServices != nil { + s = append(s, "JavaGenericServices: "+valueToGoStringDescriptor(this.JavaGenericServices, "bool")+",\n") + } + if this.PyGenericServices != nil { + s = append(s, "PyGenericServices: "+valueToGoStringDescriptor(this.PyGenericServices, "bool")+",\n") + } + if this.PhpGenericServices != nil { + s = append(s, "PhpGenericServices: "+valueToGoStringDescriptor(this.PhpGenericServices, "bool")+",\n") + } + if this.Deprecated != nil { + s = append(s, "Deprecated: "+valueToGoStringDescriptor(this.Deprecated, "bool")+",\n") + } + if this.CcEnableArenas != nil { + s = append(s, "CcEnableArenas: "+valueToGoStringDescriptor(this.CcEnableArenas, "bool")+",\n") + } + if this.ObjcClassPrefix != nil { + s = append(s, "ObjcClassPrefix: "+valueToGoStringDescriptor(this.ObjcClassPrefix, "string")+",\n") + } + if this.CsharpNamespace != nil { + s = append(s, "CsharpNamespace: "+valueToGoStringDescriptor(this.CsharpNamespace, "string")+",\n") + } + if this.SwiftPrefix != nil { + s = append(s, "SwiftPrefix: "+valueToGoStringDescriptor(this.SwiftPrefix, "string")+",\n") + } + if this.PhpClassPrefix != nil { + s = append(s, "PhpClassPrefix: "+valueToGoStringDescriptor(this.PhpClassPrefix, "string")+",\n") + } + if this.PhpNamespace != nil { + s = append(s, "PhpNamespace: "+valueToGoStringDescriptor(this.PhpNamespace, "string")+",\n") + } + if this.PhpMetadataNamespace != nil { + s = append(s, "PhpMetadataNamespace: "+valueToGoStringDescriptor(this.PhpMetadataNamespace, "string")+",\n") + } + if this.RubyPackage != nil { + s = append(s, "RubyPackage: "+valueToGoStringDescriptor(this.RubyPackage, "string")+",\n") + } + if this.UninterpretedOption != nil { + s = append(s, "UninterpretedOption: "+fmt.Sprintf("%#v", this.UninterpretedOption)+",\n") + } + s = append(s, "XXX_InternalExtensions: "+extensionToGoStringDescriptor(this)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *MessageOptions) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 9) + s = append(s, "&descriptor.MessageOptions{") + if this.MessageSetWireFormat != nil { + s = append(s, "MessageSetWireFormat: "+valueToGoStringDescriptor(this.MessageSetWireFormat, "bool")+",\n") + } + if this.NoStandardDescriptorAccessor != nil { + s = append(s, "NoStandardDescriptorAccessor: "+valueToGoStringDescriptor(this.NoStandardDescriptorAccessor, "bool")+",\n") + } + if this.Deprecated != nil { + s = append(s, "Deprecated: "+valueToGoStringDescriptor(this.Deprecated, "bool")+",\n") + } + if this.MapEntry != nil { + s = append(s, "MapEntry: "+valueToGoStringDescriptor(this.MapEntry, "bool")+",\n") + } + if this.UninterpretedOption != nil { + s = append(s, "UninterpretedOption: "+fmt.Sprintf("%#v", this.UninterpretedOption)+",\n") + } + s = append(s, "XXX_InternalExtensions: "+extensionToGoStringDescriptor(this)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *FieldOptions) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 11) + s = append(s, "&descriptor.FieldOptions{") + if this.Ctype != nil { + s = append(s, "Ctype: "+valueToGoStringDescriptor(this.Ctype, "FieldOptions_CType")+",\n") + } + if this.Packed != nil { + s = append(s, "Packed: "+valueToGoStringDescriptor(this.Packed, "bool")+",\n") + } + if this.Jstype != nil { + s = append(s, "Jstype: "+valueToGoStringDescriptor(this.Jstype, "FieldOptions_JSType")+",\n") + } + if this.Lazy != nil { + s = append(s, "Lazy: "+valueToGoStringDescriptor(this.Lazy, "bool")+",\n") + } + if this.Deprecated != nil { + s = append(s, "Deprecated: "+valueToGoStringDescriptor(this.Deprecated, "bool")+",\n") + } + if this.Weak != nil { + s = append(s, "Weak: "+valueToGoStringDescriptor(this.Weak, "bool")+",\n") + } + if this.UninterpretedOption != nil { + s = append(s, "UninterpretedOption: "+fmt.Sprintf("%#v", this.UninterpretedOption)+",\n") + } + s = append(s, "XXX_InternalExtensions: "+extensionToGoStringDescriptor(this)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *OneofOptions) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 5) + s = append(s, "&descriptor.OneofOptions{") + if this.UninterpretedOption != nil { + s = append(s, "UninterpretedOption: "+fmt.Sprintf("%#v", this.UninterpretedOption)+",\n") + } + s = append(s, "XXX_InternalExtensions: "+extensionToGoStringDescriptor(this)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *EnumOptions) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 7) + s = append(s, "&descriptor.EnumOptions{") + if this.AllowAlias != nil { + s = append(s, "AllowAlias: "+valueToGoStringDescriptor(this.AllowAlias, "bool")+",\n") + } + if this.Deprecated != nil { + s = append(s, "Deprecated: "+valueToGoStringDescriptor(this.Deprecated, "bool")+",\n") + } + if this.UninterpretedOption != nil { + s = append(s, "UninterpretedOption: "+fmt.Sprintf("%#v", this.UninterpretedOption)+",\n") + } + s = append(s, "XXX_InternalExtensions: "+extensionToGoStringDescriptor(this)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *EnumValueOptions) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 6) + s = append(s, "&descriptor.EnumValueOptions{") + if this.Deprecated != nil { + s = append(s, "Deprecated: "+valueToGoStringDescriptor(this.Deprecated, "bool")+",\n") + } + if this.UninterpretedOption != nil { + s = append(s, "UninterpretedOption: "+fmt.Sprintf("%#v", this.UninterpretedOption)+",\n") + } + s = append(s, "XXX_InternalExtensions: "+extensionToGoStringDescriptor(this)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *ServiceOptions) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 6) + s = append(s, "&descriptor.ServiceOptions{") + if this.Deprecated != nil { + s = append(s, "Deprecated: "+valueToGoStringDescriptor(this.Deprecated, "bool")+",\n") + } + if this.UninterpretedOption != nil { + s = append(s, "UninterpretedOption: "+fmt.Sprintf("%#v", this.UninterpretedOption)+",\n") + } + s = append(s, "XXX_InternalExtensions: "+extensionToGoStringDescriptor(this)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *MethodOptions) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 7) + s = append(s, "&descriptor.MethodOptions{") + if this.Deprecated != nil { + s = append(s, "Deprecated: "+valueToGoStringDescriptor(this.Deprecated, "bool")+",\n") + } + if this.IdempotencyLevel != nil { + s = append(s, "IdempotencyLevel: "+valueToGoStringDescriptor(this.IdempotencyLevel, "MethodOptions_IdempotencyLevel")+",\n") + } + if this.UninterpretedOption != nil { + s = append(s, "UninterpretedOption: "+fmt.Sprintf("%#v", this.UninterpretedOption)+",\n") + } + s = append(s, "XXX_InternalExtensions: "+extensionToGoStringDescriptor(this)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *UninterpretedOption) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 11) + s = append(s, "&descriptor.UninterpretedOption{") + if this.Name != nil { + s = append(s, "Name: "+fmt.Sprintf("%#v", this.Name)+",\n") + } + if this.IdentifierValue != nil { + s = append(s, "IdentifierValue: "+valueToGoStringDescriptor(this.IdentifierValue, "string")+",\n") + } + if this.PositiveIntValue != nil { + s = append(s, "PositiveIntValue: "+valueToGoStringDescriptor(this.PositiveIntValue, "uint64")+",\n") + } + if this.NegativeIntValue != nil { + s = append(s, "NegativeIntValue: "+valueToGoStringDescriptor(this.NegativeIntValue, "int64")+",\n") + } + if this.DoubleValue != nil { + s = append(s, "DoubleValue: "+valueToGoStringDescriptor(this.DoubleValue, "float64")+",\n") + } + if this.StringValue != nil { + s = append(s, "StringValue: "+valueToGoStringDescriptor(this.StringValue, "byte")+",\n") + } + if this.AggregateValue != nil { + s = append(s, "AggregateValue: "+valueToGoStringDescriptor(this.AggregateValue, "string")+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *UninterpretedOption_NamePart) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 6) + s = append(s, "&descriptor.UninterpretedOption_NamePart{") + if this.NamePart != nil { + s = append(s, "NamePart: "+valueToGoStringDescriptor(this.NamePart, "string")+",\n") + } + if this.IsExtension != nil { + s = append(s, "IsExtension: "+valueToGoStringDescriptor(this.IsExtension, "bool")+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *SourceCodeInfo) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 5) + s = append(s, "&descriptor.SourceCodeInfo{") + if this.Location != nil { + s = append(s, "Location: "+fmt.Sprintf("%#v", this.Location)+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *SourceCodeInfo_Location) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 9) + s = append(s, "&descriptor.SourceCodeInfo_Location{") + if this.Path != nil { + s = append(s, "Path: "+fmt.Sprintf("%#v", this.Path)+",\n") + } + if this.Span != nil { + s = append(s, "Span: "+fmt.Sprintf("%#v", this.Span)+",\n") + } + if this.LeadingComments != nil { + s = append(s, "LeadingComments: "+valueToGoStringDescriptor(this.LeadingComments, "string")+",\n") + } + if this.TrailingComments != nil { + s = append(s, "TrailingComments: "+valueToGoStringDescriptor(this.TrailingComments, "string")+",\n") + } + if this.LeadingDetachedComments != nil { + s = append(s, "LeadingDetachedComments: "+fmt.Sprintf("%#v", this.LeadingDetachedComments)+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *GeneratedCodeInfo) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 5) + s = append(s, "&descriptor.GeneratedCodeInfo{") + if this.Annotation != nil { + s = append(s, "Annotation: "+fmt.Sprintf("%#v", this.Annotation)+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *GeneratedCodeInfo_Annotation) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 8) + s = append(s, "&descriptor.GeneratedCodeInfo_Annotation{") + if this.Path != nil { + s = append(s, "Path: "+fmt.Sprintf("%#v", this.Path)+",\n") + } + if this.SourceFile != nil { + s = append(s, "SourceFile: "+valueToGoStringDescriptor(this.SourceFile, "string")+",\n") + } + if this.Begin != nil { + s = append(s, "Begin: "+valueToGoStringDescriptor(this.Begin, "int32")+",\n") + } + if this.End != nil { + s = append(s, "End: "+valueToGoStringDescriptor(this.End, "int32")+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func valueToGoStringDescriptor(v interface{}, typ string) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) +} +func extensionToGoStringDescriptor(m github_com_gogo_protobuf_proto.Message) string { + e := github_com_gogo_protobuf_proto.GetUnsafeExtensionsMap(m) + if e == nil { + return "nil" + } + s := "proto.NewUnsafeXXX_InternalExtensions(map[int32]proto.Extension{" + keys := make([]int, 0, len(e)) + for k := range e { + keys = append(keys, int(k)) + } + sort.Ints(keys) + ss := []string{} + for _, k := range keys { + ss = append(ss, strconv.Itoa(k)+": "+e[int32(k)].GoString()) + } + s += strings.Join(ss, ",") + "})" + return s +} diff --git a/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/helper.go b/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/helper.go new file mode 100644 index 0000000000..e0846a357d --- /dev/null +++ b/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/helper.go @@ -0,0 +1,390 @@ +// Protocol Buffers for Go with Gadgets +// +// Copyright (c) 2013, The GoGo Authors. All rights reserved. +// http://github.com/gogo/protobuf +// +// 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 +// OWNER 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. + +package descriptor + +import ( + "strings" +) + +func (msg *DescriptorProto) GetMapFields() (*FieldDescriptorProto, *FieldDescriptorProto) { + if !msg.GetOptions().GetMapEntry() { + return nil, nil + } + return msg.GetField()[0], msg.GetField()[1] +} + +func dotToUnderscore(r rune) rune { + if r == '.' { + return '_' + } + return r +} + +func (field *FieldDescriptorProto) WireType() (wire int) { + switch *field.Type { + case FieldDescriptorProto_TYPE_DOUBLE: + return 1 + case FieldDescriptorProto_TYPE_FLOAT: + return 5 + case FieldDescriptorProto_TYPE_INT64: + return 0 + case FieldDescriptorProto_TYPE_UINT64: + return 0 + case FieldDescriptorProto_TYPE_INT32: + return 0 + case FieldDescriptorProto_TYPE_UINT32: + return 0 + case FieldDescriptorProto_TYPE_FIXED64: + return 1 + case FieldDescriptorProto_TYPE_FIXED32: + return 5 + case FieldDescriptorProto_TYPE_BOOL: + return 0 + case FieldDescriptorProto_TYPE_STRING: + return 2 + case FieldDescriptorProto_TYPE_GROUP: + return 2 + case FieldDescriptorProto_TYPE_MESSAGE: + return 2 + case FieldDescriptorProto_TYPE_BYTES: + return 2 + case FieldDescriptorProto_TYPE_ENUM: + return 0 + case FieldDescriptorProto_TYPE_SFIXED32: + return 5 + case FieldDescriptorProto_TYPE_SFIXED64: + return 1 + case FieldDescriptorProto_TYPE_SINT32: + return 0 + case FieldDescriptorProto_TYPE_SINT64: + return 0 + } + panic("unreachable") +} + +func (field *FieldDescriptorProto) GetKeyUint64() (x uint64) { + packed := field.IsPacked() + wireType := field.WireType() + fieldNumber := field.GetNumber() + if packed { + wireType = 2 + } + x = uint64(uint32(fieldNumber)<<3 | uint32(wireType)) + return x +} + +func (field *FieldDescriptorProto) GetKey3Uint64() (x uint64) { + packed := field.IsPacked3() + wireType := field.WireType() + fieldNumber := field.GetNumber() + if packed { + wireType = 2 + } + x = uint64(uint32(fieldNumber)<<3 | uint32(wireType)) + return x +} + +func (field *FieldDescriptorProto) GetKey() []byte { + x := field.GetKeyUint64() + i := 0 + keybuf := make([]byte, 0) + for i = 0; x > 127; i++ { + keybuf = append(keybuf, 0x80|uint8(x&0x7F)) + x >>= 7 + } + keybuf = append(keybuf, uint8(x)) + return keybuf +} + +func (field *FieldDescriptorProto) GetKey3() []byte { + x := field.GetKey3Uint64() + i := 0 + keybuf := make([]byte, 0) + for i = 0; x > 127; i++ { + keybuf = append(keybuf, 0x80|uint8(x&0x7F)) + x >>= 7 + } + keybuf = append(keybuf, uint8(x)) + return keybuf +} + +func (desc *FileDescriptorSet) GetField(packageName, messageName, fieldName string) *FieldDescriptorProto { + msg := desc.GetMessage(packageName, messageName) + if msg == nil { + return nil + } + for _, field := range msg.GetField() { + if field.GetName() == fieldName { + return field + } + } + return nil +} + +func (file *FileDescriptorProto) GetMessage(typeName string) *DescriptorProto { + for _, msg := range file.GetMessageType() { + if msg.GetName() == typeName { + return msg + } + nes := file.GetNestedMessage(msg, strings.TrimPrefix(typeName, msg.GetName()+".")) + if nes != nil { + return nes + } + } + return nil +} + +func (file *FileDescriptorProto) GetNestedMessage(msg *DescriptorProto, typeName string) *DescriptorProto { + for _, nes := range msg.GetNestedType() { + if nes.GetName() == typeName { + return nes + } + res := file.GetNestedMessage(nes, strings.TrimPrefix(typeName, nes.GetName()+".")) + if res != nil { + return res + } + } + return nil +} + +func (desc *FileDescriptorSet) GetMessage(packageName string, typeName string) *DescriptorProto { + for _, file := range desc.GetFile() { + if strings.Map(dotToUnderscore, file.GetPackage()) != strings.Map(dotToUnderscore, packageName) { + continue + } + for _, msg := range file.GetMessageType() { + if msg.GetName() == typeName { + return msg + } + } + for _, msg := range file.GetMessageType() { + for _, nes := range msg.GetNestedType() { + if nes.GetName() == typeName { + return nes + } + if msg.GetName()+"."+nes.GetName() == typeName { + return nes + } + } + } + } + return nil +} + +func (desc *FileDescriptorSet) IsProto3(packageName string, typeName string) bool { + for _, file := range desc.GetFile() { + if strings.Map(dotToUnderscore, file.GetPackage()) != strings.Map(dotToUnderscore, packageName) { + continue + } + for _, msg := range file.GetMessageType() { + if msg.GetName() == typeName { + return file.GetSyntax() == "proto3" + } + } + for _, msg := range file.GetMessageType() { + for _, nes := range msg.GetNestedType() { + if nes.GetName() == typeName { + return file.GetSyntax() == "proto3" + } + if msg.GetName()+"."+nes.GetName() == typeName { + return file.GetSyntax() == "proto3" + } + } + } + } + return false +} + +func (msg *DescriptorProto) IsExtendable() bool { + return len(msg.GetExtensionRange()) > 0 +} + +func (desc *FileDescriptorSet) FindExtension(packageName string, typeName string, fieldName string) (extPackageName string, field *FieldDescriptorProto) { + parent := desc.GetMessage(packageName, typeName) + if parent == nil { + return "", nil + } + if !parent.IsExtendable() { + return "", nil + } + extendee := "." + packageName + "." + typeName + for _, file := range desc.GetFile() { + for _, ext := range file.GetExtension() { + if strings.Map(dotToUnderscore, file.GetPackage()) == strings.Map(dotToUnderscore, packageName) { + if !(ext.GetExtendee() == typeName || ext.GetExtendee() == extendee) { + continue + } + } else { + if ext.GetExtendee() != extendee { + continue + } + } + if ext.GetName() == fieldName { + return file.GetPackage(), ext + } + } + } + return "", nil +} + +func (desc *FileDescriptorSet) FindExtensionByFieldNumber(packageName string, typeName string, fieldNum int32) (extPackageName string, field *FieldDescriptorProto) { + parent := desc.GetMessage(packageName, typeName) + if parent == nil { + return "", nil + } + if !parent.IsExtendable() { + return "", nil + } + extendee := "." + packageName + "." + typeName + for _, file := range desc.GetFile() { + for _, ext := range file.GetExtension() { + if strings.Map(dotToUnderscore, file.GetPackage()) == strings.Map(dotToUnderscore, packageName) { + if !(ext.GetExtendee() == typeName || ext.GetExtendee() == extendee) { + continue + } + } else { + if ext.GetExtendee() != extendee { + continue + } + } + if ext.GetNumber() == fieldNum { + return file.GetPackage(), ext + } + } + } + return "", nil +} + +func (desc *FileDescriptorSet) FindMessage(packageName string, typeName string, fieldName string) (msgPackageName string, msgName string) { + parent := desc.GetMessage(packageName, typeName) + if parent == nil { + return "", "" + } + field := parent.GetFieldDescriptor(fieldName) + if field == nil { + var extPackageName string + extPackageName, field = desc.FindExtension(packageName, typeName, fieldName) + if field == nil { + return "", "" + } + packageName = extPackageName + } + typeNames := strings.Split(field.GetTypeName(), ".") + if len(typeNames) == 1 { + msg := desc.GetMessage(packageName, typeName) + if msg == nil { + return "", "" + } + return packageName, msg.GetName() + } + if len(typeNames) > 2 { + for i := 1; i < len(typeNames)-1; i++ { + packageName = strings.Join(typeNames[1:len(typeNames)-i], ".") + typeName = strings.Join(typeNames[len(typeNames)-i:], ".") + msg := desc.GetMessage(packageName, typeName) + if msg != nil { + typeNames := strings.Split(msg.GetName(), ".") + if len(typeNames) == 1 { + return packageName, msg.GetName() + } + return strings.Join(typeNames[1:len(typeNames)-1], "."), typeNames[len(typeNames)-1] + } + } + } + return "", "" +} + +func (msg *DescriptorProto) GetFieldDescriptor(fieldName string) *FieldDescriptorProto { + for _, field := range msg.GetField() { + if field.GetName() == fieldName { + return field + } + } + return nil +} + +func (desc *FileDescriptorSet) GetEnum(packageName string, typeName string) *EnumDescriptorProto { + for _, file := range desc.GetFile() { + if strings.Map(dotToUnderscore, file.GetPackage()) != strings.Map(dotToUnderscore, packageName) { + continue + } + for _, enum := range file.GetEnumType() { + if enum.GetName() == typeName { + return enum + } + } + } + return nil +} + +func (f *FieldDescriptorProto) IsEnum() bool { + return *f.Type == FieldDescriptorProto_TYPE_ENUM +} + +func (f *FieldDescriptorProto) IsMessage() bool { + return *f.Type == FieldDescriptorProto_TYPE_MESSAGE +} + +func (f *FieldDescriptorProto) IsBytes() bool { + return *f.Type == FieldDescriptorProto_TYPE_BYTES +} + +func (f *FieldDescriptorProto) IsRepeated() bool { + return f.Label != nil && *f.Label == FieldDescriptorProto_LABEL_REPEATED +} + +func (f *FieldDescriptorProto) IsString() bool { + return *f.Type == FieldDescriptorProto_TYPE_STRING +} + +func (f *FieldDescriptorProto) IsBool() bool { + return *f.Type == FieldDescriptorProto_TYPE_BOOL +} + +func (f *FieldDescriptorProto) IsRequired() bool { + return f.Label != nil && *f.Label == FieldDescriptorProto_LABEL_REQUIRED +} + +func (f *FieldDescriptorProto) IsPacked() bool { + return f.Options != nil && f.GetOptions().GetPacked() +} + +func (f *FieldDescriptorProto) IsPacked3() bool { + if f.IsRepeated() && f.IsScalar() { + if f.Options == nil || f.GetOptions().Packed == nil { + return true + } + return f.Options != nil && f.GetOptions().GetPacked() + } + return false +} + +func (m *DescriptorProto) HasExtension() bool { + return len(m.ExtensionRange) > 0 +} diff --git a/vendor/github.com/golang/glog/LICENSE b/vendor/github.com/golang/glog/LICENSE new file mode 100644 index 0000000000..37ec93a14f --- /dev/null +++ b/vendor/github.com/golang/glog/LICENSE @@ -0,0 +1,191 @@ +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: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +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 +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/golang/glog/README.md b/vendor/github.com/golang/glog/README.md new file mode 100644 index 0000000000..a4f73883b2 --- /dev/null +++ b/vendor/github.com/golang/glog/README.md @@ -0,0 +1,36 @@ +# glog + +[![PkgGoDev](https://pkg.go.dev/badge/github.com/golang/glog)](https://pkg.go.dev/github.com/golang/glog) + +Leveled execution logs for Go. + +This is an efficient pure Go implementation of leveled logs in the +manner of the open source C++ package [_glog_](https://github.com/google/glog). + +By binding methods to booleans it is possible to use the log package without paying the expense of evaluating the arguments to the log. Through the `-vmodule` flag, the package also provides fine-grained +control over logging at the file level. + +The comment from `glog.go` introduces the ideas: + +Package _glog_ implements logging analogous to the Google-internal C++ INFO/ERROR/V setup. It provides the functions Info, Warning, Error, Fatal, plus formatting variants such as Infof. It also provides V-style loggingcontrolled by the `-v` and `-vmodule=file=2` flags. + +Basic examples: + +```go +glog.Info("Prepare to repel boarders") + +glog.Fatalf("Initialization failed: %s", err) +``` + +See the documentation for the V function for an explanation of these examples: + +```go +if glog.V(2) { + glog.Info("Starting transaction...") +} +glog.V(2).Infoln("Processed", nItems, "elements") +``` + +The repository contains an open source version of the log package used inside Google. The master copy of the source lives inside Google, not here. The code in this repo is for export only and is not itself under development. Feature requests will be ignored. + +Send bug reports to golang-nuts@googlegroups.com. diff --git a/vendor/github.com/golang/glog/glog.go b/vendor/github.com/golang/glog/glog.go new file mode 100644 index 0000000000..718c34f886 --- /dev/null +++ b/vendor/github.com/golang/glog/glog.go @@ -0,0 +1,1180 @@ +// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// +// 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. + +// Package glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup. +// It provides functions Info, Warning, Error, Fatal, plus formatting variants such as +// Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags. +// +// Basic examples: +// +// glog.Info("Prepare to repel boarders") +// +// glog.Fatalf("Initialization failed: %s", err) +// +// See the documentation for the V function for an explanation of these examples: +// +// if glog.V(2) { +// glog.Info("Starting transaction...") +// } +// +// glog.V(2).Infoln("Processed", nItems, "elements") +// +// Log output is buffered and written periodically using Flush. Programs +// should call Flush before exiting to guarantee all log output is written. +// +// By default, all log statements write to files in a temporary directory. +// This package provides several flags that modify this behavior. +// As a result, flag.Parse must be called before any logging is done. +// +// -logtostderr=false +// Logs are written to standard error instead of to files. +// -alsologtostderr=false +// Logs are written to standard error as well as to files. +// -stderrthreshold=ERROR +// Log events at or above this severity are logged to standard +// error as well as to files. +// -log_dir="" +// Log files will be written to this directory instead of the +// default temporary directory. +// +// Other flags provide aids to debugging. +// +// -log_backtrace_at="" +// When set to a file and line number holding a logging statement, +// such as +// -log_backtrace_at=gopherflakes.go:234 +// a stack trace will be written to the Info log whenever execution +// hits that statement. (Unlike with -vmodule, the ".go" must be +// present.) +// -v=0 +// Enable V-leveled logging at the specified level. +// -vmodule="" +// The syntax of the argument is a comma-separated list of pattern=N, +// where pattern is a literal file name (minus the ".go" suffix) or +// "glob" pattern and N is a V level. For instance, +// -vmodule=gopher*=3 +// sets the V level to 3 in all Go files whose names begin "gopher". +// +package glog + +import ( + "bufio" + "bytes" + "errors" + "flag" + "fmt" + "io" + stdLog "log" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" +) + +// severity identifies the sort of log: info, warning etc. It also implements +// the flag.Value interface. The -stderrthreshold flag is of type severity and +// should be modified only through the flag.Value interface. The values match +// the corresponding constants in C++. +type severity int32 // sync/atomic int32 + +// These constants identify the log levels in order of increasing severity. +// A message written to a high-severity log file is also written to each +// lower-severity log file. +const ( + infoLog severity = iota + warningLog + errorLog + fatalLog + numSeverity = 4 +) + +const severityChar = "IWEF" + +var severityName = []string{ + infoLog: "INFO", + warningLog: "WARNING", + errorLog: "ERROR", + fatalLog: "FATAL", +} + +// get returns the value of the severity. +func (s *severity) get() severity { + return severity(atomic.LoadInt32((*int32)(s))) +} + +// set sets the value of the severity. +func (s *severity) set(val severity) { + atomic.StoreInt32((*int32)(s), int32(val)) +} + +// String is part of the flag.Value interface. +func (s *severity) String() string { + return strconv.FormatInt(int64(*s), 10) +} + +// Get is part of the flag.Value interface. +func (s *severity) Get() interface{} { + return *s +} + +// Set is part of the flag.Value interface. +func (s *severity) Set(value string) error { + var threshold severity + // Is it a known name? + if v, ok := severityByName(value); ok { + threshold = v + } else { + v, err := strconv.Atoi(value) + if err != nil { + return err + } + threshold = severity(v) + } + logging.stderrThreshold.set(threshold) + return nil +} + +func severityByName(s string) (severity, bool) { + s = strings.ToUpper(s) + for i, name := range severityName { + if name == s { + return severity(i), true + } + } + return 0, false +} + +// OutputStats tracks the number of output lines and bytes written. +type OutputStats struct { + lines int64 + bytes int64 +} + +// Lines returns the number of lines written. +func (s *OutputStats) Lines() int64 { + return atomic.LoadInt64(&s.lines) +} + +// Bytes returns the number of bytes written. +func (s *OutputStats) Bytes() int64 { + return atomic.LoadInt64(&s.bytes) +} + +// Stats tracks the number of lines of output and number of bytes +// per severity level. Values must be read with atomic.LoadInt64. +var Stats struct { + Info, Warning, Error OutputStats +} + +var severityStats = [numSeverity]*OutputStats{ + infoLog: &Stats.Info, + warningLog: &Stats.Warning, + errorLog: &Stats.Error, +} + +// Level is exported because it appears in the arguments to V and is +// the type of the v flag, which can be set programmatically. +// It's a distinct type because we want to discriminate it from logType. +// Variables of type level are only changed under logging.mu. +// The -v flag is read only with atomic ops, so the state of the logging +// module is consistent. + +// Level is treated as a sync/atomic int32. + +// Level specifies a level of verbosity for V logs. *Level implements +// flag.Value; the -v flag is of type Level and should be modified +// only through the flag.Value interface. +type Level int32 + +// get returns the value of the Level. +func (l *Level) get() Level { + return Level(atomic.LoadInt32((*int32)(l))) +} + +// set sets the value of the Level. +func (l *Level) set(val Level) { + atomic.StoreInt32((*int32)(l), int32(val)) +} + +// String is part of the flag.Value interface. +func (l *Level) String() string { + return strconv.FormatInt(int64(*l), 10) +} + +// Get is part of the flag.Value interface. +func (l *Level) Get() interface{} { + return *l +} + +// Set is part of the flag.Value interface. +func (l *Level) Set(value string) error { + v, err := strconv.Atoi(value) + if err != nil { + return err + } + logging.mu.Lock() + defer logging.mu.Unlock() + logging.setVState(Level(v), logging.vmodule.filter, false) + return nil +} + +// moduleSpec represents the setting of the -vmodule flag. +type moduleSpec struct { + filter []modulePat +} + +// modulePat contains a filter for the -vmodule flag. +// It holds a verbosity level and a file pattern to match. +type modulePat struct { + pattern string + literal bool // The pattern is a literal string + level Level +} + +// match reports whether the file matches the pattern. It uses a string +// comparison if the pattern contains no metacharacters. +func (m *modulePat) match(file string) bool { + if m.literal { + return file == m.pattern + } + match, _ := filepath.Match(m.pattern, file) + return match +} + +func (m *moduleSpec) String() string { + // Lock because the type is not atomic. TODO: clean this up. + logging.mu.Lock() + defer logging.mu.Unlock() + var b bytes.Buffer + for i, f := range m.filter { + if i > 0 { + b.WriteRune(',') + } + fmt.Fprintf(&b, "%s=%d", f.pattern, f.level) + } + return b.String() +} + +// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the +// struct is not exported. +func (m *moduleSpec) Get() interface{} { + return nil +} + +var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N") + +// Syntax: -vmodule=recordio=2,file=1,gfs*=3 +func (m *moduleSpec) Set(value string) error { + var filter []modulePat + for _, pat := range strings.Split(value, ",") { + if len(pat) == 0 { + // Empty strings such as from a trailing comma can be ignored. + continue + } + patLev := strings.Split(pat, "=") + if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 { + return errVmoduleSyntax + } + pattern := patLev[0] + v, err := strconv.Atoi(patLev[1]) + if err != nil { + return errors.New("syntax error: expect comma-separated list of filename=N") + } + if v < 0 { + return errors.New("negative value for vmodule level") + } + if v == 0 { + continue // Ignore. It's harmless but no point in paying the overhead. + } + // TODO: check syntax of filter? + filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)}) + } + logging.mu.Lock() + defer logging.mu.Unlock() + logging.setVState(logging.verbosity, filter, true) + return nil +} + +// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters +// that require filepath.Match to be called to match the pattern. +func isLiteral(pattern string) bool { + return !strings.ContainsAny(pattern, `\*?[]`) +} + +// traceLocation represents the setting of the -log_backtrace_at flag. +type traceLocation struct { + file string + line int +} + +// isSet reports whether the trace location has been specified. +// logging.mu is held. +func (t *traceLocation) isSet() bool { + return t.line > 0 +} + +// match reports whether the specified file and line matches the trace location. +// The argument file name is the full path, not the basename specified in the flag. +// logging.mu is held. +func (t *traceLocation) match(file string, line int) bool { + if t.line != line { + return false + } + if i := strings.LastIndex(file, "/"); i >= 0 { + file = file[i+1:] + } + return t.file == file +} + +func (t *traceLocation) String() string { + // Lock because the type is not atomic. TODO: clean this up. + logging.mu.Lock() + defer logging.mu.Unlock() + return fmt.Sprintf("%s:%d", t.file, t.line) +} + +// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the +// struct is not exported +func (t *traceLocation) Get() interface{} { + return nil +} + +var errTraceSyntax = errors.New("syntax error: expect file.go:234") + +// Syntax: -log_backtrace_at=gopherflakes.go:234 +// Note that unlike vmodule the file extension is included here. +func (t *traceLocation) Set(value string) error { + if value == "" { + // Unset. + t.line = 0 + t.file = "" + } + fields := strings.Split(value, ":") + if len(fields) != 2 { + return errTraceSyntax + } + file, line := fields[0], fields[1] + if !strings.Contains(file, ".") { + return errTraceSyntax + } + v, err := strconv.Atoi(line) + if err != nil { + return errTraceSyntax + } + if v <= 0 { + return errors.New("negative or zero value for level") + } + logging.mu.Lock() + defer logging.mu.Unlock() + t.line = v + t.file = file + return nil +} + +// flushSyncWriter is the interface satisfied by logging destinations. +type flushSyncWriter interface { + Flush() error + Sync() error + io.Writer +} + +func init() { + flag.BoolVar(&logging.toStderr, "logtostderr", false, "log to standard error instead of files") + flag.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files") + flag.Var(&logging.verbosity, "v", "log level for V logs") + flag.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr") + flag.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging") + flag.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace") + + // Default stderrThreshold is ERROR. + logging.stderrThreshold = errorLog + + logging.setVState(0, nil, false) + go logging.flushDaemon() +} + +// Flush flushes all pending log I/O. +func Flush() { + logging.lockAndFlushAll() +} + +// loggingT collects all the global state of the logging setup. +type loggingT struct { + // Boolean flags. Not handled atomically because the flag.Value interface + // does not let us avoid the =true, and that shorthand is necessary for + // compatibility. TODO: does this matter enough to fix? Seems unlikely. + toStderr bool // The -logtostderr flag. + alsoToStderr bool // The -alsologtostderr flag. + + // Level flag. Handled atomically. + stderrThreshold severity // The -stderrthreshold flag. + + // freeList is a list of byte buffers, maintained under freeListMu. + freeList *buffer + // freeListMu maintains the free list. It is separate from the main mutex + // so buffers can be grabbed and printed to without holding the main lock, + // for better parallelization. + freeListMu sync.Mutex + + // mu protects the remaining elements of this structure and is + // used to synchronize logging. + mu sync.Mutex + // file holds writer for each of the log types. + file [numSeverity]flushSyncWriter + // pcs is used in V to avoid an allocation when computing the caller's PC. + pcs [1]uintptr + // vmap is a cache of the V Level for each V() call site, identified by PC. + // It is wiped whenever the vmodule flag changes state. + vmap map[uintptr]Level + // filterLength stores the length of the vmodule filter chain. If greater + // than zero, it means vmodule is enabled. It may be read safely + // using sync.LoadInt32, but is only modified under mu. + filterLength int32 + // traceLocation is the state of the -log_backtrace_at flag. + traceLocation traceLocation + // These flags are modified only under lock, although verbosity may be fetched + // safely using atomic.LoadInt32. + vmodule moduleSpec // The state of the -vmodule flag. + verbosity Level // V logging level, the value of the -v flag/ +} + +// buffer holds a byte Buffer for reuse. The zero value is ready for use. +type buffer struct { + bytes.Buffer + tmp [64]byte // temporary byte array for creating headers. + next *buffer +} + +var logging loggingT + +// setVState sets a consistent state for V logging. +// l.mu is held. +func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool) { + // Turn verbosity off so V will not fire while we are in transition. + logging.verbosity.set(0) + // Ditto for filter length. + atomic.StoreInt32(&logging.filterLength, 0) + + // Set the new filters and wipe the pc->Level map if the filter has changed. + if setFilter { + logging.vmodule.filter = filter + logging.vmap = make(map[uintptr]Level) + } + + // Things are consistent now, so enable filtering and verbosity. + // They are enabled in order opposite to that in V. + atomic.StoreInt32(&logging.filterLength, int32(len(filter))) + logging.verbosity.set(verbosity) +} + +// getBuffer returns a new, ready-to-use buffer. +func (l *loggingT) getBuffer() *buffer { + l.freeListMu.Lock() + b := l.freeList + if b != nil { + l.freeList = b.next + } + l.freeListMu.Unlock() + if b == nil { + b = new(buffer) + } else { + b.next = nil + b.Reset() + } + return b +} + +// putBuffer returns a buffer to the free list. +func (l *loggingT) putBuffer(b *buffer) { + if b.Len() >= 256 { + // Let big buffers die a natural death. + return + } + l.freeListMu.Lock() + b.next = l.freeList + l.freeList = b + l.freeListMu.Unlock() +} + +var timeNow = time.Now // Stubbed out for testing. + +/* +header formats a log header as defined by the C++ implementation. +It returns a buffer containing the formatted header and the user's file and line number. +The depth specifies how many stack frames above lives the source line to be identified in the log message. + +Log lines have this form: + Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... +where the fields are defined as follows: + L A single character, representing the log level (eg 'I' for INFO) + mm The month (zero padded; ie May is '05') + dd The day (zero padded) + hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds + threadid The space-padded thread ID as returned by GetTID() + file The file name + line The line number + msg The user-supplied message +*/ +func (l *loggingT) header(s severity, depth int) (*buffer, string, int) { + _, file, line, ok := runtime.Caller(3 + depth) + if !ok { + file = "???" + line = 1 + } else { + slash := strings.LastIndex(file, "/") + if slash >= 0 { + file = file[slash+1:] + } + } + return l.formatHeader(s, file, line), file, line +} + +// formatHeader formats a log header using the provided file name and line number. +func (l *loggingT) formatHeader(s severity, file string, line int) *buffer { + now := timeNow() + if line < 0 { + line = 0 // not a real line number, but acceptable to someDigits + } + if s > fatalLog { + s = infoLog // for safety. + } + buf := l.getBuffer() + + // Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand. + // It's worth about 3X. Fprintf is hard. + _, month, day := now.Date() + hour, minute, second := now.Clock() + // Lmmdd hh:mm:ss.uuuuuu threadid file:line] + buf.tmp[0] = severityChar[s] + buf.twoDigits(1, int(month)) + buf.twoDigits(3, day) + buf.tmp[5] = ' ' + buf.twoDigits(6, hour) + buf.tmp[8] = ':' + buf.twoDigits(9, minute) + buf.tmp[11] = ':' + buf.twoDigits(12, second) + buf.tmp[14] = '.' + buf.nDigits(6, 15, now.Nanosecond()/1000, '0') + buf.tmp[21] = ' ' + buf.nDigits(7, 22, pid, ' ') // TODO: should be TID + buf.tmp[29] = ' ' + buf.Write(buf.tmp[:30]) + buf.WriteString(file) + buf.tmp[0] = ':' + n := buf.someDigits(1, line) + buf.tmp[n+1] = ']' + buf.tmp[n+2] = ' ' + buf.Write(buf.tmp[:n+3]) + return buf +} + +// Some custom tiny helper functions to print the log header efficiently. + +const digits = "0123456789" + +// twoDigits formats a zero-prefixed two-digit integer at buf.tmp[i]. +func (buf *buffer) twoDigits(i, d int) { + buf.tmp[i+1] = digits[d%10] + d /= 10 + buf.tmp[i] = digits[d%10] +} + +// nDigits formats an n-digit integer at buf.tmp[i], +// padding with pad on the left. +// It assumes d >= 0. +func (buf *buffer) nDigits(n, i, d int, pad byte) { + j := n - 1 + for ; j >= 0 && d > 0; j-- { + buf.tmp[i+j] = digits[d%10] + d /= 10 + } + for ; j >= 0; j-- { + buf.tmp[i+j] = pad + } +} + +// someDigits formats a zero-prefixed variable-width integer at buf.tmp[i]. +func (buf *buffer) someDigits(i, d int) int { + // Print into the top, then copy down. We know there's space for at least + // a 10-digit number. + j := len(buf.tmp) + for { + j-- + buf.tmp[j] = digits[d%10] + d /= 10 + if d == 0 { + break + } + } + return copy(buf.tmp[i:], buf.tmp[j:]) +} + +func (l *loggingT) println(s severity, args ...interface{}) { + buf, file, line := l.header(s, 0) + fmt.Fprintln(buf, args...) + l.output(s, buf, file, line, false) +} + +func (l *loggingT) print(s severity, args ...interface{}) { + l.printDepth(s, 1, args...) +} + +func (l *loggingT) printDepth(s severity, depth int, args ...interface{}) { + buf, file, line := l.header(s, depth) + fmt.Fprint(buf, args...) + if buf.Bytes()[buf.Len()-1] != '\n' { + buf.WriteByte('\n') + } + l.output(s, buf, file, line, false) +} + +func (l *loggingT) printf(s severity, format string, args ...interface{}) { + buf, file, line := l.header(s, 0) + fmt.Fprintf(buf, format, args...) + if buf.Bytes()[buf.Len()-1] != '\n' { + buf.WriteByte('\n') + } + l.output(s, buf, file, line, false) +} + +// printWithFileLine behaves like print but uses the provided file and line number. If +// alsoLogToStderr is true, the log message always appears on standard error; it +// will also appear in the log file unless --logtostderr is set. +func (l *loggingT) printWithFileLine(s severity, file string, line int, alsoToStderr bool, args ...interface{}) { + buf := l.formatHeader(s, file, line) + fmt.Fprint(buf, args...) + if buf.Bytes()[buf.Len()-1] != '\n' { + buf.WriteByte('\n') + } + l.output(s, buf, file, line, alsoToStderr) +} + +// output writes the data to the log files and releases the buffer. +func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoToStderr bool) { + l.mu.Lock() + if l.traceLocation.isSet() { + if l.traceLocation.match(file, line) { + buf.Write(stacks(false)) + } + } + data := buf.Bytes() + if !flag.Parsed() { + os.Stderr.Write([]byte("ERROR: logging before flag.Parse: ")) + os.Stderr.Write(data) + } else if l.toStderr { + os.Stderr.Write(data) + } else { + if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() { + os.Stderr.Write(data) + } + if l.file[s] == nil { + if err := l.createFiles(s); err != nil { + os.Stderr.Write(data) // Make sure the message appears somewhere. + l.exit(err) + } + } + switch s { + case fatalLog: + l.file[fatalLog].Write(data) + fallthrough + case errorLog: + l.file[errorLog].Write(data) + fallthrough + case warningLog: + l.file[warningLog].Write(data) + fallthrough + case infoLog: + l.file[infoLog].Write(data) + } + } + if s == fatalLog { + // If we got here via Exit rather than Fatal, print no stacks. + if atomic.LoadUint32(&fatalNoStacks) > 0 { + l.mu.Unlock() + timeoutFlush(10 * time.Second) + os.Exit(1) + } + // Dump all goroutine stacks before exiting. + // First, make sure we see the trace for the current goroutine on standard error. + // If -logtostderr has been specified, the loop below will do that anyway + // as the first stack in the full dump. + if !l.toStderr { + os.Stderr.Write(stacks(false)) + } + // Write the stack trace for all goroutines to the files. + trace := stacks(true) + logExitFunc = func(error) {} // If we get a write error, we'll still exit below. + for log := fatalLog; log >= infoLog; log-- { + if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set. + f.Write(trace) + } + } + l.mu.Unlock() + timeoutFlush(10 * time.Second) + os.Exit(255) // C++ uses -1, which is silly because it's anded with 255 anyway. + } + l.putBuffer(buf) + l.mu.Unlock() + if stats := severityStats[s]; stats != nil { + atomic.AddInt64(&stats.lines, 1) + atomic.AddInt64(&stats.bytes, int64(len(data))) + } +} + +// timeoutFlush calls Flush and returns when it completes or after timeout +// elapses, whichever happens first. This is needed because the hooks invoked +// by Flush may deadlock when glog.Fatal is called from a hook that holds +// a lock. +func timeoutFlush(timeout time.Duration) { + done := make(chan bool, 1) + go func() { + Flush() // calls logging.lockAndFlushAll() + done <- true + }() + select { + case <-done: + case <-time.After(timeout): + fmt.Fprintln(os.Stderr, "glog: Flush took longer than", timeout) + } +} + +// stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines. +func stacks(all bool) []byte { + // We don't know how big the traces are, so grow a few times if they don't fit. Start large, though. + n := 10000 + if all { + n = 100000 + } + var trace []byte + for i := 0; i < 5; i++ { + trace = make([]byte, n) + nbytes := runtime.Stack(trace, all) + if nbytes < len(trace) { + return trace[:nbytes] + } + n *= 2 + } + return trace +} + +// logExitFunc provides a simple mechanism to override the default behavior +// of exiting on error. Used in testing and to guarantee we reach a required exit +// for fatal logs. Instead, exit could be a function rather than a method but that +// would make its use clumsier. +var logExitFunc func(error) + +// exit is called if there is trouble creating or writing log files. +// It flushes the logs and exits the program; there's no point in hanging around. +// l.mu is held. +func (l *loggingT) exit(err error) { + fmt.Fprintf(os.Stderr, "log: exiting because of error: %s\n", err) + // If logExitFunc is set, we do that instead of exiting. + if logExitFunc != nil { + logExitFunc(err) + return + } + l.flushAll() + os.Exit(2) +} + +// syncBuffer joins a bufio.Writer to its underlying file, providing access to the +// file's Sync method and providing a wrapper for the Write method that provides log +// file rotation. There are conflicting methods, so the file cannot be embedded. +// l.mu is held for all its methods. +type syncBuffer struct { + logger *loggingT + *bufio.Writer + file *os.File + sev severity + nbytes uint64 // The number of bytes written to this file +} + +func (sb *syncBuffer) Sync() error { + return sb.file.Sync() +} + +func (sb *syncBuffer) Write(p []byte) (n int, err error) { + if sb.nbytes+uint64(len(p)) >= MaxSize { + if err := sb.rotateFile(time.Now()); err != nil { + sb.logger.exit(err) + } + } + n, err = sb.Writer.Write(p) + sb.nbytes += uint64(n) + if err != nil { + sb.logger.exit(err) + } + return +} + +// rotateFile closes the syncBuffer's file and starts a new one. +func (sb *syncBuffer) rotateFile(now time.Time) error { + if sb.file != nil { + sb.Flush() + sb.file.Close() + } + var err error + sb.file, _, err = create(severityName[sb.sev], now) + sb.nbytes = 0 + if err != nil { + return err + } + + sb.Writer = bufio.NewWriterSize(sb.file, bufferSize) + + // Write header. + var buf bytes.Buffer + fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05")) + fmt.Fprintf(&buf, "Running on machine: %s\n", host) + fmt.Fprintf(&buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH) + fmt.Fprintf(&buf, "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n") + n, err := sb.file.Write(buf.Bytes()) + sb.nbytes += uint64(n) + return err +} + +// bufferSize sizes the buffer associated with each log file. It's large +// so that log records can accumulate without the logging thread blocking +// on disk I/O. The flushDaemon will block instead. +const bufferSize = 256 * 1024 + +// createFiles creates all the log files for severity from sev down to infoLog. +// l.mu is held. +func (l *loggingT) createFiles(sev severity) error { + now := time.Now() + // Files are created in decreasing severity order, so as soon as we find one + // has already been created, we can stop. + for s := sev; s >= infoLog && l.file[s] == nil; s-- { + sb := &syncBuffer{ + logger: l, + sev: s, + } + if err := sb.rotateFile(now); err != nil { + return err + } + l.file[s] = sb + } + return nil +} + +const flushInterval = 30 * time.Second + +// flushDaemon periodically flushes the log file buffers. +func (l *loggingT) flushDaemon() { + for range time.NewTicker(flushInterval).C { + l.lockAndFlushAll() + } +} + +// lockAndFlushAll is like flushAll but locks l.mu first. +func (l *loggingT) lockAndFlushAll() { + l.mu.Lock() + l.flushAll() + l.mu.Unlock() +} + +// flushAll flushes all the logs and attempts to "sync" their data to disk. +// l.mu is held. +func (l *loggingT) flushAll() { + // Flush from fatal down, in case there's trouble flushing. + for s := fatalLog; s >= infoLog; s-- { + file := l.file[s] + if file != nil { + file.Flush() // ignore error + file.Sync() // ignore error + } + } +} + +// CopyStandardLogTo arranges for messages written to the Go "log" package's +// default logs to also appear in the Google logs for the named and lower +// severities. Subsequent changes to the standard log's default output location +// or format may break this behavior. +// +// Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not +// recognized, CopyStandardLogTo panics. +func CopyStandardLogTo(name string) { + sev, ok := severityByName(name) + if !ok { + panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name)) + } + // Set a log format that captures the user's file and line: + // d.go:23: message + stdLog.SetFlags(stdLog.Lshortfile) + stdLog.SetOutput(logBridge(sev)) +} + +// logBridge provides the Write method that enables CopyStandardLogTo to connect +// Go's standard logs to the logs provided by this package. +type logBridge severity + +// Write parses the standard logging line and passes its components to the +// logger for severity(lb). +func (lb logBridge) Write(b []byte) (n int, err error) { + var ( + file = "???" + line = 1 + text string + ) + // Split "d.go:23: message" into "d.go", "23", and "message". + if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 { + text = fmt.Sprintf("bad log format: %s", b) + } else { + file = string(parts[0]) + text = string(parts[2][1:]) // skip leading space + line, err = strconv.Atoi(string(parts[1])) + if err != nil { + text = fmt.Sprintf("bad line number: %s", b) + line = 1 + } + } + // printWithFileLine with alsoToStderr=true, so standard log messages + // always appear on standard error. + logging.printWithFileLine(severity(lb), file, line, true, text) + return len(b), nil +} + +// setV computes and remembers the V level for a given PC +// when vmodule is enabled. +// File pattern matching takes the basename of the file, stripped +// of its .go suffix, and uses filepath.Match, which is a little more +// general than the *? matching used in C++. +// l.mu is held. +func (l *loggingT) setV(pc uintptr) Level { + fn := runtime.FuncForPC(pc) + file, _ := fn.FileLine(pc) + // The file is something like /a/b/c/d.go. We want just the d. + if strings.HasSuffix(file, ".go") { + file = file[:len(file)-3] + } + if slash := strings.LastIndex(file, "/"); slash >= 0 { + file = file[slash+1:] + } + for _, filter := range l.vmodule.filter { + if filter.match(file) { + l.vmap[pc] = filter.level + return filter.level + } + } + l.vmap[pc] = 0 + return 0 +} + +// Verbose is a boolean type that implements Infof (like Printf) etc. +// See the documentation of V for more information. +type Verbose bool + +// V reports whether verbosity at the call site is at least the requested level. +// The returned value is a boolean of type Verbose, which implements Info, Infoln +// and Infof. These methods will write to the Info log if called. +// Thus, one may write either +// if glog.V(2) { glog.Info("log this") } +// or +// glog.V(2).Info("log this") +// The second form is shorter but the first is cheaper if logging is off because it does +// not evaluate its arguments. +// +// Whether an individual call to V generates a log record depends on the setting of +// the -v and --vmodule flags; both are off by default. If the level in the call to +// V is at most the value of -v, or of -vmodule for the source file containing the +// call, the V call will log. +func V(level Level) Verbose { + // This function tries hard to be cheap unless there's work to do. + // The fast path is two atomic loads and compares. + + // Here is a cheap but safe test to see if V logging is enabled globally. + if logging.verbosity.get() >= level { + return Verbose(true) + } + + // It's off globally but it vmodule may still be set. + // Here is another cheap but safe test to see if vmodule is enabled. + if atomic.LoadInt32(&logging.filterLength) > 0 { + // Now we need a proper lock to use the logging structure. The pcs field + // is shared so we must lock before accessing it. This is fairly expensive, + // but if V logging is enabled we're slow anyway. + logging.mu.Lock() + defer logging.mu.Unlock() + if runtime.Callers(2, logging.pcs[:]) == 0 { + return Verbose(false) + } + v, ok := logging.vmap[logging.pcs[0]] + if !ok { + v = logging.setV(logging.pcs[0]) + } + return Verbose(v >= level) + } + return Verbose(false) +} + +// Info is equivalent to the global Info function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Info(args ...interface{}) { + if v { + logging.print(infoLog, args...) + } +} + +// Infoln is equivalent to the global Infoln function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Infoln(args ...interface{}) { + if v { + logging.println(infoLog, args...) + } +} + +// Infof is equivalent to the global Infof function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Infof(format string, args ...interface{}) { + if v { + logging.printf(infoLog, format, args...) + } +} + +// Info logs to the INFO log. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Info(args ...interface{}) { + logging.print(infoLog, args...) +} + +// InfoDepth acts as Info but uses depth to determine which call frame to log. +// InfoDepth(0, "msg") is the same as Info("msg"). +func InfoDepth(depth int, args ...interface{}) { + logging.printDepth(infoLog, depth, args...) +} + +// Infoln logs to the INFO log. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Infoln(args ...interface{}) { + logging.println(infoLog, args...) +} + +// Infof logs to the INFO log. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Infof(format string, args ...interface{}) { + logging.printf(infoLog, format, args...) +} + +// Warning logs to the WARNING and INFO logs. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Warning(args ...interface{}) { + logging.print(warningLog, args...) +} + +// WarningDepth acts as Warning but uses depth to determine which call frame to log. +// WarningDepth(0, "msg") is the same as Warning("msg"). +func WarningDepth(depth int, args ...interface{}) { + logging.printDepth(warningLog, depth, args...) +} + +// Warningln logs to the WARNING and INFO logs. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Warningln(args ...interface{}) { + logging.println(warningLog, args...) +} + +// Warningf logs to the WARNING and INFO logs. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Warningf(format string, args ...interface{}) { + logging.printf(warningLog, format, args...) +} + +// Error logs to the ERROR, WARNING, and INFO logs. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Error(args ...interface{}) { + logging.print(errorLog, args...) +} + +// ErrorDepth acts as Error but uses depth to determine which call frame to log. +// ErrorDepth(0, "msg") is the same as Error("msg"). +func ErrorDepth(depth int, args ...interface{}) { + logging.printDepth(errorLog, depth, args...) +} + +// Errorln logs to the ERROR, WARNING, and INFO logs. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Errorln(args ...interface{}) { + logging.println(errorLog, args...) +} + +// Errorf logs to the ERROR, WARNING, and INFO logs. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Errorf(format string, args ...interface{}) { + logging.printf(errorLog, format, args...) +} + +// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs, +// including a stack trace of all running goroutines, then calls os.Exit(255). +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Fatal(args ...interface{}) { + logging.print(fatalLog, args...) +} + +// FatalDepth acts as Fatal but uses depth to determine which call frame to log. +// FatalDepth(0, "msg") is the same as Fatal("msg"). +func FatalDepth(depth int, args ...interface{}) { + logging.printDepth(fatalLog, depth, args...) +} + +// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs, +// including a stack trace of all running goroutines, then calls os.Exit(255). +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Fatalln(args ...interface{}) { + logging.println(fatalLog, args...) +} + +// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs, +// including a stack trace of all running goroutines, then calls os.Exit(255). +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Fatalf(format string, args ...interface{}) { + logging.printf(fatalLog, format, args...) +} + +// fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks. +// It allows Exit and relatives to use the Fatal logs. +var fatalNoStacks uint32 + +// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Exit(args ...interface{}) { + atomic.StoreUint32(&fatalNoStacks, 1) + logging.print(fatalLog, args...) +} + +// ExitDepth acts as Exit but uses depth to determine which call frame to log. +// ExitDepth(0, "msg") is the same as Exit("msg"). +func ExitDepth(depth int, args ...interface{}) { + atomic.StoreUint32(&fatalNoStacks, 1) + logging.printDepth(fatalLog, depth, args...) +} + +// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). +func Exitln(args ...interface{}) { + atomic.StoreUint32(&fatalNoStacks, 1) + logging.println(fatalLog, args...) +} + +// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Exitf(format string, args ...interface{}) { + atomic.StoreUint32(&fatalNoStacks, 1) + logging.printf(fatalLog, format, args...) +} diff --git a/vendor/github.com/golang/glog/glog_file.go b/vendor/github.com/golang/glog/glog_file.go new file mode 100644 index 0000000000..65075d2811 --- /dev/null +++ b/vendor/github.com/golang/glog/glog_file.go @@ -0,0 +1,124 @@ +// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// +// 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. + +// File I/O for logs. + +package glog + +import ( + "errors" + "flag" + "fmt" + "os" + "os/user" + "path/filepath" + "strings" + "sync" + "time" +) + +// MaxSize is the maximum size of a log file in bytes. +var MaxSize uint64 = 1024 * 1024 * 1800 + +// logDirs lists the candidate directories for new log files. +var logDirs []string + +// If non-empty, overrides the choice of directory in which to write logs. +// See createLogDirs for the full list of possible destinations. +var logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory") + +func createLogDirs() { + if *logDir != "" { + logDirs = append(logDirs, *logDir) + } + logDirs = append(logDirs, os.TempDir()) +} + +var ( + pid = os.Getpid() + program = filepath.Base(os.Args[0]) + host = "unknownhost" + userName = "unknownuser" +) + +func init() { + h, err := os.Hostname() + if err == nil { + host = shortHostname(h) + } + + current, err := user.Current() + if err == nil { + userName = current.Username + } + + // Sanitize userName since it may contain filepath separators on Windows. + userName = strings.Replace(userName, `\`, "_", -1) +} + +// shortHostname returns its argument, truncating at the first period. +// For instance, given "www.google.com" it returns "www". +func shortHostname(hostname string) string { + if i := strings.Index(hostname, "."); i >= 0 { + return hostname[:i] + } + return hostname +} + +// logName returns a new log file name containing tag, with start time t, and +// the name for the symlink for tag. +func logName(tag string, t time.Time) (name, link string) { + name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d", + program, + host, + userName, + tag, + t.Year(), + t.Month(), + t.Day(), + t.Hour(), + t.Minute(), + t.Second(), + pid) + return name, program + "." + tag +} + +var onceLogDirs sync.Once + +// create creates a new log file and returns the file and its filename, which +// contains tag ("INFO", "FATAL", etc.) and t. If the file is created +// successfully, create also attempts to update the symlink for that tag, ignoring +// errors. +func create(tag string, t time.Time) (f *os.File, filename string, err error) { + onceLogDirs.Do(createLogDirs) + if len(logDirs) == 0 { + return nil, "", errors.New("log: no log dirs") + } + name, link := logName(tag, t) + var lastErr error + for _, dir := range logDirs { + fname := filepath.Join(dir, name) + f, err := os.Create(fname) + if err == nil { + symlink := filepath.Join(dir, link) + os.Remove(symlink) // ignore err + os.Symlink(name, symlink) // ignore err + return f, fname, nil + } + lastErr = err + } + return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr) +} diff --git a/vendor/github.com/golang/glog/go.mod b/vendor/github.com/golang/glog/go.mod new file mode 100644 index 0000000000..376dc05b9e --- /dev/null +++ b/vendor/github.com/golang/glog/go.mod @@ -0,0 +1,3 @@ +module github.com/golang/glog + +go 1.11 diff --git a/vendor/github.com/google/cel-go/LICENSE b/vendor/github.com/google/cel-go/LICENSE new file mode 100644 index 0000000000..2493ed2eb4 --- /dev/null +++ b/vendor/github.com/google/cel-go/LICENSE @@ -0,0 +1,233 @@ + + 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. + +=========================================================================== +The common/types/pb/equal.go modification of proto.Equal logic +=========================================================================== +Copyright (c) 2018 The Go Authors. 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. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +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 +OWNER 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/vendor/github.com/google/cel-go/cel/cel.go b/vendor/github.com/google/cel-go/cel/cel.go new file mode 100644 index 0000000000..eb5a9f4cc5 --- /dev/null +++ b/vendor/github.com/google/cel-go/cel/cel.go @@ -0,0 +1,19 @@ +// Copyright 2019 Google LLC +// +// 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. + +// Package cel defines the top-level interface for the Common Expression Language (CEL). +// +// CEL is a non-Turing complete expression language designed to parse, check, and evaluate +// expressions against user-defined environments. +package cel diff --git a/vendor/github.com/google/cel-go/cel/env.go b/vendor/github.com/google/cel-go/cel/env.go new file mode 100644 index 0000000000..db9159c9c5 --- /dev/null +++ b/vendor/github.com/google/cel-go/cel/env.go @@ -0,0 +1,550 @@ +// Copyright 2019 Google LLC +// +// 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. + +package cel + +import ( + "errors" + "fmt" + "sync" + + "github.com/google/cel-go/checker" + "github.com/google/cel-go/checker/decls" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/interpreter" + "github.com/google/cel-go/parser" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +// Source interface representing a user-provided expression. +type Source = common.Source + +// Ast representing the checked or unchecked expression, its source, and related metadata such as +// source position information. +type Ast struct { + expr *exprpb.Expr + info *exprpb.SourceInfo + source Source + refMap map[int64]*exprpb.Reference + typeMap map[int64]*exprpb.Type +} + +// Expr returns the proto serializable instance of the parsed/checked expression. +func (ast *Ast) Expr() *exprpb.Expr { + return ast.expr +} + +// IsChecked returns whether the Ast value has been successfully type-checked. +func (ast *Ast) IsChecked() bool { + return ast.typeMap != nil && len(ast.typeMap) > 0 +} + +// SourceInfo returns character offset and newline position information about expression elements. +func (ast *Ast) SourceInfo() *exprpb.SourceInfo { + return ast.info +} + +// ResultType returns the output type of the expression if the Ast has been type-checked, else +// returns decls.Dyn as the parse step cannot infer the type. +func (ast *Ast) ResultType() *exprpb.Type { + if !ast.IsChecked() { + return decls.Dyn + } + return ast.typeMap[ast.expr.Id] +} + +// Source returns a view of the input used to create the Ast. This source may be complete or +// constructed from the SourceInfo. +func (ast *Ast) Source() Source { + return ast.source +} + +// FormatType converts a type message into a string representation. +func FormatType(t *exprpb.Type) string { + return checker.FormatCheckedType(t) +} + +// Env encapsulates the context necessary to perform parsing, type checking, or generation of +// evaluable programs for different expressions. +type Env struct { + Container *containers.Container + declarations []*exprpb.Decl + macros []parser.Macro + adapter ref.TypeAdapter + provider ref.TypeProvider + features map[int]bool + + // Internal parser representation + prsr *parser.Parser + + // Internal checker representation + chk *checker.Env + chkErr error + chkOnce sync.Once + chkOpts []checker.Option + + // Program options tied to the environment + progOpts []ProgramOption +} + +// NewEnv creates a program environment configured with the standard library of CEL functions and +// macros. The Env value returned can parse and check any CEL program which builds upon the core +// features documented in the CEL specification. +// +// See the EnvOption helper functions for the options that can be used to configure the +// environment. +func NewEnv(opts ...EnvOption) (*Env, error) { + // Extend the statically configured standard environment, disabling eager validation to ensure + // the cost of setup for the environment is still just as cheap as it is in v0.11.x and earlier + // releases. The user provided options can easily re-enable the eager validation as they are + // processed after this default option. + stdOpts := append([]EnvOption{EagerlyValidateDeclarations(false)}, opts...) + env, err := getStdEnv() + if err != nil { + return nil, err + } + return env.Extend(stdOpts...) +} + +// NewCustomEnv creates a custom program environment which is not automatically configured with the +// standard library of functions and macros documented in the CEL spec. +// +// The purpose for using a custom environment might be for subsetting the standard library produced +// by the cel.StdLib() function. Subsetting CEL is a core aspect of its design that allows users to +// limit the compute and memory impact of a CEL program by controlling the functions and macros +// that may appear in a given expression. +// +// See the EnvOption helper functions for the options that can be used to configure the +// environment. +func NewCustomEnv(opts ...EnvOption) (*Env, error) { + registry, err := types.NewRegistry() + if err != nil { + return nil, err + } + return (&Env{ + declarations: []*exprpb.Decl{}, + macros: []parser.Macro{}, + Container: containers.DefaultContainer, + adapter: registry, + provider: registry, + features: map[int]bool{}, + progOpts: []ProgramOption{}, + }).configure(opts) +} + +// Check performs type-checking on the input Ast and yields a checked Ast and/or set of Issues. +// +// Checking has failed if the returned Issues value and its Issues.Err() value are non-nil. +// Issues should be inspected if they are non-nil, but may not represent a fatal error. +// +// It is possible to have both non-nil Ast and Issues values returned from this call: however, +// the mere presence of an Ast does not imply that it is valid for use. +func (e *Env) Check(ast *Ast) (*Ast, *Issues) { + // Note, errors aren't currently possible on the Ast to ParsedExpr conversion. + pe, _ := AstToParsedExpr(ast) + + // Construct the internal checker env, erroring if there is an issue adding the declarations. + err := e.initChecker() + if err != nil { + errs := common.NewErrors(ast.Source()) + errs.ReportError(common.NoLocation, e.chkErr.Error()) + return nil, NewIssues(errs) + } + + res, errs := checker.Check(pe, ast.Source(), e.chk) + if len(errs.GetErrors()) > 0 { + return nil, NewIssues(errs) + } + // Manually create the Ast to ensure that the Ast source information (which may be more + // detailed than the information provided by Check), is returned to the caller. + return &Ast{ + source: ast.Source(), + expr: res.GetExpr(), + info: res.GetSourceInfo(), + refMap: res.GetReferenceMap(), + typeMap: res.GetTypeMap()}, nil +} + +// Compile combines the Parse and Check phases CEL program compilation to produce an Ast and +// associated issues. +// +// If an error is encountered during parsing the Compile step will not continue with the Check +// phase. If non-error issues are encountered during Parse, they may be combined with any issues +// discovered during Check. +// +// Note, for parse-only uses of CEL use Parse. +func (e *Env) Compile(txt string) (*Ast, *Issues) { + return e.CompileSource(common.NewTextSource(txt)) +} + +// CompileSource combines the Parse and Check phases CEL program compilation to produce an Ast and +// associated issues. +// +// If an error is encountered during parsing the CompileSource step will not continue with the +// Check phase. If non-error issues are encountered during Parse, they may be combined with any +// issues discovered during Check. +// +// Note, for parse-only uses of CEL use Parse. +func (e *Env) CompileSource(src Source) (*Ast, *Issues) { + ast, iss := e.ParseSource(src) + if iss.Err() != nil { + return nil, iss + } + checked, iss2 := e.Check(ast) + if iss2.Err() != nil { + return nil, iss2 + } + return checked, iss2 +} + +// Extend the current environment with additional options to produce a new Env. +// +// Note, the extended Env value should not share memory with the original. It is possible, however, +// that a CustomTypeAdapter or CustomTypeProvider options could provide values which are mutable. +// To ensure separation of state between extended environments either make sure the TypeAdapter and +// TypeProvider are immutable, or that their underlying implementations are based on the +// ref.TypeRegistry which provides a Copy method which will be invoked by this method. +func (e *Env) Extend(opts ...EnvOption) (*Env, error) { + if e.chkErr != nil { + return nil, e.chkErr + } + + // The type-checker is configured with Declarations. The declarations may either be provided + // as options which have not yet been validated, or may come from a previous checker instance + // whose types have already been validated. + chkOptsCopy := make([]checker.Option, len(e.chkOpts)) + copy(chkOptsCopy, e.chkOpts) + + // Copy the declarations if needed. + decsCopy := []*exprpb.Decl{} + if e.chk != nil { + // If the type-checker has already been instantiated, then the e.declarations have been + // valdiated within the chk instance. + chkOptsCopy = append(chkOptsCopy, checker.ValidatedDeclarations(e.chk)) + } else { + // If the type-checker has not been instantiated, ensure the unvalidated declarations are + // provided to the extended Env instance. + decsCopy = make([]*exprpb.Decl, len(e.declarations)) + copy(decsCopy, e.declarations) + } + + // Copy macros and program options + macsCopy := make([]parser.Macro, len(e.macros)) + progOptsCopy := make([]ProgramOption, len(e.progOpts)) + copy(macsCopy, e.macros) + copy(progOptsCopy, e.progOpts) + + // Copy the adapter / provider if they appear to be mutable. + adapter := e.adapter + provider := e.provider + adapterReg, isAdapterReg := e.adapter.(ref.TypeRegistry) + providerReg, isProviderReg := e.provider.(ref.TypeRegistry) + // In most cases the provider and adapter will be a ref.TypeRegistry; + // however, in the rare cases where they are not, they are assumed to + // be immutable. Since it is possible to set the TypeProvider separately + // from the TypeAdapter, the possible configurations which could use a + // TypeRegistry as the base implementation are captured below. + if isAdapterReg && isProviderReg { + reg := providerReg.Copy() + provider = reg + // If the adapter and provider are the same object, set the adapter + // to the same ref.TypeRegistry as the provider. + if adapterReg == providerReg { + adapter = reg + } else { + // Otherwise, make a copy of the adapter. + adapter = adapterReg.Copy() + } + } else if isProviderReg { + provider = providerReg.Copy() + } else if isAdapterReg { + adapter = adapterReg.Copy() + } + + featuresCopy := make(map[int]bool, len(e.features)) + for k, v := range e.features { + featuresCopy[k] = v + } + + ext := &Env{ + Container: e.Container, + declarations: decsCopy, + macros: macsCopy, + progOpts: progOptsCopy, + adapter: adapter, + features: featuresCopy, + provider: provider, + chkOpts: chkOptsCopy, + } + return ext.configure(opts) +} + +// HasFeature checks whether the environment enables the given feature +// flag, as enumerated in options.go. +func (e *Env) HasFeature(flag int) bool { + enabled, has := e.features[flag] + return has && enabled +} + +// Parse parses the input expression value `txt` to a Ast and/or a set of Issues. +// +// This form of Parse creates a Source value for the input `txt` and forwards to the +// ParseSource method. +func (e *Env) Parse(txt string) (*Ast, *Issues) { + src := common.NewTextSource(txt) + return e.ParseSource(src) +} + +// ParseSource parses the input source to an Ast and/or set of Issues. +// +// Parsing has failed if the returned Issues value and its Issues.Err() value is non-nil. +// Issues should be inspected if they are non-nil, but may not represent a fatal error. +// +// It is possible to have both non-nil Ast and Issues values returned from this call; however, +// the mere presence of an Ast does not imply that it is valid for use. +func (e *Env) ParseSource(src Source) (*Ast, *Issues) { + res, errs := e.prsr.Parse(src) + if len(errs.GetErrors()) > 0 { + return nil, &Issues{errs: errs} + } + // Manually create the Ast to ensure that the text source information is propagated on + // subsequent calls to Check. + return &Ast{ + source: src, + expr: res.GetExpr(), + info: res.GetSourceInfo()}, nil +} + +// Program generates an evaluable instance of the Ast within the environment (Env). +func (e *Env) Program(ast *Ast, opts ...ProgramOption) (Program, error) { + optSet := e.progOpts + if len(opts) != 0 { + mergedOpts := []ProgramOption{} + mergedOpts = append(mergedOpts, e.progOpts...) + mergedOpts = append(mergedOpts, opts...) + optSet = mergedOpts + } + return newProgram(e, ast, optSet) +} + +// TypeAdapter returns the `ref.TypeAdapter` configured for the environment. +func (e *Env) TypeAdapter() ref.TypeAdapter { + return e.adapter +} + +// TypeProvider returns the `ref.TypeProvider` configured for the environment. +func (e *Env) TypeProvider() ref.TypeProvider { + return e.provider +} + +// UnknownVars returns an interpreter.PartialActivation which marks all variables +// declared in the Env as unknown AttributePattern values. +// +// Note, the UnknownVars will behave the same as an interpreter.EmptyActivation +// unless the PartialAttributes option is provided as a ProgramOption. +func (e *Env) UnknownVars() interpreter.PartialActivation { + var unknownPatterns []*interpreter.AttributePattern + for _, d := range e.declarations { + switch d.GetDeclKind().(type) { + case *exprpb.Decl_Ident: + unknownPatterns = append(unknownPatterns, + interpreter.NewAttributePattern(d.GetName())) + } + } + part, _ := PartialVars( + interpreter.EmptyActivation(), + unknownPatterns...) + return part +} + +// ResidualAst takes an Ast and its EvalDetails to produce a new Ast which only contains the +// attribute references which are unknown. +// +// Residual expressions are beneficial in a few scenarios: +// +// - Optimizing constant expression evaluations away. +// - Indexing and pruning expressions based on known input arguments. +// - Surfacing additional requirements that are needed in order to complete an evaluation. +// - Sharing the evaluation of an expression across multiple machines/nodes. +// +// For example, if an expression targets a 'resource' and 'request' attribute and the possible +// values for the resource are known, a PartialActivation could mark the 'request' as an unknown +// interpreter.AttributePattern and the resulting ResidualAst would be reduced to only the parts +// of the expression that reference the 'request'. +// +// Note, the expression ids within the residual AST generated through this method have no +// correlation to the expression ids of the original AST. +// +// See the PartialVars helper for how to construct a PartialActivation. +// +// TODO: Consider adding an option to generate a Program.Residual to avoid round-tripping to an +// Ast format and then Program again. +func (e *Env) ResidualAst(a *Ast, details *EvalDetails) (*Ast, error) { + pruned := interpreter.PruneAst(a.Expr(), details.State()) + expr, err := AstToString(ParsedExprToAst(&exprpb.ParsedExpr{Expr: pruned})) + if err != nil { + return nil, err + } + parsed, iss := e.Parse(expr) + if iss != nil && iss.Err() != nil { + return nil, iss.Err() + } + if !a.IsChecked() { + return parsed, nil + } + checked, iss := e.Check(parsed) + if iss != nil && iss.Err() != nil { + return nil, iss.Err() + } + return checked, nil +} + +// EstimateCost estimates the cost of a type checked CEL expression using the length estimates of input data and +// extension functions provided by estimator. +func (e *Env) EstimateCost(ast *Ast, estimator checker.CostEstimator) (checker.CostEstimate, error) { + checked, err := AstToCheckedExpr(ast) + if err != nil { + return checker.CostEstimate{}, fmt.Errorf("EsimateCost could not inspect Ast: %v", err) + } + return checker.Cost(checked, estimator), nil +} + +// configure applies a series of EnvOptions to the current environment. +func (e *Env) configure(opts []EnvOption) (*Env, error) { + // Customized the environment using the provided EnvOption values. If an error is + // generated at any step this, will be returned as a nil Env with a non-nil error. + var err error + for _, opt := range opts { + e, err = opt(e) + if err != nil { + return nil, err + } + } + + // Configure the parser. + prsrOpts := []parser.Option{parser.Macros(e.macros...)} + if e.HasFeature(featureEnableMacroCallTracking) { + prsrOpts = append(prsrOpts, parser.PopulateMacroCalls(true)) + } + e.prsr, err = parser.NewParser(prsrOpts...) + if err != nil { + return nil, err + } + + // The simplest way to eagerly validate declarations on environment creation is to compile + // a dummy program and check for the presence of e.chkErr being non-nil. + if e.HasFeature(featureEagerlyValidateDeclarations) { + err := e.initChecker() + if err != nil { + return nil, err + } + } + + return e, nil +} + +func (e *Env) initChecker() error { + e.chkOnce.Do(func() { + chkOpts := []checker.Option{} + chkOpts = append(chkOpts, e.chkOpts...) + chkOpts = append(chkOpts, + checker.HomogeneousAggregateLiterals( + e.HasFeature(featureDisableDynamicAggregateLiterals)), + checker.CrossTypeNumericComparisons( + e.HasFeature(featureCrossTypeNumericComparisons))) + + ce, err := checker.NewEnv(e.Container, e.provider, chkOpts...) + if err != nil { + e.chkErr = err + return + } + err = ce.Add(e.declarations...) + if err != nil { + e.chkErr = err + return + } + e.chk = ce + }) + return e.chkErr +} + +// Issues defines methods for inspecting the error details of parse and check calls. +// +// Note: in the future, non-fatal warnings and notices may be inspectable via the Issues struct. +type Issues struct { + errs *common.Errors +} + +// NewIssues returns an Issues struct from a common.Errors object. +func NewIssues(errs *common.Errors) *Issues { + return &Issues{ + errs: errs, + } +} + +// Err returns an error value if the issues list contains one or more errors. +func (i *Issues) Err() error { + if i == nil { + return nil + } + if len(i.Errors()) > 0 { + return errors.New(i.String()) + } + return nil +} + +// Errors returns the collection of errors encountered in more granular detail. +func (i *Issues) Errors() []common.Error { + if i == nil { + return []common.Error{} + } + return i.errs.GetErrors() +} + +// Append collects the issues from another Issues struct into a new Issues object. +func (i *Issues) Append(other *Issues) *Issues { + if i == nil { + return other + } + if other == nil { + return i + } + return NewIssues(i.errs.Append(other.errs.GetErrors())) +} + +// String converts the issues to a suitable display string. +func (i *Issues) String() string { + if i == nil { + return "" + } + return i.errs.ToDisplayString() +} + +// getStdEnv lazy initializes the CEL standard environment. +func getStdEnv() (*Env, error) { + stdEnvInit.Do(func() { + stdEnv, stdEnvErr = NewCustomEnv(StdLib(), EagerlyValidateDeclarations(true)) + }) + return stdEnv, stdEnvErr +} + +var ( + stdEnvInit sync.Once + stdEnv *Env + stdEnvErr error +) diff --git a/vendor/github.com/google/cel-go/cel/io.go b/vendor/github.com/google/cel-go/cel/io.go new file mode 100644 index 0000000000..02b7024446 --- /dev/null +++ b/vendor/github.com/google/cel-go/cel/io.go @@ -0,0 +1,280 @@ +// Copyright 2019 Google LLC +// +// 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. + +package cel + +import ( + "errors" + "fmt" + "reflect" + + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "github.com/google/cel-go/parser" + + "google.golang.org/protobuf/proto" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" + anypb "google.golang.org/protobuf/types/known/anypb" +) + +// CheckedExprToAst converts a checked expression proto message to an Ast. +func CheckedExprToAst(checkedExpr *exprpb.CheckedExpr) *Ast { + return CheckedExprToAstWithSource(checkedExpr, nil) +} + +// CheckedExprToAstWithSource converts a checked expression proto message to an Ast, +// using the provided Source as the textual contents. +// +// In general the source is not necessary unless the AST has been modified between the +// `Parse` and `Check` calls as an `Ast` created from the `Parse` step will carry the source +// through future calls. +// +// Prefer CheckedExprToAst if loading expressions from storage. +func CheckedExprToAstWithSource(checkedExpr *exprpb.CheckedExpr, src Source) *Ast { + refMap := checkedExpr.GetReferenceMap() + if refMap == nil { + refMap = map[int64]*exprpb.Reference{} + } + typeMap := checkedExpr.GetTypeMap() + if typeMap == nil { + typeMap = map[int64]*exprpb.Type{} + } + si := checkedExpr.GetSourceInfo() + if si == nil { + si = &exprpb.SourceInfo{} + } + if src == nil { + src = common.NewInfoSource(si) + } + return &Ast{ + expr: checkedExpr.GetExpr(), + info: si, + source: src, + refMap: refMap, + typeMap: typeMap, + } +} + +// AstToCheckedExpr converts an Ast to an protobuf CheckedExpr value. +// +// If the Ast.IsChecked() returns false, this conversion method will return an error. +func AstToCheckedExpr(a *Ast) (*exprpb.CheckedExpr, error) { + if !a.IsChecked() { + return nil, fmt.Errorf("cannot convert unchecked ast") + } + return &exprpb.CheckedExpr{ + Expr: a.Expr(), + SourceInfo: a.SourceInfo(), + ReferenceMap: a.refMap, + TypeMap: a.typeMap, + }, nil +} + +// ParsedExprToAst converts a parsed expression proto message to an Ast. +func ParsedExprToAst(parsedExpr *exprpb.ParsedExpr) *Ast { + return ParsedExprToAstWithSource(parsedExpr, nil) +} + +// ParsedExprToAstWithSource converts a parsed expression proto message to an Ast, +// using the provided Source as the textual contents. +// +// In general you only need this if you need to recheck a previously checked +// expression, or if you need to separately check a subset of an expression. +// +// Prefer ParsedExprToAst if loading expressions from storage. +func ParsedExprToAstWithSource(parsedExpr *exprpb.ParsedExpr, src Source) *Ast { + si := parsedExpr.GetSourceInfo() + if si == nil { + si = &exprpb.SourceInfo{} + } + if src == nil { + src = common.NewInfoSource(si) + } + return &Ast{ + expr: parsedExpr.GetExpr(), + info: si, + source: src, + } +} + +// AstToParsedExpr converts an Ast to an protobuf ParsedExpr value. +func AstToParsedExpr(a *Ast) (*exprpb.ParsedExpr, error) { + return &exprpb.ParsedExpr{ + Expr: a.Expr(), + SourceInfo: a.SourceInfo(), + }, nil +} + +// AstToString converts an Ast back to a string if possible. +// +// Note, the conversion may not be an exact replica of the original expression, but will produce +// a string that is semantically equivalent and whose textual representation is stable. +func AstToString(a *Ast) (string, error) { + expr := a.Expr() + info := a.SourceInfo() + return parser.Unparse(expr, info) +} + +// RefValueToValue converts between ref.Val and api.expr.Value. +// The result Value is the serialized proto form. The ref.Val must not be error or unknown. +func RefValueToValue(res ref.Val) (*exprpb.Value, error) { + switch res.Type() { + case types.BoolType: + return &exprpb.Value{ + Kind: &exprpb.Value_BoolValue{BoolValue: res.Value().(bool)}}, nil + case types.BytesType: + return &exprpb.Value{ + Kind: &exprpb.Value_BytesValue{BytesValue: res.Value().([]byte)}}, nil + case types.DoubleType: + return &exprpb.Value{ + Kind: &exprpb.Value_DoubleValue{DoubleValue: res.Value().(float64)}}, nil + case types.IntType: + return &exprpb.Value{ + Kind: &exprpb.Value_Int64Value{Int64Value: res.Value().(int64)}}, nil + case types.ListType: + l := res.(traits.Lister) + sz := l.Size().(types.Int) + elts := make([]*exprpb.Value, 0, int64(sz)) + for i := types.Int(0); i < sz; i++ { + v, err := RefValueToValue(l.Get(i)) + if err != nil { + return nil, err + } + elts = append(elts, v) + } + return &exprpb.Value{ + Kind: &exprpb.Value_ListValue{ + ListValue: &exprpb.ListValue{Values: elts}}}, nil + case types.MapType: + mapper := res.(traits.Mapper) + sz := mapper.Size().(types.Int) + entries := make([]*exprpb.MapValue_Entry, 0, int64(sz)) + for it := mapper.Iterator(); it.HasNext().(types.Bool); { + k := it.Next() + v := mapper.Get(k) + kv, err := RefValueToValue(k) + if err != nil { + return nil, err + } + vv, err := RefValueToValue(v) + if err != nil { + return nil, err + } + entries = append(entries, &exprpb.MapValue_Entry{Key: kv, Value: vv}) + } + return &exprpb.Value{ + Kind: &exprpb.Value_MapValue{ + MapValue: &exprpb.MapValue{Entries: entries}}}, nil + case types.NullType: + return &exprpb.Value{ + Kind: &exprpb.Value_NullValue{}}, nil + case types.StringType: + return &exprpb.Value{ + Kind: &exprpb.Value_StringValue{StringValue: res.Value().(string)}}, nil + case types.TypeType: + typeName := res.(ref.Type).TypeName() + return &exprpb.Value{Kind: &exprpb.Value_TypeValue{TypeValue: typeName}}, nil + case types.UintType: + return &exprpb.Value{ + Kind: &exprpb.Value_Uint64Value{Uint64Value: res.Value().(uint64)}}, nil + default: + any, err := res.ConvertToNative(anyPbType) + if err != nil { + return nil, err + } + return &exprpb.Value{ + Kind: &exprpb.Value_ObjectValue{ObjectValue: any.(*anypb.Any)}}, nil + } +} + +var ( + typeNameToTypeValue = map[string]*types.TypeValue{ + "bool": types.BoolType, + "bytes": types.BytesType, + "double": types.DoubleType, + "null_type": types.NullType, + "int": types.IntType, + "list": types.ListType, + "map": types.MapType, + "string": types.StringType, + "type": types.TypeType, + "uint": types.UintType, + } + + anyPbType = reflect.TypeOf(&anypb.Any{}) +) + +// ValueToRefValue converts between exprpb.Value and ref.Val. +func ValueToRefValue(adapter ref.TypeAdapter, v *exprpb.Value) (ref.Val, error) { + switch v.Kind.(type) { + case *exprpb.Value_NullValue: + return types.NullValue, nil + case *exprpb.Value_BoolValue: + return types.Bool(v.GetBoolValue()), nil + case *exprpb.Value_Int64Value: + return types.Int(v.GetInt64Value()), nil + case *exprpb.Value_Uint64Value: + return types.Uint(v.GetUint64Value()), nil + case *exprpb.Value_DoubleValue: + return types.Double(v.GetDoubleValue()), nil + case *exprpb.Value_StringValue: + return types.String(v.GetStringValue()), nil + case *exprpb.Value_BytesValue: + return types.Bytes(v.GetBytesValue()), nil + case *exprpb.Value_ObjectValue: + any := v.GetObjectValue() + msg, err := anypb.UnmarshalNew(any, proto.UnmarshalOptions{DiscardUnknown: true}) + if err != nil { + return nil, err + } + return adapter.NativeToValue(msg.(proto.Message)), nil + case *exprpb.Value_MapValue: + m := v.GetMapValue() + entries := make(map[ref.Val]ref.Val) + for _, entry := range m.Entries { + key, err := ValueToRefValue(adapter, entry.Key) + if err != nil { + return nil, err + } + pb, err := ValueToRefValue(adapter, entry.Value) + if err != nil { + return nil, err + } + entries[key] = pb + } + return adapter.NativeToValue(entries), nil + case *exprpb.Value_ListValue: + l := v.GetListValue() + elts := make([]ref.Val, len(l.Values)) + for i, e := range l.Values { + rv, err := ValueToRefValue(adapter, e) + if err != nil { + return nil, err + } + elts[i] = rv + } + return adapter.NativeToValue(elts), nil + case *exprpb.Value_TypeValue: + typeName := v.GetTypeValue() + tv, ok := typeNameToTypeValue[typeName] + if ok { + return tv, nil + } + return types.NewObjectTypeValue(typeName), nil + } + return nil, errors.New("unknown value") +} diff --git a/vendor/github.com/google/cel-go/cel/library.go b/vendor/github.com/google/cel-go/cel/library.go new file mode 100644 index 0000000000..5d63bca8b9 --- /dev/null +++ b/vendor/github.com/google/cel-go/cel/library.go @@ -0,0 +1,77 @@ +// Copyright 2020 Google LLC +// +// 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. + +package cel + +import ( + "github.com/google/cel-go/checker" + "github.com/google/cel-go/interpreter/functions" + "github.com/google/cel-go/parser" +) + +// Library provides a collection of EnvOption and ProgramOption values used to configure a CEL +// environment for a particular use case or with a related set of functionality. +// +// Note, the ProgramOption values provided by a library are expected to be static and not vary +// between calls to Env.Program(). If there is a need for such dynamic configuration, prefer to +// configure these options outside the Library and within the Env.Program() call directly. +type Library interface { + // CompileOptions returns a collection of functional options for configuring the Parse / Check + // environment. + CompileOptions() []EnvOption + + // ProgramOptions returns a collection of functional options which should be included in every + // Program generated from the Env.Program() call. + ProgramOptions() []ProgramOption +} + +// Lib creates an EnvOption out of a Library, allowing libraries to be provided as functional args, +// and to be linked to each other. +func Lib(l Library) EnvOption { + return func(e *Env) (*Env, error) { + var err error + for _, opt := range l.CompileOptions() { + e, err = opt(e) + if err != nil { + return nil, err + } + } + e.progOpts = append(e.progOpts, l.ProgramOptions()...) + return e, nil + } +} + +// StdLib returns an EnvOption for the standard library of CEL functions and macros. +func StdLib() EnvOption { + return Lib(stdLibrary{}) +} + +// stdLibrary implements the Library interface and provides functional options for the core CEL +// features documented in the specification. +type stdLibrary struct{} + +// EnvOptions returns options for the standard CEL function declarations and macros. +func (stdLibrary) CompileOptions() []EnvOption { + return []EnvOption{ + Declarations(checker.StandardDeclarations()...), + Macros(parser.AllMacros...), + } +} + +// ProgramOptions returns function implementations for the standard CEL functions. +func (stdLibrary) ProgramOptions() []ProgramOption { + return []ProgramOption{ + Functions(functions.StandardOverloads()...), + } +} diff --git a/vendor/github.com/google/cel-go/cel/options.go b/vendor/github.com/google/cel-go/cel/options.go new file mode 100644 index 0000000000..87de33af3a --- /dev/null +++ b/vendor/github.com/google/cel-go/cel/options.go @@ -0,0 +1,528 @@ +// Copyright 2019 Google LLC +// +// 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. + +package cel + +import ( + "fmt" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protodesc" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/types/dynamicpb" + + "github.com/google/cel-go/checker/decls" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/types/pb" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/interpreter" + "github.com/google/cel-go/interpreter/functions" + "github.com/google/cel-go/parser" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" + descpb "google.golang.org/protobuf/types/descriptorpb" +) + +// These constants beginning with "Feature" enable optional behavior in +// the library. See the documentation for each constant to see its +// effects, compatibility restrictions, and standard conformance. +const ( + _ = iota + + // Disallow heterogeneous aggregate (list, map) literals. + // Note, it is still possible to have heterogeneous aggregates when + // provided as variables to the expression, as well as via conversion + // of well-known dynamic types, or with unchecked expressions. + // Affects checking. Provides a subset of standard behavior. + featureDisableDynamicAggregateLiterals + + // Enable the tracking of function call expressions replaced by macros. + featureEnableMacroCallTracking + + // Enable the use of cross-type numeric comparisons at the type-checker. + featureCrossTypeNumericComparisons + + // Enable eager validation of declarations to ensure that Env values created + // with `Extend` inherit a validated list of declarations from the parent Env. + featureEagerlyValidateDeclarations +) + +// EnvOption is a functional interface for configuring the environment. +type EnvOption func(e *Env) (*Env, error) + +// ClearMacros options clears all parser macros. +// +// Clearing macros will ensure CEL expressions can only contain linear evaluation paths, as +// comprehensions such as `all` and `exists` are enabled only via macros. +func ClearMacros() EnvOption { + return func(e *Env) (*Env, error) { + e.macros = parser.NoMacros + return e, nil + } +} + +// CustomTypeAdapter swaps the default ref.TypeAdapter implementation with a custom one. +// +// Note: This option must be specified before the Types and TypeDescs options when used together. +func CustomTypeAdapter(adapter ref.TypeAdapter) EnvOption { + return func(e *Env) (*Env, error) { + e.adapter = adapter + return e, nil + } +} + +// CustomTypeProvider swaps the default ref.TypeProvider implementation with a custom one. +// +// Note: This option must be specified before the Types and TypeDescs options when used together. +func CustomTypeProvider(provider ref.TypeProvider) EnvOption { + return func(e *Env) (*Env, error) { + e.provider = provider + return e, nil + } +} + +// Declarations option extends the declaration set configured in the environment. +// +// Note: Declarations will by default be appended to the pre-existing declaration set configured +// for the environment. The NewEnv call builds on top of the standard CEL declarations. For a +// purely custom set of declarations use NewCustomEnv. +func Declarations(decls ...*exprpb.Decl) EnvOption { + // TODO: provide an alternative means of specifying declarations that doesn't refer + // to the underlying proto implementations. + return func(e *Env) (*Env, error) { + e.declarations = append(e.declarations, decls...) + return e, nil + } +} + +// EagerlyValidateDeclarations ensures that any collisions between configured declarations are caught +// at the time of the `NewEnv` call. +// +// Eagerly validating declarations is also useful for bootstrapping a base `cel.Env` value. +// Calls to base `Env.Extend()` will be significantly faster when declarations are eagerly validated +// as declarations will be collision-checked at most once and only incrementally by way of `Extend` +// +// Disabled by default as not all environments are used for type-checking. +func EagerlyValidateDeclarations(enabled bool) EnvOption { + return features(featureEagerlyValidateDeclarations, enabled) +} + +// HomogeneousAggregateLiterals option ensures that list and map literal entry types must agree +// during type-checking. +// +// Note, it is still possible to have heterogeneous aggregates when provided as variables to the +// expression, as well as via conversion of well-known dynamic types, or with unchecked +// expressions. +func HomogeneousAggregateLiterals() EnvOption { + return features(featureDisableDynamicAggregateLiterals, true) +} + +// Macros option extends the macro set configured in the environment. +// +// Note: This option must be specified after ClearMacros if used together. +func Macros(macros ...parser.Macro) EnvOption { + return func(e *Env) (*Env, error) { + e.macros = append(e.macros, macros...) + return e, nil + } +} + +// Container sets the container for resolving variable names. Defaults to an empty container. +// +// If all references within an expression are relative to a protocol buffer package, then +// specifying a container of `google.type` would make it possible to write expressions such as +// `Expr{expression: 'a < b'}` instead of having to write `google.type.Expr{...}`. +func Container(name string) EnvOption { + return func(e *Env) (*Env, error) { + cont, err := e.Container.Extend(containers.Name(name)) + if err != nil { + return nil, err + } + e.Container = cont + return e, nil + } +} + +// Abbrevs configures a set of simple names as abbreviations for fully-qualified names. +// +// An abbreviation (abbrev for short) is a simple name that expands to a fully-qualified name. +// Abbreviations can be useful when working with variables, functions, and especially types from +// multiple namespaces: +// +// // CEL object construction +// qual.pkg.version.ObjTypeName{ +// field: alt.container.ver.FieldTypeName{value: ...} +// } +// +// Only one the qualified names above may be used as the CEL container, so at least one of these +// references must be a long qualified name within an otherwise short CEL program. Using the +// following abbreviations, the program becomes much simpler: +// +// // CEL Go option +// Abbrevs("qual.pkg.version.ObjTypeName", "alt.container.ver.FieldTypeName") +// // Simplified Object construction +// ObjTypeName{field: FieldTypeName{value: ...}} +// +// There are a few rules for the qualified names and the simple abbreviations generated from them: +// - Qualified names must be dot-delimited, e.g. `package.subpkg.name`. +// - The last element in the qualified name is the abbreviation. +// - Abbreviations must not collide with each other. +// - The abbreviation must not collide with unqualified names in use. +// +// Abbreviations are distinct from container-based references in the following important ways: +// - Abbreviations must expand to a fully-qualified name. +// - Expanded abbreviations do not participate in namespace resolution. +// - Abbreviation expansion is done instead of the container search for a matching identifier. +// - Containers follow C++ namespace resolution rules with searches from the most qualified name +// to the least qualified name. +// - Container references within the CEL program may be relative, and are resolved to fully +// qualified names at either type-check time or program plan time, whichever comes first. +// +// If there is ever a case where an identifier could be in both the container and as an +// abbreviation, the abbreviation wins as this will ensure that the meaning of a program is +// preserved between compilations even as the container evolves. +func Abbrevs(qualifiedNames ...string) EnvOption { + return func(e *Env) (*Env, error) { + cont, err := e.Container.Extend(containers.Abbrevs(qualifiedNames...)) + if err != nil { + return nil, err + } + e.Container = cont + return e, nil + } +} + +// Types adds one or more type declarations to the environment, allowing for construction of +// type-literals whose definitions are included in the common expression built-in set. +// +// The input types may either be instances of `proto.Message` or `ref.Type`. Any other type +// provided to this option will result in an error. +// +// Well-known protobuf types within the `google.protobuf.*` package are included in the standard +// environment by default. +// +// Note: This option must be specified after the CustomTypeProvider option when used together. +func Types(addTypes ...interface{}) EnvOption { + return func(e *Env) (*Env, error) { + reg, isReg := e.provider.(ref.TypeRegistry) + if !isReg { + return nil, fmt.Errorf("custom types not supported by provider: %T", e.provider) + } + for _, t := range addTypes { + switch v := t.(type) { + case proto.Message: + fdMap := pb.CollectFileDescriptorSet(v) + for _, fd := range fdMap { + err := reg.RegisterDescriptor(fd) + if err != nil { + return nil, err + } + } + case ref.Type: + err := reg.RegisterType(v) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("unsupported type: %T", t) + } + } + return e, nil + } +} + +// TypeDescs adds type declarations from any protoreflect.FileDescriptor, protoregistry.Files, +// google.protobuf.FileDescriptorProto or google.protobuf.FileDescriptorSet provided. +// +// Note that messages instantiated from these descriptors will be *dynamicpb.Message values +// rather than the concrete message type. +// +// TypeDescs are hermetic to a single Env object, but may be copied to other Env values via +// extension or by re-using the same EnvOption with another NewEnv() call. +func TypeDescs(descs ...interface{}) EnvOption { + return func(e *Env) (*Env, error) { + reg, isReg := e.provider.(ref.TypeRegistry) + if !isReg { + return nil, fmt.Errorf("custom types not supported by provider: %T", e.provider) + } + // Scan the input descriptors for FileDescriptorProto messages and accumulate them into a + // synthetic FileDescriptorSet as the FileDescriptorProto messages may refer to each other + // and will not resolve properly unless they are part of the same set. + var fds *descpb.FileDescriptorSet + for _, d := range descs { + switch f := d.(type) { + case *descpb.FileDescriptorProto: + if fds == nil { + fds = &descpb.FileDescriptorSet{ + File: []*descpb.FileDescriptorProto{}, + } + } + fds.File = append(fds.File, f) + } + } + if fds != nil { + if err := registerFileSet(reg, fds); err != nil { + return nil, err + } + } + for _, d := range descs { + switch f := d.(type) { + case *protoregistry.Files: + if err := registerFiles(reg, f); err != nil { + return nil, err + } + case protoreflect.FileDescriptor: + if err := reg.RegisterDescriptor(f); err != nil { + return nil, err + } + case *descpb.FileDescriptorSet: + if err := registerFileSet(reg, f); err != nil { + return nil, err + } + case *descpb.FileDescriptorProto: + // skip, handled as a synthetic file descriptor set. + default: + return nil, fmt.Errorf("unsupported type descriptor: %T", d) + } + } + return e, nil + } +} + +func registerFileSet(reg ref.TypeRegistry, fileSet *descpb.FileDescriptorSet) error { + files, err := protodesc.NewFiles(fileSet) + if err != nil { + return fmt.Errorf("protodesc.NewFiles(%v) failed: %v", fileSet, err) + } + return registerFiles(reg, files) +} + +func registerFiles(reg ref.TypeRegistry, files *protoregistry.Files) error { + var err error + files.RangeFiles(func(fd protoreflect.FileDescriptor) bool { + err = reg.RegisterDescriptor(fd) + return err == nil + }) + return err +} + +// ProgramOption is a functional interface for configuring evaluation bindings and behaviors. +type ProgramOption func(p *prog) (*prog, error) + +// CustomDecorator appends an InterpreterDecorator to the program. +// +// InterpretableDecorators can be used to inspect, alter, or replace the Program plan. +func CustomDecorator(dec interpreter.InterpretableDecorator) ProgramOption { + return func(p *prog) (*prog, error) { + p.decorators = append(p.decorators, dec) + return p, nil + } +} + +// Functions adds function overloads that extend or override the set of CEL built-ins. +func Functions(funcs ...*functions.Overload) ProgramOption { + return func(p *prog) (*prog, error) { + if err := p.dispatcher.Add(funcs...); err != nil { + return nil, err + } + return p, nil + } +} + +// Globals sets the global variable values for a given program. These values may be shadowed by +// variables with the same name provided to the Eval() call. +// +// The vars value may either be an `interpreter.Activation` instance or a `map[string]interface{}`. +func Globals(vars interface{}) ProgramOption { + return func(p *prog) (*prog, error) { + defaultVars, err := interpreter.NewActivation(vars) + if err != nil { + return nil, err + } + p.defaultVars = defaultVars + return p, nil + } +} + +// OptimizeRegex provides a way to replace the InterpretableCall for regex functions. This can be used +// to compile regex string constants at program creation time and report any errors and then use the +// compiled regex for all regex function invocations. +func OptimizeRegex(regexOptimizations ...*interpreter.RegexOptimization) ProgramOption { + return func(p *prog) (*prog, error) { + p.regexOptimizations = append(p.regexOptimizations, regexOptimizations...) + return p, nil + } +} + +// EvalOption indicates an evaluation option that may affect the evaluation behavior or information +// in the output result. +type EvalOption int + +const ( + // OptTrackState will cause the runtime to return an immutable EvalState value in the Result. + OptTrackState EvalOption = 1 << iota + + // OptExhaustiveEval causes the runtime to disable short-circuits and track state. + OptExhaustiveEval EvalOption = 1< 0 { + decorators = append(decorators, interpreter.InterruptableEval()) + } + // Enable constant folding first. + if p.evalOpts&OptOptimize == OptOptimize { + decorators = append(decorators, interpreter.Optimize()) + p.regexOptimizations = append(p.regexOptimizations, interpreter.MatchesRegexOptimization) + } + // Enable regex compilation of constants immediately after folding constants. + if len(p.regexOptimizations) > 0 { + decorators = append(decorators, interpreter.CompileRegexConstants(p.regexOptimizations...)) + } + + // Enable exhaustive eval, state tracking and cost tracking last since they require a factory. + if p.evalOpts&(OptExhaustiveEval|OptTrackState|OptTrackCost) != 0 { + factory := func(state interpreter.EvalState, costTracker *interpreter.CostTracker) (Program, error) { + costTracker.Estimator = p.callCostEstimator + costTracker.Limit = p.costLimit + decs := decorators + var observers []interpreter.EvalObserver + + if p.evalOpts&(OptExhaustiveEval|OptTrackState) != 0 { + // EvalStateObserver is required for OptExhaustiveEval. + observers = append(observers, interpreter.EvalStateObserver(state)) + } + if p.evalOpts&OptTrackCost == OptTrackCost { + observers = append(observers, interpreter.CostObserver(costTracker)) + } + + // Enable exhaustive eval over a basic observer since it offers a superset of features. + if p.evalOpts&OptExhaustiveEval == OptExhaustiveEval { + decs = append(decs, interpreter.ExhaustiveEval(), interpreter.Observe(observers...)) + } else if len(observers) > 0 { + decs = append(decs, interpreter.Observe(observers...)) + } + + return p.clone().initInterpretable(ast, decs) + } + return newProgGen(factory) + } + return p.initInterpretable(ast, decorators) +} + +func (p *prog) initInterpretable(ast *Ast, decs []interpreter.InterpretableDecorator) (*prog, error) { + // Unchecked programs do not contain type and reference information and may be slower to execute. + if !ast.IsChecked() { + interpretable, err := + p.interpreter.NewUncheckedInterpretable(ast.Expr(), decs...) + if err != nil { + return nil, err + } + p.interpretable = interpretable + return p, nil + } + + // When the AST has been checked it contains metadata that can be used to speed up program execution. + var checked *exprpb.CheckedExpr + checked, err := AstToCheckedExpr(ast) + if err != nil { + return nil, err + } + interpretable, err := p.interpreter.NewInterpretable(checked, decs...) + if err != nil { + return nil, err + } + p.interpretable = interpretable + return p, nil +} + +// Eval implements the Program interface method. +func (p *prog) Eval(input interface{}) (v ref.Val, det *EvalDetails, err error) { + // Configure error recovery for unexpected panics during evaluation. Note, the use of named + // return values makes it possible to modify the error response during the recovery + // function. + defer func() { + if r := recover(); r != nil { + switch t := r.(type) { + case interpreter.EvalCancelledError: + err = t + default: + err = fmt.Errorf("internal error: %v", r) + } + } + }() + // Build a hierarchical activation if there are default vars set. + var vars interpreter.Activation + switch v := input.(type) { + case interpreter.Activation: + vars = v + case map[string]interface{}: + vars = activationPool.Setup(v) + defer activationPool.Put(vars) + default: + return nil, nil, fmt.Errorf("invalid input, wanted Activation or map[string]interface{}, got: (%T)%v", input, input) + } + if p.defaultVars != nil { + vars = interpreter.NewHierarchicalActivation(p.defaultVars, vars) + } + v = p.interpretable.Eval(vars) + // The output of an internal Eval may have a value (`v`) that is a types.Err. This step + // translates the CEL value to a Go error response. This interface does not quite match the + // RPC signature which allows for multiple errors to be returned, but should be sufficient. + if types.IsError(v) { + err = v.(*types.Err) + } + return +} + +// ContextEval implements the Program interface. +func (p *prog) ContextEval(ctx context.Context, input interface{}) (ref.Val, *EvalDetails, error) { + if ctx == nil { + return nil, nil, fmt.Errorf("context can not be nil") + } + // Configure the input, making sure to wrap Activation inputs in the special ctxActivation which + // exposes the #interrupted variable and manages rate-limited checks of the ctx.Done() state. + var vars interpreter.Activation + switch v := input.(type) { + case interpreter.Activation: + vars = ctxActivationPool.Setup(v, ctx.Done(), p.interruptCheckFrequency) + defer ctxActivationPool.Put(vars) + case map[string]interface{}: + rawVars := activationPool.Setup(v) + defer activationPool.Put(rawVars) + vars = ctxActivationPool.Setup(rawVars, ctx.Done(), p.interruptCheckFrequency) + defer ctxActivationPool.Put(vars) + default: + return nil, nil, fmt.Errorf("invalid input, wanted Activation or map[string]interface{}, got: (%T)%v", input, input) + } + return p.Eval(vars) +} + +// Cost implements the Coster interface method. +func (p *prog) Cost() (min, max int64) { + return estimateCost(p.interpretable) +} + +// progFactory is a helper alias for marking a program creation factory function. +type progFactory func(interpreter.EvalState, *interpreter.CostTracker) (Program, error) + +// progGen holds a reference to a progFactory instance and implements the Program interface. +type progGen struct { + factory progFactory +} + +// newProgGen tests the factory object by calling it once and returns a factory-based Program if +// the test is successful. +func newProgGen(factory progFactory) (Program, error) { + // Test the factory to make sure that configuration errors are spotted at config + _, err := factory(interpreter.NewEvalState(), &interpreter.CostTracker{}) + if err != nil { + return nil, err + } + return &progGen{factory: factory}, nil +} + +// Eval implements the Program interface method. +func (gen *progGen) Eval(input interface{}) (ref.Val, *EvalDetails, error) { + // The factory based Eval() differs from the standard evaluation model in that it generates a + // new EvalState instance for each call to ensure that unique evaluations yield unique stateful + // results. + state := interpreter.NewEvalState() + costTracker := &interpreter.CostTracker{} + det := &EvalDetails{state: state, costTracker: costTracker} + + // Generate a new instance of the interpretable using the factory configured during the call to + // newProgram(). It is incredibly unlikely that the factory call will generate an error given + // the factory test performed within the Program() call. + p, err := gen.factory(state, costTracker) + if err != nil { + return nil, det, err + } + + // Evaluate the input, returning the result and the 'state' within EvalDetails. + v, _, err := p.Eval(input) + if err != nil { + return v, det, err + } + return v, det, nil +} + +// ContextEval implements the Program interface method. +func (gen *progGen) ContextEval(ctx context.Context, input interface{}) (ref.Val, *EvalDetails, error) { + if ctx == nil { + return nil, nil, fmt.Errorf("context can not be nil") + } + // The factory based Eval() differs from the standard evaluation model in that it generates a + // new EvalState instance for each call to ensure that unique evaluations yield unique stateful + // results. + state := interpreter.NewEvalState() + costTracker := &interpreter.CostTracker{} + det := &EvalDetails{state: state, costTracker: costTracker} + + // Generate a new instance of the interpretable using the factory configured during the call to + // newProgram(). It is incredibly unlikely that the factory call will generate an error given + // the factory test performed within the Program() call. + p, err := gen.factory(state, costTracker) + if err != nil { + return nil, det, err + } + + // Evaluate the input, returning the result and the 'state' within EvalDetails. + v, _, err := p.ContextEval(ctx, input) + if err != nil { + return v, det, err + } + return v, det, nil +} + +// Cost implements the Coster interface method. +func (gen *progGen) Cost() (min, max int64) { + // Use an empty state value since no evaluation is performed. + p, err := gen.factory(emptyEvalState, nil) + if err != nil { + return 0, math.MaxInt64 + } + return estimateCost(p) +} + +// EstimateCost returns the heuristic cost interval for the program. +func EstimateCost(p Program) (min, max int64) { + return estimateCost(p) +} + +func estimateCost(i interface{}) (min, max int64) { + c, ok := i.(interpreter.Coster) + if !ok { + return 0, math.MaxInt64 + } + return c.Cost() +} + +type ctxEvalActivation struct { + parent interpreter.Activation + interrupt <-chan struct{} + interruptCheckCount uint + interruptCheckFrequency uint +} + +// ResolveName implements the Activation interface method, but adds a special #interrupted variable +// which is capable of testing whether a 'done' signal is provided from a context.Context channel. +func (a *ctxEvalActivation) ResolveName(name string) (interface{}, bool) { + if name == "#interrupted" { + a.interruptCheckCount++ + if a.interruptCheckCount%a.interruptCheckFrequency == 0 { + select { + case <-a.interrupt: + return true, true + default: + return nil, false + } + } + return nil, false + } + return a.parent.ResolveName(name) +} + +func (a *ctxEvalActivation) Parent() interpreter.Activation { + return a.parent +} + +func newCtxEvalActivationPool() *ctxEvalActivationPool { + return &ctxEvalActivationPool{ + Pool: sync.Pool{ + New: func() interface{} { + return &ctxEvalActivation{} + }, + }, + } +} + +type ctxEvalActivationPool struct { + sync.Pool +} + +// Setup initializes a pooled Activation with the ability check for context.Context cancellation +func (p *ctxEvalActivationPool) Setup(vars interpreter.Activation, done <-chan struct{}, interruptCheckRate uint) *ctxEvalActivation { + a := p.Pool.Get().(*ctxEvalActivation) + a.parent = vars + a.interrupt = done + a.interruptCheckCount = 0 + a.interruptCheckFrequency = interruptCheckRate + return a +} + +type evalActivation struct { + vars map[string]interface{} + lazyVars map[string]interface{} +} + +// ResolveName looks up the value of the input variable name, if found. +// +// Lazy bindings may be supplied within the map-based input in either of the following forms: +// - func() interface{} +// - func() ref.Val +// +// The lazy binding will only be invoked once per evaluation. +// +// Values which are not represented as ref.Val types on input may be adapted to a ref.Val using +// the ref.TypeAdapter configured in the environment. +func (a *evalActivation) ResolveName(name string) (interface{}, bool) { + v, found := a.vars[name] + if !found { + return nil, false + } + switch obj := v.(type) { + case func() ref.Val: + if resolved, found := a.lazyVars[name]; found { + return resolved, true + } + lazy := obj() + a.lazyVars[name] = lazy + return lazy, true + case func() interface{}: + if resolved, found := a.lazyVars[name]; found { + return resolved, true + } + lazy := obj() + a.lazyVars[name] = lazy + return lazy, true + default: + return obj, true + } +} + +// Parent implements the interpreter.Activation interface +func (a *evalActivation) Parent() interpreter.Activation { + return nil +} + +func newEvalActivationPool() *evalActivationPool { + return &evalActivationPool{ + Pool: sync.Pool{ + New: func() interface{} { + return &evalActivation{lazyVars: make(map[string]interface{})} + }, + }, + } +} + +type evalActivationPool struct { + sync.Pool +} + +// Setup initializes a pooled Activation object with the map input. +func (p *evalActivationPool) Setup(vars map[string]interface{}) *evalActivation { + a := p.Pool.Get().(*evalActivation) + a.vars = vars + return a +} + +func (p *evalActivationPool) Put(value interface{}) { + a := value.(*evalActivation) + for k := range a.lazyVars { + delete(a.lazyVars, k) + } + p.Pool.Put(a) +} + +var ( + emptyEvalState = interpreter.NewEvalState() + + // activationPool is an internally managed pool of Activation values that wrap map[string]interface{} inputs + activationPool = newEvalActivationPool() + + // ctxActivationPool is an internally managed pool of Activation values that expose a special #interrupted variable + ctxActivationPool = newCtxEvalActivationPool() +) diff --git a/vendor/github.com/google/cel-go/checker/checker.go b/vendor/github.com/google/cel-go/checker/checker.go new file mode 100644 index 0000000000..4e7929d120 --- /dev/null +++ b/vendor/github.com/google/cel-go/checker/checker.go @@ -0,0 +1,646 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Package checker defines functions to type-checked a parsed expression +// against a set of identifier and function declarations. +package checker + +import ( + "fmt" + "reflect" + + "github.com/google/cel-go/checker/decls" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/types/ref" + + "google.golang.org/protobuf/proto" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +type checker struct { + env *Env + errors *typeErrors + mappings *mapping + freeTypeVarCounter int + sourceInfo *exprpb.SourceInfo + types map[int64]*exprpb.Type + references map[int64]*exprpb.Reference +} + +// Check performs type checking, giving a typed AST. +// The input is a ParsedExpr proto and an env which encapsulates +// type binding of variables, declarations of built-in functions, +// descriptions of protocol buffers, and a registry for errors. +// Returns a CheckedExpr proto, which might not be usable if +// there are errors in the error registry. +func Check(parsedExpr *exprpb.ParsedExpr, + source common.Source, + env *Env) (*exprpb.CheckedExpr, *common.Errors) { + c := checker{ + env: env, + errors: &typeErrors{common.NewErrors(source)}, + mappings: newMapping(), + freeTypeVarCounter: 0, + sourceInfo: parsedExpr.GetSourceInfo(), + types: make(map[int64]*exprpb.Type), + references: make(map[int64]*exprpb.Reference), + } + c.check(parsedExpr.GetExpr()) + + // Walk over the final type map substituting any type parameters either by their bound value or + // by DYN. + m := make(map[int64]*exprpb.Type) + for k, v := range c.types { + m[k] = substitute(c.mappings, v, true) + } + + return &exprpb.CheckedExpr{ + Expr: parsedExpr.GetExpr(), + SourceInfo: parsedExpr.GetSourceInfo(), + TypeMap: m, + ReferenceMap: c.references, + }, c.errors.Errors +} + +func (c *checker) check(e *exprpb.Expr) { + if e == nil { + return + } + + switch e.ExprKind.(type) { + case *exprpb.Expr_ConstExpr: + literal := e.GetConstExpr() + switch literal.ConstantKind.(type) { + case *exprpb.Constant_BoolValue: + c.checkBoolLiteral(e) + case *exprpb.Constant_BytesValue: + c.checkBytesLiteral(e) + case *exprpb.Constant_DoubleValue: + c.checkDoubleLiteral(e) + case *exprpb.Constant_Int64Value: + c.checkInt64Literal(e) + case *exprpb.Constant_NullValue: + c.checkNullLiteral(e) + case *exprpb.Constant_StringValue: + c.checkStringLiteral(e) + case *exprpb.Constant_Uint64Value: + c.checkUint64Literal(e) + } + case *exprpb.Expr_IdentExpr: + c.checkIdent(e) + case *exprpb.Expr_SelectExpr: + c.checkSelect(e) + case *exprpb.Expr_CallExpr: + c.checkCall(e) + case *exprpb.Expr_ListExpr: + c.checkCreateList(e) + case *exprpb.Expr_StructExpr: + c.checkCreateStruct(e) + case *exprpb.Expr_ComprehensionExpr: + c.checkComprehension(e) + default: + c.errors.ReportError( + c.location(e), "Unrecognized ast type: %v", reflect.TypeOf(e)) + } +} + +func (c *checker) checkInt64Literal(e *exprpb.Expr) { + c.setType(e, decls.Int) +} + +func (c *checker) checkUint64Literal(e *exprpb.Expr) { + c.setType(e, decls.Uint) +} + +func (c *checker) checkStringLiteral(e *exprpb.Expr) { + c.setType(e, decls.String) +} + +func (c *checker) checkBytesLiteral(e *exprpb.Expr) { + c.setType(e, decls.Bytes) +} + +func (c *checker) checkDoubleLiteral(e *exprpb.Expr) { + c.setType(e, decls.Double) +} + +func (c *checker) checkBoolLiteral(e *exprpb.Expr) { + c.setType(e, decls.Bool) +} + +func (c *checker) checkNullLiteral(e *exprpb.Expr) { + c.setType(e, decls.Null) +} + +func (c *checker) checkIdent(e *exprpb.Expr) { + identExpr := e.GetIdentExpr() + // Check to see if the identifier is declared. + if ident := c.env.LookupIdent(identExpr.GetName()); ident != nil { + c.setType(e, ident.GetIdent().Type) + c.setReference(e, newIdentReference(ident.GetName(), ident.GetIdent().Value)) + // Overwrite the identifier with its fully qualified name. + identExpr.Name = ident.GetName() + return + } + + c.setType(e, decls.Error) + c.errors.undeclaredReference( + c.location(e), c.env.container.Name(), identExpr.GetName()) +} + +func (c *checker) checkSelect(e *exprpb.Expr) { + sel := e.GetSelectExpr() + // Before traversing down the tree, try to interpret as qualified name. + qname, found := containers.ToQualifiedName(e) + if found { + ident := c.env.LookupIdent(qname) + if ident != nil { + // We don't check for a TestOnly expression here since the `found` result is + // always going to be false for TestOnly expressions. + + // Rewrite the node to be a variable reference to the resolved fully-qualified + // variable name. + c.setType(e, ident.GetIdent().Type) + c.setReference(e, newIdentReference(ident.GetName(), ident.GetIdent().Value)) + identName := ident.GetName() + e.ExprKind = &exprpb.Expr_IdentExpr{ + IdentExpr: &exprpb.Expr_Ident{ + Name: identName, + }, + } + return + } + } + + // Interpret as field selection, first traversing down the operand. + c.check(sel.Operand) + targetType := c.getType(sel.Operand) + // Assume error type by default as most types do not support field selection. + resultType := decls.Error + switch kindOf(targetType) { + case kindMap: + // Maps yield their value type as the selection result type. + mapType := targetType.GetMapType() + resultType = mapType.ValueType + case kindObject: + // Objects yield their field type declaration as the selection result type, but only if + // the field is defined. + messageType := targetType + if fieldType, found := c.lookupFieldType( + c.location(e), + messageType.GetMessageType(), + sel.Field); found { + resultType = fieldType.Type + } + case kindTypeParam: + // Set the operand type to DYN to prevent assignment to a potentially incorrect type + // at a later point in type-checking. The isAssignable call will update the type + // substitutions for the type param under the covers. + c.isAssignable(decls.Dyn, targetType) + // Also, set the result type to DYN. + resultType = decls.Dyn + default: + // Dynamic / error values are treated as DYN type. Errors are handled this way as well + // in order to allow forward progress on the check. + if isDynOrError(targetType) { + resultType = decls.Dyn + } else { + c.errors.typeDoesNotSupportFieldSelection(c.location(e), targetType) + } + } + if sel.TestOnly { + resultType = decls.Bool + } + c.setType(e, resultType) +} + +func (c *checker) checkCall(e *exprpb.Expr) { + // Note: similar logic exists within the `interpreter/planner.go`. If making changes here + // please consider the impact on planner.go and consolidate implementations or mirror code + // as appropriate. + call := e.GetCallExpr() + target := call.GetTarget() + args := call.GetArgs() + fnName := call.GetFunction() + + // Traverse arguments. + for _, arg := range args { + c.check(arg) + } + + // Regular static call with simple name. + if target == nil { + // Check for the existence of the function. + fn := c.env.LookupFunction(fnName) + if fn == nil { + c.errors.undeclaredReference( + c.location(e), c.env.container.Name(), fnName) + c.setType(e, decls.Error) + return + } + // Overwrite the function name with its fully qualified resolved name. + call.Function = fn.GetName() + // Check to see whether the overload resolves. + c.resolveOverloadOrError(c.location(e), e, fn, nil, args) + return + } + + // If a receiver 'target' is present, it may either be a receiver function, or a namespaced + // function, but not both. Given a.b.c() either a.b.c is a function or c is a function with + // target a.b. + // + // Check whether the target is a namespaced function name. + qualifiedPrefix, maybeQualified := containers.ToQualifiedName(target) + if maybeQualified { + maybeQualifiedName := qualifiedPrefix + "." + fnName + fn := c.env.LookupFunction(maybeQualifiedName) + if fn != nil { + // The function name is namespaced and so preserving the target operand would + // be an inaccurate representation of the desired evaluation behavior. + // Overwrite with fully-qualified resolved function name sans receiver target. + call.Target = nil + call.Function = fn.GetName() + c.resolveOverloadOrError(c.location(e), e, fn, nil, args) + return + } + } + + // Regular instance call. + c.check(call.Target) + fn := c.env.LookupFunction(fnName) + // Function found, attempt overload resolution. + if fn != nil { + c.resolveOverloadOrError(c.location(e), e, fn, target, args) + return + } + // Function name not declared, record error. + c.errors.undeclaredReference(c.location(e), c.env.container.Name(), fnName) +} + +func (c *checker) resolveOverloadOrError( + loc common.Location, + e *exprpb.Expr, + fn *exprpb.Decl, target *exprpb.Expr, args []*exprpb.Expr) { + // Attempt to resolve the overload. + resolution := c.resolveOverload(loc, fn, target, args) + // No such overload, error noted in the resolveOverload call, type recorded here. + if resolution == nil { + c.setType(e, decls.Error) + return + } + // Overload found. + c.setType(e, resolution.Type) + c.setReference(e, resolution.Reference) +} + +func (c *checker) resolveOverload( + loc common.Location, + fn *exprpb.Decl, target *exprpb.Expr, args []*exprpb.Expr) *overloadResolution { + + var argTypes []*exprpb.Type + if target != nil { + argTypes = append(argTypes, c.getType(target)) + } + for _, arg := range args { + argTypes = append(argTypes, c.getType(arg)) + } + + var resultType *exprpb.Type + var checkedRef *exprpb.Reference + for _, overload := range fn.GetFunction().Overloads { + // Determine whether the overload is currently considered. + if c.env.isOverloadDisabled(overload.GetOverloadId()) { + continue + } + + // Ensure the call style for the overload matches. + if (target == nil && overload.IsInstanceFunction) || + (target != nil && !overload.IsInstanceFunction) { + // not a compatible call style. + continue + } + + overloadType := decls.NewFunctionType(overload.ResultType, overload.Params...) + if len(overload.GetTypeParams()) > 0 { + // Instantiate overload's type with fresh type variables. + substitutions := newMapping() + for _, typePar := range overload.GetTypeParams() { + substitutions.add(decls.NewTypeParamType(typePar), c.newTypeVar()) + } + overloadType = substitute(substitutions, overloadType, false) + } + + candidateArgTypes := overloadType.GetFunction().GetArgTypes() + if c.isAssignableList(argTypes, candidateArgTypes) { + if checkedRef == nil { + checkedRef = newFunctionReference(overload.GetOverloadId()) + } else { + checkedRef.OverloadId = append(checkedRef.OverloadId, overload.GetOverloadId()) + } + + // First matching overload, determines result type. + fnResultType := substitute(c.mappings, + overloadType.GetFunction().GetResultType(), + false) + if resultType == nil { + resultType = fnResultType + } else if !isDyn(resultType) && !proto.Equal(fnResultType, resultType) { + resultType = decls.Dyn + } + } + } + + if resultType == nil { + c.errors.noMatchingOverload(loc, fn.GetName(), argTypes, target != nil) + resultType = decls.Error + return nil + } + + return newResolution(checkedRef, resultType) +} + +func (c *checker) checkCreateList(e *exprpb.Expr) { + create := e.GetListExpr() + var elemType *exprpb.Type + for _, e := range create.Elements { + c.check(e) + elemType = c.joinTypes(c.location(e), elemType, c.getType(e)) + } + if elemType == nil { + // If the list is empty, assign free type var to elem type. + elemType = c.newTypeVar() + } + c.setType(e, decls.NewListType(elemType)) +} + +func (c *checker) checkCreateStruct(e *exprpb.Expr) { + str := e.GetStructExpr() + if str.MessageName != "" { + c.checkCreateMessage(e) + } else { + c.checkCreateMap(e) + } +} + +func (c *checker) checkCreateMap(e *exprpb.Expr) { + mapVal := e.GetStructExpr() + var keyType *exprpb.Type + var valueType *exprpb.Type + for _, ent := range mapVal.GetEntries() { + key := ent.GetMapKey() + c.check(key) + keyType = c.joinTypes(c.location(key), keyType, c.getType(key)) + + c.check(ent.Value) + valueType = c.joinTypes(c.location(ent.Value), valueType, c.getType(ent.Value)) + } + if keyType == nil { + // If the map is empty, assign free type variables to typeKey and value type. + keyType = c.newTypeVar() + valueType = c.newTypeVar() + } + c.setType(e, decls.NewMapType(keyType, valueType)) +} + +func (c *checker) checkCreateMessage(e *exprpb.Expr) { + msgVal := e.GetStructExpr() + // Determine the type of the message. + messageType := decls.Error + decl := c.env.LookupIdent(msgVal.MessageName) + if decl == nil { + c.errors.undeclaredReference( + c.location(e), c.env.container.Name(), msgVal.MessageName) + return + } + // Ensure the type name is fully qualified in the AST. + msgVal.MessageName = decl.GetName() + c.setReference(e, newIdentReference(decl.GetName(), nil)) + ident := decl.GetIdent() + identKind := kindOf(ident.Type) + if identKind != kindError { + if identKind != kindType { + c.errors.notAType(c.location(e), ident.Type) + } else { + messageType = ident.Type.GetType() + if kindOf(messageType) != kindObject { + c.errors.notAMessageType(c.location(e), messageType) + messageType = decls.Error + } + } + } + if isObjectWellKnownType(messageType) { + c.setType(e, getObjectWellKnownType(messageType)) + } else { + c.setType(e, messageType) + } + + // Check the field initializers. + for _, ent := range msgVal.GetEntries() { + field := ent.GetFieldKey() + value := ent.Value + c.check(value) + + fieldType := decls.Error + if t, found := c.lookupFieldType( + c.locationByID(ent.Id), + messageType.GetMessageType(), + field); found { + fieldType = t.Type + } + if !c.isAssignable(fieldType, c.getType(value)) { + c.errors.fieldTypeMismatch( + c.locationByID(ent.Id), field, fieldType, c.getType(value)) + } + } +} + +func (c *checker) checkComprehension(e *exprpb.Expr) { + comp := e.GetComprehensionExpr() + c.check(comp.IterRange) + c.check(comp.AccuInit) + accuType := c.getType(comp.AccuInit) + rangeType := c.getType(comp.IterRange) + var varType *exprpb.Type + + switch kindOf(rangeType) { + case kindList: + varType = rangeType.GetListType().ElemType + case kindMap: + // Ranges over the keys. + varType = rangeType.GetMapType().KeyType + case kindDyn, kindError, kindTypeParam: + // Set the range type to DYN to prevent assignment to a potentially incorrect type + // at a later point in type-checking. The isAssignable call will update the type + // substitutions for the type param under the covers. + c.isAssignable(decls.Dyn, rangeType) + // Set the range iteration variable to type DYN as well. + varType = decls.Dyn + default: + c.errors.notAComprehensionRange(c.location(comp.IterRange), rangeType) + varType = decls.Error + } + + // Create a scope for the comprehension since it has a local accumulation variable. + // This scope will contain the accumulation variable used to compute the result. + c.env = c.env.enterScope() + c.env.Add(decls.NewVar(comp.AccuVar, accuType)) + // Create a block scope for the loop. + c.env = c.env.enterScope() + c.env.Add(decls.NewVar(comp.IterVar, varType)) + // Check the variable references in the condition and step. + c.check(comp.LoopCondition) + c.assertType(comp.LoopCondition, decls.Bool) + c.check(comp.LoopStep) + c.assertType(comp.LoopStep, accuType) + // Exit the loop's block scope before checking the result. + c.env = c.env.exitScope() + c.check(comp.Result) + // Exit the comprehension scope. + c.env = c.env.exitScope() + c.setType(e, c.getType(comp.Result)) +} + +// Checks compatibility of joined types, and returns the most general common type. +func (c *checker) joinTypes(loc common.Location, + previous *exprpb.Type, + current *exprpb.Type) *exprpb.Type { + if previous == nil { + return current + } + if c.isAssignable(previous, current) { + return mostGeneral(previous, current) + } + if c.dynAggregateLiteralElementTypesEnabled() { + return decls.Dyn + } + c.errors.typeMismatch(loc, previous, current) + return decls.Error +} + +func (c *checker) dynAggregateLiteralElementTypesEnabled() bool { + return c.env.aggLitElemType == dynElementType +} + +func (c *checker) newTypeVar() *exprpb.Type { + id := c.freeTypeVarCounter + c.freeTypeVarCounter++ + return decls.NewTypeParamType(fmt.Sprintf("_var%d", id)) +} + +func (c *checker) isAssignable(t1 *exprpb.Type, t2 *exprpb.Type) bool { + subs := isAssignable(c.mappings, t1, t2) + if subs != nil { + c.mappings = subs + return true + } + + return false +} + +func (c *checker) isAssignableList(l1 []*exprpb.Type, l2 []*exprpb.Type) bool { + subs := isAssignableList(c.mappings, l1, l2) + if subs != nil { + c.mappings = subs + return true + } + + return false +} + +func (c *checker) lookupFieldType(l common.Location, messageType string, fieldName string) (*ref.FieldType, bool) { + if _, found := c.env.provider.FindType(messageType); !found { + // This should not happen, anyway, report an error. + c.errors.unexpectedFailedResolution(l, messageType) + return nil, false + } + + if ft, found := c.env.provider.FindFieldType(messageType, fieldName); found { + return ft, found + } + + c.errors.undefinedField(l, fieldName) + return nil, false +} + +func (c *checker) setType(e *exprpb.Expr, t *exprpb.Type) { + if old, found := c.types[e.Id]; found && !proto.Equal(old, t) { + c.errors.ReportError(c.location(e), + "(Incompatible) Type already exists for expression: %v(%d) old:%v, new:%v", e, e.GetId(), old, t) + return + } + c.types[e.Id] = t +} + +func (c *checker) getType(e *exprpb.Expr) *exprpb.Type { + return c.types[e.Id] +} + +func (c *checker) setReference(e *exprpb.Expr, r *exprpb.Reference) { + if old, found := c.references[e.Id]; found && !proto.Equal(old, r) { + c.errors.ReportError(c.location(e), + "Reference already exists for expression: %v(%d) old:%v, new:%v", e, e.Id, old, r) + return + } + c.references[e.Id] = r +} + +func (c *checker) assertType(e *exprpb.Expr, t *exprpb.Type) { + if !c.isAssignable(t, c.getType(e)) { + c.errors.typeMismatch(c.location(e), t, c.getType(e)) + } +} + +type overloadResolution struct { + Reference *exprpb.Reference + Type *exprpb.Type +} + +func newResolution(checkedRef *exprpb.Reference, t *exprpb.Type) *overloadResolution { + return &overloadResolution{ + Reference: checkedRef, + Type: t, + } +} + +func (c *checker) location(e *exprpb.Expr) common.Location { + return c.locationByID(e.Id) +} + +func (c *checker) locationByID(id int64) common.Location { + positions := c.sourceInfo.GetPositions() + var line = 1 + if offset, found := positions[id]; found { + col := int(offset) + for _, lineOffset := range c.sourceInfo.LineOffsets { + if lineOffset < offset { + line++ + col = int(offset - lineOffset) + } else { + break + } + } + return common.NewLocation(line, col) + } + return common.NoLocation +} + +func newIdentReference(name string, value *exprpb.Constant) *exprpb.Reference { + return &exprpb.Reference{Name: name, Value: value} +} + +func newFunctionReference(overloads ...string) *exprpb.Reference { + return &exprpb.Reference{OverloadId: overloads} +} diff --git a/vendor/github.com/google/cel-go/checker/cost.go b/vendor/github.com/google/cel-go/checker/cost.go new file mode 100644 index 0000000000..75142ebc19 --- /dev/null +++ b/vendor/github.com/google/cel-go/checker/cost.go @@ -0,0 +1,601 @@ +// Copyright 2022 Google LLC +// +// 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. + +package checker + +import ( + "math" + + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/parser" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +// WARNING: Any changes to cost calculations in this file require a corresponding change in interpreter/runtimecost.go + +// CostEstimator estimates the sizes of variable length input data and the costs of functions. +type CostEstimator interface { + // EstimateSize returns a SizeEstimate for the given AstNode, or nil if + // the estimator has no estimate to provide. The size is equivalent to the result of the CEL `size()` function: + // length of strings and bytes, number of map entries or number of list items. + // EstimateSize is only called for AstNodes where + // CEL does not know the size; EstimateSize is not called for values defined inline in CEL where the size + // is already obvious to CEL. + EstimateSize(element AstNode) *SizeEstimate + // EstimateCallCost returns the estimated cost of an invocation, or nil if + // the estimator has no estimate to provide. + EstimateCallCost(function, overloadID string, target *AstNode, args []AstNode) *CallEstimate +} + +// CallEstimate includes a CostEstimate for the call, and an optional estimate of the result object size. +// The ResultSize should only be provided if the call results in a map, list, string or bytes. +type CallEstimate struct { + CostEstimate + ResultSize *SizeEstimate +} + +// AstNode represents an AST node for the purpose of cost estimations. +type AstNode interface { + // Path returns a field path through the provided type declarations to the type of the AstNode, or nil if the AstNode does not + // represent type directly reachable from the provided type declarations. + // The first path element is a variable. All subsequent path elements are one of: field name, '@items', '@keys', '@values'. + Path() []string + // Type returns the deduced type of the AstNode. + Type() *exprpb.Type + // Expr returns the expression of the AstNode. + Expr() *exprpb.Expr + // ComputedSize returns a size estimate of the AstNode derived from information available in the CEL expression. + // For constants and inline list and map declarations, the exact size is returned. For concatenated list, strings + // and bytes, the size is derived from the size estimates of the operands. nil is returned if there is no + // computed size available. + ComputedSize() *SizeEstimate +} + +type astNode struct { + path []string + t *exprpb.Type + expr *exprpb.Expr + derivedSize *SizeEstimate +} + +func (e astNode) Path() []string { + return e.path +} + +func (e astNode) Type() *exprpb.Type { + return e.t +} + +func (e astNode) Expr() *exprpb.Expr { + return e.expr +} + +func (e astNode) ComputedSize() *SizeEstimate { + if e.derivedSize != nil { + return e.derivedSize + } + var v uint64 + switch ek := e.expr.ExprKind.(type) { + case *exprpb.Expr_ConstExpr: + switch ck := ek.ConstExpr.ConstantKind.(type) { + case *exprpb.Constant_StringValue: + v = uint64(len(ck.StringValue)) + case *exprpb.Constant_BytesValue: + v = uint64(len(ck.BytesValue)) + case *exprpb.Constant_BoolValue, *exprpb.Constant_DoubleValue, *exprpb.Constant_DurationValue, + *exprpb.Constant_Int64Value, *exprpb.Constant_TimestampValue, *exprpb.Constant_Uint64Value, + *exprpb.Constant_NullValue: + v = uint64(1) + default: + return nil + } + case *exprpb.Expr_ListExpr: + v = uint64(len(ek.ListExpr.Elements)) + case *exprpb.Expr_StructExpr: + if ek.StructExpr.MessageName == "" { + v = uint64(len(ek.StructExpr.Entries)) + } + default: + return nil + } + + return &SizeEstimate{Min: v, Max: v} +} + +// SizeEstimate represents an estimated size of a variable length string, bytes, map or list. +type SizeEstimate struct { + Min, Max uint64 +} + +// Add adds to another SizeEstimate and returns the sum. +// If add would result in an uint64 overflow, the result is math.MaxUint64. +func (se SizeEstimate) Add(sizeEstimate SizeEstimate) SizeEstimate { + return SizeEstimate{ + addUint64NoOverflow(se.Min, sizeEstimate.Min), + addUint64NoOverflow(se.Max, sizeEstimate.Max), + } +} + +// Multiply multiplies by another SizeEstimate and returns the product. +// If multiply would result in an uint64 overflow, the result is math.MaxUint64. +func (se SizeEstimate) Multiply(sizeEstimate SizeEstimate) SizeEstimate { + return SizeEstimate{ + multiplyUint64NoOverflow(se.Min, sizeEstimate.Min), + multiplyUint64NoOverflow(se.Max, sizeEstimate.Max), + } +} + +// MultiplyByCostFactor multiplies a SizeEstimate by a cost factor and returns the CostEstimate with the +// nearest integer of the result, rounded up. +func (se SizeEstimate) MultiplyByCostFactor(costPerUnit float64) CostEstimate { + return CostEstimate{ + multiplyByCostFactor(se.Min, costPerUnit), + multiplyByCostFactor(se.Max, costPerUnit), + } +} + +// MultiplyByCost multiplies by the cost and returns the product. +// If multiply would result in an uint64 overflow, the result is math.MaxUint64. +func (se SizeEstimate) MultiplyByCost(cost CostEstimate) CostEstimate { + return CostEstimate{ + multiplyUint64NoOverflow(se.Min, cost.Min), + multiplyUint64NoOverflow(se.Max, cost.Max), + } +} + +// Union returns a SizeEstimate that encompasses both input the SizeEstimate. +func (se SizeEstimate) Union(size SizeEstimate) SizeEstimate { + result := se + if size.Min < result.Min { + result.Min = size.Min + } + if size.Max > result.Max { + result.Max = size.Max + } + return result +} + +// CostEstimate represents an estimated cost range and provides add and multiply operations +// that do not overflow. +type CostEstimate struct { + Min, Max uint64 +} + +// Add adds the costs and returns the sum. +// If add would result in an uint64 overflow for the min or max, the value is set to math.MaxUint64. +func (ce CostEstimate) Add(cost CostEstimate) CostEstimate { + return CostEstimate{ + addUint64NoOverflow(ce.Min, cost.Min), + addUint64NoOverflow(ce.Max, cost.Max), + } +} + +// Multiply multiplies by the cost and returns the product. +// If multiply would result in an uint64 overflow, the result is math.MaxUint64. +func (ce CostEstimate) Multiply(cost CostEstimate) CostEstimate { + return CostEstimate{ + multiplyUint64NoOverflow(ce.Min, cost.Min), + multiplyUint64NoOverflow(ce.Max, cost.Max), + } +} + +// MultiplyByCostFactor multiplies a CostEstimate by a cost factor and returns the CostEstimate with the +// nearest integer of the result, rounded up. +func (ce CostEstimate) MultiplyByCostFactor(costPerUnit float64) CostEstimate { + return CostEstimate{ + multiplyByCostFactor(ce.Min, costPerUnit), + multiplyByCostFactor(ce.Max, costPerUnit), + } +} + +// Union returns a CostEstimate that encompasses both input the CostEstimates. +func (ce CostEstimate) Union(size CostEstimate) CostEstimate { + result := ce + if size.Min < result.Min { + result.Min = size.Min + } + if size.Max > result.Max { + result.Max = size.Max + } + return result +} + +// addUint64NoOverflow adds non-negative ints. If the result is exceeds math.MaxUint64, math.MaxUint64 +// is returned. +func addUint64NoOverflow(x, y uint64) uint64 { + if y > 0 && x > math.MaxUint64-y { + return math.MaxUint64 + } + return x + y +} + +// multiplyUint64NoOverflow multiplies non-negative ints. If the result is exceeds math.MaxUint64, math.MaxUint64 +// is returned. +func multiplyUint64NoOverflow(x, y uint64) uint64 { + if x > 0 && y > 0 && x > math.MaxUint64/y { + return math.MaxUint64 + } + return x * y +} + +// multiplyByFactor multiplies an integer by a cost factor float and returns the nearest integer value, rounded up. +func multiplyByCostFactor(x uint64, y float64) uint64 { + xFloat := float64(x) + if xFloat > 0 && y > 0 && xFloat > math.MaxUint64/y { + return math.MaxUint64 + } + return uint64(math.Ceil(xFloat * y)) +} + +var ( + selectAndIdentCost = CostEstimate{Min: common.SelectAndIdentCost, Max: common.SelectAndIdentCost} + constCost = CostEstimate{Min: common.ConstCost, Max: common.ConstCost} + + createListBaseCost = CostEstimate{Min: common.ListCreateBaseCost, Max: common.ListCreateBaseCost} + createMapBaseCost = CostEstimate{Min: common.MapCreateBaseCost, Max: common.MapCreateBaseCost} + createMessageBaseCost = CostEstimate{Min: common.StructCreateBaseCost, Max: common.StructCreateBaseCost} +) + +type coster struct { + // exprPath maps from Expr Id to field path. + exprPath map[int64][]string + // iterRanges tracks the iterRange of each iterVar. + iterRanges iterRangeScopes + // computedSizes tracks the computed sizes of call results. + computedSizes map[int64]SizeEstimate + checkedExpr *exprpb.CheckedExpr + estimator CostEstimator +} + +// Use a stack of iterVar -> iterRange Expr Ids to handle shadowed variable names. +type iterRangeScopes map[string][]int64 + +func (vs iterRangeScopes) push(varName string, expr *exprpb.Expr) { + vs[varName] = append(vs[varName], expr.GetId()) +} + +func (vs iterRangeScopes) pop(varName string) { + varStack := vs[varName] + vs[varName] = varStack[:len(varStack)-1] +} + +func (vs iterRangeScopes) peek(varName string) (int64, bool) { + varStack := vs[varName] + if len(varStack) > 0 { + return varStack[len(varStack)-1], true + } + return 0, false +} + +// Cost estimates the cost of the parsed and type checked CEL expression. +func Cost(checker *exprpb.CheckedExpr, estimator CostEstimator) CostEstimate { + c := coster{ + checkedExpr: checker, + estimator: estimator, + exprPath: map[int64][]string{}, + iterRanges: map[string][]int64{}, + computedSizes: map[int64]SizeEstimate{}, + } + return c.cost(checker.GetExpr()) +} + +func (c *coster) cost(e *exprpb.Expr) CostEstimate { + if e == nil { + return CostEstimate{} + } + var cost CostEstimate + switch e.ExprKind.(type) { + case *exprpb.Expr_ConstExpr: + cost = constCost + case *exprpb.Expr_IdentExpr: + cost = c.costIdent(e) + case *exprpb.Expr_SelectExpr: + cost = c.costSelect(e) + case *exprpb.Expr_CallExpr: + cost = c.costCall(e) + case *exprpb.Expr_ListExpr: + cost = c.costCreateList(e) + case *exprpb.Expr_StructExpr: + cost = c.costCreateStruct(e) + case *exprpb.Expr_ComprehensionExpr: + cost = c.costComprehension(e) + default: + return CostEstimate{} + } + return cost +} + +func (c *coster) costIdent(e *exprpb.Expr) CostEstimate { + identExpr := e.GetIdentExpr() + + // build and track the field path + if iterRange, ok := c.iterRanges.peek(identExpr.GetName()); ok { + switch c.checkedExpr.TypeMap[iterRange].TypeKind.(type) { + case *exprpb.Type_ListType_: + c.addPath(e, append(c.exprPath[iterRange], "@items")) + case *exprpb.Type_MapType_: + c.addPath(e, append(c.exprPath[iterRange], "@keys")) + } + } else { + c.addPath(e, []string{identExpr.GetName()}) + } + + return selectAndIdentCost +} + +func (c *coster) costSelect(e *exprpb.Expr) CostEstimate { + sel := e.GetSelectExpr() + var sum CostEstimate + if sel.GetTestOnly() { + return sum + } + sum = sum.Add(c.cost(sel.GetOperand())) + targetType := c.getType(sel.GetOperand()) + switch kindOf(targetType) { + case kindMap, kindObject, kindTypeParam: + sum = sum.Add(selectAndIdentCost) + } + + // build and track the field path + c.addPath(e, append(c.getPath(sel.GetOperand()), sel.Field)) + + return sum +} + +func (c *coster) costCall(e *exprpb.Expr) CostEstimate { + call := e.GetCallExpr() + target := call.GetTarget() + args := call.GetArgs() + + var sum CostEstimate + + argTypes := make([]AstNode, len(args)) + argCosts := make([]CostEstimate, len(args)) + for i, arg := range args { + argCosts[i] = c.cost(arg) + argTypes[i] = c.newAstNode(arg) + } + + ref := c.checkedExpr.ReferenceMap[e.GetId()] + if ref == nil || len(ref.GetOverloadId()) == 0 { + return CostEstimate{} + } + var targetType AstNode + if target != nil { + if call.Target != nil { + sum = sum.Add(c.cost(call.GetTarget())) + targetType = c.newAstNode(call.GetTarget()) + } + } + // Pick a cost estimate range that covers all the overload cost estimation ranges + fnCost := CostEstimate{Min: uint64(math.MaxUint64), Max: 0} + var resultSize *SizeEstimate + for _, overload := range ref.GetOverloadId() { + overloadCost := c.functionCost(call.GetFunction(), overload, &targetType, argTypes, argCosts) + fnCost = fnCost.Union(overloadCost.CostEstimate) + if overloadCost.ResultSize != nil { + if resultSize == nil { + resultSize = overloadCost.ResultSize + } else { + size := resultSize.Union(*overloadCost.ResultSize) + resultSize = &size + } + } + // build and track the field path for index operations + switch overload { + case overloads.IndexList: + if len(args) > 0 { + c.addPath(e, append(c.getPath(args[0]), "@items")) + } + case overloads.IndexMap: + if len(args) > 0 { + c.addPath(e, append(c.getPath(args[0]), "@values")) + } + } + } + if resultSize != nil { + c.computedSizes[e.GetId()] = *resultSize + } + return sum.Add(fnCost) +} + +func (c *coster) costCreateList(e *exprpb.Expr) CostEstimate { + create := e.GetListExpr() + var sum CostEstimate + for _, e := range create.GetElements() { + sum = sum.Add(c.cost(e)) + } + return sum.Add(createListBaseCost) +} + +func (c *coster) costCreateStruct(e *exprpb.Expr) CostEstimate { + str := e.GetStructExpr() + if str.MessageName != "" { + return c.costCreateMessage(e) + } + return c.costCreateMap(e) +} + +func (c *coster) costCreateMap(e *exprpb.Expr) CostEstimate { + mapVal := e.GetStructExpr() + var sum CostEstimate + for _, ent := range mapVal.GetEntries() { + key := ent.GetMapKey() + sum = sum.Add(c.cost(key)) + + sum = sum.Add(c.cost(ent.GetValue())) + } + return sum.Add(createMapBaseCost) +} + +func (c *coster) costCreateMessage(e *exprpb.Expr) CostEstimate { + msgVal := e.GetStructExpr() + var sum CostEstimate + for _, ent := range msgVal.GetEntries() { + sum = sum.Add(c.cost(ent.GetValue())) + } + return sum.Add(createMessageBaseCost) +} + +func (c *coster) costComprehension(e *exprpb.Expr) CostEstimate { + comp := e.GetComprehensionExpr() + var sum CostEstimate + sum = sum.Add(c.cost(comp.GetIterRange())) + sum = sum.Add(c.cost(comp.GetAccuInit())) + + // Track the iterRange of each IterVar for field path construction + c.iterRanges.push(comp.GetIterVar(), comp.GetIterRange()) + loopCost := c.cost(comp.GetLoopCondition()) + stepCost := c.cost(comp.GetLoopStep()) + c.iterRanges.pop(comp.GetIterVar()) + sum = sum.Add(c.cost(comp.Result)) + rangeCnt := c.sizeEstimate(c.newAstNode(comp.GetIterRange())) + rangeCost := rangeCnt.MultiplyByCost(stepCost.Add(loopCost)) + sum = sum.Add(rangeCost) + + return sum +} + +func (c *coster) sizeEstimate(t AstNode) SizeEstimate { + if l := t.ComputedSize(); l != nil { + return *l + } + if l := c.estimator.EstimateSize(t); l != nil { + return *l + } + return SizeEstimate{Min: 0, Max: math.MaxUint64} +} + +func (c *coster) functionCost(function, overloadID string, target *AstNode, args []AstNode, argCosts []CostEstimate) CallEstimate { + argCostSum := func() CostEstimate { + var sum CostEstimate + for _, a := range argCosts { + sum = sum.Add(a) + } + return sum + } + + if est := c.estimator.EstimateCallCost(function, overloadID, target, args); est != nil { + callEst := *est + return CallEstimate{CostEstimate: callEst.Add(argCostSum())} + } + switch overloadID { + // O(n) functions + case overloads.StartsWithString, overloads.EndsWithString, overloads.StringToBytes, overloads.BytesToString: + if len(args) == 1 { + return CallEstimate{CostEstimate: c.sizeEstimate(args[0]).MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum())} + } + case overloads.InList: + // If a list is composed entirely of constant values this is O(1), but we don't account for that here. + // We just assume all list containment checks are O(n). + if len(args) == 2 { + return CallEstimate{CostEstimate: c.sizeEstimate(args[1]).MultiplyByCostFactor(1).Add(argCostSum())} + } + // O(nm) functions + case overloads.MatchesString: + // https://swtch.com/~rsc/regexp/regexp1.html applies to RE2 implementation supported by CEL + if target != nil && len(args) == 1 { + // Add one to string length for purposes of cost calculation to prevent product of string and regex to be 0 + // in case where string is empty but regex is still expensive. + strCost := c.sizeEstimate(*target).Add(SizeEstimate{Min: 1, Max: 1}).MultiplyByCostFactor(common.StringTraversalCostFactor) + // We don't know how many expressions are in the regex, just the string length (a huge + // improvement here would be to somehow get a count the number of expressions in the regex or + // how many states are in the regex state machine and use that to measure regex cost). + // For now, we're making a guess that each expression in a regex is typically at least 4 chars + // in length. + regexCost := c.sizeEstimate(args[0]).MultiplyByCostFactor(common.RegexStringLengthCostFactor) + return CallEstimate{CostEstimate: strCost.Multiply(regexCost).Add(argCostSum())} + } + case overloads.ContainsString: + if target != nil && len(args) == 1 { + strCost := c.sizeEstimate(*target).MultiplyByCostFactor(common.StringTraversalCostFactor) + substrCost := c.sizeEstimate(args[0]).MultiplyByCostFactor(common.StringTraversalCostFactor) + return CallEstimate{CostEstimate: strCost.Multiply(substrCost).Add(argCostSum())} + } + case overloads.LogicalOr, overloads.LogicalAnd: + lhs := argCosts[0] + rhs := argCosts[1] + // min cost is min of LHS for short circuited && or || + argCost := CostEstimate{Min: lhs.Min, Max: lhs.Add(rhs).Max} + return CallEstimate{CostEstimate: argCost} + case overloads.Conditional: + size := c.sizeEstimate(args[1]).Union(c.sizeEstimate(args[2])) + conditionalCost := argCosts[0] + ifTrueCost := argCosts[1] + ifFalseCost := argCosts[2] + argCost := conditionalCost.Add(ifTrueCost.Union(ifFalseCost)) + return CallEstimate{CostEstimate: argCost, ResultSize: &size} + case overloads.AddString, overloads.AddBytes, overloads.AddList: + if len(args) == 2 { + lhsSize := c.sizeEstimate(args[0]) + rhsSize := c.sizeEstimate(args[1]) + resultSize := lhsSize.Add(rhsSize) + switch overloadID { + case overloads.AddList: + // list concatenation is O(1), but we handle it here to track size + return CallEstimate{CostEstimate: CostEstimate{Min: 1, Max: 1}.Add(argCostSum()), ResultSize: &resultSize} + default: + return CallEstimate{CostEstimate: resultSize.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()), ResultSize: &resultSize} + } + } + case overloads.LessString, overloads.GreaterString, overloads.LessEqualsString, overloads.GreaterEqualsString, + overloads.LessBytes, overloads.GreaterBytes, overloads.LessEqualsBytes, overloads.GreaterEqualsBytes, + overloads.Equals, overloads.NotEquals: + lhsCost := c.sizeEstimate(args[0]) + rhsCost := c.sizeEstimate(args[1]) + min := uint64(0) + smallestMax := lhsCost.Max + if rhsCost.Max < smallestMax { + smallestMax = rhsCost.Max + } + if smallestMax > 0 { + min = 1 + } + // equality of 2 scalar values results in a cost of 1 + return CallEstimate{CostEstimate: CostEstimate{Min: min, Max: smallestMax}.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum())} + } + // O(1) functions + // See CostTracker.costCall for more details about O(1) cost calculations + + // Benchmarks suggest that most of the other operations take +/- 50% of a base cost unit + // which on an Intel xeon 2.20GHz CPU is 50ns. + return CallEstimate{CostEstimate: CostEstimate{Min: 1, Max: 1}.Add(argCostSum())} +} + +func (c *coster) getType(e *exprpb.Expr) *exprpb.Type { + return c.checkedExpr.TypeMap[e.GetId()] +} + +func (c *coster) getPath(e *exprpb.Expr) []string { + return c.exprPath[e.GetId()] +} + +func (c *coster) addPath(e *exprpb.Expr, path []string) { + c.exprPath[e.GetId()] = path +} + +func (c *coster) newAstNode(e *exprpb.Expr) *astNode { + path := c.getPath(e) + if len(path) > 0 && path[0] == parser.AccumulatorName { + // only provide paths to root vars; omit accumulator vars + path = nil + } + var derivedSize *SizeEstimate + if size, ok := c.computedSizes[e.GetId()]; ok { + derivedSize = &size + } + return &astNode{path: path, t: c.getType(e), expr: e, derivedSize: derivedSize} +} diff --git a/vendor/github.com/google/cel-go/checker/decls/decls.go b/vendor/github.com/google/cel-go/checker/decls/decls.go new file mode 100644 index 0000000000..88a99282d9 --- /dev/null +++ b/vendor/github.com/google/cel-go/checker/decls/decls.go @@ -0,0 +1,231 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Package decls provides helpers for creating variable and function declarations. +package decls + +import ( + emptypb "google.golang.org/protobuf/types/known/emptypb" + structpb "google.golang.org/protobuf/types/known/structpb" + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +var ( + // Error type used to communicate issues during type-checking. + Error = &exprpb.Type{ + TypeKind: &exprpb.Type_Error{ + Error: &emptypb.Empty{}}} + + // Dyn is a top-type used to represent any value. + Dyn = &exprpb.Type{ + TypeKind: &exprpb.Type_Dyn{ + Dyn: &emptypb.Empty{}}} +) + +// Commonly used types. +var ( + Bool = NewPrimitiveType(exprpb.Type_BOOL) + Bytes = NewPrimitiveType(exprpb.Type_BYTES) + Double = NewPrimitiveType(exprpb.Type_DOUBLE) + Int = NewPrimitiveType(exprpb.Type_INT64) + Null = &exprpb.Type{ + TypeKind: &exprpb.Type_Null{ + Null: structpb.NullValue_NULL_VALUE}} + String = NewPrimitiveType(exprpb.Type_STRING) + Uint = NewPrimitiveType(exprpb.Type_UINT64) +) + +// Well-known types. +// TODO: Replace with an abstract type registry. +var ( + Any = NewWellKnownType(exprpb.Type_ANY) + Duration = NewWellKnownType(exprpb.Type_DURATION) + Timestamp = NewWellKnownType(exprpb.Type_TIMESTAMP) +) + +// NewAbstractType creates an abstract type declaration which references a proto +// message name and may also include type parameters. +func NewAbstractType(name string, paramTypes ...*exprpb.Type) *exprpb.Type { + return &exprpb.Type{ + TypeKind: &exprpb.Type_AbstractType_{ + AbstractType: &exprpb.Type_AbstractType{ + Name: name, + ParameterTypes: paramTypes}}} +} + +// NewFunctionType creates a function invocation contract, typically only used +// by type-checking steps after overload resolution. +func NewFunctionType(resultType *exprpb.Type, + argTypes ...*exprpb.Type) *exprpb.Type { + return &exprpb.Type{ + TypeKind: &exprpb.Type_Function{ + Function: &exprpb.Type_FunctionType{ + ResultType: resultType, + ArgTypes: argTypes}}} +} + +// NewFunction creates a named function declaration with one or more overloads. +func NewFunction(name string, + overloads ...*exprpb.Decl_FunctionDecl_Overload) *exprpb.Decl { + return &exprpb.Decl{ + Name: name, + DeclKind: &exprpb.Decl_Function{ + Function: &exprpb.Decl_FunctionDecl{ + Overloads: overloads}}} +} + +// NewIdent creates a named identifier declaration with an optional literal +// value. +// +// Literal values are typically only associated with enum identifiers. +// +// Deprecated: Use NewVar or NewConst instead. +func NewIdent(name string, t *exprpb.Type, v *exprpb.Constant) *exprpb.Decl { + return &exprpb.Decl{ + Name: name, + DeclKind: &exprpb.Decl_Ident{ + Ident: &exprpb.Decl_IdentDecl{ + Type: t, + Value: v}}} +} + +// NewConst creates a constant identifier with a CEL constant literal value. +func NewConst(name string, t *exprpb.Type, v *exprpb.Constant) *exprpb.Decl { + return NewIdent(name, t, v) +} + +// NewVar creates a variable identifier. +func NewVar(name string, t *exprpb.Type) *exprpb.Decl { + return NewIdent(name, t, nil) +} + +// NewInstanceOverload creates a instance function overload contract. +// First element of argTypes is instance. +func NewInstanceOverload(id string, argTypes []*exprpb.Type, + resultType *exprpb.Type) *exprpb.Decl_FunctionDecl_Overload { + return &exprpb.Decl_FunctionDecl_Overload{ + OverloadId: id, + ResultType: resultType, + Params: argTypes, + IsInstanceFunction: true} +} + +// NewListType generates a new list with elements of a certain type. +func NewListType(elem *exprpb.Type) *exprpb.Type { + return &exprpb.Type{ + TypeKind: &exprpb.Type_ListType_{ + ListType: &exprpb.Type_ListType{ + ElemType: elem}}} +} + +// NewMapType generates a new map with typed keys and values. +func NewMapType(key *exprpb.Type, value *exprpb.Type) *exprpb.Type { + return &exprpb.Type{ + TypeKind: &exprpb.Type_MapType_{ + MapType: &exprpb.Type_MapType{ + KeyType: key, + ValueType: value}}} +} + +// NewObjectType creates an object type for a qualified type name. +func NewObjectType(typeName string) *exprpb.Type { + return &exprpb.Type{ + TypeKind: &exprpb.Type_MessageType{ + MessageType: typeName}} +} + +// NewOverload creates a function overload declaration which contains a unique +// overload id as well as the expected argument and result types. Overloads +// must be aggregated within a Function declaration. +func NewOverload(id string, argTypes []*exprpb.Type, + resultType *exprpb.Type) *exprpb.Decl_FunctionDecl_Overload { + return &exprpb.Decl_FunctionDecl_Overload{ + OverloadId: id, + ResultType: resultType, + Params: argTypes, + IsInstanceFunction: false} +} + +// NewParameterizedInstanceOverload creates a parametric function instance overload type. +func NewParameterizedInstanceOverload(id string, + argTypes []*exprpb.Type, + resultType *exprpb.Type, + typeParams []string) *exprpb.Decl_FunctionDecl_Overload { + return &exprpb.Decl_FunctionDecl_Overload{ + OverloadId: id, + ResultType: resultType, + Params: argTypes, + TypeParams: typeParams, + IsInstanceFunction: true} +} + +// NewParameterizedOverload creates a parametric function overload type. +func NewParameterizedOverload(id string, + argTypes []*exprpb.Type, + resultType *exprpb.Type, + typeParams []string) *exprpb.Decl_FunctionDecl_Overload { + return &exprpb.Decl_FunctionDecl_Overload{ + OverloadId: id, + ResultType: resultType, + Params: argTypes, + TypeParams: typeParams, + IsInstanceFunction: false} +} + +// NewPrimitiveType creates a type for a primitive value. See the var declarations +// for Int, Uint, etc. +func NewPrimitiveType(primitive exprpb.Type_PrimitiveType) *exprpb.Type { + return &exprpb.Type{ + TypeKind: &exprpb.Type_Primitive{ + Primitive: primitive}} +} + +// NewTypeType creates a new type designating a type. +func NewTypeType(nested *exprpb.Type) *exprpb.Type { + if nested == nil { + // must set the nested field for a valid oneof option + nested = &exprpb.Type{} + } + return &exprpb.Type{ + TypeKind: &exprpb.Type_Type{ + Type: nested}} +} + +// NewTypeParamType creates a type corresponding to a named, contextual parameter. +func NewTypeParamType(name string) *exprpb.Type { + return &exprpb.Type{ + TypeKind: &exprpb.Type_TypeParam{ + TypeParam: name}} +} + +// NewWellKnownType creates a type corresponding to a protobuf well-known type +// value. +func NewWellKnownType(wellKnown exprpb.Type_WellKnownType) *exprpb.Type { + return &exprpb.Type{ + TypeKind: &exprpb.Type_WellKnown{ + WellKnown: wellKnown}} +} + +// NewWrapperType creates a wrapped primitive type instance. Wrapped types +// are roughly equivalent to a nullable, or optionally valued type. +func NewWrapperType(wrapped *exprpb.Type) *exprpb.Type { + primitive := wrapped.GetPrimitive() + if primitive == exprpb.Type_PRIMITIVE_TYPE_UNSPECIFIED { + // TODO: return an error + panic("Wrapped type must be a primitive") + } + return &exprpb.Type{ + TypeKind: &exprpb.Type_Wrapper{ + Wrapper: primitive}} +} diff --git a/vendor/github.com/google/cel-go/checker/decls/scopes.go b/vendor/github.com/google/cel-go/checker/decls/scopes.go new file mode 100644 index 0000000000..608bca3e53 --- /dev/null +++ b/vendor/github.com/google/cel-go/checker/decls/scopes.go @@ -0,0 +1,145 @@ +// Copyright 2018 Google LLC +// +// 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. + +package decls + +import exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" + +// Scopes represents nested Decl sets where the Scopes value contains a Groups containing all +// identifiers in scope and an optional parent representing outer scopes. +// Each Groups value is a mapping of names to Decls in the ident and function namespaces. +// Lookups are performed such that bindings in inner scopes shadow those in outer scopes. +type Scopes struct { + parent *Scopes + scopes *Group +} + +// NewScopes creates a new, empty Scopes. +// Some operations can't be safely performed until a Group is added with Push. +func NewScopes() *Scopes { + return &Scopes{ + scopes: newGroup(), + } +} + +// Copy creates a copy of the current Scopes values, including a copy of its parent if non-nil. +func (s *Scopes) Copy() *Scopes { + cpy := NewScopes() + if s == nil { + return cpy + } + if s.parent != nil { + cpy.parent = s.parent.Copy() + } + cpy.scopes = s.scopes.copy() + return cpy +} + +// Push creates a new Scopes value which references the current Scope as its parent. +func (s *Scopes) Push() *Scopes { + return &Scopes{ + parent: s, + scopes: newGroup(), + } +} + +// Pop returns the parent Scopes value for the current scope, or the current scope if the parent +// is nil. +func (s *Scopes) Pop() *Scopes { + if s.parent != nil { + return s.parent + } + // TODO: Consider whether this should be an error / panic. + return s +} + +// AddIdent adds the ident Decl in the current scope. +// Note: If the name collides with an existing identifier in the scope, the Decl is overwritten. +func (s *Scopes) AddIdent(decl *exprpb.Decl) { + s.scopes.idents[decl.Name] = decl +} + +// FindIdent finds the first ident Decl with a matching name in Scopes, or nil if one cannot be +// found. +// Note: The search is performed from innermost to outermost. +func (s *Scopes) FindIdent(name string) *exprpb.Decl { + if ident, found := s.scopes.idents[name]; found { + return ident + } + if s.parent != nil { + return s.parent.FindIdent(name) + } + return nil +} + +// FindIdentInScope finds the first ident Decl with a matching name in the current Scopes value, or +// nil if one does not exist. +// Note: The search is only performed on the current scope and does not search outer scopes. +func (s *Scopes) FindIdentInScope(name string) *exprpb.Decl { + if ident, found := s.scopes.idents[name]; found { + return ident + } + return nil +} + +// SetFunction adds the function Decl to the current scope. +// Note: Any previous entry for a function in the current scope with the same name is overwritten. +func (s *Scopes) SetFunction(fn *exprpb.Decl) { + s.scopes.functions[fn.Name] = fn +} + +// FindFunction finds the first function Decl with a matching name in Scopes. +// The search is performed from innermost to outermost. +// Returns nil if no such function in Scopes. +func (s *Scopes) FindFunction(name string) *exprpb.Decl { + if fn, found := s.scopes.functions[name]; found { + return fn + } + if s.parent != nil { + return s.parent.FindFunction(name) + } + return nil +} + +// Group is a set of Decls that is pushed on or popped off a Scopes as a unit. +// Contains separate namespaces for identifier and function Decls. +// (Should be named "Scope" perhaps?) +type Group struct { + idents map[string]*exprpb.Decl + functions map[string]*exprpb.Decl +} + +// copy creates a new Group instance with a shallow copy of the variables and functions. +// If callers need to mutate the exprpb.Decl definitions for a Function, they should copy-on-write. +func (g *Group) copy() *Group { + cpy := &Group{ + idents: make(map[string]*exprpb.Decl, len(g.idents)), + functions: make(map[string]*exprpb.Decl, len(g.functions)), + } + for n, id := range g.idents { + cpy.idents[n] = id + } + for n, fn := range g.functions { + cpy.functions[n] = fn + } + return cpy +} + +// newGroup creates a new Group with empty maps for identifiers and functions. +func newGroup() *Group { + return &Group{ + idents: make(map[string]*exprpb.Decl), + functions: make(map[string]*exprpb.Decl), + } +} diff --git a/vendor/github.com/google/cel-go/checker/env.go b/vendor/github.com/google/cel-go/checker/env.go new file mode 100644 index 0000000000..c9d0614e30 --- /dev/null +++ b/vendor/github.com/google/cel-go/checker/env.go @@ -0,0 +1,394 @@ +// Copyright 2018 Google LLC +// +// 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. + +package checker + +import ( + "fmt" + "strings" + + "google.golang.org/protobuf/proto" + + "github.com/google/cel-go/checker/decls" + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/pb" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/parser" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +type aggregateLiteralElementType int + +const ( + dynElementType aggregateLiteralElementType = iota + homogenousElementType aggregateLiteralElementType = 1 << iota +) + +var ( + crossTypeNumericComparisonOverloads = map[string]struct{}{ + // double <-> int | uint + overloads.LessDoubleInt64: {}, + overloads.LessDoubleUint64: {}, + overloads.LessEqualsDoubleInt64: {}, + overloads.LessEqualsDoubleUint64: {}, + overloads.GreaterDoubleInt64: {}, + overloads.GreaterDoubleUint64: {}, + overloads.GreaterEqualsDoubleInt64: {}, + overloads.GreaterEqualsDoubleUint64: {}, + // int <-> double | uint + overloads.LessInt64Double: {}, + overloads.LessInt64Uint64: {}, + overloads.LessEqualsInt64Double: {}, + overloads.LessEqualsInt64Uint64: {}, + overloads.GreaterInt64Double: {}, + overloads.GreaterInt64Uint64: {}, + overloads.GreaterEqualsInt64Double: {}, + overloads.GreaterEqualsInt64Uint64: {}, + // uint <-> double | int + overloads.LessUint64Double: {}, + overloads.LessUint64Int64: {}, + overloads.LessEqualsUint64Double: {}, + overloads.LessEqualsUint64Int64: {}, + overloads.GreaterUint64Double: {}, + overloads.GreaterUint64Int64: {}, + overloads.GreaterEqualsUint64Double: {}, + overloads.GreaterEqualsUint64Int64: {}, + } +) + +// Env is the environment for type checking. +// +// The Env is comprised of a container, type provider, declarations, and other related objects +// which can be used to assist with type-checking. +type Env struct { + container *containers.Container + provider ref.TypeProvider + declarations *decls.Scopes + aggLitElemType aggregateLiteralElementType + filteredOverloadIDs map[string]struct{} +} + +// NewEnv returns a new *Env with the given parameters. +func NewEnv(container *containers.Container, provider ref.TypeProvider, opts ...Option) (*Env, error) { + declarations := decls.NewScopes() + declarations.Push() + + envOptions := &options{} + for _, opt := range opts { + if err := opt(envOptions); err != nil { + return nil, err + } + } + aggLitElemType := dynElementType + if envOptions.homogeneousAggregateLiterals { + aggLitElemType = homogenousElementType + } + filteredOverloadIDs := crossTypeNumericComparisonOverloads + if envOptions.crossTypeNumericComparisons { + filteredOverloadIDs = make(map[string]struct{}) + } + if envOptions.validatedDeclarations != nil { + declarations = envOptions.validatedDeclarations.Copy() + } + return &Env{ + container: container, + provider: provider, + declarations: declarations, + aggLitElemType: aggLitElemType, + filteredOverloadIDs: filteredOverloadIDs, + }, nil +} + +// Add adds new Decl protos to the Env. +// Returns an error for identifier redeclarations. +func (e *Env) Add(decls ...*exprpb.Decl) error { + errMsgs := make([]errorMsg, 0) + for _, decl := range decls { + switch decl.DeclKind.(type) { + case *exprpb.Decl_Ident: + errMsgs = append(errMsgs, e.addIdent(sanitizeIdent(decl))) + case *exprpb.Decl_Function: + errMsgs = append(errMsgs, e.setFunction(sanitizeFunction(decl))...) + } + } + return formatError(errMsgs) +} + +// LookupIdent returns a Decl proto for typeName as an identifier in the Env. +// Returns nil if no such identifier is found in the Env. +func (e *Env) LookupIdent(name string) *exprpb.Decl { + for _, candidate := range e.container.ResolveCandidateNames(name) { + if ident := e.declarations.FindIdent(candidate); ident != nil { + return ident + } + + // Next try to import the name as a reference to a message type. If found, + // the declaration is added to the outest (global) scope of the + // environment, so next time we can access it faster. + if t, found := e.provider.FindType(candidate); found { + decl := decls.NewVar(candidate, t) + e.declarations.AddIdent(decl) + return decl + } + + // Next try to import this as an enum value by splitting the name in a type prefix and + // the enum inside. + if enumValue := e.provider.EnumValue(candidate); enumValue.Type() != types.ErrType { + decl := decls.NewIdent(candidate, + decls.Int, + &exprpb.Constant{ + ConstantKind: &exprpb.Constant_Int64Value{ + Int64Value: int64(enumValue.(types.Int))}}) + e.declarations.AddIdent(decl) + return decl + } + } + return nil +} + +// LookupFunction returns a Decl proto for typeName as a function in env. +// Returns nil if no such function is found in env. +func (e *Env) LookupFunction(name string) *exprpb.Decl { + for _, candidate := range e.container.ResolveCandidateNames(name) { + if fn := e.declarations.FindFunction(candidate); fn != nil { + return fn + } + } + return nil +} + +// addOverload adds overload to function declaration f. +// Returns one or more errorMsg values if the overload overlaps with an existing overload or macro. +func (e *Env) addOverload(f *exprpb.Decl, overload *exprpb.Decl_FunctionDecl_Overload) []errorMsg { + errMsgs := make([]errorMsg, 0) + function := f.GetFunction() + emptyMappings := newMapping() + overloadFunction := decls.NewFunctionType(overload.GetResultType(), + overload.GetParams()...) + overloadErased := substitute(emptyMappings, overloadFunction, true) + for _, existing := range function.GetOverloads() { + existingFunction := decls.NewFunctionType(existing.GetResultType(), + existing.GetParams()...) + existingErased := substitute(emptyMappings, existingFunction, true) + overlap := isAssignable(emptyMappings, overloadErased, existingErased) != nil || + isAssignable(emptyMappings, existingErased, overloadErased) != nil + if overlap && + overload.GetIsInstanceFunction() == existing.GetIsInstanceFunction() { + errMsgs = append(errMsgs, + overlappingOverloadError(f.Name, + overload.GetOverloadId(), overloadFunction, + existing.GetOverloadId(), existingFunction)) + } + } + + for _, macro := range parser.AllMacros { + if macro.Function() == f.Name && + macro.IsReceiverStyle() == overload.GetIsInstanceFunction() && + macro.ArgCount() == len(overload.GetParams()) { + errMsgs = append(errMsgs, overlappingMacroError(f.Name, macro.ArgCount())) + } + } + if len(errMsgs) > 0 { + return errMsgs + } + function.Overloads = append(function.GetOverloads(), overload) + return errMsgs +} + +// setFunction adds the function Decl to the Env. +// Adds a function decl if one doesn't already exist, then adds all overloads from the Decl. +// If overload overlaps with an existing overload, adds to the errors in the Env instead. +func (e *Env) setFunction(decl *exprpb.Decl) []errorMsg { + current := e.declarations.FindFunction(decl.Name) + if current == nil { + //Add the function declaration without overloads and check the overloads below. + current = decls.NewFunction(decl.Name) + } else { + // Copy on write since we don't know where this original definition came from. + current = proto.Clone(current).(*exprpb.Decl) + } + e.declarations.SetFunction(current) + + errorMsgs := make([]errorMsg, 0) + for _, overload := range decl.GetFunction().GetOverloads() { + errorMsgs = append(errorMsgs, e.addOverload(current, overload)...) + } + return errorMsgs +} + +// addIdent adds the Decl to the declarations in the Env. +// Returns a non-empty errorMsg if the identifier is already declared in the scope. +func (e *Env) addIdent(decl *exprpb.Decl) errorMsg { + current := e.declarations.FindIdentInScope(decl.Name) + if current != nil { + return overlappingIdentifierError(decl.Name) + } + e.declarations.AddIdent(decl) + return "" +} + +// isOverloadDisabled returns whether the overloadID is disabled in the current environment. +func (e *Env) isOverloadDisabled(overloadID string) bool { + _, found := e.filteredOverloadIDs[overloadID] + return found +} + +// sanitizeFunction replaces well-known types referenced by message name with their equivalent +// CEL built-in type instances. +func sanitizeFunction(decl *exprpb.Decl) *exprpb.Decl { + fn := decl.GetFunction() + // Determine whether the declaration requires replacements from proto-based message type + // references to well-known CEL type references. + var needsSanitizing bool + for _, o := range fn.GetOverloads() { + if isObjectWellKnownType(o.GetResultType()) { + needsSanitizing = true + break + } + for _, p := range o.GetParams() { + if isObjectWellKnownType(p) { + needsSanitizing = true + break + } + } + } + + // Early return if the declaration requires no modification. + if !needsSanitizing { + return decl + } + + // Sanitize all of the overloads if any overload requires an update to its type references. + overloads := make([]*exprpb.Decl_FunctionDecl_Overload, len(fn.GetOverloads())) + for i, o := range fn.GetOverloads() { + rt := o.GetResultType() + if isObjectWellKnownType(rt) { + rt = getObjectWellKnownType(rt) + } + params := make([]*exprpb.Type, len(o.GetParams())) + copy(params, o.GetParams()) + for j, p := range params { + if isObjectWellKnownType(p) { + params[j] = getObjectWellKnownType(p) + } + } + // If sanitized, replace the overload definition. + if o.IsInstanceFunction { + overloads[i] = + decls.NewInstanceOverload(o.GetOverloadId(), params, rt) + } else { + overloads[i] = + decls.NewOverload(o.GetOverloadId(), params, rt) + } + } + return decls.NewFunction(decl.GetName(), overloads...) +} + +// sanitizeIdent replaces the identifier's well-known types referenced by message name with +// references to CEL built-in type instances. +func sanitizeIdent(decl *exprpb.Decl) *exprpb.Decl { + id := decl.GetIdent() + t := id.GetType() + if !isObjectWellKnownType(t) { + return decl + } + return decls.NewIdent(decl.GetName(), getObjectWellKnownType(t), id.GetValue()) +} + +// isObjectWellKnownType returns true if the input type is an OBJECT type with a message name +// that corresponds the message name of a built-in CEL type. +func isObjectWellKnownType(t *exprpb.Type) bool { + if kindOf(t) != kindObject { + return false + } + _, found := pb.CheckedWellKnowns[t.GetMessageType()] + return found +} + +// getObjectWellKnownType returns the built-in CEL type declaration for input type's message name. +func getObjectWellKnownType(t *exprpb.Type) *exprpb.Type { + return pb.CheckedWellKnowns[t.GetMessageType()] +} + +// validatedDeclarations returns a reference to the validated variable and function declaration scope stack. +// must be copied before use. +func (e *Env) validatedDeclarations() *decls.Scopes { + return e.declarations +} + +// enterScope creates a new Env instance with a new innermost declaration scope. +func (e *Env) enterScope() *Env { + childDecls := e.declarations.Push() + return &Env{ + declarations: childDecls, + container: e.container, + provider: e.provider, + aggLitElemType: e.aggLitElemType, + } +} + +// exitScope creates a new Env instance with the nearest outer declaration scope. +func (e *Env) exitScope() *Env { + parentDecls := e.declarations.Pop() + return &Env{ + declarations: parentDecls, + container: e.container, + provider: e.provider, + aggLitElemType: e.aggLitElemType, + } +} + +// errorMsg is a type alias meant to represent error-based return values which +// may be accumulated into an error at a later point in execution. +type errorMsg string + +func overlappingIdentifierError(name string) errorMsg { + return errorMsg(fmt.Sprintf("overlapping identifier for name '%s'", name)) +} + +func overlappingOverloadError(name string, + overloadID1 string, f1 *exprpb.Type, + overloadID2 string, f2 *exprpb.Type) errorMsg { + return errorMsg(fmt.Sprintf( + "overlapping overload for name '%s' (type '%s' with overloadId: '%s' "+ + "cannot be distinguished from '%s' with overloadId: '%s')", + name, + FormatCheckedType(f1), + overloadID1, + FormatCheckedType(f2), + overloadID2)) +} + +func overlappingMacroError(name string, argCount int) errorMsg { + return errorMsg(fmt.Sprintf( + "overlapping macro for name '%s' with %d args", name, argCount)) +} + +func formatError(errMsgs []errorMsg) error { + errStrs := make([]string, 0) + if len(errMsgs) > 0 { + for i := 0; i < len(errMsgs); i++ { + if errMsgs[i] != "" { + errStrs = append(errStrs, string(errMsgs[i])) + } + } + } + if len(errStrs) > 0 { + return fmt.Errorf("%s", strings.Join(errStrs, "\n")) + } + return nil +} diff --git a/vendor/github.com/google/cel-go/checker/errors.go b/vendor/github.com/google/cel-go/checker/errors.go new file mode 100644 index 0000000000..0014f9abe1 --- /dev/null +++ b/vendor/github.com/google/cel-go/checker/errors.go @@ -0,0 +1,96 @@ +// Copyright 2018 Google LLC +// +// 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. + +package checker + +import ( + "github.com/google/cel-go/common" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +// typeErrors is a specialization of Errors. +type typeErrors struct { + *common.Errors +} + +func (e *typeErrors) undeclaredReference(l common.Location, container string, name string) { + e.ReportError(l, "undeclared reference to '%s' (in container '%s')", name, container) +} + +func (e *typeErrors) typeDoesNotSupportFieldSelection(l common.Location, t *exprpb.Type) { + e.ReportError(l, "type '%s' does not support field selection", t) +} + +func (e *typeErrors) undefinedField(l common.Location, field string) { + e.ReportError(l, "undefined field '%s'", field) +} + +func (e *typeErrors) noMatchingOverload(l common.Location, name string, args []*exprpb.Type, isInstance bool) { + signature := formatFunction(nil, args, isInstance) + e.ReportError(l, "found no matching overload for '%s' applied to '%s'", name, signature) +} + +func (e *typeErrors) notAType(l common.Location, t *exprpb.Type) { + e.ReportError(l, "'%s(%v)' is not a type", FormatCheckedType(t), t) +} + +func (e *typeErrors) notAMessageType(l common.Location, t *exprpb.Type) { + e.ReportError(l, "'%s' is not a message type", FormatCheckedType(t)) +} + +func (e *typeErrors) fieldTypeMismatch(l common.Location, name string, field *exprpb.Type, value *exprpb.Type) { + e.ReportError(l, "expected type of field '%s' is '%s' but provided type is '%s'", + name, FormatCheckedType(field), FormatCheckedType(value)) +} + +func (e *typeErrors) unexpectedFailedResolution(l common.Location, typeName string) { + e.ReportError(l, "[internal] unexpected failed resolution of '%s'", typeName) +} + +func (e *typeErrors) notAComprehensionRange(l common.Location, t *exprpb.Type) { + e.ReportError(l, "expression of type '%s' cannot be range of a comprehension (must be list, map, or dynamic)", + FormatCheckedType(t)) +} + +func (e *typeErrors) typeMismatch(l common.Location, expected *exprpb.Type, actual *exprpb.Type) { + e.ReportError(l, "expected type '%s' but found '%s'", + FormatCheckedType(expected), FormatCheckedType(actual)) +} + +func formatFunction(resultType *exprpb.Type, argTypes []*exprpb.Type, isInstance bool) string { + result := "" + if isInstance { + target := argTypes[0] + argTypes = argTypes[1:] + + result += FormatCheckedType(target) + result += "." + } + + result += "(" + for i, arg := range argTypes { + if i > 0 { + result += ", " + } + result += FormatCheckedType(arg) + } + result += ")" + if resultType != nil { + result += " -> " + result += FormatCheckedType(resultType) + } + + return result +} diff --git a/vendor/github.com/google/cel-go/checker/mapping.go b/vendor/github.com/google/cel-go/checker/mapping.go new file mode 100644 index 0000000000..fbc55a28d9 --- /dev/null +++ b/vendor/github.com/google/cel-go/checker/mapping.go @@ -0,0 +1,49 @@ +// Copyright 2018 Google LLC +// +// 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. + +package checker + +import ( + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +type mapping struct { + mapping map[string]*exprpb.Type +} + +func newMapping() *mapping { + return &mapping{ + mapping: make(map[string]*exprpb.Type), + } +} + +func (m *mapping) add(from *exprpb.Type, to *exprpb.Type) { + m.mapping[typeKey(from)] = to +} + +func (m *mapping) find(from *exprpb.Type) (*exprpb.Type, bool) { + if r, found := m.mapping[typeKey(from)]; found { + return r, found + } + return nil, false +} + +func (m *mapping) copy() *mapping { + c := newMapping() + + for k, v := range m.mapping { + c.mapping[k] = v + } + return c +} diff --git a/vendor/github.com/google/cel-go/checker/options.go b/vendor/github.com/google/cel-go/checker/options.go new file mode 100644 index 0000000000..cded00a660 --- /dev/null +++ b/vendor/github.com/google/cel-go/checker/options.go @@ -0,0 +1,53 @@ +// Copyright 2022 Google LLC +// +// 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. + +package checker + +import "github.com/google/cel-go/checker/decls" + +type options struct { + crossTypeNumericComparisons bool + homogeneousAggregateLiterals bool + validatedDeclarations *decls.Scopes +} + +// Option is a functional option for configuring the type-checker +type Option func(*options) error + +// CrossTypeNumericComparisons toggles type-checker support for numeric comparisons across type +// See https://github.com/google/cel-spec/wiki/proposal-210 for more details. +func CrossTypeNumericComparisons(enabled bool) Option { + return func(opts *options) error { + opts.crossTypeNumericComparisons = enabled + return nil + } +} + +// HomogeneousAggregateLiterals toggles support for constructing lists and maps whose elements all +// have the same type. +func HomogeneousAggregateLiterals(enabled bool) Option { + return func(opts *options) error { + opts.homogeneousAggregateLiterals = enabled + return nil + } +} + +// ValidatedDeclarations provides a references to validated declarations which will be copied +// into new checker instances. +func ValidatedDeclarations(env *Env) Option { + return func(opts *options) error { + opts.validatedDeclarations = env.validatedDeclarations() + return nil + } +} diff --git a/vendor/github.com/google/cel-go/checker/printer.go b/vendor/github.com/google/cel-go/checker/printer.go new file mode 100644 index 0000000000..15c25ecc68 --- /dev/null +++ b/vendor/github.com/google/cel-go/checker/printer.go @@ -0,0 +1,71 @@ +// Copyright 2018 Google LLC +// +// 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. + +package checker + +import ( + "github.com/google/cel-go/common/debug" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +type semanticAdorner struct { + checks *exprpb.CheckedExpr +} + +var _ debug.Adorner = &semanticAdorner{} + +func (a *semanticAdorner) GetMetadata(elem interface{}) string { + result := "" + e, isExpr := elem.(*exprpb.Expr) + if !isExpr { + return result + } + t := a.checks.TypeMap[e.Id] + if t != nil { + result += "~" + result += FormatCheckedType(t) + } + + switch e.ExprKind.(type) { + case *exprpb.Expr_IdentExpr, + *exprpb.Expr_CallExpr, + *exprpb.Expr_StructExpr, + *exprpb.Expr_SelectExpr: + if ref, found := a.checks.ReferenceMap[e.Id]; found { + if len(ref.GetOverloadId()) == 0 { + result += "^" + ref.Name + } else { + for i, overload := range ref.OverloadId { + if i == 0 { + result += "^" + } else { + result += "|" + } + result += overload + } + } + } + } + + return result +} + +// Print returns a string representation of the Expr message, +// annotated with types from the CheckedExpr. The Expr must +// be a sub-expression embedded in the CheckedExpr. +func Print(e *exprpb.Expr, checks *exprpb.CheckedExpr) string { + a := &semanticAdorner{checks: checks} + return debug.ToAdornedDebugString(e, a) +} diff --git a/vendor/github.com/google/cel-go/checker/standard.go b/vendor/github.com/google/cel-go/checker/standard.go new file mode 100644 index 0000000000..5b48a9046a --- /dev/null +++ b/vendor/github.com/google/cel-go/checker/standard.go @@ -0,0 +1,492 @@ +// Copyright 2018 Google LLC +// +// 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. + +package checker + +import ( + "github.com/google/cel-go/checker/decls" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +var ( + standardDeclarations []*exprpb.Decl +) + +func init() { + // Some shortcuts we use when building declarations. + paramA := decls.NewTypeParamType("A") + typeParamAList := []string{"A"} + listOfA := decls.NewListType(paramA) + paramB := decls.NewTypeParamType("B") + typeParamABList := []string{"A", "B"} + mapOfAB := decls.NewMapType(paramA, paramB) + + var idents []*exprpb.Decl + for _, t := range []*exprpb.Type{ + decls.Int, decls.Uint, decls.Bool, + decls.Double, decls.Bytes, decls.String} { + idents = append(idents, + decls.NewVar(FormatCheckedType(t), decls.NewTypeType(t))) + } + idents = append(idents, + decls.NewVar("list", decls.NewTypeType(listOfA)), + decls.NewVar("map", decls.NewTypeType(mapOfAB)), + decls.NewVar("null_type", decls.NewTypeType(decls.Null)), + decls.NewVar("type", decls.NewTypeType(decls.NewTypeType(nil)))) + + standardDeclarations = append(standardDeclarations, idents...) + standardDeclarations = append(standardDeclarations, []*exprpb.Decl{ + // Booleans + decls.NewFunction(operators.Conditional, + decls.NewParameterizedOverload(overloads.Conditional, + []*exprpb.Type{decls.Bool, paramA, paramA}, paramA, + typeParamAList)), + + decls.NewFunction(operators.LogicalAnd, + decls.NewOverload(overloads.LogicalAnd, + []*exprpb.Type{decls.Bool, decls.Bool}, decls.Bool)), + + decls.NewFunction(operators.LogicalOr, + decls.NewOverload(overloads.LogicalOr, + []*exprpb.Type{decls.Bool, decls.Bool}, decls.Bool)), + + decls.NewFunction(operators.LogicalNot, + decls.NewOverload(overloads.LogicalNot, + []*exprpb.Type{decls.Bool}, decls.Bool)), + + decls.NewFunction(operators.NotStrictlyFalse, + decls.NewOverload(overloads.NotStrictlyFalse, + []*exprpb.Type{decls.Bool}, decls.Bool)), + + decls.NewFunction(operators.Equals, + decls.NewParameterizedOverload(overloads.Equals, + []*exprpb.Type{paramA, paramA}, decls.Bool, + typeParamAList)), + + decls.NewFunction(operators.NotEquals, + decls.NewParameterizedOverload(overloads.NotEquals, + []*exprpb.Type{paramA, paramA}, decls.Bool, + typeParamAList)), + + // Algebra. + + decls.NewFunction(operators.Subtract, + decls.NewOverload(overloads.SubtractInt64, + []*exprpb.Type{decls.Int, decls.Int}, decls.Int), + decls.NewOverload(overloads.SubtractUint64, + []*exprpb.Type{decls.Uint, decls.Uint}, decls.Uint), + decls.NewOverload(overloads.SubtractDouble, + []*exprpb.Type{decls.Double, decls.Double}, decls.Double), + decls.NewOverload(overloads.SubtractTimestampTimestamp, + []*exprpb.Type{decls.Timestamp, decls.Timestamp}, decls.Duration), + decls.NewOverload(overloads.SubtractTimestampDuration, + []*exprpb.Type{decls.Timestamp, decls.Duration}, decls.Timestamp), + decls.NewOverload(overloads.SubtractDurationDuration, + []*exprpb.Type{decls.Duration, decls.Duration}, decls.Duration)), + + decls.NewFunction(operators.Multiply, + decls.NewOverload(overloads.MultiplyInt64, + []*exprpb.Type{decls.Int, decls.Int}, decls.Int), + decls.NewOverload(overloads.MultiplyUint64, + []*exprpb.Type{decls.Uint, decls.Uint}, decls.Uint), + decls.NewOverload(overloads.MultiplyDouble, + []*exprpb.Type{decls.Double, decls.Double}, decls.Double)), + + decls.NewFunction(operators.Divide, + decls.NewOverload(overloads.DivideInt64, + []*exprpb.Type{decls.Int, decls.Int}, decls.Int), + decls.NewOverload(overloads.DivideUint64, + []*exprpb.Type{decls.Uint, decls.Uint}, decls.Uint), + decls.NewOverload(overloads.DivideDouble, + []*exprpb.Type{decls.Double, decls.Double}, decls.Double)), + + decls.NewFunction(operators.Modulo, + decls.NewOverload(overloads.ModuloInt64, + []*exprpb.Type{decls.Int, decls.Int}, decls.Int), + decls.NewOverload(overloads.ModuloUint64, + []*exprpb.Type{decls.Uint, decls.Uint}, decls.Uint)), + + decls.NewFunction(operators.Add, + decls.NewOverload(overloads.AddInt64, + []*exprpb.Type{decls.Int, decls.Int}, decls.Int), + decls.NewOverload(overloads.AddUint64, + []*exprpb.Type{decls.Uint, decls.Uint}, decls.Uint), + decls.NewOverload(overloads.AddDouble, + []*exprpb.Type{decls.Double, decls.Double}, decls.Double), + decls.NewOverload(overloads.AddString, + []*exprpb.Type{decls.String, decls.String}, decls.String), + decls.NewOverload(overloads.AddBytes, + []*exprpb.Type{decls.Bytes, decls.Bytes}, decls.Bytes), + decls.NewParameterizedOverload(overloads.AddList, + []*exprpb.Type{listOfA, listOfA}, listOfA, + typeParamAList), + decls.NewOverload(overloads.AddTimestampDuration, + []*exprpb.Type{decls.Timestamp, decls.Duration}, decls.Timestamp), + decls.NewOverload(overloads.AddDurationTimestamp, + []*exprpb.Type{decls.Duration, decls.Timestamp}, decls.Timestamp), + decls.NewOverload(overloads.AddDurationDuration, + []*exprpb.Type{decls.Duration, decls.Duration}, decls.Duration)), + + decls.NewFunction(operators.Negate, + decls.NewOverload(overloads.NegateInt64, + []*exprpb.Type{decls.Int}, decls.Int), + decls.NewOverload(overloads.NegateDouble, + []*exprpb.Type{decls.Double}, decls.Double)), + + // Index. + + decls.NewFunction(operators.Index, + decls.NewParameterizedOverload(overloads.IndexList, + []*exprpb.Type{listOfA, decls.Int}, paramA, + typeParamAList), + decls.NewParameterizedOverload(overloads.IndexMap, + []*exprpb.Type{mapOfAB, paramA}, paramB, + typeParamABList)), + + // Collections. + + decls.NewFunction(overloads.Size, + decls.NewInstanceOverload(overloads.SizeStringInst, + []*exprpb.Type{decls.String}, decls.Int), + decls.NewInstanceOverload(overloads.SizeBytesInst, + []*exprpb.Type{decls.Bytes}, decls.Int), + decls.NewParameterizedInstanceOverload(overloads.SizeListInst, + []*exprpb.Type{listOfA}, decls.Int, typeParamAList), + decls.NewParameterizedInstanceOverload(overloads.SizeMapInst, + []*exprpb.Type{mapOfAB}, decls.Int, typeParamABList), + decls.NewOverload(overloads.SizeString, + []*exprpb.Type{decls.String}, decls.Int), + decls.NewOverload(overloads.SizeBytes, + []*exprpb.Type{decls.Bytes}, decls.Int), + decls.NewParameterizedOverload(overloads.SizeList, + []*exprpb.Type{listOfA}, decls.Int, typeParamAList), + decls.NewParameterizedOverload(overloads.SizeMap, + []*exprpb.Type{mapOfAB}, decls.Int, typeParamABList)), + + decls.NewFunction(operators.In, + decls.NewParameterizedOverload(overloads.InList, + []*exprpb.Type{paramA, listOfA}, decls.Bool, + typeParamAList), + decls.NewParameterizedOverload(overloads.InMap, + []*exprpb.Type{paramA, mapOfAB}, decls.Bool, + typeParamABList)), + + // Deprecated 'in()' function. + + decls.NewFunction(overloads.DeprecatedIn, + decls.NewParameterizedOverload(overloads.InList, + []*exprpb.Type{paramA, listOfA}, decls.Bool, + typeParamAList), + decls.NewParameterizedOverload(overloads.InMap, + []*exprpb.Type{paramA, mapOfAB}, decls.Bool, + typeParamABList)), + + // Conversions to type. + + decls.NewFunction(overloads.TypeConvertType, + decls.NewParameterizedOverload(overloads.TypeConvertType, + []*exprpb.Type{paramA}, decls.NewTypeType(paramA), typeParamAList)), + + // Conversions to int. + + decls.NewFunction(overloads.TypeConvertInt, + decls.NewOverload(overloads.IntToInt, []*exprpb.Type{decls.Int}, decls.Int), + decls.NewOverload(overloads.UintToInt, []*exprpb.Type{decls.Uint}, decls.Int), + decls.NewOverload(overloads.DoubleToInt, []*exprpb.Type{decls.Double}, decls.Int), + decls.NewOverload(overloads.StringToInt, []*exprpb.Type{decls.String}, decls.Int), + decls.NewOverload(overloads.TimestampToInt, []*exprpb.Type{decls.Timestamp}, decls.Int), + decls.NewOverload(overloads.DurationToInt, []*exprpb.Type{decls.Duration}, decls.Int)), + + // Conversions to uint. + + decls.NewFunction(overloads.TypeConvertUint, + decls.NewOverload(overloads.UintToUint, []*exprpb.Type{decls.Uint}, decls.Uint), + decls.NewOverload(overloads.IntToUint, []*exprpb.Type{decls.Int}, decls.Uint), + decls.NewOverload(overloads.DoubleToUint, []*exprpb.Type{decls.Double}, decls.Uint), + decls.NewOverload(overloads.StringToUint, []*exprpb.Type{decls.String}, decls.Uint)), + + // Conversions to double. + + decls.NewFunction(overloads.TypeConvertDouble, + decls.NewOverload(overloads.DoubleToDouble, []*exprpb.Type{decls.Double}, decls.Double), + decls.NewOverload(overloads.IntToDouble, []*exprpb.Type{decls.Int}, decls.Double), + decls.NewOverload(overloads.UintToDouble, []*exprpb.Type{decls.Uint}, decls.Double), + decls.NewOverload(overloads.StringToDouble, []*exprpb.Type{decls.String}, decls.Double)), + + // Conversions to bool. + + decls.NewFunction(overloads.TypeConvertBool, + decls.NewOverload(overloads.BoolToBool, []*exprpb.Type{decls.Bool}, decls.Bool), + decls.NewOverload(overloads.StringToBool, []*exprpb.Type{decls.String}, decls.Bool)), + + // Conversions to string. + + decls.NewFunction(overloads.TypeConvertString, + decls.NewOverload(overloads.StringToString, []*exprpb.Type{decls.String}, decls.String), + decls.NewOverload(overloads.BoolToString, []*exprpb.Type{decls.Bool}, decls.String), + decls.NewOverload(overloads.IntToString, []*exprpb.Type{decls.Int}, decls.String), + decls.NewOverload(overloads.UintToString, []*exprpb.Type{decls.Uint}, decls.String), + decls.NewOverload(overloads.DoubleToString, []*exprpb.Type{decls.Double}, decls.String), + decls.NewOverload(overloads.BytesToString, []*exprpb.Type{decls.Bytes}, decls.String), + decls.NewOverload(overloads.TimestampToString, []*exprpb.Type{decls.Timestamp}, decls.String), + decls.NewOverload(overloads.DurationToString, []*exprpb.Type{decls.Duration}, decls.String)), + + // Conversions to bytes. + + decls.NewFunction(overloads.TypeConvertBytes, + decls.NewOverload(overloads.BytesToBytes, []*exprpb.Type{decls.Bytes}, decls.Bytes), + decls.NewOverload(overloads.StringToBytes, []*exprpb.Type{decls.String}, decls.Bytes)), + + // Conversions to timestamps. + + decls.NewFunction(overloads.TypeConvertTimestamp, + decls.NewOverload(overloads.TimestampToTimestamp, + []*exprpb.Type{decls.Timestamp}, decls.Timestamp), + decls.NewOverload(overloads.StringToTimestamp, + []*exprpb.Type{decls.String}, decls.Timestamp), + decls.NewOverload(overloads.IntToTimestamp, + []*exprpb.Type{decls.Int}, decls.Timestamp)), + + // Conversions to durations. + + decls.NewFunction(overloads.TypeConvertDuration, + decls.NewOverload(overloads.DurationToDuration, + []*exprpb.Type{decls.Duration}, decls.Duration), + decls.NewOverload(overloads.StringToDuration, + []*exprpb.Type{decls.String}, decls.Duration), + decls.NewOverload(overloads.IntToDuration, + []*exprpb.Type{decls.Int}, decls.Duration)), + + // Conversions to Dyn. + + decls.NewFunction(overloads.TypeConvertDyn, + decls.NewParameterizedOverload(overloads.ToDyn, + []*exprpb.Type{paramA}, decls.Dyn, + typeParamAList)), + + // String functions. + + decls.NewFunction(overloads.Contains, + decls.NewInstanceOverload(overloads.ContainsString, + []*exprpb.Type{decls.String, decls.String}, decls.Bool)), + decls.NewFunction(overloads.EndsWith, + decls.NewInstanceOverload(overloads.EndsWithString, + []*exprpb.Type{decls.String, decls.String}, decls.Bool)), + decls.NewFunction(overloads.Matches, + decls.NewInstanceOverload(overloads.MatchesString, + []*exprpb.Type{decls.String, decls.String}, decls.Bool)), + decls.NewFunction(overloads.StartsWith, + decls.NewInstanceOverload(overloads.StartsWithString, + []*exprpb.Type{decls.String, decls.String}, decls.Bool)), + + // Date/time functions. + + decls.NewFunction(overloads.TimeGetFullYear, + decls.NewInstanceOverload(overloads.TimestampToYear, + []*exprpb.Type{decls.Timestamp}, decls.Int), + decls.NewInstanceOverload(overloads.TimestampToYearWithTz, + []*exprpb.Type{decls.Timestamp, decls.String}, decls.Int)), + + decls.NewFunction(overloads.TimeGetMonth, + decls.NewInstanceOverload(overloads.TimestampToMonth, + []*exprpb.Type{decls.Timestamp}, decls.Int), + decls.NewInstanceOverload(overloads.TimestampToMonthWithTz, + []*exprpb.Type{decls.Timestamp, decls.String}, decls.Int)), + + decls.NewFunction(overloads.TimeGetDayOfYear, + decls.NewInstanceOverload(overloads.TimestampToDayOfYear, + []*exprpb.Type{decls.Timestamp}, decls.Int), + decls.NewInstanceOverload(overloads.TimestampToDayOfYearWithTz, + []*exprpb.Type{decls.Timestamp, decls.String}, decls.Int)), + + decls.NewFunction(overloads.TimeGetDayOfMonth, + decls.NewInstanceOverload(overloads.TimestampToDayOfMonthZeroBased, + []*exprpb.Type{decls.Timestamp}, decls.Int), + decls.NewInstanceOverload(overloads.TimestampToDayOfMonthZeroBasedWithTz, + []*exprpb.Type{decls.Timestamp, decls.String}, decls.Int)), + + decls.NewFunction(overloads.TimeGetDate, + decls.NewInstanceOverload(overloads.TimestampToDayOfMonthOneBased, + []*exprpb.Type{decls.Timestamp}, decls.Int), + decls.NewInstanceOverload(overloads.TimestampToDayOfMonthOneBasedWithTz, + []*exprpb.Type{decls.Timestamp, decls.String}, decls.Int)), + + decls.NewFunction(overloads.TimeGetDayOfWeek, + decls.NewInstanceOverload(overloads.TimestampToDayOfWeek, + []*exprpb.Type{decls.Timestamp}, decls.Int), + decls.NewInstanceOverload(overloads.TimestampToDayOfWeekWithTz, + []*exprpb.Type{decls.Timestamp, decls.String}, decls.Int)), + + decls.NewFunction(overloads.TimeGetHours, + decls.NewInstanceOverload(overloads.TimestampToHours, + []*exprpb.Type{decls.Timestamp}, decls.Int), + decls.NewInstanceOverload(overloads.TimestampToHoursWithTz, + []*exprpb.Type{decls.Timestamp, decls.String}, decls.Int), + decls.NewInstanceOverload(overloads.DurationToHours, + []*exprpb.Type{decls.Duration}, decls.Int)), + + decls.NewFunction(overloads.TimeGetMinutes, + decls.NewInstanceOverload(overloads.TimestampToMinutes, + []*exprpb.Type{decls.Timestamp}, decls.Int), + decls.NewInstanceOverload(overloads.TimestampToMinutesWithTz, + []*exprpb.Type{decls.Timestamp, decls.String}, decls.Int), + decls.NewInstanceOverload(overloads.DurationToMinutes, + []*exprpb.Type{decls.Duration}, decls.Int)), + + decls.NewFunction(overloads.TimeGetSeconds, + decls.NewInstanceOverload(overloads.TimestampToSeconds, + []*exprpb.Type{decls.Timestamp}, decls.Int), + decls.NewInstanceOverload(overloads.TimestampToSecondsWithTz, + []*exprpb.Type{decls.Timestamp, decls.String}, decls.Int), + decls.NewInstanceOverload(overloads.DurationToSeconds, + []*exprpb.Type{decls.Duration}, decls.Int)), + + decls.NewFunction(overloads.TimeGetMilliseconds, + decls.NewInstanceOverload(overloads.TimestampToMilliseconds, + []*exprpb.Type{decls.Timestamp}, decls.Int), + decls.NewInstanceOverload(overloads.TimestampToMillisecondsWithTz, + []*exprpb.Type{decls.Timestamp, decls.String}, decls.Int), + decls.NewInstanceOverload(overloads.DurationToMilliseconds, + []*exprpb.Type{decls.Duration}, decls.Int)), + + // Relations. + decls.NewFunction(operators.Less, + decls.NewOverload(overloads.LessBool, + []*exprpb.Type{decls.Bool, decls.Bool}, decls.Bool), + decls.NewOverload(overloads.LessInt64, + []*exprpb.Type{decls.Int, decls.Int}, decls.Bool), + decls.NewOverload(overloads.LessInt64Double, + []*exprpb.Type{decls.Int, decls.Double}, decls.Bool), + decls.NewOverload(overloads.LessInt64Uint64, + []*exprpb.Type{decls.Int, decls.Uint}, decls.Bool), + decls.NewOverload(overloads.LessUint64, + []*exprpb.Type{decls.Uint, decls.Uint}, decls.Bool), + decls.NewOverload(overloads.LessUint64Double, + []*exprpb.Type{decls.Uint, decls.Double}, decls.Bool), + decls.NewOverload(overloads.LessUint64Int64, + []*exprpb.Type{decls.Uint, decls.Int}, decls.Bool), + decls.NewOverload(overloads.LessDouble, + []*exprpb.Type{decls.Double, decls.Double}, decls.Bool), + decls.NewOverload(overloads.LessDoubleInt64, + []*exprpb.Type{decls.Double, decls.Int}, decls.Bool), + decls.NewOverload(overloads.LessDoubleUint64, + []*exprpb.Type{decls.Double, decls.Uint}, decls.Bool), + decls.NewOverload(overloads.LessString, + []*exprpb.Type{decls.String, decls.String}, decls.Bool), + decls.NewOverload(overloads.LessBytes, + []*exprpb.Type{decls.Bytes, decls.Bytes}, decls.Bool), + decls.NewOverload(overloads.LessTimestamp, + []*exprpb.Type{decls.Timestamp, decls.Timestamp}, decls.Bool), + decls.NewOverload(overloads.LessDuration, + []*exprpb.Type{decls.Duration, decls.Duration}, decls.Bool)), + + decls.NewFunction(operators.LessEquals, + decls.NewOverload(overloads.LessEqualsBool, + []*exprpb.Type{decls.Bool, decls.Bool}, decls.Bool), + decls.NewOverload(overloads.LessEqualsInt64, + []*exprpb.Type{decls.Int, decls.Int}, decls.Bool), + decls.NewOverload(overloads.LessEqualsInt64Double, + []*exprpb.Type{decls.Int, decls.Double}, decls.Bool), + decls.NewOverload(overloads.LessEqualsInt64Uint64, + []*exprpb.Type{decls.Int, decls.Uint}, decls.Bool), + decls.NewOverload(overloads.LessEqualsUint64, + []*exprpb.Type{decls.Uint, decls.Uint}, decls.Bool), + decls.NewOverload(overloads.LessEqualsUint64Double, + []*exprpb.Type{decls.Uint, decls.Double}, decls.Bool), + decls.NewOverload(overloads.LessEqualsUint64Int64, + []*exprpb.Type{decls.Uint, decls.Int}, decls.Bool), + decls.NewOverload(overloads.LessEqualsDouble, + []*exprpb.Type{decls.Double, decls.Double}, decls.Bool), + decls.NewOverload(overloads.LessEqualsDoubleInt64, + []*exprpb.Type{decls.Double, decls.Int}, decls.Bool), + decls.NewOverload(overloads.LessEqualsDoubleUint64, + []*exprpb.Type{decls.Double, decls.Uint}, decls.Bool), + decls.NewOverload(overloads.LessEqualsString, + []*exprpb.Type{decls.String, decls.String}, decls.Bool), + decls.NewOverload(overloads.LessEqualsBytes, + []*exprpb.Type{decls.Bytes, decls.Bytes}, decls.Bool), + decls.NewOverload(overloads.LessEqualsTimestamp, + []*exprpb.Type{decls.Timestamp, decls.Timestamp}, decls.Bool), + decls.NewOverload(overloads.LessEqualsDuration, + []*exprpb.Type{decls.Duration, decls.Duration}, decls.Bool)), + + decls.NewFunction(operators.Greater, + decls.NewOverload(overloads.GreaterBool, + []*exprpb.Type{decls.Bool, decls.Bool}, decls.Bool), + decls.NewOverload(overloads.GreaterInt64, + []*exprpb.Type{decls.Int, decls.Int}, decls.Bool), + decls.NewOverload(overloads.GreaterInt64Double, + []*exprpb.Type{decls.Int, decls.Double}, decls.Bool), + decls.NewOverload(overloads.GreaterInt64Uint64, + []*exprpb.Type{decls.Int, decls.Uint}, decls.Bool), + decls.NewOverload(overloads.GreaterUint64, + []*exprpb.Type{decls.Uint, decls.Uint}, decls.Bool), + decls.NewOverload(overloads.GreaterUint64Double, + []*exprpb.Type{decls.Uint, decls.Double}, decls.Bool), + decls.NewOverload(overloads.GreaterUint64Int64, + []*exprpb.Type{decls.Uint, decls.Int}, decls.Bool), + decls.NewOverload(overloads.GreaterDouble, + []*exprpb.Type{decls.Double, decls.Double}, decls.Bool), + decls.NewOverload(overloads.GreaterDoubleInt64, + []*exprpb.Type{decls.Double, decls.Int}, decls.Bool), + decls.NewOverload(overloads.GreaterDoubleUint64, + []*exprpb.Type{decls.Double, decls.Uint}, decls.Bool), + decls.NewOverload(overloads.GreaterString, + []*exprpb.Type{decls.String, decls.String}, decls.Bool), + decls.NewOverload(overloads.GreaterBytes, + []*exprpb.Type{decls.Bytes, decls.Bytes}, decls.Bool), + decls.NewOverload(overloads.GreaterTimestamp, + []*exprpb.Type{decls.Timestamp, decls.Timestamp}, decls.Bool), + decls.NewOverload(overloads.GreaterDuration, + []*exprpb.Type{decls.Duration, decls.Duration}, decls.Bool)), + + decls.NewFunction(operators.GreaterEquals, + decls.NewOverload(overloads.GreaterEqualsBool, + []*exprpb.Type{decls.Bool, decls.Bool}, decls.Bool), + decls.NewOverload(overloads.GreaterEqualsInt64, + []*exprpb.Type{decls.Int, decls.Int}, decls.Bool), + decls.NewOverload(overloads.GreaterEqualsInt64Double, + []*exprpb.Type{decls.Int, decls.Double}, decls.Bool), + decls.NewOverload(overloads.GreaterEqualsInt64Uint64, + []*exprpb.Type{decls.Int, decls.Uint}, decls.Bool), + decls.NewOverload(overloads.GreaterEqualsUint64, + []*exprpb.Type{decls.Uint, decls.Uint}, decls.Bool), + decls.NewOverload(overloads.GreaterEqualsUint64Double, + []*exprpb.Type{decls.Uint, decls.Double}, decls.Bool), + decls.NewOverload(overloads.GreaterEqualsUint64Int64, + []*exprpb.Type{decls.Uint, decls.Int}, decls.Bool), + decls.NewOverload(overloads.GreaterEqualsDouble, + []*exprpb.Type{decls.Double, decls.Double}, decls.Bool), + decls.NewOverload(overloads.GreaterEqualsDoubleInt64, + []*exprpb.Type{decls.Double, decls.Int}, decls.Bool), + decls.NewOverload(overloads.GreaterEqualsDoubleUint64, + []*exprpb.Type{decls.Double, decls.Uint}, decls.Bool), + decls.NewOverload(overloads.GreaterEqualsString, + []*exprpb.Type{decls.String, decls.String}, decls.Bool), + decls.NewOverload(overloads.GreaterEqualsBytes, + []*exprpb.Type{decls.Bytes, decls.Bytes}, decls.Bool), + decls.NewOverload(overloads.GreaterEqualsTimestamp, + []*exprpb.Type{decls.Timestamp, decls.Timestamp}, decls.Bool), + decls.NewOverload(overloads.GreaterEqualsDuration, + []*exprpb.Type{decls.Duration, decls.Duration}, decls.Bool)), + }...) +} + +// StandardDeclarations returns the Decls for all functions and constants in the evaluator. +func StandardDeclarations() []*exprpb.Decl { + return standardDeclarations +} diff --git a/vendor/github.com/google/cel-go/checker/types.go b/vendor/github.com/google/cel-go/checker/types.go new file mode 100644 index 0000000000..2962d53455 --- /dev/null +++ b/vendor/github.com/google/cel-go/checker/types.go @@ -0,0 +1,485 @@ +// Copyright 2018 Google LLC +// +// 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. + +package checker + +import ( + "fmt" + "strings" + + "github.com/google/cel-go/checker/decls" + + "google.golang.org/protobuf/proto" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +const ( + kindUnknown = iota + 1 + kindError + kindFunction + kindDyn + kindPrimitive + kindWellKnown + kindWrapper + kindNull + kindAbstract + kindType + kindList + kindMap + kindObject + kindTypeParam +) + +// FormatCheckedType converts a type message into a string representation. +func FormatCheckedType(t *exprpb.Type) string { + switch kindOf(t) { + case kindDyn: + return "dyn" + case kindFunction: + return formatFunction(t.GetFunction().GetResultType(), + t.GetFunction().GetArgTypes(), + false) + case kindList: + return fmt.Sprintf("list(%s)", FormatCheckedType(t.GetListType().ElemType)) + case kindObject: + return t.GetMessageType() + case kindMap: + return fmt.Sprintf("map(%s, %s)", + FormatCheckedType(t.GetMapType().KeyType), + FormatCheckedType(t.GetMapType().ValueType)) + case kindNull: + return "null" + case kindPrimitive: + switch t.GetPrimitive() { + case exprpb.Type_UINT64: + return "uint" + case exprpb.Type_INT64: + return "int" + } + return strings.Trim(strings.ToLower(t.GetPrimitive().String()), " ") + case kindType: + if t.GetType() == nil { + return "type" + } + return fmt.Sprintf("type(%s)", FormatCheckedType(t.GetType())) + case kindWellKnown: + switch t.GetWellKnown() { + case exprpb.Type_ANY: + return "any" + case exprpb.Type_DURATION: + return "duration" + case exprpb.Type_TIMESTAMP: + return "timestamp" + } + case kindWrapper: + return fmt.Sprintf("wrapper(%s)", + FormatCheckedType(decls.NewPrimitiveType(t.GetWrapper()))) + case kindError: + return "!error!" + } + return t.String() +} + +// isDyn returns true if the input t is either type DYN or a well-known ANY message. +func isDyn(t *exprpb.Type) bool { + // Note: object type values that are well-known and map to a DYN value in practice + // are sanitized prior to being added to the environment. + switch kindOf(t) { + case kindDyn: + return true + case kindWellKnown: + return t.GetWellKnown() == exprpb.Type_ANY + default: + return false + } +} + +// isDynOrError returns true if the input is either an Error, DYN, or well-known ANY message. +func isDynOrError(t *exprpb.Type) bool { + switch kindOf(t) { + case kindError: + return true + default: + return isDyn(t) + } +} + +// isEqualOrLessSpecific checks whether one type is equal or less specific than the other one. +// A type is less specific if it matches the other type using the DYN type. +func isEqualOrLessSpecific(t1 *exprpb.Type, t2 *exprpb.Type) bool { + kind1, kind2 := kindOf(t1), kindOf(t2) + // The first type is less specific. + if isDyn(t1) || kind1 == kindTypeParam { + return true + } + // The first type is not less specific. + if isDyn(t2) || kind2 == kindTypeParam { + return false + } + // Types must be of the same kind to be equal. + if kind1 != kind2 { + return false + } + + // With limited exceptions for ANY and JSON values, the types must agree and be equivalent in + // order to return true. + switch kind1 { + case kindAbstract: + a1 := t1.GetAbstractType() + a2 := t2.GetAbstractType() + if a1.GetName() != a2.GetName() || + len(a1.GetParameterTypes()) != len(a2.GetParameterTypes()) { + return false + } + for i, p1 := range a1.GetParameterTypes() { + if !isEqualOrLessSpecific(p1, a2.GetParameterTypes()[i]) { + return false + } + } + return true + case kindList: + return isEqualOrLessSpecific(t1.GetListType().ElemType, t2.GetListType().ElemType) + case kindMap: + m1 := t1.GetMapType() + m2 := t2.GetMapType() + return isEqualOrLessSpecific(m1.KeyType, m2.KeyType) && + isEqualOrLessSpecific(m1.ValueType, m2.ValueType) + case kindType: + return true + default: + return proto.Equal(t1, t2) + } +} + +/// internalIsAssignable returns true if t1 is assignable to t2. +func internalIsAssignable(m *mapping, t1 *exprpb.Type, t2 *exprpb.Type) bool { + // Process type parameters. + kind1, kind2 := kindOf(t1), kindOf(t2) + if kind2 == kindTypeParam { + // If t2 is a valid type substitution for t1, return true. + valid, t2HasSub := isValidTypeSubstitution(m, t1, t2) + if valid { + return true + } + // If t2 is not a valid type sub for t1, and already has a known substitution return false + // since it is not possible for t1 to be a substitution for t2. + if !valid && t2HasSub { + return false + } + // Otherwise, fall through to check whether t1 is a possible substitution for t2. + } + if kind1 == kindTypeParam { + // Return whether t1 is a valid substitution for t2. If not, do no additional checks as the + // possible type substitutions have been searched in both directions. + valid, _ := isValidTypeSubstitution(m, t2, t1) + return valid + } + + // Next check for wildcard types. + if isDynOrError(t1) || isDynOrError(t2) { + return true + } + + // Test for when the types do not need to agree, but are more specific than dyn. + switch kind1 { + case kindNull: + return internalIsAssignableNull(t2) + case kindPrimitive: + return internalIsAssignablePrimitive(t1.GetPrimitive(), t2) + case kindWrapper: + return internalIsAssignable(m, decls.NewPrimitiveType(t1.GetWrapper()), t2) + default: + if kind1 != kind2 { + return false + } + } + + // Test for when the types must agree. + switch kind1 { + // ERROR, TYPE_PARAM, and DYN handled above. + case kindAbstract: + return internalIsAssignableAbstractType(m, t1.GetAbstractType(), t2.GetAbstractType()) + case kindFunction: + return internalIsAssignableFunction(m, t1.GetFunction(), t2.GetFunction()) + case kindList: + return internalIsAssignable(m, t1.GetListType().GetElemType(), t2.GetListType().GetElemType()) + case kindMap: + return internalIsAssignableMap(m, t1.GetMapType(), t2.GetMapType()) + case kindObject: + return t1.GetMessageType() == t2.GetMessageType() + case kindType: + // A type is a type is a type, any additional parameterization of the + // type cannot affect method resolution or assignability. + return true + case kindWellKnown: + return t1.GetWellKnown() == t2.GetWellKnown() + default: + return false + } +} + +// isValidTypeSubstitution returns whether t2 (or its type substitution) is a valid type +// substitution for t1, and whether t2 has a type substitution in mapping m. +// +// The type t2 is a valid substitution for t1 if any of the following statements is true +// - t2 has a type substitition (t2sub) equal to t1 +// - t2 has a type substitution (t2sub) assignable to t1 +// - t2 does not occur within t1. +func isValidTypeSubstitution(m *mapping, t1, t2 *exprpb.Type) (valid, hasSub bool) { + if t2Sub, found := m.find(t2); found { + kind1, kind2 := kindOf(t1), kindOf(t2) + if kind1 == kind2 && proto.Equal(t1, t2Sub) { + return true, true + } + // If the types are compatible, pick the more general type and return true + if internalIsAssignable(m, t1, t2Sub) { + m.add(t2, mostGeneral(t1, t2Sub)) + return true, true + } + return false, true + } + if notReferencedIn(m, t2, t1) { + m.add(t2, t1) + return true, false + } + return false, false +} + +// internalIsAssignableAbstractType returns true if the abstract type names agree and all type +// parameters are assignable. +func internalIsAssignableAbstractType(m *mapping, + a1 *exprpb.Type_AbstractType, + a2 *exprpb.Type_AbstractType) bool { + return a1.GetName() == a2.GetName() && + internalIsAssignableList(m, a1.GetParameterTypes(), a2.GetParameterTypes()) +} + +// internalIsAssignableFunction returns true if the function return type and arg types are +// assignable. +func internalIsAssignableFunction(m *mapping, + f1 *exprpb.Type_FunctionType, + f2 *exprpb.Type_FunctionType) bool { + f1ArgTypes := flattenFunctionTypes(f1) + f2ArgTypes := flattenFunctionTypes(f2) + if internalIsAssignableList(m, f1ArgTypes, f2ArgTypes) { + return true + } + return false +} + +// internalIsAssignableList returns true if the element types at each index in the list are +// assignable from l1[i] to l2[i]. The list lengths must also agree for the lists to be +// assignable. +func internalIsAssignableList(m *mapping, l1 []*exprpb.Type, l2 []*exprpb.Type) bool { + if len(l1) != len(l2) { + return false + } + for i, t1 := range l1 { + if !internalIsAssignable(m, t1, l2[i]) { + return false + } + } + return true +} + +// internalIsAssignableMap returns true if map m1 may be assigned to map m2. +func internalIsAssignableMap(m *mapping, m1 *exprpb.Type_MapType, m2 *exprpb.Type_MapType) bool { + if internalIsAssignableList(m, + []*exprpb.Type{m1.GetKeyType(), m1.GetValueType()}, + []*exprpb.Type{m2.GetKeyType(), m2.GetValueType()}) { + return true + } + return false +} + +// internalIsAssignableNull returns true if the type is nullable. +func internalIsAssignableNull(t *exprpb.Type) bool { + switch kindOf(t) { + case kindAbstract, kindObject, kindNull, kindWellKnown, kindWrapper: + return true + default: + return false + } +} + +// internalIsAssignablePrimitive returns true if the target type is the same or if it is a wrapper +// for the primitive type. +func internalIsAssignablePrimitive(p exprpb.Type_PrimitiveType, target *exprpb.Type) bool { + switch kindOf(target) { + case kindPrimitive: + return p == target.GetPrimitive() + case kindWrapper: + return p == target.GetWrapper() + default: + return false + } +} + +// isAssignable returns an updated type substitution mapping if t1 is assignable to t2. +func isAssignable(m *mapping, t1 *exprpb.Type, t2 *exprpb.Type) *mapping { + mCopy := m.copy() + if internalIsAssignable(mCopy, t1, t2) { + return mCopy + } + return nil +} + +// isAssignableList returns an updated type substitution mapping if l1 is assignable to l2. +func isAssignableList(m *mapping, l1 []*exprpb.Type, l2 []*exprpb.Type) *mapping { + mCopy := m.copy() + if internalIsAssignableList(mCopy, l1, l2) { + return mCopy + } + return nil +} + +// kindOf returns the kind of the type as defined in the checked.proto. +func kindOf(t *exprpb.Type) int { + if t == nil || t.TypeKind == nil { + return kindUnknown + } + switch t.TypeKind.(type) { + case *exprpb.Type_Error: + return kindError + case *exprpb.Type_Function: + return kindFunction + case *exprpb.Type_Dyn: + return kindDyn + case *exprpb.Type_Primitive: + return kindPrimitive + case *exprpb.Type_WellKnown: + return kindWellKnown + case *exprpb.Type_Wrapper: + return kindWrapper + case *exprpb.Type_Null: + return kindNull + case *exprpb.Type_Type: + return kindType + case *exprpb.Type_ListType_: + return kindList + case *exprpb.Type_MapType_: + return kindMap + case *exprpb.Type_MessageType: + return kindObject + case *exprpb.Type_TypeParam: + return kindTypeParam + case *exprpb.Type_AbstractType_: + return kindAbstract + } + return kindUnknown +} + +// mostGeneral returns the more general of two types which are known to unify. +func mostGeneral(t1 *exprpb.Type, t2 *exprpb.Type) *exprpb.Type { + if isEqualOrLessSpecific(t1, t2) { + return t1 + } + return t2 +} + +// notReferencedIn checks whether the type doesn't appear directly or transitively within the other +// type. This is a standard requirement for type unification, commonly referred to as the "occurs +// check". +func notReferencedIn(m *mapping, t *exprpb.Type, withinType *exprpb.Type) bool { + if proto.Equal(t, withinType) { + return false + } + withinKind := kindOf(withinType) + switch withinKind { + case kindTypeParam: + wtSub, found := m.find(withinType) + if !found { + return true + } + return notReferencedIn(m, t, wtSub) + case kindAbstract: + for _, pt := range withinType.GetAbstractType().GetParameterTypes() { + if !notReferencedIn(m, t, pt) { + return false + } + } + return true + case kindList: + return notReferencedIn(m, t, withinType.GetListType().ElemType) + case kindMap: + mt := withinType.GetMapType() + return notReferencedIn(m, t, mt.KeyType) && notReferencedIn(m, t, mt.ValueType) + case kindWrapper: + return notReferencedIn(m, t, decls.NewPrimitiveType(withinType.GetWrapper())) + default: + return true + } +} + +// substitute replaces all direct and indirect occurrences of bound type parameters. Unbound type +// parameters are replaced by DYN if typeParamToDyn is true. +func substitute(m *mapping, t *exprpb.Type, typeParamToDyn bool) *exprpb.Type { + if tSub, found := m.find(t); found { + return substitute(m, tSub, typeParamToDyn) + } + kind := kindOf(t) + if typeParamToDyn && kind == kindTypeParam { + return decls.Dyn + } + switch kind { + case kindAbstract: + at := t.GetAbstractType() + params := make([]*exprpb.Type, len(at.GetParameterTypes())) + for i, p := range at.GetParameterTypes() { + params[i] = substitute(m, p, typeParamToDyn) + } + return decls.NewAbstractType(at.GetName(), params...) + case kindFunction: + fn := t.GetFunction() + rt := substitute(m, fn.ResultType, typeParamToDyn) + args := make([]*exprpb.Type, len(fn.ArgTypes)) + for i, a := range fn.ArgTypes { + args[i] = substitute(m, a, typeParamToDyn) + } + return decls.NewFunctionType(rt, args...) + case kindList: + return decls.NewListType(substitute(m, t.GetListType().ElemType, typeParamToDyn)) + case kindMap: + mt := t.GetMapType() + return decls.NewMapType(substitute(m, mt.KeyType, typeParamToDyn), + substitute(m, mt.ValueType, typeParamToDyn)) + case kindType: + if t.GetType() != nil { + return decls.NewTypeType(substitute(m, t.GetType(), typeParamToDyn)) + } + return t + default: + return t + } +} + +func typeKey(t *exprpb.Type) string { + return FormatCheckedType(t) +} + +// flattenFunctionTypes takes a function with arg types T1, T2, ..., TN and result type TR +// and returns a slice containing {T1, T2, ..., TN, TR}. +func flattenFunctionTypes(f *exprpb.Type_FunctionType) []*exprpb.Type { + argTypes := f.GetArgTypes() + if len(argTypes) == 0 { + return []*exprpb.Type{f.GetResultType()} + } + flattend := make([]*exprpb.Type, len(argTypes)+1, len(argTypes)+1) + for i, at := range argTypes { + flattend[i] = at + } + flattend[len(argTypes)] = f.GetResultType() + return flattend +} diff --git a/vendor/github.com/google/cel-go/common/containers/container.go b/vendor/github.com/google/cel-go/common/containers/container.go new file mode 100644 index 0000000000..07ae72c81b --- /dev/null +++ b/vendor/github.com/google/cel-go/common/containers/container.go @@ -0,0 +1,316 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Package containers defines types and functions for resolving qualified names within a namespace +// or type provided to CEL. +package containers + +import ( + "fmt" + "strings" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +var ( + // DefaultContainer has an empty container name. + DefaultContainer *Container = nil + + // Empty map to search for aliases when needed. + noAliases = make(map[string]string) +) + +// NewContainer creates a new Container with the fully-qualified name. +func NewContainer(opts ...ContainerOption) (*Container, error) { + var c *Container + var err error + for _, opt := range opts { + c, err = opt(c) + if err != nil { + return nil, err + } + } + return c, nil +} + +// Container holds a reference to an optional qualified container name and set of aliases. +// +// The program container can be used to simplify variable, function, and type specification within +// CEL programs and behaves more or less like a C++ namespace. See ResolveCandidateNames for more +// details. +type Container struct { + name string + aliases map[string]string +} + +// Extend creates a new Container with the existing settings and applies a series of +// ContainerOptions to further configure the new container. +func (c *Container) Extend(opts ...ContainerOption) (*Container, error) { + if c == nil { + return NewContainer(opts...) + } + // Copy the name and aliases of the existing container. + ext := &Container{name: c.Name()} + if len(c.aliasSet()) > 0 { + aliasSet := make(map[string]string, len(c.aliasSet())) + for k, v := range c.aliasSet() { + aliasSet[k] = v + } + ext.aliases = aliasSet + } + // Apply the new options to the container. + var err error + for _, opt := range opts { + ext, err = opt(ext) + if err != nil { + return nil, err + } + } + return ext, nil +} + +// Name returns the fully-qualified name of the container. +// +// The name may conceptually be a namespace, package, or type. +func (c *Container) Name() string { + if c == nil { + return "" + } + return c.name +} + +// ResolveCandidateNames returns the candidates name of namespaced identifiers in C++ resolution +// order. +// +// Names which shadow other names are returned first. If a name includes a leading dot ('.'), +// the name is treated as an absolute identifier which cannot be shadowed. +// +// Given a container name a.b.c.M.N and a type name R.s, this will deliver in order: +// +// a.b.c.M.N.R.s +// a.b.c.M.R.s +// a.b.c.R.s +// a.b.R.s +// a.R.s +// R.s +// +// If aliases or abbreviations are configured for the container, then alias names will take +// precedence over containerized names. +func (c *Container) ResolveCandidateNames(name string) []string { + if strings.HasPrefix(name, ".") { + qn := name[1:] + alias, isAlias := c.findAlias(qn) + if isAlias { + return []string{alias} + } + return []string{qn} + } + alias, isAlias := c.findAlias(name) + if isAlias { + return []string{alias} + } + if c.Name() == "" { + return []string{name} + } + nextCont := c.Name() + candidates := []string{nextCont + "." + name} + for i := strings.LastIndex(nextCont, "."); i >= 0; i = strings.LastIndex(nextCont, ".") { + nextCont = nextCont[:i] + candidates = append(candidates, nextCont+"."+name) + } + return append(candidates, name) +} + +// aliasSet returns the alias to fully-qualified name mapping stored in the container. +func (c *Container) aliasSet() map[string]string { + if c == nil || c.aliases == nil { + return noAliases + } + return c.aliases +} + +// findAlias takes a name as input and returns an alias expansion if one exists. +// +// If the name is qualified, the first component of the qualified name is checked against known +// aliases. Any alias that is found in a qualified name is expanded in the result: +// +// alias: R -> my.alias.R +// name: R.S.T +// output: my.alias.R.S.T +// +// Note, the name must not have a leading dot. +func (c *Container) findAlias(name string) (string, bool) { + // If an alias exists for the name, ensure it is searched last. + simple := name + qualifier := "" + dot := strings.Index(name, ".") + if dot >= 0 { + simple = name[0:dot] + qualifier = name[dot:] + } + alias, found := c.aliasSet()[simple] + if !found { + return "", false + } + return alias + qualifier, true +} + +// ContainerOption specifies a functional configuration option for a Container. +// +// Note, ContainerOption implementations must be able to handle nil container inputs. +type ContainerOption func(*Container) (*Container, error) + +// Abbrevs configures a set of simple names as abbreviations for fully-qualified names. +// +// An abbreviation (abbrev for short) is a simple name that expands to a fully-qualified name. +// Abbreviations can be useful when working with variables, functions, and especially types from +// multiple namespaces: +// +// // CEL object construction +// qual.pkg.version.ObjTypeName{ +// field: alt.container.ver.FieldTypeName{value: ...} +// } +// +// Only one the qualified names above may be used as the CEL container, so at least one of these +// references must be a long qualified name within an otherwise short CEL program. Using the +// following abbreviations, the program becomes much simpler: +// +// // CEL Go option +// Abbrevs("qual.pkg.version.ObjTypeName", "alt.container.ver.FieldTypeName") +// // Simplified Object construction +// ObjTypeName{field: FieldTypeName{value: ...}} +// +// There are a few rules for the qualified names and the simple abbreviations generated from them: +// - Qualified names must be dot-delimited, e.g. `package.subpkg.name`. +// - The last element in the qualified name is the abbreviation. +// - Abbreviations must not collide with each other. +// - The abbreviation must not collide with unqualified names in use. +// +// Abbreviations are distinct from container-based references in the following important ways: +// - Abbreviations must expand to a fully-qualified name. +// - Expanded abbreviations do not participate in namespace resolution. +// - Abbreviation expansion is done instead of the container search for a matching identifier. +// - Containers follow C++ namespace resolution rules with searches from the most qualified name +// to the least qualified name. +// - Container references within the CEL program may be relative, and are resolved to fully +// qualified names at either type-check time or program plan time, whichever comes first. +// +// If there is ever a case where an identifier could be in both the container and as an +// abbreviation, the abbreviation wins as this will ensure that the meaning of a program is +// preserved between compilations even as the container evolves. +func Abbrevs(qualifiedNames ...string) ContainerOption { + return func(c *Container) (*Container, error) { + for _, qn := range qualifiedNames { + ind := strings.LastIndex(qn, ".") + if ind <= 0 || ind >= len(qn)-1 { + return nil, fmt.Errorf( + "invalid qualified name: %s, wanted name of the form 'qualified.name'", qn) + } + alias := qn[ind+1:] + var err error + c, err = aliasAs("abbreviation", qn, alias)(c) + if err != nil { + return nil, err + } + } + return c, nil + } +} + +// Alias associates a fully-qualified name with a user-defined alias. +// +// In general, Abbrevs is preferred to Alias since the names generated from the Abbrevs option +// are more easily traced back to source code. The Alias option is useful for propagating alias +// configuration from one Container instance to another, and may also be useful for remapping +// poorly chosen protobuf message / package names. +// +// Note: all of the rules that apply to Abbrevs also apply to Alias. +func Alias(qualifiedName, alias string) ContainerOption { + return aliasAs("alias", qualifiedName, alias) +} + +func aliasAs(kind, qualifiedName, alias string) ContainerOption { + return func(c *Container) (*Container, error) { + if len(alias) == 0 || strings.Contains(alias, ".") { + return nil, fmt.Errorf( + "%s must be non-empty and simple (not qualified): %s=%s", kind, kind, alias) + } + + if qualifiedName[0:1] == "." { + return nil, fmt.Errorf("qualified name must not begin with a leading '.': %s", + qualifiedName) + } + ind := strings.LastIndex(qualifiedName, ".") + if ind <= 0 || ind == len(qualifiedName)-1 { + return nil, fmt.Errorf("%s must refer to a valid qualified name: %s", + kind, qualifiedName) + } + aliasRef, found := c.aliasSet()[alias] + if found { + return nil, fmt.Errorf( + "%s collides with existing reference: name=%s, %s=%s, existing=%s", + kind, qualifiedName, kind, alias, aliasRef) + } + if strings.HasPrefix(c.Name(), alias+".") || c.Name() == alias { + return nil, fmt.Errorf( + "%s collides with container name: name=%s, %s=%s, container=%s", + kind, qualifiedName, kind, alias, c.Name()) + } + if c == nil { + c = &Container{} + } + if c.aliases == nil { + c.aliases = make(map[string]string) + } + c.aliases[alias] = qualifiedName + return c, nil + } +} + +// Name sets the fully-qualified name of the Container. +func Name(name string) ContainerOption { + return func(c *Container) (*Container, error) { + if len(name) > 0 && name[0:1] == "." { + return nil, fmt.Errorf("container name must not contain a leading '.': %s", name) + } + if c.Name() == name { + return c, nil + } + if c == nil { + return &Container{name: name}, nil + } + c.name = name + return c, nil + } +} + +// ToQualifiedName converts an expression AST into a qualified name if possible, with a boolean +// 'found' value that indicates if the conversion is successful. +func ToQualifiedName(e *exprpb.Expr) (string, bool) { + switch e.ExprKind.(type) { + case *exprpb.Expr_IdentExpr: + id := e.GetIdentExpr() + return id.Name, true + case *exprpb.Expr_SelectExpr: + sel := e.GetSelectExpr() + // Test only expressions are not valid as qualified names. + if sel.GetTestOnly() { + return "", false + } + if qual, found := ToQualifiedName(sel.Operand); found { + return qual + "." + sel.Field, true + } + } + return "", false +} diff --git a/vendor/github.com/google/cel-go/common/cost.go b/vendor/github.com/google/cel-go/common/cost.go new file mode 100644 index 0000000000..5e24bd0f47 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/cost.go @@ -0,0 +1,40 @@ +// Copyright 2022 Google LLC +// +// 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. + +package common + +const ( + // SelectAndIdentCost is the cost of an operation that accesses an identifier or performs a select. + SelectAndIdentCost = 1 + + // ConstCost is the cost of an operation that accesses a constant. + ConstCost = 0 + + // ListCreateBaseCost is the base cost of any operation that creates a new list. + ListCreateBaseCost = 10 + + // MapCreateBaseCost is the base cost of any operation that creates a new map. + MapCreateBaseCost = 30 + + // StructCreateBaseCost is the base cost of any operation that creates a new struct. + StructCreateBaseCost = 40 + + // StringTraversalCostFactor is multiplied to a length of a string when computing the cost of traversing the entire + // string once. + StringTraversalCostFactor = 0.1 + + // RegexStringLengthCostFactor is multiplied ot the length of a regex string pattern when computing the cost of + // applying the regex to a string of unit cost. + RegexStringLengthCostFactor = 0.25 +) diff --git a/vendor/github.com/google/cel-go/common/debug/debug.go b/vendor/github.com/google/cel-go/common/debug/debug.go new file mode 100644 index 0000000000..5a4894733d --- /dev/null +++ b/vendor/github.com/google/cel-go/common/debug/debug.go @@ -0,0 +1,305 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Package debug provides tools to print a parsed expression graph and +// adorn each expression element with additional metadata. +package debug + +import ( + "bytes" + "fmt" + "strconv" + "strings" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +// Adorner returns debug metadata that will be tacked on to the string +// representation of an expression. +type Adorner interface { + // GetMetadata for the input context. + GetMetadata(ctx interface{}) string +} + +// Writer manages writing expressions to an internal string. +type Writer interface { + fmt.Stringer + + // Buffer pushes an expression into an internal queue of expressions to + // write to a string. + Buffer(e *exprpb.Expr) +} + +type emptyDebugAdorner struct { +} + +var emptyAdorner Adorner = &emptyDebugAdorner{} + +func (a *emptyDebugAdorner) GetMetadata(e interface{}) string { + return "" +} + +// ToDebugString gives the unadorned string representation of the Expr. +func ToDebugString(e *exprpb.Expr) string { + return ToAdornedDebugString(e, emptyAdorner) +} + +// ToAdornedDebugString gives the adorned string representation of the Expr. +func ToAdornedDebugString(e *exprpb.Expr, adorner Adorner) string { + w := newDebugWriter(adorner) + w.Buffer(e) + return w.String() +} + +// debugWriter is used to print out pretty-printed debug strings. +type debugWriter struct { + adorner Adorner + buffer bytes.Buffer + indent int + lineStart bool +} + +func newDebugWriter(a Adorner) *debugWriter { + return &debugWriter{ + adorner: a, + indent: 0, + lineStart: true, + } +} + +func (w *debugWriter) Buffer(e *exprpb.Expr) { + if e == nil { + return + } + switch e.ExprKind.(type) { + case *exprpb.Expr_ConstExpr: + w.append(formatLiteral(e.GetConstExpr())) + case *exprpb.Expr_IdentExpr: + w.append(e.GetIdentExpr().Name) + case *exprpb.Expr_SelectExpr: + w.appendSelect(e.GetSelectExpr()) + case *exprpb.Expr_CallExpr: + w.appendCall(e.GetCallExpr()) + case *exprpb.Expr_ListExpr: + w.appendList(e.GetListExpr()) + case *exprpb.Expr_StructExpr: + w.appendStruct(e.GetStructExpr()) + case *exprpb.Expr_ComprehensionExpr: + w.appendComprehension(e.GetComprehensionExpr()) + } + w.adorn(e) +} + +func (w *debugWriter) appendSelect(sel *exprpb.Expr_Select) { + w.Buffer(sel.Operand) + w.append(".") + w.append(sel.Field) + if sel.TestOnly { + w.append("~test-only~") + } +} + +func (w *debugWriter) appendCall(call *exprpb.Expr_Call) { + if call.Target != nil { + w.Buffer(call.Target) + w.append(".") + } + w.append(call.Function) + w.append("(") + if len(call.GetArgs()) > 0 { + w.addIndent() + w.appendLine() + for i, arg := range call.Args { + if i > 0 { + w.append(",") + w.appendLine() + } + w.Buffer(arg) + } + w.removeIndent() + w.appendLine() + } + w.append(")") +} + +func (w *debugWriter) appendList(list *exprpb.Expr_CreateList) { + w.append("[") + if len(list.GetElements()) > 0 { + w.appendLine() + w.addIndent() + for i, elem := range list.Elements { + if i > 0 { + w.append(",") + w.appendLine() + } + w.Buffer(elem) + } + w.removeIndent() + w.appendLine() + } + w.append("]") +} + +func (w *debugWriter) appendStruct(obj *exprpb.Expr_CreateStruct) { + if obj.MessageName != "" { + w.appendObject(obj) + } else { + w.appendMap(obj) + } +} + +func (w *debugWriter) appendObject(obj *exprpb.Expr_CreateStruct) { + w.append(obj.MessageName) + w.append("{") + if len(obj.Entries) > 0 { + w.appendLine() + w.addIndent() + for i, entry := range obj.Entries { + if i > 0 { + w.append(",") + w.appendLine() + } + w.append(entry.GetFieldKey()) + w.append(":") + w.Buffer(entry.Value) + w.adorn(entry) + } + w.removeIndent() + w.appendLine() + } + w.append("}") +} + +func (w *debugWriter) appendMap(obj *exprpb.Expr_CreateStruct) { + w.append("{") + if len(obj.Entries) > 0 { + w.appendLine() + w.addIndent() + for i, entry := range obj.Entries { + if i > 0 { + w.append(",") + w.appendLine() + } + w.Buffer(entry.GetMapKey()) + w.append(":") + w.Buffer(entry.Value) + w.adorn(entry) + } + w.removeIndent() + w.appendLine() + } + w.append("}") +} + +func (w *debugWriter) appendComprehension(comprehension *exprpb.Expr_Comprehension) { + w.append("__comprehension__(") + w.addIndent() + w.appendLine() + w.append("// Variable") + w.appendLine() + w.append(comprehension.IterVar) + w.append(",") + w.appendLine() + w.append("// Target") + w.appendLine() + w.Buffer(comprehension.IterRange) + w.append(",") + w.appendLine() + w.append("// Accumulator") + w.appendLine() + w.append(comprehension.AccuVar) + w.append(",") + w.appendLine() + w.append("// Init") + w.appendLine() + w.Buffer(comprehension.AccuInit) + w.append(",") + w.appendLine() + w.append("// LoopCondition") + w.appendLine() + w.Buffer(comprehension.LoopCondition) + w.append(",") + w.appendLine() + w.append("// LoopStep") + w.appendLine() + w.Buffer(comprehension.LoopStep) + w.append(",") + w.appendLine() + w.append("// Result") + w.appendLine() + w.Buffer(comprehension.Result) + w.append(")") + w.removeIndent() +} + +func formatLiteral(c *exprpb.Constant) string { + switch c.ConstantKind.(type) { + case *exprpb.Constant_BoolValue: + return fmt.Sprintf("%t", c.GetBoolValue()) + case *exprpb.Constant_BytesValue: + return fmt.Sprintf("b\"%s\"", string(c.GetBytesValue())) + case *exprpb.Constant_DoubleValue: + return fmt.Sprintf("%v", c.GetDoubleValue()) + case *exprpb.Constant_Int64Value: + return fmt.Sprintf("%d", c.GetInt64Value()) + case *exprpb.Constant_StringValue: + return strconv.Quote(c.GetStringValue()) + case *exprpb.Constant_Uint64Value: + return fmt.Sprintf("%du", c.GetUint64Value()) + case *exprpb.Constant_NullValue: + return "null" + default: + panic("Unknown constant type") + } +} + +func (w *debugWriter) append(s string) { + w.doIndent() + w.buffer.WriteString(s) +} + +func (w *debugWriter) appendFormat(f string, args ...interface{}) { + w.append(fmt.Sprintf(f, args...)) +} + +func (w *debugWriter) doIndent() { + if w.lineStart { + w.lineStart = false + w.buffer.WriteString(strings.Repeat(" ", w.indent)) + } +} + +func (w *debugWriter) adorn(e interface{}) { + w.append(w.adorner.GetMetadata(e)) +} + +func (w *debugWriter) appendLine() { + w.buffer.WriteString("\n") + w.lineStart = true +} + +func (w *debugWriter) addIndent() { + w.indent++ +} + +func (w *debugWriter) removeIndent() { + w.indent-- + if w.indent < 0 { + panic("negative indent") + } +} + +func (w *debugWriter) String() string { + return w.buffer.String() +} diff --git a/vendor/github.com/google/cel-go/common/doc.go b/vendor/github.com/google/cel-go/common/doc.go new file mode 100644 index 0000000000..5362fdfe4b --- /dev/null +++ b/vendor/github.com/google/cel-go/common/doc.go @@ -0,0 +1,17 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Package common defines types and utilities common to expression parsing, +// checking, and interpretation +package common diff --git a/vendor/github.com/google/cel-go/common/error.go b/vendor/github.com/google/cel-go/common/error.go new file mode 100644 index 0000000000..bfe93d9737 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/error.go @@ -0,0 +1,70 @@ +// Copyright 2018 Google LLC +// +// 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. + +package common + +import ( + "fmt" + "strings" + "unicode/utf8" + + "golang.org/x/text/width" +) + +// Error type which references a location within source and a message. +type Error struct { + Location Location + Message string +} + +const ( + dot = "." + ind = "^" +) + +var ( + wideDot = width.Widen.String(dot) + wideInd = width.Widen.String(ind) +) + +// ToDisplayString decorates the error message with the source location. +func (e *Error) ToDisplayString(source Source) string { + var result = fmt.Sprintf("ERROR: %s:%d:%d: %s", + source.Description(), + e.Location.Line(), + e.Location.Column()+1, // add one to the 0-based column for display + e.Message) + if snippet, found := source.Snippet(e.Location.Line()); found { + snippet := strings.Replace(snippet, "\t", " ", -1) + srcLine := "\n | " + snippet + var bytes = []byte(snippet) + var indLine = "\n | " + for i := 0; i < e.Location.Column() && len(bytes) > 0; i++ { + _, sz := utf8.DecodeRune(bytes) + bytes = bytes[sz:] + if sz > 1 { + indLine += wideDot + } else { + indLine += dot + } + } + if _, sz := utf8.DecodeRune(bytes); sz > 1 { + indLine += wideInd + } else { + indLine += ind + } + result += srcLine + indLine + } + return result +} diff --git a/vendor/github.com/google/cel-go/common/errors.go b/vendor/github.com/google/cel-go/common/errors.go new file mode 100644 index 0000000000..6be0fdef32 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/errors.go @@ -0,0 +1,74 @@ +// Copyright 2018 Google LLC +// +// 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. + +package common + +import ( + "fmt" + "sort" +) + +// Errors type which contains a list of errors observed during parsing. +type Errors struct { + errors []Error + source Source +} + +// NewErrors creates a new instance of the Errors type. +func NewErrors(source Source) *Errors { + return &Errors{ + errors: []Error{}, + source: source} +} + +// ReportError records an error at a source location. +func (e *Errors) ReportError(l Location, format string, args ...interface{}) { + err := Error{ + Location: l, + Message: fmt.Sprintf(format, args...), + } + e.errors = append(e.errors, err) +} + +// GetErrors returns the list of observed errors. +func (e *Errors) GetErrors() []Error { + return e.errors[:] +} + +// Append takes an Errors object as input creates a new Errors object with the current and input +// errors. +func (e *Errors) Append(errs []Error) *Errors { + return &Errors{ + errors: append(e.errors, errs...), + source: e.source, + } +} + +// ToDisplayString returns the error set to a newline delimited string. +func (e *Errors) ToDisplayString() string { + var result = "" + sort.SliceStable(e.errors, func(i, j int) bool { + ei := e.errors[i].Location + ej := e.errors[j].Location + return ei.Line() < ej.Line() || + (ei.Line() == ej.Line() && ei.Column() < ej.Column()) + }) + for i, err := range e.errors { + if i >= 1 { + result += "\n" + } + result += err.ToDisplayString(e.source) + } + return result +} diff --git a/vendor/github.com/google/cel-go/common/location.go b/vendor/github.com/google/cel-go/common/location.go new file mode 100644 index 0000000000..ec3fa7cb50 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/location.go @@ -0,0 +1,51 @@ +// Copyright 2018 Google LLC +// +// 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. + +package common + +// Location interface to represent a location within Source. +type Location interface { + Line() int // 1-based line number within source. + Column() int // 0-based column number within source. +} + +// SourceLocation helper type to manually construct a location. +type SourceLocation struct { + line int + column int +} + +var ( + // Location implements the SourceLocation interface. + _ Location = &SourceLocation{} + // NoLocation is a particular illegal location. + NoLocation = &SourceLocation{-1, -1} +) + +// NewLocation creates a new location. +func NewLocation(line, column int) Location { + return &SourceLocation{ + line: line, + column: column} +} + +// Line returns the 1-based line of the location. +func (l *SourceLocation) Line() int { + return l.line +} + +// Column returns the 0-based column number of the location. +func (l *SourceLocation) Column() int { + return l.column +} diff --git a/vendor/github.com/google/cel-go/common/operators/operators.go b/vendor/github.com/google/cel-go/common/operators/operators.go new file mode 100644 index 0000000000..8a2e909452 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/operators/operators.go @@ -0,0 +1,143 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Package operators defines the internal function names of operators. +// +// All operators in the expression language are modelled as function calls. +package operators + +// String "names" for CEL operators. +const ( + // Symbolic operators. + Conditional = "_?_:_" + LogicalAnd = "_&&_" + LogicalOr = "_||_" + LogicalNot = "!_" + Equals = "_==_" + NotEquals = "_!=_" + Less = "_<_" + LessEquals = "_<=_" + Greater = "_>_" + GreaterEquals = "_>=_" + Add = "_+_" + Subtract = "_-_" + Multiply = "_*_" + Divide = "_/_" + Modulo = "_%_" + Negate = "-_" + Index = "_[_]" + + // Macros, must have a valid identifier. + Has = "has" + All = "all" + Exists = "exists" + ExistsOne = "exists_one" + Map = "map" + Filter = "filter" + + // Named operators, must not have be valid identifiers. + NotStrictlyFalse = "@not_strictly_false" + In = "@in" + + // Deprecated: named operators with valid identifiers. + OldNotStrictlyFalse = "__not_strictly_false__" + OldIn = "_in_" +) + +var ( + operators = map[string]string{ + "+": Add, + "/": Divide, + "==": Equals, + ">": Greater, + ">=": GreaterEquals, + "in": In, + "<": Less, + "<=": LessEquals, + "%": Modulo, + "*": Multiply, + "!=": NotEquals, + "-": Subtract, + } + // operatorMap of the operator symbol which refers to a struct containing the display name, + // if applicable, the operator precedence, and the arity. + // + // If the symbol does not have a display name listed in the map, it is only because it requires + // special casing to render properly as text. + operatorMap = map[string]struct { + displayName string + precedence int + arity int + }{ + Conditional: {displayName: "", precedence: 8, arity: 3}, + LogicalOr: {displayName: "||", precedence: 7, arity: 2}, + LogicalAnd: {displayName: "&&", precedence: 6, arity: 2}, + Equals: {displayName: "==", precedence: 5, arity: 2}, + Greater: {displayName: ">", precedence: 5, arity: 2}, + GreaterEquals: {displayName: ">=", precedence: 5, arity: 2}, + In: {displayName: "in", precedence: 5, arity: 2}, + Less: {displayName: "<", precedence: 5, arity: 2}, + LessEquals: {displayName: "<=", precedence: 5, arity: 2}, + NotEquals: {displayName: "!=", precedence: 5, arity: 2}, + OldIn: {displayName: "in", precedence: 5, arity: 2}, + Add: {displayName: "+", precedence: 4, arity: 2}, + Subtract: {displayName: "-", precedence: 4, arity: 2}, + Divide: {displayName: "/", precedence: 3, arity: 2}, + Modulo: {displayName: "%", precedence: 3, arity: 2}, + Multiply: {displayName: "*", precedence: 3, arity: 2}, + LogicalNot: {displayName: "!", precedence: 2, arity: 1}, + Negate: {displayName: "-", precedence: 2, arity: 1}, + Index: {displayName: "", precedence: 1, arity: 2}, + } +) + +// Find the internal function name for an operator, if the input text is one. +func Find(text string) (string, bool) { + op, found := operators[text] + return op, found +} + +// FindReverse returns the unmangled, text representation of the operator. +func FindReverse(symbol string) (string, bool) { + op, found := operatorMap[symbol] + if !found { + return "", false + } + return op.displayName, true +} + +// FindReverseBinaryOperator returns the unmangled, text representation of a binary operator. +// +// If the symbol does refer to an operator, but the operator does not have a display name the +// result is false. +func FindReverseBinaryOperator(symbol string) (string, bool) { + op, found := operatorMap[symbol] + if !found || op.arity != 2 { + return "", false + } + if op.displayName == "" { + return "", false + } + return op.displayName, true +} + +// Precedence returns the operator precedence, where the higher the number indicates +// higher precedence operations. +func Precedence(symbol string) int { + op, found := operatorMap[symbol] + if !found { + return 0 + } + return op.precedence +} diff --git a/vendor/github.com/google/cel-go/common/overloads/overloads.go b/vendor/github.com/google/cel-go/common/overloads/overloads.go new file mode 100644 index 0000000000..9ebaf6fabf --- /dev/null +++ b/vendor/github.com/google/cel-go/common/overloads/overloads.go @@ -0,0 +1,317 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Package overloads defines the internal overload identifiers for function and +// operator overloads. +package overloads + +// Boolean logic overloads +const ( + Conditional = "conditional" + LogicalAnd = "logical_and" + LogicalOr = "logical_or" + LogicalNot = "logical_not" + NotStrictlyFalse = "not_strictly_false" + Equals = "equals" + NotEquals = "not_equals" + LessBool = "less_bool" + LessInt64 = "less_int64" + LessInt64Double = "less_int64_double" + LessInt64Uint64 = "less_int64_uint64" + LessUint64 = "less_uint64" + LessUint64Double = "less_uint64_double" + LessUint64Int64 = "less_uint64_int64" + LessDouble = "less_double" + LessDoubleInt64 = "less_double_int64" + LessDoubleUint64 = "less_double_uint64" + LessString = "less_string" + LessBytes = "less_bytes" + LessTimestamp = "less_timestamp" + LessDuration = "less_duration" + LessEqualsBool = "less_equals_bool" + LessEqualsInt64 = "less_equals_int64" + LessEqualsInt64Double = "less_equals_int64_double" + LessEqualsInt64Uint64 = "less_equals_int64_uint64" + LessEqualsUint64 = "less_equals_uint64" + LessEqualsUint64Double = "less_equals_uint64_double" + LessEqualsUint64Int64 = "less_equals_uint64_int64" + LessEqualsDouble = "less_equals_double" + LessEqualsDoubleInt64 = "less_equals_double_int64" + LessEqualsDoubleUint64 = "less_equals_double_uint64" + LessEqualsString = "less_equals_string" + LessEqualsBytes = "less_equals_bytes" + LessEqualsTimestamp = "less_equals_timestamp" + LessEqualsDuration = "less_equals_duration" + GreaterBool = "greater_bool" + GreaterInt64 = "greater_int64" + GreaterInt64Double = "greater_int64_double" + GreaterInt64Uint64 = "greater_int64_uint64" + GreaterUint64 = "greater_uint64" + GreaterUint64Double = "greater_uint64_double" + GreaterUint64Int64 = "greater_uint64_int64" + GreaterDouble = "greater_double" + GreaterDoubleInt64 = "greater_double_int64" + GreaterDoubleUint64 = "greater_double_uint64" + GreaterString = "greater_string" + GreaterBytes = "greater_bytes" + GreaterTimestamp = "greater_timestamp" + GreaterDuration = "greater_duration" + GreaterEqualsBool = "greater_equals_bool" + GreaterEqualsInt64 = "greater_equals_int64" + GreaterEqualsInt64Double = "greater_equals_int64_double" + GreaterEqualsInt64Uint64 = "greater_equals_int64_uint64" + GreaterEqualsUint64 = "greater_equals_uint64" + GreaterEqualsUint64Double = "greater_equals_uint64_double" + GreaterEqualsUint64Int64 = "greater_equals_uint64_int64" + GreaterEqualsDouble = "greater_equals_double" + GreaterEqualsDoubleInt64 = "greater_equals_double_int64" + GreaterEqualsDoubleUint64 = "greater_equals_double_uint64" + GreaterEqualsString = "greater_equals_string" + GreaterEqualsBytes = "greater_equals_bytes" + GreaterEqualsTimestamp = "greater_equals_timestamp" + GreaterEqualsDuration = "greater_equals_duration" +) + +// Math overloads +const ( + AddInt64 = "add_int64" + AddUint64 = "add_uint64" + AddDouble = "add_double" + AddString = "add_string" + AddBytes = "add_bytes" + AddList = "add_list" + AddTimestampDuration = "add_timestamp_duration" + AddDurationTimestamp = "add_duration_timestamp" + AddDurationDuration = "add_duration_duration" + SubtractInt64 = "subtract_int64" + SubtractUint64 = "subtract_uint64" + SubtractDouble = "subtract_double" + SubtractTimestampTimestamp = "subtract_timestamp_timestamp" + SubtractTimestampDuration = "subtract_timestamp_duration" + SubtractDurationDuration = "subtract_duration_duration" + MultiplyInt64 = "multiply_int64" + MultiplyUint64 = "multiply_uint64" + MultiplyDouble = "multiply_double" + DivideInt64 = "divide_int64" + DivideUint64 = "divide_uint64" + DivideDouble = "divide_double" + ModuloInt64 = "modulo_int64" + ModuloUint64 = "modulo_uint64" + NegateInt64 = "negate_int64" + NegateDouble = "negate_double" +) + +// Index overloads +const ( + IndexList = "index_list" + IndexMap = "index_map" + IndexMessage = "index_message" // TODO: introduce concept of types.Message +) + +// In operators +const ( + DeprecatedIn = "in" + InList = "in_list" + InMap = "in_map" + InMessage = "in_message" // TODO: introduce concept of types.Message +) + +// Size overloads +const ( + Size = "size" + SizeString = "size_string" + SizeBytes = "size_bytes" + SizeList = "size_list" + SizeMap = "size_map" + SizeStringInst = "string_size" + SizeBytesInst = "bytes_size" + SizeListInst = "list_size" + SizeMapInst = "map_size" +) + +// String function names. +const ( + Contains = "contains" + EndsWith = "endsWith" + Matches = "matches" + StartsWith = "startsWith" +) + +// String function overload names. +const ( + ContainsString = "contains_string" + EndsWithString = "ends_with_string" + MatchesString = "matches_string" + StartsWithString = "starts_with_string" +) + +// Time-based functions. +const ( + TimeGetFullYear = "getFullYear" + TimeGetMonth = "getMonth" + TimeGetDayOfYear = "getDayOfYear" + TimeGetDate = "getDate" + TimeGetDayOfMonth = "getDayOfMonth" + TimeGetDayOfWeek = "getDayOfWeek" + TimeGetHours = "getHours" + TimeGetMinutes = "getMinutes" + TimeGetSeconds = "getSeconds" + TimeGetMilliseconds = "getMilliseconds" +) + +// Timestamp overloads for time functions without timezones. +const ( + TimestampToYear = "timestamp_to_year" + TimestampToMonth = "timestamp_to_month" + TimestampToDayOfYear = "timestamp_to_day_of_year" + TimestampToDayOfMonthZeroBased = "timestamp_to_day_of_month" + TimestampToDayOfMonthOneBased = "timestamp_to_day_of_month_1_based" + TimestampToDayOfWeek = "timestamp_to_day_of_week" + TimestampToHours = "timestamp_to_hours" + TimestampToMinutes = "timestamp_to_minutes" + TimestampToSeconds = "timestamp_to_seconds" + TimestampToMilliseconds = "timestamp_to_milliseconds" +) + +// Timestamp overloads for time functions with timezones. +const ( + TimestampToYearWithTz = "timestamp_to_year_with_tz" + TimestampToMonthWithTz = "timestamp_to_month_with_tz" + TimestampToDayOfYearWithTz = "timestamp_to_day_of_year_with_tz" + TimestampToDayOfMonthZeroBasedWithTz = "timestamp_to_day_of_month_with_tz" + TimestampToDayOfMonthOneBasedWithTz = "timestamp_to_day_of_month_1_based_with_tz" + TimestampToDayOfWeekWithTz = "timestamp_to_day_of_week_with_tz" + TimestampToHoursWithTz = "timestamp_to_hours_with_tz" + TimestampToMinutesWithTz = "timestamp_to_minutes_with_tz" + TimestampToSecondsWithTz = "timestamp_to_seconds_tz" + TimestampToMillisecondsWithTz = "timestamp_to_milliseconds_with_tz" +) + +// Duration overloads for time functions. +const ( + DurationToHours = "duration_to_hours" + DurationToMinutes = "duration_to_minutes" + DurationToSeconds = "duration_to_seconds" + DurationToMilliseconds = "duration_to_milliseconds" +) + +// Type conversion methods and overloads +const ( + TypeConvertInt = "int" + TypeConvertUint = "uint" + TypeConvertDouble = "double" + TypeConvertBool = "bool" + TypeConvertString = "string" + TypeConvertBytes = "bytes" + TypeConvertTimestamp = "timestamp" + TypeConvertDuration = "duration" + TypeConvertType = "type" + TypeConvertDyn = "dyn" +) + +// Int conversion functions. +const ( + IntToInt = "int64_to_int64" + UintToInt = "uint64_to_int64" + DoubleToInt = "double_to_int64" + StringToInt = "string_to_int64" + TimestampToInt = "timestamp_to_int64" + DurationToInt = "duration_to_int64" +) + +// Uint conversion functions. +const ( + UintToUint = "uint64_to_uint64" + IntToUint = "int64_to_uint64" + DoubleToUint = "double_to_uint64" + StringToUint = "string_to_uint64" +) + +// Double conversion functions. +const ( + DoubleToDouble = "double_to_double" + IntToDouble = "int64_to_double" + UintToDouble = "uint64_to_double" + StringToDouble = "string_to_double" +) + +// Bool conversion functions. +const ( + BoolToBool = "bool_to_bool" + StringToBool = "string_to_bool" +) + +// Bytes conversion functions. +const ( + BytesToBytes = "bytes_to_bytes" + StringToBytes = "string_to_bytes" +) + +// String conversion functions. +const ( + StringToString = "string_to_string" + BoolToString = "bool_to_string" + IntToString = "int64_to_string" + UintToString = "uint64_to_string" + DoubleToString = "double_to_string" + BytesToString = "bytes_to_string" + TimestampToString = "timestamp_to_string" + DurationToString = "duration_to_string" +) + +// Timestamp conversion functions +const ( + TimestampToTimestamp = "timestamp_to_timestamp" + StringToTimestamp = "string_to_timestamp" + IntToTimestamp = "int64_to_timestamp" +) + +// Convert duration from string +const ( + DurationToDuration = "duration_to_duration" + StringToDuration = "string_to_duration" + IntToDuration = "int64_to_duration" +) + +// Convert to dyn +const ( + ToDyn = "to_dyn" +) + +// Comprehensions helper methods, not directly accessible via a developer. +const ( + Iterator = "@iterator" + HasNext = "@hasNext" + Next = "@next" +) + +// IsTypeConversionFunction returns whether the input function is a standard library type +// conversion function. +func IsTypeConversionFunction(function string) bool { + switch function { + case TypeConvertBool, + TypeConvertBytes, + TypeConvertDouble, + TypeConvertDuration, + TypeConvertDyn, + TypeConvertInt, + TypeConvertString, + TypeConvertTimestamp, + TypeConvertType, + TypeConvertUint: + return true + default: + return false + } +} diff --git a/vendor/github.com/google/cel-go/common/runes/buffer.go b/vendor/github.com/google/cel-go/common/runes/buffer.go new file mode 100644 index 0000000000..50aac0b273 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/runes/buffer.go @@ -0,0 +1,194 @@ +// Copyright 2021 Google LLC +// +// 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. + +// Package runes provides interfaces and utilities for working with runes. +package runes + +import ( + "strings" + "unicode/utf8" +) + +// Buffer is an interface for accessing a contiguous array of code points. +type Buffer interface { + Get(i int) rune + Slice(i, j int) string + Len() int +} + +type emptyBuffer struct{} + +func (e *emptyBuffer) Get(i int) rune { + panic("slice index out of bounds") +} + +func (e *emptyBuffer) Slice(i, j int) string { + if i != 0 || i != j { + panic("slice index out of bounds") + } + return "" +} + +func (e *emptyBuffer) Len() int { + return 0 +} + +var _ Buffer = &emptyBuffer{} + +// asciiBuffer is an implementation for an array of code points that contain code points only from +// the ASCII character set. +type asciiBuffer struct { + arr []byte +} + +func (a *asciiBuffer) Get(i int) rune { + return rune(uint32(a.arr[i])) +} + +func (a *asciiBuffer) Slice(i, j int) string { + return string(a.arr[i:j]) +} + +func (a *asciiBuffer) Len() int { + return len(a.arr) +} + +var _ Buffer = &asciiBuffer{} + +// basicBuffer is an implementation for an array of code points that contain code points from both +// the Latin-1 character set and Basic Multilingual Plane. +type basicBuffer struct { + arr []uint16 +} + +func (b *basicBuffer) Get(i int) rune { + return rune(uint32(b.arr[i])) +} + +func (b *basicBuffer) Slice(i, j int) string { + var str strings.Builder + str.Grow((j - i) * 3) // Worst case encoding size for 0xffff is 3. + for ; i < j; i++ { + str.WriteRune(rune(uint32(b.arr[i]))) + } + return str.String() +} + +func (b *basicBuffer) Len() int { + return len(b.arr) +} + +var _ Buffer = &basicBuffer{} + +// supplementalBuffer is an implementation for an array of code points that contain code points from +// the Latin-1 character set, Basic Multilingual Plane, or the Supplemental Multilingual Plane. +type supplementalBuffer struct { + arr []rune +} + +func (s *supplementalBuffer) Get(i int) rune { + return rune(uint32(s.arr[i])) +} + +func (s *supplementalBuffer) Slice(i, j int) string { + return string(s.arr[i:j]) +} + +func (s *supplementalBuffer) Len() int { + return len(s.arr) +} + +var _ Buffer = &supplementalBuffer{} + +var nilBuffer = &emptyBuffer{} + +// NewBuffer returns an efficient implementation of Buffer for the given text based on the ranges of +// the encoded code points contained within. +// +// Code points are represented as an array of byte, uint16, or rune. This approach ensures that +// each index represents a code point by itself without needing to use an array of rune. At first +// we assume all code points are less than or equal to '\u007f'. If this holds true, the +// underlying storage is a byte array containing only ASCII characters. If we encountered a code +// point above this range but less than or equal to '\uffff' we allocate a uint16 array, copy the +// elements of previous byte array to the uint16 array, and continue. If this holds true, the +// underlying storage is a uint16 array containing only Unicode characters in the Basic Multilingual +// Plane. If we encounter a code point above '\uffff' we allocate an rune array, copy the previous +// elements of the byte or uint16 array, and continue. The underlying storage is an rune array +// containing any Unicode character. +func NewBuffer(data string) Buffer { + if len(data) == 0 { + return nilBuffer + } + var ( + idx = 0 + buf8 = make([]byte, 0, len(data)) + buf16 []uint16 + buf32 []rune + ) + for idx < len(data) { + r, s := utf8.DecodeRuneInString(data[idx:]) + idx += s + if r < utf8.RuneSelf { + buf8 = append(buf8, byte(r)) + continue + } + if r <= 0xffff { + buf16 = make([]uint16, len(buf8), len(data)) + for i, v := range buf8 { + buf16[i] = uint16(v) + } + buf8 = nil + buf16 = append(buf16, uint16(r)) + goto copy16 + } + buf32 = make([]rune, len(buf8), len(data)) + for i, v := range buf8 { + buf32[i] = rune(uint32(v)) + } + buf8 = nil + buf32 = append(buf32, r) + goto copy32 + } + return &asciiBuffer{ + arr: buf8, + } +copy16: + for idx < len(data) { + r, s := utf8.DecodeRuneInString(data[idx:]) + idx += s + if r <= 0xffff { + buf16 = append(buf16, uint16(r)) + continue + } + buf32 = make([]rune, len(buf16), len(data)) + for i, v := range buf16 { + buf32[i] = rune(uint32(v)) + } + buf16 = nil + buf32 = append(buf32, r) + goto copy32 + } + return &basicBuffer{ + arr: buf16, + } +copy32: + for idx < len(data) { + r, s := utf8.DecodeRuneInString(data[idx:]) + idx += s + buf32 = append(buf32, r) + } + return &supplementalBuffer{ + arr: buf32, + } +} diff --git a/vendor/github.com/google/cel-go/common/source.go b/vendor/github.com/google/cel-go/common/source.go new file mode 100644 index 0000000000..52377d9308 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/source.go @@ -0,0 +1,186 @@ +// Copyright 2018 Google LLC +// +// 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. + +package common + +import ( + "strings" + "unicode/utf8" + + "github.com/google/cel-go/common/runes" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +// Source interface for filter source contents. +type Source interface { + // Content returns the source content represented as a string. + // Examples contents are the single file contents, textbox field, + // or url parameter. + Content() string + + // Description gives a brief description of the source. + // Example descriptions are a file name or ui element. + Description() string + + // LineOffsets gives the character offsets at which lines occur. + // The zero-th entry should refer to the break between the first + // and second line, or EOF if there is only one line of source. + LineOffsets() []int32 + + // LocationOffset translates a Location to an offset. + // Given the line and column of the Location returns the + // Location's character offset in the Source, and a bool + // indicating whether the Location was found. + LocationOffset(location Location) (int32, bool) + + // OffsetLocation translates a character offset to a Location, or + // false if the conversion was not feasible. + OffsetLocation(offset int32) (Location, bool) + + // NewLocation takes an input line and column and produces a Location. + // The default behavior is to treat the line and column as absolute, + // but concrete derivations may use this method to convert a relative + // line and column position into an absolute location. + NewLocation(line, col int) Location + + // Snippet returns a line of content and whether the line was found. + Snippet(line int) (string, bool) +} + +// The sourceImpl type implementation of the Source interface. +type sourceImpl struct { + runes.Buffer + description string + lineOffsets []int32 + idOffsets map[int64]int32 +} + +var _ runes.Buffer = &sourceImpl{} + +// TODO(jimlarson) "Character offsets" should index the code points +// within the UTF-8 encoded string. It currently indexes bytes. +// Can be accomplished by using rune[] instead of string for contents. + +// NewTextSource creates a new Source from the input text string. +func NewTextSource(text string) Source { + return NewStringSource(text, "") +} + +// NewStringSource creates a new Source from the given contents and description. +func NewStringSource(contents string, description string) Source { + // Compute line offsets up front as they are referred to frequently. + lines := strings.Split(contents, "\n") + offsets := make([]int32, len(lines)) + var offset int32 + for i, line := range lines { + offset = offset + int32(utf8.RuneCountInString(line)) + 1 + offsets[int32(i)] = offset + } + return &sourceImpl{ + Buffer: runes.NewBuffer(contents), + description: description, + lineOffsets: offsets, + idOffsets: map[int64]int32{}, + } +} + +// NewInfoSource creates a new Source from a SourceInfo. +func NewInfoSource(info *exprpb.SourceInfo) Source { + return &sourceImpl{ + Buffer: runes.NewBuffer(""), + description: info.GetLocation(), + lineOffsets: info.GetLineOffsets(), + idOffsets: info.GetPositions(), + } +} + +// Content implements the Source interface method. +func (s *sourceImpl) Content() string { + return s.Slice(0, s.Len()) +} + +// Description implements the Source interface method. +func (s *sourceImpl) Description() string { + return s.description +} + +// LineOffsets implements the Source interface method. +func (s *sourceImpl) LineOffsets() []int32 { + return s.lineOffsets +} + +// LocationOffset implements the Source interface method. +func (s *sourceImpl) LocationOffset(location Location) (int32, bool) { + if lineOffset, found := s.findLineOffset(location.Line()); found { + return lineOffset + int32(location.Column()), true + } + return -1, false +} + +// NewLocation implements the Source interface method. +func (s *sourceImpl) NewLocation(line, col int) Location { + return NewLocation(line, col) +} + +// OffsetLocation implements the Source interface method. +func (s *sourceImpl) OffsetLocation(offset int32) (Location, bool) { + line, lineOffset := s.findLine(offset) + return NewLocation(int(line), int(offset-lineOffset)), true +} + +// Snippet implements the Source interface method. +func (s *sourceImpl) Snippet(line int) (string, bool) { + charStart, found := s.findLineOffset(line) + if !found || s.Len() == 0 { + return "", false + } + charEnd, found := s.findLineOffset(line + 1) + if found { + return s.Slice(int(charStart), int(charEnd-1)), true + } + return s.Slice(int(charStart), s.Len()), true +} + +// findLineOffset returns the offset where the (1-indexed) line begins, +// or false if line doesn't exist. +func (s *sourceImpl) findLineOffset(line int) (int32, bool) { + if line == 1 { + return 0, true + } + if line > 1 && line <= int(len(s.lineOffsets)) { + offset := s.lineOffsets[line-2] + return offset, true + } + return -1, false +} + +// findLine finds the line that contains the given character offset and +// returns the line number and offset of the beginning of that line. +// Note that the last line is treated as if it contains all offsets +// beyond the end of the actual source. +func (s *sourceImpl) findLine(characterOffset int32) (int32, int32) { + var line int32 = 1 + for _, lineOffset := range s.lineOffsets { + if lineOffset > characterOffset { + break + } else { + line++ + } + } + if line == 1 { + return line, 0 + } + return line, s.lineOffsets[line-2] +} diff --git a/vendor/github.com/google/cel-go/common/types/any_value.go b/vendor/github.com/google/cel-go/common/types/any_value.go new file mode 100644 index 0000000000..cda0f13acf --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/any_value.go @@ -0,0 +1,24 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "reflect" + + anypb "google.golang.org/protobuf/types/known/anypb" +) + +// anyValueType constant representing the reflected type of google.protobuf.Any. +var anyValueType = reflect.TypeOf(&anypb.Any{}) diff --git a/vendor/github.com/google/cel-go/common/types/bool.go b/vendor/github.com/google/cel-go/common/types/bool.go new file mode 100644 index 0000000000..749cc24854 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/bool.go @@ -0,0 +1,140 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "fmt" + "reflect" + "strconv" + + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + + anypb "google.golang.org/protobuf/types/known/anypb" + structpb "google.golang.org/protobuf/types/known/structpb" + wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" +) + +// Bool type that implements ref.Val and supports comparison and negation. +type Bool bool + +var ( + // BoolType singleton. + BoolType = NewTypeValue("bool", + traits.ComparerType, + traits.NegatorType) + + // boolWrapperType golang reflected type for protobuf bool wrapper type. + boolWrapperType = reflect.TypeOf(&wrapperspb.BoolValue{}) +) + +// Boolean constants +const ( + False = Bool(false) + True = Bool(true) +) + +// Compare implements the traits.Comparer interface method. +func (b Bool) Compare(other ref.Val) ref.Val { + otherBool, ok := other.(Bool) + if !ok { + return ValOrErr(other, "no such overload") + } + if b == otherBool { + return IntZero + } + if !b && otherBool { + return IntNegOne + } + return IntOne +} + +// ConvertToNative implements the ref.Val interface method. +func (b Bool) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + switch typeDesc.Kind() { + case reflect.Bool: + return reflect.ValueOf(b).Convert(typeDesc).Interface(), nil + case reflect.Ptr: + switch typeDesc { + case anyValueType: + // Primitives must be wrapped to a wrapperspb.BoolValue before being packed into an Any. + return anypb.New(wrapperspb.Bool(bool(b))) + case boolWrapperType: + // Convert the bool to a wrapperspb.BoolValue. + return wrapperspb.Bool(bool(b)), nil + case jsonValueType: + // Return the bool as a new structpb.Value. + return structpb.NewBoolValue(bool(b)), nil + default: + if typeDesc.Elem().Kind() == reflect.Bool { + p := bool(b) + return &p, nil + } + } + case reflect.Interface: + bv := b.Value() + if reflect.TypeOf(bv).Implements(typeDesc) { + return bv, nil + } + if reflect.TypeOf(b).Implements(typeDesc) { + return b, nil + } + } + return nil, fmt.Errorf("type conversion error from bool to '%v'", typeDesc) +} + +// ConvertToType implements the ref.Val interface method. +func (b Bool) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case StringType: + return String(strconv.FormatBool(bool(b))) + case BoolType: + return b + case TypeType: + return BoolType + } + return NewErr("type conversion error from '%v' to '%v'", BoolType, typeVal) +} + +// Equal implements the ref.Val interface method. +func (b Bool) Equal(other ref.Val) ref.Val { + otherBool, ok := other.(Bool) + return Bool(ok && b == otherBool) +} + +// Negate implements the traits.Negater interface method. +func (b Bool) Negate() ref.Val { + return !b +} + +// Type implements the ref.Val interface method. +func (b Bool) Type() ref.Type { + return BoolType +} + +// Value implements the ref.Val interface method. +func (b Bool) Value() interface{} { + return bool(b) +} + +// IsBool returns whether the input ref.Val or ref.Type is equal to BoolType. +func IsBool(elem ref.Val) bool { + switch elem.(type) { + case Bool: + return true + default: + return false + } +} diff --git a/vendor/github.com/google/cel-go/common/types/bytes.go b/vendor/github.com/google/cel-go/common/types/bytes.go new file mode 100644 index 0000000000..3575717ec7 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/bytes.go @@ -0,0 +1,132 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "bytes" + "encoding/base64" + "fmt" + "reflect" + "unicode/utf8" + + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + + anypb "google.golang.org/protobuf/types/known/anypb" + structpb "google.golang.org/protobuf/types/known/structpb" + wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" +) + +// Bytes type that implements ref.Val and supports add, compare, and size +// operations. +type Bytes []byte + +var ( + // BytesType singleton. + BytesType = NewTypeValue("bytes", + traits.AdderType, + traits.ComparerType, + traits.SizerType) + + // byteWrapperType golang reflected type for protobuf bytes wrapper type. + byteWrapperType = reflect.TypeOf(&wrapperspb.BytesValue{}) +) + +// Add implements traits.Adder interface method by concatenating byte sequences. +func (b Bytes) Add(other ref.Val) ref.Val { + otherBytes, ok := other.(Bytes) + if !ok { + return ValOrErr(other, "no such overload") + } + return append(b, otherBytes...) +} + +// Compare implements traits.Comparer interface method by lexicographic ordering. +func (b Bytes) Compare(other ref.Val) ref.Val { + otherBytes, ok := other.(Bytes) + if !ok { + return ValOrErr(other, "no such overload") + } + return Int(bytes.Compare(b, otherBytes)) +} + +// ConvertToNative implements the ref.Val interface method. +func (b Bytes) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + switch typeDesc.Kind() { + case reflect.Array, reflect.Slice: + return reflect.ValueOf(b).Convert(typeDesc).Interface(), nil + case reflect.Ptr: + switch typeDesc { + case anyValueType: + // Primitives must be wrapped before being set on an Any field. + return anypb.New(wrapperspb.Bytes([]byte(b))) + case byteWrapperType: + // Convert the bytes to a wrapperspb.BytesValue. + return wrapperspb.Bytes([]byte(b)), nil + case jsonValueType: + // CEL follows the proto3 to JSON conversion by encoding bytes to a string via base64. + // The encoding below matches the golang 'encoding/json' behavior during marshaling, + // which uses base64.StdEncoding. + str := base64.StdEncoding.EncodeToString([]byte(b)) + return structpb.NewStringValue(str), nil + } + case reflect.Interface: + bv := b.Value() + if reflect.TypeOf(bv).Implements(typeDesc) { + return bv, nil + } + if reflect.TypeOf(b).Implements(typeDesc) { + return b, nil + } + } + return nil, fmt.Errorf("type conversion error from Bytes to '%v'", typeDesc) +} + +// ConvertToType implements the ref.Val interface method. +func (b Bytes) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case StringType: + if !utf8.Valid(b) { + return NewErr("invalid UTF-8 in bytes, cannot convert to string") + } + return String(b) + case BytesType: + return b + case TypeType: + return BytesType + } + return NewErr("type conversion error from '%s' to '%s'", BytesType, typeVal) +} + +// Equal implements the ref.Val interface method. +func (b Bytes) Equal(other ref.Val) ref.Val { + otherBytes, ok := other.(Bytes) + return Bool(ok && bytes.Equal(b, otherBytes)) +} + +// Size implements the traits.Sizer interface method. +func (b Bytes) Size() ref.Val { + return Int(len(b)) +} + +// Type implements the ref.Val interface method. +func (b Bytes) Type() ref.Type { + return BytesType +} + +// Value implements the ref.Val interface method. +func (b Bytes) Value() interface{} { + return []byte(b) +} diff --git a/vendor/github.com/google/cel-go/common/types/compare.go b/vendor/github.com/google/cel-go/common/types/compare.go new file mode 100644 index 0000000000..e196826180 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/compare.go @@ -0,0 +1,97 @@ +// Copyright 2021 Google LLC +// +// 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. + +package types + +import ( + "math" + + "github.com/google/cel-go/common/types/ref" +) + +func compareDoubleInt(d Double, i Int) Int { + if d < math.MinInt64 { + return IntNegOne + } + if d > math.MaxInt64 { + return IntOne + } + return compareDouble(d, Double(i)) +} + +func compareIntDouble(i Int, d Double) Int { + return -compareDoubleInt(d, i) +} + +func compareDoubleUint(d Double, u Uint) Int { + if d < 0 { + return IntNegOne + } + if d > math.MaxUint64 { + return IntOne + } + return compareDouble(d, Double(u)) +} + +func compareUintDouble(u Uint, d Double) Int { + return -compareDoubleUint(d, u) +} + +func compareIntUint(i Int, u Uint) Int { + if i < 0 || u > math.MaxInt64 { + return IntNegOne + } + cmp := i - Int(u) + if cmp < 0 { + return IntNegOne + } + if cmp > 0 { + return IntOne + } + return IntZero +} + +func compareUintInt(u Uint, i Int) Int { + return -compareIntUint(i, u) +} + +func compareDouble(a, b Double) Int { + if a < b { + return IntNegOne + } + if a > b { + return IntOne + } + return IntZero +} + +func compareInt(a, b Int) ref.Val { + if a < b { + return IntNegOne + } + if a > b { + return IntOne + } + return IntZero +} + +func compareUint(a, b Uint) ref.Val { + if a < b { + return IntNegOne + } + if a > b { + return IntOne + } + return IntZero +} diff --git a/vendor/github.com/google/cel-go/common/types/doc.go b/vendor/github.com/google/cel-go/common/types/doc.go new file mode 100644 index 0000000000..5f641d7043 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/doc.go @@ -0,0 +1,17 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Package types contains the types, traits, and utilities common to all +// components of expression handling. +package types diff --git a/vendor/github.com/google/cel-go/common/types/double.go b/vendor/github.com/google/cel-go/common/types/double.go new file mode 100644 index 0000000000..a6ec52a0f9 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/double.go @@ -0,0 +1,216 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "fmt" + "math" + "reflect" + + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + + anypb "google.golang.org/protobuf/types/known/anypb" + structpb "google.golang.org/protobuf/types/known/structpb" + wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" +) + +// Double type that implements ref.Val, comparison, and mathematical +// operations. +type Double float64 + +var ( + // DoubleType singleton. + DoubleType = NewTypeValue("double", + traits.AdderType, + traits.ComparerType, + traits.DividerType, + traits.MultiplierType, + traits.NegatorType, + traits.SubtractorType) + + // doubleWrapperType reflected type for protobuf double wrapper type. + doubleWrapperType = reflect.TypeOf(&wrapperspb.DoubleValue{}) + + // floatWrapperType reflected type for protobuf float wrapper type. + floatWrapperType = reflect.TypeOf(&wrapperspb.FloatValue{}) +) + +// Add implements traits.Adder.Add. +func (d Double) Add(other ref.Val) ref.Val { + otherDouble, ok := other.(Double) + if !ok { + return MaybeNoSuchOverloadErr(other) + } + return d + otherDouble +} + +// Compare implements traits.Comparer.Compare. +func (d Double) Compare(other ref.Val) ref.Val { + if math.IsNaN(float64(d)) { + return NewErr("NaN values cannot be ordered") + } + switch ov := other.(type) { + case Double: + if math.IsNaN(float64(ov)) { + return NewErr("NaN values cannot be ordered") + } + return compareDouble(d, ov) + case Int: + return compareDoubleInt(d, ov) + case Uint: + return compareDoubleUint(d, ov) + default: + return MaybeNoSuchOverloadErr(other) + } +} + +// ConvertToNative implements ref.Val.ConvertToNative. +func (d Double) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + switch typeDesc.Kind() { + case reflect.Float32: + v := float32(d) + return reflect.ValueOf(v).Convert(typeDesc).Interface(), nil + case reflect.Float64: + v := float64(d) + return reflect.ValueOf(v).Convert(typeDesc).Interface(), nil + case reflect.Ptr: + switch typeDesc { + case anyValueType: + // Primitives must be wrapped before being set on an Any field. + return anypb.New(wrapperspb.Double(float64(d))) + case doubleWrapperType: + // Convert to a wrapperspb.DoubleValue + return wrapperspb.Double(float64(d)), nil + case floatWrapperType: + // Convert to a wrapperspb.FloatValue (with truncation). + return wrapperspb.Float(float32(d)), nil + case jsonValueType: + // Note, there are special cases for proto3 to json conversion that + // expect the floating point value to be converted to a NaN, + // Infinity, or -Infinity string values, but the jsonpb string + // marshaling of the protobuf.Value will handle this conversion. + return structpb.NewNumberValue(float64(d)), nil + } + switch typeDesc.Elem().Kind() { + case reflect.Float32: + v := float32(d) + p := reflect.New(typeDesc.Elem()) + p.Elem().Set(reflect.ValueOf(v).Convert(typeDesc.Elem())) + return p.Interface(), nil + case reflect.Float64: + v := float64(d) + p := reflect.New(typeDesc.Elem()) + p.Elem().Set(reflect.ValueOf(v).Convert(typeDesc.Elem())) + return p.Interface(), nil + } + case reflect.Interface: + dv := d.Value() + if reflect.TypeOf(dv).Implements(typeDesc) { + return dv, nil + } + if reflect.TypeOf(d).Implements(typeDesc) { + return d, nil + } + } + return nil, fmt.Errorf("type conversion error from Double to '%v'", typeDesc) +} + +// ConvertToType implements ref.Val.ConvertToType. +func (d Double) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case IntType: + i, err := doubleToInt64Checked(float64(d)) + if err != nil { + return wrapErr(err) + } + return Int(i) + case UintType: + i, err := doubleToUint64Checked(float64(d)) + if err != nil { + return wrapErr(err) + } + return Uint(i) + case DoubleType: + return d + case StringType: + return String(fmt.Sprintf("%g", float64(d))) + case TypeType: + return DoubleType + } + return NewErr("type conversion error from '%s' to '%s'", DoubleType, typeVal) +} + +// Divide implements traits.Divider.Divide. +func (d Double) Divide(other ref.Val) ref.Val { + otherDouble, ok := other.(Double) + if !ok { + return MaybeNoSuchOverloadErr(other) + } + return d / otherDouble +} + +// Equal implements ref.Val.Equal. +func (d Double) Equal(other ref.Val) ref.Val { + if math.IsNaN(float64(d)) { + return False + } + switch ov := other.(type) { + case Double: + if math.IsNaN(float64(ov)) { + return False + } + return Bool(d == ov) + case Int: + return Bool(compareDoubleInt(d, ov) == 0) + case Uint: + return Bool(compareDoubleUint(d, ov) == 0) + default: + return False + } +} + +// Multiply implements traits.Multiplier.Multiply. +func (d Double) Multiply(other ref.Val) ref.Val { + otherDouble, ok := other.(Double) + if !ok { + return MaybeNoSuchOverloadErr(other) + } + return d * otherDouble +} + +// Negate implements traits.Negater.Negate. +func (d Double) Negate() ref.Val { + return -d +} + +// Subtract implements traits.Subtractor.Subtract. +func (d Double) Subtract(subtrahend ref.Val) ref.Val { + subtraDouble, ok := subtrahend.(Double) + if !ok { + return MaybeNoSuchOverloadErr(subtrahend) + } + return d - subtraDouble +} + +// Type implements ref.Val.Type. +func (d Double) Type() ref.Type { + return DoubleType +} + +// Value implements ref.Val.Value. +func (d Double) Value() interface{} { + return float64(d) +} diff --git a/vendor/github.com/google/cel-go/common/types/duration.go b/vendor/github.com/google/cel-go/common/types/duration.go new file mode 100644 index 0000000000..418349fa6c --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/duration.go @@ -0,0 +1,199 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "fmt" + "reflect" + "strconv" + "time" + + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + + anypb "google.golang.org/protobuf/types/known/anypb" + dpb "google.golang.org/protobuf/types/known/durationpb" + structpb "google.golang.org/protobuf/types/known/structpb" +) + +// Duration type that implements ref.Val and supports add, compare, negate, +// and subtract operators. This type is also a receiver which means it can +// participate in dispatch to receiver functions. +type Duration struct { + time.Duration +} + +func durationOf(d time.Duration) Duration { + return Duration{Duration: d} +} + +var ( + // DurationType singleton. + DurationType = NewTypeValue("google.protobuf.Duration", + traits.AdderType, + traits.ComparerType, + traits.NegatorType, + traits.ReceiverType, + traits.SubtractorType) +) + +// Add implements traits.Adder.Add. +func (d Duration) Add(other ref.Val) ref.Val { + switch other.Type() { + case DurationType: + dur2 := other.(Duration) + val, err := addDurationChecked(d.Duration, dur2.Duration) + if err != nil { + return wrapErr(err) + } + return durationOf(val) + case TimestampType: + ts := other.(Timestamp).Time + val, err := addTimeDurationChecked(ts, d.Duration) + if err != nil { + return wrapErr(err) + } + return timestampOf(val) + } + return MaybeNoSuchOverloadErr(other) +} + +// Compare implements traits.Comparer.Compare. +func (d Duration) Compare(other ref.Val) ref.Val { + otherDur, ok := other.(Duration) + if !ok { + return MaybeNoSuchOverloadErr(other) + } + d1 := d.Duration + d2 := otherDur.Duration + switch { + case d1 < d2: + return IntNegOne + case d1 > d2: + return IntOne + default: + return IntZero + } +} + +// ConvertToNative implements ref.Val.ConvertToNative. +func (d Duration) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + // If the duration is already assignable to the desired type return it. + if reflect.TypeOf(d.Duration).AssignableTo(typeDesc) { + return d.Duration, nil + } + if reflect.TypeOf(d).AssignableTo(typeDesc) { + return d, nil + } + switch typeDesc { + case anyValueType: + // Pack the duration as a dpb.Duration into an Any value. + return anypb.New(dpb.New(d.Duration)) + case durationValueType: + // Unwrap the CEL value to its underlying proto value. + return dpb.New(d.Duration), nil + case jsonValueType: + // CEL follows the proto3 to JSON conversion. + // Note, using jsonpb would wrap the result in extra double quotes. + v := d.ConvertToType(StringType) + if IsError(v) { + return nil, v.(*Err) + } + return structpb.NewStringValue(string(v.(String))), nil + } + return nil, fmt.Errorf("type conversion error from 'Duration' to '%v'", typeDesc) +} + +// ConvertToType implements ref.Val.ConvertToType. +func (d Duration) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case StringType: + return String(strconv.FormatFloat(d.Seconds(), 'f', -1, 64) + "s") + case IntType: + return Int(d.Duration) + case DurationType: + return d + case TypeType: + return DurationType + } + return NewErr("type conversion error from '%s' to '%s'", DurationType, typeVal) +} + +// Equal implements ref.Val.Equal. +func (d Duration) Equal(other ref.Val) ref.Val { + otherDur, ok := other.(Duration) + return Bool(ok && d.Duration == otherDur.Duration) +} + +// Negate implements traits.Negater.Negate. +func (d Duration) Negate() ref.Val { + val, err := negateDurationChecked(d.Duration) + if err != nil { + return wrapErr(err) + } + return durationOf(val) +} + +// Receive implements traits.Receiver.Receive. +func (d Duration) Receive(function string, overload string, args []ref.Val) ref.Val { + if len(args) == 0 { + if f, found := durationZeroArgOverloads[function]; found { + return f(d.Duration) + } + } + return NoSuchOverloadErr() +} + +// Subtract implements traits.Subtractor.Subtract. +func (d Duration) Subtract(subtrahend ref.Val) ref.Val { + subtraDur, ok := subtrahend.(Duration) + if !ok { + return MaybeNoSuchOverloadErr(subtrahend) + } + val, err := subtractDurationChecked(d.Duration, subtraDur.Duration) + if err != nil { + return wrapErr(err) + } + return durationOf(val) +} + +// Type implements ref.Val.Type. +func (d Duration) Type() ref.Type { + return DurationType +} + +// Value implements ref.Val.Value. +func (d Duration) Value() interface{} { + return d.Duration +} + +var ( + durationValueType = reflect.TypeOf(&dpb.Duration{}) + + durationZeroArgOverloads = map[string]func(time.Duration) ref.Val{ + overloads.TimeGetHours: func(dur time.Duration) ref.Val { + return Int(dur.Hours()) + }, + overloads.TimeGetMinutes: func(dur time.Duration) ref.Val { + return Int(dur.Minutes()) + }, + overloads.TimeGetSeconds: func(dur time.Duration) ref.Val { + return Int(dur.Seconds()) + }, + overloads.TimeGetMilliseconds: func(dur time.Duration) ref.Val { + return Int(dur.Milliseconds()) + }} +) diff --git a/vendor/github.com/google/cel-go/common/types/err.go b/vendor/github.com/google/cel-go/common/types/err.go new file mode 100644 index 0000000000..3fb87f5077 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/err.go @@ -0,0 +1,133 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "errors" + "fmt" + "reflect" + + "github.com/google/cel-go/common/types/ref" +) + +// Err type which extends the built-in go error and implements ref.Val. +type Err struct { + error +} + +var ( + // ErrType singleton. + ErrType = NewTypeValue("error") + + // errDivideByZero is an error indicating a division by zero of an integer value. + errDivideByZero = errors.New("division by zero") + // errModulusByZero is an error indicating a modulus by zero of an integer value. + errModulusByZero = errors.New("modulus by zero") + // errIntOverflow is an error representing integer overflow. + errIntOverflow = errors.New("integer overflow") + // errUintOverflow is an error representing unsigned integer overflow. + errUintOverflow = errors.New("unsigned integer overflow") + // errDurationOverflow is an error representing duration overflow. + errDurationOverflow = errors.New("duration overflow") + // errTimestampOverflow is an error representing timestamp overflow. + errTimestampOverflow = errors.New("timestamp overflow") + celErrTimestampOverflow = &Err{error: errTimestampOverflow} + + // celErrNoSuchOverload indicates that the call arguments did not match a supported method signature. + celErrNoSuchOverload = NewErr("no such overload") +) + +// NewErr creates a new Err described by the format string and args. +// TODO: Audit the use of this function and standardize the error messages and codes. +func NewErr(format string, args ...interface{}) ref.Val { + return &Err{fmt.Errorf(format, args...)} +} + +// NoSuchOverloadErr returns a new types.Err instance with a no such overload message. +func NoSuchOverloadErr() ref.Val { + return celErrNoSuchOverload +} + +// UnsupportedRefValConversionErr returns a types.NewErr instance with a no such conversion +// message that indicates that the native value could not be converted to a CEL ref.Val. +func UnsupportedRefValConversionErr(val interface{}) ref.Val { + return NewErr("unsupported conversion to ref.Val: (%T)%v", val, val) +} + +// MaybeNoSuchOverloadErr returns the error or unknown if the input ref.Val is one of these types, +// else a new no such overload error. +func MaybeNoSuchOverloadErr(val ref.Val) ref.Val { + return ValOrErr(val, "no such overload") +} + +// ValOrErr either returns the existing error or create a new one. +// TODO: Audit the use of this function and standardize the error messages and codes. +func ValOrErr(val ref.Val, format string, args ...interface{}) ref.Val { + if val == nil { + return NewErr(format, args...) + } + if IsUnknownOrError(val) { + return val + } + return NewErr(format, args...) +} + +// wrapErr wraps an existing Go error value into a CEL Err value. +func wrapErr(err error) ref.Val { + return &Err{error: err} +} + +// ConvertToNative implements ref.Val.ConvertToNative. +func (e *Err) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + return nil, e.error +} + +// ConvertToType implements ref.Val.ConvertToType. +func (e *Err) ConvertToType(typeVal ref.Type) ref.Val { + // Errors are not convertible to other representations. + return e +} + +// Equal implements ref.Val.Equal. +func (e *Err) Equal(other ref.Val) ref.Val { + // An error cannot be equal to any other value, so it returns itself. + return e +} + +// String implements fmt.Stringer. +func (e *Err) String() string { + return e.error.Error() +} + +// Type implements ref.Val.Type. +func (e *Err) Type() ref.Type { + return ErrType +} + +// Value implements ref.Val.Value. +func (e *Err) Value() interface{} { + return e.error +} + +// IsError returns whether the input element ref.Type or ref.Val is equal to +// the ErrType singleton. +func IsError(val ref.Val) bool { + switch val.(type) { + case *Err: + return true + default: + return false + } +} diff --git a/vendor/github.com/google/cel-go/common/types/int.go b/vendor/github.com/google/cel-go/common/types/int.go new file mode 100644 index 0000000000..95f25dcd80 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/int.go @@ -0,0 +1,297 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "fmt" + "math" + "reflect" + "strconv" + "time" + + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + + anypb "google.golang.org/protobuf/types/known/anypb" + structpb "google.golang.org/protobuf/types/known/structpb" + wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" +) + +// Int type that implements ref.Val as well as comparison and math operators. +type Int int64 + +// Int constants used for comparison results. +const ( + // IntZero is the zero-value for Int + IntZero = Int(0) + IntOne = Int(1) + IntNegOne = Int(-1) +) + +var ( + // IntType singleton. + IntType = NewTypeValue("int", + traits.AdderType, + traits.ComparerType, + traits.DividerType, + traits.ModderType, + traits.MultiplierType, + traits.NegatorType, + traits.SubtractorType) + + // int32WrapperType reflected type for protobuf int32 wrapper type. + int32WrapperType = reflect.TypeOf(&wrapperspb.Int32Value{}) + + // int64WrapperType reflected type for protobuf int64 wrapper type. + int64WrapperType = reflect.TypeOf(&wrapperspb.Int64Value{}) +) + +// Add implements traits.Adder.Add. +func (i Int) Add(other ref.Val) ref.Val { + otherInt, ok := other.(Int) + if !ok { + return MaybeNoSuchOverloadErr(other) + } + val, err := addInt64Checked(int64(i), int64(otherInt)) + if err != nil { + return wrapErr(err) + } + return Int(val) +} + +// Compare implements traits.Comparer.Compare. +func (i Int) Compare(other ref.Val) ref.Val { + switch ov := other.(type) { + case Double: + if math.IsNaN(float64(ov)) { + return NewErr("NaN values cannot be ordered") + } + return compareIntDouble(i, ov) + case Int: + return compareInt(i, ov) + case Uint: + return compareIntUint(i, ov) + default: + return MaybeNoSuchOverloadErr(other) + } +} + +// ConvertToNative implements ref.Val.ConvertToNative. +func (i Int) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + switch typeDesc.Kind() { + case reflect.Int, reflect.Int32: + // Enums are also mapped as int32 derivations. + // Note, the code doesn't convert to the enum value directly since this is not known, but + // the net effect with respect to proto-assignment is handled correctly by the reflection + // Convert method. + v, err := int64ToInt32Checked(int64(i)) + if err != nil { + return nil, err + } + return reflect.ValueOf(v).Convert(typeDesc).Interface(), nil + case reflect.Int64: + return reflect.ValueOf(i).Convert(typeDesc).Interface(), nil + case reflect.Ptr: + switch typeDesc { + case anyValueType: + // Primitives must be wrapped before being set on an Any field. + return anypb.New(wrapperspb.Int64(int64(i))) + case int32WrapperType: + // Convert the value to a wrapperspb.Int32Value, error on overflow. + v, err := int64ToInt32Checked(int64(i)) + if err != nil { + return nil, err + } + return wrapperspb.Int32(v), nil + case int64WrapperType: + // Convert the value to a wrapperspb.Int64Value. + return wrapperspb.Int64(int64(i)), nil + case jsonValueType: + // The proto-to-JSON conversion rules would convert all 64-bit integer values to JSON + // decimal strings. Because CEL ints might come from the automatic widening of 32-bit + // values in protos, the JSON type is chosen dynamically based on the value. + // + // - Integers -2^53-1 < n < 2^53-1 are encoded as JSON numbers. + // - Integers outside this range are encoded as JSON strings. + // + // The integer to float range represents the largest interval where such a conversion + // can round-trip accurately. Thus, conversions from a 32-bit source can expect a JSON + // number as with protobuf. Those consuming JSON from a 64-bit source must be able to + // handle either a JSON number or a JSON decimal string. To handle these cases safely + // the string values must be explicitly converted to int() within a CEL expression; + // however, it is best to simply stay within the JSON number range when building JSON + // objects in CEL. + if i.isJSONSafe() { + return structpb.NewNumberValue(float64(i)), nil + } + // Proto3 to JSON conversion requires string-formatted int64 values + // since the conversion to floating point would result in truncation. + return structpb.NewStringValue(strconv.FormatInt(int64(i), 10)), nil + } + switch typeDesc.Elem().Kind() { + case reflect.Int32: + // Convert the value to a wrapperspb.Int32Value, error on overflow. + v, err := int64ToInt32Checked(int64(i)) + if err != nil { + return nil, err + } + p := reflect.New(typeDesc.Elem()) + p.Elem().Set(reflect.ValueOf(v).Convert(typeDesc.Elem())) + return p.Interface(), nil + case reflect.Int64: + v := int64(i) + p := reflect.New(typeDesc.Elem()) + p.Elem().Set(reflect.ValueOf(v).Convert(typeDesc.Elem())) + return p.Interface(), nil + } + case reflect.Interface: + iv := i.Value() + if reflect.TypeOf(iv).Implements(typeDesc) { + return iv, nil + } + if reflect.TypeOf(i).Implements(typeDesc) { + return i, nil + } + } + return nil, fmt.Errorf("unsupported type conversion from 'int' to %v", typeDesc) +} + +// ConvertToType implements ref.Val.ConvertToType. +func (i Int) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case IntType: + return i + case UintType: + u, err := int64ToUint64Checked(int64(i)) + if err != nil { + return wrapErr(err) + } + return Uint(u) + case DoubleType: + return Double(i) + case StringType: + return String(fmt.Sprintf("%d", int64(i))) + case TimestampType: + // The maximum positive value that can be passed to time.Unix is math.MaxInt64 minus the number + // of seconds between year 1 and year 1970. See comments on unixToInternal. + if int64(i) < minUnixTime || int64(i) > maxUnixTime { + return celErrTimestampOverflow + } + return timestampOf(time.Unix(int64(i), 0).UTC()) + case TypeType: + return IntType + } + return NewErr("type conversion error from '%s' to '%s'", IntType, typeVal) +} + +// Divide implements traits.Divider.Divide. +func (i Int) Divide(other ref.Val) ref.Val { + otherInt, ok := other.(Int) + if !ok { + return MaybeNoSuchOverloadErr(other) + } + val, err := divideInt64Checked(int64(i), int64(otherInt)) + if err != nil { + return wrapErr(err) + } + return Int(val) +} + +// Equal implements ref.Val.Equal. +func (i Int) Equal(other ref.Val) ref.Val { + switch ov := other.(type) { + case Double: + if math.IsNaN(float64(ov)) { + return False + } + return Bool(compareIntDouble(i, ov) == 0) + case Int: + return Bool(i == ov) + case Uint: + return Bool(compareIntUint(i, ov) == 0) + default: + return False + } +} + +// Modulo implements traits.Modder.Modulo. +func (i Int) Modulo(other ref.Val) ref.Val { + otherInt, ok := other.(Int) + if !ok { + return MaybeNoSuchOverloadErr(other) + } + val, err := moduloInt64Checked(int64(i), int64(otherInt)) + if err != nil { + return wrapErr(err) + } + return Int(val) +} + +// Multiply implements traits.Multiplier.Multiply. +func (i Int) Multiply(other ref.Val) ref.Val { + otherInt, ok := other.(Int) + if !ok { + return MaybeNoSuchOverloadErr(other) + } + val, err := multiplyInt64Checked(int64(i), int64(otherInt)) + if err != nil { + return wrapErr(err) + } + return Int(val) +} + +// Negate implements traits.Negater.Negate. +func (i Int) Negate() ref.Val { + val, err := negateInt64Checked(int64(i)) + if err != nil { + return wrapErr(err) + } + return Int(val) +} + +// Subtract implements traits.Subtractor.Subtract. +func (i Int) Subtract(subtrahend ref.Val) ref.Val { + subtraInt, ok := subtrahend.(Int) + if !ok { + return MaybeNoSuchOverloadErr(subtrahend) + } + val, err := subtractInt64Checked(int64(i), int64(subtraInt)) + if err != nil { + return wrapErr(err) + } + return Int(val) +} + +// Type implements ref.Val.Type. +func (i Int) Type() ref.Type { + return IntType +} + +// Value implements ref.Val.Value. +func (i Int) Value() interface{} { + return int64(i) +} + +// isJSONSafe indicates whether the int is safely representable as a floating point value in JSON. +func (i Int) isJSONSafe() bool { + return i >= minIntJSON && i <= maxIntJSON +} + +const ( + // maxIntJSON is defined as the Number.MAX_SAFE_INTEGER value per EcmaScript 6. + maxIntJSON = 1<<53 - 1 + // minIntJSON is defined as the Number.MIN_SAFE_INTEGER value per EcmaScript 6. + minIntJSON = -maxIntJSON +) diff --git a/vendor/github.com/google/cel-go/common/types/iterator.go b/vendor/github.com/google/cel-go/common/types/iterator.go new file mode 100644 index 0000000000..4906627783 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/iterator.go @@ -0,0 +1,55 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "fmt" + "reflect" + + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" +) + +var ( + // IteratorType singleton. + IteratorType = NewTypeValue("iterator", traits.IteratorType) +) + +// baseIterator is the basis for list, map, and object iterators. +// +// An iterator in and of itself should not be a valid value for comparison, but must implement the +// `ref.Val` methods in order to be well-supported within instruction arguments processed by the +// interpreter. +type baseIterator struct{} + +func (*baseIterator) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + return nil, fmt.Errorf("type conversion on iterators not supported") +} + +func (*baseIterator) ConvertToType(typeVal ref.Type) ref.Val { + return NewErr("no such overload") +} + +func (*baseIterator) Equal(other ref.Val) ref.Val { + return NewErr("no such overload") +} + +func (*baseIterator) Type() ref.Type { + return IteratorType +} + +func (*baseIterator) Value() interface{} { + return nil +} diff --git a/vendor/github.com/google/cel-go/common/types/json_value.go b/vendor/github.com/google/cel-go/common/types/json_value.go new file mode 100644 index 0000000000..cd63b51944 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/json_value.go @@ -0,0 +1,28 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "reflect" + + structpb "google.golang.org/protobuf/types/known/structpb" +) + +// JSON type constants representing the reflected types of protobuf JSON values. +var ( + jsonValueType = reflect.TypeOf(&structpb.Value{}) + jsonListValueType = reflect.TypeOf(&structpb.ListValue{}) + jsonStructType = reflect.TypeOf(&structpb.Struct{}) +) diff --git a/vendor/github.com/google/cel-go/common/types/list.go b/vendor/github.com/google/cel-go/common/types/list.go new file mode 100644 index 0000000000..7230f7ea12 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/list.go @@ -0,0 +1,489 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "fmt" + "reflect" + + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + + anypb "google.golang.org/protobuf/types/known/anypb" + structpb "google.golang.org/protobuf/types/known/structpb" +) + +var ( + // ListType singleton. + ListType = NewTypeValue("list", + traits.AdderType, + traits.ContainerType, + traits.IndexerType, + traits.IterableType, + traits.SizerType) +) + +// NewDynamicList returns a traits.Lister with heterogenous elements. +// value should be an array of "native" types, i.e. any type that +// NativeToValue() can convert to a ref.Val. +func NewDynamicList(adapter ref.TypeAdapter, value interface{}) traits.Lister { + refValue := reflect.ValueOf(value) + return &baseList{ + TypeAdapter: adapter, + value: value, + size: refValue.Len(), + get: func(i int) interface{} { + return refValue.Index(i).Interface() + }, + } +} + +// NewStringList returns a traits.Lister containing only strings. +func NewStringList(adapter ref.TypeAdapter, elems []string) traits.Lister { + return &baseList{ + TypeAdapter: adapter, + value: elems, + size: len(elems), + get: func(i int) interface{} { return elems[i] }, + } +} + +// NewRefValList returns a traits.Lister with ref.Val elements. +// +// This type specialization is used with list literals within CEL expressions. +func NewRefValList(adapter ref.TypeAdapter, elems []ref.Val) traits.Lister { + return &baseList{ + TypeAdapter: adapter, + value: elems, + size: len(elems), + get: func(i int) interface{} { return elems[i] }, + } +} + +// NewProtoList returns a traits.Lister based on a pb.List instance. +func NewProtoList(adapter ref.TypeAdapter, list protoreflect.List) traits.Lister { + return &baseList{ + TypeAdapter: adapter, + value: list, + size: list.Len(), + get: func(i int) interface{} { return list.Get(i).Interface() }, + } +} + +// NewJSONList returns a traits.Lister based on structpb.ListValue instance. +func NewJSONList(adapter ref.TypeAdapter, l *structpb.ListValue) traits.Lister { + vals := l.GetValues() + return &baseList{ + TypeAdapter: adapter, + value: l, + size: len(vals), + get: func(i int) interface{} { return vals[i] }, + } +} + +// NewMutableList creates a new mutable list whose internal state can be modified. +func NewMutableList(adapter ref.TypeAdapter) traits.MutableLister { + var mutableValues []ref.Val + return &mutableList{ + baseList: &baseList{ + TypeAdapter: adapter, + value: mutableValues, + size: 0, + get: func(i int) interface{} { return mutableValues[i] }, + }, + mutableValues: mutableValues, + } +} + +// baseList points to a list containing elements of any type. +// The `value` is an array of native values, and refValue is its reflection object. +// The `ref.TypeAdapter` enables native type to CEL type conversions. +type baseList struct { + ref.TypeAdapter + value interface{} + + // size indicates the number of elements within the list. + // Since objects are immutable the size of a list is static. + size int + + // get returns a value at the specified integer index. + // The index is guaranteed to be checked against the list index range. + get func(int) interface{} +} + +// Add implements the traits.Adder interface method. +func (l *baseList) Add(other ref.Val) ref.Val { + otherList, ok := other.(traits.Lister) + if !ok { + return MaybeNoSuchOverloadErr(other) + } + if l.Size() == IntZero { + return other + } + if otherList.Size() == IntZero { + return l + } + return &concatList{ + TypeAdapter: l.TypeAdapter, + prevList: l, + nextList: otherList} +} + +// Contains implements the traits.Container interface method. +func (l *baseList) Contains(elem ref.Val) ref.Val { + for i := 0; i < l.size; i++ { + val := l.NativeToValue(l.get(i)) + cmp := elem.Equal(val) + b, ok := cmp.(Bool) + if ok && b == True { + return True + } + } + return False +} + +// ConvertToNative implements the ref.Val interface method. +func (l *baseList) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + // If the underlying list value is assignable to the reflected type return it. + if reflect.TypeOf(l.value).AssignableTo(typeDesc) { + return l.value, nil + } + // If the list wrapper is assignable to the desired type return it. + if reflect.TypeOf(l).AssignableTo(typeDesc) { + return l, nil + } + // Attempt to convert the list to a set of well known protobuf types. + switch typeDesc { + case anyValueType: + json, err := l.ConvertToNative(jsonListValueType) + if err != nil { + return nil, err + } + return anypb.New(json.(proto.Message)) + case jsonValueType, jsonListValueType: + jsonValues, err := + l.ConvertToNative(reflect.TypeOf([]*structpb.Value{})) + if err != nil { + return nil, err + } + jsonList := &structpb.ListValue{Values: jsonValues.([]*structpb.Value)} + if typeDesc == jsonListValueType { + return jsonList, nil + } + return structpb.NewListValue(jsonList), nil + } + // Non-list conversion. + if typeDesc.Kind() != reflect.Slice && typeDesc.Kind() != reflect.Array { + return nil, fmt.Errorf("type conversion error from list to '%v'", typeDesc) + } + + // List conversion. + // Allow the element ConvertToNative() function to determine whether conversion is possible. + otherElemType := typeDesc.Elem() + elemCount := l.size + nativeList := reflect.MakeSlice(typeDesc, elemCount, elemCount) + for i := 0; i < elemCount; i++ { + elem := l.NativeToValue(l.get(i)) + nativeElemVal, err := elem.ConvertToNative(otherElemType) + if err != nil { + return nil, err + } + nativeList.Index(i).Set(reflect.ValueOf(nativeElemVal)) + } + return nativeList.Interface(), nil +} + +// ConvertToType implements the ref.Val interface method. +func (l *baseList) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case ListType: + return l + case TypeType: + return ListType + } + return NewErr("type conversion error from '%s' to '%s'", ListType, typeVal) +} + +// Equal implements the ref.Val interface method. +func (l *baseList) Equal(other ref.Val) ref.Val { + otherList, ok := other.(traits.Lister) + if !ok { + return False + } + if l.Size() != otherList.Size() { + return False + } + for i := IntZero; i < l.Size().(Int); i++ { + thisElem := l.Get(i) + otherElem := otherList.Get(i) + elemEq := Equal(thisElem, otherElem) + if elemEq == False { + return False + } + } + return True +} + +// Get implements the traits.Indexer interface method. +func (l *baseList) Get(index ref.Val) ref.Val { + ind, err := indexOrError(index) + if err != nil { + return ValOrErr(index, err.Error()) + } + if ind < 0 || ind >= l.size { + return NewErr("index '%d' out of range in list size '%d'", ind, l.Size()) + } + return l.NativeToValue(l.get(ind)) +} + +// Iterator implements the traits.Iterable interface method. +func (l *baseList) Iterator() traits.Iterator { + return newListIterator(l) +} + +// Size implements the traits.Sizer interface method. +func (l *baseList) Size() ref.Val { + return Int(l.size) +} + +// Type implements the ref.Val interface method. +func (l *baseList) Type() ref.Type { + return ListType +} + +// Value implements the ref.Val interface method. +func (l *baseList) Value() interface{} { + return l.value +} + +// mutableList aggregates values into its internal storage. For use with internal CEL variables only. +type mutableList struct { + *baseList + mutableValues []ref.Val +} + +// Add copies elements from the other list into the internal storage of the mutable list. +// The ref.Val returned by Add is the receiver. +func (l *mutableList) Add(other ref.Val) ref.Val { + switch otherList := other.(type) { + case *mutableList: + l.mutableValues = append(l.mutableValues, otherList.mutableValues...) + l.size += len(otherList.mutableValues) + case traits.Lister: + for i := IntZero; i < otherList.Size().(Int); i++ { + l.size++ + l.mutableValues = append(l.mutableValues, otherList.Get(i)) + } + default: + return MaybeNoSuchOverloadErr(otherList) + } + return l +} + +// ToImmutableList returns an immutable list based on the internal storage of the mutable list. +func (l *mutableList) ToImmutableList() traits.Lister { + // The reference to internal state is guaranteed to be safe as this call is only performed + // when mutations have been completed. + return NewRefValList(l.TypeAdapter, l.mutableValues) +} + +// concatList combines two list implementations together into a view. +// The `ref.TypeAdapter` enables native type to CEL type conversions. +type concatList struct { + ref.TypeAdapter + value interface{} + prevList traits.Lister + nextList traits.Lister +} + +// Add implements the traits.Adder interface method. +func (l *concatList) Add(other ref.Val) ref.Val { + otherList, ok := other.(traits.Lister) + if !ok { + return MaybeNoSuchOverloadErr(other) + } + if l.Size() == IntZero { + return other + } + if otherList.Size() == IntZero { + return l + } + return &concatList{ + TypeAdapter: l.TypeAdapter, + prevList: l, + nextList: otherList} +} + +// Contains implements the traits.Container interface method. +func (l *concatList) Contains(elem ref.Val) ref.Val { + // The concat list relies on the IsErrorOrUnknown checks against the input element to be + // performed by the `prevList` and/or `nextList`. + prev := l.prevList.Contains(elem) + // Short-circuit the return if the elem was found in the prev list. + if prev == True { + return prev + } + // Return if the elem was found in the next list. + next := l.nextList.Contains(elem) + if next == True { + return next + } + // Handle the case where an error or unknown was encountered before checking next. + if IsUnknownOrError(prev) { + return prev + } + // Otherwise, rely on the next value as the representative result. + return next +} + +// ConvertToNative implements the ref.Val interface method. +func (l *concatList) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + combined := NewDynamicList(l.TypeAdapter, l.Value().([]interface{})) + return combined.ConvertToNative(typeDesc) +} + +// ConvertToType implements the ref.Val interface method. +func (l *concatList) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case ListType: + return l + case TypeType: + return ListType + } + return NewErr("type conversion error from '%s' to '%s'", ListType, typeVal) +} + +// Equal implements the ref.Val interface method. +func (l *concatList) Equal(other ref.Val) ref.Val { + otherList, ok := other.(traits.Lister) + if !ok { + return False + } + if l.Size() != otherList.Size() { + return False + } + var maybeErr ref.Val + for i := IntZero; i < l.Size().(Int); i++ { + thisElem := l.Get(i) + otherElem := otherList.Get(i) + elemEq := Equal(thisElem, otherElem) + if elemEq == False { + return False + } + if maybeErr == nil && IsUnknownOrError(elemEq) { + maybeErr = elemEq + } + } + if maybeErr != nil { + return maybeErr + } + return True +} + +// Get implements the traits.Indexer interface method. +func (l *concatList) Get(index ref.Val) ref.Val { + ind, err := indexOrError(index) + if err != nil { + return ValOrErr(index, err.Error()) + } + i := Int(ind) + if i < l.prevList.Size().(Int) { + return l.prevList.Get(i) + } + offset := i - l.prevList.Size().(Int) + return l.nextList.Get(offset) +} + +// Iterator implements the traits.Iterable interface method. +func (l *concatList) Iterator() traits.Iterator { + return newListIterator(l) +} + +// Size implements the traits.Sizer interface method. +func (l *concatList) Size() ref.Val { + return l.prevList.Size().(Int).Add(l.nextList.Size()) +} + +// Type implements the ref.Val interface method. +func (l *concatList) Type() ref.Type { + return ListType +} + +// Value implements the ref.Val interface method. +func (l *concatList) Value() interface{} { + if l.value == nil { + merged := make([]interface{}, l.Size().(Int)) + prevLen := l.prevList.Size().(Int) + for i := Int(0); i < prevLen; i++ { + merged[i] = l.prevList.Get(i).Value() + } + nextLen := l.nextList.Size().(Int) + for j := Int(0); j < nextLen; j++ { + merged[prevLen+j] = l.nextList.Get(j).Value() + } + l.value = merged + } + return l.value +} + +func newListIterator(listValue traits.Lister) traits.Iterator { + return &listIterator{ + listValue: listValue, + len: listValue.Size().(Int), + } +} + +type listIterator struct { + *baseIterator + listValue traits.Lister + cursor Int + len Int +} + +// HasNext implements the traits.Iterator interface method. +func (it *listIterator) HasNext() ref.Val { + return Bool(it.cursor < it.len) +} + +// Next implements the traits.Iterator interface method. +func (it *listIterator) Next() ref.Val { + if it.HasNext() == True { + index := it.cursor + it.cursor++ + return it.listValue.Get(index) + } + return nil +} + +func indexOrError(index ref.Val) (int, error) { + switch iv := index.(type) { + case Int: + return int(iv), nil + case Double: + if ik, ok := doubleToInt64Lossless(float64(iv)); ok { + return int(ik), nil + } + return -1, fmt.Errorf("unsupported index value %v in list", index) + case Uint: + if ik, ok := uint64ToInt64Lossless(uint64(iv)); ok { + return int(ik), nil + } + return -1, fmt.Errorf("unsupported index value %v in list", index) + default: + return -1, fmt.Errorf("unsupported index type '%s' in list", index.Type()) + } +} diff --git a/vendor/github.com/google/cel-go/common/types/map.go b/vendor/github.com/google/cel-go/common/types/map.go new file mode 100644 index 0000000000..5865594024 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/map.go @@ -0,0 +1,832 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "fmt" + "reflect" + + "github.com/google/cel-go/common/types/pb" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "github.com/stoewer/go-strcase" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + + anypb "google.golang.org/protobuf/types/known/anypb" + structpb "google.golang.org/protobuf/types/known/structpb" +) + +// NewDynamicMap returns a traits.Mapper value with dynamic key, value pairs. +func NewDynamicMap(adapter ref.TypeAdapter, value interface{}) traits.Mapper { + refValue := reflect.ValueOf(value) + return &baseMap{ + TypeAdapter: adapter, + mapAccessor: newReflectMapAccessor(adapter, refValue), + value: value, + size: refValue.Len(), + } +} + +// NewJSONStruct creates a traits.Mapper implementation backed by a JSON struct that has been +// encoded in protocol buffer form. +// +// The `adapter` argument provides type adaptation capabilities from proto to CEL. +func NewJSONStruct(adapter ref.TypeAdapter, value *structpb.Struct) traits.Mapper { + fields := value.GetFields() + return &baseMap{ + TypeAdapter: adapter, + mapAccessor: newJSONStructAccessor(adapter, fields), + value: value, + size: len(fields), + } +} + +// NewRefValMap returns a specialized traits.Mapper with CEL valued keys and values. +func NewRefValMap(adapter ref.TypeAdapter, value map[ref.Val]ref.Val) traits.Mapper { + return &baseMap{ + TypeAdapter: adapter, + mapAccessor: newRefValMapAccessor(value), + value: value, + size: len(value), + } +} + +// NewStringInterfaceMap returns a specialized traits.Mapper with string keys and interface values. +func NewStringInterfaceMap(adapter ref.TypeAdapter, value map[string]interface{}) traits.Mapper { + return &baseMap{ + TypeAdapter: adapter, + mapAccessor: newStringIfaceMapAccessor(adapter, value), + value: value, + size: len(value), + } +} + +// NewStringStringMap returns a specialized traits.Mapper with string keys and values. +func NewStringStringMap(adapter ref.TypeAdapter, value map[string]string) traits.Mapper { + return &baseMap{ + TypeAdapter: adapter, + mapAccessor: newStringMapAccessor(value), + value: value, + size: len(value), + } +} + +// NewProtoMap returns a specialized traits.Mapper for handling protobuf map values. +func NewProtoMap(adapter ref.TypeAdapter, value *pb.Map) traits.Mapper { + return &protoMap{ + TypeAdapter: adapter, + value: value, + } +} + +var ( + // MapType singleton. + MapType = NewTypeValue("map", + traits.ContainerType, + traits.IndexerType, + traits.IterableType, + traits.SizerType) +) + +// mapAccessor is a private interface for finding values within a map and iterating over the keys. +// This interface implements portions of the API surface area required by the traits.Mapper +// interface. +type mapAccessor interface { + // Find returns a value, if one exists, for the input key. + // + // If the key is not found the function returns (nil, false). + Find(ref.Val) (ref.Val, bool) + + // Iterator returns an Iterator over the map key set. + Iterator() traits.Iterator +} + +// baseMap is a reflection based map implementation designed to handle a variety of map-like types. +// +// Since CEL is side-effect free, the base map represents an immutable object. +type baseMap struct { + // TypeAdapter used to convert keys and values accessed within the map. + ref.TypeAdapter + + // mapAccessor interface implementation used to find and iterate over map keys. + mapAccessor + + // value is the native Go value upon which the map type operators. + value interface{} + + // size is the number of entries in the map. + size int +} + +// Contains implements the traits.Container interface method. +func (m *baseMap) Contains(index ref.Val) ref.Val { + _, found := m.Find(index) + return Bool(found) +} + +// ConvertToNative implements the ref.Val interface method. +func (m *baseMap) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + // If the map is already assignable to the desired type return it, e.g. interfaces and + // maps with the same key value types. + if reflect.TypeOf(m.value).AssignableTo(typeDesc) { + return m.value, nil + } + if reflect.TypeOf(m).AssignableTo(typeDesc) { + return m, nil + } + switch typeDesc { + case anyValueType: + json, err := m.ConvertToNative(jsonStructType) + if err != nil { + return nil, err + } + return anypb.New(json.(proto.Message)) + case jsonValueType, jsonStructType: + jsonEntries, err := + m.ConvertToNative(reflect.TypeOf(map[string]*structpb.Value{})) + if err != nil { + return nil, err + } + jsonMap := &structpb.Struct{Fields: jsonEntries.(map[string]*structpb.Value)} + if typeDesc == jsonStructType { + return jsonMap, nil + } + return structpb.NewStructValue(jsonMap), nil + } + + // Unwrap pointers, but track their use. + isPtr := false + if typeDesc.Kind() == reflect.Ptr { + tk := typeDesc + typeDesc = typeDesc.Elem() + if typeDesc.Kind() == reflect.Ptr { + return nil, fmt.Errorf("unsupported type conversion to '%v'", tk) + } + isPtr = true + } + switch typeDesc.Kind() { + // Map conversion. + case reflect.Map: + otherKey := typeDesc.Key() + otherElem := typeDesc.Elem() + nativeMap := reflect.MakeMapWithSize(typeDesc, m.size) + it := m.Iterator() + for it.HasNext() == True { + key := it.Next() + refKeyValue, err := key.ConvertToNative(otherKey) + if err != nil { + return nil, err + } + refElemValue, err := m.Get(key).ConvertToNative(otherElem) + if err != nil { + return nil, err + } + nativeMap.SetMapIndex(reflect.ValueOf(refKeyValue), reflect.ValueOf(refElemValue)) + } + return nativeMap.Interface(), nil + case reflect.Struct: + nativeStructPtr := reflect.New(typeDesc) + nativeStruct := nativeStructPtr.Elem() + it := m.Iterator() + for it.HasNext() == True { + key := it.Next() + // Ensure the field name being referenced is exported. + // Only exported (public) field names can be set by reflection, where the name + // must be at least one character in length and start with an upper-case letter. + fieldName := key.ConvertToType(StringType) + if IsError(fieldName) { + return nil, fieldName.(*Err) + } + name := string(fieldName.(String)) + name = strcase.UpperCamelCase(name) + fieldRef := nativeStruct.FieldByName(name) + if !fieldRef.IsValid() { + return nil, fmt.Errorf("type conversion error, no such field '%s' in type '%v'", name, typeDesc) + } + fieldValue, err := m.Get(key).ConvertToNative(fieldRef.Type()) + if err != nil { + return nil, err + } + fieldRef.Set(reflect.ValueOf(fieldValue)) + } + if isPtr { + return nativeStructPtr.Interface(), nil + } + return nativeStruct.Interface(), nil + } + return nil, fmt.Errorf("type conversion error from map to '%v'", typeDesc) +} + +// ConvertToType implements the ref.Val interface method. +func (m *baseMap) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case MapType: + return m + case TypeType: + return MapType + } + return NewErr("type conversion error from '%s' to '%s'", MapType, typeVal) +} + +// Equal implements the ref.Val interface method. +func (m *baseMap) Equal(other ref.Val) ref.Val { + otherMap, ok := other.(traits.Mapper) + if !ok { + return False + } + if m.Size() != otherMap.Size() { + return False + } + it := m.Iterator() + for it.HasNext() == True { + key := it.Next() + thisVal, _ := m.Find(key) + otherVal, found := otherMap.Find(key) + if !found { + return False + } + valEq := Equal(thisVal, otherVal) + if valEq == False { + return False + } + } + return True +} + +// Get implements the traits.Indexer interface method. +func (m *baseMap) Get(key ref.Val) ref.Val { + v, found := m.Find(key) + if !found { + return ValOrErr(v, "no such key: %v", key) + } + return v +} + +// Size implements the traits.Sizer interface method. +func (m *baseMap) Size() ref.Val { + return Int(m.size) +} + +// Type implements the ref.Val interface method. +func (m *baseMap) Type() ref.Type { + return MapType +} + +// Value implements the ref.Val interface method. +func (m *baseMap) Value() interface{} { + return m.value +} + +func newJSONStructAccessor(adapter ref.TypeAdapter, st map[string]*structpb.Value) mapAccessor { + return &jsonStructAccessor{ + TypeAdapter: adapter, + st: st, + } +} + +type jsonStructAccessor struct { + ref.TypeAdapter + st map[string]*structpb.Value +} + +// Find searches the json struct field map for the input key value and returns (value, true) if +// found. +// +// If the key is not found the function returns (nil, false). +func (a *jsonStructAccessor) Find(key ref.Val) (ref.Val, bool) { + strKey, ok := key.(String) + if !ok { + return nil, false + } + keyVal, found := a.st[string(strKey)] + if !found { + return nil, false + } + return a.NativeToValue(keyVal), true +} + +// Iterator creates a new traits.Iterator from the set of JSON struct field names. +func (a *jsonStructAccessor) Iterator() traits.Iterator { + // Copy the keys to make their order stable. + mapKeys := make([]string, len(a.st)) + i := 0 + for k := range a.st { + mapKeys[i] = k + i++ + } + return &stringKeyIterator{ + mapKeys: mapKeys, + len: len(mapKeys), + } +} + +func newReflectMapAccessor(adapter ref.TypeAdapter, value reflect.Value) mapAccessor { + keyType := value.Type().Key() + return &reflectMapAccessor{ + TypeAdapter: adapter, + refValue: value, + keyType: keyType, + } +} + +type reflectMapAccessor struct { + ref.TypeAdapter + refValue reflect.Value + keyType reflect.Type +} + +// Find converts the input key to a native Golang type and then uses reflection to find the key, +// returning (value, true) if present. +// +// If the key is not found the function returns (nil, false). +func (m *reflectMapAccessor) Find(key ref.Val) (ref.Val, bool) { + if m.refValue.Len() == 0 { + return nil, false + } + if keyVal, found := m.findInternal(key); found { + return keyVal, true + } + switch k := key.(type) { + // Double is not a valid proto map key type, so check for the key as an int or uint. + case Double: + if ik, ok := doubleToInt64Lossless(float64(k)); ok { + if keyVal, found := m.findInternal(Int(ik)); found { + return keyVal, true + } + } + if uk, ok := doubleToUint64Lossless(float64(k)); ok { + return m.findInternal(Uint(uk)) + } + // map keys of type double are not supported. + case Int: + if uk, ok := int64ToUint64Lossless(int64(k)); ok { + return m.findInternal(Uint(uk)) + } + case Uint: + if ik, ok := uint64ToInt64Lossless(uint64(k)); ok { + return m.findInternal(Int(ik)) + } + } + return nil, false +} + +// findInternal attempts to convert the incoming key to the map's internal native type +// and then returns the value, if found. +func (m *reflectMapAccessor) findInternal(key ref.Val) (ref.Val, bool) { + k, err := key.ConvertToNative(m.keyType) + if err != nil { + return nil, false + } + refKey := reflect.ValueOf(k) + val := m.refValue.MapIndex(refKey) + if val.IsValid() { + return m.NativeToValue(val.Interface()), true + } + return nil, false +} + +// Iterator creates a Golang reflection based traits.Iterator. +func (m *reflectMapAccessor) Iterator() traits.Iterator { + return &mapIterator{ + TypeAdapter: m.TypeAdapter, + mapKeys: m.refValue.MapRange(), + len: m.refValue.Len(), + } +} + +func newRefValMapAccessor(mapVal map[ref.Val]ref.Val) mapAccessor { + return &refValMapAccessor{mapVal: mapVal} +} + +type refValMapAccessor struct { + mapVal map[ref.Val]ref.Val +} + +// Find uses native map accesses to find the key, returning (value, true) if present. +// +// If the key is not found the function returns (nil, false). +func (a *refValMapAccessor) Find(key ref.Val) (ref.Val, bool) { + if len(a.mapVal) == 0 { + return nil, false + } + if keyVal, found := a.mapVal[key]; found { + return keyVal, true + } + switch k := key.(type) { + case Double: + if ik, ok := doubleToInt64Lossless(float64(k)); ok { + if keyVal, found := a.mapVal[Int(ik)]; found { + return keyVal, found + } + } + if uk, ok := doubleToUint64Lossless(float64(k)); ok { + keyVal, found := a.mapVal[Uint(uk)] + return keyVal, found + } + // map keys of type double are not supported. + case Int: + if uk, ok := int64ToUint64Lossless(int64(k)); ok { + keyVal, found := a.mapVal[Uint(uk)] + return keyVal, found + } + case Uint: + if ik, ok := uint64ToInt64Lossless(uint64(k)); ok { + keyVal, found := a.mapVal[Int(ik)] + return keyVal, found + } + } + return nil, false +} + +// Iterator produces a new traits.Iterator which iterates over the map keys via Golang reflection. +func (a *refValMapAccessor) Iterator() traits.Iterator { + return &mapIterator{ + TypeAdapter: DefaultTypeAdapter, + mapKeys: reflect.ValueOf(a.mapVal).MapRange(), + len: len(a.mapVal), + } +} + +func newStringMapAccessor(strMap map[string]string) mapAccessor { + return &stringMapAccessor{mapVal: strMap} +} + +type stringMapAccessor struct { + mapVal map[string]string +} + +// Find uses native map accesses to find the key, returning (value, true) if present. +// +// If the key is not found the function returns (nil, false). +func (a *stringMapAccessor) Find(key ref.Val) (ref.Val, bool) { + strKey, ok := key.(String) + if !ok { + return nil, false + } + keyVal, found := a.mapVal[string(strKey)] + if !found { + return nil, false + } + return String(keyVal), true +} + +// Iterator creates a new traits.Iterator from the string key set of the map. +func (a *stringMapAccessor) Iterator() traits.Iterator { + // Copy the keys to make their order stable. + mapKeys := make([]string, len(a.mapVal)) + i := 0 + for k := range a.mapVal { + mapKeys[i] = k + i++ + } + return &stringKeyIterator{ + mapKeys: mapKeys, + len: len(mapKeys), + } +} + +func newStringIfaceMapAccessor(adapter ref.TypeAdapter, mapVal map[string]interface{}) mapAccessor { + return &stringIfaceMapAccessor{ + TypeAdapter: adapter, + mapVal: mapVal, + } +} + +type stringIfaceMapAccessor struct { + ref.TypeAdapter + mapVal map[string]interface{} +} + +// Find uses native map accesses to find the key, returning (value, true) if present. +// +// If the key is not found the function returns (nil, false). +func (a *stringIfaceMapAccessor) Find(key ref.Val) (ref.Val, bool) { + strKey, ok := key.(String) + if !ok { + return nil, false + } + keyVal, found := a.mapVal[string(strKey)] + if !found { + return nil, false + } + return a.NativeToValue(keyVal), true +} + +// Iterator creates a new traits.Iterator from the string key set of the map. +func (a *stringIfaceMapAccessor) Iterator() traits.Iterator { + // Copy the keys to make their order stable. + mapKeys := make([]string, len(a.mapVal)) + i := 0 + for k := range a.mapVal { + mapKeys[i] = k + i++ + } + return &stringKeyIterator{ + mapKeys: mapKeys, + len: len(mapKeys), + } +} + +// protoMap is a specialized, separate implementation of the traits.Mapper interfaces tailored to +// accessing protoreflect.Map values. +type protoMap struct { + ref.TypeAdapter + value *pb.Map +} + +// Contains returns whether the map contains the given key. +func (m *protoMap) Contains(key ref.Val) ref.Val { + _, found := m.Find(key) + return Bool(found) +} + +// ConvertToNative implements the ref.Val interface method. +// +// Note, assignment to Golang struct types is not yet supported. +func (m *protoMap) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + // If the map is already assignable to the desired type return it, e.g. interfaces and + // maps with the same key value types. + switch typeDesc { + case anyValueType: + json, err := m.ConvertToNative(jsonStructType) + if err != nil { + return nil, err + } + return anypb.New(json.(proto.Message)) + case jsonValueType, jsonStructType: + jsonEntries, err := + m.ConvertToNative(reflect.TypeOf(map[string]*structpb.Value{})) + if err != nil { + return nil, err + } + jsonMap := &structpb.Struct{ + Fields: jsonEntries.(map[string]*structpb.Value)} + if typeDesc == jsonStructType { + return jsonMap, nil + } + return structpb.NewStructValue(jsonMap), nil + } + switch typeDesc.Kind() { + case reflect.Struct, reflect.Ptr: + if reflect.TypeOf(m.value).AssignableTo(typeDesc) { + return m.value, nil + } + if reflect.TypeOf(m).AssignableTo(typeDesc) { + return m, nil + } + } + if typeDesc.Kind() != reflect.Map { + return nil, fmt.Errorf("unsupported type conversion: %v to map", typeDesc) + } + + keyType := m.value.KeyType.ReflectType() + valType := m.value.ValueType.ReflectType() + otherKeyType := typeDesc.Key() + otherValType := typeDesc.Elem() + mapVal := reflect.MakeMapWithSize(typeDesc, m.value.Len()) + var err error + m.value.Range(func(key protoreflect.MapKey, val protoreflect.Value) bool { + ntvKey := key.Interface() + ntvVal := val.Interface() + switch ntvVal.(type) { + case protoreflect.Message: + ntvVal = ntvVal.(protoreflect.Message).Interface() + } + if keyType == otherKeyType && valType == otherValType { + mapVal.SetMapIndex(reflect.ValueOf(ntvKey), reflect.ValueOf(ntvVal)) + return true + } + celKey := m.NativeToValue(ntvKey) + celVal := m.NativeToValue(ntvVal) + ntvKey, err = celKey.ConvertToNative(otherKeyType) + if err != nil { + // early terminate the range loop. + return false + } + ntvVal, err = celVal.ConvertToNative(otherValType) + if err != nil { + // early terminate the range loop. + return false + } + mapVal.SetMapIndex(reflect.ValueOf(ntvKey), reflect.ValueOf(ntvVal)) + return true + }) + if err != nil { + return nil, err + } + return mapVal.Interface(), nil +} + +// ConvertToType implements the ref.Val interface method. +func (m *protoMap) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case MapType: + return m + case TypeType: + return MapType + } + return NewErr("type conversion error from '%s' to '%s'", MapType, typeVal) +} + +// Equal implements the ref.Val interface method. +func (m *protoMap) Equal(other ref.Val) ref.Val { + otherMap, ok := other.(traits.Mapper) + if !ok { + return False + } + if m.value.Map.Len() != int(otherMap.Size().(Int)) { + return False + } + var retVal ref.Val = True + m.value.Range(func(key protoreflect.MapKey, val protoreflect.Value) bool { + keyVal := m.NativeToValue(key.Interface()) + valVal := m.NativeToValue(val) + otherVal, found := otherMap.Find(keyVal) + if !found { + retVal = False + return false + } + valEq := Equal(valVal, otherVal) + if valEq != True { + retVal = valEq + return false + } + return true + }) + return retVal +} + +// Find returns whether the protoreflect.Map contains the input key. +// +// If the key is not found the function returns (nil, false). +func (m *protoMap) Find(key ref.Val) (ref.Val, bool) { + if keyVal, found := m.findInternal(key); found { + return keyVal, true + } + switch k := key.(type) { + // Double is not a valid proto map key type, so check for the key as an int or uint. + case Double: + if ik, ok := doubleToInt64Lossless(float64(k)); ok { + if keyVal, found := m.findInternal(Int(ik)); found { + return keyVal, true + } + } + if uk, ok := doubleToUint64Lossless(float64(k)); ok { + return m.findInternal(Uint(uk)) + } + // map keys of type double are not supported. + case Int: + if uk, ok := int64ToUint64Lossless(int64(k)); ok { + return m.findInternal(Uint(uk)) + } + case Uint: + if ik, ok := uint64ToInt64Lossless(uint64(k)); ok { + return m.findInternal(Int(ik)) + } + } + return nil, false +} + +// findInternal attempts to convert the incoming key to the map's internal native type +// and then returns the value, if found. +func (m *protoMap) findInternal(key ref.Val) (ref.Val, bool) { + // Convert the input key to the expected protobuf key type. + ntvKey, err := key.ConvertToNative(m.value.KeyType.ReflectType()) + if err != nil { + return nil, false + } + // Use protoreflection to get the key value. + val := m.value.Get(protoreflect.ValueOf(ntvKey).MapKey()) + if !val.IsValid() { + return nil, false + } + // Perform nominal type unwrapping from the input value. + switch v := val.Interface().(type) { + case protoreflect.List, protoreflect.Map: + // Maps do not support list or map values + return nil, false + default: + return m.NativeToValue(v), true + } +} + +// Get implements the traits.Indexer interface method. +func (m *protoMap) Get(key ref.Val) ref.Val { + v, found := m.Find(key) + if !found { + return ValOrErr(v, "no such key: %v", key) + } + return v +} + +// Iterator implements the traits.Iterable interface method. +func (m *protoMap) Iterator() traits.Iterator { + // Copy the keys to make their order stable. + mapKeys := make([]protoreflect.MapKey, 0, m.value.Len()) + m.value.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { + mapKeys = append(mapKeys, k) + return true + }) + return &protoMapIterator{ + TypeAdapter: m.TypeAdapter, + mapKeys: mapKeys, + len: m.value.Len(), + } +} + +// Size returns the number of entries in the protoreflect.Map. +func (m *protoMap) Size() ref.Val { + return Int(m.value.Len()) +} + +// Type implements the ref.Val interface method. +func (m *protoMap) Type() ref.Type { + return MapType +} + +// Value implements the ref.Val interface method. +func (m *protoMap) Value() interface{} { + return m.value +} + +type mapIterator struct { + *baseIterator + ref.TypeAdapter + mapKeys *reflect.MapIter + cursor int + len int +} + +// HasNext implements the traits.Iterator interface method. +func (it *mapIterator) HasNext() ref.Val { + return Bool(it.cursor < it.len) +} + +// Next implements the traits.Iterator interface method. +func (it *mapIterator) Next() ref.Val { + if it.HasNext() == True && it.mapKeys.Next() { + it.cursor++ + refKey := it.mapKeys.Key() + return it.NativeToValue(refKey.Interface()) + } + return nil +} + +type protoMapIterator struct { + *baseIterator + ref.TypeAdapter + mapKeys []protoreflect.MapKey + cursor int + len int +} + +// HasNext implements the traits.Iterator interface method. +func (it *protoMapIterator) HasNext() ref.Val { + return Bool(it.cursor < it.len) +} + +// Next implements the traits.Iterator interface method. +func (it *protoMapIterator) Next() ref.Val { + if it.HasNext() == True { + index := it.cursor + it.cursor++ + refKey := it.mapKeys[index] + return it.NativeToValue(refKey.Interface()) + } + return nil +} + +type stringKeyIterator struct { + *baseIterator + mapKeys []string + cursor int + len int +} + +// HasNext implements the traits.Iterator interface method. +func (it *stringKeyIterator) HasNext() ref.Val { + return Bool(it.cursor < it.len) +} + +// Next implements the traits.Iterator interface method. +func (it *stringKeyIterator) Next() ref.Val { + if it.HasNext() == True { + index := it.cursor + it.cursor++ + return String(it.mapKeys[index]) + } + return nil +} diff --git a/vendor/github.com/google/cel-go/common/types/null.go b/vendor/github.com/google/cel-go/common/types/null.go new file mode 100644 index 0000000000..3d3503c275 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/null.go @@ -0,0 +1,97 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "fmt" + "reflect" + + "github.com/google/cel-go/common/types/ref" + "google.golang.org/protobuf/proto" + + anypb "google.golang.org/protobuf/types/known/anypb" + structpb "google.golang.org/protobuf/types/known/structpb" +) + +// Null type implementation. +type Null structpb.NullValue + +var ( + // NullType singleton. + NullType = NewTypeValue("null_type") + // NullValue singleton. + NullValue = Null(structpb.NullValue_NULL_VALUE) + + jsonNullType = reflect.TypeOf(structpb.NullValue_NULL_VALUE) +) + +// ConvertToNative implements ref.Val.ConvertToNative. +func (n Null) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + switch typeDesc.Kind() { + case reflect.Int32: + return reflect.ValueOf(n).Convert(typeDesc).Interface(), nil + case reflect.Ptr: + switch typeDesc { + case anyValueType: + // Convert to a JSON-null before packing to an Any field since the enum value for JSON + // null cannot be packed directly. + pb, err := n.ConvertToNative(jsonValueType) + if err != nil { + return nil, err + } + return anypb.New(pb.(proto.Message)) + case jsonValueType: + return structpb.NewNullValue(), nil + } + case reflect.Interface: + nv := n.Value() + if reflect.TypeOf(nv).Implements(typeDesc) { + return nv, nil + } + if reflect.TypeOf(n).Implements(typeDesc) { + return n, nil + } + } + // If the type conversion isn't supported return an error. + return nil, fmt.Errorf("type conversion error from '%v' to '%v'", NullType, typeDesc) +} + +// ConvertToType implements ref.Val.ConvertToType. +func (n Null) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case StringType: + return String("null") + case NullType: + return n + case TypeType: + return NullType + } + return NewErr("type conversion error from '%s' to '%s'", NullType, typeVal) +} + +// Equal implements ref.Val.Equal. +func (n Null) Equal(other ref.Val) ref.Val { + return Bool(NullType == other.Type()) +} + +// Type implements ref.Val.Type. +func (n Null) Type() ref.Type { + return NullType +} + +// Value implements ref.Val.Value. +func (n Null) Value() interface{} { + return structpb.NullValue_NULL_VALUE +} diff --git a/vendor/github.com/google/cel-go/common/types/object.go b/vendor/github.com/google/cel-go/common/types/object.go new file mode 100644 index 0000000000..5e4212fa3f --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/object.go @@ -0,0 +1,156 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "fmt" + "reflect" + + "github.com/google/cel-go/common/types/pb" + "github.com/google/cel-go/common/types/ref" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + + anypb "google.golang.org/protobuf/types/known/anypb" + structpb "google.golang.org/protobuf/types/known/structpb" +) + +type protoObj struct { + ref.TypeAdapter + value proto.Message + typeDesc *pb.TypeDescription + typeValue *TypeValue +} + +// NewObject returns an object based on a proto.Message value which handles +// conversion between protobuf type values and expression type values. +// Objects support indexing and iteration. +// +// Note: the type value is pulled from the list of registered types within the +// type provider. If the proto type is not registered within the type provider, +// then this will result in an error within the type adapter / provider. +func NewObject(adapter ref.TypeAdapter, + typeDesc *pb.TypeDescription, + typeValue *TypeValue, + value proto.Message) ref.Val { + return &protoObj{ + TypeAdapter: adapter, + value: value, + typeDesc: typeDesc, + typeValue: typeValue} +} + +func (o *protoObj) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + pb := o.value + if reflect.TypeOf(pb).AssignableTo(typeDesc) { + return pb, nil + } + if reflect.TypeOf(o).AssignableTo(typeDesc) { + return o, nil + } + switch typeDesc { + case anyValueType: + _, isAny := pb.(*anypb.Any) + if isAny { + return pb, nil + } + return anypb.New(pb) + case jsonValueType: + // Marshal the proto to JSON first, and then rehydrate as protobuf.Value as there is no + // support for direct conversion from proto.Message to protobuf.Value. + bytes, err := protojson.Marshal(pb) + if err != nil { + return nil, err + } + json := &structpb.Value{} + err = protojson.Unmarshal(bytes, json) + if err != nil { + return nil, err + } + return json, nil + default: + if typeDesc == o.typeDesc.ReflectType() { + return o.value, nil + } + if typeDesc.Kind() == reflect.Ptr { + val := reflect.New(typeDesc.Elem()).Interface() + dstPB, ok := val.(proto.Message) + if ok { + proto.Merge(dstPB, pb) + return dstPB, nil + } + } + } + return nil, fmt.Errorf("type conversion error from '%T' to '%v'", o.value, typeDesc) +} + +func (o *protoObj) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + default: + if o.Type().TypeName() == typeVal.TypeName() { + return o + } + case TypeType: + return o.typeValue + } + return NewErr("type conversion error from '%s' to '%s'", o.typeDesc.Name(), typeVal) +} + +func (o *protoObj) Equal(other ref.Val) ref.Val { + otherPB, ok := other.Value().(proto.Message) + return Bool(ok && pb.Equal(o.value, otherPB)) +} + +// IsSet tests whether a field which is defined is set to a non-default value. +func (o *protoObj) IsSet(field ref.Val) ref.Val { + protoFieldName, ok := field.(String) + if !ok { + return MaybeNoSuchOverloadErr(field) + } + protoFieldStr := string(protoFieldName) + fd, found := o.typeDesc.FieldByName(protoFieldStr) + if !found { + return NewErr("no such field '%s'", field) + } + if fd.IsSet(o.value) { + return True + } + return False +} + +func (o *protoObj) Get(index ref.Val) ref.Val { + protoFieldName, ok := index.(String) + if !ok { + return MaybeNoSuchOverloadErr(index) + } + protoFieldStr := string(protoFieldName) + fd, found := o.typeDesc.FieldByName(protoFieldStr) + if !found { + return NewErr("no such field '%s'", index) + } + fv, err := fd.GetFrom(o.value) + if err != nil { + return NewErr(err.Error()) + } + return o.NativeToValue(fv) +} + +func (o *protoObj) Type() ref.Type { + return o.typeValue +} + +func (o *protoObj) Value() interface{} { + return o.value +} diff --git a/vendor/github.com/google/cel-go/common/types/overflow.go b/vendor/github.com/google/cel-go/common/types/overflow.go new file mode 100644 index 0000000000..c68a921826 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/overflow.go @@ -0,0 +1,389 @@ +// Copyright 2021 Google LLC +// +// 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. + +package types + +import ( + "math" + "time" +) + +var ( + doubleTwoTo64 = math.Ldexp(1.0, 64) +) + +// addInt64Checked performs addition with overflow detection of two int64 values. +// +// If the operation fails the error return value will be non-nil. +func addInt64Checked(x, y int64) (int64, error) { + if (y > 0 && x > math.MaxInt64-y) || (y < 0 && x < math.MinInt64-y) { + return 0, errIntOverflow + } + return x + y, nil +} + +// subtractInt64Checked performs subtraction with overflow detection of two int64 values. +// +// If the operation fails the error return value will be non-nil. +func subtractInt64Checked(x, y int64) (int64, error) { + if (y < 0 && x > math.MaxInt64+y) || (y > 0 && x < math.MinInt64+y) { + return 0, errIntOverflow + } + return x - y, nil +} + +// negateInt64Checked performs negation with overflow detection of an int64. +// +// If the operation fails the error return value will be non-nil. +func negateInt64Checked(x int64) (int64, error) { + // In twos complement, negating MinInt64 would result in a valid of MaxInt64+1. + if x == math.MinInt64 { + return 0, errIntOverflow + } + return -x, nil +} + +// multiplyInt64Checked performs multiplication with overflow detection of two int64 value. +// +// If the operation fails the error return value will be non-nil. +func multiplyInt64Checked(x, y int64) (int64, error) { + // Detecting multiplication overflow is more complicated than the others. The first two detect + // attempting to negate MinInt64, which would result in MaxInt64+1. The other four detect normal + // overflow conditions. + if (x == -1 && y == math.MinInt64) || (y == -1 && x == math.MinInt64) || + // x is positive, y is positive + (x > 0 && y > 0 && x > math.MaxInt64/y) || + // x is positive, y is negative + (x > 0 && y < 0 && y < math.MinInt64/x) || + // x is negative, y is positive + (x < 0 && y > 0 && x < math.MinInt64/y) || + // x is negative, y is negative + (x < 0 && y < 0 && y < math.MaxInt64/x) { + return 0, errIntOverflow + } + return x * y, nil +} + +// divideInt64Checked performs division with overflow detection of two int64 values, +// as well as a division by zero check. +// +// If the operation fails the error return value will be non-nil. +func divideInt64Checked(x, y int64) (int64, error) { + // Division by zero. + if y == 0 { + return 0, errDivideByZero + } + // In twos complement, negating MinInt64 would result in a valid of MaxInt64+1. + if x == math.MinInt64 && y == -1 { + return 0, errIntOverflow + } + return x / y, nil +} + +// moduloInt64Checked performs modulo with overflow detection of two int64 values +// as well as a modulus by zero check. +// +// If the operation fails the error return value will be non-nil. +func moduloInt64Checked(x, y int64) (int64, error) { + // Modulus by zero. + if y == 0 { + return 0, errModulusByZero + } + // In twos complement, negating MinInt64 would result in a valid of MaxInt64+1. + if x == math.MinInt64 && y == -1 { + return 0, errIntOverflow + } + return x % y, nil +} + +// addUint64Checked performs addition with overflow detection of two uint64 values. +// +// If the operation fails due to overflow the error return value will be non-nil. +func addUint64Checked(x, y uint64) (uint64, error) { + if y > 0 && x > math.MaxUint64-y { + return 0, errUintOverflow + } + return x + y, nil +} + +// subtractUint64Checked performs subtraction with overflow detection of two uint64 values. +// +// If the operation fails due to overflow the error return value will be non-nil. +func subtractUint64Checked(x, y uint64) (uint64, error) { + if y > x { + return 0, errUintOverflow + } + return x - y, nil +} + +// multiplyUint64Checked performs multiplication with overflow detection of two uint64 values. +// +// If the operation fails due to overflow the error return value will be non-nil. +func multiplyUint64Checked(x, y uint64) (uint64, error) { + if y != 0 && x > math.MaxUint64/y { + return 0, errUintOverflow + } + return x * y, nil +} + +// divideUint64Checked performs division with a test for division by zero. +// +// If the operation fails the error return value will be non-nil. +func divideUint64Checked(x, y uint64) (uint64, error) { + if y == 0 { + return 0, errDivideByZero + } + return x / y, nil +} + +// moduloUint64Checked performs modulo with a test for modulus by zero. +// +// If the operation fails the error return value will be non-nil. +func moduloUint64Checked(x, y uint64) (uint64, error) { + if y == 0 { + return 0, errModulusByZero + } + return x % y, nil +} + +// addDurationChecked performs addition with overflow detection of two time.Durations. +// +// If the operation fails due to overflow the error return value will be non-nil. +func addDurationChecked(x, y time.Duration) (time.Duration, error) { + val, err := addInt64Checked(int64(x), int64(y)) + if err != nil { + return time.Duration(0), err + } + return time.Duration(val), nil +} + +// subtractDurationChecked performs subtraction with overflow detection of two time.Durations. +// +// If the operation fails due to overflow the error return value will be non-nil. +func subtractDurationChecked(x, y time.Duration) (time.Duration, error) { + val, err := subtractInt64Checked(int64(x), int64(y)) + if err != nil { + return time.Duration(0), err + } + return time.Duration(val), nil +} + +// negateDurationChecked performs negation with overflow detection of a time.Duration. +// +// If the operation fails due to overflow the error return value will be non-nil. +func negateDurationChecked(x time.Duration) (time.Duration, error) { + val, err := negateInt64Checked(int64(x)) + if err != nil { + return time.Duration(0), err + } + return time.Duration(val), nil +} + +// addDurationChecked performs addition with overflow detection of a time.Time and time.Duration. +// +// If the operation fails due to overflow the error return value will be non-nil. +func addTimeDurationChecked(x time.Time, y time.Duration) (time.Time, error) { + // This is tricky. A time is represented as (int64, int32) where the first is seconds and second + // is nanoseconds. A duration is int64 representing nanoseconds. We cannot normalize time to int64 + // as it could potentially overflow. The only way to proceed is to break time and duration into + // second and nanosecond components. + + // First we break time into its components by truncating and subtracting. + sec1 := x.Truncate(time.Second).Unix() // Truncate to seconds. + nsec1 := x.Sub(x.Truncate(time.Second)).Nanoseconds() // Get nanoseconds by truncating and subtracting. + + // Second we break duration into its components by dividing and modulo. + sec2 := int64(y) / int64(time.Second) // Truncate to seconds. + nsec2 := int64(y) % int64(time.Second) // Get remainder. + + // Add seconds first, detecting any overflow. + sec, err := addInt64Checked(sec1, sec2) + if err != nil { + return time.Time{}, err + } + // Nanoseconds cannot overflow as time.Time normalizes them to [0, 999999999]. + nsec := nsec1 + nsec2 + + // We need to normalize nanoseconds to be positive and carry extra nanoseconds to seconds. + // Adapted from time.Unix(int64, int64). + if nsec < 0 || nsec >= int64(time.Second) { + // Add seconds. + sec, err = addInt64Checked(sec, nsec/int64(time.Second)) + if err != nil { + return time.Time{}, err + } + + nsec -= (nsec / int64(time.Second)) * int64(time.Second) + if nsec < 0 { + // Subtract an extra second + sec, err = addInt64Checked(sec, -1) + if err != nil { + return time.Time{}, err + } + nsec += int64(time.Second) + } + } + + // Check if the the number of seconds from Unix epoch is within our acceptable range. + if sec < minUnixTime || sec > maxUnixTime { + return time.Time{}, errTimestampOverflow + } + + // Return resulting time and propagate time zone. + return time.Unix(sec, nsec).In(x.Location()), nil +} + +// subtractTimeChecked performs subtraction with overflow detection of two time.Time. +// +// If the operation fails due to overflow the error return value will be non-nil. +func subtractTimeChecked(x, y time.Time) (time.Duration, error) { + // Similar to addTimeDurationOverflow() above. + + // First we break time into its components by truncating and subtracting. + sec1 := x.Truncate(time.Second).Unix() // Truncate to seconds. + nsec1 := x.Sub(x.Truncate(time.Second)).Nanoseconds() // Get nanoseconds by truncating and subtracting. + + // Second we break duration into its components by truncating and subtracting. + sec2 := y.Truncate(time.Second).Unix() // Truncate to seconds. + nsec2 := y.Sub(y.Truncate(time.Second)).Nanoseconds() // Get nanoseconds by truncating and subtracting. + + // Subtract seconds first, detecting any overflow. + sec, err := subtractInt64Checked(sec1, sec2) + if err != nil { + return time.Duration(0), err + } + + // Nanoseconds cannot overflow as time.Time normalizes them to [0, 999999999]. + nsec := nsec1 - nsec2 + + // Scale seconds to nanoseconds detecting overflow. + tsec, err := multiplyInt64Checked(sec, int64(time.Second)) + if err != nil { + return time.Duration(0), err + } + + // Lastly we need to add the two nanoseconds together. + val, err := addInt64Checked(tsec, nsec) + if err != nil { + return time.Duration(0), err + } + + return time.Duration(val), nil +} + +// subtractTimeDurationChecked performs subtraction with overflow detection of a time.Time and +// time.Duration. +// +// If the operation fails due to overflow the error return value will be non-nil. +func subtractTimeDurationChecked(x time.Time, y time.Duration) (time.Time, error) { + // The easiest way to implement this is to negate y and add them. + // x - y = x + -y + val, err := negateDurationChecked(y) + if err != nil { + return time.Time{}, err + } + return addTimeDurationChecked(x, val) +} + +// doubleToInt64Checked converts a double to an int64 value. +// +// If the conversion fails due to overflow the error return value will be non-nil. +func doubleToInt64Checked(v float64) (int64, error) { + if math.IsInf(v, 0) || math.IsNaN(v) || v <= float64(math.MinInt64) || v >= float64(math.MaxInt64) { + return 0, errIntOverflow + } + return int64(v), nil +} + +// doubleToInt64Checked converts a double to a uint64 value. +// +// If the conversion fails due to overflow the error return value will be non-nil. +func doubleToUint64Checked(v float64) (uint64, error) { + if math.IsInf(v, 0) || math.IsNaN(v) || v < 0 || v >= doubleTwoTo64 { + return 0, errUintOverflow + } + return uint64(v), nil +} + +// int64ToUint64Checked converts an int64 to a uint64 value. +// +// If the conversion fails due to overflow the error return value will be non-nil. +func int64ToUint64Checked(v int64) (uint64, error) { + if v < 0 { + return 0, errUintOverflow + } + return uint64(v), nil +} + +// int64ToInt32Checked converts an int64 to an int32 value. +// +// If the conversion fails due to overflow the error return value will be non-nil. +func int64ToInt32Checked(v int64) (int32, error) { + if v < math.MinInt32 || v > math.MaxInt32 { + return 0, errIntOverflow + } + return int32(v), nil +} + +// uint64ToUint32Checked converts a uint64 to a uint32 value. +// +// If the conversion fails due to overflow the error return value will be non-nil. +func uint64ToUint32Checked(v uint64) (uint32, error) { + if v > math.MaxUint32 { + return 0, errUintOverflow + } + return uint32(v), nil +} + +// uint64ToInt64Checked converts a uint64 to an int64 value. +// +// If the conversion fails due to overflow the error return value will be non-nil. +func uint64ToInt64Checked(v uint64) (int64, error) { + if v > math.MaxInt64 { + return 0, errIntOverflow + } + return int64(v), nil +} + +func doubleToUint64Lossless(v float64) (uint64, bool) { + u, err := doubleToUint64Checked(v) + if err != nil { + return 0, false + } + if float64(u) != v { + return 0, false + } + return u, true +} + +func doubleToInt64Lossless(v float64) (int64, bool) { + i, err := doubleToInt64Checked(v) + if err != nil { + return 0, false + } + if float64(i) != v { + return 0, false + } + return i, true +} + +func int64ToUint64Lossless(v int64) (uint64, bool) { + u, err := int64ToUint64Checked(v) + return u, err == nil +} + +func uint64ToInt64Lossless(v uint64) (int64, bool) { + i, err := uint64ToInt64Checked(v) + return i, err == nil +} diff --git a/vendor/github.com/google/cel-go/common/types/pb/checked.go b/vendor/github.com/google/cel-go/common/types/pb/checked.go new file mode 100644 index 0000000000..312a6a072f --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/pb/checked.go @@ -0,0 +1,93 @@ +// Copyright 2018 Google LLC +// +// 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. + +package pb + +import ( + "google.golang.org/protobuf/reflect/protoreflect" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" + emptypb "google.golang.org/protobuf/types/known/emptypb" + structpb "google.golang.org/protobuf/types/known/structpb" +) + +var ( + // CheckedPrimitives map from proto field descriptor type to expr.Type. + CheckedPrimitives = map[protoreflect.Kind]*exprpb.Type{ + protoreflect.BoolKind: checkedBool, + protoreflect.BytesKind: checkedBytes, + protoreflect.DoubleKind: checkedDouble, + protoreflect.FloatKind: checkedDouble, + protoreflect.Int32Kind: checkedInt, + protoreflect.Int64Kind: checkedInt, + protoreflect.Sint32Kind: checkedInt, + protoreflect.Sint64Kind: checkedInt, + protoreflect.Uint32Kind: checkedUint, + protoreflect.Uint64Kind: checkedUint, + protoreflect.Fixed32Kind: checkedUint, + protoreflect.Fixed64Kind: checkedUint, + protoreflect.Sfixed32Kind: checkedInt, + protoreflect.Sfixed64Kind: checkedInt, + protoreflect.StringKind: checkedString} + + // CheckedWellKnowns map from qualified proto type name to expr.Type for + // well-known proto types. + CheckedWellKnowns = map[string]*exprpb.Type{ + // Wrapper types. + "google.protobuf.BoolValue": checkedWrap(checkedBool), + "google.protobuf.BytesValue": checkedWrap(checkedBytes), + "google.protobuf.DoubleValue": checkedWrap(checkedDouble), + "google.protobuf.FloatValue": checkedWrap(checkedDouble), + "google.protobuf.Int64Value": checkedWrap(checkedInt), + "google.protobuf.Int32Value": checkedWrap(checkedInt), + "google.protobuf.UInt64Value": checkedWrap(checkedUint), + "google.protobuf.UInt32Value": checkedWrap(checkedUint), + "google.protobuf.StringValue": checkedWrap(checkedString), + // Well-known types. + "google.protobuf.Any": checkedAny, + "google.protobuf.Duration": checkedDuration, + "google.protobuf.Timestamp": checkedTimestamp, + // Json types. + "google.protobuf.ListValue": checkedListDyn, + "google.protobuf.NullValue": checkedNull, + "google.protobuf.Struct": checkedMapStringDyn, + "google.protobuf.Value": checkedDyn, + } + + // common types + checkedDyn = &exprpb.Type{TypeKind: &exprpb.Type_Dyn{Dyn: &emptypb.Empty{}}} + // Wrapper and primitive types. + checkedBool = checkedPrimitive(exprpb.Type_BOOL) + checkedBytes = checkedPrimitive(exprpb.Type_BYTES) + checkedDouble = checkedPrimitive(exprpb.Type_DOUBLE) + checkedInt = checkedPrimitive(exprpb.Type_INT64) + checkedString = checkedPrimitive(exprpb.Type_STRING) + checkedUint = checkedPrimitive(exprpb.Type_UINT64) + // Well-known type equivalents. + checkedAny = checkedWellKnown(exprpb.Type_ANY) + checkedDuration = checkedWellKnown(exprpb.Type_DURATION) + checkedTimestamp = checkedWellKnown(exprpb.Type_TIMESTAMP) + // Json-based type equivalents. + checkedNull = &exprpb.Type{ + TypeKind: &exprpb.Type_Null{ + Null: structpb.NullValue_NULL_VALUE}} + checkedListDyn = &exprpb.Type{ + TypeKind: &exprpb.Type_ListType_{ + ListType: &exprpb.Type_ListType{ElemType: checkedDyn}}} + checkedMapStringDyn = &exprpb.Type{ + TypeKind: &exprpb.Type_MapType_{ + MapType: &exprpb.Type_MapType{ + KeyType: checkedString, + ValueType: checkedDyn}}} +) diff --git a/vendor/github.com/google/cel-go/common/types/pb/enum.go b/vendor/github.com/google/cel-go/common/types/pb/enum.go new file mode 100644 index 0000000000..4a26b5c7c3 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/pb/enum.go @@ -0,0 +1,44 @@ +// Copyright 2018 Google LLC +// +// 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. + +package pb + +import ( + "google.golang.org/protobuf/reflect/protoreflect" +) + +// NewEnumValueDescription produces an enum value description with the fully qualified enum value +// name and the enum value descriptor. +func NewEnumValueDescription(name string, desc protoreflect.EnumValueDescriptor) *EnumValueDescription { + return &EnumValueDescription{ + enumValueName: name, + desc: desc, + } +} + +// EnumValueDescription maps a fully-qualified enum value name to its numeric value. +type EnumValueDescription struct { + enumValueName string + desc protoreflect.EnumValueDescriptor +} + +// Name returns the fully-qualified identifier name for the enum value. +func (ed *EnumValueDescription) Name() string { + return ed.enumValueName +} + +// Value returns the (numeric) value of the enum. +func (ed *EnumValueDescription) Value() int32 { + return int32(ed.desc.Number()) +} diff --git a/vendor/github.com/google/cel-go/common/types/pb/equal.go b/vendor/github.com/google/cel-go/common/types/pb/equal.go new file mode 100644 index 0000000000..392a949619 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/pb/equal.go @@ -0,0 +1,205 @@ +// Copyright 2022 Google LLC +// +// 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. + +package pb + +import ( + "bytes" + "reflect" + + "google.golang.org/protobuf/encoding/protowire" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/known/anypb" +) + +// Equal returns whether two proto.Message instances are equal using the following criteria: +// +// - Messages must share the same instance of the type descriptor +// - Known set fields are compared using semantics equality +// - Bytes are compared using bytes.Equal +// - Scalar values are compared with operator == +// - List and map types are equal if they have the same length and all elements are equal +// - Messages are equal if they share the same descriptor and all set fields are equal +// - Unknown fields are compared using byte equality +// - NaN values are not equal to each other +// - google.protobuf.Any values are unpacked before comparison +// - If the type descriptor for a protobuf.Any cannot be found, byte equality is used rather than +// semantic equality. +// +// This method of proto equality mirrors the behavior of the C++ protobuf MessageDifferencer +// whereas the golang proto.Equal implementation mirrors the Java protobuf equals() methods +// behaviors which needed to treat NaN values as equal due to Java semantics. +func Equal(x, y proto.Message) bool { + if x == nil || y == nil { + return x == nil && y == nil + } + xRef := x.ProtoReflect() + yRef := y.ProtoReflect() + return equalMessage(xRef, yRef) +} + +func equalMessage(mx, my protoreflect.Message) bool { + // Note, the original proto.Equal upon which this implementation is based does not specifically handle the + // case when both messages are invalid. It is assumed that the descriptors will be equal and that byte-wise + // comparison will be used, though the semantics of validity are neither clear, nor promised within the + // proto.Equal implementation. + if mx.IsValid() != my.IsValid() || mx.Descriptor() != my.Descriptor() { + return false + } + + // This is an innovation on the default proto.Equal where protobuf.Any values are unpacked before comparison + // as otherwise the Any values are compared by bytes rather than structurally. + if isAny(mx) && isAny(my) { + ax := mx.Interface().(*anypb.Any) + ay := my.Interface().(*anypb.Any) + // If the values are not the same type url, return false. + if ax.GetTypeUrl() != ay.GetTypeUrl() { + return false + } + // If the values are byte equal, then return true. + if bytes.Equal(ax.GetValue(), ay.GetValue()) { + return true + } + // Otherwise fall through to the semantic comparison of the any values. + x, err := ax.UnmarshalNew() + if err != nil { + return false + } + y, err := ay.UnmarshalNew() + if err != nil { + return false + } + // Recursively compare the unwrapped messages to ensure nested Any values are unwrapped accordingly. + return equalMessage(x.ProtoReflect(), y.ProtoReflect()) + } + + // Walk the set fields to determine field-wise equality + nx := 0 + equal := true + mx.Range(func(fd protoreflect.FieldDescriptor, vx protoreflect.Value) bool { + nx++ + equal = my.Has(fd) && equalField(fd, vx, my.Get(fd)) + return equal + }) + if !equal { + return false + } + // Establish the count of set fields on message y + ny := 0 + my.Range(func(protoreflect.FieldDescriptor, protoreflect.Value) bool { + ny++ + return true + }) + // If the number of set fields is not equal return false. + if nx != ny { + return false + } + + return equalUnknown(mx.GetUnknown(), my.GetUnknown()) +} + +func equalField(fd protoreflect.FieldDescriptor, x, y protoreflect.Value) bool { + switch { + case fd.IsMap(): + return equalMap(fd, x.Map(), y.Map()) + case fd.IsList(): + return equalList(fd, x.List(), y.List()) + default: + return equalValue(fd, x, y) + } +} + +func equalMap(fd protoreflect.FieldDescriptor, x, y protoreflect.Map) bool { + if x.Len() != y.Len() { + return false + } + equal := true + x.Range(func(k protoreflect.MapKey, vx protoreflect.Value) bool { + vy := y.Get(k) + equal = y.Has(k) && equalValue(fd.MapValue(), vx, vy) + return equal + }) + return equal +} + +func equalList(fd protoreflect.FieldDescriptor, x, y protoreflect.List) bool { + if x.Len() != y.Len() { + return false + } + for i := x.Len() - 1; i >= 0; i-- { + if !equalValue(fd, x.Get(i), y.Get(i)) { + return false + } + } + return true +} + +func equalValue(fd protoreflect.FieldDescriptor, x, y protoreflect.Value) bool { + switch fd.Kind() { + case protoreflect.BoolKind: + return x.Bool() == y.Bool() + case protoreflect.EnumKind: + return x.Enum() == y.Enum() + case protoreflect.Int32Kind, protoreflect.Sint32Kind, + protoreflect.Int64Kind, protoreflect.Sint64Kind, + protoreflect.Sfixed32Kind, protoreflect.Sfixed64Kind: + return x.Int() == y.Int() + case protoreflect.Uint32Kind, protoreflect.Uint64Kind, + protoreflect.Fixed32Kind, protoreflect.Fixed64Kind: + return x.Uint() == y.Uint() + case protoreflect.FloatKind, protoreflect.DoubleKind: + return x.Float() == y.Float() + case protoreflect.StringKind: + return x.String() == y.String() + case protoreflect.BytesKind: + return bytes.Equal(x.Bytes(), y.Bytes()) + case protoreflect.MessageKind, protoreflect.GroupKind: + return equalMessage(x.Message(), y.Message()) + default: + return x.Interface() == y.Interface() + } +} + +func equalUnknown(x, y protoreflect.RawFields) bool { + lenX := len(x) + lenY := len(y) + if lenX != lenY { + return false + } + if lenX == 0 { + return true + } + if bytes.Equal([]byte(x), []byte(y)) { + return true + } + + mx := make(map[protoreflect.FieldNumber]protoreflect.RawFields) + my := make(map[protoreflect.FieldNumber]protoreflect.RawFields) + for len(x) > 0 { + fnum, _, n := protowire.ConsumeField(x) + mx[fnum] = append(mx[fnum], x[:n]...) + x = x[n:] + } + for len(y) > 0 { + fnum, _, n := protowire.ConsumeField(y) + my[fnum] = append(my[fnum], y[:n]...) + y = y[n:] + } + return reflect.DeepEqual(mx, my) +} + +func isAny(m protoreflect.Message) bool { + return string(m.Descriptor().FullName()) == "google.protobuf.Any" +} diff --git a/vendor/github.com/google/cel-go/common/types/pb/file.go b/vendor/github.com/google/cel-go/common/types/pb/file.go new file mode 100644 index 0000000000..0bcade75f9 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/pb/file.go @@ -0,0 +1,141 @@ +// Copyright 2018 Google LLC +// +// 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. + +package pb + +import ( + "fmt" + + "google.golang.org/protobuf/reflect/protoreflect" +) + +// NewFileDescription returns a FileDescription instance with a complete listing of all the message +// types and enum values declared within any scope in the file. +func NewFileDescription(fileDesc protoreflect.FileDescriptor, pbdb *Db) *FileDescription { + metadata := collectFileMetadata(fileDesc) + enums := make(map[string]*EnumValueDescription) + for name, enumVal := range metadata.enumValues { + enums[name] = NewEnumValueDescription(name, enumVal) + } + types := make(map[string]*TypeDescription) + for name, msgType := range metadata.msgTypes { + types[name] = NewTypeDescription(name, msgType) + } + return &FileDescription{ + types: types, + enums: enums, + } +} + +// FileDescription holds a map of all types and enum values declared within a proto file. +type FileDescription struct { + types map[string]*TypeDescription + enums map[string]*EnumValueDescription +} + +// GetEnumDescription returns an EnumDescription for a qualified enum value +// name declared within the .proto file. +func (fd *FileDescription) GetEnumDescription(enumName string) (*EnumValueDescription, bool) { + ed, found := fd.enums[sanitizeProtoName(enumName)] + return ed, found +} + +// GetEnumNames returns the string names of all enum values in the file. +func (fd *FileDescription) GetEnumNames() []string { + enumNames := make([]string, len(fd.enums)) + i := 0 + for _, e := range fd.enums { + enumNames[i] = e.Name() + i++ + } + return enumNames +} + +// GetTypeDescription returns a TypeDescription for a qualified protobuf message type name +// declared within the .proto file. +func (fd *FileDescription) GetTypeDescription(typeName string) (*TypeDescription, bool) { + td, found := fd.types[sanitizeProtoName(typeName)] + return td, found +} + +// GetTypeNames returns the list of all type names contained within the file. +func (fd *FileDescription) GetTypeNames() []string { + typeNames := make([]string, len(fd.types)) + i := 0 + for _, t := range fd.types { + typeNames[i] = t.Name() + i++ + } + return typeNames +} + +// sanitizeProtoName strips the leading '.' from the proto message name. +func sanitizeProtoName(name string) string { + if name != "" && name[0] == '.' { + return name[1:] + } + return name +} + +// fileMetadata is a flattened view of message types and enum values within a file descriptor. +type fileMetadata struct { + // msgTypes maps from fully-qualified message name to descriptor. + msgTypes map[string]protoreflect.MessageDescriptor + // enumValues maps from fully-qualified enum value to enum value descriptor. + enumValues map[string]protoreflect.EnumValueDescriptor + // TODO: support enum type definitions for use in future type-check enhancements. +} + +// collectFileMetadata traverses the proto file object graph to collect message types and enum +// values and index them by their fully qualified names. +func collectFileMetadata(fileDesc protoreflect.FileDescriptor) *fileMetadata { + msgTypes := make(map[string]protoreflect.MessageDescriptor) + enumValues := make(map[string]protoreflect.EnumValueDescriptor) + collectMsgTypes(fileDesc.Messages(), msgTypes, enumValues) + collectEnumValues(fileDesc.Enums(), enumValues) + return &fileMetadata{ + msgTypes: msgTypes, + enumValues: enumValues, + } +} + +// collectMsgTypes recursively collects messages, nested messages, and nested enums into a map of +// fully qualified protobuf names to descriptors. +func collectMsgTypes(msgTypes protoreflect.MessageDescriptors, msgTypeMap map[string]protoreflect.MessageDescriptor, enumValueMap map[string]protoreflect.EnumValueDescriptor) { + for i := 0; i < msgTypes.Len(); i++ { + msgType := msgTypes.Get(i) + msgTypeMap[string(msgType.FullName())] = msgType + nestedMsgTypes := msgType.Messages() + if nestedMsgTypes.Len() != 0 { + collectMsgTypes(nestedMsgTypes, msgTypeMap, enumValueMap) + } + nestedEnumTypes := msgType.Enums() + if nestedEnumTypes.Len() != 0 { + collectEnumValues(nestedEnumTypes, enumValueMap) + } + } +} + +// collectEnumValues accumulates the enum values within an enum declaration. +func collectEnumValues(enumTypes protoreflect.EnumDescriptors, enumValueMap map[string]protoreflect.EnumValueDescriptor) { + for i := 0; i < enumTypes.Len(); i++ { + enumType := enumTypes.Get(i) + enumTypeValues := enumType.Values() + for j := 0; j < enumTypeValues.Len(); j++ { + enumValue := enumTypeValues.Get(j) + enumValueName := fmt.Sprintf("%s.%s", string(enumType.FullName()), string(enumValue.Name())) + enumValueMap[enumValueName] = enumValue + } + } +} diff --git a/vendor/github.com/google/cel-go/common/types/pb/pb.go b/vendor/github.com/google/cel-go/common/types/pb/pb.go new file mode 100644 index 0000000000..d02fbe0c25 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/pb/pb.go @@ -0,0 +1,196 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Package pb reflects over protocol buffer descriptors to generate objects +// that simplify type, enum, and field lookup. +package pb + +import ( + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + + anypb "google.golang.org/protobuf/types/known/anypb" + durpb "google.golang.org/protobuf/types/known/durationpb" + emptypb "google.golang.org/protobuf/types/known/emptypb" + structpb "google.golang.org/protobuf/types/known/structpb" + tspb "google.golang.org/protobuf/types/known/timestamppb" + wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" +) + +// Db maps from file / message / enum name to file description. +// +// Each Db is isolated from each other, and while information about protobuf descriptors may be +// fetched from the global protobuf registry, no descriptors are added to this registry, else +// the isolation guarantees of the Db object would be violated. +type Db struct { + revFileDescriptorMap map[string]*FileDescription + // files contains the deduped set of FileDescriptions whose types are contained in the pb.Db. + files []*FileDescription +} + +var ( + // DefaultDb used at evaluation time or unless overridden at check time. + DefaultDb = &Db{ + revFileDescriptorMap: make(map[string]*FileDescription), + files: []*FileDescription{}, + } +) + +// NewDb creates a new `pb.Db` with an empty type name to file description map. +func NewDb() *Db { + pbdb := &Db{ + revFileDescriptorMap: make(map[string]*FileDescription), + files: []*FileDescription{}, + } + // The FileDescription objects in the default db contain lazily initialized TypeDescription + // values which may point to the state contained in the DefaultDb irrespective of this shallow + // copy; however, the type graph for a field is idempotently computed, and is guaranteed to + // only be initialized once thanks to atomic values within the TypeDescription objects, so it + // is safe to share these values across instances. + for k, v := range DefaultDb.revFileDescriptorMap { + pbdb.revFileDescriptorMap[k] = v + } + pbdb.files = append(pbdb.files, DefaultDb.files...) + return pbdb +} + +// Copy creates a copy of the current database with its own internal descriptor mapping. +func (pbdb *Db) Copy() *Db { + copy := NewDb() + for k, v := range pbdb.revFileDescriptorMap { + copy.revFileDescriptorMap[k] = v + } + for _, f := range pbdb.files { + hasFile := false + for _, f2 := range copy.files { + if f2 == f { + hasFile = true + } + } + if !hasFile { + copy.files = append(copy.files, f) + } + } + return copy +} + +// FileDescriptions returns the set of file descriptions associated with this db. +func (pbdb *Db) FileDescriptions() []*FileDescription { + return pbdb.files +} + +// RegisterDescriptor produces a `FileDescription` from a `FileDescriptor` and registers the +// message and enum types into the `pb.Db`. +func (pbdb *Db) RegisterDescriptor(fileDesc protoreflect.FileDescriptor) (*FileDescription, error) { + fd, found := pbdb.revFileDescriptorMap[fileDesc.Path()] + if found { + return fd, nil + } + // Make sure to search the global registry to see if a protoreflect.FileDescriptor for + // the file specified has been linked into the binary. If so, use the copy of the descriptor + // from the global cache. + // + // Note: Proto reflection relies on descriptor values being object equal rather than object + // equivalence. This choice means that a FieldDescriptor generated from a FileDescriptorProto + // will be incompatible with the FieldDescriptor in the global registry and any message created + // from that global registry. + globalFD, err := protoregistry.GlobalFiles.FindFileByPath(fileDesc.Path()) + if err == nil { + fileDesc = globalFD + } + fd = NewFileDescription(fileDesc, pbdb) + for _, enumValName := range fd.GetEnumNames() { + pbdb.revFileDescriptorMap[enumValName] = fd + } + for _, msgTypeName := range fd.GetTypeNames() { + pbdb.revFileDescriptorMap[msgTypeName] = fd + } + pbdb.revFileDescriptorMap[fileDesc.Path()] = fd + + // Return the specific file descriptor registered. + pbdb.files = append(pbdb.files, fd) + return fd, nil +} + +// RegisterMessage produces a `FileDescription` from a `message` and registers the message and all +// other definitions within the message file into the `pb.Db`. +func (pbdb *Db) RegisterMessage(message proto.Message) (*FileDescription, error) { + msgDesc := message.ProtoReflect().Descriptor() + msgName := msgDesc.FullName() + typeName := sanitizeProtoName(string(msgName)) + if fd, found := pbdb.revFileDescriptorMap[typeName]; found { + return fd, nil + } + return pbdb.RegisterDescriptor(msgDesc.ParentFile()) +} + +// DescribeEnum takes a qualified enum name and returns an `EnumDescription` if it exists in the +// `pb.Db`. +func (pbdb *Db) DescribeEnum(enumName string) (*EnumValueDescription, bool) { + enumName = sanitizeProtoName(enumName) + if fd, found := pbdb.revFileDescriptorMap[enumName]; found { + return fd.GetEnumDescription(enumName) + } + return nil, false +} + +// DescribeType returns a `TypeDescription` for the `typeName` if it exists in the `pb.Db`. +func (pbdb *Db) DescribeType(typeName string) (*TypeDescription, bool) { + typeName = sanitizeProtoName(typeName) + if fd, found := pbdb.revFileDescriptorMap[typeName]; found { + return fd.GetTypeDescription(typeName) + } + return nil, false +} + +// CollectFileDescriptorSet builds a file descriptor set associated with the file where the input +// message is declared. +func CollectFileDescriptorSet(message proto.Message) map[string]protoreflect.FileDescriptor { + fdMap := map[string]protoreflect.FileDescriptor{} + parentFile := message.ProtoReflect().Descriptor().ParentFile() + fdMap[parentFile.Path()] = parentFile + // Initialize list of dependencies + deps := make([]protoreflect.FileImport, parentFile.Imports().Len()) + for i := 0; i < parentFile.Imports().Len(); i++ { + deps[i] = parentFile.Imports().Get(i) + } + // Expand list for new dependencies + for i := 0; i < len(deps); i++ { + dep := deps[i] + if _, found := fdMap[dep.Path()]; found { + continue + } + fdMap[dep.Path()] = dep.FileDescriptor + for j := 0; j < dep.FileDescriptor.Imports().Len(); j++ { + deps = append(deps, dep.FileDescriptor.Imports().Get(j)) + } + } + return fdMap +} + +func init() { + // Describe well-known types to ensure they can always be resolved by the check and interpret + // execution phases. + // + // The following subset of message types is enough to ensure that all well-known types can + // resolved in the runtime, since describing the value results in describing the whole file + // where the message is declared. + DefaultDb.RegisterMessage(&anypb.Any{}) + DefaultDb.RegisterMessage(&durpb.Duration{}) + DefaultDb.RegisterMessage(&emptypb.Empty{}) + DefaultDb.RegisterMessage(&tspb.Timestamp{}) + DefaultDb.RegisterMessage(&structpb.Value{}) + DefaultDb.RegisterMessage(&wrapperspb.BoolValue{}) +} diff --git a/vendor/github.com/google/cel-go/common/types/pb/type.go b/vendor/github.com/google/cel-go/common/types/pb/type.go new file mode 100644 index 0000000000..8594008b12 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/pb/type.go @@ -0,0 +1,532 @@ +// Copyright 2018 Google LLC +// +// 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. + +package pb + +import ( + "fmt" + "reflect" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" + dynamicpb "google.golang.org/protobuf/types/dynamicpb" + anypb "google.golang.org/protobuf/types/known/anypb" + dpb "google.golang.org/protobuf/types/known/durationpb" + structpb "google.golang.org/protobuf/types/known/structpb" + tpb "google.golang.org/protobuf/types/known/timestamppb" + wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" +) + +// description is a private interface used to make it convenient to perform type unwrapping at +// the TypeDescription or FieldDescription level. +type description interface { + // Zero returns an empty immutable protobuf message when the description is a protobuf message + // type. + Zero() proto.Message +} + +// NewTypeDescription produces a TypeDescription value for the fully-qualified proto type name +// with a given descriptor. +func NewTypeDescription(typeName string, desc protoreflect.MessageDescriptor) *TypeDescription { + msgType := dynamicpb.NewMessageType(desc) + msgZero := dynamicpb.NewMessage(desc) + fieldMap := map[string]*FieldDescription{} + fields := desc.Fields() + for i := 0; i < fields.Len(); i++ { + f := fields.Get(i) + fieldMap[string(f.Name())] = NewFieldDescription(f) + } + return &TypeDescription{ + typeName: typeName, + desc: desc, + msgType: msgType, + fieldMap: fieldMap, + reflectType: reflectTypeOf(msgZero), + zeroMsg: zeroValueOf(msgZero), + } +} + +// TypeDescription is a collection of type metadata relevant to expression +// checking and evaluation. +type TypeDescription struct { + typeName string + desc protoreflect.MessageDescriptor + msgType protoreflect.MessageType + fieldMap map[string]*FieldDescription + reflectType reflect.Type + zeroMsg proto.Message +} + +// FieldMap returns a string field name to FieldDescription map. +func (td *TypeDescription) FieldMap() map[string]*FieldDescription { + return td.fieldMap +} + +// FieldByName returns (FieldDescription, true) if the field name is declared within the type. +func (td *TypeDescription) FieldByName(name string) (*FieldDescription, bool) { + fd, found := td.fieldMap[name] + if !found { + return nil, false + } + return fd, true +} + +// MaybeUnwrap accepts a proto message as input and unwraps it to a primitive CEL type if possible. +// +// This method returns the unwrapped value and 'true', else the original value and 'false'. +func (td *TypeDescription) MaybeUnwrap(msg proto.Message) (interface{}, bool) { + return unwrap(td, msg) +} + +// Name returns the fully-qualified name of the type. +func (td *TypeDescription) Name() string { + return string(td.desc.FullName()) +} + +// New returns a mutable proto message +func (td *TypeDescription) New() protoreflect.Message { + return td.msgType.New() +} + +// ReflectType returns the Golang reflect.Type for this type. +func (td *TypeDescription) ReflectType() reflect.Type { + return td.reflectType +} + +// Zero returns the zero proto.Message value for this type. +func (td *TypeDescription) Zero() proto.Message { + return td.zeroMsg +} + +// NewFieldDescription creates a new field description from a protoreflect.FieldDescriptor. +func NewFieldDescription(fieldDesc protoreflect.FieldDescriptor) *FieldDescription { + var reflectType reflect.Type + var zeroMsg proto.Message + switch fieldDesc.Kind() { + case protoreflect.EnumKind: + reflectType = reflectTypeOf(protoreflect.EnumNumber(0)) + case protoreflect.MessageKind: + zeroMsg = dynamicpb.NewMessage(fieldDesc.Message()) + reflectType = reflectTypeOf(zeroMsg) + default: + reflectType = reflectTypeOf(fieldDesc.Default().Interface()) + if fieldDesc.IsList() { + parentMsg := dynamicpb.NewMessage(fieldDesc.ContainingMessage()) + listField := parentMsg.NewField(fieldDesc).List() + elem := listField.NewElement().Interface() + switch elemType := elem.(type) { + case protoreflect.Message: + elem = elemType.Interface() + } + reflectType = reflectTypeOf(elem) + } + } + // Ensure the list type is appropriately reflected as a Go-native list. + if fieldDesc.IsList() { + reflectType = reflect.SliceOf(reflectType) + } + var keyType, valType *FieldDescription + if fieldDesc.IsMap() { + keyType = NewFieldDescription(fieldDesc.MapKey()) + valType = NewFieldDescription(fieldDesc.MapValue()) + } + return &FieldDescription{ + desc: fieldDesc, + KeyType: keyType, + ValueType: valType, + reflectType: reflectType, + zeroMsg: zeroValueOf(zeroMsg), + } +} + +// FieldDescription holds metadata related to fields declared within a type. +type FieldDescription struct { + // KeyType holds the key FieldDescription for map fields. + KeyType *FieldDescription + // ValueType holds the value FieldDescription for map fields. + ValueType *FieldDescription + + desc protoreflect.FieldDescriptor + reflectType reflect.Type + zeroMsg proto.Message +} + +// CheckedType returns the type-definition used at type-check time. +func (fd *FieldDescription) CheckedType() *exprpb.Type { + if fd.desc.IsMap() { + return &exprpb.Type{ + TypeKind: &exprpb.Type_MapType_{ + MapType: &exprpb.Type_MapType{ + KeyType: fd.KeyType.typeDefToType(), + ValueType: fd.ValueType.typeDefToType(), + }, + }, + } + } + if fd.desc.IsList() { + return &exprpb.Type{ + TypeKind: &exprpb.Type_ListType_{ + ListType: &exprpb.Type_ListType{ + ElemType: fd.typeDefToType()}}} + } + return fd.typeDefToType() +} + +// Descriptor returns the protoreflect.FieldDescriptor for this type. +func (fd *FieldDescription) Descriptor() protoreflect.FieldDescriptor { + return fd.desc +} + +// IsSet returns whether the field is set on the target value, per the proto presence conventions +// of proto2 or proto3 accordingly. +// +// This function implements the FieldType.IsSet function contract which can be used to operate on +// more than just protobuf field accesses; however, the target here must be a protobuf.Message. +func (fd *FieldDescription) IsSet(target interface{}) bool { + switch v := target.(type) { + case proto.Message: + pbRef := v.ProtoReflect() + pbDesc := pbRef.Descriptor() + if pbDesc == fd.desc.ContainingMessage() { + // When the target protobuf shares the same message descriptor instance as the field + // descriptor, use the cached field descriptor value. + return pbRef.Has(fd.desc) + } + // Otherwise, fallback to a dynamic lookup of the field descriptor from the target + // instance as an attempt to use the cached field descriptor will result in a panic. + return pbRef.Has(pbDesc.Fields().ByName(protoreflect.Name(fd.Name()))) + default: + return false + } +} + +// GetFrom returns the accessor method associated with the field on the proto generated struct. +// +// If the field is not set, the proto default value is returned instead. +// +// This function implements the FieldType.GetFrom function contract which can be used to operate +// on more than just protobuf field accesses; however, the target here must be a protobuf.Message. +func (fd *FieldDescription) GetFrom(target interface{}) (interface{}, error) { + v, ok := target.(proto.Message) + if !ok { + return nil, fmt.Errorf("unsupported field selection target: (%T)%v", target, target) + } + pbRef := v.ProtoReflect() + pbDesc := pbRef.Descriptor() + var fieldVal interface{} + if pbDesc == fd.desc.ContainingMessage() { + // When the target protobuf shares the same message descriptor instance as the field + // descriptor, use the cached field descriptor value. + fieldVal = pbRef.Get(fd.desc).Interface() + } else { + // Otherwise, fallback to a dynamic lookup of the field descriptor from the target + // instance as an attempt to use the cached field descriptor will result in a panic. + fieldVal = pbRef.Get(pbDesc.Fields().ByName(protoreflect.Name(fd.Name()))).Interface() + } + switch fv := fieldVal.(type) { + // Fast-path return for primitive types. + case bool, []byte, float32, float64, int32, int64, string, uint32, uint64, protoreflect.List: + return fv, nil + case protoreflect.EnumNumber: + return int64(fv), nil + case protoreflect.Map: + // Return a wrapper around the protobuf-reflected Map types which carries additional + // information about the key and value definitions of the map. + return &Map{Map: fv, KeyType: fd.KeyType, ValueType: fd.ValueType}, nil + case protoreflect.Message: + // Make sure to unwrap well-known protobuf types before returning. + unwrapped, _ := fd.MaybeUnwrapDynamic(fv) + return unwrapped, nil + default: + return fv, nil + } +} + +// IsEnum returns true if the field type refers to an enum value. +func (fd *FieldDescription) IsEnum() bool { + return fd.desc.Kind() == protoreflect.EnumKind +} + +// IsMap returns true if the field is of map type. +func (fd *FieldDescription) IsMap() bool { + return fd.desc.IsMap() +} + +// IsMessage returns true if the field is of message type. +func (fd *FieldDescription) IsMessage() bool { + return fd.desc.Kind() == protoreflect.MessageKind +} + +// IsOneof returns true if the field is declared within a oneof block. +func (fd *FieldDescription) IsOneof() bool { + return fd.desc.ContainingOneof() != nil +} + +// IsList returns true if the field is a repeated value. +// +// This method will also return true for map values, so check whether the +// field is also a map. +func (fd *FieldDescription) IsList() bool { + return fd.desc.IsList() +} + +// MaybeUnwrapDynamic takes the reflected protoreflect.Message and determines whether the +// value can be unwrapped to a more primitive CEL type. +// +// This function returns the unwrapped value and 'true' on success, or the original value +// and 'false' otherwise. +func (fd *FieldDescription) MaybeUnwrapDynamic(msg protoreflect.Message) (interface{}, bool) { + return unwrapDynamic(fd, msg) +} + +// Name returns the CamelCase name of the field within the proto-based struct. +func (fd *FieldDescription) Name() string { + return string(fd.desc.Name()) +} + +// ReflectType returns the Golang reflect.Type for this field. +func (fd *FieldDescription) ReflectType() reflect.Type { + return fd.reflectType +} + +// String returns the fully qualified name of the field within its type as well as whether the +// field occurs within a oneof. +func (fd *FieldDescription) String() string { + return fmt.Sprintf("%v.%s `oneof=%t`", fd.desc.ContainingMessage().FullName(), fd.Name(), fd.IsOneof()) +} + +// Zero returns the zero value for the protobuf message represented by this field. +// +// If the field is not a proto.Message type, the zero value is nil. +func (fd *FieldDescription) Zero() proto.Message { + return fd.zeroMsg +} + +func (fd *FieldDescription) typeDefToType() *exprpb.Type { + if fd.desc.Kind() == protoreflect.MessageKind { + msgType := string(fd.desc.Message().FullName()) + if wk, found := CheckedWellKnowns[msgType]; found { + return wk + } + return checkedMessageType(msgType) + } + if fd.desc.Kind() == protoreflect.EnumKind { + return checkedInt + } + return CheckedPrimitives[fd.desc.Kind()] +} + +// Map wraps the protoreflect.Map object with a key and value FieldDescription for use in +// retrieving individual elements within CEL value data types. +type Map struct { + protoreflect.Map + KeyType *FieldDescription + ValueType *FieldDescription +} + +func checkedMessageType(name string) *exprpb.Type { + return &exprpb.Type{ + TypeKind: &exprpb.Type_MessageType{MessageType: name}} +} + +func checkedPrimitive(primitive exprpb.Type_PrimitiveType) *exprpb.Type { + return &exprpb.Type{ + TypeKind: &exprpb.Type_Primitive{Primitive: primitive}} +} + +func checkedWellKnown(wellKnown exprpb.Type_WellKnownType) *exprpb.Type { + return &exprpb.Type{ + TypeKind: &exprpb.Type_WellKnown{WellKnown: wellKnown}} +} + +func checkedWrap(t *exprpb.Type) *exprpb.Type { + return &exprpb.Type{ + TypeKind: &exprpb.Type_Wrapper{Wrapper: t.GetPrimitive()}} +} + +// unwrap unwraps the provided proto.Message value, potentially based on the description if the +// input message is a *dynamicpb.Message which obscures the typing information from Go. +// +// Returns the unwrapped value and 'true' if unwrapped, otherwise the input value and 'false'. +func unwrap(desc description, msg proto.Message) (interface{}, bool) { + switch v := msg.(type) { + case *anypb.Any: + dynMsg, err := v.UnmarshalNew() + if err != nil { + return v, false + } + return unwrapDynamic(desc, dynMsg.ProtoReflect()) + case *dynamicpb.Message: + return unwrapDynamic(desc, v) + case *dpb.Duration: + return v.AsDuration(), true + case *tpb.Timestamp: + return v.AsTime(), true + case *structpb.Value: + switch v.GetKind().(type) { + case *structpb.Value_BoolValue: + return v.GetBoolValue(), true + case *structpb.Value_ListValue: + return v.GetListValue(), true + case *structpb.Value_NullValue: + return structpb.NullValue_NULL_VALUE, true + case *structpb.Value_NumberValue: + return v.GetNumberValue(), true + case *structpb.Value_StringValue: + return v.GetStringValue(), true + case *structpb.Value_StructValue: + return v.GetStructValue(), true + default: + return structpb.NullValue_NULL_VALUE, true + } + case *wrapperspb.BoolValue: + return v.GetValue(), true + case *wrapperspb.BytesValue: + return v.GetValue(), true + case *wrapperspb.DoubleValue: + return v.GetValue(), true + case *wrapperspb.FloatValue: + return float64(v.GetValue()), true + case *wrapperspb.Int32Value: + return int64(v.GetValue()), true + case *wrapperspb.Int64Value: + return v.GetValue(), true + case *wrapperspb.StringValue: + return v.GetValue(), true + case *wrapperspb.UInt32Value: + return uint64(v.GetValue()), true + case *wrapperspb.UInt64Value: + return v.GetValue(), true + } + return msg, false +} + +// unwrapDynamic unwraps a reflected protobuf Message value. +// +// Returns the unwrapped value and 'true' if unwrapped, otherwise the input value and 'false'. +func unwrapDynamic(desc description, refMsg protoreflect.Message) (interface{}, bool) { + msg := refMsg.Interface() + if !refMsg.IsValid() { + msg = desc.Zero() + } + // In order to ensure that these wrapped types match the expectations of the CEL type system + // the dynamicpb.Message must be merged with an protobuf instance of the well-known type value. + typeName := string(refMsg.Descriptor().FullName()) + switch typeName { + case "google.protobuf.Any": + // Note, Any values require further unwrapping; however, this unwrapping may or may not + // be to a well-known type. If the unwrapped value is a well-known type it will be further + // unwrapped before being returned to the caller. Otherwise, the dynamic protobuf object + // represented by the Any will be returned. + unwrappedAny := &anypb.Any{} + proto.Merge(unwrappedAny, msg) + dynMsg, err := unwrappedAny.UnmarshalNew() + if err != nil { + // Allow the error to move further up the stack as it should result in an type + // conversion error if the caller does not recover it somehow. + return unwrappedAny, true + } + // Attempt to unwrap the dynamic type, otherwise return the dynamic message. + if unwrapped, nested := unwrapDynamic(desc, dynMsg.ProtoReflect()); nested { + return unwrapped, true + } + return dynMsg, true + case "google.protobuf.BoolValue", + "google.protobuf.BytesValue", + "google.protobuf.DoubleValue", + "google.protobuf.FloatValue", + "google.protobuf.Int32Value", + "google.protobuf.Int64Value", + "google.protobuf.StringValue", + "google.protobuf.UInt32Value", + "google.protobuf.UInt64Value": + // The msg value is ignored when dealing with wrapper types as they have a null or value + // behavior, rather than the standard zero value behavior of other proto message types. + if !refMsg.IsValid() { + return structpb.NullValue_NULL_VALUE, true + } + valueField := refMsg.Descriptor().Fields().ByName("value") + return refMsg.Get(valueField).Interface(), true + case "google.protobuf.Duration": + unwrapped := &dpb.Duration{} + proto.Merge(unwrapped, msg) + return unwrapped.AsDuration(), true + case "google.protobuf.ListValue": + unwrapped := &structpb.ListValue{} + proto.Merge(unwrapped, msg) + return unwrapped, true + case "google.protobuf.NullValue": + return structpb.NullValue_NULL_VALUE, true + case "google.protobuf.Struct": + unwrapped := &structpb.Struct{} + proto.Merge(unwrapped, msg) + return unwrapped, true + case "google.protobuf.Timestamp": + unwrapped := &tpb.Timestamp{} + proto.Merge(unwrapped, msg) + return unwrapped.AsTime(), true + case "google.protobuf.Value": + unwrapped := &structpb.Value{} + proto.Merge(unwrapped, msg) + return unwrap(desc, unwrapped) + } + return msg, false +} + +// reflectTypeOf intercepts the reflect.Type call to ensure that dynamicpb.Message types preserve +// well-known protobuf reflected types expected by the CEL type system. +func reflectTypeOf(val interface{}) reflect.Type { + switch v := val.(type) { + case proto.Message: + return reflect.TypeOf(zeroValueOf(v)) + default: + return reflect.TypeOf(v) + } +} + +// zeroValueOf will return the strongest possible proto.Message representing the default protobuf +// message value of the input msg type. +func zeroValueOf(msg proto.Message) proto.Message { + if msg == nil { + return nil + } + typeName := string(msg.ProtoReflect().Descriptor().FullName()) + zeroVal, found := zeroValueMap[typeName] + if found { + return zeroVal + } + return msg +} + +var ( + zeroValueMap = map[string]proto.Message{ + "google.protobuf.Any": &anypb.Any{}, + "google.protobuf.Duration": &dpb.Duration{}, + "google.protobuf.ListValue": &structpb.ListValue{}, + "google.protobuf.Struct": &structpb.Struct{}, + "google.protobuf.Timestamp": &tpb.Timestamp{}, + "google.protobuf.Value": &structpb.Value{}, + "google.protobuf.BoolValue": wrapperspb.Bool(false), + "google.protobuf.BytesValue": wrapperspb.Bytes([]byte{}), + "google.protobuf.DoubleValue": wrapperspb.Double(0.0), + "google.protobuf.FloatValue": wrapperspb.Float(0.0), + "google.protobuf.Int32Value": wrapperspb.Int32(0), + "google.protobuf.Int64Value": wrapperspb.Int64(0), + "google.protobuf.StringValue": wrapperspb.String(""), + "google.protobuf.UInt32Value": wrapperspb.UInt32(0), + "google.protobuf.UInt64Value": wrapperspb.UInt64(0), + } +) diff --git a/vendor/github.com/google/cel-go/common/types/provider.go b/vendor/github.com/google/cel-go/common/types/provider.go new file mode 100644 index 0000000000..3ab30b763f --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/provider.go @@ -0,0 +1,533 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "fmt" + "reflect" + "time" + + "github.com/google/cel-go/common/types/pb" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" + anypb "google.golang.org/protobuf/types/known/anypb" + dpb "google.golang.org/protobuf/types/known/durationpb" + structpb "google.golang.org/protobuf/types/known/structpb" + tpb "google.golang.org/protobuf/types/known/timestamppb" +) + +type protoTypeRegistry struct { + revTypeMap map[string]ref.Type + pbdb *pb.Db +} + +// NewRegistry accepts a list of proto message instances and returns a type +// provider which can create new instances of the provided message or any +// message that proto depends upon in its FileDescriptor. +func NewRegistry(types ...proto.Message) (ref.TypeRegistry, error) { + p := &protoTypeRegistry{ + revTypeMap: make(map[string]ref.Type), + pbdb: pb.NewDb(), + } + err := p.RegisterType( + BoolType, + BytesType, + DoubleType, + DurationType, + IntType, + ListType, + MapType, + NullType, + StringType, + TimestampType, + TypeType, + UintType) + if err != nil { + return nil, err + } + // This block ensures that the well-known protobuf types are registered by default. + for _, fd := range p.pbdb.FileDescriptions() { + err = p.registerAllTypes(fd) + if err != nil { + return nil, err + } + } + for _, msgType := range types { + err = p.RegisterMessage(msgType) + if err != nil { + return nil, err + } + } + return p, nil +} + +// NewEmptyRegistry returns a registry which is completely unconfigured. +func NewEmptyRegistry() ref.TypeRegistry { + return &protoTypeRegistry{ + revTypeMap: make(map[string]ref.Type), + pbdb: pb.NewDb(), + } +} + +// Copy implements the ref.TypeRegistry interface method which copies the current state of the +// registry into its own memory space. +func (p *protoTypeRegistry) Copy() ref.TypeRegistry { + copy := &protoTypeRegistry{ + revTypeMap: make(map[string]ref.Type), + pbdb: p.pbdb.Copy(), + } + for k, v := range p.revTypeMap { + copy.revTypeMap[k] = v + } + return copy +} + +func (p *protoTypeRegistry) EnumValue(enumName string) ref.Val { + enumVal, found := p.pbdb.DescribeEnum(enumName) + if !found { + return NewErr("unknown enum name '%s'", enumName) + } + return Int(enumVal.Value()) +} + +func (p *protoTypeRegistry) FindFieldType(messageType string, + fieldName string) (*ref.FieldType, bool) { + msgType, found := p.pbdb.DescribeType(messageType) + if !found { + return nil, false + } + field, found := msgType.FieldByName(fieldName) + if !found { + return nil, false + } + return &ref.FieldType{ + Type: field.CheckedType(), + IsSet: field.IsSet, + GetFrom: field.GetFrom}, + true +} + +func (p *protoTypeRegistry) FindIdent(identName string) (ref.Val, bool) { + if t, found := p.revTypeMap[identName]; found { + return t.(ref.Val), true + } + if enumVal, found := p.pbdb.DescribeEnum(identName); found { + return Int(enumVal.Value()), true + } + return nil, false +} + +func (p *protoTypeRegistry) FindType(typeName string) (*exprpb.Type, bool) { + if _, found := p.pbdb.DescribeType(typeName); !found { + return nil, false + } + if typeName != "" && typeName[0] == '.' { + typeName = typeName[1:] + } + return &exprpb.Type{ + TypeKind: &exprpb.Type_Type{ + Type: &exprpb.Type{ + TypeKind: &exprpb.Type_MessageType{ + MessageType: typeName}}}}, true +} + +func (p *protoTypeRegistry) NewValue(typeName string, fields map[string]ref.Val) ref.Val { + td, found := p.pbdb.DescribeType(typeName) + if !found { + return NewErr("unknown type '%s'", typeName) + } + msg := td.New() + fieldMap := td.FieldMap() + for name, value := range fields { + field, found := fieldMap[name] + if !found { + return NewErr("no such field: %s", name) + } + err := msgSetField(msg, field, value) + if err != nil { + return &Err{err} + } + } + return p.NativeToValue(msg.Interface()) +} + +func (p *protoTypeRegistry) RegisterDescriptor(fileDesc protoreflect.FileDescriptor) error { + fd, err := p.pbdb.RegisterDescriptor(fileDesc) + if err != nil { + return err + } + return p.registerAllTypes(fd) +} + +func (p *protoTypeRegistry) RegisterMessage(message proto.Message) error { + fd, err := p.pbdb.RegisterMessage(message) + if err != nil { + return err + } + return p.registerAllTypes(fd) +} + +func (p *protoTypeRegistry) RegisterType(types ...ref.Type) error { + for _, t := range types { + p.revTypeMap[t.TypeName()] = t + } + // TODO: generate an error when the type name is registered more than once. + return nil +} + +// NativeToValue converts various "native" types to ref.Val with this specific implementation +// providing support for custom proto-based types. +// +// This method should be the inverse of ref.Val.ConvertToNative. +func (p *protoTypeRegistry) NativeToValue(value interface{}) ref.Val { + if val, found := nativeToValue(p, value); found { + return val + } + switch v := value.(type) { + case proto.Message: + typeName := string(v.ProtoReflect().Descriptor().FullName()) + td, found := p.pbdb.DescribeType(typeName) + if !found { + return NewErr("unknown type: '%s'", typeName) + } + unwrapped, isUnwrapped := td.MaybeUnwrap(v) + if isUnwrapped { + return p.NativeToValue(unwrapped) + } + typeVal, found := p.FindIdent(typeName) + if !found { + return NewErr("unknown type: '%s'", typeName) + } + return NewObject(p, td, typeVal.(*TypeValue), v) + case *pb.Map: + return NewProtoMap(p, v) + case protoreflect.List: + return NewProtoList(p, v) + case protoreflect.Message: + return p.NativeToValue(v.Interface()) + case protoreflect.Value: + return p.NativeToValue(v.Interface()) + } + return UnsupportedRefValConversionErr(value) +} + +func (p *protoTypeRegistry) registerAllTypes(fd *pb.FileDescription) error { + for _, typeName := range fd.GetTypeNames() { + err := p.RegisterType(NewObjectTypeValue(typeName)) + if err != nil { + return err + } + } + return nil +} + +// defaultTypeAdapter converts go native types to CEL values. +type defaultTypeAdapter struct{} + +var ( + // DefaultTypeAdapter adapts canonical CEL types from their equivalent Go values. + DefaultTypeAdapter = &defaultTypeAdapter{} +) + +// NativeToValue implements the ref.TypeAdapter interface. +func (a *defaultTypeAdapter) NativeToValue(value interface{}) ref.Val { + if val, found := nativeToValue(a, value); found { + return val + } + return UnsupportedRefValConversionErr(value) +} + +// nativeToValue returns the converted (ref.Val, true) of a conversion is found, +// otherwise (nil, false) +func nativeToValue(a ref.TypeAdapter, value interface{}) (ref.Val, bool) { + switch v := value.(type) { + case nil: + return NullValue, true + case *Bool: + if v != nil { + return *v, true + } + case *Bytes: + if v != nil { + return *v, true + } + case *Double: + if v != nil { + return *v, true + } + case *Int: + if v != nil { + return *v, true + } + case *String: + if v != nil { + return *v, true + } + case *Uint: + if v != nil { + return *v, true + } + case bool: + return Bool(v), true + case int: + return Int(v), true + case int32: + return Int(v), true + case int64: + return Int(v), true + case uint: + return Uint(v), true + case uint32: + return Uint(v), true + case uint64: + return Uint(v), true + case float32: + return Double(v), true + case float64: + return Double(v), true + case string: + return String(v), true + case *dpb.Duration: + return Duration{Duration: v.AsDuration()}, true + case time.Duration: + return Duration{Duration: v}, true + case *tpb.Timestamp: + return Timestamp{Time: v.AsTime()}, true + case time.Time: + return Timestamp{Time: v}, true + case *bool: + if v != nil { + return Bool(*v), true + } + case *float32: + if v != nil { + return Double(*v), true + } + case *float64: + if v != nil { + return Double(*v), true + } + case *int: + if v != nil { + return Int(*v), true + } + case *int32: + if v != nil { + return Int(*v), true + } + case *int64: + if v != nil { + return Int(*v), true + } + case *string: + if v != nil { + return String(*v), true + } + case *uint: + if v != nil { + return Uint(*v), true + } + case *uint32: + if v != nil { + return Uint(*v), true + } + case *uint64: + if v != nil { + return Uint(*v), true + } + case []byte: + return Bytes(v), true + // specializations for common lists types. + case []string: + return NewStringList(a, v), true + case []ref.Val: + return NewRefValList(a, v), true + // specializations for common map types. + case map[string]string: + return NewStringStringMap(a, v), true + case map[string]interface{}: + return NewStringInterfaceMap(a, v), true + case map[ref.Val]ref.Val: + return NewRefValMap(a, v), true + // additional specializations may be added upon request / need. + case *anypb.Any: + if v == nil { + return UnsupportedRefValConversionErr(v), true + } + unpackedAny, err := v.UnmarshalNew() + if err != nil { + return NewErr("anypb.UnmarshalNew() failed for type %q: %v", v.GetTypeUrl(), err), true + } + return a.NativeToValue(unpackedAny), true + case *structpb.NullValue, structpb.NullValue: + return NullValue, true + case *structpb.ListValue: + return NewJSONList(a, v), true + case *structpb.Struct: + return NewJSONStruct(a, v), true + case ref.Val: + return v, true + case protoreflect.EnumNumber: + return Int(v), true + case proto.Message: + if v == nil { + return UnsupportedRefValConversionErr(v), true + } + typeName := string(v.ProtoReflect().Descriptor().FullName()) + td, found := pb.DefaultDb.DescribeType(typeName) + if !found { + return nil, false + } + val, unwrapped := td.MaybeUnwrap(v) + if !unwrapped { + return nil, false + } + return a.NativeToValue(val), true + // Note: dynamicpb.Message implements the proto.Message _and_ protoreflect.Message interfaces + // which means that this case must appear after handling a proto.Message type. + case protoreflect.Message: + return a.NativeToValue(v.Interface()), true + default: + refValue := reflect.ValueOf(v) + if refValue.Kind() == reflect.Ptr { + if refValue.IsNil() { + return UnsupportedRefValConversionErr(v), true + } + refValue = refValue.Elem() + } + refKind := refValue.Kind() + switch refKind { + case reflect.Array, reflect.Slice: + return NewDynamicList(a, v), true + case reflect.Map: + return NewDynamicMap(a, v), true + // type aliases of primitive types cannot be asserted as that type, but rather need + // to be downcast to int32 before being converted to a CEL representation. + case reflect.Int32: + intType := reflect.TypeOf(int32(0)) + return Int(refValue.Convert(intType).Interface().(int32)), true + case reflect.Int64: + intType := reflect.TypeOf(int64(0)) + return Int(refValue.Convert(intType).Interface().(int64)), true + case reflect.Uint32: + uintType := reflect.TypeOf(uint32(0)) + return Uint(refValue.Convert(uintType).Interface().(uint32)), true + case reflect.Uint64: + uintType := reflect.TypeOf(uint64(0)) + return Uint(refValue.Convert(uintType).Interface().(uint64)), true + case reflect.Float32: + doubleType := reflect.TypeOf(float32(0)) + return Double(refValue.Convert(doubleType).Interface().(float32)), true + case reflect.Float64: + doubleType := reflect.TypeOf(float64(0)) + return Double(refValue.Convert(doubleType).Interface().(float64)), true + } + } + return nil, false +} + +func msgSetField(target protoreflect.Message, field *pb.FieldDescription, val ref.Val) error { + if field.IsList() { + lv := target.NewField(field.Descriptor()) + list, ok := val.(traits.Lister) + if !ok { + return unsupportedTypeConversionError(field, val) + } + err := msgSetListField(lv.List(), field, list) + if err != nil { + return err + } + target.Set(field.Descriptor(), lv) + return nil + } + if field.IsMap() { + mv := target.NewField(field.Descriptor()) + mp, ok := val.(traits.Mapper) + if !ok { + return unsupportedTypeConversionError(field, val) + } + err := msgSetMapField(mv.Map(), field, mp) + if err != nil { + return err + } + target.Set(field.Descriptor(), mv) + return nil + } + v, err := val.ConvertToNative(field.ReflectType()) + if err != nil { + return fieldTypeConversionError(field, err) + } + switch v.(type) { + case proto.Message: + v = v.(proto.Message).ProtoReflect() + } + target.Set(field.Descriptor(), protoreflect.ValueOf(v)) + return nil +} + +func msgSetListField(target protoreflect.List, listField *pb.FieldDescription, listVal traits.Lister) error { + elemReflectType := listField.ReflectType().Elem() + for i := Int(0); i < listVal.Size().(Int); i++ { + elem := listVal.Get(i) + elemVal, err := elem.ConvertToNative(elemReflectType) + if err != nil { + return fieldTypeConversionError(listField, err) + } + switch ev := elemVal.(type) { + case proto.Message: + elemVal = ev.ProtoReflect() + } + target.Append(protoreflect.ValueOf(elemVal)) + } + return nil +} + +func msgSetMapField(target protoreflect.Map, mapField *pb.FieldDescription, mapVal traits.Mapper) error { + targetKeyType := mapField.KeyType.ReflectType() + targetValType := mapField.ValueType.ReflectType() + it := mapVal.Iterator() + for it.HasNext() == True { + key := it.Next() + val := mapVal.Get(key) + k, err := key.ConvertToNative(targetKeyType) + if err != nil { + return fieldTypeConversionError(mapField, err) + } + v, err := val.ConvertToNative(targetValType) + if err != nil { + return fieldTypeConversionError(mapField, err) + } + switch v.(type) { + case proto.Message: + v = v.(proto.Message).ProtoReflect() + } + target.Set(protoreflect.ValueOf(k).MapKey(), protoreflect.ValueOf(v)) + } + return nil +} + +func unsupportedTypeConversionError(field *pb.FieldDescription, val ref.Val) error { + msgName := field.Descriptor().ContainingMessage().FullName() + return fmt.Errorf("unsupported field type for %v.%v: %v", msgName, field.Name(), val.Type()) +} + +func fieldTypeConversionError(field *pb.FieldDescription, err error) error { + msgName := field.Descriptor().ContainingMessage().FullName() + return fmt.Errorf("field type conversion error for %v.%v value type: %v", msgName, field.Name(), err) +} diff --git a/vendor/github.com/google/cel-go/common/types/ref/provider.go b/vendor/github.com/google/cel-go/common/types/ref/provider.go new file mode 100644 index 0000000000..91a711fa70 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/ref/provider.go @@ -0,0 +1,103 @@ +// Copyright 2018 Google LLC +// +// 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. + +package ref + +import ( + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +// TypeProvider specifies functions for creating new object instances and for +// resolving enum values by name. +type TypeProvider interface { + // EnumValue returns the numeric value of the given enum value name. + EnumValue(enumName string) Val + + // FindIdent takes a qualified identifier name and returns a Value if one + // exists. + FindIdent(identName string) (Val, bool) + + // FindType looks up the Type given a qualified typeName. Returns false + // if not found. + // + // Used during type-checking only. + FindType(typeName string) (*exprpb.Type, bool) + + // FieldFieldType returns the field type for a checked type value. Returns + // false if the field could not be found. + // + // Used during type-checking only. + FindFieldType(messageType string, fieldName string) (*FieldType, bool) + + // NewValue creates a new type value from a qualified name and map of field + // name to value. + // + // Note, for each value, the Val.ConvertToNative function will be invoked + // to convert the Val to the field's native type. If an error occurs during + // conversion, the NewValue will be a types.Err. + NewValue(typeName string, fields map[string]Val) Val +} + +// TypeAdapter converts native Go values of varying type and complexity to equivalent CEL values. +type TypeAdapter interface { + // NativeToValue converts the input `value` to a CEL `ref.Val`. + NativeToValue(value interface{}) Val +} + +// TypeRegistry allows third-parties to add custom types to CEL. Not all `TypeProvider` +// implementations support type-customization, so these features are optional. However, a +// `TypeRegistry` should be a `TypeProvider` and a `TypeAdapter` to ensure that types +// which are registered can be converted to CEL representations. +type TypeRegistry interface { + TypeAdapter + TypeProvider + + // RegisterDescriptor registers the contents of a protocol buffer `FileDescriptor`. + RegisterDescriptor(fileDesc protoreflect.FileDescriptor) error + + // RegisterMessage registers a protocol buffer message and its dependencies. + RegisterMessage(message proto.Message) error + + // RegisterType registers a type value with the provider which ensures the + // provider is aware of how to map the type to an identifier. + // + // If a type is provided more than once with an alternative definition, the + // call will result in an error. + RegisterType(types ...Type) error + + // Copy the TypeRegistry and return a new registry whose mutable state is isolated. + Copy() TypeRegistry +} + +// FieldType represents a field's type value and whether that field supports +// presence detection. +type FieldType struct { + // Type of the field. + Type *exprpb.Type + + // IsSet indicates whether the field is set on an input object. + IsSet FieldTester + + // GetFrom retrieves the field value on the input object, if set. + GetFrom FieldGetter +} + +// FieldTester is used to test field presence on an input object. +type FieldTester func(target interface{}) bool + +// FieldGetter is used to get the field value from an input object, if set. +type FieldGetter func(target interface{}) (interface{}, error) diff --git a/vendor/github.com/google/cel-go/common/types/ref/reference.go b/vendor/github.com/google/cel-go/common/types/ref/reference.go new file mode 100644 index 0000000000..e12169988d --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/ref/reference.go @@ -0,0 +1,59 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Package ref contains the reference interfaces used throughout the types +// components. +package ref + +import ( + "reflect" +) + +// Type interface indicate the name of a given type. +type Type interface { + // HasTrait returns whether the type has a given trait associated with it. + // + // See common/types/traits/traits.go for a list of supported traits. + HasTrait(trait int) bool + + // TypeName returns the qualified type name of the type. + // + // The type name is also used as the type's identifier name at type-check + // and interpretation time. + TypeName() string +} + +// Val interface defines the functions supported by all expression values. +// Val implementations may specialize the behavior of the value through the +// addition of traits. +type Val interface { + // ConvertToNative converts the Value to a native Go struct according to the + // reflected type description, or error if the conversion is not feasible. + ConvertToNative(typeDesc reflect.Type) (interface{}, error) + + // ConvertToType supports type conversions between value types supported by + // the expression language. + ConvertToType(typeValue Type) Val + + // Equal returns true if the `other` value has the same type and content as + // the implementing struct. + Equal(other Val) Val + + // Type returns the TypeValue of the value. + Type() Type + + // Value returns the raw value of the instance which may not be directly + // compatible with the expression language types. + Value() interface{} +} diff --git a/vendor/github.com/google/cel-go/common/types/string.go b/vendor/github.com/google/cel-go/common/types/string.go new file mode 100644 index 0000000000..b6d665683c --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/string.go @@ -0,0 +1,218 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "fmt" + "reflect" + "regexp" + "strconv" + "strings" + "time" + + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + + anypb "google.golang.org/protobuf/types/known/anypb" + structpb "google.golang.org/protobuf/types/known/structpb" + wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" +) + +// String type implementation which supports addition, comparison, matching, +// and size functions. +type String string + +var ( + // StringType singleton. + StringType = NewTypeValue("string", + traits.AdderType, + traits.ComparerType, + traits.MatcherType, + traits.ReceiverType, + traits.SizerType) + + stringOneArgOverloads = map[string]func(String, ref.Val) ref.Val{ + overloads.Contains: stringContains, + overloads.EndsWith: stringEndsWith, + overloads.StartsWith: stringStartsWith, + } + + stringWrapperType = reflect.TypeOf(&wrapperspb.StringValue{}) +) + +// Add implements traits.Adder.Add. +func (s String) Add(other ref.Val) ref.Val { + otherString, ok := other.(String) + if !ok { + return MaybeNoSuchOverloadErr(other) + } + return s + otherString +} + +// Compare implements traits.Comparer.Compare. +func (s String) Compare(other ref.Val) ref.Val { + otherString, ok := other.(String) + if !ok { + return MaybeNoSuchOverloadErr(other) + } + return Int(strings.Compare(s.Value().(string), otherString.Value().(string))) +} + +// ConvertToNative implements ref.Val.ConvertToNative. +func (s String) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + switch typeDesc.Kind() { + case reflect.String: + if reflect.TypeOf(s).AssignableTo(typeDesc) { + return s, nil + } + return s.Value(), nil + case reflect.Ptr: + switch typeDesc { + case anyValueType: + // Primitives must be wrapped before being set on an Any field. + return anypb.New(wrapperspb.String(string(s))) + case jsonValueType: + // Convert to a protobuf representation of a JSON String. + return structpb.NewStringValue(string(s)), nil + case stringWrapperType: + // Convert to a wrapperspb.StringValue. + return wrapperspb.String(string(s)), nil + } + if typeDesc.Elem().Kind() == reflect.String { + p := s.Value().(string) + return &p, nil + } + case reflect.Interface: + sv := s.Value() + if reflect.TypeOf(sv).Implements(typeDesc) { + return sv, nil + } + if reflect.TypeOf(s).Implements(typeDesc) { + return s, nil + } + } + return nil, fmt.Errorf( + "unsupported native conversion from string to '%v'", typeDesc) +} + +// ConvertToType implements ref.Val.ConvertToType. +func (s String) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case IntType: + if n, err := strconv.ParseInt(s.Value().(string), 10, 64); err == nil { + return Int(n) + } + case UintType: + if n, err := strconv.ParseUint(s.Value().(string), 10, 64); err == nil { + return Uint(n) + } + case DoubleType: + if n, err := strconv.ParseFloat(s.Value().(string), 64); err == nil { + return Double(n) + } + case BoolType: + if b, err := strconv.ParseBool(s.Value().(string)); err == nil { + return Bool(b) + } + case BytesType: + return Bytes(s) + case DurationType: + if d, err := time.ParseDuration(s.Value().(string)); err == nil { + return durationOf(d) + } + case TimestampType: + if t, err := time.Parse(time.RFC3339, s.Value().(string)); err == nil { + if t.Unix() < minUnixTime || t.Unix() > maxUnixTime { + return celErrTimestampOverflow + } + return timestampOf(t) + } + case StringType: + return s + case TypeType: + return StringType + } + return NewErr("type conversion error from '%s' to '%s'", StringType, typeVal) +} + +// Equal implements ref.Val.Equal. +func (s String) Equal(other ref.Val) ref.Val { + otherString, ok := other.(String) + return Bool(ok && s == otherString) +} + +// Match implements traits.Matcher.Match. +func (s String) Match(pattern ref.Val) ref.Val { + pat, ok := pattern.(String) + if !ok { + return MaybeNoSuchOverloadErr(pattern) + } + matched, err := regexp.MatchString(pat.Value().(string), s.Value().(string)) + if err != nil { + return &Err{err} + } + return Bool(matched) +} + +// Receive implements traits.Receiver.Receive. +func (s String) Receive(function string, overload string, args []ref.Val) ref.Val { + switch len(args) { + case 1: + if f, found := stringOneArgOverloads[function]; found { + return f(s, args[0]) + } + } + return NoSuchOverloadErr() +} + +// Size implements traits.Sizer.Size. +func (s String) Size() ref.Val { + return Int(len([]rune(s.Value().(string)))) +} + +// Type implements ref.Val.Type. +func (s String) Type() ref.Type { + return StringType +} + +// Value implements ref.Val.Value. +func (s String) Value() interface{} { + return string(s) +} + +func stringContains(s String, sub ref.Val) ref.Val { + subStr, ok := sub.(String) + if !ok { + return MaybeNoSuchOverloadErr(sub) + } + return Bool(strings.Contains(string(s), string(subStr))) +} + +func stringEndsWith(s String, suf ref.Val) ref.Val { + sufStr, ok := suf.(String) + if !ok { + return MaybeNoSuchOverloadErr(suf) + } + return Bool(strings.HasSuffix(string(s), string(sufStr))) +} + +func stringStartsWith(s String, pre ref.Val) ref.Val { + preStr, ok := pre.(String) + if !ok { + return MaybeNoSuchOverloadErr(pre) + } + return Bool(strings.HasPrefix(string(s), string(preStr))) +} diff --git a/vendor/github.com/google/cel-go/common/types/timestamp.go b/vendor/github.com/google/cel-go/common/types/timestamp.go new file mode 100644 index 0000000000..cb3237447a --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/timestamp.go @@ -0,0 +1,316 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "fmt" + "reflect" + "strconv" + "strings" + "time" + + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + + anypb "google.golang.org/protobuf/types/known/anypb" + structpb "google.golang.org/protobuf/types/known/structpb" + tpb "google.golang.org/protobuf/types/known/timestamppb" +) + +// Timestamp type implementation which supports add, compare, and subtract +// operations. Timestamps are also capable of participating in dynamic +// function dispatch to instance methods. +type Timestamp struct { + time.Time +} + +func timestampOf(t time.Time) Timestamp { + // Note that this function does not validate that time.Time is in our supported range. + return Timestamp{Time: t} +} + +const ( + // The number of seconds between year 1 and year 1970. This is borrowed from + // https://golang.org/src/time/time.go. + unixToInternal int64 = (1969*365 + 1969/4 - 1969/100 + 1969/400) * (60 * 60 * 24) + + // Number of seconds between `0001-01-01T00:00:00Z` and the Unix epoch. + minUnixTime int64 = -62135596800 + // Number of seconds between `9999-12-31T23:59:59.999999999Z` and the Unix epoch. + maxUnixTime int64 = 253402300799 +) + +var ( + // TimestampType singleton. + TimestampType = NewTypeValue("google.protobuf.Timestamp", + traits.AdderType, + traits.ComparerType, + traits.ReceiverType, + traits.SubtractorType) +) + +// Add implements traits.Adder.Add. +func (t Timestamp) Add(other ref.Val) ref.Val { + switch other.Type() { + case DurationType: + return other.(Duration).Add(t) + } + return MaybeNoSuchOverloadErr(other) +} + +// Compare implements traits.Comparer.Compare. +func (t Timestamp) Compare(other ref.Val) ref.Val { + if TimestampType != other.Type() { + return MaybeNoSuchOverloadErr(other) + } + ts1 := t.Time + ts2 := other.(Timestamp).Time + switch { + case ts1.Before(ts2): + return IntNegOne + case ts1.After(ts2): + return IntOne + default: + return IntZero + } +} + +// ConvertToNative implements ref.Val.ConvertToNative. +func (t Timestamp) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + // If the timestamp is already assignable to the desired type return it. + if reflect.TypeOf(t.Time).AssignableTo(typeDesc) { + return t.Time, nil + } + if reflect.TypeOf(t).AssignableTo(typeDesc) { + return t, nil + } + switch typeDesc { + case anyValueType: + // Pack the underlying time as a tpb.Timestamp into an Any value. + return anypb.New(tpb.New(t.Time)) + case jsonValueType: + // CEL follows the proto3 to JSON conversion which formats as an RFC 3339 encoded JSON + // string. + v := t.ConvertToType(StringType) + if IsError(v) { + return nil, v.(*Err) + } + return structpb.NewStringValue(string(v.(String))), nil + case timestampValueType: + // Unwrap the underlying tpb.Timestamp. + return tpb.New(t.Time), nil + } + return nil, fmt.Errorf("type conversion error from 'Timestamp' to '%v'", typeDesc) +} + +// ConvertToType implements ref.Val.ConvertToType. +func (t Timestamp) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case StringType: + return String(t.Format(time.RFC3339Nano)) + case IntType: + // Return the Unix time in seconds since 1970 + return Int(t.Unix()) + case TimestampType: + return t + case TypeType: + return TimestampType + } + return NewErr("type conversion error from '%s' to '%s'", TimestampType, typeVal) +} + +// Equal implements ref.Val.Equal. +func (t Timestamp) Equal(other ref.Val) ref.Val { + otherTime, ok := other.(Timestamp) + return Bool(ok && t.Time.Equal(otherTime.Time)) +} + +// Receive implements traits.Receiver.Receive. +func (t Timestamp) Receive(function string, overload string, args []ref.Val) ref.Val { + switch len(args) { + case 0: + if f, found := timestampZeroArgOverloads[function]; found { + return f(t.Time) + } + case 1: + if f, found := timestampOneArgOverloads[function]; found { + return f(t.Time, args[0]) + } + } + return NoSuchOverloadErr() +} + +// Subtract implements traits.Subtractor.Subtract. +func (t Timestamp) Subtract(subtrahend ref.Val) ref.Val { + switch subtrahend.Type() { + case DurationType: + dur := subtrahend.(Duration) + val, err := subtractTimeDurationChecked(t.Time, dur.Duration) + if err != nil { + return wrapErr(err) + } + return timestampOf(val) + case TimestampType: + t2 := subtrahend.(Timestamp).Time + val, err := subtractTimeChecked(t.Time, t2) + if err != nil { + return wrapErr(err) + } + return durationOf(val) + } + return MaybeNoSuchOverloadErr(subtrahend) +} + +// Type implements ref.Val.Type. +func (t Timestamp) Type() ref.Type { + return TimestampType +} + +// Value implements ref.Val.Value. +func (t Timestamp) Value() interface{} { + return t.Time +} + +var ( + timestampValueType = reflect.TypeOf(&tpb.Timestamp{}) + + timestampZeroArgOverloads = map[string]func(time.Time) ref.Val{ + overloads.TimeGetFullYear: timestampGetFullYear, + overloads.TimeGetMonth: timestampGetMonth, + overloads.TimeGetDayOfYear: timestampGetDayOfYear, + overloads.TimeGetDate: timestampGetDayOfMonthOneBased, + overloads.TimeGetDayOfMonth: timestampGetDayOfMonthZeroBased, + overloads.TimeGetDayOfWeek: timestampGetDayOfWeek, + overloads.TimeGetHours: timestampGetHours, + overloads.TimeGetMinutes: timestampGetMinutes, + overloads.TimeGetSeconds: timestampGetSeconds, + overloads.TimeGetMilliseconds: timestampGetMilliseconds} + + timestampOneArgOverloads = map[string]func(time.Time, ref.Val) ref.Val{ + overloads.TimeGetFullYear: timestampGetFullYearWithTz, + overloads.TimeGetMonth: timestampGetMonthWithTz, + overloads.TimeGetDayOfYear: timestampGetDayOfYearWithTz, + overloads.TimeGetDate: timestampGetDayOfMonthOneBasedWithTz, + overloads.TimeGetDayOfMonth: timestampGetDayOfMonthZeroBasedWithTz, + overloads.TimeGetDayOfWeek: timestampGetDayOfWeekWithTz, + overloads.TimeGetHours: timestampGetHoursWithTz, + overloads.TimeGetMinutes: timestampGetMinutesWithTz, + overloads.TimeGetSeconds: timestampGetSecondsWithTz, + overloads.TimeGetMilliseconds: timestampGetMillisecondsWithTz} +) + +type timestampVisitor func(time.Time) ref.Val + +func timestampGetFullYear(t time.Time) ref.Val { + return Int(t.Year()) +} +func timestampGetMonth(t time.Time) ref.Val { + // CEL spec indicates that the month should be 0-based, but the Time value + // for Month() is 1-based. + return Int(t.Month() - 1) +} +func timestampGetDayOfYear(t time.Time) ref.Val { + return Int(t.YearDay() - 1) +} +func timestampGetDayOfMonthZeroBased(t time.Time) ref.Val { + return Int(t.Day() - 1) +} +func timestampGetDayOfMonthOneBased(t time.Time) ref.Val { + return Int(t.Day()) +} +func timestampGetDayOfWeek(t time.Time) ref.Val { + return Int(t.Weekday()) +} +func timestampGetHours(t time.Time) ref.Val { + return Int(t.Hour()) +} +func timestampGetMinutes(t time.Time) ref.Val { + return Int(t.Minute()) +} +func timestampGetSeconds(t time.Time) ref.Val { + return Int(t.Second()) +} +func timestampGetMilliseconds(t time.Time) ref.Val { + return Int(t.Nanosecond() / 1000000) +} + +func timestampGetFullYearWithTz(t time.Time, tz ref.Val) ref.Val { + return timeZone(tz, timestampGetFullYear)(t) +} +func timestampGetMonthWithTz(t time.Time, tz ref.Val) ref.Val { + return timeZone(tz, timestampGetMonth)(t) +} +func timestampGetDayOfYearWithTz(t time.Time, tz ref.Val) ref.Val { + return timeZone(tz, timestampGetDayOfYear)(t) +} +func timestampGetDayOfMonthZeroBasedWithTz(t time.Time, tz ref.Val) ref.Val { + return timeZone(tz, timestampGetDayOfMonthZeroBased)(t) +} +func timestampGetDayOfMonthOneBasedWithTz(t time.Time, tz ref.Val) ref.Val { + return timeZone(tz, timestampGetDayOfMonthOneBased)(t) +} +func timestampGetDayOfWeekWithTz(t time.Time, tz ref.Val) ref.Val { + return timeZone(tz, timestampGetDayOfWeek)(t) +} +func timestampGetHoursWithTz(t time.Time, tz ref.Val) ref.Val { + return timeZone(tz, timestampGetHours)(t) +} +func timestampGetMinutesWithTz(t time.Time, tz ref.Val) ref.Val { + return timeZone(tz, timestampGetMinutes)(t) +} +func timestampGetSecondsWithTz(t time.Time, tz ref.Val) ref.Val { + return timeZone(tz, timestampGetSeconds)(t) +} +func timestampGetMillisecondsWithTz(t time.Time, tz ref.Val) ref.Val { + return timeZone(tz, timestampGetMilliseconds)(t) +} + +func timeZone(tz ref.Val, visitor timestampVisitor) timestampVisitor { + return func(t time.Time) ref.Val { + if StringType != tz.Type() { + return MaybeNoSuchOverloadErr(tz) + } + val := string(tz.(String)) + ind := strings.Index(val, ":") + if ind == -1 { + loc, err := time.LoadLocation(val) + if err != nil { + return wrapErr(err) + } + return visitor(t.In(loc)) + } + + // If the input is not the name of a timezone (for example, 'US/Central'), it should be a numerical offset from UTC + // in the format ^(+|-)(0[0-9]|1[0-4]):[0-5][0-9]$. The numerical input is parsed in terms of hours and minutes. + hr, err := strconv.Atoi(string(val[0:ind])) + if err != nil { + return wrapErr(err) + } + min, err := strconv.Atoi(string(val[ind+1])) + if err != nil { + return wrapErr(err) + } + var offset int + if string(val[0]) == "-" { + offset = hr*60 - min + } else { + offset = hr*60 + min + } + secondsEastOfUTC := int((time.Duration(offset) * time.Minute).Seconds()) + timezone := time.FixedZone("", secondsEastOfUTC) + return visitor(t.In(timezone)) + } +} diff --git a/vendor/github.com/google/cel-go/common/types/traits/comparer.go b/vendor/github.com/google/cel-go/common/types/traits/comparer.go new file mode 100644 index 0000000000..b531d9ae2b --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/traits/comparer.go @@ -0,0 +1,33 @@ +// Copyright 2018 Google LLC +// +// 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. + +package traits + +import ( + "github.com/google/cel-go/common/types/ref" +) + +// Comparer interface for ordering comparisons between values in order to +// support '<', '<=', '>=', '>' overloads. +type Comparer interface { + // Compare this value to the input other value, returning an Int: + // + // this < other -> Int(-1) + // this == other -> Int(0) + // this > other -> Int(1) + // + // If the comparison cannot be made or is not supported, an error should + // be returned. + Compare(other ref.Val) ref.Val +} diff --git a/vendor/github.com/google/cel-go/common/types/traits/container.go b/vendor/github.com/google/cel-go/common/types/traits/container.go new file mode 100644 index 0000000000..cf5c621ae9 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/traits/container.go @@ -0,0 +1,23 @@ +// Copyright 2018 Google LLC +// +// 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. + +package traits + +import "github.com/google/cel-go/common/types/ref" + +// Container interface which permits containment tests such as 'a in b'. +type Container interface { + // Contains returns true if the value exists within the object. + Contains(value ref.Val) ref.Val +} diff --git a/vendor/github.com/google/cel-go/common/types/traits/field_tester.go b/vendor/github.com/google/cel-go/common/types/traits/field_tester.go new file mode 100644 index 0000000000..816a956523 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/traits/field_tester.go @@ -0,0 +1,30 @@ +// Copyright 2018 Google LLC +// +// 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. + +package traits + +import ( + "github.com/google/cel-go/common/types/ref" +) + +// FieldTester indicates if a defined field on an object type is set to a +// non-default value. +// +// For use with the `has()` macro. +type FieldTester interface { + // IsSet returns true if the field is defined and set to a non-default + // value. The method will return false if defined and not set, and an error + // if the field is not defined. + IsSet(field ref.Val) ref.Val +} diff --git a/vendor/github.com/google/cel-go/common/types/traits/indexer.go b/vendor/github.com/google/cel-go/common/types/traits/indexer.go new file mode 100644 index 0000000000..662c6836c3 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/traits/indexer.go @@ -0,0 +1,25 @@ +// Copyright 2018 Google LLC +// +// 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. + +package traits + +import ( + "github.com/google/cel-go/common/types/ref" +) + +// Indexer permits random access of elements by index 'a[b()]'. +type Indexer interface { + // Get the value at the specified index or error. + Get(index ref.Val) ref.Val +} diff --git a/vendor/github.com/google/cel-go/common/types/traits/iterator.go b/vendor/github.com/google/cel-go/common/types/traits/iterator.go new file mode 100644 index 0000000000..42dd371aa4 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/traits/iterator.go @@ -0,0 +1,36 @@ +// Copyright 2018 Google LLC +// +// 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. + +package traits + +import ( + "github.com/google/cel-go/common/types/ref" +) + +// Iterable aggregate types permit traversal over their elements. +type Iterable interface { + // Iterator returns a new iterator view of the struct. + Iterator() Iterator +} + +// Iterator permits safe traversal over the contents of an aggregate type. +type Iterator interface { + ref.Val + + // HasNext returns true if there are unvisited elements in the Iterator. + HasNext() ref.Val + + // Next returns the next element. + Next() ref.Val +} diff --git a/vendor/github.com/google/cel-go/common/types/traits/lister.go b/vendor/github.com/google/cel-go/common/types/traits/lister.go new file mode 100644 index 0000000000..5cf2593f3b --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/traits/lister.go @@ -0,0 +1,33 @@ +// Copyright 2018 Google LLC +// +// 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. + +package traits + +import "github.com/google/cel-go/common/types/ref" + +// Lister interface which aggregates the traits of a list. +type Lister interface { + ref.Val + Adder + Container + Indexer + Iterable + Sizer +} + +// MutableLister interface which emits an immutable result after an intermediate computation. +type MutableLister interface { + Lister + ToImmutableList() Lister +} diff --git a/vendor/github.com/google/cel-go/common/types/traits/mapper.go b/vendor/github.com/google/cel-go/common/types/traits/mapper.go new file mode 100644 index 0000000000..2f7c919a8b --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/traits/mapper.go @@ -0,0 +1,33 @@ +// Copyright 2018 Google LLC +// +// 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. + +package traits + +import "github.com/google/cel-go/common/types/ref" + +// Mapper interface which aggregates the traits of a maps. +type Mapper interface { + ref.Val + Container + Indexer + Iterable + Sizer + + // Find returns a value, if one exists, for the input key. + // + // If the key is not found the function returns (nil, false). + // If the input key is not valid for the map, or is Err or Unknown the function returns + // (Unknown|Err, false). + Find(key ref.Val) (ref.Val, bool) +} diff --git a/vendor/github.com/google/cel-go/common/types/traits/matcher.go b/vendor/github.com/google/cel-go/common/types/traits/matcher.go new file mode 100644 index 0000000000..085dc94ff4 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/traits/matcher.go @@ -0,0 +1,23 @@ +// Copyright 2018 Google LLC +// +// 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. + +package traits + +import "github.com/google/cel-go/common/types/ref" + +// Matcher interface for supporting 'matches()' overloads. +type Matcher interface { + // Match returns true if the pattern matches the current value. + Match(pattern ref.Val) ref.Val +} diff --git a/vendor/github.com/google/cel-go/common/types/traits/math.go b/vendor/github.com/google/cel-go/common/types/traits/math.go new file mode 100644 index 0000000000..86d5b9137e --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/traits/math.go @@ -0,0 +1,62 @@ +// Copyright 2018 Google LLC +// +// 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. + +package traits + +import "github.com/google/cel-go/common/types/ref" + +// Adder interface to support '+' operator overloads. +type Adder interface { + // Add returns a combination of the current value and other value. + // + // If the other value is an unsupported type, an error is returned. + Add(other ref.Val) ref.Val +} + +// Divider interface to support '/' operator overloads. +type Divider interface { + // Divide returns the result of dividing the current value by the input + // denominator. + // + // A denominator value of zero results in an error. + Divide(denominator ref.Val) ref.Val +} + +// Modder interface to support '%' operator overloads. +type Modder interface { + // Modulo returns the result of taking the modulus of the current value + // by the denominator. + // + // A denominator value of zero results in an error. + Modulo(denominator ref.Val) ref.Val +} + +// Multiplier interface to support '*' operator overloads. +type Multiplier interface { + // Multiply returns the result of multiplying the current and input value. + Multiply(other ref.Val) ref.Val +} + +// Negater interface to support unary '-' and '!' operator overloads. +type Negater interface { + // Negate returns the complement of the current value. + Negate() ref.Val +} + +// Subtractor interface to support binary '-' operator overloads. +type Subtractor interface { + // Subtract returns the result of subtracting the input from the current + // value. + Subtract(subtrahend ref.Val) ref.Val +} diff --git a/vendor/github.com/google/cel-go/common/types/traits/receiver.go b/vendor/github.com/google/cel-go/common/types/traits/receiver.go new file mode 100644 index 0000000000..8f41db45e8 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/traits/receiver.go @@ -0,0 +1,24 @@ +// Copyright 2018 Google LLC +// +// 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. + +package traits + +import "github.com/google/cel-go/common/types/ref" + +// Receiver interface for routing instance method calls within a value. +type Receiver interface { + // Receive accepts a function name, overload id, and arguments and returns + // a value. + Receive(function string, overload string, args []ref.Val) ref.Val +} diff --git a/vendor/github.com/google/cel-go/common/types/traits/sizer.go b/vendor/github.com/google/cel-go/common/types/traits/sizer.go new file mode 100644 index 0000000000..b80d25137a --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/traits/sizer.go @@ -0,0 +1,25 @@ +// Copyright 2018 Google LLC +// +// 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. + +package traits + +import ( + "github.com/google/cel-go/common/types/ref" +) + +// Sizer interface for supporting 'size()' overloads. +type Sizer interface { + // Size returns the number of elements or length of the value. + Size() ref.Val +} diff --git a/vendor/github.com/google/cel-go/common/types/traits/traits.go b/vendor/github.com/google/cel-go/common/types/traits/traits.go new file mode 100644 index 0000000000..6da3e6a3e1 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/traits/traits.go @@ -0,0 +1,64 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Package traits defines interfaces that a type may implement to participate +// in operator overloads and function dispatch. +package traits + +const ( + // AdderType types provide a '+' operator overload. + AdderType = 1 << iota + + // ComparerType types support ordering comparisons '<', '<=', '>', '>='. + ComparerType + + // ContainerType types support 'in' operations. + ContainerType + + // DividerType types support '/' operations. + DividerType + + // FieldTesterType types support the detection of field value presence. + FieldTesterType + + // IndexerType types support index access with dynamic values. + IndexerType + + // IterableType types can be iterated over in comprehensions. + IterableType + + // IteratorType types support iterator semantics. + IteratorType + + // MatcherType types support pattern matching via 'matches' method. + MatcherType + + // ModderType types support modulus operations '%' + ModderType + + // MultiplierType types support '*' operations. + MultiplierType + + // NegatorType types support either negation via '!' or '-' + NegatorType + + // ReceiverType types support dynamic dispatch to instance methods. + ReceiverType + + // SizerType types support the size() method. + SizerType + + // SubtractorType type support '-' operations. + SubtractorType +) diff --git a/vendor/github.com/google/cel-go/common/types/type.go b/vendor/github.com/google/cel-go/common/types/type.go new file mode 100644 index 0000000000..21160974bb --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/type.go @@ -0,0 +1,102 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "fmt" + "reflect" + + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" +) + +var ( + // TypeType is the type of a TypeValue. + TypeType = NewTypeValue("type") +) + +// TypeValue is an instance of a Value that describes a value's type. +type TypeValue struct { + name string + traitMask int +} + +// NewTypeValue returns *TypeValue which is both a ref.Type and ref.Val. +func NewTypeValue(name string, traits ...int) *TypeValue { + traitMask := 0 + for _, trait := range traits { + traitMask |= trait + } + return &TypeValue{ + name: name, + traitMask: traitMask} +} + +// NewObjectTypeValue returns a *TypeValue based on the input name, which is +// annotated with the traits relevant to all objects. +func NewObjectTypeValue(name string) *TypeValue { + return NewTypeValue(name, + traits.FieldTesterType, + traits.IndexerType) +} + +// ConvertToNative implements ref.Val.ConvertToNative. +func (t *TypeValue) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + // TODO: replace the internal type representation with a proto-value. + return nil, fmt.Errorf("type conversion not supported for 'type'") +} + +// ConvertToType implements ref.Val.ConvertToType. +func (t *TypeValue) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case TypeType: + return TypeType + case StringType: + return String(t.TypeName()) + } + return NewErr("type conversion error from '%s' to '%s'", TypeType, typeVal) +} + +// Equal implements ref.Val.Equal. +func (t *TypeValue) Equal(other ref.Val) ref.Val { + otherType, ok := other.(ref.Type) + return Bool(ok && t.TypeName() == otherType.TypeName()) +} + +// HasTrait indicates whether the type supports the given trait. +// Trait codes are defined in the traits package, e.g. see traits.AdderType. +func (t *TypeValue) HasTrait(trait int) bool { + return trait&t.traitMask == trait +} + +// String implements fmt.Stringer. +func (t *TypeValue) String() string { + return t.name +} + +// Type implements ref.Val.Type. +func (t *TypeValue) Type() ref.Type { + return TypeType +} + +// TypeName gives the type's name as a string. +func (t *TypeValue) TypeName() string { + return t.name +} + +// Value implements ref.Val.Value. +func (t *TypeValue) Value() interface{} { + return t.name +} diff --git a/vendor/github.com/google/cel-go/common/types/uint.go b/vendor/github.com/google/cel-go/common/types/uint.go new file mode 100644 index 0000000000..ca266e0457 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/uint.go @@ -0,0 +1,249 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "fmt" + "math" + "reflect" + "strconv" + + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + + anypb "google.golang.org/protobuf/types/known/anypb" + structpb "google.golang.org/protobuf/types/known/structpb" + wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" +) + +// Uint type implementation which supports comparison and math operators. +type Uint uint64 + +var ( + // UintType singleton. + UintType = NewTypeValue("uint", + traits.AdderType, + traits.ComparerType, + traits.DividerType, + traits.ModderType, + traits.MultiplierType, + traits.SubtractorType) + + uint32WrapperType = reflect.TypeOf(&wrapperspb.UInt32Value{}) + + uint64WrapperType = reflect.TypeOf(&wrapperspb.UInt64Value{}) +) + +// Uint constants +const ( + uintZero = Uint(0) +) + +// Add implements traits.Adder.Add. +func (i Uint) Add(other ref.Val) ref.Val { + otherUint, ok := other.(Uint) + if !ok { + return MaybeNoSuchOverloadErr(other) + } + val, err := addUint64Checked(uint64(i), uint64(otherUint)) + if err != nil { + return wrapErr(err) + } + return Uint(val) +} + +// Compare implements traits.Comparer.Compare. +func (i Uint) Compare(other ref.Val) ref.Val { + switch ov := other.(type) { + case Double: + if math.IsNaN(float64(ov)) { + return NewErr("NaN values cannot be ordered") + } + return compareUintDouble(i, ov) + case Int: + return compareUintInt(i, ov) + case Uint: + return compareUint(i, ov) + default: + return MaybeNoSuchOverloadErr(other) + } +} + +// ConvertToNative implements ref.Val.ConvertToNative. +func (i Uint) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + switch typeDesc.Kind() { + case reflect.Uint, reflect.Uint32: + v, err := uint64ToUint32Checked(uint64(i)) + if err != nil { + return 0, err + } + return reflect.ValueOf(v).Convert(typeDesc).Interface(), nil + case reflect.Uint64: + return reflect.ValueOf(i).Convert(typeDesc).Interface(), nil + case reflect.Ptr: + switch typeDesc { + case anyValueType: + // Primitives must be wrapped before being set on an Any field. + return anypb.New(wrapperspb.UInt64(uint64(i))) + case jsonValueType: + // JSON can accurately represent 32-bit uints as floating point values. + if i.isJSONSafe() { + return structpb.NewNumberValue(float64(i)), nil + } + // Proto3 to JSON conversion requires string-formatted uint64 values + // since the conversion to floating point would result in truncation. + return structpb.NewStringValue(strconv.FormatUint(uint64(i), 10)), nil + case uint32WrapperType: + // Convert the value to a wrapperspb.UInt32Value, error on overflow. + v, err := uint64ToUint32Checked(uint64(i)) + if err != nil { + return 0, err + } + return wrapperspb.UInt32(v), nil + case uint64WrapperType: + // Convert the value to a wrapperspb.UInt64Value. + return wrapperspb.UInt64(uint64(i)), nil + } + switch typeDesc.Elem().Kind() { + case reflect.Uint32: + v, err := uint64ToUint32Checked(uint64(i)) + if err != nil { + return 0, err + } + p := reflect.New(typeDesc.Elem()) + p.Elem().Set(reflect.ValueOf(v).Convert(typeDesc.Elem())) + return p.Interface(), nil + case reflect.Uint64: + v := uint64(i) + p := reflect.New(typeDesc.Elem()) + p.Elem().Set(reflect.ValueOf(v).Convert(typeDesc.Elem())) + return p.Interface(), nil + } + case reflect.Interface: + iv := i.Value() + if reflect.TypeOf(iv).Implements(typeDesc) { + return iv, nil + } + if reflect.TypeOf(i).Implements(typeDesc) { + return i, nil + } + } + return nil, fmt.Errorf("unsupported type conversion from 'uint' to %v", typeDesc) +} + +// ConvertToType implements ref.Val.ConvertToType. +func (i Uint) ConvertToType(typeVal ref.Type) ref.Val { + switch typeVal { + case IntType: + v, err := uint64ToInt64Checked(uint64(i)) + if err != nil { + return wrapErr(err) + } + return Int(v) + case UintType: + return i + case DoubleType: + return Double(i) + case StringType: + return String(fmt.Sprintf("%d", uint64(i))) + case TypeType: + return UintType + } + return NewErr("type conversion error from '%s' to '%s'", UintType, typeVal) +} + +// Divide implements traits.Divider.Divide. +func (i Uint) Divide(other ref.Val) ref.Val { + otherUint, ok := other.(Uint) + if !ok { + return MaybeNoSuchOverloadErr(other) + } + div, err := divideUint64Checked(uint64(i), uint64(otherUint)) + if err != nil { + return wrapErr(err) + } + return Uint(div) +} + +// Equal implements ref.Val.Equal. +func (i Uint) Equal(other ref.Val) ref.Val { + switch ov := other.(type) { + case Double: + if math.IsNaN(float64(ov)) { + return False + } + return Bool(compareUintDouble(i, ov) == 0) + case Int: + return Bool(compareUintInt(i, ov) == 0) + case Uint: + return Bool(i == ov) + default: + return False + } +} + +// Modulo implements traits.Modder.Modulo. +func (i Uint) Modulo(other ref.Val) ref.Val { + otherUint, ok := other.(Uint) + if !ok { + return MaybeNoSuchOverloadErr(other) + } + mod, err := moduloUint64Checked(uint64(i), uint64(otherUint)) + if err != nil { + return wrapErr(err) + } + return Uint(mod) +} + +// Multiply implements traits.Multiplier.Multiply. +func (i Uint) Multiply(other ref.Val) ref.Val { + otherUint, ok := other.(Uint) + if !ok { + return MaybeNoSuchOverloadErr(other) + } + val, err := multiplyUint64Checked(uint64(i), uint64(otherUint)) + if err != nil { + return wrapErr(err) + } + return Uint(val) +} + +// Subtract implements traits.Subtractor.Subtract. +func (i Uint) Subtract(subtrahend ref.Val) ref.Val { + subtraUint, ok := subtrahend.(Uint) + if !ok { + return MaybeNoSuchOverloadErr(subtrahend) + } + val, err := subtractUint64Checked(uint64(i), uint64(subtraUint)) + if err != nil { + return wrapErr(err) + } + return Uint(val) +} + +// Type implements ref.Val.Type. +func (i Uint) Type() ref.Type { + return UintType +} + +// Value implements ref.Val.Value. +func (i Uint) Value() interface{} { + return uint64(i) +} + +// isJSONSafe indicates whether the uint is safely representable as a floating point value in JSON. +func (i Uint) isJSONSafe() bool { + return i <= maxIntJSON +} diff --git a/vendor/github.com/google/cel-go/common/types/unknown.go b/vendor/github.com/google/cel-go/common/types/unknown.go new file mode 100644 index 0000000000..95b47426fd --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/unknown.go @@ -0,0 +1,66 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "reflect" + + "github.com/google/cel-go/common/types/ref" +) + +// Unknown type implementation which collects expression ids which caused the +// current value to become unknown. +type Unknown []int64 + +var ( + // UnknownType singleton. + UnknownType = NewTypeValue("unknown") +) + +// ConvertToNative implements ref.Val.ConvertToNative. +func (u Unknown) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { + return u.Value(), nil +} + +// ConvertToType is an identity function since unknown values cannot be modified. +func (u Unknown) ConvertToType(typeVal ref.Type) ref.Val { + return u +} + +// Equal is an identity function since unknown values cannot be modified. +func (u Unknown) Equal(other ref.Val) ref.Val { + return u +} + +// Type implements ref.Val.Type. +func (u Unknown) Type() ref.Type { + return UnknownType +} + +// Value implements ref.Val.Value. +func (u Unknown) Value() interface{} { + return []int64(u) +} + +// IsUnknown returns whether the element ref.Type or ref.Val is equal to the +// UnknownType singleton. +func IsUnknown(val ref.Val) bool { + switch val.(type) { + case Unknown: + return true + default: + return false + } +} diff --git a/vendor/github.com/google/cel-go/common/types/util.go b/vendor/github.com/google/cel-go/common/types/util.go new file mode 100644 index 0000000000..a8e9afa9e7 --- /dev/null +++ b/vendor/github.com/google/cel-go/common/types/util.go @@ -0,0 +1,48 @@ +// Copyright 2018 Google LLC +// +// 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. + +package types + +import ( + "github.com/google/cel-go/common/types/ref" +) + +// IsUnknownOrError returns whether the input element ref.Val is an ErrType or UnknownType. +func IsUnknownOrError(val ref.Val) bool { + switch val.(type) { + case Unknown, *Err: + return true + } + return false +} + +// IsPrimitiveType returns whether the input element ref.Val is a primitive type. +// Note, primitive types do not include well-known types such as Duration and Timestamp. +func IsPrimitiveType(val ref.Val) bool { + switch val.Type() { + case BoolType, BytesType, DoubleType, IntType, StringType, UintType: + return true + } + return false +} + +// Equal returns whether the two ref.Value are heterogeneously equivalent. +func Equal(lhs ref.Val, rhs ref.Val) ref.Val { + lNull := lhs == NullValue + rNull := rhs == NullValue + if lNull || rNull { + return Bool(lNull == rNull) + } + return lhs.Equal(rhs) +} diff --git a/vendor/github.com/google/cel-go/interpreter/activation.go b/vendor/github.com/google/cel-go/interpreter/activation.go new file mode 100644 index 0000000000..8686d4f04f --- /dev/null +++ b/vendor/github.com/google/cel-go/interpreter/activation.go @@ -0,0 +1,201 @@ +// Copyright 2018 Google LLC +// +// 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. + +package interpreter + +import ( + "errors" + "fmt" + "sync" + + "github.com/google/cel-go/common/types/ref" +) + +// Activation used to resolve identifiers by name and references by id. +// +// An Activation is the primary mechanism by which a caller supplies input into a CEL program. +type Activation interface { + // ResolveName returns a value from the activation by qualified name, or false if the name + // could not be found. + ResolveName(name string) (interface{}, bool) + + // Parent returns the parent of the current activation, may be nil. + // If non-nil, the parent will be searched during resolve calls. + Parent() Activation +} + +// EmptyActivation returns a variable-free activation. +func EmptyActivation() Activation { + return emptyActivation{} +} + +// emptyActivation is a variable-free activation. +type emptyActivation struct{} + +func (emptyActivation) ResolveName(string) (interface{}, bool) { return nil, false } +func (emptyActivation) Parent() Activation { return nil } + +// NewActivation returns an activation based on a map-based binding where the map keys are +// expected to be qualified names used with ResolveName calls. +// +// The input `bindings` may either be of type `Activation` or `map[string]interface{}`. +// +// Lazy bindings may be supplied within the map-based input in either of the following forms: +// - func() interface{} +// - func() ref.Val +// +// The output of the lazy binding will overwrite the variable reference in the internal map. +// +// Values which are not represented as ref.Val types on input may be adapted to a ref.Val using +// the ref.TypeAdapter configured in the environment. +func NewActivation(bindings interface{}) (Activation, error) { + if bindings == nil { + return nil, errors.New("bindings must be non-nil") + } + a, isActivation := bindings.(Activation) + if isActivation { + return a, nil + } + m, isMap := bindings.(map[string]interface{}) + if !isMap { + return nil, fmt.Errorf( + "activation input must be an activation or map[string]interface: got %T", + bindings) + } + return &mapActivation{bindings: m}, nil +} + +// mapActivation which implements Activation and maps of named values. +// +// Named bindings may lazily supply values by providing a function which accepts no arguments and +// produces an interface value. +type mapActivation struct { + bindings map[string]interface{} +} + +// Parent implements the Activation interface method. +func (a *mapActivation) Parent() Activation { + return nil +} + +// ResolveName implements the Activation interface method. +func (a *mapActivation) ResolveName(name string) (interface{}, bool) { + obj, found := a.bindings[name] + if !found { + return nil, false + } + fn, isLazy := obj.(func() ref.Val) + if isLazy { + obj = fn() + a.bindings[name] = obj + } + fnRaw, isLazy := obj.(func() interface{}) + if isLazy { + obj = fnRaw() + a.bindings[name] = obj + } + return obj, found +} + +// hierarchicalActivation which implements Activation and contains a parent and +// child activation. +type hierarchicalActivation struct { + parent Activation + child Activation +} + +// Parent implements the Activation interface method. +func (a *hierarchicalActivation) Parent() Activation { + return a.parent +} + +// ResolveName implements the Activation interface method. +func (a *hierarchicalActivation) ResolveName(name string) (interface{}, bool) { + if object, found := a.child.ResolveName(name); found { + return object, found + } + return a.parent.ResolveName(name) +} + +// NewHierarchicalActivation takes two activations and produces a new one which prioritizes +// resolution in the child first and parent(s) second. +func NewHierarchicalActivation(parent Activation, child Activation) Activation { + return &hierarchicalActivation{parent, child} +} + +// NewPartialActivation returns an Activation which contains a list of AttributePattern values +// representing field and index operations that should result in a 'types.Unknown' result. +// +// The `bindings` value may be any value type supported by the interpreter.NewActivation call, +// but is typically either an existing Activation or map[string]interface{}. +func NewPartialActivation(bindings interface{}, + unknowns ...*AttributePattern) (PartialActivation, error) { + a, err := NewActivation(bindings) + if err != nil { + return nil, err + } + return &partActivation{Activation: a, unknowns: unknowns}, nil +} + +// PartialActivation extends the Activation interface with a set of UnknownAttributePatterns. +type PartialActivation interface { + Activation + + // UnknownAttributePaths returns a set of AttributePattern values which match Attribute + // expressions for data accesses whose values are not yet known. + UnknownAttributePatterns() []*AttributePattern +} + +// partActivation is the default implementations of the PartialActivation interface. +type partActivation struct { + Activation + unknowns []*AttributePattern +} + +// UnknownAttributePatterns implements the PartialActivation interface method. +func (a *partActivation) UnknownAttributePatterns() []*AttributePattern { + return a.unknowns +} + +// varActivation represents a single mutable variable binding. +// +// This activation type should only be used within folds as the fold loop controls the object +// life-cycle. +type varActivation struct { + parent Activation + name string + val ref.Val +} + +// Parent implements the Activation interface method. +func (v *varActivation) Parent() Activation { + return v.parent +} + +// ResolveName implements the Activation interface method. +func (v *varActivation) ResolveName(name string) (interface{}, bool) { + if name == v.name { + return v.val, true + } + return v.parent.ResolveName(name) +} + +var ( + // pool of var activations to reduce allocations during folds. + varActivationPool = &sync.Pool{ + New: func() interface{} { + return &varActivation{} + }, + } +) diff --git a/vendor/github.com/google/cel-go/interpreter/attribute_patterns.go b/vendor/github.com/google/cel-go/interpreter/attribute_patterns.go new file mode 100644 index 0000000000..b33f7f7fd9 --- /dev/null +++ b/vendor/github.com/google/cel-go/interpreter/attribute_patterns.go @@ -0,0 +1,404 @@ +// Copyright 2020 Google LLC +// +// 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. + +package interpreter + +import ( + "fmt" + + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" +) + +// AttributePattern represents a top-level variable with an optional set of qualifier patterns. +// +// When using a CEL expression within a container, e.g. a package or namespace, the variable name +// in the pattern must match the qualified name produced during the variable namespace resolution. +// For example, if variable `c` appears in an expression whose container is `a.b`, the variable +// name supplied to the pattern must be `a.b.c` +// +// The qualifier patterns for attribute matching must be one of the following: +// +// - valid map key type: string, int, uint, bool +// - wildcard (*) +// +// Examples: +// +// 1. ns.myvar["complex-value"] +// 2. ns.myvar["complex-value"][0] +// 3. ns.myvar["complex-value"].*.name +// +// The first example is simple: match an attribute where the variable is 'ns.myvar' with a +// field access on 'complex-value'. The second example expands the match to indicate that only +// a specific index `0` should match. And lastly, the third example matches any indexed access +// that later selects the 'name' field. +type AttributePattern struct { + variable string + qualifierPatterns []*AttributeQualifierPattern +} + +// NewAttributePattern produces a new mutable AttributePattern based on a variable name. +func NewAttributePattern(variable string) *AttributePattern { + return &AttributePattern{ + variable: variable, + qualifierPatterns: []*AttributeQualifierPattern{}, + } +} + +// QualString adds a string qualifier pattern to the AttributePattern. The string may be a valid +// identifier, or string map key including empty string. +func (apat *AttributePattern) QualString(pattern string) *AttributePattern { + apat.qualifierPatterns = append(apat.qualifierPatterns, + &AttributeQualifierPattern{value: pattern}) + return apat +} + +// QualInt adds an int qualifier pattern to the AttributePattern. The index may be either a map or +// list index. +func (apat *AttributePattern) QualInt(pattern int64) *AttributePattern { + apat.qualifierPatterns = append(apat.qualifierPatterns, + &AttributeQualifierPattern{value: pattern}) + return apat +} + +// QualUint adds an uint qualifier pattern for a map index operation to the AttributePattern. +func (apat *AttributePattern) QualUint(pattern uint64) *AttributePattern { + apat.qualifierPatterns = append(apat.qualifierPatterns, + &AttributeQualifierPattern{value: pattern}) + return apat +} + +// QualBool adds a bool qualifier pattern for a map index operation to the AttributePattern. +func (apat *AttributePattern) QualBool(pattern bool) *AttributePattern { + apat.qualifierPatterns = append(apat.qualifierPatterns, + &AttributeQualifierPattern{value: pattern}) + return apat +} + +// Wildcard adds a special sentinel qualifier pattern that will match any single qualifier. +func (apat *AttributePattern) Wildcard() *AttributePattern { + apat.qualifierPatterns = append(apat.qualifierPatterns, + &AttributeQualifierPattern{wildcard: true}) + return apat +} + +// VariableMatches returns true if the fully qualified variable matches the AttributePattern +// fully qualified variable name. +func (apat *AttributePattern) VariableMatches(variable string) bool { + return apat.variable == variable +} + +// QualifierPatterns returns the set of AttributeQualifierPattern values on the AttributePattern. +func (apat *AttributePattern) QualifierPatterns() []*AttributeQualifierPattern { + return apat.qualifierPatterns +} + +// AttributeQualifierPattern holds a wildcard or valued qualifier pattern. +type AttributeQualifierPattern struct { + wildcard bool + value interface{} +} + +// Matches returns true if the qualifier pattern is a wildcard, or the Qualifier implements the +// qualifierValueEquator interface and its IsValueEqualTo returns true for the qualifier pattern. +func (qpat *AttributeQualifierPattern) Matches(q Qualifier) bool { + if qpat.wildcard { + return true + } + qve, ok := q.(qualifierValueEquator) + return ok && qve.QualifierValueEquals(qpat.value) +} + +// qualifierValueEquator defines an interface for determining if an input value, of valid map key +// type, is equal to the value held in the Qualifier. This interface is used by the +// AttributeQualifierPattern to determine pattern matches for non-wildcard qualifier patterns. +// +// Note: Attribute values are also Qualifier values; however, Attributes are resolved before +// qualification happens. This is an implementation detail, but one relevant to why the Attribute +// types do not surface in the list of implementations. +// +// See: partialAttributeFactory.matchesUnknownPatterns for more details on how this interface is +// used. +type qualifierValueEquator interface { + // QualifierValueEquals returns true if the input value is equal to the value held in the + // Qualifier. + QualifierValueEquals(value interface{}) bool +} + +// QualifierValueEquals implementation for boolean qualifiers. +func (q *boolQualifier) QualifierValueEquals(value interface{}) bool { + bval, ok := value.(bool) + return ok && q.value == bval +} + +// QualifierValueEquals implementation for field qualifiers. +func (q *fieldQualifier) QualifierValueEquals(value interface{}) bool { + sval, ok := value.(string) + return ok && q.Name == sval +} + +// QualifierValueEquals implementation for string qualifiers. +func (q *stringQualifier) QualifierValueEquals(value interface{}) bool { + sval, ok := value.(string) + return ok && q.value == sval +} + +// QualifierValueEquals implementation for int qualifiers. +func (q *intQualifier) QualifierValueEquals(value interface{}) bool { + return numericValueEquals(value, q.celValue) +} + +// QualifierValueEquals implementation for uint qualifiers. +func (q *uintQualifier) QualifierValueEquals(value interface{}) bool { + return numericValueEquals(value, q.celValue) +} + +// QualifierValueEquals implementation for double qualifiers. +func (q *doubleQualifier) QualifierValueEquals(value interface{}) bool { + return numericValueEquals(value, q.celValue) +} + +// numericValueEquals uses CEL equality to determine whether two number values are +func numericValueEquals(value interface{}, celValue ref.Val) bool { + val := types.DefaultTypeAdapter.NativeToValue(value) + return celValue.Equal(val) == types.True +} + +// NewPartialAttributeFactory returns an AttributeFactory implementation capable of performing +// AttributePattern matches with PartialActivation inputs. +func NewPartialAttributeFactory(container *containers.Container, + adapter ref.TypeAdapter, + provider ref.TypeProvider) AttributeFactory { + fac := NewAttributeFactory(container, adapter, provider) + return &partialAttributeFactory{ + AttributeFactory: fac, + container: container, + adapter: adapter, + provider: provider, + } +} + +type partialAttributeFactory struct { + AttributeFactory + container *containers.Container + adapter ref.TypeAdapter + provider ref.TypeProvider +} + +// AbsoluteAttribute implementation of the AttributeFactory interface which wraps the +// NamespacedAttribute resolution in an internal attributeMatcher object to dynamically match +// unknown patterns from PartialActivation inputs if given. +func (fac *partialAttributeFactory) AbsoluteAttribute(id int64, names ...string) NamespacedAttribute { + attr := fac.AttributeFactory.AbsoluteAttribute(id, names...) + return &attributeMatcher{fac: fac, NamespacedAttribute: attr} +} + +// MaybeAttribute implementation of the AttributeFactory interface which ensure that the set of +// 'maybe' NamespacedAttribute values are produced using the partialAttributeFactory rather than +// the base AttributeFactory implementation. +func (fac *partialAttributeFactory) MaybeAttribute(id int64, name string) Attribute { + return &maybeAttribute{ + id: id, + attrs: []NamespacedAttribute{ + fac.AbsoluteAttribute(id, fac.container.ResolveCandidateNames(name)...), + }, + adapter: fac.adapter, + provider: fac.provider, + fac: fac, + } +} + +// matchesUnknownPatterns returns true if the variable names and qualifiers for a given +// Attribute value match any of the ActivationPattern objects in the set of unknown activation +// patterns on the given PartialActivation. +// +// For example, in the expression `a.b`, the Attribute is composed of variable `a`, with string +// qualifier `b`. When a PartialActivation is supplied, it indicates that some or all of the data +// provided in the input is unknown by specifying unknown AttributePatterns. An AttributePattern +// that refers to variable `a` with a string qualifier of `c` will not match `a.b`; however, any +// of the following patterns will match Attribute `a.b`: +// +// - `AttributePattern("a")` +// - `AttributePattern("a").Wildcard()` +// - `AttributePattern("a").QualString("b")` +// - `AttributePattern("a").QualString("b").QualInt(0)` +// +// Any AttributePattern which overlaps an Attribute or vice-versa will produce an Unknown result +// for the last pattern matched variable or qualifier in the Attribute. In the first matching +// example, the expression id representing variable `a` would be listed in the Unknown result, +// whereas in the other pattern examples, the qualifier `b` would be returned as the Unknown. +func (fac *partialAttributeFactory) matchesUnknownPatterns( + vars PartialActivation, + attrID int64, + variableNames []string, + qualifiers []Qualifier) (types.Unknown, error) { + patterns := vars.UnknownAttributePatterns() + candidateIndices := map[int]struct{}{} + for _, variable := range variableNames { + for i, pat := range patterns { + if pat.VariableMatches(variable) { + candidateIndices[i] = struct{}{} + } + } + } + // Determine whether to return early if there are no candidate unknown patterns. + if len(candidateIndices) == 0 { + return nil, nil + } + // Determine whether to return early if there are no qualifiers. + if len(qualifiers) == 0 { + return types.Unknown{attrID}, nil + } + // Resolve the attribute qualifiers into a static set. This prevents more dynamic + // Attribute resolutions than necessary when there are multiple unknown patterns + // that traverse the same Attribute-based qualifier field. + newQuals := make([]Qualifier, len(qualifiers)) + for i, qual := range qualifiers { + attr, isAttr := qual.(Attribute) + if isAttr { + val, err := attr.Resolve(vars) + if err != nil { + return nil, err + } + unk, isUnk := val.(types.Unknown) + if isUnk { + return unk, nil + } + // If this resolution behavior ever changes, new implementations of the + // qualifierValueEquator may be required to handle proper resolution. + qual, err = fac.NewQualifier(nil, qual.ID(), val) + if err != nil { + return nil, err + } + } + newQuals[i] = qual + } + // Determine whether any of the unknown patterns match. + for patIdx := range candidateIndices { + pat := patterns[patIdx] + isUnk := true + matchExprID := attrID + qualPats := pat.QualifierPatterns() + for i, qual := range newQuals { + if i >= len(qualPats) { + break + } + matchExprID = qual.ID() + qualPat := qualPats[i] + // Note, the AttributeQualifierPattern relies on the input Qualifier not being an + // Attribute, since there is no way to resolve the Attribute with the information + // provided to the Matches call. + if !qualPat.Matches(qual) { + isUnk = false + break + } + } + if isUnk { + return types.Unknown{matchExprID}, nil + } + } + return nil, nil +} + +// attributeMatcher embeds the NamespacedAttribute interface which allows it to participate in +// AttributePattern matching against Attribute values without having to modify the code paths that +// identify Attributes in expressions. +type attributeMatcher struct { + NamespacedAttribute + qualifiers []Qualifier + fac *partialAttributeFactory +} + +// AddQualifier implements the Attribute interface method. +func (m *attributeMatcher) AddQualifier(qual Qualifier) (Attribute, error) { + // Add the qualifier to the embedded NamespacedAttribute. If the input to the Resolve + // method is not a PartialActivation, or does not match an unknown attribute pattern, the + // Resolve method is directly invoked on the underlying NamespacedAttribute. + _, err := m.NamespacedAttribute.AddQualifier(qual) + if err != nil { + return nil, err + } + // The attributeMatcher overloads TryResolve and will attempt to match unknown patterns against + // the variable name and qualifier set contained within the Attribute. These values are not + // directly inspectable on the top-level NamespacedAttribute interface and so are tracked within + // the attributeMatcher. + m.qualifiers = append(m.qualifiers, qual) + return m, nil +} + +// Resolve is an implementation of the Attribute interface method which uses the +// attributeMatcher TryResolve implementation rather than the embedded NamespacedAttribute +// Resolve implementation. +func (m *attributeMatcher) Resolve(vars Activation) (interface{}, error) { + obj, found, err := m.TryResolve(vars) + if err != nil { + return nil, err + } + if !found { + return nil, fmt.Errorf("no such attribute: %v", m.NamespacedAttribute) + } + return obj, nil +} + +// TryResolve is an implementation of the NamespacedAttribute interface method which tests +// for matching unknown attribute patterns and returns types.Unknown if present. Otherwise, +// the standard Resolve logic applies. +func (m *attributeMatcher) TryResolve(vars Activation) (interface{}, bool, error) { + id := m.NamespacedAttribute.ID() + // Bug in how partial activation is resolved, should search parents as well. + partial, isPartial := toPartialActivation(vars) + if isPartial { + unk, err := m.fac.matchesUnknownPatterns( + partial, + id, + m.CandidateVariableNames(), + m.qualifiers) + if err != nil { + return nil, true, err + } + if unk != nil { + return unk, true, nil + } + } + return m.NamespacedAttribute.TryResolve(vars) +} + +// Qualify is an implementation of the Qualifier interface method. +func (m *attributeMatcher) Qualify(vars Activation, obj interface{}) (interface{}, error) { + val, err := m.Resolve(vars) + if err != nil { + return nil, err + } + unk, isUnk := val.(types.Unknown) + if isUnk { + return unk, nil + } + qual, err := m.fac.NewQualifier(nil, m.ID(), val) + if err != nil { + return nil, err + } + return qual.Qualify(vars, obj) +} + +func toPartialActivation(vars Activation) (PartialActivation, bool) { + pv, ok := vars.(PartialActivation) + if ok { + return pv, true + } + if vars.Parent() != nil { + return toPartialActivation(vars.Parent()) + } + return nil, false +} diff --git a/vendor/github.com/google/cel-go/interpreter/attributes.go b/vendor/github.com/google/cel-go/interpreter/attributes.go new file mode 100644 index 0000000000..4f1772ea39 --- /dev/null +++ b/vendor/github.com/google/cel-go/interpreter/attributes.go @@ -0,0 +1,1051 @@ +// Copyright 2019 Google LLC +// +// 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. + +package interpreter + +import ( + "fmt" + "math" + + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +// AttributeFactory provides methods creating Attribute and Qualifier values. +type AttributeFactory interface { + // AbsoluteAttribute creates an attribute that refers to a top-level variable name. + // + // Checked expressions generate absolute attribute with a single name. + // Parse-only expressions may have more than one possible absolute identifier when the + // expression is created within a container, e.g. package or namespace. + // + // When there is more than one name supplied to the AbsoluteAttribute call, the names + // must be in CEL's namespace resolution order. The name arguments provided here are + // returned in the same order as they were provided by the NamespacedAttribute + // CandidateVariableNames method. + AbsoluteAttribute(id int64, names ...string) NamespacedAttribute + + // ConditionalAttribute creates an attribute with two Attribute branches, where the Attribute + // that is resolved depends on the boolean evaluation of the input 'expr'. + ConditionalAttribute(id int64, expr Interpretable, t, f Attribute) Attribute + + // MaybeAttribute creates an attribute that refers to either a field selection or a namespaced + // variable name. + // + // Only expressions which have not been type-checked may generate oneof attributes. + MaybeAttribute(id int64, name string) Attribute + + // RelativeAttribute creates an attribute whose value is a qualification of a dynamic + // computation rather than a static variable reference. + RelativeAttribute(id int64, operand Interpretable) Attribute + + // NewQualifier creates a qualifier on the target object with a given value. + // + // The 'val' may be an Attribute or any proto-supported map key type: bool, int, string, uint. + // + // The qualifier may consider the object type being qualified, if present. If absent, the + // qualification should be considered dynamic and the qualification should still work, though + // it may be sub-optimal. + NewQualifier(objType *exprpb.Type, qualID int64, val interface{}) (Qualifier, error) +} + +// Qualifier marker interface for designating different qualifier values and where they appear +// within field selections and index call expressions (`_[_]`). +type Qualifier interface { + // ID where the qualifier appears within an expression. + ID() int64 + + // Qualify performs a qualification, e.g. field selection, on the input object and returns + // the value or error that results. + Qualify(vars Activation, obj interface{}) (interface{}, error) +} + +// ConstantQualifier interface embeds the Qualifier interface and provides an option to inspect the +// qualifier's constant value. +// +// Non-constant qualifiers are of Attribute type. +type ConstantQualifier interface { + Qualifier + + Value() ref.Val +} + +// Attribute values are a variable or value with an optional set of qualifiers, such as field, key, +// or index accesses. +type Attribute interface { + Qualifier + + // AddQualifier adds a qualifier on the Attribute or error if the qualification is not a valid + // qualifier type. + AddQualifier(Qualifier) (Attribute, error) + + // Resolve returns the value of the Attribute given the current Activation. + Resolve(Activation) (interface{}, error) +} + +// NamespacedAttribute values are a variable within a namespace, and an optional set of qualifiers +// such as field, key, or index accesses. +type NamespacedAttribute interface { + Attribute + + // CandidateVariableNames returns the possible namespaced variable names for this Attribute in + // the CEL namespace resolution order. + CandidateVariableNames() []string + + // Qualifiers returns the list of qualifiers associated with the Attribute.s + Qualifiers() []Qualifier + + // TryResolve attempts to return the value of the attribute given the current Activation. + // If an error is encountered during attribute resolution, it will be returned immediately. + // If the attribute cannot be resolved within the Activation, the result must be: `nil`, + // `false`, `nil`. + TryResolve(Activation) (interface{}, bool, error) +} + +// NewAttributeFactory returns a default AttributeFactory which is produces Attribute values +// capable of resolving types by simple names and qualify the values using the supported qualifier +// types: bool, int, string, and uint. +func NewAttributeFactory(cont *containers.Container, + a ref.TypeAdapter, + p ref.TypeProvider) AttributeFactory { + return &attrFactory{ + container: cont, + adapter: a, + provider: p, + } +} + +type attrFactory struct { + container *containers.Container + adapter ref.TypeAdapter + provider ref.TypeProvider +} + +// AbsoluteAttribute refers to a variable value and an optional qualifier path. +// +// The namespaceNames represent the names the variable could have based on namespace +// resolution rules. +func (r *attrFactory) AbsoluteAttribute(id int64, names ...string) NamespacedAttribute { + return &absoluteAttribute{ + id: id, + namespaceNames: names, + qualifiers: []Qualifier{}, + adapter: r.adapter, + provider: r.provider, + fac: r, + } +} + +// ConditionalAttribute supports the case where an attribute selection may occur on a conditional +// expression, e.g. (cond ? a : b).c +func (r *attrFactory) ConditionalAttribute(id int64, expr Interpretable, t, f Attribute) Attribute { + return &conditionalAttribute{ + id: id, + expr: expr, + truthy: t, + falsy: f, + adapter: r.adapter, + fac: r, + } +} + +// MaybeAttribute collects variants of unchecked AbsoluteAttribute values which could either be +// direct variable accesses or some combination of variable access with qualification. +func (r *attrFactory) MaybeAttribute(id int64, name string) Attribute { + return &maybeAttribute{ + id: id, + attrs: []NamespacedAttribute{ + r.AbsoluteAttribute(id, r.container.ResolveCandidateNames(name)...), + }, + adapter: r.adapter, + provider: r.provider, + fac: r, + } +} + +// RelativeAttribute refers to an expression and an optional qualifier path. +func (r *attrFactory) RelativeAttribute(id int64, operand Interpretable) Attribute { + return &relativeAttribute{ + id: id, + operand: operand, + qualifiers: []Qualifier{}, + adapter: r.adapter, + fac: r, + } +} + +// NewQualifier is an implementation of the AttributeFactory interface. +func (r *attrFactory) NewQualifier(objType *exprpb.Type, + qualID int64, + val interface{}) (Qualifier, error) { + // Before creating a new qualifier check to see if this is a protobuf message field access. + // If so, use the precomputed GetFrom qualification method rather than the standard + // stringQualifier. + str, isStr := val.(string) + if isStr && objType != nil && objType.GetMessageType() != "" { + ft, found := r.provider.FindFieldType(objType.GetMessageType(), str) + if found && ft.IsSet != nil && ft.GetFrom != nil { + return &fieldQualifier{ + id: qualID, + Name: str, + FieldType: ft, + adapter: r.adapter, + }, nil + } + } + return newQualifier(r.adapter, qualID, val) +} + +type absoluteAttribute struct { + id int64 + // namespaceNames represent the names the variable could have based on declared container + // (package) of the expression. + namespaceNames []string + qualifiers []Qualifier + adapter ref.TypeAdapter + provider ref.TypeProvider + fac AttributeFactory +} + +// ID implements the Attribute interface method. +func (a *absoluteAttribute) ID() int64 { + return a.id +} + +// Cost implements the Coster interface method. +func (a *absoluteAttribute) Cost() (min, max int64) { + for _, q := range a.qualifiers { + minQ, maxQ := estimateCost(q) + min += minQ + max += maxQ + } + min++ // For object retrieval. + max++ + return +} + +// AddQualifier implements the Attribute interface method. +func (a *absoluteAttribute) AddQualifier(qual Qualifier) (Attribute, error) { + a.qualifiers = append(a.qualifiers, qual) + return a, nil +} + +// CandidateVariableNames implements the NamespaceAttribute interface method. +func (a *absoluteAttribute) CandidateVariableNames() []string { + return a.namespaceNames +} + +// Qualifiers returns the list of Qualifier instances associated with the namespaced attribute. +func (a *absoluteAttribute) Qualifiers() []Qualifier { + return a.qualifiers +} + +// Qualify is an implementation of the Qualifier interface method. +func (a *absoluteAttribute) Qualify(vars Activation, obj interface{}) (interface{}, error) { + val, err := a.Resolve(vars) + if err != nil { + return nil, err + } + unk, isUnk := val.(types.Unknown) + if isUnk { + return unk, nil + } + qual, err := a.fac.NewQualifier(nil, a.id, val) + if err != nil { + return nil, err + } + return qual.Qualify(vars, obj) +} + +// Resolve returns the resolved Attribute value given the Activation, or error if the Attribute +// variable is not found, or if its Qualifiers cannot be applied successfully. +func (a *absoluteAttribute) Resolve(vars Activation) (interface{}, error) { + obj, found, err := a.TryResolve(vars) + if err != nil { + return nil, err + } + if found { + return obj, nil + } + return nil, fmt.Errorf("no such attribute: %v", a) +} + +// String implements the Stringer interface method. +func (a *absoluteAttribute) String() string { + return fmt.Sprintf("id: %v, names: %v", a.id, a.namespaceNames) +} + +// TryResolve iterates through the namespaced variable names until one is found within the +// Activation or TypeProvider. +// +// If the variable name cannot be found as an Activation variable or in the TypeProvider as +// a type, then the result is `nil`, `false`, `nil` per the interface requirement. +func (a *absoluteAttribute) TryResolve(vars Activation) (interface{}, bool, error) { + for _, nm := range a.namespaceNames { + // If the variable is found, process it. Otherwise, wait until the checks to + // determine whether the type is unknown before returning. + op, found := vars.ResolveName(nm) + if found { + var err error + for _, qual := range a.qualifiers { + op, err = qual.Qualify(vars, op) + if err != nil { + return nil, true, err + } + } + return op, true, nil + } + // Attempt to resolve the qualified type name if the name is not a variable identifier. + typ, found := a.provider.FindIdent(nm) + if found { + if len(a.qualifiers) == 0 { + return typ, true, nil + } + return nil, true, fmt.Errorf("no such attribute: %v", typ) + } + } + return nil, false, nil +} + +type conditionalAttribute struct { + id int64 + expr Interpretable + truthy Attribute + falsy Attribute + adapter ref.TypeAdapter + fac AttributeFactory +} + +// ID is an implementation of the Attribute interface method. +func (a *conditionalAttribute) ID() int64 { + return a.id +} + +// Cost provides the heuristic cost of a ternary operation ? : . +// The cost is computed as cost(expr) plus the min/max costs of evaluating either +// `t` or `f`. +func (a *conditionalAttribute) Cost() (min, max int64) { + tMin, tMax := estimateCost(a.truthy) + fMin, fMax := estimateCost(a.falsy) + eMin, eMax := estimateCost(a.expr) + return eMin + findMin(tMin, fMin), eMax + findMax(tMax, fMax) +} + +// AddQualifier appends the same qualifier to both sides of the conditional, in effect managing +// the qualification of alternate attributes. +func (a *conditionalAttribute) AddQualifier(qual Qualifier) (Attribute, error) { + _, err := a.truthy.AddQualifier(qual) + if err != nil { + return nil, err + } + _, err = a.falsy.AddQualifier(qual) + if err != nil { + return nil, err + } + return a, nil +} + +// Qualify is an implementation of the Qualifier interface method. +func (a *conditionalAttribute) Qualify(vars Activation, obj interface{}) (interface{}, error) { + val, err := a.Resolve(vars) + if err != nil { + return nil, err + } + unk, isUnk := val.(types.Unknown) + if isUnk { + return unk, nil + } + qual, err := a.fac.NewQualifier(nil, a.id, val) + if err != nil { + return nil, err + } + return qual.Qualify(vars, obj) +} + +// Resolve evaluates the condition, and then resolves the truthy or falsy branch accordingly. +func (a *conditionalAttribute) Resolve(vars Activation) (interface{}, error) { + val := a.expr.Eval(vars) + if types.IsError(val) { + return nil, val.(*types.Err) + } + if val == types.True { + return a.truthy.Resolve(vars) + } + if val == types.False { + return a.falsy.Resolve(vars) + } + if types.IsUnknown(val) { + return val, nil + } + return nil, types.MaybeNoSuchOverloadErr(val).(*types.Err) +} + +// String is an implementation of the Stringer interface method. +func (a *conditionalAttribute) String() string { + return fmt.Sprintf("id: %v, truthy attribute: %v, falsy attribute: %v", a.id, a.truthy, a.falsy) +} + +type maybeAttribute struct { + id int64 + attrs []NamespacedAttribute + adapter ref.TypeAdapter + provider ref.TypeProvider + fac AttributeFactory +} + +// ID is an implementation of the Attribute interface method. +func (a *maybeAttribute) ID() int64 { + return a.id +} + +// Cost implements the Coster interface method. The min cost is computed as the minimal cost among +// all the possible attributes, the max cost ditto. +func (a *maybeAttribute) Cost() (min, max int64) { + min, max = math.MaxInt64, 0 + for _, a := range a.attrs { + minA, maxA := estimateCost(a) + min = findMin(min, minA) + max = findMax(max, maxA) + } + return +} + +func findMin(x, y int64) int64 { + if x < y { + return x + } + return y +} + +func findMax(x, y int64) int64 { + if x > y { + return x + } + return y +} + +// AddQualifier adds a qualifier to each possible attribute variant, and also creates +// a new namespaced variable from the qualified value. +// +// The algorithm for building the maybe attribute is as follows: +// +// 1. Create a maybe attribute from a simple identifier when it occurs in a parsed-only expression +// +// mb = MaybeAttribute(, "a") +// +// Initializing the maybe attribute creates an absolute attribute internally which includes the +// possible namespaced names of the attribute. In this example, let's assume we are in namespace +// 'ns', then the maybe is either one of the following variable names: +// +// possible variables names -- ns.a, a +// +// 2. Adding a qualifier to the maybe means that the variable name could be a longer qualified +// name, or a field selection on one of the possible variable names produced earlier: +// +// mb.AddQualifier("b") +// +// possible variables names -- ns.a.b, a.b +// possible field selection -- ns.a['b'], a['b'] +// +// If none of the attributes within the maybe resolves a value, the result is an error. +func (a *maybeAttribute) AddQualifier(qual Qualifier) (Attribute, error) { + str := "" + isStr := false + cq, isConst := qual.(ConstantQualifier) + if isConst { + str, isStr = cq.Value().Value().(string) + } + var augmentedNames []string + // First add the qualifier to all existing attributes in the oneof. + for _, attr := range a.attrs { + if isStr && len(attr.Qualifiers()) == 0 { + candidateVars := attr.CandidateVariableNames() + augmentedNames = make([]string, len(candidateVars)) + for i, name := range candidateVars { + augmentedNames[i] = fmt.Sprintf("%s.%s", name, str) + } + } + _, err := attr.AddQualifier(qual) + if err != nil { + return nil, err + } + } + // Next, ensure the most specific variable / type reference is searched first. + a.attrs = append([]NamespacedAttribute{a.fac.AbsoluteAttribute(qual.ID(), augmentedNames...)}, a.attrs...) + return a, nil +} + +// Qualify is an implementation of the Qualifier interface method. +func (a *maybeAttribute) Qualify(vars Activation, obj interface{}) (interface{}, error) { + val, err := a.Resolve(vars) + if err != nil { + return nil, err + } + unk, isUnk := val.(types.Unknown) + if isUnk { + return unk, nil + } + qual, err := a.fac.NewQualifier(nil, a.id, val) + if err != nil { + return nil, err + } + return qual.Qualify(vars, obj) +} + +// Resolve follows the variable resolution rules to determine whether the attribute is a variable +// or a field selection. +func (a *maybeAttribute) Resolve(vars Activation) (interface{}, error) { + for _, attr := range a.attrs { + obj, found, err := attr.TryResolve(vars) + // Return an error if one is encountered. + if err != nil { + return nil, err + } + // If the object was found, return it. + if found { + return obj, nil + } + } + // Else, produce a no such attribute error. + return nil, fmt.Errorf("no such attribute: %v", a) +} + +// String is an implementation of the Stringer interface method. +func (a *maybeAttribute) String() string { + return fmt.Sprintf("id: %v, attributes: %v", a.id, a.attrs) +} + +type relativeAttribute struct { + id int64 + operand Interpretable + qualifiers []Qualifier + adapter ref.TypeAdapter + fac AttributeFactory +} + +// ID is an implementation of the Attribute interface method. +func (a *relativeAttribute) ID() int64 { + return a.id +} + +// Cost implements the Coster interface method. +func (a *relativeAttribute) Cost() (min, max int64) { + min, max = estimateCost(a.operand) + for _, qual := range a.qualifiers { + minQ, maxQ := estimateCost(qual) + min += minQ + max += maxQ + } + return +} + +// AddQualifier implements the Attribute interface method. +func (a *relativeAttribute) AddQualifier(qual Qualifier) (Attribute, error) { + a.qualifiers = append(a.qualifiers, qual) + return a, nil +} + +// Qualify is an implementation of the Qualifier interface method. +func (a *relativeAttribute) Qualify(vars Activation, obj interface{}) (interface{}, error) { + val, err := a.Resolve(vars) + if err != nil { + return nil, err + } + unk, isUnk := val.(types.Unknown) + if isUnk { + return unk, nil + } + qual, err := a.fac.NewQualifier(nil, a.id, val) + if err != nil { + return nil, err + } + return qual.Qualify(vars, obj) +} + +// Resolve expression value and qualifier relative to the expression result. +func (a *relativeAttribute) Resolve(vars Activation) (interface{}, error) { + // First, evaluate the operand. + v := a.operand.Eval(vars) + if types.IsError(v) { + return nil, v.(*types.Err) + } + if types.IsUnknown(v) { + return v, nil + } + // Next, qualify it. Qualification handles unknowns as well, so there's no need to recheck. + var err error + var obj interface{} = v + for _, qual := range a.qualifiers { + obj, err = qual.Qualify(vars, obj) + if err != nil { + return nil, err + } + } + return obj, nil +} + +// String is an implementation of the Stringer interface method. +func (a *relativeAttribute) String() string { + return fmt.Sprintf("id: %v, operand: %v", a.id, a.operand) +} + +func newQualifier(adapter ref.TypeAdapter, id int64, v interface{}) (Qualifier, error) { + var qual Qualifier + switch val := v.(type) { + case Attribute: + return &attrQualifier{id: id, Attribute: val}, nil + case string: + qual = &stringQualifier{id: id, value: val, celValue: types.String(val), adapter: adapter} + case int: + qual = &intQualifier{id: id, value: int64(val), celValue: types.Int(val), adapter: adapter} + case int32: + qual = &intQualifier{id: id, value: int64(val), celValue: types.Int(val), adapter: adapter} + case int64: + qual = &intQualifier{id: id, value: val, celValue: types.Int(val), adapter: adapter} + case uint: + qual = &uintQualifier{id: id, value: uint64(val), celValue: types.Uint(val), adapter: adapter} + case uint32: + qual = &uintQualifier{id: id, value: uint64(val), celValue: types.Uint(val), adapter: adapter} + case uint64: + qual = &uintQualifier{id: id, value: val, celValue: types.Uint(val), adapter: adapter} + case bool: + qual = &boolQualifier{id: id, value: val, celValue: types.Bool(val), adapter: adapter} + case float32: + qual = &doubleQualifier{id: id, value: float64(val), celValue: types.Double(val), adapter: adapter} + case float64: + qual = &doubleQualifier{id: id, value: val, celValue: types.Double(val), adapter: adapter} + case types.String: + qual = &stringQualifier{id: id, value: string(val), celValue: val, adapter: adapter} + case types.Int: + qual = &intQualifier{id: id, value: int64(val), celValue: val, adapter: adapter} + case types.Uint: + qual = &uintQualifier{id: id, value: uint64(val), celValue: val, adapter: adapter} + case types.Bool: + qual = &boolQualifier{id: id, value: bool(val), celValue: val, adapter: adapter} + case types.Double: + qual = &doubleQualifier{id: id, value: float64(val), celValue: val, adapter: adapter} + default: + return nil, fmt.Errorf("invalid qualifier type: %T", v) + } + return qual, nil +} + +type attrQualifier struct { + id int64 + Attribute +} + +func (q *attrQualifier) ID() int64 { + return q.id +} + +// Cost returns zero for constant field qualifiers +func (q *attrQualifier) Cost() (min, max int64) { + return estimateCost(q.Attribute) +} + +type stringQualifier struct { + id int64 + value string + celValue ref.Val + adapter ref.TypeAdapter +} + +// ID is an implementation of the Qualifier interface method. +func (q *stringQualifier) ID() int64 { + return q.id +} + +// Qualify implements the Qualifier interface method. +func (q *stringQualifier) Qualify(vars Activation, obj interface{}) (interface{}, error) { + s := q.value + isMap := false + isKey := false + switch o := obj.(type) { + case map[string]interface{}: + isMap = true + obj, isKey = o[s] + case map[string]string: + isMap = true + obj, isKey = o[s] + case map[string]int: + isMap = true + obj, isKey = o[s] + case map[string]int32: + isMap = true + obj, isKey = o[s] + case map[string]int64: + isMap = true + obj, isKey = o[s] + case map[string]uint: + isMap = true + obj, isKey = o[s] + case map[string]uint32: + isMap = true + obj, isKey = o[s] + case map[string]uint64: + isMap = true + obj, isKey = o[s] + case map[string]float32: + isMap = true + obj, isKey = o[s] + case map[string]float64: + isMap = true + obj, isKey = o[s] + case map[string]bool: + isMap = true + obj, isKey = o[s] + case types.Unknown: + return o, nil + default: + elem, err := refResolve(q.adapter, q.celValue, obj) + if err != nil { + return nil, err + } + return elem, nil + } + if isMap && !isKey { + return nil, fmt.Errorf("no such key: %v", s) + } + return obj, nil +} + +// Value implements the ConstantQualifier interface +func (q *stringQualifier) Value() ref.Val { + return q.celValue +} + +// Cost returns zero for constant field qualifiers +func (q *stringQualifier) Cost() (min, max int64) { + return 0, 0 +} + +type intQualifier struct { + id int64 + value int64 + celValue ref.Val + adapter ref.TypeAdapter +} + +// ID is an implementation of the Qualifier interface method. +func (q *intQualifier) ID() int64 { + return q.id +} + +// Qualify implements the Qualifier interface method. +func (q *intQualifier) Qualify(vars Activation, obj interface{}) (interface{}, error) { + i := q.value + isMap := false + isKey := false + isIndex := false + switch o := obj.(type) { + // The specialized map types supported by an int qualifier are considerably fewer than the set + // of specialized map types supported by string qualifiers since they are less frequently used + // than string-based map keys. Additional specializations may be added in the future if + // desired. + case map[int]interface{}: + isMap = true + obj, isKey = o[int(i)] + case map[int32]interface{}: + isMap = true + obj, isKey = o[int32(i)] + case map[int64]interface{}: + isMap = true + obj, isKey = o[i] + case []interface{}: + isIndex = i >= 0 && i < int64(len(o)) + if isIndex { + obj = o[i] + } + case []string: + isIndex = i >= 0 && i < int64(len(o)) + if isIndex { + obj = o[i] + } + case []int: + isIndex = i >= 0 && i < int64(len(o)) + if isIndex { + obj = o[i] + } + case []int32: + isIndex = i >= 0 && i < int64(len(o)) + if isIndex { + obj = o[i] + } + case []int64: + isIndex = i >= 0 && i < int64(len(o)) + if isIndex { + obj = o[i] + } + case []uint: + isIndex = i >= 0 && i < int64(len(o)) + if isIndex { + obj = o[i] + } + case []uint32: + isIndex = i >= 0 && i < int64(len(o)) + if isIndex { + obj = o[i] + } + case []uint64: + isIndex = i >= 0 && i < int64(len(o)) + if isIndex { + obj = o[i] + } + case []float32: + isIndex = i >= 0 && i < int64(len(o)) + if isIndex { + obj = o[i] + } + case []float64: + isIndex = i >= 0 && i < int64(len(o)) + if isIndex { + obj = o[i] + } + case []bool: + isIndex = i >= 0 && i < int64(len(o)) + if isIndex { + obj = o[i] + } + case types.Unknown: + return o, nil + default: + elem, err := refResolve(q.adapter, q.celValue, obj) + if err != nil { + return nil, err + } + return elem, nil + } + if isMap && !isKey { + return nil, fmt.Errorf("no such key: %v", i) + } + if !isMap && !isIndex { + return nil, fmt.Errorf("index out of bounds: %v", i) + } + return obj, nil +} + +// Value implements the ConstantQualifier interface +func (q *intQualifier) Value() ref.Val { + return q.celValue +} + +// Cost returns zero for constant field qualifiers +func (q *intQualifier) Cost() (min, max int64) { + return 0, 0 +} + +type uintQualifier struct { + id int64 + value uint64 + celValue ref.Val + adapter ref.TypeAdapter +} + +// ID is an implementation of the Qualifier interface method. +func (q *uintQualifier) ID() int64 { + return q.id +} + +// Qualify implements the Qualifier interface method. +func (q *uintQualifier) Qualify(vars Activation, obj interface{}) (interface{}, error) { + u := q.value + isMap := false + isKey := false + switch o := obj.(type) { + // The specialized map types supported by a uint qualifier are considerably fewer than the set + // of specialized map types supported by string qualifiers since they are less frequently used + // than string-based map keys. Additional specializations may be added in the future if + // desired. + case map[uint]interface{}: + isMap = true + obj, isKey = o[uint(u)] + case map[uint32]interface{}: + isMap = true + obj, isKey = o[uint32(u)] + case map[uint64]interface{}: + isMap = true + obj, isKey = o[u] + case types.Unknown: + return o, nil + default: + elem, err := refResolve(q.adapter, q.celValue, obj) + if err != nil { + return nil, err + } + return elem, nil + } + if isMap && !isKey { + return nil, fmt.Errorf("no such key: %v", u) + } + return obj, nil +} + +// Value implements the ConstantQualifier interface +func (q *uintQualifier) Value() ref.Val { + return q.celValue +} + +// Cost returns zero for constant field qualifiers +func (q *uintQualifier) Cost() (min, max int64) { + return 0, 0 +} + +type boolQualifier struct { + id int64 + value bool + celValue ref.Val + adapter ref.TypeAdapter +} + +// ID is an implementation of the Qualifier interface method. +func (q *boolQualifier) ID() int64 { + return q.id +} + +// Qualify implements the Qualifier interface method. +func (q *boolQualifier) Qualify(vars Activation, obj interface{}) (interface{}, error) { + b := q.value + isKey := false + switch o := obj.(type) { + // The specialized map types supported by a bool qualifier are considerably fewer than the set + // of specialized map types supported by string qualifiers since they are less frequently used + // than string-based map keys. Additional specializations may be added in the future if + // desired. + case map[bool]interface{}: + obj, isKey = o[b] + case types.Unknown: + return o, nil + default: + elem, err := refResolve(q.adapter, q.celValue, obj) + if err != nil { + return nil, err + } + return elem, nil + } + if !isKey { + return nil, fmt.Errorf("no such key: %v", b) + } + return obj, nil +} + +// Value implements the ConstantQualifier interface +func (q *boolQualifier) Value() ref.Val { + return q.celValue +} + +// Cost returns zero for constant field qualifiers +func (q *boolQualifier) Cost() (min, max int64) { + return 0, 0 +} + +// fieldQualifier indicates that the qualification is a well-defined field with a known +// field type. When the field type is known this can be used to improve the speed and +// efficiency of field resolution. +type fieldQualifier struct { + id int64 + Name string + FieldType *ref.FieldType + adapter ref.TypeAdapter +} + +// ID is an implementation of the Qualifier interface method. +func (q *fieldQualifier) ID() int64 { + return q.id +} + +// Qualify implements the Qualifier interface method. +func (q *fieldQualifier) Qualify(vars Activation, obj interface{}) (interface{}, error) { + if rv, ok := obj.(ref.Val); ok { + obj = rv.Value() + } + return q.FieldType.GetFrom(obj) +} + +// Value implements the ConstantQualifier interface +func (q *fieldQualifier) Value() ref.Val { + return types.String(q.Name) +} + +// Cost returns zero for constant field qualifiers +func (q *fieldQualifier) Cost() (min, max int64) { + return 0, 0 +} + +// doubleQualifier qualifies a CEL object, map, or list using a double value. +// +// This qualifier is used for working with dynamic data like JSON or protobuf.Any where the value +// type may not be known ahead of time and may not conform to the standard types supported as valid +// protobuf map key types. +type doubleQualifier struct { + id int64 + value float64 + celValue ref.Val + adapter ref.TypeAdapter +} + +// ID is an implementation of the Qualifier interface method. +func (q *doubleQualifier) ID() int64 { + return q.id +} + +// Qualify implements the Qualifier interface method. +func (q *doubleQualifier) Qualify(vars Activation, obj interface{}) (interface{}, error) { + switch o := obj.(type) { + case types.Unknown: + return o, nil + default: + elem, err := refResolve(q.adapter, q.celValue, obj) + if err != nil { + return nil, err + } + return elem, nil + } +} + +// refResolve attempts to convert the value to a CEL value and then uses reflection methods +// to try and resolve the qualifier. +func refResolve(adapter ref.TypeAdapter, idx ref.Val, obj interface{}) (ref.Val, error) { + celVal := adapter.NativeToValue(obj) + mapper, isMapper := celVal.(traits.Mapper) + if isMapper { + elem, found := mapper.Find(idx) + if !found { + return nil, fmt.Errorf("no such key: %v", idx) + } + return elem, nil + } + indexer, isIndexer := celVal.(traits.Indexer) + if isIndexer { + elem := indexer.Get(idx) + if types.IsError(elem) { + return nil, elem.(*types.Err) + } + return elem, nil + } + if types.IsUnknown(celVal) { + return celVal, nil + } + // TODO: If the types.Err value contains more than just an error message at some point in the + // future, then it would be reasonable to return error values as ref.Val types rather than + // simple go error types. + if types.IsError(celVal) { + return nil, celVal.(*types.Err) + } + return nil, fmt.Errorf("no such key: %v", idx) +} diff --git a/vendor/github.com/google/cel-go/interpreter/coster.go b/vendor/github.com/google/cel-go/interpreter/coster.go new file mode 100644 index 0000000000..ac573d5745 --- /dev/null +++ b/vendor/github.com/google/cel-go/interpreter/coster.go @@ -0,0 +1,35 @@ +// Copyright 2020 Google LLC +// +// 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. + +package interpreter + +import "math" + +// TODO: remove Coster. + +// Coster calculates the heuristic cost incurred during evaluation. +// Deprecated: Please migrate cel.EstimateCost, it supports length estimates for input data and cost estimates for +// extension functions. +type Coster interface { + Cost() (min, max int64) +} + +// estimateCost returns the heuristic cost interval for the program. +func estimateCost(i interface{}) (min, max int64) { + c, ok := i.(Coster) + if !ok { + return 0, math.MaxInt64 + } + return c.Cost() +} diff --git a/vendor/github.com/google/cel-go/interpreter/decorators.go b/vendor/github.com/google/cel-go/interpreter/decorators.go new file mode 100644 index 0000000000..bdbbad43e2 --- /dev/null +++ b/vendor/github.com/google/cel-go/interpreter/decorators.go @@ -0,0 +1,269 @@ +// Copyright 2018 Google LLC +// +// 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. + +package interpreter + +import ( + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" +) + +// InterpretableDecorator is a functional interface for decorating or replacing +// Interpretable expression nodes at construction time. +type InterpretableDecorator func(Interpretable) (Interpretable, error) + +// decObserveEval records evaluation state into an EvalState object. +func decObserveEval(observer EvalObserver) InterpretableDecorator { + return func(i Interpretable) (Interpretable, error) { + switch inst := i.(type) { + case *evalWatch, *evalWatchAttr, *evalWatchConst: + // these instruction are already watching, return straight-away. + return i, nil + case InterpretableAttribute: + return &evalWatchAttr{ + InterpretableAttribute: inst, + observer: observer, + }, nil + case InterpretableConst: + return &evalWatchConst{ + InterpretableConst: inst, + observer: observer, + }, nil + default: + return &evalWatch{ + Interpretable: i, + observer: observer, + }, nil + } + } +} + +// decInterruptFolds creates an intepretable decorator which marks comprehensions as interruptable +// where the interrupt state is communicated via a hidden variable on the Activation. +func decInterruptFolds() InterpretableDecorator { + return func(i Interpretable) (Interpretable, error) { + fold, ok := i.(*evalFold) + if !ok { + return i, nil + } + fold.interruptable = true + return fold, nil + } +} + +// decDisableShortcircuits ensures that all branches of an expression will be evaluated, no short-circuiting. +func decDisableShortcircuits() InterpretableDecorator { + return func(i Interpretable) (Interpretable, error) { + switch expr := i.(type) { + case *evalOr: + return &evalExhaustiveOr{ + id: expr.id, + lhs: expr.lhs, + rhs: expr.rhs, + }, nil + case *evalAnd: + return &evalExhaustiveAnd{ + id: expr.id, + lhs: expr.lhs, + rhs: expr.rhs, + }, nil + case *evalFold: + expr.exhaustive = true + return expr, nil + case InterpretableAttribute: + cond, isCond := expr.Attr().(*conditionalAttribute) + if isCond { + return &evalExhaustiveConditional{ + id: cond.id, + attr: cond, + adapter: expr.Adapter(), + }, nil + } + } + return i, nil + } +} + +// decOptimize optimizes the program plan by looking for common evaluation patterns and +// conditionally precomputing the result. +// - build list and map values with constant elements. +// - convert 'in' operations to set membership tests if possible. +func decOptimize() InterpretableDecorator { + return func(i Interpretable) (Interpretable, error) { + switch inst := i.(type) { + case *evalList: + return maybeBuildListLiteral(i, inst) + case *evalMap: + return maybeBuildMapLiteral(i, inst) + case InterpretableCall: + if inst.OverloadID() == overloads.InList { + return maybeOptimizeSetMembership(i, inst) + } + if overloads.IsTypeConversionFunction(inst.Function()) { + return maybeOptimizeConstUnary(i, inst) + } + } + return i, nil + } +} + +// decRegexOptimizer compiles regex pattern string constants. +func decRegexOptimizer(regexOptimizations ...*RegexOptimization) InterpretableDecorator { + functionMatchMap := make(map[string]*RegexOptimization) + overloadMatchMap := make(map[string]*RegexOptimization) + for _, m := range regexOptimizations { + functionMatchMap[m.Function] = m + if m.OverloadID != "" { + overloadMatchMap[m.OverloadID] = m + } + } + + return func(i Interpretable) (Interpretable, error) { + call, ok := i.(InterpretableCall) + if !ok { + return i, nil + } + + var matcher *RegexOptimization + var found bool + if call.OverloadID() != "" { + matcher, found = overloadMatchMap[call.OverloadID()] + } + if !found { + matcher, found = functionMatchMap[call.Function()] + } + if !found || matcher.RegexIndex >= len(call.Args()) { + return i, nil + } + args := call.Args() + regexArg := args[matcher.RegexIndex] + regexStr, isConst := regexArg.(InterpretableConst) + if !isConst { + return i, nil + } + pattern, ok := regexStr.Value().(types.String) + if !ok { + return i, nil + } + return matcher.Factory(call, string(pattern)) + } +} + +func maybeOptimizeConstUnary(i Interpretable, call InterpretableCall) (Interpretable, error) { + args := call.Args() + if len(args) != 1 { + return i, nil + } + _, isConst := args[0].(InterpretableConst) + if !isConst { + return i, nil + } + val := call.Eval(EmptyActivation()) + if types.IsError(val) { + return nil, val.(*types.Err) + } + return NewConstValue(call.ID(), val), nil +} + +func maybeBuildListLiteral(i Interpretable, l *evalList) (Interpretable, error) { + for _, elem := range l.elems { + _, isConst := elem.(InterpretableConst) + if !isConst { + return i, nil + } + } + return NewConstValue(l.ID(), l.Eval(EmptyActivation())), nil +} + +func maybeBuildMapLiteral(i Interpretable, mp *evalMap) (Interpretable, error) { + for idx, key := range mp.keys { + _, isConst := key.(InterpretableConst) + if !isConst { + return i, nil + } + _, isConst = mp.vals[idx].(InterpretableConst) + if !isConst { + return i, nil + } + } + return NewConstValue(mp.ID(), mp.Eval(EmptyActivation())), nil +} + +// maybeOptimizeSetMembership may convert an 'in' operation against a list to map key membership +// test if the following conditions are true: +// - the list is a constant with homogeneous element types. +// - the elements are all of primitive type. +func maybeOptimizeSetMembership(i Interpretable, inlist InterpretableCall) (Interpretable, error) { + args := inlist.Args() + lhs := args[0] + rhs := args[1] + l, isConst := rhs.(InterpretableConst) + if !isConst { + return i, nil + } + // When the incoming binary call is flagged with as the InList overload, the value will + // always be convertible to a `traits.Lister` type. + list := l.Value().(traits.Lister) + if list.Size() == types.IntZero { + return NewConstValue(inlist.ID(), types.False), nil + } + it := list.Iterator() + valueSet := make(map[ref.Val]ref.Val) + for it.HasNext() == types.True { + elem := it.Next() + if !types.IsPrimitiveType(elem) { + // Note, non-primitive type are not yet supported. + return i, nil + } + valueSet[elem] = types.True + switch ev := elem.(type) { + case types.Double: + iv := ev.ConvertToType(types.IntType) + // Ensure that only lossless conversions are added to the set + if !types.IsError(iv) && iv.Equal(ev) == types.True { + valueSet[iv] = types.True + } + // Ensure that only lossless conversions are added to the set + uv := ev.ConvertToType(types.UintType) + if !types.IsError(uv) && uv.Equal(ev) == types.True { + valueSet[uv] = types.True + } + case types.Int: + dv := ev.ConvertToType(types.DoubleType) + if !types.IsError(dv) { + valueSet[dv] = types.True + } + uv := ev.ConvertToType(types.UintType) + if !types.IsError(uv) { + valueSet[uv] = types.True + } + case types.Uint: + dv := ev.ConvertToType(types.DoubleType) + if !types.IsError(dv) { + valueSet[dv] = types.True + } + iv := ev.ConvertToType(types.IntType) + if !types.IsError(iv) { + valueSet[iv] = types.True + } + } + } + return &evalSetMembership{ + inst: inlist, + arg: lhs, + valueSet: valueSet, + }, nil +} diff --git a/vendor/github.com/google/cel-go/interpreter/dispatcher.go b/vendor/github.com/google/cel-go/interpreter/dispatcher.go new file mode 100644 index 0000000000..febf9d8a83 --- /dev/null +++ b/vendor/github.com/google/cel-go/interpreter/dispatcher.go @@ -0,0 +1,100 @@ +// Copyright 2018 Google LLC +// +// 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. + +package interpreter + +import ( + "fmt" + + "github.com/google/cel-go/interpreter/functions" +) + +// Dispatcher resolves function calls to their appropriate overload. +type Dispatcher interface { + // Add one or more overloads, returning an error if any Overload has the same Overload#Name. + Add(overloads ...*functions.Overload) error + + // FindOverload returns an Overload definition matching the provided name. + FindOverload(overload string) (*functions.Overload, bool) + + // OverloadIds returns the set of all overload identifiers configured for dispatch. + OverloadIds() []string +} + +// NewDispatcher returns an empty Dispatcher instance. +func NewDispatcher() Dispatcher { + return &defaultDispatcher{ + overloads: make(map[string]*functions.Overload)} +} + +// ExtendDispatcher returns a Dispatcher which inherits the overloads of its parent, and +// provides an isolation layer between built-ins and extension functions which is useful +// for forward compatibility. +func ExtendDispatcher(parent Dispatcher) Dispatcher { + return &defaultDispatcher{ + parent: parent, + overloads: make(map[string]*functions.Overload)} +} + +// overloadMap helper type for indexing overloads by function name. +type overloadMap map[string]*functions.Overload + +// defaultDispatcher struct which contains an overload map. +type defaultDispatcher struct { + parent Dispatcher + overloads overloadMap +} + +// Add implements the Dispatcher.Add interface method. +func (d *defaultDispatcher) Add(overloads ...*functions.Overload) error { + for _, o := range overloads { + // add the overload unless an overload of the same name has already been provided. + if _, found := d.overloads[o.Operator]; found { + return fmt.Errorf("overload already exists '%s'", o.Operator) + } + // index the overload by function name. + d.overloads[o.Operator] = o + } + return nil +} + +// FindOverload implements the Dispatcher.FindOverload interface method. +func (d *defaultDispatcher) FindOverload(overload string) (*functions.Overload, bool) { + o, found := d.overloads[overload] + // Attempt to dispatch to an overload defined in the parent. + if !found && d.parent != nil { + return d.parent.FindOverload(overload) + } + return o, found +} + +// OverloadIds implements the Dispatcher interface method. +func (d *defaultDispatcher) OverloadIds() []string { + i := 0 + overloads := make([]string, len(d.overloads)) + for name := range d.overloads { + overloads[i] = name + i++ + } + if d.parent == nil { + return overloads + } + parentOverloads := d.parent.OverloadIds() + for _, pName := range parentOverloads { + if _, found := d.overloads[pName]; !found { + overloads = append(overloads, pName) + } + } + return overloads +} diff --git a/vendor/github.com/google/cel-go/interpreter/evalstate.go b/vendor/github.com/google/cel-go/interpreter/evalstate.go new file mode 100644 index 0000000000..cc0d3e6f94 --- /dev/null +++ b/vendor/github.com/google/cel-go/interpreter/evalstate.go @@ -0,0 +1,75 @@ +// Copyright 2018 Google LLC +// +// 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. + +package interpreter + +import ( + "github.com/google/cel-go/common/types/ref" +) + +// EvalState tracks the values associated with expression ids during execution. +type EvalState interface { + // IDs returns the list of ids with recorded values. + IDs() []int64 + + // Value returns the observed value of the given expression id if found, and a nil false + // result if not. + Value(int64) (ref.Val, bool) + + // SetValue sets the observed value of the expression id. + SetValue(int64, ref.Val) + + // Reset clears the previously recorded expression values. + Reset() +} + +// evalState permits the mutation of evaluation state for a given expression id. +type evalState struct { + values map[int64]ref.Val +} + +// NewEvalState returns an EvalState instanced used to observe the intermediate +// evaluations of an expression. +func NewEvalState() EvalState { + return &evalState{ + values: make(map[int64]ref.Val), + } +} + +// IDs implements the EvalState interface method. +func (s *evalState) IDs() []int64 { + var ids []int64 + for k, v := range s.values { + if v != nil { + ids = append(ids, k) + } + } + return ids +} + +// Value is an implementation of the EvalState interface method. +func (s *evalState) Value(exprID int64) (ref.Val, bool) { + val, found := s.values[exprID] + return val, found +} + +// SetValue is an implementation of the EvalState interface method. +func (s *evalState) SetValue(exprID int64, val ref.Val) { + s.values[exprID] = val +} + +// Reset implements the EvalState interface method. +func (s *evalState) Reset() { + s.values = map[int64]ref.Val{} +} diff --git a/vendor/github.com/google/cel-go/interpreter/functions/functions.go b/vendor/github.com/google/cel-go/interpreter/functions/functions.go new file mode 100644 index 0000000000..4ca706e96c --- /dev/null +++ b/vendor/github.com/google/cel-go/interpreter/functions/functions.go @@ -0,0 +1,58 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Package functions defines the standard builtin functions supported by the +// interpreter and as declared within the checker#StandardDeclarations. +package functions + +import "github.com/google/cel-go/common/types/ref" + +// Overload defines a named overload of a function, indicating an operand trait +// which must be present on the first argument to the overload as well as one +// of either a unary, binary, or function implementation. +// +// The majority of operators within the expression language are unary or binary +// and the specializations simplify the call contract for implementers of +// types with operator overloads. Any added complexity is assumed to be handled +// by the generic FunctionOp. +type Overload struct { + // Operator name as written in an expression or defined within + // operators.go. + Operator string + + // Operand trait used to dispatch the call. The zero-value indicates a + // global function overload or that one of the Unary / Binary / Function + // definitions should be used to execute the call. + OperandTrait int + + // Unary defines the overload with a UnaryOp implementation. May be nil. + Unary UnaryOp + + // Binary defines the overload with a BinaryOp implementation. May be nil. + Binary BinaryOp + + // Function defines the overload with a FunctionOp implementation. May be + // nil. + Function FunctionOp +} + +// UnaryOp is a function that takes a single value and produces an output. +type UnaryOp func(value ref.Val) ref.Val + +// BinaryOp is a function that takes two values and produces an output. +type BinaryOp func(lhs ref.Val, rhs ref.Val) ref.Val + +// FunctionOp is a function with accepts zero or more arguments and produces +// an value (as interface{}) or error as a result. +type FunctionOp func(values ...ref.Val) ref.Val diff --git a/vendor/github.com/google/cel-go/interpreter/functions/standard.go b/vendor/github.com/google/cel-go/interpreter/functions/standard.go new file mode 100644 index 0000000000..73e936114f --- /dev/null +++ b/vendor/github.com/google/cel-go/interpreter/functions/standard.go @@ -0,0 +1,270 @@ +// Copyright 2018 Google LLC +// +// 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. + +package functions + +import ( + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" +) + +// StandardOverloads returns the definitions of the built-in overloads. +func StandardOverloads() []*Overload { + return []*Overload{ + // Logical not (!a) + { + Operator: operators.LogicalNot, + OperandTrait: traits.NegatorType, + Unary: func(value ref.Val) ref.Val { + if !types.IsBool(value) { + return types.ValOrErr(value, "no such overload") + } + return value.(traits.Negater).Negate() + }}, + // Not strictly false: IsBool(a) ? a : true + { + Operator: operators.NotStrictlyFalse, + Unary: notStrictlyFalse}, + // Deprecated: not strictly false, may be overridden in the environment. + { + Operator: operators.OldNotStrictlyFalse, + Unary: notStrictlyFalse}, + + // Less than operator + {Operator: operators.Less, + OperandTrait: traits.ComparerType, + Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { + cmp := lhs.(traits.Comparer).Compare(rhs) + if cmp == types.IntNegOne { + return types.True + } + if cmp == types.IntOne || cmp == types.IntZero { + return types.False + } + return cmp + }}, + + // Less than or equal operator + {Operator: operators.LessEquals, + OperandTrait: traits.ComparerType, + Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { + cmp := lhs.(traits.Comparer).Compare(rhs) + if cmp == types.IntNegOne || cmp == types.IntZero { + return types.True + } + if cmp == types.IntOne { + return types.False + } + return cmp + }}, + + // Greater than operator + {Operator: operators.Greater, + OperandTrait: traits.ComparerType, + Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { + cmp := lhs.(traits.Comparer).Compare(rhs) + if cmp == types.IntOne { + return types.True + } + if cmp == types.IntNegOne || cmp == types.IntZero { + return types.False + } + return cmp + }}, + + // Greater than equal operators + {Operator: operators.GreaterEquals, + OperandTrait: traits.ComparerType, + Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { + cmp := lhs.(traits.Comparer).Compare(rhs) + if cmp == types.IntOne || cmp == types.IntZero { + return types.True + } + if cmp == types.IntNegOne { + return types.False + } + return cmp + }}, + + // Add operator + {Operator: operators.Add, + OperandTrait: traits.AdderType, + Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { + return lhs.(traits.Adder).Add(rhs) + }}, + + // Subtract operators + {Operator: operators.Subtract, + OperandTrait: traits.SubtractorType, + Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { + return lhs.(traits.Subtractor).Subtract(rhs) + }}, + + // Multiply operator + {Operator: operators.Multiply, + OperandTrait: traits.MultiplierType, + Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { + return lhs.(traits.Multiplier).Multiply(rhs) + }}, + + // Divide operator + {Operator: operators.Divide, + OperandTrait: traits.DividerType, + Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { + return lhs.(traits.Divider).Divide(rhs) + }}, + + // Modulo operator + {Operator: operators.Modulo, + OperandTrait: traits.ModderType, + Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { + return lhs.(traits.Modder).Modulo(rhs) + }}, + + // Negate operator + {Operator: operators.Negate, + OperandTrait: traits.NegatorType, + Unary: func(value ref.Val) ref.Val { + if types.IsBool(value) { + return types.ValOrErr(value, "no such overload") + } + return value.(traits.Negater).Negate() + }}, + + // Index operator + {Operator: operators.Index, + OperandTrait: traits.IndexerType, + Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { + return lhs.(traits.Indexer).Get(rhs) + }}, + + // Size function + {Operator: overloads.Size, + OperandTrait: traits.SizerType, + Unary: func(value ref.Val) ref.Val { + return value.(traits.Sizer).Size() + }}, + + // In operator + {Operator: operators.In, Binary: inAggregate}, + // Deprecated: in operator, may be overridden in the environment. + {Operator: operators.OldIn, Binary: inAggregate}, + + // Matches function + {Operator: overloads.Matches, + OperandTrait: traits.MatcherType, + Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { + return lhs.(traits.Matcher).Match(rhs) + }}, + + // Type conversion functions + // TODO: verify type conversion safety of numeric values. + + // Int conversions. + {Operator: overloads.TypeConvertInt, + Unary: func(value ref.Val) ref.Val { + return value.ConvertToType(types.IntType) + }}, + + // Uint conversions. + {Operator: overloads.TypeConvertUint, + Unary: func(value ref.Val) ref.Val { + return value.ConvertToType(types.UintType) + }}, + + // Double conversions. + {Operator: overloads.TypeConvertDouble, + Unary: func(value ref.Val) ref.Val { + return value.ConvertToType(types.DoubleType) + }}, + + // Bool conversions. + {Operator: overloads.TypeConvertBool, + Unary: func(value ref.Val) ref.Val { + return value.ConvertToType(types.BoolType) + }}, + + // Bytes conversions. + {Operator: overloads.TypeConvertBytes, + Unary: func(value ref.Val) ref.Val { + return value.ConvertToType(types.BytesType) + }}, + + // String conversions. + {Operator: overloads.TypeConvertString, + Unary: func(value ref.Val) ref.Val { + return value.ConvertToType(types.StringType) + }}, + + // Timestamp conversions. + {Operator: overloads.TypeConvertTimestamp, + Unary: func(value ref.Val) ref.Val { + return value.ConvertToType(types.TimestampType) + }}, + + // Duration conversions. + {Operator: overloads.TypeConvertDuration, + Unary: func(value ref.Val) ref.Val { + return value.ConvertToType(types.DurationType) + }}, + + // Type operations. + {Operator: overloads.TypeConvertType, + Unary: func(value ref.Val) ref.Val { + return value.ConvertToType(types.TypeType) + }}, + + // Dyn conversion (identity function). + {Operator: overloads.TypeConvertDyn, + Unary: func(value ref.Val) ref.Val { + return value + }}, + + {Operator: overloads.Iterator, + OperandTrait: traits.IterableType, + Unary: func(value ref.Val) ref.Val { + return value.(traits.Iterable).Iterator() + }}, + + {Operator: overloads.HasNext, + OperandTrait: traits.IteratorType, + Unary: func(value ref.Val) ref.Val { + return value.(traits.Iterator).HasNext() + }}, + + {Operator: overloads.Next, + OperandTrait: traits.IteratorType, + Unary: func(value ref.Val) ref.Val { + return value.(traits.Iterator).Next() + }}, + } + +} + +func notStrictlyFalse(value ref.Val) ref.Val { + if types.IsBool(value) { + return value + } + return types.True +} + +func inAggregate(lhs ref.Val, rhs ref.Val) ref.Val { + if rhs.Type().HasTrait(traits.ContainerType) { + return rhs.(traits.Container).Contains(lhs) + } + return types.ValOrErr(rhs, "no such overload") +} diff --git a/vendor/github.com/google/cel-go/interpreter/interpretable.go b/vendor/github.com/google/cel-go/interpreter/interpretable.go new file mode 100644 index 0000000000..43111f6804 --- /dev/null +++ b/vendor/github.com/google/cel-go/interpreter/interpretable.go @@ -0,0 +1,1220 @@ +// Copyright 2019 Google LLC +// +// 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. + +package interpreter + +import ( + "math" + + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + "github.com/google/cel-go/interpreter/functions" +) + +// Interpretable can accept a given Activation and produce a value along with +// an accompanying EvalState which can be used to inspect whether additional +// data might be necessary to complete the evaluation. +type Interpretable interface { + // ID value corresponding to the expression node. + ID() int64 + + // Eval an Activation to produce an output. + Eval(activation Activation) ref.Val +} + +// InterpretableConst interface for tracking whether the Interpretable is a constant value. +type InterpretableConst interface { + Interpretable + + // Value returns the constant value of the instruction. + Value() ref.Val +} + +// InterpretableAttribute interface for tracking whether the Interpretable is an attribute. +type InterpretableAttribute interface { + Interpretable + + // Attr returns the Attribute value. + Attr() Attribute + + // Adapter returns the type adapter to be used for adapting resolved Attribute values. + Adapter() ref.TypeAdapter + + // AddQualifier proxies the Attribute.AddQualifier method. + // + // Note, this method may mutate the current attribute state. If the desire is to clone the + // Attribute, the Attribute should first be copied before adding the qualifier. Attributes + // are not copyable by default, so this is a capable that would need to be added to the + // AttributeFactory or specifically to the underlying Attribute implementation. + AddQualifier(Qualifier) (Attribute, error) + + // Qualify replicates the Attribute.Qualify method to permit extension and interception + // of object qualification. + Qualify(vars Activation, obj interface{}) (interface{}, error) + + // Resolve returns the value of the Attribute given the current Activation. + Resolve(Activation) (interface{}, error) +} + +// InterpretableCall interface for inspecting Interpretable instructions related to function calls. +type InterpretableCall interface { + Interpretable + + // Function returns the function name as it appears in text or mangled operator name as it + // appears in the operators.go file. + Function() string + + // OverloadID returns the overload id associated with the function specialization. + // Overload ids are stable across language boundaries and can be treated as synonymous with a + // unique function signature. + OverloadID() string + + // Args returns the normalized arguments to the function overload. + // For receiver-style functions, the receiver target is arg 0. + Args() []Interpretable +} + +// InterpretableConstructor interface for inspecting Interpretable instructions that initialize a list, map +// or struct. +type InterpretableConstructor interface { + Interpretable + + // InitVals returns all the list elements, map key and values or struct field values. + InitVals() []Interpretable + + // Type returns the type constructed. + Type() ref.Type +} + +// Core Interpretable implementations used during the program planning phase. + +type evalTestOnly struct { + id int64 + op Interpretable + field types.String + fieldType *ref.FieldType +} + +// ID implements the Interpretable interface method. +func (test *evalTestOnly) ID() int64 { + return test.id +} + +// Eval implements the Interpretable interface method. +func (test *evalTestOnly) Eval(ctx Activation) ref.Val { + // Handle field selection on a proto in the most efficient way possible. + if test.fieldType != nil { + opAttr, ok := test.op.(InterpretableAttribute) + if ok { + opVal, err := opAttr.Resolve(ctx) + if err != nil { + return types.NewErr(err.Error()) + } + refVal, ok := opVal.(ref.Val) + if ok { + opVal = refVal.Value() + } + if test.fieldType.IsSet(opVal) { + return types.True + } + return types.False + } + } + + obj := test.op.Eval(ctx) + tester, ok := obj.(traits.FieldTester) + if ok { + return tester.IsSet(test.field) + } + container, ok := obj.(traits.Container) + if ok { + return container.Contains(test.field) + } + return types.ValOrErr(obj, "invalid type for field selection.") +} + +// Cost provides the heuristic cost of a `has(field)` macro. The cost has at least 1 for determining +// if the field exists, apart from the cost of accessing the field. +func (test *evalTestOnly) Cost() (min, max int64) { + min, max = estimateCost(test.op) + min++ + max++ + return +} + +// NewConstValue creates a new constant valued Interpretable. +func NewConstValue(id int64, val ref.Val) InterpretableConst { + return &evalConst{ + id: id, + val: val, + } +} + +type evalConst struct { + id int64 + val ref.Val +} + +// ID implements the Interpretable interface method. +func (cons *evalConst) ID() int64 { + return cons.id +} + +// Eval implements the Interpretable interface method. +func (cons *evalConst) Eval(ctx Activation) ref.Val { + return cons.val +} + +// Cost returns zero for a constant valued Interpretable. +func (cons *evalConst) Cost() (min, max int64) { + return 0, 0 +} + +// Value implements the InterpretableConst interface method. +func (cons *evalConst) Value() ref.Val { + return cons.val +} + +type evalOr struct { + id int64 + lhs Interpretable + rhs Interpretable +} + +// ID implements the Interpretable interface method. +func (or *evalOr) ID() int64 { + return or.id +} + +// Eval implements the Interpretable interface method. +func (or *evalOr) Eval(ctx Activation) ref.Val { + // short-circuit lhs. + lVal := or.lhs.Eval(ctx) + lBool, lok := lVal.(types.Bool) + if lok && lBool == types.True { + return types.True + } + // short-circuit on rhs. + rVal := or.rhs.Eval(ctx) + rBool, rok := rVal.(types.Bool) + if rok && rBool == types.True { + return types.True + } + // return if both sides are bool false. + if lok && rok { + return types.False + } + // TODO: return both values as a set if both are unknown or error. + // prefer left unknown to right unknown. + if types.IsUnknown(lVal) { + return lVal + } + if types.IsUnknown(rVal) { + return rVal + } + // If the left-hand side is non-boolean return it as the error. + if types.IsError(lVal) { + return lVal + } + return types.ValOrErr(rVal, "no such overload") +} + +// Cost implements the Coster interface method. The minimum possible cost incurs when the left-hand +// side expr is sufficient in determining the evaluation result. +func (or *evalOr) Cost() (min, max int64) { + return calShortCircuitBinaryOpsCost(or.lhs, or.rhs) +} + +type evalAnd struct { + id int64 + lhs Interpretable + rhs Interpretable +} + +// ID implements the Interpretable interface method. +func (and *evalAnd) ID() int64 { + return and.id +} + +// Eval implements the Interpretable interface method. +func (and *evalAnd) Eval(ctx Activation) ref.Val { + // short-circuit lhs. + lVal := and.lhs.Eval(ctx) + lBool, lok := lVal.(types.Bool) + if lok && lBool == types.False { + return types.False + } + // short-circuit on rhs. + rVal := and.rhs.Eval(ctx) + rBool, rok := rVal.(types.Bool) + if rok && rBool == types.False { + return types.False + } + // return if both sides are bool true. + if lok && rok { + return types.True + } + // TODO: return both values as a set if both are unknown or error. + // prefer left unknown to right unknown. + if types.IsUnknown(lVal) { + return lVal + } + if types.IsUnknown(rVal) { + return rVal + } + // If the left-hand side is non-boolean return it as the error. + if types.IsError(lVal) { + return lVal + } + return types.ValOrErr(rVal, "no such overload") +} + +// Cost implements the Coster interface method. The minimum possible cost incurs when the left-hand +// side expr is sufficient in determining the evaluation result. +func (and *evalAnd) Cost() (min, max int64) { + return calShortCircuitBinaryOpsCost(and.lhs, and.rhs) +} + +func calShortCircuitBinaryOpsCost(lhs, rhs Interpretable) (min, max int64) { + lMin, lMax := estimateCost(lhs) + _, rMax := estimateCost(rhs) + return lMin, lMax + rMax + 1 +} + +type evalEq struct { + id int64 + lhs Interpretable + rhs Interpretable +} + +// ID implements the Interpretable interface method. +func (eq *evalEq) ID() int64 { + return eq.id +} + +// Eval implements the Interpretable interface method. +func (eq *evalEq) Eval(ctx Activation) ref.Val { + lVal := eq.lhs.Eval(ctx) + rVal := eq.rhs.Eval(ctx) + if types.IsUnknownOrError(lVal) { + return lVal + } + if types.IsUnknownOrError(rVal) { + return rVal + } + return types.Equal(lVal, rVal) +} + +// Cost implements the Coster interface method. +func (eq *evalEq) Cost() (min, max int64) { + return calExhaustiveBinaryOpsCost(eq.lhs, eq.rhs) +} + +// Function implements the InterpretableCall interface method. +func (*evalEq) Function() string { + return operators.Equals +} + +// OverloadID implements the InterpretableCall interface method. +func (*evalEq) OverloadID() string { + return overloads.Equals +} + +// Args implements the InterpretableCall interface method. +func (eq *evalEq) Args() []Interpretable { + return []Interpretable{eq.lhs, eq.rhs} +} + +type evalNe struct { + id int64 + lhs Interpretable + rhs Interpretable +} + +// ID implements the Interpretable interface method. +func (ne *evalNe) ID() int64 { + return ne.id +} + +// Eval implements the Interpretable interface method. +func (ne *evalNe) Eval(ctx Activation) ref.Val { + lVal := ne.lhs.Eval(ctx) + rVal := ne.rhs.Eval(ctx) + if types.IsUnknownOrError(lVal) { + return lVal + } + if types.IsUnknownOrError(rVal) { + return rVal + } + return types.Bool(types.Equal(lVal, rVal) != types.True) +} + +// Cost implements the Coster interface method. +func (ne *evalNe) Cost() (min, max int64) { + return calExhaustiveBinaryOpsCost(ne.lhs, ne.rhs) +} + +// Function implements the InterpretableCall interface method. +func (*evalNe) Function() string { + return operators.NotEquals +} + +// OverloadID implements the InterpretableCall interface method. +func (*evalNe) OverloadID() string { + return overloads.NotEquals +} + +// Args implements the InterpretableCall interface method. +func (ne *evalNe) Args() []Interpretable { + return []Interpretable{ne.lhs, ne.rhs} +} + +type evalZeroArity struct { + id int64 + function string + overload string + impl functions.FunctionOp +} + +// ID implements the Interpretable interface method. +func (zero *evalZeroArity) ID() int64 { + return zero.id +} + +// Eval implements the Interpretable interface method. +func (zero *evalZeroArity) Eval(ctx Activation) ref.Val { + return zero.impl() +} + +// Cost returns 1 representing the heuristic cost of the function. +func (zero *evalZeroArity) Cost() (min, max int64) { + return 1, 1 +} + +// Function implements the InterpretableCall interface method. +func (zero *evalZeroArity) Function() string { + return zero.function +} + +// OverloadID implements the InterpretableCall interface method. +func (zero *evalZeroArity) OverloadID() string { + return zero.overload +} + +// Args returns the argument to the unary function. +func (zero *evalZeroArity) Args() []Interpretable { + return []Interpretable{} +} + +type evalUnary struct { + id int64 + function string + overload string + arg Interpretable + trait int + impl functions.UnaryOp +} + +// ID implements the Interpretable interface method. +func (un *evalUnary) ID() int64 { + return un.id +} + +// Eval implements the Interpretable interface method. +func (un *evalUnary) Eval(ctx Activation) ref.Val { + argVal := un.arg.Eval(ctx) + // Early return if the argument to the function is unknown or error. + if types.IsUnknownOrError(argVal) { + return argVal + } + // If the implementation is bound and the argument value has the right traits required to + // invoke it, then call the implementation. + if un.impl != nil && (un.trait == 0 || argVal.Type().HasTrait(un.trait)) { + return un.impl(argVal) + } + // Otherwise, if the argument is a ReceiverType attempt to invoke the receiver method on the + // operand (arg0). + if argVal.Type().HasTrait(traits.ReceiverType) { + return argVal.(traits.Receiver).Receive(un.function, un.overload, []ref.Val{}) + } + return types.NewErr("no such overload: %s", un.function) +} + +// Cost implements the Coster interface method. +func (un *evalUnary) Cost() (min, max int64) { + min, max = estimateCost(un.arg) + min++ // add cost for function + max++ + return +} + +// Function implements the InterpretableCall interface method. +func (un *evalUnary) Function() string { + return un.function +} + +// OverloadID implements the InterpretableCall interface method. +func (un *evalUnary) OverloadID() string { + return un.overload +} + +// Args returns the argument to the unary function. +func (un *evalUnary) Args() []Interpretable { + return []Interpretable{un.arg} +} + +type evalBinary struct { + id int64 + function string + overload string + lhs Interpretable + rhs Interpretable + trait int + impl functions.BinaryOp +} + +// ID implements the Interpretable interface method. +func (bin *evalBinary) ID() int64 { + return bin.id +} + +// Eval implements the Interpretable interface method. +func (bin *evalBinary) Eval(ctx Activation) ref.Val { + lVal := bin.lhs.Eval(ctx) + rVal := bin.rhs.Eval(ctx) + // Early return if any argument to the function is unknown or error. + if types.IsUnknownOrError(lVal) { + return lVal + } + if types.IsUnknownOrError(rVal) { + return rVal + } + // If the implementation is bound and the argument value has the right traits required to + // invoke it, then call the implementation. + if bin.impl != nil && (bin.trait == 0 || lVal.Type().HasTrait(bin.trait)) { + return bin.impl(lVal, rVal) + } + // Otherwise, if the argument is a ReceiverType attempt to invoke the receiver method on the + // operand (arg0). + if lVal.Type().HasTrait(traits.ReceiverType) { + return lVal.(traits.Receiver).Receive(bin.function, bin.overload, []ref.Val{rVal}) + } + return types.NewErr("no such overload: %s", bin.function) +} + +// Cost implements the Coster interface method. +func (bin *evalBinary) Cost() (min, max int64) { + return calExhaustiveBinaryOpsCost(bin.lhs, bin.rhs) +} + +// Function implements the InterpretableCall interface method. +func (bin *evalBinary) Function() string { + return bin.function +} + +// OverloadID implements the InterpretableCall interface method. +func (bin *evalBinary) OverloadID() string { + return bin.overload +} + +// Args returns the argument to the unary function. +func (bin *evalBinary) Args() []Interpretable { + return []Interpretable{bin.lhs, bin.rhs} +} + +type evalVarArgs struct { + id int64 + function string + overload string + args []Interpretable + trait int + impl functions.FunctionOp +} + +// NewCall creates a new call Interpretable. +func NewCall(id int64, function, overload string, args []Interpretable, impl functions.FunctionOp) InterpretableCall { + return &evalVarArgs{ + id: id, + function: function, + overload: overload, + args: args, + impl: impl, + } +} + +// ID implements the Interpretable interface method. +func (fn *evalVarArgs) ID() int64 { + return fn.id +} + +// Eval implements the Interpretable interface method. +func (fn *evalVarArgs) Eval(ctx Activation) ref.Val { + argVals := make([]ref.Val, len(fn.args)) + // Early return if any argument to the function is unknown or error. + for i, arg := range fn.args { + argVals[i] = arg.Eval(ctx) + if types.IsUnknownOrError(argVals[i]) { + return argVals[i] + } + } + // If the implementation is bound and the argument value has the right traits required to + // invoke it, then call the implementation. + arg0 := argVals[0] + if fn.impl != nil && (fn.trait == 0 || arg0.Type().HasTrait(fn.trait)) { + return fn.impl(argVals...) + } + // Otherwise, if the argument is a ReceiverType attempt to invoke the receiver method on the + // operand (arg0). + if arg0.Type().HasTrait(traits.ReceiverType) { + return arg0.(traits.Receiver).Receive(fn.function, fn.overload, argVals[1:]) + } + return types.NewErr("no such overload: %s", fn.function) +} + +// Cost implements the Coster interface method. +func (fn *evalVarArgs) Cost() (min, max int64) { + min, max = sumOfCost(fn.args) + min++ // add cost for function + max++ + return +} + +// Function implements the InterpretableCall interface method. +func (fn *evalVarArgs) Function() string { + return fn.function +} + +// OverloadID implements the InterpretableCall interface method. +func (fn *evalVarArgs) OverloadID() string { + return fn.overload +} + +// Args returns the argument to the unary function. +func (fn *evalVarArgs) Args() []Interpretable { + return fn.args +} + +type evalList struct { + id int64 + elems []Interpretable + adapter ref.TypeAdapter +} + +// ID implements the Interpretable interface method. +func (l *evalList) ID() int64 { + return l.id +} + +// Eval implements the Interpretable interface method. +func (l *evalList) Eval(ctx Activation) ref.Val { + elemVals := make([]ref.Val, len(l.elems)) + // If any argument is unknown or error early terminate. + for i, elem := range l.elems { + elemVal := elem.Eval(ctx) + if types.IsUnknownOrError(elemVal) { + return elemVal + } + elemVals[i] = elemVal + } + return l.adapter.NativeToValue(elemVals) +} + +func (l *evalList) InitVals() []Interpretable { + return l.elems +} + +func (l *evalList) Type() ref.Type { + return types.ListType +} + +// Cost implements the Coster interface method. +func (l *evalList) Cost() (min, max int64) { + return sumOfCost(l.elems) +} + +type evalMap struct { + id int64 + keys []Interpretable + vals []Interpretable + adapter ref.TypeAdapter +} + +// ID implements the Interpretable interface method. +func (m *evalMap) ID() int64 { + return m.id +} + +// Eval implements the Interpretable interface method. +func (m *evalMap) Eval(ctx Activation) ref.Val { + entries := make(map[ref.Val]ref.Val) + // If any argument is unknown or error early terminate. + for i, key := range m.keys { + keyVal := key.Eval(ctx) + if types.IsUnknownOrError(keyVal) { + return keyVal + } + valVal := m.vals[i].Eval(ctx) + if types.IsUnknownOrError(valVal) { + return valVal + } + entries[keyVal] = valVal + } + return m.adapter.NativeToValue(entries) +} + +func (m *evalMap) InitVals() []Interpretable { + if len(m.keys) != len(m.vals) { + return nil + } + result := make([]Interpretable, len(m.keys)+len(m.vals)) + idx := 0 + for i, k := range m.keys { + v := m.vals[i] + result[idx] = k + idx++ + result[idx] = v + idx++ + } + return result +} + +func (m *evalMap) Type() ref.Type { + return types.MapType +} + +// Cost implements the Coster interface method. +func (m *evalMap) Cost() (min, max int64) { + kMin, kMax := sumOfCost(m.keys) + vMin, vMax := sumOfCost(m.vals) + return kMin + vMin, kMax + vMax +} + +type evalObj struct { + id int64 + typeName string + fields []string + vals []Interpretable + provider ref.TypeProvider +} + +// ID implements the Interpretable interface method. +func (o *evalObj) ID() int64 { + return o.id +} + +// Eval implements the Interpretable interface method. +func (o *evalObj) Eval(ctx Activation) ref.Val { + fieldVals := make(map[string]ref.Val) + // If any argument is unknown or error early terminate. + for i, field := range o.fields { + val := o.vals[i].Eval(ctx) + if types.IsUnknownOrError(val) { + return val + } + fieldVals[field] = val + } + return o.provider.NewValue(o.typeName, fieldVals) +} + +func (o *evalObj) InitVals() []Interpretable { + return o.vals +} + +func (o *evalObj) Type() ref.Type { + return types.NewObjectTypeValue(o.typeName) +} + +// Cost implements the Coster interface method. +func (o *evalObj) Cost() (min, max int64) { + return sumOfCost(o.vals) +} + +func sumOfCost(interps []Interpretable) (min, max int64) { + min, max = 0, 0 + for _, in := range interps { + minT, maxT := estimateCost(in) + min += minT + max += maxT + } + return +} + +type evalFold struct { + id int64 + accuVar string + iterVar string + iterRange Interpretable + accu Interpretable + cond Interpretable + step Interpretable + result Interpretable + adapter ref.TypeAdapter + exhaustive bool + interruptable bool +} + +// ID implements the Interpretable interface method. +func (fold *evalFold) ID() int64 { + return fold.id +} + +// Eval implements the Interpretable interface method. +func (fold *evalFold) Eval(ctx Activation) ref.Val { + foldRange := fold.iterRange.Eval(ctx) + if !foldRange.Type().HasTrait(traits.IterableType) { + return types.ValOrErr(foldRange, "got '%T', expected iterable type", foldRange) + } + // Configure the fold activation with the accumulator initial value. + accuCtx := varActivationPool.Get().(*varActivation) + accuCtx.parent = ctx + accuCtx.name = fold.accuVar + accuCtx.val = fold.accu.Eval(ctx) + // If the accumulator starts as an empty list, then the comprehension will build a list + // so create a mutable list to optimize the cost of the inner loop. + l, ok := accuCtx.val.(traits.Lister) + buildingList := false + if !fold.exhaustive && ok && l.Size() == types.IntZero { + buildingList = true + accuCtx.val = types.NewMutableList(fold.adapter) + } + iterCtx := varActivationPool.Get().(*varActivation) + iterCtx.parent = accuCtx + iterCtx.name = fold.iterVar + + interrupted := false + it := foldRange.(traits.Iterable).Iterator() + for it.HasNext() == types.True { + // Modify the iter var in the fold activation. + iterCtx.val = it.Next() + + // Evaluate the condition, terminate the loop if false. + cond := fold.cond.Eval(iterCtx) + condBool, ok := cond.(types.Bool) + if !fold.exhaustive && ok && condBool != types.True { + break + } + // Evaluate the evaluation step into accu var. + accuCtx.val = fold.step.Eval(iterCtx) + if fold.interruptable { + if stop, found := ctx.ResolveName("#interrupted"); found && stop == true { + interrupted = true + break + } + } + } + varActivationPool.Put(iterCtx) + if interrupted { + varActivationPool.Put(accuCtx) + return types.NewErr("operation interrupted") + } + + // Compute the result. + res := fold.result.Eval(accuCtx) + varActivationPool.Put(accuCtx) + // Convert a mutable list to an immutable one, if the comprehension has generated a list as a result. + if !types.IsUnknownOrError(res) && buildingList { + res = res.(traits.MutableLister).ToImmutableList() + } + return res +} + +// Cost implements the Coster interface method. +func (fold *evalFold) Cost() (min, max int64) { + // Compute the cost for evaluating iterRange. + iMin, iMax := estimateCost(fold.iterRange) + + // Compute the size of iterRange. If the size depends on the input, return the maximum possible + // cost range. + foldRange := fold.iterRange.Eval(EmptyActivation()) + if !foldRange.Type().HasTrait(traits.IterableType) { + return 0, math.MaxInt64 + } + var rangeCnt int64 + it := foldRange.(traits.Iterable).Iterator() + for it.HasNext() == types.True { + it.Next() + rangeCnt++ + } + aMin, aMax := estimateCost(fold.accu) + cMin, cMax := estimateCost(fold.cond) + sMin, sMax := estimateCost(fold.step) + rMin, rMax := estimateCost(fold.result) + if fold.exhaustive { + cMin = cMin * rangeCnt + sMin = sMin * rangeCnt + } + + // The cond and step costs are multiplied by size(iterRange). The minimum possible cost incurs + // when the evaluation result can be determined by the first iteration. + return iMin + aMin + cMin + sMin + rMin, + iMax + aMax + cMax*rangeCnt + sMax*rangeCnt + rMax +} + +// Optional Interpretable implementations that specialize, subsume, or extend the core evaluation +// plan via decorators. + +// evalSetMembership is an Interpretable implementation which tests whether an input value +// exists within the set of map keys used to model a set. +type evalSetMembership struct { + inst Interpretable + arg Interpretable + valueSet map[ref.Val]ref.Val +} + +// ID implements the Interpretable interface method. +func (e *evalSetMembership) ID() int64 { + return e.inst.ID() +} + +// Eval implements the Interpretable interface method. +func (e *evalSetMembership) Eval(ctx Activation) ref.Val { + val := e.arg.Eval(ctx) + if ret, found := e.valueSet[val]; found { + return ret + } + return types.False +} + +// Cost implements the Coster interface method. +func (e *evalSetMembership) Cost() (min, max int64) { + return estimateCost(e.arg) +} + +// evalWatch is an Interpretable implementation that wraps the execution of a given +// expression so that it may observe the computed value and send it to an observer. +type evalWatch struct { + Interpretable + observer EvalObserver +} + +// Eval implements the Interpretable interface method. +func (e *evalWatch) Eval(ctx Activation) ref.Val { + val := e.Interpretable.Eval(ctx) + e.observer(e.ID(), e.Interpretable, val) + return val +} + +// Cost implements the Coster interface method. +func (e *evalWatch) Cost() (min, max int64) { + return estimateCost(e.Interpretable) +} + +// evalWatchAttr describes a watcher of an instAttr Interpretable. +// +// Since the watcher may be selected against at a later stage in program planning, the watcher +// must implement the instAttr interface by proxy. +type evalWatchAttr struct { + InterpretableAttribute + observer EvalObserver +} + +// AddQualifier creates a wrapper over the incoming qualifier which observes the qualification +// result. +func (e *evalWatchAttr) AddQualifier(q Qualifier) (Attribute, error) { + cq, isConst := q.(ConstantQualifier) + if isConst { + q = &evalWatchConstQual{ + ConstantQualifier: cq, + observer: e.observer, + adapter: e.InterpretableAttribute.Adapter(), + } + } else { + q = &evalWatchQual{ + Qualifier: q, + observer: e.observer, + adapter: e.InterpretableAttribute.Adapter(), + } + } + _, err := e.InterpretableAttribute.AddQualifier(q) + return e, err +} + +// Cost implements the Coster interface method. +func (e *evalWatchAttr) Cost() (min, max int64) { + return estimateCost(e.InterpretableAttribute) +} + +// Eval implements the Interpretable interface method. +func (e *evalWatchAttr) Eval(vars Activation) ref.Val { + val := e.InterpretableAttribute.Eval(vars) + e.observer(e.ID(), e.InterpretableAttribute, val) + return val +} + +// evalWatchConstQual observes the qualification of an object using a constant boolean, int, +// string, or uint. +type evalWatchConstQual struct { + ConstantQualifier + observer EvalObserver + adapter ref.TypeAdapter +} + +// Cost implements the Coster interface method. +func (e *evalWatchConstQual) Cost() (min, max int64) { + return estimateCost(e.ConstantQualifier) +} + +// Qualify observes the qualification of a object via a constant boolean, int, string, or uint. +func (e *evalWatchConstQual) Qualify(vars Activation, obj interface{}) (interface{}, error) { + out, err := e.ConstantQualifier.Qualify(vars, obj) + var val ref.Val + if err != nil { + val = types.NewErr(err.Error()) + } else { + val = e.adapter.NativeToValue(out) + } + e.observer(e.ID(), e.ConstantQualifier, val) + return out, err +} + +// QualifierValueEquals tests whether the incoming value is equal to the qualifying constant. +func (e *evalWatchConstQual) QualifierValueEquals(value interface{}) bool { + qve, ok := e.ConstantQualifier.(qualifierValueEquator) + return ok && qve.QualifierValueEquals(value) +} + +// evalWatchQual observes the qualification of an object by a value computed at runtime. +type evalWatchQual struct { + Qualifier + observer EvalObserver + adapter ref.TypeAdapter +} + +// Cost implements the Coster interface method. +func (e *evalWatchQual) Cost() (min, max int64) { + return estimateCost(e.Qualifier) +} + +// Qualify observes the qualification of a object via a value computed at runtime. +func (e *evalWatchQual) Qualify(vars Activation, obj interface{}) (interface{}, error) { + out, err := e.Qualifier.Qualify(vars, obj) + var val ref.Val + if err != nil { + val = types.NewErr(err.Error()) + } else { + val = e.adapter.NativeToValue(out) + } + e.observer(e.ID(), e.Qualifier, val) + return out, err +} + +// evalWatchConst describes a watcher of an instConst Interpretable. +type evalWatchConst struct { + InterpretableConst + observer EvalObserver +} + +// Eval implements the Interpretable interface method. +func (e *evalWatchConst) Eval(vars Activation) ref.Val { + val := e.Value() + e.observer(e.ID(), e.InterpretableConst, val) + return val +} + +// Cost implements the Coster interface method. +func (e *evalWatchConst) Cost() (min, max int64) { + return estimateCost(e.InterpretableConst) +} + +// evalExhaustiveOr is just like evalOr, but does not short-circuit argument evaluation. +type evalExhaustiveOr struct { + id int64 + lhs Interpretable + rhs Interpretable +} + +// ID implements the Interpretable interface method. +func (or *evalExhaustiveOr) ID() int64 { + return or.id +} + +// Eval implements the Interpretable interface method. +func (or *evalExhaustiveOr) Eval(ctx Activation) ref.Val { + lVal := or.lhs.Eval(ctx) + rVal := or.rhs.Eval(ctx) + lBool, lok := lVal.(types.Bool) + if lok && lBool == types.True { + return types.True + } + rBool, rok := rVal.(types.Bool) + if rok && rBool == types.True { + return types.True + } + if lok && rok { + return types.False + } + if types.IsUnknown(lVal) { + return lVal + } + if types.IsUnknown(rVal) { + return rVal + } + // TODO: Combine the errors into a set in the future. + // If the left-hand side is non-boolean return it as the error. + if types.IsError(lVal) { + return lVal + } + return types.ValOrErr(rVal, "no such overload") +} + +// Cost implements the Coster interface method. +func (or *evalExhaustiveOr) Cost() (min, max int64) { + return calExhaustiveBinaryOpsCost(or.lhs, or.rhs) +} + +// evalExhaustiveAnd is just like evalAnd, but does not short-circuit argument evaluation. +type evalExhaustiveAnd struct { + id int64 + lhs Interpretable + rhs Interpretable +} + +// ID implements the Interpretable interface method. +func (and *evalExhaustiveAnd) ID() int64 { + return and.id +} + +// Eval implements the Interpretable interface method. +func (and *evalExhaustiveAnd) Eval(ctx Activation) ref.Val { + lVal := and.lhs.Eval(ctx) + rVal := and.rhs.Eval(ctx) + lBool, lok := lVal.(types.Bool) + if lok && lBool == types.False { + return types.False + } + rBool, rok := rVal.(types.Bool) + if rok && rBool == types.False { + return types.False + } + if lok && rok { + return types.True + } + if types.IsUnknown(lVal) { + return lVal + } + if types.IsUnknown(rVal) { + return rVal + } + // TODO: Combine the errors into a set in the future. + // If the left-hand side is non-boolean return it as the error. + if types.IsError(lVal) { + return lVal + } + return types.ValOrErr(rVal, "no such overload") +} + +// Cost implements the Coster interface method. +func (and *evalExhaustiveAnd) Cost() (min, max int64) { + return calExhaustiveBinaryOpsCost(and.lhs, and.rhs) +} + +func calExhaustiveBinaryOpsCost(lhs, rhs Interpretable) (min, max int64) { + lMin, lMax := estimateCost(lhs) + rMin, rMax := estimateCost(rhs) + return lMin + rMin + 1, lMax + rMax + 1 +} + +// evalExhaustiveConditional is like evalConditional, but does not short-circuit argument +// evaluation. +type evalExhaustiveConditional struct { + id int64 + adapter ref.TypeAdapter + attr *conditionalAttribute +} + +// ID implements the Interpretable interface method. +func (cond *evalExhaustiveConditional) ID() int64 { + return cond.id +} + +// Eval implements the Interpretable interface method. +func (cond *evalExhaustiveConditional) Eval(ctx Activation) ref.Val { + cVal := cond.attr.expr.Eval(ctx) + tVal, err := cond.attr.truthy.Resolve(ctx) + if err != nil { + return types.NewErr(err.Error()) + } + fVal, err := cond.attr.falsy.Resolve(ctx) + if err != nil { + return types.NewErr(err.Error()) + } + cBool, ok := cVal.(types.Bool) + if !ok { + return types.ValOrErr(cVal, "no such overload") + } + if cBool { + return cond.adapter.NativeToValue(tVal) + } + return cond.adapter.NativeToValue(fVal) +} + +// Cost implements the Coster interface method. +func (cond *evalExhaustiveConditional) Cost() (min, max int64) { + return cond.attr.Cost() +} + +// evalAttr evaluates an Attribute value. +type evalAttr struct { + adapter ref.TypeAdapter + attr Attribute +} + +// ID of the attribute instruction. +func (a *evalAttr) ID() int64 { + return a.attr.ID() +} + +// AddQualifier implements the instAttr interface method. +func (a *evalAttr) AddQualifier(qual Qualifier) (Attribute, error) { + attr, err := a.attr.AddQualifier(qual) + a.attr = attr + return attr, err +} + +// Attr implements the instAttr interface method. +func (a *evalAttr) Attr() Attribute { + return a.attr +} + +// Adapter implements the instAttr interface method. +func (a *evalAttr) Adapter() ref.TypeAdapter { + return a.adapter +} + +// Cost implements the Coster interface method. +func (a *evalAttr) Cost() (min, max int64) { + return estimateCost(a.attr) +} + +// Eval implements the Interpretable interface method. +func (a *evalAttr) Eval(ctx Activation) ref.Val { + v, err := a.attr.Resolve(ctx) + if err != nil { + return types.NewErr(err.Error()) + } + return a.adapter.NativeToValue(v) +} + +// Qualify proxies to the Attribute's Qualify method. +func (a *evalAttr) Qualify(ctx Activation, obj interface{}) (interface{}, error) { + return a.attr.Qualify(ctx, obj) +} + +// Resolve proxies to the Attribute's Resolve method. +func (a *evalAttr) Resolve(ctx Activation) (interface{}, error) { + return a.attr.Resolve(ctx) +} diff --git a/vendor/github.com/google/cel-go/interpreter/interpreter.go b/vendor/github.com/google/cel-go/interpreter/interpreter.go new file mode 100644 index 0000000000..b3fd14f8b3 --- /dev/null +++ b/vendor/github.com/google/cel-go/interpreter/interpreter.go @@ -0,0 +1,217 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Package interpreter provides functions to evaluate parsed expressions with +// the option to augment the evaluation with inputs and functions supplied at +// evaluation time. +package interpreter + +import ( + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/interpreter/functions" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +// Interpreter generates a new Interpretable from a checked or unchecked expression. +type Interpreter interface { + // NewInterpretable creates an Interpretable from a checked expression and an + // optional list of InterpretableDecorator values. + NewInterpretable(checked *exprpb.CheckedExpr, + decorators ...InterpretableDecorator) (Interpretable, error) + + // NewUncheckedInterpretable returns an Interpretable from a parsed expression + // and an optional list of InterpretableDecorator values. + NewUncheckedInterpretable(expr *exprpb.Expr, + decorators ...InterpretableDecorator) (Interpretable, error) +} + +// EvalObserver is a functional interface that accepts an expression id and an observed value. +// The id identifies the expression that was evaluated, the programStep is the Interpretable or Qualifier that +// was evaluated and value is the result of the evaluation. +type EvalObserver func(id int64, programStep interface{}, value ref.Val) + +// Observe constructs a decorator that calls all the provided observers in order after evaluating each Interpretable +// or Qualifier during program evaluation. +func Observe(observers ...EvalObserver) InterpretableDecorator { + if len(observers) == 1 { + return decObserveEval(observers[0]) + } + observeFn := func(id int64, programStep interface{}, val ref.Val) { + for _, observer := range observers { + observer(id, programStep, val) + } + } + return decObserveEval(observeFn) +} + +// EvalCancelledError represents a cancelled program evaluation operation. +type EvalCancelledError struct { + Message string + // Type identifies the cause of the cancellation. + Cause CancellationCause +} + +func (e EvalCancelledError) Error() string { + return e.Message +} + +// CancellationCause enumerates the ways a program evaluation operation can be cancelled. +type CancellationCause int + +const ( + // ContextCancelled indicates that the operation was cancelled in response to a Golang context cancellation. + ContextCancelled CancellationCause = iota + + // CostLimitExceeded indicates that the operation was cancelled in response to the actual cost limit being + // exceeded. + CostLimitExceeded +) + +// TODO: Replace all usages of TrackState with EvalStateObserver + +// TrackState decorates each expression node with an observer which records the value +// associated with the given expression id. EvalState must be provided to the decorator. +// This decorator is not thread-safe, and the EvalState must be reset between Eval() +// calls. +// DEPRECATED: Please use EvalStateObserver instead. It composes gracefully with additional observers. +func TrackState(state EvalState) InterpretableDecorator { + return Observe(EvalStateObserver(state)) +} + +// EvalStateObserver provides an observer which records the value +// associated with the given expression id. EvalState must be provided to the observer. +// This decorator is not thread-safe, and the EvalState must be reset between Eval() +// calls. +func EvalStateObserver(state EvalState) EvalObserver { + return func(id int64, programStep interface{}, val ref.Val) { + state.SetValue(id, val) + } +} + +// ExhaustiveEval replaces operations that short-circuit with versions that evaluate +// expressions and couples this behavior with the TrackState() decorator to provide +// insight into the evaluation state of the entire expression. EvalState must be +// provided to the decorator. This decorator is not thread-safe, and the EvalState +// must be reset between Eval() calls. +func ExhaustiveEval() InterpretableDecorator { + ex := decDisableShortcircuits() + return func(i Interpretable) (Interpretable, error) { + return ex(i) + } +} + +// InterruptableEval annotates comprehension loops with information that indicates they +// should check the `#interrupted` state within a custom Activation. +// +// The custom activation is currently managed higher up in the stack within the 'cel' package +// and should not require any custom support on behalf of callers. +func InterruptableEval() InterpretableDecorator { + return decInterruptFolds() +} + +// Optimize will pre-compute operations such as list and map construction and optimize +// call arguments to set membership tests. The set of optimizations will increase over time. +func Optimize() InterpretableDecorator { + return decOptimize() +} + +// RegexOptimization provides a way to replace an InterpretableCall for a regex function when the +// RegexIndex argument is a string constant. Typically, the Factory would compile the regex pattern at +// RegexIndex and report any errors (at program creation time) and then use the compiled regex for +// all regex function invocations. +type RegexOptimization struct { + // Function is the name of the function to optimize. + Function string + // OverloadID is the ID of the overload to optimize. + OverloadID string + // RegexIndex is the index position of the regex pattern argument. Only calls to the function where this argument is + // a string constant will be delegated to this optimizer. + RegexIndex int + // Factory constructs a replacement InterpretableCall node that optimizes the regex function call. Factory is + // provided with the unoptimized regex call and the string constant at the RegexIndex argument. + // The Factory may compile the regex for use across all invocations of the call, return any errors and + // return an interpreter.NewCall with the desired regex optimized function impl. + Factory func(call InterpretableCall, regexPattern string) (InterpretableCall, error) +} + +// CompileRegexConstants compiles regex pattern string constants at program creation time and reports any regex pattern +// compile errors. +func CompileRegexConstants(regexOptimizations ...*RegexOptimization) InterpretableDecorator { + return decRegexOptimizer(regexOptimizations...) +} + +type exprInterpreter struct { + dispatcher Dispatcher + container *containers.Container + provider ref.TypeProvider + adapter ref.TypeAdapter + attrFactory AttributeFactory +} + +// NewInterpreter builds an Interpreter from a Dispatcher and TypeProvider which will be used +// throughout the Eval of all Interpretable instances generated from it. +func NewInterpreter(dispatcher Dispatcher, + container *containers.Container, + provider ref.TypeProvider, + adapter ref.TypeAdapter, + attrFactory AttributeFactory) Interpreter { + return &exprInterpreter{ + dispatcher: dispatcher, + container: container, + provider: provider, + adapter: adapter, + attrFactory: attrFactory} +} + +// NewStandardInterpreter builds a Dispatcher and TypeProvider with support for all of the CEL +// builtins defined in the language definition. +func NewStandardInterpreter(container *containers.Container, + provider ref.TypeProvider, + adapter ref.TypeAdapter, + resolver AttributeFactory) Interpreter { + dispatcher := NewDispatcher() + dispatcher.Add(functions.StandardOverloads()...) + return NewInterpreter(dispatcher, container, provider, adapter, resolver) +} + +// NewIntepretable implements the Interpreter interface method. +func (i *exprInterpreter) NewInterpretable( + checked *exprpb.CheckedExpr, + decorators ...InterpretableDecorator) (Interpretable, error) { + p := newPlanner( + i.dispatcher, + i.provider, + i.adapter, + i.attrFactory, + i.container, + checked, + decorators...) + return p.Plan(checked.GetExpr()) +} + +// NewUncheckedIntepretable implements the Interpreter interface method. +func (i *exprInterpreter) NewUncheckedInterpretable( + expr *exprpb.Expr, + decorators ...InterpretableDecorator) (Interpretable, error) { + p := newUncheckedPlanner( + i.dispatcher, + i.provider, + i.adapter, + i.attrFactory, + i.container, + decorators...) + return p.Plan(expr) +} diff --git a/vendor/github.com/google/cel-go/interpreter/optimizations.go b/vendor/github.com/google/cel-go/interpreter/optimizations.go new file mode 100644 index 0000000000..2fc87e693b --- /dev/null +++ b/vendor/github.com/google/cel-go/interpreter/optimizations.go @@ -0,0 +1,46 @@ +// Copyright 2022 Google LLC +// +// 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. + +package interpreter + +import ( + "regexp" + + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" +) + +// MatchesRegexOptimization optimizes the 'matches' standard library function by compiling the regex pattern and +// reporting any compilation errors at program creation time, and using the compiled regex pattern for all function +// call invocations. +var MatchesRegexOptimization = &RegexOptimization{ + Function: "matches", + RegexIndex: 1, + Factory: func(call InterpretableCall, regexPattern string) (InterpretableCall, error) { + compiledRegex, err := regexp.Compile(regexPattern) + if err != nil { + return nil, err + } + return NewCall(call.ID(), call.Function(), call.OverloadID(), call.Args(), func(values ...ref.Val) ref.Val { + if len(values) != 2 { + return types.NoSuchOverloadErr() + } + in, ok := values[0].Value().(string) + if !ok { + return types.NoSuchOverloadErr() + } + return types.Bool(compiledRegex.MatchString(in)) + }), nil + }, +} diff --git a/vendor/github.com/google/cel-go/interpreter/planner.go b/vendor/github.com/google/cel-go/interpreter/planner.go new file mode 100644 index 0000000000..4a2d5b9d86 --- /dev/null +++ b/vendor/github.com/google/cel-go/interpreter/planner.go @@ -0,0 +1,774 @@ +// Copyright 2018 Google LLC +// +// 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. + +package interpreter + +import ( + "fmt" + "strings" + + "github.com/google/cel-go/common/containers" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/interpreter/functions" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +// interpretablePlanner creates an Interpretable evaluation plan from a proto Expr value. +type interpretablePlanner interface { + // Plan generates an Interpretable value (or error) from the input proto Expr. + Plan(expr *exprpb.Expr) (Interpretable, error) +} + +// newPlanner creates an interpretablePlanner which references a Dispatcher, TypeProvider, +// TypeAdapter, Container, and CheckedExpr value. These pieces of data are used to resolve +// functions, types, and namespaced identifiers at plan time rather than at runtime since +// it only needs to be done once and may be semi-expensive to compute. +func newPlanner(disp Dispatcher, + provider ref.TypeProvider, + adapter ref.TypeAdapter, + attrFactory AttributeFactory, + cont *containers.Container, + checked *exprpb.CheckedExpr, + decorators ...InterpretableDecorator) interpretablePlanner { + return &planner{ + disp: disp, + provider: provider, + adapter: adapter, + attrFactory: attrFactory, + container: cont, + refMap: checked.GetReferenceMap(), + typeMap: checked.GetTypeMap(), + decorators: decorators, + } +} + +// newUncheckedPlanner creates an interpretablePlanner which references a Dispatcher, TypeProvider, +// TypeAdapter, and Container to resolve functions and types at plan time. Namespaces present in +// Select expressions are resolved lazily at evaluation time. +func newUncheckedPlanner(disp Dispatcher, + provider ref.TypeProvider, + adapter ref.TypeAdapter, + attrFactory AttributeFactory, + cont *containers.Container, + decorators ...InterpretableDecorator) interpretablePlanner { + return &planner{ + disp: disp, + provider: provider, + adapter: adapter, + attrFactory: attrFactory, + container: cont, + refMap: make(map[int64]*exprpb.Reference), + typeMap: make(map[int64]*exprpb.Type), + decorators: decorators, + } +} + +// planner is an implementation of the interpretablePlanner interface. +type planner struct { + disp Dispatcher + provider ref.TypeProvider + adapter ref.TypeAdapter + attrFactory AttributeFactory + container *containers.Container + refMap map[int64]*exprpb.Reference + typeMap map[int64]*exprpb.Type + decorators []InterpretableDecorator +} + +// Plan implements the interpretablePlanner interface. This implementation of the Plan method also +// applies decorators to each Interpretable generated as part of the overall plan. Decorators are +// useful for layering functionality into the evaluation that is not natively understood by CEL, +// such as state-tracking, expression re-write, and possibly efficient thread-safe memoization of +// repeated expressions. +func (p *planner) Plan(expr *exprpb.Expr) (Interpretable, error) { + switch expr.ExprKind.(type) { + case *exprpb.Expr_CallExpr: + return p.decorate(p.planCall(expr)) + case *exprpb.Expr_IdentExpr: + return p.decorate(p.planIdent(expr)) + case *exprpb.Expr_SelectExpr: + return p.decorate(p.planSelect(expr)) + case *exprpb.Expr_ListExpr: + return p.decorate(p.planCreateList(expr)) + case *exprpb.Expr_StructExpr: + return p.decorate(p.planCreateStruct(expr)) + case *exprpb.Expr_ComprehensionExpr: + return p.decorate(p.planComprehension(expr)) + case *exprpb.Expr_ConstExpr: + return p.decorate(p.planConst(expr)) + } + return nil, fmt.Errorf("unsupported expr: %v", expr) +} + +// decorate applies the InterpretableDecorator functions to the given Interpretable. +// Both the Interpretable and error generated by a Plan step are accepted as arguments +// for convenience. +func (p *planner) decorate(i Interpretable, err error) (Interpretable, error) { + if err != nil { + return nil, err + } + for _, dec := range p.decorators { + i, err = dec(i) + if err != nil { + return nil, err + } + } + return i, nil +} + +// planIdent creates an Interpretable that resolves an identifier from an Activation. +func (p *planner) planIdent(expr *exprpb.Expr) (Interpretable, error) { + // Establish whether the identifier is in the reference map. + if identRef, found := p.refMap[expr.GetId()]; found { + return p.planCheckedIdent(expr.GetId(), identRef) + } + // Create the possible attribute list for the unresolved reference. + ident := expr.GetIdentExpr() + return &evalAttr{ + adapter: p.adapter, + attr: p.attrFactory.MaybeAttribute(expr.GetId(), ident.Name), + }, nil +} + +func (p *planner) planCheckedIdent(id int64, identRef *exprpb.Reference) (Interpretable, error) { + // Plan a constant reference if this is the case for this simple identifier. + if identRef.Value != nil { + return p.Plan(&exprpb.Expr{Id: id, + ExprKind: &exprpb.Expr_ConstExpr{ + ConstExpr: identRef.Value, + }}) + } + + // Check to see whether the type map indicates this is a type name. All types should be + // registered with the provider. + cType := p.typeMap[id] + if cType.GetType() != nil { + cVal, found := p.provider.FindIdent(identRef.Name) + if !found { + return nil, fmt.Errorf("reference to undefined type: %s", identRef.Name) + } + return NewConstValue(id, cVal), nil + } + + // Otherwise, return the attribute for the resolved identifier name. + return &evalAttr{ + adapter: p.adapter, + attr: p.attrFactory.AbsoluteAttribute(id, identRef.Name), + }, nil +} + +// planSelect creates an Interpretable with either: +// a) selects a field from a map or proto. +// b) creates a field presence test for a select within a has() macro. +// c) resolves the select expression to a namespaced identifier. +func (p *planner) planSelect(expr *exprpb.Expr) (Interpretable, error) { + // If the Select id appears in the reference map from the CheckedExpr proto then it is either + // a namespaced identifier or enum value. + if identRef, found := p.refMap[expr.GetId()]; found { + return p.planCheckedIdent(expr.GetId(), identRef) + } + + sel := expr.GetSelectExpr() + // Plan the operand evaluation. + op, err := p.Plan(sel.GetOperand()) + if err != nil { + return nil, err + } + + // Determine the field type if this is a proto message type. + var fieldType *ref.FieldType + opType := p.typeMap[sel.GetOperand().GetId()] + if opType.GetMessageType() != "" { + ft, found := p.provider.FindFieldType(opType.GetMessageType(), sel.Field) + if found && ft.IsSet != nil && ft.GetFrom != nil { + fieldType = ft + } + } + + // If the Select was marked TestOnly, this is a presence test. + // + // Note: presence tests are defined for structured (e.g. proto) and dynamic values (map, json) + // as follows: + // - True if the object field has a non-default value, e.g. obj.str != "" + // - True if the dynamic value has the field defined, e.g. key in map + // + // However, presence tests are not defined for qualified identifier names with primitive types. + // If a string named 'a.b.c' is declared in the environment and referenced within `has(a.b.c)`, + // it is not clear whether has should error or follow the convention defined for structured + // values. + if sel.TestOnly { + // Return the test only eval expression. + return &evalTestOnly{ + id: expr.Id, + field: types.String(sel.Field), + fieldType: fieldType, + op: op, + }, nil + } + // Build a qualifier. + qual, err := p.attrFactory.NewQualifier( + opType, expr.Id, sel.Field) + if err != nil { + return nil, err + } + // Lastly, create a field selection Interpretable. + attr, isAttr := op.(InterpretableAttribute) + if isAttr { + _, err = attr.AddQualifier(qual) + return attr, err + } + + relAttr, err := p.relativeAttr(op.ID(), op) + if err != nil { + return nil, err + } + _, err = relAttr.AddQualifier(qual) + if err != nil { + return nil, err + } + return relAttr, nil +} + +// planCall creates a callable Interpretable while specializing for common functions and invocation +// patterns. Specifically, conditional operators &&, ||, ?:, and (in)equality functions result in +// optimized Interpretable values. +func (p *planner) planCall(expr *exprpb.Expr) (Interpretable, error) { + call := expr.GetCallExpr() + target, fnName, oName := p.resolveFunction(expr) + argCount := len(call.GetArgs()) + var offset int + if target != nil { + argCount++ + offset++ + } + + args := make([]Interpretable, argCount) + if target != nil { + arg, err := p.Plan(target) + if err != nil { + return nil, err + } + args[0] = arg + } + for i, argExpr := range call.GetArgs() { + arg, err := p.Plan(argExpr) + if err != nil { + return nil, err + } + args[i+offset] = arg + } + + // Generate specialized Interpretable operators by function name if possible. + switch fnName { + case operators.LogicalAnd: + return p.planCallLogicalAnd(expr, args) + case operators.LogicalOr: + return p.planCallLogicalOr(expr, args) + case operators.Conditional: + return p.planCallConditional(expr, args) + case operators.Equals: + return p.planCallEqual(expr, args) + case operators.NotEquals: + return p.planCallNotEqual(expr, args) + case operators.Index: + return p.planCallIndex(expr, args) + } + + // Otherwise, generate Interpretable calls specialized by argument count. + // Try to find the specific function by overload id. + var fnDef *functions.Overload + if oName != "" { + fnDef, _ = p.disp.FindOverload(oName) + } + // If the overload id couldn't resolve the function, try the simple function name. + if fnDef == nil { + fnDef, _ = p.disp.FindOverload(fnName) + } + switch argCount { + case 0: + return p.planCallZero(expr, fnName, oName, fnDef) + case 1: + return p.planCallUnary(expr, fnName, oName, fnDef, args) + case 2: + return p.planCallBinary(expr, fnName, oName, fnDef, args) + default: + return p.planCallVarArgs(expr, fnName, oName, fnDef, args) + } +} + +// planCallZero generates a zero-arity callable Interpretable. +func (p *planner) planCallZero(expr *exprpb.Expr, + function string, + overload string, + impl *functions.Overload) (Interpretable, error) { + if impl == nil || impl.Function == nil { + return nil, fmt.Errorf("no such overload: %s()", function) + } + return &evalZeroArity{ + id: expr.Id, + function: function, + overload: overload, + impl: impl.Function, + }, nil +} + +// planCallUnary generates a unary callable Interpretable. +func (p *planner) planCallUnary(expr *exprpb.Expr, + function string, + overload string, + impl *functions.Overload, + args []Interpretable) (Interpretable, error) { + var fn functions.UnaryOp + var trait int + if impl != nil { + if impl.Unary == nil { + return nil, fmt.Errorf("no such overload: %s(arg)", function) + } + fn = impl.Unary + trait = impl.OperandTrait + } + return &evalUnary{ + id: expr.Id, + function: function, + overload: overload, + arg: args[0], + trait: trait, + impl: fn, + }, nil +} + +// planCallBinary generates a binary callable Interpretable. +func (p *planner) planCallBinary(expr *exprpb.Expr, + function string, + overload string, + impl *functions.Overload, + args []Interpretable) (Interpretable, error) { + var fn functions.BinaryOp + var trait int + if impl != nil { + if impl.Binary == nil { + return nil, fmt.Errorf("no such overload: %s(lhs, rhs)", function) + } + fn = impl.Binary + trait = impl.OperandTrait + } + return &evalBinary{ + id: expr.Id, + function: function, + overload: overload, + lhs: args[0], + rhs: args[1], + trait: trait, + impl: fn, + }, nil +} + +// planCallVarArgs generates a variable argument callable Interpretable. +func (p *planner) planCallVarArgs(expr *exprpb.Expr, + function string, + overload string, + impl *functions.Overload, + args []Interpretable) (Interpretable, error) { + var fn functions.FunctionOp + var trait int + if impl != nil { + if impl.Function == nil { + return nil, fmt.Errorf("no such overload: %s(...)", function) + } + fn = impl.Function + trait = impl.OperandTrait + } + return &evalVarArgs{ + id: expr.Id, + function: function, + overload: overload, + args: args, + trait: trait, + impl: fn, + }, nil +} + +// planCallEqual generates an equals (==) Interpretable. +func (p *planner) planCallEqual(expr *exprpb.Expr, + args []Interpretable) (Interpretable, error) { + return &evalEq{ + id: expr.Id, + lhs: args[0], + rhs: args[1], + }, nil +} + +// planCallNotEqual generates a not equals (!=) Interpretable. +func (p *planner) planCallNotEqual(expr *exprpb.Expr, + args []Interpretable) (Interpretable, error) { + return &evalNe{ + id: expr.Id, + lhs: args[0], + rhs: args[1], + }, nil +} + +// planCallLogicalAnd generates a logical and (&&) Interpretable. +func (p *planner) planCallLogicalAnd(expr *exprpb.Expr, + args []Interpretable) (Interpretable, error) { + return &evalAnd{ + id: expr.Id, + lhs: args[0], + rhs: args[1], + }, nil +} + +// planCallLogicalOr generates a logical or (||) Interpretable. +func (p *planner) planCallLogicalOr(expr *exprpb.Expr, + args []Interpretable) (Interpretable, error) { + return &evalOr{ + id: expr.Id, + lhs: args[0], + rhs: args[1], + }, nil +} + +// planCallConditional generates a conditional / ternary (c ? t : f) Interpretable. +func (p *planner) planCallConditional(expr *exprpb.Expr, + args []Interpretable) (Interpretable, error) { + cond := args[0] + + t := args[1] + var tAttr Attribute + truthyAttr, isTruthyAttr := t.(InterpretableAttribute) + if isTruthyAttr { + tAttr = truthyAttr.Attr() + } else { + tAttr = p.attrFactory.RelativeAttribute(t.ID(), t) + } + + f := args[2] + var fAttr Attribute + falsyAttr, isFalsyAttr := f.(InterpretableAttribute) + if isFalsyAttr { + fAttr = falsyAttr.Attr() + } else { + fAttr = p.attrFactory.RelativeAttribute(f.ID(), f) + } + + return &evalAttr{ + adapter: p.adapter, + attr: p.attrFactory.ConditionalAttribute(expr.Id, cond, tAttr, fAttr), + }, nil +} + +// planCallIndex either extends an attribute with the argument to the index operation, or creates +// a relative attribute based on the return of a function call or operation. +func (p *planner) planCallIndex(expr *exprpb.Expr, + args []Interpretable) (Interpretable, error) { + op := args[0] + ind := args[1] + opAttr, err := p.relativeAttr(op.ID(), op) + if err != nil { + return nil, err + } + opType := p.typeMap[expr.GetCallExpr().GetTarget().GetId()] + indConst, isIndConst := ind.(InterpretableConst) + if isIndConst { + qual, err := p.attrFactory.NewQualifier( + opType, expr.GetId(), indConst.Value()) + if err != nil { + return nil, err + } + _, err = opAttr.AddQualifier(qual) + return opAttr, err + } + indAttr, isIndAttr := ind.(InterpretableAttribute) + if isIndAttr { + qual, err := p.attrFactory.NewQualifier( + opType, expr.GetId(), indAttr) + if err != nil { + return nil, err + } + _, err = opAttr.AddQualifier(qual) + return opAttr, err + } + indQual, err := p.relativeAttr(expr.GetId(), ind) + if err != nil { + return nil, err + } + _, err = opAttr.AddQualifier(indQual) + return opAttr, err +} + +// planCreateList generates a list construction Interpretable. +func (p *planner) planCreateList(expr *exprpb.Expr) (Interpretable, error) { + list := expr.GetListExpr() + elems := make([]Interpretable, len(list.GetElements())) + for i, elem := range list.GetElements() { + elemVal, err := p.Plan(elem) + if err != nil { + return nil, err + } + elems[i] = elemVal + } + return &evalList{ + id: expr.Id, + elems: elems, + adapter: p.adapter, + }, nil +} + +// planCreateStruct generates a map or object construction Interpretable. +func (p *planner) planCreateStruct(expr *exprpb.Expr) (Interpretable, error) { + str := expr.GetStructExpr() + if len(str.MessageName) != 0 { + return p.planCreateObj(expr) + } + entries := str.GetEntries() + keys := make([]Interpretable, len(entries)) + vals := make([]Interpretable, len(entries)) + for i, entry := range entries { + keyVal, err := p.Plan(entry.GetMapKey()) + if err != nil { + return nil, err + } + keys[i] = keyVal + + valVal, err := p.Plan(entry.GetValue()) + if err != nil { + return nil, err + } + vals[i] = valVal + } + return &evalMap{ + id: expr.Id, + keys: keys, + vals: vals, + adapter: p.adapter, + }, nil +} + +// planCreateObj generates an object construction Interpretable. +func (p *planner) planCreateObj(expr *exprpb.Expr) (Interpretable, error) { + obj := expr.GetStructExpr() + typeName, defined := p.resolveTypeName(obj.MessageName) + if !defined { + return nil, fmt.Errorf("unknown type: %s", typeName) + } + entries := obj.GetEntries() + fields := make([]string, len(entries)) + vals := make([]Interpretable, len(entries)) + for i, entry := range entries { + fields[i] = entry.GetFieldKey() + val, err := p.Plan(entry.GetValue()) + if err != nil { + return nil, err + } + vals[i] = val + } + return &evalObj{ + id: expr.Id, + typeName: typeName, + fields: fields, + vals: vals, + provider: p.provider, + }, nil +} + +// planComprehension generates an Interpretable fold operation. +func (p *planner) planComprehension(expr *exprpb.Expr) (Interpretable, error) { + fold := expr.GetComprehensionExpr() + accu, err := p.Plan(fold.GetAccuInit()) + if err != nil { + return nil, err + } + iterRange, err := p.Plan(fold.GetIterRange()) + if err != nil { + return nil, err + } + cond, err := p.Plan(fold.GetLoopCondition()) + if err != nil { + return nil, err + } + step, err := p.Plan(fold.GetLoopStep()) + if err != nil { + return nil, err + } + result, err := p.Plan(fold.GetResult()) + if err != nil { + return nil, err + } + return &evalFold{ + id: expr.Id, + accuVar: fold.AccuVar, + accu: accu, + iterVar: fold.IterVar, + iterRange: iterRange, + cond: cond, + step: step, + result: result, + adapter: p.adapter, + }, nil +} + +// planConst generates a constant valued Interpretable. +func (p *planner) planConst(expr *exprpb.Expr) (Interpretable, error) { + val, err := p.constValue(expr.GetConstExpr()) + if err != nil { + return nil, err + } + return NewConstValue(expr.Id, val), nil +} + +// constValue converts a proto Constant value to a ref.Val. +func (p *planner) constValue(c *exprpb.Constant) (ref.Val, error) { + switch c.ConstantKind.(type) { + case *exprpb.Constant_BoolValue: + return p.adapter.NativeToValue(c.GetBoolValue()), nil + case *exprpb.Constant_BytesValue: + return p.adapter.NativeToValue(c.GetBytesValue()), nil + case *exprpb.Constant_DoubleValue: + return p.adapter.NativeToValue(c.GetDoubleValue()), nil + case *exprpb.Constant_DurationValue: + return p.adapter.NativeToValue(c.GetDurationValue().AsDuration()), nil + case *exprpb.Constant_Int64Value: + return p.adapter.NativeToValue(c.GetInt64Value()), nil + case *exprpb.Constant_NullValue: + return p.adapter.NativeToValue(c.GetNullValue()), nil + case *exprpb.Constant_StringValue: + return p.adapter.NativeToValue(c.GetStringValue()), nil + case *exprpb.Constant_TimestampValue: + return p.adapter.NativeToValue(c.GetTimestampValue().AsTime()), nil + case *exprpb.Constant_Uint64Value: + return p.adapter.NativeToValue(c.GetUint64Value()), nil + } + return nil, fmt.Errorf("unknown constant type: %v", c) +} + +// resolveTypeName takes a qualified string constructed at parse time, applies the proto +// namespace resolution rules to it in a scan over possible matching types in the TypeProvider. +func (p *planner) resolveTypeName(typeName string) (string, bool) { + for _, qualifiedTypeName := range p.container.ResolveCandidateNames(typeName) { + if _, found := p.provider.FindType(qualifiedTypeName); found { + return qualifiedTypeName, true + } + } + return "", false +} + +// resolveFunction determines the call target, function name, and overload name from a given Expr +// value. +// +// The resolveFunction resolves ambiguities where a function may either be a receiver-style +// invocation or a qualified global function name. +// - The target expression may only consist of ident and select expressions. +// - The function is declared in the environment using its fully-qualified name. +// - The fully-qualified function name matches the string serialized target value. +func (p *planner) resolveFunction(expr *exprpb.Expr) (*exprpb.Expr, string, string) { + // Note: similar logic exists within the `checker/checker.go`. If making changes here + // please consider the impact on checker.go and consolidate implementations or mirror code + // as appropriate. + call := expr.GetCallExpr() + target := call.GetTarget() + fnName := call.GetFunction() + + // Checked expressions always have a reference map entry, and _should_ have the fully qualified + // function name as the fnName value. + oRef, hasOverload := p.refMap[expr.GetId()] + if hasOverload { + if len(oRef.GetOverloadId()) == 1 { + return target, fnName, oRef.GetOverloadId()[0] + } + // Note, this namespaced function name will not appear as a fully qualified name in ASTs + // built and stored before cel-go v0.5.0; however, this functionality did not work at all + // before the v0.5.0 release. + return target, fnName, "" + } + + // Parse-only expressions need to handle the same logic as is normally performed at check time, + // but with potentially much less information. The only reliable source of information about + // which functions are configured is the dispatcher. + if target == nil { + // If the user has a parse-only expression, then it should have been configured as such in + // the interpreter dispatcher as it may have been omitted from the checker environment. + for _, qualifiedName := range p.container.ResolveCandidateNames(fnName) { + _, found := p.disp.FindOverload(qualifiedName) + if found { + return nil, qualifiedName, "" + } + } + // It's possible that the overload was not found, but this situation is accounted for in + // the planCall phase; however, the leading dot used for denoting fully-qualified + // namespaced identifiers must be stripped, as all declarations already use fully-qualified + // names. This stripping behavior is handled automatically by the ResolveCandidateNames + // call. + return target, stripLeadingDot(fnName), "" + } + + // Handle the situation where the function target actually indicates a qualified function name. + qualifiedPrefix, maybeQualified := p.toQualifiedName(target) + if maybeQualified { + maybeQualifiedName := qualifiedPrefix + "." + fnName + for _, qualifiedName := range p.container.ResolveCandidateNames(maybeQualifiedName) { + _, found := p.disp.FindOverload(qualifiedName) + if found { + // Clear the target to ensure the proper arity is used for finding the + // implementation. + return nil, qualifiedName, "" + } + } + } + // In the default case, the function is exactly as it was advertised: a receiver call on with + // an expression-based target with the given simple function name. + return target, fnName, "" +} + +func (p *planner) relativeAttr(id int64, eval Interpretable) (InterpretableAttribute, error) { + eAttr, ok := eval.(InterpretableAttribute) + if !ok { + eAttr = &evalAttr{ + adapter: p.adapter, + attr: p.attrFactory.RelativeAttribute(id, eval), + } + } + decAttr, err := p.decorate(eAttr, nil) + if err != nil { + return nil, err + } + eAttr, ok = decAttr.(InterpretableAttribute) + if !ok { + return nil, fmt.Errorf("invalid attribute decoration: %v(%T)", decAttr, decAttr) + } + return eAttr, nil +} + +// toQualifiedName converts an expression AST into a qualified name if possible, with a boolean +// 'found' value that indicates if the conversion is successful. +func (p *planner) toQualifiedName(operand *exprpb.Expr) (string, bool) { + // If the checker identified the expression as an attribute by the type-checker, then it can't + // possibly be part of qualified name in a namespace. + _, isAttr := p.refMap[operand.GetId()] + if isAttr { + return "", false + } + // Since functions cannot be both namespaced and receiver functions, if the operand is not an + // qualified variable name, return the (possibly) qualified name given the expressions. + return containers.ToQualifiedName(operand) +} + +func stripLeadingDot(name string) string { + if strings.HasPrefix(name, ".") { + return name[1:] + } + return name +} diff --git a/vendor/github.com/google/cel-go/interpreter/prune.go b/vendor/github.com/google/cel-go/interpreter/prune.go new file mode 100644 index 0000000000..0c01eb4f3a --- /dev/null +++ b/vendor/github.com/google/cel-go/interpreter/prune.go @@ -0,0 +1,391 @@ +// Copyright 2018 Google LLC +// +// 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. + +package interpreter + +import ( + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" + structpb "google.golang.org/protobuf/types/known/structpb" +) + +type astPruner struct { + expr *exprpb.Expr + state EvalState + nextExprID int64 +} + +// TODO Consider having a separate walk of the AST that finds common +// subexpressions. This can be called before or after constant folding to find +// common subexpressions. + +// PruneAst prunes the given AST based on the given EvalState and generates a new AST. +// Given AST is copied on write and a new AST is returned. +// Couple of typical use cases this interface would be: +// +// A) +// 1) Evaluate expr with some unknowns, +// 2) If result is unknown: +// a) PruneAst +// b) Goto 1 +// Functional call results which are known would be effectively cached across +// iterations. +// +// B) +// 1) Compile the expression (maybe via a service and maybe after checking a +// compiled expression does not exists in local cache) +// 2) Prepare the environment and the interpreter. Activation might be empty. +// 3) Eval the expression. This might return unknown or error or a concrete +// value. +// 4) PruneAst +// 4) Maybe cache the expression +// This is effectively constant folding the expression. How the environment is +// prepared in step 2 is flexible. For example, If the caller caches the +// compiled and constant folded expressions, but is not willing to constant +// fold(and thus cache results of) some external calls, then they can prepare +// the overloads accordingly. +func PruneAst(expr *exprpb.Expr, state EvalState) *exprpb.Expr { + pruner := &astPruner{ + expr: expr, + state: state, + nextExprID: 1} + newExpr, _ := pruner.prune(expr) + return newExpr +} + +func (p *astPruner) createLiteral(id int64, val *exprpb.Constant) *exprpb.Expr { + return &exprpb.Expr{ + Id: id, + ExprKind: &exprpb.Expr_ConstExpr{ + ConstExpr: val, + }, + } +} + +func (p *astPruner) maybeCreateLiteral(id int64, val ref.Val) (*exprpb.Expr, bool) { + switch val.Type() { + case types.BoolType: + return p.createLiteral(id, + &exprpb.Constant{ConstantKind: &exprpb.Constant_BoolValue{BoolValue: val.Value().(bool)}}), true + case types.IntType: + return p.createLiteral(id, + &exprpb.Constant{ConstantKind: &exprpb.Constant_Int64Value{Int64Value: val.Value().(int64)}}), true + case types.UintType: + return p.createLiteral(id, + &exprpb.Constant{ConstantKind: &exprpb.Constant_Uint64Value{Uint64Value: val.Value().(uint64)}}), true + case types.StringType: + return p.createLiteral(id, + &exprpb.Constant{ConstantKind: &exprpb.Constant_StringValue{StringValue: val.Value().(string)}}), true + case types.DoubleType: + return p.createLiteral(id, + &exprpb.Constant{ConstantKind: &exprpb.Constant_DoubleValue{DoubleValue: val.Value().(float64)}}), true + case types.BytesType: + return p.createLiteral(id, + &exprpb.Constant{ConstantKind: &exprpb.Constant_BytesValue{BytesValue: val.Value().([]byte)}}), true + case types.NullType: + return p.createLiteral(id, + &exprpb.Constant{ConstantKind: &exprpb.Constant_NullValue{NullValue: val.Value().(structpb.NullValue)}}), true + } + + // Attempt to build a list literal. + if list, isList := val.(traits.Lister); isList { + sz := list.Size().(types.Int) + elemExprs := make([]*exprpb.Expr, sz) + for i := types.Int(0); i < sz; i++ { + elem := list.Get(i) + if types.IsUnknownOrError(elem) { + return nil, false + } + elemExpr, ok := p.maybeCreateLiteral(p.nextID(), elem) + if !ok { + return nil, false + } + elemExprs[i] = elemExpr + } + return &exprpb.Expr{ + Id: id, + ExprKind: &exprpb.Expr_ListExpr{ + ListExpr: &exprpb.Expr_CreateList{ + Elements: elemExprs, + }, + }, + }, true + } + + // Create a map literal if possible. + if mp, isMap := val.(traits.Mapper); isMap { + it := mp.Iterator() + entries := make([]*exprpb.Expr_CreateStruct_Entry, mp.Size().(types.Int)) + i := 0 + for it.HasNext() != types.False { + key := it.Next() + val := mp.Get(key) + if types.IsUnknownOrError(key) || types.IsUnknownOrError(val) { + return nil, false + } + keyExpr, ok := p.maybeCreateLiteral(p.nextID(), key) + if !ok { + return nil, false + } + valExpr, ok := p.maybeCreateLiteral(p.nextID(), val) + if !ok { + return nil, false + } + entry := &exprpb.Expr_CreateStruct_Entry{ + Id: p.nextID(), + KeyKind: &exprpb.Expr_CreateStruct_Entry_MapKey{ + MapKey: keyExpr, + }, + Value: valExpr, + } + entries[i] = entry + i++ + } + return &exprpb.Expr{ + Id: id, + ExprKind: &exprpb.Expr_StructExpr{ + StructExpr: &exprpb.Expr_CreateStruct{ + Entries: entries, + }, + }, + }, true + } + + // TODO(issues/377) To construct message literals, the type provider will need to support + // the enumeration the fields for a given message. + return nil, false +} + +func (p *astPruner) maybePruneAndOr(node *exprpb.Expr) (*exprpb.Expr, bool) { + if !p.existsWithUnknownValue(node.GetId()) { + return nil, false + } + + call := node.GetCallExpr() + // We know result is unknown, so we have at least one unknown arg + // and if one side is a known value, we know we can ignore it. + if p.existsWithKnownValue(call.Args[0].GetId()) { + return call.Args[1], true + } + if p.existsWithKnownValue(call.Args[1].GetId()) { + return call.Args[0], true + } + return nil, false +} + +func (p *astPruner) maybePruneConditional(node *exprpb.Expr) (*exprpb.Expr, bool) { + if !p.existsWithUnknownValue(node.GetId()) { + return nil, false + } + + call := node.GetCallExpr() + condVal, condValueExists := p.value(call.Args[0].GetId()) + if !condValueExists || types.IsUnknownOrError(condVal) { + return nil, false + } + + if condVal.Value().(bool) { + return call.Args[1], true + } + return call.Args[2], true +} + +func (p *astPruner) maybePruneFunction(node *exprpb.Expr) (*exprpb.Expr, bool) { + call := node.GetCallExpr() + if call.Function == operators.LogicalOr || call.Function == operators.LogicalAnd { + return p.maybePruneAndOr(node) + } + if call.Function == operators.Conditional { + return p.maybePruneConditional(node) + } + + return nil, false +} + +func (p *astPruner) prune(node *exprpb.Expr) (*exprpb.Expr, bool) { + if node == nil { + return node, false + } + val, valueExists := p.value(node.GetId()) + if valueExists && !types.IsUnknownOrError(val) { + if newNode, ok := p.maybeCreateLiteral(node.GetId(), val); ok { + return newNode, true + } + } + + // We have either an unknown/error value, or something we don't want to + // transform, or expression was not evaluated. If possible, drill down + // more. + + switch node.ExprKind.(type) { + case *exprpb.Expr_SelectExpr: + if operand, pruned := p.prune(node.GetSelectExpr().Operand); pruned { + return &exprpb.Expr{ + Id: node.GetId(), + ExprKind: &exprpb.Expr_SelectExpr{ + SelectExpr: &exprpb.Expr_Select{ + Operand: operand, + Field: node.GetSelectExpr().GetField(), + TestOnly: node.GetSelectExpr().GetTestOnly(), + }, + }, + }, true + } + case *exprpb.Expr_CallExpr: + if newExpr, pruned := p.maybePruneFunction(node); pruned { + newExpr, _ = p.prune(newExpr) + return newExpr, true + } + var prunedCall bool + call := node.GetCallExpr() + args := call.GetArgs() + newArgs := make([]*exprpb.Expr, len(args)) + newCall := &exprpb.Expr_Call{ + Function: call.GetFunction(), + Target: call.GetTarget(), + Args: newArgs, + } + for i, arg := range args { + newArgs[i] = arg + if newArg, prunedArg := p.prune(arg); prunedArg { + prunedCall = true + newArgs[i] = newArg + } + } + if newTarget, prunedTarget := p.prune(call.GetTarget()); prunedTarget { + prunedCall = true + newCall.Target = newTarget + } + if prunedCall { + return &exprpb.Expr{ + Id: node.GetId(), + ExprKind: &exprpb.Expr_CallExpr{ + CallExpr: newCall, + }, + }, true + } + case *exprpb.Expr_ListExpr: + elems := node.GetListExpr().GetElements() + newElems := make([]*exprpb.Expr, len(elems)) + var prunedList bool + for i, elem := range elems { + newElems[i] = elem + if newElem, prunedElem := p.prune(elem); prunedElem { + newElems[i] = newElem + prunedList = true + } + } + if prunedList { + return &exprpb.Expr{ + Id: node.GetId(), + ExprKind: &exprpb.Expr_ListExpr{ + ListExpr: &exprpb.Expr_CreateList{ + Elements: newElems, + }, + }, + }, true + } + case *exprpb.Expr_StructExpr: + var prunedStruct bool + entries := node.GetStructExpr().GetEntries() + messageType := node.GetStructExpr().GetMessageName() + newEntries := make([]*exprpb.Expr_CreateStruct_Entry, len(entries)) + for i, entry := range entries { + newEntries[i] = entry + newKey, prunedKey := p.prune(entry.GetMapKey()) + newValue, prunedValue := p.prune(entry.GetValue()) + if !prunedKey && !prunedValue { + continue + } + prunedStruct = true + newEntry := &exprpb.Expr_CreateStruct_Entry{ + Value: newValue, + } + if messageType != "" { + newEntry.KeyKind = &exprpb.Expr_CreateStruct_Entry_FieldKey{ + FieldKey: entry.GetFieldKey(), + } + } else { + newEntry.KeyKind = &exprpb.Expr_CreateStruct_Entry_MapKey{ + MapKey: newKey, + } + } + newEntries[i] = newEntry + } + if prunedStruct { + return &exprpb.Expr{ + Id: node.GetId(), + ExprKind: &exprpb.Expr_StructExpr{ + StructExpr: &exprpb.Expr_CreateStruct{ + MessageName: messageType, + Entries: newEntries, + }, + }, + }, true + } + case *exprpb.Expr_ComprehensionExpr: + compre := node.GetComprehensionExpr() + // Only the range of the comprehension is pruned since the state tracking only records + // the last iteration of the comprehension and not each step in the evaluation which + // means that the any residuals computed in between might be inaccurate. + if newRange, pruned := p.prune(compre.GetIterRange()); pruned { + return &exprpb.Expr{ + Id: node.GetId(), + ExprKind: &exprpb.Expr_ComprehensionExpr{ + ComprehensionExpr: &exprpb.Expr_Comprehension{ + IterVar: compre.GetIterVar(), + IterRange: newRange, + AccuVar: compre.GetAccuVar(), + AccuInit: compre.GetAccuInit(), + LoopCondition: compre.GetLoopCondition(), + LoopStep: compre.GetLoopStep(), + Result: compre.GetResult(), + }, + }, + }, true + } + } + return node, false +} + +func (p *astPruner) value(id int64) (ref.Val, bool) { + val, found := p.state.Value(id) + return val, (found && val != nil) +} + +func (p *astPruner) existsWithUnknownValue(id int64) bool { + val, valueExists := p.value(id) + return valueExists && types.IsUnknown(val) +} + +func (p *astPruner) existsWithKnownValue(id int64) bool { + val, valueExists := p.value(id) + return valueExists && !types.IsUnknown(val) +} + +func (p *astPruner) nextID() int64 { + for { + _, found := p.state.Value(p.nextExprID) + if !found { + next := p.nextExprID + p.nextExprID++ + return next + } + p.nextExprID++ + } +} diff --git a/vendor/github.com/google/cel-go/interpreter/runtimecost.go b/vendor/github.com/google/cel-go/interpreter/runtimecost.go new file mode 100644 index 0000000000..06b6b27ef1 --- /dev/null +++ b/vendor/github.com/google/cel-go/interpreter/runtimecost.go @@ -0,0 +1,241 @@ +// Copyright 2022 Google LLC +// +// 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. + +package interpreter + +import ( + "math" + + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/overloads" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" +) + +// WARNING: Any changes to cost calculations in this file require a corresponding change in checker/cost.go + +// ActualCostEstimator provides function call cost estimations at runtime +// CallCost returns an estimated cost for the function overload invocation with the given args, or nil if it has no +// estimate to provide. CEL attempts to provide reasonable estimates for its standard function library, so CallCost +// should typically not need to provide an estimate for CELs standard function. +type ActualCostEstimator interface { + CallCost(function, overloadID string, args []ref.Val, result ref.Val) *uint64 +} + +// CostObserver provides an observer that tracks runtime cost. +func CostObserver(tracker *CostTracker) EvalObserver { + observer := func(id int64, programStep interface{}, val ref.Val) { + switch t := programStep.(type) { + case ConstantQualifier: + // TODO: Push identifiers on to the stack before observing constant qualifiers that apply to them + // and enable the below pop. Once enabled this can case can be collapsed into the Qualifier case. + tracker.cost++ + case InterpretableConst: + // zero cost + case InterpretableAttribute: + switch a := t.Attr().(type) { + case *conditionalAttribute: + // Ternary has no direct cost. All cost is from the conditional and the true/false branch expressions. + tracker.stack.drop(a.falsy.ID(), a.truthy.ID(), a.expr.ID()) + default: + tracker.stack.drop(t.Attr().ID()) + tracker.cost += common.SelectAndIdentCost + } + case *evalExhaustiveConditional: + // Ternary has no direct cost. All cost is from the conditional and the true/false branch expressions. + tracker.stack.drop(t.attr.falsy.ID(), t.attr.truthy.ID(), t.attr.expr.ID()) + + // While the field names are identical, the boolean operation eval structs do not share an interface and so + // must be handled individually. + case *evalOr: + tracker.stack.drop(t.rhs.ID(), t.lhs.ID()) + case *evalAnd: + tracker.stack.drop(t.rhs.ID(), t.lhs.ID()) + case *evalExhaustiveOr: + tracker.stack.drop(t.rhs.ID(), t.lhs.ID()) + case *evalExhaustiveAnd: + tracker.stack.drop(t.rhs.ID(), t.lhs.ID()) + case *evalFold: + tracker.stack.drop(t.iterRange.ID()) + case Qualifier: + tracker.cost++ + case InterpretableCall: + if argVals, ok := tracker.stack.dropArgs(t.Args()); ok { + tracker.cost += tracker.costCall(t, argVals, val) + } + case InterpretableConstructor: + tracker.stack.dropArgs(t.InitVals()) + switch t.Type() { + case types.ListType: + tracker.cost += common.ListCreateBaseCost + case types.MapType: + tracker.cost += common.MapCreateBaseCost + default: + tracker.cost += common.StructCreateBaseCost + } + } + tracker.stack.push(val, id) + + if tracker.Limit != nil && tracker.cost > *tracker.Limit { + panic(EvalCancelledError{Cause: CostLimitExceeded, Message: "operation cancelled: actual cost limit exceeded"}) + } + } + return observer +} + +// CostTracker represents the information needed for tacking runtime cost +type CostTracker struct { + Estimator ActualCostEstimator + Limit *uint64 + + cost uint64 + stack refValStack +} + +// ActualCost returns the runtime cost +func (c CostTracker) ActualCost() uint64 { + return c.cost +} + +func (c CostTracker) costCall(call InterpretableCall, argValues []ref.Val, result ref.Val) uint64 { + var cost uint64 + if c.Estimator != nil { + callCost := c.Estimator.CallCost(call.Function(), call.OverloadID(), argValues, result) + if callCost != nil { + cost += *callCost + return cost + } + } + // if user didn't specify, the default way of calculating runtime cost would be used. + // if user has their own implementation of ActualCostEstimator, make sure to cover the mapping between overloadId and cost calculation + switch call.OverloadID() { + // O(n) functions + case overloads.StartsWithString, overloads.EndsWithString, overloads.StringToBytes, overloads.BytesToString: + cost += uint64(math.Ceil(float64(c.actualSize(argValues[0])) * common.StringTraversalCostFactor)) + case overloads.InList: + // If a list is composed entirely of constant values this is O(1), but we don't account for that here. + // We just assume all list containment checks are O(n). + cost += c.actualSize(argValues[1]) + // O(min(m, n)) functions + case overloads.LessString, overloads.GreaterString, overloads.LessEqualsString, overloads.GreaterEqualsString, + overloads.LessBytes, overloads.GreaterBytes, overloads.LessEqualsBytes, overloads.GreaterEqualsBytes, + overloads.Equals, overloads.NotEquals: + // When we check the equality of 2 scalar values (e.g. 2 integers, 2 floating-point numbers, 2 booleans etc.), + // the CostTracker.actualSize() function by definition returns 1 for each operand, resulting in an overall cost + // of 1. + lhsSize := c.actualSize(argValues[0]) + rhsSize := c.actualSize(argValues[1]) + minSize := lhsSize + if rhsSize < minSize { + minSize = rhsSize + } + cost += uint64(math.Ceil(float64(minSize) * common.StringTraversalCostFactor)) + // O(m+n) functions + case overloads.AddString, overloads.AddBytes: + // In the worst case scenario, we would need to reallocate a new backing store and copy both operands over. + cost += uint64(math.Ceil(float64(c.actualSize(argValues[0])+c.actualSize(argValues[1])) * common.StringTraversalCostFactor)) + // O(nm) functions + case overloads.MatchesString: + // https://swtch.com/~rsc/regexp/regexp1.html applies to RE2 implementation supported by CEL + // Add one to string length for purposes of cost calculation to prevent product of string and regex to be 0 + // in case where string is empty but regex is still expensive. + strCost := uint64(math.Ceil((1.0 + float64(c.actualSize(argValues[0]))) * common.StringTraversalCostFactor)) + // We don't know how many expressions are in the regex, just the string length (a huge + // improvement here would be to somehow get a count the number of expressions in the regex or + // how many states are in the regex state machine and use that to measure regex cost). + // For now, we're making a guess that each expression in a regex is typically at least 4 chars + // in length. + regexCost := uint64(math.Ceil(float64(c.actualSize(argValues[1])) * common.RegexStringLengthCostFactor)) + cost += strCost * regexCost + case overloads.ContainsString: + strCost := uint64(math.Ceil(float64(c.actualSize(argValues[0])) * common.StringTraversalCostFactor)) + substrCost := uint64(math.Ceil(float64(c.actualSize(argValues[1])) * common.StringTraversalCostFactor)) + cost += strCost * substrCost + + default: + // The following operations are assumed to have O(1) complexity. + // - AddList due to the implementation. Index lookup can be O(c) the + // number of concatenated lists, but we don't track that is cost calculations. + // - Conversions, since none perform a traversal of a type of unbound length. + // - Computing the size of strings, byte sequences, lists and maps. + // - Logical operations and all operators on fixed width scalars (comparisons, equality) + // - Any functions that don't have a declared cost either here or in provided ActualCostEstimator. + cost++ + + } + return cost +} + +// actualSize returns the size of value +func (c CostTracker) actualSize(value ref.Val) uint64 { + if sz, ok := value.(traits.Sizer); ok { + return uint64(sz.Size().(types.Int)) + } + return 1 +} + +type stackVal struct { + Val ref.Val + ID int64 +} + +// refValStack keeps track of values of the stack for cost calculation purposes +type refValStack []stackVal + +func (s *refValStack) push(val ref.Val, id int64) { + value := stackVal{Val: val, ID: id} + *s = append(*s, value) +} + +// TODO: Allowing drop and dropArgs to remove stack items above the IDs they are provided is a workaround. drop and dropArgs +// should find and remove only the stack items matching the provided IDs once all attributes are properly pushed and popped from stack. + +// drop searches the stack for each ID and removes the ID and all stack items above it. +// If none of the IDs are found, the stack is not modified. +// WARNING: It is possible for multiple expressions with the same ID to exist (due to how macros are implemented) so it's +// possible that a dropped ID will remain on the stack. They should be removed when IDs on the stack are popped. +func (s *refValStack) drop(ids ...int64) { + for _, id := range ids { + for idx := len(*s) - 1; idx >= 0; idx-- { + if (*s)[idx].ID == id { + *s = (*s)[:idx] + break + } + } + } +} + +// dropArgs searches the stack for all the args by their IDs, accumulates their associated ref.Vals and drops any +// stack items above any of the arg IDs. If any of the IDs are not found the stack, false is returned. +// Args are assumed to be found in the stack in reverse order, i.e. the last arg is expected to be found highest in +// the stack. +// WARNING: It is possible for multiple expressions with the same ID to exist (due to how macros are implemented) so it's +// possible that a dropped ID will remain on the stack. They should be removed when IDs on the stack are popped. +func (s *refValStack) dropArgs(args []Interpretable) ([]ref.Val, bool) { + result := make([]ref.Val, len(args)) +argloop: + for nIdx := len(args) - 1; nIdx >= 0; nIdx-- { + for idx := len(*s) - 1; idx >= 0; idx-- { + if (*s)[idx].ID == args[nIdx].ID() { + el := (*s)[idx] + *s = (*s)[:idx] + result[nIdx] = el.Val + continue argloop + } + } + return nil, false + } + return result, true +} diff --git a/vendor/github.com/google/cel-go/parser/errors.go b/vendor/github.com/google/cel-go/parser/errors.go new file mode 100644 index 0000000000..ce49bb87f8 --- /dev/null +++ b/vendor/github.com/google/cel-go/parser/errors.go @@ -0,0 +1,30 @@ +// Copyright 2018 Google LLC +// +// 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. + +package parser + +import ( + "fmt" + + "github.com/google/cel-go/common" +) + +// parseErrors is a specialization of Errors. +type parseErrors struct { + *common.Errors +} + +func (e *parseErrors) syntaxError(l common.Location, message string) { + e.ReportError(l, fmt.Sprintf("Syntax error: %s", message)) +} diff --git a/vendor/github.com/google/cel-go/parser/gen/CEL.g4 b/vendor/github.com/google/cel-go/parser/gen/CEL.g4 new file mode 100644 index 0000000000..11145ec374 --- /dev/null +++ b/vendor/github.com/google/cel-go/parser/gen/CEL.g4 @@ -0,0 +1,186 @@ +// Copyright 2018 Google LLC +// +// 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. + +grammar CEL; + +// Grammar Rules +// ============= + +start + : e=expr EOF + ; + +expr + : e=conditionalOr (op='?' e1=conditionalOr ':' e2=expr)? + ; + +conditionalOr + : e=conditionalAnd (ops+='||' e1+=conditionalAnd)* + ; + +conditionalAnd + : e=relation (ops+='&&' e1+=relation)* + ; + +relation + : calc + | relation op=('<'|'<='|'>='|'>'|'=='|'!='|'in') relation + ; + +calc + : unary + | calc op=('*'|'/'|'%') calc + | calc op=('+'|'-') calc + ; + +unary + : member # MemberExpr + | (ops+='!')+ member # LogicalNot + | (ops+='-')+ member # Negate + ; + +member + : primary # PrimaryExpr + | member op='.' id=IDENTIFIER (open='(' args=exprList? ')')? # SelectOrCall + | member op='[' index=expr ']' # Index + | member op='{' entries=fieldInitializerList? ','? '}' # CreateMessage + ; + +primary + : leadingDot='.'? id=IDENTIFIER (op='(' args=exprList? ')')? # IdentOrGlobalCall + | '(' e=expr ')' # Nested + | op='[' elems=exprList? ','? ']' # CreateList + | op='{' entries=mapInitializerList? ','? '}' # CreateStruct + | literal # ConstantLiteral + ; + +exprList + : e+=expr (',' e+=expr)* + ; + +fieldInitializerList + : fields+=IDENTIFIER cols+=':' values+=expr (',' fields+=IDENTIFIER cols+=':' values+=expr)* + ; + +mapInitializerList + : keys+=expr cols+=':' values+=expr (',' keys+=expr cols+=':' values+=expr)* + ; + +literal + : sign=MINUS? tok=NUM_INT # Int + | tok=NUM_UINT # Uint + | sign=MINUS? tok=NUM_FLOAT # Double + | tok=STRING # String + | tok=BYTES # Bytes + | tok=CEL_TRUE # BoolTrue + | tok=CEL_FALSE # BoolFalse + | tok=NUL # Null + ; + +// Lexer Rules +// =========== + +EQUALS : '=='; +NOT_EQUALS : '!='; +IN: 'in'; +LESS : '<'; +LESS_EQUALS : '<='; +GREATER_EQUALS : '>='; +GREATER : '>'; +LOGICAL_AND : '&&'; +LOGICAL_OR : '||'; + +LBRACKET : '['; +RPRACKET : ']'; +LBRACE : '{'; +RBRACE : '}'; +LPAREN : '('; +RPAREN : ')'; +DOT : '.'; +COMMA : ','; +MINUS : '-'; +EXCLAM : '!'; +QUESTIONMARK : '?'; +COLON : ':'; +PLUS : '+'; +STAR : '*'; +SLASH : '/'; +PERCENT : '%'; +CEL_TRUE : 'true'; +CEL_FALSE : 'false'; +NUL : 'null'; + +fragment BACKSLASH : '\\'; +fragment LETTER : 'A'..'Z' | 'a'..'z' ; +fragment DIGIT : '0'..'9' ; +fragment EXPONENT : ('e' | 'E') ( '+' | '-' )? DIGIT+ ; +fragment HEXDIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ; +fragment RAW : 'r' | 'R'; + +fragment ESC_SEQ + : ESC_CHAR_SEQ + | ESC_BYTE_SEQ + | ESC_UNI_SEQ + | ESC_OCT_SEQ + ; + +fragment ESC_CHAR_SEQ + : BACKSLASH ('a'|'b'|'f'|'n'|'r'|'t'|'v'|'"'|'\''|'\\'|'?'|'`') + ; + +fragment ESC_OCT_SEQ + : BACKSLASH ('0'..'3') ('0'..'7') ('0'..'7') + ; + +fragment ESC_BYTE_SEQ + : BACKSLASH ( 'x' | 'X' ) HEXDIGIT HEXDIGIT + ; + +fragment ESC_UNI_SEQ + : BACKSLASH 'u' HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT + | BACKSLASH 'U' HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT + ; + +WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+ -> channel(HIDDEN) ; +COMMENT : '//' (~'\n')* -> channel(HIDDEN) ; + +NUM_FLOAT + : ( DIGIT+ ('.' DIGIT+) EXPONENT? + | DIGIT+ EXPONENT + | '.' DIGIT+ EXPONENT? + ) + ; + +NUM_INT + : ( DIGIT+ | '0x' HEXDIGIT+ ); + +NUM_UINT + : DIGIT+ ( 'u' | 'U' ) + | '0x' HEXDIGIT+ ( 'u' | 'U' ) + ; + +STRING + : '"' (ESC_SEQ | ~('\\'|'"'|'\n'|'\r'))* '"' + | '\'' (ESC_SEQ | ~('\\'|'\''|'\n'|'\r'))* '\'' + | '"""' (ESC_SEQ | ~('\\'))*? '"""' + | '\'\'\'' (ESC_SEQ | ~('\\'))*? '\'\'\'' + | RAW '"' ~('"'|'\n'|'\r')* '"' + | RAW '\'' ~('\''|'\n'|'\r')* '\'' + | RAW '"""' .*? '"""' + | RAW '\'\'\'' .*? '\'\'\'' + ; + +BYTES : ('b' | 'B') STRING; + +IDENTIFIER : (LETTER | '_') ( LETTER | DIGIT | '_')*; diff --git a/vendor/github.com/google/cel-go/parser/gen/CEL.tokens b/vendor/github.com/google/cel-go/parser/gen/CEL.tokens new file mode 100644 index 0000000000..b305bdad32 --- /dev/null +++ b/vendor/github.com/google/cel-go/parser/gen/CEL.tokens @@ -0,0 +1,64 @@ +EQUALS=1 +NOT_EQUALS=2 +IN=3 +LESS=4 +LESS_EQUALS=5 +GREATER_EQUALS=6 +GREATER=7 +LOGICAL_AND=8 +LOGICAL_OR=9 +LBRACKET=10 +RPRACKET=11 +LBRACE=12 +RBRACE=13 +LPAREN=14 +RPAREN=15 +DOT=16 +COMMA=17 +MINUS=18 +EXCLAM=19 +QUESTIONMARK=20 +COLON=21 +PLUS=22 +STAR=23 +SLASH=24 +PERCENT=25 +CEL_TRUE=26 +CEL_FALSE=27 +NUL=28 +WHITESPACE=29 +COMMENT=30 +NUM_FLOAT=31 +NUM_INT=32 +NUM_UINT=33 +STRING=34 +BYTES=35 +IDENTIFIER=36 +'=='=1 +'!='=2 +'in'=3 +'<'=4 +'<='=5 +'>='=6 +'>'=7 +'&&'=8 +'||'=9 +'['=10 +']'=11 +'{'=12 +'}'=13 +'('=14 +')'=15 +'.'=16 +','=17 +'-'=18 +'!'=19 +'?'=20 +':'=21 +'+'=22 +'*'=23 +'/'=24 +'%'=25 +'true'=26 +'false'=27 +'null'=28 diff --git a/vendor/github.com/google/cel-go/parser/gen/CELLexer.tokens b/vendor/github.com/google/cel-go/parser/gen/CELLexer.tokens new file mode 100644 index 0000000000..b305bdad32 --- /dev/null +++ b/vendor/github.com/google/cel-go/parser/gen/CELLexer.tokens @@ -0,0 +1,64 @@ +EQUALS=1 +NOT_EQUALS=2 +IN=3 +LESS=4 +LESS_EQUALS=5 +GREATER_EQUALS=6 +GREATER=7 +LOGICAL_AND=8 +LOGICAL_OR=9 +LBRACKET=10 +RPRACKET=11 +LBRACE=12 +RBRACE=13 +LPAREN=14 +RPAREN=15 +DOT=16 +COMMA=17 +MINUS=18 +EXCLAM=19 +QUESTIONMARK=20 +COLON=21 +PLUS=22 +STAR=23 +SLASH=24 +PERCENT=25 +CEL_TRUE=26 +CEL_FALSE=27 +NUL=28 +WHITESPACE=29 +COMMENT=30 +NUM_FLOAT=31 +NUM_INT=32 +NUM_UINT=33 +STRING=34 +BYTES=35 +IDENTIFIER=36 +'=='=1 +'!='=2 +'in'=3 +'<'=4 +'<='=5 +'>='=6 +'>'=7 +'&&'=8 +'||'=9 +'['=10 +']'=11 +'{'=12 +'}'=13 +'('=14 +')'=15 +'.'=16 +','=17 +'-'=18 +'!'=19 +'?'=20 +':'=21 +'+'=22 +'*'=23 +'/'=24 +'%'=25 +'true'=26 +'false'=27 +'null'=28 diff --git a/vendor/github.com/google/cel-go/parser/gen/cel_base_listener.go b/vendor/github.com/google/cel-go/parser/gen/cel_base_listener.go new file mode 100644 index 0000000000..a3315b8420 --- /dev/null +++ b/vendor/github.com/google/cel-go/parser/gen/cel_base_listener.go @@ -0,0 +1,195 @@ +// Code generated from /Users/tswadell/go/src/github.com/google/cel-go/bin/../parser/gen/CEL.g4 by ANTLR 4.9.1. DO NOT EDIT. + +package gen // CEL +import "github.com/antlr/antlr4/runtime/Go/antlr" + +// BaseCELListener is a complete listener for a parse tree produced by CELParser. +type BaseCELListener struct{} + +var _ CELListener = &BaseCELListener{} + +// VisitTerminal is called when a terminal node is visited. +func (s *BaseCELListener) VisitTerminal(node antlr.TerminalNode) {} + +// VisitErrorNode is called when an error node is visited. +func (s *BaseCELListener) VisitErrorNode(node antlr.ErrorNode) {} + +// EnterEveryRule is called when any rule is entered. +func (s *BaseCELListener) EnterEveryRule(ctx antlr.ParserRuleContext) {} + +// ExitEveryRule is called when any rule is exited. +func (s *BaseCELListener) ExitEveryRule(ctx antlr.ParserRuleContext) {} + +// EnterStart is called when production start is entered. +func (s *BaseCELListener) EnterStart(ctx *StartContext) {} + +// ExitStart is called when production start is exited. +func (s *BaseCELListener) ExitStart(ctx *StartContext) {} + +// EnterExpr is called when production expr is entered. +func (s *BaseCELListener) EnterExpr(ctx *ExprContext) {} + +// ExitExpr is called when production expr is exited. +func (s *BaseCELListener) ExitExpr(ctx *ExprContext) {} + +// EnterConditionalOr is called when production conditionalOr is entered. +func (s *BaseCELListener) EnterConditionalOr(ctx *ConditionalOrContext) {} + +// ExitConditionalOr is called when production conditionalOr is exited. +func (s *BaseCELListener) ExitConditionalOr(ctx *ConditionalOrContext) {} + +// EnterConditionalAnd is called when production conditionalAnd is entered. +func (s *BaseCELListener) EnterConditionalAnd(ctx *ConditionalAndContext) {} + +// ExitConditionalAnd is called when production conditionalAnd is exited. +func (s *BaseCELListener) ExitConditionalAnd(ctx *ConditionalAndContext) {} + +// EnterRelation is called when production relation is entered. +func (s *BaseCELListener) EnterRelation(ctx *RelationContext) {} + +// ExitRelation is called when production relation is exited. +func (s *BaseCELListener) ExitRelation(ctx *RelationContext) {} + +// EnterCalc is called when production calc is entered. +func (s *BaseCELListener) EnterCalc(ctx *CalcContext) {} + +// ExitCalc is called when production calc is exited. +func (s *BaseCELListener) ExitCalc(ctx *CalcContext) {} + +// EnterMemberExpr is called when production MemberExpr is entered. +func (s *BaseCELListener) EnterMemberExpr(ctx *MemberExprContext) {} + +// ExitMemberExpr is called when production MemberExpr is exited. +func (s *BaseCELListener) ExitMemberExpr(ctx *MemberExprContext) {} + +// EnterLogicalNot is called when production LogicalNot is entered. +func (s *BaseCELListener) EnterLogicalNot(ctx *LogicalNotContext) {} + +// ExitLogicalNot is called when production LogicalNot is exited. +func (s *BaseCELListener) ExitLogicalNot(ctx *LogicalNotContext) {} + +// EnterNegate is called when production Negate is entered. +func (s *BaseCELListener) EnterNegate(ctx *NegateContext) {} + +// ExitNegate is called when production Negate is exited. +func (s *BaseCELListener) ExitNegate(ctx *NegateContext) {} + +// EnterSelectOrCall is called when production SelectOrCall is entered. +func (s *BaseCELListener) EnterSelectOrCall(ctx *SelectOrCallContext) {} + +// ExitSelectOrCall is called when production SelectOrCall is exited. +func (s *BaseCELListener) ExitSelectOrCall(ctx *SelectOrCallContext) {} + +// EnterPrimaryExpr is called when production PrimaryExpr is entered. +func (s *BaseCELListener) EnterPrimaryExpr(ctx *PrimaryExprContext) {} + +// ExitPrimaryExpr is called when production PrimaryExpr is exited. +func (s *BaseCELListener) ExitPrimaryExpr(ctx *PrimaryExprContext) {} + +// EnterIndex is called when production Index is entered. +func (s *BaseCELListener) EnterIndex(ctx *IndexContext) {} + +// ExitIndex is called when production Index is exited. +func (s *BaseCELListener) ExitIndex(ctx *IndexContext) {} + +// EnterCreateMessage is called when production CreateMessage is entered. +func (s *BaseCELListener) EnterCreateMessage(ctx *CreateMessageContext) {} + +// ExitCreateMessage is called when production CreateMessage is exited. +func (s *BaseCELListener) ExitCreateMessage(ctx *CreateMessageContext) {} + +// EnterIdentOrGlobalCall is called when production IdentOrGlobalCall is entered. +func (s *BaseCELListener) EnterIdentOrGlobalCall(ctx *IdentOrGlobalCallContext) {} + +// ExitIdentOrGlobalCall is called when production IdentOrGlobalCall is exited. +func (s *BaseCELListener) ExitIdentOrGlobalCall(ctx *IdentOrGlobalCallContext) {} + +// EnterNested is called when production Nested is entered. +func (s *BaseCELListener) EnterNested(ctx *NestedContext) {} + +// ExitNested is called when production Nested is exited. +func (s *BaseCELListener) ExitNested(ctx *NestedContext) {} + +// EnterCreateList is called when production CreateList is entered. +func (s *BaseCELListener) EnterCreateList(ctx *CreateListContext) {} + +// ExitCreateList is called when production CreateList is exited. +func (s *BaseCELListener) ExitCreateList(ctx *CreateListContext) {} + +// EnterCreateStruct is called when production CreateStruct is entered. +func (s *BaseCELListener) EnterCreateStruct(ctx *CreateStructContext) {} + +// ExitCreateStruct is called when production CreateStruct is exited. +func (s *BaseCELListener) ExitCreateStruct(ctx *CreateStructContext) {} + +// EnterConstantLiteral is called when production ConstantLiteral is entered. +func (s *BaseCELListener) EnterConstantLiteral(ctx *ConstantLiteralContext) {} + +// ExitConstantLiteral is called when production ConstantLiteral is exited. +func (s *BaseCELListener) ExitConstantLiteral(ctx *ConstantLiteralContext) {} + +// EnterExprList is called when production exprList is entered. +func (s *BaseCELListener) EnterExprList(ctx *ExprListContext) {} + +// ExitExprList is called when production exprList is exited. +func (s *BaseCELListener) ExitExprList(ctx *ExprListContext) {} + +// EnterFieldInitializerList is called when production fieldInitializerList is entered. +func (s *BaseCELListener) EnterFieldInitializerList(ctx *FieldInitializerListContext) {} + +// ExitFieldInitializerList is called when production fieldInitializerList is exited. +func (s *BaseCELListener) ExitFieldInitializerList(ctx *FieldInitializerListContext) {} + +// EnterMapInitializerList is called when production mapInitializerList is entered. +func (s *BaseCELListener) EnterMapInitializerList(ctx *MapInitializerListContext) {} + +// ExitMapInitializerList is called when production mapInitializerList is exited. +func (s *BaseCELListener) ExitMapInitializerList(ctx *MapInitializerListContext) {} + +// EnterInt is called when production Int is entered. +func (s *BaseCELListener) EnterInt(ctx *IntContext) {} + +// ExitInt is called when production Int is exited. +func (s *BaseCELListener) ExitInt(ctx *IntContext) {} + +// EnterUint is called when production Uint is entered. +func (s *BaseCELListener) EnterUint(ctx *UintContext) {} + +// ExitUint is called when production Uint is exited. +func (s *BaseCELListener) ExitUint(ctx *UintContext) {} + +// EnterDouble is called when production Double is entered. +func (s *BaseCELListener) EnterDouble(ctx *DoubleContext) {} + +// ExitDouble is called when production Double is exited. +func (s *BaseCELListener) ExitDouble(ctx *DoubleContext) {} + +// EnterString is called when production String is entered. +func (s *BaseCELListener) EnterString(ctx *StringContext) {} + +// ExitString is called when production String is exited. +func (s *BaseCELListener) ExitString(ctx *StringContext) {} + +// EnterBytes is called when production Bytes is entered. +func (s *BaseCELListener) EnterBytes(ctx *BytesContext) {} + +// ExitBytes is called when production Bytes is exited. +func (s *BaseCELListener) ExitBytes(ctx *BytesContext) {} + +// EnterBoolTrue is called when production BoolTrue is entered. +func (s *BaseCELListener) EnterBoolTrue(ctx *BoolTrueContext) {} + +// ExitBoolTrue is called when production BoolTrue is exited. +func (s *BaseCELListener) ExitBoolTrue(ctx *BoolTrueContext) {} + +// EnterBoolFalse is called when production BoolFalse is entered. +func (s *BaseCELListener) EnterBoolFalse(ctx *BoolFalseContext) {} + +// ExitBoolFalse is called when production BoolFalse is exited. +func (s *BaseCELListener) ExitBoolFalse(ctx *BoolFalseContext) {} + +// EnterNull is called when production Null is entered. +func (s *BaseCELListener) EnterNull(ctx *NullContext) {} + +// ExitNull is called when production Null is exited. +func (s *BaseCELListener) ExitNull(ctx *NullContext) {} diff --git a/vendor/github.com/google/cel-go/parser/gen/cel_base_visitor.go b/vendor/github.com/google/cel-go/parser/gen/cel_base_visitor.go new file mode 100644 index 0000000000..daf7f807d7 --- /dev/null +++ b/vendor/github.com/google/cel-go/parser/gen/cel_base_visitor.go @@ -0,0 +1,124 @@ +// Code generated from /Users/tswadell/go/src/github.com/google/cel-go/bin/../parser/gen/CEL.g4 by ANTLR 4.9.1. DO NOT EDIT. + +package gen // CEL +import "github.com/antlr/antlr4/runtime/Go/antlr" + +type BaseCELVisitor struct { + *antlr.BaseParseTreeVisitor +} + +func (v *BaseCELVisitor) VisitStart(ctx *StartContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitExpr(ctx *ExprContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitConditionalOr(ctx *ConditionalOrContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitConditionalAnd(ctx *ConditionalAndContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitRelation(ctx *RelationContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitCalc(ctx *CalcContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitMemberExpr(ctx *MemberExprContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitLogicalNot(ctx *LogicalNotContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitNegate(ctx *NegateContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitSelectOrCall(ctx *SelectOrCallContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitPrimaryExpr(ctx *PrimaryExprContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitIndex(ctx *IndexContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitCreateMessage(ctx *CreateMessageContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitIdentOrGlobalCall(ctx *IdentOrGlobalCallContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitNested(ctx *NestedContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitCreateList(ctx *CreateListContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitCreateStruct(ctx *CreateStructContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitConstantLiteral(ctx *ConstantLiteralContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitExprList(ctx *ExprListContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitFieldInitializerList(ctx *FieldInitializerListContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitMapInitializerList(ctx *MapInitializerListContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitInt(ctx *IntContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitUint(ctx *UintContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitDouble(ctx *DoubleContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitString(ctx *StringContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitBytes(ctx *BytesContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitBoolTrue(ctx *BoolTrueContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitBoolFalse(ctx *BoolFalseContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseCELVisitor) VisitNull(ctx *NullContext) interface{} { + return v.VisitChildren(ctx) +} diff --git a/vendor/github.com/google/cel-go/parser/gen/cel_lexer.go b/vendor/github.com/google/cel-go/parser/gen/cel_lexer.go new file mode 100644 index 0000000000..4af96aba58 --- /dev/null +++ b/vendor/github.com/google/cel-go/parser/gen/cel_lexer.go @@ -0,0 +1,324 @@ +// Code generated from /Users/tswadell/go/src/github.com/google/cel-go/bin/../parser/gen/CEL.g4 by ANTLR 4.9.1. DO NOT EDIT. + +package gen + +import ( + "fmt" + "unicode" + + "github.com/antlr/antlr4/runtime/Go/antlr" +) + +// Suppress unused import error +var _ = fmt.Printf +var _ = unicode.IsLetter + +var serializedLexerAtn = []uint16{ + 3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 38, 425, + 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, + 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, + 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, + 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, + 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, + 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, + 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, + 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, + 9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 3, 2, 3, + 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, + 6, 3, 7, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, + 3, 11, 3, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 14, 3, 14, 3, 15, 3, 15, 3, + 16, 3, 16, 3, 17, 3, 17, 3, 18, 3, 18, 3, 19, 3, 19, 3, 20, 3, 20, 3, 21, + 3, 21, 3, 22, 3, 22, 3, 23, 3, 23, 3, 24, 3, 24, 3, 25, 3, 25, 3, 26, 3, + 26, 3, 27, 3, 27, 3, 27, 3, 27, 3, 27, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, + 3, 28, 3, 29, 3, 29, 3, 29, 3, 29, 3, 29, 3, 30, 3, 30, 3, 31, 3, 31, 3, + 32, 3, 32, 3, 33, 3, 33, 5, 33, 179, 10, 33, 3, 33, 6, 33, 182, 10, 33, + 13, 33, 14, 33, 183, 3, 34, 3, 34, 3, 35, 3, 35, 3, 36, 3, 36, 3, 36, 3, + 36, 5, 36, 194, 10, 36, 3, 37, 3, 37, 3, 37, 3, 38, 3, 38, 3, 38, 3, 38, + 3, 38, 3, 39, 3, 39, 3, 39, 3, 39, 3, 39, 3, 40, 3, 40, 3, 40, 3, 40, 3, + 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, + 3, 40, 3, 40, 3, 40, 5, 40, 227, 10, 40, 3, 41, 6, 41, 230, 10, 41, 13, + 41, 14, 41, 231, 3, 41, 3, 41, 3, 42, 3, 42, 3, 42, 3, 42, 7, 42, 240, + 10, 42, 12, 42, 14, 42, 243, 11, 42, 3, 42, 3, 42, 3, 43, 6, 43, 248, 10, + 43, 13, 43, 14, 43, 249, 3, 43, 3, 43, 6, 43, 254, 10, 43, 13, 43, 14, + 43, 255, 3, 43, 5, 43, 259, 10, 43, 3, 43, 6, 43, 262, 10, 43, 13, 43, + 14, 43, 263, 3, 43, 3, 43, 3, 43, 3, 43, 6, 43, 270, 10, 43, 13, 43, 14, + 43, 271, 3, 43, 5, 43, 275, 10, 43, 5, 43, 277, 10, 43, 3, 44, 6, 44, 280, + 10, 44, 13, 44, 14, 44, 281, 3, 44, 3, 44, 3, 44, 3, 44, 6, 44, 288, 10, + 44, 13, 44, 14, 44, 289, 5, 44, 292, 10, 44, 3, 45, 6, 45, 295, 10, 45, + 13, 45, 14, 45, 296, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 6, 45, 305, + 10, 45, 13, 45, 14, 45, 306, 3, 45, 3, 45, 5, 45, 311, 10, 45, 3, 46, 3, + 46, 3, 46, 7, 46, 316, 10, 46, 12, 46, 14, 46, 319, 11, 46, 3, 46, 3, 46, + 3, 46, 3, 46, 7, 46, 325, 10, 46, 12, 46, 14, 46, 328, 11, 46, 3, 46, 3, + 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 7, 46, 337, 10, 46, 12, 46, 14, + 46, 340, 11, 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, + 3, 46, 7, 46, 351, 10, 46, 12, 46, 14, 46, 354, 11, 46, 3, 46, 3, 46, 3, + 46, 3, 46, 3, 46, 3, 46, 7, 46, 362, 10, 46, 12, 46, 14, 46, 365, 11, 46, + 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 7, 46, 372, 10, 46, 12, 46, 14, 46, + 375, 11, 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 7, + 46, 385, 10, 46, 12, 46, 14, 46, 388, 11, 46, 3, 46, 3, 46, 3, 46, 3, 46, + 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 7, 46, 400, 10, 46, 12, 46, 14, + 46, 403, 11, 46, 3, 46, 3, 46, 3, 46, 3, 46, 5, 46, 409, 10, 46, 3, 47, + 3, 47, 3, 47, 3, 48, 3, 48, 5, 48, 416, 10, 48, 3, 48, 3, 48, 3, 48, 7, + 48, 421, 10, 48, 12, 48, 14, 48, 424, 11, 48, 6, 338, 352, 386, 401, 2, + 49, 3, 3, 5, 4, 7, 5, 9, 6, 11, 7, 13, 8, 15, 9, 17, 10, 19, 11, 21, 12, + 23, 13, 25, 14, 27, 15, 29, 16, 31, 17, 33, 18, 35, 19, 37, 20, 39, 21, + 41, 22, 43, 23, 45, 24, 47, 25, 49, 26, 51, 27, 53, 28, 55, 29, 57, 30, + 59, 2, 61, 2, 63, 2, 65, 2, 67, 2, 69, 2, 71, 2, 73, 2, 75, 2, 77, 2, 79, + 2, 81, 31, 83, 32, 85, 33, 87, 34, 89, 35, 91, 36, 93, 37, 95, 38, 3, 2, + 18, 4, 2, 67, 92, 99, 124, 4, 2, 71, 71, 103, 103, 4, 2, 45, 45, 47, 47, + 5, 2, 50, 59, 67, 72, 99, 104, 4, 2, 84, 84, 116, 116, 12, 2, 36, 36, 41, + 41, 65, 65, 94, 94, 98, 100, 104, 104, 112, 112, 116, 116, 118, 118, 120, + 120, 4, 2, 90, 90, 122, 122, 5, 2, 11, 12, 14, 15, 34, 34, 3, 2, 12, 12, + 4, 2, 87, 87, 119, 119, 6, 2, 12, 12, 15, 15, 36, 36, 94, 94, 6, 2, 12, + 12, 15, 15, 41, 41, 94, 94, 3, 2, 94, 94, 5, 2, 12, 12, 15, 15, 36, 36, + 5, 2, 12, 12, 15, 15, 41, 41, 4, 2, 68, 68, 100, 100, 2, 458, 2, 3, 3, + 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, + 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, + 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, + 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, 2, + 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 2, 41, 3, 2, 2, + 2, 2, 43, 3, 2, 2, 2, 2, 45, 3, 2, 2, 2, 2, 47, 3, 2, 2, 2, 2, 49, 3, 2, + 2, 2, 2, 51, 3, 2, 2, 2, 2, 53, 3, 2, 2, 2, 2, 55, 3, 2, 2, 2, 2, 57, 3, + 2, 2, 2, 2, 81, 3, 2, 2, 2, 2, 83, 3, 2, 2, 2, 2, 85, 3, 2, 2, 2, 2, 87, + 3, 2, 2, 2, 2, 89, 3, 2, 2, 2, 2, 91, 3, 2, 2, 2, 2, 93, 3, 2, 2, 2, 2, + 95, 3, 2, 2, 2, 3, 97, 3, 2, 2, 2, 5, 100, 3, 2, 2, 2, 7, 103, 3, 2, 2, + 2, 9, 106, 3, 2, 2, 2, 11, 108, 3, 2, 2, 2, 13, 111, 3, 2, 2, 2, 15, 114, + 3, 2, 2, 2, 17, 116, 3, 2, 2, 2, 19, 119, 3, 2, 2, 2, 21, 122, 3, 2, 2, + 2, 23, 124, 3, 2, 2, 2, 25, 126, 3, 2, 2, 2, 27, 128, 3, 2, 2, 2, 29, 130, + 3, 2, 2, 2, 31, 132, 3, 2, 2, 2, 33, 134, 3, 2, 2, 2, 35, 136, 3, 2, 2, + 2, 37, 138, 3, 2, 2, 2, 39, 140, 3, 2, 2, 2, 41, 142, 3, 2, 2, 2, 43, 144, + 3, 2, 2, 2, 45, 146, 3, 2, 2, 2, 47, 148, 3, 2, 2, 2, 49, 150, 3, 2, 2, + 2, 51, 152, 3, 2, 2, 2, 53, 154, 3, 2, 2, 2, 55, 159, 3, 2, 2, 2, 57, 165, + 3, 2, 2, 2, 59, 170, 3, 2, 2, 2, 61, 172, 3, 2, 2, 2, 63, 174, 3, 2, 2, + 2, 65, 176, 3, 2, 2, 2, 67, 185, 3, 2, 2, 2, 69, 187, 3, 2, 2, 2, 71, 193, + 3, 2, 2, 2, 73, 195, 3, 2, 2, 2, 75, 198, 3, 2, 2, 2, 77, 203, 3, 2, 2, + 2, 79, 226, 3, 2, 2, 2, 81, 229, 3, 2, 2, 2, 83, 235, 3, 2, 2, 2, 85, 276, + 3, 2, 2, 2, 87, 291, 3, 2, 2, 2, 89, 310, 3, 2, 2, 2, 91, 408, 3, 2, 2, + 2, 93, 410, 3, 2, 2, 2, 95, 415, 3, 2, 2, 2, 97, 98, 7, 63, 2, 2, 98, 99, + 7, 63, 2, 2, 99, 4, 3, 2, 2, 2, 100, 101, 7, 35, 2, 2, 101, 102, 7, 63, + 2, 2, 102, 6, 3, 2, 2, 2, 103, 104, 7, 107, 2, 2, 104, 105, 7, 112, 2, + 2, 105, 8, 3, 2, 2, 2, 106, 107, 7, 62, 2, 2, 107, 10, 3, 2, 2, 2, 108, + 109, 7, 62, 2, 2, 109, 110, 7, 63, 2, 2, 110, 12, 3, 2, 2, 2, 111, 112, + 7, 64, 2, 2, 112, 113, 7, 63, 2, 2, 113, 14, 3, 2, 2, 2, 114, 115, 7, 64, + 2, 2, 115, 16, 3, 2, 2, 2, 116, 117, 7, 40, 2, 2, 117, 118, 7, 40, 2, 2, + 118, 18, 3, 2, 2, 2, 119, 120, 7, 126, 2, 2, 120, 121, 7, 126, 2, 2, 121, + 20, 3, 2, 2, 2, 122, 123, 7, 93, 2, 2, 123, 22, 3, 2, 2, 2, 124, 125, 7, + 95, 2, 2, 125, 24, 3, 2, 2, 2, 126, 127, 7, 125, 2, 2, 127, 26, 3, 2, 2, + 2, 128, 129, 7, 127, 2, 2, 129, 28, 3, 2, 2, 2, 130, 131, 7, 42, 2, 2, + 131, 30, 3, 2, 2, 2, 132, 133, 7, 43, 2, 2, 133, 32, 3, 2, 2, 2, 134, 135, + 7, 48, 2, 2, 135, 34, 3, 2, 2, 2, 136, 137, 7, 46, 2, 2, 137, 36, 3, 2, + 2, 2, 138, 139, 7, 47, 2, 2, 139, 38, 3, 2, 2, 2, 140, 141, 7, 35, 2, 2, + 141, 40, 3, 2, 2, 2, 142, 143, 7, 65, 2, 2, 143, 42, 3, 2, 2, 2, 144, 145, + 7, 60, 2, 2, 145, 44, 3, 2, 2, 2, 146, 147, 7, 45, 2, 2, 147, 46, 3, 2, + 2, 2, 148, 149, 7, 44, 2, 2, 149, 48, 3, 2, 2, 2, 150, 151, 7, 49, 2, 2, + 151, 50, 3, 2, 2, 2, 152, 153, 7, 39, 2, 2, 153, 52, 3, 2, 2, 2, 154, 155, + 7, 118, 2, 2, 155, 156, 7, 116, 2, 2, 156, 157, 7, 119, 2, 2, 157, 158, + 7, 103, 2, 2, 158, 54, 3, 2, 2, 2, 159, 160, 7, 104, 2, 2, 160, 161, 7, + 99, 2, 2, 161, 162, 7, 110, 2, 2, 162, 163, 7, 117, 2, 2, 163, 164, 7, + 103, 2, 2, 164, 56, 3, 2, 2, 2, 165, 166, 7, 112, 2, 2, 166, 167, 7, 119, + 2, 2, 167, 168, 7, 110, 2, 2, 168, 169, 7, 110, 2, 2, 169, 58, 3, 2, 2, + 2, 170, 171, 7, 94, 2, 2, 171, 60, 3, 2, 2, 2, 172, 173, 9, 2, 2, 2, 173, + 62, 3, 2, 2, 2, 174, 175, 4, 50, 59, 2, 175, 64, 3, 2, 2, 2, 176, 178, + 9, 3, 2, 2, 177, 179, 9, 4, 2, 2, 178, 177, 3, 2, 2, 2, 178, 179, 3, 2, + 2, 2, 179, 181, 3, 2, 2, 2, 180, 182, 5, 63, 32, 2, 181, 180, 3, 2, 2, + 2, 182, 183, 3, 2, 2, 2, 183, 181, 3, 2, 2, 2, 183, 184, 3, 2, 2, 2, 184, + 66, 3, 2, 2, 2, 185, 186, 9, 5, 2, 2, 186, 68, 3, 2, 2, 2, 187, 188, 9, + 6, 2, 2, 188, 70, 3, 2, 2, 2, 189, 194, 5, 73, 37, 2, 190, 194, 5, 77, + 39, 2, 191, 194, 5, 79, 40, 2, 192, 194, 5, 75, 38, 2, 193, 189, 3, 2, + 2, 2, 193, 190, 3, 2, 2, 2, 193, 191, 3, 2, 2, 2, 193, 192, 3, 2, 2, 2, + 194, 72, 3, 2, 2, 2, 195, 196, 5, 59, 30, 2, 196, 197, 9, 7, 2, 2, 197, + 74, 3, 2, 2, 2, 198, 199, 5, 59, 30, 2, 199, 200, 4, 50, 53, 2, 200, 201, + 4, 50, 57, 2, 201, 202, 4, 50, 57, 2, 202, 76, 3, 2, 2, 2, 203, 204, 5, + 59, 30, 2, 204, 205, 9, 8, 2, 2, 205, 206, 5, 67, 34, 2, 206, 207, 5, 67, + 34, 2, 207, 78, 3, 2, 2, 2, 208, 209, 5, 59, 30, 2, 209, 210, 7, 119, 2, + 2, 210, 211, 5, 67, 34, 2, 211, 212, 5, 67, 34, 2, 212, 213, 5, 67, 34, + 2, 213, 214, 5, 67, 34, 2, 214, 227, 3, 2, 2, 2, 215, 216, 5, 59, 30, 2, + 216, 217, 7, 87, 2, 2, 217, 218, 5, 67, 34, 2, 218, 219, 5, 67, 34, 2, + 219, 220, 5, 67, 34, 2, 220, 221, 5, 67, 34, 2, 221, 222, 5, 67, 34, 2, + 222, 223, 5, 67, 34, 2, 223, 224, 5, 67, 34, 2, 224, 225, 5, 67, 34, 2, + 225, 227, 3, 2, 2, 2, 226, 208, 3, 2, 2, 2, 226, 215, 3, 2, 2, 2, 227, + 80, 3, 2, 2, 2, 228, 230, 9, 9, 2, 2, 229, 228, 3, 2, 2, 2, 230, 231, 3, + 2, 2, 2, 231, 229, 3, 2, 2, 2, 231, 232, 3, 2, 2, 2, 232, 233, 3, 2, 2, + 2, 233, 234, 8, 41, 2, 2, 234, 82, 3, 2, 2, 2, 235, 236, 7, 49, 2, 2, 236, + 237, 7, 49, 2, 2, 237, 241, 3, 2, 2, 2, 238, 240, 10, 10, 2, 2, 239, 238, + 3, 2, 2, 2, 240, 243, 3, 2, 2, 2, 241, 239, 3, 2, 2, 2, 241, 242, 3, 2, + 2, 2, 242, 244, 3, 2, 2, 2, 243, 241, 3, 2, 2, 2, 244, 245, 8, 42, 2, 2, + 245, 84, 3, 2, 2, 2, 246, 248, 5, 63, 32, 2, 247, 246, 3, 2, 2, 2, 248, + 249, 3, 2, 2, 2, 249, 247, 3, 2, 2, 2, 249, 250, 3, 2, 2, 2, 250, 251, + 3, 2, 2, 2, 251, 253, 7, 48, 2, 2, 252, 254, 5, 63, 32, 2, 253, 252, 3, + 2, 2, 2, 254, 255, 3, 2, 2, 2, 255, 253, 3, 2, 2, 2, 255, 256, 3, 2, 2, + 2, 256, 258, 3, 2, 2, 2, 257, 259, 5, 65, 33, 2, 258, 257, 3, 2, 2, 2, + 258, 259, 3, 2, 2, 2, 259, 277, 3, 2, 2, 2, 260, 262, 5, 63, 32, 2, 261, + 260, 3, 2, 2, 2, 262, 263, 3, 2, 2, 2, 263, 261, 3, 2, 2, 2, 263, 264, + 3, 2, 2, 2, 264, 265, 3, 2, 2, 2, 265, 266, 5, 65, 33, 2, 266, 277, 3, + 2, 2, 2, 267, 269, 7, 48, 2, 2, 268, 270, 5, 63, 32, 2, 269, 268, 3, 2, + 2, 2, 270, 271, 3, 2, 2, 2, 271, 269, 3, 2, 2, 2, 271, 272, 3, 2, 2, 2, + 272, 274, 3, 2, 2, 2, 273, 275, 5, 65, 33, 2, 274, 273, 3, 2, 2, 2, 274, + 275, 3, 2, 2, 2, 275, 277, 3, 2, 2, 2, 276, 247, 3, 2, 2, 2, 276, 261, + 3, 2, 2, 2, 276, 267, 3, 2, 2, 2, 277, 86, 3, 2, 2, 2, 278, 280, 5, 63, + 32, 2, 279, 278, 3, 2, 2, 2, 280, 281, 3, 2, 2, 2, 281, 279, 3, 2, 2, 2, + 281, 282, 3, 2, 2, 2, 282, 292, 3, 2, 2, 2, 283, 284, 7, 50, 2, 2, 284, + 285, 7, 122, 2, 2, 285, 287, 3, 2, 2, 2, 286, 288, 5, 67, 34, 2, 287, 286, + 3, 2, 2, 2, 288, 289, 3, 2, 2, 2, 289, 287, 3, 2, 2, 2, 289, 290, 3, 2, + 2, 2, 290, 292, 3, 2, 2, 2, 291, 279, 3, 2, 2, 2, 291, 283, 3, 2, 2, 2, + 292, 88, 3, 2, 2, 2, 293, 295, 5, 63, 32, 2, 294, 293, 3, 2, 2, 2, 295, + 296, 3, 2, 2, 2, 296, 294, 3, 2, 2, 2, 296, 297, 3, 2, 2, 2, 297, 298, + 3, 2, 2, 2, 298, 299, 9, 11, 2, 2, 299, 311, 3, 2, 2, 2, 300, 301, 7, 50, + 2, 2, 301, 302, 7, 122, 2, 2, 302, 304, 3, 2, 2, 2, 303, 305, 5, 67, 34, + 2, 304, 303, 3, 2, 2, 2, 305, 306, 3, 2, 2, 2, 306, 304, 3, 2, 2, 2, 306, + 307, 3, 2, 2, 2, 307, 308, 3, 2, 2, 2, 308, 309, 9, 11, 2, 2, 309, 311, + 3, 2, 2, 2, 310, 294, 3, 2, 2, 2, 310, 300, 3, 2, 2, 2, 311, 90, 3, 2, + 2, 2, 312, 317, 7, 36, 2, 2, 313, 316, 5, 71, 36, 2, 314, 316, 10, 12, + 2, 2, 315, 313, 3, 2, 2, 2, 315, 314, 3, 2, 2, 2, 316, 319, 3, 2, 2, 2, + 317, 315, 3, 2, 2, 2, 317, 318, 3, 2, 2, 2, 318, 320, 3, 2, 2, 2, 319, + 317, 3, 2, 2, 2, 320, 409, 7, 36, 2, 2, 321, 326, 7, 41, 2, 2, 322, 325, + 5, 71, 36, 2, 323, 325, 10, 13, 2, 2, 324, 322, 3, 2, 2, 2, 324, 323, 3, + 2, 2, 2, 325, 328, 3, 2, 2, 2, 326, 324, 3, 2, 2, 2, 326, 327, 3, 2, 2, + 2, 327, 329, 3, 2, 2, 2, 328, 326, 3, 2, 2, 2, 329, 409, 7, 41, 2, 2, 330, + 331, 7, 36, 2, 2, 331, 332, 7, 36, 2, 2, 332, 333, 7, 36, 2, 2, 333, 338, + 3, 2, 2, 2, 334, 337, 5, 71, 36, 2, 335, 337, 10, 14, 2, 2, 336, 334, 3, + 2, 2, 2, 336, 335, 3, 2, 2, 2, 337, 340, 3, 2, 2, 2, 338, 339, 3, 2, 2, + 2, 338, 336, 3, 2, 2, 2, 339, 341, 3, 2, 2, 2, 340, 338, 3, 2, 2, 2, 341, + 342, 7, 36, 2, 2, 342, 343, 7, 36, 2, 2, 343, 409, 7, 36, 2, 2, 344, 345, + 7, 41, 2, 2, 345, 346, 7, 41, 2, 2, 346, 347, 7, 41, 2, 2, 347, 352, 3, + 2, 2, 2, 348, 351, 5, 71, 36, 2, 349, 351, 10, 14, 2, 2, 350, 348, 3, 2, + 2, 2, 350, 349, 3, 2, 2, 2, 351, 354, 3, 2, 2, 2, 352, 353, 3, 2, 2, 2, + 352, 350, 3, 2, 2, 2, 353, 355, 3, 2, 2, 2, 354, 352, 3, 2, 2, 2, 355, + 356, 7, 41, 2, 2, 356, 357, 7, 41, 2, 2, 357, 409, 7, 41, 2, 2, 358, 359, + 5, 69, 35, 2, 359, 363, 7, 36, 2, 2, 360, 362, 10, 15, 2, 2, 361, 360, + 3, 2, 2, 2, 362, 365, 3, 2, 2, 2, 363, 361, 3, 2, 2, 2, 363, 364, 3, 2, + 2, 2, 364, 366, 3, 2, 2, 2, 365, 363, 3, 2, 2, 2, 366, 367, 7, 36, 2, 2, + 367, 409, 3, 2, 2, 2, 368, 369, 5, 69, 35, 2, 369, 373, 7, 41, 2, 2, 370, + 372, 10, 16, 2, 2, 371, 370, 3, 2, 2, 2, 372, 375, 3, 2, 2, 2, 373, 371, + 3, 2, 2, 2, 373, 374, 3, 2, 2, 2, 374, 376, 3, 2, 2, 2, 375, 373, 3, 2, + 2, 2, 376, 377, 7, 41, 2, 2, 377, 409, 3, 2, 2, 2, 378, 379, 5, 69, 35, + 2, 379, 380, 7, 36, 2, 2, 380, 381, 7, 36, 2, 2, 381, 382, 7, 36, 2, 2, + 382, 386, 3, 2, 2, 2, 383, 385, 11, 2, 2, 2, 384, 383, 3, 2, 2, 2, 385, + 388, 3, 2, 2, 2, 386, 387, 3, 2, 2, 2, 386, 384, 3, 2, 2, 2, 387, 389, + 3, 2, 2, 2, 388, 386, 3, 2, 2, 2, 389, 390, 7, 36, 2, 2, 390, 391, 7, 36, + 2, 2, 391, 392, 7, 36, 2, 2, 392, 409, 3, 2, 2, 2, 393, 394, 5, 69, 35, + 2, 394, 395, 7, 41, 2, 2, 395, 396, 7, 41, 2, 2, 396, 397, 7, 41, 2, 2, + 397, 401, 3, 2, 2, 2, 398, 400, 11, 2, 2, 2, 399, 398, 3, 2, 2, 2, 400, + 403, 3, 2, 2, 2, 401, 402, 3, 2, 2, 2, 401, 399, 3, 2, 2, 2, 402, 404, + 3, 2, 2, 2, 403, 401, 3, 2, 2, 2, 404, 405, 7, 41, 2, 2, 405, 406, 7, 41, + 2, 2, 406, 407, 7, 41, 2, 2, 407, 409, 3, 2, 2, 2, 408, 312, 3, 2, 2, 2, + 408, 321, 3, 2, 2, 2, 408, 330, 3, 2, 2, 2, 408, 344, 3, 2, 2, 2, 408, + 358, 3, 2, 2, 2, 408, 368, 3, 2, 2, 2, 408, 378, 3, 2, 2, 2, 408, 393, + 3, 2, 2, 2, 409, 92, 3, 2, 2, 2, 410, 411, 9, 17, 2, 2, 411, 412, 5, 91, + 46, 2, 412, 94, 3, 2, 2, 2, 413, 416, 5, 61, 31, 2, 414, 416, 7, 97, 2, + 2, 415, 413, 3, 2, 2, 2, 415, 414, 3, 2, 2, 2, 416, 422, 3, 2, 2, 2, 417, + 421, 5, 61, 31, 2, 418, 421, 5, 63, 32, 2, 419, 421, 7, 97, 2, 2, 420, + 417, 3, 2, 2, 2, 420, 418, 3, 2, 2, 2, 420, 419, 3, 2, 2, 2, 421, 424, + 3, 2, 2, 2, 422, 420, 3, 2, 2, 2, 422, 423, 3, 2, 2, 2, 423, 96, 3, 2, + 2, 2, 424, 422, 3, 2, 2, 2, 38, 2, 178, 183, 193, 226, 231, 241, 249, 255, + 258, 263, 271, 274, 276, 281, 289, 291, 296, 306, 310, 315, 317, 324, 326, + 336, 338, 350, 352, 363, 373, 386, 401, 408, 415, 420, 422, 3, 2, 3, 2, +} + +var lexerChannelNames = []string{ + "DEFAULT_TOKEN_CHANNEL", "HIDDEN", +} + +var lexerModeNames = []string{ + "DEFAULT_MODE", +} + +var lexerLiteralNames = []string{ + "", "'=='", "'!='", "'in'", "'<'", "'<='", "'>='", "'>'", "'&&'", "'||'", + "'['", "']'", "'{'", "'}'", "'('", "')'", "'.'", "','", "'-'", "'!'", "'?'", + "':'", "'+'", "'*'", "'/'", "'%'", "'true'", "'false'", "'null'", +} + +var lexerSymbolicNames = []string{ + "", "EQUALS", "NOT_EQUALS", "IN", "LESS", "LESS_EQUALS", "GREATER_EQUALS", + "GREATER", "LOGICAL_AND", "LOGICAL_OR", "LBRACKET", "RPRACKET", "LBRACE", + "RBRACE", "LPAREN", "RPAREN", "DOT", "COMMA", "MINUS", "EXCLAM", "QUESTIONMARK", + "COLON", "PLUS", "STAR", "SLASH", "PERCENT", "CEL_TRUE", "CEL_FALSE", "NUL", + "WHITESPACE", "COMMENT", "NUM_FLOAT", "NUM_INT", "NUM_UINT", "STRING", + "BYTES", "IDENTIFIER", +} + +var lexerRuleNames = []string{ + "EQUALS", "NOT_EQUALS", "IN", "LESS", "LESS_EQUALS", "GREATER_EQUALS", + "GREATER", "LOGICAL_AND", "LOGICAL_OR", "LBRACKET", "RPRACKET", "LBRACE", + "RBRACE", "LPAREN", "RPAREN", "DOT", "COMMA", "MINUS", "EXCLAM", "QUESTIONMARK", + "COLON", "PLUS", "STAR", "SLASH", "PERCENT", "CEL_TRUE", "CEL_FALSE", "NUL", + "BACKSLASH", "LETTER", "DIGIT", "EXPONENT", "HEXDIGIT", "RAW", "ESC_SEQ", + "ESC_CHAR_SEQ", "ESC_OCT_SEQ", "ESC_BYTE_SEQ", "ESC_UNI_SEQ", "WHITESPACE", + "COMMENT", "NUM_FLOAT", "NUM_INT", "NUM_UINT", "STRING", "BYTES", "IDENTIFIER", +} + +type CELLexer struct { + *antlr.BaseLexer + channelNames []string + modeNames []string + // TODO: EOF string +} + +// NewCELLexer produces a new lexer instance for the optional input antlr.CharStream. +// +// The *CELLexer instance produced may be reused by calling the SetInputStream method. +// The initial lexer configuration is expensive to construct, and the object is not thread-safe; +// however, if used within a Golang sync.Pool, the construction cost amortizes well and the +// objects can be used in a thread-safe manner. +func NewCELLexer(input antlr.CharStream) *CELLexer { + l := new(CELLexer) + lexerDeserializer := antlr.NewATNDeserializer(nil) + lexerAtn := lexerDeserializer.DeserializeFromUInt16(serializedLexerAtn) + lexerDecisionToDFA := make([]*antlr.DFA, len(lexerAtn.DecisionToState)) + for index, ds := range lexerAtn.DecisionToState { + lexerDecisionToDFA[index] = antlr.NewDFA(ds, index) + } + l.BaseLexer = antlr.NewBaseLexer(input) + l.Interpreter = antlr.NewLexerATNSimulator(l, lexerAtn, lexerDecisionToDFA, antlr.NewPredictionContextCache()) + + l.channelNames = lexerChannelNames + l.modeNames = lexerModeNames + l.RuleNames = lexerRuleNames + l.LiteralNames = lexerLiteralNames + l.SymbolicNames = lexerSymbolicNames + l.GrammarFileName = "CEL.g4" + // TODO: l.EOF = antlr.TokenEOF + + return l +} + +// CELLexer tokens. +const ( + CELLexerEQUALS = 1 + CELLexerNOT_EQUALS = 2 + CELLexerIN = 3 + CELLexerLESS = 4 + CELLexerLESS_EQUALS = 5 + CELLexerGREATER_EQUALS = 6 + CELLexerGREATER = 7 + CELLexerLOGICAL_AND = 8 + CELLexerLOGICAL_OR = 9 + CELLexerLBRACKET = 10 + CELLexerRPRACKET = 11 + CELLexerLBRACE = 12 + CELLexerRBRACE = 13 + CELLexerLPAREN = 14 + CELLexerRPAREN = 15 + CELLexerDOT = 16 + CELLexerCOMMA = 17 + CELLexerMINUS = 18 + CELLexerEXCLAM = 19 + CELLexerQUESTIONMARK = 20 + CELLexerCOLON = 21 + CELLexerPLUS = 22 + CELLexerSTAR = 23 + CELLexerSLASH = 24 + CELLexerPERCENT = 25 + CELLexerCEL_TRUE = 26 + CELLexerCEL_FALSE = 27 + CELLexerNUL = 28 + CELLexerWHITESPACE = 29 + CELLexerCOMMENT = 30 + CELLexerNUM_FLOAT = 31 + CELLexerNUM_INT = 32 + CELLexerNUM_UINT = 33 + CELLexerSTRING = 34 + CELLexerBYTES = 35 + CELLexerIDENTIFIER = 36 +) diff --git a/vendor/github.com/google/cel-go/parser/gen/cel_listener.go b/vendor/github.com/google/cel-go/parser/gen/cel_listener.go new file mode 100644 index 0000000000..e555ae6272 --- /dev/null +++ b/vendor/github.com/google/cel-go/parser/gen/cel_listener.go @@ -0,0 +1,183 @@ +// Code generated from /Users/tswadell/go/src/github.com/google/cel-go/bin/../parser/gen/CEL.g4 by ANTLR 4.9.1. DO NOT EDIT. + +package gen // CEL +import "github.com/antlr/antlr4/runtime/Go/antlr" + +// CELListener is a complete listener for a parse tree produced by CELParser. +type CELListener interface { + antlr.ParseTreeListener + + // EnterStart is called when entering the start production. + EnterStart(c *StartContext) + + // EnterExpr is called when entering the expr production. + EnterExpr(c *ExprContext) + + // EnterConditionalOr is called when entering the conditionalOr production. + EnterConditionalOr(c *ConditionalOrContext) + + // EnterConditionalAnd is called when entering the conditionalAnd production. + EnterConditionalAnd(c *ConditionalAndContext) + + // EnterRelation is called when entering the relation production. + EnterRelation(c *RelationContext) + + // EnterCalc is called when entering the calc production. + EnterCalc(c *CalcContext) + + // EnterMemberExpr is called when entering the MemberExpr production. + EnterMemberExpr(c *MemberExprContext) + + // EnterLogicalNot is called when entering the LogicalNot production. + EnterLogicalNot(c *LogicalNotContext) + + // EnterNegate is called when entering the Negate production. + EnterNegate(c *NegateContext) + + // EnterSelectOrCall is called when entering the SelectOrCall production. + EnterSelectOrCall(c *SelectOrCallContext) + + // EnterPrimaryExpr is called when entering the PrimaryExpr production. + EnterPrimaryExpr(c *PrimaryExprContext) + + // EnterIndex is called when entering the Index production. + EnterIndex(c *IndexContext) + + // EnterCreateMessage is called when entering the CreateMessage production. + EnterCreateMessage(c *CreateMessageContext) + + // EnterIdentOrGlobalCall is called when entering the IdentOrGlobalCall production. + EnterIdentOrGlobalCall(c *IdentOrGlobalCallContext) + + // EnterNested is called when entering the Nested production. + EnterNested(c *NestedContext) + + // EnterCreateList is called when entering the CreateList production. + EnterCreateList(c *CreateListContext) + + // EnterCreateStruct is called when entering the CreateStruct production. + EnterCreateStruct(c *CreateStructContext) + + // EnterConstantLiteral is called when entering the ConstantLiteral production. + EnterConstantLiteral(c *ConstantLiteralContext) + + // EnterExprList is called when entering the exprList production. + EnterExprList(c *ExprListContext) + + // EnterFieldInitializerList is called when entering the fieldInitializerList production. + EnterFieldInitializerList(c *FieldInitializerListContext) + + // EnterMapInitializerList is called when entering the mapInitializerList production. + EnterMapInitializerList(c *MapInitializerListContext) + + // EnterInt is called when entering the Int production. + EnterInt(c *IntContext) + + // EnterUint is called when entering the Uint production. + EnterUint(c *UintContext) + + // EnterDouble is called when entering the Double production. + EnterDouble(c *DoubleContext) + + // EnterString is called when entering the String production. + EnterString(c *StringContext) + + // EnterBytes is called when entering the Bytes production. + EnterBytes(c *BytesContext) + + // EnterBoolTrue is called when entering the BoolTrue production. + EnterBoolTrue(c *BoolTrueContext) + + // EnterBoolFalse is called when entering the BoolFalse production. + EnterBoolFalse(c *BoolFalseContext) + + // EnterNull is called when entering the Null production. + EnterNull(c *NullContext) + + // ExitStart is called when exiting the start production. + ExitStart(c *StartContext) + + // ExitExpr is called when exiting the expr production. + ExitExpr(c *ExprContext) + + // ExitConditionalOr is called when exiting the conditionalOr production. + ExitConditionalOr(c *ConditionalOrContext) + + // ExitConditionalAnd is called when exiting the conditionalAnd production. + ExitConditionalAnd(c *ConditionalAndContext) + + // ExitRelation is called when exiting the relation production. + ExitRelation(c *RelationContext) + + // ExitCalc is called when exiting the calc production. + ExitCalc(c *CalcContext) + + // ExitMemberExpr is called when exiting the MemberExpr production. + ExitMemberExpr(c *MemberExprContext) + + // ExitLogicalNot is called when exiting the LogicalNot production. + ExitLogicalNot(c *LogicalNotContext) + + // ExitNegate is called when exiting the Negate production. + ExitNegate(c *NegateContext) + + // ExitSelectOrCall is called when exiting the SelectOrCall production. + ExitSelectOrCall(c *SelectOrCallContext) + + // ExitPrimaryExpr is called when exiting the PrimaryExpr production. + ExitPrimaryExpr(c *PrimaryExprContext) + + // ExitIndex is called when exiting the Index production. + ExitIndex(c *IndexContext) + + // ExitCreateMessage is called when exiting the CreateMessage production. + ExitCreateMessage(c *CreateMessageContext) + + // ExitIdentOrGlobalCall is called when exiting the IdentOrGlobalCall production. + ExitIdentOrGlobalCall(c *IdentOrGlobalCallContext) + + // ExitNested is called when exiting the Nested production. + ExitNested(c *NestedContext) + + // ExitCreateList is called when exiting the CreateList production. + ExitCreateList(c *CreateListContext) + + // ExitCreateStruct is called when exiting the CreateStruct production. + ExitCreateStruct(c *CreateStructContext) + + // ExitConstantLiteral is called when exiting the ConstantLiteral production. + ExitConstantLiteral(c *ConstantLiteralContext) + + // ExitExprList is called when exiting the exprList production. + ExitExprList(c *ExprListContext) + + // ExitFieldInitializerList is called when exiting the fieldInitializerList production. + ExitFieldInitializerList(c *FieldInitializerListContext) + + // ExitMapInitializerList is called when exiting the mapInitializerList production. + ExitMapInitializerList(c *MapInitializerListContext) + + // ExitInt is called when exiting the Int production. + ExitInt(c *IntContext) + + // ExitUint is called when exiting the Uint production. + ExitUint(c *UintContext) + + // ExitDouble is called when exiting the Double production. + ExitDouble(c *DoubleContext) + + // ExitString is called when exiting the String production. + ExitString(c *StringContext) + + // ExitBytes is called when exiting the Bytes production. + ExitBytes(c *BytesContext) + + // ExitBoolTrue is called when exiting the BoolTrue production. + ExitBoolTrue(c *BoolTrueContext) + + // ExitBoolFalse is called when exiting the BoolFalse production. + ExitBoolFalse(c *BoolFalseContext) + + // ExitNull is called when exiting the Null production. + ExitNull(c *NullContext) +} diff --git a/vendor/github.com/google/cel-go/parser/gen/cel_parser.go b/vendor/github.com/google/cel-go/parser/gen/cel_parser.go new file mode 100644 index 0000000000..c5e18930e5 --- /dev/null +++ b/vendor/github.com/google/cel-go/parser/gen/cel_parser.go @@ -0,0 +1,4324 @@ +// Code generated from /Users/tswadell/go/src/github.com/google/cel-go/bin/../parser/gen/CEL.g4 by ANTLR 4.9.1. DO NOT EDIT. + +package gen // CEL +import ( + "fmt" + "reflect" + "strconv" + + "github.com/antlr/antlr4/runtime/Go/antlr" +) + +// Suppress unused import errors +var _ = fmt.Printf +var _ = reflect.Copy +var _ = strconv.Itoa + +var parserATN = []uint16{ + 3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 38, 211, + 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, + 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, + 9, 13, 4, 14, 9, 14, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 5, 3, 38, 10, 3, 3, 4, 3, 4, 3, 4, 7, 4, 43, 10, 4, 12, 4, 14, 4, 46, + 11, 4, 3, 5, 3, 5, 3, 5, 7, 5, 51, 10, 5, 12, 5, 14, 5, 54, 11, 5, 3, 6, + 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 7, 6, 62, 10, 6, 12, 6, 14, 6, 65, 11, 6, + 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 7, 7, 76, 10, 7, + 12, 7, 14, 7, 79, 11, 7, 3, 8, 3, 8, 6, 8, 83, 10, 8, 13, 8, 14, 8, 84, + 3, 8, 3, 8, 6, 8, 89, 10, 8, 13, 8, 14, 8, 90, 3, 8, 5, 8, 94, 10, 8, 3, + 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 5, 9, 104, 10, 9, 3, 9, 5, + 9, 107, 10, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 5, 9, 117, + 10, 9, 3, 9, 5, 9, 120, 10, 9, 3, 9, 7, 9, 123, 10, 9, 12, 9, 14, 9, 126, + 11, 9, 3, 10, 5, 10, 129, 10, 10, 3, 10, 3, 10, 3, 10, 5, 10, 134, 10, + 10, 3, 10, 5, 10, 137, 10, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, + 5, 10, 145, 10, 10, 3, 10, 5, 10, 148, 10, 10, 3, 10, 3, 10, 3, 10, 5, + 10, 153, 10, 10, 3, 10, 5, 10, 156, 10, 10, 3, 10, 3, 10, 5, 10, 160, 10, + 10, 3, 11, 3, 11, 3, 11, 7, 11, 165, 10, 11, 12, 11, 14, 11, 168, 11, 11, + 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 7, 12, 177, 10, 12, 12, + 12, 14, 12, 180, 11, 12, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, + 3, 13, 7, 13, 190, 10, 13, 12, 13, 14, 13, 193, 11, 13, 3, 14, 5, 14, 196, + 10, 14, 3, 14, 3, 14, 3, 14, 5, 14, 201, 10, 14, 3, 14, 3, 14, 3, 14, 3, + 14, 3, 14, 3, 14, 5, 14, 209, 10, 14, 3, 14, 2, 5, 10, 12, 16, 15, 2, 4, + 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 2, 5, 3, 2, 3, 9, 3, 2, 25, 27, + 4, 2, 20, 20, 24, 24, 2, 237, 2, 28, 3, 2, 2, 2, 4, 31, 3, 2, 2, 2, 6, + 39, 3, 2, 2, 2, 8, 47, 3, 2, 2, 2, 10, 55, 3, 2, 2, 2, 12, 66, 3, 2, 2, + 2, 14, 93, 3, 2, 2, 2, 16, 95, 3, 2, 2, 2, 18, 159, 3, 2, 2, 2, 20, 161, + 3, 2, 2, 2, 22, 169, 3, 2, 2, 2, 24, 181, 3, 2, 2, 2, 26, 208, 3, 2, 2, + 2, 28, 29, 5, 4, 3, 2, 29, 30, 7, 2, 2, 3, 30, 3, 3, 2, 2, 2, 31, 37, 5, + 6, 4, 2, 32, 33, 7, 22, 2, 2, 33, 34, 5, 6, 4, 2, 34, 35, 7, 23, 2, 2, + 35, 36, 5, 4, 3, 2, 36, 38, 3, 2, 2, 2, 37, 32, 3, 2, 2, 2, 37, 38, 3, + 2, 2, 2, 38, 5, 3, 2, 2, 2, 39, 44, 5, 8, 5, 2, 40, 41, 7, 11, 2, 2, 41, + 43, 5, 8, 5, 2, 42, 40, 3, 2, 2, 2, 43, 46, 3, 2, 2, 2, 44, 42, 3, 2, 2, + 2, 44, 45, 3, 2, 2, 2, 45, 7, 3, 2, 2, 2, 46, 44, 3, 2, 2, 2, 47, 52, 5, + 10, 6, 2, 48, 49, 7, 10, 2, 2, 49, 51, 5, 10, 6, 2, 50, 48, 3, 2, 2, 2, + 51, 54, 3, 2, 2, 2, 52, 50, 3, 2, 2, 2, 52, 53, 3, 2, 2, 2, 53, 9, 3, 2, + 2, 2, 54, 52, 3, 2, 2, 2, 55, 56, 8, 6, 1, 2, 56, 57, 5, 12, 7, 2, 57, + 63, 3, 2, 2, 2, 58, 59, 12, 3, 2, 2, 59, 60, 9, 2, 2, 2, 60, 62, 5, 10, + 6, 4, 61, 58, 3, 2, 2, 2, 62, 65, 3, 2, 2, 2, 63, 61, 3, 2, 2, 2, 63, 64, + 3, 2, 2, 2, 64, 11, 3, 2, 2, 2, 65, 63, 3, 2, 2, 2, 66, 67, 8, 7, 1, 2, + 67, 68, 5, 14, 8, 2, 68, 77, 3, 2, 2, 2, 69, 70, 12, 4, 2, 2, 70, 71, 9, + 3, 2, 2, 71, 76, 5, 12, 7, 5, 72, 73, 12, 3, 2, 2, 73, 74, 9, 4, 2, 2, + 74, 76, 5, 12, 7, 4, 75, 69, 3, 2, 2, 2, 75, 72, 3, 2, 2, 2, 76, 79, 3, + 2, 2, 2, 77, 75, 3, 2, 2, 2, 77, 78, 3, 2, 2, 2, 78, 13, 3, 2, 2, 2, 79, + 77, 3, 2, 2, 2, 80, 94, 5, 16, 9, 2, 81, 83, 7, 21, 2, 2, 82, 81, 3, 2, + 2, 2, 83, 84, 3, 2, 2, 2, 84, 82, 3, 2, 2, 2, 84, 85, 3, 2, 2, 2, 85, 86, + 3, 2, 2, 2, 86, 94, 5, 16, 9, 2, 87, 89, 7, 20, 2, 2, 88, 87, 3, 2, 2, + 2, 89, 90, 3, 2, 2, 2, 90, 88, 3, 2, 2, 2, 90, 91, 3, 2, 2, 2, 91, 92, + 3, 2, 2, 2, 92, 94, 5, 16, 9, 2, 93, 80, 3, 2, 2, 2, 93, 82, 3, 2, 2, 2, + 93, 88, 3, 2, 2, 2, 94, 15, 3, 2, 2, 2, 95, 96, 8, 9, 1, 2, 96, 97, 5, + 18, 10, 2, 97, 124, 3, 2, 2, 2, 98, 99, 12, 5, 2, 2, 99, 100, 7, 18, 2, + 2, 100, 106, 7, 38, 2, 2, 101, 103, 7, 16, 2, 2, 102, 104, 5, 20, 11, 2, + 103, 102, 3, 2, 2, 2, 103, 104, 3, 2, 2, 2, 104, 105, 3, 2, 2, 2, 105, + 107, 7, 17, 2, 2, 106, 101, 3, 2, 2, 2, 106, 107, 3, 2, 2, 2, 107, 123, + 3, 2, 2, 2, 108, 109, 12, 4, 2, 2, 109, 110, 7, 12, 2, 2, 110, 111, 5, + 4, 3, 2, 111, 112, 7, 13, 2, 2, 112, 123, 3, 2, 2, 2, 113, 114, 12, 3, + 2, 2, 114, 116, 7, 14, 2, 2, 115, 117, 5, 22, 12, 2, 116, 115, 3, 2, 2, + 2, 116, 117, 3, 2, 2, 2, 117, 119, 3, 2, 2, 2, 118, 120, 7, 19, 2, 2, 119, + 118, 3, 2, 2, 2, 119, 120, 3, 2, 2, 2, 120, 121, 3, 2, 2, 2, 121, 123, + 7, 15, 2, 2, 122, 98, 3, 2, 2, 2, 122, 108, 3, 2, 2, 2, 122, 113, 3, 2, + 2, 2, 123, 126, 3, 2, 2, 2, 124, 122, 3, 2, 2, 2, 124, 125, 3, 2, 2, 2, + 125, 17, 3, 2, 2, 2, 126, 124, 3, 2, 2, 2, 127, 129, 7, 18, 2, 2, 128, + 127, 3, 2, 2, 2, 128, 129, 3, 2, 2, 2, 129, 130, 3, 2, 2, 2, 130, 136, + 7, 38, 2, 2, 131, 133, 7, 16, 2, 2, 132, 134, 5, 20, 11, 2, 133, 132, 3, + 2, 2, 2, 133, 134, 3, 2, 2, 2, 134, 135, 3, 2, 2, 2, 135, 137, 7, 17, 2, + 2, 136, 131, 3, 2, 2, 2, 136, 137, 3, 2, 2, 2, 137, 160, 3, 2, 2, 2, 138, + 139, 7, 16, 2, 2, 139, 140, 5, 4, 3, 2, 140, 141, 7, 17, 2, 2, 141, 160, + 3, 2, 2, 2, 142, 144, 7, 12, 2, 2, 143, 145, 5, 20, 11, 2, 144, 143, 3, + 2, 2, 2, 144, 145, 3, 2, 2, 2, 145, 147, 3, 2, 2, 2, 146, 148, 7, 19, 2, + 2, 147, 146, 3, 2, 2, 2, 147, 148, 3, 2, 2, 2, 148, 149, 3, 2, 2, 2, 149, + 160, 7, 13, 2, 2, 150, 152, 7, 14, 2, 2, 151, 153, 5, 24, 13, 2, 152, 151, + 3, 2, 2, 2, 152, 153, 3, 2, 2, 2, 153, 155, 3, 2, 2, 2, 154, 156, 7, 19, + 2, 2, 155, 154, 3, 2, 2, 2, 155, 156, 3, 2, 2, 2, 156, 157, 3, 2, 2, 2, + 157, 160, 7, 15, 2, 2, 158, 160, 5, 26, 14, 2, 159, 128, 3, 2, 2, 2, 159, + 138, 3, 2, 2, 2, 159, 142, 3, 2, 2, 2, 159, 150, 3, 2, 2, 2, 159, 158, + 3, 2, 2, 2, 160, 19, 3, 2, 2, 2, 161, 166, 5, 4, 3, 2, 162, 163, 7, 19, + 2, 2, 163, 165, 5, 4, 3, 2, 164, 162, 3, 2, 2, 2, 165, 168, 3, 2, 2, 2, + 166, 164, 3, 2, 2, 2, 166, 167, 3, 2, 2, 2, 167, 21, 3, 2, 2, 2, 168, 166, + 3, 2, 2, 2, 169, 170, 7, 38, 2, 2, 170, 171, 7, 23, 2, 2, 171, 178, 5, + 4, 3, 2, 172, 173, 7, 19, 2, 2, 173, 174, 7, 38, 2, 2, 174, 175, 7, 23, + 2, 2, 175, 177, 5, 4, 3, 2, 176, 172, 3, 2, 2, 2, 177, 180, 3, 2, 2, 2, + 178, 176, 3, 2, 2, 2, 178, 179, 3, 2, 2, 2, 179, 23, 3, 2, 2, 2, 180, 178, + 3, 2, 2, 2, 181, 182, 5, 4, 3, 2, 182, 183, 7, 23, 2, 2, 183, 191, 5, 4, + 3, 2, 184, 185, 7, 19, 2, 2, 185, 186, 5, 4, 3, 2, 186, 187, 7, 23, 2, + 2, 187, 188, 5, 4, 3, 2, 188, 190, 3, 2, 2, 2, 189, 184, 3, 2, 2, 2, 190, + 193, 3, 2, 2, 2, 191, 189, 3, 2, 2, 2, 191, 192, 3, 2, 2, 2, 192, 25, 3, + 2, 2, 2, 193, 191, 3, 2, 2, 2, 194, 196, 7, 20, 2, 2, 195, 194, 3, 2, 2, + 2, 195, 196, 3, 2, 2, 2, 196, 197, 3, 2, 2, 2, 197, 209, 7, 34, 2, 2, 198, + 209, 7, 35, 2, 2, 199, 201, 7, 20, 2, 2, 200, 199, 3, 2, 2, 2, 200, 201, + 3, 2, 2, 2, 201, 202, 3, 2, 2, 2, 202, 209, 7, 33, 2, 2, 203, 209, 7, 36, + 2, 2, 204, 209, 7, 37, 2, 2, 205, 209, 7, 28, 2, 2, 206, 209, 7, 29, 2, + 2, 207, 209, 7, 30, 2, 2, 208, 195, 3, 2, 2, 2, 208, 198, 3, 2, 2, 2, 208, + 200, 3, 2, 2, 2, 208, 203, 3, 2, 2, 2, 208, 204, 3, 2, 2, 2, 208, 205, + 3, 2, 2, 2, 208, 206, 3, 2, 2, 2, 208, 207, 3, 2, 2, 2, 209, 27, 3, 2, + 2, 2, 31, 37, 44, 52, 63, 75, 77, 84, 90, 93, 103, 106, 116, 119, 122, + 124, 128, 133, 136, 144, 147, 152, 155, 159, 166, 178, 191, 195, 200, 208, +} +var literalNames = []string{ + "", "'=='", "'!='", "'in'", "'<'", "'<='", "'>='", "'>'", "'&&'", "'||'", + "'['", "']'", "'{'", "'}'", "'('", "')'", "'.'", "','", "'-'", "'!'", "'?'", + "':'", "'+'", "'*'", "'/'", "'%'", "'true'", "'false'", "'null'", +} +var symbolicNames = []string{ + "", "EQUALS", "NOT_EQUALS", "IN", "LESS", "LESS_EQUALS", "GREATER_EQUALS", + "GREATER", "LOGICAL_AND", "LOGICAL_OR", "LBRACKET", "RPRACKET", "LBRACE", + "RBRACE", "LPAREN", "RPAREN", "DOT", "COMMA", "MINUS", "EXCLAM", "QUESTIONMARK", + "COLON", "PLUS", "STAR", "SLASH", "PERCENT", "CEL_TRUE", "CEL_FALSE", "NUL", + "WHITESPACE", "COMMENT", "NUM_FLOAT", "NUM_INT", "NUM_UINT", "STRING", + "BYTES", "IDENTIFIER", +} + +var ruleNames = []string{ + "start", "expr", "conditionalOr", "conditionalAnd", "relation", "calc", + "unary", "member", "primary", "exprList", "fieldInitializerList", "mapInitializerList", + "literal", +} + +type CELParser struct { + *antlr.BaseParser +} + +// NewCELParser produces a new parser instance for the optional input antlr.TokenStream. +// +// The *CELParser instance produced may be reused by calling the SetInputStream method. +// The initial parser configuration is expensive to construct, and the object is not thread-safe; +// however, if used within a Golang sync.Pool, the construction cost amortizes well and the +// objects can be used in a thread-safe manner. +func NewCELParser(input antlr.TokenStream) *CELParser { + this := new(CELParser) + deserializer := antlr.NewATNDeserializer(nil) + deserializedATN := deserializer.DeserializeFromUInt16(parserATN) + decisionToDFA := make([]*antlr.DFA, len(deserializedATN.DecisionToState)) + for index, ds := range deserializedATN.DecisionToState { + decisionToDFA[index] = antlr.NewDFA(ds, index) + } + this.BaseParser = antlr.NewBaseParser(input) + + this.Interpreter = antlr.NewParserATNSimulator(this, deserializedATN, decisionToDFA, antlr.NewPredictionContextCache()) + this.RuleNames = ruleNames + this.LiteralNames = literalNames + this.SymbolicNames = symbolicNames + this.GrammarFileName = "CEL.g4" + + return this +} + +// CELParser tokens. +const ( + CELParserEOF = antlr.TokenEOF + CELParserEQUALS = 1 + CELParserNOT_EQUALS = 2 + CELParserIN = 3 + CELParserLESS = 4 + CELParserLESS_EQUALS = 5 + CELParserGREATER_EQUALS = 6 + CELParserGREATER = 7 + CELParserLOGICAL_AND = 8 + CELParserLOGICAL_OR = 9 + CELParserLBRACKET = 10 + CELParserRPRACKET = 11 + CELParserLBRACE = 12 + CELParserRBRACE = 13 + CELParserLPAREN = 14 + CELParserRPAREN = 15 + CELParserDOT = 16 + CELParserCOMMA = 17 + CELParserMINUS = 18 + CELParserEXCLAM = 19 + CELParserQUESTIONMARK = 20 + CELParserCOLON = 21 + CELParserPLUS = 22 + CELParserSTAR = 23 + CELParserSLASH = 24 + CELParserPERCENT = 25 + CELParserCEL_TRUE = 26 + CELParserCEL_FALSE = 27 + CELParserNUL = 28 + CELParserWHITESPACE = 29 + CELParserCOMMENT = 30 + CELParserNUM_FLOAT = 31 + CELParserNUM_INT = 32 + CELParserNUM_UINT = 33 + CELParserSTRING = 34 + CELParserBYTES = 35 + CELParserIDENTIFIER = 36 +) + +// CELParser rules. +const ( + CELParserRULE_start = 0 + CELParserRULE_expr = 1 + CELParserRULE_conditionalOr = 2 + CELParserRULE_conditionalAnd = 3 + CELParserRULE_relation = 4 + CELParserRULE_calc = 5 + CELParserRULE_unary = 6 + CELParserRULE_member = 7 + CELParserRULE_primary = 8 + CELParserRULE_exprList = 9 + CELParserRULE_fieldInitializerList = 10 + CELParserRULE_mapInitializerList = 11 + CELParserRULE_literal = 12 +) + +// IStartContext is an interface to support dynamic dispatch. +type IStartContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetE returns the e rule contexts. + GetE() IExprContext + + // SetE sets the e rule contexts. + SetE(IExprContext) + + // IsStartContext differentiates from other interfaces. + IsStartContext() +} + +type StartContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + e IExprContext +} + +func NewEmptyStartContext() *StartContext { + var p = new(StartContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = CELParserRULE_start + return p +} + +func (*StartContext) IsStartContext() {} + +func NewStartContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *StartContext { + var p = new(StartContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = CELParserRULE_start + + return p +} + +func (s *StartContext) GetParser() antlr.Parser { return s.parser } + +func (s *StartContext) GetE() IExprContext { return s.e } + +func (s *StartContext) SetE(v IExprContext) { s.e = v } + +func (s *StartContext) EOF() antlr.TerminalNode { + return s.GetToken(CELParserEOF, 0) +} + +func (s *StartContext) Expr() IExprContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IExprContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IExprContext) +} + +func (s *StartContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *StartContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *StartContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(CELListener); ok { + listenerT.EnterStart(s) + } +} + +func (s *StartContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(CELListener); ok { + listenerT.ExitStart(s) + } +} + +func (s *StartContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case CELVisitor: + return t.VisitStart(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *CELParser) Start() (localctx IStartContext) { + localctx = NewStartContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 0, CELParserRULE_start) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(26) + + var _x = p.Expr() + + localctx.(*StartContext).e = _x + } + { + p.SetState(27) + p.Match(CELParserEOF) + } + + return localctx +} + +// IExprContext is an interface to support dynamic dispatch. +type IExprContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetOp returns the op token. + GetOp() antlr.Token + + // SetOp sets the op token. + SetOp(antlr.Token) + + // GetE returns the e rule contexts. + GetE() IConditionalOrContext + + // GetE1 returns the e1 rule contexts. + GetE1() IConditionalOrContext + + // GetE2 returns the e2 rule contexts. + GetE2() IExprContext + + // SetE sets the e rule contexts. + SetE(IConditionalOrContext) + + // SetE1 sets the e1 rule contexts. + SetE1(IConditionalOrContext) + + // SetE2 sets the e2 rule contexts. + SetE2(IExprContext) + + // IsExprContext differentiates from other interfaces. + IsExprContext() +} + +type ExprContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + e IConditionalOrContext + op antlr.Token + e1 IConditionalOrContext + e2 IExprContext +} + +func NewEmptyExprContext() *ExprContext { + var p = new(ExprContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = CELParserRULE_expr + return p +} + +func (*ExprContext) IsExprContext() {} + +func NewExprContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ExprContext { + var p = new(ExprContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = CELParserRULE_expr + + return p +} + +func (s *ExprContext) GetParser() antlr.Parser { return s.parser } + +func (s *ExprContext) GetOp() antlr.Token { return s.op } + +func (s *ExprContext) SetOp(v antlr.Token) { s.op = v } + +func (s *ExprContext) GetE() IConditionalOrContext { return s.e } + +func (s *ExprContext) GetE1() IConditionalOrContext { return s.e1 } + +func (s *ExprContext) GetE2() IExprContext { return s.e2 } + +func (s *ExprContext) SetE(v IConditionalOrContext) { s.e = v } + +func (s *ExprContext) SetE1(v IConditionalOrContext) { s.e1 = v } + +func (s *ExprContext) SetE2(v IExprContext) { s.e2 = v } + +func (s *ExprContext) AllConditionalOr() []IConditionalOrContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IConditionalOrContext)(nil)).Elem()) + var tst = make([]IConditionalOrContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(IConditionalOrContext) + } + } + + return tst +} + +func (s *ExprContext) ConditionalOr(i int) IConditionalOrContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IConditionalOrContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(IConditionalOrContext) +} + +func (s *ExprContext) COLON() antlr.TerminalNode { + return s.GetToken(CELParserCOLON, 0) +} + +func (s *ExprContext) QUESTIONMARK() antlr.TerminalNode { + return s.GetToken(CELParserQUESTIONMARK, 0) +} + +func (s *ExprContext) Expr() IExprContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IExprContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IExprContext) +} + +func (s *ExprContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ExprContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ExprContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(CELListener); ok { + listenerT.EnterExpr(s) + } +} + +func (s *ExprContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(CELListener); ok { + listenerT.ExitExpr(s) + } +} + +func (s *ExprContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case CELVisitor: + return t.VisitExpr(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *CELParser) Expr() (localctx IExprContext) { + localctx = NewExprContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 2, CELParserRULE_expr) + var _la int + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(29) + + var _x = p.ConditionalOr() + + localctx.(*ExprContext).e = _x + } + p.SetState(35) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + if _la == CELParserQUESTIONMARK { + { + p.SetState(30) + + var _m = p.Match(CELParserQUESTIONMARK) + + localctx.(*ExprContext).op = _m + } + { + p.SetState(31) + + var _x = p.ConditionalOr() + + localctx.(*ExprContext).e1 = _x + } + { + p.SetState(32) + p.Match(CELParserCOLON) + } + { + p.SetState(33) + + var _x = p.Expr() + + localctx.(*ExprContext).e2 = _x + } + + } + + return localctx +} + +// IConditionalOrContext is an interface to support dynamic dispatch. +type IConditionalOrContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetS9 returns the s9 token. + GetS9() antlr.Token + + // SetS9 sets the s9 token. + SetS9(antlr.Token) + + // GetOps returns the ops token list. + GetOps() []antlr.Token + + // SetOps sets the ops token list. + SetOps([]antlr.Token) + + // GetE returns the e rule contexts. + GetE() IConditionalAndContext + + // Get_conditionalAnd returns the _conditionalAnd rule contexts. + Get_conditionalAnd() IConditionalAndContext + + // SetE sets the e rule contexts. + SetE(IConditionalAndContext) + + // Set_conditionalAnd sets the _conditionalAnd rule contexts. + Set_conditionalAnd(IConditionalAndContext) + + // GetE1 returns the e1 rule context list. + GetE1() []IConditionalAndContext + + // SetE1 sets the e1 rule context list. + SetE1([]IConditionalAndContext) + + // IsConditionalOrContext differentiates from other interfaces. + IsConditionalOrContext() +} + +type ConditionalOrContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + e IConditionalAndContext + s9 antlr.Token + ops []antlr.Token + _conditionalAnd IConditionalAndContext + e1 []IConditionalAndContext +} + +func NewEmptyConditionalOrContext() *ConditionalOrContext { + var p = new(ConditionalOrContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = CELParserRULE_conditionalOr + return p +} + +func (*ConditionalOrContext) IsConditionalOrContext() {} + +func NewConditionalOrContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ConditionalOrContext { + var p = new(ConditionalOrContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = CELParserRULE_conditionalOr + + return p +} + +func (s *ConditionalOrContext) GetParser() antlr.Parser { return s.parser } + +func (s *ConditionalOrContext) GetS9() antlr.Token { return s.s9 } + +func (s *ConditionalOrContext) SetS9(v antlr.Token) { s.s9 = v } + +func (s *ConditionalOrContext) GetOps() []antlr.Token { return s.ops } + +func (s *ConditionalOrContext) SetOps(v []antlr.Token) { s.ops = v } + +func (s *ConditionalOrContext) GetE() IConditionalAndContext { return s.e } + +func (s *ConditionalOrContext) Get_conditionalAnd() IConditionalAndContext { return s._conditionalAnd } + +func (s *ConditionalOrContext) SetE(v IConditionalAndContext) { s.e = v } + +func (s *ConditionalOrContext) Set_conditionalAnd(v IConditionalAndContext) { s._conditionalAnd = v } + +func (s *ConditionalOrContext) GetE1() []IConditionalAndContext { return s.e1 } + +func (s *ConditionalOrContext) SetE1(v []IConditionalAndContext) { s.e1 = v } + +func (s *ConditionalOrContext) AllConditionalAnd() []IConditionalAndContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IConditionalAndContext)(nil)).Elem()) + var tst = make([]IConditionalAndContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(IConditionalAndContext) + } + } + + return tst +} + +func (s *ConditionalOrContext) ConditionalAnd(i int) IConditionalAndContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IConditionalAndContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(IConditionalAndContext) +} + +func (s *ConditionalOrContext) AllLOGICAL_OR() []antlr.TerminalNode { + return s.GetTokens(CELParserLOGICAL_OR) +} + +func (s *ConditionalOrContext) LOGICAL_OR(i int) antlr.TerminalNode { + return s.GetToken(CELParserLOGICAL_OR, i) +} + +func (s *ConditionalOrContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ConditionalOrContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ConditionalOrContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(CELListener); ok { + listenerT.EnterConditionalOr(s) + } +} + +func (s *ConditionalOrContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(CELListener); ok { + listenerT.ExitConditionalOr(s) + } +} + +func (s *ConditionalOrContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case CELVisitor: + return t.VisitConditionalOr(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *CELParser) ConditionalOr() (localctx IConditionalOrContext) { + localctx = NewConditionalOrContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 4, CELParserRULE_conditionalOr) + var _la int + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(37) + + var _x = p.ConditionalAnd() + + localctx.(*ConditionalOrContext).e = _x + } + p.SetState(42) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + for _la == CELParserLOGICAL_OR { + { + p.SetState(38) + + var _m = p.Match(CELParserLOGICAL_OR) + + localctx.(*ConditionalOrContext).s9 = _m + } + localctx.(*ConditionalOrContext).ops = append(localctx.(*ConditionalOrContext).ops, localctx.(*ConditionalOrContext).s9) + { + p.SetState(39) + + var _x = p.ConditionalAnd() + + localctx.(*ConditionalOrContext)._conditionalAnd = _x + } + localctx.(*ConditionalOrContext).e1 = append(localctx.(*ConditionalOrContext).e1, localctx.(*ConditionalOrContext)._conditionalAnd) + + p.SetState(44) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + } + + return localctx +} + +// IConditionalAndContext is an interface to support dynamic dispatch. +type IConditionalAndContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetS8 returns the s8 token. + GetS8() antlr.Token + + // SetS8 sets the s8 token. + SetS8(antlr.Token) + + // GetOps returns the ops token list. + GetOps() []antlr.Token + + // SetOps sets the ops token list. + SetOps([]antlr.Token) + + // GetE returns the e rule contexts. + GetE() IRelationContext + + // Get_relation returns the _relation rule contexts. + Get_relation() IRelationContext + + // SetE sets the e rule contexts. + SetE(IRelationContext) + + // Set_relation sets the _relation rule contexts. + Set_relation(IRelationContext) + + // GetE1 returns the e1 rule context list. + GetE1() []IRelationContext + + // SetE1 sets the e1 rule context list. + SetE1([]IRelationContext) + + // IsConditionalAndContext differentiates from other interfaces. + IsConditionalAndContext() +} + +type ConditionalAndContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + e IRelationContext + s8 antlr.Token + ops []antlr.Token + _relation IRelationContext + e1 []IRelationContext +} + +func NewEmptyConditionalAndContext() *ConditionalAndContext { + var p = new(ConditionalAndContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = CELParserRULE_conditionalAnd + return p +} + +func (*ConditionalAndContext) IsConditionalAndContext() {} + +func NewConditionalAndContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ConditionalAndContext { + var p = new(ConditionalAndContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = CELParserRULE_conditionalAnd + + return p +} + +func (s *ConditionalAndContext) GetParser() antlr.Parser { return s.parser } + +func (s *ConditionalAndContext) GetS8() antlr.Token { return s.s8 } + +func (s *ConditionalAndContext) SetS8(v antlr.Token) { s.s8 = v } + +func (s *ConditionalAndContext) GetOps() []antlr.Token { return s.ops } + +func (s *ConditionalAndContext) SetOps(v []antlr.Token) { s.ops = v } + +func (s *ConditionalAndContext) GetE() IRelationContext { return s.e } + +func (s *ConditionalAndContext) Get_relation() IRelationContext { return s._relation } + +func (s *ConditionalAndContext) SetE(v IRelationContext) { s.e = v } + +func (s *ConditionalAndContext) Set_relation(v IRelationContext) { s._relation = v } + +func (s *ConditionalAndContext) GetE1() []IRelationContext { return s.e1 } + +func (s *ConditionalAndContext) SetE1(v []IRelationContext) { s.e1 = v } + +func (s *ConditionalAndContext) AllRelation() []IRelationContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IRelationContext)(nil)).Elem()) + var tst = make([]IRelationContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(IRelationContext) + } + } + + return tst +} + +func (s *ConditionalAndContext) Relation(i int) IRelationContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IRelationContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(IRelationContext) +} + +func (s *ConditionalAndContext) AllLOGICAL_AND() []antlr.TerminalNode { + return s.GetTokens(CELParserLOGICAL_AND) +} + +func (s *ConditionalAndContext) LOGICAL_AND(i int) antlr.TerminalNode { + return s.GetToken(CELParserLOGICAL_AND, i) +} + +func (s *ConditionalAndContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ConditionalAndContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ConditionalAndContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(CELListener); ok { + listenerT.EnterConditionalAnd(s) + } +} + +func (s *ConditionalAndContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(CELListener); ok { + listenerT.ExitConditionalAnd(s) + } +} + +func (s *ConditionalAndContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case CELVisitor: + return t.VisitConditionalAnd(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *CELParser) ConditionalAnd() (localctx IConditionalAndContext) { + localctx = NewConditionalAndContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 6, CELParserRULE_conditionalAnd) + var _la int + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(45) + + var _x = p.relation(0) + + localctx.(*ConditionalAndContext).e = _x + } + p.SetState(50) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + for _la == CELParserLOGICAL_AND { + { + p.SetState(46) + + var _m = p.Match(CELParserLOGICAL_AND) + + localctx.(*ConditionalAndContext).s8 = _m + } + localctx.(*ConditionalAndContext).ops = append(localctx.(*ConditionalAndContext).ops, localctx.(*ConditionalAndContext).s8) + { + p.SetState(47) + + var _x = p.relation(0) + + localctx.(*ConditionalAndContext)._relation = _x + } + localctx.(*ConditionalAndContext).e1 = append(localctx.(*ConditionalAndContext).e1, localctx.(*ConditionalAndContext)._relation) + + p.SetState(52) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + } + + return localctx +} + +// IRelationContext is an interface to support dynamic dispatch. +type IRelationContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetOp returns the op token. + GetOp() antlr.Token + + // SetOp sets the op token. + SetOp(antlr.Token) + + // IsRelationContext differentiates from other interfaces. + IsRelationContext() +} + +type RelationContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + op antlr.Token +} + +func NewEmptyRelationContext() *RelationContext { + var p = new(RelationContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = CELParserRULE_relation + return p +} + +func (*RelationContext) IsRelationContext() {} + +func NewRelationContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *RelationContext { + var p = new(RelationContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = CELParserRULE_relation + + return p +} + +func (s *RelationContext) GetParser() antlr.Parser { return s.parser } + +func (s *RelationContext) GetOp() antlr.Token { return s.op } + +func (s *RelationContext) SetOp(v antlr.Token) { s.op = v } + +func (s *RelationContext) Calc() ICalcContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ICalcContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ICalcContext) +} + +func (s *RelationContext) AllRelation() []IRelationContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IRelationContext)(nil)).Elem()) + var tst = make([]IRelationContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(IRelationContext) + } + } + + return tst +} + +func (s *RelationContext) Relation(i int) IRelationContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IRelationContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(IRelationContext) +} + +func (s *RelationContext) LESS() antlr.TerminalNode { + return s.GetToken(CELParserLESS, 0) +} + +func (s *RelationContext) LESS_EQUALS() antlr.TerminalNode { + return s.GetToken(CELParserLESS_EQUALS, 0) +} + +func (s *RelationContext) GREATER_EQUALS() antlr.TerminalNode { + return s.GetToken(CELParserGREATER_EQUALS, 0) +} + +func (s *RelationContext) GREATER() antlr.TerminalNode { + return s.GetToken(CELParserGREATER, 0) +} + +func (s *RelationContext) EQUALS() antlr.TerminalNode { + return s.GetToken(CELParserEQUALS, 0) +} + +func (s *RelationContext) NOT_EQUALS() antlr.TerminalNode { + return s.GetToken(CELParserNOT_EQUALS, 0) +} + +func (s *RelationContext) IN() antlr.TerminalNode { + return s.GetToken(CELParserIN, 0) +} + +func (s *RelationContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *RelationContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *RelationContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(CELListener); ok { + listenerT.EnterRelation(s) + } +} + +func (s *RelationContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(CELListener); ok { + listenerT.ExitRelation(s) + } +} + +func (s *RelationContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case CELVisitor: + return t.VisitRelation(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *CELParser) Relation() (localctx IRelationContext) { + return p.relation(0) +} + +func (p *CELParser) relation(_p int) (localctx IRelationContext) { + var _parentctx antlr.ParserRuleContext = p.GetParserRuleContext() + _parentState := p.GetState() + localctx = NewRelationContext(p, p.GetParserRuleContext(), _parentState) + var _prevctx IRelationContext = localctx + var _ antlr.ParserRuleContext = _prevctx // TODO: To prevent unused variable warning. + _startState := 8 + p.EnterRecursionRule(localctx, 8, CELParserRULE_relation, _p) + var _la int + + defer func() { + p.UnrollRecursionContexts(_parentctx) + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + var _alt int + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(54) + p.calc(0) + } + + p.GetParserRuleContext().SetStop(p.GetTokenStream().LT(-1)) + p.SetState(61) + p.GetErrorHandler().Sync(p) + _alt = p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 3, p.GetParserRuleContext()) + + for _alt != 2 && _alt != antlr.ATNInvalidAltNumber { + if _alt == 1 { + if p.GetParseListeners() != nil { + p.TriggerExitRuleEvent() + } + _prevctx = localctx + localctx = NewRelationContext(p, _parentctx, _parentState) + p.PushNewRecursionContext(localctx, _startState, CELParserRULE_relation) + p.SetState(56) + + if !(p.Precpred(p.GetParserRuleContext(), 1)) { + panic(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 1)", "")) + } + { + p.SetState(57) + + var _lt = p.GetTokenStream().LT(1) + + localctx.(*RelationContext).op = _lt + + _la = p.GetTokenStream().LA(1) + + if !(((_la)&-(0x1f+1)) == 0 && ((1<= c.buf.Len() { + panic("cannot consume EOF") + } + c.pos++ +} + +// LA implements (antlr.CharStream).LA. +func (c *charStream) LA(offset int) int { + if offset == 0 { + return 0 + } + if offset < 0 { + offset++ + } + pos := c.pos + offset - 1 + if pos < 0 || pos >= c.buf.Len() { + return antlr.TokenEOF + } + return int(c.buf.Get(pos)) +} + +// LT mimics (*antlr.InputStream).LT. +func (c *charStream) LT(offset int) int { + return c.LA(offset) +} + +// Mark implements (antlr.CharStream).Mark. +func (c *charStream) Mark() int { + return -1 +} + +// Release implements (antlr.CharStream).Release. +func (c *charStream) Release(marker int) {} + +// Index implements (antlr.CharStream).Index. +func (c *charStream) Index() int { + return c.pos +} + +// Seek implements (antlr.CharStream).Seek. +func (c *charStream) Seek(index int) { + if index <= c.pos { + c.pos = index + return + } + if index < c.buf.Len() { + c.pos = index + } else { + c.pos = c.buf.Len() + } +} + +// Size implements (antlr.CharStream).Size. +func (c *charStream) Size() int { + return c.buf.Len() +} + +// GetSourceName implements (antlr.CharStream).GetSourceName. +func (c *charStream) GetSourceName() string { + return c.src +} + +// GetText implements (antlr.CharStream).GetText. +func (c *charStream) GetText(start, stop int) string { + if stop >= c.buf.Len() { + stop = c.buf.Len() - 1 + } + if start >= c.buf.Len() { + return "" + } + return c.buf.Slice(start, stop+1) +} + +// GetTextFromTokens implements (antlr.CharStream).GetTextFromTokens. +func (c *charStream) GetTextFromTokens(start, stop antlr.Token) string { + if start != nil && stop != nil { + return c.GetText(start.GetTokenIndex(), stop.GetTokenIndex()) + } + return "" +} + +// GetTextFromInterval implements (antlr.CharStream).GetTextFromInterval. +func (c *charStream) GetTextFromInterval(i *antlr.Interval) string { + return c.GetText(i.Start, i.Stop) +} + +// String mimics (*antlr.InputStream).String. +func (c *charStream) String() string { + return c.buf.Slice(0, c.buf.Len()) +} + +var _ antlr.CharStream = &charStream{} + +func newCharStream(buf runes.Buffer, desc string) antlr.CharStream { + return &charStream{ + buf: buf, + src: desc, + } +} diff --git a/vendor/github.com/google/cel-go/parser/macro.go b/vendor/github.com/google/cel-go/parser/macro.go new file mode 100644 index 0000000000..baeddd94ea --- /dev/null +++ b/vendor/github.com/google/cel-go/parser/macro.go @@ -0,0 +1,386 @@ +// Copyright 2018 Google LLC +// +// 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. + +package parser + +import ( + "fmt" + + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/operators" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +// TODO: Consider moving macros to common. + +// NewGlobalMacro creates a Macro for a global function with the specified arg count. +func NewGlobalMacro(function string, argCount int, expander MacroExpander) Macro { + return ¯o{ + function: function, + argCount: argCount, + expander: expander} +} + +// NewReceiverMacro creates a Macro for a receiver function matching the specified arg count. +func NewReceiverMacro(function string, argCount int, expander MacroExpander) Macro { + return ¯o{ + function: function, + argCount: argCount, + expander: expander, + receiverStyle: true} +} + +// NewGlobalVarArgMacro creates a Macro for a global function with a variable arg count. +func NewGlobalVarArgMacro(function string, expander MacroExpander) Macro { + return ¯o{ + function: function, + expander: expander, + varArgStyle: true} +} + +// NewReceiverVarArgMacro creates a Macro for a receiver function matching a variable arg +// count. +func NewReceiverVarArgMacro(function string, expander MacroExpander) Macro { + return ¯o{ + function: function, + expander: expander, + receiverStyle: true, + varArgStyle: true} +} + +// Macro interface for describing the function signature to match and the MacroExpander to apply. +// +// Note: when a Macro should apply to multiple overloads (based on arg count) of a given function, +// a Macro should be created per arg-count. +type Macro interface { + // Function name to match. + Function() string + + // ArgCount for the function call. + // + // When the macro is a var-arg style macro, the return value will be zero, but the MacroKey + // will contain a `*` where the arg count would have been. + ArgCount() int + + // IsReceiverStyle returns true if the macro matches a receiver style call. + IsReceiverStyle() bool + + // MacroKey returns the macro signatures accepted by this macro. + // + // Format: `::`. + // + // When the macros is a var-arg style macro, the `arg-count` value is represented as a `*`. + MacroKey() string + + // Expander returns the MacroExpander to apply when the macro key matches the parsed call + // signature. + Expander() MacroExpander +} + +// Macro type which declares the function name and arg count expected for the +// macro, as well as a macro expansion function. +type macro struct { + function string + receiverStyle bool + varArgStyle bool + argCount int + expander MacroExpander +} + +// Function returns the macro's function name (i.e. the function whose syntax it mimics). +func (m *macro) Function() string { + return m.function +} + +// ArgCount returns the number of arguments the macro expects. +func (m *macro) ArgCount() int { + return m.argCount +} + +// IsReceiverStyle returns whether the macro is receiver style. +func (m *macro) IsReceiverStyle() bool { + return m.receiverStyle +} + +// Expander implements the Macro interface method. +func (m *macro) Expander() MacroExpander { + return m.expander +} + +// MacroKey implements the Macro interface method. +func (m *macro) MacroKey() string { + if m.varArgStyle { + return makeVarArgMacroKey(m.function, m.receiverStyle) + } + return makeMacroKey(m.function, m.argCount, m.receiverStyle) +} + +func makeMacroKey(name string, args int, receiverStyle bool) string { + return fmt.Sprintf("%s:%d:%v", name, args, receiverStyle) +} + +func makeVarArgMacroKey(name string, receiverStyle bool) string { + return fmt.Sprintf("%s:*:%v", name, receiverStyle) +} + +// MacroExpander converts the target and args of a function call that matches a Macro. +// +// Note: when the Macros.IsReceiverStyle() is true, the target argument will be nil. +type MacroExpander func(eh ExprHelper, + target *exprpb.Expr, + args []*exprpb.Expr) (*exprpb.Expr, *common.Error) + +// ExprHelper assists with the manipulation of proto-based Expr values in a manner which is +// consistent with the source position and expression id generation code leveraged by both +// the parser and type-checker. +type ExprHelper interface { + // LiteralBool creates an Expr value for a bool literal. + LiteralBool(value bool) *exprpb.Expr + + // LiteralBytes creates an Expr value for a byte literal. + LiteralBytes(value []byte) *exprpb.Expr + + // LiteralDouble creates an Expr value for double literal. + LiteralDouble(value float64) *exprpb.Expr + + // LiteralInt creates an Expr value for an int literal. + LiteralInt(value int64) *exprpb.Expr + + // LiteralString creates am Expr value for a string literal. + LiteralString(value string) *exprpb.Expr + + // LiteralUint creates an Expr value for a uint literal. + LiteralUint(value uint64) *exprpb.Expr + + // NewList creates a CreateList instruction where the list is comprised of the optional set + // of elements provided as arguments. + NewList(elems ...*exprpb.Expr) *exprpb.Expr + + // NewMap creates a CreateStruct instruction for a map where the map is comprised of the + // optional set of key, value entries. + NewMap(entries ...*exprpb.Expr_CreateStruct_Entry) *exprpb.Expr + + // NewMapEntry creates a Map Entry for the key, value pair. + NewMapEntry(key *exprpb.Expr, val *exprpb.Expr) *exprpb.Expr_CreateStruct_Entry + + // NewObject creates a CreateStruct instruction for an object with a given type name and + // optional set of field initializers. + NewObject(typeName string, fieldInits ...*exprpb.Expr_CreateStruct_Entry) *exprpb.Expr + + // NewObjectFieldInit creates a new Object field initializer from the field name and value. + NewObjectFieldInit(field string, init *exprpb.Expr) *exprpb.Expr_CreateStruct_Entry + + // Fold creates a fold comprehension instruction. + // + // - iterVar is the iteration variable name. + // - iterRange represents the expression that resolves to a list or map where the elements or + // keys (respectively) will be iterated over. + // - accuVar is the accumulation variable name, typically parser.AccumulatorName. + // - accuInit is the initial expression whose value will be set for the accuVar prior to + // folding. + // - condition is the expression to test to determine whether to continue folding. + // - step is the expression to evaluation at the conclusion of a single fold iteration. + // - result is the computation to evaluate at the conclusion of the fold. + // + // The accuVar should not shadow variable names that you would like to reference within the + // environment in the step and condition expressions. Presently, the name __result__ is commonly + // used by built-in macros but this may change in the future. + Fold(iterVar string, + iterRange *exprpb.Expr, + accuVar string, + accuInit *exprpb.Expr, + condition *exprpb.Expr, + step *exprpb.Expr, + result *exprpb.Expr) *exprpb.Expr + + // Ident creates an identifier Expr value. + Ident(name string) *exprpb.Expr + + // GlobalCall creates a function call Expr value for a global (free) function. + GlobalCall(function string, args ...*exprpb.Expr) *exprpb.Expr + + // ReceiverCall creates a function call Expr value for a receiver-style function. + ReceiverCall(function string, target *exprpb.Expr, args ...*exprpb.Expr) *exprpb.Expr + + // PresenceTest creates a Select TestOnly Expr value for modelling has() semantics. + PresenceTest(operand *exprpb.Expr, field string) *exprpb.Expr + + // Select create a field traversal Expr value. + Select(operand *exprpb.Expr, field string) *exprpb.Expr + + // OffsetLocation returns the Location of the expression identifier. + OffsetLocation(exprID int64) common.Location +} + +var ( + // AllMacros includes the list of all spec-supported macros. + AllMacros = []Macro{ + // The macro "has(m.f)" which tests the presence of a field, avoiding the need to specify + // the field as a string. + NewGlobalMacro(operators.Has, 1, makeHas), + + // The macro "range.all(var, predicate)", which is true if for all elements in range the + // predicate holds. + NewReceiverMacro(operators.All, 2, makeAll), + + // The macro "range.exists(var, predicate)", which is true if for at least one element in + // range the predicate holds. + NewReceiverMacro(operators.Exists, 2, makeExists), + + // The macro "range.exists_one(var, predicate)", which is true if for exactly one element + // in range the predicate holds. + NewReceiverMacro(operators.ExistsOne, 2, makeExistsOne), + + // The macro "range.map(var, function)", applies the function to the vars in the range. + NewReceiverMacro(operators.Map, 2, makeMap), + + // The macro "range.map(var, predicate, function)", applies the function to the vars in + // the range for which the predicate holds true. The other variables are filtered out. + NewReceiverMacro(operators.Map, 3, makeMap), + + // The macro "range.filter(var, predicate)", filters out the variables for which the + // predicate is false. + NewReceiverMacro(operators.Filter, 2, makeFilter), + } + + // NoMacros list. + NoMacros = []Macro{} +) + +// AccumulatorName is the traditional variable name assigned to the fold accumulator variable. +const AccumulatorName = "__result__" + +type quantifierKind int + +const ( + quantifierAll quantifierKind = iota + quantifierExists + quantifierExistsOne +) + +func makeAll(eh ExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) { + return makeQuantifier(quantifierAll, eh, target, args) +} + +func makeExists(eh ExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) { + return makeQuantifier(quantifierExists, eh, target, args) +} + +func makeExistsOne(eh ExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) { + return makeQuantifier(quantifierExistsOne, eh, target, args) +} + +func makeQuantifier(kind quantifierKind, eh ExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) { + v, found := extractIdent(args[0]) + if !found { + location := eh.OffsetLocation(args[0].GetId()) + return nil, &common.Error{ + Message: "argument must be a simple name", + Location: location} + } + accuIdent := func() *exprpb.Expr { + return eh.Ident(AccumulatorName) + } + + var init *exprpb.Expr + var condition *exprpb.Expr + var step *exprpb.Expr + var result *exprpb.Expr + switch kind { + case quantifierAll: + init = eh.LiteralBool(true) + condition = eh.GlobalCall(operators.NotStrictlyFalse, accuIdent()) + step = eh.GlobalCall(operators.LogicalAnd, accuIdent(), args[1]) + result = accuIdent() + case quantifierExists: + init = eh.LiteralBool(false) + condition = eh.GlobalCall( + operators.NotStrictlyFalse, + eh.GlobalCall(operators.LogicalNot, accuIdent())) + step = eh.GlobalCall(operators.LogicalOr, accuIdent(), args[1]) + result = accuIdent() + case quantifierExistsOne: + zeroExpr := eh.LiteralInt(0) + oneExpr := eh.LiteralInt(1) + init = zeroExpr + condition = eh.LiteralBool(true) + step = eh.GlobalCall(operators.Conditional, args[1], + eh.GlobalCall(operators.Add, accuIdent(), oneExpr), accuIdent()) + result = eh.GlobalCall(operators.Equals, accuIdent(), oneExpr) + default: + return nil, &common.Error{Message: fmt.Sprintf("unrecognized quantifier '%v'", kind)} + } + return eh.Fold(v, target, AccumulatorName, init, condition, step, result), nil +} + +func makeMap(eh ExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) { + v, found := extractIdent(args[0]) + if !found { + return nil, &common.Error{Message: "argument is not an identifier"} + } + + var fn *exprpb.Expr + var filter *exprpb.Expr + + if len(args) == 3 { + filter = args[1] + fn = args[2] + } else { + filter = nil + fn = args[1] + } + + accuExpr := eh.Ident(AccumulatorName) + init := eh.NewList() + condition := eh.LiteralBool(true) + // TODO: use compiler internal method for faster, stateful add. + step := eh.GlobalCall(operators.Add, accuExpr, eh.NewList(fn)) + + if filter != nil { + step = eh.GlobalCall(operators.Conditional, filter, step, accuExpr) + } + return eh.Fold(v, target, AccumulatorName, init, condition, step, accuExpr), nil +} + +func makeFilter(eh ExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) { + v, found := extractIdent(args[0]) + if !found { + return nil, &common.Error{Message: "argument is not an identifier"} + } + + filter := args[1] + accuExpr := eh.Ident(AccumulatorName) + init := eh.NewList() + condition := eh.LiteralBool(true) + // TODO: use compiler internal method for faster, stateful add. + step := eh.GlobalCall(operators.Add, accuExpr, eh.NewList(args[0])) + step = eh.GlobalCall(operators.Conditional, filter, step, accuExpr) + return eh.Fold(v, target, AccumulatorName, init, condition, step, accuExpr), nil +} + +func extractIdent(e *exprpb.Expr) (string, bool) { + switch e.ExprKind.(type) { + case *exprpb.Expr_IdentExpr: + return e.GetIdentExpr().GetName(), true + } + return "", false +} + +func makeHas(eh ExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) { + if s, ok := args[0].ExprKind.(*exprpb.Expr_SelectExpr); ok { + return eh.PresenceTest(s.SelectExpr.GetOperand(), s.SelectExpr.GetField()), nil + } + return nil, &common.Error{Message: "invalid argument to has() macro"} +} diff --git a/vendor/github.com/google/cel-go/parser/options.go b/vendor/github.com/google/cel-go/parser/options.go new file mode 100644 index 0000000000..b50686a912 --- /dev/null +++ b/vendor/github.com/google/cel-go/parser/options.go @@ -0,0 +1,104 @@ +// Copyright 2021 Google LLC +// +// 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. + +package parser + +import "fmt" + +type options struct { + maxRecursionDepth int + errorRecoveryTokenLookaheadLimit int + errorRecoveryLimit int + expressionSizeCodePointLimit int + macros map[string]Macro + populateMacroCalls bool +} + +// Option configures the behavior of the parser. +type Option func(*options) error + +// MaxRecursionDepth limits the maximum depth the parser will attempt to parse the expression before giving up. +func MaxRecursionDepth(limit int) Option { + return func(opts *options) error { + if limit < -1 { + return fmt.Errorf("max recursion depth must be greater than or equal to -1: %d", limit) + } + opts.maxRecursionDepth = limit + return nil + } +} + +// ErrorRecoveryLookaheadTokenLimit limits the number of lexer tokens that may be considered during error recovery. +// +// Error recovery often involves looking ahead in the input to determine if there's a point at which parsing may +// successfully resume. In some pathological cases, the parser can look through quite a large set of input which +// in turn generates a lot of back-tracking and performance degredation. +// +// The limit must be > 1, and is recommended to be less than the default of 256. +func ErrorRecoveryLookaheadTokenLimit(limit int) Option { + return func(opts *options) error { + if limit < 1 { + return fmt.Errorf("error recovery lookahead token limit must be at least 1: %d", limit) + } + opts.errorRecoveryTokenLookaheadLimit = limit + return nil + } +} + +// ErrorRecoveryLimit limits the number of attempts the parser will perform to recover from an error. +func ErrorRecoveryLimit(limit int) Option { + return func(opts *options) error { + if limit < -1 { + return fmt.Errorf("error recovery limit must be greater than or equal to -1: %d", limit) + } + opts.errorRecoveryLimit = limit + return nil + } +} + +// ExpressionSizeCodePointLimit is an option which limits the maximum code point count of an +// expression. +func ExpressionSizeCodePointLimit(expressionSizeCodePointLimit int) Option { + return func(opts *options) error { + if expressionSizeCodePointLimit < -1 { + return fmt.Errorf("expression size code point limit must be greater than or equal to -1: %d", expressionSizeCodePointLimit) + } + opts.expressionSizeCodePointLimit = expressionSizeCodePointLimit + return nil + } +} + +// Macros adds the given macros to the parser. +func Macros(macros ...Macro) Option { + return func(opts *options) error { + for _, m := range macros { + if m != nil { + if opts.macros == nil { + opts.macros = make(map[string]Macro) + } + opts.macros[m.MacroKey()] = m + } + } + return nil + } +} + +// PopulateMacroCalls ensures that the original call signatures replaced by expanded macros +// are preserved in the `SourceInfo` of parse result. +func PopulateMacroCalls(populateMacroCalls bool) Option { + return func(opts *options) error { + opts.populateMacroCalls = populateMacroCalls + return nil + } +} diff --git a/vendor/github.com/google/cel-go/parser/parser.go b/vendor/github.com/google/cel-go/parser/parser.go new file mode 100644 index 0000000000..70dbdd7cf7 --- /dev/null +++ b/vendor/github.com/google/cel-go/parser/parser.go @@ -0,0 +1,897 @@ +// Copyright 2018 Google LLC +// +// 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. + +// Package parser declares an expression parser with support for macro +// expansion. +package parser + +import ( + "fmt" + "strconv" + "strings" + "sync" + + "github.com/antlr/antlr4/runtime/Go/antlr" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/runes" + "github.com/google/cel-go/parser/gen" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" + structpb "google.golang.org/protobuf/types/known/structpb" +) + +// Parser encapsulates the context necessary to perform parsing for different expressions. +type Parser struct { + options +} + +// NewParser builds and returns a new Parser using the provided options. +func NewParser(opts ...Option) (*Parser, error) { + p := &Parser{} + for _, opt := range opts { + if err := opt(&p.options); err != nil { + return nil, err + } + } + if p.maxRecursionDepth == 0 { + p.maxRecursionDepth = 200 + } + if p.maxRecursionDepth == -1 { + p.maxRecursionDepth = int((^uint(0)) >> 1) + } + if p.errorRecoveryTokenLookaheadLimit == 0 { + p.errorRecoveryTokenLookaheadLimit = 256 + } + if p.errorRecoveryLimit == 0 { + p.errorRecoveryLimit = 30 + } + if p.errorRecoveryLimit == -1 { + p.errorRecoveryLimit = int((^uint(0)) >> 1) + } + if p.expressionSizeCodePointLimit == 0 { + p.expressionSizeCodePointLimit = 100_000 + } + if p.expressionSizeCodePointLimit == -1 { + p.expressionSizeCodePointLimit = int((^uint(0)) >> 1) + } + // Bool is false by default, so populateMacroCalls will be false by default + return p, nil +} + +// mustNewParser does the work of NewParser and panics if an error occurs. +// +// This function is only intended for internal use and is for backwards compatibility in Parse and +// ParseWithMacros, where we know the options will result in an error. +func mustNewParser(opts ...Option) *Parser { + p, err := NewParser(opts...) + if err != nil { + panic(err) + } + return p +} + +// Parse parses the expression represented by source and returns the result. +func (p *Parser) Parse(source common.Source) (*exprpb.ParsedExpr, *common.Errors) { + impl := parser{ + errors: &parseErrors{common.NewErrors(source)}, + helper: newParserHelper(source), + macros: p.macros, + maxRecursionDepth: p.maxRecursionDepth, + errorRecoveryLimit: p.errorRecoveryLimit, + errorRecoveryLookaheadTokenLimit: p.errorRecoveryTokenLookaheadLimit, + populateMacroCalls: p.populateMacroCalls, + } + buf, ok := source.(runes.Buffer) + if !ok { + buf = runes.NewBuffer(source.Content()) + } + var e *exprpb.Expr + if buf.Len() > p.expressionSizeCodePointLimit { + e = impl.reportError(common.NoLocation, + "expression code point size exceeds limit: size: %d, limit %d", + buf.Len(), p.expressionSizeCodePointLimit) + } else { + e = impl.parse(buf, source.Description()) + } + return &exprpb.ParsedExpr{ + Expr: e, + SourceInfo: impl.helper.getSourceInfo(), + }, impl.errors.Errors +} + +// reservedIds are not legal to use as variables. We exclude them post-parse, as they *are* valid +// field names for protos, and it would complicate the grammar to distinguish the cases. +var reservedIds = map[string]struct{}{ + "as": {}, + "break": {}, + "const": {}, + "continue": {}, + "else": {}, + "false": {}, + "for": {}, + "function": {}, + "if": {}, + "import": {}, + "in": {}, + "let": {}, + "loop": {}, + "package": {}, + "namespace": {}, + "null": {}, + "return": {}, + "true": {}, + "var": {}, + "void": {}, + "while": {}, +} + +// Parse converts a source input a parsed expression. +// This function calls ParseWithMacros with AllMacros. +// +// Deprecated: Use NewParser().Parse() instead. +func Parse(source common.Source) (*exprpb.ParsedExpr, *common.Errors) { + return mustNewParser(Macros(AllMacros...)).Parse(source) +} + +type recursionError struct { + message string +} + +// Error implements error. +func (re *recursionError) Error() string { + return re.message +} + +var _ error = &recursionError{} + +type recursionListener struct { + maxDepth int + ruleTypeDepth map[int]*int +} + +func (rl *recursionListener) VisitTerminal(node antlr.TerminalNode) {} + +func (rl *recursionListener) VisitErrorNode(node antlr.ErrorNode) {} + +func (rl *recursionListener) EnterEveryRule(ctx antlr.ParserRuleContext) { + if ctx == nil { + return + } + ruleIndex := ctx.GetRuleIndex() + depth, found := rl.ruleTypeDepth[ruleIndex] + if !found { + var counter = 1 + rl.ruleTypeDepth[ruleIndex] = &counter + depth = &counter + } else { + *depth++ + } + if *depth >= rl.maxDepth { + panic(&recursionError{ + message: fmt.Sprintf("expression recursion limit exceeded: %d", rl.maxDepth), + }) + } +} + +func (rl *recursionListener) ExitEveryRule(ctx antlr.ParserRuleContext) { + if ctx == nil { + return + } + ruleIndex := ctx.GetRuleIndex() + if depth, found := rl.ruleTypeDepth[ruleIndex]; found && *depth > 0 { + *depth-- + } +} + +var _ antlr.ParseTreeListener = &recursionListener{} + +type recoveryLimitError struct { + message string +} + +// Error implements error. +func (rl *recoveryLimitError) Error() string { + return rl.message +} + +type lookaheadLimitError struct { + message string +} + +func (ll *lookaheadLimitError) Error() string { + return ll.message +} + +var _ error = &recoveryLimitError{} + +type recoveryLimitErrorStrategy struct { + *antlr.DefaultErrorStrategy + errorRecoveryLimit int + errorRecoveryTokenLookaheadLimit int + recoveryAttempts int +} + +type lookaheadConsumer struct { + antlr.Parser + errorRecoveryTokenLookaheadLimit int + lookaheadAttempts int +} + +func (lc *lookaheadConsumer) Consume() antlr.Token { + if lc.lookaheadAttempts >= lc.errorRecoveryTokenLookaheadLimit { + panic(&lookaheadLimitError{ + message: fmt.Sprintf("error recovery token lookahead limit exceeded: %d", lc.errorRecoveryTokenLookaheadLimit), + }) + } + lc.lookaheadAttempts++ + return lc.Parser.Consume() +} + +func (rl *recoveryLimitErrorStrategy) Recover(recognizer antlr.Parser, e antlr.RecognitionException) { + rl.checkAttempts(recognizer) + lc := &lookaheadConsumer{Parser: recognizer, errorRecoveryTokenLookaheadLimit: rl.errorRecoveryTokenLookaheadLimit} + rl.DefaultErrorStrategy.Recover(lc, e) +} + +func (rl *recoveryLimitErrorStrategy) RecoverInline(recognizer antlr.Parser) antlr.Token { + rl.checkAttempts(recognizer) + lc := &lookaheadConsumer{Parser: recognizer, errorRecoveryTokenLookaheadLimit: rl.errorRecoveryTokenLookaheadLimit} + return rl.DefaultErrorStrategy.RecoverInline(lc) +} + +func (rl *recoveryLimitErrorStrategy) checkAttempts(recognizer antlr.Parser) { + if rl.recoveryAttempts == rl.errorRecoveryLimit { + rl.recoveryAttempts++ + msg := fmt.Sprintf("error recovery attempt limit exceeded: %d", rl.errorRecoveryLimit) + recognizer.NotifyErrorListeners(msg, nil, nil) + panic(&recoveryLimitError{ + message: msg, + }) + } + rl.recoveryAttempts++ +} + +var _ antlr.ErrorStrategy = &recoveryLimitErrorStrategy{} + +type parser struct { + gen.BaseCELVisitor + errors *parseErrors + helper *parserHelper + macros map[string]Macro + maxRecursionDepth int + errorRecoveryLimit int + errorRecoveryLookaheadTokenLimit int + populateMacroCalls bool +} + +var ( + _ gen.CELVisitor = (*parser)(nil) + + lexerPool *sync.Pool = &sync.Pool{ + New: func() interface{} { + l := gen.NewCELLexer(nil) + l.RemoveErrorListeners() + return l + }, + } + + parserPool *sync.Pool = &sync.Pool{ + New: func() interface{} { + p := gen.NewCELParser(nil) + p.RemoveErrorListeners() + return p + }, + } +) + +func (p *parser) parse(expr runes.Buffer, desc string) *exprpb.Expr { + // TODO: get rid of these pools once https://github.com/antlr/antlr4/pull/3571 is in a release + lexer := lexerPool.Get().(*gen.CELLexer) + prsr := parserPool.Get().(*gen.CELParser) + + // Unfortunately ANTLR Go runtime is missing (*antlr.BaseParser).RemoveParseListeners, so this is + // good enough until that is exported. + prsrListener := &recursionListener{ + maxDepth: p.maxRecursionDepth, + ruleTypeDepth: map[int]*int{}, + } + + defer func() { + // Reset the lexer and parser before putting them back in the pool. + lexer.RemoveErrorListeners() + prsr.RemoveParseListener(prsrListener) + prsr.RemoveErrorListeners() + lexer.SetInputStream(nil) + prsr.SetInputStream(nil) + lexerPool.Put(lexer) + parserPool.Put(prsr) + }() + + lexer.SetInputStream(newCharStream(expr, desc)) + prsr.SetInputStream(antlr.NewCommonTokenStream(lexer, 0)) + + lexer.AddErrorListener(p) + prsr.AddErrorListener(p) + prsr.AddParseListener(prsrListener) + + prsr.SetErrorHandler(&recoveryLimitErrorStrategy{ + DefaultErrorStrategy: antlr.NewDefaultErrorStrategy(), + errorRecoveryLimit: p.errorRecoveryLimit, + errorRecoveryTokenLookaheadLimit: p.errorRecoveryLookaheadTokenLimit, + }) + + defer func() { + if val := recover(); val != nil { + switch err := val.(type) { + case *lookaheadLimitError: + p.errors.ReportError(common.NoLocation, err.Error()) + case *recursionError: + p.errors.ReportError(common.NoLocation, err.Error()) + case *recoveryLimitError: + // do nothing, listeners already notified and error reported. + default: + panic(val) + } + } + }() + + return p.Visit(prsr.Start()).(*exprpb.Expr) +} + +// Visitor implementations. +func (p *parser) Visit(tree antlr.ParseTree) interface{} { + switch tree.(type) { + case *gen.StartContext: + return p.VisitStart(tree.(*gen.StartContext)) + case *gen.ExprContext: + return p.VisitExpr(tree.(*gen.ExprContext)) + case *gen.ConditionalAndContext: + return p.VisitConditionalAnd(tree.(*gen.ConditionalAndContext)) + case *gen.ConditionalOrContext: + return p.VisitConditionalOr(tree.(*gen.ConditionalOrContext)) + case *gen.RelationContext: + return p.VisitRelation(tree.(*gen.RelationContext)) + case *gen.CalcContext: + return p.VisitCalc(tree.(*gen.CalcContext)) + case *gen.LogicalNotContext: + return p.VisitLogicalNot(tree.(*gen.LogicalNotContext)) + case *gen.MemberExprContext: + return p.VisitMemberExpr(tree.(*gen.MemberExprContext)) + case *gen.PrimaryExprContext: + return p.VisitPrimaryExpr(tree.(*gen.PrimaryExprContext)) + case *gen.SelectOrCallContext: + return p.VisitSelectOrCall(tree.(*gen.SelectOrCallContext)) + case *gen.MapInitializerListContext: + return p.VisitMapInitializerList(tree.(*gen.MapInitializerListContext)) + case *gen.NegateContext: + return p.VisitNegate(tree.(*gen.NegateContext)) + case *gen.IndexContext: + return p.VisitIndex(tree.(*gen.IndexContext)) + case *gen.UnaryContext: + return p.VisitUnary(tree.(*gen.UnaryContext)) + case *gen.CreateListContext: + return p.VisitCreateList(tree.(*gen.CreateListContext)) + case *gen.CreateMessageContext: + return p.VisitCreateMessage(tree.(*gen.CreateMessageContext)) + case *gen.CreateStructContext: + return p.VisitCreateStruct(tree.(*gen.CreateStructContext)) + } + + // Report at least one error if the parser reaches an unknown parse element. + // Typically, this happens if the parser has already encountered a syntax error elsewhere. + if len(p.errors.GetErrors()) == 0 { + txt := "<>" + if tree != nil { + txt = fmt.Sprintf("<<%T>>", tree) + } + return p.reportError(common.NoLocation, "unknown parse element encountered: %s", txt) + } + return p.helper.newExpr(common.NoLocation) + +} + +// Visit a parse tree produced by CELParser#start. +func (p *parser) VisitStart(ctx *gen.StartContext) interface{} { + return p.Visit(ctx.Expr()) +} + +// Visit a parse tree produced by CELParser#expr. +func (p *parser) VisitExpr(ctx *gen.ExprContext) interface{} { + result := p.Visit(ctx.GetE()).(*exprpb.Expr) + if ctx.GetOp() == nil { + return result + } + opID := p.helper.id(ctx.GetOp()) + ifTrue := p.Visit(ctx.GetE1()).(*exprpb.Expr) + ifFalse := p.Visit(ctx.GetE2()).(*exprpb.Expr) + return p.globalCallOrMacro(opID, operators.Conditional, result, ifTrue, ifFalse) +} + +// Visit a parse tree produced by CELParser#conditionalOr. +func (p *parser) VisitConditionalOr(ctx *gen.ConditionalOrContext) interface{} { + result := p.Visit(ctx.GetE()).(*exprpb.Expr) + if ctx.GetOps() == nil { + return result + } + b := newBalancer(p.helper, operators.LogicalOr, result) + rest := ctx.GetE1() + for i, op := range ctx.GetOps() { + if i >= len(rest) { + return p.reportError(ctx, "unexpected character, wanted '||'") + } + next := p.Visit(rest[i]).(*exprpb.Expr) + opID := p.helper.id(op) + b.addTerm(opID, next) + } + return b.balance() +} + +// Visit a parse tree produced by CELParser#conditionalAnd. +func (p *parser) VisitConditionalAnd(ctx *gen.ConditionalAndContext) interface{} { + result := p.Visit(ctx.GetE()).(*exprpb.Expr) + if ctx.GetOps() == nil { + return result + } + b := newBalancer(p.helper, operators.LogicalAnd, result) + rest := ctx.GetE1() + for i, op := range ctx.GetOps() { + if i >= len(rest) { + return p.reportError(ctx, "unexpected character, wanted '&&'") + } + next := p.Visit(rest[i]).(*exprpb.Expr) + opID := p.helper.id(op) + b.addTerm(opID, next) + } + return b.balance() +} + +// Visit a parse tree produced by CELParser#relation. +func (p *parser) VisitRelation(ctx *gen.RelationContext) interface{} { + if ctx.Calc() != nil { + return p.Visit(ctx.Calc()) + } + opText := "" + if ctx.GetOp() != nil { + opText = ctx.GetOp().GetText() + } + if op, found := operators.Find(opText); found { + lhs := p.Visit(ctx.Relation(0)).(*exprpb.Expr) + opID := p.helper.id(ctx.GetOp()) + rhs := p.Visit(ctx.Relation(1)).(*exprpb.Expr) + return p.globalCallOrMacro(opID, op, lhs, rhs) + } + return p.reportError(ctx, "operator not found") +} + +// Visit a parse tree produced by CELParser#calc. +func (p *parser) VisitCalc(ctx *gen.CalcContext) interface{} { + if ctx.Unary() != nil { + return p.Visit(ctx.Unary()) + } + opText := "" + if ctx.GetOp() != nil { + opText = ctx.GetOp().GetText() + } + if op, found := operators.Find(opText); found { + lhs := p.Visit(ctx.Calc(0)).(*exprpb.Expr) + opID := p.helper.id(ctx.GetOp()) + rhs := p.Visit(ctx.Calc(1)).(*exprpb.Expr) + return p.globalCallOrMacro(opID, op, lhs, rhs) + } + return p.reportError(ctx, "operator not found") +} + +func (p *parser) VisitUnary(ctx *gen.UnaryContext) interface{} { + return p.helper.newLiteralString(ctx, "<>") +} + +// Visit a parse tree produced by CELParser#MemberExpr. +func (p *parser) VisitMemberExpr(ctx *gen.MemberExprContext) interface{} { + switch ctx.Member().(type) { + case *gen.PrimaryExprContext: + return p.VisitPrimaryExpr(ctx.Member().(*gen.PrimaryExprContext)) + case *gen.SelectOrCallContext: + return p.VisitSelectOrCall(ctx.Member().(*gen.SelectOrCallContext)) + case *gen.IndexContext: + return p.VisitIndex(ctx.Member().(*gen.IndexContext)) + case *gen.CreateMessageContext: + return p.VisitCreateMessage(ctx.Member().(*gen.CreateMessageContext)) + } + return p.reportError(ctx, "unsupported simple expression") +} + +// Visit a parse tree produced by CELParser#LogicalNot. +func (p *parser) VisitLogicalNot(ctx *gen.LogicalNotContext) interface{} { + if len(ctx.GetOps())%2 == 0 { + return p.Visit(ctx.Member()) + } + opID := p.helper.id(ctx.GetOps()[0]) + target := p.Visit(ctx.Member()).(*exprpb.Expr) + return p.globalCallOrMacro(opID, operators.LogicalNot, target) +} + +func (p *parser) VisitNegate(ctx *gen.NegateContext) interface{} { + if len(ctx.GetOps())%2 == 0 { + return p.Visit(ctx.Member()) + } + opID := p.helper.id(ctx.GetOps()[0]) + target := p.Visit(ctx.Member()).(*exprpb.Expr) + return p.globalCallOrMacro(opID, operators.Negate, target) +} + +// Visit a parse tree produced by CELParser#SelectOrCall. +func (p *parser) VisitSelectOrCall(ctx *gen.SelectOrCallContext) interface{} { + operand := p.Visit(ctx.Member()).(*exprpb.Expr) + // Handle the error case where no valid identifier is specified. + if ctx.GetId() == nil { + return p.helper.newExpr(ctx) + } + id := ctx.GetId().GetText() + if ctx.GetOpen() != nil { + opID := p.helper.id(ctx.GetOpen()) + return p.receiverCallOrMacro(opID, id, operand, p.visitList(ctx.GetArgs())...) + } + return p.helper.newSelect(ctx.GetOp(), operand, id) +} + +// Visit a parse tree produced by CELParser#PrimaryExpr. +func (p *parser) VisitPrimaryExpr(ctx *gen.PrimaryExprContext) interface{} { + switch ctx.Primary().(type) { + case *gen.NestedContext: + return p.VisitNested(ctx.Primary().(*gen.NestedContext)) + case *gen.IdentOrGlobalCallContext: + return p.VisitIdentOrGlobalCall(ctx.Primary().(*gen.IdentOrGlobalCallContext)) + case *gen.CreateListContext: + return p.VisitCreateList(ctx.Primary().(*gen.CreateListContext)) + case *gen.CreateStructContext: + return p.VisitCreateStruct(ctx.Primary().(*gen.CreateStructContext)) + case *gen.ConstantLiteralContext: + return p.VisitConstantLiteral(ctx.Primary().(*gen.ConstantLiteralContext)) + } + + return p.reportError(ctx, "invalid primary expression") +} + +// Visit a parse tree produced by CELParser#Index. +func (p *parser) VisitIndex(ctx *gen.IndexContext) interface{} { + target := p.Visit(ctx.Member()).(*exprpb.Expr) + opID := p.helper.id(ctx.GetOp()) + index := p.Visit(ctx.GetIndex()).(*exprpb.Expr) + return p.globalCallOrMacro(opID, operators.Index, target, index) +} + +// Visit a parse tree produced by CELParser#CreateMessage. +func (p *parser) VisitCreateMessage(ctx *gen.CreateMessageContext) interface{} { + target := p.Visit(ctx.Member()).(*exprpb.Expr) + objID := p.helper.id(ctx.GetOp()) + if messageName, found := p.extractQualifiedName(target); found { + entries := p.VisitIFieldInitializerList(ctx.GetEntries()).([]*exprpb.Expr_CreateStruct_Entry) + return p.helper.newObject(objID, messageName, entries...) + } + return p.helper.newExpr(objID) +} + +// Visit a parse tree of field initializers. +func (p *parser) VisitIFieldInitializerList(ctx gen.IFieldInitializerListContext) interface{} { + if ctx == nil || ctx.GetFields() == nil { + // This is the result of a syntax error handled elswhere, return empty. + return []*exprpb.Expr_CreateStruct_Entry{} + } + + result := make([]*exprpb.Expr_CreateStruct_Entry, len(ctx.GetFields())) + cols := ctx.GetCols() + vals := ctx.GetValues() + for i, f := range ctx.GetFields() { + if i >= len(cols) || i >= len(vals) { + // This is the result of a syntax error detected elsewhere. + return []*exprpb.Expr_CreateStruct_Entry{} + } + initID := p.helper.id(cols[i]) + value := p.Visit(vals[i]).(*exprpb.Expr) + field := p.helper.newObjectField(initID, f.GetText(), value) + result[i] = field + } + return result +} + +// Visit a parse tree produced by CELParser#IdentOrGlobalCall. +func (p *parser) VisitIdentOrGlobalCall(ctx *gen.IdentOrGlobalCallContext) interface{} { + identName := "" + if ctx.GetLeadingDot() != nil { + identName = "." + } + // Handle the error case where no valid identifier is specified. + if ctx.GetId() == nil { + return p.helper.newExpr(ctx) + } + // Handle reserved identifiers. + id := ctx.GetId().GetText() + if _, ok := reservedIds[id]; ok { + return p.reportError(ctx, "reserved identifier: %s", id) + } + identName += id + if ctx.GetOp() != nil { + opID := p.helper.id(ctx.GetOp()) + return p.globalCallOrMacro(opID, identName, p.visitList(ctx.GetArgs())...) + } + return p.helper.newIdent(ctx.GetId(), identName) +} + +// Visit a parse tree produced by CELParser#Nested. +func (p *parser) VisitNested(ctx *gen.NestedContext) interface{} { + return p.Visit(ctx.GetE()) +} + +// Visit a parse tree produced by CELParser#CreateList. +func (p *parser) VisitCreateList(ctx *gen.CreateListContext) interface{} { + listID := p.helper.id(ctx.GetOp()) + return p.helper.newList(listID, p.visitList(ctx.GetElems())...) +} + +// Visit a parse tree produced by CELParser#CreateStruct. +func (p *parser) VisitCreateStruct(ctx *gen.CreateStructContext) interface{} { + structID := p.helper.id(ctx.GetOp()) + entries := []*exprpb.Expr_CreateStruct_Entry{} + if ctx.GetEntries() != nil { + entries = p.Visit(ctx.GetEntries()).([]*exprpb.Expr_CreateStruct_Entry) + } + return p.helper.newMap(structID, entries...) +} + +// Visit a parse tree produced by CELParser#ConstantLiteral. +func (p *parser) VisitConstantLiteral(ctx *gen.ConstantLiteralContext) interface{} { + switch ctx.Literal().(type) { + case *gen.IntContext: + return p.VisitInt(ctx.Literal().(*gen.IntContext)) + case *gen.UintContext: + return p.VisitUint(ctx.Literal().(*gen.UintContext)) + case *gen.DoubleContext: + return p.VisitDouble(ctx.Literal().(*gen.DoubleContext)) + case *gen.StringContext: + return p.VisitString(ctx.Literal().(*gen.StringContext)) + case *gen.BytesContext: + return p.VisitBytes(ctx.Literal().(*gen.BytesContext)) + case *gen.BoolFalseContext: + return p.VisitBoolFalse(ctx.Literal().(*gen.BoolFalseContext)) + case *gen.BoolTrueContext: + return p.VisitBoolTrue(ctx.Literal().(*gen.BoolTrueContext)) + case *gen.NullContext: + return p.VisitNull(ctx.Literal().(*gen.NullContext)) + } + return p.reportError(ctx, "invalid literal") +} + +// Visit a parse tree produced by CELParser#mapInitializerList. +func (p *parser) VisitMapInitializerList(ctx *gen.MapInitializerListContext) interface{} { + if ctx == nil || ctx.GetKeys() == nil { + // This is the result of a syntax error handled elswhere, return empty. + return []*exprpb.Expr_CreateStruct_Entry{} + } + + result := make([]*exprpb.Expr_CreateStruct_Entry, len(ctx.GetCols())) + keys := ctx.GetKeys() + vals := ctx.GetValues() + for i, col := range ctx.GetCols() { + colID := p.helper.id(col) + if i >= len(keys) || i >= len(vals) { + // This is the result of a syntax error detected elsewhere. + return []*exprpb.Expr_CreateStruct_Entry{} + } + key := p.Visit(keys[i]).(*exprpb.Expr) + value := p.Visit(vals[i]).(*exprpb.Expr) + entry := p.helper.newMapEntry(colID, key, value) + result[i] = entry + } + return result +} + +// Visit a parse tree produced by CELParser#Int. +func (p *parser) VisitInt(ctx *gen.IntContext) interface{} { + text := ctx.GetTok().GetText() + base := 10 + if strings.HasPrefix(text, "0x") { + base = 16 + text = text[2:] + } + if ctx.GetSign() != nil { + text = ctx.GetSign().GetText() + text + } + i, err := strconv.ParseInt(text, base, 64) + if err != nil { + return p.reportError(ctx, "invalid int literal") + } + return p.helper.newLiteralInt(ctx, i) +} + +// Visit a parse tree produced by CELParser#Uint. +func (p *parser) VisitUint(ctx *gen.UintContext) interface{} { + text := ctx.GetTok().GetText() + // trim the 'u' designator included in the uint literal. + text = text[:len(text)-1] + base := 10 + if strings.HasPrefix(text, "0x") { + base = 16 + text = text[2:] + } + i, err := strconv.ParseUint(text, base, 64) + if err != nil { + return p.reportError(ctx, "invalid uint literal") + } + return p.helper.newLiteralUint(ctx, i) +} + +// Visit a parse tree produced by CELParser#Double. +func (p *parser) VisitDouble(ctx *gen.DoubleContext) interface{} { + txt := ctx.GetTok().GetText() + if ctx.GetSign() != nil { + txt = ctx.GetSign().GetText() + txt + } + f, err := strconv.ParseFloat(txt, 64) + if err != nil { + return p.reportError(ctx, "invalid double literal") + } + return p.helper.newLiteralDouble(ctx, f) + +} + +// Visit a parse tree produced by CELParser#String. +func (p *parser) VisitString(ctx *gen.StringContext) interface{} { + s := p.unquote(ctx, ctx.GetText(), false) + return p.helper.newLiteralString(ctx, s) +} + +// Visit a parse tree produced by CELParser#Bytes. +func (p *parser) VisitBytes(ctx *gen.BytesContext) interface{} { + b := []byte(p.unquote(ctx, ctx.GetTok().GetText()[1:], true)) + return p.helper.newLiteralBytes(ctx, b) +} + +// Visit a parse tree produced by CELParser#BoolTrue. +func (p *parser) VisitBoolTrue(ctx *gen.BoolTrueContext) interface{} { + return p.helper.newLiteralBool(ctx, true) +} + +// Visit a parse tree produced by CELParser#BoolFalse. +func (p *parser) VisitBoolFalse(ctx *gen.BoolFalseContext) interface{} { + return p.helper.newLiteralBool(ctx, false) +} + +// Visit a parse tree produced by CELParser#Null. +func (p *parser) VisitNull(ctx *gen.NullContext) interface{} { + return p.helper.newLiteral(ctx, + &exprpb.Constant{ + ConstantKind: &exprpb.Constant_NullValue{ + NullValue: structpb.NullValue_NULL_VALUE}}) +} + +func (p *parser) visitList(ctx gen.IExprListContext) []*exprpb.Expr { + if ctx == nil { + return []*exprpb.Expr{} + } + return p.visitSlice(ctx.GetE()) +} + +func (p *parser) visitSlice(expressions []gen.IExprContext) []*exprpb.Expr { + if expressions == nil { + return []*exprpb.Expr{} + } + result := make([]*exprpb.Expr, len(expressions)) + for i, e := range expressions { + ex := p.Visit(e).(*exprpb.Expr) + result[i] = ex + } + return result +} + +func (p *parser) extractQualifiedName(e *exprpb.Expr) (string, bool) { + if e == nil { + return "", false + } + switch e.ExprKind.(type) { + case *exprpb.Expr_IdentExpr: + return e.GetIdentExpr().GetName(), true + case *exprpb.Expr_SelectExpr: + s := e.GetSelectExpr() + if prefix, found := p.extractQualifiedName(s.Operand); found { + return prefix + "." + s.Field, true + } + } + // TODO: Add a method to Source to get location from character offset. + location := p.helper.getLocation(e.GetId()) + p.reportError(location, "expected a qualified name") + return "", false +} + +func (p *parser) unquote(ctx interface{}, value string, isBytes bool) string { + text, err := unescape(value, isBytes) + if err != nil { + p.reportError(ctx, "%s", err.Error()) + return value + } + return text +} + +func (p *parser) reportError(ctx interface{}, format string, args ...interface{}) *exprpb.Expr { + var location common.Location + switch ctx.(type) { + case common.Location: + location = ctx.(common.Location) + case antlr.Token, antlr.ParserRuleContext: + err := p.helper.newExpr(ctx) + location = p.helper.getLocation(err.GetId()) + } + err := p.helper.newExpr(ctx) + // Provide arguments to the report error. + p.errors.ReportError(location, format, args...) + return err +} + +// ANTLR Parse listener implementations +func (p *parser) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) { + // TODO: Snippet + l := p.helper.source.NewLocation(line, column) + p.errors.syntaxError(l, msg) +} + +func (p *parser) ReportAmbiguity(recognizer antlr.Parser, dfa *antlr.DFA, startIndex, stopIndex int, exact bool, ambigAlts *antlr.BitSet, configs antlr.ATNConfigSet) { + // Intentional +} + +func (p *parser) ReportAttemptingFullContext(recognizer antlr.Parser, dfa *antlr.DFA, startIndex, stopIndex int, conflictingAlts *antlr.BitSet, configs antlr.ATNConfigSet) { + // Intentional +} + +func (p *parser) ReportContextSensitivity(recognizer antlr.Parser, dfa *antlr.DFA, startIndex, stopIndex, prediction int, configs antlr.ATNConfigSet) { + // Intentional +} + +func (p *parser) globalCallOrMacro(exprID int64, function string, args ...*exprpb.Expr) *exprpb.Expr { + if expr, found := p.expandMacro(exprID, function, nil, args...); found { + return expr + } + return p.helper.newGlobalCall(exprID, function, args...) +} + +func (p *parser) receiverCallOrMacro(exprID int64, function string, target *exprpb.Expr, args ...*exprpb.Expr) *exprpb.Expr { + if expr, found := p.expandMacro(exprID, function, target, args...); found { + return expr + } + return p.helper.newReceiverCall(exprID, function, target, args...) +} + +func (p *parser) expandMacro(exprID int64, function string, target *exprpb.Expr, args ...*exprpb.Expr) (*exprpb.Expr, bool) { + macro, found := p.macros[makeMacroKey(function, len(args), target != nil)] + if !found { + macro, found = p.macros[makeVarArgMacroKey(function, target != nil)] + if !found { + return nil, false + } + } + eh := exprHelperPool.Get().(*exprHelper) + defer exprHelperPool.Put(eh) + eh.parserHelper = p.helper + eh.id = exprID + expr, err := macro.Expander()(eh, target, args) + if err != nil { + if err.Location != nil { + return p.reportError(err.Location, err.Message), true + } + return p.reportError(p.helper.getLocation(exprID), err.Message), true + } + if p.populateMacroCalls { + p.helper.addMacroCall(expr.GetId(), function, target, args...) + } + return expr, true +} diff --git a/vendor/github.com/google/cel-go/parser/unescape.go b/vendor/github.com/google/cel-go/parser/unescape.go new file mode 100644 index 0000000000..27c57a9f3a --- /dev/null +++ b/vendor/github.com/google/cel-go/parser/unescape.go @@ -0,0 +1,237 @@ +// Copyright 2018 Google LLC +// +// 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. + +package parser + +import ( + "fmt" + "strings" + "unicode/utf8" +) + +// Unescape takes a quoted string, unquotes, and unescapes it. +// +// This function performs escaping compatible with GoogleSQL. +func unescape(value string, isBytes bool) (string, error) { + // All strings normalize newlines to the \n representation. + value = newlineNormalizer.Replace(value) + n := len(value) + + // Nothing to unescape / decode. + if n < 2 { + return value, fmt.Errorf("unable to unescape string") + } + + // Raw string preceded by the 'r|R' prefix. + isRawLiteral := false + if value[0] == 'r' || value[0] == 'R' { + value = value[1:] + n = len(value) + isRawLiteral = true + } + + // Quoted string of some form, must have same first and last char. + if value[0] != value[n-1] || (value[0] != '"' && value[0] != '\'') { + return value, fmt.Errorf("unable to unescape string") + } + + // Normalize the multi-line CEL string representation to a standard + // Go quoted string. + if n >= 6 { + if strings.HasPrefix(value, "'''") { + if !strings.HasSuffix(value, "'''") { + return value, fmt.Errorf("unable to unescape string") + } + value = "\"" + value[3:n-3] + "\"" + } else if strings.HasPrefix(value, `"""`) { + if !strings.HasSuffix(value, `"""`) { + return value, fmt.Errorf("unable to unescape string") + } + value = "\"" + value[3:n-3] + "\"" + } + n = len(value) + } + value = value[1 : n-1] + // If there is nothing to escape, then return. + if isRawLiteral || !strings.ContainsRune(value, '\\') { + return value, nil + } + + // Otherwise the string contains escape characters. + // The following logic is adapted from `strconv/quote.go` + var runeTmp [utf8.UTFMax]byte + buf := make([]byte, 0, 3*n/2) + for len(value) > 0 { + c, encode, rest, err := unescapeChar(value, isBytes) + if err != nil { + return "", err + } + value = rest + if c < utf8.RuneSelf || !encode { + buf = append(buf, byte(c)) + } else { + n := utf8.EncodeRune(runeTmp[:], c) + buf = append(buf, runeTmp[:n]...) + } + } + return string(buf), nil +} + +// unescapeChar takes a string input and returns the following info: +// +// value - the escaped unicode rune at the front of the string. +// encode - the value should be unicode-encoded +// tail - the remainder of the input string. +// err - error value, if the character could not be unescaped. +// +// When encode is true the return value may still fit within a single byte, +// but unicode encoding is attempted which is more expensive than when the +// value is known to self-represent as a single byte. +// +// If isBytes is set, unescape as a bytes literal so octal and hex escapes +// represent byte values, not unicode code points. +func unescapeChar(s string, isBytes bool) (value rune, encode bool, tail string, err error) { + // 1. Character is not an escape sequence. + switch c := s[0]; { + case c >= utf8.RuneSelf: + r, size := utf8.DecodeRuneInString(s) + return r, true, s[size:], nil + case c != '\\': + return rune(s[0]), false, s[1:], nil + } + + // 2. Last character is the start of an escape sequence. + if len(s) <= 1 { + err = fmt.Errorf("unable to unescape string, found '\\' as last character") + return + } + + c := s[1] + s = s[2:] + // 3. Common escape sequences shared with Google SQL + switch c { + case 'a': + value = '\a' + case 'b': + value = '\b' + case 'f': + value = '\f' + case 'n': + value = '\n' + case 'r': + value = '\r' + case 't': + value = '\t' + case 'v': + value = '\v' + case '\\': + value = '\\' + case '\'': + value = '\'' + case '"': + value = '"' + case '`': + value = '`' + case '?': + value = '?' + + // 4. Unicode escape sequences, reproduced from `strconv/quote.go` + case 'x', 'X', 'u', 'U': + n := 0 + encode = true + switch c { + case 'x', 'X': + n = 2 + encode = !isBytes + case 'u': + n = 4 + if isBytes { + err = fmt.Errorf("unable to unescape string") + return + } + case 'U': + n = 8 + if isBytes { + err = fmt.Errorf("unable to unescape string") + return + } + } + var v rune + if len(s) < n { + err = fmt.Errorf("unable to unescape string") + return + } + for j := 0; j < n; j++ { + x, ok := unhex(s[j]) + if !ok { + err = fmt.Errorf("unable to unescape string") + return + } + v = v<<4 | x + } + s = s[n:] + if !isBytes && v > utf8.MaxRune { + err = fmt.Errorf("unable to unescape string") + return + } + value = v + + // 5. Octal escape sequences, must be three digits \[0-3][0-7][0-7] + case '0', '1', '2', '3': + if len(s) < 2 { + err = fmt.Errorf("unable to unescape octal sequence in string") + return + } + v := rune(c - '0') + for j := 0; j < 2; j++ { + x := s[j] + if x < '0' || x > '7' { + err = fmt.Errorf("unable to unescape octal sequence in string") + return + } + v = v*8 + rune(x-'0') + } + if !isBytes && v > utf8.MaxRune { + err = fmt.Errorf("unable to unescape string") + return + } + value = v + s = s[2:] + encode = !isBytes + + // Unknown escape sequence. + default: + err = fmt.Errorf("unable to unescape string") + } + + tail = s + return +} + +func unhex(b byte) (rune, bool) { + c := rune(b) + switch { + case '0' <= c && c <= '9': + return c - '0', true + case 'a' <= c && c <= 'f': + return c - 'a' + 10, true + case 'A' <= c && c <= 'F': + return c - 'A' + 10, true + } + return 0, false +} + +var ( + newlineNormalizer = strings.NewReplacer("\r\n", "\n", "\r", "\n") +) diff --git a/vendor/github.com/google/cel-go/parser/unparser.go b/vendor/github.com/google/cel-go/parser/unparser.go new file mode 100644 index 0000000000..6a610ff76a --- /dev/null +++ b/vendor/github.com/google/cel-go/parser/unparser.go @@ -0,0 +1,446 @@ +// Copyright 2019 Google LLC +// +// 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. + +package parser + +import ( + "errors" + "fmt" + "strconv" + "strings" + + "github.com/google/cel-go/common/operators" + + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +// Unparse takes an input expression and source position information and generates a human-readable +// expression. +// +// Note, unparsing an AST will often generate the same expression as was originally parsed, but some +// formatting may be lost in translation, notably: +// +// - All quoted literals are doubled quoted. +// - Byte literals are represented as octal escapes (same as Google SQL). +// - Floating point values are converted to the small number of digits needed to represent the value. +// - Spacing around punctuation marks may be lost. +// - Parentheses will only be applied when they affect operator precedence. +func Unparse(expr *exprpb.Expr, info *exprpb.SourceInfo) (string, error) { + un := &unparser{info: info} + err := un.visit(expr) + if err != nil { + return "", err + } + return un.str.String(), nil +} + +// unparser visits an expression to reconstruct a human-readable string from an AST. +type unparser struct { + str strings.Builder + info *exprpb.SourceInfo +} + +func (un *unparser) visit(expr *exprpb.Expr) error { + if expr == nil { + return errors.New("unsupported expression") + } + visited, err := un.visitMaybeMacroCall(expr) + if visited || err != nil { + return err + } + switch expr.ExprKind.(type) { + case *exprpb.Expr_CallExpr: + return un.visitCall(expr) + case *exprpb.Expr_ConstExpr: + return un.visitConst(expr) + case *exprpb.Expr_IdentExpr: + return un.visitIdent(expr) + case *exprpb.Expr_ListExpr: + return un.visitList(expr) + case *exprpb.Expr_SelectExpr: + return un.visitSelect(expr) + case *exprpb.Expr_StructExpr: + return un.visitStruct(expr) + default: + return fmt.Errorf("unsupported expression: %v", expr) + } +} + +func (un *unparser) visitCall(expr *exprpb.Expr) error { + c := expr.GetCallExpr() + fun := c.GetFunction() + switch fun { + // ternary operator + case operators.Conditional: + return un.visitCallConditional(expr) + // index operator + case operators.Index: + return un.visitCallIndex(expr) + // unary operators + case operators.LogicalNot, operators.Negate: + return un.visitCallUnary(expr) + // binary operators + case operators.Add, + operators.Divide, + operators.Equals, + operators.Greater, + operators.GreaterEquals, + operators.In, + operators.Less, + operators.LessEquals, + operators.LogicalAnd, + operators.LogicalOr, + operators.Modulo, + operators.Multiply, + operators.NotEquals, + operators.OldIn, + operators.Subtract: + return un.visitCallBinary(expr) + // standard function calls. + default: + return un.visitCallFunc(expr) + } +} + +func (un *unparser) visitCallBinary(expr *exprpb.Expr) error { + c := expr.GetCallExpr() + fun := c.GetFunction() + args := c.GetArgs() + lhs := args[0] + // add parens if the current operator is lower precedence than the lhs expr operator. + lhsParen := isComplexOperatorWithRespectTo(fun, lhs) + rhs := args[1] + // add parens if the current operator is lower precedence than the rhs expr operator, + // or the same precedence and the operator is left recursive. + rhsParen := isComplexOperatorWithRespectTo(fun, rhs) + if !rhsParen && isLeftRecursive(fun) { + rhsParen = isSamePrecedence(fun, rhs) + } + err := un.visitMaybeNested(lhs, lhsParen) + if err != nil { + return err + } + unmangled, found := operators.FindReverseBinaryOperator(fun) + if !found { + return fmt.Errorf("cannot unmangle operator: %s", fun) + } + un.str.WriteString(" ") + un.str.WriteString(unmangled) + un.str.WriteString(" ") + return un.visitMaybeNested(rhs, rhsParen) +} + +func (un *unparser) visitCallConditional(expr *exprpb.Expr) error { + c := expr.GetCallExpr() + args := c.GetArgs() + // add parens if operand is a conditional itself. + nested := isSamePrecedence(operators.Conditional, args[0]) || + isComplexOperator(args[0]) + err := un.visitMaybeNested(args[0], nested) + if err != nil { + return err + } + un.str.WriteString(" ? ") + // add parens if operand is a conditional itself. + nested = isSamePrecedence(operators.Conditional, args[1]) || + isComplexOperator(args[1]) + err = un.visitMaybeNested(args[1], nested) + if err != nil { + return err + } + un.str.WriteString(" : ") + // add parens if operand is a conditional itself. + nested = isSamePrecedence(operators.Conditional, args[2]) || + isComplexOperator(args[2]) + + return un.visitMaybeNested(args[2], nested) +} + +func (un *unparser) visitCallFunc(expr *exprpb.Expr) error { + c := expr.GetCallExpr() + fun := c.GetFunction() + args := c.GetArgs() + if c.GetTarget() != nil { + nested := isBinaryOrTernaryOperator(c.GetTarget()) + err := un.visitMaybeNested(c.GetTarget(), nested) + if err != nil { + return err + } + un.str.WriteString(".") + } + un.str.WriteString(fun) + un.str.WriteString("(") + for i, arg := range args { + err := un.visit(arg) + if err != nil { + return err + } + if i < len(args)-1 { + un.str.WriteString(", ") + } + } + un.str.WriteString(")") + return nil +} + +func (un *unparser) visitCallIndex(expr *exprpb.Expr) error { + c := expr.GetCallExpr() + args := c.GetArgs() + nested := isBinaryOrTernaryOperator(args[0]) + err := un.visitMaybeNested(args[0], nested) + if err != nil { + return err + } + un.str.WriteString("[") + err = un.visit(args[1]) + if err != nil { + return err + } + un.str.WriteString("]") + return nil +} + +func (un *unparser) visitCallUnary(expr *exprpb.Expr) error { + c := expr.GetCallExpr() + fun := c.GetFunction() + args := c.GetArgs() + unmangled, found := operators.FindReverse(fun) + if !found { + return fmt.Errorf("cannot unmangle operator: %s", fun) + } + un.str.WriteString(unmangled) + nested := isComplexOperator(args[0]) + return un.visitMaybeNested(args[0], nested) +} + +func (un *unparser) visitConst(expr *exprpb.Expr) error { + c := expr.GetConstExpr() + switch c.ConstantKind.(type) { + case *exprpb.Constant_BoolValue: + un.str.WriteString(strconv.FormatBool(c.GetBoolValue())) + case *exprpb.Constant_BytesValue: + // bytes constants are surrounded with b"" + b := c.GetBytesValue() + un.str.WriteString(`b"`) + un.str.WriteString(bytesToOctets(b)) + un.str.WriteString(`"`) + case *exprpb.Constant_DoubleValue: + // represent the float using the minimum required digits + d := strconv.FormatFloat(c.GetDoubleValue(), 'g', -1, 64) + un.str.WriteString(d) + case *exprpb.Constant_Int64Value: + i := strconv.FormatInt(c.GetInt64Value(), 10) + un.str.WriteString(i) + case *exprpb.Constant_NullValue: + un.str.WriteString("null") + case *exprpb.Constant_StringValue: + // strings will be double quoted with quotes escaped. + un.str.WriteString(strconv.Quote(c.GetStringValue())) + case *exprpb.Constant_Uint64Value: + // uint literals have a 'u' suffix. + ui := strconv.FormatUint(c.GetUint64Value(), 10) + un.str.WriteString(ui) + un.str.WriteString("u") + default: + return fmt.Errorf("unsupported constant: %v", expr) + } + return nil +} + +func (un *unparser) visitIdent(expr *exprpb.Expr) error { + un.str.WriteString(expr.GetIdentExpr().GetName()) + return nil +} + +func (un *unparser) visitList(expr *exprpb.Expr) error { + l := expr.GetListExpr() + elems := l.GetElements() + un.str.WriteString("[") + for i, elem := range elems { + err := un.visit(elem) + if err != nil { + return err + } + if i < len(elems)-1 { + un.str.WriteString(", ") + } + } + un.str.WriteString("]") + return nil +} + +func (un *unparser) visitSelect(expr *exprpb.Expr) error { + sel := expr.GetSelectExpr() + // handle the case when the select expression was generated by the has() macro. + if sel.GetTestOnly() { + un.str.WriteString("has(") + } + nested := !sel.GetTestOnly() && isBinaryOrTernaryOperator(sel.GetOperand()) + err := un.visitMaybeNested(sel.GetOperand(), nested) + if err != nil { + return err + } + un.str.WriteString(".") + un.str.WriteString(sel.GetField()) + if sel.GetTestOnly() { + un.str.WriteString(")") + } + return nil +} + +func (un *unparser) visitStruct(expr *exprpb.Expr) error { + s := expr.GetStructExpr() + // If the message name is non-empty, then this should be treated as message construction. + if s.GetMessageName() != "" { + return un.visitStructMsg(expr) + } + // Otherwise, build a map. + return un.visitStructMap(expr) +} + +func (un *unparser) visitStructMsg(expr *exprpb.Expr) error { + m := expr.GetStructExpr() + entries := m.GetEntries() + un.str.WriteString(m.GetMessageName()) + un.str.WriteString("{") + for i, entry := range entries { + f := entry.GetFieldKey() + un.str.WriteString(f) + un.str.WriteString(": ") + v := entry.GetValue() + err := un.visit(v) + if err != nil { + return err + } + if i < len(entries)-1 { + un.str.WriteString(", ") + } + } + un.str.WriteString("}") + return nil +} + +func (un *unparser) visitStructMap(expr *exprpb.Expr) error { + m := expr.GetStructExpr() + entries := m.GetEntries() + un.str.WriteString("{") + for i, entry := range entries { + k := entry.GetMapKey() + err := un.visit(k) + if err != nil { + return err + } + un.str.WriteString(": ") + v := entry.GetValue() + err = un.visit(v) + if err != nil { + return err + } + if i < len(entries)-1 { + un.str.WriteString(", ") + } + } + un.str.WriteString("}") + return nil +} + +func (un *unparser) visitMaybeMacroCall(expr *exprpb.Expr) (bool, error) { + macroCalls := un.info.GetMacroCalls() + call, found := macroCalls[expr.GetId()] + if !found { + return false, nil + } + return true, un.visit(call) +} + +func (un *unparser) visitMaybeNested(expr *exprpb.Expr, nested bool) error { + if nested { + un.str.WriteString("(") + } + err := un.visit(expr) + if err != nil { + return err + } + if nested { + un.str.WriteString(")") + } + return nil +} + +// isLeftRecursive indicates whether the parser resolves the call in a left-recursive manner as +// this can have an effect of how parentheses affect the order of operations in the AST. +func isLeftRecursive(op string) bool { + return op != operators.LogicalAnd && op != operators.LogicalOr +} + +// isSamePrecedence indicates whether the precedence of the input operator is the same as the +// precedence of the (possible) operation represented in the input Expr. +// +// If the expr is not a Call, the result is false. +func isSamePrecedence(op string, expr *exprpb.Expr) bool { + if expr.GetCallExpr() == nil { + return false + } + c := expr.GetCallExpr() + other := c.GetFunction() + return operators.Precedence(op) == operators.Precedence(other) +} + +// isLowerPrecedence indicates whether the precedence of the input operator is lower precedence +// than the (possible) operation represented in the input Expr. +// +// If the expr is not a Call, the result is false. +func isLowerPrecedence(op string, expr *exprpb.Expr) bool { + c := expr.GetCallExpr() + other := c.GetFunction() + return operators.Precedence(op) < operators.Precedence(other) +} + +// Indicates whether the expr is a complex operator, i.e., a call expression +// with 2 or more arguments. +func isComplexOperator(expr *exprpb.Expr) bool { + if expr.GetCallExpr() != nil && len(expr.GetCallExpr().GetArgs()) >= 2 { + return true + } + return false +} + +// Indicates whether it is a complex operation compared to another. +// expr is *not* considered complex if it is not a call expression or has +// less than two arguments, or if it has a higher precedence than op. +func isComplexOperatorWithRespectTo(op string, expr *exprpb.Expr) bool { + if expr.GetCallExpr() == nil || len(expr.GetCallExpr().GetArgs()) < 2 { + return false + } + return isLowerPrecedence(op, expr) +} + +// Indicate whether this is a binary or ternary operator. +func isBinaryOrTernaryOperator(expr *exprpb.Expr) bool { + if expr.GetCallExpr() == nil || len(expr.GetCallExpr().GetArgs()) < 2 { + return false + } + _, isBinaryOp := operators.FindReverseBinaryOperator(expr.GetCallExpr().GetFunction()) + return isBinaryOp || isSamePrecedence(operators.Conditional, expr) +} + +// bytesToOctets converts byte sequences to a string using a three digit octal encoded value +// per byte. +func bytesToOctets(byteVal []byte) string { + var b strings.Builder + for _, c := range byteVal { + fmt.Fprintf(&b, "\\%03o", c) + } + return b.String() +} diff --git a/vendor/github.com/google/go-containerregistry/internal/and/and_closer.go b/vendor/github.com/google/go-containerregistry/internal/and/and_closer.go new file mode 100644 index 0000000000..14a05eaa17 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/internal/and/and_closer.go @@ -0,0 +1,48 @@ +// Copyright 2020 Google LLC All Rights Reserved. +// +// 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. + +// Package and provides helpers for adding Close to io.{Reader|Writer}. +package and + +import ( + "io" +) + +// ReadCloser implements io.ReadCloser by reading from a particular io.Reader +// and then calling the provided "Close()" method. +type ReadCloser struct { + io.Reader + CloseFunc func() error +} + +var _ io.ReadCloser = (*ReadCloser)(nil) + +// Close implements io.ReadCloser +func (rac *ReadCloser) Close() error { + return rac.CloseFunc() +} + +// WriteCloser implements io.WriteCloser by reading from a particular io.Writer +// and then calling the provided "Close()" method. +type WriteCloser struct { + io.Writer + CloseFunc func() error +} + +var _ io.WriteCloser = (*WriteCloser)(nil) + +// Close implements io.WriteCloser +func (wac *WriteCloser) Close() error { + return wac.CloseFunc() +} diff --git a/vendor/github.com/google/go-containerregistry/internal/estargz/estargz.go b/vendor/github.com/google/go-containerregistry/internal/estargz/estargz.go new file mode 100644 index 0000000000..35b1d8e7f2 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/internal/estargz/estargz.go @@ -0,0 +1,55 @@ +// Copyright 2020 Google LLC All Rights Reserved. +// +// 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. + +// Package estargz adapts the containerd estargz package to our abstractions. +package estargz + +import ( + "bytes" + "io" + "io/ioutil" + + "github.com/containerd/stargz-snapshotter/estargz" + v1 "github.com/google/go-containerregistry/pkg/v1" +) + +// Assert that what we're returning is an io.ReadCloser +var _ io.ReadCloser = (*estargz.Blob)(nil) + +// ReadCloser reads uncompressed tarball input from the io.ReadCloser and +// returns: +// * An io.ReadCloser from which compressed data may be read, and +// * A v1.Hash with the hash of the estargz table of contents, or +// * An error if the estargz processing encountered a problem. +// +// Refer to estargz for the options: +// https://pkg.go.dev/github.com/containerd/stargz-snapshotter/estargz@v0.4.1#Option +func ReadCloser(r io.ReadCloser, opts ...estargz.Option) (*estargz.Blob, v1.Hash, error) { + defer r.Close() + + // TODO(#876): Avoid buffering into memory. + bs, err := ioutil.ReadAll(r) + if err != nil { + return nil, v1.Hash{}, err + } + br := bytes.NewReader(bs) + + rc, err := estargz.Build(io.NewSectionReader(br, 0, int64(len(bs))), opts...) + if err != nil { + return nil, v1.Hash{}, err + } + + h, err := v1.NewHash(rc.TOCDigest().String()) + return rc, h, err +} diff --git a/vendor/github.com/google/go-containerregistry/internal/gzip/zip.go b/vendor/github.com/google/go-containerregistry/internal/gzip/zip.go new file mode 100644 index 0000000000..1a52694e8d --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/internal/gzip/zip.go @@ -0,0 +1,146 @@ +// Copyright 2020 Google LLC All Rights Reserved. +// +// 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. + +// Package gzip provides helper functions for interacting with gzipped streams. +package gzip + +import ( + "bufio" + "bytes" + "compress/gzip" + "io" + + "github.com/google/go-containerregistry/internal/and" +) + +var gzipMagicHeader = []byte{'\x1f', '\x8b'} + +// ReadCloser reads uncompressed input data from the io.ReadCloser and +// returns an io.ReadCloser from which compressed data may be read. +// This uses gzip.BestSpeed for the compression level. +func ReadCloser(r io.ReadCloser) io.ReadCloser { + return ReadCloserLevel(r, gzip.BestSpeed) +} + +// ReadCloserLevel reads uncompressed input data from the io.ReadCloser and +// returns an io.ReadCloser from which compressed data may be read. +// Refer to compress/gzip for the level: +// https://golang.org/pkg/compress/gzip/#pkg-constants +func ReadCloserLevel(r io.ReadCloser, level int) io.ReadCloser { + pr, pw := io.Pipe() + + // For highly compressible layers, gzip.Writer will output a very small + // number of bytes per Write(). This is normally fine, but when pushing + // to a registry, we want to ensure that we're taking full advantage of + // the available bandwidth instead of sending tons of tiny writes over + // the wire. + // 64K ought to be small enough for anybody. + bw := bufio.NewWriterSize(pw, 2<<16) + + // Returns err so we can pw.CloseWithError(err) + go func() error { + // TODO(go1.14): Just defer {pw,gw,r}.Close like you'd expect. + // Context: https://golang.org/issue/24283 + gw, err := gzip.NewWriterLevel(bw, level) + if err != nil { + return pw.CloseWithError(err) + } + + if _, err := io.Copy(gw, r); err != nil { + defer r.Close() + defer gw.Close() + return pw.CloseWithError(err) + } + + // Close gzip writer to Flush it and write gzip trailers. + if err := gw.Close(); err != nil { + return pw.CloseWithError(err) + } + + // Flush bufio writer to ensure we write out everything. + if err := bw.Flush(); err != nil { + return pw.CloseWithError(err) + } + + // We don't really care if these fail. + defer pw.Close() + defer r.Close() + + return nil + }() + + return pr +} + +// UnzipReadCloser reads compressed input data from the io.ReadCloser and +// returns an io.ReadCloser from which uncompessed data may be read. +func UnzipReadCloser(r io.ReadCloser) (io.ReadCloser, error) { + gr, err := gzip.NewReader(r) + if err != nil { + return nil, err + } + return &and.ReadCloser{ + Reader: gr, + CloseFunc: func() error { + // If the unzip fails, then this seems to return the same + // error as the read. We don't want this to interfere with + // us closing the main ReadCloser, since this could leave + // an open file descriptor (fails on Windows). + gr.Close() + return r.Close() + }, + }, nil +} + +// Is detects whether the input stream is compressed. +func Is(r io.Reader) (bool, error) { + magicHeader := make([]byte, 2) + n, err := r.Read(magicHeader) + if n == 0 && err == io.EOF { + return false, nil + } + if err != nil { + return false, err + } + return bytes.Equal(magicHeader, gzipMagicHeader), nil +} + +// PeekReader is an io.Reader that also implements Peek a la bufio.Reader. +type PeekReader interface { + io.Reader + Peek(n int) ([]byte, error) +} + +// Peek detects whether the input stream is gzip compressed. +// +// If r implements Peek, we will use that directly, otherwise a small number +// of bytes are buffered to Peek at the gzip header, and the returned +// PeekReader can be used as a replacement for the consumed input io.Reader. +func Peek(r io.Reader) (bool, PeekReader, error) { + var pr PeekReader + if p, ok := r.(PeekReader); ok { + pr = p + } else { + pr = bufio.NewReader(r) + } + header, err := pr.Peek(2) + if err != nil { + // https://github.com/google/go-containerregistry/issues/367 + if err == io.EOF { + return false, pr, nil + } + return false, pr, err + } + return bytes.Equal(header, gzipMagicHeader), pr, nil +} diff --git a/vendor/github.com/google/go-containerregistry/internal/redact/redact.go b/vendor/github.com/google/go-containerregistry/internal/redact/redact.go new file mode 100644 index 0000000000..dc9c56b7f3 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/internal/redact/redact.go @@ -0,0 +1,35 @@ +// Copyright 2020 Google LLC All Rights Reserved. +// +// 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. + +// Package redact contains a simple context signal for redacting requests. +package redact + +import ( + "context" +) + +type contextKey string + +var redactKey = contextKey("redact") + +// NewContext creates a new ctx with the reason for redaction. +func NewContext(ctx context.Context, reason string) context.Context { + return context.WithValue(ctx, redactKey, reason) +} + +// FromContext returns the redaction reason, if any. +func FromContext(ctx context.Context) (bool, string) { + reason, ok := ctx.Value(redactKey).(string) + return ok, reason +} diff --git a/vendor/github.com/google/go-containerregistry/internal/retry/retry.go b/vendor/github.com/google/go-containerregistry/internal/retry/retry.go new file mode 100644 index 0000000000..133cb1c122 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/internal/retry/retry.go @@ -0,0 +1,77 @@ +// Copyright 2019 Google LLC All Rights Reserved. +// +// 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. + +// Package retry provides methods for retrying operations. It is a thin wrapper +// around k8s.io/apimachinery/pkg/util/wait to make certain operations easier. +package retry + +import ( + "context" + "fmt" + + "github.com/google/go-containerregistry/internal/retry/wait" +) + +// Backoff is an alias of our own wait.Backoff to avoid name conflicts with +// the kubernetes wait package. Typing retry.Backoff is aesier than fixing +// the wrong import every time you use wait.Backoff. +type Backoff = wait.Backoff + +// This is implemented by several errors in the net package as well as our +// transport.Error. +type temporary interface { + Temporary() bool +} + +// IsTemporary returns true if err implements Temporary() and it returns true. +func IsTemporary(err error) bool { + if err == context.DeadlineExceeded { + return false + } + if te, ok := err.(temporary); ok && te.Temporary() { + return true + } + return false +} + +// IsNotNil returns true if err is not nil. +func IsNotNil(err error) bool { + return err != nil +} + +// Predicate determines whether an error should be retried. +type Predicate func(error) (retry bool) + +// Retry retries a given function, f, until a predicate is satisfied, using +// exponential backoff. If the predicate is never satisfied, it will return the +// last error returned by f. +func Retry(f func() error, p Predicate, backoff wait.Backoff) (err error) { + if f == nil { + return fmt.Errorf("nil f passed to retry") + } + if p == nil { + return fmt.Errorf("nil p passed to retry") + } + + condition := func() (bool, error) { + err = f() + if p(err) { + return false, nil + } + return true, err + } + + wait.ExponentialBackoff(backoff, condition) + return +} diff --git a/vendor/github.com/google/go-containerregistry/internal/retry/wait/kubernetes_apimachinery_wait.go b/vendor/github.com/google/go-containerregistry/internal/retry/wait/kubernetes_apimachinery_wait.go new file mode 100644 index 0000000000..ab06e5f109 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/internal/retry/wait/kubernetes_apimachinery_wait.go @@ -0,0 +1,123 @@ +/* +Copyright 2014 The Kubernetes Authors. + +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. +*/ + +// Package wait is a subset of k8s.io/apimachinery to avoid conflicts +// in dependencies (specifically, logging). +package wait + +import ( + "errors" + "math/rand" + "time" +) + +// Jitter returns a time.Duration between duration and duration + maxFactor * +// duration. +// +// This allows clients to avoid converging on periodic behavior. If maxFactor +// is 0.0, a suggested default value will be chosen. +func Jitter(duration time.Duration, maxFactor float64) time.Duration { + if maxFactor <= 0.0 { + maxFactor = 1.0 + } + wait := duration + time.Duration(rand.Float64()*maxFactor*float64(duration)) + return wait +} + +// ErrWaitTimeout is returned when the condition exited without success. +var ErrWaitTimeout = errors.New("timed out waiting for the condition") + +// ConditionFunc returns true if the condition is satisfied, or an error +// if the loop should be aborted. +type ConditionFunc func() (done bool, err error) + +// Backoff holds parameters applied to a Backoff function. +type Backoff struct { + // The initial duration. + Duration time.Duration + // Duration is multiplied by factor each iteration, if factor is not zero + // and the limits imposed by Steps and Cap have not been reached. + // Should not be negative. + // The jitter does not contribute to the updates to the duration parameter. + Factor float64 + // The sleep at each iteration is the duration plus an additional + // amount chosen uniformly at random from the interval between + // zero and `jitter*duration`. + Jitter float64 + // The remaining number of iterations in which the duration + // parameter may change (but progress can be stopped earlier by + // hitting the cap). If not positive, the duration is not + // changed. Used for exponential backoff in combination with + // Factor and Cap. + Steps int + // A limit on revised values of the duration parameter. If a + // multiplication by the factor parameter would make the duration + // exceed the cap then the duration is set to the cap and the + // steps parameter is set to zero. + Cap time.Duration +} + +// Step (1) returns an amount of time to sleep determined by the +// original Duration and Jitter and (2) mutates the provided Backoff +// to update its Steps and Duration. +func (b *Backoff) Step() time.Duration { + if b.Steps < 1 { + if b.Jitter > 0 { + return Jitter(b.Duration, b.Jitter) + } + return b.Duration + } + b.Steps-- + + duration := b.Duration + + // calculate the next step + if b.Factor != 0 { + b.Duration = time.Duration(float64(b.Duration) * b.Factor) + if b.Cap > 0 && b.Duration > b.Cap { + b.Duration = b.Cap + b.Steps = 0 + } + } + + if b.Jitter > 0 { + duration = Jitter(duration, b.Jitter) + } + return duration +} + +// ExponentialBackoff repeats a condition check with exponential backoff. +// +// It repeatedly checks the condition and then sleeps, using `backoff.Step()` +// to determine the length of the sleep and adjust Duration and Steps. +// Stops and returns as soon as: +// 1. the condition check returns true or an error, +// 2. `backoff.Steps` checks of the condition have been done, or +// 3. a sleep truncated by the cap on duration has been completed. +// In case (1) the returned error is what the condition function returned. +// In all other cases, ErrWaitTimeout is returned. +func ExponentialBackoff(backoff Backoff, condition ConditionFunc) error { + for backoff.Steps > 0 { + if ok, err := condition(); err != nil || ok { + return err + } + if backoff.Steps == 1 { + break + } + time.Sleep(backoff.Step()) + } + return ErrWaitTimeout +} diff --git a/vendor/github.com/google/go-containerregistry/internal/verify/verify.go b/vendor/github.com/google/go-containerregistry/internal/verify/verify.go new file mode 100644 index 0000000000..463f7e4b39 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/internal/verify/verify.go @@ -0,0 +1,122 @@ +// Copyright 2020 Google LLC All Rights Reserved. +// +// 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. + +// Package verify provides a ReadCloser that verifies content matches the +// expected hash values. +package verify + +import ( + "bytes" + "encoding/hex" + "errors" + "fmt" + "hash" + "io" + + "github.com/google/go-containerregistry/internal/and" + v1 "github.com/google/go-containerregistry/pkg/v1" +) + +// SizeUnknown is a sentinel value to indicate that the expected size is not known. +const SizeUnknown = -1 + +type verifyReader struct { + inner io.Reader + hasher hash.Hash + expected v1.Hash + gotSize, wantSize int64 +} + +// Error provides information about the failed hash verification. +type Error struct { + got string + want v1.Hash + gotSize int64 +} + +func (v Error) Error() string { + return fmt.Sprintf("error verifying %s checksum after reading %d bytes; got %q, want %q", + v.want.Algorithm, v.gotSize, v.got, v.want) +} + +// Read implements io.Reader +func (vc *verifyReader) Read(b []byte) (int, error) { + n, err := vc.inner.Read(b) + vc.gotSize += int64(n) + if err == io.EOF { + if vc.wantSize != SizeUnknown && vc.gotSize != vc.wantSize { + return n, fmt.Errorf("error verifying size; got %d, want %d", vc.gotSize, vc.wantSize) + } + got := hex.EncodeToString(vc.hasher.Sum(nil)) + if want := vc.expected.Hex; got != want { + return n, Error{ + got: vc.expected.Algorithm + ":" + got, + want: vc.expected, + gotSize: vc.gotSize, + } + } + } + return n, err +} + +// ReadCloser wraps the given io.ReadCloser to verify that its contents match +// the provided v1.Hash before io.EOF is returned. +// +// The reader will only be read up to size bytes, to prevent resource +// exhaustion. If EOF is returned before size bytes are read, an error is +// returned. +// +// A size of SizeUnknown (-1) indicates disables size verification when the size +// is unknown ahead of time. +func ReadCloser(r io.ReadCloser, size int64, h v1.Hash) (io.ReadCloser, error) { + w, err := v1.Hasher(h.Algorithm) + if err != nil { + return nil, err + } + r2 := io.TeeReader(r, w) // pass all writes to the hasher. + if size != SizeUnknown { + r2 = io.LimitReader(r2, size) // if we know the size, limit to that size. + } + return &and.ReadCloser{ + Reader: &verifyReader{ + inner: r2, + hasher: w, + expected: h, + wantSize: size, + }, + CloseFunc: r.Close, + }, nil +} + +// Descriptor verifies that the embedded Data field matches the Size and Digest +// fields of the given v1.Descriptor, returning an error if the Data field is +// missing or if it contains incorrect data. +func Descriptor(d v1.Descriptor) error { + if d.Data == nil { + return errors.New("error verifying descriptor; Data == nil") + } + + h, sz, err := v1.SHA256(bytes.NewReader(d.Data)) + if err != nil { + return err + } + if h != d.Digest { + return fmt.Errorf("error verifying Digest; got %q, want %q", h, d.Digest) + } + if sz != d.Size { + return fmt.Errorf("error verifying Size; got %d, want %d", sz, d.Size) + } + + return nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/authn/README.md b/vendor/github.com/google/go-containerregistry/pkg/authn/README.md new file mode 100644 index 0000000000..0b46b322d0 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/authn/README.md @@ -0,0 +1,322 @@ +# `authn` + +[![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/authn?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/authn) + +This README outlines how we acquire and use credentials when interacting with a registry. + +As much as possible, we attempt to emulate `docker`'s authentication behavior and configuration so that this library "just works" if you've already configured credentials that work with `docker`; however, when things don't work, a basic understanding of what's going on can help with debugging. + +The official documentation for how authentication with `docker` works is (reasonably) scattered across several different sites and GitHub repositories, so we've tried to summarize the relevant bits here. + +## tl;dr for consumers of this package + +By default, [`pkg/v1/remote`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote) uses [`Anonymous`](https://godoc.org/github.com/google/go-containerregistry/pkg/authn#Anonymous) credentials (i.e. _none_), which for most registries will only allow read access to public images. + +To use the credentials found in your Docker config file, you can use the [`DefaultKeychain`](https://godoc.org/github.com/google/go-containerregistry/pkg/authn#DefaultKeychain), e.g.: + +```go +package main + +import ( + "fmt" + + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote" +) + +func main() { + ref, err := name.ParseReference("registry.example.com/private/repo") + if err != nil { + panic(err) + } + + // Fetch the manifest using default credentials. + img, err := remote.Get(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain)) + if err != nil { + panic(err) + } + + // Prints the digest of registry.example.com/private/repo + fmt.Println(img.Digest) +} +``` + +The `DefaultKeychain` will use credentials as described in your Docker config file -- usually `~/.docker/config.json`, or `%USERPROFILE%\.docker\config.json` on Windows -- or the location described by the `DOCKER_CONFIG` environment variable, if set. + +If those are not found, `DefaultKeychain` will look for credentials configured using [Podman's expectation](https://docs.podman.io/en/latest/markdown/podman-login.1.html) that these are found in `${XDG_RUNTIME_DIR}/containers/auth.json`. + +[See below](#docker-config-auth) for more information about what is configured in this file. + +## Emulating Cloud Provider Credential Helpers + +[`pkg/v1/google.Keychain`](https://pkg.go.dev/github.com/google/go-containerregistry/pkg/v1/google#Keychain) provides a `Keychain` implementation that emulates [`docker-credential-gcr`](https://github.com/GoogleCloudPlatform/docker-credential-gcr) to find credentials in the environment. +See [`google.NewEnvAuthenticator`](https://pkg.go.dev/github.com/google/go-containerregistry/pkg/v1/google#NewEnvAuthenticator) and [`google.NewGcloudAuthenticator`](https://pkg.go.dev/github.com/google/go-containerregistry/pkg/v1/google#NewGcloudAuthenticator) for more information. + +To emulate other credential helpers without requiring them to be available as executables, [`NewKeychainFromHelper`](https://pkg.go.dev/github.com/google/go-containerregistry/pkg/authn#NewKeychainFromHelper) provides an adapter that takes a Go implementation satisfying a subset of the [`credentials.Helper`](https://pkg.go.dev/github.com/docker/docker-credential-helpers/credentials#Helper) interface, and makes it available as a `Keychain`. + +This means that you can emulate, for example, [Amazon ECR's `docker-credential-ecr-login` credential helper](https://github.com/awslabs/amazon-ecr-credential-helper) using the same implementation: + +```go +import ( + ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login" + "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/api" + + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/v1/remote" +) + +func main() { + // ... + ecrHelper := ecr.ECRHelper{ClientFactory: api.DefaultClientFactory{}} + img, err := remote.Get(ref, remote.WithAuthFromKeychain(authn.NewKeychainFromHelper(ecrHelper))) + if err != nil { + panic(err) + } + // ... +} +``` + +Likewise, you can emulate [Azure's ACR `docker-credential-acr-env` credential helper](https://github.com/chrismellard/docker-credential-acr-env): + +```go +import ( + "github.com/chrismellard/docker-credential-acr-env/pkg/credhelper" + + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/v1/remote" +) + +func main() { + // ... + acrHelper := credhelper.NewACRCredentialsHelper() + img, err := remote.Get(ref, remote.WithAuthFromKeychain(authn.NewKeychainFromHelper(acrHelper))) + if err != nil { + panic(err) + } + // ... +} +``` + + + +## Using Multiple `Keychain`s + +[`NewMultiKeychain`](https://pkg.go.dev/github.com/google/go-containerregistry/pkg/authn#NewMultiKeychain) allows you to specify multiple `Keychain` implementations, which will be checked in order when credentials are needed. + +For example: + +```go +kc := authn.NewMultiKeychain( + authn.DefaultKeychain, + google.Keychain, + authn.NewFromHelper(ecr.ECRHelper{ClientFactory: api.DefaultClientFactory{}}), + authn.NewFromHelper(acr.ACRCredHelper{}), +) +``` + +This multi-keychain will: + +- first check for credentials found in the Docker config file, as describe above, then +- check for GCP credentials available in the environment, as described above, then +- check for ECR credentials by emulating the ECR credential helper, then +- check for ACR credentials by emulating the ACR credential helper. + +If any keychain implementation is able to provide credentials for the request, they will be used, and further keychain implementations will not be consulted. + +If no implementations are able to provide credentials, `Anonymous` credentials will be used. + +## Docker Config Auth + +What follows attempts to gather useful information about Docker's config.json and make it available in one place. + +If you have questions, please [file an issue](https://github.com/google/go-containerregistry/issues/new). + +### Plaintext + +The config file is where your credentials are stored when you invoke `docker login`, e.g. the contents may look something like this: + +```json +{ + "auths": { + "registry.example.com": { + "auth": "QXp1cmVEaWFtb25kOmh1bnRlcjI=" + } + } +} +``` + +The `auths` map has an entry per registry, and the `auth` field contains your username and password encoded as [HTTP 'Basic' Auth](https://tools.ietf.org/html/rfc7617). + +**NOTE**: This means that your credentials are stored _in plaintext_: + +```bash +$ echo "QXp1cmVEaWFtb25kOmh1bnRlcjI=" | base64 -d +AzureDiamond:hunter2 +``` + +For what it's worth, this config file is equivalent to: + +```json +{ + "auths": { + "registry.example.com": { + "username": "AzureDiamond", + "password": "hunter2" + } + } +} +``` + +... which is useful to know if e.g. your CI system provides you a registry username and password via environment variables and you want to populate this file manually without invoking `docker login`. + +### Helpers + +If you log in like this, `docker` will warn you that you should use a [credential helper](https://docs.docker.com/engine/reference/commandline/login/#credentials-store), and you should! + +To configure a global credential helper: +```json +{ + "credsStore": "osxkeychain" +} +``` + +To configure a per-registry credential helper: +```json +{ + "credHelpers": { + "gcr.io": "gcr" + } +} +``` + +We use [`github.com/docker/cli/cli/config.Load`](https://godoc.org/github.com/docker/cli/cli/config#Load) to parse the config file and invoke any necessary credential helpers. This handles the logic of taking a [`ConfigFile`](https://github.com/docker/cli/blob/ba63a92655c0bea4857b8d6cc4991498858b3c60/cli/config/configfile/file.go#L25-L54) + registry domain and producing an [`AuthConfig`](https://github.com/docker/cli/blob/ba63a92655c0bea4857b8d6cc4991498858b3c60/cli/config/types/authconfig.go#L3-L22), which determines how we authenticate to the registry. + +## Credential Helpers + +The [credential helper protocol](https://github.com/docker/docker-credential-helpers) allows you to configure a binary that supplies credentials for the registry, rather than hard-coding them in the config file. + +The protocol has several verbs, but the one we most care about is `get`. + +For example, using the following config file: +```json +{ + "credHelpers": { + "gcr.io": "gcr", + "eu.gcr.io": "gcr" + } +} +``` + +To acquire credentials for `gcr.io`, we look in the `credHelpers` map to find +the credential helper for `gcr.io` is `gcr`. By appending that value to +`docker-credential-`, we can get the name of the binary we need to use. + +For this example, that's `docker-credential-gcr`, which must be on our `$PATH`. +We'll then invoke that binary to get credentials: + +```bash +$ echo "gcr.io" | docker-credential-gcr get +{"Username":"_token","Secret":""} +``` + +You can configure the same credential helper for multiple registries, which is +why we need to pass the domain in via STDIN, e.g. if we were trying to access +`eu.gcr.io`, we'd do this instead: + +```bash +$ echo "eu.gcr.io" | docker-credential-gcr get +{"Username":"_token","Secret":""} +``` + +### Debugging credential helpers + +If a credential helper is configured but doesn't seem to be working, it can be +challenging to debug. Implementing a fake credential helper lets you poke around +to make it easier to see where the failure is happening. + +This "implements" a credential helper with hard-coded values: +``` +#!/usr/bin/env bash +echo '{"Username":"","Secret":"hunter2"}' +``` + + +This implements a credential helper that prints the output of +`docker-credential-gcr` to both stderr and whatever called it, which allows you +to snoop on another credential helper: +``` +#!/usr/bin/env bash +docker-credential-gcr $@ | tee >(cat 1>&2) +``` + +Put those files somewhere on your path, naming them e.g. +`docker-credential-hardcoded` and `docker-credential-tee`, then modify the +config file to use them: + +```json +{ + "credHelpers": { + "gcr.io": "tee", + "eu.gcr.io": "hardcoded" + } +} +``` + +The `docker-credential-tee` trick works with both `crane` and `docker`: + +```bash +$ crane manifest gcr.io/google-containers/pause > /dev/null +{"ServerURL":"","Username":"_dcgcr_1_5_0_token","Secret":""} + +$ docker pull gcr.io/google-containers/pause +Using default tag: latest +{"ServerURL":"","Username":"_dcgcr_1_5_0_token","Secret":""} +latest: Pulling from google-containers/pause +a3ed95caeb02: Pull complete +4964c72cd024: Pull complete +Digest: sha256:a78c2d6208eff9b672de43f880093100050983047b7b0afe0217d3656e1b0d5f +Status: Downloaded newer image for gcr.io/google-containers/pause:latest +gcr.io/google-containers/pause:latest +``` + +## The Registry + +There are two methods for authenticating against a registry: +[token](https://docs.docker.com/registry/spec/auth/token/) and +[oauth2](https://docs.docker.com/registry/spec/auth/oauth/). + +Both methods are used to acquire an opaque `Bearer` token (or +[RegistryToken](https://github.com/docker/cli/blob/ba63a92655c0bea4857b8d6cc4991498858b3c60/cli/config/types/authconfig.go#L21)) +to use in the `Authorization` header. The registry will return a `401 +Unauthorized` during the [version +check](https://github.com/opencontainers/distribution-spec/blob/2c3975d1f03b67c9a0203199038adea0413f0573/spec.md#api-version-check) +(or during normal operations) with +[Www-Authenticate](https://tools.ietf.org/html/rfc7235#section-4.1) challenge +indicating how to proceed. + +### Token + +If we get back an `AuthConfig` containing a [`Username/Password`](https://github.com/docker/cli/blob/ba63a92655c0bea4857b8d6cc4991498858b3c60/cli/config/types/authconfig.go#L5-L6) +or +[`Auth`](https://github.com/docker/cli/blob/ba63a92655c0bea4857b8d6cc4991498858b3c60/cli/config/types/authconfig.go#L7), +we'll use the token method for authentication: + +![basic](../../images/credhelper-basic.svg) + +### OAuth 2 + +If we get back an `AuthConfig` containing an [`IdentityToken`](https://github.com/docker/cli/blob/ba63a92655c0bea4857b8d6cc4991498858b3c60/cli/config/types/authconfig.go#L18) +we'll use the oauth2 method for authentication: + +![oauth](../../images/credhelper-oauth.svg) + +This happens when a credential helper returns a response with the +[`Username`](https://github.com/docker/docker-credential-helpers/blob/f78081d1f7fef6ad74ad6b79368de6348386e591/credentials/credentials.go#L16) +set to `` (no, that's not a placeholder, the literal string `""`). +It is unclear why: [moby/moby#36926](https://github.com/moby/moby/issues/36926). + +We only support the oauth2 `grant_type` for `refresh_token` ([#629](https://github.com/google/go-containerregistry/issues/629)), +since it's impossible to determine from the registry response whether we should +use oauth, and the token method for authentication is widely implemented by +registries. diff --git a/vendor/github.com/google/go-containerregistry/pkg/authn/anon.go b/vendor/github.com/google/go-containerregistry/pkg/authn/anon.go new file mode 100644 index 0000000000..83214957d5 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/authn/anon.go @@ -0,0 +1,26 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package authn + +// anonymous implements Authenticator for anonymous authentication. +type anonymous struct{} + +// Authorization implements Authenticator. +func (a *anonymous) Authorization() (*AuthConfig, error) { + return &AuthConfig{}, nil +} + +// Anonymous is a singleton Authenticator for providing anonymous auth. +var Anonymous Authenticator = &anonymous{} diff --git a/vendor/github.com/google/go-containerregistry/pkg/authn/auth.go b/vendor/github.com/google/go-containerregistry/pkg/authn/auth.go new file mode 100644 index 0000000000..0111f1ae72 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/authn/auth.go @@ -0,0 +1,30 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package authn + +// auth is an Authenticator that simply returns the wrapped AuthConfig. +type auth struct { + config AuthConfig +} + +// FromConfig returns an Authenticator that just returns the given AuthConfig. +func FromConfig(cfg AuthConfig) Authenticator { + return &auth{cfg} +} + +// Authorization implements Authenticator. +func (a *auth) Authorization() (*AuthConfig, error) { + return &a.config, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/authn/authn.go b/vendor/github.com/google/go-containerregistry/pkg/authn/authn.go new file mode 100644 index 0000000000..172d218e4f --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/authn/authn.go @@ -0,0 +1,115 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package authn + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "strings" +) + +// Authenticator is used to authenticate Docker transports. +type Authenticator interface { + // Authorization returns the value to use in an http transport's Authorization header. + Authorization() (*AuthConfig, error) +} + +// AuthConfig contains authorization information for connecting to a Registry +// Inlined what we use from github.com/docker/cli/cli/config/types +type AuthConfig struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Auth string `json:"auth,omitempty"` + + // IdentityToken is used to authenticate the user and get + // an access token for the registry. + IdentityToken string `json:"identitytoken,omitempty"` + + // RegistryToken is a bearer token to be sent to a registry + RegistryToken string `json:"registrytoken,omitempty"` +} + +// This is effectively a copy of the type AuthConfig. This simplifies +// JSON unmarshalling since AuthConfig methods are not inherited +type authConfig AuthConfig + +// UnmarshalJSON implements json.Unmarshaler +func (a *AuthConfig) UnmarshalJSON(data []byte) error { + var shadow authConfig + err := json.Unmarshal(data, &shadow) + if err != nil { + return err + } + + *a = (AuthConfig)(shadow) + + if len(shadow.Auth) != 0 { + var derr error + a.Username, a.Password, derr = decodeDockerConfigFieldAuth(shadow.Auth) + if derr != nil { + err = fmt.Errorf("unable to decode auth field: %w", derr) + } + } else if len(a.Username) != 0 && len(a.Password) != 0 { + a.Auth = encodeDockerConfigFieldAuth(shadow.Username, shadow.Password) + } + + return err +} + +// MarshalJSON implements json.Marshaler +func (a AuthConfig) MarshalJSON() ([]byte, error) { + shadow := (authConfig)(a) + shadow.Auth = encodeDockerConfigFieldAuth(shadow.Username, shadow.Password) + return json.Marshal(shadow) +} + +// decodeDockerConfigFieldAuth deserializes the "auth" field from dockercfg into a +// username and a password. The format of the auth field is base64(:). +// +// From https://github.com/kubernetes/kubernetes/blob/75e49ec824b183288e1dbaccfd7dbe77d89db381/pkg/credentialprovider/config.go +// Copyright 2014 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 +func decodeDockerConfigFieldAuth(field string) (username, password string, err error) { + var decoded []byte + // StdEncoding can only decode padded string + // RawStdEncoding can only decode unpadded string + if strings.HasSuffix(strings.TrimSpace(field), "=") { + // decode padded data + decoded, err = base64.StdEncoding.DecodeString(field) + } else { + // decode unpadded data + decoded, err = base64.RawStdEncoding.DecodeString(field) + } + + if err != nil { + return + } + + parts := strings.SplitN(string(decoded), ":", 2) + if len(parts) != 2 { + err = fmt.Errorf("must be formatted as base64(username:password)") + return + } + + username = parts[0] + password = parts[1] + + return +} + +func encodeDockerConfigFieldAuth(username, password string) string { + return base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/authn/basic.go b/vendor/github.com/google/go-containerregistry/pkg/authn/basic.go new file mode 100644 index 0000000000..500cb6616f --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/authn/basic.go @@ -0,0 +1,29 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package authn + +// Basic implements Authenticator for basic authentication. +type Basic struct { + Username string + Password string +} + +// Authorization implements Authenticator. +func (b *Basic) Authorization() (*AuthConfig, error) { + return &AuthConfig{ + Username: b.Username, + Password: b.Password, + }, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/authn/bearer.go b/vendor/github.com/google/go-containerregistry/pkg/authn/bearer.go new file mode 100644 index 0000000000..4cf86df92f --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/authn/bearer.go @@ -0,0 +1,27 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package authn + +// Bearer implements Authenticator for bearer authentication. +type Bearer struct { + Token string `json:"token"` +} + +// Authorization implements Authenticator. +func (b *Bearer) Authorization() (*AuthConfig, error) { + return &AuthConfig{ + RegistryToken: b.Token, + }, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/authn/doc.go b/vendor/github.com/google/go-containerregistry/pkg/authn/doc.go new file mode 100644 index 0000000000..c2a5fc0267 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/authn/doc.go @@ -0,0 +1,17 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +// Package authn defines different methods of authentication for +// talking to a container registry. +package authn diff --git a/vendor/github.com/google/go-containerregistry/pkg/authn/keychain.go b/vendor/github.com/google/go-containerregistry/pkg/authn/keychain.go new file mode 100644 index 0000000000..cfc749b627 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/authn/keychain.go @@ -0,0 +1,174 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package authn + +import ( + "os" + "path/filepath" + "sync" + + "github.com/docker/cli/cli/config" + "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/cli/config/types" + "github.com/google/go-containerregistry/pkg/name" + "github.com/mitchellh/go-homedir" +) + +// Resource represents a registry or repository that can be authenticated against. +type Resource interface { + // String returns the full string representation of the target, e.g. + // gcr.io/my-project or just gcr.io. + String() string + + // RegistryStr returns just the registry portion of the target, e.g. for + // gcr.io/my-project, this should just return gcr.io. This is needed to + // pull out an appropriate hostname. + RegistryStr() string +} + +// Keychain is an interface for resolving an image reference to a credential. +type Keychain interface { + // Resolve looks up the most appropriate credential for the specified target. + Resolve(Resource) (Authenticator, error) +} + +// defaultKeychain implements Keychain with the semantics of the standard Docker +// credential keychain. +type defaultKeychain struct { + mu sync.Mutex +} + +var ( + // DefaultKeychain implements Keychain by interpreting the docker config file. + DefaultKeychain Keychain = &defaultKeychain{} +) + +const ( + // DefaultAuthKey is the key used for dockerhub in config files, which + // is hardcoded for historical reasons. + DefaultAuthKey = "https://" + name.DefaultRegistry + "/v1/" +) + +// Resolve implements Keychain. +func (dk *defaultKeychain) Resolve(target Resource) (Authenticator, error) { + dk.mu.Lock() + defer dk.mu.Unlock() + + // Podman users may have their container registry auth configured in a + // different location, that Docker packages aren't aware of. + // If the Docker config file isn't found, we'll fallback to look where + // Podman configures it, and parse that as a Docker auth config instead. + + // First, check $HOME/.docker/config.json + foundDockerConfig := false + home, err := homedir.Dir() + if err == nil { + if _, err := os.Stat(filepath.Join(home, ".docker/config.json")); err == nil { + foundDockerConfig = true + } + } + // If $HOME/.docker/config.json isn't found, check $DOCKER_CONFIG (if set) + if !foundDockerConfig && os.Getenv("DOCKER_CONFIG") != "" { + if _, err := os.Stat(filepath.Join(os.Getenv("DOCKER_CONFIG"), "config.json")); err == nil { + foundDockerConfig = true + } + } + // If either of those locations are found, load it using Docker's + // config.Load, which may fail if the config can't be parsed. + // + // If neither was found, look for Podman's auth at + // $XDG_RUNTIME_DIR/containers/auth.json and attempt to load it as a + // Docker config. + // + // If neither are found, fallback to Anonymous. + var cf *configfile.ConfigFile + if foundDockerConfig { + cf, err = config.Load(os.Getenv("DOCKER_CONFIG")) + if err != nil { + return nil, err + } + } else { + f, err := os.Open(filepath.Join(os.Getenv("XDG_RUNTIME_DIR"), "containers/auth.json")) + if err != nil { + return Anonymous, nil + } + defer f.Close() + cf, err = config.LoadFromReader(f) + if err != nil { + return nil, err + } + } + + // See: + // https://github.com/google/ko/issues/90 + // https://github.com/moby/moby/blob/fc01c2b481097a6057bec3cd1ab2d7b4488c50c4/registry/config.go#L397-L404 + var cfg, empty types.AuthConfig + for _, key := range []string{ + target.String(), + target.RegistryStr(), + } { + if key == name.DefaultRegistry { + key = DefaultAuthKey + } + + cfg, err = cf.GetAuthConfig(key) + if err != nil { + return nil, err + } + if cfg != empty { + break + } + } + if cfg == empty { + return Anonymous, nil + } + + return FromConfig(AuthConfig{ + Username: cfg.Username, + Password: cfg.Password, + Auth: cfg.Auth, + IdentityToken: cfg.IdentityToken, + RegistryToken: cfg.RegistryToken, + }), nil +} + +// Helper is a subset of the Docker credential helper credentials.Helper +// interface used by NewKeychainFromHelper. +// +// See: +// https://pkg.go.dev/github.com/docker/docker-credential-helpers/credentials#Helper +type Helper interface { + Get(serverURL string) (string, string, error) +} + +// NewKeychainFromHelper returns a Keychain based on a Docker credential helper +// implementation that can Get username and password credentials for a given +// server URL. +func NewKeychainFromHelper(h Helper) Keychain { return wrapper{h} } + +type wrapper struct{ h Helper } + +func (w wrapper) Resolve(r Resource) (Authenticator, error) { + u, p, err := w.h.Get(r.RegistryStr()) + if err != nil { + return Anonymous, nil + } + // If the secret being stored is an identity token, the Username should be set to + // ref: https://docs.docker.com/engine/reference/commandline/login/#credential-helper-protocol + if u == "" { + return FromConfig(AuthConfig{Username: u, IdentityToken: p}), nil + } + return FromConfig(AuthConfig{Username: u, Password: p}), nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/authn/multikeychain.go b/vendor/github.com/google/go-containerregistry/pkg/authn/multikeychain.go new file mode 100644 index 0000000000..3b1804f5d0 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/authn/multikeychain.go @@ -0,0 +1,41 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package authn + +type multiKeychain struct { + keychains []Keychain +} + +// Assert that our multi-keychain implements Keychain. +var _ (Keychain) = (*multiKeychain)(nil) + +// NewMultiKeychain composes a list of keychains into one new keychain. +func NewMultiKeychain(kcs ...Keychain) Keychain { + return &multiKeychain{keychains: kcs} +} + +// Resolve implements Keychain. +func (mk *multiKeychain) Resolve(target Resource) (Authenticator, error) { + for _, kc := range mk.keychains { + auth, err := kc.Resolve(target) + if err != nil { + return nil, err + } + if auth != Anonymous { + return auth, nil + } + } + return Anonymous, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/logs/logs.go b/vendor/github.com/google/go-containerregistry/pkg/logs/logs.go new file mode 100644 index 0000000000..5d25d63d61 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/logs/logs.go @@ -0,0 +1,39 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +// Package logs exposes the loggers used by this library. +package logs + +import ( + "io/ioutil" + "log" +) + +var ( + // Warn is used to log non-fatal errors. + Warn = log.New(ioutil.Discard, "", log.LstdFlags) + + // Progress is used to log notable, successful events. + Progress = log.New(ioutil.Discard, "", log.LstdFlags) + + // Debug is used to log information that is useful for debugging. + Debug = log.New(ioutil.Discard, "", log.LstdFlags) +) + +// Enabled checks to see if the logger's writer is set to something other +// than ioutil.Discard. This allows callers to avoid expensive operations +// that will end up in /dev/null anyway. +func Enabled(l *log.Logger) bool { + return l.Writer() != ioutil.Discard +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/config.go b/vendor/github.com/google/go-containerregistry/pkg/v1/config.go new file mode 100644 index 0000000000..a950b397c1 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/config.go @@ -0,0 +1,133 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package v1 + +import ( + "encoding/json" + "io" + "time" +) + +// ConfigFile is the configuration file that holds the metadata describing +// how to launch a container. See: +// https://github.com/opencontainers/image-spec/blob/master/config.md +// +// docker_version and os.version are not part of the spec but included +// for backwards compatibility. +type ConfigFile struct { + Architecture string `json:"architecture"` + Author string `json:"author,omitempty"` + Container string `json:"container,omitempty"` + Created Time `json:"created,omitempty"` + DockerVersion string `json:"docker_version,omitempty"` + History []History `json:"history,omitempty"` + OS string `json:"os"` + RootFS RootFS `json:"rootfs"` + Config Config `json:"config"` + OSVersion string `json:"os.version,omitempty"` +} + +// History is one entry of a list recording how this container image was built. +type History struct { + Author string `json:"author,omitempty"` + Created Time `json:"created,omitempty"` + CreatedBy string `json:"created_by,omitempty"` + Comment string `json:"comment,omitempty"` + EmptyLayer bool `json:"empty_layer,omitempty"` +} + +// Time is a wrapper around time.Time to help with deep copying +type Time struct { + time.Time +} + +// DeepCopyInto creates a deep-copy of the Time value. The underlying time.Time +// type is effectively immutable in the time API, so it is safe to +// copy-by-assign, despite the presence of (unexported) Pointer fields. +func (t *Time) DeepCopyInto(out *Time) { + *out = *t +} + +// RootFS holds the ordered list of file system deltas that comprise the +// container image's root filesystem. +type RootFS struct { + Type string `json:"type"` + DiffIDs []Hash `json:"diff_ids"` +} + +// HealthConfig holds configuration settings for the HEALTHCHECK feature. +type HealthConfig struct { + // Test is the test to perform to check that the container is healthy. + // An empty slice means to inherit the default. + // The options are: + // {} : inherit healthcheck + // {"NONE"} : disable healthcheck + // {"CMD", args...} : exec arguments directly + // {"CMD-SHELL", command} : run command with system's default shell + Test []string `json:",omitempty"` + + // Zero means to inherit. Durations are expressed as integer nanoseconds. + Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks. + Timeout time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung. + StartPeriod time.Duration `json:",omitempty"` // The start period for the container to initialize before the retries starts to count down. + + // Retries is the number of consecutive failures needed to consider a container as unhealthy. + // Zero means inherit. + Retries int `json:",omitempty"` +} + +// Config is a submessage of the config file described as: +// The execution parameters which SHOULD be used as a base when running +// a container using the image. +// The names of the fields in this message are chosen to reflect the JSON +// payload of the Config as defined here: +// https://git.io/vrAET +// and +// https://github.com/opencontainers/image-spec/blob/master/config.md +type Config struct { + AttachStderr bool `json:"AttachStderr,omitempty"` + AttachStdin bool `json:"AttachStdin,omitempty"` + AttachStdout bool `json:"AttachStdout,omitempty"` + Cmd []string `json:"Cmd,omitempty"` + Healthcheck *HealthConfig `json:"Healthcheck,omitempty"` + Domainname string `json:"Domainname,omitempty"` + Entrypoint []string `json:"Entrypoint,omitempty"` + Env []string `json:"Env,omitempty"` + Hostname string `json:"Hostname,omitempty"` + Image string `json:"Image,omitempty"` + Labels map[string]string `json:"Labels,omitempty"` + OnBuild []string `json:"OnBuild,omitempty"` + OpenStdin bool `json:"OpenStdin,omitempty"` + StdinOnce bool `json:"StdinOnce,omitempty"` + Tty bool `json:"Tty,omitempty"` + User string `json:"User,omitempty"` + Volumes map[string]struct{} `json:"Volumes,omitempty"` + WorkingDir string `json:"WorkingDir,omitempty"` + ExposedPorts map[string]struct{} `json:"ExposedPorts,omitempty"` + ArgsEscaped bool `json:"ArgsEscaped,omitempty"` + NetworkDisabled bool `json:"NetworkDisabled,omitempty"` + MacAddress string `json:"MacAddress,omitempty"` + StopSignal string `json:"StopSignal,omitempty"` + Shell []string `json:"Shell,omitempty"` +} + +// ParseConfigFile parses the io.Reader's contents into a ConfigFile. +func ParseConfigFile(r io.Reader) (*ConfigFile, error) { + cf := ConfigFile{} + if err := json.NewDecoder(r).Decode(&cf); err != nil { + return nil, err + } + return &cf, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/daemon/README.md b/vendor/github.com/google/go-containerregistry/pkg/v1/daemon/README.md new file mode 100644 index 0000000000..74fc3a87c0 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/daemon/README.md @@ -0,0 +1,11 @@ +# `daemon` + +[![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/daemon?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/daemon) + +The `daemon` package enables reading/writing images from/to the docker daemon. + +It is not fully fleshed out, but is useful for interoperability, see various issues: + +* https://github.com/google/go-containerregistry/issues/205 +* https://github.com/google/go-containerregistry/issues/552 +* https://github.com/google/go-containerregistry/issues/627 diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/daemon/doc.go b/vendor/github.com/google/go-containerregistry/pkg/v1/daemon/doc.go new file mode 100644 index 0000000000..ac05d96121 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/daemon/doc.go @@ -0,0 +1,17 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +// Package daemon provides facilities for reading/writing v1.Image from/to +// a running daemon. +package daemon diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/daemon/image.go b/vendor/github.com/google/go-containerregistry/pkg/v1/daemon/image.go new file mode 100644 index 0000000000..0b0047e523 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/daemon/image.go @@ -0,0 +1,204 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package daemon + +import ( + "bytes" + "context" + "io" + "io/ioutil" + "sync" + + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/tarball" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +type image struct { + ref name.Reference + opener *imageOpener + tarballImage v1.Image + id *v1.Hash + + once sync.Once + err error +} + +type imageOpener struct { + ref name.Reference + ctx context.Context + + buffered bool + client Client + + once sync.Once + bytes []byte + err error +} + +func (i *imageOpener) saveImage() (io.ReadCloser, error) { + return i.client.ImageSave(i.ctx, []string{i.ref.Name()}) +} + +func (i *imageOpener) bufferedOpener() (io.ReadCloser, error) { + // Store the tarball in memory and return a new reader into the bytes each time we need to access something. + i.once.Do(func() { + i.bytes, i.err = func() ([]byte, error) { + rc, err := i.saveImage() + if err != nil { + return nil, err + } + defer rc.Close() + + return ioutil.ReadAll(rc) + }() + }) + + // Wrap the bytes in a ReadCloser so it looks like an opened file. + return ioutil.NopCloser(bytes.NewReader(i.bytes)), i.err +} + +func (i *imageOpener) opener() tarball.Opener { + if i.buffered { + return i.bufferedOpener + } + + // To avoid storing the tarball in memory, do a save every time we need to access something. + return i.saveImage +} + +// Image provides access to an image reference from the Docker daemon, +// applying functional options to the underlying imageOpener before +// resolving the reference into a v1.Image. +func Image(ref name.Reference, options ...Option) (v1.Image, error) { + o, err := makeOptions(options...) + if err != nil { + return nil, err + } + + i := &imageOpener{ + ref: ref, + buffered: o.buffered, + client: o.client, + ctx: o.ctx, + } + + img := &image{ + ref: ref, + opener: i, + } + + // Eagerly fetch Image ID to ensure it actually exists. + // https://github.com/google/go-containerregistry/issues/1186 + id, err := img.ConfigName() + if err != nil { + return nil, err + } + img.id = &id + + return img, nil +} + +func (i *image) initialize() error { + // Don't re-initialize tarball if already initialized. + if i.tarballImage == nil { + i.once.Do(func() { + i.tarballImage, i.err = tarball.Image(i.opener.opener(), nil) + }) + } + return i.err +} + +func (i *image) Layers() ([]v1.Layer, error) { + if err := i.initialize(); err != nil { + return nil, err + } + return i.tarballImage.Layers() +} + +func (i *image) MediaType() (types.MediaType, error) { + if err := i.initialize(); err != nil { + return "", err + } + return i.tarballImage.MediaType() +} + +func (i *image) Size() (int64, error) { + if err := i.initialize(); err != nil { + return 0, err + } + return i.tarballImage.Size() +} + +func (i *image) ConfigName() (v1.Hash, error) { + if i.id != nil { + return *i.id, nil + } + res, _, err := i.opener.client.ImageInspectWithRaw(i.opener.ctx, i.ref.String()) + if err != nil { + return v1.Hash{}, err + } + return v1.NewHash(res.ID) +} + +func (i *image) ConfigFile() (*v1.ConfigFile, error) { + if err := i.initialize(); err != nil { + return nil, err + } + return i.tarballImage.ConfigFile() +} + +func (i *image) RawConfigFile() ([]byte, error) { + if err := i.initialize(); err != nil { + return nil, err + } + return i.tarballImage.RawConfigFile() +} + +func (i *image) Digest() (v1.Hash, error) { + if err := i.initialize(); err != nil { + return v1.Hash{}, err + } + return i.tarballImage.Digest() +} + +func (i *image) Manifest() (*v1.Manifest, error) { + if err := i.initialize(); err != nil { + return nil, err + } + return i.tarballImage.Manifest() +} + +func (i *image) RawManifest() ([]byte, error) { + if err := i.initialize(); err != nil { + return nil, err + } + return i.tarballImage.RawManifest() +} + +func (i *image) LayerByDigest(h v1.Hash) (v1.Layer, error) { + if err := i.initialize(); err != nil { + return nil, err + } + return i.tarballImage.LayerByDigest(h) +} + +func (i *image) LayerByDiffID(h v1.Hash) (v1.Layer, error) { + if err := i.initialize(); err != nil { + return nil, err + } + return i.tarballImage.LayerByDiffID(h) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/daemon/options.go b/vendor/github.com/google/go-containerregistry/pkg/v1/daemon/options.go new file mode 100644 index 0000000000..e8a5a1e5de --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/daemon/options.go @@ -0,0 +1,103 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package daemon + +import ( + "context" + "io" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" +) + +// ImageOption is an alias for Option. +// Deprecated: Use Option instead. +type ImageOption Option + +// Option is a functional option for daemon operations. +type Option func(*options) + +type options struct { + ctx context.Context + client Client + buffered bool +} + +var defaultClient = func() (Client, error) { + return client.NewClientWithOpts(client.FromEnv) +} + +func makeOptions(opts ...Option) (*options, error) { + o := &options{ + buffered: true, + ctx: context.Background(), + } + for _, opt := range opts { + opt(o) + } + + if o.client == nil { + client, err := defaultClient() + if err != nil { + return nil, err + } + o.client = client + } + o.client.NegotiateAPIVersion(o.ctx) + + return o, nil +} + +// WithBufferedOpener buffers the image. +func WithBufferedOpener() Option { + return func(o *options) { + o.buffered = true + } +} + +// WithUnbufferedOpener streams the image to avoid buffering. +func WithUnbufferedOpener() Option { + return func(o *options) { + o.buffered = false + } +} + +// WithClient is a functional option to allow injecting a docker client. +// +// By default, github.com/docker/docker/client.FromEnv is used. +func WithClient(client Client) Option { + return func(o *options) { + o.client = client + } +} + +// WithContext is a functional option to pass through a context.Context. +// +// By default, context.Background() is used. +func WithContext(ctx context.Context) Option { + return func(o *options) { + o.ctx = ctx + } +} + +// Client represents the subset of a docker client that the daemon +// package uses. +type Client interface { + NegotiateAPIVersion(ctx context.Context) + ImageSave(context.Context, []string) (io.ReadCloser, error) + ImageLoad(context.Context, io.Reader, bool) (types.ImageLoadResponse, error) + ImageTag(context.Context, string, string) error + ImageInspectWithRaw(context.Context, string) (types.ImageInspect, []byte, error) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/daemon/write.go b/vendor/github.com/google/go-containerregistry/pkg/v1/daemon/write.go new file mode 100644 index 0000000000..6264315c99 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/daemon/write.go @@ -0,0 +1,61 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package daemon + +import ( + "fmt" + "io" + "io/ioutil" + + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/tarball" +) + +// Tag adds a tag to an already existent image. +func Tag(src, dest name.Tag, options ...Option) error { + o, err := makeOptions(options...) + if err != nil { + return err + } + + return o.client.ImageTag(o.ctx, src.String(), dest.String()) +} + +// Write saves the image into the daemon as the given tag. +func Write(tag name.Tag, img v1.Image, options ...Option) (string, error) { + o, err := makeOptions(options...) + if err != nil { + return "", err + } + + pr, pw := io.Pipe() + go func() { + pw.CloseWithError(tarball.Write(tag, img, pw)) + }() + + // write the image in docker save format first, then load it + resp, err := o.client.ImageLoad(o.ctx, pr, false) + if err != nil { + return "", fmt.Errorf("error loading image: %w", err) + } + defer resp.Body.Close() + b, err := ioutil.ReadAll(resp.Body) + response := string(b) + if err != nil { + return response, fmt.Errorf("error reading load response body: %w", err) + } + return response, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/doc.go b/vendor/github.com/google/go-containerregistry/pkg/v1/doc.go new file mode 100644 index 0000000000..7a84736be2 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/doc.go @@ -0,0 +1,18 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +// +k8s:deepcopy-gen=package + +// Package v1 defines structured types for OCI v1 images +package v1 diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/empty/README.md b/vendor/github.com/google/go-containerregistry/pkg/v1/empty/README.md new file mode 100644 index 0000000000..8663a830fd --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/empty/README.md @@ -0,0 +1,8 @@ +# `empty` + +[![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/empty?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/empty) + +The empty packages provides an empty base for constructing a `v1.Image` or `v1.ImageIndex`. +This is especially useful when paired with the [`mutate`](/pkg/v1/mutate) package, +see [`mutate.Append`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/mutate#Append) +and [`mutate.AppendManifests`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/mutate#AppendManifests). diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/empty/doc.go b/vendor/github.com/google/go-containerregistry/pkg/v1/empty/doc.go new file mode 100644 index 0000000000..1a521e9a74 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/empty/doc.go @@ -0,0 +1,16 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +// Package empty provides an implementation of v1.Image equivalent to "FROM scratch". +package empty diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/empty/image.go b/vendor/github.com/google/go-containerregistry/pkg/v1/empty/image.go new file mode 100644 index 0000000000..c58a06ce02 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/empty/image.go @@ -0,0 +1,52 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package empty + +import ( + "fmt" + + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +// Image is a singleton empty image, think: FROM scratch. +var Image, _ = partial.UncompressedToImage(emptyImage{}) + +type emptyImage struct{} + +// MediaType implements partial.UncompressedImageCore. +func (i emptyImage) MediaType() (types.MediaType, error) { + return types.DockerManifestSchema2, nil +} + +// RawConfigFile implements partial.UncompressedImageCore. +func (i emptyImage) RawConfigFile() ([]byte, error) { + return partial.RawConfigFile(i) +} + +// ConfigFile implements v1.Image. +func (i emptyImage) ConfigFile() (*v1.ConfigFile, error) { + return &v1.ConfigFile{ + RootFS: v1.RootFS{ + // Some clients check this. + Type: "layers", + }, + }, nil +} + +func (i emptyImage) LayerByDiffID(h v1.Hash) (partial.UncompressedLayer, error) { + return nil, fmt.Errorf("LayerByDiffID(%s): empty image", h) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/empty/index.go b/vendor/github.com/google/go-containerregistry/pkg/v1/empty/index.go new file mode 100644 index 0000000000..8edab24d4a --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/empty/index.go @@ -0,0 +1,63 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package empty + +import ( + "encoding/json" + "errors" + + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +// Index is a singleton empty index, think: FROM scratch. +var Index = emptyIndex{} + +type emptyIndex struct{} + +func (i emptyIndex) MediaType() (types.MediaType, error) { + return types.OCIImageIndex, nil +} + +func (i emptyIndex) Digest() (v1.Hash, error) { + return partial.Digest(i) +} + +func (i emptyIndex) Size() (int64, error) { + return partial.Size(i) +} + +func (i emptyIndex) IndexManifest() (*v1.IndexManifest, error) { + return base(), nil +} + +func (i emptyIndex) RawManifest() ([]byte, error) { + return json.Marshal(base()) +} + +func (i emptyIndex) Image(v1.Hash) (v1.Image, error) { + return nil, errors.New("empty index") +} + +func (i emptyIndex) ImageIndex(v1.Hash) (v1.ImageIndex, error) { + return nil, errors.New("empty index") +} + +func base() *v1.IndexManifest { + return &v1.IndexManifest{ + SchemaVersion: 2, + } +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/hash.go b/vendor/github.com/google/go-containerregistry/pkg/v1/hash.go new file mode 100644 index 0000000000..e9630087e1 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/hash.go @@ -0,0 +1,123 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package v1 + +import ( + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "hash" + "io" + "strconv" + "strings" +) + +// Hash is an unqualified digest of some content, e.g. sha256:deadbeef +type Hash struct { + // Algorithm holds the algorithm used to compute the hash. + Algorithm string + + // Hex holds the hex portion of the content hash. + Hex string +} + +// String reverses NewHash returning the string-form of the hash. +func (h Hash) String() string { + return fmt.Sprintf("%s:%s", h.Algorithm, h.Hex) +} + +// NewHash validates the input string is a hash and returns a strongly type Hash object. +func NewHash(s string) (Hash, error) { + h := Hash{} + if err := h.parse(s); err != nil { + return Hash{}, err + } + return h, nil +} + +// MarshalJSON implements json.Marshaler +func (h Hash) MarshalJSON() ([]byte, error) { + return json.Marshal(h.String()) +} + +// UnmarshalJSON implements json.Unmarshaler +func (h *Hash) UnmarshalJSON(data []byte) error { + s, err := strconv.Unquote(string(data)) + if err != nil { + return err + } + return h.parse(s) +} + +// MarshalText implements encoding.TextMarshaler. This is required to use +// v1.Hash as a key in a map when marshalling JSON. +func (h Hash) MarshalText() (text []byte, err error) { + return []byte(h.String()), nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. This is required to use +// v1.Hash as a key in a map when unmarshalling JSON. +func (h *Hash) UnmarshalText(text []byte) error { + return h.parse(string(text)) +} + +// Hasher returns a hash.Hash for the named algorithm (e.g. "sha256") +func Hasher(name string) (hash.Hash, error) { + switch name { + case "sha256": + return sha256.New(), nil + default: + return nil, fmt.Errorf("unsupported hash: %q", name) + } +} + +func (h *Hash) parse(unquoted string) error { + parts := strings.Split(unquoted, ":") + if len(parts) != 2 { + return fmt.Errorf("cannot parse hash: %q", unquoted) + } + + rest := strings.TrimLeft(parts[1], "0123456789abcdef") + if len(rest) != 0 { + return fmt.Errorf("found non-hex character in hash: %c", rest[0]) + } + + hasher, err := Hasher(parts[0]) + if err != nil { + return err + } + // Compare the hex to the expected size (2 hex characters per byte) + if len(parts[1]) != hasher.Size()*2 { + return fmt.Errorf("wrong number of hex digits for %s: %s", parts[0], parts[1]) + } + + h.Algorithm = parts[0] + h.Hex = parts[1] + return nil +} + +// SHA256 computes the Hash of the provided io.Reader's content. +func SHA256(r io.Reader) (Hash, int64, error) { + hasher := sha256.New() + n, err := io.Copy(hasher, r) + if err != nil { + return Hash{}, 0, err + } + return Hash{ + Algorithm: "sha256", + Hex: hex.EncodeToString(hasher.Sum(make([]byte, 0, hasher.Size()))), + }, n, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/image.go b/vendor/github.com/google/go-containerregistry/pkg/v1/image.go new file mode 100644 index 0000000000..8de9e47645 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/image.go @@ -0,0 +1,59 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package v1 + +import ( + "github.com/google/go-containerregistry/pkg/v1/types" +) + +// Image defines the interface for interacting with an OCI v1 image. +type Image interface { + // Layers returns the ordered collection of filesystem layers that comprise this image. + // The order of the list is oldest/base layer first, and most-recent/top layer last. + Layers() ([]Layer, error) + + // MediaType of this image's manifest. + MediaType() (types.MediaType, error) + + // Size returns the size of the manifest. + Size() (int64, error) + + // ConfigName returns the hash of the image's config file, also known as + // the Image ID. + ConfigName() (Hash, error) + + // ConfigFile returns this image's config file. + ConfigFile() (*ConfigFile, error) + + // RawConfigFile returns the serialized bytes of ConfigFile(). + RawConfigFile() ([]byte, error) + + // Digest returns the sha256 of this image's manifest. + Digest() (Hash, error) + + // Manifest returns this image's Manifest object. + Manifest() (*Manifest, error) + + // RawManifest returns the serialized bytes of Manifest() + RawManifest() ([]byte, error) + + // LayerByDigest returns a Layer for interacting with a particular layer of + // the image, looking it up by "digest" (the compressed hash). + LayerByDigest(Hash) (Layer, error) + + // LayerByDiffID is an analog to LayerByDigest, looking up by "diff id" + // (the uncompressed hash). + LayerByDiffID(Hash) (Layer, error) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/index.go b/vendor/github.com/google/go-containerregistry/pkg/v1/index.go new file mode 100644 index 0000000000..8e7bc8ebb3 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/index.go @@ -0,0 +1,43 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package v1 + +import ( + "github.com/google/go-containerregistry/pkg/v1/types" +) + +// ImageIndex defines the interface for interacting with an OCI image index. +type ImageIndex interface { + // MediaType of this image's manifest. + MediaType() (types.MediaType, error) + + // Digest returns the sha256 of this index's manifest. + Digest() (Hash, error) + + // Size returns the size of the manifest. + Size() (int64, error) + + // IndexManifest returns this image index's manifest object. + IndexManifest() (*IndexManifest, error) + + // RawManifest returns the serialized bytes of IndexManifest(). + RawManifest() ([]byte, error) + + // Image returns a v1.Image that this ImageIndex references. + Image(Hash) (Image, error) + + // ImageIndex returns a v1.ImageIndex that this ImageIndex references. + ImageIndex(Hash) (ImageIndex, error) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/layer.go b/vendor/github.com/google/go-containerregistry/pkg/v1/layer.go new file mode 100644 index 0000000000..57447d263d --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/layer.go @@ -0,0 +1,42 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package v1 + +import ( + "io" + + "github.com/google/go-containerregistry/pkg/v1/types" +) + +// Layer is an interface for accessing the properties of a particular layer of a v1.Image +type Layer interface { + // Digest returns the Hash of the compressed layer. + Digest() (Hash, error) + + // DiffID returns the Hash of the uncompressed layer. + DiffID() (Hash, error) + + // Compressed returns an io.ReadCloser for the compressed layer contents. + Compressed() (io.ReadCloser, error) + + // Uncompressed returns an io.ReadCloser for the uncompressed layer contents. + Uncompressed() (io.ReadCloser, error) + + // Size returns the compressed size of the Layer. + Size() (int64, error) + + // MediaType returns the media type of the Layer. + MediaType() (types.MediaType, error) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/layout/README.md b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/README.md new file mode 100644 index 0000000000..54bee6d9fd --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/README.md @@ -0,0 +1,5 @@ +# `layout` + +[![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/layout?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/layout) + +The `layout` package implements support for interacting with an [OCI Image Layout](https://github.com/opencontainers/image-spec/blob/master/image-layout.md). diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/layout/blob.go b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/blob.go new file mode 100644 index 0000000000..ba90d4cdb4 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/blob.go @@ -0,0 +1,38 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package layout + +import ( + "io" + "io/ioutil" + "os" + + v1 "github.com/google/go-containerregistry/pkg/v1" +) + +// Blob returns a blob with the given hash from the Path. +func (l Path) Blob(h v1.Hash) (io.ReadCloser, error) { + return os.Open(l.blobPath(h)) +} + +// Bytes is a convenience function to return a blob from the Path as +// a byte slice. +func (l Path) Bytes(h v1.Hash) ([]byte, error) { + return ioutil.ReadFile(l.blobPath(h)) +} + +func (l Path) blobPath(h v1.Hash) string { + return l.path("blobs", h.Algorithm, h.Hex) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/layout/doc.go b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/doc.go new file mode 100644 index 0000000000..d80d273639 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/doc.go @@ -0,0 +1,19 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +// Package layout provides facilities for reading/writing artifacts from/to +// an OCI image layout on disk, see: +// +// https://github.com/opencontainers/image-spec/blob/master/image-layout.md +package layout diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/layout/image.go b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/image.go new file mode 100644 index 0000000000..c9ae9665c3 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/image.go @@ -0,0 +1,139 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package layout + +import ( + "fmt" + "io" + "os" + "sync" + + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +type layoutImage struct { + path Path + desc v1.Descriptor + manifestLock sync.Mutex // Protects rawManifest + rawManifest []byte +} + +var _ partial.CompressedImageCore = (*layoutImage)(nil) + +// Image reads a v1.Image with digest h from the Path. +func (l Path) Image(h v1.Hash) (v1.Image, error) { + ii, err := l.ImageIndex() + if err != nil { + return nil, err + } + + return ii.Image(h) +} + +func (li *layoutImage) MediaType() (types.MediaType, error) { + return li.desc.MediaType, nil +} + +// Implements WithManifest for partial.Blobset. +func (li *layoutImage) Manifest() (*v1.Manifest, error) { + return partial.Manifest(li) +} + +func (li *layoutImage) RawManifest() ([]byte, error) { + li.manifestLock.Lock() + defer li.manifestLock.Unlock() + if li.rawManifest != nil { + return li.rawManifest, nil + } + + b, err := li.path.Bytes(li.desc.Digest) + if err != nil { + return nil, err + } + + li.rawManifest = b + return li.rawManifest, nil +} + +func (li *layoutImage) RawConfigFile() ([]byte, error) { + manifest, err := li.Manifest() + if err != nil { + return nil, err + } + + return li.path.Bytes(manifest.Config.Digest) +} + +func (li *layoutImage) LayerByDigest(h v1.Hash) (partial.CompressedLayer, error) { + manifest, err := li.Manifest() + if err != nil { + return nil, err + } + + if h == manifest.Config.Digest { + return &compressedBlob{ + path: li.path, + desc: manifest.Config, + }, nil + } + + for _, desc := range manifest.Layers { + if h == desc.Digest { + return &compressedBlob{ + path: li.path, + desc: desc, + }, nil + } + } + + return nil, fmt.Errorf("could not find layer in image: %s", h) +} + +type compressedBlob struct { + path Path + desc v1.Descriptor +} + +func (b *compressedBlob) Digest() (v1.Hash, error) { + return b.desc.Digest, nil +} + +func (b *compressedBlob) Compressed() (io.ReadCloser, error) { + return b.path.Blob(b.desc.Digest) +} + +func (b *compressedBlob) Size() (int64, error) { + return b.desc.Size, nil +} + +func (b *compressedBlob) MediaType() (types.MediaType, error) { + return b.desc.MediaType, nil +} + +// Descriptor implements partial.withDescriptor. +func (b *compressedBlob) Descriptor() (*v1.Descriptor, error) { + return &b.desc, nil +} + +// See partial.Exists. +func (b *compressedBlob) Exists() (bool, error) { + _, err := os.Stat(b.path.blobPath(b.desc.Digest)) + if os.IsNotExist(err) { + return false, nil + } + return err == nil, err +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/layout/index.go b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/index.go new file mode 100644 index 0000000000..6b7da2c221 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/index.go @@ -0,0 +1,161 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package layout + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +var _ v1.ImageIndex = (*layoutIndex)(nil) + +type layoutIndex struct { + mediaType types.MediaType + path Path + rawIndex []byte +} + +// ImageIndexFromPath is a convenience function which constructs a Path and returns its v1.ImageIndex. +func ImageIndexFromPath(path string) (v1.ImageIndex, error) { + lp, err := FromPath(path) + if err != nil { + return nil, err + } + return lp.ImageIndex() +} + +// ImageIndex returns a v1.ImageIndex for the Path. +func (l Path) ImageIndex() (v1.ImageIndex, error) { + rawIndex, err := ioutil.ReadFile(l.path("index.json")) + if err != nil { + return nil, err + } + + idx := &layoutIndex{ + mediaType: types.OCIImageIndex, + path: l, + rawIndex: rawIndex, + } + + return idx, nil +} + +func (i *layoutIndex) MediaType() (types.MediaType, error) { + return i.mediaType, nil +} + +func (i *layoutIndex) Digest() (v1.Hash, error) { + return partial.Digest(i) +} + +func (i *layoutIndex) Size() (int64, error) { + return partial.Size(i) +} + +func (i *layoutIndex) IndexManifest() (*v1.IndexManifest, error) { + var index v1.IndexManifest + err := json.Unmarshal(i.rawIndex, &index) + return &index, err +} + +func (i *layoutIndex) RawManifest() ([]byte, error) { + return i.rawIndex, nil +} + +func (i *layoutIndex) Image(h v1.Hash) (v1.Image, error) { + // Look up the digest in our manifest first to return a better error. + desc, err := i.findDescriptor(h) + if err != nil { + return nil, err + } + + if !isExpectedMediaType(desc.MediaType, types.OCIManifestSchema1, types.DockerManifestSchema2) { + return nil, fmt.Errorf("unexpected media type for %v: %s", h, desc.MediaType) + } + + img := &layoutImage{ + path: i.path, + desc: *desc, + } + return partial.CompressedToImage(img) +} + +func (i *layoutIndex) ImageIndex(h v1.Hash) (v1.ImageIndex, error) { + // Look up the digest in our manifest first to return a better error. + desc, err := i.findDescriptor(h) + if err != nil { + return nil, err + } + + if !isExpectedMediaType(desc.MediaType, types.OCIImageIndex, types.DockerManifestList) { + return nil, fmt.Errorf("unexpected media type for %v: %s", h, desc.MediaType) + } + + rawIndex, err := i.path.Bytes(h) + if err != nil { + return nil, err + } + + return &layoutIndex{ + mediaType: desc.MediaType, + path: i.path, + rawIndex: rawIndex, + }, nil +} + +func (i *layoutIndex) Blob(h v1.Hash) (io.ReadCloser, error) { + return i.path.Blob(h) +} + +func (i *layoutIndex) findDescriptor(h v1.Hash) (*v1.Descriptor, error) { + im, err := i.IndexManifest() + if err != nil { + return nil, err + } + + if h == (v1.Hash{}) { + if len(im.Manifests) != 1 { + return nil, errors.New("oci layout must contain only a single image to be used with layout.Image") + } + return &(im.Manifests)[0], nil + } + + for _, desc := range im.Manifests { + if desc.Digest == h { + return &desc, nil + } + } + + return nil, fmt.Errorf("could not find descriptor in index: %s", h) +} + +// TODO: Pull this out into methods on types.MediaType? e.g. instead, have: +// * mt.IsIndex() +// * mt.IsImage() +func isExpectedMediaType(mt types.MediaType, expected ...types.MediaType) bool { + for _, allowed := range expected { + if mt == allowed { + return true + } + } + return false +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/layout/layoutpath.go b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/layoutpath.go new file mode 100644 index 0000000000..a031ff5ae9 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/layoutpath.go @@ -0,0 +1,25 @@ +// Copyright 2019 The original author or authors +// +// 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. + +package layout + +import "path/filepath" + +// Path represents an OCI image layout rooted in a file system path +type Path string + +func (l Path) path(elem ...string) string { + complete := []string{string(l)} + return filepath.Join(append(complete, elem...)...) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/layout/options.go b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/options.go new file mode 100644 index 0000000000..c322d77572 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/options.go @@ -0,0 +1,57 @@ +package layout + +import v1 "github.com/google/go-containerregistry/pkg/v1" + +// Option is a functional option for Layout. +type Option func(*options) + +type options struct { + descOpts []descriptorOption +} + +func makeOptions(opts ...Option) *options { + o := &options{ + descOpts: []descriptorOption{}, + } + for _, apply := range opts { + apply(o) + } + return o +} + +type descriptorOption func(*v1.Descriptor) + +// WithAnnotations adds annotations to the artifact descriptor. +func WithAnnotations(annotations map[string]string) Option { + return func(o *options) { + o.descOpts = append(o.descOpts, func(desc *v1.Descriptor) { + if desc.Annotations == nil { + desc.Annotations = make(map[string]string) + } + for k, v := range annotations { + desc.Annotations[k] = v + } + }) + } +} + +// WithURLs adds urls to the artifact descriptor. +func WithURLs(urls []string) Option { + return func(o *options) { + o.descOpts = append(o.descOpts, func(desc *v1.Descriptor) { + if desc.URLs == nil { + desc.URLs = []string{} + } + desc.URLs = append(desc.URLs, urls...) + }) + } +} + +// WithPlatform sets the platform of the artifact descriptor. +func WithPlatform(platform v1.Platform) Option { + return func(o *options) { + o.descOpts = append(o.descOpts, func(desc *v1.Descriptor) { + desc.Platform = &platform + }) + } +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/layout/read.go b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/read.go new file mode 100644 index 0000000000..796abc7dd0 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/read.go @@ -0,0 +1,32 @@ +// Copyright 2019 The original author or authors +// +// 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. + +package layout + +import ( + "os" + "path/filepath" +) + +// FromPath reads an OCI image layout at path and constructs a layout.Path. +func FromPath(path string) (Path, error) { + // TODO: check oci-layout exists + + _, err := os.Stat(filepath.Join(path, "index.json")) + if err != nil { + return "", err + } + + return Path(path), nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/layout/write.go b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/write.go new file mode 100644 index 0000000000..7c54e5f58b --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/layout/write.go @@ -0,0 +1,511 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package layout + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + + "github.com/google/go-containerregistry/pkg/logs" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/match" + "github.com/google/go-containerregistry/pkg/v1/mutate" + "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/stream" + "github.com/google/go-containerregistry/pkg/v1/types" + "golang.org/x/sync/errgroup" +) + +var layoutFile = `{ + "imageLayoutVersion": "1.0.0" +}` + +// AppendImage writes a v1.Image to the Path and updates +// the index.json to reference it. +func (l Path) AppendImage(img v1.Image, options ...Option) error { + if err := l.WriteImage(img); err != nil { + return err + } + + mt, err := img.MediaType() + if err != nil { + return err + } + + d, err := img.Digest() + if err != nil { + return err + } + + manifest, err := img.RawManifest() + if err != nil { + return err + } + + desc := v1.Descriptor{ + MediaType: mt, + Size: int64(len(manifest)), + Digest: d, + } + + o := makeOptions(options...) + for _, opt := range o.descOpts { + opt(&desc) + } + + return l.AppendDescriptor(desc) +} + +// AppendIndex writes a v1.ImageIndex to the Path and updates +// the index.json to reference it. +func (l Path) AppendIndex(ii v1.ImageIndex, options ...Option) error { + if err := l.WriteIndex(ii); err != nil { + return err + } + + mt, err := ii.MediaType() + if err != nil { + return err + } + + d, err := ii.Digest() + if err != nil { + return err + } + + manifest, err := ii.RawManifest() + if err != nil { + return err + } + + desc := v1.Descriptor{ + MediaType: mt, + Size: int64(len(manifest)), + Digest: d, + } + + o := makeOptions(options...) + for _, opt := range o.descOpts { + opt(&desc) + } + + return l.AppendDescriptor(desc) +} + +// AppendDescriptor adds a descriptor to the index.json of the Path. +func (l Path) AppendDescriptor(desc v1.Descriptor) error { + ii, err := l.ImageIndex() + if err != nil { + return err + } + + index, err := ii.IndexManifest() + if err != nil { + return err + } + + index.Manifests = append(index.Manifests, desc) + + rawIndex, err := json.MarshalIndent(index, "", " ") + if err != nil { + return err + } + + return l.WriteFile("index.json", rawIndex, os.ModePerm) +} + +// ReplaceImage writes a v1.Image to the Path and updates +// the index.json to reference it, replacing any existing one that matches matcher, if found. +func (l Path) ReplaceImage(img v1.Image, matcher match.Matcher, options ...Option) error { + if err := l.WriteImage(img); err != nil { + return err + } + + return l.replaceDescriptor(img, matcher, options...) +} + +// ReplaceIndex writes a v1.ImageIndex to the Path and updates +// the index.json to reference it, replacing any existing one that matches matcher, if found. +func (l Path) ReplaceIndex(ii v1.ImageIndex, matcher match.Matcher, options ...Option) error { + if err := l.WriteIndex(ii); err != nil { + return err + } + + return l.replaceDescriptor(ii, matcher, options...) +} + +// replaceDescriptor adds a descriptor to the index.json of the Path, replacing +// any one matching matcher, if found. +func (l Path) replaceDescriptor(append mutate.Appendable, matcher match.Matcher, options ...Option) error { + ii, err := l.ImageIndex() + if err != nil { + return err + } + + desc, err := partial.Descriptor(append) + if err != nil { + return err + } + + o := makeOptions(options...) + for _, opt := range o.descOpts { + opt(desc) + } + + add := mutate.IndexAddendum{ + Add: append, + Descriptor: *desc, + } + ii = mutate.AppendManifests(mutate.RemoveManifests(ii, matcher), add) + + index, err := ii.IndexManifest() + if err != nil { + return err + } + + rawIndex, err := json.MarshalIndent(index, "", " ") + if err != nil { + return err + } + + return l.WriteFile("index.json", rawIndex, os.ModePerm) +} + +// RemoveDescriptors removes any descriptors that match the match.Matcher from the index.json of the Path. +func (l Path) RemoveDescriptors(matcher match.Matcher) error { + ii, err := l.ImageIndex() + if err != nil { + return err + } + ii = mutate.RemoveManifests(ii, matcher) + + index, err := ii.IndexManifest() + if err != nil { + return err + } + + rawIndex, err := json.MarshalIndent(index, "", " ") + if err != nil { + return err + } + + return l.WriteFile("index.json", rawIndex, os.ModePerm) +} + +// WriteFile write a file with arbitrary data at an arbitrary location in a v1 +// layout. Used mostly internally to write files like "oci-layout" and +// "index.json", also can be used to write other arbitrary files. Do *not* use +// this to write blobs. Use only WriteBlob() for that. +func (l Path) WriteFile(name string, data []byte, perm os.FileMode) error { + if err := os.MkdirAll(l.path(), os.ModePerm); err != nil && !os.IsExist(err) { + return err + } + + return ioutil.WriteFile(l.path(name), data, perm) +} + +// WriteBlob copies a file to the blobs/ directory in the Path from the given ReadCloser at +// blobs/{hash.Algorithm}/{hash.Hex}. +func (l Path) WriteBlob(hash v1.Hash, r io.ReadCloser) error { + return l.writeBlob(hash, -1, r, nil) +} + +func (l Path) writeBlob(hash v1.Hash, size int64, rc io.ReadCloser, renamer func() (v1.Hash, error)) error { + if hash.Hex == "" && renamer == nil { + panic("writeBlob called an invalid hash and no renamer") + } + + dir := l.path("blobs", hash.Algorithm) + if err := os.MkdirAll(dir, os.ModePerm); err != nil && !os.IsExist(err) { + return err + } + + // Check if blob already exists and is the correct size + file := filepath.Join(dir, hash.Hex) + if s, err := os.Stat(file); err == nil && !s.IsDir() && (s.Size() == size || size == -1) { + return nil + } + + // If a renamer func was provided write to a temporary file + open := func() (*os.File, error) { return os.Create(file) } + if renamer != nil { + open = func() (*os.File, error) { return ioutil.TempFile(dir, hash.Hex) } + } + w, err := open() + if err != nil { + return err + } + if renamer != nil { + // Delete temp file if an error is encountered before renaming + defer func() { + if err := os.Remove(w.Name()); err != nil && !errors.Is(err, os.ErrNotExist) { + logs.Warn.Printf("error removing temporary file after encountering an error while writing blob: %v", err) + } + }() + } + defer w.Close() + + // Write to file and exit if not renaming + if n, err := io.Copy(w, rc); err != nil || renamer == nil { + return err + } else if size != -1 && n != size { + return fmt.Errorf("expected blob size %d, but only wrote %d", size, n) + } + + // Always close reader before renaming, since Close computes the digest in + // the case of streaming layers. If Close is not called explicitly, it will + // occur in a goroutine that is not guaranteed to succeed before renamer is + // called. When renamer is the layer's Digest method, it can return + // ErrNotComputed. + if err := rc.Close(); err != nil { + return err + } + + // Always close file before renaming + if err := w.Close(); err != nil { + return err + } + + // Rename file based on the final hash + finalHash, err := renamer() + if err != nil { + return fmt.Errorf("error getting final digest of layer: %w", err) + } + + renamePath := l.path("blobs", finalHash.Algorithm, finalHash.Hex) + return os.Rename(w.Name(), renamePath) +} + +// writeLayer writes the compressed layer to a blob. Unlike WriteBlob it will +// write to a temporary file (suffixed with .tmp) within the layout until the +// compressed reader is fully consumed and written to disk. Also unlike +// WriteBlob, it will not skip writing and exit without error when a blob file +// exists, but does not have the correct size. (The blob hash is not +// considered, because it may be expensive to compute.) +func (l Path) writeLayer(layer v1.Layer) error { + d, err := layer.Digest() + if errors.Is(err, stream.ErrNotComputed) { + // Allow digest errors, since streams may not have calculated the hash + // yet. Instead, use an empty value, which will be transformed into a + // random file name with `ioutil.TempFile` and the final digest will be + // calculated after writing to a temp file and before renaming to the + // final path. + d = v1.Hash{Algorithm: "sha256", Hex: ""} + } else if err != nil { + return err + } + + s, err := layer.Size() + if errors.Is(err, stream.ErrNotComputed) { + // Allow size errors, since streams may not have calculated the size + // yet. Instead, use zero as a sentinel value meaning that no size + // comparison can be done and any sized blob file should be considered + // valid and not overwritten. + // + // TODO: Provide an option to always overwrite blobs. + s = -1 + } else if err != nil { + return err + } + + r, err := layer.Compressed() + if err != nil { + return err + } + + if err := l.writeBlob(d, s, r, layer.Digest); err != nil { + return fmt.Errorf("error writing layer: %w", err) + } + return nil +} + +// RemoveBlob removes a file from the blobs directory in the Path +// at blobs/{hash.Algorithm}/{hash.Hex} +// It does *not* remove any reference to it from other manifests or indexes, or +// from the root index.json. +func (l Path) RemoveBlob(hash v1.Hash) error { + dir := l.path("blobs", hash.Algorithm) + err := os.Remove(filepath.Join(dir, hash.Hex)) + if err != nil && !os.IsNotExist(err) { + return err + } + return nil +} + +// WriteImage writes an image, including its manifest, config and all of its +// layers, to the blobs directory. If any blob already exists, as determined by +// the hash filename, does not write it. +// This function does *not* update the `index.json` file. If you want to write the +// image and also update the `index.json`, call AppendImage(), which wraps this +// and also updates the `index.json`. +func (l Path) WriteImage(img v1.Image) error { + layers, err := img.Layers() + if err != nil { + return err + } + + // Write the layers concurrently. + var g errgroup.Group + for _, layer := range layers { + layer := layer + g.Go(func() error { + return l.writeLayer(layer) + }) + } + if err := g.Wait(); err != nil { + return err + } + + // Write the config. + cfgName, err := img.ConfigName() + if err != nil { + return err + } + cfgBlob, err := img.RawConfigFile() + if err != nil { + return err + } + if err := l.WriteBlob(cfgName, ioutil.NopCloser(bytes.NewReader(cfgBlob))); err != nil { + return err + } + + // Write the img manifest. + d, err := img.Digest() + if err != nil { + return err + } + manifest, err := img.RawManifest() + if err != nil { + return err + } + + return l.WriteBlob(d, ioutil.NopCloser(bytes.NewReader(manifest))) +} + +type withLayer interface { + Layer(v1.Hash) (v1.Layer, error) +} + +type withBlob interface { + Blob(v1.Hash) (io.ReadCloser, error) +} + +func (l Path) writeIndexToFile(indexFile string, ii v1.ImageIndex) error { + index, err := ii.IndexManifest() + if err != nil { + return err + } + + // Walk the descriptors and write any v1.Image or v1.ImageIndex that we find. + // If we come across something we don't expect, just write it as a blob. + for _, desc := range index.Manifests { + switch desc.MediaType { + case types.OCIImageIndex, types.DockerManifestList: + ii, err := ii.ImageIndex(desc.Digest) + if err != nil { + return err + } + if err := l.WriteIndex(ii); err != nil { + return err + } + case types.OCIManifestSchema1, types.DockerManifestSchema2: + img, err := ii.Image(desc.Digest) + if err != nil { + return err + } + if err := l.WriteImage(img); err != nil { + return err + } + default: + // TODO: The layout could reference arbitrary things, which we should + // probably just pass through. + + var blob io.ReadCloser + // Workaround for #819. + if wl, ok := ii.(withLayer); ok { + layer, lerr := wl.Layer(desc.Digest) + if lerr != nil { + return lerr + } + blob, err = layer.Compressed() + } else if wb, ok := ii.(withBlob); ok { + blob, err = wb.Blob(desc.Digest) + } + if err != nil { + return err + } + if err := l.WriteBlob(desc.Digest, blob); err != nil { + return err + } + } + } + + rawIndex, err := ii.RawManifest() + if err != nil { + return err + } + + return l.WriteFile(indexFile, rawIndex, os.ModePerm) +} + +// WriteIndex writes an index to the blobs directory. Walks down the children, +// including its children manifests and/or indexes, and down the tree until all of +// config and all layers, have been written. If any blob already exists, as determined by +// the hash filename, does not write it. +// This function does *not* update the `index.json` file. If you want to write the +// index and also update the `index.json`, call AppendIndex(), which wraps this +// and also updates the `index.json`. +func (l Path) WriteIndex(ii v1.ImageIndex) error { + // Always just write oci-layout file, since it's small. + if err := l.WriteFile("oci-layout", []byte(layoutFile), os.ModePerm); err != nil { + return err + } + + h, err := ii.Digest() + if err != nil { + return err + } + + indexFile := filepath.Join("blobs", h.Algorithm, h.Hex) + return l.writeIndexToFile(indexFile, ii) +} + +// Write constructs a Path at path from an ImageIndex. +// +// The contents are written in the following format: +// At the top level, there is: +// One oci-layout file containing the version of this image-layout. +// One index.json file listing descriptors for the contained images. +// Under blobs/, there is, for each image: +// One file for each layer, named after the layer's SHA. +// One file for each config blob, named after its SHA. +// One file for each manifest blob, named after its SHA. +func Write(path string, ii v1.ImageIndex) (Path, error) { + lp := Path(path) + // Always just write oci-layout file, since it's small. + if err := lp.WriteFile("oci-layout", []byte(layoutFile), os.ModePerm); err != nil { + return "", err + } + + // TODO create blobs/ in case there is a blobs file which would prevent the directory from being created + + return lp, lp.writeIndexToFile("index.json", ii) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/manifest.go b/vendor/github.com/google/go-containerregistry/pkg/v1/manifest.go new file mode 100644 index 0000000000..8ded1ee53a --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/manifest.go @@ -0,0 +1,68 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package v1 + +import ( + "encoding/json" + "io" + + "github.com/google/go-containerregistry/pkg/v1/types" +) + +// Manifest represents the OCI image manifest in a structured way. +type Manifest struct { + SchemaVersion int64 `json:"schemaVersion"` + MediaType types.MediaType `json:"mediaType,omitempty"` + Config Descriptor `json:"config"` + Layers []Descriptor `json:"layers"` + Annotations map[string]string `json:"annotations,omitempty"` +} + +// IndexManifest represents an OCI image index in a structured way. +type IndexManifest struct { + SchemaVersion int64 `json:"schemaVersion"` + MediaType types.MediaType `json:"mediaType,omitempty"` + Manifests []Descriptor `json:"manifests"` + Annotations map[string]string `json:"annotations,omitempty"` +} + +// Descriptor holds a reference from the manifest to one of its constituent elements. +type Descriptor struct { + MediaType types.MediaType `json:"mediaType"` + Size int64 `json:"size"` + Digest Hash `json:"digest"` + Data []byte `json:"data,omitempty"` + URLs []string `json:"urls,omitempty"` + Annotations map[string]string `json:"annotations,omitempty"` + Platform *Platform `json:"platform,omitempty"` +} + +// ParseManifest parses the io.Reader's contents into a Manifest. +func ParseManifest(r io.Reader) (*Manifest, error) { + m := Manifest{} + if err := json.NewDecoder(r).Decode(&m); err != nil { + return nil, err + } + return &m, nil +} + +// ParseIndexManifest parses the io.Reader's contents into an IndexManifest. +func ParseIndexManifest(r io.Reader) (*IndexManifest, error) { + im := IndexManifest{} + if err := json.NewDecoder(r).Decode(&im); err != nil { + return nil, err + } + return &im, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/match/match.go b/vendor/github.com/google/go-containerregistry/pkg/v1/match/match.go new file mode 100644 index 0000000000..0f886667ad --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/match/match.go @@ -0,0 +1,90 @@ +// Copyright 2020 Google LLC All Rights Reserved. +// +// 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. + +// Package match provides functionality for conveniently matching a v1.Descriptor. +package match + +import ( + v1 "github.com/google/go-containerregistry/pkg/v1" + imagespec "github.com/opencontainers/image-spec/specs-go/v1" +) + +// Matcher function that is given a v1.Descriptor, and returns whether or +// not it matches a given rule. Can match on anything it wants in the Descriptor. +type Matcher func(desc v1.Descriptor) bool + +// Name returns a match.Matcher that matches based on the value of the +// "org.opencontainers.image.ref.name" annotation: +// github.com/opencontainers/image-spec/blob/v1.0.1/annotations.md#pre-defined-annotation-keys +func Name(name string) Matcher { + return Annotation(imagespec.AnnotationRefName, name) +} + +// Annotation returns a match.Matcher that matches based on the provided annotation. +func Annotation(key, value string) Matcher { + return func(desc v1.Descriptor) bool { + if desc.Annotations == nil { + return false + } + if aValue, ok := desc.Annotations[key]; ok && aValue == value { + return true + } + return false + } +} + +// Platforms returns a match.Matcher that matches on any one of the provided platforms. +// Ignores any descriptors that do not have a platform. +func Platforms(platforms ...v1.Platform) Matcher { + return func(desc v1.Descriptor) bool { + if desc.Platform == nil { + return false + } + for _, platform := range platforms { + if desc.Platform.Equals(platform) { + return true + } + } + return false + } +} + +// MediaTypes returns a match.Matcher that matches at least one of the provided media types. +func MediaTypes(mediaTypes ...string) Matcher { + mts := map[string]bool{} + for _, media := range mediaTypes { + mts[media] = true + } + return func(desc v1.Descriptor) bool { + if desc.MediaType == "" { + return false + } + if _, ok := mts[string(desc.MediaType)]; ok { + return true + } + return false + } +} + +// Digests returns a match.Matcher that matches at least one of the provided Digests +func Digests(digests ...v1.Hash) Matcher { + digs := map[v1.Hash]bool{} + for _, digest := range digests { + digs[digest] = true + } + return func(desc v1.Descriptor) bool { + _, ok := digs[desc.Digest] + return ok + } +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/README.md b/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/README.md new file mode 100644 index 0000000000..19e1612433 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/README.md @@ -0,0 +1,56 @@ +# `mutate` + +[![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/mutate?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/mutate) + +The `v1.Image`, `v1.ImageIndex`, and `v1.Layer` interfaces provide only +accessor methods, so they are essentially immutable. If you want to change +something about them, you need to produce a new instance of that interface. + +A common use case for this library is to read an image from somewhere (a source), +change something about it, and write the image somewhere else (a sink). + +Graphically, this looks something like: + +

+ +

+ +## Mutations + +This is obviously not a comprehensive set of useful transformations (PRs welcome!), +but a rough summary of what the `mutate` package currently does: + +### `Config` and `ConfigFile` + +These allow you to change the [image configuration](https://github.com/opencontainers/image-spec/blob/master/config.md#properties), +e.g. to change the entrypoint, environment, author, etc. + +### `Time`, `Canonical`, and `CreatedAt` + +These are useful in the context of [reproducible builds](https://reproducible-builds.org/), +where you may want to strip timestamps and other non-reproducible information. + +### `Append`, `AppendLayers`, and `AppendManifests` + +These functions allow the extension of a `v1.Image` or `v1.ImageIndex` with +new layers or manifests. + +For constructing an image `FROM scratch`, see the [`empty`](/pkg/v1/empty) package. + +### `MediaType` and `IndexMediaType` + +Sometimes, it is necessary to change the media type of an image or index, +e.g. to appease a registry with strict validation of images (_looking at you, GCR_). + +### `Rebase` + +Rebase has [its own README](/cmd/crane/rebase.md). + +This is the underlying implementation of [`crane rebase`](https://github.com/google/go-containerregistry/blob/main/cmd/crane/doc/crane_rebase.md). + +### `Extract` + +Extract will flatten an image filesystem into a single tar stream, +respecting whiteout files. + +This is the underlying implementation of [`crane export`](https://github.com/google/go-containerregistry/blob/main/cmd/crane/doc/crane_export.md). diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/doc.go b/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/doc.go new file mode 100644 index 0000000000..dfbd9951e0 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/doc.go @@ -0,0 +1,16 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +// Package mutate provides facilities for mutating v1.Images of any kind. +package mutate diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/image.go b/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/image.go new file mode 100644 index 0000000000..93c230e3b2 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/image.go @@ -0,0 +1,285 @@ +// Copyright 2019 Google LLC All Rights Reserved. +// +// 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. + +package mutate + +import ( + "bytes" + "encoding/json" + "errors" + + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/stream" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +type image struct { + base v1.Image + adds []Addendum + + computed bool + configFile *v1.ConfigFile + manifest *v1.Manifest + annotations map[string]string + mediaType *types.MediaType + configMediaType *types.MediaType + diffIDMap map[v1.Hash]v1.Layer + digestMap map[v1.Hash]v1.Layer +} + +var _ v1.Image = (*image)(nil) + +func (i *image) MediaType() (types.MediaType, error) { + if i.mediaType != nil { + return *i.mediaType, nil + } + return i.base.MediaType() +} + +func (i *image) compute() error { + // Don't re-compute if already computed. + if i.computed { + return nil + } + var configFile *v1.ConfigFile + if i.configFile != nil { + configFile = i.configFile + } else { + cf, err := i.base.ConfigFile() + if err != nil { + return err + } + configFile = cf.DeepCopy() + } + diffIDs := configFile.RootFS.DiffIDs + history := configFile.History + + diffIDMap := make(map[v1.Hash]v1.Layer) + digestMap := make(map[v1.Hash]v1.Layer) + + for _, add := range i.adds { + history = append(history, add.History) + if add.Layer != nil { + diffID, err := add.Layer.DiffID() + if err != nil { + return err + } + diffIDs = append(diffIDs, diffID) + diffIDMap[diffID] = add.Layer + } + } + + m, err := i.base.Manifest() + if err != nil { + return err + } + manifest := m.DeepCopy() + manifestLayers := manifest.Layers + for _, add := range i.adds { + if add.Layer == nil { + // Empty layers include only history in manifest. + continue + } + + desc, err := partial.Descriptor(add.Layer) + if err != nil { + return err + } + + // Fields in the addendum override the original descriptor. + if len(add.Annotations) != 0 { + desc.Annotations = add.Annotations + } + if len(add.URLs) != 0 { + desc.URLs = add.URLs + } + + if add.MediaType != "" { + desc.MediaType = add.MediaType + } + + manifestLayers = append(manifestLayers, *desc) + digestMap[desc.Digest] = add.Layer + } + + configFile.RootFS.DiffIDs = diffIDs + configFile.History = history + + manifest.Layers = manifestLayers + + rcfg, err := json.Marshal(configFile) + if err != nil { + return err + } + d, sz, err := v1.SHA256(bytes.NewBuffer(rcfg)) + if err != nil { + return err + } + manifest.Config.Digest = d + manifest.Config.Size = sz + + // If Data was set in the base image, we need to update it in the mutated image. + if m.Config.Data != nil { + manifest.Config.Data = rcfg + } + + // If the user wants to mutate the media type of the config + if i.configMediaType != nil { + manifest.Config.MediaType = *i.configMediaType + } + + if i.mediaType != nil { + manifest.MediaType = *i.mediaType + } + + if i.annotations != nil { + if manifest.Annotations == nil { + manifest.Annotations = map[string]string{} + } + + for k, v := range i.annotations { + manifest.Annotations[k] = v + } + } + + i.configFile = configFile + i.manifest = manifest + i.diffIDMap = diffIDMap + i.digestMap = digestMap + i.computed = true + return nil +} + +// Layers returns the ordered collection of filesystem layers that comprise this image. +// The order of the list is oldest/base layer first, and most-recent/top layer last. +func (i *image) Layers() ([]v1.Layer, error) { + if err := i.compute(); errors.Is(err, stream.ErrNotComputed) { + // Image contains a streamable layer which has not yet been + // consumed. Just return the layers we have in case the caller + // is going to consume the layers. + layers, err := i.base.Layers() + if err != nil { + return nil, err + } + for _, add := range i.adds { + layers = append(layers, add.Layer) + } + return layers, nil + } else if err != nil { + return nil, err + } + + diffIDs, err := partial.DiffIDs(i) + if err != nil { + return nil, err + } + ls := make([]v1.Layer, 0, len(diffIDs)) + for _, h := range diffIDs { + l, err := i.LayerByDiffID(h) + if err != nil { + return nil, err + } + ls = append(ls, l) + } + return ls, nil +} + +// ConfigName returns the hash of the image's config file. +func (i *image) ConfigName() (v1.Hash, error) { + if err := i.compute(); err != nil { + return v1.Hash{}, err + } + return partial.ConfigName(i) +} + +// ConfigFile returns this image's config file. +func (i *image) ConfigFile() (*v1.ConfigFile, error) { + if err := i.compute(); err != nil { + return nil, err + } + return i.configFile.DeepCopy(), nil +} + +// RawConfigFile returns the serialized bytes of ConfigFile() +func (i *image) RawConfigFile() ([]byte, error) { + if err := i.compute(); err != nil { + return nil, err + } + return json.Marshal(i.configFile) +} + +// Digest returns the sha256 of this image's manifest. +func (i *image) Digest() (v1.Hash, error) { + if err := i.compute(); err != nil { + return v1.Hash{}, err + } + return partial.Digest(i) +} + +// Size implements v1.Image. +func (i *image) Size() (int64, error) { + if err := i.compute(); err != nil { + return -1, err + } + return partial.Size(i) +} + +// Manifest returns this image's Manifest object. +func (i *image) Manifest() (*v1.Manifest, error) { + if err := i.compute(); err != nil { + return nil, err + } + return i.manifest.DeepCopy(), nil +} + +// RawManifest returns the serialized bytes of Manifest() +func (i *image) RawManifest() ([]byte, error) { + if err := i.compute(); err != nil { + return nil, err + } + return json.Marshal(i.manifest) +} + +// LayerByDigest returns a Layer for interacting with a particular layer of +// the image, looking it up by "digest" (the compressed hash). +func (i *image) LayerByDigest(h v1.Hash) (v1.Layer, error) { + if cn, err := i.ConfigName(); err != nil { + return nil, err + } else if h == cn { + return partial.ConfigLayer(i) + } + if layer, ok := i.digestMap[h]; ok { + return layer, nil + } + return i.base.LayerByDigest(h) +} + +// LayerByDiffID is an analog to LayerByDigest, looking up by "diff id" +// (the uncompressed hash). +func (i *image) LayerByDiffID(h v1.Hash) (v1.Layer, error) { + if layer, ok := i.diffIDMap[h]; ok { + return layer, nil + } + return i.base.LayerByDiffID(h) +} + +func validate(adds []Addendum) error { + for _, add := range adds { + if add.Layer == nil && !add.History.EmptyLayer { + return errors.New("unable to add a nil layer to the image") + } + } + return nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/index.go b/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/index.go new file mode 100644 index 0000000000..841995f158 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/index.go @@ -0,0 +1,202 @@ +// Copyright 2019 Google LLC All Rights Reserved. +// +// 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. + +package mutate + +import ( + "encoding/json" + "fmt" + + "github.com/google/go-containerregistry/pkg/logs" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/match" + "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +func computeDescriptor(ia IndexAddendum) (*v1.Descriptor, error) { + desc, err := partial.Descriptor(ia.Add) + if err != nil { + return nil, err + } + + // The IndexAddendum allows overriding Descriptor values. + if ia.Descriptor.Size != 0 { + desc.Size = ia.Descriptor.Size + } + if string(ia.Descriptor.MediaType) != "" { + desc.MediaType = ia.Descriptor.MediaType + } + if ia.Descriptor.Digest != (v1.Hash{}) { + desc.Digest = ia.Descriptor.Digest + } + if ia.Descriptor.Platform != nil { + desc.Platform = ia.Descriptor.Platform + } + if len(ia.Descriptor.URLs) != 0 { + desc.URLs = ia.Descriptor.URLs + } + if len(ia.Descriptor.Annotations) != 0 { + desc.Annotations = ia.Descriptor.Annotations + } + if ia.Descriptor.Data != nil { + desc.Data = ia.Descriptor.Data + } + + return desc, nil +} + +type index struct { + base v1.ImageIndex + adds []IndexAddendum + // remove is removed before adds + remove match.Matcher + + computed bool + manifest *v1.IndexManifest + annotations map[string]string + mediaType *types.MediaType + imageMap map[v1.Hash]v1.Image + indexMap map[v1.Hash]v1.ImageIndex + layerMap map[v1.Hash]v1.Layer +} + +var _ v1.ImageIndex = (*index)(nil) + +func (i *index) MediaType() (types.MediaType, error) { + if i.mediaType != nil { + return *i.mediaType, nil + } + return i.base.MediaType() +} + +func (i *index) Size() (int64, error) { return partial.Size(i) } + +func (i *index) compute() error { + // Don't re-compute if already computed. + if i.computed { + return nil + } + + i.imageMap = make(map[v1.Hash]v1.Image) + i.indexMap = make(map[v1.Hash]v1.ImageIndex) + i.layerMap = make(map[v1.Hash]v1.Layer) + + m, err := i.base.IndexManifest() + if err != nil { + return err + } + manifest := m.DeepCopy() + manifests := manifest.Manifests + + if i.remove != nil { + var cleanedManifests []v1.Descriptor + for _, m := range manifests { + if !i.remove(m) { + cleanedManifests = append(cleanedManifests, m) + } + } + manifests = cleanedManifests + } + + for _, add := range i.adds { + desc, err := computeDescriptor(add) + if err != nil { + return err + } + + manifests = append(manifests, *desc) + if idx, ok := add.Add.(v1.ImageIndex); ok { + i.indexMap[desc.Digest] = idx + } else if img, ok := add.Add.(v1.Image); ok { + i.imageMap[desc.Digest] = img + } else if l, ok := add.Add.(v1.Layer); ok { + i.layerMap[desc.Digest] = l + } else { + logs.Warn.Printf("Unexpected index addendum: %T", add.Add) + } + } + + manifest.Manifests = manifests + + if i.mediaType != nil { + manifest.MediaType = *i.mediaType + } + + if i.annotations != nil { + if manifest.Annotations == nil { + manifest.Annotations = map[string]string{} + } + for k, v := range i.annotations { + manifest.Annotations[k] = v + } + } + + i.manifest = manifest + i.computed = true + return nil +} + +func (i *index) Image(h v1.Hash) (v1.Image, error) { + if img, ok := i.imageMap[h]; ok { + return img, nil + } + return i.base.Image(h) +} + +func (i *index) ImageIndex(h v1.Hash) (v1.ImageIndex, error) { + if idx, ok := i.indexMap[h]; ok { + return idx, nil + } + return i.base.ImageIndex(h) +} + +type withLayer interface { + Layer(v1.Hash) (v1.Layer, error) +} + +// Workaround for #819. +func (i *index) Layer(h v1.Hash) (v1.Layer, error) { + if layer, ok := i.layerMap[h]; ok { + return layer, nil + } + if wl, ok := i.base.(withLayer); ok { + return wl.Layer(h) + } + return nil, fmt.Errorf("layer not found: %s", h) +} + +// Digest returns the sha256 of this image's manifest. +func (i *index) Digest() (v1.Hash, error) { + if err := i.compute(); err != nil { + return v1.Hash{}, err + } + return partial.Digest(i) +} + +// Manifest returns this image's Manifest object. +func (i *index) IndexManifest() (*v1.IndexManifest, error) { + if err := i.compute(); err != nil { + return nil, err + } + return i.manifest.DeepCopy(), nil +} + +// RawManifest returns the serialized bytes of Manifest() +func (i *index) RawManifest() ([]byte, error) { + if err := i.compute(); err != nil { + return nil, err + } + return json.Marshal(i.manifest) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/mutate.go b/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/mutate.go new file mode 100644 index 0000000000..7a1f59fed2 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/mutate.go @@ -0,0 +1,483 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package mutate + +import ( + "archive/tar" + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "path/filepath" + "strings" + "time" + + "github.com/google/go-containerregistry/internal/gzip" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/empty" + "github.com/google/go-containerregistry/pkg/v1/match" + "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/tarball" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +const whiteoutPrefix = ".wh." + +// Addendum contains layers and history to be appended +// to a base image +type Addendum struct { + Layer v1.Layer + History v1.History + URLs []string + Annotations map[string]string + MediaType types.MediaType +} + +// AppendLayers applies layers to a base image. +func AppendLayers(base v1.Image, layers ...v1.Layer) (v1.Image, error) { + additions := make([]Addendum, 0, len(layers)) + for _, layer := range layers { + additions = append(additions, Addendum{Layer: layer}) + } + + return Append(base, additions...) +} + +// Append will apply the list of addendums to the base image +func Append(base v1.Image, adds ...Addendum) (v1.Image, error) { + if len(adds) == 0 { + return base, nil + } + if err := validate(adds); err != nil { + return nil, err + } + + return &image{ + base: base, + adds: adds, + }, nil +} + +// Appendable is an interface that represents something that can be appended +// to an ImageIndex. We need to be able to construct a v1.Descriptor in order +// to append something, and this is the minimum required information for that. +type Appendable interface { + MediaType() (types.MediaType, error) + Digest() (v1.Hash, error) + Size() (int64, error) +} + +// IndexAddendum represents an appendable thing and all the properties that +// we may want to override in the resulting v1.Descriptor. +type IndexAddendum struct { + Add Appendable + v1.Descriptor +} + +// AppendManifests appends a manifest to the ImageIndex. +func AppendManifests(base v1.ImageIndex, adds ...IndexAddendum) v1.ImageIndex { + return &index{ + base: base, + adds: adds, + } +} + +// RemoveManifests removes any descriptors that match the match.Matcher. +func RemoveManifests(base v1.ImageIndex, matcher match.Matcher) v1.ImageIndex { + return &index{ + base: base, + remove: matcher, + } +} + +// Config mutates the provided v1.Image to have the provided v1.Config +func Config(base v1.Image, cfg v1.Config) (v1.Image, error) { + cf, err := base.ConfigFile() + if err != nil { + return nil, err + } + + cf.Config = cfg + + return ConfigFile(base, cf) +} + +// Annotatable represents a manifest that can carry annotations. +type Annotatable interface { + partial.WithRawManifest +} + +// Annotations mutates the annotations on an annotatable image or index manifest. +// +// The annotatable input is expected to be a v1.Image or v1.ImageIndex, and +// returns the same type. You can type-assert the result like so: +// +// img := Annotations(empty.Image, map[string]string{ +// "foo": "bar", +// }).(v1.Image) +// +// Or for an index: +// +// idx := Annotations(empty.Index, map[string]string{ +// "foo": "bar", +// }).(v1.ImageIndex) +// +// If the input Annotatable is not an Image or ImageIndex, the result will +// attempt to lazily annotate the raw manifest. +func Annotations(f Annotatable, anns map[string]string) Annotatable { + if img, ok := f.(v1.Image); ok { + return &image{ + base: img, + annotations: anns, + } + } + if idx, ok := f.(v1.ImageIndex); ok { + return &index{ + base: idx, + annotations: anns, + } + } + return arbitraryRawManifest{f, anns} +} + +type arbitraryRawManifest struct { + a Annotatable + anns map[string]string +} + +func (a arbitraryRawManifest) RawManifest() ([]byte, error) { + b, err := a.a.RawManifest() + if err != nil { + return nil, err + } + var m map[string]interface{} + if err := json.Unmarshal(b, &m); err != nil { + return nil, err + } + if ann, ok := m["annotations"]; ok { + if annm, ok := ann.(map[string]string); ok { + for k, v := range a.anns { + annm[k] = v + } + } else { + return nil, fmt.Errorf(".annotations is not a map: %T", ann) + } + } else { + m["annotations"] = a.anns + } + return json.Marshal(m) +} + +// ConfigFile mutates the provided v1.Image to have the provided v1.ConfigFile +func ConfigFile(base v1.Image, cfg *v1.ConfigFile) (v1.Image, error) { + m, err := base.Manifest() + if err != nil { + return nil, err + } + + image := &image{ + base: base, + manifest: m.DeepCopy(), + configFile: cfg, + } + + return image, nil +} + +// CreatedAt mutates the provided v1.Image to have the provided v1.Time +func CreatedAt(base v1.Image, created v1.Time) (v1.Image, error) { + cf, err := base.ConfigFile() + if err != nil { + return nil, err + } + + cfg := cf.DeepCopy() + cfg.Created = created + + return ConfigFile(base, cfg) +} + +// Extract takes an image and returns an io.ReadCloser containing the image's +// flattened filesystem. +// +// Callers can read the filesystem contents by passing the reader to +// tar.NewReader, or io.Copy it directly to some output. +// +// If a caller doesn't read the full contents, they should Close it to free up +// resources used during extraction. +func Extract(img v1.Image) io.ReadCloser { + pr, pw := io.Pipe() + + go func() { + // Close the writer with any errors encountered during + // extraction. These errors will be returned by the reader end + // on subsequent reads. If err == nil, the reader will return + // EOF. + pw.CloseWithError(extract(img, pw)) + }() + + return pr +} + +// Adapted from https://github.com/google/containerregistry/blob/da03b395ccdc4e149e34fbb540483efce962dc64/client/v2_2/docker_image_.py#L816 +func extract(img v1.Image, w io.Writer) error { + tarWriter := tar.NewWriter(w) + defer tarWriter.Close() + + fileMap := map[string]bool{} + + layers, err := img.Layers() + if err != nil { + return fmt.Errorf("retrieving image layers: %w", err) + } + // we iterate through the layers in reverse order because it makes handling + // whiteout layers more efficient, since we can just keep track of the removed + // files as we see .wh. layers and ignore those in previous layers. + for i := len(layers) - 1; i >= 0; i-- { + layer := layers[i] + layerReader, err := layer.Uncompressed() + if err != nil { + return fmt.Errorf("reading layer contents: %w", err) + } + defer layerReader.Close() + tarReader := tar.NewReader(layerReader) + for { + header, err := tarReader.Next() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return fmt.Errorf("reading tar: %w", err) + } + + // Some tools prepend everything with "./", so if we don't Clean the + // name, we may have duplicate entries, which angers tar-split. + header.Name = filepath.Clean(header.Name) + + basename := filepath.Base(header.Name) + dirname := filepath.Dir(header.Name) + tombstone := strings.HasPrefix(basename, whiteoutPrefix) + if tombstone { + basename = basename[len(whiteoutPrefix):] + } + + // check if we have seen value before + // if we're checking a directory, don't filepath.Join names + var name string + if header.Typeflag == tar.TypeDir { + name = header.Name + } else { + name = filepath.Join(dirname, basename) + } + + if _, ok := fileMap[name]; ok { + continue + } + + // check for a whited out parent directory + if inWhiteoutDir(fileMap, name) { + continue + } + + // mark file as handled. non-directory implicitly tombstones + // any entries with a matching (or child) name + fileMap[name] = tombstone || !(header.Typeflag == tar.TypeDir) + if !tombstone { + tarWriter.WriteHeader(header) + if header.Size > 0 { + if _, err := io.CopyN(tarWriter, tarReader, header.Size); err != nil { + return err + } + } + } + } + } + return nil +} + +func inWhiteoutDir(fileMap map[string]bool, file string) bool { + for { + if file == "" { + break + } + dirname := filepath.Dir(file) + if file == dirname { + break + } + if val, ok := fileMap[dirname]; ok && val { + return true + } + file = dirname + } + return false +} + +// Time sets all timestamps in an image to the given timestamp. +func Time(img v1.Image, t time.Time) (v1.Image, error) { + newImage := empty.Image + + layers, err := img.Layers() + if err != nil { + return nil, fmt.Errorf("getting image layers: %w", err) + } + + // Strip away all timestamps from layers + newLayers := make([]v1.Layer, len(layers)) + for idx, layer := range layers { + newLayer, err := layerTime(layer, t) + if err != nil { + return nil, fmt.Errorf("setting layer times: %w", err) + } + newLayers[idx] = newLayer + } + + newImage, err = AppendLayers(newImage, newLayers...) + if err != nil { + return nil, fmt.Errorf("appending layers: %w", err) + } + + ocf, err := img.ConfigFile() + if err != nil { + return nil, fmt.Errorf("getting original config file: %w", err) + } + + cf, err := newImage.ConfigFile() + if err != nil { + return nil, fmt.Errorf("setting config file: %w", err) + } + + cfg := cf.DeepCopy() + + // Copy basic config over + cfg.Architecture = ocf.Architecture + cfg.OS = ocf.OS + cfg.OSVersion = ocf.OSVersion + cfg.Config = ocf.Config + + // Strip away timestamps from the config file + cfg.Created = v1.Time{Time: t} + + for i, h := range cfg.History { + h.Created = v1.Time{Time: t} + h.CreatedBy = ocf.History[i].CreatedBy + h.Comment = ocf.History[i].Comment + h.EmptyLayer = ocf.History[i].EmptyLayer + // Explicitly ignore Author field; which hinders reproducibility + cfg.History[i] = h + } + + return ConfigFile(newImage, cfg) +} + +func layerTime(layer v1.Layer, t time.Time) (v1.Layer, error) { + layerReader, err := layer.Uncompressed() + if err != nil { + return nil, fmt.Errorf("getting layer: %w", err) + } + defer layerReader.Close() + w := new(bytes.Buffer) + tarWriter := tar.NewWriter(w) + defer tarWriter.Close() + + tarReader := tar.NewReader(layerReader) + for { + header, err := tarReader.Next() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return nil, fmt.Errorf("reading layer: %w", err) + } + + header.ModTime = t + if err := tarWriter.WriteHeader(header); err != nil { + return nil, fmt.Errorf("writing tar header: %w", err) + } + + if header.Typeflag == tar.TypeReg { + // TODO(#1168): This should be lazy, and not buffer the entire layer contents. + if _, err = io.CopyN(tarWriter, tarReader, header.Size); err != nil { + return nil, fmt.Errorf("writing layer file: %w", err) + } + } + } + + if err := tarWriter.Close(); err != nil { + return nil, err + } + + b := w.Bytes() + // gzip the contents, then create the layer + opener := func() (io.ReadCloser, error) { + return gzip.ReadCloser(ioutil.NopCloser(bytes.NewReader(b))), nil + } + layer, err = tarball.LayerFromOpener(opener) + if err != nil { + return nil, fmt.Errorf("creating layer: %w", err) + } + + return layer, nil +} + +// Canonical is a helper function to combine Time and configFile +// to remove any randomness during a docker build. +func Canonical(img v1.Image) (v1.Image, error) { + // Set all timestamps to 0 + created := time.Time{} + img, err := Time(img, created) + if err != nil { + return nil, err + } + + cf, err := img.ConfigFile() + if err != nil { + return nil, err + } + + // Get rid of host-dependent random config + cfg := cf.DeepCopy() + + cfg.Container = "" + cfg.Config.Hostname = "" + cfg.DockerVersion = "" + + return ConfigFile(img, cfg) +} + +// MediaType modifies the MediaType() of the given image. +func MediaType(img v1.Image, mt types.MediaType) v1.Image { + return &image{ + base: img, + mediaType: &mt, + } +} + +// ConfigMediaType modifies the MediaType() of the given image's Config. +func ConfigMediaType(img v1.Image, mt types.MediaType) v1.Image { + return &image{ + base: img, + configMediaType: &mt, + } +} + +// IndexMediaType modifies the MediaType() of the given index. +func IndexMediaType(idx v1.ImageIndex, mt types.MediaType) v1.ImageIndex { + return &index{ + base: idx, + mediaType: &mt, + } +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/rebase.go b/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/rebase.go new file mode 100644 index 0000000000..c606e0b76e --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/mutate/rebase.go @@ -0,0 +1,144 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package mutate + +import ( + "fmt" + + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/empty" +) + +// Rebase returns a new v1.Image where the oldBase in orig is replaced by newBase. +func Rebase(orig, oldBase, newBase v1.Image) (v1.Image, error) { + // Verify that oldBase's layers are present in orig, otherwise orig is + // not based on oldBase at all. + origLayers, err := orig.Layers() + if err != nil { + return nil, fmt.Errorf("failed to get layers for original: %w", err) + } + oldBaseLayers, err := oldBase.Layers() + if err != nil { + return nil, err + } + if len(oldBaseLayers) > len(origLayers) { + return nil, fmt.Errorf("image %q is not based on %q (too few layers)", orig, oldBase) + } + for i, l := range oldBaseLayers { + oldLayerDigest, err := l.Digest() + if err != nil { + return nil, fmt.Errorf("failed to get digest of layer %d of %q: %w", i, oldBase, err) + } + origLayerDigest, err := origLayers[i].Digest() + if err != nil { + return nil, fmt.Errorf("failed to get digest of layer %d of %q: %w", i, orig, err) + } + if oldLayerDigest != origLayerDigest { + return nil, fmt.Errorf("image %q is not based on %q (layer %d mismatch)", orig, oldBase, i) + } + } + + oldConfig, err := oldBase.ConfigFile() + if err != nil { + return nil, fmt.Errorf("failed to get config for old base: %w", err) + } + + origConfig, err := orig.ConfigFile() + if err != nil { + return nil, fmt.Errorf("failed to get config for original: %w", err) + } + + newConfig, err := newBase.ConfigFile() + if err != nil { + return nil, fmt.Errorf("could not get config for new base: %w", err) + } + + // Stitch together an image that contains: + // - original image's config + // - new base image's os/arch properties + // - new base image's layers + top of original image's layers + // - new base image's history + top of original image's history + rebasedImage, err := Config(empty.Image, *origConfig.Config.DeepCopy()) + if err != nil { + return nil, fmt.Errorf("failed to create empty image with original config: %w", err) + } + + // Add new config properties from existing images. + rebasedConfig, err := rebasedImage.ConfigFile() + if err != nil { + return nil, fmt.Errorf("could not get config for rebased image: %w", err) + } + // OS/Arch properties from new base + rebasedConfig.Architecture = newConfig.Architecture + rebasedConfig.OS = newConfig.OS + rebasedConfig.OSVersion = newConfig.OSVersion + + // Apply config properties to rebased. + rebasedImage, err = ConfigFile(rebasedImage, rebasedConfig) + if err != nil { + return nil, fmt.Errorf("failed to replace config for rebased image: %w", err) + } + + // Get new base layers and config for history. + newBaseLayers, err := newBase.Layers() + if err != nil { + return nil, fmt.Errorf("could not get new base layers for new base: %w", err) + } + // Add new base layers. + rebasedImage, err = Append(rebasedImage, createAddendums(0, 0, newConfig.History, newBaseLayers)...) + if err != nil { + return nil, fmt.Errorf("failed to append new base image: %w", err) + } + + // Add original layers above the old base. + rebasedImage, err = Append(rebasedImage, createAddendums(len(oldConfig.History), len(oldBaseLayers)+1, origConfig.History, origLayers)...) + if err != nil { + return nil, fmt.Errorf("failed to append original image: %w", err) + } + + return rebasedImage, nil +} + +// createAddendums makes a list of addendums from a history and layers starting from a specific history and layer +// indexes. +func createAddendums(startHistory, startLayer int, history []v1.History, layers []v1.Layer) []Addendum { + var adds []Addendum + // History should be a superset of layers; empty layers (e.g. ENV statements) only exist in history. + // They cannot be iterated identically but must be walked independently, only advancing the iterator for layers + // when a history entry for a non-empty layer is seen. + layerIndex := 0 + for historyIndex := range history { + var layer v1.Layer + emptyLayer := history[historyIndex].EmptyLayer + if !emptyLayer { + layer = layers[layerIndex] + layerIndex++ + } + if historyIndex >= startHistory || layerIndex >= startLayer { + adds = append(adds, Addendum{ + Layer: layer, + History: history[historyIndex], + }) + } + } + // In the event history was malformed or non-existent, append the remaining layers. + for i := layerIndex; i < len(layers); i++ { + if i >= startLayer { + adds = append(adds, Addendum{Layer: layers[layerIndex]}) + } + } + + return adds +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/partial/README.md b/vendor/github.com/google/go-containerregistry/pkg/v1/partial/README.md new file mode 100644 index 0000000000..53ebbc6ccf --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/partial/README.md @@ -0,0 +1,82 @@ +# `partial` + +[![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/partial?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/partial) + +## Partial Implementations + +There are roughly two kinds of image representations: compressed and uncompressed. + +The implementations for these kinds of images are almost identical, with the only +major difference being how blobs (config and layers) are fetched. This common +code lives in this package, where you provide a _partial_ implementation of a +compressed or uncompressed image, and you get back a full `v1.Image` implementation. + +### Examples + +In a registry, blobs are compressed, so it's easiest to implement a `v1.Image` in terms +of compressed layers. `remote.remoteImage` does this by implementing `CompressedImageCore`: + +```go +type CompressedImageCore interface { + RawConfigFile() ([]byte, error) + MediaType() (types.MediaType, error) + RawManifest() ([]byte, error) + LayerByDigest(v1.Hash) (CompressedLayer, error) +} +``` + +In a tarball, blobs are (often) uncompressed, so it's easiest to implement a `v1.Image` in terms +of uncompressed layers. `tarball.uncompressedImage` does this by implementing `UncompressedImageCore`: + +```go +type UncompressedImageCore interface { + RawConfigFile() ([]byte, error) + MediaType() (types.MediaType, error) + LayerByDiffID(v1.Hash) (UncompressedLayer, error) +} +``` + +## Optional Methods + +Where possible, we access some information via optional methods as an optimization. + +### [`partial.Descriptor`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/partial#Descriptor) + +There are some properties of a [`Descriptor`](https://github.com/opencontainers/image-spec/blob/master/descriptor.md#properties) that aren't derivable from just image data: + +* `MediaType` +* `Platform` +* `URLs` +* `Annotations` + +For example, in a `tarball.Image`, there is a `LayerSources` field that contains +an entire layer descriptor with `URLs` information for foreign layers. This +information can be passed through to callers by implementing this optional +`Descriptor` method. + +See [`#654`](https://github.com/google/go-containerregistry/pull/654). + +### [`partial.UncompressedSize`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/partial#UncompressedSize) + +Usually, you don't need to know the uncompressed size of a layer, since that +information isn't stored in a config file (just he sha256 is needed); however, +there are cases where it is very helpful to know the layer size, e.g. when +writing the uncompressed layer into a tarball. + +See [`#655`](https://github.com/google/go-containerregistry/pull/655). + +### [`partial.Exists`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/partial#Exists) + +We generally don't care about the existence of something as granular as a +layer, and would rather ensure all the invariants of an image are upheld via +the `validate` package. However, there are situations where we want to do a +quick smoke test to ensure that the underlying storage engine hasn't been +corrupted by something e.g. deleting files or blobs. Thus, we've exposed an +optional `Exists` method that does an existence check without actually reading +any bytes. + +The `remote` package implements this via `HEAD` requests. + +The `layout` package implements this via `os.Stat`. + +See [`#838`](https://github.com/google/go-containerregistry/pull/838). diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/partial/compressed.go b/vendor/github.com/google/go-containerregistry/pkg/v1/partial/compressed.go new file mode 100644 index 0000000000..c999517289 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/partial/compressed.go @@ -0,0 +1,181 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package partial + +import ( + "io" + + "github.com/google/go-containerregistry/internal/and" + "github.com/google/go-containerregistry/internal/gzip" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +// CompressedLayer represents the bare minimum interface a natively +// compressed layer must implement for us to produce a v1.Layer +type CompressedLayer interface { + // Digest returns the Hash of the compressed layer. + Digest() (v1.Hash, error) + + // Compressed returns an io.ReadCloser for the compressed layer contents. + Compressed() (io.ReadCloser, error) + + // Size returns the compressed size of the Layer. + Size() (int64, error) + + // Returns the mediaType for the compressed Layer + MediaType() (types.MediaType, error) +} + +// compressedLayerExtender implements v1.Image using the compressed base properties. +type compressedLayerExtender struct { + CompressedLayer +} + +// Uncompressed implements v1.Layer +func (cle *compressedLayerExtender) Uncompressed() (io.ReadCloser, error) { + rc, err := cle.Compressed() + if err != nil { + return nil, err + } + + // Often, the "compressed" bytes are not actually gzip-compressed. + // Peek at the first two bytes to determine whether or not it's correct to + // wrap this with gzip.UnzipReadCloser. + gzipped, pr, err := gzip.Peek(rc) + if err != nil { + return nil, err + } + prc := &and.ReadCloser{ + Reader: pr, + CloseFunc: rc.Close, + } + + if !gzipped { + return prc, nil + } + + return gzip.UnzipReadCloser(prc) +} + +// DiffID implements v1.Layer +func (cle *compressedLayerExtender) DiffID() (v1.Hash, error) { + // If our nested CompressedLayer implements DiffID, + // then delegate to it instead. + if wdi, ok := cle.CompressedLayer.(WithDiffID); ok { + return wdi.DiffID() + } + r, err := cle.Uncompressed() + if err != nil { + return v1.Hash{}, err + } + defer r.Close() + h, _, err := v1.SHA256(r) + return h, err +} + +// CompressedToLayer fills in the missing methods from a CompressedLayer so that it implements v1.Layer +func CompressedToLayer(ul CompressedLayer) (v1.Layer, error) { + return &compressedLayerExtender{ul}, nil +} + +// CompressedImageCore represents the base minimum interface a natively +// compressed image must implement for us to produce a v1.Image. +type CompressedImageCore interface { + ImageCore + + // RawManifest returns the serialized bytes of the manifest. + RawManifest() ([]byte, error) + + // LayerByDigest is a variation on the v1.Image method, which returns + // a CompressedLayer instead. + LayerByDigest(v1.Hash) (CompressedLayer, error) +} + +// compressedImageExtender implements v1.Image by extending CompressedImageCore with the +// appropriate methods computed from the minimal core. +type compressedImageExtender struct { + CompressedImageCore +} + +// Assert that our extender type completes the v1.Image interface +var _ v1.Image = (*compressedImageExtender)(nil) + +// Digest implements v1.Image +func (i *compressedImageExtender) Digest() (v1.Hash, error) { + return Digest(i) +} + +// ConfigName implements v1.Image +func (i *compressedImageExtender) ConfigName() (v1.Hash, error) { + return ConfigName(i) +} + +// Layers implements v1.Image +func (i *compressedImageExtender) Layers() ([]v1.Layer, error) { + hs, err := FSLayers(i) + if err != nil { + return nil, err + } + ls := make([]v1.Layer, 0, len(hs)) + for _, h := range hs { + l, err := i.LayerByDigest(h) + if err != nil { + return nil, err + } + ls = append(ls, l) + } + return ls, nil +} + +// LayerByDigest implements v1.Image +func (i *compressedImageExtender) LayerByDigest(h v1.Hash) (v1.Layer, error) { + cl, err := i.CompressedImageCore.LayerByDigest(h) + if err != nil { + return nil, err + } + return CompressedToLayer(cl) +} + +// LayerByDiffID implements v1.Image +func (i *compressedImageExtender) LayerByDiffID(h v1.Hash) (v1.Layer, error) { + h, err := DiffIDToBlob(i, h) + if err != nil { + return nil, err + } + return i.LayerByDigest(h) +} + +// ConfigFile implements v1.Image +func (i *compressedImageExtender) ConfigFile() (*v1.ConfigFile, error) { + return ConfigFile(i) +} + +// Manifest implements v1.Image +func (i *compressedImageExtender) Manifest() (*v1.Manifest, error) { + return Manifest(i) +} + +// Size implements v1.Image +func (i *compressedImageExtender) Size() (int64, error) { + return Size(i) +} + +// CompressedToImage fills in the missing methods from a CompressedImageCore so that it implements v1.Image +func CompressedToImage(cic CompressedImageCore) (v1.Image, error) { + return &compressedImageExtender{ + CompressedImageCore: cic, + }, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/partial/doc.go b/vendor/github.com/google/go-containerregistry/pkg/v1/partial/doc.go new file mode 100644 index 0000000000..153dfe4d53 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/partial/doc.go @@ -0,0 +1,17 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +// Package partial defines methods for building up a v1.Image from +// minimal subsets that are sufficient for defining a v1.Image. +package partial diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/partial/image.go b/vendor/github.com/google/go-containerregistry/pkg/v1/partial/image.go new file mode 100644 index 0000000000..c65f45e0dc --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/partial/image.go @@ -0,0 +1,28 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package partial + +import ( + "github.com/google/go-containerregistry/pkg/v1/types" +) + +// ImageCore is the core set of properties without which we cannot build a v1.Image +type ImageCore interface { + // RawConfigFile returns the serialized bytes of this image's config file. + RawConfigFile() ([]byte, error) + + // MediaType of this image's manifest. + MediaType() (types.MediaType, error) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/partial/index.go b/vendor/github.com/google/go-containerregistry/pkg/v1/partial/index.go new file mode 100644 index 0000000000..f17f27446f --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/partial/index.go @@ -0,0 +1,85 @@ +// Copyright 2020 Google LLC All Rights Reserved. +// +// 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. + +package partial + +import ( + "fmt" + + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/match" +) + +// FindManifests given a v1.ImageIndex, find the manifests that fit the matcher. +func FindManifests(index v1.ImageIndex, matcher match.Matcher) ([]v1.Descriptor, error) { + // get the actual manifest list + indexManifest, err := index.IndexManifest() + if err != nil { + return nil, fmt.Errorf("unable to get raw index: %w", err) + } + manifests := []v1.Descriptor{} + // try to get the root of our image + for _, manifest := range indexManifest.Manifests { + if matcher(manifest) { + manifests = append(manifests, manifest) + } + } + return manifests, nil +} + +// FindImages given a v1.ImageIndex, find the images that fit the matcher. If a Descriptor +// matches the provider Matcher, but the referenced item is not an Image, ignores it. +// Only returns those that match the Matcher and are images. +func FindImages(index v1.ImageIndex, matcher match.Matcher) ([]v1.Image, error) { + matches := []v1.Image{} + manifests, err := FindManifests(index, matcher) + if err != nil { + return nil, err + } + for _, desc := range manifests { + // if it is not an image, ignore it + if !desc.MediaType.IsImage() { + continue + } + img, err := index.Image(desc.Digest) + if err != nil { + return nil, err + } + matches = append(matches, img) + } + return matches, nil +} + +// FindIndexes given a v1.ImageIndex, find the indexes that fit the matcher. If a Descriptor +// matches the provider Matcher, but the referenced item is not an Index, ignores it. +// Only returns those that match the Matcher and are indexes. +func FindIndexes(index v1.ImageIndex, matcher match.Matcher) ([]v1.ImageIndex, error) { + matches := []v1.ImageIndex{} + manifests, err := FindManifests(index, matcher) + if err != nil { + return nil, err + } + for _, desc := range manifests { + if !desc.MediaType.IsIndex() { + continue + } + // if it is not an index, ignore it + idx, err := index.ImageIndex(desc.Digest) + if err != nil { + return nil, err + } + matches = append(matches, idx) + } + return matches, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/partial/uncompressed.go b/vendor/github.com/google/go-containerregistry/pkg/v1/partial/uncompressed.go new file mode 100644 index 0000000000..df20d3aa9e --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/partial/uncompressed.go @@ -0,0 +1,223 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package partial + +import ( + "bytes" + "io" + "sync" + + "github.com/google/go-containerregistry/internal/gzip" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +// UncompressedLayer represents the bare minimum interface a natively +// uncompressed layer must implement for us to produce a v1.Layer +type UncompressedLayer interface { + // DiffID returns the Hash of the uncompressed layer. + DiffID() (v1.Hash, error) + + // Uncompressed returns an io.ReadCloser for the uncompressed layer contents. + Uncompressed() (io.ReadCloser, error) + + // Returns the mediaType for the compressed Layer + MediaType() (types.MediaType, error) +} + +// uncompressedLayerExtender implements v1.Image using the uncompressed base properties. +type uncompressedLayerExtender struct { + UncompressedLayer + // Memoize size/hash so that the methods aren't twice as + // expensive as doing this manually. + hash v1.Hash + size int64 + hashSizeError error + once sync.Once +} + +// Compressed implements v1.Layer +func (ule *uncompressedLayerExtender) Compressed() (io.ReadCloser, error) { + u, err := ule.Uncompressed() + if err != nil { + return nil, err + } + return gzip.ReadCloser(u), nil +} + +// Digest implements v1.Layer +func (ule *uncompressedLayerExtender) Digest() (v1.Hash, error) { + ule.calcSizeHash() + return ule.hash, ule.hashSizeError +} + +// Size implements v1.Layer +func (ule *uncompressedLayerExtender) Size() (int64, error) { + ule.calcSizeHash() + return ule.size, ule.hashSizeError +} + +func (ule *uncompressedLayerExtender) calcSizeHash() { + ule.once.Do(func() { + var r io.ReadCloser + r, ule.hashSizeError = ule.Compressed() + if ule.hashSizeError != nil { + return + } + defer r.Close() + ule.hash, ule.size, ule.hashSizeError = v1.SHA256(r) + }) +} + +// UncompressedToLayer fills in the missing methods from an UncompressedLayer so that it implements v1.Layer +func UncompressedToLayer(ul UncompressedLayer) (v1.Layer, error) { + return &uncompressedLayerExtender{UncompressedLayer: ul}, nil +} + +// UncompressedImageCore represents the bare minimum interface a natively +// uncompressed image must implement for us to produce a v1.Image +type UncompressedImageCore interface { + ImageCore + + // LayerByDiffID is a variation on the v1.Image method, which returns + // an UncompressedLayer instead. + LayerByDiffID(v1.Hash) (UncompressedLayer, error) +} + +// UncompressedToImage fills in the missing methods from an UncompressedImageCore so that it implements v1.Image. +func UncompressedToImage(uic UncompressedImageCore) (v1.Image, error) { + return &uncompressedImageExtender{ + UncompressedImageCore: uic, + }, nil +} + +// uncompressedImageExtender implements v1.Image by extending UncompressedImageCore with the +// appropriate methods computed from the minimal core. +type uncompressedImageExtender struct { + UncompressedImageCore + + lock sync.Mutex + manifest *v1.Manifest +} + +// Assert that our extender type completes the v1.Image interface +var _ v1.Image = (*uncompressedImageExtender)(nil) + +// Digest implements v1.Image +func (i *uncompressedImageExtender) Digest() (v1.Hash, error) { + return Digest(i) +} + +// Manifest implements v1.Image +func (i *uncompressedImageExtender) Manifest() (*v1.Manifest, error) { + i.lock.Lock() + defer i.lock.Unlock() + if i.manifest != nil { + return i.manifest, nil + } + + b, err := i.RawConfigFile() + if err != nil { + return nil, err + } + + cfgHash, cfgSize, err := v1.SHA256(bytes.NewReader(b)) + if err != nil { + return nil, err + } + + m := &v1.Manifest{ + SchemaVersion: 2, + MediaType: types.DockerManifestSchema2, + Config: v1.Descriptor{ + MediaType: types.DockerConfigJSON, + Size: cfgSize, + Digest: cfgHash, + }, + } + + ls, err := i.Layers() + if err != nil { + return nil, err + } + + m.Layers = make([]v1.Descriptor, len(ls)) + for i, l := range ls { + desc, err := Descriptor(l) + if err != nil { + return nil, err + } + + m.Layers[i] = *desc + } + + i.manifest = m + return i.manifest, nil +} + +// RawManifest implements v1.Image +func (i *uncompressedImageExtender) RawManifest() ([]byte, error) { + return RawManifest(i) +} + +// Size implements v1.Image +func (i *uncompressedImageExtender) Size() (int64, error) { + return Size(i) +} + +// ConfigName implements v1.Image +func (i *uncompressedImageExtender) ConfigName() (v1.Hash, error) { + return ConfigName(i) +} + +// ConfigFile implements v1.Image +func (i *uncompressedImageExtender) ConfigFile() (*v1.ConfigFile, error) { + return ConfigFile(i) +} + +// Layers implements v1.Image +func (i *uncompressedImageExtender) Layers() ([]v1.Layer, error) { + diffIDs, err := DiffIDs(i) + if err != nil { + return nil, err + } + ls := make([]v1.Layer, 0, len(diffIDs)) + for _, h := range diffIDs { + l, err := i.LayerByDiffID(h) + if err != nil { + return nil, err + } + ls = append(ls, l) + } + return ls, nil +} + +// LayerByDiffID implements v1.Image +func (i *uncompressedImageExtender) LayerByDiffID(diffID v1.Hash) (v1.Layer, error) { + ul, err := i.UncompressedImageCore.LayerByDiffID(diffID) + if err != nil { + return nil, err + } + return UncompressedToLayer(ul) +} + +// LayerByDigest implements v1.Image +func (i *uncompressedImageExtender) LayerByDigest(h v1.Hash) (v1.Layer, error) { + diffID, err := BlobToDiffID(i, h) + if err != nil { + return nil, err + } + return i.LayerByDiffID(diffID) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/partial/with.go b/vendor/github.com/google/go-containerregistry/pkg/v1/partial/with.go new file mode 100644 index 0000000000..3a5c615722 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/partial/with.go @@ -0,0 +1,389 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package partial + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +// WithRawConfigFile defines the subset of v1.Image used by these helper methods +type WithRawConfigFile interface { + // RawConfigFile returns the serialized bytes of this image's config file. + RawConfigFile() ([]byte, error) +} + +// ConfigFile is a helper for implementing v1.Image +func ConfigFile(i WithRawConfigFile) (*v1.ConfigFile, error) { + b, err := i.RawConfigFile() + if err != nil { + return nil, err + } + return v1.ParseConfigFile(bytes.NewReader(b)) +} + +// ConfigName is a helper for implementing v1.Image +func ConfigName(i WithRawConfigFile) (v1.Hash, error) { + b, err := i.RawConfigFile() + if err != nil { + return v1.Hash{}, err + } + h, _, err := v1.SHA256(bytes.NewReader(b)) + return h, err +} + +type configLayer struct { + hash v1.Hash + content []byte +} + +// Digest implements v1.Layer +func (cl *configLayer) Digest() (v1.Hash, error) { + return cl.hash, nil +} + +// DiffID implements v1.Layer +func (cl *configLayer) DiffID() (v1.Hash, error) { + return cl.hash, nil +} + +// Uncompressed implements v1.Layer +func (cl *configLayer) Uncompressed() (io.ReadCloser, error) { + return ioutil.NopCloser(bytes.NewBuffer(cl.content)), nil +} + +// Compressed implements v1.Layer +func (cl *configLayer) Compressed() (io.ReadCloser, error) { + return ioutil.NopCloser(bytes.NewBuffer(cl.content)), nil +} + +// Size implements v1.Layer +func (cl *configLayer) Size() (int64, error) { + return int64(len(cl.content)), nil +} + +func (cl *configLayer) MediaType() (types.MediaType, error) { + // Defaulting this to OCIConfigJSON as it should remain + // backwards compatible with DockerConfigJSON + return types.OCIConfigJSON, nil +} + +var _ v1.Layer = (*configLayer)(nil) + +// ConfigLayer implements v1.Layer from the raw config bytes. +// This is so that clients (e.g. remote) can access the config as a blob. +func ConfigLayer(i WithRawConfigFile) (v1.Layer, error) { + h, err := ConfigName(i) + if err != nil { + return nil, err + } + rcfg, err := i.RawConfigFile() + if err != nil { + return nil, err + } + return &configLayer{ + hash: h, + content: rcfg, + }, nil +} + +// WithConfigFile defines the subset of v1.Image used by these helper methods +type WithConfigFile interface { + // ConfigFile returns this image's config file. + ConfigFile() (*v1.ConfigFile, error) +} + +// DiffIDs is a helper for implementing v1.Image +func DiffIDs(i WithConfigFile) ([]v1.Hash, error) { + cfg, err := i.ConfigFile() + if err != nil { + return nil, err + } + return cfg.RootFS.DiffIDs, nil +} + +// RawConfigFile is a helper for implementing v1.Image +func RawConfigFile(i WithConfigFile) ([]byte, error) { + cfg, err := i.ConfigFile() + if err != nil { + return nil, err + } + return json.Marshal(cfg) +} + +// WithRawManifest defines the subset of v1.Image used by these helper methods +type WithRawManifest interface { + // RawManifest returns the serialized bytes of this image's config file. + RawManifest() ([]byte, error) +} + +// Digest is a helper for implementing v1.Image +func Digest(i WithRawManifest) (v1.Hash, error) { + mb, err := i.RawManifest() + if err != nil { + return v1.Hash{}, err + } + digest, _, err := v1.SHA256(bytes.NewReader(mb)) + return digest, err +} + +// Manifest is a helper for implementing v1.Image +func Manifest(i WithRawManifest) (*v1.Manifest, error) { + b, err := i.RawManifest() + if err != nil { + return nil, err + } + return v1.ParseManifest(bytes.NewReader(b)) +} + +// WithManifest defines the subset of v1.Image used by these helper methods +type WithManifest interface { + // Manifest returns this image's Manifest object. + Manifest() (*v1.Manifest, error) +} + +// RawManifest is a helper for implementing v1.Image +func RawManifest(i WithManifest) ([]byte, error) { + m, err := i.Manifest() + if err != nil { + return nil, err + } + return json.Marshal(m) +} + +// Size is a helper for implementing v1.Image +func Size(i WithRawManifest) (int64, error) { + b, err := i.RawManifest() + if err != nil { + return -1, err + } + return int64(len(b)), nil +} + +// FSLayers is a helper for implementing v1.Image +func FSLayers(i WithManifest) ([]v1.Hash, error) { + m, err := i.Manifest() + if err != nil { + return nil, err + } + fsl := make([]v1.Hash, len(m.Layers)) + for i, l := range m.Layers { + fsl[i] = l.Digest + } + return fsl, nil +} + +// BlobSize is a helper for implementing v1.Image +func BlobSize(i WithManifest, h v1.Hash) (int64, error) { + d, err := BlobDescriptor(i, h) + if err != nil { + return -1, err + } + return d.Size, nil +} + +// BlobDescriptor is a helper for implementing v1.Image +func BlobDescriptor(i WithManifest, h v1.Hash) (*v1.Descriptor, error) { + m, err := i.Manifest() + if err != nil { + return nil, err + } + + if m.Config.Digest == h { + return &m.Config, nil + } + + for _, l := range m.Layers { + if l.Digest == h { + return &l, nil + } + } + return nil, fmt.Errorf("blob %v not found", h) +} + +// WithManifestAndConfigFile defines the subset of v1.Image used by these helper methods +type WithManifestAndConfigFile interface { + WithConfigFile + + // Manifest returns this image's Manifest object. + Manifest() (*v1.Manifest, error) +} + +// BlobToDiffID is a helper for mapping between compressed +// and uncompressed blob hashes. +func BlobToDiffID(i WithManifestAndConfigFile, h v1.Hash) (v1.Hash, error) { + blobs, err := FSLayers(i) + if err != nil { + return v1.Hash{}, err + } + diffIDs, err := DiffIDs(i) + if err != nil { + return v1.Hash{}, err + } + if len(blobs) != len(diffIDs) { + return v1.Hash{}, fmt.Errorf("mismatched fs layers (%d) and diff ids (%d)", len(blobs), len(diffIDs)) + } + for i, blob := range blobs { + if blob == h { + return diffIDs[i], nil + } + } + return v1.Hash{}, fmt.Errorf("unknown blob %v", h) +} + +// DiffIDToBlob is a helper for mapping between uncompressed +// and compressed blob hashes. +func DiffIDToBlob(wm WithManifestAndConfigFile, h v1.Hash) (v1.Hash, error) { + blobs, err := FSLayers(wm) + if err != nil { + return v1.Hash{}, err + } + diffIDs, err := DiffIDs(wm) + if err != nil { + return v1.Hash{}, err + } + if len(blobs) != len(diffIDs) { + return v1.Hash{}, fmt.Errorf("mismatched fs layers (%d) and diff ids (%d)", len(blobs), len(diffIDs)) + } + for i, diffID := range diffIDs { + if diffID == h { + return blobs[i], nil + } + } + return v1.Hash{}, fmt.Errorf("unknown diffID %v", h) +} + +// WithDiffID defines the subset of v1.Layer for exposing the DiffID method. +type WithDiffID interface { + DiffID() (v1.Hash, error) +} + +// withDescriptor allows partial layer implementations to provide a layer +// descriptor to the partial image manifest builder. This allows partial +// uncompressed layers to provide foreign layer metadata like URLs to the +// uncompressed image manifest. +type withDescriptor interface { + Descriptor() (*v1.Descriptor, error) +} + +// Describable represents something for which we can produce a v1.Descriptor. +type Describable interface { + Digest() (v1.Hash, error) + MediaType() (types.MediaType, error) + Size() (int64, error) +} + +// Descriptor returns a v1.Descriptor given a Describable. It also encodes +// some logic for unwrapping things that have been wrapped by +// CompressedToLayer, UncompressedToLayer, CompressedToImage, or +// UncompressedToImage. +func Descriptor(d Describable) (*v1.Descriptor, error) { + // If Describable implements Descriptor itself, return that. + if wd, ok := unwrap(d).(withDescriptor); ok { + return wd.Descriptor() + } + + // If all else fails, compute the descriptor from the individual methods. + var ( + desc v1.Descriptor + err error + ) + + if desc.Size, err = d.Size(); err != nil { + return nil, err + } + if desc.Digest, err = d.Digest(); err != nil { + return nil, err + } + if desc.MediaType, err = d.MediaType(); err != nil { + return nil, err + } + + return &desc, nil +} + +type withUncompressedSize interface { + UncompressedSize() (int64, error) +} + +// UncompressedSize returns the size of the Uncompressed layer. If the +// underlying implementation doesn't implement UncompressedSize directly, +// this will compute the uncompressedSize by reading everything returned +// by Compressed(). This is potentially expensive and may consume the contents +// for streaming layers. +func UncompressedSize(l v1.Layer) (int64, error) { + // If the layer implements UncompressedSize itself, return that. + if wus, ok := unwrap(l).(withUncompressedSize); ok { + return wus.UncompressedSize() + } + + // The layer doesn't implement UncompressedSize, we need to compute it. + rc, err := l.Uncompressed() + if err != nil { + return -1, err + } + defer rc.Close() + + return io.Copy(ioutil.Discard, rc) +} + +type withExists interface { + Exists() (bool, error) +} + +// Exists checks to see if a layer exists. This is a hack to work around the +// mistakes of the partial package. Don't use this. +func Exists(l v1.Layer) (bool, error) { + // If the layer implements Exists itself, return that. + if we, ok := unwrap(l).(withExists); ok { + return we.Exists() + } + + // The layer doesn't implement Exists, so we hope that calling Compressed() + // is enough to trigger an error if the layer does not exist. + rc, err := l.Compressed() + if err != nil { + return false, err + } + defer rc.Close() + + // We may want to try actually reading a single byte, but if we need to do + // that, we should just fix this hack. + return true, nil +} + +// Recursively unwrap our wrappers so that we can check for the original implementation. +// We might want to expose this? +func unwrap(i interface{}) interface{} { + if ule, ok := i.(*uncompressedLayerExtender); ok { + return unwrap(ule.UncompressedLayer) + } + if cle, ok := i.(*compressedLayerExtender); ok { + return unwrap(cle.CompressedLayer) + } + if uie, ok := i.(*uncompressedImageExtender); ok { + return unwrap(uie.UncompressedImageCore) + } + if cie, ok := i.(*compressedImageExtender); ok { + return unwrap(cie.CompressedImageCore) + } + return i +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/platform.go b/vendor/github.com/google/go-containerregistry/pkg/v1/platform.go new file mode 100644 index 0000000000..9ee91ee292 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/platform.go @@ -0,0 +1,108 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package v1 + +import ( + "fmt" + "sort" + "strings" +) + +// Platform represents the target os/arch for an image. +type Platform struct { + Architecture string `json:"architecture"` + OS string `json:"os"` + OSVersion string `json:"os.version,omitempty"` + OSFeatures []string `json:"os.features,omitempty"` + Variant string `json:"variant,omitempty"` + Features []string `json:"features,omitempty"` +} + +func (p Platform) String() string { + if p.OS == "" { + return "" + } + var b strings.Builder + b.WriteString(p.OS) + if p.Architecture != "" { + b.WriteString("/") + b.WriteString(p.Architecture) + } + if p.Variant != "" { + b.WriteString("/") + b.WriteString(p.Variant) + } + if p.OSVersion != "" { + b.WriteString(":") + b.WriteString(p.OSVersion) + } + return b.String() +} + +// ParsePlatform parses a string representing a Platform, if possible. +func ParsePlatform(s string) (*Platform, error) { + var p Platform + parts := strings.Split(strings.TrimSpace(s), ":") + if len(parts) == 2 { + p.OSVersion = parts[1] + } + parts = strings.Split(parts[0], "/") + if len(parts) > 0 { + p.OS = parts[0] + } + if len(parts) > 1 { + p.Architecture = parts[1] + } + if len(parts) > 2 { + p.Variant = parts[2] + } + if len(parts) > 3 { + return nil, fmt.Errorf("too many slashes in platform spec: %s", s) + } + return &p, nil +} + +// Equals returns true if the given platform is semantically equivalent to this one. +// The order of Features and OSFeatures is not important. +func (p Platform) Equals(o Platform) bool { + return p.OS == o.OS && + p.Architecture == o.Architecture && + p.Variant == o.Variant && + p.OSVersion == o.OSVersion && + stringSliceEqualIgnoreOrder(p.OSFeatures, o.OSFeatures) && + stringSliceEqualIgnoreOrder(p.Features, o.Features) +} + +// stringSliceEqual compares 2 string slices and returns if their contents are identical. +func stringSliceEqual(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i, elm := range a { + if elm != b[i] { + return false + } + } + return true +} + +// stringSliceEqualIgnoreOrder compares 2 string slices and returns if their contents are identical, ignoring order +func stringSliceEqualIgnoreOrder(a, b []string) bool { + if a != nil && b != nil { + sort.Strings(a) + sort.Strings(b) + } + return stringSliceEqual(a, b) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/progress.go b/vendor/github.com/google/go-containerregistry/pkg/v1/progress.go new file mode 100644 index 0000000000..844f04d937 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/progress.go @@ -0,0 +1,25 @@ +// Copyright 2020 Google LLC All Rights Reserved. +// +// 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. + +package v1 + +// Update representation of an update of transfer progress. Some functions +// in this module can take a channel to which updates will be sent while a +// transfer is in progress. +// +k8s:deepcopy-gen=false +type Update struct { + Total int64 + Complete int64 + Error error +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/README.md b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/README.md new file mode 100644 index 0000000000..c1e81b310b --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/README.md @@ -0,0 +1,117 @@ +# `remote` + +[![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote) + +The `remote` package implements a client for accessing a registry, +per the [OCI distribution spec](https://github.com/opencontainers/distribution-spec/blob/master/spec.md). + +It leans heavily on the lower level [`transport`](/pkg/v1/remote/transport) package, which handles the +authentication handshake and structured errors. + +## Usage + +```go +package main + +import ( + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote" +) + +func main() { + ref, err := name.ParseReference("gcr.io/google-containers/pause") + if err != nil { + panic(err) + } + + img, err := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain)) + if err != nil { + panic(err) + } + + // do stuff with img +} +``` + +## Structure + +

+ +

+ + +## Background + +There are a lot of confusingly similar terms that come up when talking about images in registries. + +### Anatomy of an image + +In general... + +* A tag refers to an image manifest. +* An image manifest references a config file and an orderered list of _compressed_ layers by sha256 digest. +* A config file references an ordered list of _uncompressed_ layers by sha256 digest and contains runtime configuration. +* The sha256 digest of the config file is the [image id](https://github.com/opencontainers/image-spec/blob/master/config.md#imageid) for the image. + +For example, an image with two layers would look something like this: + +![image anatomy](/images/image-anatomy.dot.svg) + +### Anatomy of an index + +In the normal case, an [index](https://github.com/opencontainers/image-spec/blob/master/image-index.md) is used to represent a multi-platform image. +This was the original use case for a [manifest +list](https://docs.docker.com/registry/spec/manifest-v2-2/#manifest-list). + +![image index anatomy](/images/index-anatomy.dot.svg) + +It is possible for an index to reference another index, per the OCI +[image-spec](https://github.com/opencontainers/image-spec/blob/master/media-types.md#compatibility-matrix). +In theory, both an image and image index can reference arbitrary things via +[descriptors](https://github.com/opencontainers/image-spec/blob/master/descriptor.md), +e.g. see the [image layout +example](https://github.com/opencontainers/image-spec/blob/master/image-layout.md#index-example), +which references an application/xml file from an image index. + +That could look something like this: + +![strange image index anatomy](/images/index-anatomy-strange.dot.svg) + +Using a recursive index like this might not be possible with all registries, +but this flexibility allows for some interesting applications, e.g. the +[OCI Artifacts](https://github.com/opencontainers/artifacts) effort. + +### Anatomy of an image upload + +The structure of an image requires a delicate ordering when uploading an image to a registry. +Below is a (slightly simplified) figure that describes how an image is prepared for upload +to a registry and how the data flows between various artifacts: + +![upload](/images/upload.dot.svg) + +Note that: + +* A config file references the uncompressed layer contents by sha256. +* A manifest references the compressed layer contents by sha256 and the size of the layer. +* A manifest references the config file contents by sha256 and the size of the file. + +It follows that during an upload, we need to upload layers before the config file, +and we need to upload the config file before the manifest. + +Sometimes, we know all of this information ahead of time, (e.g. when copying from remote.Image), +so the ordering is less important. + +In other cases, e.g. when using a [`stream.Layer`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/stream#Layer), +we can't compute anything until we have already uploaded the layer, so we need to be careful about ordering. + +## Caveats + +### schema 1 + +This package does not support schema 1 images, see [`#377`](https://github.com/google/go-containerregistry/issues/377), +however, it's possible to do _something_ useful with them via [`remote.Get`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote#Get), +which doesn't try to interpret what is returned by the registry. + +[`crane.Copy`](https://godoc.org/github.com/google/go-containerregistry/pkg/crane#Copy) takes advantage of this to implement support for copying schema 1 images, +see [here](https://github.com/google/go-containerregistry/blob/main/pkg/internal/legacy/copy.go). diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/catalog.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/catalog.go new file mode 100644 index 0000000000..eb4306f287 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/catalog.go @@ -0,0 +1,154 @@ +// Copyright 2019 Google LLC All Rights Reserved. +// +// 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. + +package remote + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" +) + +type catalog struct { + Repos []string `json:"repositories"` +} + +// CatalogPage calls /_catalog, returning the list of repositories on the registry. +func CatalogPage(target name.Registry, last string, n int, options ...Option) ([]string, error) { + o, err := makeOptions(target, options...) + if err != nil { + return nil, err + } + + scopes := []string{target.Scope(transport.PullScope)} + tr, err := transport.NewWithContext(o.context, target, o.auth, o.transport, scopes) + if err != nil { + return nil, err + } + + query := fmt.Sprintf("last=%s&n=%d", url.QueryEscape(last), n) + + uri := url.URL{ + Scheme: target.Scheme(), + Host: target.RegistryStr(), + Path: "/v2/_catalog", + RawQuery: query, + } + + client := http.Client{Transport: tr} + req, err := http.NewRequest(http.MethodGet, uri.String(), nil) + if err != nil { + return nil, err + } + resp, err := client.Do(req.WithContext(o.context)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if err := transport.CheckError(resp, http.StatusOK); err != nil { + return nil, err + } + + var parsed catalog + if err := json.NewDecoder(resp.Body).Decode(&parsed); err != nil { + return nil, err + } + + return parsed.Repos, nil +} + +// Catalog calls /_catalog, returning the list of repositories on the registry. +func Catalog(ctx context.Context, target name.Registry, options ...Option) ([]string, error) { + o, err := makeOptions(target, options...) + if err != nil { + return nil, err + } + + scopes := []string{target.Scope(transport.PullScope)} + tr, err := transport.NewWithContext(o.context, target, o.auth, o.transport, scopes) + if err != nil { + return nil, err + } + + uri := &url.URL{ + Scheme: target.Scheme(), + Host: target.RegistryStr(), + Path: "/v2/_catalog", + } + + if o.pageSize > 0 { + uri.RawQuery = fmt.Sprintf("n=%d", o.pageSize) + } + + client := http.Client{Transport: tr} + + // WithContext overrides the ctx passed directly. + if o.context != context.Background() { + ctx = o.context + } + + var ( + parsed catalog + repoList []string + ) + + // get responses until there is no next page + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + req, err := http.NewRequest("GET", uri.String(), nil) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + + if err := transport.CheckError(resp, http.StatusOK); err != nil { + return nil, err + } + + if err := json.NewDecoder(resp.Body).Decode(&parsed); err != nil { + return nil, err + } + if err := resp.Body.Close(); err != nil { + return nil, err + } + + repoList = append(repoList, parsed.Repos...) + + uri, err = getNextPageURL(resp) + if err != nil { + return nil, err + } + // no next page + if uri == nil { + break + } + } + return repoList, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/check.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/check.go new file mode 100644 index 0000000000..25d86956f1 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/check.go @@ -0,0 +1,59 @@ +package remote + +import ( + "context" + "fmt" + "net/http" + + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" +) + +// CheckPushPermission returns an error if the given keychain cannot authorize +// a push operation to the given ref. +// +// This can be useful to check whether the caller has permission to push an +// image before doing work to construct the image. +// +// TODO(#412): Remove the need for this method. +func CheckPushPermission(ref name.Reference, kc authn.Keychain, t http.RoundTripper) error { + auth, err := kc.Resolve(ref.Context().Registry) + if err != nil { + return fmt.Errorf("resolving authorization for %v failed: %w", ref.Context().Registry, err) + } + + scopes := []string{ref.Scope(transport.PushScope)} + tr, err := transport.New(ref.Context().Registry, auth, t, scopes) + if err != nil { + return fmt.Errorf("creating push check transport for %v failed: %w", ref.Context().Registry, err) + } + // TODO(jasonhall): Against GCR, just doing the token handshake is + // enough, but this doesn't extend to Dockerhub + // (https://github.com/docker/hub-feedback/issues/1771), so we actually + // need to initiate an upload to tell whether the credentials can + // authorize a push. Figure out how to return early here when we can, + // to avoid a roundtrip for spec-compliant registries. + w := writer{ + repo: ref.Context(), + client: &http.Client{Transport: tr}, + context: context.Background(), + } + loc, _, err := w.initiateUpload("", "") + if loc != "" { + // Since we're only initiating the upload to check whether we + // can, we should attempt to cancel it, in case initiating + // reserves some resources on the server. We shouldn't wait for + // cancelling to complete, and we don't care if it fails. + go w.cancelUpload(loc) + } + return err +} + +func (w *writer) cancelUpload(loc string) { + req, err := http.NewRequest(http.MethodDelete, loc, nil) + if err != nil { + return + } + _, _ = w.client.Do(req) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/delete.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/delete.go new file mode 100644 index 0000000000..3b9022719c --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/delete.go @@ -0,0 +1,57 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package remote + +import ( + "fmt" + "net/http" + "net/url" + + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" +) + +// Delete removes the specified image reference from the remote registry. +func Delete(ref name.Reference, options ...Option) error { + o, err := makeOptions(ref.Context(), options...) + if err != nil { + return err + } + scopes := []string{ref.Scope(transport.DeleteScope)} + tr, err := transport.NewWithContext(o.context, ref.Context().Registry, o.auth, o.transport, scopes) + if err != nil { + return err + } + c := &http.Client{Transport: tr} + + u := url.URL{ + Scheme: ref.Context().Registry.Scheme(), + Host: ref.Context().RegistryStr(), + Path: fmt.Sprintf("/v2/%s/manifests/%s", ref.Context().RepositoryStr(), ref.Identifier()), + } + + req, err := http.NewRequest(http.MethodDelete, u.String(), nil) + if err != nil { + return err + } + + resp, err := c.Do(req.WithContext(o.context)) + if err != nil { + return err + } + defer resp.Body.Close() + + return transport.CheckError(resp, http.StatusOK, http.StatusAccepted) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/descriptor.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/descriptor.go new file mode 100644 index 0000000000..755e819293 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/descriptor.go @@ -0,0 +1,430 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package remote + +import ( + "bytes" + "context" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" + + "github.com/google/go-containerregistry/internal/verify" + "github.com/google/go-containerregistry/pkg/logs" + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +// ErrSchema1 indicates that we received a schema1 manifest from the registry. +// This library doesn't have plans to support this legacy image format: +// https://github.com/google/go-containerregistry/issues/377 +type ErrSchema1 struct { + schema string +} + +// newErrSchema1 returns an ErrSchema1 with the unexpected MediaType. +func newErrSchema1(schema types.MediaType) error { + return &ErrSchema1{ + schema: string(schema), + } +} + +// Error implements error. +func (e *ErrSchema1) Error() string { + return fmt.Sprintf("unsupported MediaType: %q, see https://github.com/google/go-containerregistry/issues/377", e.schema) +} + +// Descriptor provides access to metadata about remote artifact and accessors +// for efficiently converting it into a v1.Image or v1.ImageIndex. +type Descriptor struct { + fetcher + v1.Descriptor + Manifest []byte + + // So we can share this implementation with Image.. + platform v1.Platform +} + +// RawManifest exists to satisfy the Taggable interface. +func (d *Descriptor) RawManifest() ([]byte, error) { + return d.Manifest, nil +} + +// Get returns a remote.Descriptor for the given reference. The response from +// the registry is left un-interpreted, for the most part. This is useful for +// querying what kind of artifact a reference represents. +// +// See Head if you don't need the response body. +func Get(ref name.Reference, options ...Option) (*Descriptor, error) { + acceptable := []types.MediaType{ + // Just to look at them. + types.DockerManifestSchema1, + types.DockerManifestSchema1Signed, + } + acceptable = append(acceptable, acceptableImageMediaTypes...) + acceptable = append(acceptable, acceptableIndexMediaTypes...) + return get(ref, acceptable, options...) +} + +// Head returns a v1.Descriptor for the given reference by issuing a HEAD +// request. +// +// Note that the server response will not have a body, so any errors encountered +// should be retried with Get to get more details. +func Head(ref name.Reference, options ...Option) (*v1.Descriptor, error) { + acceptable := []types.MediaType{ + // Just to look at them. + types.DockerManifestSchema1, + types.DockerManifestSchema1Signed, + } + acceptable = append(acceptable, acceptableImageMediaTypes...) + acceptable = append(acceptable, acceptableIndexMediaTypes...) + + o, err := makeOptions(ref.Context(), options...) + if err != nil { + return nil, err + } + + f, err := makeFetcher(ref, o) + if err != nil { + return nil, err + } + + return f.headManifest(ref, acceptable) +} + +// Handle options and fetch the manifest with the acceptable MediaTypes in the +// Accept header. +func get(ref name.Reference, acceptable []types.MediaType, options ...Option) (*Descriptor, error) { + o, err := makeOptions(ref.Context(), options...) + if err != nil { + return nil, err + } + f, err := makeFetcher(ref, o) + if err != nil { + return nil, err + } + b, desc, err := f.fetchManifest(ref, acceptable) + if err != nil { + return nil, err + } + return &Descriptor{ + fetcher: *f, + Manifest: b, + Descriptor: *desc, + platform: o.platform, + }, nil +} + +// Image converts the Descriptor into a v1.Image. +// +// If the fetched artifact is already an image, it will just return it. +// +// If the fetched artifact is an index, it will attempt to resolve the index to +// a child image with the appropriate platform. +// +// See WithPlatform to set the desired platform. +func (d *Descriptor) Image() (v1.Image, error) { + switch d.MediaType { + case types.DockerManifestSchema1, types.DockerManifestSchema1Signed: + // We don't care to support schema 1 images: + // https://github.com/google/go-containerregistry/issues/377 + return nil, newErrSchema1(d.MediaType) + case types.OCIImageIndex, types.DockerManifestList: + // We want an image but the registry has an index, resolve it to an image. + return d.remoteIndex().imageByPlatform(d.platform) + case types.OCIManifestSchema1, types.DockerManifestSchema2: + // These are expected. Enumerated here to allow a default case. + default: + // We could just return an error here, but some registries (e.g. static + // registries) don't set the Content-Type headers correctly, so instead... + logs.Warn.Printf("Unexpected media type for Image(): %s", d.MediaType) + } + + // Wrap the v1.Layers returned by this v1.Image in a hint for downstream + // remote.Write calls to facilitate cross-repo "mounting". + imgCore, err := partial.CompressedToImage(d.remoteImage()) + if err != nil { + return nil, err + } + return &mountableImage{ + Image: imgCore, + Reference: d.Ref, + }, nil +} + +// ImageIndex converts the Descriptor into a v1.ImageIndex. +func (d *Descriptor) ImageIndex() (v1.ImageIndex, error) { + switch d.MediaType { + case types.DockerManifestSchema1, types.DockerManifestSchema1Signed: + // We don't care to support schema 1 images: + // https://github.com/google/go-containerregistry/issues/377 + return nil, newErrSchema1(d.MediaType) + case types.OCIManifestSchema1, types.DockerManifestSchema2: + // We want an index but the registry has an image, nothing we can do. + return nil, fmt.Errorf("unexpected media type for ImageIndex(): %s; call Image() instead", d.MediaType) + case types.OCIImageIndex, types.DockerManifestList: + // These are expected. + default: + // We could just return an error here, but some registries (e.g. static + // registries) don't set the Content-Type headers correctly, so instead... + logs.Warn.Printf("Unexpected media type for ImageIndex(): %s", d.MediaType) + } + return d.remoteIndex(), nil +} + +func (d *Descriptor) remoteImage() *remoteImage { + return &remoteImage{ + fetcher: d.fetcher, + manifest: d.Manifest, + mediaType: d.MediaType, + descriptor: &d.Descriptor, + } +} + +func (d *Descriptor) remoteIndex() *remoteIndex { + return &remoteIndex{ + fetcher: d.fetcher, + manifest: d.Manifest, + mediaType: d.MediaType, + descriptor: &d.Descriptor, + } +} + +// fetcher implements methods for reading from a registry. +type fetcher struct { + Ref name.Reference + Client *http.Client + context context.Context +} + +func makeFetcher(ref name.Reference, o *options) (*fetcher, error) { + tr, err := transport.NewWithContext(o.context, ref.Context().Registry, o.auth, o.transport, []string{ref.Scope(transport.PullScope)}) + if err != nil { + return nil, err + } + return &fetcher{ + Ref: ref, + Client: &http.Client{Transport: tr}, + context: o.context, + }, nil +} + +// url returns a url.Url for the specified path in the context of this remote image reference. +func (f *fetcher) url(resource, identifier string) url.URL { + return url.URL{ + Scheme: f.Ref.Context().Registry.Scheme(), + Host: f.Ref.Context().RegistryStr(), + Path: fmt.Sprintf("/v2/%s/%s/%s", f.Ref.Context().RepositoryStr(), resource, identifier), + } +} + +func (f *fetcher) fetchManifest(ref name.Reference, acceptable []types.MediaType) ([]byte, *v1.Descriptor, error) { + u := f.url("manifests", ref.Identifier()) + req, err := http.NewRequest(http.MethodGet, u.String(), nil) + if err != nil { + return nil, nil, err + } + accept := []string{} + for _, mt := range acceptable { + accept = append(accept, string(mt)) + } + req.Header.Set("Accept", strings.Join(accept, ",")) + + resp, err := f.Client.Do(req.WithContext(f.context)) + if err != nil { + return nil, nil, err + } + defer resp.Body.Close() + + if err := transport.CheckError(resp, http.StatusOK); err != nil { + return nil, nil, err + } + + manifest, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, nil, err + } + + digest, size, err := v1.SHA256(bytes.NewReader(manifest)) + if err != nil { + return nil, nil, err + } + + mediaType := types.MediaType(resp.Header.Get("Content-Type")) + contentDigest, err := v1.NewHash(resp.Header.Get("Docker-Content-Digest")) + if err == nil && mediaType == types.DockerManifestSchema1Signed { + // If we can parse the digest from the header, and it's a signed schema 1 + // manifest, let's use that for the digest to appease older registries. + digest = contentDigest + } + + // Validate the digest matches what we asked for, if pulling by digest. + if dgst, ok := ref.(name.Digest); ok { + if digest.String() != dgst.DigestStr() { + return nil, nil, fmt.Errorf("manifest digest: %q does not match requested digest: %q for %q", digest, dgst.DigestStr(), f.Ref) + } + } + // Do nothing for tags; I give up. + // + // We'd like to validate that the "Docker-Content-Digest" header matches what is returned by the registry, + // but so many registries implement this incorrectly that it's not worth checking. + // + // For reference: + // https://github.com/GoogleContainerTools/kaniko/issues/298 + + // Return all this info since we have to calculate it anyway. + desc := v1.Descriptor{ + Digest: digest, + Size: size, + MediaType: mediaType, + } + + return manifest, &desc, nil +} + +func (f *fetcher) headManifest(ref name.Reference, acceptable []types.MediaType) (*v1.Descriptor, error) { + u := f.url("manifests", ref.Identifier()) + req, err := http.NewRequest(http.MethodHead, u.String(), nil) + if err != nil { + return nil, err + } + accept := []string{} + for _, mt := range acceptable { + accept = append(accept, string(mt)) + } + req.Header.Set("Accept", strings.Join(accept, ",")) + + resp, err := f.Client.Do(req.WithContext(f.context)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if err := transport.CheckError(resp, http.StatusOK); err != nil { + return nil, err + } + + mth := resp.Header.Get("Content-Type") + if mth == "" { + return nil, fmt.Errorf("HEAD %s: response did not include Content-Type header", u.String()) + } + mediaType := types.MediaType(mth) + + size := resp.ContentLength + if size == -1 { + return nil, fmt.Errorf("GET %s: response did not include Content-Length header", u.String()) + } + + dh := resp.Header.Get("Docker-Content-Digest") + if dh == "" { + return nil, fmt.Errorf("HEAD %s: response did not include Docker-Content-Digest header", u.String()) + } + digest, err := v1.NewHash(dh) + if err != nil { + return nil, err + } + + // Validate the digest matches what we asked for, if pulling by digest. + if dgst, ok := ref.(name.Digest); ok { + if digest.String() != dgst.DigestStr() { + return nil, fmt.Errorf("manifest digest: %q does not match requested digest: %q for %q", digest, dgst.DigestStr(), f.Ref) + } + } + + // Return all this info since we have to calculate it anyway. + return &v1.Descriptor{ + Digest: digest, + Size: size, + MediaType: mediaType, + }, nil +} + +func (f *fetcher) fetchBlob(ctx context.Context, size int64, h v1.Hash) (io.ReadCloser, error) { + u := f.url("blobs", h.String()) + req, err := http.NewRequest(http.MethodGet, u.String(), nil) + if err != nil { + return nil, err + } + + resp, err := f.Client.Do(req.WithContext(ctx)) + if err != nil { + return nil, err + } + + if err := transport.CheckError(resp, http.StatusOK); err != nil { + resp.Body.Close() + return nil, err + } + + // Do whatever we can. + // If we have an expected size and Content-Length doesn't match, return an error. + // If we don't have an expected size and we do have a Content-Length, use Content-Length. + if hsize := resp.ContentLength; hsize != -1 { + if size == verify.SizeUnknown { + size = hsize + } else if hsize != size { + return nil, fmt.Errorf("GET %s: Content-Length header %d does not match expected size %d", u.String(), hsize, size) + } + } + + return verify.ReadCloser(resp.Body, size, h) +} + +func (f *fetcher) headBlob(h v1.Hash) (*http.Response, error) { + u := f.url("blobs", h.String()) + req, err := http.NewRequest(http.MethodHead, u.String(), nil) + if err != nil { + return nil, err + } + + resp, err := f.Client.Do(req.WithContext(f.context)) + if err != nil { + return nil, err + } + + if err := transport.CheckError(resp, http.StatusOK); err != nil { + resp.Body.Close() + return nil, err + } + + return resp, nil +} + +func (f *fetcher) blobExists(h v1.Hash) (bool, error) { + u := f.url("blobs", h.String()) + req, err := http.NewRequest(http.MethodHead, u.String(), nil) + if err != nil { + return false, err + } + + resp, err := f.Client.Do(req.WithContext(f.context)) + if err != nil { + return false, err + } + defer resp.Body.Close() + + if err := transport.CheckError(resp, http.StatusOK, http.StatusNotFound); err != nil { + return false, err + } + + return resp.StatusCode == http.StatusOK, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/doc.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/doc.go new file mode 100644 index 0000000000..846ba07cda --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/doc.go @@ -0,0 +1,17 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +// Package remote provides facilities for reading/writing v1.Images from/to +// a remote image registry. +package remote diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/image.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/image.go new file mode 100644 index 0000000000..a36416d8c3 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/image.go @@ -0,0 +1,248 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package remote + +import ( + "bytes" + "io" + "io/ioutil" + "net/http" + "net/url" + "sync" + + "github.com/google/go-containerregistry/internal/redact" + "github.com/google/go-containerregistry/internal/verify" + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +var acceptableImageMediaTypes = []types.MediaType{ + types.DockerManifestSchema2, + types.OCIManifestSchema1, +} + +// remoteImage accesses an image from a remote registry +type remoteImage struct { + fetcher + manifestLock sync.Mutex // Protects manifest + manifest []byte + configLock sync.Mutex // Protects config + config []byte + mediaType types.MediaType + descriptor *v1.Descriptor +} + +var _ partial.CompressedImageCore = (*remoteImage)(nil) + +// Image provides access to a remote image reference. +func Image(ref name.Reference, options ...Option) (v1.Image, error) { + desc, err := Get(ref, options...) + if err != nil { + return nil, err + } + + return desc.Image() +} + +func (r *remoteImage) MediaType() (types.MediaType, error) { + if string(r.mediaType) != "" { + return r.mediaType, nil + } + return types.DockerManifestSchema2, nil +} + +func (r *remoteImage) RawManifest() ([]byte, error) { + r.manifestLock.Lock() + defer r.manifestLock.Unlock() + if r.manifest != nil { + return r.manifest, nil + } + + // NOTE(jonjohnsonjr): We should never get here because the public entrypoints + // do type-checking via remote.Descriptor. I've left this here for tests that + // directly instantiate a remoteImage. + manifest, desc, err := r.fetchManifest(r.Ref, acceptableImageMediaTypes) + if err != nil { + return nil, err + } + + if r.descriptor == nil { + r.descriptor = desc + } + r.mediaType = desc.MediaType + r.manifest = manifest + return r.manifest, nil +} + +func (r *remoteImage) RawConfigFile() ([]byte, error) { + r.configLock.Lock() + defer r.configLock.Unlock() + if r.config != nil { + return r.config, nil + } + + m, err := partial.Manifest(r) + if err != nil { + return nil, err + } + + if m.Config.Data != nil { + if err := verify.Descriptor(m.Config); err != nil { + return nil, err + } + r.config = m.Config.Data + return r.config, nil + } + + body, err := r.fetchBlob(r.context, m.Config.Size, m.Config.Digest) + if err != nil { + return nil, err + } + defer body.Close() + + r.config, err = ioutil.ReadAll(body) + if err != nil { + return nil, err + } + return r.config, nil +} + +// Descriptor retains the original descriptor from an index manifest. +// See partial.Descriptor. +func (r *remoteImage) Descriptor() (*v1.Descriptor, error) { + // kind of a hack, but RawManifest does appropriate locking/memoization + // and makes sure r.descriptor is populated. + _, err := r.RawManifest() + return r.descriptor, err +} + +// remoteImageLayer implements partial.CompressedLayer +type remoteImageLayer struct { + ri *remoteImage + digest v1.Hash +} + +// Digest implements partial.CompressedLayer +func (rl *remoteImageLayer) Digest() (v1.Hash, error) { + return rl.digest, nil +} + +// Compressed implements partial.CompressedLayer +func (rl *remoteImageLayer) Compressed() (io.ReadCloser, error) { + urls := []url.URL{rl.ri.url("blobs", rl.digest.String())} + + // Add alternative layer sources from URLs (usually none). + d, err := partial.BlobDescriptor(rl, rl.digest) + if err != nil { + return nil, err + } + + if d.Data != nil { + return verify.ReadCloser(ioutil.NopCloser(bytes.NewReader(d.Data)), d.Size, d.Digest) + } + + // We don't want to log binary layers -- this can break terminals. + ctx := redact.NewContext(rl.ri.context, "omitting binary blobs from logs") + + for _, s := range d.URLs { + u, err := url.Parse(s) + if err != nil { + return nil, err + } + urls = append(urls, *u) + } + + // The lastErr for most pulls will be the same (the first error), but for + // foreign layers we'll want to surface the last one, since we try to pull + // from the registry first, which would often fail. + // TODO: Maybe we don't want to try pulling from the registry first? + var lastErr error + for _, u := range urls { + req, err := http.NewRequest(http.MethodGet, u.String(), nil) + if err != nil { + return nil, err + } + + resp, err := rl.ri.Client.Do(req.WithContext(ctx)) + if err != nil { + lastErr = err + continue + } + + if err := transport.CheckError(resp, http.StatusOK); err != nil { + resp.Body.Close() + lastErr = err + continue + } + + return verify.ReadCloser(resp.Body, d.Size, rl.digest) + } + + return nil, lastErr +} + +// Manifest implements partial.WithManifest so that we can use partial.BlobSize below. +func (rl *remoteImageLayer) Manifest() (*v1.Manifest, error) { + return partial.Manifest(rl.ri) +} + +// MediaType implements v1.Layer +func (rl *remoteImageLayer) MediaType() (types.MediaType, error) { + bd, err := partial.BlobDescriptor(rl, rl.digest) + if err != nil { + return "", err + } + + return bd.MediaType, nil +} + +// Size implements partial.CompressedLayer +func (rl *remoteImageLayer) Size() (int64, error) { + // Look up the size of this digest in the manifest to avoid a request. + return partial.BlobSize(rl, rl.digest) +} + +// ConfigFile implements partial.WithManifestAndConfigFile so that we can use partial.BlobToDiffID below. +func (rl *remoteImageLayer) ConfigFile() (*v1.ConfigFile, error) { + return partial.ConfigFile(rl.ri) +} + +// DiffID implements partial.WithDiffID so that we don't recompute a DiffID that we already have +// available in our ConfigFile. +func (rl *remoteImageLayer) DiffID() (v1.Hash, error) { + return partial.BlobToDiffID(rl, rl.digest) +} + +// Descriptor retains the original descriptor from an image manifest. +// See partial.Descriptor. +func (rl *remoteImageLayer) Descriptor() (*v1.Descriptor, error) { + return partial.BlobDescriptor(rl, rl.digest) +} + +// See partial.Exists. +func (rl *remoteImageLayer) Exists() (bool, error) { + return rl.ri.blobExists(rl.digest) +} + +// LayerByDigest implements partial.CompressedLayer +func (r *remoteImage) LayerByDigest(h v1.Hash) (partial.CompressedLayer, error) { + return &remoteImageLayer{ + ri: r, + digest: h, + }, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/index.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/index.go new file mode 100644 index 0000000000..9898579188 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/index.go @@ -0,0 +1,307 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package remote + +import ( + "bytes" + "fmt" + "sync" + + "github.com/google/go-containerregistry/internal/verify" + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +var acceptableIndexMediaTypes = []types.MediaType{ + types.DockerManifestList, + types.OCIImageIndex, +} + +// remoteIndex accesses an index from a remote registry +type remoteIndex struct { + fetcher + manifestLock sync.Mutex // Protects manifest + manifest []byte + mediaType types.MediaType + descriptor *v1.Descriptor +} + +// Index provides access to a remote index reference. +func Index(ref name.Reference, options ...Option) (v1.ImageIndex, error) { + desc, err := get(ref, acceptableIndexMediaTypes, options...) + if err != nil { + return nil, err + } + + return desc.ImageIndex() +} + +func (r *remoteIndex) MediaType() (types.MediaType, error) { + if string(r.mediaType) != "" { + return r.mediaType, nil + } + return types.DockerManifestList, nil +} + +func (r *remoteIndex) Digest() (v1.Hash, error) { + return partial.Digest(r) +} + +func (r *remoteIndex) Size() (int64, error) { + return partial.Size(r) +} + +func (r *remoteIndex) RawManifest() ([]byte, error) { + r.manifestLock.Lock() + defer r.manifestLock.Unlock() + if r.manifest != nil { + return r.manifest, nil + } + + // NOTE(jonjohnsonjr): We should never get here because the public entrypoints + // do type-checking via remote.Descriptor. I've left this here for tests that + // directly instantiate a remoteIndex. + manifest, desc, err := r.fetchManifest(r.Ref, acceptableIndexMediaTypes) + if err != nil { + return nil, err + } + + if r.descriptor == nil { + r.descriptor = desc + } + r.mediaType = desc.MediaType + r.manifest = manifest + return r.manifest, nil +} + +func (r *remoteIndex) IndexManifest() (*v1.IndexManifest, error) { + b, err := r.RawManifest() + if err != nil { + return nil, err + } + return v1.ParseIndexManifest(bytes.NewReader(b)) +} + +func (r *remoteIndex) Image(h v1.Hash) (v1.Image, error) { + desc, err := r.childByHash(h) + if err != nil { + return nil, err + } + + // Descriptor.Image will handle coercing nested indexes into an Image. + return desc.Image() +} + +// Descriptor retains the original descriptor from an index manifest. +// See partial.Descriptor. +func (r *remoteIndex) Descriptor() (*v1.Descriptor, error) { + // kind of a hack, but RawManifest does appropriate locking/memoization + // and makes sure r.descriptor is populated. + _, err := r.RawManifest() + return r.descriptor, err +} + +func (r *remoteIndex) ImageIndex(h v1.Hash) (v1.ImageIndex, error) { + desc, err := r.childByHash(h) + if err != nil { + return nil, err + } + return desc.ImageIndex() +} + +// Workaround for #819. +func (r *remoteIndex) Layer(h v1.Hash) (v1.Layer, error) { + index, err := r.IndexManifest() + if err != nil { + return nil, err + } + for _, childDesc := range index.Manifests { + if h == childDesc.Digest { + l, err := partial.CompressedToLayer(&remoteLayer{ + fetcher: r.fetcher, + digest: h, + }) + if err != nil { + return nil, err + } + return &MountableLayer{ + Layer: l, + Reference: r.Ref.Context().Digest(h.String()), + }, nil + } + } + return nil, fmt.Errorf("layer not found: %s", h) +} + +// Experiment with a better API for v1.ImageIndex. We might want to move this +// to partial? +func (r *remoteIndex) Manifests() ([]partial.Describable, error) { + m, err := r.IndexManifest() + if err != nil { + return nil, err + } + manifests := []partial.Describable{} + for _, desc := range m.Manifests { + switch { + case desc.MediaType.IsImage(): + img, err := r.Image(desc.Digest) + if err != nil { + return nil, err + } + manifests = append(manifests, img) + case desc.MediaType.IsIndex(): + idx, err := r.ImageIndex(desc.Digest) + if err != nil { + return nil, err + } + manifests = append(manifests, idx) + default: + layer, err := r.Layer(desc.Digest) + if err != nil { + return nil, err + } + manifests = append(manifests, layer) + } + } + + return manifests, nil +} + +func (r *remoteIndex) imageByPlatform(platform v1.Platform) (v1.Image, error) { + desc, err := r.childByPlatform(platform) + if err != nil { + return nil, err + } + + // Descriptor.Image will handle coercing nested indexes into an Image. + return desc.Image() +} + +// This naively matches the first manifest with matching platform attributes. +// +// We should probably use this instead: +// github.com/containerd/containerd/platforms +// +// But first we'd need to migrate to: +// github.com/opencontainers/image-spec/specs-go/v1 +func (r *remoteIndex) childByPlatform(platform v1.Platform) (*Descriptor, error) { + index, err := r.IndexManifest() + if err != nil { + return nil, err + } + for _, childDesc := range index.Manifests { + // If platform is missing from child descriptor, assume it's amd64/linux. + p := defaultPlatform + if childDesc.Platform != nil { + p = *childDesc.Platform + } + + if matchesPlatform(p, platform) { + return r.childDescriptor(childDesc, platform) + } + } + return nil, fmt.Errorf("no child with platform %+v in index %s", platform, r.Ref) +} + +func (r *remoteIndex) childByHash(h v1.Hash) (*Descriptor, error) { + index, err := r.IndexManifest() + if err != nil { + return nil, err + } + for _, childDesc := range index.Manifests { + if h == childDesc.Digest { + return r.childDescriptor(childDesc, defaultPlatform) + } + } + return nil, fmt.Errorf("no child with digest %s in index %s", h, r.Ref) +} + +// Convert one of this index's child's v1.Descriptor into a remote.Descriptor, with the given platform option. +func (r *remoteIndex) childDescriptor(child v1.Descriptor, platform v1.Platform) (*Descriptor, error) { + ref := r.Ref.Context().Digest(child.Digest.String()) + var ( + manifest []byte + err error + ) + if child.Data != nil { + if err := verify.Descriptor(child); err != nil { + return nil, err + } + manifest = child.Data + } else { + manifest, _, err = r.fetchManifest(ref, []types.MediaType{child.MediaType}) + if err != nil { + return nil, err + } + } + return &Descriptor{ + fetcher: fetcher{ + Ref: ref, + Client: r.Client, + context: r.context, + }, + Manifest: manifest, + Descriptor: child, + platform: platform, + }, nil +} + +// matchesPlatform checks if the given platform matches the required platforms. +// The given platform matches the required platform if +// - architecture and OS are identical. +// - OS version and variant are identical if provided. +// - features and OS features of the required platform are subsets of those of the given platform. +func matchesPlatform(given, required v1.Platform) bool { + // Required fields that must be identical. + if given.Architecture != required.Architecture || given.OS != required.OS { + return false + } + + // Optional fields that may be empty, but must be identical if provided. + if required.OSVersion != "" && given.OSVersion != required.OSVersion { + return false + } + if required.Variant != "" && given.Variant != required.Variant { + return false + } + + // Verify required platform's features are a subset of given platform's features. + if !isSubset(given.OSFeatures, required.OSFeatures) { + return false + } + if !isSubset(given.Features, required.Features) { + return false + } + + return true +} + +// isSubset checks if the required array of strings is a subset of the given lst. +func isSubset(lst, required []string) bool { + set := make(map[string]bool) + for _, value := range lst { + set[value] = true + } + + for _, value := range required { + if _, ok := set[value]; !ok { + return false + } + } + + return true +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/layer.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/layer.go new file mode 100644 index 0000000000..b2126f599d --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/layer.go @@ -0,0 +1,94 @@ +// Copyright 2019 Google LLC All Rights Reserved. +// +// 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. + +package remote + +import ( + "io" + + "github.com/google/go-containerregistry/internal/redact" + "github.com/google/go-containerregistry/internal/verify" + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +// remoteImagelayer implements partial.CompressedLayer +type remoteLayer struct { + fetcher + digest v1.Hash +} + +// Compressed implements partial.CompressedLayer +func (rl *remoteLayer) Compressed() (io.ReadCloser, error) { + // We don't want to log binary layers -- this can break terminals. + ctx := redact.NewContext(rl.context, "omitting binary blobs from logs") + return rl.fetchBlob(ctx, verify.SizeUnknown, rl.digest) +} + +// Compressed implements partial.CompressedLayer +func (rl *remoteLayer) Size() (int64, error) { + resp, err := rl.headBlob(rl.digest) + if err != nil { + return -1, err + } + defer resp.Body.Close() + return resp.ContentLength, nil +} + +// Digest implements partial.CompressedLayer +func (rl *remoteLayer) Digest() (v1.Hash, error) { + return rl.digest, nil +} + +// MediaType implements v1.Layer +func (rl *remoteLayer) MediaType() (types.MediaType, error) { + return types.DockerLayer, nil +} + +// See partial.Exists. +func (rl *remoteLayer) Exists() (bool, error) { + return rl.blobExists(rl.digest) +} + +// Layer reads the given blob reference from a registry as a Layer. A blob +// reference here is just a punned name.Digest where the digest portion is the +// digest of the blob to be read and the repository portion is the repo where +// that blob lives. +func Layer(ref name.Digest, options ...Option) (v1.Layer, error) { + o, err := makeOptions(ref.Context(), options...) + if err != nil { + return nil, err + } + f, err := makeFetcher(ref, o) + if err != nil { + return nil, err + } + h, err := v1.NewHash(ref.Identifier()) + if err != nil { + return nil, err + } + l, err := partial.CompressedToLayer(&remoteLayer{ + fetcher: *f, + digest: h, + }) + if err != nil { + return nil, err + } + return &MountableLayer{ + Layer: l, + Reference: ref, + }, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/list.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/list.go new file mode 100644 index 0000000000..e643c49aab --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/list.go @@ -0,0 +1,141 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package remote + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + "strings" + + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" +) + +type tags struct { + Name string `json:"name"` + Tags []string `json:"tags"` +} + +// ListWithContext calls List with the given context. +// +// Deprecated: Use List and WithContext. This will be removed in a future release. +func ListWithContext(ctx context.Context, repo name.Repository, options ...Option) ([]string, error) { + return List(repo, append(options, WithContext(ctx))...) +} + +// List calls /tags/list for the given repository, returning the list of tags +// in the "tags" property. +func List(repo name.Repository, options ...Option) ([]string, error) { + o, err := makeOptions(repo, options...) + if err != nil { + return nil, err + } + scopes := []string{repo.Scope(transport.PullScope)} + tr, err := transport.NewWithContext(o.context, repo.Registry, o.auth, o.transport, scopes) + if err != nil { + return nil, err + } + + uri := &url.URL{ + Scheme: repo.Registry.Scheme(), + Host: repo.Registry.RegistryStr(), + Path: fmt.Sprintf("/v2/%s/tags/list", repo.RepositoryStr()), + } + + if o.pageSize > 0 { + uri.RawQuery = fmt.Sprintf("n=%d", o.pageSize) + } + + client := http.Client{Transport: tr} + tagList := []string{} + parsed := tags{} + + // get responses until there is no next page + for { + select { + case <-o.context.Done(): + return nil, o.context.Err() + default: + } + + req, err := http.NewRequestWithContext(o.context, "GET", uri.String(), nil) + if err != nil { + return nil, err + } + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + + if err := transport.CheckError(resp, http.StatusOK); err != nil { + return nil, err + } + + if err := json.NewDecoder(resp.Body).Decode(&parsed); err != nil { + return nil, err + } + + if err := resp.Body.Close(); err != nil { + return nil, err + } + + tagList = append(tagList, parsed.Tags...) + + uri, err = getNextPageURL(resp) + if err != nil { + return nil, err + } + // no next page + if uri == nil { + break + } + } + + return tagList, nil +} + +// getNextPageURL checks if there is a Link header in a http.Response which +// contains a link to the next page. If yes it returns the url.URL of the next +// page otherwise it returns nil. +func getNextPageURL(resp *http.Response) (*url.URL, error) { + link := resp.Header.Get("Link") + if link == "" { + return nil, nil + } + + if link[0] != '<' { + return nil, fmt.Errorf("failed to parse link header: missing '<' in: %s", link) + } + + end := strings.Index(link, ">") + if end == -1 { + return nil, fmt.Errorf("failed to parse link header: missing '>' in: %s", link) + } + link = link[1:end] + + linkURL, err := url.Parse(link) + if err != nil { + return nil, err + } + if resp.Request == nil || resp.Request.URL == nil { + return nil, nil + } + linkURL = resp.Request.URL.ResolveReference(linkURL) + return linkURL, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/mount.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/mount.go new file mode 100644 index 0000000000..728997044c --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/mount.go @@ -0,0 +1,95 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package remote + +import ( + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" +) + +// MountableLayer wraps a v1.Layer in a shim that enables the layer to be +// "mounted" when published to another registry. +type MountableLayer struct { + v1.Layer + + Reference name.Reference +} + +// Descriptor retains the original descriptor from an image manifest. +// See partial.Descriptor. +func (ml *MountableLayer) Descriptor() (*v1.Descriptor, error) { + return partial.Descriptor(ml.Layer) +} + +// Exists is a hack. See partial.Exists. +func (ml *MountableLayer) Exists() (bool, error) { + return partial.Exists(ml.Layer) +} + +// mountableImage wraps the v1.Layer references returned by the embedded v1.Image +// in MountableLayer's so that remote.Write might attempt to mount them from their +// source repository. +type mountableImage struct { + v1.Image + + Reference name.Reference +} + +// Layers implements v1.Image +func (mi *mountableImage) Layers() ([]v1.Layer, error) { + ls, err := mi.Image.Layers() + if err != nil { + return nil, err + } + mls := make([]v1.Layer, 0, len(ls)) + for _, l := range ls { + mls = append(mls, &MountableLayer{ + Layer: l, + Reference: mi.Reference, + }) + } + return mls, nil +} + +// LayerByDigest implements v1.Image +func (mi *mountableImage) LayerByDigest(d v1.Hash) (v1.Layer, error) { + l, err := mi.Image.LayerByDigest(d) + if err != nil { + return nil, err + } + return &MountableLayer{ + Layer: l, + Reference: mi.Reference, + }, nil +} + +// LayerByDiffID implements v1.Image +func (mi *mountableImage) LayerByDiffID(d v1.Hash) (v1.Layer, error) { + l, err := mi.Image.LayerByDiffID(d) + if err != nil { + return nil, err + } + return &MountableLayer{ + Layer: l, + Reference: mi.Reference, + }, nil +} + +// Descriptor retains the original descriptor from an index manifest. +// See partial.Descriptor. +func (mi *mountableImage) Descriptor() (*v1.Descriptor, error) { + return partial.Descriptor(mi.Image) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/multi_write.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/multi_write.go new file mode 100644 index 0000000000..7e41d94c43 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/multi_write.go @@ -0,0 +1,303 @@ +// Copyright 2020 Google LLC All Rights Reserved. +// +// 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. + +package remote + +import ( + "context" + "fmt" + "net/http" + + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" + "github.com/google/go-containerregistry/pkg/v1/types" + "golang.org/x/sync/errgroup" +) + +// MultiWrite writes the given Images or ImageIndexes to the given refs, as +// efficiently as possible, by deduping shared layer blobs and uploading layers +// in parallel, then uploading all manifests in parallel. +// +// Current limitations: +// - All refs must share the same repository. +// - Images cannot consist of stream.Layers. +func MultiWrite(m map[name.Reference]Taggable, options ...Option) (rerr error) { + // Determine the repository being pushed to; if asked to push to + // multiple repositories, give up. + var repo, zero name.Repository + for ref := range m { + if repo == zero { + repo = ref.Context() + } else if ref.Context() != repo { + return fmt.Errorf("MultiWrite can only push to the same repository (saw %q and %q)", repo, ref.Context()) + } + } + + o, err := makeOptions(repo, options...) + if err != nil { + return err + } + + // Collect unique blobs (layers and config blobs). + blobs := map[v1.Hash]v1.Layer{} + newManifests := []map[name.Reference]Taggable{} + // Separate originally requested images and indexes, so we can push images first. + images, indexes := map[name.Reference]Taggable{}, map[name.Reference]Taggable{} + for ref, i := range m { + if img, ok := i.(v1.Image); ok { + images[ref] = i + if err := addImageBlobs(img, blobs, o.allowNondistributableArtifacts); err != nil { + return err + } + continue + } + if idx, ok := i.(v1.ImageIndex); ok { + indexes[ref] = i + newManifests, err = addIndexBlobs(idx, blobs, repo, newManifests, 0, o.allowNondistributableArtifacts) + if err != nil { + return err + } + continue + } + return fmt.Errorf("pushable resource was not Image or ImageIndex: %T", i) + } + + // Determine if any of the layers are Mountable, because if so we need + // to request Pull scope too. + ls := []v1.Layer{} + for _, l := range blobs { + ls = append(ls, l) + } + scopes := scopesForUploadingImage(repo, ls) + tr, err := transport.NewWithContext(o.context, repo.Registry, o.auth, o.transport, scopes) + if err != nil { + return err + } + w := writer{ + repo: repo, + client: &http.Client{Transport: tr}, + context: o.context, + updates: o.updates, + lastUpdate: &v1.Update{}, + backoff: o.retryBackoff, + predicate: o.retryPredicate, + } + + // Collect the total size of blobs and manifests we're about to write. + if o.updates != nil { + defer close(o.updates) + defer func() { _ = sendError(o.updates, rerr) }() + for _, b := range blobs { + size, err := b.Size() + if err != nil { + return err + } + w.lastUpdate.Total += size + } + countManifest := func(t Taggable) error { + b, err := t.RawManifest() + if err != nil { + return err + } + w.lastUpdate.Total += int64(len(b)) + return nil + } + for _, i := range images { + if err := countManifest(i); err != nil { + return err + } + } + for _, nm := range newManifests { + for _, i := range nm { + if err := countManifest(i); err != nil { + return err + } + } + } + for _, i := range indexes { + if err := countManifest(i); err != nil { + return err + } + } + } + + // Upload individual blobs and collect any errors. + blobChan := make(chan v1.Layer, 2*o.jobs) + ctx := o.context + g, gctx := errgroup.WithContext(o.context) + for i := 0; i < o.jobs; i++ { + // Start N workers consuming blobs to upload. + g.Go(func() error { + for b := range blobChan { + if err := w.uploadOne(gctx, b); err != nil { + return err + } + } + return nil + }) + } + g.Go(func() error { + defer close(blobChan) + for _, b := range blobs { + select { + case blobChan <- b: + case <-gctx.Done(): + return gctx.Err() + } + } + return nil + }) + if err := g.Wait(); err != nil { + return err + } + + commitMany := func(ctx context.Context, m map[name.Reference]Taggable) error { + g, ctx := errgroup.WithContext(ctx) + // With all of the constituent elements uploaded, upload the manifests + // to commit the images and indexes, and collect any errors. + type task struct { + i Taggable + ref name.Reference + } + taskChan := make(chan task, 2*o.jobs) + for i := 0; i < o.jobs; i++ { + // Start N workers consuming tasks to upload manifests. + g.Go(func() error { + for t := range taskChan { + if err := w.commitManifest(ctx, t.i, t.ref); err != nil { + return err + } + } + return nil + }) + } + go func() { + for ref, i := range m { + taskChan <- task{i, ref} + } + close(taskChan) + }() + return g.Wait() + } + // Push originally requested image manifests. These have no + // dependencies. + if err := commitMany(ctx, images); err != nil { + return err + } + // Push new manifests from lowest levels up. + for i := len(newManifests) - 1; i >= 0; i-- { + if err := commitMany(ctx, newManifests[i]); err != nil { + return err + } + } + // Push originally requested index manifests, which might depend on + // newly discovered manifests. + + return commitMany(ctx, indexes) +} + +// addIndexBlobs adds blobs to the set of blobs we intend to upload, and +// returns the latest copy of the ordered collection of manifests to upload. +func addIndexBlobs(idx v1.ImageIndex, blobs map[v1.Hash]v1.Layer, repo name.Repository, newManifests []map[name.Reference]Taggable, lvl int, allowNondistributableArtifacts bool) ([]map[name.Reference]Taggable, error) { + if lvl > len(newManifests)-1 { + newManifests = append(newManifests, map[name.Reference]Taggable{}) + } + + im, err := idx.IndexManifest() + if err != nil { + return nil, err + } + for _, desc := range im.Manifests { + switch desc.MediaType { + case types.OCIImageIndex, types.DockerManifestList: + idx, err := idx.ImageIndex(desc.Digest) + if err != nil { + return nil, err + } + newManifests, err = addIndexBlobs(idx, blobs, repo, newManifests, lvl+1, allowNondistributableArtifacts) + if err != nil { + return nil, err + } + + // Also track the sub-index manifest to upload later by digest. + newManifests[lvl][repo.Digest(desc.Digest.String())] = idx + case types.OCIManifestSchema1, types.DockerManifestSchema2: + img, err := idx.Image(desc.Digest) + if err != nil { + return nil, err + } + if err := addImageBlobs(img, blobs, allowNondistributableArtifacts); err != nil { + return nil, err + } + + // Also track the sub-image manifest to upload later by digest. + newManifests[lvl][repo.Digest(desc.Digest.String())] = img + default: + // Workaround for #819. + if wl, ok := idx.(withLayer); ok { + layer, err := wl.Layer(desc.Digest) + if err != nil { + return nil, err + } + if err := addLayerBlob(layer, blobs, allowNondistributableArtifacts); err != nil { + return nil, err + } + } else { + return nil, fmt.Errorf("unknown media type: %v", desc.MediaType) + } + } + } + return newManifests, nil +} + +func addLayerBlob(l v1.Layer, blobs map[v1.Hash]v1.Layer, allowNondistributableArtifacts bool) error { + // Ignore foreign layers. + mt, err := l.MediaType() + if err != nil { + return err + } + + if mt.IsDistributable() || allowNondistributableArtifacts { + d, err := l.Digest() + if err != nil { + return err + } + + blobs[d] = l + } + + return nil +} + +func addImageBlobs(img v1.Image, blobs map[v1.Hash]v1.Layer, allowNondistributableArtifacts bool) error { + ls, err := img.Layers() + if err != nil { + return err + } + // Collect all layers. + for _, l := range ls { + if err := addLayerBlob(l, blobs, allowNondistributableArtifacts); err != nil { + return err + } + } + + // Collect config blob. + cl, err := partial.ConfigLayer(img) + if err != nil { + return err + } + return addLayerBlob(cl, blobs, allowNondistributableArtifacts) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/options.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/options.go new file mode 100644 index 0000000000..8bfd06d78f --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/options.go @@ -0,0 +1,292 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package remote + +import ( + "context" + "errors" + "io" + "net" + "net/http" + "syscall" + "time" + + "github.com/google/go-containerregistry/internal/retry" + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/logs" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" +) + +// Option is a functional option for remote operations. +type Option func(*options) error + +type options struct { + auth authn.Authenticator + keychain authn.Keychain + transport http.RoundTripper + platform v1.Platform + context context.Context + jobs int + userAgent string + allowNondistributableArtifacts bool + updates chan<- v1.Update + pageSize int + retryBackoff Backoff + retryPredicate retry.Predicate +} + +var defaultPlatform = v1.Platform{ + Architecture: "amd64", + OS: "linux", +} + +// Backoff is an alias of retry.Backoff to expose this configuration option to consumers of this lib +type Backoff = retry.Backoff + +var defaultRetryPredicate retry.Predicate = func(err error) bool { + // Various failure modes here, as we're often reading from and writing to + // the network. + if retry.IsTemporary(err) || errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.EOF) || errors.Is(err, syscall.EPIPE) { + logs.Warn.Printf("retrying %v", err) + return true + } + return false +} + +// Try this three times, waiting 1s after first failure, 3s after second. +var defaultRetryBackoff = Backoff{ + Duration: 1.0 * time.Second, + Factor: 3.0, + Jitter: 0.1, + Steps: 3, +} + +const ( + defaultJobs = 4 + + // ECR returns an error if n > 1000: + // https://github.com/google/go-containerregistry/issues/1091 + defaultPageSize = 1000 +) + +// DefaultTransport is based on http.DefaultTransport with modifications +// documented inline below. +var DefaultTransport = &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + // By default we wrap the transport in retries, so reduce the + // default dial timeout to 5s to avoid 5x 30s of connection + // timeouts when doing the "ping" on certain http registries. + Timeout: 5 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + ForceAttemptHTTP2: true, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, +} + +func makeOptions(target authn.Resource, opts ...Option) (*options, error) { + o := &options{ + transport: DefaultTransport, + platform: defaultPlatform, + context: context.Background(), + jobs: defaultJobs, + pageSize: defaultPageSize, + retryPredicate: defaultRetryPredicate, + retryBackoff: defaultRetryBackoff, + } + + for _, option := range opts { + if err := option(o); err != nil { + return nil, err + } + } + + switch { + case o.auth != nil && o.keychain != nil: + // It is a better experience to explicitly tell a caller their auth is misconfigured + // than potentially fail silently when the correct auth is overridden by option misuse. + return nil, errors.New("provide an option for either authn.Authenticator or authn.Keychain, not both") + case o.keychain != nil: + auth, err := o.keychain.Resolve(target) + if err != nil { + return nil, err + } + o.auth = auth + case o.auth == nil: + o.auth = authn.Anonymous + } + + // transport.Wrapper is a signal that consumers are opt-ing into providing their own transport without any additional wrapping. + // This is to allow consumers full control over the transports logic, such as providing retry logic. + if _, ok := o.transport.(*transport.Wrapper); !ok { + // Wrap the transport in something that logs requests and responses. + // It's expensive to generate the dumps, so skip it if we're writing + // to nothing. + if logs.Enabled(logs.Debug) { + o.transport = transport.NewLogger(o.transport) + } + + // Wrap the transport in something that can retry network flakes. + o.transport = transport.NewRetry(o.transport) + + // Wrap this last to prevent transport.New from double-wrapping. + if o.userAgent != "" { + o.transport = transport.NewUserAgent(o.transport, o.userAgent) + } + } + + return o, nil +} + +// WithTransport is a functional option for overriding the default transport +// for remote operations. +// If transport.Wrapper is provided, this signals that the consumer does *not* want any further wrapping to occur. +// i.e. logging, retry and useragent +// +// The default transport is DefaultTransport. +func WithTransport(t http.RoundTripper) Option { + return func(o *options) error { + o.transport = t + return nil + } +} + +// WithAuth is a functional option for overriding the default authenticator +// for remote operations. +// It is an error to use both WithAuth and WithAuthFromKeychain in the same Option set. +// +// The default authenticator is authn.Anonymous. +func WithAuth(auth authn.Authenticator) Option { + return func(o *options) error { + o.auth = auth + return nil + } +} + +// WithAuthFromKeychain is a functional option for overriding the default +// authenticator for remote operations, using an authn.Keychain to find +// credentials. +// It is an error to use both WithAuth and WithAuthFromKeychain in the same Option set. +// +// The default authenticator is authn.Anonymous. +func WithAuthFromKeychain(keys authn.Keychain) Option { + return func(o *options) error { + o.keychain = keys + return nil + } +} + +// WithPlatform is a functional option for overriding the default platform +// that Image and Descriptor.Image use for resolving an index to an image. +// +// The default platform is amd64/linux. +func WithPlatform(p v1.Platform) Option { + return func(o *options) error { + o.platform = p + return nil + } +} + +// WithContext is a functional option for setting the context in http requests +// performed by a given function. Note that this context is used for _all_ +// http requests, not just the initial volley. E.g., for remote.Image, the +// context will be set on http requests generated by subsequent calls to +// RawConfigFile() and even methods on layers returned by Layers(). +// +// The default context is context.Background(). +func WithContext(ctx context.Context) Option { + return func(o *options) error { + o.context = ctx + return nil + } +} + +// WithJobs is a functional option for setting the parallelism of remote +// operations performed by a given function. Note that not all remote +// operations support parallelism. +// +// The default value is 4. +func WithJobs(jobs int) Option { + return func(o *options) error { + if jobs <= 0 { + return errors.New("jobs must be greater than zero") + } + o.jobs = jobs + return nil + } +} + +// WithUserAgent adds the given string to the User-Agent header for any HTTP +// requests. This header will also include "go-containerregistry/${version}". +// +// If you want to completely overwrite the User-Agent header, use WithTransport. +func WithUserAgent(ua string) Option { + return func(o *options) error { + o.userAgent = ua + return nil + } +} + +// WithNondistributable includes non-distributable (foreign) layers +// when writing images, see: +// https://github.com/opencontainers/image-spec/blob/master/layer.md#non-distributable-layers +// +// The default behaviour is to skip these layers +func WithNondistributable(o *options) error { + o.allowNondistributableArtifacts = true + return nil +} + +// WithProgress takes a channel that will receive progress updates as bytes are written. +// +// Sending updates to an unbuffered channel will block writes, so callers +// should provide a buffered channel to avoid potential deadlocks. +func WithProgress(updates chan<- v1.Update) Option { + return func(o *options) error { + o.updates = updates + return nil + } +} + +// WithPageSize sets the given size as the value of parameter 'n' in the request. +// +// To omit the `n` parameter entirely, use WithPageSize(0). +// The default value is 1000. +func WithPageSize(size int) Option { + return func(o *options) error { + o.pageSize = size + return nil + } +} + +// WithRetryBackoff sets the httpBackoff for retry HTTP operations. +func WithRetryBackoff(backoff Backoff) Option { + return func(o *options) error { + o.retryBackoff = backoff + return nil + } +} + +// WithRetryPredicate sets the predicate for retry HTTP operations. +func WithRetryPredicate(predicate retry.Predicate) Option { + return func(o *options) error { + o.retryPredicate = predicate + return nil + } +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/README.md b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/README.md new file mode 100644 index 0000000000..bd4d957b0e --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/README.md @@ -0,0 +1,129 @@ +# `transport` + +[![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/transport?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/transport) + +The [distribution protocol](https://github.com/opencontainers/distribution-spec) is fairly simple, but correctly [implementing authentication](../../../authn/README.md) is **hard**. + +This package [implements](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote/transport#New) an [`http.RoundTripper`](https://godoc.org/net/http#RoundTripper) +that transparently performs: +* [Token +Authentication](https://docs.docker.com/registry/spec/auth/token/) and +* [OAuth2 +Authentication](https://docs.docker.com/registry/spec/auth/oauth/) + +for registry clients. + +## Raison d'être + +> Why not just use the [`docker/distribution`](https://godoc.org/github.com/docker/distribution/registry/client/auth) client? + +Great question! Mostly, because I don't want to depend on [`prometheus/client_golang`](https://github.com/prometheus/client_golang). + +As a performance optimization, that client uses [a cache](https://github.com/docker/distribution/blob/a8371794149d1d95f1e846744b05c87f2f825e5a/registry/client/repository.go#L173) to keep track of a mapping between blob digests and their [descriptors](https://github.com/docker/distribution/blob/a8371794149d1d95f1e846744b05c87f2f825e5a/blobs.go#L57-L86). Unfortunately, the cache [uses prometheus](https://github.com/docker/distribution/blob/a8371794149d1d95f1e846744b05c87f2f825e5a/registry/storage/cache/cachedblobdescriptorstore.go#L44) to track hits and misses, so if you want to use that client you have to pull in all of prometheus, which is pretty large. + +![docker/distribution](../../../../images/docker.dot.svg) + +> Why does it matter if you depend on prometheus? Who cares? + +It's generally polite to your downstream to reduce the number of dependencies your package requires: + +* Downloading your package is faster, which helps our Australian friends and people on airplanes. +* There is less code to compile, which speeds up builds and saves the planet from global warming. +* You reduce the likelihood of inflicting dependency hell upon your consumers. +* [Tim Hockin](https://twitter.com/thockin/status/958606077456654336) prefers it based on his experience working on Kubernetes, and he's a pretty smart guy. + +> Okay, what about [`containerd/containerd`](https://godoc.org/github.com/containerd/containerd/remotes/docker)? + +Similar reasons! That ends up pulling in grpc, protobuf, and logrus. + +![containerd/containerd](../../../../images/containerd.dot.svg) + +> Well... what about [`containers/image`](https://godoc.org/github.com/containers/image/docker)? + +That just uses the the `docker/distribution` client... and more! + +![containers/image](../../../../images/containers.dot.svg) + +> Wow, what about this package? + +Of course, this package isn't perfect either. `transport` depends on `authn`, +which in turn depends on docker's config file parsing and handling package, +which you don't strictly need but almost certainly want if you're going to be +interacting with a registry. + +![google/go-containerregistry](../../../../images/ggcr.dot.svg) + +*These graphs were generated by +[`kisielk/godepgraph`](https://github.com/kisielk/godepgraph).* + +## Usage + +This is heavily used by the +[`remote`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote) +package, which implements higher level image-centric functionality, but this +package is useful if you want to interact directly with the registry to do +something that `remote` doesn't support, e.g. [to handle with schema 1 +images](https://github.com/google/go-containerregistry/pull/509). + +This package also includes some [error +handling](https://github.com/opencontainers/distribution-spec/blob/60be706c34ee7805bdd1d3d11affec53b0dfb8fb/spec.md#errors) +facilities in the form of +[`CheckError`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote/transport#CheckError), +which will parse the response body into a structured error for unexpected http +status codes. + +Here's a "simple" program that writes the result of +[listing tags](https://github.com/opencontainers/distribution-spec/blob/60be706c34ee7805bdd1d3d11affec53b0dfb8fb/spec.md#tags) +for [`gcr.io/google-containers/pause`](https://gcr.io/google-containers/pause) +to stdout. + +```go +package main + +import ( + "io" + "net/http" + "os" + + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" +) + +func main() { + repo, err := name.NewRepository("gcr.io/google-containers/pause") + if err != nil { + panic(err) + } + + // Fetch credentials based on your docker config file, which is $HOME/.docker/config.json or $DOCKER_CONFIG. + auth, err := authn.DefaultKeychain.Resolve(repo.Registry) + if err != nil { + panic(err) + } + + // Construct an http.Client that is authorized to pull from gcr.io/google-containers/pause. + scopes := []string{repo.Scope(transport.PullScope)} + t, err := transport.New(repo.Registry, auth, http.DefaultTransport, scopes) + if err != nil { + panic(err) + } + client := &http.Client{Transport: t} + + // Make the actual request. + resp, err := client.Get("https://gcr.io/v2/google-containers/pause/tags/list") + if err != nil { + panic(err) + } + + // Assert that we get a 200, otherwise attempt to parse body as a structured error. + if err := transport.CheckError(resp, http.StatusOK); err != nil { + panic(err) + } + + // Write the response to stdout. + if _, err := io.Copy(os.Stdout, resp.Body); err != nil { + panic(err) + } +} +``` diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/basic.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/basic.go new file mode 100644 index 0000000000..fdb362b762 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/basic.go @@ -0,0 +1,62 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package transport + +import ( + "encoding/base64" + "fmt" + "net/http" + + "github.com/google/go-containerregistry/pkg/authn" +) + +type basicTransport struct { + inner http.RoundTripper + auth authn.Authenticator + target string +} + +var _ http.RoundTripper = (*basicTransport)(nil) + +// RoundTrip implements http.RoundTripper +func (bt *basicTransport) RoundTrip(in *http.Request) (*http.Response, error) { + if bt.auth != authn.Anonymous { + auth, err := bt.auth.Authorization() + if err != nil { + return nil, err + } + + // http.Client handles redirects at a layer above the http.RoundTripper + // abstraction, so to avoid forwarding Authorization headers to places + // we are redirected, only set it when the authorization header matches + // the host with which we are interacting. + // In case of redirect http.Client can use an empty Host, check URL too. + if in.Host == bt.target || in.URL.Host == bt.target { + if bearer := auth.RegistryToken; bearer != "" { + hdr := fmt.Sprintf("Bearer %s", bearer) + in.Header.Set("Authorization", hdr) + } else if user, pass := auth.Username, auth.Password; user != "" && pass != "" { + delimited := fmt.Sprintf("%s:%s", user, pass) + encoded := base64.StdEncoding.EncodeToString([]byte(delimited)) + hdr := fmt.Sprintf("Basic %s", encoded) + in.Header.Set("Authorization", hdr) + } else if token := auth.Auth; token != "" { + hdr := fmt.Sprintf("Basic %s", token) + in.Header.Set("Authorization", hdr) + } + } + } + return bt.inner.RoundTrip(in) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/bearer.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/bearer.go new file mode 100644 index 0000000000..ce4e707261 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/bearer.go @@ -0,0 +1,311 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package transport + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net" + "net/http" + "net/url" + "strings" + + authchallenge "github.com/docker/distribution/registry/client/auth/challenge" + "github.com/google/go-containerregistry/internal/redact" + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/logs" + "github.com/google/go-containerregistry/pkg/name" +) + +type bearerTransport struct { + // Wrapped by bearerTransport. + inner http.RoundTripper + // Basic credentials that we exchange for bearer tokens. + basic authn.Authenticator + // Holds the bearer response from the token service. + bearer authn.AuthConfig + // Registry to which we send bearer tokens. + registry name.Registry + // See https://tools.ietf.org/html/rfc6750#section-3 + realm string + // See https://docs.docker.com/registry/spec/auth/token/ + service string + scopes []string + // Scheme we should use, determined by ping response. + scheme string +} + +var _ http.RoundTripper = (*bearerTransport)(nil) + +var portMap = map[string]string{ + "http": "80", + "https": "443", +} + +func stringSet(ss []string) map[string]struct{} { + set := make(map[string]struct{}) + for _, s := range ss { + set[s] = struct{}{} + } + return set +} + +// RoundTrip implements http.RoundTripper +func (bt *bearerTransport) RoundTrip(in *http.Request) (*http.Response, error) { + sendRequest := func() (*http.Response, error) { + // http.Client handles redirects at a layer above the http.RoundTripper + // abstraction, so to avoid forwarding Authorization headers to places + // we are redirected, only set it when the authorization header matches + // the registry with which we are interacting. + // In case of redirect http.Client can use an empty Host, check URL too. + if matchesHost(bt.registry, in, bt.scheme) { + hdr := fmt.Sprintf("Bearer %s", bt.bearer.RegistryToken) + in.Header.Set("Authorization", hdr) + } + return bt.inner.RoundTrip(in) + } + + res, err := sendRequest() + if err != nil { + return nil, err + } + + // If we hit a WWW-Authenticate challenge, it might be due to expired tokens or insufficient scope. + if challenges := authchallenge.ResponseChallenges(res); len(challenges) != 0 { + newScopes := []string{} + for _, wac := range challenges { + // TODO(jonjohnsonjr): Should we also update "realm" or "service"? + if want, ok := wac.Parameters["scope"]; ok { + // Add any scopes that we don't already request. + got := stringSet(bt.scopes) + if _, ok := got[want]; !ok { + newScopes = append(newScopes, want) + } + } + } + + // Some registries seem to only look at the first scope parameter during a token exchange. + // If a request fails because it's missing a scope, we should put those at the beginning, + // otherwise the registry might just ignore it :/ + newScopes = append(newScopes, bt.scopes...) + bt.scopes = newScopes + + // TODO(jonjohnsonjr): Teach transport.Error about "error" and "error_description" from challenge. + + // Retry the request to attempt to get a valid token. + if err = bt.refresh(in.Context()); err != nil { + return nil, err + } + return sendRequest() + } + + return res, err +} + +// It's unclear which authentication flow to use based purely on the protocol, +// so we rely on heuristics and fallbacks to support as many registries as possible. +// The basic token exchange is attempted first, falling back to the oauth flow. +// If the IdentityToken is set, this indicates that we should start with the oauth flow. +func (bt *bearerTransport) refresh(ctx context.Context) error { + auth, err := bt.basic.Authorization() + if err != nil { + return err + } + + if auth.RegistryToken != "" { + bt.bearer.RegistryToken = auth.RegistryToken + return nil + } + + var content []byte + if auth.IdentityToken != "" { + // If the secret being stored is an identity token, + // the Username should be set to , which indicates + // we are using an oauth flow. + content, err = bt.refreshOauth(ctx) + var terr *Error + if errors.As(err, &terr) && terr.StatusCode == http.StatusNotFound { + // Note: Not all token servers implement oauth2. + // If the request to the endpoint returns 404 using the HTTP POST method, + // refer to Token Documentation for using the HTTP GET method supported by all token servers. + content, err = bt.refreshBasic(ctx) + } + } else { + content, err = bt.refreshBasic(ctx) + } + if err != nil { + return err + } + + // Some registries don't have "token" in the response. See #54. + type tokenResponse struct { + Token string `json:"token"` + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` + // TODO: handle expiry? + } + + var response tokenResponse + if err := json.Unmarshal(content, &response); err != nil { + return err + } + + // Some registries set access_token instead of token. + if response.AccessToken != "" { + response.Token = response.AccessToken + } + + // Find a token to turn into a Bearer authenticator + if response.Token != "" { + bt.bearer.RegistryToken = response.Token + } else { + return fmt.Errorf("no token in bearer response:\n%s", content) + } + + // If we obtained a refresh token from the oauth flow, use that for refresh() now. + if response.RefreshToken != "" { + bt.basic = authn.FromConfig(authn.AuthConfig{ + IdentityToken: response.RefreshToken, + }) + } + + return nil +} + +func matchesHost(reg name.Registry, in *http.Request, scheme string) bool { + canonicalHeaderHost := canonicalAddress(in.Host, scheme) + canonicalURLHost := canonicalAddress(in.URL.Host, scheme) + canonicalRegistryHost := canonicalAddress(reg.RegistryStr(), scheme) + return canonicalHeaderHost == canonicalRegistryHost || canonicalURLHost == canonicalRegistryHost +} + +func canonicalAddress(host, scheme string) (address string) { + // The host may be any one of: + // - hostname + // - hostname:port + // - ipv4 + // - ipv4:port + // - ipv6 + // - [ipv6]:port + // As net.SplitHostPort returns an error if the host does not contain a port, we should only attempt + // to call it when we know that the address contains a port + if strings.Count(host, ":") == 1 || (strings.Count(host, ":") >= 2 && strings.Contains(host, "]:")) { + hostname, port, err := net.SplitHostPort(host) + if err != nil { + return host + } + if port == "" { + port = portMap[scheme] + } + + return net.JoinHostPort(hostname, port) + } + + return net.JoinHostPort(host, portMap[scheme]) +} + +// https://docs.docker.com/registry/spec/auth/oauth/ +func (bt *bearerTransport) refreshOauth(ctx context.Context) ([]byte, error) { + auth, err := bt.basic.Authorization() + if err != nil { + return nil, err + } + + u, err := url.Parse(bt.realm) + if err != nil { + return nil, err + } + + v := url.Values{} + v.Set("scope", strings.Join(bt.scopes, " ")) + v.Set("service", bt.service) + v.Set("client_id", defaultUserAgent) + if auth.IdentityToken != "" { + v.Set("grant_type", "refresh_token") + v.Set("refresh_token", auth.IdentityToken) + } else if auth.Username != "" && auth.Password != "" { + // TODO(#629): This is unreachable. + v.Set("grant_type", "password") + v.Set("username", auth.Username) + v.Set("password", auth.Password) + v.Set("access_type", "offline") + } + + client := http.Client{Transport: bt.inner} + req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(v.Encode())) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + // We don't want to log credentials. + ctx = redact.NewContext(ctx, "oauth token response contains credentials") + + resp, err := client.Do(req.WithContext(ctx)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if err := CheckError(resp, http.StatusOK); err != nil { + logs.Warn.Printf("No matching credentials were found for %q", bt.registry) + return nil, err + } + + return ioutil.ReadAll(resp.Body) +} + +// https://docs.docker.com/registry/spec/auth/token/ +func (bt *bearerTransport) refreshBasic(ctx context.Context) ([]byte, error) { + u, err := url.Parse(bt.realm) + if err != nil { + return nil, err + } + b := &basicTransport{ + inner: bt.inner, + auth: bt.basic, + target: u.Host, + } + client := http.Client{Transport: b} + + v := u.Query() + v["scope"] = bt.scopes + v.Set("service", bt.service) + u.RawQuery = v.Encode() + + req, err := http.NewRequest(http.MethodGet, u.String(), nil) + if err != nil { + return nil, err + } + + // We don't want to log credentials. + ctx = redact.NewContext(ctx, "basic token response contains credentials") + + resp, err := client.Do(req.WithContext(ctx)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if err := CheckError(resp, http.StatusOK); err != nil { + logs.Warn.Printf("No matching credentials were found for %q", bt.registry) + return nil, err + } + + return ioutil.ReadAll(resp.Body) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/doc.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/doc.go new file mode 100644 index 0000000000..ff7025b5c0 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/doc.go @@ -0,0 +1,18 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +// Package transport provides facilities for setting up an authenticated +// http.RoundTripper given an Authenticator and base RoundTripper. See +// transport.New for more information. +package transport diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/error.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/error.go new file mode 100644 index 0000000000..b94b180ccf --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/error.go @@ -0,0 +1,205 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package transport + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +// The set of query string keys that we expect to send as part of the registry +// protocol. Anything else is potentially dangerous to leak, as it's probably +// from a redirect. These redirects often included tokens or signed URLs. +var paramAllowlist = map[string]struct{}{ + // Token exchange + "scope": {}, + "service": {}, + // Cross-repo mounting + "mount": {}, + "from": {}, + // Layer PUT + "digest": {}, + // Listing tags and catalog + "n": {}, + "last": {}, +} + +// Error implements error to support the following error specification: +// https://github.com/docker/distribution/blob/master/docs/spec/api.md#errors +type Error struct { + Errors []Diagnostic `json:"errors,omitempty"` + // The http status code returned. + StatusCode int + // The request that failed. + Request *http.Request + // The raw body if we couldn't understand it. + rawBody string +} + +// Check that Error implements error +var _ error = (*Error)(nil) + +// Error implements error +func (e *Error) Error() string { + prefix := "" + if e.Request != nil { + prefix = fmt.Sprintf("%s %s: ", e.Request.Method, redactURL(e.Request.URL)) + } + return prefix + e.responseErr() +} + +func (e *Error) responseErr() string { + switch len(e.Errors) { + case 0: + if len(e.rawBody) == 0 { + if e.Request != nil && e.Request.Method == http.MethodHead { + return fmt.Sprintf("unexpected status code %d %s (HEAD responses have no body, use GET for details)", e.StatusCode, http.StatusText(e.StatusCode)) + } + return fmt.Sprintf("unexpected status code %d %s", e.StatusCode, http.StatusText(e.StatusCode)) + } + return fmt.Sprintf("unexpected status code %d %s: %s", e.StatusCode, http.StatusText(e.StatusCode), e.rawBody) + case 1: + return e.Errors[0].String() + default: + var errors []string + for _, d := range e.Errors { + errors = append(errors, d.String()) + } + return fmt.Sprintf("multiple errors returned: %s", + strings.Join(errors, "; ")) + } +} + +// Temporary returns whether the request that preceded the error is temporary. +func (e *Error) Temporary() bool { + if len(e.Errors) == 0 { + _, ok := temporaryStatusCodes[e.StatusCode] + return ok + } + for _, d := range e.Errors { + if _, ok := temporaryErrorCodes[d.Code]; !ok { + return false + } + } + return true +} + +// TODO(jonjohnsonjr): Consider moving to internal/redact. +func redactURL(original *url.URL) *url.URL { + qs := original.Query() + for k, v := range qs { + for i := range v { + if _, ok := paramAllowlist[k]; !ok { + // key is not in the Allowlist + v[i] = "REDACTED" + } + } + } + redacted := *original + redacted.RawQuery = qs.Encode() + return &redacted +} + +// Diagnostic represents a single error returned by a Docker registry interaction. +type Diagnostic struct { + Code ErrorCode `json:"code"` + Message string `json:"message,omitempty"` + Detail interface{} `json:"detail,omitempty"` +} + +// String stringifies the Diagnostic in the form: $Code: $Message[; $Detail] +func (d Diagnostic) String() string { + msg := fmt.Sprintf("%s: %s", d.Code, d.Message) + if d.Detail != nil { + msg = fmt.Sprintf("%s; %v", msg, d.Detail) + } + return msg +} + +// ErrorCode is an enumeration of supported error codes. +type ErrorCode string + +// The set of error conditions a registry may return: +// https://github.com/docker/distribution/blob/master/docs/spec/api.md#errors-2 +const ( + BlobUnknownErrorCode ErrorCode = "BLOB_UNKNOWN" + BlobUploadInvalidErrorCode ErrorCode = "BLOB_UPLOAD_INVALID" + BlobUploadUnknownErrorCode ErrorCode = "BLOB_UPLOAD_UNKNOWN" + DigestInvalidErrorCode ErrorCode = "DIGEST_INVALID" + ManifestBlobUnknownErrorCode ErrorCode = "MANIFEST_BLOB_UNKNOWN" + ManifestInvalidErrorCode ErrorCode = "MANIFEST_INVALID" + ManifestUnknownErrorCode ErrorCode = "MANIFEST_UNKNOWN" + ManifestUnverifiedErrorCode ErrorCode = "MANIFEST_UNVERIFIED" + NameInvalidErrorCode ErrorCode = "NAME_INVALID" + NameUnknownErrorCode ErrorCode = "NAME_UNKNOWN" + SizeInvalidErrorCode ErrorCode = "SIZE_INVALID" + TagInvalidErrorCode ErrorCode = "TAG_INVALID" + UnauthorizedErrorCode ErrorCode = "UNAUTHORIZED" + DeniedErrorCode ErrorCode = "DENIED" + UnsupportedErrorCode ErrorCode = "UNSUPPORTED" + TooManyRequestsErrorCode ErrorCode = "TOOMANYREQUESTS" + UnknownErrorCode ErrorCode = "UNKNOWN" + + // This isn't defined by either docker or OCI spec, but is defined by docker/distribution: + // https://github.com/distribution/distribution/blob/6a977a5a754baa213041443f841705888107362a/registry/api/errcode/register.go#L60 + UnavailableErrorCode ErrorCode = "UNAVAILABLE" +) + +// TODO: Include other error types. +var temporaryErrorCodes = map[ErrorCode]struct{}{ + BlobUploadInvalidErrorCode: {}, + TooManyRequestsErrorCode: {}, + UnknownErrorCode: {}, + UnavailableErrorCode: {}, +} + +var temporaryStatusCodes = map[int]struct{}{ + http.StatusRequestTimeout: {}, + http.StatusInternalServerError: {}, + http.StatusBadGateway: {}, + http.StatusServiceUnavailable: {}, + http.StatusGatewayTimeout: {}, +} + +// CheckError returns a structured error if the response status is not in codes. +func CheckError(resp *http.Response, codes ...int) error { + for _, code := range codes { + if resp.StatusCode == code { + // This is one of the supported status codes. + return nil + } + } + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + // https://github.com/docker/distribution/blob/master/docs/spec/api.md#errors + structuredError := &Error{} + + // This can fail if e.g. the response body is not valid JSON. That's fine, + // we'll construct an appropriate error string from the body and status code. + _ = json.Unmarshal(b, structuredError) + + structuredError.rawBody = string(b) + structuredError.StatusCode = resp.StatusCode + structuredError.Request = resp.Request + + return structuredError +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/logger.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/logger.go new file mode 100644 index 0000000000..c341f844e6 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/logger.go @@ -0,0 +1,91 @@ +// Copyright 2020 Google LLC All Rights Reserved. +// +// 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. + +package transport + +import ( + "fmt" + "net/http" + "net/http/httputil" + "time" + + "github.com/google/go-containerregistry/internal/redact" + "github.com/google/go-containerregistry/pkg/logs" +) + +type logTransport struct { + inner http.RoundTripper +} + +// NewLogger returns a transport that logs requests and responses to +// github.com/google/go-containerregistry/pkg/logs.Debug. +func NewLogger(inner http.RoundTripper) http.RoundTripper { + return &logTransport{inner} +} + +func (t *logTransport) RoundTrip(in *http.Request) (out *http.Response, err error) { + // Inspired by: github.com/motemen/go-loghttp + + // We redact token responses and binary blobs in response/request. + omitBody, reason := redact.FromContext(in.Context()) + if omitBody { + logs.Debug.Printf("--> %s %s [body redacted: %s]", in.Method, in.URL, reason) + } else { + logs.Debug.Printf("--> %s %s", in.Method, in.URL) + } + + // Save these headers so we can redact Authorization. + savedHeaders := in.Header.Clone() + if in.Header != nil && in.Header.Get("authorization") != "" { + in.Header.Set("authorization", "") + } + + b, err := httputil.DumpRequestOut(in, !omitBody) + if err == nil { + logs.Debug.Println(string(b)) + } else { + logs.Debug.Printf("Failed to dump request %s %s: %v", in.Method, in.URL, err) + } + + // Restore the non-redacted headers. + in.Header = savedHeaders + + start := time.Now() + out, err = t.inner.RoundTrip(in) + duration := time.Since(start) + if err != nil { + logs.Debug.Printf("<-- %v %s %s (%s)", err, in.Method, in.URL, duration) + } + if out != nil { + msg := fmt.Sprintf("<-- %d", out.StatusCode) + if out.Request != nil { + msg = fmt.Sprintf("%s %s", msg, out.Request.URL) + } + msg = fmt.Sprintf("%s (%s)", msg, duration) + + if omitBody { + msg = fmt.Sprintf("%s [body redacted: %s]", msg, reason) + } + + logs.Debug.Print(msg) + + b, err := httputil.DumpResponse(out, !omitBody) + if err == nil { + logs.Debug.Println(string(b)) + } else { + logs.Debug.Printf("Failed to dump response %s %s: %v", in.Method, in.URL, err) + } + } + return +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/ping.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/ping.go new file mode 100644 index 0000000000..29c36afe7c --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/ping.go @@ -0,0 +1,180 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package transport + +import ( + "context" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "strings" + + authchallenge "github.com/docker/distribution/registry/client/auth/challenge" + "github.com/google/go-containerregistry/pkg/name" +) + +type challenge string + +const ( + anonymous challenge = "anonymous" + basic challenge = "basic" + bearer challenge = "bearer" +) + +type pingResp struct { + challenge challenge + + // Following the challenge there are often key/value pairs + // e.g. Bearer service="gcr.io",realm="https://auth.gcr.io/v36/tokenz" + parameters map[string]string + + // The registry's scheme to use. Communicates whether we fell back to http. + scheme string +} + +func (c challenge) Canonical() challenge { + return challenge(strings.ToLower(string(c))) +} + +func parseChallenge(suffix string) map[string]string { + kv := make(map[string]string) + for _, token := range strings.Split(suffix, ",") { + // Trim any whitespace around each token. + token = strings.Trim(token, " ") + + // Break the token into a key/value pair + if parts := strings.SplitN(token, "=", 2); len(parts) == 2 { + // Unquote the value, if it is quoted. + kv[parts[0]] = strings.Trim(parts[1], `"`) + } else { + // If there was only one part, treat is as a key with an empty value + kv[token] = "" + } + } + return kv +} + +func ping(ctx context.Context, reg name.Registry, t http.RoundTripper) (*pingResp, error) { + client := http.Client{Transport: t} + + // This first attempts to use "https" for every request, falling back to http + // if the registry matches our localhost heuristic or if it is intentionally + // set to insecure via name.NewInsecureRegistry. + schemes := []string{"https"} + if reg.Scheme() == "http" { + schemes = append(schemes, "http") + } + + var errs []error + for _, scheme := range schemes { + url := fmt.Sprintf("%s://%s/v2/", scheme, reg.Name()) + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return nil, err + } + resp, err := client.Do(req.WithContext(ctx)) + if err != nil { + errs = append(errs, err) + // Potentially retry with http. + continue + } + defer func() { + // By draining the body, make sure to reuse the connection made by + // the ping for the following access to the registry + io.Copy(ioutil.Discard, resp.Body) + resp.Body.Close() + }() + + switch resp.StatusCode { + case http.StatusOK: + // If we get a 200, then no authentication is needed. + return &pingResp{ + challenge: anonymous, + scheme: scheme, + }, nil + case http.StatusUnauthorized: + if challenges := authchallenge.ResponseChallenges(resp); len(challenges) != 0 { + // If we hit more than one, let's try to find one that we know how to handle. + wac := pickFromMultipleChallenges(challenges) + return &pingResp{ + challenge: challenge(wac.Scheme).Canonical(), + parameters: wac.Parameters, + scheme: scheme, + }, nil + } + // Otherwise, just return the challenge without parameters. + return &pingResp{ + challenge: challenge(resp.Header.Get("WWW-Authenticate")).Canonical(), + scheme: scheme, + }, nil + default: + return nil, CheckError(resp, http.StatusOK, http.StatusUnauthorized) + } + } + return nil, multierrs(errs) +} + +func pickFromMultipleChallenges(challenges []authchallenge.Challenge) authchallenge.Challenge { + // It might happen there are multiple www-authenticate headers, e.g. `Negotiate` and `Basic`. + // Picking simply the first one could result eventually in `unrecognized challenge` error, + // that's why we're looping through the challenges in search for one that can be handled. + allowedSchemes := []string{"basic", "bearer"} + + for _, wac := range challenges { + currentScheme := strings.ToLower(wac.Scheme) + for _, allowed := range allowedSchemes { + if allowed == currentScheme { + return wac + } + } + } + + return challenges[0] +} + +type multierrs []error + +func (m multierrs) Error() string { + var b strings.Builder + hasWritten := false + for _, err := range m { + if hasWritten { + b.WriteString("; ") + } + hasWritten = true + b.WriteString(err.Error()) + } + return b.String() +} + +func (m multierrs) As(target interface{}) bool { + for _, err := range m { + if errors.As(err, target) { + return true + } + } + return false +} + +func (m multierrs) Is(target error) bool { + for _, err := range m { + if errors.Is(err, target) { + return true + } + } + return false +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/retry.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/retry.go new file mode 100644 index 0000000000..0a45dc75b9 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/retry.go @@ -0,0 +1,91 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package transport + +import ( + "net/http" + "time" + + "github.com/google/go-containerregistry/internal/retry" +) + +// Sleep for 0.1, 0.3, 0.9, 2.7 seconds. This should cover networking blips. +var defaultBackoff = retry.Backoff{ + Duration: 100 * time.Millisecond, + Factor: 3.0, + Jitter: 0.1, + Steps: 5, +} + +var _ http.RoundTripper = (*retryTransport)(nil) + +// retryTransport wraps a RoundTripper and retries temporary network errors. +type retryTransport struct { + inner http.RoundTripper + backoff retry.Backoff + predicate retry.Predicate +} + +// Option is a functional option for retryTransport. +type Option func(*options) + +type options struct { + backoff retry.Backoff + predicate retry.Predicate +} + +// Backoff is an alias of retry.Backoff to expose this configuration option to consumers of this lib +type Backoff = retry.Backoff + +// WithRetryBackoff sets the backoff for retry operations. +func WithRetryBackoff(backoff Backoff) Option { + return func(o *options) { + o.backoff = backoff + } +} + +// WithRetryPredicate sets the predicate for retry operations. +func WithRetryPredicate(predicate func(error) bool) Option { + return func(o *options) { + o.predicate = predicate + } +} + +// NewRetry returns a transport that retries errors. +func NewRetry(inner http.RoundTripper, opts ...Option) http.RoundTripper { + o := &options{ + backoff: defaultBackoff, + predicate: retry.IsTemporary, + } + + for _, opt := range opts { + opt(o) + } + + return &retryTransport{ + inner: inner, + backoff: o.backoff, + predicate: o.predicate, + } +} + +func (t *retryTransport) RoundTrip(in *http.Request) (out *http.Response, err error) { + roundtrip := func() error { + out, err = t.inner.RoundTrip(in) + return err + } + retry.Retry(roundtrip, t.predicate, t.backoff) + return +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/schemer.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/schemer.go new file mode 100644 index 0000000000..d70b6a850c --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/schemer.go @@ -0,0 +1,44 @@ +// Copyright 2019 Google LLC All Rights Reserved. +// +// 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. + +package transport + +import ( + "net/http" + + "github.com/google/go-containerregistry/pkg/name" +) + +type schemeTransport struct { + // Scheme we should use, determined by ping response. + scheme string + + // Registry we're talking to. + registry name.Registry + + // Wrapped by schemeTransport. + inner http.RoundTripper +} + +// RoundTrip implements http.RoundTripper +func (st *schemeTransport) RoundTrip(in *http.Request) (*http.Response, error) { + // When we ping() the registry, we determine whether to use http or https + // based on which scheme was successful. That is only valid for the + // registry server and not e.g. a separate token server or blob storage, + // so we should only override the scheme if the host is the registry. + if matchesHost(st.registry, in, st.scheme) { + in.URL.Scheme = st.scheme + } + return st.inner.RoundTrip(in) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/scope.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/scope.go new file mode 100644 index 0000000000..c3b56f7a41 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/scope.go @@ -0,0 +1,24 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package transport + +// Scopes suitable to qualify each Repository +const ( + PullScope string = "pull" + PushScope string = "push,pull" + // For now DELETE is PUSH, which is the read/write ACL. + DeleteScope string = PushScope + CatalogScope string = "catalog" +) diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/transport.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/transport.go new file mode 100644 index 0000000000..121904df3a --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/transport.go @@ -0,0 +1,112 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package transport + +import ( + "context" + "fmt" + "net/http" + + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" +) + +// New returns a new RoundTripper based on the provided RoundTripper that has been +// setup to authenticate with the remote registry "reg", in the capacity +// laid out by the specified scopes. +// +// TODO(jonjohnsonjr): Deprecate this. +func New(reg name.Registry, auth authn.Authenticator, t http.RoundTripper, scopes []string) (http.RoundTripper, error) { + return NewWithContext(context.Background(), reg, auth, t, scopes) +} + +// NewWithContext returns a new RoundTripper based on the provided RoundTripper that has been +// setup to authenticate with the remote registry "reg", in the capacity +// laid out by the specified scopes. +func NewWithContext(ctx context.Context, reg name.Registry, auth authn.Authenticator, t http.RoundTripper, scopes []string) (http.RoundTripper, error) { + // The handshake: + // 1. Use "t" to ping() the registry for the authentication challenge. + // + // 2a. If we get back a 200, then simply use "t". + // + // 2b. If we get back a 401 with a Basic challenge, then use a transport + // that just attachs auth each roundtrip. + // + // 2c. If we get back a 401 with a Bearer challenge, then use a transport + // that attaches a bearer token to each request, and refreshes is on 401s. + // Perform an initial refresh to seed the bearer token. + + // First we ping the registry to determine the parameters of the authentication handshake + // (if one is even necessary). + pr, err := ping(ctx, reg, t) + if err != nil { + return nil, err + } + + // Wrap t with a useragent transport unless we already have one. + if _, ok := t.(*userAgentTransport); !ok { + t = NewUserAgent(t, "") + } + + // Wrap t in a transport that selects the appropriate scheme based on the ping response. + t = &schemeTransport{ + scheme: pr.scheme, + registry: reg, + inner: t, + } + + switch pr.challenge.Canonical() { + case anonymous, basic: + return &Wrapper{&basicTransport{inner: t, auth: auth, target: reg.RegistryStr()}}, nil + case bearer: + // We require the realm, which tells us where to send our Basic auth to turn it into Bearer auth. + realm, ok := pr.parameters["realm"] + if !ok { + return nil, fmt.Errorf("malformed www-authenticate, missing realm: %v", pr.parameters) + } + service, ok := pr.parameters["service"] + if !ok { + // If the service parameter is not specified, then default it to the registry + // with which we are talking. + service = reg.String() + } + bt := &bearerTransport{ + inner: t, + basic: auth, + realm: realm, + registry: reg, + service: service, + scopes: scopes, + scheme: pr.scheme, + } + if err := bt.refresh(ctx); err != nil { + return nil, err + } + return &Wrapper{bt}, nil + default: + return nil, fmt.Errorf("unrecognized challenge: %s", pr.challenge) + } +} + +// Wrapper results in *not* wrapping supplied transport with additional logic such as retries, useragent and debug logging +// Consumers are opt-ing into providing their own transport without any additional wrapping. +type Wrapper struct { + inner http.RoundTripper +} + +// RoundTrip delegates to the inner RoundTripper +func (w *Wrapper) RoundTrip(in *http.Request) (*http.Response, error) { + return w.inner.RoundTrip(in) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/useragent.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/useragent.go new file mode 100644 index 0000000000..74a9e71bdf --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/useragent.go @@ -0,0 +1,94 @@ +// Copyright 2019 Google LLC All Rights Reserved. +// +// 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. + +package transport + +import ( + "fmt" + "net/http" + "runtime/debug" +) + +var ( + // Version can be set via: + // -ldflags="-X 'github.com/google/go-containerregistry/pkg/v1/remote/transport.Version=$TAG'" + Version string + + ggcrVersion = defaultUserAgent +) + +const ( + defaultUserAgent = "go-containerregistry" + moduleName = "github.com/google/go-containerregistry" +) + +type userAgentTransport struct { + inner http.RoundTripper + ua string +} + +func init() { + if v := version(); v != "" { + ggcrVersion = fmt.Sprintf("%s/%s", defaultUserAgent, v) + } +} + +func version() string { + if Version != "" { + // Version was set via ldflags, just return it. + return Version + } + + info, ok := debug.ReadBuildInfo() + if !ok { + return "" + } + + // Happens for crane and gcrane. + if info.Main.Path == moduleName { + return info.Main.Version + } + + // Anything else. + for _, dep := range info.Deps { + if dep.Path == moduleName { + return dep.Version + } + } + + return "" +} + +// NewUserAgent returns an http.Roundtripper that sets the user agent to +// The provided string plus additional go-containerregistry information, +// e.g. if provided "crane/v0.1.4" and this modules was built at v0.1.4: +// +// User-Agent: crane/v0.1.4 go-containerregistry/v0.1.4 +func NewUserAgent(inner http.RoundTripper, ua string) http.RoundTripper { + if ua == "" { + ua = ggcrVersion + } else { + ua = fmt.Sprintf("%s %s", ua, ggcrVersion) + } + return &userAgentTransport{ + inner: inner, + ua: ua, + } +} + +// RoundTrip implements http.RoundTripper +func (ut *userAgentTransport) RoundTrip(in *http.Request) (*http.Response, error) { + in.Header.Set("User-Agent", ut.ua) + return ut.inner.RoundTrip(in) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/remote/write.go b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/write.go new file mode 100644 index 0000000000..d412f953c3 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/remote/write.go @@ -0,0 +1,892 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package remote + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "sync/atomic" + + "github.com/google/go-containerregistry/internal/redact" + "github.com/google/go-containerregistry/internal/retry" + "github.com/google/go-containerregistry/pkg/logs" + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" + "github.com/google/go-containerregistry/pkg/v1/stream" + "github.com/google/go-containerregistry/pkg/v1/types" + "golang.org/x/sync/errgroup" +) + +// Taggable is an interface that enables a manifest PUT (e.g. for tagging). +type Taggable interface { + RawManifest() ([]byte, error) +} + +// Write pushes the provided img to the specified image reference. +func Write(ref name.Reference, img v1.Image, options ...Option) (rerr error) { + o, err := makeOptions(ref.Context(), options...) + if err != nil { + return err + } + + var lastUpdate *v1.Update + if o.updates != nil { + lastUpdate = &v1.Update{} + lastUpdate.Total, err = countImage(img, o.allowNondistributableArtifacts) + if err != nil { + return err + } + defer close(o.updates) + defer func() { _ = sendError(o.updates, rerr) }() + } + return writeImage(o.context, ref, img, o, lastUpdate) +} + +func writeImage(ctx context.Context, ref name.Reference, img v1.Image, o *options, lastUpdate *v1.Update) error { + ls, err := img.Layers() + if err != nil { + return err + } + scopes := scopesForUploadingImage(ref.Context(), ls) + tr, err := transport.NewWithContext(o.context, ref.Context().Registry, o.auth, o.transport, scopes) + if err != nil { + return err + } + w := writer{ + repo: ref.Context(), + client: &http.Client{Transport: tr}, + context: ctx, + updates: o.updates, + lastUpdate: lastUpdate, + backoff: o.retryBackoff, + predicate: o.retryPredicate, + } + + // Upload individual blobs and collect any errors. + blobChan := make(chan v1.Layer, 2*o.jobs) + g, gctx := errgroup.WithContext(ctx) + for i := 0; i < o.jobs; i++ { + // Start N workers consuming blobs to upload. + g.Go(func() error { + for b := range blobChan { + if err := w.uploadOne(gctx, b); err != nil { + return err + } + } + return nil + }) + } + + // Upload individual layers in goroutines and collect any errors. + // If we can dedupe by the layer digest, try to do so. If we can't determine + // the digest for whatever reason, we can't dedupe and might re-upload. + g.Go(func() error { + defer close(blobChan) + uploaded := map[v1.Hash]bool{} + for _, l := range ls { + l := l + + // Handle foreign layers. + mt, err := l.MediaType() + if err != nil { + return err + } + if !mt.IsDistributable() && !o.allowNondistributableArtifacts { + continue + } + + // Streaming layers calculate their digests while uploading them. Assume + // an error here indicates we need to upload the layer. + h, err := l.Digest() + if err == nil { + // If we can determine the layer's digest ahead of + // time, use it to dedupe uploads. + if uploaded[h] { + continue // Already uploading. + } + uploaded[h] = true + } + select { + case blobChan <- l: + case <-gctx.Done(): + return gctx.Err() + } + } + return nil + }) + + if l, err := partial.ConfigLayer(img); err != nil { + // We can't read the ConfigLayer, possibly because of streaming layers, + // since the layer DiffIDs haven't been calculated yet. Attempt to wait + // for the other layers to be uploaded, then try the config again. + if err := g.Wait(); err != nil { + return err + } + + // Now that all the layers are uploaded, try to upload the config file blob. + l, err := partial.ConfigLayer(img) + if err != nil { + return err + } + if err := w.uploadOne(ctx, l); err != nil { + return err + } + } else { + // We *can* read the ConfigLayer, so upload it concurrently with the layers. + g.Go(func() error { + return w.uploadOne(gctx, l) + }) + + // Wait for the layers + config. + if err := g.Wait(); err != nil { + return err + } + } + + // With all of the constituent elements uploaded, upload the manifest + // to commit the image. + return w.commitManifest(ctx, img, ref) +} + +// writer writes the elements of an image to a remote image reference. +type writer struct { + repo name.Repository + client *http.Client + context context.Context + + updates chan<- v1.Update + lastUpdate *v1.Update + backoff Backoff + predicate retry.Predicate +} + +func sendError(ch chan<- v1.Update, err error) error { + if err != nil && ch != nil { + ch <- v1.Update{Error: err} + } + return err +} + +// url returns a url.Url for the specified path in the context of this remote image reference. +func (w *writer) url(path string) url.URL { + return url.URL{ + Scheme: w.repo.Registry.Scheme(), + Host: w.repo.RegistryStr(), + Path: path, + } +} + +// nextLocation extracts the fully-qualified URL to which we should send the next request in an upload sequence. +func (w *writer) nextLocation(resp *http.Response) (string, error) { + loc := resp.Header.Get("Location") + if len(loc) == 0 { + return "", errors.New("missing Location header") + } + u, err := url.Parse(loc) + if err != nil { + return "", err + } + + // If the location header returned is just a url path, then fully qualify it. + // We cannot simply call w.url, since there might be an embedded query string. + return resp.Request.URL.ResolveReference(u).String(), nil +} + +// checkExistingBlob checks if a blob exists already in the repository by making a +// HEAD request to the blob store API. GCR performs an existence check on the +// initiation if "mount" is specified, even if no "from" sources are specified. +// However, this is not broadly applicable to all registries, e.g. ECR. +func (w *writer) checkExistingBlob(h v1.Hash) (bool, error) { + u := w.url(fmt.Sprintf("/v2/%s/blobs/%s", w.repo.RepositoryStr(), h.String())) + + req, err := http.NewRequest(http.MethodHead, u.String(), nil) + if err != nil { + return false, err + } + + resp, err := w.client.Do(req.WithContext(w.context)) + if err != nil { + return false, err + } + defer resp.Body.Close() + + if err := transport.CheckError(resp, http.StatusOK, http.StatusNotFound); err != nil { + return false, err + } + + return resp.StatusCode == http.StatusOK, nil +} + +// checkExistingManifest checks if a manifest exists already in the repository +// by making a HEAD request to the manifest API. +func (w *writer) checkExistingManifest(h v1.Hash, mt types.MediaType) (bool, error) { + u := w.url(fmt.Sprintf("/v2/%s/manifests/%s", w.repo.RepositoryStr(), h.String())) + + req, err := http.NewRequest(http.MethodHead, u.String(), nil) + if err != nil { + return false, err + } + req.Header.Set("Accept", string(mt)) + + resp, err := w.client.Do(req.WithContext(w.context)) + if err != nil { + return false, err + } + defer resp.Body.Close() + + if err := transport.CheckError(resp, http.StatusOK, http.StatusNotFound); err != nil { + return false, err + } + + return resp.StatusCode == http.StatusOK, nil +} + +// initiateUpload initiates the blob upload, which starts with a POST that can +// optionally include the hash of the layer and a list of repositories from +// which that layer might be read. On failure, an error is returned. +// On success, the layer was either mounted (nothing more to do) or a blob +// upload was initiated and the body of that blob should be sent to the returned +// location. +func (w *writer) initiateUpload(from, mount string) (location string, mounted bool, err error) { + u := w.url(fmt.Sprintf("/v2/%s/blobs/uploads/", w.repo.RepositoryStr())) + uv := url.Values{} + if mount != "" && from != "" { + // Quay will fail if we specify a "mount" without a "from". + uv["mount"] = []string{mount} + uv["from"] = []string{from} + } + u.RawQuery = uv.Encode() + + // Make the request to initiate the blob upload. + req, err := http.NewRequest(http.MethodPost, u.String(), nil) + if err != nil { + return "", false, err + } + req.Header.Set("Content-Type", "application/json") + resp, err := w.client.Do(req.WithContext(w.context)) + if err != nil { + return "", false, err + } + defer resp.Body.Close() + + if err := transport.CheckError(resp, http.StatusCreated, http.StatusAccepted); err != nil { + return "", false, err + } + + // Check the response code to determine the result. + switch resp.StatusCode { + case http.StatusCreated: + // We're done, we were able to fast-path. + return "", true, nil + case http.StatusAccepted: + // Proceed to PATCH, upload has begun. + loc, err := w.nextLocation(resp) + return loc, false, err + default: + panic("Unreachable: initiateUpload") + } +} + +type progressReader struct { + rc io.ReadCloser + + count *int64 // number of bytes this reader has read, to support resetting on retry. + updates chan<- v1.Update + lastUpdate *v1.Update +} + +func (r *progressReader) Read(b []byte) (int, error) { + n, err := r.rc.Read(b) + if err != nil { + return n, err + } + atomic.AddInt64(r.count, int64(n)) + // TODO: warn/debug log if sending takes too long, or if sending is blocked while context is cancelled. + r.updates <- v1.Update{ + Total: r.lastUpdate.Total, + Complete: atomic.AddInt64(&r.lastUpdate.Complete, int64(n)), + } + return n, nil +} + +func (r *progressReader) Close() error { return r.rc.Close() } + +// streamBlob streams the contents of the blob to the specified location. +// On failure, this will return an error. On success, this will return the location +// header indicating how to commit the streamed blob. +func (w *writer) streamBlob(ctx context.Context, blob io.ReadCloser, streamLocation string) (commitLocation string, rerr error) { + reset := func() {} + defer func() { + if rerr != nil { + reset() + } + }() + if w.updates != nil { + var count int64 + blob = &progressReader{rc: blob, updates: w.updates, lastUpdate: w.lastUpdate, count: &count} + reset = func() { + atomic.AddInt64(&w.lastUpdate.Complete, -count) + w.updates <- *w.lastUpdate + } + } + + req, err := http.NewRequest(http.MethodPatch, streamLocation, blob) + if err != nil { + return "", err + } + req.Header.Set("Content-Type", "application/octet-stream") + + resp, err := w.client.Do(req.WithContext(ctx)) + if err != nil { + return "", err + } + defer resp.Body.Close() + + if err := transport.CheckError(resp, http.StatusNoContent, http.StatusAccepted, http.StatusCreated); err != nil { + return "", err + } + + // The blob has been uploaded, return the location header indicating + // how to commit this layer. + return w.nextLocation(resp) +} + +// commitBlob commits this blob by sending a PUT to the location returned from +// streaming the blob. +func (w *writer) commitBlob(location, digest string) error { + u, err := url.Parse(location) + if err != nil { + return err + } + v := u.Query() + v.Set("digest", digest) + u.RawQuery = v.Encode() + + req, err := http.NewRequest(http.MethodPut, u.String(), nil) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/octet-stream") + + resp, err := w.client.Do(req.WithContext(w.context)) + if err != nil { + return err + } + defer resp.Body.Close() + + return transport.CheckError(resp, http.StatusCreated) +} + +// incrProgress increments and sends a progress update, if WithProgress is used. +func (w *writer) incrProgress(written int64) { + if w.updates == nil { + return + } + w.updates <- v1.Update{ + Total: w.lastUpdate.Total, + Complete: atomic.AddInt64(&w.lastUpdate.Complete, written), + } +} + +// uploadOne performs a complete upload of a single layer. +func (w *writer) uploadOne(ctx context.Context, l v1.Layer) error { + tryUpload := func() error { + var from, mount string + if h, err := l.Digest(); err == nil { + // If we know the digest, this isn't a streaming layer. Do an existence + // check so we can skip uploading the layer if possible. + existing, err := w.checkExistingBlob(h) + if err != nil { + return err + } + if existing { + size, err := l.Size() + if err != nil { + return err + } + w.incrProgress(size) + logs.Progress.Printf("existing blob: %v", h) + return nil + } + + mount = h.String() + } + if ml, ok := l.(*MountableLayer); ok { + if w.repo.RegistryStr() == ml.Reference.Context().RegistryStr() { + from = ml.Reference.Context().RepositoryStr() + } + } + + location, mounted, err := w.initiateUpload(from, mount) + if err != nil { + return err + } else if mounted { + size, err := l.Size() + if err != nil { + return err + } + w.incrProgress(size) + h, err := l.Digest() + if err != nil { + return err + } + logs.Progress.Printf("mounted blob: %s", h.String()) + return nil + } + + // Only log layers with +json or +yaml. We can let through other stuff if it becomes popular. + // TODO(opencontainers/image-spec#791): Would be great to have an actual parser. + mt, err := l.MediaType() + if err != nil { + return err + } + smt := string(mt) + if !(strings.HasSuffix(smt, "+json") || strings.HasSuffix(smt, "+yaml")) { + ctx = redact.NewContext(ctx, "omitting binary blobs from logs") + } + + blob, err := l.Compressed() + if err != nil { + return err + } + location, err = w.streamBlob(ctx, blob, location) + if err != nil { + return err + } + + h, err := l.Digest() + if err != nil { + return err + } + digest := h.String() + + if err := w.commitBlob(location, digest); err != nil { + return err + } + logs.Progress.Printf("pushed blob: %s", digest) + return nil + } + + return retry.Retry(tryUpload, w.predicate, w.backoff) +} + +type withLayer interface { + Layer(v1.Hash) (v1.Layer, error) +} + +func (w *writer) writeIndex(ctx context.Context, ref name.Reference, ii v1.ImageIndex, options ...Option) error { + index, err := ii.IndexManifest() + if err != nil { + return err + } + + o, err := makeOptions(ref.Context(), options...) + if err != nil { + return err + } + + // TODO(#803): Pipe through remote.WithJobs and upload these in parallel. + for _, desc := range index.Manifests { + ref := ref.Context().Digest(desc.Digest.String()) + exists, err := w.checkExistingManifest(desc.Digest, desc.MediaType) + if err != nil { + return err + } + if exists { + logs.Progress.Print("existing manifest: ", desc.Digest) + continue + } + + switch desc.MediaType { + case types.OCIImageIndex, types.DockerManifestList: + ii, err := ii.ImageIndex(desc.Digest) + if err != nil { + return err + } + if err := w.writeIndex(ctx, ref, ii, options...); err != nil { + return err + } + case types.OCIManifestSchema1, types.DockerManifestSchema2: + img, err := ii.Image(desc.Digest) + if err != nil { + return err + } + if err := writeImage(ctx, ref, img, o, w.lastUpdate); err != nil { + return err + } + default: + // Workaround for #819. + if wl, ok := ii.(withLayer); ok { + layer, err := wl.Layer(desc.Digest) + if err != nil { + return err + } + if err := w.uploadOne(ctx, layer); err != nil { + return err + } + } + } + } + + // With all of the constituent elements uploaded, upload the manifest + // to commit the image. + return w.commitManifest(ctx, ii, ref) +} + +type withMediaType interface { + MediaType() (types.MediaType, error) +} + +// This is really silly, but go interfaces don't let me satisfy remote.Taggable +// with remote.Descriptor because of name collisions between method names and +// struct fields. +// +// Use reflection to either pull the v1.Descriptor out of remote.Descriptor or +// create a descriptor based on the RawManifest and (optionally) MediaType. +func unpackTaggable(t Taggable) ([]byte, *v1.Descriptor, error) { + if d, ok := t.(*Descriptor); ok { + return d.Manifest, &d.Descriptor, nil + } + b, err := t.RawManifest() + if err != nil { + return nil, nil, err + } + + // A reasonable default if Taggable doesn't implement MediaType. + mt := types.DockerManifestSchema2 + + if wmt, ok := t.(withMediaType); ok { + m, err := wmt.MediaType() + if err != nil { + return nil, nil, err + } + mt = m + } + + h, sz, err := v1.SHA256(bytes.NewReader(b)) + if err != nil { + return nil, nil, err + } + + return b, &v1.Descriptor{ + MediaType: mt, + Size: sz, + Digest: h, + }, nil +} + +// commitManifest does a PUT of the image's manifest. +func (w *writer) commitManifest(ctx context.Context, t Taggable, ref name.Reference) error { + tryUpload := func() error { + raw, desc, err := unpackTaggable(t) + if err != nil { + return err + } + + u := w.url(fmt.Sprintf("/v2/%s/manifests/%s", w.repo.RepositoryStr(), ref.Identifier())) + + // Make the request to PUT the serialized manifest + req, err := http.NewRequest(http.MethodPut, u.String(), bytes.NewBuffer(raw)) + if err != nil { + return err + } + req.Header.Set("Content-Type", string(desc.MediaType)) + + resp, err := w.client.Do(req.WithContext(ctx)) + if err != nil { + return err + } + defer resp.Body.Close() + + if err := transport.CheckError(resp, http.StatusOK, http.StatusCreated, http.StatusAccepted); err != nil { + return err + } + + // The image was successfully pushed! + logs.Progress.Printf("%v: digest: %v size: %d", ref, desc.Digest, desc.Size) + w.incrProgress(int64(len(raw))) + return nil + } + + return retry.Retry(tryUpload, w.predicate, w.backoff) +} + +func scopesForUploadingImage(repo name.Repository, layers []v1.Layer) []string { + // use a map as set to remove duplicates scope strings + scopeSet := map[string]struct{}{} + + for _, l := range layers { + if ml, ok := l.(*MountableLayer); ok { + // we will add push scope for ref.Context() after the loop. + // for now we ask pull scope for references of the same registry + if ml.Reference.Context().String() != repo.String() && ml.Reference.Context().Registry.String() == repo.Registry.String() { + scopeSet[ml.Reference.Scope(transport.PullScope)] = struct{}{} + } + } + } + + scopes := make([]string, 0) + // Push scope should be the first element because a few registries just look at the first scope to determine access. + scopes = append(scopes, repo.Scope(transport.PushScope)) + + for scope := range scopeSet { + scopes = append(scopes, scope) + } + + return scopes +} + +// WriteIndex pushes the provided ImageIndex to the specified image reference. +// WriteIndex will attempt to push all of the referenced manifests before +// attempting to push the ImageIndex, to retain referential integrity. +func WriteIndex(ref name.Reference, ii v1.ImageIndex, options ...Option) (rerr error) { + o, err := makeOptions(ref.Context(), options...) + if err != nil { + return err + } + + scopes := []string{ref.Scope(transport.PushScope)} + tr, err := transport.NewWithContext(o.context, ref.Context().Registry, o.auth, o.transport, scopes) + if err != nil { + return err + } + w := writer{ + repo: ref.Context(), + client: &http.Client{Transport: tr}, + context: o.context, + updates: o.updates, + backoff: o.retryBackoff, + predicate: o.retryPredicate, + } + + if o.updates != nil { + w.lastUpdate = &v1.Update{} + w.lastUpdate.Total, err = countIndex(ii, o.allowNondistributableArtifacts) + if err != nil { + return err + } + defer close(o.updates) + defer func() { sendError(o.updates, rerr) }() + } + + return w.writeIndex(o.context, ref, ii, options...) +} + +// countImage counts the total size of all layers + config blob + manifest for +// an image. It de-dupes duplicate layers. +func countImage(img v1.Image, allowNondistributableArtifacts bool) (int64, error) { + var total int64 + ls, err := img.Layers() + if err != nil { + return 0, err + } + seen := map[v1.Hash]bool{} + for _, l := range ls { + // Handle foreign layers. + mt, err := l.MediaType() + if err != nil { + return 0, err + } + if !mt.IsDistributable() && !allowNondistributableArtifacts { + continue + } + + // TODO: support streaming layers which update the total count as they write. + if _, ok := l.(*stream.Layer); ok { + return 0, errors.New("cannot use stream.Layer and WithProgress") + } + + // Dedupe layers. + d, err := l.Digest() + if err != nil { + return 0, err + } + if seen[d] { + continue + } + seen[d] = true + + size, err := l.Size() + if err != nil { + return 0, err + } + total += size + } + b, err := img.RawConfigFile() + if err != nil { + return 0, err + } + total += int64(len(b)) + size, err := img.Size() + if err != nil { + return 0, err + } + total += size + return total, nil +} + +// countIndex counts the total size of all images + sub-indexes for an index. +// It does not attempt to de-dupe duplicate images, etc. +func countIndex(idx v1.ImageIndex, allowNondistributableArtifacts bool) (int64, error) { + var total int64 + mf, err := idx.IndexManifest() + if err != nil { + return 0, err + } + + for _, desc := range mf.Manifests { + switch desc.MediaType { + case types.OCIImageIndex, types.DockerManifestList: + sidx, err := idx.ImageIndex(desc.Digest) + if err != nil { + return 0, err + } + size, err := countIndex(sidx, allowNondistributableArtifacts) + if err != nil { + return 0, err + } + total += size + case types.OCIManifestSchema1, types.DockerManifestSchema2: + simg, err := idx.Image(desc.Digest) + if err != nil { + return 0, err + } + size, err := countImage(simg, allowNondistributableArtifacts) + if err != nil { + return 0, err + } + total += size + default: + // Workaround for #819. + if wl, ok := idx.(withLayer); ok { + layer, err := wl.Layer(desc.Digest) + if err != nil { + return 0, err + } + size, err := layer.Size() + if err != nil { + return 0, err + } + total += size + } + } + } + + size, err := idx.Size() + if err != nil { + return 0, err + } + total += size + return total, nil +} + +// WriteLayer uploads the provided Layer to the specified repo. +func WriteLayer(repo name.Repository, layer v1.Layer, options ...Option) (rerr error) { + o, err := makeOptions(repo, options...) + if err != nil { + return err + } + scopes := scopesForUploadingImage(repo, []v1.Layer{layer}) + tr, err := transport.NewWithContext(o.context, repo.Registry, o.auth, o.transport, scopes) + if err != nil { + return err + } + w := writer{ + repo: repo, + client: &http.Client{Transport: tr}, + context: o.context, + updates: o.updates, + backoff: o.retryBackoff, + predicate: o.retryPredicate, + } + + if o.updates != nil { + defer close(o.updates) + defer func() { sendError(o.updates, rerr) }() + + // TODO: support streaming layers which update the total count as they write. + if _, ok := layer.(*stream.Layer); ok { + return errors.New("cannot use stream.Layer and WithProgress") + } + size, err := layer.Size() + if err != nil { + return err + } + w.lastUpdate = &v1.Update{Total: size} + } + return w.uploadOne(o.context, layer) +} + +// Tag adds a tag to the given Taggable via PUT /v2/.../manifests/ +// +// Notable implementations of Taggable are v1.Image, v1.ImageIndex, and +// remote.Descriptor. +// +// If t implements MediaType, we will use that for the Content-Type, otherwise +// we will default to types.DockerManifestSchema2. +// +// Tag does not attempt to write anything other than the manifest, so callers +// should ensure that all blobs or manifests that are referenced by t exist +// in the target registry. +func Tag(tag name.Tag, t Taggable, options ...Option) error { + return Put(tag, t, options...) +} + +// Put adds a manifest from the given Taggable via PUT /v1/.../manifest/ +// +// Notable implementations of Taggable are v1.Image, v1.ImageIndex, and +// remote.Descriptor. +// +// If t implements MediaType, we will use that for the Content-Type, otherwise +// we will default to types.DockerManifestSchema2. +// +// Put does not attempt to write anything other than the manifest, so callers +// should ensure that all blobs or manifests that are referenced by t exist +// in the target registry. +func Put(ref name.Reference, t Taggable, options ...Option) error { + o, err := makeOptions(ref.Context(), options...) + if err != nil { + return err + } + scopes := []string{ref.Scope(transport.PushScope)} + + // TODO: This *always* does a token exchange. For some registries, + // that's pretty slow. Some ideas; + // * Tag could take a list of tags. + // * Allow callers to pass in a transport.Transport, typecheck + // it to allow them to reuse the transport across multiple calls. + // * WithTag option to do multiple manifest PUTs in commitManifest. + tr, err := transport.NewWithContext(o.context, ref.Context().Registry, o.auth, o.transport, scopes) + if err != nil { + return err + } + w := writer{ + repo: ref.Context(), + client: &http.Client{Transport: tr}, + context: o.context, + backoff: o.retryBackoff, + predicate: o.retryPredicate, + } + + return w.commitManifest(o.context, t, ref) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/stream/README.md b/vendor/github.com/google/go-containerregistry/pkg/v1/stream/README.md new file mode 100644 index 0000000000..da0dda48d9 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/stream/README.md @@ -0,0 +1,68 @@ +# `stream` + +[![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/stream?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/stream) + +The `stream` package contains an implementation of +[`v1.Layer`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1#Layer) +that supports _streaming_ access, i.e. the layer contents are read once and not +buffered. + +## Usage + +```go +package main + +import ( + "os" + + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/google/go-containerregistry/pkg/v1/stream" +) + +// upload the contents of stdin as a layer to a local registry +func main() { + repo, err := name.NewRepository("localhost:5000/stream") + if err != nil { + panic(err) + } + + layer := stream.NewLayer(os.Stdin) + + if err := remote.WriteLayer(repo, layer); err != nil { + panic(err) + } +} +``` + +## Structure + +This implements the layer portion of an [image +upload](/pkg/v1/remote#anatomy-of-an-image-upload). We launch a goroutine that +is responsible for hashing the uncompressed contents to compute the `DiffID`, +gzipping them to produce the `Compressed` contents, and hashing/counting the +bytes to produce the `Digest`/`Size`. This goroutine writes to an +`io.PipeWriter`, which blocks until `Compressed` reads the gzipped contents from +the corresponding `io.PipeReader`. + +

+ +

+ +## Caveats + +This assumes that you have an uncompressed layer (i.e. a tarball) and would like +to compress it. Calling `Uncompressed` is always an error. Likewise, other +methods are invalid until the contents of `Compressed` have been completely +consumed and `Close`d. + +Using a `stream.Layer` will likely not work without careful consideration. For +example, in the `mutate` package, we defer computing the manifest and config +file until they are actually called. This allows you to `mutate.Append` a +streaming layer to an image without accidentally consuming it. Similarly, in +`remote.Write`, if calling `Digest` on a layer fails, we attempt to upload the +layer anyway, understanding that we may be dealing with a `stream.Layer` whose +contents need to be uploaded before we can upload the config file. + +Given the [structure](#structure) of how this is implemented, forgetting to +`Close` a `stream.Layer` will leak a goroutine. diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/stream/layer.go b/vendor/github.com/google/go-containerregistry/pkg/v1/stream/layer.go new file mode 100644 index 0000000000..eeed00b3c5 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/stream/layer.go @@ -0,0 +1,263 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package stream + +import ( + "bufio" + "compress/gzip" + "crypto/sha256" + "encoding/hex" + "errors" + "hash" + "io" + "os" + "sync" + + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +var ( + // ErrNotComputed is returned when the requested value is not yet + // computed because the stream has not been consumed yet. + ErrNotComputed = errors.New("value not computed until stream is consumed") + + // ErrConsumed is returned by Compressed when the underlying stream has + // already been consumed and closed. + ErrConsumed = errors.New("stream was already consumed") +) + +// Layer is a streaming implementation of v1.Layer. +type Layer struct { + blob io.ReadCloser + consumed bool + compression int + + mu sync.Mutex + digest, diffID *v1.Hash + size int64 +} + +var _ v1.Layer = (*Layer)(nil) + +// LayerOption applies options to layer +type LayerOption func(*Layer) + +// WithCompressionLevel sets the gzip compression. See `gzip.NewWriterLevel` for possible values. +func WithCompressionLevel(level int) LayerOption { + return func(l *Layer) { + l.compression = level + } +} + +// NewLayer creates a Layer from an io.ReadCloser. +func NewLayer(rc io.ReadCloser, opts ...LayerOption) *Layer { + layer := &Layer{ + blob: rc, + compression: gzip.BestSpeed, + } + + for _, opt := range opts { + opt(layer) + } + + return layer +} + +// Digest implements v1.Layer. +func (l *Layer) Digest() (v1.Hash, error) { + l.mu.Lock() + defer l.mu.Unlock() + if l.digest == nil { + return v1.Hash{}, ErrNotComputed + } + return *l.digest, nil +} + +// DiffID implements v1.Layer. +func (l *Layer) DiffID() (v1.Hash, error) { + l.mu.Lock() + defer l.mu.Unlock() + if l.diffID == nil { + return v1.Hash{}, ErrNotComputed + } + return *l.diffID, nil +} + +// Size implements v1.Layer. +func (l *Layer) Size() (int64, error) { + l.mu.Lock() + defer l.mu.Unlock() + if l.size == 0 { + return 0, ErrNotComputed + } + return l.size, nil +} + +// MediaType implements v1.Layer +func (l *Layer) MediaType() (types.MediaType, error) { + // We return DockerLayer for now as uncompressed layers + // are unimplemented + return types.DockerLayer, nil +} + +// Uncompressed implements v1.Layer. +func (l *Layer) Uncompressed() (io.ReadCloser, error) { + return nil, errors.New("NYI: stream.Layer.Uncompressed is not implemented") +} + +// Compressed implements v1.Layer. +func (l *Layer) Compressed() (io.ReadCloser, error) { + if l.consumed { + return nil, ErrConsumed + } + return newCompressedReader(l) +} + +// finalize sets the layer to consumed and computes all hash and size values. +func (l *Layer) finalize(uncompressed, compressed hash.Hash, size int64) error { + l.mu.Lock() + defer l.mu.Unlock() + + diffID, err := v1.NewHash("sha256:" + hex.EncodeToString(uncompressed.Sum(nil))) + if err != nil { + return err + } + l.diffID = &diffID + + digest, err := v1.NewHash("sha256:" + hex.EncodeToString(compressed.Sum(nil))) + if err != nil { + return err + } + l.digest = &digest + + l.size = size + l.consumed = true + return nil +} + +type compressedReader struct { + pr io.Reader + closer func() error +} + +func newCompressedReader(l *Layer) (*compressedReader, error) { + // Collect digests of compressed and uncompressed stream and size of + // compressed stream. + h := sha256.New() + zh := sha256.New() + count := &countWriter{} + + // gzip.Writer writes to the output stream via pipe, a hasher to + // capture compressed digest, and a countWriter to capture compressed + // size. + pr, pw := io.Pipe() + + // Write compressed bytes to be read by the pipe.Reader, hashed by zh, and counted by count. + mw := io.MultiWriter(pw, zh, count) + + // Buffer the output of the gzip writer so we don't have to wait on pr to keep writing. + // 64K ought to be small enough for anybody. + bw := bufio.NewWriterSize(mw, 2<<16) + zw, err := gzip.NewWriterLevel(bw, l.compression) + if err != nil { + return nil, err + } + + doneDigesting := make(chan struct{}) + + cr := &compressedReader{ + pr: pr, + closer: func() error { + // Immediately close pw without error. There are three ways to get + // here. + // + // 1. There was a copy error due from the underlying reader, in which + // case the error will not be overwritten. + // 2. Copying from the underlying reader completed successfully. + // 3. Close has been called before the underlying reader has been + // fully consumed. In this case pw must be closed in order to + // keep the flush of bw from blocking indefinitely. + // + // NOTE: pw.Close never returns an error. The signature is only to + // implement io.Closer. + _ = pw.Close() + + // Close the inner ReadCloser. + // + // NOTE: net/http will call close on success, so if we've already + // closed the inner rc, it's not an error. + if err := l.blob.Close(); err != nil && !errors.Is(err, os.ErrClosed) { + return err + } + + // Finalize layer with its digest and size values. + <-doneDigesting + return l.finalize(h, zh, count.n) + }, + } + go func() { + // Copy blob into the gzip writer, which also hashes and counts the + // size of the compressed output, and hasher of the raw contents. + _, copyErr := io.Copy(io.MultiWriter(h, zw), l.blob) + + // Close the gzip writer once copying is done. If this is done in the + // Close method of compressedReader instead, then it can cause a panic + // when the compressedReader is closed before the blob is fully + // consumed and io.Copy in this goroutine is still blocking. + closeErr := zw.Close() + + // Check errors from writing and closing streams. + if copyErr != nil { + close(doneDigesting) + pw.CloseWithError(copyErr) + return + } + if closeErr != nil { + close(doneDigesting) + pw.CloseWithError(closeErr) + return + } + + // Flush the buffer once all writes are complete to the gzip writer. + if err := bw.Flush(); err != nil { + close(doneDigesting) + pw.CloseWithError(err) + return + } + + // Notify closer that digests are done being written. + close(doneDigesting) + + // Close the compressed reader to calculate digest/diffID/size. This + // will cause pr to return EOF which will cause readers of the + // Compressed stream to finish reading. + pw.CloseWithError(cr.Close()) + }() + + return cr, nil +} + +func (cr *compressedReader) Read(b []byte) (int, error) { return cr.pr.Read(b) } + +func (cr *compressedReader) Close() error { return cr.closer() } + +// countWriter counts bytes written to it. +type countWriter struct{ n int64 } + +func (c *countWriter) Write(p []byte) (int, error) { + c.n += int64(len(p)) + return len(p), nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/tarball/README.md b/vendor/github.com/google/go-containerregistry/pkg/v1/tarball/README.md new file mode 100644 index 0000000000..03f339b063 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/tarball/README.md @@ -0,0 +1,280 @@ +# `tarball` + +[![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/tarball?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/tarball) + +This package produces tarballs that can consumed via `docker load`. Note +that this is a _different_ format from the [`legacy`](/pkg/legacy/tarball) +tarballs that are produced by `docker save`, but this package is still able to +read the legacy tarballs produced by `docker save`. + +## Usage + +```go +package main + +import ( + "os" + + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/tarball" +) + +func main() { + // Read a tarball from os.Args[1] that contains ubuntu. + tag, err := name.NewTag("ubuntu") + if err != nil { + panic(err) + } + img, err := tarball.ImageFromPath(os.Args[1], &tag) + if err != nil { + panic(err) + } + + // Write that tarball to os.Args[2] with a different tag. + newTag, err := name.NewTag("ubuntu:newest") + if err != nil { + panic(err) + } + f, err := os.Create(os.Args[2]) + if err != nil { + panic(err) + } + defer f.Close() + + if err := tarball.Write(newTag, img, f); err != nil { + panic(err) + } +} +``` + +## Structure + +

+ +

+ +Let's look at what happens when we write out a tarball: + + +### `ubuntu:latest` + +``` +$ crane pull ubuntu ubuntu.tar && mkdir ubuntu && tar xf ubuntu.tar -C ubuntu && rm ubuntu.tar +$ tree ubuntu/ +ubuntu/ +├── 423ae2b273f4c17ceee9e8482fa8d071d90c7d052ae208e1fe4963fceb3d6954.tar.gz +├── b6b53be908de2c0c78070fff0a9f04835211b3156c4e73785747af365e71a0d7.tar.gz +├── de83a2304fa1f7c4a13708a0d15b9704f5945c2be5cbb2b3ed9b2ccb718d0b3d.tar.gz +├── f9a83bce3af0648efaa60b9bb28225b09136d2d35d0bed25ac764297076dec1b.tar.gz +├── manifest.json +└── sha256:72300a873c2ca11c70d0c8642177ce76ff69ae04d61a5813ef58d40ff66e3e7c + +0 directories, 6 files +``` + +There are a couple interesting files here. + +`manifest.json` is the entrypoint: a list of [`tarball.Descriptor`s](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/tarball#Descriptor) +that describe the images contained in this tarball. + +For each image, this has the `RepoTags` (how it was pulled), a `Config` file +that points to the image's config file, a list of `Layers`, and (optionally) +`LayerSources`. + +``` +$ jq < ubuntu/manifest.json +[ + { + "Config": "sha256:72300a873c2ca11c70d0c8642177ce76ff69ae04d61a5813ef58d40ff66e3e7c", + "RepoTags": [ + "ubuntu" + ], + "Layers": [ + "423ae2b273f4c17ceee9e8482fa8d071d90c7d052ae208e1fe4963fceb3d6954.tar.gz", + "de83a2304fa1f7c4a13708a0d15b9704f5945c2be5cbb2b3ed9b2ccb718d0b3d.tar.gz", + "f9a83bce3af0648efaa60b9bb28225b09136d2d35d0bed25ac764297076dec1b.tar.gz", + "b6b53be908de2c0c78070fff0a9f04835211b3156c4e73785747af365e71a0d7.tar.gz" + ] + } +] +``` + +The config file and layers are exactly what you would expect, and match the +registry representations of the same artifacts. You'll notice that the +`manifest.json` contains similar information as the registry manifest, but isn't +quite the same: + +``` +$ crane manifest ubuntu@sha256:0925d086715714114c1988f7c947db94064fd385e171a63c07730f1fa014e6f9 +{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "config": { + "mediaType": "application/vnd.docker.container.image.v1+json", + "size": 3408, + "digest": "sha256:72300a873c2ca11c70d0c8642177ce76ff69ae04d61a5813ef58d40ff66e3e7c" + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 26692096, + "digest": "sha256:423ae2b273f4c17ceee9e8482fa8d071d90c7d052ae208e1fe4963fceb3d6954" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 35365, + "digest": "sha256:de83a2304fa1f7c4a13708a0d15b9704f5945c2be5cbb2b3ed9b2ccb718d0b3d" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 852, + "digest": "sha256:f9a83bce3af0648efaa60b9bb28225b09136d2d35d0bed25ac764297076dec1b" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 163, + "digest": "sha256:b6b53be908de2c0c78070fff0a9f04835211b3156c4e73785747af365e71a0d7" + } + ] +} +``` + +This makes it difficult to maintain image digests when roundtripping images +through the tarball format, so it's not a great format if you care about +provenance. + +The ubuntu example didn't have any `LayerSources` -- let's look at another image +that does. + +### `hello-world:nanoserver` + +``` +$ crane pull hello-world:nanoserver@sha256:63c287625c2b0b72900e562de73c0e381472a83b1b39217aef3856cd398eca0b nanoserver.tar +$ mkdir nanoserver && tar xf nanoserver.tar -C nanoserver && rm nanoserver.tar +$ tree nanoserver/ +nanoserver/ +├── 10d1439be4eb8819987ec2e9c140d44d74d6b42a823d57fe1953bd99948e1bc0.tar.gz +├── a35da61c356213336e646756218539950461ff2bf096badf307a23add6e70053.tar.gz +├── be21f08f670160cbae227e3053205b91d6bfa3de750b90c7e00bd2c511ccb63a.tar.gz +├── manifest.json +└── sha256:bc5d255ea81f83c8c38a982a6d29a6f2198427d258aea5f166e49856896b2da6 + +0 directories, 5 files + +$ jq < nanoserver/manifest.json +[ + { + "Config": "sha256:bc5d255ea81f83c8c38a982a6d29a6f2198427d258aea5f166e49856896b2da6", + "RepoTags": [ + "index.docker.io/library/hello-world:i-was-a-digest" + ], + "Layers": [ + "a35da61c356213336e646756218539950461ff2bf096badf307a23add6e70053.tar.gz", + "be21f08f670160cbae227e3053205b91d6bfa3de750b90c7e00bd2c511ccb63a.tar.gz", + "10d1439be4eb8819987ec2e9c140d44d74d6b42a823d57fe1953bd99948e1bc0.tar.gz" + ], + "LayerSources": { + "sha256:26fd2d9d4c64a4f965bbc77939a454a31b607470f430b5d69fc21ded301fa55e": { + "mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip", + "size": 101145811, + "digest": "sha256:a35da61c356213336e646756218539950461ff2bf096badf307a23add6e70053", + "urls": [ + "https://mcr.microsoft.com/v2/windows/nanoserver/blobs/sha256:a35da61c356213336e646756218539950461ff2bf096badf307a23add6e70053" + ] + } + } + } +] +``` + +A couple things to note about this `manifest.json` versus the other: +* The `RepoTags` field is a bit weird here. `hello-world` is a multi-platform + image, so We had to pull this image by digest, since we're (I'm) on + amd64/linux and wanted to grab a windows image. Since the tarball format + expects a tag under `RepoTags`, and we didn't pull by tag, we replace the + digest with a sentinel `i-was-a-digest` "tag" to appease docker. +* The `LayerSources` has enough information to reconstruct the foreign layers + pointer when pushing/pulling from the registry. For legal reasons, microsoft + doesn't want anyone but them to serve windows base images, so the mediaType + here indicates a "foreign" or "non-distributable" layer with an URL for where + you can download it from microsoft (see the [OCI + image-spec](https://github.com/opencontainers/image-spec/blob/master/layer.md#non-distributable-layers)). + +We can look at what's in the registry to explain both of these things: +``` +$ crane manifest hello-world:nanoserver | jq . +{ + "manifests": [ + { + "digest": "sha256:63c287625c2b0b72900e562de73c0e381472a83b1b39217aef3856cd398eca0b", + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "platform": { + "architecture": "amd64", + "os": "windows", + "os.version": "10.0.17763.1040" + }, + "size": 1124 + } + ], + "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json", + "schemaVersion": 2 +} + + +# Note the media type and "urls" field. +$ crane manifest hello-world:nanoserver@sha256:63c287625c2b0b72900e562de73c0e381472a83b1b39217aef3856cd398eca0b | jq . +{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "config": { + "mediaType": "application/vnd.docker.container.image.v1+json", + "size": 1721, + "digest": "sha256:bc5d255ea81f83c8c38a982a6d29a6f2198427d258aea5f166e49856896b2da6" + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip", + "size": 101145811, + "digest": "sha256:a35da61c356213336e646756218539950461ff2bf096badf307a23add6e70053", + "urls": [ + "https://mcr.microsoft.com/v2/windows/nanoserver/blobs/sha256:a35da61c356213336e646756218539950461ff2bf096badf307a23add6e70053" + ] + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 1669, + "digest": "sha256:be21f08f670160cbae227e3053205b91d6bfa3de750b90c7e00bd2c511ccb63a" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 949, + "digest": "sha256:10d1439be4eb8819987ec2e9c140d44d74d6b42a823d57fe1953bd99948e1bc0" + } + ] +} +``` + +The `LayerSources` map is keyed by the diffid. Note that `sha256:26fd2d9d4c64a4f965bbc77939a454a31b607470f430b5d69fc21ded301fa55e` matches the first layer in the config file: +``` +$ jq '.[0].LayerSources' < nanoserver/manifest.json +{ + "sha256:26fd2d9d4c64a4f965bbc77939a454a31b607470f430b5d69fc21ded301fa55e": { + "mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip", + "size": 101145811, + "digest": "sha256:a35da61c356213336e646756218539950461ff2bf096badf307a23add6e70053", + "urls": [ + "https://mcr.microsoft.com/v2/windows/nanoserver/blobs/sha256:a35da61c356213336e646756218539950461ff2bf096badf307a23add6e70053" + ] + } +} + +$ jq < nanoserver/sha256\:bc5d255ea81f83c8c38a982a6d29a6f2198427d258aea5f166e49856896b2da6 | jq .rootfs +{ + "type": "layers", + "diff_ids": [ + "sha256:26fd2d9d4c64a4f965bbc77939a454a31b607470f430b5d69fc21ded301fa55e", + "sha256:601cf7d78c62e4b4d32a7bbf96a17606a9cea5bd9d22ffa6f34aa431d056b0e8", + "sha256:a1e1a3bf6529adcce4d91dce2cad86c2604a66b507ccbc4d2239f3da0ec5aab9" + ] +} +``` diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/tarball/doc.go b/vendor/github.com/google/go-containerregistry/pkg/v1/tarball/doc.go new file mode 100644 index 0000000000..4eb79bb4e5 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/tarball/doc.go @@ -0,0 +1,17 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +// Package tarball provides facilities for reading/writing v1.Images from/to +// a tarball on-disk. +package tarball diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/tarball/image.go b/vendor/github.com/google/go-containerregistry/pkg/v1/tarball/image.go new file mode 100644 index 0000000000..b2e44df756 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/tarball/image.go @@ -0,0 +1,423 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package tarball + +import ( + "archive/tar" + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path" + "path/filepath" + "sync" + + "github.com/google/go-containerregistry/internal/gzip" + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +type image struct { + opener Opener + manifest *Manifest + config []byte + imgDescriptor *Descriptor + + tag *name.Tag +} + +type uncompressedImage struct { + *image +} + +type compressedImage struct { + *image + manifestLock sync.Mutex // Protects manifest + manifest *v1.Manifest +} + +var _ partial.UncompressedImageCore = (*uncompressedImage)(nil) +var _ partial.CompressedImageCore = (*compressedImage)(nil) + +// Opener is a thunk for opening a tar file. +type Opener func() (io.ReadCloser, error) + +func pathOpener(path string) Opener { + return func() (io.ReadCloser, error) { + return os.Open(path) + } +} + +// ImageFromPath returns a v1.Image from a tarball located on path. +func ImageFromPath(path string, tag *name.Tag) (v1.Image, error) { + return Image(pathOpener(path), tag) +} + +// LoadManifest load manifest +func LoadManifest(opener Opener) (Manifest, error) { + m, err := extractFileFromTar(opener, "manifest.json") + if err != nil { + return nil, err + } + defer m.Close() + + var manifest Manifest + + if err := json.NewDecoder(m).Decode(&manifest); err != nil { + return nil, err + } + return manifest, nil +} + +// Image exposes an image from the tarball at the provided path. +func Image(opener Opener, tag *name.Tag) (v1.Image, error) { + img := &image{ + opener: opener, + tag: tag, + } + if err := img.loadTarDescriptorAndConfig(); err != nil { + return nil, err + } + + // Peek at the first layer and see if it's compressed. + if len(img.imgDescriptor.Layers) > 0 { + compressed, err := img.areLayersCompressed() + if err != nil { + return nil, err + } + if compressed { + c := compressedImage{ + image: img, + } + return partial.CompressedToImage(&c) + } + } + + uc := uncompressedImage{ + image: img, + } + return partial.UncompressedToImage(&uc) +} + +func (i *image) MediaType() (types.MediaType, error) { + return types.DockerManifestSchema2, nil +} + +// Descriptor stores the manifest data for a single image inside a `docker save` tarball. +type Descriptor struct { + Config string + RepoTags []string + Layers []string + + // Tracks foreign layer info. Key is DiffID. + LayerSources map[v1.Hash]v1.Descriptor `json:",omitempty"` +} + +// Manifest represents the manifests of all images as the `manifest.json` file in a `docker save` tarball. +type Manifest []Descriptor + +func (m Manifest) findDescriptor(tag *name.Tag) (*Descriptor, error) { + if tag == nil { + if len(m) != 1 { + return nil, errors.New("tarball must contain only a single image to be used with tarball.Image") + } + return &(m)[0], nil + } + for _, img := range m { + for _, tagStr := range img.RepoTags { + repoTag, err := name.NewTag(tagStr) + if err != nil { + return nil, err + } + + // Compare the resolved names, since there are several ways to specify the same tag. + if repoTag.Name() == tag.Name() { + return &img, nil + } + } + } + return nil, fmt.Errorf("tag %s not found in tarball", tag) +} + +func (i *image) areLayersCompressed() (bool, error) { + if len(i.imgDescriptor.Layers) == 0 { + return false, errors.New("0 layers found in image") + } + layer := i.imgDescriptor.Layers[0] + blob, err := extractFileFromTar(i.opener, layer) + if err != nil { + return false, err + } + defer blob.Close() + return gzip.Is(blob) +} + +func (i *image) loadTarDescriptorAndConfig() error { + m, err := extractFileFromTar(i.opener, "manifest.json") + if err != nil { + return err + } + defer m.Close() + + if err := json.NewDecoder(m).Decode(&i.manifest); err != nil { + return err + } + + if i.manifest == nil { + return errors.New("no valid manifest.json in tarball") + } + + i.imgDescriptor, err = i.manifest.findDescriptor(i.tag) + if err != nil { + return err + } + + cfg, err := extractFileFromTar(i.opener, i.imgDescriptor.Config) + if err != nil { + return err + } + defer cfg.Close() + + i.config, err = ioutil.ReadAll(cfg) + if err != nil { + return err + } + return nil +} + +func (i *image) RawConfigFile() ([]byte, error) { + return i.config, nil +} + +// tarFile represents a single file inside a tar. Closing it closes the tar itself. +type tarFile struct { + io.Reader + io.Closer +} + +func extractFileFromTar(opener Opener, filePath string) (io.ReadCloser, error) { + f, err := opener() + if err != nil { + return nil, err + } + close := true + defer func() { + if close { + f.Close() + } + }() + + tf := tar.NewReader(f) + for { + hdr, err := tf.Next() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return nil, err + } + if hdr.Name == filePath { + if hdr.Typeflag == tar.TypeSymlink || hdr.Typeflag == tar.TypeLink { + currentDir := filepath.Dir(filePath) + return extractFileFromTar(opener, path.Join(currentDir, path.Clean(hdr.Linkname))) + } + close = false + return tarFile{ + Reader: tf, + Closer: f, + }, nil + } + } + return nil, fmt.Errorf("file %s not found in tar", filePath) +} + +// uncompressedLayerFromTarball implements partial.UncompressedLayer +type uncompressedLayerFromTarball struct { + diffID v1.Hash + mediaType types.MediaType + opener Opener + filePath string +} + +// foreignUncompressedLayer implements partial.UncompressedLayer but returns +// a custom descriptor. This allows the foreign layer URLs to be included in +// the generated image manifest for uncompressed layers. +type foreignUncompressedLayer struct { + uncompressedLayerFromTarball + desc v1.Descriptor +} + +func (fl *foreignUncompressedLayer) Descriptor() (*v1.Descriptor, error) { + return &fl.desc, nil +} + +// DiffID implements partial.UncompressedLayer +func (ulft *uncompressedLayerFromTarball) DiffID() (v1.Hash, error) { + return ulft.diffID, nil +} + +// Uncompressed implements partial.UncompressedLayer +func (ulft *uncompressedLayerFromTarball) Uncompressed() (io.ReadCloser, error) { + return extractFileFromTar(ulft.opener, ulft.filePath) +} + +func (ulft *uncompressedLayerFromTarball) MediaType() (types.MediaType, error) { + return ulft.mediaType, nil +} + +func (i *uncompressedImage) LayerByDiffID(h v1.Hash) (partial.UncompressedLayer, error) { + cfg, err := partial.ConfigFile(i) + if err != nil { + return nil, err + } + for idx, diffID := range cfg.RootFS.DiffIDs { + if diffID == h { + // Technically the media type should be 'application/tar' but given that our + // v1.Layer doesn't force consumers to care about whether the layer is compressed + // we should be fine returning the DockerLayer media type + mt := types.DockerLayer + if bd, ok := i.imgDescriptor.LayerSources[h]; ok { + // Overwrite the mediaType for foreign layers. + return &foreignUncompressedLayer{ + uncompressedLayerFromTarball: uncompressedLayerFromTarball{ + diffID: diffID, + mediaType: bd.MediaType, + opener: i.opener, + filePath: i.imgDescriptor.Layers[idx], + }, + desc: bd, + }, nil + } + return &uncompressedLayerFromTarball{ + diffID: diffID, + mediaType: mt, + opener: i.opener, + filePath: i.imgDescriptor.Layers[idx], + }, nil + } + } + return nil, fmt.Errorf("diff id %q not found", h) +} + +func (c *compressedImage) Manifest() (*v1.Manifest, error) { + c.manifestLock.Lock() + defer c.manifestLock.Unlock() + if c.manifest != nil { + return c.manifest, nil + } + + b, err := c.RawConfigFile() + if err != nil { + return nil, err + } + + cfgHash, cfgSize, err := v1.SHA256(bytes.NewReader(b)) + if err != nil { + return nil, err + } + + c.manifest = &v1.Manifest{ + SchemaVersion: 2, + MediaType: types.DockerManifestSchema2, + Config: v1.Descriptor{ + MediaType: types.DockerConfigJSON, + Size: cfgSize, + Digest: cfgHash, + }, + } + + for i, p := range c.imgDescriptor.Layers { + cfg, err := partial.ConfigFile(c) + if err != nil { + return nil, err + } + diffid := cfg.RootFS.DiffIDs[i] + if d, ok := c.imgDescriptor.LayerSources[diffid]; ok { + // If it's a foreign layer, just append the descriptor so we can avoid + // reading the entire file. + c.manifest.Layers = append(c.manifest.Layers, d) + } else { + l, err := extractFileFromTar(c.opener, p) + if err != nil { + return nil, err + } + defer l.Close() + sha, size, err := v1.SHA256(l) + if err != nil { + return nil, err + } + c.manifest.Layers = append(c.manifest.Layers, v1.Descriptor{ + MediaType: types.DockerLayer, + Size: size, + Digest: sha, + }) + } + } + return c.manifest, nil +} + +func (c *compressedImage) RawManifest() ([]byte, error) { + return partial.RawManifest(c) +} + +// compressedLayerFromTarball implements partial.CompressedLayer +type compressedLayerFromTarball struct { + desc v1.Descriptor + opener Opener + filePath string +} + +// Digest implements partial.CompressedLayer +func (clft *compressedLayerFromTarball) Digest() (v1.Hash, error) { + return clft.desc.Digest, nil +} + +// Compressed implements partial.CompressedLayer +func (clft *compressedLayerFromTarball) Compressed() (io.ReadCloser, error) { + return extractFileFromTar(clft.opener, clft.filePath) +} + +// MediaType implements partial.CompressedLayer +func (clft *compressedLayerFromTarball) MediaType() (types.MediaType, error) { + return clft.desc.MediaType, nil +} + +// Size implements partial.CompressedLayer +func (clft *compressedLayerFromTarball) Size() (int64, error) { + return clft.desc.Size, nil +} + +func (c *compressedImage) LayerByDigest(h v1.Hash) (partial.CompressedLayer, error) { + m, err := c.Manifest() + if err != nil { + return nil, err + } + for i, l := range m.Layers { + if l.Digest == h { + fp := c.imgDescriptor.Layers[i] + return &compressedLayerFromTarball{ + desc: l, + opener: c.opener, + filePath: fp, + }, nil + } + } + return nil, fmt.Errorf("blob %v not found", h) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/tarball/layer.go b/vendor/github.com/google/go-containerregistry/pkg/v1/tarball/layer.go new file mode 100644 index 0000000000..29c5fa0607 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/tarball/layer.go @@ -0,0 +1,296 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package tarball + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "os" + "sync" + + "github.com/containerd/stargz-snapshotter/estargz" + "github.com/google/go-containerregistry/internal/and" + gestargz "github.com/google/go-containerregistry/internal/estargz" + ggzip "github.com/google/go-containerregistry/internal/gzip" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/types" +) + +type layer struct { + digest v1.Hash + diffID v1.Hash + size int64 + compressedopener Opener + uncompressedopener Opener + compression int + annotations map[string]string + estgzopts []estargz.Option + mediaType types.MediaType +} + +// Descriptor implements partial.withDescriptor. +func (l *layer) Descriptor() (*v1.Descriptor, error) { + digest, err := l.Digest() + if err != nil { + return nil, err + } + return &v1.Descriptor{ + Size: l.size, + Digest: digest, + Annotations: l.annotations, + MediaType: l.mediaType, + }, nil +} + +// Digest implements v1.Layer +func (l *layer) Digest() (v1.Hash, error) { + return l.digest, nil +} + +// DiffID implements v1.Layer +func (l *layer) DiffID() (v1.Hash, error) { + return l.diffID, nil +} + +// Compressed implements v1.Layer +func (l *layer) Compressed() (io.ReadCloser, error) { + return l.compressedopener() +} + +// Uncompressed implements v1.Layer +func (l *layer) Uncompressed() (io.ReadCloser, error) { + return l.uncompressedopener() +} + +// Size implements v1.Layer +func (l *layer) Size() (int64, error) { + return l.size, nil +} + +// MediaType implements v1.Layer +func (l *layer) MediaType() (types.MediaType, error) { + return l.mediaType, nil +} + +// LayerOption applies options to layer +type LayerOption func(*layer) + +// WithCompressionLevel is a functional option for overriding the default +// compression level used for compressing uncompressed tarballs. +func WithCompressionLevel(level int) LayerOption { + return func(l *layer) { + l.compression = level + } +} + +// WithMediaType is a functional option for overriding the layer's media type. +func WithMediaType(mt types.MediaType) LayerOption { + return func(l *layer) { + l.mediaType = mt + } +} + +// WithCompressedCaching is a functional option that overrides the +// logic for accessing the compressed bytes to memoize the result +// and avoid expensive repeated gzips. +func WithCompressedCaching(l *layer) { + var once sync.Once + var err error + + buf := bytes.NewBuffer(nil) + og := l.compressedopener + + l.compressedopener = func() (io.ReadCloser, error) { + once.Do(func() { + var rc io.ReadCloser + rc, err = og() + if err == nil { + defer rc.Close() + _, err = io.Copy(buf, rc) + } + }) + if err != nil { + return nil, err + } + + return ioutil.NopCloser(bytes.NewBuffer(buf.Bytes())), nil + } +} + +// WithEstargzOptions is a functional option that allow the caller to pass +// through estargz.Options to the underlying compression layer. This is +// only meaningful when estargz is enabled. +func WithEstargzOptions(opts ...estargz.Option) LayerOption { + return func(l *layer) { + l.estgzopts = opts + } +} + +// WithEstargz is a functional option that explicitly enables estargz support. +func WithEstargz(l *layer) { + oguncompressed := l.uncompressedopener + estargz := func() (io.ReadCloser, error) { + crc, err := oguncompressed() + if err != nil { + return nil, err + } + eopts := append(l.estgzopts, estargz.WithCompressionLevel(l.compression)) + rc, h, err := gestargz.ReadCloser(crc, eopts...) + if err != nil { + return nil, err + } + l.annotations[estargz.TOCJSONDigestAnnotation] = h.String() + return &and.ReadCloser{ + Reader: rc, + CloseFunc: func() error { + err := rc.Close() + if err != nil { + return err + } + // As an optimization, leverage the DiffID exposed by the estargz ReadCloser + l.diffID, err = v1.NewHash(rc.DiffID().String()) + return err + }, + }, nil + } + uncompressed := func() (io.ReadCloser, error) { + urc, err := estargz() + if err != nil { + return nil, err + } + return ggzip.UnzipReadCloser(urc) + } + + l.compressedopener = estargz + l.uncompressedopener = uncompressed +} + +// LayerFromFile returns a v1.Layer given a tarball +func LayerFromFile(path string, opts ...LayerOption) (v1.Layer, error) { + opener := func() (io.ReadCloser, error) { + return os.Open(path) + } + return LayerFromOpener(opener, opts...) +} + +// LayerFromOpener returns a v1.Layer given an Opener function. +// The Opener may return either an uncompressed tarball (common), +// or a compressed tarball (uncommon). +// +// When using this in conjunction with something like remote.Write +// the uncompressed path may end up gzipping things multiple times: +// 1. Compute the layer SHA256 +// 2. Upload the compressed layer. +// Since gzip can be expensive, we support an option to memoize the +// compression that can be passed here: tarball.WithCompressedCaching +func LayerFromOpener(opener Opener, opts ...LayerOption) (v1.Layer, error) { + rc, err := opener() + if err != nil { + return nil, err + } + defer rc.Close() + + compressed, err := ggzip.Is(rc) + if err != nil { + return nil, err + } + + layer := &layer{ + compression: gzip.BestSpeed, + annotations: make(map[string]string, 1), + mediaType: types.DockerLayer, + } + + if estgz := os.Getenv("GGCR_EXPERIMENT_ESTARGZ"); estgz == "1" { + opts = append([]LayerOption{WithEstargz}, opts...) + } + + if compressed { + layer.compressedopener = opener + layer.uncompressedopener = func() (io.ReadCloser, error) { + urc, err := opener() + if err != nil { + return nil, err + } + return ggzip.UnzipReadCloser(urc) + } + } else { + layer.uncompressedopener = opener + layer.compressedopener = func() (io.ReadCloser, error) { + crc, err := opener() + if err != nil { + return nil, err + } + return ggzip.ReadCloserLevel(crc, layer.compression), nil + } + } + + for _, opt := range opts { + opt(layer) + } + + if layer.digest, layer.size, err = computeDigest(layer.compressedopener); err != nil { + return nil, err + } + + empty := v1.Hash{} + if layer.diffID == empty { + if layer.diffID, err = computeDiffID(layer.uncompressedopener); err != nil { + return nil, err + } + } + + return layer, nil +} + +// LayerFromReader returns a v1.Layer given a io.Reader. +// +// The reader's contents are read and buffered to a temp file in the process. +// +// Deprecated: Use LayerFromOpener or stream.NewLayer instead, if possible. +func LayerFromReader(reader io.Reader, opts ...LayerOption) (v1.Layer, error) { + tmp, err := ioutil.TempFile("", "") + if err != nil { + return nil, fmt.Errorf("creating temp file to buffer reader: %w", err) + } + if _, err := io.Copy(tmp, reader); err != nil { + return nil, fmt.Errorf("writing temp file to buffer reader: %w", err) + } + return LayerFromFile(tmp.Name(), opts...) +} + +func computeDigest(opener Opener) (v1.Hash, int64, error) { + rc, err := opener() + if err != nil { + return v1.Hash{}, 0, err + } + defer rc.Close() + + return v1.SHA256(rc) +} + +func computeDiffID(opener Opener) (v1.Hash, error) { + rc, err := opener() + if err != nil { + return v1.Hash{}, err + } + defer rc.Close() + + digest, _, err := v1.SHA256(rc) + return digest, err +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/tarball/write.go b/vendor/github.com/google/go-containerregistry/pkg/v1/tarball/write.go new file mode 100644 index 0000000000..add390df88 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/tarball/write.go @@ -0,0 +1,453 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package tarball + +import ( + "archive/tar" + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "sort" + "strings" + + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" +) + +// WriteToFile writes in the compressed format to a tarball, on disk. +// This is just syntactic sugar wrapping tarball.Write with a new file. +func WriteToFile(p string, ref name.Reference, img v1.Image, opts ...WriteOption) error { + w, err := os.Create(p) + if err != nil { + return err + } + defer w.Close() + + return Write(ref, img, w, opts...) +} + +// MultiWriteToFile writes in the compressed format to a tarball, on disk. +// This is just syntactic sugar wrapping tarball.MultiWrite with a new file. +func MultiWriteToFile(p string, tagToImage map[name.Tag]v1.Image, opts ...WriteOption) error { + refToImage := make(map[name.Reference]v1.Image, len(tagToImage)) + for i, d := range tagToImage { + refToImage[i] = d + } + return MultiRefWriteToFile(p, refToImage, opts...) +} + +// MultiRefWriteToFile writes in the compressed format to a tarball, on disk. +// This is just syntactic sugar wrapping tarball.MultiRefWrite with a new file. +func MultiRefWriteToFile(p string, refToImage map[name.Reference]v1.Image, opts ...WriteOption) error { + w, err := os.Create(p) + if err != nil { + return err + } + defer w.Close() + + return MultiRefWrite(refToImage, w, opts...) +} + +// Write is a wrapper to write a single image and tag to a tarball. +func Write(ref name.Reference, img v1.Image, w io.Writer, opts ...WriteOption) error { + return MultiRefWrite(map[name.Reference]v1.Image{ref: img}, w, opts...) +} + +// MultiWrite writes the contents of each image to the provided reader, in the compressed format. +// The contents are written in the following format: +// One manifest.json file at the top level containing information about several images. +// One file for each layer, named after the layer's SHA. +// One file for the config blob, named after its SHA. +func MultiWrite(tagToImage map[name.Tag]v1.Image, w io.Writer, opts ...WriteOption) error { + refToImage := make(map[name.Reference]v1.Image, len(tagToImage)) + for i, d := range tagToImage { + refToImage[i] = d + } + return MultiRefWrite(refToImage, w, opts...) +} + +// MultiRefWrite writes the contents of each image to the provided reader, in the compressed format. +// The contents are written in the following format: +// One manifest.json file at the top level containing information about several images. +// One file for each layer, named after the layer's SHA. +// One file for the config blob, named after its SHA. +func MultiRefWrite(refToImage map[name.Reference]v1.Image, w io.Writer, opts ...WriteOption) error { + // process options + o := &writeOptions{ + updates: nil, + } + for _, option := range opts { + if err := option(o); err != nil { + return err + } + } + + size, mBytes, err := getSizeAndManifest(refToImage) + if err != nil { + return sendUpdateReturn(o, err) + } + + return writeImagesToTar(refToImage, mBytes, size, w, o) +} + +// sendUpdateReturn return the passed in error message, also sending on update channel, if it exists +func sendUpdateReturn(o *writeOptions, err error) error { + if o != nil && o.updates != nil { + o.updates <- v1.Update{ + Error: err, + } + } + return err +} + +// sendProgressWriterReturn return the passed in error message, also sending on update channel, if it exists, along with downloaded information +func sendProgressWriterReturn(pw *progressWriter, err error) error { + if pw != nil { + return pw.Error(err) + } + return err +} + +// writeImagesToTar writes the images to the tarball +func writeImagesToTar(refToImage map[name.Reference]v1.Image, m []byte, size int64, w io.Writer, o *writeOptions) (err error) { + if w == nil { + return sendUpdateReturn(o, errors.New("must pass valid writer")) + } + imageToTags := dedupRefToImage(refToImage) + + tw := w + var pw *progressWriter + + // we only calculate the sizes and use a progressWriter if we were provided + // an option with a progress channel + if o != nil && o.updates != nil { + pw = &progressWriter{ + w: w, + updates: o.updates, + size: size, + } + tw = pw + } + + tf := tar.NewWriter(tw) + defer tf.Close() + + seenLayerDigests := make(map[string]struct{}) + + for img := range imageToTags { + // Write the config. + cfgName, err := img.ConfigName() + if err != nil { + return sendProgressWriterReturn(pw, err) + } + cfgBlob, err := img.RawConfigFile() + if err != nil { + return sendProgressWriterReturn(pw, err) + } + if err := writeTarEntry(tf, cfgName.String(), bytes.NewReader(cfgBlob), int64(len(cfgBlob))); err != nil { + return sendProgressWriterReturn(pw, err) + } + + // Write the layers. + layers, err := img.Layers() + if err != nil { + return sendProgressWriterReturn(pw, err) + } + layerFiles := make([]string, len(layers)) + for i, l := range layers { + d, err := l.Digest() + if err != nil { + return sendProgressWriterReturn(pw, err) + } + // Munge the file name to appease ancient technology. + // + // tar assumes anything with a colon is a remote tape drive: + // https://www.gnu.org/software/tar/manual/html_section/tar_45.html + // Drop the algorithm prefix, e.g. "sha256:" + hex := d.Hex + + // gunzip expects certain file extensions: + // https://www.gnu.org/software/gzip/manual/html_node/Overview.html + layerFiles[i] = fmt.Sprintf("%s.tar.gz", hex) + + if _, ok := seenLayerDigests[hex]; ok { + continue + } + seenLayerDigests[hex] = struct{}{} + + r, err := l.Compressed() + if err != nil { + return sendProgressWriterReturn(pw, err) + } + blobSize, err := l.Size() + if err != nil { + return sendProgressWriterReturn(pw, err) + } + + if err := writeTarEntry(tf, layerFiles[i], r, blobSize); err != nil { + return sendProgressWriterReturn(pw, err) + } + } + } + if err := writeTarEntry(tf, "manifest.json", bytes.NewReader(m), int64(len(m))); err != nil { + return sendProgressWriterReturn(pw, err) + } + + // be sure to close the tar writer so everything is flushed out before we send our EOF + if err := tf.Close(); err != nil { + return sendProgressWriterReturn(pw, err) + } + // send an EOF to indicate finished on the channel, but nil as our return error + _ = sendProgressWriterReturn(pw, io.EOF) + return nil +} + +// calculateManifest calculates the manifest and optionally the size of the tar file +func calculateManifest(refToImage map[name.Reference]v1.Image) (m Manifest, err error) { + imageToTags := dedupRefToImage(refToImage) + + if len(imageToTags) == 0 { + return nil, errors.New("set of images is empty") + } + + for img, tags := range imageToTags { + cfgName, err := img.ConfigName() + if err != nil { + return nil, err + } + + // Store foreign layer info. + layerSources := make(map[v1.Hash]v1.Descriptor) + + // Write the layers. + layers, err := img.Layers() + if err != nil { + return nil, err + } + layerFiles := make([]string, len(layers)) + for i, l := range layers { + d, err := l.Digest() + if err != nil { + return nil, err + } + // Munge the file name to appease ancient technology. + // + // tar assumes anything with a colon is a remote tape drive: + // https://www.gnu.org/software/tar/manual/html_section/tar_45.html + // Drop the algorithm prefix, e.g. "sha256:" + hex := d.Hex + + // gunzip expects certain file extensions: + // https://www.gnu.org/software/gzip/manual/html_node/Overview.html + layerFiles[i] = fmt.Sprintf("%s.tar.gz", hex) + + // Add to LayerSources if it's a foreign layer. + desc, err := partial.BlobDescriptor(img, d) + if err != nil { + return nil, err + } + if !desc.MediaType.IsDistributable() { + diffid, err := partial.BlobToDiffID(img, d) + if err != nil { + return nil, err + } + layerSources[diffid] = *desc + } + } + + // Generate the tar descriptor and write it. + m = append(m, Descriptor{ + Config: cfgName.String(), + RepoTags: tags, + Layers: layerFiles, + LayerSources: layerSources, + }) + } + // sort by name of the repotags so it is consistent. Alternatively, we could sort by hash of the + // descriptor, but that would make it hard for humans to process + sort.Slice(m, func(i, j int) bool { + return strings.Join(m[i].RepoTags, ",") < strings.Join(m[j].RepoTags, ",") + }) + + return m, nil +} + +// CalculateSize calculates the expected complete size of the output tar file +func CalculateSize(refToImage map[name.Reference]v1.Image) (size int64, err error) { + size, _, err = getSizeAndManifest(refToImage) + return size, err +} + +func getSizeAndManifest(refToImage map[name.Reference]v1.Image) (int64, []byte, error) { + m, err := calculateManifest(refToImage) + if err != nil { + return 0, nil, fmt.Errorf("unable to calculate manifest: %w", err) + } + mBytes, err := json.Marshal(m) + if err != nil { + return 0, nil, fmt.Errorf("could not marshall manifest to bytes: %w", err) + } + + size, err := calculateTarballSize(refToImage, mBytes) + if err != nil { + return 0, nil, fmt.Errorf("error calculating tarball size: %w", err) + } + return size, mBytes, nil +} + +// calculateTarballSize calculates the size of the tar file +func calculateTarballSize(refToImage map[name.Reference]v1.Image, mBytes []byte) (size int64, err error) { + imageToTags := dedupRefToImage(refToImage) + + for img, name := range imageToTags { + manifest, err := img.Manifest() + if err != nil { + return size, fmt.Errorf("unable to get manifest for img %s: %w", name, err) + } + size += calculateSingleFileInTarSize(manifest.Config.Size) + for _, l := range manifest.Layers { + size += calculateSingleFileInTarSize(l.Size) + } + } + // add the manifest + size += calculateSingleFileInTarSize(int64(len(mBytes))) + + // add the two padding blocks that indicate end of a tar file + size += 1024 + return size, nil +} + +func dedupRefToImage(refToImage map[name.Reference]v1.Image) map[v1.Image][]string { + imageToTags := make(map[v1.Image][]string) + + for ref, img := range refToImage { + if tag, ok := ref.(name.Tag); ok { + if tags, ok := imageToTags[img]; !ok || tags == nil { + imageToTags[img] = []string{} + } + // Docker cannot load tarballs without an explicit tag: + // https://github.com/google/go-containerregistry/issues/890 + // + // We can't use the fully qualified tag.Name() because of rules_docker: + // https://github.com/google/go-containerregistry/issues/527 + // + // If the tag is "latest", but tag.String() doesn't end in ":latest", + // just append it. Kind of gross, but should work for now. + ts := tag.String() + if tag.Identifier() == name.DefaultTag && !strings.HasSuffix(ts, ":"+name.DefaultTag) { + ts = fmt.Sprintf("%s:%s", ts, name.DefaultTag) + } + imageToTags[img] = append(imageToTags[img], ts) + } else if _, ok := imageToTags[img]; !ok { + imageToTags[img] = nil + } + } + + return imageToTags +} + +// writeTarEntry writes a file to the provided writer with a corresponding tar header +func writeTarEntry(tf *tar.Writer, path string, r io.Reader, size int64) error { + hdr := &tar.Header{ + Mode: 0644, + Typeflag: tar.TypeReg, + Size: size, + Name: path, + } + if err := tf.WriteHeader(hdr); err != nil { + return err + } + _, err := io.Copy(tf, r) + return err +} + +// ComputeManifest get the manifest.json that will be written to the tarball +// for multiple references +func ComputeManifest(refToImage map[name.Reference]v1.Image) (Manifest, error) { + return calculateManifest(refToImage) +} + +// WriteOption a function option to pass to Write() +type WriteOption func(*writeOptions) error +type writeOptions struct { + updates chan<- v1.Update +} + +// WithProgress create a WriteOption for passing to Write() that enables +// a channel to receive updates as they are downloaded and written to disk. +func WithProgress(updates chan<- v1.Update) WriteOption { + return func(o *writeOptions) error { + o.updates = updates + return nil + } +} + +// progressWriter is a writer which will send the download progress +type progressWriter struct { + w io.Writer + updates chan<- v1.Update + size, complete int64 +} + +func (pw *progressWriter) Write(p []byte) (int, error) { + n, err := pw.w.Write(p) + if err != nil { + return n, err + } + + pw.complete += int64(n) + + pw.updates <- v1.Update{ + Total: pw.size, + Complete: pw.complete, + } + + return n, err +} + +func (pw *progressWriter) Error(err error) error { + pw.updates <- v1.Update{ + Total: pw.size, + Complete: pw.complete, + Error: err, + } + return err +} + +func (pw *progressWriter) Close() error { + pw.updates <- v1.Update{ + Total: pw.size, + Complete: pw.complete, + Error: io.EOF, + } + return io.EOF +} + +// calculateSingleFileInTarSize calculate the size a file will take up in a tar archive, +// given the input data. Provided by rounding up to nearest whole block (512) +// and adding header 512 +func calculateSingleFileInTarSize(in int64) (out int64) { + // doing this manually, because math.Round() works with float64 + out += in + if remainder := out % 512; remainder != 0 { + out += (512 - remainder) + } + out += 512 + return out +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/types/types.go b/vendor/github.com/google/go-containerregistry/pkg/v1/types/types.go new file mode 100644 index 0000000000..21f2236502 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/types/types.go @@ -0,0 +1,71 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +package types + +// MediaType is an enumeration of the supported mime types that an element of an image might have. +type MediaType string + +// The collection of known MediaType values. +const ( + OCIContentDescriptor MediaType = "application/vnd.oci.descriptor.v1+json" + OCIImageIndex MediaType = "application/vnd.oci.image.index.v1+json" + OCIManifestSchema1 MediaType = "application/vnd.oci.image.manifest.v1+json" + OCIConfigJSON MediaType = "application/vnd.oci.image.config.v1+json" + OCILayer MediaType = "application/vnd.oci.image.layer.v1.tar+gzip" + OCIRestrictedLayer MediaType = "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip" + OCIUncompressedLayer MediaType = "application/vnd.oci.image.layer.v1.tar" + OCIUncompressedRestrictedLayer MediaType = "application/vnd.oci.image.layer.nondistributable.v1.tar" + + DockerManifestSchema1 MediaType = "application/vnd.docker.distribution.manifest.v1+json" + DockerManifestSchema1Signed MediaType = "application/vnd.docker.distribution.manifest.v1+prettyjws" + DockerManifestSchema2 MediaType = "application/vnd.docker.distribution.manifest.v2+json" + DockerManifestList MediaType = "application/vnd.docker.distribution.manifest.list.v2+json" + DockerLayer MediaType = "application/vnd.docker.image.rootfs.diff.tar.gzip" + DockerConfigJSON MediaType = "application/vnd.docker.container.image.v1+json" + DockerPluginConfig MediaType = "application/vnd.docker.plugin.v1+json" + DockerForeignLayer MediaType = "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip" + DockerUncompressedLayer MediaType = "application/vnd.docker.image.rootfs.diff.tar" + + OCIVendorPrefix = "vnd.oci" + DockerVendorPrefix = "vnd.docker" +) + +// IsDistributable returns true if a layer is distributable, see: +// https://github.com/opencontainers/image-spec/blob/master/layer.md#non-distributable-layers +func (m MediaType) IsDistributable() bool { + switch m { + case DockerForeignLayer, OCIRestrictedLayer, OCIUncompressedRestrictedLayer: + return false + } + return true +} + +// IsImage returns true if the mediaType represents an image manifest, as opposed to something else, like an index. +func (m MediaType) IsImage() bool { + switch m { + case OCIManifestSchema1, DockerManifestSchema2: + return true + } + return false +} + +// IsIndex returns true if the mediaType represents an index, as opposed to something else, like an image. +func (m MediaType) IsIndex() bool { + switch m { + case OCIImageIndex, DockerManifestList: + return true + } + return false +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/zz_deepcopy_generated.go b/vendor/github.com/google/go-containerregistry/pkg/v1/zz_deepcopy_generated.go new file mode 100644 index 0000000000..0cb1586f1e --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/zz_deepcopy_generated.go @@ -0,0 +1,324 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1 + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + if in.Cmd != nil { + in, out := &in.Cmd, &out.Cmd + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Healthcheck != nil { + in, out := &in.Healthcheck, &out.Healthcheck + *out = new(HealthConfig) + (*in).DeepCopyInto(*out) + } + if in.Entrypoint != nil { + in, out := &in.Entrypoint, &out.Entrypoint + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.OnBuild != nil { + in, out := &in.OnBuild, &out.OnBuild + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make(map[string]struct{}, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.ExposedPorts != nil { + in, out := &in.ExposedPorts, &out.ExposedPorts + *out = make(map[string]struct{}, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Shell != nil { + in, out := &in.Shell, &out.Shell + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigFile) DeepCopyInto(out *ConfigFile) { + *out = *in + in.Created.DeepCopyInto(&out.Created) + if in.History != nil { + in, out := &in.History, &out.History + *out = make([]History, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.RootFS.DeepCopyInto(&out.RootFS) + in.Config.DeepCopyInto(&out.Config) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigFile. +func (in *ConfigFile) DeepCopy() *ConfigFile { + if in == nil { + return nil + } + out := new(ConfigFile) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Descriptor) DeepCopyInto(out *Descriptor) { + *out = *in + out.Digest = in.Digest + if in.Data != nil { + in, out := &in.Data, &out.Data + *out = make([]byte, len(*in)) + copy(*out, *in) + } + if in.URLs != nil { + in, out := &in.URLs, &out.URLs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Platform != nil { + in, out := &in.Platform, &out.Platform + *out = new(Platform) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Descriptor. +func (in *Descriptor) DeepCopy() *Descriptor { + if in == nil { + return nil + } + out := new(Descriptor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Hash) DeepCopyInto(out *Hash) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Hash. +func (in *Hash) DeepCopy() *Hash { + if in == nil { + return nil + } + out := new(Hash) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HealthConfig) DeepCopyInto(out *HealthConfig) { + *out = *in + if in.Test != nil { + in, out := &in.Test, &out.Test + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HealthConfig. +func (in *HealthConfig) DeepCopy() *HealthConfig { + if in == nil { + return nil + } + out := new(HealthConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *History) DeepCopyInto(out *History) { + *out = *in + in.Created.DeepCopyInto(&out.Created) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new History. +func (in *History) DeepCopy() *History { + if in == nil { + return nil + } + out := new(History) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IndexManifest) DeepCopyInto(out *IndexManifest) { + *out = *in + if in.Manifests != nil { + in, out := &in.Manifests, &out.Manifests + *out = make([]Descriptor, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IndexManifest. +func (in *IndexManifest) DeepCopy() *IndexManifest { + if in == nil { + return nil + } + out := new(IndexManifest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Manifest) DeepCopyInto(out *Manifest) { + *out = *in + in.Config.DeepCopyInto(&out.Config) + if in.Layers != nil { + in, out := &in.Layers, &out.Layers + *out = make([]Descriptor, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Manifest. +func (in *Manifest) DeepCopy() *Manifest { + if in == nil { + return nil + } + out := new(Manifest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Platform) DeepCopyInto(out *Platform) { + *out = *in + if in.OSFeatures != nil { + in, out := &in.OSFeatures, &out.OSFeatures + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Features != nil { + in, out := &in.Features, &out.Features + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Platform. +func (in *Platform) DeepCopy() *Platform { + if in == nil { + return nil + } + out := new(Platform) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RootFS) DeepCopyInto(out *RootFS) { + *out = *in + if in.DiffIDs != nil { + in, out := &in.DiffIDs, &out.DiffIDs + *out = make([]Hash, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RootFS. +func (in *RootFS) DeepCopy() *RootFS { + if in == nil { + return nil + } + out := new(RootFS) + in.DeepCopyInto(out) + return out +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Time. +func (in *Time) DeepCopy() *Time { + if in == nil { + return nil + } + out := new(Time) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/hako/durafmt/.gitignore b/vendor/github.com/hako/durafmt/.gitignore new file mode 100644 index 0000000000..daf913b1b3 --- /dev/null +++ b/vendor/github.com/hako/durafmt/.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/vendor/github.com/hako/durafmt/.travis.yml b/vendor/github.com/hako/durafmt/.travis.yml new file mode 100644 index 0000000000..77f8c12ca6 --- /dev/null +++ b/vendor/github.com/hako/durafmt/.travis.yml @@ -0,0 +1,21 @@ +language: go + +go: + - 1.5 + - 1.6 + - 1.7 + - 1.8 + - 1.9 + - tip + +before_install: + - go get golang.org/x/tools/cmd/cover + +script: + - GOARCH=386 go test # test 32bit architectures. + - go test -coverprofile=coverage.txt -covermode=atomic + +after_success: + - bash <(curl -s https://codecov.io/bash) + +sudo: false diff --git a/vendor/github.com/hako/durafmt/CODE_OF_CONDUCT.md b/vendor/github.com/hako/durafmt/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..19a95e513c --- /dev/null +++ b/vendor/github.com/hako/durafmt/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at wesley@hakobaito.co.uk. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/hako/durafmt/CONTRIBUTING.md b/vendor/github.com/hako/durafmt/CONTRIBUTING.md new file mode 100644 index 0000000000..bdfb376fcd --- /dev/null +++ b/vendor/github.com/hako/durafmt/CONTRIBUTING.md @@ -0,0 +1,9 @@ +# Contributing + +Contributions are welcome! Fork this repo and add your changes and submit a PR. + +If you would like to fix a bug, add a feature or provide feedback you can do so in the issues section. + +You can run tests by runnning `go test`. Running `go test; go vet; golint` is recommended. + +durafmt is also tested against `gometalinter`. diff --git a/vendor/github.com/hako/durafmt/LICENSE b/vendor/github.com/hako/durafmt/LICENSE new file mode 100644 index 0000000000..ccb595049d --- /dev/null +++ b/vendor/github.com/hako/durafmt/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Wesley Hill + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/hako/durafmt/README.md b/vendor/github.com/hako/durafmt/README.md new file mode 100644 index 0000000000..45448f514f --- /dev/null +++ b/vendor/github.com/hako/durafmt/README.md @@ -0,0 +1,154 @@ +# durafmt + +[![Build Status](https://travis-ci.org/hako/durafmt.svg?branch=master)](https://travis-ci.org/hako/durafmt) [![Go Report Card](https://goreportcard.com/badge/github.com/hako/durafmt)](https://goreportcard.com/report/github.com/hako/durafmt) [![codecov](https://codecov.io/gh/hako/durafmt/branch/master/graph/badge.svg)](https://codecov.io/gh/hako/durafmt) [![GoDoc](https://godoc.org/github.com/hako/durafmt?status.svg)](https://godoc.org/github.com/hako/durafmt) +[![Open Source Helpers](https://www.codetriage.com/hako/durafmt/badges/users.svg)](https://www.codetriage.com/hako/durafmt) + + + +durafmt is a tiny Go library that formats `time.Duration` strings (and types) into a human readable format. + +``` +go get github.com/hako/durafmt +``` + +# Why + +If you've worked with `time.Duration` in Go, you most likely have come across this: + +``` +53m28.587093086s // :) +``` + +The above seems very easy to read, unless your duration looks like this: + +``` +354h22m3.24s // :S +``` + +# Usage + +### durafmt.ParseString() + +```go +package main + +import ( + "fmt" + "github.com/hako/durafmt" +) + +func main() { + duration, err := durafmt.ParseString("354h22m3.24s") + if err != nil { + fmt.Println(err) + } + fmt.Println(duration) // 2 weeks 18 hours 22 minutes 3 seconds + // duration.String() // String representation. "2 weeks 18 hours 22 minutes 3 seconds" +} +``` + +### durafmt.ParseStringShort() + +Version of `durafmt.ParseString()` that only returns the first part of the duration string. + +```go +package main + +import ( + "fmt" + "github.com/hako/durafmt" +) + +func main() { + duration, err := durafmt.ParseStringShort("354h22m3.24s") + if err != nil { + fmt.Println(err) + } + fmt.Println(duration) // 2 weeks + // duration.String() // String short representation. "2 weeks" +} +``` + +### durafmt.Parse() + +```go +package main + +import ( + "fmt" + "time" + "github.com/hako/durafmt" +) + +func main() { + timeduration := (354 * time.Hour) + (22 * time.Minute) + (3 * time.Second) + duration := durafmt.Parse(timeduration).String() + fmt.Println(duration) // 2 weeks 18 hours 22 minutes 3 seconds +} +``` + +#### LimitFirstN() + +Like `durafmt.ParseStringShort()` but for limiting the first N parts of the duration string. + +```go +package main + +import ( + "fmt" + "time" + "github.com/hako/durafmt" +) + +func main() { + timeduration := (354 * time.Hour) + (22 * time.Minute) + (3 * time.Second) + duration := durafmt.Parse(timeduration).LimitFirstN(2) // // limit first two parts. + fmt.Println(duration) // 2 weeks 18 hours +} +``` + +#### Custom Units + +Like `durafmt.Units{}` and `durafmt.Durafmt.Format(units)` to stringify duration with custom units. + +```go +package main + +import ( + "fmt" + "time" + "github.com/hako/durafmt" +) + +func main() { + timeduration := (354 * time.Hour) + (22 * time.Minute) + (1 * time.Second) + (100*time.Microsecond) + duration := durafmt.Parse(timeduration) + // units in portuguese + units, err := durafmt.DefaultUnitsCoder.Decode("ano,semana,dia,hora,minuto,segundo,milissegundo,microssegundo") + if err != nil { + panic(err) + } + fmt.Println(duration.Format(units)) // 2 semanas 18 horas 22 minutos 1 segundo 100 microssegundos + + // custom plural (singular:plural) + units, err = durafmt.DefaultUnitsCoder.Decode("ano,semana:SEMANAS,dia,hora,minuto,segundo,milissegundo,microssegundo") + if err != nil { + panic(err) + } + fmt.Println(duration.Format(units)) // 2 SEMANAS 18 horas 22 minutos 1 segundo 100 microssegundos +} +``` + +# Contributing + +Contributions are welcome! Fork this repo, add your changes and submit a PR. + +If you would like to fix a bug, add a feature or provide feedback you can do so in the issues section. + +durafmt is tested against `golangci-lint` and you can run tests with `go test`. + +When contributing, running `go test; go vet; golint` or `golangci-lint` is recommended. + +# License + +MIT diff --git a/vendor/github.com/hako/durafmt/durafmt.go b/vendor/github.com/hako/durafmt/durafmt.go new file mode 100644 index 0000000000..128726ed26 --- /dev/null +++ b/vendor/github.com/hako/durafmt/durafmt.go @@ -0,0 +1,330 @@ +// Package durafmt formats time.Duration into a human readable format. +package durafmt + +import ( + "errors" + "fmt" + "regexp" + "strconv" + "strings" + "time" +) + +var ( + units, _ = DefaultUnitsCoder.Decode("year,week,day,hour,minute,second,millisecond,microsecond") + unitsShort = []string{"y", "w", "d", "h", "m", "s", "ms", "µs"} +) + +// Durafmt holds the parsed duration and the original input duration. +type Durafmt struct { + duration time.Duration + input string // Used as reference. + limitN int // Non-zero to limit only first N elements to output. + limitUnit string // Non-empty to limit max unit +} + +// LimitToUnit sets the output format, you will not have unit bigger than the UNIT specified. UNIT = "" means no restriction. +func (d *Durafmt) LimitToUnit(unit string) *Durafmt { + d.limitUnit = unit + return d +} + +// LimitFirstN sets the output format, outputing only first N elements. n == 0 means no limit. +func (d *Durafmt) LimitFirstN(n int) *Durafmt { + d.limitN = n + return d +} + +func (d *Durafmt) Duration() time.Duration { + return d.duration +} + +// Parse creates a new *Durafmt struct, returns error if input is invalid. +func Parse(dinput time.Duration) *Durafmt { + input := dinput.String() + return &Durafmt{dinput, input, 0, ""} +} + +// ParseShort creates a new *Durafmt struct, short form, returns error if input is invalid. +// It's shortcut for `Parse(dur).LimitFirstN(1)` +func ParseShort(dinput time.Duration) *Durafmt { + input := dinput.String() + return &Durafmt{dinput, input, 1, ""} +} + +// ParseString creates a new *Durafmt struct from a string. +// returns an error if input is invalid. +func ParseString(input string) (*Durafmt, error) { + if input == "0" || input == "-0" { + return nil, errors.New("durafmt: missing unit in duration " + input) + } + duration, err := time.ParseDuration(input) + if err != nil { + return nil, err + } + return &Durafmt{duration, input, 0, ""}, nil +} + +// ParseStringShort creates a new *Durafmt struct from a string, short form +// returns an error if input is invalid. +// It's shortcut for `ParseString(durStr)` and then calling `LimitFirstN(1)` +func ParseStringShort(input string) (*Durafmt, error) { + if input == "0" || input == "-0" { + return nil, errors.New("durafmt: missing unit in duration " + input) + } + duration, err := time.ParseDuration(input) + if err != nil { + return nil, err + } + return &Durafmt{duration, input, 1, ""}, nil +} + +// String parses d *Durafmt into a human readable duration with default units. +func (d *Durafmt) String() string { + return d.Format(units) +} + +// Format parses d *Durafmt into a human readable duration with units. +func (d *Durafmt) Format(units Units) string { + var duration string + + // Check for minus durations. + if string(d.input[0]) == "-" { + duration += "-" + d.duration = -d.duration + } + + var microseconds int64 + var milliseconds int64 + var seconds int64 + var minutes int64 + var hours int64 + var days int64 + var weeks int64 + var years int64 + var shouldConvert = false + + remainingSecondsToConvert := int64(d.duration / time.Microsecond) + + // Convert duration. + if d.limitUnit == "" { + shouldConvert = true + } + + if d.limitUnit == "years" || shouldConvert { + years = remainingSecondsToConvert / (365 * 24 * 3600 * 1000000) + remainingSecondsToConvert -= years * 365 * 24 * 3600 * 1000000 + shouldConvert = true + } + + if d.limitUnit == "weeks" || shouldConvert { + weeks = remainingSecondsToConvert / (7 * 24 * 3600 * 1000000) + remainingSecondsToConvert -= weeks * 7 * 24 * 3600 * 1000000 + shouldConvert = true + } + + if d.limitUnit == "days" || shouldConvert { + days = remainingSecondsToConvert / (24 * 3600 * 1000000) + remainingSecondsToConvert -= days * 24 * 3600 * 1000000 + shouldConvert = true + } + + if d.limitUnit == "hours" || shouldConvert { + hours = remainingSecondsToConvert / (3600 * 1000000) + remainingSecondsToConvert -= hours * 3600 * 1000000 + shouldConvert = true + } + + if d.limitUnit == "minutes" || shouldConvert { + minutes = remainingSecondsToConvert / (60 * 1000000) + remainingSecondsToConvert -= minutes * 60 * 1000000 + shouldConvert = true + } + + if d.limitUnit == "seconds" || shouldConvert { + seconds = remainingSecondsToConvert / 1000000 + remainingSecondsToConvert -= seconds * 1000000 + shouldConvert = true + } + + if d.limitUnit == "milliseconds" || shouldConvert { + milliseconds = remainingSecondsToConvert / 1000 + remainingSecondsToConvert -= milliseconds * 1000 + } + + microseconds = remainingSecondsToConvert + + // Create a map of the converted duration time. + durationMap := []int64{ + microseconds, + milliseconds, + seconds, + minutes, + hours, + days, + weeks, + years, + } + + // Construct duration string. + for i, u := range units.Units() { + v := durationMap[7-i] + strval := strconv.FormatInt(v, 10) + switch { + // add to the duration string if v > 1. + case v > 1: + duration += strval + " " + u.Plural + " " + // remove the plural 's', if v is 1. + case v == 1: + duration += strval + " " + u.Singular + " " + // omit any value with 0s or 0. + case d.duration.String() == "0" || d.duration.String() == "0s": + pattern := fmt.Sprintf("^-?0%s$", unitsShort[i]) + isMatch, err := regexp.MatchString(pattern, d.input) + if err != nil { + return "" + } + if isMatch { + duration += strval + " " + u.Plural + } + + // omit any value with 0. + case v == 0: + continue + } + } + // trim any remaining spaces. + duration = strings.TrimSpace(duration) + + // if more than 2 spaces present return the first 2 strings + // if short version is requested + if d.limitN > 0 { + parts := strings.Split(duration, " ") + if len(parts) > d.limitN*2 { + duration = strings.Join(parts[:d.limitN*2], " ") + } + } + + return duration +} + +func (d *Durafmt) InternationalString() string { + var duration string + + // Check for minus durations. + if string(d.input[0]) == "-" { + duration += "-" + d.duration = -d.duration + } + + var microseconds int64 + var milliseconds int64 + var seconds int64 + var minutes int64 + var hours int64 + var days int64 + var weeks int64 + var years int64 + var shouldConvert = false + + remainingSecondsToConvert := int64(d.duration / time.Microsecond) + + // Convert duration. + if d.limitUnit == "" { + shouldConvert = true + } + + if d.limitUnit == "years" || shouldConvert { + years = remainingSecondsToConvert / (365 * 24 * 3600 * 1000000) + remainingSecondsToConvert -= years * 365 * 24 * 3600 * 1000000 + shouldConvert = true + } + + if d.limitUnit == "weeks" || shouldConvert { + weeks = remainingSecondsToConvert / (7 * 24 * 3600 * 1000000) + remainingSecondsToConvert -= weeks * 7 * 24 * 3600 * 1000000 + shouldConvert = true + } + + if d.limitUnit == "days" || shouldConvert { + days = remainingSecondsToConvert / (24 * 3600 * 1000000) + remainingSecondsToConvert -= days * 24 * 3600 * 1000000 + shouldConvert = true + } + + if d.limitUnit == "hours" || shouldConvert { + hours = remainingSecondsToConvert / (3600 * 1000000) + remainingSecondsToConvert -= hours * 3600 * 1000000 + shouldConvert = true + } + + if d.limitUnit == "minutes" || shouldConvert { + minutes = remainingSecondsToConvert / (60 * 1000000) + remainingSecondsToConvert -= minutes * 60 * 1000000 + shouldConvert = true + } + + if d.limitUnit == "seconds" || shouldConvert { + seconds = remainingSecondsToConvert / 1000000 + remainingSecondsToConvert -= seconds * 1000000 + shouldConvert = true + } + + if d.limitUnit == "milliseconds" || shouldConvert { + milliseconds = remainingSecondsToConvert / 1000 + remainingSecondsToConvert -= milliseconds * 1000 + } + + microseconds = remainingSecondsToConvert + + // Create a map of the converted duration time. + durationMap := map[string]int64{ + "µs": microseconds, + "ms": milliseconds, + "s": seconds, + "m": minutes, + "h": hours, + "d": days, + "w": weeks, + "y": years, + } + + // Construct duration string. + for i := range units.Units() { + u := unitsShort[i] + v := durationMap[u] + strval := strconv.FormatInt(v, 10) + switch { + // add to the duration string if v > 0. + case v > 0: + duration += strval + " " + u + " " + // omit any value with 0. + case d.duration.String() == "0": + pattern := fmt.Sprintf("^-?0%s$", unitsShort[i]) + isMatch, err := regexp.MatchString(pattern, d.input) + if err != nil { + return "" + } + if isMatch { + duration += strval + " " + u + } + + // omit any value with 0. + case v == 0: + continue + } + } + // trim any remaining spaces. + duration = strings.TrimSpace(duration) + + // if more than 2 spaces present return the first 2 strings + // if short version is requested + if d.limitN > 0 { + parts := strings.Split(duration, " ") + if len(parts) > d.limitN*2 { + duration = strings.Join(parts[:d.limitN*2], " ") + } + } + + return duration +} diff --git a/vendor/github.com/hako/durafmt/go.mod b/vendor/github.com/hako/durafmt/go.mod new file mode 100644 index 0000000000..72a9f5b714 --- /dev/null +++ b/vendor/github.com/hako/durafmt/go.mod @@ -0,0 +1,3 @@ +module github.com/hako/durafmt + +go 1.11 diff --git a/vendor/github.com/hako/durafmt/unit.go b/vendor/github.com/hako/durafmt/unit.go new file mode 100644 index 0000000000..041f0b1b59 --- /dev/null +++ b/vendor/github.com/hako/durafmt/unit.go @@ -0,0 +1,108 @@ +package durafmt + +import ( + "fmt" + "strings" +) + +// DefaultUnitsCoder default units coder using `":"` as PluralSep and `","` as UnitsSep +var DefaultUnitsCoder = UnitsCoder{":", ","} + +// Unit the pair of singular and plural units +type Unit struct { + Singular, Plural string +} + +// Units duration units +type Units struct { + Year, Week, Day, Hour, Minute, + Second, Millisecond, Microsecond Unit +} + +// Units return a slice of units +func (u Units) Units() []Unit { + return []Unit{u.Year, u.Week, u.Day, u.Hour, u.Minute, + u.Second, u.Millisecond, u.Microsecond} +} + +// UnitsCoder the units encoder and decoder +type UnitsCoder struct { + // PluralSep char to sep singular and plural pair. + // Example with char `":"`: `"year:year"` (english) or `"mês:meses"` (portuguese) + PluralSep, + // UnitsSep char to sep units (singular and plural pairs). + // Example with char `","`: `"year:year,week:weeks"` (english) or `"mês:meses,semana:semanas"` (portuguese) + UnitsSep string +} + +// Encode encodes input Units to string +// Examples with `UnitsCoder{PluralSep: ":", UnitsSep = ","}` +// - singular and plural pair units: `"year:wers,week:weeks,day:days,hour:hours,minute:minutes,second:seconds,millisecond:millliseconds,microsecond:microsseconds"` +func (coder UnitsCoder) Encode(units Units) string { + var pairs = make([]string, 8) + for i, u := range units.Units() { + pairs[i] = u.Singular + coder.PluralSep + u.Plural + } + return strings.Join(pairs, coder.UnitsSep) +} + +// Decode decodes input string to Units. +// The input must follow the following formats: +// - Unit format (singular and plural pair) +// - must singular (the plural receives 's' character as suffix) +// - singular and plural: separated by `PluralSep` char +// Example with char `":"`: `"year:year"` (english) or `"mês:meses"` (portuguese) +// - Units format (pairs of Year, Week, Day, Hour, Minute, +// Second, Millisecond and Microsecond units) separated by `UnitsSep` char +// - Examples with `UnitsCoder{PluralSep: ":", UnitsSep = ","}` +// - must singular units: `"year,week,day,hour,minute,second,millisecond,microsecond"` +// - mixed units: `"year,week:weeks,day,hour,minute:minutes,second,millisecond,microsecond"` +// - singular and plural pair units: `"year:wers,week:weeks,day:days,hour:hours,minute:minutes,second:seconds,millisecond:millliseconds,microsecond:microsseconds"` +func (coder UnitsCoder) Decode(s string) (units Units, err error) { + parts := strings.Split(s, coder.UnitsSep) + if len(parts) != 8 { + err = fmt.Errorf("bad parts length") + return units, err + } + + var parse = func(name, part string, u *Unit) bool { + ps := strings.Split(part, coder.PluralSep) + switch len(ps) { + case 1: + // create plural form with sigular + 's' suffix + u.Singular, u.Plural = ps[0], ps[0]+"s" + case 2: + u.Singular, u.Plural = ps[0], ps[1] + default: + err = fmt.Errorf("bad unit %q pair length", name) + return false + } + return true + } + + if !parse("Year", parts[0], &units.Year) { + return units, err + } + if !parse("Week", parts[1], &units.Week) { + return units, err + } + if !parse("Day", parts[2], &units.Day) { + return units, err + } + if !parse("Hour", parts[3], &units.Hour) { + return units, err + } + if !parse("Minute", parts[4], &units.Minute) { + return units, err + } + if !parse("Second", parts[5], &units.Second) { + return units, err + } + if !parse("Millisecond", parts[6], &units.Millisecond) { + return units, err + } + if !parse("Microsecond", parts[7], &units.Microsecond) { + return units, err + } + return units, err +} diff --git a/vendor/github.com/hashicorp/errwrap/LICENSE b/vendor/github.com/hashicorp/errwrap/LICENSE new file mode 100644 index 0000000000..c33dcc7c92 --- /dev/null +++ b/vendor/github.com/hashicorp/errwrap/LICENSE @@ -0,0 +1,354 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. + diff --git a/vendor/github.com/hashicorp/errwrap/README.md b/vendor/github.com/hashicorp/errwrap/README.md new file mode 100644 index 0000000000..444df08f8e --- /dev/null +++ b/vendor/github.com/hashicorp/errwrap/README.md @@ -0,0 +1,89 @@ +# errwrap + +`errwrap` is a package for Go that formalizes the pattern of wrapping errors +and checking if an error contains another error. + +There is a common pattern in Go of taking a returned `error` value and +then wrapping it (such as with `fmt.Errorf`) before returning it. The problem +with this pattern is that you completely lose the original `error` structure. + +Arguably the _correct_ approach is that you should make a custom structure +implementing the `error` interface, and have the original error as a field +on that structure, such [as this example](http://golang.org/pkg/os/#PathError). +This is a good approach, but you have to know the entire chain of possible +rewrapping that happens, when you might just care about one. + +`errwrap` formalizes this pattern (it doesn't matter what approach you use +above) by giving a single interface for wrapping errors, checking if a specific +error is wrapped, and extracting that error. + +## Installation and Docs + +Install using `go get github.com/hashicorp/errwrap`. + +Full documentation is available at +http://godoc.org/github.com/hashicorp/errwrap + +## Usage + +#### Basic Usage + +Below is a very basic example of its usage: + +```go +// A function that always returns an error, but wraps it, like a real +// function might. +func tryOpen() error { + _, err := os.Open("/i/dont/exist") + if err != nil { + return errwrap.Wrapf("Doesn't exist: {{err}}", err) + } + + return nil +} + +func main() { + err := tryOpen() + + // We can use the Contains helpers to check if an error contains + // another error. It is safe to do this with a nil error, or with + // an error that doesn't even use the errwrap package. + if errwrap.Contains(err, "does not exist") { + // Do something + } + if errwrap.ContainsType(err, new(os.PathError)) { + // Do something + } + + // Or we can use the associated `Get` functions to just extract + // a specific error. This would return nil if that specific error doesn't + // exist. + perr := errwrap.GetType(err, new(os.PathError)) +} +``` + +#### Custom Types + +If you're already making custom types that properly wrap errors, then +you can get all the functionality of `errwraps.Contains` and such by +implementing the `Wrapper` interface with just one function. Example: + +```go +type AppError { + Code ErrorCode + Err error +} + +func (e *AppError) WrappedErrors() []error { + return []error{e.Err} +} +``` + +Now this works: + +```go +err := &AppError{Err: fmt.Errorf("an error")} +if errwrap.ContainsType(err, fmt.Errorf("")) { + // This will work! +} +``` diff --git a/vendor/github.com/hashicorp/errwrap/errwrap.go b/vendor/github.com/hashicorp/errwrap/errwrap.go new file mode 100644 index 0000000000..44e368e569 --- /dev/null +++ b/vendor/github.com/hashicorp/errwrap/errwrap.go @@ -0,0 +1,178 @@ +// Package errwrap implements methods to formalize error wrapping in Go. +// +// All of the top-level functions that take an `error` are built to be able +// to take any error, not just wrapped errors. This allows you to use errwrap +// without having to type-check and type-cast everywhere. +package errwrap + +import ( + "errors" + "reflect" + "strings" +) + +// WalkFunc is the callback called for Walk. +type WalkFunc func(error) + +// Wrapper is an interface that can be implemented by custom types to +// have all the Contains, Get, etc. functions in errwrap work. +// +// When Walk reaches a Wrapper, it will call the callback for every +// wrapped error in addition to the wrapper itself. Since all the top-level +// functions in errwrap use Walk, this means that all those functions work +// with your custom type. +type Wrapper interface { + WrappedErrors() []error +} + +// Wrap defines that outer wraps inner, returning an error type that +// can be cleanly used with the other methods in this package, such as +// Contains, GetAll, etc. +// +// This function won't modify the error message at all (the outer message +// will be used). +func Wrap(outer, inner error) error { + return &wrappedError{ + Outer: outer, + Inner: inner, + } +} + +// Wrapf wraps an error with a formatting message. This is similar to using +// `fmt.Errorf` to wrap an error. If you're using `fmt.Errorf` to wrap +// errors, you should replace it with this. +// +// format is the format of the error message. The string '{{err}}' will +// be replaced with the original error message. +// +// Deprecated: Use fmt.Errorf() +func Wrapf(format string, err error) error { + outerMsg := "" + if err != nil { + outerMsg = err.Error() + } + + outer := errors.New(strings.Replace( + format, "{{err}}", outerMsg, -1)) + + return Wrap(outer, err) +} + +// Contains checks if the given error contains an error with the +// message msg. If err is not a wrapped error, this will always return +// false unless the error itself happens to match this msg. +func Contains(err error, msg string) bool { + return len(GetAll(err, msg)) > 0 +} + +// ContainsType checks if the given error contains an error with +// the same concrete type as v. If err is not a wrapped error, this will +// check the err itself. +func ContainsType(err error, v interface{}) bool { + return len(GetAllType(err, v)) > 0 +} + +// Get is the same as GetAll but returns the deepest matching error. +func Get(err error, msg string) error { + es := GetAll(err, msg) + if len(es) > 0 { + return es[len(es)-1] + } + + return nil +} + +// GetType is the same as GetAllType but returns the deepest matching error. +func GetType(err error, v interface{}) error { + es := GetAllType(err, v) + if len(es) > 0 { + return es[len(es)-1] + } + + return nil +} + +// GetAll gets all the errors that might be wrapped in err with the +// given message. The order of the errors is such that the outermost +// matching error (the most recent wrap) is index zero, and so on. +func GetAll(err error, msg string) []error { + var result []error + + Walk(err, func(err error) { + if err.Error() == msg { + result = append(result, err) + } + }) + + return result +} + +// GetAllType gets all the errors that are the same type as v. +// +// The order of the return value is the same as described in GetAll. +func GetAllType(err error, v interface{}) []error { + var result []error + + var search string + if v != nil { + search = reflect.TypeOf(v).String() + } + Walk(err, func(err error) { + var needle string + if err != nil { + needle = reflect.TypeOf(err).String() + } + + if needle == search { + result = append(result, err) + } + }) + + return result +} + +// Walk walks all the wrapped errors in err and calls the callback. If +// err isn't a wrapped error, this will be called once for err. If err +// is a wrapped error, the callback will be called for both the wrapper +// that implements error as well as the wrapped error itself. +func Walk(err error, cb WalkFunc) { + if err == nil { + return + } + + switch e := err.(type) { + case *wrappedError: + cb(e.Outer) + Walk(e.Inner, cb) + case Wrapper: + cb(err) + + for _, err := range e.WrappedErrors() { + Walk(err, cb) + } + case interface{ Unwrap() error }: + cb(err) + Walk(e.Unwrap(), cb) + default: + cb(err) + } +} + +// wrappedError is an implementation of error that has both the +// outer and inner errors. +type wrappedError struct { + Outer error + Inner error +} + +func (w *wrappedError) Error() string { + return w.Outer.Error() +} + +func (w *wrappedError) WrappedErrors() []error { + return []error{w.Outer, w.Inner} +} + +func (w *wrappedError) Unwrap() error { + return w.Inner +} diff --git a/vendor/github.com/hashicorp/errwrap/go.mod b/vendor/github.com/hashicorp/errwrap/go.mod new file mode 100644 index 0000000000..c9b84022cf --- /dev/null +++ b/vendor/github.com/hashicorp/errwrap/go.mod @@ -0,0 +1 @@ +module github.com/hashicorp/errwrap diff --git a/vendor/github.com/hashicorp/go-multierror/LICENSE b/vendor/github.com/hashicorp/go-multierror/LICENSE new file mode 100644 index 0000000000..82b4de97c7 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/LICENSE @@ -0,0 +1,353 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. diff --git a/vendor/github.com/hashicorp/go-multierror/Makefile b/vendor/github.com/hashicorp/go-multierror/Makefile new file mode 100644 index 0000000000..b97cd6ed02 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/Makefile @@ -0,0 +1,31 @@ +TEST?=./... + +default: test + +# test runs the test suite and vets the code. +test: generate + @echo "==> Running tests..." + @go list $(TEST) \ + | grep -v "/vendor/" \ + | xargs -n1 go test -timeout=60s -parallel=10 ${TESTARGS} + +# testrace runs the race checker +testrace: generate + @echo "==> Running tests (race)..." + @go list $(TEST) \ + | grep -v "/vendor/" \ + | xargs -n1 go test -timeout=60s -race ${TESTARGS} + +# updatedeps installs all the dependencies needed to run and build. +updatedeps: + @sh -c "'${CURDIR}/scripts/deps.sh' '${NAME}'" + +# generate runs `go generate` to build the dynamically generated source files. +generate: + @echo "==> Generating..." + @find . -type f -name '.DS_Store' -delete + @go list ./... \ + | grep -v "/vendor/" \ + | xargs -n1 go generate + +.PHONY: default test testrace updatedeps generate diff --git a/vendor/github.com/hashicorp/go-multierror/README.md b/vendor/github.com/hashicorp/go-multierror/README.md new file mode 100644 index 0000000000..71dd308ed8 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/README.md @@ -0,0 +1,150 @@ +# go-multierror + +[![CircleCI](https://img.shields.io/circleci/build/github/hashicorp/go-multierror/master)](https://circleci.com/gh/hashicorp/go-multierror) +[![Go Reference](https://pkg.go.dev/badge/github.com/hashicorp/go-multierror.svg)](https://pkg.go.dev/github.com/hashicorp/go-multierror) +![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/hashicorp/go-multierror) + +[circleci]: https://app.circleci.com/pipelines/github/hashicorp/go-multierror +[godocs]: https://pkg.go.dev/github.com/hashicorp/go-multierror + +`go-multierror` is a package for Go that provides a mechanism for +representing a list of `error` values as a single `error`. + +This allows a function in Go to return an `error` that might actually +be a list of errors. If the caller knows this, they can unwrap the +list and access the errors. If the caller doesn't know, the error +formats to a nice human-readable format. + +`go-multierror` is fully compatible with the Go standard library +[errors](https://golang.org/pkg/errors/) package, including the +functions `As`, `Is`, and `Unwrap`. This provides a standardized approach +for introspecting on error values. + +## Installation and Docs + +Install using `go get github.com/hashicorp/go-multierror`. + +Full documentation is available at +https://pkg.go.dev/github.com/hashicorp/go-multierror + +### Requires go version 1.13 or newer + +`go-multierror` requires go version 1.13 or newer. Go 1.13 introduced +[error wrapping](https://golang.org/doc/go1.13#error_wrapping), which +this library takes advantage of. + +If you need to use an earlier version of go, you can use the +[v1.0.0](https://github.com/hashicorp/go-multierror/tree/v1.0.0) +tag, which doesn't rely on features in go 1.13. + +If you see compile errors that look like the below, it's likely that +you're on an older version of go: + +``` +/go/src/github.com/hashicorp/go-multierror/multierror.go:112:9: undefined: errors.As +/go/src/github.com/hashicorp/go-multierror/multierror.go:117:9: undefined: errors.Is +``` + +## Usage + +go-multierror is easy to use and purposely built to be unobtrusive in +existing Go applications/libraries that may not be aware of it. + +**Building a list of errors** + +The `Append` function is used to create a list of errors. This function +behaves a lot like the Go built-in `append` function: it doesn't matter +if the first argument is nil, a `multierror.Error`, or any other `error`, +the function behaves as you would expect. + +```go +var result error + +if err := step1(); err != nil { + result = multierror.Append(result, err) +} +if err := step2(); err != nil { + result = multierror.Append(result, err) +} + +return result +``` + +**Customizing the formatting of the errors** + +By specifying a custom `ErrorFormat`, you can customize the format +of the `Error() string` function: + +```go +var result *multierror.Error + +// ... accumulate errors here, maybe using Append + +if result != nil { + result.ErrorFormat = func([]error) string { + return "errors!" + } +} +``` + +**Accessing the list of errors** + +`multierror.Error` implements `error` so if the caller doesn't know about +multierror, it will work just fine. But if you're aware a multierror might +be returned, you can use type switches to access the list of errors: + +```go +if err := something(); err != nil { + if merr, ok := err.(*multierror.Error); ok { + // Use merr.Errors + } +} +``` + +You can also use the standard [`errors.Unwrap`](https://golang.org/pkg/errors/#Unwrap) +function. This will continue to unwrap into subsequent errors until none exist. + +**Extracting an error** + +The standard library [`errors.As`](https://golang.org/pkg/errors/#As) +function can be used directly with a multierror to extract a specific error: + +```go +// Assume err is a multierror value +err := somefunc() + +// We want to know if "err" has a "RichErrorType" in it and extract it. +var errRich RichErrorType +if errors.As(err, &errRich) { + // It has it, and now errRich is populated. +} +``` + +**Checking for an exact error value** + +Some errors are returned as exact errors such as the [`ErrNotExist`](https://golang.org/pkg/os/#pkg-variables) +error in the `os` package. You can check if this error is present by using +the standard [`errors.Is`](https://golang.org/pkg/errors/#Is) function. + +```go +// Assume err is a multierror value +err := somefunc() +if errors.Is(err, os.ErrNotExist) { + // err contains os.ErrNotExist +} +``` + +**Returning a multierror only if there are errors** + +If you build a `multierror.Error`, you can use the `ErrorOrNil` function +to return an `error` implementation only if there are errors to return: + +```go +var result *multierror.Error + +// ... accumulate errors here + +// Return the `error` only if errors were added to the multierror, otherwise +// return nil since there are no errors. +return result.ErrorOrNil() +``` diff --git a/vendor/github.com/hashicorp/go-multierror/append.go b/vendor/github.com/hashicorp/go-multierror/append.go new file mode 100644 index 0000000000..3e2589bfde --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/append.go @@ -0,0 +1,43 @@ +package multierror + +// Append is a helper function that will append more errors +// onto an Error in order to create a larger multi-error. +// +// If err is not a multierror.Error, then it will be turned into +// one. If any of the errs are multierr.Error, they will be flattened +// one level into err. +// Any nil errors within errs will be ignored. If err is nil, a new +// *Error will be returned. +func Append(err error, errs ...error) *Error { + switch err := err.(type) { + case *Error: + // Typed nils can reach here, so initialize if we are nil + if err == nil { + err = new(Error) + } + + // Go through each error and flatten + for _, e := range errs { + switch e := e.(type) { + case *Error: + if e != nil { + err.Errors = append(err.Errors, e.Errors...) + } + default: + if e != nil { + err.Errors = append(err.Errors, e) + } + } + } + + return err + default: + newErrs := make([]error, 0, len(errs)+1) + if err != nil { + newErrs = append(newErrs, err) + } + newErrs = append(newErrs, errs...) + + return Append(&Error{}, newErrs...) + } +} diff --git a/vendor/github.com/hashicorp/go-multierror/flatten.go b/vendor/github.com/hashicorp/go-multierror/flatten.go new file mode 100644 index 0000000000..aab8e9abec --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/flatten.go @@ -0,0 +1,26 @@ +package multierror + +// Flatten flattens the given error, merging any *Errors together into +// a single *Error. +func Flatten(err error) error { + // If it isn't an *Error, just return the error as-is + if _, ok := err.(*Error); !ok { + return err + } + + // Otherwise, make the result and flatten away! + flatErr := new(Error) + flatten(err, flatErr) + return flatErr +} + +func flatten(err error, flatErr *Error) { + switch err := err.(type) { + case *Error: + for _, e := range err.Errors { + flatten(e, flatErr) + } + default: + flatErr.Errors = append(flatErr.Errors, err) + } +} diff --git a/vendor/github.com/hashicorp/go-multierror/format.go b/vendor/github.com/hashicorp/go-multierror/format.go new file mode 100644 index 0000000000..47f13c49a6 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/format.go @@ -0,0 +1,27 @@ +package multierror + +import ( + "fmt" + "strings" +) + +// ErrorFormatFunc is a function callback that is called by Error to +// turn the list of errors into a string. +type ErrorFormatFunc func([]error) string + +// ListFormatFunc is a basic formatter that outputs the number of errors +// that occurred along with a bullet point list of the errors. +func ListFormatFunc(es []error) string { + if len(es) == 1 { + return fmt.Sprintf("1 error occurred:\n\t* %s\n\n", es[0]) + } + + points := make([]string, len(es)) + for i, err := range es { + points[i] = fmt.Sprintf("* %s", err) + } + + return fmt.Sprintf( + "%d errors occurred:\n\t%s\n\n", + len(es), strings.Join(points, "\n\t")) +} diff --git a/vendor/github.com/hashicorp/go-multierror/go.mod b/vendor/github.com/hashicorp/go-multierror/go.mod new file mode 100644 index 0000000000..141cc4ccb2 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/go.mod @@ -0,0 +1,5 @@ +module github.com/hashicorp/go-multierror + +go 1.13 + +require github.com/hashicorp/errwrap v1.0.0 diff --git a/vendor/github.com/hashicorp/go-multierror/go.sum b/vendor/github.com/hashicorp/go-multierror/go.sum new file mode 100644 index 0000000000..e8238e9ec9 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/go.sum @@ -0,0 +1,2 @@ +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= diff --git a/vendor/github.com/hashicorp/go-multierror/group.go b/vendor/github.com/hashicorp/go-multierror/group.go new file mode 100644 index 0000000000..9c29efb7f8 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/group.go @@ -0,0 +1,38 @@ +package multierror + +import "sync" + +// Group is a collection of goroutines which return errors that need to be +// coalesced. +type Group struct { + mutex sync.Mutex + err *Error + wg sync.WaitGroup +} + +// Go calls the given function in a new goroutine. +// +// If the function returns an error it is added to the group multierror which +// is returned by Wait. +func (g *Group) Go(f func() error) { + g.wg.Add(1) + + go func() { + defer g.wg.Done() + + if err := f(); err != nil { + g.mutex.Lock() + g.err = Append(g.err, err) + g.mutex.Unlock() + } + }() +} + +// Wait blocks until all function calls from the Go method have returned, then +// returns the multierror. +func (g *Group) Wait() *Error { + g.wg.Wait() + g.mutex.Lock() + defer g.mutex.Unlock() + return g.err +} diff --git a/vendor/github.com/hashicorp/go-multierror/multierror.go b/vendor/github.com/hashicorp/go-multierror/multierror.go new file mode 100644 index 0000000000..f545743264 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/multierror.go @@ -0,0 +1,121 @@ +package multierror + +import ( + "errors" + "fmt" +) + +// Error is an error type to track multiple errors. This is used to +// accumulate errors in cases and return them as a single "error". +type Error struct { + Errors []error + ErrorFormat ErrorFormatFunc +} + +func (e *Error) Error() string { + fn := e.ErrorFormat + if fn == nil { + fn = ListFormatFunc + } + + return fn(e.Errors) +} + +// ErrorOrNil returns an error interface if this Error represents +// a list of errors, or returns nil if the list of errors is empty. This +// function is useful at the end of accumulation to make sure that the value +// returned represents the existence of errors. +func (e *Error) ErrorOrNil() error { + if e == nil { + return nil + } + if len(e.Errors) == 0 { + return nil + } + + return e +} + +func (e *Error) GoString() string { + return fmt.Sprintf("*%#v", *e) +} + +// WrappedErrors returns the list of errors that this Error is wrapping. It is +// an implementation of the errwrap.Wrapper interface so that multierror.Error +// can be used with that library. +// +// This method is not safe to be called concurrently. Unlike accessing the +// Errors field directly, this function also checks if the multierror is nil to +// prevent a null-pointer panic. It satisfies the errwrap.Wrapper interface. +func (e *Error) WrappedErrors() []error { + if e == nil { + return nil + } + return e.Errors +} + +// Unwrap returns an error from Error (or nil if there are no errors). +// This error returned will further support Unwrap to get the next error, +// etc. The order will match the order of Errors in the multierror.Error +// at the time of calling. +// +// The resulting error supports errors.As/Is/Unwrap so you can continue +// to use the stdlib errors package to introspect further. +// +// This will perform a shallow copy of the errors slice. Any errors appended +// to this error after calling Unwrap will not be available until a new +// Unwrap is called on the multierror.Error. +func (e *Error) Unwrap() error { + // If we have no errors then we do nothing + if e == nil || len(e.Errors) == 0 { + return nil + } + + // If we have exactly one error, we can just return that directly. + if len(e.Errors) == 1 { + return e.Errors[0] + } + + // Shallow copy the slice + errs := make([]error, len(e.Errors)) + copy(errs, e.Errors) + return chain(errs) +} + +// chain implements the interfaces necessary for errors.Is/As/Unwrap to +// work in a deterministic way with multierror. A chain tracks a list of +// errors while accounting for the current represented error. This lets +// Is/As be meaningful. +// +// Unwrap returns the next error. In the cleanest form, Unwrap would return +// the wrapped error here but we can't do that if we want to properly +// get access to all the errors. Instead, users are recommended to use +// Is/As to get the correct error type out. +// +// Precondition: []error is non-empty (len > 0) +type chain []error + +// Error implements the error interface +func (e chain) Error() string { + return e[0].Error() +} + +// Unwrap implements errors.Unwrap by returning the next error in the +// chain or nil if there are no more errors. +func (e chain) Unwrap() error { + if len(e) == 1 { + return nil + } + + return e[1:] +} + +// As implements errors.As by attempting to map to the current value. +func (e chain) As(target interface{}) bool { + return errors.As(e[0], target) +} + +// Is implements errors.Is by comparing the current value directly. +func (e chain) Is(target error) bool { + return errors.Is(e[0], target) +} diff --git a/vendor/github.com/hashicorp/go-multierror/prefix.go b/vendor/github.com/hashicorp/go-multierror/prefix.go new file mode 100644 index 0000000000..5c477abe44 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/prefix.go @@ -0,0 +1,37 @@ +package multierror + +import ( + "fmt" + + "github.com/hashicorp/errwrap" +) + +// Prefix is a helper function that will prefix some text +// to the given error. If the error is a multierror.Error, then +// it will be prefixed to each wrapped error. +// +// This is useful to use when appending multiple multierrors +// together in order to give better scoping. +func Prefix(err error, prefix string) error { + if err == nil { + return nil + } + + format := fmt.Sprintf("%s {{err}}", prefix) + switch err := err.(type) { + case *Error: + // Typed nils can reach here, so initialize if we are nil + if err == nil { + err = new(Error) + } + + // Wrap each of the errors + for i, e := range err.Errors { + err.Errors[i] = errwrap.Wrapf(format, e) + } + + return err + default: + return errwrap.Wrapf(format, err) + } +} diff --git a/vendor/github.com/hashicorp/go-multierror/sort.go b/vendor/github.com/hashicorp/go-multierror/sort.go new file mode 100644 index 0000000000..fecb14e81c --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/sort.go @@ -0,0 +1,16 @@ +package multierror + +// Len implements sort.Interface function for length +func (err Error) Len() int { + return len(err.Errors) +} + +// Swap implements sort.Interface function for swapping elements +func (err Error) Swap(i, j int) { + err.Errors[i], err.Errors[j] = err.Errors[j], err.Errors[i] +} + +// Less implements sort.Interface function for determining order +func (err Error) Less(i, j int) bool { + return err.Errors[i].Error() < err.Errors[j].Error() +} diff --git a/vendor/github.com/heroku/color/.gitignore b/vendor/github.com/heroku/color/.gitignore new file mode 100644 index 0000000000..ed3ac085ae --- /dev/null +++ b/vendor/github.com/heroku/color/.gitignore @@ -0,0 +1,16 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +.idea/ +c.out +*.test +*.prof \ No newline at end of file diff --git a/vendor/github.com/heroku/color/.golangcli.yml b/vendor/github.com/heroku/color/.golangcli.yml new file mode 100644 index 0000000000..b9bd650135 --- /dev/null +++ b/vendor/github.com/heroku/color/.golangcli.yml @@ -0,0 +1,26 @@ +run: + concurrency: 4 + +linter-settings: + goimports: + local-prefixes: github.com/heroku/code + lll: + line-length: 110 + + +linters: + enable-all: true + disable: + - golint + - maligned + - gochecknoglobals + +issues: + exclude-rules: + - path: _test\.go + linters: + - scopelint + - funlen + - path: color\.go + linters: + - gocritic diff --git a/vendor/github.com/heroku/color/LICENSE b/vendor/github.com/heroku/color/LICENSE new file mode 100644 index 0000000000..d23abe7400 --- /dev/null +++ b/vendor/github.com/heroku/color/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Heroku + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/heroku/color/Makefile b/vendor/github.com/heroku/color/Makefile new file mode 100644 index 0000000000..17c681e420 --- /dev/null +++ b/vendor/github.com/heroku/color/Makefile @@ -0,0 +1,21 @@ +PROJECT_ROOT := $(shell pwd) + +.PHONY: test bench fmt lint + +test: + @go test -race -coverprofile c.out -cpuprofile=cpu2.prof github.com/heroku/color + +bench: + @go test -bench=. -count=5 -cpu=1,2,4,8 -cpuprofile=cpu.prof github.com/heroku/color + +cover: test + @go tool cover -html=c.out + +lint: $(GOPATH)/bin/golangci-lint + @echo "--> Running linter with default config" + @golangci-lint run --deadline 3m0s -c $(PROJECT_ROOT)/.golangcli.yml + +$(GOPATH)/bin/golangci-lint: + @echo "--> Installing linter" + curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.18.0 + diff --git a/vendor/github.com/heroku/color/README.md b/vendor/github.com/heroku/color/README.md new file mode 100644 index 0000000000..52c1644bc4 --- /dev/null +++ b/vendor/github.com/heroku/color/README.md @@ -0,0 +1,109 @@ +# Color [![GoDoc](https://godoc.org/github.com/heroku/color?status.svg)](https://godoc.org/github.com/heroku/color) [![CircleCI](https://circleci.com/gh/heroku/color.svg?style=svg)](https://circleci.com/gh/heroku/color) + +Color is based on the [github.com/fatih/color](https://github.com/fatih/color) package. Unfortunately the original +color package is archived and is no longer supported. Like the original, this package lets you use colorized outputs in +terms of [ANSI Escape Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Golang but offers a number of +improvements from the original. Posix and Windows platforms are supported. + +Color seeks to remain *mostly* backward compatible with fatih/color but has a number of changes to support concurrency, +improved performance and a more idiomatic style. + +## Changes And Improvements + +The methods of the new `Color` struct do not mutate the sender. This +results in better concurrency support and improved performance. + +You don't need to remember to wrap io.Writer arguments in `colorable.NewColorable` in order to support Windows functionality. + +Package public global variables are removed. `color.NoColor` was removed and replaced with the `Disable` function. + Colored output can be toggled using the `Disable`. `color.Output` and `color.Error`replaced by `Stdout()` and `Stderr()` +. + +Instances of `Console` can be passed to methods in third party packages that take `io.Writer` as an argument. If the + third party package emits ANSI color information the passed in writer will be interpreted correctly on Windows. In + addition, color information can be stripped for a console by calling `Console.DisableColors(true)`. + +Performance is improved significantly, as much as 400%. Note that some functions that you'd expect to take an +array of interface{} take an array of strings instead because underlying calls to fmt.SprintXX functions are slow. + +`fatih/color` has race conditions. This package was developed with `test.Parallel` and `-race` enabled for tests. Thus +far no race conditions are known and so this package is suitable for use in a multi goroutine environment. + +## Examples + +### Standard colors + +```go +// Print with default helper functions +color.Cyan("Prints text in cyan.") + +// A newline will be appended automatically +color.Blue("Prints %s in blue.", "text") +``` + +### Mix and reuse colors + +```go +// Create a new color object +color.Stdout().Println(color.New(color.FgCyan, color.Underline), "Prints cyan text with an underline.") +``` + +### Use your own output (io.Writer) + +```go +// Use your own io.Writer output +wtr := color.NewConsole(os.Stderr) +wtr.Println(color.New(color.FgBlue), "Hello! I'm blue.") +``` + +### Custom print functions (PrintFunc) + +```go +// Create a custom print function for convenience +red := color.StdErr().PrintfFunc(color.New(color.FgRed)) +red("Warning") +red("Error: %s", err) + +// Mix up multiple attributes +notice := color.Stdout().PrintlnFunc(color.New(color.Bold, color.FgGreen)) +notice("Don't forget this...") +``` +### Insert into noncolor strings (SprintFunc) + +```go +// Create SprintXxx functions to mix strings with other non-colorized strings: +yellow := color.New(color.FgYellow).SprintFunc() +red := color.New(color.FgRed).SprintFunc() +fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error")) + +info := color.New(color.FgWhite, color.BgGreen).SprintFunc() +fmt.Printf("This %s rocks!\n", info("package")) + +// Use helper functions +fmt.Println("This", color.RedString("warning"), "should be not neglected.") +fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.") +``` +### Disable/Enable color + +There might be a case where you want to explicitly disable/enable color output. + +`Color` has support to disable/enable colors on a per `Console` basis. +For example suppose you have a CLI app and a `--no-color` bool flag. You +can easily disable the color output with: + +```go + +var flagNoColor = flag.Bool("no-color", false, "Disable color output") +color.Stdout().DisableColors(*flagNoColor) + +``` +## Credits + + * [Fatih Arslan](https://github.com/fatih) + * Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable) + +## License + +The MIT License (MIT) - see [`LICENSE.md`](https://github.com/heroku/color/blob/master/LICENSE) for more details + + diff --git a/vendor/github.com/heroku/color/attributes.go b/vendor/github.com/heroku/color/attributes.go new file mode 100644 index 0000000000..552b88b6b1 --- /dev/null +++ b/vendor/github.com/heroku/color/attributes.go @@ -0,0 +1,175 @@ +package color + +import ( + "fmt" +) + +// Attribute defines a single SGR Code. +type Attribute uint64 + +// String returns color code as a string. +func (a Attribute) String() string { + return attributeToSGRCode[a] +} + +// Name returns a human readable name for an Attribute. +func (a Attribute) Name() string { + m := map[Attribute]string{ + Reset: "Reset", + Bold: "Bold", + Faint: "Faint", + Italic: "Italic", + Underline: "Underline", + BlinkSlow: "BlinkSlow", + BlinkRapid: "BlinkRapid", + ReverseVideo: "ReverseVideo", + Concealed: "Concealed", + CrossedOut: "CrossedOut", + FgBlack: "FgBlack", + FgRed: "FgRed", + FgGreen: "FgGreen", + FgYellow: "FgYellow", + FgBlue: "FgBlue", + FgMagenta: "FgMagenta", + FgCyan: "FgCyan", + FgWhite: "FgWhite", + FgHiBlack: "FgHiBlack", + FgHiRed: "FgHiRed", + FgHiGreen: "FgHiGreen", + FgHiYellow: "FgHiYellow", + FgHiBlue: "FgHiBlue", + FgHiMagenta: "FgHiMagenta", + FgHiCyan: "FgHiCyan", + FgHiWhite: "FgHiWhite", + BgBlack: "BgBlack", + BgRed: "BgRed", + BgGreen: "BgGreen", + BgYellow: "BgYellow", + BgBlue: "BgBlue", + BgMagenta: "BgMagenta", + BgCyan: "BgCyan", + BgWhite: "BgWhite", + BgHiBlack: "BgHiBlack", + BgHiRed: "BgHiRed", + BgHiGreen: "BgHiGreen", + BgHiYellow: "BgHiYellow", + BgHiBlue: "BgHiBlue", + BgHiMagenta: "BgHiMagenta", + BgHiCyan: "BgHiCyan", + BgHiWhite: "BgHiWhite", + } + if s, ok := m[a]; ok { + return s + } + return fmt.Sprintf("unknown color %d", a) +} + +const ( + Reset Attribute = 1 << iota + Bold + Faint + Italic + Underline + BlinkSlow + BlinkRapid + ReverseVideo + Concealed + CrossedOut + FgBlack + FgRed + FgGreen + FgYellow + FgBlue + FgMagenta + FgCyan + FgWhite + FgHiBlack + FgHiRed + FgHiGreen + FgHiYellow + FgHiBlue + FgHiMagenta + FgHiCyan + FgHiWhite + BgBlack + BgRed + BgGreen + BgYellow + BgBlue + BgMagenta + BgCyan + BgWhite + BgHiBlack + BgHiRed + BgHiGreen + BgHiYellow + BgHiBlue + BgHiMagenta + BgHiCyan + BgHiWhite +) + +var attributeToSGRCode = map[Attribute]string{ + Reset: "0", + Bold: "1", + Faint: "2", + Italic: "3", + Underline: "4", + BlinkSlow: "5", + BlinkRapid: "6", + ReverseVideo: "7", + Concealed: "8", + CrossedOut: "9", + FgBlack: "30", + FgRed: "31", + FgGreen: "32", + FgYellow: "33", + FgBlue: "34", + FgMagenta: "35", + FgCyan: "36", + FgWhite: "37", + BgBlack: "40", + BgRed: "41", + BgGreen: "42", + BgYellow: "43", + BgBlue: "44", + BgMagenta: "45", + BgCyan: "46", + BgWhite: "47", + FgHiBlack: "90", + FgHiRed: "91", + FgHiGreen: "92", + FgHiYellow: "93", + FgHiBlue: "94", + FgHiMagenta: "95", + FgHiCyan: "96", + FgHiWhite: "97", + BgHiBlack: "100", + BgHiRed: "101", + BgHiGreen: "102", + BgHiYellow: "103", + BgHiBlue: "104", + BgHiMagenta: "105", + BgHiCyan: "106", + BgHiWhite: "107", +} + +func to_codes(attrs []Attribute) []string { + codes := make([]string, len(attrs)) + for i := 0; i < len(attrs); i++ { + code, ok := attributeToSGRCode[attrs[i]] + if !ok { + return nil + } + codes[i] = code + } + return codes +} + +func to_key(attr []Attribute) Attribute { + var key Attribute + for i := 0; i < len(attr); i++ { + key |= attr[i] + } + return key +} diff --git a/vendor/github.com/heroku/color/cache.go b/vendor/github.com/heroku/color/cache.go new file mode 100644 index 0000000000..60c58f3ae9 --- /dev/null +++ b/vendor/github.com/heroku/color/cache.go @@ -0,0 +1,53 @@ +package color + +import "sync" + +var cacheSingleton *colorCache +var cacheOnce sync.Once + +func cache() *colorCache { + cacheOnce.Do(func() { + cacheSingleton = &colorCache{ + cache: make(colorMap), + } + }) + return cacheSingleton +} + +type colorMap map[Attribute]*Color + +type colorCache struct { + sync.RWMutex + cache colorMap +} + +func (cc *colorCache) value(attrs ...Attribute) *Color { + key := to_key(attrs) + if v := cc.getIfExists(key); v != nil { + return v + } + cc.Lock() + v := &Color{ + colorStart: chainSGRCodes(attrs), + } + cc.cache[key] = v + cc.Unlock() + return v +} + +func (vc *colorCache) getIfExists(key Attribute) *Color { + vc.RLock() + if v, ok := vc.cache[key]; ok { + vc.RUnlock() + return v + + } + vc.RUnlock() + return nil +} + +func (vc *colorCache) clear() { + vc.Lock() + defer vc.Unlock() + vc.cache = make(colorMap) +} diff --git a/vendor/github.com/heroku/color/color.go b/vendor/github.com/heroku/color/color.go new file mode 100644 index 0000000000..ca6d5cc2e7 --- /dev/null +++ b/vendor/github.com/heroku/color/color.go @@ -0,0 +1,271 @@ +package color + +import ( + "fmt" + "strings" +) + +const ( + escape = "\x1b[" + endCode = "m" + lineFeed = "\n" + delimiter = ";" + colorReset = "\x1b[0m" +) + +func chainSGRCodes(a []Attribute) string { + codes := to_codes(a) + if len(codes) == 0 { + return colorReset + } + if len(codes) == 1 { + return escape + codes[0] + endCode + } + var bld strings.Builder + bld.Grow((len(codes) * 2) + len(escape) + len(endCode)) + bld.WriteString(escape) + delimsAdded := 0 + for i := 0; i < len(a); i++ { + if delimsAdded > 0 { + _, _ = bld.WriteString(delimiter) + } + bld.WriteString(codes[i]) + delimsAdded++ + } + bld.WriteString(endCode) + return bld.String() +} + +// Color contains methods to create colored strings of text. +type Color struct { + colorStart string +} + +// New creates a Color. It takes a list of Attributes to define +// the appearance of output. +func New(attrs ...Attribute) *Color { + return cache().value(attrs...) +} + +// Sprint returns text decorated with the display Attributes passed to Color constructor function. +func (v Color) Sprint(a ...interface{}) string { + if Enabled() { + return v.wrap(fmt.Sprint(a...)) + } + return fmt.Sprint(a...) +} + +// Sprint formats according to the format specifier and returns text decorated with the display Attributes +// passed to Color constructor function. +func (v Color) Sprintf(format string, a ...interface{}) string { + if Enabled() { + return v.wrap(fmt.Sprintf(format, a...)) + } + return fmt.Sprintf(format, a...) +} + +// Sprint returns text decorated with the display Attributes and terminated by a line feed. +func (v Color) Sprintln(a ...interface{}) string { + var s string + if Enabled() { + s = v.wrap(fmt.Sprint(a...)) + } else { + s = fmt.Sprint(a...) + } + if !strings.HasSuffix(s, lineFeed) { + s += lineFeed + } + return s +} + +// SprintFunc returns function that wraps Sprint. +func (v Color) SprintFunc() func(a ...interface{}) string { + return func(a ...interface{}) string { + return v.Sprint(a...) + } +} + +// SprintfFunc returns function that wraps Sprintf. +func (v Color) SprintfFunc() func(format string, a ...interface{}) string { + return func(format string, a ...interface{}) string { + return v.Sprintf(format, a...) + } +} + +// SprintlnFunc returns function that wraps Sprintln. +func (v Color) SprintlnFunc() func(a ...interface{}) string { + return func(a ...interface{}) string { + return v.Sprintln(a...) + } +} + +func (v Color) wrap(s ...string) string { + var b strings.Builder + b.Grow(len(v.colorStart) + len(s) + len(colorReset)) + b.WriteString(v.colorStart) + for i := 0; i < len(s); i++ { + b.WriteString(s[i]) + } + b.WriteString(colorReset) + return b.String() +} + +func colorString(format string, attr Attribute, a ...interface{}) string { + return cache().value(attr).Sprintf(format, a...) +} + +// Black helper to produce black text to stdout. +func Black(format string, a ...interface{}) { Stdout().colorPrint(format, FgBlack, a...) } + +// BlackE helper to produce black text to stderr. +func BlackE(format string, a ...interface{}) { Stderr().colorPrint(format, FgBlack, a...) } + +// Red helper to produce red text to stdout. +func Red(format string, a ...interface{}) { Stdout().colorPrint(format, FgRed, a...) } + +// RedE helper to produce red text to stderr. +func RedE(format string, a ...interface{}) { Stderr().colorPrint(format, FgRed, a...) } + +// Green helper to produce green text to stdout. +func Green(format string, a ...interface{}) { Stdout().colorPrint(format, FgGreen, a...) } + +// GreenE helper to produce green text to stderr. +func GreenE(format string, a ...interface{}) { Stderr().colorPrint(format, FgGreen, a...) } + +// Yellow helper to produce yellow text to stdout. +func Yellow(format string, a ...interface{}) { Stdout().colorPrint(format, FgYellow, a...) } + +// YellowE helper to produce yellow text to stderr. +func YellowE(format string, a ...interface{}) { Stderr().colorPrint(format, FgYellow, a...) } + +// Blue helper to produce blue text to stdout. +func Blue(format string, a ...interface{}) { Stdout().colorPrint(format, FgBlue, a...) } + +// BlueE helper to produce blue text to stderr. +func BlueE(format string, a ...interface{}) { Stderr().colorPrint(format, FgBlue, a...) } + +// Magenta helper to produce magenta text to stdout. +func Magenta(format string, a ...interface{}) { Stdout().colorPrint(format, FgMagenta, a...) } + +// MagentaE produces magenta text to stderr. +func MagentaE(format string, a ...interface{}) { Stderr().colorPrint(format, FgMagenta, a...) } + +// Cyan helper to produce cyan text to stdout. +func Cyan(format string, a ...interface{}) { Stdout().colorPrint(format, FgCyan, a...) } + +// CyanE helper to produce cyan text to stderr. +func CyanE(format string, a ...interface{}) { Stderr().colorPrint(format, FgCyan, a...) } + +// White helper to produce white text to stdout. +func White(format string, a ...interface{}) { Stdout().colorPrint(format, FgWhite, a...) } + +// WhiteE helper to produce white text to stderr. +func WhiteE(format string, a ...interface{}) { Stderr().colorPrint(format, FgWhite, a...) } + +// BlackString returns a string decorated with black attributes. +func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) } + +// RedString returns a string decorated with red attributes. +func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) } + +// GreenString returns a string decorated with green attributes. +func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) } + +// YellowString returns a string decorated with yellow attributes. +func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) } + +// BlueString returns a string decorated with blue attributes. +func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) } + +// MagentaString returns a string decorated with magenta attributes. +func MagentaString(format string, a ...interface{}) string { + return colorString(format, FgMagenta, a...) +} + +// CyanString returns a string decorated with cyan attributes. +func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) } + +// WhiteString returns a string decorated with white attributes. +func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) } + +// HiBlack helper to produce black text to stdout. +func HiBlack(format string, a ...interface{}) { Stdout().colorPrint(format, FgHiBlack, a...) } + +// HiBlackE helper to produce black text to stderr. +func HiBlackE(format string, a ...interface{}) { Stderr().colorPrint(format, FgHiBlack, a...) } + +// HiRed helper to write high contrast red text to stdout. +func HiRed(format string, a ...interface{}) { Stdout().colorPrint(format, FgHiRed, a...) } + +// HiRedE helper to write high contrast red text to stderr. +func HiRedE(format string, a ...interface{}) { Stderr().colorPrint(format, FgHiRed, a...) } + +// HiGreen helper writes high contrast green text to stdout. +func HiGreen(format string, a ...interface{}) { Stdout().colorPrint(format, FgHiGreen, a...) } + +// HiGreenE helper writes high contrast green text to stderr. +func HiGreenE(format string, a ...interface{}) { Stderr().colorPrint(format, FgHiGreen, a...) } + +// HiYellow helper writes high contrast yellow text to stdout. +func HiYellow(format string, a ...interface{}) { Stdout().colorPrint(format, FgHiYellow, a...) } + +// HiYellowE helper writes high contrast yellow text to stderr. +func HiYellowE(format string, a ...interface{}) { Stderr().colorPrint(format, FgHiYellow, a...) } + +// HiBlue helper writes high contrast blue text to stdout. +func HiBlue(format string, a ...interface{}) { Stdout().colorPrint(format, FgHiBlue, a...) } + +// HiBlueE helper writes high contrast blue text to stderr. +func HiBlueE(format string, a ...interface{}) { Stderr().colorPrint(format, FgHiBlue, a...) } + +// HiMagenta writes high contrast magenta text to stdout. +func HiMagenta(format string, a ...interface{}) { Stdout().colorPrint(format, FgHiMagenta, a...) } + +// HiMagentaE writes high contrast magenta text to stderr. +func HiMagentaE(format string, a ...interface{}) { Stderr().colorPrint(format, FgHiMagenta, a...) } + +// HiCyan writes high contrast cyan colored text to stdout. +func HiCyan(format string, a ...interface{}) { Stdout().colorPrint(format, FgHiCyan, a...) } + +// HiCyanE writes high contrast contrast cyan colored text to stderr. +func HiCyanE(format string, a ...interface{}) { Stderr().colorPrint(format, FgHiCyan, a...) } + +// HiWhite writes high contrast white colored text to stdout. +func HiWhite(format string, a ...interface{}) { Stdout().colorPrint(format, FgHiWhite, a...) } + +// HiWhiteE writes high contrast white colored text to stderr. +func HiWhiteE(format string, a ...interface{}) { Stderr().colorPrint(format, FgHiWhite, a...) } + +// HiBlackString returns a high contrast black string. +func HiBlackString(format string, a ...interface{}) string { + return colorString(format, FgHiBlack, a...) +} + +// HiRedString returns a high contrast contrast black string. +func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) } + +// HiGreenString returns a high contrast green string. +func HiGreenString(format string, a ...interface{}) string { + return colorString(format, FgHiGreen, a...) +} + +// HiYellowString returns a high contrast yellow string. +func HiYellowString(format string, a ...interface{}) string { + return colorString(format, FgHiYellow, a...) +} + +// HiBlueString returns a high contrast blue string. +func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) } + +// HiMagentaString returns a high contrast magenta string. +func HiMagentaString(format string, a ...interface{}) string { + return colorString(format, FgHiMagenta, a...) +} + +// HiCyanString returns a high contrast cyan string. +func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) } + +// HiWhiteString returns a high contrast white string. +func HiWhiteString(format string, a ...interface{}) string { + return colorString(format, FgHiWhite, a...) +} diff --git a/vendor/github.com/heroku/color/console.go b/vendor/github.com/heroku/color/console.go new file mode 100644 index 0000000000..031a7442ee --- /dev/null +++ b/vendor/github.com/heroku/color/console.go @@ -0,0 +1,167 @@ +// Package color produces colored output in terms of ANSI Escape Codes. Posix and Windows platforms are supported. +package color + +import ( + "fmt" + "io" + "os" + "strings" + "sync" + + "github.com/mattn/go-colorable" +) + +var noColor bool +var noColorLock sync.RWMutex + +// Disable is used to turn color output on and off globally. +func Disable(flag bool) { + noColorLock.Lock() + defer noColorLock.Unlock() + noColor = flag +} + +// Enabled returns flag indicating whether colors are enabled or not. +func Enabled() bool { + noColorLock.RLock() + defer noColorLock.RUnlock() + return !noColor +} + +var stdout *Console // Don't use directly use Stdout() instead. +var stdoutOnce sync.Once + +// Stdout returns an io.Writer that writes colored text to standard out. +func Stdout() *Console { + stdoutOnce.Do(func() { + stdout = NewConsole(os.Stdout) + }) + return stdout +} + +var stderr *Console // Don't use directly use Stderr() instead. +var stderrOnce sync.Once + +// Stderr returns an io.Writer that writes colored text to standard error. +func Stderr() *Console { + stderrOnce.Do(func() { + stderr = NewConsole(os.Stderr) + }) + return stderr +} + +// Console manages state for output, typically stdout or stderr. +type Console struct { + sync.Mutex + colored io.Writer + noncolored io.Writer + current io.Writer + fileDescriptor uintptr +} + +// NewConsole creates a wrapper around out which will output platform independent colored text. +func NewConsole(out *os.File) *Console { + c := &Console{ + colored: colorable.NewColorable(out), + noncolored: colorable.NewNonColorable(out), + fileDescriptor: out.Fd(), + } + if Enabled() { + c.current = c.colored + return c + } + c.current = c.noncolored + return c +} + +func (c *Console) Fd() uintptr { + c.Lock() + defer c.Unlock() + return c.fileDescriptor +} + +// DisableColors if true ANSI color information will be removed for this console object. Passing true will enable +// colors for this Console, even if colors are disabled globally. +func (c *Console) DisableColors(strip bool) { + c.Lock() + defer c.Unlock() + if strip { + c.current = c.noncolored + return + } + c.current = c.colored +} + +// Set will cause the color passed in as an argument to be written until Unset is called. +func (c *Console) Set(color *Color) { + c.Lock() + defer c.Unlock() + _, _ = c.current.Write([]byte(color.colorStart)) +} + +// Unset will restore console output to default. It will undo colored console output defined from a call to Set. +func (c *Console) Unset() { + c.Lock() + defer c.Unlock() + _, _ = c.current.Write([]byte(colorReset)) +} + +// Write so we can treat a console as a Writer +func (c *Console) Write(b []byte) (int, error) { + c.Lock() + n, err := c.current.Write(b) + c.Unlock() + return n, err +} + +// Print writes colored text to the console. The number of bytes written +// is returned. +func (c *Console) Print(col *Color, args ...string) (int, error) { + return c.Write([]byte(col.wrap(args...))) +} + +// Printf formats according to a format specifier and writes colored text to the console. +func (c *Console) Printf(col *Color, format string, args ...interface{}) (int, error) { + return c.Write([]byte(col.wrap(fmt.Sprintf(format, args...)))) +} + +// Println writes colored text to console, appending input with a line feed. +// The number of bytes written is returned. +func (c *Console) Println(col *Color, args ...string) (int, error) { + s := col.wrap(args...) + if !strings.HasSuffix(s, lineFeed) { + s += lineFeed + } + return c.Write([]byte(s)) + +} + +// PrintFunc returns a wrapper function for Print. +func (c *Console) PrintFunc(col *Color) func(a ...string) { + return func(a ...string) { + _, _ = c.Print(col, a...) + } +} + +// PrintfFunc returns a wrapper function for Printf. +func (c *Console) PrintfFunc(col *Color) func(format string, args ...interface{}) { + return func(format string, s ...interface{}) { + _, _ = c.Printf(col, format, s...) + } +} + +// PrintlnFunc returns a wrapper function for Println. +func (c *Console) PrintlnFunc(col *Color) func(a ...string) { + return func(a ...string) { + _, _ = c.Println(col, a...) + } +} + +func (c *Console) colorPrint(format string, attr Attribute, a ...interface{}) { + col := cache().value(attr) + if !strings.HasSuffix(format, lineFeed) { + _, _ = c.Println(col, fmt.Sprintf(format, a...)) + return + } + _, _ = c.Printf(col, format, fmt.Sprint(a...)) +} diff --git a/vendor/github.com/heroku/color/go.mod b/vendor/github.com/heroku/color/go.mod new file mode 100644 index 0000000000..d9507de7f8 --- /dev/null +++ b/vendor/github.com/heroku/color/go.mod @@ -0,0 +1,5 @@ +module github.com/heroku/color + +go 1.13 + +require github.com/mattn/go-colorable v0.1.2 diff --git a/vendor/github.com/heroku/color/go.sum b/vendor/github.com/heroku/color/go.sum new file mode 100644 index 0000000000..0a92752b3a --- /dev/null +++ b/vendor/github.com/heroku/color/go.sum @@ -0,0 +1,6 @@ +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/vendor/github.com/jonboulle/clockwork/.editorconfig b/vendor/github.com/jonboulle/clockwork/.editorconfig new file mode 100644 index 0000000000..4492e9f9fe --- /dev/null +++ b/vendor/github.com/jonboulle/clockwork/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.go] +indent_style = tab diff --git a/vendor/github.com/jonboulle/clockwork/.gitignore b/vendor/github.com/jonboulle/clockwork/.gitignore new file mode 100644 index 0000000000..00852bd942 --- /dev/null +++ b/vendor/github.com/jonboulle/clockwork/.gitignore @@ -0,0 +1,27 @@ +/.idea/ + +# 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 + +*.swp diff --git a/vendor/github.com/jonboulle/clockwork/LICENSE b/vendor/github.com/jonboulle/clockwork/LICENSE new file mode 100644 index 0000000000..5c304d1a4a --- /dev/null +++ b/vendor/github.com/jonboulle/clockwork/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/vendor/github.com/jonboulle/clockwork/README.md b/vendor/github.com/jonboulle/clockwork/README.md new file mode 100644 index 0000000000..cad6083572 --- /dev/null +++ b/vendor/github.com/jonboulle/clockwork/README.md @@ -0,0 +1,80 @@ +# clockwork + +[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#utilities) + +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/jonboulle/clockwork/CI?style=flat-square)](https://github.com/jonboulle/clockwork/actions?query=workflow%3ACI) +[![Go Report Card](https://goreportcard.com/badge/github.com/jonboulle/clockwork?style=flat-square)](https://goreportcard.com/report/github.com/jonboulle/clockwork) +![Go Version](https://img.shields.io/badge/go%20version-%3E=1.11-61CFDD.svg?style=flat-square) +[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/mod/github.com/jonboulle/clockwork) + +**A simple fake clock for Go.** + + +## Usage + +Replace uses of the `time` package with the `clockwork.Clock` interface instead. + +For example, instead of using `time.Sleep` directly: + +```go +func myFunc() { + time.Sleep(3 * time.Second) + doSomething() +} +``` + +Inject a clock and use its `Sleep` method instead: + +```go +func myFunc(clock clockwork.Clock) { + clock.Sleep(3 * time.Second) + doSomething() +} +``` + +Now you can easily test `myFunc` with a `FakeClock`: + +```go +func TestMyFunc(t *testing.T) { + c := clockwork.NewFakeClock() + + // Start our sleepy function + var wg sync.WaitGroup + wg.Add(1) + go func() { + myFunc(c) + wg.Done() + }() + + // Ensure we wait until myFunc is sleeping + c.BlockUntil(1) + + assertState() + + // Advance the FakeClock forward in time + c.Advance(3 * time.Second) + + // Wait until the function completes + wg.Wait() + + assertState() +} +``` + +and in production builds, simply inject the real clock instead: + +```go +myFunc(clockwork.NewRealClock()) +``` + +See [example_test.go](example_test.go) for a full example. + + +# Credits + +clockwork is inspired by @wickman's [threaded fake clock](https://gist.github.com/wickman/3840816), and the [Golang playground](https://blog.golang.org/playground#TOC_3.1.) + + +## License + +Apache License, Version 2.0. Please see [License File](LICENSE) for more information. diff --git a/vendor/github.com/jonboulle/clockwork/clockwork.go b/vendor/github.com/jonboulle/clockwork/clockwork.go new file mode 100644 index 0000000000..1018051f4a --- /dev/null +++ b/vendor/github.com/jonboulle/clockwork/clockwork.go @@ -0,0 +1,195 @@ +package clockwork + +import ( + "sync" + "time" +) + +// Clock provides an interface that packages can use instead of directly +// using the time module, so that chronology-related behavior can be tested +type Clock interface { + After(d time.Duration) <-chan time.Time + Sleep(d time.Duration) + Now() time.Time + Since(t time.Time) time.Duration + NewTicker(d time.Duration) Ticker +} + +// FakeClock provides an interface for a clock which can be +// manually advanced through time +type FakeClock interface { + Clock + // Advance advances the FakeClock to a new point in time, ensuring any existing + // sleepers are notified appropriately before returning + Advance(d time.Duration) + // BlockUntil will block until the FakeClock has the given number of + // sleepers (callers of Sleep or After) + BlockUntil(n int) +} + +// NewRealClock returns a Clock which simply delegates calls to the actual time +// package; it should be used by packages in production. +func NewRealClock() Clock { + return &realClock{} +} + +// NewFakeClock returns a FakeClock implementation which can be +// manually advanced through time for testing. The initial time of the +// FakeClock will be an arbitrary non-zero time. +func NewFakeClock() FakeClock { + // use a fixture that does not fulfill Time.IsZero() + return NewFakeClockAt(time.Date(1984, time.April, 4, 0, 0, 0, 0, time.UTC)) +} + +// NewFakeClockAt returns a FakeClock initialised at the given time.Time. +func NewFakeClockAt(t time.Time) FakeClock { + return &fakeClock{ + time: t, + } +} + +type realClock struct{} + +func (rc *realClock) After(d time.Duration) <-chan time.Time { + return time.After(d) +} + +func (rc *realClock) Sleep(d time.Duration) { + time.Sleep(d) +} + +func (rc *realClock) Now() time.Time { + return time.Now() +} + +func (rc *realClock) Since(t time.Time) time.Duration { + return rc.Now().Sub(t) +} + +func (rc *realClock) NewTicker(d time.Duration) Ticker { + return &realTicker{time.NewTicker(d)} +} + +type fakeClock struct { + sleepers []*sleeper + blockers []*blocker + time time.Time + + l sync.RWMutex +} + +// sleeper represents a caller of After or Sleep +type sleeper struct { + until time.Time + done chan time.Time +} + +// blocker represents a caller of BlockUntil +type blocker struct { + count int + ch chan struct{} +} + +// After mimics time.After; it waits for the given duration to elapse on the +// fakeClock, then sends the current time on the returned channel. +func (fc *fakeClock) After(d time.Duration) <-chan time.Time { + fc.l.Lock() + defer fc.l.Unlock() + now := fc.time + done := make(chan time.Time, 1) + if d.Nanoseconds() <= 0 { + // special case - trigger immediately + done <- now + } else { + // otherwise, add to the set of sleepers + s := &sleeper{ + until: now.Add(d), + done: done, + } + fc.sleepers = append(fc.sleepers, s) + // and notify any blockers + fc.blockers = notifyBlockers(fc.blockers, len(fc.sleepers)) + } + return done +} + +// notifyBlockers notifies all the blockers waiting until the +// given number of sleepers are waiting on the fakeClock. It +// returns an updated slice of blockers (i.e. those still waiting) +func notifyBlockers(blockers []*blocker, count int) (newBlockers []*blocker) { + for _, b := range blockers { + if b.count == count { + close(b.ch) + } else { + newBlockers = append(newBlockers, b) + } + } + return +} + +// Sleep blocks until the given duration has passed on the fakeClock +func (fc *fakeClock) Sleep(d time.Duration) { + <-fc.After(d) +} + +// Time returns the current time of the fakeClock +func (fc *fakeClock) Now() time.Time { + fc.l.RLock() + t := fc.time + fc.l.RUnlock() + return t +} + +// Since returns the duration that has passed since the given time on the fakeClock +func (fc *fakeClock) Since(t time.Time) time.Duration { + return fc.Now().Sub(t) +} + +func (fc *fakeClock) NewTicker(d time.Duration) Ticker { + ft := &fakeTicker{ + c: make(chan time.Time, 1), + stop: make(chan bool, 1), + clock: fc, + period: d, + } + ft.runTickThread() + return ft +} + +// Advance advances fakeClock to a new point in time, ensuring channels from any +// previous invocations of After are notified appropriately before returning +func (fc *fakeClock) Advance(d time.Duration) { + fc.l.Lock() + defer fc.l.Unlock() + end := fc.time.Add(d) + var newSleepers []*sleeper + for _, s := range fc.sleepers { + if end.Sub(s.until) >= 0 { + s.done <- end + } else { + newSleepers = append(newSleepers, s) + } + } + fc.sleepers = newSleepers + fc.blockers = notifyBlockers(fc.blockers, len(fc.sleepers)) + fc.time = end +} + +// BlockUntil will block until the fakeClock has the given number of sleepers +// (callers of Sleep or After) +func (fc *fakeClock) BlockUntil(n int) { + fc.l.Lock() + // Fast path: current number of sleepers is what we're looking for + if len(fc.sleepers) == n { + fc.l.Unlock() + return + } + // Otherwise, set up a new blocker + b := &blocker{ + count: n, + ch: make(chan struct{}), + } + fc.blockers = append(fc.blockers, b) + fc.l.Unlock() + <-b.ch +} diff --git a/vendor/github.com/jonboulle/clockwork/go.mod b/vendor/github.com/jonboulle/clockwork/go.mod new file mode 100644 index 0000000000..4f4bb16587 --- /dev/null +++ b/vendor/github.com/jonboulle/clockwork/go.mod @@ -0,0 +1,3 @@ +module github.com/jonboulle/clockwork + +go 1.13 diff --git a/vendor/github.com/jonboulle/clockwork/ticker.go b/vendor/github.com/jonboulle/clockwork/ticker.go new file mode 100644 index 0000000000..32b5d01e75 --- /dev/null +++ b/vendor/github.com/jonboulle/clockwork/ticker.go @@ -0,0 +1,72 @@ +package clockwork + +import ( + "time" +) + +// Ticker provides an interface which can be used instead of directly +// using the ticker within the time module. The real-time ticker t +// provides ticks through t.C which becomes now t.Chan() to make +// this channel requirement definable in this interface. +type Ticker interface { + Chan() <-chan time.Time + Stop() +} + +type realTicker struct{ *time.Ticker } + +func (rt *realTicker) Chan() <-chan time.Time { + return rt.C +} + +type fakeTicker struct { + c chan time.Time + stop chan bool + clock FakeClock + period time.Duration +} + +func (ft *fakeTicker) Chan() <-chan time.Time { + return ft.c +} + +func (ft *fakeTicker) Stop() { + ft.stop <- true +} + +// runTickThread initializes a background goroutine to send the tick time to the ticker channel +// after every period. Tick events are discarded if the underlying ticker channel does not have +// enough capacity. +func (ft *fakeTicker) runTickThread() { + nextTick := ft.clock.Now().Add(ft.period) + next := ft.clock.After(ft.period) + go func() { + for { + select { + case <-ft.stop: + return + case <-next: + // We send the time that the tick was supposed to occur at. + tick := nextTick + // Before sending the tick, we'll compute the next tick time and star the clock.After call. + now := ft.clock.Now() + // First, figure out how many periods there have been between "now" and the time we were + // supposed to have trigged, then advance over all of those. + skipTicks := (now.Sub(tick) + ft.period - 1) / ft.period + nextTick = nextTick.Add(skipTicks * ft.period) + // Now, keep advancing until we are past now. This should happen at most once. + for !nextTick.After(now) { + nextTick = nextTick.Add(ft.period) + } + // Figure out how long between now and the next scheduled tick, then wait that long. + remaining := nextTick.Sub(now) + next = ft.clock.After(remaining) + // Finally, we can actually send the tick. + select { + case ft.c <- tick: + default: + } + } + } + }() +} diff --git a/vendor/github.com/kballard/go-shellquote/LICENSE b/vendor/github.com/kballard/go-shellquote/LICENSE new file mode 100644 index 0000000000..a6d77312e1 --- /dev/null +++ b/vendor/github.com/kballard/go-shellquote/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2014 Kevin Ballard + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/kballard/go-shellquote/README b/vendor/github.com/kballard/go-shellquote/README new file mode 100644 index 0000000000..4d34e87afc --- /dev/null +++ b/vendor/github.com/kballard/go-shellquote/README @@ -0,0 +1,36 @@ +PACKAGE + +package shellquote + import "github.com/kballard/go-shellquote" + + Shellquote provides utilities for joining/splitting strings using sh's + word-splitting rules. + +VARIABLES + +var ( + UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string") + UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string") + UnterminatedEscapeError = errors.New("Unterminated backslash-escape") +) + + +FUNCTIONS + +func Join(args ...string) string + Join quotes each argument and joins them with a space. If passed to + /bin/sh, the resulting string will be split back into the original + arguments. + +func Split(input string) (words []string, err error) + Split splits a string according to /bin/sh's word-splitting rules. It + supports backslash-escapes, single-quotes, and double-quotes. Notably it + does not support the $'' style of quoting. It also doesn't attempt to + perform any other sort of expansion, including brace expansion, shell + expansion, or pathname expansion. + + If the given input has an unterminated quoted string or ends in a + backslash-escape, one of UnterminatedSingleQuoteError, + UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned. + + diff --git a/vendor/github.com/kballard/go-shellquote/doc.go b/vendor/github.com/kballard/go-shellquote/doc.go new file mode 100644 index 0000000000..9445fa4ad9 --- /dev/null +++ b/vendor/github.com/kballard/go-shellquote/doc.go @@ -0,0 +1,3 @@ +// Shellquote provides utilities for joining/splitting strings using sh's +// word-splitting rules. +package shellquote diff --git a/vendor/github.com/kballard/go-shellquote/quote.go b/vendor/github.com/kballard/go-shellquote/quote.go new file mode 100644 index 0000000000..72a8cb38b5 --- /dev/null +++ b/vendor/github.com/kballard/go-shellquote/quote.go @@ -0,0 +1,102 @@ +package shellquote + +import ( + "bytes" + "strings" + "unicode/utf8" +) + +// Join quotes each argument and joins them with a space. +// If passed to /bin/sh, the resulting string will be split back into the +// original arguments. +func Join(args ...string) string { + var buf bytes.Buffer + for i, arg := range args { + if i != 0 { + buf.WriteByte(' ') + } + quote(arg, &buf) + } + return buf.String() +} + +const ( + specialChars = "\\'\"`${[|&;<>()*?!" + extraSpecialChars = " \t\n" + prefixChars = "~" +) + +func quote(word string, buf *bytes.Buffer) { + // We want to try to produce a "nice" output. As such, we will + // backslash-escape most characters, but if we encounter a space, or if we + // encounter an extra-special char (which doesn't work with + // backslash-escaping) we switch over to quoting the whole word. We do this + // with a space because it's typically easier for people to read multi-word + // arguments when quoted with a space rather than with ugly backslashes + // everywhere. + origLen := buf.Len() + + if len(word) == 0 { + // oops, no content + buf.WriteString("''") + return + } + + cur, prev := word, word + atStart := true + for len(cur) > 0 { + c, l := utf8.DecodeRuneInString(cur) + cur = cur[l:] + if strings.ContainsRune(specialChars, c) || (atStart && strings.ContainsRune(prefixChars, c)) { + // copy the non-special chars up to this point + if len(cur) < len(prev) { + buf.WriteString(prev[0 : len(prev)-len(cur)-l]) + } + buf.WriteByte('\\') + buf.WriteRune(c) + prev = cur + } else if strings.ContainsRune(extraSpecialChars, c) { + // start over in quote mode + buf.Truncate(origLen) + goto quote + } + atStart = false + } + if len(prev) > 0 { + buf.WriteString(prev) + } + return + +quote: + // quote mode + // Use single-quotes, but if we find a single-quote in the word, we need + // to terminate the string, emit an escaped quote, and start the string up + // again + inQuote := false + for len(word) > 0 { + i := strings.IndexRune(word, '\'') + if i == -1 { + break + } + if i > 0 { + if !inQuote { + buf.WriteByte('\'') + inQuote = true + } + buf.WriteString(word[0:i]) + } + word = word[i+1:] + if inQuote { + buf.WriteByte('\'') + inQuote = false + } + buf.WriteString("\\'") + } + if len(word) > 0 { + if !inQuote { + buf.WriteByte('\'') + } + buf.WriteString(word) + buf.WriteByte('\'') + } +} diff --git a/vendor/github.com/kballard/go-shellquote/unquote.go b/vendor/github.com/kballard/go-shellquote/unquote.go new file mode 100644 index 0000000000..b1b13da932 --- /dev/null +++ b/vendor/github.com/kballard/go-shellquote/unquote.go @@ -0,0 +1,156 @@ +package shellquote + +import ( + "bytes" + "errors" + "strings" + "unicode/utf8" +) + +var ( + UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string") + UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string") + UnterminatedEscapeError = errors.New("Unterminated backslash-escape") +) + +var ( + splitChars = " \n\t" + singleChar = '\'' + doubleChar = '"' + escapeChar = '\\' + doubleEscapeChars = "$`\"\n\\" +) + +// Split splits a string according to /bin/sh's word-splitting rules. It +// supports backslash-escapes, single-quotes, and double-quotes. Notably it does +// not support the $'' style of quoting. It also doesn't attempt to perform any +// other sort of expansion, including brace expansion, shell expansion, or +// pathname expansion. +// +// If the given input has an unterminated quoted string or ends in a +// backslash-escape, one of UnterminatedSingleQuoteError, +// UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned. +func Split(input string) (words []string, err error) { + var buf bytes.Buffer + words = make([]string, 0) + + for len(input) > 0 { + // skip any splitChars at the start + c, l := utf8.DecodeRuneInString(input) + if strings.ContainsRune(splitChars, c) { + input = input[l:] + continue + } else if c == escapeChar { + // Look ahead for escaped newline so we can skip over it + next := input[l:] + if len(next) == 0 { + err = UnterminatedEscapeError + return + } + c2, l2 := utf8.DecodeRuneInString(next) + if c2 == '\n' { + input = next[l2:] + continue + } + } + + var word string + word, input, err = splitWord(input, &buf) + if err != nil { + return + } + words = append(words, word) + } + return +} + +func splitWord(input string, buf *bytes.Buffer) (word string, remainder string, err error) { + buf.Reset() + +raw: + { + cur := input + for len(cur) > 0 { + c, l := utf8.DecodeRuneInString(cur) + cur = cur[l:] + if c == singleChar { + buf.WriteString(input[0 : len(input)-len(cur)-l]) + input = cur + goto single + } else if c == doubleChar { + buf.WriteString(input[0 : len(input)-len(cur)-l]) + input = cur + goto double + } else if c == escapeChar { + buf.WriteString(input[0 : len(input)-len(cur)-l]) + input = cur + goto escape + } else if strings.ContainsRune(splitChars, c) { + buf.WriteString(input[0 : len(input)-len(cur)-l]) + return buf.String(), cur, nil + } + } + if len(input) > 0 { + buf.WriteString(input) + input = "" + } + goto done + } + +escape: + { + if len(input) == 0 { + return "", "", UnterminatedEscapeError + } + c, l := utf8.DecodeRuneInString(input) + if c == '\n' { + // a backslash-escaped newline is elided from the output entirely + } else { + buf.WriteString(input[:l]) + } + input = input[l:] + } + goto raw + +single: + { + i := strings.IndexRune(input, singleChar) + if i == -1 { + return "", "", UnterminatedSingleQuoteError + } + buf.WriteString(input[0:i]) + input = input[i+1:] + goto raw + } + +double: + { + cur := input + for len(cur) > 0 { + c, l := utf8.DecodeRuneInString(cur) + cur = cur[l:] + if c == doubleChar { + buf.WriteString(input[0 : len(input)-len(cur)-l]) + input = cur + goto raw + } else if c == escapeChar { + // bash only supports certain escapes in double-quoted strings + c2, l2 := utf8.DecodeRuneInString(cur) + cur = cur[l2:] + if strings.ContainsRune(doubleEscapeChars, c2) { + buf.WriteString(input[0 : len(input)-len(cur)-l-l2]) + if c2 == '\n' { + // newline is special, skip the backslash entirely + } else { + buf.WriteRune(c2) + } + input = cur + } + } + } + return "", "", UnterminatedDoubleQuoteError + } + +done: + return buf.String(), input, nil +} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/.gitignore b/vendor/github.com/lucasb-eyer/go-colorful/.gitignore new file mode 100644 index 0000000000..0aa2c92281 --- /dev/null +++ b/vendor/github.com/lucasb-eyer/go-colorful/.gitignore @@ -0,0 +1,101 @@ +# Created by https://www.toptal.com/developers/gitignore/api/code,go,linux,macos,windows +# Edit at https://www.toptal.com/developers/gitignore?templates=code,go,linux,macos,windows + +### Code ### +.vscode/* +!.vscode/tasks.json +!.vscode/launch.json +*.code-workspace + +### Go ### +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +### Go Patch ### +/vendor/ +/Godeps/ + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/code,go,linux,macos,windows diff --git a/vendor/github.com/lucasb-eyer/go-colorful/CHANGELOG.md b/vendor/github.com/lucasb-eyer/go-colorful/CHANGELOG.md new file mode 100644 index 0000000000..84f9c7b2c7 --- /dev/null +++ b/vendor/github.com/lucasb-eyer/go-colorful/CHANGELOG.md @@ -0,0 +1,42 @@ +# Changelog +All notable changes to this project will be documented in this file. + +This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +The format of this file is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +but only releases after v1.0.3 properly adhere to it. + + +## [1.2.0] - 2021-01-27 +### Added +- HSLuv and HPLuv color spaces (#41, #51) +- CIE LCh(uv) color space, called `LuvLCh` in code (#51) +- JSON and envconfig serialization support for `HexColor` (#42) +- `DistanceLinearRGB` (#53) + +### Fixed +- RGB to/from XYZ conversion is more accurate (#51) +- A bug in `XYZToLuvWhiteRef` that only applied to very small values was fixed (#51) +- `BlendHCL` output is clamped so that it's not invalid (#46) +- Properly documented `DistanceCIE76` (#40) +- Some small godoc fixes + + +## [1.0.3] - 2019-11-11 +- Remove SQLMock dependency + + +## [1.0.2] - 2019-04-07 +- Fixes SQLMock dependency + + +## [1.0.1] - 2019-03-24 +- Adds support for Go Modules + + +## [1.0.0] - 2018-05-26 +- API Breaking change in `MakeColor`: instead of `panic`ing when alpha is zero, it now returns a secondary, boolean return value indicating success. See [the color.Color interface](#the-colorcolor-interface) section and [this FAQ entry](#q-why-would-makecolor-ever-fail) for details. + + +## [0.9.0] - 2018-05-26 +- Initial version number after having ignored versioning for a long time :) diff --git a/vendor/github.com/lucasb-eyer/go-colorful/LICENSE b/vendor/github.com/lucasb-eyer/go-colorful/LICENSE new file mode 100644 index 0000000000..4e402a00e5 --- /dev/null +++ b/vendor/github.com/lucasb-eyer/go-colorful/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2013 Lucas Beyer + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/lucasb-eyer/go-colorful/README.md b/vendor/github.com/lucasb-eyer/go-colorful/README.md new file mode 100644 index 0000000000..8b9bd49991 --- /dev/null +++ b/vendor/github.com/lucasb-eyer/go-colorful/README.md @@ -0,0 +1,482 @@ +go-colorful +=========== + +[![go reportcard](https://goreportcard.com/badge/github.com/lucasb-eyer/go-colorful)](https://goreportcard.com/report/github.com/lucasb-eyer/go-colorful) + +A library for playing with colors in Go. Supports Go 1.13 onwards. + +Why? +==== +I love games. I make games. I love detail and I get lost in detail. +One such detail popped up during the development of [Memory Which Does Not Suck](https://github.com/lucasb-eyer/mwdns/), +when we wanted the server to assign the players random colors. Sometimes +two players got very similar colors, which bugged me. The very same evening, +[I want hue](http://tools.medialab.sciences-po.fr/iwanthue/) was the top post +on HackerNews' frontpage and showed me how to Do It Right™. Last but not +least, there was no library for handling color spaces available in go. Colorful +does just that and implements Go's `color.Color` interface. + +What? +===== +Go-Colorful stores colors in RGB and provides methods from converting these to various color-spaces. Currently supported colorspaces are: + +- **RGB:** All three of Red, Green and Blue in [0..1]. +- **HSL:** Hue in [0..360], Saturation and Luminance in [0..1]. For legacy reasons; please forget that it exists. +- **HSV:** Hue in [0..360], Saturation and Value in [0..1]. You're better off using HCL, see below. +- **Hex RGB:** The "internet" color format, as in #FF00FF. +- **Linear RGB:** See [gamma correct rendering](http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/). +- **CIE-XYZ:** CIE's standard color space, almost in [0..1]. +- **CIE-xyY:** encodes chromacity in x and y and luminance in Y, all in [0..1] +- **CIE-L\*a\*b\*:** A *perceptually uniform* color space, i.e. distances are meaningful. L\* in [0..1] and a\*, b\* almost in [-1..1]. +- **CIE-L\*u\*v\*:** Very similar to CIE-L\*a\*b\*, there is [no consensus](http://en.wikipedia.org/wiki/CIELUV#Historical_background) on which one is "better". +- **CIE-L\*C\*h° (HCL):** This is generally the [most useful](http://vis4.net/blog/posts/avoid-equidistant-hsv-colors/) one; CIE-L\*a\*b\* space in polar coordinates, i.e. a *better* HSV. H° is in [0..360], C\* almost in [-1..1] and L\* as in CIE-L\*a\*b\*. +- **CIE LCh(uv):** Called `LuvLCh` in code, this is a cylindrical transformation of the CIE-L\*u\*v\* color space. Like HCL above: H° is in [0..360], C\* almost in [-1..1] and L\* as in CIE-L\*u\*v\*. +- **HSLuv:** The better alternative to HSL, see [here](https://www.hsluv.org/) and [here](https://www.kuon.ch/post/2020-03-08-hsluv/). Hue in [0..360], Saturation and Luminance in [0..1]. +- **HPLuv:** A variant of HSLuv. The color space is smoother, but only pastel colors can be included. Because the valid colors are limited, it's easy to get invalid Saturation values way above 1.0, indicating the color can't be represented in HPLuv beccause it's not pastel. + +For the colorspaces where it makes sense (XYZ, Lab, Luv, HCl), the +[D65](http://en.wikipedia.org/wiki/Illuminant_D65) is used as reference white +by default but methods for using your own reference white are provided. + +A coordinate being *almost in* a range means that generally it is, but for very +bright colors and depending on the reference white, it might overflow this +range slightly. For example, C\* of #0000ff is 1.338. + +Unit-tests are provided. + +Nice, but what's it useful for? +------------------------------- + +- Converting color spaces. Some people like to do that. +- Blending (interpolating) between colors in a "natural" look by using the right colorspace. +- Generating random colors under some constraints (e.g. colors of the same shade, or shades of one color.) +- Generating gorgeous random palettes with distinct colors of a same temperature. + +What not (yet)? +=============== +There are a few features which are currently missing and might be useful. +I just haven't implemented them yet because I didn't have the need for it. +Pull requests welcome. + +- Sorting colors (potentially using above mentioned distances) + +So which colorspace should I use? +================================= +It depends on what you want to do. I think the folks from *I want hue* are +on-spot when they say that RGB fits to how *screens produce* color, CIE L\*a\*b\* +fits how *humans perceive* color and HCL fits how *humans think* colors. + +Whenever you'd use HSV, rather go for CIE-L\*C\*h°. for fixed lightness L\* and +chroma C\* values, the hue angle h° rotates through colors of the same +perceived brightness and intensity. + +How? +==== + +### Installing +Installing the library is as easy as + +```bash +$ go get github.com/lucasb-eyer/go-colorful +``` + +The package can then be used through an + +```go +import "github.com/lucasb-eyer/go-colorful" +``` + +### Basic usage + +Create a beautiful blue color using different source space: + +```go +// Any of the following should be the same +c := colorful.Color{0.313725, 0.478431, 0.721569} +c, err := colorful.Hex("#517AB8") +if err != nil { + log.Fatal(err) +} +c = colorful.Hsv(216.0, 0.56, 0.722) +c = colorful.Xyz(0.189165, 0.190837, 0.480248) +c = colorful.Xyy(0.219895, 0.221839, 0.190837) +c = colorful.Lab(0.507850, 0.040585,-0.370945) +c = colorful.Luv(0.507849,-0.194172,-0.567924) +c = colorful.Hcl(276.2440, 0.373160, 0.507849) +fmt.Printf("RGB values: %v, %v, %v", c.R, c.G, c.B) +``` + +And then converting this color back into various color spaces: + +```go +hex := c.Hex() +h, s, v := c.Hsv() +x, y, z := c.Xyz() +x, y, Y := c.Xyy() +l, a, b := c.Lab() +l, u, v := c.Luv() +h, c, l := c.Hcl() +``` + +Note that, because of Go's unfortunate choice of requiring an initial uppercase, +the name of the functions relating to the xyY space are just off. If you have +any good suggestion, please open an issue. (I don't consider XyY good.) + +### The `color.Color` interface +Because a `colorful.Color` implements Go's `color.Color` interface (found in the +`image/color` package), it can be used anywhere that expects a `color.Color`. + +Furthermore, you can convert anything that implements the `color.Color` interface +into a `colorful.Color` using the `MakeColor` function: + +```go +c, ok := colorful.MakeColor(color.Gray16{12345}) +``` + +**Caveat:** Be aware that this latter conversion (using `MakeColor`) hits a +corner-case when alpha is exactly zero. Because `color.Color` uses pre-multiplied +alpha colors, this means the RGB values are lost (set to 0) and it's impossible +to recover them. In such a case `MakeColor` will return `false` as its second value. + +### Comparing colors +In the RGB color space, the Euclidian distance between colors *doesn't* correspond +to visual/perceptual distance. This means that two pairs of colors which have the +same distance in RGB space can look much further apart. This is fixed by the +CIE-L\*a\*b\*, CIE-L\*u\*v\* and CIE-L\*C\*h° color spaces. +Thus you should only compare colors in any of these space. +(Note that the distance in CIE-L\*a\*b\* and CIE-L\*C\*h° are the same, since it's the same space but in cylindrical coordinates) + +![Color distance comparison](doc/colordist/colordist.png) + +The two colors shown on the top look much more different than the two shown on +the bottom. Still, in RGB space, their distance is the same. +Here is a little example program which shows the distances between the top two +and bottom two colors in RGB, CIE-L\*a\*b\* and CIE-L\*u\*v\* space. You can find it in `doc/colordist/colordist.go`. + +```go +package main + +import "fmt" +import "github.com/lucasb-eyer/go-colorful" + +func main() { + c1a := colorful.Color{150.0 / 255.0, 10.0 / 255.0, 150.0 / 255.0} + c1b := colorful.Color{53.0 / 255.0, 10.0 / 255.0, 150.0 / 255.0} + c2a := colorful.Color{10.0 / 255.0, 150.0 / 255.0, 50.0 / 255.0} + c2b := colorful.Color{99.9 / 255.0, 150.0 / 255.0, 10.0 / 255.0} + + fmt.Printf("DistanceRgb: c1: %v\tand c2: %v\n", c1a.DistanceRgb(c1b), c2a.DistanceRgb(c2b)) + fmt.Printf("DistanceLab: c1: %v\tand c2: %v\n", c1a.DistanceLab(c1b), c2a.DistanceLab(c2b)) + fmt.Printf("DistanceLuv: c1: %v\tand c2: %v\n", c1a.DistanceLuv(c1b), c2a.DistanceLuv(c2b)) + fmt.Printf("DistanceCIE76: c1: %v\tand c2: %v\n", c1a.DistanceCIE76(c1b), c2a.DistanceCIE76(c2b)) + fmt.Printf("DistanceCIE94: c1: %v\tand c2: %v\n", c1a.DistanceCIE94(c1b), c2a.DistanceCIE94(c2b)) + fmt.Printf("DistanceCIEDE2000: c1: %v\tand c2: %v\n", c1a.DistanceCIEDE2000(c1b), c2a.DistanceCIEDE2000(c2b)) +} +``` + +Running the above program shows that you should always prefer any of the CIE distances: + +```bash +$ go run colordist.go +DistanceRgb: c1: 0.3803921568627451 and c2: 0.3858713931171159 +DistanceLab: c1: 0.32048458312798056 and c2: 0.24397151758565272 +DistanceLuv: c1: 0.5134369614199698 and c2: 0.2568692839860636 +DistanceCIE76: c1: 0.32048458312798056 and c2: 0.24397151758565272 +DistanceCIE94: c1: 0.19799168128511324 and c2: 0.12207136371167401 +DistanceCIEDE2000: c1: 0.17274551120971166 and c2: 0.10665210031428465 +``` + +It also shows that `DistanceLab` is more formally known as `DistanceCIE76` and +has been superseded by the slightly more accurate, but much more expensive +`DistanceCIE94` and `DistanceCIEDE2000`. + +Note that `AlmostEqualRgb` is provided mainly for (unit-)testing purposes. Use +it only if you really know what you're doing. It will eat your cat. + +### Blending colors +Blending is highly connected to distance, since it basically "walks through" the +colorspace thus, if the colorspace maps distances well, the walk is "smooth". + +Colorful comes with blending functions in RGB, HSV and any of the LAB spaces. +Of course, you'd rather want to use the blending functions of the LAB spaces since +these spaces map distances well but, just in case, here is an example showing +you how the blendings (`#fdffcc` to `#242a42`) are done in the various spaces: + +![Blending colors in different spaces.](doc/colorblend/colorblend.png) + +What you see is that HSV is really bad: it adds some green, which is not present +in the original colors at all! RGB is much better, but it stays light a little +too long. LUV and LAB both hit the right lightness but LAB has a little more +color. HCL works in the same vein as HSV (both cylindrical interpolations) but +it does it right in that there is no green appearing and the lighthness changes +in a linear manner. + +While this seems all good, you need to know one thing: When interpolating in any +of the CIE color spaces, you might get invalid RGB colors! This is important if +the starting and ending colors are user-input or random. An example of where this +happens is when blending between `#eeef61` and `#1e3140`: + +![Invalid RGB colors may crop up when blending in CIE spaces.](doc/colorblend/invalid.png) + +You can test whether a color is a valid RGB color by calling the `IsValid` method +and indeed, calling IsValid will return false for the redish colors on the bottom. +One way to "fix" this is to get a valid color close to the invalid one by calling +`Clamped`, which always returns a nearby valid color. Doing this, we get the +following result, which is satisfactory: + +![Fixing invalid RGB colors by clamping them to the valid range.](doc/colorblend/clamped.png) + +The following is the code creating the above three images; it can be found in `doc/colorblend/colorblend.go` + +```go +package main + +import "fmt" +import "github.com/lucasb-eyer/go-colorful" +import "image" +import "image/draw" +import "image/png" +import "os" + +func main() { + blocks := 10 + blockw := 40 + img := image.NewRGBA(image.Rect(0,0,blocks*blockw,200)) + + c1, _ := colorful.Hex("#fdffcc") + c2, _ := colorful.Hex("#242a42") + + // Use these colors to get invalid RGB in the gradient. + //c1, _ := colorful.Hex("#EEEF61") + //c2, _ := colorful.Hex("#1E3140") + + for i := 0 ; i < blocks ; i++ { + draw.Draw(img, image.Rect(i*blockw, 0,(i+1)*blockw, 40), &image.Uniform{c1.BlendHsv(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src) + draw.Draw(img, image.Rect(i*blockw, 40,(i+1)*blockw, 80), &image.Uniform{c1.BlendLuv(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src) + draw.Draw(img, image.Rect(i*blockw, 80,(i+1)*blockw,120), &image.Uniform{c1.BlendRgb(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src) + draw.Draw(img, image.Rect(i*blockw,120,(i+1)*blockw,160), &image.Uniform{c1.BlendLab(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src) + draw.Draw(img, image.Rect(i*blockw,160,(i+1)*blockw,200), &image.Uniform{c1.BlendHcl(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src) + + // This can be used to "fix" invalid colors in the gradient. + //draw.Draw(img, image.Rect(i*blockw,160,(i+1)*blockw,200), &image.Uniform{c1.BlendHcl(c2, float64(i)/float64(blocks-1)).Clamped()}, image.Point{}, draw.Src) + } + + toimg, err := os.Create("colorblend.png") + if err != nil { + fmt.Printf("Error: %v", err) + return + } + defer toimg.Close() + + png.Encode(toimg, img) +} +``` + +#### Generating color gradients +A very common reason to blend colors is creating gradients. There is an example +program in [doc/gradientgen.go](doc/gradientgen/gradientgen.go); it doesn't use any API +which hasn't been used in the previous example code, so I won't bother pasting +the code in here. Just look at that gorgeous gradient it generated in HCL space: + +!["Spectral" colorbrewer gradient in HCL space.](doc/gradientgen/gradientgen.png) + +### Getting random colors +It is sometimes necessary to generate random colors. You could simply do this +on your own by generating colors with random values. By restricting the random +values to a range smaller than [0..1] and using a space such as CIE-H\*C\*l° or +HSV, you can generate both random shades of a color or random colors of a +lightness: + +```go +random_blue := colorful.Hcl(180.0+rand.Float64()*50.0, 0.2+rand.Float64()*0.8, 0.3+rand.Float64()*0.7) +random_dark := colorful.Hcl(rand.Float64()*360.0, rand.Float64(), rand.Float64()*0.4) +random_light := colorful.Hcl(rand.Float64()*360.0, rand.Float64(), 0.6+rand.Float64()*0.4) +``` + +Since getting random "warm" and "happy" colors is quite a common task, there +are some helper functions: + +```go +colorful.WarmColor() +colorful.HappyColor() +colorful.FastWarmColor() +colorful.FastHappyColor() +``` + +The ones prefixed by `Fast` are faster but less coherent since they use the HSV +space as opposed to the regular ones which use CIE-L\*C\*h° space. The +following picture shows the warm colors in the top two rows and happy colors +in the bottom two rows. Within these, the first is the regular one and the +second is the fast one. + +![Warm, fast warm, happy and fast happy random colors, respectively.](doc/colorgens/colorgens.png) + +Don't forget to initialize the random seed! You can see the code used for +generating this picture in `doc/colorgens/colorgens.go`. + +### Getting random palettes +As soon as you need to generate more than one random color, you probably want +them to be distinguishible. Playing against an opponent which has almost the +same blue as I do is not fun. This is where random palettes can help. + +These palettes are generated using an algorithm which ensures that all colors +on the palette are as distinguishible as possible. Again, there is a `Fast` +method which works in HSV and is less perceptually uniform and a non-`Fast` +method which works in CIE spaces. For more theory on `SoftPalette`, check out +[I want hue](http://tools.medialab.sciences-po.fr/iwanthue/theory.php). Yet +again, there is a `Happy` and a `Warm` version, which do what you expect, but +now there is an additional `Soft` version, which is more configurable: you can +give a constraint on the color space in order to get colors within a certain *feel*. + +Let's start with the simple methods first, all they take is the amount of +colors to generate, which could, for example, be the player count. They return +an array of `colorful.Color` objects: + +```go +pal1, err1 := colorful.WarmPalette(10) +pal2 := colorful.FastWarmPalette(10) +pal3, err3 := colorful.HappyPalette(10) +pal4 := colorful.FastHappyPalette(10) +pal5, err5 := colorful.SoftPalette(10) +``` + +Note that the non-fast methods *may* fail if you ask for way too many colors. +Let's move on to the advanced one, namely `SoftPaletteEx`. Besides the color +count, this function takes a `SoftPaletteSettings` object as argument. The +interesting part here is its `CheckColor` member, which is a boolean function +taking three floating points as arguments: `l`, `a` and `b`. This function +should return `true` for colors which lie within the region you want and `false` +otherwise. The other members are `Iteration`, which should be within [5..100] +where higher means slower but more exact palette, and `ManySamples` which you +should set to `true` in case your `CheckColor` constraint rejects a large part +of the color space. + +For example, to create a palette of 10 brownish colors, you'd call it like this: + +```go +func isbrowny(l, a, b float64) bool { + h, c, L := colorful.LabToHcl(l, a, b) + return 10.0 < h && h < 50.0 && 0.1 < c && c < 0.5 && L < 0.5 +} +// Since the above function is pretty restrictive, we set ManySamples to true. +brownies := colorful.SoftPaletteEx(10, colorful.SoftPaletteSettings{isbrowny, 50, true}) +``` + +The following picture shows the palettes generated by all of these methods +(sourcecode in `doc/palettegens/palettegens.go`), in the order they were presented, i.e. +from top to bottom: `Warm`, `FastWarm`, `Happy`, `FastHappy`, `Soft`, +`SoftEx(isbrowny)`. All of them contain some randomness, so YMMV. + +![All example palettes](doc/palettegens/palettegens.png) + +Again, the code used for generating the above image is available as [doc/palettegens/palettegens.go](https://github.com/lucasb-eyer/go-colorful/blob/master/doc/palettegens/palettegens.go). + +### Sorting colors +TODO: Sort using dist fn. + +### Using linear RGB for computations +There are two methods for transforming RGB<->Linear RGB: a fast and almost precise one, +and a slow and precise one. + +```go +r, g, b := colorful.Hex("#FF0000").FastLinearRgb() +``` + +TODO: describe some more. + +### Want to use some other reference point? + +```go +c := colorful.LabWhiteRef(0.507850, 0.040585,-0.370945, colorful.D50) +l, a, b := c.LabWhiteRef(colorful.D50) +``` + +### Reading and writing colors from databases + +The type `HexColor` makes it easy to store colors as strings in a database. It +implements the [https://godoc.org/database/sql#Scanner](database/sql.Scanner) +and [database/sql/driver.Value](https://godoc.org/database/sql/driver.Value) +interfaces which provide automatic type conversion. + +Example: + +```go +var hc HexColor +_, err := db.QueryRow("SELECT '#ff0000';").Scan(&hc) +// hc == HexColor{R: 1, G: 0, B: 0}; err == nil +``` + +FAQ +=== + +### Q: I get all f!@#ed up values! Your library sucks! +A: You probably provided values in the wrong range. For example, RGB values are +expected to reside between 0 and 1, *not* between 0 and 255. Normalize your colors. + +### Q: Lab/Luv/HCl seem broken! Your library sucks! +They look like this: + + + +A: You're likely trying to generate and display colors that can't be represented by RGB, +and thus monitors. When you're trying to convert, say, `HCL(190.0, 1.0, 1.0).RGB255()`, +you're asking for RGB values of `(-2105.254 300.680 286.185)`, which clearly don't exist, +and the `RGB255` function just casts these numbers to `uint8`, creating wrap-around and +what looks like a completely broken gradient. What you want to do, is either use more +reasonable values of colors which actually exist in RGB, or just `Clamp()` the resulting +color to its nearest existing one, living with the consequences: +`HCL(190.0, 1.0, 1.0).Clamp().RGB255()`. It will look something like this: + + + +[Here's an issue going in-depth about this](https://github.com/lucasb-eyer/go-colorful/issues/14), +as well as [my answer](https://github.com/lucasb-eyer/go-colorful/issues/14#issuecomment-324205385), +both with code and pretty pictures. Also note that this was somewhat covered above in the +["Blending colors" section](https://github.com/lucasb-eyer/go-colorful#blending-colors). + +### Q: In a tight loop, conversion to Lab/Luv/HCl/... are slooooow! +A: Yes, they are. +This library aims for correctness, readability, and modularity; it wasn't written with speed in mind. +A large part of the slowness comes from these conversions going through `LinearRgb` which uses powers. +I implemented a fast approximation to `LinearRgb` called `FastLinearRgb` by using Taylor approximations. +The approximation is roughly 5x faster and precise up to roughly 0.5%, +the major caveat being that if the input values are outside the range 0-1, accuracy drops dramatically. +You can use these in your conversions as follows: + +```go +col := // Get your color somehow +l, a, b := XyzToLab(LinearRgbToXyz(col.LinearRgb())) +``` + +If you need faster versions of `Distance*` and `Blend*` that make use of this fast approximation, +feel free to implement them and open a pull-request, I'll happily accept. + +The derivation of these functions can be followed in [this Jupyter notebook](doc/LinearRGB Approximations.ipynb). +Here's the main figure showing the approximation quality: + +![approximation quality](doc/approx-quality.png) + +More speed could be gained by using SIMD instructions in many places. +You can also get more speed for specific conversions by approximating the full conversion function, +but that is outside the scope of this library. +Thanks to [@ZirconiumX](https://github.com/ZirconiumX) for starting this investigation, +see [issue #18](https://github.com/lucasb-eyer/go-colorful/issues/18) for details. + +### Q: Why would `MakeColor` ever fail!? +A: `MakeColor` fails when the alpha channel is zero. In that case, the +conversion is undefined. See [issue 21](https://github.com/lucasb-eyer/go-colorful/issues/21) +as well as the short caveat note in the ["The `color.Color` interface"](README.md#the-colorcolor-interface) +section above. + +Who? +==== + +This library was developed by Lucas Beyer with contributions from +Bastien Dejean (@baskerville), Phil Kulak (@pkulak) and Christian Muehlhaeuser (@muesli). + +It is now maintained by makeworld (@makeworld-the-better-one). + + +## License + +This repo is under the MIT license, see [LICENSE](LICENSE) for details. diff --git a/vendor/github.com/lucasb-eyer/go-colorful/colorgens.go b/vendor/github.com/lucasb-eyer/go-colorful/colorgens.go new file mode 100644 index 0000000000..2e2e49e19f --- /dev/null +++ b/vendor/github.com/lucasb-eyer/go-colorful/colorgens.go @@ -0,0 +1,55 @@ +// Various ways to generate single random colors + +package colorful + +import ( + "math/rand" +) + +// Creates a random dark, "warm" color through a restricted HSV space. +func FastWarmColor() Color { + return Hsv( + rand.Float64()*360.0, + 0.5+rand.Float64()*0.3, + 0.3+rand.Float64()*0.3) +} + +// Creates a random dark, "warm" color through restricted HCL space. +// This is slower than FastWarmColor but will likely give you colors which have +// the same "warmness" if you run it many times. +func WarmColor() (c Color) { + for c = randomWarm(); !c.IsValid(); c = randomWarm() { + } + return +} + +func randomWarm() Color { + return Hcl( + rand.Float64()*360.0, + 0.1+rand.Float64()*0.3, + 0.2+rand.Float64()*0.3) +} + +// Creates a random bright, "pimpy" color through a restricted HSV space. +func FastHappyColor() Color { + return Hsv( + rand.Float64()*360.0, + 0.7+rand.Float64()*0.3, + 0.6+rand.Float64()*0.3) +} + +// Creates a random bright, "pimpy" color through restricted HCL space. +// This is slower than FastHappyColor but will likely give you colors which +// have the same "brightness" if you run it many times. +func HappyColor() (c Color) { + for c = randomPimp(); !c.IsValid(); c = randomPimp() { + } + return +} + +func randomPimp() Color { + return Hcl( + rand.Float64()*360.0, + 0.5+rand.Float64()*0.3, + 0.5+rand.Float64()*0.3) +} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/colors.go b/vendor/github.com/lucasb-eyer/go-colorful/colors.go new file mode 100644 index 0000000000..0d5bffe5db --- /dev/null +++ b/vendor/github.com/lucasb-eyer/go-colorful/colors.go @@ -0,0 +1,979 @@ +// The colorful package provides all kinds of functions for working with colors. +package colorful + +import ( + "fmt" + "image/color" + "math" +) + +// A color is stored internally using sRGB (standard RGB) values in the range 0-1 +type Color struct { + R, G, B float64 +} + +// Implement the Go color.Color interface. +func (col Color) RGBA() (r, g, b, a uint32) { + r = uint32(col.R*65535.0 + 0.5) + g = uint32(col.G*65535.0 + 0.5) + b = uint32(col.B*65535.0 + 0.5) + a = 0xFFFF + return +} + +// Constructs a colorful.Color from something implementing color.Color +func MakeColor(col color.Color) (Color, bool) { + r, g, b, a := col.RGBA() + if a == 0 { + return Color{0, 0, 0}, false + } + + // Since color.Color is alpha pre-multiplied, we need to divide the + // RGB values by alpha again in order to get back the original RGB. + r *= 0xffff + r /= a + g *= 0xffff + g /= a + b *= 0xffff + b /= a + + return Color{float64(r) / 65535.0, float64(g) / 65535.0, float64(b) / 65535.0}, true +} + +// Might come in handy sometimes to reduce boilerplate code. +func (col Color) RGB255() (r, g, b uint8) { + r = uint8(col.R*255.0 + 0.5) + g = uint8(col.G*255.0 + 0.5) + b = uint8(col.B*255.0 + 0.5) + return +} + +// Used to simplify HSLuv testing. +func (col Color) values() (float64, float64, float64) { + return col.R, col.G, col.B +} + +// This is the tolerance used when comparing colors using AlmostEqualRgb. +const Delta = 1.0 / 255.0 + +// This is the default reference white point. +var D65 = [3]float64{0.95047, 1.00000, 1.08883} + +// And another one. +var D50 = [3]float64{0.96422, 1.00000, 0.82521} + +// Checks whether the color exists in RGB space, i.e. all values are in [0..1] +func (c Color) IsValid() bool { + return 0.0 <= c.R && c.R <= 1.0 && + 0.0 <= c.G && c.G <= 1.0 && + 0.0 <= c.B && c.B <= 1.0 +} + +// clamp01 clamps from 0 to 1. +func clamp01(v float64) float64 { + return math.Max(0.0, math.Min(v, 1.0)) +} + +// Returns Clamps the color into valid range, clamping each value to [0..1] +// If the color is valid already, this is a no-op. +func (c Color) Clamped() Color { + return Color{clamp01(c.R), clamp01(c.G), clamp01(c.B)} +} + +func sq(v float64) float64 { + return v * v +} + +func cub(v float64) float64 { + return v * v * v +} + +// DistanceRgb computes the distance between two colors in RGB space. +// This is not a good measure! Rather do it in Lab space. +func (c1 Color) DistanceRgb(c2 Color) float64 { + return math.Sqrt(sq(c1.R-c2.R) + sq(c1.G-c2.G) + sq(c1.B-c2.B)) +} + +// DistanceLinearRGB computes the distance between two colors in linear RGB +// space. This is not useful for measuring how humans perceive color, but +// might be useful for other things, like dithering. +func (c1 Color) DistanceLinearRGB(c2 Color) float64 { + r1, g1, b1 := c1.LinearRgb() + r2, g2, b2 := c2.LinearRgb() + return math.Sqrt(sq(r1-r2) + sq(g1-g2) + sq(b1-b2)) +} + +// Check for equality between colors within the tolerance Delta (1/255). +func (c1 Color) AlmostEqualRgb(c2 Color) bool { + return math.Abs(c1.R-c2.R)+ + math.Abs(c1.G-c2.G)+ + math.Abs(c1.B-c2.B) < 3.0*Delta +} + +// You don't really want to use this, do you? Go for BlendLab, BlendLuv or BlendHcl. +func (c1 Color) BlendRgb(c2 Color, t float64) Color { + return Color{c1.R + t*(c2.R-c1.R), + c1.G + t*(c2.G-c1.G), + c1.B + t*(c2.B-c1.B)} +} + +// Utility used by Hxx color-spaces for interpolating between two angles in [0,360]. +func interp_angle(a0, a1, t float64) float64 { + // Based on the answer here: http://stackoverflow.com/a/14498790/2366315 + // With potential proof that it works here: http://math.stackexchange.com/a/2144499 + delta := math.Mod(math.Mod(a1-a0, 360.0)+540, 360.0) - 180.0 + return math.Mod(a0+t*delta+360.0, 360.0) +} + +/// HSV /// +/////////// +// From http://en.wikipedia.org/wiki/HSL_and_HSV +// Note that h is in [0..360] and s,v in [0..1] + +// Hsv returns the Hue [0..360], Saturation and Value [0..1] of the color. +func (col Color) Hsv() (h, s, v float64) { + min := math.Min(math.Min(col.R, col.G), col.B) + v = math.Max(math.Max(col.R, col.G), col.B) + C := v - min + + s = 0.0 + if v != 0.0 { + s = C / v + } + + h = 0.0 // We use 0 instead of undefined as in wp. + if min != v { + if v == col.R { + h = math.Mod((col.G-col.B)/C, 6.0) + } + if v == col.G { + h = (col.B-col.R)/C + 2.0 + } + if v == col.B { + h = (col.R-col.G)/C + 4.0 + } + h *= 60.0 + if h < 0.0 { + h += 360.0 + } + } + return +} + +// Hsv creates a new Color given a Hue in [0..360], a Saturation and a Value in [0..1] +func Hsv(H, S, V float64) Color { + Hp := H / 60.0 + C := V * S + X := C * (1.0 - math.Abs(math.Mod(Hp, 2.0)-1.0)) + + m := V - C + r, g, b := 0.0, 0.0, 0.0 + + switch { + case 0.0 <= Hp && Hp < 1.0: + r = C + g = X + case 1.0 <= Hp && Hp < 2.0: + r = X + g = C + case 2.0 <= Hp && Hp < 3.0: + g = C + b = X + case 3.0 <= Hp && Hp < 4.0: + g = X + b = C + case 4.0 <= Hp && Hp < 5.0: + r = X + b = C + case 5.0 <= Hp && Hp < 6.0: + r = C + b = X + } + + return Color{m + r, m + g, m + b} +} + +// You don't really want to use this, do you? Go for BlendLab, BlendLuv or BlendHcl. +func (c1 Color) BlendHsv(c2 Color, t float64) Color { + h1, s1, v1 := c1.Hsv() + h2, s2, v2 := c2.Hsv() + + // We know that h are both in [0..360] + return Hsv(interp_angle(h1, h2, t), s1+t*(s2-s1), v1+t*(v2-v1)) +} + +/// HSL /// +/////////// + +// Hsl returns the Hue [0..360], Saturation [0..1], and Luminance (lightness) [0..1] of the color. +func (col Color) Hsl() (h, s, l float64) { + min := math.Min(math.Min(col.R, col.G), col.B) + max := math.Max(math.Max(col.R, col.G), col.B) + + l = (max + min) / 2 + + if min == max { + s = 0 + h = 0 + } else { + if l < 0.5 { + s = (max - min) / (max + min) + } else { + s = (max - min) / (2.0 - max - min) + } + + if max == col.R { + h = (col.G - col.B) / (max - min) + } else if max == col.G { + h = 2.0 + (col.B-col.R)/(max-min) + } else { + h = 4.0 + (col.R-col.G)/(max-min) + } + + h *= 60 + + if h < 0 { + h += 360 + } + } + + return +} + +// Hsl creates a new Color given a Hue in [0..360], a Saturation [0..1], and a Luminance (lightness) in [0..1] +func Hsl(h, s, l float64) Color { + if s == 0 { + return Color{l, l, l} + } + + var r, g, b float64 + var t1 float64 + var t2 float64 + var tr float64 + var tg float64 + var tb float64 + + if l < 0.5 { + t1 = l * (1.0 + s) + } else { + t1 = l + s - l*s + } + + t2 = 2*l - t1 + h /= 360 + tr = h + 1.0/3.0 + tg = h + tb = h - 1.0/3.0 + + if tr < 0 { + tr++ + } + if tr > 1 { + tr-- + } + if tg < 0 { + tg++ + } + if tg > 1 { + tg-- + } + if tb < 0 { + tb++ + } + if tb > 1 { + tb-- + } + + // Red + if 6*tr < 1 { + r = t2 + (t1-t2)*6*tr + } else if 2*tr < 1 { + r = t1 + } else if 3*tr < 2 { + r = t2 + (t1-t2)*(2.0/3.0-tr)*6 + } else { + r = t2 + } + + // Green + if 6*tg < 1 { + g = t2 + (t1-t2)*6*tg + } else if 2*tg < 1 { + g = t1 + } else if 3*tg < 2 { + g = t2 + (t1-t2)*(2.0/3.0-tg)*6 + } else { + g = t2 + } + + // Blue + if 6*tb < 1 { + b = t2 + (t1-t2)*6*tb + } else if 2*tb < 1 { + b = t1 + } else if 3*tb < 2 { + b = t2 + (t1-t2)*(2.0/3.0-tb)*6 + } else { + b = t2 + } + + return Color{r, g, b} +} + +/// Hex /// +/////////// + +// Hex returns the hex "html" representation of the color, as in #ff0080. +func (col Color) Hex() string { + // Add 0.5 for rounding + return fmt.Sprintf("#%02x%02x%02x", uint8(col.R*255.0+0.5), uint8(col.G*255.0+0.5), uint8(col.B*255.0+0.5)) +} + +// Hex parses a "html" hex color-string, either in the 3 "#f0c" or 6 "#ff1034" digits form. +func Hex(scol string) (Color, error) { + format := "#%02x%02x%02x" + factor := 1.0 / 255.0 + if len(scol) == 4 { + format = "#%1x%1x%1x" + factor = 1.0 / 15.0 + } + + var r, g, b uint8 + n, err := fmt.Sscanf(scol, format, &r, &g, &b) + if err != nil { + return Color{}, err + } + if n != 3 { + return Color{}, fmt.Errorf("color: %v is not a hex-color", scol) + } + + return Color{float64(r) * factor, float64(g) * factor, float64(b) * factor}, nil +} + +/// Linear /// +////////////// +// http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/ +// http://www.brucelindbloom.com/Eqn_RGB_to_XYZ.html + +func linearize(v float64) float64 { + if v <= 0.04045 { + return v / 12.92 + } + return math.Pow((v+0.055)/1.055, 2.4) +} + +// LinearRgb converts the color into the linear RGB space (see http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/). +func (col Color) LinearRgb() (r, g, b float64) { + r = linearize(col.R) + g = linearize(col.G) + b = linearize(col.B) + return +} + +// A much faster and still quite precise linearization using a 6th-order Taylor approximation. +// See the accompanying Jupyter notebook for derivation of the constants. +func linearize_fast(v float64) float64 { + v1 := v - 0.5 + v2 := v1 * v1 + v3 := v2 * v1 + v4 := v2 * v2 + //v5 := v3*v2 + return -0.248750514614486 + 0.925583310193438*v + 1.16740237321695*v2 + 0.280457026598666*v3 - 0.0757991963780179*v4 //+ 0.0437040411548932*v5 +} + +// FastLinearRgb is much faster than and almost as accurate as LinearRgb. +// BUT it is important to NOTE that they only produce good results for valid colors r,g,b in [0,1]. +func (col Color) FastLinearRgb() (r, g, b float64) { + r = linearize_fast(col.R) + g = linearize_fast(col.G) + b = linearize_fast(col.B) + return +} + +func delinearize(v float64) float64 { + if v <= 0.0031308 { + return 12.92 * v + } + return 1.055*math.Pow(v, 1.0/2.4) - 0.055 +} + +// LinearRgb creates an sRGB color out of the given linear RGB color (see http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/). +func LinearRgb(r, g, b float64) Color { + return Color{delinearize(r), delinearize(g), delinearize(b)} +} + +func delinearize_fast(v float64) float64 { + // This function (fractional root) is much harder to linearize, so we need to split. + if v > 0.2 { + v1 := v - 0.6 + v2 := v1 * v1 + v3 := v2 * v1 + v4 := v2 * v2 + v5 := v3 * v2 + return 0.442430344268235 + 0.592178981271708*v - 0.287864782562636*v2 + 0.253214392068985*v3 - 0.272557158129811*v4 + 0.325554383321718*v5 + } else if v > 0.03 { + v1 := v - 0.115 + v2 := v1 * v1 + v3 := v2 * v1 + v4 := v2 * v2 + v5 := v3 * v2 + return 0.194915592891669 + 1.55227076330229*v - 3.93691860257828*v2 + 18.0679839248761*v3 - 101.468750302746*v4 + 632.341487393927*v5 + } else { + v1 := v - 0.015 + v2 := v1 * v1 + v3 := v2 * v1 + v4 := v2 * v2 + v5 := v3 * v2 + // You can clearly see from the involved constants that the low-end is highly nonlinear. + return 0.0519565234928877 + 5.09316778537561*v - 99.0338180489702*v2 + 3484.52322764895*v3 - 150028.083412663*v4 + 7168008.42971613*v5 + } +} + +// FastLinearRgb is much faster than and almost as accurate as LinearRgb. +// BUT it is important to NOTE that they only produce good results for valid inputs r,g,b in [0,1]. +func FastLinearRgb(r, g, b float64) Color { + return Color{delinearize_fast(r), delinearize_fast(g), delinearize_fast(b)} +} + +// XyzToLinearRgb converts from CIE XYZ-space to Linear RGB space. +func XyzToLinearRgb(x, y, z float64) (r, g, b float64) { + r = 3.2409699419045214*x - 1.5373831775700935*y - 0.49861076029300328*z + g = -0.96924363628087983*x + 1.8759675015077207*y + 0.041555057407175613*z + b = 0.055630079696993609*x - 0.20397695888897657*y + 1.0569715142428786*z + return +} + +func LinearRgbToXyz(r, g, b float64) (x, y, z float64) { + x = 0.41239079926595948*r + 0.35758433938387796*g + 0.18048078840183429*b + y = 0.21263900587151036*r + 0.71516867876775593*g + 0.072192315360733715*b + z = 0.019330818715591851*r + 0.11919477979462599*g + 0.95053215224966058*b + return +} + +/// XYZ /// +/////////// +// http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/ + +func (col Color) Xyz() (x, y, z float64) { + return LinearRgbToXyz(col.LinearRgb()) +} + +func Xyz(x, y, z float64) Color { + return LinearRgb(XyzToLinearRgb(x, y, z)) +} + +/// xyY /// +/////////// +// http://www.brucelindbloom.com/Eqn_XYZ_to_xyY.html + +// Well, the name is bad, since it's xyY but Golang needs me to start with a +// capital letter to make the method public. +func XyzToXyy(X, Y, Z float64) (x, y, Yout float64) { + return XyzToXyyWhiteRef(X, Y, Z, D65) +} + +func XyzToXyyWhiteRef(X, Y, Z float64, wref [3]float64) (x, y, Yout float64) { + Yout = Y + N := X + Y + Z + if math.Abs(N) < 1e-14 { + // When we have black, Bruce Lindbloom recommends to use + // the reference white's chromacity for x and y. + x = wref[0] / (wref[0] + wref[1] + wref[2]) + y = wref[1] / (wref[0] + wref[1] + wref[2]) + } else { + x = X / N + y = Y / N + } + return +} + +func XyyToXyz(x, y, Y float64) (X, Yout, Z float64) { + Yout = Y + + if -1e-14 < y && y < 1e-14 { + X = 0.0 + Z = 0.0 + } else { + X = Y / y * x + Z = Y / y * (1.0 - x - y) + } + + return +} + +// Converts the given color to CIE xyY space using D65 as reference white. +// (Note that the reference white is only used for black input.) +// x, y and Y are in [0..1] +func (col Color) Xyy() (x, y, Y float64) { + return XyzToXyy(col.Xyz()) +} + +// Converts the given color to CIE xyY space, taking into account +// a given reference white. (i.e. the monitor's white) +// (Note that the reference white is only used for black input.) +// x, y and Y are in [0..1] +func (col Color) XyyWhiteRef(wref [3]float64) (x, y, Y float64) { + X, Y2, Z := col.Xyz() + return XyzToXyyWhiteRef(X, Y2, Z, wref) +} + +// Generates a color by using data given in CIE xyY space. +// x, y and Y are in [0..1] +func Xyy(x, y, Y float64) Color { + return Xyz(XyyToXyz(x, y, Y)) +} + +/// L*a*b* /// +////////////// +// http://en.wikipedia.org/wiki/Lab_color_space#CIELAB-CIEXYZ_conversions +// For L*a*b*, we need to L*a*b*<->XYZ->RGB and the first one is device dependent. + +func lab_f(t float64) float64 { + if t > 6.0/29.0*6.0/29.0*6.0/29.0 { + return math.Cbrt(t) + } + return t/3.0*29.0/6.0*29.0/6.0 + 4.0/29.0 +} + +func XyzToLab(x, y, z float64) (l, a, b float64) { + // Use D65 white as reference point by default. + // http://www.fredmiranda.com/forum/topic/1035332 + // http://en.wikipedia.org/wiki/Standard_illuminant + return XyzToLabWhiteRef(x, y, z, D65) +} + +func XyzToLabWhiteRef(x, y, z float64, wref [3]float64) (l, a, b float64) { + fy := lab_f(y / wref[1]) + l = 1.16*fy - 0.16 + a = 5.0 * (lab_f(x/wref[0]) - fy) + b = 2.0 * (fy - lab_f(z/wref[2])) + return +} + +func lab_finv(t float64) float64 { + if t > 6.0/29.0 { + return t * t * t + } + return 3.0 * 6.0 / 29.0 * 6.0 / 29.0 * (t - 4.0/29.0) +} + +func LabToXyz(l, a, b float64) (x, y, z float64) { + // D65 white (see above). + return LabToXyzWhiteRef(l, a, b, D65) +} + +func LabToXyzWhiteRef(l, a, b float64, wref [3]float64) (x, y, z float64) { + l2 := (l + 0.16) / 1.16 + x = wref[0] * lab_finv(l2+a/5.0) + y = wref[1] * lab_finv(l2) + z = wref[2] * lab_finv(l2-b/2.0) + return +} + +// Converts the given color to CIE L*a*b* space using D65 as reference white. +func (col Color) Lab() (l, a, b float64) { + return XyzToLab(col.Xyz()) +} + +// Converts the given color to CIE L*a*b* space, taking into account +// a given reference white. (i.e. the monitor's white) +func (col Color) LabWhiteRef(wref [3]float64) (l, a, b float64) { + x, y, z := col.Xyz() + return XyzToLabWhiteRef(x, y, z, wref) +} + +// Generates a color by using data given in CIE L*a*b* space using D65 as reference white. +// WARNING: many combinations of `l`, `a`, and `b` values do not have corresponding +// valid RGB values, check the FAQ in the README if you're unsure. +func Lab(l, a, b float64) Color { + return Xyz(LabToXyz(l, a, b)) +} + +// Generates a color by using data given in CIE L*a*b* space, taking +// into account a given reference white. (i.e. the monitor's white) +func LabWhiteRef(l, a, b float64, wref [3]float64) Color { + return Xyz(LabToXyzWhiteRef(l, a, b, wref)) +} + +// DistanceLab is a good measure of visual similarity between two colors! +// A result of 0 would mean identical colors, while a result of 1 or higher +// means the colors differ a lot. +func (c1 Color) DistanceLab(c2 Color) float64 { + l1, a1, b1 := c1.Lab() + l2, a2, b2 := c2.Lab() + return math.Sqrt(sq(l1-l2) + sq(a1-a2) + sq(b1-b2)) +} + +// DistanceCIE76 is the same as DistanceLab. +func (c1 Color) DistanceCIE76(c2 Color) float64 { + return c1.DistanceLab(c2) +} + +// Uses the CIE94 formula to calculate color distance. More accurate than +// DistanceLab, but also more work. +func (cl Color) DistanceCIE94(cr Color) float64 { + l1, a1, b1 := cl.Lab() + l2, a2, b2 := cr.Lab() + + // NOTE: Since all those formulas expect L,a,b values 100x larger than we + // have them in this library, we either need to adjust all constants + // in the formula, or convert the ranges of L,a,b before, and then + // scale the distances down again. The latter is less error-prone. + l1, a1, b1 = l1*100.0, a1*100.0, b1*100.0 + l2, a2, b2 = l2*100.0, a2*100.0, b2*100.0 + + kl := 1.0 // 2.0 for textiles + kc := 1.0 + kh := 1.0 + k1 := 0.045 // 0.048 for textiles + k2 := 0.015 // 0.014 for textiles. + + deltaL := l1 - l2 + c1 := math.Sqrt(sq(a1) + sq(b1)) + c2 := math.Sqrt(sq(a2) + sq(b2)) + deltaCab := c1 - c2 + + // Not taking Sqrt here for stability, and it's unnecessary. + deltaHab2 := sq(a1-a2) + sq(b1-b2) - sq(deltaCab) + sl := 1.0 + sc := 1.0 + k1*c1 + sh := 1.0 + k2*c1 + + vL2 := sq(deltaL / (kl * sl)) + vC2 := sq(deltaCab / (kc * sc)) + vH2 := deltaHab2 / sq(kh*sh) + + return math.Sqrt(vL2+vC2+vH2) * 0.01 // See above. +} + +// DistanceCIEDE2000 uses the Delta E 2000 formula to calculate color +// distance. It is more expensive but more accurate than both DistanceLab +// and DistanceCIE94. +func (cl Color) DistanceCIEDE2000(cr Color) float64 { + return cl.DistanceCIEDE2000klch(cr, 1.0, 1.0, 1.0) +} + +// DistanceCIEDE2000klch uses the Delta E 2000 formula with custom values +// for the weighting factors kL, kC, and kH. +func (cl Color) DistanceCIEDE2000klch(cr Color, kl, kc, kh float64) float64 { + l1, a1, b1 := cl.Lab() + l2, a2, b2 := cr.Lab() + + // As with CIE94, we scale up the ranges of L,a,b beforehand and scale + // them down again afterwards. + l1, a1, b1 = l1*100.0, a1*100.0, b1*100.0 + l2, a2, b2 = l2*100.0, a2*100.0, b2*100.0 + + cab1 := math.Sqrt(sq(a1) + sq(b1)) + cab2 := math.Sqrt(sq(a2) + sq(b2)) + cabmean := (cab1 + cab2) / 2 + + g := 0.5 * (1 - math.Sqrt(math.Pow(cabmean, 7)/(math.Pow(cabmean, 7)+math.Pow(25, 7)))) + ap1 := (1 + g) * a1 + ap2 := (1 + g) * a2 + cp1 := math.Sqrt(sq(ap1) + sq(b1)) + cp2 := math.Sqrt(sq(ap2) + sq(b2)) + + hp1 := 0.0 + if b1 != ap1 || ap1 != 0 { + hp1 = math.Atan2(b1, ap1) + if hp1 < 0 { + hp1 += math.Pi * 2 + } + hp1 *= 180 / math.Pi + } + hp2 := 0.0 + if b2 != ap2 || ap2 != 0 { + hp2 = math.Atan2(b2, ap2) + if hp2 < 0 { + hp2 += math.Pi * 2 + } + hp2 *= 180 / math.Pi + } + + deltaLp := l2 - l1 + deltaCp := cp2 - cp1 + dhp := 0.0 + cpProduct := cp1 * cp2 + if cpProduct != 0 { + dhp = hp2 - hp1 + if dhp > 180 { + dhp -= 360 + } else if dhp < -180 { + dhp += 360 + } + } + deltaHp := 2 * math.Sqrt(cpProduct) * math.Sin(dhp/2*math.Pi/180) + + lpmean := (l1 + l2) / 2 + cpmean := (cp1 + cp2) / 2 + hpmean := hp1 + hp2 + if cpProduct != 0 { + hpmean /= 2 + if math.Abs(hp1-hp2) > 180 { + if hp1+hp2 < 360 { + hpmean += 180 + } else { + hpmean -= 180 + } + } + } + + t := 1 - 0.17*math.Cos((hpmean-30)*math.Pi/180) + 0.24*math.Cos(2*hpmean*math.Pi/180) + 0.32*math.Cos((3*hpmean+6)*math.Pi/180) - 0.2*math.Cos((4*hpmean-63)*math.Pi/180) + deltaTheta := 30 * math.Exp(-sq((hpmean-275)/25)) + rc := 2 * math.Sqrt(math.Pow(cpmean, 7)/(math.Pow(cpmean, 7)+math.Pow(25, 7))) + sl := 1 + (0.015*sq(lpmean-50))/math.Sqrt(20+sq(lpmean-50)) + sc := 1 + 0.045*cpmean + sh := 1 + 0.015*cpmean*t + rt := -math.Sin(2*deltaTheta*math.Pi/180) * rc + + return math.Sqrt(sq(deltaLp/(kl*sl))+sq(deltaCp/(kc*sc))+sq(deltaHp/(kh*sh))+rt*(deltaCp/(kc*sc))*(deltaHp/(kh*sh))) * 0.01 +} + +// BlendLab blends two colors in the L*a*b* color-space, which should result in a smoother blend. +// t == 0 results in c1, t == 1 results in c2 +func (c1 Color) BlendLab(c2 Color, t float64) Color { + l1, a1, b1 := c1.Lab() + l2, a2, b2 := c2.Lab() + return Lab(l1+t*(l2-l1), + a1+t*(a2-a1), + b1+t*(b2-b1)) +} + +/// L*u*v* /// +////////////// +// http://en.wikipedia.org/wiki/CIELUV#XYZ_.E2.86.92_CIELUV_and_CIELUV_.E2.86.92_XYZ_conversions +// For L*u*v*, we need to L*u*v*<->XYZ<->RGB and the first one is device dependent. + +func XyzToLuv(x, y, z float64) (l, a, b float64) { + // Use D65 white as reference point by default. + // http://www.fredmiranda.com/forum/topic/1035332 + // http://en.wikipedia.org/wiki/Standard_illuminant + return XyzToLuvWhiteRef(x, y, z, D65) +} + +func XyzToLuvWhiteRef(x, y, z float64, wref [3]float64) (l, u, v float64) { + if y/wref[1] <= 6.0/29.0*6.0/29.0*6.0/29.0 { + l = y / wref[1] * (29.0 / 3.0 * 29.0 / 3.0 * 29.0 / 3.0) / 100.0 + } else { + l = 1.16*math.Cbrt(y/wref[1]) - 0.16 + } + ubis, vbis := xyz_to_uv(x, y, z) + un, vn := xyz_to_uv(wref[0], wref[1], wref[2]) + u = 13.0 * l * (ubis - un) + v = 13.0 * l * (vbis - vn) + return +} + +// For this part, we do as R's graphics.hcl does, not as wikipedia does. +// Or is it the same? +func xyz_to_uv(x, y, z float64) (u, v float64) { + denom := x + 15.0*y + 3.0*z + if denom == 0.0 { + u, v = 0.0, 0.0 + } else { + u = 4.0 * x / denom + v = 9.0 * y / denom + } + return +} + +func LuvToXyz(l, u, v float64) (x, y, z float64) { + // D65 white (see above). + return LuvToXyzWhiteRef(l, u, v, D65) +} + +func LuvToXyzWhiteRef(l, u, v float64, wref [3]float64) (x, y, z float64) { + //y = wref[1] * lab_finv((l + 0.16) / 1.16) + if l <= 0.08 { + y = wref[1] * l * 100.0 * 3.0 / 29.0 * 3.0 / 29.0 * 3.0 / 29.0 + } else { + y = wref[1] * cub((l+0.16)/1.16) + } + un, vn := xyz_to_uv(wref[0], wref[1], wref[2]) + if l != 0.0 { + ubis := u/(13.0*l) + un + vbis := v/(13.0*l) + vn + x = y * 9.0 * ubis / (4.0 * vbis) + z = y * (12.0 - 3.0*ubis - 20.0*vbis) / (4.0 * vbis) + } else { + x, y = 0.0, 0.0 + } + return +} + +// Converts the given color to CIE L*u*v* space using D65 as reference white. +// L* is in [0..1] and both u* and v* are in about [-1..1] +func (col Color) Luv() (l, u, v float64) { + return XyzToLuv(col.Xyz()) +} + +// Converts the given color to CIE L*u*v* space, taking into account +// a given reference white. (i.e. the monitor's white) +// L* is in [0..1] and both u* and v* are in about [-1..1] +func (col Color) LuvWhiteRef(wref [3]float64) (l, u, v float64) { + x, y, z := col.Xyz() + return XyzToLuvWhiteRef(x, y, z, wref) +} + +// Generates a color by using data given in CIE L*u*v* space using D65 as reference white. +// L* is in [0..1] and both u* and v* are in about [-1..1] +// WARNING: many combinations of `l`, `u`, and `v` values do not have corresponding +// valid RGB values, check the FAQ in the README if you're unsure. +func Luv(l, u, v float64) Color { + return Xyz(LuvToXyz(l, u, v)) +} + +// Generates a color by using data given in CIE L*u*v* space, taking +// into account a given reference white. (i.e. the monitor's white) +// L* is in [0..1] and both u* and v* are in about [-1..1] +func LuvWhiteRef(l, u, v float64, wref [3]float64) Color { + return Xyz(LuvToXyzWhiteRef(l, u, v, wref)) +} + +// DistanceLuv is a good measure of visual similarity between two colors! +// A result of 0 would mean identical colors, while a result of 1 or higher +// means the colors differ a lot. +func (c1 Color) DistanceLuv(c2 Color) float64 { + l1, u1, v1 := c1.Luv() + l2, u2, v2 := c2.Luv() + return math.Sqrt(sq(l1-l2) + sq(u1-u2) + sq(v1-v2)) +} + +// BlendLuv blends two colors in the CIE-L*u*v* color-space, which should result in a smoother blend. +// t == 0 results in c1, t == 1 results in c2 +func (c1 Color) BlendLuv(c2 Color, t float64) Color { + l1, u1, v1 := c1.Luv() + l2, u2, v2 := c2.Luv() + return Luv(l1+t*(l2-l1), + u1+t*(u2-u1), + v1+t*(v2-v1)) +} + +/// HCL /// +/////////// +// HCL is nothing else than L*a*b* in cylindrical coordinates! +// (this was wrong on English wikipedia, I fixed it, let's hope the fix stays.) +// But it is widely popular since it is a "correct HSV" +// http://www.hunterlab.com/appnotes/an09_96a.pdf + +// Converts the given color to HCL space using D65 as reference white. +// H values are in [0..360], C and L values are in [0..1] although C can overshoot 1.0 +func (col Color) Hcl() (h, c, l float64) { + return col.HclWhiteRef(D65) +} + +func LabToHcl(L, a, b float64) (h, c, l float64) { + // Oops, floating point workaround necessary if a ~= b and both are very small (i.e. almost zero). + if math.Abs(b-a) > 1e-4 && math.Abs(a) > 1e-4 { + h = math.Mod(57.29577951308232087721*math.Atan2(b, a)+360.0, 360.0) // Rad2Deg + } else { + h = 0.0 + } + c = math.Sqrt(sq(a) + sq(b)) + l = L + return +} + +// Converts the given color to HCL space, taking into account +// a given reference white. (i.e. the monitor's white) +// H values are in [0..360], C and L values are in [0..1] +func (col Color) HclWhiteRef(wref [3]float64) (h, c, l float64) { + L, a, b := col.LabWhiteRef(wref) + return LabToHcl(L, a, b) +} + +// Generates a color by using data given in HCL space using D65 as reference white. +// H values are in [0..360], C and L values are in [0..1] +// WARNING: many combinations of `h`, `c`, and `l` values do not have corresponding +// valid RGB values, check the FAQ in the README if you're unsure. +func Hcl(h, c, l float64) Color { + return HclWhiteRef(h, c, l, D65) +} + +func HclToLab(h, c, l float64) (L, a, b float64) { + H := 0.01745329251994329576 * h // Deg2Rad + a = c * math.Cos(H) + b = c * math.Sin(H) + L = l + return +} + +// Generates a color by using data given in HCL space, taking +// into account a given reference white. (i.e. the monitor's white) +// H values are in [0..360], C and L values are in [0..1] +func HclWhiteRef(h, c, l float64, wref [3]float64) Color { + L, a, b := HclToLab(h, c, l) + return LabWhiteRef(L, a, b, wref) +} + +// BlendHcl blends two colors in the CIE-L*C*h° color-space, which should result in a smoother blend. +// t == 0 results in c1, t == 1 results in c2 +func (col1 Color) BlendHcl(col2 Color, t float64) Color { + h1, c1, l1 := col1.Hcl() + h2, c2, l2 := col2.Hcl() + + // We know that h are both in [0..360] + return Hcl(interp_angle(h1, h2, t), c1+t*(c2-c1), l1+t*(l2-l1)).Clamped() +} + +// LuvLch + +// Converts the given color to LuvLCh space using D65 as reference white. +// h values are in [0..360], C and L values are in [0..1] although C can overshoot 1.0 +func (col Color) LuvLCh() (l, c, h float64) { + return col.LuvLChWhiteRef(D65) +} + +func LuvToLuvLCh(L, u, v float64) (l, c, h float64) { + // Oops, floating point workaround necessary if u ~= v and both are very small (i.e. almost zero). + if math.Abs(v-u) > 1e-4 && math.Abs(u) > 1e-4 { + h = math.Mod(57.29577951308232087721*math.Atan2(v, u)+360.0, 360.0) // Rad2Deg + } else { + h = 0.0 + } + l = L + c = math.Sqrt(sq(u) + sq(v)) + return +} + +// Converts the given color to LuvLCh space, taking into account +// a given reference white. (i.e. the monitor's white) +// h values are in [0..360], c and l values are in [0..1] +func (col Color) LuvLChWhiteRef(wref [3]float64) (l, c, h float64) { + return LuvToLuvLCh(col.LuvWhiteRef(wref)) +} + +// Generates a color by using data given in LuvLCh space using D65 as reference white. +// h values are in [0..360], C and L values are in [0..1] +// WARNING: many combinations of `l`, `c`, and `h` values do not have corresponding +// valid RGB values, check the FAQ in the README if you're unsure. +func LuvLCh(l, c, h float64) Color { + return LuvLChWhiteRef(l, c, h, D65) +} + +func LuvLChToLuv(l, c, h float64) (L, u, v float64) { + H := 0.01745329251994329576 * h // Deg2Rad + u = c * math.Cos(H) + v = c * math.Sin(H) + L = l + return +} + +// Generates a color by using data given in LuvLCh space, taking +// into account a given reference white. (i.e. the monitor's white) +// h values are in [0..360], C and L values are in [0..1] +func LuvLChWhiteRef(l, c, h float64, wref [3]float64) Color { + L, u, v := LuvLChToLuv(l, c, h) + return LuvWhiteRef(L, u, v, wref) +} + +// BlendLuvLCh blends two colors in the cylindrical CIELUV color space. +// t == 0 results in c1, t == 1 results in c2 +func (col1 Color) BlendLuvLCh(col2 Color, t float64) Color { + l1, c1, h1 := col1.LuvLCh() + l2, c2, h2 := col2.LuvLCh() + + // We know that h are both in [0..360] + return LuvLCh(l1+t*(l2-l1), c1+t*(c2-c1), interp_angle(h1, h2, t)) +} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/go.mod b/vendor/github.com/lucasb-eyer/go-colorful/go.mod new file mode 100644 index 0000000000..35925f3d72 --- /dev/null +++ b/vendor/github.com/lucasb-eyer/go-colorful/go.mod @@ -0,0 +1,3 @@ +module github.com/lucasb-eyer/go-colorful + +go 1.12 diff --git a/vendor/github.com/lucasb-eyer/go-colorful/happy_palettegen.go b/vendor/github.com/lucasb-eyer/go-colorful/happy_palettegen.go new file mode 100644 index 0000000000..bb66dfa4f9 --- /dev/null +++ b/vendor/github.com/lucasb-eyer/go-colorful/happy_palettegen.go @@ -0,0 +1,25 @@ +package colorful + +import ( + "math/rand" +) + +// Uses the HSV color space to generate colors with similar S,V but distributed +// evenly along their Hue. This is fast but not always pretty. +// If you've got time to spare, use Lab (the non-fast below). +func FastHappyPalette(colorsCount int) (colors []Color) { + colors = make([]Color, colorsCount) + + for i := 0; i < colorsCount; i++ { + colors[i] = Hsv(float64(i)*(360.0/float64(colorsCount)), 0.8+rand.Float64()*0.2, 0.65+rand.Float64()*0.2) + } + return +} + +func HappyPalette(colorsCount int) ([]Color, error) { + pimpy := func(l, a, b float64) bool { + _, c, _ := LabToHcl(l, a, b) + return 0.3 <= c && 0.4 <= l && l <= 0.8 + } + return SoftPaletteEx(colorsCount, SoftPaletteSettings{pimpy, 50, true}) +} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go b/vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go new file mode 100644 index 0000000000..76f31d8f9f --- /dev/null +++ b/vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go @@ -0,0 +1,67 @@ +package colorful + +import ( + "database/sql/driver" + "encoding/json" + "fmt" + "reflect" +) + +// A HexColor is a Color stored as a hex string "#rrggbb". It implements the +// database/sql.Scanner, database/sql/driver.Value, +// encoding/json.Unmarshaler and encoding/json.Marshaler interfaces. +type HexColor Color + +type errUnsupportedType struct { + got interface{} + want reflect.Type +} + +func (hc *HexColor) Scan(value interface{}) error { + s, ok := value.(string) + if !ok { + return errUnsupportedType{got: reflect.TypeOf(value), want: reflect.TypeOf("")} + } + c, err := Hex(s) + if err != nil { + return err + } + *hc = HexColor(c) + return nil +} + +func (hc *HexColor) Value() (driver.Value, error) { + return Color(*hc).Hex(), nil +} + +func (e errUnsupportedType) Error() string { + return fmt.Sprintf("unsupported type: got %v, want a %s", e.got, e.want) +} + +func (hc *HexColor) UnmarshalJSON(data []byte) error { + var hexCode string + if err := json.Unmarshal(data, &hexCode); err != nil { + return err + } + + var col, err = Hex(hexCode) + if err != nil { + return err + } + *hc = HexColor(col) + return nil +} + +func (hc HexColor) MarshalJSON() ([]byte, error) { + return json.Marshal(Color(hc).Hex()) +} + +// Decode - deserialize function for https://github.com/kelseyhightower/envconfig +func (hc *HexColor) Decode(hexCode string) error { + var col, err = Hex(hexCode) + if err != nil { + return err + } + *hc = HexColor(col) + return nil +} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/hsluv-snapshot-rev4.json b/vendor/github.com/lucasb-eyer/go-colorful/hsluv-snapshot-rev4.json new file mode 100644 index 0000000000..16354abf51 --- /dev/null +++ b/vendor/github.com/lucasb-eyer/go-colorful/hsluv-snapshot-rev4.json @@ -0,0 +1 @@ +{"#11ee00":{"lch":[82.5213119008325577,127.202882727266427,127.478988192005161],"luv":[82.5213119008325577,-77.3991947082883627,100.945222931227221],"rgb":[0.0666666666666666657,0.933333333333333348,0],"xyz":[0.308043578886299796,0.612655858810891907,0.102019012460713238],"hpluv":[127.478988192005161,308.195222762673438,82.5213119008325577],"hsluv":[127.478988192005161,100.000000000002416,82.5213119008325577]},"#11ee11":{"lch":[82.5429986110943759,126.352581314528209,127.715012949240403],"luv":[82.5429986110943759,-77.2942129186682,99.9528861720763473],"rgb":[0.0666666666666666657,0.933333333333333348,0.0666666666666666657],"xyz":[0.3090552443859369,0.613060525010746815,0.107347117425468874],"hpluv":[127.715012949240403,306.573296560288782,82.5429986110943759],"hsluv":[127.715012949240403,98.9038130800949205,82.5429986110943759]},"#11ee22":{"lch":[82.5831747617793184,124.791738379333623,128.158354445562821],"luv":[82.5831747617793184,-77.1009570540098,98.1245147202868253],"rgb":[0.0666666666666666657,0.933333333333333348,0.133333333333333331],"xyz":[0.310930602524413957,0.613810668266137616,0.117224003621448067],"hpluv":[128.158354445562821,303.59085997924285,82.5831747617793184],"hsluv":[128.158354445562821,98.9085620232469864,82.5831747617793184]},"#11ee33":{"lch":[82.6492529720821381,122.265269823008623,128.905098358231896],"luv":[82.6492529720821381,-76.7865393115689301,95.1452762119380537],"rgb":[0.0666666666666666657,0.933333333333333348,0.2],"xyz":[0.314018353256871663,0.615045768559120742,0.133486157479059203],"hpluv":[128.905098358231896,298.749143147736106,82.6492529720821381],"hsluv":[128.905098358231896,98.916292078887,82.6492529720821381]},"#11ee44":{"lch":[82.7444986901015511,118.712635154498344,130.021230388522838],"luv":[82.7444986901015511,-76.3407023620842,90.9108734321077],"rgb":[0.0666666666666666657,0.933333333333333348,0.266666666666666663],"xyz":[0.318476348501090578,0.616828966656808308,0.156964932431945842],"hpluv":[130.021230388522838,291.911386756693616,82.7444986901015511],"hsluv":[130.021230388522838,98.9272612770947148,82.7444986901015511]},"#11ee55":{"lch":[82.8716000285422894,114.135934527262179,131.587310643629934],"luv":[82.8716000285422894,-75.758934185545,85.3674144008224],"rgb":[0.0666666666666666657,0.933333333333333348,0.333333333333333315],"xyz":[0.324438762540452563,0.619213932272553058,0.188366979705919757],"hpluv":[131.587310643629934,283.052591495130912,82.8716000285422894],"hsluv":[131.587310643629934,98.941589727101146,82.8716000285422894]},"#11ee66":{"lch":[83.0328193013522622,108.602333046050703,133.707640253052432],"luv":[83.0328193013522622,-75.0419109433949103,78.5059128028513697],"rgb":[0.0666666666666666657,0.933333333333333348,0.4],"xyz":[0.332023758313960748,0.622247930581956377,0.228314624113063719],"hpluv":[133.707640253052432,272.269449526145593,83.0328193013522622],"hsluv":[133.707640253052432,98.9592735060659,83.0328193013522622]},"#11ee77":{"lch":[83.2300736177455747,102.250357200027821,136.520544097163679],"luv":[83.2300736177455747,-74.1950209885278866,70.3579022430685228],"rgb":[0.0666666666666666657,0.933333333333333348,0.466666666666666674],"xyz":[0.341337771334162654,0.625973535790037228,0.277368426019461656],"hpluv":[136.520544097163679,259.803949175129901,83.2300736177455747],"hsluv":[136.520544097163679,98.9801962733070155,83.2300736177455747]},"#11ee88":{"lch":[83.4649827070576151,95.3003261118453651,140.209511574476238],"luv":[83.4649827070576151,-73.2277962837693082,60.990507527375577],"rgb":[0.0666666666666666657,0.933333333333333348,0.533333333333333326],"xyz":[0.352478188436106454,0.63042970263081477,0.336041289423033795],"hpluv":[140.209511574476238,246.084644167270028,83.4649827070576151],"hsluv":[140.209511574476238,99.0041428894333109,83.4649827070576151]},"#11ee99":{"lch":[83.7388997377875626,88.07037792773761,145.011549795441141],"luv":[83.7388997377875626,-72.1532115864445416,50.5005497603372433],"rgb":[0.0666666666666666657,0.933333333333333348,0.6],"xyz":[0.365535152545179209,0.635652488274444,0.404807967064151675],"hpluv":[145.011549795441141,231.793725377578141,83.7388997377875626],"hsluv":[145.011549795441141,99.0308160530368582,83.7388997377875626]},"#11eeaa":{"lch":[84.0529327571252907,80.9984265129003802,151.210882439188083],"luv":[84.0529327571252907,-70.9868724309634729,39.0078074241021824],"rgb":[0.0666666666666666657,0.933333333333333348,0.66666666666666663],"xyz":[0.38059284551043171,0.641675565460545,0.484111816681150331],"hpluv":[151.210882439188083,217.967504021816438,84.0529327571252907],"hsluv":[151.210882439188083,99.0598554997167895,84.0529327571252907]},"#11eebb":{"lch":[84.4079608499599914,74.6634505604909435,159.089705667287262],"luv":[84.4079608499599914,-69.7461428935043273,26.6478216947980826],"rgb":[0.0666666666666666657,0.933333333333333348,0.733333333333333282],"xyz":[0.397730437617768384,0.648530602303479808,0.574369801779792],"hpluv":[159.089705667287262,206.122134265545043,84.4079608499599914],"hsluv":[159.089705667287262,99.0908585861444209,84.4079608499599914]},"#11eecc":{"lch":[84.8046473826435,69.7804076798411,168.790807110150524],"luv":[84.8046473826435,-68.449275126866155,13.5647348138992196],"rgb":[0.0666666666666666657,0.933333333333333348,0.8],"xyz":[0.417022813061490139,0.656247552480968666,0.675976312450062178],"hpluv":[168.790807110150524,198.342538571842852,84.8046473826435],"hsluv":[168.790807110150524,99.123400814408285,84.8046473826435]},"#11eedd":{"lch":[85.2434517572140749,67.1146678094459,180.081412911690762],"luv":[85.2434517572140749,-67.114600056421537,-0.0953647673755886743],"rgb":[0.0666666666666666657,0.933333333333333348,0.866666666666666696],"xyz":[0.438541138612123627,0.664854882701222172,0.789306160350068176],"hpluv":[180.081412911690762,197.173954094180345,85.2434517572140749],"hsluv":[180.081412911690762,99.1570549081779546,85.2434517572140749]},"#11eeee":{"lch":[85.7246405502341275,67.2734484234975,192.17705063006116],"luv":[85.7246405502341275,-65.759826803247222,-14.1902093570146306],"rgb":[0.0666666666666666657,0.933333333333333348,0.933333333333333348],"xyz":[0.462353318878298392,0.674379754807692189,0.914716976418591399],"hpluv":[192.17705063006116,205.138082793863816,85.7246405502341275],"hsluv":[192.17705063006116,99.1914073274009098,85.7246405502341275]},"#11eeff":{"lch":[86.2482985645723517,70.4606934075819282,203.935071880927921],"luv":[86.2482985645723517,-64.401481656171029,-28.585983907627277],"rgb":[0.0666666666666666657,0.933333333333333348,1],"xyz":[0.488524367288129757,0.684848174171624913,1.05255116471037335],"hpluv":[203.935071880927921,224.026806300523,86.2482985645723517],"hsluv":[203.935071880927921,99.9999999999942304,86.2482985645723517]},"#11ff00":{"lch":[87.7931168603164,135.408535196841626,127.513270797457935],"luv":[87.7931168603164,-82.4563732780469,107.407718111808407],"rgb":[0.0666666666666666657,1,0],"xyz":[0.359895951315973628,0.716360603670241,0.119303136603937349],"hpluv":[127.513270797457935,491.310985978769054,87.7931168603164],"hsluv":[127.513270797457935,100.000000000002373,87.7931168603164]},"#11ff11":{"lch":[87.8126571401035108,134.634318462908169,127.715012949240432],"luv":[87.8126571401035108,-82.3604359259359313,106.50426424355777],"rgb":[0.0666666666666666657,1,0.0666666666666666657],"xyz":[0.360907616815610732,0.716765269870095922,0.124631241568692985],"hpluv":[127.715012949240432,489.364334505449051,87.8126571401035108],"hsluv":[127.715012949240432,99.999999999991914,87.8126571401035108]},"#11ff22":{"lch":[87.848860165327963,133.211117719966126,128.093229681784152],"luv":[87.848860165327963,-82.1836510367646,104.838205757586152],"rgb":[0.0666666666666666657,1,0.133333333333333331],"xyz":[0.362782974954087789,0.717515413125486723,0.134508127764672164],"hpluv":[128.093229681784152,485.7796458877379,87.848860165327963],"hsluv":[128.093229681784152,99.9999999999918572,87.848860165327963]},"#11ff33":{"lch":[87.9084130007832698,130.901693692038833,128.728166832562891],"luv":[87.9084130007832698,-81.8955348861488659,102.119414300885666],"rgb":[0.0666666666666666657,1,0.2],"xyz":[0.365870725686545495,0.71875051341846985,0.150770281622283314],"hpluv":[128.728166832562891,479.945632467831388,87.9084130007832698],"hsluv":[128.728166832562891,99.9999999999919567,87.9084130007832698]},"#11ff44":{"lch":[87.9942732352876,127.641489512823171,129.672386074694657],"luv":[87.9942732352876,-81.4859348177847806,98.2465891108891185],"rgb":[0.0666666666666666657,1,0.266666666666666663],"xyz":[0.37032872093076441,0.720533711516157416,0.174249056575169953],"hpluv":[129.672386074694657,471.674193406224788,87.9942732352876],"hsluv":[129.672386074694657,99.9999999999918856,87.9942732352876]},"#11ff55":{"lch":[88.1088871723243727,123.41738800901625,130.987994113812931],"luv":[88.1088871723243727,-80.9495722978130772,93.1612494966077662],"rgb":[0.0666666666666666657,1,0.333333333333333315],"xyz":[0.376291134970126395,0.722918677131902165,0.205651103849143868],"hpluv":[130.987994113812931,460.897243009671797,88.1088871723243727],"hsluv":[130.987994113812931,99.9999999999917,88.1088871723243727]},"#11ff66":{"lch":[88.2543278429396,118.268592142924746,132.753132104158254],"luv":[88.2543278429396,-80.2855559529031382,86.8429006471038],"rgb":[0.0666666666666666657,1,0.4],"xyz":[0.38387613074363458,0.725952675441305484,0.24559874825628783],"hpluv":[132.753132104158254,447.675525940365219,88.2543278429396],"hsluv":[132.753132104158254,99.9999999999916724,88.2543278429396]},"#11ff77":{"lch":[88.4323687925046613,112.290518027227137,135.069051083024959],"luv":[88.4323687925046613,-79.4970211443964558,79.3056370505300521],"rgb":[0.0666666666666666657,1,0.466666666666666674],"xyz":[0.393190143763836486,0.729678280649386335,0.294652550162685767],"hpluv":[135.069051083024959,432.222948715922314,88.4323687925046613],"hsluv":[135.069051083024959,99.999999999991644,88.4323687925046613]},"#11ff88":{"lch":[88.6445280109338825,105.641380676940045,138.068036362648229],"luv":[88.6445280109338825,-78.5907289650887577,70.5946076698930369],"rgb":[0.0666666666666666657,1,0.533333333333333326],"xyz":[0.404330560865780286,0.734134447490163877,0.353325413566257907],"hpluv":[138.068036362648229,414.95023459347243,88.6445280109338825],"hsluv":[138.068036362648229,99.9999999999915,88.6445280109338825]},"#11ff99":{"lch":[88.8920961876840465,98.552301258979881,141.921030988541872],"luv":[88.8920961876840465,-77.5765731609304225,60.7818342932124338],"rgb":[0.0666666666666666657,1,0.6],"xyz":[0.417387524974853,0.73935723313379309,0.422092091207375786],"hpluv":[141.921030988541872,396.537381185702543,88.8920961876840465],"hsluv":[141.921030988541872,99.9999999999913456,88.8920961876840465]},"#11ffaa":{"lch":[89.1761561490339147,91.3418654410923523,146.840553381528281],"luv":[89.1761561490339147,-76.4669946123218,49.9613362233014158],"rgb":[0.0666666666666666657,1,0.66666666666666663],"xyz":[0.432445217940105542,0.745380310319894157,0.501395940824374442],"hpluv":[146.840553381528281,378.048392077141443,89.1761561490339147],"hsluv":[146.840553381528281,99.9999999999913,89.1761561490339147]},"#11ffbb":{"lch":[89.4975971674113,84.4340589142561413,153.067388238784645],"luv":[89.4975971674113,-75.2763296710648859,38.2437510711127],"rgb":[0.0666666666666666657,1,0.733333333333333282],"xyz":[0.449582810047442216,0.752235347162828916,0.591653925923016133],"hpluv":[153.067388238784645,361.099415032935838,89.4975971674113],"hsluv":[153.067388238784645,99.9999999999909335,89.4975971674113]},"#11ffcc":{"lch":[89.8571262823018628,78.3714319324892159,160.817799258328876],"luv":[89.8571262823018628,-74.0201316086282901,25.7507564896672143],"rgb":[0.0666666666666666657,1,0.8],"xyz":[0.468875185491163915,0.759952297340317773,0.693260436593286289],"hpluv":[160.817799258328876,348.067615225706845,89.8571262823018628],"hsluv":[160.817799258328876,99.999999999991,89.8571262823018628]},"#11ffdd":{"lch":[90.2552779380141317,73.7997451229305,170.162013498752287],"luv":[90.2552779380141317,-72.7145076797264238,12.6096293801408788],"rgb":[0.0666666666666666657,1,0.866666666666666696],"xyz":[0.490393511041797514,0.768559627560571279,0.806590284493292287],"hpluv":[170.162013498752287,342.256686666565315,90.2552779380141317],"hsluv":[170.162013498752287,99.999999999990834,90.2552779380141317]},"#11ffee":{"lch":[90.6924227584195819,71.3832589696730793,180.844217403257659],"luv":[90.6924227584195819,-71.3755103944163665,-1.05174952720347981],"rgb":[0.0666666666666666657,1,0.933333333333333348],"xyz":[0.514205691307972224,0.778084499667041296,0.93200110056181551],"hpluv":[180.844217403257659,347.82122947809512,90.6924227584195819],"hsluv":[180.844217403257659,99.9999999999901803,90.6924227584195819]},"#11ffff":{"lch":[91.1687759776689859,71.6302608322469467,192.17705063006116],"luv":[91.1687759776689859,-70.0186129384549361,-15.1092061032524665],"rgb":[0.0666666666666666657,1,1],"xyz":[0.540376739717803645,0.788552919030974,1.06983528885359735],"hpluv":[192.17705063006116,369.258709956275879,91.1687759776689859],"hsluv":[192.17705063006116,99.9999999999898108,91.1687759776689859]},"#00aa00":{"lch":[60.5587499434736287,93.727653253516209,127.71501294924046],"luv":[60.5587499434736287,-57.3364240886418415,74.1445038903004559],"rgb":[0,0.66666666666666663,0],"xyz":[0.143740958848290495,0.287481917696585,0.0479136529494288144],"hpluv":[127.71501294924046,196.394882900214554,60.5587499434736287],"hsluv":[127.71501294924046,100.000000000002359,60.5587499434736287]},"#00aa11":{"lch":[60.5946550577951939,92.4075267438518182,128.220974416403209],"luv":[60.5946550577951939,-57.1721703645967665,72.5981675713458543],"rgb":[0,0.66666666666666663,0.0666666666666666657],"xyz":[0.144752624347927628,0.28788658389643984,0.0532417579141844441],"hpluv":[128.220974416403209,193.513984665985475,60.5946550577951939],"hsluv":[128.220974416403209,99.9999999999907772,60.5946550577951939]},"#00aa22":{"lch":[60.661124672570665,90.0113827545795715,129.185497299711983],"luv":[60.661124672570665,-56.8721735728637725,69.7682226983709199],"rgb":[0,0.66666666666666663,0.133333333333333331],"xyz":[0.146627982486404629,0.288636727151830641,0.0631186441101636436],"hpluv":[129.185497299711983,188.289586599726533,60.661124672570665],"hsluv":[129.185497299711983,99.9999999999908624,60.661124672570665]},"#00aa33":{"lch":[60.7703154938824355,86.2098857925288513,130.852037745481823],"luv":[60.7703154938824355,-56.3905639077229353,65.2092685937350751],"rgb":[0,0.66666666666666663,0.2],"xyz":[0.14971573321886239,0.289871827444813768,0.0793807979677747799],"hpluv":[130.852037745481823,180.013429236819462,60.7703154938824355],"hsluv":[130.852037745481823,99.9999999999908,60.7703154938824355]},"#00aa44":{"lch":[60.9274158721733841,81.0355822964375108,133.441426631804489],"luv":[60.9274158721733841,-55.7210928860807044,58.8381288426430444],"rgb":[0,0.66666666666666663,0.266666666666666663],"xyz":[0.15417372846308125,0.291655025542501334,0.102859572920661418],"hpluv":[133.441426631804489,168.77274926729055,60.9274158721733841],"hsluv":[133.441426631804489,99.9999999999908908,60.9274158721733841]},"#00aa55":{"lch":[61.1365343944832915,74.6960845180523592,137.272019015051796],"luv":[61.1365343944832915,-54.8704978500827636,50.6826746335676717],"rgb":[0,0.66666666666666663,0.333333333333333315],"xyz":[0.160136142502443235,0.294039991158246194,0.134261620194635334],"hpluv":[137.272019015051796,155.037353806827582,61.1365343944832915],"hsluv":[137.272019015051796,99.9999999999910614,61.1365343944832915]},"#00aa66":{"lch":[61.4009335299549264,67.6053275851037512,142.80970662058607],"luv":[61.4009335299549264,-53.8565898531593703,40.8649978254956139],"rgb":[0,0.66666666666666663,0.4],"xyz":[0.16772113827595142,0.297073989467649513,0.174209264601779296],"hpluv":[142.80970662058607,139.715720243970395,61.4009335299549264],"hsluv":[142.80970662058607,99.9999999999911893,61.4009335299549264]},"#00aa77":{"lch":[61.7231520087844814,60.4394033477847188,150.696962972825474],"luv":[61.7231520087844814,-52.7057786154446859,29.5807771631501382],"rgb":[0,0.66666666666666663,0.466666666666666674],"xyz":[0.177035151296153326,0.300799594675730309,0.223263066508177205],"hpluv":[150.696962972825474,124.254291935777843,61.7231520087844814],"hsluv":[150.696962972825474,99.9999999999911893,61.7231520087844814]},"#00aa88":{"lch":[62.1050795642419615,54.2095218153359397,161.640221068188367],"luv":[62.1050795642419615,-51.4501140589855126,17.0750700954568337],"rgb":[0,0.66666666666666663,0.533333333333333326],"xyz":[0.188175568398097182,0.305255761516507906,0.281935929911749372],"hpluv":[161.640221068188367,110.761232665855573,62.1050795642419615],"hsluv":[161.640221068188367,99.9999999999911466,62.1050795642419615]},"#00aa99":{"lch":[62.5480102999456307,50.2545412813378576,175.872445658321794],"luv":[62.5480102999456307,-50.1241952468173793,3.61717711159146882],"rgb":[0,0.66666666666666663,0.6],"xyz":[0.201232532507169881,0.310478547160137064,0.350702607552867307],"hpluv":[175.872445658321794,101.95326553071466,62.5480102999456307],"hsluv":[175.872445658321794,99.9999999999913314,62.5480102999456307]},"#00aaaa":{"lch":[63.0526871437625829,49.8847230087107931,192.17705063006116],"luv":[63.0526871437625829,-48.762339705407328,-10.5223484123201398],"rgb":[0,0.66666666666666663,0.66666666666666663],"xyz":[0.216290225472422437,0.316501624346238186,0.430006457169865852],"hpluv":[192.17705063006116,100.392967527320806,63.0526871437625829],"hsluv":[192.17705063006116,99.9999999999914451,63.0526871437625829]},"#00aabb":{"lch":[63.6193436646561565,53.6276681768737,207.895374658889665],"luv":[63.6193436646561565,-47.3963155750249143,-25.0901587081772526],"rgb":[0,0.66666666666666663,0.733333333333333282],"xyz":[0.233427817579759056,0.323356661189172945,0.520264442268507654],"hpluv":[207.895374658889665,106.964349821245364,63.6193436646561565],"hsluv":[207.895374658889665,99.9999999999916,63.6193436646561565]},"#00aacc":{"lch":[64.2477463386430259,60.9097449106327886,220.878520684721707],"luv":[64.2477463386430259,-46.0537892020538,-39.8628338833449],"rgb":[0,0.66666666666666663,0.8],"xyz":[0.25272019302348081,0.331073611366661746,0.62187095293877781],"hpluv":[220.878520684721707,120.300715116377788,64.2477463386430259],"hsluv":[220.878520684721707,99.9999999999916298,64.2477463386430259]},"#00aadd":{"lch":[64.9372385342214926,70.6418801813473465,230.685034316882962],"luv":[64.9372385342214926,-44.7574928469198525,-54.6538385624811625],"rgb":[0,0.66666666666666663,0.866666666666666696],"xyz":[0.274238518574114354,0.339680941586915253,0.735200800838783808],"hpluv":[230.685034316882962,138.04089297290011,64.9372385342214926],"hsluv":[230.685034316882962,99.9999999999918145,64.9372385342214926]},"#00aaee":{"lch":[65.6867863979168618,81.8478503674051,237.87423205753521],"luv":[65.6867863979168618,-43.5250094774703129,-69.3155405356638283],"rgb":[0,0.66666666666666663,0.933333333333333348],"xyz":[0.298050698840289119,0.34920581369338527,0.860611616907307],"hpluv":[237.87423205753521,158.11336767521891,65.6867863979168618],"hsluv":[237.87423205753521,99.999999999991843,65.6867863979168618]},"#00aaff":{"lch":[66.4950261675888,93.8462134827344,243.161780722675303],"luv":[66.4950261675888,-42.369016683119284,-83.7375555551541],"rgb":[0,0.66666666666666663,1],"xyz":[0.324221747250120484,0.359674233057318,0.998445805199088876],"hpluv":[243.161780722675303,179.088178632175044,66.4950261675888],"hsluv":[243.161780722675303,99.9999999999982805,66.4950261675888]},"#00bb00":{"lch":[66.1662429166961772,102.406451239047826,127.71501294924046],"luv":[66.1662429166961772,-62.6455428450044352,81.0099822060849135],"rgb":[0,0.733333333333333282,0],"xyz":[0.177695456756889275,0.355390913513783546,0.0592318189189614333],"hpluv":[127.71501294924046,196.39488290021464,66.1662429166961772],"hsluv":[127.71501294924046,100.000000000002373,66.1662429166961772]},"#00bb11":{"lch":[66.1974173108447559,101.237205455569821,128.123527834983577],"luv":[66.1974173108447559,-62.4996967519340956,79.6414444518024425],"rgb":[0,0.733333333333333282,0.0666666666666666657],"xyz":[0.178707122256526407,0.355795579713638399,0.064559923883717063],"hpluv":[128.123527834983577,194.061073356438868,66.1974173108447559],"hsluv":[128.123527834983577,99.9999999999909335,66.1974173108447559]},"#00bb22":{"lch":[66.2551438620851911,99.1062916374383747,128.898124119483072],"luv":[66.2551438620851911,-62.232564676438038,77.1308299962987718],"rgb":[0,0.733333333333333282,0.133333333333333331],"xyz":[0.180582480395003409,0.3565457229690292,0.0744368100796962556],"hpluv":[128.898124119483072,189.810813804630897,66.2551438620851911],"hsluv":[128.898124119483072,99.9999999999908624,66.2551438620851911]},"#00bb33":{"lch":[66.3500136661217255,95.7008075372637137,130.224174268563928],"luv":[66.3500136661217255,-61.8016571104716235,73.0698278476423155],"rgb":[0,0.733333333333333282,0.2],"xyz":[0.18367023112746117,0.357780823262012326,0.0906989639373074],"hpluv":[130.224174268563928,183.02647365261987,66.3500136661217255],"hsluv":[130.224174268563928,99.9999999999909335,66.3500136661217255]},"#00bb44":{"lch":[66.4865992404304,91.0092453899789859,132.255785626190885],"luv":[66.4865992404304,-61.1983980271068,67.3605138443080875],"rgb":[0,0.733333333333333282,0.266666666666666663],"xyz":[0.18812822637168003,0.359564021359699892,0.114177738890194044],"hpluv":[132.255785626190885,173.696361176634838,66.4865992404304],"hsluv":[132.255785626190885,99.9999999999909619,66.4865992404304]},"#00bb55":{"lch":[66.6685736373934219,85.1496193371524,135.204737263674588],"luv":[66.6685736373934219,-60.4246386982598,59.9943389949978751],"rgb":[0,0.733333333333333282,0.333333333333333315],"xyz":[0.194090640411042015,0.361948986975444753,0.145579786164167946],"hpluv":[135.204737263674588,162.069343805127659,66.6685736373934219],"hsluv":[135.204737263674588,99.9999999999909477,66.6685736373934219]},"#00bb66":{"lch":[66.8989180170192412,78.3861452968700689,139.371990675590268],"luv":[66.8989180170192412,-59.4914065933894,51.0407711152765629],"rgb":[0,0.733333333333333282,0.4],"xyz":[0.2016756361845502,0.364982985284848072,0.185527430571311908],"hpluv":[139.371990675590268,148.682392510907704,66.8989180170192412],"hsluv":[139.371990675590268,99.9999999999911182,66.8989180170192412]},"#00bb77":{"lch":[67.1800303821267448,71.1598447269708316,145.178146497089472],"luv":[67.1800303821267448,-58.4173559625633061,40.6341731047866617],"rgb":[0,0.733333333333333282,0.466666666666666674],"xyz":[0.210989649204752105,0.368708590492928867,0.234581232477709817],"hpluv":[145.178146497089472,134.410786503463328,67.1800303821267448],"hsluv":[145.178146497089472,99.9999999999910898,67.1800303821267448]},"#00bb88":{"lch":[67.5137905946342,64.1363411600919,153.159702568813543],"luv":[67.5137905946342,-57.2268359754185525,28.9578918715820954],"rgb":[0,0.733333333333333282,0.533333333333333326],"xyz":[0.222130066306695961,0.373164757333706465,0.293254095881282],"hpluv":[153.159702568813543,120.545503395456095,67.5137905946342],"hsluv":[153.159702568813543,99.9999999999911608,67.5137905946342]},"#00bb99":{"lch":[67.9016044714860811,58.2533417764790187,163.826150797364875],"luv":[67.9016044714860811,-55.9477282230567141,16.2266304205851419],"rgb":[0,0.733333333333333282,0.6],"xyz":[0.235187030415768661,0.378387542977335622,0.362020773522399919],"hpluv":[163.826150797364875,108.862958898475256,67.9016044714860811],"hsluv":[163.826150797364875,99.9999999999912461,67.9016044714860811]},"#00bbaa":{"lch":[68.3444379186728384,54.6744668749029543,177.202021912208522],"luv":[68.3444379186728384,-54.6092872876890922,2.66890801368250408],"rgb":[0,0.733333333333333282,0.66666666666666663],"xyz":[0.250244723381021217,0.384410620163436745,0.44132462313939852],"hpluv":[177.202021912208522,101.512776720033713,68.3444379186728384],"hsluv":[177.202021912208522,99.9999999999913598,68.3444379186728384]},"#00bbbb":{"lch":[68.8428468315880338,54.4656619866929645,192.177050630061132],"luv":[68.8428468315880338,-53.2402096652165113,-11.4886209116881091],"rgb":[0,0.733333333333333282,0.733333333333333282],"xyz":[0.267382315488357836,0.391265657006371503,0.531582608238040266],"hpluv":[192.177050630061132,100.392967527320806,68.8428468315880338],"hsluv":[192.177050630061132,99.9999999999914451,68.8428468315880338]},"#00bbcc":{"lch":[69.3970058395379397,58.0340346662075675,206.653495587531239],"luv":[69.3970058395379397,-51.8670935918889384,-26.0337047299998297],"rgb":[0,0.733333333333333282,0.8],"xyz":[0.28667469093207959,0.398982607183860305,0.633189118908310422],"hpluv":[206.653495587531239,106.116119046155191,69.3970058395379397],"hsluv":[206.653495587531239,99.9999999999915161,69.3970058395379397]},"#00bbdd":{"lch":[70.0067374807312461,64.9183055759271923,218.91244904401708],"luv":[70.0067374807312461,-50.5133677649133119,-40.7772740125682844],"rgb":[0,0.733333333333333282,0.866666666666666696],"xyz":[0.308193016482713134,0.407589937404113811,0.74651896680831642],"hpluv":[218.91244904401708,117.670246608059514,70.0067374807312461],"hsluv":[218.91244904401708,99.999999999991644,70.0067374807312461]},"#00bbee":{"lch":[70.6715424904064236,74.2108860535778,228.474155043258463],"luv":[70.6715424904064236,-49.1986871961444336,-55.5584807840624819],"rgb":[0,0.733333333333333282,0.933333333333333348],"xyz":[0.332005196748887843,0.417114809510583828,0.871929782876839643],"hpluv":[228.474155043258463,133.248513578578667,70.6715424904064236],"hsluv":[228.474155043258463,99.9999999999918288,70.6715424904064236]},"#00bbff":{"lch":[71.3906313155650167,85.0452269855302,235.688960914523477],"luv":[71.3906313155650167,-47.9387359102869155,-70.246481992653],"rgb":[0,0.733333333333333282,1],"xyz":[0.358176245158719264,0.427583228874516552,1.00976397116862149],"hpluv":[235.688960914523477,151.163886263776277,71.3906313155650167],"hsluv":[235.688960914523477,99.9999999999978,71.3906313155650167]},"#00cc00":{"lch":[71.6795694698327139,110.939506494120423,127.71501294924046],"luv":[71.6795694698327139,-67.8655057683618566,87.7601688009055181],"rgb":[0,0.8,0],"xyz":[0.215919200066506195,0.431838400133018441,0.0719730666888333814],"hpluv":[127.71501294924046,196.394882900214611,71.6795694698327139],"hsluv":[127.71501294924046,100.000000000002359,71.6795694698327139]},"#00cc11":{"lch":[71.7069484470386698,109.895339051400697,128.05073784188761],"luv":[71.7069484470386698,-67.7349868616780668,86.5387606802328548],"rgb":[0,0.8,0.0666666666666666657],"xyz":[0.216930865566143327,0.432243066332873294,0.0773011716535890181],"hpluv":[128.05073784188761,194.472124503698296,71.7069484470386698],"hsluv":[128.05073784188761,99.9999999999908766,71.7069484470386698]},"#00cc22":{"lch":[71.7576566073484,107.986601617430239,128.68476606632143],"luv":[71.7576566073484,-67.4954197535952289,84.2939763041676287],"rgb":[0,0.8,0.133333333333333331],"xyz":[0.218806223704620328,0.432993209588264094,0.0871780578495682107],"hpluv":[128.68476606632143,190.959361108477,71.7576566073484],"hsluv":[128.68476606632143,99.9999999999909193,71.7576566073484]},"#00cc33":{"lch":[71.8410194320707,104.91966800737103,129.762682813168567],"luv":[71.8410194320707,-67.1075822658835364,80.6517770244687853],"rgb":[0,0.8,0.2],"xyz":[0.22189397443707809,0.434228309881247221,0.103440211707179347],"hpluv":[129.762682813168567,185.320621425294917,71.8410194320707],"hsluv":[129.762682813168567,99.9999999999909761,71.8410194320707]},"#00cc44":{"lch":[71.9610975929873717,100.65733905537941,131.396818004218431],"luv":[71.9610975929873717,-66.561699323308261,75.5078809721416491],"rgb":[0,0.8,0.266666666666666663],"xyz":[0.226351969681296949,0.436011507978934787,0.126918986660065986],"hpluv":[131.396818004218431,177.495355343216744,71.9610975929873717],"hsluv":[131.396818004218431,99.9999999999909903,71.9610975929873717]},"#00cc55":{"lch":[72.1211872877728837,95.2615727691762828,133.734892870047815],"luv":[72.1211872877728837,-65.8564749898269639,68.8308938513177],"rgb":[0,0.8,0.333333333333333315],"xyz":[0.232314383720658935,0.438396473594679648,0.158321033934039901],"hpluv":[133.734892870047815,167.607792551030144,72.1211872877728837],"hsluv":[133.734892870047815,99.999999999991033,72.1211872877728837]},"#00cc66":{"lch":[72.3240060759138,88.9026050634798821,136.980115521422647],"luv":[72.3240060759138,-64.9982032665013207,60.6539921126355495],"rgb":[0,0.8,0.4],"xyz":[0.23989937949416712,0.441430471904082966,0.198268678341183863],"hpluv":[136.980115521422647,155.980870440536961,72.3240060759138],"hsluv":[136.980115521422647,99.9999999999910614,72.3240060759138]},"#00cc77":{"lch":[72.5717906268391459,81.8763194870781206,141.413175407098493],"luv":[72.5717906268391459,-63.9997644777707464,51.066249515114805],"rgb":[0,0.8,0.466666666666666674],"xyz":[0.249213392514369025,0.445156077112163762,0.247322480247581772],"hpluv":[141.413175407098493,143.162673336571032,72.5717906268391459],"hsluv":[141.413175407098493,99.9999999999910756,72.5717906268391459]},"#00cc88":{"lch":[72.8663546950801,74.6325704710961162,147.40707881161012],"luv":[72.8663546950801,-62.8793552775828672,40.2020802322297683],"rgb":[0,0.8,0.533333333333333326],"xyz":[0.260353809616312881,0.449612243952941359,0.305995343651153939],"hpluv":[147.40707881161012,129.969270924532168,72.8663546950801],"hsluv":[147.40707881161012,99.9999999999911608,72.8663546950801]},"#00cc99":{"lch":[73.2091273059676695,67.813783770663278,155.40051707617576],"luv":[73.2091273059676695,-61.6589956558222809,28.2290191825639418],"rgb":[0,0.8,0.6],"xyz":[0.273410773725385581,0.454835029596570517,0.374762021292271874],"hpluv":[155.40051707617576,117.541728748843539,73.2091273059676695],"hsluv":[155.40051707617576,99.9999999999911893,73.2091273059676695]},"#00ccaa":{"lch":[73.6011808048110368,62.2803364521242102,165.745935171574274],"luv":[73.6011808048110368,-60.3629393869259374,15.3347923742089414],"rgb":[0,0.8,0.66666666666666663],"xyz":[0.288468466690638137,0.460858106782671639,0.454065870909270419],"hpluv":[165.745935171574274,107.375573062224,73.6011808048110368],"hsluv":[165.745935171574274,99.9999999999912887,73.6011808048110368]},"#00ccbb":{"lch":[74.043253901593,59.041045922693165,178.335616576813749],"luv":[74.043253901593,-59.0161369965186395,1.7148404163965667],"rgb":[0,0.8,0.733333333333333282],"xyz":[0.305606058797974756,0.467713143625606398,0.544323856007912221],"hpluv":[178.335616576813749,101.183074845522739,74.043253901593],"hsluv":[178.335616576813749,99.9999999999913882,74.043253901593]},"#00cccc":{"lch":[74.5357725840108714,58.9696734274942429,192.177050630061132],"luv":[74.5357725840108714,-57.64288292201784,-12.4386668330598962],"rgb":[0,0.8,0.8],"xyz":[0.32489843424169651,0.4754300938030952,0.645930366678182377],"hpluv":[192.177050630061132,100.392967527320835,74.5357725840108714],"hsluv":[192.177050630061132,99.9999999999914877,74.5357725840108714]},"#00ccdd":{"lch":[75.0788705190671,62.3850861111967063,205.58971515357635],"luv":[75.0788705190671,-56.2657375800620656,-26.9456071312750254],"rgb":[0,0.8,0.866666666666666696],"xyz":[0.346416759792330053,0.484037424023348706,0.759260214578188375],"hpluv":[205.58971515357635,105.439266222061761,75.0788705190671],"hsluv":[205.58971515357635,99.9999999999915588,75.0788705190671]},"#00ccee":{"lch":[75.672409810316779,68.9113069897593,217.179575991302841],"luv":[75.672409810316779,-54.9047659259632326,-41.6441461630807765],"rgb":[0,0.8,0.933333333333333348],"xyz":[0.370228940058504818,0.493562296129818723,0.884671030646711598],"hpluv":[217.179575991302841,115.555933163518176,75.672409810316779],"hsluv":[217.179575991302841,99.9999999999916,75.672409810316779]},"#00ccff":{"lch":[76.3160024985922263,77.7871508482342193,226.46755023570978],"luv":[76.3160024985922263,-53.5770891110031471,-56.3944710009551713],"rgb":[0,0.8,1],"xyz":[0.396399988468336184,0.504030715493751447,1.02250521893849333],"hpluv":[226.46755023570978,131.600547876461974,76.3160024985922263],"hsluv":[226.46755023570978,99.9999999999969731,76.3160024985922263]},"#00dd00":{"lch":[77.1074905447145369,119.34037845513086,127.715012949240503],"luv":[77.1074905447145369,-73.004607631587163,94.4057900468603],"rgb":[0,0.866666666666666696,0],"xyz":[0.258553190613681372,0.51710638122737,0.0861843968712247],"hpluv":[127.715012949240503,210.385995725156505,77.1074905447145369],"hsluv":[127.715012949240503,100.000000000002203,77.1074905447145369]},"#00dd11":{"lch":[77.1317715771024268,118.40111864948102,127.995077421524911],"luv":[77.1317715771024268,-72.8869911141770359,93.3076171797906255],"rgb":[0,0.866666666666666696,0.0666666666666666657],"xyz":[0.259564856113318476,0.517511047427224868,0.0915125018359803366],"hpluv":[127.995077421524911,208.997725019578468,77.1317715771024268],"hsluv":[127.995077421524911,99.9999999999909193,77.1317715771024268]},"#00dd22":{"lch":[77.1767486793617081,116.680170458435171,128.522366120948305],"luv":[77.1767486793617081,-72.6707542705971434,91.2864921658838568],"rgb":[0,0.866666666666666696,0.133333333333333331],"xyz":[0.261440214251795533,0.518261190682615669,0.101389388031959529],"hpluv":[128.522366120948305,206.449864525990506,77.1767486793617081],"hsluv":[128.522366120948305,99.9999999999909335,77.1767486793617081]},"#00dd33":{"lch":[77.2507083817471312,113.903613467858165,129.414072915332611],"luv":[77.2507083817471312,-72.3197155087496668,87.9993858488157485],"rgb":[0,0.866666666666666696,0.2],"xyz":[0.264527964984253239,0.519496290975598796,0.117651541889570666],"hpluv":[129.414072915332611,202.327676795977681,77.2507083817471312],"hsluv":[129.414072915332611,99.9999999999909335,77.2507083817471312]},"#00dd44":{"lch":[77.3572825066044,110.019432359123073,130.755032484191332],"luv":[77.3572825066044,-71.8235777962907633,83.3405613681826907],"rgb":[0,0.866666666666666696,0.266666666666666663],"xyz":[0.268985960228472154,0.521279489073286362,0.141130316842457304],"hpluv":[130.755032484191332,196.537344059934071,77.3572825066044],"hsluv":[130.755032484191332,99.9999999999909193,77.3572825066044]},"#00dd55":{"lch":[77.499442461574418,105.05363654061,132.652443872197409],"luv":[77.499442461574418,-71.1790335676869717,77.2645567564888],"rgb":[0,0.866666666666666696,0.333333333333333315],"xyz":[0.274948374267834139,0.523664454689031111,0.172532364116431219],"hpluv":[132.652443872197409,189.094972829508237,77.499442461574418],"hsluv":[132.652443872197409,99.9999999999909477,77.499442461574418]},"#00dd66":{"lch":[77.6796666807438,99.1151742217995,135.249123061333165],"luv":[77.6796666807438,-70.3890792332532413,69.7796194150732276],"rgb":[0,0.866666666666666696,0.4],"xyz":[0.282533370041342324,0.52669845299843443,0.212480008523575181],"hpluv":[135.249123061333165,180.139247328423863,77.6796666807438],"hsluv":[135.249123061333165,99.9999999999909903,77.6796666807438]},"#00dd77":{"lch":[77.9000291762011301,92.4061998396230138,138.73841210181584],"luv":[77.9000291762011301,-69.4623356452591878,60.9416909472136155],"rgb":[0,0.866666666666666696,0.466666666666666674],"xyz":[0.29184738306154423,0.530424058206515281,0.26153381042997309],"hpluv":[138.73841210181584,169.957910917592017,77.9000291762011301],"hsluv":[138.73841210181584,99.9999999999910187,77.9000291762011301]},"#00dd88":{"lch":[78.1622519856154,85.2389230174627386,143.3784757437721],"luv":[78.1622519856154,-68.4122000424903,50.8472701580255091],"rgb":[0,0.866666666666666696,0.533333333333333326],"xyz":[0.30298780016348803,0.534880225047292823,0.320206673833545286],"hpluv":[143.3784757437721,159.033158409305884,78.1622519856154],"hsluv":[143.3784757437721,99.9999999999911608,78.1622519856154]},"#00dd99":{"lch":[78.4677391993035798,78.0607504013048583,149.494791226300919],"luv":[78.4677391993035798,-67.2558167964087801,39.6249398770864545],"rgb":[0,0.866666666666666696,0.6],"xyz":[0.316044764272560785,0.540103010690922,0.388973351474663165],"hpluv":[149.494791226300919,148.113090063328627,78.4677391993035798],"hsluv":[149.494791226300919,99.9999999999911466,78.4677391993035798]},"#00ddaa":{"lch":[78.8176011215583401,71.4835041270533225,157.438879868811341],"luv":[78.8176011215583401,-66.0129273361177,27.4259874352573583],"rgb":[0,0.866666666666666696,0.66666666666666663],"xyz":[0.331102457237813286,0.546126087877023103,0.468277201091661766],"hpluv":[157.438879868811341,138.307036304413771,78.8176011215583401],"hsluv":[157.438879868811341,99.9999999999912319,78.8176011215583401]},"#00ddbb":{"lch":[79.21267314937,66.2909050184163675,167.440816272526462],"luv":[79.21267314937,-64.7046905962200896,14.4148223370296193],"rgb":[0,0.866666666666666696,0.733333333333333282],"xyz":[0.34824004934514996,0.552981124719957862,0.558535186190303512],"hpluv":[167.440816272526462,131.160951364069831,79.21267314937],"hsluv":[167.440816272526462,99.9999999999912319,79.21267314937]},"#00ddcc":{"lch":[79.6535319864315738,63.3571261830985,179.312753048293331],"luv":[79.6535319864315738,-63.3525685364998381,0.759932897798095253],"rgb":[0,0.866666666666666696,0.8],"xyz":[0.367532424788871714,0.560698074897446719,0.660141696860573668],"hpluv":[179.312753048293331,128.577362979680402,79.6535319864315738],"hsluv":[179.312753048293331,99.9999999999913314,79.6535319864315738]},"#00dddd":{"lch":[80.1405107346531338,63.4039144225475795,192.177050630061245],"luv":[80.1405107346531338,-61.9773555359817649,-13.3739958452306631],"rgb":[0,0.866666666666666696,0.866666666666666696],"xyz":[0.389050750339505202,0.569305405117700225,0.773471544760579666],"hpluv":[192.177050630061245,132.399857962191078,80.1405107346531338],"hsluv":[192.177050630061245,99.9999999999915,80.1405107346531338]},"#00ddee":{"lch":[80.6737137665329,66.6843941199945078,204.668960845135786],"luv":[80.6737137665329,-60.59840405426975,-27.8323884211582282],"rgb":[0,0.866666666666666696,0.933333333333333348],"xyz":[0.412862930605679967,0.578830277224170242,0.898882360829102889],"hpluv":[204.668960845135786,143.769811077134563,80.6737137665329],"hsluv":[204.668960845135786,99.9999999999914735,80.6737137665329]},"#00ddff":{"lch":[81.2530318771427,72.8883394631876627,215.643856178856652],"luv":[81.2530318771427,-59.2330695533496296,-42.475328144570291],"rgb":[0,0.866666666666666696,1],"xyz":[0.439033979015511333,0.589298696588103,1.03671654912088473],"hpluv":[215.643856178856652,162.831862460855405,81.2530318771427],"hsluv":[215.643856178856652,99.9999999999960636,81.2530318771427]},"#00ee00":{"lch":[82.4573791946470749,127.620478503329409,127.715012949240503],"luv":[82.4573791946470749,-78.0698291684561241,100.955873068518613],"rgb":[0,0.933333333333333348,0],"xyz":[0.305731966954196188,0.611463933908400925,0.101910655651395884],"hpluv":[127.715012949240503,307.908475174189959,82.4573791946470749],"hsluv":[127.715012949240503,100.000000000002217,82.4573791946470749]},"#00ee11":{"lch":[82.4790940690076582,126.770138643430457,127.951660682688043],"luv":[82.4790940690076582,-77.963182339567652,99.9620440525398],"rgb":[0,0.933333333333333348,0.0666666666666666657],"xyz":[0.306743632453833293,0.611868600108255833,0.107238760616151521],"hpluv":[127.951660682688043,306.293921948395678,82.4790940690076582],"hsluv":[127.951660682688043,99.9999999999909193,82.4790940690076582]},"#00ee22":{"lch":[82.5193223464761729,125.209295581045268,128.396138884075839],"luv":[82.5193223464761729,-77.7668632323815814,98.1309466116455],"rgb":[0,0.933333333333333348,0.133333333333333331],"xyz":[0.308618990592310349,0.612618743363646634,0.117115646812130714],"hpluv":[128.396138884075839,303.325246698320768,82.5193223464761729],"hsluv":[128.396138884075839,99.9999999999907914,82.5193223464761729]},"#00ee33":{"lch":[82.5854861516441616,122.683025615083068,129.144698003447559],"luv":[82.5854861516441616,-77.4474668310457304,95.1473313105795881],"rgb":[0,0.933333333333333348,0.2],"xyz":[0.311706741324768055,0.613853843656629761,0.133377800669741864],"hpluv":[129.144698003447559,298.506449004286878,82.5854861516441616],"hsluv":[129.144698003447559,99.9999999999910187,82.5854861516441616]},"#00ee44":{"lch":[82.680854944152216,119.131104912681948,130.263308305441626],"luv":[82.680854944152216,-76.994580956063885,90.9068460629701889],"rgb":[0,0.933333333333333348,0.266666666666666663],"xyz":[0.31616473656898697,0.615637041754317327,0.156856575622628502],"hpluv":[130.263308305441626,291.702339981024693,82.680854944152216],"hsluv":[130.263308305441626,99.9999999999908624,82.680854944152216]},"#00ee55":{"lch":[82.8081199656530913,114.556122924925475,131.832385242542614],"luv":[82.8081199656530913,-76.4036333062175572,85.3556683366704192],"rgb":[0,0.933333333333333348,0.333333333333333315],"xyz":[0.322127150608348956,0.618022007370062076,0.18825862289660239],"hpluv":[131.832385242542614,282.889526663711365,82.8081199656530913],"hsluv":[131.832385242542614,99.9999999999908908,82.8081199656530913]},"#00ee66":{"lch":[82.9695459516691756,109.025909834785097,133.955863991345211],"luv":[82.9695459516691756,-75.6753248662734137,78.4849936082476347],"rgb":[0,0.933333333333333348,0.4],"xyz":[0.329712146381857141,0.621056005679465395,0.22820626730374638],"hpluv":[133.955863991345211,272.166364406401044,82.9695459516691756],"hsluv":[133.955863991345211,99.9999999999909193,82.9695459516691756]},"#00ee77":{"lch":[83.167051813506589,102.679799146220446,136.771308753659213],"luv":[83.167051813506589,-74.8151450958190338,70.3266323450776127],"rgb":[0,0.933333333333333348,0.466666666666666674],"xyz":[0.339026159402059046,0.624781610887546246,0.277260069210144289],"hpluv":[136.771308753659213,259.776444306911685,83.167051813506589],"hsluv":[136.771308753659213,99.9999999999910898,83.167051813506589]},"#00ee88":{"lch":[83.4022585136551839,95.7389522528198427,140.46074817536558],"luv":[83.4022585136551839,-73.8327925713246742,60.9480575538504],"rgb":[0,0.933333333333333348,0.533333333333333326],"xyz":[0.350166576504002847,0.629237777728323788,0.335932932613716428],"hpluv":[140.46074817536558,246.149488794882956,83.4022585136551839],"hsluv":[140.46074817536558,99.999999999991033,83.4022585136551839]},"#00ee99":{"lch":[83.6765199188301096,88.5221307628359142,145.258543938418427],"luv":[83.6765199188301096,-72.7414610621927,50.4464813176313],"rgb":[0,0.933333333333333348,0.6],"xyz":[0.363223540613075602,0.634460563371953,0.404699610254834363],"hpluv":[145.258543938418427,231.96752956627526,83.6765199188301096],"hsluv":[145.258543938418427,99.9999999999911,83.6765199188301096]},"#00eeaa":{"lch":[83.9909442670452364,81.4671227341597159,151.444498676017645],"luv":[83.9909442670452364,-71.557012704351564,38.9420854527837363],"rgb":[0,0.933333333333333348,0.66666666666666663],"xyz":[0.378281233578328102,0.640483640558054068,0.484003459871832964],"hpluv":[151.444498676017645,218.263507316576721,83.9909442670452364],"hsluv":[151.444498676017645,99.9999999999911893,83.9909442670452364]},"#00eebb":{"lch":[84.3464103530465366,75.1511025294100392,159.294479220170871],"luv":[84.3464103530465366,-70.2970903476626319,26.5707978811035801],"rgb":[0,0.933333333333333348,0.733333333333333282],"xyz":[0.395418825685664777,0.647338677400988827,0.57426144497047471],"hpluv":[159.294479220170871,206.543608310772072,84.3464103530465366],"hsluv":[159.294479220170871,99.9999999999912,84.3464103530465366]},"#00eecc":{"lch":[84.743580800257746,70.2844566194431195,168.945018717488722],"luv":[84.743580800257746,-68.9802322625990456,13.4771064879770393],"rgb":[0,0.933333333333333348,0.8],"xyz":[0.414711201129386531,0.655055627578477684,0.675867955640744866],"hpluv":[168.945018717488722,198.871854707918374,84.743580800257746],"hsluv":[168.945018717488722,99.9999999999913,84.743580800257746]},"#00eedd":{"lch":[85.1829138464002114,67.6253239558150625,180.163192871920216],"luv":[85.1829138464002114,-67.6250496492668418,-0.192613766721418916],"rgb":[0,0.933333333333333348,0.866666666666666696],"xyz":[0.43622952668002,0.66366295779873119,0.789197803540750864],"hpluv":[180.163192871920216,197.760624486431198,85.1829138464002114],"hsluv":[180.163192871920216,99.9999999999913882,85.1829138464002114]},"#00eeee":{"lch":[85.6646745174910507,67.7744082531008303,192.177050630061217],"luv":[85.6646745174910507,-66.2495152673009358,-14.2958784586901881],"rgb":[0,0.933333333333333348,0.933333333333333348],"xyz":[0.460041706946194784,0.673187829905201207,0.914608619609274087],"hpluv":[192.177050630061217,205.696714727687493,85.6646745174910507],"hsluv":[192.177050630061217,99.9999999999914309,85.6646745174910507]},"#00eeff":{"lch":[86.1889457184888,70.9350767712842867,203.864647638418489],"luv":[86.1889457184888,-64.8703943995767247,-28.6987290135183635],"rgb":[0,0.933333333333333348,1],"xyz":[0.486212755356026149,0.683656249269133931,1.05244280790105593],"hpluv":[203.864647638418489,224.453619733699583,86.1889457184888],"hsluv":[203.864647638418489,99.9999999999939888,86.1889457184888]},"#00ff00":{"lch":[87.7355191096597338,135.789531996666284,127.715012949240474],"luv":[87.7355191096597338,-83.0671197143942663,107.418111239344327],"rgb":[0,1,0],"xyz":[0.35758433938387,0.71516867876775,0.11919477979462],"hpluv":[127.715012949240474,490.145375063702204,87.7355191096597338],"hsluv":[127.715012949240474,100.000000000002217,87.7355191096597338]},"#00ff11":{"lch":[87.7550810882892165,135.01527678270574,127.917210072153054],"luv":[87.7550810882892165,-82.9698837721702915,106.513489059100834],"rgb":[0,1,0.0666666666666666657],"xyz":[0.358596004883507125,0.715573344967604941,0.124522884759375632],"hpluv":[127.917210072153054,488.208403570135204,87.7550810882892165],"hsluv":[127.917210072153054,99.9999999999917719,87.7550810882892165]},"#00ff22":{"lch":[87.7913242833811864,133.592052176160422,128.296258949772664],"luv":[87.7913242833811864,-82.7907071985999892,104.845291769319132],"rgb":[0,1,0.133333333333333331],"xyz":[0.360471363021984181,0.716323488222995741,0.134399770955354825],"hpluv":[128.296258949772664,484.641757887342919,87.7913242833811864],"hsluv":[128.296258949772664,99.9999999999919,87.7913242833811864]},"#00ff33":{"lch":[87.850943105558116,131.282721750620482,128.932531697131338],"luv":[87.850943105558116,-82.4986966230128616,102.123053644879462],"rgb":[0,1,0.2],"xyz":[0.363559113754441887,0.717558588515978868,0.150661924812965975],"hpluv":[128.932531697131338,478.837727878060548,87.850943105558116],"hsluv":[128.932531697131338,99.999999999991843,87.850943105558116]},"#00ff44":{"lch":[87.9368982766027756,128.022939247233296,129.878593634905172],"luv":[87.9368982766027756,-82.0835673214571528,98.245411848516369],"rgb":[0,1,0.266666666666666663],"xyz":[0.368017108998660802,0.719341786613666434,0.174140699765852613],"hpluv":[129.878593634905172,470.610169071279643,87.9368982766027756],"hsluv":[129.878593634905172,99.9999999999916724,87.9368982766027756]},"#00ff55":{"lch":[88.0516385770734189,123.799916713223595,131.196479790431113],"luv":[88.0516385770734189,-81.5399771501718362,93.1539129857171133],"rgb":[0,1,0.333333333333333315],"xyz":[0.373979523038022788,0.721726752229411184,0.205542747039826501],"hpluv":[131.196479790431113,459.892953467552729,88.0516385770734189],"hsluv":[131.196479790431113,99.9999999999917719,88.0516385770734189]},"#00ff66":{"lch":[88.197238997611,118.653311588493224,132.964137709394919],"luv":[88.197238997611,-80.8670327043222557,86.8281715373192498],"rgb":[0,1,0.4],"xyz":[0.381564518811531,0.724760750538814502,0.245490391446970491],"hpluv":[132.964137709394919,446.748834194207859,88.197238997611],"hsluv":[132.964137709394919,99.9999999999917719,88.197238997611]},"#00ff77":{"lch":[88.3754745956423164,112.679107800887323,135.2824164931273],"luv":[88.3754745956423164,-80.0679233099649537,79.2824633297525452],"rgb":[0,1,0.466666666666666674],"xyz":[0.390878531831732878,0.728486355746895353,0.2945441933533684],"hpluv":[135.2824164931273,431.3936933951166,88.3754745956423164],"hsluv":[135.2824164931273,99.9999999999915445,88.3754745956423164]},"#00ff88":{"lch":[88.587864465470858,106.036155512425779,138.2828406903445],"luv":[88.587864465470858,-79.1495135423216425,70.5621767086956311],"rgb":[0,1,0.533333333333333326],"xyz":[0.402018948933676679,0.732942522587672896,0.353217056756940539],"hpluv":[138.2828406903445,414.239888157084465,88.587864465470858],"hsluv":[138.2828406903445,99.9999999999914,88.587864465470858]},"#00ff99":{"lch":[88.8357000190422,98.9561663203651278,142.1349886621461],"luv":[88.8357000190422,-78.1218422027535695,60.739613298668921],"rgb":[0,1,0.6],"xyz":[0.415075913042749378,0.738165308231302109,0.421983734398058474],"hpluv":[142.1349886621461,395.967958147281365,88.8357000190422],"hsluv":[142.1349886621461,99.9999999999915303,88.8357000190422]},"#00ffaa":{"lch":[89.1200644426462674,91.7580340339716258,147.049061977519528],"luv":[89.1200644426462674,-76.997527549554917,49.9090929694682828],"rgb":[0,1,0.66666666666666663],"xyz":[0.430133606008001934,0.744188385417403175,0.501287584015057],"hpluv":[147.049061977519528,377.639750156066668,89.1200644426462674],"hsluv":[147.049061977519528,99.9999999999913,89.1200644426462674]},"#00ffbb":{"lch":[89.4418470234824241,84.8653215767476468,153.262243037154207],"luv":[89.4418470234824241,-75.791105608661141,38.1815546689964407],"rgb":[0,1,0.733333333333333282],"xyz":[0.447271198115338608,0.751043422260337934,0.591545569113698821],"hpluv":[153.262243037154207,360.863433446149145,89.4418470234824241],"hsluv":[153.262243037154207,99.9999999999912,89.4418470234824241]},"#00ffcc":{"lch":[89.801754487955634,78.8187300060100569,160.986090443114392],"luv":[89.801754487955634,-74.5183415032407197,25.6789598575702236],"rgb":[0,1,0.8],"xyz":[0.466563573559060307,0.758760372437826791,0.693152079783969],"hpluv":[160.986090443114392,347.997153451554084,89.801754487955634],"hsluv":[160.986090443114392,99.9999999999912461,89.801754487955634]},"#00ffdd":{"lch":[90.2003206582774339,74.260092310928,170.286849800478649],"luv":[90.2003206582774339,-73.1955569867131572,12.5288366352336915],"rgb":[0,1,0.866666666666666696],"xyz":[0.488081899109693906,0.767367702658080297,0.806481927683975],"hpluv":[170.286849800478649,342.308208972166483,90.2003206582774339],"hsluv":[170.286849800478649,99.9999999999913314,90.2003206582774339]},"#00ffee":{"lch":[90.6379152481429458,71.8480695265374294,180.909719109957],"luv":[90.6379152481429458,-71.8390133400929898,-1.14072652818200426],"rgb":[0,1,0.933333333333333348],"xyz":[0.511894079375868616,0.776892574764550314,0.931892743752498198],"hpluv":[180.909719109957,347.895283980605143,90.6379152481429458],"hsluv":[180.909719109957,99.999999999991374,90.6379152481429458]},"#00ffff":{"lch":[91.114752316705065,72.0862882649682,192.17705063006116],"luv":[91.114752316705065,-70.4643799638718207,-15.205397466925735],"rgb":[0,1,1],"xyz":[0.5380651277857,0.787360994128483,1.06972693204428],"hpluv":[192.17705063006116,369.190533917051368,91.114752316705065],"hsluv":[192.17705063006116,99.9999999999914877,91.114752316705065]},"#ff0000":{"lch":[53.23711559542933,179.038096923620287,12.1770506300617765],"luv":[53.23711559542933,175.009822162883836,37.7650936255616],"rgb":[1,0,0],"xyz":[0.41239079926595,0.21263900587151,0.019330818715591],"hpluv":[12.1770506300617765,426.746789183125202,53.23711559542933],"hsluv":[12.1770506300617765,100.000000000002203,53.23711559542933]},"#ff0011":{"lch":[53.2810087118185294,177.689248384364731,11.7592124156573554],"luv":[53.2810087118185294,173.960033822228979,36.2129206771479346],"rgb":[1,0,0.0666666666666666657],"xyz":[0.413402464765587119,0.213043672071364848,0.0246589236803466325],"hpluv":[11.7592124156573554,423.182830024727082,53.2810087118185294],"hsluv":[11.7592124156573554,99.9999999999986073,53.2810087118185294]},"#ff0022":{"lch":[53.362228057366309,175.255817292919801,10.9800713678561319],"luv":[53.362228057366309,172.047495148921342,33.3805468497921751],"rgb":[1,0,0.133333333333333331],"xyz":[0.415277822904064176,0.213793815326755676,0.0345358098763258251],"hpluv":[10.9800713678561319,416.75211680728853,53.362228057366309],"hsluv":[10.9800713678561319,99.9999999999986215,53.362228057366309]},"#ff0033":{"lch":[53.4955416476677499,171.43316235878109,9.68478250033725],"luv":[53.4955416476677499,168.989928530586468,28.8397852204116347],"rgb":[1,0,0.2],"xyz":[0.418365573636521881,0.215028915619738775,0.0507979637339369683],"hpluv":[9.68478250033725,406.646064741178918,53.4955416476677499],"hsluv":[9.68478250033725,99.9999999999986215,53.4955416476677499]},"#ff0044":{"lch":[53.6871179383659722,166.29954793496961,7.78930386328567259],"luv":[53.6871179383659722,164.765128442936401,22.5386799204815809],"rgb":[1,0,0.266666666666666663],"xyz":[0.422823568880740797,0.216812113717426369,0.0742767386868236],"hpluv":[7.78930386328567259,393.061316669856922,53.6871179383659722],"hsluv":[7.78930386328567259,99.9999999999987637,53.6871179383659722]},"#ff0055":{"lch":[53.9417095924386558,160.100368719231,5.2128969892355661],"luv":[53.9417095924386558,159.438189183864722,14.5461986032050437],"rgb":[1,0,0.333333333333333315],"xyz":[0.428785982920102782,0.219197079333171202,0.105678785960797522],"hpluv":[5.2128969892355661,376.623098544524225,53.9417095924386558],"hsluv":[5.2128969892355661,99.9999999999988,53.9417095924386558]},"#ff0066":{"lch":[54.2629295430466669,153.227313284557312,1.88082466234467849],"luv":[54.2629295430466669,153.144763004983872,5.02902580538230204],"rgb":[1,0,0.4],"xyz":[0.436370978693610967,0.222231077642574493,0.145626430367941484],"hpluv":[1.88082466234467849,358.321012364802243,54.2629295430466669],"hsluv":[1.88082466234467849,99.99999999999892,54.2629295430466669]},"#ff0077":{"lch":[54.6533978532017244,146.184101175375929,357.735148851436577],"luv":[54.6533978532017244,146.06990602550718,-5.77702260269427281],"rgb":[1,0,0.466666666666666674],"xyz":[0.445684991713812872,0.225956682850655316,0.194680232274339393],"hpluv":[357.735148851436577,339.408176675868503,54.6533978532017244],"hsluv":[357.735148851436577,99.9999999999990479,54.6533978532017244]},"#ff0088":{"lch":[55.1148373309560782,139.538803635294983,352.754628092234327],"luv":[55.1148373309560782,138.424605854630585,-17.5984719211521572],"rgb":[1,0,0.533333333333333326],"xyz":[0.456825408815756673,0.230412849691432914,0.253353095677911533],"hpluv":[352.754628092234327,321.26675874055752,55.1148373309560782],"hsluv":[352.754628092234327,99.9999999999991616,55.1148373309560782]},"#ff0099":{"lch":[55.6481496721619493,133.863929319774144,346.981903482220218],"luv":[55.6481496721619493,130.423488026195145,-30.1540269949209758],"rgb":[1,0,0.6],"xyz":[0.469882372924829372,0.235635635335062071,0.322119773319029468],"hpluv":[346.981903482220218,305.247535929832054,55.6481496721619493],"hsluv":[346.981903482220218,99.9999999999993463,55.6481496721619493]},"#ff00aa":{"lch":[56.2534865150640258,129.667114810270476,340.549180922221581],"luv":[56.2534865150640258,122.266710865918853,-43.1788383036143273],"rgb":[1,0,0.66666666666666663],"xyz":[0.484940065890081928,0.241658712521163166,0.401423622936028068],"hpluv":[340.549180922221581,292.495864077812769,56.2534865150640258],"hsluv":[340.549180922221581,99.9999999999994742,56.2534865150640258]},"#ff00bb":{"lch":[56.9303217870161689,127.321325924901956,333.685619315648239],"luv":[56.9303217870161689,114.127678427447606,-56.4410582115202928],"rgb":[1,0,0.733333333333333282],"xyz":[0.502077657997418547,0.248513749364097924,0.491681608034669815],"hpluv":[333.685619315648239,283.789838362024682,56.9303217870161689],"hsluv":[333.685619315648239,99.9999999999995879,56.9303217870161689]},"#ff00cc":{"lch":[57.6775275187384153,127.012826563172382,326.690520651062286],"luv":[57.6775275187384153,106.146716951325558,-69.7505024499586312],"rgb":[1,0,0.8],"xyz":[0.521370033441140301,0.256230699541586726,0.593288118704939915],"hpluv":[326.690520651062286,279.434659423159303,57.6775275187384153],"hsluv":[326.690520651062286,99.9999999999997726,57.6775275187384153]},"#ff00dd":{"lch":[58.4934529509690151,128.727977043064641,319.874434183361473],"luv":[58.4934529509690151,98.4297766537384149,-82.9606602040687],"rgb":[1,0,0.866666666666666696],"xyz":[0.542888358991773901,0.264838029761840288,0.706617966604945913],"hpluv":[319.874434183361473,279.257606739571429,58.4934529509690151],"hsluv":[319.874434183361473,99.9999999999999716,58.4934529509690151]},"#ff00ee":{"lch":[59.3760054748790367,132.286429048213932,313.494468670954461],"luv":[59.3760054748790367,91.0507046157626,-95.9659757377649214],"rgb":[1,0,0.933333333333333348],"xyz":[0.56670053925794861,0.274362901868310305,0.832028782673469136],"hpluv":[313.494468670954461,282.711609251625362,59.3760054748790367],"hsluv":[313.494468670954461,100.000000000000156,59.3760054748790367]},"#ff00ff":{"lch":[60.3227313545512942,137.405400537897037,307.715012949243601],"luv":[60.3227313545512942,84.0556019897527875,-108.696365491768773],"rgb":[1,0,1],"xyz":[0.59287158766778,0.284831321232243,0.969862970965251],"hpluv":[307.715012949243601,289.042783730483336,60.3227313545512942],"hsluv":[307.715012949243601,100.000000000000384,60.3227313545512942]},"#ff1100":{"lch":[53.6695097624616864,176.771562285449363,12.5954542867932275],"luv":[53.6695097624616864,172.517389506501019,38.5478345786208934],"rgb":[1,0.0666666666666666657,0],"xyz":[0.414395199526878422,0.216647806393366865,0.019998952135900451],"hpluv":[12.5954542867932275,417.949777534481484,53.6695097624616864],"hsluv":[12.5954542867932275,100.000000000002245,53.6695097624616864]},"#ff1111":{"lch":[53.7128602445647658,175.445128796306847,12.1770506300617765],"luv":[53.7128602445647658,171.497694164414924,37.0072170615611569],"rgb":[1,0.0666666666666666657,0.0666666666666666657],"xyz":[0.415406865026515526,0.217052472593221718,0.0253270571006560807],"hpluv":[12.1770506300617765,414.478837946685644,53.7128602445647658],"hsluv":[12.1770506300617765,99.9999999999986215,53.7128602445647658]},"#ff1122":{"lch":[53.7930781791116743,173.051572118951754,11.3967197916969329],"luv":[53.7930781791116743,169.639425839354459,34.1952016185739538],"rgb":[1,0.0666666666666666657,0.133333333333333331],"xyz":[0.417282223164992583,0.217802615848612546,0.0352039432966352803],"hpluv":[11.3967197916969329,408.214548988049671,53.7930781791116743],"hsluv":[11.3967197916969329,99.9999999999987,53.7930781791116743]},"#ff1133":{"lch":[53.9247555399676912,169.290109899416,10.0990648343251674],"luv":[53.9247555399676912,166.667136068812482,29.6851320424264138],"rgb":[1,0.0666666666666666657,0.2],"xyz":[0.420369973897450289,0.219037716141595645,0.0514660971542464235],"hpluv":[10.0990648343251674,398.366425235699353,53.9247555399676912],"hsluv":[10.0990648343251674,99.9999999999987,53.9247555399676912]},"#ff1144":{"lch":[54.1139966850166445,164.235972949617775,8.19925898659400154],"luv":[54.1139966850166445,162.55716522781,23.4226993279181244],"rgb":[1,0.0666666666666666657,0.266666666666666663],"xyz":[0.424827969141669204,0.220820914239283239,0.074944872107133062],"hpluv":[8.19925898659400154,385.121711929848118,54.1139966850166445],"hsluv":[8.19925898659400154,99.9999999999988489,54.1139966850166445]},"#ff1155":{"lch":[54.365514290002,158.12888296709221,5.61535385404219856],"luv":[54.365514290002,157.370056367003059,15.4728467796533362],"rgb":[1,0.0666666666666666657,0.333333333333333315],"xyz":[0.430790383181031189,0.223205879855028072,0.106346919381106964],"hpluv":[5.61535385404219856,369.085538340858477,54.365514290002],"hsluv":[5.61535385404219856,99.9999999999988916,54.365514290002]},"#ff1166":{"lch":[54.6829025612910442,151.353597545298243,2.27091305216541839],"luv":[54.6829025612910442,151.234730451097789,5.99731567352495443],"rgb":[1,0.0666666666666666657,0.4],"xyz":[0.438375378954539374,0.226239878164431363,0.146294563788250925],"hpluv":[2.27091305216541839,351.221033033747858,54.6829025612910442],"hsluv":[2.27091305216541839,99.999999999999,54.6829025612910442]},"#ff1177":{"lch":[55.0687823252034292,144.407362773795285,358.105880212246802],"luv":[55.0687823252034292,144.328460520523464,-4.77303960367160496],"rgb":[1,0.0666666666666666657,0.466666666666666674],"xyz":[0.44768939197474128,0.229965483372512186,0.195348365694648834],"hpluv":[358.105880212246802,332.753927221166919,55.0687823252034292],"hsluv":[358.105880212246802,99.9999999999990905,55.0687823252034292]},"#ff1188":{"lch":[55.5248949860500716,137.85386672349216,353.096828842063303],"luv":[55.5248949860500716,136.85452147125136,-16.5689023019976744],"rgb":[1,0.0666666666666666657,0.533333333333333326],"xyz":[0.45882980907668508,0.234421650213289784,0.254021229098221],"hpluv":[353.096828842063303,315.043506786171235,55.5248949860500716],"hsluv":[353.096828842063303,99.9999999999992,55.5248949860500716]},"#ff1199":{"lch":[56.0521767726019249,132.264360312052816,347.28491936957397],"luv":[56.0521767726019249,129.020794472918823,-29.1117777254046715],"rgb":[1,0.0666666666666666657,0.6],"xyz":[0.47188677318575778,0.239644435856918941,0.322787906739338937],"hpluv":[347.28491936957397,299.426117704125659,56.0521767726019249],"hsluv":[347.28491936957397,99.9999999999993605,56.0521767726019249]},"#ff11aa":{"lch":[56.6508275614924912,128.148315107741439,340.802676353967],"luv":[56.6508275614924912,121.022209124101792,-42.1380536294115586],"rgb":[1,0.0666666666666666657,0.66666666666666663],"xyz":[0.486944466151010336,0.245667513043020036,0.402091756356337537],"hpluv":[340.802676353967,287.042344439162662,56.6508275614924912],"hsluv":[340.802676353967,99.9999999999995168,56.6508275614924912]},"#ff11bb":{"lch":[57.3203806938084455,125.882364893771992,333.882217516525884],"luv":[57.3203806938084455,113.02864145467457,-55.4156656746030762],"rgb":[1,0.0666666666666666657,0.733333333333333282],"xyz":[0.504082058258347,0.252522549885954795,0.492349741454979284],"hpluv":[333.882217516525884,278.673167271969135,57.3203806938084455],"hsluv":[333.882217516525884,99.9999999999996163,57.3203806938084455]},"#ff11cc":{"lch":[58.0597760671947754,125.656277294901628,326.828156543045054],"luv":[58.0597760671947754,105.178488239830273,-68.7530772780178694],"rgb":[1,0.0666666666666666657,0.8],"xyz":[0.523374433702068709,0.260239500063443596,0.593956252125249384],"hpluv":[326.828156543045054,274.630115267561905,58.0597760671947754],"hsluv":[326.828156543045054,99.9999999999997868,58.0597760671947754]},"#ff11dd":{"lch":[58.8674364673636177,127.458097444326981,319.957026901825429],"luv":[58.8674364673636177,97.5770916499376568,-82.0016938195011846],"rgb":[1,0.0666666666666666657,0.866666666666666696],"xyz":[0.544892759252702308,0.268846830283697158,0.707286100025255382],"hpluv":[319.957026901825429,274.746161939823423,58.8674364673636177],"hsluv":[319.957026901825429,100.000000000000028,58.8674364673636177]},"#ff11ee":{"lch":[59.7413458233107519,131.106916258937218,313.5305052972667],"luv":[59.7413458233107519,90.2986667085849319,-95.0535337669245877],"rgb":[1,0.0666666666666666657,0.933333333333333348],"xyz":[0.568704939518877,0.278371702390167175,0.832696916093778605],"hpluv":[313.5305052972667,278.477381794919836,59.7413458233107519],"hsluv":[313.5305052972667,100.000000000000199,59.7413458233107519]},"#ff11ff":{"lch":[60.6791274610807534,136.317870534400242,307.715012949243601],"luv":[60.6791274610807534,83.3903225409976,-107.83606045076894],"rgb":[1,0.0666666666666666657,1],"xyz":[0.594875987928708438,0.288840121754099899,0.97053110438556045],"hpluv":[307.715012949243601,285.070838096226908,60.6791274610807534],"hsluv":[307.715012949243601,100.000000000000398,60.6791274610807534]},"#ff2200":{"lch":[54.4571507543770679,172.725520469573979,13.3786813235288875],"luv":[54.4571507543770679,168.038102184023103,39.9662562154253393],"rgb":[1,0.133333333333333331,0],"xyz":[0.418110823261646336,0.224079053862902833,0.0212374933808230602],"hpluv":[13.3786813235288875,402.476865089738737,54.4571507543770679],"hsluv":[13.3786813235288875,100.00000000000216,54.4571507543770679]},"#ff2211":{"lch":[54.4995382972682876,171.437527349711331,12.9593558016228254],"luv":[54.4995382972682876,167.070909686171433,38.446546274251638],"rgb":[1,0.133333333333333331,0.0666666666666666657],"xyz":[0.41912248876128344,0.224483720062757686,0.0265655983455786934],"hpluv":[12.9593558016228254,399.164948195999784,54.4995382972682876],"hsluv":[12.9593558016228254,99.999999999998721,54.4995382972682876]},"#ff2222":{"lch":[54.5779789595956117,169.112342257331477,12.1770506300617924],"luv":[54.5779789595956117,165.307392407273255,35.6714216042562171],"rgb":[1,0.133333333333333331,0.133333333333333331],"xyz":[0.420997846899760497,0.225233863318148514,0.036442484541557886],"hpluv":[12.1770506300617924,393.185217729465933,54.5779789595956117],"hsluv":[12.1770506300617924,99.9999999999987494,54.5779789595956117]},"#ff2233":{"lch":[54.7067518227456,165.455769736233549,10.8753803895539445],"luv":[54.7067518227456,162.484163442906947,31.2171166072114978],"rgb":[1,0.133333333333333331,0.2],"xyz":[0.424085597632218203,0.226468963611131613,0.0527046383991690293],"hpluv":[10.8753803895539445,383.778210348001721,54.7067518227456],"hsluv":[10.8753803895539445,99.9999999999987779,54.7067518227456]},"#ff2244":{"lch":[54.8918465894738148,160.537768894747074,8.96806115251763103],"luv":[54.8918465894738148,158.575257224785304,25.0252480066910863],"rgb":[1,0.133333333333333331,0.266666666666666663],"xyz":[0.428543592876437118,0.228252161708819207,0.0761834133520556678],"hpluv":[8.96806115251763103,371.115171122776133,54.8918465894738148],"hsluv":[8.96806115251763103,99.9999999999988347,54.8918465894738148]},"#ff2255":{"lch":[55.1379036013317432,154.588213330392733,6.3708707633682522],"luv":[55.1379036013317432,153.633547891150499,17.1536778289841081],"rgb":[1,0.133333333333333331,0.333333333333333315],"xyz":[0.434506006915799103,0.23063712732456404,0.107585460626029583],"hpluv":[6.3708707633682522,355.76683037739258,55.1379036013317432],"hsluv":[6.3708707633682522,99.9999999999989342,55.1379036013317432]},"#ff2266":{"lch":[55.4484819892530254,147.979820726080618,3.00414546296194196],"luv":[55.4484819892530254,147.776458796099433,7.75535736170059753],"rgb":[1,0.133333333333333331,0.4],"xyz":[0.442091002689307289,0.23367112563396733,0.147533105033173545],"hpluv":[3.00414546296194196,338.650844053811227,55.4484819892530254],"hsluv":[3.00414546296194196,99.9999999999990763,55.4484819892530254]},"#ff2277":{"lch":[55.8262016697843961,141.198613687408425,358.803757025958646],"luv":[55.8262016697843961,141.167840095073672,-2.94778393674187145],"rgb":[1,0.133333333333333331,0.466666666666666674],"xyz":[0.451405015709509194,0.237396730842048154,0.196586906939571454],"hpluv":[358.803757025958646,320.945787006908631,55.8262016697843961],"hsluv":[358.803757025958646,99.9999999999991616,55.8262016697843961]},"#ff2288":{"lch":[56.2728344602164299,134.800794021339357,353.742009538390391],"luv":[56.2728344602164299,133.997535770156077,-14.694028593592142],"rgb":[1,0.133333333333333331,0.533333333333333326],"xyz":[0.462545432811453,0.241852897682825752,0.255259770343143622],"hpluv":[353.742009538390391,303.971583410200083,56.2728344602164299],"hsluv":[353.742009538390391,99.9999999999992752,56.2728344602164299]},"#ff2299":{"lch":[56.7893750973531866,129.355771045476672,347.857065824102108],"luv":[56.7893750973531866,126.461550291410035,-27.2101415039127161],"rgb":[1,0.133333333333333331,0.6],"xyz":[0.47560239692052575,0.247075683326454909,0.324026447984261501],"hpluv":[347.857065824102108,289.040064659782502,56.7893750973531866],"hsluv":[347.857065824102108,99.9999999999994316,56.7893750973531866]},"#ff22aa":{"lch":[57.3761062638205743,125.376808270539939,341.281866819384959],"luv":[57.3761062638205743,118.745473604842076,-40.2350164715945766],"rgb":[1,0.133333333333333331,0.66666666666666663],"xyz":[0.49066008988577825,0.253098760512556031,0.403330297601260102],"hpluv":[341.281866819384959,277.284417349873706,57.3761062638205743],"hsluv":[341.281866819384959,99.999999999999531,57.3761062638205743]},"#ff22bb":{"lch":[58.0326640845464112,123.247659375493754,334.254064951888324],"luv":[58.0326640845464112,111.01274832153284,-53.5364852379920606],"rgb":[1,0.133333333333333331,0.733333333333333282],"xyz":[0.507797681993114924,0.25995379735549079,0.493588282699901848],"hpluv":[334.254064951888324,269.491764983662165,58.0326640845464112],"hsluv":[334.254064951888324,99.9999999999997726,58.0326640845464112]},"#ff22cc":{"lch":[58.7581065478829316,123.164795236141373,327.088444575119183],"luv":[58.7581065478829316,103.398114188518221,-66.9208246199849128],"rgb":[1,0.133333333333333331,0.8],"xyz":[0.527090057436836679,0.267670747532979592,0.595194793370172],"hpluv":[327.088444575119183,265.985598747154427,58.7581065478829316],"hsluv":[327.088444575119183,99.9999999999998721,58.7581065478829316]},"#ff22dd":{"lch":[59.550985046801415,125.119413407905796,320.113090366201448],"luv":[59.550985046801415,96.0055877759378,-80.2358693312108358],"rgb":[1,0.133333333333333331,0.866666666666666696],"xyz":[0.548608382987470167,0.276278077753233098,0.708524641270178],"hpluv":[320.113090366201448,266.609166102550091,59.550985046801415],"hsluv":[320.113090366201448,100.000000000000028,59.550985046801415]},"#ff22ee":{"lch":[60.4094179672163705,128.929416765847606,313.598505937960113],"luv":[60.4094179672163705,88.9098108664153273,-93.3693742041783281],"rgb":[1,0.133333333333333331,0.933333333333333348],"xyz":[0.572420563253645,0.285802949859703115,0.833935457338701225],"hpluv":[313.598505937960113,270.823716236275,60.4094179672163705],"hsluv":[313.598505937960113,100.000000000000227,60.4094179672163705]},"#ff22ff":{"lch":[61.3311646171935223,134.305840538380238,307.715012949243601],"luv":[61.3311646171935223,82.1594946996257249,-106.244417422389745],"rgb":[1,0.133333333333333331,1],"xyz":[0.598591611663476297,0.296271369223635839,0.97176964563048307],"hpluv":[307.715012949243601,277.877263991976,61.3311646171935223],"hsluv":[307.715012949243601,100.000000000000398,61.3311646171935223]},"#ff3300":{"lch":[55.7168894472394811,166.476173059961667,14.689559134518138],"luv":[55.7168894472394811,161.034729269155179,42.2153072463082495],"rgb":[1,0.2,0],"xyz":[0.424228545350657182,0.236314498040924637,0.0232767340771599419],"hpluv":[14.689559134518138,379.144314271077917,55.7168894472394811],"hsluv":[14.689559134518138,100.000000000002203,55.7168894472394811]},"#ff3311":{"lch":[55.7578022303213,165.243627812887922,14.2690908575150317],"luv":[55.7578022303213,160.145669888681539,40.7286256663500339],"rgb":[1,0.2,0.0666666666666666657],"xyz":[0.425240210850294287,0.236719164240779489,0.0286048390419155751],"hpluv":[14.2690908575150317,376.06108995847427,55.7578022303213],"hsluv":[14.2690908575150317,99.9999999999988,55.7578022303213]},"#ff3322":{"lch":[55.8335204651182835,163.01701714894287,13.4842232594842422],"luv":[55.8335204651182835,158.523316810386802,38.0119179675594552],"rgb":[1,0.2,0.133333333333333331],"xyz":[0.427115568988771344,0.237469307496170318,0.0384817252378947677],"hpluv":[13.4842232594842422,370.490653647292163,55.8335204651182835],"hsluv":[13.4842232594842422,99.9999999999987494,55.8335204651182835]},"#ff3333":{"lch":[55.9578428172660267,159.511521097175432,12.1770506300617853],"luv":[55.9578428172660267,155.922585303490365,33.6462888742638455],"rgb":[1,0.2,0.2],"xyz":[0.430203319721229049,0.238704407789153417,0.0547438790955059179],"hpluv":[12.1770506300617853,361.718248261175631,55.9578428172660267],"hsluv":[12.1770506300617853,99.9999999999988773,55.9578428172660267]},"#ff3344":{"lch":[56.1365811585215368,154.789240798906889,10.2588910791084782],"luv":[56.1365811585215368,152.31463646984821,27.5673826135157078],"rgb":[1,0.2,0.266666666666666663],"xyz":[0.434661314965447965,0.240487605886841,0.0782226540483925564],"hpluv":[10.2588910791084782,349.892100101075414,56.1365811585215368],"hsluv":[10.2588910791084782,99.9999999999989626,56.1365811585215368]},"#ff3355":{"lch":[56.3742616664660403,149.065442517766684,7.64169944339336649],"luv":[56.3742616664660403,147.741595680550319,19.8223878173745902],"rgb":[1,0.2,0.333333333333333315],"xyz":[0.44062372900480995,0.242872571502585843,0.109624701322366458],"hpluv":[7.64169944339336649,335.533149366899124,56.3742616664660403],"hsluv":[7.64169944339336649,99.9999999999990195,56.3742616664660403]},"#ff3366":{"lch":[56.674385203130754,142.694983818340035,4.24028319431916056],"luv":[56.674385203130754,142.304390380845462,10.550776523662277],"rgb":[1,0.2,0.4],"xyz":[0.448208724778318135,0.245906569811989134,0.14957234572951042],"hpluv":[4.24028319431916056,319.492902958598108,56.674385203130754],"hsluv":[4.24028319431916056,99.9999999999991758,56.674385203130754]},"#ff3377":{"lch":[57.0395646827704468,136.14730874514737,359.983392279567909],"luv":[57.0395646827704468,136.147303025702882,-0.0394635770517579934],"rgb":[1,0.2,0.466666666666666674],"xyz":[0.457522737798520041,0.249632175020069957,0.198626147635908329],"hpluv":[359.983392279567909,302.881107814185,57.0395646827704468],"hsluv":[359.983392279567909,99.9999999999992895,57.0395646827704468]},"#ff3388":{"lch":[57.4716120619286954,129.967879448766553,354.83565117969431],"luv":[57.4716120619286954,129.440287861866665,-11.6987848363070146],"rgb":[1,0.2,0.533333333333333326],"xyz":[0.468663154900463841,0.254088341860847555,0.257299011039480496],"hpluv":[354.83565117969431,286.960407533356261,57.4716120619286954],"hsluv":[354.83565117969431,99.9999999999993889,57.4716120619286954]},"#ff3399":{"lch":[57.9716047421228353,124.724336507791776,348.82951213288959],"luv":[57.9716047421228353,122.361442957788952,-24.1627273832372573],"rgb":[1,0.2,0.6],"xyz":[0.481720119009536596,0.259311127504476713,0.326065688680598431],"hpluv":[348.82951213288959,273.007894976207297,57.9716047421228353],"hsluv":[348.82951213288959,99.9999999999995737,57.9716047421228353]},"#ff33aa":{"lch":[58.5399451724763935,120.937271340322638,342.098036856126953],"luv":[58.5399451724763935,115.081956962752116,-37.174813797329108],"rgb":[1,0.2,0.66666666666666663],"xyz":[0.496777811974789096,0.265334204690577835,0.405369538297597032],"hpluv":[342.098036856126953,262.148381504719794,58.5399451724763935],"hsluv":[342.098036856126953,99.9999999999996732,58.5399451724763935]},"#ff33bb":{"lch":[59.1764201449825862,119.003132790944747,334.888094830460091],"luv":[59.1764201449825862,107.755032536641394,-50.5034511403585498],"rgb":[1,0.2,0.733333333333333282],"xyz":[0.513915404082125771,0.272189241533512594,0.495627523396238778],"hpluv":[334.888094830460091,255.181409444549388,59.1764201449825862],"hsluv":[334.888094830460091,99.9999999999998295,59.1764201449825862]},"#ff33cc":{"lch":[59.8802624584280494,119.13012657470172,327.532171012183937],"luv":[59.8802624584280494,100.509254029343069,-63.9521454852723039],"rgb":[1,0.2,0.8],"xyz":[0.533207779525847525,0.279906191711001395,0.597234034066508879],"hpluv":[327.532171012183937,252.451080902073329,59.8802624584280494],"hsluv":[327.532171012183937,100.000000000000043,59.8802624584280494]},"#ff33dd":{"lch":[60.650215463767978,121.314823858854638,320.378757122173454],"luv":[60.650215463767978,93.4460015542912146,-77.3636302238999747],"rgb":[1,0.2,0.866666666666666696],"xyz":[0.554726105076481,0.288513521931254902,0.710563881966514876],"hpluv":[320.378757122173454,253.817084039055629,60.650215463767978],"hsluv":[320.378757122173454,100.000000000000171,60.650215463767978]},"#ff33ee":{"lch":[61.484599762034378,125.372817174433621,313.71398784253438],"luv":[61.484599762034378,86.6400000332951805,-90.6192787462169775],"rgb":[1,0.2,0.933333333333333348],"xyz":[0.578538285342655723,0.298038394037724919,0.8359746980350381],"hpluv":[313.71398784253438,258.747618308410438,61.484599762034378],"hsluv":[313.71398784253438,100.000000000000242,61.484599762034378]},"#ff33ff":{"lch":[62.3813806681475,131.007738376122177,307.715012949243658],"luv":[62.3813806681475,80.141932350642,-103.635409940481253],"rgb":[1,0.2,1],"xyz":[0.604709333752487144,0.308506813401657642,0.97380888632682],"hpluv":[307.715012949243658,266.490230971107223,62.3813806681475],"hsluv":[307.715012949243658,100.000000000000597,62.3813806681475]},"#ff4400":{"lch":[57.461133143380664,158.273971604467,16.6278363926044079],"luv":[57.461133143380664,151.655533944896689,45.2907177172089845],"rgb":[1,0.266666666666666663,0],"xyz":[0.433061115833623222,0.253979639006856939,0.0262209242381485352],"hpluv":[16.6278363926044079,349.522099776260404,57.461133143380664],"hsluv":[16.6278363926044079,100.000000000002203,57.461133143380664]},"#ff4411":{"lch":[57.500127691013958,157.107055615985729,16.2066010587584444],"luv":[57.500127691013958,150.863862649088418,43.84885256105823],"rgb":[1,0.266666666666666663,0.0666666666666666657],"xyz":[0.434072781333260327,0.254384305206711792,0.031549029202904165],"hpluv":[16.2066010587584444,346.709871357654038,57.500127691013958],"hsluv":[16.2066010587584444,99.9999999999990195,57.500127691013958]},"#ff4422":{"lch":[57.5723039440668174,154.996970095022306,15.4196600073807488],"luv":[57.5723039440668174,149.417734299680745,41.2116660108183908],"rgb":[1,0.266666666666666663,0.133333333333333331],"xyz":[0.435948139471737384,0.255134448462102592,0.0414259153988833645],"hpluv":[15.4196600073807488,341.624434338608523,57.5723039440668174],"hsluv":[15.4196600073807488,99.9999999999990195,57.5723039440668174]},"#ff4433":{"lch":[57.6908335218327437,151.669616752661852,14.1071803519879388],"luv":[57.6908335218327437,147.095485225188412,36.9674298845038223],"rgb":[1,0.266666666666666663,0.2],"xyz":[0.439035890204195089,0.256369548755085719,0.0576880692564945077],"hpluv":[14.1071803519879388,333.603886972203838,57.6908335218327437],"hsluv":[14.1071803519879388,99.9999999999991189,57.6908335218327437]},"#ff4444":{"lch":[57.8612930010941682,147.177084719743902,12.177050630061812],"luv":[57.8612930010941682,143.865668066403089,31.0445457111261],"rgb":[1,0.266666666666666663,0.266666666666666663],"xyz":[0.443493885448414,0.258152746852773285,0.0811668442093811393],"hpluv":[12.177050630061812,322.76868159643891,57.8612930010941682],"hsluv":[12.177050630061812,99.9999999999991616,57.8612930010941682]},"#ff4455":{"lch":[58.088054010202,141.716285969530816,9.53556562214303405],"luv":[58.088054010202,139.758186425649882,23.4766913344092778],"rgb":[1,0.266666666666666663,0.333333333333333315],"xyz":[0.449456299487776,0.260537712468518146,0.112568891483355055],"hpluv":[9.53556562214303405,309.579547415252762,58.088054010202],"hsluv":[9.53556562214303405,99.9999999999992184,58.088054010202]},"#ff4466":{"lch":[58.3745334436288772,135.619673907166316,6.08910281061040859],"luv":[58.3745334436288772,134.854526247228307,14.3858507333331573],"rgb":[1,0.266666666666666663,0.4],"xyz":[0.457041295261284175,0.263571710777921464,0.152516535890499016],"hpluv":[6.08910281061040859,294.807548797669426,58.3745334436288772],"hsluv":[6.08910281061040859,99.9999999999992468,58.3745334436288772]},"#ff4477":{"lch":[58.7233249761193292,129.336052383116169,1.75519751784143763],"luv":[58.7233249761193292,129.275370036155351,3.96145782045869943],"rgb":[1,0.266666666666666663,0.466666666666666674],"xyz":[0.466355308281486081,0.26729731598600226,0.201570337796896926],"hpluv":[1.75519751784143763,279.478426792191101,58.7233249761193292],"hsluv":[1.75519751784143763,99.99999999999946,58.7233249761193292]},"#ff4488":{"lch":[59.1362810655005831,123.398173767481396,356.485857706034096],"luv":[59.1362810655005831,123.16614811103635,-7.56367957014252212],"rgb":[1,0.266666666666666663,0.533333333333333326],"xyz":[0.477495725383429881,0.271753482826779857,0.260243201200469065],"hpluv":[356.485857706034096,264.785408966002421,59.1362810655005831],"hsluv":[356.485857706034096,99.9999999999995595,59.1362810655005831]},"#ff4499":{"lch":[59.6145739069951901,118.37348532064955,350.303370213710309],"luv":[59.6145739069951901,116.682328099077466,-19.9378117238896806],"rgb":[1,0.266666666666666663,0.6],"xyz":[0.490552689492502636,0.276976268470409,0.329009878841587],"hpluv":[350.303370213710309,251.965637795338,59.6145739069951901],"hsluv":[350.303370213710309,99.9999999999997158,59.6145739069951901]},"#ff44aa":{"lch":[60.1587486598557177,114.795436800176844,343.339450530546515],"luv":[60.1587486598557177,109.976338996192666,-32.9119609129858475],"rgb":[1,0.266666666666666663,0.66666666666666663],"xyz":[0.505610382457755136,0.282999345656510137,0.408313728458585601],"hpluv":[343.339450530546515,242.139230170638513,60.1587486598557177],"hsluv":[343.339450530546515,99.9999999999997726,60.1587486598557177]},"#ff44bb":{"lch":[60.768775409955694,113.081121999454581,335.854341209703],"luv":[60.768775409955694,103.187483930843442,-46.2567110015126133],"rgb":[1,0.266666666666666663,0.733333333333333282],"xyz":[0.522747974565091811,0.289854382499444896,0.498571713557227347],"hpluv":[335.854341209703,236.128794952899398,60.768775409955694],"hsluv":[335.854341209703,99.9999999999999716,60.768775409955694]},"#ff44cc":{"lch":[61.4441027606342232,113.457557937670586,328.208302422827],"luv":[61.4441027606342232,96.4354118722023088,-59.7731443895883743],"rgb":[1,0.266666666666666663,0.8],"xyz":[0.542040350008813565,0.297571332676933697,0.600178224227497559],"hpluv":[328.208302422827,234.310931883055929,61.4441027606342232],"hsluv":[328.208302422827,100.000000000000128,61.4441027606342232]},"#ff44dd":{"lch":[62.1837139115479403,115.929787300919912,320.782684481283354],"luv":[62.1837139115479403,89.8170022013310358,-73.2981698216444357],"rgb":[1,0.266666666666666663,0.866666666666666696],"xyz":[0.563558675559447,0.306178662897187204,0.713508072127503556],"hpluv":[320.782684481283354,236.568931830040128,62.1837139115479403],"hsluv":[320.782684481283354,100.000000000000384,62.1837139115479403]},"#ff44ee":{"lch":[62.986184892514558,120.309477517885213,313.888915695758442],"luv":[62.986184892514558,83.4060394378513195,-86.7052649261745643],"rgb":[1,0.266666666666666663,0.933333333333333348],"xyz":[0.587370855825621874,0.315703535003657221,0.83891888819602678],"hpluv":[313.888915695758442,242.378371703623515,62.986184892514558],"hsluv":[313.888915695758442,100.000000000000512,62.986184892514558]},"#ff44ff":{"lch":[63.8497439492436,126.288239910703226,307.715012949243771],"luv":[63.8497439492436,77.2548530724802447,-99.9019880507531468],"rgb":[1,0.266666666666666663,1],"xyz":[0.613541904235453184,0.326171954367589945,0.976753076487808514],"hpluv":[307.715012949243771,250.982289693600563,63.8497439492436],"hsluv":[307.715012949243771,100.000000000000711,63.8497439492436]},"#ff5500":{"lch":[59.6718499915998279,148.630700843778015,19.3008598736449528],"luv":[59.6718499915998279,140.27705963161867,49.1266910591374923],"rgb":[1,0.333333333333333315,0],"xyz":[0.444874372547969188,0.277606152435549203,0.030158676476263746],"hpluv":[19.3008598736449528,316.066414507984518,59.6718499915998279],"hsluv":[19.3008598736449528,100.00000000000226,59.6718499915998279]},"#ff5511":{"lch":[59.7086010657385486,147.530698996531413,18.8803784611224046],"luv":[59.7086010657385486,139.59299153240957,47.7399608445355526],"rgb":[1,0.333333333333333315,0.0666666666666666657],"xyz":[0.445886038047606292,0.278010818635404056,0.0354867814410193758],"hpluv":[18.8803784611224046,313.53413530658878,59.7086010657385486],"hsluv":[18.8803784611224046,99.9999999999992184,59.7086010657385486]},"#ff5522":{"lch":[59.7766335415963255,145.539064184811622,18.0939597274483681],"luv":[59.7766335415963255,138.341936048844929,45.2009727113036],"rgb":[1,0.333333333333333315,0.133333333333333331],"xyz":[0.447761396186083349,0.278760961890794856,0.0453636676369985753],"hpluv":[18.0939597274483681,308.949467849715688,59.7766335415963255],"hsluv":[18.0939597274483681,99.9999999999993,59.7766335415963255]},"#ff5533":{"lch":[59.8883826376776085,142.391759670631302,16.7797766500676033],"luv":[59.8883826376776085,136.328925827546385,41.1076295206398825],"rgb":[1,0.333333333333333315,0.2],"xyz":[0.450849146918541055,0.279996062183778,0.0616258214946097185],"hpluv":[16.7797766500676033,301.704368813615531,59.8883826376776085],"hsluv":[16.7797766500676033,99.9999999999992752,59.8883826376776085]},"#ff5544":{"lch":[60.0491441299879654,138.129067713899872,14.841281480974498],"luv":[60.0491441299879654,133.520956258941027,35.3806951204903228],"rgb":[1,0.333333333333333315,0.266666666666666663],"xyz":[0.45530714216275997,0.281779260281465549,0.0851045964474963501],"hpluv":[14.841281480974498,291.888903616465711,60.0491441299879654],"hsluv":[14.841281480974498,99.9999999999994458,60.0491441299879654]},"#ff5555":{"lch":[60.2631003442631936,132.926854505406169,12.1770506300618191],"luv":[60.2631003442631936,129.936061471805857,28.0386978637829927],"rgb":[1,0.333333333333333315,0.333333333333333315],"xyz":[0.461269556202121955,0.28416422589721041,0.116506643721470265],"hpluv":[12.1770506300618191,279.898508055628838,60.2631003442631936],"hsluv":[12.1770506300618191,99.99999999999946,60.2631003442631936]},"#ff5566":{"lch":[60.5335583680784168,127.091978224389294,8.68145952340772098],"luv":[60.5335583680784168,125.635857711143842,19.1833830742614],"rgb":[1,0.333333333333333315,0.4],"xyz":[0.468854551975630141,0.287198224206613728,0.156454288128614227],"hpluv":[8.68145952340772098,266.416588145649541,60.5335583680784168],"hsluv":[8.68145952340772098,99.9999999999995737,60.5335583680784168]},"#ff5577":{"lch":[60.8630749033481351,121.049870295691591,4.25532383082281918],"luv":[60.8630749033481351,120.716171483201308,8.98203991541407],"rgb":[1,0.333333333333333315,0.466666666666666674],"xyz":[0.478168564995832046,0.290923829414694524,0.205508090035012136],"hpluv":[4.25532383082281918,252.376995060411161,60.8630749033481351],"hsluv":[4.25532383082281918,99.9999999999997158,60.8630749033481351]},"#ff5588":{"lch":[61.2535329118914404,115.319895978664789,358.830706871579594],"luv":[61.2535329118914404,115.295882188952703,-2.35328680810625102],"rgb":[1,0.333333333333333315,0.533333333333333326],"xyz":[0.489308982097775846,0.295379996255472121,0.264180953438584276],"hpluv":[358.830706871579594,238.897951612134108,61.2535329118914404],"hsluv":[358.830706871579594,99.9999999999997868,61.2535329118914404]},"#ff5599":{"lch":[61.7061969251912075,110.472562823261683,352.412124726619879],"luv":[61.7061969251912075,109.505210454219565,-14.5875296097928366],"rgb":[1,0.333333333333333315,0.6],"xyz":[0.502365946206848601,0.300602781899101279,0.332947631079702211],"hpluv":[352.412124726619879,227.177321581811952,61.7061969251912075],"hsluv":[352.412124726619879,99.9999999999998721,61.7061969251912075]},"#ff55aa":{"lch":[62.2217597266614177,107.062368992166355,345.125792918237266],"luv":[62.2217597266614177,103.474894783269619,-27.4826673342684735],"rgb":[1,0.333333333333333315,0.66666666666666663],"xyz":[0.517423639172101102,0.306625859085202401,0.412251480696700812],"hpluv":[345.125792918237266,218.340291577764589,62.2217597266614177],"hsluv":[345.125792918237266,100.000000000000071,62.2217597266614177]},"#ff55bb":{"lch":[62.8003867495987862,105.538663220643826,337.249357740418191],"luv":[62.8003867495987862,97.3274002701012,-40.8140489422941855],"rgb":[1,0.333333333333333315,0.733333333333333282],"xyz":[0.534561231279437776,0.31348089592813716,0.502509465795342614],"hpluv":[337.249357740418191,213.249782655969199,62.8003867495987862],"hsluv":[337.249357740418191,100.000000000000284,62.8003867495987862]},"#ff55cc":{"lch":[63.441761241476712,106.157882562261193,329.184616986090759],"luv":[63.441761241476712,91.1707664760994447,-54.3818661895983837],"rgb":[1,0.333333333333333315,0.8],"xyz":[0.553853606723159531,0.321197846105625961,0.60411597646561277],"hpluv":[329.184616986090759,212.332436268611161,63.441761241476712],"hsluv":[329.184616986090759,100.000000000000441,63.441761241476712]},"#ff55dd":{"lch":[64.1451313698934769,108.938462409011748,321.364198961949114],"luv":[64.1451313698934769,85.0951546537234549,-68.0176686346904518],"rgb":[1,0.333333333333333315,0.866666666666666696],"xyz":[0.575371932273793,0.329805176325879468,0.717445824365618767],"hpluv":[321.364198961949114,215.504760823814451,64.1451313698934769],"hsluv":[321.364198961949114,100.000000000000597,64.1451313698934769]},"#ff55ee":{"lch":[64.9093593252901258,113.686114680552976,314.13939983200612],"luv":[64.9093593252901258,79.1717442713053288,-81.5865649491316418],"rgb":[1,0.333333333333333315,0.933333333333333348],"xyz":[0.599184112539967728,0.339330048432349485,0.842856640434142],"hpluv":[314.13939983200612,222.248801840624651,64.9093593252901258],"hsluv":[314.13939983200612,100.000000000000753,64.9093593252901258]},"#ff55ff":{"lch":[65.7329718140353378,120.074032289562709,307.715012949243885],"luv":[65.7329718140353378,73.4534088756767147,-94.9861566483116633],"rgb":[1,0.333333333333333315,1],"xyz":[0.625355160949799149,0.349798467796282209,0.980690828725923724],"hpluv":[307.715012949243885,231.795582155087629,65.7329718140353378],"hsluv":[307.715012949243885,100.000000000000981,65.7329718140353378]},"#ff6600":{"lch":[62.3097916023938438,138.227046243322206,22.8239093069931798],"luv":[62.3097916023938438,127.404056867086908,53.6183047751569717],"rgb":[1,0.4,0],"xyz":[0.459902430253815608,0.307662267847242543,0.03516802904487909],"hpluv":[22.8239093069931798,281.498480884542573,62.3097916023938438],"hsluv":[22.8239093069931798,100.000000000002359,62.3097916023938438]},"#ff6611":{"lch":[62.344110015411573,137.186959502953613,22.4076195476895244],"luv":[62.344110015411573,126.828705913080029,52.2947532174930245],"rgb":[1,0.4,0.0666666666666666657],"xyz":[0.460914095753452713,0.308066934047097396,0.0404961340096347197],"hpluv":[22.4076195476895244,279.226561167599414,62.344110015411573],"hsluv":[22.4076195476895244,99.9999999999995737,62.344110015411573]},"#ff6622":{"lch":[62.4076477973658257,135.300699513710725,21.6278909170268392],"luv":[62.4076477973658257,125.775148313603225,49.8687412673566115],"rgb":[1,0.4,0.133333333333333331],"xyz":[0.46278945389192977,0.308817077302488197,0.0503730202056139192],"hpluv":[21.6278909170268392,275.106945224361368,62.4076477973658257],"hsluv":[21.6278909170268392,99.9999999999996163,62.4076477973658257]},"#ff6633":{"lch":[62.5120380635233346,132.311574345484274,20.3215228987586443],"luv":[62.5120380635233346,124.076309265494103,45.9502141979128851],"rgb":[1,0.4,0.2],"xyz":[0.465877204624387475,0.310052177595471323,0.0666351740632250555],"hpluv":[20.3215228987586443,268.579898420339646,62.5120380635233346],"hsluv":[20.3215228987586443,99.9999999999995879,62.5120380635233346]},"#ff6644":{"lch":[62.6622654373265675,128.246261163642686,18.3868048135947362],"luv":[62.6622654373265675,121.699120282046835,40.4527826611364603],"rgb":[1,0.4,0.266666666666666663],"xyz":[0.470335199868606391,0.311835375693158889,0.0901139490161117],"hpluv":[18.3868048135947362,259.703586528718,62.6622654373265675],"hsluv":[18.3868048135947362,99.9999999999997726,62.6622654373265675]},"#ff6655":{"lch":[62.8622967709428764,123.257362768531593,15.7125644918265355],"luv":[62.8622967709428764,118.651528823693667,33.3795174388964284],"rgb":[1,0.4,0.333333333333333315],"xyz":[0.476297613907968376,0.31422034130890375,0.121515996290085609],"hpluv":[15.7125644918265355,248.806632458920831,62.8622967709428764],"hsluv":[15.7125644918265355,99.9999999999997158,62.8622967709428764]},"#ff6666":{"lch":[63.1153061541487119,117.623502253606588,12.1770506300618742],"luv":[63.1153061541487119,114.97702760078576,24.8107115273291683],"rgb":[1,0.4,0.4],"xyz":[0.483882609681476561,0.317254339618307069,0.161463640697229571],"hpluv":[12.1770506300618742,236.482353971627703,63.1153061541487119],"hsluv":[12.1770506300618742,99.9999999999999,63.1153061541487119]},"#ff6677":{"lch":[63.4237926928396121,111.744324598031497,7.65713975886231157],"luv":[63.4237926928396121,110.747917341816802,14.8893547314965815],"rgb":[1,0.4,0.466666666666666674],"xyz":[0.493196622701678467,0.320979944826387864,0.21051744260362748],"hpluv":[7.65713975886231157,223.569519019308729,63.4237926928396121],"hsluv":[7.65713975886231157,100.000000000000071,63.4237926928396121]},"#ff6688":{"lch":[63.7896518301749751,106.125321016318935,2.05404070639815961],"luv":[63.7896518301749751,106.057131857198982,3.80375380925921824],"rgb":[1,0.4,0.533333333333333326],"xyz":[0.504337039803622322,0.325436111667165462,0.26919030600719962],"hpluv":[2.05404070639815961,211.109662635719985,63.7896518301749751],"hsluv":[2.05404070639815961,100.000000000000128,63.7896518301749751]},"#ff6699":{"lch":[64.2142253202301276,101.344202045456129,355.341285926877504],"luv":[64.2142253202301276,101.009378125853075,-8.23121004826552394],"rgb":[1,0.4,0.6],"xyz":[0.517394003912695,0.330658897310794619,0.337956983648317555],"hpluv":[355.341285926877504,200.265890662959123,64.2142253202301276],"hsluv":[355.341285926877504,100.000000000000199,64.2142253202301276]},"#ff66aa":{"lch":[64.6983418323177233,97.9876087444390436,347.629516841099075],"luv":[64.6983418323177233,95.7126081968365838,-20.9920961224008664],"rgb":[1,0.4,0.66666666666666663],"xyz":[0.532451696877947578,0.336681974496895742,0.417260833265316156],"hpluv":[347.629516841099075,192.184047560801417,64.6983418323177233],"hsluv":[347.629516841099075,100.000000000000441,64.6983418323177233]},"#ff66bb":{"lch":[65.2423543089962408,96.5541832870936787,339.215698562051898],"luv":[65.2423543089962408,90.270720679111,-34.2623306024503123],"rgb":[1,0.4,0.733333333333333282],"xyz":[0.549589288985284141,0.3435370113398305,0.507518818363957847],"hpluv":[339.215698562051898,187.793604034801348,65.2423543089962408],"hsluv":[339.215698562051898,100.000000000000597,65.2423543089962408]},"#ff66cc":{"lch":[65.8461771980182533,97.3465701370285,330.562118792095362],"luv":[65.8461771980182533,84.7780628468697159,-47.8438583036069218],"rgb":[1,0.4,0.8],"xyz":[0.568881664429005895,0.351253961517319302,0.609125329034228],"hpluv":[330.562118792095362,187.598522894675455,65.8461771980182533],"hsluv":[330.562118792095362,100.000000000000711,65.8461771980182533]},"#ff66dd":{"lch":[66.5093249736543157,100.405273498350255,322.181562409870594],"luv":[66.5093249736543157,79.3159229397089831,-61.5646271368605298],"rgb":[1,0.4,0.866666666666666696],"xyz":[0.590399989979639495,0.359861291737572808,0.722455176934234],"hpluv":[322.181562409870594,191.563741116159406,66.5093249736543157],"hsluv":[322.181562409870594,100.000000000000952,66.5093249736543157]},"#ff66ee":{"lch":[67.2309523334132706,105.527911758853008,314.488878023448478],"luv":[67.2309523334132706,73.9508789773533408,-75.2821868615750702],"rgb":[1,0.4,0.933333333333333348],"xyz":[0.614212170245814204,0.369386163844042825,0.847865993002757223],"hpluv":[314.488878023448478,199.176184031939982,67.2309523334132706],"hsluv":[314.488878023448478,100.000000000001066,67.2309523334132706]},"#ff66ff":{"lch":[68.0098958254125137,112.360313920932768,307.715012949244056],"luv":[68.0098958254125137,68.7346624616611592,-88.8841173702707437],"rgb":[1,0.4,1],"xyz":[0.640383218655645625,0.379854583207975549,0.985700181294539179],"hpluv":[307.715012949244056,209.642901019847784,68.0098958254125137],"hsluv":[307.715012949244056,100.000000000001421,68.0098958254125137]},"#ff7700":{"lch":[65.3236824647912755,127.817378582796977,27.3102887077963814],"luv":[65.3236824647912755,113.570196302134065,58.6437786953806466],"rgb":[1,0.466666666666666674,0],"xyz":[0.478356168307233265,0.344569743954078356,0.0413192750626848],"hpluv":[27.3102887077963814,248.289625700463205,65.3236824647912755],"hsluv":[27.3102887077963814,100.00000000000226,65.3236824647912755]},"#ff7711":{"lch":[65.3555057958206476,126.824695098806032,26.9045059733925385],"luv":[65.3555057958206476,113.097436843789765,57.3887886809793],"rgb":[1,0.466666666666666674,0.0666666666666666657],"xyz":[0.479367833806870369,0.344974410153933209,0.0466473800274404271],"hpluv":[26.9045059733925385,246.24134425049084,65.3555057958206476],"hsluv":[26.9045059733925385,99.9999999999999716,65.3555057958206476]},"#ff7722":{"lch":[65.4144320044565291,125.020679344179442,26.1430666348463],"luv":[65.4144320044565291,112.230643754756542,55.0858681158159058],"rgb":[1,0.466666666666666674,0.133333333333333331],"xyz":[0.481243191945347426,0.345724553409324,0.0565242662234196266],"hpluv":[26.1430666348463,242.520026060215031,65.4144320044565291],"hsluv":[26.1430666348463,100.000000000000156,65.4144320044565291]},"#ff7733":{"lch":[65.511267747206432,122.151716277204869,24.8632302030062533],"luv":[65.511267747206432,110.829965834679456,51.3591322215488688],"rgb":[1,0.466666666666666674,0.2],"xyz":[0.484330942677805132,0.346959653702307136,0.0727864200810307699],"hpluv":[24.8632302030062533,236.604443239770575,65.511267747206432],"hsluv":[24.8632302030062533,100.000000000000128,65.511267747206432]},"#ff7744":{"lch":[65.6506715027637853,118.22870540382597,22.9581907744090898],"luv":[65.6506715027637853,108.863777518532288,46.1162089276663139],"rgb":[1,0.466666666666666674,0.266666666666666663],"xyz":[0.488788937922024047,0.348742851799994702,0.0962651950339174084],"hpluv":[22.9581907744090898,228.519406804146513,65.6506715027637853],"hsluv":[22.9581907744090898,100.000000000000199,65.6506715027637853]},"#ff7755":{"lch":[65.8363783536997857,113.378413750733145,20.3056908730066645],"luv":[65.8363783536997857,106.332452226692425,39.3455754576116092],"rgb":[1,0.466666666666666674,0.333333333333333315],"xyz":[0.494751351961386032,0.351127817415739563,0.12766724230789131],"hpluv":[20.3056908730066645,218.526329281612362,65.8363783536997857],"hsluv":[20.3056908730066645,100.000000000000171,65.8363783536997857]},"#ff7766":{"lch":[66.0714111968285351,107.847817312906827,16.7638759706376135],"luv":[66.0714111968285351,103.264450834403533,31.1063481146080676],"rgb":[1,0.466666666666666674,0.4],"xyz":[0.502336347734894217,0.354161815725142881,0.167614886715035272],"hpluv":[16.7638759706376135,207.127185394700234,66.0714111968285351],"hsluv":[16.7638759706376135,100.000000000000426,66.0714111968285351]},"#ff7777":{"lch":[66.3581913431115851,102.006782949974053,12.1770506300619488],"luv":[66.3581913431115851,99.7116772923406671,21.5166256497442419],"rgb":[1,0.466666666666666674,0.466666666666666674],"xyz":[0.511650360755096067,0.357887420933223677,0.216668688621433181],"hpluv":[12.1770506300619488,195.062523033846361,66.3581913431115851],"hsluv":[12.1770506300619488,100.000000000000355,66.3581913431115851]},"#ff7788":{"lch":[66.6986047917809,96.3441833198397291,6.39999172914420456],"luv":[66.6986047917809,95.7437601312525,10.7393694179903871],"rgb":[1,0.466666666666666674,0.533333333333333326],"xyz":[0.52279077785704,0.362343587774001274,0.275341552025005376],"hpluv":[6.39999172914420456,183.293927388427107,66.6986047917809],"hsluv":[6.39999172914420456,100.000000000000639,66.6986047917809]},"#ff7799":{"lch":[67.0940474565320244,91.4474963932601,359.352586865695173],"luv":[67.0940474565320244,91.4416585161261679,-1.0332881570442134],"rgb":[1,0.466666666666666674,0.6],"xyz":[0.535847741966112623,0.367566373417630432,0.344108229666123255],"hpluv":[359.352586865695173,172.952623850798517,67.0940474565320244],"hsluv":[359.352586865695173,100.000000000000782,67.0940474565320244]},"#ff77aa":{"lch":[67.5454605183692,87.9484746627524,351.107126776790835],"luv":[67.5454605183692,86.8912550098467449,-13.5957345634056814],"rgb":[1,0.466666666666666674,0.66666666666666663],"xyz":[0.550905434931365234,0.373589450603731554,0.423412079283121856],"hpluv":[351.107126776790835,165.223368139110704,67.5454605183692],"hsluv":[351.107126776790835,100.000000000000938,67.5454605183692]},"#ff77bb":{"lch":[68.0533617234635244,86.4195813509952,341.973592157308417],"luv":[68.0533617234635244,82.1775887588569,-26.7429980866295693],"rgb":[1,0.466666666666666674,0.733333333333333282],"xyz":[0.568043027038701798,0.380444487446666313,0.513670064381763658],"hpluv":[341.973592157308417,161.139458954487083,68.0533617234635244],"hsluv":[341.973592157308417,100.000000000001037,68.0533617234635244]},"#ff77cc":{"lch":[68.6178757233526682,87.2373067072756214,332.49967924393593],"luv":[68.6178757233526682,77.3802105983984347,-40.2821385887937],"rgb":[1,0.466666666666666674,0.8],"xyz":[0.587335402482423552,0.388161437624155115,0.615276575052033814],"hpluv":[332.49967924393593,161.325977991170333,68.6178757233526682],"hsluv":[332.49967924393593,100.000000000001265,68.6178757233526682]},"#ff77dd":{"lch":[69.238765020261809,90.480647802514838,323.326201907778],"luv":[69.238765020261809,72.5699004301545756,-54.0403291840472],"rgb":[1,0.466666666666666674,0.866666666666666696],"xyz":[0.608853728033057151,0.396768767844408621,0.728606422952039812],"hpluv":[323.326201907778,165.823361543811586,69.238765020261809],"hsluv":[323.326201907778,100.00000000000145,69.238765020261809]},"#ff77ee":{"lch":[69.9154621504300593,95.9376886025569604,314.973456368277198],"luv":[69.9154621504300593,67.8067552495039791,-67.8696105553513149],"rgb":[1,0.466666666666666674,0.933333333333333348],"xyz":[0.632665908299231861,0.406293639950878638,0.854017239020563],"hpluv":[314.973456368277198,174.122680701596721,69.9154621504300593],"hsluv":[314.973456368277198,100.000000000001535,69.9154621504300593]},"#ff77ff":{"lch":[70.6471031550122,103.213892868752552,307.715012949244283],"luv":[70.6471031550122,63.1394826173239494,-81.6487196221654],"rgb":[1,0.466666666666666674,1],"xyz":[0.658836956709063282,0.416762059314811362,0.99185142731234488],"hpluv":[307.715012949244283,185.388643374650655,70.6471031550122],"hsluv":[307.715012949244283,100.000000000001975,70.6471031550122]},"#ff8800":{"lch":[68.6580440198892603,118.150361410828182,32.8458067740872153],"luv":[68.6580440198892603,99.2620471866307383,64.0823992202883375],"rgb":[1,0.533333333333333326,0],"xyz":[0.500428538032203774,0.388714483404019873,0.0486767316376747472],"hpluv":[32.8458067740872153,218.364961888913399,68.6580440198892603],"hsluv":[32.8458067740872153,100.000000000002245,68.6580440198892603]},"#ff8811":{"lch":[68.6874112197728408,117.19102013872596,32.4606037779481582],"luv":[68.6874112197728408,98.8811760474647485,62.89871401408422],"rgb":[1,0.533333333333333326,0.0666666666666666657],"xyz":[0.501440203531840933,0.389119149603874726,0.0540048366024303769],"hpluv":[32.4606037779481582,216.499308154785638,68.6874112197728408],"hsluv":[32.4606037779481582,100.000000000000739,68.6874112197728408]},"#ff8822":{"lch":[68.7417963707939492,115.443262249268372,31.7362513605757321],"luv":[68.7417963707939492,98.1820090833864754,60.724294076614548],"rgb":[1,0.533333333333333326,0.133333333333333331],"xyz":[0.503315561670317879,0.389869292859265526,0.0638817227984095765],"hpluv":[31.7362513605757321,213.101761826290613,68.7417963707939492],"hsluv":[31.7362513605757321,100.000000000000668,68.7417963707939492]},"#ff8833":{"lch":[68.8311889682804292,112.651741292777714,30.5141745142023382],"luv":[68.8311889682804292,97.0498776480180823,57.1990914683060439],"rgb":[1,0.533333333333333326,0.2],"xyz":[0.506403312402775696,0.391104393152248653,0.0801438766560207128],"hpluv":[30.5141745142023382,207.678703624478942,68.8311889682804292],"hsluv":[30.5141745142023382,100.000000000000824,68.8311889682804292]},"#ff8844":{"lch":[68.9599197258043688,108.808998086617962,28.6842020071901302],"luv":[68.9599197258043688,95.4557990826346554,52.2263198599069725],"rgb":[1,0.533333333333333326,0.266666666666666663],"xyz":[0.510861307646994556,0.392887591249936219,0.103622651608907351],"hpluv":[28.6842020071901302,200.2199693629492,68.9599197258043688],"hsluv":[28.6842020071901302,100.000000000000838,68.9599197258043688]},"#ff8855":{"lch":[69.1314852187197602,104.012526361958052,26.1137258191789492],"luv":[69.1314852187197602,93.395152113439849,45.7815596272609682],"rgb":[1,0.533333333333333326,0.333333333333333315],"xyz":[0.516823721686356485,0.39527255686568108,0.135024698882881267],"hpluv":[26.1137258191789492,190.918970683811096,69.1314852187197602],"hsluv":[26.1137258191789492,100.000000000000867,69.1314852187197602]},"#ff8866":{"lch":[69.3487452092138881,98.4723154092605171,22.6389332988698087],"luv":[69.3487452092138881,90.8849122177262529,37.9042165627661802],"rgb":[1,0.533333333333333326,0.4],"xyz":[0.524408717459864726,0.398306555175084398,0.174972343290025228],"hpluv":[22.6389332988698087,180.183437843423292,69.3487452092138881],"hsluv":[22.6389332988698087,100.000000000001066,69.3487452092138881]},"#ff8877":{"lch":[69.6140261744794344,92.5206854303452246,18.0637242730473773],"luv":[69.6140261744794344,87.9605480867009248,28.6883114314535526],"rgb":[1,0.533333333333333326,0.466666666666666674],"xyz":[0.533722730480066576,0.402032160383165194,0.224026145196423138],"hpluv":[18.0637242730473773,168.648085666048672,69.6140261744794344],"hsluv":[18.0637242730473773,100.000000000001108,69.6140261744794344]},"#ff8888":{"lch":[69.9291829132988596,86.6211090413054,12.1770506300619186],"luv":[69.9291829132988596,84.672173963834382,18.2712749359175248],"rgb":[1,0.533333333333333326,0.533333333333333326],"xyz":[0.544863147582010487,0.406488327223942791,0.282699008599995305],"hpluv":[12.1770506300619186,157.182652238587849,69.9291829132988596],"hsluv":[12.1770506300619186,100.000000000001251,69.9291829132988596]},"#ff8899":{"lch":[70.29563969089034,81.3665448969888274,4.80888772903122597],"luv":[70.29563969089034,81.0801238487794791,6.82115423812421362],"rgb":[1,0.533333333333333326,0.6],"xyz":[0.557920111691083132,0.411711112867571949,0.351465686241113184],"hpluv":[4.80888772903122597,146.878021536398364,70.29563969089034],"hsluv":[4.80888772903122597,100.00000000000135,70.29563969089034]},"#ff88aa":{"lch":[70.7144212664750427,77.4442353273614827,355.944797831634332],"luv":[70.7144212664750427,77.2503443699940533,-5.47666688388778589],"rgb":[1,0.533333333333333326,0.66666666666666663],"xyz":[0.572977804656335743,0.417734190053673071,0.430769535858111841],"hpluv":[355.944797831634332,138.969799542374091,70.7144212664750427],"hsluv":[355.944797831634332,100.000000000001648,70.7144212664750427]},"#ff88bb":{"lch":[71.1861792611668847,75.5334875912487718,345.875835641341098],"luv":[71.1861792611668847,73.2500487000805,-18.431986141845023],"rgb":[1,0.533333333333333326,0.733333333333333282],"xyz":[0.590115396763672306,0.42458922689660783,0.521027520956753532],"hpluv":[345.875835641341098,134.642814203514433,71.1861792611668847],"hsluv":[345.875835641341098,100.000000000001776,71.1861792611668847]},"#ff88cc":{"lch":[71.711216864189268,76.1313589407537563,335.260476444218114],"luv":[71.711216864189268,69.1440007334156377,-31.8604924121286039],"rgb":[1,0.533333333333333326,0.8],"xyz":[0.609407772207394061,0.432306177074096631,0.622634031627023687],"hpluv":[335.260476444218114,134.714956877670915,71.711216864189268],"hsluv":[335.260476444218114,100.000000000001933,71.711216864189268]},"#ff88dd":{"lch":[72.2895135005839649,79.3885839073469413,324.950129439258774],"luv":[72.2895135005839649,64.9916618916399784,-45.5920074067441803],"rgb":[1,0.533333333333333326,0.866666666666666696],"xyz":[0.63092609775802766,0.440913507294350138,0.735963879527029685],"hpluv":[324.950129439258774,139.354847315526115,72.2895135005839649],"hsluv":[324.950129439258774,100.000000000002245,72.2895135005839649]},"#ff88ee":{"lch":[72.9207502525545124,85.0855243828499,315.651995307064169],"luv":[72.9207502525545124,60.845281511842515,-59.4760302748021203],"rgb":[1,0.533333333333333326,0.933333333333333348],"xyz":[0.654738278024202369,0.450438379400820155,0.861374695595552908],"hpluv":[315.651995307064169,148.062090862911901,72.9207502525545124],"hsluv":[315.651995307064169,100.000000000002444,72.9207502525545124]},"#ff88ff":{"lch":[73.6043362991539709,92.7672005781522842,307.715012949244624],"luv":[73.6043362991539709,56.7488822053271349,-73.3847250560567375],"rgb":[1,0.533333333333333326,1],"xyz":[0.68090932643403379,0.460906798764752879,0.999208883887334753],"hpluv":[307.715012949244624,159.930161835956909,73.6043362991539709],"hsluv":[307.715012949244624,100.000000000002771,73.6043362991539709]},"#ff9900":{"lch":[72.2588108283115389,109.907462524380705,39.4434130396340095],"luv":[72.2588108283115389,84.8763034831777077,69.8259509464759418],"rgb":[1,0.6,0],"xyz":[0.526298138484671219,0.440453684308955595,0.057299931788497],"hpluv":[39.4434130396340095,193.008172097547572,72.2588108283115389],"hsluv":[39.4434130396340095,100.000000000002288,72.2588108283115389]},"#ff9911":{"lch":[72.2858317740783889,108.970035258541955,39.0927051304156805],"luv":[72.2858317740783889,84.5745536570817791,68.7139975401902348],"rgb":[1,0.6,0.0666666666666666657],"xyz":[0.527309803984308378,0.440858350508810448,0.0626280367532526389],"hpluv":[39.0927051304156805,191.2904264008464,72.2858317740783889],"hsluv":[39.0927051304156805,100.000000000001506,72.2858317740783889]},"#ff9922":{"lch":[72.3358777005795304,107.257428554778556,38.4317580680427469],"luv":[72.3358777005795304,84.0200042697796,66.6692947517044274],"rgb":[1,0.6,0.133333333333333331],"xyz":[0.529185162122785324,0.441608493764201249,0.0725049229492318315],"hpluv":[38.4317580680427469,188.15378179700545,72.3358777005795304],"hsluv":[38.4317580680427469,100.00000000000145,72.3358777005795304]},"#ff9933":{"lch":[72.418154282067718,104.508625212907305,37.3122251519614778],"luv":[72.418154282067718,83.1203251002183237,63.3487513620113845],"rgb":[1,0.6,0.2],"xyz":[0.532272912855243141,0.442843594057184375,0.0887670768068429816],"hpluv":[37.3122251519614778,183.123470124205028,72.418154282067718],"hsluv":[37.3122251519614778,100.00000000000162,72.418154282067718]},"#ff9944":{"lch":[72.5366731246789556,100.695423976150749,35.6250099256014607],"luv":[72.5366731246789556,81.8499313718691326,58.6528528219818952],"rgb":[1,0.6,0.266666666666666663],"xyz":[0.536730908099462,0.444626792154871942,0.11224585175972962],"hpluv":[35.6250099256014607,176.153561585765658,72.5366731246789556],"hsluv":[35.6250099256014607,100.000000000001748,72.5366731246789556]},"#ff9955":{"lch":[72.6946936633514582,95.8821930466240673,33.2320443565807508],"luv":[72.6946936633514582,80.201421842029859,52.5464259293328269],"rgb":[1,0.6,0.333333333333333315],"xyz":[0.54269332213882393,0.447011757770616802,0.143647899033703508],"hpluv":[33.2320443565807508,167.368827825995851,72.6946936633514582],"hsluv":[33.2320443565807508,100.000000000001705,72.6946936633514582]},"#ff9966":{"lch":[72.8949069034106,90.2347392462793749,29.9516480142673025],"luv":[72.8949069034106,78.1836232693519406,45.0514064077924345],"rgb":[1,0.6,0.4],"xyz":[0.550278317912332171,0.450045756080020121,0.183595543440847497],"hpluv":[29.9516480142673025,157.078197331028889,72.8949069034106],"hsluv":[29.9516480142673025,100.000000000001819,72.8949069034106]},"#ff9977":{"lch":[73.1395321193821,84.0351966301436,25.5464816978182121],"luv":[73.1395321193821,75.8195571045914,36.2396058633438045],"rgb":[1,0.6,0.466666666666666674],"xyz":[0.559592330932534,0.453771361288100916,0.232649345347245406],"hpluv":[25.5464816978182121,145.796926659053128,73.1395321193821],"hsluv":[25.5464816978182121,100.000000000002018,73.1395321193821]},"#ff9988":{"lch":[73.430374185650777,77.702838567593929,19.7240568661095921],"luv":[73.430374185650777,73.1439295084068419,26.2239718107454713],"rgb":[1,0.6,0.533333333333333326],"xyz":[0.570732748034477932,0.458227528128878514,0.291322208750817546],"hpluv":[19.7240568661095921,134.276641294628575,73.430374185650777],"hsluv":[19.7240568661095921,100.000000000002203,73.430374185650777]},"#ff9999":{"lch":[73.76886125649402,71.8160022700114098,12.1770506300620251],"luv":[73.76886125649402,70.2001752793754,15.1483851545719261],"rgb":[1,0.6,0.6],"xyz":[0.583789712143550577,0.463450313772507672,0.360088886391935481],"hpluv":[12.1770506300620251,123.534275619879125,73.76886125649402],"hsluv":[12.1770506300620251,100.000000000002331,73.76886125649402]},"#ff99aa":{"lch":[74.1560723225582592,67.1124973440613,2.7130535693684088],"luv":[74.1560723225582592,67.0372720786985923,3.17670458229478481],"rgb":[1,0.6,0.66666666666666663],"xyz":[0.598847405108803188,0.469473390958608794,0.439392736008934082],"hpluv":[2.7130535693684088,114.840746523486033,74.1560723225582592],"hsluv":[2.7130535693684088,100.00000000000253,74.1560723225582592]},"#ff99bb":{"lch":[74.5927597146433925,64.4136927281220864,351.502648062184],"luv":[74.5927597146433925,63.7066038913843187,-9.51800564715024322],"rgb":[1,0.6,0.733333333333333282],"xyz":[0.615984997216139751,0.476328427801543552,0.529650721107575828],"hpluv":[351.502648062184,109.577363966618833,74.5927597146433925],"hsluv":[351.502648062184,100.000000000002615,74.5927597146433925]},"#ff99cc":{"lch":[75.0793694015197,64.4152606478183145,339.305696269483292],"luv":[75.0793694015197,60.259134450288677,-22.7631834247409977],"rgb":[1,0.6,0.8],"xyz":[0.635277372659861506,0.484045377979032354,0.631257231777846],"hpluv":[339.305696269483292,108.869813431806975,75.0793694015197],"hsluv":[339.305696269483292,100.000000000002871,75.0793694015197]},"#ff99dd":{"lch":[75.6160606971696296,67.4118390527965232,327.324068761847229],"luv":[75.6160606971696296,56.7430830794555,-36.3947601601954887],"rgb":[1,0.6,0.866666666666666696],"xyz":[0.656795698210495105,0.49265270819928586,0.744587079677852],"hpluv":[327.324068761847229,113.125745227459021,75.6160606971696296],"hsluv":[327.324068761847229,100.00000000000324,75.6160606971696296]},"#ff99ee":{"lch":[76.202726253448489,73.1905233351813393,316.627151984536795],"luv":[76.202726253448489,53.2022056350908201,-50.2630880631028845],"rgb":[1,0.6,0.933333333333333348],"xyz":[0.680607878476669814,0.502177580305755877,0.869997895746375205],"hpluv":[316.627151984536795,123.107716827744753,76.202726253448489],"hsluv":[316.627151984536795,100.000000000003524,76.202726253448489]},"#ff99ff":{"lch":[76.8390127436129,81.2030526869262275,307.715012949245],"luv":[76.8390127436129,49.6746958291708154,-64.2367524082209229],"rgb":[1,0.6,1],"xyz":[0.706778926886501235,0.512645999669688601,1.00783208403815694],"hpluv":[307.715012949245,141.150312559224801,76.8390127436129],"hsluv":[307.715012949245,100.000000000003752,76.8390127436129]},"#ee0000":{"lch":[49.7142799595632,167.190689697178925,12.1770506300617765],"luv":[49.7142799595632,163.428976145092918,35.2660811203203934],"rgb":[0.933333333333333348,0,0],"xyz":[0.352591085030832,0.181804778219026603,0.0165277071108199],"hpluv":[12.1770506300617765,426.746789183125202,49.7142799595632],"hsluv":[12.1770506300617765,100.000000000002217,49.7142799595632]},"#ee0011":{"lch":[49.7630000621001756,165.722449822455,11.6881730851639158],"luv":[49.7630000621001756,162.286136628676445,33.5729092170260728],"rgb":[0.933333333333333348,0,0.0666666666666666657],"xyz":[0.353602750530469079,0.182209444418881455,0.0218558120755755342],"hpluv":[11.6881730851639158,422.585038037937124,49.7630000621001756],"hsluv":[11.6881730851639158,99.9999999999963762,49.7630000621001756]},"#ee0022":{"lch":[49.8531236873270558,163.0858535413212,10.7756858750078184],"luv":[49.8531236873270558,160.210108449799208,30.4912573667411486],"rgb":[0.933333333333333348,0,0.133333333333333331],"xyz":[0.355478108668946136,0.182959587674272284,0.0317326982715547268],"hpluv":[10.7756858750078184,415.110044299310516,49.8531236873270558],"hsluv":[10.7756858750078184,99.9999999999964473,49.8531236873270558]},"#ee0033":{"lch":[50.000975779064234,158.977402767524836,9.25647316775448559],"luv":[50.000975779064234,156.907230803998146,25.5721628363475],"rgb":[0.933333333333333348,0,0.2],"xyz":[0.358565859401403841,0.184194687967255383,0.047994852129165877],"hpluv":[9.25647316775448559,403.456061197389261,50.000975779064234],"hsluv":[9.25647316775448559,99.9999999999965183,50.000975779064234]},"#ee0044":{"lch":[50.2132784041556164,153.529579514286212,7.02933300215353],"luv":[50.2132784041556164,152.375594335021788,18.7885613308314312],"rgb":[0.933333333333333348,0,0.266666666666666663],"xyz":[0.363023854645622757,0.185977886064942977,0.0714736270820525155],"hpluv":[7.02933300215353,387.983100931209492,50.2132784041556164],"hsluv":[7.02933300215353,99.9999999999966462,50.2132784041556164]},"#ee0055":{"lch":[50.4951150037793326,147.071833727726926,3.99754465361350508],"luv":[50.4951150037793326,146.714013644902735,10.2529252527975352],"rgb":[0.933333333333333348,0,0.333333333333333315],"xyz":[0.368986268684984742,0.188362851680687809,0.102875674356026417],"hpluv":[3.99754465361350508,369.589367027053072,50.4951150037793326],"hsluv":[3.99754465361350508,99.999999999996831,50.4951150037793326]},"#ee0066":{"lch":[50.8502318550204109,140.098840030056522,0.0757634158231174915],"luv":[50.8502318550204109,140.0987175463531,0.185255592479522613],"rgb":[0.933333333333333348,0,0.4],"xyz":[0.376571264458492927,0.1913968499900911,0.142823318763170393],"hpluv":[0.0757634158231174915,349.60765112382461,50.8502318550204109],"hsluv":[0.0757634158231174915,99.9999999999970584,50.8502318550204109]},"#ee0077":{"lch":[51.2812017254514956,133.219993530026585,355.209470699020642],"luv":[51.2812017254514956,132.754613083251769,-11.1256182415379214],"rgb":[0.933333333333333348,0,0.466666666666666674],"xyz":[0.385885277478694833,0.195122455198171924,0.191877120669568302],"hpluv":[355.209470699020642,329.648072606093592,51.2812017254514956],"hsluv":[355.209470699020642,99.9999999999973284,51.2812017254514956]},"#ee0088":{"lch":[51.7895361854883163,127.090944021268115,349.407446028193533],"luv":[51.7895361854883163,124.925218603260163,-23.3623160055840167],"rgb":[0.933333333333333348,0,0.533333333333333326],"xyz":[0.397025694580638633,0.199578622038949521,0.250549984073140442],"hpluv":[349.407446028193533,311.395197619459395,51.7895361854883163],"hsluv":[349.407446028193533,99.9999999999974705,51.7895361854883163]},"#ee0099":{"lch":[52.3757812732210652,122.329563392952366,342.780178840499048],"luv":[52.3757812732210652,116.846263825073436,-36.2142611415956637],"rgb":[0.933333333333333348,0,0.6],"xyz":[0.410082658689711388,0.204801407682578679,0.319316661714258376],"hpluv":[342.780178840499048,296.374093031221,52.3757812732210652],"hsluv":[342.780178840499048,99.9999999999978,52.3757812732210652]},"#ee00aa":{"lch":[53.0396114453995722,119.424239873739239,335.563712743666827],"luv":[53.0396114453995722,108.726436909364494,-49.403552366347],"rgb":[0.933333333333333348,0,0.66666666666666663],"xyz":[0.425140351654963888,0.210824484868679773,0.398620511331257],"hpluv":[335.563712743666827,285.713971708863653,53.0396114453995722],"hsluv":[335.563712743666827,99.999999999998,53.0396114453995722]},"#ee00bb":{"lch":[53.779927529436435,118.655378520732356,328.101249142938343],"luv":[53.779927529436435,100.736423933687789,-62.6998544252744381],"rgb":[0.933333333333333348,0,0.733333333333333282],"xyz":[0.442277943762300563,0.217679521711614532,0.488878496429898723],"hpluv":[328.101249142938343,279.966806180862307,53.779927529436435],"hsluv":[328.101249142938343,99.9999999999982094,53.779927529436435]},"#ee00cc":{"lch":[54.5949595671901,120.061129768120921,320.773339602207614],"luv":[54.5949595671901,93.0053918954961,-75.9254368414351575],"rgb":[0.933333333333333348,0,0.8],"xyz":[0.461570319206022317,0.225396471889103334,0.590485007100168824],"hpluv":[320.773339602207614,279.054611328209262,54.5949595671901],"hsluv":[320.773339602207614,99.9999999999984,54.5949595671901]},"#ee00dd":{"lch":[55.4823728661035744,123.466264594666441,313.907226483092529],"luv":[55.4823728661035744,85.6229535549924634,-88.9529556421808252],"rgb":[0.933333333333333348,0,0.866666666666666696],"xyz":[0.483088644756655805,0.234003802109356868,0.703814855000174822],"hpluv":[313.907226483092529,282.379138449157608,55.4823728661035744],"hsluv":[313.907226483092529,99.9999999999986215,55.4823728661035744]},"#ee00ee":{"lch":[56.4393743497109597,128.559742977308588,307.715012949243601],"luv":[56.4393743497109597,78.644409501394918,-101.698890694877051],"rgb":[0.933333333333333348,0,0.933333333333333348],"xyz":[0.506900825022830626,0.243528674215826912,0.829225671068698],"hpluv":[307.715012949243601,289.042783730483393,56.4393743497109597],"hsluv":[307.715012949243601,99.9999999999988489,56.4393743497109597]},"#ee00ff":{"lch":[57.4628159598150745,134.982567880189606,302.284502363601803],"luv":[57.4628159598150745,72.0973885084188879,-114.115118199983058],"rgb":[0.933333333333333348,0,1],"xyz":[0.533071873432661936,0.253997093579759636,0.96705985936047989],"hpluv":[302.284502363601803,298.078126285043766,57.4628159598150745],"hsluv":[302.284502363601803,99.9999999999989484,57.4628159598150745]},"#ee1100":{"lch":[50.1937733395544683,164.746074066243921,12.6667024036514828],"luv":[50.1937733395544683,160.736507742479517,36.1253927174799117],"rgb":[0.933333333333333348,0.0666666666666666657,0],"xyz":[0.354595485291760382,0.185813578740883473,0.0171958405311293527],"hpluv":[12.6667024036514828,416.489977947977081,50.1937733395544683],"hsluv":[12.6667024036514828,100.000000000002245,50.1937733395544683]},"#ee1111":{"lch":[50.2417909300708345,163.305921695383518,12.1770506300617907],"luv":[50.2417909300708345,159.631613634988071,34.4466542507197317],"rgb":[0.933333333333333348,0.0666666666666666657,0.0666666666666666657],"xyz":[0.355607150791397486,0.186218244940738326,0.0225239454958849825],"hpluv":[12.1770506300617907,412.454596338970589,50.2417909300708345],"hsluv":[12.1770506300617907,96.6508962208003197,50.2417909300708345]},"#ee1122":{"lch":[50.3306190654122219,160.718934991358793,11.2629010575952293],"luv":[50.3306190654122219,157.623701713525833,31.390201064696047],"rgb":[0.933333333333333348,0.0666666666666666657,0.133333333333333331],"xyz":[0.357482508929874543,0.186968388196129154,0.032400831691864182],"hpluv":[11.2629010575952293,405.204351077732667,50.3306190654122219],"hsluv":[11.2629010575952293,96.6948337079543592,50.3306190654122219]},"#ee1133":{"lch":[50.4763571232054318,156.685700791802191,9.74029685215880647],"luv":[50.4763571232054318,154.427033260746356,26.5084935615454427],"rgb":[0.933333333333333348,0.0666666666666666657,0.2],"xyz":[0.360570259662332249,0.188203488489112253,0.0486629855494753252],"hpluv":[9.74029685215880647,393.895198182016713,50.4763571232054318],"hsluv":[9.74029685215880647,96.7647175585846,50.4763571232054318]},"#ee1144":{"lch":[50.6856484752898382,151.333831494518165,7.50679337730589413],"luv":[50.6856484752898382,150.036806479185287,19.7708183022028514],"rgb":[0.933333333333333348,0.0666666666666666657,0.266666666666666663],"xyz":[0.365028254906551164,0.189986686586799847,0.0721417605023619568],"hpluv":[7.50679337730589413,378.870112575267399,50.6856484752898382],"hsluv":[7.50679337730589413,96.8605546889772455,50.6856484752898382]},"#ee1155":{"lch":[50.9635312364098496,144.984673036864649,4.46374659640210858],"luv":[50.9635312364098496,144.544902405536135,11.283909082431693],"rgb":[0.933333333333333348,0.0666666666666666657,0.333333333333333315],"xyz":[0.370990668945913149,0.19237165220254468,0.103543807776335872],"hpluv":[4.46374659640210858,360.995599227985224,50.9635312364098496],"hsluv":[4.46374659640210858,96.9801964566503,50.9635312364098496]},"#ee1166":{"lch":[51.3137360134299598,138.123816003378664,0.523151936541392],"luv":[51.3137360134299598,138.118058344046318,1.26115288756691357],"rgb":[0.933333333333333348,0.0666666666666666657,0.4],"xyz":[0.378575664719421334,0.19540565051194797,0.143491452183479834],"hpluv":[0.523151936541392,341.565705345826359,51.3137360134299598],"hsluv":[0.523151936541392,97.1198273857598764,51.3137360134299598]},"#ee1177":{"lch":[51.7388469835676119,131.353183798903302,355.627348241097309],"luv":[51.7388469835676119,130.970848560864681,-10.014775152519368],"rgb":[0.933333333333333348,0.0666666666666666657,0.466666666666666674],"xyz":[0.38788967773962324,0.199131255720028794,0.192545254089877743],"hpluv":[355.627348241097309,322.153744971780554,51.7388469835676119],"hsluv":[355.627348241097309,97.2745732157476,51.7388469835676119]},"#ee1188":{"lch":[52.2404115410600411,125.324707533591734,349.782339698165117],"luv":[52.2404115410600411,123.337180279157977,-22.2311106147846438],"rgb":[0.933333333333333348,0.0666666666666666657,0.533333333333333326],"xyz":[0.39903009484156704,0.203587422560806391,0.251218117493449911],"hpluv":[349.782339698165117,304.417375015566734,52.2404115410600411],"hsluv":[349.782339698165117,97.4391430985313605,52.2404115410600411]},"#ee1199":{"lch":[52.819032808459994,120.657103381490217,343.097768544337384],"luv":[52.819032808459994,115.44498939014953,-35.079780802050081],"rgb":[0.933333333333333348,0.0666666666666666657,0.6],"xyz":[0.412087058950639795,0.208810208204435549,0.31998479513456779],"hpluv":[343.097768544337384,289.869003225703352,52.819032808459994],"hsluv":[343.097768544337384,97.6083995478766298,52.819032808459994]},"#ee11aa":{"lch":[53.4744599034404615,117.843047501566133,335.812437212199143],"luv":[53.4744599034404615,107.497497568615216,-48.2832461723726496],"rgb":[0.933333333333333348,0.0666666666666666657,0.66666666666666663],"xyz":[0.427144751915892296,0.214833285390536644,0.399288644751566446],"hpluv":[335.812437212199143,279.638449078323276,53.4744599034404615],"hsluv":[335.812437212199143,97.7777799659692,53.4744599034404615]},"#ee11bb":{"lch":[54.205681814132376,117.167943285093472,328.276238054291071],"luv":[54.205681814132376,99.6622453247977376,-61.6097702517929662],"rgb":[0.933333333333333348,0.0666666666666666657,0.733333333333333282],"xyz":[0.44428234402322897,0.221688322233471402,0.489546629850208137],"hpluv":[328.276238054291071,274.285798241860448,54.205681814132376],"hsluv":[328.276238054291071,97.9435422525978652,54.205681814132376]},"#ee11cc":{"lch":[55.0110259993956703,118.672848043699901,320.878255441103249],"luv":[55.0110259993956703,92.0672263164348266,-74.8790404666182781],"rgb":[0.933333333333333348,0.0666666666666666657,0.8],"xyz":[0.463574719466950724,0.229405272410960204,0.591153140520478293],"hpluv":[320.878255441103249,273.741691636115945,55.0110259993956703],"hsluv":[320.878255441103249,98.102849778989,55.0110259993956703]},"#ee11dd":{"lch":[55.8882602794840864,122.182226882396691,313.952719233652829],"luv":[55.8882602794840864,84.8023500190691522,-87.9605479586430334],"rgb":[0.933333333333333348,0.0666666666666666657,0.866666666666666696],"xyz":[0.485093045017584212,0.238012602631213738,0.704482988420484291],"hpluv":[313.952719233652829,277.412976370396279,55.8882602794840864],"hsluv":[313.952719233652829,98.2537358693348608,55.8882602794840864]},"#ee11ee":{"lch":[56.83469533821048,127.382376320214306,307.715012949243601],"luv":[56.83469533821048,77.9241738866570302,-100.767519176899128],"rgb":[0.933333333333333348,0.0666666666666666657,0.933333333333333348],"xyz":[0.508905225283758922,0.247537474737683783,0.829893804489007514],"hpluv":[307.715012949243601,284.403630900032795,56.83469533821048],"hsluv":[307.715012949243601,98.3949944120453495,56.83469533821048]},"#ee11ff":{"lch":[57.8472847680859275,133.910906422249354,302.2526850652647],"luv":[57.8472847680859275,71.4621107907268254,-113.248830369952643],"rgb":[0.933333333333333348,0.0666666666666666657,1],"xyz":[0.535076273693590343,0.258005894101616451,0.967727992780789359],"hpluv":[302.2526850652647,293.746227206253536,57.8472847680859275],"hsluv":[302.2526850652647,99.99999999999892,57.8472847680859275]},"#ee2200":{"lch":[51.0646940471157222,160.407609402057773,13.5847947923325787],"luv":[51.0646940471157222,155.919944837816502,37.677207378671163],"rgb":[0.933333333333333348,0.133333333333333331,0],"xyz":[0.358311109026528296,0.19324482621041944,0.018434381776051962],"hpluv":[13.5847947923325787,398.605749597291435,51.0646940471157222],"hsluv":[13.5847947923325787,100.000000000002203,51.0646940471157222]},"#ee2211":{"lch":[51.1114738997186322,159.015005648229618,13.0939108674416342],"luv":[51.1114738997186322,154.880623331235768,36.0244991337056817],"rgb":[0.933333333333333348,0.133333333333333331,0.0666666666666666657],"xyz":[0.3593227745261654,0.193649492410274293,0.0237624867408075952],"hpluv":[13.0939108674416342,394.783534202752207,51.1114738997186322],"hsluv":[13.0939108674416342,96.7702863870018462,51.1114738997186322]},"#ee2222":{"lch":[51.1980191888258105,156.511980987808,12.1770506300618031],"luv":[51.1980191888258105,152.990533465747774,33.0135860305098348],"rgb":[0.933333333333333348,0.133333333333333331,0.133333333333333331],"xyz":[0.361198132664642457,0.194399635665665121,0.0336393729367867877],"hpluv":[12.1770506300618031,387.912483642854795,51.1980191888258105],"hsluv":[12.1770506300618031,90.899918517349,51.1980191888258105]},"#ee2233":{"lch":[51.340031013958,152.605977320930094,10.6487510890373542],"luv":[51.340031013958,149.977869701108148,28.1996970549978769],"rgb":[0.933333333333333348,0.133333333333333331,0.2],"xyz":[0.364285883397100163,0.19563473595864822,0.0499015267943979379],"hpluv":[10.6487510890373542,377.185287566809563,51.340031013958],"hsluv":[10.6487510890373542,91.0856949770771,51.340031013958]},"#ee2244":{"lch":[51.5440125284501391,147.416232714385046,8.4042516634418849],"luv":[51.5440125284501391,145.833202035739902,21.5458314229178391],"rgb":[0.933333333333333348,0.133333333333333331,0.266666666666666663],"xyz":[0.368743878641319078,0.197417934056335814,0.0733803017472845764],"hpluv":[8.4042516634418849,362.916246958463432,51.5440125284501391],"hsluv":[8.4042516634418849,91.3409150161676848,51.5440125284501391]},"#ee2255":{"lch":[51.8149196409757,141.25016266814734,5.34127242035781613],"luv":[51.8149196409757,140.636840571385164,13.1486701942394504],"rgb":[0.933333333333333348,0.133333333333333331,0.333333333333333315],"xyz":[0.374706292680681063,0.199802899672080647,0.104782349021258478],"hpluv":[5.34127242035781613,345.918233291596209,51.8149196409757],"hsluv":[5.34127242035781613,91.6602615743055082,51.8149196409757]},"#ee2266":{"lch":[52.1564522427987924,134.577740656965489,1.36671179444129165],"luv":[52.1564522427987924,134.539455426625494,3.20986196595936],"rgb":[0.933333333333333348,0.133333333333333331,0.4],"xyz":[0.382291288454189249,0.202836897981483938,0.144729993428402454],"hpluv":[1.36671179444129165,327.419481975304109,52.1564522427987924],"hsluv":[1.36671179444129165,92.0339967122243365,52.1564522427987924]},"#ee2277":{"lch":[52.5712108639856694,127.988129961564283,356.416786702014292],"luv":[52.5712108639856694,127.737923683219179,-7.99901644943552803],"rgb":[0.933333333333333348,0.133333333333333331,0.466666666666666674],"xyz":[0.391605301474391154,0.206562503189564761,0.193783795334800363],"hpluv":[356.416786702014292,308.930679724572485,52.5712108639856694],"hsluv":[356.416786702014292,92.4494947830095,52.5712108639856694]},"#ee2288":{"lch":[53.0608018273771194,122.127216672078461,350.491948161024197],"luv":[53.0608018273771194,120.449481512132621,-20.1737318195523763],"rgb":[0.933333333333333348,0.133333333333333331,0.533333333333333326],"xyz":[0.402745718576334955,0.211018670030342359,0.25245665873837253],"hpluv":[350.491948161024197,292.063965437042782,53.0608018273771194],"hsluv":[350.491948161024197,92.892885362452958,53.0608018273771194]},"#ee2299":{"lch":[53.6259244704506557,117.615935516390621,343.699890485995525],"luv":[53.6259244704506557,112.888334232708232,-33.0110933105844282],"rgb":[0.933333333333333348,0.133333333333333331,0.6],"xyz":[0.415802682685407654,0.216241455673971517,0.32122333637949041],"hpluv":[343.699890485995525,278.311211390643507,53.6259244704506557],"hsluv":[343.699890485995525,93.3505397228777412,53.6259244704506557]},"#ee22aa":{"lch":[54.266455218013121,114.955451111725907,336.284451026203612],"luv":[54.266455218013121,105.247863162980167,-46.2346519390706305],"rgb":[0.933333333333333348,0.133333333333333331,0.66666666666666663],"xyz":[0.43086037565066021,0.222264532860072611,0.400527185996489],"hpluv":[336.284451026203612,268.805062052359688,54.266455218013121],"hsluv":[336.284451026203612,93.8102001508292,54.266455218013121]},"#ee22bb":{"lch":[54.981534566577821,114.440827463928798,328.608334651454868],"luv":[54.981534566577821,97.6897319326175,-59.6105633722921],"rgb":[0.933333333333333348,0.133333333333333331,0.733333333333333282],"xyz":[0.447997967757996884,0.22911956970300737,0.490785171095130757],"hpluv":[328.608334651454868,264.121319791368,54.981534566577821],"hsluv":[328.608334651454868,94.2616688954678636,54.981534566577821]},"#ee22cc":{"lch":[55.7696584616915629,116.118636638570607,321.077185717181408],"luv":[55.7696584616915629,90.3394913251054419,-72.9541916679335856],"rgb":[0.933333333333333348,0.133333333333333331,0.8],"xyz":[0.467290343201718583,0.236836519880496171,0.592391681765400913],"hpluv":[321.077185717181408,264.206361207665168,55.7696584616915629],"hsluv":[321.077185717181408,94.6970823725699518,55.7696584616915629]},"#ee22dd":{"lch":[56.6287730491083749,119.812596042269817,314.038835862099],"luv":[56.6287730491083749,83.2872216309056,-86.1295354880806201],"rgb":[0.933333333333333348,0.133333333333333331,0.866666666666666696],"xyz":[0.488808668752352182,0.245443850100749705,0.70572152966540691],"hpluv":[314.038835862099,268.475495638829329,56.6287730491083749],"hsluv":[314.038835862099,95.1108639535381,56.6287730491083749]},"#ee22ee":{"lch":[57.5563705104872128,125.203701850491953,307.715012949243658],"luv":[57.5563705104872128,76.5914038981754,-99.0440498261932163],"rgb":[0.933333333333333348,0.133333333333333331,0.933333333333333348],"xyz":[0.512620849018526892,0.254968722207219722,0.831132345733930133],"hpluv":[307.715012949243658,276.03432908057755,57.5563705104872128],"hsluv":[307.715012949243658,95.4994708803944263,57.5563705104872128]},"#ee22ff":{"lch":[58.5495832280214046,131.922896299071255,302.192710378625122],"luv":[58.5495832280214046,70.2843784028787582,-111.641196341030223],"rgb":[0.933333333333333348,0.133333333333333331,1],"xyz":[0.538791897428358313,0.265437141571152446,0.968966534025712],"hpluv":[302.192710378625122,285.914180736870946,58.5495832280214046],"hsluv":[302.192710378625122,99.9999999999989,58.5495832280214046]},"#ee3300":{"lch":[52.4512471844783761,153.77210005382733,15.1254552240259841],"luv":[52.4512471844783761,148.444942331914945,40.1242800687903269],"rgb":[0.933333333333333348,0.2,0],"xyz":[0.364428831115539142,0.205480270388441244,0.0204736224723888437],"hpluv":[15.1254552240259841,372.015515114283232,52.4512471844783761],"hsluv":[15.1254552240259841,100.000000000002174,52.4512471844783761]},"#ee3311":{"lch":[52.4961529429458693,152.446596739109111,14.6331501802662043],"luv":[52.4961529429458693,147.501711829562538,38.5124637576621893],"rgb":[0.933333333333333348,0.2,0.0666666666666666657],"xyz":[0.365440496615176247,0.205884936588296097,0.0258017274371444769],"hpluv":[14.6331501802662043,368.493287978140756,52.4961529429458693],"hsluv":[14.6331501802662043,96.9493433827183395,52.4961529429458693]},"#ee3322":{"lch":[52.5792408568970302,150.061966488521733,13.7129404445972121],"luv":[52.5792408568970302,145.784540850223095,35.5733247742160685],"rgb":[0.933333333333333348,0.2,0.133333333333333331],"xyz":[0.367315854753653304,0.206635079843686925,0.0356786136331236695],"hpluv":[13.7129404445972121,362.155969729293304,52.5792408568970302],"hsluv":[13.7129404445972121,91.3983957113456,52.5792408568970302]},"#ee3333":{"lch":[52.7156069212027916,146.335083442311,12.177050630061796],"luv":[52.7156069212027916,143.042611430097821,30.8669396171076365],"rgb":[0.933333333333333348,0.2,0.2],"xyz":[0.370403605486111,0.207870180136670024,0.0519407674907348127],"hpluv":[12.177050630061796,352.248031751653059,52.7156069212027916],"hsluv":[12.177050630061796,82.5426319963487316,52.7156069212027916]},"#ee3344":{"lch":[52.9115382124740705,141.372894204534333,9.91688783885485314],"luv":[52.9115382124740705,139.260586308771792,24.3471623953096028],"rgb":[0.933333333333333348,0.2,0.266666666666666663],"xyz":[0.374861600730329925,0.209653378234357618,0.0754195424436214512],"hpluv":[9.91688783885485314,339.043238938553714,52.9115382124740705],"hsluv":[9.91688783885485314,83.0163224279527867,52.9115382124740705]},"#ee3355":{"lch":[53.1718605143623222,135.462446214194244,6.82400118051175664],"luv":[53.1718605143623222,134.502806079917804,16.0956357737583851],"rgb":[0.933333333333333348,0.2,0.333333333333333315],"xyz":[0.38082401476969191,0.212038343850102451,0.106821589717595367],"hpluv":[6.82400118051175664,323.278173463789,53.1718605143623222],"hsluv":[6.82400118051175664,83.6110915378108,53.1718605143623222]},"#ee3366":{"lch":[53.5002196972096158,129.050901787066266,2.79642975700151464],"luv":[53.5002196972096158,128.89722530875369,6.29607494868134765],"rgb":[0.933333333333333348,0.2,0.4],"xyz":[0.388409010543200095,0.215072342159505742,0.146769234124739328],"hpluv":[2.79642975700151464,306.086943567777439,53.5002196972096158],"hsluv":[2.79642975700151464,84.310080928774866,53.5002196972096158]},"#ee3377":{"lch":[53.8992319384372252,122.709031404530748,357.759441587930837],"luv":[53.8992319384372252,122.615219389586755,-4.79732866099687261],"rgb":[0.933333333333333348,0.2,0.466666666666666674],"xyz":[0.397723023563402,0.218797947367586565,0.195823036031137238],"hpluv":[357.759441587930837,288.89051161323988,53.8992319384372252],"hsluv":[357.759441587930837,85.0909052409050872,53.8992319384372252]},"#ee3388":{"lch":[54.3705825415329,117.074862235921088,351.703100554939169],"luv":[54.3705825415329,115.849509832295894,-16.8942131860788436],"rgb":[0.933333333333333348,0.2,0.533333333333333326],"xyz":[0.408863440665345801,0.223254114208364163,0.254495899434709405],"hpluv":[351.703100554939169,273.2366774905733,54.3705825415329],"hsluv":[351.703100554939169,85.9285066952877,54.3705825415329]},"#ee3399":{"lch":[54.9151057717267292,112.774715889061866,344.730687431692274],"luv":[54.9151057717267292,108.793611621096844,-29.6999430015709507],"rgb":[0.933333333333333348,0.2,0.6],"xyz":[0.4219204047744185,0.22847689985199332,0.323262577075827284],"hpluv":[344.730687431692274,260.590898768244074,54.9151057717267292],"hsluv":[344.730687431692274,86.7978129198036896,54.9151057717267292]},"#ee33aa":{"lch":[55.5328602544255,110.325325240584178,337.093995035693695],"luv":[55.5328602544255,101.625579555338021,-42.9408776049389047],"rgb":[0.933333333333333348,0.2,0.66666666666666663],"xyz":[0.436978097739671056,0.234499977038094415,0.402566426692825885],"hpluv":[337.093995035693695,252.095155701888757,55.5328602544255],"hsluv":[337.093995035693695,87.6758366884225779,55.5328602544255]},"#ee33bb":{"lch":[56.2232062298057826,110.03895679054483,329.178007243031175],"luv":[56.2232062298057826,94.4974163460242664,-56.3809392922602726],"rgb":[0.933333333333333348,0.2,0.733333333333333282],"xyz":[0.454115689847007731,0.241355013881029173,0.492824411791467631],"hpluv":[329.178007243031175,248.353441541401367,56.2232062298057826],"hsluv":[329.178007243031175,88.5430404155392665,56.2232062298057826]},"#ee33cc":{"lch":[56.9848866198670123,111.971807156825847,321.417898872172259],"luv":[56.9848866198670123,87.5300782585605788,-69.8295854062993726],"rgb":[0.933333333333333348,0.2,0.8],"xyz":[0.47340806529072943,0.249071964058517975,0.594430922461737787],"hpluv":[321.417898872172259,249.337916143678171,56.9848866198670123],"hsluv":[321.417898872172259,89.3839737673679764,56.9848866198670123]},"#ee33dd":{"lch":[57.8161114567543848,115.945977330727956,314.185904182223908],"luv":[57.8161114567543848,80.813036999634221,-83.1427850752753557],"rgb":[0.933333333333333348,0.2,0.866666666666666696],"xyz":[0.494926390841363029,0.257679294278771509,0.707760770361743785],"hpluv":[314.185904182223908,254.475591939392586,57.8161114567543848],"hsluv":[314.185904182223908,90.1873197259471482,57.8161114567543848]},"#ee33ee":{"lch":[58.7146439354817886,121.632779311923699,307.715012949243715],"luv":[58.7146439354817886,74.4069479563921448,-96.2192241652253415],"rgb":[0.933333333333333348,0.2,0.933333333333333348],"xyz":[0.518738571107537738,0.267204166385241526,0.833171586430267],"hpluv":[307.715012949243715,262.87151341613469,58.7146439354817886],"hsluv":[307.715012949243715,90.9455375510239747,58.7146439354817886]},"#ee33ff":{"lch":[59.6778857977730581,128.651158016084139,302.091050100274117],"luv":[59.6778857977730581,68.3480180420837229,-108.993893813362092],"rgb":[0.933333333333333348,0.2,1],"xyz":[0.544909619517369159,0.27767258574917425,0.971005774722048853],"hpluv":[302.091050100274117,273.551812848380507,59.6778857977730581],"hsluv":[302.091050100274117,99.9999999999986784,59.6778857977730581]},"#ee4400":{"lch":[54.3591594970822598,145.188828472067655,17.4116852889838647],"luv":[54.3591594970822598,138.536177662057611,43.4456372018905412],"rgb":[0.933333333333333348,0.266666666666666663,0],"xyz":[0.373261401598505183,0.223145411354373546,0.023417812633377437],"hpluv":[17.4116852889838647,338.922026437804789,54.3591594970822598],"hsluv":[17.4116852889838647,100.000000000002217,54.3591594970822598]},"#ee4411":{"lch":[54.4016650840252112,143.940172045268554,16.9187727396215735],"luv":[54.4016650840252112,137.710194578710485,41.8888462184768713],"rgb":[0.933333333333333348,0.266666666666666663,0.0666666666666666657],"xyz":[0.374273067098142287,0.223550077554228399,0.0287459175981330667],"hpluv":[16.9187727396215735,335.744689025208913,54.4016650840252112],"hsluv":[16.9187727396215735,97.1754310281257574,54.4016650840252112]},"#ee4422":{"lch":[54.4803236312215944,141.690847159829417,15.9963876830432117],"luv":[54.4803236312215944,136.204446081142294,39.0467032744039173],"rgb":[0.933333333333333348,0.266666666666666663,0.133333333333333331],"xyz":[0.376148425236619344,0.224300220809619227,0.0386228037941122662],"hpluv":[15.9963876830432117,330.020900264600868,54.4803236312215944],"hsluv":[15.9963876830432117,92.0288025918740118,54.4803236312215944]},"#ee4433":{"lch":[54.6094526105793534,138.167821982121467,14.4538486850626899],"luv":[54.6094526105793534,133.794673004457735,34.4867004352900608],"rgb":[0.933333333333333348,0.266666666666666663,0.2],"xyz":[0.37923617596907705,0.225535321102602326,0.0548849576517234095],"hpluv":[14.4538486850626899,321.054243711338643,54.6094526105793534],"hsluv":[14.4538486850626899,83.7991355104008591,54.6094526105793534]},"#ee4444":{"lch":[54.7950558424119549,133.462657054844783,12.1770506300618084],"luv":[54.7950558424119549,130.459808710538397,28.151716454753565],"rgb":[0.933333333333333348,0.266666666666666663,0.266666666666666663],"xyz":[0.383694171213295965,0.22731851920028992,0.078363732604610048],"hpluv":[12.1770506300618084,309.070617226475065,54.7950558424119549],"hsluv":[12.1770506300618084,79.6495466444067546,54.7950558424119549]},"#ee4455":{"lch":[55.04178262974213,127.837203216659944,9.0482956458548145],"luv":[55.04178262974213,126.246413461005446,20.1045670057943724],"rgb":[0.933333333333333348,0.266666666666666663,0.333333333333333315],"xyz":[0.38965658525265795,0.229703484816034753,0.109765779878583963],"hpluv":[9.0482956458548145,294.716259365516066,55.04178262974213],"hsluv":[9.0482956458548145,80.0457187830871106,55.04178262974213]},"#ee4466":{"lch":[55.3531965298607105,121.710491561886229,4.95183922571805102],"luv":[55.3531965298607105,121.256220070521849,10.5058483924504795],"rgb":[0.933333333333333348,0.266666666666666663,0.4],"xyz":[0.397241581026166135,0.232737483125438044,0.149713424285727925],"hpluv":[4.95183922571805102,279.013127650855779,55.3531965298607105],"hsluv":[4.95183922571805102,80.511500113091131,55.3531965298607105]},"#ee4477":{"lch":[55.7319177265462855,115.631099927359429,359.795147057523252],"luv":[55.7319177265462855,115.63036086114974,-0.41342173536332294],"rgb":[0.933333333333333348,0.266666666666666663,0.466666666666666674],"xyz":[0.406555594046368041,0.236463088333518867,0.198767226192125834],"hpluv":[359.795147057523252,263.275227085929203,55.7319177265462855],"hsluv":[359.795147057523252,81.0316034214938412,55.7319177265462855]},"#ee4488":{"lch":[56.1797144871475069,110.229334232011354,353.550243523911263],"luv":[56.1797144871475069,109.531664472369371,-12.3822697089333467],"rgb":[0.933333333333333348,0.266666666666666663,0.533333333333333326],"xyz":[0.417696011148311841,0.240919255174296465,0.257440089595698],"hpluv":[353.550243523911263,248.975712077739217,56.1797144871475069],"hsluv":[353.550243523911263,81.5885451367485217,56.1797144871475069]},"#ee4499":{"lch":[56.6975745677487,106.142668326601637,346.310852745323245],"luv":[56.6975745677487,103.127575819694869,-25.1190992084518889],"rgb":[0.933333333333333348,0.266666666666666663,0.6],"xyz":[0.430752975257384541,0.246142040817925623,0.326206767236815909],"hpluv":[346.310852745323245,237.55536706581762,56.6975745677487],"hsluv":[346.310852745323245,82.1643886194057,56.6975745677487]},"#ee44aa":{"lch":[57.2857706939250164,103.913945498461985,338.339047623856459],"luv":[57.2857706939250164,96.5759938315173798,-38.356035828954532],"rgb":[0.933333333333333348,0.266666666666666663,0.66666666666666663],"xyz":[0.445810668222637096,0.252165118004026745,0.405510616853814509],"hpluv":[338.339047623856459,230.179372132151769,57.2857706939250164],"hsluv":[338.339047623856459,82.742186475039972,57.2857706939250164]},"#ee44bb":{"lch":[57.9439265752057224,103.883653730246948,330.054621671216069],"luv":[57.9439265752057224,90.015359877018625,-51.8560362788815183],"rgb":[0.933333333333333348,0.266666666666666663,0.733333333333333282],"xyz":[0.462948260329973771,0.259020154846961503,0.495768601952456256],"hpluv":[330.054621671216069,227.498543532010189,57.9439265752057224],"hsluv":[330.054621671216069,83.3069826860278,57.9439265752057224]},"#ee44cc":{"lch":[58.6710858878032866,106.123661593235155,321.940977409416575],"luv":[58.6710858878032866,83.5592367986741493,-65.4223623509468837],"rgb":[0.933333333333333348,0.266666666666666663,0.8],"xyz":[0.48224063577369547,0.266737105024450305,0.597375112622726356],"hpluv":[321.940977409416575,229.523641905846944,58.6710858878032866],"hsluv":[321.940977409416575,83.8463488142865288,58.6710858878032866]},"#ee44dd":{"lch":[59.4657843936948041,110.45324904546132,314.41066654104867],"luv":[59.4657843936948041,77.2947793232282407,-78.9014405069524116],"rgb":[0.933333333333333348,0.266666666666666663,0.866666666666666696],"xyz":[0.503758961324329069,0.275344435244703811,0.710704960522732354],"hpluv":[314.41066654104867,235.695163072106425,59.4657843936948041],"hsluv":[314.41066654104867,84.3505154479208699,59.4657843936948041]},"#ee44ee":{"lch":[60.3261240941145189,116.527805305600168,307.715012949243771],"luv":[60.3261240941145189,71.28406005268711,-92.180866733529669],"rgb":[0.933333333333333348,0.266666666666666663,0.933333333333333348],"xyz":[0.527571141590503778,0.284869307351173828,0.836115776591255577],"hpluv":[307.715012949243771,245.111377339321677,60.3261240941145189],"hsluv":[307.715012949243771,84.8122051950840898,60.3261240941145189]},"#ee44ff":{"lch":[61.2498476847862321,123.946828366557639,301.937515996566106],"luv":[61.2498476847862321,65.5671420475233617,-105.184438705774298],"rgb":[0.933333333333333348,0.266666666666666663,1],"xyz":[0.553742190000335199,0.295337726715106552,0.973949964883037422],"hpluv":[301.937515996566106,256.785047727470896,61.2498476847862321],"hsluv":[301.937515996566106,99.9999999999986073,61.2498476847862321]},"#ee5500":{"lch":[56.7595334156469136,135.29504726150742,20.5772435658132551],"luv":[56.7595334156469136,126.663115619922038,47.5521288161507414],"rgb":[0.933333333333333348,0.333333333333333315,0],"xyz":[0.385074658312851148,0.24677192478306581,0.0273555648714926478],"hpluv":[20.5772435658132551,302.470071141489655,56.7595334156469136],"hsluv":[20.5772435658132551,100.000000000002331,56.7595334156469136]},"#ee5511":{"lch":[56.7992830001534799,134.121232245619609,20.0864579205919],"luv":[56.7992830001534799,125.963368804879124,46.0622910677426],"rgb":[0.933333333333333348,0.333333333333333315,0.0666666666666666657],"xyz":[0.386086323812488252,0.247176590982920663,0.0326836698362482775],"hpluv":[20.0864579205919,299.636011805134103,56.7992830001534799],"hsluv":[20.0864579205919,97.4301566790671245,56.7992830001534799]},"#ee5522":{"lch":[56.8728535321199331,132.003018379302972,19.1666168944474329],"luv":[56.8728535321199331,124.685803630375247,43.3387498007741385],"rgb":[0.933333333333333348,0.333333333333333315,0.133333333333333331],"xyz":[0.387961681950965309,0.247926734238311491,0.042560556032227477],"hpluv":[19.1666168944474329,294.522290528054612,56.8728535321199331],"hsluv":[19.1666168944474329,92.7404035063857748,56.8728535321199331]},"#ee5533":{"lch":[56.9936637318031813,128.675597773649343,17.6241311186411558],"luv":[56.9936637318031813,122.635981466153211,38.9592801812266671],"rgb":[0.933333333333333348,0.333333333333333315,0.2],"xyz":[0.391049432683423,0.24916183453129459,0.0588227098898386203],"hpluv":[17.6241311186411558,286.489655583397507,56.9936637318031813],"hsluv":[17.6241311186411558,85.2217597168545353,56.9936637318031813]},"#ee5544":{"lch":[57.1673833238913431,124.212647444540593,15.3377586553938237],"luv":[57.1673833238913431,119.788604494467549,32.8553194848226298],"rgb":[0.933333333333333348,0.333333333333333315,0.266666666666666663],"xyz":[0.39550742792764193,0.250945032628982156,0.0823014848427252588],"hpluv":[15.3377586553938237,275.712737821839653,57.1673833238913431],"hsluv":[15.3377586553938237,78.8138286806830308,57.1673833238913431]},"#ee5555":{"lch":[57.3984455800741813,118.847398490007407,12.1770506300618084],"luv":[57.3984455800741813,116.173386735287238,25.068871978930396],"rgb":[0.933333333333333348,0.333333333333333315,0.333333333333333315],"xyz":[0.401469841967003915,0.253329998244727,0.113703532116699174],"hpluv":[12.1770506300618084,262.741620924066638,57.3984455800741813],"hsluv":[12.1770506300618084,79.1862648733910817,57.3984455800741813]},"#ee5566":{"lch":[57.6903015433249777,112.967028718059339,8.00617638558467881],"luv":[57.6903015433249777,111.865945908872092,15.7340307391382019],"rgb":[0.933333333333333348,0.333333333333333315,0.4],"xyz":[0.409054837740512101,0.256363996554130336,0.153651176523843136],"hpluv":[8.00617638558467881,248.478160638648092,57.6903015433249777],"hsluv":[8.00617638558467881,79.626682914648967,57.6903015433249777]},"#ee5577":{"lch":[58.0455538260385,107.095574323644513,2.70497781295448236],"luv":[58.0455538260385,106.976246145051803,5.05418642558356],"rgb":[0.933333333333333348,0.333333333333333315,0.466666666666666674],"xyz":[0.418368850760714,0.260089601762211131,0.202704978430241045],"hpluv":[2.70497781295448236,234.121819944652458,58.0455538260385],"hsluv":[2.70497781295448236,80.121743738144815,58.0455538260385]},"#ee5588":{"lch":[58.4660405277881523,101.857434077675435,356.214905006905212],"luv":[58.4660405277881523,101.63524992227579,-6.72405012805051783],"rgb":[0.933333333333333348,0.333333333333333315,0.533333333333333326],"xyz":[0.429509267862657806,0.264545768602988729,0.261377841833813185],"hpluv":[356.214905006905212,221.069268787819283,58.4660405277881523],"hsluv":[356.214905006905212,80.6557447307456385,58.4660405277881523]},"#ee5599":{"lch":[58.9528982622070714,97.9100075268454475,348.609359498013816],"luv":[58.9528982622070714,95.9815181358717808,-19.3369529719717974],"rgb":[0.933333333333333348,0.333333333333333315,0.6],"xyz":[0.442566231971730506,0.269768554246617887,0.33014451947493112],"hpluv":[348.609359498013816,210.746926462762332,58.9528982622070714],"hsluv":[348.609359498013816,81.2121283261168685,58.9528982622070714]},"#ee55aa":{"lch":[59.5066178042993812,95.8379147411847327,340.160257686713578],"luv":[59.5066178042993812,90.1495111294489817,-32.5264745255299346],"rgb":[0.933333333333333348,0.333333333333333315,0.66666666666666663],"xyz":[0.457623924936983062,0.275791631432719,0.40944836909192972],"hpluv":[340.160257686713578,204.36730380296666,59.5066178042993812],"hsluv":[340.160257686713578,81.7747968800248515,59.5066178042993812]},"#ee55bb":{"lch":[60.1270988419473156,96.02691270708236,331.338718337462637],"luv":[60.1270988419473156,84.2607812264238305,-46.0574501157669],"rgb":[0.933333333333333348,0.333333333333333315,0.733333333333333282],"xyz":[0.474761517044319736,0.282646668275653767,0.499706354190571467],"hpluv":[331.338718337462637,202.657202566236862,60.1270988419473156],"hsluv":[331.338718337462637,82.3290961128101202,60.1270988419473156]},"#ee55cc":{"lch":[60.8137066481247359,98.5745380895967855,322.704854800823],"luv":[60.8137066481247359,78.4184921400445774,-59.728382282288],"rgb":[0.933333333333333348,0.333333333333333315,0.8],"xyz":[0.494053892488041435,0.290363618453142569,0.601312864860841567],"hpluv":[322.704854800823,205.68499115665557,60.8137066481247359],"hsluv":[322.704854800823,82.862416210457269,60.8137066481247359]},"#ee55dd":{"lch":[61.5653314057239669,103.296154527471415,314.73674606959],"luv":[61.5653314057239669,72.7050419742243577,-73.3762387404091925],"rgb":[0.933333333333333348,0.333333333333333315,0.866666666666666696],"xyz":[0.515572218038675,0.298970948673396075,0.714642712760847565],"hpluv":[314.73674606959,212.905685416828703,61.5653314057239669],"hsluv":[314.73674606959,83.3644351442966496,61.5653314057239669]},"#ee55ee":{"lch":[62.3804497031794796,109.822432229930158,307.715012949243942],"luv":[62.3804497031794796,67.1821530808003473,-86.8764923804219364],"rgb":[0.933333333333333348,0.333333333333333315,0.933333333333333348],"xyz":[0.539384398304849744,0.308495820779866092,0.840053528829370788],"hpluv":[307.715012949243942,223.399338603574023,62.3804497031794796],"hsluv":[307.715012949243942,83.8270760150894318,62.3804497031794796]},"#ee55ff":{"lch":[63.2571870514493355,117.722992850638121,301.718618818209791],"luv":[63.2571870514493355,61.8926401270512301,-100.139922827085911],"rgb":[0.933333333333333348,0.333333333333333315,1],"xyz":[0.565555446714681165,0.318964240143798816,0.977887717121152633],"hpluv":[301.718618818209791,236.15152010236153,63.2571870514493355],"hsluv":[301.718618818209791,99.9999999999986358,63.2571870514493355]},"#ee6600":{"lch":[59.6010827175637274,124.896403377083828,24.7633991985742],"luv":[59.6010827175637274,113.411584725929117,52.315619335764687],"rgb":[0.933333333333333348,0.4,0],"xyz":[0.400102716018697624,0.276828040194759151,0.032364917440108],"hpluv":[24.7633991985742,265.910269095548301,59.6010827175637274],"hsluv":[24.7633991985742,100.000000000002458,59.6010827175637274]},"#ee6611":{"lch":[59.6379025762155521,123.785593795891074,24.2806773941880323],"luv":[59.6379025762155521,112.835768380983566,50.9014990474188],"rgb":[0.933333333333333348,0.4,0.0666666666666666657],"xyz":[0.401114381518334728,0.277232706394614,0.0376930224048636284],"hpluv":[24.2806773941880323,263.38259338209491,59.6379025762155521],"hsluv":[24.2806773941880323,97.6946368166697425,59.6379025762155521]},"#ee6622":{"lch":[59.7060621192549235,121.776511986799889,23.3741045361832462],"luv":[59.7060621192549235,111.782804083827386,48.312768320888857],"rgb":[0.933333333333333348,0.4,0.133333333333333331],"xyz":[0.402989739656811785,0.277982849650004804,0.0475699086008428279],"hpluv":[23.3741045361832462,258.812011755725052,59.7060621192549235],"hsluv":[23.3741045361832462,93.4807634058905847,59.7060621192549235]},"#ee6633":{"lch":[59.818019190990654,118.60827278102343,21.848413141726418],"luv":[59.818019190990654,110.088842330137865,44.1403349161252621],"rgb":[0.933333333333333348,0.4,0.2],"xyz":[0.406077490389269491,0.279217949942987931,0.0638320624584539642],"hpluv":[21.848413141726418,251.606745577849637,59.818019190990654],"hsluv":[21.848413141726418,86.7067277856042722,59.818019190990654]},"#ee6644":{"lch":[59.9790782653121,114.334087778781978,19.5741908506499591],"luv":[59.9790782653121,107.726545105393242,38.3050271878496318],"rgb":[0.933333333333333348,0.4,0.266666666666666663],"xyz":[0.410535485633488406,0.281001148040675497,0.0873108374113406],"hpluv":[19.5741908506499591,241.888527179759762,59.9790782653121],"hsluv":[19.5741908506499591,77.7164494297015551,59.9790782653121]},"#ee6655":{"lch":[60.1934276072459227,109.155383321287928,16.4048569251700904],"luv":[60.1934276072459227,104.711671726378157,30.827966398783424],"rgb":[0.933333333333333348,0.4,0.333333333333333315],"xyz":[0.416497899672850391,0.283386113656420358,0.118712884685314518],"hpluv":[16.4048569251700904,230.109957101660228,60.1934276072459227],"hsluv":[16.4048569251700904,78.0627041699660822,60.1934276072459227]},"#ee6666":{"lch":[60.4643778553048179,103.423697151150392,12.1770506300619203],"luv":[60.4643778553048179,101.096711576265875,21.8155000143976636],"rgb":[0.933333333333333348,0.4,0.4],"xyz":[0.424082895446358576,0.286420111965823676,0.15866052909245848],"hpluv":[12.1770506300619203,217.050003231938149,60.4643778553048179],"hsluv":[12.1770506300619203,78.4746058088251601,60.4643778553048179]},"#ee6677":{"lch":[60.7944870758990845,97.6355083856116,6.72933164538236728],"luv":[60.7944870758990845,96.9628770314166388,11.4408468002682859],"rgb":[0.933333333333333348,0.4,0.466666666666666674],"xyz":[0.433396908466560482,0.290145717173904472,0.207714330998856389],"hpluv":[6.72933164538236728,203.79002370037793,60.7944870758990845],"hsluv":[6.72933164538236728,78.9407958828298177,60.7944870758990845]},"#ee6688":{"lch":[61.1856375663111294,92.4106294683140419,359.951978350089689],"luv":[61.1856375663111294,92.4105970103857,-0.0774526573243247418],"rgb":[0.933333333333333348,0.4,0.533333333333333326],"xyz":[0.444537325568504282,0.294601884014682069,0.266387194402428529],"hpluv":[359.951978350089689,191.6512981090967,61.1856375663111294],"hsluv":[359.951978350089689,79.4474583444281706,61.1856375663111294]},"#ee6699":{"lch":[61.6390913266860281,88.437141466109523,351.875732288608958],"luv":[61.6390913266860281,87.5495765855601746,-12.4979850530316181],"rgb":[0.933333333333333348,0.4,0.6],"xyz":[0.457594289677577,0.299824669658311227,0.335153872043546464],"hpluv":[351.875732288608958,182.061365307506776,61.6390913266860281],"hsluv":[351.875732288608958,79.9795786975914353,61.6390913266860281]},"#ee66aa":{"lch":[62.1555369290736337,86.3639450462453624,342.77320214224],"luv":[62.1555369290736337,82.489654306882187,-25.5770978862333536],"rgb":[0.933333333333333348,0.4,0.66666666666666663],"xyz":[0.472651982642829538,0.305847746844412349,0.414457721660545064],"hpluv":[342.77320214224,176.316102232879075,62.1555369290736337],"hsluv":[342.77320214224,80.5221008496243,62.1555369290736337]},"#ee66bb":{"lch":[62.735134131647655,86.6495273870251168,333.187217955259598],"luv":[62.735134131647655,77.3334216921159765,-39.0855790002422907],"rgb":[0.933333333333333348,0.4,0.733333333333333282],"xyz":[0.489789574750166212,0.312702783687347108,0.504715706759186755],"hpluv":[333.187217955259598,175.264796900814019,62.735134131647655],"hsluv":[333.187217955259598,81.0608551813628679,62.735134131647655]},"#ee66cc":{"lch":[63.3775592853136516,89.4351699392481549,323.800511847500275],"luv":[63.3775592853136516,72.1711045195964545,-52.8202735176909144],"rgb":[0.933333333333333348,0.4,0.8],"xyz":[0.509081950193887911,0.320419733864835909,0.606322217429456911],"hpluv":[323.800511847500275,179.065596131410075,63.3775592853136516],"hsluv":[323.800511847500275,81.5831918811758072,63.3775592853136516]},"#ee66dd":{"lch":[64.0820526997291751,94.5324861988586918,315.200217206616742],"luv":[64.0820526997291751,67.0777383779463179,-66.6105694393704795],"rgb":[0.933333333333333348,0.4,0.866666666666666696],"xyz":[0.53060027574452151,0.329027064085089416,0.719652065329462909],"hpluv":[315.200217206616742,187.190580763285084,64.0820526997291751],"hsluv":[315.200217206616742,82.0783141076240241,64.0820526997291751]},"#ee66ee":{"lch":[64.8474680131467,101.534802649490857,307.715012949244169],"luv":[64.8474680131467,62.1123254704966499,-80.32045302236628],"rgb":[0.933333333333333348,0.4,0.933333333333333348],"xyz":[0.55441245601069622,0.338551936191559433,0.845062881397986132],"hpluv":[307.715012949244169,198.683239249207219,64.8474680131467],"hsluv":[307.715012949244169,82.5373501246235,64.8474680131467]},"#ee66ff":{"lch":[65.6723229483953759,109.966867844968618,301.415067453827589],"luv":[65.6723229483953759,57.3184789471434897,-93.8472375449520797],"rgb":[0.933333333333333348,0.4,1],"xyz":[0.580583504420527641,0.349020355555492157,0.982897069689768088],"hpluv":[301.415067453827589,212.480364902930489,65.6723229483953759],"hsluv":[301.415067453827589,99.9999999999984794,65.6723229483953759]},"#ee7700":{"lch":[62.8217158048736763,114.851740825540901,30.0981414692213356],"luv":[62.8217158048736763,99.3660150566978,57.5961580525066097],"rgb":[0.933333333333333348,0.466666666666666674,0],"xyz":[0.418556454072115225,0.313735516301594908,0.0385161634579137],"hpluv":[30.0981414692213356,231.988851559171735,62.8217158048736763],"hsluv":[30.0981414692213356,100.000000000002203,62.8217158048736763]},"#ee7711":{"lch":[62.8555901763931075,113.786950077776382,29.6341042910547],"luv":[62.8555901763931075,98.9037041004629458,56.2630191441096059],"rgb":[0.933333333333333348,0.466666666666666674,0.0666666666666666657],"xyz":[0.419568119571752329,0.314140182501449761,0.0438442684226693288],"hpluv":[29.6341042910547,229.71421718860654,62.8555901763931075],"hsluv":[29.6341042910547,97.9532933827149463,62.8555901763931075]},"#ee7722":{"lch":[62.9183073649527955,111.855609649988921,28.7604537228304977],"luv":[62.9183073649527955,98.0569879769147406,53.8191835600085398],"rgb":[0.933333333333333348,0.466666666666666674,0.133333333333333331],"xyz":[0.421443477710229386,0.314890325756840561,0.0537211546186485284],"hpluv":[28.7604537228304977,225.59011469442973,62.9183073649527955],"hsluv":[28.7604537228304977,94.206312745702121,62.9183073649527955]},"#ee7733":{"lch":[63.0213536795682501,108.794922632811733,27.2836719807386174],"luv":[63.0213536795682501,96.6912591797812411,49.8711899688373208],"rgb":[0.933333333333333348,0.466666666666666674,0.2],"xyz":[0.424531228442687092,0.316125426049823688,0.0699833084762596647],"hpluv":[27.2836719807386174,219.058559128587405,63.0213536795682501],"hsluv":[27.2836719807386174,88.1668112455654,63.0213536795682501]},"#ee7744":{"lch":[63.1696562136619235,104.634411539935968,25.0668522383795889],"luv":[63.1696562136619235,94.7793214210065571,44.3310309972846497],"rgb":[0.933333333333333348,0.466666666666666674,0.266666666666666663],"xyz":[0.428989223686906,0.317908624147511254,0.0934620834291463],"hpluv":[25.0668522383795889,210.186756956079222,63.1696562136619235],"hsluv":[25.0668522383795889,79.7210233436604199,63.1696562136619235]},"#ee7755":{"lch":[63.3671413614491286,99.5393983003294096,21.9455950678528],"luv":[63.3671413614491286,92.3266881357997278,37.2004633286522051],"rgb":[0.933333333333333348,0.466666666666666674,0.333333333333333315],"xyz":[0.434951637726268,0.320293589763256115,0.124864130703120219],"hpluv":[21.9455950678528,199.328877993420377,63.3671413614491286],"hsluv":[21.9455950678528,76.6090179557391,63.3671413614491286]},"#ee7766":{"lch":[63.6169573916324822,93.8195852400781263,17.7221756586824775],"luv":[63.6169573916324822,89.367258373090749,28.5588463614608763],"rgb":[0.933333333333333348,0.466666666666666674,0.4],"xyz":[0.442536633499776177,0.323327588072659433,0.16481177511026418],"hpluv":[17.7221756586824775,187.13711975631665,63.6169573916324822],"hsluv":[17.7221756586824775,76.9903860669667,63.6169573916324822]},"#ee7777":{"lch":[63.9215909451051232,87.936547917610838,12.1770506300618937],"luv":[63.9215909451051232,85.9580160709841863,18.5487447771187846],"rgb":[0.933333333333333348,0.466666666666666674,0.466666666666666674],"xyz":[0.451850646519978083,0.327053193280740229,0.21386557701666209],"hpluv":[12.1770506300618937,174.56660414904394,63.9215909451051232],"hsluv":[12.1770506300618937,77.4248320836617268,63.9215909451051232]},"#ee7788":{"lch":[64.2829374304473,82.5014209284754543,5.11726519711922112],"luv":[64.2829374304473,82.1725895095950847,7.35866757674724514],"rgb":[0.933333333333333348,0.466666666666666674,0.533333333333333326],"xyz":[0.462991063621921883,0.331509360121517827,0.272538440420234229],"hpluv":[5.11726519711922112,162.85647909200884,64.2829374304473],"hsluv":[5.11726519711922112,77.9003779592442669,64.2829374304473]},"#ee7799":{"lch":[64.7023501026032477,78.2413537817778177,356.48599323172067],"luv":[64.7023501026032477,78.0942478632934893,-4.79561177242263259],"rgb":[0.933333333333333348,0.466666666666666674,0.6],"xyz":[0.476048027730994638,0.336732145765147,0.341305118061352164],"hpluv":[356.48599323172067,153.446019679421255,64.7023501026032477],"hsluv":[356.48599323172067,78.4035888028378167,64.7023501026032477]},"#ee77aa":{"lch":[65.1806796634753596,75.8992199041959,346.522692233148291],"luv":[65.1806796634753596,73.8091300431969302,-17.6890899803221124],"rgb":[0.933333333333333348,0.466666666666666674,0.66666666666666663],"xyz":[0.491105720696247139,0.342755222951248106,0.420608967678350765],"hpluv":[346.522692233148291,147.76029654451807,65.1806796634753596],"hsluv":[346.522692233148291,78.9205551316135256,65.1806796634753596]},"#ee77bb":{"lch":[65.7183104585581646,76.0514193956389875,335.85959217762661],"luv":[65.7183104585581646,69.4004166910179094,-31.1030634376174824],"rgb":[0.933333333333333348,0.466666666666666674,0.733333333333333282],"xyz":[0.508243312803583813,0.349610259794182865,0.510866952776992456],"hpluv":[335.85959217762661,146.845370988132231,65.7183104585581646],"hsluv":[335.85959217762661,79.4377328719524627,65.7183104585581646]},"#ee77cc":{"lch":[66.3151963922866,78.9180750862202416,325.378996221060731],"luv":[66.3151963922866,64.9439055461397459,-44.8369457894756067],"rgb":[0.933333333333333348,0.466666666666666674,0.8],"xyz":[0.527535688247305568,0.357327209971671667,0.612473463447262612],"hpluv":[325.378996221060731,151.008971652655617,66.3151963922866],"hsluv":[325.378996221060731,79.9425706591808307,66.3151963922866]},"#ee77dd":{"lch":[66.9708980107196652,84.3115421117289543,315.859798591258766],"luv":[66.9708980107196652,60.5051526262217152,-58.7159487612646842],"rgb":[0.933333333333333348,0.466666666666666674,0.866666666666666696],"xyz":[0.549054013797939056,0.365934540191925173,0.725803311347268609],"hpluv":[315.859798591258766,159.749768323538632,66.9708980107196652],"hsluv":[315.859798591258766,80.4238985377879345,66.9708980107196652]},"#ee77ee":{"lch":[67.6846211881785251,91.7687338274624409,307.715012949244453],"luv":[67.6846211881785251,56.1380858067324056,-72.5948746830766112],"rgb":[0.933333333333333348,0.466666666666666674,0.933333333333333348],"xyz":[0.572866194064113765,0.37545941229839519,0.851214127415791832],"hpluv":[307.715012949244453,172.045795420537047,67.6846211881785251],"hsluv":[307.715012949244453,80.8720902094370757,67.6846211881785251]},"#ee77ff":{"lch":[68.4552572311626761,100.746525491660947,300.997699928034137],"luv":[68.4552572311626761,51.8848298156272918,-86.3587102361150585],"rgb":[0.933333333333333348,0.466666666666666674,1],"xyz":[0.599037242473945186,0.385927831662327914,0.989048315707573789],"hpluv":[300.997699928034137,186.750854251257437,68.4552572311626761],"hsluv":[300.997699928034137,99.9999999999982,68.4552572311626761]},"#ee8800":{"lch":[66.3576417146455,105.981377873447272,36.6492300119340797],"luv":[66.3576417146455,85.0293774107247771,63.2618165491550428],"rgb":[0.933333333333333348,0.533333333333333326,0],"xyz":[0.440628823797085678,0.35788025575153648,0.045873620032903642],"hpluv":[36.6492300119340797,202.664622836431278,66.3576417146455],"hsluv":[36.6492300119340797,100.000000000002288,66.3576417146455]},"#ee8811":{"lch":[66.3886714607036907,104.946342152180421,36.2201184819273792],"luv":[66.3886714607036907,84.6657638625926552,62.0116373004794923],"rgb":[0.933333333333333348,0.533333333333333326,0.0666666666666666657],"xyz":[0.441640489296722782,0.358284921951391333,0.0512017249976592717],"hpluv":[36.2201184819273792,200.591559481147556,66.3886714607036907],"hsluv":[36.2201184819273792,98.1954604930108701,66.3886714607036907]},"#ee8822":{"lch":[66.4461305943750773,103.062674429887437,35.4099902294173745],"luv":[66.4461305943750773,83.9988395226221201,59.7169140151578],"rgb":[0.933333333333333348,0.533333333333333326,0.133333333333333331],"xyz":[0.443515847435199839,0.359035065206782134,0.0610786111936384712],"hpluv":[35.4099902294173745,196.820821024497235,66.4461305943750773],"hsluv":[35.4099902294173745,94.8869488142181581,66.4461305943750773]},"#ee8833":{"lch":[66.5405621290638578,100.059958052790449,34.0337853874148877],"luv":[66.5405621290638578,82.920456825626232,56.0017235927220369],"rgb":[0.933333333333333348,0.533333333333333326,0.2],"xyz":[0.446603598167657545,0.36027016549976526,0.0773407650512496214],"hpluv":[34.0337853874148877,190.815292572549708,66.5405621290638578],"hsluv":[34.0337853874148877,89.5408718212573689,66.5405621290638578]},"#ee8844":{"lch":[66.6765193585480347,95.9403990837829639,31.9512880443390657],"luv":[66.6765193585480347,81.4052672986078107,50.7714745934935223],"rgb":[0.933333333333333348,0.533333333333333326,0.266666666666666663],"xyz":[0.45106159341187646,0.362053363597452826,0.10081954000413626],"hpluv":[31.9512880443390657,182.586190035925256,66.6765193585480347],"hsluv":[31.9512880443390657,82.0371002457568608,66.6765193585480347]},"#ee8855":{"lch":[66.8576614114874559,90.8274017958005,28.983496619036984],"luv":[66.8576614114874559,79.4521157428502391,44.0111147434430805],"rgb":[0.933333333333333348,0.533333333333333326,0.333333333333333315],"xyz":[0.457024007451238445,0.364438329213197687,0.132221587278110175],"hpluv":[28.983496619036984,172.387208051834335,66.8576614114874559],"hsluv":[28.983496619036984,74.7174368883009663,66.8576614114874559]},"#ee8866":{"lch":[67.0869600103699213,84.978663004295683,24.8971939400565283],"luv":[67.0869600103699213,77.081139807049837,35.7752854921339249],"rgb":[0.933333333333333348,0.533333333333333326,0.4],"xyz":[0.464609003224746631,0.367472327522601,0.172169231685254109],"hpluv":[24.8971939400565283,160.735241770302764,67.0869600103699213],"hsluv":[24.8971939400565283,75.0669061044520447,67.0869600103699213]},"#ee8877":{"lch":[67.3668077908477727,78.8051510957513841,19.4009345351312952],"luv":[67.3668077908477727,74.3303771171996743,26.1772205713113095],"rgb":[0.933333333333333348,0.533333333333333326,0.466666666666666674],"xyz":[0.473923016244948536,0.371197932730681801,0.221223033591652019],"hpluv":[19.4009345351312952,148.438980876884,67.3668077908477727],"hsluv":[19.4009345351312952,75.4672397967126329,67.3668077908477727]},"#ee8888":{"lch":[67.6990830402889117,72.8916076032019191,12.177050630062066],"luv":[67.6990830402889117,71.2515799877231757,15.3752661190709663],"rgb":[0.933333333333333348,0.533333333333333326,0.533333333333333326],"xyz":[0.485063433346892336,0.375654099571459399,0.279895896995224214],"hpluv":[12.177050630062066,136.626224949154164,67.6990830402889117],"hsluv":[12.177050630062066,75.9081099773692927,67.6990830402889117]},"#ee8899":{"lch":[68.0851935471165319,67.9986383575237312,2.99916583787236446],"luv":[68.0851935471165319,67.9055003914663615,3.55778513430191889],"rgb":[0.933333333333333348,0.533333333333333326,0.6],"xyz":[0.498120397455965036,0.380876885215088556,0.348662574636342093],"hpluv":[2.99916583787236446,126.732168582193651,68.0851935471165319],"hsluv":[2.99916583787236446,76.3775584855855385,68.0851935471165319]},"#ee88aa":{"lch":[68.5261104708773274,64.9933943358063573,351.976176804910949],"luv":[68.5261104708773274,64.3571165389368,-9.07209226602857832],"rgb":[0.933333333333333348,0.533333333333333326,0.66666666666666663],"xyz":[0.513178090421217648,0.386899962401189679,0.427966424253340694],"hpluv":[351.976176804910949,120.351764916629207,68.5261104708773274],"hsluv":[351.976176804910949,76.8628030471707859,68.5261104708773274]},"#ee88bb":{"lch":[69.0223979406526098,64.6433047463018,339.810246341231903],"luv":[69.0223979406526098,60.6712812589502875,-22.3103670727442456],"rgb":[0.933333333333333348,0.533333333333333326,0.733333333333333282],"xyz":[0.530315682528554211,0.393754999244124437,0.51822440935198244],"hpluv":[339.810246341231903,118.842788638920595,69.0223979406526098],"hsluv":[339.810246341231903,77.3509666785266887,69.0223979406526098]},"#ee88cc":{"lch":[69.5742414545850778,67.3203562589766307,327.709288072293873],"luv":[69.5742414545850778,56.9091584539883399,-35.9635656031820687],"rgb":[0.933333333333333348,0.533333333333333326,0.8],"xyz":[0.549608057972276,0.401471949421613239,0.619830920022252596],"hpluv":[327.709288072293873,122.782720563937247,69.5742414545850778],"hsluv":[327.709288072293873,77.8296635442389686,69.5742414545850778]},"#ee88dd":{"lch":[70.1814766713242,72.856404222049747,316.817937357318669],"luv":[70.1814766713242,53.1256441200966663,-49.8570112721525547],"rgb":[0.933333333333333348,0.533333333333333326,0.866666666666666696],"xyz":[0.571126383522909564,0.410079279641866745,0.733160767922258594],"hpluv":[316.817937357318669,131.729959343498166,70.1814766713242],"hsluv":[316.817937357318669,78.2874043120714163,70.1814766713242]},"#ee88ee":{"lch":[70.8436192863675558,80.7013698438951224,307.715012949244851],"luv":[70.8436192863675558,49.367799206375345,-63.839889537812887],"rgb":[0.933333333333333348,0.533333333333333326,0.933333333333333348],"xyz":[0.594938563789084274,0.419604151748336762,0.858571583990781817],"hpluv":[307.715012949244851,144.550464850223619,70.8436192863675558],"hsluv":[307.715012949244851,78.7138135635212848,70.8436192863675558]},"#ee88ff":{"lch":[71.5598961203093182,90.2054153167292583,300.42003582834775],"luv":[71.5598961203093182,45.674190228859068,-77.7874366424399426],"rgb":[0.933333333333333348,0.533333333333333326,1],"xyz":[0.621109612198915695,0.430072571112269486,0.996405772282563662],"hpluv":[300.42003582834775,159.956626210428567,71.5598961203093182],"hsluv":[300.42003582834775,99.99999999999784,71.5598961203093182]},"#ee9900":{"lch":[70.1492527845175715,98.9919938823364731,44.3502140795235036],"luv":[70.1492527845175715,70.7872313214223112,69.1995862317686772],"rgb":[0.933333333333333348,0.6,0],"xyz":[0.466498424249553179,0.409619456656472147,0.0544968201837259],"hpluv":[44.3502140795235036,179.06732625175573,70.1492527845175715],"hsluv":[44.3502140795235036,100.000000000002217,70.1492527845175715]},"#ee9911":{"lch":[70.1776126165771785,97.9766822185852533,43.9766782844564119],"luv":[70.1776126165771785,70.5062245085518526,68.0316291449155273],"rgb":[0.933333333333333348,0.6,0.0666666666666666657],"xyz":[0.467510089749190283,0.410024122856327,0.0598249251484815267],"hpluv":[43.9766782844564119,177.15910010767405,70.1776126165771785],"hsluv":[43.9766782844564119,98.4152296143538337,70.1776126165771785]},"#ee9922":{"lch":[70.2301348691785,96.1222676294158447,43.2696050504519505],"luv":[70.2301348691785,69.9901292501651824,65.8852953379296622],"rgb":[0.933333333333333348,0.6,0.133333333333333331],"xyz":[0.46938544788766734,0.410774266111717801,0.0697018113444607262],"hpluv":[43.2696050504519505,173.676009463775955,70.2301348691785],"hsluv":[43.2696050504519505,95.5057584668238,70.2301348691785]},"#ee9933":{"lch":[70.3164728806357573,93.1473438817036339,42.0626701373461103],"luv":[70.3164728806357573,69.1537511222041417,62.4034163964169508],"rgb":[0.933333333333333348,0.6,0.2],"xyz":[0.472473198620125046,0.412009366404700927,0.0859639652020718625],"hpluv":[42.0626701373461103,168.094198140054317,70.3164728806357573],"hsluv":[42.0626701373461103,90.7937976503380213,70.3164728806357573]},"#ee9944":{"lch":[70.4408210614760719,89.024296887742608,40.221678197216157],"luv":[70.4408210614760719,67.9746586813449483,57.4871395488729533],"rgb":[0.933333333333333348,0.6,0.266666666666666663],"xyz":[0.476931193864343961,0.413792564502388494,0.109442740154958501],"hpluv":[40.221678197216157,160.370125602871781,70.4408210614760719],"hsluv":[40.221678197216157,84.1577311163605657,70.4408210614760719]},"#ee9955":{"lch":[70.6065752665828654,83.8291063606938138,37.5652459120346904],"luv":[70.6065752665828654,66.4479455240540631,51.1076276974862154],"rgb":[0.933333333333333348,0.6,0.333333333333333315],"xyz":[0.482893607903705946,0.416177530118133354,0.140844787428932416],"hpluv":[37.5652459120346904,150.656896294971034,70.6065752665828654],"hsluv":[37.5652459120346904,75.5772228053980797,70.6065752665828654]},"#ee9966":{"lch":[70.8165243284349373,77.7550236522342,33.8383101580563],"luv":[70.8165243284349373,64.5842808158336652,43.297971946282189],"rgb":[0.933333333333333348,0.6,0.4],"xyz":[0.490478603677214131,0.419211528427536673,0.180792431836076378],"hpluv":[33.8383101580563,139.326323276972687,70.8165243284349373],"hsluv":[33.8383101580563,72.525293376848623,70.8165243284349373]},"#ee9977":{"lch":[71.0729506656700778,71.1378337295946,28.6840524218341386],"luv":[71.0729506656700778,62.4077841236731601,34.1446901949989154],"rgb":[0.933333333333333348,0.6,0.466666666666666674],"xyz":[0.499792616697416037,0.422937133635617468,0.229846233742474287],"hpluv":[28.6840524218341386,127.009327518651787,71.0729506656700778],"hsluv":[28.6840524218341386,72.8885787597460677,71.0729506656700778]},"#ee9988":{"lch":[71.3776900371935312,64.4963091800695878,21.6331741754282376],"luv":[71.3776900371935312,59.9533946760230378,23.7773918811996943],"rgb":[0.933333333333333348,0.6,0.533333333333333326],"xyz":[0.510933033799359837,0.427393300476395066,0.288519097146046455],"hpluv":[21.6331741754282376,114.659937381049531,71.3776900371935312],"hsluv":[21.6331741754282376,73.2902809537896189,71.3776900371935312]},"#ee9999":{"lch":[71.732171153908709,58.5818834203282179,12.1770506300621602],"luv":[71.732171153908709,57.263818011494017,12.3568690136061505],"rgb":[0.933333333333333348,0.6,0.6],"xyz":[0.523989997908432592,0.432616086120024224,0.35728577478716439],"hpluv":[12.1770506300621602,103.630759412975706,71.732171153908709],"hsluv":[12.1770506300621602,73.7196701825771186,71.732171153908709]},"#ee99aa":{"lch":[72.1374451439022408,54.3863410009709725,0.0659165211073427237],"luv":[72.1374451439022408,54.3863050092105,0.0625693137293968082],"rgb":[0.933333333333333348,0.6,0.66666666666666663],"xyz":[0.539047690873685093,0.438639163306125346,0.436589624404162935],"hpluv":[0.0659165211073427237,95.6683780017161,72.1374451439022408],"hsluv":[0.0659165211073427237,74.1649147609968082,72.1374451439022408]},"#ee99bb":{"lch":[72.5942101669252366,52.9692652640659247,345.882936464906891],"luv":[72.5942101669252366,51.3695627363271683,-12.9194073739288822],"rgb":[0.933333333333333348,0.6,0.733333333333333282],"xyz":[0.556185282981021767,0.445494200149060104,0.526847609502804737],"hpluv":[345.882936464906891,92.5894045166522091,72.5942101669252366],"hsluv":[345.882936464906891,74.6136876066909878,72.5942101669252366]},"#ee99cc":{"lch":[73.1028341171650737,55.013164482536844,331.314039518972208],"luv":[73.1028341171650737,48.2610582375421515,-26.4067893575727517],"rgb":[0.933333333333333348,0.6,0.8],"xyz":[0.575477658424743521,0.453211150326548906,0.628454120173074893],"hpluv":[331.314039518972208,95.4930444317639342,73.1028341171650737],"hsluv":[331.314039518972208,75.0536815693659491,73.1028341171650737]},"#ee99dd":{"lch":[73.6633770412179274,60.4388711558699896,318.269971219550712],"luv":[73.6633770412179274,45.1048908137830935,-40.2294167403973049],"rgb":[0.933333333333333348,0.6,0.866666666666666696],"xyz":[0.596995983975377,0.461818480546802412,0.74178396807308089],"hpluv":[318.269971219550712,104.112780668711437,73.6633770412179274],"hsluv":[318.269971219550712,75.472992612571872,73.6633770412179274]},"#ee99ee":{"lch":[74.2756141069900337,68.5596754700476083,307.71501294924542],"luv":[74.2756141069900337,41.9403078139404499,-54.2350410807456953],"rgb":[0.933333333333333348,0.6,0.933333333333333348],"xyz":[0.620808164241551719,0.471343352653272429,0.867194784141604114],"hpluv":[307.71501294924542,117.128296895720368,74.2756141069900337],"hsluv":[307.71501294924542,75.8603506282489235,74.2756141069900337]},"#ee99ff":{"lch":[74.9390594560707,78.5455210447045857,299.603294913962486],"luv":[74.9390594560707,38.8008486558083519,-68.2927010724659],"rgb":[0.933333333333333348,0.6,1],"xyz":[0.64697921265138314,0.481811772017205153,1.00502897243338585],"hpluv":[299.603294913962486,133.000267199001968,74.9390594560707],"hsluv":[299.603294913962486,99.9999999999973284,74.9390594560707]},"#dd0000":{"lch":[46.1435564305616239,155.182233977468201,12.1770506300617765],"luv":[46.1435564305616239,151.69070515099267,32.7330981276182555],"rgb":[0.866666666666666696,0,0],"xyz":[0.298181282529475455,0.153749723804264049,0.0139772476185688679],"hpluv":[12.1770506300617765,426.746789183125316,46.1435564305616239],"hsluv":[12.1770506300617765,100.000000000002217,46.1435564305616239]},"#dd0011":{"lch":[46.1980288678146636,153.577384001942391,11.5987087531524224],"luv":[46.1980288678146636,150.441300178721519,30.8776306962803169],"rgb":[0.866666666666666696,0,0.0666666666666666657],"xyz":[0.29919294802911256,0.154154390004118902,0.0193053525833244977],"hpluv":[11.5987087531524224,421.835520233675084,46.1980288678146636],"hsluv":[11.5987087531524224,99.9999999999964473,46.1980288678146636]},"#dd0022":{"lch":[46.2987546285526292,150.712226421231577,10.5179424282654246],"luv":[46.2987546285526292,148.17992816752087,27.5115263319381462],"rgb":[0.866666666666666696,0,0.133333333333333331],"xyz":[0.301068306167589617,0.15490453325950973,0.0291822387793036972],"hpluv":[10.5179424282654246,413.065099977246746,46.2987546285526292],"hsluv":[10.5179424282654246,99.9999999999964615,46.2987546285526292]},"#dd0033":{"lch":[46.4638920568500637,146.293058552209629,8.71533624525386585],"luv":[46.4638920568500637,144.603865814948932,22.1671146505918131],"rgb":[0.866666666666666696,0,0.2],"xyz":[0.304156056900047322,0.156139633552492829,0.0454443926369148404],"hpluv":[8.71533624525386585,399.528220173505417,46.4638920568500637],"hsluv":[8.71533624525386585,99.9999999999966,46.4638920568500637]},"#dd0044":{"lch":[46.7007828741672242,140.527307525302433,6.06736355557067153],"luv":[46.7007828741672242,139.740117429456177,14.853408400522655],"rgb":[0.866666666666666696,0,0.266666666666666663],"xyz":[0.308614052144266238,0.157922831650180423,0.0689231675898014789],"hpluv":[6.06736355557067153,381.835137536210595,46.7007828741672242],"hsluv":[6.06736355557067153,99.9999999999967315,46.7007828741672242]},"#dd0055":{"lch":[47.0148448700731194,133.854751810486647,2.4577968894866693],"luv":[47.0148448700731194,133.731616129679537,5.74015936982693],"rgb":[0.866666666666666696,0,0.333333333333333315],"xyz":[0.314576466183628223,0.160307797265925256,0.10032521486377538],"hpluv":[2.4577968894866693,361.275168455412427,47.0148448700731194],"hsluv":[2.4577968894866693,99.9999999999969873,47.0148448700731194]},"#dd0066":{"lch":[47.4099042919878073,126.898325188331157,357.792852491951692],"luv":[47.4099042919878073,126.80418183984473,-4.88716722969802841],"rgb":[0.866666666666666696,0,0.4],"xyz":[0.322161461957136408,0.163341795575328547,0.140272859270919342],"hpluv":[357.792852491951692,339.645713706877359,47.4099042919878073],"hsluv":[357.792852491951692,99.9999999999972857,47.4099042919878073]},"#dd0077":{"lch":[47.8883827301537,120.388643903007392,352.036106438093952],"luv":[47.8883827301537,119.227564738695264,-16.6797298325047478],"rgb":[0.866666666666666696,0,0.466666666666666674],"xyz":[0.331475474977338314,0.16706740078340937,0.189326661177317251],"hpluv":[352.036106438093952,319.002934776287759,47.8883827301537],"hsluv":[352.036106438093952,99.9999999999974136,47.8883827301537]},"#dd0088":{"lch":[48.4514347566520058,115.064311489444805,345.260130057314882],"luv":[48.4514347566520058,111.277652945923506,-29.2759241252361342],"rgb":[0.866666666666666696,0,0.533333333333333326],"xyz":[0.342615892079282114,0.171523567624186968,0.247999524580889419],"hpluv":[345.260130057314882,301.351479235409442,48.4514347566520058],"hsluv":[345.260130057314882,99.9999999999976836,48.4514347566520058]},"#dd0099":{"lch":[49.0990738312553816,111.554442433955828,337.69359677942],"luv":[49.0990738312553816,103.206522936464793,-42.3415546492536166],"rgb":[0.866666666666666696,0,0.6],"xyz":[0.355672856188354869,0.176746353267816125,0.316766202222007354],"hpluv":[337.69359677942,288.305479360883112,49.0990738312553816],"hsluv":[337.69359677942,99.9999999999979252,49.0990738312553816]},"#dd00aa":{"lch":[49.8303011832281442,110.265023964610052,329.72204926251294],"luv":[49.8303011832281442,95.2237329190616606,-55.5951094870335041],"rgb":[0.866666666666666696,0,0.66666666666666663],"xyz":[0.370730549153607369,0.18276943045391722,0.396070051839005954],"hpluv":[329.72204926251294,280.791263168904948,49.8303011832281442],"hsluv":[329.72204926251294,99.9999999999981526,49.8303011832281442]},"#dd00bb":{"lch":[50.6432416523731064,111.311454300018838,321.811503537589374],"luv":[50.6432416523731064,87.4886923962170613,-68.8183737179635244],"rgb":[0.866666666666666696,0,0.733333333333333282],"xyz":[0.387868141260944044,0.189624467296851978,0.486328036937647701],"hpluv":[321.811503537589374,278.905890401213071,50.6432416523731064],"hsluv":[321.811503537589374,99.9999999999984,50.6432416523731064]},"#dd00cc":{"lch":[51.5352850119508901,114.534817141075266,314.3830496716472],"luv":[51.5352850119508901,80.1116001600253753,-81.8557014345352201],"rgb":[0.866666666666666696,0,0.8],"xyz":[0.407160516704665798,0.19734141747434078,0.587934547607917857],"hpluv":[314.3830496716472,282.014975724645751,51.5352850119508901],"hsluv":[314.3830496716472,99.9999999999986215,51.5352850119508901]},"#dd00dd":{"lch":[52.5032286812834883,119.593841400887641,307.715012949243601],"luv":[52.5032286812834883,73.1596596193909647,-94.6062952735996419],"rgb":[0.866666666666666696,0,0.866666666666666696],"xyz":[0.428678842255299286,0.205948747694594314,0.701264395507923854],"hpluv":[307.715012949243601,289.042783730483222,52.5032286812834883],"hsluv":[307.715012949243601,99.9999999999987779,52.5032286812834883]},"#dd00ee":{"lch":[53.5434168792756111,126.080010820296707,301.921476351261958],"luv":[53.5434168792756111,66.6656277920787659,-107.01337860068783],"rgb":[0.866666666666666696,0,0.933333333333333348],"xyz":[0.452491022521474051,0.215473619801064359,0.826675211576447078],"hpluv":[301.921476351261958,298.799235277631283,53.5434168792756111],"hsluv":[301.921476351261958,99.999999999998991,53.5434168792756111]},"#dd00ff":{"lch":[54.6518715304170399,133.605457484958208,296.990855958497434],"luv":[54.6518715304170399,60.6366090811706258,-119.053013019000375],"rgb":[0.866666666666666696,0,1],"xyz":[0.478662070931305417,0.225942039164997055,0.964509399868228923],"hpluv":[296.990855958497434,310.211923209940835,54.6518715304170399],"hsluv":[296.990855958497434,99.99999999999919,54.6518715304170399]},"#dd1100":{"lch":[46.6790301132195,152.538998994032681,12.7564763340959253],"luv":[46.6790301132195,148.773935032481603,33.6817824506429915],"rgb":[0.866666666666666696,0.0666666666666666657,0],"xyz":[0.300185682790403863,0.157758524326120919,0.0146453810388783197],"hpluv":[12.7564763340959253,414.665968881342394,46.6790301132195],"hsluv":[12.7564763340959253,100.000000000002373,46.6790301132195]},"#dd1111":{"lch":[46.7325769897078942,150.969760125239,12.1770506300617818],"luv":[46.7325769897078942,147.573010021229663,31.8445471870185592],"rgb":[0.866666666666666696,0.0666666666666666657,0.0666666666666666657],"xyz":[0.301197348290040967,0.158163190525975772,0.0199734860036339529],"hpluv":[12.1770506300617818,409.92986676092562,46.7325769897078942],"hsluv":[12.1770506300617818,96.0592738250283,46.7325769897078942]},"#dd1122":{"lch":[46.8315975390355774,148.166934443607602,11.093898425687982],"luv":[46.8315975390355774,145.398162733087418,28.509905932130728],"rgb":[0.866666666666666696,0.0666666666666666657,0.133333333333333331],"xyz":[0.303072706428518,0.1589133337813666,0.0298503721996131455],"hpluv":[11.093898425687982,401.468660625611221,46.8315975390355774],"hsluv":[11.093898425687982,96.1199649520447821,46.8315975390355774]},"#dd1133":{"lch":[46.9939567691892393,143.840838530693645,9.28627571582045697],"luv":[46.9939567691892393,141.955717705878527,23.2112265902085468],"rgb":[0.866666666666666696,0.0666666666666666657,0.2],"xyz":[0.30616045716097573,0.160148434074349699,0.0461125260572242957],"hpluv":[9.28627571582045697,388.400266865181436,46.9939567691892393],"hsluv":[9.28627571582045697,96.2159198798013477,46.9939567691892393]},"#dd1144":{"lch":[47.2268997120704555,138.191174032002039,6.62861883301083665],"luv":[47.2268997120704555,137.267398010546174,15.951865839373701],"rgb":[0.866666666666666696,0.0666666666666666657,0.266666666666666663],"xyz":[0.310618452405194645,0.161931632172037293,0.0695913010101109342],"hpluv":[6.62861883301083665,371.304486157060069,47.2268997120704555],"hsluv":[6.62861883301083665,96.346372634165391,47.2268997120704555]},"#dd1155":{"lch":[47.5357948285950442,131.646215003298352,3.00154982487266553],"luv":[47.5357948285950442,131.465612026054913,6.89338663571585908],"rgb":[0.866666666666666696,0.0666666666666666657,0.333333333333333315],"xyz":[0.31658086644455663,0.164316597787782126,0.100993348284084836],"hpluv":[3.00154982487266553,351.420379681935685,47.5357948285950442],"hsluv":[3.00154982487266553,96.5074087268808114,47.5357948285950442]},"#dd1166":{"lch":[47.9244613368761776,124.817323026056556,358.307054390798669],"luv":[47.9244613368761776,124.762840903711279,-3.68750010524401839],"rgb":[0.866666666666666696,0.0666666666666666657,0.4],"xyz":[0.324165862218064815,0.167350596097185417,0.140940992691228811],"hpluv":[358.307054390798669,330.488955339688346,47.9244613368761776],"hsluv":[358.307054390798669,96.6928417837132912,47.9244613368761776]},"#dd1177":{"lch":[48.3953520744879313,118.427317384197096,352.504166614065639],"luv":[48.3953520744879313,117.415279069986227,-15.4493282615978131],"rgb":[0.866666666666666696,0.0666666666666666657,0.466666666666666674],"xyz":[0.333479875238266721,0.17107620130526624,0.189994794597626721],"hpluv":[352.504166614065639,310.51856077863863,48.3953520744879313],"hsluv":[352.504166614065639,96.8952584834877229,48.3953520744879313]},"#dd1188":{"lch":[48.949686611979061,113.213254375126112,345.662599129740954],"luv":[48.949686611979061,109.687147393784173,-28.0351683216148473],"rgb":[0.866666666666666696,0.0666666666666666657,0.533333333333333326],"xyz":[0.344620292340210521,0.175532368146043838,0.248667658001198888],"hpluv":[345.662599129740954,293.48552443008623,48.949686611979061],"hsluv":[345.662599129740954,97.1070447043031209,48.949686611979061]},"#dd1199":{"lch":[49.5875717372425,109.808639676001164,338.012756373247385],"luv":[49.5875717372425,101.821953610845213,-41.1123717433662961],"rgb":[0.866666666666666696,0.0666666666666666657,0.6],"xyz":[0.357677256449283276,0.180755153789673,0.317434335642316767],"hpluv":[338.012756373247385,280.997849503034615,49.5875717372425],"hsluv":[338.012756373247385,97.321211179253126,49.5875717372425]},"#dd11aa":{"lch":[50.3081241313593779,108.626384639058173,329.948207544292131],"luv":[50.3081241313593779,94.0240738412992556,-54.3982074892042462],"rgb":[0.866666666666666696,0.0666666666666666657,0.66666666666666663],"xyz":[0.372734949414535777,0.18677823097577409,0.396738185259315368],"hpluv":[329.948207544292131,273.991145484396441,50.3081241313593779],"hsluv":[329.948207544292131,97.5319211216277751,50.3081241313593779]},"#dd11bb":{"lch":[51.1095995740137,109.78676639377484,321.947120969557943],"luv":[51.1095995740137,86.4507346157054855,-67.6712979010019495],"rgb":[0.866666666666666696,0.0666666666666666657,0.733333333333333282],"xyz":[0.389872541521872451,0.193633267818708849,0.486996170357957114],"hpluv":[321.947120969557943,272.57551535007633,51.1095995740137],"hsluv":[321.947120969557943,97.7347175386083791,51.1095995740137]},"#dd11cc":{"lch":[51.9895276454598303,113.13117809908816,314.441471026924035],"luv":[51.9895276454598303,79.2122218564644527,-80.7718228508547469],"rgb":[0.866666666666666696,0.0666666666666666657,0.8],"xyz":[0.409164916965594205,0.20135021799619765,0.588602681028227326],"hpluv":[314.441471026924035,276.12502270002949,51.9895276454598303],"hsluv":[314.441471026924035,97.9265130550616,51.9895276454598303]},"#dd11dd":{"lch":[52.9448482611329325,118.314931067086022,307.715012949243601],"luv":[52.9448482611329325,72.3773062506166553,-93.594596282658145],"rgb":[0.866666666666666696,0.0666666666666666657,0.866666666666666696],"xyz":[0.430683242516227693,0.209957548216451184,0.701932528928233324],"hpluv":[307.715012949243601,283.566663729067216,52.9448482611329325],"hsluv":[307.715012949243601,98.1054292618058525,52.9448482611329325]},"#dd11ee":{"lch":[53.9720454332022257,124.924845967379298,301.881652150577509],"luv":[53.9720454332022257,65.9811112999591103,-106.078791902980541],"rgb":[0.866666666666666696,0.0666666666666666657,0.933333333333333348],"xyz":[0.454495422782402458,0.219482420322921229,0.827343344996756547],"hpluv":[301.881652150577509,293.71036424495378,53.9720454332022257],"hsluv":[301.881652150577509,98.2705657762439841,53.9720454332022257]},"#dd11ff":{"lch":[55.067273793018515,132.56906123155207,296.926443611211937],"luv":[55.067273793018515,60.0334023394080774,-118.197066796810802],"rgb":[0.866666666666666696,0.0666666666666666657,1],"xyz":[0.480666471192233824,0.229950839686853925,0.965177533288538392],"hpluv":[296.926443611211937,305.483621811531123,55.067273793018515],"hsluv":[296.926443611211937,99.9999999999990763,55.067273793018515]},"#dd2200":{"lch":[47.6481385708110494,147.881667770992,13.8451074484812633],"luv":[47.6481385708110494,143.585141583480663,35.3877772568702937],"rgb":[0.866666666666666696,0.133333333333333331,0],"xyz":[0.303901306525171777,0.165189771795656887,0.0158839222838009289],"hpluv":[13.8451074484812633,393.829031299888356,47.6481385708110494],"hsluv":[13.8451074484812633,100.000000000002302,47.6481385708110494]},"#dd2211":{"lch":[47.7000692420668,146.371743885583,13.2640103652051735],"luv":[47.7000692420668,142.467011758872331,33.583298953557744],"rgb":[0.866666666666666696,0.133333333333333331,0.0666666666666666657],"xyz":[0.304912972024808882,0.16559443799551174,0.0212120272485565586],"hpluv":[13.2640103652051735,389.383517616197651,47.7000692420668],"hsluv":[13.2640103652051735,96.2235359913314596,47.7000692420668]},"#dd2222":{"lch":[47.796111526211412,143.672697420673273,12.1770506300617871],"luv":[47.796111526211412,140.440127868319678,30.3053537920670948],"rgb":[0.866666666666666696,0.133333333333333331,0.133333333333333331],"xyz":[0.306788330163285938,0.166344581250902568,0.0310889134445357582],"hpluv":[12.1770506300617871,381.435408792809369,47.796111526211412],"hsluv":[12.1770506300617871,89.3821391188339618,47.796111526211412]},"#dd2233":{"lch":[47.9536166692339805,139.501418135148583,10.3611027729364178],"luv":[47.9536166692339805,137.226679529052944,25.0895214611231978],"rgb":[0.866666666666666696,0.133333333333333331,0.2],"xyz":[0.309876080895743644,0.167579681543885667,0.0473510673021469],"hpluv":[10.3611027729364178,369.144652707036585,47.9536166692339805],"hsluv":[10.3611027729364178,89.634195646670733,47.9536166692339805]},"#dd2244":{"lch":[48.1796580724099073,134.044487106872765,7.68678657448498281],"luv":[48.1796580724099073,132.839973125453383,17.9294747210675],"rgb":[0.866666666666666696,0.133333333333333331,0.266666666666666663],"xyz":[0.314334076139962559,0.169362879641573261,0.0708298422550335399],"hpluv":[7.68678657448498281,353.040533161258963,48.1796580724099073],"hsluv":[7.68678657448498281,89.9776949827144819,48.1796580724099073]},"#dd2255":{"lch":[48.4795139291676236,127.710640494506933,4.02871777991100277],"luv":[48.4795139291676236,127.395062601571667,8.9724979943620351],"rgb":[0.866666666666666696,0.133333333333333331,0.333333333333333315],"xyz":[0.320296490179324544,0.171747845257318094,0.102231889529007441],"hpluv":[4.02871777991100277,334.278275235680212,48.4795139291676236],"hsluv":[4.02871777991100277,90.4030378986951746,48.4795139291676236]},"#dd2266":{"lch":[48.8569858046774499,121.091623353111601,359.280669781128381],"luv":[48.8569858046774499,121.082080247128843,-1.52022673298942879],"rgb":[0.866666666666666696,0.133333333333333331,0.4],"xyz":[0.32788148595283273,0.174781843566721384,0.142179533936151403],"hpluv":[359.280669781128381,314.504423311283745,48.8569858046774499],"hsluv":[359.280669781128381,90.8946273178398627,48.8569858046774499]},"#dd2277":{"lch":[49.3145747506863046,114.897314975199777,353.392641366134796],"luv":[49.3145747506863046,114.134166521370943,-13.2206286152450421],"rgb":[0.866666666666666696,0.133333333333333331,0.466666666666666674],"xyz":[0.337195498973034635,0.178507448774802208,0.191233335842549312],"hpluv":[353.392641366134796,295.64729776044,49.3145747506863046],"hsluv":[353.392641366134796,91.4334620569677128,49.3145747506863046]},"#dd2288":{"lch":[49.8536069462695934,109.863045744465836,346.42832123602642],"luv":[49.8536069462695934,106.795352255968297,-25.780643063628979],"rgb":[0.866666666666666696,0.133333333333333331,0.533333333333333326],"xyz":[0.348335916074978436,0.182963615615579805,0.24990619924612148],"hpluv":[346.42832123602642,279.636833718457694,49.8536069462695934],"hsluv":[346.42832123602642,91.999736258284841,49.8536069462695934]},"#dd2299":{"lch":[50.4743452384724947,106.631383564155598,338.620922954440232],"luv":[50.4743452384724947,99.2939711585852507,-38.8710593162073792],"rgb":[0.866666666666666696,0.133333333333333331,0.6],"xyz":[0.361392880184051135,0.188186401259208963,0.318672876887239387],"hpluv":[338.620922954440232,268.07337181170567,50.4743452384724947],"hsluv":[338.620922954440232,92.5749897333771088,50.4743452384724947]},"#dd22aa":{"lch":[51.176101525576982,105.62881092572934,330.379319008457628],"luv":[51.176101525576982,91.8248770713425699,-52.2076397514415902],"rgb":[0.866666666666666696,0.133333333333333331,0.66666666666666663],"xyz":[0.376450573149303691,0.194209478445310058,0.397976726504238],"hpluv":[330.379319008457628,261.911470041672374,51.176101525576982],"hsluv":[330.379319008457628,93.1435427232804898,51.176101525576982]},"#dd22bb":{"lch":[51.9573548685870321,106.984993341056935,322.205396597718504],"luv":[51.9573548685870321,84.5409044706425306,-65.5638945721781425],"rgb":[0.866666666666666696,0.133333333333333331,0.733333333333333282],"xyz":[0.393588165256640365,0.201064515288244816,0.488234711602879734],"hpluv":[322.205396597718504,261.285408571692301,51.9573548685870321],"hsluv":[322.205396597718504,93.6931794305449301,51.9573548685870321]},"#dd22cc":{"lch":[52.8158750154556174,110.541708291411879,314.55250800555325],"luv":[52.8158750154556174,77.5519304210182838,-78.7728846745956],"rgb":[0.866666666666666696,0.133333333333333331,0.8],"xyz":[0.412880540700362064,0.208781465465733618,0.58984122227315],"hpluv":[314.55250800555325,265.583456637160452,52.8158750154556174],"hsluv":[314.55250800555325,94.2152138812719073,52.8158750154556174]},"#dd22dd":{"lch":[53.7488483860564088,115.947384169062644,307.715012949243658],"luv":[53.7488483860564088,70.9289965118926204,-91.721716891171],"rgb":[0.866666666666666696,0.133333333333333331,0.866666666666666696],"xyz":[0.434398866250995663,0.217388795685987152,0.703171070173155943],"hpluv":[307.715012949243658,273.735496610715643,53.7488483860564088],"hsluv":[307.715012949243658,94.704144859729837,53.7488483860564088]},"#dd22ee":{"lch":[54.7530025006588374,122.779599443276055,301.806367069585178],"luv":[54.7530025006588374,64.7110170709106,-104.342293961267828],"rgb":[0.866666666666666696,0.133333333333333331,0.933333333333333348],"xyz":[0.458211046517170373,0.226913667792457197,0.828581886241679166],"hpluv":[301.806367069585178,284.549350776984397,54.7530025006588374],"hsluv":[301.806367069585178,95.1571011878196629,54.7530025006588374]},"#dd22ff":{"lch":[55.8247247862810525,130.638613434108407,296.804995701950531],"luv":[55.8247247862810525,58.9121835814083425,-116.601037498200881],"rgb":[0.866666666666666696,0.133333333333333331,1],"xyz":[0.484382094927001794,0.237382087156389893,0.966416074533461],"hpluv":[296.804995701950531,296.950662194199822,55.8247247862810525],"hsluv":[296.804995701950531,99.9999999999989768,55.8247247862810525]},"#dd3300":{"lch":[49.1823134049741526,140.84390252957769,15.6779143459349193],"luv":[49.1823134049741526,135.603943521894649,38.06015476941716],"rgb":[0.866666666666666696,0.2,0],"xyz":[0.310019028614182623,0.17742521597367869,0.0179231629801378106],"hpluv":[15.6779143459349193,363.386194305474646,49.1823134049741526],"hsluv":[15.6779143459349193,100.000000000002331,49.1823134049741526]},"#dd3311":{"lch":[49.2318310772226226,139.415415721210906,15.0950854994101622],"luv":[49.2318310772226226,134.60488288385605,36.3067988748864323],"rgb":[0.866666666666666696,0.2,0.0666666666666666657],"xyz":[0.311030694113819728,0.177829882173533543,0.0232512679448934403],"hpluv":[15.0950854994101622,359.338818773581409,49.2318310772226226],"hsluv":[15.0950854994101622,96.4660724045404834,49.2318310772226226]},"#dd3322":{"lch":[49.3234253076193,136.858666305461554,14.0037238091571297],"luv":[49.3234253076193,132.791226888227897,33.117738516222083],"rgb":[0.866666666666666696,0.2,0.133333333333333331],"xyz":[0.312906052252296785,0.178580025428924372,0.0331281541408726399],"hpluv":[14.0037238091571297,352.093818956611813,49.3234253076193],"hsluv":[14.0037238091571297,90.0546185843159321,49.3234253076193]},"#dd3333":{"lch":[49.4736766963079901,132.899088309008249,12.1770506300618315],"luv":[49.4736766963079901,129.908920002044738,28.0328410488111714],"rgb":[0.866666666666666696,0.2,0.2],"xyz":[0.31599380298475449,0.179815125721907471,0.0493903079984837831],"hpluv":[12.1770506300618315,340.868713502871344,49.4736766963079901],"hsluv":[12.1770506300618315,79.876105021286179,49.4736766963079901]},"#dd3344":{"lch":[49.6893958667399289,127.704341644457742,9.47932118493828391],"luv":[49.6893958667399289,125.960552293014786,21.031836364974712],"rgb":[0.866666666666666696,0.2,0.266666666666666663],"xyz":[0.320451798228973406,0.181598323819595064,0.0728690829513704286],"hpluv":[9.47932118493828391,326.122882655454703,49.6893958667399289],"hsluv":[9.47932118493828391,80.502956859434363,49.6893958667399289]},"#dd3355":{"lch":[49.9757165444531495,121.655077778432485,5.77481987360355742],"luv":[49.9757165444531495,121.037681249067731,12.2408197080725127],"rgb":[0.866666666666666696,0.2,0.333333333333333315],"xyz":[0.326414212268335391,0.183983289435339897,0.10427113022534433],"hpluv":[5.77481987360355742,308.89475746512926,49.9757165444531495],"hsluv":[5.77481987360355742,81.2827465905028674,49.9757165444531495]},"#dd3366":{"lch":[50.3364012453720164,115.314978813651479,0.942739432835561],"luv":[50.3364012453720164,115.29936949267524,1.8972963354308543],"rgb":[0.866666666666666696,0.2,0.4],"xyz":[0.333999208041843576,0.187017287744743188,0.144218774632488278],"hpluv":[0.942739432835561,290.698564526315124,50.3364012453720164],"hsluv":[0.942739432835561,82.1889615211792375,50.3364012453720164]},"#dd3377":{"lch":[50.77400791740952,109.376176789803836,354.916348657894],"luv":[50.77400791740952,108.945933542464203,-9.69183231981436],"rgb":[0.866666666666666696,0.2,0.466666666666666674],"xyz":[0.343313221062045482,0.190742892952824,0.193272576538886187],"hpluv":[354.916348657894,273.35096965093777,50.77400791740952],"hsluv":[354.916348657894,83.1884512053650269,50.77400791740952]},"#dd3388":{"lch":[51.2900053848270545,104.574014460431073,347.747121452304157],"luv":[51.2900053848270545,102.191864987712989,-22.1934051173651383],"rgb":[0.866666666666666696,0.2,0.533333333333333326],"xyz":[0.354453638163989282,0.195199059793601609,0.251945439942458382],"hpluv":[347.747121452304157,258.720214242298141,51.2900053848270545],"hsluv":[347.747121452304157,84.245872474104587,51.2900053848270545]},"#dd3399":{"lch":[51.8848727297214509,101.568582070491487,339.671486537323062],"luv":[51.8848727297214509,95.2425012264059347,-35.2851643605103433],"rgb":[0.866666666666666696,0.2,0.6],"xyz":[0.367510602273062,0.200421845437230767,0.320712117583576262],"hpluv":[339.671486537323062,248.403642764366595,51.8848727297214509],"hsluv":[339.671486537323062,85.3275173092541763,51.8848727297214509]},"#dd33aa":{"lch":[52.5581975758694284,100.811065681036794,331.124654782349864],"luv":[52.5581975758694284,88.2774684157494,-48.6822301651504219],"rgb":[0.866666666666666696,0.2,0.66666666666666663],"xyz":[0.382568295238314537,0.206444922623331861,0.400015967200574862],"hpluv":[331.124654782349864,243.392431352391867,52.5581975758694284],"hsluv":[331.124654782349864,86.4040244757801,52.5581975758694284]},"#dd33bb":{"lch":[53.3087788146031301,102.447919096954379,322.651217662355],"luv":[53.3087788146031301,81.4417156804123579,-62.1516136100020091],"rgb":[0.866666666666666696,0.2,0.733333333333333282],"xyz":[0.399705887345651212,0.21329995946626662,0.490273952299216609],"hpluv":[322.651217662355,243.861776984359892,53.3087788146031301],"hsluv":[322.651217662355,87.4518397844355633,53.3087788146031301]},"#dd33cc":{"lch":[54.1347343907921612,106.321268361530926,314.743497623169446],"luv":[54.1347343907921612,74.8431687457043751,-75.5163041872859253],"rgb":[0.866666666666666696,0.2,0.8],"xyz":[0.418998262789372911,0.221016909643755421,0.59188046296948682],"hpluv":[314.743497623169446,249.220329072313831,54.1347343907921612],"hsluv":[314.743497623169446,88.4535853271675734,54.1347343907921612]},"#dd33dd":{"lch":[55.033612168624586,112.066789743934578,307.715012949243715],"luv":[55.033612168624586,68.5551036430149,-88.6519211749340741],"rgb":[0.866666666666666696,0.2,0.866666666666666696],"xyz":[0.44051658834000651,0.229624239864008955,0.705210310869492818],"hpluv":[307.715012949243715,258.397459164480438,55.033612168624586],"hsluv":[307.715012949243715,89.3976510430439077,55.033612168624586]},"#dd33ee":{"lch":[56.0025007026426351,119.245240694744666,301.678101579798295],"luv":[56.0025007026426351,62.6212128139869506,-101.479116738632214],"rgb":[0.866666666666666696,0.2,0.933333333333333348],"xyz":[0.464328768606181219,0.239149111970479,0.830621126938016],"hpluv":[301.678101579798295,270.192295422478139,56.0025007026426351],"hsluv":[301.678101579798295,90.277343199481848,56.0025007026426351]},"#dd33ff":{"lch":[57.0381364623091116,127.442655532056975,296.59904001960814],"luv":[57.0381364623091116,57.0616979163907772,-113.954346472440875],"rgb":[0.866666666666666696,0.2,1],"xyz":[0.49049981701601264,0.249617531334411696,0.968455315229797886],"hpluv":[296.59904001960814,283.523336448078851,57.0381364623091116],"hsluv":[296.59904001960814,99.9999999999989626,57.0381364623091116]},"#dd4400":{"lch":[51.2775121999195278,131.902040393952689,18.409420821930695],"luv":[51.2775121999195278,125.151834557466444,41.6553305951168156],"rgb":[0.866666666666666696,0.266666666666666663,0],"xyz":[0.318851599097148664,0.19509035693961102,0.0208673531411264039],"hpluv":[18.409420821930695,326.410329295551833,51.2775121999195278],"hsluv":[18.409420821930695,100.000000000002245,51.2775121999195278]},"#dd4411":{"lch":[51.3239968707682124,130.563017922041524,17.8265447991253865],"luv":[51.3239968707682124,124.294382399750674,39.9700907276414128],"rgb":[0.866666666666666696,0.266666666666666663,0.0666666666666666657],"xyz":[0.319863264596785768,0.195495023139465873,0.0261954581058820371],"hpluv":[17.8265447991253865,322.804096021626094,51.3239968707682124],"hsluv":[17.8265447991253865,96.7659447415066154,51.3239968707682124]},"#dd4422":{"lch":[51.4099976690743148,128.162025810787327,16.7333496749677373],"luv":[51.4099976690743148,122.735013936090269,36.9001519513490237],"rgb":[0.866666666666666696,0.266666666666666663,0.133333333333333331],"xyz":[0.321738622735262825,0.196245166394856702,0.0360723443018612297],"hpluv":[16.7333496749677373,316.337811591652041,51.4099976690743148],"hsluv":[16.7333496749677373,90.8878404873512551,51.4099976690743148]},"#dd4433":{"lch":[51.551120550377874,124.432504768689228,14.8985084842763058],"luv":[51.551120550377874,120.249428964759446,31.9925472049216744],"rgb":[0.866666666666666696,0.266666666666666663,0.2],"xyz":[0.324826373467720531,0.197480266687839801,0.0523344981594723729],"hpluv":[14.8985084842763058,306.291581325991444,51.551120550377874],"hsluv":[14.8985084842763058,81.5276169642245634,51.551120550377874]},"#dd4444":{"lch":[51.7538349343952575,119.518854000271219,12.1770506300618191],"luv":[51.7538349343952575,116.829734805674121,25.2105042943215345],"rgb":[0.866666666666666696,0.266666666666666663,0.266666666666666663],"xyz":[0.329284368711939446,0.199263464785527394,0.0758132731123590115],"hpluv":[12.1770506300618191,293.044254198223086,51.7538349343952575],"hsluv":[12.1770506300618191,68.6693518559736,51.7538349343952575]},"#dd4455":{"lch":[52.0230766847265045,113.767549619287195,8.41751308754220773],"luv":[52.0230766847265045,112.542004033713866,16.6539086839248256],"rgb":[0.866666666666666696,0.266666666666666663,0.333333333333333315],"xyz":[0.335246782751301431,0.201648430401272227,0.107215320386332927],"hpluv":[8.41751308754220773,277.499175779034886,52.0230766847265045],"hsluv":[8.41751308754220773,69.8262296658756867,52.0230766847265045]},"#dd4466":{"lch":[52.3625377834239316,107.708396397149357,3.47572472865958737],"luv":[52.3625377834239316,107.51027478189765,6.52989056311981209],"rgb":[0.866666666666666696,0.266666666666666663,0.4],"xyz":[0.342831778524809616,0.204682428710675518,0.147162964793476875],"hpluv":[3.47572472865958737,261.016642983210886,52.3625377834239316],"hsluv":[3.47572472865958737,71.1800009153795088,52.3625377834239316]},"#dd4477":{"lch":[52.7748219535637304,102.013560717275851,357.256373562230806],"luv":[52.7748219535637304,101.896624345108023,-4.88308481282748108],"rgb":[0.866666666666666696,0.266666666666666663,0.466666666666666674],"xyz":[0.352145791545011522,0.208408033918756341,0.196216766699874784],"hpluv":[357.256373562230806,245.284698321799027,52.7748219535637304],"hsluv":[357.256373562230806,72.6848757644217898,52.7748219535637304]},"#dd4488":{"lch":[53.2615487789460502,97.4233940898595137,349.787328406912707],"luv":[53.2615487789460502,95.8798587184095368,-17.273401753155067],"rgb":[0.866666666666666696,0.266666666666666663,0.533333333333333326],"xyz":[0.363286208646955322,0.212864200759533939,0.254889630103446951],"hpluv":[349.787328406912707,232.107295472344475,53.2615487789460502],"hsluv":[349.787328406912707,74.290572198007979,53.2615487789460502]},"#dd4499":{"lch":[53.8234397136373133,94.6288173232078123,341.30539151945959],"luv":[53.8234397136373133,89.63624285692255,-30.330793502376995],"rgb":[0.866666666666666696,0.266666666666666663,0.6],"xyz":[0.376343172756028,0.218086986403163097,0.323656307744564886],"hpluv":[341.30539151945959,223.095746339835301,53.8234397136373133],"hsluv":[341.30539151945959,75.9477058190526577,53.8234397136373133]},"#dd44aa":{"lch":[54.4604007326765327,94.1226543521493539,332.285935566136516],"luv":[54.4604007326765327,83.3248558176969425,-43.7726223255380802],"rgb":[0.866666666666666696,0.266666666666666663,0.66666666666666663],"xyz":[0.391400865721280578,0.224110063589264191,0.402960157361563487],"hpluv":[332.285935566136516,219.307083848265933,54.4604007326765327],"hsluv":[332.285935566136516,77.6118806813578175,54.4604007326765327]},"#dd44bb":{"lch":[55.1716077278512387,96.0796222427451596,323.344264550451],"luv":[55.1716077278512387,77.0786382371216,-57.3600674495728526],"rgb":[0.866666666666666696,0.266666666666666663,0.733333333333333282],"xyz":[0.408538457828617252,0.23096510043219895,0.493218142460205233],"hpluv":[323.344264550451,220.981019921853772,55.1716077278512387],"hsluv":[323.344264550451,79.2461809113807334,55.1716077278512387]},"#dd44cc":{"lch":[55.9555962107488511,100.342448880687911,315.038748994660807],"luv":[55.9555962107488511,71.0007950362134,-70.9048246002984],"rgb":[0.866666666666666696,0.266666666666666663,0.8],"xyz":[0.427830833272338951,0.238682050609687751,0.594824653130475389],"hpluv":[315.038748994660807,227.551915064947707,55.9555962107488511],"hsluv":[315.038748994660807,80.8221578956601547,55.9555962107488511]},"#dd44dd":{"lch":[56.8103543983327484,106.525561993860563,307.715012949243828],"luv":[56.8103543983327484,65.1653443433699664,-84.2684594300729515],"rgb":[0.866666666666666696,0.266666666666666663,0.866666666666666696],"xyz":[0.44934915882297255,0.247289380829941285,0.708154501030481387],"hpluv":[307.715012949243828,237.939016458104504,56.8103543983327484],"hsluv":[307.715012949243828,82.3196529548951474,56.8103543983327484]},"#dd44ee":{"lch":[57.7334174818232384,114.162073161526394,301.482814132357476],"luv":[57.7334174818232384,59.6203198077272134,-97.3570563162323452],"rgb":[0.866666666666666696,0.266666666666666663,0.933333333333333348],"xyz":[0.473161339089147259,0.256814252936411302,0.83356531709900461],"hpluv":[301.482814132357476,250.91920763959763,57.7334174818232384],"hsluv":[301.482814132357476,85.8927976857303577,57.7334174818232384]},"#dd44ff":{"lch":[58.721960397178492,122.814959128704743,296.287773174859751],"luv":[58.721960397178492,54.3922733794591196,-110.113554035820684],"rgb":[0.866666666666666696,0.266666666666666663,1],"xyz":[0.49933238749897868,0.267282672300344,0.971399505390786455],"hpluv":[296.287773174859751,265.393357493052918,58.721960397178492],"hsluv":[296.287773174859751,99.9999999999987779,58.721960397178492]},"#dd5500":{"lch":[53.8905970004369834,121.845910621274882,22.2085433527856502],"luv":[53.8905970004369834,112.806678507237621,46.055175814369008],"rgb":[0.866666666666666696,0.333333333333333315,0],"xyz":[0.330664855811494629,0.218716870368303284,0.0248051053792416147],"hpluv":[22.2085433527856502,286.904453583707834,53.8905970004369834],"hsluv":[22.2085433527856502,100.000000000002217,53.8905970004369834]},"#dd5511":{"lch":[53.9336739056601573,120.58904296901602,21.6306448037720749],"luv":[53.9336739056601573,112.097097503939963,44.4517492948856656],"rgb":[0.866666666666666696,0.333333333333333315,0.0666666666666666657],"xyz":[0.331676521311131733,0.219121536568158137,0.0301332103439972479],"hpluv":[21.6306448037720749,283.71818310283129,53.9336739056601573],"hsluv":[21.6306448037720749,97.0955711707227493,53.9336739056601573]},"#dd5522":{"lch":[54.0133869328819856,118.329807147086328,20.5443811064534074],"luv":[54.0133869328819856,110.804107011743554,41.5258128011568957],"rgb":[0.866666666666666696,0.333333333333333315,0.133333333333333331],"xyz":[0.33355187944960879,0.219871679823548966,0.0400100965399764405],"hpluv":[20.5443811064534074,277.99185559962018,54.0133869328819856],"hsluv":[20.5443811064534074,91.805999212684128,54.0133869328819856]},"#dd5533":{"lch":[54.1442392255445526,114.805940194650788,18.7140712474621722],"luv":[54.1442392255445526,108.736323489647134,36.8349814433582736],"rgb":[0.866666666666666696,0.333333333333333315,0.2],"xyz":[0.336639630182066496,0.221106780116532065,0.0562722503975875837],"hpluv":[18.7140712474621722,269.061420184977408,54.1442392255445526],"hsluv":[18.7140712474621722,83.3546452149230674,54.1442392255445526]},"#dd5544":{"lch":[54.3323026853354207,110.135081135525823,15.9827981501284579],"luv":[54.3323026853354207,105.877744255237971,30.3255563535325479],"rgb":[0.866666666666666696,0.333333333333333315,0.266666666666666663],"xyz":[0.341097625426285411,0.222889978214219658,0.0797510253504742223],"hpluv":[15.9827981501284579,257.221277658214035,54.3323026853354207],"hsluv":[15.9827981501284579,71.6878656400709104,54.3323026853354207]},"#dd5555":{"lch":[54.5822696158357132,104.625049281135261,12.1770506300618937],"luv":[54.5822696158357132,102.271033836367579,22.0689051636128752],"rgb":[0.866666666666666696,0.333333333333333315,0.333333333333333315],"xyz":[0.347060039465647396,0.225274943829964491,0.111153072624448138],"hpluv":[12.1770506300618937,243.233512665758,54.5822696158357132],"hsluv":[12.1770506300618937,61.8784513389384,54.5822696158357132]},"#dd5566":{"lch":[54.8977244977922254,98.7674373059184205,7.11744080010036573],"luv":[54.8977244977922254,98.0063618739055897,12.2376347477602359],"rgb":[0.866666666666666696,0.333333333333333315,0.4],"xyz":[0.354645035239155582,0.228308942139367782,0.151100717031592086],"hpluv":[7.11744080010036573,228.296245111676484,54.8977244977922254],"hsluv":[7.11744080010036573,62.7979151590930655,54.8977244977922254]},"#dd5577":{"lch":[55.2812881935381597,93.2135187108595886,0.661514811515945267],"luv":[55.2812881935381597,93.2073060454778783,1.07618316489082022],"rgb":[0.866666666666666696,0.333333333333333315,0.466666666666666674],"xyz":[0.363959048259357487,0.232034547347448605,0.20015451893799],"hpluv":[0.661514811515945267,213.963687644495565,55.2812881935381597],"hsluv":[0.661514811515945267,63.8225359413096456,55.2812881935381597]},"#dd5588":{"lch":[55.7347110848163538,88.7163187329837,352.791835078768599],"luv":[55.7347110848163538,88.0151786426913532,-11.1316457915027787],"rgb":[0.866666666666666696,0.333333333333333315,0.533333333333333326],"xyz":[0.375099465361301287,0.236490714188226203,0.258827382341562162],"hpluv":[352.791835078768599,201.984054112510194,55.7347110848163538],"hsluv":[352.791835078768599,64.9173177009572,55.7347110848163538]},"#dd5599":{"lch":[56.2589463845586408,86.0173277442407169,343.733753082454825],"luv":[56.2589463845586408,82.5740942724548717,-24.0935598727929836],"rgb":[0.866666666666666696,0.333333333333333315,0.6],"xyz":[0.388156429470374,0.241713499831855361,0.327594059982680097],"hpluv":[343.733753082454825,194.014271994181229,56.2589463845586408],"hsluv":[343.733753082454825,66.0466553819121,56.2589463845586408]},"#dd55aa":{"lch":[56.8542178605490278,85.677853149792881,334.018383993549605],"luv":[56.8542178605490278,77.0187914763158545,-37.5339883290498477],"rgb":[0.866666666666666696,0.333333333333333315,0.66666666666666663],"xyz":[0.403214122435626543,0.247736577017956455,0.406897909599678698],"hpluv":[334.018383993549605,191.225239227061849,56.8542178605490278],"hsluv":[334.018383993549605,67.3075554564469485,56.8542178605490278]},"#dd55bb":{"lch":[57.5200884026389332,87.9206422548043633,324.375348896731964],"luv":[57.5200884026389332,71.4663144117056817,-51.2113780219251638],"rgb":[0.866666666666666696,0.333333333333333315,0.733333333333333282],"xyz":[0.420351714542963217,0.254591613860891242,0.497155894698320444],"hpluv":[324.375348896731964,193.959311397505388,57.5200884026389332],"hsluv":[324.375348896731964,69.5366313504716,57.5200884026389332]},"#dd55cc":{"lch":[58.2555317670128829,92.5910381182408315,315.474469199465034],"luv":[58.2555317670128829,66.0116746837007895,-64.9273374262858596],"rgb":[0.866666666666666696,0.333333333333333315,0.8],"xyz":[0.439644089986684916,0.262308564038380043,0.5987624053685906],"hpluv":[315.474469199465034,201.683843426221756,58.2555317670128829],"hsluv":[315.474469199465034,71.7082017587958802,58.2555317670128829]},"#dd55dd":{"lch":[59.0590075291469532,99.2700295618383848,307.715012949244056],"luv":[59.0590075291469532,60.7268860008133231,-78.5288742174016363],"rgb":[0.866666666666666696,0.333333333333333315,0.866666666666666696],"xyz":[0.461162415537318515,0.270915894258633549,0.712092253268596598],"hpluv":[307.715012949244056,213.290412049590259,59.0590075291469532],"hsluv":[307.715012949244056,73.7919865345850923,59.0590075291469532]},"#dd55ee":{"lch":[59.9285380001613674,107.447476486145689,301.201070052482692],"luv":[59.9285380001613674,55.6624113436061592,-91.905691698915],"rgb":[0.866666666666666696,0.333333333333333315,0.933333333333333348],"xyz":[0.484974595803493225,0.280440766365103566,0.837503069337119821],"hpluv":[301.201070052482692,227.510719406260364,59.9285380001613674],"hsluv":[301.201070052482692,84.9240358585231405,59.9285380001613674]},"#dd55ff":{"lch":[60.8617852443614,116.651127585902827,295.843561463814751],"luv":[60.8617852443614,50.8500320114402413,-104.984569397117028],"rgb":[0.866666666666666696,0.333333333333333315,1],"xyz":[0.511145644213324646,0.29090918572903629,0.975337257628901666],"hpluv":[295.843561463814751,243.211205533984923,60.8617852443614],"hsluv":[295.843561463814751,99.9999999999987,60.8617852443614]},"#dd6600":{"lch":[56.9556719941368783,111.624872970007345,27.247071009398578],"luv":[56.9556719941368783,99.2390381331236568,51.1050445257873349],"rgb":[0.866666666666666696,0.4,0],"xyz":[0.345692913517341105,0.248772985779996625,0.0298144579478569621],"hpluv":[27.247071009398578,248.69286407076,56.9556719941368783],"hsluv":[27.247071009398578,100.000000000002402,56.9556719941368783]},"#dd6611":{"lch":[56.9952083090352204,110.431802069127656,26.6846840374429064],"luv":[56.9952083090352204,98.6698726856829325,49.5927326573774181],"rgb":[0.866666666666666696,0.4,0.0666666666666666657],"xyz":[0.346704579016978209,0.249177651979851478,0.0351425629126125919],"hpluv":[26.6846840374429064,245.864111809588593,56.9952083090352204],"hsluv":[26.6846840374429064,97.4289366186781,56.9952083090352204]},"#dd6622":{"lch":[57.0683850250612181,108.280332786330632,25.6245441521289443],"luv":[57.0683850250612181,97.630674939023308,46.8282156319165637],"rgb":[0.866666666666666696,0.4,0.133333333333333331],"xyz":[0.348579937155455266,0.249927795235242306,0.0450194491085917914],"hpluv":[25.6245441521289443,240.764984433182946,57.0683850250612181],"hsluv":[25.6245441521289443,92.7369917538900239,57.0683850250612181]},"#dd6633":{"lch":[57.1885511035567617,104.905985735707603,23.8291565941278485],"luv":[57.1885511035567617,95.9631904105633566,42.3831561992078036],"rgb":[0.866666666666666696,0.4,0.2],"xyz":[0.351667687887912972,0.251162895528225405,0.0612816029662029346],"hpluv":[23.8291565941278485,232.771873337598265,57.1885511035567617],"hsluv":[23.8291565941278485,85.2149281001260306,57.1885511035567617]},"#dd6644":{"lch":[57.3613500282636153,100.395707721520722,21.1283100845564071],"luv":[57.3613500282636153,93.6466609873464222,36.1884099516133162],"rgb":[0.866666666666666696,0.4,0.266666666666666663],"xyz":[0.356125683132131887,0.252946093625912971,0.0847603779190895801],"hpluv":[21.1283100845564071,222.093121414046323,57.3613500282636153],"hsluv":[21.1283100845564071,74.7790089775498785,57.3613500282636153]},"#dd6655":{"lch":[57.5911977652478555,95.0134939889914136,17.3206113628181804],"luv":[57.5911977652478555,90.7049894048208927,28.2872575034667619],"rgb":[0.866666666666666696,0.4,0.333333333333333315],"xyz":[0.362088097171493872,0.255331059241657832,0.116162425193063482],"hpluv":[17.3206113628181804,209.347849744778358,57.5911977652478555],"hsluv":[17.3206113628181804,61.5516693045169205,57.5911977652478555]},"#dd6666":{"lch":[57.8815358558834703,89.2064417623026742,12.1770506300619559],"luv":[57.8815358558834703,87.1993378887650579,18.8166076552624659],"rgb":[0.866666666666666696,0.4,0.4],"xyz":[0.369673092945002058,0.25836505755106115,0.156110069600207457],"hpluv":[12.1770506300619559,195.566965385494854,57.8815358558834703],"hsluv":[12.1770506300619559,60.6635523422702,57.8815358558834703]},"#dd6677":{"lch":[58.2349645673757834,83.6008411979688901,5.48003957367995387],"luv":[58.2349645673757834,83.2187459143753188,7.98379467713524704],"rgb":[0.866666666666666696,0.4,0.466666666666666674],"xyz":[0.378987105965203963,0.262090662759141946,0.205163871506605366],"hpluv":[5.48003957367995387,182.165511808695555,58.2349645673757834],"hsluv":[5.48003957367995387,61.6238417996400329,58.2349645673757834]},"#dd6688":{"lch":[58.6533262634944208,78.9686504698842,357.125632416080862],"luv":[58.6533262634944208,78.8692993006444,-3.95997283577995],"rgb":[0.866666666666666696,0.4,0.533333333333333326],"xyz":[0.390127523067147763,0.266546829599919544,0.263836734910177506],"hpluv":[357.125632416080862,170.8446551494321,58.6533262634944208],"hsluv":[357.125632416080862,62.6596910240402778,58.6533262634944208]},"#dd6699":{"lch":[59.1377678367746853,76.1279623836736192,347.292482429378936],"luv":[59.1377678367746853,74.2632604984211184,-16.746187531306056],"rgb":[0.866666666666666696,0.4,0.6],"xyz":[0.403184487176220463,0.271769615243548701,0.332603412551295441],"hpluv":[347.292482429378936,163.349798950832565,59.1377678367746853],"hsluv":[347.292482429378936,63.7390009960191435,59.1377678367746853]},"#dd66aa":{"lch":[59.6887956605557406,75.751758533498986,336.577260120739709],"luv":[59.6887956605557406,69.5095811529117,-30.1122408476074845],"rgb":[0.866666666666666696,0.4,0.66666666666666663],"xyz":[0.418242180141473,0.277792692429649823,0.411907262168294042],"hpluv":[336.577260120739709,161.042027442088397,59.6887956605557406],"hsluv":[336.577260120739709,64.8305690383666473,59.6887956605557406]},"#dd66bb":{"lch":[60.3063295400458372,78.1477005551706,325.894443158435308],"luv":[60.3063295400458372,64.706761589723726,-43.8189240697585518],"rgb":[0.866666666666666696,0.4,0.733333333333333282],"xyz":[0.435379772248809693,0.284647729272584582,0.502165247266935788],"hpluv":[325.894443158435308,164.434383230317081,60.3063295400458372],"hsluv":[325.894443158435308,65.9059953936301213,60.3063295400458372]},"#dd66cc":{"lch":[60.9897585015337427,83.1712006992562891,316.109248272524042],"luv":[60.9897585015337427,59.9384091909213694,-57.6613885491638314],"rgb":[0.866666666666666696,0.4,0.8],"xyz":[0.454672147692531392,0.292364679450073384,0.603771757937205944],"hpluv":[316.109248272524042,173.043537218511,60.9897585015337427],"hsluv":[316.109248272524042,66.9408479310096709,60.9897585015337427]},"#dd66dd":{"lch":[61.7379991889007158,90.3518241723814555,307.715012949244283],"luv":[61.7379991889007158,55.2713135142553753,-71.4740094977595675],"rgb":[0.866666666666666696,0.4,0.866666666666666696],"xyz":[0.476190473243165,0.30097200967032689,0.717101605837211942],"hpluv":[307.715012949244283,185.705044478150711,61.7379991889007158],"hsluv":[307.715012949244283,67.9151328937623759,61.7379991889007158]},"#dd66ee":{"lch":[62.5495564285741779,99.1126076890086125,300.803780654240427],"luv":[62.5495564285741779,50.7555210146825431,-85.1304063742412183],"rgb":[0.866666666666666696,0.4,0.933333333333333348],"xyz":[0.500002653509339701,0.310496881776796907,0.842512421905735165],"hpluv":[300.803780654240427,201.068480083703747,62.5495564285741779],"hsluv":[300.803780654240427,83.670703518064812,62.5495564285741779]},"#dd66ff":{"lch":[63.4225848554444696,108.928953372808309,295.226788235463459],"luv":[63.4225848554444696,46.4257694276883512,-98.5401685402585628],"rgb":[0.866666666666666696,0.4,1],"xyz":[0.526173701919171122,0.320965301140729631,0.980346610197517],"hpluv":[295.226788235463459,217.940889521273107,63.4225848554444696],"hsluv":[295.226788235463459,99.9999999999986073,63.4225848554444696]},"#dd7700":{"lch":[60.3985006876916088,102.209421710697811,33.6568691403047779],"luv":[60.3985006876916088,85.0762157619203,56.6463008330327753],"rgb":[0.866666666666666696,0.466666666666666674,0],"xyz":[0.364146651570758706,0.285680461886832382,0.0359657039656626626],"hpluv":[33.6568691403047779,214.735624532269611,60.3985006876916088],"hsluv":[33.6568691403047779,100.000000000002245,60.3985006876916088]},"#dd7711":{"lch":[60.4345564785723894,101.058408313912437,33.1281817520222],"luv":[60.4345564785723894,84.6313647704988,55.2298287886556736],"rgb":[0.866666666666666696,0.466666666666666674,0.0666666666666666657],"xyz":[0.36515831707039581,0.286085128086687235,0.0412938089304182923],"hpluv":[33.1281817520222,212.190746676882327,60.4345564785723894],"hsluv":[33.1281817520222,97.7465438968969238,60.4345564785723894]},"#dd7722":{"lch":[60.5013044729937803,98.9745052833742136,32.1281810047412648],"luv":[60.5013044729937803,83.8175940663154364,52.6361436754539582],"rgb":[0.866666666666666696,0.466666666666666674,0.133333333333333331],"xyz":[0.367033675208872867,0.286835271342078035,0.0511706951263974918],"hpluv":[32.1281810047412648,207.585936448863947,60.5013044729937803],"hsluv":[32.1281810047412648,93.6262478826975126,60.5013044729937803]},"#dd7733":{"lch":[60.6109510167574115,95.6833141687104813,30.4242910043150516],"luv":[60.6109510167574115,82.5076313520097813,48.4539717565960331],"rgb":[0.866666666666666696,0.466666666666666674,0.2],"xyz":[0.370121425941330573,0.288070371635061162,0.067432848984008642],"hpluv":[30.4242910043150516,200.320058173129354,60.6109510167574115],"hsluv":[30.4242910043150516,86.9991150080846722,60.6109510167574115]},"#dd7744":{"lch":[60.7687036553482756,91.2360291121886888,27.8356422373186483],"luv":[60.7687036553482756,80.6791699404734857,42.6014617809804932],"rgb":[0.866666666666666696,0.466666666666666674,0.266666666666666663],"xyz":[0.374579421185549488,0.289853569732748728,0.0909116239368952805],"hpluv":[27.8356422373186483,190.513488615549676,60.7687036553482756],"hsluv":[27.8356422373186483,77.760618312285672,60.7687036553482756]},"#dd7755":{"lch":[60.9786842032445122,85.8448802225428125,24.131655223886618],"luv":[60.9786842032445122,78.3427622472179479,35.096368243717265],"rgb":[0.866666666666666696,0.466666666666666674,0.333333333333333315],"xyz":[0.380541835224911473,0.292238535348493589,0.122313671210869182],"hpluv":[24.131655223886618,178.63875223296418,60.9786842032445122],"hsluv":[24.131655223886618,65.9765814384596894,60.9786842032445122]},"#dd7766":{"lch":[61.2441632046235895,79.8999585154359693,19.0216316495787474],"luv":[61.2441632046235895,75.5370685872685073,26.0414024201977448],"rgb":[0.866666666666666696,0.466666666666666674,0.4],"xyz":[0.388126830998419659,0.295272533657896907,0.162261315618013158],"hpluv":[19.0216316495787474,165.546946717194828,61.2441632046235895],"hsluv":[19.0216316495787474,57.9572057581871576,61.2441632046235895]},"#dd7777":{"lch":[61.5676827516498122,73.9875996712566,12.1770506300619097],"luv":[61.5676827516498122,72.3229127387857318,15.6064473244910626],"rgb":[0.866666666666666696,0.466666666666666674,0.466666666666666674],"xyz":[0.397440844018621564,0.298998138865977703,0.211315117524411067],"hpluv":[12.1770506300619097,152.491436777287873,61.5676827516498122],"hsluv":[12.1770506300619097,58.8480912007490744,61.5676827516498122]},"#dd7788":{"lch":[61.9511315612573,68.8928540500868252,3.33484257213660307],"luv":[61.9511315612573,68.7761927043778769,4.00757485979294081],"rgb":[0.866666666666666696,0.466666666666666674,0.533333333333333326],"xyz":[0.408581261120565364,0.303454305706755301,0.269987980927983207],"hpluv":[3.33484257213660307,141.112101622994089,61.9511315612573],"hsluv":[3.33484257213660307,59.8177792812577849,61.9511315612573]},"#dd7799":{"lch":[62.395798681375723,65.5359342889949659,352.534540584191575],"luv":[62.395798681375723,64.9804103256140309,-8.5149842657693231],"rgb":[0.866666666666666696,0.466666666666666674,0.6],"xyz":[0.421638225229638119,0.308677091350384458,0.338754658569101141],"hpluv":[352.534540584191575,133.27953643418,62.395798681375723],"hsluv":[352.534540584191575,60.8378233331756135,62.395798681375723]},"#dd77aa":{"lch":[62.9024183325334576,64.7691926058513587,340.409048511657261],"luv":[62.9024183325334576,61.0197310981447743,-21.7172909803219412],"rgb":[0.866666666666666696,0.466666666666666674,0.66666666666666663],"xyz":[0.43669591819489062,0.314700168536485581,0.418058508186099742],"hpluv":[340.409048511657261,130.659342151792146,62.9024183325334576],"hsluv":[340.409048511657261,61.8795186032410598,62.9024183325334576]},"#dd77bb":{"lch":[63.4712121738611472,67.0588140204747,328.168414880738283],"luv":[63.4712121738611472,56.973307333119962,-35.3684434115847779],"rgb":[0.866666666666666696,0.466666666666666674,0.733333333333333282],"xyz":[0.453833510302227294,0.321555205379420339,0.508316493284741489],"hpluv":[328.168414880738283,134.065922948238125,63.4712121738611472],"hsluv":[328.168414880738283,62.9156692244191049,63.4712121738611472]},"#dd77cc":{"lch":[64.1019320742502856,72.2937871392105649,317.045265551381931],"luv":[64.1019320742502856,52.9112642996390861,-49.2624580095555586],"rgb":[0.866666666666666696,0.466666666666666674,0.8],"xyz":[0.473125885745949049,0.329272155556909141,0.609923003955011644],"hpluv":[317.045265551381931,143.109736775843601,64.1019320742502856],"hsluv":[317.045265551381931,63.9218174738772262,64.1019320742502856]},"#dd77dd":{"lch":[64.7939046430230547,79.9242419089019904,307.715012949244624],"luv":[64.7939046430230547,48.8924033620837761,-63.225132172198613],"rgb":[0.866666666666666696,0.466666666666666674,0.866666666666666696],"xyz":[0.494644211296582537,0.337879485777162647,0.723252851855017642],"hpluv":[307.715012949244624,156.524995415635317,64.7939046430230547],"hsluv":[307.715012949244624,64.8769154463944,64.7939046430230547]},"#dd77ee":{"lch":[65.5460776792256,89.2673698488479772,300.244676230176196],"luv":[65.5460776792256,44.9634126652007424,-77.116501743983946],"rgb":[0.866666666666666696,0.466666666666666674,0.933333333333333348],"xyz":[0.518456391562757357,0.347404357883632664,0.848663667923540865],"hpluv":[300.244676230176196,172.816560464305326,65.5460776792256],"hsluv":[300.244676230176196,82.07388698168063,65.5460776792256]},"#dd77ff":{"lch":[66.3570680439545,99.7204006423648366,294.377473048092611],"luv":[66.3570680439545,41.1592308753033436,-90.8299290874281837],"rgb":[0.866666666666666696,0.466666666666666674,1],"xyz":[0.544627439972588667,0.357872777247565388,0.98649785621532271],"hpluv":[294.377473048092611,190.693615319177383,66.3570680439545],"hsluv":[294.377473048092611,99.9999999999983373,66.3570680439545]},"#dd8800":{"lch":[64.1467534130096766,94.4821411478558701,41.4445641191571781],"luv":[64.1467534130096766,70.8234804869986192,62.5372657508392],"rgb":[0.866666666666666696,0.533333333333333326,0],"xyz":[0.386219021295729159,0.329825201336773954,0.0433231605406526124],"hpluv":[41.4445641191571781,186.902182331454583,64.1467534130096766],"hsluv":[41.4445641191571781,100.000000000002416,64.1467534130096766]},"#dd8811":{"lch":[64.1795176641247593,93.3565504581455,40.9755438084832377],"luv":[64.1795176641247593,70.4832194968749235,61.2173282886440333],"rgb":[0.866666666666666696,0.533333333333333326,0.0666666666666666657],"xyz":[0.387230686795366263,0.330229867536628807,0.0486512655054082421],"hpluv":[40.9755438084832377,184.581288668542754,64.1795176641247593],"hsluv":[40.9755438084832377,98.0366401848460072,64.1795176641247593]},"#dd8822":{"lch":[64.2401831232140665,91.3094921720035444,40.0852847486271529],"luv":[64.2401831232140665,69.8596876954725303,58.7966614341343359],"rgb":[0.866666666666666696,0.533333333333333326,0.133333333333333331],"xyz":[0.38910604493384332,0.330980010792019608,0.0585281517013874417],"hpluv":[40.0852847486271529,180.363429125237786,64.2401831232140665],"hsluv":[40.0852847486271529,94.440421143342,64.2401831232140665]},"#dd8833":{"lch":[64.3398685532121704,88.0506151089842319,38.5586382769189342],"luv":[64.3398685532121704,68.8529964065635,54.8832916916272],"rgb":[0.866666666666666696,0.533333333333333326,0.2],"xyz":[0.392193795666301,0.332215111085002734,0.0747903055589985849],"hpluv":[38.5586382769189342,173.656703033806934,64.3398685532121704],"hsluv":[38.5586382769189342,88.6389494712849171,64.3398685532121704]},"#dd8844":{"lch":[64.4833562447751376,83.590391105324656,36.2144134416389392],"luv":[64.4833562447751376,67.4417066096926305,49.3859260793323571],"rgb":[0.866666666666666696,0.533333333333333326,0.266666666666666663],"xyz":[0.396651790910519941,0.333998309182690301,0.0982690805118852234],"hpluv":[36.2144134416389392,164.493238171948974,64.4833562447751376],"hsluv":[36.2144134416389392,80.515719620269337,64.4833562447751376]},"#dd8855":{"lch":[64.6744699451661234,78.0792033713498768,32.8038905377638059],"luv":[64.6744699451661234,65.6278986079903746,42.3006019272060527],"rgb":[0.866666666666666696,0.533333333333333326,0.333333333333333315],"xyz":[0.402614204949881926,0.336383274798435161,0.129671127785859125],"hpluv":[32.8038905377638059,153.194023310958244,64.6744699451661234],"hsluv":[32.8038905377638059,70.0933757373770874,64.6744699451661234]},"#dd8866":{"lch":[64.9162913059566762,71.8298791231791114,27.9793586637294176],"luv":[64.9162913059566762,63.4341634455056251,33.69923504798588],"rgb":[0.866666666666666696,0.533333333333333326,0.4],"xyz":[0.410199200723390112,0.33941727310783848,0.169618772193003087],"hpluv":[27.9793586637294176,140.40764710249897,64.9162913059566762],"hsluv":[27.9793586637294176,57.516131894534638,64.9162913059566762]},"#dd8877":{"lch":[65.211273186305,65.3549274640793243,21.2774014292581],"luv":[65.211273186305,60.8999714810578325,23.7162395299384023],"rgb":[0.866666666666666696,0.533333333333333326,0.466666666666666674],"xyz":[0.419513213743592,0.343142878315919275,0.218672574099401],"hpluv":[21.2774014292581,127.173019810527066,65.211273186305],"hsluv":[21.2774014292581,55.3192807564291797,65.211273186305]},"#dd8888":{"lch":[65.5613077882642585,59.4140252364257222,12.17705063006205],"luv":[65.5613077882642585,58.0772370198051675,12.5323954190731097],"rgb":[0.866666666666666696,0.533333333333333326,0.533333333333333326],"xyz":[0.430653630845535818,0.347599045156696873,0.277345437502973191],"hpluv":[12.17705063006205,114.995459986814794,65.5613077882642585],"hsluv":[12.17705063006205,56.217054427929,65.5613077882642585]},"#dd8899":{"lch":[65.9677735951440809,55.0265961591163304,0.371909902430330563],"luv":[65.9677735951440809,55.0254369244830599,0.357178006339119947],"rgb":[0.866666666666666696,0.533333333333333326,0.6],"xyz":[0.443710594954608517,0.352821830800326031,0.34611211514409107],"hpluv":[0.371909902430330563,105.847388780281833,65.9677735951440809],"hsluv":[0.371909902430330563,57.1692646853200088,65.9677735951440809]},"#dd88aa":{"lch":[66.4315725960282,53.3143351650078756,346.342357286170909],"luv":[66.4315725960282,51.8068159744011751,-12.5885723051236678],"rgb":[0.866666666666666696,0.533333333333333326,0.66666666666666663],"xyz":[0.458768287919861073,0.358844907986427153,0.425415964761089671],"hpluv":[346.342357286170909,101.837748961685463,66.4315725960282],"hsluv":[346.342357286170909,58.1497775423036174,66.4315725960282]},"#dd88bb":{"lch":[66.9531637379342754,55.0554122119122695,331.715662007953938],"luv":[66.9531637379342754,48.482176665295448,-26.0878699709014299],"rgb":[0.866666666666666696,0.533333333333333326,0.733333333333333282],"xyz":[0.475905880027197747,0.365699944829361911,0.515673949859731473],"hpluv":[331.715662007953938,104.344182679937973,66.9531637379342754],"hsluv":[331.715662007953938,59.1328526037280824,66.9531637379342754]},"#dd88cc":{"lch":[67.5325957217288391,60.2482932018326949,318.477460357427788],"luv":[67.5325957217288391,45.1075955230381,-39.9394749572899741],"rgb":[0.866666666666666696,0.533333333333333326,0.8],"xyz":[0.495198255470919446,0.373416895006850713,0.617280460530001629],"hpluv":[318.477460357427788,113.206309259525241,67.5325957217288391],"hsluv":[318.477460357427788,60.0943355072680134,67.5325957217288391]},"#dd88dd":{"lch":[68.1695406599531566,68.219499196860184,307.715012949245079],"luv":[68.1695406599531566,41.7322103060295,-53.965940125636024],"rgb":[0.866666666666666696,0.533333333333333326,0.866666666666666696],"xyz":[0.516716581021553,0.382024225227104219,0.730610308430007627],"hpluv":[307.715012949245079,126.986480244117786,68.1695406599531566],"hsluv":[307.715012949245079,61.0124281772922785,68.1695406599531566]},"#dd88ee":{"lch":[68.8633291469121,78.1074456653267504,299.445313850889818],"luv":[68.8633291469121,38.3970440138518896,-68.0179393936650456],"rgb":[0.866666666666666696,0.533333333333333326,0.933333333333333348],"xyz":[0.540528761287727755,0.391549097333574236,0.85602112449853085],"hpluv":[299.445313850889818,143.92748858218269,68.8633291469121],"hsluv":[299.445313850889818,80.0342386911036385,68.8633291469121]},"#dd88ff":{"lch":[69.6129866887261244,89.1875622693819281,293.199992160066699],"luv":[69.6129866887261244,35.1347073752451706,-81.975445111391565],"rgb":[0.866666666666666696,0.533333333333333326,1],"xyz":[0.566699809697559176,0.40201751669750696,0.993855312790312695],"hpluv":[293.199992160066699,162.574846632166981,69.6129866887261244],"hsluv":[293.199992160066699,99.9999999999981242,69.6129866887261244]},"#dd9900":{"lch":[68.1357569139589287,89.1370219700488775,50.3810095729648921],"luv":[68.1357569139589287,56.8408371488252229,68.6624199829113735],"rgb":[0.866666666666666696,0.6,0],"xyz":[0.41208862174819666,0.381564402241709621,0.0519463606914748674],"hpluv":[50.3810095729648921,166.005456049637957,68.1357569139589287],"hsluv":[50.3810095729648921,100.000000000002245,68.1357569139589287]},"#dd9911":{"lch":[68.1654896561650077,88.0318570495471278,50.0002977758109424],"luv":[68.1654896561650077,56.5854364905009248,67.4366089951992507],"rgb":[0.866666666666666696,0.6,0.0666666666666666657],"xyz":[0.413100287247833764,0.381969068441564474,0.0572744656562305],"hpluv":[50.0002977758109424,163.875726960716179,68.1654896561650077],"hsluv":[50.0002977758109424,98.2940371374324826,68.1654896561650077]},"#dd9922":{"lch":[68.2205507365204,86.012937831153792,49.27558445879027],"luv":[68.2205507365204,56.1166821435168472,65.1854543556261774],"rgb":[0.866666666666666696,0.6,0.133333333333333331],"xyz":[0.414975645386310821,0.382719211696955275,0.0671513518522096897],"hpluv":[49.27558445879027,159.988175949224541,68.2205507365204],"hsluv":[49.27558445879027,95.1643810214000325,68.2205507365204]},"#dd9933":{"lch":[68.3110514885433133,82.7730333151944677,48.0261582829398179],"luv":[68.3110514885433133,55.3578808500206918,61.5376313485756867],"rgb":[0.866666666666666696,0.6,0.2],"xyz":[0.418063396118768527,0.383954311989938402,0.0834135057098208399],"hpluv":[48.0261582829398179,153.757824966526499,68.3110514885433133],"hsluv":[48.0261582829398179,90.1021642959546512,68.3110514885433133]},"#dd9944":{"lch":[68.4413718194248,78.2806718730308688,46.0898611008159875],"luv":[68.4413718194248,54.2899414547091936,56.3956190296495805],"rgb":[0.866666666666666696,0.6,0.266666666666666663],"xyz":[0.422521391362987442,0.385737510087625968,0.106892280662707478],"hpluv":[46.0898611008159875,145.136005207448051,68.4413718194248],"hsluv":[46.0898611008159875,82.9860784920926449,68.4413718194248]},"#dd9955":{"lch":[68.615044439797245,72.617894762441864,43.2298844404699452],"luv":[68.615044439797245,52.9102318436571935,49.7379734808277689],"rgb":[0.866666666666666696,0.6,0.333333333333333315],"xyz":[0.428483805402349427,0.388122475703370828,0.138294327936681394],"hpluv":[43.2298844404699452,134.296171447129637,68.615044439797245],"hsluv":[43.2298844404699452,73.8078183466747362,68.615044439797245]},"#dd9966":{"lch":[68.8349542760461333,65.9999398631214262,39.0839575312566296],"luv":[68.8349542760461333,51.2306688139135815,41.6102227200810404],"rgb":[0.866666666666666696,0.6,0.4],"xyz":[0.436068801175857612,0.391156474012774147,0.178241972343825356],"hpluv":[39.0839575312566296,121.667290932950721,68.8349542760461333],"hsluv":[39.0839575312566296,62.6592399312796076,68.8349542760461333]},"#dd9977":{"lch":[69.1034430542988929,58.8170492635600723,33.0939178586399834],"luv":[69.1034430542988929,49.2755524050587468,32.1148753578207291],"rgb":[0.866666666666666696,0.6,0.466666666666666674],"xyz":[0.445382814196059518,0.394882079220854942,0.227295774250223265],"hpluv":[33.0939178586399834,108.004754244465147,69.1034430542988929],"hsluv":[33.0939178586399834,50.7393036295826789,69.1034430542988929]},"#dd9988":{"lch":[69.4223715869125328,51.7141196395305656,24.4437159362198635],"luv":[69.4223715869125328,47.0788901370553745,21.3992587150758773],"rgb":[0.866666666666666696,0.6,0.533333333333333326],"xyz":[0.456523231298003318,0.39933824606163254,0.285968637653795432],"hpluv":[24.4437159362198635,94.5255072345235305,69.4223715869125328],"hsluv":[24.4437159362198635,51.5589665528693786,69.4223715869125328]},"#dd9999":{"lch":[69.7931614924381591,45.7097919546320597,12.1770506300622632],"luv":[69.7931614924381591,44.6813426781189875,9.64171649740066705],"rgb":[0.866666666666666696,0.6,0.6],"xyz":[0.469580195407076073,0.404561031705261698,0.354735315294913311],"hpluv":[12.1770506300622632,83.1066353202273689,69.7931614924381591],"hsluv":[12.1770506300622632,52.4335711570844438,69.7931614924381591]},"#dd99aa":{"lch":[70.2168268189972196,42.2311183685245624,355.977290330288042],"luv":[70.2168268189972196,42.1270745474506256,-2.96259155636920823],"rgb":[0.866666666666666696,0.6,0.66666666666666663],"xyz":[0.484637888372328574,0.41058410889136282,0.434039164911911912],"hpluv":[355.977290330288042,76.3186553239153369,70.2168268189972196],"hsluv":[355.977290330288042,53.3392978779897788,70.2168268189972196]},"#dd99bb":{"lch":[70.694001085559151,42.6623948649138427,337.661645838825223],"luv":[70.694001085559151,39.4608168046039651,-16.2149274658665163],"rgb":[0.866666666666666696,0.6,0.733333333333333282],"xyz":[0.501775480479665248,0.417439145734297579,0.524297150010553659],"hpluv":[337.661645838825223,76.5776430619999502,70.694001085559151],"hsluv":[337.661645838825223,54.2517942883204398,70.694001085559151]},"#dd99cc":{"lch":[71.2249627580945912,47.3730793907595,320.82657583403028],"luv":[71.2249627580945912,36.7253906259007294,-29.9241430008982121],"rgb":[0.866666666666666696,0.6,0.8],"xyz":[0.521067855923387,0.42515609591178638,0.625903660680823815],"hpluv":[320.82657583403028,84.3992741769230719,71.2249627580945912],"hsluv":[320.82657583403028,55.1472527266246075,71.2249627580945912]},"#dd99dd":{"lch":[71.8096607795551876,55.5140341348433424,307.715012949246],"luv":[71.8096607795551876,33.9598410238411077,-43.9151133843462844],"rgb":[0.866666666666666696,0.6,0.866666666666666696],"xyz":[0.54258618147402049,0.433763426132039887,0.739233508580829812],"hpluv":[307.715012949246,98.0977936102115677,71.8096607795551876],"hsluv":[307.715012949246,56.0031828352742878,71.8096607795551876]},"#dd99ee":{"lch":[72.447740927007942,65.8885644567307907,298.26171852853804],"luv":[72.447740927007942,31.1982236173053273,-58.0342465212858656],"rgb":[0.866666666666666696,0.6,0.933333333333333348],"xyz":[0.566398361740195311,0.443288298238509904,0.864644324649353],"hpluv":[298.26171852853804,115.404973126410823,72.447740927007942],"hsluv":[298.26171852853804,77.3800261839098908,72.447740927007942]},"#dd99ff":{"lch":[73.1385732331520302,77.5656763031263,291.532620718417377],"luv":[73.1385732331520302,28.4689992262389602,-72.1522710898123592],"rgb":[0.866666666666666696,0.6,1],"xyz":[0.592569410150026621,0.453756717602442627,1.00247851294113488],"hpluv":[291.532620718417377,134.574391894016571,73.1385732331520302],"hsluv":[291.532620718417377,99.9999999999977689,73.1385732331520302]},"#cc0000":{"lch":[42.5207510295766156,142.998625281495549,12.1770506300617818],"luv":[42.5207510295766156,139.781222041964895,30.163169542547891],"rgb":[0.8,0,0],"xyz":[0.249012838889184379,0.128397245052238429,0.0116724768229302719],"hpluv":[12.1770506300617818,426.746789183124861,42.5207510295766156],"hsluv":[12.1770506300617818,100.000000000002174,42.5207510295766156]},"#cc0011":{"lch":[42.5821659889152784,141.236718626044905,11.4841194603559],"luv":[42.5821659889152784,138.409148973409117,28.119711390930437],"rgb":[0.8,0,0.0666666666666666657],"xyz":[0.250024504388821511,0.128801911252093282,0.0170005817876859033],"hpluv":[11.4841194603559,420.880880123779207,42.5821659889152784],"hsluv":[11.4841194603559,99.9999999999964331,42.5821659889152784]},"#cc0022":{"lch":[42.6956735686566518,138.114600243667155,10.1872609469282853],"luv":[42.6956735686566518,135.937217546775798,24.4277646564013864],"rgb":[0.8,0,0.133333333333333331],"xyz":[0.251899862527298513,0.12955205450748411,0.0268774679836651],"hpluv":[10.1872609469282853,410.482879191578036,42.6956735686566518],"hsluv":[10.1872609469282853,99.9999999999964615,42.6956735686566518]},"#cc0033":{"lch":[42.881611378965772,133.362165770655935,8.01952044887972626],"luv":[42.881611378965772,132.057963211529,18.6054188736068511],"rgb":[0.8,0,0.2],"xyz":[0.254987613259756274,0.130787154800467209,0.0431396218412762461],"hpluv":[8.01952044887972626,394.639788400466045,42.881611378965772],"hsluv":[8.01952044887972626,99.9999999999966604,42.881611378965772]},"#cc0044":{"lch":[43.1480085091585153,127.29097956278504,4.82801781999359658],"luv":[43.1480085091585153,126.839328429887985,10.7134607624411853],"rgb":[0.8,0,0.266666666666666663],"xyz":[0.259445608503975134,0.132570352898154803,0.0666183967941628846],"hpluv":[4.82801781999359658,374.34858804079829,43.1480085091585153],"hsluv":[4.82801781999359658,99.9999999999967741,43.1480085091585153]},"#cc0055":{"lch":[43.5005971125795,120.485699890795146,0.473888563816867114],"luv":[43.5005971125795,120.481578818580687,0.996515708296922487],"rgb":[0.8,0,0.333333333333333315],"xyz":[0.265408022543337119,0.134955318513899636,0.0980204440681367861],"hpluv":[0.473888563816867114,351.463000970195878,43.5005971125795],"hsluv":[0.473888563816867114,99.999999999997,43.5005971125795]},"#cc0066":{"lch":[43.9431844272177372,113.726547538665841,354.863826263116096],"luv":[43.9431844272177372,113.269906269789104,-10.1811565500985228],"rgb":[0.8,0,0.4],"xyz":[0.272993018316845304,0.137989316823302927,0.137968088475280748],"hpluv":[354.863826263116096,328.404920869645196,43.9431844272177372],"hsluv":[354.863826263116096,99.9999999999972857,43.9431844272177372]},"#cc0077":{"lch":[44.4778741065655,107.874648109024193,348.012259047653401],"luv":[44.4778741065655,105.522124609829902,-22.4058234053856609],"rgb":[0.8,0,0.466666666666666674],"xyz":[0.28230703133704721,0.14171492203138375,0.187021890381678657],"hpluv":[348.012259047653401,307.761788629886667,44.4778741065655],"hsluv":[348.012259047653401,99.9999999999975273,44.4778741065655]},"#cc0088":{"lch":[45.1052440924579,103.725434836726933,340.1176986346278],"luv":[45.1052440924579,97.5426962017022703,-35.2758876538989838],"rgb":[0.8,0,0.533333333333333326],"xyz":[0.293447448438991065,0.146171088872161348,0.245694753785250825],"hpluv":[340.1176986346278,291.808241377507443,45.1052440924579],"hsluv":[340.1176986346278,99.9999999999978,45.1052440924579]},"#cc0099":{"lch":[45.8245205562958589,101.850048541314862,331.598662995615],"luv":[45.8245205562958589,89.5911194129305102,-48.4444394147173441],"rgb":[0.8,0,0.6],"xyz":[0.306504412548063765,0.151393874515790505,0.314461431426368732],"hpluv":[331.598662995615,282.034759885138044,45.8245205562958589],"hsluv":[331.598662995615,99.9999999999981,45.8245205562958589]},"#cc00aa":{"lch":[46.633760692471931,102.477609530343315,323.022725489580409],"luv":[46.633760692471931,81.8667129915779,-61.640098629123905],"rgb":[0.8,0,0.66666666666666663],"xyz":[0.321562105513316321,0.1574169517018916,0.393765281043367332],"hpluv":[323.022725489580409,278.848217687739293,46.633760692471931],"hsluv":[323.022725489580409,99.9999999999983658,46.633760692471931]},"#cc00bb":{"lch":[47.5300446684938933,105.484027274260768,314.937463984289479],"luv":[47.5300446684938933,74.5070162947061903,-74.6698368342758414],"rgb":[0.8,0,0.733333333333333282],"xyz":[0.33869969762065294,0.164271988544826358,0.484023266142009079],"hpluv":[314.937463984289479,281.616311803476265,47.5300446684938933],"hsluv":[314.937463984289479,99.9999999999986,47.5300446684938933]},"#cc00cc":{"lch":[48.5096711653281147,110.497164945278598,307.715012949243601],"luv":[48.5096711653281147,67.5949102529980621,-87.4102486487325],"rgb":[0.8,0,0.8],"xyz":[0.357992073064374694,0.17198893872231516,0.585629776812279235],"hpluv":[307.715012949243601,289.042783730483393,48.5096711653281147],"hsluv":[307.715012949243601,99.9999999999988,48.5096711653281147]},"#cc00dd":{"lch":[49.5683488162236614,117.049051317219835,301.506761454082039],"luv":[49.5683488162236614,61.1697383356450075,-99.7935044289451],"rgb":[0.8,0,0.866666666666666696],"xyz":[0.379510398615008238,0.180596268942568694,0.698959624712285232],"hpluv":[301.506761454082039,299.64205877637869,49.5683488162236614],"hsluv":[301.506761454082039,99.9999999999990337,49.5683488162236614]},"#cc00ee":{"lch":[50.7013760136427862,124.695255359169607,296.294949026353493],"luv":[50.7013760136427862,55.2390203059142522,-111.792474454818787],"rgb":[0.8,0,0.933333333333333348],"xyz":[0.403322578881182947,0.190121141049038739,0.824370440780808456],"hpluv":[296.294949026353493,312.082566880879938,50.7013760136427862],"hsluv":[296.294949026353493,99.99999999999919,50.7013760136427862]},"#cc00ff":{"lch":[51.9038030272213,133.072735088441448,291.971633700566258],"luv":[51.9038030272213,49.7888328026579075,-123.407556300526],"rgb":[0.8,0,1],"xyz":[0.429493627291014368,0.200589560412971435,0.962204629072590301],"hpluv":[291.971633700566258,325.333832743425603,51.9038030272213],"hsluv":[291.971633700566258,99.9999999999993321,51.9038030272213]},"#cc1100":{"lch":[43.1235624482234172,140.134259476931788,12.8715382160273855],"luv":[43.1235624482234172,136.61296213687416,31.2171307992428524],"rgb":[0.8,0.0666666666666666657,0],"xyz":[0.251017239150112814,0.132406045574095299,0.0123406102432397219],"hpluv":[12.8715382160273855,412.352867097941,43.1235624482234172],"hsluv":[12.8715382160273855,100.000000000002245,43.1235624482234172]},"#cc1111":{"lch":[43.1837333530957892,138.41807101963343,12.1770506300617676],"luv":[43.1837333530957892,135.303728142340162,29.196978192614182],"rgb":[0.8,0.0666666666666666657,0.0666666666666666657],"xyz":[0.252028904649749919,0.132810711773950152,0.0176687152079953516],"hpluv":[12.1770506300617676,406.735363437937394,43.1837333530957892],"hsluv":[12.1770506300617676,95.3107026807431197,43.1837333530957892]},"#cc1122":{"lch":[43.294951674171287,135.375047793376126,10.8766574447476163],"luv":[43.294951674171287,132.943125696930394,25.5446451333550044],"rgb":[0.8,0.0666666666666666657,0.133333333333333331],"xyz":[0.253904262788227,0.13356085502934098,0.0275456014039745511],"hpluv":[10.8766574447476163,396.771701832449367,43.294951674171287],"hsluv":[10.8766574447476163,95.396390587568618,43.294951674171287]},"#cc1133":{"lch":[43.4771672841157724,130.73841758888139,8.7012157385065958],"luv":[43.4771672841157724,129.233706917218683,19.7783424502448213],"rgb":[0.8,0.0666666666666666657,0.2],"xyz":[0.256992013520684681,0.134795955322324079,0.0438077552615856944],"hpluv":[8.7012157385065958,381.576227833431062,43.4771672841157724],"hsluv":[8.7012157385065958,95.5308510527687389,43.4771672841157724]},"#cc1144":{"lch":[43.7382910834512586,124.807582872189826,5.49437317543092796],"luv":[43.7382910834512586,124.234167693991893,11.9500761411646046],"rgb":[0.8,0.0666666666666666657,0.266666666666666663],"xyz":[0.261450008764903596,0.136579153420011673,0.0672865302144723398],"hpluv":[5.49437317543092796,362.091632024479338,43.7382910834512586],"hsluv":[5.49437317543092796,95.7116850897169229,43.7382910834512586]},"#cc1155":{"lch":[44.0840061747103107,118.151091154502552,1.11194247693657511],"luv":[44.0840061747103107,118.128842001262925,2.29282106589917234],"rgb":[0.8,0.0666666666666666657,0.333333333333333315],"xyz":[0.267412422804265582,0.138964119035756506,0.0986885774884462413],"hpluv":[1.11194247693657511,340.091681007194666,44.0840061747103107],"hsluv":[1.11194247693657511,95.9318436200579754,44.0840061747103107]},"#cc1166":{"lch":[44.5181325219627837,111.53522478210337,355.453854482233226],"luv":[44.5181325219627837,111.184314904818763,-8.84050260677650712],"rgb":[0.8,0.0666666666666666657,0.4],"xyz":[0.274997418577773767,0.141998117345159797,0.138636221895590217],"hpluv":[355.453854482233226,317.917500588946211,44.5181325219627837],"hsluv":[355.453854482233226,96.1812476973727115,44.5181325219627837]},"#cc1177":{"lch":[45.0428415016287857,105.814757555455103,348.528515458334311],"luv":[45.0428415016287857,103.700981473984271,-21.0444614531261323],"rgb":[0.8,0.0666666666666666657,0.466666666666666674],"xyz":[0.284311431597975672,0.14572372255324062,0.187690023801988126],"hpluv":[348.528515458334311,298.098498005115459,45.0428415016287857],"hsluv":[348.528515458334311,96.4486017572014589,45.0428415016287857]},"#cc1188":{"lch":[45.6588256994622341,101.788242110562607,340.533613155211185],"luv":[45.6588256994622341,95.9697371205835594,-33.9213176183443323],"rgb":[0.8,0.0666666666666666657,0.533333333333333326],"xyz":[0.295451848699919473,0.150179889394018218,0.246362887205560266],"hpluv":[340.533613155211185,282.886487347344485,45.6588256994622341],"hsluv":[340.533613155211185,96.7230145871901215,45.6588256994622341]},"#cc1199":{"lch":[46.3654632546324876,100.035938879036408,331.896400713626349],"luv":[46.3654632546324876,88.2414291467186,-47.1236591273953138],"rgb":[0.8,0.0666666666666666657,0.6],"xyz":[0.308508812808992228,0.155402675037647375,0.315129564846678201],"hpluv":[331.896400713626349,273.779405248492822,46.3654632546324876],"hsluv":[331.896400713626349,96.9951405388504355,46.3654632546324876]},"#cc11aa":{"lch":[47.1609900317596882,100.794975336059878,323.201580807901109],"luv":[47.1609900317596882,80.7113646178594735,-60.3763420100941488],"rgb":[0.8,0.0666666666666666657,0.66666666666666663],"xyz":[0.323566505774244728,0.16142575222374847,0.394433414463676801],"hpluv":[323.201580807901109,271.203503720358924,47.1609900317596882],"hsluv":[323.201580807901109,97.2577546113476075,47.1609900317596882]},"#cc11bb":{"lch":[48.0426807208370548,103.942897448445919,315.013990059648165],"luv":[48.0426807208370548,73.5166718264647727,-73.4807790754699681],"rgb":[0.8,0.0666666666666666657,0.733333333333333282],"xyz":[0.340704097881581403,0.168280789066683228,0.484691399562318548],"hpluv":[315.013990059648165,274.540811344802705,48.0426807208370548],"hsluv":[315.013990059648165,97.5058443216483,48.0426807208370548]},"#cc11cc":{"lch":[49.0070341259591515,109.103198367120783,307.715012949243601],"luv":[49.0070341259591515,66.7421730285370387,-86.3075328888743769],"rgb":[0.8,0.0666666666666666657,0.8],"xyz":[0.359996473325303157,0.17599773924417203,0.586297910232588704],"hpluv":[307.715012949243601,282.499958642668389,49.0070341259591515],"hsluv":[307.715012949243601,97.7363817897909541,49.0070341259591515]},"#cc11dd":{"lch":[50.0499556366759037,115.801352096543823,301.456118533327128],"luv":[50.0499556366759037,60.4304023076134555,-98.7831950502093292],"rgb":[0.8,0.0666666666666666657,0.866666666666666696],"xyz":[0.381514798875936645,0.184605069464425564,0.699627758132594701],"hpluv":[301.456118533327128,293.595408819402792,50.0499556366759037],"hsluv":[301.456118533327128,97.9479403979501342,50.0499556366759037]},"#cc11ee":{"lch":[51.1669298024285837,123.587345912593733,296.214453457233276],"luv":[51.1669298024285837,54.5925079172689109,-110.876012505059137],"rgb":[0.8,0.0666666666666666657,0.933333333333333348],"xyz":[0.40532697914211141,0.194129941570895609,0.825038574201117925],"hpluv":[296.214453457233276,306.495409047480564,51.1669298024285837],"hsluv":[296.214453457233276,98.1402788193950215,51.1669298024285837]},"#cc11ff":{"lch":[52.3531771468210678,132.094610043027387,291.87590029388349],"luv":[52.3531771468210678,49.2181193296501576,-122.582881072651375],"rgb":[0.8,0.0666666666666666657,1],"xyz":[0.431498027551942775,0.204598360934828305,0.96287276249289977],"hpluv":[291.87590029388349,320.170549145207644,52.3531771468210678],"hsluv":[291.87590029388349,99.9999999999991189,52.3531771468210678]},"#cc2200":{"lch":[44.2095884480383674,135.132222138307952,14.1797238149512133],"luv":[44.2095884480383674,131.015027118873576,33.102569825888537],"rgb":[0.8,0.133333333333333331,0],"xyz":[0.254732862884880729,0.139837293043631267,0.0135791514881623328],"hpluv":[14.1797238149512133,387.866054960954045,44.2095884480383674],"hsluv":[14.1797238149512133,100.00000000000226,44.2095884480383674]},"#cc2211":{"lch":[44.2676114068871129,133.490625771231663,13.482935392010976],"luv":[44.2676114068871129,129.81154471696064,31.1241068464871375],"rgb":[0.8,0.133333333333333331,0.0666666666666666657],"xyz":[0.255744528384517833,0.14024195924348612,0.0189072564529179643],"hpluv":[13.482935392010976,382.652016671578622,44.2676114068871129],"hsluv":[13.482935392010976,95.5414705532830197,44.2676114068871129]},"#cc2222":{"lch":[44.3748759613401162,130.576558981694717,12.1770506300617747],"luv":[44.3748759613401162,127.638646515421286,27.5429423121666019],"rgb":[0.8,0.133333333333333331,0.133333333333333331],"xyz":[0.25761988652299489,0.140992102498876948,0.0287841426488971604],"hpluv":[12.1770506300617747,373.394050741154899,44.3748759613401162],"hsluv":[12.1770506300617747,87.4977996802062847,44.3748759613401162]},"#cc2233":{"lch":[44.5506596541482907,126.128431434884163,9.98899557195718657],"luv":[44.5506596541482907,124.216461430809,21.8781152257824267],"rgb":[0.8,0.133333333333333331,0.2],"xyz":[0.260707637255452596,0.142227202791860047,0.0450462965065083071],"hpluv":[9.98899557195718657,359.251162664680805,44.5506596541482907],"hsluv":[9.98899557195718657,87.8457930667704829,44.5506596541482907]},"#cc2244":{"lch":[44.8026641682027602,120.425301369152351,6.75586226508684629],"luv":[44.8026641682027602,119.589119356054482,14.1667124448306829],"rgb":[0.8,0.133333333333333331,0.266666666666666663],"xyz":[0.265165632499671511,0.144010400889547641,0.0685250714593949456],"hpluv":[6.75586226508684629,341.077623618219945,44.8026641682027602],"hsluv":[6.75586226508684629,88.3153597242729,44.8026641682027602]},"#cc2255":{"lch":[45.136480402373536,114.008772215671115,2.323188749975583],"luv":[45.136480402373536,113.915065115628011,4.62148047999889577],"rgb":[0.8,0.133333333333333331,0.333333333333333315],"xyz":[0.271128046539033496,0.146395366505292474,0.0999271187333688471],"hpluv":[2.323188749975583,320.51614012782295,45.136480402373536],"hsluv":[2.323188749975583,88.8894607126765663,45.136480402373536]},"#cc2266":{"lch":[45.5559407124691731,107.622008592458101,356.577515499379103],"luv":[45.5559407124691731,107.430062323442542,-6.42483016556681452],"rgb":[0.8,0.133333333333333331,0.4],"xyz":[0.278713042312541681,0.149429364814695764,0.139874763140512809],"hpluv":[356.577515499379103,299.774992396016216,45.5559407124691731],"hsluv":[356.577515499379103,89.543058875786258,45.5559407124691731]},"#cc2277":{"lch":[46.0633224094211542,102.110102175352239,349.514816334356908],"luv":[46.0633224094211542,100.405067655344695,-18.5821246198201955],"rgb":[0.8,0.133333333333333331,0.466666666666666674],"xyz":[0.288027055332743587,0.153154970022776588,0.188928565046910718],"hpluv":[349.514816334356908,281.289018816444,46.0633224094211542],"hsluv":[349.514816334356908,90.2475430222303174,46.0633224094211542]},"#cc2288":{"lch":[46.6595045299101443,98.2790088680822294,341.330158110485115],"luv":[46.6595045299101443,93.107459692013677,-31.4605234189234864],"rgb":[0.8,0.133333333333333331,0.533333333333333326],"xyz":[0.299167472434687387,0.157611136863554185,0.247601428450482886],"hpluv":[341.330158110485115,267.276005324070297,46.6595045299101443],"hsluv":[341.330158110485115,90.9748001704168,46.6595045299101443]},"#cc2299":{"lch":[47.3441166442187154,96.727251962869147,332.467163788405],"luv":[47.3441166442187154,85.772509513995189,-44.7128380217581167],"rgb":[0.8,0.133333333333333331,0.6],"xyz":[0.312224436543760087,0.162833922507183343,0.31636810609160082],"hpluv":[332.467163788405,259.252025571181889,47.3441166442187154],"hsluv":[332.467163788405,91.7002075152857259,47.3441166442187154]},"#cc22aa":{"lch":[48.1156936783416285,97.7076198445846131,323.544141865814368],"luv":[48.1156936783416285,78.5876931080813534,-58.0581903579838254],"rgb":[0.8,0.133333333333333331,0.66666666666666663],"xyz":[0.327282129509012643,0.168856999693284437,0.395671955708599421],"hpluv":[323.544141865814368,257.680176436907345,48.1156936783416285],"hsluv":[323.544141865814368,92.4042695013683328,48.1156936783416285]},"#cc22bb":{"lch":[48.9718390817589295,101.100583268181566,315.160199675634601],"luv":[48.9718390817589295,71.6885121422158846,-71.2887450022924867],"rgb":[0.8,0.133333333333333331,0.733333333333333282],"xyz":[0.344419721616349317,0.175712036536219196,0.485929940807241167],"hpluv":[315.160199675634601,261.96699229126267,48.9718390817589295],"hsluv":[315.160199675634601,93.0730408422972602,48.9718390817589295]},"#cc22cc":{"lch":[49.9093929354593513,106.520702596514482,307.715012949243658],"luv":[49.9093929354593513,65.1623716831421405,-84.2646153393173876],"rgb":[0.8,0.133333333333333331,0.8],"xyz":[0.363712097060071,0.183428986713708,0.587536451477511323],"hpluv":[307.715012949243658,270.826440261226253,49.9093929354593513],"hsluv":[307.715012949243658,93.6976999618693327,49.9093929354593513]},"#cc22dd":{"lch":[50.9245991417877377,113.480326695625976,301.360030221234524],"luv":[50.9245991417877377,59.0567580516805819,-96.9024451465042347],"rgb":[0.8,0.133333333333333331,0.866666666666666696],"xyz":[0.385230422610704615,0.192036316933961532,0.700866299377517321],"hpluv":[301.360030221234524,282.769318292038,50.9245991417877377],"hsluv":[301.360030221234524,94.2736696463096848,50.9245991417877377]},"#cc22ee":{"lch":[52.0132654143591964,121.518329969744158,296.062236941936249],"luv":[52.0132654143591964,53.3887372455597244,-109.162022947361166],"rgb":[0.8,0.133333333333333331,0.933333333333333348],"xyz":[0.409042602876879324,0.201561189040431576,0.826277115446040544],"hpluv":[296.062236941936249,296.460610812118318,52.0132654143591964],"hsluv":[296.062236941936249,94.7995997265514,52.0132654143591964]},"#cc22ff":{"lch":[53.170910599170611,130.261070707018604,291.695402941657903],"luv":[53.170910599170611,48.1538976563848422,-121.033667556747687],"rgb":[0.8,0.133333333333333331,1],"xyz":[0.435213651286710745,0.212029608404364273,0.964111303737822389],"hpluv":[291.695402941657903,310.870757963074425,53.170910599170611],"hsluv":[291.695402941657903,99.9999999999990621,53.170910599170611]},"#cc3300":{"lch":[45.9167915379707807,127.686226573765651,16.3911473443809399],"luv":[45.9167915379707807,122.496750231663356,36.0321889333482446],"rgb":[0.8,0.2,0],"xyz":[0.260850584973891519,0.15207273722165307,0.0156183921844992128],"hpluv":[16.3911473443809399,352.867650162608584,45.9167915379707807],"hsluv":[16.3911473443809399,100.000000000002288,45.9167915379707807]},"#cc3311":{"lch":[45.9716631772740811,126.14350976506573,15.6923563051461095],"luv":[45.9716631772740811,121.44186843726601,34.118289029712578],"rgb":[0.8,0.2,0.0666666666666666657],"xyz":[0.261862250473528624,0.152477403421507923,0.020946497149254846],"hpluv":[15.6923563051461095,348.188177538365835,45.9716631772740811],"hsluv":[15.6923563051461095,95.8756509753329595,45.9716631772740811]},"#cc3322":{"lch":[46.0731243265426613,123.399991281457972,14.3806932854006604],"luv":[46.0731243265426613,119.533487768482047,30.6480529588105668],"rgb":[0.8,0.2,0.133333333333333331],"xyz":[0.263737608612005681,0.153227546676898752,0.0308233833452340386],"hpluv":[14.3806932854006604,339.865273437066321,46.0731243265426613],"hsluv":[14.3806932854006604,88.4197842587317524,46.0731243265426613]},"#cc3333":{"lch":[46.2394596481243951,119.199958247304309,12.1770506300617924],"luv":[46.2394596481243951,116.518014060345578,25.1432385661076303],"rgb":[0.8,0.2,0.2],"xyz":[0.266825359344463386,0.154462646969881851,0.0470855372028451818],"hpluv":[12.1770506300617924,327.11667224844831,46.2394596481243951],"hsluv":[12.1770506300617924,76.6535755019082,46.2394596481243951]},"#cc3344":{"lch":[46.4780522046582405,113.793717346871745,8.90746564227168669],"luv":[46.4780522046582405,112.421330819758609,17.6197186224449389],"rgb":[0.8,0.2,0.266666666666666663],"xyz":[0.271283354588682302,0.156245845067569444,0.0705643121557318204],"hpluv":[8.90746564227168669,310.677421508065,46.4780522046582405],"hsluv":[8.90746564227168669,77.4930753906613887,46.4780522046582405]},"#cc3355":{"lch":[46.7943405275181661,107.68493286266704,4.39945159143432907],"luv":[46.7943405275181661,107.367637754253522,8.26045572038872855],"rgb":[0.8,0.2,0.333333333333333315],"xyz":[0.277245768628044287,0.158630810683314277,0.101966359429705736],"hpluv":[4.39945159143432907,292.012160158640199,46.7943405275181661],"hsluv":[4.39945159143432907,78.5258346284665265,46.7943405275181661]},"#cc3366":{"lch":[47.192153202602185,101.584765682520938,358.514989280316684],"luv":[47.192153202602185,101.55064731932022,-2.63261235272558647],"rgb":[0.8,0.2,0.4],"xyz":[0.284830764401552472,0.161664808992717568,0.141914003836849684],"hpluv":[358.514989280316684,273.148057687163259,47.192153202602185],"hsluv":[358.514989280316684,79.7102211537212781,47.192153202602185]},"#cc3377":{"lch":[47.6738975277608859,96.3272258277819589,351.225603296168742],"luv":[47.6738975277608859,95.1998742335289876,-14.6941614798792699],"rgb":[0.8,0.2,0.466666666666666674],"xyz":[0.294144777421754378,0.165390414200798391,0.190967805743247593],"hpluv":[351.225603296168742,256.39391915093114,47.6738975277608859],"hsluv":[351.225603296168742,80.9972134637005894,47.6738975277608859]},"#cc3388":{"lch":[48.2406991607903279,92.7347249941698095,342.718318936463845],"luv":[48.2406991607903279,88.5482927213948301,-27.5486674064369161],"rgb":[0.8,0.2,0.533333333333333326],"xyz":[0.305285194523698178,0.169846581041576,0.24964066914681976],"hpluv":[342.718318936463845,243.93163202064531,48.2406991607903279],"hsluv":[342.718318936463845,82.3372641433758758,48.2406991607903279]},"#cc3399":{"lch":[48.8925304323200436,91.4400639314067405,333.463846826874658],"luv":[48.8925304323200436,81.8070942553503926,-40.8519843003490877],"rgb":[0.8,0.2,0.6],"xyz":[0.318342158632770933,0.175069366685205147,0.318407346787937695],"hpluv":[333.463846826874658,237.319449797121564,48.8925304323200436],"hsluv":[333.463846826874658,83.6856508008112,48.8925304323200436]},"#cc33aa":{"lch":[49.6283419748853873,92.7248127600779242,324.141487451800515],"luv":[49.6283419748853873,75.1503095778714254,-54.3168654447363792],"rgb":[0.8,0.2,0.66666666666666663],"xyz":[0.333399851598023433,0.181092443871306241,0.397711196404936296],"hpluv":[324.141487451800515,237.085790295602294,49.6283419748853873],"hsluv":[324.141487451800515,85.0057245198840263,49.6283419748853873]},"#cc33bb":{"lch":[50.4462014889725054,96.4744776262418355,315.414049480091705],"luv":[50.4462014889725054,68.7089492450233905,-67.7230029377097],"rgb":[0.8,0.2,0.733333333333333282],"xyz":[0.350537443705360108,0.187947480714241,0.487969181503578042],"hpluv":[315.414049480091705,242.674024226837219,50.4462014889725054],"hsluv":[315.414049480091705,86.2701296035756258,50.4462014889725054]},"#cc33cc":{"lch":[51.3434379695087273,102.286811532428814,307.715012949243771],"luv":[51.3434379695087273,62.5723551280607779,-80.915339628518268],"rgb":[0.8,0.2,0.8],"xyz":[0.369829819149081862,0.195664430891729801,0.589575692173848198],"hpluv":[307.715012949243771,252.798225898109365,51.3434379695087273],"hsluv":[307.715012949243771,87.4604868647498,51.3434379695087273]},"#cc33dd":{"lch":[52.3167871114961827,109.649949983538605,301.195256028086874],"luv":[52.3167871114961827,56.7938698017029751,-93.7953510806358],"rgb":[0.8,0.2,0.866666666666666696],"xyz":[0.39134814469971535,0.204271761111983335,0.702905540073854196],"hpluv":[301.195256028086874,265.954105752122757,52.3167871114961827],"hsluv":[301.195256028086874,88.5661577004671,52.3167871114961827]},"#cc33ee":{"lch":[53.3625327970638494,118.082474280319559,295.802769884900215],"luv":[53.3625327970638494,51.3983045372134626,-106.309383512755787],"rgb":[0.8,0.2,0.933333333333333348],"xyz":[0.415160324965890115,0.21379663321845338,0.828316356142377419],"hpluv":[295.802769884900215,280.794331636146467,53.3625327970638494],"hsluv":[295.802769884900215,89.5826266077864375,53.3625327970638494]},"#cc33ff":{"lch":[54.4766398815527566,127.197777570935344,291.389330811727291],"luv":[54.4766398815527566,46.3894624378335,-118.436870921660102],"rgb":[0.8,0.2,1],"xyz":[0.441331373375721481,0.224265052582386076,0.966150544434159264],"hpluv":[291.389330811727291,296.284230666095709,54.4766398815527566],"hsluv":[291.389330811727291,99.9999999999990195,54.4766398815527566]},"#cc4400":{"lch":[48.2269914221542848,118.435883841274119,19.7039935064818295],"luv":[48.2269914221542848,111.501113106022,39.9319465764175447],"rgb":[0.8,0.266666666666666663,0],"xyz":[0.26968315545685756,0.1697378781875854,0.0185625823454878096],"hpluv":[19.7039935064818295,311.625122342549162,48.2269914221542848],"hsluv":[19.7039935064818295,100.000000000002174,48.2269914221542848]},"#cc4411":{"lch":[48.2779913635395,116.996103035931512,19.0066631306561966],"luv":[48.2779913635395,110.617558299276112,38.1030697123021],"rgb":[0.8,0.266666666666666663,0.0666666666666666657],"xyz":[0.270694820956494664,0.170142544387440253,0.0238906873102434428],"hpluv":[19.0066631306561966,307.511619232155283,48.2779913635395],"hsluv":[19.0066631306561966,96.278384876640132,48.2779913635395]},"#cc4422":{"lch":[48.3723181772035389,114.428966114904512,17.6946931608209894],"luv":[48.3723181772035389,109.015290220227484,34.7800917814421453],"rgb":[0.8,0.266666666666666663,0.133333333333333331],"xyz":[0.272570179094971721,0.170892687642831081,0.0337675735062226354],"hpluv":[17.6946931608209894,300.177682052924467,48.3723181772035389],"hsluv":[17.6946931608209894,89.5341066932624869,48.3723181772035389]},"#cc4433":{"lch":[48.5270263662828114,110.48209503335984,15.4815426750504042],"luv":[48.5270263662828114,106.473417017787412,29.4907577304601034],"rgb":[0.8,0.266666666666666663,0.2],"xyz":[0.275657929827429427,0.17212778793581418,0.0500297273638337786],"hpluv":[15.4815426750504042,288.900004133964501,48.5270263662828114],"hsluv":[15.4815426750504042,78.8475327486048,48.5270263662828114]},"#cc4444":{"lch":[48.7490888960709725,105.371058014361893,12.1770506300618457],"luv":[48.7490888960709725,103.000257716521162,22.2262632351064191],"rgb":[0.8,0.266666666666666663,0.266666666666666663],"xyz":[0.280115925071648342,0.173910986033501774,0.0735085023167204171],"hpluv":[12.1770506300618457,274.28001464324592,48.7490888960709725],"hsluv":[12.1770506300618457,64.2723089184284788,48.7490888960709725]},"#cc4455":{"lch":[49.0437296069087,99.5540950311582691,7.58056168126664254],"luv":[49.0437296069087,98.6840273694228074,13.1331861946711772],"rgb":[0.8,0.266666666666666663,0.333333333333333315],"xyz":[0.286078339111010327,0.176295951649246607,0.104910549590694333],"hpluv":[7.58056168126664254,257.581676799067395,49.0437296069087],"hsluv":[7.58056168126664254,65.7689369270132858,49.0437296069087]},"#cc4466":{"lch":[49.4147368002801244,93.7049448509207679,1.51289058041819868],"luv":[49.4147368002801244,93.6722802728102835,2.47398423725391092],"rgb":[0.8,0.266666666666666663,0.4],"xyz":[0.293663334884518512,0.179329949958649898,0.14485819399783828],"hpluv":[1.51289058041819868,240.627550105868352,49.4147368002801244],"hsluv":[1.51289058041819868,67.500804726855,49.4147368002801244]},"#cc4477":{"lch":[49.8646356184384132,88.6490107187132566,353.900159994018],"luv":[49.8646356184384132,88.1471013723349586,-9.41995865499641383],"rgb":[0.8,0.266666666666666663,0.466666666666666674],"xyz":[0.302977347904720418,0.183055555166730721,0.19391199590423619],"hpluv":[353.900159994018,225.590377060975072,49.8646356184384132],"hsluv":[353.900159994018,69.4017616341687784,49.8646356184384132]},"#cc4488":{"lch":[50.3948096201307436,85.2405150759366279,344.907000147604322],"luv":[50.3948096201307436,82.3000967091543743,-22.1954835963269055],"rgb":[0.8,0.266666666666666663,0.533333333333333326],"xyz":[0.314117765006664218,0.187511722007508319,0.252584859307808385],"hpluv":[344.907000147604322,214.634525667930632,50.3948096201307436],"hsluv":[344.907000147604322,71.4025087995354824,50.3948096201307436]},"#cc4499":{"lch":[51.0056074652318046,84.1718897613591679,335.041688295241613],"luv":[51.0056074652318046,76.3115014820887581,-35.5170630478867793],"rgb":[0.8,0.266666666666666663,0.6],"xyz":[0.327174729115737,0.192734507651137477,0.321351536948926264],"hpluv":[335.041688295241613,209.40569084337892,51.0056074652318046],"hsluv":[335.041688295241613,73.4381222823911344,51.0056074652318046]},"#cc44aa":{"lch":[51.6964496814969152,85.7749410926589348,325.085448007895536],"luv":[51.6964496814969152,70.3360122964612913,-49.093644127128691],"rgb":[0.8,0.266666666666666663,0.66666666666666663],"xyz":[0.342232422080989473,0.198757584837238571,0.400655386565924865],"hpluv":[325.085448007895536,210.542141790665795,51.6964496814969152],"hsluv":[325.085448007895536,75.4531429469973602,51.6964496814969152]},"#cc44bb":{"lch":[52.465940673938249,89.9444778306290829,315.812454406659811],"luv":[52.465940673938249,64.4957791941851326,-62.692133145710109],"rgb":[0.8,0.266666666666666663,0.733333333333333282],"xyz":[0.359370014188326148,0.20561262168017333,0.490913371664566611],"hpluv":[315.812454406659811,217.538619181736436,52.465940673938249],"hsluv":[315.812454406659811,77.4040753157756143,52.465940673938249]},"#cc44cc":{"lch":[53.3119860408958175,96.2498903650287758,307.715012949243885],"luv":[53.3119860408958175,58.8793631430003828,-76.1397530279337502],"rgb":[0.8,0.266666666666666663,0.8],"xyz":[0.378662389632047902,0.213329571857662131,0.592519882334836767],"hpluv":[307.715012949243885,229.094523932317799,53.3119860408958175],"hsluv":[307.715012949243885,79.2597279113993,53.3119860408958175]},"#cc44dd":{"lch":[54.2319126329379486,104.138844540210442,300.941773361922515],"luv":[54.2319126329379486,53.5447276840333259,-89.31887303035586],"rgb":[0.8,0.266666666666666663,0.866666666666666696],"xyz":[0.40018071518268139,0.221936902077915665,0.705849730234842765],"hpluv":[300.941773361922515,243.66724889744043,54.2319126329379486],"hsluv":[300.941773361922515,81.0000946003390538,54.2319126329379486]},"#cc44ee":{"lch":[55.2225876682288401,113.096616320403683,295.407423092027],"luv":[55.2225876682288401,48.5243479249087528,-102.157879194837463],"rgb":[0.8,0.266666666666666663,0.933333333333333348],"xyz":[0.423992895448856155,0.23146177418438571,0.831260546303366],"hpluv":[295.407423092027,259.879597148300718,55.2225876682288401],"hsluv":[295.407423092027,86.8652077390175634,55.2225876682288401]},"#cc44ff":{"lch":[56.2805330741479537,122.715491745326418,290.92682559337851],"luv":[56.2805330741479537,43.8309487146615382,-114.62085259266739],"rgb":[0.8,0.266666666666666663,1],"xyz":[0.450163943858687521,0.241930193548318406,0.969094734595147833],"hpluv":[290.92682559337851,276.681751484143376,56.2805330741479537],"hsluv":[290.92682559337851,99.9999999999989768,56.2805330741479537]},"#cc5500":{"lch":[51.07852272981998,108.355754132896138,24.3337665629108457],"luv":[51.07852272981998,98.7294936728878838,44.6481414260866],"rgb":[0.8,0.333333333333333315,0],"xyz":[0.281496412171203525,0.193364391616277664,0.0225003345836030169],"hpluv":[24.3337665629108457,269.186315008697875,51.07852272981998],"hsluv":[24.3337665629108457,100.000000000002217,51.07852272981998]},"#cc5511":{"lch":[51.1252833166066,107.003417336388864,23.6474066829241423],"luv":[51.1252833166066,98.0184651549503201,42.91983003616388],"rgb":[0.8,0.333333333333333315,0.0666666666666666657],"xyz":[0.282508077670840629,0.193769057816132517,0.0278284395483586466],"hpluv":[23.6474066829241423,265.583595811902,51.1252833166066],"hsluv":[23.6474066829241423,96.7082849697218307,51.1252833166066]},"#cc5522":{"lch":[51.2117930688627467,104.583581473170284,22.351909846807331],"luv":[51.2117930688627467,96.7257518395109486,39.7725337995530808],"rgb":[0.8,0.333333333333333315,0.133333333333333331],"xyz":[0.284383435809317686,0.194519201071523345,0.0377053257443378462],"hpluv":[22.351909846807331,259.139045481141636,51.2117930688627467],"hsluv":[22.351909846807331,90.7274756498525079,51.2117930688627467]},"#cc5533":{"lch":[51.3537468781662625,100.840694749975015,20.1540565840537802],"luv":[51.3537468781662625,94.6661789932146718,34.7442120716858369],"rgb":[0.8,0.333333333333333315,0.2],"xyz":[0.287471186541775392,0.195754301364506444,0.0539674796019489894],"hpluv":[20.1540565840537802,249.174169543579183,51.3537468781662625],"hsluv":[20.1540565840537802,81.2092815218714321,51.3537468781662625]},"#cc5544":{"lch":[51.5576456995760424,95.9504499860225764,16.842752904303623],"luv":[51.5576456995760424,91.8345174188591074,27.8012636936791502],"rgb":[0.8,0.333333333333333315,0.266666666666666663],"xyz":[0.291929181785994307,0.197537499462194038,0.0774462545548356279],"hpluv":[16.842752904303623,236.152889903020736,51.5576456995760424],"hsluv":[16.842752904303623,68.1451659970290109,51.5576456995760424]},"#cc5555":{"lch":[51.8284441386287114,90.3192908744292851,12.1770506300618919],"luv":[51.8284441386287114,88.2871484081680364,19.0513445723360242],"rgb":[0.8,0.333333333333333315,0.333333333333333315],"xyz":[0.297891595825356292,0.199922465077938871,0.108848301828809529],"hpluv":[12.1770506300618919,221.132040760117775,51.8284441386287114],"hsluv":[12.1770506300618919,51.8180912815801,51.8284441386287114]},"#cc5566":{"lch":[52.1698415772242612,84.57836719340618,5.91246023067467696],"luv":[52.1698415772242612,84.1284474176031836,8.71232071300305577],"rgb":[0.8,0.333333333333333315,0.4],"xyz":[0.305476591598864478,0.202956463387342162,0.148795946235953491],"hpluv":[5.91246023067467696,205.721226704565879,52.1698415772242612],"hsluv":[5.91246023067467696,54.0130370973951415,52.1698415772242612]},"#cc5577":{"lch":[52.5844387621358607,79.5477054283049654,357.892334615122479],"luv":[52.5844387621358607,79.493890006545314,-2.92555815796844554],"rgb":[0.8,0.333333333333333315,0.466666666666666674],"xyz":[0.314790604619066383,0.206682068595422985,0.1978497481423514],"hpluv":[357.892334615122479,191.959557587962337,52.5844387621358607],"hsluv":[357.892334615122479,56.4492590331968884,52.5844387621358607]},"#cc5588":{"lch":[53.0738428910491962,76.1348505840982,348.222799824086337],"luv":[53.0738428910491962,74.5321119975331072,-15.5396188708133902],"rgb":[0.8,0.333333333333333315,0.533333333333333326],"xyz":[0.325931021721010183,0.211138235436200583,0.256522611545923596],"hpluv":[348.222799824086337,182.029716109510787,53.0738428910491962],"hsluv":[348.222799824086337,59.0443947768825339,53.0738428910491962]},"#cc5599":{"lch":[53.6387547547300443,75.1322702050955655,337.451566852479516],"luv":[53.6387547547300443,69.3888374020903314,-28.8105409556599774],"rgb":[0.8,0.333333333333333315,0.6],"xyz":[0.338987985830082938,0.216361021079829741,0.325289289187041475],"hpluv":[337.451566852479516,177.740808472615271,53.6387547547300443],"hsluv":[337.451566852479516,61.7180151219793274,53.6387547547300443]},"#cc55aa":{"lch":[54.2790527162633651,76.9604755372169507,326.525055670409472],"luv":[54.2790527162633651,64.1948187783047359,-42.449264268479169],"rgb":[0.8,0.333333333333333315,0.66666666666666663],"xyz":[0.354045678795335439,0.222384098265930835,0.404593138804040076],"hpluv":[326.525055670409472,179.918080614224607,54.2790527162633651],"hsluv":[326.525055670409472,64.3982563598392517,54.2790527162633651]},"#cc55bb":{"lch":[54.9938795911038767,81.534325182187132,316.414019967357433],"luv":[54.9938795911038767,59.0586208757266817,-56.2132144888709888],"rgb":[0.8,0.333333333333333315,0.733333333333333282],"xyz":[0.371183270902672113,0.229239135108865594,0.494851123902681822],"hpluv":[316.414019967357433,188.133202867989326,54.9938795911038767],"hsluv":[316.414019967357433,67.0257727218245378,54.9938795911038767]},"#cc55cc":{"lch":[55.7817339145568667,88.3780574248019,307.715012949244169],"luv":[55.7817339145568667,54.0638926159085145,-69.9126351198200382],"rgb":[0.8,0.333333333333333315,0.8],"xyz":[0.390475646346393868,0.236956085286354395,0.596457634572952],"hpluv":[307.715012949244169,201.044301715196383,55.7817339145568667],"hsluv":[307.715012949244169,69.5552053299685156,55.7817339145568667]},"#cc55dd":{"lch":[56.6405645837994882,96.8744721490496232,300.570417552325068],"luv":[56.6405645837994882,49.2700598324241312,-83.4093793183137],"rgb":[0.8,0.333333333333333315,0.866666666666666696],"xyz":[0.411993971897027356,0.245563415506607929,0.709787482472958],"hpluv":[300.570417552325068,217.030665421015726,56.6405645837994882],"hsluv":[300.570417552325068,72.1703256475756234,56.6405645837994882]},"#cc55ee":{"lch":[57.5678665910353118,106.457154357221242,294.836459829444038],"luv":[57.5678665910353118,44.7151619549172707,-96.6109724885468],"rgb":[0.8,0.333333333333333315,0.933333333333333348],"xyz":[0.435806152163202121,0.255088287613077946,0.835198298541481199],"hpluv":[294.836459829444038,234.657286550118499,57.5678665910353118],"hsluv":[294.836459829444038,85.8995510844284809,57.5678665910353118]},"#cc55ff":{"lch":[58.560775097021633,116.686665261471418,290.266986003053091],"luv":[58.560775097021633,40.4196983751927874,-109.462440284789551],"rgb":[0.8,0.333333333333333315,1],"xyz":[0.461977200573033486,0.26555670697701067,0.973032486833263],"hpluv":[290.266986003053091,252.844632524376181,58.560775097021633],"hsluv":[290.266986003053091,99.9999999999988,58.560775097021633]},"#cc6600":{"lch":[54.388060759003551,98.5584029412379579,30.482787603130209],"luv":[54.388060759003551,84.9358174587045482,49.9966569177284157],"rgb":[0.8,0.4,0],"xyz":[0.29652446987705,0.223420507027971,0.0275096871522183678],"hpluv":[30.482787603130209,229.947880001204965,54.388060759003551],"hsluv":[30.482787603130209,100.000000000002359,54.388060759003551]},"#cc6611":{"lch":[54.430531479182676,97.2669366851907711,29.8266301777559697],"luv":[54.430531479182676,84.3824110244092367,48.3783596438383583],"rgb":[0.8,0.4,0.0666666666666666657],"xyz":[0.297536135376687105,0.223825173227825858,0.032837792116974],"hpluv":[29.8266301777559697,226.757672191171366,54.430531479182676],"hsluv":[29.8266301777559697,97.1300271864182463,54.430531479182676]},"#cc6622":{"lch":[54.5091256699603548,94.945229062596681,28.5830621108902889],"luv":[54.5091256699603548,83.3737269438019695,45.4248629854764],"rgb":[0.8,0.4,0.133333333333333331],"xyz":[0.299411493515164162,0.224575316483216686,0.0427146783129532],"hpluv":[28.5830621108902889,221.025945543219876,54.5091256699603548],"hsluv":[28.5830621108902889,91.902111940908739,54.5091256699603548]},"#cc6633":{"lch":[54.6381494647888388,91.324965774397,26.4577509745500876],"luv":[54.6381494647888388,81.7598754062496624,40.688722605278663],"rgb":[0.8,0.4,0.2],"xyz":[0.302499244247621868,0.225810416776199785,0.0589768321705643403],"hpluv":[26.4577509745500876,212.096187894217309,54.6381494647888388],"hsluv":[26.4577509745500876,83.5463344335908289,54.6381494647888388]},"#cc6644":{"lch":[54.8236025158742137,86.5353782352002,23.2174871910251],"luv":[54.8236025158742137,79.5273159773273903,34.1141861950638798],"rgb":[0.8,0.4,0.266666666666666663],"xyz":[0.306957239491840783,0.227593614873887379,0.0824556071234509858],"hpluv":[23.2174871910251,200.292852977212362,54.8236025158742137],"hsluv":[23.2174871910251,72.0055590941951635,54.8236025158742137]},"#cc6655":{"lch":[55.0701314820163077,80.921365039871,18.5706632184066578],"luv":[55.0701314820163077,76.7079190692017505,25.7713498286149552],"rgb":[0.8,0.4,0.333333333333333315],"xyz":[0.312919653531202768,0.229978580489632212,0.113857654397424887],"hpluv":[18.5706632184066578,186.460314881725708,55.0701314820163077],"hsluv":[18.5706632184066578,57.463707489477386,55.0701314820163077]},"#cc6666":{"lch":[55.3812986167643686,75.0592421503045841,12.1770506300619576],"luv":[55.3812986167643686,73.3704437553848834,15.8324923911544637],"rgb":[0.8,0.4,0.4],"xyz":[0.320504649304710953,0.233012578799035502,0.153805298804568835],"hpluv":[12.1770506300619576,171.980959079196282,55.3812986167643686],"hsluv":[12.1770506300619576,45.9214429163451925,55.3812986167643686]},"#cc6677":{"lch":[55.7597240294908403,69.7578535305897702,3.7339069954147126],"luv":[55.7597240294908403,69.6097753289024,4.54283037928401079],"rgb":[0.8,0.4,0.466666666666666674],"xyz":[0.329818662324912859,0.236738184007116326,0.202859100710966744],"hpluv":[3.7339069954147126,158.749300244695775,55.7597240294908403],"hsluv":[3.7339069954147126,47.4001946683844935,55.7597240294908403]},"#cc6688":{"lch":[56.2071770412836855,65.9988703650454198,353.20230012342131],"luv":[56.2071770412836855,65.534915505775615,-7.81189727997758432],"rgb":[0.8,0.4,0.533333333333333326],"xyz":[0.340959079426856659,0.241194350847893924,0.26153196411453894],"hpluv":[353.20230012342131,148.999240608904586,56.2071770412836855],"hsluv":[353.20230012342131,48.9818497710332537,56.2071770412836855]},"#cc6699":{"lch":[56.7246474757154573,64.7364860461136544,341.13073016495332],"luv":[56.7246474757154573,61.2574795353059756,-20.9364234428957907],"rgb":[0.8,0.4,0.6],"xyz":[0.354016043535929414,0.246417136491523081,0.330298641755656819],"hpluv":[341.13073016495332,144.81603174775961,56.7246474757154573],"hsluv":[341.13073016495332,50.615240188358726,56.7246474757154573]},"#cc66aa":{"lch":[57.3124110050500661,66.5536753014576874,328.724652687595039],"luv":[57.3124110050500661,56.882247269492467,-34.5514347271451925],"rgb":[0.8,0.4,0.66666666666666663],"xyz":[0.369073736501181915,0.252440213677624203,0.40960249137265542],"hpluv":[328.724652687595039,147.354258527000582,57.3124110050500661],"hsluv":[328.724652687595039,52.5782983823555057,57.3124110050500661]},"#cc66bb":{"lch":[57.9700950113726634,71.4140361316817831,317.320615294889421],"luv":[57.9700950113726634,52.5006394421278628,-48.4112323204526405],"rgb":[0.8,0.4,0.733333333333333282],"xyz":[0.386211328608518589,0.259295250520558962,0.499860476471297166],"hpluv":[317.320615294889421,156.321564636453559,57.9700950113726634],"hsluv":[317.320615294889421,55.7867325729046044,57.9700950113726634]},"#cc66cc":{"lch":[58.6967474031167882,78.7715159072838844,307.71501294924451],"luv":[58.6967474031167882,48.1872412824568599,-62.3132529717215391],"rgb":[0.8,0.4,0.8],"xyz":[0.405503704052240344,0.267012200698047764,0.601466987141567322],"hpluv":[307.71501294924451,170.292097080892688,58.6967474031167882],"hsluv":[307.71501294924451,58.9158791245518429,58.6967474031167882]},"#cc66dd":{"lch":[59.4909085631812928,87.9045601549668589,300.035118747227784],"luv":[59.4909085631812928,43.9989332886277964,-76.1006278916348151],"rgb":[0.8,0.4,0.866666666666666696],"xyz":[0.427022029602873832,0.27561953091830127,0.71479683504157332],"hpluv":[300.035118747227784,187.499506963343748,59.4909085631812928],"hsluv":[300.035118747227784,69.8010448945604907,59.4909085631812928]},"#cc66ee":{"lch":[60.3506853352839272,98.1678970647401314,294.030303780460372],"luv":[60.3506853352839272,39.9759075841908498,-89.6597057040320919],"rgb":[0.8,0.4,0.933333333333333348],"xyz":[0.450834209869048597,0.285144403024771287,0.840207651110096543],"hpluv":[294.030303780460372,206.408039079415715,60.3506853352839272],"hsluv":[294.030303780460372,84.6650997716967169,60.3506853352839272]},"#cc66ff":{"lch":[61.2738253236974799,109.076950193692937,289.351384827957531],"luv":[61.2738253236974799,36.143813792667018,-102.914555763887847],"rgb":[0.8,0.4,1],"xyz":[0.477005258278879962,0.295612822388704,0.978041839401878388],"hpluv":[289.351384827957531,225.890163025573315,61.2738253236974799],"hsluv":[289.351384827957531,99.9999999999987,61.2738253236974799]},"#cc7700":{"lch":[58.0681687130694684,90.1274111260576,38.2527636780657616],"luv":[58.0681687130694684,70.775890411238,55.8007488550273862],"rgb":[0.8,0.466666666666666674,0],"xyz":[0.314978207930467602,0.260327983134806762,0.0336609331700240683],"hpluv":[38.2527636780657616,196.95095583694345,58.0681687130694684],"hsluv":[38.2527636780657616,100.00000000000226,58.0681687130694684]},"#cc7711":{"lch":[58.1065272060428,88.8714225311590837,37.658223554494576],"luv":[58.1065272060428,70.356768805075,54.2959927252558288],"rgb":[0.8,0.466666666666666674,0.0666666666666666657],"xyz":[0.315989873430104706,0.260732649334661615,0.0389890381347797],"hpluv":[37.658223554494576,194.078102813960953,58.1065272060428],"hsluv":[37.658223554494576,97.5201736027742925,58.1065272060428]},"#cc7722":{"lch":[58.1775287784180364,86.6008188207648573,36.5262538454337786],"luv":[58.1775287784180364,69.5910513475594286,51.5440335321973748],"rgb":[0.8,0.466666666666666674,0.133333333333333331],"xyz":[0.317865231568581763,0.261482792590052415,0.0488659243307589],"hpluv":[36.5262538454337786,188.888733728089306,58.1775287784180364],"hsluv":[36.5262538454337786,92.9922153048462832,58.1775287784180364]},"#cc7733":{"lch":[58.2941365993826111,83.0249283524691606,34.5753600294232513],"luv":[58.2941365993826111,68.3611064237300781,47.1157920070976459],"rgb":[0.8,0.466666666666666674,0.2],"xyz":[0.320952982301039469,0.262717892883035542,0.0651280781883700477],"hpluv":[34.5753600294232513,180.726967614303447,58.2941365993826111],"hsluv":[34.5753600294232513,85.7262720770138458,58.2941365993826111]},"#cc7744":{"lch":[58.4618482438389577,78.2181767747613321,31.5590767862408974],"luv":[58.4618482438389577,66.6497843857434447,40.9376284034837923],"rgb":[0.8,0.466666666666666674,0.266666666666666663],"xyz":[0.325410977545258384,0.264501090980723108,0.0886068531412566862],"hpluv":[31.5590767862408974,169.775287201810244,58.4618482438389577],"hsluv":[31.5590767862408974,75.6318325574838,58.4618482438389577]},"#cc7755":{"lch":[58.6849825995062133,72.4480337937475,27.1381330463907204],"luv":[58.6849825995062133,64.4721877935720187,33.0462494345479811],"rgb":[0.8,0.466666666666666674,0.333333333333333315],"xyz":[0.331373391584620369,0.266886056596467969,0.120008900415230588],"hpluv":[27.1381330463907204,156.653084219512607,58.6849825995062133],"hsluv":[27.1381330463907204,62.8141161752895059,58.6849825995062133]},"#cc7766":{"lch":[58.9669266929607829,66.2083874258113667,20.8554290651987451],"luv":[58.9669266929607829,61.8705265372573407,23.5709251309504673],"rgb":[0.8,0.466666666666666674,0.4],"xyz":[0.338958387358128554,0.269920054905871287,0.159956544822374536],"hpluv":[20.8554290651987451,142.476698671576116,58.9669266929607829],"hsluv":[20.8554290651987451,47.542957476979069,58.9669266929607829]},"#cc7777":{"lch":[59.3102652975897229,60.2635194006596251,12.177050630062082],"luv":[59.3102652975897229,58.907617956407762,12.7115820123061383],"rgb":[0.8,0.466666666666666674,0.466666666666666674],"xyz":[0.34827240037833046,0.273645660113952083,0.209010346728772445],"hpluv":[12.177050630062082,128.932959302114057,59.3102652975897229],"hsluv":[12.177050630062082,43.5373021749198443,59.3102652975897229]},"#cc7788":{"lch":[59.7168613687891963,55.6638868177661834,0.734433949810619269],"luv":[59.7168613687891963,55.6593138534775917,0.713496335773026069],"rgb":[0.8,0.466666666666666674,0.533333333333333326],"xyz":[0.35941281748027426,0.278101826954729681,0.26768321013234464],"hpluv":[0.734433949810619269,118.281243349182901,59.7168613687891963],"hsluv":[0.734433949810619269,45.0130000154657779,59.7168613687891963]},"#cc7799":{"lch":[60.187915321807921,53.6038783156568073,346.890122071781263],"luv":[60.187915321807921,52.2067938730354371,-12.1583898596044406],"rgb":[0.8,0.466666666666666674,0.6],"xyz":[0.37246978158934696,0.283324612598358838,0.336449887773462519],"hpluv":[346.890122071781263,113.012436406344946,60.187915321807921],"hsluv":[346.890122071781263,46.5549497102544,60.187915321807921]},"#cc77aa":{"lch":[60.7240163061688349,54.9761438743297148,332.197464335395125],"luv":[60.7240163061688349,48.6297161920954863,-25.6422912074417866],"rgb":[0.8,0.466666666666666674,0.66666666666666663],"xyz":[0.387527474554599516,0.28934768978445996,0.41575373739046112],"hpluv":[332.197464335395125,114.882297594683308,60.7240163061688349],"hsluv":[332.197464335395125,48.1189262345266471,60.7240163061688349]},"#cc77bb":{"lch":[61.3251919150652043,59.875823087291181,318.726648244723037],"luv":[61.3251919150652043,45.0009333565193046,-39.4972174643062743],"rgb":[0.8,0.466666666666666674,0.733333333333333282],"xyz":[0.40466506666193619,0.296202726627394719,0.506011722489102866],"hpluv":[318.726648244723037,123.894465684476771,61.3251919150652043],"hsluv":[318.726648244723037,49.6642191709372156,61.3251919150652043]},"#cc77cc":{"lch":[61.9909592768387228,67.6487625915650881,307.715012949244965],"luv":[61.9909592768387228,41.3830711255614219,-53.514451360232222],"rgb":[0.8,0.466666666666666674,0.8],"xyz":[0.423957442105657889,0.303919676804883521,0.607618233159373],"hpluv":[307.715012949244965,138.474825543749517,61.9909592768387228],"hsluv":[307.715012949244965,51.1553628289834066,61.9909592768387228]},"#cc77dd":{"lch":[62.7203784873954362,77.3966796673740305,299.257833182927357],"luv":[62.7203784873954362,37.8268934727942252,-67.5231231041040161],"rgb":[0.8,0.466666666666666674,0.866666666666666696],"xyz":[0.445475767656291488,0.312527007025137,0.720948081059379],"hpluv":[299.257833182927357,156.586020071329443,62.7203784873954362],"hsluv":[299.257833182927357,66.8390608629672158,62.7203784873954362]},"#cc77ee":{"lch":[63.5121081687847351,88.3507940057556453,292.894169444170245],"luv":[63.5121081687847351,34.371127600373157,-81.3909601179783],"rgb":[0.8,0.466666666666666674,0.933333333333333348],"xyz":[0.469287947922466198,0.322051879131607044,0.846358897127902243],"hpluv":[292.894169444170245,176.519730115921618,63.5121081687847351],"hsluv":[292.894169444170245,83.1130682001540322,63.5121081687847351]},"#cc77ff":{"lch":[64.3644622692190467,99.9639900757921112,288.092077643344339],"luv":[64.3644622692190467,31.0433170684360782,-95.0216384686225553],"rgb":[0.8,0.466666666666666674,1],"xyz":[0.495458996332297619,0.332520298495539768,0.984193085419684088],"hpluv":[288.092077643344339,197.077372703744913,64.3644622692190467],"hsluv":[288.092077643344339,99.9999999999984794,64.3644622692190467]},"#cc8800":{"lch":[62.03823759594124,83.9779445354575813,47.4964941193052752],"luv":[62.03823759594124,56.738465406715342,61.9115636346826221],"rgb":[0.8,0.533333333333333326,0],"xyz":[0.337050577655438111,0.304472722584748334,0.0410183897450140181],"hpluv":[47.4964941193052752,171.769129739761638,62.03823759594124],"hsluv":[47.4964941193052752,100.000000000002245,62.03823759594124]},"#cc8811":{"lch":[62.0727951053461879,82.7448035916202542,47.0033697453910904],"luv":[62.0727951053461879,56.4282611354359744,60.519037225048919],"rgb":[0.8,0.533333333333333326,0.0666666666666666657],"xyz":[0.338062243155075215,0.304877388784603187,0.0463464947097696478],"hpluv":[47.0033697453910904,169.152629782786704,62.0727951053461879],"hsluv":[47.0033697453910904,97.8669953021426,62.0727951053461879]},"#cc8822":{"lch":[62.1367747194074127,80.5023095814055836,46.0605806208152728],"luv":[62.1367747194074127,55.8603434591127268,57.9676105818622389],"rgb":[0.8,0.533333333333333326,0.133333333333333331],"xyz":[0.339937601293552272,0.305627532039994,0.0562233809057488473],"hpluv":[46.0605806208152728,164.398919884807611,62.1367747194074127],"hsluv":[46.0605806208152728,93.9640765124603,62.1367747194074127]},"#cc8833":{"lch":[62.2418885518498541,76.9329948636363241,44.4230175211057343],"luv":[62.2418885518498541,54.9448944756850963,53.8492736231801246],"rgb":[0.8,0.533333333333333326,0.2],"xyz":[0.34302535202601,0.306862632332977114,0.0724855347633599906],"hpluv":[44.4230175211057343,156.844467463147254,62.2418885518498541],"hsluv":[44.4230175211057343,87.678806867525978,62.2418885518498541]},"#cc8844":{"lch":[62.3931521103864668,72.0506908450526424,41.8566920827827929],"luv":[62.3931521103864668,53.6645163812677168,48.0772475586321661],"rgb":[0.8,0.533333333333333326,0.266666666666666663],"xyz":[0.347483347270228893,0.30864583043066468,0.0959643097162466291],"hpluv":[41.8566920827827929,146.534723453778,62.3931521103864668],"hsluv":[41.8566920827827929,78.900857077449,62.3931521103864668]},"#cc8855":{"lch":[62.5945538889838673,66.028576146577123,38.0101307109045408],"luv":[62.5945538889838673,52.0240395147187513,40.6604498316892062],"rgb":[0.8,0.533333333333333326,0.333333333333333315],"xyz":[0.353445761309590878,0.311030796046409541,0.127366356990220531],"hpluv":[38.0101307109045408,133.855034432196135,62.5945538889838673],"hsluv":[38.0101307109045408,67.6770944211119314,62.5945538889838673]},"#cc8866":{"lch":[62.8492816845599265,59.2369010326618337,32.3420191431213624],"luv":[62.8492816845599265,50.047464385327217,31.6900891849924626],"rgb":[0.8,0.533333333333333326,0.4],"xyz":[0.361030757083099063,0.31406479435581286,0.167314001397364492],"hpluv":[32.3420191431213624,119.60004374597824,62.8492816845599265],"hsluv":[32.3420191431213624,54.1901780257147436,62.8492816845599265]},"#cc8877":{"lch":[63.1598410661450771,52.3169991415742,24.053169540805424],"luv":[63.1598410661450771,47.7741895619322392,21.3235834436907119],"rgb":[0.8,0.533333333333333326,0.466666666666666674],"xyz":[0.370344770103300969,0.317790399563893655,0.216367803303762402],"hpluv":[24.053169540805424,105.109295035123296,63.1598410661450771],"hsluv":[24.053169540805424,38.7316682482548558,63.1598410661450771]},"#cc8888":{"lch":[63.5281271999152182,46.2961098245983322,12.1770506300622312],"luv":[63.5281271999152182,45.2544686659219906,9.76539045078869528],"rgb":[0.8,0.533333333333333326,0.533333333333333326],"xyz":[0.381485187205244769,0.322246566404671253,0.275040666707334569],"hpluv":[12.1770506300622312,92.4736018048895403,63.5281271999152182],"hsluv":[12.1770506300622312,40.0703189706204199,63.5281271999152182]},"#cc8899":{"lch":[63.9554753143552119,42.6335463086933615,356.296984030050055],"luv":[63.9554753143552119,42.5445368433351,-2.7534806412255004],"rgb":[0.8,0.533333333333333326,0.6],"xyz":[0.394542151314317469,0.327469352048300411,0.343807344348452504],"hpluv":[356.296984030050055,84.588837176407921,63.9554753143552119],"hsluv":[356.296984030050055,41.5088242950414781,63.9554753143552119]},"#cc88aa":{"lch":[64.442701858069384,42.8028595258545153,338.056731442080661],"luv":[64.442701858069384,39.701977128446444,-15.9949303118941444],"rgb":[0.8,0.533333333333333326,0.66666666666666663],"xyz":[0.40959984427957,0.333492429234401533,0.423111193965451104],"hpluv":[338.056731442080661,84.28268641071584,64.442701858069384],"hsluv":[338.056731442080661,42.9831867606486924,64.442701858069384]},"#cc88bb":{"lch":[64.9901424985427099,47.2946673003310707,321.051918945199532],"luv":[64.9901424985427099,36.7818149859187784,-29.7301806484697622],"rgb":[0.8,0.533333333333333326,0.733333333333333282],"xyz":[0.426737436386906699,0.340347466077336291,0.513369179064092851],"hpluv":[321.051918945199532,92.3430068912496296,64.9901424985427099],"hsluv":[321.051918945199532,44.4548878388224864,64.9901424985427099]},"#cc88cc":{"lch":[65.5976900795525637,55.3077284551996158,307.715012949245761],"luv":[65.5976900795525637,33.8336367550969399,-43.751912538747959],"rgb":[0.8,0.533333333333333326,0.8],"xyz":[0.446029811830628398,0.348064416254825093,0.614975689734363],"hpluv":[307.715012949245761,106.988377595373095,65.5976900795525637],"hsluv":[307.715012949245761,45.8886814173124122,65.5976900795525637]},"#cc88dd":{"lch":[66.2648339334855905,65.6132669537387727,298.095232643272539],"luv":[66.2648339334855905,30.8998122908772608,-57.8817967994354774],"rgb":[0.8,0.533333333333333326,0.866666666666666696],"xyz":[0.467548137381262,0.356671746475078599,0.728305537634369],"hpluv":[298.095232643272539,125.645770778198369,66.2648339334855905],"hsluv":[298.095232643272539,63.1276078601331037,66.2648339334855905]},"#cc88ee":{"lch":[66.9907009061042,77.2337728548956193,291.267726386147558],"luv":[66.9907009061042,28.0147259973813227,-71.9738202174461463],"rgb":[0.8,0.533333333333333326,0.933333333333333348],"xyz":[0.491360317647436706,0.366196618581548616,0.853716353702892228],"hpluv":[291.267726386147558,146.295866424915545,66.9907009061042],"hsluv":[291.267726386147558,81.1585487563552874,66.9907009061042]},"#cc88ff":{"lch":[67.7740978167257,89.535143384050329,286.350196506734335],"luv":[67.7740978167257,25.2048126805589376,-85.914255618845857],"rgb":[0.8,0.533333333333333326,1],"xyz":[0.517531366057268127,0.37666503794548134,0.991550541994674073],"hpluv":[286.350196506734335,167.63670457649863,67.7740978167257],"hsluv":[286.350196506734335,99.9999999999982379,67.7740978167257]},"#cc9900":{"lch":[66.2294666531998217,80.7116888085701163,57.6888018595631422],"luv":[66.2294666531998217,43.1418134232290527,68.2140795209226383],"rgb":[0.8,0.6,0],"xyz":[0.362920178107905556,0.356211923489684,0.0496415898958362731],"hpluv":[57.6888018595631422,154.64094800189136,66.2294666531998217],"hsluv":[57.6888018595631422,100.000000000002331,66.2294666531998217]},"#cc9911":{"lch":[66.2605931548954459,79.5070460268624,57.3307205104302042],"luv":[66.2605931548954459,42.9170325725223805,66.9290570909725915],"rgb":[0.8,0.6,0.0666666666666666657],"xyz":[0.36393184360754266,0.356616589689538854,0.0549696948605919],"hpluv":[57.3307205104302042,152.261332222626407,66.2605931548954459],"hsluv":[57.3307205104302042,98.1673920986410877,66.2605931548954459]},"#cc9922":{"lch":[66.318231165714252,77.3048675578901339,56.6444543825244],"luv":[66.318231165714252,42.5047546114663533,64.5708013235236535],"rgb":[0.8,0.6,0.133333333333333331],"xyz":[0.365807201746019717,0.357366732944929655,0.0648465810565711],"hpluv":[56.6444543825244,147.915345655385721,66.318231165714252],"hsluv":[56.6444543825244,94.8079930897652901,66.318231165714252]},"#cc9933":{"lch":[66.4129558628457772,73.7662990604099207,55.446788144651876],"luv":[66.4129558628457772,41.8381333944198062,60.7539091017242114],"rgb":[0.8,0.6,0.2],"xyz":[0.368894952478477423,0.358601833237912782,0.0811087349141822456],"hpluv":[55.446788144651876,140.943324602767206,66.4129558628457772],"hsluv":[55.446788144651876,89.3812598244831804,66.4129558628457772]},"#cc9944":{"lch":[66.5493334014023361,68.8491039809152596,53.5533094328142383],"luv":[66.5493334014023361,40.9015039610359423,55.3829043360877122],"rgb":[0.8,0.6,0.266666666666666663],"xyz":[0.373352947722696338,0.360385031335600348,0.104587509867068884],"hpluv":[53.5533094328142383,131.278591794008349,66.5493334014023361],"hsluv":[53.5533094328142383,81.7675495661837459,66.5493334014023361]},"#cc9955":{"lch":[66.7310322275847341,62.6300376921671571,50.6698876242401539],"luv":[66.7310322275847341,39.6941340540006422,48.4447865412296466],"rgb":[0.8,0.6,0.333333333333333315],"xyz":[0.379315361762058323,0.362769996951345208,0.135989557141042799],"hpluv":[50.6698876242401539,119.095172229338388,66.7310322275847341],"hsluv":[50.6698876242401539,71.9728873324543912,66.7310322275847341]},"#cc9966":{"lch":[66.9610303820851,55.3290996264574488,46.2964172073191236],"luv":[66.9610303820851,38.2284030091626121,39.9987308404091877],"rgb":[0.8,0.6,0.4],"xyz":[0.386900357535566508,0.365803995260748527,0.175937201548186761],"hpluv":[46.2964172073191236,104.850571413887963,66.9610303820851],"hsluv":[46.2964172073191236,60.1139469393688586,66.9610303820851]},"#cc9977":{"lch":[67.2417240975963608,47.3725698769106316,39.5497292985384448],"luv":[67.2417240975963608,36.5276723895038842,30.1643751227811094],"rgb":[0.8,0.6,0.466666666666666674],"xyz":[0.396214370555768414,0.369529600468829322,0.22499100345458467],"hpluv":[39.5497292985384448,89.3979234879296598,67.2417240975963608],"hsluv":[39.5497292985384448,46.4000360859745484,67.2417240975963608]},"#cc9988":{"lch":[67.5749927230407508,39.5461637624335367,28.8927829606528306],"luv":[67.5749927230407508,34.6236706423047593,19.1076031876950658],"rgb":[0.8,0.6,0.533333333333333326],"xyz":[0.407354787657712214,0.37398576730960692,0.28366386685815681],"hpluv":[28.8927829606528306,74.2604675709266076,67.5749927230407508],"hsluv":[28.8927829606528306,33.7758353105824227,67.5749927230407508]},"#cc9999":{"lch":[67.962242737641,33.3028609095241,12.177050630062606],"luv":[67.962242737641,32.5535618700051401,7.02468179598591647],"rgb":[0.8,0.6,0.6],"xyz":[0.420411751766784914,0.379208552953236078,0.352430544499274745],"hpluv":[12.177050630062606,62.1803508213615217,67.962242737641],"hsluv":[12.177050630062606,35.0991912912463349,67.962242737641]},"#cc99aa":{"lch":[68.4044417972397838,30.9200145018949506,349.049331623372325],"luv":[68.4044417972397838,30.3569952347367966,-5.87368173427548168],"rgb":[0.8,0.6,0.66666666666666663],"xyz":[0.43546944473203747,0.3852316301393372,0.431734394116273346],"hpluv":[349.049331623372325,57.3580941092039609,68.4044417972397838],"hsluv":[349.049331623372325,36.4663034143199312,68.4044417972397838]},"#cc99bb":{"lch":[68.9021485343020856,34.1112448474386483,325.385883063702067],"luv":[68.9021485343020856,28.0734328763681233,-19.3767745401024669],"rgb":[0.8,0.6,0.733333333333333282],"xyz":[0.452607036839374144,0.392086666982271959,0.521992379214915],"hpluv":[325.385883063702067,62.8208966420876678,68.9021485343020856],"hsluv":[325.385883063702067,37.8410036888738404,68.9021485343020856]},"#cc99cc":{"lch":[69.4555411877739601,42.0770553751994854,307.715012949247],"luv":[69.4555411877739601,25.7399796927440505,-33.285613025220492],"rgb":[0.8,0.6,0.8],"xyz":[0.471899412283095843,0.39980361715976076,0.623598889885185192],"hpluv":[307.715012949247,76.8736967911951581,69.4555411877739601],"hsluv":[307.715012949247,39.1886552488513473,69.4555411877739601]},"#cc99dd":{"lch":[70.0644466506374215,52.8759829560521695,296.254085335195782],"luv":[70.0644466506374215,23.3898305690433475,-47.4213601610093534],"rgb":[0.8,0.6,0.866666666666666696],"xyz":[0.493417737833729442,0.408410947380014266,0.73692873778519119],"hpluv":[296.254085335195782,95.7635162234915498,70.0644466506374215],"hsluv":[296.254085335195782,58.3905887561973813,70.0644466506374215]},"#cc99ee":{"lch":[70.7283706212672,65.1265075826905218,288.858843135035954],"luv":[70.7283706212672,21.0513451251656711,-61.6303728557552191],"rgb":[0.8,0.6,0.933333333333333348],"xyz":[0.517229918099904151,0.417935819486484283,0.862339553853714413],"hpluv":[288.858843135035954,116.843205481858362,70.7283706212672],"hsluv":[288.858843135035954,78.6530020758075494,70.7283706212672]},"#cc99ff":{"lch":[71.4465289765693115,78.0706881495843561,283.894640570210413],"luv":[71.4465289765693115,18.7476796475605703,-75.7862576987549517],"rgb":[0.8,0.6,1],"xyz":[0.543400966509735572,0.428404238850417,1.00017374214549637],"hpluv":[283.894640570210413,138.658404713871533,71.4465289765693115],"hsluv":[283.894640570210413,99.9999999999978,71.4465289765693115]},"#990000":{"lch":[31.2857235930303546,105.214874065330946,12.1770506300617765],"luv":[31.2857235930303546,102.847587834444283,22.1933188419334826],"rgb":[0.6,0,0],"xyz":[0.131365760434599882,0.067735470224092,0.00615777002037173893],"hpluv":[12.1770506300617765,426.746789183125316,31.2857235930303546],"hsluv":[12.1770506300617765,100.000000000002217,31.2857235930303546]},"#990011":{"lch":[31.379701704172021,102.819321078199806,10.8595456684147944],"luv":[31.379701704172021,100.978030711674904,19.3713732237542438],"rgb":[0.6,0,0.0666666666666666657],"xyz":[0.132377425934237014,0.0681401364239468538,0.0114858749851273704],"hpluv":[10.8595456684147944,415.781582167217948,31.379701704172021],"hsluv":[10.8595456684147944,99.9999999999964473,31.379701704172021]},"#990022":{"lch":[31.5529326060038784,98.7447775317108807,8.37468971343924729],"luv":[31.5529326060038784,97.6918390895904309,14.3817824027706251],"rgb":[0.6,0,0.133333333333333331],"xyz":[0.134252784072714015,0.0688902796793376682,0.0213627611811065682],"hpluv":[8.37468971343924729,397.112659756655944,31.5529326060038784],"hsluv":[8.37468971343924729,99.999999999996632,31.5529326060038784]},"#990033":{"lch":[31.8354354483696653,92.9837515463916162,4.18138532137367758],"luv":[31.8354354483696653,92.7362491408617586,6.7798338419980837],"rgb":[0.6,0,0.2],"xyz":[0.137340534805171777,0.070125379972320781,0.0376249150387177114],"hpluv":[4.18138532137367758,370.62575576901952,31.8354354483696653],"hsluv":[4.18138532137367758,99.9999999999969,31.8354354483696653]},"#990044":{"lch":[32.2375108843075537,86.4821897260425771,357.977822115898675],"luv":[32.2375108843075537,86.4283323676992552,-3.05163955108307716],"rgb":[0.6,0,0.266666666666666663],"xyz":[0.141798530049390636,0.071908578070008361,0.0611036899916043499],"hpluv":[357.977822115898675,340.411718586576399,32.2375108843075537],"hsluv":[357.977822115898675,99.9999999999971294,32.2375108843075537]},"#990055":{"lch":[32.7650133258702,80.5606445545256804,349.629319937368109],"luv":[32.7650133258702,79.244583226626645,-14.502188809930411],"rgb":[0.6,0,0.333333333333333315],"xyz":[0.147760944088752622,0.0742935436857532,0.0925057372655782584],"hpluv":[349.629319937368109,311.998071704954214,32.7650133258702],"hsluv":[349.629319937368109,99.9999999999974847,32.7650133258702]},"#990066":{"lch":[33.4199981031921354,76.5714397631706589,339.419101050621862],"luv":[33.4199981031921354,71.6844038684648268,-26.9171252073414813],"rgb":[0.6,0,0.4],"xyz":[0.155345939862260807,0.0773275419951565124,0.13245338167272222],"hpluv":[339.419101050621862,290.7366076723265,33.4199981031921354],"hsluv":[339.419101050621862,99.9999999999978257,33.4199981031921354]},"#990077":{"lch":[34.2012599030024091,75.4745938555541187,328.234093427391315],"luv":[34.2012599030024091,64.1689603650959413,-39.7335984190155429],"rgb":[0.6,0,0.466666666666666674],"xyz":[0.164659952882462712,0.0810531472032373218,0.181507183579120129],"hpluv":[328.234093427391315,280.025774017920355,34.2012599030024091],"hsluv":[328.234093427391315,99.9999999999982094,34.2012599030024091]},"#990088":{"lch":[35.1048906557013396,77.5195253213057214,317.327493504651898],"luv":[35.1048906557013396,56.9954501323273419,-52.5432723595889613],"rgb":[0.6,0,0.533333333333333326],"xyz":[0.175800369984406568,0.0855093140440149196,0.240180046982692297],"hpluv":[317.327493504651898,280.209468657326,35.1048906557013396],"hsluv":[317.327493504651898,99.9999999999985505,35.1048906557013396]},"#990099":{"lch":[36.1248689761228263,82.286593786153162,307.715012949243601],"luv":[36.1248689761228263,50.3375351282041592,-65.0939019735657922],"rgb":[0.6,0,0.6],"xyz":[0.188857334093479268,0.0907320996876440772,0.308946724623810232],"hpluv":[307.715012949243601,289.042783730483336,36.1248689761228263],"hsluv":[307.715012949243601,99.9999999999988205,36.1248689761228263]},"#9900aa":{"lch":[37.2536516336468,89.0432435337247,299.813571633796073],"luv":[37.2536516336468,44.2704748017611038,-77.2581664281054685],"rgb":[0.6,0,0.66666666666666663],"xyz":[0.203915027058731824,0.0967551768737451856,0.388250574240808777],"hpluv":[299.813571633796073,303.299328566743952,37.2536516336468],"hsluv":[299.813571633796073,99.9999999999990905,37.2536516336468]},"#9900bb":{"lch":[38.4827280957899163,97.0854614833978786,293.557760104203282],"luv":[38.4827280957899163,38.802472411223853,-88.9941288300556579],"rgb":[0.6,0,0.733333333333333282],"xyz":[0.221052619166068443,0.103610213716679944,0.478508559339450579],"hpluv":[293.557760104203282,320.130957524774431,38.4827280957899163],"hsluv":[293.557760104203282,99.9999999999993179,38.4827280957899163]},"#9900cc":{"lch":[39.8031058181596933,105.884836559305498,288.673688741635],"luv":[39.8031058181596933,33.9019931565070394,-100.310784431221492],"rgb":[0.6,0,0.8],"xyz":[0.240344994609790197,0.111327163894168746,0.580115070009720735],"hpluv":[288.673688741635,337.564008898092311,39.8031058181596933],"hsluv":[288.673688741635,99.9999999999995879,39.8031058181596933]},"#9900dd":{"lch":[41.2057071388761145,115.092674624289529,284.860629917023232],"luv":[41.2057071388761145,29.5176685469448401,-111.243116621772529],"rgb":[0.6,0,0.866666666666666696],"xyz":[0.26186332016042374,0.11993449411442228,0.693444917909726732],"hpluv":[284.860629917023232,354.429316861661562,41.2057071388761145],"hsluv":[284.860629917023232,99.9999999999996732,41.2057071388761145]},"#9900ee":{"lch":[42.6816722484951754,124.494824438150232,281.862271937449748],"luv":[42.6816722484951754,25.5911328567321625,-121.836181945245357],"rgb":[0.6,0,0.933333333333333348],"xyz":[0.285675500426598505,0.129459366220892297,0.81885573397825],"hpluv":[281.862271937449748,370.125661914021862,42.6816722484951754],"hsluv":[281.862271937449748,99.9999999999998437,42.6816722484951754]},"#9900ff":{"lch":[44.2225734052255817,133.965544030308308,279.479958267333473],"luv":[44.2225734052255817,22.0644732467518,-132.136013288126151],"rgb":[0.6,0,1],"xyz":[0.311846548836429871,0.139927785584825,0.956689922270031801],"hpluv":[279.479958267333473,384.404468177447882,44.2225734052255817],"hsluv":[279.479958267333473,99.9999999999999574,44.2225734052255817]},"#bb0000":{"lch":[38.8409426943877918,130.623313921981463,12.1770506300617818],"luv":[38.8409426943877918,127.684349491075153,27.5528044852332741],"rgb":[0.733333333333333282,0,0],"xyz":[0.20493059501477473,0.105667338054495463,0.00960612164131736251],"hpluv":[12.1770506300617818,426.746789183125145,38.8409426943877918],"hsluv":[12.1770506300617818,100.000000000002217,38.8409426943877918]},"#bb0011":{"lch":[38.9108602521517142,128.680110500437479,11.3344428162225856],"luv":[38.9108602521517142,126.170422440132384,25.2902222149853806],"rgb":[0.733333333333333282,0,0.0666666666666666657],"xyz":[0.205942260514411862,0.106072004254350316,0.014934226606072994],"hpluv":[11.3344428162225856,419.642938315359174,38.9108602521517142],"hsluv":[11.3344428162225856,99.9999999999964189,38.9108602521517142]},"#bb0022":{"lch":[39.0399998564474373,125.270257566289573,9.75441483214293292],"luv":[39.0399998564474373,123.459226352671962,21.2239689767074431],"rgb":[0.733333333333333282,0,0.133333333333333331],"xyz":[0.207817618652888864,0.10682214750974113,0.0248111128020521918],"hpluv":[9.75441483214293292,407.171610230013243,39.0399998564474373],"hsluv":[9.75441483214293292,99.9999999999965326,39.0399998564474373]},"#bb0033":{"lch":[39.2513155564018916,120.169209623826248,7.10634666793171554],"luv":[39.2513155564018916,119.246098647877318,14.866300779810846],"rgb":[0.733333333333333282,0,0.2],"xyz":[0.210905369385346597,0.108057247802724243,0.041073266659663335],"hpluv":[7.10634666793171554,388.488631169232178,39.2513155564018916],"hsluv":[7.10634666793171554,99.9999999999967173,39.2513155564018916]},"#bb0044":{"lch":[39.5535843326651886,113.833969399977519,3.19865110237705785],"luv":[39.5535843326651886,113.656624965458903,6.3517077086437],"rgb":[0.733333333333333282,0,0.266666666666666663],"xyz":[0.215363364629565485,0.109840445900411823,0.0645520416125499735],"hpluv":[3.19865110237705785,365.195452768261646,39.5535843326651886],"hsluv":[3.19865110237705785,99.9999999999968878,39.5535843326651886]},"#bb0055":{"lch":[39.9527871554326666,107.03859947839959,357.869864695501747],"luv":[39.9527871554326666,106.964633924407806,-3.97855095665153691],"rgb":[0.733333333333333282,0,0.333333333333333315],"xyz":[0.221325778668927498,0.112225411516156656,0.095954088886523875],"hpluv":[357.869864695501747,339.963790558847222,39.9527871554326666],"hsluv":[357.869864695501747,99.9999999999971436,39.9527871554326666]},"#bb0066":{"lch":[40.452535568346093,100.749762000256624,351.053086521713055],"luv":[40.452535568346093,99.5239253968718884,-15.6685295004423661],"rgb":[0.733333333333333282,0,0.4],"xyz":[0.228910774442435655,0.115259409825559975,0.135901733293667837],"hpluv":[351.053086521713055,316.036764522848955,40.452535568346093],"hsluv":[351.053086521713055,99.999999999997442,40.452535568346093]},"#bb0077":{"lch":[41.0543478797665813,95.9494038996296581,342.883287985183927],"luv":[41.0543478797665813,91.699536963203,-28.2397420212797314],"rgb":[0.733333333333333282,0,0.466666666666666674],"xyz":[0.238224787462637588,0.118985015033640784,0.184955535200065746],"hpluv":[342.883287985183927,296.56674422547627,41.0543478797665813],"hsluv":[342.883287985183927,99.9999999999977,41.0543478797665813]},"#bb0088":{"lch":[41.7578935904565398,93.4210879643116243,333.788939203308246],"luv":[41.7578935904565398,83.8148890966975699,-41.2621381189091565],"rgb":[0.733333333333333282,0,0.533333333333333326],"xyz":[0.249365204564581389,0.123441181874418382,0.243628398603637913],"hpluv":[333.788939203308246,283.887103643995431,41.7578935904565398],"hsluv":[333.788939203308246,99.9999999999980531,41.7578935904565398]},"#bb0099":{"lch":[42.5612451572515,93.5592166386053918,324.452137443226093],"luv":[42.5612451572515,76.1225984195714318,-54.3937223205229472],"rgb":[0.733333333333333282,0,0.6],"xyz":[0.262422168673654088,0.12866396751804754,0.31239507624475582],"hpluv":[324.452137443226093,278.940502109978524,42.5612451572515],"hsluv":[324.452137443226093,99.9999999999983089,42.5612451572515]},"#bb00aa":{"lch":[43.461144448190268,96.3048592888224562,315.591494301740738],"luv":[43.461144448190268,68.7971872487792,-67.3911934105362747],"rgb":[0.733333333333333282,0,0.66666666666666663],"xyz":[0.277479861638906644,0.134687044704148634,0.391698925861754421],"hpluv":[315.591494301740738,281.181257774391042,43.461144448190268],"hsluv":[315.591494301740738,99.9999999999985647,43.461144448190268]},"#bb00bb":{"lch":[44.4532771259814652,101.257357078489918,307.715012949243601],"luv":[44.4532771259814652,61.9426024872754866,-80.100976021670192],"rgb":[0.733333333333333282,0,0.733333333333333282],"xyz":[0.294617453746243319,0.141542081547083393,0.481956910960396168],"hpluv":[307.715012949243601,289.042783730483507,44.4532771259814652],"hsluv":[307.715012949243601,99.9999999999988205,44.4532771259814652]},"#bb00cc":{"lch":[45.5325428123826796,107.876917991024385,301.028560594476971],"luv":[45.5325428123826796,55.6068066637028551,-92.44085940701639],"rgb":[0.733333333333333282,0,0.8],"xyz":[0.313909829189965,0.149259031724572194,0.583563421630666324],"hpluv":[301.028560594476971,300.639438898355309,45.5325428123826796],"hsluv":[301.028560594476971,99.999999999999,45.5325428123826796]},"#bb00dd":{"lch":[46.6933085129957348,115.650155059812704,295.504945579136574],"luv":[46.6933085129957348,49.7976850024997,-104.379830109799173],"rgb":[0.733333333333333282,0,0.866666666666666696],"xyz":[0.335428154740598616,0.157866361944825728,0.696893269530672321],"hpluv":[295.504945579136574,314.290242754568055,46.6933085129957348],"hsluv":[295.504945579136574,99.9999999999992468,46.6933085129957348]},"#bb00ee":{"lch":[47.929635203682146,124.167261181765113,290.999747870951808],"luv":[47.929635203682146,44.4970566855085821,-115.920320460682433],"rgb":[0.733333333333333282,0,0.933333333333333348],"xyz":[0.359240335006773326,0.167391234051295773,0.822304085599195544],"hpluv":[290.999747870951808,328.732244305823656,47.929635203682146],"hsluv":[290.999747870951808,99.9999999999993889,47.929635203682146]},"#bb00ff":{"lch":[49.2354711183318727,133.13261796854033,287.33664116340708],"luv":[49.2354711183318727,39.6715752597668896,-127.084460433075606],"rgb":[0.733333333333333282,0,1],"xyz":[0.385411383416604747,0.177859653415228469,0.96013827389097739],"hpluv":[287.33664116340708,343.119737385630629,49.2354711183318727],"hsluv":[287.33664116340708,99.9999999999995595,49.2354711183318727]},"#991100":{"lch":[32.2007428060931531,101.551746681272988,13.5001929330929755],"luv":[32.2007428060931531,98.7457840078795,23.707116962774041],"rgb":[0.6,0.0666666666666666657,0],"xyz":[0.133370160695528289,0.071744270745948871,0.00682590344068119],"hpluv":[13.5001929330929755,400.185025755779861,32.2007428060931531],"hsluv":[13.5001929330929755,100.000000000002359,32.2007428060931531]},"#991111":{"lch":[32.2911967351305,99.2607003603350506,12.1770506300617907],"luv":[32.2911967351305,97.0273802968116854,20.9373854328113431],"rgb":[0.6,0.0666666666666666657,0.0666666666666666657],"xyz":[0.134381826195165421,0.0721489369458037239,0.0121540084054368204],"hpluv":[12.1770506300617907,390.060992150638072,32.2911967351305],"hsluv":[12.1770506300617907,91.4033806551417,32.2911967351305]},"#991122":{"lch":[32.4579836187547883,95.3555453821432337,9.67722696349737355],"luv":[32.4579836187547883,93.9986702773161085,16.0290368151797],"rgb":[0.6,0.0666666666666666657,0.133333333333333331],"xyz":[0.136257184333642423,0.0728990802011945382,0.0220308946014160165],"hpluv":[9.67722696349737355,372.789562407290305,32.4579836187547883],"hsluv":[9.67722696349737355,91.6870397393079,32.4579836187547883]},"#991133":{"lch":[32.7301206059751877,89.8170234432985382,5.44607482402752385],"luv":[32.7301206059751877,89.4115862394302,8.52443231910342547],"rgb":[0.6,0.0666666666666666657,0.2],"xyz":[0.139344935066100184,0.0741341804941776511,0.0382930484590271597],"hpluv":[5.44607482402752385,348.217328437078379,32.7301206059751877],"hsluv":[5.44607482402752385,92.1153976677825312,32.7301206059751877]},"#991144":{"lch":[33.1177416447746893,83.547386161100178,359.159050762907725],"luv":[33.1177416447746893,83.5383872622118275,-1.22620878349376072],"rgb":[0.6,0.0666666666666666657,0.266666666666666663],"xyz":[0.143802930310319044,0.0759173785918652311,0.0617718234119138],"hpluv":[359.159050762907725,320.119020896680809,33.1177416447746893],"hsluv":[359.159050762907725,92.6613616101701609,33.1177416447746893]},"#991155":{"lch":[33.6267967661613341,77.8365467514172451,350.652860745276428],"luv":[33.6267967661613341,76.8030661853154,-12.6418762341523276],"rgb":[0.6,0.0666666666666666657,0.333333333333333315],"xyz":[0.149765344349681029,0.078302344207610064,0.0931738706858877136],"hpluv":[350.652860745276428,293.722615948770908,33.6267967661613341],"hsluv":[350.652860745276428,93.2833986807069664,33.6267967661613341]},"#991166":{"lch":[34.2596587707945375,74.039902428482776,340.197584074025258],"luv":[34.2596587707945375,69.661662743272629,-25.0830599301957804],"rgb":[0.6,0.0666666666666666657,0.4],"xyz":[0.157350340123189214,0.0813363425170133825,0.133121515093031662],"hpluv":[340.197584074025258,274.234525914752396,34.2596587707945375],"hsluv":[340.197584074025258,93.9371476037906774,34.2596587707945375]},"#991177":{"lch":[35.0156115165229096,73.1458980715855773,328.71391057162549],"luv":[35.0156115165229096,62.509382661628635,-37.9855167657472848],"rgb":[0.6,0.0666666666666666657,0.466666666666666674],"xyz":[0.16666435314339112,0.0850619477250941919,0.182175316999429571],"hpluv":[328.71391057162549,265.074278305330154,35.0156115165229096],"hsluv":[328.71391057162549,94.5844210689808165,35.0156115165229096]},"#991188":{"lch":[35.8913494409224185,75.4242755669397269,317.528981112118743],"luv":[35.8913494409224185,55.6343762077295239,-50.9277677576239398],"rgb":[0.6,0.0666666666666666657,0.533333333333333326],"xyz":[0.177804770245334975,0.0895181145658717897,0.240848180403001738],"hpluv":[317.528981112118743,266.661726649655066,35.8913494409224185],"hsluv":[317.528981112118743,95.1976582537924116,35.8913494409224185]},"#991199":{"lch":[36.8815072257793,80.448343562419069,307.715012949243601],"luv":[36.8815072257793,49.2130143411107568,-63.6397297401445599],"rgb":[0.6,0.0666666666666666657,0.6],"xyz":[0.190861734354407675,0.0947409002095009473,0.309614858044119645],"hpluv":[307.715012949243601,276.788327826692239,36.8815072257793],"hsluv":[307.715012949243601,95.7603314825458511,36.8815072257793]},"#9911aa":{"lch":[37.9791974354050694,87.4639739592788,299.695850237394552],"luv":[37.9791974354050694,43.3292813921454609,-75.9771025690614152],"rgb":[0.6,0.0666666666666666657,0.66666666666666663],"xyz":[0.205919427319660231,0.100763977395602056,0.388918707661118246],"hpluv":[299.695850237394552,292.228621346341356,37.9791974354050694],"hsluv":[299.695850237394552,96.26500390067784,37.9791974354050694]},"#9911bb":{"lch":[39.176522525078866,95.7489617369993624,293.383950362709356],"luv":[39.176522525078866,38.0018814997404846,-87.8847010360338459],"rgb":[0.6,0.0666666666666666657,0.733333333333333282],"xyz":[0.22305701942699685,0.107619014238536814,0.47917669275976],"hpluv":[293.383950362709356,310.132668732371314,39.176522525078866],"hsluv":[293.383950362709356,96.7106400677762537,39.176522525078866]},"#9911cc":{"lch":[40.465031277763515,104.765415075180798,288.480743990765689],"luv":[40.465031277763515,33.2091620218187842,-99.3626879350770622],"rgb":[0.6,0.0666666666666666657,0.8],"xyz":[0.242349394870718604,0.115335964416025616,0.580783203430030204],"hpluv":[288.480743990765689,328.531778006508034,40.465031277763515],"hsluv":[288.480743990765689,97.1001366995766,40.465031277763515]},"#9911dd":{"lch":[41.8361001822542917,114.161763941518927,284.668123617886636],"luv":[41.8361001822542917,28.9080153835727351,-110.441092863219225],"rgb":[0.6,0.0666666666666666657,0.866666666666666696],"xyz":[0.263867720421352148,0.12394329463627915,0.694113051330036201],"hpluv":[284.668123617886636,346.265164959266087,41.8361001822542917],"hsluv":[284.668123617886636,97.4384492036098,41.8361001822542917]},"#9911ee":{"lch":[43.2812320372341617,123.724619665436521,281.679545129349094],"luv":[43.2812320372341617,25.0465268392882301,-121.162919264293635],"rgb":[0.6,0.0666666666666666657,0.933333333333333348],"xyz":[0.287679900687526913,0.133468166742749167,0.819523867398559425],"hpluv":[281.679545129349094,362.740326129136179,43.2812320372341617],"hsluv":[281.679545129349094,97.7313369542794561,43.2812320372341617]},"#9911ff":{"lch":[44.7922739406791948,133.33068560825987,279.310828677429186],"luv":[44.7922739406791948,21.5716156333953251,-131.574074664174219],"rgb":[0.6,0.0666666666666666657,1],"xyz":[0.313850949097358278,0.143936586106681863,0.95735805569034127],"hpluv":[279.310828677429186,377.716823123197173,44.7922739406791948],"hsluv":[279.310828677429186,99.9999999999993179,44.7922739406791948]},"#bb1100":{"lch":[39.5258701457598747,127.514079962112703,13.0219609303782402],"luv":[39.5258701457598747,124.2348987579322,28.7320469022032583],"rgb":[0.733333333333333282,0.0666666666666666657,0],"xyz":[0.206934995275703137,0.109676138576352333,0.0102742550616268143],"hpluv":[13.0219609303782402,409.370014873310311,39.5258701457598747],"hsluv":[13.0219609303782402,100.000000000002203,39.5258701457598747]},"#bb1111":{"lch":[39.5940766091873897,125.63034182067031,12.177050630061796],"luv":[39.5940766091873897,122.803716963532779,26.4996204863200759],"rgb":[0.733333333333333282,0.0666666666666666657,0.0666666666666666657],"xyz":[0.20794666077534027,0.110080804776207186,0.0156023600263824457],"hpluv":[12.177050630061796,402.627698793753552,39.5940766091873897],"hsluv":[12.177050630061796,94.3481495348726753,39.5940766091873897]},"#bb1122":{"lch":[39.7200723855077413,122.321798686612851,10.5915831721034426],"luv":[39.7200723855077413,120.23772406897838,22.4835972353282152],"rgb":[0.733333333333333282,0.0666666666666666657,0.133333333333333331],"xyz":[0.209822018913817271,0.110830948031598,0.0254792462223616401],"hpluv":[10.5915831721034426,390.780742551338619,39.7200723855077413],"hsluv":[10.5915831721034426,94.4721603032542561,39.7200723855077413]},"#bb1133":{"lch":[39.9262897734852,117.365304386249704,7.93115519261489421],"luv":[39.9262897734852,116.242655940639409,16.194431559742192],"rgb":[0.733333333333333282,0.0666666666666666657,0.2],"xyz":[0.212909769646275,0.112066048324581113,0.0417414000799727902],"hpluv":[7.93115519261489421,373.009679290071517,39.9262897734852],"hsluv":[7.93115519261489421,94.6648992051481173,39.9262897734852]},"#bb1144":{"lch":[40.2213637516280755,111.198866264944101,3.99799547396429888],"luv":[40.2213637516280755,110.928262725239179,7.75295991020434627],"rgb":[0.733333333333333282,0.0666666666666666657,0.266666666666666663],"xyz":[0.217367764890493892,0.113849246422268693,0.0652201750328594287],"hpluv":[3.99799547396429888,350.818828640487084,40.2213637516280755],"hsluv":[3.99799547396429888,94.920595316052,40.2213637516280755]},"#bb1155":{"lch":[40.6112374139617245,104.574630781207446,358.621384873601698],"luv":[40.6112374139617245,104.544360517453882,-2.51596641918957786],"rgb":[0.733333333333333282,0.0666666666666666657,0.333333333333333315],"xyz":[0.223330178929855905,0.116234212038013526,0.0966222223068333302],"hpluv":[358.621384873601698,326.752894857139097,40.6112374139617245],"hsluv":[358.621384873601698,95.2265760165129791,40.6112374139617245]},"#bb1166":{"lch":[41.0995768863194755,98.4443929415329251,351.723860164231496],"luv":[41.0995768863194755,97.4191751888273672,-14.1704907168206535],"rgb":[0.733333333333333282,0.0666666666666666657,0.4],"xyz":[0.230915174703364062,0.119268210347416845,0.136569866713977306],"hpluv":[351.723860164231496,303.943570446579713,41.0995768863194755],"hsluv":[351.723860164231496,95.5663396203906643,41.0995768863194755]},"#bb1177":{"lch":[41.688035181331955,93.7889310063258,343.43446060875408],"luv":[41.688035181331955,89.8961490854954093,-26.7403432832812094],"rgb":[0.733333333333333282,0.0666666666666666657,0.466666666666666674],"xyz":[0.240229187723566,0.122993815555497654,0.185623668620375215],"hpluv":[343.43446060875408,285.48249634694713,41.688035181331955],"hsluv":[343.43446060875408,95.9227087501184883,41.688035181331955]},"#bb1188":{"lch":[42.3764815581906475,91.4027083669889606,334.189166164708297],"luv":[42.3764815581906475,82.2840505377722309,-39.7968607294366805],"rgb":[0.733333333333333282,0.0666666666666666657,0.533333333333333326],"xyz":[0.251369604825509796,0.127449982396275252,0.244296532023947383],"hpluv":[334.189166164708297,273.699179760613617,42.3764815581906475],"hsluv":[334.189166164708297,96.2803174994205,42.3764815581906475]},"#bb1199":{"lch":[43.1632358766101092,91.692942572352564,324.693570122963422],"luv":[43.1632358766101092,74.8281105443768269,-52.9938637007650755],"rgb":[0.733333333333333282,0.0666666666666666657,0.6],"xyz":[0.264426568934582495,0.13267276803990441,0.31306320966506529],"hpluv":[324.693570122963422,269.563595898888195,43.1632358766101092],"hsluv":[324.693570122963422,96.6270445785018239,43.1632358766101092]},"#bb11aa":{"lch":[44.0453166933651,94.6050581990027553,315.694091730478135],"luv":[44.0453166933651,67.7013389352867847,-66.0806003544619784],"rgb":[0.733333333333333282,0.0666666666666666657,0.66666666666666663],"xyz":[0.279484261899835051,0.138695845226005504,0.39236705928206389],"hpluv":[315.694091730478135,272.554870225691275,44.0453166933651],"hsluv":[315.694091730478135,96.9544350138240105,44.0453166933651]},"#bb11bb":{"lch":[45.0186979872658242,99.7328976909900717,307.715012949243601],"luv":[45.0186979872658242,61.0100383302365472,-78.8950321933172205],"rgb":[0.733333333333333282,0.0666666666666666657,0.733333333333333282],"xyz":[0.296621854007171726,0.145550882068940263,0.482625044380705637],"hpluv":[307.715012949243601,281.115526817766181,45.0186979872658242],"hsluv":[307.715012949243601,97.2574105430315115,45.0186979872658242]},"#bb11cc":{"lch":[46.0785638011469771,106.527215460047387,300.962972083371881],"luv":[46.0785638011469771,54.8065495444917161,-91.3470840295328372],"rgb":[0.733333333333333282,0.0666666666666666657,0.8],"xyz":[0.315914229450893425,0.153267832246429064,0.584231555050975793],"hpluv":[300.962972083371881,293.360047474541318,46.0785638011469771],"hsluv":[300.962972083371881,97.5336183018287528,46.0785638011469771]},"#bb11dd":{"lch":[47.2195492447565,114.4666153484132,295.402801066287566],"luv":[47.2195492447565,49.1038079522977853,-103.3993330438134],"rgb":[0.733333333333333282,0.0666666666666666657,0.866666666666666696],"xyz":[0.337432555001527,0.161875162466682598,0.69756140295098179],"hpluv":[295.402801066287566,307.607082675331128,47.2195492447565],"hsluv":[295.402801066287566,97.7826899351839813,47.2195492447565]},"#bb11ee":{"lch":[48.4359581768354701,123.136484869400022,290.880608651701721],"luv":[48.4359581768354701,43.8885280811888,-115.049515465553711],"rgb":[0.733333333333333282,0.0666666666666666657,0.933333333333333348],"xyz":[0.361244735267701733,0.171400034573152643,0.822972219019505],"hpluv":[290.880608651701721,322.595409245987128,48.4359581768354701],"hsluv":[290.880608651701721,98.0055710342431325,48.4359581768354701]},"#bb11ff":{"lch":[49.7219510368964,132.239138283310353,287.21247838519713],"luv":[49.7219510368964,39.1316890181496575,-126.316667975763593],"rgb":[0.733333333333333282,0.0666666666666666657,1],"xyz":[0.387415783677533154,0.181868453937085339,0.960806407311286859],"hpluv":[287.21247838519713,337.482436204013879,49.7219510368964],"hsluv":[287.21247838519713,99.9999999999991758,49.7219510368964]},"#992200":{"lch":[33.8105832897308716,95.4307991554818358,16.0266852535062476],"luv":[33.8105832897308716,91.7217107848809263,26.3470149760569932],"rgb":[0.6,0.133333333333333331,0],"xyz":[0.137085784430296231,0.0791755182154848525,0.0080644446856038],"hpluv":[16.0266852535062476,358.158468302090569,33.8105832897308716],"hsluv":[16.0266852535062476,100.000000000002331,33.8105832897308716]},"#992211":{"lch":[33.8952997814050718,93.2922690289088195,14.6972380002076104],"luv":[33.8952997814050718,90.239744522062125,23.669304365681274],"rgb":[0.6,0.133333333333333331,0.0666666666666666657],"xyz":[0.138097449929933364,0.0795801844153397,0.0133925496503594314],"hpluv":[14.6972380002076104,349.257308780581,33.8952997814050718],"hsluv":[14.6972380002076104,92.1483909924374274,33.8952997814050718]},"#992222":{"lch":[34.0515850466810335,89.6330213591727,12.1770506300618084],"luv":[34.0515850466810335,87.6163196410811338,18.9065874902323579],"rgb":[0.6,0.133333333333333331,0.133333333333333331],"xyz":[0.139972808068410365,0.0803303276707305197,0.0232694358463386292],"hpluv":[12.1770506300618084,334.018122077437397,34.0515850466810335],"hsluv":[12.1770506300618084,78.2707991117683,34.0515850466810335]},"#992233":{"lch":[34.3068003204445446,84.4135643942939282,7.88651435003668233],"luv":[34.3068003204445446,83.6151628365757773,11.58250394183076],"rgb":[0.6,0.133333333333333331,0.2],"xyz":[0.143060558800868098,0.0815654279637136326,0.0395315897039497724],"hpluv":[7.88651435003668233,312.227643050581207,34.3068003204445446],"hsluv":[7.88651435003668233,79.3008398259009226,34.3068003204445446]},"#992244":{"lch":[34.6707661525426,78.4682482149262199,1.45557545410962708],"luv":[34.6707661525426,78.4429281517087134,1.99323879782018309],"rgb":[0.6,0.133333333333333331,0.266666666666666663],"xyz":[0.147518554045086986,0.0833486260614012126,0.0630103646568364],"hpluv":[1.45557545410962708,287.190351340663,34.6707661525426],"hsluv":[1.45557545410962708,80.6267396657693212,34.6707661525426]},"#992255":{"lch":[35.149531709850983,73.0418005898087443,352.659616011821072],"luv":[35.149531709850983,72.4431965063170082,-9.33209051330535821],"rgb":[0.6,0.133333333333333331,0.333333333333333315],"xyz":[0.153480968084449,0.0857335916771460455,0.0944124119308103193],"hpluv":[352.659616011821072,263.688538908073838,35.149531709850983],"hsluv":[352.659616011821072,82.1555178352066804,35.149531709850983]},"#992266":{"lch":[35.7459223236079495,69.5043059181480203,341.734006615383123],"luv":[35.7459223236079495,66.0021002604389793,-21.7846574995905513],"rgb":[0.6,0.133333333333333331,0.4],"xyz":[0.161065963857957156,0.0887675899865493639,0.134360056337954281],"hpluv":[341.734006615383123,246.731460763727796,35.7459223236079495],"hsluv":[341.734006615383123,83.7834402291280753,35.7459223236079495]},"#992277":{"lch":[36.4599553630224946,68.9051746310403104,329.662355423262511],"luv":[36.4599553630224946,59.4695672743266783,-34.8036443370302],"rgb":[0.6,0.133333333333333331,0.466666666666666674],"xyz":[0.17037997687815909,0.0924931951946301734,0.18341385824435219],"hpluv":[329.662355423262511,239.814275331209444,36.4599553630224946],"hsluv":[329.662355423262511,85.4170781092293225,36.4599553630224946]},"#992288":{"lch":[37.2892540647929,71.5563678213307242,317.925484235555643],"luv":[37.2892540647929,53.1144286836587369,-47.9496740488411],"rgb":[0.6,0.133333333333333331,0.533333333333333326],"xyz":[0.18152039398010289,0.0969493620354077712,0.242086721647924358],"hpluv":[317.925484235555643,243.502777039008038,37.2892540647929],"hsluv":[317.925484235555643,86.9852623869732,37.2892540647929]},"#992299":{"lch":[38.2294870734457888,77.015786119064046,307.715012949243771],"luv":[38.2294870734457888,47.1132010795059841,-60.9243596238766827],"rgb":[0.6,0.133333333333333331,0.6],"xyz":[0.194577358089175617,0.102172147679036929,0.310853399289042265],"hpluv":[307.715012949243771,255.635172818446421,38.2294870734457888],"hsluv":[307.715012949243771,88.441984096309227,38.2294870734457888]},"#9922aa":{"lch":[39.2748221448681178,84.484081905089468,299.468150353755561],"luv":[39.2748221448681178,41.5610713460956234,-73.5543162833468],"rgb":[0.6,0.133333333333333331,0.66666666666666663],"xyz":[0.209635051054428145,0.108195224865138023,0.390157248906040865],"hpluv":[299.468150353755561,272.960615272696657,39.2748221448681178],"hsluv":[299.468150353755561,89.7633288768494708,39.2748221448681178]},"#9922bb":{"lch":[40.4183688993281436,93.2013493596814726,293.05045530637],"luv":[40.4183688993281436,36.4922039393154094,-85.7601922462677919],"rgb":[0.6,0.133333333333333331,0.733333333333333282],"xyz":[0.22677264316176482,0.115050261708072782,0.480415234004682612],"hpluv":[293.05045530637,292.605673863240838,40.4183688993281436],"hsluv":[293.05045530637,90.9419133161227222,40.4183688993281436]},"#9922cc":{"lch":[41.6525852773545182,102.609691310931794,288.11294916137831],"luv":[41.6525852773545182,31.900454524651952,-97.5249186210648702],"rgb":[0.6,0.133333333333333331,0.8],"xyz":[0.246065018605486546,0.122767211885561583,0.582021744674952712],"hpluv":[288.11294916137831,312.597676326075884,41.6525852773545182],"hsluv":[288.11294916137831,91.9812251551093851,41.6525852773545182]},"#9922dd":{"lch":[42.969628845807982,112.350489371469905,284.303043907998301],"luv":[42.969628845807982,27.7562437333976213,-108.867917201630149],"rgb":[0.6,0.133333333333333331,0.866666666666666696],"xyz":[0.267583344156120062,0.131374542105815117,0.69535159257495871],"hpluv":[284.303043907998301,331.781902020401162,42.969628845807982],"hsluv":[284.303043907998301,92.8910125551507235,42.969628845807982]},"#9922ee":{"lch":[44.361642902098545,122.210344632057584,281.334390799049743],"luv":[44.361642902098545,24.018574136526297,-119.826860225637532],"rgb":[0.6,0.133333333333333331,0.933333333333333348],"xyz":[0.291395524422294827,0.140899414212285162,0.820762408643481933],"hpluv":[281.334390799049743,349.574442537581717,44.361642902098545],"hsluv":[281.334390799049743,93.6839973987951566,44.361642902098545]},"#9922ff":{"lch":[45.8209755847726612,132.069283427413211,278.992348895848238],"luv":[45.8209755847726612,20.6427685166220094,-130.446049127597433],"rgb":[0.6,0.133333333333333331,1],"xyz":[0.317566572832126193,0.151367833576217858,0.958596596935263889],"hpluv":[278.992348895848238,365.74366826955071,45.8209755847726612],"hsluv":[278.992348895848238,99.9999999999993179,45.8209755847726612]},"#bb2200":{"lch":[40.7526421249889452,122.145166616692975,14.6188079362681389],"luv":[40.7526421249889452,118.190884669319914,30.8278528104570881],"rgb":[0.733333333333333282,0.133333333333333331,0],"xyz":[0.21065061901047108,0.117107386045888315,0.0115127963065494235],"hpluv":[14.6188079362681389,380.329350781024857,40.7526421249889452],"hsluv":[14.6188079362681389,100.000000000002217,40.7526421249889452]},"#bb2211":{"lch":[40.8179368215716849,120.355879944515436,13.7706881972771793],"luv":[40.8179368215716849,116.896392622501153,28.6491052053056343],"rgb":[0.733333333333333282,0.133333333333333331,0.0666666666666666657],"xyz":[0.211662284510108212,0.117512052245743168,0.0168409012713050532],"hpluv":[13.7706881972771793,374.158477594779924,40.8179368215716849],"hsluv":[13.7706881972771793,94.6800257514418,40.8179368215716849]},"#bb2222":{"lch":[40.9385803904414161,117.208042434762348,12.1770506300618102],"luv":[40.9385803904414161,114.570915436608942,24.7230772236478238],"rgb":[0.733333333333333282,0.133333333333333331,0.133333333333333331],"xyz":[0.213537642648585213,0.118262195501133982,0.0267177874672842527],"hpluv":[12.1770506300618102,363.298797482753,40.9385803904414161],"hsluv":[12.1770506300618102,85.1321689328196101,40.9385803904414161]},"#bb2233":{"lch":[41.136111673530813,112.480172956632245,9.49666640172232235],"luv":[41.136111673530813,110.938654987290519,18.5581286223806821],"rgb":[0.733333333333333282,0.133333333333333331,0.2],"xyz":[0.216625393381042974,0.119497295794117095,0.042979941324895396],"hpluv":[9.49666640172232235,346.970109935861444,41.136111673530813],"hsluv":[9.49666640172232235,85.6217357677037398,41.136111673530813]},"#bb2244":{"lch":[41.4189140922405201,106.57909239531196,5.51995404221549],"luv":[41.4189140922405201,106.084859394728952,10.2520994439692306],"rgb":[0.733333333333333282,0.133333333333333331,0.266666666666666663],"xyz":[0.221083388625261834,0.121280493891804675,0.0664587162777820345],"hpluv":[5.51995404221549,326.522141050690152,41.4189140922405201],"hsluv":[5.51995404221549,86.2742612371986297,41.4189140922405201]},"#bb2255":{"lch":[41.7928521194743823,100.22104851316422,0.0579838467833058424],"luv":[41.7928521194743823,100.220997191859041,0.101424589714973798],"rgb":[0.733333333333333282,0.133333333333333331,0.333333333333333315],"xyz":[0.227045802664623819,0.123665459507549508,0.097860763551755936],"hpluv":[0.0579838467833058424,304.296010337655673,41.7928521194743823],"hsluv":[0.0579838467833058424,87.0597094656736772,41.7928521194743823]},"#bb2266":{"lch":[42.2616671880265216,94.3342448936905,353.011685321171171],"luv":[42.2616671880265216,93.6334344607092106,-11.4773564358193614],"rgb":[0.733333333333333282,0.133333333333333331,0.4],"xyz":[0.234630798438132,0.126699457816952826,0.137808407958899898],"hpluv":[353.011685321171171,283.244886623536,42.2616671880265216],"hsluv":[353.011685321171171,87.937804292381287,42.2616671880265216]},"#bb2277":{"lch":[42.8272221099346666,89.9002723684957,344.496654306277264],"luv":[42.8272221099346666,86.6292371691086771,-24.0298614109202155],"rgb":[0.733333333333333282,0.133333333333333331,0.466666666666666674],"xyz":[0.24394481145833391,0.13042506302503365,0.186862209865297807],"hpluv":[344.496654306277264,266.367005402095344,42.8272221099346666],"hsluv":[344.496654306277264,88.8655868844719805,42.8272221099346666]},"#bb2288":{"lch":[43.4897067779173554,87.7363054907772693,334.961981200390596],"luv":[43.4897067779173554,79.4914755013727898,-37.1317199141871441],"rgb":[0.733333333333333282,0.133333333333333331,0.533333333333333326],"xyz":[0.255085228560277766,0.13488122986581122,0.245535073268869974],"hpluv":[334.961981200390596,255.995416581420926,43.4897067779173554],"hsluv":[334.961981200390596,89.8036443318747786,43.4897067779173554]},"#bb2299":{"lch":[44.2478449340908639,88.2751432264235092,325.159464012784042],"luv":[44.2478449340908639,72.4514028672968635,-50.4310929309115],"rgb":[0.733333333333333282,0.133333333333333331,0.6],"xyz":[0.268142192669350465,0.140104015509440405,0.314301750909987909],"hpluv":[325.159464012784042,253.154489640486645,44.2478449340908639],"hsluv":[325.159464012784042,90.7199745934293844,44.2478449340908639]},"#bb22aa":{"lch":[45.0991127685299062,91.4705260319517919,315.891475419905078],"luv":[45.0991127685299062,65.6779186933914758,-63.6652819727222195],"rgb":[0.733333333333333282,0.133333333333333331,0.66666666666666663],"xyz":[0.283199885634603,0.146127092695541499,0.39360560052698651],"hpluv":[315.891475419905078,257.366789521381691,45.0991127685299062],"hsluv":[315.891475419905078,91.5914304646717,45.0991127685299062]},"#bb22bb":{"lch":[46.0399667792549678,96.9048462314552,307.715012949243658],"luv":[46.0399667792549678,59.2800221375787615,-76.6578645574617497],"rgb":[0.733333333333333282,0.133333333333333331,0.733333333333333282],"xyz":[0.30033747774193964,0.152982129538476258,0.483863585625628256],"hpluv":[307.715012949243658,267.0851991180906,46.0399667792549678],"hsluv":[307.715012949243658,92.4033444706684,46.0399667792549678]},"#bb22cc":{"lch":[47.066072507765945,104.009692587178989,300.837938637053412],"luv":[47.066072507765945,53.3165662395356,-89.3048706202785496],"rgb":[0.733333333333333282,0.133333333333333331,0.8],"xyz":[0.319629853185661394,0.16069907971596506,0.585470096295898412],"hpluv":[300.837938637053412,280.417543020287,47.066072507765945],"hsluv":[300.837938637053412,93.1481326309998,47.066072507765945]},"#bb22dd":{"lch":[48.1725242595717589,112.247547842848149,295.20893392435471],"luv":[48.1725242595717589,47.808517424121824,-101.557164486017044],"rgb":[0.733333333333333282,0.133333333333333331,0.866666666666666696],"xyz":[0.341148178736294938,0.169306409936218594,0.69879994419590441],"hpluv":[295.20893392435471,295.676483579310798,48.1725242595717589],"hsluv":[295.20893392435471,93.8235497257627316,48.1725242595717589]},"#bb22ee":{"lch":[49.3540469689012724,121.194036197623419,290.655339197387],"luv":[49.3540469689012724,42.7506603945962169,-113.403595382583688],"rgb":[0.733333333333333282,0.133333333333333331,0.933333333333333348],"xyz":[0.364960359002469703,0.178831282042688611,0.824210760264427633],"hpluv":[290.655339197387,311.600255956968681,49.3540469689012724],"hsluv":[290.655339197387,94.4310255689598,49.3540469689012724]},"#bb22ff":{"lch":[50.6051737457033397,130.547024148317973,286.97844412333734],"luv":[50.6051737457033397,38.1212848334983647,-124.85709093449519],"rgb":[0.733333333333333282,0.133333333333333331,1],"xyz":[0.391131407412301069,0.189299701406621335,0.962044948556209478],"hpluv":[286.97844412333734,327.349274436557835,50.6051737457033397],"hsluv":[286.97844412333734,99.9999999999991616,50.6051737457033397]},"#993300":{"lch":[36.2545465004255476,86.9834057059747749,20.3835344027483316],"luv":[36.2545465004255476,81.5366895093473,30.2965531383769893],"rgb":[0.6,0.2,0],"xyz":[0.14320350651930705,0.0914109623935066423,0.0101036853819406816],"hpluv":[20.3835344027483316,304.448092478673459,36.2545465004255476],"hsluv":[20.3835344027483316,100.00000000000226,36.2545465004255476]},"#993311":{"lch":[36.3315413581227133,85.0112276295678839,19.0571063974297203],"luv":[36.3315413581227133,80.3520694714869279,27.7570487396543],"rgb":[0.6,0.2,0.0666666666666666657],"xyz":[0.144215172018944182,0.0918156285933615,0.0154317903466963131],"hpluv":[19.0571063974297203,296.914762557758195,36.3315413581227133],"hsluv":[19.0571063974297203,93.1288353931581,36.3315413581227133]},"#993322":{"lch":[36.4736730302835852,81.6150828115545863,16.5278497598068661],"luv":[36.4736730302835852,78.2428757472059573,23.217970134019108],"rgb":[0.6,0.2,0.133333333333333331],"xyz":[0.146090530157421183,0.0925657718487523096,0.0253086765426755109],"hpluv":[16.5278497598068661,283.942401799749632,36.4736730302835852],"hsluv":[16.5278497598068661,80.9121244795507124,36.4736730302835852]},"#993333":{"lch":[36.7060271438600836,76.7221326388105638,12.1770506300618369],"luv":[36.7060271438600836,74.9959199734098121,16.1832513417159589],"rgb":[0.6,0.2,0.2],"xyz":[0.149178280889878945,0.0938008721417354224,0.0415708304002866541],"hpluv":[12.1770506300618369,265.229979343802,36.7060271438600836],"hsluv":[12.1770506300618369,62.1516051360361459,36.7060271438600836]},"#993344":{"lch":[37.0379214664673668,71.077614272095758,5.55118145677439934],"luv":[37.0379214664673668,70.7442733159460744,6.87568495580995354],"rgb":[0.6,0.2,0.266666666666666663],"xyz":[0.153636276134097804,0.095584070239423,0.0650496053531732926],"hpluv":[5.55118145677439934,243.514912685509444,37.0379214664673668],"hsluv":[5.55118145677439934,64.3097526797848644,37.0379214664673668]},"#993355":{"lch":[37.4754277574064858,65.8722356173568073,356.298722520573506],"luv":[37.4754277574064858,65.7348379379961614,-4.25235305377523254],"rgb":[0.6,0.2,0.333333333333333315],"xyz":[0.15959869017345979,0.0979690358551678353,0.0964516526271472],"hpluv":[356.298722520573506,223.046355214908289,37.4754277574064858],"hsluv":[356.298722520573506,66.8387430740197885,37.4754277574064858]},"#993366":{"lch":[38.0218512407041942,62.5198646461221941,344.559385799457459],"luv":[38.0218512407041942,60.2633303059628602,-16.6452544529622166],"rgb":[0.6,0.2,0.4],"xyz":[0.167183685946967975,0.101003034164571154,0.136399297034291156],"hpluv":[344.559385799457459,208.652742449272864,38.0218512407041942],"hsluv":[344.559385799457459,69.5808124884664778,38.0218512407041942]},"#993377":{"lch":[38.6780657603296234,62.1964572713534949,331.414072321130675],"luv":[38.6780657603296234,54.6147414507713478,-29.7595247504508862],"rgb":[0.6,0.2,0.466666666666666674],"xyz":[0.17649769896716988,0.104728639372651963,0.185453098940689065],"hpluv":[331.414072321130675,204.051704505442274,38.6780657603296234],"hsluv":[331.414072321130675,72.3848063102254571,38.6780657603296234]},"#993388":{"lch":[39.4428302118465908,65.3028355433768155,318.6521895607018],"luv":[39.4428302118465908,49.0236963230770257,-43.1409031990296086],"rgb":[0.6,0.2,0.533333333333333326],"xyz":[0.187638116069113736,0.109184806213429561,0.244125962344261233],"hpluv":[318.6521895607018,210.08899125401652,39.4428302118465908],"hsluv":[318.6521895607018,75.1271370389489306,39.4428302118465908]},"#993399":{"lch":[40.3131218316236897,71.3679900272828149,307.715012949243942],"luv":[40.3131218316236897,43.6582502656979656,-56.4565955781255226],"rgb":[0.6,0.2,0.6],"xyz":[0.200695080178186436,0.114407591857058719,0.31289263998537914],"hpluv":[307.715012949243942,224.64479534599792,40.3131218316236897],"hsluv":[307.715012949243942,77.7202573427554313,40.3131218316236897]},"#9933aa":{"lch":[41.2844862133256925,79.5040030135899372,299.063916375798101],"luv":[41.2844862133256925,38.6218522191785354,-69.4927264276258825],"rgb":[0.6,0.2,0.66666666666666663],"xyz":[0.215752773143439,0.120430669043159827,0.39219648960237774],"hpluv":[299.063916375798101,244.366370382710016,41.2844862133256925],"hsluv":[299.063916375798101,80.1114582016200814,41.2844862133256925]},"#9933bb":{"lch":[42.3513893410192637,88.8787943355742,292.467029257400327],"luv":[42.3513893410192637,33.9651845170614,-82.1328577566084],"rgb":[0.6,0.2,0.733333333333333282],"xyz":[0.232890365250775611,0.127285705886094586,0.482454474701019487],"hpluv":[292.467029257400327,266.299173904300574,42.3513893410192637],"hsluv":[292.467029257400327,82.2764131802073848,42.3513893410192637]},"#9933cc":{"lch":[43.5075532005759911,98.8962106119506785,287.477062230467],"luv":[43.5075532005759911,29.700902070050418,-94.3308904316533],"rgb":[0.6,0.2,0.8],"xyz":[0.252182740694497365,0.135002656063583387,0.584060985371289698],"hpluv":[287.477062230467,288.439223502221068,43.5075532005759911],"hsluv":[287.477062230467,84.2111914011780129,43.5075532005759911]},"#9933dd":{"lch":[44.7462588110156716,109.182503226938962,283.677602435208257],"luv":[44.7462588110156716,25.8171133061496079,-106.086265234656878],"rgb":[0.6,0.2,0.866666666666666696],"xyz":[0.273701066245130908,0.143609986283836921,0.697390833271295696],"hpluv":[283.677602435208257,309.624731809924413,44.7462588110156716],"hsluv":[283.677602435208257,85.9249592908513478,44.7462588110156716]},"#9933ee":{"lch":[46.0606056636097208,119.52114969179982,280.747238708189116],"luv":[46.0606056636097208,22.2879081330132465,-117.42467532296601],"rgb":[0.6,0.2,0.933333333333333348],"xyz":[0.297513246511305618,0.153134858390306938,0.822801649339818919],"hpluv":[280.747238708189116,329.271729063034456,46.0606056636097208],"hsluv":[280.747238708189116,90.0471632149093324,46.0606056636097208]},"#9933ff":{"lch":[47.4437223771408512,129.794782591236896,278.453521985212944],"luv":[47.4437223771408512,19.0807517890999456,-128.38461940228359],"rgb":[0.6,0.2,1],"xyz":[0.323684294921137039,0.163603277754239662,0.960635837631600764],"hpluv":[278.453521985212944,347.150508646733101,47.4437223771408512],"hsluv":[278.453521985212944,99.999999999999261,47.4437223771408512]},"#bb3300":{"lch":[42.6640590509798585,114.303280915030754,17.3320761189885637],"luv":[42.6640590509798585,109.113245500432,34.0520143942012652],"rgb":[0.733333333333333282,0.2,0],"xyz":[0.216768341099481898,0.129342830223910105,0.0135520370028863052],"hpluv":[17.3320761189885637,339.966286272656077,42.6640590509798585],"hsluv":[17.3320761189885637,100.000000000002302,42.6640590509798585]},"#bb3311":{"lch":[42.7251747888925806,112.6335820513806,16.4819551739351198],"luv":[42.7251747888925806,108.005370801027979,31.9556518296457135],"rgb":[0.733333333333333282,0.2,0.0666666666666666657],"xyz":[0.21778000659911903,0.129747496423764958,0.0188801419676419349],"hpluv":[16.4819551739351198,334.52099084556113,42.7251747888925806],"hsluv":[16.4819551739351198,95.1490253673008368,42.7251747888925806]},"#bb3322":{"lch":[42.8381318005571785,109.688338781622747,14.8808580240480524],"luv":[42.8381318005571785,106.009603708482885,28.1690536980292627],"rgb":[0.733333333333333282,0.2,0.133333333333333331],"xyz":[0.219655364737596032,0.130497639679155786,0.0287570281636211345],"hpluv":[14.8808580240480524,324.914627119009424,42.8381318005571785],"hsluv":[14.8808580240480524,86.4181297836030211,42.8381318005571785]},"#bb3333":{"lch":[43.023174549414108,105.246046554892928,12.1770506300618351],"luv":[43.023174549414108,102.878058957341324,22.1998941575065203],"rgb":[0.733333333333333282,0.2,0.2],"xyz":[0.222743115470053765,0.131732739972138885,0.0450191820212322777],"hpluv":[12.1770506300618351,310.414975564112126,43.023174549414108],"hsluv":[12.1770506300618351,72.7398502888125762,43.023174549414108]},"#bb3344":{"lch":[43.2883038991094082,99.6704509181560496,8.14070356751482116],"luv":[43.2883038991094082,98.6661026897846085,14.1137863891102171],"rgb":[0.733333333333333282,0.2,0.266666666666666663],"xyz":[0.227201110714272653,0.133515938069826479,0.0684979569741189231],"hpluv":[8.14070356751482116,292.169703441320848,43.2883038991094082],"hsluv":[8.14070356751482116,73.8775464962187556,43.2883038991094082]},"#bb3355":{"lch":[43.6392404977582515,93.6285749325397632,2.54995992142812078],"luv":[43.6392404977582515,93.5358645919991289,4.16558266337591299],"rgb":[0.733333333333333282,0.2,0.333333333333333315],"xyz":[0.233163524753634666,0.135900903685571312,0.0999000042480928246],"hpluv":[2.54995992142812078,272.251672456619758,43.6392404977582515],"hsluv":[2.54995992142812078,75.2585830443305781,43.6392404977582515]},"#bb3366":{"lch":[44.0797950159491521,88.0182600678664784,355.263953050872033],"luv":[44.0797950159491521,87.7177339493983794,-7.26727295188107547],"rgb":[0.733333333333333282,0.2,0.4],"xyz":[0.240748520527142823,0.138934901994974602,0.1398476486552368],"hpluv":[355.263953050872033,253.380121177946961,44.0797950159491521],"hsluv":[355.263953050872033,76.8177075569623753,44.0797950159491521]},"#bb3377":{"lch":[44.6120878205181057,83.829516929164356,346.367941021132197],"luv":[44.6120878205181057,81.4679789337245239,-19.757437005625782],"rgb":[0.733333333333333282,0.2,0.466666666666666674],"xyz":[0.250062533547344756,0.142660507203055426,0.188901450561634709],"hpluv":[346.367941021132197,238.442541154787421,44.6120878205181057],"hsluv":[346.367941021132197,78.4827471040858597,44.6120878205181057]},"#bb3388":{"lch":[45.2367248685103078,81.924239827592,336.329010851829594],"luv":[45.2367248685103078,75.03162551729379,-32.8912791414593499],"rgb":[0.733333333333333282,0.2,0.533333333333333326],"xyz":[0.261202950649288557,0.147116674043833023,0.247574313965206849],"hpluv":[336.329010851829594,229.805591899252,45.2367248685103078],"hsluv":[336.329010851829594,80.18501002925629,45.2367248685103078]},"#bb3399":{"lch":[45.9529692994627226,82.7837935230449347,325.982908927834501],"luv":[45.9529692994627226,68.6170634488305495,-46.3125800806357404],"rgb":[0.733333333333333282,0.2,0.6],"xyz":[0.274259914758361312,0.152339459687462181,0.316340991606324784],"hpluv":[325.982908927834501,228.597286714525296,45.9529692994627226],"hsluv":[325.982908927834501,81.8663863744617402,45.9529692994627226]},"#bb33aa":{"lch":[46.7589216358443664,86.3774619636168524,316.238480868355282],"luv":[46.7589216358443664,62.3839556422166126,-59.7436859735489136],"rgb":[0.733333333333333282,0.2,0.66666666666666663],"xyz":[0.289317607723613812,0.158362536873563275,0.395644841223323385],"hpluv":[316.238480868355282,234.409537876970433,46.7589216358443664],"hsluv":[316.238480868355282,83.4827024616804181,46.7589216358443664]},"#bb33bb":{"lch":[47.6517090930198108,92.2657594707273461,307.715012949243828],"luv":[47.6517090930198108,56.4421334605023191,-72.9880534138084585],"rgb":[0.733333333333333282,0.2,0.733333333333333282],"xyz":[0.306455199830950487,0.165217573716498034,0.485902826321965131],"hpluv":[307.715012949243828,245.697877980233102,47.6517090930198108],"hsluv":[307.715012949243828,85.0039827354176,47.6517090930198108]},"#bb33cc":{"lch":[48.6276786348411179,99.8443429984049402,300.621719827817344],"luv":[48.6276786348411179,50.8574804969275078,-85.9209491700817551],"rgb":[0.733333333333333282,0.2,0.8],"xyz":[0.325747575274672241,0.172934523893986836,0.587509336992235287],"hpluv":[300.621719827817344,260.542905615006589,48.6276786348411179],"hsluv":[300.621719827817344,86.4127454729900535,48.6276786348411179]},"#bb33dd":{"lch":[49.6825862492002273,108.545865187730342,294.87630802524211],"luv":[49.6825862492002273,45.6609809337446322,-98.4747666639588459],"rgb":[0.733333333333333282,0.2,0.866666666666666696],"xyz":[0.347265900825305729,0.18154185411424037,0.700839184892241285],"hpluv":[294.87630802524211,277.235228758227834,49.6825862492002273],"hsluv":[294.87630802524211,87.70141038259,49.6825862492002273]},"#bb33ee":{"lch":[50.8117750205940695,117.927813495206621,290.271422146528892],"luv":[50.8117750205940695,40.8581913283156268,-110.623584271795281],"rgb":[0.733333333333333282,0.2,0.933333333333333348],"xyz":[0.371078081091480494,0.191066726220710414,0.826250000960764508],"hpluv":[290.271422146528892,294.504005794210514,50.8117750205940695],"hsluv":[290.271422146528892,88.8695912604113687,50.8117750205940695]},"#bb33ff":{"lch":[52.0103359867018611,127.679308171663493,286.581788346289272],"luv":[52.0103359867018611,36.4375993761859931,-122.36955130625914],"rgb":[0.733333333333333282,0.2,1],"xyz":[0.39724912950131186,0.201535145584643111,0.964084189252546353],"hpluv":[286.581788346289272,311.508705442628695,52.0103359867018611],"hsluv":[286.581788346289272,99.9999999999991616,52.0103359867018611]},"#994400":{"lch":[39.4244247356725168,77.6708634712958315,27.0445710144404678],"luv":[39.4244247356725168,69.1777945884316097,35.3156589667733627],"rgb":[0.6,0.266666666666666663,0],"xyz":[0.152036077002273062,0.109076103359438958,0.0130478755429292749],"hpluv":[27.0445710144404678,249.995444431237956,39.4244247356725168],"hsluv":[27.0445710144404678,100.000000000002302,39.4244247356725168]},"#994411":{"lch":[39.4928806474909351,75.8240556098730849,25.7566327743493559],"luv":[39.4928806474909351,68.2907795083206395,32.9493071744317447],"rgb":[0.6,0.266666666666666663,0.0666666666666666657],"xyz":[0.153047742501910194,0.109480769559293811,0.0183759805076849081],"hpluv":[25.7566327743493559,243.628181561178451,39.4928806474909351],"hsluv":[25.7566327743493559,94.1783894720599,39.4928806474909351]},"#994422":{"lch":[39.6193348047394807,72.6128884539244268,23.2788782073808527],"luv":[39.6193348047394807,66.7016282141236161,28.6971141964989123],"rgb":[0.6,0.266666666666666663,0.133333333333333331],"xyz":[0.154923100640387196,0.110230912814684626,0.0282528667036641],"hpluv":[23.2788782073808527,232.565805292122405,39.6193348047394807],"hsluv":[23.2788782073808527,83.7619004180350402,39.6193348047394807]},"#994433":{"lch":[39.8262957210095,67.9099451463512764,18.9473520481579776],"luv":[39.8262957210095,64.2304033034813671,22.0503047882012062],"rgb":[0.6,0.266666666666666663,0.2],"xyz":[0.158010851372844929,0.111466013107667739,0.0445150205612752509],"hpluv":[18.9473520481579776,216.372863201479333,39.8262957210095],"hsluv":[18.9473520481579776,67.5996848307310358,39.8262957210095]},"#994444":{"lch":[40.1224193460439267,62.3513134554678956,12.1770506300619097],"luv":[40.1224193460439267,60.9484376060972082,13.1519672672071142],"rgb":[0.6,0.266666666666666663,0.266666666666666663],"xyz":[0.162468846617063817,0.113249211205355318,0.0679937955141618894],"hpluv":[12.1770506300619097,197.195872414250289,40.1224193460439267],"hsluv":[12.1770506300619097,46.2091051210312429,40.1224193460439267]},"#994455":{"lch":[40.5136546800823041,57.0624194645929776,2.37288379618767387],"luv":[40.5136546800823041,57.0134905267935324,2.36254377830001205],"rgb":[0.6,0.266666666666666663,0.333333333333333315],"xyz":[0.16843126065642583,0.115634176821100151,0.0993958427881357909],"hpluv":[2.37288379618767387,178.726144329621945,40.5136546800823041],"hsluv":[2.37288379618767387,49.5312591923758063,40.5136546800823041]},"#994466":{"lch":[41.0036603670202382,53.5416261730848646,349.413399835018254],"luv":[41.0036603670202382,52.6302589877717111,-9.83674601372026558],"rgb":[0.6,0.266666666666666663,0.4],"xyz":[0.176016256429934,0.11866817513050347,0.139343487195279753],"hpluv":[349.413399835018254,165.694562401887765,41.0036603670202382],"hsluv":[349.413399835018254,53.2076295784804643,41.0036603670202382]},"#994477":{"lch":[41.594070304433572,53.2098960104776424,334.460192523955527],"luv":[41.594070304433572,48.0105420863306946,-22.9408125841809181],"rgb":[0.6,0.266666666666666663,0.466666666666666674],"xyz":[0.18533026945013592,0.122393780338584279,0.188397289101677662],"hpluv":[334.460192523955527,162.330570569563577,41.594070304433572],"hsluv":[334.460192523955527,57.0495308436177666,41.594070304433572]},"#994488":{"lch":[42.2847251453823887,56.6612659292255287,319.901700116735],"luv":[42.2847251453823887,43.3424978513170416,-36.4955742071691631],"rgb":[0.6,0.266666666666666663,0.533333333333333326],"xyz":[0.196470686552079721,0.126849947179361877,0.247070152505249829],"hpluv":[319.901700116735,170.036472949794188,42.2847251453823887],"hsluv":[319.901700116735,60.8903164523640683,42.2847251453823887]},"#994499":{"lch":[43.0739091348830314,63.3829857303906,307.715012949244226],"luv":[43.0739091348830314,38.7735489334465555,-50.1399519665159232],"rgb":[0.6,0.266666666666666663,0.6],"xyz":[0.209527650661152448,0.132072732822991035,0.315836830146367764],"hpluv":[307.715012949244226,186.722963823125951,43.0739091348830314],"hsluv":[307.715012949244226,64.600458594127474,43.0739091348830314]},"#9944aa":{"lch":[43.9586008361636686,72.3252063731545,298.405725418599673],"luv":[43.9586008361636686,34.4059763632637896,-63.6173267861038099],"rgb":[0.6,0.266666666666666663,0.66666666666666663],"xyz":[0.224585343626404976,0.138095810009092129,0.395140679763366365],"hpluv":[298.405725418599673,208.77819920337123,43.9586008361636686],"hsluv":[298.405725418599673,68.0913106207235,43.9586008361636686]},"#9944bb":{"lch":[44.9347323991438827,82.5300732915554676,291.540124858279853],"luv":[44.9347323991438827,30.3011407548278626,-76.7662286846607316],"rgb":[0.6,0.266666666666666663,0.733333333333333282],"xyz":[0.241722935733741651,0.144950846852026888,0.485398664862008111],"hpluv":[291.540124858279853,233.060888419388505,44.9347323991438827],"hsluv":[291.540124858279853,71.3111773355981882,44.9347323991438827]},"#9944cc":{"lch":[45.9974464532923903,93.3382836250318206,286.486143775676851],"luv":[45.9974464532923903,26.4878609436744341,-89.5009967134190561],"rgb":[0.6,0.266666666666666663,0.8],"xyz":[0.261015311177463349,0.152667797029515689,0.587005175532278267],"hpluv":[286.486143775676851,257.492992496327645,45.9974464532923903],"hsluv":[286.486143775676851,74.2376853125076792,45.9974464532923903]},"#9944dd":{"lch":[47.1413389271288139,104.350309752399141,282.717215233582351],"luv":[47.1413389271288139,22.9716047676972792,-101.790434323753402],"rgb":[0.6,0.266666666666666663,0.866666666666666696],"xyz":[0.282533636728096949,0.161275127249769223,0.700335023432284265],"hpluv":[282.717215233582351,280.886686990529654,47.1413389271288139],"hsluv":[282.717215233582351,78.691216526215527,47.1413389271288139]},"#9944ee":{"lch":[48.3606780479664593,115.341161543855208,279.855683836472622],"luv":[48.3606780479664593,19.7426125181249787,-113.638958096441911],"rgb":[0.6,0.266666666666666663,0.933333333333333348],"xyz":[0.306345816994271658,0.170799999356239268,0.825745839500807488],"hpluv":[279.855683836472622,302.643440789384783,48.3606780479664593],"hsluv":[279.855683836472622,89.2619995530309325,48.3606780479664593]},"#9944ff":{"lch":[49.6495929972458185,126.19285787586071,277.642335930208503],"luv":[49.6495929972458185,16.7822322716138856,-125.071955525044785],"rgb":[0.6,0.266666666666666663,1],"xyz":[0.332516865404103079,0.181268418720171964,0.963580027792589333],"hpluv":[277.642335930208503,322.521305960549284,49.6495929972458185],"hsluv":[277.642335930208503,99.9999999999991616,49.6495929972458185]},"#bb4400":{"lch":[45.2216387767487547,104.837609625168909,21.4216552556228201],"luv":[45.2216387767487547,97.5952013308911,38.2896992558330354],"rgb":[0.733333333333333282,0.266666666666666663,0],"xyz":[0.225600911582447911,0.147007971189842435,0.0164962271638749],"hpluv":[21.4216552556228201,294.177965476355098,45.2216387767487547],"hsluv":[21.4216552556228201,100.000000000002373,45.2216387767487547]},"#bb4411":{"lch":[45.2777618800582076,103.283777149094803,20.576768268141489],"luv":[45.2777618800582076,96.6944911384892123,36.3003306548646947],"rgb":[0.733333333333333282,0.266666666666666663,0.0666666666666666657],"xyz":[0.226612577082085043,0.147412637389697287,0.0218243321286305317],"hpluv":[20.576768268141489,289.458620037032802,45.2777618800582076],"hsluv":[20.576768268141489,95.6967439910137614,45.2777618800582076]},"#bb4422":{"lch":[45.3815280810129238,100.53232916519147,18.9799525675269258],"luv":[45.3815280810129238,95.0666309197234,32.6968636561903381],"rgb":[0.733333333333333282,0.266666666666666663,0.133333333333333331],"xyz":[0.228487935220562044,0.148162780645088116,0.0317012183246097243],"hpluv":[18.9799525675269258,281.103307599105051,45.3815280810129238],"hsluv":[18.9799525675269258,87.925891065222288,45.3815280810129238]},"#bb4433":{"lch":[45.5516172521931111,96.3559005693685862,16.2666991179303722],"luv":[45.5516172521931111,92.4986058403436147,26.9901369416087924],"rgb":[0.733333333333333282,0.266666666666666663,0.2],"xyz":[0.231575685953019805,0.149397880938071215,0.0479633721822208675],"hpluv":[16.2666991179303722,268.419362556396,45.5516172521931111],"hsluv":[16.2666991179303722,75.6846484949249572,45.5516172521931111]},"#bb4444":{"lch":[45.7955406359936816,91.066890863790789,12.177050630061844],"luv":[45.7955406359936816,89.0179277419259734,19.2090382927107299],"rgb":[0.733333333333333282,0.266666666666666663,0.266666666666666663],"xyz":[0.236033681197238665,0.151181079035758809,0.0714421471351075],"hpluv":[12.177050630061844,252.334507458167678,45.7955406359936816],"hsluv":[12.177050630061844,59.1297963696904461,45.7955406359936816]},"#bb4455":{"lch":[46.1187996938414813,85.2741675307746,6.43569277192082279],"luv":[46.1187996938414813,84.7367941199702273,9.55820957796975],"rgb":[0.733333333333333282,0.266666666666666663,0.333333333333333315],"xyz":[0.24199609523660065,0.153566044651503641,0.102844194409081421],"hpluv":[6.43569277192082279,234.627449284537505,46.1187996938414813],"hsluv":[6.43569277192082279,61.0765315010866274,46.1187996938414813]},"#bb4466":{"lch":[46.5252276318027427,79.8426568240615637,358.82639812637143],"luv":[46.5252276318027427,79.8259079304732779,-1.63532009113408461],"rgb":[0.733333333333333282,0.266666666666666663,0.4],"xyz":[0.249581091010108835,0.156600042960906932,0.142791838816225369],"hpluv":[358.82639812637143,217.763856882437722,46.5252276318027427],"hsluv":[358.82639812637143,63.3003300169989842,46.5252276318027427]},"#bb4477":{"lch":[47.0171837667790697,75.785442181693,349.367581794189107],"luv":[47.0171837667790697,74.484290362097866,-13.9829802234512588],"rgb":[0.733333333333333282,0.266666666666666663,0.466666666666666674],"xyz":[0.258895104030310741,0.160325648168987756,0.191845640722623278],"hpluv":[349.367581794189107,204.535408840651428,47.0171837667790697],"hsluv":[349.367581794189107,65.7062458495858266,47.0171837667790697]},"#bb4488":{"lch":[47.5956997014540235,74.0450948660102,338.538246495536782],"luv":[47.5956997014540235,68.9109568360576219,-27.0916241974425098],"rgb":[0.733333333333333282,0.266666666666666663,0.533333333333333326],"xyz":[0.270035521132254597,0.164781815009765353,0.250518504126195474],"hpluv":[338.538246495536782,197.409434601059019,47.5956997014540235],"hsluv":[338.538246495536782,68.1997676790361,47.5956997014540235]},"#bb4499":{"lch":[48.2606154557730633,75.1911240047971,327.313188255720945],"luv":[48.2606154557730633,63.2834900243205496,-40.6067115074158309],"rgb":[0.733333333333333282,0.266666666666666663,0.6],"xyz":[0.283092485241327296,0.170004600653394511,0.319285181767313353],"hpluv":[327.313188255720945,197.702903480158483,48.2606154557730633],"hsluv":[327.313188255720945,70.6969578310621074,48.2606154557730633]},"#bb44aa":{"lch":[49.0107199856960278,79.2228796099941093,316.794363507436401],"luv":[49.0107199856960278,57.7456584584256944,-54.2374739723603838],"rgb":[0.733333333333333282,0.266666666666666663,0.66666666666666663],"xyz":[0.298150178206579852,0.176027677839495605,0.398589031384311954],"hpluv":[316.794363507436401,205.115683333718124,49.0107199856960278],"hsluv":[316.794363507436401,73.1302855273877128,49.0107199856960278]},"#bb44bb":{"lch":[49.8438993628519427,85.6636869410227746,307.715012949243942],"luv":[49.8438993628519427,52.4034189799077268,-67.7653963284049752],"rgb":[0.733333333333333282,0.266666666666666663,0.733333333333333282],"xyz":[0.315287770313916471,0.182882714682430364,0.4888470164829537],"hpluv":[307.715012949243942,218.084137693810391,49.8438993628519427],"hsluv":[307.715012949243942,75.450469608388417,49.8438993628519427]},"#bb44cc":{"lch":[50.757290285110841,93.8478600934752905,300.284565440631638],"luv":[50.757290285110841,47.3270085455435918,-81.0405769121528152],"rgb":[0.733333333333333282,0.266666666666666663,0.8],"xyz":[0.334580145757638225,0.190599664859919166,0.590453527153223856],"hpluv":[300.284565440631638,234.620130462749955,50.757290285110841],"hsluv":[300.284565440631638,77.6254237907312898,50.757290285110841]},"#bb44dd":{"lch":[51.7474340276366291,103.158361374368454,294.364201848969515],"luv":[51.7474340276366291,42.5564715489483518,-93.971241615445],"rgb":[0.733333333333333282,0.266666666666666663,0.866666666666666696],"xyz":[0.356098471308271769,0.1992069950801727,0.703783375053229854],"hpluv":[294.364201848969515,252.961799427415912,51.7474340276366291],"hsluv":[294.364201848969515,79.6375689955678467,51.7474340276366291]},"#bb44ee":{"lch":[52.8104252671910217,113.122924260907325,289.686672533024307],"luv":[52.8104252671910217,38.1084271425454304,-106.510768347901248],"rgb":[0.733333333333333282,0.266666666666666663,0.933333333333333348],"xyz":[0.379910651574446478,0.208731867186642744,0.829194191121753077],"hpluv":[289.686672533024307,271.813038246621943,52.8104252671910217],"hsluv":[289.686672533024307,87.7449679178180872,52.8104252671910217]},"#bb44ff":{"lch":[53.942050711908152,123.415863319803691,285.982908620336502],"luv":[53.942050711908152,33.9826318144238115,-118.645084406973112],"rgb":[0.733333333333333282,0.266666666666666663,1],"xyz":[0.4060816999842779,0.219200286550575441,0.967028379413534922],"hpluv":[285.982908620336502,290.323946696930363,53.942050711908152],"hsluv":[285.982908620336502,99.9999999999990621,53.942050711908152]},"#995500":{"lch":[43.167672396478018,69.2675138179999,36.3951762413548678],"luv":[43.167672396478018,55.7564526526882105,41.099956911356287],"rgb":[0.6,0.333333333333333315,0],"xyz":[0.163849333716619028,0.132702616788131222,0.0169856277810444857],"hpluv":[36.3951762413548678,203.615246511519132,43.167672396478018],"hsluv":[36.3951762413548678,100.000000000002288,43.167672396478018]},"#995511":{"lch":[43.2277537555276865,67.4837391190291385,35.2266717066771307],"luv":[43.2277537555276865,55.1258789529511333,38.9254738590266101],"rgb":[0.6,0.333333333333333315,0.0666666666666666657],"xyz":[0.16486099921625616,0.133107282987986075,0.0223137327458001189],"hpluv":[35.2266717066771307,198.096040704981476,43.2277537555276865],"hsluv":[35.2266717066771307,95.1659583326808303,43.2277537555276865]},"#995522":{"lch":[43.3388072781739,64.3407602415628475,32.9528815923873708],"luv":[43.3388072781739,53.9895016234129059,34.998101990218963],"rgb":[0.6,0.333333333333333315,0.133333333333333331],"xyz":[0.166736357354733161,0.133857426243376904,0.0321906189417793115],"hpluv":[32.9528815923873708,188.385971738918272,43.3388072781739],"hsluv":[32.9528815923873708,86.4646465379692302,43.3388072781739]},"#995533":{"lch":[43.5207548807750584,59.6262244551406582,28.8908620435756234],"luv":[43.5207548807750584,52.2052395636471,28.8079781462883915],"rgb":[0.6,0.333333333333333315,0.2],"xyz":[0.169824108087190895,0.13509252653636,0.0484527727993904617],"hpluv":[28.8908620435756234,173.852210644027934,43.5207548807750584],"hsluv":[28.8908620435756234,72.8304069554253601,43.5207548807750584]},"#995544":{"lch":[43.7814988718974831,53.8284560366537335,22.2989672577237812],"luv":[43.7814988718974831,49.8029788080235036,20.4246415179685634],"rgb":[0.6,0.333333333333333315,0.266666666666666663],"xyz":[0.174282103331409782,0.136875724634047596,0.0719315477522771],"hpluv":[22.2989672577237812,156.01294104827457,43.7814988718974831],"hsluv":[22.2989672577237812,54.5318604429391058,43.7814988718974831]},"#995555":{"lch":[44.1267187000120629,47.9518139182857226,12.1770506300620411],"luv":[44.1267187000120629,46.8729201797035,10.1146335514957979],"rgb":[0.6,0.333333333333333315,0.333333333333333315],"xyz":[0.180244517370771795,0.139260690249792429,0.103333595026251],"hpluv":[12.1770506300620411,137.893162706519348,44.1267187000120629],"hsluv":[12.1770506300620411,32.3126421104369754,44.1267187000120629]},"#995566":{"lch":[44.5602350881765048,43.578232165907,357.727809148728397],"luv":[44.5602350881765048,43.5439690351937116,-1.72773822316436432],"rgb":[0.6,0.333333333333333315,0.4],"xyz":[0.187829513144279953,0.14229468855919572,0.143281239433394963],"hpluv":[357.727809148728397,124.097051059849122,44.5602350881765048],"hsluv":[357.727809148728397,36.5660257696708157,44.5602350881765048]},"#995577":{"lch":[45.0842241078155226,42.5628566452735342,339.858530865537546],"luv":[45.0842241078155226,39.9599368331855729,-14.6560640723873874],"rgb":[0.6,0.333333333333333315,0.466666666666666674],"xyz":[0.197143526164481886,0.146020293767276543,0.192335041339792873],"hpluv":[339.858530865537546,119.796876557388956,45.0842241078155226],"hsluv":[339.858530865537546,41.1101378179314167,45.0842241078155226]},"#995588":{"lch":[45.699386409692309,45.957423726275,322.088602167082399],"luv":[45.699386409692309,36.2586549471185506,-28.2381787122006145],"rgb":[0.6,0.333333333333333315,0.533333333333333326],"xyz":[0.208283943266425686,0.150476460608054141,0.25100790474336504],"hpluv":[322.088602167082399,127.609977655436467,45.699386409692309],"hsluv":[322.088602167082399,45.7577454208568852,45.699386409692309]},"#995599":{"lch":[46.4051108942887964,53.2218555896971921,307.715012949244795],"luv":[46.4051108942887964,32.5576366947089113,-42.1018551285561102],"rgb":[0.6,0.333333333333333315,0.6],"xyz":[0.221340907375498414,0.155699246251683299,0.319774582384483],"hpluv":[307.715012949244795,145.533684272522947,46.4051108942887964],"hsluv":[307.715012949244795,50.3502223422482516,46.4051108942887964]},"#9955aa":{"lch":[47.1996461355186625,63.0010233462712037,297.353030705211779],"luv":[47.1996461355186625,28.9471952899390068,-55.9570266143899246],"rgb":[0.6,0.333333333333333315,0.66666666666666663],"xyz":[0.236398600340750942,0.161722323437784393,0.399078432001481576],"hpluv":[297.353030705211779,169.374563019623821,47.1996461355186625],"hsluv":[297.353030705211779,54.7667714505238337,47.1996461355186625]},"#9955bb":{"lch":[48.0802807126223541,74.1194144420538,290.114722102094788],"luv":[48.0802807126223541,25.4897394646759068,-69.5985688017783701],"rgb":[0.6,0.333333333333333315,0.733333333333333282],"xyz":[0.253536192448087616,0.168577360280719152,0.489336417100123322],"hpluv":[290.114722102094788,195.615971831826613,48.0802807126223541],"hsluv":[290.114722102094788,58.9252289891592866,48.0802807126223541]},"#9955cc":{"lch":[49.0435277691913,85.8237639836856658,285.007043429489613],"luv":[49.0435277691913,22.2230153813451068,-82.8966588692685491],"rgb":[0.6,0.333333333333333315,0.8],"xyz":[0.272828567891809315,0.176294310458207953,0.590942927770393478],"hpluv":[285.007043429489613,222.057364979540267,49.0435277691913],"hsluv":[285.007043429489613,65.256702758776143,49.0435277691913]},"#9955dd":{"lch":[50.0853068419578875,97.6800340866365104,281.315028513382344],"luv":[50.0853068419578875,19.1651498984824649,-95.7814496055220843],"rgb":[0.6,0.333333333333333315,0.866666666666666696],"xyz":[0.294346893442442914,0.184901640678461487,0.704272775670399476],"hpluv":[281.315028513382344,247.476970093373268,50.0853068419578875],"hsluv":[281.315028513382344,76.6956878028438638,50.0853068419578875]},"#9955ee":{"lch":[51.2011159126469266,109.450459224116145,278.575243894560685],"luv":[51.2011159126469266,16.3199513369978568,-108.226901520499666],"rgb":[0.6,0.333333333333333315,0.933333333333333348],"xyz":[0.318159073708617623,0.194426512784931532,0.829683591738922699],"hpluv":[278.575243894560685,271.254835109068836,51.2011159126469266],"hsluv":[278.575243894560685,88.2491190723243335,51.2011159126469266]},"#9955ff":{"lch":[52.3861878346365444,121.012399839465246,276.491711029928183],"luv":[52.3861878346365444,13.6815981187824764,-120.2364952409323],"rgb":[0.6,0.333333333333333315,1],"xyz":[0.344330122118449045,0.204894932148864228,0.967517780030704544],"hpluv":[276.491711029928183,293.124692446110771,52.3861878346365444],"hsluv":[276.491711029928183,99.9999999999991,52.3861878346365444]},"#bb5500":{"lch":[48.3398816318057811,94.9450628471467724,27.1627331553413143],"luv":[48.3398816318057811,84.4739036935134,43.344256295703957],"rgb":[0.733333333333333282,0.333333333333333315,0],"xyz":[0.237414168296793876,0.170634484618534699,0.0204339794019901093],"hpluv":[27.1627331553413143,249.233335779464397,48.3398816318057811],"hsluv":[27.1627331553413143,100.000000000002217,48.3398816318057811]},"#bb5511":{"lch":[48.3907029738951735,93.4795954244126364,26.3417432533787057],"luv":[48.3907029738951735,83.7729911103365907,41.4791600823755289],"rgb":[0.733333333333333282,0.333333333333333315,0.0666666666666666657],"xyz":[0.238425833796431,0.171039150818389551,0.0257620843667457425],"hpluv":[26.3417432533787057,245.128732426237889,48.3907029738951735],"hsluv":[26.3417432533787057,96.2613278044251786,48.3907029738951735]},"#bb5522":{"lch":[48.4847005727335869,90.8707197847641623,24.7826102649670759],"luv":[48.4847005727335869,82.5019575685759321,38.090874386840035],"rgb":[0.733333333333333282,0.333333333333333315,0.133333333333333331],"xyz":[0.240301191934908,0.17178929407378038,0.0356389705627249351],"hpluv":[24.7826102649670759,237.825586669453713,48.4847005727335869],"hsluv":[24.7826102649670759,89.48684037186257,48.4847005727335869]},"#bb5533":{"lch":[48.6388719159228629,86.8743588299812473,22.1104535643633149],"luv":[48.6388719159228629,80.4856161939384407,32.6989267102511363],"rgb":[0.733333333333333282,0.333333333333333315,0.2],"xyz":[0.243388942667365771,0.173024394366763479,0.0519011244203360783],"hpluv":[22.1104535643633149,226.64567974804504,48.6388719159228629],"hsluv":[22.1104535643633149,78.7542493880857393,48.6388719159228629]},"#bb5544":{"lch":[48.8601705631915,81.7431600256382751,18.0266646696809296],"luv":[48.8601705631915,77.730600917119915,25.2962031151029265],"rgb":[0.733333333333333282,0.333333333333333315,0.266666666666666663],"xyz":[0.24784693791158463,0.174807592464451073,0.0753798993732227168],"hpluv":[18.0266646696809296,212.293047020031764,48.8601705631915],"hsluv":[18.0266646696809296,64.1199207289341,48.8601705631915]},"#bb5555":{"lch":[49.1538097277392154,76.0172073873862075,12.1770506300619399],"luv":[49.1538097277392154,74.3068552156307,16.0345591439297159],"rgb":[0.733333333333333282,0.333333333333333315,0.333333333333333315],"xyz":[0.253809351950946616,0.177192558080195905,0.106781946647196632],"hpluv":[12.1770506300619399,196.242945408672853,49.1538097277392154],"hsluv":[12.1770506300619399,45.9858047870319879,49.1538097277392154]},"#bb5566":{"lch":[49.523574907380123,70.5220596563995,4.21567942747197],"luv":[49.523574907380123,70.3312550088346882,5.18415538569556134],"rgb":[0.733333333333333282,0.333333333333333315,0.4],"xyz":[0.261394347724454801,0.180226556389599196,0.14672959105434058],"hpluv":[4.21567942747197,180.697576197923894,49.523574907380123],"hsluv":[4.21567942747197,48.7291682320237527,49.523574907380123]},"#bb5577":{"lch":[49.971995559373525,66.3094111666939341,354.014227306533371],"luv":[49.971995559373525,65.9478803557508257,-6.91484532417379327],"rgb":[0.733333333333333282,0.333333333333333315,0.466666666666666674],"xyz":[0.270708360744656706,0.18395216159768002,0.195783392960738489],"hpluv":[354.014227306533371,168.378953180241098,49.971995559373525],"hsluv":[354.014227306533371,51.7390719561838495,49.971995559373525]},"#bb5588":{"lch":[50.5004659153875508,64.4571905510646275,342.016521446261379],"luv":[50.5004659153875508,61.308172080607541,-19.9006896832966476],"rgb":[0.733333333333333282,0.333333333333333315,0.533333333333333326],"xyz":[0.281848777846600562,0.188408328438457617,0.254456256364310685],"hpluv":[342.016521446261379,161.962814057843559,50.5004659153875508],"hsluv":[342.016521446261379,54.9055054808170055,50.5004659153875508]},"#bb5599":{"lch":[51.1093507584050286,65.6961991151429459,329.411862939203161],"luv":[51.1093507584050286,56.5544024759803,-33.4303774845774342],"rgb":[0.733333333333333282,0.333333333333333315,0.6],"xyz":[0.294905741955673262,0.193631114082086775,0.323222934005428564],"hpluv":[329.411862939203161,163.109481188372769,51.1093507584050286],"hsluv":[329.411862939203161,58.1255686218068561,51.1093507584050286]},"#bb55aa":{"lch":[51.7980911374347386,70.0901192661774246,317.660759061539352],"luv":[51.7980911374347386,51.8085124997667563,-47.2070212077450151],"rgb":[0.733333333333333282,0.333333333333333315,0.66666666666666663],"xyz":[0.309963434920925818,0.199654191268187869,0.402526783622427164],"hpluv":[317.660759061539352,171.704773930815691,51.7980911374347386],"hsluv":[317.660759061539352,61.3115173935257403,51.7980911374347386]},"#bb55bb":{"lch":[52.5653152299933737,77.1031380177621344,307.715012949244226],"luv":[52.5653152299933737,47.1666372355911179,-60.9934605025180332],"rgb":[0.733333333333333282,0.333333333333333315,0.733333333333333282],"xyz":[0.327101027028262437,0.206509228111122628,0.492784768721068911],"hpluv":[307.715012949244226,186.128169856082764,52.5653152299933737],"hsluv":[307.715012949244226,64.3946779967479,52.5653152299933737]},"#bb55cc":{"lch":[53.4089544864585,85.9678373228364308,299.780474196008072],"luv":[53.4089544864585,42.6983512805399883,-74.6144748148057175],"rgb":[0.733333333333333282,0.333333333333333315,0.8],"xyz":[0.346393402471984191,0.21422617828861143,0.594391279391339067],"hpluv":[299.780474196008072,204.249617293250537,53.4089544864585],"hsluv":[299.780474196008072,67.3259326792693855,53.4089544864585]},"#bb55dd":{"lch":[54.3263625650166944,95.9883710948233926,293.613183872979789],"luv":[54.3263625650166944,38.4490902963257213,-87.9513208588848698],"rgb":[0.733333333333333282,0.333333333333333315,0.866666666666666696],"xyz":[0.367911728022617734,0.222833508508864964,0.707721127291345065],"hpluv":[293.613183872979789,224.206046477737118,54.3263625650166944],"hsluv":[293.613183872979789,73.8407928406032283,54.3263625650166944]},"#bb55ee":{"lch":[55.314433433552054,106.647677925948784,288.842761327535072],"luv":[55.314433433552054,34.444226028074695,-100.932266893812525],"rgb":[0.733333333333333282,0.333333333333333315,0.933333333333333348],"xyz":[0.391723908288792444,0.232358380615335,0.833131943359868288],"hpluv":[288.842761327535072,244.653966901481454,55.314433433552054],"hsluv":[288.842761327535072,86.7715876359309135,55.314433433552054]},"#bb55ff":{"lch":[56.3697148536960526,117.598184211748716,285.129655441003138],"luv":[56.3697148536960526,30.6936185759692179,-113.521956944959783],"rgb":[0.733333333333333282,0.333333333333333315,1],"xyz":[0.417894956698623865,0.242826799979267705,0.970966131651650133],"hpluv":[285.129655441003138,264.724480425834031,56.3697148536960526],"hsluv":[285.129655441003138,99.9999999999989,56.3697148536960526]},"#996600":{"lch":[47.3343652017352454,63.4240894393546952,48.3260196362919672],"luv":[47.3343652017352454,42.1701199842588,47.3740023823666476],"rgb":[0.6,0.4,0],"xyz":[0.178877391422465504,0.162758732199824563,0.0219949803496598331],"hpluv":[48.3260196362919672,170.026654750900292,47.3343652017352454],"hsluv":[48.3260196362919672,100.000000000002288,47.3343652017352454]},"#996611":{"lch":[47.3868110627231189,61.667674323653,47.4017350737566616],"luv":[47.3868110627231189,41.7399921999205,45.3946594616449133],"rgb":[0.6,0.4,0.0666666666666666657],"xyz":[0.179889056922102636,0.163163398399679416,0.0273230853144154628],"hpluv":[47.4017350737566616,165.135107398016657,47.3868110627231189],"hsluv":[47.4017350737566616,96.0239926064661,47.3868110627231189]},"#996622":{"lch":[47.4838028017404099,58.5263777304533406,45.5834379771503322],"luv":[47.4838028017404099,40.9608465044279413,41.8036594557031336],"rgb":[0.6,0.4,0.133333333333333331],"xyz":[0.181764415060579637,0.163913541655070244,0.0371999715103946624],"hpluv":[45.5834379771503322,156.40314428015364,47.4838028017404099],"hsluv":[45.5834379771503322,88.8298219708016177,47.4838028017404099]},"#996633":{"lch":[47.642855645786625,53.6803525088045674,42.2629253198225712],"luv":[47.642855645786625,39.7270268335614887,36.1018501525199724],"rgb":[0.6,0.4,0.2],"xyz":[0.184852165793037371,0.165148641948053343,0.0534621253680058056],"hpluv":[42.2629253198225712,142.973945144987511,47.642855645786625],"hsluv":[42.2629253198225712,77.4596172761956865,47.642855645786625]},"#996644":{"lch":[47.8710980897590872,47.4159845831850149,36.6424092143452071],"luv":[47.8710980897590872,38.045445264132745,28.298757722676072],"rgb":[0.6,0.4,0.266666666666666663],"xyz":[0.189310161037256258,0.166931840045740937,0.0769409003208924441],"hpluv":[36.6424092143452071,125.687101185572914,47.8710980897590872],"hsluv":[36.6424092143452071,62.0095295256686185,47.8710980897590872]},"#996655":{"lch":[48.173837669734425,40.4765536471140521,27.3183313201514686],"luv":[48.173837669734425,35.9622216701857838,18.5760600691352664],"rgb":[0.6,0.4,0.333333333333333315],"xyz":[0.195272575076618271,0.16931680566148577,0.108342947594866346],"hpluv":[27.3183313201514686,106.618264590322553,48.173837669734425],"hsluv":[27.3183313201514686,42.9499413016611484,48.173837669734425]},"#996666":{"lch":[48.5548823199147819,34.3240193245421,12.1770506300621335],"luv":[48.5548823199147819,33.551744690774747,7.24007809326721219],"rgb":[0.6,0.4,0.4],"xyz":[0.202857570850126429,0.17235080397088906,0.148290592002010307],"hpluv":[12.1770506300621335,89.702502372613651,48.5548823199147819],"hsluv":[12.1770506300621335,21.0200766933302461,48.5548823199147819]},"#996677":{"lch":[49.0167186013709,31.3606600839303482,350.2076113500313],"luv":[49.0167186013709,30.9037509766054157,-5.33377694282222681],"rgb":[0.6,0.4,0.466666666666666674],"xyz":[0.212171583870328362,0.176076409178969884,0.197344393908408217],"hpluv":[350.2076113500313,81.185839798992177,49.0167186013709],"hsluv":[350.2076113500313,25.8881256225468483,49.0167186013709]},"#996688":{"lch":[49.5606396668562752,33.7929953796712823,326.289263208678278],"luv":[49.5606396668562752,28.1107077320421368,-18.7551232343109682],"rgb":[0.6,0.4,0.533333333333333326],"xyz":[0.223312000972272162,0.180532576019747482,0.256017257311980384],"hpluv":[326.289263208678278,86.5225105267045365,49.5606396668562752],"hsluv":[326.289263208678278,30.9767265160446463,49.5606396668562752]},"#996699":{"lch":[50.1868595811773304,41.2886932006772,307.715012949245818],"luv":[50.1868595811773304,25.2577114783499113,-32.6619686653465351],"rgb":[0.6,0.4,0.6],"xyz":[0.236368965081344889,0.185755361663376639,0.324783934953098319],"hpluv":[307.715012949245818,104.395179003902854,50.1868595811773304],"hsluv":[307.715012949245818,36.1175524455388413,50.1868595811773304]},"#9966aa":{"lch":[50.8946289106688141,51.8448774557822,295.617938374847199],"luv":[50.8946289106688141,22.4160699516755599,-46.7483810021979451],"rgb":[0.6,0.4,0.66666666666666663],"xyz":[0.251426658046597418,0.191778438849477734,0.40408778457009692],"hpluv":[295.617938374847199,129.262701065701,50.8946289106688141],"hsluv":[295.617938374847199,41.1705422178494445,50.8946289106688141]},"#9966bb":{"lch":[51.6823563026294,63.8712345518136,287.908782454232437],"luv":[51.6823563026294,19.6405628457828776,-60.776499523036378],"rgb":[0.6,0.4,0.733333333333333282],"xyz":[0.268564250153934092,0.198633475692412492,0.494345769668738666],"hpluv":[287.908782454232437,156.820318415219759,51.6823563026294],"hsluv":[287.908782454232437,49.349420166275209,51.6823563026294]},"#9966cc":{"lch":[52.5477355185796569,76.4825896496335389,282.819195018988751],"luv":[52.5477355185796569,16.9695880464031816,-74.5762670093482285],"rgb":[0.6,0.4,0.8],"xyz":[0.287856625597655791,0.206350425869901294,0.595952280339008822],"hpluv":[282.819195018988751,184.691924030055,52.5477355185796569],"hsluv":[282.819195018988751,61.7185051859638136,52.5477355185796569]},"#9966dd":{"lch":[53.4878747475352725,89.2117449852098758,279.306611445978092],"luv":[53.4878747475352725,14.4271177471338046,-88.0374563286358],"rgb":[0.6,0.4,0.866666666666666696],"xyz":[0.30937495114828939,0.214957756090154828,0.70928212823901482],"hpluv":[279.306611445978092,211.644028054176175,53.4878747475352725],"hsluv":[279.306611445978092,74.2596178074049362,53.4878747475352725]},"#9966ee":{"lch":[54.4994239962059339,101.810849438236403,276.783380903602506],"luv":[54.4994239962059339,12.0254847729914225,-101.098154183495325],"rgb":[0.6,0.4,0.933333333333333348],"xyz":[0.333187131414464099,0.224482628196624873,0.834692944307538],"hpluv":[276.783380903602506,237.050827216632513,54.4994239962059339],"hsluv":[276.783380903602506,86.9990828065156734,54.4994239962059339]},"#9966ff":{"lch":[55.5786963614876,114.151421142661491,274.908981870437117],"luv":[55.5786963614876,9.76829238934238475,-113.732701597586171],"rgb":[0.6,0.4,1],"xyz":[0.359358179824295521,0.234951047560557569,0.972527132599319888],"hpluv":[274.908981870437117,260.622732185953453,55.5786963614876],"hsluv":[274.908981870437117,99.999999999998991,55.5786963614876]},"#bb6600":{"lch":[51.9152024616159622,85.9194467179265899,34.7713476038742],"luv":[51.9152024616159622,70.5771984524152884,49.0001059480794936],"rgb":[0.733333333333333282,0.4,0],"xyz":[0.252442226002640324,0.200690600030228039,0.0254433319706054567],"hpluv":[34.7713476038742,210.008196913669821,51.9152024616159622],"hsluv":[34.7713476038742,100.000000000002217,51.9152024616159622]},"#bb6611":{"lch":[51.960819173128,84.5067938711160735,34.0102696578751846],"luv":[51.960819173128,70.0508360673827184,47.2681560528436],"rgb":[0.733333333333333282,0.4,0.0666666666666666657],"xyz":[0.253453891502277429,0.201095266230082892,0.0307714369353610864],"hpluv":[34.0102696578751846,206.373990798532162,51.960819173128],"hsluv":[34.0102696578751846,96.7960731817087492,51.960819173128]},"#bb6622":{"lch":[52.0452187907305586,81.9745670334704215,32.5563816919222901],"luv":[52.0452187907305586,69.0932729256005587,44.1129150788451909],"rgb":[0.733333333333333282,0.4,0.133333333333333331],"xyz":[0.255329249640754485,0.20184540948547372,0.040648323131340286],"hpluv":[32.5563816919222901,199.865401092535734,52.0452187907305586],"hsluv":[32.5563816919222901,90.9716633106406221,52.0452187907305586]},"#bb6633":{"lch":[52.1837271506259412,78.04817012610809,30.0375791414029081],"luv":[52.1837271506259412,67.5660884046363,39.0684086920722109],"rgb":[0.733333333333333282,0.4,0.2],"xyz":[0.258417000373212191,0.203080509778456819,0.0569104769889514292],"hpluv":[30.0375791414029081,189.787216830323956,52.1837271506259412],"hsluv":[30.0375791414029081,81.6941023093515639,52.1837271506259412]},"#bb6644":{"lch":[52.3827138306128859,72.9081368603095257,26.1184165145312406],"luv":[52.3827138306128859,65.4632041398932,32.0961886238895318],"rgb":[0.733333333333333282,0.4,0.266666666666666663],"xyz":[0.262874995617431106,0.204863707876144413,0.0803892519418380747],"hpluv":[26.1184165145312406,176.614896915735159,52.3827138306128859],"hsluv":[26.1184165145312406,68.9437006343278824,52.3827138306128859]},"#bb6655":{"lch":[52.6470547760809637,67.004522830646,20.3474245095699864],"luv":[52.6470547760809637,62.823537757634746,23.2982656731697979],"rgb":[0.733333333333333282,0.4,0.333333333333333315],"xyz":[0.268837409656793092,0.207248673491889246,0.111791299215811976],"hpluv":[20.3474245095699864,161.498824171429789,52.6470547760809637],"hsluv":[20.3474245095699864,52.9816895309479534,52.6470547760809637]},"#bb6666":{"lch":[52.9804174131186727,61.0960132663801261,12.1770506300620109],"luv":[52.9804174131186727,59.7213810933874498,12.8871826767560087],"rgb":[0.733333333333333282,0.4,0.4],"xyz":[0.276422405430301277,0.210282671801292537,0.151738943622955924],"hpluv":[12.1770506300620109,146.331162504643544,52.9804174131186727],"hsluv":[12.1770506300620109,34.2899270044314335,52.9804174131186727]},"#bb6677":{"lch":[53.3854132757716826,56.2661599102721226,1.17050216666979279],"luv":[53.3854132757716826,56.2544190111897535,1.14938799445880213],"rgb":[0.733333333333333282,0.4,0.466666666666666674],"xyz":[0.285736418450503182,0.21400827700937336,0.200792745529353833],"hpluv":[1.17050216666979279,133.74082367080041,53.3854132757716826],"hsluv":[1.17050216666979279,37.694842795415525,53.3854132757716826]},"#bb6688":{"lch":[53.8636991644475387,53.7950791600554297,347.554026547444039],"luv":[53.8636991644475387,52.5308766640911458,-11.5938578022448873],"rgb":[0.733333333333333282,0.4,0.533333333333333326],"xyz":[0.296876835552447,0.218464443850150958,0.259465608932926028],"hpluv":[347.554026547444039,126.731828674836166,53.8636991644475387],"hsluv":[347.554026547444039,41.3310399817098855,53.8636991644475387]},"#bb6699":{"lch":[54.4160596975863484,54.7145736516266723,332.78581188167891],"luv":[54.4160596975863484,48.6578429809048814,-25.0219680745716886],"rgb":[0.733333333333333282,0.4,0.6],"xyz":[0.309933799661519682,0.223687229493780115,0.328232286574043908],"hpluv":[332.78581188167891,127.589593839900076,54.4160596975863484],"hsluv":[332.78581188167891,45.0871489409768813,54.4160596975863484]},"#bb66aa":{"lch":[55.0424859024436,59.2427277394444332,319.03138367428437],"luv":[55.0424859024436,44.7323366642012274,-38.8422301956323963],"rgb":[0.733333333333333282,0.4,0.66666666666666663],"xyz":[0.324991492626772238,0.22971030667988121,0.407536136191042508],"hpluv":[319.03138367428437,136.57661144398341,55.0424859024436],"hsluv":[319.03138367428437,48.8627280546895548,55.0424859024436]},"#bb66bb":{"lch":[55.7422560614222,66.7541112543564168,307.715012949244738],"luv":[55.7422560614222,40.8357821803987235,-52.8067255477370452],"rgb":[0.733333333333333282,0.4,0.733333333333333282],"xyz":[0.342129084734108913,0.236565343522815968,0.497794121289684255],"hpluv":[307.715012949244738,151.961230908585406,55.7422560614222],"hsluv":[307.715012949244738,52.5739577191036176,55.7422560614222]},"#bb66cc":{"lch":[56.5140206008290704,76.3075420390205466,299.031845144425],"luv":[56.5140206008290704,37.0317190732607671,-66.7195080506142801],"rgb":[0.733333333333333282,0.4,0.8],"xyz":[0.361421460177830611,0.24428229370030477,0.599400631959954411],"hpluv":[299.031845144425,171.336773989457299,56.5140206008290704],"hsluv":[299.031845144425,57.7422914315118732,56.5140206008290704]},"#bb66dd":{"lch":[57.3558903641351208,87.0819801731049523,292.529347086481266],"luv":[57.3558903641351208,33.3660351201831276,-80.4361794917420809],"rgb":[0.733333333333333282,0.4,0.866666666666666696],"xyz":[0.38293978572846421,0.252889623920558304,0.712730479859960409],"hpluv":[292.529347086481266,192.659130793538424,57.3558903641351208],"hsluv":[292.529347086481266,71.4699790991907,57.3558903641351208]},"#bb66ee":{"lch":[58.2655263288606164,98.4965661150924916,287.652756970849339],"luv":[58.2655263288606164,29.8688319231142287,-93.8585447149780805],"rgb":[0.733333333333333282,0.4,0.933333333333333348],"xyz":[0.40675196599463892,0.262414496027028321,0.838141295928483632],"hpluv":[287.652756970849339,214.510592558062115,58.2655263288606164],"hsluv":[287.652756970849339,85.5420625539119186,58.2655263288606164]},"#bb66ff":{"lch":[59.2402283004695533,110.175409569553764,283.948158265715847],"luv":[59.2402283004695533,26.5571064878979755,-106.926801919861816],"rgb":[0.733333333333333282,0.4,1],"xyz":[0.432923014404470341,0.272882915390961045,0.975975484220265477],"hpluv":[283.948158265715847,235.997431668916079,59.2402283004695533],"hsluv":[283.948158265715847,99.9999999999988205,59.2402283004695533]},"#997700":{"lch":[51.799451349173637,61.2288227532233265,61.7368019650066202],"luv":[51.799451349173637,28.9932293289078338,53.9292257391759406],"rgb":[0.6,0.466666666666666674,0],"xyz":[0.197331129475883132,0.19966620830666032,0.028146226367465537],"hpluv":[61.7368019650066202,149.992683828924328,51.799451349173637],"hsluv":[61.7368019650066202,100.000000000002373,51.799451349173637]},"#997711":{"lch":[51.8452237949875752,59.5195003520895298,61.1595916745536812],"luv":[51.8452237949875752,28.7105152364728049,52.1371003894409952],"rgb":[0.6,0.466666666666666674,0.0666666666666666657],"xyz":[0.198342794975520265,0.200070874506515173,0.0334743313322211702],"hpluv":[61.1595916745536812,145.676617799321178,51.8452237949875752],"hsluv":[61.1595916745536812,96.7355174862457687,51.8452237949875752]},"#997722":{"lch":[51.9299107218419778,56.4237897442973448,60.0184197539949],"luv":[51.9299107218419778,28.1961842142201746,48.8735024820856836],"rgb":[0.6,0.466666666666666674,0.133333333333333331],"xyz":[0.200218153113997266,0.200821017761906,0.0433512175282003628],"hpluv":[60.0184197539949,137.874516690543771,51.9299107218419778],"hsluv":[60.0184197539949,90.8032063934702762,51.9299107218419778]},"#997733":{"lch":[52.0688882655341843,51.532107019520943,57.9108306747127841],"luv":[52.0688882655341843,27.375836028403171,43.6591531710973797],"rgb":[0.6,0.466666666666666674,0.2],"xyz":[0.203305903846455027,0.2020561180548891,0.0596133713858115061],"hpluv":[57.9108306747127841,125.585333003110208,52.0688882655341843],"hsluv":[57.9108306747127841,81.359583819486275,52.0688882655341843]},"#997744":{"lch":[52.2685439893789265,44.9234500117750244,54.2507061045172563],"luv":[52.2685439893789265,26.2460613351246792,36.4590266649196622],"rgb":[0.6,0.466666666666666674,0.266666666666666663],"xyz":[0.207763899090673887,0.203839316152576694,0.0830921463386981446],"hpluv":[54.2507061045172563,109.061640425204757,52.2685439893789265],"hsluv":[54.2507061045172563,68.3925961892813632,52.2685439893789265]},"#997755":{"lch":[52.5337646967731615,36.9638675487782962,47.8040559904272229],"luv":[52.5337646967731615,24.8274523779219614,27.3847605902560787],"rgb":[0.6,0.466666666666666674,0.333333333333333315],"xyz":[0.213726313130035872,0.206224281768321527,0.11449419361267206],"hpluv":[47.8040559904272229,89.2849392346346,52.5337646967731615],"hsluv":[47.8040559904272229,52.1784042692219217,52.5337646967731615]},"#997766":{"lch":[52.8682223623880958,28.5307109293219519,35.7342712802299047],"luv":[52.8682223623880958,23.1593577603421643,16.6627012894371447],"rgb":[0.6,0.466666666666666674,0.4],"xyz":[0.221311308903544057,0.209258280077724818,0.154441838019816],"hpluv":[35.7342712802299047,68.4789688439394695,52.8682223623880958],"hsluv":[35.7342712802299047,33.2180784313787072,52.8682223623880958]},"#997777":{"lch":[53.2745272921510349,21.7835186536615062,12.1770506300626185],"luv":[53.2745272921510349,21.2933995119794766,4.59486911213486],"rgb":[0.6,0.466666666666666674,0.466666666666666674],"xyz":[0.230625321923745963,0.212983885285805641,0.203495639926213917],"hpluv":[12.1770506300626185,51.8857087556556777,53.2745272921510349],"hsluv":[12.1770506300626185,12.1864056638809046,53.2745272921510349]},"#997788":{"lch":[53.7543298043441524,21.0694932264164443,336.259644884191403],"luv":[53.7543298043441524,19.28657717102978,-8.48242216845644492],"rgb":[0.6,0.466666666666666674,0.533333333333333326],"xyz":[0.241765739025689819,0.217440052126583239,0.262168503329786085],"hpluv":[336.259644884191403,49.7370433553737143,53.7543298043441524],"hsluv":[336.259644884191403,17.3594920402921318,53.7543298043441524]},"#997799":{"lch":[54.308403390094881,28.1087123643927,307.715012949247921],"luv":[54.308403390094881,17.1950646022419384,-22.235769923906151],"rgb":[0.6,0.466666666666666674,0.6],"xyz":[0.254822703134762518,0.222662837770212396,0.330935180970904],"hpluv":[307.715012949247921,65.67699031054849,54.308403390094881],"hsluv":[307.715012949247921,22.7222383700086681,54.308403390094881]},"#9977aa":{"lch":[54.9367240193748785,39.361724048309469,292.510155009375],"luv":[54.9367240193748785,15.0695247856183894,-36.3628208860492137],"rgb":[0.6,0.466666666666666674,0.66666666666666663],"xyz":[0.269880396100015074,0.228685914956313491,0.41023903058790262],"hpluv":[292.510155009375,90.9181689788077847,54.9367240193748785],"hsluv":[292.510155009375,30.8964586946041671,54.9367240193748785]},"#9977bb":{"lch":[55.6385517902762388,52.2405182414828104,284.355371582691191],"luv":[55.6385517902762388,12.9522722169503091,-50.6093903397058824],"rgb":[0.6,0.466666666666666674,0.733333333333333282],"xyz":[0.287017988207351693,0.235540951799248249,0.500497015686544366],"hpluv":[284.355371582691191,119.143673365147691,55.6385517902762388],"hsluv":[284.355371582691191,44.025050152294007,55.6385517902762388]},"#9977cc":{"lch":[56.4125166695819615,65.6830300518217172,279.531250686135081],"luv":[56.4125166695819615,10.8761592408186605,-64.7763042860340192],"rgb":[0.6,0.466666666666666674,0.8],"xyz":[0.306310363651073447,0.243257901976737051,0.602103526356814522],"hpluv":[279.531250686135081,147.746441920881637,56.4125166695819615],"hsluv":[279.531250686135081,57.5215558896316352,56.4125166695819615]},"#9977dd":{"lch":[57.256707620000924,79.2159287347205,276.425234899334782],"luv":[57.256707620000924,8.86478593594131858,-78.718351962006011],"rgb":[0.6,0.466666666666666674,0.866666666666666696],"xyz":[0.327828689201707,0.251865232196990585,0.71543337425682052],"hpluv":[276.425234899334782,175.559960004438778,57.256707620000924],"hsluv":[276.425234899334782,71.3413917340773764,57.256707620000924]},"#9977ee":{"lch":[58.1687631275758434,92.5981917117116211,274.294200356840065],"luv":[58.1687631275758434,6.93354781471817905,-92.3382424728774822],"rgb":[0.6,0.466666666666666674,0.933333333333333348],"xyz":[0.351640869467881756,0.261390104303460602,0.840844190325343743],"hpluv":[274.294200356840065,202.000294664132923,58.1687631275758434],"hsluv":[274.294200356840065,85.4864748016987903,58.1687631275758434]},"#9977ff":{"lch":[59.1459606243173,105.700835267318482,272.760722153075335],"luv":[59.1459606243173,5.0910859147335179,-105.578157875659116],"rgb":[0.6,0.466666666666666674,1],"xyz":[0.377811917877713122,0.271858523667393326,0.978678378617125588],"hpluv":[272.760722153075335,226.773684284234889,59.1459606243173],"hsluv":[272.760722153075335,99.9999999999988,59.1459606243173]},"#bb7700":{"lch":[55.8465021194210323,78.9426527823167703,44.2288975260652037],"luv":[55.8465021194210323,56.5670601566064,55.0645996403160751],"rgb":[0.733333333333333282,0.466666666666666674,0],"xyz":[0.270895964056058,0.237598076137063796,0.0315945779884111572],"hpluv":[44.2288975260652037,179.372171604304526,55.8465021194210323],"hsluv":[44.2288975260652037,100.000000000002402,55.8465021194210323]},"#bb7711":{"lch":[55.8872675460691113,77.5581898683241775,43.5811171927028127],"luv":[55.8872675460691113,56.1830827877517081,53.4671303149482782],"rgb":[0.733333333333333282,0.466666666666666674,0.0666666666666666657],"xyz":[0.271907629555695085,0.238002742336918649,0.0369226829531667869],"hpluv":[43.5811171927028127,176.097874689063588,55.8872675460691113],"hsluv":[43.5811171927028127,97.2747266570755613,55.8872675460691113]},"#bb7722":{"lch":[55.9627137258116534,75.0570685818031365,42.3362918908129799],"luv":[55.9627137258116534,55.4825342436315907,50.549499878809371],"rgb":[0.733333333333333282,0.466666666666666674,0.133333333333333331],"xyz":[0.273782987694172142,0.238752885592309477,0.0467995691491459864],"hpluv":[42.3362918908129799,170.189263625481374,55.9627137258116534],"hsluv":[42.3362918908129799,92.3060252972229875,55.9627137258116534]},"#bb7733":{"lch":[56.086591241629975,71.12386069085467,40.1553843778465094],"luv":[56.086591241629975,54.3598535070873652,45.8651271257379562],"rgb":[0.733333333333333282,0.466666666666666674,0.2],"xyz":[0.276870738426629848,0.239987985885292576,0.0630617230067571366],"hpluv":[40.1553843778465094,160.914656808849855,56.086591241629975],"hsluv":[40.1553843778465094,84.3528141134219425,56.086591241629975]},"#bb7744":{"lch":[56.2646940492590346,65.8532423012147632,36.6949041745596887],"luv":[56.2646940492590346,52.8030258036740747,39.3508575199960617],"rgb":[0.733333333333333282,0.466666666666666674,0.266666666666666663],"xyz":[0.281328733670848763,0.24177118398298017,0.0865404979596437751],"hpluv":[36.6949041745596887,148.518490760970963,56.2646940492590346],"hsluv":[36.6949041745596887,73.3442464951488517,56.2646940492590346]},"#bb7755":{"lch":[56.5015366957073866,59.5720019456390375,31.4313823601585],"luv":[56.5015366957073866,50.8307220514796256,31.0654327434279267],"rgb":[0.733333333333333282,0.466666666666666674,0.333333333333333315],"xyz":[0.287291147710210748,0.244156149598725,0.117942545233617677],"hpluv":[31.4313823601585,133.789263155984059,56.5015366957073866],"hsluv":[31.4313823601585,59.433527841274838,56.5015366957073866]},"#bb7766":{"lch":[56.8006139195957758,52.9060784141758376,23.584548946691168],"luv":[56.8006139195957758,48.4868685528452588,21.1678225404983067],"rgb":[0.733333333333333282,0.466666666666666674,0.4],"xyz":[0.294876143483718933,0.247190147908128294,0.157890189640761625],"hpluv":[23.584548946691168,118.193030459121616,56.8006139195957758],"hsluv":[23.584548946691168,42.9582981176577476,56.8006139195957758]},"#bb7777":{"lch":[57.1645375630264851,46.8887603377642677,12.1770506300621175],"luv":[57.1645375630264851,45.8337848153679062,9.89040016939892652],"rgb":[0.733333333333333282,0.466666666666666674,0.466666666666666674],"xyz":[0.304190156503920839,0.250915753116209117,0.206943991547159534],"hpluv":[12.1770506300621175,104.083378979503351,57.1645375630264851],"hsluv":[12.1770506300621175,30.9338815185941769,57.1645375630264851]},"#bb7788":{"lch":[57.5951231639082408,43.0166042307866761,356.679907444569722],"luv":[57.5951231639082408,42.9444038116514264,-2.49126891562360298],"rgb":[0.733333333333333282,0.466666666666666674,0.533333333333333326],"xyz":[0.315330573605864639,0.255371919956986715,0.265616854950731729],"hpluv":[356.679907444569722,94.7741152880685149,57.5951231639082408],"hsluv":[356.679907444569722,32.9204937798227419,57.5951231639082408]},"#bb7799":{"lch":[58.0934559183715322,42.8683503206465062,338.533521874652],"luv":[58.0934559183715322,39.8946515879682408,-15.6879646190414519],"rgb":[0.733333333333333282,0.466666666666666674,0.6],"xyz":[0.328387537714937339,0.260594705600615872,0.334383532591849608],"hpluv":[338.533521874652,93.6373004736019823,58.0934559183715322],"hsluv":[338.533521874652,34.9811369830312131,58.0934559183715322]},"#bb77aa":{"lch":[58.6599497668646706,47.0820583870944276,321.324781752589786],"luv":[58.6599497668646706,36.7569990603765,-29.4218157502430451],"rgb":[0.733333333333333282,0.466666666666666674,0.66666666666666663],"xyz":[0.343445230680189895,0.266617782786717,0.413687382208848209],"hpluv":[321.324781752589786,101.848135129910219,58.6599497668646706],"hsluv":[321.324781752589786,37.0556810629478051,58.6599497668646706]},"#bb77bb":{"lch":[59.2944060970233693,54.9189596898792303,307.71501294924542],"luv":[59.2944060970233693,33.5958135510894706,-43.4443718479046765],"rgb":[0.733333333333333282,0.466666666666666674,0.733333333333333282],"xyz":[0.360582822787526569,0.273472819629651753,0.50394536730749],"hpluv":[307.71501294924542,117.529775077760561,59.2944060970233693],"hsluv":[307.71501294924542,40.6617226560308467,59.2944060970233693]},"#bb77cc":{"lch":[59.9960747587738155,65.1135012197087519,297.896140559284788],"luv":[59.9960747587738155,30.4646722292654815,-57.5471266620014177],"rgb":[0.733333333333333282,0.466666666666666674,0.8],"xyz":[0.379875198231248268,0.281189769807140555,0.605551877977760111],"hpluv":[297.896140559284788,137.716994859863917,59.9960747587738155],"hsluv":[297.896140559284788,53.5682479917909,59.9960747587738155]},"#bb77dd":{"lch":[60.7637179337253599,76.6332290650186394,290.953982706702106],"luv":[60.7637179337253599,27.4054240934965065,-71.5653165100750357],"rgb":[0.733333333333333282,0.466666666666666674,0.866666666666666696],"xyz":[0.401393523781881867,0.289797100027394061,0.718881725877766109],"hpluv":[290.953982706702106,160.033945707839,60.7637179337253599],"hsluv":[290.953982706702106,68.5485010485900119,60.7637179337253599]},"#bb77ee":{"lch":[61.5956761624293563,88.8082274030350902,285.979672103614405],"luv":[61.5956761624293563,24.4485758512518743,-85.3766267389077456],"rgb":[0.733333333333333282,0.466666666666666674,0.933333333333333348],"xyz":[0.425205704048056576,0.299321972133864078,0.844292541946289332],"hpluv":[285.979672103614405,182.954165183101395,61.5956761624293563],"hsluv":[285.979672103614405,84.0163684558434909,61.5956761624293563]},"#bb77ff":{"lch":[62.4899351736807773,101.23109143545085,282.328516880108566],"luv":[62.4899351736807773,21.6145233767131266,-98.8966442929694693],"rgb":[0.733333333333333282,0.466666666666666674,1],"xyz":[0.451376752457888,0.309790391497796802,0.982126730238071177],"hpluv":[282.328516880108566,205.562159598045383,62.4899351736807773],"hsluv":[282.328516880108566,99.9999999999986784,62.4899351736807773]},"#998800":{"lch":[56.4673516485332527,62.834492950420568,74.7562721675545561],"luv":[56.4673516485332527,16.5207966112614884,60.6237311922136897],"rgb":[0.6,0.533333333333333326,0],"xyz":[0.219403499200853586,0.243810947756601892,0.0355036829424554834],"hpluv":[74.7562721675545561,141.201731332299261,56.4673516485332527],"hsluv":[74.7562721675545561,100.000000000002331,56.4673516485332527]},"#998811":{"lch":[56.5074221469723881,61.2259080808398366,74.51872844438833],"luv":[56.5074221469723881,16.3426261455871646,59.0044946676862878],"rgb":[0.6,0.533333333333333326,0.0666666666666666657],"xyz":[0.220415164700490718,0.244215613956456745,0.0408317879072111131],"hpluv":[74.51872844438833,137.489352382480689,56.5074221469723881],"hsluv":[74.51872844438833,97.3110722480099781,56.5074221469723881]},"#998822":{"lch":[56.5815852437789744,58.2905156727598666,74.0508275651119],"luv":[56.5815852437789744,16.0173304476746452,56.0466711119068961],"rgb":[0.6,0.533333333333333326,0.133333333333333331],"xyz":[0.222290522838967719,0.244965757211847573,0.0507086741031903127],"hpluv":[74.0508275651119,130.726042229432494,56.5815852437789744],"hsluv":[74.0508275651119,92.4075523300517574,56.5815852437789744]},"#998833":{"lch":[56.7033645714083,53.5858970619529273,73.1917859227149],"luv":[56.7033645714083,15.4953822971072128,51.2966031175623911],"rgb":[0.6,0.533333333333333326,0.2],"xyz":[0.22537827357142548,0.246200857504830672,0.066970827960801449],"hpluv":[73.1917859227149,119.917068416956084,56.7033645714083],"hsluv":[73.1917859227149,84.5557602276611533,56.7033645714083]},"#998844":{"lch":[56.8784692361674189,47.0662938677721456,71.7105236832421156],"luv":[56.8784692361674189,14.7702533586865563,44.6886521856241927],"rgb":[0.6,0.533333333333333326,0.266666666666666663],"xyz":[0.22983626881564434,0.247984055602518266,0.0904496029136880875],"hpluv":[71.7105236832421156,105.00293267331034,56.8784692361674189],"hsluv":[71.7105236832421156,73.6816499137771075,56.8784692361674189]},"#998855":{"lch":[57.1113583918905761,38.8337969655052433,69.1066660847648393],"luv":[57.1113583918905761,13.8492700909341142,36.2803184206870242],"rgb":[0.6,0.533333333333333326,0.333333333333333315],"xyz":[0.235798682855006325,0.250369021218263099,0.121851650187662],"hpluv":[69.1066660847648393,86.2832891510872599,57.1113583918905761],"hsluv":[69.1066660847648393,59.9309888570698774,57.1113583918905761]},"#998866":{"lch":[57.4054971647218224,29.1657433305745144,64.0747883902216],"luv":[57.4054971647218224,12.7511922431808031,26.2306629806110223],"rgb":[0.6,0.533333333333333326,0.4],"xyz":[0.24338367862851451,0.253403019527666418,0.161799294594805965],"hpluv":[64.0747883902216,64.4701815948561574,57.4054971647218224],"hsluv":[64.0747883902216,43.6310875702010321,57.4054971647218224]},"#998877":{"lch":[57.7634914296009612,18.7238256654584347,52.0945218145914097],"luv":[57.7634914296009612,11.5031815844705712,14.773573060880608],"rgb":[0.6,0.533333333333333326,0.466666666666666674],"xyz":[0.252697691648716416,0.257128624735747213,0.210853096501203874],"hpluv":[52.0945218145914097,41.1320618043462858,57.7634914296009612],"hsluv":[52.0945218145914097,25.2418236256697028,57.7634914296009612]},"#998888":{"lch":[58.1871725604667489,10.3706980586515272,12.1770506300640946],"luv":[58.1871725604667489,10.1373621264743505,2.18752539195365081],"rgb":[0.6,0.533333333333333326,0.533333333333333326],"xyz":[0.263838108750660272,0.261584791576524811,0.269525959904776],"hpluv":[12.1770506300640946,22.6162221883482317,58.1871725604667489],"hsluv":[12.1770506300640946,7.14421708061451799,58.1871725604667489]},"#998899":{"lch":[58.6776613659523605,14.2013195506443459,307.715012949254117],"luv":[58.6776613659523605,8.6874348403017283,-11.2341422847952668],"rgb":[0.6,0.533333333333333326,0.6],"xyz":[0.276895072859732971,0.266807577220153969,0.338292637545893948],"hpluv":[307.715012949254117,30.7110899398720818,58.6776613659523605],"hsluv":[307.715012949254117,10.6251017733449729,58.6776613659523605]},"#9988aa":{"lch":[59.2354248002074399,26.2127450059066938,285.910383521223935],"luv":[59.2354248002074399,7.18579172527609789,-25.2085778659891844],"rgb":[0.6,0.533333333333333326,0.66666666666666663],"xyz":[0.291952765824985527,0.272830654406255091,0.417596487162892549],"hpluv":[285.910383521223935,56.1526584479988173,59.2354248002074399],"hsluv":[285.910383521223935,23.5088603674401817,59.2354248002074399]},"#9988bb":{"lch":[59.8603319378123189,39.8857557696632696,278.160160819701673],"luv":[59.8603319378123189,5.66141142610705117,-39.4819190767314581],"rgb":[0.6,0.533333333333333326,0.733333333333333282],"xyz":[0.309090357932322146,0.279685691249189849,0.507854472261534351],"hpluv":[278.160160819701673,84.5508603994872,59.8603319378123189],"hsluv":[278.160160819701673,37.7262541103550291,59.8603319378123189]},"#9988cc":{"lch":[60.55171199345871,53.9990869041691823,274.395593429514747],"luv":[60.55171199345871,4.13861667015410095,-53.8402566723222264],"rgb":[0.6,0.533333333333333326,0.8],"xyz":[0.3283827333760439,0.287402641426678651,0.609460982931804507],"hpluv":[274.395593429514747,113.161661700826286,60.55171199345871],"hsluv":[274.395593429514747,52.5240219153236723,60.55171199345871]},"#9988dd":{"lch":[61.3084150605589855,68.1648195931338563,272.216747448240312],"luv":[61.3084150605589855,2.63660773112319324,-68.1138086575451],"rgb":[0.6,0.533333333333333326,0.866666666666666696],"xyz":[0.349901058926677444,0.296009971646932157,0.722790830831810505],"hpluv":[272.216747448240312,141.084572716849891,61.3084150605589855],"hsluv":[272.216747448240312,67.8395486149695,61.3084150605589855]},"#9988ee":{"lch":[62.128875020953032,82.18421866163294,270.815422264053723],"luv":[62.128875020953032,1.1695901512833331,-82.1758958326656597],"rgb":[0.6,0.533333333333333326,0.933333333333333348],"xyz":[0.373713239192852154,0.305534843753402174,0.848201646900333728],"hpluv":[270.815422264053723,167.854994169255662,62.128875020953032],"hsluv":[270.815422264053723,83.6558877567472905,62.128875020953032]},"#9988ff":{"lch":[63.0111734122257303,95.9388777898474387,269.8490772999765],"luv":[63.0111734122257303,-0.252712116882344406,-95.9385449554102],"rgb":[0.6,0.533333333333333326,1],"xyz":[0.399884287602683575,0.316003263117334898,0.986035835192115462],"hpluv":[269.8490772999765,193.204124490752207,63.0111734122257303],"hsluv":[269.8490772999765,99.9999999999985363,63.0111734122257303]},"#bb8800":{"lch":[60.0458653136574,74.8864062555341832,55.056379834278971],"luv":[60.0458653136574,42.8926945435799,61.3855894869476728],"rgb":[0.733333333333333282,0.533333333333333326,0],"xyz":[0.292968333781028434,0.281742815587005313,0.038952034563401107],"hpluv":[55.056379834278971,158.255644288368103,60.0458653136574],"hsluv":[55.056379834278971,100.000000000002288,60.0458653136574]},"#bb8811":{"lch":[60.0822560315187957,73.5298298886522161,54.5759299869630823],"luv":[60.0822560315187957,42.619621560372849,59.9183089005750773],"rgb":[0.733333333333333282,0.533333333333333326,0.0666666666666666657],"xyz":[0.293979999280665538,0.282147481786860166,0.0442801395281567367],"hpluv":[54.5759299869630823,155.294707870281485,60.0822560315187957],"hsluv":[54.5759299869630823,97.6878818876268866,60.0822560315187957]},"#bb8822":{"lch":[60.1496227929862499,71.0610449842002367,53.6488709316330343],"luv":[60.1496227929862499,42.1201645307819774,57.2325419158225586],"rgb":[0.733333333333333282,0.533333333333333326,0.133333333333333331],"xyz":[0.295855357419142595,0.282897625042250966,0.0541570257241359362],"hpluv":[53.6488709316330343,149.912555699661567,60.1496227929862499],"hsluv":[53.6488709316330343,93.46183514856709,60.1496227929862499]},"#bb8833":{"lch":[60.2602822329103844,67.1261991777524,52.0115270519145412],"luv":[60.2602822329103844,41.3163720666953154,52.9044801061090197],"rgb":[0.733333333333333282,0.533333333333333326,0.2],"xyz":[0.298943108151600301,0.284132725335234093,0.0704191795817470795],"hpluv":[52.0115270519145412,141.351435183858257,60.2602822329103844],"hsluv":[52.0115270519145412,86.668700859176,60.2602822329103844]},"#bb8844":{"lch":[60.4194844691316746,61.7320119628667285,49.374045706205429],"luv":[60.4194844691316746,40.1948297943626258,46.8531424643613406],"rgb":[0.733333333333333282,0.533333333333333326,0.266666666666666663],"xyz":[0.303401103395819216,0.285915923432921659,0.093897954534633718],"hpluv":[49.374045706205429,129.650066203053939,60.4194844691316746],"hsluv":[49.374045706205429,77.207197386412,60.4194844691316746]},"#bb8855":{"lch":[60.6313805732006585,55.0590005000961682,45.2500033552910708],"luv":[60.6313805732006585,38.7623447760430935,39.1020992215604082],"rgb":[0.733333333333333282,0.533333333333333326,0.333333333333333315],"xyz":[0.309363517435181201,0.28830088904866652,0.125300001808607619],"hpluv":[45.2500033552910708,115.231228654446355,60.6313805732006585],"hsluv":[45.2500033552910708,65.1528926573884775,60.6313805732006585]},"#bb8866":{"lch":[60.8992588402534949,47.519038379270981,38.7816256818529581],"luv":[60.8992588402534949,37.0429375720225096,29.7637327048519147],"rgb":[0.733333333333333282,0.533333333333333326,0.4],"xyz":[0.316948513208689386,0.291334887358069838,0.165247646215751581],"hpluv":[38.7816256818529581,99.0136254476035305,60.8992588402534949],"hsluv":[38.7816256818529581,50.7321114426118,60.8992588402534949]},"#bb8877":{"lch":[61.2256685857267087,39.8992767652027354,28.4701407483744688],"luv":[61.2256685857267087,35.0740840991728362,19.0200134329710231],"rgb":[0.733333333333333282,0.533333333333333326,0.466666666666666674],"xyz":[0.326262526228891292,0.295060492566150634,0.21430144812214949],"hpluv":[28.4701407483744688,82.6933933486175761,61.2256685857267087],"hsluv":[28.4701407483744688,34.2887258466009897,61.2256685857267087]},"#bb8888":{"lch":[61.6124959728340684,33.6595928281588499,12.1770506300623627],"luv":[61.6124959728340684,32.9022674846918903,7.09992843085263114],"rgb":[0.733333333333333282,0.533333333333333326,0.533333333333333326],"xyz":[0.337402943330835092,0.299516659406928232,0.272974311525721658],"hpluv":[12.1770506300623627,69.3233245158679,61.6124959728340684],"hsluv":[12.1770506300623627,26.8235367690150284,61.6124959728340684]},"#bb8899":{"lch":[62.0610184830546388,31.1131530731089718,349.361203223606594],"luv":[62.0610184830546388,30.5783355372127765,-5.74401339869088723],"rgb":[0.733333333333333282,0.533333333333333326,0.6],"xyz":[0.350459907439907847,0.304739445050557389,0.341740989166839593],"hpluv":[349.361203223606594,63.6157209963685091,62.0610184830546388],"hsluv":[349.361203223606594,28.7433747177793393,62.0610184830546388]},"#bb88aa":{"lch":[62.5719506337331097,34.1093853871598185,325.627133749050586],"luv":[62.5719506337331097,28.1532372220531357,-19.2573467904743723],"rgb":[0.733333333333333282,0.533333333333333326,0.66666666666666663],"xyz":[0.365517600405160348,0.310762522236658512,0.421044838783838193],"hpluv":[325.627133749050586,69.1725086761469328,62.5719506337331097],"hsluv":[325.627133749050586,30.7007491098828176,62.5719506337331097]},"#bb88bb":{"lch":[63.1454872588298,41.9701566603395477,307.715012949246614],"luv":[63.1454872588298,25.674586077978244,-33.2010493782640808],"rgb":[0.733333333333333282,0.533333333333333326,0.733333333333333282],"xyz":[0.382655192512497,0.31761755907959327,0.51130282388247994],"hpluv":[307.715012949246614,84.3407770596407,63.1454872588298],"hsluv":[307.715012949246614,32.6446535119142354,63.1454872588298]},"#bb88cc":{"lch":[63.7813474201422,52.7335158558763055,296.081540752202443],"luv":[63.7813474201422,23.1842810338905281,-47.3636232510092228],"rgb":[0.733333333333333282,0.533333333333333326,0.8],"xyz":[0.401947567956218776,0.325334509257082072,0.612909334552750096],"hpluv":[296.081540752202443,104.913738848088499,63.7813474201422],"hsluv":[296.081540752202443,48.441096457031712,63.7813474201422]},"#bb88dd":{"lch":[64.4788201663492089,64.9599578897110916,288.597779393076337],"luv":[64.4788201663492089,20.7171971389296665,-61.56779898404492],"rgb":[0.733333333333333282,0.533333333333333326,0.866666666666666696],"xyz":[0.423465893506852264,0.333941839477335578,0.726239182452756094],"hpluv":[288.597779393076337,127.840358269428478,64.4788201663492089],"hsluv":[288.597779393076337,64.9415793589177,64.4788201663492089]},"#bb88ee":{"lch":[65.2368122498474463,77.8541115668441392,283.595470556329246],"luv":[65.2368122498474463,18.300798164755431,-75.6726071600252084],"rgb":[0.733333333333333282,0.533333333333333326,0.933333333333333348],"xyz":[0.44727807377302703,0.343466711583805595,0.851649998521279317],"hpluv":[283.595470556329246,151.435657753316406,65.2368122498474463],"hsluv":[283.595470556329246,82.1213401763518789,65.2368122498474463]},"#bb88ff":{"lch":[66.0538972531437452,90.9819525935223652,280.100148709787334],"luv":[66.0538972531437452,15.9554396417090967,-89.5719802369565201],"rgb":[0.733333333333333282,0.533333333333333326,1],"xyz":[0.473449122182858395,0.353935130947738319,0.989484186813061162],"hpluv":[280.100148709787334,174.781769995450787,66.0538972531437452],"hsluv":[280.100148709787334,99.99999999999838,66.0538972531437452]},"#999900":{"lch":[61.2683639221826866,67.5422828804358772,85.8743202181747449],"luv":[61.2683639221826866,4.85929488236129092,67.3672563635114869],"rgb":[0.6,0.6,0],"xyz":[0.245273099653321058,0.295550148661537559,0.0441268830932777384],"hpluv":[85.8743202181747449,139.887458074797593,61.2683639221826866],"hsluv":[85.8743202181747449,100.000000000002359,61.2683639221826866]},"#999911":{"lch":[61.3036130280217861,66.0751339072958785,85.8743202181746881],"luv":[61.3036130280217861,4.75374160235953358,65.9039093047224185],"rgb":[0.6,0.6,0.0666666666666666657],"xyz":[0.24628476515295819,0.295954814861392412,0.0494549880580333681],"hpluv":[85.8743202181746881,136.770144995815713,61.3036130280217861],"hsluv":[85.8743202181746881,97.7715564197957718,61.3036130280217861]},"#999922":{"lch":[61.3688705786650104,63.38848415762304,85.8743202181745744],"luv":[61.3688705786650104,4.56045196477970372,63.2242216375825663],"rgb":[0.6,0.6,0.133333333333333331],"xyz":[0.248160123291435192,0.296704958116783213,0.0593318742540125676],"hpluv":[85.8743202181745744,131.069475710796667,61.3688705786650104],"hsluv":[85.8743202181745744,93.6963738669960691,61.3688705786650104]},"#999933":{"lch":[61.4760769955270945,59.0559618954583243,85.8743202181743754],"luv":[61.4760769955270945,4.24875087387181871,58.9029265097210484],"rgb":[0.6,0.6,0.2],"xyz":[0.251247874023892925,0.29794005840976634,0.0755940281116237178],"hpluv":[85.8743202181743754,121.898097720990123,61.4760769955270945],"hsluv":[85.8743202181743754,87.1401192062652683,61.4760769955270945]},"#999944":{"lch":[61.6303367515695,52.9921690524208,85.8743202181739775],"luv":[61.6303367515695,3.81249440942850049,52.8548471500809569],"rgb":[0.6,0.6,0.266666666666666663],"xyz":[0.255705869268111841,0.299723256507453906,0.0990728030645103563],"hpluv":[85.8743202181739775,109.10797160418339,61.6303367515695],"hsluv":[85.8743202181739775,77.9969649215058,61.6303367515695]},"#999955":{"lch":[61.8357003743425935,45.2147461889200173,85.8743202181734517],"luv":[61.8357003743425935,3.25295171251598125,45.0975784059909],"rgb":[0.6,0.6,0.333333333333333315],"xyz":[0.261668283307473826,0.302108222123198766,0.130474850338484272],"hpluv":[85.8743202181734517,92.7855058259100218,61.8357003743425935],"hsluv":[85.8743202181734517,66.3286810003367577,61.8357003743425935]},"#999966":{"lch":[62.0953945325949377,35.8293841981041083,85.874320218172457],"luv":[62.0953945325949377,2.57772666020637553,35.7365372872164784],"rgb":[0.6,0.6,0.4],"xyz":[0.269253279080982,0.305142220432602085,0.170422494745628206],"hpluv":[85.874320218172457,73.2182390722606,62.0953945325949377],"hsluv":[85.874320218172457,52.3408174542086542,62.0953945325949377]},"#999977":{"lch":[62.4119425079225749,25.0116267171883422,85.8743202181703],"luv":[62.4119425079225749,1.79944864939855109,24.9468125338318],"rgb":[0.6,0.6,0.466666666666666674],"xyz":[0.278567292101183916,0.30886782564068288,0.219476296652026115],"hpluv":[85.8743202181703,50.8526471570801775,62.4119425079225749],"hsluv":[85.8743202181703,36.3525421484824918,62.4119425079225749]},"#999988":{"lch":[62.7872374999600567,12.9853368609797517,85.8743202181639589],"luv":[62.7872374999600567,0.934223397011331502,12.9516871502363],"rgb":[0.6,0.6,0.533333333333333326],"xyz":[0.289707709203127717,0.313323992481460478,0.27814916005559831],"hpluv":[85.8743202181639589,26.2434647477884546,62.7872374999600567],"hsluv":[85.8743202181639589,18.7604129126121961,62.7872374999600567]},"#999999":{"lch":[63.2225945523589843,3.33307052034688283e-12,0],"luv":[63.2225945523589843,3.14807442966336163e-12,1.09498241031769098e-12],"rgb":[0.6,0.6,0.6],"xyz":[0.302764673312200472,0.318546778125089636,0.346915837696716189],"hpluv":[0,6.68977504875838914e-12,63.2225945523589843],"hsluv":[0,3.10313074237261963e-12,63.2225945523589843]},"#9999aa":{"lch":[63.7187933641432238,13.6904464527836414,265.874320218190064],"luv":[63.7187933641432238,-0.984952144759020598,-13.6549695477167123],"rgb":[0.6,0.6,0.66666666666666663],"xyz":[0.317822366277453,0.324569855311190758,0.42621968731371479],"hpluv":[265.874320218190064,27.2639887848552753,63.7187933641432238],"hsluv":[265.874320218190064,14.5770868731616492,63.7187933641432238]},"#9999bb":{"lch":[64.276118203606174,27.8450519356751158,265.874320218183641],"luv":[64.276118203606174,-2.00329797275791854,-27.7728953213882335],"rgb":[0.6,0.6,0.733333333333333282],"xyz":[0.334959958384789647,0.331424892154125517,0.516477672412356537],"hpluv":[265.874320218183641,54.9715165011475904,64.276118203606174],"hsluv":[265.874320218183641,30.0955931685464577,64.276118203606174]},"#9999cc":{"lch":[64.8943980299807635,42.2483295275786332,265.874320218181538],"luv":[64.8943980299807635,-3.03953438803302189,-42.138848804575062],"rgb":[0.6,0.6,0.8],"xyz":[0.354252333828511401,0.339141842331614318,0.618084183082626692],"hpluv":[265.874320218181538,82.6117192029769,64.8943980299807635],"hsluv":[265.874320218181538,46.4456834766813316,64.8943980299807635]},"#9999dd":{"lch":[65.5730481583578353,56.7175687031348,265.874320218180458],"luv":[65.5730481583578353,-4.08051637559571567,-56.570592941061804],"rgb":[0.6,0.6,0.866666666666666696],"xyz":[0.375770659379144889,0.347749172551867824,0.73141403098263269],"hpluv":[265.874320218180458,109.756831209262941,65.5730481583578353],"hsluv":[265.874320218180458,63.5568222493012627,65.5730481583578353]},"#9999ee":{"lch":[66.311113738117,71.1055788100052695,265.874320218179832],"luv":[66.311113738117,-5.11565437949467672,-70.9213185027987691],"rgb":[0.6,0.6,0.933333333333333348],"xyz":[0.399582839645319654,0.357274044658337842,0.856824847051155913],"hpluv":[265.874320218179832,136.068212717368169,66.311113738117],"hsluv":[265.874320218179832,81.4020980414818922,66.311113738117]},"#9999ff":{"lch":[67.1073146704137145,85.2999068143523829,265.874320218179378],"luv":[67.1073146704137145,-6.13685802391602486,-85.0788638624864149],"rgb":[0.6,0.6,1],"xyz":[0.425753888055151,0.367742464022270565,0.994659035342937758],"hpluv":[265.874320218179378,161.293929533565688,67.1073146704137145],"hsluv":[265.874320218179378,99.9999999999983,67.1073146704137145]},"#bb9900":{"lch":[64.4418646198176219,74.1135014806344117,66.2793330800256228],"luv":[64.4418646198176219,29.8142337654579457,67.8522112145111],"rgb":[0.733333333333333282,0.6,0],"xyz":[0.318837934233495934,0.333482016491941036,0.047575234714223362],"hpluv":[66.2793330800256228,145.938057142603384,64.4418646198176219],"hsluv":[66.2793330800256228,100.000000000002416,64.4418646198176219]},"#bb9911":{"lch":[64.4743890579801331,72.806990252212529,65.9899074816349],"luv":[64.4743890579801331,29.6249863695688624,66.5072778888794147],"rgb":[0.733333333333333282,0.6,0.0666666666666666657],"xyz":[0.319849599733133039,0.333886682691795889,0.0529033396789789917],"hpluv":[65.9899074816349,143.29306400111571,64.4743890579801331],"hsluv":[65.9899074816349,98.0367215419372542,64.4743890579801331]},"#bb9922":{"lch":[64.5346112536789,70.4161712525116599,65.4311312102869636],"luv":[64.5346112536789,29.2781077929992222,64.0408430450799671],"rgb":[0.733333333333333282,0.6,0.133333333333333331],"xyz":[0.321724957871610096,0.334636825947186689,0.0627802258749581843],"hpluv":[65.4311312102869636,138.458312961065701,64.5346112536789],"hsluv":[65.4311312102869636,94.4406497380354892,64.5346112536789]},"#bb9933":{"lch":[64.6335704733000398,66.566957174521292,64.4427367566729146],"luv":[64.6335704733000398,28.7178476591491503,60.0536844273559396],"rgb":[0.733333333333333282,0.6,0.2],"xyz":[0.324812708604067801,0.335871926240169816,0.0790423797325693345],"hpluv":[64.4427367566729146,130.689255588928205,64.6335704733000398],"hsluv":[64.4427367566729146,88.6394107340449153,64.6335704733000398]},"#bb9944":{"lch":[64.7760175449466828,61.1991201001527685,62.8442649593615386],"luv":[64.7760175449466828,27.9319304868930693,54.4530950480162872],"rgb":[0.733333333333333282,0.6,0.266666666666666663],"xyz":[0.329270703848286717,0.337655124337857382,0.102521154685455973],"hpluv":[62.8442649593615386,119.886494259466,64.7760175449466828],"hsluv":[62.8442649593615386,80.5164965868838607,64.7760175449466828]},"#bb9955":{"lch":[64.965753761967747,54.3684881208399204,60.3198292286154],"luv":[64.965753761967747,26.9209928274273445,47.235501963369849],"rgb":[0.733333333333333282,0.6,0.333333333333333315],"xyz":[0.335233117887648702,0.340040089953602243,0.133923201959429888],"hpluv":[60.3198292286154,106.194518749025775,64.965753761967747],"hsluv":[60.3198292286154,70.0945405394646883,64.965753761967747]},"#bb9966":{"lch":[65.2058459998609,46.2670609228556557,56.2614414361724258],"luv":[65.2058459998609,25.6969192719684436,38.4747874116176689],"rgb":[0.733333333333333282,0.6,0.4],"xyz":[0.342818113661156887,0.343074088263005561,0.17387084636657385],"hpluv":[56.2614414361724258,90.0377647384168,65.2058459998609],"hsluv":[56.2614414361724258,57.5177389460533064,65.2058459998609]},"#bb9977":{"lch":[65.4987393303808147,37.2952826673053153,49.3796729345645886],"luv":[65.4987393303808147,24.280853083902965,28.3086255892461871],"rgb":[0.733333333333333282,0.6,0.466666666666666674],"xyz":[0.352132126681358792,0.346799693471086357,0.222924648272971759],"hpluv":[49.3796729345645886,72.2537327682772172,65.4987393303808147],"hsluv":[49.3796729345645886,43.0302399575574199,65.4987393303808147]},"#bb9988":{"lch":[65.8463246780106601,28.3139420599436384,36.7022131699001193],"luv":[65.8463246780106601,22.7007755052872291,16.9220006628175241],"rgb":[0.733333333333333282,0.6,0.533333333333333326],"xyz":[0.363272543783302593,0.351255860311863954,0.281597511676543899],"hpluv":[36.7022131699001193,54.564242001153282,65.8463246780106601],"hsluv":[36.7022131699001193,26.9495650592517677,65.8463246780106601]},"#bb9999":{"lch":[66.2499853133799377,21.4719543680734333,12.1770506300627517],"luv":[66.2499853133799377,20.9888452793887552,4.52914983440691099],"rgb":[0.733333333333333282,0.6,0.6],"xyz":[0.376329507892375292,0.356478645955493112,0.350364189317661834],"hpluv":[12.1770506300627517,41.1268186121042731,66.2499853133799377],"hsluv":[12.1770506300627517,20.948078856310218,66.2499853133799377]},"#bb99aa":{"lch":[66.7106335886793715,21.0368039825091344,335.738246937474969],"luv":[66.7106335886793715,19.1787866223333445,-8.64414631375011489],"rgb":[0.733333333333333282,0.6,0.66666666666666663],"xyz":[0.391387200857627848,0.362501723141594234,0.429668038934660435],"hpluv":[335.738246937474969,40.0151105801343192,66.7106335886793715],"hsluv":[335.738246937474969,22.766845509204984,66.7106335886793715]},"#bb99bb":{"lch":[67.2287438260669887,28.2861274819753,307.715012949249171],"luv":[67.2287438260669887,17.3035955220760833,-22.3761165070024362],"rgb":[0.733333333333333282,0.6,0.733333333333333282],"xyz":[0.408524792964964523,0.369356759984529,0.519926024033302125],"hpluv":[307.715012949249171,53.3897422679679323,67.2287438260669887],"hsluv":[307.715012949249171,24.5905380245485432,67.2287438260669887]},"#bb99cc":{"lch":[67.8043844715017343,39.5775547497090656,292.889275489017223],"luv":[67.8043844715017343,15.393749985076461,-36.4611478338680044],"rgb":[0.733333333333333282,0.6,0.8],"xyz":[0.427817168408686221,0.377073710162017794,0.621532534703572281],"hpluv":[292.889275489017223,74.0679810995506642,67.8043844715017343],"hsluv":[292.889275489017223,42.0187450252511,67.8043844715017343]},"#bb99dd":{"lch":[68.4372510447458353,52.4777928084534082,284.879936967142157],"luv":[68.4372510447458353,13.4760025497769718,-50.718005612655638],"rgb":[0.733333333333333282,0.6,0.866666666666666696],"xyz":[0.449335493959319821,0.385681040382271301,0.734862382603578279],"hpluv":[284.879936967142157,97.3021261982971737,68.4372510447458353],"hsluv":[284.879936967142157,60.4059975969609724,68.4372510447458353]},"#bb99ee":{"lch":[69.1267004581107898,66.0165196934359244,280.096152477623832],"luv":[69.1267004581107898,11.5727364631099334,-64.9942508472035456],"rgb":[0.733333333333333282,0.6,0.933333333333333348],"xyz":[0.47314767422549453,0.395205912488741318,0.860273198672101502],"hpluv":[280.096152477623832,121.184234531677617,69.1267004581107898],"hsluv":[280.096152477623832,79.7264365589122548,69.1267004581107898]},"#bb99ff":{"lch":[69.8717866786541,79.7596884688517207,276.986638727898821],"luv":[69.8717866786541,9.70179942494587877,-79.1674364405365765],"rgb":[0.733333333333333282,0.6,1],"xyz":[0.499318722635325951,0.405674331852674042,0.998107386963883347],"hpluv":[276.986638727898821,144.850809586534439,69.8717866786541],"hsluv":[276.986638727898821,99.9999999999980247,69.8717866786541]},"#880000":{"lch":[27.3946073685119416,92.1289276169810876,12.1770506300617765],"luv":[27.3946073685119416,90.0560691570773,19.4330571920800175],"rgb":[0.533333333333333326,0,0],"xyz":[0.101531161901381561,0.0523520053554009795,0.00475927321412716],"hpluv":[12.1770506300617765,426.746789183125316,27.3946073685119416],"hsluv":[12.1770506300617765,100.000000000002245,27.3946073685119416]},"#880011":{"lch":[27.5061298630582485,89.4551794237446529,10.4692299831444977],"luv":[27.5061298630582485,87.9659862388495242,16.254672889999533],"rgb":[0.533333333333333326,0,0.0666666666666666657],"xyz":[0.10254282740101868,0.0527566715552558324,0.0100873781788827915],"hpluv":[10.4692299831444977,412.68181181873,27.5061298630582485],"hsluv":[10.4692299831444977,99.9999999999965,27.5061298630582485]},"#880022":{"lch":[27.711363673312789,85.0234292319238421,7.23413932290422057],"luv":[27.711363673312789,84.3466296586470463,10.7065206104973338],"rgb":[0.533333333333333326,0,0.133333333333333331],"xyz":[0.104418185539495709,0.0535068148106466537,0.0199642643748619876],"hpluv":[7.23413932290422057,389.331950846774873,27.711363673312789],"hsluv":[7.23413932290422057,99.9999999999967,27.711363673312789]},"#880033":{"lch":[28.0451389930846,79.0521177396887396,1.75350406004841131],"luv":[28.0451389930846,79.0150993176991392,2.41896650323101259],"rgb":[0.533333333333333326,0,0.2],"xyz":[0.107505936271953442,0.0547419151036297666,0.0362264182324731343],"hpluv":[1.75350406004841131,357.680479105960103,28.0451389930846],"hsluv":[1.75350406004841131,99.9999999999969589,28.0451389930846]},"#880044":{"lch":[28.5182895144164306,72.8806899851902585,353.674121255230034],"luv":[28.5182895144164306,72.4369406321056459,-8.0302306678086488],"rgb":[0.533333333333333326,0,0.266666666666666663],"xyz":[0.111963931516172316,0.0565251132013173396,0.0597051931853597728],"hpluv":[353.674121255230034,324.286096087098713,28.5182895144164306],"hsluv":[353.674121255230034,99.9999999999972857,28.5182895144164306]},"#880055":{"lch":[29.1358047874334787,68.1690091719341922,343.056201782139055],"luv":[29.1358047874334787,65.2098664506571453,-19.866734230132252],"rgb":[0.533333333333333326,0,0.333333333333333315],"xyz":[0.117926345555534315,0.0589100788170621725,0.0911072404593336743],"hpluv":[343.056201782139055,296.892542908362316,29.1358047874334787],"hsluv":[343.056201782139055,99.9999999999977405,29.1358047874334787]},"#880066":{"lch":[29.8977347275108087,66.3157421691867768,330.790160549998632],"luv":[29.8977347275108087,57.8829185884671915,-32.3627161272186115],"rgb":[0.533333333333333326,0,0.4],"xyz":[0.125511341329042486,0.0619440771264654841,0.13105488486647765],"hpluv":[330.790160549998632,281.460643767249167,29.8977347275108087],"hsluv":[330.790160549998632,99.9999999999981384,29.8977347275108087]},"#880077":{"lch":[30.8000475559674527,67.8890879971799,318.512376228514142],"luv":[30.8000475559674527,50.8556366042998462,-44.9736866918894549],"rgb":[0.533333333333333326,0,0.466666666666666674],"xyz":[0.13482535434924442,0.0656696823345463,0.180108686772875559],"hpluv":[318.512376228514142,279.697068124812404,30.8000475559674527],"hsluv":[318.512376228514142,99.9999999999984652,30.8000475559674527]},"#880088":{"lch":[31.8355421357531156,72.5162027692933862,307.715012949243601],"luv":[31.8355421357531156,44.3606514294377803,-57.3649045046986288],"rgb":[0.533333333333333326,0,0.533333333333333326],"xyz":[0.14596577145118822,0.0701258491753239,0.238781550176447727],"hpluv":[307.715012949243601,289.042783730483279,31.8355421357531156],"hsluv":[307.715012949243601,99.9999999999987921,31.8355421357531156]},"#880099":{"lch":[32.9947769935272675,79.3376809512942,299.026215263792551],"luv":[32.9947769935272675,38.4954159686478121,-69.3726932454703444],"rgb":[0.533333333333333326,0,0.6],"xyz":[0.159022735560260947,0.0753486348189530558,0.307548227817565634],"hpluv":[299.026215263792551,305.122076286487129,32.9947769935272675],"hsluv":[299.026215263792551,99.9999999999991189,32.9947769935272675]},"#8800aa":{"lch":[34.2669429307518527,87.5167556566874651,292.341813883439613],"luv":[34.2669429307518527,33.2678552259606306,-80.9470958672197298],"rgb":[0.533333333333333326,0,0.66666666666666663],"xyz":[0.174080428525513475,0.0813717120050541642,0.386852077434564234],"hpluv":[292.341813883439613,324.082197305514,34.2669429307518527],"hsluv":[292.341813883439613,99.9999999999993605,34.2669429307518527]},"#8800bb":{"lch":[35.6406160405817047,96.4510237672048589,287.271351738157534],"luv":[35.6406160405817047,28.6360630376966157,-92.1019862947753296],"rgb":[0.533333333333333326,0,0.733333333333333282],"xyz":[0.19121802063285015,0.0882267488479889228,0.477110062533206],"hpluv":[287.271351738157534,343.400533998367337,35.6406160405817047],"hsluv":[287.271351738157534,99.999999999999531,35.6406160405817047]},"#8800cc":{"lch":[37.1043554501127346,105.765919518381835,283.413875530142832],"luv":[37.1043554501127346,24.5359458020369345,-102.880596300606314],"rgb":[0.533333333333333326,0,0.8],"xyz":[0.210510396076571876,0.0959436990254777244,0.578716573203476137],"hpluv":[283.413875530142832,361.709723992276565,37.1043554501127346],"hsluv":[283.413875530142832,99.9999999999998,37.1043554501127346]},"#8800dd":{"lch":[38.6471386159700145,115.245648848701009,280.44740978906907],"luv":[38.6471386159700145,20.8978330626497737,-113.335079087825761],"rgb":[0.533333333333333326,0,0.866666666666666696],"xyz":[0.23202872162720542,0.104551029245731258,0.692046421103482134],"hpluv":[280.44740978906907,378.39598449622531,38.6471386159700145],"hsluv":[280.44740978906907,99.9999999999998863,38.6471386159700145]},"#8800ee":{"lch":[40.258648150966188,124.7713904223,278.13468614008417],"luv":[40.258648150966188,17.6552208530237813,-123.515962711485074],"rgb":[0.533333333333333326,0,0.933333333333333348],"xyz":[0.255840901893380157,0.114075901352201275,0.817457237172005358],"hpluv":[278.13468614008417,393.273926011730225,40.258648150966188],"hsluv":[278.13468614008417,99.9999999999999858,40.258648150966188]},"#8800ff":{"lch":[41.9294357887748674,134.280036872974534,276.305800055850909],"luv":[41.9294357887748674,14.7486383519278057,-133.467621426964229],"rgb":[0.533333333333333326,0,1],"xyz":[0.282011950303211578,0.124544320716133985,0.955291425463787203],"hpluv":[276.305800055850909,406.37947026199555,41.9294357887748674],"hsluv":[276.305800055850909,100.000000000000171,41.9294357887748674]},"#aa0000":{"lch":[35.0982840320529732,118.036634932245676,12.1770506300617765],"luv":[35.0982840320529732,115.380864984340803,24.8978549596859438],"rgb":[0.66666666666666663,0,0],"xyz":[0.165771937912151307,0.08547615548595483,0.00777055958963192815],"hpluv":[12.1770506300617765,426.746789183125145,35.0982840320529732],"hsluv":[12.1770506300617765,100.000000000002217,35.0982840320529732]},"#aa0011":{"lch":[35.178794604810534,115.883637018633408,11.1343823918443601],"luv":[35.178794604810534,113.702354404428164,22.3783808966644813],"rgb":[0.66666666666666663,0,0.0666666666666666657],"xyz":[0.166783603411788439,0.0858808216858096829,0.0130986645543875596],"hpluv":[11.1343823918443601,418.004049663923468,35.178794604810534],"hsluv":[11.1343823918443601,99.9999999999964473,35.178794604810534]},"#aa0022":{"lch":[35.327373324777,112.154849255399441,9.17432067350408431],"luv":[35.327373324777,110.720144559212301,17.8818287736043224],"rgb":[0.66666666666666663,0,0.133333333333333331],"xyz":[0.16865896155026544,0.0866309649412005,0.0229755507503667557],"hpluv":[9.17432067350408431,402.852473647417696,35.327373324777],"hsluv":[9.17432067350408431,99.9999999999965752,35.327373324777]},"#aa0033":{"lch":[35.5701485089931921,106.706281850707128,5.8788523359554592],"luv":[35.5701485089931921,106.14508040487398,10.9294323844098908],"rgb":[0.66666666666666663,0,0.2],"xyz":[0.171746712282723202,0.0878660652341836101,0.0392377046079779],"hpluv":[5.8788523359554592,380.665602767339294,35.5701485089931921],"hsluv":[5.8788523359554592,99.9999999999967741,35.5701485089931921]},"#aa0044":{"lch":[35.9166782648329104,100.198740700315142,1.0062433800652546],"luv":[35.9166782648329104,100.183288799466339,1.75962588401964615],"rgb":[0.66666666666666663,0,0.266666666666666663],"xyz":[0.176204707526942062,0.0896492633318711901,0.0627164795608645409],"hpluv":[1.0062433800652546,354.001763490246503,35.9166782648329104],"hsluv":[1.0062433800652546,99.999999999997,35.9166782648329104]},"#aa0055":{"lch":[36.3730398367095,93.6502679946689369,354.384147096436777],"luv":[36.3730398367095,93.2007806216229113,-9.16445235643848832],"rgb":[0.66666666666666663,0,0.333333333333333315],"xyz":[0.182167121566304047,0.092034228947616023,0.0941185268348384424],"hpluv":[354.384147096436777,326.714758289773386,36.3730398367095],"hsluv":[354.384147096436777,99.9999999999973,36.3730398367095]},"#aa0066":{"lch":[36.9423385777606228,88.2319659172366926,346.039412913085584],"luv":[36.9423385777606228,85.6257622699194,-21.2863488018886251],"rgb":[0.66666666666666663,0,0.4],"xyz":[0.189752117339812232,0.0950682272570193415,0.134066171241982418],"hpluv":[346.039412913085584,303.068568849792825,36.9423385777606228],"hsluv":[346.039412913085584,99.9999999999976126,36.9423385777606228]},"#aa0077":{"lch":[37.6250775946346891,84.9907340508927689,336.365700313169],"luv":[37.6250775946346891,77.8619577045834319,-34.0725757306260348],"rgb":[0.66666666666666663,0,0.466666666666666674],"xyz":[0.199066130360014137,0.0987938324651001509,0.183119973148380327],"hpluv":[336.365700313169,286.637826777930513,37.6250775946346891],"hsluv":[336.365700313169,99.9999999999979536,37.6250775946346891]},"#aa0088":{"lch":[38.4195160158879432,84.5572797483387717,326.161033183527252],"luv":[38.4195160158879432,70.2337789048813761,-47.0866208086656215],"rgb":[0.66666666666666663,0,0.533333333333333326],"xyz":[0.210206547461958,0.103249999305877749,0.241792836551952495],"hpluv":[326.161033183527252,279.279102381419364,38.4195160158879432],"hsluv":[326.161033183527252,99.9999999999982379,38.4195160158879432]},"#aa0099":{"lch":[39.3220484546604681,86.9871636461465272,316.374304421046759],"luv":[39.3220484546604681,62.9667468975572859,-60.0162929906574334],"rgb":[0.66666666666666663,0,0.6],"xyz":[0.223263511571030693,0.108472784949506906,0.310559514193070374],"hpluv":[316.374304421046759,280.710309296009257,39.3220484546604681],"hsluv":[316.374304421046759,99.9999999999986,39.3220484546604681]},"#aa00aa":{"lch":[40.3276007574525863,91.8597353001339627,307.715012949243601],"luv":[40.3276007574525863,56.1937545325413,-72.6668626056414411],"rgb":[0.66666666666666663,0,0.66666666666666663],"xyz":[0.238321204536283249,0.114495862135608,0.389863363810069],"hpluv":[307.715012949243601,289.042783730483393,40.3276007574525863],"hsluv":[307.715012949243601,99.9999999999988205,40.3276007574525863]},"#aa00bb":{"lch":[41.4300227805658849,98.5480850422065089,300.471226581677797],"luv":[41.4300227805658849,49.974285465742625,-84.9370111180893304],"rgb":[0.66666666666666663,0,0.733333333333333282],"xyz":[0.255458796643619868,0.121350898978542759,0.480121348908710721],"hpluv":[300.471226581677797,301.836908489583834,41.4300227805658849],"hsluv":[300.471226581677797,99.9999999999990621,41.4300227805658849]},"#aa00cc":{"lch":[42.6224565622471445,106.453892931925211,294.601049164416338],"luv":[42.6224565622471445,44.3164832708711813,-96.7909119228886681],"rgb":[0.66666666666666663,0,0.8],"xyz":[0.274751172087341622,0.129067849156031561,0.581727859578980877],"hpluv":[294.601049164416338,316.929304470761622,42.6224565622471445],"hsluv":[294.601049164416338,99.9999999999992895,42.6224565622471445]},"#aa00dd":{"lch":[43.8976622887243266,115.112632227118652,289.907671140995035],"luv":[43.8976622887243266,39.1964773946494063,-108.233794436426436],"rgb":[0.66666666666666663,0,0.866666666666666696],"xyz":[0.296269497637975165,0.137675179376285095,0.695057707478986875],"hpluv":[289.907671140995035,332.752186796280228,43.8976622887243266],"hsluv":[289.907671140995035,99.9999999999994174,43.8976622887243266]},"#aa00ee":{"lch":[45.2482911917969233,124.202454763835647,286.162342623679535],"luv":[45.2482911917969233,34.5729825657367655,-119.293581746345012],"rgb":[0.66666666666666663,0,0.933333333333333348],"xyz":[0.320081677904149875,0.14720005148275514,0.820468523547510098],"hpluv":[286.162342623679535,348.311106794177135,45.2482911917969233],"hsluv":[286.162342623679535,99.9999999999996589,45.2482911917969233]},"#aa00ff":{"lch":[46.667101462293175,133.514790614533382,283.159905061129905],"luv":[46.667101462293175,30.397247590160724,-130.008486845225434],"rgb":[0.66666666666666663,0,1],"xyz":[0.346252726313981296,0.157668470846687836,0.958302711839291943],"hpluv":[283.159905061129905,363.042841924949244,46.667101462293175],"hsluv":[283.159905061129905,99.9999999999998153,46.667101462293175]},"#881100":{"lch":[28.4751123640698864,88.1761994811112,13.8943544232398857],"luv":[28.4751123640698864,85.5961768878489124,21.1739617718743069],"rgb":[0.533333333333333326,0.0666666666666666657,0],"xyz":[0.103535562162309969,0.0563608058772578496,0.00542740663443661096],"hpluv":[13.8943544232398857,392.939109149716501,28.4751123640698864],"hsluv":[13.8943544232398857,100.000000000002331,28.4751123640698864]},"#881111":{"lch":[28.5813012406410962,85.6429421929893522,12.1770506300617782],"luv":[28.5813012406410962,83.7160154193071548,18.0649469909557752],"rgb":[0.533333333333333326,0.0666666666666666657,0.0666666666666666657],"xyz":[0.104547227661947087,0.0567654720771127,0.0107555115991922433],"hpluv":[12.1770506300617782,380.232213605760478,28.5813012406410962],"hsluv":[12.1770506300617782,89.1001931926906536,28.5813012406410962]},"#881122":{"lch":[28.776819878520115,81.4294437186752589,8.91447414891876377],"luv":[28.776819878520115,80.445837197505071,12.6183034487767767],"rgb":[0.533333333333333326,0.0666666666666666657,0.133333333333333331],"xyz":[0.106422585800424116,0.0575156153325035238,0.0206323977951714393],"hpluv":[8.91447414891876377,359.069069298387092,28.776819878520115],"hsluv":[8.91447414891876377,89.5522119422979,28.776819878520115]},"#881133":{"lch":[29.0950676619922959,75.7256767264573227,3.35964558590209394],"luv":[29.0950676619922959,75.5955308554484162,4.43777313107212557],"rgb":[0.533333333333333326,0.0666666666666666657,0.2],"xyz":[0.10951033653288185,0.0587507156254866367,0.0368945516527825826],"hpluv":[3.35964558590209394,330.265430862114329,29.0950676619922959],"hsluv":[3.35964558590209394,90.2199940579986475,29.0950676619922959]},"#881144":{"lch":[29.5467689283324617,69.8105982852884779,355.112641815866198],"luv":[29.5467689283324617,69.5567752183087435,-5.94765955478224484],"rgb":[0.533333333333333326,0.0666666666666666657,0.266666666666666663],"xyz":[0.113968331777100723,0.0605339137231742097,0.0603733266056692211],"hpluv":[355.112641815866198,299.81315922456514,29.5467689283324617],"hsluv":[355.112641815866198,91.0462468049379083,29.5467689283324617]},"#881155":{"lch":[30.1372440361953267,65.3247498846549,344.189828060851937],"luv":[30.1372440361953267,62.8534910411243928,-17.7977979378353552],"rgb":[0.533333333333333326,0.0666666666666666657,0.333333333333333315],"xyz":[0.119930745816462722,0.0629188793389190426,0.0917753738796431295],"hpluv":[344.189828060851937,275.05120204756264,30.1372440361953267],"hsluv":[344.189828060851937,91.9552565263170294,30.1372440361953267]},"#881166":{"lch":[30.8672249177773494,63.7021552959616173,331.50461515751158],"luv":[30.8672249177773494,55.9849923977636479,-30.3915319714745138],"rgb":[0.533333333333333326,0.0666666666666666657,0.4],"xyz":[0.127515741589970893,0.0659528776483223611,0.131723018286787091],"hpluv":[331.50461515751158,261.876101180723595,30.8672249177773494],"hsluv":[331.50461515751158,92.8754029221989299,30.8672249177773494]},"#881177":{"lch":[31.7336031237729514,65.5512880923603376,318.81152503011009],"luv":[31.7336031237729514,49.3304506912780525,-43.1680206305895737],"rgb":[0.533333333333333326,0.0666666666666666657,0.466666666666666674],"xyz":[0.136829754610172827,0.0696784828564031705,0.180776820193185],"hpluv":[318.81152503011009,262.120610410187965,31.7336031237729514],"hsluv":[318.81152503011009,93.7528273751248094,31.7336031237729514]},"#881188":{"lch":[32.7302234117729114,70.4946015177073377,307.715012949243658],"luv":[32.7302234117729114,43.1239685223607268,-55.7656900075914663],"rgb":[0.533333333333333326,0.0666666666666666657,0.533333333333333326],"xyz":[0.147970171712116627,0.0741346496971807684,0.239449683596757168],"hpluv":[307.715012949243658,273.304143969878908,32.7302234117729114],"hsluv":[307.715012949243658,94.5549099834012736,32.7302234117729114]},"#881199":{"lch":[33.8487030992268245,77.6382380820710836,298.861624073140206],"luv":[33.8487030992268245,37.4756592651389795,-67.9946393117248249],"rgb":[0.533333333333333326,0.0666666666666666657,0.6],"xyz":[0.161027135821189354,0.0793574353408099259,0.308216361237875103],"hpluv":[298.861624073140206,291.053592363859707,33.8487030992268245],"hsluv":[298.861624073140206,95.267111116431,33.8487030992268245]},"#8811aa":{"lch":[35.0792182273937954,86.1162494337963551,292.107316515455238],"luv":[35.0792182273937954,32.4092110837954692,-79.7850327659882623],"rgb":[0.533333333333333326,0.0666666666666666657,0.66666666666666663],"xyz":[0.176084828786441883,0.0853805125269110343,0.387520210854873703],"hpluv":[292.107316515455238,311.511817997128389,35.0792182273937954],"hsluv":[292.107316515455238,95.8874450484264571,35.0792182273937954]},"#8811bb":{"lch":[36.4111998559147381,95.3124415598142,287.019214731984619],"luv":[36.4111998559147381,27.8972267898096504,-91.1383906678789515],"rgb":[0.533333333333333326,0.0666666666666666657,0.733333333333333282],"xyz":[0.193222420893778557,0.0922355493698457929,0.47777819595351545],"hpluv":[287.019214731984619,332.16504711372977,36.4111998559147381],"hsluv":[287.019214731984619,96.4212920970111753,36.4111998559147381]},"#8811cc":{"lch":[37.8339039869932847,104.849214202707898,283.169050576302368],"luv":[37.8339039869932847,23.8872658659353974,-102.091900993053514],"rgb":[0.533333333333333326,0.0666666666666666657,0.8],"xyz":[0.212514796337500284,0.0999524995473346,0.579384706623785606],"hpluv":[283.169050576302368,351.660305548048939,37.8339039869932847],"hsluv":[283.169050576302368,96.8775739500717776,37.8339039869932847]},"#8811dd":{"lch":[39.3368423655390274,114.513051177954694,280.22024610097435],"luv":[39.3368423655390274,20.3183376083265372,-112.696069349906494],"rgb":[0.533333333333333326,0.0666666666666666657,0.866666666666666696],"xyz":[0.234033121888133827,0.108559829767588129,0.692714554523791604],"hpluv":[280.22024610097435,369.398236331583689,39.3368423655390274],"hsluv":[280.22024610097435,97.2663289333616348,39.3368423655390274]},"#8811ee":{"lch":[40.9100807353410261,124.189527798253138,277.928390028110698],"luv":[40.9100807353410261,17.130123009315259,-123.002429652583771],"rgb":[0.533333333333333326,0.0666666666666666657,0.933333333333333348],"xyz":[0.257845302154308564,0.118084701874058146,0.818125370592314827],"hpluv":[277.928390028110698,385.206818333834917,40.9100807353410261],"hsluv":[277.928390028110698,97.5973562787359867,40.9100807353410261]},"#8811ff":{"lch":[42.5444231432324926,133.820472646418182,276.120297984259253],"luv":[42.5444231432324926,14.2674470621751119,-133.057727523202459],"rgb":[0.533333333333333326,0.0666666666666666657,1],"xyz":[0.28401635056414,0.128553121237990842,0.955959558884096672],"hpluv":[276.120297984259253,399.134479754608662,42.5444231432324926],"hsluv":[276.120297984259253,99.9999999999993605,42.5444231432324926]},"#aa1100":{"lch":[35.8849415951509485,114.659477700983,13.2232466646238507],"luv":[35.8849415951509485,111.619416231509064,26.2278810962561089],"rgb":[0.66666666666666663,0.0666666666666666657,0],"xyz":[0.167776338173079714,0.0894849560078117,0.00843869300994137816],"hpluv":[13.2232466646238507,405.449754626827882,35.8849415951509485],"hsluv":[13.2232466646238507,100.000000000002245,35.8849415951509485]},"#aa1111":{"lch":[35.9630348414680086,112.584844162769954,12.1770506300617871],"luv":[35.9630348414680086,110.051736997450746,23.747890832642895],"rgb":[0.66666666666666663,0.0666666666666666657,0.0666666666666666657],"xyz":[0.168788003672716846,0.089889622207666553,0.0137667979746970096],"hpluv":[12.1770506300617871,397.249101663635656,35.9630348414680086],"hsluv":[12.1770506300617871,93.0877775141683514,35.9630348414680086]},"#aa1122":{"lch":[36.1071812157442409,108.986817867719594,10.2082214608018411],"luv":[36.1071812157442409,107.261576994584843,19.3152936702042],"rgb":[0.66666666666666663,0.0666666666666666657,0.133333333333333331],"xyz":[0.170663361811193848,0.0906397654630573674,0.0236436841706762074],"hpluv":[10.2082214608018411,383.018466712830786,36.1071812157442409],"hsluv":[10.2082214608018411,93.272361347425246,36.1071812157442409]},"#aa1133":{"lch":[36.3427932754706546,103.718469067724868,6.89182233030552727],"luv":[36.3427932754706546,102.969049051934846,12.4457126390109512],"rgb":[0.66666666666666663,0.0666666666666666657,0.2],"xyz":[0.173751112543651609,0.0918748657560404802,0.0399058380282873507],"hpluv":[6.89182233030552727,362.140519718911037,36.3427932754706546],"hsluv":[6.89182233030552727,93.5557024333493388,36.3427932754706546]},"#aa1144":{"lch":[36.6792659124992824,97.4113439982971698,1.97455903872184],"luv":[36.6792659124992824,97.3535035649005778,3.35637947697775552],"rgb":[0.66666666666666663,0.0666666666666666657,0.266666666666666663],"xyz":[0.178209107787870469,0.0936580638537280602,0.0633846129811739822],"hpluv":[1.97455903872184,336.99870087691761,36.6792659124992824],"hsluv":[1.97455903872184,93.9250914747756696,36.6792659124992824]},"#aa1155":{"lch":[37.1226754299384396,91.055498296574811,355.267689161716703],"luv":[37.1226754299384396,90.7450919896636634,-7.51212685096688926],"rgb":[0.66666666666666663,0.0666666666666666657,0.333333333333333315],"xyz":[0.184171521827232454,0.0960430294694729,0.0947866602551479],"hpluv":[355.267689161716703,311.247759321881176,37.1226754299384396],"hsluv":[355.267689161716703,94.3576556410013154,37.1226754299384396]},"#aa1166":{"lch":[37.6762679798416,85.8108023849569577,346.783206271719791],"luv":[37.6762679798416,83.5378398512485205,-19.6194576616503156],"rgb":[0.66666666666666663,0.0666666666666666657,0.4],"xyz":[0.191756517600740639,0.0990770277788762116,0.134734304662291859],"hpluv":[346.783206271719791,289.010360822200312,37.6762679798416],"hsluv":[346.783206271719791,94.8263018378468558,37.6762679798416]},"#aa1177":{"lch":[38.3408051028578285,82.7345113545946163,336.916515476294876],"luv":[38.3408051028578285,76.110335477500783,-32.4378822148708],"rgb":[0.66666666666666663,0.0666666666666666657,0.466666666666666674],"xyz":[0.201070530620942545,0.102802632986957021,0.183788106568689769],"hpluv":[336.916515476294876,273.819772016881302,38.3408051028578285],"hsluv":[336.916515476294876,95.3051408978498387,38.3408051028578285]},"#aa1188":{"lch":[39.1148927869010379,82.477083595297529,326.495944929629673],"luv":[39.1148927869010379,68.7732486834780445,-45.5270203714912753],"rgb":[0.66666666666666663,0.0666666666666666657,0.533333333333333326],"xyz":[0.212210947722886401,0.107258799827734619,0.242460969972261936],"hpluv":[326.495944929629673,267.565723971153261,39.1148927869010379],"hsluv":[326.495944929629673,95.7730681487448,39.1148927869010379]},"#aa1199":{"lch":[39.9953287808464424,85.1038505069809617,316.515705271857257],"luv":[39.9953287808464424,61.7482074461545665,-58.5647013848890552],"rgb":[0.66666666666666663,0.0666666666666666657,0.6],"xyz":[0.2252679118319591,0.112481585471363776,0.311227647613379843],"hpluv":[316.515705271857257,270.00963724100518,39.9953287808464424],"hsluv":[316.515705271857257,96.2151887572794,39.9953287808464424]},"#aa11aa":{"lch":[40.9774666162921406,90.1875437006381588,307.715012949243601],"luv":[40.9774666162921406,55.1708175083225498,-71.3440532505540261],"rgb":[0.66666666666666663,0.0666666666666666657,0.66666666666666663],"xyz":[0.240325604797211656,0.118504662657464871,0.390531497230378444],"hpluv":[307.715012949243601,279.28060733669264,40.9774666162921406],"hsluv":[307.715012949243601,96.6225842874192864,40.9774666162921406]},"#aa11bb":{"lch":[42.0555802442747719,97.087212137786878,300.384453602166161],"luv":[42.0555802442747719,49.106684193967304,-83.7523750598249705],"rgb":[0.66666666666666663,0.0666666666666666657,0.733333333333333282],"xyz":[0.257463196904548275,0.12535969950039963,0.48078948232902019],"hpluv":[300.384453602166161,292.939359498794147,42.0555802442747719],"hsluv":[300.384453602166161,96.9911870522472697,42.0555802442747719]},"#aa11cc":{"lch":[43.2232098485165395,105.192683835863036,294.469145625450437],"luv":[43.2232098485165395,43.5711419401365,-95.7447456658814247],"rgb":[0.66666666666666663,0.0666666666666666657,0.8],"xyz":[0.276755572348270029,0.133076649677888431,0.582395992999290346],"hpluv":[294.469145625450437,308.821726609797679,43.2232098485165395],"hsluv":[294.469145625450437,97.3204020480748255,43.2232098485165395]},"#aa11dd":{"lch":[44.4734721926781518,114.032755796715193,289.757274940509092],"luv":[44.4734721926781518,38.5472014936014489,-107.320001172218454],"rgb":[0.66666666666666663,0.0666666666666666657,0.866666666666666696],"xyz":[0.298273897898903573,0.141683979898141965,0.695725840899296344],"hpluv":[289.757274940509092,325.362808980276498,44.4734721926781518],"hsluv":[289.757274940509092,97.611854654502622,44.4734721926781518]},"#aa11ee":{"lch":[45.7993244881172,123.283094749558884,286.008743686799619],"luv":[45.7993244881172,33.9995108001485136,-118.502129585840351],"rgb":[0.66666666666666663,0.0666666666666666657,0.933333333333333348],"xyz":[0.322086078165078282,0.151208852004612,0.821136656967819567],"hpluv":[286.008743686799619,341.573194884792258,45.7993244881172],"hsluv":[286.008743686799619,97.8684161167955153,45.7993244881172]},"#aa11ff":{"lch":[47.1937769411101868,132.735165800167636,283.011169167098501],"luv":[47.1937769411101868,29.8841269734434611,-129.327348983241365],"rgb":[0.66666666666666663,0.0666666666666666657,1],"xyz":[0.348257126574909703,0.161677271368544706,0.958970845259601412],"hpluv":[283.011169167098501,356.89510187446183,47.1937769411101868],"hsluv":[283.011169167098501,99.9999999999993321,47.1937769411101868]},"#882200":{"lch":[30.3496916993887922,81.7292062801124786,17.2000641303745212],"luv":[30.3496916993887922,78.0741152618852254,24.1680716080335465],"rgb":[0.533333333333333326,0.133333333333333331,0],"xyz":[0.107251185897077911,0.0637920533467938311,0.00666594787935922105],"hpluv":[17.2000641303745212,341.713647377264522,30.3496916993887922],"hsluv":[17.2000641303745212,100.000000000002359,30.3496916993887922]},"#882211":{"lch":[30.4474919309639347,79.3871193031655338,15.4743840495427136],"luv":[30.4474919309639347,76.5093230935452908,21.181080969170015],"rgb":[0.533333333333333326,0.133333333333333331,0.0666666666666666657],"xyz":[0.108262851396715029,0.064196719546648684,0.0119940528441148525],"hpluv":[15.4743840495427136,330.855109199112462,30.4474919309639347],"hsluv":[15.4743840495427136,90.2707057474005,30.4474919309639347]},"#882222":{"lch":[30.6277058928754826,75.4670009360781648,12.1770506300618102],"luv":[30.6277058928754826,73.7690281561900321,15.9185022906448506],"rgb":[0.533333333333333326,0.133333333333333331,0.133333333333333331],"xyz":[0.110138209535192058,0.0649468628020395,0.0218709390400940486],"hpluv":[12.1770506300618102,312.666930334371557,30.6277058928754826],"hsluv":[12.1770506300618102,73.2675530922876277,30.6277058928754826]},"#882233":{"lch":[30.9214262019897674,70.1124269978110135,6.50693872003014],"luv":[30.9214262019897674,69.6607724946186266,7.94538828354938],"rgb":[0.533333333333333326,0.133333333333333331,0.2],"xyz":[0.113225960267649792,0.0661819630950226112,0.0381330928977051953],"hpluv":[6.50693872003014,287.723152758323693,30.9214262019897674],"hsluv":[6.50693872003014,74.8097141082451458,30.9214262019897674]},"#882244":{"lch":[31.3391119188553589,64.5127804411509,357.965654494967],"luv":[31.3391119188553589,64.472119792654567,-2.29011128326569047],"rgb":[0.533333333333333326,0.133333333333333331,0.266666666666666663],"xyz":[0.117683955511868665,0.0679651611927101912,0.0616118678505918338],"hpluv":[357.965654494967,261.215173773686786,31.3391119188553589],"hsluv":[357.965654494967,76.7464797952550839,31.3391119188553589]},"#882255":{"lch":[31.8864840032734449,60.2907989794282599,346.464164292394514],"luv":[31.8864840032734449,58.6161449596217778,-14.1112717942230379],"rgb":[0.533333333333333326,0.133333333333333331,0.333333333333333315],"xyz":[0.123646369551230664,0.0703501268084550241,0.0930139151245657353],"hpluv":[346.464164292394514,239.929545755427228,31.8864840032734449],"hsluv":[346.464164292394514,78.9147131880069566,31.8864840032734449]},"#882266":{"lch":[32.565220274416383,58.9629659850378189,332.945096803324191],"luv":[32.565220274416383,52.5107124518348698,-26.8189566455043362],"rgb":[0.533333333333333326,0.133333333333333331,0.4],"xyz":[0.131231365324738836,0.0733841251178583426,0.132961559531709711],"hpluv":[332.945096803324191,229.754818264706444,32.565220274416383],"hsluv":[332.945096803324191,81.1505919454384923,32.565220274416383]},"#882277":{"lch":[33.3735533542235316,61.2256527964903086,319.411642653163199],"luv":[33.3735533542235316,46.4949769025583564,-39.8346291960518499],"rgb":[0.533333333333333326,0.133333333333333331,0.466666666666666674],"xyz":[0.140545378344940741,0.077109730325939152,0.18201536143810762],"hpluv":[319.411642653163199,232.79320602780777,33.3735533542235316],"hsluv":[319.411642653163199,83.3222334130424116,33.3735533542235316]},"#882288":{"lch":[34.3068967831130962,66.691064714973308,307.715012949243771],"luv":[34.3068967831130962,40.7972144472485709,-52.7568517461189046],"rgb":[0.533333333333333326,0.133333333333333331,0.533333333333333326],"xyz":[0.151685795446884597,0.0815658971667167498,0.240688224841679788],"hpluv":[307.715012949243771,246.675229855048,34.3068967831130962],"hsluv":[307.715012949243771,85.3421167175917,34.3068967831130962]},"#882299":{"lch":[35.3585028262625087,74.3915148492043699,298.539568373309862],"luv":[35.3585028262625087,35.5417034620318262,-65.3520068289902554],"rgb":[0.533333333333333326,0.133333333333333331,0.6],"xyz":[0.164742759555957297,0.0867886828103459074,0.309454902482797667],"hpluv":[298.539568373309862,266.973934190138948,35.3585028262625087],"hsluv":[298.539568373309862,87.1641407220543556,35.3585028262625087]},"#8822aa":{"lch":[36.5201138266519365,83.3993900511107142,291.653660077047903],"luv":[36.5201138266519365,30.7739719973965897,-77.5140045953036463],"rgb":[0.533333333333333326,0.133333333333333331,0.66666666666666663],"xyz":[0.179800452521209853,0.092811759996447,0.388758752099796268],"hpluv":[291.653660077047903,289.781114528802732,36.5201138266519365],"hsluv":[291.653660077047903,88.7734689989794532,36.5201138266519365]},"#8822bb":{"lch":[37.7825623664262196,93.0686060696910857,286.53575696187113],"luv":[37.7825623664262196,26.4885972028076893,-89.2195026548722],"rgb":[0.533333333333333326,0.133333333333333331,0.733333333333333282],"xyz":[0.196938044628546471,0.0996667968393817605,0.479016737198438],"hpluv":[286.53575696187113,312.57276028475934,37.7825623664262196],"hsluv":[286.53575696187113,90.1753980157506874,37.7825623664262196]},"#8822cc":{"lch":[39.1362858369643476,103.012880313051866,282.702767559286599],"luv":[39.1362858369643476,22.6518448072254479,-100.491529181421669],"rgb":[0.533333333333333326,0.133333333333333331,0.8],"xyz":[0.216230420072268226,0.107383747016870562,0.580623247868708225],"hpluv":[282.702767559286599,334.003678399645651,39.1362858369643476],"hsluv":[282.702767559286599,91.3862929083300628,39.1362858369643476]},"#8822dd":{"lch":[40.5717373677475379,113.020349666590874,279.789793007972776],"luv":[40.5717373677475379,19.2172965257181652,-111.374570495248548],"rgb":[0.533333333333333326,0.133333333333333331,0.866666666666666696],"xyz":[0.237748745622901769,0.115991077237124096,0.693953095768714223],"hpluv":[279.789793007972776,353.486121759760863,40.5717373677475379],"hsluv":[279.789793007972776,92.4273238443811209,40.5717373677475379]},"#8822ee":{"lch":[42.0796906219744145,122.982600065668066,277.538986095624125],"luv":[42.0796906219744145,16.1354126197819454,-121.919515986988074],"rgb":[0.533333333333333326,0.133333333333333331,0.933333333333333348],"xyz":[0.261560925889076534,0.125515949343594141,0.819363911837237446],"hpluv":[277.538986095624125,370.860397035002336,42.0796906219744145],"hsluv":[277.538986095624125,93.320628909539181,42.0796906219744145]},"#8822ff":{"lch":[43.6514473624058752,132.848943476626658,275.771185477405766],"luv":[43.6514473624058752,13.3587518063908028,-132.175585994657865],"rgb":[0.533333333333333326,0.133333333333333331,1],"xyz":[0.2877319742989079,0.135984368707526837,0.957198100129019291],"hpluv":[275.771185477405766,386.188007357759091,43.6514473624058752],"hsluv":[275.771185477405766,99.9999999999994,43.6514473624058752]},"#ffaa00":{"lch":[76.0766826449234799,103.646966048157225,46.9849230608437125],"luv":[76.0766826449234799,70.7070052858721,75.7839889059127785],"rgb":[1,0.66666666666666663,0],"xyz":[0.556131758114240538,0.500120923568095,0.0672444716650198171],"hpluv":[46.9849230608437125,173.218766512771339,76.0766826449234799],"hsluv":[46.9849230608437125,100.0000000000028,76.0766826449234799]},"#ffaa11":{"lch":[76.1015101579349533,102.726050652762069,46.6846637022245687],"luv":[76.1015101579349533,70.4714195905473275,74.7423608377930719],"rgb":[1,0.66666666666666663,0.0666666666666666657],"xyz":[0.557143423613877697,0.500525589767949919,0.0725725766297754538],"hpluv":[46.6846637022245687,171.896872437304751,76.1015101579349533],"hsluv":[46.6846637022245687,100.000000000002771,76.1015101579349533]},"#ffaa22":{"lch":[76.1474983763177,101.038792737361192,46.1176753587789605],"luv":[76.1474983763177,70.0380208569547591,72.8252241483965719],"rgb":[1,0.66666666666666663,0.133333333333333331],"xyz":[0.559018781752354643,0.501275733023340719,0.0824494628257546464],"hpluv":[46.1176753587789605,169.470349592440897,76.1474983763177],"hsluv":[46.1176753587789605,100.0000000000028,76.1474983763177]},"#ffaa33":{"lch":[76.2231174741888395,98.3169656691378577,45.1538509265191337],"luv":[76.2231174741888395,69.3336656617894533,69.7070193329597885],"rgb":[1,0.66666666666666663,0.2],"xyz":[0.56210653248481246,0.502510833316323846,0.0987116166833657827],"hpluv":[45.1538509265191337,165.543337136749699,76.2231174741888395],"hsluv":[45.1538509265191337,100.000000000002927,76.2231174741888395]},"#ffaa44":{"lch":[76.3320756204529118,94.5107446089494516,43.6926927141772694],"luv":[76.3320756204529118,68.3364903114306514,65.2870962629968119],"rgb":[1,0.66666666666666663,0.266666666666666663],"xyz":[0.56656452772903132,0.504294031414011412,0.122190391636252421],"hpluv":[43.6926927141772694,160.025535099593441,76.3320756204529118],"hsluv":[43.6926927141772694,100.000000000003,76.3320756204529118]},"#ffaa55":{"lch":[76.4774026026215,89.6489515946998807,41.6012791226812411],"luv":[76.4774026026215,67.0379860163697288,59.5217855318358247],"rgb":[1,0.66666666666666663,0.333333333333333315],"xyz":[0.572526941768393249,0.506678997029756162,0.153592438910226337],"hpluv":[41.6012791226812411,152.933128718005122,76.4774026026215],"hsluv":[41.6012791226812411,100.000000000003,76.4774026026215]},"#ffaa66":{"lch":[76.6616205587261,83.8466863985853905,38.6944265301345354],"luv":[76.6616205587261,65.4416029270402788,52.418159318716242],"rgb":[1,0.66666666666666663,0.4],"xyz":[0.58011193754190149,0.50971299533915948,0.193540083317370298],"hpluv":[38.6944265301345354,144.405277715469396,76.6616205587261],"hsluv":[38.6944265301345354,100.000000000003197,76.6616205587261]},"#ffaa77":{"lch":[76.8868341725165,77.3210793721064533,34.7099370327462324],"luv":[76.8868341725165,63.5614296008349058,44.0283315873505927],"rgb":[1,0.66666666666666663,0.466666666666666674],"xyz":[0.58942595056210334,0.513438600547240331,0.242593885223768208],"hpluv":[34.7099370327462324,134.738986801151128,76.8868341725165],"hsluv":[34.7099370327462324,100.000000000003354,76.8868341725165]},"#ffaa88":{"lch":[77.1547840912050873,70.4186738688562741,29.282319230158226],"luv":[77.1547840912050873,61.4205932059712723,34.4427112706728948],"rgb":[1,0.66666666666666663,0.533333333333333326],"xyz":[0.600566367664047251,0.517894767388017874,0.301266748627340375],"hpluv":[29.282319230158226,124.451835787871019,77.1547840912050873],"hsluv":[29.282319230158226,100.000000000003638,77.1547840912050873]},"#ffaa99":{"lch":[77.466881654564645,63.658531214354845,21.9370110659791244],"luv":[77.466881654564645,59.0493431107362383,23.7820031654092716],"rgb":[1,0.66666666666666663,0.6],"xyz":[0.613623331773119896,0.523117553031647087,0.370033426268458254],"hpluv":[21.9370110659791244,114.385173247539697,77.466881654564645],"hsluv":[21.9370110659791244,100.000000000003624,77.466881654564645]},"#ffaaaa":{"lch":[77.8242336850598,57.783013099698,12.1770506300621957],"luv":[77.8242336850598,56.482921905318662,12.1883606739193855],"rgb":[1,0.66666666666666663,0.66666666666666663],"xyz":[0.628681024738372507,0.529140630217748154,0.44933727588545691],"hpluv":[12.1770506300621957,105.841692205508735,77.8242336850598],"hsluv":[12.1770506300621957,100.000000000003837,77.8242336850598]},"#ffaabb":{"lch":[78.227662021793833,53.7597014753195,359.804273109779956],"luv":[78.227662021793833,53.7593877986938224,-0.183647012281759531],"rgb":[1,0.66666666666666663,0.733333333333333282],"xyz":[0.64581861684570907,0.535995667060682912,0.539595260984098601],"hpluv":[359.804273109779956,100.661858044669231,78.227662021793833],"hsluv":[359.804273109779956,100.000000000004135,78.227662021793833]},"#ffaacc":{"lch":[78.6777204654413254,52.5946111834740293,345.492217824016791],"luv":[78.6777204654413254,50.9175596191628586,-13.1755549397286202],"rgb":[1,0.66666666666666663,0.8],"xyz":[0.665110992289430825,0.543712617238171769,0.641201771654368757],"hpluv":[345.492217824016791,100.966318741741958,78.6777204654413254],"hsluv":[345.492217824016791,100.000000000004306,78.6777204654413254]},"#ffaadd":{"lch":[79.1747106956411244,54.8892328831665353,330.97422205899818],"luv":[79.1747106956411244,47.9952274547753177,-26.6324243745640352],"rgb":[1,0.66666666666666663,0.866666666666666696],"xyz":[0.686629317840064424,0.552319947458425275,0.754531619554374755],"hpluv":[330.97422205899818,108.36723319715793,79.1747106956411244],"hsluv":[330.97422205899818,100.000000000004704,79.1747106956411244]},"#ffaaee":{"lch":[79.718698064048283,60.5009523664383337,318.094564198374599],"luv":[79.718698064048283,45.0277239197174595,-40.4087777080146822],"rgb":[1,0.66666666666666663,0.933333333333333348],"xyz":[0.710441498106239133,0.561844819564895293,0.879942435622898],"hpluv":[318.094564198374599,123.247069988098687,79.718698064048283],"hsluv":[318.094564198374599,100.000000000004945,79.718698064048283]},"#ffaaff":{"lch":[80.3095277487323074,68.733917080261989,307.715012949245647],"luv":[80.3095277487323074,42.0468973903394,-54.3728772187255203],"rgb":[1,0.66666666666666663,1],"xyz":[0.736612546516070554,0.572313238928828,1.01777662391468],"hpluv":[307.715012949245647,144.979279509576116,80.3095277487323074],"hsluv":[307.715012949245647,100.000000000005301,80.3095277487323074]},"#aa2200":{"lch":[37.2831780533064929,108.910935722579069,15.2092016225530191],"luv":[37.2831780533064929,105.096262108869439,28.5721474641225],"rgb":[0.66666666666666663,0.133333333333333331,0],"xyz":[0.171491961907847656,0.0969162034773476816,0.00967723425486398912],"hpluv":[15.2092016225530191,370.67892165569458,37.2831780533064929],"hsluv":[15.2092016225530191,100.000000000002217,37.2831780533064929]},"#aa2211":{"lch":[37.3572350214345619,106.95640568906397,14.158492547926917],"luv":[37.3572350214345619,103.707370245599336,26.1620732103901794],"rgb":[0.66666666666666663,0.133333333333333331,0.0666666666666666657],"xyz":[0.172503627407484789,0.0973208696772025345,0.0150053392196196206],"hpluv":[14.158492547926917,363.305022455593,37.3572350214345619],"hsluv":[14.158492547926917,93.5777596020973732,37.3572350214345619]},"#aa2222":{"lch":[37.4939757158163331,103.55828406892158,12.1770506300617907],"luv":[37.4939757158163331,101.228270350344232,21.8438888748564],"rgb":[0.66666666666666663,0.133333333333333331,0.133333333333333331],"xyz":[0.17437898554596179,0.0980710129325933488,0.0248822254155988166],"hpluv":[12.1770506300617907,350.479546677114797,37.4939757158163331],"hsluv":[12.1770506300617907,82.128221128038831,37.4939757158163331]},"#aa2233":{"lch":[37.7176061419824791,98.5638584928897359,8.82735266140639],"luv":[37.7176061419824791,97.3963926843060506,15.125372494284127],"rgb":[0.66666666666666663,0.133333333333333331,0.2],"xyz":[0.177466736278419523,0.0993061132255764617,0.0411443792732099634],"hpluv":[8.82735266140639,331.598763076121088,37.7176061419824791],"hsluv":[8.82735266140639,82.8309253801110401,37.7176061419824791]},"#aa2244":{"lch":[38.0372287502177358,92.5577577991589209,3.83362915136278648],"luv":[38.0372287502177358,92.350650343690134,6.18836892123021798],"rgb":[0.66666666666666663,0.133333333333333331,0.266666666666666663],"xyz":[0.181924731522638411,0.101089311323264042,0.0646231542260966],"hpluv":[3.83362915136278648,308.775819843535,38.0372287502177358],"hsluv":[3.83362915136278648,83.7532195801290698,38.0372287502177358]},"#aa2255":{"lch":[38.4588905236098242,86.4856662057644172,356.973865768881865],"luv":[38.4588905236098242,86.3650670431288603,-4.56570407393633459],"rgb":[0.66666666666666663,0.133333333333333331,0.333333333333333315],"xyz":[0.187887145562000424,0.103474276939008875,0.0960252015000705],"hpluv":[356.973865768881865,285.355803984318584,38.4588905236098242],"hsluv":[356.973865768881865,84.8422496447961,38.4588905236098242]},"#aa2266":{"lch":[38.9860395203518237,81.4924101374980268,348.227712846231327],"luv":[38.9860395203518237,79.7783138580272464,-16.6262908668256522],"rgb":[0.66666666666666663,0.133333333333333331,0.4],"xyz":[0.195472141335508581,0.106508275248412193,0.135972845907214479],"hpluv":[348.227712846231327,265.245100213362434,38.9860395203518237],"hsluv":[348.227712846231327,86.0332228090823747,38.9860395203518237]},"#aa2277":{"lch":[39.619833929041036,78.6591168776988354,337.990195281021442],"luv":[39.619833929041036,72.9264197190586572,-29.4787037526953952],"rgb":[0.66666666666666663,0.133333333333333331,0.466666666666666674],"xyz":[0.204786154355710515,0.110233880456493,0.185026647813612388],"hpluv":[337.990195281021442,251.92759566726761,39.619833929041036],"hsluv":[337.990195281021442,87.2621982611374278,39.619833929041036]},"#aa2288":{"lch":[40.3594266716885386,78.6767938432408,327.148786779116733],"luv":[40.3594266716885386,66.0949636498191637,-42.6789605025813046],"rgb":[0.66666666666666663,0.133333333333333331,0.533333333333333326],"xyz":[0.215926571457654315,0.1146900472972706,0.243699511217184556],"hpluv":[327.148786779116733,247.366561360315984,40.3594266716885386],"hsluv":[327.148786779116733,88.4751585260979283,40.3594266716885386]},"#aa2299":{"lch":[41.2022629883412748,81.6302410017403162,316.790315261789033],"luv":[41.2022629883412748,59.4964385039503,-55.8898027492302489],"rgb":[0.66666666666666663,0.133333333333333331,0.6],"xyz":[0.228983535566727042,0.119912832940899758,0.312466188858302463],"hpluv":[316.790315261789033,251.402351399829286,41.2022629883412748],"hsluv":[316.790315261789033,89.6322731802278554,41.2022629883412748]},"#aa22aa":{"lch":[42.1443943233873242,87.0780915666379229,307.715012949243715],"luv":[42.1443943233873242,53.2686588598376076,-68.8842798769212834],"rgb":[0.66666666666666663,0.133333333333333331,0.66666666666666663],"xyz":[0.24404122853197957,0.125935910127000866,0.391770038475301063],"hpluv":[307.715012949243715,262.185344504614818,42.1443943233873242],"hsluv":[307.715012949243715,90.7081440057972515,42.1443943233873242]},"#aa22bb":{"lch":[43.1807973125030387,94.3504956605328573,300.218008125398399],"luv":[43.1807973125030387,47.4858085519839577,-81.5298351375283801],"rgb":[0.66666666666666663,0.133333333333333331,0.733333333333333282],"xyz":[0.261178820639316245,0.132790946969935625,0.48202802357394281],"hpluv":[300.218008125398399,277.263598469343151,43.1807973125030387],"hsluv":[300.218008125398399,91.6896384965505291,43.1807973125030387]},"#aa22cc":{"lch":[44.3056820912093627,102.813108633557576,294.217612554784239],"luv":[44.3056820912093627,42.1742864712375294,-93.764944769021767],"rgb":[0.66666666666666663,0.133333333333333331,0.8],"xyz":[0.280471196083037944,0.140507897147424426,0.583634534244213],"hpluv":[294.217612554784239,294.461411899371626,44.3056820912093627],"hsluv":[294.217612554784239,92.5728114271618097,44.3056820912093627]},"#aa22dd":{"lch":[45.5127751844210451,111.980933923074502,289.471886144522102],"luv":[45.5127751844210451,37.3282042805071441,-105.576203414769125],"rgb":[0.66666666666666663,0.133333333333333331,0.866666666666666696],"xyz":[0.301989521633671543,0.14911522736767796,0.696964382144219],"hpluv":[289.471886144522102,312.212361278410071,45.5127751844210451],"hsluv":[289.471886144522102,93.3598993754704622,45.5127751844210451]},"#aa22ee":{"lch":[46.7955661660676938,121.524022862348417,285.718434714393425],"luv":[46.7955661660676938,32.9220942551774698,-116.979587289842115],"rgb":[0.66666666666666663,0.133333333333333331,0.933333333333333348],"xyz":[0.325801701899846252,0.158640099474147978,0.822375198212742187],"hpluv":[285.718434714393425,329.531365671141714,46.7955661660676938],"hsluv":[285.718434714393425,94.0568560040361348,46.7955661660676938]},"#aa22ff":{"lch":[48.1475121680676921,131.233078667623346,282.730941389390409],"luv":[48.1475121680676921,28.9202258451599548,-128.006802450680539],"rgb":[0.66666666666666663,0.133333333333333331,1],"xyz":[0.351972750309677673,0.169108518838080701,0.960209386504524],"hpluv":[282.730941389390409,345.866733454918517,48.1475121680676921],"hsluv":[282.730941389390409,99.9999999999992,48.1475121680676921]},"#883300":{"lch":[33.1414787667816597,73.2165592554870841,22.9600016117944072],"luv":[33.1414787667816597,67.4161530625393084,28.5609323282788417],"rgb":[0.533333333333333326,0.2,0],"xyz":[0.113368907986088716,0.076027497524815621,0.00870518857569610102],"hpluv":[22.9600016117944072,280.334636286210525,33.1414787667816597],"hsluv":[22.9600016117944072,100.000000000002245,33.1414787667816597]},"#883311":{"lch":[33.2285118286029402,71.0572306739368287,21.2511434941679589],"luv":[33.2285118286029402,66.2253841559006844,25.7551650053430592],"rgb":[0.533333333333333326,0.2,0.0666666666666666657],"xyz":[0.114380573485725834,0.0764321637246704738,0.0140332935404517325],"hpluv":[21.2511434941679589,271.354302974885854,33.2285118286029402],"hsluv":[21.2511434941679589,91.7325092821930355,33.2285118286029402]},"#883322":{"lch":[33.389038834633638,67.403958774683133,17.9527330699243208],"luv":[33.389038834633638,64.1221355411587126,20.7760774002325519],"rgb":[0.533333333333333326,0.2,0.133333333333333331],"xyz":[0.116255931624202863,0.0771823069800612882,0.0239101797364309268],"hpluv":[17.9527330699243208,256.165602847605,33.389038834633638],"hsluv":[17.9527330699243208,77.1564226992543354,33.389038834633638]},"#883333":{"lch":[33.6510932573449324,62.3280121609532785,12.1770506300618564],"luv":[33.6510932573449324,60.9256605799824484,13.1470522486492083],"rgb":[0.533333333333333326,0.2,0.2],"xyz":[0.119343682356660596,0.0784174072730444,0.04017233359404207],"hpluv":[12.1770506300618564,235.030067027939708,33.6510932573449324],"hsluv":[12.1770506300618564,55.0748296144977,33.6510932573449324]},"#883344":{"lch":[34.0246284162643136,56.9037682047597428,3.23132728809417369],"luv":[34.0246284162643136,56.8132965471078748,3.20751794249172528],"rgb":[0.533333333333333326,0.2,0.266666666666666663],"xyz":[0.12380167760087947,0.080200605370731981,0.0636511085469287086],"hpluv":[3.23132728809417369,212.220318588139889,34.0246284162643136],"hsluv":[3.23132728809417369,58.083398150148156,34.0246284162643136]},"#883355":{"lch":[34.5156618951709859,52.7529688087382524,350.767304332875],"luv":[34.5156618951709859,52.0695471674777721,-8.46392201697997137],"rgb":[0.533333333333333326,0.2,0.333333333333333315],"xyz":[0.129764091640241469,0.0825855709864768139,0.0950531558209026239],"hpluv":[350.767304332875,193.941176615342812,34.5156618951709859],"hsluv":[350.767304332875,61.5291535706030714,34.5156618951709859]},"#883366":{"lch":[35.1268460128593318,51.5738240122621576,335.705329263136434],"luv":[35.1268460128593318,47.0065260432940519,-21.2189969741481157],"rgb":[0.533333333333333326,0.2,0.4],"xyz":[0.137349087413749654,0.0856195692958801324,0.135000800228046586],"hpluv":[335.705329263136434,186.307141954737205,35.1268460128593318],"hsluv":[335.705329263136434,65.1713869724459869,35.1268460128593318]},"#883377":{"lch":[35.8579115963162849,54.2604071268956929,320.552373035814298],"luv":[35.8579115963162849,41.9001950458929713,-34.4755773946224],"rgb":[0.533333333333333326,0.2,0.466666666666666674],"xyz":[0.14666310043395156,0.0893451745039609418,0.184054602134444495],"hpluv":[320.552373035814298,192.015984070736607,35.8579115963162849],"hsluv":[320.552373035814298,68.798738775930957,35.8579115963162849]},"#883388":{"lch":[36.7061150242973682,60.4128011412536097,307.715012949244056],"luv":[36.7061150242973682,36.9565850245806402,-47.7903480323547711],"rgb":[0.533333333333333326,0.2,0.533333333333333326],"xyz":[0.157803517535895388,0.0938013413447385397,0.242727465538016662],"hpluv":[307.715012949244056,208.847787272345244,36.7061150242973682],"hsluv":[307.715012949244056,72.2549736675254479,36.7061150242973682]},"#883399":{"lch":[37.6667130487112445,68.9141309342046213,297.955533412138379],"luv":[37.6667130487112445,32.3059919210196611,-60.8726566564636684],"rgb":[0.533333333333333326,0.2,0.6],"xyz":[0.170860481644968087,0.0990241269883677,0.311494143179134542],"hpluv":[297.955533412138379,232.161331922109071,37.6667130487112445],"hsluv":[297.955533412138379,75.4431641885032604,37.6667130487112445]},"#8833aa":{"lch":[38.7334497692602824,78.7159372772915162,290.848124870138179],"luv":[38.7334497692602824,28.0143748789862741,-73.5621749378178436],"rgb":[0.533333333333333326,0.2,0.66666666666666663],"xyz":[0.185918174610220643,0.105047204174468806,0.390797992796133142],"hpluv":[290.848124870138179,257.878905714084965,38.7334497692602824],"hsluv":[290.848124870138179,78.3166072053086282,38.7334497692602824]},"#8833bb":{"lch":[39.8990272727434743,89.1142300257769193,285.691107407551272],"luv":[39.8990272727434743,24.1010351313191435,-85.7932753698447073],"rgb":[0.533333333333333326,0.2,0.733333333333333282],"xyz":[0.20305576671755729,0.111902241017403564,0.481055977894774889],"hpluv":[285.691107407551272,283.415812953045702,39.8990272727434743],"hsluv":[285.691107407551272,80.8649153118493444,39.8990272727434743]},"#8833cc":{"lch":[41.1555326498064318,99.7027359993818578,281.897918690971494],"luv":[41.1555326498064318,20.5555775093346966,-97.560769774639283],"rgb":[0.533333333333333326,0.2,0.8],"xyz":[0.222348142161279017,0.119619191194892366,0.5826624885650451],"hpluv":[281.897918690971494,307.410130900702256,41.1555326498064318],"hsluv":[281.897918690971494,83.1006969987668356,41.1555326498064318]},"#8833dd":{"lch":[42.4948021164729042,110.266949448261684,279.053462184119098],"luv":[42.4948021164729042,17.3511661022166734,-108.893237510502843],"rgb":[0.533333333333333326,0.2,0.866666666666666696],"xyz":[0.24386646771191256,0.1282265214151459,0.695992336465051098],"hpluv":[279.053462184119098,329.267506795456711,42.4948021164729042],"hsluv":[279.053462184119098,85.0491409670088103,42.4948021164729042]},"#8833ee":{"lch":[43.9087129541284185,120.703343561802441,276.877390721452173],"luv":[43.9087129541284185,14.4536316684396038,-119.834843341123431],"rgb":[0.533333333333333326,0.2,0.933333333333333348],"xyz":[0.267678647978087325,0.137751393521615917,0.821403152533574321],"hpluv":[276.877390721452173,348.825254458779511,43.9087129541284185],"hsluv":[276.877390721452173,90.7214777394212177,43.9087129541284185]},"#8833ff":{"lch":[45.3894029264418037,130.969293653816607,275.181129330284705],"luv":[45.3894029264418037,11.8271264623124566,-130.43417864894198],"rgb":[0.533333333333333326,0.2,1],"xyz":[0.293849696387918691,0.148219812885548641,0.959237340825356166],"hpluv":[275.181129330284705,366.146040402293636,45.3894029264418037],"hsluv":[275.181129330284705,99.9999999999993179,45.3894029264418037]},"#ffbb00":{"lch":[80.0686585320779614,99.7432534700870832,55.1804439586775146],"luv":[80.0686585320779614,56.9527800089419642,81.8846595037867928],"rgb":[1,0.733333333333333282,0],"xyz":[0.590086256022839262,0.568029919385293569,0.0785626376345524291],"hpluv":[55.1804439586775146,207.400278899961506,80.0686585320779614],"hsluv":[55.1804439586775146,100.000000000004661,80.0686585320779614]},"#ffbb11":{"lch":[80.0914663159454,98.8408701901197304,54.9443852376931758],"luv":[80.0914663159454,56.7713575947746136,80.9106332739172558],"rgb":[1,0.733333333333333282,0.0666666666666666657],"xyz":[0.591097921522476422,0.568434585585148477,0.0838907425993080658],"hpluv":[54.9443852376931758,205.801067903733326,80.0914663159454],"hsluv":[54.9443852376931758,100.000000000004746,80.0914663159454]},"#ffbb22":{"lch":[80.1337172522408849,97.1832370837577173,54.4980435584705063],"luv":[80.1337172522408849,56.4372945908461148,79.11645435270664],"rgb":[1,0.733333333333333282,0.133333333333333331],"xyz":[0.592973279660953367,0.569184728840539278,0.0937676287952872584],"hpluv":[54.4980435584705063,202.856099527528187,80.1337172522408849],"hsluv":[54.4980435584705063,100.00000000000469,80.1337172522408849]},"#ffbb33":{"lch":[80.2032020182086569,94.4967793168558643,53.7374633716008248],"luv":[80.2032020182086569,55.8935303906135772,76.1941898161015558],"rgb":[1,0.733333333333333282,0.2],"xyz":[0.596061030393411184,0.570419829133522405,0.110029782652898395],"hpluv":[53.7374633716008248,198.062979410590685,80.2032020182086569],"hsluv":[53.7374633716008248,100.000000000004576,80.2032020182086569]},"#ffbb44":{"lch":[80.3033451682561,90.7120739700224874,52.5796541788809932],"luv":[80.3033451682561,55.1219083097901148,72.0434284874749835],"rgb":[1,0.733333333333333282,0.266666666666666663],"xyz":[0.60051902563763,0.57220302723121,0.133508557605785033],"hpluv":[52.5796541788809932,191.266883403483888,80.3033451682561],"hsluv":[52.5796541788809932,100.000000000004732,80.3033451682561]},"#ffbb55":{"lch":[80.4369584648287343,85.824023801574981,50.9113466162360169],"luv":[80.4369584648287343,54.1139445833304435,66.614143116349922],"rgb":[1,0.733333333333333282,0.333333333333333315],"xyz":[0.606481439676992,0.57458799284695472,0.164910604879758949],"hpluv":[50.9113466162360169,182.412290123698938,80.4369584648287343],"hsluv":[50.9113466162360169,100.000000000004846,80.4369584648287343]},"#ffbb66":{"lch":[80.6063993458739532,79.8959983704508545,48.5678239065140147],"luv":[80.6063993458739532,52.8698192869345931,59.9011916757752374],"rgb":[1,0.733333333333333282,0.4],"xyz":[0.614066435450500214,0.577621991156358,0.20485824928690291],"hpluv":[48.5678239065140147,171.553596694327723,80.6063993458739532],"hsluv":[48.5678239065140147,100.000000000004945,80.6063993458739532]},"#ffbb77":{"lch":[80.8136549908608828,73.0715029906331353,45.3006510664637219],"luv":[80.8136549908608828,51.397517928562209,51.9397699272266067],"rgb":[1,0.733333333333333282,0.466666666666666674],"xyz":[0.623380448470702064,0.58134759636443889,0.253912051193300847],"hpluv":[45.3006510664637219,158.885762577352,80.8136549908608828],"hsluv":[45.3006510664637219,100.000000000005144,80.8136549908608828]},"#ffbb88":{"lch":[81.0603921498240823,65.5982066090816,40.7273497798256443],"luv":[81.0603921498240823,49.7118280718431649,42.8002203275083914],"rgb":[1,0.733333333333333282,0.533333333333333326],"xyz":[0.634520865572646,0.585803763205216432,0.312584914596873],"hpluv":[40.7273497798256443,144.809180382553194,81.0603921498240823],"hsluv":[40.7273497798256443,100.0000000000054,81.0603921498240823]},"#ffbb99":{"lch":[81.347989327564818,57.8755898316684423,34.2609783084776538],"luv":[81.347989327564818,47.8331272298795724,32.5818329406692087],"rgb":[1,0.733333333333333282,0.6],"xyz":[0.64757782968171862,0.591026548848845645,0.381351592237990866],"hpluv":[34.2609783084776538,130.060421106466862,81.347989327564818],"hsluv":[34.2609783084776538,100.000000000005514,81.347989327564818]},"#ffbbaa":{"lch":[81.6775593509345725,50.5427251754064883,25.056975338279841],"luv":[81.6775593509345725,45.7860019440054415,21.405819165362459],"rgb":[1,0.733333333333333282,0.66666666666666663],"xyz":[0.662635522646971231,0.597049626034946712,0.460655441854989522],"hpluv":[25.056975338279841,115.96007016550756,81.6775593509345725],"hsluv":[25.056975338279841,100.000000000005954,81.6775593509345725]},"#ffbbbb":{"lch":[82.0499666293022,44.6012959670408264,12.1770506300623094],"luv":[82.0499666293022,43.5977873399533138,9.40789779917804125],"rgb":[1,0.733333333333333282,0.733333333333333282],"xyz":[0.679773114754307795,0.603904662877881471,0.550913426953631213],"hpluv":[12.1770506300623094,104.793068167285782,82.0499666293022],"hsluv":[12.1770506300623094,100.000000000006168,82.0499666293022]},"#ffbbcc":{"lch":[82.4658415859876,41.4263127316455169,355.474040938847054],"luv":[82.4658415859876,41.2971323922898534,-3.26898190783910625],"rgb":[1,0.733333333333333282,0.8],"xyz":[0.699065490198029549,0.611621613055370328,0.652519937623901369],"hpluv":[355.474040938847054,100.004408983591958,82.4658415859876],"hsluv":[355.474040938847054,100.000000000006509,82.4658415859876]},"#ffbbdd":{"lch":[82.9255937413379,42.2590852539423381,337.045135839305544],"luv":[82.9255937413379,38.9126886018087319,-16.4812909773061058],"rgb":[1,0.733333333333333282,0.866666666666666696],"xyz":[0.720583815748663148,0.620228943275623834,0.765849785523907367],"hpluv":[337.045135839305544,105.181583048825317,82.9255937413379],"hsluv":[337.045135839305544,100.000000000006992,82.9255937413379]},"#ffbbee":{"lch":[83.4294243398036315,47.2826492521794748,320.476273654572083],"luv":[83.4294243398036315,36.4719970678196077,-30.0905691237235686],"rgb":[1,0.733333333333333282,0.933333333333333348],"xyz":[0.744395996014837857,0.629753815382093851,0.89126060159243059],"hpluv":[320.476273654572083,121.793886282549721,83.4294243398036315],"hsluv":[320.476273654572083,100.000000000007375,83.4294243398036315]},"#ffbbff":{"lch":[83.9773390427358493,55.5806936350452148,307.715012949246614],"luv":[83.9773390427358493,34.0006189291922496,-43.9678452665650781],"rgb":[1,0.733333333333333282,1],"xyz":[0.770567044424669279,0.640222234746026575,1.02909478988421244],"hpluv":[307.715012949246614,148.765749509941259,83.9773390427358493],"hsluv":[307.715012949246614,100.00000000000793,83.9773390427358493]},"#aa3300":{"lch":[39.4372171279304595,100.717281062042773,18.6056676884160446],"luv":[39.4372171279304595,95.4534790945485838,32.1341568004684959],"rgb":[0.66666666666666663,0.2,0],"xyz":[0.177609683996858475,0.109151647655369471,0.0117164749512008691],"hpluv":[18.6056676884160446,324.068678498457416,39.4372171279304595],"hsluv":[18.6056676884160446,100.00000000000226,39.4372171279304595]},"#aa3311":{"lch":[39.5056415087576553,98.9056898290362199,17.5539290045831464],"luv":[39.5056415087576553,94.2999972712563,29.8302865423477179],"rgb":[0.66666666666666663,0.2,0.0666666666666666657],"xyz":[0.178621349496495607,0.109556313855224324,0.0170445799159565023],"hpluv":[17.5539290045831464,317.688492914835,39.5056415087576553],"hsluv":[17.5539290045831464,94.2489803369173558,39.5056415087576553]},"#aa3322":{"lch":[39.6320377265530155,95.7434230458582789,15.5633947922033684],"luv":[39.6320377265530155,92.2329117474569102,25.688383505468213],"rgb":[0.66666666666666663,0.2,0.133333333333333331],"xyz":[0.180496707634972609,0.110306457110615139,0.0269214661119356949],"hpluv":[15.5633947922033684,306.55039093266322,39.6320377265530155],"hsluv":[15.5633947922033684,83.9544037116345123,39.6320377265530155]},"#aa3333":{"lch":[39.8389046640011415,91.0660982539230162,12.1770506300618351],"luv":[39.8389046640011415,89.0171529654153488,19.2088711049089049],"rgb":[0.66666666666666663,0.2,0.2],"xyz":[0.18358445836743037,0.111541557403598252,0.0431836199695468381],"hpluv":[12.1770506300618351,290.060550373943784,39.8389046640011415],"hsluv":[12.1770506300618351,67.9701775681036366,39.8389046640011415]},"#aa3344":{"lch":[40.1348956250933142,85.3951569144799691,7.07959423789482756],"luv":[40.1348956250933142,84.7440953003879116,10.5247867516366789],"rgb":[0.66666666666666663,0.2,0.266666666666666663],"xyz":[0.18804245361164923,0.113324755501285832,0.0666623949224334766],"hpluv":[7.07959423789482756,269.991710008472637,40.1348956250933142],"hsluv":[7.07959423789482756,69.5294395234689659,40.1348956250933142]},"#aa3355":{"lch":[40.5259588707466,79.61783322386637,359.98582832830067],"luv":[40.5259588707466,79.6178307884273693,-0.0196928603108827253],"rgb":[0.66666666666666663,0.2,0.333333333333333315],"xyz":[0.194004867651011215,0.115709721117030664,0.098064442196407392],"hpluv":[359.98582832830067,249.296614878837403,40.5259588707466],"hsluv":[359.98582832830067,71.3920974276079079,40.5259588707466]},"#aa3366":{"lch":[41.0157539995587683,74.8661602955128558,350.806369630673316],"luv":[41.0157539995587683,73.9044320805857,-11.9614746682639641],"rgb":[0.66666666666666663,0.2,0.4],"xyz":[0.2015898634245194,0.118743719426433983,0.138012086603551354],"hpluv":[350.806369630673316,231.619002340768361,41.0157539995587683],"hsluv":[350.806369630673316,73.4562780141226597,41.0157539995587683]},"#aa3377":{"lch":[41.6059173351841167,72.2728884464882668,339.921631600955322],"luv":[41.6059173351841167,67.8804264173117531,-24.8116527825629412],"rgb":[0.66666666666666663,0.2,0.466666666666666674],"xyz":[0.210903876444721305,0.122469324634514792,0.187065888509949263],"hpluv":[339.921631600955322,220.424384448141751,41.6059173351841167],"hsluv":[339.921631600955322,75.6166865764569138,41.6059173351841167]},"#aa3388":{"lch":[42.296293156356171,72.6091331125239918,328.324027314743319],"luv":[42.296293156356171,61.7926517819245191,-38.1281313089174],"rgb":[0.66666666666666663,0.2,0.533333333333333326],"xyz":[0.222044293546665161,0.12692549147529239,0.24573875191352143],"hpluv":[328.324027314743319,217.83530631356345,42.296293156356171],"hsluv":[328.324027314743319,77.7798333688196237,42.296293156356171]},"#aa3399":{"lch":[43.0851702185186838,75.9982874692542794,317.281450106519685],"luv":[43.0851702185186838,55.835561651799118,-51.5570533805758799],"rgb":[0.66666666666666663,0.2,0.6],"xyz":[0.235101257655737861,0.132148277118921548,0.314505429554639337],"hpluv":[317.281450106519685,223.828466633931441,43.0851702185186838],"hsluv":[317.281450106519685,79.8726039522015441,43.0851702185186838]},"#aa33aa":{"lch":[43.9695321467927229,81.9717995507402861,307.715012949243828],"luv":[43.9695321467927229,50.1449646844158607,-64.8448798162617521],"rgb":[0.66666666666666663,0.2,0.66666666666666663],"xyz":[0.250158950620990417,0.138171354305022642,0.393809279171637938],"hpluv":[307.715012949243828,236.565795567314069,43.9695321467927229],"hsluv":[307.715012949243828,81.8445603498948628,43.9695321467927229]},"#aa33bb":{"lch":[44.9453163823579231,89.8045713491188593,299.92695612823394],"luv":[44.9453163823579231,44.8030997809084,-77.8302208992172],"rgb":[0.66666666666666663,0.2,0.733333333333333282],"xyz":[0.267296542728327036,0.145026391147957401,0.484067264270279685],"hpluv":[299.92695612823394,253.543994890590483,44.9453163823579231],"hsluv":[299.92695612823394,83.6659201788580305,44.9453163823579231]},"#aa33cc":{"lch":[46.0076707905516145,98.8168321279874249,293.782406660809556],"luv":[46.0076707905516145,39.8493034294229815,-90.425656359246986],"rgb":[0.66666666666666663,0.2,0.8],"xyz":[0.28658891817204879,0.152743341325446202,0.585673774940549841],"hpluv":[293.782406660809556,272.546121846523647,46.0076707905516145],"hsluv":[293.782406660809556,85.323407183561244,46.0076707905516145]},"#aa33dd":{"lch":[47.1511962508372804,108.497592079042477,288.982430572084695],"luv":[47.1511962508372804,35.2919015872993782,-102.597315604759743],"rgb":[0.66666666666666663,0.2,0.866666666666666696],"xyz":[0.308107243722682334,0.161350671545699736,0.699003622840555838],"hpluv":[288.982430572084695,291.989148013150611,47.1511962508372804],"hsluv":[288.982430572084695,86.8156421407842771,47.1511962508372804]},"#aa33ee":{"lch":[48.3701654903461247,118.505438755587946,285.224041333998457],"luv":[48.3701654903461247,31.1188262963008349,-114.346655677352729],"rgb":[0.66666666666666663,0.2,0.933333333333333348],"xyz":[0.331919423988857099,0.170875543652169781,0.824414438909079061],"hpluv":[285.224041333998457,310.885191630161273,48.3701654903461247],"hsluv":[285.224041333998457,89.3017266962351215,48.3701654903461247]},"#aa33ff":{"lch":[49.6587116356326135,128.627945638670809,282.256374557143658],"luv":[49.6587116356326135,27.305962831451918,-125.696192436653462],"rgb":[0.66666666666666663,0.2,1],"xyz":[0.358090472398688464,0.181343963016102477,0.962248627200860907],"hpluv":[282.256374557143658,328.684490794403757,49.6587116356326135],"hsluv":[282.256374557143658,99.9999999999991616,49.6587116356326135]},"#884400":{"lch":[36.685747441671559,64.5393704655657814,31.8123524502136021],"luv":[36.685747441671559,54.844205976921188,34.0212200082916922],"rgb":[0.533333333333333326,0.266666666666666663,0],"xyz":[0.122201478469054756,0.093692638490747937,0.0116493787366846978],"hpluv":[31.8123524502136021,223.237258003095718,36.685747441671559],"hsluv":[31.8123524502136021,100.000000000002245,36.685747441671559]},"#884411":{"lch":[36.7614898568215622,62.4864851085245405,30.2058215583615599],"luv":[36.7614898568215622,54.0023006087270119,31.4374354899146],"rgb":[0.533333333333333326,0.266666666666666663,0.0666666666666666657],"xyz":[0.123213143968691874,0.0940973046906027899,0.016977483701440331],"hpluv":[30.2058215583615599,215.691146343233044,36.7614898568215622],"hsluv":[30.2058215583615599,93.206231917801162,36.7614898568215622]},"#884422":{"lch":[36.9013237077522405,58.9550348899468375,27.0578850153250627],"luv":[36.9013237077522405,52.5022536375556825,26.8180816214102],"rgb":[0.533333333333333326,0.266666666666666663,0.133333333333333331],"xyz":[0.125088502107168903,0.0948474479459936,0.0268543698974195236],"hpluv":[27.0578850153250627,202.730122118290922,36.9013237077522405],"hsluv":[27.0578850153250627,81.1214838671569822,36.9013237077522405]},"#884433":{"lch":[37.1299605496210461,53.9021323280231357,21.3870333458705382],"luv":[37.1299605496210461,50.1903435049312,19.6562785990819577],"rgb":[0.533333333333333326,0.266666666666666663,0.2],"xyz":[0.128176252839626637,0.0960825482389767171,0.0431165237550306668],"hpluv":[21.3870333458705382,184.213216030002656,37.1299605496210461],"hsluv":[21.3870333458705382,62.5495295304316272,37.1299605496210461]},"#884444":{"lch":[37.4566279620983806,48.2433933618324389,12.1770506300619505],"luv":[37.4566279620983806,47.157939219998795,10.1761373608750691],"rgb":[0.533333333333333326,0.266666666666666663,0.266666666666666663],"xyz":[0.132634248083845524,0.0978657463366643,0.0665952987079173],"hpluv":[12.1770506300619505,163.436290620587812,37.4566279620983806],"hsluv":[12.1770506300619505,38.2981887065726,37.4566279620983806]},"#884455":{"lch":[37.8873893879065804,43.5926938841600631,358.470393497241901],"luv":[37.8873893879065804,43.5771602849424937,-1.16364151632571566],"rgb":[0.533333333333333326,0.266666666666666663,0.333333333333333315],"xyz":[0.138596662123207509,0.10025071195240913,0.0979973459818912207],"hpluv":[358.470393497241901,146.001848007416612,37.8873893879065804],"hsluv":[358.470393497241901,42.6300163695831245,37.8873893879065804]},"#884466":{"lch":[38.425613648805637,41.9872152878557756,340.815857604715632],"luv":[38.425613648805637,39.6555541095459816,-13.7972198610307206],"rgb":[0.533333333333333326,0.266666666666666663,0.4],"xyz":[0.146181657896715694,0.103284710261812449,0.137944990389035183],"hpluv":[340.815857604715632,138.655016432961,38.425613648805637],"hsluv":[340.815857604715632,47.3337116796912767,38.425613648805637]},"#884477":{"lch":[39.0722986805963117,44.7783217464764363,322.645837420375756],"luv":[39.0722986805963117,35.5943002873345478,-27.1688035343115608],"rgb":[0.533333333333333326,0.266666666666666663,0.466666666666666674],"xyz":[0.1554956709169176,0.107010315469893258,0.186998792295433092],"hpluv":[322.645837420375756,145.424700121889231,39.0722986805963117],"hsluv":[322.645837420375756,52.1510622716411,39.0722986805963117]},"#884488":{"lch":[39.8263740522966856,51.5911203050634839,307.71501294924451],"luv":[39.8263740522966856,31.5600599218946343,-40.8118403414738609],"rgb":[0.533333333333333326,0.266666666666666663,0.533333333333333326],"xyz":[0.166636088018861428,0.111466482310670856,0.245671655699005259],"hpluv":[307.71501294924451,164.377933698467302,39.8263740522966856],"hsluv":[307.71501294924451,56.8697587177058,39.8263740522966856]},"#884499":{"lch":[40.6850187117946192,61.0189075542410393,296.969581150574243],"luv":[40.6850187117946192,27.6731358367265301,-54.3829443123034295],"rgb":[0.533333333333333326,0.266666666666666663,0.6],"xyz":[0.179693052127934128,0.116689267954300013,0.314438333340123166],"hpluv":[296.969581150574243,190.313341437332326,40.6850187117946192],"hsluv":[296.969581150574243,61.3383523001243134,40.6850187117946192]},"#8844aa":{"lch":[41.643995358805,71.7935926347933417,289.536616505740085],"luv":[41.643995358805,24.0084388126510113,-67.6602897510042141],"rgb":[0.533333333333333326,0.266666666666666663,0.66666666666666663],"xyz":[0.194750745093186683,0.122712345140401108,0.393742182957121767],"hpluv":[289.536616505740085,218.762371943685849,41.643995358805],"hsluv":[289.536616505740085,65.4647221474276506,41.643995358805]},"#8844bb":{"lch":[42.6979882651059626,83.1164887776927515,284.352541737053286],"luv":[42.6979882651059626,20.6035404121657031,-80.5223250360828757],"rgb":[0.533333333333333326,0.266666666666666663,0.733333333333333282],"xyz":[0.21188833720052333,0.129567381983335866,0.484000168055763513],"hpluv":[284.352541737053286,247.012596783276,42.6979882651059626],"hsluv":[284.352541737053286,69.2054262339616741,42.6979882651059626]},"#8844cc":{"lch":[43.840927241858644,94.5481620176080924,280.647262826657595],"luv":[43.840927241858644,17.4689172853010533,-92.9203522904814463],"rgb":[0.533333333333333326,0.266666666666666663,0.8],"xyz":[0.231180712644245057,0.137284332160824668,0.585606678726033669],"hpluv":[280.647262826657595,273.660859280114778,43.840927241858644],"hsluv":[280.647262826657595,72.5522514276637764,43.840927241858644]},"#8844dd":{"lch":[45.0662821798681108,105.864396975636453,277.925647559652191],"luv":[45.0662821798681108,14.5974213234566292,-104.853163222292139],"rgb":[0.533333333333333326,0.266666666666666663,0.866666666666666696],"xyz":[0.2526990381948786,0.145891662381078202,0.698936526626039667],"hpluv":[277.925647559652191,298.083215805211921,45.0662821798681108],"hsluv":[277.925647559652191,79.9664783418063649,45.0662821798681108]},"#8844ee":{"lch":[46.3673172023828,116.961454232737537,275.874868362231723],"luv":[46.3673172023828,11.97173240994087,-116.347150370524886],"rgb":[0.533333333333333326,0.266666666666666663,0.933333333333333348],"xyz":[0.276511218461053365,0.155416534487548247,0.82434734269456289],"hpluv":[275.874868362231723,320.088534026972411,46.3673172023828],"hsluv":[275.874868362231723,89.9071832422553,46.3673172023828]},"#8844ff":{"lch":[47.7372988913525091,127.801124524511906,274.29427304351259],"luv":[47.7372988913525091,9.5696272486796623,-127.442338585146516],"rgb":[0.533333333333333326,0.266666666666666663,1],"xyz":[0.302682266870884731,0.165884953851480943,0.962181530986344735],"hpluv":[274.29427304351259,339.716123682654541,47.7372988913525091],"hsluv":[274.29427304351259,99.9999999999992468,47.7372988913525091]},"#ffcc00":{"lch":[84.1983464973243,98.3335943421723613,63.5926937648685069],"luv":[84.1983464973243,43.7338065737115329,88.0729807536005751],"rgb":[1,0.8,0],"xyz":[0.628309999332456237,0.644477406004528408,0.0913038854044243842],"hpluv":[63.5926937648685069,267.385577483165775,84.1983464973243],"hsluv":[63.5926937648685069,100.000000000007688,84.1983464973243]},"#ffcc11":{"lch":[84.2193135631731877,97.4576917861448777,63.4272907637844199],"luv":[84.2193135631731877,43.5960552023901897,87.1629833075564164],"rgb":[1,0.8,0.0666666666666666657],"xyz":[0.629321664832093397,0.644882072204383316,0.0966319903691800208],"hpluv":[63.4272907637844199,265.403720884510847,84.2193135631731877],"hsluv":[63.4272907637844199,100.000000000007645,84.2193135631731877]},"#ffcc22":{"lch":[84.2581577251319516,95.845309095571892,63.1144413478925301],"luv":[84.2581577251319516,43.342199386504106,85.4855369519677168],"rgb":[1,0.8,0.133333333333333331],"xyz":[0.631197022970570343,0.645632215459774117,0.106508876565159213],"hpluv":[63.1144413478925301,261.744085553517664,84.2581577251319516],"hsluv":[63.1144413478925301,100.00000000000766,84.2581577251319516]},"#ffcc33":{"lch":[84.3220485899852719,93.2224175872051148,62.5809741236821822],"luv":[84.3220485899852719,42.9284172035605494,82.7500461462124832],"rgb":[1,0.8,0.2],"xyz":[0.63428477370302816,0.646867315752757244,0.12277103042277035],"hpluv":[62.5809741236821822,255.758846695652977,84.3220485899852719],"hsluv":[62.5809741236821822,100.000000000007859,84.3220485899852719]},"#ffcc44":{"lch":[84.41414885501203,89.5051620308737,61.7678249437873745],"luv":[84.41414885501203,42.3400226901080785,78.8574442191356439],"rgb":[1,0.8,0.266666666666666663],"xyz":[0.638742768947247,0.64865051385044481,0.146249805375657],"hpluv":[61.7678249437873745,247.206609954939694,84.41414885501203],"hsluv":[61.7678249437873745,100.000000000007887,84.41414885501203]},"#ffcc55":{"lch":[84.5370662928161,84.6613331238571476,60.5932597011580469],"luv":[84.5370662928161,41.569242864558305,73.7532329730438221],"rgb":[1,0.8,0.333333333333333315],"xyz":[0.644705182986609,0.651035479466189559,0.177651852649630904],"hpluv":[60.5932597011580469,235.935294148211483,84.5370662928161],"hsluv":[60.5932597011580469,100.000000000008015,84.5370662928161]},"#ffcc66":{"lch":[84.6930007913096,78.7104448491835456,58.9357730127494222],"luv":[84.6930007913096,40.6144802919859131,67.4225342075500151],"rgb":[1,0.8,0.4],"xyz":[0.65229017876011719,0.654069477775592878,0.217599497056774865],"hpluv":[58.9357730127494222,221.881579382217893,84.6930007913096],"hsluv":[58.9357730127494222,100.000000000008285,84.6930007913096]},"#ffcc77":{"lch":[84.8838226897762809,71.7288671198513441,56.6053668146737863],"luv":[84.8838226897762809,39.4797505573258718,59.8863897244492378],"rgb":[1,0.8,0.466666666666666674],"xyz":[0.661604191780319,0.657795082983673729,0.266653298963172802],"hpluv":[56.6053668146737863,205.087229042099608,84.8838226897762809],"hsluv":[56.6053668146737863,100.000000000008399,84.8838226897762809]},"#ffcc88":{"lch":[85.1111193079521371,63.8628893840605159,53.2910582019814285],"luv":[85.1111193079521371,38.1740592359010691,51.1977523133063457],"rgb":[1,0.8,0.533333333333333326],"xyz":[0.672744608882263,0.662251249824451271,0.325326162366744942],"hpluv":[53.2910582019814285,185.743844227516831,85.1111193079521371],"hsluv":[53.2910582019814285,100.000000000008683,85.1111193079521371]},"#ffcc99":{"lch":[85.3762249003348899,55.3594884395636555,48.4608196634464292],"luv":[85.3762249003348899,36.710650975629143,41.436711563970924],"rgb":[1,0.8,0.6],"xyz":[0.685801572991335595,0.667474035468080484,0.394092840007862821],"hpluv":[48.4608196634464292,164.300705026796521,85.3762249003348899],"hsluv":[48.4608196634464292,100.000000000008811,85.3762249003348899]},"#ffccaa":{"lch":[85.6802414041430467,46.6394331206389836,41.1740083194267896],"luv":[85.6802414041430467,35.1061374539925311,30.7049806200222122],"rgb":[1,0.8,0.66666666666666663],"xyz":[0.700859265956588207,0.673497112654181551,0.473396689624861478],"hpluv":[41.1740083194267896,141.724192848301414,85.6802414041430467],"hsluv":[41.1740083194267896,100.000000000009393,85.6802414041430467]},"#ffccbb":{"lch":[86.0240539433014106,38.4677176438646384,29.8042196075727404],"luv":[86.0240539433014106,33.3795484258888635,19.1199123327444944],"rgb":[1,0.8,0.733333333333333282],"xyz":[0.71799685806392477,0.68035214949711631,0.563654674723503168],"hpluv":[29.8042196075727404,120.116808153279436,86.0240539433014106],"hsluv":[29.8042196075727404,100.000000000009621,86.0240539433014106]},"#ffcccc":{"lch":[86.4083433793485,32.2775975377643,12.1770506300627517],"luv":[86.4083433793485,31.5513664521307504,6.80841962670093892],"rgb":[1,0.8,0.8],"xyz":[0.737289233507646524,0.688069099674605167,0.665261185393773324],"hpluv":[12.1770506300627517,103.973607583717524,86.4083433793485],"hsluv":[12.1770506300627517,100.000000000010388,86.4083433793485]},"#ffccdd":{"lch":[86.8335972965778637,30.2635044437543748,348.373924949033096],"luv":[86.8335972965778637,29.6426074465171432,-6.09881340826452867],"rgb":[1,0.8,0.866666666666666696],"xyz":[0.758807559058280123,0.696676429894858673,0.778591033293779322],"hpluv":[348.373924949033096,100.994037434302086,86.8335972965778637],"hsluv":[348.373924949033096,100.000000000011042,86.8335972965778637]},"#ffccee":{"lch":[87.3001202800073344,33.8382691858854301,324.868297683069],"luv":[87.3001202800073344,27.6740002852911822,-19.4724977777928352],"rgb":[1,0.8,0.933333333333333348],"xyz":[0.782619739324454833,0.70620130200132869,0.904001849362302545],"hpluv":[324.868297683069,117.528800717138253,87.3001202800073344],"hsluv":[324.868297683069,100.000000000011482,87.3001202800073344]},"#ffccff":{"lch":[87.8080440143565255,41.9549825399590404,307.715012949248376],"luv":[87.8080440143565255,25.6653035474654807,-33.1890456889728],"rgb":[1,0.8,1],"xyz":[0.808790787734286254,0.716669721365261414,1.0418360376540845],"hpluv":[307.715012949248376,152.433043069806,87.8080440143565255],"hsluv":[307.715012949248376,100.000000000012506,87.8080440143565255]},"#aa4400":{"lch":[42.2796461632011074,91.196234940608619,23.7609213617016479],"luv":[42.2796461632011074,83.4659581293579578,36.7448921741641357],"rgb":[0.66666666666666663,0.266666666666666663,0],"xyz":[0.186442254479824487,0.126816788621301801,0.0146606651121894659],"hpluv":[23.7609213617016479,273.706361359402308,42.2796461632011074],"hsluv":[23.7609213617016479,100.000000000002331,42.2796461632011074]},"#aa4411":{"lch":[42.3415695164373815,89.5099073241138399,22.7237862619920179],"luv":[42.3415695164373815,82.5619515402106572,34.5766925405446202],"rgb":[0.66666666666666663,0.266666666666666663,0.0666666666666666657],"xyz":[0.18745391997946162,0.127221454821156654,0.0199887700769451],"hpluv":[22.7237862619920179,268.252316854423896,42.3415695164373815],"hsluv":[22.7237862619920179,95.0030043013494634,42.3415695164373815]},"#aa4422":{"lch":[42.4560124733741162,86.5487219398638,20.7501492775311753],"luv":[42.4560124733741162,80.9346773489764075,30.663647399501361],"rgb":[0.66666666666666663,0.266666666666666663,0.133333333333333331],"xyz":[0.189329278117938621,0.127971598076547483,0.0298656562729242916],"hpluv":[20.7501492775311753,258.678767688655569,42.4560124733741162],"hsluv":[20.7501492775311753,86.0172479577967692,42.4560124733741162]},"#aa4433":{"lch":[42.643470741830761,82.1252826930209494,17.3597256589108966],"luv":[42.643470741830761,78.3845002494828691,24.5037176372775498],"rgb":[0.66666666666666663,0.266666666666666663,0.2],"xyz":[0.192417028850396354,0.129206698369530582,0.0461278101305354349],"hpluv":[17.3597256589108966,244.378874013338617,42.643470741830761],"hsluv":[17.3597256589108966,71.9602068881057733,42.643470741830761]},"#aa4444":{"lch":[42.9120210749329871,76.685866981555165,12.1770506300619239],"luv":[42.9120210749329871,74.9604702767477704,16.1756017075590073],"rgb":[0.66666666666666663,0.266666666666666663,0.266666666666666663],"xyz":[0.196875024094615242,0.130989896467218175,0.0696065850834220734],"hpluv":[12.1770506300619239,226.764824908056937,42.9120210749329871],"hsluv":[12.1770506300619239,53.1380271992519795,42.9120210749329871]},"#aa4455":{"lch":[43.2674147484635299,71.0489272172787594,4.80785478807657096],"luv":[43.2674147484635299,70.7989329223205,5.9549270177674023],"rgb":[0.66666666666666663,0.266666666666666663,0.333333333333333315],"xyz":[0.202837438133977255,0.133374862082963,0.101008632357395989],"hpluv":[4.80785478807657096,208.370342069187018,43.2674147484635299],"hsluv":[4.80785478807657096,55.6796565441877931,43.2674147484635299]},"#aa4466":{"lch":[43.7134526285125489,66.3411716499448261,355.020980864279068],"luv":[43.7134526285125489,66.0908363196155477,-5.75781299290188],"rgb":[0.66666666666666663,0.266666666666666663,0.4],"xyz":[0.210422433907485412,0.136408860392366299,0.140956276764539951],"hpluv":[355.020980864279068,192.578302395247789,43.7134526285125489],"hsluv":[355.020980864279068,58.5402216503940949,43.7134526285125489]},"#aa4477":{"lch":[44.252209356870793,63.7921835342278598,343.127789968560137],"luv":[44.252209356870793,61.046215168807521,-18.5149208377531842],"rgb":[0.66666666666666663,0.266666666666666663,0.466666666666666674],"xyz":[0.219736446927687346,0.140134465600447122,0.19001007867093786],"hpluv":[343.127789968560137,182.924482874911519,44.252209356870793],"hsluv":[343.127789968560137,61.5848400189491372,44.252209356870793]},"#aa4488":{"lch":[44.8842146397509367,64.3242328637060865,330.281311898752961],"luv":[44.8842146397509367,55.8636578331810938,-31.8882214461946916],"rgb":[0.66666666666666663,0.266666666666666663,0.533333333333333326],"xyz":[0.230876864029631146,0.14459063244122472,0.248682942074510027],"hpluv":[330.281311898752961,181.852933643495049,44.8842146397509367],"hsluv":[330.281311898752961,64.6866121014520274,44.8842146397509367]},"#aa4499":{"lch":[45.6086312393750077,68.1395242873703779,318.091147538790551],"luv":[45.6086312393750077,50.7100032224043318,-45.5136281051387854],"rgb":[0.66666666666666663,0.266666666666666663,0.6],"xyz":[0.243933828138703873,0.149813418084853878,0.317449619715627906],"hpluv":[318.091147538790551,189.579504981677246,45.6086312393750077],"hsluv":[318.091147538790551,67.7395528675925,45.6086312393750077]},"#aa44aa":{"lch":[46.4234422285949293,74.7240378323112822,307.715012949244056],"luv":[46.4234422285949293,45.7112599542092468,-59.1114417296972476],"rgb":[0.66666666666666663,0.266666666666666663,0.66666666666666663],"xyz":[0.258991521103956401,0.155836495270954972,0.396753469332626507],"hpluv":[307.715012949244056,204.25011915803762,46.4234422285949293],"hsluv":[307.715012949244056,70.6643205278868862,46.4234422285949293]},"#aa44bb":{"lch":[47.3256474704596144,83.2556513741305224,299.464802712285689],"luv":[47.3256474704596144,40.9525224701243289,-72.4872015535482177],"rgb":[0.66666666666666663,0.266666666666666663,0.733333333333333282],"xyz":[0.276129113211293076,0.162691532113889731,0.487011454431268254],"hpluv":[299.464802712285689,223.2320179514779,47.3256474704596144],"hsluv":[299.464802712285689,73.4081717041738244,47.3256474704596144]},"#aa44cc":{"lch":[48.311463406953564,92.9774863668884564,293.103270963637385],"luv":[48.311463406953564,36.4834012629607827,-85.5206080625642073],"rgb":[0.66666666666666663,0.266666666666666663,0.8],"xyz":[0.29542148865501483,0.170408482291378532,0.58861796510153841],"hpluv":[293.103270963637385,244.211962975346665,48.311463406953564],"hsluv":[293.103270963637385,75.9413806031634806,48.311463406953564]},"#aa44dd":{"lch":[49.376518181476186,103.335675962773564,288.229438001673088],"luv":[49.376518181476186,32.3257723855767125,-98.1494083851717676],"rgb":[0.66666666666666663,0.266666666666666663,0.866666666666666696],"xyz":[0.316939814205648318,0.179015812511632066,0.701947813001544407],"hpluv":[288.229438001673088,265.563967984367252,49.376518181476186],"hsluv":[288.229438001673088,78.2521754793284572,49.376518181476186]},"#aa44ee":{"lch":[50.5160343838387149,113.969241853628489,284.471970959088878],"luv":[50.5160343838387149,28.4816380653286,-110.35299897060564],"rgb":[0.66666666666666663,0.266666666666666663,0.933333333333333348],"xyz":[0.340751994471823083,0.188540684618102111,0.82735862907006763],"hpluv":[284.471970959088878,286.284434407824506,50.5160343838387149],"hsluv":[284.471970959088878,88.5420288112715355,50.5160343838387149]},"#aa44ff":{"lch":[51.7249932896939271,124.658572052901789,281.540806859048416],"luv":[51.7249932896939271,24.9399169363974949,-122.138282816953421],"rgb":[0.66666666666666663,0.266666666666666663,1],"xyz":[0.366923042881654449,0.199009103982034807,0.965192817361849476],"hpluv":[281.540806859048416,305.816582895375404,51.7249932896939271],"hsluv":[281.540806859048416,99.9999999999991189,51.7249932896939271]},"#885500":{"lch":[40.7868302215615941,57.8204075903908716,44.0255445375638317],"luv":[40.7868302215615941,41.5746091122878738,40.1839695784202107],"rgb":[0.533333333333333326,0.333333333333333315,0],"xyz":[0.134014735183400707,0.117319151919440201,0.0155871309747999068],"hpluv":[44.0255445375638317,179.887306981779091,40.7868302215615941],"hsluv":[44.0255445375638317,100.000000000002402,40.7868302215615941]},"#885511":{"lch":[40.8520464566488215,55.8013246771799061,42.6953812551229817],"luv":[40.8520464566488215,41.012258348334349,37.838901951530346],"rgb":[0.533333333333333326,0.333333333333333315,0.0666666666666666657],"xyz":[0.135026400683037839,0.117723818119295054,0.0209152359395555383],"hpluv":[42.6953812551229817,173.328515822040885,40.8520464566488215],"hsluv":[42.6953812551229817,94.5141207032234121,40.8520464566488215]},"#885522":{"lch":[40.9725457623763205,52.2529579480882163,40.0436802938645613],"luv":[40.9725457623763205,40.0024704692244413,33.6180601862079769],"rgb":[0.533333333333333326,0.333333333333333315,0.133333333333333331],"xyz":[0.136901758821514841,0.118473961374685868,0.0307921221355347344],"hpluv":[40.0436802938645613,161.829338044896133,40.9725457623763205],"hsluv":[40.0436802938645613,84.6783920295854813,40.9725457623763205]},"#885533":{"lch":[41.169842808691,46.965215190705,35.0963808532685348],"luv":[41.169842808691,38.426283274472091,27.0028182162390955],"rgb":[0.533333333333333326,0.333333333333333315,0.2],"xyz":[0.139989509553972602,0.119709061667668981,0.0470542759931458776],"hpluv":[35.0963808532685348,144.75595347357384,41.169842808691],"hsluv":[35.0963808532685348,69.3663942781120113,41.169842808691]},"#885544":{"lch":[41.4523140669009891,40.5881659839312263,26.5063976077417607],"luv":[41.4523140669009891,36.321721997537459,18.1144066718387258],"rgb":[0.533333333333333326,0.333333333333333315,0.266666666666666663],"xyz":[0.144447504798191462,0.121492259765356561,0.0705330509460325161],"hpluv":[26.5063976077417607,124.248162385526584,41.4523140669009891],"hsluv":[26.5063976077417607,49.0053368988820708,41.4523140669009891]},"#885555":{"lch":[41.8258216452066449,34.5587016635497619,12.1770506300621708],"luv":[41.8258216452066449,33.7811467851905647,7.28958040957408926],"rgb":[0.533333333333333326,0.333333333333333315,0.333333333333333315],"xyz":[0.150409918837553447,0.123877225381101394,0.101935098220006432],"hpluv":[12.1770506300621708,104.846095879990827,41.8258216452066449],"hsluv":[12.1770506300621708,24.5686900376428454,41.8258216452066449]},"#885566":{"lch":[42.2941086985740071,31.3312262907584227,350.801819307985852],"luv":[42.2941086985740071,30.9283487442626033,-5.00829160852097477],"rgb":[0.533333333333333326,0.333333333333333315,0.4],"xyz":[0.157994914611061632,0.126911223690504699,0.141882742627150393],"hpluv":[350.801819307985852,94.0019456509277092,42.2941086985740071],"hsluv":[350.801819307985852,29.8132814869556348,42.2941086985740071]},"#885577":{"lch":[42.8590433503379202,33.3514130793808121,326.760730108285],"luv":[42.8590433503379202,27.8947491372056646,-18.2811302977648822],"rgb":[0.533333333333333326,0.333333333333333315,0.466666666666666674],"xyz":[0.167308927631263538,0.130636828898585522,0.190936544533548302],"hpluv":[326.760730108285,98.7440857113442263,42.8590433503379202],"hsluv":[326.760730108285,35.3342565461126838,42.8590433503379202]},"#885588":{"lch":[43.5208237898043535,40.5407907233736822,307.71501294924542],"luv":[43.5208237898043535,24.8001938501248809,-32.0703304858651137],"rgb":[0.533333333333333326,0.333333333333333315,0.533333333333333326],"xyz":[0.178449344733207393,0.13509299573936312,0.24960940793712047],"hpluv":[307.71501294924542,118.204615389413121,43.5208237898043535],"hsluv":[307.71501294924542,40.8951968507306276,43.5208237898043535]},"#885599":{"lch":[44.278184332936334,50.8805981998246,295.296495298175159],"luv":[44.278184332936334,21.7414099229374891,-46.0015909261276477],"rgb":[0.533333333333333326,0.333333333333333315,0.6],"xyz":[0.191506308842280093,0.140315781382992277,0.318376085578238377],"hpluv":[295.296495298175159,145.814841874582299,44.278184332936334],"hsluv":[295.296495298175159,46.3068912836000735,44.278184332936334]},"#8855aa":{"lch":[45.1286132569148819,62.6826496939872868,287.441560465434577],"luv":[45.1286132569148819,18.7880516159354372,-59.8006997378464717],"rgb":[0.533333333333333326,0.333333333333333315,0.66666666666666663],"xyz":[0.206564001807532649,0.146338858569093372,0.397679935195237],"hpluv":[287.441560465434577,176.252255995348207,45.1286132569148819],"hsluv":[287.441560465434577,51.4349240401982044,45.1286132569148819]},"#8855bb":{"lch":[46.0685799041538857,75.0110486022347374,282.303621490188448],"luv":[46.0685799041538857,15.9842650126874819,-73.2882029006783284],"rgb":[0.533333333333333326,0.333333333333333315,0.733333333333333282],"xyz":[0.223701593914869268,0.15319389541202813,0.487937920293878724],"hpluv":[282.303621490188448,206.613996430434867,46.0685799041538857],"hsluv":[282.303621490188448,56.1963190544194191,46.0685799041538857]},"#8855cc":{"lch":[47.0937627302438173,87.3889723072186229,278.789341148015808],"luv":[47.0937627302438173,13.3532090014702405,-86.3627482788434406],"rgb":[0.533333333333333326,0.333333333333333315,0.8],"xyz":[0.242993969358591022,0.160910845589516932,0.58954443096414888],"hpluv":[278.789341148015808,235.468364438494291,47.0937627302438173],"hsluv":[278.789341148015808,67.0071532265272083,47.0937627302438173]},"#8855dd":{"lch":[48.1992684502362323,99.579643468316263,276.285619014825784],"luv":[48.1992684502362323,10.9024601244232162,-98.9810171523426163],"rgb":[0.533333333333333326,0.333333333333333315,0.866666666666666696],"xyz":[0.264512294909224566,0.169518175809770466,0.702874278864154878],"hpluv":[276.285619014825784,262.161822059859048,48.1992684502362323],"hsluv":[276.285619014825784,77.9008511174594673,48.1992684502362323]},"#8855ee":{"lch":[49.3798334670730128,111.473561000814087,274.439637972028379],"luv":[49.3798334670730128,8.62903279282213198,-111.139077714648238],"rgb":[0.533333333333333326,0.333333333333333315,0.933333333333333348],"xyz":[0.288324475175399275,0.179043047916240511,0.828285094932678101],"hpluv":[274.439637972028379,286.45841220928196,49.3798334670730128],"hsluv":[274.439637972028379,88.8665998649397295,49.3798334670730128]},"#8855ff":{"lch":[50.6300011250937416,123.030386306339324,273.039422554437692],"luv":[50.6300011250937416,6.52344684402536235,-122.857318039912712],"rgb":[0.533333333333333326,0.333333333333333315,1],"xyz":[0.314495523585230696,0.189511467280173207,0.96611928322446],"hpluv":[273.039422554437692,308.349875308867752,50.6300011250937416],"hsluv":[273.039422554437692,99.9999999999992,50.6300011250937416]},"#ffdd00":{"lch":[88.435570144315335,99.3071523162376195,71.7429005549186911],"luv":[88.435570144315335,31.1110916577435432,94.3080615696447211],"rgb":[1,0.866666666666666696,0],"xyz":[0.670943989879631442,0.729745387098879927,0.105515215586815703],"hpluv":[71.7429005549186911,382.363935262913174,88.435570144315335],"hsluv":[71.7429005549186911,100.000000000012946,88.435570144315335]},"#ffdd11":{"lch":[88.454870819445,98.4678340856778,71.6448189166009826],"luv":[88.454870819445,31.0081800872079896,93.4580500395971683],"rgb":[1,0.866666666666666696,0.0666666666666666657],"xyz":[0.671955655379268602,0.730150053298734836,0.110843320551571339],"hpluv":[71.6448189166009826,379.826472639876158,88.454870819445],"hsluv":[71.6448189166009826,100.00000000001296,88.454870819445]},"#ffdd22":{"lch":[88.4906302718539735,96.9204819130462,71.4594307996413],"luv":[88.4906302718539735,30.8183922605744947,91.8901872494037093],"rgb":[1,0.866666666666666696,0.133333333333333331],"xyz":[0.673831013517745547,0.730900196554125636,0.120720206747550532],"hpluv":[71.4594307996413,375.129750349680876,88.4906302718539735],"hsluv":[71.4594307996413,100.00000000001296,88.4906302718539735]},"#ffdd33":{"lch":[88.5494544369894641,94.3967280931520776,71.1436744567929793],"luv":[88.5494544369894641,30.5086598694284135,89.330643945199526],"rgb":[1,0.866666666666666696,0.2],"xyz":[0.676918764250203364,0.732135296847108763,0.136982360605161668],"hpluv":[71.1436744567929793,367.416320973762822,88.5494544369894641],"hsluv":[71.1436744567929793,100.000000000013216,88.5494544369894641]},"#ffdd44":{"lch":[88.6342662809689301,90.8049878425315455,70.6631891871382152],"luv":[88.6342662809689301,30.0674100616276441,85.6825342136204284],"rgb":[1,0.866666666666666696,0.266666666666666663],"xyz":[0.681376759494422224,0.733918494944796329,0.160461135558048307],"hpluv":[70.6631891871382152,356.322085242203968,88.6342662809689301],"hsluv":[70.6631891871382152,100.000000000013173,88.6342662809689301]},"#ffdd55":{"lch":[88.7474847112806486,86.0957791731635353,69.970644696948483],"luv":[88.7474847112806486,29.4879375414461293,80.8884709398435433],"rgb":[1,0.866666666666666696,0.333333333333333315],"xyz":[0.687339173533784153,0.736303460560541079,0.191863182832022222],"hpluv":[69.970644696948483,341.559684906391112,88.7474847112806486],"hsluv":[69.970644696948483,100.000000000013429,88.7474847112806486]},"#ffdd66":{"lch":[88.8911610589964454,80.258955992054581,68.9956984045855819],"luv":[88.8911610589964454,28.7678627944319345,74.9260307715235427],"rgb":[1,0.866666666666666696,0.4],"xyz":[0.694924169307292394,0.739337458869944397,0.231810827239166184],"hpluv":[68.9956984045855819,322.902183683526573,88.8911610589964454],"hsluv":[68.9956984045855819,100.000000000013586,88.8911610589964454]},"#ffdd77":{"lch":[89.0670520916409885,73.323747836811151,67.6276760217336346],"luv":[89.0670520916409885,27.9087592291374555,67.8046691248198812],"rgb":[1,0.866666666666666696,0.466666666666666674],"xyz":[0.704238182327494244,0.743063064078025248,0.280864629145564093],"hpluv":[67.6276760217336346,300.178104609939055,89.0670520916409885],"hsluv":[67.6276760217336346,100.000000000014367,89.0670520916409885]},"#ffdd88":{"lch":[89.2766635023192379,65.3617696880632622,65.6822421460803554],"luv":[89.2766635023192379,26.9157684368328205,59.5625918359016637],"rgb":[1,0.866666666666666696,0.533333333333333326],"xyz":[0.715378599429438156,0.747519230918802791,0.339537492549136233],"hpluv":[65.6822421460803554,273.280966903928913,89.2766635023192379],"hsluv":[65.6822421460803554,100.00000000001468,89.2766635023192379]},"#ffdd99":{"lch":[89.5212778802452362,56.4966374008110606,62.8311838382076928],"luv":[89.5212778802452362,25.7971434460875209,50.2630821540094814],"rgb":[1,0.866666666666666696,0.6],"xyz":[0.7284355635385108,0.752742016562432,0.408304170190254168],"hpluv":[62.8311838382076928,242.212083001239,89.5212778802452362],"hsluv":[62.8311838382076928,100.000000000014893,89.5212778802452362]},"#ffddaa":{"lch":[89.8019739344538408,46.9317870748453103,58.439957868293348],"luv":[89.8019739344538408,24.5637119196041205,39.9902074859498],"rgb":[1,0.866666666666666696,0.66666666666666663],"xyz":[0.743493256503763411,0.75876509374853307,0.487608019807252768],"hpluv":[58.439957868293348,207.216087370130765,89.8019739344538408],"hsluv":[58.439957868293348,100.000000000015916,89.8019739344538408]},"#ffddbb":{"lch":[90.1196406145249114,37.0342016806554142,51.1553076529108282],"luv":[90.1196406145249114,23.228278171915747,28.8440494260352231],"rgb":[1,0.866666666666666696,0.733333333333333282],"xyz":[0.7606308486111,0.765620130591467829,0.57786600490589457],"hpluv":[51.1553076529108282,169.207898233907059,90.1196406145249114],"hsluv":[51.1553076529108282,100.00000000001647,90.1196406145249114]},"#ffddcc":{"lch":[90.4749882420216665,27.6093496047032829,37.8361109659034085],"luv":[90.4749882420216665,21.8049964890922183,16.9357111957368183],"rgb":[1,0.866666666666666696,0.8],"xyz":[0.779923224054821729,0.773337080768956686,0.679472515576164726],"hpluv":[37.8361109659034085,131.228146902908946,90.4749882420216665],"hsluv":[37.8361109659034085,100.000000000017565,90.4749882420216665]},"#ffdddd":{"lch":[90.8685579434819743,20.7762078419716971,12.1770506300632935],"luv":[90.8685579434819743,20.3087527298379129,4.38239373528981613],"rgb":[1,0.866666666666666696,0.866666666666666696],"xyz":[0.801441549605455328,0.781944410989210192,0.792802363476170724],"hpluv":[12.1770506300632935,103.332662997484363,90.8685579434819743],"hsluv":[12.1770506300632935,100.000000000018645,90.8685579434819743]},"#ffddee":{"lch":[91.3007301977477255,20.6730653908416642,335.121130580225611],"luv":[91.3007301977477255,18.7545890012647547,-8.69718483462634318],"rgb":[1,0.866666666666666696,0.933333333333333348],"xyz":[0.82525372987163,0.791469283095680209,0.918213179544694],"hpluv":[335.121130580225611,108.301670079840079,91.3007301977477255],"hsluv":[335.121130580225611,100.000000000019696,91.3007301977477255]},"#ffddff":{"lch":[91.7717330140538081,28.0468143452174594,307.715012949251673],"luv":[91.7717330140538081,17.1571994583452359,-22.1868046744377736],"rgb":[1,0.866666666666666696,1],"xyz":[0.851424778281461458,0.801937702459612933,1.05604736783647568],"hpluv":[307.715012949251673,155.925616416863875,91.7717330140538081],"hsluv":[307.715012949251673,100.000000000021274,91.7717330140538081]},"#aa5500":{"lch":[45.6948541105979729,81.8101604081173406,31.0178153240564285],"luv":[45.6948541105979729,70.1118895783240674,42.1571498770823254],"rgb":[0.66666666666666663,0.333333333333333315,0],"xyz":[0.198255511194170453,0.150443302049994065,0.0185984173503046732],"hpluv":[31.0178153240564285,227.184802334875343,45.6948541105979729],"hsluv":[31.0178153240564285,100.000000000002132,45.6948541105979729]},"#aa5511":{"lch":[45.7501207141343542,80.2053049231342357,30.0334267749456458],"luv":[45.7501207141343542,69.4364235827854088,40.1431690060082786],"rgb":[0.66666666666666663,0.333333333333333315,0.0666666666666666657],"xyz":[0.199267176693807585,0.150847968249848918,0.0239265223150603029],"hpluv":[30.0334267749456458,222.459100300712947,45.7501207141343542],"hsluv":[30.0334267749456458,95.7485290168496874,45.7501207141343542]},"#aa5522":{"lch":[45.8523093955831129,77.3637164970789826,28.1463245173948593],"luv":[45.8523093955831129,68.2151287979892231,36.4943945464800308],"rgb":[0.66666666666666663,0.333333333333333315,0.133333333333333331],"xyz":[0.201142534832284586,0.151598111505239747,0.0338034085110395],"hpluv":[28.1463245173948593,214.099393521154866,45.8523093955831129],"hsluv":[28.1463245173948593,88.0687786146531835,45.8523093955831129]},"#aa5533":{"lch":[46.0198296754266707,73.057054041475979,24.8605966968797674],"luv":[46.0198296754266707,66.2871018342682845,30.7140566456547255],"rgb":[0.66666666666666663,0.333333333333333315,0.2],"xyz":[0.20423028556474232,0.152833211798222846,0.0500655623686506457],"hpluv":[24.8605966968797674,201.444992058953261,46.0198296754266707],"hsluv":[24.8605966968797674,75.9647480733224683,46.0198296754266707]},"#aa5544":{"lch":[46.260105303202792,67.6400753638857,19.7251402693972757],"luv":[46.260105303202792,63.6711277752059743,22.829088529013422],"rgb":[0.66666666666666663,0.333333333333333315,0.266666666666666663],"xyz":[0.208688280808961207,0.154616409895910439,0.0735443373215372842],"hpluv":[19.7251402693972757,185.539675327038225,46.260105303202792],"hsluv":[19.7251402693972757,59.5831884891410581,46.260105303202792]},"#aa5555":{"lch":[46.5785950143652201,61.8404850960729462,12.1770506300619488],"luv":[46.5785950143652201,60.4491026496292108,13.0442165641410206],"rgb":[0.66666666666666663,0.333333333333333315,0.333333333333333315],"xyz":[0.21465069484832322,0.157001375511655272,0.1049463845955112],"hpluv":[12.1770506300619488,168.471262237657498,46.5785950143652201],"hsluv":[12.1770506300619488,39.4780386186733381,46.5785950143652201]},"#aa5566":{"lch":[46.9791292969753727,56.7719582569154682,1.70455911852653919],"luv":[46.9791292969753727,56.7468364589401801,1.68872621441415594],"rgb":[0.66666666666666663,0.333333333333333315,0.4],"xyz":[0.222235690621831378,0.160035373821058563,0.144894029002655161],"hpluv":[1.70455911852653919,153.34451205140752,46.9791292969753727],"hsluv":[1.70455911852653919,42.9013314457092818,46.9791292969753727]},"#aa5577":{"lch":[47.4641008255870247,53.815805251865207,348.374645401059922],"luv":[47.4641008255870247,52.7118371029350072,-10.8444974129891012],"rgb":[0.66666666666666663,0.333333333333333315,0.466666666666666674],"xyz":[0.231549703642033311,0.163760979029139386,0.193947830909053071],"hpluv":[348.374645401059922,143.874527939012694,47.4641008255870247],"hsluv":[348.374645401059922,46.6096363259647291,47.4641008255870247]},"#aa5588":{"lch":[48.0346061313586716,54.1759936988691209,333.519859036575326],"luv":[48.0346061313586716,48.4923339918738,-24.1564036495598238],"rgb":[0.66666666666666663,0.333333333333333315,0.533333333333333326],"xyz":[0.242690120743977111,0.168217145869916984,0.252620694312625238],"hpluv":[333.519859036575326,143.117248614217317,48.0346061313586716],"hsluv":[333.519859036575326,50.4580944804944309,48.0346061313586716]},"#aa5599":{"lch":[48.6905763376225,58.2309730669685877,319.412334073918828],"luv":[48.6905763376225,44.2212637341432782,-37.885697275903027],"rgb":[0.66666666666666663,0.333333333333333315,0.6],"xyz":[0.255747084853049866,0.173439931513546142,0.321387371953743117],"hpluv":[319.412334073918828,151.756904292295189,48.6905763376225],"hsluv":[319.412334073918828,54.3174516364451492,48.6905763376225]},"#aa55aa":{"lch":[49.4309115490906095,65.3990862514302904,307.715012949244453],"luv":[49.4309115490906095,40.0068668547544135,-51.7348150377284242],"rgb":[0.66666666666666663,0.333333333333333315,0.66666666666666663],"xyz":[0.270804777818302367,0.179463008699647236,0.400691221570741718],"hpluv":[307.715012949244453,167.885190443837445,49.4309115490906095],"hsluv":[307.715012949244453,58.0831627335764651,49.4309115490906095]},"#aa55bb":{"lch":[50.2536225490081137,74.6889496830887651,298.754319252296796],"luv":[50.2536225490081137,35.9294822147829578,-65.4790921786531896],"rgb":[0.66666666666666663,0.333333333333333315,0.733333333333333282],"xyz":[0.287942369925639041,0.186318045542582,0.490949206669383464],"hpluv":[298.754319252296796,188.594188212432869,50.2536225490081137],"hsluv":[298.754319252296796,61.6784311565058445,50.2536225490081137]},"#aa55cc":{"lch":[51.1559779262539394,85.2173265065914194,292.086991032215337],"luv":[51.1559779262539394,32.0428980728004476,-78.9635702082114],"rgb":[0.66666666666666663,0.333333333333333315,0.8],"xyz":[0.307234745369360795,0.194034995720070796,0.59255571733965362],"hpluv":[292.086991032215337,211.383381159177,51.1559779262539394],"hsluv":[292.086991032215337,65.0527897247966536,51.1559779262539394]},"#aa55dd":{"lch":[52.1346521614624407,96.3659410029227,287.126655540846059],"luv":[52.1346521614624407,28.3783197098194826,-92.0927008824589137],"rgb":[0.66666666666666663,0.333333333333333315,0.866666666666666696],"xyz":[0.328753070919994284,0.20264232594032433,0.705885565239659618],"hpluv":[287.126655540846059,234.550526854663673,52.1346521614624407],"hsluv":[287.126655540846059,75.3446722268955256,52.1346521614624407]},"#aa55ee":{"lch":[53.1858694266106227,107.745747725204239,283.388779775386126],"luv":[53.1858694266106227,24.9493252566021582,-104.817352199450397],"rgb":[0.66666666666666663,0.333333333333333315,0.933333333333333348],"xyz":[0.352565251186169049,0.212167198046794375,0.831296381308182841],"hpluv":[283.388779775386126,257.065149407391289,53.1858694266106227],"hsluv":[283.388779775386126,87.5522298773574335,53.1858694266106227]},"#aa55ff":{"lch":[54.3055382240479361,119.125691570750178,280.523377624217346],"luv":[54.3055382240479361,21.7567225446561139,-117.12205350114192],"rgb":[0.66666666666666663,0.333333333333333315,1],"xyz":[0.378736299596000414,0.222635617410727071,0.969130569599964686],"hpluv":[280.523377624217346,278.356033354379861,54.3055382240479361],"hsluv":[280.523377624217346,99.999999999999,54.3055382240479361]},"#886600":{"lch":[45.272583339231268,54.7393681124008964,58.6614018365849361],"luv":[45.272583339231268,28.4696504737342408,46.7533680417607513],"rgb":[0.533333333333333326,0.4,0],"xyz":[0.149042792889247183,0.147375267331133541,0.020596483543415256],"hpluv":[58.6614018365849361,153.427719347991228,45.272583339231268],"hsluv":[58.6614018365849361,100.000000000002288,45.272583339231268]},"#886611":{"lch":[45.3286132830504442,52.7569286908504935,57.8019218571504112],"luv":[45.3286132830504442,28.1114182485843394,44.6434954830448234],"rgb":[0.533333333333333326,0.4,0.0666666666666666657],"xyz":[0.150054458388884315,0.147779933530988394,0.0259245885081708857],"hpluv":[57.8019218571504112,147.688404254652824,45.3286132830504442],"hsluv":[57.8019218571504112,95.5933263309827765,45.3286132830504442]},"#886622":{"lch":[45.4322079122274616,49.2027541487777427,56.0700181971155658],"luv":[45.4322079122274616,27.4639619392241023,40.8245246197174367],"rgb":[0.533333333333333326,0.4,0.133333333333333331],"xyz":[0.151929816527361317,0.148530076786379223,0.0358014747041500853],"hpluv":[56.0700181971155658,137.424731357888106,45.4322079122274616],"hsluv":[56.0700181971155658,87.640709221506242,45.4322079122274616]},"#886633":{"lch":[45.6020177209920377,43.6934420281053448,52.7580970187872822],"luv":[45.6020177209920377,26.4424624059276816,34.7838045385277539],"rgb":[0.533333333333333326,0.4,0.2],"xyz":[0.155017567259819078,0.149765177079362322,0.0520636285617612285],"hpluv":[52.7580970187872822,121.582627853675135,45.6020177209920377],"hsluv":[52.7580970187872822,75.126145239043538,45.6020177209920377]},"#886644":{"lch":[45.8455444844743383,36.5157977851307862,46.6690567683987183],"luv":[45.8455444844743383,25.057552877875338,26.56167411246971],"rgb":[0.533333333333333326,0.4,0.266666666666666663],"xyz":[0.159475562504037938,0.151548375177049915,0.0755424035146478601],"hpluv":[46.6690567683987183,101.07016738030697,45.8455444844743383],"hsluv":[46.6690567683987183,58.2269221932267413,45.8455444844743383]},"#886655":{"lch":[46.1682850891648,28.5288655346450497,35.057100942904512],"luv":[46.1682850891648,23.3531590809093643,16.3867669061227161],"rgb":[0.533333333333333326,0.4,0.333333333333333315],"xyz":[0.165437976543399923,0.153933340792794748,0.106944450788621775],"hpluv":[35.057100942904512,78.4115584201214517,46.1682850891648],"hsluv":[35.057100942904512,37.5458750067509825,46.1682850891648]},"#886666":{"lch":[46.5740725388720946,21.8884171395184417,12.1770506300623786],"luv":[46.5740725388720946,21.3959378301945833,4.61699569417310141],"rgb":[0.533333333333333326,0.4,0.4],"xyz":[0.173022972316908108,0.156967339102198039,0.146892095195765737],"hpluv":[12.1770506300623786,59.6361320849851921,46.5740725388720946],"hsluv":[12.1770506300623786,13.9745942082290213,46.5740725388720946]},"#886677":{"lch":[47.0652698878111053,20.9860757761626395,336.621799391030947],"luv":[47.0652698878111053,19.2632377900225293,-8.32724722389627559],"rgb":[0.533333333333333326,0.4,0.466666666666666674],"xyz":[0.18233698533711,0.160692944310278862,0.195945897102163646],"hpluv":[336.621799391030947,56.5809206524471051,47.0652698878111053],"hsluv":[336.621799391030947,19.7183189325496855,47.0652698878111053]},"#886688":{"lch":[47.6429159175320649,27.8420771343032278,307.715012949247239],"luv":[47.6429159175320649,17.0319546757823481,-22.0248445868429776],"rgb":[0.533333333333333326,0.4,0.533333333333333326],"xyz":[0.193477402439053869,0.16514911115105646,0.254618760505735842],"hpluv":[307.715012949247239,74.1553731862279335,47.6429159175320649],"hsluv":[307.715012949247239,25.6555006249089494,47.6429159175320649]},"#886699":{"lch":[48.306860672000596,39.0046617113806562,292.251886736336132],"luv":[48.306860672000596,14.770249996667463,-36.0999078981538304],"rgb":[0.533333333333333326,0.4,0.6],"xyz":[0.206534366548126569,0.170371896794685618,0.323385438146853721],"hpluv":[292.251886736336132,102.458272059709785,48.306860672000596],"hsluv":[292.251886736336132,31.5854767603208231,48.306860672000596]},"#8866aa":{"lch":[49.0559053000777112,51.7871044207240132,284.005102499462396],"luv":[49.0559053000777112,12.5329093477081912,-50.2476901714407305],"rgb":[0.533333333333333326,0.4,0.66666666666666663],"xyz":[0.221592059513379125,0.176394973980786712,0.402689287763852322],"hpluv":[284.005102499462396,133.958310232957899,49.0559053000777112],"hsluv":[284.005102499462396,39.6059098285329867,49.0559053000777112]},"#8866bb":{"lch":[49.8879495217490074,65.0723394771694501,279.16089377463328],"luv":[49.8879495217490074,10.3599978518109275,-64.242352148271],"rgb":[0.533333333333333326,0.4,0.733333333333333282],"xyz":[0.238729651620715744,0.183250010823721471,0.492947272862494068],"hpluv":[279.16089377463328,165.516042277675325,49.8879495217490074],"hsluv":[279.16089377463328,51.4342030459355897,49.8879495217490074]},"#8866cc":{"lch":[50.800144438276476,78.3702679805165161,276.063171305995638],"luv":[50.800144438276476,8.2778522611756955,-77.9318680982312912],"rgb":[0.533333333333333326,0.4,0.8],"xyz":[0.258022027064437498,0.190966961001210273,0.594553783532764224],"hpluv":[276.063171305995638,195.760791019658512,50.800144438276476],"hsluv":[276.063171305995638,63.370404435102877,50.800144438276476]},"#8866dd":{"lch":[51.7890458420015278,91.4433004674113903,273.951375062119212],"luv":[51.7890458420015278,6.3013441888180548,-91.225929766636682],"rgb":[0.533333333333333326,0.4,0.866666666666666696],"xyz":[0.279540352615071042,0.199574291221463807,0.707883631432770222],"hpluv":[273.951375062119212,224.054313572832513,51.7890458420015278],"hsluv":[273.951375062119212,75.4110532620532,51.7890458420015278]},"#8866ee":{"lch":[52.8507624936088831,104.176067138923727,272.440799986921],"luv":[52.8507624936088831,4.4365578942807,-104.081554170680207],"rgb":[0.533333333333333326,0.4,0.933333333333333348],"xyz":[0.303352532881245751,0.209099163327933851,0.833294447501293445],"hpluv":[272.440799986921,250.124381092910085,52.8507624936088831],"hsluv":[272.440799986921,87.5962217171162365,52.8507624936088831]},"#8866ff":{"lch":[53.9810943197935273,116.520298408763196,271.319576027424205],"luv":[53.9810943197935273,2.6833355157121912,-116.48939716462327],"rgb":[0.533333333333333326,0.4,1],"xyz":[0.329523581291077172,0.219567582691866547,0.97112863579307529],"hpluv":[271.319576027424205,273.904539658900717,53.9810943197935273],"hsluv":[271.319576027424205,99.9999999999990621,53.9810943197935273]},"#ffee00":{"lch":[92.75564548426334,102.358730475882979,79.2433869538170228],"luv":[92.75564548426334,19.1039702538988,100.560171167180329],"rgb":[1,0.933333333333333348,0],"xyz":[0.718122766220146147,0.824102939779910892,0.121241474366986887],"hpluv":[79.2433869538170228,651.393632104361359,92.75564548426334],"hsluv":[79.2433869538170228,100.000000000024428,92.75564548426334]},"#ffee11":{"lch":[92.7734436379168,101.564402459740549,79.2015044483446218],"luv":[92.7734436379168,19.0286516433192254,99.765917344759373],"rgb":[1,0.933333333333333348,0.0666666666666666657],"xyz":[0.719134431719783307,0.824507605979765801,0.12656957933174251],"hpluv":[79.2015044483446218,648.021125158270593,92.7734436379168],"hsluv":[79.2015044483446218,100.000000000024428,92.7734436379168]},"#ffee22":{"lch":[92.8064212727168183,100.098587827824673,79.1224559985664797],"luv":[92.8064212727168183,18.8896613956421291,98.3000914418838079],"rgb":[1,0.933333333333333348,0.133333333333333331],"xyz":[0.721009789858260253,0.825257749235156601,0.136446465527721716],"hpluv":[79.1224559985664797,641.762708667564539,92.8064212727168183],"hsluv":[79.1224559985664797,100.000000000024357,92.8064212727168183]},"#ffee33":{"lch":[92.8606749716073665,97.7038183815635506,78.9881622996309147],"luv":[92.8606749716073665,18.6625824309063972,95.9048702796014396],"rgb":[1,0.933333333333333348,0.2],"xyz":[0.72409754059071807,0.826492849528139728,0.152708619385332867],"hpluv":[78.9881622996309147,631.438241912838748,92.8606749716073665],"hsluv":[78.9881622996309147,100.000000000024599,92.8606749716073665]},"#ffee44":{"lch":[92.9389094487226828,94.2867391890675606,78.7846200921967181],"luv":[92.9389094487226828,18.3385503337116411,92.4861436030564192],"rgb":[1,0.933333333333333348,0.266666666666666663],"xyz":[0.728555535834936929,0.828276047625827294,0.176187394338219505],"hpluv":[78.7846200921967181,616.484076987319668,92.9389094487226828],"hsluv":[78.7846200921967181,100.000000000025381,92.9389094487226828]},"#ffee55":{"lch":[93.0433700241155179,89.7896797607108,78.4929149488566082],"luv":[93.0433700241155179,17.9120631143144244,87.984911129805738],"rgb":[1,0.933333333333333348,0.333333333333333315],"xyz":[0.734517949874298859,0.830661013241572,0.207589441612193393],"hpluv":[78.4929149488566082,596.384103057951847,93.0433700241155179],"hsluv":[78.4929149488566082,100.000000000025906,93.0433700241155179]},"#ffee66":{"lch":[93.1759694096933,84.1867368500463,78.0854442567743661],"luv":[93.1759694096933,17.3805845725397461,82.3730656305550895],"rgb":[1,0.933333333333333348,0.4],"xyz":[0.7421029456478071,0.833695011550975362,0.247537086019337382],"hpluv":[78.0854442567743661,570.62648367707834,93.1759694096933],"hsluv":[78.0854442567743661,100.000000000026517,93.1759694096933]},"#ffee77":{"lch":[93.3383558005883742,77.4816602435733586,77.5195453952684659],"luv":[93.3383558005883742,16.7442947506152713,75.6507519288807515],"rgb":[1,0.933333333333333348,0.466666666666666674],"xyz":[0.751416958668009,0.837420616759056213,0.296590887925735291],"hpluv":[77.5195453952684659,538.663675570032183,93.3383558005883742],"hsluv":[77.5195453952684659,100.00000000002693,93.3383558005883742]},"#ffee88":{"lch":[93.5319535615141433,69.7063855050026433,76.7254067288761235],"luv":[93.5319535615141433,16.0058531534094222,67.8438858336072599],"rgb":[1,0.933333333333333348,0.533333333333333326],"xyz":[0.762557375769952861,0.841876783599833756,0.355263751329307431],"hpluv":[76.7254067288761235,499.867858345774721,93.5319535615141433],"hsluv":[76.7254067288761235,100.000000000028109,93.5319535615141433]},"#ffee99":{"lch":[93.7579894103796647,60.9203426164438397,75.5807218945013517],"luv":[93.7579894103796647,15.1701259512809905,59.0013171304435744],"rgb":[1,0.933333333333333348,0.6],"xyz":[0.775614339879025505,0.847099569243463,0.424030428970425366],"hpluv":[75.5807218945013517,453.479030486808085,93.7579894103796647],"hsluv":[75.5807218945013517,100.000000000029459,93.7579894103796647]},"#ffeeaa":{"lch":[94.0175103342715204,51.2122166590618946,73.8511258956299],"luv":[94.0175103342715204,14.2438643230739057,49.191497886124175],"rgb":[1,0.933333333333333348,0.66666666666666663],"xyz":[0.790672032844278116,0.853122646429564,0.503334278587424],"hpluv":[73.8511258956299,398.552269307706354,94.0175103342715204],"hsluv":[73.8511258956299,100.00000000003169,94.0175103342715204]},"#ffeebb":{"lch":[94.3113965930375855,40.7102572776027571,71.0277189369888],"luv":[94.3113965930375855,13.2353397134719373,38.4987120599845625],"rgb":[1,0.933333333333333348,0.733333333333333282],"xyz":[0.80780962495161468,0.859977683272498794,0.593592263686065658],"hpluv":[71.0277189369888,333.94796839081863,94.3113965930375855],"hsluv":[71.0277189369888,100.000000000032855,94.3113965930375855]},"#ffeecc":{"lch":[94.6403717602024841,29.6267857036886042,65.7803861656043125],"luv":[94.6403717602024841,12.1539519061426287,27.0190281874729799],"rgb":[1,0.933333333333333348,0.8],"xyz":[0.827102000395336434,0.867694633449987651,0.695198774356335814],"hpluv":[65.7803861656043125,258.601677072085806,94.6403717602024841],"hsluv":[65.7803861656043125,100.000000000035726,94.6403717602024841]},"#ffeedd":{"lch":[95.0050109981125814,18.4911495042727,53.4580761074439366],"luv":[95.0050109981125814,11.0098304435300403,14.8561853648264481],"rgb":[1,0.933333333333333348,0.866666666666666696],"xyz":[0.84862032594597,0.876301963670241157,0.808528622256341811],"hpluv":[53.4580761074439366,173.670551799097524,95.0050109981125814],"hsluv":[53.4580761074439366,100.000000000039279,95.0050109981125814]},"#ffeeee":{"lch":[95.4057483293867,10.0393308083340358,12.1770506300655121],"luv":[95.4057483293867,9.81345048674430487,2.11762900985580904],"rgb":[1,0.933333333333333348,0.933333333333333348],"xyz":[0.872432506212144743,0.885826835776711174,0.933939438324865],"hpluv":[12.1770506300655121,102.829227108855335,95.4057483293867],"hsluv":[12.1770506300655121,100.000000000042746,95.4057483293867]},"#ffeeff":{"lch":[95.8428833991312104,14.017983351086059,307.715012949261848],"luv":[95.8428833991312104,8.57528179129596,-11.0891117512266888],"rgb":[1,0.933333333333333348,1],"xyz":[0.898603554621976164,0.896295255140643898,1.07177362661664688],"hpluv":[307.715012949261848,159.207478793902965,95.8428833991312104],"hsluv":[307.715012949261848,100.000000000047876,95.8428833991312104]},"#aa6600":{"lch":[49.5566255632669623,74.0434564420528574,40.5370999312324685],"luv":[49.5566255632669623,56.2719368465296,48.1238253407432595],"rgb":[0.66666666666666663,0.4,0],"xyz":[0.213283568900016929,0.180499417461687406,0.0236077699189200241],"hpluv":[40.5370999312324685,189.593866720893345,49.5566255632669623],"hsluv":[40.5370999312324685,100.000000000002302,49.5566255632669623]},"#aa6611":{"lch":[49.6055800152373934,72.4782904829715449,39.6736286483530876],"luv":[49.6055800152373934,55.7860674256664097,46.2711278500211662],"rgb":[0.66666666666666663,0.4,0.0666666666666666657],"xyz":[0.214295234399654061,0.180904083661542259,0.0289358748836756538],"hpluv":[39.6736286483530876,185.402990863318706,49.6055800152373934],"hsluv":[39.6736286483530876,96.4267217319678878,49.6055800152373934]},"#aa6622":{"lch":[49.6961357673044404,69.6784302133091,38.0044715349972151],"luv":[49.6961357673044404,54.9040042209680124,42.902610147809348],"rgb":[0.66666666666666663,0.4,0.133333333333333331],"xyz":[0.216170592538131062,0.181654226916933087,0.0388127610796548533],"hpluv":[38.0044715349972151,177.916023719914563,49.6961357673044404],"hsluv":[38.0044715349972151,89.9454244119252593,49.6961357673044404]},"#aa6633":{"lch":[49.8446929303716502,65.3554895773238798,35.0519229290669685],"luv":[49.8446929303716502,53.5020897896209959,37.5348692023167061],"rgb":[0.66666666666666663,0.4,0.2],"xyz":[0.219258343270588796,0.182889327209916186,0.055074914937266],"hpluv":[35.0519229290669685,166.380518722129068,49.8446929303716502],"hsluv":[35.0519229290669685,79.6601301034586,49.8446929303716502]},"#aa6644":{"lch":[50.0579996788189163,59.7471791719702878,30.3078082111971625],"luv":[50.0579996788189163,51.5813401714318687,30.1511320703974341],"rgb":[0.66666666666666663,0.4,0.266666666666666663],"xyz":[0.223716338514807683,0.18467252530760378,0.0785536898901526282],"hpluv":[30.3078082111971625,151.454869534606559,50.0579996788189163],"hsluv":[30.3078082111971625,65.6016329960131,50.0579996788189163]},"#aa6655":{"lch":[50.3411543587309183,53.4384070136854916,23.0123473649378063],"luv":[50.3411543587309183,49.1858121787411307,20.8906492114991877],"rgb":[0.66666666666666663,0.4,0.333333333333333315],"xyz":[0.229678752554169696,0.187057490923348613,0.109955737164126544],"hpluv":[23.0123473649378063,134.70064023894011,50.3411543587309183],"hsluv":[23.0123473649378063,48.1262851503794238,50.3411543587309183]},"#aa6666":{"lch":[50.6979081899742283,47.4599132867605107,12.1770506300620198],"luv":[50.6979081899742283,46.3920870859415544,10.0108753362152232],"rgb":[0.66666666666666663,0.4,0.4],"xyz":[0.237263748327677854,0.190091489232751903,0.149903381571270505],"hpluv":[12.1770506300620198,118.788999996072334,50.6979081899742283],"hsluv":[12.1770506300620198,27.8359446414257086,50.6979081899742283]},"#aa6677":{"lch":[51.1308297914663399,43.3502150416948382,357.146673366158041],"luv":[51.1308297914663399,43.29647113668576,-2.15794607701909591],"rgb":[0.66666666666666663,0.4,0.466666666666666674],"xyz":[0.246577761347879787,0.193817094440832727,0.198957183477668414],"hpluv":[357.146673366158041,107.584013476394517,51.1308297914663399],"hsluv":[357.146673366158041,31.9217683762365354,51.1308297914663399]},"#aa6688":{"lch":[51.6414184020027,42.8134625942885876,339.120025752150127],"luv":[51.6414184020027,40.0018640870324234,-15.2592086582212207],"rgb":[0.66666666666666663,0.4,0.533333333333333326],"xyz":[0.257718178449823587,0.198273261281610325,0.257630046881240582],"hpluv":[339.120025752150127,105.201399585535427,51.6414184020027],"hsluv":[339.120025752150127,36.2396804223311,51.6414184020027]},"#aa6699":{"lch":[52.2302006219705675,46.6703467022409342,321.661853982703349],"luv":[52.2302006219705675,36.6065194780657563,-28.949680361091449],"rgb":[0.66666666666666663,0.4,0.6],"xyz":[0.270775142558896342,0.203496046925239482,0.326396724522358461],"hpluv":[321.661853982703349,113.385797667979,52.2302006219705675],"hsluv":[321.661853982703349,40.6514838109423,52.2302006219705675]},"#aa66aa":{"lch":[52.8968256208086274,54.2656136904176165,307.715012949245079],"luv":[52.8968256208086274,33.1961393674155048,-42.9275338250028753],"rgb":[0.66666666666666663,0.4,0.66666666666666663],"xyz":[0.285832835524148843,0.209519124111340577,0.405700574139357117],"hpluv":[307.715012949245079,130.177052782763241,52.8968256208086274],"hsluv":[307.715012949245079,45.0372955528084091,52.8968256208086274]},"#aa66bb":{"lch":[53.6401644756464293,64.2916348467718848,297.653919381785329],"luv":[53.6401644756464293,29.8396642728746109,-56.9474209016772193],"rgb":[0.66666666666666663,0.4,0.733333333333333282],"xyz":[0.302970427631485517,0.216374160954275335,0.495958559237998808],"hpluv":[297.653919381785329,152.091062924306783,53.6401644756464293],"hsluv":[297.653919381785329,49.3013965401631964,53.6401644756464293]},"#aa66cc":{"lch":[54.4584144535918853,75.6506824974921699,290.576693925999962],"luv":[54.4584144535918853,26.5882539678026326,-70.824363839571447],"rgb":[0.66666666666666663,0.4,0.8],"xyz":[0.322262803075207271,0.224091111131764137,0.597565069908269],"hpluv":[290.576693925999962,176.273563499159138,54.4584144535918853],"hsluv":[290.576693925999962,59.8478295858589107,54.4584144535918853]},"#aa66dd":{"lch":[55.3492064676394619,87.6329338161637423,285.539109011475546],"luv":[55.3492064676394619,23.4765185392287,-84.4297587732275],"rgb":[0.66666666666666663,0.4,0.866666666666666696],"xyz":[0.343781128625840759,0.232698441352017671,0.710894917808275],"hpluv":[285.539109011475546,200.907097340780666,55.3492064676394619],"hsluv":[285.539109011475546,72.9499123576365,55.3492064676394619]},"#aa66ee":{"lch":[56.3097127205812171,99.8160041995885337,281.866291349003632],"luv":[56.3097127205812171,20.525011879724925,-97.6829492885499775],"rgb":[0.66666666666666663,0.4,0.933333333333333348],"xyz":[0.367593308892015525,0.242223313458487716,0.836305733876798185],"hpluv":[281.866291349003632,224.934563930918017,56.3097127205812171],"hsluv":[281.866291349003632,86.3166325899195215,56.3097127205812171]},"#aa66ff":{"lch":[57.3367512293125543,111.956114799504135,279.118878442970129],"luv":[57.3367512293125543,17.7431860168323468,-110.541173283857603],"rgb":[0.66666666666666663,0.4,1],"xyz":[0.39376435730184689,0.252691732822420412,0.97413992216858],"hpluv":[279.118878442970129,247.773048158040382,57.3367512293125543],"hsluv":[279.118878442970129,99.9999999999988631,57.3367512293125543]},"#887700":{"lch":[50.0114915023736586,55.8665567864094825,73.357205010908],"luv":[50.0114915023736586,16.0004093344436384,53.5262465366231766],"rgb":[0.533333333333333326,0.466666666666666674,0],"xyz":[0.167496530942664812,0.184282743437969299,0.0267477295612209565],"hpluv":[73.357205010908,141.749463920516746,50.0114915023736586],"hsluv":[73.357205010908,100.000000000002359,50.0114915023736586]},"#887711":{"lch":[50.0597743565714524,53.993823922257576,73.0031869817398302],"luv":[50.0597743565714524,15.783394234863108,51.6354286142242103],"rgb":[0.533333333333333326,0.466666666666666674,0.0666666666666666657],"xyz":[0.168508196442301944,0.184687409637824151,0.0320758345259765862],"hpluv":[73.0031869817398302,136.865669051735267,50.0597743565714524],"hsluv":[73.0031869817398302,96.4507191106164328,50.0597743565714524]},"#887722":{"lch":[50.1490916772086592,50.5946145563497751,72.2920831328592755],"luv":[50.1490916772086592,15.3890953737340599,48.1974145124374616],"rgb":[0.533333333333333326,0.466666666666666674,0.133333333333333331],"xyz":[0.170383554580778945,0.18543755289321498,0.0419527207219557857],"hpluv":[72.2920831328592755,128.020802989304741,50.1490916772086592],"hsluv":[72.2920831328592755,90.012010730737714,50.1490916772086592]},"#887733":{"lch":[50.2956280558193356,45.1970113165044083,70.9372107781068735],"luv":[50.2956280558193356,14.7615306672340392,42.718462569532349],"rgb":[0.533333333333333326,0.466666666666666674,0.2],"xyz":[0.173471305313236679,0.186672653186198079,0.058214874579566929],"hpluv":[70.9372107781068735,114.029917097227894,50.2956280558193356],"hsluv":[70.9372107781068735,79.7918184760720237,50.2956280558193356]},"#887744":{"lch":[50.5060566549544916,37.8272673959478567,68.4411772520018076],"luv":[50.5060566549544916,13.89986571870684,35.1809023711225777],"rgb":[0.533333333333333326,0.466666666666666674,0.266666666666666663],"xyz":[0.177929300557455566,0.188455851283885673,0.0816936495324535605],"hpluv":[68.4411772520018076,95.0387759917387598,50.5060566549544916],"hsluv":[68.4411772520018076,65.8173762074568316,50.5060566549544916]},"#887755":{"lch":[50.7854328179731453,28.7445471843262688,63.5083223958924279],"luv":[50.7854328179731453,12.8220173934607509,25.7263456945161408],"rgb":[0.533333333333333326,0.466666666666666674,0.333333333333333315],"xyz":[0.183891714596817579,0.190840816899630505,0.113095696806427476],"hpluv":[63.5083223958924279,71.82169329487634,50.7854328179731453],"hsluv":[63.5083223958924279,48.4385690265679756,50.7854328179731453]},"#887766":{"lch":[51.1374932189691549,18.6360854649452037,51.6613245975388082],"luv":[51.1374932189691549,11.5601245482137447,14.6173596072111405],"rgb":[0.533333333333333326,0.466666666666666674,0.4],"xyz":[0.191476710370325737,0.193874815209033796,0.153043341213571438],"hpluv":[51.6613245975388082,46.2439140208651,51.1374932189691549],"hsluv":[51.6613245975388082,28.2492665088726049,51.1374932189691549]},"#887777":{"lch":[51.5648179079599629,10.388802862097231,12.1770506300635812],"luv":[51.5648179079599629,10.1550595801770775,2.19134429758843829],"rgb":[0.533333333333333326,0.466666666666666674,0.466666666666666674],"xyz":[0.20079072339052767,0.197600420417114619,0.202097143119969347],"hpluv":[12.1770506300635812,25.5653264810281158,51.5648179079599629],"hsluv":[12.1770506300635812,5.9907484084339373,51.5648179079599629]},"#887788":{"lch":[52.0689409540354262,14.1414171783799034,307.715012949252923],"luv":[52.0689409540354262,8.65079050214889378,-11.1867557182995974],"rgb":[0.533333333333333326,0.466666666666666674,0.533333333333333326],"xyz":[0.21193114049247147,0.202056587257892217,0.260770006523541542],"hpluv":[307.715012949252923,34.4630346227371902,52.0689409540354262],"hsluv":[307.715012949252923,11.9231603633022036,52.0689409540354262]},"#887799":{"lch":[52.6504441343355154,26.1446518461971777,285.73365348909897],"luv":[52.6504441343355154,7.08953673880610147,-25.1650807467009763],"rgb":[0.533333333333333326,0.466666666666666674,0.6],"xyz":[0.224988104601544198,0.207279372901521375,0.329536684164659421],"hpluv":[285.73365348909897,63.0115482025775862,52.6504441343355154],"hsluv":[285.73365348909897,20.7849368541844512,52.6504441343355154]},"#8877aa":{"lch":[53.3090485775123142,39.8165287152511951,277.951975125090712],"luv":[53.3090485775123142,5.50833871717125412,-39.4336678931764482],"rgb":[0.533333333333333326,0.466666666666666674,0.66666666666666663],"xyz":[0.240045797566796726,0.213302450087622469,0.408840533781658],"hpluv":[277.951975125090712,94.7767450693341118,53.3090485775123142],"hsluv":[277.951975125090712,33.2193336653547036,53.3090485775123142]},"#8877bb":{"lch":[54.043710164283695,53.8841311835491,274.190220195010625],"luv":[54.043710164283695,3.93720379281895738,-53.7400969453883732],"rgb":[0.533333333333333326,0.466666666666666674,0.733333333333333282],"xyz":[0.2571833896741334,0.220157486930557228,0.499098518880299769],"hpluv":[274.190220195010625,126.518797380186697,54.043710164283695],"hsluv":[274.190220195010625,46.027457746288988,54.043710164283695]},"#8877cc":{"lch":[54.8527197178713095,67.9360625264533695,272.023364737824068],"luv":[54.8527197178713095,2.39862107173988059,-67.8937052203839215],"rgb":[0.533333333333333326,0.466666666666666674,0.8],"xyz":[0.276475765117855099,0.22787443710804603,0.600705029550569924],"hpluv":[272.023364737824068,157.159823716941304,54.8527197178713095],"hsluv":[272.023364737824068,59.1220253911564413,54.8527197178713095]},"#8877dd":{"lch":[55.7338064566187228,81.7667290312133,270.636302714775695],"luv":[55.7338064566187228,0.90804807322034331,-81.7616867803042595],"rgb":[0.533333333333333326,0.466666666666666674,0.866666666666666696],"xyz":[0.297994090668488698,0.236481767328299564,0.714034877450575922],"hpluv":[270.636302714775695,186.164661983392193,55.7338064566187228],"hsluv":[270.636302714775695,72.4679508704990809,55.7338064566187228]},"#8877ee":{"lch":[56.6842419440431939,95.2620356561917419,269.684203716511661],"luv":[56.6842419440431939,-0.525051666249150784,-95.2605886928551229],"rgb":[0.533333333333333326,0.466666666666666674,0.933333333333333348],"xyz":[0.321806270934663408,0.246006639434769608,0.839445693519099145],"hpluv":[269.684203716511661,213.253830698535069,56.6842419440431939],"hsluv":[269.684203716511661,86.076772525698,56.6842419440431939]},"#8877ff":{"lch":[57.7009414002340577,108.362660778640901,268.997474997449615],"luv":[57.7009414002340577,-1.89596396936046907,-108.346073171359961],"rgb":[0.533333333333333326,0.466666666666666674,1],"xyz":[0.347977319344494829,0.256475058798702304,0.977279881810881],"hpluv":[268.997474997449615,238.306609639193027,57.7009414002340577],"hsluv":[268.997474997449615,99.9999999999988631,57.7009414002340577]},"#ffff00":{"lch":[97.1385593417967357,107.085608846920664,85.8743202181747307],"luv":[97.1385593417967357,7.70421917727499928,106.808111250898],"rgb":[1,1,0],"xyz":[0.76997513864982,0.92780768463926,0.138525598510210984],"hpluv":[85.8743202181747307,1784.23591835690763,97.1385593417967357],"hsluv":[85.8743202181747307,100.000000000072717,97.1385593417967357]},"#ffff11":{"lch":[97.1550055288865337,106.340968495662651,85.8743202181747307],"luv":[97.1550055288865337,7.65064640931757278,106.065400532478591],"rgb":[1,1,0.0666666666666666657],"xyz":[0.770986804149457194,0.928212350839114908,0.143853703474966621],"hpluv":[85.8743202181747307,1782.29032599077573,97.1550055288865337],"hsluv":[85.8743202181747307,100.000000000072447,97.1550055288865337]},"#ffff22":{"lch":[97.1854797367251564,104.966044999604463,85.8743202181747],"luv":[97.1854797367251564,7.5517282439387623,104.694039961158666],"rgb":[1,1,0.133333333333333331],"xyz":[0.77286216228793414,0.928962494094505709,0.1537305896709458],"hpluv":[85.8743202181747,1778.69938503976459,97.1854797367251564],"hsluv":[85.8743202181747,100.00000000007401,97.1854797367251564]},"#ffff33":{"lch":[97.2356193677236291,102.717517786777336,85.8743202181746312],"luv":[97.2356193677236291,7.38995910744871409,102.451339496695468],"rgb":[1,1,0.2],"xyz":[0.775949913020392,0.930197594387488835,0.16999274352855695],"hpluv":[85.8743202181746312,1772.83090468185333,97.2356193677236291],"hsluv":[85.8743202181746312,100.000000000075445,97.2356193677236291]},"#ffff44":{"lch":[97.3079311184623776,99.5042093292491,85.874320218174546],"luv":[97.3079311184623776,7.15877927938833114,99.2463578851537704],"rgb":[1,1,0.266666666666666663],"xyz":[0.780407908264610817,0.931980792485176401,0.193471518481443588],"hpluv":[85.874320218174546,1764.45330998562531,97.3079311184623776],"hsluv":[85.874320218174546,100.000000000077918,97.3079311184623776]},"#ffff55":{"lch":[97.4045015397841212,95.2663481722239283,85.8743202181744323],"luv":[97.4045015397841212,6.8538885331141568,95.0194785612246875],"rgb":[1,1,0.333333333333333315],"xyz":[0.786370322303972746,0.934365758100921151,0.224873565755417504],"hpluv":[85.8743202181744323,1753.42077174454698,97.4045015397841212],"hsluv":[85.8743202181744323,100.000000000080163,97.4045015397841212]},"#ffff66":{"lch":[97.5271149532436539,89.9715947326486258,85.8743202181742333],"luv":[97.5271149532436539,6.47296021391862286,89.7384457454272706],"rgb":[1,1,0.4],"xyz":[0.793955318077481,0.93739975641032447,0.264821210162561438],"hpluv":[85.8743202181742333,1739.66322518688298,97.5271149532436539],"hsluv":[85.8743202181742333,100.000000000084981,97.5271149532436539]},"#ffff77":{"lch":[97.6773170086398608,83.6127156419164663,85.8743202181740202],"luv":[97.6773170086398608,6.01547392080898469,83.3960448134325389],"rgb":[1,1,0.466666666666666674],"xyz":[0.803269331097682837,0.941125361618405321,0.313875012068959403],"hpluv":[85.8743202181740202,1723.18045161093028,97.6773170086398608],"hsluv":[85.8743202181740202,100.00000000009112,97.6773170086398608]},"#ffff88":{"lch":[97.8564527859654589,76.2055692953657342,85.8743202181736791],"luv":[97.8564527859654589,5.48257057790026181,76.0080930657330214],"rgb":[1,1,0.533333333333333326],"xyz":[0.814409748199626748,0.945581528459182863,0.372547875472531542],"hpluv":[85.8743202181736791,1704.03672017478311,97.8564527859654589],"hsluv":[85.8743202181736791,100.000000000099803,97.8564527859654589]},"#ffff99":{"lch":[98.0656913545514612,67.7868897983338741,85.8743202181732102],"luv":[98.0656913545514612,4.87689300155069283,67.6112294162950889],"rgb":[1,1,0.6],"xyz":[0.827466712308699393,0.950804314102812076,0.441314553113649422],"hpluv":[85.8743202181732102,1682.35465810463256,98.0656913545514612],"hsluv":[85.8743202181732102,100.000000000112891,98.0656913545514612]},"#ffffaa":{"lch":[98.3060425431328611,58.4116937234916094,85.8743202181725707],"luv":[98.3060425431328611,4.20239933084915052,58.260327869924204],"rgb":[1,1,0.66666666666666663],"xyz":[0.842524405273952,0.956827391288913143,0.520618402730648078],"hpluv":[85.8743202181725707,1658.30791632356272,98.3060425431328611],"hsluv":[85.8743202181725707,100.000000000127613,98.3060425431328611]},"#ffffbb":{"lch":[98.5783690162300559,48.1503065934375414,85.8743202181715759],"luv":[98.5783690162300559,3.46414909943131732,48.0255317103199246],"rgb":[1,1,0.733333333333333282],"xyz":[0.859661997381288567,0.963682428131847901,0.610876387829289769],"hpluv":[85.8743202181715759,1632.1126639545671,98.5783690162300559],"hsluv":[85.8743202181715759,100.000000000152809,98.5783690162300559]},"#ffffcc":{"lch":[98.8833954570195317,37.0851031688938804,85.8743202181698706],"luv":[98.8833954570195317,2.66806871718659799,36.9890022353654899],"rgb":[1,1,0.8],"xyz":[0.878954372825010322,0.971399378309336758,0.712482898499559925],"hpluv":[85.8743202181698706,1604.018210645404,98.8833954570195317],"hsluv":[85.8743202181698706,100.00000000019709,98.8833954570195317]},"#ffffdd":{"lch":[99.2217159651800245,25.3071072074552177,85.8743202181663889],"luv":[99.2217159651800245,1.82070684164607655,25.2415273271332552],"rgb":[1,1,0.866666666666666696],"xyz":[0.900472698375643921,0.980006708529590265,0.825812746399565922],"hpluv":[85.8743202181663889,1574.29719653830034,99.2217159651800245],"hsluv":[85.8743202181663889,100.000000000286278,99.2217159651800245]},"#ffffee":{"lch":[99.5938003805277248,12.9126149352850259,85.8743202181558161],"luv":[99.5938003805277248,0.928991455386458775,12.8791536733888243],"rgb":[1,1,0.933333333333333348],"xyz":[0.92428487864181863,0.989531580636060282,0.951223562468089145],"hpluv":[85.8743202181558161,1543.23583838085528,99.5938003805277248],"hsluv":[85.8743202181558161,100.000000000556355,99.5938003805277248]},"#ffffff":{"lch":[99.99999999999973,5.29610712429325706e-12,0],"luv":[99.99999999999973,4.97935026544381416e-12,1.80411241501587473e-12],"rgb":[1,1,1],"xyz":[0.95045592705165,0.999999999999993,1.0890577507598711],"hpluv":[0,0,100],"hsluv":[0,0,100]},"#aa7700":{"lch":[53.7507838912622304,69.116848270999057,51.9676330333141223],"luv":[53.7507838912622304,42.5833417137676875,54.4407726194696622],"rgb":[0.66666666666666663,0.466666666666666674,0],"xyz":[0.231737306953434558,0.217406893568523163,0.0297590159367257245],"hpluv":[51.9676330333141223,163.169299961930307,53.7507838912622304],"hsluv":[51.9676330333141223,100.000000000002359,53.7507838912622304]},"#aa7711":{"lch":[53.7940335015026,67.5786316453491906,51.3090056740019378],"luv":[53.7940335015026,42.244752971039,52.7470596476588653],"rgb":[0.66666666666666663,0.466666666666666674,0.0666666666666666657],"xyz":[0.23274897245307169,0.217811559768378016,0.0350871209014813543],"hpluv":[51.3090056740019378,159.40965108533166,53.7940335015026],"hsluv":[51.3090056740019378,97.0120153186068,53.7940335015026]},"#aa7722":{"lch":[53.874065271669366,64.7981771198060272,50.0272013346139],"luv":[53.874065271669366,41.6278947732915157,49.6580520640260588],"rgb":[0.66666666666666663,0.466666666666666674,0.133333333333333331],"xyz":[0.234624330591548691,0.218561703023768844,0.0449640070974605538],"hpluv":[50.0272013346139,152.623836787181489,53.874065271669366],"hsluv":[50.0272013346139,91.5730337049880632,53.874065271669366]},"#aa7733":{"lch":[54.0054384284815,60.4215101577181812,47.7291529037971785],"luv":[54.0054384284815,40.6416884431496115,44.7103125713654705],"rgb":[0.66666666666666663,0.466666666666666674,0.2],"xyz":[0.237712081324006452,0.219796803316751943,0.0612261609550717],"hpluv":[47.7291529037971785,141.968961696257139,54.0054384284815],"hsluv":[47.7291529037971785,82.890357503842381,54.0054384284815]},"#aa7744":{"lch":[54.1942453736720324,54.5501661616690754,43.9413891432679051],"luv":[54.1942453736720324,39.2788485799816911,37.8535689532252277],"rgb":[0.66666666666666663,0.466666666666666674,0.266666666666666663],"xyz":[0.242170076568225312,0.221580001414439537,0.0847049359079583286],"hpluv":[43.9413891432679051,127.726858599491337,54.1942453736720324],"hsluv":[43.9413891432679051,70.9191698542686453,54.1942453736720324]},"#aa7755":{"lch":[54.4451912879813307,47.55778496198932,37.8352816528713],"luv":[54.4451912879813307,37.5600659479400178,29.1716361638701045],"rgb":[0.66666666666666663,0.466666666666666674,0.333333333333333315],"xyz":[0.248132490607587297,0.22396496703018437,0.116106983181932244],"hpluv":[37.8352816528713,110.841250641501858,54.4451912879813307],"hsluv":[37.8352816528713,55.8697139193963039,54.4451912879813307]},"#aa7766":{"lch":[54.7618668139504621,40.2221891209492881,27.9562221290694595],"luv":[54.7618668139504621,35.5285027246357359,18.8560333004309904],"rgb":[0.66666666666666663,0.466666666666666674,0.4],"xyz":[0.255717486381095482,0.226998965339587661,0.156054627589076206],"hpluv":[27.9562221290694595,93.2023334238246264,54.7618668139504621],"hsluv":[27.9562221290694595,38.157009054435612,54.7618668139504621]},"#aa7777":{"lch":[55.1468928183874851,34.0080558607991321,12.1770506300622881],"luv":[55.1468928183874851,33.2428902595124569,7.17343088244713467],"rgb":[0.66666666666666663,0.466666666666666674,0.466666666666666674],"xyz":[0.265031499401297388,0.230724570547668484,0.205108429495474115],"hpluv":[12.1770506300622881,78.2528356679829074,55.1468928183874851],"hsluv":[12.1770506300622881,20.6006796366476941,55.1468928183874851]},"#aa7788":{"lch":[55.6020140468043849,31.2700131437067519,349.739442339375785],"luv":[55.6020140468043849,30.7699397165362107,-5.56996695217490778],"rgb":[0.66666666666666663,0.466666666666666674,0.533333333333333326],"xyz":[0.276171916503241244,0.235180737388446082,0.263781292899046282],"hpluv":[349.739442339375785,71.363619208349732,55.6020140468043849],"hsluv":[349.739442339375785,23.0952607722130772,55.6020140468043849]},"#aa7799":{"lch":[56.1281730235999845,34.0192547961324365,325.92167501088062],"luv":[56.1281730235999845,28.1772086346441704,-19.0618627223027133],"rgb":[0.66666666666666663,0.466666666666666674,0.6],"xyz":[0.289228880612313943,0.240403523032075239,0.332547970540164162],"hpluv":[325.92167501088062,76.9100717288581706,56.1281730235999845],"hsluv":[325.92167501088062,27.5449434619861648,56.1281730235999845]},"#aa77aa":{"lch":[56.7255784680210127,41.7295245342496131,307.715012949246272],"luv":[56.7255784680210127,25.5273831431779215,-33.0106941416859],"rgb":[0.66666666666666663,0.466666666666666674,0.66666666666666663],"xyz":[0.304286573577566499,0.246426600218176334,0.411851820157162818],"hpluv":[307.715012949246272,93.3477446513022642,56.7255784680210127],"hsluv":[307.715012949246272,32.2954766233998285,56.7255784680210127]},"#aa77bb":{"lch":[57.3937746480490176,52.417961949749369,295.873528074876958],"luv":[57.3937746480490176,22.8744713506946127,-47.1635589771554891],"rgb":[0.66666666666666663,0.466666666666666674,0.733333333333333282],"xyz":[0.321424165684903118,0.25328163706111112,0.502109805255804509],"hpluv":[295.873528074876958,115.892323381948941,57.3937746480490176],"hsluv":[295.873528074876958,41.7229864085831679,57.3937746480490176]},"#aa77cc":{"lch":[58.1317139736185,64.5762938068646548,288.286403622885132],"luv":[58.1317139736185,20.2619194712353625,-61.3151885031081],"rgb":[0.66666666666666663,0.466666666666666674,0.8],"xyz":[0.340716541128624872,0.260998587238599922,0.603716315926074665],"hpluv":[288.286403622885132,140.961111207958226,58.1317139736185],"hsluv":[288.286403622885132,55.678510160825347,58.1317139736185]},"#aa77dd":{"lch":[58.9378328182195759,77.3666791836153,283.242275299631103],"luv":[58.9378328182195759,17.7223199365118553,-75.3095108466943088],"rgb":[0.66666666666666663,0.466666666666666674,0.866666666666666696],"xyz":[0.362234866679258416,0.269605917458853428,0.717046163826080662],"hpluv":[283.242275299631103,166.570895286205939,58.9378328182195759],"hsluv":[283.242275299631103,70.0408478070886105,58.9378328182195759]},"#aa77ee":{"lch":[59.8101292792768646,90.3373867943789151,279.736895248195651],"luv":[59.8101292792768646,15.2782276108220607,-89.0360556960445138],"rgb":[0.66666666666666663,0.466666666666666674,0.933333333333333348],"xyz":[0.386047046945433125,0.279130789565323445,0.842456979894603886],"hpluv":[279.736895248195651,191.660275847677298,59.8101292792768646],"hsluv":[279.736895248195651,84.8029379630096685,59.8101292792768646]},"#aa77ff":{"lch":[60.7462409754246551,103.238062985892157,277.202485092995744],"luv":[60.7462409754246551,12.9436026905507742,-102.423438716283115],"rgb":[0.66666666666666663,0.466666666666666674,1],"xyz":[0.412218095355264547,0.289599208929256169,0.980291168186385731],"hpluv":[277.202485092995744,215.655115976047284,60.7462409754246551],"hsluv":[277.202485092995744,99.999999999998721,60.7462409754246551]},"#888800":{"lch":[54.9099926918455452,60.532810441385358,85.8743202181747449],"luv":[54.9099926918455452,4.35500198466006783,60.375948006191166],"rgb":[0.533333333333333326,0.533333333333333326,0],"xyz":[0.189568900667635265,0.228427482887910871,0.0341051861362109063],"hpluv":[85.8743202181747449,139.887458074797593,54.9099926918455452],"hsluv":[85.8743202181747449,100.000000000002331,54.9099926918455452]},"#888811":{"lch":[54.9518410557904673,58.8347385736240369,85.8743202181746739],"luv":[54.9518410557904673,4.23283507550337568,58.6822764576347353],"rgb":[0.533333333333333326,0.533333333333333326,0.0666666666666666657],"xyz":[0.190580566167272397,0.228832149087765724,0.039433291100966536],"hpluv":[85.8743202181746739,135.85978011465275,54.9518410557904673],"hsluv":[85.8743202181746739,97.1207726442580395,54.9518410557904673]},"#888822":{"lch":[55.0292864560463215,55.7361292450240882,85.8743202181745602],"luv":[55.0292864560463215,4.00990721741558787,55.5916967480373643],"rgb":[0.533333333333333326,0.533333333333333326,0.133333333333333331],"xyz":[0.192455924305749398,0.229582292343156552,0.0493101772969457355],"hpluv":[85.8743202181745602,128.523412903997382,55.0292864560463215],"hsluv":[85.8743202181745602,91.8762944675706,55.0292864560463215]},"#888833":{"lch":[55.1564325013520573,50.7686053645684225,85.8743202181742902],"luv":[55.1564325013520573,3.65252126093227947,50.6370455210582335],"rgb":[0.533333333333333326,0.533333333333333326,0.2],"xyz":[0.195543675038207132,0.230817392636139651,0.0655723311545568788],"hpluv":[85.8743202181742902,116.798802852822334,55.1564325013520573],"hsluv":[85.8743202181742902,83.4948353914423,55.1564325013520573]},"#888844":{"lch":[55.3392041906722767,43.8756115710196184,85.8743202181737786],"luv":[55.3392041906722767,3.15660835961073616,43.7619139708837039],"rgb":[0.533333333333333326,0.533333333333333326,0.266666666666666663],"xyz":[0.20000167028242602,0.232600590733827245,0.0890511061074435173],"hpluv":[85.8743202181737786,100.607324583255647,55.3392041906722767],"hsluv":[85.8743202181737786,71.9201892491773833,55.3392041906722767]},"#888855":{"lch":[55.5822005995452173,35.1333862553221152,85.8743202181729],"luv":[55.5822005995452173,2.5276534453655044,35.0423429271758167],"rgb":[0.533333333333333326,0.533333333333333326,0.333333333333333315],"xyz":[0.205964084321788032,0.234985556349572078,0.120453153381417433],"hpluv":[85.8743202181729,80.2090919262666233,55.5822005995452173],"hsluv":[85.8743202181729,57.3383011101545321,55.5822005995452173]},"#888866":{"lch":[55.8889601924437187,24.7258905438507242,85.874320218171],"luv":[55.8889601924437187,1.778891507033598,24.6618168064052092],"rgb":[0.533333333333333326,0.533333333333333326,0.4],"xyz":[0.21354908009529619,0.238019554658975369,0.160400797788561394],"hpluv":[85.874320218171,56.1390732800859524,55.8889601924437187],"hsluv":[85.874320218171,40.1315986813270698,55.8889601924437187]},"#888877":{"lch":[56.2621011123828509,12.9137749110131566,85.8743202181651668],"luv":[56.2621011123828509,0.929074909242871283,12.8803106431998842],"rgb":[0.533333333333333326,0.533333333333333326,0.466666666666666674],"xyz":[0.222863093115498123,0.241745159867056192,0.209454599694959304],"hpluv":[85.8743202181651668,29.1257147579972724,56.2621011123828509],"hsluv":[85.8743202181651668,20.8208192205656601,56.2621011123828509]},"#888888":{"lch":[56.703410756754252,2.95076376078202623e-12,0],"luv":[56.703410756754252,2.78254170310414444e-12,9.82073542272051e-13],"rgb":[0.533333333333333326,0.533333333333333326,0.533333333333333326],"xyz":[0.234003510217441923,0.24620132670783379,0.268127463098531471],"hpluv":[0,6.60335407213460764e-12,56.703410756754252],"hsluv":[0,2.14018342731852893e-12,56.703410756754252]},"#888899":{"lch":[57.2139150634865246,13.7029898302256612,265.874320218188814],"luv":[57.2139150634865246,-0.985854571612734376,-13.6674804207248872],"rgb":[0.533333333333333326,0.533333333333333326,0.6],"xyz":[0.247060474326514651,0.251424112351462947,0.33689414073964935],"hpluv":[265.874320218188814,30.3915601408835876,57.2139150634865246],"hsluv":[265.874320218188814,12.5386286039598396,57.2139150634865246]},"#8888aa":{"lch":[57.7939415002624486,27.9001972781706051,265.874320218182902],"luv":[57.7939415002624486,-2.00726537612613365,-27.8278977623291475],"rgb":[0.533333333333333326,0.533333333333333326,0.66666666666666663],"xyz":[0.262118167291767179,0.25744718953756407,0.416197990356647951],"hpluv":[265.874320218182902,61.2582077856443377,57.7939415002624486],"hsluv":[265.874320218182902,25.8334660761224093,57.7939415002624486]},"#8888bb":{"lch":[58.4431822360017605,42.3326731508362428,265.874320218181083],"luv":[58.4431822360017605,-3.04560244672749647,-42.2229738629578222],"rgb":[0.533333333333333326,0.533333333333333326,0.733333333333333282],"xyz":[0.279255759399103853,0.264302226380498828,0.506455975455289753],"hpluv":[265.874320218181083,91.9138937804104756,58.4431822360017605],"hsluv":[265.874320218181083,39.7348050695490116,58.4431822360017605]},"#8888cc":{"lch":[59.1607600358786812,56.7874726838639603,265.874320218180117],"luv":[59.1607600358786812,-4.0855455816182138,-56.6403157752595448],"rgb":[0.533333333333333326,0.533333333333333326,0.8],"xyz":[0.298548134842825608,0.27201917655798763,0.608062486125559909],"hpluv":[265.874320218180117,121.803038601679276,59.1607600358786812],"hsluv":[265.874320218180117,54.1372084350884322,59.1607600358786812]},"#8888dd":{"lch":[59.9452971965242654,71.1002375720468649,265.874320218179605],"luv":[59.9452971965242654,-5.11527010687093497,-70.9159911059223447],"rgb":[0.533333333333333326,0.533333333333333326,0.866666666666666696],"xyz":[0.320066460393459096,0.280626506778241136,0.721392334025565907],"hpluv":[265.874320218179605,150.506501481916018,59.9452971965242654],"hsluv":[265.874320218179605,68.9826297466640881,59.9452971965242654]},"#8888ee":{"lch":[60.7949865781877747,85.1524606014505,265.874320218179207],"luv":[60.7949865781877747,-6.12625008179143,-84.9317997361231676],"rgb":[0.533333333333333326,0.533333333333333326,0.933333333333333348],"xyz":[0.343878640659633861,0.290151378884711153,0.84680315009408913],"hpluv":[265.874320218179207,177.733282428962553,60.7949865781877747],"hsluv":[265.874320218179207,84.2595641984559194,60.7949865781877747]},"#8888ff":{"lch":[61.7076631467729726,98.8655769196339,265.874320218179],"luv":[61.7076631467729726,-7.11283319838656247,-98.6093804034077408],"rgb":[0.533333333333333326,0.533333333333333326,1],"xyz":[0.370049689069465226,0.300619798248643877,0.984637338385871],"hpluv":[265.874320218179,203.303722842755434,61.7076631467729726],"hsluv":[265.874320218179,99.9999999999986073,61.7076631467729726]},"#aa8800":{"lch":[58.1840377660698493,67.6904417424552634,64.2288134226940173],"luv":[58.1840377660698493,29.4303340948507071,60.9577832467208225],"rgb":[0.66666666666666663,0.533333333333333326,0],"xyz":[0.253809676678405038,0.261551633018464735,0.0371164725117156744],"hpluv":[64.2288134226940173,147.625988392398114,58.1840377660698493],"hsluv":[64.2288134226940173,100.000000000002373,58.1840377660698493]},"#aa8811":{"lch":[58.2222766199063955,66.2027965316335809,63.8264905250604926],"luv":[58.2222766199063955,29.2014551434886229,59.4145208354969085],"rgb":[0.66666666666666663,0.533333333333333326,0.0666666666666666657],"xyz":[0.254821342178042143,0.261956299218319588,0.0424445774764713041],"hpluv":[63.8264905250604926,144.286759049554206,58.2222766199063955],"hsluv":[63.8264905250604926,97.5015111084285877,58.2222766199063955]},"#aa8822":{"lch":[58.2930572278629,63.4916551128893474,63.0419050459203],"luv":[58.2930572278629,28.7832252220538578,56.5925455761676304],"rgb":[0.66666666666666663,0.533333333333333326,0.133333333333333331],"xyz":[0.2566967003165192,0.262706442473710389,0.0523214636724505036],"hpluv":[63.0419050459203,138.209896631886636,58.2930572278629],"hsluv":[63.0419050459203,92.9399945222758106,58.2930572278629]},"#aa8833":{"lch":[58.4093035212624585,59.1585039849960737,61.628621374019076],"luv":[58.4093035212624585,28.1112178941065629,52.0527426967384628],"rgb":[0.66666666666666663,0.533333333333333326,0.2],"xyz":[0.259784451048976905,0.263941542766693515,0.0685836175300616468],"hpluv":[61.628621374019076,128.521114132395894,58.4093035212624585],"hsluv":[61.628621374019076,85.621599137751673,58.4093035212624585]},"#aa8844":{"lch":[58.5764981609594315,53.1878639814319953,59.2736460930020499],"luv":[58.5764981609594315,27.1757201885359478,45.7212106919940453],"rgb":[0.66666666666666663,0.533333333333333326,0.266666666666666663],"xyz":[0.264242446293195821,0.265724740864381082,0.0920623924829482854],"hpluv":[59.2736460930020499,115.22015920662389,58.5764981609594315],"hsluv":[59.2736460930020499,75.4571473133808581,58.5764981609594315]},"#aa8855":{"lch":[58.7989500318507083,45.736161182952344,55.3795129841665315],"luv":[58.7989500318507083,25.9844527075537677,37.6378088793954433],"rgb":[0.66666666666666663,0.533333333333333326,0.333333333333333315],"xyz":[0.270204860332557806,0.268109706480125942,0.123464439756922201],"hpluv":[55.3795129841665315,98.7027982667044483,58.7989500318507083],"hsluv":[55.3795129841665315,62.5553187882680319,58.7989500318507083]},"#aa8866":{"lch":[59.0800404303715112,37.1980666756204,48.681601920545944],"luv":[59.0800404303715112,24.5597583059268025,27.9376884576795419],"rgb":[0.66666666666666663,0.533333333333333326,0.4],"xyz":[0.277789856106066,0.271143704789529261,0.163412084164066163],"hpluv":[48.681601920545944,79.8948730878687883,59.0800404303715112],"hsluv":[48.681601920545944,47.1909371341698645,59.0800404303715112]},"#aa8877":{"lch":[59.4223523155875881,28.4467220316745042,36.2691810942760355],"luv":[59.4223523155875881,22.9350730451589406,16.8285001934390941],"rgb":[0.66666666666666663,0.533333333333333326,0.466666666666666674],"xyz":[0.287103869126267897,0.274869309997610056,0.212465886070464072],"hpluv":[36.2691810942760355,60.7465636644032,59.4223523155875881],"hsluv":[36.2691810942760355,29.7643761063162415,59.4223523155875881]},"#aa8888":{"lch":[59.8277504540149323,21.6376696880998622,12.1770506300627677],"luv":[59.8277504540149323,21.1508320810015036,4.56410471095846049],"rgb":[0.66666666666666663,0.533333333333333326,0.533333333333333326],"xyz":[0.298244286228211697,0.279325476838387654,0.271138749474036211],"hpluv":[12.1770506300627677,45.8930730764781174,59.8277504540149323],"hsluv":[12.1770506300627677,15.9793094134510145,59.8277504540149323]},"#aa8899":{"lch":[60.2974403890441693,21.076671863141442,335.972081494736813],"luv":[60.2974403890441693,19.2503183610400761,-8.58203587880762697],"rgb":[0.66666666666666663,0.533333333333333326,0.6],"xyz":[0.311301250337284396,0.284548262482016812,0.339905427115154146],"hpluv":[335.972081494736813,44.3549898137704872,60.2974403890441693],"hsluv":[335.972081494736813,18.3674189175529285,60.2974403890441693]},"#aa88aa":{"lch":[60.8320193568852119,28.2409959286201691,307.715012949248376],"luv":[60.8320193568852119,17.2759870010776844,-22.3404145928166677],"rgb":[0.66666666666666663,0.533333333333333326,0.66666666666666663],"xyz":[0.326358943302536952,0.290571339668117934,0.419209276732152747],"hpluv":[307.715012949248376,58.9097393716334068,60.8320193568852119],"hsluv":[307.715012949248376,20.7885743891348937,60.8320193568852119]},"#aa88bb":{"lch":[61.4315255818646904,39.5305448534603343,292.718173175904553],"luv":[61.4315255818646904,15.2666424694527869,-36.4635928581008],"rgb":[0.66666666666666663,0.533333333333333326,0.733333333333333282],"xyz":[0.343496535409873627,0.297426376511052692,0.509467261830794493],"hpluv":[292.718173175904553,81.6546320542196185,61.4315255818646904],"hsluv":[292.718173175904553,35.3775178816614826,61.4315255818646904]},"#aa88cc":{"lch":[62.0954889075932783,52.4408733717370339,284.64164926901276],"luv":[62.0954889075932783,13.2556230251873277,-50.737891739849438],"rgb":[0.66666666666666663,0.533333333333333326,0.8],"xyz":[0.362788910853595326,0.305143326688541494,0.611073772501064649],"hpluv":[284.64164926901276,107.164068097081099,62.0954889075932783],"hsluv":[284.64164926901276,50.6369924233865092,62.0954889075932783]},"#aa88dd":{"lch":[62.8229837406334894,65.9589053782665644,279.83800714750987],"luv":[62.8229837406334894,11.2699451472508105,-64.988964717689413],"rgb":[0.66666666666666663,0.533333333333333326,0.866666666666666696],"xyz":[0.384307236404228925,0.313750656908795,0.724403620401070647],"hpluv":[279.83800714750987,133.227600809414753,62.8229837406334894],"hsluv":[279.83800714750987,66.50088929558828,62.8229837406334894]},"#aa88ee":{"lch":[63.6126841134072,79.6308922643202237,276.728713669089302],"luv":[63.6126841134072,9.33022805769535,-79.0823990986816767],"rgb":[0.66666666666666663,0.533333333333333326,0.933333333333333348],"xyz":[0.408119416670403634,0.323275529015265,0.84981443646959387],"hpluv":[276.728713669089302,158.846330872338797,63.6126841134072],"hsluv":[276.728713669089302,82.9476730407324112,63.6126841134072]},"#aa88ff":{"lch":[64.4629200033750323,93.219110130063271,274.584640952303687],"luv":[64.4629200033750323,7.4511637136690565,-92.9208407880221756],"rgb":[0.66666666666666663,0.533333333333333326,1],"xyz":[0.434290465080235055,0.333743948379197741,0.987648624761375715],"hpluv":[274.584640952303687,183.499254977583263,64.4629200033750323],"hsluv":[274.584640952303687,99.999999999998451,64.4629200033750323]},"#889900":{"lch":[59.9037942457991477,67.5360782410098892,95.4734085527772578],"luv":[59.9037942457991477,-6.44184579214223074,67.2281524881211396],"rgb":[0.533333333333333326,0.6,0],"xyz":[0.215438501120102766,0.280166683792846538,0.0427283862870331613],"hpluv":[95.4734085527772578,143.060860652479761,59.9037942457991477],"hsluv":[95.4734085527772578,100.000000000002359,59.9037942457991477]},"#889911":{"lch":[59.9403212197486,66.0293008977046867,95.6505578181906628],"luv":[59.9403212197486,-6.50131434341495318,65.7084582747741166],"rgb":[0.533333333333333326,0.6,0.0666666666666666657],"xyz":[0.216450166619739898,0.280571349992701391,0.048056491251788791],"hpluv":[95.6505578181906628,139.783837686775883,59.9403212197486],"hsluv":[95.6505578181906628,97.642419329775592,59.9403212197486]},"#889922":{"lch":[60.0079397017320275,63.2737960078627495,95.9966659299995655],"luv":[60.0079397017320275,-6.6102508868722456,62.9275602932231308],"rgb":[0.533333333333333326,0.6,0.133333333333333331],"xyz":[0.218325524758216899,0.281321493248092191,0.0579333774477679905],"hpluv":[95.9966659299995655,133.799503075015934,60.0079397017320275],"hsluv":[95.9966659299995655,93.3344688530494864,60.0079397017320275]},"#889933":{"lch":[60.1190111745068521,58.8410136656902196,96.6225595920901696],"luv":[60.1190111745068521,-6.78603238946032139,58.4483930798370039],"rgb":[0.533333333333333326,0.6,0.2],"xyz":[0.221413275490674633,0.282556593541075318,0.0741955313053791338],"hpluv":[96.6225595920901696,124.196009967059197,60.1190111745068521],"hsluv":[96.6225595920901696,86.4129060235578,60.1190111745068521]},"#889944":{"lch":[60.2788030378330859,52.6619605596060651,97.6740140778758104],"luv":[60.2788030378330859,-7.03230554676784081,52.1903129773946],"rgb":[0.533333333333333326,0.6,0.266666666666666663],"xyz":[0.22587127073489352,0.284339791638762884,0.0976743062582657723],"hpluv":[97.6740140778758104,110.859197411007315,60.2788030378330859],"hsluv":[97.6740140778758104,76.7791514013919,60.2788030378330859]},"#889955":{"lch":[60.491478208304315,44.7890969420590679,99.4433583384469557],"luv":[60.491478208304315,-7.34865909500015935,44.1821277711999301],"rgb":[0.533333333333333326,0.6,0.333333333333333315],"xyz":[0.231833684774255533,0.286724757254507745,0.129076353532239674],"hpluv":[99.4433583384469557,93.954467458231008,60.491478208304315],"hsluv":[99.4433583384469557,64.5165507795669555,60.491478208304315]},"#889966":{"lch":[60.7603321241253269,35.3998784596055387,102.614945913325585],"luv":[60.7603321241253269,-7.73125582812020351,34.5453191948634597],"rgb":[0.533333333333333326,0.6,0.4],"xyz":[0.23941868054776369,0.289758755563911063,0.169023997939383636],"hpluv":[102.614945913325585,73.9300429237522394,60.7603321241253269],"hsluv":[102.614945913325585,49.862982431557576,60.7603321241253269]},"#889977":{"lch":[61.0879169406466644,24.8557994274465628,109.198389045907604],"luv":[61.0879169406466644,-8.17358342252196,23.4734594640953418],"rgb":[0.533333333333333326,0.6,0.466666666666666674],"xyz":[0.248732693567965624,0.293484360771991859,0.218077799845781545],"hpluv":[109.198389045907604,51.6311437806165543,61.0879169406466644],"hsluv":[109.198389045907604,33.1759220302134779,61.0879169406466644]},"#889988":{"lch":[61.4761176658877702,14.1684419896747276,127.715012949229816],"luv":[61.4761176658877702,-8.66732250724137,11.2081340539023326],"rgb":[0.533333333333333326,0.6,0.533333333333333326],"xyz":[0.259873110669909424,0.297940527612769457,0.276750663249353712],"hpluv":[127.715012949229816,29.2452265306994263,61.4761176658877702],"hsluv":[127.715012949229816,14.8910328511789984,61.4761176658877702]},"#889999":{"lch":[61.9262069462763094,9.41507553536713537,192.177050630058915],"luv":[61.9262069462763094,-9.20324067004397861,-1.98595279549067061],"rgb":[0.533333333333333326,0.6,0.6],"xyz":[0.272930074778982124,0.303163313256398614,0.345517340890471647],"hpluv":[192.177050630058915,19.2925065058214678,61.9262069462763094],"hsluv":[192.177050630058915,19.2169899754877207,61.9262069462763094]},"#8899aa":{"lch":[62.4388911462841207,18.6148502867865133,238.334617604481764],"luv":[62.4388911462841207,-9.77200531832159669,-15.8436284751369474],"rgb":[0.533333333333333326,0.6,0.66666666666666663],"xyz":[0.28798776774423468,0.309186390442499737,0.424821190507470248],"hpluv":[238.334617604481764,37.8306403353000036,62.4388911462841207],"hsluv":[238.334617604481764,23.6900457250072343,62.4388911462841207]},"#8899bb":{"lch":[63.0143540484962,31.8509402607766567,251.009167860858838],"luv":[63.0143540484962,-10.3648329189902189,-30.1173145226625429],"rgb":[0.533333333333333326,0.6,0.733333333333333282],"xyz":[0.305125359851571354,0.316041427285434495,0.515079175606111939],"hpluv":[251.009167860858838,64.1389868285616132,63.0143540484962],"hsluv":[251.009167860858838,32.2218698989210282,63.0143540484962]},"#8899cc":{"lch":[63.6523012354060143,45.9199042321153357,256.173658840600297],"luv":[63.6523012354060143,-10.9739341944061159,-44.5893526863026821],"rgb":[0.533333333333333326,0.6,0.8],"xyz":[0.324417735295293053,0.323758377462923297,0.616685686276382095],"hpluv":[256.173658840600297,91.543221315317254,63.6523012354060143],"hsluv":[256.173658840600297,48.1563502503440546,63.6523012354060143]},"#8899dd":{"lch":[64.3520063546654,60.2052734177332738,258.898139298145679],"luv":[64.3520063546654,-11.5927562398827622,-59.0786166905309358],"rgb":[0.533333333333333326,0.6,0.866666666666666696],"xyz":[0.345936060845926652,0.332365707683176803,0.730015534176388092],"hpluv":[258.898139298145679,118.716686841439611,64.3520063546654],"hsluv":[258.898139298145679,64.7736147673059435,64.3520063546654]},"#8899ee":{"lch":[65.1123593572591091,74.4513212160393465,260.556144021857],"luv":[65.1123593572591091,-12.2160522582942246,-73.442271874149526],"rgb":[0.533333333333333326,0.6,0.933333333333333348],"xyz":[0.369748241112101361,0.34189057978964682,0.855426350244911315],"hpluv":[260.556144021857,145.093615685215838,65.1123593572591091],"hsluv":[260.556144021857,82.0482136329290626,65.1123593572591091]},"#8899ff":{"lch":[65.9319161385595862,88.5102230179824829,261.65889869963604],"luv":[65.9319161385595862,-12.8398242875011679,-87.5739601191992],"rgb":[0.533333333333333326,0.6,1],"xyz":[0.395919289521932782,0.352358999153579544,0.993260538536693161],"hpluv":[261.65889869963604,170.348009793708229,65.9319161385595862],"hsluv":[261.65889869963604,99.9999999999983373,65.9319161385595862]},"#aa9900":{"lch":[62.7844580943873609,69.6780489210530618,75.8779002673010297],"luv":[62.7844580943873609,17.0006834702273615,67.5722373685362072],"rgb":[0.66666666666666663,0.6,0],"xyz":[0.279679277130872483,0.313290833923400402,0.0457396726625379293],"hpluv":[75.8779002673010297,140.82610179048271,62.7844580943873609],"hsluv":[75.8779002673010297,100.000000000002217,62.7844580943873609]},"#aa9911":{"lch":[62.8183644916567232,68.2796688580209548,75.7117398414304],"luv":[62.8183644916567232,16.8514534980901978,66.1675274916835576],"rgb":[0.66666666666666663,0.6,0.0666666666666666657],"xyz":[0.280690942630509588,0.313695500123255255,0.0510677776272935591],"hpluv":[75.7117398414304,137.925354096716262,62.8183644916567232],"hsluv":[75.7117398414304,97.9039601429264792,62.8183644916567232]},"#aa9922":{"lch":[62.8811408657966098,65.7186920323652259,75.3888160486312415],"luv":[62.8811408657966098,16.5780820569936154,63.5933461751813311],"rgb":[0.66666666666666663,0.6,0.133333333333333331],"xyz":[0.282566300768986645,0.314445643378646056,0.0609446638232727586],"hpluv":[75.3888160486312415,132.619634057532949,62.8811408657966098],"hsluv":[75.3888160486312415,94.0678156084237287,62.8811408657966098]},"#aa9933":{"lch":[62.984284118013818,61.5887344728930373,74.8105333616033477],"luv":[62.984284118013818,16.1369729495721401,59.4371122952427768],"rgb":[0.66666666666666663,0.6,0.2],"xyz":[0.28565405150144435,0.315680743671629183,0.0772068176808839],"hpluv":[74.8105333616033477,124.08189266143988,62.984284118013818],"hsluv":[74.8105333616033477,87.8877322232676335,62.984284118013818]},"#aa9944":{"lch":[63.1327254984845325,55.810649390305,73.8551623649617284],"luv":[63.1327254984845325,15.5190685808424753,53.6095802609072862],"rgb":[0.66666666666666663,0.6,0.266666666666666663],"xyz":[0.290112046745663266,0.317463941769316749,0.10068559263377054],"hpluv":[73.8551623649617284,112.176494362550613,63.1327254984845325],"hsluv":[73.8551623649617284,79.2518346201028407,63.1327254984845325]},"#aa9955":{"lch":[63.330394330910508,48.4114193249398497,72.2912631539462893],"luv":[63.330394330910508,14.725704471349518,46.1174495053405593],"rgb":[0.66666666666666663,0.6,0.333333333333333315],"xyz":[0.296074460785025251,0.319848907385061609,0.132087639907744442],"hpluv":[72.2912631539462893,97.0007181904759,63.330394330910508],"hsluv":[72.2912631539462893,68.2014618333714253,63.330394330910508]},"#aa9966":{"lch":[63.5804407621292285,39.5252766283835086,69.6158026784446662],"luv":[63.5804407621292285,13.7671883687516523,37.0501284339143524],"rgb":[0.66666666666666663,0.6,0.4],"xyz":[0.303659456558533436,0.322882905694464928,0.172035284314888404],"hpluv":[69.6158026784446662,78.8843243984218105,63.5804407621292285],"hsluv":[69.6158026784446662,54.9106459007729129,63.5804407621292285]},"#aa9977":{"lch":[63.8853523521207762,29.4269320044373437,64.5162682653354835],"luv":[63.8853523521207762,12.6610788645847681,26.563911782313685],"rgb":[0.66666666666666663,0.6,0.466666666666666674],"xyz":[0.312973469578735342,0.326608510902545723,0.221089086221286313],"hpluv":[64.5162682653354835,58.4497984351079,63.8853523521207762],"hsluv":[64.5162682653354835,39.6604297676390303,63.8853523521207762]},"#aa9988":{"lch":[64.2470245358341288,18.7498828458428441,52.4386898809200943],"luv":[64.2470245358341288,11.4301164327749429,14.8630597477787205],"rgb":[0.66666666666666663,0.6,0.533333333333333326],"xyz":[0.324113886680679142,0.331064677743323321,0.279761949624858508],"hpluv":[52.4386898809200943,37.0326564248891472,64.2470245358341288],"hsluv":[52.4386898809200943,22.8076315183033955,64.2470245358341288]},"#aa9999":{"lch":[64.6668097656484,10.3324715423982241,12.1770506300641514],"luv":[64.6668097656484,10.0999956892392,2.17946214737001709],"rgb":[0.66666666666666663,0.6,0.6],"xyz":[0.337170850789751841,0.336287463386952479,0.348528627265976387],"hpluv":[12.1770506300641514,20.2750581327120152,64.6668097656484],"hsluv":[12.1770506300641514,9.39861318597140283,64.6668097656484]},"#aa99aa":{"lch":[65.1455571833188,14.2173372068657535,307.715012949255367],"luv":[65.1455571833188,8.69723339065650514,-11.2468132643064216],"rgb":[0.66666666666666663,0.6,0.66666666666666663],"xyz":[0.352228543755004397,0.342310540573053601,0.427832476882975],"hpluv":[307.715012949255367,27.6931773999722353,65.1455571833188],"hsluv":[307.715012949255367,11.6506794595111955,65.1455571833188]},"#aa99bb":{"lch":[65.6836488991384186,26.2061362707181722,286.054514249721478],"luv":[65.6836488991384186,7.24735484809431529,-25.1840708771488],"rgb":[0.66666666666666663,0.6,0.733333333333333282],"xyz":[0.369366135862341072,0.34916557741598836,0.51809046198161679],"hpluv":[286.054514249721478,50.6273330900370553,65.6836488991384186],"hsluv":[286.054514249721478,27.5721610485895,65.6836488991384186]},"#aa99cc":{"lch":[66.2810360009151651,39.8419200501833544,278.33213307587846],"luv":[66.2810360009151651,5.77353773601307907,-39.4213756139487757],"rgb":[0.66666666666666663,0.6,0.8],"xyz":[0.388658511306062771,0.356882527593477161,0.619696972651887],"hpluv":[278.33213307587846,76.2764194246967,66.2810360009151651],"hsluv":[278.33213307587846,44.416066179202609,66.2810360009151651]},"#aa99dd":{"lch":[66.937275739096421,53.9476968390211695,274.567192844176702],"luv":[66.937275739096421,4.29575598209881182,-53.7763932853180151],"rgb":[0.66666666666666663,0.6,0.866666666666666696],"xyz":[0.41017683685669637,0.365489857813730668,0.733026820551892944],"hpluv":[274.567192844176702,102.269045280662851,66.937275739096421],"hsluv":[274.567192844176702,62.1125641321495365,66.937275739096421]},"#aa99ee":{"lch":[67.6515703211096309,68.1578213039093299,272.380002942583644],"luv":[67.6515703211096309,2.83038594926376286,-68.0990273078393358],"rgb":[0.66666666666666663,0.6,0.933333333333333348],"xyz":[0.433989017122871079,0.375014729920200685,0.858437636620416167],"hpluv":[272.380002942583644,127.843056183363871,67.6515703211096309],"hsluv":[272.380002942583644,80.63443597048024,67.6515703211096309]},"#aa99ff":{"lch":[68.4228071241374778,82.2834014236355387,270.968064044661787],"luv":[68.4228071241374778,1.39018638405132466,-82.2716569157370543],"rgb":[0.66666666666666663,0.6,1],"xyz":[0.4601600655327025,0.385483149284133408,0.996271824912198],"hpluv":[270.968064044661787,152.598644218259693,68.4228071241374778],"hsluv":[270.968064044661787,99.9999999999981668,68.4228071241374778]},"#770000":{"lch":[23.4140868272264697,78.7423116347599432,12.177050630061796],"luv":[23.4140868272264697,76.9706458719381317,16.6093743302492847],"rgb":[0.466666666666666674,0,0],"xyz":[0.0760757904266185919,0.0392265794387260461,0.00356605267624767169],"hpluv":[12.177050630061796,426.746789183125429,23.4140868272264697],"hsluv":[12.177050630061796,100.000000000002359,23.4140868272264697]},"#770011":{"lch":[23.5491569362977273,75.7570426868466456,9.89164947332394462],"luv":[23.5491569362977273,74.6308667748156864,13.0139633123970793],"rgb":[0.466666666666666674,0,0.0666666666666666657],"xyz":[0.0770874559262557102,0.0396312456385809,0.00889415764100330228],"hpluv":[9.89164947332394462,408.213135586655085,23.5491569362977273],"hsluv":[9.89164947332394462,99.9999999999965183,23.5491569362977273]},"#770022":{"lch":[23.7971287372198219,70.9964864167640854,5.53723409440817704],"luv":[23.7971287372198219,70.6651956613751224,6.85063542055327357],"rgb":[0.466666666666666674,0,0.133333333333333331],"xyz":[0.0789628140647327392,0.0403813888939717203,0.0187710438369825],"hpluv":[5.53723409440817704,378.574731225432288,23.7971287372198219],"hsluv":[5.53723409440817704,99.9999999999967741,23.7971287372198219]},"#770033":{"lch":[24.198804347572846,65.0463245726941182,358.140059561726389],"luv":[24.198804347572846,65.0120550988218895,-2.11116845467566527],"rgb":[0.466666666666666674,0,0.2],"xyz":[0.0820505647971904728,0.0416164891869548331,0.0350331976945936416],"hpluv":[358.140059561726389,341.089366306392606,24.198804347572846],"hsluv":[358.140059561726389,99.9999999999971578,24.198804347572846]},"#770044":{"lch":[24.764944554878376,59.7650645123016702,347.391874641304071],"luv":[24.764944554878376,58.3238787679626398,-13.0456161839744915],"rgb":[0.466666666666666674,0,0.266666666666666663],"xyz":[0.0865085600414093464,0.0433996872846424062,0.0585119726474802801],"hpluv":[347.391874641304071,306.231145677972847,24.764944554878376],"hsluv":[347.391874641304071,99.9999999999975557,24.764944554878376]},"#770055":{"lch":[25.4983947844981387,57.0853266397623571,333.997796644431901],"luv":[25.4983947844981387,51.3069893372134587,-25.0265331742018731],"rgb":[0.466666666666666674,0,0.333333333333333315],"xyz":[0.0924709740807713454,0.0457846529003872391,0.0899140199214541885],"hpluv":[333.997796644431901,284.086748448009075,25.4983947844981387],"hsluv":[333.997796644431901,99.999999999998,25.4983947844981387]},"#770066":{"lch":[26.3955149445472088,58.0812929265372375,320.022905340944305],"luv":[26.3955149445472088,44.5077732621065394,-37.3161453966930949],"rgb":[0.466666666666666674,0,0.4],"xyz":[0.100055969854279517,0.0488186512097905506,0.129861664328598164],"hpluv":[320.022905340944305,279.219318659546161,26.3955149445472088],"hsluv":[320.022905340944305,99.9999999999984,26.3955149445472088]},"#770077":{"lch":[27.4476614837194361,62.5213221502200156,307.715012949243601],"luv":[27.4476614837194361,38.2464397320582776,-49.4583215569799322],"rgb":[0.466666666666666674,0,0.466666666666666674],"xyz":[0.109369982874481436,0.052544256417871367,0.178915466234996073],"hpluv":[307.715012949243601,289.04278373048345,27.4476614837194361],"hsluv":[307.715012949243601,99.9999999999988631,27.4476614837194361]},"#770088":{"lch":[28.6427236217895711,69.3985842918787341,298.067280282401043],"luv":[28.6427236217895711,32.6525926425722872,-61.2370124633397808],"rgb":[0.466666666666666674,0,0.533333333333333326],"xyz":[0.120510399976425264,0.0570004232586489579,0.237588329638568241],"hpluv":[298.067280282401043,307.450798390810235,28.6427236217895711],"hsluv":[298.067280282401043,99.9999999999991473,28.6427236217895711]},"#770099":{"lch":[29.9665727349335924,77.70857748467688,290.909274437861086],"luv":[29.9665727349335924,27.7333531267432782,-72.5912125469701],"rgb":[0.466666666666666674,0,0.6],"xyz":[0.133567364085497964,0.0622232089022781223,0.30635500727968612],"hpluv":[290.909274437861086,329.057057444315717,29.9665727349335924],"hsluv":[290.909274437861086,99.9999999999993605,29.9665727349335924]},"#7700aa":{"lch":[31.4042918618800115,86.7647813143177,285.668616902051383],"luv":[31.4042918618800115,23.4328336498598695,-83.5405864455078415],"rgb":[0.466666666666666674,0,0.66666666666666663],"xyz":[0.14862505705075052,0.0682462860883792238,0.385658856896684721],"hpluv":[285.668616902051383,350.585377409449279,31.4042918618800115],"hsluv":[285.668616902051383,99.9999999999996447,31.4042918618800115]},"#7700bb":{"lch":[32.9411141237069387,96.170393631615,281.802895608829544],"luv":[32.9411141237069387,19.6712233173334745,-94.1370680681067853],"rgb":[0.466666666666666674,0,0.733333333333333282],"xyz":[0.165762649158087166,0.0751013229313139824,0.475916841995326467],"hpluv":[281.802895608829544,370.460950364720645,32.9411141237069387],"hsluv":[281.802895608829544,99.9999999999998295,32.9411141237069387]},"#7700cc":{"lch":[34.5630635499026226,105.713400517707811,278.906152205018032],"luv":[34.5630635499026226,16.3661753747823724,-104.438840249301762],"rgb":[0.466666666666666674,0,0.8],"xyz":[0.185055024601808893,0.082818273108802784,0.577523352665596623],"hpluv":[278.906152205018032,388.112061604616713,34.5630635499026226],"hsluv":[278.906152205018032,99.9999999999998721,34.5630635499026226]},"#7700dd":{"lch":[36.2573361534597964,115.285120662302717,276.696107756350386],"luv":[36.2573361534597964,13.4426218553338188,-114.498711624961956],"rgb":[0.466666666666666674,0,0.866666666666666696],"xyz":[0.206573350152442436,0.091425603329056318,0.690853200565602621],"hpluv":[276.696107756350386,403.475057258468723,36.2573361534597964],"hsluv":[276.696107756350386,100.000000000000156,36.2573361534597964]},"#7700ee":{"lch":[38.012479203832,124.831519574090535,274.979891409884715],"luv":[38.012479203832,10.8361388436516783,-124.360308676593633],"rgb":[0.466666666666666674,0,0.933333333333333348],"xyz":[0.230385530418617201,0.100950475435526349,0.816264016634125844],"hpluv":[274.979891409884715,416.713325299391272,38.012479203832],"hsluv":[274.979891409884715,100.000000000000156,38.012479203832]},"#7700ff":{"lch":[39.8184284160989037,134.326708962856742,273.625091115001112],"luv":[39.8184284160989037,8.49315165227522861,-134.057939398617776],"rgb":[0.466666666666666674,0,1],"xyz":[0.256556578828448567,0.111418894799459045,0.954098204925907689],"hpluv":[273.625091115001112,428.072753406140123,39.8184284160989037],"hsluv":[273.625091115001112,100.000000000000313,39.8184284160989037]},"#771100":{"lch":[24.7134353555624457,74.5310598854495794,14.479461840222152],"luv":[24.7134353555624457,72.1637543524892351,18.6352205622059],"rgb":[0.466666666666666674,0.0666666666666666657,0],"xyz":[0.078080190687547,0.0432353799605829231,0.00423418609655712257],"hpluv":[14.479461840222152,382.686818993669249,24.7134353555624457],"hsluv":[14.479461840222152,100.000000000002174,24.7134353555624457]},"#771111":{"lch":[24.8400617115613187,71.7342088143168723,12.1770506300618244],"luv":[24.8400617115613187,70.1202221387804912,15.1311321924069837],"rgb":[0.466666666666666674,0.0666666666666666657,0.0666666666666666657],"xyz":[0.0790918561871841175,0.043640046160437776,0.00956229106131275403],"hpluv":[12.1770506300618244,366.448517223619376,24.8400617115613187],"hsluv":[12.1770506300618244,85.8702458957174173,24.8400617115613187]},"#771122":{"lch":[25.0727380413203562,67.2476760425649616,7.76714475492354417],"luv":[25.0727380413203562,66.6307123231625,9.0883501491595],"rgb":[0.466666666666666674,0.0666666666666666657,0.133333333333333331],"xyz":[0.0809672143256611465,0.0443901894158286,0.0194391772572919501],"hpluv":[7.76714475492354417,340.341449223453765,25.0727380413203562],"hsluv":[7.76714475492354417,86.6206298981217,25.0727380413203562]},"#771133":{"lch":[25.4501908259833556,61.6001849310061189,0.209311103178295294],"luv":[25.4501908259833556,61.599773884646936,0.225035318388215971],"rgb":[0.466666666666666674,0.0666666666666666657,0.2],"xyz":[0.0840549650581188801,0.0456252897088117101,0.0357013311149031],"hpluv":[0.209311103178295294,307.135699817562568,25.4501908259833556],"hsluv":[0.209311103178295294,87.6964449022470802,25.4501908259833556]},"#771144":{"lch":[25.9833113937366775,56.5829308746863688,349.098656617234155],"luv":[25.9833113937366775,55.5618510799655,-10.7008771106518541],"rgb":[0.466666666666666674,0.0666666666666666657,0.266666666666666663],"xyz":[0.0885129603023377537,0.0474084878064992832,0.0591801060677897353],"hpluv":[349.098656617234155,276.331419289390624,25.9833113937366775],"hsluv":[349.098656617234155,88.9762099121112442,25.9833113937366775]},"#771155":{"lch":[26.6758393728738312,54.157242934368746,335.112354986374442],"luv":[26.6758393728738312,49.127918835603694,-22.7915456504066185],"rgb":[0.466666666666666674,0.0666666666666666657,0.333333333333333315],"xyz":[0.0944753743416997527,0.0497934534222441161,0.0905821533417636438],"hpluv":[335.112354986374442,257.618934567198892,26.6758393728738312],"hsluv":[335.112354986374442,90.3225181656420375,26.6758393728738312]},"#771166":{"lch":[27.5255776115618076,55.4731266930874796,320.490705765847224],"luv":[27.5255776115618076,42.7987039146097388,-35.2921907557026557],"rgb":[0.466666666666666674,0.0666666666666666657,0.4],"xyz":[0.102060370115207924,0.0528274517316474276,0.130529797748907606],"hpluv":[320.490705765847224,255.732268141411282,27.5255776115618076],"hsluv":[320.490705765847224,91.6238582413064364,27.5255776115618076]},"#771177":{"lch":[28.525624322061,60.3055315504910538,307.715012949243658],"luv":[28.525624322061,36.8909645322892956,-47.7054909990940672],"rgb":[0.466666666666666674,0.0666666666666666657,0.466666666666666674],"xyz":[0.111374383135409843,0.056553056939728244,0.179583599655305515],"hpluv":[307.715012949243658,268.263334170626,28.525624322061],"hsluv":[307.715012949243658,92.8109433172232201,28.525624322061]},"#771188":{"lch":[29.665668786552871,67.5901402176946,297.828537901307072],"luv":[29.665668786552871,31.5529142063876336,-59.77324367751811],"rgb":[0.466666666666666674,0.0666666666666666657,0.533333333333333326],"xyz":[0.122514800237353672,0.0610092237805058418,0.238256463058877682],"hpluv":[297.828537901307072,289.113605882780575,29.665668786552871],"hsluv":[297.828537901307072,93.8529472884676892,29.665668786552871]},"#771199":{"lch":[30.9332504381216253,76.2730393291315494,290.583951381139741],"luv":[30.9332504381216253,26.816032696200331,-71.4036197887723461],"rgb":[0.466666666666666674,0.0666666666666666657,0.6],"xyz":[0.135571764346426371,0.066232009424135,0.307023140699995589],"hpluv":[290.583951381139741,312.885056330098905,30.9332504381216253],"hsluv":[290.583951381139741,94.745562802664864,30.9332504381216253]},"#7711aa":{"lch":[32.3148680584756391,85.6471240548678452,285.332056603477554],"luv":[32.3148680584756391,22.6461849228904,-82.5989114172104166],"rgb":[0.466666666666666674,0.0666666666666666657,0.66666666666666663],"xyz":[0.150629457311678927,0.0722550866102361078,0.38632699031699419],"hpluv":[285.332056603477554,336.317699636744408,32.3148680584756391],"hsluv":[285.332056603477554,95.4992611083078771,32.3148680584756391]},"#7711bb":{"lch":[33.796865882550442,95.3131880713117,281.486339493443666],"luv":[33.796865882550442,18.9801244444786903,-93.4042755787407],"rgb":[0.466666666666666674,0.0666666666666666657,0.733333333333333282],"xyz":[0.167767049419015574,0.0791101234531708664,0.476584975415635936],"hpluv":[281.486339493443666,357.862255189103962,33.796865882550442],"hsluv":[281.486339493443666,96.130904738193,33.796865882550442]},"#7711cc":{"lch":[35.36607449089243,105.064609202725904,278.619980375929231],"luv":[35.36607449089243,15.7470979978009513,-103.877817707002151],"rgb":[0.466666666666666674,0.0666666666666666657,0.8],"xyz":[0.1870594248627373,0.086827073630659668,0.578191486085906092],"hpluv":[278.619980375929231,376.971848031202455,35.36607449089243],"hsluv":[278.619980375929231,96.6587778670915441,35.36607449089243]},"#7711dd":{"lch":[37.0102245888209,114.800572621107989,276.441726024966442],"luv":[37.0102245888209,12.8797770828984923,-114.075776641796878],"rgb":[0.466666666666666674,0.0666666666666666657,0.866666666666666696],"xyz":[0.208577750413370844,0.0954344038509132,0.69152133398591209],"hpluv":[276.441726024966442,393.605954910771402,37.0102245888209],"hsluv":[276.441726024966442,97.1000737310191084,37.0102245888209]},"#7711ee":{"lch":[38.7181742300654648,124.475046910670883,274.755182332196796],"luv":[38.7181742300654648,10.3187754184265525,-124.046604859938796],"rgb":[0.466666666666666674,0.0666666666666666657,0.933333333333333348],"xyz":[0.232389930679545609,0.104959275957383219,0.816932150054435313],"hpluv":[274.755182332196796,407.949828918098,38.7181742300654648],"hsluv":[274.755182332196796,97.4698666617264706,38.7181742300654648]},"#7711ff":{"lch":[40.4799968781786,134.069342311184641,273.42679883886251],"luv":[40.4799968781786,8.01376323316973682,-133.829623576382744],"rgb":[0.466666666666666674,0.0666666666666666657,1],"xyz":[0.258560979089377,0.115427695321315929,0.954766338346217158],"hpluv":[273.42679883886251,420.269946860795244,40.4799968781786],"hsluv":[273.42679883886251,99.99999999999946,40.4799968781786]},"#772200":{"lch":[26.9238486490213944,67.8779226750429814,18.9619118830866213],"luv":[26.9238486490213944,64.1945131058521241,22.0562207502031953],"rgb":[0.466666666666666674,0.133333333333333331,0],"xyz":[0.0817958144223149414,0.0506666274301188907,0.00547272734147973266],"hpluv":[18.9619118830866213,319.912145739235086,26.9238486490213944],"hsluv":[18.9619118830866213,100.000000000002203,26.9238486490213944]},"#772211":{"lch":[27.0378210495853537,65.3114997865878451,16.6506371445360628],"luv":[27.0378210495853537,62.5729698674152957,18.7140440938083152],"rgb":[0.466666666666666674,0.133333333333333331,0.0666666666666666657],"xyz":[0.0828074799219520596,0.0510712936299737436,0.0108008323062353633],"hpluv":[16.6506371445360628,306.518925183772069,27.0378210495853537],"hsluv":[16.6506371445360628,87.77659617077137,27.0378210495853537]},"#772222":{"lch":[27.2475131582451553,61.1491638550902934,12.1770506300618262],"luv":[27.2475131582451553,59.7733358183195094,12.8983939049434628],"rgb":[0.466666666666666674,0.133333333333333331,0.133333333333333331],"xyz":[0.0846828380604290887,0.0518214368853645649,0.0206777185022145593],"hpluv":[12.1770506300618262,284.775733052529233,27.2475131582451553],"hsluv":[12.1770506300618262,66.7317810633447,27.2475131582451553]},"#772233":{"lch":[27.5884028886125066,55.830319472153235,4.36926883706767555],"luv":[27.5884028886125066,55.668063043001986,4.25339034219168255],"rgb":[0.466666666666666674,0.133333333333333331,0.2],"xyz":[0.0877705887928868222,0.0530565371783476777,0.0369398723598257],"hpluv":[4.36926883706767555,256.792821962959806,27.5884028886125066],"hsluv":[4.36926883706767555,69.0869865520346,27.5884028886125066]},"#772244":{"lch":[28.0713586292933357,51.0577981227000137,352.597218961633928],"luv":[28.0713586292933357,50.6322267647614197,-6.57847717790113862],"rgb":[0.466666666666666674,0.133333333333333331,0.266666666666666663],"xyz":[0.0922285840371057,0.0548397352760352508,0.0604186473127123411],"hpluv":[352.597218961633928,230.801153887638264,28.0713586292933357],"hsluv":[352.597218961633928,71.9536276136468871,28.0713586292933357]},"#772255":{"lch":[28.7011983995690869,48.8793621784160877,337.425357822166575],"luv":[28.7011983995690869,45.134235573623549,-18.7641367015226166],"rgb":[0.466666666666666674,0.133333333333333331,0.333333333333333315],"xyz":[0.0981909980764677,0.0572247008917800837,0.0918206945866862495],"hpluv":[337.425357822166575,216.105005327694982,28.7011983995690869],"hsluv":[337.425357822166575,75.0482997779786416,28.7011983995690869]},"#772266":{"lch":[29.4776386596593341,50.6214045611569148,321.457127980188602],"luv":[29.4776386596593341,39.5931335065919541,-31.5421999688275427],"rgb":[0.466666666666666674,0.133333333333333331,0.4],"xyz":[0.105775993849975866,0.0602586992011833952,0.131768338993830225],"hpluv":[321.457127980188602,217.911839443782668,29.4776386596593341],"hsluv":[321.457127980188602,78.1196257410634871,29.4776386596593341]},"#772277":{"lch":[30.3962065887853328,56.0773495547330114,307.715012949243828],"luv":[30.3962065887853328,34.3044404103525125,-44.3607315225559375],"rgb":[0.466666666666666674,0.133333333333333331,0.466666666666666674],"xyz":[0.115090006870177786,0.0639843044092642116,0.180822140900228134],"hpluv":[307.715012949243828,234.103236488433623,30.3962065887853328],"hsluv":[307.715012949243828,80.9925899090149,30.3962065887853328]},"#772288":{"lch":[31.4492100235983827,64.0564791390884,297.353574907339521],"luv":[31.4492100235983827,29.432687880373738,-56.8941948166328118],"rgb":[0.466666666666666674,0.133333333333333331,0.533333333333333326],"xyz":[0.126230423972121614,0.0684404712500418094,0.239495004303800302],"hpluv":[297.353574907339521,258.459589193709576,31.4492100235983827],"hsluv":[297.353574907339521,83.5725358287189692,31.4492100235983827]},"#772299":{"lch":[32.6267183371791276,73.3979299617999885,289.947447059718741],"luv":[32.6267183371791276,25.040297814612174,-68.9944896932561136],"rgb":[0.466666666666666674,0.133333333333333331,0.6],"xyz":[0.139287388081194341,0.073663256893670967,0.308261681944918209],"hpluv":[289.947447059718741,285.462946683821,32.6267183371791276],"hsluv":[289.947447059718741,85.8272099061853169,32.6267183371791276]},"#7722aa":{"lch":[33.91747454857272,83.3484305918169213,284.681710760769079],"luv":[33.91747454857272,21.1245907707188039,-80.6269963900962],"rgb":[0.466666666666666674,0.133333333333333331,0.66666666666666663],"xyz":[0.154345081046446869,0.0796863340797720754,0.38756553156191681],"hpluv":[284.681710760769079,311.826661765562392,33.91747454857272],"hsluv":[284.681710760769079,87.7639475735253,33.91747454857272]},"#7722bb":{"lch":[35.3096729107126137,93.4985765466947,280.880145280973409],"luv":[35.3096729107126137,17.6483381470917493,-91.8178630599952186],"rgb":[0.466666666666666674,0.133333333333333331,0.733333333333333282],"xyz":[0.171482673153783516,0.086541370922706834,0.477823516660558556],"hpluv":[280.880145280973409,336.008784107710085,35.3096729107126137],"hsluv":[280.880145280973409,89.410850129911168,35.3096729107126137]},"#7722cc":{"lch":[36.7915673195940158,103.646851370476796,278.075561058441508],"luv":[36.7915673195940158,14.5601992392030191,-102.619054746808203],"rgb":[0.466666666666666674,0.133333333333333331,0.8],"xyz":[0.19077504859750527,0.0942583211001956356,0.579430027330828712],"hpluv":[278.075561058441508,357.476214898523438,36.7915673195940158],"hsluv":[278.075561058441508,90.8041771911133395,36.7915673195940158]},"#7722dd":{"lch":[38.351906528896663,113.703099806952181,275.960131804992216],"luv":[38.351906528896663,11.8065226659589069,-113.088465053903391],"rgb":[0.466666666666666674,0.133333333333333331,0.866666666666666696],"xyz":[0.212293374148138814,0.10286565132044917,0.69275987523083471],"hpluv":[275.960131804992216,376.205095027198126,38.351906528896663],"hsluv":[275.960131804992216,91.9810564333708101,38.351906528896663]},"#7722ee":{"lch":[39.9802139341708269,123.633267676123751,274.331320845995606],"luv":[39.9802139341708269,9.33725773346982812,-123.280170726255989],"rgb":[0.466666666666666674,0.133333333333333331,0.933333333333333348],"xyz":[0.236105554414313523,0.112390523426919187,0.818170691299357933],"hpluv":[274.331320845995606,392.400507860275241,39.9802139341708269],"hsluv":[274.331320845995606,92.975835565241681,39.9802139341708269]},"#7722ff":{"lch":[41.6669409214524222,133.430100966175758,273.053819065229],"luv":[41.6669409214524222,7.10835060884393233,-133.240621416539511],"rgb":[0.466666666666666674,0.133333333333333331,1],"xyz":[0.262276602824144944,0.122858942790851897,0.956004879591139778],"hpluv":[273.053819065229,406.351179140502,41.6669409214524222],"hsluv":[273.053819065229,99.9999999999994,41.6669409214524222]},"#eeaa00":{"lch":[74.1441199778221716,94.3993067628715323,52.9277228913731435],"luv":[74.1441199778221716,56.9059790518510553,75.3189130661151438],"rgb":[0.933333333333333348,0.66666666666666663,0],"xyz":[0.496332043879122442,0.469286695915611562,0.0644413600602487119],"hpluv":[52.9277228913731435,161.559096825574699,74.1441199778221716],"hsluv":[52.9277228913731435,100.00000000000226,74.1441199778221716]},"#eeaa11":{"lch":[74.170022976797469,93.4032488378738748,52.6298301838218165],"luv":[74.170022976797469,56.6922374254564474,74.2304325001913412],"rgb":[0.933333333333333348,0.66666666666666663,0.0666666666666666657],"xyz":[0.497343709378759546,0.469691362115466415,0.0697694650250043485],"hpluv":[52.6298301838218165,159.798572537124,74.170022976797469],"hsluv":[52.6298301838218165,98.6103970010943698,74.170022976797469]},"#eeaa22":{"lch":[74.2180009061856794,91.5777923210439866,52.06484069365154],"luv":[74.2180009061856794,56.2992155036066038,72.2280442769616],"rgb":[0.933333333333333348,0.66666666666666663,0.133333333333333331],"xyz":[0.499219067517236603,0.470441505370857216,0.0796463512209835411],"hpluv":[52.06484069365154,156.574215350823607,74.2180009061856794],"hsluv":[52.06484069365154,96.0561964998804427,74.2180009061856794]},"#eeaa33":{"lch":[74.296884894391,88.6315089581908353,51.0969321655322659],"luv":[74.296884894391,55.6610065739707096,68.9738843866158646],"rgb":[0.933333333333333348,0.66666666666666663,0.2],"xyz":[0.502306818249694365,0.471676605663840343,0.0959085050785946913],"hpluv":[51.0969321655322659,151.375944062314744,74.296884894391],"hsluv":[51.0969321655322659,91.9112466174575786,74.296884894391]},"#eeaa44":{"lch":[74.4105324975616,84.5080331915566,49.6113532778883268],"luv":[74.4105324975616,54.7585847399730952,64.3669563610119582],"rgb":[0.933333333333333348,0.66666666666666663,0.266666666666666663],"xyz":[0.506764813493913224,0.473459803761527909,0.11938728003148133],"hpluv":[49.6113532778883268,144.112916810009352,74.4105324975616],"hsluv":[49.6113532778883268,86.056132752536,74.4105324975616]},"#eeaa55":{"lch":[74.5620870475656545,79.2349762990306203,47.4460085751506284],"luv":[74.5620870475656545,53.5853993814341081,58.3676832008958897],"rgb":[0.933333333333333348,0.66666666666666663,0.333333333333333315],"xyz":[0.512727227533275154,0.475844769377272769,0.150789327305455245],"hpluv":[47.4460085751506284,134.846041865267,74.5620870475656545],"hsluv":[47.4460085751506284,78.4547380743125302,74.5620870475656545]},"#eeaa66":{"lch":[74.7541548019056705,72.9335579755352512,44.3584242628533616],"luv":[74.7541548019056705,52.1460491859645643,50.9911113162453518],"rgb":[0.933333333333333348,0.66666666666666663,0.4],"xyz":[0.520312223306783395,0.478878767686676088,0.190736971712599179],"hpluv":[44.3584242628533616,123.803063546095515,74.7541548019056705],"hsluv":[44.3584242628533616,69.1456590086766,74.7541548019056705]},"#eeaa77":{"lch":[74.988898345165353,65.8409092602829702,39.9757578623994689],"luv":[74.988898345165353,50.4549646902964213,42.3003767160737851],"rgb":[0.933333333333333348,0.66666666666666663,0.466666666666666674],"xyz":[0.529626236326985245,0.482604372894756883,0.239790773618997088],"hpluv":[39.9757578623994689,111.41359114675457,74.988898345165353],"hsluv":[39.9757578623994689,69.3768546233612398,74.988898345165353]},"#eeaa88":{"lch":[75.268091919562039,58.355059996267,33.7246537035848917],"luv":[75.268091919562039,48.5347963512250544,32.3988668060016],"rgb":[0.933333333333333348,0.66666666666666663,0.533333333333333326],"xyz":[0.540766653428929156,0.487060539735534481,0.298463637022569284],"hpluv":[33.7246537035848917,98.3800275430836706,75.268091919562039],"hsluv":[33.7246537035848917,69.7313730072723388,75.268091919562039]},"#eeaa99":{"lch":[75.5931575717450102,51.1191249468368198,24.7740386445795764],"luv":[75.5931575717450102,46.4145012045046172,21.4209946843594],"rgb":[0.933333333333333348,0.66666666666666663,0.6],"xyz":[0.5538236175380018,0.492283325379163639,0.367230314663687163],"hpluv":[24.7740386445795764,85.8104641434512274,75.5931575717450102],"hsluv":[24.7740386445795764,70.1099573125584925,75.5931575717450102]},"#eeaaaa":{"lch":[75.9651912478537,45.142946910964838,12.1770506300622827],"luv":[75.9651912478537,44.1272513869876164,9.52215001119964199],"rgb":[0.933333333333333348,0.66666666666666663,0.66666666666666663],"xyz":[0.568881310503254412,0.498306402565264761,0.446534164280685764],"hpluv":[12.1770506300622827,75.4075094474617771,75.9651912478537],"hsluv":[12.1770506300622827,70.5013292017584661,75.9651912478537]},"#eeaabb":{"lch":[76.3849837125259512,41.8257198613105743,355.706426922992932],"luv":[76.3849837125259512,41.7083375548448743,-3.13136077895000353],"rgb":[0.933333333333333348,0.66666666666666663,0.733333333333333282],"xyz":[0.586018902610591,0.505161439408199464,0.53679214937932751],"hpluv":[355.706426922992932,71.0120800923082243,76.3849837125259512],"hsluv":[355.706426922992932,70.8933377227291857,76.3849837125259512]},"#eeaacc":{"lch":[76.8530390510081,42.4751513806234229,337.329003704434115],"luv":[76.8530390510081,39.1932374848878169,-16.3715796507258275],"rgb":[0.933333333333333348,0.66666666666666663,0.8],"xyz":[0.605311278054312729,0.512878389585688321,0.638398660049597666],"hpluv":[337.329003704434115,73.8860979443606283,76.8530390510081],"hsluv":[337.329003704434115,71.273377003825658,76.8530390510081]},"#eeaadd":{"lch":[77.3695923472492666,47.3596417233686893,320.637369166639587],"luv":[77.3695923472492666,36.6159833837478814,-30.0367345929437413],"rgb":[0.933333333333333348,0.66666666666666663,0.866666666666666696],"xyz":[0.626829603604946328,0.521485719805941828,0.751728507949603664],"hpluv":[320.637369166639587,84.6581146126829,77.3695923472492666],"hsluv":[320.637369166639587,71.6287050870027144,77.3695923472492666]},"#eeaaee":{"lch":[77.9346274334411078,55.5926087174301244,307.715012949246329],"luv":[77.9346274334411078,34.00790779424905,-43.9772708506012506],"rgb":[0.933333333333333348,0.66666666666666663,0.933333333333333348],"xyz":[0.650641783871121,0.531010591912411845,0.877139324018126887],"hpluv":[307.715012949246329,102.440850498764817,77.9346274334411078],"hsluv":[307.715012949246329,71.9466349992656,77.9346274334411078]},"#eeaaff":{"lch":[78.5478951631237123,66.0054362136172,298.40296943037481],"luv":[78.5478951631237123,31.3967924706888546,-58.0599606639755166],"rgb":[0.933333333333333348,0.66666666666666663,1],"xyz":[0.676812832280952459,0.541479011276344568,1.01497351230990884],"hpluv":[298.40296943037481,125.797711632464541,78.5478951631237123],"hsluv":[298.40296943037481,99.9999999999967457,78.5478951631237123]},"#773300":{"lch":[30.1331354048611715,59.6402239078303253,26.8671398719653283],"luv":[30.1331354048611715,53.2024709950710601,26.952799257122777],"rgb":[0.466666666666666674,0.2,0],"xyz":[0.0879135365113257461,0.0629020716081407,0.0075119680378166135],"hpluv":[26.8671398719653283,251.150628123644026,30.1331354048611715],"hsluv":[26.8671398719653283,100.000000000002174,30.1331354048611715]},"#773311":{"lch":[30.23185303241101,57.2493346092074376,24.6191658238461528],"luv":[30.23185303241101,52.0451874365790204,23.8492091669378929],"rgb":[0.466666666666666674,0.2,0.0666666666666666657],"xyz":[0.0889252020109628644,0.0633067378079955473,0.012840073002572245],"hpluv":[24.6191658238461528,240.295148301589592,30.23185303241101],"hsluv":[24.6191658238461528,89.9983576544591131,30.23185303241101]},"#773322":{"lch":[30.4137421865898716,53.2944669038914185,20.1857997859538507],"luv":[30.4137421865898716,50.0210446853216055,18.3900867632275506],"rgb":[0.466666666666666674,0.2,0.133333333333333331],"xyz":[0.0908005601494399,0.0640568810633863617,0.022716959198551441],"hpluv":[20.1857997859538507,222.35740076876786,30.4137421865898716],"hsluv":[20.1857997859538507,72.5478159849102,30.4137421865898716]},"#773333":{"lch":[30.7101510688592612,48.0738359215612618,12.1770506300619186],"luv":[30.7101510688592612,46.9921967440780932,10.1403720532853221],"rgb":[0.466666666666666674,0.2,0.2],"xyz":[0.093888310881897627,0.0652919813563694745,0.0389791130561625843],"hpluv":[12.1770506300619186,198.639745699191,30.7101510688592612],"hsluv":[12.1770506300619186,46.5474493854848177,30.7101510688592612]},"#773344":{"lch":[31.1315732769052218,43.177963340605487,359.450637080068248],"luv":[31.1315732769052218,43.1759786068728815,-0.413992248148033204],"rgb":[0.466666666666666674,0.2,0.266666666666666663],"xyz":[0.0983463061261165,0.0670751794540570545,0.0624578880090492228],"hpluv":[359.450637080068248,175.995032884643848,31.1315732769052218],"hsluv":[359.450637080068248,50.753084379908266,31.1315732769052218]},"#773355":{"lch":[31.6836931484193158,40.8566925980369149,342.103665666318761],"luv":[31.6836931484193158,38.8798033655970627,-12.5550874271345432],"rgb":[0.466666666666666674,0.2,0.333333333333333315],"xyz":[0.1043087201654785,0.0694601450698018874,0.0938599352830231382],"hpluv":[342.103665666318761,163.631433468067485,31.6836931484193158],"hsluv":[342.103665666318761,55.4419897041632623,31.6836931484193158]},"#773366":{"lch":[32.368092831934419,42.8604868736005429,323.403106692152903],"luv":[32.368092831934419,34.4105334223914952,-25.55262265656037],"rgb":[0.466666666666666674,0.2,0.4],"xyz":[0.111893715938986671,0.0724941433792052,0.1338075796901671],"hpluv":[323.403106692152903,168.027093700417197,32.368092831934419],"hsluv":[323.403106692152903,60.2559031079253913,32.368092831934419]},"#773377":{"lch":[33.18286532501061,49.0630735877418189,307.715012949244226],"luv":[33.18286532501061,30.0135669321659,-38.8119954380674201],"rgb":[0.466666666666666674,0.2,0.466666666666666674],"xyz":[0.12120772895918859,0.0762197485872860153,0.182861381596565],"hpluv":[307.715012949244226,187.620458725202155,33.18286532501061],"hsluv":[307.715012949244226,64.9109644958811458,33.18286532501061]},"#773388":{"lch":[34.1232577260479,58.0074356166469371,296.464975996700218],"luv":[34.1232577260479,25.8510525803809479,-51.9286594021626868],"rgb":[0.466666666666666674,0.2,0.533333333333333326],"xyz":[0.132348146061132432,0.0806759154280636132,0.241534245000137177],"hpluv":[296.464975996700218,215.711106662248483,34.1232577260479],"hsluv":[296.464975996700218,69.2226633917654226,34.1232577260479]},"#773399":{"lch":[35.1823459678372572,68.3160830057283,288.793689920994325],"luv":[35.1823459678372572,22.0088074868374761,-64.6737936899706369],"rgb":[0.466666666666666674,0.2,0.6],"xyz":[0.145405110170205132,0.0858987010716927707,0.310300922641255084],"hpluv":[288.793689920994325,246.398176299420243,35.1823459678372572],"hsluv":[288.793689920994325,73.0971646513407,35.1823459678372572]},"#7733aa":{"lch":[36.3517007299652,79.1457370086552316,283.529620316849901],"luv":[36.3517007299652,18.5159885211015585,-76.9493720294691741],"rgb":[0.466666666666666674,0.2,0.66666666666666663],"xyz":[0.160462803135457688,0.0919217782577938791,0.389604772258253684],"hpluv":[283.529620316849901,276.275338575971432,36.3517007299652],"hsluv":[283.529620316849901,76.5077273075914093,36.3517007299652]},"#7733bb":{"lch":[37.6219984216960484,90.0609255961329183,279.823655256592],"luv":[37.6219984216960484,15.3658639022925581,-88.7404110074345596],"rgb":[0.466666666666666674,0.2,0.733333333333333282],"xyz":[0.177600395242794307,0.0987768151007286377,0.479862757356895431],"hpluv":[279.823655256592,303.762299466947354,37.6219984216960484],"hsluv":[279.823655256592,79.4699002038008615,37.6219984216960484]},"#7733cc":{"lch":[38.9835424310364687,100.85881095677091,277.137789072490136],"luv":[38.9835424310364687,12.5323059717609908,-100.077175492935524],"rgb":[0.466666666666666674,0.2,0.8],"xyz":[0.196892770686516061,0.106493765278217439,0.581469268027165587],"hpluv":[277.137789072490136,328.300745951509327,38.9835424310364687],"hsluv":[277.137789072490136,82.0218198813884,38.9835424310364687]},"#7733dd":{"lch":[40.4266769703902469,111.457640409912926,275.137669231249674],"luv":[40.4266769703902469,9.98093438608736,-111.009848907770674],"rgb":[0.466666666666666674,0.2,0.866666666666666696],"xyz":[0.218411096237149605,0.115101095498470973,0.694799115927171584],"hpluv":[275.137669231249674,349.849394775278881,40.4266769703902469],"hsluv":[275.137669231249674,84.2108753320266459,40.4266769703902469]},"#7733ee":{"lch":[41.9420918590451066,121.83559331992231,273.612094072304103],"luv":[41.9420918590451066,7.67578650940870766,-121.593561100411392],"rgb":[0.466666666666666674,0.2,0.933333333333333348],"xyz":[0.242223276503324342,0.12462596760494099,0.820209931995694808],"hpluv":[273.612094072304103,368.606867281158145,41.9420918590451066],"hsluv":[273.612094072304103,91.3228806729277665,41.9420918590451066]},"#7733ff":{"lch":[43.521028110395612,131.998699032970592,272.424037139620168],"luv":[43.521028110395612,5.58285979872954208,-131.88058319125102],"rgb":[0.466666666666666674,0.2,1],"xyz":[0.268394324913155735,0.135094386968873714,0.958044120287476653],"hpluv":[272.424037139620168,384.866252510120546,43.521028110395612],"hsluv":[272.424037139620168,99.9999999999993605,43.521028110395612]},"#eebb00":{"lch":[78.2979307719844115,92.4506686575273307,61.8965912674010781],"luv":[78.2979307719844115,43.5502151824584587,81.5506277890334275],"rgb":[0.933333333333333348,0.733333333333333282,0],"xyz":[0.530286541787721277,0.537195691732810121,0.0757595260297813378],"hpluv":[61.8965912674010781,173.778692590363192,78.2979307719844115],"hsluv":[61.8965912674010781,100.000000000002373,78.2979307719844115]},"#eebb11":{"lch":[78.3216028454340858,91.4822746680663528,61.6861071172261504],"luv":[78.3216028454340858,43.3901974999079059,80.5375523551748],"rgb":[0.933333333333333348,0.733333333333333282,0.0666666666666666657],"xyz":[0.531298207287358437,0.537600357932665,0.0810876309945369744],"hpluv":[61.6861071172261504,172.182825477748111,78.3216028454340858],"hsluv":[61.6861071172261504,98.7812238781787642,78.3216028454340858]},"#eebb22":{"lch":[78.3654531575016335,89.702527227679937,61.2866058237147371],"luv":[78.3654531575016335,43.0956539167563477,78.672155204492924],"rgb":[0.933333333333333348,0.733333333333333282,0.133333333333333331],"xyz":[0.533173565425835383,0.53835050118805583,0.090964517190516167],"hpluv":[61.2866058237147371,169.241982225815036,78.3654531575016335],"hsluv":[61.2866058237147371,96.5386826089343,78.3654531575016335]},"#eebb33":{"lch":[78.4375634156200192,86.8156112143301897,60.6012126372659665],"luv":[78.4375634156200192,42.6165086308843897,75.6358614853528906],"rgb":[0.933333333333333348,0.733333333333333282,0.2],"xyz":[0.5362613161582932,0.539585601481039,0.107226671048127303],"hpluv":[60.6012126372659665,164.44952499458185,78.4375634156200192],"hsluv":[60.6012126372659665,92.89304460263088,78.4375634156200192]},"#eebb44":{"lch":[78.5414800230656,82.7424491039972878,59.5464137785053],"luv":[78.5414800230656,41.9372005918819397,71.3273025583039839],"rgb":[0.933333333333333348,0.733333333333333282,0.266666666666666663],"xyz":[0.540719311402512059,0.541368799578726523,0.130705446001013942],"hpluv":[59.5464137785053,157.640041580932575,78.5414800230656],"hsluv":[59.5464137785053,87.729612164687,78.5414800230656]},"#eebb55":{"lch":[78.6801087960333,77.4695312482544,58.0014729567720551],"luv":[78.6801087960333,41.0509080282697809,65.6989438414256455],"rgb":[0.933333333333333348,0.733333333333333282,0.333333333333333315],"xyz":[0.546681725441874,0.543753765194471272,0.162107493274987857],"hpluv":[58.0014729567720551,148.738759066450825,78.6801087960333],"hsluv":[58.0014729567720551,81.0022494643358613,78.6801087960333]},"#eebb66":{"lch":[78.8558787138066748,71.0520814803831229,55.7790951769469174],"luv":[78.8558787138066748,39.9586324736418845,58.7512210386423703],"rgb":[0.933333333333333348,0.733333333333333282,0.4],"xyz":[0.55426672121538223,0.546787763503874591,0.202055137682131819],"hpluv":[55.7790951769469174,137.768430134850121,78.8558787138066748],"hsluv":[55.7790951769469174,72.7264864885570574,78.8558787138066748]},"#eebb77":{"lch":[79.0708286262536,63.6259938562226566,52.5733668905169],"luv":[79.0708286262536,38.6683825737350801,50.5274507898760348],"rgb":[0.933333333333333348,0.733333333333333282,0.466666666666666674],"xyz":[0.56358073423558408,0.550513368711955442,0.251108939588529756],"hpluv":[52.5733668905169,124.876553308513152,79.0708286262536],"hsluv":[52.5733668905169,64.3532422469102841,79.0708286262536]},"#eebb88":{"lch":[79.3266586201773833,55.4370708593257149,47.8613800964459557],"luv":[79.3266586201773833,37.1942049853437169,41.1079060640423961],"rgb":[0.933333333333333348,0.733333333333333282,0.533333333333333326],"xyz":[0.574721151337528,0.554969535552733,0.309781802992101896],"hpluv":[47.8613800964459557,110.403075040931697,79.3266586201773833],"hsluv":[47.8613800964459557,64.6428486235092379,79.3266586201773833]},"#eebb99":{"lch":[79.6247632766216071,46.9114913521919519,40.7190885070752131],"luv":[79.6247632766216071,35.5550187983100727,30.6027557441907163],"rgb":[0.933333333333333348,0.733333333333333282,0.6],"xyz":[0.587778115446600635,0.560192321196362197,0.378548480633219775],"hpluv":[40.7190885070752131,95.0436922052712845,79.6247632766216071],"hsluv":[40.7190885070752131,64.94797236425741,79.6247632766216071]},"#eebbaa":{"lch":[79.9662551563314,38.821829319770238,29.5464132636418633],"luv":[79.9662551563314,33.7733033676501,19.1441482278540249],"rgb":[0.933333333333333348,0.733333333333333282,0.66666666666666663],"xyz":[0.602835808411853247,0.566215398382463264,0.457852330250218431],"hpluv":[29.5464132636418633,80.2381481795300573,79.9662551563314],"hsluv":[29.5464132636418633,65.2570717669902081,79.9662551563314]},"#eebbbb":{"lch":[80.3519829843595,32.6073830473385868,12.1770506300627979],"luv":[80.3519829843595,31.8737319395561,6.87798236703069765],"rgb":[0.933333333333333348,0.733333333333333282,0.733333333333333282],"xyz":[0.61997340051918981,0.573070435225398,0.548110315348860122],"hpluv":[12.1770506300627979,68.9527679352530498,80.3519829843595],"hsluv":[12.1770506300627979,65.5572779496269646,80.3519829843595]},"#eebbcc":{"lch":[80.7825470949933,30.4869713003172755,348.56539147946927],"luv":[80.7825470949933,29.8818538307637169,-6.04402437976891349],"rgb":[0.933333333333333348,0.733333333333333282,0.8],"xyz":[0.639265775962911564,0.58078738540288688,0.649716826019130278],"hpluv":[348.56539147946927,66.1649916626687542,80.7825470949933],"hsluv":[348.56539147946927,65.8346704867411,80.7825470949933]},"#eebbdd":{"lch":[81.2583136554081165,33.9595526191752555,325.014354592586812],"luv":[81.2583136554081165,27.8229160969035512,-19.4714291709468021],"rgb":[0.933333333333333348,0.733333333333333282,0.866666666666666696],"xyz":[0.660784101513545163,0.589394715623140386,0.763046673919136276],"hpluv":[325.014354592586812,75.8902298772919721,81.2583136554081165],"hsluv":[325.014354592586812,66.0744631525984119,81.2583136554081165]},"#eebbee":{"lch":[81.7794285687783429,42.0458499431423576,307.715012949247921],"luv":[81.7794285687783429,25.7208902583653902,-33.2609275540891716],"rgb":[0.933333333333333348,0.733333333333333282,0.933333333333333348],"xyz":[0.684596281779719873,0.598919587729610403,0.888457489987659499],"hpluv":[307.715012949247921,97.0917602325266245,81.7794285687783429],"hsluv":[307.715012949247921,66.2610602343042103,81.7794285687783429]},"#eebbff":{"lch":[82.3458315671937697,52.842459086223478,296.523687653639684],"luv":[82.3458315671937697,23.5977388768935938,-47.2807804734349446],"rgb":[0.933333333333333348,0.733333333333333282,1],"xyz":[0.710767330189551294,0.609388007093543127,1.02629167827944134],"hpluv":[296.523687653639684,126.563624284615543,82.3458315671937697],"hsluv":[296.523687653639684,99.9999999999958504,82.3458315671937697]},"#774400":{"lch":[34.1007355557283631,52.2824067620925845,38.9690248280103901],"luv":[34.1007355557283631,40.6488429776156863,32.8804139483986475],"rgb":[0.466666666666666674,0.266666666666666663,0],"xyz":[0.0967461069942917862,0.0805672125740730105,0.0104561581988052085],"hpluv":[38.9690248280103901,194.549962435525771,34.1007355557283631],"hsluv":[38.9690248280103901,100.000000000002302,34.1007355557283631]},"#774411":{"lch":[34.1844760931178726,49.9480603042207,37.0032460066389604],"luv":[34.1844760931178726,39.8885915773263164,30.0617529450848728],"rgb":[0.466666666666666674,0.266666666666666663,0.0666666666666666657],"xyz":[0.0977577724939289,0.0809718787739278634,0.0157842631635608383],"hpluv":[37.0032460066389604,185.408237524396696,34.1844760931178726],"hsluv":[37.0032460066389604,92.0774266797174477,34.1844760931178726]},"#774422":{"lch":[34.3389737161705639,45.9687551907429608,33.0232295817012798],"luv":[34.3389737161705639,38.542488289151045,25.0520069109664867],"rgb":[0.466666666666666674,0.266666666666666663,0.133333333333333331],"xyz":[0.0996331306324059335,0.0817220220293186778,0.0256611493595400378],"hpluv":[33.0232295817012798,169.869245826721482,34.3389737161705639],"hsluv":[33.0232295817012798,78.0803941927279794,34.3389737161705639]},"#774433":{"lch":[34.5913049894652787,40.4066166426811861,25.4401077009201266],"luv":[34.5913049894652787,36.4885814660887746,17.357364174931714],"rgb":[0.466666666666666674,0.266666666666666663,0.2],"xyz":[0.102720881364863667,0.0829571223223017906,0.041923303217151181],"hpluv":[25.4401077009201266,148.226163260119392,34.5913049894652787],"hsluv":[25.4401077009201266,56.8050953227402715,34.5913049894652787]},"#774444":{"lch":[34.9512320153617324,34.6101233100924119,12.1770506300620909],"luv":[34.9512320153617324,33.8314114683579277,7.30042694631360689],"rgb":[0.466666666666666674,0.266666666666666663,0.266666666666666663],"xyz":[0.107178876609082541,0.0847403204199893706,0.0654020781700378195],"hpluv":[12.1770506300620909,125.655060642768362,34.9512320153617324],"hsluv":[12.1770506300620909,29.4448754689639038,34.9512320153617324]},"#774455":{"lch":[35.4248138391838907,31.0799263948782318,351.580242092888511],"luv":[35.4248138391838907,30.7449438027498871,-4.55085214842230634],"rgb":[0.466666666666666674,0.266666666666666663,0.333333333333333315],"xyz":[0.11314129064844454,0.0871252860357342,0.0968041254440117349],"hpluv":[351.580242092888511,111.329877567225012,35.4248138391838907],"hsluv":[351.580242092888511,35.0525071981411855,35.4248138391838907]},"#774466":{"lch":[36.0149447056398699,32.55773059028094,327.386935336661793],"luv":[36.0149447056398699,27.4243377055297302,-17.5474078599198116],"rgb":[0.466666666666666674,0.266666666666666663,0.4],"xyz":[0.120726286421952711,0.090159284345137522,0.136751769851155697],"hpluv":[327.386935336661793,114.712488009680612,36.0149447056398699],"hsluv":[327.386935336661793,41.0162201785438469,36.0149447056398699]},"#774477":{"lch":[36.7217587051522898,39.3078830273429531,307.715012949244965],"luv":[36.7217587051522898,24.0459818745983789,-31.0950224919964064],"rgb":[0.466666666666666674,0.266666666666666663,0.466666666666666674],"xyz":[0.130040299442154617,0.0938848895532183314,0.185805571757553606],"hpluv":[307.715012949244965,135.82994003022975,36.7217587051522898],"hsluv":[307.715012949244965,46.9930223744603381,36.7217587051522898]},"#774488":{"lch":[37.5430301686231331,49.3077922843884906,294.880307130505798],"luv":[37.5430301686231331,20.7449732069104158,-44.731470651041306],"rgb":[0.466666666666666674,0.266666666666666663,0.533333333333333326],"xyz":[0.141180716544098472,0.0983410563939959292,0.244478435161125773],"hpluv":[294.880307130505798,166.657768282509579,37.5430301686231331],"hsluv":[294.880307130505798,52.7229750286883814,37.5430301686231331]},"#774499":{"lch":[38.4745988052595678,60.7540316024919775,286.849160899925266],"luv":[38.4745988052595678,17.609743748149274,-58.1459309073396042],"rgb":[0.466666666666666674,0.266666666666666663,0.6],"xyz":[0.154237680653171172,0.103563842037625087,0.313245112802243653],"hpluv":[286.849160899925266,200.373520820200838,38.4745988052595678],"hsluv":[286.849160899925266,58.0391908614757739,38.4745988052595678]},"#7744aa":{"lch":[39.5108096650206306,72.6607854160185695,281.662296555210446],"luv":[39.5108096650206306,14.6878600390207232,-71.1607792589909423],"rgb":[0.466666666666666674,0.266666666666666663,0.66666666666666663],"xyz":[0.169295373618423728,0.109586919223726181,0.392548962419242253],"hpluv":[281.662296555210446,233.358425064810547,39.5108096650206306],"hsluv":[281.662296555210446,62.855979622580108,39.5108096650206306]},"#7744bb":{"lch":[40.6449442050895,84.5532389367896684,278.156646558388104],"luv":[40.6449442050895,11.9964108827533504,-83.6978873128468],"rgb":[0.466666666666666674,0.266666666666666663,0.733333333333333282],"xyz":[0.186432965725760347,0.11644195606666094,0.482806947517884],"hpluv":[278.156646558388104,263.975148792079324,40.6449442050895],"hsluv":[278.156646558388104,67.1480450168623832,40.6449442050895]},"#7744cc":{"lch":[41.8696179576200223,96.2178725735773099,275.685728840067554],"luv":[41.8696179576200223,9.53248740063005151,-95.7445073439828747],"rgb":[0.466666666666666674,0.266666666666666663,0.8],"xyz":[0.205725341169482101,0.124158906244149742,0.584413458188154156],"hpluv":[275.685728840067554,291.605746800226,41.8696179576200223],"hsluv":[275.685728840067554,71.7563947172966721,41.8696179576200223]},"#7744dd":{"lch":[43.1771261833787037,107.572507589609089,273.881356516641858],"luv":[43.1771261833787037,7.28165300546471084,-107.325774717094461],"rgb":[0.466666666666666674,0.266666666666666663,0.866666666666666696],"xyz":[0.227243666720115645,0.132766236464403276,0.697743306088160153],"hpluv":[273.881356516641858,316.145413845169799,43.1771261833787037],"hsluv":[273.881356516641858,81.1040313116107683,43.1771261833787037]},"#7744ee":{"lch":[44.5597272061305958,118.600491111934247,272.524492751992966],"luv":[44.5597272061305958,5.22393127379498168,-118.485387428318177],"rgb":[0.466666666666666674,0.266666666666666663,0.933333333333333348],"xyz":[0.251055846986290354,0.14229110857087332,0.823154122156683377],"hpluv":[272.524492751992966,337.740615485066769,44.5597272061305958],"hsluv":[272.524492751992966,90.4775463334620866,44.5597272061305958]},"#7744ff":{"lch":[46.0098610845945188,129.316457315512423,271.478956563127554],"luv":[46.0098610845945188,3.33763127257498882,-129.273378350389976],"rgb":[0.466666666666666674,0.266666666666666663,1],"xyz":[0.277226895396121775,0.152759527934806016,0.960988310448465222],"hpluv":[271.478956563127554,356.649979093308843,46.0098610845945188],"hsluv":[271.478956563127554,99.9999999999992752,46.0098610845945188]},"#eecc00":{"lch":[82.5742071813858161,93.0890420253441278,70.6743105766144737],"luv":[82.5742071813858161,30.8066573410433797,87.8437226480516529],"rgb":[0.933333333333333348,0.8,0],"xyz":[0.568510285097338142,0.613643178352045071,0.088500773799653279],"hpluv":[70.6743105766144737,226.330948640265689,82.5742071813858161],"hsluv":[70.6743105766144737,100.000000000002331,82.5742071813858161]},"#eecc11":{"lch":[82.5958706312260773,92.1609271203023,70.5494354113464226],"luv":[82.5958706312260773,30.6889817522442812,86.9012248859824297],"rgb":[0.933333333333333348,0.8,0.0666666666666666657],"xyz":[0.569521950596975302,0.6140478445519,0.0938288787644089156],"hpluv":[70.5494354113464226,224.395635781555484,82.5958706312260773],"hsluv":[70.5494354113464226,98.9293851282895389,82.5958706312260773]},"#eecc22":{"lch":[82.636003730849751,90.4517890224530277,70.3125751850760849],"luv":[82.636003730849751,30.4721781878481224,85.1643851257694848],"rgb":[0.933333333333333348,0.8,0.133333333333333331],"xyz":[0.571397308735452247,0.61479798780729078,0.103705764960388108],"hpluv":[70.3125751850760849,220.820363432361461,82.636003730849751],"hsluv":[70.3125751850760849,96.9576791715233668,82.636003730849751]},"#eecc33":{"lch":[82.7020112487531,87.6695564091762236,69.9066450473675332],"luv":[82.7020112487531,30.1189443150749909,82.3334701948554084],"rgb":[0.933333333333333348,0.8,0.2],"xyz":[0.574485059467910064,0.616033088100273907,0.119967918817999258],"hpluv":[69.9066450473675332,214.968262161852493,82.7020112487531],"hsluv":[69.9066450473675332,93.7473974454290726,82.7020112487531]},"#eecc44":{"lch":[82.7971553193909102,83.7217279628800242,69.2828434677537786],"luv":[82.7971553193909102,29.6169744667019792,78.3081257375581],"rgb":[0.933333333333333348,0.8,0.266666666666666663],"xyz":[0.578943054712128924,0.617816286197961473,0.143446693770885897],"hpluv":[69.2828434677537786,206.594355258371763,82.7971553193909102],"hsluv":[69.2828434677537786,89.1900904304584685,82.7971553193909102]},"#eecc55":{"lch":[82.9241214704336471,78.5673404382155098,68.3706518767947387],"luv":[82.9241214704336471,28.9599810639926183,73.0352413585910085],"rgb":[0.933333333333333348,0.8,0.333333333333333315],"xyz":[0.584905468751490853,0.620201251813706222,0.174848741044859812],"hpluv":[68.3706518767947387,195.532369916851508,82.9241214704336471],"hsluv":[68.3706518767947387,83.2339277722755497,82.9241214704336471]},"#eecc66":{"lch":[83.0851700146488241,72.2150018940459688,67.0599726095305186],"luv":[83.0851700146488241,28.1470538355007811,66.503758231685552],"rgb":[0.933333333333333348,0.8,0.4],"xyz":[0.592490464524999094,0.623235250123109541,0.214796385452003746],"hpluv":[67.0599726095305186,181.688085793908328,83.0851700146488241],"hsluv":[67.0599726095305186,75.8779818086204898,83.0851700146488241]},"#eecc77":{"lch":[83.2822165713090925,64.7250307453925586,65.1677162202921778],"luv":[83.2822165713090925,27.1821511319325673,58.740618526133936],"rgb":[0.933333333333333348,0.8,0.466666666666666674],"xyz":[0.601804477545200944,0.626960855331190392,0.263850187358401655],"hpluv":[65.1677162202921778,165.044758034407693,83.2822165713090925],"hsluv":[65.1677162202921778,67.1675472191497533,83.2822165713090925]},"#eecc88":{"lch":[83.5168798492942699,56.2182636624862511,62.3679934299132839],"luv":[83.5168798492942699,26.0735255024375405,49.8062690541919793],"rgb":[0.933333333333333348,0.8,0.533333333333333326],"xyz":[0.612944894647144856,0.631417022171967934,0.322523050761973851],"hpluv":[62.3679934299132839,145.689263900370548,83.5168798492942699],"hsluv":[62.3679934299132839,57.1887031148042198,83.5168798492942699]},"#eecc99":{"lch":[83.79051243806407,46.9027423702446598,58.0311593919332083],"luv":[83.79051243806407,24.8330316313984234,39.7892923013655633],"rgb":[0.933333333333333348,0.8,0.6],"xyz":[0.6260018587562175,0.636639807815597147,0.39128972840309173],"hpluv":[58.0311593919332083,123.89218615802821,83.79051243806407],"hsluv":[58.0311593919332083,56.9799526918745158,83.79051243806407]},"#eeccaa":{"lch":[84.1042222244633,37.1556922719042646,50.8162292091241454],"luv":[84.1042222244633,23.475329457535171,28.8002495660129298],"rgb":[0.933333333333333348,0.8,0.66666666666666663],"xyz":[0.641059551721470111,0.642662885001698214,0.470593578020090331],"hpluv":[50.8162292091241454,100.353156070069275,84.1042222244633],"hsluv":[50.8162292091241454,57.1375361174420533,84.1042222244633]},"#eeccbb":{"lch":[84.4588885299527,27.7950211383377486,37.6158086991229084],"luv":[84.4588885299527,22.0170273242058485,16.9650731765535063],"rgb":[0.933333333333333348,0.8,0.733333333333333282],"xyz":[0.658197143828806674,0.649517921844633,0.560851563118732077],"hpluv":[37.6158086991229084,77.0182985337263517,84.4588885299527],"hsluv":[37.6158086991229084,57.2612530554736736,84.4588885299527]},"#eecccc":{"lch":[84.8551753311588897,20.9471233587290264,12.1770506300631585],"luv":[84.8551753311588897,20.4758227261490333,4.41844550641173317],"rgb":[0.933333333333333348,0.8,0.8],"xyz":[0.677489519272528429,0.65723487202212183,0.662458073789002233],"hpluv":[12.1770506300631585,59.764130158742411,84.8551753311588897],"hsluv":[12.1770506300631585,57.3329985994000637,84.8551753311588897]},"#eeccdd":{"lch":[85.2935429882433596,20.7797326136980232,335.241604291037675],"luv":[85.2935429882433596,18.8696973425293919,-8.70240252448279],"rgb":[0.933333333333333348,0.8,0.866666666666666696],"xyz":[0.699007844823162,0.665842202242375336,0.775787921689008231],"hpluv":[335.241604291037675,61.2821093808529582,85.2935429882433596],"hsluv":[335.241604291037675,57.3323512744098664,85.2935429882433596]},"#eeccee":{"lch":[85.7742593547863805,28.14328338963319,307.715012949251047],"luv":[85.7742593547863805,17.2162128855465397,-22.2631177921859056],"rgb":[0.933333333333333348,0.8,0.933333333333333348],"xyz":[0.722820025089336737,0.675367074348845353,0.901198737757531454],"hpluv":[307.715012949251047,86.153290074940827,85.7742593547863805],"hsluv":[307.715012949251047,57.2362127773837557,85.7742593547863805]},"#eeccff":{"lch":[86.2974107975625344,39.333162863939684,293.258584701896098],"luv":[86.2974107975625344,15.5319389668875889,-36.1366375415599919],"rgb":[0.933333333333333348,0.8,1],"xyz":[0.748991073499168158,0.685835493712778077,1.03903292604931341],"hpluv":[293.258584701896098,125.558261528980708,86.2974107975625344],"hsluv":[293.258584701896098,99.9999999999940314,86.2974107975625344]},"#775500":{"lch":[38.5848153490983421,48.2339285723334328,54.8056311564330656],"luv":[38.5848153490983421,27.7997213245256276,39.4168410682499868],"rgb":[0.466666666666666674,0.333333333333333315,0],"xyz":[0.108559363708637752,0.104193726002765275,0.0143939104369204193],"hpluv":[54.8056311564330656,158.626424871442595,38.5848153490983421],"hsluv":[54.8056311564330656,100.000000000002302,38.5848153490983421]},"#775511":{"lch":[38.6553893217116595,45.9150245993572952,53.4685955296553956],"luv":[38.6553893217116595,27.3315290852913257,36.8941323494598521],"rgb":[0.466666666666666674,0.333333333333333315,0.0666666666666666657],"xyz":[0.10957102920827487,0.104598392202620127,0.0197220154016760491],"hpluv":[53.4685955296553956,150.724584795248319,38.6553893217116595],"hsluv":[53.4685955296553956,93.8009130268131344,38.6553893217116595]},"#775522":{"lch":[38.7857346943923531,41.8305053308362176,50.7011784026830412],"luv":[38.7857346943923531,26.4939762066725,32.3706719268134577],"rgb":[0.466666666666666674,0.333333333333333315,0.133333333333333331],"xyz":[0.111446387346751899,0.105348535458010942,0.0295989015976552486],"hpluv":[50.7011784026830412,136.854920013655288,38.7857346943923531],"hsluv":[50.7011784026830412,82.7342982429400138,38.7857346943923531]},"#775533":{"lch":[38.9990050188249739,35.7224202133018096,45.1472123185964946],"luv":[38.9990050188249739,25.1945818654867857,25.3243825298663161],"rgb":[0.466666666666666674,0.333333333333333315,0.2],"xyz":[0.114534138079209633,0.106583635750994055,0.0458610554552663918],"hpluv":[45.1472123185964946,116.232257903435652,38.9990050188249739],"hsluv":[45.1472123185964946,65.6272728324870656,38.9990050188249739]},"#775544":{"lch":[39.3040305977305735,28.372964966311546,34.1730058033032336],"luv":[39.3040305977305735,23.4742392428959334,15.9369141601135258],"rgb":[0.466666666666666674,0.333333333333333315,0.266666666666666663],"xyz":[0.118992133323428506,0.108366833848681635,0.0693398304081530303],"hpluv":[34.1730058033032336,91.6024225041024,39.3040305977305735],"hsluv":[34.1730058033032336,43.1049791856067799,39.3040305977305735]},"#775555":{"lch":[39.7068052905653701,21.9117916496410494,12.1770506300624941],"luv":[39.7068052905653701,21.4187864245998618,4.62192615633957704],"rgb":[0.466666666666666674,0.333333333333333315,0.333333333333333315],"xyz":[0.124954547362790505,0.110751799464426468,0.100741877682126946],"hpluv":[12.1770506300624941,70.0248633547080601,39.7068052905653701],"hsluv":[12.1770506300624941,16.4089959502104463,39.7068052905653701]},"#775566":{"lch":[40.21091767922141,20.7753835118276839,337.09176723456028],"luv":[40.21091767922141,19.1368182791551824,-8.08694912894311],"rgb":[0.466666666666666674,0.333333333333333315,0.4],"xyz":[0.132539543136298676,0.113785797773829786,0.140689522089270908],"hpluv":[337.09176723456028,65.5608222190156482,40.21091767922141],"hsluv":[337.09176723456028,22.8015335286909036,40.21091767922141]},"#775577":{"lch":[40.8178321801082404,27.3616881241227183,307.715012949246557],"luv":[40.8178321801082404,16.7380842217685313,-21.6448264854847388],"rgb":[0.466666666666666674,0.333333333333333315,0.466666666666666674],"xyz":[0.141853556156500582,0.117511402981910595,0.189743323995668817],"hpluv":[307.715012949246557,85.0613515767839,40.8178321801082404],"hsluv":[307.715012949246557,29.4286369924042717,40.8178321801082404]},"#775588":{"lch":[41.5271394874135922,38.3484586217177892,291.922773984013077],"luv":[41.5271394874135922,14.317648223710199,-35.5754020076188269],"rgb":[0.466666666666666674,0.333333333333333315,0.533333333333333326],"xyz":[0.152993973258444438,0.121967569822688193,0.248416187399240984],"hpluv":[291.922773984013077,117.180466459805771,41.5271394874135922],"hsluv":[291.922773984013077,36.0000576829853429,41.5271394874135922]},"#775599":{"lch":[42.336815252734,50.9328741768069335,283.566916489070877],"luv":[42.336815252734,11.9478768110398388,-49.5116744982298442],"rgb":[0.466666666666666674,0.333333333333333315,0.6],"xyz":[0.166050937367517137,0.127190355466317351,0.317182865040358863],"hpluv":[283.566916489070877,152.657914615048412,42.336815252734],"hsluv":[283.566916489070877,42.2975255710275704,42.336815252734]},"#7755aa":{"lch":[43.2434937800222059,63.9392804809027098,278.705184193400783],"luv":[43.2434937800222059,9.67722671121172517,-63.202712533524668],"rgb":[0.466666666666666674,0.333333333333333315,0.66666666666666663],"xyz":[0.181108630332769693,0.133213432652418445,0.396486714657357464],"hpluv":[278.705184193400783,187.623095354238075,43.2434937800222059],"hsluv":[278.705184193400783,48.1780536314011272,43.2434937800222059]},"#7755bb":{"lch":[44.2427493107278096,76.8690732738289171,275.624120681329657],"luv":[44.2427493107278096,7.53331271169703509,-76.4990432983646116],"rgb":[0.466666666666666674,0.333333333333333315,0.733333333333333282],"xyz":[0.198246222440106312,0.140068469495353204,0.486744699755999211],"hpluv":[275.624120681329657,220.469676556121556,44.2427493107278096],"hsluv":[275.624120681329657,58.1137757976744496,44.2427493107278096]},"#7755cc":{"lch":[45.3293721892173949,89.4999308488430785,273.541003165926895],"luv":[45.3293721892173949,5.52776894932058127,-89.3290624175056536],"rgb":[0.466666666666666674,0.333333333333333315,0.8],"xyz":[0.217538597883828067,0.147785419672842,0.588351210426269366],"hpluv":[273.541003165926895,250.543028147628775,45.3293721892173949],"hsluv":[273.541003165926895,68.560007412736141,45.3293721892173949]},"#7755dd":{"lch":[46.4976270234735622,101.740884988783861,272.062259876532949],"luv":[46.4976270234735622,3.66119191329505167,-101.674988822595736],"rgb":[0.466666666666666674,0.333333333333333315,0.866666666666666696],"xyz":[0.23905692343446161,0.15639274989309554,0.701681058326275364],"hpluv":[272.062259876532949,277.654072578046794,46.4976270234735622],"hsluv":[272.062259876532949,78.9623412233913911,46.4976270234735622]},"#7755ee":{"lch":[47.7414825998049253,113.569026574645434,270.972372145675877],"luv":[47.7414825998049253,1.92729827623402139,-113.552672000560818],"rgb":[0.466666666666666674,0.333333333333333315,0.933333333333333348],"xyz":[0.26286910370063632,0.165917621999565584,0.827091874394798587],"hpluv":[270.972372145675877,301.858443342198598,47.7414825998049253],"hsluv":[270.972372145675877,89.4067229175139175,47.7414825998049253]},"#7755ff":{"lch":[49.0548071408334749,124.996939079083958,270.144864217432826],"luv":[49.0548071408334749,0.31603661949764611,-124.996539552082652],"rgb":[0.466666666666666674,0.333333333333333315,1],"xyz":[0.289040152110467741,0.17638604136349828,0.964926062686580432],"hpluv":[270.144864217432826,323.338286172745597,49.0548071408334749],"hsluv":[270.144864217432826,99.9999999999992184,49.0548071408334749]},"#eedd00":{"lch":[86.9434330779808562,96.0018853881048528,78.7633058197047831],"luv":[86.9434330779808562,18.7071720605167293,94.1615829920517],"rgb":[0.933333333333333348,0.866666666666666696,0],"xyz":[0.611144275644513346,0.69891115944639659,0.102712103982044597],"hpluv":[78.7633058197047831,323.365375109368927,86.9434330779808562],"hsluv":[78.7633058197047831,100.000000000002331,86.9434330779808562]},"#eedd11":{"lch":[86.9632971622836,95.1251800329902153,78.7103442402005555],"luv":[86.9632971622836,18.6225708681111755,93.2845095960255293],"rgb":[0.933333333333333348,0.866666666666666696,0.0666666666666666657],"xyz":[0.612155941144150506,0.699315825646251499,0.108040208946800234],"hpluv":[78.7103442402005555,320.953864246165836,86.9632971622836],"hsluv":[78.7103442402005555,99.0572185708442134,86.9632971622836]},"#eedd22":{"lch":[87.0000996195567,93.508678235103929,78.6100630695382421],"luv":[87.0000996195567,18.466577094457584,91.6671066178629559],"rgb":[0.933333333333333348,0.866666666666666696,0.133333333333333331],"xyz":[0.614031299282627452,0.700065968901642299,0.117917095142779427],"hpluv":[78.6100630695382421,316.490280961531141,87.0000996195567],"hsluv":[78.6100630695382421,97.3195837272491104,87.0000996195567]},"#eedd33":{"lch":[87.0606371109171704,90.8714304533391157,78.438725785200063],"luv":[87.0606371109171704,18.2120693177914781,89.0277339024194561],"rgb":[0.933333333333333348,0.866666666666666696,0.2],"xyz":[0.617119050015085269,0.701301069194625426,0.134179249000390577],"hpluv":[78.438725785200063,309.159510866930361,87.0606371109171704],"hsluv":[78.438725785200063,94.4866487647953335,87.0606371109171704]},"#eedd44":{"lch":[87.1479139339873399,87.1163282896156375,78.1766823729436737],"luv":[87.1479139339873399,17.8496481580416315,85.2680755927926839],"rgb":[0.933333333333333348,0.866666666666666696,0.266666666666666663],"xyz":[0.621577045259304128,0.703084267292313,0.157658023953277215],"hpluv":[78.1766823729436737,298.614243514229941,87.1479139339873399],"hsluv":[78.1766823729436737,90.4569971269957449,87.1479139339873399]},"#eedd55":{"lch":[87.2644132886328,82.1887294441124823,77.7961103956051119],"luv":[87.2644132886328,17.3739699699556027,80.3313912186301],"rgb":[0.933333333333333348,0.866666666666666696,0.333333333333333315],"xyz":[0.627539459298666058,0.705469232908057742,0.189060071227251103],"hpluv":[77.7961103956051119,284.577035202400168,87.2644132886328],"hsluv":[77.7961103956051119,85.1762064326895114,87.2644132886328]},"#eedd66":{"lch":[87.4122373516825775,76.0722975624531,77.2543738936701],"luv":[87.4122373516825775,16.7832968217641607,74.1978126646801428],"rgb":[0.933333333333333348,0.866666666666666696,0.4],"xyz":[0.635124455072174299,0.70850323121746106,0.229007715634395093],"hpluv":[77.2543738936701,266.820521177772889,87.4122373516825775],"hsluv":[77.2543738936701,78.6319742502837187,87.4122373516825775]},"#eedd77":{"lch":[87.5931821049200323,68.7867162946957,76.4818073814204666],"luv":[87.5931821049200323,16.0791769624109904,66.8810317415814524],"rgb":[0.933333333333333348,0.866666666666666696,0.466666666666666674],"xyz":[0.644438468092376149,0.712228836425541911,0.278061517540793],"hpluv":[76.4818073814204666,245.153905791490757,87.5931821049200323],"hsluv":[76.4818073814204666,70.850552214552053,87.5931821049200323]},"#eedd88":{"lch":[87.8087818591101694,60.3863988515096,75.3563176494847],"luv":[87.8087818591101694,15.266108392374667,58.4248500280990655],"rgb":[0.933333333333333348,0.866666666666666696,0.533333333333333326],"xyz":[0.65557888519432006,0.716685003266319454,0.336734380944365141],"hpluv":[75.3563176494847,219.413652035392374,87.8087818591101694],"hsluv":[75.3563176494847,61.8928475378351,87.8087818591101694]},"#eedd99":{"lch":[88.0603378936253165,50.9615259485641658,73.6438422487017164],"luv":[88.0603378936253165,14.3511385044238935,48.8990996914362483],"rgb":[0.933333333333333348,0.866666666666666696,0.6],"xyz":[0.668635849303392704,0.721907788909948667,0.405501058585483076],"hpluv":[73.6438422487017164,189.463858183723431,88.0603378936253165],"hsluv":[73.6438422487017164,51.8496821371243328,88.0603378936253165]},"#eeddaa":{"lch":[88.3489381850503719,40.6474355343677374,70.8361013393810595],"luv":[88.3489381850503719,13.3433963411670824,38.3948927541556],"rgb":[0.933333333333333348,0.866666666666666696,0.66666666666666663],"xyz":[0.683693542268645316,0.727930866096049733,0.484804908202481677],"hpluv":[70.8361013393810595,155.230940371192503,88.3489381850503719],"hsluv":[70.8361013393810595,42.897511243529749,88.3489381850503719]},"#eeddbb":{"lch":[88.6754719765582422,29.6681200622416164,65.6052069824857256],"luv":[88.6754719765582422,12.2535763784239329,27.0193858917206811],"rgb":[0.933333333333333348,0.866666666666666696,0.733333333333333282],"xyz":[0.700831134375981879,0.734785902938984492,0.575062893301123368],"hpluv":[65.6052069824857256,116.881954111652547,88.6754719765582422],"hsluv":[65.6052069824857256,42.5880034653653823,88.6754719765582422]},"#eeddcc":{"lch":[89.0406413623298,18.5684245799280596,53.3136761054906927],"luv":[89.0406413623298,11.093403559257359,14.8903589228712931],"rgb":[0.933333333333333348,0.866666666666666696,0.8],"xyz":[0.720123509819703633,0.742502853116473349,0.676669403971393524],"hpluv":[53.3136761054906927,75.8171941810365837,89.0406413623298],"hsluv":[53.3136761054906927,42.1389745374101,89.0406413623298]},"#eedddd":{"lch":[89.4449712115231677,10.1024117660870978,12.1770506300652031],"luv":[89.4449712115231677,9.87511215198720294,2.13093488339032255],"rgb":[0.933333333333333348,0.866666666666666696,0.866666666666666696],"xyz":[0.741641835370337232,0.751110183336726855,0.789999251871399522],"hpluv":[12.1770506300652031,42.9711785560074802,89.4449712115231677],"hsluv":[12.1770506300652031,41.5103310668104939,89.4449712115231677]},"#eeddee":{"lch":[89.8888182614484919,14.0763196099440542,307.715012949260654],"luv":[89.8888182614484919,8.61096808409750203,-11.1352594229299271],"rgb":[0.933333333333333348,0.866666666666666696,0.933333333333333348],"xyz":[0.765454015636511942,0.760635055443196872,0.915410067939922745],"hpluv":[307.715012949260654,62.7286135322124423,89.8888182614484919],"hsluv":[307.715012949260654,40.6526002298302203,89.8888182614484919]},"#eeddff":{"lch":[90.3723799019863776,25.8444533047225526,286.436741223308786],"luv":[90.3723799019863776,7.31285770395748802,-24.7882609075678033],"rgb":[0.933333333333333348,0.866666666666666696,1],"xyz":[0.791625064046343363,0.771103474807129596,1.05324425623170459],"hpluv":[286.436741223308786,121.429851146909542,90.3723799019863776],"hsluv":[286.436741223308786,99.9999999999912461,90.3723799019863776]},"#776600":{"lch":[43.3967364031710616,48.7618731822316747,71.5665709091534836],"luv":[43.3967364031710616,15.4186312136361749,46.2599836547520695],"rgb":[0.466666666666666674,0.4,0],"xyz":[0.123587421414484214,0.134249841414458615,0.0194032630055357667],"hpluv":[71.5665709091534836,142.581321953300886,43.3967364031710616],"hsluv":[71.5665709091534836,100.000000000002203,43.3967364031710616]},"#776611":{"lch":[43.4563559440565683,46.5521225716425278,71.0105555334865812],"luv":[43.4563559440565683,15.1477793781447385,44.0186880294754204],"rgb":[0.466666666666666674,0.4,0.0666666666666666657],"xyz":[0.124599086914121332,0.134654507614313468,0.0247313679702914],"hpluv":[71.0105555334865812,135.933189968578517,43.4563559440565683],"hsluv":[71.0105555334865812,95.1446041192036,43.4563559440565683]},"#776622":{"lch":[43.5665595032511135,42.5748985848782695,69.8598868578742582],"luv":[43.5665595032511135,14.6592646006394602,39.9715892964128727],"rgb":[0.466666666666666674,0.4,0.133333333333333331],"xyz":[0.126474445052598361,0.135404650869704296,0.0346082541662705925],"hpluv":[69.8598868578742582,124.005139157578384,43.5665595032511135],"hsluv":[69.8598868578742582,86.4059851293991699,43.5665595032511135]},"#776633":{"lch":[43.7471247164395862,36.3538810550032,67.535659471968259],"luv":[43.7471247164395862,13.8911218179866047,33.5952586297408544],"rgb":[0.466666666666666674,0.4,0.2],"xyz":[0.129562195785056095,0.136639751162687395,0.0508704080238817358],"hpluv":[67.535659471968259,105.448545621014873,43.7471247164395862],"hsluv":[67.535659471968259,72.7162107178341,43.7471247164395862]},"#776644":{"lch":[44.0059094002531381,28.0798421405657628,62.7556004259007807],"luv":[44.0059094002531381,12.8545871201011757,24.9647175952547435],"rgb":[0.466666666666666674,0.4,0.266666666666666663],"xyz":[0.134020191029274982,0.138422949260375,0.0743491829767683743],"hpluv":[62.7556004259007807,80.969785387169,44.0059094002531381],"hsluv":[62.7556004259007807,54.3487361937933,44.0059094002531381]},"#776655":{"lch":[44.348573895236818,18.4503377813959695,51.0995937541716287],"luv":[44.348573895236818,11.5862323365157813,14.3587668165439446],"rgb":[0.466666666666666674,0.4,0.333333333333333315],"xyz":[0.139982605068636967,0.140807914876119822,0.10575123025074229],"hpluv":[51.0995937541716287,52.7914984191523473,44.348573895236818],"hsluv":[51.0995937541716287,32.0544530960382303,44.348573895236818]},"#776666":{"lch":[44.7789425039584543,10.3722772339139464,12.177050630063178],"luv":[44.7789425039584543,10.1389057710203723,2.18785849257654696],"rgb":[0.466666666666666674,0.4,0.4],"xyz":[0.147567600842145152,0.143841913185523113,0.145698874657886251],"hpluv":[12.177050630063178,29.3927086392471182,44.7789425039584543],"hsluv":[12.177050630063178,6.88762268030489189,44.7789425039584543]},"#776677":{"lch":[45.2992151274866899,14.0123007519981453,307.715012949251729],"luv":[45.2992151274866899,8.57180555029232849,-11.0846164558105222],"rgb":[0.466666666666666674,0.4,0.466666666666666674],"xyz":[0.156881613862347058,0.147567518393603936,0.194752676564284161],"hpluv":[307.715012949251729,39.2516664249610088,45.2992151274866899],"hsluv":[307.715012949251729,13.5798811229143528,45.2992151274866899]},"#776688":{"lch":[45.9101336093725934,25.9566305949561666,285.511882327844],"luv":[45.9101336093725934,6.94179491604529719,-25.0111606125461954],"rgb":[0.466666666666666674,0.4,0.533333333333333326],"xyz":[0.168022030964290886,0.152023685234381534,0.253425539967856328],"hpluv":[285.511882327844,71.7429261821633304,45.9101336093725934],"hsluv":[285.511882327844,20.4210179876919788,45.9101336093725934]},"#776699":{"lch":[46.6111419677041781,39.5636276789354753,277.694853057332239],"luv":[46.6111419677041781,5.29745753469698322,-39.2073663842078446],"rgb":[0.466666666666666674,0.4,0.6],"xyz":[0.181078995073363613,0.157246470878010691,0.322192217608974207],"hpluv":[277.694853057332239,107.707436484347127,46.6111419677041781],"hsluv":[277.694853057332239,30.3115853005661187,46.6111419677041781]},"#7766aa":{"lch":[47.4005539940279945,53.5010996637438936,273.94010537559177],"luv":[47.4005539940279945,3.67625443841083355,-53.3746458398921959],"rgb":[0.466666666666666674,0.4,0.66666666666666663],"xyz":[0.196136688038616142,0.163269548064111786,0.401496067225972808],"hpluv":[273.94010537559177,143.224929654224553,47.4005539940279945],"hsluv":[273.94010537559177,41.7476492103462178,47.4005539940279945]},"#7766bb":{"lch":[48.2757296522395052,67.3356727079262356,271.790784098674919],"luv":[48.2757296522395052,2.10423911354751,-67.3027859511178],"rgb":[0.466666666666666674,0.4,0.733333333333333282],"xyz":[0.213274280145952788,0.170124584907046544,0.491754052324614555],"hpluv":[271.790784098674919,176.992833999799871,48.2757296522395052],"hsluv":[271.790784098674919,53.26174830093764,48.2757296522395052]},"#7766cc":{"lch":[49.2332558923506838,80.8618894300095263,270.423298283194697],"luv":[49.2332558923506838,0.597398061184884921,-80.8596826468393886],"rgb":[0.466666666666666674,0.4,0.8],"xyz":[0.232566655589674542,0.177841535084535346,0.59336056299488471],"hpluv":[270.423298283194697,208.412927467485218,49.2332558923506838],"hsluv":[270.423298283194697,64.812294986267446,49.2332558923506838]},"#7766dd":{"lch":[50.2691251722936698,93.9789905273776327,269.490145829531343],"luv":[50.2691251722936698,-0.836273601566472236,-93.9752696564806769],"rgb":[0.466666666666666674,0.4,0.866666666666666696],"xyz":[0.254084981140308086,0.18644886530478888,0.706690410894890708],"hpluv":[269.490145829531343,237.229544488008315,50.2691251722936698],"hsluv":[269.490145829531343,76.411365903071939,50.2691251722936698]},"#7766ee":{"lch":[51.378904811117593,106.647241299318011,268.82087794331261],"luv":[51.378904811117593,-2.19459861633770803,-106.624658562961542],"rgb":[0.466666666666666674,0.4,0.933333333333333348],"xyz":[0.277897161406482796,0.195973737411258925,0.832101226963413931],"hpluv":[268.82087794331261,263.392927722023558,51.378904811117593],"hsluv":[268.82087794331261,88.1124589032604177,51.378904811117593]},"#7766ff":{"lch":[52.5578914047646748,118.864223743766644,268.322642340607558],"luv":[52.5578914047646748,-3.47930230784918848,-118.81329109850806],"rgb":[0.466666666666666674,0.4,1],"xyz":[0.304068209816314217,0.206442156775191621,0.969935415255195776],"hpluv":[268.322642340607558,286.980608281330717,52.5578914047646748],"hsluv":[268.322642340607558,99.9999999999990621,52.5578914047646748]},"#eeee00":{"lch":[91.3819857871042416,100.73955854358779,85.8743202181747591],"luv":[91.3819857871042416,7.24765584469138,100.478505862268193],"rgb":[0.933333333333333348,0.933333333333333348,0],"xyz":[0.658323051985028163,0.793268712127427555,0.118438362762215782],"hpluv":[85.8743202181747591,533.074105620447313,91.3819857871042416],"hsluv":[85.8743202181747591,100.000000000002302,91.3819857871042416]},"#eeee11":{"lch":[91.4002420948697,99.9206079899190485,85.8743202181747449],"luv":[91.4002420948697,7.18873686735402107,99.6616775060856526],"rgb":[0.933333333333333348,0.933333333333333348,0.0666666666666666657],"xyz":[0.659334717484665322,0.793673378327282464,0.123766467726971419],"hpluv":[85.8743202181747449,529.940172906192515,91.4002420948697],"hsluv":[85.8743202181747449,99.1672499526241182,91.4002420948697]},"#eeee22":{"lch":[91.4340680155716,98.4094794247264559,85.8743202181747],"luv":[91.4340680155716,7.08001949817026599,98.1544648222952105],"rgb":[0.933333333333333348,0.933333333333333348,0.133333333333333331],"xyz":[0.661210075623142268,0.794423521582673264,0.133643353922950597],"hpluv":[85.8743202181747,524.128135432551403,91.4340680155716],"hsluv":[85.8743202181747,97.631382664670781,91.4340680155716]},"#eeee33":{"lch":[91.4897155548310224,95.9410009888260475,85.874320218174617],"luv":[91.4897155548310224,6.90242608380469225,95.6923831080380864],"rgb":[0.933333333333333348,0.933333333333333348,0.2],"xyz":[0.664297826355600085,0.795658621875656391,0.149905507780561748],"hpluv":[85.874320218174617,514.550466890897383,91.4897155548310224],"hsluv":[85.874320218174617,95.1245282264191,91.4897155548310224]},"#eeee44":{"lch":[91.569956182858661,92.4193611961338917,85.8743202181745],"luv":[91.569956182858661,6.64906351605686385,92.1798691594911],"rgb":[0.933333333333333348,0.933333333333333348,0.266666666666666663],"xyz":[0.668755821599819,0.797441819973344,0.173384282733448386],"hpluv":[85.8743202181745,500.701064757674544,91.569956182858661],"hsluv":[85.8743202181745,91.5525623856835438,91.569956182858661]},"#eeee55":{"lch":[91.6770884747666912,87.7855710128071536,85.8743202181743612],"luv":[91.6770884747666912,6.31568785915728625,87.5580868047623682],"rgb":[0.933333333333333348,0.933333333333333348,0.333333333333333315],"xyz":[0.674718235639180874,0.799826785589088707,0.204786330007422301],"hpluv":[85.8743202181743612,482.129233036801622,91.6770884747666912],"hsluv":[85.8743202181743612,86.8606098102410584,91.6770884747666912]},"#eeee66":{"lch":[91.8130678710263197,82.0131153940214261,85.8743202181741481],"luv":[91.8130678710263197,5.90039150181238092,81.8005897091115912],"rgb":[0.933333333333333348,0.933333333333333348,0.4],"xyz":[0.682303231412689115,0.802860783898492,0.244733974414566263],"hpluv":[85.8743202181741481,458.402522986492727,91.8130678710263197],"hsluv":[85.8743202181741481,81.0287908474737435,91.8130678710263197]},"#eeee77":{"lch":[91.9795762822891163,75.1051560234190703,85.8743202181738923],"luv":[91.9795762822891163,5.40340190972927914,74.9105313631138],"rgb":[0.933333333333333348,0.933333333333333348,0.466666666666666674],"xyz":[0.691617244432891,0.806586389106572876,0.2937877763209642],"hpluv":[85.8743202181738923,429.072804464814112,91.9795762822891163],"hsluv":[85.8743202181738923,74.0694118243717838,91.9795762822891163]},"#eeee88":{"lch":[92.1780636375363542,67.0918310376028444,85.8743202181734233],"luv":[92.1780636375363542,4.82688735568056,66.9179718045445497],"rgb":[0.933333333333333348,0.933333333333333348,0.533333333333333326],"xyz":[0.702757661534834877,0.811042555947350419,0.35246063972453634],"hpluv":[85.8743202181734233,393.637417052406249,92.1780636375363542],"hsluv":[85.8743202181734233,66.0241185279663085,92.1780636375363542]},"#eeee99":{"lch":[92.4097746137617264,58.0271343519240759,85.8743202181728549],"luv":[92.4097746137617264,4.17473240419888647,57.8767650309654],"rgb":[0.933333333333333348,0.933333333333333348,0.6],"xyz":[0.715814625643907521,0.816265341590979632,0.421227317365654219],"hpluv":[85.8743202181728549,351.489405737917593,92.4097746137617264],"hsluv":[85.8743202181728549,56.9604957584853295,92.4097746137617264]},"#eeeeaa":{"lch":[92.6757669457712,47.9852530272659195,85.8743202181718885],"luv":[92.6757669457712,3.45227440531043239,47.8609058577161903],"rgb":[0.933333333333333348,0.933333333333333348,0.66666666666666663],"xyz":[0.730872318609160132,0.822288418777080699,0.500531166982652875],"hpluv":[85.8743202181718885,301.850049763156903,92.6757669457712],"hsluv":[85.8743202181718885,46.9680087594427178,92.6757669457712]},"#eeeebb":{"lch":[92.9769247593952741,37.0564456712956627,85.8743202181703253],"luv":[92.9769247593952741,2.66600696822725,36.9604189997225347],"rgb":[0.933333333333333348,0.933333333333333348,0.733333333333333282],"xyz":[0.748009910716496695,0.829143455620015457,0.590789152081294566],"hpluv":[85.8743202181703253,243.673832720465015,92.9769247593952741],"hsluv":[85.8743202181703253,36.1533987495139,92.9769247593952741]},"#eeeecc":{"lch":[93.3139689281547788,25.3426537884513579,85.8743202181670711],"luv":[93.3139689281547788,1.82326422217474082,25.2769817940640493],"rgb":[0.933333333333333348,0.933333333333333348,0.8],"xyz":[0.76730228616021845,0.836860405797504314,0.692395662751564722],"hpluv":[85.8743202181670711,175.509740066336235,93.3139689281547788],"hsluv":[85.8743202181670711,24.635760697738192,93.3139689281547788]},"#eeeedd":{"lch":[93.6874656794852143,12.9530841337210045,85.8743202181571377],"luv":[93.6874656794852143,0.931902991100789779,12.919518001474831],"rgb":[0.933333333333333348,0.933333333333333348,0.866666666666666696],"xyz":[0.788820611710852,0.84546773601775782,0.80572551065157072],"hpluv":[85.8743202181571377,95.2907564198152812,93.6874656794852143],"hsluv":[85.8743202181571377,12.5415797244653469,93.6874656794852143]},"#eeeeee":{"lch":[94.0978342288501466,4.90671076366048496e-12,0],"luv":[94.0978342288501466,4.6515081442594671e-12,1.56182025281704723e-12],"rgb":[0.933333333333333348,0.933333333333333348,0.933333333333333348],"xyz":[0.812632791977026758,0.854992608124227838,0.931136326720093943],"hpluv":[0,3.87295813278393312e-11,94.0978342288501466],"hsluv":[0,3.8714510065860851e-11,94.0978342288501466]},"#eeeeff":{"lch":[94.5453539438838391,13.4050739145486393,265.874320218197],"luv":[94.5453539438838391,-0.964421163933715353,-13.3703365130825738],"rgb":[0.933333333333333348,0.933333333333333348,1],"xyz":[0.838803840386858179,0.865461027488160561,1.06897051501187579],"hpluv":[265.874320218197,114.885858328026472,94.5453539438838391],"hsluv":[265.874320218197,99.999999999981938,94.5453539438838391]},"#777700":{"lch":[48.4055282063088868,53.3622847060179737,85.8743202181747],"luv":[48.4055282063088868,3.83912219020021839,53.2240036999738706],"rgb":[0.466666666666666674,0.466666666666666674,0],"xyz":[0.142041159467901856,0.171157317521294372,0.0255545090233414707],"hpluv":[85.8743202181747,139.887458074797621,48.4055282063088868],"hsluv":[85.8743202181747,100.000000000002331,48.4055282063088868]},"#777711":{"lch":[48.4562461221814829,51.3697569370731628,85.874320218174617],"luv":[48.4562461221814829,3.69577080233359201,51.2366392921178502],"rgb":[0.466666666666666674,0.466666666666666674,0.0666666666666666657],"xyz":[0.143052824967539,0.171561983721149225,0.0308826139880971],"hpluv":[85.874320218174617,134.523163178361983,48.4562461221814829],"hsluv":[85.874320218174617,96.1652781669932324,48.4562461221814829]},"#777722":{"lch":[48.5500530694338579,47.7524066126053768,85.8743202181744323],"luv":[48.5500530694338579,3.43552238949120969,47.6286628324468282],"rgb":[0.466666666666666674,0.466666666666666674,0.133333333333333331],"xyz":[0.144928183106016,0.172312126976540053,0.0407595001840763],"hpluv":[85.8743202181744323,124.808706278838557,48.5500530694338579],"hsluv":[85.8743202181744323,89.2207979160694293,48.5500530694338579]},"#777733":{"lch":[48.7039135074563063,42.0024110114446501,85.874320218174],"luv":[48.7039135074563063,3.02184190658892726,41.8935675523809934],"rgb":[0.466666666666666674,0.466666666666666674,0.2],"xyz":[0.148015933838473723,0.173547227269523152,0.0570216540416874432],"hpluv":[85.874320218174,109.433348296247431,48.7039135074563063],"hsluv":[85.874320218174,78.2295638238963704,48.7039135074563063]},"#777744":{"lch":[48.9247697387985312,34.1210359912247227,85.8743202181732528],"luv":[48.9247697387985312,2.45482042510454956,34.0326160292585],"rgb":[0.466666666666666674,0.466666666666666674,0.266666666666666663],"xyz":[0.152473929082692611,0.175330425367210746,0.0805004289945740747],"hpluv":[85.8743202181732528,88.497855114462908,48.9247697387985312],"hsluv":[85.8743202181732528,63.2636094274766378,48.9247697387985312]},"#777755":{"lch":[49.2178287935421537,24.2815247917763841,85.8743202181716327],"luv":[49.2178287935421537,1.74692184102758041,24.2186025669317253],"rgb":[0.466666666666666674,0.466666666666666674,0.333333333333333315],"xyz":[0.158436343122054624,0.177715390982955579,0.11190247626854799],"hpluv":[85.8743202181716327,62.6026663147617,49.2178287935421537],"hsluv":[85.8743202181716327,44.7521651878825324,49.2178287935421537]},"#777766":{"lch":[49.5868745078829676,12.7836483150604128,85.8743202181664742],"luv":[49.5868745078829676,0.919713018071489197,12.750521252385596],"rgb":[0.466666666666666674,0.466666666666666674,0.4],"xyz":[0.166021338895562781,0.18074938929235887,0.151850120675691952],"hpluv":[85.8743202181664742,32.7135296885410085,49.5868745078829676],"hsluv":[85.8743202181664742,23.385605928338574,49.5868745078829676]},"#777777":{"lch":[50.0344387925380687,2.67192546523751356e-12,0],"luv":[50.0344387925380687,2.52749706171116152e-12,8.66570421158112546e-13],"rgb":[0.466666666666666674,0.466666666666666674,0.466666666666666674],"xyz":[0.175335351915764714,0.184474994500439693,0.200903922582089861],"hpluv":[0,6.77633132918180515e-12,50.0344387925380687],"hsluv":[0,1.94020402484744743e-12,50.0344387925380687]},"#777788":{"lch":[50.5619220099523545,13.6772258441596737,265.874320218187563],"luv":[50.5619220099523545,-0.984000994856362388,-13.6417831984777926],"rgb":[0.466666666666666674,0.466666666666666674,0.533333333333333326],"xyz":[0.186475769017708515,0.188931161341217291,0.259576785985662029],"hpluv":[265.874320218187563,34.3252548472133,50.5619220099523545],"hsluv":[265.874320218187563,11.1797639211738336,50.5619220099523545]},"#777799":{"lch":[51.1696982290560669,27.8798810968622455,265.87432021818239],"luv":[51.1696982290560669,-2.00580373888746255,-27.8076342276045096],"rgb":[0.466666666666666674,0.466666666666666674,0.6],"xyz":[0.199532733126781242,0.194153946984846448,0.328343463626779908],"hpluv":[265.87432021818239,69.1380916549642563,51.1696982290560669],"hsluv":[265.87432021818239,22.9846011782403536,51.1696982290560669]},"#7777aa":{"lch":[51.8572203345112,42.2927406525888614,265.874320218180628],"luv":[51.8572203345112,-3.04272952363285398,-42.1831448442758443],"rgb":[0.466666666666666674,0.466666666666666674,0.66666666666666663],"xyz":[0.21459042609203377,0.200177024170947543,0.407647313243778564],"hpluv":[265.874320218180628,103.489412577951782,51.8572203345112],"hsluv":[265.874320218180628,35.2207148819814435,51.8572203345112]},"#7777bb":{"lch":[52.6231302285762439,56.6671214785474,265.874320218179832],"luv":[52.6231302285762439,-4.07688697591046,-56.5202764435038816],"rgb":[0.466666666666666674,0.466666666666666674,0.733333333333333282],"xyz":[0.231728018199370445,0.207032061013882301,0.497905298342420255],"hpluv":[265.874320218179832,136.645017565491173,52.6231302285762439],"hsluv":[265.874320218179832,47.751077300634,52.6231302285762439]},"#7777cc":{"lch":[53.4653742998309696,70.8221320427834513,265.874320218179378],"luv":[53.4653742998309696,-5.09526194727884452,-70.6386062488075],"rgb":[0.466666666666666674,0.466666666666666674,0.8],"xyz":[0.251020393643092143,0.214749011191371103,0.599511809012690411],"hpluv":[265.874320218179378,168.087613695837888,53.4653742998309696],"hsluv":[265.874320218179378,60.4968982098403671,53.4653742998309696]},"#7777dd":{"lch":[54.3813217092404102,84.6378263199215581,265.874320218179037],"luv":[54.3813217092404102,-6.08922498249284594,-84.4184990584042083],"rgb":[0.466666666666666674,0.466666666666666674,0.866666666666666696],"xyz":[0.272538719193725743,0.223356341411624637,0.712841656912696409],"hpluv":[265.874320218179037,197.494074150637061,54.3813217092404102],"hsluv":[265.874320218179037,73.4330970128385445,54.3813217092404102]},"#7777ee":{"lch":[55.3678819060931,98.0443353361264656,265.874320218178809],"luv":[55.3678819060931,-7.05374939408338086,-97.7902669542745713],"rgb":[0.466666666666666674,0.466666666666666674,0.933333333333333348],"xyz":[0.296350899459900452,0.232881213518094682,0.838252472981219632],"hpluv":[265.874320218178809,224.700440227144242,55.3678819060931],"hsluv":[265.874320218178809,86.5809092876847,55.3678819060931]},"#7777ff":{"lch":[56.4216176153771158,111.010032393554411,265.874320218178696],"luv":[56.4216176153771158,-7.98655981550332,-110.722365194803288],"rgb":[0.466666666666666674,0.466666666666666674,1],"xyz":[0.322521947869731873,0.243349632882027378,0.976086661273001477],"hpluv":[265.874320218178696,249.664056525023511,56.4216176153771158],"hsluv":[265.874320218178696,99.99999999999892,56.4216176153771158]},"#eeff00":{"lch":[95.8710857598618702,106.837421111995667,91.929871543819857],"luv":[95.8710857598618702,-3.59788306357601462,106.776822332015158],"rgb":[0.933333333333333348,1,0],"xyz":[0.710175424414702,0.896973456986776663,0.135722486905439893],"hpluv":[91.929871543819857,1221.941869030823,95.8710857598618702],"hsluv":[91.929871543819857,100.000000000002217,95.8710857598618702]},"#eeff11":{"lch":[95.8879066370154192,106.077593073364397,91.9648080695471748],"luv":[95.8879066370154192,-3.63693915095535747,106.015227330089402],"rgb":[0.933333333333333348,1,0.0666666666666666657],"xyz":[0.71118708991433921,0.897378123186631571,0.14105059187019553],"hpluv":[91.9648080695471748,1218.36968740939687,95.8879066370154192],"hsluv":[91.9648080695471748,99.9999999999846096,95.8879066370154192]},"#eeff22":{"lch":[95.9190746883988083,104.674935415410417,92.0306447189025221],"luv":[95.9190746883988083,-3.7090534526558554,104.609201443781629],"rgb":[0.933333333333333348,1,0.133333333333333331],"xyz":[0.713062448052816156,0.898128266442022372,0.150927478066174708],"hpluv":[92.0306447189025221,1211.72771901411761,95.9190746883988083],"hsluv":[92.0306447189025221,99.9999999999843254,95.9190746883988083]},"#eeff33":{"lch":[95.9703546560603,102.38192729680722,92.1421884898051076],"luv":[95.9703546560603,-3.826988551916231,102.31037677397255],"rgb":[0.933333333333333348,1,0.2],"xyz":[0.716150198785274,0.899363366735005498,0.167189631923785859],"hpluv":[92.1421884898051076,1200.73225066318059,95.9703546560603],"hsluv":[92.1421884898051076,99.9999999999843,95.9703546560603]},"#eeff44":{"lch":[96.0443082677326316,99.1069805797193482,92.3105231291127666],"luv":[96.0443082677326316,-3.99552836231266495,99.0264073504377],"rgb":[0.933333333333333348,1,0.266666666666666663],"xyz":[0.720608194029492832,0.901146564832693064,0.190668406876672497],"hpluv":[92.3105231291127666,1184.71704479359755,96.0443082677326316],"hsluv":[92.3105231291127666,99.999999999984,96.0443082677326316]},"#eeff55":{"lch":[96.1430663154878,94.7914212488158086,92.5502587525383831],"luv":[96.1430663154878,-4.21781218752730513,94.6975374691506744],"rgb":[0.933333333333333348,1,0.333333333333333315],"xyz":[0.726570608068854762,0.903531530448437814,0.222070454150646412],"hpluv":[92.5502587525383831,1163.01160371768833,96.1430663154878],"hsluv":[92.5502587525383831,99.9999999999832312,96.1430663154878]},"#eeff66":{"lch":[96.2684490440968261,89.4056432425818315,92.8821944044194083],"luv":[96.2684490440968261,-4.49554545672610839,89.2925479234763912],"rgb":[0.933333333333333348,1,0.4],"xyz":[0.734155603842363,0.906565528757841133,0.262018098557790347],"hpluv":[92.8821944044194083,1134.86447310352946,96.2684490440968261],"hsluv":[92.8821944044194083,99.9999999999826912,96.2684490440968261]},"#eeff77":{"lch":[96.4220309754045104,82.947030730659,93.3376121292550494],"luv":[96.4220309754045104,-4.82912482473711879,82.8063370791148827],"rgb":[0.933333333333333348,1,0.466666666666666674],"xyz":[0.743469616862564853,0.910291133965922,0.311071900464188311],"hpluv":[93.3376121292550494,1099.34916045901696,96.4220309754045104],"hsluv":[93.3376121292550494,99.9999999999824354,96.4220309754045104]},"#eeff88":{"lch":[96.6051797330544559,75.4384320618763553,93.966065888554354],"luv":[96.6051797330544559,-5.21774760700183382,75.2577713054536446],"rgb":[0.933333333333333348,1,0.533333333333333326],"xyz":[0.754610033964508764,0.914747300806699526,0.369744763867760451],"hpluv":[93.966065888554354,1055.22684316018899,96.6051797330544559],"hsluv":[93.966065888554354,99.9999999999810711,96.6051797330544559]},"#eeff99":{"lch":[96.8190810543072,66.9270103141668926,94.8508818247366],"luv":[96.8190810543072,-5.65953211211599072,66.6872881879637589],"rgb":[0.933333333333333348,1,0.6],"xyz":[0.767666998073581408,0.919970086450328739,0.43851144150887833],"hpluv":[94.8508818247366,1000.72847038880911,96.8190810543072],"hsluv":[94.8508818247366,99.9999999999804,96.8190810543072]},"#eeffaa":{"lch":[97.0647558759300182,57.4841926777339438,96.1432585072641785],"luv":[97.0647558759300182,-6.15165762861061438,57.1540857352402938],"rgb":[0.933333333333333348,1,0.66666666666666663],"xyz":[0.782724691038834,0.925993163636429806,0.517815291125877],"hpluv":[96.1432585072641785,933.191048721381,97.0647558759300182],"hsluv":[96.1432585072641785,99.9999999999786127,97.0647558759300182]},"#eeffbb":{"lch":[97.3430726615411857,47.2094061136043166,98.1473956779809242],"luv":[97.3430726615411857,-6.69052372550339935,46.7329104366258861],"rgb":[0.933333333333333348,1,0.733333333333333282],"xyz":[0.799862283146170583,0.932848200479364564,0.608073276224518677],"hpluv":[98.1473956779809242,848.432918810296883,97.3430726615411857],"hsluv":[98.1473956779809242,99.9999999999760689,97.3430726615411857]},"#eeffcc":{"lch":[97.6547568121604,36.2474903433398126,101.573139749476653],"luv":[97.6547568121604,-7.27192346215819718,35.5105573787715443],"rgb":[0.933333333333333348,1,0.8],"xyz":[0.819154658589892337,0.940565150656853421,0.709679786894788833],"hpluv":[101.573139749476653,739.717147432346792,97.6547568121604],"hsluv":[101.573139749476653,99.999999999972971,97.6547568121604]},"#eeffdd":{"lch":[98.0003982932414743,24.8675243497069864,108.50155795297276],"luv":[98.0003982932414743,-7.89122250686935445,23.5822470225029335],"rgb":[0.933333333333333348,1,0.866666666666666696],"xyz":[0.840672984140525936,0.949172480877106928,0.823009634794794831],"hpluv":[108.50155795297276,596.728870831386416,98.0003982932414743],"hsluv":[108.50155795297276,99.9999999999688072,98.0003982932414743]},"#eeffee":{"lch":[98.3804582036479,13.96608756186059,127.715012949221688],"luv":[98.3804582036479,-8.54353535492541205,11.0480588984987129],"rgb":[0.933333333333333348,1,0.933333333333333348],"xyz":[0.864485164406700646,0.958697352983577,0.948420450863318],"hpluv":[127.715012949221688,414.943064796341446,98.3804582036479],"hsluv":[127.715012949221688,99.9999999999608491,98.3804582036479]},"#eeffff":{"lch":[98.7952747621608438,9.43620053547767768,192.177050630058204],"luv":[98.7952747621608438,-9.22389036737693679,-1.99040876112424892],"rgb":[0.933333333333333348,1,1],"xyz":[0.890656212816532067,0.969165772347509669,1.0862546391551],"hpluv":[192.177050630058204,378.040319927501173,98.7952747621608438],"hsluv":[192.177050630058204,99.999999999948713,98.7952747621608438]},"#778800":{"lch":[53.5249548615687303,60.5938372915660253,96.5029793655947259],"luv":[53.5249548615687303,-6.86254771619202231,60.2039745910497572],"rgb":[0.466666666666666674,0.533333333333333326,0],"xyz":[0.164113529192872309,0.215302056971235917,0.0329119655983314136],"hpluv":[96.5029793655947259,143.651932639250902,53.5249548615687303],"hsluv":[96.5029793655947259,100.000000000002373,53.5249548615687303]},"#778811":{"lch":[53.5684856288310272,58.8481082257442409,96.7583563096261088],"luv":[53.5684856288310272,-6.9253766303469888,58.4391906196240143],"rgb":[0.466666666666666674,0.533333333333333326,0.0666666666666666657],"xyz":[0.165125194692509442,0.21570672317109077,0.0382400705630870433],"hpluv":[96.7583563096261088,139.399900460742,53.5684856288310272],"hsluv":[96.7583563096261088,96.9357421545617,53.5684856288310272]},"#778822":{"lch":[53.6490362820541407,55.6690512282284615,97.2652817371477],"luv":[53.6490362820541407,-7.04010577997538,55.2220986133077147],"rgb":[0.466666666666666674,0.533333333333333326,0.133333333333333331],"xyz":[0.167000552830986443,0.216456866426481598,0.0481169567590662428],"hpluv":[97.2652817371477,131.671329649176641,53.6490362820541407],"hsluv":[97.2652817371477,91.3605066517003337,53.6490362820541407]},"#778833":{"lch":[53.7812573057213,50.5917713326567053,98.2096275165039856],"luv":[53.7812573057213,-7.22426441450666346,50.0733195448945381],"rgb":[0.466666666666666674,0.533333333333333326,0.2],"xyz":[0.170088303563444176,0.217691966719464697,0.064379110616677393],"hpluv":[98.2096275165039856,119.368094402160139,53.7812573057213],"hsluv":[98.2096275165039856,82.4672262716137539,53.7812573057213]},"#778844":{"lch":[53.9712743947247162,43.5928841845235198,99.8805739619221811],"luv":[53.9712743947247162,-7.48032491712259429,42.9462954241638357],"rgb":[0.466666666666666674,0.533333333333333326,0.266666666666666663],"xyz":[0.174546298807663064,0.219475164817152291,0.0878578855695640315],"hpluv":[99.8805739619221811,102.492540276722124,53.9712743947247162],"hsluv":[99.8805739619221811,70.2195807792145672,53.9712743947247162]},"#778855":{"lch":[54.2238135722873,34.8221097745323078,102.954066597198249],"luv":[54.2238135722873,-7.80606678579386326,33.9358903004666175],"rgb":[0.466666666666666674,0.533333333333333326,0.333333333333333315],"xyz":[0.180508712847025077,0.221860130432897124,0.119259932843537933],"hpluv":[102.954066597198249,81.4900057495692636,54.2238135722873],"hsluv":[102.954066597198249,54.8454134878709638,54.2238135722873]},"#778866":{"lch":[54.542475349788262,24.6519214642532667,109.417534351990682],"luv":[54.542475349788262,-8.19552569001744224,23.2497438812554442],"rgb":[0.466666666666666674,0.533333333333333326,0.4],"xyz":[0.188093708620533234,0.224894128742300414,0.159207577250681909],"hpluv":[109.417534351990682,57.3528835536874197,54.542475349788262],"hsluv":[109.417534351990682,36.7830408334215164,54.542475349788262]},"#778877":{"lch":[54.9298804020046703,14.1239929232838151,127.715012949231166],"luv":[54.9298804020046703,-8.6401314869560828,11.1729720300858784],"rgb":[0.466666666666666674,0.533333333333333326,0.466666666666666674],"xyz":[0.197407721640735168,0.228619733950381238,0.208261379157079818],"hpluv":[127.715012949231166,32.6278280107521041,54.9298804020046703],"hsluv":[127.715012949231166,16.6133799052879709,54.9298804020046703]},"#778888":{"lch":[55.3877640712208574,9.3400800844087577,192.177050630059369],"luv":[55.3877640712208574,-9.12993258220805437,-1.97013375878514596],"rgb":[0.466666666666666674,0.533333333333333326,0.533333333333333326],"xyz":[0.208548138742678968,0.233075900791158835,0.266934242560651958],"hpluv":[192.177050630059369,21.3981433643613,55.3877640712208574],"hsluv":[192.177050630059369,21.3143847536295468,55.3877640712208574]},"#778899":{"lch":[55.9170512374386703,18.5603965019494623,238.655736169794807],"luv":[55.9170512374386703,-9.65472976501325597,-15.851640655596027],"rgb":[0.466666666666666674,0.533333333333333326,0.6],"xyz":[0.221605102851751695,0.238298686434788,0.335700920201769892],"hpluv":[238.655736169794807,42.1194135101242679,55.9170512374386703],"hsluv":[238.655736169794807,26.1912729818073053,55.9170512374386703]},"#7788aa":{"lch":[56.5179258756863732,31.8473117719276075,251.310804784322386],"luv":[56.5179258756863732,-10.2049729927717898,-30.1680260095876527],"rgb":[0.466666666666666674,0.533333333333333326,0.66666666666666663],"xyz":[0.236662795817004223,0.244321763620889087,0.415004769818768493],"hpluv":[251.310804784322386,71.5032678590983721,56.5179258756863732],"hsluv":[251.310804784322386,31.1149405392867244,56.5179258756863732]},"#7788bb":{"lch":[57.1899017055338845,45.9393215269311881,256.438388794294667],"luv":[57.1899017055338845,-10.7723499078102041,-44.6584565319768387],"rgb":[0.466666666666666674,0.533333333333333326,0.733333333333333282],"xyz":[0.253800387924340898,0.251176800463823846,0.505262754917410239],"hpluv":[256.438388794294667,101.930593576715296,57.1899017055338845],"hsluv":[256.438388794294667,41.4357647235180622,57.1899017055338845]},"#7788cc":{"lch":[57.9318961804226547,60.1932243037694406,259.131204763586595],"luv":[57.9318961804226547,-11.3500713849249486,-59.1134513595764233],"rgb":[0.466666666666666674,0.533333333333333326,0.8],"xyz":[0.273092763368062652,0.258893750641312648,0.606869265587680395],"hpluv":[259.131204763586595,131.8466774878151,57.9318961804226547],"hsluv":[259.131204763586595,55.5026255722761661,57.9318961804226547]},"#7788dd":{"lch":[58.7423077351926679,74.3397271130694861,260.762999043897821],"luv":[58.7423077351926679,-11.9329115646152975,-73.3757497327060264],"rgb":[0.466666666666666674,0.533333333333333326,0.866666666666666696],"xyz":[0.29461108891869614,0.267501080861566209,0.720199113487686393],"hpluv":[260.762999043897821,160.586593274196787,58.7423077351926679],"hsluv":[260.762999043897821,69.9489143241080598,58.7423077351926679]},"#7788ee":{"lch":[59.6190949291064101,88.2283098571320465,261.84384456781811],"luv":[59.6190949291064101,-12.5170812067924775,-87.3358880318318285],"rgb":[0.466666666666666674,0.533333333333333326,0.933333333333333348],"xyz":[0.318423269184870905,0.277025952968036226,0.845609929556209616],"hpluv":[261.84384456781811,187.785436290663455,59.6190949291064101],"hsluv":[261.84384456781811,84.7689484415743237,59.6190949291064101]},"#7788ff":{"lch":[60.55985551741054,101.77153783075579,262.604380260147],"luv":[60.55985551741054,-13.1000102630052879,-100.924901008354297],"rgb":[0.466666666666666674,0.533333333333333326,1],"xyz":[0.344594317594702271,0.28749437233196895,0.983444117847991461],"hpluv":[262.604380260147,213.245970352856943,60.55985551741054],"hsluv":[262.604380260147,99.9999999999987921,60.55985551741054]},"#779900":{"lch":[58.6994568897504223,69.1976359459353603,103.993850276975294],"luv":[58.6994568897504223,-16.7332165977800322,67.1439668384135899],"rgb":[0.466666666666666674,0.6,0],"xyz":[0.189983129645339782,0.267041257876171612,0.0415351657491536685],"hpluv":[103.993850276975294,149.587912305351722,58.6994568897504223],"hsluv":[103.993850276975294,100.000000000002444,58.6994568897504223]},"#779911":{"lch":[58.7371702745588493,67.6759907780605232,104.326162498332039],"luv":[58.7371702745588493,-16.7458460058928829,65.5714600214076],"rgb":[0.466666666666666674,0.6,0.0666666666666666657],"xyz":[0.190994795144976914,0.267445924076026464,0.0468632707139093],"hpluv":[104.326162498332039,146.20456366036808,58.7371702745588493],"hsluv":[104.326162498332039,97.5197891999702,58.7371702745588493]},"#779922":{"lch":[58.8069803361006223,64.8997387816913,104.97431376012679],"luv":[58.8069803361006223,-16.7691829588827659,62.6958578920751464],"rgb":[0.466666666666666674,0.6,0.133333333333333331],"xyz":[0.192870153283453916,0.268196067331417265,0.0567401569098885],"hpluv":[104.97431376012679,140.04041716275114,58.8069803361006223],"hsluv":[104.97431376012679,92.9911396041894704,58.8069803361006223]},"#779933":{"lch":[58.9216385591388132,60.4528100001228097,106.14237332279157],"luv":[58.9216385591388132,-16.8074000849803582,58.0693855598141369],"rgb":[0.466666666666666674,0.6,0.2],"xyz":[0.195957904015911677,0.269431167624400392,0.0730023107674996341],"hpluv":[106.14237332279157,130.191012221085145,58.9216385591388132],"hsluv":[106.14237332279157,85.724115661044948,58.9216385591388132]},"#779944":{"lch":[59.0865618982540752,54.3006768161790845,108.091337704968765],"luv":[59.0865618982540752,-16.8621369683366211,51.6161974534756354],"rgb":[0.466666666666666674,0.6,0.266666666666666663],"xyz":[0.200415899260130537,0.271214365722087958,0.0964810857203862726],"hpluv":[108.091337704968765,116.615384645059635,59.0865618982540752],"hsluv":[108.091337704968765,75.6282332091309399,59.0865618982540752]},"#779955":{"lch":[59.3060149965037056,46.5647823410072945,111.326222899357617],"luv":[59.3060149965037056,-16.9345685221747893,43.3762532203180413],"rgb":[0.466666666666666674,0.6,0.333333333333333315],"xyz":[0.206378313299492522,0.273599331337832818,0.127883132994360188],"hpluv":[111.326222899357617,99.6318422422000367,59.3060149965037056],"hsluv":[111.326222899357617,62.8087826319283806,59.3060149965037056]},"#779966":{"lch":[59.5833535636433282,37.5662597340720836,116.949978591108717],"luv":[59.5833535636433282,-17.0254963867212936,33.4866591823296957],"rgb":[0.466666666666666674,0.6,0.4],"xyz":[0.213963309073000707,0.276633329647236137,0.16783077740150415],"hpluv":[116.949978591108717,80.0041177209304522,59.5833535636433282],"hsluv":[116.949978591108717,47.5357009492008,59.5833535636433282]},"#779977":{"lch":[59.921152065724,28.0111989307655129,127.71501294923614],"luv":[59.921152065724,-17.1354122862910181,22.1586306282167591],"rgb":[0.466666666666666674,0.6,0.466666666666666674],"xyz":[0.223277322093202613,0.280358934855316932,0.216884579307902059],"hpluv":[127.71501294923614,59.3185968765002301,59.921152065724],"hsluv":[127.71501294923614,30.2037385091367128,59.921152065724]},"#779988":{"lch":[60.3212826107906608,19.7788738743410271,150.794869664161695],"luv":[60.3212826107906608,-17.2645515875173317,9.65086059473676272],"rgb":[0.466666666666666674,0.6,0.533333333333333326],"xyz":[0.234417739195146468,0.28481510169609453,0.275557442711474199],"hpluv":[150.794869664161695,41.6073752981458043,60.3212826107906608],"hsluv":[150.794869664161695,33.5278156565034706,60.3212826107906608]},"#779999":{"lch":[60.7849725890424537,17.8137422376051369,192.177050630060307],"luv":[60.7849725890424537,-17.4129412378011317,-3.75751113860251662],"rgb":[0.466666666666666674,0.6,0.6],"xyz":[0.247474703304219168,0.290037887339723688,0.344324120352592133],"hpluv":[192.177050630060307,37.1876097830212373,60.7849725890424537],"hsluv":[192.177050630060307,37.0420465685486562,60.7849725890424537]},"#7799aa":{"lch":[61.3128540464897895,25.0117677757311512,225.340924984985747],"luv":[61.3128540464897895,-17.5804418295499474,-17.7909131959243076],"rgb":[0.466666666666666674,0.6,0.66666666666666663],"xyz":[0.262532396269471724,0.29606096452582481,0.423627969969590734],"hpluv":[225.340924984985747,51.7645188682129742,61.3128540464897895],"hsluv":[225.340924984985747,40.6599304384258815,61.3128540464897895]},"#7799bb":{"lch":[61.9050111975332129,36.7727368637825478,241.108601392342877],"luv":[61.9050111975332129,-17.766782778670386,-32.1958942436537],"rgb":[0.466666666666666674,0.6,0.733333333333333282],"xyz":[0.279669988376808343,0.302916001368759569,0.513885955068232425],"hpluv":[241.108601392342877,75.37710856774234,61.9050111975332129],"hsluv":[241.108601392342877,44.3026829048042927,61.9050111975332129]},"#7799cc":{"lch":[62.5610290710097274,50.0883036089361084,248.973595844380469],"luv":[62.5610290710097274,-17.971590279241223,-46.7531827927901],"rgb":[0.466666666666666674,0.6,0.8],"xyz":[0.298962363820530097,0.31063295154624837,0.615492465738502581],"hpluv":[248.973595844380469,101.594866952252644,62.5610290710097274],"hsluv":[248.973595844380469,49.5852758814030068,62.5610290710097274]},"#7799dd":{"lch":[63.2800443412030518,63.9273589508424962,253.464421950176984],"luv":[63.2800443412030518,-18.1944086519195096,-61.2835272829234228],"rgb":[0.466666666666666674,0.6,0.866666666666666696],"xyz":[0.320480689371163641,0.319240281766501877,0.728822313638508579],"hpluv":[253.464421950176984,128.191525099113591,63.2800443412030518],"hsluv":[253.464421950176984,65.7895471985927429,63.2800443412030518]},"#7799ee":{"lch":[64.0607982423263138,77.8622331281598292,256.304567564122067],"luv":[64.0607982423263138,-18.434716379612933,-75.6484539148497106],"rgb":[0.466666666666666674,0.6,0.933333333333333348],"xyz":[0.344292869637338406,0.328765153872971894,0.854233129707031802],"hpluv":[256.304567564122067,154.231767587931557,64.0607982423263138],"hsluv":[256.304567564122067,82.5871459557978085,64.0607982423263138]},"#7799ff":{"lch":[64.9016907971027,91.67329289213788,258.23504341307023],"luv":[64.9016907971027,-18.6919383586334327,-89.7474460365571929],"rgb":[0.466666666666666674,0.6,1],"xyz":[0.370463918047169771,0.339233573236904618,0.992067317998813758],"hpluv":[258.23504341307023,179.236372955016122,64.9016907971027],"hsluv":[258.23504341307023,99.9999999999984368,64.9016907971027]},"#660000":{"lch":[19.330201679573328,65.0080772249371819,12.1770506300617765],"luv":[19.330201679573328,63.5454254137925432,13.7123671721378795],"rgb":[0.4,0,0],"xyz":[0.0547936733227042463,0.0282529878070199789,0.00256845343700170745],"hpluv":[12.1770506300617765,426.746789183125202,19.330201679573328],"hsluv":[12.1770506300617765,100.000000000002217,19.330201679573328]},"#660011":{"lch":[19.4980803058243595,61.695772445130423,8.9911342856641614],"luv":[19.4980803058243595,60.9376877881905799,9.641916024853316],"rgb":[0.4,0,0.0666666666666666657],"xyz":[0.0558053388223413716,0.0286576540068748317,0.0078965584017573389],"hpluv":[8.9911342856641614,401.51602003210553,19.4980803058243595],"hsluv":[8.9911342856641614,99.9999999999966178,19.4980803058243595]},"#660022":{"lch":[19.8051492014688648,56.728751528179842,2.87530221933591967],"luv":[19.8051492014688648,56.6573341637173584,2.84565201787440492],"rgb":[0.4,0,0.133333333333333331],"xyz":[0.0576806969608183867,0.029407797262265653,0.0177734445977365332],"hpluv":[2.87530221933591967,363.466537566247382,19.8051492014688648],"hsluv":[2.87530221933591967,99.9999999999968594,19.8051492014688648]},"#660033":{"lch":[20.2995520444984123,51.2727836305606957,352.516911450402631],"luv":[20.2995520444984123,50.836110988726972,-6.67743669143524787],"rgb":[0.4,0,0.2],"xyz":[0.0607684476932761272,0.0306428975552487659,0.0340355984553476765],"hpluv":[352.516911450402631,320.508659944055125,20.2995520444984123],"hsluv":[352.516911450402631,99.9999999999973852,20.2995520444984123]},"#660044":{"lch":[20.9904438433464762,47.7800185043531087,338.095292373375855],"luv":[20.9904438433464762,44.3305689635839855,-17.8250055719836844],"rgb":[0.4,0,0.266666666666666663],"xyz":[0.0652264429374950078,0.0324260956529363389,0.057514373408234315],"hpluv":[338.095292373375855,288.844444062118953,20.9904438433464762],"hsluv":[338.095292373375855,99.999999999997911,20.9904438433464762]},"#660055":{"lch":[21.8759682447435324,48.0792454528985687,322.009867044845],"luv":[21.8759682447435324,37.8920594608334085,-29.5940141436867741],"rgb":[0.4,0,0.333333333333333315],"xyz":[0.071188856976857,0.0348110612686811718,0.0889164206822082304],"hpluv":[322.009867044845,278.887908627948889,21.8759682447435324],"hsluv":[322.009867044845,99.9999999999984,21.8759682447435324]},"#660066":{"lch":[22.9458380566939866,52.2668983658326383,307.715012949243601],"luv":[22.9458380566939866,31.9734565677830815,-41.3464235441515271],"rgb":[0.4,0,0.4],"xyz":[0.0787738527503651781,0.0378450595780844834,0.128864065089352192],"hpluv":[307.715012949243601,289.042783730483393,22.9458380566939866],"hsluv":[307.715012949243601,99.9999999999988,22.9458380566939866]},"#660077":{"lch":[24.1840444716539054,59.1802438936044553,296.875135467660698],"luv":[24.1840444716539054,26.7522904844618061,-52.7884099129864097],"rgb":[0.4,0,0.466666666666666674],"xyz":[0.0880878657705671,0.0415706647861653,0.177917866995750101],"hpluv":[296.875135467660698,310.518260327731298,24.1840444716539054],"hsluv":[296.875135467660698,99.9999999999992326,24.1840444716539054]},"#660088":{"lch":[25.5714349826340381,67.6035320092512819,289.201479741547303],"luv":[25.5714349826340381,22.2341957091879898,-63.8426039670327725],"rgb":[0.4,0,0.533333333333333326],"xyz":[0.0992282828725109256,0.0460268316269428907,0.236590730399322269],"hpluv":[289.201479741547303,335.469941782198191,25.5714349826340381],"hsluv":[289.201479741547303,99.9999999999995168,25.5714349826340381]},"#660099":{"lch":[27.0878540213863559,76.7583277742970296,283.827270614430063],"luv":[27.0878540213863559,18.3449067131383,-74.5339203342523291],"rgb":[0.4,0,0.6],"xyz":[0.112285246981583625,0.0512496172705720551,0.305357408040440148],"hpluv":[283.827270614430063,359.575614235331898,27.0878540213863559],"hsluv":[283.827270614430063,99.9999999999996732,27.0878540213863559]},"#6600aa":{"lch":[28.7136916664512327,86.2331190336617226,280.0081392435834],"luv":[28.7136916664512327,14.9862877024472088,-84.9209161465722246],"rgb":[0.4,0,0.66666666666666663],"xyz":[0.127342939946836181,0.0572726944566731566,0.384661257657438749],"hpluv":[280.0081392435834,381.087223541781498,28.7136916664512327],"hsluv":[280.0081392435834,99.9999999999998863,28.7136916664512327]},"#6600bb":{"lch":[30.4308478844127208,95.8259209373334784,277.232006261077231],"luv":[30.4308478844127208,12.063278317584528,-95.06358103774852],"rgb":[0.4,0,0.733333333333333282],"xyz":[0.144480532054172828,0.0641277312996079152,0.474919242756080495],"hpluv":[277.232006261077231,399.584170303460496,30.4308478844127208],"hsluv":[277.232006261077231,100.000000000000071,30.4308478844127208]},"#6600cc":{"lch":[32.2232190058254631,105.440117399912424,275.16595430901134],"luv":[32.2232190058254631,9.49391256045434,-105.011827817640437],"rgb":[0.4,0,0.8],"xyz":[0.163772907497894554,0.0718446814770967168,0.576525753426350707],"hpluv":[275.16595430901134,415.218107165999243,32.2232190058254631],"hsluv":[275.16595430901134,100.000000000000284,32.2232190058254631]},"#6600dd":{"lch":[34.0768449366564425,115.029794578821409,273.594219506454294],"luv":[34.0768449366564425,7.21119822634443164,-114.803537667557165],"rgb":[0.4,0,0.866666666666666696],"xyz":[0.185291233048528098,0.0804520116973502508,0.689855601326356704],"hpluv":[273.594219506454294,428.341637585051217,34.0768449366564425],"hsluv":[273.594219506454294,100.000000000000313,34.0768449366564425]},"#6600ee":{"lch":[35.9798440153965657,124.573148355199,272.374748889870204],"luv":[35.9798440153965657,5.16172842086039729,-124.466163477612255],"rgb":[0.4,0,0.933333333333333348],"xyz":[0.209103413314702835,0.0899768838038202817,0.815266417394879928],"hpluv":[272.374748889870204,439.343788723350144,35.9798440153965657],"hsluv":[272.374748889870204,100.000000000000313,35.9798440153965657]},"#6600ff":{"lch":[37.9222328155672699,134.059876636217865,271.411957283269032],"luv":[37.9222328155672699,3.30334385288953181,-134.01917192367489],"rgb":[0.4,0,1],"xyz":[0.235274461724534228,0.100445303167752992,0.953100605686661773],"hpluv":[271.411957283269032,448.58447779597617,37.9222328155672699],"hsluv":[271.411957283269032,100.00000000000054,37.9222328155672699]},"#661100":{"lch":[20.9278595225824304,60.6482483509225645,15.3961031612090817],"luv":[20.9278595225824304,58.4717927536604165,16.1015365791022269],"rgb":[0.4,0.0666666666666666657,0],"xyz":[0.0567980735836326536,0.0322617883288768559,0.00323658685731115833],"hpluv":[15.3961031612090817,367.733145903292666,20.9278595225824304],"hsluv":[15.3961031612090817,100.00000000000216,20.9278595225824304]},"#661111":{"lch":[21.0816163302651134,57.5811303886473453,12.1770506300618102],"luv":[21.0816163302651134,56.2855814623301782,12.1457768908291825],"rgb":[0.4,0.0666666666666666657,0.0666666666666666657],"xyz":[0.0578097390832697788,0.0326664545287317087,0.00856469182206678892],"hpluv":[12.1770506300618102,346.589664487627374,21.0816163302651134],"hsluv":[12.1770506300618102,81.2167011616125762,21.0816163302651134]},"#661122":{"lch":[21.363314856368774,52.932385078801687,5.93410154371749865],"luv":[21.363314856368774,52.6487447994206548,5.4723908098767966],"rgb":[0.4,0.0666666666666666657,0.133333333333333331],"xyz":[0.059685097221746794,0.0334165977841225301,0.018441578018045985],"hpluv":[5.93410154371749865,314.406957937895,21.363314856368774],"hsluv":[5.93410154371749865,82.5199513610387356,21.363314856368774]},"#661133":{"lch":[21.8180817632255923,47.7742586248590229,355.183549580278111],"luv":[21.8180817632255923,47.6055578330604661,-4.01131531521171425],"rgb":[0.4,0.0666666666666666657,0.2],"xyz":[0.0627728479542045414,0.0346516980771056429,0.0347037318756571317],"hpluv":[355.183549580278111,277.854041301729922,21.8180817632255923],"hsluv":[355.183549580278111,84.3121175303292603,21.8180817632255923]},"#661144":{"lch":[22.4559756968324393,44.5402485804784689,339.935360677682411],"luv":[22.4559756968324393,41.8369301419790247,-15.2808710453946635],"rgb":[0.4,0.0666666666666666657,0.266666666666666663],"xyz":[0.0672308431984234151,0.036434896174793216,0.0581825068285437702],"hpluv":[339.935360677682411,251.686564911624259,22.4559756968324393],"hsluv":[339.935360677682411,86.3348863624116518,22.4559756968324393]},"#661155":{"lch":[23.2773926977910151,45.1903944783221903,322.792583995755933],"luv":[23.2773926977910151,35.9919645110905364,-27.3267313035930464],"rgb":[0.4,0.0666666666666666657,0.333333333333333315],"xyz":[0.073193257237785414,0.0388198617905380489,0.0895845541025176717],"hpluv":[322.792583995755933,246.349182588766354,23.2773926977910151],"hsluv":[322.792583995755933,88.344826800164725,23.2773926977910151]},"#661166":{"lch":[24.275087751098269,49.8658315110702119,307.715012949243715],"luv":[24.275087751098269,30.5046415204515107,-39.4470277460696508],"rgb":[0.4,0.0666666666666666657,0.4],"xyz":[0.0807782530112935854,0.0418538600999413604,0.129532198509661634],"hpluv":[307.715012949243715,260.664316843383688,24.275087751098269],"hsluv":[307.715012949243715,90.1819147598703239,24.275087751098269]},"#661177":{"lch":[25.4363091503391701,57.3032374709331265,296.512546946797],"luv":[25.4363091503391701,25.5798087859014238,-51.2770358652572114],"rgb":[0.4,0.0666666666666666657,0.466666666666666674],"xyz":[0.0900922660314955,0.0455794653080221768,0.178586000416059543],"hpluv":[296.512546946797,285.867241464858182,25.4363091503391701],"hsluv":[296.512546946797,91.7740463972148461,25.4363091503391701]},"#661188":{"lch":[26.7449145171680129,66.1973281861227179,288.734792076243366],"luv":[26.7449145171680129,21.2617948521868456,-62.6898902427238127],"rgb":[0.4,0.0666666666666666657,0.533333333333333326],"xyz":[0.101232683133439333,0.0500356321487997746,0.23725886381963171],"hpluv":[288.734792076243366,314.078764213635395,26.7449145171680129],"hsluv":[288.734792076243366,93.110372692245349,26.7449145171680129]},"#661199":{"lch":[28.1832309630650286,75.7381406209258614,283.36677781159608],"luv":[28.1832309630650286,17.5094321789343716,-73.6864012521064637],"rgb":[0.4,0.0666666666666666657,0.6],"xyz":[0.114289647242512032,0.0552584177924289321,0.306025541460749617],"hpluv":[283.36677781159608,341.006914961476241,28.1832309630650286],"hsluv":[283.36677781159608,94.2123428080992,28.1832309630650286]},"#6611aa":{"lch":[29.733500038717203,85.5146667147819102,279.591039159426828],"luv":[29.733500038717203,14.2479867443400359,-84.3193518539685],"rgb":[0.4,0.0666666666666666657,0.66666666666666663],"xyz":[0.129347340207764588,0.0612814949785300336,0.385329391077748218],"hpluv":[279.591039159426828,364.950446526832081,29.733500038717203],"hsluv":[279.591039159426828,95.1136535912152254,29.733500038717203]},"#6611bb":{"lch":[31.378866229854367,95.3367425151459287,276.866082014695],"luv":[31.378866229854367,11.3974241300945351,-94.6530147253534153],"rgb":[0.4,0.0666666666666666657,0.733333333333333282],"xyz":[0.146484932315101235,0.0681365318214647853,0.475587376176389964],"hpluv":[276.866082014695,385.533736621086632,31.378866229854367],"hsluv":[276.866082014695,95.8493220980777636,31.378866229854367]},"#6611cc":{"lch":[33.1039576367877899,105.121866511028642,274.848446148865946],"luv":[33.1039576367877899,8.88494143701037409,-104.745714157780327],"rgb":[0.4,0.0666666666666666657,0.8],"xyz":[0.165777307758822962,0.0758534819989535869,0.577193886846660176],"hpluv":[274.848446148865946,402.951217622234481,33.1039576367877899],"hsluv":[274.848446148865946,96.4508528344463372,33.1039576367877899]},"#6611dd":{"lch":[34.8951509835915559,114.837148656435488,273.319244536407894],"luv":[34.8951509835915559,6.64899595047804048,-114.644500803094743],"rgb":[0.4,0.0666666666666666657,0.866666666666666696],"xyz":[0.187295633309456505,0.0844608122192071209,0.690523734746666173],"hpluv":[273.319244536407894,417.596303228590671,34.8951509835915559],"hsluv":[273.319244536407894,96.944722371929771,34.8951509835915559]},"#6611ee":{"lch":[36.7406193088150914,124.471664845310258,272.136065946829319],"luv":[36.7406193088150914,4.63940101804097527,-124.385173182164465],"rgb":[0.4,0.0666666666666666657,0.933333333333333348],"xyz":[0.211107813575631242,0.0939856843256771657,0.815934550815189397],"hpluv":[272.136065946829319,429.895948632666091,36.7406193088150914],"hsluv":[272.136065946829319,97.3524117238680446,36.7406193088150914]},"#6611ff":{"lch":[38.6302462525687815,134.023748920536207,271.203906716280642],"luv":[38.6302462525687815,2.81591803113255,-133.994163605572282],"rgb":[0.4,0.0666666666666666657,1],"xyz":[0.237278861985462636,0.104454103689609862,0.953768739106971242],"hpluv":[271.203906716280642,440.244168299440901,38.6302462525687815],"hsluv":[271.203906716280642,99.999999999999531,38.6302462525687815]},"#662200":{"lch":[23.5697003211059126,54.0433218319152,21.7646438431993481],"luv":[23.5697003211059126,50.1908335966774928,20.038983444740694],"rgb":[0.4,0.133333333333333331,0],"xyz":[0.0605136973184005889,0.0396930357984128235,0.00447512810223376842],"hpluv":[21.7646438431993481,290.955989204018863,23.5697003211059126],"hsluv":[21.7646438431993481,100.000000000002302,23.5697003211059126]},"#662211":{"lch":[23.7037155268300239,51.2256671637292627,18.5481266113504795],"luv":[23.7037155268300239,48.5648418325832196,16.2949413667354186],"rgb":[0.4,0.133333333333333331,0.0666666666666666657],"xyz":[0.0615253628180377071,0.0400977019982676763,0.0098032330669894],"hpluv":[18.5481266113504795,274.227196288753476,23.7037155268300239],"hsluv":[18.5481266113504795,84.4421944630679775,23.7037155268300239]},"#662222":{"lch":[23.9497782760704823,46.8639841586094761,12.177050630061828],"luv":[23.9497782760704823,45.8095660888384515,9.88517404858116],"rgb":[0.4,0.133333333333333331,0.133333333333333331],"xyz":[0.0634007209565147362,0.0408478452536585,0.0196801192629685942],"hpluv":[12.177050630061828,248.300181449835577,23.9497782760704823],"hsluv":[12.177050630061828,58.184428739378582,23.9497782760704823]},"#662233":{"lch":[24.3484354577474491,41.8866591548313139,0.79920805816821483],"luv":[24.3484354577474491,41.8825842907357284,0.584250188216085209],"rgb":[0.4,0.133333333333333331,0.2],"xyz":[0.0664884716889724697,0.0420829455466416105,0.0359422731205797374],"hpluv":[0.79920805816821483,218.295100847162672,24.3484354577474491],"hsluv":[0.79920805816821483,61.8388407102714908,24.3484354577474491]},"#662244":{"lch":[24.9104705449366364,38.7518995195090881,343.921953822216381],"luv":[24.9104705449366364,37.2361321960480467,-10.7322027305005232],"rgb":[0.4,0.133333333333333331,0.266666666666666663],"xyz":[0.0709464669331913433,0.0438661436443291836,0.059421048073466376],"hpluv":[343.921953822216381,197.40147375767063,24.9104705449366364],"hsluv":[343.921953822216381,66.1143919991375,24.9104705449366364]},"#662255":{"lch":[25.6388481254070371,39.7515606661792376,324.488651014584605],"luv":[25.6388481254070371,32.3577893991285421,-23.0902585650003651],"rgb":[0.4,0.133333333333333331,0.333333333333333315],"xyz":[0.0769088809725533423,0.0462511092600740165,0.0908230953474402913],"hpluv":[324.488651014584605,196.74105716675524,25.6388481254070371],"hsluv":[324.488651014584605,70.5303777194930746,25.6388481254070371]},"#662266":{"lch":[26.530115175026431,45.1550068711170525,307.715012949244],"luv":[26.530115175026431,27.6228683191850166,-35.7204674010796381],"rgb":[0.4,0.133333333333333331,0.4],"xyz":[0.0844938767460615137,0.049285107569477328,0.130770739754584253],"hpluv":[307.715012949244,215.976303505553034,26.530115175026431],"hsluv":[307.715012949244,74.7212231760600076,26.530115175026431]},"#662277":{"lch":[27.5758503181253047,53.47184404785515,295.772994892671647],"luv":[27.5758503181253047,23.2499163509258366,-48.1526686233805137],"rgb":[0.4,0.133333333333333331,0.466666666666666674],"xyz":[0.0938078897662634331,0.0530107127775581444,0.179824541660982162],"hpluv":[295.772994892671647,246.056915236342576,27.5758503181253047],"hsluv":[295.772994892671647,78.4792949436714622,27.5758503181253047]},"#662288":{"lch":[28.7641691288539221,63.1969785740596492,287.807032602413699],"luv":[28.7641691288539221,19.3264050913403267,-60.169329123196718],"rgb":[0.4,0.133333333333333331,0.533333333333333326],"xyz":[0.104948306868207261,0.0574668796183357422,0.23849740506455433],"hpluv":[287.807032602413699,278.794247273594806,28.7641691288539221],"hsluv":[287.807032602413699,81.7283139306751565,28.7641691288539221]},"#662299":{"lch":[30.081149723697223,73.446586603294,282.467625610713],"luv":[30.081149723697223,15.8562317990126083,-71.7145800853016766],"rgb":[0.4,0.133333333333333331,0.6],"xyz":[0.118005270977279975,0.0626896652619649,0.307264082705672237],"hpluv":[282.467625610713,309.825038390781458,30.081149723697223],"hsluv":[282.467625610713,84.475317587044259,30.081149723697223]},"#6622aa":{"lch":[31.512047889249807,83.7990879252892569,278.786617054458873],"luv":[31.512047889249807,12.8007301136733549,-82.8156292354725565],"rgb":[0.4,0.133333333333333331,0.66666666666666663],"xyz":[0.133062963942532531,0.068712742448066,0.386567932322670837],"hpluv":[278.786617054458873,337.444217562648532,31.512047889249807],"hsluv":[278.786617054458873,86.7692530837111349,31.512047889249807]},"#6622bb":{"lch":[33.0422299311305281,94.07636648958254,276.16643073789237],"luv":[33.0422299311305281,10.1053888262289746,-93.5320471739658643],"rgb":[0.4,0.133333333333333331,0.733333333333333282],"xyz":[0.150200556049869149,0.0755677792910007529,0.476825917421312584],"hpluv":[276.16643073789237,361.285484259324733,33.0422299311305281],"hsluv":[276.16643073789237,88.6739591516067662,33.0422299311305281]},"#6622cc":{"lch":[34.6578144612334853,104.214655833315277,274.245119058655746],"luv":[34.6578144612334853,7.71433729467952212,-103.928742369761551],"rgb":[0.4,0.133333333333333331,0.8],"xyz":[0.169492931493590904,0.0832847294684895545,0.578432428091582684],"hpluv":[274.245119058655746,381.563613242959605,34.6578144612334853],"hsluv":[274.245119058655746,90.2534916489383079,34.6578144612334853]},"#6622dd":{"lch":[36.346058304563158,114.200305800004017,272.799119030217753],"luv":[36.346058304563158,5.5769048208672336,-114.064052082299],"rgb":[0.4,0.133333333333333331,0.866666666666666696],"xyz":[0.191011257044224447,0.0918920596887430885,0.691762275991588682],"hpluv":[272.799119030217753,398.702789727268566,36.346058304563158],"hsluv":[272.799119030217753,91.5654894217155118,36.346058304563158]},"#6622ee":{"lch":[38.0955422395423,124.039689220569301,271.686141036519132],"luv":[38.0955422395423,3.64980151132168062,-123.985980864222597],"rgb":[0.4,0.133333333333333331,0.933333333333333348],"xyz":[0.214823437310399185,0.101416931795213133,0.817173092060111905],"hpluv":[271.686141036519132,413.167199445381073,38.0955422395423],"hsluv":[271.686141036519132,92.6590152569973498,38.0955422395423]},"#6622ff":{"lch":[39.8962147757429264,133.745689778340022,270.812765394378914],"luv":[39.8962147757429264,1.89717678294479941,-133.732233416399],"rgb":[0.4,0.133333333333333331,1],"xyz":[0.240994485720230578,0.111885351159145829,0.95500728035189375],"hpluv":[270.812765394378914,425.390148773484384,39.8962147757429264],"hsluv":[270.812765394378914,99.99999999999946,39.8962147757429264]},"#ddaa00":{"lch":[72.3107430320736881,86.5721801417344494,59.9465914104400071],"luv":[72.3107430320736881,43.355958426121056,74.9333253195491267],"rgb":[0.866666666666666696,0.66666666666666663,0],"xyz":[0.441922241377765923,0.441231641500849037,0.0618909005679976823],"hpluv":[59.9465914104400071,151.919966798177825,72.3107430320736881],"hsluv":[59.9465914104400071,100.000000000002402,72.3107430320736881]},"#ddaa11":{"lch":[72.3377322217577188,85.4954146103564625,59.6744594052448178],"luv":[72.3377322217577188,43.1676990261245663,73.7971251485224],"rgb":[0.866666666666666696,0.66666666666666663,0.0666666666666666657],"xyz":[0.442933906877403,0.44163630770070389,0.067219005532753312],"hpluv":[59.6744594052448178,149.974443711406025,72.3377322217577188],"hsluv":[59.6744594052448178,98.5180859943944398,72.3377322217577188]},"#ddaa22":{"lch":[72.3877194091470386,83.5208789068112623,59.1557200492863586],"luv":[72.3877194091470386,42.8217010650181038,71.708013019916109],"rgb":[0.866666666666666696,0.66666666666666663,0.133333333333333331],"xyz":[0.444809265015880084,0.44238645095609469,0.0770958917287325],"hpluv":[59.1557200492863586,146.409577733002152,72.3877194091470386],"hsluv":[59.1557200492863586,95.7957425728033911,72.3877194091470386]},"#ddaa33":{"lch":[72.4698996386445771,80.3304907566891,58.2589820164620633],"luv":[72.4698996386445771,42.2603136650720543,68.3158373595774435],"rgb":[0.866666666666666696,0.66666666666666663,0.2],"xyz":[0.44789701574833779,0.443621551249077817,0.0933580455863436548],"hpluv":[58.2589820164620633,140.657238526484292,72.4698996386445771],"hsluv":[58.2589820164620633,91.3821490755410082,72.4698996386445771]},"#ddaa44":{"lch":[72.5882801580772679,75.8571050863154,56.8623293870095949],"luv":[72.5882801580772679,41.467485474955744,63.5196665644635701],"rgb":[0.866666666666666696,0.66666666666666663,0.266666666666666663],"xyz":[0.452355010992556705,0.445404749346765383,0.116836820539230293],"hpluv":[56.8623293870095949,132.607804442486071,72.5882801580772679],"hsluv":[56.8623293870095949,85.1564308385751332,72.5882801580772679]},"#ddaa55":{"lch":[72.7461171317656579,70.1198989219024469,54.780917696258733],"luv":[72.7461171317656579,40.4384565855633085,57.2846528644045065],"rgb":[0.866666666666666696,0.66666666666666663,0.333333333333333315],"xyz":[0.45831742503191869,0.447789714962510244,0.148238867813204195],"hpluv":[54.780917696258733,122.3124851735341,72.7461171317656579],"hsluv":[54.780917696258733,77.0893717991816345,72.7461171317656579]},"#ddaa66":{"lch":[72.9460991959627307,63.2340905833455835,51.7145858463931702],"luv":[72.9460991959627307,39.17852917707404,49.6345954292357661],"rgb":[0.866666666666666696,0.66666666666666663,0.4],"xyz":[0.465902420805426876,0.450823713271913562,0.188186512220348157],"hpluv":[51.7145858463931702,109.998947735143361,72.9460991959627307],"hsluv":[51.7145858463931702,67.2337497701281421,72.9460991959627307]},"#ddaa77":{"lch":[73.1904440644495651,55.4382228560327377,47.150998877343909],"luv":[73.1904440644495651,37.7017926183457632,40.6444508733775294],"rgb":[0.866666666666666696,0.66666666666666663,0.466666666666666674],"xyz":[0.475216433825628781,0.454549318479994358,0.237240314126746066],"hpluv":[47.150998877343909,96.1156803308001173,73.1904440644495651],"hsluv":[47.150998877343909,55.7140089565642214,73.1904440644495651]},"#ddaa88":{"lch":[73.4809558021361511,47.1613293807763228,40.1850915920125189],"luv":[73.4809558021361511,36.0295555463961819,30.4312687229965526],"rgb":[0.866666666666666696,0.66666666666666663,0.533333333333333326],"xyz":[0.486356850927572582,0.459005485320771955,0.295913177530318261],"hpluv":[40.1850915920125189,81.4424009312931645,73.4809558021361511],"hsluv":[40.1850915920125189,45.325944585658668,73.4809558021361511]},"#ddaa99":{"lch":[73.8190624606049539,39.1833139250455389,29.2463480703396321],"luv":[73.8190624606049539,34.1885051927702435,19.1436204212419021],"rgb":[0.866666666666666696,0.66666666666666663,0.6],"xyz":[0.499413815036645281,0.464228270964401113,0.36467985517143614],"hpluv":[29.2463480703396321,67.3553294964948,73.8190624606049539],"hsluv":[29.2463480703396321,46.1073405736803323,73.8190624606049539]},"#ddaaaa":{"lch":[74.2058435914849923,32.9500913661112946,12.1770506300625296],"luv":[74.2058435914849923,32.2087294789219527,6.95027095793437244],"rgb":[0.866666666666666696,0.66666666666666663,0.66666666666666663],"xyz":[0.514471508001897893,0.470251348150502235,0.443983704788434741],"hpluv":[12.1770506300625296,56.3453191386000114,74.2058435914849923],"hsluv":[12.1770506300625296,46.9171896008106,74.2058435914849923]},"#ddaabb":{"lch":[74.6420527030620633,30.7078714289026884,348.787813428378513],"luv":[74.6420527030620633,30.1217754775259223,-5.97093039446416807],"rgb":[0.866666666666666696,0.66666666666666663,0.733333333333333282],"xyz":[0.531609100109234456,0.477106384993437,0.534241689887076543],"hpluv":[348.787813428378513,52.2042025244198,74.6420527030620633],"hsluv":[348.787813428378513,47.7320050944543937,74.6420527030620633]},"#ddaacc":{"lch":[75.1281375044867445,34.0548346014018435,325.184688201169763],"luv":[75.1281375044867445,27.9589055182432595,-19.4430286208394669],"rgb":[0.866666666666666696,0.66666666666666663,0.8],"xyz":[0.55090147555295621,0.484823335170925795,0.635848200557346699],"hpluv":[325.184688201169763,57.5195499622066393,75.1281375044867445],"hsluv":[325.184688201169763,48.5278754329488109,75.1281375044867445]},"#ddaadd":{"lch":[75.6642595524878772,42.092894356631966,307.715012949247466],"luv":[75.6642595524878772,25.749668941594777,-33.2981426616688125],"rgb":[0.866666666666666696,0.66666666666666663,0.866666666666666696],"xyz":[0.572419801103589809,0.493430665391179302,0.749178048457352697],"hpluv":[307.715012949247466,70.5922995729826255,75.6642595524878772],"hsluv":[307.715012949247466,49.2811497501269855,75.6642595524878772]},"#ddaaee":{"lch":[76.2503141773092636,52.9001531738191915,296.399494755977059],"luv":[76.2503141773092636,23.5208512504545659,-47.3834967290038875],"rgb":[0.866666666666666696,0.66666666666666663,0.933333333333333348],"xyz":[0.596231981369764519,0.502955537497649319,0.87458886452587592],"hpluv":[296.399494755977059,89.1958388421213,76.2503141773092636],"hsluv":[296.399494755977059,73.8038640729286,76.2503141773092636]},"#ddaaff":{"lch":[76.8859510948180542,65.1444004641461447,289.080822119258073],"luv":[76.8859510948180542,21.295808093697417,-61.565261872824486],"rgb":[0.866666666666666696,0.66666666666666663,1],"xyz":[0.62240302977959594,0.513423956861582,1.01242305281765765],"hpluv":[289.080822119258073,113.514774922986319,76.8859510948180542],"hsluv":[289.080822119258073,99.9999999999968736,76.8859510948180542]},"#663300":{"lch":[27.2772702365161024,46.6784293424923,33.1138040531735385],"luv":[27.2772702365161024,39.0972512948193938,25.5006020923387098],"rgb":[0.4,0.2,0],"xyz":[0.0666314194074114075,0.0519284799764346272,0.00651436879857064926],"hpluv":[33.1138040531735385,217.147410386557254,27.2772702365161024],"hsluv":[33.1138040531735385,100.000000000002288,27.2772702365161024]},"#663311":{"lch":[27.3893959478715772,43.971204952951652,30.1269617465812196],"luv":[27.3893959478715772,38.0313691656755353,22.0699303215615963],"rgb":[0.4,0.2,0.0666666666666666657],"xyz":[0.0676430849070485257,0.0523331461762894801,0.0118424737633262799],"hpluv":[30.1269617465812196,203.716046393577358,27.3893959478715772],"hsluv":[30.1269617465812196,87.8713966090680572,27.3893959478715772]},"#663322":{"lch":[27.5957277293980781,39.608651287835805,23.9834802389854467],"luv":[27.5957277293980781,36.1889469500517436,16.0998563809655693],"rgb":[0.4,0.2,0.133333333333333331],"xyz":[0.0695184430455255548,0.0530832894316803,0.0217193599593054759],"hpluv":[23.9834802389854467,182.132533879714401,27.5957277293980781],"hsluv":[23.9834802389854467,66.9779468056010501,27.5957277293980781]},"#663333":{"lch":[27.9312558147072,34.2527069144242517,12.1770506300619683],"luv":[27.9312558147072,33.482036777055967,7.2250359324586606],"rgb":[0.4,0.2,0.2],"xyz":[0.0726061937779832883,0.0543183897246634143,0.0379815138169166192],"hpluv":[12.1770506300619683,155.612242967619039,27.9312558147072],"hsluv":[12.1770506300619683,36.4647718300342518,27.9312558147072]},"#663344":{"lch":[28.4068233476218452,30.4023991370198274,352.642674566889241],"luv":[28.4068233476218452,30.1520905841737843,-3.89323858637544307],"rgb":[0.4,0.2,0.266666666666666663],"xyz":[0.0770641890222021619,0.0561015878223509873,0.0614602887698032577],"hpluv":[352.642674566889241,135.807736934047625,28.4068233476218452],"hsluv":[352.642674566889241,42.3197568133242044,28.4068233476218452]},"#663355":{"lch":[29.027378781744666,31.164203729876963,328.257954430910445],"luv":[29.027378781744666,26.5028264215118483,-16.3953586660521324],"rgb":[0.4,0.2,0.333333333333333315],"xyz":[0.0830266030615641609,0.0584865534380958202,0.0928623360437771661],"hpluv":[328.257954430910445,136.234637721643935,29.027378781744666],"hsluv":[328.257954430910445,48.648881209958347,29.027378781744666]},"#663366":{"lch":[29.7928910898429251,37.2833882928919067,307.715012949244567],"luv":[29.7928910898429251,22.8075289246907325,-29.4935190668720466],"rgb":[0.4,0.2,0.4],"xyz":[0.0906115988350723323,0.0615205517474991317,0.132809980450921128],"hpluv":[307.715012949244567,158.796911496206434,29.7928910898429251],"hsluv":[307.715012949244567,54.9388950129523792,29.7928910898429251]},"#663377":{"lch":[30.6992208566667273,46.7654342463890345,294.320996437271049],"luv":[30.6992208566667273,19.2602655771695837,-42.6151148086008646],"rgb":[0.4,0.2,0.466666666666666674],"xyz":[0.0999256118552742517,0.0652461569555799481,0.181863782357319037],"hpluv":[294.320996437271049,193.302265796468333,30.6992208566667273],"hsluv":[294.320996437271049,60.8304716126584424,30.6992208566667273]},"#663388":{"lch":[31.7390466224972485,57.6768981875091811,286.074834637191429],"luv":[31.7390466224972485,15.9703082974272554,-55.4217812544619122],"rgb":[0.4,0.2,0.533333333333333326],"xyz":[0.11106602895721808,0.0697023237963575459,0.240536645760891205],"hpluv":[286.074834637191429,230.593651019326217,31.7390466224972485],"hsluv":[286.074834637191429,66.1275975958513,31.7390466224972485]},"#663399":{"lch":[32.9028065942714818,68.9918273860838696,280.844497617061506],"luv":[32.9028065942714818,12.9804074562249703,-67.7597319087197292],"rgb":[0.4,0.2,0.6],"xyz":[0.124122993066290793,0.0749251094399867,0.309303323402009112],"hpluv":[280.844497617061506,266.074975063872387,32.9028065942714818],"hsluv":[280.844497617061506,70.7618769362906761,32.9028065942714818]},"#6633aa":{"lch":[34.1795810026756612,80.2563017922262,277.366876751775067],"luv":[34.1795810026756612,10.2906460265496023,-79.5938225097975],"rgb":[0.4,0.2,0.66666666666666663],"xyz":[0.139180686031543321,0.0809481866260878,0.388607173019007712],"hpluv":[277.366876751775067,297.955725741163178,34.1795810026756612],"hsluv":[277.366876751775067,74.7463951454814293,34.1795810026756612]},"#6633bb":{"lch":[35.5578512622049701,91.2933310256451875,274.950579703742051],"luv":[35.5578512622049701,7.87828999249439477,-90.9527615663878208],"rgb":[0.4,0.2,0.733333333333333282],"xyz":[0.15631827813888,0.0878032234690225566,0.478865158117649459],"hpluv":[274.950579703742051,325.793841875348,35.5578512622049701],"hsluv":[274.950579703742051,78.1373397599298158,35.5578512622049701]},"#6633cc":{"lch":[37.0261004663704369,102.052471518039624,273.208108112313198],"luv":[37.0261004663704369,5.71114175070883,-101.892540467119574],"rgb":[0.4,0.2,0.8],"xyz":[0.175610653582601722,0.0955201736465113582,0.58047166878791967],"hpluv":[273.208108112313198,349.747708937339723,37.0261004663704369],"hsluv":[273.208108112313198,81.008127051099,37.0261004663704369]},"#6633dd":{"lch":[38.5732487885037258,112.538160698021628,271.912259810227567],"luv":[38.5732487885037258,3.75528971541350476,-112.475488051606391],"rgb":[0.4,0.2,0.866666666666666696],"xyz":[0.197128979133235266,0.104127503866764892,0.693801516687925668],"hpluv":[271.912259810227567,370.214070757058153,38.5732487885037258],"hsluv":[271.912259810227567,83.8924044294761,38.5732487885037258]},"#6633ee":{"lch":[40.1889386764538372,122.775879994578517,270.923585069715159],"luv":[40.1889386764538372,1.97901241536538453,-122.759929204537158],"rgb":[0.4,0.2,0.933333333333333348],"xyz":[0.22094115939941,0.113652375973234937,0.819212332756448891],"hpluv":[270.923585069715159,387.655406037674595,40.1889386764538372],"hsluv":[270.923585069715159,91.8481370725274502,40.1889386764538372]},"#6633ff":{"lch":[41.8636962738951581,132.796565847050772,270.152898062524457],"luv":[41.8636962738951581,0.354377123439640451,-132.796093005873018],"rgb":[0.4,0.2,1],"xyz":[0.247112207809241397,0.124120795337167633,0.957046521048230736],"hpluv":[270.152898062524457,402.521052726566381,41.8636962738951581],"hsluv":[270.152898062524457,99.9999999999993747,41.8636962738951581]},"#ddbb00":{"lch":[76.6269242453545871,86.8164471599593,69.4373142874166263],"luv":[76.6269242453545871,30.4927107526369845,81.2852390562642],"rgb":[0.866666666666666696,0.733333333333333282,0],"xyz":[0.475876739286364758,0.509140637318047595,0.0732090665375303],"hpluv":[69.4373142874166263,149.25100710879434,76.6269242453545871],"hsluv":[69.4373142874166263,100.000000000002373,76.6269242453545871]},"#ddbb11":{"lch":[76.6514577805917412,85.7837129406502754,69.2755127455160391],"luv":[76.6514577805917412,30.3566774947925779,80.2328956062352461],"rgb":[0.866666666666666696,0.733333333333333282,0.0666666666666666657],"xyz":[0.476888404786001863,0.509545303517902504,0.0785371715022859379],"hpluv":[69.2755127455160391,147.663405788509436,76.6514577805917412],"hsluv":[69.2755127455160391,98.710789014839,76.6514577805917412]},"#ddbb22":{"lch":[76.6969020797125722,83.8847347199512257,68.9672438614109353],"luv":[76.6969020797125722,30.1063672837106679,78.2959473281657523],"rgb":[0.866666666666666696,0.733333333333333282,0.133333333333333331],"xyz":[0.478763762924478919,0.510295446773293304,0.0884140576982651305],"hpluv":[68.9672438614109353,144.735860207932831,76.6969020797125722],"hsluv":[68.9672438614109353,96.3396664069398554,76.6969020797125722]},"#ddbb33":{"lch":[76.7716285455552452,80.8014056767331823,68.4347482297151828],"luv":[76.7716285455552452,29.6994133556607949,75.1452726767666093],"rgb":[0.866666666666666696,0.733333333333333282,0.2],"xyz":[0.481851513656936625,0.511530547066276431,0.104676211555876281],"hpluv":[68.4347482297151828,139.9591753538609,76.7716285455552452],"hsluv":[68.4347482297151828,92.4878111801133542,76.7716285455552452]},"#ddbb44":{"lch":[76.8793043130786771,76.4437058057830683,67.6060651816737277],"luv":[76.8793043130786771,29.122950030038453,70.6788082728408398],"rgb":[0.866666666666666696,0.733333333333333282,0.266666666666666663],"xyz":[0.486309508901155541,0.513313745163964,0.128154986508762919],"hpluv":[67.6060651816737277,133.157612926581692,76.8793043130786771],"hsluv":[67.6060651816737277,87.0382611213693167,76.8793043130786771]},"#ddbb55":{"lch":[77.0229278214451654,70.786451757311653,66.3713649049339551],"luv":[77.0229278214451654,28.3717024864450664,64.851894732620238],"rgb":[0.866666666666666696,0.733333333333333282,0.333333333333333315],"xyz":[0.492271922940517526,0.515698710779708747,0.159557033782736835],"hpluv":[66.3713649049339551,124.235507886155276,77.0229278214451654],"hsluv":[66.3713649049339551,79.9485715087726,77.0229278214451654]},"#ddbb66":{"lch":[77.2049977781888259,63.8693414267369732,64.5489675558404912],"luv":[77.2049977781888259,27.4471820183984,57.6710063509733359],"rgb":[0.866666666666666696,0.733333333333333282,0.4],"xyz":[0.499856918714025711,0.518732709089112065,0.199504678189880769],"hpluv":[64.5489675558404912,113.177180957201557,77.2049977781888259],"hsluv":[64.5489675558404912,71.2432689272834665,77.2049977781888259]},"#ddbb77":{"lch":[77.4276024714994264,55.80459657383539,61.8158033937064815],"luv":[77.4276024714994264,26.3569386858829375,49.1880552764299495],"rgb":[0.866666666666666696,0.733333333333333282,0.466666666666666674],"xyz":[0.509170931734227561,0.522458314297192916,0.248558480096278678],"hpluv":[61.8158033937064815,100.062778032150931,77.4276024714994264],"hsluv":[61.8158033937064815,61.00659046349368,77.4276024714994264]},"#ddbb88":{"lch":[77.6924726660212883,46.8021917196541537,57.5480835848938881],"luv":[77.6924726660212883,25.1136643841879952,39.4936578574539112],"rgb":[0.866666666666666696,0.733333333333333282,0.533333333333333326],"xyz":[0.520311348836171472,0.526914481137970458,0.307231343499850873],"hpluv":[57.5480835848938881,85.1204314231653285,77.6924726660212883],"hsluv":[57.5480835848938881,49.3735690170151145,77.6924726660212883]},"#ddbb99":{"lch":[78.001015952892,37.2494741955694195,50.4191367157405352],"luv":[78.001015952892,23.7341209657771088,28.7091419207933036],"rgb":[0.866666666666666696,0.733333333333333282,0.6],"xyz":[0.533368312945244116,0.532137266781599672,0.375998021140968752],"hpluv":[50.4191367157405352,68.8881727109007471,78.001015952892],"hsluv":[50.4191367157405352,37.2278198354843894,78.001015952892]},"#ddbbaa":{"lch":[78.3543411988951135,27.9779961680489,37.3601297670291643],"luv":[78.3543411988951135,22.2379488027189254,16.9776883770144309],"rgb":[0.866666666666666696,0.733333333333333282,0.66666666666666663],"xyz":[0.548426005910496728,0.538160343967700738,0.455301870757967353],"hpluv":[37.3601297670291643,52.7537881236722086,78.3543411988951135],"hsluv":[37.3601297670291643,37.9002282962103152,78.3543411988951135]},"#ddbbbb":{"lch":[78.7532777240269724,21.1216737069600953,12.1770506300632171],"luv":[78.7532777240269724,20.6464457718990104,4.45526398447083238],"rgb":[0.866666666666666696,0.733333333333333282,0.733333333333333282],"xyz":[0.565563598017833291,0.545015380810635497,0.545559855856609155],"hpluv":[12.1770506300632171,40.7192307977840144,78.7532777240269724],"hsluv":[12.1770506300632171,38.5653299871361952,78.7532777240269724]},"#ddbbcc":{"lch":[79.1983918483363425,20.8793784390591419,335.381067074864461],"luv":[79.1983918483363425,18.9814116783666442,-8.69795692664743747],"rgb":[0.866666666666666696,0.733333333333333282,0.8],"xyz":[0.584855973461555,0.552732330988124354,0.647166366526879311],"hpluv":[335.381067074864461,41.2776027841095186,79.1983918483363425],"hsluv":[335.381067074864461,39.1976988204569,79.1983918483363425]},"#ddbbdd":{"lch":[79.6900023594157858,28.2216267397768341,307.715012949250536],"luv":[79.6900023594157858,17.264138203128045,-22.325092338948604],"rgb":[0.866666666666666696,0.733333333333333282,0.866666666666666696],"xyz":[0.606374299012188644,0.56133966120837786,0.760496214426885309],"hpluv":[307.715012949250536,57.3946451248898555,79.6900023594157858],"hsluv":[307.715012949250536,39.7709084435557401,79.6900023594157858]},"#ddbbee":{"lch":[80.2281958041266,39.4567857635703447,293.154060026294076],"luv":[80.2281958041266,15.5145982189510505,-36.2785774927409221],"rgb":[0.866666666666666696,0.733333333333333282,0.933333333333333348],"xyz":[0.630186479278363354,0.570864533314847877,0.885907030495408532],"hpluv":[293.154060026294076,82.8233888781043674,80.2281958041266],"hsluv":[293.154060026294076,68.7191774169969278,80.2281958041266]},"#ddbbff":{"lch":[80.8128420975971409,52.2658591287574765,285.253756774661895],"luv":[80.8128420975971409,13.7508586713070624,-50.4245368473411],"rgb":[0.866666666666666696,0.733333333333333282,1],"xyz":[0.656357527688194775,0.581332952678780601,1.02374121878719038],"hpluv":[285.253756774661895,113.64059963508393,80.8128420975971409],"hsluv":[285.253756774661895,99.9999999999962768,80.8128420975971409]},"#664400":{"lch":[31.7142168878436834,41.7146560735594463,49.9018869072924431],"luv":[31.7142168878436834,26.8683448374877969,31.9093180282686859],"rgb":[0.4,0.266666666666666663,0],"xyz":[0.0754639898903774337,0.0695936209423669294,0.00945855895955924342],"hpluv":[49.9018869072924431,166.906788900061372,31.7142168878436834],"hsluv":[49.9018869072924431,100.000000000002103,31.7142168878436834]},"#664411":{"lch":[31.8065195391856221,38.988494662362406,47.7128576067384387],"luv":[31.8065195391856221,26.2332728657793552,28.8429906699465946],"rgb":[0.4,0.266666666666666663,0.0666666666666666657],"xyz":[0.0764756553900145519,0.0699982871422217823,0.0147866639243148749],"hpluv":[47.7128576067384387,155.546285842055198,31.8065195391856221],"hsluv":[47.7128576067384387,90.7993319288460157,31.8065195391856221]},"#664422":{"lch":[31.9766874661881033,34.3475437583520318,43.0092135947734064],"luv":[31.9766874661881033,25.116436032279772,23.4290077311931029],"rgb":[0.4,0.266666666666666663,0.133333333333333331],"xyz":[0.078351013528491581,0.0707484303976126,0.0246635501202940727],"hpluv":[43.0092135947734064,136.301783870263904,31.9766874661881033],"hsluv":[43.0092135947734064,74.6688439558526227,31.9766874661881033]},"#664433":{"lch":[32.2542649002247757,27.9288783689085562,32.9719795273007179],"luv":[32.2542649002247757,23.4305644913898661,15.1997004759998031],"rgb":[0.4,0.266666666666666663,0.2],"xyz":[0.0814387642609493145,0.0719835306905957095,0.0409257039779052159],"hpluv":[32.9719795273007179,109.876716511985933,32.2542649002247757],"hsluv":[32.9719795273007179,50.4508902462759465,32.2542649002247757]},"#664444":{"lch":[32.6494757012261942,21.7704999617243,12.1770506300622419],"luv":[32.6494757012261942,21.2806737346177428,4.59212303669972144],"rgb":[0.4,0.266666666666666663,0.266666666666666663],"xyz":[0.0858967595051681881,0.0737667287882832895,0.0644044789307918475],"hpluv":[12.1770506300622419,84.6119136876739,32.6494757012261942],"hsluv":[12.1770506300622419,19.8271939783404392,32.6494757012261942]},"#664455":{"lch":[33.1682230288457163,20.3425996427482829,337.72581918360828],"luv":[33.1682230288457163,18.8246474429619,-7.71064257201243208],"rgb":[0.4,0.266666666666666663,0.333333333333333315],"xyz":[0.0918591735445301871,0.0761516944040281224,0.0958065262047657629],"hpluv":[337.72581918360828,77.8257963773028649,33.1682230288457163],"hsluv":[337.72581918360828,26.9902218950403885,33.1682230288457163]},"#664466":{"lch":[33.8127168447387447,26.5268160416637819,307.715012949245931],"luv":[33.8127168447387447,16.2273642995468066,-20.9843898457416387],"rgb":[0.4,0.266666666666666663,0.4],"xyz":[0.0994441693180383585,0.0791856927134314409,0.135754170611909725],"hpluv":[307.715012949245931,99.5507152142919125,33.8127168447387447],"hsluv":[307.715012949245931,34.4415155187259359,33.8127168447387447]},"#664477":{"lch":[34.5819879544663067,37.1994974310500766,291.489286484323088],"luv":[34.5819879544663067,13.6271894146704895,-34.6136146303646512],"rgb":[0.4,0.266666666666666663,0.466666666666666674],"xyz":[0.108758182338240278,0.0829112979215122503,0.184807972518307634],"hpluv":[291.489286484323088,136.49804898345451,34.5819879544663067],"hsluv":[291.489286484323088,41.7425383187140824,34.5819879544663067]},"#664488":{"lch":[35.4724176022540263,49.4256461635840623,283.003444619748336],"luv":[35.4724176022540263,11.1212465051830698,-48.1582015326441066],"rgb":[0.4,0.266666666666666663,0.533333333333333326],"xyz":[0.119898599440184106,0.0873674647622898481,0.243480835921879801],"hpluv":[283.003444619748336,176.807585609029246,35.4724176022540263],"hsluv":[283.003444619748336,48.5905204317402166,35.4724176022540263]},"#664499":{"lch":[36.4782980897457563,61.9753280698008169,278.131406705371774],"luv":[36.4782980897457563,8.76603164871553453,-61.3522450974139844],"rgb":[0.4,0.266666666666666663,0.6],"xyz":[0.132955563549256806,0.092590250405919,0.312247513562997736],"hpluv":[278.131406705371774,215.5875082467536,36.4782980897457563],"hsluv":[278.131406705371774,54.8155424382278511,36.4782980897457563]},"#6644aa":{"lch":[37.5923984663849922,74.3597011725147468,275.081334091822],"luv":[37.5923984663849922,6.58602388672208239,-74.0674655150911576],"rgb":[0.4,0.266666666666666663,0.66666666666666663],"xyz":[0.148013256514509361,0.0986133275920201,0.391551363179996281],"hpluv":[275.081334091822,251.001852587777108,37.5923984663849922],"hsluv":[275.081334091822,60.3520360208115179,37.5923984663849922]},"#6644bb":{"lch":[38.8064988843830392,86.3901107279874765,273.04172423936518],"luv":[38.8064988843830392,4.58413310153298603,-86.2684006766171905],"rgb":[0.4,0.266666666666666663,0.733333333333333282],"xyz":[0.165150848621846,0.105468364434954859,0.481809348278638083],"hpluv":[273.04172423936518,282.487278503057098,38.8064988843830392],"hsluv":[273.04172423936518,65.2044789549227346,38.8064988843830392]},"#6644cc":{"lch":[40.1118623747323184,98.0126069922284415,271.608181870646],"luv":[40.1118623747323184,2.75066337520804938,-97.9740015535209],"rgb":[0.4,0.266666666666666663,0.8],"xyz":[0.184443224065567735,0.11318531461244366,0.583415858948908239],"hpluv":[271.608181870646,310.061926380003911,40.1118623747323184],"hsluv":[271.608181870646,73.249037078124374,40.1118623747323184]},"#6644dd":{"lch":[41.4996246628331491,109.23540368219534,270.561113733160255],"luv":[41.4996246628331491,1.06975602599714947,-109.230165429047204],"rgb":[0.4,0.266666666666666663,0.866666666666666696],"xyz":[0.205961549616201278,0.121792644832697194,0.696745706848914237],"hpluv":[270.561113733160255,334.009312605211903,41.4996246628331491],"hsluv":[270.561113733160255,82.0982912580276434,41.4996246628331491]},"#6644ee":{"lch":[42.9610953823040305,120.093362966476874,269.772657810053431],"luv":[42.9610953823040305,-0.476513570908160711,-120.092417593346411],"rgb":[0.4,0.266666666666666663,0.933333333333333348],"xyz":[0.229773729882376043,0.131317516939167239,0.82215652291743746],"hpluv":[269.772657810053431,354.717803858999673,42.9610953823040305],"hsluv":[269.772657810053431,90.971694410809846,42.9610953823040305]},"#6644ff":{"lch":[44.4879743720372502,130.630057251556309,269.16406595263021],"luv":[44.4879743720372502,-1.90579898569535566,-130.61615439053088],"rgb":[0.4,0.266666666666666663,1],"xyz":[0.255944778292207409,0.141785936303099935,0.959990711209219305],"hpluv":[269.16406595263021,372.597392941492103,44.4879743720372502],"hsluv":[269.16406595263021,99.9999999999993463,44.4879743720372502]},"#ddcc00":{"lch":[81.0484811072975475,89.5621409057231119,78.2088923998372394],"luv":[81.0484811072975475,18.3014975440108643,87.6723004789036224],"rgb":[0.866666666666666696,0.8,0],"xyz":[0.514100482595981623,0.585588123937282434,0.0859503143074022424],"hpluv":[78.2088923998372394,197.564965691755532,81.0484811072975475],"hsluv":[78.2088923998372394,100.000000000002245,81.0484811072975475]},"#ddcc11":{"lch":[81.070830830397739,88.588371741804238,78.1407504993947413],"luv":[81.070830830397739,18.2056357525625714,86.6974880530521261],"rgb":[0.866666666666666696,0.8,0.0666666666666666657],"xyz":[0.515112148095618783,0.585992790137137343,0.0912784192721578791],"hpluv":[78.1407504993947413,195.686226016320433,81.070830830397739],"hsluv":[78.1407504993947413,98.8754134531589699,81.070830830397739]},"#ddcc22":{"lch":[81.1122340585289265,86.7947856050049751,78.0111959233441894],"luv":[81.1122340585289265,18.0290607078827385,84.9016358983162149],"rgb":[0.866666666666666696,0.8,0.133333333333333331],"xyz":[0.516987506234095728,0.586742933392528143,0.101155305468137072],"hpluv":[78.0111959233441894,192.214732219338657,81.1122340585289265],"hsluv":[78.0111959233441894,96.8049917221618443,81.1122340585289265]},"#ddcc33":{"lch":[81.1803270736657367,83.8738846457710139,77.7882255314295747],"luv":[81.1803270736657367,17.7414783140301893,81.9760237679585089],"rgb":[0.866666666666666696,0.8,0.2],"xyz":[0.520075256966553545,0.58797803368551127,0.117417459325748208],"hpluv":[77.7882255314295747,186.530107062901806,81.1803270736657367],"hsluv":[77.7882255314295747,93.4358723678778631,81.1803270736657367]},"#ddcc44":{"lch":[81.2784695635313312,79.7261575767135469,77.443216014682],"luv":[81.2784695635313312,17.3330313363216426,77.8191893214074213],"rgb":[0.866666666666666696,0.8,0.266666666666666663],"xyz":[0.524533252210772405,0.589761231783198836,0.140896234278634847],"hpluv":[77.443216014682,178.389435414851135,81.2784695635313312],"hsluv":[77.443216014682,88.6570950843060643,81.2784695635313312]},"#ddcc55":{"lch":[81.4094229919429893,74.303525235023784,76.9333685993953509],"luv":[81.4094229919429893,16.7988406729037365,72.37964364652683],"rgb":[0.866666666666666696,0.8,0.333333333333333315],"xyz":[0.530495666250134335,0.592146197398943586,0.172298281552608762],"hpluv":[76.9333685993953509,167.620308244181615,81.4094229919429893],"hsluv":[76.9333685993953509,82.4185745103993668,81.4094229919429893]},"#ddcc66":{"lch":[81.5755062452221154,67.6049359495878406,76.1891492108976252],"luv":[81.5755062452221154,16.1384724175358123,65.6504156329288548],"rgb":[0.866666666666666696,0.8,0.4],"xyz":[0.538080662023642575,0.595180195708346904,0.212245925959752724],"hpluv":[76.1891492108976252,154.10859642349061,81.5755062452221154],"hsluv":[76.1891492108976252,74.724981631947216,81.5755062452221154]},"#ddcc77":{"lch":[81.7786782860545571,59.6741108071854,75.0887439649445554],"luv":[81.7786782860545571,15.3554995822328237,57.6646176889111857],"rgb":[0.866666666666666696,0.8,0.466666666666666674],"xyz":[0.547394675043844425,0.598905800916427755,0.261299727866150633],"hpluv":[75.0887439649445554,137.792143148878608,81.7786782860545571],"hsluv":[75.0887439649445554,65.6305091544174104,81.7786782860545571]},"#ddcc88":{"lch":[82.020587165389415,50.599662209211,73.3985048227604722],"luv":[82.020587165389415,14.4570002873895778,48.4904213054152891],"rgb":[0.866666666666666696,0.8,0.533333333333333326],"xyz":[0.558535092145788337,0.603361967757205298,0.319972591269722773],"hpluv":[73.3985048227604722,118.661765286999071,82.020587165389415],"hsluv":[73.3985048227604722,55.2326761810852389,82.020587165389415]},"#ddcc99":{"lch":[82.3026016456871901,40.5232855707982438,70.6109505258247765],"luv":[82.3026016456871901,13.4529549720920478,38.2250529884698622],"rgb":[0.866666666666666696,0.8,0.6],"xyz":[0.571592056254861,0.608584753400834511,0.388739268910840707],"hpluv":[70.6109505258247765,96.7842051030752231,82.3026016456871901],"hsluv":[70.6109505258247765,43.6647666281718685,82.3026016456871901]},"#ddccaa":{"lch":[82.6258332993788542,29.6818113573176419,65.4008685536862231],"luv":[82.6258332993788542,12.3555588351715961,26.9879619705157374],"rgb":[0.866666666666666696,0.8,0.66666666666666663],"xyz":[0.586649749220113592,0.614607830586935577,0.468043118527839308],"hpluv":[65.4008685536862231,72.4135107626852,82.6258332993788542],"hsluv":[65.4008685536862231,31.0871436920773085,82.6258332993788542]},"#ddccbb":{"lch":[82.9911533066729419,18.6379527757414252,53.1465475992889],"luv":[82.9911533066729419,11.1784915779275345,14.9135713265793797],"rgb":[0.866666666666666696,0.8,0.733333333333333282],"xyz":[0.603787341327450156,0.621462867429870336,0.55830110362648111],"hpluv":[53.1465475992889,46.5946179098545272,82.9911533066729419],"hsluv":[53.1465475992889,24.678645774572626,82.9911533066729419]},"#ddcccc":{"lch":[83.3992063850657,10.164901186858037,12.1770506300648638],"luv":[83.3992063850657,9.93619558955776228,2.14411598208697862],"rgb":[0.866666666666666696,0.8,0.8],"xyz":[0.62307971677117191,0.629179817607359193,0.659907614296751266],"hpluv":[12.1770506300648638,26.1289592314662436,83.3992063850657],"hsluv":[12.1770506300648638,25.0002112827592455,83.3992063850657]},"#ddccdd":{"lch":[83.8504233095379163,14.1290270468723165,307.715012949259346],"luv":[83.8504233095379163,8.64321103323171513,-11.1769543403501022],"rgb":[0.866666666666666696,0.8,0.866666666666666696],"xyz":[0.644598042321805509,0.637787147827612699,0.773237462196757264],"hpluv":[307.715012949259346,37.4791950150616557,83.8504233095379163],"hsluv":[307.715012949259346,25.214872966603707,83.8504233095379163]},"#ddccee":{"lch":[84.3450329093034,25.9620569722210597,286.361909425528779],"luv":[84.3450329093034,7.31360586969829818,-24.9106316943502399],"rgb":[0.866666666666666696,0.8,0.933333333333333348],"xyz":[0.668410222587980218,0.647312019934082716,0.898648278265280487],"hpluv":[286.361909425528779,71.3464154396222199,84.3450329093034],"hsluv":[286.361909425528779,60.879598082714125,84.3450329093034]},"#ddccff":{"lch":[84.8830740665913623,39.380529428060008,278.705583312848262],"luv":[84.8830740665913623,5.96052452183370107,-38.926831947371717],"rgb":[0.866666666666666696,0.8,1],"xyz":[0.694581270997811639,0.65778043929801544,1.03648246655706222],"hpluv":[278.705583312848262,112.590543218900592,84.8830740665913623],"hsluv":[278.705583312848262,99.9999999999947704,84.8830740665913623]},"#665500":{"lch":[36.5970311204425656,41.5054710368830655,69.2006364019199651],"luv":[36.5970311204425656,14.7384507745119784,38.800543743107859],"rgb":[0.4,0.333333333333333315,0],"xyz":[0.0872772466047234,0.0932201343710592,0.0133963111976744542],"hpluv":[69.2006364019199651,143.912599562803223,36.5970311204425656],"hsluv":[69.2006364019199651,100.000000000002359,36.5970311204425656]},"#665511":{"lch":[36.673028710438345,38.8606023214232366,68.2658014049057869],"luv":[36.673028710438345,14.3901304021596417,36.0980686435250391],"rgb":[0.4,0.333333333333333315,0.0666666666666666657],"xyz":[0.0882889121043605174,0.0936248005709140463,0.0187244161624300839],"hpluv":[68.2658014049057869,134.462776824764262,36.673028710438345],"hsluv":[68.2658014049057869,93.0449405246809107,36.673028710438345]},"#665522":{"lch":[36.8133307706753357,34.1687527519613923,66.2355675996872719],"luv":[36.8133307706753357,13.769229594380441,31.2715842419585037],"rgb":[0.4,0.333333333333333315,0.133333333333333331],"xyz":[0.0901642702428375464,0.0943749438263048607,0.0286013023584092835],"hpluv":[66.2355675996872719,117.777773408929676,36.8133307706753357],"hsluv":[66.2355675996872719,80.6853283069105629,36.8133307706753357]},"#665533":{"lch":[37.0427251812615097,27.0303271109949854,61.7081001991288645],"luv":[37.0427251812615097,12.8113945713483854,23.8014023297917383],"rgb":[0.4,0.333333333333333315,0.2],"xyz":[0.09325202097529528,0.0956100441192879735,0.0448634562160204267],"hpluv":[61.7081001991288645,92.5950345984193177,37.0427251812615097],"hsluv":[61.7081001991288645,61.7209513910547045,37.0427251812615097]},"#665544":{"lch":[37.3704580906404473,18.1024774089589755,50.3425610862832542],"luv":[37.3704580906404473,11.5529306171195891,13.9366237840407656],"rgb":[0.4,0.333333333333333315,0.266666666666666663],"xyz":[0.0977100162195141536,0.0973932422169755535,0.0683422311689070583],"hpluv":[50.3425610862832542,61.4679768710498351,37.3704580906404473],"hsluv":[50.3425610862832542,37.0117339514184067,37.3704580906404473]},"#665555":{"lch":[37.8025949068387348,10.2943047784276782,12.1770506300631105],"luv":[37.8025949068387348,10.0626876598879917,2.1714115065313786],"rgb":[0.4,0.333333333333333315,0.333333333333333315],"xyz":[0.103672430258876153,0.0997782078327203864,0.0997442784428809737],"hpluv":[12.1770506300631105,34.5553054430909654,37.8025949068387348],"hsluv":[12.1770506300631105,8.09737912949257321,37.8025949068387348]},"#665566":{"lch":[38.3424918197480693,13.7697499972876347,307.715012949250308],"luv":[38.3424918197480693,8.42342892447929,-10.8927434626015351],"rgb":[0.4,0.333333333333333315,0.4],"xyz":[0.111257426032384324,0.102812206142123705,0.139691922850024935],"hpluv":[307.715012949250308,45.570631638882567,38.3424918197480693],"hsluv":[307.715012949250308,15.7660506346962208,38.3424918197480693]},"#665577":{"lch":[38.9911218270375812,25.5692820710091411,285.225395910208761],"luv":[38.9911218270375812,6.71492531026355,-24.671805035392353],"rgb":[0.4,0.333333333333333315,0.466666666666666674],"xyz":[0.120571439052586243,0.106537811350204514,0.188745724756422845],"hpluv":[285.225395910208761,83.2131821356252885,38.9911218270375812],"hsluv":[285.225395910208761,23.5948697041559434,38.9911218270375812]},"#665588":{"lch":[39.7473800461840554,39.003702064123587,277.369365694294345],"luv":[39.7473800461840554,5.00282395952989312,-38.6815269493963],"rgb":[0.4,0.333333333333333315,0.533333333333333326],"xyz":[0.131711856154530071,0.110993978190982112,0.247418588159995],"hpluv":[277.369365694294345,124.519293959659265,39.7473800461840554],"hsluv":[277.369365694294345,31.2388068533835614,39.7473800461840554]},"#665599":{"lch":[40.6084045881889466,52.6763518020623636,273.629018089310307],"luv":[40.6084045881889466,3.33420095564239061,-52.5707251534733189],"rgb":[0.4,0.333333333333333315,0.6],"xyz":[0.144768820263602771,0.11621676383461127,0.316185265801112947],"hpluv":[273.629018089310307,164.603508765513965,40.6084045881889466],"hsluv":[273.629018089310307,39.2894117268144569,40.6084045881889466]},"#6655aa":{"lch":[41.5699140891343575,66.1399121177165,271.505962745252077],"luv":[41.5699140891343575,1.73822184255944534,-66.1170670838121168],"rgb":[0.4,0.333333333333333315,0.66666666666666663],"xyz":[0.159826513228855327,0.122239841020712364,0.395489115418111492],"hpluv":[271.505962745252077,201.894186201954597,41.5699140891343575],"hsluv":[271.505962745252077,49.6682832978127067,41.5699140891343575]},"#6655bb":{"lch":[42.6265484117568647,79.2031960637105499,270.166039680829499],"luv":[42.6265484117568647,0.229525718995523903,-79.2028634876978],"rgb":[0.4,0.333333333333333315,0.733333333333333282],"xyz":[0.176964105336191974,0.129094877863647123,0.485747100516753294],"hpluv":[270.166039680829499,235.777232293519603,42.6265484117568647],"hsluv":[270.166039680829499,59.8653820834470309,42.6265484117568647]},"#6655cc":{"lch":[43.7721949853351333,91.7929775852419,269.258674658723919],"luv":[43.7721949853351333,-1.18763654850412936,-91.7852943199147688],"rgb":[0.4,0.333333333333333315,0.8],"xyz":[0.1962564807799137,0.136811828041135924,0.58735361118702345],"hpluv":[269.258674658723919,266.103421792879146,43.7721949853351333],"hsluv":[269.258674658723919,69.9112236392489734,43.7721949853351333]},"#6655dd":{"lch":[45.0002850881211458,103.899953671233533,268.61259940679895],"luv":[45.0002850881211458,-2.51566121975990953,-103.869494181457682],"rgb":[0.4,0.333333333333333315,0.866666666666666696],"xyz":[0.217774806330547244,0.145419158261389458,0.700683459087029448],"hpluv":[268.61259940679895,292.98097185143456,45.0002850881211458],"hsluv":[268.61259940679895,79.8793625185682714,45.0002850881211458]},"#6655ee":{"lch":[46.3040490971424106,115.549020689755764,268.134901078425969],"luv":[46.3040490971424106,-3.7607010001107648,-115.487805894602445],"rgb":[0.4,0.333333333333333315,0.933333333333333348],"xyz":[0.241586986596722,0.154944030367859503,0.826094275155552671],"hpluv":[268.134901078425969,316.655201018988919,46.3040490971424106],"hsluv":[268.134901078425969,89.8701820385079344,46.3040490971424106]},"#6655ff":{"lch":[47.6767252326213651,126.781348408818275,267.771145841725911],"luv":[47.6767252326213651,-4.93065761335473951,-126.685432942615918],"rgb":[0.4,0.333333333333333315,1],"xyz":[0.267758035006553374,0.165412449731792199,0.963928463447334516],"hpluv":[267.771145841725911,337.433561350206048,47.6767252326213651],"hsluv":[267.771145841725911,99.9999999999992468,47.6767252326213651]},"#dddd00":{"lch":[85.547159878993142,94.3072427966830844,85.8743202181747449],"luv":[85.547159878993142,6.78488618903739749,94.0628585750738466],"rgb":[0.866666666666666696,0.866666666666666696,0],"xyz":[0.556734473143156827,0.670856105031634,0.100161644489793561],"hpluv":[85.8743202181747449,283.614606809988061,85.547159878993142],"hsluv":[85.8743202181747449,100.000000000002203,85.547159878993142]},"#dddd11":{"lch":[85.5675738163798627,93.4011806547303394,85.8743202181746881],"luv":[85.5675738163798627,6.71970001318247423,93.159144368282],"rgb":[0.866666666666666696,0.866666666666666696,0.0666666666666666657],"xyz":[0.557746138642794,0.671260771231488862,0.105489749454549198],"hpluv":[85.8743202181746881,281.335749103468061,85.5675738163798627],"hsluv":[85.8743202181746881,99.0156164862488,85.5675738163798627]},"#dddd22":{"lch":[85.6053941241358558,91.7307060091609401,85.8743202181746739],"luv":[85.6053941241358558,6.59951857201479086,91.4929985275198447],"rgb":[0.866666666666666696,0.866666666666666696,0.133333333333333331],"xyz":[0.559621496781270933,0.672010914486879662,0.11536663565052839],"hpluv":[85.8743202181746739,277.118842420723468,85.6053941241358558],"hsluv":[85.8743202181746739,97.2017654403352083,85.6053941241358558]},"#dddd33":{"lch":[85.667603455332241,89.0058006932873,85.8743202181745602],"luv":[85.667603455332241,6.4034766573555908,88.775154428206335],"rgb":[0.866666666666666696,0.866666666666666696,0.2],"xyz":[0.56270924751372875,0.673246014779862789,0.131628789508139526],"hpluv":[85.8743202181745602,270.196330508983522,85.667603455332241],"hsluv":[85.8743202181745602,94.2458512979473113,85.667603455332241]},"#dddd44":{"lch":[85.7572852094861418,85.1265937151141117,85.8743202181744607],"luv":[85.7572852094861418,6.12438910193468278,84.9059998802570419],"rgb":[0.866666666666666696,0.866666666666666696,0.266666666666666663],"xyz":[0.567167242757947609,0.675029212877550355,0.155107564461026165],"hpluv":[85.8743202181744607,260.24482525674506,85.7572852094861418],"hsluv":[85.8743202181744607,90.0440000232135844,85.7572852094861418]},"#dddd55":{"lch":[85.8769849033878074,80.0369945631262,85.874320218174276],"luv":[85.8769849033878074,5.75822050268421481,79.8295897229865545],"rgb":[0.866666666666666696,0.866666666666666696,0.333333333333333315],"xyz":[0.573129656797309539,0.677414178493295105,0.18650961173500008],"hpluv":[85.874320218174276,247.008869171162701,85.8769849033878074],"hsluv":[85.874320218174276,84.5423921226573327,85.8769849033878074]},"#dddd66":{"lch":[86.0288537292730098,73.7198014533942398,85.8743202181739775],"luv":[86.0288537292730098,5.30373328608645522,73.5287667485177252],"rgb":[0.866666666666666696,0.866666666666666696,0.4],"xyz":[0.58071465257081778,0.680448176802698423,0.226457256142144042],"hpluv":[85.8743202181739775,230.281095596483937,86.0288537292730098],"hsluv":[85.8743202181739775,77.7321300368988,86.0288537292730098]},"#dddd77":{"lch":[86.2147251389940834,66.1931358813644124,85.8743202181736365],"luv":[86.2147251389940834,4.76223119383214133,66.0216054929389315],"rgb":[0.866666666666666696,0.866666666666666696,0.466666666666666674],"xyz":[0.59002866559101963,0.684173782010779274,0.275511058048541924],"hpluv":[85.8743202181736365,209.886373280136951,86.2147251389940834],"hsluv":[85.8743202181736365,69.6453389130317362,86.2147251389940834]},"#dddd88":{"lch":[86.4361603707972,57.5066396271378224,85.8743202181730823],"luv":[86.4361603707972,4.13728567831694072,57.3576190965201391],"rgb":[0.866666666666666696,0.866666666666666696,0.533333333333333326],"xyz":[0.601169082692963541,0.688629948851556817,0.334183921452114119],"hpluv":[85.8743202181730823,185.665493576193455,86.4361603707972],"hsluv":[85.8743202181730823,60.3508056243124429,86.4361603707972]},"#dddd99":{"lch":[86.6944777431662,47.7369558995854888,85.8743202181722154],"luv":[86.6944777431662,3.43441079587341314,47.613252157820078],"rgb":[0.866666666666666696,0.866666666666666696,0.6],"xyz":[0.614226046802036185,0.693852734495186,0.402950599093232054],"hpluv":[85.8743202181722154,157.456397081560084,86.6944777431662],"hsluv":[85.8743202181722154,49.9486591868920939,86.6944777431662]},"#ddddaa":{"lch":[86.990772885999732,36.9824894935128,85.874320218170638],"luv":[86.990772885999732,2.66068622896724838,36.886654469181245],"rgb":[0.866666666666666696,0.866666666666666696,0.66666666666666663],"xyz":[0.629283739767288797,0.699875811681287097,0.482254448710230599],"hpluv":[85.874320218170638,125.071764704863014,86.990772885999732],"hsluv":[85.874320218170638,38.5641267001819443,86.990772885999732]},"#ddddbb":{"lch":[87.3259337660435477,25.3576808227713499,85.8743202181676821],"luv":[87.3259337660435477,1.82434533444747116,25.2919698878957568],"rgb":[0.866666666666666696,0.866666666666666696,0.733333333333333282],"xyz":[0.64642133187462536,0.706730848524221855,0.572512433808872401],"hpluv":[85.8743202181676821,88.2719508819342735,87.3259337660435477],"hsluv":[85.8743202181676821,26.340671416181145,87.3259337660435477]},"#ddddcc":{"lch":[87.7006527393466797,12.9871390430395461,85.8743202181585445],"luv":[87.7006527393466797,0.934353054076054512,12.9534846621895881],"rgb":[0.866666666666666696,0.866666666666666696,0.8],"xyz":[0.665713707318347114,0.714447798701710712,0.674118944479142557],"hpluv":[85.8743202181585445,46.7319159493201113,87.7006527393466797],"hsluv":[85.8743202181585445,13.4329442518860063,87.7006527393466797]},"#dddddd":{"lch":[88.1154369871094,4.67545248961294327e-12,0],"luv":[88.1154369871094,4.4193702762792188e-12,1.52611347670073729e-12],"rgb":[0.866666666666666696,0.866666666666666696,0.866666666666666696],"xyz":[0.687232032868980713,0.723055128921964219,0.787448792379148554],"hpluv":[0,1.74708563976297451e-11,88.1154369871094],"hsluv":[0,1.74437740136320375e-11,88.1154369871094]},"#ddddee":{"lch":[88.5706181797242209,13.4751686036456029,265.874320218195521],"luv":[88.5706181797242209,-0.969464090371862097,-13.4402495614536726],"rgb":[0.866666666666666696,0.866666666666666696,0.933333333333333348],"xyz":[0.711044213135155423,0.732580001028434236,0.912859608447671778],"hpluv":[265.874320218195521,52.5550848411252431,88.5706181797242209],"hsluv":[265.874320218195521,47.1269490590101725,88.5706181797242209]},"#ddddff":{"lch":[89.0663618949558753,27.3146757005029208,265.874320218186369],"luv":[89.0663618949558753,-1.9651403266809826,-27.2438934831293338],"rgb":[0.866666666666666696,0.866666666666666696,1],"xyz":[0.737215261544986844,0.743048420392367,1.05069379673945362],"hpluv":[265.874320218186369,111.815120511018762,89.0663618949558753],"hsluv":[265.874320218186369,99.9999999999922125,89.0663618949558753]},"#666600":{"lch":[41.7321583215394583,46.0055575524193685,85.8743202181747449],"luv":[41.7321583215394583,3.30984623025532709,45.8863404908370924],"rgb":[0.4,0.4,0],"xyz":[0.102305304310569861,0.123276249782752534,0.0184056637662898],"hpluv":[85.8743202181747449,139.887458074797365,41.7321583215394583],"hsluv":[85.8743202181747449,100.000000000002203,41.7321583215394583]},"#666611":{"lch":[41.7952597887023742,43.6298127640598423,85.8743202181746],"luv":[41.7952597887023742,3.13892449057558931,43.5167521176544625],"rgb":[0.4,0.4,0.0666666666666666657],"xyz":[0.103316969810206979,0.123680915982607387,0.0237337687310454348],"hpluv":[85.8743202181746,132.463323325332908,41.7952597887023742],"hsluv":[85.8743202181746,94.6927802880713756,41.7952597887023742]},"#666622":{"lch":[41.9118699845736913,39.3503176022612067,85.874320218174276],"luv":[41.9118699845736913,2.83103840719259514,39.248346677737],"rgb":[0.4,0.4,0.133333333333333331],"xyz":[0.105192327948684008,0.124431059237998201,0.0336106549270246274],"hpluv":[85.874320218174276,119.138061739500813,41.9118699845736913],"hsluv":[85.874320218174276,85.1670788640685288,41.9118699845736913]},"#666633":{"lch":[42.1028501842444953,32.6344620115447057,85.8743202181736507],"luv":[42.1028501842444953,2.34786962297492563,32.5498943011565842],"rgb":[0.4,0.4,0.2],"xyz":[0.108280078681141742,0.125666159530981314,0.0498728087846357776],"hpluv":[85.8743202181736507,98.3567766096709306,42.1028501842444953],"hsluv":[85.8743202181736507,70.3113616926845,42.1028501842444953]},"#666644":{"lch":[42.3763861696741557,23.5947988734222314,85.8743202181723575],"luv":[42.3763861696741557,1.69751569722617934,23.5336562041455402],"rgb":[0.4,0.4,0.266666666666666663],"xyz":[0.112738073925360616,0.127449357628668908,0.0733515837375224161],"hpluv":[85.8743202181723575,70.6531759312171346,42.3763861696741557],"hsluv":[85.8743202181723575,50.5071554688203506,42.3763861696741557]},"#666655":{"lch":[42.7382714661199543,12.562340839470254,85.87432021816781],"luv":[42.7382714661199543,0.903791165304248856,12.5297872646162745],"rgb":[0.4,0.4,0.333333333333333315],"xyz":[0.118700487964722615,0.129834323244413741,0.104753631011496318],"hpluv":[85.87432021816781,37.2986356199978459,42.7382714661199543],"hsluv":[85.87432021816781,26.6633164497530721,42.7382714661199543]},"#666666":{"lch":[43.1922895629847048,2.27708065554704512e-12,0],"luv":[43.1922895629847048,2.15069538500574498e-12,7.48067960001998255e-13],"rgb":[0.4,0.4,0.4],"xyz":[0.126285483738230786,0.132868321553817031,0.144701275418640279],"hpluv":[0,6.68977504875838914e-12,43.1922895629847048],"hsluv":[0,1.91542116883063395e-12,43.1922895629847048]},"#666677":{"lch":[43.7404449074606489,13.5883126365404472,265.874320218186085],"luv":[43.7404449074606489,-0.977604179760566572,-13.5531003971814314],"rgb":[0.4,0.4,0.466666666666666674],"xyz":[0.135599496758432692,0.136593926761897855,0.193755077325038189],"hpluv":[265.874320218186085,39.4204575510779804,43.7404449074606489],"hsluv":[265.874320218186085,10.3527957183817456,43.7404449074606489]},"#666688":{"lch":[44.3831523723879684,27.7327571842679852,265.874320218181708],"luv":[44.3831523723879684,-1.9952189844931838,-27.660891566352042],"rgb":[0.4,0.4,0.533333333333333326],"xyz":[0.146739913860376547,0.141050093602675453,0.252427940728610356],"hpluv":[265.874320218181708,79.2892354857961692,44.3831523723879684],"hsluv":[265.874320218181708,21.2254167484079588,44.3831523723879684]},"#666699":{"lch":[45.1194249231942308,42.0446421145154,265.87432021818023],"luv":[45.1194249231942308,-3.02488020162380478,-41.9356892193690598],"rgb":[0.4,0.4,0.6],"xyz":[0.159796877969449247,0.14627287924630461,0.321194618369728291],"hpluv":[265.87432021818023,118.245992523098394,45.1194249231942308],"hsluv":[265.87432021818023,32.3647541960069702,45.1194249231942308]},"#6666aa":{"lch":[45.9470714788517682,56.2348015337582652,265.874320218179548],"luv":[45.9470714788517682,-4.04578393932889835,-56.0890767962662125],"rgb":[0.4,0.4,0.66666666666666663],"xyz":[0.174854570934701803,0.152295956432405705,0.400498467986726892],"hpluv":[265.874320218179548,155.305436018888514,45.9470714788517682],"hsluv":[265.874320218179548,43.5990379455573205,45.9470714788517682]},"#6666bb":{"lch":[46.8629040956598786,70.1103551977131,265.874320218179093],"luv":[46.8629040956598786,-5.04405352741049562,-69.9286738753289541],"rgb":[0.4,0.4,0.733333333333333282],"xyz":[0.191992163042038422,0.159150993275340463,0.490756453085368638],"hpluv":[265.874320218179093,189.841997809706953,46.8629040956598786],"hsluv":[265.874320218179093,54.8353857399755285,46.8629040956598786]},"#6666cc":{"lch":[47.8629477245616854,83.5592716582008,265.874320218178866],"luv":[47.8629477245616854,-6.01162892081844,-83.3427393224351505],"rgb":[0.4,0.4,0.8],"xyz":[0.211284538485760176,0.166867943452829265,0.592362963755638794],"hpluv":[265.874320218178866,221.531011478982748,47.8629477245616854],"hsluv":[265.874320218178866,66.0482344892977693,47.8629477245616854]},"#6666dd":{"lch":[48.9426439028117102,96.5306872715973583,265.874320218178696],"luv":[48.9426439028117102,-6.94485076081307,-96.2805413000828736],"rgb":[0.4,0.4,0.866666666666666696],"xyz":[0.23280286403639372,0.175475273673082799,0.705692811655644792],"hpluv":[265.874320218178696,250.274901054084751,48.9426439028117102],"hsluv":[265.874320218178696,77.2646968282616911,48.9426439028117102]},"#6666ee":{"lch":[50.0970402589656203,109.016062738443594,265.874320218178525],"luv":[50.0970402589656203,-7.84310469187671711,-108.73356263723052],"rgb":[0.4,0.4,0.933333333333333348],"xyz":[0.256615044302568429,0.185000145779552844,0.831103627724168],"hpluv":[265.874320218178525,276.132643737939816,50.0970402589656203],"hsluv":[265.874320218178525,88.5507283896609181,50.0970402589656203]},"#6666ff":{"lch":[51.3209595583197142,121.033610519319112,265.874320218178411],"luv":[51.3209595583197142,-8.70770100014002502,-120.719968599376287],"rgb":[0.4,0.4,1],"xyz":[0.28278609271239985,0.19546856514348554,0.96893781601594986],"hpluv":[265.874320218178411,299.261292593223402,51.3209595583197142],"hsluv":[265.874320218178411,99.9999999999991616,51.3209595583197142]},"#ddee00":{"lch":[90.1008574130140261,100.518542770188731,92.3281002120423295],"luv":[90.1008574130140261,-4.08324753875312307,100.435574027231638],"rgb":[0.866666666666666696,0.933333333333333348,0],"xyz":[0.603913249483671644,0.765213657712664919,0.115887903269964759],"hpluv":[92.3281002120423295,458.324419080212692,90.1008574130140261],"hsluv":[92.3281002120423295,100.000000000002288,90.1008574130140261]},"#ddee11":{"lch":[90.1195571422023676,99.6821059706602739,92.3717344777628284],"luv":[90.1195571422023676,-4.12512016496088219,99.5967149778072667],"rgb":[0.866666666666666696,0.933333333333333348,0.0666666666666666657],"xyz":[0.604924914983308804,0.765618323912519827,0.121216008234720396],"hpluv":[92.3717344777628284,455.439701471196656,90.1195571422023676],"hsluv":[92.3717344777628284,99.1349582088955827,90.1195571422023676]},"#ddee22":{"lch":[90.1542040339558213,98.139145338163118,92.4541942657322409],"luv":[90.1542040339558213,-4.20238430415396635,98.0491295925940562],"rgb":[0.866666666666666696,0.933333333333333348,0.133333333333333331],"xyz":[0.606800273121785749,0.766368467167910628,0.131092894430699575],"hpluv":[92.4541942657322409,450.094002053356689,90.1542040339558213],"hsluv":[92.4541942657322409,97.539848527320828,90.1542040339558213]},"#ddee33":{"lch":[90.2112004280082402,95.6198982889531237,92.5945991605726562],"luv":[90.2112004280082402,-4.32860020636881337,95.5218727257959728],"rgb":[0.866666666666666696,0.933333333333333348,0.2],"xyz":[0.609888023854243566,0.767603567460893754,0.147355048288310725],"hpluv":[92.5945991605726562,441.296742170845221,90.2112004280082402],"hsluv":[92.5945991605726562,94.9371734060285348,90.2112004280082402]},"#ddee44":{"lch":[90.2933822328294582,92.028546743238266,92.808164678773025],"luv":[90.2933822328294582,-4.50867173729536486,91.9180357429253689],"rgb":[0.866666666666666696,0.933333333333333348,0.266666666666666663],"xyz":[0.614346019098462426,0.76938676555858132,0.170833823241197363],"hpluv":[92.808164678773025,428.602609406071736,90.2933822328294582],"hsluv":[92.808164678773025,91.2305485856041827,90.2933822328294582]},"#ddee55":{"lch":[90.4030992965535,87.3081168904988658,93.1158428735316477],"luv":[90.4030992965535,-4.74562541158387852,87.1790474507375137],"rgb":[0.866666666666666696,0.933333333333333348,0.333333333333333315],"xyz":[0.620308433137824355,0.77177173117432607,0.202235870515171279],"hpluv":[93.1158428735316477,411.63119840797242,90.4030992965535],"hsluv":[93.1158428735316477,86.365058197250562,90.4030992965535]},"#ddee66":{"lch":[90.5423480313319828,81.4363754503924,93.5488244177654451],"luv":[90.5423480313319828,-5.04083628618753,81.2802141731499717],"rgb":[0.866666666666666696,0.933333333333333348,0.4],"xyz":[0.627893428911332596,0.774805729483729388,0.242183514922315241],"hpluv":[93.5488244177654451,390.03815237863023,90.5423480313319828],"hsluv":[93.5488244177654451,80.3228278296054583,90.5423480313319828]},"#ddee77":{"lch":[90.7128424721769449,74.4235139877598613,94.1564113073248],"luv":[90.7128424721769449,-5.39417557361030209,74.2277731322134855],"rgb":[0.866666666666666696,0.933333333333333348,0.466666666666666674],"xyz":[0.637207441931534446,0.77853133469181024,0.291237316828713122],"hpluv":[94.1564113073248,363.492124647987794,90.7128424721769449],"hsluv":[94.1564113073248,73.1200108954656116,90.7128424721769449]},"#ddee88":{"lch":[90.9160566530372449,66.3104835404903099,95.0215233218939801],"luv":[90.9160566530372449,-5.80415396856009735,66.0559764448523197],"rgb":[0.866666666666666696,0.933333333333333348,0.533333333333333326],"xyz":[0.648347859033478358,0.782987501532587782,0.349910180232285317],"hpluv":[95.0215233218939801,331.65301907995223,90.9160566530372449],"hsluv":[95.0215233218939801,64.803658473982523,90.9160566530372449]},"#ddee99":{"lch":[91.1532518637430798,57.1683554138076389,96.2947128608995513],"luv":[91.1532518637430798,-6.26808654843775237,56.8236918172404799],"rgb":[0.866666666666666696,0.933333333333333348,0.6],"xyz":[0.661404823142551,0.788210287176217,0.418676857873403252],"hpluv":[96.2947128608995513,294.152965661212647,91.1532518637430798],"hsluv":[96.2947128608995513,55.4479539090275679,91.1532518637430798]},"#ddeeaa":{"lch":[91.4254953447680805,47.1012792006961263,98.2790075719046712],"luv":[91.4254953447680805,-6.78228473243696595,46.6104185365255219],"rgb":[0.866666666666666696,0.933333333333333348,0.66666666666666663],"xyz":[0.676462516107803613,0.794233364362318062,0.497980707490401797],"hpluv":[98.2790075719046712,250.593114601078071,91.4254953447680805],"hsluv":[98.2790075719046712,45.1497304611624699,91.4254953447680805]},"#ddeebb":{"lch":[91.7336739482950634,36.2629153409390739,101.681625346389353],"luv":[91.7336739482950634,-7.34227032126066,35.5118303605101318],"rgb":[0.866666666666666696,0.933333333333333348,0.733333333333333282],"xyz":[0.693600108215140176,0.80108840120525282,0.588238692589043599],"hpluv":[101.681625346389353,200.613962446551909,91.7336739482950634],"hsluv":[101.681625346389353,34.0234190840686495,91.7336739482950634]},"#ddeecc":{"lch":[92.0785048140775189,24.9340669502590622,108.575873850927678],"luv":[92.0785048140775189,-7.94300120518182506,23.6350677285782567],"rgb":[0.866666666666666696,0.933333333333333348,0.8],"xyz":[0.712892483658861931,0.808805351382741677,0.689845203259313755],"hpluv":[108.575873850927678,144.339382081965653,92.0785048140775189],"hsluv":[108.575873850927678,22.1956929245148693,92.0785048140775189]},"#ddeedd":{"lch":[92.4605443140240908,14.0242187757329084,127.71501294922345],"luv":[92.4605443140240908,-8.57909621466854766,11.0940443666446207],"rgb":[0.866666666666666696,0.933333333333333348,0.866666666666666696],"xyz":[0.73441080920949553,0.817412681602995184,0.803175051159319753],"hpluv":[127.71501294922345,85.5555802205660854,92.4605443140240908],"hsluv":[127.71501294922345,19.0167034911391681,92.4605443140240908]},"#ddeeee":{"lch":[92.8801960589335636,9.45784403502816851,192.177050630058346],"luv":[92.8801960589335636,-9.24504689815110403,-1.99497409554724747],"rgb":[0.866666666666666696,0.933333333333333348,0.933333333333333348],"xyz":[0.758222989475670239,0.826937553709465201,0.928585867227843],"hpluv":[192.177050630058346,61.3009405779386327,92.8801960589335636],"hsluv":[192.177050630058346,16.5065503962475049,92.8801960589335636]},"#ddeeff":{"lch":[93.3377184761608305,18.4254994321377019,237.36941304521946],"luv":[93.3377184761608305,-9.93540601395951306,-15.5173044263971267],"rgb":[0.866666666666666696,0.933333333333333348,1],"xyz":[0.78439403788550166,0.837405973073397925,1.06642005551962482],"hpluv":[237.36941304521946,128.083838047846456,93.3377184761608305],"hsluv":[237.36941304521946,99.9999999999860592,93.3377184761608305]},"#667700":{"lch":[46.9985837429297462,53.5023535392226,97.7743932102929705],"luv":[46.9985837429297462,-7.23741162388150272,53.0105810873873509],"rgb":[0.4,0.466666666666666674,0],"xyz":[0.12075904236398749,0.160183725889588319,0.0245569097840955056],"hpluv":[97.7743932102929705,144.453291553004675,46.9985837429297462],"hsluv":[97.7743932102929705,100.000000000002416,46.9985837429297462]},"#667711":{"lch":[47.0515894602548315,51.4525286527524344,98.1592061252685681],"luv":[47.0515894602548315,-7.3023584206057679,50.9317019768564],"rgb":[0.4,0.466666666666666674,0.0666666666666666657],"xyz":[0.121770707863624608,0.160588392089443172,0.0298850147488511353],"hpluv":[98.1592061252685681,138.762383385982389,47.0515894602548315],"hsluv":[98.1592061252685681,95.8888552433840573,47.0515894602548315]},"#667722":{"lch":[47.1496128779850068,47.7434140964333835,98.9413563019921],"luv":[47.1496128779850068,-7.4204465613794337,47.1632331632727],"rgb":[0.4,0.466666666666666674,0.133333333333333331],"xyz":[0.123646066002101637,0.161338535344834,0.0397619009448303348],"hpluv":[98.9413563019921,128.491579290861381,47.1496128779850068],"hsluv":[98.9413563019921,88.4562635009272071,47.1496128779850068]},"#667733":{"lch":[47.3103471969426579,41.884713336750373,100.466311561708949],"luv":[47.3103471969426579,-7.6086666810921777,41.1878307590699961],"rgb":[0.4,0.466666666666666674,0.2],"xyz":[0.126733816734559357,0.162573635637817099,0.0560240548024414781],"hpluv":[100.466311561708949,112.341117260385403,47.3103471969426579],"hsluv":[100.466311561708949,76.7252257071794,47.3103471969426579]},"#667744":{"lch":[47.5409803755201068,33.9506682862991624,103.399633201782777],"luv":[47.5409803755201068,-7.86778476785512915,33.0264415269496823],"rgb":[0.4,0.466666666666666674,0.266666666666666663],"xyz":[0.131191811978778244,0.164356833735504693,0.0795028297553281166],"hpluv":[103.399633201782777,90.6190532449782324,47.5409803755201068],"hsluv":[103.399633201782777,60.8161329795325543,47.5409803755201068]},"#667755":{"lch":[47.8468512336942808,24.3055974565694441,109.700167733355244],"luv":[47.8468512336942808,-8.19336864569332768,22.8829800934354815],"rgb":[0.4,0.466666666666666674,0.333333333333333315],"xyz":[0.137154226018140257,0.166741799351249526,0.110904877029302018],"hpluv":[109.700167733355244,64.4602914823941262,47.8468512336942808],"hsluv":[109.700167733355244,41.23982633361328,47.8468512336942808]},"#667766":{"lch":[48.2317738399223543,14.0211946941261125,127.715012949232488],"luv":[48.2317738399223543,-8.57724628010491408,11.0916521267580634],"rgb":[0.4,0.466666666666666674,0.4],"xyz":[0.144739221791648415,0.169775797660652816,0.15085252143644598],"hpluv":[127.715012949232488,36.8885098590324958,48.2317738399223543],"hsluv":[127.715012949232488,18.7828263722028552,48.2317738399223543]},"#667777":{"lch":[48.6982180758881356,9.21652694043341,192.177050630059739],"luv":[48.6982180758881356,-9.00915932709454736,-1.94407228846053126],"rgb":[0.4,0.466666666666666674,0.466666666666666674],"xyz":[0.154053234811850348,0.17350140286873364,0.199906323342843889],"hpluv":[192.177050630059739,24.0156061835451808,48.6982180758881356],"hsluv":[192.177050630059739,23.9216020554503501,48.6982180758881356]},"#667788":{"lch":[49.2474401880289605,18.4334880243097601,239.056580638027469],"luv":[49.2474401880289605,-9.47834019220879398,-15.8099509152663327],"rgb":[0.4,0.466666666666666674,0.533333333333333326],"xyz":[0.165193651913794148,0.177957569709511237,0.258579186746416056],"hpluv":[239.056580638027469,47.4966726259429564,49.2474401880289605],"hsluv":[239.056580638027469,29.274081353383373,49.2474401880289605]},"#667799":{"lch":[49.8796002039077422,31.7351192214463786,251.680675473596239],"luv":[49.8796002039077422,-9.97474964621831,-30.126768188683684],"rgb":[0.4,0.466666666666666674,0.6],"xyz":[0.178250616022866876,0.183180355353140395,0.327345864387534],"hpluv":[251.680675473596239,80.734004933806176,49.8796002039077422],"hsluv":[251.680675473596239,34.661761655835349,49.8796002039077422]},"#6677aa":{"lch":[50.5938810850088174,45.7960576637453798,256.758518919433186],"luv":[50.5938810850088174,-10.4898463625666416,-44.5784928057335676],"rgb":[0.4,0.466666666666666674,0.66666666666666663],"xyz":[0.193308308988119404,0.18920343253924149,0.406649714004532592],"hpluv":[256.758518919433186,114.860161977636537,50.5938810850088174],"hsluv":[256.758518919433186,39.9381656359214858,50.5938810850088174]},"#6677bb":{"lch":[51.388614147457119,59.9444468574027738,259.409682348511467],"luv":[51.388614147457119,-11.0169047239158306,-58.9233783768730106],"rgb":[0.4,0.466666666666666674,0.733333333333333282],"xyz":[0.210445901095456078,0.196058469382176248,0.496907699103174338],"hpluv":[259.409682348511467,148.020333314730891,51.388614147457119],"hsluv":[259.409682348511467,49.2101017344363356,51.388614147457119]},"#6677cc":{"lch":[52.2614099661724225,73.9004259234932306,261.00752312302825],"luv":[52.2614099661724225,-11.5509895931656974,-72.9921063615257566],"rgb":[0.4,0.466666666666666674,0.8],"xyz":[0.229738276539177805,0.20377541955966505,0.598514209773444494],"hpluv":[261.00752312302825,179.434168102703751,52.2614099661724225],"hsluv":[261.00752312302825,61.6581230008595327,52.2614099661724225]},"#6677dd":{"lch":[53.2092913421323,87.5170631601914266,262.060353074135],"luv":[53.2092913421323,-12.0887245225321198,-86.6781349799548],"rgb":[0.4,0.466666666666666674,0.866666666666666696],"xyz":[0.251256602089811376,0.212382749779918584,0.711844057673450492],"hpluv":[262.060353074135,208.710638687620559,53.2092913421323],"hsluv":[262.060353074135,74.2459971892304083,53.2092913421323]},"#6677ee":{"lch":[54.2288239257805884,100.719531231348427,262.797438231409785],"luv":[54.2288239257805884,-12.6279723106459851,-99.9247631309883531],"rgb":[0.4,0.466666666666666674,0.933333333333333348],"xyz":[0.275068782355986086,0.221907621886388629,0.837254873741973715],"hpluv":[262.797438231409785,235.68005803746027,54.2288239257805884],"hsluv":[262.797438231409785,87.0042080584200193,54.2288239257805884]},"#6677ff":{"lch":[55.3162401631211793,113.47857319487936,263.336661992011841],"luv":[55.3162401631211793,-13.1675101506557386,-112.712036849566218],"rgb":[0.4,0.466666666666666674,1],"xyz":[0.301239830765817507,0.232376041250321325,0.97508906203375556],"hpluv":[263.336661992011841,260.315806593762318,55.3162401631211793],"hsluv":[263.336661992011841,99.999999999999,55.3162401631211793]},"#ddff00":{"lch":[94.69236188875891,107.73563953931891,97.6513944636985],"luv":[94.69236188875891,-14.3445112693176284,106.77641604488548],"rgb":[0.866666666666666696,1,0],"xyz":[0.65576562191334542,0.868918402572014,0.13317202741318887],"hpluv":[97.6513944636985,949.977135711580445,94.69236188875891],"hsluv":[97.6513944636985,100.000000000002302,94.69236188875891]},"#ddff11":{"lch":[94.7095428290633237,106.965998536257018,97.7198690947758308],"luv":[94.7095428290633237,-14.3687245726656343,105.996531061225838],"rgb":[0.866666666666666696,1,0.0666666666666666657],"xyz":[0.65677728741298258,0.869323068771868934,0.138500132377944507],"hpluv":[97.7198690947758308,946.378692368412885,94.7095428290633237],"hsluv":[97.7198690947758308,99.9999999999867697,94.7095428290633237]},"#ddff22":{"lch":[94.7413776147606086,105.545731309599958,97.8489007917202827],"luv":[94.7413776147606086,-14.4134418215914106,104.556941866783148],"rgb":[0.866666666666666696,1,0.133333333333333331],"xyz":[0.658652645551459526,0.870073212027259735,0.148377018573923686],"hpluv":[97.8489007917202827,939.695870823628752,94.7413776147606086],"hsluv":[97.8489007917202827,99.9999999999872,94.7413776147606086]},"#ddff33":{"lch":[94.7937532988665197,103.225383790498825,98.0674915546983],"luv":[94.7937532988665197,-14.4865977424562669,102.203807876928138],"rgb":[0.866666666666666696,1,0.2],"xyz":[0.661740396283917343,0.871308312320242861,0.164639172431534836],"hpluv":[98.0674915546983,928.656435156239354,94.7937532988665197],"hsluv":[98.0674915546983,99.9999999999868834,94.7937532988665197]},"#ddff44":{"lch":[94.8692843830354491,99.9146373114600692,98.397314807396512],"luv":[94.8692843830354491,-14.5912004882884094,98.8434702820129729],"rgb":[0.866666666666666696,1,0.266666666666666663],"xyz":[0.666198391528136202,0.873091510417930428,0.188117947384421474],"hpluv":[98.397314807396512,912.633071017653265,94.8692843830354491],"hsluv":[98.397314807396512,99.9999999999866276,94.8692843830354491]},"#ddff55":{"lch":[94.9701440016210654,95.558111661139634,98.8668834316730738],"luv":[94.9701440016210654,-14.7292629682473848,94.4161083536863828],"rgb":[0.866666666666666696,1,0.333333333333333315],"xyz":[0.672160805567498132,0.875476476033675177,0.21951999465839539],"hpluv":[98.8668834316730738,891.031026100052486,94.9701440016210654],"hsluv":[98.8668834316730738,99.9999999999861586,94.9701440016210654]},"#ddff66":{"lch":[95.0981866754888,90.1320341607989235,99.5166683548804798],"luv":[95.0981866754888,-14.9019372161492765,88.8915960547979438],"rgb":[0.866666666666666696,1,0.4],"xyz":[0.679745801341006373,0.878510474343078496,0.259467639065539379],"hpluv":[99.5166683548804798,863.234823568518,95.0981866754888],"hsluv":[99.5166683548804798,99.9999999999856186,95.0981866754888]},"#ddff77":{"lch":[95.2550143462764396,83.6430269583913599,100.407261812829162],"luv":[95.2550143462764396,-15.1095945804084604,82.2669806810598487],"rgb":[0.866666666666666696,1,0.466666666666666674],"xyz":[0.689059814361208223,0.882236079551159347,0.308521440971937233],"hpluv":[100.407261812829162,828.556184265804632,95.2550143462764396],"hsluv":[100.407261812829162,99.9999999999855476,95.2550143462764396]},"#ddff88":{"lch":[95.4420158908659175,76.1281736437493,101.633961649281417],"luv":[95.4420158908659175,-15.3518949618923237,74.5641880758576434],"rgb":[0.866666666666666696,1,0.533333333333333326],"xyz":[0.700200231463152134,0.886692246391936889,0.367194304375509428],"hpluv":[101.633961649281417,786.17501332264635,95.4420158908659175],"hsluv":[101.633961649281417,99.9999999999848512,95.4420158908659175]},"#ddff99":{"lch":[95.6603925662724208,67.6570848719276086,103.355147801887099],"luv":[95.6603925662724208,-15.6278614338241617,65.8274341013866859],"rgb":[0.866666666666666696,1,0.6],"xyz":[0.713257195572224778,0.891915032035566102,0.435960982016627363],"hpluv":[103.355147801887099,735.074270676232231,95.6603925662724208],"hsluv":[103.355147801887099,99.9999999999844107,95.6603925662724208]},"#ddffaa":{"lch":[95.9111754000973775,58.338925144698,105.852499131344544],"luv":[95.9111754000973775,-15.935965587620279,56.1201852084333268],"rgb":[0.866666666666666696,1,0.66666666666666663],"xyz":[0.72831488853747739,0.897938109221667169,0.515264831633625908],"hpluv":[105.852499131344544,673.992295850936557,95.9111754000973775],"hsluv":[105.852499131344544,99.9999999999831886,95.9111754000973775]},"#ddffbb":{"lch":[96.1952377631310185,48.3433070572267596,109.672211431084662],"luv":[96.1952377631310185,-16.2742233848326485,45.5216980180866813],"rgb":[0.866666666666666696,1,0.733333333333333282],"xyz":[0.745452480644814,0.904793146064601927,0.60552281673226771],"hpluv":[109.672211431084662,601.502194015230771,96.1952377631310185],"hsluv":[109.672211431084662,99.9999999999821654,96.1952377631310185]},"#ddffcc":{"lch":[96.513305005727517,37.9644197829535202,115.996292551248089],"luv":[96.513305005727517,-16.6402982513575566,34.1232712904577653],"rgb":[0.866666666666666696,1,0.8],"xyz":[0.764744856088535707,0.912510096242090785,0.707129327402537866],"hpluv":[115.996292551248089,516.693096965009204,96.513305005727517],"hsluv":[115.996292551248089,99.9999999999804885,96.513305005727517]},"#ddffdd":{"lch":[96.8659623148576,27.841508205801528,127.715012949232161],"luv":[96.8659623148576,-17.0316066426751398,22.0243945284064],"rgb":[0.866666666666666696,1,0.866666666666666696],"xyz":[0.786263181639169306,0.921117426462344291,0.820459175302543864],"hpluv":[127.715012949232161,422.676993554754517,96.8659623148576],"hsluv":[127.715012949232161,99.9999999999786411,96.8659623148576]},"#ddffee":{"lch":[97.2536615310726802,19.7831433293950418,151.864226334417424],"luv":[97.2536615310726802,-17.4454209009420502,9.32898974060759478],"rgb":[0.866666666666666696,1,0.933333333333333348],"xyz":[0.810075361905344,0.930642298568814308,0.945869991371067087],"hpluv":[151.864226334417424,343.73229759561508,97.2536615310726802],"hsluv":[151.864226334417424,99.9999999999752,97.2536615310726802]},"#ddffff":{"lch":[97.6767274082888406,18.2904922799610645,192.177050630059568],"luv":[97.6767274082888406,-17.878964623675607,-3.85807359036494368],"rgb":[0.866666666666666696,1,1],"xyz":[0.836246410315175437,0.941110717932747,1.08370417966284882],"hpluv":[192.177050630059568,376.852754928906336,97.6767274082888406],"hsluv":[192.177050630059568,99.9999999999715072,97.6767274082888406]},"#668800":{"lch":[52.32310792684153,62.4331707825390509,105.73052795354684],"luv":[52.32310792684153,-16.9264656143939405,60.0948881001240167],"rgb":[0.4,0.533333333333333326,0],"xyz":[0.142831412088957943,0.204328465339529863,0.0319143663590854554],"hpluv":[105.73052795354684,151.412310196323318,52.32310792684153],"hsluv":[105.73052795354684,100.000000000002359,52.32310792684153]},"#668811":{"lch":[52.3681821172622222,60.6739112189649603,106.201766876928076],"luv":[52.3681821172622222,-16.9292783492562116,58.26425179456308],"rgb":[0.4,0.533333333333333326,0.0666666666666666657],"xyz":[0.143843077588595075,0.204733131539384716,0.0372424713238410851],"hpluv":[106.201766876928076,147.019120365759306,52.3681821172622222],"hsluv":[106.201766876928076,96.7617570127925859,52.3681821172622222]},"#668822":{"lch":[52.4515808002199,57.4815115963739842,107.134347730867304],"luv":[52.4515808002199,-16.9348150675510247,54.9302850350507796],"rgb":[0.4,0.533333333333333326,0.133333333333333331],"xyz":[0.145718435727072076,0.205483274794775544,0.0471193575198202846],"hpluv":[107.134347730867304,139.062145386582984,52.4515808002199],"hsluv":[107.134347730867304,90.8761910280814647,52.4515808002199]},"#668833":{"lch":[52.5884544541714121,52.4173031151438593,108.860637572968898],"luv":[52.5884544541714121,-16.9448041098426323,49.6028958786070291],"rgb":[0.4,0.533333333333333326,0.2],"xyz":[0.14880618645952981,0.206718375087758643,0.0633815113774314209],"hpluv":[108.860637572968898,126.480505252334424,52.5884544541714121],"hsluv":[108.860637572968898,81.504484256448066,52.5884544541714121]},"#668844":{"lch":[52.7851097594501937,45.5231929703366234,111.874794573011059],"luv":[52.7851097594501937,-16.9610118856818559,42.2455343678863215],"rgb":[0.4,0.533333333333333326,0.266666666666666663],"xyz":[0.153264181703748698,0.208501573185446237,0.0868602863303180595],"hpluv":[111.874794573011059,109.436101408621766,52.7851097594501937],"hsluv":[111.874794573011059,68.6312430201703734,52.7851097594501937]},"#668855":{"lch":[53.0463844713544859,37.0894540992544819,117.255878513522262],"luv":[53.0463844713544859,-16.9856764955971045,32.9714179156375593],"rgb":[0.4,0.533333333333333326,0.333333333333333315],"xyz":[0.15922659574311071,0.21088653880119107,0.118262333604291975],"hpluv":[117.255878513522262,88.7225425584692715,53.0463844713544859],"hsluv":[117.255878513522262,52.5261258187504225,53.0463844713544859]},"#668866":{"lch":[53.3759296841588906,27.8248149657310222,127.715012949236794],"luv":[53.3759296841588906,-17.0213948144824521,22.0111891193247189],"rgb":[0.4,0.533333333333333326,0.4],"xyz":[0.166811591516618868,0.213920537110594361,0.158209978011435937],"hpluv":[127.715012949236794,66.1494380276081415,53.3759296841588906],"hsluv":[127.715012949236794,33.681854155652033,53.3759296841588906]},"#668877":{"lch":[53.7763606180623839,19.6211535767711887,150.461713858693599],"luv":[53.7763606180623839,-17.0709226847205855,9.67332757511772101],"rgb":[0.4,0.533333333333333326,0.466666666666666674],"xyz":[0.176125604536820801,0.217646142318675184,0.207263779917833846],"hpluv":[150.461713858693599,46.2990901048939207,53.7763606180623839],"hsluv":[150.461713858693599,37.1484166060608132,53.7763606180623839]},"#668888":{"lch":[54.2493559855519436,17.5313913512660982,192.17705063006045],"luv":[54.2493559855519436,-17.1369431164247104,-3.69795393909545],"rgb":[0.4,0.533333333333333326,0.533333333333333326],"xyz":[0.187266021638764601,0.222102309159452782,0.265936643321406],"hpluv":[192.17705063006045,41.0072951616226788,54.2493559855519436],"hsluv":[192.17705063006045,40.8467805779917228,54.2493559855519436]},"#668899":{"lch":[54.7957384612029728,24.7393499057112685,225.882505108050964],"luv":[54.7957384612029728,-17.2218541013304964,-17.7607200042594577],"rgb":[0.4,0.533333333333333326,0.6],"xyz":[0.200322985747837329,0.22732509480308194,0.33470332096252392],"hpluv":[225.882505108050964,57.2902642666713859,54.7957384612029728],"hsluv":[225.882505108050964,44.6631352998217963,54.7957384612029728]},"#6688aa":{"lch":[55.4155508256813363,36.5699037808867828,241.717344836465486],"luv":[55.4155508256813363,-17.3276119763631087,-32.2042190673817643],"rgb":[0.4,0.533333333333333326,0.66666666666666663],"xyz":[0.215380678713089857,0.233348171989183034,0.414007170579522521],"hpluv":[241.717344836465486,83.7397171719788389,55.4155508256813363],"hsluv":[241.717344836465486,48.4952118884804193,55.4155508256813363]},"#6688bb":{"lch":[56.1081340603271457,49.918102431374308,249.531909378681803],"luv":[56.1081340603271457,-17.4556451944419777,-46.7666270025424495],"rgb":[0.4,0.533333333333333326,0.733333333333333282],"xyz":[0.232518270820426531,0.240203208832117793,0.504265155678164323],"hpluv":[249.531909378681803,112.8941838879785,56.1081340603271457],"hsluv":[249.531909378681803,52.2580147864780216,56.1081340603271457]},"#6688cc":{"lch":[56.8722093096567107,63.7230017864969795,253.960340799970709],"luv":[56.8722093096567107,-17.6068348542363715,-61.2423082770199372],"rgb":[0.4,0.533333333333333326,0.8],"xyz":[0.251810646264148286,0.247920159009606594,0.605871666348434479],"hpluv":[253.960340799970709,142.178999158492672,56.8722093096567107],"hsluv":[253.960340799970709,56.6402695601832349,56.8722093096567107]},"#6688dd":{"lch":[57.7059632125805564,77.5471512008463719,256.744147904563079],"luv":[57.7059632125805564,-17.7815469636593271,-75.4809727477468755],"rgb":[0.4,0.533333333333333326,0.866666666666666696],"xyz":[0.273328971814781774,0.256527489229860128,0.719201514248440477],"hpluv":[256.744147904563079,170.523595036459966,57.7059632125805564],"hsluv":[256.744147904563079,70.7529337108087475,57.7059632125805564]},"#6688ee":{"lch":[58.6071348177704721,91.1720868217648501,258.626369492504523],"luv":[58.6071348177704721,-17.979697586292076,-89.3816529839368599],"rgb":[0.4,0.533333333333333326,0.933333333333333348],"xyz":[0.297141152080956539,0.266052361336330145,0.8446123303169637],"hpluv":[258.626369492504523,197.40162879311913,58.6071348177704721],"hsluv":[258.626369492504523,85.1920367601911295,58.6071348177704721]},"#6688ff":{"lch":[59.57310174908622,104.481663139573541,259.967822360236937],"luv":[59.57310174908622,-18.2008336002305597,-102.884146439906075],"rgb":[0.4,0.533333333333333326,1],"xyz":[0.323312200490787904,0.276520780700262869,0.982446518608745434],"hpluv":[259.967822360236937,222.550815911907222,59.57310174908622],"hsluv":[259.967822360236937,99.9999999999987352,59.57310174908622]},"#669900":{"lch":[57.6618978033021961,71.9113437902946373,111.072092359847389],"luv":[57.6618978033021961,-25.8551729794803826,67.1025438856612624],"rgb":[0.4,0.6,0],"xyz":[0.168701012541425444,0.25606766624446553,0.0405375665099077104],"hpluv":[111.072092359847389,158.251486754186431,57.6618978033021961],"hsluv":[111.072092359847389,100.000000000002444,57.6618978033021961]},"#669911":{"lch":[57.7006802499588929,70.3894808316696867,111.521292839157113],"luv":[57.7006802499588929,-25.8221679479897865,65.4820177928093727],"rgb":[0.4,0.6,0.0666666666666666657],"xyz":[0.169712678041062576,0.256472332444320383,0.0458656714746633401],"hpluv":[111.521292839157113,154.798288730060023,57.7006802499588929],"hsluv":[111.521292839157113,97.4070268725327821,57.7006802499588929]},"#669922":{"lch":[57.7724648019637499,67.6202459496272326,112.394641072548438],"luv":[57.7724648019637499,-25.7622250908158499,62.5204400229094404],"rgb":[0.4,0.6,0.133333333333333331],"xyz":[0.171588036179539577,0.257222475699711184,0.0557425576706425396],"hpluv":[112.394641072548438,148.523500329687067,57.7724648019637499],"hsluv":[112.394641072548438,92.6757293451634183,57.7724648019637499]},"#669933":{"lch":[57.8903535973237524,63.206621217633213,113.958803391015238],"luv":[57.8903535973237524,-25.6669247559924365,57.7605915769530824],"rgb":[0.4,0.6,0.2],"xyz":[0.174675786911997311,0.258457575992694311,0.0720047115282536898],"hpluv":[113.958803391015238,138.546544821103367,57.8903535973237524],"hsluv":[113.958803391015238,85.092275495173979,57.8903535973237524]},"#669944":{"lch":[58.0598969225296457,57.1535921702901888,116.538768682419729],"luv":[58.0598969225296457,-25.5364113217858204,51.131446241744662],"rgb":[0.4,0.6,0.266666666666666663],"xyz":[0.179133782156216198,0.260240774090381877,0.0954834864811403283],"hpluv":[116.538768682419729,124.912700512594284,58.0598969225296457],"hsluv":[116.538768682419729,74.5746503624431796,58.0598969225296457]},"#669955":{"lch":[58.2854489010818355,49.6561447471622515,120.7300937454592],"luv":[58.2854489010818355,-25.3740154630121886,42.6836274282534163],"rgb":[0.4,0.6,0.333333333333333315],"xyz":[0.185096196195578211,0.262625739706126737,0.126885533755114244],"hpluv":[120.7300937454592,108.106592630407334,58.2854489010818355],"hsluv":[120.7300937454592,61.2495338993745833,58.2854489010818355]},"#669966":{"lch":[58.5704165792398754,41.1710358166226,127.715012949238272],"luv":[58.5704165792398754,-25.1857364161826034,32.5688942303571949],"rgb":[0.4,0.6,0.4],"xyz":[0.192681191969086368,0.265659738015530056,0.166833178162258178],"hpluv":[127.715012949238272,89.1975256314243552,58.5704165792398754],"hsluv":[127.715012949238272,45.4174387408809324,58.5704165792398754]},"#669977":{"lch":[58.9173908027988489,32.643461159974926,139.926834832055846],"luv":[58.9173908027988489,-24.9795271581232114,21.0147276798767138],"rgb":[0.4,0.6,0.466666666666666674],"xyz":[0.201995204989288302,0.269385343223610851,0.215886980068656087],"hpluv":[139.926834832055846,70.3059403526018514,58.9173908027988489],"hsluv":[139.926834832055846,47.7876005322921813,58.9173908027988489]},"#669988":{"lch":[59.328227692638464,26.116847909850911,161.480821243886396],"luv":[59.328227692638464,-24.7644493579715288,8.29528738156475498],"rgb":[0.4,0.6,0.533333333333333326],"xyz":[0.213135622091232102,0.273841510064388449,0.274559843472228282],"hpluv":[161.480821243886396,55.8597144892801083,59.328227692638464],"hsluv":[161.480821243886396,50.3655359274122105,59.328227692638464]},"#669999":{"lch":[59.8041090330486043,25.1148951486962346,192.17705063006062],"luv":[59.8041090330486043,-24.5498215694726056,-5.29756729424584893],"rgb":[0.4,0.6,0.6],"xyz":[0.226192586200304829,0.279064295708017607,0.343326521113346161],"hpluv":[192.17705063006062,53.2892577697712042,59.8041090330486043],"hsluv":[192.17705063006062,53.0806679813151447,59.8041090330486043]},"#6699aa":{"lch":[60.3455948386344119,31.1785657150768039,218.6653689057203],"luv":[60.3455948386344119,-24.3444790905776323,-19.4794583563761137],"rgb":[0.4,0.6,0.66666666666666663],"xyz":[0.241250279165557358,0.285087372894118729,0.422630370730344762],"hpluv":[218.6653689057203,65.5616517705923911,60.3455948386344119],"hsluv":[218.6653689057203,55.8649153078387357,60.3455948386344119]},"#6699bb":{"lch":[60.9526745558420231,41.7013653705125407,234.60099737489486],"luv":[60.9526745558420231,-24.1562241025528444,-33.9923625373734168],"rgb":[0.4,0.6,0.733333333333333282],"xyz":[0.258387871272894032,0.291942409737053488,0.512888355828986509],"hpluv":[234.60099737489486,86.8154127469588,60.9526745558420231],"hsluv":[234.60099737489486,58.6571249967150123,60.9526745558420231]},"#6699cc":{"lch":[61.6248198105828493,54.2138016672169485,243.734276496040906],"luv":[61.6248198105828493,-23.9914938552849968,-48.6162988492970101],"rgb":[0.4,0.6,0.8],"xyz":[0.277680246716615731,0.299659359914542289,0.614494866499256664],"hpluv":[243.734276496040906,111.633239234030881,61.6248198105828493],"hsluv":[243.734276496040906,61.4057918386087067,61.6248198105828493]},"#6699dd":{"lch":[62.361039595623,67.5283063616342361,249.312993820438976],"luv":[62.361039595623,-23.8552311607989473,-63.1743627299522288],"rgb":[0.4,0.6,0.866666666666666696],"xyz":[0.29919857226724933,0.308266690134795796,0.727824714399262662],"hpluv":[249.312993820438976,137.407942777337979,62.361039595623],"hsluv":[249.312993820438976,66.62550454031512,62.361039595623]},"#6699ee":{"lch":[63.1599376048740453,81.0888167917790526,252.968345076420633],"luv":[63.1599376048740453,-23.7509145884581336,-77.5325110189427278],"rgb":[0.4,0.6,0.933333333333333348],"xyz":[0.323010752533424039,0.317791562241265813,0.853235530467785885],"hpluv":[252.968345076420633,162.914071535813093,63.1599376048740453],"hsluv":[252.968345076420633,83.0301883133999183,63.1599376048740453]},"#6699ff":{"lch":[64.0197707514621186,94.6074384193794771,255.504450424431923],"luv":[64.0197707514621186,-23.6806962224400728,-91.5958079318982499],"rgb":[0.4,0.6,1],"xyz":[0.34918180094325546,0.328259981605198536,0.99106971875956773],"hpluv":[255.504450424431923,187.521252715437782,64.0197707514621186],"hsluv":[255.504450424431923,99.9999999999985079,64.0197707514621186]},"#550000":{"lch":[15.1243819173422267,50.8637728648741643,12.1770506300617765],"luv":[15.1243819173422267,49.7193613905117289,10.7288626130266547],"rgb":[0.333333333333333315,0,0],"xyz":[0.0374622858816120868,0.019316491157706641,0.00175604465070052949],"hpluv":[12.1770506300617765,426.746789183125202,15.1243819173422267],"hsluv":[12.1770506300617765,100.000000000002203,15.1243819173422267]},"#550011":{"lch":[15.3402258633588957,47.2707050856887108,7.4875089370669734],"luv":[15.3402258633588957,46.8676416739534929,6.15984766208174239],"rgb":[0.333333333333333315,0,0.0666666666666666657],"xyz":[0.0384739513812492051,0.0197211573575614939,0.00708414961545616138],"hpluv":[7.4875089370669734,391.020613457768548,15.3402258633588957],"hsluv":[7.4875089370669734,99.9999999999966889,15.3402258633588957]},"#550022":{"lch":[15.7326592199860933,42.4312907985821823,358.411234527054887],"luv":[15.7326592199860933,42.4149789665186745,-1.17643448760390168],"rgb":[0.333333333333333315,0,0.133333333333333331],"xyz":[0.0403493095197262272,0.0204713006129523117,0.0169610358114353557],"hpluv":[358.411234527054887,342.234221563623748,15.7326592199860933],"hsluv":[358.411234527054887,99.9999999999971578,15.7326592199860933]},"#550033":{"lch":[16.358416636328208,38.360101220613565,343.406058671947278],"luv":[16.358416636328208,36.7625096026365128,-10.9551473459637485],"rgb":[0.333333333333333315,0,0.2],"xyz":[0.0434370602521839677,0.0217064009059354281,0.0332231896690465],"hpluv":[343.406058671947278,297.562230749163234,16.358416636328208],"hsluv":[343.406058671947278,99.9999999999977547,16.358416636328208]},"#550044":{"lch":[17.2212923868602061,37.8614359764106112,324.728975934647224],"luv":[17.2212923868602061,30.9112016871952413,-21.8628896637516235],"rgb":[0.333333333333333315,0,0.266666666666666663],"xyz":[0.0478950554964028483,0.0234895990036230046,0.0567019646219331375],"hpluv":[324.728975934647224,278.978456842737614,17.2212923868602061],"hsluv":[324.728975934647224,99.9999999999983089,17.2212923868602061]},"#550055":{"lch":[18.3096014215038,41.7063030886972754,307.715012949243544],"luv":[18.3096014215038,25.5131777875110508,-32.9923245090298636],"rgb":[0.333333333333333315,0,0.333333333333333315],"xyz":[0.0538574695357648403,0.025874564619367834,0.0881040118959070528],"hpluv":[307.715012949243544,289.042783730483393,18.3096014215038],"hsluv":[307.715012949243544,99.9999999999988,18.3096014215038]},"#550066":{"lch":[19.6013792550641099,48.6148100748190828,295.355623011865077],"luv":[19.6013792550641099,20.818580180871372,-43.9316113733990079],"rgb":[0.333333333333333315,0,0.4],"xyz":[0.0614424653092730116,0.028908562928771149,0.128051656303051015],"hpluv":[295.355623011865077,314.717786655224245,19.6013792550641099],"hsluv":[295.355623011865077,99.9999999999992468,19.6013792550641099]},"#550077":{"lch":[21.069395574911745,57.1127554515679421,287.139622223683091],"luv":[21.069395574911745,16.83119884103942,-54.5763463493480572],"rgb":[0.333333333333333315,0,0.466666666666666674],"xyz":[0.0707564783294749311,0.0326341681368519654,0.177105458209448924],"hpluv":[287.139622223683091,343.969838941793114,21.069395574911745],"hsluv":[287.139622223683091,99.9999999999995737,21.069395574911745]},"#550088":{"lch":[22.6852054601189934,66.3294491167530822,281.703433904835379],"luv":[22.6852054601189934,13.454662288017035,-64.9505033302079084],"rgb":[0.333333333333333315,0,0.533333333333333326],"xyz":[0.0818968954314187592,0.0370903349776295563,0.235778321613021091],"hpluv":[281.703433904835379,371.024851449370942,22.6852054601189934],"hsluv":[281.703433904835379,99.9999999999998721,22.6852054601189934]},"#550099":{"lch":[24.4218644362266417,75.8503102235033424,278.01254475278904],"luv":[24.4218644362266417,10.5727682252252198,-75.1098271403773339],"rgb":[0.333333333333333315,0,0.6],"xyz":[0.0949538595404914726,0.0423131206212587208,0.304544999254138971],"hpluv":[278.01254475278904,394.110378481836165,24.4218644362266417],"hsluv":[278.01254475278904,100.000000000000071,24.4218644362266417]},"#5500aa":{"lch":[26.2553935553790794,85.4876195151354,275.424483319872081],"luv":[26.2553935553790794,8.0814629277942,-85.1047768771603614],"rgb":[0.333333333333333315,0,0.66666666666666663],"xyz":[0.110011552505744015,0.0483361978073598153,0.383848848871137571],"hpluv":[275.424483319872081,413.165469396605374,26.2553935553790794],"hsluv":[275.424483319872081,100.00000000000027,26.2553935553790794]},"#5500bb":{"lch":[28.1653177219846,95.1546470205467756,273.553022331801344],"luv":[28.1653177219846,5.89694295360029841,-94.9717479748942708],"rgb":[0.333333333333333315,0,0.733333333333333282],"xyz":[0.127149144613080661,0.0551912346502945739,0.474106833969779318],"hpluv":[273.553022331801344,428.701175528050442,28.1653177219846],"hsluv":[273.553022331801344,100.000000000000355,28.1653177219846]},"#5500cc":{"lch":[30.1346298593711452,104.80902699908826,272.162345307959299],"luv":[30.1346298593711452,3.95455850560158062,-104.734395532324456],"rgb":[0.333333333333333315,0,0.8],"xyz":[0.146441520056802388,0.0629081848277833755,0.575713344640049529],"hpluv":[272.162345307959299,441.338845171454864,30.1346298593711452],"hsluv":[272.162345307959299,100.000000000000441,30.1346298593711452]},"#5500dd":{"lch":[32.1494591091083208,114.428501102308275,271.104225820707256],"luv":[32.1494591091083208,2.20517261265448461,-114.407250986418532],"rgb":[0.333333333333333315,0,0.866666666666666696],"xyz":[0.167959845607435931,0.0715155150480369095,0.689043192540055527],"hpluv":[271.104225820707256,451.647764573950099,32.1494591091083208],"hsluv":[271.104225820707256,100.000000000000483,32.1494591091083208]},"#5500ee":{"lch":[34.1986254005705774,124.000502171291956,270.282536199645165],"luv":[34.1986254005705774,0.611467178704377612,-123.998994538754019],"rgb":[0.333333333333333315,0,0.933333333333333348],"xyz":[0.191772025873610696,0.0810403871545069404,0.81445400860857875],"hpluv":[270.282536199645165,460.101999214721616,34.1986254005705774],"hsluv":[270.282536199645165,100.000000000000597,34.1986254005705774]},"#5500ff":{"lch":[36.2731838611955055,133.517545782829444,269.6330586770423],"luv":[36.2731838611955055,-0.855085145745556,-133.514807647929],"rgb":[0.333333333333333315,0,1],"xyz":[0.217943074283442062,0.0915088065184396504,0.952288196900360595],"hpluv":[269.6330586770423,467.080772865482345,36.2731838611955055],"hsluv":[269.6330586770423,100.000000000000668,36.2731838611955055]},"#551100":{"lch":[17.1436512350983392,46.6418802884309827,16.9386517648024579],"luv":[17.1436512350983392,44.618427652544149,13.5889996193616174],"rgb":[0.333333333333333315,0.0666666666666666657,0],"xyz":[0.0394666861425404941,0.0233252916795635146,0.00242417807100998037],"hpluv":[16.9386517648024579,345.232802292268,17.1436512350983392],"hsluv":[16.9386517648024579,100.000000000002245,17.1436512350983392]},"#551111":{"lch":[17.3342210988239742,43.3325537190722372,12.1770506300618173],"luv":[17.3342210988239742,42.3575912084996133,9.14027783111198566],"rgb":[0.333333333333333315,0.0666666666666666657,0.0666666666666666657],"xyz":[0.0404783516421776124,0.0237299578794183674,0.0077522830357656114],"hpluv":[12.1770506300618173,317.211759513802576,17.3342210988239742],"hsluv":[12.1770506300618173,74.3325474389658751,17.3342210988239742]},"#551122":{"lch":[17.681833534927847,38.7809423842287515,2.75476418742331486],"luv":[17.681833534927847,38.7361268795629599,1.86385798259144919],"rgb":[0.333333333333333315,0.0666666666666666657,0.133333333333333331],"xyz":[0.0423537097806546345,0.0244801011348091888,0.0176291692317448075],"hpluv":[2.75476418742331486,278.311009894887945,17.681833534927847],"hsluv":[2.75476418742331486,76.7057995287142234,17.681833534927847]},"#551133":{"lch":[18.2390179286851222,34.9181665436168771,346.660743506282301],"luv":[18.2390179286851222,33.9761101882180157,-8.05619582966011727],"rgb":[0.333333333333333315,0.0666666666666666657,0.2],"xyz":[0.045441460513112375,0.0257152014277923,0.0338913230893559542],"hpluv":[346.660743506282301,242.9345634355779,18.2390179286851222],"hsluv":[346.660743506282301,79.7834922439867285,18.2390179286851222]},"#551144":{"lch":[19.0128230091186055,34.7408929356053093,326.164658676814156],"luv":[19.0128230091186055,28.8572161490225767,-19.3440098762325228],"rgb":[0.333333333333333315,0.0666666666666666657,0.266666666666666663],"xyz":[0.0498994557573312555,0.0274983995254798746,0.0573700980422425927],"hpluv":[326.164658676814156,231.864199750183133,19.0128230091186055],"hsluv":[326.164658676814156,83.0221027238197706,19.0128230091186055]},"#551155":{"lch":[19.9971255718025702,39.1813209297373,307.715012949243771],"luv":[19.9971255718025702,23.9685594933690105,-30.9949038651824331],"rgb":[0.333333333333333315,0.0666666666666666657,0.333333333333333315],"xyz":[0.0558618697966932476,0.0298833651412247076,0.0887721453162165],"hpluv":[307.715012949243771,248.628452083429778,19.9971255718025702],"hsluv":[307.715012949243771,86.0178721207098391,19.9971255718025702]},"#551166":{"lch":[21.1763147828962417,46.7756191769153702,294.771362117319313],"luv":[21.1763147828962417,19.5989049109657927,-42.4716549674572263],"rgb":[0.333333333333333315,0.0666666666666666657,0.4],"xyz":[0.0634468655702014189,0.0329173634506280191,0.128719789723360456],"hpluv":[294.771362117319313,280.29057047770084,21.1763147828962417],"hsluv":[294.771362117319313,88.5810746573468464,21.1763147828962417]},"#551177":{"lch":[22.529041607596703,55.8726050260016436,286.442196236272423],"luv":[22.529041607596703,15.8146223861076685,-53.5877384405836494],"rgb":[0.333333333333333315,0.0666666666666666657,0.466666666666666674],"xyz":[0.0727608785904033384,0.0366429686587088355,0.177773591629758365],"hpluv":[286.442196236272423,314.699121073082495,22.529041607596703],"hsluv":[286.442196236272423,90.6792509168865166,22.529041607596703]},"#551188":{"lch":[24.0315326783493077,65.5523157673088548,281.05474383939486],"luv":[24.0315326783493077,12.5694476912001676,-64.3359548557037613],"rgb":[0.333333333333333315,0.0666666666666666657,0.533333333333333326],"xyz":[0.0839012956923471664,0.0410991354994864333,0.236446455033330533],"hpluv":[281.05474383939486,346.135302225987516,24.0315326783493077],"hsluv":[281.05474383939486,92.3586421581981654,24.0315326783493077]},"#551199":{"lch":[25.6600874124784752,75.409923320082811,277.450872837297311],"luv":[25.6600874124784752,9.77886091464338847,-74.7731931533810439],"rgb":[0.333333333333333315,0.0666666666666666657,0.6],"xyz":[0.0969582598014198799,0.0463219211431155908,0.30521313267444844],"hpluv":[277.450872837297311,372.914863977489176,25.6600874124784752],"hsluv":[277.450872837297311,93.6909530677945099,25.6600874124784752]},"#5511aa":{"lch":[27.3926712394503795,85.2829889480110097,274.948569823839534],"luv":[27.3926712394503795,7.3566383451248436,-84.9650991652784882],"rgb":[0.333333333333333315,0.0666666666666666657,0.66666666666666663],"xyz":[0.112015952766672422,0.0523449983292166923,0.38451698229144704],"hpluv":[274.948569823839534,395.063906765864772,27.3926712394503795],"hsluv":[274.948569823839534,94.7471527755802185,27.3926712394503795]},"#5511bb":{"lch":[29.2097366740877575,95.1102004757886732,273.151254165932869],"luv":[29.2097366740877575,5.22840137893499524,-94.9663838079847125],"rgb":[0.333333333333333315,0.0666666666666666657,0.733333333333333282],"xyz":[0.129153544874009069,0.0592000351721514509,0.474774967390088787],"hpluv":[273.151254165932869,413.179515249173164,29.2097366740877575],"hsluv":[273.151254165932869,95.5878244802283,29.2097366740877575]},"#5511cc":{"lch":[31.0944914716528729,104.870266468627406,271.822015936655589],"luv":[31.0944914716528729,3.33433100744475697,-104.81724584215],"rgb":[0.333333333333333315,0.0666666666666666657,0.8],"xyz":[0.148445920317730795,0.0669169853496402456,0.576381478060359],"hpluv":[271.822015936655589,427.964986778194486,31.0944914716528729],"hsluv":[271.822015936655589,96.2613872132177733,31.0944914716528729]},"#5511dd":{"lch":[33.0328257175950526,114.557035031638449,270.814150293263936],"luv":[33.0328257175950526,1.62775523553093748,-114.545469959022128],"rgb":[0.333333333333333315,0.0666666666666666657,0.866666666666666696],"xyz":[0.169964245868364339,0.0755243155698937796,0.689711325960365],"hpluv":[270.814150293263936,440.063516862193467,33.0328257175950526],"hsluv":[270.814150293263936,96.8053522828386832,33.0328257175950526]},"#5511ee":{"lch":[35.0130604571318926,124.169729525043778,270.033521846843314],"luv":[35.0130604571318926,0.0726475571782520396,-124.169708273213573],"rgb":[0.333333333333333315,0.0666666666666666657,0.933333333333333348],"xyz":[0.193776426134539104,0.0850491876763638244,0.815122142028888219],"hpluv":[270.033521846843314,450.012925469310176,35.0130604571318926],"hsluv":[270.033521846843314,97.2483877085506663,35.0130604571318926]},"#5511ff":{"lch":[37.0256255288684244,133.709282746677673,269.417732433602225],"luv":[37.0256255288684244,-1.35879534673770408,-133.702378317802101],"rgb":[0.333333333333333315,0.0666666666666666657,1],"xyz":[0.219947474544370469,0.0955176070402965205,0.952956330320670064],"hpluv":[269.417732433602225,458.245787382607887,37.0256255288684244],"hsluv":[269.417732433602225,99.999999999999531,37.0256255288684244]},"#552200":{"lch":[20.34436993371488,40.5799496107340403,26.5709502396200712],"luv":[20.34436993371488,36.2939416756913289,18.1516420207988389],"rgb":[0.333333333333333315,0.133333333333333331,0],"xyz":[0.0431823098773084293,0.0307565391490994891,0.0036627193159325909],"hpluv":[26.5709502396200712,253.10841584108,20.34436993371488],"hsluv":[26.5709502396200712,100.000000000002359,20.34436993371488]},"#552211":{"lch":[20.5030711832139332,37.4561676233306144,21.8926823519782197],"luv":[20.5030711832139332,34.7549742653004543,13.9662542166947503],"rgb":[0.333333333333333315,0.133333333333333331,0.0666666666666666657],"xyz":[0.0441939753769455546,0.031161205348954342,0.00899082428068822236],"hpluv":[21.8926823519782197,231.816181029165051,20.5030711832139332],"hsluv":[21.8926823519782197,79.998991439989,20.5030711832139332]},"#552222":{"lch":[20.7936643332181177,32.9512801850020054,12.1770506300619488],"luv":[20.7936643332181177,32.2098915499349658,6.95052171940595098],"rgb":[0.333333333333333315,0.133333333333333331,0.133333333333333331],"xyz":[0.0460693335154225697,0.0319113486043451633,0.0188677104766674167],"hpluv":[12.1770506300619488,201.08542320769223,20.7936643332181177],"hsluv":[12.1770506300619488,47.1205474310924046,20.7936643332181177]},"#552233":{"lch":[21.2623572347893699,28.8586800011052311,354.1745907436],"luv":[21.2623572347893699,28.7096478626609084,-2.92908364650122532],"rgb":[0.333333333333333315,0.133333333333333331,0.2],"xyz":[0.0491570842478803102,0.0331464488973282762,0.0351298643342785599],"hpluv":[354.1745907436,172.228245917758017,21.2623572347893699],"hsluv":[354.1745907436,52.8325366869496236,21.2623572347893699]},"#552244":{"lch":[21.9189283311679688,28.7014290952845421,329.54904741067952],"luv":[21.9189283311679688,24.7424491748557642,-14.5459011732283336],"rgb":[0.333333333333333315,0.133333333333333331,0.266666666666666663],"xyz":[0.0536150794920991908,0.0349296469950158492,0.0586086392871652],"hpluv":[329.54904741067952,166.158870546468904,21.9189283311679688],"hsluv":[329.54904741067952,59.1960886419386,21.9189283311679688]},"#552255":{"lch":[22.7630226511172538,33.9275210993755394,307.715012949244226],"luv":[22.7630226511172538,20.7546297224435,-26.8388157904337739],"rgb":[0.333333333333333315,0.133333333333333331,0.333333333333333315],"xyz":[0.0595774935314611828,0.0373146126107606821,0.0900106865611391138],"hpluv":[307.715012949244226,189.13048019699076,22.7630226511172538],"hsluv":[307.715012949244226,65.4333859354686211,22.7630226511172538]},"#552266":{"lch":[23.7863579144178132,42.6539605518829816,293.531429927677038],"luv":[23.7863579144178132,17.0296819355444384,-39.1069083914310625],"rgb":[0.333333333333333315,0.133333333333333331,0.4],"xyz":[0.0671624893049693611,0.0403486109201639936,0.129958330968283076],"hpluv":[293.531429927677038,227.546804006344104,23.7863579144178132],"hsluv":[293.531429927677038,71.0608597854246,23.7863579144178132]},"#552277":{"lch":[24.975052770659552,52.81712200152176,285.022920758889427],"luv":[24.975052770659552,13.6904851349353347,-51.0119495147337858],"rgb":[0.333333333333333315,0.133333333333333331,0.466666666666666674],"xyz":[0.0764765023251712805,0.04407421612824481,0.179012132874680985],"hpluv":[285.022920758889427,268.353735360872861,24.975052770659552],"hsluv":[285.022920758889427,75.8822745115455604,24.975052770659552]},"#552288":{"lch":[26.3119033569515395,63.3751525334233818,279.769698022236867],"luv":[26.3119033569515395,10.754023379801918,-62.4560720809542573],"rgb":[0.333333333333333315,0.133333333333333331,0.533333333333333326],"xyz":[0.0876169194271151086,0.0485303829690224,0.237684996278253152],"hpluv":[279.769698022236867,305.637106002952862,26.3119033569515395],"hsluv":[279.769698022236867,79.8897505498065357,26.3119033569515395]},"#552299":{"lch":[27.7783456471686065,73.9162973481211,276.357347681517297],"luv":[27.7783456471686065,8.18468635974612368,-73.4617582341209783],"rgb":[0.333333333333333315,0.133333333333333331,0.6],"xyz":[0.100673883536187808,0.0537531686126515654,0.306451673919371059],"hpluv":[276.357347681517297,337.654974874305083,27.7783456471686065],"hsluv":[276.357347681517297,83.1678045973750102,27.7783456471686065]},"#5522aa":{"lch":[29.3559430420228864,84.3037876124251824,274.032676149589577],"luv":[29.3559430420228864,5.92869585954675404,-84.0950603258353482],"rgb":[0.333333333333333315,0.133333333333333331,0.66666666666666663],"xyz":[0.115731576501440364,0.0597762457987526669,0.38575552353636966],"hpluv":[274.032676149589577,364.410076381253305,29.3559430420228864],"hsluv":[274.032676149589577,85.8312635149374898,29.3559430420228864]},"#5522bb":{"lch":[31.0273723986379082,94.5090555446981568,272.384235640696716],"luv":[31.0273723986379082,3.93164792491766635,-94.4272403734501324],"rgb":[0.333333333333333315,0.133333333333333331,0.733333333333333282],"xyz":[0.132869168608777,0.0666312826416874254,0.476013508635011406],"hpluv":[272.384235640696716,386.516244750379769,31.0273723986379082],"hsluv":[272.384235640696716,87.9935415010939437,31.0273723986379082]},"#5522cc":{"lch":[32.7769760620793207,104.54175742802569,271.176024316906762],"luv":[32.7769760620793207,2.1456208028374788,-104.519736688869415],"rgb":[0.333333333333333315,0.133333333333333331,0.8],"xyz":[0.152161544052498737,0.0743482328191762271,0.577620019305281507],"hpluv":[271.176024316906762,404.725193356887132,32.7769760620793207],"hsluv":[271.176024316906762,89.7538092894248507,32.7769760620793207]},"#5522dd":{"lch":[34.5909880118612847,114.421842397276407,270.265889656874094],"luv":[34.5909880118612847,0.530989812409553341,-114.420610326139567],"rgb":[0.333333333333333315,0.133333333333333331,0.866666666666666696],"xyz":[0.173679869603132281,0.0829555630394297611,0.690949867205287505],"hpluv":[270.265889656874094,419.744772471420788,34.5909880118612847],"hsluv":[270.265889656874094,91.1938440016712519,34.5909880118612847]},"#5522ee":{"lch":[36.4575428526747132,124.16924767107497,269.564389707514863],"luv":[36.4575428526747132,-0.94402906580191559,-124.165659005715739],"rgb":[0.333333333333333315,0.133333333333333331,0.933333333333333348],"xyz":[0.197492049869307018,0.092480435145899792,0.816360683273810728],"hpluv":[269.564389707514863,432.181309790662738,36.4575428526747132],"hsluv":[269.564389707514863,92.960018101107309,36.4575428526747132]},"#5522ff":{"lch":[38.3665568136218695,133.800754994484379,269.013084090219763],"luv":[38.3665568136218695,-2.30459494965050871,-133.780906258001124],"rgb":[0.333333333333333315,0.133333333333333331,1],"xyz":[0.223663098279138411,0.102948854509832488,0.954194871565592573],"hpluv":[269.013084090219763,442.532391911887146,38.3665568136218695],"hsluv":[269.013084090219763,99.9999999999994458,38.3665568136218695]},"#ccaa00":{"lch":[70.5858735612972623,80.4904122142546186,67.9906634155396],"luv":[70.5858735612972623,30.1643998932495876,74.6244962294604335],"rgb":[0.8,0.66666666666666663,0],"xyz":[0.392753797737474875,0.415879162748823417,0.059586129772359088],"hpluv":[67.9906634155396,144.699051457387782,70.5858735612972623],"hsluv":[67.9906634155396,100.000000000002373,70.5858735612972623]},"#ccaa11":{"lch":[70.6139482370970342,79.3332745728624786,67.7767588069511078],"luv":[70.6139482370970342,30.0051392857341135,73.4401802210267078],"rgb":[0.8,0.66666666666666663,0.0666666666666666657],"xyz":[0.393765463237112,0.416283828948678269,0.0649142347371147177],"hpluv":[67.7767588069511078,142.56214209062793,70.6139482370970342],"hsluv":[67.7767588069511078,98.4234445622979308,70.6139482370970342]},"#ccaa22":{"lch":[70.6659431154106,77.2098224605726529,67.3668792611096166],"luv":[70.6659431154106,29.7125737342128318,71.263733037795],"rgb":[0.8,0.66666666666666663,0.133333333333333331],"xyz":[0.395640821375589036,0.41703397220406907,0.0747911209330939103],"hpluv":[67.3668792611096166,138.644204697820271,70.6659431154106],"hsluv":[67.3668792611096166,95.5289103121581746,70.6659431154106]},"#ccaa33":{"lch":[70.7514162745237911,73.7740913884545,66.6515854645136159],"luv":[70.7514162745237911,29.2382542862800783,67.7328653349528622],"rgb":[0.8,0.66666666666666663,0.2],"xyz":[0.398728572108046742,0.418269072497052197,0.0910532747907050605],"hpluv":[66.6515854645136159,132.314688307412609,70.7514162745237911],"hsluv":[66.6515854645136159,90.8407422295077822,70.7514162745237911]},"#ccaa44":{"lch":[70.8745233475107597,68.9449101039795096,65.5199261400916271],"luv":[70.8745233475107597,28.5691680681362534,62.74713750555],"rgb":[0.8,0.66666666666666663,0.266666666666666663],"xyz":[0.403186567352265657,0.420052270594739763,0.114532049743591699],"hpluv":[65.5199261400916271,123.438713002021714,70.8745233475107597],"hsluv":[65.5199261400916271,84.237349466135143,70.8745233475107597]},"#ccaa55":{"lch":[71.0386313772099,62.7256766939065713,63.7915144306433959],"luv":[71.0386313772099,27.7020883768036121,56.2770363138665672],"rgb":[0.8,0.66666666666666663,0.333333333333333315],"xyz":[0.409148981391627642,0.422437236210484623,0.1459340970175656],"hpluv":[63.7915144306433959,112.044384698186093,71.0386313772099],"hsluv":[63.7915144306433959,75.6976413431368229,71.0386313772099]},"#ccaa66":{"lch":[71.2465086991263661,55.2096541299921952,61.1466680322388356],"luv":[71.2465086991263661,26.6424757380098143,48.3558103613508123],"rgb":[0.8,0.66666666666666663,0.4],"xyz":[0.416733977165135827,0.425471234519887942,0.185881741424709562],"hpluv":[61.1466680322388356,98.331070119416168,71.2465086991263661],"hsluv":[61.1466680322388356,65.2901205751262,71.2465086991263661]},"#ccaa77":{"lch":[71.5004247203994794,46.6032511345785068,56.9687004258729388],"luv":[71.5004247203994794,25.4032971934547263,39.0709035986321638],"rgb":[0.8,0.66666666666666663,0.466666666666666674],"xyz":[0.426047990185337733,0.429196839727968737,0.234935543331107471],"hpluv":[56.9687004258729388,82.707885898124573,71.5004247203994794],"hsluv":[56.9687004258729388,53.160756563752912,71.5004247203994794]},"#ccaa88":{"lch":[71.8022091544958556,37.3024926838847648,49.9478623796008847],"luv":[71.8022091544958556,24.003572993357885,28.5535364531929083],"rgb":[0.8,0.66666666666666663,0.533333333333333326],"xyz":[0.437188407287281533,0.433653006568746335,0.293608406734679639],"hpluv":[49.9478623796008847,65.9233659814751149,71.8022091544958556],"hsluv":[49.9478623796008847,39.5178130280575743,71.8022091544958556]},"#ccaa99":{"lch":[72.1532912119235874,28.1535194843350283,37.0598425499876214],"luv":[72.1532912119235874,22.4666918036374241,16.9666855559712388],"rgb":[0.8,0.66666666666666663,0.6],"xyz":[0.450245371396354233,0.438875792212375493,0.362375084375797574],"hpluv":[37.0598425499876214,49.5126161932360702,72.1532912119235874],"hsluv":[37.0598425499876214,26.6696495224772221,72.1532912119235874]},"#ccaaaa":{"lch":[72.5547286434336,21.297823518763618,12.177050630063027],"luv":[72.5547286434336,20.8186322940273918,4.49241984263274841],"rgb":[0.8,0.66666666666666663,0.66666666666666663],"xyz":[0.465303064361606789,0.444898869398476615,0.441678933992796174],"hpluv":[12.177050630063027,37.2485034287350203,72.5547286434336],"hsluv":[12.177050630063027,27.9047031904792959,72.5547286434336]},"#ccaabb":{"lch":[73.0072318845295,20.9674028584132515,335.544386587188285],"luv":[73.0072318845295,19.0862548050106788,-8.68025691700441193],"rgb":[0.8,0.66666666666666663,0.733333333333333282],"xyz":[0.482440656468943463,0.451753906241411374,0.531936919091437921],"hpluv":[335.544386587188285,36.4433325399841053,73.0072318845295],"hsluv":[335.544386587188285,29.1493731958161852,73.0072318845295]},"#ccaacc":{"lch":[73.5111862218870442,28.2733952257813925,307.715012949249683],"luv":[73.5111862218870442,17.2958067637387,-22.366044486780595],"rgb":[0.8,0.66666666666666663,0.8],"xyz":[0.501733031912665162,0.459470856418900175,0.633543429761708077],"hpluv":[307.715012949249683,48.8049486057506,73.5111862218870442],"hsluv":[307.715012949249683,30.3693282248248444,73.5111862218870442]},"#ccaadd":{"lch":[74.0666736076556,39.5441054640130645,293.032470315564865],"luv":[74.0666736076556,15.4717391639018693,-36.3917787995197202],"rgb":[0.8,0.66666666666666663,0.866666666666666696],"xyz":[0.523251357463298761,0.468078186639153682,0.746873277661714075],"hpluv":[293.032470315564865,67.7482749884916871,74.0666736076556],"hsluv":[293.032470315564865,52.1368854824616719,74.0666736076556]},"#ccaaee":{"lch":[74.6734949675833093,52.4074038625381462,285.081329657377239],"luv":[74.6734949675833093,13.6358764846886817,-50.6023601436286],"rgb":[0.8,0.66666666666666663,0.933333333333333348],"xyz":[0.54706353772947347,0.477603058745623699,0.872284093730237298],"hpluv":[285.081329657377239,89.0564736794959799,74.6734949675833093],"hsluv":[285.081329657377239,75.3315532053798194,74.6734949675833093]},"#ccaaff":{"lch":[75.3311933526529316,65.9300530396585458,280.316334998223624],"luv":[75.3311933526529316,11.8069327652914797,-64.8642292214135239],"rgb":[0.8,0.66666666666666663,1],"xyz":[0.573234586139304891,0.488071478109556423,1.01011828202201914],"hpluv":[280.316334998223624,111.057502918871393,75.3311933526529316],"hsluv":[280.316334998223624,99.9999999999973426,75.3311933526529316]},"#553300":{"lch":[24.6368918170402651,35.1311640480653367,43.6144672720514848],"luv":[24.6368918170402651,25.4348822629288698,24.2335604409056025],"rgb":[0.333333333333333315,0.2,0],"xyz":[0.049300031966319241,0.0429919833271212859,0.00570196001226947087],"hpluv":[43.6144672720514848,180.944734702515461,24.6368918170402651],"hsluv":[43.6144672720514848,100.000000000002245,24.6368918170402651]},"#553311":{"lch":[24.7639934196671305,31.9442274162558917,39.8156129865950632],"luv":[24.7639934196671305,24.536650698978292,20.4544967598277161],"rgb":[0.333333333333333315,0.2,0.0666666666666666657],"xyz":[0.0503116974659563593,0.0433966495269761388,0.0110300649770251023],"hpluv":[39.8156129865950632,163.685811409294416,24.7639934196671305],"hsluv":[39.8156129865950632,85.3309596574143256,24.7639934196671305]},"#553322":{"lch":[24.9975315322943885,26.9096454002331456,31.2519949010175395],"luv":[24.9975315322943885,23.0048892205904032,13.9608054035092266],"rgb":[0.333333333333333315,0.2,0.133333333333333331],"xyz":[0.0521870556044333814,0.0441467927823669601,0.0209069511730043],"hpluv":[31.2519949010175395,136.59983556614165,24.9975315322943885],"hsluv":[31.2519949010175395,60.4417762068684823,24.9975315322943885]},"#553333":{"lch":[25.3763514371309924,21.2787516643900716,12.1770506300621495],"luv":[25.3763514371309924,20.7999895475976082,4.48839695377019332],"rgb":[0.333333333333333315,0.2,0.2],"xyz":[0.0552748063368911219,0.0453818930753500729,0.0371691050306154416],"hpluv":[12.1770506300621495,106.403592780468983,25.3763514371309924],"hsluv":[12.1770506300621495,24.9336598370546909,25.3763514371309924]},"#553344":{"lch":[25.9113402150992655,19.4972565515274532,338.627269772390264],"luv":[25.9113402150992655,18.1564179844864455,-7.10545558065754257],"rgb":[0.333333333333333315,0.2,0.266666666666666663],"xyz":[0.05973280158111,0.047165091173037646,0.0606478799835020801],"hpluv":[338.627269772390264,95.4823185470749536,25.9113402150992655],"hsluv":[338.627269772390264,32.9723178491547841,25.9113402150992655]},"#553355":{"lch":[26.6061908173450519,25.0719265662328183,307.715012949245363],"luv":[26.6061908173450519,15.3373584467403763,-19.8334802195371829],"rgb":[0.333333333333333315,0.2,0.333333333333333315],"xyz":[0.065695215620472,0.0495500567887824789,0.0920499272574759886],"hpluv":[307.715012949245363,119.576085528419512,26.6061908173450519],"hsluv":[307.715012949245363,41.369683748934996,26.6061908173450519]},"#553366":{"lch":[27.4586282592714284,35.2024776289392847,290.893042573756475],"luv":[27.4586282592714284,12.5540679631371699,-32.8878367910220533],"rgb":[0.333333333333333315,0.2,0.4],"xyz":[0.0732802113939801658,0.0525840550981857904,0.13199757166461995],"hpluv":[290.893042573756475,162.679833835368925,27.4586282592714284],"hsluv":[290.893042573756475,49.429407074258,27.4586282592714284]},"#553377":{"lch":[28.461655060413058,46.8206771520520633,282.253113271302652],"luv":[28.461655060413058,9.93678843358592445,-45.7540822725452898],"rgb":[0.333333333333333315,0.2,0.466666666666666674],"xyz":[0.0825942244141820853,0.0563096603062666068,0.18105137357101786],"hpluv":[282.253113271302652,208.74537696470955,28.461655060413058],"hsluv":[282.253113271302652,56.732994513665389,28.461655060413058]},"#553388":{"lch":[29.6048600369324433,58.6666973974172876,277.388246485053742],"luv":[29.6048600369324433,7.54407762538358551,-58.1796207988872567],"rgb":[0.333333333333333315,0.2,0.533333333333333326],"xyz":[0.0937346415161259133,0.0607658271470442046,0.239724236974590027],"hpluv":[277.388246485053742,251.459446283522851,29.6048600369324433],"hsluv":[277.388246485053742,63.1061783000114715,29.6048600369324433]},"#553399":{"lch":[30.8756880539778678,70.2946203588512759,274.394660626066695],"luv":[30.8756880539778678,5.38640350826703,-70.0879469569565714],"rgb":[0.333333333333333315,0.2,0.6],"xyz":[0.106791605625198627,0.0659886127906733622,0.308490914615707934],"hpluv":[274.394660626066695,288.898157334443908,30.8756880539778678],"hsluv":[274.394660626066695,68.537799314251,30.8756880539778678]},"#5533aa":{"lch":[32.2605562861205,81.571224268378316,272.422737250332261],"luv":[32.2605562861205,3.44819039224039026,-81.498310483475251],"rgb":[0.333333333333333315,0.2,0.66666666666666663],"xyz":[0.121849298590451169,0.0720116899767744567,0.387794764232706535],"hpluv":[272.422737250332261,320.851781583216223,32.2605562861205],"hsluv":[272.422737250332261,73.1042224024611897,32.2605562861205]},"#5533bb":{"lch":[33.7457437232739395,92.4864953275551755,271.055064965734516],"luv":[33.7457437232739395,1.70298313903478205,-92.4708152143261231],"rgb":[0.333333333333333315,0.2,0.733333333333333282],"xyz":[0.138986890697787802,0.0788667268197092153,0.478052749331348281],"hpluv":[271.055064965734516,347.775228922495899,33.7457437232739395],"hsluv":[271.055064965734516,76.9172199670655772,33.7457437232739395]},"#5533cc":{"lch":[35.3180325241442,103.075547069605378,270.067872161558512],"luv":[35.3180325241442,0.12210251098248695,-103.075474748725966],"rgb":[0.333333333333333315,0.2,0.8],"xyz":[0.158279266141509556,0.0865836769971980169,0.579659260001618493],"hpluv":[270.067872161558512,370.338167129355497,35.3180325241442],"hsluv":[270.067872161558512,80.0940957669769205,35.3180325241442]},"#5533dd":{"lch":[36.9651203282397915,113.385246995104879,269.332372034398134],"luv":[36.9651203282397915,-1.32116971635525937,-113.377549570986602],"rgb":[0.333333333333333315,0.2,0.866666666666666696],"xyz":[0.179797591692143099,0.0951910072174515509,0.69298910790162449],"hpluv":[269.332372034398134,389.227711455139399,36.9651203282397915],"hsluv":[269.332372034398134,84.8119298710034855,36.9651203282397915]},"#5533ee":{"lch":[38.6758450270606247,123.460606758128,268.77009087446072],"luv":[38.6758450270606247,-2.64999738284680575,-123.432163292052948],"rgb":[0.333333333333333315,0.2,0.933333333333333348],"xyz":[0.203609771958317837,0.104715879323921596,0.818399923970147714],"hpluv":[268.77009087446072,405.067987408818738,38.6758450270606247],"hsluv":[268.77009087446072,92.2936685874649356,38.6758450270606247]},"#5533ff":{"lch":[40.4402700894382363,133.340096114557781,268.33094335317071],"luv":[40.4402700894382363,-3.88371885476632706,-133.283524712158766],"rgb":[0.333333333333333315,0.2,1],"xyz":[0.22978082036814923,0.115184298687854292,0.956234112261929559],"hpluv":[268.33094335317071,418.394573227645935,40.4402700894382363],"hsluv":[268.33094335317071,99.99999999999946,40.4402700894382363]},"#ccbb00":{"lch":[75.0632334950121418,83.0331806403360275,77.5616137481136292],"luv":[75.0632334950121418,17.8844849446013434,81.0842419062853423],"rgb":[0.8,0.733333333333333282,0],"xyz":[0.426708295646073654,0.483788158566022,0.0709042957418917],"hpluv":[77.5616137481136292,140.366584388758554,75.0632334950121418],"hsluv":[77.5616137481136292,100.000000000002373,75.0632334950121418]},"#ccbb11":{"lch":[75.0886164663743898,81.9438461169387438,77.472162284636525],"luv":[75.0886164663743898,17.7747618107910519,79.9928231718707394],"rgb":[0.8,0.733333333333333282,0.0666666666666666657],"xyz":[0.427719961145710759,0.484192824765876828,0.0762324007066473436],"hpluv":[77.472162284636525,138.478250548668058,75.0886164663743898],"hsluv":[77.472162284636525,98.6397508878022222,75.0886164663743898]},"#ccbb22":{"lch":[75.1356323460541802,79.9401497843360289,77.301186242421764],"luv":[75.1356323460541802,17.5729239259544805,77.9847414064744839],"rgb":[0.8,0.733333333333333282,0.133333333333333331],"xyz":[0.429595319284187815,0.484942968021267629,0.0861092869026265362],"hpluv":[77.301186242421764,135.007637737713509,75.1356323460541802],"hsluv":[77.301186242421764,96.1390577710114087,75.1356323460541802]},"#ccbb33":{"lch":[75.2129378077961235,76.6847204783522471,77.0041162235267223],"luv":[75.2129378077961235,17.2449407213965777,74.720535158405653],"rgb":[0.8,0.733333333333333282,0.2],"xyz":[0.432683070016645521,0.486178068314250755,0.102371440760237686],"hpluv":[77.0041162235267223,129.376563997369885,75.2129378077961235],"hsluv":[77.0041162235267223,92.0797058264172392,75.2129378077961235]},"#ccbb44":{"lch":[75.3243183189310628,72.0782997366288,76.5373230188340585],"luv":[75.3243183189310628,16.7806861485978907,70.0977165484408431],"rgb":[0.8,0.733333333333333282,0.266666666666666663],"xyz":[0.437141065260864436,0.487961266411938321,0.125850215713124325],"hpluv":[76.5373230188340585,121.425150369898518,75.3243183189310628],"hsluv":[76.5373230188340585,86.34290383100182,75.3243183189310628]},"#ccbb55":{"lch":[75.4728625341146397,66.0852327898053,75.831238609121371],"luv":[75.4728625341146397,16.1762634357197506,64.0748507149330351],"rgb":[0.8,0.733333333333333282,0.333333333333333315],"xyz":[0.443103479300226422,0.490346232027683182,0.157252262987098212],"hpluv":[75.831238609121371,111.10994393806304,75.4728625341146397],"hsluv":[75.831238609121371,78.8905565478608395,75.4728625341146397]},"#ccbb66":{"lch":[75.6611363515091284,58.7289677793131,74.7643785098968863],"luv":[75.6611363515091284,15.4333320312120961,56.6648384700597134],"rgb":[0.8,0.733333333333333282,0.4],"xyz":[0.450688475073734607,0.493380230337086501,0.197199907394242202],"hpluv":[74.7643785098968863,98.4960546107130739,75.6611363515091284],"hsluv":[74.7643785098968863,69.7569980114599701,75.6611363515091284]},"#ccbb77":{"lch":[75.8912747684230737,50.090867045562554,73.1036630558194389],"luv":[75.8912747684230737,14.5584607724557671,47.9285528814827799],"rgb":[0.8,0.733333333333333282,0.466666666666666674],"xyz":[0.460002488093936512,0.497105835545167296,0.246253709300640111],"hpluv":[73.1036630558194389,83.7540906733876795,75.8912747684230737],"hsluv":[73.1036630558194389,59.0406908273499624,75.8912747684230737]},"#ccbb88":{"lch":[76.1650362860674335,40.3168412407358332,70.3426656865815119],"luv":[76.1650362860674335,13.5623472284534756,37.9672283066009157],"rgb":[0.8,0.733333333333333282,0.533333333333333326],"xyz":[0.471142905195880313,0.501562002385944838,0.304926572704212251],"hpluv":[70.3426656865815119,67.6831847004182379,76.1650362860674335],"hsluv":[70.3426656865815119,46.8939441374685586,76.1650362860674335]},"#ccbb99":{"lch":[76.4838383644648871,29.6573629081618826,65.1594516064859732],"luv":[76.4838383644648871,12.4588924968264081,26.9134756658990248],"rgb":[0.8,0.733333333333333282,0.6],"xyz":[0.484199869304953068,0.506784788029574,0.373693250345330186],"hpluv":[65.1594516064859732,50.6096239917615662,76.4838383644648871],"hsluv":[65.1594516064859732,33.5105935092195324,76.4838383644648871]},"#ccbbaa":{"lch":[76.8487828748923,18.695758935396551,52.9508743401004551],"luv":[76.8487828748923,11.26418644921627,14.9214444946778713],"rgb":[0.8,0.733333333333333282,0.66666666666666663],"xyz":[0.499257562270205568,0.512807865215675118,0.452997099962328786],"hpluv":[52.9508743401004551,32.5142912020767696,76.8487828748923],"hsluv":[52.9508743401004551,19.1124274046376854,76.8487828748923]},"#ccbbbb":{"lch":[77.2606763328388126,10.2255548171674207,12.1770506300639045],"luv":[77.2606763328388126,9.99548454110707,2.15690985147396086],"rgb":[0.8,0.733333333333333282,0.733333333333333282],"xyz":[0.516395154377542243,0.519662902058609877,0.543255085060970533],"hpluv":[12.1770506300639045,18.1733155366010308,77.2606763328388126],"hsluv":[12.1770506300639045,17.1437484893634,77.2606763328388126]},"#ccbbcc":{"lch":[77.7200476270310361,14.1732404034447406,307.715012949257925],"luv":[77.7200476270310361,8.67025786172679247,-11.2119299027867267],"rgb":[0.8,0.733333333333333282,0.8],"xyz":[0.535687529821264,0.527379852236098734,0.644861595731240689],"hpluv":[307.715012949257925,25.815587331502627,77.7200476270310361],"hsluv":[307.715012949257925,18.1614974693692588,77.7200476270310361]},"#ccbbdd":{"lch":[78.2271648233418375,26.0669286991650679,286.275412631614586],"luv":[78.2271648233418375,7.3053819239814537,-25.022313377306272],"rgb":[0.8,0.733333333333333282,0.866666666666666696],"xyz":[0.557205855371897485,0.53598718245635224,0.758191443631246687],"hpluv":[286.275412631614586,48.8074477639820898,78.2271648233418375],"hsluv":[286.275412631614586,43.4510455050629929,78.2271648233418375]},"#ccbbee":{"lch":[78.7820519440635,39.5678846485468299,278.599571826643512],"luv":[78.7820519440635,5.9165048525659989,-39.1230426461218741],"rgb":[0.8,0.733333333333333282,0.933333333333333348],"xyz":[0.581018035638072305,0.545512054562822257,0.88360225969976991],"hpluv":[278.599571826643512,76.4037468789834,78.7820519440635],"hsluv":[278.599571826643512,70.6968150087455314,78.7820519440635]},"#ccbbff":{"lch":[79.3845061922316546,53.5695670840829834,274.837592460092935],"luv":[79.3845061922316546,4.51760925339512553,-53.3787385033564235],"rgb":[0.8,0.733333333333333282,1],"xyz":[0.607189084047903616,0.555980473926755,1.02143644799155164],"hpluv":[274.837592460092935,107.038572744282547,79.3845061922316546],"hsluv":[274.837592460092935,99.9999999999963762,79.3845061922316546]},"#554400":{"lch":[29.5776499109456879,34.0768703371065413,65.9474553070004674],"luv":[29.5776499109456879,13.8888553561515469,31.1180460322924546],"rgb":[0.333333333333333315,0.266666666666666663,0],"xyz":[0.0581326024492852811,0.0606571242930536,0.0086461501732580659],"hpluv":[65.9474553070004674,146.195957524823825,29.5776499109456879],"hsluv":[65.9474553070004674,100.000000000002217,29.5776499109456879]},"#554411":{"lch":[29.6787804923011507,30.8713815411758574,64.2244588846852906],"luv":[29.6787804923011507,13.4243191404277429,27.7998175151708296],"rgb":[0.333333333333333315,0.266666666666666663,0.0666666666666666657],"xyz":[0.0591442679489224,0.0610617904929084548,0.0139742551380136974],"hpluv":[64.2244588846852906,131.992525946838612,29.6787804923011507],"hsluv":[64.2244588846852906,89.4077694450363509,29.6787804923011507]},"#554422":{"lch":[29.8650740872788916,25.3442505658492152,60.1547603291711539],"luv":[29.8650740872788916,12.6127938179319639,21.9829131111162219],"rgb":[0.333333333333333315,0.266666666666666663,0.133333333333333331],"xyz":[0.0610196260873994215,0.0618119337482992762,0.0238511413339928952],"hpluv":[60.1547603291711539,107.684992873038837,29.8650740872788916],"hsluv":[60.1547603291711539,70.9921927199271892,29.8650740872788916]},"#554433":{"lch":[30.1685472793317686,17.4710669916127905,49.2680266756281497],"luv":[30.1685472793317686,11.400244593092479,13.2390560480378525],"rgb":[0.333333333333333315,0.266666666666666663,0.2],"xyz":[0.064107376819857162,0.0630470340412823821,0.0401132951916040384],"hpluv":[49.2680266756281497,73.4859575586117302,30.1685472793317686],"hsluv":[49.2680266756281497,43.7071568358896,30.1685472793317686]},"#554444":{"lch":[30.5997780424982437,10.1013456632853149,12.1770506300629258],"luv":[30.5997780424982437,9.87407003600776356,2.13071000682558],"rgb":[0.333333333333333315,0.266666666666666663,0.266666666666666663],"xyz":[0.0685653720640760356,0.0648302321389699621,0.06359207014449067],"hpluv":[12.1770506300629258,41.8890279889816597,30.5997780424982437],"hsluv":[12.1770506300629258,9.81589763549682282,30.5997780424982437]},"#554455":{"lch":[31.1643459369041338,13.3310860490153988,307.715012949249],"luv":[31.1643459369041338,8.15508312366709198,-10.5457325251654854],"rgb":[0.333333333333333315,0.266666666666666663,0.333333333333333315],"xyz":[0.0745277861034380346,0.0672151977547148,0.0949941174184645853],"hpluv":[307.715012949249,54.2808752323906702,31.1643459369041338],"hsluv":[307.715012949249,18.7795296363480162,31.1643459369041338]},"#554466":{"lch":[31.8635722620044533,24.8340912161126894,284.841165372516684],"luv":[31.8635722620044533,6.36101223964894835,-24.005616214070443],"rgb":[0.333333333333333315,0.266666666666666663,0.4],"xyz":[0.0821127818769462,0.0702491960641181135,0.134941761825608547],"hpluv":[284.841165372516684,98.8992811700442331,31.8635722620044533],"hsluv":[284.841165372516684,27.8963704264996828,31.8635722620044533]},"#554477":{"lch":[32.6951743277909443,37.9095930577936784,276.944338121378166],"luv":[32.6951743277909443,4.58346102749711282,-37.6314912117090259],"rgb":[0.333333333333333315,0.266666666666666663,0.466666666666666674],"xyz":[0.0914267948971481254,0.0739748012721989229,0.183995563732006456],"hpluv":[276.944338121378166,147.131204947893167,32.6951743277909443],"hsluv":[276.944338121378166,36.6308714978363952,32.6951743277909443]},"#554488":{"lch":[33.6539551717380903,51.1018638808679526,273.231940865766],"luv":[33.6539551717380903,2.88102600900669881,-51.0205858574182116],"rgb":[0.333333333333333315,0.266666666666666663,0.533333333333333326],"xyz":[0.102567211999091953,0.0784309681129765207,0.242668427135578624],"hpluv":[273.231940865766,192.681471981123309,33.6539551717380903],"hsluv":[273.231940865766,44.6505812495932801,33.6539551717380903]},"#554499":{"lch":[34.7325237210335871,63.9719124096453271,271.149517924849135],"luv":[34.7325237210335871,1.28337422751110575,-63.9590378909774913],"rgb":[0.333333333333333315,0.266666666666666663,0.6],"xyz":[0.115624176108164667,0.0836537537566056782,0.311435104776696559],"hpluv":[271.149517924849135,233.718085138698228,34.7325237210335871],"hsluv":[271.149517924849135,51.7999710200133876,34.7325237210335871]},"#5544aa":{"lch":[35.9219992682327671,76.3678985235758461,269.849629436139082],"luv":[35.9219992682327671,-0.200424374653859844,-76.367635520403681],"rgb":[0.333333333333333315,0.266666666666666663,0.66666666666666663],"xyz":[0.130681869073417223,0.0896768309427067867,0.390738954393695104],"hpluv":[269.849629436139082,269.767524553786757,35.9219992682327671],"hsluv":[269.849629436139082,58.0490239963672465,35.9219992682327671]},"#5544bb":{"lch":[37.2126506061998725,88.2627815084196925,268.978337620063314],"luv":[37.2126506061998725,-1.57376312233016602,-88.2487499584998574],"rgb":[0.333333333333333315,0.266666666666666663,0.733333333333333282],"xyz":[0.147819461180753842,0.0965318677856415452,0.480996939492336906],"hpluv":[268.978337620063314,300.972163983169935,37.2126506061998725],"hsluv":[268.978337620063314,66.0075029321504729,37.2126506061998725]},"#5544cc":{"lch":[38.5944341452357733,99.6871153018899605,268.363808876145868],"luv":[38.5944341452357733,-2.84637030136680202,-99.6464707519528758],"rgb":[0.333333333333333315,0.266666666666666663,0.8],"xyz":[0.167111836624475596,0.104248817963130347,0.582603450162607062],"hpluv":[268.363808876145868,327.758284181939189,38.5944341452357733],"hsluv":[268.363808876145868,74.5283681063652352,38.5944341452357733]},"#5544dd":{"lch":[40.0574145710935738,110.694467066426,267.913412719582],"luv":[40.0574145710935738,-4.03035995406870118,-110.621070496360275],"rgb":[0.333333333333333315,0.266666666666666663,0.866666666666666696],"xyz":[0.18863016217510914,0.112856148183383881,0.695933298062613059],"hpluv":[267.913412719582,350.65684831918054,40.0574145710935738],"hsluv":[267.913412719582,82.9425769408534,40.0574145710935738]},"#5544ee":{"lch":[41.5920687629554777,121.343449115737442,267.573227144435918],"luv":[41.5920687629554777,-5.13798666158471651,-121.23462268002875],"rgb":[0.333333333333333315,0.266666666666666663,0.933333333333333348],"xyz":[0.212442342441283877,0.122381020289853898,0.821344114131136283],"hpluv":[267.573227144435918,370.207437425804358,41.5920687629554777],"hsluv":[267.573227144435918,91.3878688389195872,41.5920687629554777]},"#5544ff":{"lch":[43.1894854939413833,131.689092168021887,267.309962581974105],"luv":[43.1894854939413833,-6.18053297470727,-131.543977468321657],"rgb":[0.333333333333333315,0.266666666666666663,1],"xyz":[0.23861339085111527,0.132849439653786594,0.959178302422918128],"hpluv":[267.309962581974105,386.911020330846497,43.1894854939413833],"hsluv":[267.309962581974105,99.9999999999994174,43.1894854939413833]},"#cccc00":{"lch":[79.627228346343,87.7811065558180132,85.8743202181747449],"luv":[79.627228346343,6.31536666608958797,87.5536339167982192],"rgb":[0.8,0.8,0],"xyz":[0.464932038955690574,0.560235645185256814,0.0836455435117636481],"hpluv":[85.8743202181747449,177.871840357077815,79.627228346343],"hsluv":[85.8743202181747449,100.000000000002245,79.627228346343]},"#cccc11":{"lch":[79.6502471087807891,86.7718911178833707,85.8743202181747],"luv":[79.6502471087807891,6.24275917928854351,86.5470337215737],"rgb":[0.8,0.8,0.0666666666666666657],"xyz":[0.465943704455327679,0.560640311385111723,0.0889736484765192848],"hpluv":[85.8743202181747,176.061859354342118,79.6502471087807891],"hsluv":[85.8743202181747,98.8217369524532927,79.6502471087807891]},"#cccc22":{"lch":[79.6928884771461838,84.9132131482774497,85.8743202181746597],"luv":[79.6928884771461838,6.10903754655005482,84.6931722597497298],"rgb":[0.8,0.8,0.133333333333333331],"xyz":[0.467819062593804735,0.561390454640502523,0.0988505346724984774],"hpluv":[85.8743202181746597,172.717957044242922,79.6928884771461838],"hsluv":[85.8743202181746597,96.653203912443459,79.6928884771461838]},"#cccc33":{"lch":[79.7630142061039891,81.8867831673476445,85.8743202181745602],"luv":[79.7630142061039891,5.89130259459140682,81.6745848549912665],"rgb":[0.8,0.8,0.2],"xyz":[0.470906813326262441,0.56262555493348565,0.115112688530109614],"hpluv":[85.8743202181745602,167.243641009868185,79.7630142061039891],"hsluv":[85.8743202181745602,93.1263971058523623,79.7630142061039891]},"#cccc44":{"lch":[79.8640786601047523,77.5899505798540901,85.8743202181744],"luv":[79.8640786601047523,5.58216917901353593,77.3888869169280156],"rgb":[0.8,0.8,0.266666666666666663],"xyz":[0.475364808570481356,0.564408753031173216,0.138591463482996252],"hpluv":[85.8743202181744,159.406599578518,79.8640786601047523],"hsluv":[85.8743202181744,88.1281264379617113,79.8640786601047523]},"#cccc55":{"lch":[79.9989166638852396,71.9729259129294832,85.8743202181741481],"luv":[79.9989166638852396,5.17805522174052,71.7864179952492236],"rgb":[0.8,0.8,0.333333333333333315],"xyz":[0.481327222609843342,0.566793718646918,0.169993510756970168],"hpluv":[85.8743202181741481,149.042041148428552,79.9989166638852396],"hsluv":[85.8743202181741481,81.6104176612222147,79.9989166638852396]},"#cccc66":{"lch":[80.1699032642976448,65.0330295388014,85.8743202181738],"luv":[80.1699032642976448,4.67876793832737103,64.8645054060685453],"rgb":[0.8,0.8,0.4],"xyz":[0.488912218383351527,0.569827716956321284,0.209941155164114129],"hpluv":[85.8743202181738,136.038559250079544,80.1699032642976448],"hsluv":[85.8743202181738,73.5839630734023,80.1699032642976448]},"#cccc77":{"lch":[80.3790384438565724,56.8098315426934803,85.8743202181733096],"luv":[80.3790384438565724,4.0871541782499472,56.6626166941875837],"rgb":[0.8,0.8,0.466666666666666674],"xyz":[0.498226231403553432,0.573553322164402135,0.258994957070512],"hpluv":[85.8743202181733096,120.326663009793734,80.3790384438565724],"hsluv":[85.8743202181733096,64.1122831116318252,80.3790384438565724]},"#cccc88":{"lch":[80.6279973255091704,47.3795499902223085,85.8743202181724854],"luv":[80.6279973255091704,3.40869741112766222,47.2567724166107865],"rgb":[0.8,0.8,0.533333333333333326],"xyz":[0.509366648505497288,0.578009489005179677,0.317667820474084206],"hpluv":[85.8743202181724854,101.866799769447482,80.6279973255091704],"hsluv":[85.8743202181724854,53.3047126681541172,80.6279973255091704]},"#cccc99":{"lch":[80.9181626169042119,36.8483625211582861,85.8743202181711496],"luv":[80.9181626169042119,2.65103653276840934,36.7528750683892724],"rgb":[0.8,0.8,0.6],"xyz":[0.522423612614569932,0.58323227464880889,0.386434498115202141],"hpluv":[85.8743202181711496,80.635831276566,80.9181626169042119],"hsluv":[85.8743202181711496,41.30786304104587,80.9181626169042119]},"#ccccaa":{"lch":[81.2506473976222452,25.3448572993520607,85.8743202181683216],"luv":[81.2506473976222452,1.82342275263520492,25.27917959487],"rgb":[0.8,0.8,0.66666666666666663],"xyz":[0.537481305579822544,0.58925535183491,0.465738347732200686],"hpluv":[85.8743202181683216,56.6117825452175509,81.2506473976222452],"hsluv":[85.8743202181683216,28.2959036617823898,81.2506473976222452]},"#ccccbb":{"lch":[81.6263125989092657,13.0121158497344798,85.8743202181599],"luv":[81.6263125989092657,0.936149997616518514,12.9783967449324749],"rgb":[0.8,0.8,0.733333333333333282],"xyz":[0.554618897687159107,0.596110388677844716,0.555996332830842488],"hpluv":[85.8743202181599,29.7570160502500514,81.6263125989092657],"hsluv":[85.8743202181599,14.4603328966824272,81.6263125989092657]},"#cccccc":{"lch":[82.0457816743453,4.34523248843710382e-12,0],"luv":[82.0457816743453,4.08534684758697557e-12,1.48019813318368677e-12],"rgb":[0.8,0.8,0.8],"xyz":[0.573911273130880861,0.603827338855333573,0.657602843501112644],"hpluv":[0,1.02065966511349575e-11,82.0457816743453],"hsluv":[0,1.01642596056880755e-11,82.0457816743453]},"#ccccdd":{"lch":[82.5094539517328087,13.5418343873660199,265.874320218194214],"luv":[82.5094539517328087,-0.974260325972223118,-13.5067425899839115],"rgb":[0.8,0.8,0.866666666666666696],"xyz":[0.59542959868151446,0.612434669075587079,0.770932691401118642],"hpluv":[265.874320218194214,32.7843842039110882,82.5094539517328087],"hsluv":[265.874320218194214,30.4637656032122131,82.5094539517328087]},"#ccccee":{"lch":[83.0175175610870895,27.4698052042972165,265.874320218185687],"luv":[83.0175175610870895,-1.97630103922669687,-27.3986209901955817],"rgb":[0.8,0.8,0.933333333333333348],"xyz":[0.61924177894768917,0.621959541182057096,0.896343507469641865],"hpluv":[265.874320218185687,68.7964750954906776,83.0175175610870895],"hsluv":[265.874320218185687,63.7216981471091941,83.0175175610870895]},"#ccccff":{"lch":[83.5699624582004219,41.6509292947620153,265.874320218182845],"luv":[83.5699624582004219,-2.99655473483939438,-41.5429966521238825],"rgb":[0.8,0.8,1],"xyz":[0.645412827357520591,0.63242796054598982,1.03417769576142371],"hpluv":[265.874320218182845,108.336501116640306,83.5699624582004219],"hsluv":[265.874320218182845,99.9999999999952536,83.5699624582004219]},"#555500":{"lch":[34.8595382729148753,38.4291768930055397,85.8743202181747307],"luv":[34.8595382729148753,2.76476741155027961,38.3295929776711901],"rgb":[0.333333333333333315,0.333333333333333315,0],"xyz":[0.0699458591636312466,0.084283637721745866,0.0125839024113732767],"hpluv":[85.8743202181747307,139.887458074797564,34.8595382729148753],"hsluv":[85.8743202181747307,100.000000000002331,34.8595382729148753]},"#555511":{"lch":[34.9408046802893,35.5443725161734108,85.8743202181745318],"luv":[34.9408046802893,2.55722164100294558,35.4522641737772517],"rgb":[0.333333333333333315,0.333333333333333315,0.0666666666666666657],"xyz":[0.0709575246632683648,0.0846883039216007188,0.0179120073761289064],"hpluv":[85.8743202181745318,129.085444875460666,34.9408046802893],"hsluv":[85.8743202181745318,92.2780688504912,34.9408046802893]},"#555522":{"lch":[35.0907688239250604,30.4130442263608813,85.8743202181740202],"luv":[35.0907688239250604,2.18805086034508589,30.3342330139914047],"rgb":[0.333333333333333315,0.333333333333333315,0.133333333333333331],"xyz":[0.0728328828017454,0.0854384471769915332,0.027788893572108106],"hpluv":[85.8743202181740202,109.978131404858061,35.0907688239250604],"hsluv":[85.8743202181740202,78.6190076783406653,35.0907688239250604]},"#555533":{"lch":[35.3357817552570097,22.5221214621125654,85.8743202181728549],"luv":[35.3357817552570097,1.62034247131604014,22.4637584885032275],"rgb":[0.333333333333333315,0.333333333333333315,0.2],"xyz":[0.0759206335342031274,0.0866735474699746461,0.0440510474297192492],"hpluv":[85.8743202181728549,80.8786547215116656,35.3357817552570097],"hsluv":[85.8743202181728549,57.8169450175211566,35.3357817552570097]},"#555544":{"lch":[35.6854507669058592,12.1926559388895619,85.8743202181691743],"luv":[35.6854507669058592,0.877194374836262392,12.1610603515028455],"rgb":[0.333333333333333315,0.333333333333333315,0.266666666666666663],"xyz":[0.080378628778422,0.0884567455676622261,0.0675298223826058808],"hpluv":[85.8743202181691743,43.355725530805,35.6854507669058592],"hsluv":[85.8743202181691743,30.9932899828832262,35.6854507669058592]},"#555555":{"lch":[36.1458508397197278,1.89718584003012571e-12,0],"luv":[36.1458508397197278,1.79982851973451413e-12,5.9994283991150471e-13],"rgb":[0.333333333333333315,0.333333333333333315,0.333333333333333315],"xyz":[0.086341042817784,0.090841711183407059,0.0989318696565798],"hpluv":[0,6.66025333978279224e-12,36.1458508397197278],"hsluv":[0,1.90696849203660445e-12,36.1458508397197278]},"#555566":{"lch":[36.7200402720523087,13.391014832031539,265.874320218184835],"luv":[36.7200402720523087,-0.963409690459316,-13.3563138627398903],"rgb":[0.333333333333333315,0.333333333333333315,0.4],"xyz":[0.0939260385912921714,0.0938757094928103775,0.138879514063723758],"hpluv":[265.874320218184835,46.2753453717946712,36.7200402720523087],"hsluv":[265.874320218184835,10.0205788523093844,36.7200402720523087]},"#555577":{"lch":[37.4084382237490445,27.3651172837118537,265.874320218181],"luv":[37.4084382237490445,-1.96876932050326592,-27.294204353927416],"rgb":[0.333333333333333315,0.333333333333333315,0.466666666666666674],"xyz":[0.103240051611494091,0.0976013147008911869,0.187933315970121667],"hpluv":[265.874320218181,92.8254499938530131,37.4084382237490445],"hsluv":[265.874320218181,20.457601163446,37.4084382237490445]},"#555588":{"lch":[38.2091925227490421,41.4380747403329508,265.874320218179832],"luv":[38.2091925227490421,-2.98124102314999906,-41.330693680935326],"rgb":[0.333333333333333315,0.333333333333333315,0.533333333333333326],"xyz":[0.114380468713437919,0.102057481541668785,0.246606179373693835],"hpluv":[265.874320218179832,137.616667264503377,38.2091925227490421],"hsluv":[265.874320218179832,30.9677616121988244,38.2091925227490421]},"#555599":{"lch":[39.1185695394092079,55.2798607696674651,265.874320218179207],"luv":[39.1185695394092079,-3.97708121608675258,-55.1366106295487626],"rgb":[0.333333333333333315,0.333333333333333315,0.6],"xyz":[0.127437432822510632,0.107280267185297942,0.315372857014811769],"hpluv":[265.874320218179207,179.317758559659353,39.1185695394092079],"hsluv":[265.874320218179207,41.3377460212352688,39.1185695394092079]},"#5555aa":{"lch":[40.1313601009005083,68.6985541131433166,265.874320218178866],"luv":[40.1313601009005083,-4.94248222285034178,-68.5205312786852829],"rgb":[0.333333333333333315,0.333333333333333315,0.66666666666666663],"xyz":[0.142495125787763188,0.113303344371399051,0.394676706631810315],"hpluv":[265.874320218178866,217.221618066114,40.1313601009005083],"hsluv":[265.874320218178866,51.4705865731736907,40.1313601009005083]},"#5555bb":{"lch":[41.2412811653463791,81.6070675548115787,265.874320218178639],"luv":[41.2412811653463791,-5.8711785983783864,-81.3955940869131],"rgb":[0.333333333333333315,0.333333333333333315,0.733333333333333282],"xyz":[0.159632717895099807,0.120158381214333809,0.484934691730452117],"hpluv":[265.874320218178639,251.093199488524249,41.2412811653463791],"hsluv":[265.874320218178639,61.3584924374872642,41.2412811653463791]},"#5555cc":{"lch":[42.4413509436270431,93.9883543596933,265.874320218178468],"luv":[42.4413509436270431,-6.76194392407029365,-93.7447964935174838],"rgb":[0.333333333333333315,0.333333333333333315,0.8],"xyz":[0.178925093338821561,0.127875331391822611,0.586541202400722272],"hpluv":[265.874320218178468,281.011551484187919,42.4413509436270431],"hsluv":[265.874320218178468,71.0546332315999791,42.4413509436270431]},"#5555dd":{"lch":[43.7242196004532389,105.866759380085014,265.874320218178411],"luv":[43.7242196004532389,-7.61652967783202328,-105.592420264465588],"rgb":[0.333333333333333315,0.333333333333333315,0.866666666666666696],"xyz":[0.200443418889455105,0.136482661612076145,0.69987105030072827],"hpluv":[265.874320218178411,307.239379297131052,43.7242196004532389],"hsluv":[265.874320218178411,80.6500300064864462,43.7242196004532389]},"#5555ee":{"lch":[45.0824447652298588,117.287521338877212,265.874320218178354],"luv":[45.0824447652298588,-8.43819053636753402,-116.983586892732674],"rgb":[0.333333333333333315,0.333333333333333315,0.933333333333333348],"xyz":[0.224255599155629842,0.146007533718546162,0.825281866369251493],"hpluv":[265.874320218178354,330.128999417958312,45.0824447652298588],"hsluv":[265.874320218178354,90.2572110855712708,45.0824447652298588]},"#5555ff":{"lch":[46.508708270344421,128.30356479361032,265.874320218178241],"luv":[46.508708270344421,-9.23073412981071,-127.971083789162734],"rgb":[0.333333333333333315,0.333333333333333315,1],"xyz":[0.250426647565461236,0.156475953082478858,0.963116054661033338],"hpluv":[265.874320218178241,350.061034522531031,46.508708270344421],"hsluv":[265.874320218178241,99.9999999999992468,46.508708270344421]},"#ccdd00":{"lch":[84.2515012159558552,94.1174138813685772,92.7819892835375555],"luv":[84.2515012159558552,-4.56806363508048818,94.0064912138661128],"rgb":[0.8,0.866666666666666696,0],"xyz":[0.507566029502865779,0.645503626279608334,0.0978568736941549666],"hpluv":[92.7819892835375555,256.902059824464118,84.2515012159558552],"hsluv":[92.7819892835375555,100.000000000002373,84.2515012159558552]},"#ccdd11":{"lch":[84.2724460606142287,93.1910749716768692,92.8373396690251695],"luv":[84.2724460606142287,-4.61302172096193619,93.0768310858219508],"rgb":[0.8,0.866666666666666696,0.0666666666666666657],"xyz":[0.508577695002502939,0.645908292479463242,0.103184978658910603],"hpluv":[92.8373396690251695,254.758074317949962,84.2724460606142287],"hsluv":[92.8373396690251695,98.9747327605244465,84.2724460606142287]},"#ccdd22":{"lch":[84.3112490917695396,91.4838404394451175,92.9423140418060711],"luv":[84.3112490917695396,-4.69591125178302349,91.3632392106652],"rgb":[0.8,0.866666666666666696,0.133333333333333331],"xyz":[0.510453053140979884,0.646658435734854,0.113061864854889796],"hpluv":[92.9423140418060711,250.792999873820406,84.3112490917695396],"hsluv":[92.9423140418060711,97.0860193213801637,84.3112490917695396]},"#ccdd33":{"lch":[84.3750724084435291,88.7007441607730271,93.1221866378423329],"luv":[84.3750724084435291,-4.83112791130122865,88.5690816130525],"rgb":[0.8,0.866666666666666696,0.2],"xyz":[0.513540803873437701,0.647893536027837169,0.129324018712500932],"hpluv":[93.1221866378423329,244.290353733928498,84.3750724084435291],"hsluv":[93.1221866378423329,94.0094146319777,84.3750724084435291]},"#ccdd44":{"lch":[84.4670755276021197,84.7426243382763857,93.3985511967960491],"luv":[84.4670755276021197,-5.02363867224446459,84.5935897939611152],"rgb":[0.8,0.866666666666666696,0.266666666666666663],"xyz":[0.517998799117656561,0.649676734125524735,0.152802793665387571],"hpluv":[93.3985511967960491,234.956854948695792,84.4670755276021197],"hsluv":[93.3985511967960491,89.638788485123,84.4670755276021197]},"#ccdd55":{"lch":[84.5898637292301601,79.5568981087007359,93.8026723407881633],"luv":[84.5898637292301601,-5.27624840258561711,79.3817437416967095],"rgb":[0.8,0.866666666666666696,0.333333333333333315],"xyz":[0.523961213157018491,0.652061699741269485,0.184204840939361486],"hpluv":[93.8026723407881633,222.570337020009788,84.5898637292301601],"hsluv":[93.8026723407881633,83.9211409492819485,84.5898637292301601]},"#ccdd66":{"lch":[84.7456349568684857,73.1331501088793772,94.3836122863904],"luv":[84.7456349568684857,-5.58984810871231,72.9192103836116],"rgb":[0.8,0.866666666666666696,0.4],"xyz":[0.531546208930526731,0.655095698050672803,0.224152485346505448],"hpluv":[94.3836122863904,206.963810787645969,84.7456349568684857],"hsluv":[94.3836122863904,76.851201462777,84.7456349568684857]},"#ccdd77":{"lch":[84.9362580835765186,65.5005774458529828,95.2238145131341156],"luv":[84.9362580835765186,-5.96359854749608687,65.2285300930849274],"rgb":[0.8,0.866666666666666696,0.466666666666666674],"xyz":[0.540860221950728581,0.658821303258753654,0.273206287252903357],"hpluv":[95.2238145131341156,188.015270942662227,84.9362580835765186],"hsluv":[95.2238145131341156,68.4671748287691,84.9362580835765186]},"#ccdd88":{"lch":[85.1633194008604733,56.7265968622164536,96.4730545580279],"luv":[85.1633194008604733,-6.39512608731957588,56.3649638862273221],"rgb":[0.8,0.866666666666666696,0.533333333333333326],"xyz":[0.552000639052672493,0.663277470099531197,0.331879150656475552],"hpluv":[96.4730545580279,165.642146962004915,85.1633194008604733],"hsluv":[96.4730545580279,58.8458897999489707,85.1633194008604733]},"#ccdd99":{"lch":[85.4281525463895548,46.9188240443766134,98.432972024211054],"luv":[85.4281525463895548,-6.88075351903593724,46.4115425375783843],"rgb":[0.8,0.866666666666666696,0.6],"xyz":[0.565057603161745137,0.66850025574316041,0.400645828297593432],"hpluv":[98.432972024211054,139.807612964747022,85.4281525463895548],"hsluv":[98.432972024211054,48.0968721297954644,85.4281525463895548]},"#ccddaa":{"lch":[85.7318592309590457,36.2402041704229134,101.807730551991185],"luv":[85.7318592309590457,-7.41576493035038453,35.4733537857886176],"rgb":[0.8,0.866666666666666696,0.66666666666666663],"xyz":[0.580115296126997748,0.674523332929261477,0.479949677914592032],"hpluv":[101.807730551991185,110.570541660882469,85.7318592309590457],"hsluv":[101.807730551991185,36.355428538792907,85.7318592309590457]},"#ccddbb":{"lch":[86.0753247228475828,24.9849555175277693,108.661729708420211],"luv":[86.0753247228475828,-7.99469198849898,23.671351930590518],"rgb":[0.8,0.866666666666666696,0.733333333333333282],"xyz":[0.597252888234334312,0.681378369772196235,0.570207663013233779],"hpluv":[108.661729708420211,78.3375416693066313,86.0753247228475828],"hsluv":[108.661729708420211,23.7751141135998658,86.0753247228475828]},"#ccddcc":{"lch":[86.4592303781436158,14.0773589090278701,127.715012949224516],"luv":[86.4592303781436158,-8.61160385902942771,11.1360815742673065],"rgb":[0.8,0.866666666666666696,0.8],"xyz":[0.616545263678056066,0.689095319949685092,0.671814173683503935],"hpluv":[127.715012949224516,45.5363150686036633,86.4592303781436158],"hsluv":[127.715012949224516,10.5200784033656536,86.4592303781436158]},"#ccdddd":{"lch":[86.8840646031333677,9.47353258269296106,192.177050630057835],"luv":[86.8840646031333677,-9.26038246071566817,-1.99828333241680145],"rgb":[0.8,0.866666666666666696,0.866666666666666696],"xyz":[0.638063589228689665,0.697702650169938599,0.785144021583509932],"hpluv":[192.177050630057835,31.7497104564534354,86.8840646031333677],"hsluv":[192.177050630057835,13.7818607651735867,86.8840646031333677]},"#ccddee":{"lch":[87.3501331068194276,18.4943223550678582,237.507437159903361],"luv":[87.3501331068194276,-9.9349674143158051,-15.5992429896340141],"rgb":[0.8,0.866666666666666696,0.933333333333333348],"xyz":[0.661875769494864374,0.707227522276408616,0.910554837652033155],"hpluv":[237.507437159903361,64.5162574350921432,87.3501331068194276],"hsluv":[237.507437159903361,51.9237885878372083,87.3501331068194276]},"#ccddff":{"lch":[87.8575689716950876,31.3946063828198874,250.2096672964189],"luv":[87.8575689716950876,-10.6295595905023852,-29.5403752996496962],"rgb":[0.8,0.866666666666666696,1],"xyz":[0.688046817904695795,0.717695941640341339,1.04838902594381489],"hpluv":[250.2096672964189,114.576774718516177,87.8575689716950876],"hsluv":[250.2096672964189,99.999999999992923,87.8575689716950876]},"#556600":{"lch":[40.3019892206732919,46.2375853800199934,99.381148915360626],"luv":[40.3019892206732919,-7.53678920078613679,45.6191967302971264],"rgb":[0.333333333333333315,0.4,0],"xyz":[0.0849739168694777086,0.114339753133439206,0.0175932549799886241],"hpluv":[99.381148915360626,145.582103533726,40.3019892206732919],"hsluv":[99.381148915360626,100.000000000002302,40.3019892206732919]},"#556611":{"lch":[40.3683315206726334,43.7939991819964618,99.9948312384981364],"luv":[40.3683315206726334,-7.60085740022420797,43.1293557931736729],"rgb":[0.333333333333333315,0.4,0.0666666666666666657],"xyz":[0.0859855823691148269,0.114744419333294059,0.0229213599447442573],"hpluv":[99.9948312384981364,137.661701347396985,40.3683315206726334],"hsluv":[99.9948312384981364,94.2576138599214204,40.3683315206726334]},"#556622":{"lch":[40.4909010608148421,39.4175015547736436,101.28961011017671],"luv":[40.4909010608148421,-7.7166980000465113,38.6547797923705758],"rgb":[0.333333333333333315,0.4,0.133333333333333331],"xyz":[0.0878609405075918559,0.115494562588684874,0.0327982461407234499],"hpluv":[101.28961011017671,123.529583899053776,40.4909010608148421],"hsluv":[101.28961011017671,83.977954812826539,40.4909010608148421]},"#556633":{"lch":[40.6915589391927455,32.6300466430477343,104.01044750968255],"luv":[40.6915589391927455,-7.89969574444685207,31.6593548745491518],"rgb":[0.333333333333333315,0.4,0.2],"xyz":[0.0909486912400495895,0.116729662881667987,0.0490603999983346],"hpluv":[104.01044750968255,101.754281640855311,40.6915589391927455],"hsluv":[104.01044750968255,68.015525760737134,40.6915589391927455]},"#556644":{"lch":[40.9787805135115306,23.7356999706126039,110.078417815454017],"luv":[40.9787805135115306,-8.1486066005911173,22.2931304119395044],"rgb":[0.333333333333333315,0.4,0.266666666666666663],"xyz":[0.0954066864842684631,0.118512860979355567,0.0725391749512212386],"hpluv":[110.078417815454017,73.4991539757345436,40.9787805135115306],"hsluv":[110.078417815454017,46.8659194429644117,40.9787805135115306]},"#556655":{"lch":[41.3584605937638372,13.8246485480389332,127.71501294923371],"luv":[41.3584605937638372,-8.45701225317865202,10.9361716896901875],"rgb":[0.333333333333333315,0.4,0.333333333333333315],"xyz":[0.101369100523630462,0.120897826595100399,0.10394122222519514],"hpluv":[127.71501294923371,42.4159365242361659,41.3584605937638372],"hsluv":[127.71501294923371,21.5972717302380097,41.3584605937638372]},"#556666":{"lch":[41.8343160733152146,9.01834401177998402,192.177050630060307],"luv":[41.8343160733152146,-8.8154354231024481,-1.90226891261946607],"rgb":[0.333333333333333315,0.4,0.4],"xyz":[0.108954096297138633,0.123931824904503718,0.143888866632339102],"hpluv":[192.177050630060307,27.3547941496945484,41.8343160733152146],"hsluv":[192.177050630060307,27.2477194602778816,41.8343160733152146]},"#556677":{"lch":[42.4081371223492525,18.1914318380534858,239.570903430569189],"luv":[42.4081371223492525,-9.21344558994119289,-15.6856817728697795],"rgb":[0.333333333333333315,0.4,0.466666666666666674],"xyz":[0.118268109317340553,0.127657430112584541,0.192942668538737],"hpluv":[239.570903430569189,54.4323412384199443,42.4081371223492525],"hsluv":[239.570903430569189,33.1626844996427934,42.4081371223492525]},"#556688":{"lch":[43.0800011190655425,31.444407039181371,252.144687081813345],"luv":[43.0800011190655425,-9.64130614192028546,-29.9298504828121246],"rgb":[0.333333333333333315,0.4,0.533333333333333326],"xyz":[0.129408526419284381,0.132113596953362111,0.251615531942309179],"hpluv":[252.144687081813345,92.6204805766441694,43.0800011190655425],"hsluv":[252.144687081813345,39.0862788716996903,43.0800011190655425]},"#556699":{"lch":[43.8484890486876964,45.3842681536749,257.153237313451427],"luv":[43.8484890486876964,-10.090933534038701,-44.2482186789059639],"rgb":[0.333333333333333315,0.4,0.6],"xyz":[0.142465490528357108,0.137336382596991269,0.320382209583427113],"hpluv":[257.153237313451427,131.337888607863903,43.8484890486876964],"hsluv":[257.153237313451427,44.8182326355090055,43.8484890486876964]},"#5566aa":{"lch":[44.7109144588579,59.3119903927914223,259.748012643434],"luv":[44.7109144588579,-10.5562103110165033,-58.3650462882037928],"rgb":[0.333333333333333315,0.4,0.66666666666666663],"xyz":[0.157523183493609636,0.143359459783092391,0.399686059200425714],"hpluv":[259.748012643434,168.332615885742257,44.7109144588579],"hsluv":[259.748012643434,50.2202557252039341,44.7109144588579]},"#5566bb":{"lch":[45.6635615252987463,72.9454914642558805,261.300749278862554],"luv":[45.6635615252987463,-11.0328519112314041,-72.1063166696695532],"rgb":[0.333333333333333315,0.4,0.733333333333333282],"xyz":[0.174660775600946283,0.15021449662602715,0.48994404429906746],"hpluv":[261.300749278862554,202.706651650989187,45.6635615252987463],"hsluv":[261.300749278862554,56.1551480082100767,45.6635615252987463]},"#5566cc":{"lch":[46.7019230239281953,86.153038102066219,262.316955218783903],"luv":[46.7019230239281953,-11.5180515282988498,-85.3796255742992],"rgb":[0.333333333333333315,0.4,0.8],"xyz":[0.193953151044668037,0.157931446803515951,0.591550554969337616],"hpluv":[262.316955218783903,234.085848684940913,46.7019230239281953],"hsluv":[262.316955218783903,67.0801136297732086,46.7019230239281953]},"#5566dd":{"lch":[47.8209277182799539,98.8836589551542,263.023825475898036],"luv":[47.8209277182799539,-12.010072956506761,-98.1515978267222664],"rgb":[0.333333333333333315,0.4,0.866666666666666696],"xyz":[0.215471476595301581,0.166538777023769485,0.704880402869343614],"hpluv":[263.023825475898036,262.38914084011509,47.8209277182799539],"hsluv":[263.023825475898036,77.9738615315733909,47.8209277182799539]},"#5566ee":{"lch":[49.0151480019121095,111.133834597019145,263.537783903780621],"luv":[49.0151480019121095,-12.5078880087352609,-110.427722650603215],"rgb":[0.333333333333333315,0.4,0.933333333333333348],"xyz":[0.23928365686147629,0.176063649130239502,0.830291218937866837],"hpluv":[263.537783903780621,287.710232398192659,49.0151480019121095],"hsluv":[263.537783903780621,88.9133133505171145,49.0151480019121095]},"#5566ff":{"lch":[50.2789812841098751,122.927042369796439,263.924295565134457],"luv":[50.2789812841098751,-13.0108962765956715,-122.236550687040406],"rgb":[0.333333333333333315,0.4,1],"xyz":[0.265454705271307712,0.186532068494172198,0.968125407229648682],"hpluv":[263.924295565134457,310.2417842032321,50.2789812841098751],"hsluv":[263.924295565134457,99.99999999999919,50.2789812841098751]},"#ccee00":{"lch":[88.9159222564839382,101.512339113439552,98.3897184755654],"luv":[88.9159222564839382,-14.8112090817394879,100.426007975120399],"rgb":[0.8,0.933333333333333348,0],"xyz":[0.554744805843380595,0.739861178960639299,0.113583132474326151],"hpluv":[98.3897184755654,409.405376777149172,88.9159222564839382],"hsluv":[98.3897184755654,100.000000000002402,88.9159222564839382]},"#ccee11":{"lch":[88.9350466502285855,100.665090554083037,98.4749110233884863],"luv":[88.9350466502285855,-14.8356507509631186,99.5658773027044],"rgb":[0.8,0.933333333333333348,0.0666666666666666657],"xyz":[0.555756471343017755,0.740265845160494207,0.118911237439081788],"hpluv":[98.4749110233884863,406.753799510206477,88.9350466502285855],"hsluv":[98.4749110233884863,99.103544824997428,88.9350466502285855]},"#ccee22":{"lch":[88.9704797513205108,99.1029038393990191,98.635890718104875],"luv":[88.9704797513205108,-14.8807650249594605,97.9793262973016823],"rgb":[0.8,0.933333333333333348,0.133333333333333331],"xyz":[0.557631829481494701,0.741015988415885,0.12878812363506098],"hpluv":[98.635890718104875,401.844579090604725,88.9704797513205108],"hsluv":[98.635890718104875,97.4508266239822376,88.9704797513205108]},"#ccee33":{"lch":[89.0287677898111696,96.5543294248683281,98.9099371535565837],"luv":[89.0287677898111696,-14.95450175665089,95.3892101230339335],"rgb":[0.8,0.933333333333333348,0.2],"xyz":[0.560719580213952518,0.742251088708868134,0.145050277492672131],"hpluv":[98.9099371535565837,393.778440936160052,89.0287677898111696],"hsluv":[98.9099371535565837,94.7550356083218333,89.0287677898111696]},"#ccee44":{"lch":[89.1128082270408157,92.9258219449750698,99.3266297509707101],"luv":[89.1128082270408157,-15.0597883526499068,91.6973890518296],"rgb":[0.8,0.933333333333333348,0.266666666666666663],"xyz":[0.565177575458171377,0.7440342868065557,0.168529052445558769],"hpluv":[99.3266297509707101,382.168331694223468,89.1128082270408157],"hsluv":[99.3266297509707101,90.9176847425828782,89.1128082270408157]},"#ccee55":{"lch":[89.224999764075946,88.1655281542066263,99.9265688821158591],"luv":[89.224999764075946,-15.1984931464969293,86.8456456063635187],"rgb":[0.8,0.933333333333333348,0.333333333333333315],"xyz":[0.571139989497533307,0.74641925242230045,0.199931099719532657],"hpluv":[99.9265688821158591,366.702481299920066,89.224999764075946],"hsluv":[99.9265688821158591,85.8839575374920372,89.224999764075946]},"#ccee66":{"lch":[89.3673776950467555,82.2600687786622444,100.76990980050725],"luv":[89.3673776950467555,-15.3715621348797811,80.8111006793235873],"rgb":[0.8,0.933333333333333348,0.4],"xyz":[0.578724985271041548,0.749453250731703768,0.239878744126676646],"hpluv":[100.76990980050725,347.124780760482849,89.3673776950467555],"hsluv":[100.76990980050725,79.6381055931254451,89.3673776950467555]},"#ccee77":{"lch":[89.5416863487684651,75.2338064272131817,101.95104715507324],"luv":[89.5416863487684651,-15.5791079616514043,73.6031047215169707],"rgb":[0.8,0.933333333333333348,0.466666666666666674],"xyz":[0.588038998291243398,0.753178855939784619,0.288932546033074555],"hpluv":[101.95104715507324,323.225079834159544,89.5416863487684651],"hsluv":[101.95104715507324,72.2002225208687349,89.5416863487684651]},"#ccee88":{"lch":[89.7494222523926481,67.150335761217363,103.626883623375818],"luv":[89.7494222523926481,-15.8204941270879562,65.2600916212887654],"rgb":[0.8,0.933333333333333348,0.533333333333333326],"xyz":[0.599179415393187309,0.757635022780562162,0.347605409436646695],"hpluv":[103.626883623375818,294.840431689210504,89.7494222523926481],"hsluv":[103.626883623375818,63.6228180815060256,89.7494222523926481]},"#ccee99":{"lch":[89.9918618926625697,58.1187887690418705,106.076645299715267],"luv":[89.9918618926625697,-16.094429395756972,55.8458857070554586],"rgb":[0.8,0.933333333333333348,0.6],"xyz":[0.61223637950226,0.762857808424191375,0.41637208707776463],"hpluv":[106.076645299715267,261.881276027636488,89.9918618926625697],"hsluv":[106.076645299715267,53.9866751643902418,89.9918618926625697]},"#cceeaa":{"lch":[90.2700807835134071,48.3136880988356481,109.842100564752428],"luv":[90.2700807835134071,-16.3990756203548145,45.4453823452884791],"rgb":[0.8,0.933333333333333348,0.66666666666666663],"xyz":[0.627294072467512565,0.768880885610292442,0.495675936694763231],"hpluv":[109.842100564752428,224.429228200920562,90.2700807835134071],"hsluv":[109.842100564752428,43.3959332818477037,90.2700807835134071]},"#cceebb":{"lch":[90.5849674500281736,38.0375276056681,116.096591597387416],"luv":[90.5849674500281736,-16.7321662581560417,34.1597441246474745],"rgb":[0.8,0.933333333333333348,0.733333333333333282],"xyz":[0.644431664574849128,0.7757359224532272,0.585933921793405],"hpluv":[116.096591597387416,183.067580850930824,90.5849674500281736],"hsluv":[116.096591597387416,31.9725829176952736,90.5849674500281736]},"#cceecc":{"lch":[90.9372344233822,27.9388105970907,127.715012949233042],"luv":[90.9372344233822,-17.0911298567764973,22.1013668762570106],"rgb":[0.8,0.933333333333333348,0.8],"xyz":[0.663724040018570882,0.783452872630716057,0.687540432463675133],"hpluv":[127.715012949233042,140.086760618050221,90.9372344233822],"hsluv":[127.715012949233042,30.3633811556705,90.9372344233822]},"#cceedd":{"lch":[91.327427526889025,19.8359452431300802,151.749458426713772],"luv":[91.327427526889025,-17.4732117028180483,9.38890816213244861],"rgb":[0.8,0.933333333333333348,0.866666666666666696],"xyz":[0.685242365569204481,0.792060202850969564,0.800870280363681131],"hpluv":[151.749458426713772,104.258293798779974,91.327427526889025],"hsluv":[151.749458426713772,29.0871711351253381,91.327427526889025]},"#cceeee":{"lch":[91.7559342603931,18.2870370266976643,192.177050630059739],"luv":[91.7559342603931,-17.87558711202,-3.8573447624494035],"rgb":[0.8,0.933333333333333348,0.933333333333333348],"xyz":[0.709054545835379191,0.801585074957439581,0.926281096432204354],"hpluv":[192.177050630059739,101.458793010104,91.7559342603931],"hsluv":[192.177050630059739,27.4217374065650326,91.7559342603931]},"#cceeff":{"lch":[92.2229917973594553,25.3309747642836847,223.758903531404655],"luv":[92.2229917973594553,-18.2954610486514362,-17.5195430170439934],"rgb":[0.8,0.933333333333333348,1],"xyz":[0.735225594245210612,0.812053494321372304,1.06411528472398609],"hpluv":[223.758903531404655,149.53216426558339,92.2229917973594553],"hsluv":[223.758903531404655,99.9999999999890861,92.2229917973594553]},"#557700":{"lch":[45.8045523271613533,55.5294361467329836,107.801769483020877],"luv":[45.8045523271613533,-16.9767207468495087,52.8706840456749276],"rgb":[0.333333333333333315,0.466666666666666674,0],"xyz":[0.103427654922895337,0.151247229240274977,0.023744500997794328],"hpluv":[107.801769483020877,153.83457187868342,45.8045523271613533],"hsluv":[107.801769483020877,100.000000000002288,45.8045523271613533]},"#557711":{"lch":[45.859623178970125,53.470483995502839,108.496139146384877],"luv":[45.859623178970125,-16.9630166203079504,50.7084679896019068],"rgb":[0.333333333333333315,0.466666666666666674,0.0666666666666666657],"xyz":[0.104439320422532456,0.15165189544012983,0.0290726059625499578],"hpluv":[108.496139146384877,147.952722071756682,45.859623178970125],"hsluv":[108.496139146384877,95.6324690191426,45.859623178970125]},"#557722":{"lch":[45.9614512879267494,49.76586948312967,109.899607245517743],"luv":[45.9614512879267494,-16.93896350220896,46.7943723206405409],"rgb":[0.333333333333333315,0.466666666666666674,0.133333333333333331],"xyz":[0.106314678561009485,0.152402038695520659,0.0389494921585291573],"hpluv":[109.899607245517743,137.396980684000368,45.9614512879267494],"hsluv":[109.899607245517743,87.7486210093461239,45.9614512879267494]},"#557733":{"lch":[46.1283843073061,43.9801463057798472,112.602269297118809],"luv":[46.1283843073061,-16.9029726498973396,40.6022509803905223],"rgb":[0.333333333333333315,0.466666666666666674,0.2],"xyz":[0.109402429293467218,0.153637138988503757,0.0552116460161403],"hpluv":[112.602269297118809,120.983948144693443,46.1283843073061],"hsluv":[112.602269297118809,75.3373974156097432,46.1283843073061]},"#557744":{"lch":[46.3678258482665413,36.3216896494577526,117.654446368962965],"luv":[46.3678258482665413,-16.8582748313623227,32.1724060135666861],"rgb":[0.333333333333333315,0.466666666666666674,0.266666666666666663],"xyz":[0.113860424537686092,0.155420337086191351,0.0786904209690269391],"hpluv":[117.654446368962965,99.4005156049610861,46.3678258482665413],"hsluv":[117.654446368962965,58.5682493514162346,46.3678258482665413]},"#557755":{"lch":[46.6852246722490349,27.4798560002662136,127.715012949237362],"luv":[46.6852246722490349,-16.8103715694690621,21.7383047520213317],"rgb":[0.333333333333333315,0.466666666666666674,0.333333333333333315],"xyz":[0.119822838577048091,0.157805302701936184,0.110092468243000841],"hpluv":[127.715012949237362,74.6920408293198506,46.6852246722490349],"hsluv":[127.715012949237362,38.0315615795731219,46.6852246722490349]},"#557766":{"lch":[47.0844103757355299,19.3533005907945039,150.034269154893224],"luv":[47.0844103757355299,-16.7662346577491,9.66662397939114371],"rgb":[0.333333333333333315,0.466666666666666674,0.4],"xyz":[0.127407834350556276,0.160839301011339475,0.150040112650144802],"hpluv":[150.034269154893224,52.1575559127659147,47.0844103757355299],"hsluv":[150.034269154893224,41.6155983808333403,47.0844103757355299]},"#557777":{"lch":[47.5677829408255519,17.1184872206298522,192.177050630060563],"luv":[47.5677829408255519,-16.733329138647683,-3.61085871511915846],"rgb":[0.333333333333333315,0.466666666666666674,0.466666666666666674],"xyz":[0.136721847370758182,0.164564906219420298,0.199093914556542712],"hpluv":[192.177050630060563,45.665876663612373,47.5677829408255519],"hsluv":[192.177050630060563,45.4871270252879683,47.5677829408255519]},"#557788":{"lch":[48.1364533988364371,24.3189053895014062,226.569261606948],"luv":[48.1364533988364371,-16.7186932071720697,-17.6605338760746164],"rgb":[0.333333333333333315,0.466666666666666674,0.533333333333333326],"xyz":[0.147862264472702,0.169021073060197896,0.257766777960114879],"hpluv":[226.569261606948,64.1075619232690173,48.1364533988364371],"hsluv":[226.569261606948,49.4930397679002567,48.1364533988364371]},"#557799":{"lch":[48.7903733600049776,36.1922931768580227,242.470518495943224],"luv":[48.7903733600049776,-16.7282575252316086,-32.0943528610435749],"rgb":[0.333333333333333315,0.466666666666666674,0.6],"xyz":[0.160919228581774709,0.174243858703827054,0.326533455601232814],"hpluv":[242.470518495943224,94.128532149155447,48.7903733600049776],"hsluv":[242.470518495943224,53.4981409183259657,48.7903733600049776]},"#5577aa":{"lch":[49.5284680294880957,49.5215284063763121,250.210257263218182],"luv":[49.5284680294880957,-16.7664778987851584,-46.5968560588918308],"rgb":[0.333333333333333315,0.466666666666666674,0.66666666666666663],"xyz":[0.175976921547027265,0.180266935889928148,0.405837305218231414],"hpluv":[250.210257263218182,126.875705911364861,49.5284680294880957],"hsluv":[250.210257263218182,57.3943781585142503,49.5284680294880957]},"#5577bb":{"lch":[50.3487764471929324,63.2168000831305,254.554261837862072],"luv":[50.3487764471929324,-16.8362555209572413,-60.9336057753316069],"rgb":[0.333333333333333315,0.466666666666666674,0.733333333333333282],"xyz":[0.193114513654363912,0.187121972732862907,0.496095290316873161],"hpluv":[254.554261837862072,159.324628464713726,50.3487764471929324],"hsluv":[254.554261837862072,61.1035457400689737,50.3487764471929324]},"#5577cc":{"lch":[51.2485971738404942,76.8367273058076279,257.264224247337211],"luv":[51.2485971738404942,-16.9390629845235594,-74.9463195111900546],"rgb":[0.333333333333333315,0.466666666666666674,0.8],"xyz":[0.212406889098085638,0.194838922910351708,0.597701800987143317],"hpluv":[257.264224247337211,190.250673687597981,51.2485971738404942],"hsluv":[257.264224247337211,64.5753193228694329,51.2485971738404942]},"#5577dd":{"lch":[52.2246350466270428,90.1721178795433502,259.084439545341468],"luv":[52.2246350466270428,-17.075183181685393,-88.5406627612090347],"rgb":[0.333333333333333315,0.466666666666666674,0.866666666666666696],"xyz":[0.233925214648719182,0.203446253130605242,0.711031648887149315],"hpluv":[259.084439545341468,219.096874823476185,52.2246350466270428],"hsluv":[259.084439545341468,74.9139940771913757,52.2246350466270428]},"#5577ee":{"lch":[53.2731438322457791,103.123503170291656,260.373961187264626],"luv":[53.2731438322457791,-17.2439851141504441,-101.671539200978515],"rgb":[0.333333333333333315,0.466666666666666674,0.933333333333333348],"xyz":[0.257737394914893947,0.212971125237075287,0.836442464955672538],"hpluv":[260.373961187264626,245.63410016831412,53.2731438322457791],"hsluv":[260.373961187264626,87.3510385353649,53.2731438322457791]},"#5577ff":{"lch":[54.3900599484937572,115.652149768064447,261.324783585170792],"luv":[54.3900599484937572,-17.4441870719707097,-114.32899931064253],"rgb":[0.333333333333333315,0.466666666666666674,1],"xyz":[0.283908443324725313,0.223439544601007983,0.974276653247454383],"hpluv":[261.324783585170792,269.819603010977914,54.3900599484937572],"hsluv":[261.324783585170792,99.9999999999989768,54.3900599484937572]},"#ccff00":{"lch":[93.605159534834371,109.568762044642341,102.903766821995461],"luv":[93.605159534834371,-24.4682604068618268,106.801768939739262],"rgb":[0.8,1,0],"xyz":[0.606597178273054372,0.843565923819988406,0.130867256617550276],"hpluv":[102.903766821995461,795.170643052662513,93.605159534834371],"hsluv":[102.903766821995461,100.000000000002359,93.605159534834371]},"#ccff11":{"lch":[93.6226829283917681,108.793716578542018,103.00230683973929],"luv":[93.6226829283917681,-24.4775292120886689,106.004355243751533],"rgb":[0.8,1,0.0666666666666666657],"xyz":[0.607608843772691531,0.843970590019843314,0.136195361582305913],"hpluv":[103.00230683973929,791.823376913543598,93.6226829283917681],"hsluv":[103.00230683973929,99.9999999999883755,93.6226829283917681]},"#ccff22":{"lch":[93.6551518183817535,107.364125357399203,103.187929529698394],"luv":[93.6551518183817535,-24.4946701604029187,104.532609971684906],"rgb":[0.8,1,0.133333333333333331],"xyz":[0.609484201911168477,0.844720733275234115,0.146072247778285091],"hpluv":[103.187929529698394,785.615721446726184,93.6551518183817535],"hsluv":[103.187929529698394,99.9999999999883613,93.6551518183817535]},"#ccff33":{"lch":[93.7085695338431464,105.030410790236445,103.502196488744218],"luv":[93.7085695338431464,-24.5227776356263192,102.127472150246831],"rgb":[0.8,1,0.2],"xyz":[0.612571952643626294,0.845955833568217241,0.162334401635896242],"hpluv":[103.502196488744218,775.386532390006209,93.7085695338431464],"hsluv":[103.502196488744218,99.9999999999882334,93.7085695338431464]},"#ccff44":{"lch":[93.7856006692118456,101.704796230104463,103.975902087852319],"luv":[93.7856006692118456,-24.5631097372148304,98.6940687997238],"rgb":[0.8,1,0.266666666666666663],"xyz":[0.617029947887845154,0.847739031665904808,0.18581317658878288],"hpluv":[103.975902087852319,760.597322717251814,93.7856006692118456],"hsluv":[103.975902087852319,99.9999999999883329,93.7856006692118456]},"#ccff55":{"lch":[93.8884584724407887,97.3367975087049473,104.649262803016768],"luv":[93.8884584724407887,-24.6166025777849207,94.1725810773922802],"rgb":[0.8,1,0.333333333333333315],"xyz":[0.622992361927207083,0.850123997281649557,0.217215223862756796],"hpluv":[104.649262803016768,740.773636872505676,93.8884584724407887],"hsluv":[104.649262803016768,99.999999999988,93.8884584724407887]},"#ccff66":{"lch":[94.0190298395239381,91.9106503723783561,105.578857731119598],"luv":[94.0190298395239381,-24.6839280463924027,88.5340123798424514],"rgb":[0.8,1,0.4],"xyz":[0.630577357700715324,0.853157995591052876,0.257162868269900757],"hpluv":[105.578857731119598,715.472563222964368,94.0190298395239381],"hsluv":[105.578857731119598,99.9999999999875371,94.0190298395239381]},"#ccff77":{"lch":[94.1789424927264349,85.4453105292158597,106.848417981185563],"luv":[94.1789424927264349,-24.7655268674123263,81.7775627566241781],"rgb":[0.8,1,0.466666666666666674],"xyz":[0.639891370720917174,0.856883600799133727,0.306216670176298666],"hpluv":[106.848417981185563,684.262206540111833,94.1789424927264349],"hsluv":[106.848417981185563,99.9999999999873381,94.1789424927264349]},"#ccff88":{"lch":[94.3696051468525781,77.9966506735583778,108.587502777405675],"luv":[94.3696051468525781,-24.8616333733667219,73.92818611532],"rgb":[0.8,1,0.533333333333333326],"xyz":[0.651031787822861086,0.861339767639911269,0.364889533579870862],"hpluv":[108.587502777405675,646.714804777793233,94.3696051468525781],"hsluv":[108.587502777405675,99.9999999999870397,94.3696051468525781]},"#ccff99":{"lch":[94.5922333645115572,69.6635615227201868,111.00626449694991],"luv":[94.5922333645115572,-24.9722983929036886,65.0338074927618],"rgb":[0.8,1,0.6],"xyz":[0.66408875193193373,0.866562553283540482,0.433656211220988741],"hpluv":[111.00626449694991,602.43247210261859,94.5922333645115572],"hsluv":[111.00626449694991,99.999999999986656,94.5922333645115572]},"#ccffaa":{"lch":[94.8478672375315881,60.6030718965076574,114.464397640121035],"luv":[94.8478672375315881,-25.0974127381864349,55.1620539605116],"rgb":[0.8,1,0.66666666666666663],"xyz":[0.679146444897186341,0.872585630469641549,0.512960060837987397],"hpluv":[114.464397640121035,551.164539659931279,94.8478672375315881],"hsluv":[114.464397640121035,99.9999999999858886,94.8478672375315881]},"#ccffbb":{"lch":[95.1373841969384,51.0680528276179899,119.615591899534252],"luv":[95.1373841969384,-25.2367319023901224,44.3965469658538439],"rgb":[0.8,1,0.733333333333333282],"xyz":[0.696284037004522904,0.879440667312576307,0.603218045936629088],"hpluv":[119.615591899534252,493.192076617112832,95.1373841969384],"hsluv":[119.615591899534252,99.999999999985036,95.1373841969384]},"#ccffcc":{"lch":[95.4615088709507802,41.5047839306073527,127.715012949235671],"luv":[95.4615088709507802,-25.3899015983654444,32.8329101048280876],"rgb":[0.8,1,0.8],"xyz":[0.715576412448244659,0.887157617490065165,0.704824556606899244],"hpluv":[127.715012949235671,430.524428546451247,95.4615088709507802],"hsluv":[127.715012949235671,99.9999999999844,95.4615088709507802]},"#ccffdd":{"lch":[95.8208211701733603,32.8093273722257,141.163585516297758],"luv":[95.8208211701733603,-25.5564832849791728,20.5746962243056899],"rgb":[0.8,1,0.866666666666666696],"xyz":[0.737094737998878258,0.895764947710318671,0.818154404506905242],"hpluv":[141.163585516297758,370.598640927928557,95.8208211701733603],"hsluv":[141.163585516297758,99.9999999999829612,95.8208211701733603]},"#ccffee":{"lch":[96.2157633520208293,26.8716381406037179,163.283084738459195],"luv":[96.2157633520208293,-25.7359786647352209,7.72944619800421506],"rgb":[0.8,1,0.933333333333333348],"xyz":[0.760906918265053,0.905289819816788688,0.943565220575428465],"hpluv":[163.283084738459195,336.210724351278486,96.2157633520208293],"hsluv":[163.283084738459195,99.9999999999813411,96.2157633520208293]},"#ccffff":{"lch":[96.6466465538527899,26.5246444827845806,192.177050630060279],"luv":[96.6466465538527899,-25.9278521925209873,-5.59493034995961125],"rgb":[0.8,1,1],"xyz":[0.787077966674884388,0.915758239180721412,1.08139940886721031],"hpluv":[192.177050630060279,375.729722461639881,96.6466465538527899],"hsluv":[192.177050630060279,99.9999999999789111,96.6466465538527899]},"#558800":{"lch":[51.3121649295003266,65.2833526322192,113.133039202335894],"luv":[51.3121649295003266,-25.6477049188799029,60.0342515843809466],"rgb":[0.333333333333333315,0.533333333333333326,0],"xyz":[0.125500024647865804,0.195391968690216522,0.0311019575727842744],"hpluv":[113.133039202335894,161.443824642532,51.3121649295003266],"hsluv":[113.133039202335894,100.000000000002416,51.3121649295003266]},"#558811":{"lch":[51.3586018009433758,63.5282336791645363,113.757082680238781],"luv":[51.3586018009433758,-25.5929736747770171,58.1449582756467862],"rgb":[0.333333333333333315,0.533333333333333326,0.0666666666666666657],"xyz":[0.126511690147502937,0.195796634890071375,0.0364300625375399076],"hpluv":[113.757082680238781,156.961418592175818,51.3586018009433758],"hsluv":[113.757082680238781,96.6047658949560315,51.3586018009433758]},"#558822":{"lch":[51.4445144263052754,60.3557257759597761,114.985955444381503],"luv":[51.4445144263052754,-25.4940226670596211,54.707115096615226],"rgb":[0.333333333333333315,0.533333333333333326,0.133333333333333331],"xyz":[0.128387048285979938,0.196546778145462203,0.0463069487335191],"hpluv":[114.985955444381503,148.873956641671725,51.4445144263052754],"hsluv":[114.985955444381503,90.4397474873449454,51.4445144263052754]},"#558833":{"lch":[51.5854933484676366,55.3603846502330512,117.238150003797628],"luv":[51.5854933484676366,-25.3378965271949284,49.2215723864945502],"rgb":[0.333333333333333315,0.533333333333333326,0.2],"xyz":[0.131474799018437671,0.197781878438445302,0.0625691025911302434],"hpluv":[117.238150003797628,136.179218375975154,51.5854933484676366],"hsluv":[117.238150003797628,80.6386945119108844,51.5854933484676366]},"#558844":{"lch":[51.788002513998137,48.6519311113283521,121.094634958525788],"luv":[51.788002513998137,-25.1264429677671792,41.6614001762900799],"rgb":[0.333333333333333315,0.533333333333333326,0.266666666666666663],"xyz":[0.135932794262656559,0.199565076536132896,0.0860478775440168819],"hpluv":[121.094634958525788,119.20933058927335,51.788002513998137],"hsluv":[121.094634958525788,67.2069053574962112,51.788002513998137]},"#558855":{"lch":[52.0569745246593669,40.6501953066927229,127.715012949238655],"luv":[52.0569745246593669,-24.8671203906742271,32.1568764333225801],"rgb":[0.333333333333333315,0.533333333333333326,0.333333333333333315],"xyz":[0.141895208302018572,0.201950042151877729,0.117449924817990797],"hpluv":[127.715012949238655,99.0884509425644495,52.0569745246593669],"hsluv":[127.715012949238655,50.4536826414745647,52.0569745246593669]},"#558866":{"lch":[52.3961000606252156,32.296627997442414,139.535908661945541],"luv":[52.3961000606252156,-24.57168930668605,20.9595864611130054],"rgb":[0.333333333333333315,0.533333333333333326,0.4],"xyz":[0.149480204075526729,0.20498404046128102,0.157397569225134759],"hpluv":[139.535908661945541,78.2163498792783258,52.3961000606252156],"hsluv":[139.535908661945541,52.7717106037122505,52.3961000606252156]},"#558877":{"lch":[52.8079833364028417,25.6675371494617757,160.900154113051656],"luv":[52.8079833364028417,-24.2545338981935465,8.39881233868101873],"rgb":[0.333333333333333315,0.533333333333333326,0.466666666666666674],"xyz":[0.158794217095728663,0.208709645669361843,0.206451371131532668],"hpluv":[160.900154113051656,61.6770996704155934,52.8079833364028417],"hsluv":[160.900154113051656,55.3375604028527732,52.8079833364028417]},"#558888":{"lch":[53.2942460543653311,24.4817100724115,192.177050630060762],"luv":[53.2942460543653311,-23.9308828659180151,-5.16400748714853908],"rgb":[0.333333333333333315,0.533333333333333326,0.533333333333333326],"xyz":[0.169934634197672463,0.213165812510139441,0.265124234535104808],"hpluv":[192.177050630060762,58.2908991249141764,53.2942460543653311],"hsluv":[192.177050630060762,58.0627314448554515,53.2942460543653311]},"#558899":{"lch":[53.8556132197311399,30.5464388010487,219.367537814697016],"luv":[53.8556132197311399,-23.6152401502492637,-19.3753801529742589],"rgb":[0.333333333333333315,0.533333333333333326,0.6],"xyz":[0.18299159830674519,0.218388598153768598,0.333890912176222743],"hpluv":[219.367537814697016,71.9728892793730921,53.8556132197311399],"hsluv":[219.367537814697016,60.861676067449,53.8556132197311399]},"#5588aa":{"lch":[54.491995562986105,41.1624410064978434,235.49057853568965],"luv":[54.491995562986105,-23.3202411533326384,-33.9192114024460452],"rgb":[0.333333333333333315,0.533333333333333326,0.66666666666666663],"xyz":[0.198049291271997718,0.224411675339869693,0.413194761793221343],"hpluv":[235.49057853568965,95.8534470006528494,54.491995562986105],"hsluv":[235.49057853568965,63.6587877222075207,54.491995562986105]},"#5588bb":{"lch":[55.2025746803936102,53.7318677649407164,244.589939769846183],"luv":[55.2025746803936102,-23.0560079965343867,-48.5338449823709865],"rgb":[0.333333333333333315,0.533333333333333326,0.733333333333333282],"xyz":[0.215186883379334393,0.231266712182804451,0.503452746891863],"hpluv":[244.589939769846183,123.512789385845494,55.2025746803936102],"hsluv":[244.589939769846183,66.3923972476147,55.2025746803936102]},"#5588cc":{"lch":[55.9858924676882,67.0258402153595227,250.085698604565607],"luv":[55.9858924676882,-22.8299557310329213,-63.0179052166444436],"rgb":[0.333333333333333315,0.533333333333333326,0.8],"xyz":[0.234479258823056119,0.238983662360293253,0.60505925756213319],"hpluv":[250.085698604565607,151.915814945370954,55.9858924676882],"hsluv":[250.085698604565607,69.0160689386852653,55.9858924676882]},"#5588dd":{"lch":[56.8399439373084761,80.4799585659197589,253.656315856923044],"luv":[56.8399439373084761,-22.6469326722406379,-77.2278458285038596],"rgb":[0.333333333333333315,0.533333333333333326,0.866666666666666696],"xyz":[0.255997584373689691,0.247590992580546787,0.718389105462139188],"hpluv":[253.656315856923044,179.669115472769334,56.8399439373084761],"hsluv":[253.656315856923044,71.4978966707909791,56.8399439373084761]},"#5588ee":{"lch":[57.7622712344346212,93.8110643698785083,256.11666422549024],"luv":[57.7622712344346212,-22.5095618551803298,-91.0704969970917517],"rgb":[0.333333333333333315,0.533333333333333326,0.933333333333333348],"xyz":[0.2798097646398644,0.257115864687016804,0.843799921530662411],"hpluv":[256.11666422549024,206.086302990026525,57.7622712344346212],"hsluv":[256.11666422549024,85.5354286092897098,57.7622712344346212]},"#5588ff":{"lch":[58.7500561820581169,106.871298098059711,257.890974112124525],"luv":[58.7500561820581169,-22.4186692043534066,-104.493433421771144],"rgb":[0.333333333333333315,0.533333333333333326,1],"xyz":[0.305980813049695821,0.267584284050949528,0.981634109822444256],"hpluv":[257.890974112124525,230.829932612430156,58.7500561820581169],"hsluv":[257.890974112124525,99.9999999999988489,58.7500561820581169]},"#559900":{"lch":[56.7948235068901113,75.0667586450735769,116.650835958582277],"luv":[56.7948235068901113,-33.6713638042581849,67.0914116190665766],"rgb":[0.333333333333333315,0.6,0],"xyz":[0.151369625100333277,0.247131169595152217,0.0397251577236065259],"hpluv":[116.650835958582277,167.717444253109818,56.7948235068901113],"hsluv":[116.650835958582277,100.000000000002373,56.7948235068901113]},"#559911":{"lch":[56.8345345919718,73.5501475520203343,117.179331557793219],"luv":[56.8345345919718,-33.596019870913949,65.4288289193465431],"rgb":[0.333333333333333315,0.6,0.0666666666666666657],"xyz":[0.152381290599970409,0.24753583579500707,0.0450532626883621556],"hpluv":[117.179331557793219,164.214146288467191,56.8345345919718],"hsluv":[117.179331557793219,97.3073310640873501,56.8345345919718]},"#559922":{"lch":[56.9080340332033785,70.7973668034807559,118.203267177206257],"luv":[56.9080340332033785,-33.458907670269852,62.3920559351826256],"rgb":[0.333333333333333315,0.6,0.133333333333333331],"xyz":[0.154256648738447411,0.248285979050397898,0.0549301488843413552],"hpluv":[118.203267177206257,157.863907732936383,56.9080340332033785],"hsluv":[118.203267177206257,92.3971004449738444,56.9080340332033785]},"#559933":{"lch":[57.0287279601738675,66.4302504595433163,120.024921583566851],"luv":[57.0287279601738675,-33.2401456716524066,57.5158316626386181],"rgb":[0.333333333333333315,0.6,0.2],"xyz":[0.157344399470905172,0.249521079343381,0.0711923027419525],"hpluv":[120.024921583566851,147.812625691912672,57.0287279601738675],"hsluv":[120.024921583566851,84.5348632876942645,57.0287279601738675]},"#559944":{"lch":[57.2022813646338335,60.4886548808755862,122.993651447985457],"luv":[57.2022813646338335,-32.9388613723515604,50.7337045837447178],"rgb":[0.333333333333333315,0.6,0.266666666666666663],"xyz":[0.161802394715124032,0.251304277441068591,0.0946710776948391369],"hpluv":[122.993651447985457,134.183743453609452,57.2022813646338335],"hsluv":[122.993651447985457,73.6468983233054075,57.2022813646338335]},"#559955":{"lch":[57.4331244004656583,53.227141365646645,127.715012949239252],"luv":[57.4331244004656583,-32.5608701853658573,42.1060365117673499],"rgb":[0.333333333333333315,0.6,0.333333333333333315],"xyz":[0.167764808754486017,0.253689243056813396,0.126073124968813038],"hpluv":[127.715012949239252,117.600732304920115,57.4331244004656583],"hsluv":[127.715012949239252,59.8797334066369444,57.4331244004656583]},"#559966":{"lch":[57.7247064038159152,45.1954856350919556,135.286653522876605],"luv":[57.7247064038159152,-32.1175213159608433,31.7977475037242847],"rgb":[0.333333333333333315,0.6,0.4],"xyz":[0.175349804527994202,0.256723241366216715,0.166020769375957],"hpluv":[135.286653522876605,99.3510934192793371,57.7247064038159152],"hsluv":[135.286653522876605,61.4133131906424055,57.7247064038159152]},"#559977":{"lch":[58.0796295741768631,37.4448814339638858,147.624029462041108],"luv":[58.0796295741768631,-31.624170974627738,20.0507096076703846],"rgb":[0.333333333333333315,0.6,0.466666666666666674],"xyz":[0.184663817548196107,0.260448846574297566,0.215074571282354909],"hpluv":[147.624029462041108,81.8102930750829671,58.0796295741768631],"hsluv":[147.624029462041108,63.1432913181764448,58.0796295741768631]},"#559988":{"lch":[58.4997327930321092,31.9103493306277741,167.047427400638497],"luv":[58.4997327930321092,-31.0984204046450543,7.15252701768072],"rgb":[0.333333333333333315,0.6,0.533333333333333326],"xyz":[0.195804234650139963,0.264905013415075108,0.273747434685927105],"hpluv":[167.047427400638497,69.2176742514874093,58.4997327930321092],"hsluv":[167.047427400638497,65.0191416919107752,58.4997327930321092]},"#559999":{"lch":[58.9861545428406373,31.2617491707160085,192.177050630060933],"luv":[58.9861545428406373,-30.5583742056962677,-6.59414339527129822],"rgb":[0.333333333333333315,0.6,0.6],"xyz":[0.208861198759212663,0.270127799058704321,0.342514112327045],"hpluv":[192.177050630060933,67.2515837667627494,58.9861545428406373],"hsluv":[192.177050630060933,66.9883413382070216,58.9861545428406373]},"#5599aa":{"lch":[59.5393884281606915,36.5784761777074152,214.841775998693208],"luv":[59.5393884281606915,-30.0211576326403424,-20.8977274812183715],"rgb":[0.333333333333333315,0.6,0.66666666666666663],"xyz":[0.223918891724465219,0.276150876244805388,0.421817961944043585],"hpluv":[214.841775998693208,77.957978019602777,59.5393884281606915],"hsluv":[214.841775998693208,69.0007957908225,59.5393884281606915]},"#5599bb":{"lch":[60.1593377819191488,46.1560476826162613,230.269588518041076],"luv":[60.1593377819191488,-29.5018430010724479,-35.4967885479802],"rgb":[0.333333333333333315,0.6,0.733333333333333282],"xyz":[0.241056483831801838,0.283005913087740146,0.512075947042685331],"hpluv":[230.269588518041076,97.3564866347465312,60.1593377819191488],"hsluv":[230.269588518041076,71.0120526680823758,60.1593377819191488]},"#5599cc":{"lch":[60.8453721774886276,57.9560381686168284,239.960256152221575],"luv":[60.8453721774886276,-29.0128279276179057,-50.1712883614184761],"rgb":[0.333333333333333315,0.6,0.8],"xyz":[0.260348859275523592,0.290722863265228948,0.613682457712955487],"hpluv":[239.960256152221575,120.867757439807349,60.8453721774886276],"hsluv":[239.960256152221575,72.9851794318318667,60.8453721774886276]},"#5599dd":{"lch":[61.596386585270082,70.7674616346471623,246.194917025964173],"luv":[61.596386585270082,-28.563620381934669,-64.7468394355121],"rgb":[0.333333333333333315,0.6,0.866666666666666696],"xyz":[0.281867184826157136,0.29933019348548251,0.727012305612961485],"hpluv":[246.194917025964173,145.7866339341696,61.596386585270082],"hsluv":[246.194917025964173,74.8914393241793164,61.596386585270082]},"#5599ee":{"lch":[62.4108626997728209,83.9573424358243,250.401946751580681],"luv":[62.4108626997728209,-28.1609349474859023,-79.0935970339554757],"rgb":[0.333333333333333315,0.6,0.933333333333333348],"xyz":[0.305679365092331845,0.308855065591952527,0.852423121681484708],"hpluv":[250.401946751580681,170.701691169737018,62.4108626997728209],"hsluv":[250.401946751580681,83.3865807682554845,62.4108626997728209]},"#5599ff":{"lch":[63.2869312953637,97.1853240423286309,253.372761171991215],"luv":[63.2869312953637,-27.8089904224615658,-93.1216798650875859],"rgb":[0.333333333333333315,0.6,1],"xyz":[0.331850413502163266,0.319323484955885251,0.990257309973266553],"hpluv":[253.372761171991215,194.861470594675211,63.2869312953637],"hsluv":[253.372761171991215,99.9999999999986215,63.2869312953637]},"#440000":{"lch":[10.7708306123528814,36.2226426723970221,12.1770506300617765],"luv":[10.7708306123528814,35.407649887332731,7.64056094984030221],"rgb":[0.266666666666666663,0,0],"xyz":[0.0238384275584062923,0.0122916892098035059,0.00111742629180027137],"hpluv":[12.1770506300617765,426.746789183125145,10.7708306123528814],"hsluv":[12.1770506300617765,100.000000000002203,10.7708306123528814]},"#440011":{"lch":[11.0614468716721248,32.5827232355020158,4.73042674181564848],"luv":[11.0614468716721248,32.4717377839629151,2.68702414036008763],"rgb":[0.266666666666666663,0,0.0666666666666666657],"xyz":[0.024850093058043414,0.0126963554096583605,0.00644553125655590239],"hpluv":[4.73042674181564848,373.778888471265759,11.0614468716721248],"hsluv":[4.73042674181564848,99.999999999996831,11.0614468716721248]},"#440022":{"lch":[11.5842423793746683,28.6540476811609395,350.304317532446703],"luv":[11.5842423793746683,28.2447579299318896,-4.82577434136679706],"rgb":[0.266666666666666663,0,0.133333333333333331],"xyz":[0.0267254511965204326,0.0134464986650491784,0.0163224174525351],"hpluv":[350.304317532446703,313.875682434467763,11.5842423793746683],"hsluv":[350.304317532446703,99.9999999999974847,11.5842423793746683]},"#440033":{"lch":[12.4041921203750505,27.3919603751935874,328.642516788172941],"luv":[12.4041921203750505,23.3910134404871357,-14.2541216293086901],"rgb":[0.266666666666666663,0,0.2],"xyz":[0.0298132019289781731,0.0146815989580322912,0.0325845713101462417],"hpluv":[328.642516788172941,280.216663156604227,12.4041921203750505],"hsluv":[328.642516788172941,99.9999999999981668,12.4041921203750505]},"#440044":{"lch":[13.5105146335658439,30.7747615701782742,307.715012949243601],"luv":[13.5105146335658439,18.8259784531467211,-24.3447835271332202],"rgb":[0.266666666666666663,0,0.266666666666666663],"xyz":[0.0342711971731970502,0.0164647970557198695,0.0560633462630328802],"hpluv":[307.715012949243601,289.042783730483222,13.5105146335658439],"hsluv":[307.715012949243601,99.9999999999987779,13.5105146335658439]},"#440055":{"lch":[14.871657786523194,37.5926423334987589,293.358518425732086],"luv":[14.871657786523194,14.9048564440935021,-34.5116214049025416],"rgb":[0.266666666666666663,0,0.333333333333333315],"xyz":[0.0402336112125590423,0.0188497626714647,0.0874653935370067886],"hpluv":[293.358518425732086,320.761913781574776,14.871657786523194],"hsluv":[293.358518425732086,99.999999999999261,14.871657786523194]},"#440066":{"lch":[16.4463097679727497,46.0898544445027,284.618444278650202],"luv":[16.4463097679727497,11.6321972733021664,-44.597832562922008],"rgb":[0.266666666666666663,0,0.4],"xyz":[0.0478186069860672205,0.0218837609808680139,0.127413037944150764],"hpluv":[284.618444278650202,355.611827609674208,16.4463097679727497],"hsluv":[284.618444278650202,99.9999999999996447,16.4463097679727497]},"#440077":{"lch":[18.1919811936642475,55.3198462112611651,279.251207899416613],"luv":[18.1919811936642475,8.8934050845755,-54.6002997321373],"rgb":[0.266666666666666663,0,0.466666666666666674],"xyz":[0.0571326200062691331,0.0256093661889488303,0.176466839850548673],"hpluv":[279.251207899416613,385.869357778503058,18.1919811936642475],"hsluv":[279.251207899416613,100.000000000000028,18.1919811936642475]},"#440088":{"lch":[20.0701231572475791,64.8751824688912,275.807883046004235],"luv":[20.0701231572475791,6.56492582220058196,-64.5421648949036353],"rgb":[0.266666666666666663,0,0.533333333333333326],"xyz":[0.0682730371082129611,0.0300655330297264212,0.235139703254120841],"hpluv":[275.807883046004235,410.173767132845569,20.0701231572475791],"hsluv":[275.807883046004235,100.000000000000284,20.0701231572475791]},"#440099":{"lch":[22.0482755473713041,74.5764302852581267,273.495279820248],"luv":[22.0482755473713041,4.54664979059262,-74.4377050275844283],"rgb":[0.266666666666666663,0,0.6],"xyz":[0.0813300012172856746,0.0352883186733555787,0.303906380895238748],"hpluv":[273.495279820248,429.20649964036636,22.0482755473713041],"hsluv":[273.495279820248,100.000000000000384,22.0482755473713041]},"#4400aa":{"lch":[24.1003299188330615,84.3353594354864811,271.878389761009714],"luv":[24.1003299188330615,2.7643624238057809,-84.290041828810061],"rgb":[0.266666666666666663,0,0.66666666666666663],"xyz":[0.0963876941825382166,0.0413113958594566871,0.383210230512237349],"hpluv":[271.878389761009714,444.044033459252,24.1003299188330615],"hsluv":[271.878389761009714,100.000000000000398,24.1003299188330615]},"#4400bb":{"lch":[26.2058661049044161,94.1030045511215434,270.708980538846049],"luv":[26.2058661049044161,1.16440507249082947,-94.0958002589677704],"rgb":[0.266666666666666663,0,0.733333333333333282],"xyz":[0.113525286289874863,0.0481664327023914457,0.473468215610879095],"hpluv":[270.708980538846049,455.663559843794246,26.2058661049044161],"hsluv":[270.708980538846049,100.000000000000597,26.2058661049044161]},"#4400cc":{"lch":[28.3491756730399374,103.84959146554381,269.838851579513857],"luv":[28.3491756730399374,-0.292083914573144032,-103.849180711969012],"rgb":[0.266666666666666663,0,0.8],"xyz":[0.132817661733596604,0.0558833828798802473,0.575074726281149196],"hpluv":[269.838851579513857,464.840204698682,28.3491756730399374],"hsluv":[269.838851579513857,100.00000000000054,28.3491756730399374]},"#4400dd":{"lch":[30.5182871942398464,113.556060117994207,269.175711116484081],"luv":[30.5182871942398464,-1.63362415099869285,-113.544308803456943],"rgb":[0.266666666666666663,0,0.866666666666666696],"xyz":[0.154335987284230147,0.0644907131001337813,0.688404574181155193],"hpluv":[269.175711116484081,472.160320460814546,30.5182871942398464],"hsluv":[269.175711116484081,100.000000000000583,30.5182871942398464]},"#4400ee":{"lch":[32.7041215904695406,123.209994802530275,268.6598990566049],"luv":[32.7041215904695406,-2.88151723804096926,-123.176295112519156],"rgb":[0.266666666666666663,0,0.933333333333333348],"xyz":[0.178148167550404884,0.0740155852066038122,0.813815390249678416],"hpluv":[268.6598990566049,478.060407115886846,32.7041215904695406],"hsluv":[268.6598990566049,100.000000000000682,32.7041215904695406]},"#4400ff":{"lch":[34.8998090420324161,132.803387625161918,268.251574356178935],"luv":[34.8998090420324161,-4.05197057625710322,-132.741558297197031],"rgb":[0.266666666666666663,0,1],"xyz":[0.204319215960236278,0.0844840045705365084,0.951649578541460262],"hpluv":[268.251574356178935,482.864668803745815,34.8998090420324161],"hsluv":[268.251574356178935,100.000000000000824,34.8998090420324161]},"#441100":{"lch":[13.412021407860891,32.8203905090178907,19.8063713084711637],"luv":[13.412021407860891,30.878837797926078,11.1209446277644677],"rgb":[0.266666666666666663,0.0666666666666666657,0],"xyz":[0.025842827819334703,0.0163004897316603795,0.00178555971210972225],"hpluv":[19.8063713084711637,310.519467471828818,13.412021407860891],"hsluv":[19.8063713084711637,100.00000000000226,13.412021407860891]},"#441111":{"lch":[13.6534230745514442,29.3615880370140658,12.1770506300618155],"luv":[13.6534230745514442,28.7009658227648572,6.19333616848069],"rgb":[0.266666666666666663,0.0666666666666666657,0.0666666666666666657],"xyz":[0.0268544933189718248,0.0167051559315152323,0.00711366467686535414],"hpluv":[12.1770506300618155,272.883526996448495,13.6534230745514442],"hsluv":[12.1770506300618155,63.9450685777404857,13.6534230745514442]},"#441122":{"lch":[14.0908014406849,25.4444274015795777,356.558123350094036],"luv":[14.0908014406849,25.3985311285907684,-1.52758053936917793],"rgb":[0.266666666666666663,0.0666666666666666657,0.133333333333333331],"xyz":[0.0287298514574488434,0.0174552991869060536,0.0169905508728445502],"hpluv":[356.558123350094036,229.137575437366081,14.0908014406849],"hsluv":[356.558123350094036,68.4589860949294,14.0908014406849]},"#441133":{"lch":[14.7844111345271223,24.27011571268606,331.648240125609],"luv":[14.7844111345271223,21.358884082162259,-11.5254755854986932],"rgb":[0.266666666666666663,0.0666666666666666657,0.2],"xyz":[0.0318176021899065839,0.0186903994798891665,0.0332527047304557],"hpluv":[331.648240125609,208.308572243058649,14.7844111345271223],"hsluv":[331.648240125609,73.8494669394836762,14.7844111345271223]},"#441144":{"lch":[15.733846020816415,28.3218749128348932,307.715012949243942],"luv":[15.733846020816415,17.3254634530922189,-22.4043949865608347],"rgb":[0.266666666666666663,0.0666666666666666657,0.266666666666666663],"xyz":[0.0362755974341254644,0.0204735975775767395,0.0567314796833423354],"hpluv":[307.715012949243942,228.415952286272613,15.733846020816415],"hsluv":[307.715012949243942,79.0249627886423127,15.733846020816415]},"#441155":{"lch":[16.9210970319156715,36.0495621705848492,292.339268883647492],"luv":[16.9210970319156715,13.7020846665308547,-33.3440220801582683],"rgb":[0.266666666666666663,0.0666666666666666657,0.333333333333333315],"xyz":[0.0422380114734874565,0.0228585631933215724,0.0881335269573162439],"hpluv":[292.339268883647492,270.340308544513618,16.9210970319156715],"hsluv":[292.339268883647492,83.4150097743113292,16.9210970319156715]},"#441166":{"lch":[18.3175541838796221,45.305096001334384,283.521508601515677],"luv":[18.3175541838796221,10.592801308498224,-44.0493392019540195],"rgb":[0.266666666666666663,0.0666666666666666657,0.4],"xyz":[0.0498230072469956348,0.025892561502724884,0.128081171364460206],"hpluv":[283.521508601515677,313.84766122316239,18.3175541838796221],"hsluv":[283.521508601515677,86.9023417707839485,18.3175541838796221]},"#441177":{"lch":[19.8903241139664431,55.0653797371295326,278.304914930180701],"luv":[19.8903241139664431,7.95370312292975612,-54.4879312529533735],"rgb":[0.266666666666666663,0.0666666666666666657,0.466666666666666674],"xyz":[0.0591370202671975473,0.0296181667108057,0.177134973270858115],"hpluv":[278.304914930180701,351.298345972174616,19.8903241139664431],"hsluv":[278.304914930180701,89.590178354910762,19.8903241139664431]},"#441188":{"lch":[21.6068634066631873,64.9598488176177824,275.03199667533886],"luv":[21.6068634066631873,5.69776159856002,-64.7094851716016422],"rgb":[0.266666666666666663,0.0666666666666666657,0.533333333333333326],"xyz":[0.0702774373691413823,0.0340743335515832912,0.235807836674430282],"hpluv":[275.03199667533886,381.498298720028686,21.6068634066631873],"hsluv":[275.03199667533886,91.6417594317348545,21.6068634066631873]},"#441199":{"lch":[23.437698327746169,74.8628785955689153,272.864058339832695],"luv":[23.437698327746169,3.74063102269824954,-74.769367197849931],"rgb":[0.266666666666666663,0.0666666666666666657,0.6],"xyz":[0.0833344014782140818,0.0392971191952124557,0.304574514315548162],"hpluv":[272.864058339832695,405.313331644098525,23.437698327746169],"hsluv":[272.864058339832695,93.2101268622270283,23.437698327746169]},"#4411aa":{"lch":[25.3575925472355337,84.7316846097945273,271.361966301038933],"luv":[25.3575925472355337,2.01394993465393446,-84.7077468858332878],"rgb":[0.266666666666666663,0.0666666666666666657,0.66666666666666663],"xyz":[0.0983920944434666378,0.0453201963813135572,0.383878363932546762],"hpluv":[271.361966301038933,424.011024215555381,25.3575925472355337],"hsluv":[271.361966301038933,94.4180404350203872,25.3575925472355337]},"#4411bb":{"lch":[27.345710117802,94.5503656807627,270.282295464151048],"luv":[27.345710117802,0.465846378280231621,-94.5492180693092337],"rgb":[0.266666666666666663,0.0666666666666666657,0.733333333333333282],"xyz":[0.115529686550803284,0.0521752332242483158,0.474136349031188509],"hpluv":[270.282295464151048,438.746165283595928,27.345710117802],"hsluv":[270.282295464151048,95.3579307613671,27.345710117802]},"#4411cc":{"lch":[29.3852471201757481,104.311620276565549,269.482476007084244],"luv":[29.3852471201757481,-0.942181651797373232,-104.307365120864873],"rgb":[0.266666666666666663,0.0666666666666666657,0.8],"xyz":[0.134822061994525,0.0598921834017371174,0.575742859701458665],"hpluv":[269.482476007084244,450.445933463405538,29.3852471201757481],"hsluv":[269.482476007084244,96.097629526357963,29.3852471201757481]},"#4411dd":{"lch":[31.4628506304707116,114.011096091836691,268.874895171215144],"luv":[31.4628506304707116,-2.23866735310417164,-113.989115272223103],"rgb":[0.266666666666666663,0.0666666666666666657,0.866666666666666696],"xyz":[0.156340387545158555,0.0684995136219906514,0.689072707601464662],"hpluv":[268.874895171215144,459.820552568088715,31.4628506304707116],"hsluv":[268.874895171215144,96.6865382756064378,31.4628506304707116]},"#4411ee":{"lch":[33.5680052377948073,123.64577428881995,268.40345713539682],"luv":[33.5680052377948073,-3.44493495426529961,-123.597774747940718],"rgb":[0.266666666666666663,0.0666666666666666657,0.933333333333333348],"xyz":[0.180152567811333292,0.0780243857284606823,0.814483523669987886],"hpluv":[268.40345713539682,467.404695294247745,33.5680052377948073],"hsluv":[268.40345713539682,97.1606953094382817,33.5680052377948073]},"#4411ff":{"lch":[35.6924730299026081,133.21354134861582,268.030966648817412],"luv":[35.6924730299026081,-4.57713081064657,-133.134884505082312],"rgb":[0.266666666666666663,0.0666666666666666657,1],"xyz":[0.206323616221164685,0.0884928050923933784,0.952317711961769731],"hpluv":[268.030966648817412,473.599309181226886,35.6924730299026081],"hsluv":[268.030966648817412,99.999999999999531,35.6924730299026081]},"#99aa00":{"lch":[66.1528677227115907,74.3468963767982842,94.6234982733471384],"luv":[66.1528677227115907,-5.99293371363564553,74.1049643840839565],"rgb":[0.6,0.66666666666666663,0],"xyz":[0.275106719282890377,0.355217387920677,0.0540714229698005533],"hpluv":[94.6234982733471384,142.611154102436132,66.1528677227115907],"hsluv":[94.6234982733471384,100.000000000002217,66.1528677227115907]},"#99aa11":{"lch":[66.184052262829,73.0311244080130706,94.7508326892346275],"luv":[66.184052262829,-6.04863423256243316,72.7802112955118901],"rgb":[0.6,0.66666666666666663,0.0666666666666666657],"xyz":[0.276118384782527482,0.355622054120531828,0.059399527934556183],"hpluv":[94.7508326892346275,140.021252931940353,66.184052262829],"hsluv":[94.7508326892346275,98.1390759685954,66.184052262829]},"#99aa22":{"lch":[66.2417975711995695,70.6178785015108872,94.9968669170454376],"luv":[66.2417975711995695,-6.15090676716144458,70.3494926065273],"rgb":[0.6,0.66666666666666663,0.133333333333333331],"xyz":[0.277993742921004539,0.356372197375922628,0.0692764141305353826],"hpluv":[94.9968669170454376,135.276352185575888,66.2417975711995695],"hsluv":[94.9968669170454376,94.7283581868373403,66.2417975711995695]},"#99aa33":{"lch":[66.3366981156763273,66.7162457715457,95.432805623918],"luv":[66.3366981156763273,-6.31658226271684953,66.416550937003791],"rgb":[0.6,0.66666666666666663,0.2],"xyz":[0.281081493653462244,0.357607297668905755,0.0855385679881465189],"hpluv":[95.432805623918,127.619510800204154,66.3366981156763273],"hsluv":[95.432805623918,89.220332613511431,66.3366981156763273]},"#99aa44":{"lch":[66.4733277612737652,61.2361369282317085,96.1403021162386],"luv":[66.4733277612737652,-6.55002950525979344,60.8848222414533211],"rgb":[0.6,0.66666666666666663,0.266666666666666663],"xyz":[0.28553948889768116,0.359390495766593321,0.109017342941033157],"hpluv":[96.1403021162386,116.896010950353556,66.4733277612737652],"hsluv":[96.1403021162386,81.495914591183535,66.4733277612737652]},"#99aa55":{"lch":[66.6553605356915853,54.1785870660784568,97.2658616968833769],"luv":[66.6553605356915853,-6.85216039608048888,53.7435316515674231],"rgb":[0.6,0.66666666666666663,0.333333333333333315],"xyz":[0.291501902937043145,0.361775461382338182,0.140419390215007073],"hpluv":[97.2658616968833769,103.141138146130345,66.6553605356915853],"hsluv":[97.2658616968833769,71.5645731506566563,66.6553605356915853]},"#99aa66":{"lch":[66.8857782590742573,45.6318272725976186,99.1048787772859328],"luv":[66.8857782590742573,-7.22087825912546055,45.0568815765480224],"rgb":[0.6,0.66666666666666663,0.4],"xyz":[0.29908689871055133,0.3648094596917415,0.180367034622151035],"hpluv":[99.1048787772859328,86.5711928674077882,66.8857782590742573],"hsluv":[99.1048787772859328,59.5487850828384495,66.8857782590742573]},"#99aa77":{"lch":[67.16697930835997,35.7793898766585272,102.348317274789],"luv":[67.16697930835997,-7.65157453727816339,34.9516544250255947],"rgb":[0.6,0.66666666666666663,0.466666666666666674],"xyz":[0.308400911730753235,0.368535064899822296,0.229420836528548944],"hpluv":[102.348317274789,67.5952906506277742,67.16697930835997],"hsluv":[102.348317274789,45.6652750399669927,67.16697930835997]},"#99aa88":{"lch":[67.5008436530857523,24.9656779050761699,109.02352802026725],"luv":[67.5008436530857523,-8.13772239581267165,23.6021725158659379],"rgb":[0.6,0.66666666666666663,0.533333333333333326],"xyz":[0.319541328832697036,0.372991231740599893,0.288093699932121083],"hpluv":[109.02352802026725,46.9324783865521482,67.5008436530857523],"hsluv":[109.02352802026725,30.201776912068496,67.5008436530857523]},"#99aa99":{"lch":[67.8887769686822509,14.1753245834603039,127.715012949228395],"luv":[67.8887769686822509,-8.67153282620695443,11.213578621049983],"rgb":[0.6,0.66666666666666663,0.6],"xyz":[0.332598292941769791,0.378214017384229051,0.356860377573239],"hpluv":[127.715012949228395,26.4956363039349618,67.8887769686822509],"hsluv":[127.715012949228395,13.4910013502760879,67.8887769686822509]},"#99aaaa":{"lch":[68.3317447891119798,9.45739632834534838,192.177050630059227],"luv":[68.3317447891119798,-9.24460926466251642,-1.99487965930680966],"rgb":[0.6,0.66666666666666663,0.66666666666666663],"xyz":[0.347655985907022291,0.384237094570330173,0.436164227190237619],"hpluv":[192.177050630059227,17.5625836428344968,68.3317447891119798],"hsluv":[192.177050630059227,17.4938385380963979,68.3317447891119798]},"#99aabb":{"lch":[68.8303024285350205,18.6222464744475609,238.07162859052869],"luv":[68.8303024285350205,-9.84853629911531137,-15.8048852105949411],"rgb":[0.6,0.66666666666666663,0.733333333333333282],"xyz":[0.364793578014358966,0.391092131413264932,0.52642221228887931],"hpluv":[238.07162859052869,34.3314162301526622,68.8303024285350205],"hsluv":[238.07162859052869,21.6214176811966929,68.8303024285350205]},"#99aacc":{"lch":[69.3846237501618646,31.7868422871013969,250.758533684062627],"luv":[69.3846237501618646,-10.475354770609286,-30.0111693376804425],"rgb":[0.6,0.66666666666666663,0.8],"xyz":[0.38408595345808072,0.398809081590753733,0.628028722959149466],"hpluv":[250.758533684062627,58.1330991079694357,69.3846237501618646],"hsluv":[250.758533684062627,38.7837447081888556,69.3846237501618646]},"#99aadd":{"lch":[69.9945303682966,45.800012761152324,255.951164454091042],"luv":[69.9945303682966,-11.1178995355210173,-44.4300965432191859],"rgb":[0.6,0.66666666666666663,0.866666666666666696],"xyz":[0.405604279008714208,0.40741641181100724,0.741358570859155463],"hpluv":[255.951164454091042,83.0311057452140346,69.9945303682966],"hsluv":[255.951164454091042,58.1375555879370935,69.9945303682966]},"#99aaee":{"lch":[70.6595219654936,60.0690973767372896,258.700300270425373],"luv":[70.6595219654936,-11.7699993169592201,-58.9046990972259],"rgb":[0.6,0.66666666666666663,0.933333333333333348],"xyz":[0.429416459274889,0.416941283917477257,0.866769386927678687],"hpluv":[258.700300270425373,107.874733956899746,70.6595219654936],"hsluv":[258.700300270425373,78.5353046306170199,70.6595219654936]},"#99aaff":{"lch":[71.378807837336737,74.3523754541628676,260.378973553217],"luv":[71.378807837336737,-12.4265554124673407,-73.3065921746310636],"rgb":[0.6,0.66666666666666663,1],"xyz":[0.455587507684720339,0.42740970328141,1.00460357521946064],"hpluv":[260.378973553217,132.179737350703419,71.378807837336737],"hsluv":[260.378973553217,99.99999999999784,71.378807837336737]},"#442200":{"lch":[17.3350542344952459,28.221345162136295,35.6239292836567927],"luv":[17.3350542344952459,22.9399340369809366,16.4378754448194186],"rgb":[0.266666666666666663,0.133333333333333331,0],"xyz":[0.0295584515541026382,0.023731737201196354,0.00302410095703233277],"hpluv":[35.6239292836567927,206.581692971425213,17.3350542344952459],"hsluv":[35.6239292836567927,100.000000000002331,17.3350542344952459]},"#442211":{"lch":[17.5234603686317101,24.6335663377647,28.604455948929278],"luv":[17.5234603686317101,21.6269347365689519,11.7935696215031331],"rgb":[0.266666666666666663,0.133333333333333331,0.0666666666666666657],"xyz":[0.03057011705373976,0.0241364034010512069,0.00835220592178796337],"hpluv":[28.604455948929278,178.380241443971528,17.5234603686317101],"hsluv":[28.604455948929278,74.2089163354216765,17.5234603686317101]},"#442222":{"lch":[17.8672188947691239,19.9697171584766728,12.1770506300619807],"luv":[17.8672188947691239,19.520407715454283,4.21227800744330061],"rgb":[0.266666666666666663,0.133333333333333331,0.133333333333333331],"xyz":[0.0324454751922167786,0.0248865466564420282,0.0182290921177671594],"hpluv":[12.1770506300619807,141.825486578211439,17.8672188947691239],"hsluv":[12.1770506300619807,33.2341074785113406,17.8672188947691239]},"#442233":{"lch":[18.4184657925371695,17.8165085202964129,340.00784749027008],"luv":[18.4184657925371695,16.7428760354525643,-6.09131167444770583],"rgb":[0.266666666666666663,0.133333333333333331,0.2],"xyz":[0.0355332259246745191,0.026121646949425141,0.0344912459753783],"hpluv":[340.00784749027008,122.746318288975218,18.4184657925371695],"hsluv":[340.00784749027008,42.0888291674463915,18.4184657925371695]},"#442244":{"lch":[19.1844846541763658,22.4593922221866436,307.715012949244738],"luv":[19.1844846541763658,13.7391814744519145,-17.7668002578431441],"rgb":[0.266666666666666663,0.133333333333333331,0.266666666666666663],"xyz":[0.0399912211688934,0.0279048450471127141,0.0579700209282649412],"hpluv":[307.715012949244738,148.554970924606664,19.1844846541763658],"hsluv":[307.715012949244738,51.3954955066877517,19.1844846541763658]},"#442255":{"lch":[20.1595909359386596,31.6741463862469885,290.023039430837684],"luv":[20.1595909359386596,10.8451637140407797,-29.7596030436068872],"rgb":[0.266666666666666663,0.133333333333333331,0.333333333333333315],"xyz":[0.0459536352082553917,0.030289810662857547,0.0893720682022388496],"hpluv":[290.023039430837684,199.371249454156271,20.1595909359386596],"hsluv":[290.023039430837684,59.9942694866150177,20.1595909359386596]},"#442266":{"lch":[21.3287002071660226,42.3208084503031685,281.207510247893651],"luv":[21.3287002071660226,8.22559640374024781,-41.5137373852322256],"rgb":[0.266666666666666663,0.133333333333333331,0.4],"xyz":[0.0535386309817635631,0.0333238089722608585,0.129319712609382825],"hpluv":[281.207510247893651,251.784442505332123,21.3287002071660226],"hsluv":[281.207510247893651,67.3437899952651406,21.3287002071660226]},"#442277":{"lch":[22.6709734328231463,53.1747129073287113,276.391210783405313],"luv":[22.6709734328231463,5.91922219198187882,-52.8442324328661215],"rgb":[0.266666666666666663,0.133333333333333331,0.466666666666666674],"xyz":[0.0628526440019654825,0.0370494141803416749,0.178373514515780734],"hpluv":[276.391210783405313,297.628360698128461,22.6709734328231463],"hsluv":[276.391210783405313,73.3546851738733,22.6709734328231463]},"#442288":{"lch":[24.1630625686767715,63.8732961962266543,273.501787345358252],"luv":[24.1630625686767715,3.90136026108148481,-63.7540379512086801],"rgb":[0.266666666666666663,0.133333333333333331,0.533333333333333326],"xyz":[0.0739930611039093106,0.0415055810211192727,0.237046377919352902],"hpluv":[273.501787345358252,335.433687598002,24.1630625686767715],"hsluv":[273.501787345358252,78.1624380374364875,24.1630625686767715]},"#442299":{"lch":[25.7815797239733442,74.3421908873045112,271.638748742297366],"luv":[25.7815797239733442,2.1260128283266071,-74.3117851715204836],"rgb":[0.266666666666666663,0.133333333333333331,0.6],"xyz":[0.087050025212982024,0.0467283666647484303,0.305813055560470781],"hpluv":[271.638748742297366,365.902315457699217,25.7815797239733442],"hsluv":[271.638748742297366,81.974423252689391,25.7815797239733442]},"#4422aa":{"lch":[27.5046927955253,84.5952413694672885,270.370058715866037],"luv":[27.5046927955253,0.546375125789773097,-84.5934769150692176],"rgb":[0.266666666666666663,0.133333333333333331,0.66666666666666663],"xyz":[0.102107718178234566,0.0527514438508495317,0.385116905177469382],"hpluv":[270.370058715866037,390.281946032504152,27.5046927955253],"hsluv":[270.370058715866037,84.9950544768410765,27.5046927955253]},"#4422bb":{"lch":[29.3129652277151251,94.666070770970677,269.4687873747244],"luv":[29.3129652277151251,-0.87767531850734,-94.6620020982533],"rgb":[0.266666666666666663,0.133333333333333331,0.733333333333333282],"xyz":[0.119245310285571213,0.0596064806937842903,0.475374890276111128],"hpluv":[269.4687873747244,409.801860030343846,29.3129652277151251],"hsluv":[269.4687873747244,87.3984435941311517,29.3129652277151251]},"#4422cc":{"lch":[31.1896477872922873,104.586362634530047,268.806646243455475],"luv":[31.1896477872922873,-2.17816227055809408,-104.563678484665701],"rgb":[0.266666666666666663,0.133333333333333331,0.8],"xyz":[0.138537685729292953,0.0673234308712731,0.576981400946381284],"hpluv":[268.806646243455475,425.504262664631483,31.1896477872922873],"hsluv":[268.806646243455475,89.3235413687926325,31.1896477872922873]},"#4422dd":{"lch":[33.1206275871571,114.38069493574325,268.306702684458742],"luv":[33.1206275871571,-3.37987077956927,-114.330747603157818],"rgb":[0.266666666666666663,0.133333333333333331,0.866666666666666696],"xyz":[0.160056011279926497,0.0759307610915266329,0.690311248846387282],"hpluv":[268.306702684458742,438.22131720165,33.1206275871571],"hsluv":[268.306702684458742,90.877884626449017,33.1206275871571]},"#4422ee":{"lch":[35.0941942457284952,124.066768949832422,267.920557621455941],"luv":[35.0941942457284952,-4.50178121539094178,-123.985068147498538],"rgb":[0.266666666666666663,0.133333333333333331,0.933333333333333348],"xyz":[0.183868191546101234,0.0854556331979966499,0.815722064914910505],"hpluv":[267.920557621455941,448.600262181889605,35.0941942457284952],"hsluv":[267.920557621455941,93.3452679661598523,35.0941942457284952]},"#4422ff":{"lch":[37.1007304630435,133.656986375695226,267.616535799179189],"luv":[37.1007304630435,-5.55843332369103926,-133.54135623850334],"rgb":[0.266666666666666663,0.133333333333333331,1],"xyz":[0.210039239955932627,0.0959240525619293599,0.95355625320669235],"hpluv":[267.616535799179189,457.139270395610822,37.1007304630435],"hsluv":[267.616535799179189,99.999999999999531,37.1007304630435]},"#bbaa00":{"lch":[68.9787767407419,76.4078567958722346,76.7962953219783344],"luv":[68.9787767407419,17.4526104568015299,74.3879490803561652],"rgb":[0.733333333333333282,0.66666666666666663,0],"xyz":[0.348671553863065253,0.393149255751080451,0.0575197745907461769],"hpluv":[76.7962953219783344,140.560034871296551,68.9787767407419],"hsluv":[76.7962953219783344,100.000000000002245,68.9787767407419]},"#bbaa11":{"lch":[69.0079227935645747,75.1793224892161902,76.6760735029220797],"luv":[69.0079227935645747,17.325534429098667,73.1556996185784811],"rgb":[0.733333333333333282,0.66666666666666663,0.0666666666666666657],"xyz":[0.349683219362702358,0.393553921950935304,0.0628478795555018],"hpluv":[76.6760735029220797,138.241608756115426,69.0079227935645747],"hsluv":[76.6760735029220797,98.327691673723,69.0079227935645747]},"#bbaa22":{"lch":[69.0618990033698168,72.923722885125926,76.4446579961253718],"luv":[69.0618990033698168,17.0921879270318975,70.8923583420220496],"rgb":[0.733333333333333282,0.66666666666666663,0.133333333333333331],"xyz":[0.351558577501179415,0.394304065206326104,0.072724765751481],"hpluv":[76.4446579961253718,133.98915323660276,69.0618990033698168],"hsluv":[76.4446579961253718,95.2591470849694701,69.0618990033698168]},"#bbaa33":{"lch":[69.1506211183474448,69.2704933393546298,76.037430434814425],"luv":[69.1506211183474448,16.7141363744442764,67.2237970716773248],"rgb":[0.733333333333333282,0.66666666666666663,0.2],"xyz":[0.35464632823363712,0.395539165499309231,0.0889869196090921494],"hpluv":[76.037430434814425,127.113454601419591,69.1506211183474448],"hsluv":[76.037430434814425,90.2940538071316752,69.1506211183474448]},"#bbaa44":{"lch":[69.2783900215706439,64.125663575375043,75.3840533289293688],"luv":[69.2783900215706439,16.1813855024742246,62.0504914743025964],"rgb":[0.733333333333333282,0.66666666666666663,0.266666666666666663],"xyz":[0.359104323477856036,0.397322363596996797,0.112465694561978788],"hpluv":[75.3840533289293688,117.455514697890109,69.2783900215706439],"hsluv":[75.3840533289293688,83.3108908827552455,69.2783900215706439]},"#bbaa55":{"lch":[69.4486803199850158,57.4759826537307603,74.3632861967268752],"luv":[69.4486803199850158,15.4919003466967737,55.3487994961044762],"rgb":[0.733333333333333282,0.66666666666666663,0.333333333333333315],"xyz":[0.365066737517218,0.399707329212741658,0.143867741835952689],"hpluv":[74.3632861967268752,105.017514149864908,69.4486803199850158],"hsluv":[74.3632861967268752,74.2978448337225501,69.4486803199850158]},"#bbaa66":{"lch":[69.6643364030220766,49.3851711615203186,72.7428070953876],"luv":[69.6643364030220766,14.6506772614363818,47.1619845472377079],"rgb":[0.733333333333333282,0.66666666666666663,0.4],"xyz":[0.372651733290726206,0.402741327522145,0.183815386243096651],"hpluv":[72.7428070953876,89.9550160304510342,69.6643364030220766],"hsluv":[72.7428070953876,63.3404983901227254,69.6643364030220766]},"#bbaa77":{"lch":[69.927675331165787,39.998456174204172,70.017617272721],"luv":[69.927675331165787,13.6687200676018641,37.5904587366697314],"rgb":[0.733333333333333282,0.66666666666666663,0.466666666666666674],"xyz":[0.381965746310928111,0.406466932730225772,0.23286918814949456],"hpluv":[70.017617272721,72.5827577171275635,69.927675331165787],"hsluv":[70.017617272721,50.6076787731359303,69.927675331165787]},"#bbaa88":{"lch":[70.2405480333122,29.5797452752191568,64.8698942165899837],"luv":[70.2405480333122,12.5617839686329518,26.7798975739685403],"rgb":[0.733333333333333282,0.66666666666666663,0.533333333333333326],"xyz":[0.393106163412871912,0.41092309957100337,0.291542051553066728],"hpluv":[64.8698942165899837,53.4374671266607422,70.2405480333122],"hsluv":[64.8698942165899837,36.3337734813298923,70.2405480333122]},"#bbaa99":{"lch":[70.6043801086793,18.7359983934483161,52.7186742094296079],"luv":[70.6043801086793,11.3489394865138475,14.907689570511744],"rgb":[0.733333333333333282,0.66666666666666663,0.6],"xyz":[0.406163127521944611,0.416145885214632527,0.360308729194184663],"hpluv":[52.7186742094296079,33.6732102985619051,70.6043801086793],"hsluv":[52.7186742094296079,20.7983697782308,70.6043801086793]},"#bbaaaa":{"lch":[71.0202025015365876,10.2824341490213804,12.177050630064155],"luv":[71.0202025015365876,10.0510841141786607,2.16890759569567715],"rgb":[0.733333333333333282,0.66666666666666663,0.66666666666666663],"xyz":[0.421220820487197167,0.422168962400733649,0.439612578811183263],"hpluv":[12.177050630064155,18.3718697222702261,71.0202025015365876],"hsluv":[12.177050630064155,12.5020210815887047,71.0202025015365876]},"#bbaabb":{"lch":[71.4886774714552615,14.2047670368859897,307.715012949256788],"luv":[71.4886774714552615,8.68954378602241562,-11.236869464535129],"rgb":[0.733333333333333282,0.66666666666666663,0.733333333333333282],"xyz":[0.438358412594533842,0.429023999243668408,0.529870563909825],"hpluv":[307.715012949256788,25.2136766683934965,71.4886774714552615],"hsluv":[307.715012949256788,14.1682246563909,71.4886774714552615]},"#bbaacc":{"lch":[72.010122844957138,26.1519063744379174,286.174295641089373],"luv":[72.010122844957138,7.28488198445553525,-25.1167812724863673],"rgb":[0.733333333333333282,0.66666666666666663,0.8],"xyz":[0.45765078803825554,0.43674094942115721,0.631477074580095166],"hpluv":[286.174295641089373,46.0838895939184923,72.010122844957138],"hsluv":[286.174295641089373,33.6938059680335442,72.010122844957138]},"#bbaadd":{"lch":[72.5845361735228494,39.7272427931479,278.476565574149959],"luv":[72.5845361735228494,5.85598959517198825,-39.2932717625671799],"rgb":[0.733333333333333282,0.66666666666666663,0.866666666666666696],"xyz":[0.47916911358888914,0.445348279641410716,0.744806922480101163],"hpluv":[278.476565574149959,69.451822258827633,72.5845361735228494],"hsluv":[278.476565574149959,54.5084651382981775,72.5845361735228494]},"#bbaaee":{"lch":[73.2116196006959825,53.7916684525604,274.71268656160521],"luv":[73.2116196006959825,4.41947960396952144,-53.6098106221266093],"rgb":[0.733333333333333282,0.66666666666666663,0.933333333333333348],"xyz":[0.502981293855063849,0.454873151747880733,0.870217738548624387],"hpluv":[274.71268656160521,93.2340021292866084,73.2116196006959825],"hsluv":[274.71268656160521,76.5987468488048506,73.2116196006959825]},"#bbaaff":{"lch":[73.8908057188696574,67.9996555049746121,272.519622378571285],"luv":[73.8908057188696574,2.98936916429642707,-67.9339150998584671],"rgb":[0.733333333333333282,0.66666666666666663,1],"xyz":[0.52915234226489527,0.465341571111813457,1.00805192684040623],"hpluv":[272.519622378571285,116.776549017196231,73.8908057188696574],"hsluv":[272.519622378571285,99.9999999999974847,73.8908057188696574]},"#99bb00":{"lch":[71.0859361318702696,82.3913749493211327,101.26222245755217],"luv":[71.0859361318702696,-16.0909976916487061,80.8048170552163185],"rgb":[0.6,0.733333333333333282,0],"xyz":[0.309061217191489157,0.423126383737875533,0.0653895889393331653],"hpluv":[101.26222245755217,147.074503152999483,71.0859361318702696],"hsluv":[101.26222245755217,100.000000000002302,71.0859361318702696]},"#99bb11":{"lch":[71.1136894680978457,81.2167121813616291,101.442926834262664],"luv":[71.1136894680978457,-16.1127230849120338,79.6023523040561685],"rgb":[0.6,0.733333333333333282,0.0666666666666666657],"xyz":[0.310072882691126261,0.423531049937730386,0.0707176939040888],"hpluv":[101.442926834262664,144.921066000062638,71.1136894680978457],"hsluv":[101.442926834262664,98.4331733262411,71.1136894680978457]},"#99bb22":{"lch":[71.1650900316793837,79.0601549481488917,101.789083890819583],"luv":[71.1650900316793837,-16.1527448632600859,77.392486287802754],"rgb":[0.6,0.733333333333333282,0.133333333333333331],"xyz":[0.311948240829603318,0.424281193193121187,0.080594580100068],"hpluv":[101.789083890819583,140.971066242853169,71.1650900316793837],"hsluv":[101.789083890819583,95.5563304227746357,71.1650900316793837]},"#99bb33":{"lch":[71.2495884989653,75.5680342451554168,102.392870056849247],"luv":[71.2495884989653,-16.2179420391938294,73.807222923575253],"rgb":[0.6,0.733333333333333282,0.2],"xyz":[0.315035991562061,0.425516293486104313,0.0968567339576791309],"hpluv":[102.392870056849247,134.584514176993849,71.2495884989653],"hsluv":[102.392870056849247,90.8963478676004826,71.2495884989653]},"#99bb44":{"lch":[71.3712967033779648,70.652786516275043,103.347443045401377],"luv":[71.3712967033779648,-16.3105834167977157,68.7443169368786471],"rgb":[0.6,0.733333333333333282,0.266666666666666663],"xyz":[0.319493986806279939,0.427299491583791879,0.120335508910565769],"hpluv":[103.347443045401377,125.616021445862458,71.3712967033779648],"hsluv":[103.347443045401377,84.331672265085615,71.3712967033779648]},"#99bb55":{"lch":[71.5335490617920442,64.3081830776187502,104.804237401434776],"luv":[71.5335490617920442,-16.4318507493525949,62.1734404042074189],"rgb":[0.6,0.733333333333333282,0.333333333333333315],"xyz":[0.325456400845641924,0.42968445719953674,0.151737556184539685],"hpluv":[104.804237401434776,114.076396323867073,71.5335490617920442],"hsluv":[104.804237401434776,75.8403293762878263,71.5335490617920442]},"#99bb66":{"lch":[71.7390905918245494,56.6109161874071347,107.032342660002968],"luv":[71.7390905918245494,-16.5819871692753225,54.1279367156704367],"rgb":[0.6,0.733333333333333282,0.4],"xyz":[0.33304139661915011,0.432718455508940059,0.191685200591683647],"hpluv":[107.032342660002968,100.134479122606095,71.7390905918245494],"hsluv":[107.032342660002968,65.4891515707011,71.7390905918245494]},"#99bb77":{"lch":[71.9901757540345102,47.73532562127712,110.555323727161408],"luv":[71.9901757540345102,-16.7604289857032249,44.6961892378372099],"rgb":[0.6,0.733333333333333282,0.466666666666666674],"xyz":[0.342355409639352,0.436444060717020854,0.240739002498081556],"hpluv":[110.555323727161408,84.1406731320706,71.9901757540345102],"hsluv":[110.555323727161408,53.4218452589237,71.9901757540345102]},"#99bb88":{"lch":[72.2886271035685581,38.007890289968195,116.511646356383693],"luv":[72.2886271035685581,-16.9659512259030869,34.0111191126451473],"rgb":[0.6,0.733333333333333282,0.533333333333333326],"xyz":[0.353495826741295815,0.440900227557798452,0.299411865901653695],"hpluv":[116.511646356383693,66.7180145269523592,72.2886271035685581],"hsluv":[116.511646356383693,39.8440619041218724,72.2886271035685581]},"#99bb99":{"lch":[72.6358740128595315,28.1115962970155238,127.715012949234961],"luv":[72.6358740128595315,-17.1968288028560856,22.2380512970816788],"rgb":[0.6,0.733333333333333282,0.6],"xyz":[0.366552790850368515,0.44612301320142761,0.36817854354277163],"hpluv":[127.715012949234961,49.1104221429672094,72.6358740128595315],"hsluv":[127.715012949234961,25.0059581073328268,72.6358740128595315]},"#99bbaa":{"lch":[73.0329812495468929,19.8989345424194468,151.280397210880039],"luv":[73.0329812495468929,-17.4510036711552878,9.56190706882358654],"rgb":[0.6,0.733333333333333282,0.66666666666666663],"xyz":[0.381610483815621071,0.452146090387528732,0.447482393159770231],"hpluv":[151.280397210880039,34.5740392557976648,73.0329812495468929],"hsluv":[151.280397210880039,28.0327457551748473,73.0329812495468929]},"#99bbbb":{"lch":[73.4806726048520602,18.1342571597367979,192.177050630060279],"luv":[73.4806726048520602,-17.7262447217336288,-3.82511840348453136],"rgb":[0.6,0.733333333333333282,0.733333333333333282],"xyz":[0.398748075922957745,0.45900112723046349,0.537740378258412],"hpluv":[192.177050630060279,31.3159775944509384,73.4806726048520602],"hsluv":[192.177050630060279,31.1933976709334857,73.4806726048520602]},"#99bbcc":{"lch":[73.9793524714423,25.2831678504957686,224.541830016242],"luv":[73.9793524714423,-18.0202882726228886,-17.734367401402082],"rgb":[0.6,0.733333333333333282,0.8],"xyz":[0.418040451366679444,0.466718077407952292,0.639346888928682189],"hpluv":[224.541830016242,43.3670907410057183,73.9793524714423],"hsluv":[224.541830016242,34.4349286550277398,73.9793524714423]},"#99bbdd":{"lch":[74.5291269946933284,36.8692317824205347,240.185846709470951],"luv":[74.5291269946933284,-18.3309507682094441,-31.9893184697537762],"rgb":[0.6,0.733333333333333282,0.866666666666666696],"xyz":[0.439558776917313043,0.475325407628205798,0.752676736828688187],"hpluv":[240.185846709470951,62.7736499155611796,74.5291269946933284],"hsluv":[240.185846709470951,50.9371920612126914,74.5291269946933284]},"#99bbee":{"lch":[75.129825653719962,50.0395173115419851,248.109771752659725],"luv":[75.129825653719962,-18.6562099892881648,-46.4316607672792],"rgb":[0.6,0.733333333333333282,0.933333333333333348],"xyz":[0.463370957183487753,0.484850279734675815,0.87808755289721141],"hpluv":[248.109771752659725,84.5162185097210568,75.129825653719962],"hsluv":[248.109771752659725,74.708817528734329,75.129825653719962]},"#99bbff":{"lch":[75.7810236401202104,63.8171103470632701,252.684342225014],"luv":[75.7810236401202104,-18.9942553989637197,-60.9248868270445527],"rgb":[0.6,0.733333333333333282,1],"xyz":[0.489542005593319174,0.495318699098608539,1.01592174118899314],"hpluv":[252.684342225014,106.860202909060845,75.7810236401202104],"hsluv":[252.684342225014,99.9999999999971,75.7810236401202104]},"#443300":{"lch":[22.2907133772276609,26.4379209369795269,61.2454831359909],"luv":[22.2907133772276609,12.7181702882918319,23.1778300966244757],"rgb":[0.266666666666666663,0.2,0],"xyz":[0.0356761736431134499,0.0359671813792181508,0.00506334165336921362],"hpluv":[61.2454831359909,150.502134175174433,22.2907133772276609],"hsluv":[61.2454831359909,100.000000000002217,22.2907133772276609]},"#443311":{"lch":[22.433780901835803,22.5495741902090607,57.6291729330006959],"luv":[22.433780901835803,12.072970412625418,19.0453847841311372],"rgb":[0.266666666666666663,0.2,0.0666666666666666657],"xyz":[0.0366878391427505751,0.0363718475790730036,0.0103914466181248451],"hpluv":[57.6291729330006959,127.548453681164943,22.433780901835803],"hsluv":[57.6291729330006959,82.4396262904162853,22.433780901835803]},"#443322":{"lch":[22.6962080128251955,16.3034653251506185,47.6268315603120129],"luv":[22.6962080128251955,10.9878263584328284,12.044527949459761],"rgb":[0.266666666666666663,0.2,0.133333333333333331],"xyz":[0.0385631972812275903,0.037121990834463825,0.0202683328141040411],"hpluv":[47.6268315603120129,91.1519465603444,22.6962080128251955],"hsluv":[47.6268315603120129,53.1527363908354289,22.6962080128251955]},"#443333":{"lch":[23.1206934094119845,9.67437860897999613,12.1770506300626202],"luv":[23.1206934094119845,9.4567095438703852,2.04064844418642899],"rgb":[0.266666666666666663,0.2,0.2],"xyz":[0.0416509480136853308,0.0383570911274469378,0.0365304866717151844],"hpluv":[12.1770506300626202,53.0959690287213917,23.1206934094119845],"hsluv":[12.1770506300626202,12.4420312875371923,23.1206934094119845]},"#443344":{"lch":[23.7177668131648574,12.5269391023357528,307.715012949247694],"luv":[23.7177668131648574,7.66315882209806354,-9.90960141180911],"rgb":[0.266666666666666663,0.2,0.266666666666666663],"xyz":[0.0461089432579042113,0.0401402892251345109,0.0600092616246018229],"hpluv":[307.715012949247694,67.0209373905010608,23.7177668131648574],"hsluv":[307.715012949247694,23.1872031280306743,23.7177668131648574]},"#443355":{"lch":[24.4893027034144382,23.4497607107203301,284.299245815683662],"luv":[24.4893027034144382,5.79176863833153277,-22.7232632654309157],"rgb":[0.266666666666666663,0.2,0.333333333333333315],"xyz":[0.0520713572972662,0.0425252548408793438,0.0914113088985757383],"hpluv":[284.299245815683662,121.507006770462795,24.4893027034144382],"hsluv":[284.299245815683662,34.0172562824217479,24.4893027034144382]},"#443366":{"lch":[25.4301832846655458,35.8361728283181407,276.366541048647719],"luv":[25.4301832846655458,3.97382245046802396,-35.6151655634680964],"rgb":[0.266666666666666663,0.2,0.4],"xyz":[0.0596563530707743817,0.0455592531502826553,0.1313589533057197],"hpluv":[276.366541048647719,178.818092823782393,25.4301832846655458],"hsluv":[276.366541048647719,44.0521665839457555,25.4301832846655458]},"#443377":{"lch":[26.5300434901181958,48.2102429207408818,272.708376965990851],"luv":[26.5300434901181958,2.27805419202829862,-48.1563909733176416],"rgb":[0.266666666666666663,0.2,0.466666666666666674],"xyz":[0.0689703660909762872,0.0492848583583634717,0.180412755212117609],"hpluv":[272.708376965990851,230.590114205629249,26.5300434901181958],"hsluv":[272.708376965990851,52.8563285469964583,26.5300434901181958]},"#443388":{"lch":[27.7750487339787355,60.1896827108213373,270.691543734184165],"luv":[27.7750487339787355,0.726454682878815761,-60.1852986070773568],"rgb":[0.266666666666666663,0.2,0.533333333333333326],"xyz":[0.0801107831929201153,0.0537410251991410626,0.239085618615689777],"hpluv":[270.691543734184165,274.983446475144433,27.7750487339787355],"hsluv":[270.691543734184165,60.324174233719404,27.7750487339787355]},"#443399":{"lch":[29.1495234429961272,71.6961686956082218,269.452076389218803],"luv":[29.1495234429961272,-0.685625105343165342,-71.6928903298230438],"rgb":[0.266666666666666663,0.2,0.6],"xyz":[0.0931677473019928426,0.058963810842770227,0.307852296256807656],"hpluv":[269.452076389218803,312.107220431461315,29.1495234429961272],"hsluv":[269.452076389218803,66.5368599437509,29.1495234429961272]},"#4433aa":{"lch":[30.6372824460415245,82.7612763398458924,268.632918119842316],"luv":[30.6372824460415245,-1.97450330946598829,-82.7377193188284821],"rgb":[0.266666666666666663,0.2,0.66666666666666663],"xyz":[0.108225440267245371,0.0649868880288713285,0.387156145873806257],"hpluv":[268.632918119842316,342.78062465137026,30.6372824460415245],"hsluv":[268.632918119842316,71.6553387322885698,30.6372824460415245]},"#4433bb":{"lch":[32.2226022397772525,93.4505902198726375,268.062492493372758],"luv":[32.2226022397772525,-3.15951219941267691,-93.3971642776391633],"rgb":[0.266666666666666663,0.2,0.733333333333333282],"xyz":[0.125363032374582017,0.0718419248718060871,0.477414130972448],"hpluv":[268.062492493372758,368.010970109266054,32.2226022397772525],"hsluv":[268.062492493372758,75.8581190135042789,32.2226022397772525]},"#4433cc":{"lch":[33.8908458580626331,103.832026214070311,267.649236367220567],"luv":[33.8908458580626331,-4.25888397543315911,-103.744646006447894],"rgb":[0.266666666666666663,0.2,0.8],"xyz":[0.144655407818303772,0.0795588750492948887,0.579020641642718159],"hpluv":[267.649236367220567,388.765974213008576,33.8908458580626331],"hsluv":[267.649236367220567,79.3114034284002,33.8908458580626331]},"#4433dd":{"lch":[35.628800942302739,113.96424557581804,267.340372831489958],"luv":[35.628800942302739,-5.28823530427115518,-113.841485571087048],"rgb":[0.266666666666666663,0.2,0.866666666666666696],"xyz":[0.166173733368937315,0.0881662052695484227,0.692350489542724157],"hpluv":[267.340372831489958,405.888493424646128,35.628800942302739],"hsluv":[267.340372831489958,85.5677988674314,35.628800942302739]},"#4433ee":{"lch":[37.4248062251042484,123.894025620863303,267.103630337901393],"luv":[37.4248062251042484,-6.26032296204841732,-123.73575853791813],"rgb":[0.266666666666666663,0.2,0.933333333333333348],"xyz":[0.189985913635112053,0.0976910773760184536,0.81776130561124738],"hpluv":[267.103630337901393,420.078186698807599,37.4248062251042484],"hsluv":[267.103630337901393,92.6566888343209,37.4248062251042484]},"#4433ff":{"lch":[39.2687372084732473,133.657198385904053,266.918330051954797],"luv":[39.2687372084732473,-7.18532525912719411,-133.4639193988003],"rgb":[0.266666666666666663,0.2,1],"xyz":[0.216156962044943446,0.10815949673995115,0.955595493903029225],"hpluv":[266.918330051954797,431.901531941155895,39.2687372084732473],"hsluv":[266.918330051954797,99.99999999999946,39.2687372084732473]},"#bbbb00":{"lch":[73.6141498101152223,81.1522849996882485,85.8743202181747591],"luv":[73.6141498101152223,5.83845950082822274,80.9419900380996],"rgb":[0.733333333333333282,0.733333333333333282,0],"xyz":[0.382626051771664,0.461058251568279,0.0688379405602788],"hpluv":[85.8743202181747591,139.887458074797564,73.6141498101152223],"hsluv":[85.8743202181747591,100.000000000002331,73.6141498101152223]},"#bbbb11":{"lch":[73.6403599567658205,80.0195247391518478,85.8743202181747449],"luv":[73.6403599567658205,5.75696364516240155,79.8121651696532695],"rgb":[0.733333333333333282,0.733333333333333282,0.0666666666666666657],"xyz":[0.383637717271301082,0.461462917768133862,0.0741660455250344325],"hpluv":[85.8743202181747449,137.885751829634614,73.6403599567658205],"hsluv":[85.8743202181747449,98.5690595334933732,73.6403599567658205]},"#bbbb22":{"lch":[73.6889060807276763,77.9361940700347873,85.8743202181746597],"luv":[73.6889060807276763,5.60707948923862176,77.7342331648256817],"rgb":[0.733333333333333282,0.733333333333333282,0.133333333333333331],"xyz":[0.385513075409778139,0.462213061023524663,0.0840429317210136251],"hpluv":[85.8743202181746597,134.207383902194948,73.6889060807276763],"hsluv":[85.8743202181746597,95.9395400768792541,73.6889060807276763]},"#bbbb33":{"lch":[73.768722281637082,74.5519121144895536,85.8743202181745318],"luv":[73.768722281637082,5.36359906059884217,74.3587211095223],"rgb":[0.733333333333333282,0.733333333333333282,0.2],"xyz":[0.388600826142235845,0.46344816131650779,0.100305085578624775],"hpluv":[85.8743202181745318,128.24069174643796,73.768722281637082],"hsluv":[85.8743202181745318,91.6741883163470419,73.768722281637082]},"#bbbb44":{"lch":[73.8837085661944144,69.7637661309282464,85.8743202181743328],"luv":[73.8837085661944144,5.01911835485944824,69.5829829463408629],"rgb":[0.733333333333333282,0.733333333333333282,0.266666666666666663],"xyz":[0.39305882138645476,0.465231359414195356,0.123783860531511414],"hpluv":[85.8743202181743328,119.817583791868643,73.8837085661944144],"hsluv":[85.8743202181743328,85.652842249664161,73.8837085661944144]},"#bbbb55":{"lch":[74.0370403615741,63.5337415760462747,85.8743202181740628],"luv":[74.0370403615741,4.57090243520957262,63.3691026414263376],"rgb":[0.733333333333333282,0.733333333333333282,0.333333333333333315],"xyz":[0.399021235425816745,0.467616325029940216,0.155185907805485301],"hpluv":[85.8743202181740628,108.891682763750694,74.0370403615741],"hsluv":[85.8743202181740628,77.8423486010655239,74.0370403615741]},"#bbbb66":{"lch":[74.2313474843288361,55.8815023675101799,85.8743202181735512],"luv":[74.2313474843288361,4.02036601211528843,55.7366931561128212],"rgb":[0.733333333333333282,0.733333333333333282,0.4],"xyz":[0.40660623119932493,0.470650323339343535,0.195133552212629291],"hpluv":[85.8743202181735512,95.5256619678334857,74.2313474843288361],"hsluv":[85.8743202181735512,68.2875100330713138,74.2313474843288361]},"#bbbb77":{"lch":[74.468808451125966,46.8772880877030289,85.8743202181728549],"luv":[74.468808451125966,3.37256243628739805,46.7558120565865849],"rgb":[0.733333333333333282,0.733333333333333282,0.466666666666666674],"xyz":[0.415920244219526836,0.47437592854742433,0.2441873541190272],"hpluv":[85.8743202181728549,79.8780401793102328,74.468808451125966],"hsluv":[85.8743202181728549,57.1016453359253688,74.468808451125966]},"#bbbb88":{"lch":[74.7512063572608128,36.6333476151433146,85.874320218171647],"luv":[74.7512063572608128,2.63556739569020726,36.5384173438683746],"rgb":[0.733333333333333282,0.733333333333333282,0.533333333333333326],"xyz":[0.427060661321470636,0.478832095388201928,0.30286021752259934],"hpluv":[85.874320218171647,62.1867310089120195,74.7512063572608128],"hsluv":[85.874320218171647,44.4548295213588744,74.7512063572608128]},"#bbbb99":{"lch":[75.079965438194165,25.2938928616919938,85.8743202181691316],"luv":[75.079965438194165,1.81975614231989979,25.2283472245258693],"rgb":[0.733333333333333282,0.733333333333333282,0.6],"xyz":[0.440117625430543336,0.484054881031831086,0.371626895163717275],"hpluv":[85.8743202181691316,42.7494899193068392,75.079965438194165],"hsluv":[85.8743202181691316,30.559916169503893,75.079965438194165]},"#bbbbaa":{"lch":[75.4561775549407372,13.0242335847304886,85.8743202181613583],"luv":[75.4561775549407372,0.937021801842234714,12.9904830784884187],"rgb":[0.733333333333333282,0.733333333333333282,0.66666666666666663],"xyz":[0.455175318395795891,0.490077958217932208,0.450930744780715875],"hpluv":[85.8743202181613583,21.9026519543336242,75.4561775549407372],"hsluv":[85.8743202181613583,15.6573378741524305,75.4561775549407372]},"#bbbbbb":{"lch":[75.8806235332097856,3.97454725928322e-12,0],"luv":[75.8806235332097856,3.75098259432623098e-12,1.31421287976393485e-12],"rgb":[0.733333333333333282,0.733333333333333282,0.733333333333333282],"xyz":[0.472312910503132566,0.496932995060866967,0.541188729879357622],"hpluv":[0,6.64654731741433278e-12,75.8806235332097856],"hsluv":[0,6.51507609526145538e-12,75.8806235332097856]},"#bbbbcc":{"lch":[76.3537921403793,13.6026726964261613,265.874320218192793],"luv":[76.3537921403793,-0.97863730689855577,-13.5674232449512715],"rgb":[0.733333333333333282,0.733333333333333282,0.8],"xyz":[0.491605285946854265,0.504649945238355713,0.642795240549627778],"hpluv":[265.874320218192793,23.0577392955455913,76.3537921403793],"hsluv":[265.874320218192793,22.3559583930985184,76.3537921403793]},"#bbbbdd":{"lch":[76.875898300454,27.6153317552162676,265.874320218184891],"luv":[76.875898300454,-1.98677087225565296,-27.543770429115412],"rgb":[0.733333333333333282,0.733333333333333282,0.866666666666666696],"xyz":[0.513123611497487864,0.51325727545860933,0.756125088449633775],"hpluv":[265.874320218184891,48.094692651754464,76.875898300454],"hsluv":[265.874320218184891,46.4508399898830717,76.875898300454]},"#bbbbee":{"lch":[77.4469014383288794,41.8833893600305487,265.874320218182334],"luv":[77.4469014383288794,-3.01327895494808,-41.7748543291725554],"rgb":[0.733333333333333282,0.733333333333333282,0.933333333333333348],"xyz":[0.536935791763662573,0.522782147565079347,0.881535904518157],"hpluv":[265.874320218182334,75.1781465494946,77.4469014383288794],"hsluv":[265.874320218182334,72.306149300046286,77.4469014383288794]},"#bbbbff":{"lch":[78.0665243938900915,56.270213901735211,265.874320218181083],"luv":[78.0665243938900915,-4.04833166396953814,-56.1243973979724444],"rgb":[0.733333333333333282,0.733333333333333282,1],"xyz":[0.563106840173494,0.533250566929012,1.01937009280993873],"hpluv":[265.874320218181083,104.437018855576454,78.0665243938900915],"hsluv":[265.874320218181083,99.9999999999968,78.0665243938900915]},"#99cc00":{"lch":[76.0430979526319,91.0941172293808,106.263360497649074],"luv":[76.0430979526319,-25.5111694560196547,87.4489475453331124],"rgb":[0.6,0.8,0],"xyz":[0.347284960501106077,0.499573870357110428,0.0781308367092051204],"hpluv":[106.263360497649074,152.00919412554731,76.0430979526319],"hsluv":[106.263360497649074,100.000000000002444,76.0430979526319]},"#99cc11":{"lch":[76.0679435797449202,90.0431717383104342,106.457693210299354],"luv":[76.0679435797449202,-25.5098862277023812,86.3540299079582496],"rgb":[0.6,0.8,0.0666666666666666657],"xyz":[0.348296626000743181,0.499978536556965281,0.083458941673960757],"hpluv":[106.457693210299354,150.416675371578,76.0679435797449202],"hsluv":[106.457693210299354,98.6698238814781377,76.0679435797449202]},"#99cc22":{"lch":[76.1139653123302224,88.1120975577654235,106.827460928226145],"luv":[76.1139653123302224,-25.5076232207001254,84.3392132620406301],"rgb":[0.6,0.8,0.133333333333333331],"xyz":[0.350171984139220238,0.500728679812356137,0.0933358278699399496],"hpluv":[106.827460928226145,147.536178080277807,76.1139653123302224],"hsluv":[106.827460928226145,96.2239688839219554,76.1139653123302224]},"#99cc33":{"lch":[76.1896394090333615,84.9806694979027668,107.464683868975555],"luv":[76.1896394090333615,-25.5042190273125513,81.0632407452271195],"rgb":[0.6,0.8,0.2],"xyz":[0.353259734871677944,0.501963780105339263,0.109597981727551086],"hpluv":[107.464683868975555,142.843382477451314,76.1896394090333615],"hsluv":[107.464683868975555,92.2523981769505497,76.1896394090333615]},"#99cc44":{"lch":[76.2986765643697566,80.5641186971074461,108.452476349375274],"luv":[76.2986765643697566,-25.4999909675489818,76.4220366262018445],"rgb":[0.6,0.8,0.266666666666666663],"xyz":[0.357717730115896859,0.503746978203026829,0.133076756680437724],"hpluv":[108.452476349375274,136.177549938509202,76.2986765643697566],"hsluv":[108.452476349375274,86.6370393323296,76.2986765643697566]},"#99cc55":{"lch":[76.4441084930992645,74.8483308647658276,109.915235611542258],"luv":[76.4441084930992645,-25.4955548836031696,70.3722197633315858],"rgb":[0.6,0.8,0.333333333333333315],"xyz":[0.363680144155258844,0.506131943818771579,0.16447880395441164],"hpluv":[109.915235611542258,127.465568679298812,76.4441084930992645],"hsluv":[109.915235611542258,79.3378797119037245,76.4441084930992645]},"#99cc66":{"lch":[76.6284587708723279,67.8919791104198111,112.053749389120497],"luv":[76.6284587708723279,-25.4918238993559072,62.9244606001029396],"rgb":[0.6,0.8,0.4],"xyz":[0.37126513992876703,0.509165942128174898,0.204426448361555602],"hpluv":[112.053749389120497,116.726185086844026,76.6284587708723279],"hsluv":[112.053749389120497,70.3850130172815796,76.6284587708723279]},"#99cc77":{"lch":[76.8538330837355801,59.8379910057359723,115.212918879407354],"luv":[76.8538330837355801,-25.4899847789994141,54.1372869986012262],"rgb":[0.6,0.8,0.466666666666666674],"xyz":[0.380579152948968935,0.512891547336255749,0.253480250267953511],"hpluv":[115.212918879407354,104.093311342856381,76.8538330837355801],"hsluv":[115.212918879407354,59.8707914454029577,76.8538330837355801]},"#99cc88":{"lch":[77.1219726780439885,50.9458531819172649,120.024060615406185],"luv":[77.1219726780439885,-25.4914521322680621,44.1097021597488919],"rgb":[0.6,0.8,0.533333333333333326],"xyz":[0.391719570050912735,0.517347714177033291,0.31215311367152565],"hpluv":[120.024060615406185,89.8814395273866324,77.1219726780439885],"hsluv":[120.024060615406185,47.9401368886289845,77.1219726780439885]},"#99cc99":{"lch":[77.4342891130262103,41.681173098632776,127.715012949236964],"luv":[77.4342891130262103,-25.4978049096247332,32.9724450969124447],"rgb":[0.6,0.8,0.6],"xyz":[0.40477653415998549,0.522570499820662504,0.380919791312643585],"hpluv":[127.715012949236964,74.7648437658604337,77.4342891130262103],"hsluv":[127.715012949236964,34.7788947556027495,77.4342891130262103]},"#99ccaa":{"lch":[77.7918890721377352,32.9651751214879525,140.702563314804109],"luv":[77.7918890721377352,-25.5107121176450882,20.8783701002018844],"rgb":[0.6,0.8,0.66666666666666663],"xyz":[0.419834227125238,0.528593577006763571,0.460223640929642186],"hpluv":[140.702563314804109,60.2770963674260756,77.7918890721377352],"hsluv":[140.702563314804109,37.0803100068772622,77.7918890721377352]},"#99ccbb":{"lch":[78.1955939192693421,26.7536671513630395,162.617393192268167],"luv":[78.1955939192693421,-25.5318556358660231,7.9926875226808507],"rgb":[0.6,0.8,0.733333333333333282],"xyz":[0.436971819232574665,0.535448613849698329,0.550481626028283877],"hpluv":[162.617393192268167,50.006554278151107,78.1955939192693421],"hsluv":[162.617393192268167,39.5096284608221353,78.1955939192693421]},"#99cccc":{"lch":[78.6459566685868481,26.1512486118356264,192.177050630060364],"luv":[78.6459566685868481,-25.562857556775878,-5.51616873291790277],"rgb":[0.6,0.8,0.8],"xyz":[0.45626419467629642,0.543165564027187187,0.652088136698554],"hpluv":[192.177050630060364,50.1138147500145,78.6459566685868481],"hsluv":[192.177050630060364,42.0292910605185952,78.6459566685868481]},"#99ccdd":{"lch":[79.1432779300782,32.1756084020034763,217.269502822152816],"luv":[79.1432779300782,-25.6052179328205156,-19.4844191766612091],"rgb":[0.6,0.8,0.866666666666666696],"xyz":[0.477782520226929908,0.551772894247440693,0.76541798459856],"hpluv":[217.269502822152816,63.4104034428488532,79.1432779300782],"hsluv":[217.269502822152816,44.6030324020319142,79.1432779300782]},"#99ccee":{"lch":[79.6876217339600146,42.4043636674930298,232.761609201673764],"luv":[79.6876217339600146,-25.6602666815912279,-33.7591583407321],"rgb":[0.6,0.8,0.933333333333333348],"xyz":[0.501594700493104728,0.56129776635391071,0.890828800667083254],"hpluv":[232.761609201673764,86.2263034184575901,79.6876217339600146],"hsluv":[232.761609201673764,69.2463215394111842,79.6876217339600146]},"#99ccff":{"lch":[80.278831719152322,54.6396736698167231,241.908088190648726],"luv":[80.278831719152322,-25.7291311718118969,-48.2027566419988105],"rgb":[0.6,0.8,1],"xyz":[0.527765748902936,0.571766185717843434,1.02866298895886521],"hpluv":[241.908088190648726,115.039816302159181,80.278831719152322],"hsluv":[241.908088190648726,99.9999999999963762,80.278831719152322]},"#444400":{"lch":[27.7455139749470092,30.5866720374503593,85.8743202181747307],"luv":[27.7455139749470092,2.20054242411605072,30.5074108925390952],"rgb":[0.266666666666666663,0.266666666666666663,0],"xyz":[0.044508744126079483,0.0536323223451504599,0.00800753181435780864],"hpluv":[85.8743202181747307,139.887458074797593,27.7455139749470092],"hsluv":[85.8743202181747307,100.000000000002331,27.7455139749470092]},"#444411":{"lch":[27.8552611903384602,27.0161424908788135,85.8743202181744],"luv":[27.8552611903384602,1.9436625081132386,26.9461338811715763],"rgb":[0.266666666666666663,0.266666666666666663,0.0666666666666666657],"xyz":[0.0455204096257166,0.0540369885450053128,0.0133356367791134401],"hpluv":[85.8743202181744,123.070915058641674,27.8552611903384602],"hsluv":[85.8743202181744,87.9785198418851451,27.8552611903384602]},"#444422":{"lch":[28.0572627170229296,20.802612285424587,85.8743202181735086],"luv":[28.0572627170229296,1.49663326596887214,20.7487051828516122],"rgb":[0.266666666666666663,0.266666666666666663,0.133333333333333331],"xyz":[0.0473957677641936234,0.0547871318003961341,0.0232125229750926379],"hpluv":[85.8743202181735086,94.0831614915658463,28.0572627170229296],"hsluv":[85.8743202181735086,67.2563236092702539,28.0572627170229296]},"#444433":{"lch":[28.3858756417530103,11.5666907278610811,85.8743202181704],"luv":[28.3858756417530103,0.832159628943940688,11.5367172430437677],"rgb":[0.266666666666666663,0.266666666666666663,0.2],"xyz":[0.0504835184966513639,0.056022232093379247,0.0394746768327037811],"hpluv":[85.8743202181704,51.7066205750809758,28.3858756417530103],"hsluv":[85.8743202181704,36.9630139018145201,28.3858756417530103]},"#444444":{"lch":[28.8519023983998864,1.56211738287899238e-12,0],"luv":[28.8519023983998864,1.45745810878583046e-12,5.6216241338882039e-13],"rgb":[0.266666666666666663,0.266666666666666663,0.266666666666666663],"xyz":[0.0549415137408702445,0.05780543019106682,0.0629534517855904197],"hpluv":[0,6.87034486140541504e-12,28.8519023983998864],"hsluv":[0,1.96712204652458306e-12,28.8519023983998864]},"#444455":{"lch":[29.4604491554767947,12.996237632929807,265.874320218183527],"luv":[29.4604491554767947,-0.935007647451096213,-12.9625596743385874],"rgb":[0.266666666666666663,0.266666666666666663,0.333333333333333315],"xyz":[0.0609039277802322365,0.0601903958068116529,0.094355499059564335],"hpluv":[265.874320218183527,55.9780294653588157,29.4604491554767947],"hsluv":[265.874320218183527,10.903125265393685,29.4604491554767947]},"#444466":{"lch":[30.2117995944983235,26.5936989313503034,265.874320218180401],"luv":[30.2117995944983235,-1.9132700229980284,-26.52478502590359],"rgb":[0.266666666666666663,0.266666666666666663,0.4],"xyz":[0.0684889235537404079,0.0632243941162149714,0.134303143466708297],"hpluv":[265.874320218180401,111.69699235114156,30.2117995944983235],"hsluv":[265.874320218180401,21.7557908165695544,30.2117995944983235]},"#444477":{"lch":[31.1022350000615333,40.188693881548005,265.874320218179378],"luv":[31.1022350000615333,-2.89135495838767076,-40.084550420447286],"rgb":[0.266666666666666663,0.266666666666666663,0.466666666666666674],"xyz":[0.0778029365739423273,0.0669499993242957808,0.183356945373106206],"hpluv":[265.874320218179378,163.965176201048621,31.1022350000615333],"hsluv":[265.874320218179378,31.9363305989270607,31.1022350000615333]},"#444488":{"lch":[32.1249060438116132,53.4239352836437,265.874320218178923],"luv":[32.1249060438116132,-3.84355760936744639,-53.2854945186250859],"rgb":[0.266666666666666663,0.266666666666666663,0.533333333333333326],"xyz":[0.0889433536758861554,0.0714061661650733787,0.242029808776678373],"hpluv":[265.874320218178923,211.024721596932807,32.1249060438116132],"hsluv":[265.874320218178923,41.1023574005893479,32.1249060438116132]},"#444499":{"lch":[33.2707247827276404,66.1374776044503818,265.874320218178639],"luv":[33.2707247827276404,-4.75822688765494473,-65.9660914467787336],"rgb":[0.266666666666666663,0.266666666666666663,0.6],"xyz":[0.102000317784958869,0.0766289518087025362,0.31079648641779628],"hpluv":[265.874320218178639,252.246234683596128,33.2707247827276404],"hsluv":[265.874320218178639,50.2337582903708224,33.2707247827276404]},"#4444aa":{"lch":[34.5292085317775772,78.2936422443982707,265.874320218178468],"luv":[34.5292085317775772,-5.63279591471267516,-78.090755061511743],"rgb":[0.266666666666666663,0.266666666666666663,0.66666666666666663],"xyz":[0.117058010750211411,0.0826520289948036446,0.390100336034794881],"hpluv":[265.874320218178468,287.726060771882089,34.5292085317775772],"hsluv":[265.874320218178468,59.0260968416557645,34.5292085317775772]},"#4444bb":{"lch":[35.8892144652077647,89.9250819913669801,265.874320218178354],"luv":[35.8892144652077647,-6.46961387860876,-89.6920535355044],"rgb":[0.266666666666666663,0.266666666666666663,0.733333333333333282],"xyz":[0.134195602857548058,0.0895070658377384,0.480358321133436628],"hpluv":[265.874320218178354,317.948086985701252,35.8892144652077647],"hsluv":[265.874320218178354,67.4283717547759807,35.8892144652077647]},"#4444cc":{"lch":[37.3395287853000397,101.093816173965237,265.874320218178298],"luv":[37.3395287853000397,-7.27314272811459173,-100.831845482823283],"rgb":[0.266666666666666663,0.266666666666666663,0.8],"xyz":[0.153487978301269812,0.0972240160152272,0.581964831803706728],"hpluv":[265.874320218178298,343.55405942077391,37.3395287853000397],"hsluv":[265.874320218178298,75.5808548987534294,37.3395287853000397]},"#4444dd":{"lch":[38.8693012328948697,111.868746538356049,265.874320218178241],"luv":[38.8693012328948697,-8.04833956400084105,-111.578854100252741],"rgb":[0.266666666666666663,0.266666666666666663,0.866666666666666696],"xyz":[0.175006303851903355,0.105831346235480739,0.695294679703712726],"hpluv":[265.874320218178241,365.208910634554,38.8693012328948697],"hsluv":[265.874320218178241,83.6313726076760702,38.8693012328948697]},"#4444ee":{"lch":[40.4683363226646691,122.314460404417545,265.874320218178127],"luv":[40.4683363226646691,-8.799851087852959,-121.997499338533331],"rgb":[0.266666666666666663,0.266666666666666663,0.933333333333333348],"xyz":[0.198818484118078065,0.115356218341950756,0.820705495772236],"hpluv":[265.874320218178127,383.532154053589807,40.4683363226646691],"hsluv":[265.874320218178127,91.7249319236633625,40.4683363226646691]},"#4444ff":{"lch":[42.1272645151277203,132.4867415013303,265.874320218178127],"luv":[42.1272645151277203,-9.53169063144130568,-132.143420370999962],"rgb":[0.266666666666666663,0.266666666666666663,1],"xyz":[0.224989532527909486,0.12582463770588348,0.958539684064017794],"hpluv":[265.874320218178127,399.069452944254863,42.1272645151277203],"hsluv":[265.874320218178127,99.9999999999994458,42.1272645151277203]},"#bbcc00":{"lch":[78.3160688649495711,87.6272661942945916,93.3039767998847651],"luv":[78.3160688649495711,-5.05025027162958828,87.4816137990130471],"rgb":[0.733333333333333282,0.8,0],"xyz":[0.420849795081280953,0.537505738187513904,0.081579188330150737],"hpluv":[93.3039767998847651,164.876849582678972,78.3160688649495711],"hsluv":[93.3039767998847651,100.000000000002302,78.3160688649495711]},"#bbcc11":{"lch":[78.3397318378619332,86.5943586896397193,93.3754549086192],"luv":[78.3397318378619332,-5.09856582429126703,86.4441298377475249],"rgb":[0.733333333333333282,0.8,0.0666666666666666657],"xyz":[0.421861460580918057,0.537910404387368812,0.0869072932949063737],"hpluv":[93.3754549086192,163.146061131239662,78.3397318378619332],"hsluv":[93.3754549086192,98.7690619278293,78.3397318378619332]},"#bbcc22":{"lch":[78.3835653101130134,84.6929692802742125,93.5116381573829329],"luv":[78.3835653101130134,-5.18755307791811582,84.5339478468458481],"rgb":[0.733333333333333282,0.8,0.133333333333333331],"xyz":[0.423736818719395114,0.538660547642759613,0.0967841794908855663],"hpluv":[93.5116381573829329,159.95035726483485,78.3835653101130134],"hsluv":[93.5116381573829329,96.5043106774720343,78.3835653101130134]},"#bbcc33":{"lch":[78.4556479267243,81.5996211754986405,93.7468972468941644],"luv":[78.4556479267243,-5.33246113691784,81.4251990123951],"rgb":[0.733333333333333282,0.8,0.2],"xyz":[0.42682456945185282,0.53989564793574274,0.113046333348496703],"hpluv":[93.7468972468941644,154.72407311273281,78.4556479267243],"hsluv":[93.7468972468941644,92.8230308779070157,78.4556479267243]},"#bbcc44":{"lch":[78.5595248117047475,77.2136355312914873,94.1131309849175],"luv":[78.5595248117047475,-5.53822791342104281,77.0147618547127877],"rgb":[0.733333333333333282,0.8,0.266666666666666663],"xyz":[0.431282564696071735,0.541678846033430306,0.136525108301383341],"hpluv":[94.1131309849175,147.254264694427633,78.5595248117047475],"hsluv":[94.1131309849175,87.6101015600037556,78.5595248117047475]},"#bbcc55":{"lch":[78.6981007967589079,71.4912273296626921,94.6593076474989488],"luv":[78.6981007967589079,-5.80727962831129307,71.2549723768110823],"rgb":[0.733333333333333282,0.8,0.333333333333333315],"xyz":[0.43724497873543372,0.544063811649175,0.167927155575357256],"hpluv":[94.6593076474989488,137.398731222980217,78.6981007967589079],"hsluv":[94.6593076474989488,80.8199795518833639,78.6981007967589079]},"#bbcc66":{"lch":[78.8738041161037273,64.440755538040392,95.4673459440295176],"luv":[78.8738041161037273,-6.13981465986491681,64.1475927081912403],"rgb":[0.733333333333333282,0.8,0.4],"xyz":[0.444829974508941905,0.547097809958578374,0.207874799982501218],"hpluv":[95.4673459440295176,125.075317361724217,78.8738041161037273],"hsluv":[95.4673459440295176,72.4696955468646848,78.8738041161037273]},"#bbcc77":{"lch":[79.0886730863018244,56.1202023011108153,96.6860819525870454],"luv":[79.0886730863018244,-6.5340457953894715,55.7385266387743386],"rgb":[0.733333333333333282,0.8,0.466666666666666674],"xyz":[0.454143987529143811,0.550823415166659225,0.2569286018888991],"hpluv":[96.6860819525870454,110.256785788709905,79.0886730863018244],"hsluv":[96.6860819525870454,62.6323703341884,79.0886730863018244]},"#bbcc88":{"lch":[79.3444074564468451,46.6379090458556931,98.6154841820407],"luv":[79.3444074564468451,-6.9864776714612713,46.1116437563841473],"rgb":[0.733333333333333282,0.8,0.533333333333333326],"xyz":[0.465284404631087611,0.555279582007436767,0.315601465292471295],"hpluv":[98.6154841820407,92.9740665461038702,79.3444074564468451],"hsluv":[98.6154841820407,51.4293340198482483,79.3444074564468451]},"#bbcc99":{"lch":[79.6424016540563,36.1659853952486898,101.956116252097843],"luv":[79.6424016540563,-7.49223418159530397,35.3814206410296137],"rgb":[0.733333333333333282,0.8,0.6],"xyz":[0.478341368740160311,0.560502367651066,0.38436814293358923],"hpluv":[101.956116252097843,73.3480812445522616,79.6424016540563],"hsluv":[101.956116252097843,39.0205672484538439,79.6424016540563]},"#bbccaa":{"lch":[79.9837682606211899,25.01387761080953,108.762036830266098],"luv":[79.9837682606211899,-8.04542335958215382,23.6847046866463593],"rgb":[0.733333333333333282,0.8,0.66666666666666663],"xyz":[0.493399061705412867,0.566525444837167,0.463671992550587775],"hpluv":[108.762036830266098,51.7527419020047645,79.9837682606211899],"hsluv":[108.762036830266098,25.5939208298632828,79.9837682606211899]},"#bbccbb":{"lch":[80.3693561861161356,14.1229851396422195,127.715012949226079],"luv":[80.3693561861161356,-8.63951499109438892,11.1721748094634776],"rgb":[0.733333333333333282,0.8,0.733333333333333282],"xyz":[0.510536653812749486,0.573380481680101806,0.553929977649229577],"hpluv":[127.715012949226079,29.8960175179438359,80.3693561861161356],"hsluv":[127.715012949226079,11.3539027840963094,80.3693561861161356]},"#bbcccc":{"lch":[80.7997661027856537,9.48102181692548207,192.177050630058517],"luv":[80.7997661027856537,-9.26770319062559622,-1.99986306118322021],"rgb":[0.733333333333333282,0.8,0.8],"xyz":[0.52982902925647124,0.581097431857590663,0.655536488319499733],"hpluv":[192.177050630058517,20.5979746439306091,80.7997661027856537],"hsluv":[192.177050630058517,14.8313634310922779,80.7997661027856537]},"#bbccdd":{"lch":[81.2753646562375138,18.5534915910332536,237.666646406054781],"luv":[81.2753646562375138,-9.9232294288842251,-15.6767843616051454],"rgb":[0.733333333333333282,0.8,0.866666666666666696],"xyz":[0.551347354807104839,0.589704762077844169,0.76886633621950573],"hpluv":[237.666646406054781,41.5059211719903089,81.2753646562375138],"hsluv":[237.666646406054781,34.6395739712995834,81.2753646562375138]},"#bbccee":{"lch":[81.7962983545147466,31.5457627715974915,250.366116177600304],"luv":[81.7962983545147466,-10.5996485000078611,-29.7116576534899401],"rgb":[0.733333333333333282,0.8,0.933333333333333348],"xyz":[0.575159535073279549,0.599229634184314186,0.894277152288029],"hpluv":[250.366116177600304,72.9233806917818725,81.7962983545147466],"hsluv":[250.366116177600304,65.9765826509477478,81.7962983545147466]},"#bbccff":{"lch":[82.3625076456434329,45.3963265868234203,255.598148289110497],"luv":[82.3625076456434329,-11.2910283768039683,-43.9697526234994669],"rgb":[0.733333333333333282,0.8,1],"xyz":[0.601330583483111,0.60969805354824691,1.0321113405798108],"hpluv":[255.598148289110497,108.847942718229262,82.3625076456434329],"hsluv":[255.598148289110497,99.9999999999958789,82.3625076456434329]},"#99dd00":{"lch":[81.0072374435841738,100.100388992767378,110.059278565234735],"luv":[81.0072374435841738,-34.3336498242400623,94.028125400062109],"rgb":[0.6,0.866666666666666696,0],"xyz":[0.389918951048281226,0.584841851451462,0.0923421668915964389],"hpluv":[110.059278565234735,220.251619684458433,81.0072374435841738],"hsluv":[110.059278565234735,100.000000000002245,81.0072374435841738]},"#99dd11":{"lch":[81.0296061686003,99.1554895677318626,110.248463134872608],"luv":[81.0296061686003,-34.3169109383134483,93.0277417498051733],"rgb":[0.6,0.866666666666666696,0.0666666666666666657],"xyz":[0.39093061654791833,0.585246517651316855,0.0976702718563520755],"hpluv":[110.248463134872608,218.472914234601802,81.0296061686003],"hsluv":[110.248463134872608,98.8616036668283,81.0296061686003]},"#99dd22":{"lch":[81.0710445633976,97.4178442494470431,110.606606003803336],"luv":[81.0710445633976,-34.2861684396920552,91.184949591117217],"rgb":[0.6,0.866666666666666696,0.133333333333333331],"xyz":[0.392805974686395387,0.585996660906707656,0.107547158052331268],"hpluv":[110.606606003803336,215.192819254761019,81.0710445633976],"hsluv":[110.606606003803336,96.7659338960775131,81.0710445633976]},"#99dd33":{"lch":[81.1391953168863154,94.5961335984436857,111.218234049095599],"luv":[81.1391953168863154,-34.2363517117206513,88.1833358024404106],"rgb":[0.6,0.866666666666666696,0.2],"xyz":[0.395893725418853093,0.587231761199690783,0.123809311909942404],"hpluv":[111.218234049095599,209.840835623165077,81.1391953168863154],"hsluv":[111.218234049095599,93.3562182204636599,81.1391953168863154]},"#99dd44":{"lch":[81.2374208116197565,90.6080011845065343,112.152709421166279],"luv":[81.2374208116197565,-34.1661449288095653,83.9195115533635487],"rgb":[0.6,0.866666666666666696,0.266666666666666663],"xyz":[0.400351720663072,0.589014959297378349,0.147288086862829043],"hpluv":[112.152709421166279,202.22119953609328,81.2374208116197565],"hsluv":[112.152709421166279,88.5208972681928827,81.2374208116197565]},"#99dd55":{"lch":[81.3684846041416421,85.4315699411036604,113.506988965296216],"luv":[81.3684846041416421,-34.0753153837539244,78.3417259453695607],"rgb":[0.6,0.866666666666666696,0.333333333333333315],"xyz":[0.406314134702434,0.591399924913123098,0.178690134136802958],"hpluv":[113.506988965296216,192.231211336261111,81.3684846041416421],"hsluv":[113.506988965296216,82.2103932307851579,81.3684846041416421]},"#99dd66":{"lch":[81.5347071887187695,79.1067404569191837,115.426438711198742],"luv":[81.5347071887187695,-33.9646313013793,71.4442454385217758],"rgb":[0.6,0.866666666666666696,0.4],"xyz":[0.413899130475942179,0.594433923222526417,0.21863777854394692],"hpluv":[115.426438711198742,179.864586291563711,81.5347071887187695],"hsluv":[115.426438711198742,74.4308834765056,81.5347071887187695]},"#99dd77":{"lch":[81.7380487099571,71.7428264562351217,118.139890255285593],"luv":[81.7380487099571,-33.8357763954940651,63.2627329764024324],"rgb":[0.6,0.866666666666666696,0.466666666666666674],"xyz":[0.423213143496144084,0.598159528430607268,0.267691580450344802],"hpluv":[118.139890255285593,165.232292085670224,81.7380487099571],"hsluv":[118.139890255285593,65.2389156719347,81.7380487099571]},"#99dd88":{"lch":[81.9801580086414248,63.5371271244577898,122.023063704989269],"luv":[81.9801580086414248,-33.6912346765093815,53.8689820694792942],"rgb":[0.6,0.866666666666666696,0.533333333333333326],"xyz":[0.434353560598087884,0.60261569527138481,0.326364443853917],"hpluv":[122.023063704989269,148.614644251947254,81.9801580086414248],"hsluv":[122.023063704989269,54.7350081565161943,81.9801580086414248]},"#99dd99":{"lch":[82.2624042681600827,54.8181455895933,127.715012949238044],"luv":[82.2624042681600827,-33.5341421039979721,43.3646215160552],"rgb":[0.6,0.866666666666666696,0.6],"xyz":[0.447410524707160584,0.607838480915014,0.395131121495034932],"hpluv":[127.715012949238044,130.58293025992694,82.2624042681600827],"hsluv":[127.715012949238044,43.0558433261763724,82.2624042681600827]},"#99ddaa":{"lch":[82.5858991321833855,46.1452119261310898,136.311935434488049],"luv":[82.5858991321833855,-33.3681126370237138,31.8739649675161907],"rgb":[0.6,0.866666666666666696,0.66666666666666663],"xyz":[0.46246821767241314,0.61386155810111509,0.474434971112033477],"hpluv":[136.311935434488049,112.281381223378744,82.5858991321833855],"hsluv":[136.311935434488049,44.8180878493830122,82.5858991321833855]},"#99ddbb":{"lch":[82.9515135213076,38.5190468703316,149.523212457301526],"luv":[82.9515135213076,-33.1970516149390349,19.536446347119437],"rgb":[0.6,0.866666666666666696,0.733333333333333282],"xyz":[0.479605809779749814,0.620716594944049849,0.564692956210675279],"hpluv":[149.523212457301526,96.0401150933983416,82.9515135213076],"hsluv":[149.523212457301526,46.6955526922259807,82.9515135213076]},"#99ddcc":{"lch":[83.3598915829196585,33.6584009809739158,168.866706644517421],"luv":[83.3598915829196585,-33.0249710093605202,6.49917274942921],"rgb":[0.6,0.866666666666666696,0.8],"xyz":[0.498898185223471513,0.628433545121538706,0.666299466880945435],"hpluv":[168.866706644517421,86.2854242091523,83.3598915829196585],"hsluv":[168.866706644517421,48.6618587245334879,83.3598915829196585]},"#99dddd":{"lch":[83.811463234187741,33.6120761253887,192.17705063006062],"luv":[83.811463234187741,-32.85581988586,-7.08990557672323529],"rgb":[0.6,0.866666666666666696,0.866666666666666696],"xyz":[0.520416510774105112,0.637040875341792212,0.779629314780951432],"hpluv":[192.17705063006062,88.9162454401141,83.811463234187741],"hsluv":[192.17705063006062,50.6906605199231777,83.811463234187741]},"#99ddee":{"lch":[84.3064561843391402,38.9043785470910208,212.822778279485817],"luv":[84.3064561843391402,-32.6933403312743565,-21.0878203738277712],"rgb":[0.6,0.866666666666666696,0.933333333333333348],"xyz":[0.544228691040279822,0.646565747448262229,0.905040130849474656],"hpluv":[212.822778279485817,106.615167592423816,84.3064561843391402],"hsluv":[212.822778279485817,60.6575363344623781,84.3064561843391402]},"#99ddff":{"lch":[84.8449079615810575,48.055447414973429,227.378328123916845],"luv":[84.8449079615810575,-32.5409551013358396,-35.3611689193969099],"rgb":[0.6,0.866666666666666696,1],"xyz":[0.570399739450111243,0.657034166812195,1.04287431914125639],"hpluv":[227.378328123916845,137.001856984753886,84.8449079615810575],"hsluv":[227.378328123916845,99.999999999994813,84.8449079615810575]},"#445500":{"lch":[33.4053570608210535,38.7644311760376397,101.469350612776353],"luv":[33.4053570608210535,-7.7080633936099785,37.9903525006263791],"rgb":[0.266666666666666663,0.333333333333333315,0],"xyz":[0.0563220008404254485,0.0772588357738427239,0.0119452840524730177],"hpluv":[101.469350612776353,147.25044771073371,33.4053570608210535],"hsluv":[101.469350612776353,100.000000000002245,33.4053570608210535]},"#445511":{"lch":[33.4914653280992525,35.8039831739898275,102.524785035338],"luv":[33.4914653280992525,-7.76452054412734,34.9519303021045076],"rgb":[0.266666666666666663,0.333333333333333315,0.0666666666666666657],"xyz":[0.0573336663400625668,0.0776635019736975768,0.0172733890172286492],"hpluv":[102.524785035338,135.655223597469387,33.4914653280992525],"hsluv":[102.524785035338,91.5452633494882093,33.4914653280992525]},"#445522":{"lch":[33.6502992474903806,30.5965852313305291,104.897478349629381],"luv":[33.6502992474903806,-7.86608410210478493,29.5681543001366229],"rgb":[0.266666666666666663,0.333333333333333315,0.133333333333333331],"xyz":[0.0592090244785395889,0.0784136452290883912,0.0271502752132078452],"hpluv":[104.897478349629381,115.378092601865731,33.6502992474903806],"hsluv":[104.897478349629381,76.6558497494370243,33.6502992474903806]},"#445533":{"lch":[33.9096245159150911,22.79863905315759,110.61032755328398],"luv":[33.9096245159150911,-8.02535730567797501,21.3394372651287],"rgb":[0.266666666666666663,0.333333333333333315,0.2],"xyz":[0.0622967752109973294,0.0796487455220715,0.0434124290708189919],"hpluv":[110.61032755328398,85.3149751467856419,33.9096245159150911],"hsluv":[110.61032755328398,54.1403397653178331,33.9096245159150911]},"#445544":{"lch":[34.2793424585633204,13.4702363502677187,127.715012949235046],"luv":[34.2793424585633204,-8.24020614134090401,10.6558092175245633],"rgb":[0.266666666666666663,0.333333333333333315,0.266666666666666663],"xyz":[0.0667547704552162,0.081431943619759084,0.0668912040237056305],"hpluv":[127.715012949235046,49.8634197051089814,34.2793424585633204],"hsluv":[127.715012949235046,25.3893680776039865,34.2793424585633204]},"#445555":{"lch":[34.7654846399243738,8.70030094248716424,192.177050630060222],"luv":[34.7654846399243738,-8.50454817645784544,-1.83518304377262664],"rgb":[0.266666666666666663,0.333333333333333315,0.333333333333333315],"xyz":[0.0727171844945782,0.0838169092355039169,0.098293251297679532],"hpluv":[192.177050630060222,31.7559649298661668,34.7654846399243738],"hsluv":[192.177050630060222,31.6316627668381969,34.7654846399243738]},"#445566":{"lch":[35.3707740335649916,17.7582093223154978,240.254504050727519],"luv":[35.3707740335649916,-8.81070452432763318,-15.4183489427423961],"rgb":[0.266666666666666663,0.333333333333333315,0.4],"xyz":[0.0803021802680863733,0.0868509075449072354,0.138240895704823508],"hpluv":[240.254504050727519,63.7079944588343352,35.3707740335649916],"hsluv":[240.254504050727519,38.2083413049732812,35.3707740335649916]},"#445577":{"lch":[36.0950574442792913,30.8488081821149329,252.743594747999822],"luv":[36.0950574442792913,-9.15124765847803623,-29.4602042177260124],"rgb":[0.266666666666666663,0.333333333333333315,0.466666666666666674],"xyz":[0.0896161932882882928,0.0905765127529880448,0.187294697611221417],"hpluv":[252.743594747999822,108.450105614435046,36.0950574442792913],"hsluv":[252.743594747999822,44.7362415879762878,36.0950574442792913]},"#445588":{"lch":[36.935739068143242,44.5159055243500319,257.651563208142],"luv":[36.935739068143242,-9.5200062368872409,-43.4860359874579245],"rgb":[0.266666666666666663,0.333333333333333315,0.533333333333333326],"xyz":[0.100756610390232121,0.0950326795937656427,0.245967561014793584],"hpluv":[257.651563208142,152.935302017774575,36.935739068143242],"hsluv":[257.651563208142,50.9359550606187952,36.935739068143242]},"#445599":{"lch":[37.8882410462664865,58.0440865604997711,260.167284745959819],"luv":[37.8882410462664865,-9.91231220062277885,-57.1914508600732674],"rgb":[0.266666666666666663,0.333333333333333315,0.6],"xyz":[0.113813574499304834,0.1002554652373948,0.314734238655911491],"hpluv":[260.167284745959819,194.398479571090235,37.8882410462664865],"hsluv":[260.167284745959819,56.6374271499956805,37.8882410462664865]},"#4455aa":{"lch":[38.9464770503006932,71.1678309986128141,261.658277796723496],"luv":[38.9464770503006932,-10.3247958657557941,-70.4149043837850144],"rgb":[0.266666666666666663,0.333333333333333315,0.66666666666666663],"xyz":[0.128871267464557376,0.106278542423495909,0.394038088272910092],"hpluv":[261.658277796723496,231.875507383352442,38.9464770503006932],"hsluv":[261.658277796723496,61.7617646139185652,38.9464770503006932]},"#4455bb":{"lch":[40.1033117689144,83.7900384834958,262.625350778636744],"luv":[40.1033117689144,-10.7550224317659584,-83.0969316013408417],"rgb":[0.266666666666666663,0.333333333333333315,0.733333333333333282],"xyz":[0.146008859571894023,0.113133579266430667,0.484296073371551838],"hpluv":[262.625350778636744,265.125486140416797,40.1033117689144],"hsluv":[262.625350778636744,66.2950009745661,40.1033117689144]},"#4455cc":{"lch":[41.3509797710146,95.8983760986006075,263.292411358069444],"luv":[41.3509797710146,-11.2011488171863753,-95.24196975873555],"rgb":[0.266666666666666663,0.333333333333333315,0.8],"xyz":[0.165301235015615777,0.120850529443919469,0.585902584041821939],"hpluv":[263.292411358069444,294.28272942652967,41.3509797710146],"hsluv":[263.292411358069444,71.9850161095278906,41.3509797710146]},"#4455dd":{"lch":[42.6814446156526657,107.523441304607104,263.773623462042394],"luv":[42.6814446156526657,-11.661670844638417,-106.88917561239117],"rgb":[0.266666666666666663,0.333333333333333315,0.866666666666666696],"xyz":[0.186819560566249321,0.129457859664173,0.699232431941827937],"hpluv":[263.773623462042394,319.67109728436958,42.6814446156526657],"hsluv":[263.773623462042394,81.2732363837730247,42.6814446156526657]},"#4455ee":{"lch":[44.0866885883675,118.714724855013472,264.132858108854521],"luv":[44.0866885883675,-12.1352655753704255,-118.092850024109339],"rgb":[0.266666666666666663,0.333333333333333315,0.933333333333333348],"xyz":[0.21063174083242403,0.13898273177064302,0.82464324801035116],"hpluv":[264.132858108854521,341.693279595385377,44.0866885883675],"hsluv":[264.132858108854521,90.5684283810677186,44.0866885883675]},"#4455ff":{"lch":[45.5589321196955765,129.526958246416882,264.408404412275218],"luv":[45.5589321196955765,-12.6207071057408875,-128.910630534181024],"rgb":[0.266666666666666663,0.333333333333333315,1],"xyz":[0.236802789242255451,0.149451151134575744,0.962477436302133],"hpluv":[264.408404412275218,360.766296003954039,45.5589321196955765],"hsluv":[264.408404412275218,99.9999999999992752,45.5589321196955765]},"#bbdd00":{"lch":[83.0607051195576673,95.2176030862527796,99.223939245402],"luv":[83.0607051195576673,-15.2627740644400269,93.9863802119702427],"rgb":[0.733333333333333282,0.866666666666666696,0],"xyz":[0.463483785628456102,0.622773719281865423,0.0957905185125420555],"hpluv":[99.223939245402,239.164338292747971,83.0607051195576673],"hsluv":[99.223939245402,100.00000000000216,83.0607051195576673]},"#bbdd11":{"lch":[83.082156377860457,94.2793845093074765,99.3314951275217481],"luv":[83.082156377860457,-15.2870500651298524,93.0317604034238457],"rgb":[0.733333333333333282,0.866666666666666696,0.0666666666666666657],"xyz":[0.464495451128093206,0.623178385481720332,0.101118623477297692],"hpluv":[99.3314951275217481,237.15213625798873,83.082156377860457],"hsluv":[99.3314951275217481,98.93507934353417,83.082156377860457]},"#bbdd22":{"lch":[83.1218967422411765,92.5513012339483794,99.5354402549794486],"luv":[83.1218967422411765,-15.3318301742674485,91.2725497814347],"rgb":[0.733333333333333282,0.866666666666666696,0.133333333333333331],"xyz":[0.466370809266570263,0.623928528737111132,0.110995509673276885],"hpluv":[99.5354402549794486,233.433860498438293,83.1218967422411765],"hsluv":[99.5354402549794486,96.9737920722735538,83.1218967422411765]},"#bbdd33":{"lch":[83.1872593514570298,89.7372330308786,99.8847694763800718],"luv":[83.1872593514570298,-15.4049421957575614,88.4050832700449263],"rgb":[0.733333333333333282,0.866666666666666696,0.2],"xyz":[0.469458559999027969,0.625163629030094259,0.127257663530888021],"hpluv":[99.8847694763800718,227.34486653748678,83.1872593514570298],"hsluv":[99.8847694763800718,93.7802848880547373,83.1872593514570298]},"#bbdd44":{"lch":[83.2814760883348697,85.7419087717973,100.421139587323779],"luv":[83.2814760883348697,-15.5091702542505097,84.3275788686944452],"rgb":[0.733333333333333282,0.866666666666666696,0.266666666666666663],"xyz":[0.473916555243246884,0.626946827127781825,0.15073643848377466],"hpluv":[100.421139587323779,218.62539011377612,83.2814760883348697],"hsluv":[100.421139587323779,89.2463889616074511,83.2814760883348697]},"#bbdd55":{"lch":[83.4072088624658,80.5207963453540572,101.204557559233223],"luv":[83.4072088624658,-15.6461875861262687,78.9860459708531835],"rgb":[0.733333333333333282,0.866666666666666696,0.333333333333333315],"xyz":[0.479878969282608869,0.629331792743526575,0.182138485757748575],"hpluv":[101.204557559233223,207.093501481883948,83.4072088624658],"hsluv":[101.204557559233223,83.3201121340644448,83.4072088624658]},"#bbdd66":{"lch":[83.5666996797624364,74.0772937371066149,102.328486133362119],"luv":[83.5666996797624364,-15.816696660053216,72.3690372616455164],"rgb":[0.733333333333333282,0.866666666666666696,0.4],"xyz":[0.487463965056117055,0.632365791052929893,0.222086130164892537],"hpluv":[102.328486133362119,192.635686460640301,83.5666996797624364],"hsluv":[102.328486133362119,75.9999631534792854,83.5666996797624364]},"#bbdd77":{"lch":[83.7618505003220122,66.4632811318093388,103.948126608074304],"luv":[83.7618505003220122,-16.0205304866066705,64.5035684418593149],"rgb":[0.733333333333333282,0.866666666666666696,0.466666666666666674],"xyz":[0.49677797807631896,0.636091396261010744,0.271139932071290446],"hpluv":[103.948126608074304,175.207590447676779,83.7618505003220122],"hsluv":[103.948126608074304,67.3303268013154,83.7618505003220122]},"#bbdd88":{"lch":[83.9942706402616,57.7845188457632091,106.339844930601203],"luv":[83.9942706402616,-16.2567564016803381,55.4505950331715454],"rgb":[0.733333333333333282,0.866666666666666696,0.533333333333333326],"xyz":[0.50791839517826276,0.640547563101788286,0.329812795474862641],"hpluv":[106.339844930601203,154.850612540446264,83.9942706402616],"hsluv":[106.339844930601203,57.39610210967858,83.9942706402616]},"#bbdd99":{"lch":[84.2653073070245711,48.2191913160874819,110.040291826115165],"luv":[84.2653073070245711,-16.5237946272095257,45.2996095159246153],"rgb":[0.733333333333333282,0.866666666666666696,0.6],"xyz":[0.520975359287335515,0.6457703487454175,0.39857947311598052],"hpluv":[110.040291826115165,131.749772406416298,84.2653073070245711],"hsluv":[110.040291826115165,46.3161497548768466,84.2653073070245711]},"#bbddaa":{"lch":[84.5760668100806328,38.0784660246866622,116.212889721452115],"luv":[84.5760668100806328,-16.8195514991441222,34.1624393473425059],"rgb":[0.733333333333333282,0.866666666666666696,0.66666666666666663],"xyz":[0.536033052252588,0.651793425931518566,0.477883322732979121],"hpluv":[116.212889721452115,106.421399158629868,84.5760668100806328],"hsluv":[116.212889721452115,34.2356923349747433,84.5760668100806328]},"#bbddbb":{"lch":[84.9274305013996553,28.0212505683590543,127.715012949233824],"luv":[84.9274305013996553,-17.1415612181778,22.1665821095230058],"rgb":[0.733333333333333282,0.866666666666666696,0.733333333333333282],"xyz":[0.55317064435992469,0.658648462774453325,0.568141307831620868],"hpluv":[127.715012949233824,80.3800713533813109,84.9274305013996553],"hsluv":[127.715012949233824,21.3181094477761164,84.9274305013996553]},"#bbddcc":{"lch":[85.3200677868051,19.876471170849122,151.617083508593169],"luv":[85.3200677868051,-17.4871274790593141,9.44851727715650291],"rgb":[0.733333333333333282,0.866666666666666696,0.8],"xyz":[0.572463019803646445,0.666365412951942182,0.669747818501891],"hpluv":[151.617083508593169,58.7374284962306703,85.3200677868051],"hsluv":[151.617083508593169,24.0698773292010699,85.3200677868051]},"#bbdddd":{"lch":[85.7544476215988,18.2643967282432129,192.177050630059739],"luv":[85.7544476215988,-17.8534562098583329,-3.85256916996079246],"rgb":[0.733333333333333282,0.866666666666666696,0.866666666666666696],"xyz":[0.593981345354279933,0.674972743172195688,0.783077666401897],"hpluv":[192.177050630059739,55.8245506661868234,85.7544476215988],"hsluv":[192.177050630059739,26.9205986284609722,85.7544476215988]},"#bbddee":{"lch":[86.2308493598319359,25.3453434934362818,223.980933900351914],"luv":[86.2308493598319359,-18.2377721186398958,-17.6002870700688909],"rgb":[0.733333333333333282,0.866666666666666696,0.933333333333333348],"xyz":[0.617793525620454753,0.684497615278665705,0.908488482470420244],"hpluv":[223.980933900351914,80.4705047412629142,86.2308493598319359],"hsluv":[223.980933900351914,55.5800524236117397,86.2308493598319359]},"#bbddff":{"lch":[86.7493734858622076,36.743147173338194,239.520163688183],"luv":[86.7493734858622076,-18.6374141011141354,-31.6655279416778761],"rgb":[0.733333333333333282,0.866666666666666696,1],"xyz":[0.643964574030286063,0.694966034642598429,1.0463226707622022],"hpluv":[239.520163688183,121.752323062157373,86.7493734858622076],"hsluv":[239.520163688183,99.9999999999938325,86.7493734858622076]},"#99ee00":{"lch":[85.9664003491010646,109.204546268980621,112.979852313128234],"luv":[85.9664003491010646,-42.6342645949387915,100.538313136150634],"rgb":[0.6,0.933333333333333348,0],"xyz":[0.437097727388796042,0.679199404132492912,0.108068425671767623],"hpluv":[112.979852313128234,339.428934639809256,85.9664003491010646],"hsluv":[112.979852313128234,100.000000000002444,85.9664003491010646]},"#99ee11":{"lch":[85.9866468155118326,108.350123359521945,113.156064955940181],"luv":[85.9866468155118326,-42.6072764400583495,99.6211284135251702],"rgb":[0.6,0.933333333333333348,0.0666666666666666657],"xyz":[0.438109392888433147,0.67960407033234782,0.11339653063652326],"hpluv":[113.156064955940181,337.317445797395,85.9866468155118326],"hsluv":[113.156064955940181,99.0182391276988,85.9866468155118326]},"#99ee22":{"lch":[86.0241571186350455,106.77763062148405,113.488368653358094],"luv":[86.0241571186350455,-42.5576013317541353,97.9301433166804287],"rgb":[0.6,0.933333333333333348,0.133333333333333331],"xyz":[0.439984751026910204,0.680354213587738621,0.123273416832502453],"hpluv":[113.488368653358094,333.419677856626379,86.0241571186350455],"hsluv":[113.488368653358094,97.2091916908085238,86.0241571186350455]},"#99ee33":{"lch":[86.0858572784747906,104.220652596579697,114.05202992935186],"luv":[86.0858572784747906,-42.476800751453851,95.1717701084636],"rgb":[0.6,0.933333333333333348,0.2],"xyz":[0.443072501759367909,0.681589313880721748,0.139535570690113603],"hpluv":[114.05202992935186,327.048640581280779,86.0858572784747906],"hsluv":[114.05202992935186,94.2610252844147709,86.0858572784747906]},"#99ee44":{"lch":[86.1748066309051239,100.599171066032113,114.904029942881266],"luv":[86.1748066309051239,-42.3622716989010186,91.2448966007484756],"rgb":[0.6,0.933333333333333348,0.266666666666666663],"xyz":[0.447530497003586825,0.683372511978409314,0.163014345643000241],"hpluv":[114.904029942881266,317.953677409096201,86.1748066309051239],"hsluv":[114.904029942881266,90.0700159696272777,86.1748066309051239]},"#99ee55":{"lch":[86.2935317577421586,95.884762204841536,116.119599614249481],"luv":[86.2935317577421586,-42.2129155656731854,86.0927254913233355],"rgb":[0.6,0.933333333333333348,0.333333333333333315],"xyz":[0.45349291104294881,0.685757477594154063,0.194416392916974157],"hpluv":[116.119599614249481,305.984138616458665,86.2935317577421586],"hsluv":[116.119599614249481,84.5822993375075924,86.2935317577421586]},"#99ee66":{"lch":[86.4441689863048879,90.1009507230509143,117.805124209421223],"luv":[86.4441689863048879,-42.0290075887870955,79.6978283411745565],"rgb":[0.6,0.933333333333333348,0.4],"xyz":[0.461077906816457,0.688791475903557382,0.234364037324118091],"hpluv":[117.805124209421223,291.090686380889622,86.4441689863048879],"hsluv":[117.805124209421223,77.7887546135139587,86.4441689863048879]},"#99ee77":{"lch":[86.6285404402691199,83.3280712532883712,120.117619079442107],"luv":[86.6285404402691199,-41.8120893585486826,72.0785449510868261],"rgb":[0.6,0.933333333333333348,0.466666666666666674],"xyz":[0.470391919836658901,0.692517081111638233,0.283417839230516],"hpluv":[120.117619079442107,273.344218704658658,86.6285404402691199],"hsluv":[120.117619079442107,69.7211272565473,86.6285404402691199]},"#99ee88":{"lch":[86.8481992617441563,75.7142340291470646,123.296374810060541],"luv":[86.8481992617441563,-41.5648380664255939,63.2851441582641],"rgb":[0.6,0.933333333333333348,0.533333333333333326],"xyz":[0.481532336938602701,0.696973247952415775,0.342090702634088195],"hpluv":[123.296374810060541,252.981665625798911,86.8481992617441563],"hsluv":[123.296374810060541,60.4476982156994964,86.8481992617441563]},"#99ee99":{"lch":[87.1044587056640864,67.4980907628946483,127.715012949238613],"luv":[87.1044587056640864,-41.2908999939099388,53.3952604107227131],"rgb":[0.6,0.933333333333333348,0.6],"xyz":[0.4945893010476754,0.702196033596045,0.410857380275206074],"hpluv":[127.715012949238613,230.504268403717248,87.1044587056640864],"hsluv":[127.715012949238613,50.0680020008431583,87.1044587056640864]},"#99eeaa":{"lch":[87.3984122167822477,59.0554460818204134,133.961345837807],"luv":[87.3984122167822477,-40.9946911699179566,42.5085992218733182],"rgb":[0.6,0.933333333333333348,0.66666666666666663],"xyz":[0.509646994012928,0.708219110782146,0.490161229892204675],"hpluv":[133.961345837807,206.883546406693341,87.3984122167822477],"hsluv":[133.961345837807,51.4281429353489514,87.3984122167822477]},"#99eebb":{"lch":[87.7309483141988409,50.9899623050450046,142.923108379062683],"luv":[87.7309483141988409,-40.6811762109167603,30.7411476358032338],"rgb":[0.6,0.933333333333333348,0.733333333333333282],"xyz":[0.526784586120264575,0.715074147625080814,0.580419214990846477],"hpluv":[142.923108379062683,183.977628608542716,87.7309483141988409],"hsluv":[142.923108379062683,52.8887094272277523,87.7309483141988409]},"#99eecc":{"lch":[88.1027624984453581,44.2777406304916781,155.702396707036257],"luv":[88.1027624984453581,-40.3556400464907483,18.219237958245273],"rgb":[0.6,0.933333333333333348,0.8],"xyz":[0.546076961563986329,0.722791097802569671,0.682025725661116633],"hpluv":[155.702396707036257,165.259943503195302,88.1027624984453581],"hsluv":[155.702396707036257,54.4312604691531305,88.1027624984453581]},"#99eedd":{"lch":[88.514367527899708,40.3437945821223494,172.775068456479858],"luv":[88.514367527899708,-40.023467685182176,5.07383442158517628],"rgb":[0.6,0.933333333333333348,0.866666666666666696],"xyz":[0.567595287114619929,0.731398428022823177,0.795355573561122631],"hpluv":[172.775068456479858,156.503534311163719,88.514367527899708],"hsluv":[172.775068456479858,56.0368199873083199,88.514367527899708]},"#99eeee":{"lch":[88.9661029048661476,40.6035054290346196,192.177050630060762],"luv":[88.9661029048661476,-39.6899452486728,-8.56463071492281891],"rgb":[0.6,0.933333333333333348,0.933333333333333348],"xyz":[0.591407467380794638,0.740923300129293194,0.920766389629645854],"hpluv":[192.177050630060762,164.568757380081195,88.9661029048661476],"hsluv":[192.177050630060762,57.6866071933722324,88.9661029048661476]},"#99eeff":{"lch":[89.4581440962481338,45.3723498615675496,209.831659516829347],"luv":[89.4581440962481338,-39.3600920674606272,-22.5706287994267498],"rgb":[0.6,0.933333333333333348,1],"xyz":[0.617578515790626059,0.751391719493225918,1.05860057792142759],"hpluv":[209.831659516829347,193.255751503994162,89.4581440962481338],"hsluv":[209.831659516829347,99.9999999999917293,89.4581440962481338]},"#446600":{"lch":[39.1245088935371612,48.4489514943966242,110.29724752770484],"luv":[39.1245088935371612,-16.806485623906017,45.4405429311736668],"rgb":[0.266666666666666663,0.4,0],"xyz":[0.0713500585462719106,0.107314951185536064,0.0169546366210883669],"hpluv":[110.29724752770484,157.13568029472475,39.1245088935371612],"hsluv":[110.29724752770484,100.000000000002302,39.1245088935371612]},"#446611":{"lch":[39.1937103273453289,46.0071342898824156,111.370502443062165],"luv":[39.1937103273453289,-16.7648801632844737,42.843846683952],"rgb":[0.266666666666666663,0.4,0.0666666666666666657],"xyz":[0.0723617240459090288,0.107719617385390917,0.022282741585844],"hpluv":[111.370502443062165,148.9526145018813,39.1937103273453289],"hsluv":[111.370502443062165,93.861989786091,39.1937103273453289]},"#446622":{"lch":[39.3215343087732165,41.6757739733598,113.610346420587263],"luv":[39.3215343087732165,-16.6917518579939177,38.1871124358693947],"rgb":[0.266666666666666663,0.4,0.133333333333333331],"xyz":[0.0742370821843860579,0.108469760640781732,0.0321596277818231926],"hpluv":[113.610346420587263,134.490790122462073,39.3215343087732165],"hsluv":[113.610346420587263,82.9003633409612,39.3215343087732165]},"#446633":{"lch":[39.530716823468623,35.0960892868442684,118.194686061271412],"luv":[39.530716823468623,-16.5818151114938139,30.931842668007679],"rgb":[0.266666666666666663,0.4,0.2],"xyz":[0.0773248329168437915,0.109704860933764844,0.0484217816394343359],"hpluv":[118.194686061271412,112.658344014206364,39.530716823468623],"hsluv":[118.194686061271412,65.945418212904,39.530716823468623]},"#446644":{"lch":[39.8299759493166761,26.8801274380109838,127.715012949238059],"luv":[39.8299759493166761,-16.4434970133495142,21.2638815143349724],"rgb":[0.266666666666666663,0.4,0.266666666666666663],"xyz":[0.0817828281610626651,0.111488059031452424,0.0719005565923209744],"hpluv":[127.715012949238059,85.6368345314009,39.8299759493166761],"hsluv":[127.715012949238059,43.6044123282566858,39.8299759493166761]},"#446655":{"lch":[40.2252775564066809,18.9132283301115756,149.466153210293243],"luv":[40.2252775564066809,-16.2905155992652197,9.6088140463343148],"rgb":[0.266666666666666663,0.4,0.333333333333333315],"xyz":[0.087745242200424664,0.113873024647197257,0.10330260386629489],"hpluv":[149.466153210293243,59.6631175613842473,40.2252775564066809],"hsluv":[149.466153210293243,47.2452022491104273,40.2252775564066809]},"#446666":{"lch":[40.7202569602655728,16.510473073285187,192.177050630060847],"luv":[40.7202569602655728,-16.1389950297194247,-3.48260829470770128],"rgb":[0.266666666666666663,0.4,0.4],"xyz":[0.0953302379739328354,0.116907022956600576,0.143250248273438852],"hpluv":[192.177050630060847,51.4503500463259655,40.7202569602655728],"hsluv":[192.177050630060847,51.2489582821824214,40.7202569602655728]},"#446677":{"lch":[41.3164898648363632,23.6752994583974647,227.46784435344162],"luv":[41.3164898648363632,-16.0045942322577233,-17.4462823519963166],"rgb":[0.266666666666666663,0.4,0.466666666666666674],"xyz":[0.104644250994134755,0.120632628164681385,0.192304050179836761],"hpluv":[227.46784435344162,72.7128872034151783,41.3164898648363632],"hsluv":[227.46784435344162,55.4049423234493261,41.3164898648363632]},"#446688":{"lch":[42.0137303768536654,35.5424939627414,243.425378218088099],"luv":[42.0137303768536654,-15.9003964812462133,-31.7875175002811758],"rgb":[0.266666666666666663,0.4,0.533333333333333326],"xyz":[0.115784668096078583,0.125088795005458969,0.2509769135834089],"hpluv":[243.425378218088099,107.348500116853913,42.0137303768536654],"hsluv":[243.425378218088099,59.5313600522085409,42.0137303768536654]},"#446699":{"lch":[42.8101553746289696,48.7666560663799089,251.050944767945737],"luv":[42.8101553746289696,-15.8358652521886238,-46.1238779333558782],"rgb":[0.266666666666666663,0.4,0.6],"xyz":[0.128841632205151296,0.130311580649088155,0.319743591224526835],"hpluv":[251.050944767945737,144.549135771893674,42.8101553746289696],"hsluv":[251.050944767945737,63.4907652576423231,42.8101553746289696]},"#4466aa":{"lch":[43.7026231888915078,62.2363741998113298,255.27738032783347],"luv":[43.7026231888915078,-15.8167390859625119,-60.1929982491778404],"rgb":[0.266666666666666663,0.4,0.66666666666666663],"xyz":[0.143899325170403825,0.136334657835189249,0.399047440841525436],"hpluv":[255.27738032783347,180.707469421570693,43.7026231888915078],"hsluv":[255.27738032783347,67.1924648453768896,43.7026231888915078]},"#4466bb":{"lch":[44.6869405362706402,75.5191451090451409,257.888120209084718],"luv":[44.6869405362706402,-15.8455247037125186,-73.83807029483539],"rgb":[0.266666666666666663,0.4,0.733333333333333282],"xyz":[0.161036917277740499,0.143189694678124,0.489305425940167182],"hpluv":[257.888120209084718,214.444923406304468,44.6869405362706402],"hsluv":[257.888120209084718,70.5866413561882098,44.6869405362706402]},"#4466cc":{"lch":[45.7581261645645299,88.428992560325014,259.626929599743789],"luv":[45.7581261645645299,-15.9222446749778896,-86.983727499712],"rgb":[0.266666666666666663,0.4,0.8],"xyz":[0.180329292721462225,0.150906644855612809,0.590911936610437283],"hpluv":[259.626929599743789,245.225581131482073,45.7581261645645299],"hsluv":[259.626929599743789,73.6549291646736179,45.7581261645645299]},"#4466dd":{"lch":[46.9106590907269165,100.895184366494064,260.849495088158733],"luv":[46.9106590907269165,-16.0451985527389489,-99.6111933055317422],"rgb":[0.266666666666666663,0.4,0.866666666666666696],"xyz":[0.201847618272095769,0.159513975075866343,0.704241784510443281],"hpluv":[260.849495088158733,272.92180192815988,46.9106590907269165],"hsluv":[260.849495088158733,78.541907894368677,46.9106590907269165]},"#4466ee":{"lch":[48.1387014995781897,112.90717489320852,261.744736192665698],"luv":[48.1387014995781897,-16.2116120228509466,-111.737253313235385],"rgb":[0.266666666666666663,0.4,0.933333333333333348],"xyz":[0.225659798538270506,0.16903884718233636,0.829652600578966504],"hpluv":[261.744736192665698,297.623004098842955,48.1387014995781897],"hsluv":[261.744736192665698,89.2025828989012126,48.1387014995781897]},"#4466ff":{"lch":[49.4362898036433194,124.485902067172418,262.421323096893047],"luv":[49.4362898036433194,-16.4181334215125325,-123.398479360286501],"rgb":[0.266666666666666663,0.4,1],"xyz":[0.251830846948101872,0.179507266546269084,0.967486788870748349],"hpluv":[262.421323096893047,319.531462910383539,49.4362898036433194],"hsluv":[262.421323096893047,99.9999999999992,49.4362898036433194]},"#bbee00":{"lch":[87.830324097455545,103.474670767003104,103.901308289378321],"luv":[87.830324097455545,-24.8598111448325625,100.444000717728017],"rgb":[0.733333333333333282,0.933333333333333348,0],"xyz":[0.510662561968970863,0.717131271962896388,0.111516777292713254],"hpluv":[103.901308289378321,376.707439149390723,87.830324097455545],"hsluv":[103.901308289378321,100.000000000002331,87.830324097455545]},"#bbee11":{"lch":[87.8498503793181413,102.621932764236291,104.023160127863235],"luv":[87.8498503793181413,-24.8667401733108697,99.5635792718425705],"rgb":[0.733333333333333282,0.933333333333333348,0.0666666666666666657],"xyz":[0.511674227468608,0.717535938162751297,0.116844882257468891],"hpluv":[104.023160127863235,374.263941750279344,87.8498503793181413],"hsluv":[104.023160127863235,99.0733759990956315,87.8498503793181413]},"#bbee22":{"lch":[87.8860274909110757,101.050540606732312,104.253301125000391],"luv":[87.8860274909110757,-24.8795662288424886,97.9398741125263],"rgb":[0.733333333333333282,0.933333333333333348,0.133333333333333331],"xyz":[0.513549585607085,0.718286081418142097,0.126721768453448069],"hpluv":[104.253301125000391,369.744422226518111,87.8860274909110757],"hsluv":[104.253301125000391,97.3653524759520224,87.8860274909110757]},"#bbee33":{"lch":[87.9455377581158899,98.4895893313188111,104.644745385959411],"luv":[87.9455377581158899,-24.9006321902472507,95.2898615970127878],"rgb":[0.733333333333333282,0.933333333333333348,0.2],"xyz":[0.516637336339542785,0.719521181711125224,0.142983922311059219],"hpluv":[104.644745385959411,362.331462484484575,87.9455377581158899],"hsluv":[104.644745385959411,94.5802259429375454,87.9455377581158899]},"#bbee44":{"lch":[88.03133674845418,94.8494424101932339,105.239087045756335],"luv":[88.03133674845418,-24.9309340294718567,91.5142898892991781],"rgb":[0.733333333333333282,0.933333333333333348,0.266666666666666663],"xyz":[0.521095331583761645,0.72130437980881279,0.166462697263945858],"hpluv":[105.239087045756335,351.690778632920285,88.03133674845418],"hsluv":[105.239087045756335,90.61757843521751,88.03133674845418]},"#bbee55":{"lch":[88.145869167101,90.085489786575252,106.092871571181419],"luv":[88.145869167101,-24.9712578135623886,86.5553681367929499],"rgb":[0.733333333333333282,0.933333333333333348,0.333333333333333315],"xyz":[0.527057745623123575,0.72368934542455754,0.197864744537919773],"hpluv":[106.092871571181419,337.573290416608245,88.145869167101],"hsluv":[106.092871571181419,85.422816781051381,88.145869167101]},"#bbee66":{"lch":[88.2912067804751786,84.1961694748550116,107.288882190620882],"luv":[88.2912067804751786,-25.0222263001917433,80.3920589686599527],"rgb":[0.733333333333333282,0.933333333333333348,0.4],"xyz":[0.534642741396631815,0.726723343733960858,0.237812388945063735],"hpluv":[107.288882190620882,319.804166688067426,88.2912067804751786],"hsluv":[107.288882190620882,78.9823949944632631,88.2912067804751786]},"#bbee77":{"lch":[88.4691221578712828,77.2243366572971439,108.954917992899311],"luv":[88.4691221578712828,-25.0843250262429365,73.0368044901840534],"rgb":[0.733333333333333282,0.933333333333333348,0.466666666666666674],"xyz":[0.543956754416833665,0.730448948942041709,0.286866190851461644],"hpluv":[108.954917992899311,298.285685525501094,88.4691221578712828],"hsluv":[108.954917992899311,71.3203705839542,88.4691221578712828]},"#bbee88":{"lch":[88.6811325759484106,69.2626802552854599,111.298371855766348],"luv":[88.6811325759484106,-25.1579200624522237,64.5321465184381822],"rgb":[0.733333333333333282,0.933333333333333348,0.533333333333333326],"xyz":[0.555097171518777577,0.734905115782819252,0.34553905425503384],"hpluv":[111.298371855766348,273.019747479454225,88.6811325759484106],"hsluv":[111.298371855766348,62.4946759175697224,88.6811325759484106]},"#bbee99":{"lch":[88.9285282571087095,60.4678989000773228,114.674688063820184],"luv":[88.9285282571087095,-25.2432722518949362,54.9467378777547069],"rgb":[0.733333333333333282,0.933333333333333348,0.6],"xyz":[0.568154135627850221,0.740127901426448465,0.414305731896151719],"hpluv":[114.674688063820184,244.173567048365754,88.9285282571087095],"hsluv":[114.674688063820184,52.5925914284503548,88.9285282571087095]},"#bbeeaa":{"lch":[89.2123917942545148,51.0969811847688931,119.731125412935242],"luv":[89.2123917942545148,-25.3405499711494109,44.3706886734509212],"rgb":[0.733333333333333282,0.933333333333333348,0.66666666666666663],"xyz":[0.583211828593102832,0.746150978612549531,0.493609581513150319],"hpluv":[119.731125412935242,212.254877789093854,89.2123917942545148],"hsluv":[119.731125412935242,41.7253920748903084,89.2123917942545148]},"#bbeebb":{"lch":[89.5336124490095,41.6027669810227181,127.715012949236097],"luv":[89.5336124490095,-25.4498411950279184,32.9104208971130276],"rgb":[0.733333333333333282,0.933333333333333348,0.733333333333333282],"xyz":[0.600349420700439396,0.75300601545548429,0.583867566611792066],"hpluv":[127.715012949236097,178.587287563939924,89.5336124490095],"hsluv":[127.715012949236097,37.7465419844818,89.5336124490095]},"#bbeecc":{"lch":[89.8928974614309766,32.8889663829363883,141.03222670888556],"luv":[89.8928974614309766,-25.571165029146556,20.6833176446640721],"rgb":[0.733333333333333282,0.933333333333333348,0.8],"xyz":[0.61964179614416115,0.760722965632973147,0.685474077282062222],"hpluv":[141.03222670888556,146.627829178506289,89.8928974614309766],"hsluv":[141.03222670888556,37.1073685119788,89.8928974614309766]},"#bbeedd":{"lch":[90.290781675649967,26.8655052232995288,163.094148385079734],"luv":[90.290781675649967,-25.704482585445124,7.81248653872008436],"rgb":[0.733333333333333282,0.933333333333333348,0.866666666666666696],"xyz":[0.641160121694794749,0.769330295853226653,0.79880392518206822],"hpluv":[163.094148385079734,125.084011642469022,90.290781675649967],"hsluv":[163.094148385079734,36.2429238875219255,90.290781675649967]},"#bbeeee":{"lch":[90.7276363011350782,26.4447005646089579,192.177050630060336],"luv":[90.7276363011350782,-25.8497069757021904,-5.5780675168164624],"rgb":[0.733333333333333282,0.933333333333333348,0.933333333333333348],"xyz":[0.664972301960969459,0.77885516795969667,0.924214741250591443],"hpluv":[192.177050630060336,129.38009793870765,90.7276363011350782],"hsluv":[192.177050630060336,36.8413123479339077,90.7276363011350782]},"#bbeeff":{"lch":[91.2036773284388289,32.4261328799939648,216.675663960190036],"luv":[91.2036773284388289,-26.0067122100428421,-19.367627980086624],"rgb":[0.733333333333333282,0.933333333333333348,1],"xyz":[0.69114335037080088,0.789323587323629394,1.06204892954237318],"hpluv":[216.675663960190036,167.86892764061264,91.2036773284388289],"hsluv":[216.675663960190036,99.9999999999901235,91.2036773284388289]},"#99ff00":{"lch":[90.9122626200542214,118.290950299530564,115.261698016578393],"luv":[90.9122626200542214,-50.4810643573161286,106.978554225220847],"rgb":[0.6,1,0],"xyz":[0.48895009981846993,0.782904148991842,0.125352549814991721],"hpluv":[115.261698016578393,591.369219456757151,90.9122626200542214],"hsluv":[115.261698016578393,100.000000000002402,90.9122626200542214]},"#99ff11":{"lch":[90.9306796583728,117.513960007406183,115.422432906031332],"luv":[90.9306796583728,-50.4474247175277384,106.134764031356056],"rgb":[0.6,1,0.0666666666666666657],"xyz":[0.489961765318107034,0.783308815191696928,0.130680654779747357],"hpluv":[115.422432906031332,588.764777700997797,90.9306796583728],"hsluv":[115.422432906031332,99.9999999999905356,90.9306796583728]},"#99ff22":{"lch":[90.9648031673089577,116.082935310332914,115.724658723180369],"luv":[90.9648031673089577,-50.3854319755480944,104.577990585497247],"rgb":[0.6,1,0.133333333333333331],"xyz":[0.491837123456584091,0.784058958447087728,0.140557540975726536],"hpluv":[115.724658723180369,583.951350293788323,90.9648031673089577],"hsluv":[115.724658723180369,99.9999999999904219,90.9648031673089577]},"#99ff33":{"lch":[91.0209396556716399,113.753057852482627,116.234667099004312],"luv":[91.0209396556716399,-50.2843870930134642,102.035477092383132],"rgb":[0.6,1,0.2],"xyz":[0.494924874189041797,0.785294058740070855,0.156819694833337686],"hpluv":[116.234667099004312,576.067585056329449,91.0209396556716399],"hsluv":[116.234667099004312,99.9999999999905924,91.0209396556716399]},"#99ff44":{"lch":[91.1018839706868562,110.446777199697451,116.999375135233294],"luv":[91.1018839706868562,-50.1407143313292565,98.409345898362929],"rgb":[0.6,1,0.266666666666666663],"xyz":[0.499382869433260712,0.787077256837758421,0.180298469786224325],"hpluv":[116.999375135233294,564.777897061868,91.1018839706868562],"hsluv":[116.999375135233294,99.9999999999903793,91.1018839706868562]},"#99ff55":{"lch":[91.2099533036034558,106.130684884370282,118.07780645420398],"luv":[91.2099533036034558,-49.9525457622324112,93.6400846106921279],"rgb":[0.6,1,0.333333333333333315],"xyz":[0.505345283472622642,0.789462222453503171,0.21170051706019824],"hpluv":[118.07780645420398,549.854625367801646,91.2099533036034558],"hsluv":[118.07780645420398,99.9999999999901377,91.2099533036034558]},"#99ff66":{"lch":[91.3471179899501351,100.815112661229691,119.549522622156928],"luv":[91.3471179899501351,-49.7195590247369807,87.7020660605103899],"rgb":[0.6,1,0.4],"xyz":[0.512930279246130882,0.792496220762906489,0.251648161467342202],"hpluv":[119.549522622156928,531.176396965461777,91.3471179899501351],"hsluv":[119.549522622156928,99.9999999999902656,91.3471179899501351]},"#99ff77":{"lch":[91.5150716426202,94.5571579863012488,121.52621580287979],"luv":[91.5150716426202,-49.442863485124569,80.6006164792656534],"rgb":[0.6,1,0.466666666666666674],"xyz":[0.522244292266332732,0.79622182597098734,0.300701963373740111],"hpluv":[121.52621580287979,508.746904082031563,91.5150716426202],"hsluv":[121.52621580287979,99.9999999999900098,91.5150716426202]},"#99ff88":{"lch":[91.7152730008064481,87.4673964918121385,124.169042550433474],"luv":[91.7152730008064481,-49.1248751175299461,72.3691377159004077],"rgb":[0.6,1,0.533333333333333326],"xyz":[0.533384709368276644,0.800677992811764883,0.359374826777312251],"hpluv":[124.169042550433474,482.74240688111388,91.7152730008064481],"hsluv":[124.169042550433474,99.999999999989754,91.7152730008064481]},"#99ff99":{"lch":[91.9489728509177,79.7227767672504,127.715012949239039],"luv":[91.9489728509177,-48.7691602166467675,63.0657604984184559],"rgb":[0.6,1,0.6],"xyz":[0.546441673477349288,0.805900778455394096,0.428141504418430185],"hpluv":[127.715012949239039,453.611771943482722,91.9489728509177],"hsluv":[127.715012949239039,99.9999999999896119,91.9489728509177]},"#99ffaa":{"lch":[92.2172324754492365,71.5909188251234525,132.515331265000469],"luv":[92.2172324754492365,-48.380245540777544,52.769418223432119],"rgb":[0.6,1,0.66666666666666663],"xyz":[0.561499366442601899,0.811923855641495162,0.507445354035428786],"hpluv":[132.515331265000469,422.278899597361089,92.2172324754492365],"hsluv":[132.515331265000469,99.999999999989285,92.2172324754492365]},"#99ffbb":{"lch":[92.5209371039878334,63.4744472180726902,139.080731133451],"luv":[92.5209371039878334,-47.9634018094000325,41.5754439123617558],"rgb":[0.6,1,0.733333333333333282],"xyz":[0.578636958549938463,0.818778892484429921,0.597703339134070477],"hpluv":[139.080731133451,390.542311531215887,92.5209371039878334],"hsluv":[139.080731133451,99.9999999999889866,92.5209371039878334]},"#99ffcc":{"lch":[92.8608063839845,55.9838497302527216,148.091690615728766],"luv":[92.8608063839845,-47.524412431304512,29.5909049148346774],"rgb":[0.6,1,0.8],"xyz":[0.597929333993660217,0.826495842661918778,0.699309849804340633],"hpluv":[148.091690615728766,361.818313682118117,92.8608063839845],"hsluv":[148.091690615728766,99.999999999988475,92.8608063839845]},"#99ffdd":{"lch":[93.237403107538583,50.0214511206169306,160.217319691395858],"luv":[93.237403107538583,-47.0693412211659066,16.9299347080170577],"rgb":[0.6,1,0.866666666666666696],"xyz":[0.619447659544293816,0.835103172882172284,0.812639697704346631],"hpluv":[160.217319691395858,342.295010089686684,93.237403107538583],"hsluv":[160.217319691395858,99.999999999988,93.237403107538583]},"#99ffee":{"lch":[93.6511409780710267,46.7516947594382444,175.449324589633676],"luv":[93.6511409780710267,-46.6043124006013088,3.70931645277729727],"rgb":[0.6,1,0.933333333333333348],"xyz":[0.643259839810468526,0.844628044988642301,0.938050513772869854],"hpluv":[175.449324589633676,341.869484566500148,93.6511409780710267],"hsluv":[175.449324589633676,99.9999999999875,93.6511409780710267]},"#99ffff":{"lch":[94.102291921527609,47.1972299789563579,192.177050630060847],"luv":[94.102291921527609,-46.1353140316371437,-9.95546668362271703],"rgb":[0.6,1,1],"xyz":[0.6694308882203,0.855096464352575,1.07588470206465181],"hpluv":[192.177050630060847,372.830957625984183,94.102291921527609],"hsluv":[192.177050630060847,99.999999999986585,94.102291921527609]},"#447700":{"lch":[44.83248944102629,58.4741115144389809,115.479055163134589],"luv":[44.83248944102629,-25.1544589141716,52.7870714677211339],"rgb":[0.266666666666666663,0.466666666666666674,0],"xyz":[0.0898037965996895393,0.144222427292371835,0.0231058826388940708],"hpluv":[115.479055163134589,165.50461307776385,44.83248944102629],"hsluv":[115.479055163134589,100.00000000000216,44.83248944102629]},"#447711":{"lch":[44.8893318820142468,56.4264635629906337,116.3750363045054],"luv":[44.8893318820142468,-25.0671673404006405,50.5527735317644158],"rgb":[0.266666666666666663,0.466666666666666674,0.0666666666666666657],"xyz":[0.0908154620993266576,0.144627093492226688,0.0284339876036497],"hpluv":[116.3750363045054,159.506732057184706,44.8893318820142468],"hsluv":[116.3750363045054,95.4073228169634433,44.8893318820142468]},"#447722":{"lch":[44.9944227930058389,52.7636609180533824,118.171656995978211],"luv":[44.9944227930058389,-24.9105023189221484,46.5131248971114388],"rgb":[0.266666666666666663,0.466666666666666674,0.133333333333333331],"xyz":[0.0926908202378036866,0.145377236747617516,0.0383108737996289],"hpluv":[118.171656995978211,148.804327803607492,44.9944227930058389],"hsluv":[118.171656995978211,87.128369469673089,44.9944227930058389]},"#447733":{"lch":[45.1666686690004795,47.1090234718488,121.574089601592789],"luv":[45.1666686690004795,-24.6663168629185812,40.1351828809748881],"rgb":[0.266666666666666663,0.466666666666666674,0.2],"xyz":[0.0957785709702614202,0.146612337040600615,0.0545730276572400433],"hpluv":[121.574089601592789,132.350433634303585,45.1666686690004795],"hsluv":[121.574089601592789,74.1245535442141232,45.1666686690004795]},"#447744":{"lch":[45.4136534494367368,39.7908238780444137,127.715012949239082],"luv":[45.4136534494367368,-24.3414133770851677,31.4770592606648769],"rgb":[0.266666666666666663,0.466666666666666674,0.266666666666666663],"xyz":[0.100236566214480294,0.148395535138288209,0.0780518026101266749],"hpluv":[127.715012949239082,111.18234158710581,45.4136534494367368],"hsluv":[127.715012949239082,56.6116285441109,45.4136534494367368]},"#447755":{"lch":[45.7409133262359902,31.7219583115713704,139.030718806592802],"luv":[45.7409133262359902,-23.9520203549447466,20.798638417872084],"rgb":[0.266666666666666663,0.466666666666666674,0.333333333333333315],"xyz":[0.106198980253842293,0.150780500754033042,0.10945384988410059],"hpluv":[139.030718806592802,88.0023935360314766,45.7409133262359902],"hsluv":[139.030718806592802,58.7992252631310066,45.7409133262359902]},"#447766":{"lch":[46.1522822994750328,25.0075472636366349,160.141849635166039],"luv":[46.1522822994750328,-23.5205108300662609,8.49488024846472],"rgb":[0.266666666666666663,0.466666666666666674,0.4],"xyz":[0.113783976027350464,0.153814499063436333,0.149401494291244552],"hpluv":[160.141849635166039,68.7570511480334,46.1522822994750328],"hsluv":[160.141849635166039,61.2825519214888672,46.1522822994750328]},"#447777":{"lch":[46.650089933282672,23.6026970305602966,192.177050630060933],"luv":[46.650089933282672,-23.0716472128555,-4.97859438013953248],"rgb":[0.266666666666666663,0.466666666666666674,0.466666666666666674],"xyz":[0.123097989047552384,0.157540104271517156,0.198455296197642461],"hpluv":[192.177050630060933,64.2019875277067,46.650089933282672],"hsluv":[192.177050630060933,63.9506821134950414,46.650089933282672]},"#447788":{"lch":[47.2353114433284134,29.6592888392359697,220.273189901853613],"luv":[47.2353114433284134,-22.6291741496980734,-19.1727382434503575],"rgb":[0.266666666666666663,0.466666666666666674,0.533333333333333326],"xyz":[0.134238406149496226,0.161996271112294754,0.257128159601214601],"hpluv":[220.273189901853613,79.6770556088496,47.2353114433284134],"hsluv":[220.273189901853613,66.6958169909158585,47.2353114433284134]},"#447799":{"lch":[47.9077085122244526,40.3569508451274856,236.603980798736757],"luv":[47.9077085122244526,-22.2133832668581981,-33.6934576046404],"rgb":[0.266666666666666663,0.466666666666666674,0.6],"xyz":[0.147295370258568925,0.167219056755923912,0.325894837242332536],"hpluv":[236.603980798736757,106.893740352524901,47.9077085122244526],"hsluv":[236.603980798736757,69.4246608010291197,47.9077085122244526]},"#4477aa":{"lch":[48.6659751090485,52.9419227285019574,245.636459716194196],"luv":[48.6659751090485,-21.8398581964299439,-48.2272513849846476],"rgb":[0.266666666666666663,0.466666666666666674,0.66666666666666663],"xyz":[0.162353063223821481,0.173242133942025,0.405198686859331136],"hpluv":[245.636459716194196,138.042751347741245,48.6659751090485],"hsluv":[245.636459716194196,72.0643617009564,48.6659751090485]},"#4477bb":{"lch":[49.5078912458612,66.145388752002,251.014275727268966],"luv":[49.5078912458612,-21.5192486824625604,-62.5470573991759125],"rgb":[0.266666666666666663,0.466666666666666674,0.733333333333333282],"xyz":[0.1794906553311581,0.180097170784959765,0.495456671957972883],"hpluv":[251.014275727268966,169.536991245322184,49.5078912458612],"hsluv":[251.014275727268966,74.5637193650047578,49.5078912458612]},"#4477cc":{"lch":[50.4304819457797606,79.4031370336833,254.471410742848605],"luv":[50.4304819457797606,-21.2577420931439356,-76.5046833330565761],"rgb":[0.266666666666666663,0.466666666666666674,0.8],"xyz":[0.198783030774879854,0.187814120962448566,0.597063182628243094],"hpluv":[254.471410742848605,199.794656417231295,50.4304819457797606],"hsluv":[254.471410742848605,76.8911855438839638,50.4304819457797606]},"#4477dd":{"lch":[51.4301761714764183,92.4467030892661654,256.833353640671476],"luv":[51.4301761714764183,-21.0578873175855144,-90.0164334652001514],"rgb":[0.266666666666666663,0.466666666666666674,0.866666666666666696],"xyz":[0.220301356325513398,0.1964214511827021,0.710393030528249092],"hpluv":[256.833353640671476,228.093412194404408,51.4301761714764183],"hsluv":[256.833353640671476,79.0312420746427904,51.4301761714764183]},"#4477ee":{"lch":[52.5029598761355913,105.148975578657073,258.524369979875587],"luv":[52.5029598761355913,-20.9195063280493,-103.046985983248078],"rgb":[0.266666666666666663,0.466666666666666674,0.933333333333333348],"xyz":[0.244113536591688135,0.205946323289172145,0.835803846596772315],"hpluv":[258.524369979875587,254.132720010738154,52.5029598761355913],"hsluv":[258.524369979875587,87.6254974090696,52.5029598761355913]},"#4477ff":{"lch":[53.6445179522116,117.458222301342076,259.779939364455799],"luv":[53.6445179522116,-20.8405325230258285,-115.594576820663164],"rgb":[0.266666666666666663,0.466666666666666674,1],"xyz":[0.270284585001519528,0.216414742653104841,0.973638034888554],"hpluv":[259.779939364455799,277.841684308431127,53.6445179522116],"hsluv":[259.779939364455799,99.999999999999,53.6445179522116]},"#bbff00":{"lch":[92.6117448358007778,112.09772632761252,107.605046807390437],"luv":[92.6117448358007778,-33.9043888880720843,106.847520616749406],"rgb":[0.733333333333333282,1,0],"xyz":[0.56251493439864475,0.820836016822245496,0.128800901435937365],"hpluv":[107.605046807390437,698.685604225905081,92.6117448358007778],"hsluv":[107.605046807390437,100.000000000002302,92.6117448358007778]},"#bbff11":{"lch":[92.6295901709342,111.320425805292416,107.729052066712285],"luv":[92.6295901709342,-33.8988588935206678,106.033506813595423],"rgb":[0.733333333333333282,1,0.0666666666666666657],"xyz":[0.56352659989828191,0.821240683022100404,0.134129006400693],"hpluv":[107.729052066712285,695.618439183936061,92.6295901709342],"hsluv":[107.729052066712285,99.9999999999893703,92.6295901709342]},"#bbff22":{"lch":[92.6626551654141,109.887399193340656,107.962519071076329],"luv":[92.6626551654141,-33.888700280085942,104.531318248662245],"rgb":[0.733333333333333282,1,0.133333333333333331],"xyz":[0.565401958036758856,0.821990826277491204,0.14400589259667218],"hpluv":[107.962519071076329,689.937572971075,92.6626551654141],"hsluv":[107.962519071076329,99.9999999999891713,92.6626551654141]},"#bbff33":{"lch":[92.7170524122382318,107.550157892444602,108.357408717671873],"luv":[92.7170524122382318,-33.8722333827189388,102.076972272674624],"rgb":[0.733333333333333282,1,0.2],"xyz":[0.568489708769216673,0.823225926570474331,0.160268046454283331],"hpluv":[108.357408717671873,680.59767216104342,92.7170524122382318],"hsluv":[108.357408717671873,99.9999999999894271,92.7170524122382318]},"#bbff44":{"lch":[92.7954935084343475,104.224206838295117,108.951712424811362],"luv":[92.7954935084343475,-33.8490183743317,98.5744857768270464],"rgb":[0.733333333333333282,1,0.266666666666666663],"xyz":[0.572947704013435533,0.825009124668161897,0.183746821407169969],"hpluv":[108.951712424811362,667.142517523628385,92.7954935084343475],"hsluv":[108.951712424811362,99.9999999999890576,92.7954935084343475]},"#bbff55":{"lch":[92.9002292713318809,99.8647848732016,109.794505003516988],"luv":[92.9002292713318809,-33.8189779888925628,93.9640994505754605],"rgb":[0.733333333333333282,1,0.333333333333333315],"xyz":[0.578910118052797462,0.827394090283906647,0.215148868681143884],"hpluv":[109.794505003516988,649.201221057988732,92.9002292713318809],"hsluv":[109.794505003516988,99.9999999999888445,92.9002292713318809]},"#bbff66":{"lch":[93.0331768315467826,94.4651128464689691,110.953951756581517],"luv":[93.0331768315467826,-33.7823795150753,88.2179595059619146],"rgb":[0.733333333333333282,1,0.4],"xyz":[0.586495113826305703,0.83042808859331,0.255096513088287846],"hpluv":[110.953951756581517,626.470359589551208,93.0331768315467826],"hsluv":[110.953951756581517,99.9999999999888871,93.0331768315467826]},"#bbff77":{"lch":[93.1959878807374196,88.0576290523864742,112.529317332402641],"luv":[93.1959878807374196,-33.7398191848892566,81.3373876867131571],"rgb":[0.733333333333333282,1,0.466666666666666674],"xyz":[0.595809126846507553,0.834153693801390816,0.304150314994685755],"hpluv":[112.529317332402641,598.713569857690459,93.1959878807374196],"hsluv":[112.529317332402641,99.9999999999886455,93.1959878807374196]},"#bbff88":{"lch":[93.3900894470196334,80.7182094219550805,114.670873894576516],"luv":[93.3900894470196334,-33.6921989698203319,73.3502901212028888],"rgb":[0.733333333333333282,1,0.533333333333333326],"xyz":[0.606949543948451464,0.838609860642168359,0.362823178398257951],"hpluv":[114.670873894576516,565.785048639302204,93.3900894470196334],"hsluv":[114.670873894576516,99.9999999999880913,93.3900894470196334]},"#bbff99":{"lch":[93.6167101348934239,72.575742292483838,117.61482369288214],"luv":[93.6167101348934239,-33.6406927329059684,64.3081811417119411],"rgb":[0.733333333333333282,1,0.6],"xyz":[0.620006508057524108,0.843832646285797572,0.43158985603937583],"hpluv":[117.61482369288214,527.702623330867254,93.6167101348934239],"hsluv":[117.61482369288214,99.9999999999881,93.6167101348934239]},"#bbffaa":{"lch":[93.8768980816178384,63.8331941826517948,121.746607961207644],"luv":[93.8768980816178384,-33.5867019719833948,54.2826872050868303],"rgb":[0.733333333333333282,1,0.66666666666666663],"xyz":[0.63506420102277672,0.849855723471898639,0.510893705656374486],"hpluv":[121.746607961207644,484.836572610502856,93.8768980816178384],"hsluv":[121.746607961207644,99.9999999999878781,93.8768980816178384]},"#bbffbb":{"lch":[94.1715339943383,54.8143223318485937,127.715012949237362],"luv":[94.1715339943383,-33.5318032859449531,43.3615970772812602],"rgb":[0.733333333333333282,1,0.733333333333333282],"xyz":[0.652201793130113283,0.856710760314833397,0.601151690755016177],"hpluv":[127.715012949237362,438.38038704048256,94.1715339943383],"hsluv":[127.715012949237362,99.999999999986926,94.1715339943383]},"#bbffcc":{"lch":[94.5013412228876888,46.0666753200389323,136.612331153023604],"luv":[94.5013412228876888,-33.4776907719138919,31.6446329670924698],"rgb":[0.733333333333333282,1,0.8],"xyz":[0.671494168573835,0.864427710492322254,0.702758201425286333],"hpluv":[136.612331153023604,391.513584290668746,94.5013412228876888],"hsluv":[136.612331153023604,99.9999999999865707,94.5013412228876888]},"#bbffdd":{"lch":[94.8668940681869515,38.5674836948257322,150.076437256866],"luv":[94.8668940681869515,-33.4261169986321818,19.2391658068743077],"rgb":[0.733333333333333282,1,0.866666666666666696],"xyz":[0.693012494124468637,0.873035040712575761,0.816088049325292331],"hpluv":[150.076437256866,352.109825744613431,94.8668940681869515],"hsluv":[150.076437256866,99.9999999999853628,94.8668940681869515]},"#bbffee":{"lch":[95.2686250900245568,33.9600576278843675,169.384373669661102],"luv":[95.2686250900245568,-33.3788361105619771,6.25610214058809699],"rgb":[0.733333333333333282,1,0.933333333333333348],"xyz":[0.716824674390643346,0.882559912819045778,0.941498865393815554],"hpluv":[169.384373669661102,337.406400875084444,95.2686250900245568],"hsluv":[169.384373669661102,99.9999999999845,95.2686250900245568]},"#bbffff":{"lch":[95.7068319095003,34.1048965933827191,192.177050630060478],"luv":[95.7068319095003,-33.3375521201936849,-7.19385781613020914],"rgb":[0.733333333333333282,1,1],"xyz":[0.742995722800474767,0.893028332182978501,1.0793330536855974],"hpluv":[192.177050630060478,374.679972152143307,95.7068319095003],"hsluv":[192.177050630060478,99.9999999999829186,95.7068319095003]},"#448800":{"lch":[50.4956227619448157,68.4221216779621244,118.715311426014551],"luv":[50.4956227619448157,-32.873947818662586,60.0074186224478723],"rgb":[0.266666666666666663,0.533333333333333326,0],"xyz":[0.111876166324659992,0.18836716674231338,0.0304633392138840171],"hpluv":[118.715311426014551,171.942062028892252,50.4956227619448157],"hsluv":[118.715311426014551,100.000000000002373,50.4956227619448157]},"#448811":{"lch":[50.543205868460916,66.6756511812714763,119.435612575278299],"luv":[50.543205868460916,-32.7674265649617098,58.0683925794095899],"rgb":[0.266666666666666663,0.533333333333333326,0.0666666666666666657],"xyz":[0.112887831824297111,0.188771832942168233,0.0357914441786396503],"hpluv":[119.435612575278299,167.395510973175305,50.543205868460916],"hsluv":[119.435612575278299,96.4702491056824272,50.543205868460916]},"#448822":{"lch":[50.6312327062127565,63.5295563644356918,120.846986072134357],"luv":[50.6312327062127565,-32.5745953261955492,54.5426463530750425],"rgb":[0.266666666666666663,0.533333333333333326,0.133333333333333331],"xyz":[0.11476318996277414,0.189521976197559061,0.0456683303746188429],"hpluv":[120.846986072134357,159.219643782083125,50.6312327062127565],"hsluv":[120.846986072134357,90.0662104861249,50.6312327062127565]},"#448833":{"lch":[50.775662969350762,58.6076690216783831,123.408487274566809],"luv":[50.775662969350762,-32.2696404838739213,48.9237076599487182],"rgb":[0.266666666666666663,0.533333333333333326,0.2],"xyz":[0.117850940695231873,0.19075707649054216,0.0619304842322299931],"hpluv":[123.408487274566809,146.466455796741883,50.775662969350762],"hsluv":[123.408487274566809,79.8990388198805732,50.775662969350762]},"#448844":{"lch":[50.9830910358637652,52.0733509329185,127.715012949239537],"luv":[50.9830910358637652,-31.855056956676961,41.1933152789351382],"rgb":[0.266666666666666663,0.533333333333333326,0.266666666666666663],"xyz":[0.122308935939450747,0.192540274588229754,0.0854092591851166316],"hpluv":[127.715012949239537,129.607069016793787,50.9830910358637652],"hsluv":[127.715012949239537,65.9930987522981241,50.9830910358637652]},"#448855":{"lch":[51.2585265038955384,44.4350179761376651,134.860489600104756],"luv":[51.2585265038955384,-31.3437036744621551,31.4967151066136601],"rgb":[0.266666666666666663,0.533333333333333326,0.333333333333333315],"xyz":[0.128271349978812732,0.194925240203974587,0.116811306459090533],"hpluv":[134.860489600104756,110.001490313735033,51.2585265038955384],"hsluv":[134.860489600104756,67.3517725464548676,51.2585265038955384]},"#448866":{"lch":[51.6056896491522537,36.7437212642011914,146.829747927558799],"luv":[51.6056896491522537,-30.7562765884277063,20.1035445321824895],"rgb":[0.266666666666666663,0.533333333333333326,0.4],"xyz":[0.135856345752320917,0.197959238513377878,0.156758950866234509],"hpluv":[146.829747927558799,90.3493178961374781,51.6056896491522537],"hsluv":[146.829747927558799,68.9308752248562797,51.6056896491522537]},"#448877":{"lch":[52.0271709342862039,31.0045548915217921,166.266503182991642],"luv":[52.0271709342862039,-30.1181499011556575,7.36067052332227245],"rgb":[0.266666666666666663,0.533333333333333326,0.466666666666666674],"xyz":[0.145170358772522823,0.201684843721458701,0.205812752772632418],"hpluv":[166.266503182991642,75.6196418937204555,52.0271709342862039],"hsluv":[166.266503182991642,70.6726995402934506,52.0271709342862039]},"#448888":{"lch":[52.5245390493459041,30.1341009641634088,192.177050630061],"luv":[52.5245390493459041,-29.456097564679137,-6.35628485662009179],"rgb":[0.266666666666666663,0.533333333333333326,0.533333333333333326],"xyz":[0.156310775874466679,0.206141010562236299,0.264485616176204585],"hpluv":[192.177050630061,72.8006598676027181,52.5245390493459041],"hsluv":[192.177050630061,72.5156967272479847,52.5245390493459041]},"#448899":{"lch":[53.0984315279962118,35.4547108695225,215.690916113393854],"luv":[53.0984315279962118,-28.7954664423823949,-20.6847198484066084],"rgb":[0.266666666666666663,0.533333333333333326,0.6],"xyz":[0.169367739983539378,0.211363796205865456,0.333252293817322465],"hpluv":[215.690916113393854,84.7289026839812,53.0984315279962118],"hsluv":[215.690916113393854,74.4011420122721319,53.0984315279962118]},"#4488aa":{"lch":[53.7486427971268625,45.1587425535104146,231.425064236121358],"luv":[53.7486427971268625,-28.1581765792043193,-35.3048030832715654],"rgb":[0.266666666666666663,0.533333333333333326,0.66666666666666663],"xyz":[0.184425432948791934,0.217386873391966551,0.412556143434321065],"hpluv":[231.425064236121358,106.613859301848393,53.7486427971268625],"hsluv":[231.425064236121358,76.2778611087785,53.7486427971268625]},"#4488bb":{"lch":[54.4742155909258514,57.0545505189943469,241.113543257937238],"luv":[54.4742155909258514,-27.5616516575745152,-49.9557513488787208],"rgb":[0.266666666666666663,0.533333333333333326,0.733333333333333282],"xyz":[0.201563025056128553,0.224241910234901309,0.502814128532962812],"hpluv":[241.113543257937238,132.904176722924205,54.4742155909258514],"hsluv":[241.113543257937238,78.1047803623200707,54.4742155909258514]},"#4488cc":{"lch":[55.273536978614473,69.875332030770025,247.25266296501286],"luv":[55.273536978614473,-27.0185618464256976,-64.4403549195783398],"rgb":[0.266666666666666663,0.533333333333333326,0.8],"xyz":[0.220855400499850307,0.231958860412390111,0.604420639203233],"hpluv":[247.25266296501286,160.415361718249983,55.273536978614473],"hsluv":[247.25266296501286,79.8515774594708603,55.273536978614473]},"#4488dd":{"lch":[56.1444377207636194,82.97891177410213,251.348812353132246],"luv":[56.1444377207636194,-26.53714651812043,-78.6211145551246631],"rgb":[0.266666666666666663,0.533333333333333326,0.866666666666666696],"xyz":[0.242373726050483851,0.240566190632643645,0.717750487103239],"hpluv":[251.348812353132246,187.542769341675751,56.1444377207636194],"hsluv":[251.348812353132246,81.4979766670821,56.1444377207636194]},"#4488ee":{"lch":[57.0842924141545609,96.0325607902411775,254.216059778839536],"luv":[57.0842924141545609,-26.1218678035424112,-92.4115834426919776],"rgb":[0.266666666666666663,0.533333333333333326,0.933333333333333348],"xyz":[0.266185906316658616,0.25009106273911369,0.843161303171762189],"hpluv":[254.216059778839536,213.472145126724115,57.0842924141545609],"hsluv":[254.216059778839536,85.8050168900109469,57.0842924141545609]},"#4488ff":{"lch":[58.090117466996233,108.861046277687535,256.304473865935392],"luv":[58.090117466996233,-25.7741898685227575,-105.765866579412872],"rgb":[0.266666666666666663,0.533333333333333326,1],"xyz":[0.29235695472649,0.260559482103046358,0.980995491463544],"hpluv":[256.304473865935392,237.798754650411269,58.090117466996233],"hsluv":[256.304473865935392,99.9999999999988631,58.090117466996233]},"#449900":{"lch":[56.0984423000037538,78.159424491283,120.852962610827774],"luv":[56.0984423000037538,-40.0830169860907191,67.0987882610508279],"rgb":[0.266666666666666663,0.6,0],"xyz":[0.137745766777127493,0.240106367647249075,0.0390865393647062687],"hpluv":[120.852962610827774,176.794958777624345,56.0984423000037538],"hsluv":[120.852962610827774,100.000000000002416,56.0984423000037538]},"#449911":{"lch":[56.1389235634784143,76.6482268999804859,121.431666795625887],"luv":[56.1389235634784143,-39.9706170908808929,65.4010738159938256],"rgb":[0.266666666666666663,0.6,0.0666666666666666657],"xyz":[0.138757432276764625,0.240511033847103928,0.0444146443294619],"hpluv":[121.431666795625887,173.251641588156104,56.1389235634784143],"hsluv":[121.431666795625887,97.2234127009916,56.1389235634784143]},"#449922":{"lch":[56.21384509356389,73.9109075723002746,122.549403254372123],"luv":[56.21384509356389,-39.7660359546788555,62.3015621201611793],"rgb":[0.266666666666666663,0.6,0.133333333333333331],"xyz":[0.140632790415241626,0.241261177102494756,0.0542915305254411],"hpluv":[122.549403254372123,166.841685682494415,56.21384509356389],"hsluv":[122.549403254372123,92.1627352690631,56.21384509356389]},"#449933":{"lch":[56.3368647095541064,69.5846668696270143,124.526315047954697],"luv":[56.3368647095541064,-39.4395235157555177,57.3284383897480296],"rgb":[0.266666666666666663,0.6,0.2],"xyz":[0.143720541147699388,0.242496277395477855,0.0705536843830522342],"hpluv":[124.526315047954697,156.73291295139785,56.3368647095541064],"hsluv":[124.526315047954697,84.0665441712718859,56.3368647095541064]},"#449944":{"lch":[56.5137417426914368,63.7361451042890153,127.715012949239735],"luv":[56.5137417426914368,-38.9895886498958575,50.4193233757345922],"rgb":[0.266666666666666663,0.6,0.266666666666666663],"xyz":[0.148178536391918247,0.244279475493165449,0.0940324593359388727],"hpluv":[127.715012949239735,143.110354253444,56.5137417426914368],"hsluv":[127.715012949239735,72.8686777069245,56.5137417426914368]},"#449955":{"lch":[56.7489681974646629,56.6620380577650309,132.698160989074609],"luv":[56.7489681974646629,-38.4245724364924328,41.642992086692793],"rgb":[0.266666666666666663,0.6,0.333333333333333315],"xyz":[0.154140950431280233,0.246664441108910282,0.125434506609912788],"hpluv":[132.698160989074609,126.69910537289924,56.7489681974646629],"hsluv":[132.698160989074609,73.7405349232989664,56.7489681974646629]},"#449966":{"lch":[57.0460268500437166,48.9673458777373511,140.456743036678859],"luv":[57.0460268500437166,-37.7608817984154683,31.1755796757022061],"rgb":[0.266666666666666663,0.6,0.4],"xyz":[0.161725946204788418,0.249698439418313572,0.16538215101705675],"hpluv":[140.456743036678859,108.923224533285605,57.0460268500437166],"hsluv":[140.456743036678859,74.7718537787272766,57.0460268500437166]},"#449977":{"lch":[57.4075272841014481,41.7361359201695734,152.500832231763781],"luv":[57.4075272841014481,-37.0206846158697,19.2710651526888199],"rgb":[0.266666666666666663,0.6,0.466666666666666674],"xyz":[0.171039959224990323,0.253424044626394396,0.214435952923454659],"hpluv":[152.500832231763781,92.2534727828265915,57.4075272841014481],"hsluv":[152.500832231763781,75.9325502249844817,57.4075272841014481]},"#449988":{"lch":[57.8352917232380861,36.7607331244742284,170.245603374965924],"luv":[57.8352917232380861,-36.2292854489646174,6.22819200942437323],"rgb":[0.266666666666666663,0.6,0.533333333333333326],"xyz":[0.182180376326934179,0.257880211467172,0.273108816327026827],"hpluv":[170.245603374965924,80.6548633994409414,57.8352917232380861],"hsluv":[170.245603374965924,77.1878957355486222,57.8352917232380861]},"#449999":{"lch":[58.3304201548299517,36.2276744984656105,192.17705063006116],"luv":[58.3304201548299517,-35.4125684996312486,-7.64162232943372643],"rgb":[0.266666666666666663,0.6,0.6],"xyz":[0.195237340436006879,0.263102997110801151,0.341875493968144761],"hpluv":[192.17705063006116,78.8106081595615251,58.3304201548299517],"hsluv":[192.17705063006116,78.5021203184449803,58.3304201548299517]},"#4499aa":{"lch":[58.8933484328044585,41.0197173156303379,212.502133513081617],"luv":[58.8933484328044585,-34.5948579745346478,-22.041166266239177],"rgb":[0.266666666666666663,0.6,0.66666666666666663],"xyz":[0.210295033401259435,0.269126074296902273,0.421179343585143307],"hpluv":[212.502133513081617,88.3823902117899536,58.8933484328044585],"hsluv":[212.502133513081617,79.8414276244135692,58.8933484328044585]},"#4499bb":{"lch":[59.523905851944221,49.8962585520308082,227.36281958486083],"luv":[59.523905851944221,-33.7974051546762055,-36.7065664738854878],"rgb":[0.266666666666666663,0.6,0.733333333333333282],"xyz":[0.227432625508596054,0.275981111139837032,0.511437328683785108],"hpluv":[227.36281958486083,106.36919851968392,59.523905851944221],"hsluv":[227.36281958486083,81.1761313086710885,59.523905851944221]},"#4499cc":{"lch":[60.2213749252976385,61.1171242450200438,237.278146949597897],"luv":[60.2213749252976385,-33.0375484580145766,-51.4181219791773],"rgb":[0.266666666666666663,0.6,0.8],"xyz":[0.246725000952317808,0.283698061317325834,0.613043839354055264],"hpluv":[237.278146949597897,128.780934100622716,60.2213749252976385],"hsluv":[237.278146949597897,82.4818489079811741,60.2213749252976385]},"#4499dd":{"lch":[60.9845539619455508,73.4958381074912,243.904542785491344],"luv":[60.9845539619455508,-32.3284648877968195,-66.0038527445257728],"rgb":[0.266666666666666663,0.6,0.866666666666666696],"xyz":[0.268243326502951351,0.29230539153757934,0.726373687254061262],"hpluv":[243.904542785491344,152.926314240713424,60.9845539619455508],"hsluv":[243.904542785491344,83.7398679369964469,60.9845539619455508]},"#4499ee":{"lch":[61.8118218103912653,86.3580647779335351,248.479300934605874],"luv":[61.8118218103912653,-31.6793620640961606,-80.3376211460214193],"rgb":[0.266666666666666663,0.6,0.933333333333333348],"xyz":[0.292055506769126061,0.301830263644049357,0.851784503322584485],"hpluv":[248.479300934605874,177.284466220706,61.8118218103912653],"hsluv":[248.479300934605874,84.9368954206658344,61.8118218103912653]},"#4499ff":{"lch":[62.7012034705467585,99.3269113509348,251.755860244601337],"luv":[62.7012034705467585,-31.0959452870692914,-94.3338619225358173],"rgb":[0.266666666666666663,0.6,1],"xyz":[0.318226555178957482,0.312298683007982081,0.98961869161436633],"hpluv":[251.755860244601337,201.015886170810859,62.7012034705467585],"hsluv":[251.755860244601337,99.9999999999986073,62.7012034705467585]},"#330000":{"lch":[6.35863201887414942,21.3842798011123882,12.1770506300617836],"luv":[6.35863201887414942,20.9031433498234946,4.51065635013277699],"rgb":[0.2,0,0],"xyz":[0.0136521011456799905,0.00703936465324139522,0.000639942241203736136],"hpluv":[12.1770506300617836,426.746789183125031,6.35863201887414942],"hsluv":[12.1770506300617836,100.000000000002217,6.35863201887414942]},"#330011":{"lch":[6.72416549840036915,18.2596394021459751,358.956333183931122],"luv":[6.72416549840036915,18.2566101970553,-0.332588648601129133],"rgb":[0.2,0,0.0666666666666666657],"xyz":[0.0146637666453171122,0.00744403085309625,0.00596804720595936738],"hpluv":[358.956333183931122,344.582429927088697,6.72416549840036915],"hsluv":[358.956333183931122,99.9999999999970868,6.72416549840036915]},"#330022":{"lch":[7.4017671226143058,16.6083885778583671,334.642609555635659],"luv":[7.4017671226143058,15.0082373074967617,-7.11276205668364714],"rgb":[0.2,0,0.133333333333333331],"xyz":[0.0165391247837941326,0.00819417410848706854,0.0158449334019385643],"hpluv":[334.642609555635659,284.728805881674077,7.4017671226143058],"hsluv":[334.642609555635659,99.999999999998,7.4017671226143058]},"#330033":{"lch":[8.50665746950019,19.3767863388894384,307.715012949243601],"luv":[8.50665746950019,11.8534455994177517,-15.3282639670843448],"rgb":[0.2,0,0.2],"xyz":[0.0196268755162518696,0.00942927440147018139,0.0321070872595497075],"hpluv":[307.715012949243601,289.042783730483279,8.50665746950019],"hsluv":[307.715012949243601,99.9999999999987921,8.50665746950019]},"#330044":{"lch":[9.96321399083228343,25.9151774110163871,290.632214162589],"luv":[9.96321399083228343,9.13167627644372,-24.2530185467023678],"rgb":[0.2,0,0.266666666666666663],"xyz":[0.0240848707604707502,0.0112124724991577579,0.0555858622124363461],"hpluv":[290.632214162589,330.060881015257678,9.96321399083228343],"hsluv":[290.632214162589,99.9999999999994,9.96321399083228343]},"#330055":{"lch":[11.6870713271151807,34.2775786608295405,281.502617436257196],"luv":[11.6870713271151807,6.83538450996046,-33.589133919325],"rgb":[0.2,0,0.333333333333333315],"xyz":[0.0300472847998327422,0.0135974381149025891,0.0869879094864102614],"hpluv":[281.502617436257196,372.172061509357604,11.6870713271151807],"hsluv":[281.502617436257196,99.9999999999999289,11.6870713271151807]},"#330066":{"lch":[13.6097387714237676,43.4818398400869768,276.434806151814087],"luv":[13.6097387714237676,4.87312317733106592,-43.2079051375733059],"rgb":[0.2,0,0.4],"xyz":[0.0376322805733409205,0.0166314364243059024,0.126935553893554209],"hpluv":[276.434806151814087,405.412793254212261,13.6097387714237676],"hsluv":[276.434806151814087,100.00000000000027,13.6097387714237676]},"#330077":{"lch":[15.6735112457106673,53.108485659557914,273.408523183706109],"luv":[15.6735112457106673,3.15755804167271537,-53.0145364618510655],"rgb":[0.2,0,0.466666666666666674],"xyz":[0.046946293593542833,0.0203570416323867187,0.175989355799952119],"hpluv":[273.408523183706109,429.968801903614121,15.6735112457106673],"hsluv":[273.408523183706109,100.000000000000313,15.6735112457106673]},"#330088":{"lch":[17.8339183845063687,62.9511834901283365,271.47985970994057],"luv":[17.8339183845063687,1.62574911173865799,-62.9301870538574448],"rgb":[0.2,0,0.533333333333333326],"xyz":[0.058086710695486661,0.0248132084731643096,0.234662219203524286],"hpluv":[271.47985970994057,447.91587095992594,17.8339183845063687],"hsluv":[271.47985970994057,100.000000000000441,17.8339183845063687]},"#330099":{"lch":[20.0583065104412341,72.8932825114363,270.184356583024851],"luv":[20.0583065104412341,0.234543162084408924,-72.8929051746271313],"rgb":[0.2,0,0.6],"xyz":[0.0711436748045593814,0.0300359941167934706,0.303428896844642193],"hpluv":[270.184356583024851,461.139761646516433,20.0583065104412341],"hsluv":[270.184356583024851,100.000000000000625,20.0583065104412341]},"#3300aa":{"lch":[22.3232943619689834,82.8637729479105474,269.276671227287579],"luv":[22.3232943619689834,-1.04608331699459467,-82.8571697371855578],"rgb":[0.2,0,0.66666666666666663],"xyz":[0.0862013677698119235,0.0360590713028945756,0.382732746461640794],"hpluv":[269.276671227287579,471.026936966419044,22.3232943619689834],"hsluv":[269.276671227287579,100.000000000000554,22.3232943619689834]},"#3300bb":{"lch":[24.6123405885396807,92.8181896970849,268.61855571411644],"luv":[24.6123405885396807,-2.23769945956788,-92.7912120826788538],"rgb":[0.2,0,0.733333333333333282],"xyz":[0.10333895987714857,0.0429141081458293341,0.47299073156028254],"hpluv":[268.61855571411644,478.541387025058441,24.6123405885396807],"hsluv":[268.61855571411644,100.000000000000625,24.6123405885396807]},"#3300cc":{"lch":[26.9138017967000778,102.728605647013808,268.127719933063759],"luv":[26.9138017967000778,-3.35631165306985224,-102.673762910819349],"rgb":[0.2,0,0.8],"xyz":[0.122631335320870311,0.0506310583233181358,0.574597242230552752],"hpluv":[268.127719933063759,484.345947247320225,26.9138017967000778],"hsluv":[268.127719933063759,100.000000000000881,26.9138017967000778]},"#3300dd":{"lch":[29.2194977691074271,112.577730384171886,267.752877980499],"luv":[29.2194977691074271,-4.41413050162269105,-112.49115889867052],"rgb":[0.2,0,0.866666666666666696],"xyz":[0.144149660871503854,0.0592383885435716698,0.687927090130558749],"hpluv":[267.752877980499,488.89890937186334,29.2194977691074271],"hsluv":[267.752877980499,100.000000000000753,29.2194977691074271]},"#3300ee":{"lch":[31.5236887929336334,122.355215968494591,267.460804758990776],"luv":[31.5236887929336334,-5.42068013199621,-122.235081304851008],"rgb":[0.2,0,0.933333333333333348],"xyz":[0.167961841137678591,0.0687632606500417,0.813337906199082],"hpluv":[267.460804758990776,492.52103452607281,31.5236887929336334],"hsluv":[267.460804758990776,100.000000000000824,31.5236887929336334]},"#3300ff":{"lch":[33.8223579343154,132.055276159319874,267.229255072945307],"luv":[33.8223579343154,-6.38352242786170443,-131.900896899631505],"rgb":[0.2,0,1],"xyz":[0.194132889547509985,0.0792316800139744,0.951172094490863818],"hpluv":[267.229255072945307,495.440155164142311,33.8223579343154],"hsluv":[267.229255072945307,100.000000000000881,33.8223579343154]},"#331100":{"lch":[9.83576796362177319,19.9321083570360571,25.9770166386959609],"luv":[9.83576796362177319,17.918363864654,8.73047421223431108],"rgb":[0.2,0.0666666666666666657,0],"xyz":[0.0156565014066084,0.0110481651750982679,0.00130807566151318702],"hpluv":[25.9770166386959609,257.148675223584291,9.83576796362177319],"hsluv":[25.9770166386959609,100.000000000002302,9.83576796362177319]},"#331111":{"lch":[10.1474261289244687,16.4836545456174051,12.1770506300618813],"luv":[10.1474261289244687,16.1127799065782149,3.4769513746129066],"rgb":[0.2,0.0666666666666666657,0.0666666666666666657],"xyz":[0.0166681669062455212,0.0114528313749531225,0.00663618062626881826],"hpluv":[12.1770506300618813,206.127972902374523,10.1474261289244687],"hsluv":[12.1770506300618813,48.3021731216650707,10.1474261289244687]},"#331122":{"lch":[10.7062693823806221,14.2435433110065777,342.375847990242676],"luv":[10.7062693823806221,13.5749958919169824,-4.31254131423193],"rgb":[0.2,0.0666666666666666657,0.133333333333333331],"xyz":[0.0185435250447225398,0.0122029746303439404,0.0165130668222480161],"hpluv":[342.375847990242676,168.818174775843545,10.7062693823806221],"hsluv":[342.375847990242676,57.1044970617697913,10.7062693823806221]},"#331133":{"lch":[11.5784810016780177,17.5377888786733784,307.715012949244169],"luv":[11.5784810016780177,10.7284677021084427,-13.8735006223280077],"rgb":[0.2,0.0666666666666666657,0.2],"xyz":[0.0216312757771802804,0.0134380749233270532,0.0327752206798591628],"hpluv":[307.715012949244169,192.204068690519591,11.5784810016780177],"hsluv":[307.715012949244169,66.4967539441281,11.5784810016780177]},"#331144":{"lch":[12.7480449023252049,25.2894553184220108,288.641508688419037],"luv":[12.7480449023252049,8.08366941467993172,-23.9626968243691536],"rgb":[0.2,0.0666666666666666657,0.266666666666666663],"xyz":[0.0260892710213991574,0.0152212730210146297,0.0562539956327458],"hpluv":[288.641508688419037,251.73014018207067,12.7480449023252049],"hsluv":[288.641508688419037,74.5439781366083309,12.7480449023252049]},"#331155":{"lch":[14.1772863520069095,34.570642435857458,279.659572498507771],"luv":[14.1772863520069095,5.80074057604611415,-34.0805036230000695],"rgb":[0.2,0.0666666666666666657,0.333333333333333315],"xyz":[0.0320516850607611564,0.0176062386367594609,0.0876560429067197],"hpluv":[279.659572498507771,309.423764447612427,14.1772863520069095],"hsluv":[279.659572498507771,80.733364837348816,14.1772863520069095]},"#331166":{"lch":[15.8197098676790517,44.2946391552180785,274.993838621827194],"luv":[15.8197098676790517,3.85578699555013271,-44.1264995726595686],"rgb":[0.2,0.0666666666666666657,0.4],"xyz":[0.0396366808342693278,0.0206402369461627724,0.127603687313863678],"hpluv":[274.993838621827194,355.297359803625511,15.8197098676790517],"hsluv":[274.993838621827194,85.2848648605675095,15.8197098676790517]},"#331177":{"lch":[17.6293493428787755,54.1580116435351115,272.303494486185969],"luv":[17.6293493428787755,2.17675853502087469,-54.1142490242773135],"rgb":[0.2,0.0666666666666666657,0.466666666666666674],"xyz":[0.0489506938544712472,0.0243658421542435888,0.176657489220261588],"hpluv":[272.303494486185969,389.821469213037517,17.6293493428787755],"hsluv":[272.303494486185969,88.5936990192462588,17.6293493428787755]},"#331188":{"lch":[19.5658128626021437,64.0679547566568,270.623065923527406],"luv":[19.5658128626021437,0.696696562564985133,-64.064166587888522],"rgb":[0.2,0.0666666666666666657,0.533333333333333326],"xyz":[0.0600911109564150753,0.0288220089950211832,0.235330352623833755],"hpluv":[270.623065923527406,415.51077593183,19.5658128626021437],"hsluv":[270.623065923527406,91.0112456871911348,19.5658128626021437]},"#331199":{"lch":[21.5959931816331263,73.9854871429769645,269.508319412823],"luv":[21.5959931816331263,-0.634894604710849,-73.9827629696574576],"rgb":[0.2,0.0666666666666666657,0.6],"xyz":[0.0731480750654877887,0.0340447946386503442,0.304097030264951662],"hpluv":[269.508319412823,434.723064450461493,21.5959931816331263],"hsluv":[269.508319412823,92.7998436287799,21.5959931816331263]},"#3311aa":{"lch":[23.6938673935249824,83.8866252842298792,268.733700318675346],"luv":[23.6938673935249824,-1.85383566080119344,-83.8661385478044821],"rgb":[0.2,0.0666666666666666657,0.66666666666666663],"xyz":[0.0882057680307403308,0.0400678718247514457,0.383400879881950263],"hpluv":[268.733700318675346,449.258286440651602,23.6938673935249824],"hsluv":[268.733700318675346,94.1436997610677,23.6938673935249824]},"#3311bb":{"lch":[25.8394818705352094,93.7535423490500079,268.175268957576],"luv":[25.8394818705352094,-2.98531729009247,-93.7060007879569525],"rgb":[0.2,0.0666666666666666657,0.733333333333333282],"xyz":[0.105343360138076977,0.0469229086676862042,0.473658864980592],"hpluv":[268.175268957576,460.408371224539451,25.8394818705352094],"hsluv":[268.175268957576,95.1697562295976525,25.8394818705352094]},"#3311cc":{"lch":[28.017750605254669,103.572636018550753,267.760492630427507],"luv":[28.017750605254669,-4.04728980779695124,-103.493528189171812],"rgb":[0.2,0.0666666666666666657,0.8],"xyz":[0.124635735581798718,0.0546398588451750059,0.57526537565086211],"hpluv":[267.760492630427507,469.084508820223505,28.017750605254669],"hsluv":[267.760492630427507,95.9655004656031423,28.017750605254669]},"#3311dd":{"lch":[30.2173526676312889,113.33386550323506,267.444704079349265],"luv":[30.2173526676312889,-5.05282540459801588,-113.221173043455181],"rgb":[0.2,0.0666666666666666657,0.866666666666666696],"xyz":[0.146154061132432261,0.0632471890654285329,0.688595223550868107],"hpluv":[267.444704079349265,475.929488946899937,30.2173526676312889],"hsluv":[267.444704079349265,96.591763702558211,30.2173526676312889]},"#3311ee":{"lch":[32.429822932533412,123.030197380053124,267.199217974228532],"luv":[32.429822932533412,-6.01167404447580456,-122.883234180085807],"rgb":[0.2,0.0666666666666666657,0.933333333333333348],"xyz":[0.169966241398607,0.0727720611718985777,0.814006039619391331],"hpluv":[267.199217974228532,481.400425551936735,32.429822932533412],"hsluv":[267.199217974228532,97.0913881744298095,32.429822932533412]},"#3311ff":{"lch":[34.6488414224811834,132.657034918193688,267.004954598207064],"luv":[34.6488414224811834,-6.93127707868383425,-132.475832933200962],"rgb":[0.2,0.0666666666666666657,1],"xyz":[0.196137289808438392,0.0832404805358312738,0.951840227911173176],"hpluv":[267.004954598207064,485.826158631145177,34.6488414224811834],"hsluv":[267.004954598207064,99.999999999999531,34.6488414224811834]},"#88aa00":{"lch":[64.9493872277699467,75.8454165204624502,102.522158340464031],"luv":[64.9493872277699467,-16.4445883060260414,74.0412231301438624],"rgb":[0.533333333333333326,0.66666666666666663,0],"xyz":[0.245272120749672057,0.339833923051985953,0.0526729261635559762],"hpluv":[102.522158340464031,148.181371186867864,64.9493872277699467],"hsluv":[102.522158340464031,100.000000000002217,64.9493872277699467]},"#88aa11":{"lch":[64.9815053546997206,74.514883993558044,102.764001416735823],"luv":[64.9815053546997206,-16.4630035423646639,72.6734989589568414],"rgb":[0.533333333333333326,0.66666666666666663,0.0666666666666666657],"xyz":[0.246283786249309189,0.340238589251840806,0.0580010311283116059],"hpluv":[102.764001416735823,145.509915409181133,64.9815053546997206],"hsluv":[102.764001416735823,98.0498740713468635,64.9815053546997206]},"#88aa22":{"lch":[65.040976504183746,72.0784265262596762,103.230826782560158],"luv":[65.040976504183746,-16.4969246852166869,70.1651697527462801],"rgb":[0.533333333333333326,0.66666666666666663,0.133333333333333331],"xyz":[0.24815914438778619,0.340988732507231607,0.0678779173242908],"hpluv":[103.230826782560158,140.623392494363173,65.040976504183746],"hsluv":[103.230826782560158,94.4776072436414296,65.040976504183746]},"#88aa33":{"lch":[65.138705174337673,68.1506383708936596,104.056367163177015],"luv":[65.138705174337673,-16.5521781693680836,66.1100212389152],"rgb":[0.533333333333333326,0.66666666666666663,0.2],"xyz":[0.251246895120243952,0.342223832800214733,0.0841400711819019487],"hpluv":[104.056367163177015,132.760883332008575,65.138705174337673],"hsluv":[104.056367163177015,88.7139905695652544,65.138705174337673]},"#88aa44":{"lch":[65.2793887406011,62.6606585185119442,105.391240881396499],"luv":[65.2793887406011,-16.6306856640551395,60.4133960336360687],"rgb":[0.533333333333333326,0.66666666666666663,0.266666666666666663],"xyz":[0.255704890364462811,0.344007030897902299,0.107618846134788587],"hpluv":[105.391240881396499,121.803060858356815,65.2793887406011],"hsluv":[105.391240881396499,80.6421361820775,65.2793887406011]},"#88aa55":{"lch":[65.4667902277309821,55.6467221212728731,107.50018297907917],"luv":[65.4667902277309821,-16.7334615527701551,53.0711687011330397],"rgb":[0.533333333333333326,0.66666666666666663,0.333333333333333315],"xyz":[0.261667304403824796,0.34639199651364716,0.139020893408762503],"hpluv":[107.50018297907917,107.859366541167589,65.4667902277309821],"hsluv":[107.50018297907917,70.2829236383929157,65.4667902277309821]},"#88aa66":{"lch":[65.7039511656785606,47.2674293485405386,110.898270106627507],"luv":[65.7039511656785606,-16.8607549547505542,44.1579530727492582],"rgb":[0.533333333333333326,0.66666666666666663,0.4],"xyz":[0.269252300177333,0.349425994823050479,0.178968537815906464],"hpluv":[110.898270106627507,91.2871867148882075,65.7039511656785606],"hsluv":[110.898270106627507,57.7776943224234358,65.7039511656785606]},"#88aa77":{"lch":[65.993303036365262,37.8517489546572605,116.707912668539493],"luv":[65.993303036365262,-17.0121797895697142,33.8133204186420073],"rgb":[0.533333333333333326,0.66666666666666663,0.466666666666666674],"xyz":[0.278566313197534887,0.353151600031131274,0.228022339722304374],"hpluv":[116.707912668539493,72.782239014817236,65.993303036365262],"hsluv":[116.707912668539493,43.3670204896631262,65.993303036365262]},"#88aa88":{"lch":[66.3367341259492,28.0952907790087707,127.715012949235486],"luv":[66.3367341259492,-17.1868541575625393,22.2251526006848934],"rgb":[0.533333333333333326,0.66666666666666663,0.533333333333333326],"xyz":[0.289706730299478743,0.357607766871908872,0.286695203125876541],"hpluv":[127.715012949235486,53.7426121206727316,66.3367341259492],"hsluv":[127.715012949235486,27.3645684281827677,66.3367341259492]},"#88aa99":{"lch":[66.7356352778598705,19.8636867466165334,151.061783220075199],"luv":[66.7356352778598705,-17.3835461247401071,9.6113669837720046],"rgb":[0.533333333333333326,0.66666666666666663,0.6],"xyz":[0.302763694408551443,0.36283055251553803,0.35546188076699442],"hpluv":[151.061783220075199,37.7695130125235536,66.7356352778598705],"hsluv":[151.061783220075199,30.5390729913306274,66.7356352778598705]},"#88aaaa":{"lch":[67.1909358184889811,18.0059400546344968,192.177050630060364],"luv":[67.1909358184889811,-17.6008146924254447,-3.79805205519771905],"rgb":[0.533333333333333326,0.66666666666666663,0.66666666666666663],"xyz":[0.317821387373804,0.368853629701639152,0.434765730383993],"hpluv":[192.177050630060364,34.0051297749840913,67.1909358184889811],"hsluv":[192.177050630060364,33.872023720911649,67.1909358184889811]},"#88aabb":{"lch":[67.7031355134684674,25.1829477340388976,224.903065382651],"luv":[67.7031355134684674,-17.8371340556046292,-17.7768812016541453],"rgb":[0.533333333333333326,0.66666666666666663,0.733333333333333282],"xyz":[0.334958979481140617,0.37570866654457391,0.525023715482634823],"hpluv":[224.903065382651,47.1994684953027459,67.7031355134684674],"hsluv":[224.903065382651,37.296330310174362,67.7031355134684674]},"#88aacc":{"lch":[68.2723356589922901,36.8600300209604939,240.606662797769701],"luv":[68.2723356589922901,-18.0909926283411373,-32.1150712106857341],"rgb":[0.533333333333333326,0.66666666666666663,0.8],"xyz":[0.354251354924862372,0.383425616722062712,0.626630226152905],"hpluv":[240.606662797769701,68.5094136262693,68.2723356589922901],"hsluv":[240.606662797769701,40.7502319604911136,68.2723356589922901]},"#88aadd":{"lch":[68.8982708583434,50.1120766914241216,248.506358436024811],"luv":[68.8982708583434,-18.3609632112572676,-46.6271944285953168],"rgb":[0.533333333333333326,0.66666666666666663,0.866666666666666696],"xyz":[0.375769680475495915,0.392032946942316218,0.739960074052911],"hpluv":[248.506358436024811,92.2939864083951278,68.8982708583434],"hsluv":[248.506358436024811,59.5109742880650217,68.8982708583434]},"#88aaee":{"lch":[69.5803420919898,63.9362726255054454,253.044423586561948],"luv":[69.5803420919898,-18.6457452648911577,-61.1570367231747554],"rgb":[0.533333333333333326,0.66666666666666663,0.933333333333333348],"xyz":[0.39958186074167068,0.401557819048786235,0.8653708901214342],"hpluv":[253.044423586561948,116.600410917186778,69.5803420919898],"hsluv":[253.044423586561948,79.2673662112875093,69.5803420919898]},"#88aaff":{"lch":[70.3176511000829549,77.916964899147743,255.928474825822],"luv":[70.3176511000829549,-18.9441834980668631,-75.578907974954177],"rgb":[0.533333333333333326,0.66666666666666663,1],"xyz":[0.425752909151502046,0.412026238412718959,1.00320507841321604],"hpluv":[255.928474825822,140.607018245256825,70.3176511000829549],"hsluv":[255.928474825822,99.9999999999979394,70.3176511000829549]},"#332200":{"lch":[14.6681357538016819,18.4720509904151484,54.0318728094203635],"luv":[14.6681357538016819,10.8492842291989344,14.9502407842332925],"rgb":[0.2,0.133333333333333331,0],"xyz":[0.0193721251413763347,0.0184794126446342424,0.00254661690643579732],"hpluv":[54.0318728094203635,159.801011716648361,14.6681357538016819],"hsluv":[54.0318728094203635,100.000000000002359,14.6681357538016819]},"#332211":{"lch":[14.8903804788128475,14.003495227987683,44.8263438888978243],"luv":[14.8903804788128475,9.93193249146711,9.87190941941900668],"rgb":[0.2,0.133333333333333331,0.0666666666666666657],"xyz":[0.0203837906410134564,0.0188840788444890953,0.00787472187119143],"hpluv":[44.8263438888978243,119.33558852926538,14.8903804788128475],"hsluv":[44.8263438888978243,67.0844803779226595,14.8903804788128475]},"#332222":{"lch":[15.2941064614028619,8.70381909014442101,12.1770506300622809],"luv":[15.2941064614028619,8.50798716741232397,1.83592513820952408],"rgb":[0.2,0.133333333333333331,0.133333333333333331],"xyz":[0.0222591487794904751,0.0196342220998799166,0.0177516080671706253],"hpluv":[12.1770506300622809,72.2146104972558476,15.2941064614028619],"hsluv":[12.1770506300622809,16.9221215783466867,15.2941064614028619]},"#332233":{"lch":[15.9369990430381634,10.9638268591401484,307.7150129492465],"luv":[15.9369990430381634,6.70694938589646661,-8.67308072902737],"rgb":[0.2,0.133333333333333331,0.2],"xyz":[0.0253468995119482156,0.0208693223928630295,0.0340137619247817685],"hpluv":[307.7150129492465,87.2961214462547,15.9369990430381634],"hsluv":[307.7150129492465,30.2017993044426838,15.9369990430381634]},"#332244":{"lch":[16.8218835175385664,20.7382675483863608,283.478697838556343],"luv":[16.8218835175385664,4.83375477674434428,-20.1670661145035197],"rgb":[0.2,0.133333333333333331,0.266666666666666663],"xyz":[0.0298048947561670927,0.0226525204905506025,0.057492536877668407],"hpluv":[283.478697838556343,156.436171283708973,16.8218835175385664],"hsluv":[283.478697838556343,43.2894908809756558,16.8218835175385664]},"#332255":{"lch":[17.9355503164319856,31.8518286021958055,275.537546938931484],"luv":[17.9355503164319856,3.0736387963894658,-31.7031816992078603],"rgb":[0.2,0.133333333333333331,0.333333333333333315],"xyz":[0.0357673087955290847,0.0250374861062954354,0.0888945841516423224],"hpluv":[275.537546938931484,225.350740722061715,17.9355503164319856],"hsluv":[275.537546938931484,54.6600619512456092,17.9355503164319856]},"#332266":{"lch":[19.2543827660255502,42.9346801988288,271.988815040461645],"luv":[19.2543827660255502,1.49002234071320738,-42.9088172430781114],"rgb":[0.2,0.133333333333333331,0.4],"xyz":[0.043352304569037263,0.028071484415698747,0.12884222855878627],"hpluv":[271.988815040461645,282.955381276792764,19.2543827660255502],"hsluv":[271.988815040461645,63.8744485302889515,19.2543827660255502]},"#332277":{"lch":[20.7496984269819,53.7295137018198687,270.083996688769219],"luv":[20.7496984269819,0.0787684479011579453,-53.7294559637975908],"rgb":[0.2,0.133333333333333331,0.466666666666666674],"xyz":[0.0526663175892391755,0.0317970896237795633,0.17789603046518418],"hpluv":[270.083996688769219,328.579487011522247,20.7496984269819],"hsluv":[270.083996688769219,71.0892762838988546,20.7496984269819]},"#332288":{"lch":[22.3919640579388926,64.2366699579867,268.940142222195846],"luv":[22.3919640579388926,-1.18818265954209568,-64.2256801385464087],"rgb":[0.2,0.133333333333333331,0.533333333333333326],"xyz":[0.063806734691183,0.0362532564645571542,0.236568893868756347],"hpluv":[268.940142222195846,364.024117005976393,22.3919640579388926],"hsluv":[268.940142222195846,76.6637840765232426,22.3919640579388926]},"#332299":{"lch":[24.1535324867621668,74.5121507797046689,268.199285594367666],"luv":[24.1535324867621668,-2.34141182983712737,-74.4753543426316469],"rgb":[0.2,0.133333333333333331,0.6],"xyz":[0.076863698800255717,0.0414760421081863187,0.305335571509874282],"hpluv":[268.199285594367666,391.458538227207669,24.1535324867621668],"hsluv":[268.199285594367666,80.9654036049547301,24.1535324867621668]},"#3322aa":{"lch":[26.0100477302332607,84.6098540849434926,267.692459882235937],"luv":[26.0100477302332607,-3.40667075751279169,-84.541244387726735],"rgb":[0.2,0.133333333333333331,0.66666666666666663],"xyz":[0.0919213917655082591,0.0474991192942874202,0.384639421126872882],"hpluv":[267.692459882235937,412.780453116303818,26.0100477302332607],"hsluv":[267.692459882235937,84.3023702715129,26.0100477302332607]},"#3322bb":{"lch":[27.9408960039881222,94.5690254119923281,267.330995248517297],"luv":[27.9408960039881222,-4.40370835341661504,-94.4664380619492761],"rgb":[0.2,0.133333333333333331,0.733333333333333282],"xyz":[0.109058983872844906,0.0543541561372221788,0.474897406225514629],"hpluv":[267.330995248517297,429.484911570580266,27.9408960039881222],"hsluv":[267.330995248517297,86.9134730370406743,27.9408960039881222]},"#3322cc":{"lch":[29.9290875828623939,104.415306333233346,267.064581258752071],"luv":[29.9290875828623939,-5.34714045276937444,-104.278302084572317],"rgb":[0.2,0.133333333333333331,0.8],"xyz":[0.12835135931656666,0.0620711063147109804,0.576503916895784729],"hpluv":[267.064581258752071,442.700503900749,29.9290875828623939],"hsluv":[267.064581258752071,88.9774620844202389,29.9290875828623939]},"#3322dd":{"lch":[31.9608605817263296,114.164622106097937,266.862904678446171],"luv":[31.9608605817263296,-6.2476920173399213,-113.993540541052639],"rgb":[0.2,0.133333333333333331,0.866666666666666696],"xyz":[0.149869684867200204,0.0706784365349645144,0.689833764795790727],"hpluv":[266.862904678446171,453.265229710644405,31.9608605817263296],"hsluv":[266.862904678446171,90.6264253960307826,31.9608605817263296]},"#3322ee":{"lch":[34.0251904593745635,123.826689560879501,266.706806867468629],"luv":[34.0251904593745635,-7.11327635006579229,-123.622208147136817],"rgb":[0.2,0.133333333333333331,0.933333333333333348],"xyz":[0.173681865133374941,0.0802033086414345453,0.815244580864314],"hpluv":[266.706806867468629,461.799038215803307,34.0251904593745635],"hsluv":[266.706806867468629,93.6430785136650741,34.0251904593745635]},"#3322ff":{"lch":[36.1133053940478774,133.407509730883817,266.583697157343806],"luv":[36.1133053940478774,-7.94980801741223875,-133.170432923686747],"rgb":[0.2,0.133333333333333331,1],"xyz":[0.199852913543206334,0.0906717280053672414,0.953078769156095795],"hpluv":[266.583697157343806,468.761962088723578,36.1133053940478774],"hsluv":[266.583697157343806,99.999999999999531,36.1133053940478774]},"#aaaa00":{"lch":[67.4983691984715506,74.4102446110960472,85.8743202181747449],"luv":[67.4983691984715506,5.35340686476390193,74.217420717938225],"rgb":[0.66666666666666663,0.66666666666666663,0],"xyz":[0.309512896760441802,0.372958073182539818,0.0556842125390607443],"hpluv":[85.8743202181747449,139.887458074797593,67.4983691984715506],"hsluv":[85.8743202181747449,100.000000000002373,67.4983691984715506]},"#aaaa11":{"lch":[67.528557359020084,73.1276023311446863,85.8743202181746881],"luv":[67.528557359020084,5.26112782412359792,72.9381022286724345],"rgb":[0.66666666666666663,0.66666666666666663,0.0666666666666666657],"xyz":[0.310524562260078907,0.373362739382394671,0.061012317503816374],"hpluv":[85.8743202181746881,137.414698385368666,67.528557359020084],"hsluv":[85.8743202181746881,98.2323220941626118,67.528557359020084]},"#aaaa22":{"lch":[67.5844605157977,70.7729399531690575,85.8743202181746],"luv":[67.5844605157977,5.09172284764045369,70.589541633712912],"rgb":[0.66666666666666663,0.66666666666666663,0.133333333333333331],"xyz":[0.312399920398555964,0.374112882637785471,0.0708892036997955666],"hpluv":[85.8743202181746,132.880028295953878,67.5844605157977],"hsluv":[85.8743202181746,94.9906661574379854,67.5844605157977]},"#aaaa33":{"lch":[67.6763416895574181,66.9597491826677924,85.8743202181744607],"luv":[67.6763416895574181,4.81738479440415723,66.7862322215321882],"rgb":[0.66666666666666663,0.66666666666666663,0.2],"xyz":[0.315487671131013669,0.375347982930768598,0.0871513575574067167],"hpluv":[85.8743202181744607,125.549870841925909,67.6763416895574181],"hsluv":[85.8743202181744607,89.7506270896691376,67.6763416895574181]},"#aaaa44":{"lch":[67.8086418759902898,61.5895608625658824,85.8743202181741623],"luv":[67.8086418759902898,4.43102935143870269,61.429960004304057],"rgb":[0.66666666666666663,0.66666666666666663,0.266666666666666663],"xyz":[0.319945666375232585,0.377131181028456164,0.110630132510293355],"hpluv":[85.8743202181741623,115.255428047766188,67.8086418759902898],"hsluv":[85.8743202181741623,82.3915379076671854,67.8086418759902898]},"#aaaa55":{"lch":[67.9849384953625844,54.6449851984581514,85.8743202181737786],"luv":[67.9849384953625844,3.93140541890911,54.5033802508787772],"rgb":[0.66666666666666663,0.66666666666666663,0.333333333333333315],"xyz":[0.32590808041459457,0.379516146644201,0.142032179784267271],"hpluv":[85.8743202181737786,101.994541545207838,67.9849384953625844],"hsluv":[85.8743202181737786,72.9118556794948631,67.9849384953625844]},"#aaaa66":{"lch":[68.2081473948541515,46.1798212544818156,85.8743202181731675],"luv":[68.2081473948541515,3.32238354287766668,46.0601526125338268],"rgb":[0.66666666666666663,0.66666666666666663,0.4],"xyz":[0.333493076188102755,0.382550144953604343,0.181979824191411232],"hpluv":[85.8743202181731675,85.9122949373369806,68.2081473948541515],"hsluv":[85.8743202181731675,61.415294923296095,68.2081473948541515]},"#aaaa77":{"lch":[68.4806287458147551,36.3079853189649668,85.874320218171988],"luv":[68.4806287458147551,2.61215937225179973,36.2138981792368284],"rgb":[0.66666666666666663,0.66666666666666663,0.466666666666666674],"xyz":[0.342807089208304661,0.386275750161685139,0.231033626097809142],"hpluv":[85.874320218171988,67.2781031791916604,68.4806287458147551],"hsluv":[85.874320218171988,48.0944497134402909,68.4806287458147551]},"#aaaa88":{"lch":[68.804250183835336,25.1900382005990835,85.8743202181696574],"luv":[68.804250183835336,1.81228437202058101,25.1247616884732103],"rgb":[0.66666666666666663,0.66666666666666663,0.533333333333333326],"xyz":[0.353947506310248461,0.390731917002462736,0.289706489501381281],"hpluv":[85.8743202181696574,46.4571845078746506,68.804250183835336],"hsluv":[85.8743202181696574,33.210400093935057,68.804250183835336]},"#aaaa99":{"lch":[69.1804292601881485,13.0180161266067085,85.8743202181625236],"luv":[69.1804292601881485,0.936574490007753058,12.9842817320504498],"rgb":[0.66666666666666663,0.66666666666666663,0.6],"xyz":[0.36700447041932116,0.395954702646091894,0.358473167142499216],"hpluv":[85.8743202181625236,23.8781611725121081,69.1804292601881485],"hsluv":[85.8743202181625236,17.0695511242654057,69.1804292601881485]},"#aaaaaa":{"lch":[69.6101658300367916,3.6866289517569387e-12,0],"luv":[69.6101658300367916,3.46613397703382525e-12,1.25584564385283521e-12],"rgb":[0.66666666666666663,0.66666666666666663,0.66666666666666663],"xyz":[0.382062163384573716,0.401977779832193,0.437777016759497817],"hpluv":[0,6.72041492281092149e-12,69.6101658300367916],"hsluv":[0,4.48262290109626775e-12,69.6101658300367916]},"#aaaabb":{"lch":[70.0940699613229441,13.6540669730780309,265.874320218191428],"luv":[70.0940699613229441,-0.982334841759501587,-13.618684340418703],"rgb":[0.66666666666666663,0.66666666666666663,0.733333333333333282],"xyz":[0.399199755491910391,0.408832816675127775,0.528035001858139563],"hpluv":[265.874320218191428,24.7183841606301087,70.0940699613229441],"hsluv":[265.874320218191428,17.6184615311656536,70.0940699613229441]},"#aaaacc":{"lch":[70.6323884029978188,27.7441307883788433,265.87432021818438],"luv":[70.6323884029978188,-1.99603725260327192,-27.6722356973355303],"rgb":[0.66666666666666663,0.66666666666666663,0.8],"xyz":[0.41849213093563209,0.416549766852616576,0.629641512528409719],"hpluv":[265.87432021818438,49.8432735452352063,70.6323884029978188],"hsluv":[265.87432021818438,36.467826786828347,70.6323884029978188]},"#aaaadd":{"lch":[71.2250312240615813,42.0886841218373817,265.874320218182],"luv":[71.2250312240615813,-3.02804878123905441,-41.979617097899343],"rgb":[0.66666666666666663,0.66666666666666663,0.866666666666666696],"xyz":[0.440010456486265689,0.425157097072870083,0.742971360428415717],"hpluv":[265.874320218182,74.9845908684596,71.2250312240615813],"hsluv":[265.874320218182,56.4865697219014891,71.2250312240615813]},"#aaaaee":{"lch":[71.8715993709786432,56.5301989351418541,265.874320218180742],"luv":[71.8715993709786432,-4.0670361537863835,-56.3837087159979902],"rgb":[0.66666666666666663,0.66666666666666663,0.933333333333333348],"xyz":[0.463822636752440398,0.4346819691793401,0.86838217649693894],"hpluv":[265.874320218180742,99.8073514218055,71.8715993709786432],"hsluv":[265.874320218180742,77.6546881169827259,71.8715993709786432]},"#aaaaff":{"lch":[72.5714133442747595,70.9376272522327,265.87432021818006],"luv":[72.5714133442747595,-5.10357119085512778,-70.7538021683399],"rgb":[0.66666666666666663,0.66666666666666663,1],"xyz":[0.489993685162271819,0.445150388543272824,1.00621636478872079],"hpluv":[265.87432021818006,124.036757123492009,72.5714133442747595],"hsluv":[265.87432021818006,99.999999999997641,72.5714133442747595]},"#88bb00":{"lch":[70.0174964893220135,84.793654921948729,107.670265811619984],"luv":[70.0174964893220135,-25.7381496181260658,80.7930168347331659],"rgb":[0.533333333333333326,0.733333333333333282,0],"xyz":[0.279226618658270809,0.407742918869184512,0.0639910921330886],"hpluv":[107.670265811619984,153.672481251000221,70.0174964893220135],"hsluv":[107.670265811619984,100.000000000002288,70.0174964893220135]},"#88bb11":{"lch":[70.045943224524,83.6147085810298,107.921872667069366],"luv":[70.045943224524,-25.7299069713944561,79.5574721715938296],"rgb":[0.533333333333333326,0.733333333333333282,0.0666666666666666657],"xyz":[0.280238284157907913,0.408147585069039365,0.0693191970978442318],"hpluv":[107.921872667069366,151.474322676164348,70.045943224524],"hsluv":[107.921872667069366,98.3704135852689,70.045943224524]},"#88bb22":{"lch":[70.0986261940145567,81.4532385975490172,108.403169751544382],"luv":[70.0986261940145567,-25.7149121100318041,77.2876016784231],"rgb":[0.533333333333333326,0.733333333333333282,0.133333333333333331],"xyz":[0.28211364229638497,0.408897728324430165,0.0791960832938234244],"hpluv":[108.403169751544382,147.447759071005407,70.0986261940145567],"hsluv":[108.403169751544382,95.3794817356858431,70.0986261940145567]},"#88bb33":{"lch":[70.185227758130182,77.9617926087192643,109.240455548552404],"luv":[70.185227758130182,-25.6910124331637526,73.6071530961765887],"rgb":[0.533333333333333326,0.733333333333333282,0.2],"xyz":[0.285201393028842676,0.410132828617413292,0.0954582371514345607],"hpluv":[109.240455548552404,140.95335886915251,70.185227758130182],"hsluv":[109.240455548552404,90.5378353052476399,70.185227758130182]},"#88bb44":{"lch":[70.3099541281250708,73.067556170236557,110.558097475340105],"luv":[70.3099541281250708,-25.6581823155790048,68.4143657790612281],"rgb":[0.533333333333333326,0.733333333333333282,0.266666666666666663],"xyz":[0.289659388273061591,0.411916026715100858,0.118937012104321199],"hpluv":[110.558097475340105,131.870330314080604,70.3099541281250708],"hsluv":[110.558097475340105,83.7238240635208655,70.3099541281250708]},"#88bb55":{"lch":[70.4762099132372,66.7907607981454845,112.553407091589676],"luv":[70.4762099132372,-25.6172250438111178,61.682765089203194],"rgb":[0.533333333333333326,0.733333333333333282,0.333333333333333315],"xyz":[0.295621802312423576,0.414300992330845719,0.150339059378295115],"hpluv":[112.553407091589676,120.257778383662554,70.4762099132372],"hsluv":[112.553407091589676,74.9213657205140606,70.4762099132372]},"#88bb66":{"lch":[70.6867901559138687,59.2534136994931373,115.564788197424377],"luv":[70.6867901559138687,-25.56971066242372,53.4523800375924836],"rgb":[0.533333333333333326,0.733333333333333282,0.4],"xyz":[0.303206798085931761,0.417334990640249037,0.190286703785439076],"hpluv":[115.564788197424377,106.368844477737383,70.6867901559138687],"hsluv":[115.564788197424377,64.2082802413586791,70.6867901559138687]},"#88bb77":{"lch":[70.9439811929011483,50.7088031944788611,120.213509646437856],"luv":[70.9439811929011483,-25.5178724843925657,43.8203252530908287],"rgb":[0.533333333333333326,0.733333333333333282,0.466666666666666674],"xyz":[0.312520811106133667,0.421060595848329833,0.239340505691836986],"hpluv":[120.213509646437856,90.6999673798200519,70.9439811929011483],"hsluv":[120.213509646437856,51.7430673824667338,70.9439811929011483]},"#88bb88":{"lch":[71.2496205680497781,41.6266624709187667,127.715012949237462],"luv":[71.2496205680497781,-25.4644588867656232,32.9293237414924747],"rgb":[0.533333333333333326,0.733333333333333282,0.533333333333333326],"xyz":[0.323661228208077467,0.425516762689107431,0.298013369095409153],"hpluv":[127.715012949237462,74.1358655131278397,71.2496205680497781],"hsluv":[127.715012949237462,37.7483692132655,71.2496205680497781]},"#88bb99":{"lch":[71.605136773449729,32.9376822973712677,140.491872589160209],"luv":[71.605136773449729,-25.4125532319559895,20.9545473192710077],"rgb":[0.533333333333333326,0.733333333333333282,0.6],"xyz":[0.336718192317150167,0.430739548332736588,0.366780046736527088],"hpluv":[140.491872589160209,58.3697971606621948,71.605136773449729],"hsluv":[140.491872589160209,40.0947707109065163,71.605136773449729]},"#88bbaa":{"lch":[72.0115788449514298,26.624214972975043,162.310745910586235],"luv":[72.0115788449514298,-25.3653817896029707,8.0898843993512255],"rgb":[0.533333333333333326,0.733333333333333282,0.66666666666666663],"xyz":[0.351775885282402723,0.43676262551883771,0.446083896353525633],"hpluv":[162.310745910586235,46.9152251525928463,72.0115788449514298],"hsluv":[162.310745910586235,42.5902472855224445,72.0115788449514298]},"#88bbbb":{"lch":[72.4696411221425478,25.9090725846495289,192.177050630060563],"luv":[72.4696411221425478,-25.3261303787159093,-5.46508574835939687],"rgb":[0.533333333333333326,0.733333333333333282,0.733333333333333282],"xyz":[0.368913477389739397,0.443617662361772469,0.536341881452167435],"hpluv":[192.177050630060563,45.3664800039612786,72.4696411221425478],"hsluv":[192.177050630060563,45.1889022919971595,72.4696411221425478]},"#88bbcc":{"lch":[72.9796861172365539,31.9509756483125074,217.648879320727843],"luv":[72.9796861172365539,-25.297786835453639,-19.5163220435359541],"rgb":[0.533333333333333326,0.733333333333333282,0.8],"xyz":[0.388205852833461096,0.451334612539261271,0.637948392122437591],"hpluv":[217.648879320727843,55.5547835161394303,72.9796861172365539],"hsluv":[217.648879320727843,47.846459663484211,72.9796861172365539]},"#88bbdd":{"lch":[73.5417671198988501,42.2771793498237827,233.271065498294831],"luv":[73.5417671198988501,-25.2830202693059967,-33.8840490472881442],"rgb":[0.533333333333333326,0.733333333333333282,0.866666666666666696],"xyz":[0.409724178384094695,0.459941942759514777,0.751278240022443589],"hpluv":[233.271065498294831,72.9476439375157781,73.5417671198988501],"hsluv":[233.271065498294831,52.600286903313318,73.5417671198988501]},"#88bbee":{"lch":[74.1556513704433655,54.6140049995012049,242.42172167609948],"luv":[74.1556513704433655,-25.2841013367454792,-48.4087157615106349],"rgb":[0.533333333333333326,0.733333333333333282,0.933333333333333348],"xyz":[0.433536358650269404,0.469466814865984794,0.876689056090966812],"hpluv":[242.42172167609948,93.4542589851821646,74.1556513704433655],"hsluv":[242.42172167609948,75.5983078602269387,74.1556513704433655]},"#88bbff":{"lch":[74.8208441285393206,67.8489412305869877,248.103607922481928],"luv":[74.8208441285393206,-25.3028619495915343,-62.9543009116260492],"rgb":[0.533333333333333326,0.733333333333333282,1],"xyz":[0.459707407060100826,0.479935234229917518,1.01452324438274855],"hpluv":[248.103607922481928,115.069386567942303,74.8208441285393206],"hsluv":[248.103607922481928,99.9999999999973568,74.8208441285393206]},"#333300":{"lch":[20.3279441284931792,22.4095383785379596,85.8743202181747449],"luv":[20.3279441284931792,1.61224273913978733,22.3514671484727536],"rgb":[0.2,0.2,0],"xyz":[0.0254898472303871464,0.0307148568226560392,0.00458585760277267773],"hpluv":[85.8743202181747449,139.887458074797735,20.3279441284931792],"hsluv":[85.8743202181747449,100.000000000002458,20.3279441284931792]},"#333311":{"lch":[20.4867879892499971,17.9332091798965507,85.8743202181741],"luv":[20.4867879892499971,1.2901955319819518,17.8867377399899183],"rgb":[0.2,0.2,0.0666666666666666657],"xyz":[0.0265015127300242681,0.0311195230225108921,0.00991396256752831],"hpluv":[85.8743202181741,111.076827622251201,20.4867879892499971],"hsluv":[85.8743202181741,79.4044220625277717,20.4867879892499971]},"#333322":{"lch":[20.7776374982028358,10.4602453251552614,85.8743202181717749],"luv":[20.7776374982028358,0.752556982220839221,10.4331390411008691],"rgb":[0.2,0.2,0.133333333333333331],"xyz":[0.0283768708685012867,0.0318696662779017134,0.019790848763507507],"hpluv":[85.8743202181717749,63.8829601302186703,20.7776374982028358],"hsluv":[85.8743202181717749,45.6673964981637113,20.7776374982028358]},"#333333":{"lch":[21.246731294981295,1.12524964979295229e-12,0],"luv":[21.246731294981295,1.05794917113478783e-12,3.83314917077821647e-13],"rgb":[0.2,0.2,0.2],"xyz":[0.0314646216009590307,0.0331047665708848263,0.0360530026211186502],"hpluv":[0,6.72041492281092149e-12,21.246731294981295],"hsluv":[0,1.92419399944792236e-12,21.246731294981295]},"#333344":{"lch":[21.9038391599933462,12.2084714240410825,265.874320218182163],"luv":[21.9038391599933462,-0.878332211796974,-12.176834853004598],"rgb":[0.2,0.2,0.266666666666666663],"xyz":[0.0359226168451779043,0.0348879646685724,0.0595317775740052887],"hpluv":[265.874320218182163,70.7262082967351517,21.9038391599933462],"hsluv":[265.874320218182163,13.7757030029577514,21.9038391599933462]},"#333355":{"lch":[22.7485838486986935,25.0264321710322868,265.874320218179776],"luv":[22.7485838486986935,-1.80051382017376982,-24.9615796213830023],"rgb":[0.2,0.2,0.333333333333333315],"xyz":[0.0418850308845399,0.0372729302843172322,0.0909338248479792],"hpluv":[265.874320218179776,139.599512106194084,22.7485838486986935],"hsluv":[265.874320218179776,27.1905063829271256,22.7485838486986935]},"#333366":{"lch":[23.7726526978294,37.7235732610660364,265.874320218179],"luv":[23.7726526978294,-2.71400312032964841,-37.625817820292724],"rgb":[0.2,0.2,0.4],"xyz":[0.0494700266580480746,0.0403069285937205438,0.130881469255123173],"hpluv":[265.874320218179,201.360603518100845,23.7726526978294],"hsluv":[265.874320218179,39.2200280117306193,23.7726526978294]},"#333377":{"lch":[24.9621315786770737,49.9646270765614062,265.874320218178639],"luv":[24.9621315786770737,-3.59467945556105306,-49.8351506319748268],"rgb":[0.2,0.2,0.466666666666666674],"xyz":[0.0587840396782499941,0.0440325338018013601,0.179935271161521082],"hpluv":[265.874320218178639,253.992158426909725,24.9621315786770737],"hsluv":[265.874320218178639,49.4713434217918646,24.9621315786770737]},"#333388":{"lch":[26.2997861111378413,61.6680265106551175,265.874320218178468],"luv":[26.2997861111378413,-4.43667452222106906,-61.5082223194812912],"rgb":[0.2,0.2,0.533333333333333326],"xyz":[0.0699244567801938222,0.048488700642578958,0.238608134565093222],"hpluv":[265.874320218178468,297.541234413863208,26.2997861111378413],"hsluv":[265.874320218178468,57.953618257344317,26.2997861111378413]},"#333399":{"lch":[27.7670269025285634,72.8744236647892336,265.874320218178354],"luv":[27.7670269025285634,-5.24291301488723782,-72.6855796399362788],"rgb":[0.2,0.2,0.6],"xyz":[0.0829814208892665356,0.0537114862862081155,0.307374812206211157],"hpluv":[265.874320218178354,333.031319879373427,27.7670269025285634],"hsluv":[265.874320218178354,64.8662025552495862,27.7670269025285634]},"#3333aa":{"lch":[29.34539826905295,83.6653121043175361,265.874320218178241],"luv":[29.34539826905295,-6.01925794630032573,-83.4485049793509575],"rgb":[0.2,0.2,0.66666666666666663],"xyz":[0.0980391138545190777,0.059734563472309217,0.386678661823209757],"hpluv":[265.874320218178241,361.780166220798492,29.34539826905295],"hsluv":[265.874320218178241,70.4657614516561353,29.34539826905295]},"#3333bb":{"lch":[31.0175640968910713,94.1237197643615247,265.874320218178184],"luv":[31.0175640968910713,-6.77168271864664284,-93.8798111173964855],"rgb":[0.2,0.2,0.733333333333333282],"xyz":[0.115176705961855724,0.0665896003152439686,0.476936646921851504],"hpluv":[265.874320218178184,385.062051502536349,31.0175640968910713],"hsluv":[265.874320218178184,75.0004925607309758,31.0175640968910713]},"#3333cc":{"lch":[32.7678589751368321,104.319620441623087,265.874320218178127],"luv":[32.7678589751368321,-7.50522156082274261,-104.049290523324899],"rgb":[0.2,0.2,0.8],"xyz":[0.134469081405577451,0.0743065504927327702,0.578543157592121604],"hpluv":[265.874320218178127,403.977575952485893,32.7678589751368321],"hsluv":[265.874320218178127,79.524052836351089,32.7678589751368321]},"#3333dd":{"lch":[34.5825131799139243,114.307143948468337,265.874320218178127],"luv":[34.5825131799139243,-8.22376881440244745,-114.010932739554079],"rgb":[0.2,0.2,0.866666666666666696],"xyz":[0.155987406956211,0.0829138807129863,0.691873005492127602],"hpluv":[265.874320218178127,419.426773039758132,34.5825131799139243],"hsluv":[265.874320218178127,86.1542613798901584,34.5825131799139243]},"#3333ee":{"lch":[36.4496605331747929,124.126383834434506,265.87432021817807],"luv":[36.4496605331747929,-8.93020898923314732,-123.80472741871364],"rgb":[0.2,0.2,0.933333333333333348],"xyz":[0.179799587222385732,0.092438752819456349,0.817283821560650825],"hpluv":[265.87432021817807,432.12554656995303,36.4496605331747929],"hsluv":[265.87432021817807,92.9362870993519,36.4496605331747929]},"#3333ff":{"lch":[38.3592184432327414,133.806417871427385,265.87432021817807],"luv":[38.3592184432327414,-9.62663407069321231,-133.45967698167118],"rgb":[0.2,0.2,1],"xyz":[0.205970635632217125,0.102907172183389045,0.95511800985243267],"hpluv":[265.87432021817807,442.635784237250618,38.3592184432327414],"hsluv":[265.87432021817807,99.99999999999946,38.3592184432327414]},"#aabb00":{"lch":[72.2864137555308162,81.0402066187271686,93.9104624709461291],"luv":[72.2864137555308162,-5.52673715975233737,80.8515322376329664],"rgb":[0.66666666666666663,0.733333333333333282,0],"xyz":[0.343467394669040582,0.440867068999738376,0.0670023785085933632],"hpluv":[93.9104624709461291,142.260125307220505,72.2864137555308162],"hsluv":[93.9104624709461291,100.00000000000226,72.2864137555308162]},"#aabb11":{"lch":[72.3134178153803759,79.8795424406406198,94.0047051989067199],"luv":[72.3134178153803759,-5.5786590117117969,79.6845020324350912],"rgb":[0.66666666666666663,0.733333333333333282,0.0666666666666666657],"xyz":[0.344479060168677687,0.441271735199593229,0.072330483473349],"hpluv":[94.0047051989067199,140.170301198642704,72.3134178153803759],"hsluv":[94.0047051989067199,98.4998030851846522,72.3134178153803759]},"#aabb22":{"lch":[72.3634325080893319,77.7463008610920667,94.1853407287125179],"luv":[72.3634325080893319,-5.67416055225396132,77.5389656857163],"rgb":[0.66666666666666663,0.733333333333333282,0.133333333333333331],"xyz":[0.346354418307154743,0.44202187845498403,0.0822073696693281925],"hpluv":[94.1853407287125179,136.332658010127801,72.3634325080893319],"hsluv":[94.1853407287125179,95.7441801677588131,72.3634325080893319]},"#aabb33":{"lch":[72.4456578556837343,74.284969405898,94.5007685828256427],"luv":[72.4456578556837343,-5.82932493434540167,74.0558954469191377],"rgb":[0.66666666666666663,0.733333333333333282,0.2],"xyz":[0.349442169039612449,0.443256978747967156,0.0984695235269393288],"hpluv":[94.5007685828256427,130.115164427369336,72.4456578556837343],"hsluv":[94.5007685828256427,91.2774770665997721,72.4456578556837343]},"#aabb44":{"lch":[72.5641031469497193,69.3968632274838768,95.0004815565242922],"luv":[72.5641031469497193,-6.04891620231322502,69.1327363742496317],"rgb":[0.66666666666666663,0.733333333333333282,0.266666666666666663],"xyz":[0.353900164283831364,0.445040176845654722,0.121948298479825967],"hpluv":[95.0004815565242922,121.354905136961719,72.5641031469497193],"hsluv":[95.0004815565242922,84.978619462346515,72.5641031469497193]},"#aabb55":{"lch":[72.7220260779140659,63.0543258899363366,95.7659530899235705],"luv":[72.7220260779140659,-6.33475857174489843,62.7353078200147181],"rgb":[0.66666666666666663,0.733333333333333282,0.333333333333333315],"xyz":[0.359862578323193349,0.447425142461399583,0.153350345753799883],"hpluv":[95.7659530899235705,110.024206089370551,72.7220260779140659],"hsluv":[95.7659530899235705,76.8198788639563475,72.7220260779140659]},"#aabb66":{"lch":[72.922116394456026,55.2958006214329245,96.9449007941817342],"luv":[72.922116394456026,-6.68608027099689206,54.8900892418207675],"rgb":[0.66666666666666663,0.733333333333333282,0.4],"xyz":[0.367447574096701535,0.450459140770802902,0.193297990160943844],"hpluv":[96.9449007941817342,96.2215198645611167,72.922116394456026],"hsluv":[96.9449007941817342,66.8569788454327778,72.922116394456026]},"#aabb77":{"lch":[73.1665925415562555,46.2248747385797927,98.8352478745929801],"luv":[73.1665925415562555,-7.09985217623529596,45.6763740206356488],"rgb":[0.66666666666666663,0.733333333333333282,0.466666666666666674],"xyz":[0.37676158711690344,0.454184745978883697,0.242351792067341754],"hpluv":[98.8352478745929801,80.1682197570307267,73.1665925415562555],"hsluv":[98.8352478745929801,55.2184354086421223,73.1665925415562555]},"#aabb88":{"lch":[73.4572589636137,36.0214199172978056,102.133241078873],"luv":[73.4572589636137,-7.57119112276486117,35.2167539367396216],"rgb":[0.66666666666666663,0.733333333333333282,0.533333333333333326],"xyz":[0.387902004218847241,0.458640912819661295,0.301024655470913949],"hpluv":[102.133241078873,62.2250760292602862,73.4572589636137],"hsluv":[102.133241078873,42.0922357464751116,73.4572589636137]},"#aabb99":{"lch":[73.7955437608147236,25.0118273668521454,108.880774444702425],"luv":[73.7955437608147236,-8.09382587885112592,23.6660408786948082],"rgb":[0.66666666666666663,0.733333333333333282,0.6],"xyz":[0.40095896832792,0.463863698463290453,0.369791333112031828],"hpluv":[108.880774444702425,43.0085312761643337,73.7955437608147236],"hsluv":[108.880774444702425,27.7101400322224,73.7955437608147236]},"#aabbaa":{"lch":[74.1825262226786464,14.1574418893213867,127.715012949227386],"luv":[74.1825262226786464,-8.66059336811296632,11.1994322785447302],"rgb":[0.66666666666666663,0.733333333333333282,0.66666666666666663],"xyz":[0.416016661293172496,0.469886775649391575,0.449095182729030429],"hpluv":[127.715012949227386,24.2171200924073027,74.1825262226786464],"hsluv":[127.715012949227386,12.3308304853939106,74.1825262226786464]},"#aabbbb":{"lch":[74.6189593067414734,9.47715438031144153,192.177050630059],"luv":[74.6189593067414734,-9.26392276955498417,-1.99904729008021675],"rgb":[0.66666666666666663,0.733333333333333282,0.733333333333333282],"xyz":[0.43315425340050917,0.476741812492326333,0.539353167827672175],"hpluv":[192.177050630059,16.1164020856357908,74.6189593067414734],"hsluv":[192.177050630059,16.0533177597809029,74.6189593067414734]},"#aabbcc":{"lch":[75.1052899078477623,18.5983929047544514,237.852316168687679],"luv":[75.1052899078477623,-9.89626817417043547,-15.7468757175676597],"rgb":[0.66666666666666663,0.733333333333333282,0.8],"xyz":[0.452446628844230925,0.484458762669815135,0.640959678497942331],"hpluv":[237.852316168687679,31.4227520087917149,75.1052899078477623],"hsluv":[237.852316168687679,25.8004515395991127,75.1052899078477623]},"#aabbdd":{"lch":[75.6416785331857682,31.6798915222297,250.547006926215033],"luv":[75.6416785331857682,-10.5504613433008458,-29.8714461033234144],"rgb":[0.66666666666666663,0.733333333333333282,0.866666666666666696],"xyz":[0.473964954394864413,0.493066092890068641,0.754289526397948329],"hpluv":[250.547006926215033,53.1449318902714438,75.6416785331857682],"hsluv":[250.547006926215033,48.9122949421499484,75.6416785331857682]},"#aabbee":{"lch":[76.2280192594463699,45.6175545861468166,255.761586302643195],"luv":[76.2280192594463699,-11.2199701465387403,-44.2162137267639],"rgb":[0.66666666666666663,0.733333333333333282,0.933333333333333348],"xyz":[0.497777134661039178,0.502590964996538658,0.879700342466471552],"hpluv":[255.761586302643195,76.8288153841444483,76.2280192594463699],"hsluv":[255.761586302643195,73.6249760737912311,76.2280192594463699]},"#aabbff":{"lch":[76.863960378353255,59.8395450787859247,258.530312903596609],"luv":[76.863960378353255,-11.8990616804050067,-58.6445520603744583],"rgb":[0.66666666666666663,0.733333333333333282,1],"xyz":[0.523948183070870543,0.513059384360471382,1.01753453075825329],"hpluv":[258.530312903596609,104.151140840900069,76.863960378353255],"hsluv":[258.530312903596609,99.9999999999969305,76.863960378353255]},"#88cc00":{"lch":[75.0884647575288,93.9986167747887,111.475410134903882],"luv":[75.0884647575288,-34.413070492851,87.4727416674912348],"rgb":[0.533333333333333326,0.8,0],"xyz":[0.317450361967887784,0.484190405488419406,0.0767323399029605363],"hpluv":[111.475410134903882,158.85012628624969,75.0884647575288],"hsluv":[111.475410134903882,100.000000000002288,75.0884647575288]},"#88cc11":{"lch":[75.1138336746799,92.9472917959209752,111.713770271434171],"luv":[75.1138336746799,-34.3877142984514421,86.3520941119794685],"rgb":[0.533333333333333326,0.8,0.0666666666666666657],"xyz":[0.318462027467524889,0.484595071688274259,0.082060444867716173],"hpluv":[111.713770271434171,157.020421425354783,75.1138336746799],"hsluv":[111.713770271434171,98.6248626695712289,75.1138336746799]},"#88cc22":{"lch":[75.1608235532889495,91.0175657840787551,112.166724150973],"luv":[75.1608235532889495,-34.3412008388942667,84.2904455214339805],"rgb":[0.533333333333333326,0.8,0.133333333333333331],"xyz":[0.320337385606001945,0.48534521494366506,0.0919373310636953656],"hpluv":[112.166724150973,153.664310911800271,75.1608235532889495],"hsluv":[112.166724150973,96.0970283406349,75.1608235532889495]},"#88cc33":{"lch":[75.2380863503033908,87.8942550668024865,112.94545296029041],"luv":[75.2380863503033908,-34.2659803117460342,80.9397471396048331],"rgb":[0.533333333333333326,0.8,0.2],"xyz":[0.323425136338459651,0.486580315236648187,0.108199484921306516],"hpluv":[112.94545296029041,148.2388630129999,75.2380863503033908],"hsluv":[112.94545296029041,91.9942509365254324,75.2380863503033908]},"#88cc44":{"lch":[75.3494055810326415,83.5027286146262,114.147802574759467],"luv":[75.3494055810326415,-34.1602904869016655,76.1956707427555244],"rgb":[0.533333333333333326,0.8,0.266666666666666663],"xyz":[0.327883131582678566,0.488363513334335753,0.131678259874193154],"hpluv":[114.147802574759467,140.624231819797672,75.3494055810326415],"hsluv":[114.147802574759467,86.1974134191241177,75.3494055810326415]},"#88cc55":{"lch":[75.4978684165763241,77.8461227965530469,115.917002828128631],"luv":[75.4978684165763241,-34.0241050685150555,70.0169915716368365],"rgb":[0.533333333333333326,0.8,0.333333333333333315],"xyz":[0.333845545622040552,0.490748478950080613,0.16308030714816707],"hpluv":[115.917002828128631,130.840327774204638,75.4978684165763241],"hsluv":[115.917002828128631,78.6694108520956092,75.4978684165763241]},"#88cc66":{"lch":[75.6860396561484663,71.0106875190110287,118.477611184333398],"luv":[75.6860396561484663,-33.8589835890750237,62.4186428259808963],"rgb":[0.533333333333333326,0.8,0.4],"xyz":[0.341430541395548737,0.493782477259483932,0.203027951555311],"hpluv":[118.477611184333398,119.054896833398359,75.6860396561484663],"hsluv":[118.477611184333398,69.4467240035668141,75.6860396561484663]},"#88cc77":{"lch":[75.9160535408259705,63.1826013081171638,122.199343200922883],"luv":[75.9160535408259705,-33.6678964132470924,53.4650713940171656],"rgb":[0.533333333333333326,0.8,0.466666666666666674],"xyz":[0.350744554415750642,0.497508082467564727,0.252081753461708913],"hpluv":[122.199343200922883,105.609553255175555,75.9160535408259705],"hsluv":[122.199343200922883,58.6308874961384063,75.9160535408259705]},"#88cc88":{"lch":[76.1896681333118124,54.6887581458639929,127.715012949238314],"luv":[76.1896681333118124,-33.4549913615237,43.2622678616965288],"rgb":[0.533333333333333326,0.8,0.533333333333333326],"xyz":[0.361884971517694443,0.50196424930834227,0.310754616865281108],"hpluv":[127.715012949238314,91.92606633748386,76.1896681333118124],"hsluv":[127.715012949238314,46.3779062484352878,76.1896681333118124]},"#88cc99":{"lch":[76.5083007543168492,46.0935908553499587,136.122445502284393],"luv":[76.5083007543168492,-33.2253063874269188,31.9483666781575764],"rgb":[0.533333333333333326,0.8,0.6],"xyz":[0.374941935626767142,0.507187034951971483,0.379521294506399],"hpluv":[136.122445502284393,78.7570782006024643,76.5083007543168492],"hsluv":[136.122445502284393,48.1280089891728622,76.5083007543168492]},"#88ccaa":{"lch":[76.8730534179438223,38.4110576559043437,149.173477570542047],"luv":[76.8730534179438223,-32.9844501501569098,19.6833787378341469],"rgb":[0.533333333333333326,0.8,0.66666666666666663],"xyz":[0.389999628592019698,0.513210112138072549,0.458825144123397588],"hpluv":[149.173477570542047,66.8865090410736229,76.8730534179438223],"hsluv":[149.173477570542047,50.0095890879800891,76.8730534179438223]},"#88ccbb":{"lch":[77.2847330465352087,33.404634806871222,168.536638924911273],"luv":[77.2847330465352087,-32.73827895277973,6.638879257104497],"rgb":[0.533333333333333326,0.8,0.733333333333333282],"xyz":[0.407137220699356372,0.520065148981007308,0.54908312922203939],"hpluv":[168.536638924911273,59.444025112992847,77.2847330465352087],"hsluv":[168.536638924911273,51.9915664660802292,77.2847330465352087]},"#88cccc":{"lch":[77.7438691793350074,33.2404934388306,192.177050630060876],"luv":[77.7438691793350074,-32.4925976386916417,-7.01152642061846443],"rgb":[0.533333333333333326,0.8,0.8],"xyz":[0.426429596143078071,0.527782099158496165,0.650689639892309546],"hpluv":[192.177050630060876,60.6231146417445359,77.7438691793350074],"hsluv":[192.177050630060876,54.0427382519684585,77.7438691793350074]},"#88ccdd":{"lch":[78.2507307533704,38.5418325774740964,213.193277164244591],"luv":[78.2507307533704,-32.2529061156020589,-21.1003058159876602],"rgb":[0.533333333333333326,0.8,0.866666666666666696],"xyz":[0.44794792169371167,0.536389429378749671,0.764019487792315544],"hpluv":[213.193277164244591,72.2588367593471759,78.2507307533704],"hsluv":[213.193277164244591,56.1332045791076126,78.2507307533704]},"#88ccee":{"lch":[78.8053428571366368,47.78945312574524,227.924421751618695],"luv":[78.8053428571366368,-32.0242046898233,-35.4722728908382834],"rgb":[0.533333333333333326,0.8,0.933333333333333348],"xyz":[0.47176010195988638,0.545914301485219688,0.889430303860838767],"hpluv":[227.924421751618695,92.3999074491721473,78.8053428571366368],"hsluv":[227.924421751618695,70.4270935368067796,78.8053428571366368]},"#88ccff":{"lch":[79.4075039272108114,59.2521837878726885,237.529017152572294],"luv":[79.4075039272108114,-31.8108626015509195,-49.9889018100727327],"rgb":[0.533333333333333326,0.8,1],"xyz":[0.497931150369717801,0.556382720849152412,1.02726449215262061],"hpluv":[237.529017152572294,118.549697277888072,79.4075039272108114],"hsluv":[237.529017152572294,99.999999999996561,79.4075039272108114]},"#334400":{"lch":[26.2681529832905483,31.0251081485104194,104.276907196552472],"luv":[26.2681529832905483,-7.65105337509169647,30.0669040288198879],"rgb":[0.2,0.266666666666666663,0],"xyz":[0.034322417713353183,0.0483799977885883553,0.00753004776376127276],"hpluv":[104.276907196552472,149.872894059772364,26.2681529832905483],"hsluv":[104.276907196552472,100.000000000002302,26.2681529832905483]},"#334411":{"lch":[26.3856741683463127,27.3886432490873446,106.295788944443402],"luv":[26.3856741683463127,-7.68514827558869662,26.2883296351821087],"rgb":[0.2,0.266666666666666663,0.0666666666666666657],"xyz":[0.0353340832129903082,0.0487846639884432082,0.0128581527285169042],"hpluv":[106.295788944443402,131.716945387015755,26.3856741683463127],"hsluv":[106.295788944443402,86.6296124974134614,26.3856741683463127]},"#334422":{"lch":[26.6018195362025054,21.2226436820006832,111.412237748000109],"luv":[26.6018195362025054,-7.74787020962619799,19.7578114189793403],"rgb":[0.2,0.266666666666666663,0.133333333333333331],"xyz":[0.0372094413514673233,0.0495348072438340295,0.0227350389244961],"hpluv":[111.412237748000109,101.234249163259577,26.6018195362025054],"hsluv":[111.412237748000109,63.7673799144094,26.6018195362025054]},"#334433":{"lch":[26.9529945323855813,12.8320575385027151,127.715012949236225],"luv":[26.9529945323855813,-7.8498102472204323,10.150969399721264],"rgb":[0.2,0.266666666666666663,0.2],"xyz":[0.0402971920839250639,0.0507699075368171424,0.038997192782107247],"hpluv":[127.715012949236225,60.4127494816677171,26.9529945323855813],"hsluv":[127.715012949236225,30.7608572023581708,26.9529945323855813]},"#334444":{"lch":[27.4501004194092673,8.17817622085537721,192.177050630060421],"luv":[27.4501004194092673,-7.99417102070306651,-1.72504956193021397],"rgb":[0.2,0.266666666666666663,0.266666666666666663],"xyz":[0.0447551873281439444,0.0525531056345047154,0.0624759677349938855],"hpluv":[192.177050630060421,37.8052272806132379,27.4501004194092673],"hsluv":[192.177050630060421,37.6572465300637518,27.4501004194092673]},"#334455":{"lch":[28.0976851129048839,16.9879579660521678,241.20654356816641],"luv":[28.0976851129048839,-8.1823109542023289,-14.8875956186718064],"rgb":[0.2,0.266666666666666663,0.333333333333333315],"xyz":[0.0507176013675059364,0.0549380712502495483,0.0938780150089678],"hpluv":[241.20654356816641,76.720241835207986,28.0976851129048839],"hsluv":[241.20654356816641,44.9859363973003568,28.0976851129048839]},"#334466":{"lch":[28.8949601880565652,29.7043747380071501,253.545194757545516],"luv":[28.8949601880565652,-8.41402976058801855,-28.487786536758108],"rgb":[0.2,0.266666666666666663,0.4],"xyz":[0.0583025971410141147,0.0579720695596528598,0.133825659416111742],"hpluv":[253.545194757545516,130.448064952476017,28.8949601880565652],"hsluv":[253.545194757545516,52.1427730716471629,28.8949601880565652]},"#334477":{"lch":[29.8367962202096138,42.8398018741040758,258.299336276781332],"luv":[29.8367962202096138,-8.68785350705267589,-41.9496105590085691],"rgb":[0.2,0.266666666666666663,0.466666666666666674],"xyz":[0.0676166101612160342,0.0616976747677336762,0.182879461322509651],"hpluv":[258.299336276781332,182.19421868795277,29.8367962202096138],"hsluv":[258.299336276781332,58.7347196438674857,29.8367962202096138]},"#334488":{"lch":[30.9147794404111025,55.6960507233291082,260.699324576281469],"luv":[30.9147794404111025,-9.00134255207500189,-54.9638599302837605],"rgb":[0.2,0.266666666666666663,0.533333333333333326],"xyz":[0.0787570272631598622,0.066153841608511274,0.241552324726081818],"hpluv":[260.699324576281469,228.611238042833349,30.9147794404111025],"hsluv":[260.699324576281469,64.5716173949788583,30.9147794404111025]},"#334499":{"lch":[32.1182691124294664,68.0605660836803281,262.102678541403236],"luv":[32.1182691124294664,-9.35140200670356769,-67.4150720250304],"rgb":[0.2,0.266666666666666663,0.6],"xyz":[0.0918139913722325618,0.0713766272521404316,0.310319002367199726],"hpluv":[262.102678541403236,268.895014901030436,32.1182691124294664],"hsluv":[262.102678541403236,69.6097770986353623,32.1182691124294664]},"#3344aa":{"lch":[33.435366318838156,79.8975403264742852,263.001802037450773],"luv":[33.435366318838156,-9.73456660877536528,-79.3023023824649158],"rgb":[0.2,0.266666666666666663,0.66666666666666663],"xyz":[0.106871684337485118,0.07739970443824154,0.389622851984198326],"hpluv":[263.001802037450773,303.226149032408784,33.435366318838156],"hsluv":[263.001802037450773,73.891501645941716,33.435366318838156]},"#3344bb":{"lch":[34.8537252521307721,91.2457354427413492,263.615054402289786],"luv":[34.8537252521307721,-10.1472390443123768,-90.6797539490724773],"rgb":[0.2,0.266666666666666663,0.733333333333333282],"xyz":[0.124009276444821764,0.0842547412811763,0.479880837082840073],"hpluv":[263.615054402289786,332.202349056841342,34.8537252521307721],"hsluv":[263.615054402289786,77.4995146205371412,34.8537252521307721]},"#3344cc":{"lch":[36.3611746115969083,102.170263494902102,264.052905077601281],"luv":[36.3611746115969083,-10.5858721727669973,-101.620382074461631],"rgb":[0.2,0.266666666666666663,0.8],"xyz":[0.143301651888543491,0.0919716914586651,0.581487347753110284],"hpluv":[264.052905077601281,356.55451128807033,36.3611746115969083],"hsluv":[264.052905077601281,80.5286468598271767,36.3611746115969083]},"#3344dd":{"lch":[37.9461503215655611,112.73940211649635,264.376684246138268],"luv":[37.9461503215655611,-11.0470957042534899,-112.196855865421313],"rgb":[0.2,0.266666666666666663,0.866666666666666696],"xyz":[0.164819977439177034,0.100579021678918634,0.694817195653116282],"hpluv":[264.376684246138268,377.005191833442723,37.9461503215655611],"hsluv":[264.376684246138268,84.1625325255073,37.9461503215655611]},"#3344ee":{"lch":[39.5979632159824462,123.014623823289881,264.622878978329823],"luv":[39.5979632159824462,-11.5277944422180187,-122.473293536523641],"rgb":[0.2,0.266666666666666663,0.933333333333333348],"xyz":[0.188632157705351772,0.110103893785388651,0.820228011721639505],"hpluv":[264.622878978329823,394.205992168221826,39.5979632159824462],"hsluv":[264.622878978329823,91.9832832027675664,39.5979632159824462]},"#3344ff":{"lch":[41.3069357297154482,133.047388274610427,264.814390787746049],"luv":[41.3069357297154482,-12.0251481753234213,-132.502842754623401],"rgb":[0.2,0.266666666666666663,1],"xyz":[0.214803206115183165,0.120572313149321361,0.95806220001342135],"hpluv":[264.814390787746049,408.716999433792864,41.3069357297154482],"hsluv":[264.814390787746049,99.9999999999994458,41.3069357297154482]},"#aacc00":{"lch":[77.1199831352121,88.8460909387721,100.173289143969555],"luv":[77.1199831352121,-15.692520471132994,87.4492577233428108],"rgb":[0.66666666666666663,0.8,0],"xyz":[0.381691137978657502,0.517314555618973326,0.0797436262784653],"hpluv":[100.173289143969555,156.730643533439519,77.1199831352121],"hsluv":[100.173289143969555,100.000000000002373,77.1199831352121]},"#aacc11":{"lch":[77.1442576556984676,87.800209961997254,100.311366942902],"luv":[77.1442576556984676,-15.7160097521192821,86.3821967007212237],"rgb":[0.66666666666666663,0.8,0.0666666666666666657],"xyz":[0.382702803478294606,0.517719221818828235,0.085071731243220941],"hpluv":[100.311366942902,155.08420894585916,77.1442576556984676],"hsluv":[100.311366942902,98.7181568051142,77.1442576556984676]},"#aacc22":{"lch":[77.1892227090432641,85.8765024717939411,100.574350259733862],"luv":[77.1892227090432641,-15.7593085348968636,84.4181134075499102],"rgb":[0.66666666666666663,0.8,0.133333333333333331],"xyz":[0.384578161616771663,0.518469365074219,0.0949486174392001336],"hpluv":[100.574350259733862,152.047171814071362,77.1892227090432641],"hsluv":[100.574350259733862,96.3604791744028404,77.1892227090432641]},"#aacc33":{"lch":[77.2631626362465482,82.7513779572315116,101.028364552392574],"luv":[77.2631626362465482,-15.8299191574657527,81.2231753460099668],"rgb":[0.66666666666666663,0.8,0.2],"xyz":[0.387665912349229369,0.519704465367202162,0.111210771296811284],"hpluv":[101.028364552392574,147.088857703341773,77.2631626362465482],"hsluv":[101.028364552392574,92.5301726329619,77.2631626362465482]},"#aacc44":{"lch":[77.3697083481362569,78.3307208035327278,101.734312614214147],"luv":[77.3697083481362569,-15.9304072801298968,76.6937021240348],"rgb":[0.66666666666666663,0.8,0.266666666666666663],"xyz":[0.392123907593448284,0.521487663464889728,0.134689546249697922],"hpluv":[101.734312614214147,140.021584702469511,77.3697083481362569],"hsluv":[101.734312614214147,87.1104907412273377,77.3697083481362569]},"#aacc55":{"lch":[77.5118305539362495,72.5836718124644875,102.784946304826448],"luv":[77.5118305539362495,-16.0622064150904862,70.7841432727597493],"rgb":[0.66666666666666663,0.8,0.333333333333333315],"xyz":[0.398086321632810269,0.523872629080634478,0.16609159352367181],"hpluv":[102.784946304826448,130.736122666873541,77.5118305539362495],"hsluv":[102.784946304826448,80.0585735006639,77.5118305539362495]},"#aacc66":{"lch":[77.6920071630818114,65.5409770618580865,104.333567867024144],"luv":[77.6920071630818114,-16.225762577214013,63.5007425390513731],"rgb":[0.66666666666666663,0.8,0.4],"xyz":[0.405671317406318455,0.526906627390037796,0.2060392379308158],"hpluv":[104.333567867024144,119.198186871977654,77.6920071630818114],"hsluv":[104.333567867024144,71.3979720959128,77.6920071630818114]},"#aacc77":{"lch":[77.912311817127474,57.2990991241466574,106.653185897117098],"luv":[77.912311817127474,-16.4206512214041886,54.8958010589496865],"rgb":[0.66666666666666663,0.8,0.466666666666666674],"xyz":[0.41498533042652036,0.530632232598118647,0.255093039837213709],"hpluv":[106.653185897117098,105.457526508453554,77.912311817127474],"hsluv":[106.653185897117098,61.2114900666368769,77.912311817127474]},"#aacc88":{"lch":[78.1744663257663,48.0371110011961946,110.274462486898784],"luv":[78.1744663257663,-16.6457037997795574,45.0608985524173633],"rgb":[0.66666666666666663,0.8,0.533333333333333326],"xyz":[0.42612574752846416,0.53508839943889619,0.313765903240785848],"hpluv":[110.274462486898784,89.6844859178180513,78.1744663257663],"hsluv":[110.274462486898784,49.6324036967210418,78.1744663257663]},"#aacc99":{"lch":[78.4798746855988583,38.0746183254558517,116.349336294343573],"luv":[78.4798746855988583,-16.8991518201031816,34.1188397866961779],"rgb":[0.66666666666666663,0.8,0.6],"xyz":[0.43918271163753686,0.540311185082525403,0.382532580881903783],"hpluv":[116.349336294343573,72.2917022308792383,78.4798746855988583],"hsluv":[116.349336294343573,36.8338589582570535,78.4798746855988583]},"#aaccaa":{"lch":[78.8296472340984593,28.0820980315160789,127.715012949234293],"luv":[78.8296472340984593,-17.1787837008838338,22.2147163027108476],"rgb":[0.66666666666666663,0.8,0.66666666666666663],"xyz":[0.454240404602789416,0.546334262268626469,0.461836430498902384],"hpluv":[127.715012949234293,54.3703242245777503,78.8296472340984593],"hsluv":[127.715012949234293,23.0170216670413055,78.8296472340984593]},"#aaccbb":{"lch":[79.2246195207133,19.8998018903229301,151.462718702096396],"luv":[79.2246195207133,-17.4821042808439238,9.50674209115818236],"rgb":[0.66666666666666663,0.8,0.733333333333333282],"xyz":[0.47137799671012609,0.553189299111561228,0.55209441559754413],"hpluv":[151.462718702096396,39.3999249764932173,79.2246195207133],"hsluv":[151.462718702096396,25.9022649263565761,79.2246195207133]},"#aacccc":{"lch":[79.665368512397464,18.216345023755192,192.177050630059796],"luv":[79.665368512397464,-17.80648564660072,-3.84243346616347603],"rgb":[0.66666666666666663,0.8,0.8],"xyz":[0.490670372153847789,0.560906249289050085,0.653700926267814286],"hpluv":[192.177050630059796,36.9937893704297878,79.665368512397464],"hsluv":[192.177050630059796,28.9019874878943099,79.665368512397464]},"#aaccdd":{"lch":[80.1522276755556504,25.332665849504842,224.238778454296437],"luv":[80.1522276755556504,-18.1492995031005968,-17.6733382978265112],"rgb":[0.66666666666666663,0.8,0.866666666666666696],"xyz":[0.512188697704481388,0.569513579509303591,0.767030774167820284],"hpluv":[224.238778454296437,52.9363308364733882,80.1522276755556504],"hsluv":[224.238778454296437,37.9969627853472787,80.1522276755556504]},"#aaccee":{"lch":[80.6853018377357216,36.8247992939862954,239.828051209207672],"luv":[80.6853018377357216,-18.5080244281037345,-31.8358112007702587],"rgb":[0.66666666666666663,0.8,0.933333333333333348],"xyz":[0.536000877970656098,0.579038451615773608,0.892441590236343507],"hpluv":[239.828051209207672,79.4490805971393854,80.6853018377357216],"hsluv":[239.828051209207672,67.7857500876551313,80.6853018377357216]},"#aaccff":{"lch":[81.2644823279674284,49.9029399741520834,247.769045833277914],"luv":[81.2644823279674284,-18.8803249793402337,-46.1934708236816434],"rgb":[0.66666666666666663,0.8,1],"xyz":[0.562171926380487519,0.589506870979706332,1.03027577852812535],"hpluv":[247.769045833277914,111.562078640814406,81.2644823279674284],"hsluv":[247.769045833277914,99.9999999999962625,81.2644823279674284]},"#88dd00":{"lch":[80.1491214608994085,103.252993693735874,114.339076779275487],"luv":[80.1491214608994085,-42.5542610136121837,94.0761158653142218],"rgb":[0.533333333333333326,0.866666666666666696,0],"xyz":[0.360084352515062933,0.569458386582770926,0.0909436700853518548],"hpluv":[114.339076779275487,215.722602995807392,80.1491214608994085],"hsluv":[114.339076779275487,100.000000000002288,80.1491214608994085]},"#88dd11":{"lch":[80.1718911006532551,102.308936337717924,114.55620444532569],"luv":[80.1718911006532551,-42.518128048011647,93.0555062414261727],"rgb":[0.533333333333333326,0.866666666666666696,0.0666666666666666657],"xyz":[0.361096018014700038,0.569863052782625834,0.0962717750501074915],"hpluv":[114.55620444532569,214.038948901986089,80.1718911006532551],"hsluv":[114.55620444532569,98.8288322371321186,80.1718911006532551]},"#88dd22":{"lch":[80.2140714473537315,100.574216022873557,114.966813202702411],"luv":[80.2140714473537315,-42.4516967311958453,91.1757992740300125],"rgb":[0.533333333333333326,0.866666666666666696,0.133333333333333331],"xyz":[0.362971376153177094,0.570613196038016635,0.106148661246086684],"hpluv":[114.966813202702411,210.937294411552,80.2140714473537315],"hsluv":[114.966813202702411,96.6732644510524,80.2140714473537315]},"#88dd33":{"lch":[80.283440325316,97.7612202004146695,115.666709499512805],"luv":[80.283440325316,-42.3438510501166832,88.1150069699792908],"rgb":[0.533333333333333326,0.866666666666666696,0.2],"xyz":[0.3660591268856348,0.571848296330999761,0.122410815103697834],"hpluv":[115.666709499512805,205.88559104418357,80.283440325316],"hsluv":[115.666709499512805,93.1672856447162303,80.283440325316]},"#88dd44":{"lch":[80.3834168948860821,93.7943752395521,116.732715571790365],"luv":[80.3834168948860821,-42.1914333437924896,83.7691338081886698],"rgb":[0.533333333333333326,0.866666666666666696,0.266666666666666663],"xyz":[0.370517122129853715,0.573631494428687327,0.145889590056584473],"hpluv":[116.732715571790365,198.714220363605108,80.3834168948860821],"hsluv":[116.732715571790365,88.1979852499996184,80.3834168948860821]},"#88dd55":{"lch":[80.5168087418625475,88.6629430887769,118.270203219525229],"luv":[80.5168087418625475,-41.9934519908601374,78.0875628192801088],"rgb":[0.533333333333333326,0.866666666666666696,0.333333333333333315],"xyz":[0.376479536169215701,0.576016460044432077,0.17729163733055836],"hpluv":[118.270203219525229,189.352414361266369,80.5168087418625475],"hsluv":[118.270203219525229,81.7170886524359474,80.5168087418625475]},"#88dd66":{"lch":[80.6859701106092757,82.4238546620344437,120.433562333743893],"luv":[80.6859701106092757,-41.7508898851674672,71.0672569552589266],"rgb":[0.533333333333333326,0.866666666666666696,0.4],"xyz":[0.384064531942723886,0.579050458353835396,0.21723928173770235],"hpluv":[120.433562333743893,177.835721711884361,80.6859701106092757],"hsluv":[120.433562333743893,73.7344666622195604,80.6859701106092757]},"#88dd77":{"lch":[80.8928858528116734,75.2114908165065827,123.458458278308711],"luv":[80.8928858528116734,-41.4665196955592208,62.7478772165182832],"rgb":[0.533333333333333326,0.866666666666666696,0.466666666666666674],"xyz":[0.393378544962925791,0.582776063561916247,0.266293083644100259],"hpluv":[123.458458278308711,164.332036041008877,80.8928858528116734],"hsluv":[123.458458278308711,64.312379996729149,80.8928858528116734]},"#88dd88":{"lch":[81.1392211885512,67.2590471315356524,127.71501294923884],"luv":[81.1392211885512,-41.1446687958856145,53.2061617739807531],"rgb":[0.533333333333333326,0.866666666666666696,0.533333333333333326],"xyz":[0.404518962064869592,0.587232230402693789,0.324965947047672399],"hpluv":[127.71501294923884,149.199726190229825,81.1392211885512],"hsluv":[127.71501294923884,53.5585775308776633,81.1392211885512]},"#88dd99":{"lch":[81.4263538436063072,58.943488890057,133.791370377408185],"luv":[81.4263538436063072,-40.7909253788266,42.5492102073736831],"rgb":[0.533333333333333326,0.866666666666666696,0.6],"xyz":[0.417575926173942347,0.592455016046323,0.393732624688790334],"hpluv":[133.791370377408185,133.11083868071384,81.4263538436063072],"hsluv":[133.791370377408185,54.8771054313611515,81.4263538436063072]},"#88ddaa":{"lch":[81.7553965772464437,50.8758256364689956,142.591286230634154],"luv":[81.7553965772464437,-40.4117997418374131,30.906893661738728],"rgb":[0.533333333333333326,0.866666666666666696,0.66666666666666663],"xyz":[0.432633619139194847,0.598478093232424069,0.473036474305788934],"hpluv":[142.591286230634154,117.302359569381537,81.7553965772464437],"hsluv":[142.591286230634154,56.3076629138628,81.7553965772464437]},"#88ddbb":{"lch":[82.1272144034785327,44.0521153731503716,155.27715859232552],"luv":[82.1272144034785327,-40.0143654100278567,18.4238820470094566],"rgb":[0.533333333333333326,0.866666666666666696,0.733333333333333282],"xyz":[0.449771211246531522,0.605333130075358827,0.563294459404430681],"hpluv":[155.27715859232552,104.020343594656595,82.1272144034785327],"hsluv":[155.27715859232552,57.829298146967858,82.1272144034785327]},"#88ddcc":{"lch":[82.542438979776648,39.9525966793572422,172.446474622419316],"luv":[82.542438979776648,-39.6059077008678173,5.25186220437346396],"rgb":[0.533333333333333326,0.866666666666666696,0.8],"xyz":[0.469063586690253276,0.613050080252847684,0.664900970074700837],"hpluv":[172.446474622419316,96.9346789585341355,82.542438979776648],"hsluv":[172.446474622419316,59.4202271347422908,82.542438979776648]},"#88dddd":{"lch":[83.0014816422074375,40.0957403917498354,192.177050630060734],"luv":[83.0014816422074375,-39.1936046909777,-8.4575261684472629],"rgb":[0.533333333333333326,0.866666666666666696,0.866666666666666696],"xyz":[0.490581912240886764,0.621657410473101191,0.778230817974706834],"hpluv":[192.177050630060734,100.308679558078737,83.0014816422074375],"hsluv":[192.177050630060734,61.0588228331018783,83.0014816422074375]},"#88ddee":{"lch":[83.5045459788204,44.8677054153188,210.184175125347622],"luv":[83.5045459788204,-38.7842593009493157,-22.5586395802706683],"rgb":[0.533333333333333326,0.866666666666666696,0.933333333333333348],"xyz":[0.514394092507061584,0.631182282579571208,0.903641634043230058],"hpluv":[210.184175125347622,116.175045715242419,83.5045459788204],"hsluv":[210.184175125347622,62.7244468832174746,83.5045459788204]},"#88ddff":{"lch":[84.0516404633952732,53.2556266987753659,223.883407592331508],"luv":[84.0516404633952732,-38.3840933399078779,-36.9164347351224436],"rgb":[0.533333333333333326,0.866666666666666696,1],"xyz":[0.540565140916892894,0.641650701943503932,1.04147582233501179],"hpluv":[223.883407592331508,143.298172182841085,84.0516404633952732],"hsluv":[223.883407592331508,99.9999999999952,84.0516404633952732]},"#335500":{"lch":[32.2593993637483862,41.1235506245754365,113.326494368716226],"luv":[32.2593993637483862,-16.2836990313566261,37.7622504868051223],"rgb":[0.2,0.333333333333333315,0],"xyz":[0.0461356744276991415,0.0720065112172806193,0.0114678000018764836],"hpluv":[113.326494368716226,161.760937136611716,32.2593993637483862],"hsluv":[113.326494368716226,100.000000000002288,32.2593993637483862]},"#335511":{"lch":[32.3496341583576381,38.1897920123001313,115.090818108356473],"luv":[32.3496341583576381,-16.1945453684027605,34.5860797757347456],"rgb":[0.2,0.333333333333333315,0.0666666666666666657],"xyz":[0.0471473399273362598,0.0724111774171354722,0.016795904966632115],"hpluv":[115.090818108356473,149.801873896373706,32.3496341583576381],"hsluv":[115.090818108356473,90.8995446090399923,32.3496341583576381]},"#335522":{"lch":[32.5160201858231659,33.1206139518783189,118.966642902489184],"luv":[32.5160201858231659,-16.0403246718637718,28.9772851207727022],"rgb":[0.2,0.333333333333333315,0.133333333333333331],"xyz":[0.0490226980658132819,0.0731613206725262866,0.0266727911626113111],"hpluv":[118.966642902489184,129.252907346834121,32.5160201858231659],"hsluv":[118.966642902489184,74.935129302234273,32.5160201858231659]},"#335533":{"lch":[32.7875119073456176,25.8516304293262671,127.715012949238741],"luv":[32.7875119073456176,-15.8143300747053051,20.4502753072602665],"rgb":[0.2,0.333333333333333315,0.2],"xyz":[0.0521104487982710224,0.0743964209655094,0.0429349450202224578],"hpluv":[127.715012949238741,100.050394151032577,32.7875119073456176],"hsluv":[127.715012949238741,50.9434831873252207,32.7875119073456176]},"#335544":{"lch":[33.1742322541989836,18.1915220066791647,148.674883917516439],"luv":[33.1742322541989836,-15.539762262897943,9.45765624941535421],"rgb":[0.2,0.333333333333333315,0.266666666666666663],"xyz":[0.0565684440424899,0.0761796190631969794,0.0664137199731090894],"hpluv":[148.674883917516439,69.5836942134034899,33.1742322541989836],"hsluv":[148.674883917516439,54.5088767633859703,33.1742322541989836]},"#335555":{"lch":[33.6821363315134121,15.5994691341387064,192.177050630060819],"luv":[33.6821363315134121,-15.2484882598238034,-3.29044724269541522],"rgb":[0.2,0.333333333333333315,0.333333333333333315],"xyz":[0.0625308580818519,0.0785645846789418123,0.097815767247083],"hpluv":[192.177050630060819,58.7691644617976934,33.6821363315134121],"hsluv":[192.177050630060819,58.539124710901028,33.6821363315134121]},"#335566":{"lch":[34.3136156967701496,22.6822476529859181,228.692277460856218],"luv":[34.3136156967701496,-14.9726179113300351,-17.0383412183435361],"rgb":[0.2,0.333333333333333315,0.4],"xyz":[0.0701158538553600663,0.0815985829883451308,0.137763411654226953],"hpluv":[228.692277460856218,83.8800984368335,34.3136156967701496],"hsluv":[228.692277460856218,62.7386213411417089,34.3136156967701496]},"#335577":{"lch":[35.0679836745485218,34.4527831327976131,244.673076132176305],"luv":[35.0679836745485218,-14.7383029974912176,-31.1412377780608658],"rgb":[0.2,0.333333333333333315,0.466666666666666674],"xyz":[0.0794298668755619858,0.0853241881964259402,0.186817213560624862],"hpluv":[244.673076132176305,124.667382519216758,35.0679836745485218],"hsluv":[244.673076132176305,66.8604241763398335,35.0679836745485218]},"#335588":{"lch":[35.9419713942028523,47.4274747217606318,252.117898483801355],"luv":[35.9419713942028523,-14.5630489263951493,-45.1362710516793229],"rgb":[0.2,0.333333333333333315,0.533333333333333326],"xyz":[0.0905702839775058138,0.089780355037203538,0.245490076964197029],"hpluv":[252.117898483801355,167.443168522882189,35.9419713942028523],"hsluv":[252.117898483801355,70.7327730687023433,35.9419713942028523]},"#335599":{"lch":[36.9302538454711851,60.4953772811161272,256.174865065393647],"luv":[36.9302538454711851,-14.4559426241851021,-58.7427986669927833],"rgb":[0.2,0.333333333333333315,0.6],"xyz":[0.103627248086578527,0.0950031406808327,0.314256754605314936],"hpluv":[256.174865065393647,207.863972679723531,36.9302538454711851],"hsluv":[256.174865065393647,74.2581677802949258,36.9302538454711851]},"#3355aa":{"lch":[38.0259842211890557,73.2582460718320903,258.648348715518409],"luv":[38.0259842211890557,-14.4194259870406505,-71.8251402485603165],"rgb":[0.2,0.333333333333333315,0.66666666666666663],"xyz":[0.118684941051831069,0.101026217866933804,0.393560604222313537],"hpluv":[258.648348715518409,244.464262316607687,38.0259842211890557],"hsluv":[258.648348715518409,77.3978910060545,38.0259842211890557]},"#3355bb":{"lch":[39.2213032744107579,85.5750988898230815,260.277622363739169],"luv":[39.2213032744107579,-14.4514388960387397,-84.3460340729607623],"rgb":[0.2,0.333333333333333315,0.733333333333333282],"xyz":[0.135822533159167702,0.107881254709868563,0.483818589320955283],"hpluv":[260.277622363739169,276.862869377010384,39.2213032744107579],"hsluv":[260.277622363739169,80.1529964351826578,39.2213032744107579]},"#3355cc":{"lch":[40.5077939091134667,97.4185991778777,261.412040351160215],"luv":[40.5077939091134667,-14.5472816309580448,-96.326320717286734],"rgb":[0.2,0.333333333333333315,0.8],"xyz":[0.155114908602889456,0.115598204887357364,0.585425099991225495],"hpluv":[261.412040351160215,305.17054630009352,40.5077939091134667],"hsluv":[261.412040351160215,82.5478264076183734,40.5077939091134667]},"#3355dd":{"lch":[41.8768615158020552,108.813506268261335,262.235450095721887],"luv":[41.8768615158020552,-14.7009821832840935,-107.815862790406285],"rgb":[0.2,0.333333333333333315,0.866666666666666696],"xyz":[0.176633234153523,0.124205535107610898,0.698754947891231493],"hpluv":[262.235450095721887,329.72206067647727,41.8768615158020552],"hsluv":[262.235450095721887,84.6180359154754456,41.8768615158020552]},"#3355ee":{"lch":[43.3200322197542533,119.805986402952101,262.852770803428143],"luv":[43.3200322197542533,-14.9061911990543372,-118.875059797762361],"rgb":[0.2,0.333333333333333315,0.933333333333333348],"xyz":[0.200445414419697737,0.133730407214080915,0.824165763959754716],"hpluv":[262.852770803428143,350.936931775297865,43.3200322197542533],"hsluv":[262.852770803428143,90.8055999978327577,43.3200322197542533]},"#3355ff":{"lch":[44.8291710285026497,130.447860525532377,263.327743444412704],"luv":[44.8291710285026497,-15.1567128937849134,-129.564340657235334],"rgb":[0.2,0.333333333333333315,1],"xyz":[0.226616462829529131,0.144198826578013639,0.961999952251536561],"hpluv":[263.327743444412704,369.245812735261836,44.8291710285026497],"hsluv":[263.327743444412704,99.9999999999993,44.8291710285026497]},"#aadd00":{"lch":[81.9783608763648175,97.3184216433040916,105.014728605041086],"luv":[81.9783608763648175,-25.2120246284188454,93.9958988747909245],"rgb":[0.66666666666666663,0.866666666666666696,0],"xyz":[0.424325128525832707,0.602582536713324846,0.0939549564608566229],"hpluv":[105.014728605041086,227.603505169437568,81.9783608763648175],"hsluv":[105.014728605041086,100.000000000002203,81.9783608763648175]},"#aadd11":{"lch":[82.0002885274849262,96.374791846509865,105.167446282879951],"luv":[82.0002885274849262,-25.2155818179290208,93.017605521973266],"rgb":[0.66666666666666663,0.866666666666666696,0.0666666666666666657],"xyz":[0.425336794025469811,0.602987202913179754,0.0992830614256122596],"hpluv":[105.167446282879951,225.71449396257529,82.0002885274849262],"hsluv":[105.167446282879951,98.8971907906987298,82.0002885274849262]},"#aadd22":{"lch":[82.0409106120811,94.6380623122949345,105.456830413527484],"luv":[82.0409106120811,-25.2222030700150874,91.2151484706391216],"rgb":[0.66666666666666663,0.866666666666666696,0.133333333333333331],"xyz":[0.427212152163946868,0.603737346168570554,0.109159947621591452],"hpluv":[105.456830413527484,222.227322886837186,82.0409106120811],"hsluv":[105.456830413527484,96.86659260915998,82.0409106120811]},"#aadd33":{"lch":[82.1077210543303693,91.8137349028061891,105.951891130476156],"luv":[82.1077210543303693,-25.2331804702422886,88.2782448860360773],"rgb":[0.66666666666666663,0.866666666666666696,0.2],"xyz":[0.430299902896404574,0.604972446461553681,0.125422101479202602],"hpluv":[105.951891130476156,216.526835376233663,82.1077210543303693],"hsluv":[105.951891130476156,93.5615293661406469,82.1077210543303693]},"#aadd44":{"lch":[82.204019596801416,87.812555976334508,106.710442618715334],"luv":[82.204019596801416,-25.2491908313752447,84.1042409718896238],"rgb":[0.66666666666666663,0.866666666666666696,0.266666666666666663],"xyz":[0.434757898140623489,0.606755644559241247,0.148900876432089241],"hpluv":[106.710442618715334,208.386709262610594,82.204019596801416],"hsluv":[106.710442618715334,88.8720213109455415,82.204019596801416]},"#aadd55":{"lch":[82.332521082647844,82.6008871447080537,107.814707884186447],"luv":[82.332521082647844,-25.2708912490316564,78.6402480449573],"rgb":[0.66666666666666663,0.866666666666666696,0.333333333333333315],"xyz":[0.440720312179985474,0.609140610174986,0.180302923706063156],"hpluv":[107.814707884186447,197.666387369374576,82.332521082647844],"hsluv":[107.814707884186447,82.7472331150865,82.332521082647844]},"#aadd66":{"lch":[82.495508489392364,76.2000100439918526,109.390646830849064],"luv":[82.495508489392364,-25.2989483474785786,71.8777068583581666],"rgb":[0.66666666666666663,0.866666666666666696,0.4],"xyz":[0.448305307953493659,0.612174608484389315,0.22025056811320709],"hpluv":[109.390646830849064,184.308617744019813,82.495508489392364],"hsluv":[109.390646830849064,75.1895211365387723,82.495508489392364]},"#aadd77":{"lch":[82.694914273715753,68.6905071379487,111.642517734809914],"luv":[82.694914273715753,-25.3340491820501299,63.8480361711309499],"rgb":[0.66666666666666663,0.866666666666666696,0.466666666666666674],"xyz":[0.457619320973695565,0.615900213692470166,0.269304370019605],"hpluv":[111.642517734809914,168.351618208890358,82.694914273715753],"hsluv":[111.642517734809914,66.2494286969421182,82.694914273715753]},"#aadd88":{"lch":[82.9323686283524921,60.2252781448786436,114.920875432447886],"luv":[82.9323686283524921,-25.3769003534370228,54.6177357282387277],"rgb":[0.66666666666666663,0.866666666666666696,0.533333333333333326],"xyz":[0.468759738075639365,0.620356380533247709,0.327977233423177195],"hpluv":[114.920875432447886,149.967217229555729,82.9323686283524921],"hsluv":[114.920875432447886,56.0198042099318698,82.9323686283524921]},"#aadd99":{"lch":[83.2092305924335,51.0640475908324305,119.865599423026282],"luv":[83.2092305924335,-25.4282188712646082,44.2825320120006438],"rgb":[0.66666666666666663,0.866666666666666696,0.6],"xyz":[0.481816702184712065,0.625579166176876922,0.396743911064295074],"hpluv":[119.865599423026282,129.562195810811914,83.2092305924335],"hsluv":[119.865599423026282,44.6286172342680487,83.2092305924335]},"#aaddaa":{"lch":[83.526609727912188,41.6663179647472717,127.715012949236481],"luv":[83.526609727912188,-25.4887175141036408,32.9606937461220255],"rgb":[0.66666666666666663,0.866666666666666696,0.66666666666666663],"xyz":[0.496874395149964621,0.631602243362978,0.476047760681293675],"hpluv":[127.715012949236481,108.050849780995421,83.526609727912188],"hsluv":[127.715012949236481,32.2306770176982198,83.526609727912188]},"#aaddbb":{"lch":[83.8853825066367449,32.9442078204095523,140.880295511025736],"luv":[83.8853825066367449,-25.5590871805440472,20.7859060762739638],"rgb":[0.66666666666666663,0.866666666666666696,0.733333333333333282],"xyz":[0.514011987257301239,0.638457280205912747,0.566305745779935421],"hpluv":[140.880295511025736,87.605014056209555,83.8853825066367449],"hsluv":[140.880295511025736,34.4766875289621382,83.8853825066367449]},"#aaddcc":{"lch":[84.2862057978904602,26.8294893367150316,162.87488866986746],"luv":[84.2862057978904602,-25.639978502468459,7.90018990036700242],"rgb":[0.66666666666666663,0.866666666666666696,0.8],"xyz":[0.533304362701023,0.646174230383401604,0.667912256450205577],"hpluv":[162.87488866986746,73.4171355960865526,84.2862057978904602],"hsluv":[162.87488866986746,36.8333761539634708,84.2862057978904602]},"#aadddd":{"lch":[84.729528894444158,26.3242685722419552,192.177050630060421],"luv":[84.729528894444158,-25.7319846477226655,-5.55266440124834],"rgb":[0.66666666666666663,0.866666666666666696,0.866666666666666696],"xyz":[0.554822688251656593,0.654781560603655111,0.781242104350211575],"hpluv":[192.177050630060421,74.4077864529294146,84.729528894444158],"hsluv":[192.177050630060421,39.269697519551606,84.729528894444158]},"#aaddee":{"lch":[85.2156049558348769,32.3281965376105802,216.94937298104162],"luv":[85.2156049558348769,-25.8356267715816657,-19.4327733659894548],"rgb":[0.66666666666666663,0.866666666666666696,0.933333333333333348],"xyz":[0.578634868517831302,0.664306432710125128,0.906652920418734798],"hpluv":[216.94937298104162,94.7745998726022094,85.2156049558348769],"hsluv":[216.94937298104162,58.4216362813771397,85.2156049558348769]},"#aaddff":{"lch":[85.744502396509489,42.4619511969095527,232.326067904807843],"luv":[85.744502396509489,-25.9513430438233641,-33.608705623253762],"rgb":[0.66666666666666663,0.866666666666666696,1],"xyz":[0.604805916927662723,0.674774852074057852,1.04448710871051675],"hpluv":[232.326067904807843,129.682154580054771,85.744502396509489],"hsluv":[232.326067904807843,99.9999999999945572,85.744502396509489]},"#88ee00":{"lch":[85.1906878331824515,112.448241602139106,116.535675589642423],"luv":[85.1906878331824515,-50.2368100475369914,100.602534538950536],"rgb":[0.533333333333333326,0.933333333333333348,0],"xyz":[0.40726312885557775,0.663815939263801891,0.106669928865523039],"hpluv":[116.535675589642423,329.03324103323672,85.1906878331824515],"hsluv":[116.535675589642423,100.000000000002416,85.1906878331824515]},"#88ee11":{"lch":[85.2112458075113182,111.594873497969587,116.730132104203335],"luv":[85.2112458075113182,-50.1941202672300193,99.669283541253],"rgb":[0.533333333333333326,0.933333333333333348,0.0666666666666666657],"xyz":[0.408274794355214854,0.664220605463656799,0.111998033830278676],"hpluv":[116.730132104203335,327.047318494663614,85.2112458075113182],"hsluv":[116.730132104203335,98.9939616796445,85.2112458075113182]},"#88ee22":{"lch":[85.2493327369145,110.025246585506409,117.096539077460619],"luv":[85.2493327369145,-50.115524286233132,97.9489107224652429],"rgb":[0.533333333333333326,0.933333333333333348,0.133333333333333331],"xyz":[0.410150152493691911,0.6649707487190476,0.121874920026257869],"hpluv":[117.096539077460619,323.384561507736407,85.2493327369145],"hsluv":[117.096539077460619,97.140453749143191,85.2493327369145]},"#88ee33":{"lch":[85.3119799721786,107.475578977757436,117.717135468026783],"luv":[85.3119799721786,-49.9876247553973769,95.1432469906181097],"rgb":[0.533333333333333326,0.933333333333333348,0.2],"xyz":[0.413237903226149617,0.666205849012030726,0.138137073883869],"hpluv":[117.717135468026783,317.406918528502,85.3119799721786],"hsluv":[117.717135468026783,94.120593299189423,85.3119799721786]},"#88ee44":{"lch":[85.4022915890189864,103.870397610997969,118.652974854946393],"luv":[85.4022915890189864,-49.8062112723248518,91.1504296126099],"rgb":[0.533333333333333326,0.933333333333333348,0.266666666666666663],"xyz":[0.417695898470368532,0.667989047109718292,0.161615848836755643],"hpluv":[118.652974854946393,308.894438519259097,85.4022915890189864],"hsluv":[118.652974854946393,89.8292917088674869,85.4022915890189864]},"#88ee55":{"lch":[85.5228293578601466,99.1885702075232,119.98340282305692],"luv":[85.5228293578601466,-49.5693999595221584,85.9141842041562],"rgb":[0.533333333333333326,0.933333333333333348,0.333333333333333315],"xyz":[0.423658312509730517,0.670374012725463,0.193017896110729559],"hpluv":[119.98340282305692,297.731707367046795,85.5228293578601466],"hsluv":[119.98340282305692,84.2131356884780473,85.5228293578601466]},"#88ee66":{"lch":[85.6757572108422,93.4644574192068234,121.818598205465591],"luv":[85.6757572108422,-49.2774194314266083,79.4187681524083189],"rgb":[0.533333333333333326,0.933333333333333348,0.4],"xyz":[0.431243308283238702,0.673408011034866361,0.232965540517873521],"hpluv":[121.818598205465591,283.912655540997662,85.6757572108422],"hsluv":[121.818598205465591,77.2651144072775082,85.6757572108422]},"#88ee77":{"lch":[85.8629182835971676,86.7937408847250822,124.317503927832064],"luv":[85.8629182835971676,-48.9324360738715,71.6852157466327071],"rgb":[0.533333333333333326,0.933333333333333348,0.466666666666666674],"xyz":[0.440557321303440608,0.677133616242947212,0.282019342424271402],"hpluv":[124.317503927832064,267.563092583833793,85.8629182835971676],"hsluv":[124.317503927832064,69.0205357789205749,85.8629182835971676]},"#88ee88":{"lch":[86.0858807036169793,79.3454652904755164,127.71501294923921],"luv":[86.0858807036169793,-48.5383458294846,62.7672832477191918],"rgb":[0.533333333333333326,0.933333333333333348,0.533333333333333326],"xyz":[0.451697738405384408,0.681589783083724754,0.340692205827843597],"hpluv":[127.71501294923921,248.989760930573482,86.0858807036169793],"hsluv":[127.71501294923921,59.5524101967812,86.0858807036169793]},"#88ee99":{"lch":[86.3459670558122525,71.3853284581525429,132.362204285947513],"luv":[86.3459670558122525,-48.1005130697284287,52.7466184841000825],"rgb":[0.533333333333333326,0.933333333333333348,0.6],"xyz":[0.464754702514457163,0.686812568727354,0.409458883468961532],"hpluv":[132.362204285947513,228.778132650246619,86.3459670558122525],"hsluv":[132.362204285947513,60.5562348819982645,86.3459670558122525]},"#88eeaa":{"lch":[86.6442747471204768,63.319361027400987,138.776693085718904],"luv":[86.6442747471204768,-47.625461320552418,41.7271723811105204],"rgb":[0.533333333333333326,0.933333333333333348,0.66666666666666663],"xyz":[0.479812395479709664,0.692835645913455,0.488762733085960077],"hpluv":[138.776693085718904,207.981038552319745,86.6442747471204768],"hsluv":[138.776693085718904,61.6537348625427342,86.6442747471204768]},"#88eebb":{"lch":[86.9816911545789679,55.7684583245382299,147.664608842749317],"luv":[86.9816911545789679,-47.1205330954276,29.8291184029711189],"rgb":[0.533333333333333326,0.933333333333333348,0.733333333333333282],"xyz":[0.496949987587046338,0.699690682756389792,0.579020718184601879],"hpluv":[147.664608842749317,188.458500832318975,86.9816911545789679],"hsluv":[147.664608842749317,62.830800627143077,86.9816911545789679]},"#88eecc":{"lch":[87.3589058048410294,49.6608679924305463,159.75715297219557],"luv":[87.3589058048410294,-46.5935415809549696,17.182656741768227],"rgb":[0.533333333333333326,0.933333333333333348,0.8],"xyz":[0.516242363030768092,0.70740763293387865,0.680627228854872],"hpluv":[159.75715297219557,173.371642714007834,87.3589058048410294],"hsluv":[159.75715297219557,64.0722902507549179,87.3589058048410294]},"#88eedd":{"lch":[87.7764209496729,46.2191498548509543,175.132102674355139],"luv":[87.7764209496729,-46.0524373199716166,3.92209512827700335],"rgb":[0.533333333333333326,0.933333333333333348,0.866666666666666696],"xyz":[0.53776068858140158,0.716014963154132156,0.793957076754878],"hpluv":[175.132102674355139,167.447502067072264,87.7764209496729],"hsluv":[175.132102674355139,65.3626972838926434,87.7764209496729]},"#88eeee":{"lch":[88.2345613859691866,46.5524177333431339,192.177050630060933],"luv":[88.2345613859691866,-45.5050097647113958,-9.8194543195231],"rgb":[0.533333333333333326,0.933333333333333348,0.933333333333333348],"xyz":[0.56157286884757629,0.725539835260602173,0.919367892823401256],"hpluv":[192.177050630060933,175.887543813927181,88.2345613859691866],"hsluv":[192.177050630060933,66.6867488369664443,88.2345613859691866]},"#88eeff":{"lch":[88.7334840469836763,50.9233073192915242,208.009521596121829],"luv":[88.7334840469836763,-44.9586380478967271,-23.9145163700465169],"rgb":[0.533333333333333326,0.933333333333333348,1],"xyz":[0.587743917257407711,0.736008254624534897,1.05720208111518299],"hpluv":[208.009521596121829,201.749007974481685,88.7334840469836763],"hsluv":[208.009521596121829,99.9999999999925109,88.7334840469836763]},"#336600":{"lch":[38.2101034680229574,51.4017776291135888,118.130952889189317],"luv":[38.2101034680229574,-24.2353400261634,45.3298029694491902],"rgb":[0.2,0.4,0],"xyz":[0.0611637321335456105,0.10206262662897396,0.0164771525704918292],"hpluv":[118.130952889189317,170.702252266418213,38.2101034680229574],"hsluv":[118.130952889189317,100.000000000002288,38.2101034680229574]},"#336611":{"lch":[38.2816545292110959,48.9828222693822468,119.470954908934345],"luv":[38.2816545292110959,-24.0986808399199681,42.6447002480913682],"rgb":[0.2,0.4,0.0666666666666666657],"xyz":[0.0621753976331827357,0.102467292828828813,0.021805257535247459],"hpluv":[119.470954908934345,162.365005406289157,38.2816545292110959],"hsluv":[119.470954908934345,93.5286374368429,38.2816545292110959]},"#336622":{"lch":[38.4137944304132617,44.7307250918699282,122.230818066602779],"luv":[38.4137944304132617,-23.8562978467226365,37.8380076152656173],"rgb":[0.2,0.4,0.133333333333333331],"xyz":[0.0640507557716597509,0.103217436084219627,0.0316821437312266585],"hpluv":[122.230818066602779,147.760399550800031,38.4137944304132617],"hsluv":[122.230818066602779,81.994947572555219,38.4137944304132617]},"#336633":{"lch":[38.6299730126545171,38.3921679396875817,127.715012949239437],"luv":[38.6299730126545171,-23.4858075099586969,30.3706339201924216],"rgb":[0.2,0.4,0.2],"xyz":[0.0671385065041174844,0.10445253637720274,0.0479442975888378],"hpluv":[127.715012949239437,126.112332565807833,38.6299730126545171],"hsluv":[127.715012949239437,64.2136550115152,38.6299730126545171]},"#336644":{"lch":[38.9390987599147635,30.7877900918170191,138.353415806708398],"luv":[38.9390987599147635,-23.0064236741332095,20.4595329483365589],"rgb":[0.2,0.4,0.266666666666666663],"xyz":[0.0715965017483363719,0.10623573447489032,0.0714230725417244472],"hpluv":[138.353415806708398,100.330262339301328,38.9390987599147635],"hsluv":[138.353415806708398,66.1490613915476899,38.9390987599147635]},"#336655":{"lch":[39.3471830293532108,24.0314899024670439,159.111050143656939],"luv":[39.3471830293532108,-22.4519783641565418,8.56861566811244479],"rgb":[0.2,0.4,0.333333333333333315],"xyz":[0.077558915787698357,0.108620700090635153,0.102825119815698349],"hpluv":[159.111050143656939,77.5008343503898089,39.3471830293532108],"hsluv":[159.111050143656939,68.4324421599544337,39.3471830293532108]},"#336666":{"lch":[39.8577781510875653,22.3660784387423632,192.177050630061],"luv":[39.8577781510875653,-21.8628519700770774,-4.71775036033837925],"rgb":[0.2,0.4,0.4],"xyz":[0.0851439115612065422,0.111654698400038471,0.142772764222842297],"hpluv":[192.177050630061,71.205917149352544,39.8577781510875653],"hsluv":[192.177050630061,70.9271962998489727,39.8577781510875653]},"#336677":{"lch":[40.4722660639059,28.403364995264031,221.483563289625266],"luv":[40.4722660639059,-21.2782609928981294,-18.8145356618838697],"rgb":[0.2,0.4,0.466666666666666674],"xyz":[0.0944579245814084478,0.115380303608119281,0.191826566129240206],"hpluv":[221.483563289625266,89.0536244466218534,40.4722660639059],"hsluv":[221.483563289625266,73.4989606489913,40.4722660639059]},"#336688":{"lch":[41.1901179582142731,39.1584560294521253,238.034471274574031],"luv":[41.1901179582142731,-20.7308371309926542,-33.2207325394668516],"rgb":[0.2,0.4,0.533333333333333326],"xyz":[0.105598341683352276,0.119836470448896878,0.250499429532812401],"hpluv":[238.034471274574031,120.634589470818128,41.1901179582142731],"hsluv":[238.034471274574031,76.0346320246428746,41.1901179582142731]},"#336699":{"lch":[42.0091634944821948,51.6886413393569626,246.942440261812],"luv":[42.0091634944821948,-20.2441498032976277,-47.5593318103852951],"rgb":[0.2,0.4,0.6],"xyz":[0.118655305792425,0.12505925609252605,0.31926610717393028],"hpluv":[246.942440261812,156.131455895945,42.0091634944821948],"hsluv":[246.942440261812,78.4511744564818514,42.0091634944821948]},"#3366aa":{"lch":[42.9258754740709847,64.6951641030825,252.148217293940775],"luv":[42.9258754740709847,-19.832670948043944,-61.5802681172418218],"rgb":[0.2,0.4,0.66666666666666663],"xyz":[0.133712998757677531,0.131082333278627144,0.398569956790928881],"hpluv":[252.148217293940775,191.245834212653307,42.9258754740709847],"hsluv":[252.148217293940775,80.6959595489171,42.9258754740709847]},"#3366bb":{"lch":[43.9356615222631106,77.6311657782158164,255.449757662516049],"luv":[43.9356615222631106,-19.5031901130623,-75.1413566253536374],"rgb":[0.2,0.4,0.733333333333333282],"xyz":[0.150850590865014178,0.137937370121561903,0.488827941889570627],"hpluv":[255.449757662516049,224.211696450950683,43.9356615222631106],"hsluv":[255.449757662516049,82.7420790911240402,43.9356615222631106]},"#3366cc":{"lch":[45.0331492258045287,90.2591849706621332,257.681278432902445],"luv":[45.0331492258045287,-19.2567634880452658,-88.1810497302775786],"rgb":[0.2,0.4,0.8],"xyz":[0.170142966308735932,0.145654320299050705,0.590434452559840839],"hpluv":[257.681278432902445,254.330482568364204,45.0331492258045287],"hsluv":[257.681278432902445,84.5818015821218694,45.0331492258045287]},"#3366dd":{"lch":[46.2124513047425367,102.485732700579547,259.264483160570819],"luv":[46.2124513047425367,-19.0906003056708045,-100.691977769551045],"rgb":[0.2,0.4,0.866666666666666696],"xyz":[0.191661291859369476,0.154261650519304239,0.703764300459846837],"hpluv":[259.264483160570819,281.412724566330553,46.2124513047425367],"hsluv":[259.264483160570819,86.2202337385041488,46.2124513047425367]},"#3366ee":{"lch":[47.467400384741687,114.289765210034219,260.43068521754094],"luv":[47.467400384741687,-18.99960653492559,-112.699447129445844],"rgb":[0.2,0.4,0.933333333333333348],"xyz":[0.215473472125544213,0.163786522625774256,0.82917511652837006],"hpluv":[260.43068521754094,305.528142270574733,47.467400384741687],"hsluv":[260.43068521754094,89.4216395109538524,47.467400384741687]},"#3366ff":{"lch":[48.7917470574018068,125.686826272807437,261.315666926990161],"luv":[48.7917470574018068,-18.9775194272026404,-124.245853270525785],"rgb":[0.2,0.4,1],"xyz":[0.241644520535375606,0.17425494198970698,0.967009304820151905],"hpluv":[261.315666926990161,326.875761207371056,48.7917470574018068],"hsluv":[261.315666926990161,99.9999999999992184,48.7917470574018068]},"#aaee00":{"lch":[86.8465682321076713,106.130019137569278,108.773889799394041],"luv":[86.8465682321076713,-34.1562767274136,100.483479847491182],"rgb":[0.66666666666666663,0.933333333333333348,0],"xyz":[0.471503904866347523,0.696940089394355811,0.109681215241027807],"hpluv":[108.773889799394041,354.560190530248747,86.8465682321076713],"hsluv":[108.773889799394041,100.000000000002302,86.8465682321076713]},"#aaee11":{"lch":[86.8664697406872364,105.275413073012288,108.926122010215039],"luv":[86.8664697406872364,-34.1459453835818323,99.5839696515197801],"rgb":[0.66666666666666663,0.933333333333333348,0.0666666666666666657],"xyz":[0.472515570365984627,0.697344755594210719,0.115009320205783444],"hpluv":[108.926122010215039,352.296735211520456,86.8664697406872364],"hsluv":[108.926122010215039,99.0448212343627,86.8664697406872364]},"#aaee22":{"lch":[86.9033414770782286,103.701584155213482,109.21343759479339],"luv":[86.9033414770782286,-34.1269596340241961,97.9253245204656224],"rgb":[0.66666666666666663,0.933333333333333348,0.133333333333333331],"xyz":[0.474390928504461684,0.69809489884960152,0.124886206401762637],"hpluv":[109.21343759479339,348.114419217609168,86.9033414770782286],"hsluv":[109.21343759479339,97.2844700630890742,86.9033414770782286]},"#aaee33":{"lch":[86.9639927659971,101.139559122436452,109.701502859778287],"luv":[86.9639927659971,-34.0961634116812462,95.2190215245081504],"rgb":[0.66666666666666663,0.933333333333333348,0.2],"xyz":[0.47747867923691939,0.699329999142584646,0.141148360259373773],"hpluv":[109.701502859778287,341.266668172598315,86.9639927659971],"hsluv":[109.701502859778287,94.4148698146506,86.9639927659971]},"#aaee44":{"lch":[87.0514332975086091,97.5044422856038864,110.440992284795044],"luv":[87.0514332975086091,-34.052698770047833,91.3648180204122298],"rgb":[0.66666666666666663,0.933333333333333348,0.266666666666666663],"xyz":[0.481936674481138305,0.701113197240272212,0.164627135212260411],"hpluv":[110.440992284795044,331.464832239423686,87.0514332975086091],"hsluv":[110.440992284795044,90.3338544995677353,87.0514332975086091]},"#aaee55":{"lch":[87.1681505263978,92.7597711807761556,111.499860176168639],"luv":[87.1681505263978,-33.9963593100021271,86.3054036730909644],"rgb":[0.66666666666666663,0.933333333333333348,0.333333333333333315],"xyz":[0.48789908852050029,0.703498162856017,0.196029182486234327],"hpluv":[111.499860176168639,318.513853400728863,87.1681505263978],"hsluv":[111.499860176168639,84.9871568734489813,87.1681505263978]},"#aaee66":{"lch":[87.3162499846890086,86.9168013961255923,112.975973936880195],"luv":[87.3162499846890086,-33.9275471197994563,80.0215715377255492],"rgb":[0.66666666666666663,0.933333333333333348,0.4],"xyz":[0.495484084294008476,0.70653216116542028,0.235976826893378289],"hpluv":[112.975973936880195,302.308385752370668,87.3162499846890086],"hsluv":[112.975973936880195,78.3634586897027106,87.3162499846890086]},"#aaee77":{"lch":[87.4975302296396,80.0378594698754853,115.017197938190947],"luv":[87.4975302296396,-33.8472328521792534,72.5287789554594298],"rgb":[0.66666666666666663,0.933333333333333348,0.466666666666666674],"xyz":[0.504798097314210326,0.710257766373501132,0.285030628799776198],"hpluv":[115.017197938190947,282.845349503141222,87.4975302296396],"hsluv":[115.017197938190947,70.4907283307095,87.4975302296396]},"#aaee88":{"lch":[87.7135274362348838,72.2451165326571072,117.856267937731204],"luv":[87.7135274362348838,-33.7569011423620324,63.8735351149597861],"rgb":[0.66666666666666663,0.933333333333333348,0.533333333333333326],"xyz":[0.515938514416154237,0.714713933214278674,0.343703492203348393],"hpluv":[117.856267937731204,260.261444274219798,87.7135274362348838],"hsluv":[117.856267937731204,61.4321883079204412,87.7135274362348838]},"#aaee99":{"lch":[87.9655440812905312,63.7405265694191954,121.874104620002555],"luv":[87.9655440812905312,-33.6584769753751445,54.1291202131068871],"rgb":[0.66666666666666663,0.933333333333333348,0.6],"xyz":[0.528995478525226881,0.719936718857907887,0.412470169844466272],"hpluv":[121.874104620002555,234.922523447188695,87.9655440812905312],"hsluv":[121.874104620002555,51.2814060811362182,87.9655440812905312]},"#aaeeaa":{"lch":[88.2546687059401,54.8509913255605497,127.715012949237604],"luv":[88.2546687059401,-33.5542349686064441,43.3906045713616138],"rgb":[0.66666666666666663,0.933333333333333348,0.66666666666666663],"xyz":[0.544053171490479492,0.725959796044009,0.491774019461464873],"hpluv":[127.715012949237604,207.631017102058365,88.2546687059401],"hsluv":[127.715012949237604,42.8122115000723795,88.2546687059401]},"#aaeebb":{"lch":[88.5817905154621457,46.1299748711590425,136.47329364537066],"luv":[88.5817905154621457,-33.4466969972147226,31.7693726974624511],"rgb":[0.66666666666666663,0.933333333333333348,0.733333333333333282],"xyz":[0.561190763597816056,0.732814832886943712,0.582032004560106619],"hpluv":[136.47329364537066,180.106084736741337,88.5817905154621457],"hsluv":[136.47329364537066,42.5306558804052,88.5817905154621457]},"#aaeecc":{"lch":[88.947610994722524,38.5659339652702471,149.820563383504265],"luv":[88.947610994722524,-33.3385252257337896,19.3874701388989088],"rgb":[0.66666666666666663,0.933333333333333348,0.8],"xyz":[0.58048313904153781,0.740531783064432569,0.683638515230376775],"hpluv":[149.820563383504265,156.025185046421541,88.947610994722524],"hsluv":[149.820563383504265,43.7743007306855176,88.947610994722524]},"#aaeedd":{"lch":[89.3526538659385636,33.8378115459028308,169.14561851222],"luv":[89.3526538659385636,-33.2324178366612202,6.37211856022751721],"rgb":[0.66666666666666663,0.933333333333333348,0.866666666666666696],"xyz":[0.602001464592171409,0.749139113284686076,0.796968363130382773],"hpluv":[169.14561851222,142.575756051086472,89.3526538659385636],"hsluv":[169.14561851222,45.7128350403065866,89.3526538659385636]},"#aaeeee":{"lch":[89.797274219494,33.893604395025335,192.177050630060734],"luv":[89.797274219494,-33.1310138990311174,-7.14928925898844092],"rgb":[0.66666666666666663,0.933333333333333348,0.933333333333333348],"xyz":[0.625813644858346119,0.758663985391156093,0.922379179198906],"hpluv":[192.177050630060734,149.574468983420843,89.797274219494],"hsluv":[192.177050630060734,47.7079369778472326,89.797274219494]},"#aaeeff":{"lch":[90.2816673401601406,39.1749897548907384,212.508271358442045],"luv":[90.2816673401601406,-33.0368122621910558,-21.0534761464356919],"rgb":[0.66666666666666663,0.933333333333333348,1],"xyz":[0.65198469326817754,0.769132404755088817,1.06021336749068773],"hpluv":[212.508271358442045,182.211685385120148,90.2816673401601406],"hsluv":[212.508271358442045,99.9999999999910898,90.2816673401601406]},"#88ff00":{"lch":[90.2073775103659727,121.530167505498795,118.25137340908573],"luv":[90.2073775103659727,-57.5251845782800331,107.053420090856534],"rgb":[0.533333333333333326,1,0],"xyz":[0.459115501285251582,0.767520684123151,0.12395405300874715],"hpluv":[118.25137340908573,560.639311859311,90.2073775103659727],"hsluv":[118.25137340908573,100.00000000000226,90.2073775103659727]},"#88ff11":{"lch":[90.2260397586701828,120.75410845816802,118.424372307304225],"luv":[90.2260397586701828,-57.4787561773275684,106.196738640291272],"rgb":[0.533333333333333326,1,0.0666666666666666657],"xyz":[0.460127166784888686,0.767925350323005906,0.129282157973502787],"hpluv":[118.424372307304225,558.207032378019221,90.2260397586701828],"hsluv":[118.424372307304225,99.9999999999909335,90.2260397586701828]},"#88ff22":{"lch":[90.260617257984066,119.32542599803422,118.749452026898098],"luv":[90.260617257984066,-57.393189210317729,104.616342518179295],"rgb":[0.533333333333333326,1,0.133333333333333331],"xyz":[0.462002524923365743,0.768675493578396707,0.139159044169481966],"hpluv":[118.749452026898098,553.71568515647823,90.260617257984066],"hsluv":[118.749452026898098,99.9999999999907772,90.260617257984066]},"#88ff33":{"lch":[90.3174996442954665,117.001153508636776,119.297411154505227],"luv":[90.3174996442954665,-57.253701051451273,102.035697862378711],"rgb":[0.533333333333333326,1,0.2],"xyz":[0.465090275655823449,0.769910593871379834,0.155421198027093116],"hpluv":[119.297411154505227,546.370637823869743,90.3174996442954665],"hsluv":[119.297411154505227,99.9999999999907914,90.3174996442954665]},"#88ff44":{"lch":[90.399517385893148,113.706822542904789,120.117552028257023],"luv":[90.399517385893148,-57.0553255590242614,98.3561452994036074],"rgb":[0.533333333333333326,1,0.266666666666666663],"xyz":[0.469548270900042364,0.7716937919690674,0.178899972979979754],"hpluv":[120.117552028257023,535.877566613280692,90.399517385893148],"hsluv":[120.117552028257023,99.9999999999907914,90.399517385893148]},"#88ff55":{"lch":[90.5090160073098389,109.413894688598035,121.271093020798972],"luv":[90.5090160073098389,-56.7954345723420317,93.518334901057969],"rgb":[0.533333333333333326,1,0.333333333333333315],"xyz":[0.475510684939404349,0.774078757584812149,0.21030202025395367],"hpluv":[121.271093020798972,522.05545338002139,90.5090160073098389],"hsluv":[121.271093020798972,99.9999999999906493,90.5090160073098389]},"#88ff66":{"lch":[90.6479884721694,104.139810410505333,122.839359042112989],"luv":[90.6479884721694,-56.4735097148607039,87.4976731851854908],"rgb":[0.533333333333333326,1,0.4],"xyz":[0.483095680712912534,0.777112755894215468,0.250249664661097659],"hpluv":[122.839359042112989,504.839052288882328,90.6479884721694],"hsluv":[122.839359042112989,99.9999999999905498,90.6479884721694]},"#88ff77":{"lch":[90.8181461195308515,97.9515013566305441,124.934550835890477],"luv":[90.8181461195308515,-56.0909812525741813,80.3012978733306],"rgb":[0.533333333333333326,1,0.466666666666666674],"xyz":[0.49240969373311444,0.780838361102296319,0.299303466567495513],"hpluv":[124.934550835890477,484.301391198376791,90.8181461195308515],"hsluv":[124.934550835890477,99.9999999999904,90.8181461195308515]},"#88ff88":{"lch":[91.0209609702079803,90.9725812653771,127.715012949239437],"luv":[91.0209609702079803,-55.6510519447396348,71.9650676337452779],"rgb":[0.533333333333333326,1,0.533333333333333326],"xyz":[0.503550110835058295,0.785294527943073861,0.357976329971067708],"hpluv":[127.715012949239437,460.703998076760797,91.0209609702079803],"hsluv":[127.715012949239437,99.9999999999902087,91.0209609702079803]},"#88ff99":{"lch":[91.2576929391802167,83.3964386150156116,131.406800448379954],"luv":[91.2576929391802167,-55.158478848678719,62.5500454417735057],"rgb":[0.533333333333333326,1,0.6],"xyz":[0.51660707494413094,0.790517313586703074,0.426743007612185643],"hpluv":[131.406800448379954,434.595507650037121,91.2576929391802167],"hsluv":[131.406800448379954,99.9999999999900524,91.2576929391802167]},"#88ffaa":{"lch":[91.5294084976530229,75.5094657025462226,136.33125230150921],"luv":[91.5294084976530229,-54.6193101996060264,52.1383770748881048],"rgb":[0.533333333333333326,1,0.66666666666666663],"xyz":[0.531664767909383551,0.796540390772804141,0.506046857229184188],"hpluv":[136.33125230150921,406.99849880976177,91.5294084976530229],"hsluv":[136.33125230150921,99.9999999999897824,91.5294084976530229]},"#88ffbb":{"lch":[91.8369943060547911,67.7301008509963225,142.928263991524119],"luv":[91.8369943060547911,-54.0405872892441,40.8286846067776423],"rgb":[0.533333333333333326,1,0.733333333333333282],"xyz":[0.548802360016720114,0.8033954276157389,0.596304842327826],"hpluv":[142.928263991524119,379.751065707187763,91.8369943060547911],"hsluv":[142.928263991524119,99.9999999999894413,91.8369943060547911]},"#88ffcc":{"lch":[92.1811678623774498,60.665065650155249,151.731515164157571],"luv":[92.1811678623774498,-53.4300279465168941,28.7312078404662543],"rgb":[0.533333333333333326,1,0.8],"xyz":[0.568094735460441869,0.811112377793227757,0.697911352998096146],"hpluv":[151.731515164157571,356.080713038752435,92.1811678623774498],"hsluv":[151.731515164157571,99.9999999999893134,92.1811678623774498]},"#88ffdd":{"lch":[92.5624864174544371,55.1561732277505712,163.177123134509742],"luv":[92.5624864174544371,-52.7957106082625316,15.9629692256241533],"rgb":[0.533333333333333326,1,0.866666666666666696],"xyz":[0.589613061011075468,0.819719708013481263,0.811241200898102144],"hpluv":[163.177123134509742,341.369587308077,92.5624864174544371],"hsluv":[163.177123134509742,99.9999999999887,92.5624864174544371]},"#88ffee":{"lch":[92.9813549493531752,52.2127257219241301,177.098205352907513],"luv":[92.9813549493531752,-52.1457771028834927,2.64322864112605771],"rgb":[0.533333333333333326,1,0.933333333333333348],"xyz":[0.613425241277250177,0.82924458011995128,0.936652016966625367],"hpluv":[177.098205352907513,343.566349205584061,92.9813549493531752],"hsluv":[177.098205352907513,99.9999999999882,92.9813549493531752]},"#88ffff":{"lch":[93.4380337051328524,52.6732939730945162,192.177050630061075],"luv":[93.4380337051328524,-51.4881691068088543,-11.1105508416409933],"rgb":[0.533333333333333326,1,1],"xyz":[0.639596289687081598,0.839712999483884,1.07448620525840721],"hpluv":[192.177050630061075,372.044084252862206,93.4380337051328524],"hsluv":[192.177050630061075,99.9999999999874802,93.4380337051328524]},"#337700":{"lch":[44.0848685544221084,61.4877933810524127,120.932619831412623],"luv":[44.0848685544221084,-31.6065510805688668,52.7425318283298168],"rgb":[0.2,0.466666666666666674,0],"xyz":[0.0796174701869632462,0.138970102735809731,0.0226283985882975332],"hpluv":[120.932619831412623,176.985906279588789,44.0848685544221084],"hsluv":[120.932619831412623,100.000000000002217,44.0848685544221084]},"#337711":{"lch":[44.1431322932100159,59.4532082216814146,121.943929432148849],"luv":[44.1431322932100159,-31.4560442613915434,50.449987584497805],"rgb":[0.2,0.466666666666666674,0.0666666666666666657],"xyz":[0.0806291356866003645,0.139374768935664584,0.0279565035530531664],"hpluv":[121.943929432148849,170.903703930819205,44.1431322932100159],"hsluv":[121.943929432148849,95.223210781581642,44.1431322932100159]},"#337722":{"lch":[44.2508401312458517,55.8308883755444327,123.957293362486936],"luv":[44.2508401312458517,-31.1857277137109214,46.3091619851707605],"rgb":[0.2,0.466666666666666674,0.133333333333333331],"xyz":[0.0825044938250774,0.140124912191055412,0.037833389749032359],"hpluv":[123.957293362486936,160.100373243526178,44.2508401312458517],"hsluv":[123.957293362486936,86.6219708852072,44.2508401312458517]},"#337733":{"lch":[44.4273451577554681,50.2894934834324943,127.715012949239792],"luv":[44.4273451577554681,-30.7638100974309978,39.782171171385329],"rgb":[0.2,0.466666666666666674,0.2],"xyz":[0.0855922445575351271,0.141360012484038511,0.0540955436066435091],"hpluv":[127.715012949239792,143.636966976451419,44.4273451577554681],"hsluv":[127.715012949239792,73.1368174441878409,44.4273451577554681]},"#337744":{"lch":[44.6803728315295743,43.2379901525315873,134.305487238163835],"luv":[44.6803728315295743,-30.2010367205865329,30.9422231494797764],"rgb":[0.2,0.466666666666666674,0.266666666666666663],"xyz":[0.090050239801754,0.143143210581726105,0.0775743185595301477],"hpluv":[134.305487238163835,122.797079037201513,44.6803728315295743],"hsluv":[134.305487238163835,74.2422920898123664,44.6803728315295743]},"#337755":{"lch":[45.0155248592042057,35.7001949681147,145.791078733963843],"luv":[45.0155248592042057,-29.5238129364495734,20.071083439987369],"rgb":[0.2,0.466666666666666674,0.333333333333333315],"xyz":[0.096012653841116,0.145528176197470938,0.108976365833504049],"hpluv":[145.791078733963843,100.634662922797503,45.0155248592042057],"hsluv":[145.791078733963843,75.5860232244433377,45.0155248592042057]},"#337766":{"lch":[45.436632811343884,29.7520619336037022,165.22669317203713],"luv":[45.436632811343884,-28.7685269489331041,7.58663604567268468],"rgb":[0.2,0.466666666666666674,0.4],"xyz":[0.103597649614624171,0.148562174506874228,0.148924010240648025],"hpluv":[165.22669317203713,83.0902864995861847,45.436632811343884],"hsluv":[165.22669317203713,77.1054195363406194,45.436632811343884]},"#337777":{"lch":[45.9459628200325696,28.6191137838588361,192.177050630061018],"luv":[45.9459628200325696,-27.9751968985082726,-6.03672363647301413],"rgb":[0.2,0.466666666666666674,0.466666666666666674],"xyz":[0.11291166263482609,0.152287779714955052,0.197977812147045934],"hpluv":[192.177050630061018,79.0402219352416324,45.9459628200325696],"hsluv":[192.177050630061018,78.7308353184449743,45.9459628200325696]},"#337788":{"lch":[46.5443737533918309,33.948531272854666,216.805091226728479],"luv":[46.5443737533918309,-27.1818468531358306,-20.3383867904182836],"rgb":[0.2,0.466666666666666674,0.533333333333333326],"xyz":[0.124052079736769919,0.156743946555732649,0.256650675550618101],"hpluv":[216.805091226728479,92.5535593562344587,46.5443737533918309],"hsluv":[216.805091226728479,80.3955897349876381,46.5443737533918309]},"#337799":{"lch":[47.2314677007312085,43.7891028043947799,232.889035089769209],"luv":[47.2314677007312085,-26.4206199262946413,-34.9204290798973105],"rgb":[0.2,0.466666666666666674,0.6],"xyz":[0.137109043845842632,0.161966732199361807,0.325417353191736],"hpluv":[232.889035089769209,117.645124104106614,47.2314677007312085],"hsluv":[232.889035089769209,82.0429045074046144,47.2314677007312085]},"#3377aa":{"lch":[48.0057466772197472,55.756363166772573,242.534195481556935],"luv":[48.0057466772197472,-25.7159019754829359,-49.471854818395343],"rgb":[0.2,0.466666666666666674,0.66666666666666663],"xyz":[0.15216673681109516,0.167989809385462902,0.404721202808734581],"hpluv":[242.534195481556935,147.380673097935755,48.0057466772197472],"hsluv":[242.534195481556935,83.6292827510673078,48.0057466772197472]},"#3377bb":{"lch":[48.8647777502949623,68.5281348418467076,248.528316013511841],"luv":[48.8647777502949623,-25.0841318464231762,-63.7721851155626],"rgb":[0.2,0.466666666666666674,0.733333333333333282],"xyz":[0.169304328918431835,0.17484484622839766,0.494979187907376328],"hpluv":[248.528316013511841,177.955866809146045,48.8647777502949623],"hsluv":[248.528316013511841,85.1249045257873,48.8647777502949623]},"#3377cc":{"lch":[49.8053630390326845,81.4640551302433096,252.471981644912631],"luv":[49.8053630390326845,-24.5347040681135518,-77.6816617648812553],"rgb":[0.2,0.466666666666666674,0.8],"xyz":[0.188596704362153561,0.182561796405886462,0.596585698577646539],"hpluv":[252.471981644912631,207.553107999557341,49.8053630390326845],"hsluv":[252.471981644912631,86.5120902950020678,49.8053630390326845]},"#3377dd":{"lch":[50.8237086019957047,94.2508792352234,255.202895611269554],"luv":[50.8237086019957047,-24.0713820337122506,-91.1251710758325],"rgb":[0.2,0.466666666666666674,0.866666666666666696],"xyz":[0.210115029912787105,0.19116912662614,0.709915546477652537],"hpluv":[255.202895611269554,235.319753343529385,50.8237086019957047],"hsluv":[255.202895611269554,87.7828608732328,50.8237086019957047]},"#3377ee":{"lch":[51.9155858415672498,106.73807185779232,257.174602046687482],"luv":[51.9155858415672498,-23.6937959642247655,-104.075069141100855],"rgb":[0.2,0.466666666666666674,0.933333333333333348],"xyz":[0.233927210178961842,0.200693998732610041,0.83532636254617576],"hpluv":[257.174602046687482,260.892095272406607,51.9155858415672498],"hsluv":[257.174602046687482,88.9363454602875692,51.9155858415672498]},"#3377ff":{"lch":[53.0764799083082721,118.861737619173724,258.646767383963777],"luv":[53.0764799083082721,-23.3987809317452253,-116.535873106771263],"rgb":[0.2,0.466666666666666674,1],"xyz":[0.260098258588793207,0.211162418096542737,0.973160550837957605],"hpluv":[258.646767383963777,284.170694785425781,53.0764799083082721],"hsluv":[258.646767383963777,99.9999999999990763,53.0764799083082721]},"#aaff00":{"lch":[91.7137860391432156,115.080534629040301,111.722667154579099],"luv":[91.7137860391432156,-42.5929524944460596,106.908230966149674],"rgb":[0.66666666666666663,1,0],"xyz":[0.523356277296021299,0.800644834253704918,0.126965339384251918],"hpluv":[111.722667154579099,635.020942157405898,91.7137860391432156],"hsluv":[111.722667154579099,100.000000000002359,91.7137860391432156]},"#aaff11":{"lch":[91.7319300755291209,114.302910123776968,111.867287470019974],"luv":[91.7319300755291209,-42.5730312099484394,106.078707931238455],"rgb":[0.66666666666666663,1,0.0666666666666666657],"xyz":[0.524367942795658459,0.801049500453559826,0.132293444349007555],"hpluv":[111.867287470019974,632.205251281199821,91.7319300755291209],"hsluv":[111.867287470019974,99.9999999999902087,91.7319300755291209]},"#aaff22":{"lch":[91.76554812600844,112.870020698142184,112.139393409643191],"luv":[91.76554812600844,-42.5363318976714595,104.548084827461963],"rgb":[0.66666666666666663,1,0.133333333333333331],"xyz":[0.526243300934135405,0.801799643708950627,0.142170330544986734],"hpluv":[112.139393409643191,626.996151229530483,91.76554812600844],"hsluv":[112.139393409643191,99.9999999999901,91.76554812600844]},"#aaff33":{"lch":[91.8208541183991116,110.535111156119868,112.599114759903571],"luv":[91.8208541183991116,-42.4765495373732875,102.047800258971023],"rgb":[0.66666666666666663,1,0.2],"xyz":[0.529331051666593222,0.803034744001933753,0.158432484402597884],"hpluv":[112.599114759903571,618.449193227995465,91.8208541183991116],"hsluv":[112.599114759903571,99.9999999999900808,91.8208541183991116]},"#aaff44":{"lch":[91.9006031807778498,107.217215837920833,113.289723447654],"luv":[91.9006031807778498,-42.3916247074254713,98.4808688360338],"rgb":[0.66666666666666663,1,0.266666666666666663],"xyz":[0.533789046910812082,0.80481794209962132,0.181911259355484523],"hpluv":[113.289723447654,606.175591066234915,91.9006031807778498],"hsluv":[113.289723447654,99.9999999999900808,91.9006031807778498]},"#aaff55":{"lch":[92.0070808640835338,102.877427982590888,114.266397603879824],"luv":[92.0070808640835338,-42.2805422001356135,93.7876374559873796],"rgb":[0.66666666666666663,1,0.333333333333333315],"xyz":[0.539751460950174,0.807202907715366069,0.213313306629458438],"hpluv":[114.266397603879824,589.885725054723707,92.0070808640835338],"hsluv":[114.266397603879824,99.9999999999897,92.0070808640835338]},"#aaff66":{"lch":[92.142232175119787,97.5178938455906,115.604656391492952],"luv":[92.142232175119787,-42.1432392632638226,87.9413839126902559],"rgb":[0.66666666666666663,1,0.4],"xyz":[0.547336456723682252,0.810236906024769388,0.2532609510366024],"hpluv":[115.604656391492952,569.381090837871511,92.142232175119787],"hsluv":[115.604656391492952,99.9999999999898,92.142232175119787]},"#aaff77":{"lch":[92.3077308115560555,91.1840841453542197,117.412488820025033],"luv":[92.3077308115560555,-41.9805406670279666,80.9454841651530899],"rgb":[0.66666666666666663,1,0.466666666666666674],"xyz":[0.556650469743884102,0.813962511232850239,0.302314752943000309],"hpluv":[117.412488820025033,544.56608764574878,92.3077308115560555],"hsluv":[117.412488820025033,99.9999999999895692,92.3077308115560555]},"#aaff88":{"lch":[92.5050204995058749,83.9705472319660657,119.849478645386625],"luv":[92.5050204995058749,-41.7940850853900727,72.8306752289927601],"rgb":[0.66666666666666663,1,0.533333333333333326],"xyz":[0.567790886845828,0.818418678073627781,0.360987616346572504],"hpluv":[119.849478645386625,515.487838204951345,92.5050204995058749],"hsluv":[119.849478645386625,99.9999999999892708,92.5050204995058749]},"#aaff99":{"lch":[92.7353415895377,76.0327408171719,123.158131820453576],"luv":[92.7353415895377,-41.5862309318022625,63.6518897838709137],"rgb":[0.66666666666666663,1,0.6],"xyz":[0.580847850954900657,0.823641463717257,0.429754293987690383],"hpluv":[123.158131820453576,482.430180447041607,92.7353415895377],"hsluv":[123.158131820453576,99.9999999999891145,92.7353415895377]},"#aaffaa":{"lch":[92.9997492696274435,67.6109506527765802,127.715012949238414],"luv":[92.9997492696274435,-41.3599402641421108,53.4845397242865488],"rgb":[0.66666666666666663,1,0.66666666666666663],"xyz":[0.595905543920153269,0.829664540903358061,0.509058143604689],"hpluv":[127.715012949238414,446.121940639593902,92.9997492696274435],"hsluv":[127.715012949238414,99.9999999999887308,92.9997492696274435]},"#aaffbb":{"lch":[93.2991268167713201,59.0781485982363,134.10730048328],"luv":[93.2991268167713201,-41.1186450338129887,42.4203332421914823],"rgb":[0.66666666666666663,1,0.733333333333333282],"xyz":[0.613043136027489832,0.836519577746292819,0.599316128703330731],"hpluv":[134.10730048328,408.190288953842469,93.2991268167713201],"hsluv":[134.10730048328,99.9999999999883613,93.2991268167713201]},"#aaffcc":{"lch":[93.6341958749929404,51.0306218960527929,143.208028901391231],"luv":[93.6341958749929404,-40.8661030238149436,30.5628204644932246],"rgb":[0.66666666666666663,1,0.8],"xyz":[0.632335511471211587,0.844236527923781677,0.700922639373600886],"hpluv":[143.208028901391231,372.11656739719416,93.6341958749929404],"hsluv":[143.208028901391231,99.9999999999879492,93.6341958749929404]},"#aaffdd":{"lch":[94.005524978734,44.4262644213682094,156.066165366624773],"luv":[94.005524978734,-40.6062520520773305,18.0229094410556279],"rgb":[0.66666666666666663,1,0.866666666666666696],"xyz":[0.653853837021845186,0.852843858144035183,0.814252487273606884],"hpluv":[156.066165366624773,345.018188987702331,94.005524978734],"hsluv":[156.066165366624773,99.999999999986926,94.005524978734]},"#aaffee":{"lch":[94.4135370960349576,40.641316399200953,173.054449077896464],"luv":[94.4135370960349576,-40.3430708217409446,4.91459411670529711],"rgb":[0.66666666666666663,1,0.933333333333333348],"xyz":[0.677666017288019895,0.8623687302505052,0.939663303342130107],"hpluv":[173.054449077896464,339.745490646316398,94.4135370960349576],"hsluv":[173.054449077896464,99.9999999999862865,94.4135370960349576]},"#aaffff":{"lch":[94.8585166918378633,41.0030022313427764,192.177050630060705],"luv":[94.8585166918378633,-40.0804535568370639,-8.64889788711415264],"rgb":[0.66666666666666663,1,1],"xyz":[0.703837065697851316,0.872837149614437924,1.07749749163391195],"hpluv":[192.177050630060705,373.711432895013843,94.8585166918378633],"hsluv":[192.177050630060705,99.9999999999852491,94.8585166918378633]},"#338800":{"lch":[49.8717454753508918,71.2965557675853461,122.69380865426514],"luv":[49.8717454753508918,-38.5107906934601871,60.0009821960016509],"rgb":[0.2,0.533333333333333326,0],"xyz":[0.101689839911933699,0.183114842185751275,0.0299858551632874795],"hpluv":[122.69380865426514,181.406693814272955,49.8717454753508918],"hsluv":[122.69380865426514,100.000000000002245,49.8717454753508918]},"#338811":{"lch":[49.9202331837381053,69.5570511040883162,123.47043015419375],"luv":[49.9202331837381053,-38.361169332567485,58.0224443274741475],"rgb":[0.2,0.533333333333333326,0.0666666666666666657],"xyz":[0.102701505411570818,0.183519508385606128,0.0353139601280431092],"hpluv":[123.47043015419375,176.808802540010674,49.9202331837381053],"hsluv":[123.47043015419375,96.3624965314944291,49.9202331837381053]},"#338822":{"lch":[50.0099282713402,66.4316762124597915,124.985970570646856],"luv":[50.0099282713402,-38.0903182709230137,54.426971791721833],"rgb":[0.2,0.533333333333333326,0.133333333333333331],"xyz":[0.104576863550047847,0.184269651640996956,0.0451908463240223088],"hpluv":[124.985970570646856,168.561468288507513,50.0099282713402],"hsluv":[124.985970570646856,89.7672781446156876,50.0099282713402]},"#338833":{"lch":[50.1570811029395429,61.5658815699287487,127.715012949239991],"luv":[50.1570811029395429,-37.6619639194057143,48.7025076070614134],"rgb":[0.2,0.533333333333333326,0.2],"xyz":[0.10766461428250558,0.185504751933980055,0.061453000181633452],"hpluv":[127.715012949239991,155.756856758587162,50.1570811029395429],"hsluv":[127.715012949239991,79.3080015418374416,50.1570811029395429]},"#338844":{"lch":[50.3683877843642165,55.1600753981733334,132.238380805917416],"luv":[50.3683877843642165,-37.0795228857786441,40.8380080377971524],"rgb":[0.2,0.533333333333333326,0.266666666666666663],"xyz":[0.112122609526724454,0.187287950031667649,0.0849317751345200905],"hpluv":[132.238380805917416,138.965222681713072,50.3683877843642165],"hsluv":[132.238380805917416,79.9701630926319922,50.3683877843642165]},"#338855":{"lch":[50.648916869713247,47.7759983149871488,139.558488969535745],"luv":[50.648916869713247,-36.3608093871966886,30.990926990033774],"rgb":[0.2,0.533333333333333326,0.333333333333333315],"xyz":[0.118085023566086453,0.189672915647412482,0.116333822408493992],"hpluv":[139.558488969535745,119.695806374350084,50.648916869713247],"hsluv":[139.558488969535745,80.7922551095087726,50.648916869713247]},"#338866":{"lch":[51.0024095938582747,40.50700493167151,151.3112863122482],"luv":[51.0024095938582747,-35.5343950908498,19.4454162738124445],"rgb":[0.2,0.533333333333333326,0.4],"xyz":[0.125670019339594624,0.192706913956815773,0.156281466815637954],"hpluv":[151.3112863122482,100.78102351571394,51.0024095938582747],"hsluv":[151.3112863122482,81.7453783644028249,51.0024095938582747]},"#338877":{"lch":[51.4314426692160964,35.2513066472794421,169.271441625736571],"luv":[51.4314426692160964,-34.635118433957949,6.56225505495134165],"rgb":[0.2,0.533333333333333326,0.466666666666666674],"xyz":[0.134984032359796557,0.196432519164896596,0.205335268722035863],"hpluv":[169.271441625736571,86.9732781104886499,51.4314426692160964],"hsluv":[169.271441625736571,82.7938072280844182,51.4314426692160964]},"#338888":{"lch":[51.9375397067754818,34.4751674240590873,192.177050630061075],"luv":[51.9375397067754818,-33.6994920276336458,-7.27196025816705482],"rgb":[0.2,0.533333333333333326,0.533333333333333326],"xyz":[0.146124449461740358,0.200888686005674194,0.26400813212560803],"hpluv":[192.177050630061075,84.229522237058,51.9375397067754818],"hsluv":[192.177050630061075,83.8998231764876579,51.9375397067754818]},"#338899":{"lch":[52.5212658253882267,39.2899369869163,213.503984387174484],"luv":[52.5212658253882267,-32.7618132949538179,-21.68784770470414],"rgb":[0.2,0.533333333333333326,0.6],"xyz":[0.159181413570813085,0.206111471649303352,0.332774809766725965],"hpluv":[213.503984387174484,94.9260641143146415,52.5212658253882267],"hsluv":[213.503984387174484,85.0278067813718081,52.5212658253882267]},"#3388aa":{"lch":[53.1823203703714142,48.3403865273999855,228.783963154055925],"luv":[53.1823203703714142,-31.8514822326876157,-36.3631138545534753],"rgb":[0.2,0.533333333333333326,0.66666666666666663],"xyz":[0.174239106536065613,0.212134548835404446,0.412078659383724566],"hpluv":[228.783963154055925,115.340588590435829,53.1823203703714142],"hsluv":[228.783963154055925,86.1470646970195588,53.1823203703714142]},"#3388bb":{"lch":[53.9196335036436238,59.7107302664646866,238.732819450220632],"luv":[53.9196335036436238,-30.9916356267104831,-51.0381213421469],"rgb":[0.2,0.533333333333333326,0.733333333333333282],"xyz":[0.191376698643402288,0.218989585678339205,0.502336644482366257],"hpluv":[238.732819450220632,140.522147305520861,53.9196335036436238],"hsluv":[238.732819450220632,87.2332789223686262,53.9196335036436238]},"#3388cc":{"lch":[54.7314676322995268,72.1439684856409826,245.254262274299492],"luv":[54.7314676322995268,-30.1989009668194051,-65.5192992121664446],"rgb":[0.2,0.533333333333333326,0.8],"xyz":[0.210669074087124014,0.226706535855828,0.603943155152636413],"hpluv":[245.254262274299492,167.263916186866481,54.7314676322995268],"hsluv":[245.254262274299492,88.2687848362996,54.7314676322995268]},"#3388dd":{"lch":[55.6155220749816692,84.9545141717074728,249.692682338977761],"luv":[55.6155220749816692,-29.4839257322198733,-79.6741338300441697],"rgb":[0.2,0.533333333333333326,0.866666666666666696],"xyz":[0.232187399637757558,0.23531386607608154,0.717273003052642411],"hpluv":[249.692682338977761,193.833915517882872,55.6155220749816692],"hsluv":[249.692682338977761,89.2420383366021923,55.6155220749816692]},"#3388ee":{"lch":[56.5690381344568323,97.7754852991564434,252.83721230554255],"luv":[56.5690381344568323,-28.8523290085395629,-93.4215640859675602],"rgb":[0.2,0.533333333333333326,0.933333333333333348],"xyz":[0.255999579903932295,0.244838738182551585,0.842683819121165634],"hpluv":[252.83721230554255,219.32619909500923,56.5690381344568323],"hsluv":[252.83721230554255,90.146641202583325,56.5690381344568323]},"#3388ff":{"lch":[57.5889013880528182,110.410831334845568,255.145344615238457],"luv":[57.5889013880528182,-28.3057941013035759,-106.720821287817699],"rgb":[0.2,0.533333333333333326,1],"xyz":[0.282170628313763716,0.255307157546484254,0.980518007412947479],"hpluv":[255.145344615238457,243.283252172694205,57.5889013880528182],"hsluv":[255.145344615238457,99.9999999999988489,57.5889013880528182]},"#339900":{"lch":[55.5688440832231,80.82821284508357,123.866754715109295],"luv":[55.5688440832231,-45.0426054376311527,67.1145564473163603],"rgb":[0.2,0.6,0],"xyz":[0.127559440364401172,0.23485404309068697,0.0386090553141097345],"hpluv":[123.866754715109295,184.574176757765827,55.5688440832231],"hsluv":[123.866754715109295,100.00000000000226,55.5688440832231]},"#339911":{"lch":[55.6099261578814463,79.3206910999707446,124.475743785225987],"luv":[55.6099261578814463,-44.9000555787807372,65.3892731692238556],"rgb":[0.2,0.6,0.0666666666666666657],"xyz":[0.128571105864038304,0.235258709290541823,0.0439371602788653642],"hpluv":[124.475743785225987,180.997883954149785,55.6099261578814463],"hsluv":[124.475743785225987,97.1571693172972601,55.6099261578814463]},"#339922":{"lch":[55.6859569378682124,76.5942224429011418,125.649114872203555],"luv":[55.6859569378682124,-44.6406262928881503,62.240576763164114],"rgb":[0.2,0.6,0.133333333333333331],"xyz":[0.130446464002515305,0.236008852545932651,0.0538140464748445638],"hpluv":[125.649114872203555,174.537861698610129,55.6859569378682124],"hsluv":[125.649114872203555,91.9778413864117113,55.6859569378682124]},"#339933":{"lch":[55.8107903110879278,72.2971425020977563,127.715012949240148],"luv":[55.8107903110879278,-44.2266447414942618,57.191613973364035],"rgb":[0.2,0.6,0.2],"xyz":[0.133534214734973067,0.23724395283891575,0.070076200332455707],"hpluv":[127.715012949240148,164.377468022308932,55.8107903110879278],"hsluv":[127.715012949240148,83.6974291768240874,55.8107903110879278]},"#339944":{"lch":[55.9902586993563602,66.5149718399310501,131.021169741293448],"luv":[55.9902586993563602,-43.6562926505411042,50.1833597009719128],"rgb":[0.2,0.6,0.266666666666666663],"xyz":[0.137992209979191927,0.239027150936603344,0.0935549752853423455],"hpluv":[131.021169741293448,150.746162046382238,55.9902586993563602],"hsluv":[131.021169741293448,84.1112663411127386,55.9902586993563602]},"#339955":{"lch":[56.2289015921924,59.572114639014508,136.121417864766215],"luv":[56.2289015921924,-42.9401916463123783,41.2913645202220323],"rgb":[0.2,0.6,0.333333333333333315],"xyz":[0.143954624018553912,0.241412116552348177,0.124957022559316261],"hpluv":[136.121417864766215,134.438216783596687,56.2289015921924],"hsluv":[136.121417864766215,84.6329955380286236,56.2289015921924]},"#339966":{"lch":[56.5302268191487514,52.1043875673731947,143.898662284026642],"luv":[56.5302268191487514,-42.0991012865816785,30.700698275986678],"rgb":[0.2,0.6,0.4],"xyz":[0.151539619792062097,0.244446114861751468,0.164904666966460223],"hpluv":[143.898662284026642,116.958797396111564,56.5302268191487514],"hsluv":[143.898662284026642,85.2491724948614689,56.5302268191487514]},"#339977":{"lch":[56.8968484427531678,45.1992705399313124,155.596009487994166],"luv":[56.8968484427531678,-41.1609366004351855,18.6748856895258584],"rgb":[0.2,0.6,0.466666666666666674],"xyz":[0.160853632812264,0.248171720069832291,0.213958468872858132],"hpluv":[155.596009487994166,100.805108459810327,56.8968484427531678],"hsluv":[155.596009487994166,85.9413862611579873,56.8968484427531678]},"#339988":{"lch":[57.330574208667926,40.5351988689668303,172.17134267960472],"luv":[57.330574208667926,-40.1574041849560217,5.52134371985359085],"rgb":[0.2,0.6,0.533333333333333326],"xyz":[0.171994049914207858,0.252627886910609889,0.272631332276430272],"hpluv":[172.17134267960472,89.7191897797317,57.330574208667926],"hsluv":[172.17134267960472,86.6885468018933665,57.330574208667926]},"#339999":{"lch":[57.8324724587931627,40.0212192418936539,192.177050630061103],"luv":[57.8324724587931627,-39.1207602326874735,-8.44180717763071],"rgb":[0.2,0.6,0.6],"xyz":[0.185051014023280558,0.257850672554239047,0.341398009917548206],"hpluv":[192.177050630061103,87.8128114771670312,57.8324724587931627],"hsluv":[192.177050630061103,87.4690863712762194,57.8324724587931627]},"#3399aa":{"lch":[58.4029323329449568,44.4430671798845367,211.035048784986628],"luv":[58.4029323329449568,-38.0811346860811923,-22.913170914918652],"rgb":[0.2,0.6,0.66666666666666663],"xyz":[0.200108706988533114,0.263873749740340169,0.420701859534546807],"hpluv":[211.035048784986628,96.5625435737210154,58.4029323329449568],"hsluv":[211.035048784986628,88.2627766327166,58.4029323329449568]},"#3399bb":{"lch":[59.0417237507550823,52.8166449153409445,225.431517702392057],"luv":[59.0417237507550823,-37.064675814253917,-37.6272213549907448],"rgb":[0.2,0.6,0.733333333333333282],"xyz":[0.217246299095869733,0.270728786583274927,0.510959844633188554],"hpluv":[225.431517702392057,113.514439229551684,59.0417237507550823],"hsluv":[225.431517702392057,89.0519907331041196,59.0417237507550823]},"#3399cc":{"lch":[59.7480598269018373,63.5982744854026194,235.423336237979868],"luv":[59.7480598269018373,-36.0925574956591646,-52.364757336811472],"rgb":[0.2,0.6,0.8],"xyz":[0.236538674539591487,0.278445736760763729,0.612566355303458709],"hpluv":[235.423336237979868,135.070607162709791,59.7480598269018373],"hsluv":[235.423336237979868,89.8223822716188636,59.7480598269018373]},"#3399dd":{"lch":[60.5206621982976856,75.6357863680042186,242.281144195606601],"luv":[60.5206621982976856,-35.1807304699237235,-66.9558689220665286],"rgb":[0.2,0.6,0.866666666666666696],"xyz":[0.25805700009022503,0.287053066981017235,0.725896203203464707],"hpluv":[242.281144195606601,158.58531958905732,60.5206621982976856],"hsluv":[242.281144195606601,90.5630616197976508,60.5206621982976856]},"#3399ee":{"lch":[61.3578284965586818,88.2341933332873083,247.095644636752098],"luv":[61.3578284965586818,-34.3402162860227946,-81.277441019049661],"rgb":[0.2,0.6,0.933333333333333348],"xyz":[0.28186918035639974,0.296577939087487252,0.85130701927198793],"hpluv":[247.095644636752098,182.476215369006297,61.3578284965586818],"hsluv":[247.095644636752098,91.2664056494933504,61.3578284965586818]},"#3399ff":{"lch":[62.2575005434706554,100.992674077385175,250.5808182685887],"luv":[62.2575005434706554,-33.5777301361133595,-95.2473425151972],"rgb":[0.2,0.6,1],"xyz":[0.308040228766231161,0.30704635845142,0.989141207563769775],"hpluv":[250.5808182685887,205.84367033720622,62.2575005434706554],"hsluv":[250.5808182685887,99.9999999999985789,62.2575005434706554]},"#220000":{"lch":[3.07250446727781679,10.3329293192956264,12.1770506300617765],"luv":[3.07250446727781679,10.1004431663672367,2.17955870775360072],"rgb":[0.133333333333333331,0,0],"xyz":[0.00659672420629513,0.00340143591887099878,0.000309221447170077699],"hpluv":[12.1770506300617765,426.746789183125429,3.07250446727781679],"hsluv":[12.1770506300617765,100.000000000002217,3.07250446727781679]},"#220011":{"lch":[3.43803794680403607,8.12070857757986353,344.488545895364155],"luv":[3.43803794680403607,7.82492808895188574,-2.17172931202554675],"rgb":[0.133333333333333331,0,0.0666666666666666657],"xyz":[0.00760838970593225201,0.00380610211872585338,0.00563732641192570948],"hpluv":[344.488545895364155,299.724735916282839,3.43803794680403607],"hsluv":[344.488545895364155,99.9999999999976836,3.43803794680403607]},"#220022":{"lch":[4.11563957101797229,9.37475958111893348,307.715012949243601],"luv":[4.11563957101797229,5.73486236359989565,-7.41602797151862436],"rgb":[0.133333333333333331,0,0.133333333333333331],"xyz":[0.00948374784440927,0.00455624537411667124,0.0155142126079049047],"hpluv":[307.715012949243601,289.042783730483393,4.11563957101797229],"hsluv":[307.715012949243601,99.9999999999988205,4.11563957101797229]},"#220033":{"lch":[5.23130109110515384,14.2535250315243012,286.735013267555587],"luv":[5.23130109110515384,4.10424250296207127,-13.6498413654214126],"rgb":[0.133333333333333331,0,0.2],"xyz":[0.0125714985768670112,0.00579134566709978496,0.0317763664655160497],"hpluv":[286.735013267555587,345.74180296647927,5.23130109110515384],"hsluv":[286.735013267555587,99.9999999999995737,5.23130109110515384]},"#220044":{"lch":[6.84205732813722722,21.3889830656619786,277.641816515271671],"luv":[6.84205732813722722,2.84430225454687724,-21.1990221771654959],"rgb":[0.133333333333333331,0,0.266666666666666663],"xyz":[0.01702949382108589,0.0075745437647873606,0.0552551414184026882],"hpluv":[277.641816515271671,396.682237683346386,6.84205732813722722],"hsluv":[277.641816515271671,100.000000000000085,6.84205732813722722]},"#220055":{"lch":[8.95766614306443,30.4428627575942237,273.263558660643355],"luv":[8.95766614306443,1.73308321478426808,-30.3934913336449455],"rgb":[0.133333333333333331,0,0.333333333333333315],"xyz":[0.0229919078604478855,0.00995950938053219263,0.0866571886923766],"hpluv":[273.263558660643355,431.250830347711485,8.95766614306443],"hsluv":[273.263558660643355,100.000000000000242,8.95766614306443]},"#220066":{"lch":[11.2709410858812937,40.3162667149428913,270.881506896841],"luv":[11.2709410858812937,0.620249265146302853,-40.3114952920317435],"rgb":[0.133333333333333331,0,0.4],"xyz":[0.0305769036339560568,0.0129935076899355059,0.126604833099520558],"hpluv":[270.881506896841,453.899240935372916,11.2709410858812937],"hsluv":[270.881506896841,100.000000000000469,11.2709410858812937]},"#220077":{"lch":[13.6616791408408957,50.492834518379162,269.459540268375122],"luv":[13.6616791408408957,-0.476281836738408071,-50.4905881656414763],"rgb":[0.133333333333333331,0,0.466666666666666674],"xyz":[0.0398909166541579763,0.0167191128980163223,0.175658635005918468],"hpluv":[269.459540268375122,468.991527020998944,13.6616791408408957],"hsluv":[269.459540268375122,100.000000000000711,13.6616791408408957]},"#220088":{"lch":[16.0923146306383913,60.7890037695263104,268.549935621017426],"luv":[16.0923146306383913,-1.53830805749361632,-60.7695366743217278],"rgb":[0.133333333333333331,0,0.533333333333333326],"xyz":[0.0510313337561018043,0.0211752797387939132,0.234331498409490635],"hpluv":[268.549935621017426,479.34239057424071,16.0923146306383913],"hsluv":[268.549935621017426,100.000000000000711,16.0923146306383913]},"#220099":{"lch":[18.5394450926422749,71.1015482986176437,267.936483797094468],"luv":[18.5394450926422749,-2.56017951479828287,-71.0554406876254916],"rgb":[0.133333333333333331,0,0.6],"xyz":[0.0640882978651745178,0.0263980653824230742,0.30309817605060857],"hpluv":[267.936483797094468,486.655519564945394,18.5394450926422749],"hsluv":[267.936483797094468,100.000000000000739,18.5394450926422749]},"#2200aa":{"lch":[20.9885603179873783,81.3727160976321,267.505178931910336],"luv":[20.9885603179873783,-3.54207977840461252,-81.2955878012407851],"rgb":[0.133333333333333331,0,0.66666666666666663],"xyz":[0.0791459908304270598,0.0324211425685241791,0.382402025667607171],"hpluv":[267.505178931910336,491.966452636739518,20.9885603179873783],"hsluv":[267.505178931910336,100.000000000000782,20.9885603179873783]},"#2200bb":{"lch":[23.4306921856835828,91.57073581353,267.191578225858507],"luv":[23.4306921856835828,-4.48665301772878333,-91.4607544366971155],"rgb":[0.133333333333333331,0,0.733333333333333282],"xyz":[0.0962835829377637,0.0392761794114589377,0.472660010766248917],"hpluv":[267.191578225858507,495.919187528698728,23.4306921856835828],"hsluv":[267.191578225858507,100.000000000000909,23.4306921856835828]},"#2200cc":{"lch":[25.860342630381858,101.678845182637474,266.957159441292106],"luv":[25.860342630381858,-5.39738007409318588,-101.535490573545474],"rgb":[0.133333333333333331,0,0.8],"xyz":[0.115575958381485447,0.0469931295889477393,0.574266521436519],"hpluv":[266.957159441292106,498.925449111647538,25.860342630381858],"hsluv":[266.957159441292106,100.000000000000881,25.860342630381858]},"#2200dd":{"lch":[28.2742062228116282,111.689036790699618,266.777814373778199],"luv":[28.2742062228116282,-6.27782958394359714,-111.512464751476173],"rgb":[0.133333333333333331,0,0.866666666666666696],"xyz":[0.137094283932118977,0.0556004598092012733,0.687596369336525],"hpluv":[266.777814373778199,501.255846139694427,28.2742062228116282],"hsluv":[266.777814373778199,100.000000000000838,28.2742062228116282]},"#2200ee":{"lch":[30.6703766456275062,121.598437280905173,266.637867063772376],"luv":[30.6703766456275062,-7.13133722672581882,-121.389142753859758],"rgb":[0.133333333333333331,0,0.933333333333333348],"xyz":[0.160906464198293714,0.0651253319156713,0.813007185405048238],"hpluv":[266.637867063772376,503.092926092128948,30.6703766456275062],"hsluv":[266.637867063772376,100.000000000000838,30.6703766456275062]},"#2200ff":{"lch":[33.0478477502328261,131.407178056457695,266.526788769360394],"luv":[33.0478477502328261,-7.96089030872800674,-131.165813649189772],"rgb":[0.133333333333333331,0,1],"xyz":[0.187077512608125107,0.0755937512796040073,0.950841373696830083],"hpluv":[266.526788769360394,504.562807291912918,33.0478477502328261],"hsluv":[266.526788769360394,100.000000000000824,33.0478477502328261]},"#221100":{"lch":[6.69363913087575835,9.72440836304526535,42.3457761997067053],"luv":[6.69363913087575835,7.18724369375563921,6.55039282011655288],"rgb":[0.133333333333333331,0.0666666666666666657,0],"xyz":[0.00860112446722354,0.00741023644072787233,0.000977354867479528471],"hpluv":[42.3457761997067053,184.348759610596915,6.69363913087575835],"hsluv":[42.3457761997067053,100.000000000002402,6.69363913087575835]},"#221111":{"lch":[7.0591726104019763,6.19439175917428564,12.1770506300621],"luv":[7.0591726104019763,6.05502079617615863,1.30660339200560327],"rgb":[0.133333333333333331,0.0666666666666666657,0.0666666666666666657],"xyz":[0.00961278996686066103,0.00781490264058272606,0.00630545983223516],"hpluv":[12.1770506300621,111.348454543071412,7.0591726104019763],"hsluv":[12.1770506300621,26.092394217240134,7.0591726104019763]},"#221122":{"lch":[7.73677423461591474,7.55259268754738677,307.715012949245249],"luv":[7.73677423461591474,4.62018030186617779,-5.97457866985129],"rgb":[0.133333333333333331,0.0666666666666666657,0.133333333333333331],"xyz":[0.0114881481053376797,0.00856504589597354565,0.0161823460282143547],"hpluv":[307.715012949245249,123.872660774597591,7.73677423461591474],"hsluv":[307.715012949245249,42.856167926372315,7.73677423461591474]},"#221133":{"lch":[8.8238329822443653,14.9761175001236957,282.095598903329574],"luv":[8.8238329822443653,3.13814740657790647,-14.6436377390353236],"rgb":[0.133333333333333331,0.0666666666666666657,0.2],"xyz":[0.0145758988377954202,0.00980014618895665851,0.0324444998858255],"hpluv":[282.095598903329574,215.36805510923017,8.8238329822443653],"hsluv":[282.095598903329574,58.3941618505161273,8.8238329822443653]},"#221144":{"lch":[10.2463738670161,23.9334379374521049,274.255517801158362],"luv":[10.2463738670161,1.77596948547040223,-23.8674544912638211],"rgb":[0.133333333333333331,0.0666666666666666657,0.266666666666666663],"xyz":[0.0190338940820143,0.011583344286644235,0.0559232748387121364],"hpluv":[274.255517801158362,296.397281412249697,10.2463738670161],"hsluv":[274.255517801158362,70.1230959508528287,10.2463738670161]},"#221155":{"lch":[11.9365395500671561,33.3852442414483903,270.945812779521702],"luv":[11.9365395500671561,0.551083463009601,-33.3806956200430349],"rgb":[0.133333333333333331,0.0666666666666666657,0.333333333333333315],"xyz":[0.0249963081213762928,0.0139683099023890662,0.0873253221126860518],"hpluv":[270.945812779521702,354.907717298979946,11.9365395500671561],"hsluv":[270.945812779521702,78.3035236172291,11.9365395500671561]},"#221166":{"lch":[13.8282163263251512,43.1006504094303509,269.247085426223],"luv":[13.8282163263251512,-0.566362377205730683,-43.0969291176716496],"rgb":[0.133333333333333331,0.0666666666666666657,0.4],"xyz":[0.0325813038948844641,0.0170023082117923795,0.12727296651983],"hpluv":[269.247085426223,395.509560036398682,13.8282163263251512],"hsluv":[269.247085426223,83.9084652674952167,13.8282163263251512]},"#221177":{"lch":[15.8647012598499089,52.9790758801604,268.263491631539182],"luv":[15.8647012598499089,-1.60543281970815044,-52.954745458525224],"rgb":[0.133333333333333331,0.0666666666666666657,0.466666666666666674],"xyz":[0.0418953169150863836,0.0207279134198731958,0.176326768426227909],"hpluv":[268.263491631539182,423.75204448359591,15.8647012598499089],"hsluv":[268.263491631539182,87.783734282584831,15.8647012598499089]},"#221188":{"lch":[18.0016522099437424,62.9493123294311872,267.645454528547475],"luv":[18.0016522099437424,-2.58614693443793442,-62.8961665507666297],"rgb":[0.133333333333333331,0.0666666666666666657,0.533333333333333326],"xyz":[0.0530357340170302116,0.0251840802606507867,0.234999631829800076],"hpluv":[267.645454528547475,443.729139668046173,18.0016522099437424],"hsluv":[267.645454528547475,90.5156952975567464,18.0016522099437424]},"#221199":{"lch":[20.2061107483083475,72.9561611588118524,267.23334268313738],"luv":[20.2061107483083475,-3.52148568403582729,-72.8711231531918742],"rgb":[0.133333333333333331,0.0666666666666666657,0.6],"xyz":[0.066092698126102925,0.0304068659042799477,0.303766309470918],"hpluv":[267.23334268313738,458.161478440778865,20.2061107483083475],"hsluv":[267.23334268313738,92.4853336382361704,20.2061107483083475]},"#2211aa":{"lch":[22.4542330055690798,82.9584275230068471,266.945798214496847],"luv":[22.4542330055690798,-4.42007776686241449,-82.8405915576709759],"rgb":[0.133333333333333331,0.0666666666666666657,0.66666666666666663],"xyz":[0.0811503910913554671,0.0364299430903810492,0.383070159087916584],"hpluv":[266.945798214496847,468.815123306781686,22.4542330055690798],"hsluv":[266.945798214496847,93.9373181855700921,22.4542330055690798]},"#2211bb":{"lch":[24.7289983316777295,92.9268656932788701,266.737844101991243],"luv":[24.7289983316777295,-5.28796663316276483,-92.7762888698575097],"rgb":[0.133333333333333331,0.0666666666666666657,0.733333333333333282],"xyz":[0.0982879831986921138,0.0432849799333158078,0.473328144186558331],"hpluv":[266.737844101991243,476.841549380862261,24.7289983316777295],"hsluv":[266.737844101991243,95.0302193246277369,24.7289983316777295]},"#2211cc":{"lch":[27.0183279904475668,102.841627589427389,266.583003116257],"luv":[27.0183279904475668,-6.12961814831514129,-102.658794784462174],"rgb":[0.133333333333333331,0.0666666666666666657,0.8],"xyz":[0.117580358642413854,0.0510019301108046094,0.574934654856828486],"hpluv":[266.583003116257,483.002966655360069,27.0183279904475668],"hsluv":[266.583003116257,95.8686128249461689,27.0183279904475668]},"#2211dd":{"lch":[29.313669578695368,112.689841457512074,266.464886381494523],"luv":[29.313669578695368,-6.94848190374932617,-112.475414944566779],"rgb":[0.133333333333333331,0.0666666666666666657,0.866666666666666696],"xyz":[0.139098684193047384,0.0596092603310581434,0.688264502756834484],"hpluv":[266.464886381494523,487.813602393737767,29.313669578695368],"hsluv":[266.464886381494523,96.5228774582602256,29.313669578695368]},"#2211ee":{"lch":[31.6089746608533417,122.463629630364608,266.372923821556242],"luv":[31.6089746608533417,-7.74731231829930689,-122.21832814306471],"rgb":[0.133333333333333331,0.0666666666666666657,0.933333333333333348],"xyz":[0.162910864459222149,0.0691341324375281813,0.813675318825357707],"hpluv":[266.372923821556242,491.627361233059389,31.6089746608533417],"hsluv":[266.372923821556242,97.0413663214109761,31.6089746608533417]},"#2211ff":{"lch":[33.8999739889387115,132.15860685194221,266.300059947999785],"luv":[33.8999739889387115,-8.52836196764118,-131.883146789857562],"rgb":[0.133333333333333331,0.0666666666666666657,1],"xyz":[0.189081912869053514,0.0796025518014608774,0.951509507117139552],"hpluv":[266.300059947999785,494.692599475339,33.8999739889387115],"hsluv":[266.300059947999785,99.9999999999995737,33.8999739889387115]},"#77aa00":{"lch":[63.8935034159882491,78.4053265973676616,109.262687899665323],"luv":[63.8935034159882491,-25.8658938451783591,74.0158819067222424],"rgb":[0.466666666666666674,0.66666666666666663,0],"xyz":[0.219816749274909073,0.326708497135311,0.0514797056256764834],"hpluv":[109.262687899665323,155.714190603412163,63.8935034159882491],"hsluv":[109.262687899665323,100.000000000002132,63.8935034159882491]},"#77aa11":{"lch":[63.9264755829735662,77.0719876411919,109.595002167467626],"luv":[63.9264755829735662,-25.8475857965541422,72.6084952843247464],"rgb":[0.466666666666666674,0.66666666666666663,0.0666666666666666657],"xyz":[0.220828414774546206,0.32711316333516588,0.0568078105904321132],"hpluv":[109.595002167467626,152.98720995680398,63.9264755829735662],"hsluv":[109.595002167467626,97.9667163347824612,63.9264755829735662]},"#77aa22":{"lch":[63.9875253363426424,74.6349938152274603,110.235118064089221],"luv":[63.9875253363426424,-25.8142561390327501,70.0286118796271],"rgb":[0.466666666666666674,0.66666666666666663,0.133333333333333331],"xyz":[0.222703772913023207,0.32786330659055668,0.0666846967864113127],"hpluv":[110.235118064089221,148.008450888663447,63.9875253363426424],"hsluv":[110.235118064089221,94.2440053809013421,63.9875253363426424]},"#77aa33":{"lch":[64.0878403339217328,70.7199026331045,111.362593229805157],"luv":[64.0878403339217328,-25.7610573917560757,65.8610093340090685],"rgb":[0.466666666666666674,0.66666666666666663,0.2],"xyz":[0.22579152364548094,0.329098406883539807,0.0829468506440224629],"hpluv":[111.362593229805157,140.024923426145421,64.0878403339217328],"hsluv":[111.362593229805157,88.2427941375042,64.0878403339217328]},"#77aa44":{"lch":[64.2322300921174616,65.2796522257345,113.17268826837423],"luv":[64.2322300921174616,-25.6877867579927681,60.0130869576689463],"rgb":[0.466666666666666674,0.66666666666666663,0.266666666666666663],"xyz":[0.230249518889699828,0.330881604981227373,0.106425625596909101],"hpluv":[113.17268826837423,128.962712139229978,64.2322300921174616],"hsluv":[113.17268826837423,79.8488372015295482,64.2322300921174616]},"#77aa55":{"lch":[64.4245377454048338,58.3957340906732298,115.996670283872277],"luv":[64.4245377454048338,-25.5959546947607706,52.487225714952487],"rgb":[0.466666666666666674,0.66666666666666663,0.333333333333333315],"xyz":[0.236211932929061841,0.333266570596972234,0.137827672870883],"hpluv":[115.996670283872277,115.018880896771989,64.4245377454048338],"hsluv":[115.996670283872277,69.0944053471027075,64.4245377454048338]},"#77aa66":{"lch":[64.6678576456364,50.3036055521827663,120.443938093288892],"luv":[64.6678576456364,-25.4885877435648602,43.368013862617552],"rgb":[0.466666666666666674,0.66666666666666663,0.4],"xyz":[0.24379692870257,0.336300568906375552,0.177775317278026979],"hpluv":[120.443938093288892,98.707457943709727,64.6678576456364],"hsluv":[120.443938093288892,56.1391996219007723,64.6678576456364]},"#77aa77":{"lch":[64.964649263009,41.4721527505437351,127.71501294923786],"luv":[64.964649263009,-25.3699399849719889,32.8070967768157118],"rgb":[0.466666666666666674,0.66666666666666663,0.466666666666666674],"xyz":[0.253110941722771932,0.340026174114456348,0.226829119184424888],"hpluv":[127.71501294923786,81.006302212696113,64.964649263009],"hsluv":[127.71501294923786,41.2466460512923874,64.964649263009]},"#77aa88":{"lch":[65.3168057391263375,32.8409027786736161,140.238161573904051],"luv":[65.3168057391263375,-25.2451203613846147,21.004970679758074],"rgb":[0.466666666666666674,0.66666666666666663,0.533333333333333326],"xyz":[0.264251358824715732,0.344482340955233945,0.285501982587997],"hpluv":[140.238161573904051,63.8012923553667,65.3168057391263375],"hsluv":[140.238161573904051,43.6199696205744232,65.3168057391263375]},"#77aa99":{"lch":[65.7257012160132,26.4214783611449491,161.939459903532622],"luv":[65.7257012160132,-25.1196781367095241,8.19123245284666],"rgb":[0.466666666666666674,0.66666666666666663,0.6],"xyz":[0.277308322933788487,0.349705126598863103,0.354268660229114962],"hpluv":[161.939459903532622,51.0106916742292213,65.7257012160132],"hsluv":[161.939459903532622,46.1685656356417766,65.7257012160132]},"#77aaaa":{"lch":[66.1922284915170565,25.5746156232648509,192.177050630060677],"luv":[66.1922284915170565,-24.9991985449953802,-5.39453764336142338],"rgb":[0.466666666666666674,0.66666666666666663,0.66666666666666663],"xyz":[0.292366015899041,0.355728203784964225,0.433572509846113563],"hpluv":[192.177050630060677,49.0276908522131265,66.1922284915170565],"hsluv":[192.177050630060677,48.8357820868943122,66.1922284915170565]},"#77aabb":{"lch":[66.7168329685055568,31.6300901405034409,218.105494045696133],"luv":[66.7168329685055568,-24.8889541052261336,-19.519287021925777],"rgb":[0.466666666666666674,0.66666666666666663,0.733333333333333282],"xyz":[0.309503608006377662,0.362583240627899,0.523830494944755309],"hpluv":[218.105494045696133,60.1595160354738923,66.7168329685055568],"hsluv":[218.105494045696133,51.56699221483656,66.7168329685055568]},"#77aacc":{"lch":[67.2995460165272306,42.0550186755790207,233.874741843371567],"luv":[67.2995460165272306,-24.7936410920562054,-33.9691029790543908],"rgb":[0.466666666666666674,0.66666666666666663,0.8],"xyz":[0.328795983450099416,0.370300190805387786,0.625437005615025465],"hpluv":[233.874741843371567,79.2948573353567099,67.2995460165272306],"hsluv":[233.874741843371567,54.3124963748670382,67.2995460165272306]},"#77aadd":{"lch":[67.9400192524912,54.4863937886276304,243.022531341574677],"luv":[67.9400192524912,-24.7172120267711577,-48.557456046554158],"rgb":[0.466666666666666674,0.66666666666666663,0.866666666666666696],"xyz":[0.350314309000732904,0.378907521025641292,0.738766853515031463],"hpluv":[243.022531341574677,101.765770647841308,67.9400192524912],"hsluv":[243.022531341574677,60.6446157506369659,67.9400192524912]},"#77aaee":{"lch":[68.6375602707836,67.7758398266106781,248.660849953701415],"luv":[68.6375602707836,-24.6627990544268876,-63.1293181256010882],"rgb":[0.466666666666666674,0.66666666666666663,0.933333333333333348],"xyz":[0.374126489266907669,0.388432393132111309,0.864177669583554686],"hpluv":[248.660849953701415,125.300382334337471,68.6375602707836],"hsluv":[248.660849953701415,79.8712566390148453,68.6375602707836]},"#77aaff":{"lch":[69.3911697465266428,81.3794470542267874,252.380781260055755],"luv":[69.3911697465266428,-24.6327125013740549,-77.5618712878715399],"rgb":[0.466666666666666674,0.66666666666666663,1],"xyz":[0.400297537676739035,0.398900812496044033,1.00201185787533653],"hpluv":[252.380781260055755,148.816077288451226,69.3911697465266428],"hsluv":[252.380781260055755,99.9999999999980531,69.3911697465266428]},"#222200":{"lch":[12.5069288045758107,13.787646171799997,85.8743202181747307],"luv":[12.5069288045758107,0.991945128669063814,13.751917387057734],"rgb":[0.133333333333333331,0.133333333333333331,0],"xyz":[0.0123167482019914745,0.014841483910263846,0.002215896112402139],"hpluv":[85.8743202181747307,139.887458074797621,12.5069288045758107],"hsluv":[85.8743202181747307,100.000000000002359,12.5069288045758107]},"#222211":{"lch":[12.7636979604368612,8.34346759842367,85.8743202181729828],"luv":[12.7636979604368612,0.600266494900015157,8.32184665209868513],"rgb":[0.133333333333333331,0.133333333333333331,0.0666666666666666657],"xyz":[0.0133284137016285963,0.0152461501101187,0.00754400107715777046],"hpluv":[85.8743202181729828,82.9486632734846552,12.7636979604368612],"hsluv":[85.8743202181729828,59.2967120963297631,12.7636979604368612]},"#222222":{"lch":[13.2279109842717837,6.86787642036123471e-13,0],"luv":[13.2279109842717837,6.53891093021720259e-13,2.10008818196756883e-13],"rgb":[0.133333333333333331,0.133333333333333331,0.133333333333333331],"xyz":[0.0152037718401056149,0.0159962933655095202,0.0174208872731369674],"hpluv":[0,6.58825703928357502e-12,13.2279109842717837],"hsluv":[0,1.88635445986832e-12,13.2279109842717837]},"#222233":{"lch":[13.9615854376221584,10.5260121123804868,265.874320218180912],"luv":[13.9615854376221584,-0.757288539977712838,-10.4987354027615698],"rgb":[0.133333333333333331,0.133333333333333331,0.2],"xyz":[0.0182915225725633554,0.017231393658492633,0.0336830411307481106],"hpluv":[265.874320218180912,95.6683874279760431,13.9615854376221584],"hsluv":[265.874320218180912,18.6338179823007195,13.9615854376221584]},"#222244":{"lch":[14.9613810506728697,21.7214686924654536,265.874320218179207],"luv":[14.9613810506728697,-1.5627399186581008,-21.6651805001571454],"rgb":[0.133333333333333331,0.133333333333333331,0.266666666666666663],"xyz":[0.0227495178167822359,0.0190145917561802061,0.0571618160836347491],"hpluv":[265.874320218179207,184.228505509793536,14.9613810506728697],"hsluv":[265.874320218179207,35.8831222215914138,14.9613810506728697]},"#222255":{"lch":[16.2052187005970154,32.8139057554865161,265.874320218178639],"luv":[16.2052187005970154,-2.3607796110480268,-32.728873041368395],"rgb":[0.133333333333333331,0.133333333333333331,0.333333333333333315],"xyz":[0.028711931856144228,0.021399557371925039,0.0885638633576086576],"hpluv":[265.874320218178639,256.946292996249099,16.2052187005970154],"hsluv":[265.874320218178639,50.0467352240393097,16.2052187005970154]},"#222266":{"lch":[17.6604729086265309,43.5908485911403716,265.874320218178354],"luv":[17.6604729086265309,-3.13612123314657865,-43.477888915018994],"rgb":[0.133333333333333331,0.133333333333333331,0.4],"xyz":[0.0362969276296524063,0.0244335556813283505,0.128511507764752619],"hpluv":[265.874320218178354,313.207621322876264,17.6604729086265309],"hsluv":[265.874320218178354,61.00504004829466,17.6604729086265309]},"#222277":{"lch":[19.2910482951380544,54.0745009411091573,265.874320218178241],"luv":[19.2910482951380544,-3.89036222175517965,-53.9343743248547867],"rgb":[0.133333333333333331,0.133333333333333331,0.466666666666666674],"xyz":[0.0456109406498543188,0.0281591608894091669,0.177565309671150529],"hpluv":[265.874320218178241,355.693573155256843,19.2910482951380544],"hsluv":[265.874320218178241,69.2802447897283429,19.2910482951380544]},"#222288":{"lch":[21.0622605487373207,64.3390225585563371,265.874320218178184],"luv":[21.0622605487373207,-4.62883796225993471,-64.1722968492601353],"rgb":[0.133333333333333331,0.133333333333333331,0.533333333333333326],"xyz":[0.0567513577517981468,0.0326153277301867578,0.236238173074722696],"hpluv":[265.874320218178184,387.622344883614403,21.0622605487373207],"hsluv":[265.874320218178184,75.4991739133377706,21.0622605487373207]},"#222299":{"lch":[22.9434551626666803,74.4470789880776351,265.874320218178127],"luv":[22.9434551626666803,-5.35605689510984373,-74.2541596437089737],"rgb":[0.133333333333333331,0.133333333333333331,0.6],"xyz":[0.0698083218608708533,0.0378381133738159223,0.305004850715840603],"hpluv":[265.874320218178127,411.744842564929684,22.9434551626666803],"hsluv":[265.874320218178127,80.1976353712613559,22.9434551626666803]},"#2222aa":{"lch":[24.9089307040763188,84.4389391258505526,265.87432021817807],"luv":[24.9089307040763188,-6.07491614537627278,-84.2201272530844705],"rgb":[0.133333333333333331,0.133333333333333331,0.66666666666666663],"xyz":[0.0848660148261234093,0.0438611905599170238,0.384308700332839204],"hpluv":[265.87432021817807,430.157015573344836,24.9089307040763188],"hsluv":[265.87432021817807,83.7838678741945557,24.9089307040763188]},"#2222bb":{"lch":[26.937850813592469,94.3371638934954149,265.87432021817807],"luv":[26.937850813592469,-6.78703884698804139,-94.0927021354881106],"rgb":[0.133333333333333331,0.133333333333333331,0.733333333333333282],"xyz":[0.102003606933460056,0.0507162274028517823,0.47456668543148095],"hpluv":[265.87432021817807,444.384803230596,26.937850813592469],"hsluv":[265.87432021817807,86.5550863782758,26.937850813592469]},"#2222cc":{"lch":[29.0136770200274086,104.153206308830732,265.874320218178],"luv":[29.0136770200274086,-7.49324898143496743,-103.883307629821275],"rgb":[0.133333333333333331,0.133333333333333331,0.8],"xyz":[0.121295982377181782,0.058433177580340584,0.576173196101751106],"hpluv":[265.874320218178,455.521834046362642,29.0136770200274086],"hsluv":[265.874320218178,88.7243024658832695,29.0136770200274086]},"#2222dd":{"lch":[31.1234509916598299,113.892375340460845,265.874320218178],"luv":[31.1234509916598299,-8.19392849973896809,-113.597238947227837],"rgb":[0.133333333333333331,0.133333333333333331,0.866666666666666696],"xyz":[0.142814307927815326,0.067040507800594118,0.689503044001757104],"hpluv":[265.874320218178,464.350835522916555,31.1234509916598299],"hsluv":[265.874320218178,90.4439719502614565,31.1234509916598299]},"#2222ee":{"lch":[33.2570959032629503,123.556928623667645,265.874320218178],"luv":[33.2570959032629503,-8.88923982631188103,-123.236747872639071],"rgb":[0.133333333333333331,0.133333333333333331,0.933333333333333348],"xyz":[0.166626488193990063,0.0765653799070641489,0.814913860070280327],"hpluv":[265.874320218178,471.435310205520636,33.2570959032629503],"hsluv":[265.874320218178,93.8546607467714296,33.2570959032629503]},"#2222ff":{"lch":[35.4068078244889,133.147814572056944,265.874320218177957],"luv":[35.4068078244889,-9.57925119428392335,-132.802780361977625],"rgb":[0.133333333333333331,0.133333333333333331,1],"xyz":[0.192797536603821457,0.0870337992709968589,0.952748048362062172],"hpluv":[265.874320218177957,477.184793215987838,35.4068078244889],"hsluv":[265.874320218177957,99.999999999999531,35.4068078244889]},"#77bb00":{"lch":[69.0844312744863629,87.8096536524333544,113.037133893102563],"luv":[69.0844312744863629,-34.3623439699837476,80.8069588058406225],"rgb":[0.466666666666666674,0.733333333333333282,0],"xyz":[0.253771247183507853,0.394617492952509585,0.0627978715952091093],"hpluv":[113.037133893102563,161.287757631366873,69.0844312744863629],"hsluv":[113.037133893102563,100.000000000002331,69.0844312744863629]},"#77bb11":{"lch":[69.1135050244688216,86.6316159473835654,113.342165034517933],"luv":[69.1135050244688216,-34.3252912529264051,79.5412550948043275],"rgb":[0.466666666666666674,0.733333333333333282,0.0666666666666666657],"xyz":[0.254782912683144958,0.395022159152364438,0.068125976559964746],"hpluv":[113.342165034517933,159.057013433643618,69.1135050244688216],"hsluv":[113.342165034517933,98.3127503818399,69.1135050244688216]},"#77bb22":{"lch":[69.1673475306790664,84.4748154027895595,113.924619678029501],"luv":[69.1673475306790664,-34.2574435126740582,77.2167209955934197],"rgb":[0.466666666666666674,0.733333333333333282,0.133333333333333331],"xyz":[0.256658270821622,0.395772302407755239,0.0780028627559439386],"hpluv":[113.924619678029501,154.976360107776344,69.1673475306790664],"hsluv":[113.924619678029501,95.2170715716625438,69.1673475306790664]},"#77bb33":{"lch":[69.2558504240405313,80.9996568929936132,114.934551128374181],"luv":[69.2558504240405313,-34.1480550559542593,73.4496749664571666],"rgb":[0.466666666666666674,0.733333333333333282,0.2],"xyz":[0.25974602155407972,0.397007402700738365,0.0942650166135550749],"hpluv":[114.934551128374181,148.410982175614635,69.2558504240405313],"hsluv":[114.934551128374181,90.2088459481891363,69.2558504240405313]},"#77bb44":{"lch":[69.3833048234310752,76.148419012573342,116.514961433737128],"luv":[69.3833048234310752,-33.9950520676451049,68.1389620777462426],"rgb":[0.466666666666666674,0.733333333333333282,0.266666666666666663],"xyz":[0.264204016798298635,0.398790600798425932,0.117743791566441713],"hpluv":[116.514961433737128,139.266042804817573,69.3833048234310752],"hsluv":[116.514961433737128,83.166634884803841,69.3833048234310752]},"#77bb55":{"lch":[69.5531781358710788,69.9670725739878918,118.886197685461212],"luv":[69.5531781358710788,-33.7990968432466516,61.2618339356121666],"rgb":[0.466666666666666674,0.733333333333333282,0.333333333333333315],"xyz":[0.270166430837660621,0.401175566414170792,0.149145838840415629],"hpluv":[118.886197685461212,127.648598426560824,69.5531781358710788],"hsluv":[118.886197685461212,74.0801666731923092,69.5531781358710788]},"#77bb66":{"lch":[69.7683097004737647,62.6192143930699672,122.411113057511386],"luv":[69.7683097004737647,-33.5633071802414804,52.8646424591145],"rgb":[0.466666666666666674,0.733333333333333282,0.4],"xyz":[0.277751426611168806,0.404209564723574111,0.189093483247559591],"hpluv":[122.411113057511386,113.8908252780179,69.7683097004737647],"hsluv":[122.411113057511386,63.0377921091212627,69.7683097004737647]},"#77bb77":{"lch":[70.0310134677453391,54.4237865803123384,127.71501294923867],"luv":[70.0310134677453391,-33.2928991557924476,43.0526585885427053],"rgb":[0.466666666666666674,0.733333333333333282,0.466666666666666674],"xyz":[0.287065439631370711,0.407935169931654906,0.2381472851539575],"hpluv":[127.71501294923867,98.6137944921033522,70.0310134677453391],"hsluv":[127.71501294923867,50.2119979074062357,70.0310134677453391]},"#77bb88":{"lch":[70.3431390634792422,45.948041729110173,135.896820739423845],"luv":[70.3431390634792422,-32.9947227526694,31.9776610966863863],"rgb":[0.466666666666666674,0.733333333333333282,0.533333333333333326],"xyz":[0.298205856733314512,0.412391336772432504,0.296820148557529695],"hpluv":[135.896820739423845,82.8866502392756246,70.3431390634792422],"hsluv":[135.896820739423845,51.9268484553617711,70.3431390634792422]},"#77bb99":{"lch":[70.7061124793432612,38.2197788608509725,148.75629343317604],"luv":[70.7061124793432612,-32.6767203094589,19.8238100775241328],"rgb":[0.466666666666666674,0.733333333333333282,0.6],"xyz":[0.311262820842387211,0.417614122416061662,0.365586826198647574],"hpluv":[148.75629343317604,68.5915373148405,70.7061124793432612],"hsluv":[148.75629343317604,53.7921873551213565,70.7061124793432612]},"#77bbaa":{"lch":[71.1209666334754615,33.0529501718716148,168.140026251468868],"luv":[71.1209666334754615,-32.3473621836046874,6.79306078486928744],"rgb":[0.466666666666666674,0.733333333333333282,0.66666666666666663],"xyz":[0.326320513807639767,0.423637199602162784,0.444890675815646175],"hpluv":[168.140026251468868,58.9728212370576585,71.1209666334754615],"hsluv":[168.140026251468868,55.7713219876506372,71.1209666334754615]},"#77bbbb":{"lch":[71.5883672123020744,32.7520226121926044,192.177050630060876],"luv":[71.5883672123020744,-32.0151171807858432,-6.90849166534396097],"rgb":[0.466666666666666674,0.733333333333333282,0.733333333333333282],"xyz":[0.343458105914976441,0.430492236445097542,0.535148660914287921],"hpluv":[192.177050630060876,58.0543804308889762,71.5883672123020744],"hsluv":[192.177050630060876,57.8271385543864938,71.5883672123020744]},"#77bbcc":{"lch":[72.1086367753872111,38.0603267014846267,213.636027248927832],"luv":[72.1086367753872111,-31.6880044447458609,-21.0821925551758049],"rgb":[0.466666666666666674,0.733333333333333282,0.8],"xyz":[0.36275048135869814,0.438209186622586344,0.636755171584558077],"hpluv":[213.636027248927832,66.97682452789428,72.1086367753872111],"hsluv":[213.636027248927832,59.9241419588218278,72.1086367753872111]},"#77bbdd":{"lch":[72.6817787487410101,47.4106862821029651,228.567760630430627],"luv":[72.6817787487410101,-31.3732552756954277,-35.5456330249727728],"rgb":[0.466666666666666674,0.733333333333333282,0.866666666666666696],"xyz":[0.384268806909331739,0.44681651684283985,0.750085019484564075],"hpluv":[228.567760630430627,82.7732525696479371,72.6817787487410101],"hsluv":[228.567760630430627,62.0300015657167449,72.6817787487410101]},"#77bbee":{"lch":[73.3075021126589803,58.9885691422652769,238.208313348868131],"luv":[73.3075021126589803,-31.0770938488335098,-50.1384635520735316],"rgb":[0.466666666666666674,0.733333333333333282,0.933333333333333348],"xyz":[0.408080987175506449,0.456341388949309867,0.875495835553087298],"hpluv":[238.208313348868131,102.107764199280055,73.3075021126589803],"hsluv":[238.208313348868131,76.3238953534044,73.3075021126589803]},"#77bbff":{"lch":[73.9852470697490219,71.6819577745528846,244.548916872628638],"luv":[73.9852470697490219,-30.8046295043453391,-64.7254036023940102],"rgb":[0.466666666666666674,0.733333333333333282,1],"xyz":[0.43425203558533787,0.466809808313242591,1.01333002384486903],"hpluv":[244.548916872628638,122.943070755930805,73.9852470697490219],"hsluv":[244.548916872628638,99.9999999999974,73.9852470697490219]},"#223300":{"lch":[18.8330192465532917,22.9063411551717806,108.204985820955727],"luv":[18.8330192465532917,-7.15634373768739707,21.7597612446731326],"rgb":[0.133333333333333331,0.2,0],"xyz":[0.0184344702910022862,0.0270769280882856428,0.00425513680873902],"hpluv":[108.204985820955727,154.338793470845559,18.8330192465532917],"hsluv":[108.204985820955727,100.000000000002331,18.8330192465532917]},"#223311":{"lch":[19.0056890338669575,18.4529510656336271,112.754551304246377],"luv":[19.0056890338669575,-7.13731020483261602,17.0167625026225657],"rgb":[0.133333333333333331,0.2,0.0666666666666666657],"xyz":[0.0194461357906394079,0.0274815942881404957,0.0095832417734946513],"hpluv":[112.754551304246377,123.203072156705915,19.0056890338669575],"hsluv":[112.754551304246377,76.6034511576994248,19.0056890338669575]},"#223322":{"lch":[19.3213416797184507,11.6344605438365232,127.71501294923759],"luv":[19.3213416797184507,-7.11719904028337247,9.20359440474635],"rgb":[0.133333333333333331,0.2,0.133333333333333331],"xyz":[0.0213214939291164265,0.028231737543531317,0.0194601279694738491],"hpluv":[127.71501294923759,76.4096652359405084,19.3213416797184507],"hsluv":[127.71501294923759,38.9061385447444366,19.3213416797184507]},"#223333":{"lch":[19.8290945906418372,7.27996715422488894,192.177050630060677],"luv":[19.8290945906418372,-7.11617124458192585,-1.53558737438758408],"rgb":[0.133333333333333331,0.2,0.2],"xyz":[0.0244092446615741671,0.0294668378365144298,0.0357222818270849923],"hpluv":[192.177050630060677,46.5871198449043149,19.8290945906418372],"hsluv":[192.177050630060677,46.4047641905018935,19.8290945906418372]},"#223344":{"lch":[20.5377244517829496,15.5714684077650816,242.621028364370432],"luv":[20.5377244517829496,-7.16091210703038339,-13.8272183091687548],"rgb":[0.133333333333333331,0.2,0.266666666666666663],"xyz":[0.0288672399057930476,0.031250035934202,0.0592010567799716309],"hpluv":[242.621028364370432,96.2091932027738181,20.5377244517829496],"hsluv":[242.621028364370432,54.472556800898019,20.5377244517829496]},"#223355":{"lch":[21.4445377167678828,27.4997828393463344,254.670418676715883],"luv":[21.4445377167678828,-7.27014529691604761,-26.5213695644274097],"rgb":[0.133333333333333331,0.2,0.333333333333333315],"xyz":[0.0348296539451550397,0.0336350015499468358,0.0906031040539455323],"hpluv":[254.670418676715883,162.72410902482622,21.4445377167678828],"hsluv":[254.670418676715883,62.1025047738862597,21.4445377167678828]},"#223366":{"lch":[22.538163137523668,39.6650934211933617,259.172698399253136],"luv":[22.538163137523668,-7.45106225263504562,-38.9589695374422647],"rgb":[0.133333333333333331,0.2,0.4],"xyz":[0.042414649718663211,0.0366689998593501473,0.130550748461089494],"hpluv":[259.172698399253136,223.320859110196579,22.538163137523668],"hsluv":[259.172698399253136,68.7622962285398103,22.538163137523668]},"#223377":{"lch":[23.8014699151847751,51.4679604591091,261.393782909523054],"luv":[23.8014699151847751,-7.70180102721624227,-50.8884389105972517],"rgb":[0.133333333333333331,0.2,0.466666666666666674],"xyz":[0.0517286627388651304,0.0403946050674309637,0.179604550367487403],"hpluv":[261.393782909523054,274.392670476726266,23.8014699151847751],"hsluv":[261.393782909523054,74.3066649079193837,23.8014699151847751]},"#223388":{"lch":[25.214303338898695,62.8010579136007649,262.667168962641028],"luv":[25.214303338898695,-8.01548446335257658,-62.2874376089207189],"rgb":[0.133333333333333331,0.2,0.533333333333333326],"xyz":[0.0628690798408089585,0.0448507719082085615,0.238277413771059571],"hpluv":[262.667168962641028,316.052560368409843,25.214303338898695],"hsluv":[262.667168962641028,78.8060188103052184,25.214303338898695]},"#223399":{"lch":[26.7557115473943199,73.7050787305795723,263.468936800572067],"luv":[26.7557115473943199,-8.38335327394953111,-73.2267575314180732],"rgb":[0.133333333333333331,0.2,0.6],"xyz":[0.0759260439498816719,0.0500735575518377191,0.307044091412177478],"hpluv":[263.468936800572067,349.558796760350788,26.7557115473943199],"hsluv":[263.468936800572067,82.4151943494814105,26.7557115473943199]},"#2233aa":{"lch":[28.4055164492709622,84.2578478733767184,264.007358934798901],"luv":[28.4055164492709622,-8.79658068794244485,-83.7974050699281],"rgb":[0.133333333333333331,0.2,0.66666666666666663],"xyz":[0.090983736915134214,0.0560966347379388205,0.386347941029176078],"hpluv":[264.007358934798901,376.397732872534505,28.4055164492709622],"hsluv":[264.007358934798901,85.301735461295209,28.4055164492709622]},"#2233bb":{"lch":[30.1452579578322855,94.5340435151979506,264.386468786418391],"luv":[30.1452579578322855,-9.24712484322681583,-94.0806891209195868],"rgb":[0.133333333333333331,0.2,0.733333333333333282],"xyz":[0.108121329022470861,0.0629516715808735861,0.476605926127817825],"hpluv":[264.386468786418391,397.931717533943186,30.1452579578322855],"hsluv":[264.386468786418391,87.6154589527103553,30.1452579578322855]},"#2233cc":{"lch":[31.9586404471462444,104.593501952517812,264.663323368501551],"luv":[31.9586404471462444,-9.72802821613955615,-104.140127317558722],"rgb":[0.133333333333333331,0.2,0.8],"xyz":[0.127413704466192601,0.0706686217583623877,0.578212436798088],"hpluv":[264.663323368501551,415.294074833826699,31.9586404471462444],"hsluv":[264.663323368501551,89.4797268300864,31.9586404471462444]},"#2233dd":{"lch":[33.8316358854510284,114.480561125644712,264.87147297862515],"luv":[33.8316358854510284,-10.233440845943651,-114.022259072932982],"rgb":[0.133333333333333331,0.2,0.866666666666666696],"xyz":[0.148932030016826145,0.0792759519786159217,0.691542284698094],"hpluv":[264.87147297862515,429.386195390892226,33.8316358854510284],"hsluv":[264.87147297862515,90.9921469937159,33.8316358854510284]},"#2233ee":{"lch":[35.7523793143002209,124.226868169083772,265.031742065807748],"luv":[35.7523793143002209,-10.7585230682670669,-123.760126682580946],"rgb":[0.133333333333333331,0.2,0.933333333333333348],"xyz":[0.172744210283000882,0.0888008240850859387,0.816953100766617202],"hpluv":[265.031742065807748,440.909964314972513,35.7523793143002209],"hsluv":[265.031742065807748,93.1343838757889557,35.7523793143002209]},"#2233ff":{"lch":[37.7109573358094536,133.854664271403067,265.157628752861342],"luv":[37.7109573358094536,-11.2993072928814158,-133.376897556927247],"rgb":[0.133333333333333331,0.2,1],"xyz":[0.198915258692832275,0.0992692434490186487,0.954787289058399],"hpluv":[265.157628752861342,450.407151983715153,37.7109573358094536],"hsluv":[265.157628752861342,99.9999999999994884,37.7109573358094536]},"#77cc00":{"lch":[74.2578384949046892,97.2071675743180776,115.806356387580706],"luv":[74.2578384949046892,-42.3172913249353897,87.5127435448238202],"rgb":[0.466666666666666674,0.8,0],"xyz":[0.291994990493124773,0.47106497957174448,0.0755391193650810505],"hpluv":[115.806356387580706,166.109821583261578,74.2578384949046892],"hsluv":[115.806356387580706,100.000000000002331,74.2578384949046892]},"#77cc11":{"lch":[74.283676291108776,96.1575231653334441,116.077287726142885],"luv":[74.283676291108776,-42.2692272766684525,86.3688698937585144],"rgb":[0.466666666666666674,0.8,0.0666666666666666657],"xyz":[0.293006655992761877,0.471469645771599333,0.0808672243298366872],"hpluv":[116.077287726142885,164.2590118749402,74.283676291108776],"hsluv":[116.077287726142885,98.5840266713216522,74.283676291108776]},"#77cc22":{"lch":[74.3315335987815331,94.2328042547367914,116.591431825394338],"luv":[74.3315335987815331,-42.1809937248656581,84.2649699821603519],"rgb":[0.466666666666666674,0.8,0.133333333333333331],"xyz":[0.294882014131238934,0.472219789026990133,0.0907441105258158798],"hpluv":[116.591431825394338,160.867513369155,74.3315335987815331],"hsluv":[116.591431825394338,95.9817736999432611,74.3315335987815331]},"#77cc33":{"lch":[74.410219680569412,91.1231466477069176,117.473161926834081],"luv":[74.410219680569412,-42.0381214847294515,80.8469182901527148],"rgb":[0.466666666666666674,0.8,0.2],"xyz":[0.29796976486369664,0.47345488931997326,0.10700626438342703],"hpluv":[117.473161926834081,155.394430123968846,74.410219680569412],"hsluv":[117.473161926834081,91.7599970515073551,74.410219680569412]},"#77cc44":{"lch":[74.5235830687713445,86.7634188586395,118.828926444460791],"luv":[74.5235830687713445,-41.8369758074171045,76.0102513305232748],"rgb":[0.466666666666666674,0.8,0.266666666666666663],"xyz":[0.302427760107915555,0.475238087417660826,0.130485039336313668],"hpluv":[118.828926444460791,147.734611978654129,74.5235830687713445],"hsluv":[118.828926444460791,85.7987877580492153,74.5235830687713445]},"#77cc55":{"lch":[74.6747602628398,81.1723246922669688,120.810965560633605],"luv":[74.6747602628398,-41.5770530159641396,69.7158156981220571],"rgb":[0.466666666666666674,0.8,0.333333333333333315],"xyz":[0.30839017414727754,0.477623053033405687,0.161887086610287556],"hpluv":[120.810965560633605,137.934680546119694,74.6747602628398],"hsluv":[120.810965560633605,78.0638956664307671,74.6747602628398]},"#77cc66":{"lch":[74.866352492363319,74.4599116278811692,123.651008617564372],"luv":[74.866352492363319,-41.2606832822808798,61.982533464768764],"rgb":[0.466666666666666674,0.8,0.4],"xyz":[0.315975169920785726,0.480657051342809,0.201834731017431546],"hpluv":[123.651008617564372,126.204595224585091,74.866352492363319],"hsluv":[123.651008617564372,68.5978490432360388,74.866352492363319]},"#77cc77":{"lch":[75.1005189251371519,66.8471540376336719,127.715012949239053],"luv":[75.1005189251371519,-40.8926996459993219,52.8803282761483189],"rgb":[0.466666666666666674,0.8,0.466666666666666674],"xyz":[0.325289182940987631,0.484382656550889801,0.250888532923829455],"hpluv":[127.715012949239053,112.948199149412858,75.1005189251371519],"hsluv":[127.715012949239053,57.5107647824030153,75.1005189251371519]},"#77cc88":{"lch":[75.3790318815133,58.7087525381891311,133.590980823477452],"luv":[75.3790318815133,-40.4800101313413947,42.5215992685696946],"rgb":[0.466666666666666674,0.8,0.533333333333333326],"xyz":[0.336429600042931432,0.488838823391667399,0.309561396327401595],"hpluv":[133.590980823477452,98.8306436104363542,75.3790318815133],"hsluv":[133.590980823477452,58.7660231359317748,75.3790318815133]},"#77cc99":{"lch":[75.7033128937519848,50.6622606125816759,142.200076129410462],"luv":[75.7033128937519848,-40.0310804193600305,31.0512036938324556],"rgb":[0.466666666666666674,0.8,0.6],"xyz":[0.349486564152004187,0.494061609035296556,0.378328073968519529],"hpluv":[142.200076129410462,84.9198084445103802,75.7033128937519848],"hsluv":[142.200076129410462,60.1459848784672815,75.7033128937519848]},"#77ccaa":{"lch":[76.0744587192654,43.7254195479873289,154.773618890386814],"luv":[76.0744587192654,-39.5553661600219684,18.6355928962322395],"rgb":[0.466666666666666674,0.8,0.66666666666666663],"xyz":[0.364544257117256687,0.500084686221397678,0.45763192358551813],"hpluv":[154.773618890386814,73.067319501552376,76.0744587192654],"hsluv":[154.773618890386814,61.6269813218812459,76.0744587192654]},"#77ccbb":{"lch":[76.4932621718819235,39.4413790272598561,172.054536865796422],"luv":[76.4932621718819235,-39.0627454070693716,5.45199970968949721],"rgb":[0.466666666666666674,0.8,0.733333333333333282],"xyz":[0.381681849224593361,0.506939723064332437,0.547889908684159876],"hpluv":[172.054536865796422,67.3385729198675733,76.4932621718819235],"hsluv":[172.054536865796422,63.1840553036415713,76.4932621718819235]},"#77cccc":{"lch":[76.9602305249106,39.4506200876857847,192.177050630060961],"luv":[76.9602305249106,-38.5629992967744357,-8.32144882456723778],"rgb":[0.466666666666666674,0.8,0.8],"xyz":[0.400974224668315116,0.514656673241821183,0.64949641935443],"hpluv":[192.177050630060961,69.0111626451701312,76.9602305249106],"hsluv":[192.177050630060961,64.792316690459316,76.9602305249106]},"#77ccdd":{"lch":[77.4756030772436475,44.2250390945155942,210.602473757279853],"luv":[77.4756030772436475,-38.0653777824919359,-22.5140200139291444],"rgb":[0.466666666666666674,0.8,0.866666666666666696],"xyz":[0.422492550218948604,0.5232640034620748,0.762826267254436],"hpluv":[210.602473757279853,79.5030322309331439,77.4756030772436475],"hsluv":[210.602473757279853,66.4280770246703156,77.4756030772436475]},"#77ccee":{"lch":[78.0393687787239116,52.7144148097026246,224.531483635923479],"luv":[78.0393687787239116,-37.5782716813780482,-36.9686762837126182],"rgb":[0.466666666666666674,0.8,0.933333333333333348],"xyz":[0.446304730485123369,0.532788875568544817,0.888237083322959253],"hpluv":[224.531483635923479,97.6926138158747648,78.0393687787239116],"hsluv":[224.531483635923479,71.3781641956177282,78.0393687787239116]},"#77ccff":{"lch":[78.6512843692400736,63.5145451324089052,234.249283216901347],"luv":[78.6512843692400736,-37.1089966152940249,-51.5462880679385549],"rgb":[0.466666666666666674,0.8,1],"xyz":[0.472475778894954734,0.54325729493247743,1.0260712716147411],"hpluv":[234.249283216901347,121.749546403725816,78.6512843692400736],"hsluv":[234.249283216901347,99.9999999999968168,78.6512843692400736]},"#224400":{"lch":[25.1809799681870601,33.4179584834008523,116.999863609689683],"luv":[25.1809799681870601,-15.1713647924420147,29.7756551486773162],"rgb":[0.133333333333333331,0.266666666666666663,0],"xyz":[0.0272670407739683193,0.0447420690542179589,0.00719932696972761486],"hpluv":[116.999863609689683,168.401755360818214,25.1809799681870601],"hsluv":[116.999863609689683,100.000000000002217,25.1809799681870601]},"#224411":{"lch":[25.304760275593587,29.8643723317269938,120.153298663054528],"luv":[25.304760275593587,-15.0013316265206242,25.8232605261065515],"rgb":[0.133333333333333331,0.266666666666666663,0.0666666666666666657],"xyz":[0.0282787062736054411,0.0451467352540728117,0.0125274319344832463],"hpluv":[120.153298663054528,149.758158325470362,25.304760275593587],"hsluv":[120.153298663054528,85.5029145399767287,25.304760275593587]},"#224422":{"lch":[25.5322735505540379,24.0578500603585184,127.715012949239281],"luv":[25.5322735505540379,-14.7170130248608242,19.0312815425753499],"rgb":[0.133333333333333331,0.266666666666666663,0.133333333333333331],"xyz":[0.0301540644120824597,0.0458968785094636331,0.0224043181304624424],"hpluv":[127.715012949239281,119.565711231297882,25.5322735505540379],"hsluv":[127.715012949239281,60.8802579098014363,25.5322735505540379]},"#224433":{"lch":[25.9015299317797343,16.981905402382786,147.498859327993841],"luv":[25.9015299317797343,-14.3222120947549634,9.12465625699578098],"rgb":[0.133333333333333331,0.266666666666666663,0.2],"xyz":[0.0332418151445402,0.0471319788024467459,0.0386664719880735891],"hpluv":[147.498859327993841,83.1955939146196357,25.9015299317797343],"hsluv":[147.498859327993841,64.09678513773909,25.9015299317797343]},"#224444":{"lch":[26.423438440277998,14.1959776348833024,192.177050630061],"luv":[26.423438440277998,-13.8765746732054378,-2.99440417262938974],"rgb":[0.133333333333333331,0.266666666666666663,0.266666666666666663],"xyz":[0.0376998103887590807,0.048915176900134319,0.0621452469409602276],"hpluv":[192.177050630061,68.1734546180548762,26.423438440277998],"hsluv":[192.177050630061,67.9066037165366367,26.423438440277998]},"#224455":{"lch":[27.1020089847707979,21.1158760403194243,230.453768193421723],"luv":[27.1020089847707979,-13.4444916811297333,-16.2826860310628518],"rgb":[0.133333333333333331,0.266666666666666663,0.333333333333333315],"xyz":[0.0436622244281210728,0.0513001425158791519,0.0935472942149341291],"hpluv":[230.453768193421723,98.865995891455,27.1020089847707979],"hsluv":[230.453768193421723,71.8953118433392717,27.1020089847707979]},"#224466":{"lch":[27.935501760142138,32.6185654266362306,246.366656325875056],"luv":[27.935501760142138,-13.0762038460066634,-29.8828329224226863],"rgb":[0.133333333333333331,0.266666666666666663,0.4],"xyz":[0.0512472202016292511,0.0543341408252824634,0.133494938622078091],"hpluv":[246.366656325875056,148.165710166053657,27.935501760142138],"hsluv":[246.366656325875056,75.7322080769735351,27.935501760142138]},"#224477":{"lch":[28.9175817086007072,45.1016041758277453,253.511262930840303],"luv":[28.9175817086007072,-12.8010466495854338,-43.2468253621948548],"rgb":[0.133333333333333331,0.266666666666666663,0.466666666666666674],"xyz":[0.0605612332218311636,0.0580597460333632798,0.182548740528476],"hpluv":[253.511262930840303,197.910731630760921,28.9175817086007072],"hsluv":[253.511262930840303,79.2164102145581381,28.9175817086007072]},"#224488":{"lch":[30.0385370730522183,57.5093846577101502,257.31357801273],"luv":[30.0385370730522183,-12.6299243743950456,-56.1053859625395432],"rgb":[0.133333333333333331,0.266666666666666663,0.533333333333333326],"xyz":[0.0717016503237749847,0.0625159128741408776,0.241221603932048168],"hpluv":[257.31357801273,242.94013543841632,30.0385370730522183],"hsluv":[257.31357801273,82.2624245727818106,30.0385370730522183]},"#224499":{"lch":[31.2864747985506213,69.5224780150470139,259.59064804278853],"luv":[31.2864747985506213,-12.5612993437317204,-68.3782765807230675],"rgb":[0.133333333333333331,0.266666666666666663,0.6],"xyz":[0.084758614432847712,0.0677386985177700351,0.309988281573166102],"hpluv":[259.59064804278853,281.973268281811727,31.2864747985506213],"hsluv":[259.59064804278853,84.8626039888072796,31.2864747985506213]},"#2244aa":{"lch":[32.6483868166792277,81.0719578322086676,261.068394107427196],"luv":[32.6483868166792277,-12.5868550230291323,-80.0889095154670372],"rgb":[0.133333333333333331,0.266666666666666663,0.66666666666666663],"xyz":[0.0998163073981002402,0.0737617757038711297,0.389292131190164703],"hpluv":[261.068394107427196,315.099896322028769,32.6483868166792277],"hsluv":[261.068394107427196,87.0515845849570695,32.6483868166792277]},"#2244bb":{"lch":[34.1110146972578292,92.183713113584,262.084105297610279],"luv":[34.1110146972578292,-12.6954785449376448,-91.3053217940914266],"rgb":[0.133333333333333331,0.266666666666666663,0.733333333333333282],"xyz":[0.116953899505436887,0.0806168125468058883,0.479550116288806449],"hpluv":[262.084105297610279,342.924793116778346,34.1110146972578292],"hsluv":[262.084105297610279,88.8814901896574128,34.1110146972578292]},"#2244cc":{"lch":[35.6614866183058439,102.915147630289923,262.812952152813068],"luv":[35.6614866183058439,-12.875606592693158,-102.106544191029045],"rgb":[0.133333333333333331,0.266666666666666663,0.8],"xyz":[0.136246274949158641,0.0883337627242946899,0.58115662695907655],"hpluv":[262.812952152813068,366.200711918304478,35.6614866183058439],"hsluv":[262.812952152813068,90.4075680541016453,35.6614866183058439]},"#2244dd":{"lch":[37.2877389569632456,113.32806156453313,263.353781276912969],"luv":[37.2877389569632456,-13.1164125913708496,-112.566465959927598],"rgb":[0.133333333333333331,0.266666666666666663,0.866666666666666696],"xyz":[0.157764600499792185,0.0969410929445482239,0.694486474859082548],"hpluv":[263.353781276912969,385.665452845521429,37.2877389569632456],"hsluv":[263.353781276912969,91.6811227311716408,37.2877389569632456]},"#2244ee":{"lch":[38.9787575249373575,123.477944203962494,263.76603971616106],"luv":[38.9787575249373575,-13.4082956886680194,-122.74779147325674],"rgb":[0.133333333333333331,0.266666666666666663,0.933333333333333348],"xyz":[0.181576780765966922,0.106465965051018269,0.819897290927605771],"hpluv":[263.76603971616106,401.976556214066079,38.9787575249373575],"hsluv":[263.76603971616106,92.7467647141115208,38.9787575249373575]},"#2244ff":{"lch":[40.7246816385265333,133.410810959437413,264.087324287658078],"luv":[40.7246816385265333,-13.7429938745542355,-132.701072340123574],"rgb":[0.133333333333333331,0.266666666666666663,1],"xyz":[0.207747829175798315,0.116934384414950965,0.957731479219387616],"hpluv":[264.087324287658078,415.692943868353552,40.7246816385265333],"hsluv":[264.087324287658078,99.9999999999994174,40.7246816385265333]},"#77dd00":{"lch":[79.4046595803128525,106.500737968556749,117.886764510297155],"luv":[79.4046595803128525,-49.8131268255250319,94.1332012826045457],"rgb":[0.466666666666666674,0.866666666666666696,0],"xyz":[0.3346289810403,0.556332960666096,0.089750449547472369],"hpluv":[117.886764510297155,213.048114553231642,79.4046595803128525],"hsluv":[117.886764510297155,100.000000000002288,79.4046595803128525]},"#77dd11":{"lch":[79.427785829561941,105.558219679332908,118.124490997004173],"luv":[79.427785829561941,-49.7589732732473422,93.0944806133132801],"rgb":[0.466666666666666674,0.866666666666666696,0.0666666666666666657],"xyz":[0.335640646539937082,0.556737626865951,0.095078554512228],"hpluv":[118.124490997004173,211.44368721760469,79.427785829561941],"hsluv":[118.124490997004173,98.7993418471683498,79.427785829561941]},"#77dd22":{"lch":[79.4706261070012658,103.827566318990023,118.57359388187615],"luv":[79.4706261070012658,-49.6593926395600249,91.1817319993116],"rgb":[0.466666666666666674,0.866666666666666696,0.133333333333333331],"xyz":[0.337516004678414139,0.557487770121341764,0.104955440708207198],"hpluv":[118.57359388187615,208.49071074424171,79.4706261070012658],"hsluv":[118.57359388187615,96.5898934808467,79.4706261070012658]},"#77dd33":{"lch":[79.5410783702752582,101.02472031386101,119.337697767432388],"luv":[79.5410783702752582,-49.4976803830995067,88.0680063995226874],"rgb":[0.466666666666666674,0.866666666666666696,0.2],"xyz":[0.340603755410871845,0.55872287041432489,0.121217594565818348],"hpluv":[119.337697767432388,203.689012727245881,79.5410783702752582],"hsluv":[119.337697767432388,92.9973786049096276,79.5410783702752582]},"#77dd44":{"lch":[79.6426121547728485,97.0801788178617073,120.498016398630853],"luv":[79.6426121547728485,-49.2690191092090188,83.6488187323909642],"rgb":[0.466666666666666674,0.866666666666666696,0.266666666666666663],"xyz":[0.34506175065509076,0.560506068512012456,0.144696369518705],"hpluv":[120.498016398630853,196.890303230407255,79.6426121547728485],"hsluv":[120.498016398630853,87.9077551580221126,79.6426121547728485]},"#77dd55":{"lch":[79.7780740091910729,91.9929116678805912,122.163902680224],"luv":[79.7780740091910729,-48.9717875622404577,77.8746417012192325],"rgb":[0.466666666666666674,0.866666666666666696,0.333333333333333315],"xyz":[0.351024164694452745,0.562891034127757206,0.176098416792678902],"hpluv":[122.163902680224,188.049297266091,79.7780740091910729],"hsluv":[122.163902680224,81.2740392578912889,79.7780740091910729]},"#77dd66":{"lch":[79.9498479155991788,85.8342168020908645,124.492086237753256],"luv":[79.9498479155991788,-48.6072648253697466,70.7449403153661],"rgb":[0.466666666666666674,0.866666666666666696,0.4],"xyz":[0.35860916046796093,0.565925032437160525,0.216046061199822836],"hpluv":[124.492086237753256,177.233761467474238,79.9498479155991788],"hsluv":[124.492086237753256,73.1095665987697174,79.9498479155991788]},"#77dd77":{"lch":[80.1599403321921,78.7586110849663896,127.715012949239352],"luv":[80.1599403321921,-48.1793469594900188,62.3030444407821093],"rgb":[0.466666666666666674,0.866666666666666696,0.466666666666666674],"xyz":[0.367923173488162836,0.569650637645241376,0.265099863106220746],"hpluv":[127.715012949239352,164.652954432832814,80.1599403321921],"hsluv":[127.715012949239352,63.4818861677236654,80.1599403321921]},"#77dd88":{"lch":[80.4100305893493754,71.0258644695610855,132.183293417803185],"luv":[80.4100305893493754,-47.69419139775556,52.6301959958607597],"rgb":[0.466666666666666674,0.866666666666666696,0.533333333333333326],"xyz":[0.379063590590106636,0.574106804486018918,0.323772726509792941],"hpluv":[132.183293417803185,150.716476916292407,80.4100305893493754],"hsluv":[132.183293417803185,64.4129914780986184,80.4100305893493754]},"#77dd99":{"lch":[80.7015034668449829,63.0436351039456042,138.421724913218469],"luv":[80.7015034668449829,-47.1597772740100254,41.838443261971527],"rgb":[0.466666666666666674,0.866666666666666696,0.6],"xyz":[0.392120554699179391,0.579329590129648131,0.39253940415091082],"hpluv":[138.421724913218469,136.149613262507529,80.7015034668449829],"hsluv":[138.421724913218469,65.4455974050235909,80.7015034668449829]},"#77ddaa":{"lch":[81.0354720941451916,55.4433276111880673,147.164903113494319],"luv":[81.0354720941451916,-46.5854032558204594,30.0626475895607292],"rgb":[0.466666666666666674,0.866666666666666696,0.66666666666666663],"xyz":[0.407178247664431892,0.585352667315749198,0.471843253767909421],"hpluv":[147.164903113494319,122.204418643173668,81.0354720941451916],"hsluv":[147.164903113494319,66.5644374268903505,81.0354720941451916]},"#77ddbb":{"lch":[81.4127955433910415,49.1817473647014651,159.215751775487576],"luv":[81.4127955433910415,-45.9811599326092804,17.452140415923953],"rgb":[0.466666666666666674,0.866666666666666696,0.733333333333333282],"xyz":[0.424315839771768566,0.592207704158684,0.562101238866551167],"hpluv":[159.215751775487576,110.971854928909636,81.4127955433910415],"hsluv":[159.215751775487576,67.7527973989238461,81.4127955433910415]},"#77ddcc":{"lch":[81.8340936239464298,45.5480287935550479,174.756411210455894],"luv":[81.8340936239464298,-45.3574175840417197,4.16264304083146897],"rgb":[0.466666666666666674,0.866666666666666696,0.8],"xyz":[0.44360821521549032,0.599924654336172813,0.663707749536821323],"hpluv":[174.756411210455894,105.54602277980743,81.8340936239464298],"hsluv":[174.756411210455894,68.9933910523739371,81.8340936239464298]},"#77dddd":{"lch":[82.299760373596115,45.7538051586653296,192.177050630060847],"luv":[82.299760373596115,-44.7243656053231931,-9.65100034703627],"rgb":[0.466666666666666674,0.866666666666666696,0.866666666666666696],"xyz":[0.465126540766123808,0.60853198455642632,0.777037597436827321],"hpluv":[192.177050630060847,109.256318691998032,82.299760373596115],"hsluv":[192.177050630060847,70.2691471280789557,82.299760373596115]},"#77ddee":{"lch":[82.8099771424448221,50.1244355238275574,208.400620203695752],"luv":[82.8099771424448221,-44.0916301090396274,-23.8408722766172367],"rgb":[0.466666666666666674,0.866666666666666696,0.933333333333333348],"xyz":[0.488938721032298573,0.618056856662896337,0.902448413505350544],"hpluv":[208.400620203695752,123.794681382199428,82.8099771424448221],"hsluv":[208.400620203695752,71.5638635335918,82.8099771424448221]},"#77ddff":{"lch":[83.364725787715372,57.9146258331384161,221.361714592462022],"luv":[83.364725787715372,-43.4679842885535521,-38.2705921992655504],"rgb":[0.466666666666666674,0.866666666666666696,1],"xyz":[0.515109769442129939,0.628525276026829061,1.0402826017971325],"hpluv":[221.361714592462022,148.517135389694232,83.364725787715372],"hsluv":[221.361714592462022,99.9999999999953531,83.364725787715372]},"#225500":{"lch":[31.4325909084541877,43.9203091385023825,121.065637009975248],"luv":[31.4325909084541877,-22.6637443991712324,37.6211143459447541],"rgb":[0.133333333333333331,0.333333333333333315,0],"xyz":[0.0390802974883142848,0.0683685824829102229,0.0111370792078428239],"hpluv":[121.065637009975248,177.306450001223254,31.4325909084541877],"hsluv":[121.065637009975248,100.000000000002373,31.4325909084541877]},"#225511":{"lch":[31.5259896590935043,41.027632513747335,123.178290947815412],"luv":[31.5259896590935043,-22.452213479362328,34.3389682366875704],"rgb":[0.133333333333333331,0.333333333333333315,0.0666666666666666657],"xyz":[0.04009196298795141,0.0687732486827650757,0.0164651841725984571],"hpluv":[123.178290947815412,165.138012967735222,31.5259896590935043],"hsluv":[123.178290947815412,90.3912533003173877,31.5259896590935043]},"#225522":{"lch":[31.6981615382414716,36.1013694166085486,127.715012949239792],"luv":[31.6981615382414716,-22.0844473877159331,28.5584673491706482],"rgb":[0.133333333333333331,0.333333333333333315,0.133333333333333331],"xyz":[0.0419673211264284252,0.0695233919381558901,0.0263420703685776497],"hpluv":[127.715012949239792,144.520324593974209,31.6981615382414716],"hsluv":[127.715012949239792,73.5866039174798345,31.6981615382414716]},"#225533":{"lch":[31.9789617713411829,29.2625646618711706,137.400330584271074],"luv":[31.9789617713411829,-21.5402028917408828,19.8070025489176231],"rgb":[0.133333333333333331,0.333333333333333315,0.2],"xyz":[0.0450550718588861657,0.070758492231139,0.0426042242261888],"hpluv":[137.400330584271074,116.114739730974975,31.9789617713411829],"hsluv":[137.400330584271074,75.0932106461487,31.9789617713411829]},"#225544":{"lch":[32.3786649626227785,22.5648709259736577,157.632626155133437],"luv":[32.3786649626227785,-20.8671549807805796,8.58692278490586247],"rgb":[0.133333333333333331,0.333333333333333315,0.266666666666666663],"xyz":[0.0495130671031050462,0.072541690328826583,0.0660829991790754384],"hpluv":[157.632626155133437,88.4327727432137465,32.3786649626227785],"hsluv":[157.632626155133437,76.9882679005547459,32.3786649626227785]},"#225555":{"lch":[32.9031430542149863,20.5945867127178737,192.177050630061132],"luv":[32.9031430542149863,-20.1312179923833199,-4.34408379417313384],"rgb":[0.133333333333333331,0.333333333333333315,0.333333333333333315],"xyz":[0.0554754811424670383,0.0749266559445714159,0.0974850464530493399],"hpluv":[192.177050630061132,79.4245973683706268,32.9031430542149863],"hsluv":[192.177050630061132,79.1137061933639245,32.9031430542149863]},"#225566":{"lch":[33.5545056011551,26.6006796322727546,223.177731373198952],"luv":[33.5545056011551,-19.398136743372234,-18.2018803364993715],"rgb":[0.133333333333333331,0.333333333333333315,0.4],"xyz":[0.0630604769159752165,0.0779606542539747344,0.137432690860193302],"hpluv":[223.177731373198952,100.596116474312993,33.5545056011551],"hsluv":[223.177731373198952,81.3097794801720113,33.5545056011551]},"#225577":{"lch":[34.3316296590174,37.3644949384619807,239.932022094073261],"luv":[34.3316296590174,-18.7206257951117045,-32.3364137134245127],"rgb":[0.133333333333333331,0.333333333333333315,0.466666666666666674],"xyz":[0.0723744899361771221,0.0816862594620555438,0.186486492766591211],"hpluv":[239.932022094073261,138.103289638438355,34.3316296590174],"hsluv":[239.932022094073261,83.4469752602442298,34.3316296590174]},"#225588":{"lch":[35.230707776085,49.7211482448765594,248.610781811292384],"luv":[35.230707776085,-18.1333810040152876,-46.2965773697387775],"rgb":[0.133333333333333331,0.333333333333333315,0.533333333333333326],"xyz":[0.0835149070381209502,0.0861424263028331416,0.245159356170163378],"hpluv":[248.610781811292384,179.084957393431893,35.230707776085],"hsluv":[248.610781811292384,85.4385601139613158,35.230707776085]},"#225599":{"lch":[36.2458273864096512,62.3718238545276336,253.557833057747018],"luv":[36.2458273864096512,-17.6541819100499602,-59.8211858126123],"rgb":[0.133333333333333331,0.333333333333333315,0.6],"xyz":[0.0965718711471936775,0.0913652119464623,0.313926033811281313],"hpluv":[253.557833057747018,218.35832406203005,36.2458273864096512],"hsluv":[253.557833057747018,87.2381583586425791,36.2458273864096512]},"#2255aa":{"lch":[37.3695533294905928,74.8190204360075,256.640324292617947],"luv":[37.3695533294905928,-17.2879233745104131,-72.7943234352841841],"rgb":[0.133333333333333331,0.333333333333333315,0.66666666666666663],"xyz":[0.111629564112446206,0.0973882891325634,0.393229883428279914],"hpluv":[256.640324292617947,254.058329165629146,37.3695533294905928],"hsluv":[256.640324292617947,88.8301308270208807,37.3695533294905928]},"#2255bb":{"lch":[38.5934754222231291,86.8805221786483,258.695157685582501],"luv":[38.5934754222231291,-17.0311035792378,-85.1948745225196831],"rgb":[0.133333333333333331,0.333333333333333315,0.733333333333333282],"xyz":[0.128767156219782852,0.104243325975498152,0.48348786852692166],"hpluv":[258.695157685582501,285.658965815982469,38.5934754222231291],"hsluv":[258.695157685582501,90.2188193496234874,38.5934754222231291]},"#2255cc":{"lch":[39.9086891196534097,98.5112685419302,260.136263381096],"luv":[39.9086891196534097,-16.8755295039118458,-97.0550695930042],"rgb":[0.133333333333333331,0.333333333333333315,0.8],"xyz":[0.148059531663504607,0.111960276152986954,0.585094379197191761],"hpluv":[260.136263381096,313.22597901471056,39.9086891196534097],"hsluv":[260.136263381096,91.4196885710314433,39.9086891196534097]},"#2255dd":{"lch":[41.3061900239028503,109.727240919989157,261.187210775965298],"luv":[41.3061900239028503,-16.8109176424286737,-108.431823962952976],"rgb":[0.133333333333333331,0.333333333333333315,0.866666666666666696],"xyz":[0.16957785721413815,0.120567606373240488,0.698424227097197758],"hpluv":[261.187210775965298,337.084394380702577,41.3061900239028503],"hsluv":[261.187210775965298,92.4531471705562353,41.3061900239028503]},"#2255ee":{"lch":[42.7771763125841602,120.569176865205208,261.977669343247612],"luv":[42.7771763125841602,-16.8265186863462972,-119.389256965822398],"rgb":[0.133333333333333331,0.333333333333333315,0.933333333333333348],"xyz":[0.193390037480312887,0.130092478479710533,0.823835043165721],"hpluv":[261.977669343247612,357.654347674158601,42.7771763125841602],"hsluv":[261.977669343247612,93.3407272081067,42.7771763125841602]},"#2255ff":{"lch":[44.3132637322964129,131.084767922007643,262.587267096581058],"luv":[44.3132637322964129,-16.9120289519133,-129.989229007238322],"rgb":[0.133333333333333331,0.333333333333333315,1],"xyz":[0.219561085890144281,0.140560897843643229,0.961669231457502827],"hpluv":[262.587267096581058,375.368494421854962,44.3132637322964129],"hsluv":[262.587267096581058,99.9999999999993463,44.3132637322964129]},"#77ee00":{"lch":[84.5193058633960703,115.647601499010833,119.483871035599748],"luv":[84.5193058633960703,-56.9192667804191,100.670575649757225],"rgb":[0.466666666666666674,0.933333333333333348,0],"xyz":[0.381807757380814794,0.650690513347127,0.105476708327643554],"hpluv":[119.483871035599748,321.869538605652735,84.5193058633960703],"hsluv":[119.483871035599748,100.000000000002359,84.5193058633960703]},"#77ee11":{"lch":[84.5401392884337355,114.795422022346642,119.691827475064969],"luv":[84.5401392884337355,-56.862163274863434,99.7230329712822083],"rgb":[0.466666666666666674,0.933333333333333348,0.0666666666666666657],"xyz":[0.382819422880451898,0.651095179546981928,0.11080481329239919],"hpluv":[119.691827475064969,319.985367790437692,84.5401392884337355],"hsluv":[119.691827475064969,98.9722780394741477,84.5401392884337355]},"#77ee22":{"lch":[84.5787360835130499,113.228793274865538,120.083373039169615],"luv":[84.5787360835130499,-56.7570256545281637,97.9765260934141651],"rgb":[0.466666666666666674,0.933333333333333348,0.133333333333333331],"xyz":[0.384694781018928955,0.651845322802372729,0.120681699488378383],"hpluv":[120.083373039169615,316.512949618186383,84.5787360835130499],"hsluv":[120.083373039169615,97.0790709390529,84.5787360835130499]},"#77ee33":{"lch":[84.6422206992536275,110.686316006337648,120.745647293955486],"luv":[84.6422206992536275,-56.585921413754825,95.1288286946244313],"rgb":[0.466666666666666674,0.933333333333333348,0.2],"xyz":[0.387782531751386661,0.653080423095355855,0.136943853345989519],"hpluv":[120.745647293955486,310.853610471304819,84.6422206992536275],"hsluv":[120.745647293955486,93.9952250088888093,84.6422206992536275]},"#77ee44":{"lch":[84.7337366993681513,107.096480548503891,121.742158736883837],"luv":[84.7337366993681513,-56.3431952848259598,91.0774422728923412],"rgb":[0.466666666666666674,0.933333333333333348,0.266666666666666663],"xyz":[0.392240526995605576,0.654863621193043421,0.160422628298876158],"hpluv":[121.742158736883837,302.811644310583176,84.7337366993681513],"hsluv":[121.742158736883837,89.6144795711786628,84.7337366993681513]},"#77ee55":{"lch":[84.8558768548969766,102.444399932600575,123.154228511336783],"luv":[84.8558768548969766,-56.0262879596216621,85.7666026785261266],"rgb":[0.466666666666666674,0.933333333333333348,0.333333333333333315],"xyz":[0.398202941034967561,0.657248586808788171,0.191824675572850073],"hpluv":[123.154228511336783,292.298881153811294,84.8558768548969766],"hsluv":[123.154228511336783,83.883891015311761,84.8558768548969766]},"#77ee66":{"lch":[85.0108293450033159,96.7735040486586,125.092887734820366],"luv":[85.0108293450033159,-55.6354444449268186,79.1821217654036],"rgb":[0.466666666666666674,0.933333333333333348,0.4],"xyz":[0.405787936808475747,0.66028258511819149,0.231772319979994035],"hpluv":[125.092887734820366,279.341716913636219,85.0108293450033159],"hsluv":[125.092887734820366,76.7984153057879411,85.0108293450033159]},"#77ee77":{"lch":[85.2004556710930814,90.1918871908529667,127.71501294923965],"luv":[85.2004556710930814,-55.1734745704367882,71.3474892261312306],"rgb":[0.466666666666666674,0.933333333333333348,0.466666666666666674],"xyz":[0.415101949828677652,0.664008190326272341,0.280826121886391944],"hpluv":[127.71501294923965,264.105357222364148,85.2004556710930814],"hsluv":[127.71501294923965,68.3966317942099522,85.2004556710930814]},"#77ee88":{"lch":[85.4263369442757323,82.8846309918514521,131.246169999264879],"luv":[85.4263369442757323,-54.6454688778224167,62.3196179848555],"rgb":[0.466666666666666674,0.933333333333333348,0.533333333333333326],"xyz":[0.426242366930621452,0.668464357167049883,0.339498985289964139],"hpluv":[131.246169999264879,246.94310788397641,85.4263369442757323],"hsluv":[131.246169999264879,69.0963798671032379,85.4263369442757323]},"#77ee99":{"lch":[85.6898036798217,75.1362836057233,136.010896750223054],"luv":[85.6898036798217,-54.0584447513185395,52.1837682153016473],"rgb":[0.466666666666666674,0.933333333333333348,0.6],"xyz":[0.439299331039694207,0.673687142810679096,0.408265662931082],"hpluv":[136.010896750223054,228.489214549916312,85.6898036798217],"hsluv":[136.010896750223054,69.8780652000076827,85.6898036798217]},"#77eeaa":{"lch":[85.9919564191548602,67.3699388554866232,142.461966648640384],"luv":[85.9919564191548602,-53.4209300451477489,41.0476904892764551],"rgb":[0.466666666666666674,0.933333333333333348,0.66666666666666663],"xyz":[0.454357024004946708,0.679710219996780163,0.487569512548080619],"hpluv":[142.461966648640384,209.826142071523691,85.9919564191548602],"hsluv":[142.461966648640384,70.7318226594993575,85.9919564191548602]},"#77eebb":{"lch":[86.3336811163097337,60.2064759054885243,151.166773543866697],"luv":[86.3336811163097337,-52.7425080063862524,29.0352818163428843],"rgb":[0.466666666666666674,0.933333333333333348,0.733333333333333282],"xyz":[0.471494616112283382,0.686565256839714921,0.577827497646722366],"hpluv":[151.166773543866697,192.758378827545073,86.3336811163097337],"hsluv":[151.166773543866697,71.6464628016145895,86.3336811163097337]},"#77eecc":{"lch":[86.7156615657761449,54.5207620102438284,162.626180625161169],"luv":[86.7156615657761449,-52.0333543078463094,16.2801575438255028],"rgb":[0.466666666666666674,0.933333333333333348,0.8],"xyz":[0.490786991556005137,0.694282207017203778,0.679434008316992522],"hpluv":[162.626180625161169,180.150974339718545,86.7156615657761449],"hsluv":[162.626180625161169,72.6100300694190111,86.7156615657761449]},"#77eedd":{"lch":[87.1383902516757445,51.3868015721907483,176.742975614533037],"luv":[87.1383902516757445,-51.3037971188162771,2.91955082350760353],"rgb":[0.466666666666666674,0.933333333333333348,0.866666666666666696],"xyz":[0.512305317106638625,0.702889537237457285,0.792763856216998519],"hpluv":[176.742975614533037,175.997429168902841,87.1383902516757445],"hsluv":[176.742975614533037,73.6103328445444305,87.1383902516757445]},"#77eeee":{"lch":[87.6021784736708327,51.7277775307036,192.177050630061075],"luv":[87.6021784736708327,-50.5639263491039941,-10.9111099540033418],"rgb":[0.466666666666666674,0.933333333333333348,0.933333333333333348],"xyz":[0.536117497372813334,0.712414409343927302,0.918174672285521742],"hpluv":[192.177050630061075,184.503896016801804,87.6021784736708327],"hsluv":[192.177050630061075,74.6354143053272594,87.6021784736708327]},"#77eeff":{"lch":[88.107166277409533,55.7808213308350673,206.722210113893169],"luv":[88.107166277409533,-49.8232704905303336,-25.0826981397535107],"rgb":[0.466666666666666674,0.933333333333333348,1],"xyz":[0.562288545782644755,0.72288282870786,1.05600886057730348],"hpluv":[206.722210113893169,208.278116369071,88.107166277409533],"hsluv":[206.722210113893169,99.9999999999928804,88.107166277409533]},"#226600":{"lch":[37.5582057574881532,54.1200869321592961,123.236537452327113],"luv":[37.5582057574881532,-29.6630419039077431,45.2668505040000824],"rgb":[0.133333333333333331,0.4,0],"xyz":[0.0541083551941607538,0.0984246978946035633,0.0161464317764581713],"hpluv":[123.236537452327113,182.849162381267462,37.5582057574881532],"hsluv":[123.236537452327113,100.000000000002288,37.5582057574881532]},"#226611":{"lch":[37.6315056544729529,51.7196180195473119,124.710513292064419],"luv":[37.6315056544729529,-29.4507212427394194,42.5155725160833242],"rgb":[0.133333333333333331,0.4,0.0666666666666666657],"xyz":[0.0551200206937978721,0.0988293640944584162,0.0214745367412138],"hpluv":[124.710513292064419,174.398618448608886,37.6315056544729529],"hsluv":[124.710513292064419,93.2756924270169918,37.6315056544729529]},"#226622":{"lch":[37.7668566222969062,47.5272711912458519,127.715012949240034],"luv":[37.7668566222969062,-29.0740638670039928,37.5970785719263176],"rgb":[0.133333333333333331,0.4,0.133333333333333331],"xyz":[0.0569953788322748942,0.0995795073498492306,0.031351422937193],"hpluv":[127.715012949240034,159.687663587322874,37.7668566222969062],"hsluv":[127.715012949240034,81.309482828258183,37.7668566222969062]},"#226633":{"lch":[37.9882367851431084,41.3581300307957349,133.555287588173542],"luv":[37.9882367851431084,-28.497993426715972,29.9726423642471254],"rgb":[0.133333333333333331,0.4,0.2],"xyz":[0.0600831295647326347,0.100814607642832343,0.0476135767948041438],"hpluv":[133.555287588173542,138.150062101989391,37.9882367851431084],"hsluv":[133.555287588173542,82.0766642375396742,37.9882367851431084]},"#226644":{"lch":[38.3046909892153806,34.1393686276432291,144.379277801066621],"luv":[38.3046909892153806,-27.7515572052733894,19.8833488873617412],"rgb":[0.133333333333333331,0.4,0.266666666666666663],"xyz":[0.0645411248089515083,0.102597805740519923,0.0710923517476907824],"hpluv":[144.379277801066621,113.094855071532635,38.3046909892153806],"hsluv":[144.379277801066621,83.0794073538424414,38.3046909892153806]},"#226655":{"lch":[38.7222568592555234,28.0005039743963025,163.778220785970291],"luv":[38.7222568592555234,-26.8857357715149732,7.82211191715276755],"rgb":[0.133333333333333331,0.4,0.333333333333333315],"xyz":[0.0705035388483135073,0.104982771356264756,0.102494399021664684],"hpluv":[163.778220785970291,91.7581213405574516,38.7222568592555234],"hsluv":[163.778220785970291,84.2573678500350809,38.7222568592555234]},"#226666":{"lch":[39.2444156655659739,26.5583838540848376,192.177050630061103],"luv":[39.2444156655659739,-25.9608324434988,-5.60204710632589631],"rgb":[0.133333333333333331,0.4,0.4],"xyz":[0.0780885346218216786,0.108016769665668075,0.142442043428808646],"hpluv":[192.177050630061103,85.8742788705857691,39.2444156655659739],"hsluv":[192.177050630061103,85.5381417500271652,39.2444156655659739]},"#226677":{"lch":[39.8723950361637449,31.9118423017057466,218.325872967111877],"luv":[39.8723950361637449,-25.034725973132506,-19.7895976345933065],"rgb":[0.133333333333333331,0.4,0.466666666666666674],"xyz":[0.0874025476420236,0.111742374873748884,0.191495845335206555],"hpluv":[218.325872967111877,101.559108573692257,39.8723950361637449],"hsluv":[218.325872967111877,86.8516914397336137,39.8723950361637449]},"#226688":{"lch":[40.6054458094686836,41.8995680003496318,234.7955790683381],"luv":[40.6054458094686836,-24.1549067561574908,-34.2361545477476241],"rgb":[0.133333333333333331,0.4,0.533333333333333326],"xyz":[0.0985429647439674261,0.116198541714526482,0.250168708738778722],"hpluv":[234.7955790683381,130.937664052949941,40.6054458094686836],"hsluv":[234.7955790683381,88.1401386465251733,40.6054458094686836]},"#226699":{"lch":[41.4411308461218226,53.8933488408681782,244.319142730286046],"luv":[41.4411308461218226,-23.3551142576285748,-48.5698639826860301],"rgb":[0.133333333333333331,0.4,0.6],"xyz":[0.11159992885304014,0.12142132735815564,0.318935386379896657],"hpluv":[244.319142730286046,165.022400136876769,41.4411308461218226],"hsluv":[244.319142730286046,89.3619383181549267,41.4411308461218226]},"#2266aa":{"lch":[42.3756299296545578,66.5269840788925251,250.089735516440328],"luv":[42.3756299296545578,-22.655631138578233,-62.5504755245386193],"rgb":[0.133333333333333331,0.4,0.66666666666666663],"xyz":[0.126657621818292682,0.127444404544256734,0.398239235996895258],"hpluv":[250.089735516440328,199.214522587526545,42.3756299296545578],"hsluv":[250.089735516440328,90.4915899210210597,42.3756299296545578]},"#2266bb":{"lch":[43.404050412459263,79.1834510223872599,253.819475134999237],"luv":[43.404050412459263,-22.0656311263342282,-76.0468726432017803],"rgb":[0.133333333333333331,0.4,0.733333333333333282],"xyz":[0.143795213925629328,0.134299441387191493,0.488497221095537],"hpluv":[253.819475134999237,231.496000814517572,43.404050412459263],"hsluv":[253.819475134999237,91.5168425909476895,43.404050412459263]},"#2266cc":{"lch":[44.520728505208055,91.5877705166709,256.367788484306971],"luv":[44.520728505208055,-21.5861851194902563,-89.0076194502553193],"rgb":[0.133333333333333331,0.4,0.8],"xyz":[0.163087589369351083,0.142016391564680294,0.590103731765807105],"hpluv":[256.367788484306971,261.044502527732277,44.520728505208055],"hsluv":[256.367788484306971,92.43509558526695,44.520728505208055]},"#2266dd":{"lch":[45.7195068588792211,103.627896901795907,258.1878164043369],"luv":[45.7195068588792211,-21.2130654589597789,-101.433460308337743],"rgb":[0.133333333333333331,0.4,0.866666666666666696],"xyz":[0.184605914919984626,0.150623721784933828,0.703433579665813102],"hpluv":[258.1878164043369,287.616948003961852,45.7195068588792211],"hsluv":[258.1878164043369,93.2500413525610696,45.7195068588792211]},"#2266ee":{"lch":[46.9939777237187144,115.273293340910215,259.534328145616598],"luv":[46.9939777237187144,-20.9389771714321675,-113.35559709460216],"rgb":[0.133333333333333331,0.4,0.933333333333333348],"xyz":[0.208418095186159336,0.160148593891403873,0.828844395734336326],"hpluv":[259.534328145616598,311.261797341727515,46.9939777237187144],"hsluv":[259.534328145616598,93.969002023907521,46.9939777237187144]},"#2266ff":{"lch":[48.3376856243364728,126.534150990995641,260.559220542156197],"luv":[48.3376856243364728,-20.75515631493003,-124.820330288598825],"rgb":[0.133333333333333331,0.4,1],"xyz":[0.234589143595990757,0.170617013255336569,0.966678584026118171],"hpluv":[260.559220542156197,332.170629177470857,48.3376856243364728],"hsluv":[260.559220542156197,99.9999999999992184,48.3376856243364728]},"#77ff00":{"lch":[89.5984732569245921,124.632639236881928,120.733702851753719],"luv":[89.5984732569245921,-63.6933378884713406,107.128210438594465],"rgb":[0.466666666666666674,1,0],"xyz":[0.433660129810488626,0.754395258206476127,0.122760832470867665],"hpluv":[120.733702851753719,538.628162219261071,89.5984732569245921],"hsluv":[120.733702851753719,100.000000000002359,89.5984732569245921]},"#77ff11":{"lch":[89.6173512893739144,123.857431845856226,120.915842858481739],"luv":[89.6173512893739144,-63.635285050623331,106.260123846986147],"rgb":[0.466666666666666674,1,0.0666666666666666657],"xyz":[0.43467179531012573,0.754799924406331,0.128088937435623301],"hpluv":[120.915842858481739,536.333532616984598,89.6173512893739144],"hsluv":[120.915842858481739,99.9999999999912461,89.6173512893739144]},"#77ff22":{"lch":[89.6523282897647107,122.430860002153082,121.257903913559275],"luv":[89.6523282897647107,-63.5282943614823736,104.658832863679763],"rgb":[0.466666666666666674,1,0.133333333333333331],"xyz":[0.436547153448602787,0.755550067661721836,0.13796582363160248],"hpluv":[121.257903913559275,532.099457877515,89.6523282897647107],"hsluv":[121.257903913559275,99.9999999999912319,89.6523282897647107]},"#77ff33":{"lch":[89.7098670229763684,120.111567958755899,121.833904144252088],"luv":[89.7098670229763684,-63.3538816193187557,102.044472860004433],"rgb":[0.466666666666666674,1,0.2],"xyz":[0.439634904181060493,0.756785167954705,0.15422797748921363],"hpluv":[121.833904144252088,525.184015431200237,89.7098670229763684],"hsluv":[121.833904144252088,99.9999999999911893,89.7098670229763684]},"#77ff44":{"lch":[89.7928292607091834,116.827732221897932,122.694629127971339],"luv":[89.7928292607091834,-63.1058355300939766,98.317712230097257],"rgb":[0.466666666666666674,1,0.266666666666666663],"xyz":[0.444092899425279408,0.758568366052392529,0.177706752442100269],"hpluv":[122.694629127971339,515.324540814372313,89.7928292607091834],"hsluv":[122.694629127971339,99.9999999999912319,89.7928292607091834]},"#77ff55":{"lch":[89.9035853929870683,112.554947355203538,123.902387884937639],"luv":[89.9035853929870683,-62.7808648215699492,93.4193726503684161],"rgb":[0.466666666666666674,1,0.333333333333333315],"xyz":[0.450055313464641393,0.760953331668137278,0.209108799716074184],"hpluv":[123.902387884937639,502.374863630046946,89.9035853929870683],"hsluv":[123.902387884937639,99.9999999999911466,89.9035853929870683]},"#77ff66":{"lch":[90.0441481999633169,107.316590618570714,125.538864396596409],"luv":[90.0441481999633169,-62.3783098318914213,87.3258099562234662],"rgb":[0.466666666666666674,1,0.4],"xyz":[0.457640309238149579,0.763987329977540597,0.249056444123218146],"hpluv":[125.538864396596409,486.310350268178581,90.0441481999633169],"hsluv":[125.538864396596409,99.9999999999909193,90.0441481999633169]},"#77ff77":{"lch":[90.2162444924982,101.18761180958829,127.715012949239778],"luv":[90.2162444924982,-61.8999369112410065,80.0458031011764746],"rgb":[0.466666666666666674,1,0.466666666666666674],"xyz":[0.466954322258351484,0.767712935185621448,0.298110246029616055],"hpluv":[127.715012949239778,467.252180695244249,90.2162444924982],"hsluv":[127.715012949239778,99.9999999999908908,90.2162444924982]},"#77ff88":{"lch":[90.4213578195287084,94.3018691223763312,130.584388318929172],"luv":[90.4213578195287084,-61.3497133683486666,71.6174223886566352],"rgb":[0.466666666666666674,1,0.533333333333333326],"xyz":[0.478094739360295284,0.772169102026399,0.35678310943318825],"hpluv":[130.584388318929172,445.517691352744919,90.4213578195287084],"hsluv":[130.584388318929172,99.999999999990834,90.4213578195287084]},"#77ff99":{"lch":[90.660755936805927,86.8649116623181,134.360625617567337],"luv":[90.660755936805927,-60.7335297134313734,62.1043577106315],"rgb":[0.466666666666666674,1,0.6],"xyz":[0.491151703469368,0.777391887670028203,0.425549787074306129],"hpluv":[134.360625617567337,421.714412879972315,90.660755936805927],"hsluv":[134.360625617567337,99.9999999999904645,90.660755936805927]},"#77ffaa":{"lch":[90.9355096576679927,79.1755179707932513,139.336843070371486],"luv":[90.9355096576679927,-60.0588662825700368,51.5916197341755947],"rgb":[0.466666666666666674,1,0.66666666666666663],"xyz":[0.506209396434620595,0.78341496485612927,0.504853636691304786],"hpluv":[139.336843070371486,396.909453229520182,90.9355096576679927],"hsluv":[139.336843070371486,99.9999999999902371,90.9355096576679927]},"#77ffbb":{"lch":[91.2465066485729466,71.6593730134265599,145.894485367843032],"luv":[91.2465066485729466,-59.3344173347434776,40.1807498713488229],"rgb":[0.466666666666666674,1,0.733333333333333282],"xyz":[0.523346988541957159,0.790270001699064,0.595111621789946477],"hpluv":[145.894485367843032,372.920765055848051,91.2465066485729466],"hsluv":[145.894485367843032,99.9999999999901803,91.2465066485729466]},"#77ffcc":{"lch":[91.5944622372901591,64.9119206982031329,154.461378490718602],"luv":[91.5944622372901591,-58.5696939322881676,27.9847887504604707],"rgb":[0.466666666666666674,1,0.8],"xyz":[0.542639363985678913,0.797986951876552886,0.696718132460216633],"hpluv":[154.461378490718602,352.767904832744364,91.5944622372901591],"hsluv":[154.461378490718602,99.999999999989825,91.5944622372901591]},"#77ffdd":{"lch":[91.9799284987297,59.7212008687906604,165.331177548206284],"luv":[91.9799284987297,-57.7746296637575298,15.1232933062250314],"rgb":[0.466666666666666674,1,0.866666666666666696],"xyz":[0.564157689536312512,0.806594282096806392,0.81004798036022263],"hpluv":[165.331177548206284,341.200809564626638,91.9799284987297],"hsluv":[165.331177548206284,99.9999999999894698,91.9799284987297]},"#77ffee":{"lch":[92.4033024177180238,56.9851054432815545,178.272694676027839],"luv":[92.4033024177180238,-56.9592119034112,1.71767916801026743],"rgb":[0.466666666666666674,1,0.933333333333333348],"xyz":[0.587969869802487222,0.816119154203276409,0.935458796428745853],"hpluv":[178.272694676027839,344.865805035808421,92.4033024177180238],"hsluv":[178.272694676027839,99.9999999999889582,92.4033024177180238]},"#77ffff":{"lch":[92.8648336399367196,57.4251975820971623,192.177050630061018],"luv":[92.8648336399367196,-56.1331570721439945,-12.112885471963672],"rgb":[0.466666666666666674,1,1],"xyz":[0.614140918212318643,0.826587573567209133,1.0732929847205277],"hpluv":[192.177050630061018,371.354821198433683,92.8648336399367196],"hsluv":[192.177050630061018,99.9999999999883187,92.8648336399367196]},"#227700":{"lch":[43.5559297152692295,63.9882214930525208,124.519604676885976],"luv":[43.5559297152692295,-36.2613695379953711,52.7219647687080268],"rgb":[0.133333333333333331,0.466666666666666674,0],"xyz":[0.0725620932475783825,0.13533217400143932,0.0222976777942638753],"hpluv":[124.519604676885976,186.419817132403381,43.5559297152692295],"hsluv":[124.519604676885976,100.000000000002331,43.5559297152692295]},"#227711":{"lch":[43.6152314308602769,61.9621092027414093,125.591738779589861],"luv":[43.6152314308602769,-36.0623023987336282,50.3866383335378174],"rgb":[0.133333333333333331,0.466666666666666674,0.0666666666666666657],"xyz":[0.0735737587472155,0.135736840201294173,0.0276257827590195085],"hpluv":[125.591738779589861,180.271610265320959,43.6152314308602769],"hsluv":[125.591738779589861,95.0867888092805345,43.6152314308602769]},"#227722":{"lch":[43.7248500006056062,58.3665782213362476,127.715012949240148],"luv":[43.7248500006056062,-35.7048401974777292,46.1716562377823081],"rgb":[0.133333333333333331,0.466666666666666674,0.133333333333333331],"xyz":[0.0754491168856925298,0.136486983456685,0.0375026689549987],"hpluv":[127.715012949240148,169.385110384242353,43.7248500006056062],"hsluv":[127.715012949240148,86.2472116803111248,43.7248500006056062]},"#227733":{"lch":[43.9044636679785,52.8998675233047422,131.63685913347058],"luv":[43.9044636679785,-35.1470498804547518,39.5358175416168365],"rgb":[0.133333333333333331,0.466666666666666674,0.2],"xyz":[0.0785368676181502634,0.137722083749668101,0.0537648228126098443],"hpluv":[131.63685913347058,152.892166506547881,43.9044636679785],"hsluv":[131.63685913347058,86.6671370230467204,43.9044636679785]},"#227744":{"lch":[44.1618994548856207,46.0187725021067919,138.382113458020484],"luv":[44.1618994548856207,-34.403210499316,30.5638107889119937],"rgb":[0.133333333333333331,0.466666666666666674,0.266666666666666663],"xyz":[0.082994862862369137,0.139505281847355694,0.0772435977654964828],"hpluv":[138.382113458020484,132.228969104156789,44.1618994548856207],"hsluv":[138.382113458020484,87.230083060842972,44.1618994548856207]},"#227755":{"lch":[44.5028042963196171,38.7961505601339098,149.7341572603126],"luv":[44.5028042963196171,-33.5080868304533865,19.5537570621958743],"rgb":[0.133333333333333331,0.466666666666666674,0.333333333333333315],"xyz":[0.088957276901731136,0.141890247463100527,0.108645645039470398],"hpluv":[149.7341572603126,110.621765171639552,44.5028042963196171],"hsluv":[149.7341572603126,87.9126674499387235,44.5028042963196171]},"#227766":{"lch":[44.9310046324235657,33.2418513983527504,167.948858330193701],"luv":[44.9310046324235657,-32.5092552143098246,6.94038974417402166],"rgb":[0.133333333333333331,0.466666666666666674,0.4],"xyz":[0.0965422726752393073,0.144924245772503818,0.148593289446614346],"hpluv":[167.948858330193701,93.8811500215461,44.9310046324235657],"hsluv":[167.948858330193701,88.6822605795820778,44.9310046324235657]},"#227777":{"lch":[45.4487163896678652,32.1827345016432886,192.177050630061217],"luv":[45.4487163896678652,-31.4586377906525136,-6.7884098550242733],"rgb":[0.133333333333333331,0.466666666666666674,0.466666666666666674],"xyz":[0.105856285695441227,0.148649850980584641,0.197647091353012255],"hpluv":[192.177050630061217,89.8546686552558,45.4487163896678652],"hsluv":[192.177050630061217,89.5029511213474,45.4487163896678652]},"#227788":{"lch":[46.0567093690380389,37.0457871068229778,214.840569513336618],"luv":[46.0567093690380389,-30.4051407329018595,-21.1640676472289648],"rgb":[0.133333333333333331,0.466666666666666674,0.533333333333333326],"xyz":[0.116996702797385055,0.153106017821362239,0.256319954756584423],"hpluv":[214.840569513336618,102.066975938242322,46.0567093690380389],"hsluv":[214.840569513336618,90.3407286635711415,46.0567093690380389]},"#227799":{"lch":[46.7544651468991219,46.3079141704010908,230.605362437026685],"luv":[46.7544651468991219,-29.3896969252833173,-35.7864308006991223],"rgb":[0.133333333333333331,0.466666666666666674,0.6],"xyz":[0.130053666906457754,0.158328803464991397,0.325086632397702358],"hpluv":[230.605362437026685,125.681528260779189,46.7544651468991219],"hsluv":[230.605362437026685,91.1669787694701,46.7544651468991219]},"#2277aa":{"lch":[47.5403420753171133,57.8256716053654429,240.536105931551504],"luv":[47.5403420753171133,-28.4430017925967782,-50.3468365007961935],"rgb":[0.133333333333333331,0.466666666666666674,0.66666666666666663],"xyz":[0.14511135987171031,0.164351880651092491,0.404390482014700958],"hpluv":[240.536105931551504,154.346828351852139,47.5403420753171133],"hsluv":[240.536105931551504,91.9600907928071365,47.5403420753171133]},"#2277bb":{"lch":[48.4117490016133161,70.2708193466387172,246.886175208709233],"luv":[48.4117490016133161,-27.5854458877112236,-64.6299561103370337],"rgb":[0.133333333333333331,0.466666666666666674,0.733333333333333282],"xyz":[0.162248951979046957,0.17120691749402725,0.494648467113342705],"hpluv":[246.886175208709233,184.188949495161438,48.4117490016133161],"hsluv":[246.886175208709233,92.7055270960754427,48.4117490016133161]},"#2277cc":{"lch":[49.3653235224945348,82.9601291275659634,251.131977113837081],"luv":[49.3653235224945348,-26.8284223951791141,-78.5023488607078],"rgb":[0.133333333333333331,0.466666666666666674,0.8],"xyz":[0.181541327422768684,0.178923867671516051,0.596254977783612805],"hpluv":[251.131977113837081,213.248879712122147,49.3653235224945348],"hsluv":[251.131977113837081,93.3949294454883301,49.3653235224945348]},"#2277dd":{"lch":[50.3971082370692898,95.5506012475210156,254.100456017762497],"luv":[50.3971082370692898,-26.1762366694964186,-91.8951687118820928],"rgb":[0.133333333333333331,0.466666666666666674,0.866666666666666696],"xyz":[0.203059652973402227,0.187531197891769585,0.709584825683618803],"hpluv":[254.100456017762497,240.584217083197558,50.3971082370692898],"hsluv":[254.100456017762497,94.0248036114472399,50.3971082370692898]},"#2277ee":{"lch":[51.5027182397596448,107.874744495734575,256.256689112315712],"luv":[51.5027182397596448,-25.6280715933506258,-104.786270314512635],"rgb":[0.133333333333333331,0.466666666666666674,0.933333333333333348],"xyz":[0.226871833239577,0.19705606999823963,0.834995641752142],"hpluv":[256.256689112315712,265.784074864026707,51.5027182397596448],"hsluv":[256.256689112315712,94.5951642228933594,51.5027182397596448]},"#2277ff":{"lch":[52.6774941409559432,119.859275592882611,257.873120264558906],"luv":[52.6774941409559432,-25.1797078347732324,-117.18459053564186],"rgb":[0.133333333333333331,0.466666666666666674,1],"xyz":[0.253042881649408358,0.207524489362172326,0.972829830043923871],"hpluv":[257.873120264558906,288.725982332353851,52.6774941409559432],"hsluv":[257.873120264558906,99.9999999999990195,52.6774941409559432]},"#228800":{"lch":[49.4326013626652951,73.5543249838887903,125.335546592141156],"luv":[49.4326013626652951,-42.5411625112882845,60.0040683289365475],"rgb":[0.133333333333333331,0.533333333333333326,0],"xyz":[0.0946344629725488357,0.179476913451380865,0.0296551343692538216],"hpluv":[125.335546592141156,188.81394887832684,49.4326013626652951],"hsluv":[125.335546592141156,100.000000000002359,49.4326013626652951]},"#228811":{"lch":[49.4817413630627669,71.8188220709608,126.143293588176633],"luv":[49.4817413630627669,-42.3592238010278379,57.9968909738682825],"rgb":[0.133333333333333331,0.533333333333333326,0.0666666666666666657],"xyz":[0.0956461284721859539,0.179881579651235718,0.0349832393340094513],"hpluv":[126.143293588176633,184.175827324420425,49.4817413630627669],"hsluv":[126.143293588176633,96.2839239867753776,49.4817413630627669]},"#228822":{"lch":[49.5726392440562904,68.7061860444130161,127.715012949240233],"luv":[49.5726392440562904,-42.0299333634258545,54.3509402148249521],"rgb":[0.133333333333333331,0.533333333333333326,0.133333333333333331],"xyz":[0.097521486610662983,0.180631722906626546,0.0448601255299886509],"hpluv":[127.715012949240233,175.870551812838869,49.5726392440562904],"hsluv":[127.715012949240233,89.549457305464,49.5726392440562904]},"#228833":{"lch":[49.7217546026668913,63.8760413965615115,130.529662443073192],"luv":[49.7217546026668913,-41.5093107949551552,48.5502397710151357],"rgb":[0.133333333333333331,0.533333333333333326,0.2],"xyz":[0.100609237343120717,0.181866823199609645,0.0611222793875997941],"hpluv":[130.529662443073192,163.016240956960957,49.7217546026668913],"hsluv":[130.529662443073192,89.7937238944362122,49.7217546026668913]},"#228844":{"lch":[49.9358562328630171,57.5522088774297558,135.149605574971361],"luv":[49.9358562328630171,-40.8016788905052792,40.5891579906186308],"rgb":[0.133333333333333331,0.533333333333333326,0.266666666666666663],"xyz":[0.10506723258733959,0.183650021297297239,0.0846010543404864257],"hpluv":[135.149605574971361,146.247625060139768,49.9358562328630171],"hsluv":[135.149605574971361,90.1269016918236616,49.9358562328630171]},"#228855":{"lch":[50.2200542052193697,50.3265642713731509,142.503880569306347],"luv":[50.2200542052193697,-39.9288227743001443,30.6341669254964764],"rgb":[0.133333333333333331,0.533333333333333326,0.333333333333333315],"xyz":[0.111029646626701589,0.186034986913042072,0.116003101614460341],"hpluv":[142.503880569306347,127.162609675202816,50.2200542052193697],"hsluv":[142.503880569306347,90.5399352865824341,50.2200542052193697]},"#228866":{"lch":[50.5781035519961648,43.3062651916131074,154.006170397495453],"luv":[50.5781035519961648,-38.9254575775356315,18.9800252166796355],"rgb":[0.133333333333333331,0.533333333333333326,0.4],"xyz":[0.11861464240020976,0.189068985222445363,0.155950746021604303],"hpluv":[154.006170397495453,108.649446020320369,50.5781035519961648],"hsluv":[154.006170397495453,91.0179493017372607,50.5781035519961648]},"#228877":{"lch":[51.0125694858746357,38.3061120533697519,170.992189866461246],"luv":[51.0125694858746357,-37.8336830583721451,5.997553408179356],"rgb":[0.133333333333333331,0.533333333333333326,0.466666666666666674],"xyz":[0.12792865542041168,0.192794590430526186,0.205004547928002212],"hpluv":[170.992189866461246,95.2862426106229634,51.0125694858746357],"hsluv":[170.992189866461246,91.5427076584867621,51.0125694858746357]},"#228888":{"lch":[51.5249413470067594,37.5420496271247259,192.177050630061103],"luv":[51.5249413470067594,-36.6973707929670923,-7.91886779085127834],"rgb":[0.133333333333333331,0.533333333333333326,0.533333333333333326],"xyz":[0.139069072522355508,0.197250757271303784,0.263677411331574407],"hpluv":[192.177050630061103,92.4570004995607775,51.5249413470067594],"hsluv":[192.177050630061103,92.0950966753639761,51.5249413470067594]},"#228899":{"lch":[52.1157302062907064,42.0223592343639183,212.203992327233067],"luv":[52.1157302062907064,-35.5574728144251,-22.3951959730954577],"rgb":[0.133333333333333331,0.533333333333333326,0.6],"xyz":[0.152126036631428208,0.202473542914932941,0.332444088972692287],"hpluv":[212.203992327233067,102.317738016510475,52.1157302062907064],"hsluv":[212.203992327233067,92.6571999954113465,52.1157302062907064]},"#2288aa":{"lch":[52.784565077464336,50.6329849579481888,227.127925237574203],"luv":[52.784565077464336,-34.4488478228089221,-37.1076279143892478],"rgb":[0.133333333333333331,0.533333333333333326,0.66666666666666663],"xyz":[0.167183729596680763,0.208496620101034036,0.411747938589690887],"hpluv":[227.127925237574203,121.72111165959474,52.784565077464336],"hsluv":[227.127925237574203,93.213704377986,52.784565077464336]},"#2288bb":{"lch":[53.5302933631107294,61.6316847476891141,237.186375672219185],"luv":[53.5302933631107294,-33.3987075035381409,-51.7975955226846381],"rgb":[0.133333333333333331,0.533333333333333326,0.733333333333333282],"xyz":[0.18432132170401741,0.215351656943968794,0.502005923688332634],"hpluv":[237.186375672219185,146.097822373379188,53.5302933631107294],"hsluv":[237.186375672219185,93.7525919029994,53.5302933631107294]},"#2288cc":{"lch":[54.351086283976727,73.7818378825302261,243.928624140525301],"luv":[54.351086283976727,-32.4264148607849876,-66.274333045306733],"rgb":[0.133333333333333331,0.533333333333333326,0.8],"xyz":[0.203613697147739137,0.223068607121457596,0.60361243435860279],"hpluv":[243.928624140525301,172.258462735913952,54.351086283976727],"hsluv":[243.928624140525301,94.2652369190112864,54.351086283976727]},"#2288dd":{"lch":[55.2445474530087779,86.375042864399532,248.580085361336785],"luv":[55.2445474530087779,-31.544198041281593,-80.4090268549449831],"rgb":[0.133333333333333331,0.533333333333333326,0.866666666666666696],"xyz":[0.22513202269837268,0.23167593734171113,0.716942282258608787],"hpluv":[248.580085361336785,198.39840761978212,55.2445474530087779],"hsluv":[248.580085361336785,94.7460999996689566,55.2445474530087779]},"#2288ee":{"lch":[56.2078215466105604,99.0227420546019772,251.903461744910402],"luv":[56.2078215466105604,-30.7583451142169402,-94.1245326673496265],"rgb":[0.133333333333333331,0.533333333333333326,0.933333333333333348],"xyz":[0.248944202964547445,0.241200809448181175,0.842353098327132],"hpluv":[251.903461744910402,223.551465466634539,56.2078215466105604],"hsluv":[251.903461744910402,95.1922101623356838,56.2078215466105604]},"#2288ff":{"lch":[57.2376997180489866,111.514610807296222,254.356224713070389],"luv":[57.2376997180489866,-30.0705415498761575,-107.383755542446536],"rgb":[0.133333333333333331,0.533333333333333326,1],"xyz":[0.275115251374378811,0.251669228812113843,0.980187286618913856],"hpluv":[254.356224713070389,247.223031548482197,57.2376997180489866],"hsluv":[254.356224713070389,99.9999999999988773,57.2376997180489866]},"#229900":{"lch":[55.1973816023000694,82.8565734474734086,125.883775052172936],"luv":[55.1973816023000694,-48.5657961090338617,67.1310301704979508],"rgb":[0.133333333333333331,0.6,0],"xyz":[0.120504063425016322,0.23121611435631656,0.0382783345200760766],"hpluv":[125.883775052172936,190.479314261094402,55.1973816023000694],"hsluv":[125.883775052172936,100.000000000002331,55.1973816023000694]},"#229911":{"lch":[55.2388931006948951,81.3509781714907376,126.510168490119042],"luv":[55.2388931006948951,-48.4010205856972817,65.3859530459048273],"rgb":[0.133333333333333331,0.6,0.0666666666666666657],"xyz":[0.121515728924653441,0.231620780556171413,0.0436064394848317063],"hpluv":[126.510168490119042,186.877552842351321,55.2388931006948951],"hsluv":[126.510168490119042,97.109403045153627,55.2388931006948951]},"#229922":{"lch":[55.3157166450500739,78.6308396618125869,127.715012949240275],"luv":[55.3157166450500739,-48.1011847922964293,62.2019691609440102],"rgb":[0.133333333333333331,0.6,0.133333333333333331],"xyz":[0.12339108706313047,0.232370923811562241,0.0534833256808109059],"hpluv":[127.715012949240275,180.378053709807,55.3157166450500739],"hsluv":[127.715012949240275,91.8445791693357592,55.3157166450500739]},"#229933":{"lch":[55.4418461137647,74.3518111171957656,129.829614369777261],"luv":[55.4418461137647,-47.6228343175573201,57.0986643273697],"rgb":[0.133333333333333331,0.6,0.2],"xyz":[0.126478837795588217,0.23360602410454534,0.0697454795384220561],"hpluv":[129.829614369777261,170.173995330187722,55.4418461137647],"hsluv":[129.829614369777261,91.9941043082508827,55.4418461137647]},"#229944":{"lch":[55.6231658975890042,68.6117263626113925,133.195472742568398],"luv":[55.6231658975890042,-46.964006542137362,50.0195070344354846],"rgb":[0.133333333333333331,0.6,0.266666666666666663],"xyz":[0.130936833039807077,0.235389222202232934,0.0932242544913087],"hpluv":[133.195472742568398,156.524371813771666,55.6231658975890042],"hsluv":[133.195472742568398,92.2005608183228844,55.6231658975890042]},"#229955":{"lch":[55.8642490130281715,61.7521412094073696,138.342726405552],"luv":[55.8642490130281715,-46.1371272258517706,41.0450049980768625],"rgb":[0.133333333333333331,0.6,0.333333333333333315],"xyz":[0.136899247079169062,0.237774187817977767,0.124626301765282596],"hpluv":[138.342726405552,140.267605312348593,55.8642490130281715],"hsluv":[138.342726405552,92.4605995247666357,55.8642490130281715]},"#229966":{"lch":[56.1686206396836383,54.4254646973719787,146.085824005346524],"luv":[56.1686206396836383,-45.1662924386951516,30.3667126121210664],"rgb":[0.133333333333333331,0.6,0.4],"xyz":[0.144484242852677247,0.240808186127381058,0.164573946172426544],"hpluv":[146.085824005346524,122.955430161041207,56.1686206396836383],"hsluv":[146.085824005346524,92.7673635780271,56.1686206396836383]},"#229977":{"lch":[56.5388973522399,47.7139826201784629,157.505617321427167],"luv":[56.5388973522399,-44.0837619060267798,18.2550287233801534],"rgb":[0.133333333333333331,0.6,0.466666666666666674],"xyz":[0.153798255872879153,0.244533791335461881,0.213627748078824453],"hpluv":[157.505617321427167,107.087223886255089,56.5388973522399],"hsluv":[157.505617321427167,93.1115325865156,56.5388973522399]},"#229988":{"lch":[56.9768757530356,43.2189787336254057,173.325138204180888],"luv":[56.9768757530356,-42.9260293704251126,5.02355703331473169],"rgb":[0.133333333333333331,0.6,0.533333333333333326],"xyz":[0.164938672974823,0.248989958176239479,0.272300611482396648],"hpluv":[173.325138204180888,96.2532040641867,56.9768757530356],"hsluv":[173.325138204180888,93.4824878896153706,56.9768757530356]},"#229999":{"lch":[57.4836007022547477,42.6905833214490045,192.177050630061103],"luv":[57.4836007022547477,-41.7300648492924537,-9.00486490733903366],"rgb":[0.133333333333333331,0.6,0.6],"xyz":[0.177995637083895708,0.254212743819868636,0.341067289123514528],"hpluv":[192.177050630061103,94.2383018299879467,57.4836007022547477],"hsluv":[192.177050630061103,93.8694254698009587,57.4836007022547477]},"#2299aa":{"lch":[58.0594270662980563,46.8633307325653,210.133219640519371],"luv":[58.0594270662980563,-40.5302437993065823,-23.5259666096543114],"rgb":[0.133333333333333331,0.6,0.66666666666666663],"xyz":[0.193053330049148264,0.260235821005969759,0.420371138740513128],"hpluv":[210.133219640519371,102.423528384682896,58.0594270662980563],"hsluv":[210.133219640519371,94.2622655036202701,58.0594270662980563]},"#2299bb":{"lch":[58.704081470592044,54.8976329577059445,224.200669016044458],"luv":[58.704081470592044,-39.3562485070025474,-38.2731734745639],"rgb":[0.133333333333333331,0.6,0.733333333333333282],"xyz":[0.210190922156484883,0.267090857848904517,0.51062912383915493],"hpluv":[224.200669016044458,118.665547847191604,58.704081470592044],"hsluv":[224.200669016044458,94.6522748426748279,58.704081470592044]},"#2299cc":{"lch":[59.4167266489170771,65.3729917945351247,234.209137470411804],"luv":[59.4167266489170771,-38.231976961148348,-53.0277662532618663],"rgb":[0.133333333333333331,0.6,0.8],"xyz":[0.229483297600206637,0.274807808026393319,0.612235634509425086],"hpluv":[234.209137470411804,139.613998456385502,59.4167266489170771],"hsluv":[234.209137470411804,95.032392249276171,59.4167266489170771]},"#2299dd":{"lch":[60.1960287785942114,77.1664383037622201,241.199920521286145],"luv":[60.1960287785942114,-37.1753089722913614,-67.6214137925481],"rgb":[0.133333333333333331,0.6,0.866666666666666696],"xyz":[0.251001623150840181,0.283415138246646825,0.725565482409431084],"hpluv":[241.199920521286145,162.667181112599422,60.1960287785942114],"hsluv":[241.199920521286145,95.397300800485425,60.1960287785942114]},"#2299ee":{"lch":[61.0402269370523243,89.5733454479413211,246.163931095701571],"luv":[61.0402269370523243,-36.1984881015500051,-81.9332269222821],"rgb":[0.133333333333333331,0.6,0.933333333333333348],"xyz":[0.27481380341701489,0.292940010353116842,0.850976298477954307],"hpluv":[246.163931095701571,186.209563381880429,61.0402269370523243],"hsluv":[246.163931095701571,95.74331870201,61.0402269370523243]},"#2299ff":{"lch":[61.9472031658153384,102.178145565387652,249.783903318571561],"luv":[61.9472031658153384,-35.3088685699608078,-95.8835607989752532],"rgb":[0.133333333333333331,0.6,1],"xyz":[0.300984851826846311,0.303408429717049566,0.988810486769736152],"hpluv":[249.783903318571561,209.303089797111312,61.9472031658153384],"hsluv":[249.783903318571561,99.9999999999986,61.9472031658153384]},"#110000":{"lch":[1.07666134976862637,3.62084603829176643,12.1770506300617818],"luv":[1.07666134976862637,3.53937866928378497,0.763756943295526236],"rgb":[0.0666666666666666657,0,0],"xyz":[0.00231161193210362246,0.00119192490249095569,0.000108356809317355026],"hpluv":[12.1770506300617818,426.746789183125145,1.07666134976862637],"hsluv":[12.1770506300617818,100.000000000002203,1.07666134976862637]},"#110011":{"lch":[1.44219482929484544,3.28508596549136378,307.715012949243601],"luv":[1.44219482929484544,2.00959989444743092,-2.59871084672866193],"rgb":[0.0666666666666666657,0,0.0666666666666666657],"xyz":[0.0033232774317407442,0.00159659110234581,0.00543646177407298634],"hpluv":[307.715012949243601,289.042783730483393,1.44219482929484544],"hsluv":[307.715012949243601,99.9999999999988347,1.44219482929484544]},"#110022":{"lch":[2.1197964535087821,6.27745605271938789,280.884754167684719],"luv":[2.1197964535087821,1.18539805862553327,-6.16451830530416167],"rgb":[0.0666666666666666657,0,0.133333333333333331],"xyz":[0.00519863557021776369,0.00234673435773662814,0.0153133479700521824],"hpluv":[280.884754167684719,375.775833064690062,2.1197964535087821],"hsluv":[280.884754167684719,99.9999999999998721,2.1197964535087821]},"#110033":{"lch":[3.23545797359596321,11.0622687483975319,272.972319481398301],"luv":[3.23545797359596321,0.57361730895702967,-11.0473867065762477],"rgb":[0.0666666666666666657,0,0.2],"xyz":[0.00828638630267550247,0.00358183465071974143,0.0315755018276633256],"hpluv":[272.972319481398301,433.858158519435221,3.23545797359596321],"hsluv":[272.972319481398301,100.000000000000355,3.23545797359596321]},"#110044":{"lch":[4.84621421062803659,17.7312810137515946,269.891014646828467],"luv":[4.84621421062803659,-0.0337275934556249754,-17.7312489362161827],"rgb":[0.0666666666666666657,0,0.266666666666666663],"xyz":[0.012744381546894383,0.0053650327484073175,0.0550542767805499642],"hpluv":[269.891014646828467,464.276639746945534,4.84621421062803659],"hsluv":[269.891014646828467,100.000000000000711,4.84621421062803659]},"#110055":{"lch":[7.00054481789469563,26.532890242342738,268.413820694361107],"luv":[7.00054481789469563,-0.734444075336115332,-26.5227233992365541],"rgb":[0.0666666666666666657,0,0.333333333333333315],"xyz":[0.0187067955862563751,0.00774999836415214954,0.0864563240545238726],"hpluv":[268.413820694361107,480.941270902403687,7.00054481789469563],"hsluv":[268.413820694361107,100.000000000000682,7.00054481789469563]},"#110066":{"lch":[9.62818818466394,37.2351477319955,267.604082628906383],"luv":[9.62818818466394,-1.55659527409507947,-37.20259719127408],"rgb":[0.0666666666666666657,0,0.4],"xyz":[0.0262917913597645499,0.010783996673555462,0.126403968461667848],"hpluv":[267.604082628906383,490.735908571457742,9.62818818466394],"hsluv":[267.604082628906383,100.000000000000753,9.62818818466394]},"#110077":{"lch":[12.2928363787590555,48.1341065988899643,267.117295446388],"luv":[12.2928363787590555,-2.42073458662620622,-48.0731969202633138],"rgb":[0.0666666666666666657,0,0.466666666666666674],"xyz":[0.0356058043799664659,0.0145096018816362783,0.175457770368065757],"hpluv":[267.117295446388,496.866985521105335,12.2928363787590555],"hsluv":[267.117295446388,100.000000000000739,12.2928363787590555]},"#110088":{"lch":[14.9348588897968106,58.9551979191609803,266.804247897724281],"luv":[14.9348588897968106,-3.28660375460643372,-58.8635166928348781],"rgb":[0.0666666666666666657,0,0.533333333333333326],"xyz":[0.0467462214819102939,0.0189657687224138727,0.234130633771637925],"hpluv":[266.804247897724281,500.910695182750828,14.9348588897968106],"hsluv":[266.804247897724281,100.000000000000753,14.9348588897968106]},"#110099":{"lch":[17.5475874535139624,69.6538923837914297,266.59230255326986],"luv":[17.5475874535139624,-4.14026096141065292,-69.5307339482636],"rgb":[0.0666666666666666657,0,0.6],"xyz":[0.0598031855909830073,0.0241885543660430302,0.302897311412755832],"hpluv":[266.59230255326986,503.694607743992833,17.5475874535139624],"hsluv":[266.59230255326986,100.000000000000796,17.5475874535139624]},"#1100aa":{"lch":[20.1284543895036734,80.2134478690449,266.442863009455],"luv":[20.1284543895036734,-4.97675334080985809,-80.0589104673847203],"rgb":[0.0666666666666666657,0,0.66666666666666663],"xyz":[0.0748608785562355494,0.0302116315521441317,0.382201161029754433],"hpluv":[266.442863009455,505.680355905096519,20.1284543895036734],"hsluv":[266.442863009455,100.000000000000782,20.1284543895036734]},"#1100bb":{"lch":[22.6769756305364183,90.6302333913987894,266.3339747285724],"luv":[22.6769756305364183,-5.79494806122388795,-90.444777525002138],"rgb":[0.0666666666666666657,0,0.733333333333333282],"xyz":[0.0919984706635722,0.0370666683950788903,0.472459146128396179],"hpluv":[266.3339747285724,507.139328846885462,22.6769756305364183],"hsluv":[266.3339747285724,100.000000000001,22.6769756305364183]},"#1100cc":{"lch":[25.1937235339869332,100.906819147977927,266.252448568601267],"luv":[25.1937235339869332,-6.59531864805210422,-100.691051849175651],"rgb":[0.0666666666666666657,0,0.8],"xyz":[0.111290846107293936,0.0447836185725676919,0.574065656798666279],"hpluv":[266.252448568601267,508.238409831726415,25.1937235339869332],"hsluv":[266.252448568601267,100.000000000000824,25.1937235339869332]},"#1100dd":{"lch":[27.6797893663012289,111.048607057141055,266.189998210144608],"luv":[27.6797893663012289,-7.37896669992084409,-110.803176758488192],"rgb":[0.0666666666666666657,0,0.866666666666666696],"xyz":[0.132809171657927494,0.0533909487928212259,0.687395504698672277],"hpluv":[266.189998210144608,509.084249760461944,27.6797893663012289],"hsluv":[266.189998210144608,100.000000000000938,27.6797893663012289]},"#1100ee":{"lch":[30.1364964584496846,121.062148077250455,266.141219022825339],"luv":[30.1364964584496846,-8.14718371295858823,-120.787694301304626],"rgb":[0.0666666666666666657,0,0.933333333333333348],"xyz":[0.156621351924102231,0.0629158208992912638,0.8128063207671955],"hpluv":[266.141219022825339,509.74730741907581,30.1364964584496846],"hsluv":[266.141219022825339,100.000000000000952,30.1364964584496846]},"#1100ff":{"lch":[32.5652456752648263,130.954293728553409,266.102472093749839],"luv":[32.5652456752648263,-8.90125725083502495,-130.651424275813781],"rgb":[0.0666666666666666657,0,1],"xyz":[0.182792400333933625,0.0733842402632239599,0.950640509058977345],"hpluv":[266.102472093749839,510.275492181060656,32.5652456752648263],"hsluv":[266.102472093749839,100.000000000001108,32.5652456752648263]},"#111100":{"lch":[4.69779601336656771,5.17885327658484673,85.8743202181747307],"luv":[4.69779601336656771,0.372589941443898953,5.16543299210515716],"rgb":[0.0666666666666666657,0.0666666666666666657,0],"xyz":[0.00431601219303203148,0.00520072542434782924,0.000776490229626805866],"hpluv":[85.8743202181747307,139.887458074797621,4.69779601336656771],"hsluv":[85.8743202181747307,100.000000000002359,4.69779601336656771]},"#111111":{"lch":[5.06332949289278655,2.68159353999537178e-13,0],"luv":[5.06332949289278655,2.52120910544652531e-13,9.13481559944393266e-14],"rgb":[0.0666666666666666657,0.0666666666666666657,0.0666666666666666657],"xyz":[0.00532767769266915322,0.00560539162420268383,0.00610459519438243722],"hpluv":[0,6.72041492281092e-12,5.06332949289278655],"hsluv":[0,1.92419399944792277e-12,5.06332949289278655]},"#111122":{"lch":[5.74093111710672321,6.60006851394265048,265.874320218179719],"luv":[5.74093111710672321,-0.474838542395297381,-6.58296534605743222],"rgb":[0.0666666666666666657,0.0666666666666666657,0.133333333333333331],"xyz":[0.00720303583114617271,0.00635553487959350169,0.0159814813903616341],"hpluv":[265.874320218179719,145.883251481840432,5.74093111710672321],"hsluv":[265.874320218179719,28.41442223352254,5.74093111710672321]},"#111133":{"lch":[6.85659263719390388,14.212336546779186,265.874320218178582],"luv":[6.85659263719390388,-1.02249925976518052,-14.1755072354640976],"rgb":[0.0666666666666666657,0.0666666666666666657,0.2],"xyz":[0.0102907865636039132,0.00759063517257661455,0.0322436352479727809],"hpluv":[265.874320218178582,263.024656142887807,6.85659263719390388],"hsluv":[265.874320218178582,51.2306489028398957,6.85659263719390388]},"#111144":{"lch":[8.45853257854777141,22.7927945223118087,265.874320218178241],"luv":[8.45853257854777141,-1.63981590573345759,-22.7337301367732181],"rgb":[0.0666666666666666657,0.0666666666666666657,0.266666666666666663],"xyz":[0.0147487818078227903,0.00937383327026419105,0.0557224102008594194],"hpluv":[265.874320218178241,341.933676209697239,8.45853257854777141],"hsluv":[265.874320218178241,66.600159737267461,8.45853257854777141]},"#111155":{"lch":[10.3782295585045325,32.1242805487719707,265.874320218178127],"luv":[10.3782295585045325,-2.31116487943396587,-32.0410349033279545],"rgb":[0.0666666666666666657,0.0666666666666666657,0.333333333333333315],"xyz":[0.0207111958471847858,0.011758798886009024,0.0871244574748333278],"hpluv":[265.874320218178127,392.780088665713265,10.3782295585045325],"hsluv":[265.874320218178127,76.5037738801481453,10.3782295585045325]},"#111166":{"lch":[12.4757228248048477,41.8651930040424887,265.87432021817807],"luv":[12.4757228248048477,-3.01196982745712916,-41.7567051265328644],"rgb":[0.0666666666666666657,0.0666666666666666657,0.4],"xyz":[0.0282961916206929606,0.0147927971954123355,0.12707210188197729],"hpluv":[265.87432021817807,425.820638501567,12.4757228248048477],"hsluv":[265.87432021817807,82.9392496755345263,12.4757228248048477]},"#111177":{"lch":[14.6896895275036599,51.8467212520223697,265.874320218178],"luv":[14.6896895275036599,-3.73008575521422614,-51.7123676197837199],"rgb":[0.0666666666666666657,0.0666666666666666657,0.466666666666666674],"xyz":[0.0376102046408948731,0.0185184024034931519,0.176125903788375199],"hpluv":[265.874320218178,447.865910041391658,14.6896895275036599],"hsluv":[265.874320218178,87.2331192419333235,14.6896895275036599]},"#111188":{"lch":[16.9766940539391484,61.9476661879793511,265.874320218178],"luv":[16.9766940539391484,-4.45679305530888747,-61.7871373491236469],"rgb":[0.0666666666666666657,0.0666666666666666657,0.533333333333333326],"xyz":[0.0487506217428387,0.0229745692442707428,0.234798767191947366],"hpluv":[265.874320218178,463.03215765547759,16.9766940539391484],"hsluv":[265.874320218178,90.1871263608273495,16.9766940539391484]},"#111199":{"lch":[19.3069968916820471,72.0861980616198537,265.874320218178],"luv":[19.3069968916820471,-5.18620452834737478,-71.8993966147786],"rgb":[0.0666666666666666657,0.0666666666666666657,0.6],"xyz":[0.0618075858519114146,0.0281973548878999072,0.303565444833065246],"hpluv":[265.874320218178,473.779996738615694,19.3069968916820471],"hsluv":[265.874320218178,92.280537596895428,19.3069968916820471]},"#1111aa":{"lch":[21.6605350192491244,82.2093341223612839,265.874320218177957],"luv":[21.6605350192491244,-5.91450835752722703,-81.9962999636616274],"rgb":[0.0666666666666666657,0.0666666666666666657,0.66666666666666663],"xyz":[0.0768652788171639567,0.0342204320740010087,0.382869294450063846],"hpluv":[265.874320218177957,481.605322004481,21.6605350192491244],"hsluv":[265.874320218177957,93.8047159652847569,21.6605350192491244]},"#1111bb":{"lch":[24.0238472654082429,92.2838634174554215,265.874320218177957],"luv":[24.0238472654082429,-6.63931519789526092,-92.0447225046312241],"rgb":[0.0666666666666666657,0.0666666666666666657,0.733333333333333282],"xyz":[0.0940028709245006,0.0410754689169357673,0.473127279548705593],"hpluv":[265.874320218177957,487.441538809997496,24.0238472654082429],"hsluv":[265.874320218177957,94.9414655706975736,24.0238472654082429]},"#1111cc":{"lch":[26.3879200105999,102.289669569688542,265.874320218177957],"luv":[26.3879200105999,-7.35917778701558589,-102.024599989292597],"rgb":[0.0666666666666666657,0.0666666666666666657,0.8],"xyz":[0.113295246368222344,0.0487924190944245689,0.574733790218975749],"hpluv":[265.874320218177957,491.887677819884516,26.3879200105999],"hsluv":[265.874320218177957,95.8074626598259727,26.3879200105999]},"#1111dd":{"lch":[28.7467318202544035,112.215160110792439,265.874320218177957],"luv":[28.7467318202544035,-8.07326211070735766,-111.924369989661315],"rgb":[0.0666666666666666657,0.0666666666666666657,0.866666666666666696],"xyz":[0.134813571918855901,0.0573997493146781,0.688063638118981746],"hpluv":[265.874320218177957,495.338839734480189,28.7467318202544035],"hsluv":[265.874320218177957,96.479663003878315,28.7467318202544035]},"#1111ee":{"lch":[31.096282761883856,122.054240127147821,265.874320218177957],"luv":[31.096282761883856,-8.78112967353786367,-121.737953386246701],"rgb":[0.0666666666666666657,0.0666666666666666657,0.933333333333333348],"xyz":[0.158625752185030638,0.0669246214211481338,0.813474454187505],"hpluv":[265.874320218177957,498.062358817216193,31.096282761883856],"hsluv":[265.874320218177957,97.0101366558694451,31.096282761883856]},"#1111ff":{"lch":[33.4339475813396589,131.804336466263976,265.874320218177957],"luv":[33.4339475813396589,-9.48259535137154863,-131.462783694528071],"rgb":[0.0666666666666666657,0.0666666666666666657,1],"xyz":[0.184796800594862032,0.07739304078508083,0.951308642479286815],"hpluv":[265.874320218177957,500.243401112503761,33.4339475813396589],"hsluv":[265.874320218177957,99.9999999999995168,33.4339475813396589]},"#66aa00":{"lch":[62.9888010115071921,81.5107592316300185,114.758667910078074],"luv":[62.9888010115071921,-34.136471206054587,74.0182761493062884],"rgb":[0.4,0.66666666666666663,0],"xyz":[0.198534632170994735,0.315734905503604946,0.0504821063864305253],"hpluv":[114.758667910078074,164.206718875588678,62.9888010115071921],"hsluv":[114.758667910078074,100.000000000002245,62.9888010115071921]},"#66aa11":{"lch":[63.0225323172591345,80.1801511939703744,115.156040801232848],"luv":[63.0225323172591345,-34.0833758310267285,72.5753411114886262],"rgb":[0.4,0.66666666666666663,0.0666666666666666657],"xyz":[0.199546297670631867,0.316139571703459799,0.055810211351186155],"hpluv":[115.156040801232848,161.439702186697787,63.0225323172591345],"hsluv":[115.156040801232848,97.8915472494395,63.0225323172591345]},"#66aa22":{"lch":[63.0849851082382287,77.7526522520100372,115.919582231606014],"luv":[63.0849851082382287,-33.9864002990590066,69.9313915701248],"rgb":[0.4,0.66666666666666663,0.133333333333333331],"xyz":[0.201421655809108868,0.316889714958850599,0.0656870975471653545],"hpluv":[115.919582231606014,156.397041701241,63.0849851082382287],"hsluv":[115.919582231606014,94.0329763727763748,63.0849851082382287]},"#66aa33":{"lch":[63.1875983682703577,73.8659889058236416,117.258215410479664],"luv":[63.1875983682703577,-33.8307250746429062,65.6632801340230827],"rgb":[0.4,0.66666666666666663,0.2],"xyz":[0.20450940654156663,0.318124815251833726,0.0819492514047764908],"hpluv":[117.258215410479664,148.337854737344315,63.1875983682703577],"hsluv":[117.258215410479664,87.8175568215645228,63.1875983682703577]},"#66aa44":{"lch":[63.3352806074861121,68.495891054581449,119.389904478135335],"luv":[63.3352806074861121,-33.6143748396710436,59.6804900742209057],"rgb":[0.4,0.66666666666666663,0.266666666666666663],"xyz":[0.208967401785785489,0.319908013349521292,0.105428026357663129],"hpluv":[119.389904478135335,137.232870385557277,63.3352806074861121],"hsluv":[119.389904478135335,79.1339200396795093,63.3352806074861121]},"#66aa55":{"lch":[63.5319451116367162,61.7628687774086131,122.670093374403834],"luv":[63.5319451116367162,-33.3396585938880747,51.9915293529473],"rgb":[0.4,0.66666666666666663,0.333333333333333315],"xyz":[0.214929815825147474,0.322292978965266153,0.136830073631637045],"hpluv":[122.670093374403834,123.360077774302084,63.5319451116367162],"hsluv":[122.670093374403834,68.0252461175988401,63.5319451116367162]},"#66aa66":{"lch":[63.7807317293932101,53.9656100581627314,127.715012949238869],"luv":[63.7807317293932101,-33.0126168434810836,42.6902119706049703],"rgb":[0.4,0.66666666666666663,0.4],"xyz":[0.22251481159865566,0.325326977274669471,0.176777718038781],"hpluv":[127.715012949238869,107.366036241042551,63.7807317293932101],"hsluv":[127.715012949238869,54.6684489206366493,63.7807317293932101]},"#66aa77":{"lch":[64.0841229578331308,45.668717811445795,135.623736827110406],"luv":[64.0841229578331308,-32.6422859505805931,31.9392071670853355],"rgb":[0.4,0.66666666666666663,0.466666666666666674],"xyz":[0.231828824618857565,0.329052582482750267,0.225831519945178916],"hpluv":[135.623736827110406,90.428994130424627,64.0841229578331308],"hsluv":[135.623736827110406,56.3153951517164302,64.0841229578331308]},"#66aa88":{"lch":[64.4440140424290888,37.9133385548872752,148.25026899586868],"luv":[64.4440140424290888,-32.239785461804118,19.9503752785341248],"rgb":[0.4,0.66666666666666663,0.533333333333333326],"xyz":[0.242969241720801421,0.333508749323527864,0.284504383348751055],"hpluv":[148.25026899586868,74.65325904191441,64.4440140424290888],"hsluv":[148.25026899586868,58.1346686170252127,64.4440140424290888]},"#66aa99":{"lch":[64.861761772217136,32.5704605989434484,167.654678216446086],"luv":[64.861761772217136,-31.8173259789585323,6.96366793981576304],"rgb":[0.4,0.66666666666666663,0.6],"xyz":[0.256026205829874121,0.338731534967157,0.353271060989869],"hpluv":[167.654678216446086,63.7198120272805681,64.861761772217136],"hsluv":[167.654678216446086,60.083023100845935,64.861761772217136]},"#66aaaa":{"lch":[65.3382237636027,32.1097126044189949,192.177050630060876],"luv":[65.3382237636027,-31.387258852500235,-6.77300710648746396],"rgb":[0.4,0.66666666666666663,0.66666666666666663],"xyz":[0.271083898795126677,0.344754612153258144,0.432574910606867591],"hpluv":[192.177050630060876,62.3603323483304592,65.3382237636027],"hsluv":[192.177050630060876,62.1162357127798757,65.3382237636027]},"#66aabb":{"lch":[65.8737942906507641,37.4229485671739255,214.174175962111633],"luv":[65.8737942906507641,-30.9612713447490577,-21.020864781881972],"rgb":[0.4,0.66666666666666663,0.733333333333333282],"xyz":[0.288221490902463295,0.351609648996192903,0.522832895705509282],"hpluv":[214.174175962111633,72.0882777495769744,65.8737942906507641],"hsluv":[214.174175962111633,64.192083786910942,65.8737942906507641]},"#66aacc":{"lch":[66.4684397846860833,46.8829891397435361,229.336310284400895],"luv":[66.4684397846860833,-30.5497910563422188,-35.5629714322516],"rgb":[0.4,0.66666666666666663,0.8],"xyz":[0.30751386634618505,0.359326599173681704,0.624439406375779438],"hpluv":[229.336310284400895,89.5033179536005,66.4684397846860833],"hsluv":[229.336310284400895,66.2725553364081748,66.4684397846860833]},"#66aadd":{"lch":[67.1217354618507471,58.5746749070863473,239.007496148652194],"luv":[67.1217354618507471,-30.1616186688116876,-50.2122425285710676],"rgb":[0.4,0.66666666666666663,0.866666666666666696],"xyz":[0.329032191896818593,0.367933929393935211,0.737769254275785435],"hpluv":[239.007496148652194,110.735287258409116,67.1217354618507471],"hsluv":[239.007496148652194,68.3252248927570349,67.1217354618507471]},"#66aaee":{"lch":[67.8329035309127,71.3390505041132,245.305718817638592],"luv":[67.8329035309127,-29.803771094245274,-64.8150858627082584],"rgb":[0.4,0.66666666666666663,0.933333333333333348],"xyz":[0.352844372162993358,0.377458801500405228,0.863180070344308659],"hpluv":[245.305718817638592,133.452355648257935,67.8329035309127],"hsluv":[245.305718817638592,80.362890995181445,67.8329035309127]},"#66aaff":{"lch":[68.6008528128324144,84.5574659264264312,249.594848961048797],"luv":[68.6008528128324144,-29.4814940521041038,-79.2515397475312],"rgb":[0.4,0.66666666666666663,1],"xyz":[0.379015420572824724,0.387927220864337952,1.00101425863609061],"hpluv":[249.594848961048797,156.409011428865199,68.6008528128324144],"hsluv":[249.594848961048797,99.9999999999981,68.6008528128324144]},"#112200":{"lch":[11.0156269675282488,14.1286449823385087,113.920199516574741],"luv":[11.0156269675282488,-5.72865521459208082,12.9151507335100835],"rgb":[0.0666666666666666657,0.133333333333333331,0],"xyz":[0.00803163592779996757,0.0126319728938838038,0.00201503147454941628],"hpluv":[113.920199516574741,162.753605553330914,11.0156269675282488],"hsluv":[113.920199516574741,100.000000000002302,11.0156269675282488]},"#112211":{"lch":[11.3010826742418828,9.17677244733547,127.715012949238741],"luv":[11.3010826742418828,-5.61374683501564142,7.25940762201209555],"rgb":[0.0666666666666666657,0.133333333333333331,0.0666666666666666657],"xyz":[0.00904330142743709,0.0130366390937386584,0.00734313643930504731],"hpluv":[127.715012949238741,103.040803658029205,11.3010826742418828],"hsluv":[127.715012949238741,52.4661346244892783,11.3010826742418828]},"#112222":{"lch":[11.8149934741043623,5.60956124878379736,192.177050630060876],"luv":[11.8149934741043623,-5.48334870304322308,-1.18324317225625331],"rgb":[0.0666666666666666657,0.133333333333333331,0.133333333333333331],"xyz":[0.0109186595659141079,0.0137867823491294762,0.0172200226352842434],"hpluv":[192.177050630060876,60.2469040904941551,11.8149934741043623],"hsluv":[192.177050630060876,60.0110800331641911,11.8149934741043623]},"#112233":{"lch":[12.6219648570067733,12.7575995118883281,244.93155638428982],"luv":[12.6219648570067733,-5.40540262923445,-11.5558629154900672],"rgb":[0.0666666666666666657,0.133333333333333331,0.2],"xyz":[0.0140064102983718484,0.0150218826421125891,0.0334821764928953866],"hpluv":[244.93155638428982,128.257072990564865,12.6219648570067733],"hsluv":[244.93155638428982,68.2965554989448265,12.6219648570067733]},"#112244":{"lch":[13.7124312845167182,23.0561698733830092,256.354402060867073],"luv":[13.7124312845167182,-5.43930918870511082,-22.4053762472305387],"rgb":[0.0666666666666666657,0.133333333333333331,0.266666666666666663],"xyz":[0.0184644055425907255,0.0168050807398001656,0.0569609514457820251],"hpluv":[256.354402060867073,213.359519949101497,13.7124312845167182],"hsluv":[256.354402060867073,75.5965994738604508,13.7124312845167182]},"#112255":{"lch":[15.056320299603339,33.5848303856462849,260.40485184836416],"luv":[15.056320299603339,-5.59809589119781581,-33.114983835502386],"rgb":[0.0666666666666666657,0.133333333333333331,0.333333333333333315],"xyz":[0.024426819581952721,0.019190046355545,0.0883629987197559336],"hpluv":[260.40485184836416,283.050313811336878,15.056320299603339],"hsluv":[260.40485184836416,81.3424686793067,15.056320299603339]},"#112266":{"lch":[16.6136212231118279,43.9645077489691118,262.329755848243337],"luv":[16.6136212231118279,-5.86800935048734384,-43.5711419160865958],"rgb":[0.0666666666666666657,0.133333333333333331,0.4],"xyz":[0.0320118153554608958,0.02222404466494831,0.128310643126899909],"hpluv":[262.329755848243337,335.797328537363626,16.6136212231118279],"hsluv":[262.329755848243337,85.6428490187558396,16.6136212231118279]},"#112277":{"lch":[18.3427569840269769,54.1821774694947678,263.400149453671133],"luv":[18.3427569840269769,-6.22740469049246936,-53.8231157232341815],"rgb":[0.0666666666666666657,0.133333333333333331,0.466666666666666674],"xyz":[0.0413258283756628153,0.0259496498730291264,0.177364445033297818],"hpluv":[263.400149453671133,374.827260372181627,18.3427569840269769],"hsluv":[263.400149453671133,88.8099744527525559,18.3427569840269769]},"#112288":{"lch":[20.2056943122802366,64.2803592359499,264.056887374403459],"luv":[20.2056943122802366,-6.65565132438349316,-63.9348644242795103],"rgb":[0.0666666666666666657,0.133333333333333331,0.533333333333333326],"xyz":[0.0524662454776066434,0.0304058167138067173,0.236037308436869986],"hpluv":[264.056887374403459,403.686144257890874,20.2056943122802366],"hsluv":[264.056887374403459,91.1461001738222762,20.2056943122802366]},"#112299":{"lch":[22.17018380613613,74.2918952905243799,264.488199331991609],"luv":[22.17018380613613,-7.13579321215933149,-73.9484020185124677],"rgb":[0.0666666666666666657,0.133333333333333331,0.6],"xyz":[0.0655232095866793568,0.0356286023574358818,0.304803986077987865],"hpluv":[264.488199331991609,425.217833826531262,22.17018380613613],"hsluv":[264.488199331991609,92.8866294242426136,22.17018380613613]},"#1122aa":{"lch":[24.2101316823922517,84.2333021684004848,264.786067280823943],"luv":[24.2101316823922517,-7.6546801533155886,-83.8847725510625537],"rgb":[0.0666666666666666657,0.133333333333333331,0.66666666666666663],"xyz":[0.0805809025519319,0.0416516795435369833,0.384107835694986466],"hpluv":[264.786067280823943,441.495215422381477,24.2101316823922517],"hsluv":[264.786067280823943,94.2012423737010209,24.2101316823922517]},"#1122bb":{"lch":[26.3050242232650149,94.1101405136049891,264.999933480500431],"luv":[26.3050242232650149,-8.20234804116949334,-93.7520134935884926],"rgb":[0.0666666666666666657,0.133333333333333331,0.733333333333333282],"xyz":[0.0977184946592685455,0.0485067163864717418,0.474365820793628212],"hpluv":[264.999933480500431,453.98033637772636,26.3050242232650149],"hsluv":[264.999933480500431,95.2089691602506605,26.3050242232650149]},"#1122cc":{"lch":[28.4390102065576187,103.92243186966887,265.158337172634162],"luv":[28.4390102065576187,-8.77130520241401612,-103.551610565708117],"rgb":[0.0666666666666666657,0.133333333333333331,0.8],"xyz":[0.117010870102990286,0.0562236665639605435,0.575972331463898368],"hpluv":[265.158337172634162,463.69685512523705,28.4390102065576187],"hsluv":[265.158337172634162,95.9928942328389496,28.4390102065576187]},"#1122dd":{"lch":[30.599961250020371,113.668086087716972,265.278693166229971],"luv":[30.599961250020371,-9.35592092097037664,-113.282392888591403],"rgb":[0.0666666666666666657,0.133333333333333331,0.866666666666666696],"xyz":[0.138529195653623816,0.0648309967842140705,0.689302179363904366],"hpluv":[265.278693166229971,471.364637015548908,30.599961250020371],"hsluv":[265.278693166229971,96.6113336559982,30.599961250020371]},"#1122ee":{"lch":[32.7786459144851463,123.344693870669801,265.37211393361406],"luv":[32.7786459144851463,-9.95194954038693247,-122.942556531067964],"rgb":[0.0666666666666666657,0.133333333333333331,0.933333333333333348],"xyz":[0.162341375919798553,0.0743558688906841,0.814712995432427589],"hpluv":[265.37211393361406,477.494956838131543,32.7786459144851463],"hsluv":[265.37211393361406,97.1056530386567118,32.7786459144851463]},"#1122ff":{"lch":[34.9680553815075,132.950321242967135,265.445956583698],"luv":[34.9680553815075,-10.5561742154436597,-132.530581770930752],"rgb":[0.0666666666666666657,0.133333333333333331,1],"xyz":[0.188512424329629946,0.0848242882546168114,0.952547183724209434],"hpluv":[265.445956583698,482.455471693424897,34.9680553815075],"hsluv":[265.445956583698,99.9999999999995595,34.9680553815075]},"#66bb00":{"lch":[68.2883247343563,91.0397787832552297,117.384795663234385],"luv":[68.2883247343563,-41.875036539852637,80.8376313092488914],"rgb":[0.4,0.733333333333333282,0],"xyz":[0.232489130079593515,0.383643901320803504,0.0618002723559631373],"hpluv":[117.384795663234385,169.170277477244,68.2883247343563],"hsluv":[117.384795663234385,100.000000000002402,68.2883247343563]},"#66bb11":{"lch":[68.3179499939719079,89.8644626985519466,117.727722441428654],"luv":[68.3179499939719079,-41.8112733484955896,79.5452014710932644],"rgb":[0.4,0.733333333333333282,0.0666666666666666657],"xyz":[0.233500795579230647,0.384048567520658357,0.0671283773207187739],"hpluv":[117.727722441428654,166.91389144855998,68.3179499939719079],"hsluv":[117.727722441428654,98.2613130840239108,68.3179499939719079]},"#66bb22":{"lch":[68.3728123300379309,87.7153655991844374,118.381366327960222],"luv":[68.3728123300379309,-41.6944557120635579,77.1722600749346839],"rgb":[0.4,0.733333333333333282,0.133333333333333331],"xyz":[0.235376153717707648,0.384798710776049158,0.0770052635166979665],"hpluv":[118.381366327960222,162.791438416492724,68.3728123300379309],"hsluv":[118.381366327960222,95.072259168186676,68.3728123300379309]},"#66bb33":{"lch":[68.4629872592027766,84.2604886941914515,119.511012469656151],"luv":[68.4629872592027766,-41.5059446288263274,73.3286200296498691],"rgb":[0.4,0.733333333333333282,0.2],"xyz":[0.238463904450165409,0.386033811069032284,0.0932674173743091],"hpluv":[119.511012469656151,156.173540237969377,68.4629872592027766],"hsluv":[119.511012469656151,89.9157079277594846,68.4629872592027766]},"#66bb44":{"lch":[68.5928402387875451,79.4555052917154683,121.268984442668796],"luv":[68.5928402387875451,-41.2418962487648457,67.9137932600435761],"rgb":[0.4,0.733333333333333282,0.266666666666666663],"xyz":[0.242921899694384269,0.38781700916671985,0.116746192327195741],"hpluv":[121.268984442668796,146.988898097842537,68.5928402387875451],"hsluv":[121.268984442668796,82.6706505658268,68.5928402387875451]},"#66bb55":{"lch":[68.7658933773114711,73.3682686647690474,123.883282583110088],"luv":[68.7658933773114711,-40.9030232283392365,60.9085013577552417],"rgb":[0.4,0.733333333333333282,0.333333333333333315],"xyz":[0.248884313733746254,0.390201974782464711,0.148148239601169657],"hpluv":[123.883282583110088,135.386233996967292,68.7658933773114711],"hsluv":[123.883282583110088,73.3323147762666565,68.7658933773114711]},"#66bb66":{"lch":[68.985024233854773,66.1955602149500493,127.715012949239338],"luv":[68.985024233854773,-40.4940973290304953,52.3648763359380354],"rgb":[0.4,0.733333333333333282,0.4],"xyz":[0.256469309507254439,0.39323597309186803,0.188095884008313619],"hpluv":[127.715012949239338,121.76244699215377,68.985024233854773],"hsluv":[127.715012949239338,61.9987879490855889,68.985024233854773]},"#66bb77":{"lch":[69.2525700917713,58.3030977404443149,133.351332140289912],"luv":[69.2525700917713,-40.0233333378199134,42.395565742911792],"rgb":[0.4,0.733333333333333282,0.466666666666666674],"xyz":[0.265783322527456345,0.396961578299948825,0.237149685914711528],"hpluv":[133.351332140289912,106.830450835327468,69.2525700917713],"hsluv":[133.351332140289912,63.1630001095548,69.2525700917713]},"#66bb88":{"lch":[69.5703900447947632,50.3125315521488901,141.732198444264128],"luv":[69.5703900447947632,-39.5016033478381487,31.1604583428427624],"rgb":[0.4,0.733333333333333282,0.533333333333333326],"xyz":[0.276923739629400201,0.401417745140726423,0.295822549318283667],"hpluv":[141.732198444264128,91.7679559652102625,69.5703900447947632],"hsluv":[141.732198444264128,64.4651291783298888,69.5703900447947632]},"#66bb99":{"lch":[69.9399065468101924,43.264635747530761,154.168279853010262],"luv":[69.9399065468101924,-38.9415329854338594,18.8516767188195544],"rgb":[0.4,0.733333333333333282,0.6],"xyz":[0.2899807037384729,0.406640530784355581,0.364589226959401602],"hpluv":[154.168279853010262,78.4959645714096865,69.9399065468101924],"hsluv":[154.168279853010262,65.8787579381207848,69.9399065468101924]},"#66bbaa":{"lch":[70.3621368583926881,38.7745898249838348,171.579209782291855],"luv":[70.3621368583926881,-38.3565710720190296,5.67822786553099768],"rgb":[0.4,0.733333333333333282,0.66666666666666663],"xyz":[0.305038396703725456,0.412663607970456703,0.443893076576400203],"hpluv":[171.579209782291855,69.9274221790226278,70.3621368583926881],"hsluv":[171.579209782291855,67.37547943529961,70.3621368583926881]},"#66bbbb":{"lch":[70.8377198879813221,38.6292683935386592,192.177050630060933],"luv":[70.8377198879813221,-37.7601276376370194,-8.14819841495072161],"rgb":[0.4,0.733333333333333282,0.733333333333333282],"xyz":[0.322175988811062075,0.419518644813391461,0.534151061675042],"hpluv":[192.177050630060933,69.1976324805594629,70.8377198879813221],"hsluv":[192.177050630060933,68.9267726464228758,70.8377198879813221]},"#66bbcc":{"lch":[71.3669414392261103,43.4063518114314,211.106579982723787],"luv":[71.3669414392261103,-37.1648552128157,-22.4250956427882215],"rgb":[0.4,0.733333333333333282,0.8],"xyz":[0.341468364254783829,0.427235594990880263,0.635757572345312161],"hpluv":[211.106579982723787,77.1783568223693806,71.3669414392261103],"hsluv":[211.106579982723787,70.5055674968506736,71.3669414392261103]},"#66bbdd":{"lch":[71.9497594848613602,52.0084901368895629,225.300588521911806],"luv":[71.9497594848613602,-36.5821167433876298,-36.9679831866995059],"rgb":[0.4,0.733333333333333282,0.866666666666666696],"xyz":[0.362986689805417373,0.435842925211133769,0.749087420245318159],"hpluv":[225.300588521911806,91.724261948179759,71.9497594848613602],"hsluv":[225.300588521911806,72.0874065633031336,71.9497594848613602]},"#66bbee":{"lch":[72.5858302461357852,62.9426478584387183,235.089705553804919],"luv":[72.5858302461357852,-36.0216507769597953,-51.6160594653851135],"rgb":[0.4,0.733333333333333282,0.933333333333333348],"xyz":[0.386798870071592082,0.445367797317603786,0.874498236313841382],"hpluv":[235.089705553804919,110.035415213757233,72.5858302461357852],"hsluv":[235.089705553804919,76.908955535231712,72.5858302461357852]},"#66bbff":{"lch":[73.2745353232689638,75.1445669264803513,241.815748730615525],"luv":[73.2745353232689638,-35.4914181095182641,-66.2349241650025817],"rgb":[0.4,0.733333333333333282,1],"xyz":[0.412969918481423504,0.45583621668153651,1.01233242460562312],"hpluv":[241.815748730615525,130.131920555189879,73.2745353232689638],"hsluv":[241.815748730615525,99.9999999999976552,73.2745353232689638]},"#113300":{"lch":[17.8585390793191152,25.0449080182821966,121.332554648991049],"luv":[17.8585390793191152,-13.0234653569097247,21.3924465113644295],"rgb":[0.0666666666666666657,0.2,0],"xyz":[0.0141493580168107792,0.0248674170719056023,0.00405427217088629669],"hpluv":[121.332554648991049,177.956083469309505,17.8585390793191152],"hsluv":[121.332554648991049,100.000000000002288,17.8585390793191152]},"#113311":{"lch":[18.041211184449395,20.8015074137336562,127.715012949239792],"luv":[18.041211184449395,-12.7249964056056086,16.4553084796651738],"rgb":[0.0666666666666666657,0.2,0.0666666666666666657],"xyz":[0.015161023516447901,0.0252720832717604552,0.00938237713564192902],"hpluv":[127.715012949239792,146.308124837666583,18.041211184449395],"hsluv":[127.715012949239792,74.4969128915689254,18.041211184449395]},"#113322":{"lch":[18.3747440863758129,14.8635488733567129,145.575764327225926],"luv":[18.3747440863758129,-12.2605616690393529,8.40260154180516672],"rgb":[0.0666666666666666657,0.2,0.133333333333333331],"xyz":[0.0170363816549249196,0.0260222265271512765,0.0192592633316211251],"hpluv":[145.575764327225926,102.645648490479701,18.3747440863758129],"hsluv":[145.575764327225926,76.8412554017051,18.3747440863758129]},"#113333":{"lch":[18.910205854271,11.9516732098830207,192.177050630061075],"luv":[18.910205854271,-11.6827660646039035,-2.5210056714682163],"rgb":[0.0666666666666666657,0.2,0.2],"xyz":[0.0201241323873826601,0.0272573268201343893,0.0355214171892322683],"hpluv":[192.177050630061075,80.19952200231,18.910205854271],"hsluv":[192.177050630061075,79.885597544945341,18.910205854271]},"#113344":{"lch":[19.6554681695294136,18.5554908550806559,233.185939638237187],"luv":[19.6554681695294136,-11.1188227079461566,-14.8552355236207116],"rgb":[0.0666666666666666657,0.2,0.266666666666666663],"xyz":[0.0245821276316015372,0.0290405249178219624,0.0590001921421189068],"hpluv":[233.185939638237187,119.79215597403514,19.6554681695294136],"hsluv":[233.185939638237187,83.0941728600946163,19.6554681695294136]},"#113355":{"lch":[20.6059777210847557,29.4541985213363553,248.778672986371305],"luv":[20.6059777210847557,-10.6615827984222822,-27.4568837045772334],"rgb":[0.0666666666666666657,0.2,0.333333333333333315],"xyz":[0.0305445416709635327,0.0314254905335667953,0.0904022394160928222],"hpluv":[248.778672986371305,181.381634101163,20.6059777210847557],"hsluv":[248.778672986371305,86.0667890851332089,20.6059777210847557]},"#113366":{"lch":[21.7480278014825927,41.0513772817531262,255.392805777322508],"luv":[21.7480278014825927,-10.3527823296753354,-39.7244946445288178],"rgb":[0.0666666666666666657,0.2,0.4],"xyz":[0.0381295374444717075,0.0344594888429701068,0.130349883823236784],"hpluv":[255.392805777322508,239.522976201191227,21.7480278014825927],"hsluv":[255.392805777322508,88.6137215649990537,21.7480278014825927]},"#113377":{"lch":[23.0621320749224097,52.4639032010768602,258.793139915612699],"luv":[23.0621320749224097,-10.1964540550792755,-51.4635158514711293],"rgb":[0.0666666666666666657,0.2,0.466666666666666674],"xyz":[0.04744355046467362,0.0381850940510509232,0.179403685729634693],"hpluv":[258.793139915612699,288.669213908808,23.0621320749224097],"hsluv":[258.793139915612699,90.7010140503724926,23.0621320749224097]},"#113388":{"lch":[24.5260866455155693,63.5079254673071389,260.778240759541745],"luv":[24.5260866455155693,-10.1775299952298717,-62.6871157444433322],"rgb":[0.0666666666666666657,0.2,0.533333333333333326],"xyz":[0.0585839675666174481,0.0426412608918285141,0.238076549133206861],"hpluv":[260.778240759541745,328.5783880762134,24.5260866455155693],"hsluv":[260.778240759541745,92.3732755032704,24.5260866455155693]},"#113399":{"lch":[26.1173586094444445,74.1961224571638667,262.040130643645512],"luv":[26.1173586094444445,-10.2746398160742789,-73.4812653968914447],"rgb":[0.0666666666666666657,0.2,0.6],"xyz":[0.0716409316756901615,0.0478640465354576786,0.30684322677432474],"hpluv":[262.040130643645512,360.488391101723778,26.1173586094444445],"hsluv":[262.040130643645512,93.700931793532277,26.1173586094444445]},"#1133aa":{"lch":[27.8146812937100378,84.589542856674214,262.892366774415223],"luv":[27.8146812937100378,-10.4665753413372666,-83.9395113240792767],"rgb":[0.0666666666666666657,0.2,0.66666666666666663],"xyz":[0.0866986246409427,0.0538871237215587801,0.386147076391323341],"hpluv":[262.892366774415223,385.906342473210088,27.8146812937100378],"hsluv":[262.892366774415223,94.7540719156047544,27.8146812937100378]},"#1133bb":{"lch":[29.5989386642012917,94.7497677250031245,263.494633441352278],"luv":[29.5989386642012917,-10.734795769033445,-94.1396974912241],"rgb":[0.0666666666666666657,0.2,0.733333333333333282],"xyz":[0.10383621674827935,0.0607421605644935386,0.476405061489965087],"hpluv":[263.494633441352278,406.201330660618282,29.5989386642012917],"hsluv":[263.494633441352278,95.59270717643426,29.5989386642012917]},"#1133cc":{"lch":[31.4535171675710927,104.725970292241058,263.935552719695806],"luv":[31.4535171675710927,-11.0639900580249932,-104.139891384844844],"rgb":[0.0666666666666666657,0.2,0.8],"xyz":[0.123128592192001091,0.0684591107419823403,0.578011572160235243],"hpluv":[263.935552719695806,422.497850271701509,31.4535171675710927],"hsluv":[263.935552719695806,96.2648935085394299,31.4535171675710927]},"#1133dd":{"lch":[33.3643121953656845,114.554023382916327,264.267643395871801],"luv":[33.3643121953656845,-11.4418472625073555,-113.981175658242762],"rgb":[0.0666666666666666657,0.2,0.866666666666666696],"xyz":[0.144646917742634634,0.0770664409622358743,0.691341420060241241],"hpluv":[264.267643395871801,435.679872957086786,33.3643121953656845],"hsluv":[264.267643395871801,96.8079128297289913,33.3643121953656845]},"#1133ee":{"lch":[35.3195411096734375,124.259061611723823,264.523680461367292],"luv":[35.3195411096734375,-11.8585821660500468,-123.691909281234786],"rgb":[0.0666666666666666657,0.2,0.933333333333333348],"xyz":[0.168459098008809371,0.0865913130687059,0.816752236128764464],"hpluv":[264.523680461367292,446.428943593210363,35.3195411096734375],"hsluv":[264.523680461367292,97.2502875158067752,35.3195411096734375]},"#1133ff":{"lch":[37.3094684856901466,133.858437412957159,264.724991571549936],"luv":[37.3094684856901466,-12.3064438760062309,-133.291532760957],"rgb":[0.0666666666666666657,0.2,1],"xyz":[0.194630146418640765,0.0970597324326386,0.954586424420546309],"hpluv":[264.724991571549936,455.266836240216,37.3094684856901466],"hsluv":[264.724991571549936,99.9999999999995168,37.3094684856901466]},"#66cc00":{"lch":[73.5514640948473328,100.417876322708906,119.311479215942285],"luv":[73.5514640948473328,-49.1602904566029579,87.5614968315714322],"rgb":[0.4,0.8,0],"xyz":[0.270712873389210462,0.460091387940038399,0.0745415201258350923],"hpluv":[119.311479215942285,173.244332851636557,73.5514640948473328],"hsluv":[119.311479215942285,100.00000000000226,73.5514640948473328]},"#66cc11":{"lch":[73.5777109322390572,99.3703085818509,119.605548775623987],"luv":[73.5777109322390572,-49.0915230219986611,86.397225621155485],"rgb":[0.4,0.8,0.0666666666666666657],"xyz":[0.271724538888847567,0.460496054139893252,0.079869625090590729],"hpluv":[119.605548775623987,171.375877908330068,73.5777109322390572],"hsluv":[119.605548775623987,98.5479766016144652,73.5777109322390572]},"#66cc22":{"lch":[73.626324929263788,97.4510710028789333,120.162888823436575],"luv":[73.626324929263788,-48.965269014871545,84.2562381661406192],"rgb":[0.4,0.8,0.133333333333333331],"xyz":[0.273599897027324623,0.461246197395284052,0.0897465112865699216],"hpluv":[120.162888823436575,167.954954584373922,73.626324929263788],"hsluv":[120.162888823436575,95.8800576342850377,73.626324929263788]},"#66cc33":{"lch":[73.7062524827218084,94.3550745227874,121.116489614327122],"luv":[73.7062524827218084,-48.7607908304490252,80.779114663321],"rgb":[0.4,0.8,0.2],"xyz":[0.276687647759782329,0.462481297688267179,0.106008665144181058],"hpluv":[121.116489614327122,162.442721914772306,73.7062524827218084],"hsluv":[121.116489614327122,91.5533619475712896,73.7062524827218084]},"#66cc44":{"lch":[73.8213986639243,90.0253172969507318,122.577193203242018],"luv":[73.8213986639243,-48.472817822786439,75.8613451418811451],"rgb":[0.4,0.8,0.266666666666666663],"xyz":[0.281145643004001244,0.464264495785954745,0.129487440097067696],"hpluv":[122.577193203242018,154.746814564283426,73.8213986639243],"hsluv":[122.577193203242018,85.4474048693987527,73.8213986639243]},"#66cc55":{"lch":[73.974942724102462,84.4934421806047879,124.700104619680829],"luv":[73.974942724102462,-48.100513339115345,69.4656921655632118],"rgb":[0.4,0.8,0.333333333333333315],"xyz":[0.28710805704336323,0.466649461401699606,0.160889487371041612],"hpluv":[124.700104619680829,144.936474170198323,73.974942724102462],"hsluv":[124.700104619680829,77.5306238992907,73.974942724102462]},"#66cc66":{"lch":[74.1695172801941567,77.8884466842553564,127.715012949239551],"luv":[74.1695172801941567,-47.6470375142611218,61.6146893443493937],"rgb":[0.4,0.8,0.4],"xyz":[0.294693052816871415,0.469683459711102924,0.200837131778185574],"hpluv":[127.715012949239551,133.256044234036153,74.1695172801941567],"hsluv":[127.715012949239551,67.8510775159793,74.1695172801941567]},"#66cc77":{"lch":[74.407302734023375,70.4570986939790203,131.971622948486811],"luv":[74.407302734023375,-47.1190629819757234,52.3831715350808551],"rgb":[0.4,0.8,0.466666666666666674],"xyz":[0.30400706583707332,0.47340906491918372,0.249890933684583483],"hpluv":[131.971622948486811,120.156846155807528,74.407302734023375],"hsluv":[131.971622948486811,68.6882673601907499,74.407302734023375]},"#66cc88":{"lch":[74.6900832163393602,62.6050444414555045,138.002106903522986],"luv":[74.6900832163393602,-46.5261552249599859,41.8892404980031543],"rgb":[0.4,0.8,0.533333333333333326],"xyz":[0.315147482939017121,0.477865231759961318,0.308563797088155622],"hpluv":[138.002106903522986,106.361808066007058,74.6900832163393602],"hsluv":[138.002106903522986,69.6340873295455367,74.6900832163393602]},"#66cc99":{"lch":[75.0192831982140405,54.9734389641357382,146.572693391746668],"luv":[75.0192831982140405,-45.8800303566060634,30.2836887782922481],"rgb":[0.4,0.8,0.6],"xyz":[0.32820444704808982,0.483088017403590475,0.377330474729273557],"hpluv":[146.572693391746668,92.9863760296957338,75.0192831982140405],"hsluv":[146.572693391746668,70.6723780843603,75.0192831982140405]},"#66ccaa":{"lch":[75.3959940236094,48.5503126646653,158.569930493367366],"luv":[75.3959940236094,-45.1937477629490587,17.7386026218441692],"rgb":[0.4,0.8,0.66666666666666663],"xyz":[0.343262140013342376,0.489111094589691597,0.456634324346272158],"hpluv":[158.569930493367366,81.7114817988577187,75.3959940236094],"hsluv":[158.569930493367366,71.7849539596152368,75.3959940236094]},"#66ccbb":{"lch":[75.8209952994642,44.7015709258060667,174.304687468137473],"luv":[75.8209952994642,-44.4809102105153755,4.43610979112772],"rgb":[0.4,0.8,0.733333333333333282],"xyz":[0.360399732120679051,0.495966131432626356,0.546892309444913849],"hpluv":[174.304687468137473,74.8122352806046109,75.8209952994642],"hsluv":[174.304687468137473,72.9527506991265113,75.8209952994642]},"#66cccc":{"lch":[76.2947739303160262,44.7620652034609634,192.177050630061018],"luv":[76.2947739303160262,-43.7549393425641924,-9.44180938207391],"rgb":[0.4,0.8,0.8],"xyz":[0.379692107564400749,0.503683081610115102,0.648498820115184],"hpluv":[192.177050630061018,75.6461904464395,76.2947739303160262],"hsluv":[192.177050630061018,74.1568646821725679,76.2947739303160262]},"#66ccdd":{"lch":[76.8175423984245782,49.1342213212598082,208.868111129197587],"luv":[76.8175423984245782,-43.0284772118039101,-23.7217590764220851],"rgb":[0.4,0.8,0.866666666666666696],"xyz":[0.401210433115034348,0.512290411830368719,0.76182866801519],"hpluv":[208.868111129197587,85.3113130970085791,76.8175423984245782],"hsluv":[208.868111129197587,75.3794120212671714,76.8175423984245782]},"#66ccee":{"lch":[77.3892571801628435,57.0358051623713038,222.109360872917676],"luv":[77.3892571801628435,-42.312941633688304,-38.245235531031426],"rgb":[0.4,0.8,0.933333333333333348],"xyz":[0.425022613381209058,0.521815283936838736,0.887239484083713226],"hpluv":[222.109360872917676,102.061631830010029,77.3892571801628435],"hsluv":[222.109360872917676,76.6041682601332923,77.3892571801628435]},"#66ccff":{"lch":[78.0096377377628158,67.2874731922355664,231.792303901557148],"luv":[78.0096377377628158,-41.6182406850010267,-52.872734853430309],"rgb":[0.4,0.8,1],"xyz":[0.451193661791040479,0.53228370330077146,1.02507367237549518],"hpluv":[231.792303901557148,124.498094909356865,78.0096377377628158],"hsluv":[231.792303901557148,99.9999999999966604,78.0096377377628158]},"#114400":{"lch":[24.4916204196936391,35.767443133059956,124.131260038140155],"luv":[24.4916204196936391,-20.0687794552527912,29.6066559991685239],"rgb":[0.0666666666666666657,0.266666666666666663,0],"xyz":[0.0229819284997768124,0.0425325580378379114,0.00699846233187489172],"hpluv":[124.131260038140155,185.314627891622,24.4916204196936391],"hsluv":[124.131260038140155,100.000000000002416,24.4916204196936391]},"#114411":{"lch":[24.6196313539200702,32.2821952767626144,127.715012949240105],"luv":[24.6196313539200702,-19.7481274165088401,25.5372589646650674],"rgb":[0.0666666666666666657,0.266666666666666663,0.0666666666666666657],"xyz":[0.0239935939994139341,0.0429372242376927643,0.0123265672966305223],"hpluv":[127.715012949240105,166.387555424049935,24.6196313539200702],"hsluv":[127.715012949240105,84.7209219338948,24.6196313539200702]},"#114422":{"lch":[24.8548180969752792,26.7199561105771828,135.968420687644709],"luv":[24.8548180969752792,-19.2104947057918238,18.5718321042894203],"rgb":[0.0666666666666666657,0.266666666666666663,0.133333333333333331],"xyz":[0.0258689521378909527,0.0436873674930835856,0.0222034534926097184],"hpluv":[135.968420687644709,136.415738329938534,24.8548180969752792],"hsluv":[135.968420687644709,85.5945702875542338,24.8548180969752792]},"#114433":{"lch":[25.2362525898650887,20.3101465248311648,155.348193219538018],"luv":[25.2362525898650887,-18.4590663004995292,8.4714180143512],"rgb":[0.0666666666666666657,0.266666666666666663,0.2],"xyz":[0.0289567028703486933,0.0449224677860667,0.0384656073502208651],"hpluv":[155.348193219538018,102.123929581721498,25.2362525898650887],"hsluv":[155.348193219538018,86.8340768870874911,25.2362525898650887]},"#114444":{"lch":[25.774812755707849,18.0038630185053101,192.177050630061132],"luv":[25.774812755707849,-17.5987843886529483,-3.79761393914762779],"rgb":[0.0666666666666666657,0.266666666666666663,0.266666666666666663],"xyz":[0.0334146981145675703,0.0467056658837542715,0.0619443823031075036],"hpluv":[192.177050630061132,88.6358691141452226,25.774812755707849],"hsluv":[192.177050630061132,88.2889223192016743,25.774812755707849]},"#114455":{"lch":[26.4741010086829718,23.9697809957076196,225.699525295295985],"luv":[26.4741010086829718,-16.7410035671662349,-17.1548593857925162],"rgb":[0.0666666666666666657,0.266666666666666663,0.333333333333333315],"xyz":[0.0393771121539295693,0.0490906314994991044,0.0933464295770814],"hpluv":[225.699525295295985,114.889984549549212,26.4741010086829718],"hsluv":[225.699525295295985,89.7968555301121398,26.4741010086829718]},"#114466":{"lch":[27.3316922889079734,34.6428172107851182,242.548035964380944],"luv":[27.3316922889079734,-15.9705048052882308,-30.7419544037807526],"rgb":[0.0666666666666666657,0.266666666666666663,0.4],"xyz":[0.0469621079274377407,0.052124629808902416,0.133294073984225381],"hpluv":[242.548035964380944,160.837015687659374,27.3316922889079734],"hsluv":[242.548035964380944,91.2329185483074525,27.3316922889079734]},"#114477":{"lch":[28.3404051997208484,46.6267191674415,250.798290317077829],"luv":[28.3404051997208484,-15.3352867062553546,-44.0327142242719844],"rgb":[0.0666666666666666657,0.266666666666666663,0.466666666666666674],"xyz":[0.0562761209476396601,0.0558502350169832323,0.18234787589062329],"hpluv":[250.798290317077829,208.770018220734585,28.3404051997208484],"hsluv":[250.798290317077829,92.5248535524246734,28.3404051997208484]},"#114488":{"lch":[29.4896359978219706,58.6954673108201632,255.342505689020953],"luv":[29.4896359978219706,-14.8523182742844106,-56.7852667926718055],"rgb":[0.0666666666666666657,0.266666666666666663,0.533333333333333326],"xyz":[0.0674165380495834882,0.0603064018577608302,0.241020739294195457],"hpluv":[255.342505689020953,252.565763352450517,29.4896359978219706],"hsluv":[255.342505689020953,93.6449802292742817,29.4896359978219706]},"#114499":{"lch":[30.7666487879374699,70.4540899928233699,258.107815399211404],"luv":[30.7666487879374699,-14.5185243824624628,-68.9419411278265102],"rgb":[0.0666666666666666657,0.266666666666666663,0.6],"xyz":[0.0804735021586562,0.0655291875013899877,0.309787416935313364],"hpluv":[258.107815399211404,290.579747717257874,30.7666487879374699],"hsluv":[258.107815399211404,94.5943513350065359,30.7666487879374699]},"#1144aa":{"lch":[32.1577052090601185,81.8040240174920541,259.917940759024589],"luv":[32.1577052090601185,-14.3204852090806973,-80.5408098347099894],"rgb":[0.0666666666666666657,0.266666666666666663,0.66666666666666663],"xyz":[0.0955311951239087437,0.0715522646874911,0.389091266552311965],"hpluv":[259.917940759024589,322.796594575352628,32.1577052090601185],"hsluv":[259.917940759024589,95.3887741756132925,32.1577052090601185]},"#1144bb":{"lch":[33.6489603009756664,92.7573184463875435,261.168522985608377],"luv":[33.6489603009756664,-14.2409127099882671,-91.6576048702508928],"rgb":[0.0666666666666666657,0.266666666666666663,0.733333333333333282],"xyz":[0.11266878723124539,0.0784073015304258547,0.479349251650953712],"hpluv":[261.168522985608377,349.796823816496214,33.6489603009756664],"hsluv":[261.168522985608377,96.0495339612535872,33.6489603009756664]},"#1144cc":{"lch":[35.2271045850644597,103.362934076908545,262.068884255867147],"luv":[35.2271045850644597,-14.262270250582695,-102.374234005860515],"rgb":[0.0666666666666666657,0.266666666666666663,0.8],"xyz":[0.131961162674967131,0.0861242517079146563,0.580955762321223812],"hpluv":[262.068884255867147,372.329293739207799,35.2271045850644597],"hsluv":[262.068884255867147,96.5982694251149638,35.2271045850644597]},"#1144dd":{"lch":[36.8797734412618,113.676357292171801,262.73849416957313],"luv":[36.8797734412618,-14.3684841809298494,-112.764625967365617],"rgb":[0.0666666666666666657,0.266666666666666663,0.866666666666666696],"xyz":[0.153479488225600674,0.0947315819281681903,0.69428561022122981],"hpluv":[262.73849416957313,391.13009227787353,36.8797734412618],"hsluv":[262.73849416957313,97.0546019825808,36.8797734412618]},"#1144ee":{"lch":[38.5957670998368911,123.747928777943088,263.249741523271325],"luv":[38.5957670998368911,-14.5455638799169762,-122.890099065165245],"rgb":[0.0666666666666666657,0.266666666666666663,0.933333333333333348],"xyz":[0.177291668491775412,0.104256454034638207,0.819696426289753],"hpluv":[263.249741523271325,406.853061330967478,38.5957670998368911],"hsluv":[263.249741523271325,97.4353215147193623,38.5957670998368911]},"#1144ff":{"lch":[40.3651306844127546,133.619394536005728,263.648645306126298],"luv":[40.3651306844127546,-14.7816672391382209,-132.799265471613751],"rgb":[0.0666666666666666657,0.266666666666666663,1],"xyz":[0.203462716901606805,0.114724873398570917,0.957530614581534878],"hpluv":[263.648645306126298,420.051425771921231,40.3651306844127546],"hsluv":[263.648645306126298,99.9999999999994458,40.3651306844127546]},"#66dd00":{"lch":[78.7732081443282084,109.616469768408933,120.762072840728067],"luv":[78.7732081443282084,-56.0659919017112145,94.1932853050883],"rgb":[0.4,0.866666666666666696,0],"xyz":[0.313346863936385611,0.54535936903439,0.0887528503082264109],"hpluv":[120.762072840728067,211.559351010719155,78.7732081443282084],"hsluv":[120.762072840728067,100.000000000002444,78.7732081443282084]},"#66dd11":{"lch":[78.7966434753788576,108.675393583239966,121.014553037765893],"luv":[78.7966434753788576,-55.9956244091473678,93.1387739746547],"rgb":[0.4,0.866666666666666696,0.0666666666666666657],"xyz":[0.314358529436022716,0.545764035234244882,0.0940809552729820475],"hpluv":[121.014553037765893,210.019013530063148,78.7966434753788576],"hsluv":[121.014553037765893,98.7735218244661866,78.7966434753788576]},"#66dd22":{"lch":[78.8400557089289435,106.948443645027297,121.491090191754736],"luv":[78.8400557089289435,-55.866227309203758,91.1972271745690506],"rgb":[0.4,0.866666666666666696,0.133333333333333331],"xyz":[0.316233887574499772,0.546514178489635682,0.10395784146896124],"hpluv":[121.491090191754736,207.186246213049145,78.8400557089289435],"hsluv":[121.491090191754736,96.5169147905729,78.8400557089289435]},"#66dd33":{"lch":[78.911446872650572,104.154611952746194,122.300539429107701],"luv":[78.911446872650572,-55.6560904548950788,88.037394249850891],"rgb":[0.4,0.866666666666666696,0.2],"xyz":[0.319321638306957478,0.547749278782618809,0.120219995326572376],"hpluv":[122.300539429107701,202.586476802368111,78.911446872650572],"hsluv":[122.300539429107701,92.8487034899531807,78.911446872650572]},"#66dd44":{"lch":[79.0143300648071687,100.229492569308348,123.526451276321851],"luv":[79.0143300648071687,-55.3589437481663,83.5544045983716899],"rgb":[0.4,0.866666666666666696,0.266666666666666663],"xyz":[0.323779633551176393,0.549532476880306375,0.143698770279459015],"hpluv":[123.526451276321851,196.088207898644384,79.0143300648071687],"hsluv":[123.526451276321851,87.6539206040991843,79.0143300648071687]},"#66dd55":{"lch":[79.1515854502963,95.1800529291645603,125.27945296022996],"luv":[79.1515854502963,-54.9726586351532518,77.699737954393882],"rgb":[0.4,0.866666666666666696,0.333333333333333315],"xyz":[0.329742047590538379,0.551917442496051125,0.17510081755343293],"hpluv":[125.27945296022996,187.665730733793509,79.1515854502963],"hsluv":[125.27945296022996,80.8868034505971707,79.1515854502963]},"#66dd66":{"lch":[79.3256225689869723,89.0890908185611607,127.715012949239735],"luv":[79.3256225689869723,-54.4988561596450722,70.4751075215082921],"rgb":[0.4,0.866666666666666696,0.4],"xyz":[0.337327043364046564,0.554951440805454443,0.215048461960576892],"hpluv":[127.715012949239735,177.410630109117562,79.3256225689869723],"hsluv":[127.715012949239735,72.5638298210149,79.3256225689869723]},"#66dd77":{"lch":[79.538466322575843,82.1265081512392072,131.057978326052336],"luv":[79.538466322575843,-53.9425296262626048,61.9271090770055395],"rgb":[0.4,0.866666666666666696,0.466666666666666674],"xyz":[0.346641056384248469,0.558677046013535294,0.264102263866974774],"hpluv":[131.057978326052336,165.560886464635786,79.538466322575843],"hsluv":[131.057978326052336,73.175897046832489,79.538466322575843]},"#66dd88":{"lch":[79.7918079202835173,74.5708009722257543,135.636001638862723],"luv":[79.7918079202835173,-53.3115731427918,52.1409678341327378],"rgb":[0.4,0.866666666666666696,0.533333333333333326],"xyz":[0.35778147348619227,0.563133212854312837,0.322775127270546969],"hpluv":[135.636001638862723,152.557764790666909,79.7918079202835173],"hsluv":[135.636001638862723,73.8730513922454435,79.7918079202835173]},"#66dd99":{"lch":[80.0870378401123162,66.8478260511747919,141.915708870878916],"luv":[80.0870378401123162,-52.6162023759431392,41.2330825345609924],"rgb":[0.4,0.866666666666666696,0.6],"xyz":[0.370838437595265,0.568355998497942,0.391541804911664904],"hpluv":[141.915708870878916,139.150468179242381,80.0870378401123162],"hsluv":[141.915708870878916,74.6453790933081791,80.0870378401123162]},"#66ddaa":{"lch":[80.4252690581998877,59.592891473004542,150.502508447871207],"luv":[80.4252690581998877,-51.8682972227800931,29.3426729750825572],"rgb":[0.4,0.866666666666666696,0.66666666666666663],"xyz":[0.385896130560517525,0.574379075684043117,0.470845654528663449],"hpluv":[150.502508447871207,126.571308780245914,80.4252690581998877],"hsluv":[150.502508447871207,75.4812281571350638,80.4252690581998877]},"#66ddbb":{"lch":[80.807354973374558,53.7174771102931672,161.973645166837599],"luv":[80.807354973374558,-51.0807157732000334,16.6231111165276104],"rgb":[0.4,0.866666666666666696,0.733333333333333282],"xyz":[0.4030337226678542,0.581234112526977875,0.561103639627305251],"hpluv":[161.973645166837599,116.757770235740637,80.807354973374558],"hsluv":[161.973645166837599,76.3679029554368753,80.807354973374558]},"#66ddcc":{"lch":[81.2339045655681389,50.3705316419042148,176.319314132685548],"luv":[81.2339045655681389,-50.2666331071559256,3.23358221770368903],"rgb":[0.4,0.866666666666666696,0.8],"xyz":[0.422326098111575954,0.588951062704466732,0.662710150297575407],"hpluv":[176.319314132685548,112.393665646206941,81.2339045655681389],"hsluv":[176.319314132685548,77.2923330418296501,81.2339045655681389]},"#66dddd":{"lch":[81.7052962965957903,50.5769089008318389,192.177050630061],"luv":[81.7052962965957903,-49.4389517336029,-10.6683534552210038],"rgb":[0.4,0.866666666666666696,0.866666666666666696],"xyz":[0.443844423662209442,0.597558392924720239,0.776039998197581404],"hpluv":[192.177050630061,116.242714563462513,81.7052962965957903],"hsluv":[192.177050630061,78.2416694753709407,81.7052962965957903]},"#66ddee":{"lch":[82.2216916522674524,54.6311568672274745,207.154140846621715],"luv":[82.2216916522674524,-48.6098170936026577,-24.9328895793911158],"rgb":[0.4,0.866666666666666696,0.933333333333333348],"xyz":[0.467656603928384207,0.607083265031190256,0.901450814266104627],"hpluv":[207.154140846621715,129.793318723999505,82.2216916522674524],"hsluv":[207.154140846621715,79.2037752530793,82.2216916522674524]},"#66ddff":{"lch":[82.7830488398693376,61.9519841783888339,219.519505956683815],"luv":[82.7830488398693376,-47.7902556034158366,-39.4225799891321387],"rgb":[0.4,0.866666666666666696,1],"xyz":[0.493827652338215572,0.617551684395123,1.03928500255788636],"hpluv":[219.519505956683815,152.730665075356086,82.7830488398693376],"hsluv":[219.519505956683815,99.9999999999957,82.7830488398693376]},"#115500":{"lch":[30.9160157060817227,46.0913193883500583,125.457330883646421],"luv":[30.9160157060817227,-26.7374134918097575,37.543580579466358],"rgb":[0.0666666666666666657,0.333333333333333315,0],"xyz":[0.0347951852141227744,0.0661590714665301755,0.0109362145699901016],"hpluv":[125.457330883646421,189.179880792461034,30.9160157060817227],"hsluv":[125.457330883646421,100.000000000002402,30.9160157060817227]},"#115511":{"lch":[31.0114762783458957,43.2230667766736616,127.715012949240275],"luv":[31.0114762783458957,-26.4410342208804181,34.1921805521528697],"rgb":[0.0666666666666666657,0.333333333333333315,0.0666666666666666657],"xyz":[0.0358068507137599,0.0665637376663850283,0.0162643195347457331],"hpluv":[127.715012949240275,176.861157643680144,31.0114762783458957],"hsluv":[127.715012949240275,90.0538522348087156,31.0114762783458957]},"#115522":{"lch":[31.1874163697014737,38.3803455570512071,132.492971129528541],"luv":[31.1874163697014737,-25.925914044022,28.3001396827055132],"rgb":[0.0666666666666666657,0.333333333333333315,0.133333333333333331],"xyz":[0.0376822088522369147,0.0673138809217758427,0.0261412057307249292],"hpluv":[132.492971129528541,156.159643444286843,31.1874163697014737],"hsluv":[132.492971129528541,90.4316047034468653,31.1874163697014737]},"#115533":{"lch":[31.4742731349983,31.7764378074468219,142.363318860140765],"luv":[31.4742731349983,-25.163724938647551,19.4043538140960301],"rgb":[0.0666666666666666657,0.333333333333333315,0.2],"xyz":[0.0407699595846946553,0.0685489812147589556,0.0424033595883360759],"hpluv":[142.363318860140765,128.111709873485438,31.4742731349983],"hsluv":[142.363318860140765,90.9947325890089616,31.4742731349983]},"#115544":{"lch":[31.8824114421380642,25.5202649789159608,161.635705606154772],"luv":[31.8824114421380642,-24.2205825435089714,8.04035483341083435],"rgb":[0.0666666666666666657,0.333333333333333315,0.266666666666666663],"xyz":[0.0452279548289135358,0.0703321793124465355,0.0658821345412227144],"hpluv":[161.635705606154772,101.571845607751229,31.8824114421380642],"hsluv":[161.635705606154772,91.6999843863076194,31.8824114421380642]},"#115555":{"lch":[32.417637609391285,23.7206023942150033,192.177050630061103],"luv":[32.417637609391285,-23.1868997601056,-5.00346454561771115],"rgb":[0.0666666666666666657,0.333333333333333315,0.333333333333333315],"xyz":[0.0511903688682755278,0.0727171449281913684,0.0972841818151966159],"hpluv":[192.177050630061103,92.8503782686988899,32.417637609391285],"hsluv":[192.177050630061103,92.4869346485079,32.417637609391285]},"#115566":{"lch":[33.0818646063754045,29.1355194477524577,220.509575549450261],"luv":[33.0818646063754045,-22.1516601517912761,-18.9257085999416219],"rgb":[0.0666666666666666657,0.333333333333333315,0.4],"xyz":[0.0587753646417837061,0.0757511432375946869,0.137231826222340592],"hpluv":[220.509575549450261,111.756325010930979,33.0818646063754045],"hsluv":[220.509575549450261,93.2955878193037904,33.0818646063754045]},"#115577":{"lch":[33.8736729304774826,39.2775515041961185,237.359341141202208],"luv":[33.8736729304774826,-21.1850732372535866,-33.0744421585153958],"rgb":[0.0666666666666666657,0.333333333333333315,0.466666666666666674],"xyz":[0.0680893776619856117,0.0794767484456755,0.186285628128738501],"hpluv":[237.359341141202208,147.13684637222039,33.8736729304774826],"hsluv":[237.359341141202208,94.078253732623736,33.8736729304774826]},"#115588":{"lch":[34.7888943497230514,51.2161985161337938,246.60972521059881],"luv":[34.7888943497230514,-20.3324266151093482,-47.0073549392562455],"rgb":[0.0666666666666666657,0.333333333333333315,0.533333333333333326],"xyz":[0.0792297947639294398,0.0839329152864531,0.244958491532310668],"hpluv":[246.60972521059881,186.812546427038342,34.7888943497230514],"hsluv":[246.60972521059881,94.8038015691112719,34.7888943497230514]},"#115599":{"lch":[35.8212274371681403,63.5782184467896,252.028930114170464],"luv":[35.8212274371681403,-19.6162163870228632,-60.476391389741444],"rgb":[0.0666666666666666657,0.333333333333333315,0.6],"xyz":[0.0922867588730021671,0.0891557009300822517,0.313725169173428575],"hpluv":[252.028930114170464,225.22013661091745,35.8212274371681403],"hsluv":[252.028930114170464,95.4562831747131355,35.8212274371681403]},"#1155aa":{"lch":[36.9628521043173777,75.808874965898184,255.452401876993463],"luv":[36.9628521043173777,-19.0419916394553752,-73.3783897206670588],"rgb":[0.0666666666666666657,0.333333333333333315,0.66666666666666663],"xyz":[0.107344451838254695,0.0951787781161833601,0.393029018790427176],"hpluv":[255.452401876993463,260.251896817562397,36.9628521043173777],"hsluv":[255.452401876993463,96.0310300088164155,36.9628521043173777]},"#1155bb":{"lch":[38.2050019251475845,87.6984258792502089,257.752076786877694],"luv":[38.2050019251475845,-18.6045414354547205,-85.7023041678273216],"rgb":[0.0666666666666666657,0.333333333333333315,0.733333333333333282],"xyz":[0.124482043945591342,0.102033814959118119,0.483287003889068922],"hpluv":[257.752076786877694,291.280156181798475,38.2050019251475845],"hsluv":[257.752076786877694,96.5305142623785,38.2050019251475845]},"#1155cc":{"lch":[39.5384610498345523,99.1886562531622,259.372402836059223],"luv":[39.5384610498345523,-18.292845239891637,-97.4872368176334447],"rgb":[0.0666666666666666657,0.333333333333333315,0.8],"xyz":[0.143774419389313096,0.10975076513660692,0.584893514559339],"hpluv":[259.372402836059223,318.332933912315752,39.5384610498345523],"hsluv":[259.372402836059223,96.9610449958707,39.5384610498345523]},"#1155dd":{"lch":[40.9539668975822053,110.288480556245688,260.557616930578945],"luv":[40.9539668975822053,-18.0934548393737415,-108.7941902648341],"rgb":[0.0666666666666666657,0.333333333333333315,0.866666666666666696],"xyz":[0.16529274493994664,0.118358095356860454,0.698223362459345],"hpluv":[260.557616930578945,341.722445031840948,40.9539668975822053],"hsluv":[260.557616930578945,97.330522563257,40.9539668975822053]},"#1155ee":{"lch":[42.4425141949683038,121.034132372772049,261.450904748640312],"luv":[42.4425141949683038,-17.992549051743687,-119.689303523123172],"rgb":[0.0666666666666666657,0.333333333333333315,0.933333333333333348],"xyz":[0.189104925206121377,0.127882967463330471,0.823634178527868244],"hpluv":[261.450904748640312,361.864588035447412,42.4425141949683038],"hsluv":[261.450904748640312,97.6470858685672596,42.4425141949683038]},"#1155ff":{"lch":[43.9955669218353762,131.469960671873054,262.140820458865903],"luv":[43.9955669218353762,-17.9770474653633769,-130.235081001594637],"rgb":[0.0666666666666666657,0.333333333333333315,1],"xyz":[0.21527597361595277,0.138351386827263168,0.961468366819650089],"hpluv":[262.140820458865903,379.190057269809415,43.9955669218353762],"hsluv":[262.140820458865903,99.9999999999993321,43.9955669218353762]},"#66ee00":{"lch":[83.9510288300903511,118.631054776009961,121.878900606421581],"luv":[83.9510288300903511,-62.6521043104729145,100.737485489455693],"rgb":[0.4,0.933333333333333348,0],"xyz":[0.360525640276900428,0.639716921715420939,0.104479109088397595],"hpluv":[121.878900606421581,316.932305812441825,83.9510288300903511],"hsluv":[121.878900606421581,100.000000000002245,83.9510288300903511]},"#66ee11":{"lch":[83.9720997528394406,117.779830021225337,122.096631719604488],"luv":[83.9720997528394406,-62.5821687959979371,99.7775551344988401],"rgb":[0.4,0.933333333333333348,0.0666666666666666657],"xyz":[0.361537305776537532,0.640121587915275847,0.109807214053153232],"hpluv":[122.096631719604488,315.12898496103287,83.9720997528394406],"hsluv":[122.096631719604488,98.9534187198870825,83.9720997528394406]},"#66ee22":{"lch":[84.0111361497279461,116.215639794016113,122.506307539641156],"luv":[84.0111361497279461,-62.4534076072217061,98.0084017366813498],"rgb":[0.4,0.933333333333333348,0.133333333333333331],"xyz":[0.363412663915014589,0.640871731170666648,0.119684100249132425],"hpluv":[122.506307539641156,311.807725961995743,84.0111361497279461],"hsluv":[122.506307539641156,97.0256918708191,84.0111361497279461]},"#66ee33":{"lch":[84.0753427139745213,113.679067087327056,123.198426402241765],"luv":[84.0753427139745213,-62.2438639038254564,95.1242960560921],"rgb":[0.4,0.933333333333333348,0.2],"xyz":[0.366500414647472295,0.642106831463649774,0.135946254106743575],"hpluv":[123.198426402241765,306.400922934277446,84.0753427139745213],"hsluv":[123.198426402241765,93.8862320250161133,84.0753427139745213]},"#66ee44":{"lch":[84.1678970009459704,110.10189342016568,124.237870325737177],"luv":[84.1678970009459704,-61.946619900698991,91.0222127702015626],"rgb":[0.4,0.933333333333333348,0.266666666666666663],"xyz":[0.37095840989169121,0.64389002956133734,0.159425029059630213],"hpluv":[124.237870325737177,298.731636810455427,84.1678970009459704],"hsluv":[124.237870325737177,89.4277949312148337,84.1678970009459704]},"#66ee55":{"lch":[84.2914184237995414,105.474359140243067,125.706632503417083],"luv":[84.2914184237995414,-61.5585501093914829,85.6468641835444657],"rgb":[0.4,0.933333333333333348,0.333333333333333315],"xyz":[0.376920823931053195,0.64627499517708209,0.190827076333604129],"hpluv":[125.706632503417083,288.732343582820249,84.2914184237995414],"hsluv":[125.706632503417083,83.5978964235977315,84.2914184237995414]},"#66ee66":{"lch":[84.4481159447294374,99.8471979874678652,127.715012949239849],"luv":[84.4481159447294374,-61.0799597466417339,78.9854509596370633],"rgb":[0.4,0.933333333333333348,0.4],"xyz":[0.38450581970456138,0.649308993486485408,0.230774720740748063],"hpluv":[127.715012949239849,276.453302697114395,84.4481159447294374],"hsluv":[127.715012949239849,76.3932656313124454,84.4481159447294374]},"#66ee77":{"lch":[84.6398667383604391,93.3381964812165,130.416076861551431],"luv":[84.6398667383604391,-60.5142850922984863,71.0636350191446127],"rgb":[0.4,0.933333333333333348,0.466666666666666674],"xyz":[0.393819832724763286,0.65303459869456626,0.279828522647145972],"hpluv":[130.416076861551431,262.087365127001192,84.6398667383604391],"hsluv":[130.416076861551431,76.8478121958162461,84.6398667383604391]},"#66ee88":{"lch":[84.8682629083727704,86.1443361377895513,134.024834696689027],"luv":[84.8682629083727704,-59.8677379892917187,61.9411058721588503],"rgb":[0.4,0.933333333333333348,0.533333333333333326],"xyz":[0.404960249826707086,0.657490765535343802,0.338501386050718167],"hpluv":[134.024834696689027,246.018013362770375,84.8682629083727704],"hsluv":[134.024834696689027,77.3690194462974858,84.8682629083727704]},"#66ee99":{"lch":[85.1346415661787432,78.5628931277068,138.840952739605655],"luv":[85.1346415661787432,-59.1488647733143296,51.7062856200641718],"rgb":[0.4,0.933333333333333348,0.6],"xyz":[0.418017213935779841,0.662713551178973,0.407268063691836046],"hpluv":[138.840952739605655,228.905884969324347,85.1346415661787432],"hsluv":[138.840952739605655,77.9507906120806,85.1346415661787432]},"#66eeaa":{"lch":[85.4401056772853451,71.025817565124143,145.263991147699045],"luv":[85.4401056772853451,-58.3680297483556387,40.4702342974360576],"rgb":[0.4,0.933333333333333348,0.66666666666666663],"xyz":[0.433074906901032342,0.668736628365074082,0.486571913308834647],"hpluv":[145.263991147699045,211.83630636980061,85.4401056772853451],"hsluv":[145.263991147699045,78.5856392083502868,85.4401056772853451]},"#66eebb":{"lch":[85.7855396574798448,64.1465768711300228,153.7611846199909],"luv":[85.7855396574798448,-57.5368534805651,28.3600743271201026],"rgb":[0.4,0.933333333333333348,0.733333333333333282],"xyz":[0.450212499008369,0.67559166520800884,0.576829898407476449],"hpluv":[153.7611846199909,196.542528595596508,85.7855396574798448],"hsluv":[153.7611846199909,79.2651091664321683,85.7855396574798448]},"#66eecc":{"lch":[86.1716220205593,58.7524914026155827,164.690737532459508],"luv":[86.1716220205593,-56.6676444389915517,15.5123602250092176],"rgb":[0.4,0.933333333333333348,0.8],"xyz":[0.46950487445209077,0.683308615385497697,0.678436409077746605],"hpluv":[164.690737532459508,185.645341664818176,86.1716220205593],"hsluv":[164.690737532459508,79.9801993176568118,86.1716220205593]},"#66eedd":{"lch":[86.5988364705929285,55.8111450312658164,177.877712634427581],"luv":[86.5988364705929285,-55.7728621696499403,2.06682246606168096],"rgb":[0.4,0.933333333333333348,0.866666666666666696],"xyz":[0.491023200002724258,0.691915945605751204,0.791766256977752603],"hpluv":[177.877712634427581,182.628088950326941,86.5988364705929285],"hsluv":[177.877712634427581,80.7217641095277543,86.5988364705929285]},"#66eeee":{"lch":[87.0674822997282263,56.1274864183573783,192.177050630061075],"luv":[87.0674822997282263,-54.8646438121880493,-11.8391549953798521],"rgb":[0.4,0.933333333333333348,0.933333333333333348],"xyz":[0.514835380268899,0.701440817712221221,0.917177073046275826],"hpluv":[192.177050630061075,191.066910285097691,87.0674822997282263],"hsluv":[192.177050630061075,81.4808670778618165,87.0674822997282263]},"#66eeff":{"lch":[87.5776846199412518,59.9248574237073512,205.793536431897621],"luv":[87.5776846199412518,-53.9544158989052178,-26.0750751151289606],"rgb":[0.4,0.933333333333333348,1],"xyz":[0.541006428678730389,0.711909237076154,1.05501126133805756],"hpluv":[205.793536431897621,213.276590696447101,87.5776846199412518],"hsluv":[205.793536431897621,99.9999999999933351,87.5776846199412518]},"#116600":{"lch":[37.1543973335168118,56.0416844920186463,126.180156646926719],"luv":[37.1543973335168118,-33.0828721903909511,45.2348755755691201],"rgb":[0.0666666666666666657,0.4,0],"xyz":[0.0498232429199692434,0.0962151868782235159,0.0159455671386054508],"hpluv":[126.180156646926719,191.399273993181851,37.1543973335168118],"hsluv":[126.180156646926719,100.000000000002359,37.1543973335168118]},"#116611":{"lch":[37.2288128297302237,53.6508389550451668,127.715012949240304],"luv":[37.2288128297302237,-32.820060550512963,42.4412081124095124],"rgb":[0.0666666666666666657,0.4,0.0666666666666666657],"xyz":[0.0508349084196063616,0.0966198530780783688,0.021273672103361084],"hpluv":[127.715012949240304,182.867554307566394,37.2288128297302237],"hsluv":[127.715012949240304,93.1121786917857719,37.2288128297302237]},"#116622":{"lch":[37.366211587350719,49.4912288674311,130.823584918521846],"luv":[37.366211587350719,-32.3540076461773722,37.4513006989015125],"rgb":[0.0666666666666666657,0.4,0.133333333333333331],"xyz":[0.0527102665580833837,0.0973699963334691831,0.0311505582993402766],"hpluv":[130.823584918521846,168.069340269208539,37.366211587350719],"hsluv":[130.823584918521846,93.2954788878505639,37.366211587350719]},"#116633":{"lch":[37.5909073580641291,43.4152735453156,136.786924005406348],"luv":[37.5909073580641291,-31.6415889136076061,29.7270218494359924],"rgb":[0.0666666666666666657,0.4,0.2],"xyz":[0.0557980172905411242,0.0986050966264523,0.0474127121569514198],"hpluv":[136.786924005406348,146.554466726503705,37.5909073580641291],"hsluv":[136.786924005406348,93.5769168130898095,37.5909073580641291]},"#116644":{"lch":[37.9120295698984506,36.3990668435195417,147.559563985674146],"luv":[37.9120295698984506,-30.7189764474867033,19.525279846848818],"rgb":[0.0666666666666666657,0.4,0.266666666666666663],"xyz":[0.0602560125347600048,0.100388294724139876,0.0708914871098380583],"hpluv":[147.559563985674146,121.829522477327146,37.9120295698984506],"hsluv":[147.559563985674146,93.9439470857740559,37.9120295698984506]},"#116655":{"lch":[38.335629212958338,30.5483862618043851,166.063087397862887],"luv":[38.335629212958338,-29.6490880228798659,7.35768187759708869],"rgb":[0.0666666666666666657,0.4,0.333333333333333315],"xyz":[0.066218426574122,0.102773260339884709,0.102293534383811974],"hpluv":[166.063087397862887,101.117192726530064,38.335629212958338],"hsluv":[166.063087397862887,94.3739252192313529,38.335629212958338]},"#116666":{"lch":[38.8651381017916293,29.1618890828208741,192.177050630061217],"luv":[38.8651381017916293,-28.5057600031098204,-6.15121301239451324],"rgb":[0.0666666666666666657,0.4,0.4],"xyz":[0.0738034223476301682,0.105807258649288027,0.142241178790955936],"hpluv":[192.177050630061217,95.2126746116157392,38.8651381017916293],"hsluv":[192.177050630061217,94.8399842705083245,38.8651381017916293]},"#116677":{"lch":[39.5016809883423079,34.1234106492309124,216.700227589977857],"luv":[39.5016809883423079,-27.3592385518708738,-20.3931169809293316],"rgb":[0.0666666666666666657,0.4,0.466666666666666674],"xyz":[0.0831174353678320876,0.109532863857368837,0.191294980697353845],"hpluv":[216.700227589977857,109.616563248578402,39.5016809883423079],"hsluv":[216.700227589977857,95.3164106037614687,39.5016809883423079]},"#116688":{"lch":[40.2443638992953723,43.6497103352843823,233.004065811630028],"luv":[40.2443638992953723,-26.2665776267255495,-34.862072688430807],"rgb":[0.0666666666666666657,0.4,0.533333333333333326],"xyz":[0.0942578524697759157,0.113989030698146435,0.249967844100926],"hpluv":[233.004065811630028,137.630797119362086,40.2443638992953723],"hsluv":[233.004065811630028,95.7822055324918864,40.2443638992953723]},"#116699":{"lch":[41.090575936542443,55.3002439505846866,242.81143298809],"luv":[41.090575936542443,-25.2678118474637223,-49.189985418125],"rgb":[0.0666666666666666657,0.4,0.6],"xyz":[0.107314816578848629,0.119211816341775592,0.318734521742043919],"hpluv":[242.81143298809,170.774941475023041,41.090575936542443],"hsluv":[242.81143298809,96.2225162944548202,41.090575936542443]},"#1166aa":{"lch":[42.0363074660961757,67.6887016451143,248.882871092507173],"luv":[42.0363074660961757,-24.3865946332867658,-63.1431257746482615],"rgb":[0.0666666666666666657,0.4,0.66666666666666663],"xyz":[0.122372509544101171,0.125234893527876701,0.39803837135904252],"hpluv":[248.882871092507173,204.329442679703192,42.0363074660961757],"hsluv":[248.882871092507173,96.6284204735347885,42.0363074660961757]},"#1166bb":{"lch":[43.0764730814379746,80.16050208662584,252.853006907061769],"luv":[43.0764730814379746,-23.6332520973097289,-76.5974901030376429],"rgb":[0.0666666666666666657,0.4,0.733333333333333282],"xyz":[0.139510101651437818,0.132089930370811459,0.488296356457684266],"hpluv":[252.853006907061769,236.134594524550181,43.0764730814379746],"hsluv":[252.853006907061769,96.9958197331145,43.0764730814379746]},"#1166cc":{"lch":[44.2052232400861271,92.4179309088515737,255.583916344807051],"luv":[44.2052232400861271,-23.008531750755072,-89.5079964033815259],"rgb":[0.0666666666666666657,0.4,0.8],"xyz":[0.158802477095159544,0.139806880548300261,0.589902867127954367],"hpluv":[255.583916344807051,265.290671800528912,44.2052232400861271],"hsluv":[255.583916344807051,97.3240770235992159,44.2052232400861271]},"#1166dd":{"lch":[45.4162296513266455,104.336857924373376,257.542523527564185],"luv":[45.4162296513266455,-22.507022318245486,-101.880390006599782],"rgb":[0.0666666666666666657,0.4,0.866666666666666696],"xyz":[0.180320802645793088,0.148414210768553795,0.703232715027960364],"hpluv":[257.542523527564185,291.518421142394175,45.4162296513266455],"hsluv":[257.542523527564185,97.6147741074162,45.4162296513266455]},"#1166ee":{"lch":[46.7029335650228674,115.880201424536907,258.995545526323895],"luv":[46.7029335650228674,-22.1198283610194721,-113.74943637429719],"rgb":[0.0666666666666666657,0.4,0.933333333333333348],"xyz":[0.204132982911967853,0.157939082875023812,0.828643531096483588],"hpluv":[258.995545526323895,314.850514307077333,46.7029335650228674],"hsluv":[258.995545526323895,97.870742288996567,46.7029335650228674]},"#1166ff":{"lch":[48.0587511138394348,127.054293237547355,260.103604495506659],"luv":[48.0587511138394348,-21.8364562757409573,-125.163743182322264],"rgb":[0.0666666666666666657,0.4,1],"xyz":[0.230304031321799219,0.168407502238956508,0.966477719388265433],"hpluv":[260.103604495506659,335.471932494038299,48.0587511138394348],"hsluv":[260.103604495506659,99.9999999999992184,48.0587511138394348]},"#66ff00":{"lch":[89.0839511722278417,127.467952451328657,122.755484474710229],"luv":[89.0839511722278417,-68.9671698198214074,107.198919720201],"rgb":[0.4,1,0],"xyz":[0.41237801270657426,0.74342166657477,0.121763233231621706],"hpluv":[122.755484474710229,522.717702913530729,89.0839511722278417],"hsluv":[122.755484474710229,100.000000000002402,89.0839511722278417]},"#66ff11":{"lch":[89.1030144718140917,126.693355761899767,122.944319876693868],"luv":[89.1030144718140917,-68.8988566751164342,106.320994836735167],"rgb":[0.4,1,0.0666666666666666657],"xyz":[0.413389678206211364,0.743826332774625,0.127091338196377329],"hpluv":[122.944319876693868,520.53129302948,89.1030144718140917],"hsluv":[122.944319876693868,99.9999999999913456,89.1030144718140917]},"#66ff22":{"lch":[89.1383344673707825,125.268361506965746,123.29878008449495],"luv":[89.1383344673707825,-68.7729595958538482,104.70168300016438],"rgb":[0.4,1,0.133333333333333331],"xyz":[0.415265036344688421,0.744576476030015755,0.136968224392356536],"hpluv":[123.29878008449495,516.49933125362179,89.1383344673707825],"hsluv":[123.29878008449495,99.9999999999914877,89.1383344673707825]},"#66ff33":{"lch":[89.1964366933732,122.952924407393425,123.895139690212403],"luv":[89.1964366933732,-68.5677350303770368,102.0582546055644],"rgb":[0.4,1,0.2],"xyz":[0.418352787077146127,0.745811576322998881,0.153230378249967686],"hpluv":[123.895139690212403,509.920932540516333,89.1964366933732],"hsluv":[123.895139690212403,99.9999999999913456,89.1964366933732]},"#66ff44":{"lch":[89.2802097655713,119.677402261160566,124.785058224819977],"luv":[89.2802097655713,-68.2758869475835439,98.2908127624369143],"rgb":[0.4,1,0.266666666666666663],"xyz":[0.422810782321365042,0.747594774420686448,0.176709153202854324],"hpluv":[124.785058224819977,500.557479589511445,89.2802097655713],"hsluv":[124.785058224819977,99.9999999999914451,89.2802097655713]},"#66ff55":{"lch":[89.392045372062455,115.420778437487911,126.031255758612545],"luv":[89.392045372062455,-67.8935601171189802,93.3403481337991536],"rgb":[0.4,1,0.333333333333333315],"xyz":[0.428773196360727,0.749979740036431197,0.20811120047682824],"hpluv":[126.031255758612545,488.288652672415253,89.392045372062455],"hsluv":[126.031255758612545,99.9999999999912177,89.392045372062455]},"#66ff66":{"lch":[89.5339732348528088,110.211236984550467,127.715012949239977],"luv":[89.5339732348528088,-67.4199983006921428,87.1840615410833664],"rgb":[0.4,1,0.4],"xyz":[0.436358192134235212,0.753013738345834516,0.248058844883972174],"hpluv":[127.715012949239977,473.1190638884799,89.5339732348528088],"hsluv":[127.715012949239977,99.9999999999912177,89.5339732348528088]},"#66ff77":{"lch":[89.7077333531255,104.130085615398272,129.945267186452298],"luv":[89.7077333531255,-66.8572982376138754,79.8321764869086223],"rgb":[0.4,1,0.466666666666666674],"xyz":[0.445672205154437118,0.756739343553915367,0.297112646790370083],"hpluv":[129.945267186452298,455.203211628206077,89.7077333531255],"hsluv":[129.945267186452298,99.9999999999911466,89.7077333531255]},"#66ff88":{"lch":[89.9148190538961529,97.3190386909276413,132.870301628739924],"luv":[89.9148190538961529,-66.2101394375234378,71.3246992800528687],"rgb":[0.4,1,0.533333333333333326],"xyz":[0.456812622256380918,0.761195510394692909,0.355785510193942278],"hpluv":[132.870301628739924,434.894743355908361,89.9148190538961529],"hsluv":[132.870301628739924,99.999999999990834,89.9148190538961529]},"#66ff99":{"lch":[90.1565046807361,89.9924560427222247,136.692010716646195],"luv":[90.1565046807361,-65.4854512845448795,61.7276098246221707],"rgb":[0.4,1,0.6],"xyz":[0.469869586365453618,0.766418296038322122,0.424552187835060157],"hpluv":[136.692010716646195,412.835114866532763,90.1565046807361],"hsluv":[136.692010716646195,99.9999999999909903,90.1565046807361]},"#66ffaa":{"lch":[90.4338646074596113,82.4570841698328678,141.679424663280656],"luv":[90.4338646074596113,-64.6920148236146,51.1284064669742264],"rgb":[0.4,1,0.66666666666666663],"xyz":[0.484927279330706174,0.772441373224423189,0.503856037452058758],"hpluv":[141.679424663280656,390.10709074414774,90.4338646074596113],"hsluv":[141.679424663280656,99.9999999999905924,90.4338646074596113]},"#66ffbb":{"lch":[90.747787175062669,75.1410408914244101,148.168460326052468],"luv":[90.747787175062669,-63.84001546278364,39.631155067172358],"rgb":[0.4,1,0.733333333333333282],"xyz":[0.502064871438042792,0.779296410067358,0.59411402255070056],"hpluv":[148.168460326052468,368.486167720556807,90.747787175062669],"hsluv":[148.168460326052468,99.9999999999904077,90.747787175062669]},"#66ffcc":{"lch":[91.0989856399247486,68.6265957122477,156.512275503644645],"luv":[91.0989856399247486,-62.9405723747737085,27.3513068826366492],"rgb":[0.4,1,0.8],"xyz":[0.521357246881764547,0.787013360244846805,0.695720533220970716],"hpluv":[156.512275503644645,350.804850059399143,91.0989856399247486],"hsluv":[156.512275503644645,99.9999999999903508,91.0989856399247486]},"#66ffdd":{"lch":[91.4880074096490716,63.6578272942329875,166.916209854529029],"luv":[91.4880074096490716,-62.0052733715472826,14.410588119229697],"rgb":[0.4,1,0.866666666666666696],"xyz":[0.542875572432398146,0.795620690465100311,0.809050381120976714],"hpluv":[166.916209854529029,341.336295176875581,91.4880074096490716],"hsluv":[166.916209854529029,99.9999999999898819,91.4880074096490716]},"#66ffee":{"lch":[91.9152423718395113,61.0528599966611765,179.125088100836763],"luv":[91.9152423718395113,-61.045742111776633,0.93224663816584552],"rgb":[0.4,1,0.933333333333333348],"xyz":[0.566687752698572855,0.805145562571570328,0.934461197189499937],"hpluv":[179.125088100836763,345.840646438583576,91.9152423718395113],"hsluv":[179.125088100836763,99.9999999999894,91.9152423718395113]},"#66ffff":{"lch":[92.3809308294128,61.4559907165056,192.17705063006116],"luv":[92.3809308294128,-60.0732592166006256,-12.9631139022354667],"rgb":[0.4,1,1],"xyz":[0.592858801108404276,0.815613981935503,1.07229538548128178],"hpluv":[192.17705063006116,370.76546272919029,92.3809308294128],"hsluv":[192.17705063006116,99.9999999999889866,92.3809308294128]},"#117700":{"lch":[43.2300348418233042,65.6725964696673685,126.613348243544976],"luv":[43.2300348418233042,-39.1679175007181684,52.7139845365981],"rgb":[0.0666666666666666657,0.466666666666666674,0],"xyz":[0.0682769809733868721,0.133122662985059287,0.0220968131564111547],"hpluv":[126.613348243544976,192.769325646383436,43.2300348418233042],"hsluv":[126.613348243544976,100.000000000002359,43.2300348418233042]},"#117711":{"lch":[43.289989941732955,63.6505577690815443,127.715012949240403],"luv":[43.289989941732955,-38.9372319378938414,50.351618378457772],"rgb":[0.0666666666666666657,0.466666666666666674,0.0666666666666666657],"xyz":[0.0692886464730239904,0.13352732918491414,0.0274249181211667845],"hpluv":[127.715012949240403,186.57525992916959,43.289989941732955],"hsluv":[127.715012949240403,95.0000616991484321,43.289989941732955]},"#117722":{"lch":[43.400811094951429,60.069144786817489,129.889765673233825],"luv":[43.400811094951429,-38.5230987158151308,46.089836414888758],"rgb":[0.0666666666666666657,0.466666666666666674,0.133333333333333331],"xyz":[0.0711640046115010194,0.134277472440304968,0.037301804317145984],"hpluv":[129.889765673233825,175.627665872500842,43.400811094951429],"hsluv":[129.889765673233825,95.0973605906006725,43.400811094951429]},"#117733":{"lch":[43.5823807888255317,54.6433249694405205,133.881596062605809],"luv":[43.5823807888255317,-37.8771324094584756,39.3854770715327476],"rgb":[0.0666666666666666657,0.466666666666666674,0.2],"xyz":[0.074251755343958753,0.135512572733288067,0.0535639581747571272],"hpluv":[133.881596062605809,159.098283206795713,43.5823807888255317],"hsluv":[133.881596062605809,95.249567515942033,43.5823807888255317]},"#117744":{"lch":[43.8425891980029,47.8554364611196803,140.669149905405504],"luv":[43.8425891980029,-37.0161352468332083,30.3306533110056051],"rgb":[0.0666666666666666657,0.466666666666666674,0.266666666666666663],"xyz":[0.0787097505881776266,0.137295770830975661,0.0770427331276437588],"hpluv":[140.669149905405504,138.507858249946878,43.8425891980029],"hsluv":[140.669149905405504,95.4533619015585373,43.8425891980029]},"#117755":{"lch":[44.18711059062651,40.7988283335817243,151.872601466806259],"luv":[44.18711059062651,-35.9805491223838843,19.2339407882209947],"rgb":[0.0666666666666666657,0.466666666666666674,0.333333333333333315],"xyz":[0.0846721646275396256,0.139680736446720494,0.108444780401617674],"hpluv":[151.872601466806259,117.163251003272293,44.18711059062651],"hsluv":[151.872601466806259,95.7000805167539426,44.18711059062651]},"#117766":{"lch":[44.6197667240920879,35.4343089397309186,169.362936023125116],"luv":[44.6197667240920879,-34.8254110013137463,6.54071850990925796],"rgb":[0.0666666666666666657,0.466666666666666674,0.4],"xyz":[0.0922571604010478,0.142714734756123784,0.148392424808761636],"hpluv":[169.362936023125116,100.771099800457392,44.6197667240920879],"hsluv":[169.362936023125116,95.9777414193434453,44.6197667240920879]},"#117777":{"lch":[45.1427402486772138,34.384087641882445,192.177050630061217],"luv":[45.1427402486772138,-33.6104614986274584,-7.25274850066749],"rgb":[0.0666666666666666657,0.466666666666666674,0.466666666666666674],"xyz":[0.101571173421249716,0.146440339964204608,0.197446226715159545],"hpluv":[192.177050630061217,96.651570122263351,45.1427402486772138],"hsluv":[192.177050630061217,96.2732475219964385,45.1427402486772138]},"#117788":{"lch":[45.7567431438856502,38.9735922704815891,213.78628123605418],"luv":[45.7567431438856502,-32.3916401681270685,-21.6730833451154119],"rgb":[0.0666666666666666657,0.466666666666666674,0.533333333333333326],"xyz":[0.112711590523193544,0.150896506804982206,0.256119090118731685],"hpluv":[213.78628123605418,108.082320192335175,45.7567431438856502],"hsluv":[213.78628123605418,96.5742797371803192,45.7567431438856502]},"#117799":{"lch":[46.4611794427891169,47.8902134896154763,229.321579893426758],"luv":[46.4611794427891169,-31.2154548044213769,-36.3189747850083791],"rgb":[0.0666666666666666657,0.466666666666666674,0.6],"xyz":[0.125768554632266272,0.156119292448611363,0.32488576775984962],"hpluv":[229.321579893426758,130.796423620493698,46.4611794427891169],"hsluv":[229.321579893426758,96.8705519561321,46.4611794427891169]},"#1177aa":{"lch":[47.254315604307827,59.1280670765604199,239.379878554404911],"luv":[47.254315604307827,-30.1165063567495856,-50.8834389666627231],"rgb":[0.0666666666666666657,0.466666666666666674,0.66666666666666663],"xyz":[0.1408262475975188,0.162142369634712458,0.40418961737684822],"hpluv":[239.379878554404911,158.77844008322549,47.254315604307827],"hsluv":[239.379878554404911,97.1543662497403488,47.254315604307827]},"#1177bb":{"lch":[48.1334597651774914,71.3648635001192133,245.920143546346225],"luv":[48.1334597651774914,-29.1175429122131746,-65.1545273725937761],"rgb":[0.0666666666666666657,0.466666666666666674,0.733333333333333282],"xyz":[0.157963839704855447,0.168997406477647216,0.494447602475489967],"hpluv":[245.920143546346225,188.138070935962588,48.1334597651774914],"hsluv":[245.920143546346225,97.4206074841270464,48.1334597651774914]},"#1177cc":{"lch":[49.0951452720171488,83.8954152066927463,250.336037436893122],"luv":[49.0951452720171488,-28.2310616920000612,-79.0028344329863756],"rgb":[0.0666666666666666657,0.466666666666666674,0.8],"xyz":[0.177256215148577201,0.176714356655136018,0.596054113145760178],"hpluv":[250.336037436893122,216.83980339157776,49.0951452720171488],"hsluv":[250.336037436893122,97.666394869173061,49.0951452720171488]},"#1177dd":{"lch":[50.1353116048344702,96.3594544857749,253.441700788778064],"luv":[50.1353116048344702,-27.4615589113116805,-92.3634519220491512],"rgb":[0.0666666666666666657,0.466666666666666674,0.866666666666666696],"xyz":[0.198774540699210744,0.185321686875389552,0.709383961045766176],"hpluv":[253.441700788778064,243.887723915279508,50.1353116048344702],"hsluv":[253.441700788778064,97.8905898818146341,50.1353116048344702]},"#1177ee":{"lch":[51.2494756916451593,108.579054392530807,255.706052301270915],"luv":[51.2494756916451593,-26.8078049752198453,-105.217644172385562],"rgb":[0.0666666666666666657,0.466666666666666674,0.933333333333333348],"xyz":[0.222586720965385454,0.194846558981859597,0.834794777114289399],"hpluv":[255.706052301270915,268.841280267375566,51.2494756916451593],"hsluv":[255.706052301270915,98.0932983984315,51.2494756916451593]},"#1177ff":{"lch":[52.4328877873246739,120.474912105814852,257.407785454377859],"luv":[52.4328877873246739,-26.2648114927385947,-117.577056112809359],"rgb":[0.0666666666666666657,0.466666666666666674,1],"xyz":[0.248757769375216875,0.205314978345792293,0.972628965406071133],"hpluv":[257.407785454377859,291.562836812545811,52.4328877873246739],"hsluv":[257.407785454377859,99.9999999999990905,52.4328877873246739]},"#118800":{"lch":[49.1629818744817157,75.0325981068150725,126.891404302910644],"luv":[49.1629818744817157,-45.0420871994947447,60.0091756264985],"rgb":[0.0666666666666666657,0.533333333333333326,0],"xyz":[0.0903493506983573252,0.177267402435000831,0.0294542697314011],"hpluv":[126.891404302910644,193.664979881129256,49.1629818744817157],"hsluv":[126.891404302910644,100.000000000002487,49.1629818744817157]},"#118811":{"lch":[49.2125288978643,73.2988946552939211,127.715012949240403],"luv":[49.2125288978643,-44.8394509336223663,57.9840633075946883],"rgb":[0.0666666666666666657,0.533333333333333326,0.0666666666666666657],"xyz":[0.0913610161979944435,0.177672068634855684,0.0347823746961567343],"hpluv":[127.715012949240403,188.999680167490567,49.2125288978643],"hsluv":[127.715012949240403,96.2345237189928326,49.2125288978643]},"#118822":{"lch":[49.3041772439320312,70.1927728141533,129.31476933948187],"luv":[49.3041772439320312,-44.4727599639014315,54.3065279366352556],"rgb":[0.0666666666666666657,0.533333333333333326,0.133333333333333331],"xyz":[0.0932363743364714725,0.178422211890246513,0.0446592608921359269],"hpluv":[129.31476933948187,180.6541776618526,49.3041772439320312],"hsluv":[129.31476933948187,96.2899748067816859,49.3041772439320312]},"#118833":{"lch":[49.454516912369769,65.3819990709954766,132.170105234646456],"luv":[49.454516912369769,-43.8931572746598917,48.4581938064309767],"rgb":[0.0666666666666666657,0.533333333333333326,0.2],"xyz":[0.0963241250689292,0.179657312183229612,0.060921414749747077],"hpluv":[132.170105234646456,167.761213036429979,49.454516912369769],"hsluv":[132.170105234646456,96.3778002926358,49.454516912369769]},"#118844":{"lch":[49.6703617695526,59.1036258375550787,136.829676092870073],"luv":[49.6703617695526,-43.1056388924535057,40.4368950689726887],"rgb":[0.0666666666666666657,0.533333333333333326,0.266666666666666663],"xyz":[0.10078212031314808,0.181440510280917205,0.0844001897026337156],"hpluv":[136.829676092870073,150.9927614306973,49.6703617695526],"hsluv":[136.829676092870073,96.4975069627829356,49.6703617695526]},"#118855":{"lch":[49.9568473676091145,51.9652717779864091,144.176427678513875],"luv":[49.9568473676091145,-42.1346422468413806,30.4148219408018328],"rgb":[0.0666666666666666657,0.533333333333333326,0.333333333333333315],"xyz":[0.106744534352510079,0.183825475896662038,0.115802236976607617],"hpluv":[144.176427678513875,131.995007799011802,49.9568473676091145],"hsluv":[144.176427678513875,96.6457662191899658,49.9568473676091145]},"#118866":{"lch":[50.3177367885428879,45.0776565581224133,155.49998607369966],"luv":[50.3177367885428879,-41.0189171061503615,18.6934095394821433],"rgb":[0.0666666666666666657,0.533333333333333326,0.4],"xyz":[0.11432953012601825,0.186859474206065329,0.155749881383751593],"hpluv":[155.49998607369966,113.67882025976219,50.3177367885428879],"hsluv":[155.49998607369966,96.8171590628882512,50.3177367885428879]},"#118877":{"lch":[50.7555873970602391,40.2042284802375036,171.921706717236162],"luv":[50.7555873970602391,-39.8052806191025255,5.64974535051283322],"rgb":[0.0666666666666666657,0.533333333333333326,0.466666666666666674],"xyz":[0.12364354314622017,0.190585079414146152,0.204803683290149502],"hpluv":[171.921706717236162,100.514149141729121,50.7555873970602391],"hsluv":[171.921706717236162,97.0050762990714333,50.7555873970602391]},"#118888":{"lch":[51.2718664023781088,39.4294820301430349,192.17705063006116],"luv":[51.2718664023781088,-38.5423368357954814,-8.3169901046866368],"rgb":[0.0666666666666666657,0.533333333333333326,0.533333333333333326],"xyz":[0.134783960248164,0.19504124625492375,0.263476546693721669],"hpluv":[192.17705063006116,97.5845966821491118,51.2718664023781088],"hsluv":[192.17705063006116,97.2026219422035,51.2718664023781088]},"#118899":{"lch":[51.8670503792929907,43.7110075029310678,211.486174513119295],"luv":[51.8670503792929907,-37.2752705782940623,-22.8299448145688864],"rgb":[0.0666666666666666657,0.533333333333333326,0.6],"xyz":[0.147840924357236725,0.200264031898552908,0.332243224334839549],"hpluv":[211.486174513119295,106.93960912970239,51.8670503792929907],"hsluv":[211.486174513119295,97.4033619208845,51.8670503792929907]},"#1188aa":{"lch":[52.5407237145479371,52.059033028958666,226.184651315961702],"luv":[52.5407237145479371,-36.0423686073764102,-37.5644856890150507],"rgb":[0.0666666666666666657,0.533333333333333326,0.66666666666666663],"xyz":[0.162898617322489253,0.206287109084654,0.411547073951838149],"hpluv":[226.184651315961702,125.730132415512543,52.5407237145479371],"hsluv":[226.184651315961702,97.6018250350267,52.5407237145479371]},"#1188bb":{"lch":[53.2916815113449047,62.8295543710454396,236.286042109927791],"luv":[53.2916815113449047,-34.8733609598062557,-52.2628127623381573],"rgb":[0.0666666666666666657,0.533333333333333326,0.733333333333333282],"xyz":[0.1800362094298259,0.213142145927588761,0.501805059050479896],"hpluv":[236.286042109927791,149.604233097343041,53.2916815113449047],"hsluv":[236.286042109927791,97.7937430875757201,53.2916815113449047]},"#1188cc":{"lch":[54.1180375597057548,74.8024459991104607,243.146336149822275],"luv":[54.1180375597057548,-33.7892633498457826,-66.735984369188742],"rgb":[0.0666666666666666657,0.533333333333333326,0.8],"xyz":[0.199328584873547654,0.220859096105077563,0.60341156972075],"hpluv":[243.146336149822275,175.393335022858878,54.1180375597057548],"hsluv":[243.146336149822275,97.9760759990174819,54.1180375597057548]},"#1188dd":{"lch":[55.0173353812408266,87.2581949437380331,247.918019831454984],"luv":[55.0173353812408266,-32.8032215872693484,-80.8575366823396138],"rgb":[0.0666666666666666657,0.533333333333333326,0.866666666666666696],"xyz":[0.220846910424181198,0.229466426325331097,0.716741417620756],"hpluv":[247.918019831454984,201.254684785248685,55.0173353812408266],"hsluv":[247.918019831454984,98.1468934162311513,55.0173353812408266]},"#1188ee":{"lch":[55.9866591638471363,99.7959623087877645,251.344854654704591],"luv":[55.9866591638471363,-31.921869990719685,-94.5527805483930734],"rgb":[0.0666666666666666657,0.533333333333333326,0.933333333333333348],"xyz":[0.244659090690355907,0.238991298431801141,0.842152233689279273],"hpluv":[251.344854654704591,226.187053837960264,55.9866591638471363],"hsluv":[251.344854654704591,98.3051827042817,55.9866591638471363]},"#1188ff":{"lch":[57.0227411270994082,112.196885547184024,253.882464988485651],"luv":[57.0227411270994082,-31.1468293758358783,-107.786901552649141],"rgb":[0.0666666666666666657,0.533333333333333326,1],"xyz":[0.270830139100187328,0.249459717795733837,0.979986421981061118],"hpluv":[253.882464988485651,249.673263359937977,57.0227411270994082],"hsluv":[253.882464988485651,99.9999999999988916,57.0227411270994082]},"#119900":{"lch":[54.9698669410824721,84.160615619067471,127.079428544988929],"luv":[54.9698669410824721,-50.7422517332937772,67.1433772639973],"rgb":[0.0666666666666666657,0.6,0],"xyz":[0.116218951150824812,0.229006603339936526,0.0380774698822233526],"hpluv":[127.079428544988929,194.277964405092661,54.9698669410824721],"hsluv":[127.079428544988929,100.000000000002373,54.9698669410824721]},"#119911":{"lch":[55.0116447857556494,82.6558224500047,127.715012949240403],"luv":[55.0116447857556494,-50.563404981135136,65.3859852078442],"rgb":[0.0666666666666666657,0.6,0.0666666666666666657],"xyz":[0.11723061665046193,0.229411269539791379,0.0434055748469789823],"hpluv":[127.715012949240403,190.659367551848248,55.0116447857556494],"hsluv":[127.715012949240403,97.0796004133795094,55.0116447857556494]},"#119922":{"lch":[55.0889600096002852,79.9388272851777657,128.936266554168185],"luv":[55.0889600096002852,-50.237998627918941,62.1800579091938204],"rgb":[0.0666666666666666657,0.6,0.133333333333333331],"xyz":[0.119105974788938959,0.230161412795182208,0.0532824610429581819],"hpluv":[128.936266554168185,184.133380166819137,55.0889600096002852],"hsluv":[128.936266554168185,97.113065541928691,55.0889600096002852]},"#119933":{"lch":[55.215893256771821,75.6695179958245916,131.075562701176153],"luv":[55.215893256771821,-49.7189430335536,57.0429895547794175],"rgb":[0.0666666666666666657,0.6,0.2],"xyz":[0.122193725521396693,0.231396513088165307,0.0695446149005693182],"hpluv":[131.075562701176153,173.898642472918851,55.215893256771821],"hsluv":[131.075562701176153,97.1665253707361,55.215893256771821]},"#119944":{"lch":[55.398361036949,69.9528706873860671,134.469703140623466],"luv":[55.398361036949,-49.0042253731102591,49.9198358669935303],"rgb":[0.0666666666666666657,0.6,0.266666666666666663],"xyz":[0.126651720765615566,0.2331797111858529,0.0930233898534559567],"hpluv":[134.469703140623466,160.231519707698453,55.398361036949],"hsluv":[134.469703140623466,97.2403070889690184,55.398361036949]},"#119955":{"lch":[55.6409569880907497,63.1400511875114887,139.633471194580181],"luv":[55.6409569880907497,-48.1074656204671172,40.8942271664002206],"rgb":[0.0666666666666666657,0.6,0.333333333333333315],"xyz":[0.132614134804977579,0.235564676801597733,0.124425437127429872],"hpluv":[139.633471194580181,143.995747016857166,55.6409569880907497],"hsluv":[139.633471194580181,97.3331834328701575,55.6409569880907497]},"#119966":{"lch":[55.9472168173363,55.8918514512489466,147.340496807064056],"luv":[55.9472168173363,-47.0549257965888,30.1617807320125095],"rgb":[0.0666666666666666657,0.6,0.4],"xyz":[0.140199130578485737,0.238598675111001024,0.164373081534573834],"hpluv":[147.340496807064056,126.76791164907155,55.9472168173363],"hsluv":[147.340496807064056,97.4426708460479,55.9472168173363]},"#119977":{"lch":[56.319758368673476,49.2851425936320169,158.582299284916047],"luv":[56.319758368673476,-45.8816608646539308,17.9971796894826],"rgb":[0.0666666666666666657,0.6,0.466666666666666674],"xyz":[0.14951314359868767,0.242324280319081847,0.213426883440971743],"hpluv":[158.582299284916047,111.043862853999471,56.319758368673476],"hsluv":[158.582299284916047,97.5654087237770398,56.319758368673476]},"#119988":{"lch":[56.7603710001512951,44.8759084930006864,173.96527373508107],"luv":[56.7603710001512951,-44.6272223318454451,4.71785862613608131],"rgb":[0.0666666666666666657,0.6,0.533333333333333326],"xyz":[0.16065356070063147,0.246780447159859445,0.27209974684454391],"hpluv":[173.96527373508107,100.324581054675164,56.7603710001512951],"hsluv":[173.96527373508107,97.6975811674153647,56.7603710001512951]},"#119999":{"lch":[57.2700846473106822,44.3289506401906692,192.177050630061103],"luv":[57.2700846473106822,-43.3315696575835219,-9.35045110518458422],"rgb":[0.0666666666666666657,0.6,0.6],"xyz":[0.173710524809704198,0.252003232803488575,0.340866424485661845],"hpluv":[192.177050630061103,98.2197789195824384,57.2700846473106822],"hsluv":[192.177050630061103,97.8353178900151903,57.2700846473106822]},"#1199aa":{"lch":[57.849232550626823,48.3524707931101716,209.625127722111756],"luv":[57.849232550626823,-42.0317498411296384,-23.9017454821038378],"rgb":[0.0666666666666666657,0.6,0.66666666666666663],"xyz":[0.188768217774956726,0.258026309989589697,0.42017027410266039],"hpluv":[209.625127722111756,106.062142541807418,57.849232550626823],"hsluv":[209.625127722111756,97.9750198241805492,57.849232550626823]},"#1199bb":{"lch":[58.4975141278551263,56.1838887208170235,223.492153209811363],"luv":[58.4975141278551263,-40.7596491137071553,-38.6688551152037832],"rgb":[0.0666666666666666657,0.6,0.733333333333333282],"xyz":[0.2059058098822934,0.264881346832524456,0.510428259201302192],"hpluv":[223.492153209811363,121.8747424026255,58.4975141278551263],"hsluv":[223.492153209811363,98.1135798876595686,58.4975141278551263]},"#1199cc":{"lch":[59.2140605434028515,66.4726101728698211,233.498474693243395],"luv":[59.2140605434028515,-39.5408457239066138,-53.4334111079625274],"rgb":[0.0666666666666666657,0.6,0.8],"xyz":[0.225198185326015127,0.272598297010013257,0.612034769871572348],"hpluv":[233.498474693243395,142.448281943894045,59.2140605434028515],"hsluv":[233.498474693243395,98.2484961488154482,59.2140605434028515]},"#1199dd":{"lch":[59.9975033067865553,78.1149919905427907,240.560044488236258],"luv":[59.9975033067865553,-38.3943918665613637,-68.0281018909060151],"rgb":[0.0666666666666666657,0.6,0.866666666666666696],"xyz":[0.24671651087664867,0.281205627230266819,0.725364617771578346],"hpluv":[240.560044488236258,165.211601361935521,59.9975033067865553],"hsluv":[240.560044488236258,98.3778942246892285,59.9975033067865553]},"#1199ee":{"lch":[60.8460449735814706,90.4022578402124,245.60857134482],"luv":[60.8460449735814706,-37.3332566635070862,-82.3334450239084106],"rgb":[0.0666666666666666657,0.6,0.933333333333333348],"xyz":[0.270528691142823408,0.290730499336736836,0.850775433840101569],"hpluv":[245.60857134482,188.532510112076068,60.8460449735814706],"hsluv":[245.60857134482,98.5004851129029788,60.8460449735814706]},"#1199ff":{"lch":[61.7575303771721877,102.910627792409826,249.306643617498082],"luv":[61.7575303771721877,-36.3651553748586,-96.2713497733895167],"rgb":[0.0666666666666666657,0.6,1],"xyz":[0.296699739552654829,0.301198918700669505,0.988609622131883414],"hpluv":[249.306643617498082,211.450946466820028,61.7575303771721877],"hsluv":[249.306643617498082,99.9999999999986215,61.7575303771721877]},"#000000":{"lch":[0,0,0],"luv":[0,0,0],"rgb":[0,0,0],"xyz":[0,0,0],"hpluv":[0,0,0],"hsluv":[0,0,0]},"#000011":{"lch":[0.365533479526218952,1.47895322486610792,265.8743202181779],"luv":[0.365533479526218952,-0.106402530834795422,-1.47512072142377915],"rgb":[0,0,0.0666666666666666657],"xyz":[0.00101166549963712174,0.000404666199854854377,0.00532810496475563146],"hpluv":[265.8743202181779,513.41269684428039,0.365533479526218952],"hsluv":[265.8743202181779,100.000000000000867,0.365533479526218952]},"#000022":{"lch":[1.04313510374015572,4.22053823263236,265.8743202181779],"luv":[1.04313510374015572,-0.303644457367982512,-4.20960128950726],"rgb":[0,0,0.133333333333333331],"xyz":[0.0028870236381141408,0.00115480945524567245,0.0152049911607348275],"hpluv":[265.8743202181779,513.41269684428039,1.04313510374015572],"hsluv":[265.8743202181779,100.000000000000838,1.04313510374015572]},"#000033":{"lch":[2.15879662382733661,8.73451929157831,265.8743202181779],"luv":[2.15879662382733661,-0.62840050829424543,-8.71188498868810868],"rgb":[0,0,0.2],"xyz":[0.00597477437057188088,0.00238990974822878574,0.0314671450183459725],"hpluv":[265.8743202181779,513.412696844280276,2.15879662382733661],"hsluv":[265.8743202181779,100.000000000000838,2.15879662382733661]},"#000044":{"lch":[3.76955286085941,15.251660031516769,265.874320218177957],"luv":[3.76955286085941,-1.0972728545435857,-15.2121374566379668],"rgb":[0,0,0.266666666666666663],"xyz":[0.0104327696147907597,0.00417310784591636182,0.054945919971232611],"hpluv":[265.874320218177957,513.41269684428039,3.76955286085941],"hsluv":[265.874320218177957,100.000000000000981,3.76955286085941]},"#000055":{"lch":[5.92388346812606947,23.9681097618519345,265.8743202181779],"luv":[5.92388346812606947,-1.7243733575266309,-23.905999708860417],"rgb":[0,0,0.333333333333333315],"xyz":[0.0163951836541527535,0.00655807346166119385,0.0863479672452065194],"hpluv":[265.8743202181779,513.41269684428039,5.92388346812606947],"hsluv":[265.8743202181779,100.000000000000838,5.92388346812606947]},"#000066":{"lch":[8.64689012997685,34.9854302247980513,265.8743202181779],"luv":[8.64689012997685,-2.51700882467034193,-34.8947703043127149],"rgb":[0,0,0.4],"xyz":[0.0239801794276609283,0.00959207177106450627,0.126295611652350481],"hpluv":[265.8743202181779,513.412696844280276,8.64689012997685],"hsluv":[265.8743202181779,100.000000000000838,8.64689012997685]},"#000077":{"lch":[11.4958709948623863,46.5124439559768703,265.874320218177957],"luv":[11.4958709948623863,-3.34631391244679577,-46.3919133681426672],"rgb":[0,0,0.466666666666666674],"xyz":[0.0332941924478628443,0.0133176769791453226,0.17534941355874839],"hpluv":[265.874320218177957,513.412696844280276,11.4958709948623863],"hsluv":[265.874320218177957,100.000000000001,11.4958709948623863]},"#000088":{"lch":[14.2727431262745554,57.7477048111956535,265.874320218177957],"luv":[14.2727431262745554,-4.15462898927595781,-57.598059593379169],"rgb":[0,0,0.533333333333333326],"xyz":[0.0444346095498066723,0.0177738438199229153,0.234022276962320558],"hpluv":[265.874320218177957,513.41269684428039,14.2727431262745554],"hsluv":[265.874320218177957,100.000000000000952,14.2727431262745554]},"#000099":{"lch":[16.9872454361813823,68.7306165552763701,265.874320218177957],"luv":[16.9872454361813823,-4.94478893879780923,-68.5525106354185567],"rgb":[0,0,0.6],"xyz":[0.0574915736588793858,0.0229966294635520763,0.302788954603438465],"hpluv":[265.874320218177957,513.412696844280163,16.9872454361813823],"hsluv":[265.874320218177957,100.000000000000952,16.9872454361813823]},"#0000aa":{"lch":[19.6469460262523299,79.4917998262647529,265.8743202181779],"luv":[19.6469460262523299,-5.71899674710351302,-79.2858077831434116],"rgb":[0,0,0.66666666666666663],"xyz":[0.0725492666241319278,0.0290197066496531778,0.382092804220437066],"hpluv":[265.8743202181779,513.41269684428039,19.6469460262523299],"hsluv":[265.8743202181779,100.000000000000824,19.6469460262523299]},"#0000bb":{"lch":[22.2578820656552736,90.0556810893410926,265.8743202181779],"luv":[22.2578820656552736,-6.47900976369593895,-89.8223142039161644],"rgb":[0,0,0.733333333333333282],"xyz":[0.0896868587314685745,0.0358747434925879363,0.472350789319078812],"hpluv":[265.8743202181779,513.41269684428039,22.2578820656552736],"hsluv":[265.8743202181779,100.000000000000796,22.2578820656552736]},"#0000cc":{"lch":[24.8249727536546274,100.442163488877583,265.874320218177957],"luv":[24.8249727536546274,-7.22625991008361535,-100.18188146585355],"rgb":[0,0,0.8],"xyz":[0.108979234175190315,0.043591693670076738,0.573957299989349],"hpluv":[265.874320218177957,513.41269684428039,24.8249727536546274],"hsluv":[265.874320218177957,100.000000000001,24.8249727536546274]},"#0000dd":{"lch":[27.3522973211786535,110.667751646404724,265.8743202181779],"luv":[27.3522973211786535,-7.96193460279319343,-110.380971421034161],"rgb":[0,0,0.866666666666666696],"xyz":[0.130497559725823858,0.052199023890330272,0.687287147889355],"hpluv":[265.8743202181779,513.412696844280276,27.3522973211786535],"hsluv":[265.8743202181779,100.000000000000824,27.3522973211786535]},"#0000ee":{"lch":[29.8432887766479737,120.746335558760222,265.8743202181779],"luv":[29.8432887766479737,-8.68703315051946,-120.433438072283309],"rgb":[0,0,0.933333333333333348],"xyz":[0.154309739991998596,0.0617238959968003,0.812697963957878189],"hpluv":[265.8743202181779,513.41269684428039,29.8432887766479737],"hsluv":[265.8743202181779,100.000000000000838,29.8432887766479737]},"#0000ff":{"lch":[32.3008729039800215,130.68975298582734,265.8743202181779],"luv":[32.3008729039800215,-9.40240721482262,-130.351088503561101],"rgb":[0,0,1],"xyz":[0.18048078840183,0.072192315360733,0.95053215224966],"hpluv":[265.8743202181779,513.41269684428039,32.3008729039800215],"hsluv":[265.8743202181779,100.000000000000824,32.3008729039800215]},"#001100":{"lch":[3.62113466359794112,5.60448249758782424,127.715012949240474],"luv":[3.62113466359794112,-3.42845440085753106,4.43350025228474376],"rgb":[0,0.0666666666666666657,0],"xyz":[0.00200440026092840902,0.00400880052185687355,0.00066813342030945088],"hpluv":[127.715012949240474,196.394882900214469,3.62113466359794112],"hsluv":[127.715012949240474,100.000000000002217,3.62113466359794112]},"#001111":{"lch":[3.9866681431241604,3.15408977882195618,192.17705063006116],"luv":[3.9866681431241604,-3.08312421078118115,-0.665302512969894178],"rgb":[0,0.0666666666666666657,0.0666666666666666657],"xyz":[0.00301606576056553076,0.00441346672171172814,0.00599623838506508234],"hpluv":[192.17705063006116,100.392967527320764,3.9866681431241604],"hsluv":[192.17705063006116,99.9999999999914,3.9866681431241604]},"#001122":{"lch":[4.66426976733809706,7.30142401028103372,246.87889630792742],"luv":[4.66426976733809706,-2.86709314837997242,-6.71495118794031054],"rgb":[0,0.0666666666666666657,0.133333333333333331],"xyz":[0.00489142389904254939,0.005163609977102546,0.0158731245810442775],"hpluv":[246.87889630792742,198.638412351210178,4.66426976733809706],"hsluv":[246.87889630792742,99.9999999999921414,4.66426976733809706]},"#001133":{"lch":[5.77993128742527773,13.8979406242137369,257.974087263939282],"luv":[5.77993128742527773,-2.89569220292521434,-13.5929290537429299],"rgb":[0,0.0666666666666666657,0.2],"xyz":[0.00797917463150029,0.00639871027008565886,0.0321352784386554208],"hpluv":[257.974087263939282,305.117489912579458,5.77993128742527773],"hsluv":[257.974087263939282,99.9999999999925,5.77993128742527773]},"#001144":{"lch":[7.39068752445735111,21.802452480470059,261.611708702028636],"luv":[7.39068752445735111,-3.1805605696034065,-21.569213444774455],"rgb":[0,0.0666666666666666657,0.266666666666666663],"xyz":[0.0124371698757191687,0.00818190836777323537,0.0556140533915420593],"hpluv":[261.611708702028636,374.334482048802613,7.39068752445735111],"hsluv":[261.611708702028636,99.9999999999929656,7.39068752445735111]},"#001155":{"lch":[9.4550232844459714,31.0886305445366773,263.238579866128873],"luv":[9.4550232844459714,-3.6602302532303379,-30.8724094237562916],"rgb":[0,0.0666666666666666657,0.333333333333333315],"xyz":[0.0183995839150811608,0.0105668739835180665,0.0870161006655159747],"hpluv":[263.238579866128873,417.232678203522596,9.4550232844459714],"hsluv":[263.238579866128873,99.9999999999929514,9.4550232844459714]},"#001166":{"lch":[11.6894020192987682,40.9340765206813302,264.100423242359113],"luv":[11.6894020192987682,-4.20741678933990659,-40.7172723123955791],"rgb":[0,0.0666666666666666657,0.4],"xyz":[0.025984579688589339,0.0136008722929213798,0.126963745072659923],"hpluv":[264.100423242359113,444.357002567308371,11.6894020192987682],"hsluv":[264.100423242359113,99.9999999999928235,11.6894020192987682]},"#001177":{"lch":[14.0165943101603965,51.0460922578313898,264.608714664977526],"luv":[14.0165943101603965,-4.79613195559092276,-50.8202779710972621],"rgb":[0,0.0666666666666666657,0.466666666666666674],"xyz":[0.0352985927087912515,0.0173264775010021979,0.176017546979057832],"hpluv":[264.608714664977526,462.124851551559573,14.0165943101603965],"hsluv":[264.608714664977526,99.9999999999931504,14.0165943101603965]},"#001188":{"lch":[16.3962585295353378,61.2721603523949625,264.931782730652174],"luv":[16.3962585295353378,-5.41289085195630371,-61.0325998698597871],"rgb":[0,0.0666666666666666657,0.533333333333333326],"xyz":[0.0464390098107350796,0.0217826443417797888,0.23469041038263],"hpluv":[264.931782730652174,474.195864485329537,16.3962585295353378],"hsluv":[264.931782730652174,99.9999999999933209,16.3962585295353378]},"#001199":{"lch":[18.8023327262484941,71.5200065602600148,265.148843888859801],"luv":[18.8023327262484941,-6.04826966448705239,-71.2638040834565771],"rgb":[0,0.0666666666666666657,0.6],"xyz":[0.059495973919807793,0.0270054299854089498,0.303457088023747934],"hpluv":[265.148843888859801,482.675370310212884,18.8023327262484941],"hsluv":[265.148843888859801,99.9999999999930651,18.8023327262484941]},"#0011aa":{"lch":[21.2181090603332123,81.7349311996174919,265.301088447161305],"luv":[21.2181090603332123,-6.69569086805443892,-81.4602154551880488],"rgb":[0,0.0666666666666666657,0.66666666666666663],"xyz":[0.0745536668850603351,0.0330285071715100548,0.382760937640746535],"hpluv":[265.301088447161305,488.81030222212587,21.2181090603332123],"hsluv":[265.301088447161305,99.9999999999931788,21.2181090603332123]},"#0011bb":{"lch":[23.6329047323064216,91.8852368853417,265.411605614461337],"luv":[23.6329047323064216,-7.35054503717070418,-91.590754146539723],"rgb":[0,0.0666666666666666657,0.733333333333333282],"xyz":[0.0916912589923969817,0.0398835440144448133,0.473018922739388281],"hpluv":[265.411605614461337,493.364573724961247,23.6329047323064216],"hsluv":[265.411605614461337,99.999999999993,23.6329047323064216]},"#0011cc":{"lch":[26.0399131129061345,101.953231398784169,265.494123438592396],"luv":[26.0399131129061345,-8.00958293333115,-101.638122640513785],"rgb":[0,0.0666666666666666657,0.8],"xyz":[0.110983634436118722,0.047600494191933615,0.574625433409658437],"hpluv":[265.494123438592396,496.821968194535657,26.0399131129061345],"hsluv":[265.494123438592396,99.9999999999927383,26.0399131129061345]},"#0011dd":{"lch":[28.43483595206839,111.929749681002491,265.557201901085818],"luv":[28.43483595206839,-8.67050070949402496,-111.593419524175076],"rgb":[0,0.0666666666666666657,0.866666666666666696],"xyz":[0.132501959986752266,0.056207824412187149,0.687955281309664435],"hpluv":[265.557201901085818,499.498435149301031,28.43483595206839],"hsluv":[265.557201901085818,99.9999999999932214,28.43483595206839]},"#0011ee":{"lch":[30.8150119654139019,121.810820553676152,265.60639254385444],"luv":[30.8150119654139019,-9.33165721729429798,-121.452855781734542],"rgb":[0,0.0666666666666666657,0.933333333333333348],"xyz":[0.156314140252927,0.0657326965186571799,0.813366097378187658],"hpluv":[265.60639254385444,501.606152289563909,30.8150119654139019],"hsluv":[265.60639254385444,99.9999999999933635,30.8150119654139019]},"#0011ff":{"lch":[33.1788572452669683,131.59562707663585,265.645416939351662],"luv":[33.1788572452669683,-9.99188030865750321,-131.215743695604147],"rgb":[0,0.0666666666666666657,1],"xyz":[0.182485188662758396,0.076201115882589876,0.951200285669969503],"hpluv":[265.645416939351662,503.291227463659,33.1788572452669683],"hsluv":[265.645416939351662,99.9999999999995,33.1788572452669683]},"#55aa00":{"lch":[62.2364297391950743,84.7105424007581291,119.071642820441127],"luv":[62.2364297391950743,-41.1610955154551661,74.0380997176333437],"rgb":[0.333333333333333315,0.66666666666666663,0],"xyz":[0.181203244729902568,0.306798408854291604,0.0496696976001293408],"hpluv":[119.071642820441127,172.715819722381468,62.2364297391950743],"hsluv":[119.071642820441127,100.00000000000216,62.2364297391950743]},"#55aa11":{"lch":[62.270812500354296,83.3839078255156352,119.512873370738717],"luv":[62.270812500354296,-41.0765057562956173,72.5644317769187808],"rgb":[0.333333333333333315,0.66666666666666663,0.0666666666666666657],"xyz":[0.182214910229539701,0.307203075054146457,0.0549978025648849705],"hpluv":[119.512873370738717,169.917081036602212,62.270812500354296],"hsluv":[119.512873370738717,97.826098763204655,62.270812500354296]},"#55aa22":{"lch":[62.3344691942433826,80.9675552156414,120.358690899927907],"luv":[62.3344691942433826,-40.9219560187625078,69.8651451955580285],"rgb":[0.333333333333333315,0.66666666666666663,0.133333333333333331],"xyz":[0.184090268368016702,0.307953218309537258,0.0648746887608641631],"hpluv":[120.358690899927907,164.824621464176317,62.3344691942433826],"hsluv":[120.358690899927907,93.8493385636837729,62.3344691942433826]},"#55aa33":{"lch":[62.4390542004851312,77.1100321042589343,121.835090998601942],"luv":[62.4390542004851312,-40.6737079775314143,65.5103543760705094],"rgb":[0.333333333333333315,0.66666666666666663,0.2],"xyz":[0.187178019100474435,0.309188318602520384,0.0811368426184753133],"hpluv":[121.835090998601942,156.708983850290963,62.4390542004851312],"hsluv":[121.835090998601942,87.4478443822902,62.4390542004851312]},"#55aa44":{"lch":[62.5895604608427192,71.8060360850404322,124.168545167949461],"luv":[62.5895604608427192,-40.3283690350335959,59.4115264003566281],"rgb":[0.333333333333333315,0.66666666666666663,0.266666666666666663],"xyz":[0.191636014344693323,0.310971516700207951,0.104615617571361952],"hpluv":[124.168545167949461,145.578881327618376,62.5895604608427192],"hsluv":[124.168545167949461,78.5131262768233569,62.5895604608427192]},"#55aa55":{"lch":[62.7899606618147317,65.2067785819955361,127.715012949239551],"luv":[62.7899606618147317,-39.8892256495404212,51.582687503865472],"rgb":[0.333333333333333315,0.66666666666666663,0.333333333333333315],"xyz":[0.197598428384055336,0.313356482315952811,0.136017664845335867],"hpluv":[127.715012949239551,131.777681042751937,62.7899606618147317],"hsluv":[127.715012949239551,67.0983271543339583,62.7899606618147317]},"#55aa66":{"lch":[63.0434325957243402,57.6560159218084,133.059858461454269],"luv":[63.0434325957243402,-39.3653397911167744,42.1258376190401904],"rgb":[0.333333333333333315,0.66666666666666663,0.4],"xyz":[0.205183424157563493,0.31639048062535613,0.175965309252479829],"hpluv":[133.059858461454269,116.049721847977636,63.0434325957243402],"hsluv":[133.059858461454269,68.1368400599560289,63.0434325957243402]},"#55aa77":{"lch":[63.3524771250310863,49.7736139598232441,141.163090238883626],"luv":[63.3524771250310863,-38.7703675351212311,31.2133184364163512],"rgb":[0.333333333333333315,0.66666666666666663,0.466666666666666674],"xyz":[0.214497437177765427,0.320116085833436925,0.225019111158877738],"hpluv":[141.163090238883626,99.6953496544650903,63.3524771250310863],"hsluv":[141.163090238883626,69.3257510819996696,63.3524771250310863]},"#55aa88":{"lch":[63.7189896387035901,42.62351374085884,153.427389325734708],"luv":[63.7189896387035901,-38.1211143540371324,19.0668446268281784],"rgb":[0.333333333333333315,0.66666666666666663,0.533333333333333326],"xyz":[0.225637854279709227,0.324572252674214523,0.283691974562449878],"hpluv":[153.427389325734708,84.882799477410984,63.7189896387035901],"hsluv":[153.427389325734708,70.6362499177174783,63.7189896387035901]},"#55aa99":{"lch":[64.1443101574831473,37.9035590936142697,170.991128647613664],"luv":[64.1443101574831473,-37.4359848569684885,5.93521943587048106],"rgb":[0.333333333333333315,0.66666666666666663,0.6],"xyz":[0.238694818388781954,0.329795038317843681,0.352458652203567813],"hpluv":[170.991128647613664,74.9827180661079211,64.1443101574831473],"hsluv":[170.991128647613664,72.0364673991159918,64.1443101574831473]},"#55aaaa":{"lch":[64.6292640862610881,37.5790288142004414,192.177050630061],"luv":[64.6292640862610881,-36.7335179653569526,-7.92666793219602717],"rgb":[0.333333333333333315,0.66666666666666663,0.66666666666666663],"xyz":[0.253752511354034482,0.335818115503944803,0.431762501820566413],"hpluv":[192.177050630061,73.7828909738526,64.6292640862610881],"hsluv":[192.177050630061,73.4940830927894808,64.6292640862610881]},"#55aabb":{"lch":[65.1741997434662466,42.3607987370151307,211.725401595501864],"luv":[65.1741997434662466,-36.0311661131296148,-22.2753751520813346],"rgb":[0.333333333333333315,0.66666666666666663,0.733333333333333282],"xyz":[0.270890103461371157,0.342673152346879561,0.522020486919208104],"hpluv":[211.725401595501864,82.4760322189960249,65.1741997434662466],"hsluv":[211.725401595501864,74.9784994025983,65.1741997434662466]},"#55aacc":{"lch":[65.7790257871148327,51.0903516363502916,226.227071697937646],"luv":[65.7790257871148327,-35.3444111183519212,-36.8916878581455236],"rgb":[0.333333333333333315,0.66666666666666663,0.8],"xyz":[0.290182478905092855,0.350390102524368363,0.62362699758947826],"hpluv":[226.227071697937646,98.5577501957325808,65.7790257871148327],"hsluv":[226.227071697937646,76.4624197891718,65.7790257871148327]},"#55aadd":{"lch":[66.4432499478878071,62.1650528283955595,236.084470311275709],"luv":[66.4432499478878071,-34.6862381636523693,-51.5883579425784404],"rgb":[0.333333333333333315,0.66666666666666663,0.866666666666666696],"xyz":[0.311700804455726455,0.358997432744621869,0.736956845489484258],"hpluv":[236.084470311275709,118.722973851074883,66.4432499478878071],"hsluv":[236.084470311275709,77.9227939155192928,66.4432499478878071]},"#55aaee":{"lch":[67.1660194476775274,74.4631251020179832,242.774049894046698],"luv":[67.1660194476775274,-34.0669326024190866,-66.2133000462974763],"rgb":[0.333333333333333315,0.66666666666666663,0.933333333333333348],"xyz":[0.335512984721901164,0.368522304851091886,0.862367661558007481],"hpluv":[242.774049894046698,140.679551339708695,67.1660194476775274],"hsluv":[242.774049894046698,80.7550915423080085,67.1660194476775274]},"#55aaff":{"lch":[67.9461628502375135,87.3278464079016,247.446578213330071],"luv":[67.9461628502375135,-33.4941307575646476,-80.6492155140842897],"rgb":[0.333333333333333315,0.66666666666666663,1],"xyz":[0.361684033131732585,0.37899072421502461,1.00020184984978933],"hpluv":[247.446578213330071,163.089927997179217,67.9461628502375135],"hsluv":[247.446578213330071,99.9999999999981,67.9461628502375135]},"#002200":{"lch":[10.1376941245203973,15.6902558355344119,127.715012949240474],"luv":[10.1376941245203973,-9.59826829561359141,12.4119850914324186],"rgb":[0,0.133333333333333331,0],"xyz":[0.00572002399569634425,0.0114400479913928481,0.00190667466523206119],"hpluv":[127.715012949240474,196.394882900214583,10.1376941245203973],"hsluv":[127.715012949240474,100.000000000002331,10.1376941245203973]},"#002211":{"lch":[10.4423176349325608,11.2803579121031614,143.951720967420982],"luv":[10.4423176349325608,-9.12041102953238614,6.63811549142769763],"rgb":[0,0.133333333333333331,0.0666666666666666657],"xyz":[0.00673168949533346599,0.0118447141912477027,0.00723477962998769243],"hpluv":[143.951720967420982,137.077225818420459,10.4423176349325608],"hsluv":[143.951720967420982,99.9999999999911,10.4423176349325608]},"#002222":{"lch":[10.9891417742670896,8.69416226881610399,192.17705063006116],"luv":[10.9891417742670896,-8.49854762011842,-1.83388819318003415],"rgb":[0,0.133333333333333331,0.133333333333333331],"xyz":[0.00860704763381048461,0.0125948574466385205,0.0171116658259668902],"hpluv":[192.17705063006116,100.392967527320849,10.9891417742670896],"hsluv":[192.17705063006116,99.9999999999915,10.9891417742670896]},"#002233":{"lch":[11.8439988341371283,14.4341695325786503,236.81663495428262],"luv":[11.8439988341371283,-7.90011340243738758,-12.0802921456333543],"rgb":[0,0.133333333333333331,0.2],"xyz":[0.0116947983662682251,0.0138299577396216334,0.0333738196835780335],"hpluv":[236.81663495428262,154.643892414528665,11.8439988341371283],"hsluv":[236.81663495428262,99.9999999999918572,11.8439988341371283]},"#002244":{"lch":[12.9926705590666103,23.9154033254141893,251.756603241059679],"luv":[12.9926705590666103,-7.48682111947174356,-22.713300635140282],"rgb":[0,0.133333333333333331,0.266666666666666663],"xyz":[0.0161527936104871039,0.0156131558373092099,0.056852594636464672],"hpluv":[251.756603241059679,233.570832873869165,12.9926705590666103],"hsluv":[251.756603241059679,99.9999999999922551,12.9926705590666103]},"#002255":{"lch":[14.3995425627967926,34.0053227001087492,257.612107564284656],"luv":[14.3995425627967926,-7.29512566501762105,-33.2135983216232162],"rgb":[0,0.133333333333333331,0.333333333333333315],"xyz":[0.022115207649849096,0.0179981214530540411,0.0882546419104385804],"hpluv":[257.612107564284656,299.666041626864057,14.3995425627967926],"hsluv":[257.612107564284656,99.9999999999922551,14.3995425627967926]},"#002266":{"lch":[16.0198287291043684,44.1221041927951489,260.479541157990241],"luv":[16.0198287291043684,-7.29778599254304705,-43.514392998258792],"rgb":[0,0.133333333333333331,0.4],"xyz":[0.0297002034233572743,0.0210321197624573561,0.128202286317582542],"hpluv":[260.479541157990241,349.492349810916096,16.0198287291043684],"hsluv":[260.479541157990241,99.9999999999926672,16.0198287291043684]},"#002277":{"lch":[17.8086814865908138,54.1839210795750787,262.094384047744654],"luv":[17.8086814865908138,-7.45254493353373,-53.6689563674502708],"rgb":[0,0.133333333333333331,0.466666666666666674],"xyz":[0.0390142164435591868,0.0247577249705381724,0.177256088223980451],"hpluv":[262.094384047744654,386.080609388904179,17.8086814865908138],"hsluv":[262.094384047744654,99.9999999999926246,17.8086814865908138]},"#002288":{"lch":[19.7262797638069571,64.1945165648047862,263.091662768615947],"luv":[19.7262797638069571,-7.7213996360385595,-63.7284547486409778],"rgb":[0,0.133333333333333331,0.533333333333333326],"xyz":[0.0501546335455030148,0.0292138918113157633,0.235928951627552619],"hpluv":[263.091662768615947,412.944865974292611,19.7262797638069571],"hsluv":[263.091662768615947,99.9999999999928093,19.7262797638069571]},"#002299":{"lch":[21.7396965211461932,74.1610579713059082,263.749129578079874],"luv":[21.7396965211461932,-8.0748025544340809,-73.7201470639492129],"rgb":[0,0.133333333333333331,0.6],"xyz":[0.0632115976545757352,0.0344366774549449278,0.304695629268670554],"hpluv":[263.749129578079874,432.874263951475,21.7396965211461932],"hsluv":[263.749129578079874,99.9999999999928662,21.7396965211461932]},"#0022aa":{"lch":[23.8228560713303921,84.0831956926279389,264.204285416148139],"luv":[23.8228560713303921,-8.49087961251211532,-83.6533846373868641],"rgb":[0,0.133333333333333331,0.66666666666666663],"xyz":[0.0782692906198282773,0.0404597546410460224,0.383999478885669154],"hpluv":[264.204285416148139,447.872821658188343,23.8228560713303921],"hsluv":[264.204285416148139,99.9999999999925251,23.8228560713303921]},"#0022bb":{"lch":[25.9556350824861326,93.9557715434331868,264.531619021467236],"luv":[25.9556350824861326,-8.95364890679069,-93.5281731756572725],"rgb":[0,0.133333333333333331,0.733333333333333282],"xyz":[0.0954068827271649239,0.0473147914839807809,0.474257463984310901],"hpluv":[264.531619021467236,459.336683180505304,25.9556350824861326],"hsluv":[264.531619021467236,99.9999999999932783,25.9556350824861326]},"#0022cc":{"lch":[28.122733334265547,103.772183036952711,264.774345627526145],"luv":[28.122733334265547,-9.45141286238599143,-103.340876555018355],"rgb":[0,0.133333333333333331,0.8],"xyz":[0.114699258170886664,0.0550317416614695826,0.575863974654581057],"hpluv":[264.774345627526145,468.233789407088068,28.122733334265547],"hsluv":[264.774345627526145,99.9999999999932,28.122733334265547]},"#0022dd":{"lch":[30.3126112219004114,113.526334495176528,264.958927468127968],"luv":[30.3126112219004114,-9.97554123069502552,-113.087210599012522],"rgb":[0,0.133333333333333331,0.866666666666666696],"xyz":[0.136217583721520208,0.0636390718817231166,0.689193822554587],"hpluv":[264.958927468127968,475.239568383116307,30.3126112219004114],"hsluv":[264.958927468127968,99.9999999999930651,30.3126112219004114]},"#0022ee":{"lch":[32.516600051948771,123.213441320075319,265.102292473050682],"luv":[32.516600051948771,-10.519601137712538,-122.76355368691101],"rgb":[0,0.133333333333333331,0.933333333333333348],"xyz":[0.160029763987694945,0.0731639439881931475,0.814604638623110278],"hpluv":[265.102292473050682,480.830806343612153,32.516600051948771],"hsluv":[265.102292473050682,99.9999999999931504,32.516600051948771]},"#0022ff":{"lch":[34.728199222084136,132.830192238289243,265.215668718406278],"luv":[34.728199222084136,-11.0787458291525667,-132.367372720447662],"rgb":[0,0.133333333333333331,1],"xyz":[0.186200812397526339,0.0836323633521258575,0.952438826914892123],"hpluv":[265.215668718406278,485.348691920142073,34.728199222084136],"hsluv":[265.215668718406278,99.9999999999995595,34.728199222084136]},"#55bb00":{"lch":[67.6287132051522093,94.1564927152114421,120.799924159261636],"luv":[67.6287132051522093,-48.2120532219748839,80.8767150949587403],"rgb":[0.333333333333333315,0.733333333333333282,0],"xyz":[0.215157742638501348,0.374707404671490163,0.0609878635696619598],"hpluv":[120.799924159261636,176.668237076728246,67.6287132051522093],"hsluv":[120.799924159261636,100.000000000002245,67.6287132051522093]},"#55bb11":{"lch":[67.658807387059241,92.983820197622,121.168613223743336],"luv":[67.658807387059241,-48.1245535730997,79.5614112615755],"rgb":[0.333333333333333315,0.733333333333333282,0.0666666666666666657],"xyz":[0.21616940813813848,0.375112070871345,0.0663159685344176],"hpluv":[121.168613223743336,174.390319462510746,67.658807387059241],"hsluv":[121.168613223743336,98.2170484527892853,67.658807387059241]},"#55bb22":{"lch":[67.7145367797011,90.8418307914021739,121.8702408444057],"luv":[67.7145367797011,-47.9642424389893733,77.1470651987980318],"rgb":[0.333333333333333315,0.733333333333333282,0.133333333333333331],"xyz":[0.218044766276615481,0.375862214126735816,0.076192854730396789],"hpluv":[121.8702408444057,170.232819736279453,67.7145367797011],"hsluv":[121.8702408444057,94.9476871157098401,67.7145367797011]},"#55bb33":{"lch":[67.8061331119394595,87.4049119918652337,123.079288563119889],"luv":[67.8061331119394595,-47.7055226907732077,73.2379802090816128],"rgb":[0.333333333333333315,0.733333333333333282,0.2],"xyz":[0.221132517009073215,0.377097314419718943,0.0924550085880079253],"hpluv":[123.079288563119889,163.570954697809668,67.8061331119394595],"hsluv":[123.079288563119889,89.6636954368763526,67.8061331119394595]},"#55bb44":{"lch":[67.9380247917114701,82.6396215824211851,124.951716096499126],"luv":[67.9380247917114701,-47.3430758547011195,67.7343356349037293],"rgb":[0.333333333333333315,0.733333333333333282,0.266666666666666663],"xyz":[0.225590512253292103,0.378880512517406509,0.115933783540894564],"hpluv":[124.951716096499126,154.352877500253584,67.9380247917114701],"hsluv":[124.951716096499126,82.2446152582935213,67.9380247917114701]},"#55bb55":{"lch":[68.1137800414251,76.6309713561306154,127.715012949239735],"luv":[68.1137800414251,-46.8777966745344372,60.6199467990950964],"rgb":[0.333333333333333315,0.733333333333333282,0.333333333333333315],"xyz":[0.231552926292654115,0.38126547813315137,0.147335830814868479],"hpluv":[127.715012949239735,142.760701907139094,68.1137800414251],"hsluv":[127.715012949239735,72.6906423420812189,68.1137800414251]},"#55bb66":{"lch":[68.3363083667640723,69.6002362161904813,131.717534816394789],"luv":[68.3363083667640723,-46.3160914021311,51.9520216986687728],"rgb":[0.333333333333333315,0.733333333333333282,0.4],"xyz":[0.239137922066162273,0.384299476442554688,0.187283475222012441],"hpluv":[131.717534816394789,129.240469264566769,68.3363083667640723],"hsluv":[131.717534816394789,73.4099839145656432,68.3363083667640723]},"#55bb77":{"lch":[68.6079661831172416,61.943926665161591,137.498849513297245],"luv":[68.6079661831172416,-45.6690129611091,41.8496273084595956],"rgb":[0.333333333333333315,0.733333333333333282,0.466666666666666674],"xyz":[0.248451935086364206,0.388025081650635484,0.23633727712841035],"hpluv":[137.498849513297245,114.568047908441656,68.6079661831172416],"hsluv":[137.498849513297245,74.2430874467708719,68.6079661831172416]},"#55bb88":{"lch":[68.930619778887035,54.3103726013937376,145.860320527647957],"luv":[68.930619778887035,-44.9511677919681389,30.4796503628330058],"rgb":[0.333333333333333315,0.733333333333333282,0.533333333333333326],"xyz":[0.259592352188308,0.392481248491413082,0.29501014053198249],"hpluv":[145.860320527647957,99.9792617157038421,68.930619778887035],"hsluv":[145.860320527647957,75.173468866550067,68.930619778887035]},"#55bb99":{"lch":[69.3056876145919176,47.7211055743842465,157.786981113384826],"luv":[69.3056876145919176,-44.1794697421466793,18.0410191104680564],"rgb":[0.333333333333333315,0.733333333333333282,0.6],"xyz":[0.272649316297380762,0.397704034135042239,0.363776818173100425],"hpluv":[157.786981113384826,87.3737441175344713,69.3056876145919176],"hsluv":[157.786981113384826,76.1818306173187807,69.3056876145919176]},"#55bbaa":{"lch":[69.7341725511637378,43.6310575063277497,173.751690425302456],"luv":[69.7341725511637378,-43.37186937951342,4.74869725787034813],"rgb":[0.333333333333333315,0.733333333333333282,0.66666666666666663],"xyz":[0.287707009262633262,0.403727111321143362,0.443080667790099],"hpluv":[173.751690425302456,79.3943164161001675,69.7341725511637378],"hsluv":[173.751690425302456,77.247554342351421,69.7341725511637378]},"#55bbbb":{"lch":[70.2166895771587605,43.5254926875218899,192.177050630061075],"luv":[70.2166895771587605,-42.5461891389785691,-9.18097508120914796],"rgb":[0.333333333333333315,0.733333333333333282,0.733333333333333282],"xyz":[0.304844601369969936,0.41058214816407812,0.533338652888740716],"hpluv":[192.177050630061075,78.6579587560082274,70.2166895771587605],"hsluv":[192.177050630061075,78.350068429440185,70.2166895771587605]},"#55bbcc":{"lch":[70.753492069254392,47.9033609117661214,209.436295084806801],"luv":[70.753492069254392,-41.719164640327179,-23.5423721905035848],"rgb":[0.333333333333333315,0.733333333333333282,0.8],"xyz":[0.324136976813691691,0.418299098341566922,0.634945163559010872],"hpluv":[209.436295084806801,85.9127126132272849,70.753492069254392],"hsluv":[209.436295084806801,79.4699741443966445,70.753492069254392]},"#55bbdd":{"lch":[71.3444981992470701,55.9344204047748121,223.003151043067675],"luv":[71.3444981992470701,-40.905747403701568,-38.1494327004039633],"rgb":[0.333333333333333315,0.733333333333333282,0.866666666666666696],"xyz":[0.345655302364325179,0.426906428561820428,0.74827501145901687],"hpluv":[223.003151043067675,99.4850863142100081,71.3444981992470701],"hsluv":[223.003151043067675,80.5898663629362,71.3444981992470701]},"#55bbee":{"lch":[71.9893182406489,66.3452770137575385,232.793079173014576],"luv":[71.9893182406489,-40.1186787976363846,-52.8411524624918272],"rgb":[0.333333333333333315,0.733333333333333282,0.933333333333333348],"xyz":[0.369467482630499944,0.436431300668290445,0.873685827527540093],"hpluv":[232.793079173014576,116.944897502912525,71.9893182406489],"hsluv":[232.793079173014576,81.6948376758296888,71.9893182406489]},"#55bbff":{"lch":[72.6872829834048417,78.1278426582451146,239.741904567598624],"luv":[72.6872829834048417,-39.3683093588317803,-67.4840426816504788],"rgb":[0.333333333333333315,0.733333333333333282,1],"xyz":[0.395638531040331309,0.446899720032223169,1.01152001581932205],"hpluv":[239.741904567598624,136.39131713467242,72.6872829834048417],"hsluv":[239.741904567598624,99.9999999999976126,72.6872829834048417]},"#003300":{"lch":[17.3086983277836381,26.7889227675687067,127.71501294924046],"luv":[17.3086983277836381,-16.3877039844862402,21.1917328494772867],"rgb":[0,0.2,0],"xyz":[0.0118377460847071559,0.0236754921694146449,0.00394591536156894181],"hpluv":[127.71501294924046,196.394882900214611,17.3086983277836381],"hsluv":[127.71501294924046,100.000000000002402,17.3086983277836381]},"#003311":{"lch":[17.4974002223845133,22.6621201022865968,134.58430385811792],"luv":[17.4974002223845133,-15.9078557679049606,16.1403783226414816],"rgb":[0,0.2,0.0666666666666666657],"xyz":[0.0128494115843442776,0.0240801583692694977,0.00927402032632457241],"hpluv":[134.58430385811792,164.348724425256108,17.4974002223845133],"hsluv":[134.58430385811792,99.9999999999909335,17.4974002223845133]},"#003322":{"lch":[17.8416856931397234,17.1190432019509622,152.323942273369369],"luv":[17.8416856931397234,-15.1604156821769873,7.95131665159082601],"rgb":[0,0.2,0.133333333333333331],"xyz":[0.0147247697228212963,0.0248303016246603156,0.0191509065223037685],"hpluv":[152.323942273369369,121.753913655152402,17.8416856931397234],"hsluv":[152.323942273369369,99.9999999999912177,17.8416856931397234]},"#003333":{"lch":[18.3937448040413543,14.5523831926532932,192.17705063006116],"luv":[18.3937448040413543,-14.2249612699966086,-3.06958196712752551],"rgb":[0,0.2,0.2],"xyz":[0.017812520455279035,0.0260654019176434319,0.0354130603799149152],"hpluv":[192.17705063006116,100.392967527320849,18.3937448040413543],"hsluv":[192.17705063006116,99.9999999999915,18.3937448040413543]},"#003344":{"lch":[19.1608294605123817,20.3566459399555,229.223567805483242],"luv":[19.1608294605123817,-13.2951121246929258,-15.41535038578591],"rgb":[0,0.2,0.266666666666666663],"xyz":[0.0222705156994979156,0.0278486000153310084,0.0588918353328015537],"hpluv":[229.223567805483242,134.812835768594709,19.1608294605123817],"hsluv":[229.223567805483242,99.9999999999917577,19.1608294605123817]},"#003355":{"lch":[20.1371955335767296,30.6237106081975696,245.893961784182551],"luv":[20.1371955335767296,-12.5075398018694361,-27.9530517031554915],"rgb":[0,0.2,0.333333333333333315],"xyz":[0.0282329297388599076,0.0302335656310758379,0.0902938826067754552],"hpluv":[245.893961784182551,192.973712242731381,20.1371955335767296],"hsluv":[245.893961784182551,99.9999999999920419,20.1371955335767296]},"#003366":{"lch":[21.3076868402923836,41.8514919359335167,253.451236278131262],"luv":[21.3076868402923836,-11.9206140697212764,-40.1179054471226308],"rgb":[0,0.2,0.4],"xyz":[0.0358179255123680859,0.0332675639404791529,0.130241527013919417],"hpluv":[253.451236278131262,249.237832456686277,21.3076868402923836],"hsluv":[253.451236278131262,99.9999999999920846,21.3076868402923836]},"#003377":{"lch":[22.6513946103128916,53.0186284648852251,257.430711853641924],"luv":[22.6513946103128916,-11.5379190314963687,-51.7479602372902434],"rgb":[0,0.2,0.466666666666666674],"xyz":[0.04513193853257,0.0369931691485599692,0.179295328920317326],"hpluv":[257.430711853641924,297.011229333042763,22.6513946103128916],"hsluv":[257.430711853641924,99.9999999999922409,22.6513946103128916]},"#003388":{"lch":[24.1449124481648099,63.8887963004775941,259.778872090702237],"luv":[24.1449124481648099,-11.3369168899634012,-62.8748964862287636],"rgb":[0,0.2,0.533333333333333326],"xyz":[0.0562723556345138265,0.0414493359893375601,0.237968192323889494],"hpluv":[259.778872090702237,335.767299249073346,24.1449124481648099],"hsluv":[259.778872090702237,99.9999999999925677,24.1449124481648099]},"#003399":{"lch":[25.764809398314533,74.4520642325545481,261.279947020055374],"luv":[25.764809398314533,-11.2874373109462987,-73.5914643653724596],"rgb":[0,0.2,0.6],"xyz":[0.0693293197435865399,0.0466721216329667177,0.306734869965007428],"hpluv":[261.279947020055374,366.681615332004242,25.764809398314533],"hsluv":[261.279947020055374,99.9999999999928662,25.764809398314533]},"#0033aa":{"lch":[27.4892253326185596,84.7561178753388447,262.297068677869788],"luv":[27.4892253326185596,-11.360446037840406,-83.9913077831251],"rgb":[0,0.2,0.66666666666666663],"xyz":[0.084387012708839082,0.0526951988190678261,0.386038719582006029],"hpluv":[262.297068677869788,391.244172205097584,27.4892253326185596],"hsluv":[262.297068677869788,99.9999999999925819,27.4892253326185596]},"#0033bb":{"lch":[29.2987082140811808,94.8530383242648725,263.01737082090068],"luv":[29.2987082140811808,-11.531133966499155,-94.1495184734957178],"rgb":[0,0.2,0.733333333333333282],"xyz":[0.101524604816175729,0.0595502356620025847,0.476296704680647776],"hpluv":[263.01737082090068,410.811034734971429,29.2987082140811808],"hsluv":[263.01737082090068,99.999999999992923,29.2987082140811808]},"#0033cc":{"lch":[31.1765026722858281,104.784892642773883,263.545454640352943],"luv":[31.1765026722858281,-11.7793879173059235,-104.120697973319764],"rgb":[0,0.2,0.8],"xyz":[0.120816980259897469,0.0672671858394913863,0.577903215350917931],"hpluv":[263.545454640352943,426.491720029659064,31.1765026722858281],"hsluv":[263.545454640352943,99.9999999999928519,31.1765026722858281]},"#0033dd":{"lch":[33.108496036114694,114.582250296133722,263.943596884481394],"luv":[33.108496036114694,-12.0892798728815123,-113.942711022166648],"rgb":[0,0.2,0.866666666666666696],"xyz":[0.142335305810531026,0.0758745160597449203,0.691233063250923929],"hpluv":[263.943596884481394,439.154381827523366,33.108496036114694],"hsluv":[263.943596884481394,99.999999999992724,33.108496036114694]},"#0033ee":{"lch":[35.0829820911796091,124.266344101148519,264.250786122927309],"luv":[35.0829820911796091,-12.4483077284590724,-123.641271066592637],"rgb":[0,0.2,0.933333333333333348],"xyz":[0.166147486076705764,0.0853993881662149512,0.816643879319447152],"hpluv":[264.250786122927309,449.46548401365061,35.0829820911796091],"hsluv":[264.250786122927309,99.9999999999932214,35.0829820911796091]},"#0033ff":{"lch":[37.0903499028545482,133.851694130242549,264.492451291459133],"luv":[37.0903499028545482,-12.8466699872586325,-133.233776092154926],"rgb":[0,0.2,1],"xyz":[0.192318534486537157,0.0958678075301476473,0.954478067611229],"hpluv":[264.492451291459133,457.933345064777,37.0903499028545482],"hsluv":[264.492451291459133,99.999999999999531,37.0903499028545482]},"#55cc00":{"lch":[72.9678739916599,103.392643433537373,122.072672834653702],"luv":[72.9678739916599,-54.9009234662034089,87.6123696673744661],"rgb":[0.333333333333333315,0.8,0],"xyz":[0.253381485948118268,0.451154891290725057,0.0737291113395339148],"hpluv":[122.072672834653702,179.803140433373983,72.9678739916599],"hsluv":[122.072672834653702,100.000000000002444,72.9678739916599]},"#55cc11":{"lch":[72.9944661394807497,102.34674561848017,122.382691003133885],"luv":[72.9944661394807497,-54.8140205324080085,86.4307786136781431],"rgb":[0.333333333333333315,0.8,0.0666666666666666657],"xyz":[0.254393151447755372,0.45155955749057991,0.0790572163042895515],"hpluv":[122.382691003133885,177.91945009175069,72.9944661394807497],"hsluv":[122.382691003133885,98.5172338886757757,72.9944661394807497]},"#55cc22":{"lch":[73.0437189009647909,100.431941691916791,122.969611448584445],"luv":[73.0437189009647909,-54.6544745496639663,84.2583130836875114],"rgb":[0.333333333333333315,0.8,0.133333333333333331],"xyz":[0.256268509586232429,0.452309700745970711,0.0889341025002687441],"hpluv":[122.969611448584445,174.473032354427772,73.0437189009647909],"hsluv":[122.969611448584445,95.7933392549395,73.0437189009647909]},"#55cc33":{"lch":[73.1246943704287702,97.3470237033586,123.971835615120114],"luv":[73.1246943704287702,-54.3960871275936384,80.7310889875115123],"rgb":[0.333333333333333315,0.8,0.2],"xyz":[0.259356260318690135,0.453544801038953838,0.10519625635787988],"hpluv":[123.971835615120114,168.926560877595364,73.1246943704287702],"hsluv":[123.971835615120114,91.3772697810403542,73.1246943704287702]},"#55cc44":{"lch":[73.2413452172043,93.0415252478239836,125.502056943833935],"luv":[73.2413452172043,-54.0322080150887771,75.7446098244333399],"rgb":[0.333333333333333315,0.8,0.266666666666666663],"xyz":[0.26381425556290905,0.455327999136641404,0.128675031310766519],"hpluv":[125.502056943833935,161.198069131808353,73.2413452172043],"hsluv":[125.502056943833935,85.148140842768953,73.2413452172043]},"#55cc55":{"lch":[73.3968865779486919,87.5572640326508207,127.71501294923992],"luv":[73.3968865779486919,-53.5617851120033066,69.2633356148757144],"rgb":[0.333333333333333315,0.8,0.333333333333333315],"xyz":[0.269776669602271035,0.457712964752386264,0.160077078584740434],"hpluv":[127.71501294923992,151.374900584602614,73.3968865779486919],"hsluv":[127.71501294923992,77.0768048277098,73.3968865779486919]},"#55cc66":{"lch":[73.5939772793636,81.0375847478920832,130.834727778769576],"luv":[73.5939772793636,-52.9887999422805,61.3129449826769175],"rgb":[0.333333333333333315,0.8,0.4],"xyz":[0.277361665375779221,0.460746963061789583,0.200024722991884396],"hpluv":[130.834727778769576,139.728031568505,73.5939772793636],"hsluv":[130.834727778769576,77.5857883184082766,73.5939772793636]},"#55cc77":{"lch":[73.8348152761848553,73.7475179973278898,135.191797127350526],"luv":[73.8348152761848553,-52.3216404431773,51.972515352838883],"rgb":[0.333333333333333315,0.8,0.466666666666666674],"xyz":[0.286675678395981126,0.464472568269870378,0.249078524898282305],"hpluv":[135.191797127350526,126.743455190439619,73.8348152761848553],"hsluv":[135.191797127350526,78.1806978106980779,73.8348152761848553]},"#55cc88":{"lch":[74.1211942127210648,66.1117924559585646,141.267644806662588],"luv":[74.1211942127210648,-51.5723023085178269,41.3650424433311343],"rgb":[0.333333333333333315,0.8,0.533333333333333326],"xyz":[0.297816095497924926,0.468928735110648,0.307751388301854445],"hpluv":[141.267644806662588,113.181605649052202,74.1211942127210648],"hsluv":[141.267644806662588,78.852064841283763,74.1211942127210648]},"#55cc99":{"lch":[74.4545405069311,58.7794373107374923,149.710693018120253],"luv":[74.4545405069311,-50.7554383804248,29.646377947024483],"rgb":[0.333333333333333315,0.8,0.6],"xyz":[0.310873059606997626,0.474151520754277134,0.37651806594297238],"hpluv":[149.710693018120253,100.178278207809555,74.4545405069311],"hsluv":[149.710693018120253,79.5881732610030355,74.4545405069311]},"#55ccaa":{"lch":[74.8359403329188808,52.7021878309829361,161.189357777818884],"luv":[74.8359403329188808,-49.8873315971569866,16.9933736582089097],"rgb":[0.333333333333333315,0.8,0.66666666666666663],"xyz":[0.325930752572250182,0.480174597940378256,0.455821915559971],"hpluv":[161.189357777818884,89.3630022499163772,74.8359403329188808],"hsluv":[161.189357777818884,80.3759073289738097,74.8359403329188808]},"#55ccbb":{"lch":[75.2661614985634,49.1164256219739315,175.805780272408356],"luv":[75.2661614985634,-48.9848849956305372,3.59225665059547428],"rgb":[0.333333333333333315,0.8,0.733333333333333282],"xyz":[0.343068344679586856,0.487029634783313,0.546079900658612782],"hpluv":[175.805780272408356,82.8068592257523619,75.2661614985634],"hsluv":[175.805780272408356,81.2015846585734238,75.2661614985634]},"#55cccc":{"lch":[75.7456730324682894,49.1710405410584386,192.177050630061103],"luv":[75.7456730324682894,-48.0647147647340063,-10.3718090261618983],"rgb":[0.333333333333333315,0.8,0.8],"xyz":[0.362360720123308555,0.494746584960801816,0.647686411328882938],"hpluv":[192.177050630061103,82.3741405590198639,75.7456730324682894],"hsluv":[192.177050630061103,82.0517040066532815,75.7456730324682894]},"#55ccdd":{"lch":[76.2746640882933349,53.2320113470533514,207.674227909684788],"luv":[76.2746640882933349,-47.1424090239802069,-24.7232745296525458],"rgb":[0.333333333333333315,0.8,0.866666666666666696],"xyz":[0.383879045673942154,0.503353915181055322,0.761016259228888936],"hpluv":[207.674227909684788,89.8673986045394,76.2746640882933349],"hsluv":[207.674227909684788,82.913556803619926,76.2746640882933349]},"#55ccee":{"lch":[76.8530630513789674,60.6801737900983298,220.368363670310828],"luv":[76.8530630513789674,-46.2319850937693673,-39.3025068600722207],"rgb":[0.333333333333333315,0.8,0.933333333333333348],"xyz":[0.407691225940116864,0.51287878728752534,0.886427075297412159],"hpluv":[220.368363670310828,105.55411204702996,76.8530630513789674],"hsluv":[220.368363670310828,83.7756757275922865,76.8530630513789674]},"#55ccff":{"lch":[77.4805572724897758,70.4908339109135511,229.9629437526429],"luv":[77.4805572724897758,-45.3455492712466395,-53.9697955040126445],"rgb":[0.333333333333333315,0.8,1],"xyz":[0.433862274349948285,0.523347206651458063,1.02426126358919389],"hpluv":[229.9629437526429,126.754412601721029,77.4805572724897758],"hsluv":[229.9629437526429,99.9999999999968878,77.4805572724897758]},"#004400":{"lch":[24.1097877444294397,37.3150672336374782,127.715012949240432],"luv":[24.1097877444294397,-22.8269080205926969,29.5185791133361732],"rgb":[0,0.266666666666666663,0],"xyz":[0.0206703165676731908,0.0413406331353469575,0.00689010552255753684],"hpluv":[127.715012949240432,196.39488290021464,24.1097877444294397],"hsluv":[127.715012949240432,100.000000000002458,24.1097877444294397]},"#004411":{"lch":[24.2402356883412295,33.8624764720942082,131.447767669063751],"luv":[24.2402356883412295,-22.4148262696793807,25.3819399598065552],"rgb":[0,0.266666666666666663,0.0666666666666666657],"xyz":[0.0216819820673103125,0.0417452993352018104,0.0122182104873131692],"hpluv":[131.447767669063751,177.264269230781338,24.2402356883412295],"hsluv":[131.447767669063751,99.9999999999909335,24.2402356883412295]},"#004422":{"lch":[24.4798388415780295,28.4164365287723264,139.862893984056171],"luv":[24.4798388415780295,-21.7244820008415864,18.3177713379345413],"rgb":[0,0.266666666666666663,0.133333333333333331],"xyz":[0.0235573402057873311,0.0424954425905926317,0.0220950966832923652],"hpluv":[139.862893984056171,147.299199898382369,24.4798388415780295],"hsluv":[139.862893984056171,99.9999999999911466,24.4798388415780295]},"#004433":{"lch":[24.8682723444395748,22.2877633761795622,158.664607016269599],"luv":[24.8682723444395748,-20.760308528761005,8.10888316004269427],"rgb":[0,0.266666666666666663,0.2],"xyz":[0.0266450909382450717,0.0437305428835757445,0.0383572505409035119],"hpluv":[158.664607016269599,113.726114076625834,24.8682723444395748],"hsluv":[158.664607016269599,99.9999999999913172,24.8682723444395748]},"#004444":{"lch":[25.4163828994624552,20.1084089871091685,192.17705063006116],"luv":[25.4163828994624552,-19.6559790417892835,-4.24153273022775057],"rgb":[0,0.266666666666666663,0.266666666666666663],"xyz":[0.0311030861824639487,0.0455137409812633176,0.0618360254937901505],"hpluv":[192.17705063006116,100.392967527320792,25.4163828994624552],"hsluv":[192.17705063006116,99.9999999999914166,25.4163828994624552]},"#004455":{"lch":[26.1275223832094383,25.5944772375096541,223.546306053382096],"luv":[26.1275223832094383,-18.5513329588178841,-17.6330743352507042],"rgb":[0,0.266666666666666663,0.333333333333333315],"xyz":[0.0370655002218259477,0.0478987065970081505,0.093238072767764052],"hpluv":[223.546306053382096,124.304646155287358,26.1275223832094383],"hsluv":[223.546306053382096,99.999999999991644,26.1275223832094383]},"#004466":{"lch":[26.9988561724938734,35.8050932952910514,240.647496291889979],"luv":[26.9988561724938734,-17.5509899640931621,-31.2084516944496499],"rgb":[0,0.266666666666666663,0.4],"xyz":[0.0446504959953341191,0.050932704906411462,0.133185717174908],"hpluv":[240.647496291889979,168.282428552664811,26.9988561724938734],"hsluv":[240.647496291889979,99.9999999999920135,26.9988561724938734]},"#004477":{"lch":[28.0227048150185141,47.4927479988473422,249.396343090395135],"luv":[28.0227048150185141,-16.7127641209025413,-44.4549730392572684],"rgb":[0,0.266666666666666663,0.466666666666666674],"xyz":[0.0539645090155360385,0.0546583101144922784,0.182239519081305923],"hpluv":[249.396343090395135,215.05848085050232,28.0227048150185141],"hsluv":[249.396343090395135,99.9999999999921698,28.0227048150185141]},"#004488":{"lch":[29.1879465441319326,59.3592998069098,254.306666373838311],"luv":[29.1879465441319326,-16.0560040952153962,-57.1465765034181956],"rgb":[0,0.266666666666666663,0.533333333333333326],"xyz":[0.0651049261174798666,0.0591144769552698762,0.24091238248487809],"hpluv":[254.306666373838311,258.06229412425995,29.1879465441319326],"hsluv":[254.306666373838311,99.9999999999923,29.1879465441319326]},"#004499":{"lch":[30.4813623757938217,70.9684349502494,257.321762040288718],"luv":[30.4813623757938217,-15.5758442205369825,-69.2380808233832141],"rgb":[0,0.266666666666666663,0.6],"xyz":[0.07816189022655258,0.0643372625988990338,0.309679060125996],"hpluv":[257.321762040288718,295.440602563868879,30.4813623757938217],"hsluv":[257.321762040288718,99.9999999999926672,30.4813623757938217]},"#0044aa":{"lch":[31.8888011745219231,82.2033012029853,259.305022310013101],"luv":[31.8888011745219231,-15.2553283345918977,-80.7753532092093138],"rgb":[0,0.266666666666666663,0.66666666666666663],"xyz":[0.0932195831918051221,0.0703603397850001422,0.388982909742994598],"hpluv":[259.305022310013101,327.107417663034141,31.8888011745219231],"hsluv":[259.305022310013101,99.9999999999925109,31.8888011745219231]},"#0044bb":{"lch":[33.3960915948532602,93.0667571364503,260.679203518012741],"luv":[33.3960915948532602,-15.0732787327803415,-91.8379962332527],"rgb":[0,0.266666666666666663,0.733333333333333282],"xyz":[0.110357175299141769,0.0772153766279349,0.479240894841636345],"hpluv":[260.679203518012741,353.621176249907,33.3960915948532602],"hsluv":[260.679203518012741,99.9999999999929656,33.3960915948532602]},"#0044cc":{"lch":[34.9896851087579108,103.60206818965716,261.670396128603272],"luv":[34.9896851087579108,-15.0085679377548828,-102.509177255659253],"rgb":[0,0.266666666666666663,0.8],"xyz":[0.129649550742863495,0.0849323268054237,0.5808474055119065],"hpluv":[261.670396128603272,375.722944942957042,34.9896851087579108],"hsluv":[261.670396128603272,99.999999999992923,34.9896851087579108]},"#0044dd":{"lch":[36.6570567010139499,113.860579159150092,262.408492682931296],"luv":[36.6570567010139499,-15.0420602661110987,-112.862606338006245],"rgb":[0,0.266666666666666663,0.866666666666666696],"xyz":[0.151167876293497039,0.0935396570256772364,0.694177253411912498],"hpluv":[262.408492682931296,394.144185167595538,36.6570567010139499],"hsluv":[262.408492682931296,99.9999999999929088,36.6570567010139499]},"#0044ee":{"lch":[38.386911506645724,123.889384104134805,262.972536787865295],"luv":[38.386911506645724,-15.1572566165017264,-122.958680318078251],"rgb":[0,0.266666666666666663,0.933333333333333348],"xyz":[0.174980056559671776,0.103064529132147253,0.819588069480435721],"hpluv":[262.972536787865295,409.534269399062566,38.386911506645724],"hsluv":[262.972536787865295,99.9999999999931,38.386911506645724]},"#0044ff":{"lch":[40.1692504091911928,133.727629508879147,263.412926975584469],"luv":[40.1692504091911928,-15.3403008413028115,-132.844849595919101],"rgb":[0,0.266666666666666663,1],"xyz":[0.201151104969503169,0.113532948496079963,0.957422257772217566],"hpluv":[263.412926975584469,422.441664595501038,40.1692504091911928],"hsluv":[263.412926975584469,99.9999999999994174,40.1692504091911928]},"#55dd00":{"lch":[78.2526895057908121,112.425836873112019,123.034721453117925],"luv":[78.2526895057908121,-61.2886270790004488,94.2511166373683267],"rgb":[0.333333333333333315,0.866666666666666696,0],"xyz":[0.296015476495293473,0.536422872385076577,0.0879404415219252333],"hpluv":[123.034721453117925,210.800433631405241,78.2526895057908121],"hsluv":[123.034721453117925,100.000000000002288,78.2526895057908121]},"#55dd11":{"lch":[78.2763843013059102,111.485784004679431,123.297526334296066],"luv":[78.2763843013059102,-61.2042162992655108,93.1832814529020368],"rgb":[0.333333333333333315,0.866666666666666696,0.0666666666666666657],"xyz":[0.297027141994930577,0.536827538584931485,0.09326854648668087],"hpluv":[123.297526334296066,209.310421322213443,78.2763843013059102],"hsluv":[123.297526334296066,98.7516598856555561,78.2763843013059102]},"#55dd22":{"lch":[78.3202766586626353,109.761570808260174,123.793166282380056],"luv":[78.3202766586626353,-61.0490014413279738,91.2174426812846235],"rgb":[0.333333333333333315,0.866666666666666696,0.133333333333333331],"xyz":[0.298902500133407634,0.537577681840322286,0.103145432682660063],"hpluv":[123.793166282380056,206.571995072092733,78.3202766586626353],"hsluv":[123.793166282380056,96.4551350157602201,78.3202766586626353]},"#55dd33":{"lch":[78.3924559312840898,106.97461234521991,124.633900466047606],"luv":[78.3924559312840898,-60.7969536029841819,88.018737317724927],"rgb":[0.333333333333333315,0.866666666666666696,0.2],"xyz":[0.30199025086586534,0.538812782133305412,0.119407586540271199],"hpluv":[124.633900466047606,202.130538056106332,78.3924559312840898],"hsluv":[124.633900466047606,92.7228817840519355,78.3924559312840898]},"#55dd44":{"lch":[78.4964717161501255,103.064578059178857,125.904361390923313],"luv":[78.4964717161501255,-60.4405744101571756,83.4819993500804],"rgb":[0.333333333333333315,0.866666666666666696,0.266666666666666663],"xyz":[0.306448246110084255,0.540595980230993,0.142886361493157837],"hpluv":[125.904361390923313,195.867385876976442,78.4964717161501255],"hsluv":[125.904361390923313,87.4391970363123221,78.4964717161501255]},"#55dd55":{"lch":[78.635232286968062,98.0447483954765175,127.715012949239977],"luv":[78.635232286968062,-59.9773394353739,77.559599291037074],"rgb":[0.333333333333333315,0.866666666666666696,0.333333333333333315],"xyz":[0.31241066014944624,0.542980945846737728,0.174288408767131753],"hpluv":[127.715012949239977,187.771435495127037,78.635232286968062],"hsluv":[127.715012949239977,80.5594181605674891,78.635232286968062]},"#55dd66":{"lch":[78.8111684825228451,92.0067823672703469,130.218539636235505],"luv":[78.8111684825228451,-59.4092211255214551,70.2551951590567],"rgb":[0.333333333333333315,0.866666666666666696,0.4],"xyz":[0.319995655922954425,0.546014944156141,0.214236053174275715],"hpluv":[130.218539636235505,177.951388767985407,78.8111684825228451],"hsluv":[130.218539636235505,80.9267337864342693,78.8111684825228451]},"#55dd77":{"lch":[79.0263204832261863,85.1319563310668315,133.631202536406704],"luv":[79.0263204832261863,-58.7422260542947043,61.618186170487121],"rgb":[0.333333333333333315,0.866666666666666696,0.466666666666666674],"xyz":[0.329309668943156331,0.549740549364221898,0.263289855080673596],"hpluv":[133.631202536406704,166.66460441184762,79.0263204832261863],"hsluv":[133.631202536406704,81.3592233632428,79.0263204832261863]},"#55dd88":{"lch":[79.2823892178289,77.7115996221609322,138.259418470179128],"luv":[79.2823892178289,-57.9858175415186921,51.7371982221375],"rgb":[0.333333333333333315,0.866666666666666696,0.533333333333333326],"xyz":[0.340450086045100131,0.55419671620499944,0.321962718484245791],"hpluv":[138.259418470179128,154.371102046011828,79.2823892178289],"hsluv":[138.259418470179128,81.8514411788724,79.2823892178289]},"#55dd99":{"lch":[79.5807696492298504,70.1818875082800133,144.522564094970761],"luv":[79.5807696492298504,-57.1522092721799737,40.7323251181885624],"rgb":[0.333333333333333315,0.866666666666666696,0.6],"xyz":[0.353507050154172831,0.559419501848628653,0.390729396125363726],"hpluv":[144.522564094970761,141.828030979619143,79.5807696492298504],"hsluv":[144.522564094970761,82.3962440946128538,79.5807696492298504]},"#55ddaa":{"lch":[79.9225742847085,63.1747442044720913,152.933070337002903],"luv":[79.9225742847085,-56.2555676549498145,28.7464678372812266],"rgb":[0.333333333333333315,0.866666666666666696,0.66666666666666663],"xyz":[0.368564743119425386,0.56544257903472972,0.470033245742362271],"hpluv":[152.933070337002903,130.236824512138128,79.9225742847085],"hsluv":[152.933070337002903,82.9852754707380313,79.9225742847085]},"#55ddbb":{"lch":[80.3086513857730466,57.5612329714098649,163.927126105934775],"luv":[80.3086513857730466,-55.3111838467604926,15.9363886328987263],"rgb":[0.333333333333333315,0.866666666666666696,0.733333333333333282],"xyz":[0.385702335226762061,0.572297615877664478,0.560291230841004073],"hpluv":[163.927126105934775,121.406580229851471,80.3086513857730466],"hsluv":[163.927126105934775,83.6094665313934,80.3086513857730466]},"#55ddcc":{"lch":[80.7396004409482657,54.3905283694756534,177.403333860877041],"luv":[80.7396004409482657,-54.3346805898883076,2.46415533308082235],"rgb":[0.333333333333333315,0.866666666666666696,0.8],"xyz":[0.40499471067048376,0.580014566055153336,0.661897741511274229],"hpluv":[177.403333860877041,117.73427745544555,80.7396004409482657],"hsluv":[177.403333860877041,84.2595162876674664,80.7396004409482657]},"#55dddd":{"lch":[81.2157864208401605,54.5690893966631236,192.177050630061103],"luv":[81.2157864208401605,-53.3413100060750764,-11.5104371948595805],"rgb":[0.333333333333333315,0.866666666666666696,0.866666666666666696],"xyz":[0.426513036221117359,0.588621896275406842,0.775227589411280227],"hpluv":[192.177050630061103,121.625211000731653,81.2157864208401605],"hsluv":[192.177050630061103,84.9263164151063847,81.2157864208401605]},"#55ddee":{"lch":[81.73735371635685,58.3741450028570412,206.269517439458],"luv":[81.73735371635685,-52.3453816136692609,-25.8360567528002605],"rgb":[0.333333333333333315,0.866666666666666696,0.933333333333333348],"xyz":[0.450325216487292068,0.598146768381876859,0.90063840547980345],"hpluv":[206.269517439458,134.436687270459231,81.73735371635685],"hsluv":[206.269517439458,85.6012975649288421,81.73735371635685]},"#55ddff":{"lch":[82.3042402700550753,65.3293790133507599,218.171205050819623],"luv":[82.3042402700550753,-51.3598401525014268,-40.374429801293],"rgb":[0.333333333333333315,0.866666666666666696,1],"xyz":[0.476496264897123489,0.608615187745809583,1.0384725937715853],"hpluv":[218.171205050819623,156.046773818868189,82.3042402700550753],"hsluv":[218.171205050819623,99.9999999999958078,82.3042402700550753]},"#005500":{"lch":[30.6325595368381371,47.4104554868850059,127.715012949240474],"luv":[30.6325595368381371,-29.0026036892131067,37.5046699588244152],"rgb":[0,0.333333333333333315,0],"xyz":[0.0324835732820191528,0.0649671465640392215,0.0108278577606727468],"hpluv":[127.715012949240474,196.39488290021464,30.6325595368381371],"hsluv":[127.715012949240474,100.000000000002331,30.6325595368381371]},"#005511":{"lch":[30.7291805540017933,44.5531719728198325,130.030983014983661],"luv":[30.7291805540017933,-28.6566785608310113,34.1142185415534129],"rgb":[0,0.333333333333333315,0.0666666666666666657],"xyz":[0.033495238781656278,0.0653718127638940744,0.01615596272542838],"hpluv":[130.030983014983661,183.978458821492183,30.7291805540017933],"hsluv":[130.030983014983661,99.9999999999909335,30.7291805540017933]},"#005522":{"lch":[30.9072407208055395,39.7502752239143149,134.89425658266407],"luv":[30.9072407208055395,-28.0557665948475297,28.1595159964153758],"rgb":[0,0.333333333333333315,0.133333333333333331],"xyz":[0.0353705969201332932,0.0661219560192848888,0.0260328489214075726],"hpluv":[134.89425658266407,163.199653312489744,30.9072407208055395],"hsluv":[134.89425658266407,99.9999999999909903,30.9072407208055395]},"#005533":{"lch":[31.1975029455576802,33.2574057268159,144.773587953954916],"luv":[31.1975029455576802,-27.1672793055155033,19.1831689460880241],"rgb":[0,0.333333333333333315,0.2],"xyz":[0.0384583476525910337,0.067357056312268,0.0422950027790187227],"hpluv":[144.773587953954916,135.271984326190278,31.1975029455576802],"hsluv":[144.773587953954916,99.9999999999912177,31.1975029455576802]},"#005544":{"lch":[31.6103799108948778,27.1932751339722678,163.464109831123295],"luv":[31.6103799108948778,-26.0686058468900157,7.73963834500076331],"rgb":[0,0.333333333333333315,0.266666666666666663],"xyz":[0.0429163428968099142,0.0691402544099555816,0.0657737777319053613],"hpluv":[163.464109831123295,109.16191048912313,31.6103799108948778],"hsluv":[163.464109831123295,99.999999999991374,31.6103799108948778]},"#005555":{"lch":[32.1516370434520482,25.4370682812028797,192.17705063006116],"luv":[32.1516370434520482,-24.8647459548099441,-5.36552433088697445],"rgb":[0,0.333333333333333315,0.333333333333333315],"xyz":[0.0488787569361719063,0.0715252200257004145,0.0971758250058792628],"hpluv":[192.17705063006116,100.392967527320877,32.1516370434520482],"hsluv":[192.17705063006116,99.9999999999915,32.1516370434520482]},"#005566":{"lch":[32.8230722751799036,30.545644410021449,219.238547792689275],"luv":[32.8230722751799036,-23.6581848019048806,-19.3216636007977201],"rgb":[0,0.333333333333333315,0.4],"xyz":[0.0564637527096800845,0.074559218335103733,0.137123469413023225],"hpluv":[219.238547792689275,118.088984839520307,32.8230722751799036],"hsluv":[219.238547792689275,99.9999999999916298,32.8230722751799036]},"#005577":{"lch":[33.6230950493472065,40.3511971197610322,236.060106868306946],"luv":[33.6230950493472065,-22.5289967279924532,-33.4763112577822142],"rgb":[0,0.333333333333333315,0.466666666666666674],"xyz":[0.065777765729882,0.0782848235431845424,0.186177271319421134],"hpluv":[236.060106868306946,152.285327594300298,33.6230950493472065],"hsluv":[236.060106868306946,99.9999999999918,33.6230950493472065]},"#005588":{"lch":[34.5473308406039266,52.0529464564925703,245.569923252468897],"luv":[34.5473308406039266,-21.5281838547339461,-47.3924733973576764],"rgb":[0,0.333333333333333315,0.533333333333333326],"xyz":[0.076918182831825832,0.0827409903839621402,0.244850134722993301],"hpluv":[245.569923252468897,191.192188881903292,34.5473308406039266],"hsluv":[245.569923252468897,99.9999999999919709,34.5473308406039266]},"#005599":{"lch":[35.5892574919693772,64.2489447401961655,251.22324643469841],"luv":[35.5892574919693772,-20.6805523421639172,-60.8296116628390067],"rgb":[0,0.333333333333333315,0.6],"xyz":[0.0899751469408985316,0.0879637760275913,0.313616812364111208],"hpluv":[251.22324643469841,229.079590546614583,35.5892574919693772],"hsluv":[251.22324643469841,99.9999999999922125,35.5892574919693772]},"#0055aa":{"lch":[36.7408379507026,76.3552968047486189,254.821646708521541],"luv":[36.7408379507026,-19.9916927982920107,-73.6916791042243347],"rgb":[0,0.333333333333333315,0.66666666666666663],"xyz":[0.105032839906151088,0.0939868532136924,0.392920661981109809],"hpluv":[254.821646708521541,263.711724718685957,36.7408379507026],"hsluv":[254.821646708521541,99.9999999999921556,36.7408379507026]},"#0055bb":{"lch":[37.993106438468665,88.146943006458784,257.249085911334646],"luv":[37.993106438468665,-19.4551758320548949,-85.9731335635018],"rgb":[0,0.333333333333333315,0.733333333333333282],"xyz":[0.122170432013487734,0.100841890056627165,0.483178647079751555],"hpluv":[257.249085911334646,294.402692429224658,37.993106438468665],"hsluv":[257.249085911334646,99.9999999999924114,37.993106438468665]},"#0055cc":{"lch":[39.3366742314503384,99.5579220492412,258.963845190076711],"luv":[39.3366742314503384,-19.0582121518786707,-97.7167559446012888],"rgb":[0,0.333333333333333315,0.8],"xyz":[0.141462807457209461,0.108558840234115966,0.584785157750021711],"hpluv":[258.963845190076711,321.157087606315883,39.3366742314503384],"hsluv":[258.963845190076711,99.9999999999926,39.3366742314503384]},"#0055dd":{"lch":[40.7621365800243538,110.592821589198408,260.220233587680809],"luv":[40.7621365800243538,-18.7854624460211568,-108.985680654613518],"rgb":[0,0.333333333333333315,0.866666666666666696],"xyz":[0.162981133007843,0.1171661704543695,0.698115005650027709],"hpluv":[260.220233587680809,344.278042660470817,40.7621365800243538],"hsluv":[260.220233587680809,99.9999999999926672,40.7621365800243538]},"#0055ee":{"lch":[42.2603772648371105,121.285075365136905,261.168248695532611],"luv":[42.2603772648371105,-18.6213165580406681,-119.847052846418293],"rgb":[0,0.333333333333333315,0.933333333333333348],"xyz":[0.186793313274017742,0.126691042560839517,0.823525821718550932],"hpluv":[261.168248695532611,364.177675198016914,42.2603772648371105],"hsluv":[261.168248695532611,99.9999999999924256,42.2603772648371105]},"#0055ff":{"lch":[43.8227784910393,131.676969611306021,261.90102950371454],"luv":[43.8227784910393,-18.5511048350842884,-130.363648443170376],"rgb":[0,0.333333333333333315,1],"xyz":[0.212964361683849135,0.137159461924772241,0.961360010010332777],"hpluv":[261.90102950371454,381.28457852045068,43.8227784910393],"hsluv":[261.90102950371454,99.9999999999993321,43.8227784910393]},"#55ee00":{"lch":[83.4834241678481277,121.26988118033438,123.77813801339137],"luv":[83.4834241678481277,-67.423446821455,100.799121524975831],"rgb":[0.333333333333333315,0.933333333333333348,0],"xyz":[0.343194252835808289,0.630780425066107542,0.103666700302096418],"hpluv":[123.77813801339137,313.542875178701877,83.4834241678481277],"hsluv":[123.77813801339137,100.000000000002402,83.4834241678481277]},"#55ee11":{"lch":[83.5046935730824487,120.419265504422668,124.002811201886018],"luv":[83.5046935730824487,-67.3424968648776456,99.8287915414615412],"rgb":[0.333333333333333315,0.933333333333333348,0.0666666666666666657],"xyz":[0.344205918335445393,0.63118509126596245,0.108994805266852055],"hpluv":[124.002811201886018,311.80236567126093,83.5046935730824487],"hsluv":[124.002811201886018,98.9375412624106616,83.5046935730824487]},"#55ee22":{"lch":[83.5440973470955868,118.856748008926118,124.425313598414533],"luv":[83.5440973470955868,-67.1934622190367463,98.0406302625408728],"rgb":[0.333333333333333315,0.933333333333333348,0.133333333333333331],"xyz":[0.34608127647392245,0.631935234521353251,0.118871691462831247],"hpluv":[124.425313598414533,308.59848358059196,83.5440973470955868],"hsluv":[124.425313598414533,96.9807587800033133,83.5440973470955868]},"#55ee33":{"lch":[83.6089072227085524,116.32446068797897,125.138397832270286],"luv":[83.6089072227085524,-66.950941309724243,95.1259775880964469],"rgb":[0.333333333333333315,0.933333333333333348,0.2],"xyz":[0.349169027206380156,0.633170334814336377,0.135133845320442397],"hpluv":[125.138397832270286,303.387603747434184,83.6089072227085524],"hsluv":[125.138397832270286,93.7945051225822084,83.6089072227085524]},"#55ee44":{"lch":[83.7023291233000748,112.756808619009803,126.207644663357115],"luv":[83.7023291233000748,-66.6069499756988535,90.9813832873448547],"rgb":[0.333333333333333315,0.933333333333333348,0.266666666666666663],"xyz":[0.353627022450599071,0.634953532912023944,0.158612620273329036],"hpluv":[126.207644663357115,296.007020538140409,83.7023291233000748],"hsluv":[126.207644663357115,89.2707337544585,83.7023291233000748]},"#55ee55":{"lch":[83.8270046653184,108.148092962556504,127.715012949240077],"luv":[83.8270046653184,-66.1579022543843,85.5519840841642747],"rgb":[0.333333333333333315,0.933333333333333348,0.333333333333333315],"xyz":[0.359589436489961056,0.637338498527768693,0.190014667547302951],"hpluv":[127.715012949240077,286.404562223907249,83.8270046653184],"hsluv":[127.715012949240077,83.3573838846113091,83.8270046653184]},"#55ee66":{"lch":[83.985160091985378,102.554744038676162,129.769393513035965],"luv":[83.985160091985378,-65.6041880698479645,78.8261760618539853],"rgb":[0.333333333333333315,0.933333333333333348,0.4],"xyz":[0.367174432263469241,0.640372496837172,0.229962311954446885],"hpluv":[129.769393513035965,274.647728218817576,83.985160091985378],"hsluv":[129.769393513035965,83.6273111034757477,83.985160091985378]},"#55ee77":{"lch":[84.1786855608653752,96.1019099790829756,132.519661951802362],"luv":[84.1786855608653752,-64.9498200787083,70.8314758661090451],"rgb":[0.333333333333333315,0.933333333333333348,0.466666666666666674],"xyz":[0.376488445283671147,0.644098102045252863,0.279016113860844794],"hpluv":[132.519661951802362,260.948457539577589,84.1786855608653752],"hsluv":[132.519661951802362,83.9470254314832687,84.1786855608653752]},"#55ee88":{"lch":[84.4091822183787741,88.9952087097784,136.170997575445568],"luv":[84.4091822183787741,-64.2020140446184371,61.6299323860706],"rgb":[0.333333333333333315,0.933333333333333348,0.533333333333333326],"xyz":[0.387628862385614947,0.648554268886030405,0.337688977264417],"hpluv":[136.170997575445568,245.709397001158351,84.4091822183787741],"hsluv":[136.170997575445568,84.3134084109568533,84.4091822183787741]},"#55ee99":{"lch":[84.6779925165317877,81.540352796223317,141.002136712429774],"luv":[84.6779925165317877,-63.3706695007570531,51.3126434824633151],"rgb":[0.333333333333333315,0.933333333333333348,0.6],"xyz":[0.400685826494687647,0.653777054529659618,0.406455654905534869],"hpluv":[141.002136712429774,229.604367075154897,84.6779925165317877],"hsluv":[141.002136712429774,84.7220894550445394,84.6779925165317877]},"#55eeaa":{"lch":[84.9862212486181363,74.1734332109310373,147.37159021859884],"luv":[84.9862212486181363,-62.4677637576075426,39.9934580327855755],"rgb":[0.333333333333333315,0.933333333333333348,0.66666666666666663],"xyz":[0.415743519459940203,0.659800131715760685,0.48575950452253347],"hpluv":[147.37159021859884,213.709694911287,84.9862212486181363],"hsluv":[147.37159021859884,85.1677250765186358,84.9862212486181363]},"#55eebb":{"lch":[85.334751323756052,67.4984157699475418,155.676120933413],"luv":[85.334751323756052,-61.5066955643592337,27.8022037293077737],"rgb":[0.333333333333333315,0.933333333333333348,0.733333333333333282],"xyz":[0.432881111567276877,0.666655168558695443,0.576017489621175272],"hpluv":[155.676120933413,199.690784145783283,85.334751323756052],"hsluv":[155.676120933413,85.6443005177139298,85.334751323756052]},"#55eecc":{"lch":[85.7242566025865784,62.3040769944455,166.184583472908372],"luv":[85.7242566025865784,-60.5016239571924643,14.8778865660505755],"rgb":[0.333333333333333315,0.933333333333333348,0.8],"xyz":[0.452173487010998576,0.674372118736184301,0.677624000291445427],"hpluv":[166.184583472908372,189.979166061651426,85.7242566025865784],"hsluv":[166.184583472908372,86.1454332210543186,85.7242566025865784]},"#55eedd":{"lch":[86.1552131978466349,59.4824491826881427,178.687660418725557],"luv":[86.1552131978466349,-59.4668469392298462,1.36230535390821395],"rgb":[0.333333333333333315,0.933333333333333348,0.866666666666666696],"xyz":[0.473691812561632175,0.682979448956437807,0.790953848191451425],"hpluv":[178.687660418725557,187.703134777137279,86.1552131978466349],"hsluv":[178.687660418725557,86.6646578889921,86.1552131978466349]},"#55eeee":{"lch":[86.6279101052896578,59.7608483004272202,192.177050630061245],"luv":[86.6279101052896578,-58.4162567245265265,-12.6055519466972967],"rgb":[0.333333333333333315,0.933333333333333348,0.933333333333333348],"xyz":[0.497503992827806885,0.692504321062907824,0.916364664259974648],"hpluv":[192.177050630061245,196.025490067494644,86.6279101052896578],"hsluv":[192.177050630061245,87.195676441965972,86.6279101052896578]},"#55eeff":{"lch":[87.1424596935915901,63.3545065705612274,205.119112492248092],"luv":[87.1424596935915901,-57.3628964007582169,-26.8940814923128677],"rgb":[0.333333333333333315,0.933333333333333348,1],"xyz":[0.523675041237638306,0.702972740426840548,1.05419885255175649],"hpluv":[205.119112492248092,217.06231600799947,87.1424596935915901],"hsluv":[205.119112492248092,99.9999999999933,87.1424596935915901]},"#006600":{"lch":[36.9339903888407548,57.1632711650289735,127.71501294924046],"luv":[36.9339903888407548,-34.9687359497521086,45.2197642227726249],"rgb":[0,0.4,0],"xyz":[0.0475116309878656218,0.0950232619757325619,0.0158372103292880942],"hpluv":[127.71501294924046,196.394882900214554,36.9339903888407548],"hsluv":[127.71501294924046,100.000000000002331,36.9339903888407548]},"#006611":{"lch":[37.0090255636121412,54.7765813256580216,129.276595687178855],"luv":[37.0090255636121412,-34.6771211058740789,42.4024897091546933],"rgb":[0,0.4,0.0666666666666666657],"xyz":[0.04852329648750274,0.0954279281755874148,0.0211653152940437239],"hpluv":[129.276595687178855,187.813410896691579,37.0090255636121412],"hsluv":[129.276595687178855,99.9999999999908908,37.0090255636121412]},"#006622":{"lch":[37.1475616040544949,50.6324758020482761,132.428423847637788],"luv":[37.1475616040544949,-34.160143827310165,37.3728802682170453],"rgb":[0,0.4,0.133333333333333331],"xyz":[0.0503986546259797621,0.0961780714309782292,0.0310422014900229234],"hpluv":[132.428423847637788,172.957013965329963,37.1475616040544949],"hsluv":[132.428423847637788,99.9999999999909619,37.1475616040544949]},"#006633":{"lch":[37.3740982281225911,44.601943170857588,138.432874640413417],"luv":[37.3740982281225911,-33.3702331720845464,29.5932572160807972],"rgb":[0,0.4,0.2],"xyz":[0.0534864053584375,0.0974131717239613421,0.0473043553476340667],"hpluv":[138.432874640413417,151.43364776149042,37.3740982281225911],"hsluv":[138.432874640413417,99.9999999999910187,37.3740982281225911]},"#006644":{"lch":[37.6978110334583292,37.6830106217376226,149.139540405640531],"luv":[37.6978110334583292,-32.3478161181420845,19.3294615006434647],"rgb":[0,0.4,0.266666666666666663],"xyz":[0.0579444006026563832,0.099196369821648922,0.0707831303005207],"hpluv":[149.139540405640531,126.843666215367492,37.6978110334583292],"hsluv":[149.139540405640531,99.9999999999911893,37.6978110334583292]},"#006655":{"lch":[38.1247572916394191,31.9621551107412678,167.158861879927684],"luv":[38.1247572916394191,-31.1627826377188839,7.10354401671241],"rgb":[0,0.4,0.333333333333333315],"xyz":[0.0639068146420183752,0.101581335437393755,0.102185177574494607],"hpluv":[167.158861879927684,106.382034992292617,38.1247572916394191],"hsluv":[167.158861879927684,99.9999999999912461,38.1247572916394191]},"#006666":{"lch":[38.6583399620500714,30.584907136256998,192.177050630061132],"luv":[38.6583399620500714,-29.8967608054288689,-6.45137488264605441],"rgb":[0,0.4,0.4],"xyz":[0.0714918104155265466,0.104615333746797073,0.142132821981638569],"hpluv":[192.177050630061132,100.392967527320849,38.6583399620500714],"hsluv":[192.177050630061132,99.9999999999914877,38.6583399620500714]},"#006677":{"lch":[39.2996251720959648,35.3400629634721852,215.899751113488037],"luv":[39.2996251720959648,-28.6270125920824867,-20.7223116547099835],"rgb":[0,0.4,0.466666666666666674],"xyz":[0.080805823435728466,0.108340938954877883,0.191186623888036478],"hpluv":[215.899751113488037,114.108563264217921,39.2996251720959648],"hsluv":[215.899751113488037,99.999999999991644,39.2996251720959648]},"#006688":{"lch":[40.0476371065618721,44.619030030948764,232.088426656893603],"luv":[40.0476371065618721,-27.4159210327312195,-35.2026293738089109],"rgb":[0,0.4,0.533333333333333326],"xyz":[0.0919462405376723,0.112797105795655481,0.249859487291608645],"hpluv":[232.088426656893603,141.378234692038774,40.0476371065618721],"hsluv":[232.088426656893603,99.9999999999918145,40.0476371065618721]},"#006699":{"lch":[40.8996671875350728,56.0796471869528546,242.024131169727298],"luv":[40.8996671875350728,-26.3069429553173038,-49.5264735364712863],"rgb":[0,0.4,0.6],"xyz":[0.105003204646745008,0.118019891439284638,0.318626164932726552],"hpluv":[242.024131169727298,173.990215160848891,40.8996671875350728],"hsluv":[242.024131169727298,99.9999999999919,40.8996671875350728]},"#0066aa":{"lch":[41.8515997707465104,68.3303466488807345,248.245335088520562],"luv":[41.8515997707465104,-25.3254853434547229,-63.4638169768779861],"rgb":[0,0.4,0.66666666666666663],"xyz":[0.12006089761199755,0.124042968625385747,0.397930014549725153],"hpluv":[248.245335088520562,207.176688473106537,41.8515997707465104],"hsluv":[248.245335088520562,99.9999999999921,41.8515997707465104]},"#0066bb":{"lch":[42.8982420202673396,80.6980154219041452,252.339105133311591],"luv":[42.8982420202673396,-24.4823887435570384,-76.8946183711398],"rgb":[0,0.4,0.733333333333333282],"xyz":[0.137198489719334182,0.130898005468320505,0.488187999648366899],"hpluv":[252.339105133311591,238.705643004687346,42.8982420202673396],"hsluv":[252.339105133311591,99.9999999999922125,42.8982420202673396]},"#0066cc":{"lch":[44.0336413015701211,92.8728169416488356,255.165448618736946],"luv":[44.0336413015701211,-23.7781103155697018,-89.7772888680517696],"rgb":[0,0.4,0.8],"xyz":[0.156490865163055937,0.138614955645809307,0.589794510318637],"hpluv":[255.165448618736946,267.63526922347819,44.0336413015701211],"hsluv":[255.165448618736946,99.9999999999922835,44.0336413015701211]},"#0066dd":{"lch":[45.2513748624169807,104.723900430114114,257.197154574648096],"luv":[45.2513748624169807,-23.2064943358636171,-102.120291528843495],"rgb":[0,0.4,0.866666666666666696],"xyz":[0.17800919071368948,0.147222285866062841,0.703124358218643],"hpluv":[257.197154574648096,293.665789717253858,45.2513748624169807],"hsluv":[257.197154574648096,99.9999999999923119,45.2513748624169807]},"#0066ee":{"lch":[46.5448008930790422,116.210429262859975,258.70668283343889],"luv":[46.5448008930790422,-22.7576936505684415,-113.960305585607557],"rgb":[0,0.4,0.933333333333333348],"xyz":[0.201821370979864217,0.156747157972532858,0.828535174287166276],"hpluv":[258.70668283343889,316.82048544727013,46.5448008930790422],"hsluv":[258.70668283343889,99.9999999999923119,46.5448008930790422]},"#0066ff":{"lch":[47.9072652547968758,127.336469583599694,259.859032010401279],"luv":[47.9072652547968758,-22.4202120236612075,-125.347160234402949],"rgb":[0,0.4,1],"xyz":[0.227992419389695611,0.167215577336465582,0.966369362578948121],"hpluv":[259.859032010401279,337.280125749862,47.9072652547968758],"hsluv":[259.859032010401279,99.9999999999992326,47.9072652547968758]},"#55ff00":{"lch":[88.6611895097861691,129.940408372856581,124.363532639485271],"luv":[88.6611895097861691,-73.3437881830395213,107.262288168144963],"rgb":[0.333333333333333315,1,0],"xyz":[0.395046625265482121,0.734485169925456649,0.120950824445320529],"hpluv":[124.363532639485271,511.214684976108288,88.6611895097861691],"hsluv":[124.363532639485271,100.000000000002203,88.6611895097861691]},"#55ff11":{"lch":[88.6804070841335488,129.16615931328721,124.557188606571273],"luv":[88.6804070841335488,-73.2667318726085597,106.376137890284312],"rgb":[0.333333333333333315,1,0.0666666666666666657],"xyz":[0.396058290765119225,0.734889836125311557,0.126278929410076152],"hpluv":[124.557188606571273,509.111714190739917,88.6804070841335488],"hsluv":[124.557188606571273,99.999999999991374,88.6804070841335488]},"#55ff22":{"lch":[88.7160126920472862,127.742171235629925,124.920549061750862],"luv":[88.7160126920472862,-73.1247263453711867,104.741762009752222],"rgb":[0.333333333333333315,1,0.133333333333333331],"xyz":[0.397933648903596282,0.735639979380702358,0.136155815606055358],"hpluv":[124.920549061750862,505.235489858807625,88.7160126920472862],"hsluv":[124.920549061750862,99.9999999999912461,88.7160126920472862]},"#55ff33":{"lch":[88.7745841250638819,125.429409451765778,125.531442964179078],"luv":[88.7745841250638819,-72.893256299655377,102.074041467218606],"rgb":[0.333333333333333315,1,0.2],"xyz":[0.401021399636054,0.736875079673685485,0.152417969463666508],"hpluv":[125.531442964179078,498.916518871762833,88.7745841250638819],"hsluv":[125.531442964179078,99.9999999999915445,88.7745841250638819]},"#55ff44":{"lch":[88.8590323294941271,122.159967165607114,126.442013329478428],"luv":[88.8590323294941271,-72.5641116581323189,98.2726171279075658],"rgb":[0.333333333333333315,1,0.266666666666666663],"xyz":[0.405479394880272903,0.738658277771373,0.175896744416553147],"hpluv":[126.442013329478428,489.934204596741552,88.8590323294941271],"hsluv":[126.442013329478428,99.9999999999914735,88.8590323294941271]},"#55ff55":{"lch":[88.9717666926620154,117.915518658848484,127.715012949240119],"luv":[88.9717666926620154,-72.1329719647303,93.2786357968462],"rgb":[0.333333333333333315,1,0.333333333333333315],"xyz":[0.411441808919634888,0.7410432433871178,0.207298791690527062],"hpluv":[127.715012949240119,478.187207442594797,88.9717666926620154],"hsluv":[127.715012949240119,99.9999999999912461,88.9717666926620154]},"#55ff66":{"lch":[89.1148309932425,112.728028648536309,129.430986513959965],"luv":[89.1148309932425,-71.5990188667543919,87.0700232014633713],"rgb":[0.333333333333333315,1,0.4],"xyz":[0.419026804693143073,0.744077241696521119,0.247246436097671],"hpluv":[129.430986513959965,463.701047487447568,89.1148309932425],"hsluv":[129.430986513959965,99.999999999991374,89.1148309932425]},"#55ff77":{"lch":[89.2899761450384233,106.6837193332601,131.696695826109391],"luv":[89.2899761450384233,-70.9646547782272705,79.6582308552282825],"rgb":[0.333333333333333315,1,0.466666666666666674],"xyz":[0.428340817713345,0.747802846904602,0.296300238004068905],"hpluv":[131.696695826109391,446.653165807945641,89.2899761450384233],"hsluv":[131.696695826109391,99.9999999999911466,89.2899761450384233]},"#55ff88":{"lch":[89.4987035327786344,99.9302046144725722,134.655503696120093],"luv":[89.4987035327786344,-70.2351927280585784,71.0849034376696807],"rgb":[0.333333333333333315,1,0.533333333333333326],"xyz":[0.439481234815288779,0.752259013745379512,0.354973101407641101],"hpluv":[134.655503696120093,427.420708471833962,89.4987035327786344],"hsluv":[134.655503696120093,99.9999999999910472,89.4987035327786344]},"#55ff99":{"lch":[89.7422928839939544,92.6881300553494185,138.499225093055145],"luv":[89.7422928839939544,-69.4184746025382395,61.4179520744067275],"rgb":[0.333333333333333315,1,0.6],"xyz":[0.452538198924361534,0.757481799389008725,0.423739779048759],"hpluv":[138.499225093055145,406.664135605650245,89.7422928839939544],"hsluv":[138.499225093055145,99.9999999999909193,89.7422928839939544]},"#55ffaa":{"lch":[90.021821419032733,85.2692799565717081,143.477548510033301],"luv":[90.021821419032733,-68.524415632830582,50.7469660816416095],"rgb":[0.333333333333333315,1,0.66666666666666663],"xyz":[0.467595891889614035,0.763504876575109792,0.503043628665757581],"hpluv":[143.477548510033301,385.467603776639692,90.021821419032733],"hsluv":[143.477548510033301,99.9999999999908908,90.021821419032733]},"#55ffbb":{"lch":[90.3381779071049635,78.1017696608261929,149.892156464581973],"luv":[90.3381779071049635,-67.5644942943643514,39.178125719584358],"rgb":[0.333333333333333315,1,0.733333333333333282],"xyz":[0.484733483996950709,0.770359913418044551,0.593301613764399383],"hpluv":[149.892156464581973,365.560319764238727,90.3381779071049635],"hsluv":[149.892156464581973,99.9999999999906,90.3381779071049635]},"#55ffcc":{"lch":[90.6920737330737836,71.7555158590460849,158.04408297895796],"luv":[90.6920737330737836,-66.5512174426729217,26.828893251416094],"rgb":[0.333333333333333315,1,0.8],"xyz":[0.504025859440672463,0.778076863595533408,0.694908124434669539],"hpluv":[158.04408297895796,349.620992563925881,90.6920737330737836],"hsluv":[158.04408297895796,99.9999999999905214,90.6920737330737836]},"#55ffdd":{"lch":[91.0840522607878142,66.9403052018893874,168.083008151042208],"luv":[91.0840522607878142,-65.4975936461766,13.8227959213187273],"rgb":[0.333333333333333315,1,0.866666666666666696],"xyz":[0.525544184991306,0.786684193815786914,0.808237972334675536],"hpluv":[168.083008151042208,341.570927955438208,91.0840522607878142],"hsluv":[168.083008151042208,99.9999999999901092,91.0840522607878142]},"#55ffee":{"lch":[91.5144973031429,64.4172738793593,179.746868091755],"luv":[91.5144973031429,-64.4166452130874347,0.284593639433804613],"rgb":[0.333333333333333315,1,0.933333333333333348],"xyz":[0.549356365257480661,0.796209065922256931,0.933648788403198759],"hpluv":[179.746868091755,346.559914030924404,91.5144973031429],"hsluv":[179.746868091755,99.9999999999898108,91.5144973031429]},"#55ffff":{"lch":[91.9836412143362,64.7784688708661918,192.177050630061103],"luv":[91.9836412143362,-63.3209831419883,-13.663935128132529],"rgb":[0.333333333333333315,1,1],"xyz":[0.575527413667312082,0.806677485286189655,1.07148297669498049],"hpluv":[192.177050630061103,370.276433987554753,91.9836412143362],"hsluv":[192.177050630061103,99.9999999999890719,91.9836412143362]},"#007700":{"lch":[43.052730924646589,66.6333343982289534,127.715012949240503],"luv":[43.052730924646589,-40.7618988300426395,52.7111834129681611],"rgb":[0,0.466666666666666674,0],"xyz":[0.0659653690412832505,0.131930738082568333,0.0219884563470937981],"hpluv":[127.715012949240503,196.394882900214441,43.052730924646589],"hsluv":[127.715012949240503,100.000000000002217,43.052730924646589]},"#007711":{"lch":[43.1130460407029,64.6129906803504497,128.830381027920708],"luv":[43.1130460407029,-40.513441406878222,50.333881531534],"rgb":[0,0.466666666666666674,0.0666666666666666657],"xyz":[0.0669770345409203688,0.132335404282423186,0.0273165613118494313],"hpluv":[128.830381027920708,190.173702516526191,43.1130460407029],"hsluv":[128.830381027920708,99.9999999999908908,43.1130460407029]},"#007722":{"lch":[43.2245297597171856,61.038201507507992,131.028383581415682],"luv":[43.2245297597171856,-40.0674788501528809,46.0462721820529381],"rgb":[0,0.466666666666666674,0.133333333333333331],"xyz":[0.0688523926793974,0.133085547537814014,0.0371934475078286239],"hpluv":[131.028383581415682,179.188765673473426,43.2245297597171856],"hsluv":[131.028383581415682,99.9999999999909193,43.2245297597171856]},"#007733":{"lch":[43.4071769639209251,55.6324991773261601,135.049443606961],"luv":[43.4071769639209251,-39.3720497464739694,39.3041558041433419],"rgb":[0,0.466666666666666674,0.2],"xyz":[0.0719401434118551314,0.134320647830797113,0.0534556013654397671],"hpluv":[135.049443606961,162.632131787412106,43.4071769639209251],"hsluv":[135.049443606961,99.9999999999909193,43.4071769639209251]},"#007744":{"lch":[43.6689123670523855,48.8909209361139787,141.845848120172377],"luv":[43.6689123670523855,-38.4454486392046704,30.2038015639029105],"rgb":[0,0.466666666666666674,0.266666666666666663],"xyz":[0.076398138656074,0.136103845928484707,0.0769343763183264],"hpluv":[141.845848120172377,142.067640978907804,43.6689123670523855],"hsluv":[141.845848120172377,99.9999999999910614,43.6689123670523855]},"#007755":{"lch":[44.0154249216106,41.9153980271179,152.953344682219807],"luv":[44.0154249216106,-37.3313854656975224,19.0595973457790286],"rgb":[0,0.466666666666666674,0.333333333333333315],"xyz":[0.082360552695436,0.13848881154422954,0.108336423592300321],"hpluv":[152.953344682219807,120.839250305189026,44.0154249216106],"hsluv":[152.953344682219807,99.9999999999912461,44.0154249216106]},"#007766":{"lch":[44.4505333250062549,36.6388362985735725,170.062665906075836],"luv":[44.4505333250062549,-36.0891470448365865,6.32279929223085357],"rgb":[0,0.466666666666666674,0.4],"xyz":[0.0899455484689441753,0.141522809853632831,0.148284067999444269],"hpluv":[170.062665906075836,104.593337643625588,44.4505333250062549],"hsluv":[170.062665906075836,99.9999999999913314,44.4505333250062549]},"#007777":{"lch":[44.9764013416840669,35.58350047386471,192.17705063006116],"luv":[44.9764013416840669,-34.7828880940388601,-7.50574458738768],"rgb":[0,0.466666666666666674,0.466666666666666674],"xyz":[0.0992595614891461,0.145248415061713654,0.197337869905842178],"hpluv":[192.17705063006116,100.392967527320835,44.9764013416840669],"hsluv":[192.17705063006116,99.9999999999914451,44.9764013416840669]},"#007788":{"lch":[45.5937085159301603,40.0276538709377,213.25546015720218],"luv":[45.5937085159301603,-33.4724811425817,-21.9500815618326577],"rgb":[0,0.466666666666666674,0.533333333333333326],"xyz":[0.110399978591089923,0.149704581902491252,0.256010733309414373],"hpluv":[213.25546015720218,111.402399127386914,45.5937085159301603],"hsluv":[213.25546015720218,99.9999999999916156,45.5937085159301603]},"#007799":{"lch":[46.3018156057360244,48.7595612734022907,228.659125647543163],"luv":[46.3018156057360244,-32.2075160625452597,-36.6083422822662357],"rgb":[0,0.466666666666666674,0.6],"xyz":[0.123456942700162636,0.154927367546120409,0.324777410950532253],"hpluv":[228.659125647543163,133.62911578541167,46.3018156057360244],"hsluv":[228.659125647543163,99.9999999999917577,46.3018156057360244]},"#0077aa":{"lch":[47.0989379645613795,59.8445186957143,238.773531847525192],"luv":[47.0989379645613795,-31.0247207616125031,-51.1745358511991597],"rgb":[0,0.466666666666666674,0.66666666666666663],"xyz":[0.138514635665415164,0.160950444732221504,0.404081260567530853],"hpluv":[238.773531847525192,161.232500989130415,47.0989379645613795],"hsluv":[238.773531847525192,99.999999999991914,47.0989379645613795]},"#0077bb":{"lch":[47.9823278818304146,71.9659744020705432,245.408717039049918],"luv":[47.9823278818304146,-29.9480972873728,-65.4386196408934495],"rgb":[0,0.466666666666666674,0.733333333333333282],"xyz":[0.155652227772751839,0.167805481575156262,0.4943392456661726],"hpluv":[245.408717039049918,190.32034768447943,47.9823278818304146],"hsluv":[245.408717039049918,99.9999999999920561,47.9823278818304146]},"#0077cc":{"lch":[48.9484610491917067,84.4081580704908276,249.9122747909542],"luv":[48.9484610491917067,-28.9906993044602252,-79.2734287305105454],"rgb":[0,0.466666666666666674,0.8],"xyz":[0.174944603216473565,0.175522431752645064,0.595945756336442756],"hpluv":[249.9122747909542,218.818838178067409,48.9484610491917067],"hsluv":[249.9122747909542,99.9999999999922125,48.9484610491917067]},"#0077dd":{"lch":[49.9932200675849,96.8017840095652389,253.089738624096071],"luv":[49.9932200675849,-28.15707849035525,-92.6162206005108715],"rgb":[0,0.466666666666666674,0.866666666666666696],"xyz":[0.196462928767107109,0.184129761972898598,0.709275604236448753],"hpluv":[253.089738624096071,245.70363248193695,49.9932200675849],"hsluv":[253.089738624096071,99.9999999999921272,49.9932200675849]},"#0077ee":{"lch":[51.1120678627821547,108.963279527823246,255.411176145550684],"luv":[51.1120678627821547,-27.4457353077348785,-105.450120430828804],"rgb":[0,0.466666666666666674,0.933333333333333348],"xyz":[0.220275109033281846,0.193654634079368643,0.834686420304972],"hpluv":[255.411176145550684,270.517920771775039,51.1120678627821547],"hsluv":[255.411176145550684,99.9999999999922551,51.1120678627821547]},"#0077ff":{"lch":[52.300205122294,120.810007187166491,257.158195690943],"luv":[52.300205122294,-26.851223719183885,-117.788240590245493],"rgb":[0,0.466666666666666674,1],"xyz":[0.24644615744311324,0.204123053443301339,0.972520608596753822],"hpluv":[257.158195690943,293.11554041762389,52.300205122294],"hsluv":[257.158195690943,99.9999999999991189,52.300205122294]},"#008800":{"lch":[49.0166039301270473,75.8637069146273291,127.71501294924046],"luv":[49.0166039301270473,-46.4084346679225348,60.0129920808956214],"rgb":[0,0.533333333333333326,0],"xyz":[0.0880377387662537,0.176075477532509878,0.0293459129220837445],"hpluv":[127.71501294924046,196.394882900214583,49.0166039301270473],"hsluv":[127.71501294924046,100.000000000002359,49.0166039301270473]},"#008811":{"lch":[49.066374048408079,74.1307167522113133,128.546257021813432],"luv":[49.066374048408079,-46.1942790787678064,57.9780281364371461],"rgb":[0,0.533333333333333326,0.0666666666666666657],"xyz":[0.0890494042658908219,0.17648014373236473,0.0346740178868393742],"hpluv":[128.546257021813432,191.713881645209739,49.066374048408079],"hsluv":[128.546257021813432,99.9999999999908908,49.066374048408079]},"#008822":{"lch":[49.1584337135343503,71.0276132120671,130.159311596065152],"luv":[49.1584337135343503,-45.8067815452400779,54.2831521106656112],"rgb":[0,0.533333333333333326,0.133333333333333331],"xyz":[0.090924762404367851,0.177230286987755559,0.0445509040828185737],"hpluv":[130.159311596065152,183.344763498744726,49.1584337135343503],"hsluv":[130.159311596065152,99.9999999999909335,49.1584337135343503]},"#008833":{"lch":[49.3094443496477197,66.226384742663825,133.033255321350083],"luv":[49.3094443496477197,-45.1943905199784481,48.4086882864121648],"rgb":[0,0.533333333333333326,0.2],"xyz":[0.0940125131368255845,0.178465387280738658,0.060813057940429717],"hpluv":[133.033255321350083,170.427732437953466,49.3094443496477197],"hsluv":[133.033255321350083,99.9999999999909903,49.3094443496477197]},"#008844":{"lch":[49.5262444349700388,59.9709063982634589,137.708745518338219],"luv":[49.5262444349700388,-44.3625073014275202,40.3543995143041],"rgb":[0,0.533333333333333326,0.266666666666666663],"xyz":[0.0984705083810444581,0.180248585378426252,0.0842918328933163485],"hpluv":[137.708745518338219,153.654237765673173,49.5262444349700388],"hsluv":[137.708745518338219,99.9999999999910756,49.5262444349700388]},"#008855":{"lch":[49.8139833497656355,52.876539169604392,145.043769270071607],"luv":[49.8139833497656355,-43.3370811793054074,30.2956397756013871],"rgb":[0,0.533333333333333326,0.333333333333333315],"xyz":[0.104432922420406457,0.182633550994171084,0.115693880167290264],"hpluv":[145.043769270071607,134.694876863850226,49.8139833497656355],"hsluv":[145.043769270071607,99.9999999999911893,49.8139833497656355]},"#008866":{"lch":[50.1764284055384593,46.0546718070978613,156.264738854980266],"luv":[50.1764284055384593,-42.1591397878491918,18.5375221539472825],"rgb":[0,0.533333333333333326,0.4],"xyz":[0.112017918193914628,0.185667549303574375,0.155641524574434226],"hpluv":[156.264738854980266,116.469784961641182,50.1764284055384593],"hsluv":[156.264738854980266,99.9999999999912461,50.1764284055384593]},"#008877":{"lch":[50.6161324463586766,41.2412500498738694,172.391379993232761],"luv":[50.6161324463586766,-40.8781468825216336,5.46056893805106114],"rgb":[0,0.533333333333333326,0.466666666666666674],"xyz":[0.121331931214116548,0.189393154511655198,0.204695326480832135],"hpluv":[172.391379993232761,103.390869865470918,50.6161324463586766],"hsluv":[172.391379993232761,99.9999999999913456,50.6161324463586766]},"#008888":{"lch":[51.1345503085294695,40.4555776108317602,192.177050630061132],"luv":[51.1345503085294695,-39.545345738280993,-8.53342781453345],"rgb":[0,0.533333333333333326,0.533333333333333326],"xyz":[0.132472348316060362,0.193849321352432796,0.263368189884404302],"hpluv":[192.177050630061132,100.392967527320849,51.1345503085294695],"hsluv":[192.177050630061132,99.9999999999914877,51.1345503085294695]},"#008899":{"lch":[51.7321394091786715,44.6308971675799881,211.11913642158629],"luv":[51.7321394091786715,-38.208266400248,-23.066108485628412],"rgb":[0,0.533333333333333326,0.6],"xyz":[0.145529312425133089,0.199072106996061954,0.332134867525522237],"hpluv":[211.11913642158629,109.474886689832829,51.7321394091786715],"hsluv":[211.11913642158629,99.999999999991573,51.7321394091786715]},"#0088aa":{"lch":[52.4084594351014914,52.8385448128107598,225.694192047300788],"luv":[52.4084594351014914,-36.9070805124816346,-37.8124215831333572],"rgb":[0,0.533333333333333326,0.66666666666666663],"xyz":[0.160587005390385618,0.205095184182163048,0.411438717142520782],"hpluv":[225.694192047300788,127.934825585488483,52.4084594351014914],"hsluv":[225.694192047300788,99.9999999999917,52.4084594351014914]},"#0088bb":{"lch":[53.1622766119302952,63.4852929684367,235.812153399491365],"luv":[53.1622766119302952,-35.672889398211332,-52.5150205681309856],"rgb":[0,0.533333333333333326,0.733333333333333282],"xyz":[0.177724597497722292,0.211950221025097807,0.501696702241162584],"hpluv":[235.812153399491365,151.533580059798538,53.1622766119302952],"hsluv":[235.812153399491365,99.9999999999918572,53.1622766119302952]},"#0088cc":{"lch":[53.9916730817088961,75.3609801883338406,242.731381598031476],"luv":[53.9916730817088961,-34.5275961267233455,-66.9859868976812578],"rgb":[0,0.533333333333333326,0.8],"xyz":[0.197016972941444019,0.219667171202586609,0.60330321291143274],"hpluv":[242.731381598031476,177.116523654060018,53.9916730817088961],"hsluv":[242.731381598031476,99.9999999999919282,53.9916730817088961]},"#0088dd":{"lch":[54.894159312243417,87.7409205251226,247.565131731451658],"luv":[54.894159312243417,-33.4848266090853,-81.1001573460580545],"rgb":[0,0.533333333333333326,0.866666666666666696],"xyz":[0.218535298492077562,0.228274501422840143,0.716633060811438738],"hpluv":[247.565131731451658,202.822146488853946,54.894159312243417],"hsluv":[247.565131731451658,99.9999999999921414,54.894159312243417]},"#0088ee":{"lch":[55.8667862779657725,100.217939448335898,251.046181944553609],"luv":[55.8667862779657725,-32.551381555669181,-94.7841914355319091],"rgb":[0,0.533333333333333326,0.933333333333333348],"xyz":[0.242347478758252299,0.237799373529310187,0.842043876879962],"hpluv":[251.046181944553609,227.630842720065772,55.8667862779657725],"hsluv":[251.046181944553609,99.9999999999923261,55.8667862779657725]},"#0088ff":{"lch":[56.9062538959811803,112.568622459607909,253.628629682131134],"luv":[56.9062538959811803,-31.728824885135996,-108.00452043253614],"rgb":[0,0.533333333333333326,1],"xyz":[0.268518527168083665,0.248267792893242883,0.979878065171743806],"hpluv":[253.628629682131134,251.013269675548315,56.9062538959811803],"hsluv":[253.628629682131134,99.9999999999989,56.9062538959811803]},"#009900":{"lch":[54.8465256129575778,84.8867610313905629,127.71501294924046],"luv":[54.8465256129575778,-51.9281467214630865,67.1507987776363677],"rgb":[0,0.6,0],"xyz":[0.11390733921872119,0.227814678437445572,0.037969113072906],"hpluv":[127.71501294924046,196.394882900214611,54.8465256129575778],"hsluv":[127.71501294924046,100.000000000002359,54.8465256129575778]},"#009911":{"lch":[54.8884489227774139,83.3822613920475533,128.355135015114286],"luv":[54.8884489227774139,-51.7415219578455918,65.3866685214771195],"rgb":[0,0.6,0.0666666666666666657],"xyz":[0.114919004718358309,0.228219344637300425,0.0432972180376616292],"hpluv":[128.355135015114286,192.766711025891595,54.8884489227774139],"hsluv":[128.355135015114286,99.9999999999908908,54.8884489227774139]},"#009922":{"lch":[54.9660326693047665,80.6666974094322882,129.584419925030318],"luv":[54.9660326693047665,-51.4019848556742147,62.168738316310943],"rgb":[0,0.6,0.133333333333333331],"xyz":[0.116794362856835338,0.228969487892691254,0.0531741042336408287],"hpluv":[129.584419925030318,186.225526863887183,54.9660326693047665],"hsluv":[129.584419925030318,99.9999999999908766,54.9660326693047665]},"#009933":{"lch":[55.0934048637144826,76.4021664793699529,131.735569901926],"luv":[55.0934048637144826,-50.8604445821364948,57.0132109221080725],"rgb":[0,0.6,0.2],"xyz":[0.119882113589293071,0.230204588185674353,0.0694362580912519789],"hpluv":[131.735569901926,175.972736191316358,55.0934048637144826],"hsluv":[131.735569901926,99.9999999999909903,55.0934048637144826]},"#009944":{"lch":[55.2764995203901321,70.6973886175228614,135.142608572152881],"luv":[55.2764995203901321,-50.1148739946869739,49.8660221176072795],"rgb":[0,0.6,0.266666666666666663],"xyz":[0.124340108833511945,0.231987786283361946,0.0929150330441386174],"hpluv":[135.142608572152881,162.293888564466016,55.2764995203901321],"hsluv":[135.142608572152881,99.9999999999910472,55.2764995203901321]},"#009955":{"lch":[55.5199214835444792,63.9084670453141044,140.311824754994205],"luv":[55.5199214835444792,-49.1795700812718,40.8125231602173173],"rgb":[0,0.6,0.333333333333333315],"xyz":[0.130302522872873944,0.234372751899106779,0.124317080318112519],"hpluv":[140.311824754994205,146.065915008593578,55.5199214835444792],"hsluv":[140.311824754994205,99.999999999991033,55.5199214835444792]},"#009966":{"lch":[55.8272121342916847,56.7002292022714229,147.995210176352344],"luv":[55.8272121342916847,-48.0820094218291771,30.0505634148387166],"rgb":[0,0.6,0.4],"xyz":[0.137887518646382129,0.23740675020851007,0.164264724725256495],"hpluv":[147.995210176352344,128.877825594911201,55.8272121342916847],"hsluv":[147.995210176352344,99.9999999999911893,55.8272121342916847]},"#009977":{"lch":[56.2009899164422393,50.1460500452165832,159.138923639260469],"luv":[56.2009899164422393,-46.85880632633,17.8571723631952],"rgb":[0,0.6,0.466666666666666674],"xyz":[0.147201531666584035,0.241132355416590893,0.213318526631654404],"hpluv":[159.138923639260469,113.222330796973324,56.2009899164422393],"hsluv":[159.138923639260469,99.9999999999912887,56.2009899164422393]},"#009988":{"lch":[56.6430401241061077,45.7780774967248476,174.293450088826631],"luv":[56.6430401241061077,-45.5512109338885409,4.55187453172192669],"rgb":[0,0.6,0.533333333333333326],"xyz":[0.158341948768527863,0.245588522257368491,0.271991390035226543],"hpluv":[174.293450088826631,102.553461073272217,56.6430401241061077],"hsluv":[174.293450088826631,99.9999999999913456,56.6430401241061077]},"#009999":{"lch":[57.1543844255405133,45.2182256610376498,192.177050630061132],"luv":[57.1543844255405133,-44.2008363998384866,-9.53802880511673301],"rgb":[0,0.6,0.6],"xyz":[0.171398912877600562,0.250811307900997649,0.340758067676344478],"hpluv":[192.177050630061132,100.392967527320849,57.1543844255405133],"hsluv":[192.177050630061132,99.9999999999914877,57.1543844255405133]},"#0099aa":{"lch":[57.7353441317496,49.1617433665336065,209.362441333496832],"luv":[57.7353441317496,-42.8462007667450138,-24.1056028900479831],"rgb":[0,0.6,0.66666666666666663],"xyz":[0.186456605842853118,0.256834385087098771,0.420061917293343079],"hpluv":[209.362441333496832,108.050017888493571,57.7353441317496],"hsluv":[209.362441333496832,99.9999999999915872,57.7353441317496]},"#0099bb":{"lch":[58.3856036871333686,56.8846197873520509,223.121526867409756],"luv":[58.3856036871333686,-41.5203973647636531,-38.8833713947714656],"rgb":[0,0.6,0.733333333333333282],"xyz":[0.203594197950189765,0.26368942193003353,0.51031990239198477],"hpluv":[223.121526867409756,123.631292939903787,58.3856036871333686],"hsluv":[223.121526867409756,99.9999999999917151,58.3856036871333686]},"#0099cc":{"lch":[59.1042769117952531,67.0724902608298237,233.123241992210865],"luv":[59.1042769117952531,-40.2499186561976856,-53.6531732328907225],"rgb":[0,0.6,0.8],"xyz":[0.222886573393911491,0.271406372107522331,0.611926413062254926],"hpluv":[233.123241992210865,144.000781521109104,59.1042769117952531],"hsluv":[233.123241992210865,99.9999999999918572,59.1042769117952531]},"#0099dd":{"lch":[59.889976295668248,78.6325369214165448,240.220044999287609],"luv":[59.889976295668248,-39.0544489896310338,-68.2482664747921888],"rgb":[0,0.6,0.866666666666666696],"xyz":[0.244404898944545035,0.280013702327775837,0.725256260962260924],"hpluv":[240.220044999287609,166.60478591483178,59.889976295668248],"hsluv":[240.220044999287609,99.9999999999918288,59.889976295668248]},"#0099ee":{"lch":[60.7408843834734853,90.8542474807105549,245.312254820906361],"luv":[60.7408843834734853,-37.9473430473261288,-82.5499451298106521],"rgb":[0,0.6,0.933333333333333348],"xyz":[0.2682170792107198,0.289538574434245854,0.850667077030784147],"hpluv":[245.312254820906361,189.803165481490907,60.7408843834734853],"hsluv":[245.312254820906361,99.9999999999919424,60.7408843834734853]},"#0099ff":{"lch":[61.6548256470178444,103.309645725501895,249.051296659176671],"luv":[61.6548256470178444,-36.936501733242423,-96.4809708680364082],"rgb":[0,0.6,1],"xyz":[0.294388127620551165,0.300006993798178578,0.988501265322566],"hpluv":[249.051296659176671,212.624411607996194,61.6548256470178444],"hsluv":[249.051296659176671,99.9999999999986926,61.6548256470178444]},"#44aa00":{"lch":[61.6346835386869714,87.655425968627469,122.331376925101353],"luv":[61.6346835386869714,-46.8794507107122556,74.0661245293922],"rgb":[0.266666666666666663,0.66666666666666663,0],"xyz":[0.167579386406696784,0.29977360690638849,0.0490310792412290836],"hpluv":[122.331376925101353,180.464989524422549,61.6346835386869714],"hsluv":[122.331376925101353,100.00000000000226,61.6346835386869714]},"#44aa11":{"lch":[61.6696010074672927,86.3319887480293175,122.80087722620695],"luv":[61.6696010074672927,-46.7678581574140253,72.5670705248452492],"rgb":[0.266666666666666663,0.66666666666666663,0.0666666666666666657],"xyz":[0.168591051906333916,0.300178273106243343,0.0543591842059847133],"hpluv":[122.80087722620695,177.639660035048053,61.6696010074672927],"hsluv":[122.80087722620695,97.7717273205757778,61.6696010074672927]},"#44aa22":{"lch":[61.7342457720645541,83.9245562706402097,123.699102784065559],"luv":[61.7342457720645541,-46.5639790081192,69.8221097085679],"rgb":[0.266666666666666663,0.66666666666666663,0.133333333333333331],"xyz":[0.170466410044810918,0.300928416361634143,0.0642360704019639128],"hpluv":[123.699102784065559,172.505216938407472,61.7342457720645541],"hsluv":[123.699102784065559,93.6968530578778882,61.7342457720645541]},"#44aa33":{"lch":[61.8404488118369784,80.0901636841586111,125.261260913590505],"luv":[61.8404488118369784,-46.2365065380172098,65.3958697634278],"rgb":[0.266666666666666663,0.66666666666666663,0.2],"xyz":[0.173554160777268651,0.30216351665461727,0.080498224259575063],"hpluv":[125.261260913590505,164.340980941234676,61.8404488118369784],"hsluv":[125.261260913590505,87.1410828818075913,61.8404488118369784]},"#44aa44":{"lch":[61.9932720073472,74.8379874444649573,127.71501294923992],"luv":[61.9932720073472,-45.7809668449718501,59.2015830825281526],"rgb":[0.266666666666666663,0.66666666666666663,0.266666666666666663],"xyz":[0.178012156021487539,0.303946714752304836,0.103976999212461702],"hpluv":[127.71501294923992,153.185220958209158,61.9932720073472],"hsluv":[127.71501294923992,77.998580561819125,61.9932720073472]},"#44aa55":{"lch":[62.1967362447927883,68.3415808764166854,131.40733952388851],"luv":[62.1967362447927883,-45.2016647888952292,51.2579864704043118],"rgb":[0.266666666666666663,0.66666666666666663,0.333333333333333315],"xyz":[0.183974570060849552,0.306331680368049697,0.135379046486435617],"hpluv":[131.40733952388851,139.430168232009549,62.1967362447927883],"hsluv":[131.40733952388851,78.575415606985473,62.1967362447927883]},"#44aa66":{"lch":[62.4540496972210377,60.9745995384144,136.885019691281769],"luv":[62.4540496972210377,-44.510458035487396,41.6739836629655684],"rgb":[0.266666666666666663,0.66666666666666663,0.4],"xyz":[0.191559565834357709,0.309365678677453,0.175326690893579551],"hpluv":[136.885019691281769,123.88755407265441,62.4540496972210377],"hsluv":[136.885019691281769,79.2669246679894854,62.4540496972210377]},"#44aa77":{"lch":[62.7677273349888196,53.3862032124370103,144.988299459670287],"luv":[62.7677273349888196,-43.7251633677153,30.629998072253084],"rgb":[0.266666666666666663,0.66666666666666663,0.466666666666666674],"xyz":[0.200873578854559642,0.313091283885533811,0.22438049279997746],"hpluv":[144.988299459670287,107.927460302012818,62.7677273349888196],"hsluv":[144.988299459670287,80.0573307147673603,62.7677273349888196]},"#44aa88":{"lch":[63.1396635168947142,46.6323085826041606,156.819414130132799],"luv":[63.1396635168947142,-42.8676249510766354,18.3558964585511255],"rgb":[0.266666666666666663,0.66666666666666663,0.533333333333333326],"xyz":[0.212013995956503443,0.317547450726311409,0.283053356203549655],"hpluv":[156.819414130132799,93.7182088199909,63.1396635168947142],"hsluv":[156.819414130132799,80.9270306732118456,63.1396635168947142]},"#44aa99":{"lch":[63.5711832083111,42.2713825005608328,173.059905511526438],"luv":[63.5711832083111,-41.9616613239929848,5.10771548142927578],"rgb":[0.266666666666666663,0.66666666666666663,0.6],"xyz":[0.22507096006557617,0.322770236369940566,0.351820033844667535],"hpluv":[173.059905511526438,84.3772726773670598,63.5711832083111],"hsluv":[173.059905511526438,81.8544924931781281,63.5711832083111]},"#44aaaa":{"lch":[64.0630839897801536,41.9755778813547948,192.177050630061],"luv":[64.0630839897801536,-41.03114670244819,-8.85404646225986802],"rgb":[0.266666666666666663,0.66666666666666663,0.66666666666666663],"xyz":[0.240128653030828698,0.328793313556041689,0.431123883461666135],"hpluv":[192.177050630061,83.1434743556685731,64.0630839897801536],"hsluv":[192.177050630061,82.8180264051213868,64.0630839897801536]},"#44aabb":{"lch":[64.6156750410269893,46.3675567205894481,210.140689328835606],"luv":[64.6156750410269893,-40.0984334486196374,-23.2823098339426302],"rgb":[0.266666666666666663,0.66666666666666663,0.733333333333333282],"xyz":[0.257266245138165373,0.335648350398976447,0.521381868560307882],"hpluv":[210.140689328835606,91.0574853549996135,64.6156750410269893],"hsluv":[210.140689328835606,83.7972417316187,64.6156750410269893]},"#44aacc":{"lch":[65.2288162125558131,54.5527176111565382,224.088695054270687],"luv":[65.2288162125558131,-39.1832309762682058,-37.956203827872514],"rgb":[0.266666666666666663,0.66666666666666663,0.8],"xyz":[0.276558620581887071,0.343365300576465249,0.622988379230578],"hpluv":[224.088695054270687,106.124637545316332,65.2288162125558131],"hsluv":[224.088695054270687,84.7740840933985424,65.2288162125558131]},"#44aadd":{"lch":[65.9019585504415772,65.1392687949415574,233.984713087438024],"luv":[65.9019585504415772,-38.3019605797345406,-52.6885581021924381],"rgb":[0.266666666666666663,0.66666666666666663,0.866666666666666696],"xyz":[0.29807694613252067,0.351972630796718755,0.736318227130584],"hpluv":[233.984713087438024,125.424934833645892,65.9019585504415772],"hsluv":[233.984713087438024,85.7334354760652246,65.9019585504415772]},"#44aaee":{"lch":[66.634186587349916,77.0517400481588,240.904642753345257],"luv":[66.634186587349916,-37.4675316968449081,-67.3287064556787698],"rgb":[0.266666666666666663,0.66666666666666663,0.933333333333333348],"xyz":[0.32188912639869538,0.361497502903188772,0.861729043199107259],"hpluv":[240.904642753345257,146.73194579095005,66.634186587349916],"hsluv":[240.904642753345257,86.6633273618325717,66.634186587349916]},"#44aaff":{"lch":[67.4242620727880677,89.6163291450103827,245.832487512483851],"luv":[67.4242620727880677,-36.6894434071527229,-81.7616731231704534],"rgb":[0.266666666666666663,0.66666666666666663,1],"xyz":[0.348060174808526801,0.371965922267121496,0.999563231490889104],"hpluv":[245.832487512483851,168.659292786049974,67.4242620727880677],"hsluv":[245.832487512483851,99.9999999999982094,67.4242620727880677]},"#44bb00":{"lch":[67.1028050092269126,96.9162111575721497,123.392710981560953],"luv":[67.1028050092269126,-53.3402140179528601,80.9170782570533476],"rgb":[0.266666666666666663,0.733333333333333282,0],"xyz":[0.201533884315295564,0.367682602723587049,0.0603492452107617],"hpluv":[123.392710981560953,183.271561122122193,67.1028050092269126],"hsluv":[123.392710981560953,100.00000000000226,67.1028050092269126]},"#44bb11":{"lch":[67.1332810832727347,95.7453830613523138,123.778355355208646],"luv":[67.1332810832727347,-53.2326764896654581,79.5830417319651673],"rgb":[0.266666666666666663,0.733333333333333282,0.0666666666666666657],"xyz":[0.202545549814932696,0.368087268923441902,0.0656773501755173322],"hpluv":[123.778355355208646,180.975295348072393,67.1332810832727347],"hsluv":[123.778355355208646,98.1806384027583334,67.1332810832727347]},"#44bb22":{"lch":[67.1897165718252438,93.6085464076245728,124.511290753684577],"luv":[67.1897165718252438,-53.03566579282883,77.1348048189646391],"rgb":[0.266666666666666663,0.733333333333333282,0.133333333333333331],"xyz":[0.204420907953409697,0.368837412178832702,0.0755542363714965248],"hpluv":[124.511290753684577,176.787689053856155,67.1897165718252438],"hsluv":[124.511290753684577,94.8452524510698538,67.1897165718252438]},"#44bb33":{"lch":[67.2824703298360873,90.1849869824975627,125.771296969252688],"luv":[67.2824703298360873,-52.7177504009765485,73.1721987485246501],"rgb":[0.266666666666666663,0.733333333333333282,0.2],"xyz":[0.207508658685867431,0.370072512471815829,0.091816390229107675],"hpluv":[125.771296969252688,170.087205132927863,67.2824703298360873],"hsluv":[125.771296969252688,89.4565739304913308,67.2824703298360873]},"#44bb44":{"lch":[67.4160218575577375,85.4495691526201284,127.715012949240077],"luv":[67.4160218575577375,-52.2724357759646807,67.5960156105081182],"rgb":[0.266666666666666663,0.733333333333333282,0.266666666666666663],"xyz":[0.211966653930086318,0.371855710569503395,0.115295165181994314],"hpluv":[127.715012949240077,160.837043785954393,67.4160218575577375],"hsluv":[127.715012949240077,81.8947222100884318,67.4160218575577375]},"#44bb55":{"lch":[67.5939766620447813,79.5000401119176,130.566112214394138],"luv":[67.5939766620447813,-51.7008659583180759,60.3926886051327756],"rgb":[0.266666666666666663,0.733333333333333282,0.333333333333333315],"xyz":[0.217929067969448331,0.374240676185248256,0.146697212455968229],"hpluv":[130.566112214394138,149.244617358403957,67.5939766620447813],"hsluv":[130.566112214394138,82.2871698148834412,67.5939766620447813]},"#44bb66":{"lch":[67.8192698910356313,72.5749222232247,134.657948179728322],"luv":[67.8192698910356313,-51.0109407747319139,51.6236695420221778],"rgb":[0.266666666666666663,0.733333333333333282,0.4],"xyz":[0.225514063742956489,0.377274674494651574,0.186644856863112191],"hpluv":[134.657948179728322,135.791565604529097,67.8192698910356313],"hsluv":[134.657948179728322,82.7624878443234451,67.8192698910356313]},"#44bb77":{"lch":[68.0942730594189527,65.0902087481164671,140.487577627549143],"luv":[68.0942730594189527,-50.2162275054035163,41.4133525568618168],"rgb":[0.266666666666666663,0.733333333333333282,0.466666666666666674],"xyz":[0.234828076763158422,0.38100027970273237,0.2356986587695101],"hpluv":[140.487577627549143,121.295420087000366,68.0942730594189527],"hsluv":[140.487577627549143,83.3123735080707206,68.0942730594189527]},"#44bb88":{"lch":[68.4208577329380461,57.7058903955470939,148.752185803016886],"luv":[68.4208577329380461,-49.3345927236295907,29.9343907092910229],"rgb":[0.266666666666666663,0.733333333333333282,0.533333333333333326],"xyz":[0.245968493865102222,0.385456446543509967,0.294371522173082267],"hpluv":[148.752185803016886,107.021481540968693,68.4208577329380461],"hsluv":[148.752185803016886,83.9257037652282776,68.4208577329380461]},"#44bb99":{"lch":[68.8004384509552267,51.4171630403873507,160.23039633414129],"luv":[68.8004384509552267,-48.3866530594068536,17.3912754227620781],"rgb":[0.266666666666666663,0.733333333333333282,0.6],"xyz":[0.259025457974175,0.390679232187139125,0.363138199814200147],"hpluv":[160.23039633414129,94.8322885243955,68.8004384509552267],"hsluv":[160.23039633414129,84.5895330052649683,68.8004384509552267]},"#44bbaa":{"lch":[69.2340056114394571,47.5630236168925,175.171204236910796],"luv":[69.2340056114394571,-47.3942071553531576,4.003791165451279],"rgb":[0.266666666666666663,0.733333333333333282,0.66666666666666663],"xyz":[0.274083150939427478,0.396702309373240247,0.442442049431198747],"hpluv":[175.171204236910796,87.1744714415039113,69.2340056114394571],"hsluv":[175.171204236910796,85.2901010545764251,69.2340056114394571]},"#44bbbb":{"lch":[69.722153945093,47.4463312905994243,192.177050630061103],"luv":[69.722153945093,-46.3788106784417877,-10.0080104411700219],"rgb":[0.266666666666666663,0.733333333333333282,0.733333333333333282],"xyz":[0.291220743046764152,0.403557346216175,0.532700034529840494],"hpluv":[192.177050630061103,86.3517549054621156,69.722153945093],"hsluv":[192.177050630061103,86.0137488036252,69.722153945093]},"#44bbcc":{"lch":[70.265109629848,51.5238495855336254,208.311647260201596],"luv":[70.265109629848,-45.3606162241423405,-24.4360711219862],"rgb":[0.266666666666666663,0.733333333333333282,0.8],"xyz":[0.310513118490485907,0.411274296393663807,0.63430654520011065],"hpluv":[208.311647260201596,93.0481841100277478,70.265109629848],"hsluv":[208.311647260201596,86.7476639801695484,70.265109629848]},"#44bbdd":{"lch":[70.8627576511434683,59.1258582536945,221.390354970453018],"luv":[70.8627576511434683,-44.357542252504274,-39.0931651258026491],"rgb":[0.266666666666666663,0.733333333333333282,0.866666666666666696],"xyz":[0.332031444041119395,0.419881626613917314,0.747636393100116647],"hpluv":[221.390354970453018,105.876295000483495,70.8627576511434683],"hsluv":[221.390354970453018,87.4804162985680449,70.8627576511434683]},"#44bbee":{"lch":[71.5146701379092207,69.1277260896420813,231.126492355228834],"luv":[71.5146701379092207,-43.3847784652122499,-53.8182451576511269],"rgb":[0.266666666666666663,0.733333333333333282,0.933333333333333348],"xyz":[0.35584362430729416,0.429406498720387331,0.873047209168639871],"hpluv":[231.126492355228834,122.65816352572412,71.5146701379092207],"hsluv":[231.126492355228834,88.202277385306985,71.5146701379092207]},"#44bbff":{"lch":[72.2201358507708,80.5712163793027685,238.202407995552562],"luv":[72.2201358507708,-42.4545914747530801,-68.4786723845625573],"rgb":[0.266666666666666663,0.733333333333333282,1],"xyz":[0.382014672717125525,0.439874918084320055,1.01088139746042183],"hpluv":[238.202407995552562,141.566646923483631,72.2201358507708],"hsluv":[238.202407995552562,99.9999999999976694,72.2201358507708]},"#44cc00":{"lch":[72.503692055952385,105.959797206167082,124.178253965335855],"luv":[72.503692055952385,-59.5249745926526543,87.6598883396283384],"rgb":[0.266666666666666663,0.8,0],"xyz":[0.239757627624912484,0.444130089342821943,0.0730904929806336506],"hpluv":[124.178253965335855,185.447217969921951,72.503692055952385],"hsluv":[124.178253965335855,100.000000000002359,72.503692055952385]},"#44cc11":{"lch":[72.5305637479271752,104.914943747871533,124.498982817725434],"luv":[72.5305637479271752,-59.4229434801529877,86.4642076802510928],"rgb":[0.266666666666666663,0.8,0.0666666666666666657],"xyz":[0.240769293124549616,0.444534755542676796,0.0784185979453892873],"hpluv":[124.498982817725434,183.55052269402043,72.5305637479271752],"hsluv":[124.498982817725434,98.4921383302381628,72.5305637479271752]},"#44cc22":{"lch":[72.5803335982668,103.003123626508128,125.105641856618192],"luv":[72.5803335982668,-59.2356349500081265,84.2661440252673088],"rgb":[0.266666666666666663,0.8,0.133333333333333331],"xyz":[0.242644651263026617,0.445284898798067597,0.0882954841413684799],"hpluv":[125.105641856618192,180.082189217111164,72.5803335982668],"hsluv":[125.105641856618192,95.7225658430248387,72.5803335982668]},"#44cc33":{"lch":[72.6621573160580283,99.9260617146724144,126.139923542820412],"luv":[72.6621573160580283,-58.9323161564504758,80.6982027200153],"rgb":[0.266666666666666663,0.8,0.2],"xyz":[0.245732401995484351,0.446519999091050723,0.10455763799897963],"hpluv":[126.139923542820412,174.505777504147545,72.6621573160580283],"hsluv":[126.139923542820412,91.233607132201584,72.6621573160580283]},"#44cc44":{"lch":[72.780026014363628,95.6382843785450518,127.715012949240148],"luv":[72.780026014363628,-58.5052227586060383,75.6559339961998],"rgb":[0.266666666666666663,0.8,0.266666666666666663],"xyz":[0.250190397239703266,0.448303197188738289,0.128036412951866269],"hpluv":[127.715012949240148,166.747333178852926,72.780026014363628],"hsluv":[127.715012949240148,84.9041129363740339,72.780026014363628]},"#44cc55":{"lch":[72.9371837422115732,90.1892610669535912,129.983942971096695],"luv":[72.9371837422115732,-57.9531752136704,69.1052262456084492],"rgb":[0.266666666666666663,0.8,0.333333333333333315],"xyz":[0.256152811279065251,0.45068816280448315,0.159438460225840184],"hpluv":[129.983942971096695,156.908028239254975,72.9371837422115732],"hsluv":[129.983942971096695,85.1779272269293557,72.9371837422115732]},"#44cc66":{"lch":[73.1363103507417236,83.732792729713168,133.164212643981188],"luv":[73.1363103507417236,-57.28090462038152,61.074368962666],"rgb":[0.266666666666666663,0.8,0.4],"xyz":[0.263737807052573436,0.453722161113886469,0.199386104632984118],"hpluv":[133.164212643981188,145.278670258241675,73.1363103507417236],"hsluv":[133.164212643981188,85.5122241677148764,73.1363103507417236]},"#44cc77":{"lch":[73.3796177536812309,76.5464624175537,137.569101073773794],"luv":[73.3796177536812309,-56.4983001604517625,51.6459387330839],"rgb":[0.266666666666666663,0.8,0.466666666666666674],"xyz":[0.273051820072775342,0.457447766321967264,0.248439906539382027],"hpluv":[137.569101073773794,132.369828459728325,73.3796177536812309],"hsluv":[137.569101073773794,85.9026534082125153,73.3796177536812309]},"#44cc88":{"lch":[73.6689069899719442,69.0664605730878378,143.639563525196422],"luv":[73.6689069899719442,-55.6194534086204087,40.9469459010095278],"rgb":[0.266666666666666663,0.8,0.533333333333333326],"xyz":[0.284192237174719142,0.461903933162744862,0.307112769942954222],"hpluv":[143.639563525196422,118.965846887396481,73.6689069899719442],"hsluv":[143.639563525196422,86.3428682965135863,73.6689069899719442]},"#44cc99":{"lch":[74.0056056952011,61.9424628035573548,151.940118655705163],"luv":[74.0056056952011,-54.661526046940331,29.1373689338922652],"rgb":[0.266666666666666663,0.8,0.6],"xyz":[0.297249201283791842,0.467126718806374,0.375879447584072102],"hpluv":[151.940118655705163,106.209454381597496,74.0056056952011],"hsluv":[151.940118655705163,86.8250568088678,74.0056056952011]},"#44ccaa":{"lch":[74.3907954563299256,56.0938540204294327,163.002493202880316],"luv":[74.3907954563299256,-53.6435329798001206,16.3979214631106132],"rgb":[0.266666666666666663,0.8,0.66666666666666663],"xyz":[0.312306894249044398,0.473149795992475142,0.455183297201070702],"hpluv":[163.002493202880316,95.6831366692641581,74.3907954563299256],"hsluv":[163.002493202880316,87.3405106459777727,74.3907954563299256]},"#44ccbb":{"lch":[74.8252340948823331,52.6660587979454959,176.823724318960615],"luv":[74.8252340948823331,-52.5851528012588929,2.91812511330579927],"rgb":[0.266666666666666663,0.8,0.733333333333333282],"xyz":[0.329444486356381072,0.4800048328354099,0.545441282299712449],"hpluv":[176.823724318960615,89.3145186414393493,74.8252340948823331],"hsluv":[176.823724318960615,87.8801809558037235,74.8252340948823331]},"#44cccc":{"lch":[75.3093757141467393,52.6911947618550442,192.177050630061103],"luv":[75.3093757141467393,-51.5056671360628826,-11.1143267137884898],"rgb":[0.266666666666666663,0.8,0.8],"xyz":[0.348736861800102771,0.487721783012898702,0.647047792969982605],"hpluv":[192.177050630061103,88.7826952764381758,75.3093757141467393],"hsluv":[192.177050630061103,88.4351737608208168,75.3093757141467393]},"#44ccdd":{"lch":[75.8433901234223669,56.5143539908888,206.846753698803184],"luv":[75.8433901234223669,-50.423101427505074,-25.5222069860548118],"rgb":[0.266666666666666663,0.8,0.866666666666666696],"xyz":[0.37025518735073637,0.496329113233152208,0.760377640869988602],"hpluv":[206.846753698803184,94.5540981724771257,75.8433901234223669],"hsluv":[206.846753698803184,88.9971515077065334,75.8433901234223669]},"#44ccee":{"lch":[76.4271825186181104,63.6191993934700264,219.125479385679029],"luv":[76.4271825186181104,-49.3536035237008,-40.1450414209705926],"rgb":[0.266666666666666663,0.8,0.933333333333333348],"xyz":[0.39406736761691108,0.505853985339622225,0.885788456938511826],"hpluv":[219.125479385679029,108.248092211119214,76.4271825186181104],"hsluv":[219.125479385679029,89.5586239337052,76.4271825186181104]},"#44ccff":{"lch":[77.0604138316104752,73.087044724203011,228.6232677289035],"luv":[77.0604138316104752,-48.3110621928290342,-54.8430248647743426],"rgb":[0.266666666666666663,0.8,1],"xyz":[0.420238416026742501,0.516322404703555,1.02362264523029367],"hpluv":[228.6232677289035,128.526456918779161,77.0604138316104752],"hsluv":[228.6232677289035,99.9999999999969731,77.0604138316104752]},"#44dd00":{"lch":[77.8394471675691193,114.806757868746558,124.774603647715026],"luv":[77.8394471675691193,-65.4799812263264869,94.3025116894186],"rgb":[0.266666666666666663,0.866666666666666696,0],"xyz":[0.282391618172087688,0.529398070437173462,0.0873018231630249691],"hpluv":[124.774603647715026,210.465861771712326,77.8394471675691193],"hsluv":[124.774603647715026,100.000000000002174,77.8394471675691193]},"#44dd11":{"lch":[77.8633510332093692,113.867279553553601,125.04447057905567],"luv":[77.8633510332093692,-65.3839645843072503,93.2238946202376724],"rgb":[0.266666666666666663,0.866666666666666696,0.0666666666666666657],"xyz":[0.283403283671724793,0.529802736637028371,0.0926299281277806],"hpluv":[125.04447057905567,209.014039294370775,77.8633510332093692],"hsluv":[125.04447057905567,98.7339197526847272,77.8633510332093692]},"#44dd22":{"lch":[77.9076302523257738,112.144788460185168,125.553114236235714],"luv":[77.9076302523257738,-65.2074178024426914,91.2384033306006472],"rgb":[0.266666666666666663,0.866666666666666696,0.133333333333333331],"xyz":[0.28527864181020185,0.530552879892419171,0.102506814323759798],"hpluv":[125.553114236235714,206.347170822894697,77.9076302523257738],"hsluv":[125.553114236235714,96.4050109160080382,77.9076302523257738]},"#44dd33":{"lch":[77.9804445180848802,109.362510567887881,126.41495586461761],"luv":[77.9804445180848802,-64.9207541848309546,88.0082632130877869],"rgb":[0.266666666666666663,0.866666666666666696,0.2],"xyz":[0.288366392542659555,0.531787980185402298,0.118768968181370949],"hpluv":[126.41495586461761,202.025685311970193,77.9804445180848802],"hsluv":[126.41495586461761,92.6208241654068729,77.9804445180848802]},"#44dd44":{"lch":[78.0853727898892345,105.463232003770898,127.715012949240119],"luv":[78.0853727898892345,-64.5154806081751389,83.428089194092081],"rgb":[0.266666666666666663,0.866666666666666696,0.266666666666666663],"xyz":[0.292824387786878471,0.533571178283089864,0.142247743134257587],"hpluv":[127.715012949240119,195.940425098340825,78.0853727898892345],"hsluv":[127.715012949240119,87.2650912217990395,78.0853727898892345]},"#44dd55":{"lch":[78.2253459168282888,100.46502238820301,129.562971792001804],"luv":[78.2253459168282888,-63.9887749627021662,77.4510000079714303],"rgb":[0.266666666666666663,0.866666666666666696,0.333333333333333315],"xyz":[0.298786801826240456,0.535956143898834614,0.173649790408231475],"hpluv":[129.562971792001804,188.090878204767051,78.2253459168282888],"hsluv":[129.562971792001804,87.4605090914447,78.2253459168282888]},"#44dd66":{"lch":[78.4028117957757615,94.4661355921174,132.108441056441876],"luv":[78.4028117957757615,-63.3429373931831776,70.0822592109557],"rgb":[0.266666666666666663,0.866666666666666696,0.4],"xyz":[0.306371797599748641,0.538990142208237932,0.213597434815375464],"hpluv":[132.108441056441876,178.59774401526758,78.4028117957757615],"hsluv":[132.108441056441876,87.7006053461373654,78.4028117957757615]},"#44dd77":{"lch":[78.6198227824069278,87.656024220764337,135.559861003190832],"luv":[78.6198227824069278,-62.584854150819659,61.3735660778470375],"rgb":[0.266666666666666663,0.866666666666666696,0.466666666666666674],"xyz":[0.315685810619950546,0.542715747416318783,0.262651236721773373],"hpluv":[135.559861003190832,167.73117312681623,78.6198227824069278],"hsluv":[135.559861003190832,87.9831439767702221,78.6198227824069278]},"#44dd88":{"lch":[78.8780874692010201,80.3346414261830404,140.206181380764804],"luv":[78.8780874692010201,-61.7253288138421823,51.416324216113189],"rgb":[0.266666666666666663,0.866666666666666696,0.533333333333333326],"xyz":[0.326826227721894347,0.547171914257096326,0.321324100125345513],"hpluv":[140.206181380764804,155.961921377148798,78.8780874692010201],"hsluv":[140.206181380764804,88.3044935606717729,78.8780874692010201]},"#44dd99":{"lch":[79.1790042342924,72.9438114093828602,146.430978952194238],"luv":[79.1790042342924,-60.7782665628728651,40.3336328210091182],"rgb":[0.266666666666666663,0.866666666666666696,0.6],"xyz":[0.339883191830967046,0.552394699900725539,0.390090777766463448],"hpluv":[146.430978952194238,144.047423549525234,79.1790042342924],"hsluv":[146.430978952194238,88.6599152631773109,79.1790042342924]},"#44ddaa":{"lch":[79.5236849812282,66.109750002698334,154.681927012774025],"luv":[79.5236849812282,-59.7597565852220498,28.2713731236083063],"rgb":[0.266666666666666663,0.866666666666666696,0.66666666666666663],"xyz":[0.354940884796219602,0.558417777086826606,0.469394627383462049],"hpluv":[154.681927012774025,133.15854000611057,79.5236849812282],"hsluv":[154.681927012774025,89.0438856180527267,79.5236849812282]},"#44ddbb":{"lch":[79.9129735834354733,60.6712530747818448,165.306614809553963],"luv":[79.9129735834354733,-58.687123678359157,15.3890371376899697],"rgb":[0.266666666666666663,0.866666666666666696,0.733333333333333282],"xyz":[0.372078476903556277,0.565272813929761364,0.55965261248210374],"hpluv":[165.306614809553963,125.005338667233715,79.9129735834354733],"hsluv":[165.306614809553963,89.4504295982059574,79.9129735834354733]},"#44ddcc":{"lch":[80.3474616163736783,57.6077690930220925,178.158710030216184],"luv":[80.3474616163736783,-57.5780241737671616,1.85099760130491053],"rgb":[0.266666666666666663,0.866666666666666696,0.8],"xyz":[0.391370852347278,0.572989764107250221,0.661259123152373895],"hpluv":[178.158710030216184,121.786592068950441,80.3474616163736783],"hsluv":[178.158710030216184,89.8734379183952399,80.3474616163736783]},"#44dddd":{"lch":[80.8275029051271758,57.7489755309586,192.177050630061132],"luv":[80.8275029051271758,-56.4496501662069505,-12.1811810177875159],"rgb":[0.266666666666666663,0.866666666666666696,0.866666666666666696],"xyz":[0.412889177897911575,0.581597094327503727,0.774588971052379893],"hpluv":[192.177050630061132,125.674721736272474,80.8275029051271758],"hsluv":[192.177050630061132,90.3069463225795204,80.8275029051271758]},"#44ddee":{"lch":[81.3532277894993143,61.361693698739991,205.642938459455962],"luv":[81.3532277894993143,-55.3180860243278758,-26.5549771640468073],"rgb":[0.266666666666666663,0.866666666666666696,0.933333333333333348],"xyz":[0.436701358164086284,0.591121966433973745,0.899999787120903116],"hpluv":[205.642938459455962,137.939634766348263,81.3532277894993143],"hsluv":[205.642938459455962,90.7453615563817806,81.3532277894993143]},"#44ddff":{"lch":[81.9245576129038113,68.0382655080336463,217.195369709248553],"luv":[81.9245576129038113,-54.1978382178125813,-41.1314953029609],"rgb":[0.266666666666666663,0.866666666666666696,1],"xyz":[0.462872406573917705,0.601590385797906468,1.03783397541268507],"hpluv":[217.195369709248553,158.576151866946,81.9245576129038113],"hsluv":[217.195369709248553,99.9999999999957367,81.9245576129038113]},"#44ee00":{"lch":[83.112739541513335,123.476763986331008,125.23710114083579],"luv":[83.112739541513335,-71.241317589729718,100.852297507867192],"rgb":[0.266666666666666663,0.933333333333333348,0],"xyz":[0.329570394512602505,0.623755623118204428,0.103028081943196154],"hpluv":[125.23710114083579,311.240798427125753,83.112739541513335],"hsluv":[125.23710114083579,100.000000000002402,83.112739541513335]},"#44ee11":{"lch":[83.1341682891089135,122.626446998812852,125.466600012922257],"luv":[83.1341682891089135,-71.1513320296005674,99.8735873690646656],"rgb":[0.266666666666666663,0.933333333333333348,0.0666666666666666657],"xyz":[0.330582060012239609,0.624160289318059336,0.10835618690795179],"hpluv":[125.466600012922257,309.547675363147619,83.1341682891089135],"hsluv":[125.466600012922257,98.9247180409442279,83.1341682891089135]},"#44ee22":{"lch":[83.1738669889620184,121.064908518289769,125.897986234483838],"luv":[83.1738669889620184,-70.9856689153146192,98.0701120789979],"rgb":[0.266666666666666663,0.933333333333333348,0.133333333333333331],"xyz":[0.332457418150716666,0.624910432573450136,0.118233073103930983],"hpluv":[125.897986234483838,306.432309176179558,83.1738669889620184],"hsluv":[125.897986234483838,96.9444732717922,83.1738669889620184]},"#44ee33":{"lch":[83.2391611795664517,118.535427876706891,126.625494433478437],"luv":[83.2391611795664517,-70.7161072615436694,95.1308563805026921],"rgb":[0.266666666666666663,0.933333333333333348,0.2],"xyz":[0.335545168883174372,0.626145532866433263,0.134495226961542119],"hpluv":[126.625494433478437,301.369067751793693,83.2391611795664517],"hsluv":[126.625494433478437,93.7204451717133651,83.2391611795664517]},"#44ee44":{"lch":[83.3332795320092714,114.974418092600288,127.715012949240275],"luv":[83.3332795320092714,-70.3337997514069855,90.9520391649414],"rgb":[0.266666666666666663,0.933333333333333348,0.266666666666666663],"xyz":[0.340003164127393287,0.627928730964120829,0.157974001914428758],"hpluv":[127.715012949240275,294.205765091151079,83.3332795320092714],"hsluv":[127.715012949240275,89.1439564147074321,83.3332795320092714]},"#44ee55":{"lch":[83.4588814464859183,110.379279114867671,129.248151137931956],"luv":[83.4588814464859183,-69.8347997913980123,85.479155330485213],"rgb":[0.266666666666666663,0.933333333333333348,0.333333333333333315],"xyz":[0.345965578166755272,0.630313696579865579,0.189376049188402673],"hpluv":[129.248151137931956,284.901382538762221,83.4588814464859183],"hsluv":[129.248151137931956,89.2862876088480419,83.4588814464859183]},"#44ee66":{"lch":[83.6182069813856,104.810737833959209,131.33226272314829],"luv":[83.6182069813856,-69.2195888954281,78.7015837099029],"rgb":[0.266666666666666663,0.933333333333333348,0.4],"xyz":[0.353550573940263457,0.633347694889268897,0.229323693595546635],"hpluv":[131.33226272314829,273.53562680177663,83.6182069813856],"hsluv":[131.33226272314829,89.462046779026835,83.6182069813856]},"#44ee77":{"lch":[83.8131566406418642,98.3993759238413759,134.112401675041724],"luv":[83.8131566406418642,-68.4926783274834889,70.6483559449852123],"rgb":[0.266666666666666663,0.933333333333333348,0.466666666666666674],"xyz":[0.362864586960465363,0.637073300097349748,0.278377495501944572],"hpluv":[134.112401675041724,260.333399951005049,83.8131566406418642],"hsluv":[134.112401675041724,89.6701381262035682,83.8131566406418642]},"#44ee88":{"lch":[84.045338735079568,91.3569443918750892,137.785521400150031],"luv":[84.045338735079568,-67.6621349200187439,61.3834406553211949],"rgb":[0.266666666666666663,0.933333333333333348,0.533333333333333326],"xyz":[0.374005004062409163,0.641529466938127291,0.337050358905516712],"hpluv":[137.785521400150031,245.709311488591595,84.045338735079568],"hsluv":[137.785521400150031,89.9084899734552323,84.045338735079568]},"#44ee99":{"lch":[84.3160998906388386,83.9946679616676306,142.613931790318958],"luv":[84.3160998906388386,-66.738995210465,51.0001055321292895],"rgb":[0.266666666666666663,0.933333333333333348,0.6],"xyz":[0.387061968171481863,0.646752252581756504,0.405817036546634591],"hpluv":[142.613931790318958,230.343048149298568,84.3160998906388386],"hsluv":[142.613931790318958,90.1742147406472725,84.3160998906388386]},"#44eeaa":{"lch":[84.626546237332235,76.7502660373389887,148.925817786423067],"luv":[84.626546237332235,-65.7365837009088239,39.6144531722415607],"rgb":[0.266666666666666663,0.933333333333333348,0.66666666666666663],"xyz":[0.402119661136734419,0.652775329767857571,0.485120886163633247],"hpluv":[148.925817786423067,215.298316825794302,84.626546237332235],"hsluv":[148.925817786423067,90.4637941794664897,84.626546237332235]},"#44eebb":{"lch":[84.9775593290447,70.218708007349818,157.069253127653155],"luv":[84.9775593290447,-64.6697768267590618,27.3584889823731068],"rgb":[0.266666666666666663,0.933333333333333348,0.733333333333333282],"xyz":[0.419257253244071093,0.659630366610792329,0.575378871262274938],"hpluv":[157.069253127653155,202.183693696822303,84.9775593290447],"hsluv":[157.069253127653155,90.7732788992861401,84.9775593290447]},"#44eecc":{"lch":[85.3698091329633826,65.1592886039499462,167.256585364158155],"luv":[85.3698091329633826,-63.5542641697608914,14.3731832665241086],"rgb":[0.266666666666666663,0.933333333333333348,0.8],"xyz":[0.438549628687792792,0.667347316788281186,0.676985381932545094],"hpluv":[167.256585364158155,193.290067765431047,85.3698091329633826],"hsluv":[167.256585364158155,91.0984884260964,85.3698091329633826]},"#44eedd":{"lch":[85.803765500838054,62.4110099600490216,179.263728091065275],"luv":[85.803765500838054,-62.4058570040272613,0.801982435119061421],"rgb":[0.266666666666666663,0.933333333333333348,0.866666666666666696],"xyz":[0.460067954238426391,0.675954647008534693,0.790315229832551092],"hpluv":[179.263728091065275,191.499842549603557,85.803765500838054],"hsluv":[179.263728091065275,91.4351983708881875,85.803765500838054]},"#44eeee":{"lch":[86.2797089909746546,62.6494691870182407,192.17705063006116],"luv":[86.2797089909746546,-61.2398849709435922,-13.2148582346064085],"rgb":[0.266666666666666663,0.933333333333333348,0.933333333333333348],"xyz":[0.4838801345046011,0.68547951911500471,0.915726045901074315],"hpluv":[192.17705063006116,199.700166684316315,86.2797089909746546],"hsluv":[192.17705063006116,91.7793037580661775,86.2797089909746546]},"#44eeff":{"lch":[86.7977415696122847,66.0848691943621134,204.633388514668525],"luv":[86.7977415696122847,-60.0707080483631159,-27.5448719547627086],"rgb":[0.266666666666666663,0.933333333333333348,1],"xyz":[0.510051182914432522,0.695947938478937433,1.05356023419285627],"hpluv":[204.633388514668525,219.870556477974674,86.7977415696122847],"hsluv":[204.633388514668525,99.9999999999936904,86.7977415696122847]},"#44ff00":{"lch":[88.3264513606833,131.987460278186802,125.602389702763816],"luv":[88.3264513606833,-76.837408418496949,107.315899745634312],"rgb":[0.266666666666666663,1,0],"xyz":[0.381422766942276337,0.727460367977553535,0.120312206086420265],"hpluv":[125.602389702763816,502.990651378155178,88.3264513606833],"hsluv":[125.602389702763816,100.000000000002331,88.3264513606833]},"#44ff11":{"lch":[88.3457924202418496,131.213348009411362,125.799444038401859],"luv":[88.3457924202418496,-76.7532223203665609,106.423143908077066],"rgb":[0.266666666666666663,1,0.0666666666666666657],"xyz":[0.382434432441913441,0.727865034177408443,0.125640311051175901],"hpluv":[125.799444038401859,500.950310911531346,88.3457924202418496],"hsluv":[125.799444038401859,99.9999999999917577,88.3457924202418496]},"#44ff22":{"lch":[88.3816266358799538,129.789896608084,126.169061428897493],"luv":[88.3816266358799538,-76.5980824061806089,104.776672180562713],"rgb":[0.266666666666666663,1,0.133333333333333331],"xyz":[0.384309790580390498,0.728615177432799244,0.13551719724715508],"hpluv":[126.169061428897493,497.190929074143924,88.3816266358799538],"hsluv":[126.169061428897493,99.9999999999915445,88.3816266358799538]},"#44ff33":{"lch":[88.4405736189592204,127.478814054469,126.790121098902588],"luv":[88.4405736189592204,-76.3452168675528071,102.089450454884],"rgb":[0.266666666666666663,1,0.2],"xyz":[0.387397541312848204,0.72985027772578237,0.15177935110476623],"hpluv":[126.790121098902588,491.06642972530841,88.4405736189592204],"hsluv":[126.790121098902588,99.9999999999916,88.4405736189592204]},"#44ff44":{"lch":[88.5255621746627099,124.213522374992053,127.715012949240247],"luv":[88.5255621746627099,-75.9856771103925,98.2607552122302],"rgb":[0.266666666666666663,1,0.266666666666666663],"xyz":[0.391855536557067119,0.731633475823469936,0.175258126057652869],"hpluv":[127.715012949240247,482.369437382385513,88.5255621746627099],"hsluv":[127.715012949240247,99.9999999999915872,88.5255621746627099]},"#44ff55":{"lch":[88.6390158335401,119.977757557138,129.006344472088813],"luv":[88.6390158335401,-75.5147735105559832,93.2318684253877734],"rgb":[0.266666666666666663,1,0.333333333333333315],"xyz":[0.397817950596429104,0.734018441439214686,0.206660173331626784],"hpluv":[129.006344472088813,471.012302242813519,88.6390158335401],"hsluv":[129.006344472088813,99.9999999999916724,88.6390158335401]},"#44ff66":{"lch":[88.7829895920100256,114.806361082699226,130.743859599912923],"luv":[88.7829895920100256,-74.9316504675157518,86.9813100801842438],"rgb":[0.266666666666666663,1,0.4],"xyz":[0.405402946369937289,0.737052439748618,0.246607817738770746],"hpluv":[130.743859599912923,457.03528250903878,88.7829895920100256],"hsluv":[130.743859599912923,99.999999999991374,88.7829895920100256]},"#44ff77":{"lch":[88.9592430558717524,108.789243130847709,133.032335199193767],"luv":[88.9592430558717524,-74.2389756563468239,79.5215311376676226],"rgb":[0.266666666666666663,1,0.466666666666666674],"xyz":[0.414716959390139195,0.740778044956698856,0.295661619645168683],"hpluv":[133.032335199193767,440.631455855157412,88.9592430558717524],"hsluv":[133.032335199193767,99.999999999991374,88.9592430558717524]},"#44ff88":{"lch":[89.1692840038805343,102.078340317719523,136.01097660716411],"luv":[89.1692840038805343,-73.4425963051882462,70.8955048643657904],"rgb":[0.266666666666666663,1,0.533333333333333326],"xyz":[0.425857376492083,0.745234211797476398,0.354334483048740823],"hpluv":[136.01097660716411,422.1930512248951,89.1692840038805343],"hsluv":[136.01097660716411,99.9999999999912177,89.1692840038805343]},"#44ff99":{"lch":[89.4143964062191117,94.8987049380611438,139.863519733640288],"luv":[89.4143964062191117,-72.5511163277215303,61.1727040314764139],"rgb":[0.266666666666666663,1,0.6],"xyz":[0.43891434060115575,0.750456997441105611,0.423101160689858702],"hpluv":[139.863519733640288,402.3908266141122,89.4143964062191117],"hsluv":[139.863519733640288,99.9999999999912,89.4143964062191117]},"#44ffaa":{"lch":[89.695659684091666,87.5652599917726207,144.82485232612342],"luv":[89.695659684091666,-71.5753927145854192,50.4444042008586777],"rgb":[0.266666666666666663,1,0.66666666666666663],"xyz":[0.453972033566408251,0.756480074627206678,0.502405010306857358],"hpluv":[144.82485232612342,382.304282325397367,89.695659684091666],"hsluv":[144.82485232612342,99.9999999999909335,89.695659684091666]},"#44ffbb":{"lch":[90.0139628620153616,80.5050918065685153,151.171682460988421],"luv":[90.0139628620153616,-70.527972915407986,38.8184858440859557],"rgb":[0.266666666666666663,1,0.733333333333333282],"xyz":[0.471109625673744925,0.763335111470141436,0.592662995405499],"hpluv":[151.171682460988421,363.621116250194575,90.0139628620153616],"hsluv":[151.171682460988421,99.999999999990834,90.0139628620153616]},"#44ffcc":{"lch":[90.3700157308713443,74.2777558665274853,159.169074542576084],"luv":[90.3700157308713443,-69.4225064855060481,26.4140229771485018],"rgb":[0.266666666666666663,1,0.8],"xyz":[0.490402001117466679,0.771052061647630294,0.694269506075769205],"hpluv":[159.169074542576084,348.900804743837909,90.3700157308713443],"hsluv":[159.169074542576084,99.9999999999905924,90.3700157308713443]},"#44ffdd":{"lch":[90.7643583149998,69.56728840997188,168.931262835156701],"luv":[90.7643583149998,-68.2731674827513899,13.3559806299794577],"rgb":[0.266666666666666663,1,0.866666666666666696],"xyz":[0.511920326668100167,0.7796593918678838,0.807599353975775203],"hpluv":[168.931262835156701,341.810509676969502,90.7643583149998],"hsluv":[168.931262835156701,99.9999999999902798,90.7643583149998]},"#44ffee":{"lch":[91.1973694573754869,67.0945142582603182,180.196137266844971],"luv":[91.1973694573754869,-67.094121132442,-0.229680249688981986],"rgb":[0.266666666666666663,1,0.933333333333333348],"xyz":[0.535732506934274877,0.789184263974353817,0.933010170044298426],"hpluv":[180.196137266844971,347.079488330816218,91.1973694573754869],"hsluv":[180.196137266844971,99.999999999989825,91.1973694573754869]},"#44ffff":{"lch":[91.6692750397398726,67.4158875874256,192.177050630061103],"luv":[91.6692750397398726,-65.8990611515587261,-14.2202545175369188],"rgb":[0.266666666666666663,1,1],"xyz":[0.561903555344106298,0.799652683338286541,1.07084435833608027],"hpluv":[192.177050630061103,369.886157390881351,91.6692750397398726],"hsluv":[192.177050630061103,99.9999999999897,91.6692750397398726]},"#33aa00":{"lch":[61.1785977172963129,90.1064171712311435,124.683940112874311],"luv":[61.1785977172963129,-51.2749716142469723,74.09482911373847],"rgb":[0.2,0.66666666666666663,0],"xyz":[0.157393059993970491,0.294521282349826385,0.0485535951906325494],"hpluv":[124.683940112874311,186.894073454811917,61.1785977172963129],"hsluv":[124.683940112874311,100.000000000002302,61.1785977172963129]},"#33aa11":{"lch":[61.2139288108167818,88.7849168066190089,125.171000261233829],"luv":[61.2139288108167818,-51.1417680861379793,72.5760360544852148],"rgb":[0.2,0.66666666666666663,0.0666666666666666657],"xyz":[0.158404725493607623,0.294925948549681238,0.0538817001553881791],"hpluv":[125.171000261233829,184.046797309440706,61.2139288108167818],"hsluv":[125.171000261233829,97.729263879491441,61.2139288108167818]},"#33aa22":{"lch":[61.2793378507832642,86.3832953662584657,126.10140218084841],"luv":[61.2793378507832642,-50.8984310656693708,69.7955831939783593],"rgb":[0.2,0.66666666666666663,0.133333333333333331],"xyz":[0.160280083632084625,0.295676091805072039,0.0637585863513673717],"hpluv":[126.10140218084841,178.877217305585333,61.2793378507832642],"hsluv":[126.10140218084841,93.5778090815338146,61.2793378507832642]},"#33aa33":{"lch":[61.3867923044640946,82.5646745763668548,127.715012949240119],"luv":[61.3867923044640946,-50.5076466968263134,65.3138814728533],"rgb":[0.2,0.66666666666666663,0.2],"xyz":[0.163367834364542386,0.296911192098055166,0.0800207402089785219],"hpluv":[127.715012949240119,170.670578118814461,61.3867923044640946],"hsluv":[127.715012949240119,86.9017438736093339,61.3867923044640946]},"#33aa44":{"lch":[61.5414071550205364,77.3485744588317488,130.237764523164799],"luv":[61.5414071550205364,-49.9641607354037305,59.0456146789935588],"rgb":[0.2,0.66666666666666663,0.266666666666666663],"xyz":[0.167825829608761246,0.298694390195742732,0.10349951516186516],"hpluv":[130.237764523164799,159.486606837004757,61.5414071550205364],"hsluv":[130.237764523164799,87.1702267355774723,61.5414071550205364]},"#33aa55":{"lch":[61.7472402279952775,70.9239260559490106,134.00596147156574],"luv":[61.7472402279952775,-49.2732069613111605,51.0132763399638876],"rgb":[0.2,0.66666666666666663,0.333333333333333315],"xyz":[0.173788243648123231,0.301079355811487592,0.134901562435839062],"hpluv":[134.00596147156574,145.752006815371971,61.7472402279952775],"hsluv":[134.00596147156574,87.512567621072165,61.7472402279952775]},"#33aa66":{"lch":[62.0075227235960824,63.6834089970413046,139.532932917076664],"luv":[62.0075227235960824,-48.4490087982300253,41.3312246123130436],"rgb":[0.2,0.66666666666666663,0.4],"xyz":[0.181373239421631416,0.304113354120890911,0.174849206842983024],"hpluv":[139.532932917076664,130.323054003441854,62.0075227235960824],"hsluv":[139.532932917076664,87.9225345315596769,62.0075227235960824]},"#33aa77":{"lch":[62.3247799262656201,56.2910074750267,147.571074265173024],"luv":[62.3247799262656201,-47.5128361285441727,30.1862207898839152],"rgb":[0.2,0.66666666666666663,0.466666666666666674],"xyz":[0.190687252441833321,0.307838959328971706,0.223903008749380933],"hpluv":[147.571074265173024,114.608702125460667,62.3247799262656201],"hsluv":[147.571074265173024,88.3905588997681519,62.3247799262656201]},"#33aa88":{"lch":[62.7009046876535052,49.786919253891476,159.033972270618222],"luv":[62.7009046876535052,-46.4906641824602147,17.8144736848221363],"rgb":[0.2,0.66666666666666663,0.533333333333333326],"xyz":[0.201827669543777177,0.312295126169749304,0.2825758721529531],"hpluv":[159.033972270618222,100.758286424528237,62.7009046876535052],"hsluv":[159.033972270618222,88.9048289963628804,62.7009046876535052]},"#33aa99":{"lch":[63.1372095297142835,45.6309193537352797,174.368717478757389],"luv":[63.1372095297142835,-45.4107033068770818,4.47759156711236095],"rgb":[0.2,0.66666666666666663,0.6],"xyz":[0.214884633652849877,0.317517911813378462,0.351342549794071035],"hpluv":[174.368717478757389,91.7092542399893631,63.1372095297142835],"hsluv":[174.368717478757389,89.4524406674979,63.1372095297142835]},"#33aaaa":{"lch":[63.6344696573538897,45.3208071547743288,192.17705063006116],"luv":[63.6344696573538897,-44.3011098571894,-9.5596666563997],"rgb":[0.2,0.66666666666666663,0.66666666666666663],"xyz":[0.229942326618102433,0.323540988999479584,0.430646399411069636],"hpluv":[192.17705063006116,90.3742140686623117,63.6344696573538897],"hsluv":[192.17705063006116,90.0204628815860559,63.6344696573538897]},"#33aabb":{"lch":[64.1929631055189844,49.4317459506583177,209.109442205543161],"luv":[64.1929631055189844,-43.188124160558381,-24.0475245965695663],"rgb":[0.2,0.66666666666666663,0.733333333333333282],"xyz":[0.247079918725439052,0.330396025842414343,0.520904384509711327],"hpluv":[209.109442205543161,97.7142389408223835,64.1929631055189844],"hsluv":[209.109442205543161,90.5968047396532228,64.1929631055189844]},"#33aacc":{"lch":[64.8125111239688181,57.2242456540803062,222.641155468482083],"luv":[64.8125111239688181,-42.0947673064025736,-38.7639633693247347],"rgb":[0.2,0.66666666666666663,0.8],"xyz":[0.266372294169160806,0.338112976019903144,0.622510895179981483],"hpluv":[222.641155468482083,112.036763534802631,64.8125111239688181],"hsluv":[222.641155468482083,91.1708232266795875,64.8125111239688181]},"#33aadd":{"lch":[65.4925201274692199,67.4454999587780151,232.519386199289158],"luv":[65.4925201274692199,-41.0401119544032653,-53.5220017886062465],"rgb":[0.2,0.66666666666666663,0.866666666666666696],"xyz":[0.28789061971979435,0.34672030624015665,0.73584074307998748],"hpluv":[232.519386199289158,130.67743538587149,65.4925201274692199],"hsluv":[232.519386199289158,91.7336648454097485,65.4925201274692199]},"#33aaee":{"lch":[66.23202547083838,79.0605595482254,239.573325902293959],"luv":[66.23202547083838,-40.039054494753934,-68.1721804788773],"rgb":[0.2,0.66666666666666663,0.933333333333333348],"xyz":[0.311702799985969059,0.356245178346626667,0.861251559148510704],"hpluv":[239.573325902293959,151.471586303299148,66.23202547083838],"hsluv":[239.573325902293959,92.278374899274425,66.23202547083838]},"#33aaff":{"lch":[67.0297366624436,91.3892467801412778,244.667711773110682],"luv":[67.0297366624436,-39.1024682991195291,-82.6014007142609898],"rgb":[0.2,0.66666666666666663,1],"xyz":[0.33787384839580048,0.366713597710559391,0.999085747440292549],"hpluv":[244.667711773110682,173.00828905748071,67.0297366624436],"hsluv":[244.667711773110682,99.9999999999982094,67.0297366624436]},"#33bb00":{"lch":[66.705199456007648,99.1588934495857757,125.274120260315158],"luv":[66.705199456007648,-57.2631629787864327,80.9531735993793262],"rgb":[0.2,0.733333333333333282,0],"xyz":[0.191347557902569271,0.362430278167024944,0.0598717611601651684],"hpluv":[125.274120260315158,188.630237299381349,66.705199456007648],"hsluv":[125.274120260315158,100.000000000002331,66.705199456007648]},"#33bb11":{"lch":[66.7359690986495764,97.9890755357957914,125.670572132139952],"luv":[66.7359690986495764,-57.1397853669544133,79.6046723036925385],"rgb":[0.2,0.733333333333333282,0.0666666666666666657],"xyz":[0.192359223402206403,0.362834944366879797,0.0651998661249208],"hpluv":[125.670572132139952,186.318944611331347,66.7359690986495764],"hsluv":[125.670572132139952,98.1524285745497451,66.7359690986495764]},"#33bb22":{"lch":[66.7929473545782,95.8553977164860243,126.423299967719473],"luv":[66.7929473545782,-56.9137739802675,77.1302768224563],"rgb":[0.2,0.733333333333333282,0.133333333333333331],"xyz":[0.194234581540683404,0.363585087622270597,0.0750767523209],"hpluv":[126.423299967719473,182.106434833594022,66.7929473545782],"hsluv":[126.423299967719473,94.7659081903439073,66.7929473545782]},"#33bb33":{"lch":[66.8865907457163,92.4406335233364302,127.715012949240233],"luv":[66.8865907457163,-56.5491099236273058,73.126389853740946],"rgb":[0.2,0.733333333333333282,0.2],"xyz":[0.197322332273141166,0.364820187915253724,0.0913389061785111478],"hpluv":[127.715012949240233,175.373180985258983,66.8865907457163],"hsluv":[127.715012949240233,89.2962069049265637,66.8865907457163]},"#33bb44":{"lch":[67.0214179407225572,87.725631811763563,129.70178400392939],"luv":[67.0214179407225572,-56.0384119529137763,67.4943172612971409],"rgb":[0.2,0.733333333333333282,0.266666666666666663],"xyz":[0.201780327517360025,0.36660338601294129,0.114817681131397786],"hpluv":[129.70178400392939,166.093340387543549,67.0214179407225572],"hsluv":[129.70178400392939,89.4761749642590871,67.0214179407225572]},"#33bb55":{"lch":[67.2010629421400552,81.8171313208689099,132.60288320444127],"luv":[67.2010629421400552,-55.3830806540106479,60.2225651632978796],"rgb":[0.2,0.733333333333333282,0.333333333333333315],"xyz":[0.207742741556722,0.368988351628686151,0.146219728405371674],"hpluv":[132.60288320444127,154.492511096020365,67.2010629421400552],"hsluv":[132.60288320444127,89.7076242739581,67.2010629421400552]},"#33bb66":{"lch":[67.4284803792762091,74.9653268862145,136.738510871982356],"luv":[67.4284803792762091,-54.5922668909562248,51.3759148907695788],"rgb":[0.2,0.733333333333333282,0.4],"xyz":[0.215327737330230196,0.372022349938089469,0.186167372812515663],"hpluv":[136.738510871982356,141.07705768332076,67.4284803792762091],"hsluv":[136.738510871982356,89.9877463438330096,67.4284803792762091]},"#33bb77":{"lch":[67.7060530794905446,67.5982244112705,142.572908280802977],"luv":[67.7060530794905446,-53.681598162732449,41.0829156980295878],"rgb":[0.2,0.733333333333333282,0.466666666666666674],"xyz":[0.224641750350432101,0.375747955146170265,0.235221174718913573],"hpluv":[142.572908280802977,126.69139486293237,67.7060530794905446],"hsluv":[142.572908280802977,90.3115397167098,67.7060530794905446]},"#33bb88":{"lch":[68.0356563096068641,60.380187227113133,150.730801091974854],"luv":[68.0356563096068641,-52.67158330257773,29.5206931148449],"rgb":[0.2,0.733333333333333282,0.533333333333333326],"xyz":[0.235782167452375957,0.380204121986947863,0.293894038122485712],"hpluv":[150.730801091974854,112.615249481580875,68.0356563096068641],"hsluv":[150.730801091974854,90.6723452470342437,68.0356563096068641]},"#33bb99":{"lch":[68.4187011865960244,54.2829584497859159,161.862695711717],"luv":[68.4187011865960244,-51.585814888834193,16.8980259295610082],"rgb":[0.2,0.733333333333333282,0.6],"xyz":[0.248839131561448657,0.385426907630577,0.362660715763603647],"hpluv":[161.862695711717,100.676477021468287,68.4187011865960244],"hsluv":[161.862695711717,91.0624469223214,68.4187011865960244]},"#33bbaa":{"lch":[68.8561680799326439,50.5661966451060749,176.101011456941364],"luv":[68.8561680799326439,-50.4491601398581224,3.43838397135738649],"rgb":[0.2,0.733333333333333282,0.66666666666666663],"xyz":[0.263896824526701212,0.391449984816678143,0.441964565380602248],"hpluv":[176.101011456941364,93.1873079321678404,68.8561680799326439],"hsluv":[176.101011456941364,91.473675634614068,68.8561680799326439]},"#33bbbb":{"lch":[69.3486356756669835,50.4205674478029,192.177050630061103],"luv":[69.3486356756669835,-49.286127891295429,-10.635375839212843],"rgb":[0.2,0.733333333333333282,0.733333333333333282],"xyz":[0.281034416634037831,0.398305021659612901,0.532222550479243939],"hpluv":[192.177050630061103,92.2590830970113132,69.3486356756669835],"hsluv":[192.177050630061103,91.8979539795913922,69.3486356756669835]},"#33bbcc":{"lch":[69.8963087653012423,54.2786660468549442,207.559867588447844],"luv":[69.8963087653012423,-48.1195504219082224,-25.1133919457216273],"rgb":[0.2,0.733333333333333282,0.8],"xyz":[0.300326792077759586,0.406021971837101703,0.633829061149514095],"hpluv":[207.559867588447844,98.540384199822455,69.8963087653012423],"hsluv":[207.559867588447844,92.3277362004695306,69.8963087653012423]},"#33bbdd":{"lch":[70.4990463576295241,61.569428837921933,220.28187220447964],"luv":[70.4990463576295241,-46.9696505240293618,-39.807618580850594],"rgb":[0.2,0.733333333333333282,0.866666666666666696],"xyz":[0.321845117628393129,0.414629302057355209,0.747158909049520092],"hpluv":[220.28187220447964,110.820781636478685,70.4990463576295241],"hsluv":[220.28187220447964,92.7563200135089119,70.4990463576295241]},"#33bbee":{"lch":[71.1563908243766576,71.2672074810155181,229.953995552192254],"luv":[71.1563908243766576,-45.8534981630350487,-54.557048750410388],"rgb":[0.2,0.733333333333333282,0.933333333333333348],"xyz":[0.345657297894567894,0.424154174163825226,0.872569725118043316],"hpluv":[229.953995552192254,127.091105244163856,71.1563908243766576],"hsluv":[229.953995552192254,93.1780289437208893,71.1563908243766576]},"#33bbff":{"lch":[71.8675982303626597,82.4526478797043296,237.101125866277243],"luv":[71.8675982303626597,-44.7848112977892,-69.2297610814592161],"rgb":[0.2,0.733333333333333282,1],"xyz":[0.37182834630439926,0.43462259352775795,1.01040391340982527],"hpluv":[237.101125866277243,145.583046200088774,71.8675982303626597],"hsluv":[237.101125866277243,99.9999999999977831,71.8675982303626597]},"#33cc00":{"lch":[72.1534232831706532,108.011475964841438,125.713046635977918],"luv":[72.1534232831706532,-63.0491190384013507,87.6999859098336287],"rgb":[0.2,0.8,0],"xyz":[0.229571301212186191,0.438877764786259839,0.0726130089300371234],"hpluv":[125.713046635977918,189.955680955455,72.1534232831706532],"hsluv":[125.713046635977918,100.000000000002402,72.1534232831706532]},"#33cc11":{"lch":[72.1805088449519,106.967132064598133,126.040754358939225],"luv":[72.1805088449519,-62.9352412435677095,86.493483868665578],"rgb":[0.2,0.8,0.0666666666666666657],"xyz":[0.230582966711823323,0.439282430986114691,0.0779411138947927601],"hpluv":[126.040754358939225,188.048441784684599,72.1805088449519],"hsluv":[126.040754358939225,98.4728126855506645,72.1805088449519]},"#33cc22":{"lch":[72.2306742907645543,105.057034533260762,126.660198785960176],"luv":[72.2306742907645543,-62.7261976516662614,84.2757653960309199],"rgb":[0.2,0.8,0.133333333333333331],"xyz":[0.232458324850300324,0.440032574241505492,0.0878180000907719527],"hpluv":[126.660198785960176,184.562215611711281,72.2306742907645543],"hsluv":[126.660198785960176,95.6680740449206866,72.2306742907645543]},"#33cc33":{"lch":[72.31314692234902,101.984984863208481,127.715012949240304],"luv":[72.31314692234902,-62.3877173898115416,80.6765756365369668],"rgb":[0.2,0.8,0.2],"xyz":[0.235546075582758085,0.441267674534488619,0.104080153948383103],"hpluv":[127.715012949240304,178.960959976488198,72.31314692234902],"hsluv":[127.715012949240304,91.1230258822071306,72.31314692234902]},"#33cc44":{"lch":[72.4319472107582669,97.7091257880760651,129.318260791805528],"luv":[72.4319472107582669,-61.9111863445413135,75.5915224590797123],"rgb":[0.2,0.8,0.266666666666666663],"xyz":[0.240004070826976945,0.443050872632176185,0.127558928901269741],"hpluv":[129.318260791805528,171.176559629977817,72.4319472107582669],"hsluv":[129.318260791805528,91.2471619418673612,72.4319472107582669]},"#33cc55":{"lch":[72.590341240881628,92.2844112105744756,131.621023493746776],"luv":[72.590341240881628,-61.2953573189119396,68.9876200751215407],"rgb":[0.2,0.8,0.333333333333333315],"xyz":[0.24596648486633893,0.445435838247921045,0.158960976175243629],"hpluv":[131.621023493746776,161.32023063104279,72.590341240881628],"hsluv":[131.621023493746776,91.4078622695478,72.590341240881628]},"#33cc66":{"lch":[72.7910248315973405,85.8719567505974197,134.835055825888389],"luv":[72.7910248315973405,-60.5455878238572112,60.8951948123993319],"rgb":[0.2,0.8,0.4],"xyz":[0.253551480639847115,0.448469836557324364,0.198908620582387619],"hpluv":[134.835055825888389,149.696915507144809,72.7910248315973405],"hsluv":[134.835055825888389,91.6039613293452533,72.7910248315973405]},"#33cc77":{"lch":[73.0362204241858421,78.757716060864837,139.259917231121676],"luv":[73.0362204241858421,-59.6729854923348,51.3995393126768079],"rgb":[0.2,0.8,0.466666666666666674],"xyz":[0.262865493660049,0.452195441765405159,0.247962422488785528],"hpluv":[139.259917231121676,136.834039856532058,73.0362204241858421],"hsluv":[139.259917231121676,91.8328510325735294,73.0362204241858421]},"#33cc88":{"lch":[73.3277345291399,71.3846311778678597,145.306874573990314],"luv":[73.3277345291399,-58.6933246064594201,40.6307668527313197],"rgb":[0.2,0.8,0.533333333333333326],"xyz":[0.274005910761992877,0.456651608606182757,0.306635285892357667],"hpluv":[145.306874573990314,123.530949311180805,73.3277345291399],"hsluv":[145.306874573990314,92.0907512285691894,73.3277345291399]},"#33cc99":{"lch":[73.6669954969027714,64.400374936046,153.483372150156],"luv":[73.6669954969027714,-57.6257667092137709,28.7520312861295437],"rgb":[0.2,0.8,0.6],"xyz":[0.287062874871065576,0.461874394249811915,0.375401963533475602],"hpluv":[153.483372150156,110.931469165383049,73.6669954969027714],"hsluv":[153.483372150156,92.3730273510429072,73.6669954969027714]},"#33ccaa":{"lch":[74.0550811623464114,58.6991946004706691,164.236139418897579],"luv":[74.0550811623464114,-56.4914910882685106,15.9470022690160835],"rgb":[0.2,0.8,0.66666666666666663],"xyz":[0.302120567836318132,0.467897471435913037,0.454705813150474203],"hpluv":[164.236139418897579,100.581152280716978,74.0550811623464114],"hsluv":[164.236139418897579,92.6745296641919794,74.0550811623464114]},"#33ccbb":{"lch":[74.4927414449451106,55.3647402049724846,177.507530206592946],"luv":[74.4927414449451106,-55.3123621552270137,2.40770653799565881],"rgb":[0.2,0.8,0.733333333333333282],"xyz":[0.319258159943654751,0.474752508278847796,0.544963798249115894],"hpluv":[177.507530206592946,94.310193436061823,74.4927414449451106],"hsluv":[177.507530206592946,92.9899230693886,74.4927414449451106]},"#33cccc":{"lch":[74.9804187561532416,55.3552144916165361,192.177050630061132],"luv":[74.9804187561532416,-54.1097476482840918,-11.6762571422475752],"rgb":[0.2,0.8,0.8],"xyz":[0.338550535387376506,0.482469458456336597,0.646570308919386],"hpluv":[192.177050630061132,93.6806731785530928,74.9804187561532416],"hsluv":[192.177050630061132,93.3139795405001422,74.9804187561532416]},"#33ccdd":{"lch":[75.5182678303382602,59.0031963362726231,206.282454691493683],"luv":[75.5182678303382602,-52.9035678654544128,-26.1264173778571198],"rgb":[0.2,0.8,0.866666666666666696],"xyz":[0.360068860938010049,0.491076788676590104,0.759900156819392],"hpluv":[206.282454691493683,99.1431801932779564,75.5182678303382602],"hsluv":[206.282454691493683,93.6418135046530296,75.5182678303382602]},"#33ccee":{"lch":[76.106175853756767,65.8577115847402439,218.26062431545347],"luv":[76.106175853756767,-51.7116147040808585,-40.7817003063324606],"rgb":[0.2,0.8,0.933333333333333348],"xyz":[0.383881041204184759,0.500601660783060121,0.885310972887915271],"hpluv":[218.26062431545347,110.229350215344013,76.106175853756767],"hsluv":[218.26062431545347,93.969050662624,76.106175853756767]},"#33ccff":{"lch":[76.7437832939395435,75.0714180144803,227.674056146546604],"luv":[76.7437832939395435,-50.5491406549608,-55.5022718611574248],"rgb":[0.2,0.8,1],"xyz":[0.41005208961401618,0.511070080146992844,1.02314516117969712],"hpluv":[227.674056146546604,129.845560156838474,76.7437832939395435],"hsluv":[227.674056146546604,99.9999999999969731,76.7437832939395435]},"#33dd00":{"lch":[77.5280782787270653,116.686614644285086,126.047543424376144],"luv":[77.5280782787270653,-68.6649809221669187,94.3445092843369508],"rgb":[0.2,0.866666666666666696,0],"xyz":[0.272205291759361367,0.524145745880611358,0.0868243391124284419],"hpluv":[126.047543424376144,210.356208283509261,77.5280782787270653],"hsluv":[126.047543424376144,100.000000000002416,77.5280782787270653]},"#33dd11":{"lch":[77.5521415069474926,115.747372453695789,126.322097671746874],"luv":[77.5521415069474926,-68.5599423432795,93.2576459912042],"rgb":[0.2,0.866666666666666696,0.0666666666666666657],"xyz":[0.273216957258998472,0.524550412080466266,0.0921524440771840786],"hpluv":[126.322097671746874,208.932074165120611,77.5521415069474926],"hsluv":[126.322097671746874,98.7203227917640902,77.5521415069474926]},"#33dd22":{"lch":[77.5967156031793337,114.025807364398673,126.839335004613],"luv":[77.5967156031793337,-68.36681588768,91.2571270119929778],"rgb":[0.2,0.866666666666666696,0.133333333333333331],"xyz":[0.275092315397475529,0.525300555335857067,0.102029330273163271],"hpluv":[126.839335004613,206.31706664059891,77.5967156031793337],"hsluv":[126.839335004613,96.3665979525037102,77.5967156031793337]},"#33dd33":{"lch":[77.670013861504259,111.246421345384377,127.715012949240332],"luv":[77.670013861504259,-68.0532561222895,88.0029578668175247],"rgb":[0.2,0.866666666666666696,0.2],"xyz":[0.278180066129933234,0.526535655628840193,0.118291484130774421],"hpluv":[127.715012949240332,202.082466523340599,77.670013861504259],"hsluv":[127.715012949240332,92.5426273046004439,77.670013861504259]},"#33dd44":{"lch":[77.7756375941771267,107.354265763003397,129.034195217585733],"luv":[77.7756375941771267,-67.6100090567609726,83.3895979895470134],"rgb":[0.2,0.866666666666666696,0.266666666666666663],"xyz":[0.28263806137415215,0.52831885372652776,0.14177025908366106],"hpluv":[129.034195217585733,196.125875311323085,77.7756375941771267],"hsluv":[129.034195217585733,92.6304308505642098,77.7756375941771267]},"#33dd55":{"lch":[77.9165348137244,102.370875286307594,130.905664069221132],"luv":[77.9165348137244,-67.0340391276217105,77.3707548439410431],"rgb":[0.2,0.866666666666666696,0.333333333333333315],"xyz":[0.288600475413514135,0.530703819342272509,0.173172306357634975],"hpluv":[130.905664069221132,188.454179514464698,77.9165348137244],"hsluv":[130.905664069221132,92.7446831943234287,77.9165348137244]},"#33dd66":{"lch":[78.095166356878579,96.3992072274516261,133.476315028685519],"luv":[78.095166356878579,-66.3279238591037767,69.9529389705828777],"rgb":[0.2,0.866666666666666696,0.4],"xyz":[0.29618547118702232,0.533737817651675828,0.213119950764778909],"hpluv":[133.476315028685519,179.196222734944627,78.095166356878579],"hsluv":[133.476315028685519,92.8850067954864187,78.095166356878579]},"#33dd77":{"lch":[78.3135937781308513,89.6343875602499196,136.948304354896891],"luv":[78.3135937781308513,-65.499258897229538,61.1896275296294192],"rgb":[0.2,0.866666666666666696,0.466666666666666674],"xyz":[0.305499484207224226,0.537463422859756679,0.262173752671176818],"hpluv":[136.948304354896891,168.630388284117117,78.3135937781308513],"hsluv":[136.948304354896891,93.050064801238122,78.3135937781308513]},"#33dd88":{"lch":[78.5735314106716487,82.3820261839553609,141.59738099104726],"luv":[78.5735314106716487,-64.5599157920348432,51.1743638074698],"rgb":[0.2,0.866666666666666696,0.533333333333333326],"xyz":[0.316639901309168,0.541919589700534221,0.320846616074749],"hpluv":[141.59738099104726,157.233383580683977,78.5735314106716487],"hsluv":[141.59738099104726,93.2377028740416449,78.5735314106716487]},"#33dd99":{"lch":[78.8763801167060592,75.0869406053615762,147.781507566587862],"luv":[78.8763801167060592,-63.5251386535812799,40.0325543591259105],"rgb":[0.2,0.866666666666666696,0.6],"xyz":[0.329696865418240725,0.547142375344163434,0.389613293715866893],"hpluv":[147.781507566587862,145.760006886720333,78.8763801167060592],"hsluv":[147.781507566587862,93.4451205095912343,78.8763801167060592]},"#33ddaa":{"lch":[79.2232512041385633,68.3697583649377094,155.904677195463563],"luv":[79.2232512041385633,-62.4125308872686801,27.9123601138582131],"rgb":[0.2,0.866666666666666696,0.66666666666666663],"xyz":[0.344754558383493281,0.553165452530264501,0.468917143332865494],"hpluv":[155.904677195463563,135.355769729163313,79.2232512041385633],"hsluv":[155.904677195463563,93.6690625743759,79.2232512041385633]},"#33ddbb":{"lch":[79.6149850527770866,63.045401416642612,166.259028854183981],"luv":[79.6149850527770866,-61.2410113290283604,14.9753521221847095],"rgb":[0.2,0.866666666666666696,0.733333333333333282],"xyz":[0.361892150490829956,0.560020489373199259,0.559175128431507296],"hpluv":[166.259028854183981,127.658944712944631,79.6149850527770866],"hsluv":[166.259028854183981,93.9060163408359614,79.6149850527770866]},"#33ddcc":{"lch":[80.0521670478692613,60.0458591823856054,178.675787060338223],"luv":[80.0521670478692613,-60.0298228841068138,1.3876488942262355],"rgb":[0.2,0.866666666666666696,0.8],"xyz":[0.381184525934551655,0.567737439550688117,0.660781639101777452],"hpluv":[178.675787060338223,124.734340702728062,80.0521670478692613],"hsluv":[178.675787060338223,94.1523985056595905,80.0521670478692613]},"#33dddd":{"lch":[80.5351423549551555,60.1510343961145963,192.177050630061245],"luv":[80.5351423549551555,-58.7976638126828,-12.6878551809710149],"rgb":[0.2,0.866666666666666696,0.866666666666666696],"xyz":[0.402702851485185254,0.576344769770941623,0.774111487001783449],"hpluv":[192.177050630061245,128.603021497070955,80.5351423549551555],"hsluv":[192.177050630061245,94.4047190696773271,80.5351423549551555]},"#33ddee":{"lch":[81.0640304387754,63.6213092914002303,205.209077734070348],"luv":[81.0640304387754,-57.5619892131040913,-27.0977562499646396],"rgb":[0.2,0.866666666666666696,0.933333333333333348],"xyz":[0.426515031751359963,0.58586964187741164,0.899522303070306672],"hpluv":[205.209077734070348,140.476637056352985,81.0640304387754],"hsluv":[205.209077734070348,94.6597131799347835,81.0640304387754]},"#33ddff":{"lch":[81.6387398294900208,70.0938175080633528,216.50946924270437],"luv":[81.6387398294900208,-56.3385046754299452,-41.7027114680837769],"rgb":[0.2,0.866666666666666696,1],"xyz":[0.452686080161191384,0.596338061241344364,1.03735649136208852],"hpluv":[216.50946924270437,160.421433033358312,81.6387398294900208],"hsluv":[216.50946924270437,99.999999999996,81.6387398294900208]},"#33ee00":{"lch":[82.833762600699373,125.203353646442437,126.3077634478595],"luv":[82.833762600699373,-74.1357070958390239,100.894879442497455],"rgb":[0.2,0.933333333333333348,0],"xyz":[0.319384068099876184,0.618503298561642323,0.102550597892599626],"hpluv":[126.3077634478595,309.713105305240845,82.833762600699373],"hsluv":[126.3077634478595,100.000000000002302,82.833762600699373]},"#33ee11":{"lch":[82.85531245284119,124.353122892884315,126.540515263097504],"luv":[82.85531245284119,-74.0387382831010541,99.9097813362598544],"rgb":[0.2,0.933333333333333348,0.0666666666666666657],"xyz":[0.320395733599513288,0.618907964761497231,0.107878702857355263],"hpluv":[126.540515263097504,308.054239915702169,82.85531245284119],"hsluv":[126.540515263097504,98.9149262576223833,82.85531245284119]},"#33ee22":{"lch":[82.8952353009988,122.792060636983948,126.977870705292972],"luv":[82.8952353009988,-73.860225327277135,98.0946342573363808],"rgb":[0.2,0.933333333333333348,0.133333333333333331],"xyz":[0.322271091737990345,0.619658108016888,0.117755589053334456],"hpluv":[126.977870705292972,305.002845011722627,82.8952353009988],"hsluv":[126.977870705292972,96.9167682151396264,82.8952353009988]},"#33ee33":{"lch":[82.9608975691188846,120.264250910780831,127.715012949240375],"luv":[82.9608975691188846,-73.5697721383487391,95.1366316128687259],"rgb":[0.2,0.933333333333333348,0.2],"xyz":[0.325358842470448051,0.620893208309871159,0.134017742910945592],"hpluv":[127.715012949240375,300.046240285686,82.9608975691188846],"hsluv":[127.715012949240375,93.6639064416312834,82.9608975691188846]},"#33ee44":{"lch":[83.0555452014946241,116.707569363843,128.817932535493583],"luv":[83.0555452014946241,-73.1578713419028,90.9317469728687513],"rgb":[0.2,0.933333333333333348,0.266666666666666663],"xyz":[0.329816837714666966,0.622676406407558725,0.157496517863832231],"hpluv":[128.817932535493583,293.039720989359807,83.0555452014946241],"hsluv":[128.817932535493583,93.7274034182796356,83.0555452014946241]},"#33ee55":{"lch":[83.1818510898783643,112.121671621867506,130.367812557605703],"luv":[83.1818510898783643,-72.6203079673681202,85.4257579305358092],"rgb":[0.2,0.933333333333333348,0.333333333333333315],"xyz":[0.335779251754028951,0.625061372023303474,0.188898565137806146],"hpluv":[130.367812557605703,283.950089878719211,83.1818510898783643],"hsluv":[130.367812557605703,93.8103653792530139,83.1818510898783643]},"#33ee66":{"lch":[83.3420657578551,106.5703857928128,132.470647679843353],"luv":[83.3420657578551,-71.9576476395269,78.6088040502757224],"rgb":[0.2,0.933333333333333348,0.4],"xyz":[0.343364247527537136,0.628095370332706793,0.228846209544950108],"hpluv":[132.470647679843353,272.865433725808,83.3420657578551],"hsluv":[132.470647679843353,93.912785079345241,83.3420657578551]},"#33ee77":{"lch":[83.5380975272703523,100.188150337510052,135.268396470867572],"luv":[83.5380975272703523,-71.1748024762597851,70.5110839550557387],"rgb":[0.2,0.933333333333333348,0.466666666666666674],"xyz":[0.352678260547739042,0.631820975540787644,0.277900011451348],"hpluv":[135.268396470867572,260.019238129472967,83.5380975272703523],"hsluv":[135.268396470867572,94.0340074276028304,83.5380975272703523]},"#33ee88":{"lch":[83.7715600973053682,93.1909370684561651,138.951686086792108],"luv":[83.7715600973053682,-70.2805135444840801,61.1980405537673],"rgb":[0.2,0.933333333333333348,0.533333333333333326],"xyz":[0.363818677649682842,0.636277142381565186,0.336572874854920157],"hpluv":[138.951686086792108,245.83342468256393,83.7715600973053682],"hsluv":[138.951686086792108,94.1728069818675237,83.7715600973053682]},"#33ee99":{"lch":[84.0438031984902807,85.8935006436990278,143.770719305914184],"luv":[84.0438031984902807,-69.2867121669879822,50.7646035138469784],"rgb":[0.2,0.933333333333333348,0.6],"xyz":[0.376875641758755542,0.641499928025194399,0.405339552496038091],"hpluv":[143.770719305914184,230.989810482694878,84.0438031984902807],"hsluv":[143.770719305914184,94.3274826341768886,84.0438031984902807]},"#33eeaa":{"lch":[84.3559338989094698,78.7339796376561907,150.032255922422308],"luv":[84.3559338989094698,-68.2077782157370223,39.328596980513943],"rgb":[0.2,0.933333333333333348,0.66666666666666663],"xyz":[0.391933334724008098,0.647523005211295466,0.484643402113036692],"hpluv":[150.032255922422308,216.540119706931335,84.3559338989094698],"hsluv":[150.032255922422308,94.495967075681591,84.3559338989094698]},"#33eebb":{"lch":[84.7088326356722,72.2999891840671,158.051587623557367],"luv":[84.7088326356722,-67.0597412553262,27.0236847743774931],"rgb":[0.2,0.933333333333333348,0.733333333333333282],"xyz":[0.409070926831344772,0.654378042054230225,0.574901387211678383],"hpluv":[158.051587623557367,204.04938686040532,84.7088326356722],"hsluv":[158.051587623557367,94.6759444498571838,84.7088326356722]},"#33eecc":{"lch":[85.1031663228954,67.3294619946068451,168.005441410177127],"luv":[85.1031663228954,-65.8594808644086,13.992327638881461],"rgb":[0.2,0.933333333333333348,0.8],"xyz":[0.428363302275066471,0.662094992231719082,0.676507897881948539],"hpluv":[168.005441410177127,195.708498626929867,85.1031663228954],"hsluv":[168.005441410177127,94.8649680125767389,85.1031663228954]},"#33eedd":{"lch":[85.539399954776755,64.6250931348259314,179.663789762776076],"luv":[85.539399954776755,-64.6239805156479861,0.37921629457989664],"rgb":[0.2,0.933333333333333348,0.866666666666666696],"xyz":[0.44988162782570007,0.670702322451972588,0.789837745781954537],"hpluv":[179.663789762776076,194.233009793031641,85.539399954776755],"hsluv":[179.663789762776076,95.0605698730458926,85.539399954776755]},"#33eeee":{"lch":[86.0178075751720286,64.8282855412949601,192.177050630061217],"luv":[86.0178075751720286,-63.3696789602654533,-13.6744431219908602],"rgb":[0.2,0.933333333333333348,0.933333333333333348],"xyz":[0.47369380809187478,0.680227194558442605,0.91524856185047776],"hpluv":[192.177050630061217,202.327515221469156,86.0178075751720286],"hsluv":[192.177050630061217,95.2603564020327127,86.0178075751720286]},"#33eeff":{"lch":[86.538483142230433,68.1460620684978124,204.293044736593487],"luv":[86.538483142230433,-62.1119480142598803,-28.035543321245509],"rgb":[0.2,0.933333333333333348,1],"xyz":[0.499864856501706201,0.690695613922375329,1.05308275014225972],"hpluv":[204.293044736593487,221.878852364873978,86.538483142230433],"hsluv":[204.293044736593487,99.999999999993932,86.538483142230433]},"#33ff00":{"lch":[88.074762753062231,133.577745567808222,126.513803819973305],"luv":[88.074762753062231,-79.4809541468946,107.358241597361044],"rgb":[0.2,1,0],"xyz":[0.37123644052955,0.72220804342099143,0.119834722035823737],"hpluv":[126.513803819973305,497.272976699974663,88.074762753062231],"hsluv":[126.513803819973305,100.00000000000226,88.074762753062231]},"#33ff11":{"lch":[88.0941974462199,132.80363803677966,126.71317891025123],"luv":[88.0941974462199,-79.3912833442141448,106.460464045403725],"rgb":[0.2,1,0.0666666666666666657],"xyz":[0.37224810602918712,0.722612709620846339,0.125162827000579374],"hpluv":[126.71317891025123,495.277769978645495,88.0941974462199],"hsluv":[126.71317891025123,99.9999999999917861,88.0941974462199]},"#33ff22":{"lch":[88.1302050034733355,131.38040615815558,127.087058789859782],"luv":[88.1302050034733355,-79.2260405323659285,104.804797713872986],"rgb":[0.2,1,0.133333333333333331],"xyz":[0.374123464167664177,0.723362852876237139,0.135039713196558553],"hpluv":[127.087058789859782,491.602558840386564,88.1302050034733355],"hsluv":[127.087058789859782,99.9999999999915161,88.1302050034733355]},"#33ff33":{"lch":[88.1894367416410745,129.070276381710187,127.715012949240347],"luv":[88.1894367416410745,-78.9567203165017162,102.10275491931047],"rgb":[0.2,1,0.2],"xyz":[0.377211214900121883,0.724597953169220266,0.151301867054169703],"hpluv":[127.715012949240347,485.618062737129037,88.1894367416410745],"hsluv":[127.715012949240347,99.9999999999917577,88.1894367416410745]},"#33ff44":{"lch":[88.2748349985884,125.807640063387211,128.649544439126117],"luv":[88.2748349985884,-78.5738101086462137,98.2533391968394909],"rgb":[0.2,1,0.266666666666666663],"xyz":[0.381669210144340798,0.726381151266907832,0.174780642007056342],"hpluv":[128.649544439126117,477.126267833886629,88.2748349985884],"hsluv":[128.649544439126117,99.9999999999917151,88.2748349985884]},"#33ff55":{"lch":[88.3888340150102181,121.57774004888698,129.95306271607248],"luv":[88.3888340150102181,-78.0723424240601,93.1979410921456122],"rgb":[0.2,1,0.333333333333333315],"xyz":[0.387631624183702783,0.728766116882652581,0.206182689281030257],"hpluv":[129.95306271607248,466.04908765344419,88.3888340150102181],"hsluv":[129.95306271607248,99.999999999991644,88.3888340150102181]},"#33ff66":{"lch":[88.5334972733264,116.417510431790106,131.704600668064415],"luv":[88.5334972733264,-77.4514410423726929,86.9155395518881],"rgb":[0.2,1,0.4],"xyz":[0.395216619957210968,0.7318001151920559,0.246130333688174219],"hpluv":[131.704600668064415,452.43688525451455,88.5334972733264],"hsluv":[131.704600668064415,99.9999999999913882,88.5334972733264]},"#33ff77":{"lch":[88.7105909437740081,110.419515958012653,134.007319384137503],"luv":[88.7105909437740081,-76.7139872603015505,79.419353202025647],"rgb":[0.2,1,0.466666666666666674],"xyz":[0.404530632977412874,0.735525720400136751,0.295184135594572128],"hpluv":[134.007319384137503,436.493157669178117,88.7105909437740081],"hsluv":[134.007319384137503,99.9999999999915,88.7105909437740081]},"#33ff88":{"lch":[88.9216276204312379,103.738746998712813,136.99719245927011],"luv":[88.9216276204312379,-75.8662497359534882,70.7533729221786558],"rgb":[0.2,1,0.533333333333333326],"xyz":[0.415671050079356674,0.739981887240914293,0.353856998998144268],"hpluv":[136.99719245927011,418.619650658891032,88.9216276204312379],"hsluv":[136.99719245927011,99.9999999999912603,88.9216276204312379]},"#33ff99":{"lch":[89.1678944508512359,96.6032590942606788,140.85189111032139],"luv":[89.1678944508512359,-74.9174292298428242,60.9882649796200695],"rgb":[0.2,1,0.6],"xyz":[0.428728014188429429,0.745204672884543506,0.422623676639262202],"hpluv":[140.85189111032139,399.492483487252343,89.1678944508512359],"hsluv":[140.85189111032139,99.9999999999913456,89.1678944508512359]},"#33ffaa":{"lch":[89.4504724790268426,89.3298939826383105,145.795546705853383],"luv":[89.4504724790268426,-73.8791171714821,50.2165909327964926],"rgb":[0.2,1,0.66666666666666663],"xyz":[0.44378570715368193,0.751227750070644573,0.501927526256260803],"hpluv":[145.795546705853383,380.184928602968114,89.4504724790268426],"hsluv":[145.795546705853383,99.9999999999912,89.4504724790268426]},"#33ffbb":{"lch":[89.7702508712150262,82.3444719603705,152.087330110215447],"luv":[89.7702508712150262,-72.7646918614874494,38.5475249625060314],"rgb":[0.2,1,0.733333333333333282],"xyz":[0.460923299261018604,0.758082786913579332,0.592185511354902605],"hpluv":[152.087330110215447,362.351543222333419,89.7702508712150262],"hsluv":[152.087330110215447,99.9999999999909761,89.7702508712150262]},"#33ffcc":{"lch":[90.127938152783571,76.1985663634222,159.968053619612647],"luv":[90.127938152783571,-71.5886883295869723,26.1013643147272063],"rgb":[0.2,1,0.8],"xyz":[0.480215674704740358,0.765799737091068189,0.693792022025172761],"hpluv":[159.968053619612647,348.464440630653087,90.127938152783571],"hsluv":[159.968053619612647,99.999999999990834,90.127938152783571]},"#33ffdd":{"lch":[90.5240717550146314,71.5577226375514357,169.529475161807255],"luv":[90.5240717550146314,-70.3661811113599924,13.0041618290463337],"rgb":[0.2,1,0.866666666666666696],"xyz":[0.501734000255373846,0.774407067311321695,0.807121869925178759],"hpluv":[169.529475161807255,342.013155745428833,90.5240717550146314],"hsluv":[169.529475161807255,99.9999999999904077,90.5240717550146314]},"#33ffee":{"lch":[90.9590266887802,69.1149718308150511,180.51167449693034],"luv":[90.9590266887802,-69.1122158161064561,-0.61721646306864264],"rgb":[0.2,1,0.933333333333333348],"xyz":[0.525546180521548667,0.783931939417791712,0.932532685993702],"hpluv":[180.51167449693034,347.442342228089899,90.9590266887802],"hsluv":[180.51167449693034,99.9999999999901519,90.9590266887802]},"#33ffff":{"lch":[91.4330238629877243,69.4028497051403122,192.177050630061132],"luv":[91.4330238629877243,-67.8413175363212702,-14.6393709608822622],"rgb":[0.2,1,1],"xyz":[0.55171722893138,0.794400358781724436,1.07036687428548372],"hpluv":[192.177050630061132,369.590917988860895,91.4330238629877243],"hsluv":[192.177050630061132,99.9999999999897256,91.4330238629877243]},"#22aa00":{"lch":[60.8595101229647923,91.9296409673314656,126.268252023172565],"luv":[60.8595101229647923,-54.3824975763784053,74.1188427172042594],"rgb":[0.133333333333333331,0.66666666666666663,0],"xyz":[0.150337683054585614,0.290883353615456,0.0482228743965988915],"hpluv":[126.268252023172565,191.675426474830772,60.8595101229647923],"hsluv":[126.268252023172565,100.000000000002288,60.8595101229647923]},"#22aa11":{"lch":[60.8951349805759605,90.6090655032342482,126.765726538166419],"luv":[60.8951349805759605,-54.2335583573106,72.5859759132132893],"rgb":[0.133333333333333331,0.66666666666666663,0.0666666666666666657],"xyz":[0.151349348554222746,0.291288019815310828,0.0535509793613545212],"hpluv":[126.765726538166419,188.811473003651798,60.8951349805759605],"hsluv":[126.765726538166419,97.6988909499644365,60.8951349805759605]},"#22aa22":{"lch":[60.9610867967894592,88.21068058626,127.715012949240347],"luv":[60.9610867967894592,-53.9615024560723953,69.7802294505720226],"rgb":[0.133333333333333331,0.66666666666666663,0.133333333333333331],"xyz":[0.153224706692699747,0.292038163070701628,0.0634278655573337208],"hpluv":[127.715012949240347,183.614848439340193,60.9610867967894592],"hsluv":[127.715012949240347,93.4926845994439475,60.9610867967894592]},"#22aa33":{"lch":[61.0694299105106495,84.4016098893730629,129.358188857231823],"luv":[61.0694299105106495,-53.5246691509727626,65.259034196016259],"rgb":[0.133333333333333331,0.66666666666666663,0.2],"xyz":[0.156312457425157481,0.293273263363684755,0.0796900194149448571],"hpluv":[129.358188857231823,175.374397166590711,61.0694299105106495],"hsluv":[129.358188857231823,93.5882360519160699,61.0694299105106495]},"#22aa44":{"lch":[61.2253168994486145,79.2082340196825214,131.918899994809664],"luv":[61.2253168994486145,-52.917281977470445,58.938150671985575],"rgb":[0.133333333333333331,0.66666666666666663,0.266666666666666663],"xyz":[0.160770452669376368,0.295056461461372321,0.103168794367831496],"hpluv":[131.918899994809664,164.164260791814144,61.2253168994486145],"hsluv":[131.918899994809664,93.7213426507132397,61.2253168994486145]},"#22aa55":{"lch":[61.4328316402448422,72.8294169380228169,135.724592815827151],"luv":[61.4328316402448422,-52.1453123372646488,50.8428005993127883],"rgb":[0.133333333333333331,0.66666666666666663,0.333333333333333315],"xyz":[0.166732866708738381,0.297441427077117182,0.134570841641805411],"hpluv":[135.724592815827151,150.433869175471955,61.4328316402448422],"hsluv":[135.724592815827151,93.8909627694616802,61.4328316402448422]},"#22aa66":{"lch":[61.695221435526193,65.6694032551541511,141.26425661379642],"luv":[61.695221435526193,-51.2247747289881588,41.0912761769754056],"rgb":[0.133333333333333331,0.66666666666666663,0.4],"xyz":[0.174317862482246538,0.3004754253865205,0.174518486048949373],"hpluv":[141.26425661379642,135.067502346464636,61.695221435526193],"hsluv":[141.26425661379642,94.0939374356695737,61.695221435526193]},"#22aa77":{"lch":[62.015018576984005,58.3994247120946,149.232117680428786],"luv":[62.015018576984005,-50.1795183581889,29.8748848373309741],"rgb":[0.133333333333333331,0.66666666666666663,0.466666666666666674],"xyz":[0.183631875502448472,0.304201030594601296,0.223572287955347282],"hpluv":[149.232117680428786,119.495353216490457,62.015018576984005],"hsluv":[149.232117680428786,94.3254538020573534,62.015018576984005]},"#22aa88":{"lch":[62.3941144695078265,52.0455462506657582,160.428514439052776],"luv":[62.3941144695078265,-49.0385773736463904,17.4343572780611709],"rgb":[0.133333333333333331,0.66666666666666663,0.533333333333333326],"xyz":[0.194772292604392272,0.308657197435378894,0.282245151358919477],"hpluv":[160.428514439052776,105.847175197261592,62.3941144695078265],"hsluv":[160.428514439052776,94.5795977554836753,62.3941144695078265]},"#22aa99":{"lch":[62.8338123759312168,48.0032947436357915,175.177921691935865],"luv":[62.8338123759312168,-47.8333890460383486,4.03524455451089725],"rgb":[0.133333333333333331,0.66666666666666663,0.6],"xyz":[0.207829256713465,0.313879983079008051,0.351011829000037356],"hpluv":[175.177921691935865,96.9431131493944918,62.8338123759312168],"hsluv":[175.177921691935865,94.8499327714938119,62.8338123759312168]},"#22aaaa":{"lch":[63.334871160235295,47.6677335272966047,192.177050630061132],"luv":[63.334871160235295,-46.595231466735612,-10.0547115418935782],"rgb":[0.133333333333333331,0.66666666666666663,0.66666666666666663],"xyz":[0.222886949678717528,0.319903060265109174,0.430315678617035957],"hpluv":[192.177050630061132,95.5038628742744748,63.334871160235295],"hsluv":[192.177050630061132,95.1300327368807075,63.334871160235295]},"#22aabb":{"lch":[63.8975462810157211,51.5875561200845425,208.460106210027732],"luv":[63.8975462810157211,-45.3531553267970224,-24.5838818811491713],"rgb":[0.133333333333333331,0.66666666666666663,0.733333333333333282],"xyz":[0.240024541786054202,0.326758097108043932,0.520573663715677704],"hpluv":[208.460106210027732,102.447201536891086,63.8975462810157211],"hsluv":[208.460106210027732,95.4139121453229393,63.8975462810157211]},"#22aacc":{"lch":[64.5216311304052681,59.1142282054053112,221.706391964203618],"luv":[64.5216311304052681,-44.1325525375176184,-39.3295027027292079],"rgb":[0.133333333333333331,0.66666666666666663,0.8],"xyz":[0.259316917229775901,0.334475047285532734,0.62218017438594786],"hpluv":[221.706391964203618,116.258847224370101,64.5216311304052681],"hsluv":[221.706391964203618,95.6963242429470853,64.5216311304052681]},"#22aadd":{"lch":[65.2065000175346796,69.0826091468118619,231.553565124642319],"luv":[65.2065000175346796,-42.9543721979751538,-54.1047945713593279],"rgb":[0.133333333333333331,0.66666666666666663,0.866666666666666696],"xyz":[0.2808352427804095,0.34308237750578624,0.735510022285953857],"hpluv":[231.553565124642319,134.436491082611866,65.2065000175346796],"hsluv":[231.553565124642319,95.9729250289437,65.2065000175346796]},"#22aaee":{"lch":[65.951153016283925,80.4876559434330687,238.683318882444183],"luv":[65.951153016283925,-41.8348964739996418,-68.7612114224159825],"rgb":[0.133333333333333331,0.66666666666666663,0.933333333333333348],"xyz":[0.304647423046584209,0.352607249612256257,0.86092083835447708],"hpluv":[238.683318882444183,154.862481256054423,65.951153016283925],"hsluv":[238.683318882444183,96.2403212652285305,65.951153016283925]},"#22aaff":{"lch":[66.7542622474436911,92.6475815050926741,243.881654723446388],"luv":[66.7542622474436911,-40.785937465112724,-83.187028218555227],"rgb":[0.133333333333333331,0.66666666666666663,1],"xyz":[0.33081847145641563,0.363075668976189,0.998755026646258925],"hpluv":[243.881654723446388,176.114215620682756,66.7542622474436911],"hsluv":[243.881654723446388,99.9999999999982521,66.7542622474436911]},"#22bb00":{"lch":[66.4275479271698117,100.802558180890344,126.547308547849156],"luv":[66.4275479271698117,-60.0265444602621798,80.9812922592122533],"rgb":[0.133333333333333331,0.733333333333333282,0],"xyz":[0.184292180963184393,0.358792349432654534,0.0595410403661315105],"hpluv":[126.547308547849156,192.558484368086766,66.4275479271698117],"hsluv":[126.547308547849156,100.000000000002402,66.4275479271698117]},"#22bb11":{"lch":[66.4585250929908682,99.6331665050874449,126.950333401467461],"luv":[66.4585250929908682,-59.891738470658936,79.6225315522727612],"rgb":[0.133333333333333331,0.733333333333333282,0.0666666666666666657],"xyz":[0.185303846462821525,0.359197015632509387,0.0648691453308871402],"hpluv":[126.950333401467461,190.235936561387632,66.4585250929908682],"hsluv":[126.950333401467461,98.1323711753334464,66.4585250929908682]},"#22bb22":{"lch":[66.5158870179191553,97.5011623585143496,127.715012949240375],"luv":[66.5158870179191553,-59.6448091899021904,77.1295883430057785],"rgb":[0.133333333333333331,0.733333333333333282,0.133333333333333331],"xyz":[0.187179204601298527,0.359947158887900187,0.0747460315268663328],"hpluv":[127.715012949240375,186.004620566556611,66.5158870179191553],"hsluv":[127.715012949240375,94.7095045552014341,66.5158870179191553]},"#22bb33":{"lch":[66.6101592341861561,94.091611241079562,129.025604662043349],"luv":[66.6101592341861561,-59.2464412491434089,73.0964465979998721],"rgb":[0.133333333333333331,0.733333333333333282,0.2],"xyz":[0.19026695533375626,0.361182259180883314,0.091008185384477483],"hpluv":[129.025604662043349,179.246118589082698,66.6101592341861561],"hsluv":[129.025604662043349,94.7728361420699628,66.6101592341861561]},"#22bb44":{"lch":[66.7458880420052907,89.3893698914418735,131.03732282887168],"luv":[66.7458880420052907,-58.6886365964374903,67.424797990356],"rgb":[0.133333333333333331,0.733333333333333282,0.266666666666666663],"xyz":[0.194724950577975148,0.36296545727857088,0.114486960337364121],"hpluv":[131.03732282887168,169.941985039444177,66.7458880420052907],"hsluv":[131.03732282887168,94.8616428317247511,66.7458880420052907]},"#22bb55":{"lch":[66.9267274999506,83.5071173210959472,133.96576260666626],"luv":[66.9267274999506,-57.9730125462468777,60.1046459068846488],"rgb":[0.133333333333333331,0.733333333333333282,0.333333333333333315],"xyz":[0.200687364617337161,0.365350422894315741,0.145889007611338023],"hpluv":[133.96576260666626,158.330006142604873,66.9267274999506],"hsluv":[133.96576260666626,94.9758066387206128,66.9267274999506]},"#22bb66":{"lch":[67.1556458878222315,76.7025221245859541,138.121353671725132],"luv":[67.1556458878222315,-57.1096598103208919,51.2031606116460054],"rgb":[0.133333333333333331,0.733333333333333282,0.4],"xyz":[0.208272360390845318,0.368384421203719059,0.185836652018481985],"hpluv":[138.121353671725132,144.932719296840418,67.1556458878222315],"hsluv":[138.121353671725132,95.1139083236691363,67.1556458878222315]},"#22bb77":{"lch":[67.4350338747947831,69.4107315991336407,143.945643371462126],"luv":[67.4350338747947831,-56.1157304677756557,40.8518598743670935],"rgb":[0.133333333333333331,0.733333333333333282,0.466666666666666674],"xyz":[0.217586373411047251,0.372110026411799855,0.234890453924879894],"hpluv":[143.945643371462126,130.611184627207962,67.4350338747947831],"hsluv":[143.945643371462126,95.2734443978328471,67.4350338747947831]},"#22bb88":{"lch":[67.7667691572368653,62.2973404800357571,152.016434409622292],"luv":[67.7667691572368653,-55.0136735570032656,29.2310511724943218],"rgb":[0.133333333333333331,0.733333333333333282,0.533333333333333326],"xyz":[0.228726790512991052,0.376566193252577452,0.293563317328452089],"hpluv":[152.016434409622292,116.65196328010299,67.7667691572368653],"hsluv":[152.016434409622292,95.4510958201536823,67.7667691572368653]},"#22bb99":{"lch":[68.1522602188863402,56.3167420179958214,162.907285030510934],"luv":[68.1522602188863402,-53.8292536917802948,16.552549003324323],"rgb":[0.133333333333333331,0.733333333333333282,0.6],"xyz":[0.241783754622063779,0.38178897889620661,0.362329994969569968],"hpluv":[162.907285030510934,104.856796539193,68.1522602188863402],"hsluv":[162.907285030510934,95.6430286983925555,68.1522602188863402]},"#22bbaa":{"lch":[68.5924801056558238,52.6774965449754617,176.68900923381463],"luv":[68.5924801056558238,-52.5895649511659542,3.04241688349887474],"rgb":[0.133333333333333331,0.733333333333333282,0.66666666666666663],"xyz":[0.256841447587316307,0.387812056082307732,0.441633844586568569],"hpluv":[176.68900923381463,97.4513697523019,68.5924801056558238],"hsluv":[176.68900923381463,95.8451953079369616,68.5924801056558238]},"#22bbbb":{"lch":[69.087995915136645,52.5025293878432677,192.177050630061075],"luv":[69.087995915136645,-51.3212466461144174,-11.0745309069971647],"rgb":[0.133333333333333331,0.733333333333333282,0.733333333333333282],"xyz":[0.273979039694653,0.394667092925242491,0.531891829685210316],"hpluv":[192.177050630061075,96.4310638968393903,69.087995915136645],"hsluv":[192.177050630061075,96.05360442262905,69.087995915136645]},"#22bbcc":{"lch":[69.6389970716796824,56.2104265974936,207.07804051519031],"luv":[69.6389970716796824,-50.0490518690675117,-25.5871933841835393],"rgb":[0.133333333333333331,0.733333333333333282,0.8],"xyz":[0.293271415138374736,0.402384043102731292,0.633498340355480472],"hpluv":[207.07804051519031,102.424464009042,69.6389970716796824],"hsluv":[207.07804051519031,96.2645382772070519,69.6389970716796824]},"#22bbdd":{"lch":[70.2453239794146924,63.2897284454122868,219.558412279776348],"luv":[70.2453239794146924,-48.7948432782897541,-40.3069844585422956],"rgb":[0.133333333333333331,0.733333333333333282,0.866666666666666696],"xyz":[0.314789740689008224,0.410991373322984799,0.746828188255486469],"hpluv":[219.558412279776348,114.328666738486675,70.2453239794146924],"hsluv":[219.558412279776348,96.4747049793171101,70.2453239794146924]},"#22bbee":{"lch":[70.9064977498674409,72.7777709471329644,229.176572531806158],"luv":[70.9064977498674409,-47.5770176148035588,-55.072963774560229],"rgb":[0.133333333333333331,0.733333333333333282,0.933333333333333348],"xyz":[0.338601920955183,0.420516245429454816,0.872239004324009692],"hpluv":[229.176572531806158,130.242295294742263,70.9064977498674409],"hsluv":[229.176572531806158,96.6813261778791286,70.9064977498674409]},"#22bbff":{"lch":[71.621751136046143,83.782469360031385,236.362391595167765],"luv":[71.621751136046143,-46.4103062114400586,-69.7537500742789121],"rgb":[0.133333333333333331,0.733333333333333282,1],"xyz":[0.364772969365014355,0.43098466479338754,1.01007319261579154],"hpluv":[236.362391595167765,148.438838585801221,71.621751136046143],"hsluv":[236.362391595167765,99.9999999999977405,71.621751136046143]},"#22cc00":{"lch":[71.9091745039523431,109.499123564337054,126.755635680122666],"luv":[71.9091745039523431,-65.5246489265194185,87.7301455852794732],"rgb":[0.133333333333333331,0.8,0],"xyz":[0.222515924272801313,0.435239836051889428,0.0722822881360034586],"hpluv":[126.755635680122666,193.226045742870838,71.9091745039523431],"hsluv":[126.755635680122666,100.000000000002288,71.9091745039523431]},"#22cc11":{"lch":[71.9364107151438077,108.454952609175663,127.087674169957992],"luv":[71.9364107151438077,-65.4022833696805,86.5159989567857508],"rgb":[0.133333333333333331,0.8,0.0666666666666666657],"xyz":[0.223527589772438445,0.435644502251744281,0.0776103931007591],"hpluv":[127.087674169957992,191.311003889673145,71.9364107151438077],"hsluv":[127.087674169957992,98.4591341389350418,71.9364107151438077]},"#22cc22":{"lch":[71.9868548113652196,106.54571101335867,127.715012949240403],"luv":[71.9868548113652196,-65.1776701905061771,84.2843985793322332],"rgb":[0.133333333333333331,0.8,0.133333333333333331],"xyz":[0.225402947910915447,0.436394645507135082,0.0874872792967382878],"hpluv":[127.715012949240403,187.811464536348922,71.9868548113652196],"hsluv":[127.715012949240403,95.6295101801496,71.9868548113652196]},"#22cc33":{"lch":[72.0697845089905229,103.47656324227728,128.782383231673776],"luv":[72.0697845089905229,-64.8140104316899084,80.6631464312780224],"rgb":[0.133333333333333331,0.8,0.2],"xyz":[0.22849069864337318,0.437629745800118208,0.103749433154349424],"hpluv":[128.782383231673776,182.191494825933376,72.0697845089905229],"hsluv":[128.782383231673776,95.6728206524347,72.0697845089905229]},"#22cc44":{"lch":[72.1892409305665126,99.2080881077430092,130.402545810446583],"luv":[72.1892409305665126,-64.3020931154350563,75.5479024657046381],"rgb":[0.133333333333333331,0.8,0.266666666666666663],"xyz":[0.232948693887592068,0.439412943897805774,0.127228208107236063],"hpluv":[130.402545810446583,174.38692971209278,72.1892409305665126],"hsluv":[130.402545810446583,95.7338579762306807,72.1892409305665126]},"#22cc55":{"lch":[72.3485056391290584,93.798910065649892,132.724941359454647],"luv":[72.3485056391290584,-63.6406395178356732,68.9064912128369116],"rgb":[0.133333333333333331,0.8,0.333333333333333315],"xyz":[0.23891110792695408,0.441797909513550635,0.158630255381209978],"hpluv":[132.724941359454647,164.515777124050715,72.3485056391290584],"hsluv":[132.724941359454647,95.8128514989610665,72.3485056391290584]},"#22cc66":{"lch":[72.5502856399570106,87.4149553130202577,135.957009780349864],"luv":[72.5502856399570106,-62.8354766726053597,60.7706942835425821],"rgb":[0.133333333333333331,0.8,0.4],"xyz":[0.246496103700462238,0.444831907822953954,0.19857789978835394],"hpluv":[135.957009780349864,152.89241471165704,72.5502856399570106],"hsluv":[135.957009780349864,95.9092114165347454,72.5502856399570106]},"#22cc77":{"lch":[72.7968107106438,80.347487444586676,140.388595454497391],"luv":[72.7968107106438,-61.8986076052927601,51.2277377519640922],"rgb":[0.133333333333333331,0.8,0.466666666666666674],"xyz":[0.255810116720664171,0.448557513031034749,0.247631701694751849],"hpluv":[140.388595454497391,140.055211607516185,72.7968107106438],"hsluv":[140.388595454497391,96.0216367005683651,72.7968107106438]},"#22cc88":{"lch":[73.0898911189871399,73.0433057087839899,146.410934307510047],"luv":[73.0898911189871399,-60.8470337434031379,40.4099368162836328],"rgb":[0.133333333333333331,0.8,0.533333333333333326],"xyz":[0.266950533822607972,0.453013679871812347,0.306304565098324044],"hpluv":[146.410934307510047,126.812607157634474,73.0898911189871399],"hsluv":[146.410934307510047,96.1482500942118463,73.0898911189871399]},"#22cc99":{"lch":[73.4309556061454316,66.1476907411168,154.494840416347301],"luv":[73.4309556061454316,-59.7013675833161201,28.4826912187770667],"rgb":[0.133333333333333331,0.8,0.6],"xyz":[0.280007497931680671,0.458236465515441505,0.375071242739441923],"hpluv":[154.494840416347301,114.307528246897718,73.4309556061454316],"hsluv":[154.494840416347301,96.2867563581127826,73.4309556061454316]},"#22ccaa":{"lch":[73.8210792378530272,60.5373530055450857,165.03572853292485],"luv":[73.8210792378530272,-58.4843517505691963,15.631753236079188],"rgb":[0.133333333333333331,0.8,0.66666666666666663],"xyz":[0.295065190896933227,0.464259542701542627,0.454375092356440524],"hpluv":[165.03572853292485,104.059650734936554,73.8210792378530272],"hsluv":[165.03572853292485,96.4346108423233233,73.8210792378530272]},"#22ccbb":{"lch":[74.2610062314211916,57.2561659312404601,177.947236116536601],"luv":[74.2610062314211916,-57.2194225987583565,2.05090580243043252],"rgb":[0.133333333333333331,0.8,0.733333333333333282],"xyz":[0.312202783004269901,0.471114579544477385,0.544633077455082271],"hpluv":[177.947236116536601,97.8364666306377302,74.2610062314211916],"hsluv":[177.947236116536601,96.5891828531354406,74.2610062314211916]},"#22cccc":{"lch":[74.7511706210643467,57.2167927817266,192.177050630061132],"luv":[74.7511706210643467,-55.9294412838427633,-12.0689259631595149],"rgb":[0.133333333333333331,0.8,0.8],"xyz":[0.3314951584479916,0.478831529721966187,0.646239588125352427],"hpluv":[192.177050630061132,97.128087782713564,74.7511706210643467],"hsluv":[192.177050630061132,96.747899952527,74.7511706210643467]},"#22ccdd":{"lch":[75.2917163799825602,60.7443465218384091,205.915913497891523],"luv":[75.2917163799825602,-54.6356779140553357,-26.5484148912282159],"rgb":[0.133333333333333331,0.8,0.866666666666666696],"xyz":[0.353013483998625199,0.487438859942219693,0.759569436025358424],"hpluv":[205.915913497891523,102.375961416692178,75.2917163799825602],"hsluv":[205.915913497891523,96.9083635301369,75.2917163799825602]},"#22ccee":{"lch":[75.8825178700112843,67.4282526949852894,217.691288579377613],"luv":[75.8825178700112843,-53.3570890727704779,-41.2260877015904583],"rgb":[0.133333333333333331,0.8,0.933333333333333348],"xyz":[0.376825664264799909,0.49696373204868971,0.884980252093881647],"hpluv":[217.691288579377613,112.755958051566466,75.8825178700112843],"hsluv":[217.691288579377613,97.0684311053177851,75.8825178700112843]},"#22ccff":{"lch":[76.5232010138481,76.4669756721874165,227.041443142980768],"luv":[76.5232010138481,-52.1098871667616095,-55.9621124325938553],"rgb":[0.133333333333333331,0.8,1],"xyz":[0.40299671267463133,0.507432151412622434,1.02281444038566338],"hpluv":[227.041443142980768,130.754689276117347,76.5232010138481],"hsluv":[227.041443142980768,99.999999999997,76.5232010138481]},"#22dd00":{"lch":[77.3111928538645543,118.038829613749726,126.914864539429317],"luv":[77.3111928538645543,-70.8973873693031607,94.3754510494695],"rgb":[0.133333333333333331,0.866666666666666696,0],"xyz":[0.265149914819976518,0.520507817146241,0.0864936183183947771],"hpluv":[126.914864539429317,210.347075729226248,77.3111928538645543],"hsluv":[126.914864539429317,100.000000000002288,77.3111928538645543]},"#22dd11":{"lch":[77.3353680300965323,117.099630973594756,127.192375056145337],"luv":[77.3353680300965323,-70.7859197567446756,93.2827805028547346],"rgb":[0.133333333333333331,0.866666666666666696,0.0666666666666666657],"xyz":[0.266161580319613622,0.520912483346095856,0.0918217232831504138],"hpluv":[127.192375056145337,208.941659016378082,77.3353680300965323],"hsluv":[127.192375056145337,98.7107326231664217,77.3353680300965323]},"#22dd22":{"lch":[77.3801492665193535,115.37848303888174,127.715012949240432],"luv":[77.3801492665193535,-70.5809801545768636,91.2716801027172409],"rgb":[0.133333333333333331,0.866666666666666696,0.133333333333333331],"xyz":[0.268036938458090679,0.521662626601486656,0.101698609479129606],"hpluv":[127.715012949240432,206.361699793513822,77.3801492665193535],"hsluv":[127.715012949240432,96.3395071146262154,77.3801492665193535]},"#22dd33":{"lch":[77.4537875025684315,112.600723810091978,128.59932725776207],"luv":[77.4537875025684315,-70.24826089977104,88.0005956974969195],"rgb":[0.133333333333333331,0.866666666666666696,0.2],"xyz":[0.271124689190548385,0.522897726894469783,0.117960763336740743],"hpluv":[128.59932725776207,202.185786680839726,77.4537875025684315],"hsluv":[128.59932725776207,96.3699377868481122,77.4537875025684315]},"#22dd44":{"lch":[77.5598997380018,108.71293616133481,129.930302741787841],"luv":[77.5598997380018,-69.7779723132927217,83.3638834787812],"rgb":[0.133333333333333331,0.866666666666666696,0.266666666666666663],"xyz":[0.2755826844347673,0.524680924992157349,0.141439538289627381],"hpluv":[129.930302741787841,196.316024449944422,77.5598997380018],"hsluv":[129.930302741787841,96.4129906320409162,77.5598997380018]},"#22dd55":{"lch":[77.7014460191091416,103.73899221962921,131.815952634500974],"luv":[77.7014460191091416,-69.1669361195931,77.3156740549574266],"rgb":[0.133333333333333331,0.866666666666666696,0.333333333333333315],"xyz":[0.281545098474129285,0.527065890607902099,0.172841585563601297],"hpluv":[131.815952634500974,188.764163413831653,77.7014460191091416],"hsluv":[131.815952634500974,96.4690009350202189,77.7014460191091416]},"#22dd66":{"lch":[77.880896240080375,97.784990294992113,134.401120560320578],"luv":[77.880896240080375,-68.4179393254247117,69.8633659756971923],"rgb":[0.133333333333333331,0.866666666666666696,0.4],"xyz":[0.28913009424763747,0.530099888917305417,0.212789229970745258],"hpluv":[134.401120560320578,179.664283635232721,77.880896240080375],"hsluv":[134.401120560320578,96.5377748783722467,77.880896240080375]},"#22dd77":{"lch":[78.1003183749745205,91.0497694522495635,137.883451987021573],"luv":[78.1003183749745205,-67.5390964016577584,61.0616980975421],"rgb":[0.133333333333333331,0.866666666666666696,0.466666666666666674],"xyz":[0.298444107267839376,0.533825494125386268,0.261843031877143195],"hpluv":[137.883451987021573,169.299836923025936,78.1003183749745205],"hsluv":[137.883451987021573,96.6186469084632478,78.1003183749745205]},"#22dd88":{"lch":[78.3614307373917285,83.8424849668191143,142.529576014285681],"luv":[78.3614307373917285,-66.5430534683674608,51.0057283108209205],"rgb":[0.133333333333333331,0.866666666666666696,0.533333333333333326],"xyz":[0.309584524369783176,0.538281660966163811,0.320515895280715335],"hpluv":[142.529576014285681,158.150702843881334,78.3614307373917285],"hsluv":[142.529576014285681,96.7105501690208,78.3614307373917285]},"#22dd99":{"lch":[78.665635872828048,76.6094810635621144,148.680389056502293],"luv":[78.665635872828048,-65.4460211356496586,39.8224924677044854],"rgb":[0.133333333333333331,0.866666666666666696,0.6],"xyz":[0.322641488478855876,0.543504446609793,0.389282572921833214],"hpluv":[148.680389056502293,146.968629494944878,78.665635872828048],"hsluv":[148.680389056502293,96.8121015046769884,78.665635872828048]},"#22ddaa":{"lch":[79.0140446006893882,69.96699558248838,156.711923446039975],"luv":[79.0140446006893882,-64.2666918031728613,27.6617569130368608],"rgb":[0.133333333333333331,0.866666666666666696,0.66666666666666663],"xyz":[0.337699181444108432,0.549527523795894091,0.46858642253883187],"hpluv":[156.711923446039975,136.880679993502099,79.0140446006893882],"hsluv":[156.711923446039975,96.9216963326973513,79.0140446006893882]},"#22ddbb":{"lch":[79.4074947696843196,64.7136880558744849,166.88262774299784],"luv":[79.4074947696843196,-63.0251268096233161,14.6865520944819572],"rgb":[0.133333333333333331,0.866666666666666696,0.733333333333333282],"xyz":[0.354836773551445106,0.556382560638828849,0.558844407637473561],"hpluv":[166.88262774299784,129.476815865977585,79.4074947696843196],"hsluv":[166.88262774299784,97.0376060255481,79.4074947696843196]},"#22ddcc":{"lch":[79.8465673347569123,61.7508733998812431,179.012513434233142],"luv":[79.8465673347569123,-61.7417023409151184,1.06421693934889783],"rgb":[0.133333333333333331,0.866666666666666696,0.8],"xyz":[0.374129148995166805,0.564099510816317706,0.660450918307743717],"hpluv":[179.012513434233142,126.735630147597362,79.8465673347569123],"hsluv":[179.012513434233142,97.1580701030331824,79.8465673347569123]},"#22dddd":{"lch":[80.3316012938198867,61.8272738566169,192.177050630061245],"luv":[80.3316012938198867,-60.4361886569793,-13.0414298740249546],"rgb":[0.133333333333333331,0.866666666666666696,0.866666666666666696],"xyz":[0.395647474545800404,0.572706841036571213,0.773780766207749715],"hpluv":[192.177050630061245,130.583134255532286,80.3316012938198867],"hsluv":[192.177050630061245,97.2813767637234434,80.3316012938198867]},"#22ddee":{"lch":[80.8627083873859078,65.1993477029452606,204.924336649163848],"luv":[80.8627083873859078,-59.1270126845314579,-27.4763773429615092],"rgb":[0.133333333333333331,0.866666666666666696,0.933333333333333348],"xyz":[0.419459654811975113,0.58223171314304123,0.899191582276272938],"hpluv":[204.924336649163848,142.193638870282854,80.8627083873859078],"hsluv":[204.924336649163848,97.405927410883,80.8627083873859078]},"#22ddff":{"lch":[81.4397880620905141,71.5324096951162574,216.054756332513932],"luv":[81.4397880620905141,-57.8307265123863,-42.1009822669203544],"rgb":[0.133333333333333331,0.866666666666666696,1],"xyz":[0.445630703221806534,0.592700132506974,1.03702577056805478],"hpluv":[216.054756332513932,161.676156351457934,81.4397880620905141],"hsluv":[216.054756332513932,99.9999999999961,81.4397880620905141]},"#22ee00":{"lch":[82.639607109796458,126.437751030410212,127.039022267349651],"luv":[82.639607109796458,-76.1608929793909368,100.92583052028732],"rgb":[0.133333333333333331,0.933333333333333348,0],"xyz":[0.312328691160491334,0.614865369827271913,0.102219877098565962],"hpluv":[127.039022267349651,308.746810598848469,82.639607109796458],"hsluv":[127.039022267349651,100.000000000002203,82.639607109796458]},"#22ee11":{"lch":[82.6612418520526262,125.587498690937579,127.273852876359626],"luv":[82.6612418520526262,-76.0589691017437,99.9362449095734462],"rgb":[0.133333333333333331,0.933333333333333348,0.0666666666666666657],"xyz":[0.313340356660128438,0.615270036027126821,0.107547982063321598],"hpluv":[127.273852876359626,307.111076239802628,82.6612418520526262],"hsluv":[127.273852876359626,98.9080389542438638,82.6612418520526262]},"#22ee22":{"lch":[82.7013218186993271,124.026614606467561,127.715012949240375],"luv":[82.7013218186993271,-75.8713392100017643,98.112899341636421],"rgb":[0.133333333333333331,0.933333333333333348,0.133333333333333331],"xyz":[0.315215714798605495,0.616020179282517621,0.117424868259300791],"hpluv":[127.715012949240375,304.102865014696874,82.7013218186993271],"hsluv":[127.715012949240375,96.8972824334497176,82.7013218186993271]},"#22ee33":{"lch":[82.7672420886773921,121.499708898279266,128.458265706389938],"luv":[82.7672420886773921,-75.5660658728233443,95.1417308591278754],"rgb":[0.133333333333333331,0.933333333333333348,0.2],"xyz":[0.318303465531063201,0.617255279575500748,0.133687022116911941],"hpluv":[128.458265706389938,299.21821405622444,82.7672420886773921],"hsluv":[128.458265706389938,96.9191735237910308,82.7672420886773921]},"#22ee44":{"lch":[82.8622607061112575,117.945648419515081,129.569612755667265],"luv":[82.8622607061112575,-75.1331770294334,90.918544261133448],"rgb":[0.133333333333333331,0.933333333333333348,0.266666666666666663],"xyz":[0.322761460775282116,0.619038477673188314,0.15716579706979858],"hpluv":[129.569612755667265,292.317409010606752,82.8622607061112575],"hsluv":[129.569612755667265,96.9502397298715692,82.8622607061112575]},"#22ee55":{"lch":[82.9890600071846194,113.365618159982461,131.129866929811442],"luv":[82.9890600071846194,-74.5682726576598185,85.3893207236587],"rgb":[0.133333333333333331,0.933333333333333348,0.333333333333333315],"xyz":[0.328723874814644101,0.621423443288933064,0.188567844343772495],"hpluv":[131.129866929811442,283.372415217622233,82.9890600071846194],"hsluv":[131.129866929811442,96.9908233877961266,82.9890600071846194]},"#22ee66":{"lch":[83.1498978333136,107.825536766413322,133.243989823694022],"luv":[83.1498978333136,-73.8719849852999175,78.5447402013449647],"rgb":[0.133333333333333331,0.933333333333333348,0.4],"xyz":[0.336308870588152287,0.624457441598336382,0.228515488750916429],"hpluv":[133.243989823694022,272.476761047092111,83.1498978333136],"hsluv":[133.243989823694022,97.0409162623008683,83.1498978333136]},"#22ee77":{"lch":[83.3466879629012709,101.462411308546436,136.05169958656856],"luv":[83.3466879629012709,-73.0495185954435726,70.415827350948561],"rgb":[0.133333333333333331,0.933333333333333348,0.466666666666666674],"xyz":[0.345622883608354192,0.628183046806417233,0.277569290657314338],"hpluv":[136.05169958656856,259.869348467080897,83.3466879629012709],"hsluv":[136.05169958656856,97.1001922741487391,83.3466879629012709]},"#22ee88":{"lch":[83.5810478428977746,94.494960715205238,139.739189865999265],"luv":[83.5810478428977746,-72.1101014829990277,61.0690663485185681],"rgb":[0.133333333333333331,0.933333333333333348,0.533333333333333326],"xyz":[0.356763300710298,0.632639213647194776,0.336242154060886533],"hpluv":[139.739189865999265,245.976403795765407,83.5810478428977746],"hsluv":[139.739189865999265,97.1680458209452098,83.5810478428977746]},"#22ee99":{"lch":[83.8543293451422187,87.2401092672494514,144.548413258572367],"luv":[83.8543293451422187,-71.0663081347816,50.6005584263049286],"rgb":[0.133333333333333331,0.933333333333333348,0.6],"xyz":[0.369820264819370692,0.637861999290824,0.405008831702004413],"hpluv":[144.548413258572367,231.480208097577446,83.8543293451422187],"hsluv":[144.548413258572367,97.2436385928483844,83.8543293451422187]},"#22eeaa":{"lch":[84.1676401551603846,80.1359348037682508,150.771943883792943],"luv":[84.1676401551603846,-69.9332745317766324,39.1293388665967328],"rgb":[0.133333333333333331,0.933333333333333348,0.66666666666666663],"xyz":[0.384877957784623248,0.643885076476925056,0.484312681319003],"hpluv":[150.771943883792943,217.42312560604816,84.1676401551603846],"hsluv":[150.771943883792943,97.3259536357082311,84.1676401551603846]},"#22eebb":{"lch":[84.5218598825754697,73.7647249619211181,158.704077932293842],"luv":[84.5218598825754697,-68.7278540770417834,26.7902355844945639],"rgb":[0.133333333333333331,0.933333333333333348,0.733333333333333282],"xyz":[0.402015549891959922,0.650740113319859814,0.57457066641764476],"hpluv":[158.704077932293842,205.339796192481117,84.5218598825754697],"hsluv":[158.704077932293842,97.4138533690930331,84.5218598825754697]},"#22eecc":{"lch":[84.9176532530853621,68.8500130904050422,168.499772582144715],"luv":[84.9176532530853621,-67.467774257192545,13.7267526869778429],"rgb":[0.133333333333333331,0.933333333333333348,0.8],"xyz":[0.421307925335681621,0.658457063497348671,0.676177177087914916],"hpluv":[168.499772582144715,197.354512375483154,84.9176532530853621],"hsluv":[168.499772582144715,97.5061375012374896,84.9176532530853621]},"#22eedd":{"lch":[85.3554818050158275,66.1709053937965166,179.926877690260085],"luv":[85.3554818050158275,-66.170851505859261,0.0844489448895467398],"rgb":[0.133333333333333331,0.933333333333333348,0.866666666666666696],"xyz":[0.44282625088631522,0.667064393717602178,0.789507024987920913],"hpluv":[179.926877690260085,196.075052309852396,85.3554818050158275],"hsluv":[179.926877690260085,97.6015969291830174,85.3554818050158275]},"#22eeee":{"lch":[85.8356149635753667,66.3470894225537648,192.177050630061245],"luv":[85.8356149635753667,-64.8543104533760868,-13.9948094114017536],"rgb":[0.133333333333333331,0.933333333333333348,0.933333333333333348],"xyz":[0.46663843115248993,0.676589265824072195,0.914917841056444137],"hpluv":[192.177050630061245,204.089886920334152,85.8356149635753667],"hsluv":[192.177050630061245,97.6990604766853608,85.8356149635753667]},"#22eeff":{"lch":[86.35814102124138,69.583621560973242,204.067857145017655],"luv":[86.35814102124138,-63.5342376580599435,-28.377474072755291],"rgb":[0.133333333333333331,0.933333333333333348,1],"xyz":[0.492809479562321351,0.687057685188004919,1.05275202934822598],"hpluv":[204.067857145017655,223.225826840448832,86.35814102124138],"hsluv":[204.067857145017655,99.9999999999940883,86.35814102124138]},"#22ff00":{"lch":[87.8997189713237361,134.70927246092154,127.137510750393233],"luv":[87.8997189713237361,-81.328032504319566,107.388729464162736],"rgb":[0.133333333333333331,1,0],"xyz":[0.364181063590165166,0.718570114686621,0.119504001241790073],"hpluv":[127.137510750393233,493.515561032875894,87.8997189713237361],"hsluv":[127.137510750393233,100.000000000002359,87.8997189713237361]},"#22ff11":{"lch":[87.9192191859362708,133.935112153716517,127.338384986715198],"luv":[87.9192191859362708,-81.2344832365115792,106.487431187561],"rgb":[0.133333333333333331,1,0.0666666666666666657],"xyz":[0.36519272908980227,0.718974780886475928,0.124832106206545709],"hpluv":[127.338384986715198,491.550771148457443,87.9192191859362708],"hsluv":[127.338384986715198,99.9999999999918572,87.9192191859362708]},"#22ff22":{"lch":[87.9553480404818089,132.511927889398493,127.71501294924046],"luv":[87.9553480404818089,-81.0620967295481449,104.825319015849075],"rgb":[0.133333333333333331,1,0.133333333333333331],"xyz":[0.367068087228279327,0.719724924141866729,0.134708992402524902],"hpluv":[127.71501294924046,487.932270281505453,87.9553480404818089],"hsluv":[127.71501294924046,99.9999999999918,87.9553480404818089]},"#22ff33":{"lch":[88.0147790356886,130.202284180175667,128.347396099003475],"luv":[88.0147790356886,-80.7811428723405101,102.112887345201102],"rgb":[0.133333333333333331,1,0.2],"xyz":[0.370155837960737033,0.720960024434849855,0.150971146260136052],"hpluv":[128.347396099003475,482.0420586134328,88.0147790356886],"hsluv":[128.347396099003475,99.9999999999918572,88.0147790356886]},"#22ff44":{"lch":[88.1004639745127349,126.941230298117631,129.288082018787549],"luv":[88.1004639745127349,-80.3817123888383,98.2489504424236486],"rgb":[0.133333333333333331,1,0.266666666666666663],"xyz":[0.374613833204955948,0.722743222532537422,0.174449921213022691],"hpluv":[129.288082018787549,473.688351575452941,88.1004639745127349],"hsluv":[129.288082018787549,99.9999999999917719,88.1004639745127349]},"#22ff55":{"lch":[88.2148445849976497,122.715034192366474,130.599301535876663],"luv":[88.2148445849976497,-79.8586444797792865,93.1749779645044498],"rgb":[0.133333333333333331,1,0.333333333333333315],"xyz":[0.380576247244317933,0.725128188148282171,0.205851968486996606],"hpluv":[130.599301535876663,462.799362736769183,88.2148445849976497],"hsluv":[130.599301535876663,99.9999999999917,88.2148445849976497]},"#22ff66":{"lch":[88.3599902775167863,117.562049147645283,132.359544385640646],"luv":[88.3599902775167863,-79.2110524978463104,86.8702743288911279],"rgb":[0.133333333333333331,1,0.4],"xyz":[0.388161243017826119,0.72816218645768549,0.24579961289414054],"hpluv":[132.359544385640646,449.432018335727776,88.3599902775167863],"hsluv":[132.359544385640646,99.999999999991644,88.3599902775167863]},"#22ff77":{"lch":[88.5376718023346427,111.576626717172417,134.67079586388752],"luv":[88.5376718023346427,-78.441973739162,79.3486004003085839],"rgb":[0.133333333333333331,1,0.466666666666666674],"xyz":[0.397475256038028,0.731887791665766341,0.294853414800538449],"hpluv":[134.67079586388752,433.796433649144,88.5376718023346427],"hsluv":[134.67079586388752,99.9999999999914593,88.5376718023346427]},"#22ff88":{"lch":[88.7494051061703,104.915784817642518,137.666744209541747],"luv":[88.7494051061703,-77.5579802767934581,70.6546643845003786],"rgb":[0.133333333333333331,1,0.533333333333333326],"xyz":[0.408615673139971824,0.736343958506543883,0.353526278204110644],"hpluv":[137.666744209541747,416.300130961303353,88.7494051061703],"hsluv":[137.666744209541747,99.9999999999915161,88.7494051061703]},"#22ff99":{"lch":[88.996479539624346,97.8095196496336143,141.520869213027311],"luv":[88.996479539624346,-76.5687003963823827,60.8599725082183696],"rgb":[0.133333333333333331,1,0.6],"xyz":[0.42167263724904458,0.741566744150173096,0.422292955845228524],"hpluv":[141.520869213027311,397.621886071746928,88.996479539624346],"hsluv":[141.520869213027311,99.9999999999913882,88.996479539624346]},"#22ffaa":{"lch":[89.2799772586324281,90.5758041558366784,146.450029989774293],"luv":[89.2799772586324281,-75.4862499862398,50.057989986528554],"rgb":[0.133333333333333331,1,0.66666666666666663],"xyz":[0.43673033021429708,0.747589821336274163,0.50159680546222718],"hpluv":[146.450029989774293,378.829469872945424,89.2799772586324281],"hsluv":[146.450029989774293,99.9999999999911893,89.2799772586324281]},"#22ffbb":{"lch":[89.6007875020334552,83.6393655876379825,152.701818689515648],"luv":[89.6007875020334552,-74.3245992165371661,38.3587985630895787],"rgb":[0.133333333333333331,1,0.733333333333333282],"xyz":[0.453867922321633754,0.754444858179208921,0.591854790560868871],"hpluv":[152.701818689515648,361.553698033539774,89.6007875020334552],"hsluv":[152.701818689515648,99.9999999999909903,89.6007875020334552]},"#22ffcc":{"lch":[89.9596178804745,77.5461751086222,160.50148390925915],"luv":[89.9596178804745,-73.0989125181650223,25.8835519710636532],"rgb":[0.133333333333333331,1,0.8],"xyz":[0.473160297765355509,0.762161808356697779,0.693461301231139],"hpluv":[160.50148390925915,348.207300802542932,89.9596178804745],"hsluv":[160.50148390925915,99.999999999990834,89.9596178804745]},"#22ffdd":{"lch":[90.3570039795250466,72.9493687180361,169.926987012389134],"luv":[90.3570039795250466,-71.8249031216737421,12.7590629719491808],"rgb":[0.133333333333333331,1,0.866666666666666696],"xyz":[0.494678623315989,0.770769138576951285,0.806791149131145],"hpluv":[169.926987012389134,342.16275562632859,90.3570039795250466],"hsluv":[169.926987012389134,99.9999999999906,90.3570039795250466]},"#22ffee":{"lch":[90.793318095908873,70.5238201303846637,180.720785562676866],"luv":[90.793318095908873,-70.5182397089352,-0.887171987837557796],"rgb":[0.133333333333333331,1,0.933333333333333348],"xyz":[0.518490803582163817,0.780294010683421302,0.932201965199668248],"hpluv":[180.720785562676866,347.681124246536797,90.793318095908873],"hsluv":[180.720785562676866,99.9999999999901661,90.793318095908873]},"#22ffff":{"lch":[91.2687776254429082,70.7867026205843786,192.177050630061217],"luv":[91.2687776254429082,-69.1940343982234225,-14.9312715999851058],"rgb":[0.133333333333333331,1,1],"xyz":[0.544661851991995127,0.790762430047354,1.0700361534914502],"hpluv":[192.177050630061217,369.384676987995,91.2687776254429082],"hsluv":[192.177050630061217,99.9999999999901803,91.2687776254429082]},"#11aa00":{"lch":[60.6644104521350869,93.0873160838275,127.211890667698796],"luv":[60.6644104521350869,-56.2958954857129186,74.1351506854346241],"rgb":[0.0666666666666666657,0.66666666666666663,0],"xyz":[0.146052570780394131,0.288673842599075969,0.0480220097587461675],"hpluv":[127.211890667698796,194.713406103753528,60.6644104521350869],"hsluv":[127.211890667698796,100.000000000002444,60.6644104521350869]},"#11aa11":{"lch":[60.7002167335786424,91.7670835597914447,127.715012949240375],"luv":[60.7002167335786424,-56.1370762813229547,72.5935692169401],"rgb":[0.0666666666666666657,0.66666666666666663,0.0666666666666666657],"xyz":[0.147064236280031263,0.289078508798930822,0.0533501147235018],"hpluv":[127.715012949240375,191.838607973199288,60.7002167335786424],"hsluv":[127.715012949240375,97.6800439707354826,60.7002167335786424]},"#11aa22":{"lch":[60.7665037483511696,89.3702510768545153,128.6744569519864],"luv":[60.7665037483511696,-55.8469935821418844,69.7721655488506229],"rgb":[0.0666666666666666657,0.66666666666666663,0.133333333333333331],"xyz":[0.148939594418508264,0.289828652054321623,0.063227000919481],"hpluv":[128.6744569519864,186.624241227076766,60.7665037483511696],"hsluv":[128.6744569519864,97.7012125574833163,60.7665037483511696]},"#11aa33":{"lch":[60.8753956347493101,85.5662665428975799,130.333267948257316],"luv":[60.8753956347493101,-55.3812688292774595,65.2265362636212274],"rgb":[0.0666666666666666657,0.66666666666666663,0.2],"xyz":[0.152027345150966,0.291063752347304749,0.0794891547770921469],"hpluv":[130.333267948257316,178.361088002118152,60.8753956347493101],"hsluv":[130.333267948257316,97.7352371443594876,60.8753956347493101]},"#11aa44":{"lch":[61.0320681383245898,80.3855440613202603,132.913331110856149],"luv":[61.0320681383245898,-54.7338170014505394,58.8731260464924233],"rgb":[0.0666666666666666657,0.66666666666666663,0.266666666666666663],"xyz":[0.156485340395184885,0.292846950444992316,0.102967929729978785],"hpluv":[132.913331110856149,167.131840298358668,61.0320681383245898],"hsluv":[132.913331110856149,97.7826211010278428,61.0320681383245898]},"#11aa55":{"lch":[61.2406211479126199,74.0326893716562466,136.73626291081149],"luv":[61.2406211479126199,-53.9110982897111626,50.7388665304739064],"rgb":[0.0666666666666666657,0.66666666666666663,0.333333333333333315],"xyz":[0.162447754434546898,0.295231916060737176,0.134369977003952701],"hpluv":[136.73626291081149,153.399260409180499,61.2406211479126199],"hsluv":[136.73626291081149,97.8429805383323412,61.2406211479126199]},"#11aa66":{"lch":[61.5043118059827236,66.9183608277608926,142.276062400218507],"luv":[61.5043118059827236,-52.9302802927860938,40.944504442006675],"rgb":[0.0666666666666666657,0.66666666666666663,0.4],"xyz":[0.170032750208055056,0.298265914370140495,0.174317621411096635],"hpluv":[142.276062400218507,138.063556785316223,61.5043118059827236],"hsluv":[142.276062400218507,97.9151756761398246,61.5043118059827236]},"#11aa77":{"lch":[61.8256765996828648,59.7171413308715628,150.193024237310198],"luv":[61.8256765996828648,-51.8168585560505335,29.6841731923518672],"rgb":[0.0666666666666666657,0.66666666666666663,0.466666666666666674],"xyz":[0.179346763228257,0.30199151957822129,0.223371423317494544],"hpluv":[150.193024237310198,122.565843528602272,61.8256765996828648],"hsluv":[150.193024237310198,97.9974781059027578,61.8256765996828648]},"#11aa88":{"lch":[62.2066058842594,53.445591933814967,161.225226782668472],"luv":[62.2066058842594,-50.6018085779820197,17.201403018159084],"rgb":[0.0666666666666666657,0.66666666666666663,0.533333333333333326],"xyz":[0.190487180330200789,0.306447686418998888,0.282044286721066739],"hpluv":[161.225226782668472,109.022142353571084,62.2066058842594],"hsluv":[161.225226782668472,98.0877698343988698,62.2066058842594]},"#11aa99":{"lch":[62.6483970296267643,49.4620441969123803,175.635441576327452],"luv":[62.6483970296267643,-49.3186051456987613,3.76417356400704],"rgb":[0.0666666666666666657,0.66666666666666663,0.6],"xyz":[0.203544144439273517,0.311670472062628046,0.350810964362184619],"hpluv":[175.635441576327452,100.184705161952,62.6483970296267643],"hsluv":[175.635441576327452,98.1837511724305756,62.6483970296267643]},"#11aaaa":{"lch":[63.1517986220979424,49.10533458105958,192.177050630061103],"luv":[63.1517986220979424,-48.0004871585874326,-10.3579494522848794],"rgb":[0.0666666666666666657,0.66666666666666663,0.66666666666666663],"xyz":[0.218601837404526045,0.317693549248729168,0.430114813979183219],"hpluv":[192.177050630061103,98.6693521766944741,63.1517986220979424],"hsluv":[192.177050630061103,98.283131385506934,63.1517986220979424]},"#11aabb":{"lch":[63.7170519890822931,52.9100997091116483,208.088870357104184],"luv":[63.7170519890822931,-46.6782604891356812,-24.9122188681885817],"rgb":[0.0666666666666666657,0.66666666666666663,0.733333333333333282],"xyz":[0.235739429511862719,0.324548586091663926,0.520372799077825],"hpluv":[208.088870357104184,105.371274370578277,63.7170519890822931],"hsluv":[208.088870357104184,98.3837818799450474,63.7170519890822931]},"#11aacc":{"lch":[64.3439331362496603,60.2774830024396,221.163749704229559],"luv":[64.3439331362496603,-45.3787880982832661,-39.6754401089704345],"rgb":[0.0666666666666666657,0.66666666666666663,0.8],"xyz":[0.255031804955584418,0.332265536269152728,0.621979309748095122],"hpluv":[221.163749704229559,118.873987633214512,64.3439331362496603],"hsluv":[221.163749704229559,98.4838416101877385,64.3439331362496603]},"#11aadd":{"lch":[65.0317963778061738,70.0923801470555,230.985682804055784],"luv":[65.0317963778061738,-44.1241742926816372,-54.4609860144708762],"rgb":[0.0666666666666666657,0.66666666666666663,0.866666666666666696],"xyz":[0.276550130506218,0.340872866489406234,0.735309157648101119],"hpluv":[230.985682804055784,136.767964522831733,65.0317963778061738],"hsluv":[230.985682804055784,98.5817742563244,65.0317963778061738]},"#11aaee":{"lch":[65.7796198544585877,81.3683973110580325,238.155251398024461],"luv":[65.7796198544585877,-42.9315457639956293,-69.1208974137644248],"rgb":[0.0666666666666666657,0.66666666666666663,0.933333333333333348],"xyz":[0.300362310772392727,0.350397738595876251,0.860719973716624343],"hpluv":[238.155251398024461,156.965326989669,65.7796198544585877],"hsluv":[238.155251398024461,98.676383948407647,65.7796198544585877]},"#11aaff":{"lch":[66.5860524818013317,93.423779787985481,243.412392023658668],"luv":[66.5860524818013317,-41.8132782993845566,-83.5443139162218529],"rgb":[0.0666666666666666657,0.66666666666666663,1],"xyz":[0.326533359182224148,0.360866157959809,0.998554162008406188],"hpluv":[243.412392023658668,178.038321821181739,66.5860524818013317],"hsluv":[243.412392023658668,99.9999999999982805,66.5860524818013317]},"#11bb00":{"lch":[66.2579979425279788,101.837028338191359,127.308350947248712],"luv":[66.2579979425279788,-61.7238643700190792,80.9996599251199285],"rgb":[0.0666666666666666657,0.733333333333333282,0],"xyz":[0.180007068688992911,0.356582838416274528,0.0593401757282787864],"hpluv":[127.308350947248712,195.032386644097869,66.2579979425279788],"hsluv":[127.308350947248712,100.000000000002416,66.2579979425279788]},"#11bb11":{"lch":[66.2891028676776,100.667761827005108,127.715012949240403],"luv":[66.2891028676776,-61.5819268253258301,79.6345688739434507],"rgb":[0.0666666666666666657,0.733333333333333282,0.0666666666666666657],"xyz":[0.181018734188630043,0.356987504616129381,0.0646682806930344162],"hpluv":[127.715012949240403,192.702610495025482,66.2891028676776],"hsluv":[127.715012949240403,98.1199752505464318,66.2891028676776]},"#11bb22":{"lch":[66.3467009890152895,98.5365229927559199,128.48627008521666],"luv":[66.3467009890152895,-61.3219466901703356,77.130183570569983],"rgb":[0.0666666666666666657,0.733333333333333282,0.133333333333333331],"xyz":[0.182894092327107044,0.357737647871520181,0.0745451668890136088],"hpluv":[128.48627008521666,188.459149582366734,66.3467009890152895],"hsluv":[128.48627008521666,98.1339007760711155,66.3467009890152895]},"#11bb33":{"lch":[66.4413603116689586,95.1297105779412107,129.807139434656818],"luv":[66.4413603116689586,-60.9025570517959949,73.0790009455219121],"rgb":[0.0666666666666666657,0.733333333333333282,0.2],"xyz":[0.185981843059564778,0.358972748164503308,0.0908073207466247589],"hpluv":[129.807139434656818,181.684126992490178,66.4413603116689586],"hsluv":[129.807139434656818,98.1563849168539093,66.4413603116689586]},"#11bb44":{"lch":[66.5776441235544,90.4345556189602888,131.832147550242752],"luv":[66.5776441235544,-60.3153845158839061,67.3829595721343679],"rgb":[0.0666666666666666657,0.733333333333333282,0.266666666666666663],"xyz":[0.190439838303783665,0.360755946262190874,0.114286095699511397],"hpluv":[131.832147550242752,172.363503065423913,66.5776441235544],"hsluv":[131.832147550242752,98.1879073212924567,66.5776441235544]},"#11bb55":{"lch":[66.7592187944261,84.5672269635890501,134.774448398028227],"luv":[66.7592187944261,-59.5621947933412841,60.0329978237906374],"rgb":[0.0666666666666666657,0.733333333333333282,0.333333333333333315],"xyz":[0.196402252343145678,0.363140911877935735,0.145688142973485313],"hpluv":[134.774448398028227,160.742297130584,66.7592187944261],"hsluv":[134.774448398028227,98.2284201297941451,66.7592187944261]},"#11bb66":{"lch":[66.9890609579081513,77.7896938122995749,138.938350400658521],"luv":[66.9890609579081513,-58.6536805127572336,51.0977712402284752],"rgb":[0.0666666666666666657,0.733333333333333282,0.4],"xyz":[0.203987248116653835,0.366174910187339053,0.185635787380629275],"hpluv":[138.938350400658521,147.352496635070423,66.9890609579081513],"hsluv":[138.938350400658521,98.2774122804504486,66.9890609579081513]},"#11bb77":{"lch":[67.2695660469751573,70.5410036497719375,144.751753478195667],"luv":[67.2695660469751573,-57.6079608572489548,40.7106379437464625],"rgb":[0.0666666666666666657,0.733333333333333282,0.466666666666666674],"xyz":[0.213301261136855769,0.369900515395419849,0.234689589287027184],"hpluv":[144.751753478195667,133.064539371584743,67.2695660469751573],"hsluv":[144.751753478195667,98.33398740837616,67.2695660469751573]},"#11bb88":{"lch":[67.6026131767386289,63.4868829762913904,152.765342955174589],"luv":[67.6026131767386289,-56.44870944299889,29.0538725863731493],"rgb":[0.0666666666666666657,0.733333333333333282,0.533333333333333326],"xyz":[0.224441678238799569,0.374356682236197447,0.293362452690599351],"hpluv":[152.765342955174589,119.168054047142519,67.6026131767386289],"hsluv":[152.765342955174589,98.3969602233376293,67.6026131767386289]},"#11bb99":{"lch":[67.9896091217093357,57.5709210660889568,163.510238814203348],"luv":[67.9896091217093357,-55.2030563361408468,16.3411604100414145],"rgb":[0.0666666666666666657,0.733333333333333282,0.6],"xyz":[0.237498642347872296,0.379579467879826604,0.362129130331717231],"hpluv":[163.510238814203348,107.448401728013394,67.9896091217093357],"hsluv":[163.510238814203348,98.464964167861,67.9896091217093357]},"#11bbaa":{"lch":[68.431522322705149,53.97217733059707,177.026074660918823],"luv":[68.431522322705149,-53.8994902384006807,2.80015675382041573],"rgb":[0.0666666666666666657,0.733333333333333282,0.66666666666666663],"xyz":[0.252556335313124825,0.385602545065927726,0.441432979948715831],"hpluv":[177.026074660918823,100.081328983600983,68.431522322705149],"hsluv":[177.026074660918823,98.5365589333087684,68.431522322705149]},"#11bbbb":{"lch":[68.9289126417652511,53.7759127589290102,192.177050630061103],"luv":[68.9289126417652511,-52.5659794775517426,-11.3431298424003177],"rgb":[0.0666666666666666657,0.733333333333333282,0.733333333333333282],"xyz":[0.269693927420461499,0.392457581908862485,0.531690965047357578],"hpluv":[192.177050630061103,98.9978332941913095,68.9289126417652511],"hsluv":[192.177050630061103,98.6103267315429122,68.9289126417652511]},"#11bbcc":{"lch":[69.4819599404948463,57.3930965266865485,206.799543430538137],"luv":[69.4819599404948463,-51.2284702417124365,-25.8768499940688805],"rgb":[0.0666666666666666657,0.733333333333333282,0.8],"xyz":[0.288986302864183253,0.400174532086351287,0.633297475717627734],"hpluv":[206.799543430538137,104.81584090136549,69.4819599404948463],"hsluv":[206.799543430538137,98.6849492658553658,69.4819599404948463]},"#11bbdd":{"lch":[70.0904930703520535,64.3454332007566876,219.135570681285316],"luv":[70.0904930703520535,-49.9098388382008054,-40.6121011637893901],"rgb":[0.0666666666666666657,0.733333333333333282,0.866666666666666696],"xyz":[0.310504628414816741,0.408781862306604793,0.746627323617633731],"hpluv":[219.135570681285316,116.492493254234162,70.0904930703520535],"hsluv":[219.135570681285316,98.7592614999562244,70.0904930703520535]},"#11bbee":{"lch":[70.7540199625255184,73.7064384587933858,228.717665412779098],"luv":[70.7540199625255184,-48.6292974270947838,-55.3879996211006826],"rgb":[0.0666666666666666657,0.733333333333333282,0.933333333333333348],"xyz":[0.334316808680991506,0.41830673441307481,0.872038139686157],"hpluv":[228.717665412779098,132.188487390259695,70.7540199625255184],"hsluv":[228.717665412779098,98.8322822642660412,70.7540199625255184]},"#11bbff":{"lch":[71.471758937406932,84.600620314641418,235.923069973878228],"luv":[71.471758937406932,-47.4021956671632836,-70.0735099988154246],"rgb":[0.0666666666666666657,0.733333333333333282,1],"xyz":[0.360487857090822872,0.428775153777007534,1.00987232797793891],"hpluv":[235.923069973878228,150.202929688167188,71.471758937406932],"hsluv":[235.923069973878228,99.9999999999978115,71.471758937406932]},"#11cc00":{"lch":[71.760164015117,110.429324102022079,127.380540485202317],"luv":[71.760164015117,-67.042304751477019,87.7494444155603617],"rgb":[0.0666666666666666657,0.8,0],"xyz":[0.21823081199860983,0.433030325035509422,0.0720814234981507346],"hpluv":[127.380540485202317,195.272154443078705,71.760164015117],"hsluv":[127.380540485202317,100.000000000002245,71.760164015117]},"#11cc11":{"lch":[71.7874927519263366,109.385174124956521,127.715012949240432],"luv":[71.7874927519263366,-66.9146672826053219,86.5305935539869466],"rgb":[0.0666666666666666657,0.8,0.0666666666666666657],"xyz":[0.219242477498246963,0.433434991235364275,0.0774095284629063712],"hpluv":[127.715012949240432,193.352149077988855,71.7874927519263366],"hsluv":[127.715012949240432,98.4507061603193563,71.7874927519263366]},"#11cc22":{"lch":[71.8381079866900905,107.476295008714118,128.346772012349533],"luv":[71.8381079866900905,-66.680384726115463,84.2904519004221129],"rgb":[0.0666666666666666657,0.8,0.133333333333333331],"xyz":[0.221117835636723964,0.434185134490755076,0.0872864146588855638],"hpluv":[128.346772012349533,189.84411001864018,71.8381079866900905],"hsluv":[128.346772012349533,98.4601754700601361,71.8381079866900905]},"#11cc33":{"lch":[71.9213183931711626,104.40864440796355,129.421122123890029],"luv":[71.9213183931711626,-66.3010906987881441,80.6556284412913556],"rgb":[0.0666666666666666657,0.8,0.2],"xyz":[0.224205586369181697,0.435420234783738203,0.103548568516496714],"hpluv":[129.421122123890029,184.212095875088124,71.9213183931711626],"hsluv":[129.421122123890029,98.4755168761512749,71.9213183931711626]},"#11cc44":{"lch":[72.0411777660314669,100.144250866202455,131.050559631523186],"luv":[72.0411777660314669,-65.7672084530810395,75.5218198525557085],"rgb":[0.0666666666666666657,0.8,0.266666666666666663],"xyz":[0.228663581613400585,0.437203432881425769,0.127027343469383353],"hpluv":[131.050559631523186,176.394298837834185,72.0411777660314669],"hsluv":[131.050559631523186,98.4971346697200261,72.0411777660314669]},"#11cc55":{"lch":[72.2009771566369523,94.7439152050601621,133.383427271112737],"luv":[72.2009771566369523,-65.077446685205,68.8573554627095],"rgb":[0.0666666666666666657,0.8,0.333333333333333315],"xyz":[0.234625995652762598,0.439588398497170629,0.158429390743357268],"hpluv":[133.383427271112737,166.512782767528847,72.2009771566369523],"hsluv":[133.383427271112737,98.5251071985599225,72.2009771566369523]},"#11cc66":{"lch":[72.4034304155724,88.37638367841096,136.624435218533876],"luv":[72.4034304155724,-64.2379326004453333,60.6949191225620339],"rgb":[0.0666666666666666657,0.8,0.4],"xyz":[0.242210991426270755,0.442622396806573948,0.198377035150501202],"hpluv":[136.624435218533876,154.887514188688357,72.4034304155724],"hsluv":[136.624435218533876,98.5592219307307289,72.4034304155724]},"#11cc77":{"lch":[72.6507717937069657,81.3359578844494422,141.057484765247835],"luv":[72.6507717937069657,-63.261234579889333,51.1229326663597],"rgb":[0.0666666666666666657,0.8,0.466666666666666674],"xyz":[0.251525004446472689,0.446348002014654743,0.247430837056899111],"hpluv":[141.057484765247835,142.063228414279422,72.6507717937069657],"hsluv":[141.057484765247835,98.5990140248714795,72.6507717937069657]},"#11cc88":{"lch":[72.9448138267759703,74.0714793913140284,147.061910762632237],"luv":[72.9448138267759703,-62.1651250648783815,40.2750702654370087],"rgb":[0.0666666666666666657,0.8,0.533333333333333326],"xyz":[0.262665421548416489,0.450804168855432341,0.306103700460471306],"hpluv":[147.061910762632237,128.853412583983044,72.9448138267759703],"hsluv":[147.061910762632237,98.6438145792393755,72.9448138267759703]},"#11cc99":{"lch":[73.286985449663689,67.2264361640895203,155.087429558943455],"luv":[73.286985449663689,-60.9711251229010074,28.3181147072320734],"rgb":[0.0666666666666666657,0.8,0.6],"xyz":[0.275722385657489188,0.456026954499061499,0.374870378101589186],"hpluv":[155.087429558943455,116.399886643338576,73.286985449663689],"hsluv":[155.087429558943455,98.6928071276572609,73.286985449663689]},"#11ccaa":{"lch":[73.6783599756885224,61.6669147974486336,165.501022011161467],"luv":[73.6783599756885224,-59.7029534543248275,15.4390974304365383],"rgb":[0.0666666666666666657,0.8,0.66666666666666663],"xyz":[0.290780078622741744,0.462050031685162621,0.454174227718587786],"hpluv":[165.501022011161467,106.206622375239988,73.6783599756885224],"hsluv":[165.501022011161467,98.7450877264532494,73.6783599756885224]},"#11ccbb":{"lch":[74.1196780643846864,58.4137876130139801,178.201908682105085],"luv":[74.1196780643846864,-58.3850250256368426,1.83287644266177074],"rgb":[0.0666666666666666657,0.8,0.733333333333333282],"xyz":[0.307917670730078419,0.46890506852809738,0.544432212817229533],"hpluv":[178.201908682105085,100.004874571957387,74.1196780643846864],"hsluv":[178.201908682105085,98.7997230732590452,74.1196780643846864]},"#11cccc":{"lch":[74.6113685470067,58.3540675645504,192.177050630061103],"luv":[74.6113685470067,-57.0411279075965751,-12.3088150671508707],"rgb":[0.0666666666666666657,0.8,0.8],"xyz":[0.327210046173800118,0.476622018705586181,0.646038723487499689],"hpluv":[192.177050630061103,99.244272920381249,74.6113685470067],"hsluv":[192.177050630061103,98.8558017207376167,74.6113685470067]},"#11ccdd":{"lch":[75.1535687293590087,61.8087365466066316,205.702276410621693],"luv":[75.1535687293590087,-55.6933672323303881,-26.8061328768737823],"rgb":[0.0666666666666666657,0.8,0.866666666666666696],"xyz":[0.348728371724433717,0.485229348925839687,0.759368571387505686],"hpluv":[205.702276410621693,104.361325002032586,75.1535687293590087],"hsluv":[205.702276410621693,98.9124749687685352,75.1535687293590087]},"#11ccee":{"lch":[75.7461450403856418,68.3900229325434168,217.356690237772426],"luv":[75.7461450403856418,-54.3614174191380712,-41.4973677827407],"rgb":[0.0666666666666666657,0.8,0.933333333333333348],"xyz":[0.372540551990608426,0.494754221032309704,0.88477938745602891],"hpluv":[217.356690237772426,114.570165581500433,75.7461450403856418],"hsluv":[217.356690237772426,98.968985855810871,75.7461450403856418]},"#11ccff":{"lch":[76.3887144168947714,77.3228649101567811,226.666757851513921],"luv":[76.3887144168947714,-53.0620799518220849,-56.2426982736496726],"rgb":[0.0666666666666666657,0.8,1],"xyz":[0.398711600400439847,0.505222640396242428,1.02261357574781075],"hpluv":[226.666757851513921,131.304642630845649,76.3887144168947714],"hsluv":[226.666757851513921,99.9999999999969162,76.3887144168947714]},"#11dd00":{"lch":[77.1789729208637851,118.880191052714352,127.435820588671049],"luv":[77.1789729208637851,-72.2639845338074,94.3950017957916572],"rgb":[0.0666666666666666657,0.866666666666666696,0],"xyz":[0.260864802545785,0.518298306129860942,0.0862927536805420531],"hpluv":[127.435820588671049,210.367240137055802,77.1789729208637851],"hsluv":[127.435820588671049,100.000000000002302,77.1789729208637851]},"#11dd11":{"lch":[77.2032167276219,117.940964566200989,127.715012949240474],"luv":[77.2032167276219,-72.148538100067,93.2987651195291],"rgb":[0.0666666666666666657,0.866666666666666696,0.0666666666666666657],"xyz":[0.261876468045422084,0.51870297232971585,0.0916208586452976897],"hpluv":[127.715012949240474,208.973000844217893,77.2032167276219],"hsluv":[127.715012949240474,98.7048375113237739,77.2032167276219]},"#11dd22":{"lch":[77.2481249515021773,116.219968574506154,128.240715222563949],"luv":[77.2481249515021773,-71.936288170647174,91.2811674974022509],"rgb":[0.0666666666666666657,0.866666666666666696,0.133333333333333331],"xyz":[0.263751826183899141,0.519453115585106651,0.101497744841276882],"hpluv":[128.240715222563949,206.413967447437244,77.2481249515021773],"hsluv":[128.240715222563949,98.7114617332334348,77.2481249515021773]},"#11dd33":{"lch":[77.3219716074714114,113.443027450646795,129.129905575085218],"luv":[77.3219716074714114,-71.591714193384675,87.9996984996024],"rgb":[0.0666666666666666657,0.866666666666666696,0.2],"xyz":[0.266839576916356847,0.520688215878089777,0.117759898698888033],"hpluv":[129.129905575085218,202.273081507843187,77.3219716074714114],"hsluv":[129.129905575085218,98.7222220245650419,77.3219716074714114]},"#11dd44":{"lch":[77.4283833065767,109.557638087167803,130.467479357182611],"luv":[77.4283833065767,-71.1046976382296378,83.3486534805139],"rgb":[0.0666666666666666657,0.866666666666666696,0.266666666666666663],"xyz":[0.271297572160575762,0.522471413975777343,0.141238673651774671],"hpluv":[130.467479357182611,196.45511110559741,77.4283833065767],"hsluv":[130.467479357182611,98.737444128486942,77.4283833065767]},"#11dd55":{"lch":[77.5703274630482156,104.589066335953675,132.360930841179908],"luv":[77.5703274630482156,-70.4719757705036471,77.2824263854860192],"rgb":[0.0666666666666666657,0.866666666666666696,0.333333333333333315],"xyz":[0.277259986199937747,0.524856379591522093,0.172640720925748559],"hpluv":[132.360930841179908,188.974657198429043,77.5703274630482156],"hsluv":[132.360930841179908,98.7572451358875583,77.5703274630482156]},"#11dd66":{"lch":[77.7502795105496176,98.645271303892585,134.953797592355841],"luv":[77.7502795105496176,-69.6964700953403167,69.8089650895062732],"rgb":[0.0666666666666666657,0.866666666666666696,0.4],"xyz":[0.284844981973445932,0.527890377900925412,0.212588365332892548],"hpluv":[134.953797592355841,179.968822287066047,77.7502795105496176],"hsluv":[134.953797592355841,98.7815546318288114,77.7502795105496176]},"#11dd77":{"lch":[77.9703113413929714,91.9272626112809377,138.440937564009943],"luv":[77.9703113413929714,-68.7866216277350588,60.9837871589344616],"rgb":[0.0666666666666666657,0.866666666666666696,0.466666666666666674],"xyz":[0.294158994993647838,0.531615983109006263,0.261642167239290457],"hpluv":[138.440937564009943,169.723893920939275,77.9703113413929714],"hsluv":[138.440937564009943,98.8101351189296,77.9703113413929714]},"#11dd88":{"lch":[78.2321436880382919,84.7462074021241705,143.083472555296623],"luv":[78.2321436880382919,-67.7555613828794492,50.90288371727776],"rgb":[0.0666666666666666657,0.866666666666666696,0.533333333333333326],"xyz":[0.305299412095591638,0.536072149949783805,0.320315030642862597],"hpluv":[143.083472555296623,158.721324423216174,78.2321436880382919],"hsluv":[143.083472555296623,98.842607114853692,78.2321436880382919]},"#11dd99":{"lch":[78.5371801027425,77.5491781494168748,149.212222309724666],"luv":[78.5371801027425,-66.6201031545698,39.6942928810235145],"rgb":[0.0666666666666666657,0.866666666666666696,0.6],"xyz":[0.318356376204664393,0.541294935593413,0.389081708283980532],"hpluv":[149.212222309724666,147.710574340426348,78.5371801027425],"hsluv":[149.212222309724666,98.8784794246756746,78.5371801027425]},"#11ddaa":{"lch":[78.8865310768390771,70.9495750262721288,157.187121587348372],"luv":[78.8865310768390771,-65.3996173248364414,27.5087667875819264],"rgb":[0.0666666666666666657,0.866666666666666696,0.66666666666666663],"xyz":[0.333414069169916893,0.547318012779514085,0.468385557900979133],"hpluv":[157.187121587348372,137.807232739715857,78.8865310768390771],"hsluv":[157.187121587348372,98.9171828916172728,78.8865310768390771]},"#11ddbb":{"lch":[79.2810328759594398,65.7363215025974483,167.247861929956969],"luv":[79.2810328759594398,-64.1148749316804611,14.5102300873497096],"rgb":[0.0666666666666666657,0.866666666666666696,0.733333333333333282],"xyz":[0.350551661277253568,0.554173049622448843,0.558643542999620824],"hpluv":[167.247861929956969,130.572542181043048,79.2810328759594398],"hsluv":[167.247861929956969,98.9581049946704496,79.2810328759594398]},"#11ddcc":{"lch":[79.7212637056701396,62.7929370759791,179.209087927276954],"luv":[79.7212637056701396,-62.7869545437069903,0.866767416521026846],"rgb":[0.0666666666666666657,0.866666666666666696,0.8],"xyz":[0.369844036720975322,0.561889999799937701,0.660250053669891],"hpluv":[179.209087927276954,127.935201467087722,79.7212637056701396],"hsluv":[179.209087927276954,99.0006225499112,79.7212637056701396]},"#11dddd":{"lch":[80.2075587483664378,62.8503941808937796,192.177050630061132],"luv":[80.2075587483664378,-61.4362892449526,-13.2572400032679489],"rgb":[0.0666666666666666657,0.866666666666666696,0.866666666666666696],"xyz":[0.39136236227160881,0.570497330020191207,0.773579901569897],"hpluv":[192.177050630061132,131.767009082741112,80.2075587483664378],"hsluv":[192.177050630061132,99.0441302201931109,80.2075587483664378]},"#11ddee":{"lch":[80.7400249746916643,66.1629514334268691,204.757295278220823],"luv":[80.7400249746916643,-60.0819052998990202,-27.707414132610225],"rgb":[0.0666666666666666657,0.866666666666666696,0.933333333333333348],"xyz":[0.415174542537783575,0.580022202126661224,0.8989907176384202],"hpluv":[204.757295278220823,143.22067561241397,80.7400249746916643],"hsluv":[204.757295278220823,99.0880633014724594,80.7400249746916643]},"#11ddff":{"lch":[81.3185562290371848,72.4120095186264194,215.786316478612605],"luv":[81.3185562290371848,-58.7408753400219084,-42.3439332940817934],"rgb":[0.0666666666666666657,0.866666666666666696,1],"xyz":[0.441345590947614941,0.590490621490594,1.03682490593020216],"hpluv":[215.786316478612605,162.428807277722512,81.3185562290371848],"hsluv":[215.786316478612605,99.9999999999959925,81.3185562290371848]}} \ No newline at end of file diff --git a/vendor/github.com/lucasb-eyer/go-colorful/hsluv.go b/vendor/github.com/lucasb-eyer/go-colorful/hsluv.go new file mode 100644 index 0000000000..d19fb6443d --- /dev/null +++ b/vendor/github.com/lucasb-eyer/go-colorful/hsluv.go @@ -0,0 +1,207 @@ +package colorful + +import "math" + +// Source: https://github.com/hsluv/hsluv-go +// Under MIT License +// Modified so that Saturation and Luminance are in [0..1] instead of [0..100]. + +// HSLuv uses a rounded version of the D65. This has no impact on the final RGB +// values, but to keep high levels of accuracy for internal operations and when +// comparing to the test values, this modified white reference is used internally. +// +// See this GitHub thread for details on these values: +// https://github.com/hsluv/hsluv/issues/79 +var hSLuvD65 = [3]float64{0.95045592705167, 1.0, 1.089057750759878} + +func LuvLChToHSLuv(l, c, h float64) (float64, float64, float64) { + // [-1..1] but the code expects it to be [-100..100] + c *= 100.0 + l *= 100.0 + + var s, max float64 + if l > 99.9999999 || l < 0.00000001 { + s = 0.0 + } else { + max = maxChromaForLH(l, h) + s = c / max * 100.0 + } + return h, clamp01(s / 100.0), clamp01(l / 100.0) +} + +func HSLuvToLuvLCh(h, s, l float64) (float64, float64, float64) { + l *= 100.0 + s *= 100.0 + + var c, max float64 + if l > 99.9999999 || l < 0.00000001 { + c = 0.0 + } else { + max = maxChromaForLH(l, h) + c = max / 100.0 * s + } + + // c is [-100..100], but for LCh it's supposed to be almost [-1..1] + return clamp01(l / 100.0), c / 100.0, h +} + +func LuvLChToHPLuv(l, c, h float64) (float64, float64, float64) { + // [-1..1] but the code expects it to be [-100..100] + c *= 100.0 + l *= 100.0 + + var s, max float64 + if l > 99.9999999 || l < 0.00000001 { + s = 0.0 + } else { + max = maxSafeChromaForL(l) + s = c / max * 100.0 + } + return h, s / 100.0, l / 100.0 +} + +func HPLuvToLuvLCh(h, s, l float64) (float64, float64, float64) { + // [-1..1] but the code expects it to be [-100..100] + l *= 100.0 + s *= 100.0 + + var c, max float64 + if l > 99.9999999 || l < 0.00000001 { + c = 0.0 + } else { + max = maxSafeChromaForL(l) + c = max / 100.0 * s + } + return l / 100.0, c / 100.0, h +} + +// HSLuv creates a new Color from values in the HSLuv color space. +// Hue in [0..360], a Saturation [0..1], and a Luminance (lightness) in [0..1]. +// +// The returned color values are clamped (using .Clamped), so this will never output +// an invalid color. +func HSLuv(h, s, l float64) Color { + // HSLuv -> LuvLCh -> CIELUV -> CIEXYZ -> Linear RGB -> sRGB + l, u, v := LuvLChToLuv(HSLuvToLuvLCh(h, s, l)) + return LinearRgb(XyzToLinearRgb(LuvToXyzWhiteRef(l, u, v, hSLuvD65))).Clamped() +} + +// HPLuv creates a new Color from values in the HPLuv color space. +// Hue in [0..360], a Saturation [0..1], and a Luminance (lightness) in [0..1]. +// +// The returned color values are clamped (using .Clamped), so this will never output +// an invalid color. +func HPLuv(h, s, l float64) Color { + // HPLuv -> LuvLCh -> CIELUV -> CIEXYZ -> Linear RGB -> sRGB + l, u, v := LuvLChToLuv(HPLuvToLuvLCh(h, s, l)) + return LinearRgb(XyzToLinearRgb(LuvToXyzWhiteRef(l, u, v, hSLuvD65))).Clamped() +} + +// HSLuv returns the Hue, Saturation and Luminance of the color in the HSLuv +// color space. Hue in [0..360], a Saturation [0..1], and a Luminance +// (lightness) in [0..1]. +func (col Color) HSLuv() (h, s, l float64) { + // sRGB -> Linear RGB -> CIEXYZ -> CIELUV -> LuvLCh -> HSLuv + return LuvLChToHSLuv(col.LuvLChWhiteRef(hSLuvD65)) +} + +// HPLuv returns the Hue, Saturation and Luminance of the color in the HSLuv +// color space. Hue in [0..360], a Saturation [0..1], and a Luminance +// (lightness) in [0..1]. +// +// Note that HPLuv can only represent pastel colors, and so the Saturation +// value could be much larger than 1 for colors it can't represent. +func (col Color) HPLuv() (h, s, l float64) { + return LuvLChToHPLuv(col.LuvLChWhiteRef(hSLuvD65)) +} + +// DistanceHSLuv calculates Euclidan distance in the HSLuv colorspace. No idea +// how useful this is. +// +// The Hue value is divided by 100 before the calculation, so that H, S, and L +// have the same relative ranges. +func (c1 Color) DistanceHSLuv(c2 Color) float64 { + h1, s1, l1 := c1.HSLuv() + h2, s2, l2 := c2.HSLuv() + return math.Sqrt(sq((h1-h2)/100.0) + sq(s1-s2) + sq(l1-l2)) +} + +// DistanceHPLuv calculates Euclidean distance in the HPLuv colorspace. No idea +// how useful this is. +// +// The Hue value is divided by 100 before the calculation, so that H, S, and L +// have the same relative ranges. +func (c1 Color) DistanceHPLuv(c2 Color) float64 { + h1, s1, l1 := c1.HPLuv() + h2, s2, l2 := c2.HPLuv() + return math.Sqrt(sq((h1-h2)/100.0) + sq(s1-s2) + sq(l1-l2)) +} + +var m = [3][3]float64{ + {3.2409699419045214, -1.5373831775700935, -0.49861076029300328}, + {-0.96924363628087983, 1.8759675015077207, 0.041555057407175613}, + {0.055630079696993609, -0.20397695888897657, 1.0569715142428786}, +} + +const kappa = 903.2962962962963 +const epsilon = 0.0088564516790356308 + +func maxChromaForLH(l, h float64) float64 { + hRad := h / 360.0 * math.Pi * 2.0 + minLength := math.MaxFloat64 + for _, line := range getBounds(l) { + length := lengthOfRayUntilIntersect(hRad, line[0], line[1]) + if length > 0.0 && length < minLength { + minLength = length + } + } + return minLength +} + +func getBounds(l float64) [6][2]float64 { + var sub2 float64 + var ret [6][2]float64 + sub1 := math.Pow(l+16.0, 3.0) / 1560896.0 + if sub1 > epsilon { + sub2 = sub1 + } else { + sub2 = l / kappa + } + for i := range m { + for k := 0; k < 2; k++ { + top1 := (284517.0*m[i][0] - 94839.0*m[i][2]) * sub2 + top2 := (838422.0*m[i][2]+769860.0*m[i][1]+731718.0*m[i][0])*l*sub2 - 769860.0*float64(k)*l + bottom := (632260.0*m[i][2]-126452.0*m[i][1])*sub2 + 126452.0*float64(k) + ret[i*2+k][0] = top1 / bottom + ret[i*2+k][1] = top2 / bottom + } + } + return ret +} + +func lengthOfRayUntilIntersect(theta, x, y float64) (length float64) { + length = y / (math.Sin(theta) - x*math.Cos(theta)) + return +} + +func maxSafeChromaForL(l float64) float64 { + minLength := math.MaxFloat64 + for _, line := range getBounds(l) { + m1 := line[0] + b1 := line[1] + x := intersectLineLine(m1, b1, -1.0/m1, 0.0) + dist := distanceFromPole(x, b1+x*m1) + if dist < minLength { + minLength = dist + } + } + return minLength +} + +func intersectLineLine(x1, y1, x2, y2 float64) float64 { + return (y1 - y2) / (x2 - x1) +} + +func distanceFromPole(x, y float64) float64 { + return math.Sqrt(math.Pow(x, 2.0) + math.Pow(y, 2.0)) +} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/soft_palettegen.go b/vendor/github.com/lucasb-eyer/go-colorful/soft_palettegen.go new file mode 100644 index 0000000000..9f7bf6f7c7 --- /dev/null +++ b/vendor/github.com/lucasb-eyer/go-colorful/soft_palettegen.go @@ -0,0 +1,185 @@ +// Largely inspired by the descriptions in http://lab.medialab.sciences-po.fr/iwanthue/ +// but written from scratch. + +package colorful + +import ( + "fmt" + "math" + "math/rand" +) + +// The algorithm works in L*a*b* color space and converts to RGB in the end. +// L* in [0..1], a* and b* in [-1..1] +type lab_t struct { + L, A, B float64 +} + +type SoftPaletteSettings struct { + // A function which can be used to restrict the allowed color-space. + CheckColor func(l, a, b float64) bool + + // The higher, the better quality but the slower. Usually two figures. + Iterations int + + // Use up to 160000 or 8000 samples of the L*a*b* space (and thus calls to CheckColor). + // Set this to true only if your CheckColor shapes the Lab space weirdly. + ManySamples bool +} + +// Yeah, windows-stype Foo, FooEx, screw you golang... +// Uses K-means to cluster the color-space and return the means of the clusters +// as a new palette of distinctive colors. Falls back to K-medoid if the mean +// happens to fall outside of the color-space, which can only happen if you +// specify a CheckColor function. +func SoftPaletteEx(colorsCount int, settings SoftPaletteSettings) ([]Color, error) { + + // Checks whether it's a valid RGB and also fulfills the potentially provided constraint. + check := func(col lab_t) bool { + c := Lab(col.L, col.A, col.B) + return c.IsValid() && (settings.CheckColor == nil || settings.CheckColor(col.L, col.A, col.B)) + } + + // Sample the color space. These will be the points k-means is run on. + dl := 0.05 + dab := 0.1 + if settings.ManySamples { + dl = 0.01 + dab = 0.05 + } + + samples := make([]lab_t, 0, int(1.0/dl*2.0/dab*2.0/dab)) + for l := 0.0; l <= 1.0; l += dl { + for a := -1.0; a <= 1.0; a += dab { + for b := -1.0; b <= 1.0; b += dab { + if check(lab_t{l, a, b}) { + samples = append(samples, lab_t{l, a, b}) + } + } + } + } + + // That would cause some infinite loops down there... + if len(samples) < colorsCount { + return nil, fmt.Errorf("palettegen: more colors requested (%v) than samples available (%v). Your requested color count may be wrong, you might want to use many samples or your constraint function makes the valid color space too small", colorsCount, len(samples)) + } else if len(samples) == colorsCount { + return labs2cols(samples), nil // Oops? + } + + // We take the initial means out of the samples, so they are in fact medoids. + // This helps us avoid infinite loops or arbitrary cutoffs with too restrictive constraints. + means := make([]lab_t, colorsCount) + for i := 0; i < colorsCount; i++ { + for means[i] = samples[rand.Intn(len(samples))]; in(means, i, means[i]); means[i] = samples[rand.Intn(len(samples))] { + } + } + + clusters := make([]int, len(samples)) + samples_used := make([]bool, len(samples)) + + // The actual k-means/medoid iterations + for i := 0; i < settings.Iterations; i++ { + // Reassing the samples to clusters, i.e. to their closest mean. + // By the way, also check if any sample is used as a medoid and if so, mark that. + for isample, sample := range samples { + samples_used[isample] = false + mindist := math.Inf(+1) + for imean, mean := range means { + dist := lab_dist(sample, mean) + if dist < mindist { + mindist = dist + clusters[isample] = imean + } + + // Mark samples which are used as a medoid. + if lab_eq(sample, mean) { + samples_used[isample] = true + } + } + } + + // Compute new means according to the samples. + for imean := range means { + // The new mean is the average of all samples belonging to it.. + nsamples := 0 + newmean := lab_t{0.0, 0.0, 0.0} + for isample, sample := range samples { + if clusters[isample] == imean { + nsamples++ + newmean.L += sample.L + newmean.A += sample.A + newmean.B += sample.B + } + } + if nsamples > 0 { + newmean.L /= float64(nsamples) + newmean.A /= float64(nsamples) + newmean.B /= float64(nsamples) + } else { + // That mean doesn't have any samples? Get a new mean from the sample list! + var inewmean int + for inewmean = rand.Intn(len(samples_used)); samples_used[inewmean]; inewmean = rand.Intn(len(samples_used)) { + } + newmean = samples[inewmean] + samples_used[inewmean] = true + } + + // But now we still need to check whether the new mean is an allowed color. + if nsamples > 0 && check(newmean) { + // It does, life's good (TM) + means[imean] = newmean + } else { + // New mean isn't an allowed color or doesn't have any samples! + // Switch to medoid mode and pick the closest (unused) sample. + // This should always find something thanks to len(samples) >= colorsCount + mindist := math.Inf(+1) + for isample, sample := range samples { + if !samples_used[isample] { + dist := lab_dist(sample, newmean) + if dist < mindist { + mindist = dist + newmean = sample + } + } + } + } + } + } + return labs2cols(means), nil +} + +// A wrapper which uses common parameters. +func SoftPalette(colorsCount int) ([]Color, error) { + return SoftPaletteEx(colorsCount, SoftPaletteSettings{nil, 50, false}) +} + +func in(haystack []lab_t, upto int, needle lab_t) bool { + for i := 0; i < upto && i < len(haystack); i++ { + if haystack[i] == needle { + return true + } + } + return false +} + +const LAB_DELTA = 1e-6 + +func lab_eq(lab1, lab2 lab_t) bool { + return math.Abs(lab1.L-lab2.L) < LAB_DELTA && + math.Abs(lab1.A-lab2.A) < LAB_DELTA && + math.Abs(lab1.B-lab2.B) < LAB_DELTA +} + +// That's faster than using colorful's DistanceLab since we would have to +// convert back and forth for that. Here is no conversion. +func lab_dist(lab1, lab2 lab_t) float64 { + return math.Sqrt(sq(lab1.L-lab2.L) + sq(lab1.A-lab2.A) + sq(lab1.B-lab2.B)) +} + +func labs2cols(labs []lab_t) (cols []Color) { + cols = make([]Color, len(labs)) + for k, v := range labs { + cols[k] = Lab(v.L, v.A, v.B) + } + return cols +} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/warm_palettegen.go b/vendor/github.com/lucasb-eyer/go-colorful/warm_palettegen.go new file mode 100644 index 0000000000..00f42a5cc7 --- /dev/null +++ b/vendor/github.com/lucasb-eyer/go-colorful/warm_palettegen.go @@ -0,0 +1,25 @@ +package colorful + +import ( + "math/rand" +) + +// Uses the HSV color space to generate colors with similar S,V but distributed +// evenly along their Hue. This is fast but not always pretty. +// If you've got time to spare, use Lab (the non-fast below). +func FastWarmPalette(colorsCount int) (colors []Color) { + colors = make([]Color, colorsCount) + + for i := 0; i < colorsCount; i++ { + colors[i] = Hsv(float64(i)*(360.0/float64(colorsCount)), 0.55+rand.Float64()*0.2, 0.35+rand.Float64()*0.2) + } + return +} + +func WarmPalette(colorsCount int) ([]Color, error) { + warmy := func(l, a, b float64) bool { + _, c, _ := LabToHcl(l, a, b) + return 0.1 <= c && c <= 0.4 && 0.2 <= l && l <= 0.5 + } + return SoftPaletteEx(colorsCount, SoftPaletteSettings{warmy, 50, true}) +} diff --git a/vendor/github.com/mattn/go-colorable/LICENSE b/vendor/github.com/mattn/go-colorable/LICENSE new file mode 100644 index 0000000000..91b5cef30e --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Yasuhiro Matsumoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mattn/go-colorable/README.md b/vendor/github.com/mattn/go-colorable/README.md new file mode 100644 index 0000000000..ca0483711c --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/README.md @@ -0,0 +1,48 @@ +# go-colorable + +[![Build Status](https://github.com/mattn/go-colorable/workflows/test/badge.svg)](https://github.com/mattn/go-colorable/actions?query=workflow%3Atest) +[![Codecov](https://codecov.io/gh/mattn/go-colorable/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-colorable) +[![GoDoc](https://godoc.org/github.com/mattn/go-colorable?status.svg)](http://godoc.org/github.com/mattn/go-colorable) +[![Go Report Card](https://goreportcard.com/badge/mattn/go-colorable)](https://goreportcard.com/report/mattn/go-colorable) + +Colorable writer for windows. + +For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.) +This package is possible to handle escape sequence for ansi color on windows. + +## Too Bad! + +![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png) + + +## So Good! + +![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png) + +## Usage + +```go +logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true}) +logrus.SetOutput(colorable.NewColorableStdout()) + +logrus.Info("succeeded") +logrus.Warn("not correct") +logrus.Error("something error") +logrus.Fatal("panic") +``` + +You can compile above code on non-windows OSs. + +## Installation + +``` +$ go get github.com/mattn/go-colorable +``` + +# License + +MIT + +# Author + +Yasuhiro Matsumoto (a.k.a mattn) diff --git a/vendor/github.com/mattn/go-colorable/colorable_appengine.go b/vendor/github.com/mattn/go-colorable/colorable_appengine.go new file mode 100644 index 0000000000..416d1bbbf8 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/colorable_appengine.go @@ -0,0 +1,38 @@ +//go:build appengine +// +build appengine + +package colorable + +import ( + "io" + "os" + + _ "github.com/mattn/go-isatty" +) + +// NewColorable returns new instance of Writer which handles escape sequence. +func NewColorable(file *os.File) io.Writer { + if file == nil { + panic("nil passed instead of *os.File to NewColorable()") + } + + return file +} + +// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. +func NewColorableStdout() io.Writer { + return os.Stdout +} + +// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. +func NewColorableStderr() io.Writer { + return os.Stderr +} + +// EnableColorsStdout enable colors if possible. +func EnableColorsStdout(enabled *bool) func() { + if enabled != nil { + *enabled = true + } + return func() {} +} diff --git a/vendor/github.com/mattn/go-colorable/colorable_others.go b/vendor/github.com/mattn/go-colorable/colorable_others.go new file mode 100644 index 0000000000..766d94603a --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/colorable_others.go @@ -0,0 +1,38 @@ +//go:build !windows && !appengine +// +build !windows,!appengine + +package colorable + +import ( + "io" + "os" + + _ "github.com/mattn/go-isatty" +) + +// NewColorable returns new instance of Writer which handles escape sequence. +func NewColorable(file *os.File) io.Writer { + if file == nil { + panic("nil passed instead of *os.File to NewColorable()") + } + + return file +} + +// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. +func NewColorableStdout() io.Writer { + return os.Stdout +} + +// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. +func NewColorableStderr() io.Writer { + return os.Stderr +} + +// EnableColorsStdout enable colors if possible. +func EnableColorsStdout(enabled *bool) func() { + if enabled != nil { + *enabled = true + } + return func() {} +} diff --git a/vendor/github.com/mattn/go-colorable/colorable_windows.go b/vendor/github.com/mattn/go-colorable/colorable_windows.go new file mode 100644 index 0000000000..1846ad5ab4 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/colorable_windows.go @@ -0,0 +1,1047 @@ +//go:build windows && !appengine +// +build windows,!appengine + +package colorable + +import ( + "bytes" + "io" + "math" + "os" + "strconv" + "strings" + "sync" + "syscall" + "unsafe" + + "github.com/mattn/go-isatty" +) + +const ( + foregroundBlue = 0x1 + foregroundGreen = 0x2 + foregroundRed = 0x4 + foregroundIntensity = 0x8 + foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) + backgroundBlue = 0x10 + backgroundGreen = 0x20 + backgroundRed = 0x40 + backgroundIntensity = 0x80 + backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) + commonLvbUnderscore = 0x8000 + + cENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4 +) + +const ( + genericRead = 0x80000000 + genericWrite = 0x40000000 +) + +const ( + consoleTextmodeBuffer = 0x1 +) + +type wchar uint16 +type short int16 +type dword uint32 +type word uint16 + +type coord struct { + x short + y short +} + +type smallRect struct { + left short + top short + right short + bottom short +} + +type consoleScreenBufferInfo struct { + size coord + cursorPosition coord + attributes word + window smallRect + maximumWindowSize coord +} + +type consoleCursorInfo struct { + size dword + visible int32 +} + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") + procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") + procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") + procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") + procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") + procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") + procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW") + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procSetConsoleMode = kernel32.NewProc("SetConsoleMode") + procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer") +) + +// Writer provides colorable Writer to the console +type Writer struct { + out io.Writer + handle syscall.Handle + althandle syscall.Handle + oldattr word + oldpos coord + rest bytes.Buffer + mutex sync.Mutex +} + +// NewColorable returns new instance of Writer which handles escape sequence from File. +func NewColorable(file *os.File) io.Writer { + if file == nil { + panic("nil passed instead of *os.File to NewColorable()") + } + + if isatty.IsTerminal(file.Fd()) { + var mode uint32 + if r, _, _ := procGetConsoleMode.Call(file.Fd(), uintptr(unsafe.Pointer(&mode))); r != 0 && mode&cENABLE_VIRTUAL_TERMINAL_PROCESSING != 0 { + return file + } + var csbi consoleScreenBufferInfo + handle := syscall.Handle(file.Fd()) + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} + } + return file +} + +// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. +func NewColorableStdout() io.Writer { + return NewColorable(os.Stdout) +} + +// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. +func NewColorableStderr() io.Writer { + return NewColorable(os.Stderr) +} + +var color256 = map[int]int{ + 0: 0x000000, + 1: 0x800000, + 2: 0x008000, + 3: 0x808000, + 4: 0x000080, + 5: 0x800080, + 6: 0x008080, + 7: 0xc0c0c0, + 8: 0x808080, + 9: 0xff0000, + 10: 0x00ff00, + 11: 0xffff00, + 12: 0x0000ff, + 13: 0xff00ff, + 14: 0x00ffff, + 15: 0xffffff, + 16: 0x000000, + 17: 0x00005f, + 18: 0x000087, + 19: 0x0000af, + 20: 0x0000d7, + 21: 0x0000ff, + 22: 0x005f00, + 23: 0x005f5f, + 24: 0x005f87, + 25: 0x005faf, + 26: 0x005fd7, + 27: 0x005fff, + 28: 0x008700, + 29: 0x00875f, + 30: 0x008787, + 31: 0x0087af, + 32: 0x0087d7, + 33: 0x0087ff, + 34: 0x00af00, + 35: 0x00af5f, + 36: 0x00af87, + 37: 0x00afaf, + 38: 0x00afd7, + 39: 0x00afff, + 40: 0x00d700, + 41: 0x00d75f, + 42: 0x00d787, + 43: 0x00d7af, + 44: 0x00d7d7, + 45: 0x00d7ff, + 46: 0x00ff00, + 47: 0x00ff5f, + 48: 0x00ff87, + 49: 0x00ffaf, + 50: 0x00ffd7, + 51: 0x00ffff, + 52: 0x5f0000, + 53: 0x5f005f, + 54: 0x5f0087, + 55: 0x5f00af, + 56: 0x5f00d7, + 57: 0x5f00ff, + 58: 0x5f5f00, + 59: 0x5f5f5f, + 60: 0x5f5f87, + 61: 0x5f5faf, + 62: 0x5f5fd7, + 63: 0x5f5fff, + 64: 0x5f8700, + 65: 0x5f875f, + 66: 0x5f8787, + 67: 0x5f87af, + 68: 0x5f87d7, + 69: 0x5f87ff, + 70: 0x5faf00, + 71: 0x5faf5f, + 72: 0x5faf87, + 73: 0x5fafaf, + 74: 0x5fafd7, + 75: 0x5fafff, + 76: 0x5fd700, + 77: 0x5fd75f, + 78: 0x5fd787, + 79: 0x5fd7af, + 80: 0x5fd7d7, + 81: 0x5fd7ff, + 82: 0x5fff00, + 83: 0x5fff5f, + 84: 0x5fff87, + 85: 0x5fffaf, + 86: 0x5fffd7, + 87: 0x5fffff, + 88: 0x870000, + 89: 0x87005f, + 90: 0x870087, + 91: 0x8700af, + 92: 0x8700d7, + 93: 0x8700ff, + 94: 0x875f00, + 95: 0x875f5f, + 96: 0x875f87, + 97: 0x875faf, + 98: 0x875fd7, + 99: 0x875fff, + 100: 0x878700, + 101: 0x87875f, + 102: 0x878787, + 103: 0x8787af, + 104: 0x8787d7, + 105: 0x8787ff, + 106: 0x87af00, + 107: 0x87af5f, + 108: 0x87af87, + 109: 0x87afaf, + 110: 0x87afd7, + 111: 0x87afff, + 112: 0x87d700, + 113: 0x87d75f, + 114: 0x87d787, + 115: 0x87d7af, + 116: 0x87d7d7, + 117: 0x87d7ff, + 118: 0x87ff00, + 119: 0x87ff5f, + 120: 0x87ff87, + 121: 0x87ffaf, + 122: 0x87ffd7, + 123: 0x87ffff, + 124: 0xaf0000, + 125: 0xaf005f, + 126: 0xaf0087, + 127: 0xaf00af, + 128: 0xaf00d7, + 129: 0xaf00ff, + 130: 0xaf5f00, + 131: 0xaf5f5f, + 132: 0xaf5f87, + 133: 0xaf5faf, + 134: 0xaf5fd7, + 135: 0xaf5fff, + 136: 0xaf8700, + 137: 0xaf875f, + 138: 0xaf8787, + 139: 0xaf87af, + 140: 0xaf87d7, + 141: 0xaf87ff, + 142: 0xafaf00, + 143: 0xafaf5f, + 144: 0xafaf87, + 145: 0xafafaf, + 146: 0xafafd7, + 147: 0xafafff, + 148: 0xafd700, + 149: 0xafd75f, + 150: 0xafd787, + 151: 0xafd7af, + 152: 0xafd7d7, + 153: 0xafd7ff, + 154: 0xafff00, + 155: 0xafff5f, + 156: 0xafff87, + 157: 0xafffaf, + 158: 0xafffd7, + 159: 0xafffff, + 160: 0xd70000, + 161: 0xd7005f, + 162: 0xd70087, + 163: 0xd700af, + 164: 0xd700d7, + 165: 0xd700ff, + 166: 0xd75f00, + 167: 0xd75f5f, + 168: 0xd75f87, + 169: 0xd75faf, + 170: 0xd75fd7, + 171: 0xd75fff, + 172: 0xd78700, + 173: 0xd7875f, + 174: 0xd78787, + 175: 0xd787af, + 176: 0xd787d7, + 177: 0xd787ff, + 178: 0xd7af00, + 179: 0xd7af5f, + 180: 0xd7af87, + 181: 0xd7afaf, + 182: 0xd7afd7, + 183: 0xd7afff, + 184: 0xd7d700, + 185: 0xd7d75f, + 186: 0xd7d787, + 187: 0xd7d7af, + 188: 0xd7d7d7, + 189: 0xd7d7ff, + 190: 0xd7ff00, + 191: 0xd7ff5f, + 192: 0xd7ff87, + 193: 0xd7ffaf, + 194: 0xd7ffd7, + 195: 0xd7ffff, + 196: 0xff0000, + 197: 0xff005f, + 198: 0xff0087, + 199: 0xff00af, + 200: 0xff00d7, + 201: 0xff00ff, + 202: 0xff5f00, + 203: 0xff5f5f, + 204: 0xff5f87, + 205: 0xff5faf, + 206: 0xff5fd7, + 207: 0xff5fff, + 208: 0xff8700, + 209: 0xff875f, + 210: 0xff8787, + 211: 0xff87af, + 212: 0xff87d7, + 213: 0xff87ff, + 214: 0xffaf00, + 215: 0xffaf5f, + 216: 0xffaf87, + 217: 0xffafaf, + 218: 0xffafd7, + 219: 0xffafff, + 220: 0xffd700, + 221: 0xffd75f, + 222: 0xffd787, + 223: 0xffd7af, + 224: 0xffd7d7, + 225: 0xffd7ff, + 226: 0xffff00, + 227: 0xffff5f, + 228: 0xffff87, + 229: 0xffffaf, + 230: 0xffffd7, + 231: 0xffffff, + 232: 0x080808, + 233: 0x121212, + 234: 0x1c1c1c, + 235: 0x262626, + 236: 0x303030, + 237: 0x3a3a3a, + 238: 0x444444, + 239: 0x4e4e4e, + 240: 0x585858, + 241: 0x626262, + 242: 0x6c6c6c, + 243: 0x767676, + 244: 0x808080, + 245: 0x8a8a8a, + 246: 0x949494, + 247: 0x9e9e9e, + 248: 0xa8a8a8, + 249: 0xb2b2b2, + 250: 0xbcbcbc, + 251: 0xc6c6c6, + 252: 0xd0d0d0, + 253: 0xdadada, + 254: 0xe4e4e4, + 255: 0xeeeeee, +} + +// `\033]0;TITLESTR\007` +func doTitleSequence(er *bytes.Reader) error { + var c byte + var err error + + c, err = er.ReadByte() + if err != nil { + return err + } + if c != '0' && c != '2' { + return nil + } + c, err = er.ReadByte() + if err != nil { + return err + } + if c != ';' { + return nil + } + title := make([]byte, 0, 80) + for { + c, err = er.ReadByte() + if err != nil { + return err + } + if c == 0x07 || c == '\n' { + break + } + title = append(title, c) + } + if len(title) > 0 { + title8, err := syscall.UTF16PtrFromString(string(title)) + if err == nil { + procSetConsoleTitle.Call(uintptr(unsafe.Pointer(title8))) + } + } + return nil +} + +// returns Atoi(s) unless s == "" in which case it returns def +func atoiWithDefault(s string, def int) (int, error) { + if s == "" { + return def, nil + } + return strconv.Atoi(s) +} + +// Write writes data on console +func (w *Writer) Write(data []byte) (n int, err error) { + w.mutex.Lock() + defer w.mutex.Unlock() + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + + handle := w.handle + + var er *bytes.Reader + if w.rest.Len() > 0 { + var rest bytes.Buffer + w.rest.WriteTo(&rest) + w.rest.Reset() + rest.Write(data) + er = bytes.NewReader(rest.Bytes()) + } else { + er = bytes.NewReader(data) + } + var plaintext bytes.Buffer +loop: + for { + c1, err := er.ReadByte() + if err != nil { + plaintext.WriteTo(w.out) + break loop + } + if c1 != 0x1b { + plaintext.WriteByte(c1) + continue + } + _, err = plaintext.WriteTo(w.out) + if err != nil { + break loop + } + c2, err := er.ReadByte() + if err != nil { + break loop + } + + switch c2 { + case '>': + continue + case ']': + w.rest.WriteByte(c1) + w.rest.WriteByte(c2) + er.WriteTo(&w.rest) + if bytes.IndexByte(w.rest.Bytes(), 0x07) == -1 { + break loop + } + er = bytes.NewReader(w.rest.Bytes()[2:]) + err := doTitleSequence(er) + if err != nil { + break loop + } + w.rest.Reset() + continue + // https://github.com/mattn/go-colorable/issues/27 + case '7': + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + w.oldpos = csbi.cursorPosition + continue + case '8': + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) + continue + case 0x5b: + // execute part after switch + default: + continue + } + + w.rest.WriteByte(c1) + w.rest.WriteByte(c2) + er.WriteTo(&w.rest) + + var buf bytes.Buffer + var m byte + for i, c := range w.rest.Bytes()[2:] { + if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { + m = c + er = bytes.NewReader(w.rest.Bytes()[2+i+1:]) + w.rest.Reset() + break + } + buf.Write([]byte(string(c))) + } + if m == 0 { + break loop + } + + switch m { + case 'A': + n, err = atoiWithDefault(buf.String(), 1) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.y -= short(n) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'B': + n, err = atoiWithDefault(buf.String(), 1) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.y += short(n) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'C': + n, err = atoiWithDefault(buf.String(), 1) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x += short(n) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'D': + n, err = atoiWithDefault(buf.String(), 1) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x -= short(n) + if csbi.cursorPosition.x < 0 { + csbi.cursorPosition.x = 0 + } + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'E': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = 0 + csbi.cursorPosition.y += short(n) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'F': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = 0 + csbi.cursorPosition.y -= short(n) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'G': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + if n < 1 { + n = 1 + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = short(n - 1) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'H', 'f': + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + if buf.Len() > 0 { + token := strings.Split(buf.String(), ";") + switch len(token) { + case 1: + n1, err := strconv.Atoi(token[0]) + if err != nil { + continue + } + csbi.cursorPosition.y = short(n1 - 1) + case 2: + n1, err := strconv.Atoi(token[0]) + if err != nil { + continue + } + n2, err := strconv.Atoi(token[1]) + if err != nil { + continue + } + csbi.cursorPosition.x = short(n2 - 1) + csbi.cursorPosition.y = short(n1 - 1) + } + } else { + csbi.cursorPosition.y = 0 + } + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'J': + n := 0 + if buf.Len() > 0 { + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + } + var count, written dword + var cursor coord + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + switch n { + case 0: + cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} + count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) + case 1: + cursor = coord{x: csbi.window.left, y: csbi.window.top} + count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.window.top-csbi.cursorPosition.y)*dword(csbi.size.x) + case 2: + cursor = coord{x: csbi.window.left, y: csbi.window.top} + count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) + } + procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + case 'K': + n := 0 + if buf.Len() > 0 { + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + var cursor coord + var count, written dword + switch n { + case 0: + cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} + count = dword(csbi.size.x - csbi.cursorPosition.x) + case 1: + cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} + count = dword(csbi.size.x - csbi.cursorPosition.x) + case 2: + cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} + count = dword(csbi.size.x) + } + procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + case 'X': + n := 0 + if buf.Len() > 0 { + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + var cursor coord + var written dword + cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} + procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + case 'm': + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + attr := csbi.attributes + cs := buf.String() + if cs == "" { + procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr)) + continue + } + token := strings.Split(cs, ";") + for i := 0; i < len(token); i++ { + ns := token[i] + if n, err = strconv.Atoi(ns); err == nil { + switch { + case n == 0 || n == 100: + attr = w.oldattr + case n == 4: + attr |= commonLvbUnderscore + case (1 <= n && n <= 3) || n == 5: + attr |= foregroundIntensity + case n == 7 || n == 27: + attr = + (attr &^ (foregroundMask | backgroundMask)) | + ((attr & foregroundMask) << 4) | + ((attr & backgroundMask) >> 4) + case n == 22: + attr &^= foregroundIntensity + case n == 24: + attr &^= commonLvbUnderscore + case 30 <= n && n <= 37: + attr &= backgroundMask + if (n-30)&1 != 0 { + attr |= foregroundRed + } + if (n-30)&2 != 0 { + attr |= foregroundGreen + } + if (n-30)&4 != 0 { + attr |= foregroundBlue + } + case n == 38: // set foreground color. + if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") { + if n256, err := strconv.Atoi(token[i+2]); err == nil { + if n256foreAttr == nil { + n256setup() + } + attr &= backgroundMask + attr |= n256foreAttr[n256%len(n256foreAttr)] + i += 2 + } + } else if len(token) == 5 && token[i+1] == "2" { + var r, g, b int + r, _ = strconv.Atoi(token[i+2]) + g, _ = strconv.Atoi(token[i+3]) + b, _ = strconv.Atoi(token[i+4]) + i += 4 + if r > 127 { + attr |= foregroundRed + } + if g > 127 { + attr |= foregroundGreen + } + if b > 127 { + attr |= foregroundBlue + } + } else { + attr = attr & (w.oldattr & backgroundMask) + } + case n == 39: // reset foreground color. + attr &= backgroundMask + attr |= w.oldattr & foregroundMask + case 40 <= n && n <= 47: + attr &= foregroundMask + if (n-40)&1 != 0 { + attr |= backgroundRed + } + if (n-40)&2 != 0 { + attr |= backgroundGreen + } + if (n-40)&4 != 0 { + attr |= backgroundBlue + } + case n == 48: // set background color. + if i < len(token)-2 && token[i+1] == "5" { + if n256, err := strconv.Atoi(token[i+2]); err == nil { + if n256backAttr == nil { + n256setup() + } + attr &= foregroundMask + attr |= n256backAttr[n256%len(n256backAttr)] + i += 2 + } + } else if len(token) == 5 && token[i+1] == "2" { + var r, g, b int + r, _ = strconv.Atoi(token[i+2]) + g, _ = strconv.Atoi(token[i+3]) + b, _ = strconv.Atoi(token[i+4]) + i += 4 + if r > 127 { + attr |= backgroundRed + } + if g > 127 { + attr |= backgroundGreen + } + if b > 127 { + attr |= backgroundBlue + } + } else { + attr = attr & (w.oldattr & foregroundMask) + } + case n == 49: // reset foreground color. + attr &= foregroundMask + attr |= w.oldattr & backgroundMask + case 90 <= n && n <= 97: + attr = (attr & backgroundMask) + attr |= foregroundIntensity + if (n-90)&1 != 0 { + attr |= foregroundRed + } + if (n-90)&2 != 0 { + attr |= foregroundGreen + } + if (n-90)&4 != 0 { + attr |= foregroundBlue + } + case 100 <= n && n <= 107: + attr = (attr & foregroundMask) + attr |= backgroundIntensity + if (n-100)&1 != 0 { + attr |= backgroundRed + } + if (n-100)&2 != 0 { + attr |= backgroundGreen + } + if (n-100)&4 != 0 { + attr |= backgroundBlue + } + } + procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr)) + } + } + case 'h': + var ci consoleCursorInfo + cs := buf.String() + if cs == "5>" { + procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 0 + procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + } else if cs == "?25" { + procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 1 + procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + } else if cs == "?1049" { + if w.althandle == 0 { + h, _, _ := procCreateConsoleScreenBuffer.Call(uintptr(genericRead|genericWrite), 0, 0, uintptr(consoleTextmodeBuffer), 0, 0) + w.althandle = syscall.Handle(h) + if w.althandle != 0 { + handle = w.althandle + } + } + } + case 'l': + var ci consoleCursorInfo + cs := buf.String() + if cs == "5>" { + procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 1 + procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + } else if cs == "?25" { + procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 0 + procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + } else if cs == "?1049" { + if w.althandle != 0 { + syscall.CloseHandle(w.althandle) + w.althandle = 0 + handle = w.handle + } + } + case 's': + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + w.oldpos = csbi.cursorPosition + case 'u': + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) + } + } + + return len(data), nil +} + +type consoleColor struct { + rgb int + red bool + green bool + blue bool + intensity bool +} + +func (c consoleColor) foregroundAttr() (attr word) { + if c.red { + attr |= foregroundRed + } + if c.green { + attr |= foregroundGreen + } + if c.blue { + attr |= foregroundBlue + } + if c.intensity { + attr |= foregroundIntensity + } + return +} + +func (c consoleColor) backgroundAttr() (attr word) { + if c.red { + attr |= backgroundRed + } + if c.green { + attr |= backgroundGreen + } + if c.blue { + attr |= backgroundBlue + } + if c.intensity { + attr |= backgroundIntensity + } + return +} + +var color16 = []consoleColor{ + {0x000000, false, false, false, false}, + {0x000080, false, false, true, false}, + {0x008000, false, true, false, false}, + {0x008080, false, true, true, false}, + {0x800000, true, false, false, false}, + {0x800080, true, false, true, false}, + {0x808000, true, true, false, false}, + {0xc0c0c0, true, true, true, false}, + {0x808080, false, false, false, true}, + {0x0000ff, false, false, true, true}, + {0x00ff00, false, true, false, true}, + {0x00ffff, false, true, true, true}, + {0xff0000, true, false, false, true}, + {0xff00ff, true, false, true, true}, + {0xffff00, true, true, false, true}, + {0xffffff, true, true, true, true}, +} + +type hsv struct { + h, s, v float32 +} + +func (a hsv) dist(b hsv) float32 { + dh := a.h - b.h + switch { + case dh > 0.5: + dh = 1 - dh + case dh < -0.5: + dh = -1 - dh + } + ds := a.s - b.s + dv := a.v - b.v + return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv))) +} + +func toHSV(rgb int) hsv { + r, g, b := float32((rgb&0xFF0000)>>16)/256.0, + float32((rgb&0x00FF00)>>8)/256.0, + float32(rgb&0x0000FF)/256.0 + min, max := minmax3f(r, g, b) + h := max - min + if h > 0 { + if max == r { + h = (g - b) / h + if h < 0 { + h += 6 + } + } else if max == g { + h = 2 + (b-r)/h + } else { + h = 4 + (r-g)/h + } + } + h /= 6.0 + s := max - min + if max != 0 { + s /= max + } + v := max + return hsv{h: h, s: s, v: v} +} + +type hsvTable []hsv + +func toHSVTable(rgbTable []consoleColor) hsvTable { + t := make(hsvTable, len(rgbTable)) + for i, c := range rgbTable { + t[i] = toHSV(c.rgb) + } + return t +} + +func (t hsvTable) find(rgb int) consoleColor { + hsv := toHSV(rgb) + n := 7 + l := float32(5.0) + for i, p := range t { + d := hsv.dist(p) + if d < l { + l, n = d, i + } + } + return color16[n] +} + +func minmax3f(a, b, c float32) (min, max float32) { + if a < b { + if b < c { + return a, c + } else if a < c { + return a, b + } else { + return c, b + } + } else { + if a < c { + return b, c + } else if b < c { + return b, a + } else { + return c, a + } + } +} + +var n256foreAttr []word +var n256backAttr []word + +func n256setup() { + n256foreAttr = make([]word, 256) + n256backAttr = make([]word, 256) + t := toHSVTable(color16) + for i, rgb := range color256 { + c := t.find(rgb) + n256foreAttr[i] = c.foregroundAttr() + n256backAttr[i] = c.backgroundAttr() + } +} + +// EnableColorsStdout enable colors if possible. +func EnableColorsStdout(enabled *bool) func() { + var mode uint32 + h := os.Stdout.Fd() + if r, _, _ := procGetConsoleMode.Call(h, uintptr(unsafe.Pointer(&mode))); r != 0 { + if r, _, _ = procSetConsoleMode.Call(h, uintptr(mode|cENABLE_VIRTUAL_TERMINAL_PROCESSING)); r != 0 { + if enabled != nil { + *enabled = true + } + return func() { + procSetConsoleMode.Call(h, uintptr(mode)) + } + } + } + if enabled != nil { + *enabled = true + } + return func() {} +} diff --git a/vendor/github.com/mattn/go-colorable/go.mod b/vendor/github.com/mattn/go-colorable/go.mod new file mode 100644 index 0000000000..27351c0278 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/go.mod @@ -0,0 +1,8 @@ +module github.com/mattn/go-colorable + +require ( + github.com/mattn/go-isatty v0.0.14 + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect +) + +go 1.13 diff --git a/vendor/github.com/mattn/go-colorable/go.sum b/vendor/github.com/mattn/go-colorable/go.sum new file mode 100644 index 0000000000..40c33b333c --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/go.sum @@ -0,0 +1,5 @@ +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/vendor/github.com/mattn/go-colorable/go.test.sh b/vendor/github.com/mattn/go-colorable/go.test.sh new file mode 100644 index 0000000000..012162b077 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/go.test.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -e +echo "" > coverage.txt + +for d in $(go list ./... | grep -v vendor); do + go test -race -coverprofile=profile.out -covermode=atomic "$d" + if [ -f profile.out ]; then + cat profile.out >> coverage.txt + rm profile.out + fi +done diff --git a/vendor/github.com/mattn/go-colorable/noncolorable.go b/vendor/github.com/mattn/go-colorable/noncolorable.go new file mode 100644 index 0000000000..05d6f74bf6 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/noncolorable.go @@ -0,0 +1,57 @@ +package colorable + +import ( + "bytes" + "io" +) + +// NonColorable holds writer but removes escape sequence. +type NonColorable struct { + out io.Writer +} + +// NewNonColorable returns new instance of Writer which removes escape sequence from Writer. +func NewNonColorable(w io.Writer) io.Writer { + return &NonColorable{out: w} +} + +// Write writes data on console +func (w *NonColorable) Write(data []byte) (n int, err error) { + er := bytes.NewReader(data) + var plaintext bytes.Buffer +loop: + for { + c1, err := er.ReadByte() + if err != nil { + plaintext.WriteTo(w.out) + break loop + } + if c1 != 0x1b { + plaintext.WriteByte(c1) + continue + } + _, err = plaintext.WriteTo(w.out) + if err != nil { + break loop + } + c2, err := er.ReadByte() + if err != nil { + break loop + } + if c2 != 0x5b { + continue + } + + for { + c, err := er.ReadByte() + if err != nil { + break loop + } + if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { + break + } + } + } + + return len(data), nil +} diff --git a/vendor/github.com/mattn/go-isatty/LICENSE b/vendor/github.com/mattn/go-isatty/LICENSE new file mode 100644 index 0000000000..65dc692b6b --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) Yasuhiro MATSUMOTO + +MIT License (Expat) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/mattn/go-isatty/README.md b/vendor/github.com/mattn/go-isatty/README.md new file mode 100644 index 0000000000..38418353e3 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/README.md @@ -0,0 +1,50 @@ +# go-isatty + +[![Godoc Reference](https://godoc.org/github.com/mattn/go-isatty?status.svg)](http://godoc.org/github.com/mattn/go-isatty) +[![Codecov](https://codecov.io/gh/mattn/go-isatty/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-isatty) +[![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master) +[![Go Report Card](https://goreportcard.com/badge/mattn/go-isatty)](https://goreportcard.com/report/mattn/go-isatty) + +isatty for golang + +## Usage + +```go +package main + +import ( + "fmt" + "github.com/mattn/go-isatty" + "os" +) + +func main() { + if isatty.IsTerminal(os.Stdout.Fd()) { + fmt.Println("Is Terminal") + } else if isatty.IsCygwinTerminal(os.Stdout.Fd()) { + fmt.Println("Is Cygwin/MSYS2 Terminal") + } else { + fmt.Println("Is Not Terminal") + } +} +``` + +## Installation + +``` +$ go get github.com/mattn/go-isatty +``` + +## License + +MIT + +## Author + +Yasuhiro Matsumoto (a.k.a mattn) + +## Thanks + +* k-takata: base idea for IsCygwinTerminal + + https://github.com/k-takata/go-iscygpty diff --git a/vendor/github.com/mattn/go-isatty/doc.go b/vendor/github.com/mattn/go-isatty/doc.go new file mode 100644 index 0000000000..17d4f90ebc --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/doc.go @@ -0,0 +1,2 @@ +// Package isatty implements interface to isatty +package isatty diff --git a/vendor/github.com/mattn/go-isatty/go.mod b/vendor/github.com/mattn/go-isatty/go.mod new file mode 100644 index 0000000000..c9a20b7f3f --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/go.mod @@ -0,0 +1,5 @@ +module github.com/mattn/go-isatty + +go 1.12 + +require golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c diff --git a/vendor/github.com/mattn/go-isatty/go.sum b/vendor/github.com/mattn/go-isatty/go.sum new file mode 100644 index 0000000000..912e29cbc1 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/mattn/go-isatty/go.test.sh b/vendor/github.com/mattn/go-isatty/go.test.sh new file mode 100644 index 0000000000..012162b077 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/go.test.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -e +echo "" > coverage.txt + +for d in $(go list ./... | grep -v vendor); do + go test -race -coverprofile=profile.out -covermode=atomic "$d" + if [ -f profile.out ]; then + cat profile.out >> coverage.txt + rm profile.out + fi +done diff --git a/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/vendor/github.com/mattn/go-isatty/isatty_bsd.go new file mode 100644 index 0000000000..39bbcf00f0 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_bsd.go @@ -0,0 +1,19 @@ +//go:build (darwin || freebsd || openbsd || netbsd || dragonfly) && !appengine +// +build darwin freebsd openbsd netbsd dragonfly +// +build !appengine + +package isatty + +import "golang.org/x/sys/unix" + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + _, err := unix.IoctlGetTermios(int(fd), unix.TIOCGETA) + return err == nil +} + +// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_others.go b/vendor/github.com/mattn/go-isatty/isatty_others.go new file mode 100644 index 0000000000..31503226f6 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_others.go @@ -0,0 +1,16 @@ +//go:build appengine || js || nacl || wasm +// +build appengine js nacl wasm + +package isatty + +// IsTerminal returns true if the file descriptor is terminal which +// is always false on js and appengine classic which is a sandboxed PaaS. +func IsTerminal(fd uintptr) bool { + return false +} + +// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_plan9.go b/vendor/github.com/mattn/go-isatty/isatty_plan9.go new file mode 100644 index 0000000000..bae7f9bb3d --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_plan9.go @@ -0,0 +1,23 @@ +//go:build plan9 +// +build plan9 + +package isatty + +import ( + "syscall" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd uintptr) bool { + path, err := syscall.Fd2path(int(fd)) + if err != nil { + return false + } + return path == "/dev/cons" || path == "/mnt/term/dev/cons" +} + +// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_solaris.go b/vendor/github.com/mattn/go-isatty/isatty_solaris.go new file mode 100644 index 0000000000..0c3acf2dc2 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_solaris.go @@ -0,0 +1,21 @@ +//go:build solaris && !appengine +// +build solaris,!appengine + +package isatty + +import ( + "golang.org/x/sys/unix" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +// see: https://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/isatty.c +func IsTerminal(fd uintptr) bool { + _, err := unix.IoctlGetTermio(int(fd), unix.TCGETA) + return err == nil +} + +// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go new file mode 100644 index 0000000000..67787657fb --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go @@ -0,0 +1,19 @@ +//go:build (linux || aix || zos) && !appengine +// +build linux aix zos +// +build !appengine + +package isatty + +import "golang.org/x/sys/unix" + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + _, err := unix.IoctlGetTermios(int(fd), unix.TCGETS) + return err == nil +} + +// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_windows.go b/vendor/github.com/mattn/go-isatty/isatty_windows.go new file mode 100644 index 0000000000..8e3c99171b --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_windows.go @@ -0,0 +1,125 @@ +//go:build windows && !appengine +// +build windows,!appengine + +package isatty + +import ( + "errors" + "strings" + "syscall" + "unicode/utf16" + "unsafe" +) + +const ( + objectNameInfo uintptr = 1 + fileNameInfo = 2 + fileTypePipe = 3 +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + ntdll = syscall.NewLazyDLL("ntdll.dll") + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx") + procGetFileType = kernel32.NewProc("GetFileType") + procNtQueryObject = ntdll.NewProc("NtQueryObject") +) + +func init() { + // Check if GetFileInformationByHandleEx is available. + if procGetFileInformationByHandleEx.Find() != nil { + procGetFileInformationByHandleEx = nil + } +} + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} + +// Check pipe name is used for cygwin/msys2 pty. +// Cygwin/MSYS2 PTY has a name like: +// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master +func isCygwinPipeName(name string) bool { + token := strings.Split(name, "-") + if len(token) < 5 { + return false + } + + if token[0] != `\msys` && + token[0] != `\cygwin` && + token[0] != `\Device\NamedPipe\msys` && + token[0] != `\Device\NamedPipe\cygwin` { + return false + } + + if token[1] == "" { + return false + } + + if !strings.HasPrefix(token[2], "pty") { + return false + } + + if token[3] != `from` && token[3] != `to` { + return false + } + + if token[4] != "master" { + return false + } + + return true +} + +// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler +// since GetFileInformationByHandleEx is not available under windows Vista and still some old fashion +// guys are using Windows XP, this is a workaround for those guys, it will also work on system from +// Windows vista to 10 +// see https://stackoverflow.com/a/18792477 for details +func getFileNameByHandle(fd uintptr) (string, error) { + if procNtQueryObject == nil { + return "", errors.New("ntdll.dll: NtQueryObject not supported") + } + + var buf [4 + syscall.MAX_PATH]uint16 + var result int + r, _, e := syscall.Syscall6(procNtQueryObject.Addr(), 5, + fd, objectNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(2*len(buf)), uintptr(unsafe.Pointer(&result)), 0) + if r != 0 { + return "", e + } + return string(utf16.Decode(buf[4 : 4+buf[0]/2])), nil +} + +// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 +// terminal. +func IsCygwinTerminal(fd uintptr) bool { + if procGetFileInformationByHandleEx == nil { + name, err := getFileNameByHandle(fd) + if err != nil { + return false + } + return isCygwinPipeName(name) + } + + // Cygwin/msys's pty is a pipe. + ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0) + if ft != fileTypePipe || e != 0 { + return false + } + + var buf [2 + syscall.MAX_PATH]uint16 + r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), + 4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)), + uintptr(len(buf)*2), 0, 0) + if r == 0 || e != 0 { + return false + } + + l := *(*uint32)(unsafe.Pointer(&buf)) + return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2]))) +} diff --git a/vendor/github.com/mattn/go-runewidth/.travis.yml b/vendor/github.com/mattn/go-runewidth/.travis.yml new file mode 100644 index 0000000000..6a21813a3e --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/.travis.yml @@ -0,0 +1,16 @@ +language: go +sudo: false +go: + - 1.13.x + - tip + +before_install: + - go get -t -v ./... + +script: + - go generate + - git diff --cached --exit-code + - ./go.test.sh + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/mattn/go-runewidth/LICENSE b/vendor/github.com/mattn/go-runewidth/LICENSE new file mode 100644 index 0000000000..91b5cef30e --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Yasuhiro Matsumoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mattn/go-runewidth/README.md b/vendor/github.com/mattn/go-runewidth/README.md new file mode 100644 index 0000000000..aa56ab96c2 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/README.md @@ -0,0 +1,27 @@ +go-runewidth +============ + +[![Build Status](https://travis-ci.org/mattn/go-runewidth.png?branch=master)](https://travis-ci.org/mattn/go-runewidth) +[![Codecov](https://codecov.io/gh/mattn/go-runewidth/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-runewidth) +[![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth) +[![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth) + +Provides functions to get fixed width of the character or string. + +Usage +----- + +```go +runewidth.StringWidth("つのだ☆HIRO") == 12 +``` + + +Author +------ + +Yasuhiro Matsumoto + +License +------- + +under the MIT License: http://mattn.mit-license.org/2013 diff --git a/vendor/github.com/mattn/go-runewidth/go.mod b/vendor/github.com/mattn/go-runewidth/go.mod new file mode 100644 index 0000000000..62dba1bfc8 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/go.mod @@ -0,0 +1,5 @@ +module github.com/mattn/go-runewidth + +go 1.9 + +require github.com/rivo/uniseg v0.2.0 diff --git a/vendor/github.com/mattn/go-runewidth/go.sum b/vendor/github.com/mattn/go-runewidth/go.sum new file mode 100644 index 0000000000..03f902d56d --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/go.sum @@ -0,0 +1,2 @@ +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= diff --git a/vendor/github.com/mattn/go-runewidth/go.test.sh b/vendor/github.com/mattn/go-runewidth/go.test.sh new file mode 100644 index 0000000000..012162b077 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/go.test.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -e +echo "" > coverage.txt + +for d in $(go list ./... | grep -v vendor); do + go test -race -coverprofile=profile.out -covermode=atomic "$d" + if [ -f profile.out ]; then + cat profile.out >> coverage.txt + rm profile.out + fi +done diff --git a/vendor/github.com/mattn/go-runewidth/runewidth.go b/vendor/github.com/mattn/go-runewidth/runewidth.go new file mode 100644 index 0000000000..3d7fa560b8 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth.go @@ -0,0 +1,273 @@ +package runewidth + +import ( + "os" + + "github.com/rivo/uniseg" +) + +//go:generate go run script/generate.go + +var ( + // EastAsianWidth will be set true if the current locale is CJK + EastAsianWidth bool + + // StrictEmojiNeutral should be set false if handle broken fonts + StrictEmojiNeutral bool = true + + // DefaultCondition is a condition in current locale + DefaultCondition = &Condition{ + EastAsianWidth: false, + StrictEmojiNeutral: true, + } +) + +func init() { + handleEnv() +} + +func handleEnv() { + env := os.Getenv("RUNEWIDTH_EASTASIAN") + if env == "" { + EastAsianWidth = IsEastAsian() + } else { + EastAsianWidth = env == "1" + } + // update DefaultCondition + DefaultCondition.EastAsianWidth = EastAsianWidth +} + +type interval struct { + first rune + last rune +} + +type table []interval + +func inTables(r rune, ts ...table) bool { + for _, t := range ts { + if inTable(r, t) { + return true + } + } + return false +} + +func inTable(r rune, t table) bool { + if r < t[0].first { + return false + } + + bot := 0 + top := len(t) - 1 + for top >= bot { + mid := (bot + top) >> 1 + + switch { + case t[mid].last < r: + bot = mid + 1 + case t[mid].first > r: + top = mid - 1 + default: + return true + } + } + + return false +} + +var private = table{ + {0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD}, +} + +var nonprint = table{ + {0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD}, + {0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F}, + {0x2028, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF}, + {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF}, +} + +// Condition have flag EastAsianWidth whether the current locale is CJK or not. +type Condition struct { + EastAsianWidth bool + StrictEmojiNeutral bool +} + +// NewCondition return new instance of Condition which is current locale. +func NewCondition() *Condition { + return &Condition{ + EastAsianWidth: EastAsianWidth, + StrictEmojiNeutral: StrictEmojiNeutral, + } +} + +// RuneWidth returns the number of cells in r. +// See http://www.unicode.org/reports/tr11/ +func (c *Condition) RuneWidth(r rune) int { + // optimized version, verified by TestRuneWidthChecksums() + if !c.EastAsianWidth { + switch { + case r < 0x20 || r > 0x10FFFF: + return 0 + case (r >= 0x7F && r <= 0x9F) || r == 0xAD: // nonprint + return 0 + case r < 0x300: + return 1 + case inTable(r, narrow): + return 1 + case inTables(r, nonprint, combining): + return 0 + case inTable(r, doublewidth): + return 2 + default: + return 1 + } + } else { + switch { + case r < 0 || r > 0x10FFFF || inTables(r, nonprint, combining): + return 0 + case inTable(r, narrow): + return 1 + case inTables(r, ambiguous, doublewidth): + return 2 + case !c.StrictEmojiNeutral && inTables(r, ambiguous, emoji, narrow): + return 2 + default: + return 1 + } + } +} + +// StringWidth return width as you can see +func (c *Condition) StringWidth(s string) (width int) { + g := uniseg.NewGraphemes(s) + for g.Next() { + var chWidth int + for _, r := range g.Runes() { + chWidth = c.RuneWidth(r) + if chWidth > 0 { + break // Our best guess at this point is to use the width of the first non-zero-width rune. + } + } + width += chWidth + } + return +} + +// Truncate return string truncated with w cells +func (c *Condition) Truncate(s string, w int, tail string) string { + if c.StringWidth(s) <= w { + return s + } + w -= c.StringWidth(tail) + var width int + pos := len(s) + g := uniseg.NewGraphemes(s) + for g.Next() { + var chWidth int + for _, r := range g.Runes() { + chWidth = c.RuneWidth(r) + if chWidth > 0 { + break // See StringWidth() for details. + } + } + if width+chWidth > w { + pos, _ = g.Positions() + break + } + width += chWidth + } + return s[:pos] + tail +} + +// Wrap return string wrapped with w cells +func (c *Condition) Wrap(s string, w int) string { + width := 0 + out := "" + for _, r := range []rune(s) { + cw := c.RuneWidth(r) + if r == '\n' { + out += string(r) + width = 0 + continue + } else if width+cw > w { + out += "\n" + width = 0 + out += string(r) + width += cw + continue + } + out += string(r) + width += cw + } + return out +} + +// FillLeft return string filled in left by spaces in w cells +func (c *Condition) FillLeft(s string, w int) string { + width := c.StringWidth(s) + count := w - width + if count > 0 { + b := make([]byte, count) + for i := range b { + b[i] = ' ' + } + return string(b) + s + } + return s +} + +// FillRight return string filled in left by spaces in w cells +func (c *Condition) FillRight(s string, w int) string { + width := c.StringWidth(s) + count := w - width + if count > 0 { + b := make([]byte, count) + for i := range b { + b[i] = ' ' + } + return s + string(b) + } + return s +} + +// RuneWidth returns the number of cells in r. +// See http://www.unicode.org/reports/tr11/ +func RuneWidth(r rune) int { + return DefaultCondition.RuneWidth(r) +} + +// IsAmbiguousWidth returns whether is ambiguous width or not. +func IsAmbiguousWidth(r rune) bool { + return inTables(r, private, ambiguous) +} + +// IsNeutralWidth returns whether is neutral width or not. +func IsNeutralWidth(r rune) bool { + return inTable(r, neutral) +} + +// StringWidth return width as you can see +func StringWidth(s string) (width int) { + return DefaultCondition.StringWidth(s) +} + +// Truncate return string truncated with w cells +func Truncate(s string, w int, tail string) string { + return DefaultCondition.Truncate(s, w, tail) +} + +// Wrap return string wrapped with w cells +func Wrap(s string, w int) string { + return DefaultCondition.Wrap(s, w) +} + +// FillLeft return string filled in left by spaces in w cells +func FillLeft(s string, w int) string { + return DefaultCondition.FillLeft(s, w) +} + +// FillRight return string filled in left by spaces in w cells +func FillRight(s string, w int) string { + return DefaultCondition.FillRight(s, w) +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go b/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go new file mode 100644 index 0000000000..7d99f6e521 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go @@ -0,0 +1,8 @@ +// +build appengine + +package runewidth + +// IsEastAsian return true if the current locale is CJK +func IsEastAsian() bool { + return false +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_js.go b/vendor/github.com/mattn/go-runewidth/runewidth_js.go new file mode 100644 index 0000000000..c5fdf40baa --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_js.go @@ -0,0 +1,9 @@ +// +build js +// +build !appengine + +package runewidth + +func IsEastAsian() bool { + // TODO: Implement this for the web. Detect east asian in a compatible way, and return true. + return false +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_posix.go b/vendor/github.com/mattn/go-runewidth/runewidth_posix.go new file mode 100644 index 0000000000..480ad74853 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_posix.go @@ -0,0 +1,82 @@ +// +build !windows +// +build !js +// +build !appengine + +package runewidth + +import ( + "os" + "regexp" + "strings" +) + +var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`) + +var mblenTable = map[string]int{ + "utf-8": 6, + "utf8": 6, + "jis": 8, + "eucjp": 3, + "euckr": 2, + "euccn": 2, + "sjis": 2, + "cp932": 2, + "cp51932": 2, + "cp936": 2, + "cp949": 2, + "cp950": 2, + "big5": 2, + "gbk": 2, + "gb2312": 2, +} + +func isEastAsian(locale string) bool { + charset := strings.ToLower(locale) + r := reLoc.FindStringSubmatch(locale) + if len(r) == 2 { + charset = strings.ToLower(r[1]) + } + + if strings.HasSuffix(charset, "@cjk_narrow") { + return false + } + + for pos, b := range []byte(charset) { + if b == '@' { + charset = charset[:pos] + break + } + } + max := 1 + if m, ok := mblenTable[charset]; ok { + max = m + } + if max > 1 && (charset[0] != 'u' || + strings.HasPrefix(locale, "ja") || + strings.HasPrefix(locale, "ko") || + strings.HasPrefix(locale, "zh")) { + return true + } + return false +} + +// IsEastAsian return true if the current locale is CJK +func IsEastAsian() bool { + locale := os.Getenv("LC_ALL") + if locale == "" { + locale = os.Getenv("LC_CTYPE") + } + if locale == "" { + locale = os.Getenv("LANG") + } + + // ignore C locale + if locale == "POSIX" || locale == "C" { + return false + } + if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') { + return false + } + + return isEastAsian(locale) +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_table.go b/vendor/github.com/mattn/go-runewidth/runewidth_table.go new file mode 100644 index 0000000000..e5d890c266 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_table.go @@ -0,0 +1,439 @@ +// Code generated by script/generate.go. DO NOT EDIT. + +package runewidth + +var combining = table{ + {0x0300, 0x036F}, {0x0483, 0x0489}, {0x07EB, 0x07F3}, + {0x0C00, 0x0C00}, {0x0C04, 0x0C04}, {0x0D00, 0x0D01}, + {0x135D, 0x135F}, {0x1A7F, 0x1A7F}, {0x1AB0, 0x1AC0}, + {0x1B6B, 0x1B73}, {0x1DC0, 0x1DF9}, {0x1DFB, 0x1DFF}, + {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2DE0, 0x2DFF}, + {0x3099, 0x309A}, {0xA66F, 0xA672}, {0xA674, 0xA67D}, + {0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA8E0, 0xA8F1}, + {0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, {0x10376, 0x1037A}, + {0x10EAB, 0x10EAC}, {0x10F46, 0x10F50}, {0x11300, 0x11301}, + {0x1133B, 0x1133C}, {0x11366, 0x1136C}, {0x11370, 0x11374}, + {0x16AF0, 0x16AF4}, {0x1D165, 0x1D169}, {0x1D16D, 0x1D172}, + {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD}, + {0x1D242, 0x1D244}, {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, + {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, {0x1E026, 0x1E02A}, + {0x1E8D0, 0x1E8D6}, +} + +var doublewidth = table{ + {0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A}, + {0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3}, + {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653}, + {0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1}, + {0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5}, + {0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA}, + {0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA}, + {0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B}, + {0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E}, + {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797}, + {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C}, + {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99}, + {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, + {0x3000, 0x303E}, {0x3041, 0x3096}, {0x3099, 0x30FF}, + {0x3105, 0x312F}, {0x3131, 0x318E}, {0x3190, 0x31E3}, + {0x31F0, 0x321E}, {0x3220, 0x3247}, {0x3250, 0x4DBF}, + {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C}, + {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, + {0xFE30, 0xFE52}, {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, + {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE4}, + {0x16FF0, 0x16FF1}, {0x17000, 0x187F7}, {0x18800, 0x18CD5}, + {0x18D00, 0x18D08}, {0x1B000, 0x1B11E}, {0x1B150, 0x1B152}, + {0x1B164, 0x1B167}, {0x1B170, 0x1B2FB}, {0x1F004, 0x1F004}, + {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, + {0x1F200, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248}, + {0x1F250, 0x1F251}, {0x1F260, 0x1F265}, {0x1F300, 0x1F320}, + {0x1F32D, 0x1F335}, {0x1F337, 0x1F37C}, {0x1F37E, 0x1F393}, + {0x1F3A0, 0x1F3CA}, {0x1F3CF, 0x1F3D3}, {0x1F3E0, 0x1F3F0}, + {0x1F3F4, 0x1F3F4}, {0x1F3F8, 0x1F43E}, {0x1F440, 0x1F440}, + {0x1F442, 0x1F4FC}, {0x1F4FF, 0x1F53D}, {0x1F54B, 0x1F54E}, + {0x1F550, 0x1F567}, {0x1F57A, 0x1F57A}, {0x1F595, 0x1F596}, + {0x1F5A4, 0x1F5A4}, {0x1F5FB, 0x1F64F}, {0x1F680, 0x1F6C5}, + {0x1F6CC, 0x1F6CC}, {0x1F6D0, 0x1F6D2}, {0x1F6D5, 0x1F6D7}, + {0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6FC}, {0x1F7E0, 0x1F7EB}, + {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1F978}, + {0x1F97A, 0x1F9CB}, {0x1F9CD, 0x1F9FF}, {0x1FA70, 0x1FA74}, + {0x1FA78, 0x1FA7A}, {0x1FA80, 0x1FA86}, {0x1FA90, 0x1FAA8}, + {0x1FAB0, 0x1FAB6}, {0x1FAC0, 0x1FAC2}, {0x1FAD0, 0x1FAD6}, + {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD}, +} + +var ambiguous = table{ + {0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8}, + {0x00AA, 0x00AA}, {0x00AD, 0x00AE}, {0x00B0, 0x00B4}, + {0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6}, + {0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1}, + {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED}, + {0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA}, + {0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101}, + {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B}, + {0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133}, + {0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144}, + {0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153}, + {0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE}, + {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4}, + {0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA}, + {0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261}, + {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB}, + {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB}, + {0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0300, 0x036F}, + {0x0391, 0x03A1}, {0x03A3, 0x03A9}, {0x03B1, 0x03C1}, + {0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F}, + {0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016}, + {0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022}, + {0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033}, + {0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E}, + {0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084}, + {0x20AC, 0x20AC}, {0x2103, 0x2103}, {0x2105, 0x2105}, + {0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116}, + {0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B}, + {0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B}, + {0x2170, 0x2179}, {0x2189, 0x2189}, {0x2190, 0x2199}, + {0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4}, + {0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203}, + {0x2207, 0x2208}, {0x220B, 0x220B}, {0x220F, 0x220F}, + {0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A}, + {0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225}, + {0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237}, + {0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C}, + {0x2252, 0x2252}, {0x2260, 0x2261}, {0x2264, 0x2267}, + {0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283}, + {0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299}, + {0x22A5, 0x22A5}, {0x22BF, 0x22BF}, {0x2312, 0x2312}, + {0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573}, + {0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1}, + {0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7}, + {0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8}, + {0x25CB, 0x25CB}, {0x25CE, 0x25D1}, {0x25E2, 0x25E5}, + {0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609}, + {0x260E, 0x260F}, {0x261C, 0x261C}, {0x261E, 0x261E}, + {0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661}, + {0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D}, + {0x266F, 0x266F}, {0x269E, 0x269F}, {0x26BF, 0x26BF}, + {0x26C6, 0x26CD}, {0x26CF, 0x26D3}, {0x26D5, 0x26E1}, + {0x26E3, 0x26E3}, {0x26E8, 0x26E9}, {0x26EB, 0x26F1}, + {0x26F4, 0x26F4}, {0x26F6, 0x26F9}, {0x26FB, 0x26FC}, + {0x26FE, 0x26FF}, {0x273D, 0x273D}, {0x2776, 0x277F}, + {0x2B56, 0x2B59}, {0x3248, 0x324F}, {0xE000, 0xF8FF}, + {0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A}, + {0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D}, + {0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {0xE0100, 0xE01EF}, + {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}, +} +var narrow = table{ + {0x0020, 0x007E}, {0x00A2, 0x00A3}, {0x00A5, 0x00A6}, + {0x00AC, 0x00AC}, {0x00AF, 0x00AF}, {0x27E6, 0x27ED}, + {0x2985, 0x2986}, +} + +var neutral = table{ + {0x0000, 0x001F}, {0x007F, 0x00A0}, {0x00A9, 0x00A9}, + {0x00AB, 0x00AB}, {0x00B5, 0x00B5}, {0x00BB, 0x00BB}, + {0x00C0, 0x00C5}, {0x00C7, 0x00CF}, {0x00D1, 0x00D6}, + {0x00D9, 0x00DD}, {0x00E2, 0x00E5}, {0x00E7, 0x00E7}, + {0x00EB, 0x00EB}, {0x00EE, 0x00EF}, {0x00F1, 0x00F1}, + {0x00F4, 0x00F6}, {0x00FB, 0x00FB}, {0x00FD, 0x00FD}, + {0x00FF, 0x0100}, {0x0102, 0x0110}, {0x0112, 0x0112}, + {0x0114, 0x011A}, {0x011C, 0x0125}, {0x0128, 0x012A}, + {0x012C, 0x0130}, {0x0134, 0x0137}, {0x0139, 0x013E}, + {0x0143, 0x0143}, {0x0145, 0x0147}, {0x014C, 0x014C}, + {0x014E, 0x0151}, {0x0154, 0x0165}, {0x0168, 0x016A}, + {0x016C, 0x01CD}, {0x01CF, 0x01CF}, {0x01D1, 0x01D1}, + {0x01D3, 0x01D3}, {0x01D5, 0x01D5}, {0x01D7, 0x01D7}, + {0x01D9, 0x01D9}, {0x01DB, 0x01DB}, {0x01DD, 0x0250}, + {0x0252, 0x0260}, {0x0262, 0x02C3}, {0x02C5, 0x02C6}, + {0x02C8, 0x02C8}, {0x02CC, 0x02CC}, {0x02CE, 0x02CF}, + {0x02D1, 0x02D7}, {0x02DC, 0x02DC}, {0x02DE, 0x02DE}, + {0x02E0, 0x02FF}, {0x0370, 0x0377}, {0x037A, 0x037F}, + {0x0384, 0x038A}, {0x038C, 0x038C}, {0x038E, 0x0390}, + {0x03AA, 0x03B0}, {0x03C2, 0x03C2}, {0x03CA, 0x0400}, + {0x0402, 0x040F}, {0x0450, 0x0450}, {0x0452, 0x052F}, + {0x0531, 0x0556}, {0x0559, 0x058A}, {0x058D, 0x058F}, + {0x0591, 0x05C7}, {0x05D0, 0x05EA}, {0x05EF, 0x05F4}, + {0x0600, 0x061C}, {0x061E, 0x070D}, {0x070F, 0x074A}, + {0x074D, 0x07B1}, {0x07C0, 0x07FA}, {0x07FD, 0x082D}, + {0x0830, 0x083E}, {0x0840, 0x085B}, {0x085E, 0x085E}, + {0x0860, 0x086A}, {0x08A0, 0x08B4}, {0x08B6, 0x08C7}, + {0x08D3, 0x0983}, {0x0985, 0x098C}, {0x098F, 0x0990}, + {0x0993, 0x09A8}, {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, + {0x09B6, 0x09B9}, {0x09BC, 0x09C4}, {0x09C7, 0x09C8}, + {0x09CB, 0x09CE}, {0x09D7, 0x09D7}, {0x09DC, 0x09DD}, + {0x09DF, 0x09E3}, {0x09E6, 0x09FE}, {0x0A01, 0x0A03}, + {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, + {0x0A2A, 0x0A30}, {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, + {0x0A38, 0x0A39}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42}, + {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, + {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, {0x0A66, 0x0A76}, + {0x0A81, 0x0A83}, {0x0A85, 0x0A8D}, {0x0A8F, 0x0A91}, + {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3}, + {0x0AB5, 0x0AB9}, {0x0ABC, 0x0AC5}, {0x0AC7, 0x0AC9}, + {0x0ACB, 0x0ACD}, {0x0AD0, 0x0AD0}, {0x0AE0, 0x0AE3}, + {0x0AE6, 0x0AF1}, {0x0AF9, 0x0AFF}, {0x0B01, 0x0B03}, + {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, {0x0B13, 0x0B28}, + {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, {0x0B35, 0x0B39}, + {0x0B3C, 0x0B44}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D}, + {0x0B55, 0x0B57}, {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B63}, + {0x0B66, 0x0B77}, {0x0B82, 0x0B83}, {0x0B85, 0x0B8A}, + {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, {0x0B99, 0x0B9A}, + {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, {0x0BA3, 0x0BA4}, + {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9}, {0x0BBE, 0x0BC2}, + {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD}, {0x0BD0, 0x0BD0}, + {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BFA}, {0x0C00, 0x0C0C}, + {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, {0x0C2A, 0x0C39}, + {0x0C3D, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, + {0x0C55, 0x0C56}, {0x0C58, 0x0C5A}, {0x0C60, 0x0C63}, + {0x0C66, 0x0C6F}, {0x0C77, 0x0C8C}, {0x0C8E, 0x0C90}, + {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, + {0x0CBC, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, + {0x0CD5, 0x0CD6}, {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE3}, + {0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF2}, {0x0D00, 0x0D0C}, + {0x0D0E, 0x0D10}, {0x0D12, 0x0D44}, {0x0D46, 0x0D48}, + {0x0D4A, 0x0D4F}, {0x0D54, 0x0D63}, {0x0D66, 0x0D7F}, + {0x0D81, 0x0D83}, {0x0D85, 0x0D96}, {0x0D9A, 0x0DB1}, + {0x0DB3, 0x0DBB}, {0x0DBD, 0x0DBD}, {0x0DC0, 0x0DC6}, + {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4}, {0x0DD6, 0x0DD6}, + {0x0DD8, 0x0DDF}, {0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF4}, + {0x0E01, 0x0E3A}, {0x0E3F, 0x0E5B}, {0x0E81, 0x0E82}, + {0x0E84, 0x0E84}, {0x0E86, 0x0E8A}, {0x0E8C, 0x0EA3}, + {0x0EA5, 0x0EA5}, {0x0EA7, 0x0EBD}, {0x0EC0, 0x0EC4}, + {0x0EC6, 0x0EC6}, {0x0EC8, 0x0ECD}, {0x0ED0, 0x0ED9}, + {0x0EDC, 0x0EDF}, {0x0F00, 0x0F47}, {0x0F49, 0x0F6C}, + {0x0F71, 0x0F97}, {0x0F99, 0x0FBC}, {0x0FBE, 0x0FCC}, + {0x0FCE, 0x0FDA}, {0x1000, 0x10C5}, {0x10C7, 0x10C7}, + {0x10CD, 0x10CD}, {0x10D0, 0x10FF}, {0x1160, 0x1248}, + {0x124A, 0x124D}, {0x1250, 0x1256}, {0x1258, 0x1258}, + {0x125A, 0x125D}, {0x1260, 0x1288}, {0x128A, 0x128D}, + {0x1290, 0x12B0}, {0x12B2, 0x12B5}, {0x12B8, 0x12BE}, + {0x12C0, 0x12C0}, {0x12C2, 0x12C5}, {0x12C8, 0x12D6}, + {0x12D8, 0x1310}, {0x1312, 0x1315}, {0x1318, 0x135A}, + {0x135D, 0x137C}, {0x1380, 0x1399}, {0x13A0, 0x13F5}, + {0x13F8, 0x13FD}, {0x1400, 0x169C}, {0x16A0, 0x16F8}, + {0x1700, 0x170C}, {0x170E, 0x1714}, {0x1720, 0x1736}, + {0x1740, 0x1753}, {0x1760, 0x176C}, {0x176E, 0x1770}, + {0x1772, 0x1773}, {0x1780, 0x17DD}, {0x17E0, 0x17E9}, + {0x17F0, 0x17F9}, {0x1800, 0x180E}, {0x1810, 0x1819}, + {0x1820, 0x1878}, {0x1880, 0x18AA}, {0x18B0, 0x18F5}, + {0x1900, 0x191E}, {0x1920, 0x192B}, {0x1930, 0x193B}, + {0x1940, 0x1940}, {0x1944, 0x196D}, {0x1970, 0x1974}, + {0x1980, 0x19AB}, {0x19B0, 0x19C9}, {0x19D0, 0x19DA}, + {0x19DE, 0x1A1B}, {0x1A1E, 0x1A5E}, {0x1A60, 0x1A7C}, + {0x1A7F, 0x1A89}, {0x1A90, 0x1A99}, {0x1AA0, 0x1AAD}, + {0x1AB0, 0x1AC0}, {0x1B00, 0x1B4B}, {0x1B50, 0x1B7C}, + {0x1B80, 0x1BF3}, {0x1BFC, 0x1C37}, {0x1C3B, 0x1C49}, + {0x1C4D, 0x1C88}, {0x1C90, 0x1CBA}, {0x1CBD, 0x1CC7}, + {0x1CD0, 0x1CFA}, {0x1D00, 0x1DF9}, {0x1DFB, 0x1F15}, + {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, + {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, + {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, + {0x1FB6, 0x1FC4}, {0x1FC6, 0x1FD3}, {0x1FD6, 0x1FDB}, + {0x1FDD, 0x1FEF}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFE}, + {0x2000, 0x200F}, {0x2011, 0x2012}, {0x2017, 0x2017}, + {0x201A, 0x201B}, {0x201E, 0x201F}, {0x2023, 0x2023}, + {0x2028, 0x202F}, {0x2031, 0x2031}, {0x2034, 0x2034}, + {0x2036, 0x203A}, {0x203C, 0x203D}, {0x203F, 0x2064}, + {0x2066, 0x2071}, {0x2075, 0x207E}, {0x2080, 0x2080}, + {0x2085, 0x208E}, {0x2090, 0x209C}, {0x20A0, 0x20A8}, + {0x20AA, 0x20AB}, {0x20AD, 0x20BF}, {0x20D0, 0x20F0}, + {0x2100, 0x2102}, {0x2104, 0x2104}, {0x2106, 0x2108}, + {0x210A, 0x2112}, {0x2114, 0x2115}, {0x2117, 0x2120}, + {0x2123, 0x2125}, {0x2127, 0x212A}, {0x212C, 0x2152}, + {0x2155, 0x215A}, {0x215F, 0x215F}, {0x216C, 0x216F}, + {0x217A, 0x2188}, {0x218A, 0x218B}, {0x219A, 0x21B7}, + {0x21BA, 0x21D1}, {0x21D3, 0x21D3}, {0x21D5, 0x21E6}, + {0x21E8, 0x21FF}, {0x2201, 0x2201}, {0x2204, 0x2206}, + {0x2209, 0x220A}, {0x220C, 0x220E}, {0x2210, 0x2210}, + {0x2212, 0x2214}, {0x2216, 0x2219}, {0x221B, 0x221C}, + {0x2221, 0x2222}, {0x2224, 0x2224}, {0x2226, 0x2226}, + {0x222D, 0x222D}, {0x222F, 0x2233}, {0x2238, 0x223B}, + {0x223E, 0x2247}, {0x2249, 0x224B}, {0x224D, 0x2251}, + {0x2253, 0x225F}, {0x2262, 0x2263}, {0x2268, 0x2269}, + {0x226C, 0x226D}, {0x2270, 0x2281}, {0x2284, 0x2285}, + {0x2288, 0x2294}, {0x2296, 0x2298}, {0x229A, 0x22A4}, + {0x22A6, 0x22BE}, {0x22C0, 0x2311}, {0x2313, 0x2319}, + {0x231C, 0x2328}, {0x232B, 0x23E8}, {0x23ED, 0x23EF}, + {0x23F1, 0x23F2}, {0x23F4, 0x2426}, {0x2440, 0x244A}, + {0x24EA, 0x24EA}, {0x254C, 0x254F}, {0x2574, 0x257F}, + {0x2590, 0x2591}, {0x2596, 0x259F}, {0x25A2, 0x25A2}, + {0x25AA, 0x25B1}, {0x25B4, 0x25B5}, {0x25B8, 0x25BB}, + {0x25BE, 0x25BF}, {0x25C2, 0x25C5}, {0x25C9, 0x25CA}, + {0x25CC, 0x25CD}, {0x25D2, 0x25E1}, {0x25E6, 0x25EE}, + {0x25F0, 0x25FC}, {0x25FF, 0x2604}, {0x2607, 0x2608}, + {0x260A, 0x260D}, {0x2610, 0x2613}, {0x2616, 0x261B}, + {0x261D, 0x261D}, {0x261F, 0x263F}, {0x2641, 0x2641}, + {0x2643, 0x2647}, {0x2654, 0x265F}, {0x2662, 0x2662}, + {0x2666, 0x2666}, {0x266B, 0x266B}, {0x266E, 0x266E}, + {0x2670, 0x267E}, {0x2680, 0x2692}, {0x2694, 0x269D}, + {0x26A0, 0x26A0}, {0x26A2, 0x26A9}, {0x26AC, 0x26BC}, + {0x26C0, 0x26C3}, {0x26E2, 0x26E2}, {0x26E4, 0x26E7}, + {0x2700, 0x2704}, {0x2706, 0x2709}, {0x270C, 0x2727}, + {0x2729, 0x273C}, {0x273E, 0x274B}, {0x274D, 0x274D}, + {0x274F, 0x2752}, {0x2756, 0x2756}, {0x2758, 0x2775}, + {0x2780, 0x2794}, {0x2798, 0x27AF}, {0x27B1, 0x27BE}, + {0x27C0, 0x27E5}, {0x27EE, 0x2984}, {0x2987, 0x2B1A}, + {0x2B1D, 0x2B4F}, {0x2B51, 0x2B54}, {0x2B5A, 0x2B73}, + {0x2B76, 0x2B95}, {0x2B97, 0x2C2E}, {0x2C30, 0x2C5E}, + {0x2C60, 0x2CF3}, {0x2CF9, 0x2D25}, {0x2D27, 0x2D27}, + {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67}, {0x2D6F, 0x2D70}, + {0x2D7F, 0x2D96}, {0x2DA0, 0x2DA6}, {0x2DA8, 0x2DAE}, + {0x2DB0, 0x2DB6}, {0x2DB8, 0x2DBE}, {0x2DC0, 0x2DC6}, + {0x2DC8, 0x2DCE}, {0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE}, + {0x2DE0, 0x2E52}, {0x303F, 0x303F}, {0x4DC0, 0x4DFF}, + {0xA4D0, 0xA62B}, {0xA640, 0xA6F7}, {0xA700, 0xA7BF}, + {0xA7C2, 0xA7CA}, {0xA7F5, 0xA82C}, {0xA830, 0xA839}, + {0xA840, 0xA877}, {0xA880, 0xA8C5}, {0xA8CE, 0xA8D9}, + {0xA8E0, 0xA953}, {0xA95F, 0xA95F}, {0xA980, 0xA9CD}, + {0xA9CF, 0xA9D9}, {0xA9DE, 0xA9FE}, {0xAA00, 0xAA36}, + {0xAA40, 0xAA4D}, {0xAA50, 0xAA59}, {0xAA5C, 0xAAC2}, + {0xAADB, 0xAAF6}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E}, + {0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E}, + {0xAB30, 0xAB6B}, {0xAB70, 0xABED}, {0xABF0, 0xABF9}, + {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xD800, 0xDFFF}, + {0xFB00, 0xFB06}, {0xFB13, 0xFB17}, {0xFB1D, 0xFB36}, + {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41}, + {0xFB43, 0xFB44}, {0xFB46, 0xFBC1}, {0xFBD3, 0xFD3F}, + {0xFD50, 0xFD8F}, {0xFD92, 0xFDC7}, {0xFDF0, 0xFDFD}, + {0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC}, + {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFC}, {0x10000, 0x1000B}, + {0x1000D, 0x10026}, {0x10028, 0x1003A}, {0x1003C, 0x1003D}, + {0x1003F, 0x1004D}, {0x10050, 0x1005D}, {0x10080, 0x100FA}, + {0x10100, 0x10102}, {0x10107, 0x10133}, {0x10137, 0x1018E}, + {0x10190, 0x1019C}, {0x101A0, 0x101A0}, {0x101D0, 0x101FD}, + {0x10280, 0x1029C}, {0x102A0, 0x102D0}, {0x102E0, 0x102FB}, + {0x10300, 0x10323}, {0x1032D, 0x1034A}, {0x10350, 0x1037A}, + {0x10380, 0x1039D}, {0x1039F, 0x103C3}, {0x103C8, 0x103D5}, + {0x10400, 0x1049D}, {0x104A0, 0x104A9}, {0x104B0, 0x104D3}, + {0x104D8, 0x104FB}, {0x10500, 0x10527}, {0x10530, 0x10563}, + {0x1056F, 0x1056F}, {0x10600, 0x10736}, {0x10740, 0x10755}, + {0x10760, 0x10767}, {0x10800, 0x10805}, {0x10808, 0x10808}, + {0x1080A, 0x10835}, {0x10837, 0x10838}, {0x1083C, 0x1083C}, + {0x1083F, 0x10855}, {0x10857, 0x1089E}, {0x108A7, 0x108AF}, + {0x108E0, 0x108F2}, {0x108F4, 0x108F5}, {0x108FB, 0x1091B}, + {0x1091F, 0x10939}, {0x1093F, 0x1093F}, {0x10980, 0x109B7}, + {0x109BC, 0x109CF}, {0x109D2, 0x10A03}, {0x10A05, 0x10A06}, + {0x10A0C, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A35}, + {0x10A38, 0x10A3A}, {0x10A3F, 0x10A48}, {0x10A50, 0x10A58}, + {0x10A60, 0x10A9F}, {0x10AC0, 0x10AE6}, {0x10AEB, 0x10AF6}, + {0x10B00, 0x10B35}, {0x10B39, 0x10B55}, {0x10B58, 0x10B72}, + {0x10B78, 0x10B91}, {0x10B99, 0x10B9C}, {0x10BA9, 0x10BAF}, + {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2}, + {0x10CFA, 0x10D27}, {0x10D30, 0x10D39}, {0x10E60, 0x10E7E}, + {0x10E80, 0x10EA9}, {0x10EAB, 0x10EAD}, {0x10EB0, 0x10EB1}, + {0x10F00, 0x10F27}, {0x10F30, 0x10F59}, {0x10FB0, 0x10FCB}, + {0x10FE0, 0x10FF6}, {0x11000, 0x1104D}, {0x11052, 0x1106F}, + {0x1107F, 0x110C1}, {0x110CD, 0x110CD}, {0x110D0, 0x110E8}, + {0x110F0, 0x110F9}, {0x11100, 0x11134}, {0x11136, 0x11147}, + {0x11150, 0x11176}, {0x11180, 0x111DF}, {0x111E1, 0x111F4}, + {0x11200, 0x11211}, {0x11213, 0x1123E}, {0x11280, 0x11286}, + {0x11288, 0x11288}, {0x1128A, 0x1128D}, {0x1128F, 0x1129D}, + {0x1129F, 0x112A9}, {0x112B0, 0x112EA}, {0x112F0, 0x112F9}, + {0x11300, 0x11303}, {0x11305, 0x1130C}, {0x1130F, 0x11310}, + {0x11313, 0x11328}, {0x1132A, 0x11330}, {0x11332, 0x11333}, + {0x11335, 0x11339}, {0x1133B, 0x11344}, {0x11347, 0x11348}, + {0x1134B, 0x1134D}, {0x11350, 0x11350}, {0x11357, 0x11357}, + {0x1135D, 0x11363}, {0x11366, 0x1136C}, {0x11370, 0x11374}, + {0x11400, 0x1145B}, {0x1145D, 0x11461}, {0x11480, 0x114C7}, + {0x114D0, 0x114D9}, {0x11580, 0x115B5}, {0x115B8, 0x115DD}, + {0x11600, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C}, + {0x11680, 0x116B8}, {0x116C0, 0x116C9}, {0x11700, 0x1171A}, + {0x1171D, 0x1172B}, {0x11730, 0x1173F}, {0x11800, 0x1183B}, + {0x118A0, 0x118F2}, {0x118FF, 0x11906}, {0x11909, 0x11909}, + {0x1190C, 0x11913}, {0x11915, 0x11916}, {0x11918, 0x11935}, + {0x11937, 0x11938}, {0x1193B, 0x11946}, {0x11950, 0x11959}, + {0x119A0, 0x119A7}, {0x119AA, 0x119D7}, {0x119DA, 0x119E4}, + {0x11A00, 0x11A47}, {0x11A50, 0x11AA2}, {0x11AC0, 0x11AF8}, + {0x11C00, 0x11C08}, {0x11C0A, 0x11C36}, {0x11C38, 0x11C45}, + {0x11C50, 0x11C6C}, {0x11C70, 0x11C8F}, {0x11C92, 0x11CA7}, + {0x11CA9, 0x11CB6}, {0x11D00, 0x11D06}, {0x11D08, 0x11D09}, + {0x11D0B, 0x11D36}, {0x11D3A, 0x11D3A}, {0x11D3C, 0x11D3D}, + {0x11D3F, 0x11D47}, {0x11D50, 0x11D59}, {0x11D60, 0x11D65}, + {0x11D67, 0x11D68}, {0x11D6A, 0x11D8E}, {0x11D90, 0x11D91}, + {0x11D93, 0x11D98}, {0x11DA0, 0x11DA9}, {0x11EE0, 0x11EF8}, + {0x11FB0, 0x11FB0}, {0x11FC0, 0x11FF1}, {0x11FFF, 0x12399}, + {0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543}, + {0x13000, 0x1342E}, {0x13430, 0x13438}, {0x14400, 0x14646}, + {0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16A60, 0x16A69}, + {0x16A6E, 0x16A6F}, {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF5}, + {0x16B00, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61}, + {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16E40, 0x16E9A}, + {0x16F00, 0x16F4A}, {0x16F4F, 0x16F87}, {0x16F8F, 0x16F9F}, + {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, {0x1BC80, 0x1BC88}, + {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BCA3}, {0x1D000, 0x1D0F5}, + {0x1D100, 0x1D126}, {0x1D129, 0x1D1E8}, {0x1D200, 0x1D245}, + {0x1D2E0, 0x1D2F3}, {0x1D300, 0x1D356}, {0x1D360, 0x1D378}, + {0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, {0x1D49E, 0x1D49F}, + {0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, {0x1D4A9, 0x1D4AC}, + {0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, {0x1D4BD, 0x1D4C3}, + {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, {0x1D50D, 0x1D514}, + {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, {0x1D53B, 0x1D53E}, + {0x1D540, 0x1D544}, {0x1D546, 0x1D546}, {0x1D54A, 0x1D550}, + {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D7CB}, {0x1D7CE, 0x1DA8B}, + {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1E000, 0x1E006}, + {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, + {0x1E026, 0x1E02A}, {0x1E100, 0x1E12C}, {0x1E130, 0x1E13D}, + {0x1E140, 0x1E149}, {0x1E14E, 0x1E14F}, {0x1E2C0, 0x1E2F9}, + {0x1E2FF, 0x1E2FF}, {0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6}, + {0x1E900, 0x1E94B}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F}, + {0x1EC71, 0x1ECB4}, {0x1ED01, 0x1ED3D}, {0x1EE00, 0x1EE03}, + {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, {0x1EE24, 0x1EE24}, + {0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37}, + {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, {0x1EE42, 0x1EE42}, + {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, {0x1EE4B, 0x1EE4B}, + {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, {0x1EE54, 0x1EE54}, + {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, {0x1EE5B, 0x1EE5B}, + {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, {0x1EE61, 0x1EE62}, + {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72}, + {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, {0x1EE7E, 0x1EE7E}, + {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3}, + {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, {0x1EEF0, 0x1EEF1}, + {0x1F000, 0x1F003}, {0x1F005, 0x1F02B}, {0x1F030, 0x1F093}, + {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, {0x1F0C1, 0x1F0CE}, + {0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10F}, {0x1F12E, 0x1F12F}, + {0x1F16A, 0x1F16F}, {0x1F1AD, 0x1F1AD}, {0x1F1E6, 0x1F1FF}, + {0x1F321, 0x1F32C}, {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, + {0x1F394, 0x1F39F}, {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF}, + {0x1F3F1, 0x1F3F3}, {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F}, + {0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A}, + {0x1F54F, 0x1F54F}, {0x1F568, 0x1F579}, {0x1F57B, 0x1F594}, + {0x1F597, 0x1F5A3}, {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F}, + {0x1F6C6, 0x1F6CB}, {0x1F6CD, 0x1F6CF}, {0x1F6D3, 0x1F6D4}, + {0x1F6E0, 0x1F6EA}, {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F773}, + {0x1F780, 0x1F7D8}, {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, + {0x1F850, 0x1F859}, {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD}, + {0x1F8B0, 0x1F8B1}, {0x1F900, 0x1F90B}, {0x1F93B, 0x1F93B}, + {0x1F946, 0x1F946}, {0x1FA00, 0x1FA53}, {0x1FA60, 0x1FA6D}, + {0x1FB00, 0x1FB92}, {0x1FB94, 0x1FBCA}, {0x1FBF0, 0x1FBF9}, + {0xE0001, 0xE0001}, {0xE0020, 0xE007F}, +} + +var emoji = table{ + {0x203C, 0x203C}, {0x2049, 0x2049}, {0x2122, 0x2122}, + {0x2139, 0x2139}, {0x2194, 0x2199}, {0x21A9, 0x21AA}, + {0x231A, 0x231B}, {0x2328, 0x2328}, {0x2388, 0x2388}, + {0x23CF, 0x23CF}, {0x23E9, 0x23F3}, {0x23F8, 0x23FA}, + {0x24C2, 0x24C2}, {0x25AA, 0x25AB}, {0x25B6, 0x25B6}, + {0x25C0, 0x25C0}, {0x25FB, 0x25FE}, {0x2600, 0x2605}, + {0x2607, 0x2612}, {0x2614, 0x2685}, {0x2690, 0x2705}, + {0x2708, 0x2712}, {0x2714, 0x2714}, {0x2716, 0x2716}, + {0x271D, 0x271D}, {0x2721, 0x2721}, {0x2728, 0x2728}, + {0x2733, 0x2734}, {0x2744, 0x2744}, {0x2747, 0x2747}, + {0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755}, + {0x2757, 0x2757}, {0x2763, 0x2767}, {0x2795, 0x2797}, + {0x27A1, 0x27A1}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, + {0x2934, 0x2935}, {0x2B05, 0x2B07}, {0x2B1B, 0x2B1C}, + {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x3030, 0x3030}, + {0x303D, 0x303D}, {0x3297, 0x3297}, {0x3299, 0x3299}, + {0x1F000, 0x1F0FF}, {0x1F10D, 0x1F10F}, {0x1F12F, 0x1F12F}, + {0x1F16C, 0x1F171}, {0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E}, + {0x1F191, 0x1F19A}, {0x1F1AD, 0x1F1E5}, {0x1F201, 0x1F20F}, + {0x1F21A, 0x1F21A}, {0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A}, + {0x1F23C, 0x1F23F}, {0x1F249, 0x1F3FA}, {0x1F400, 0x1F53D}, + {0x1F546, 0x1F64F}, {0x1F680, 0x1F6FF}, {0x1F774, 0x1F77F}, + {0x1F7D5, 0x1F7FF}, {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F}, + {0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F8FF}, + {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1FAFF}, + {0x1FC00, 0x1FFFD}, +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_windows.go b/vendor/github.com/mattn/go-runewidth/runewidth_windows.go new file mode 100644 index 0000000000..d6a61777d7 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_windows.go @@ -0,0 +1,28 @@ +// +build windows +// +build !appengine + +package runewidth + +import ( + "syscall" +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32") + procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP") +) + +// IsEastAsian return true if the current locale is CJK +func IsEastAsian() bool { + r1, _, _ := procGetConsoleOutputCP.Call() + if r1 == 0 { + return false + } + + switch int(r1) { + case 932, 51932, 936, 949, 950: + return true + } + + return false +} diff --git a/vendor/github.com/mgutz/ansi/.gitignore b/vendor/github.com/mgutz/ansi/.gitignore new file mode 100644 index 0000000000..9ed3b07cef --- /dev/null +++ b/vendor/github.com/mgutz/ansi/.gitignore @@ -0,0 +1 @@ +*.test diff --git a/vendor/github.com/mgutz/ansi/LICENSE b/vendor/github.com/mgutz/ansi/LICENSE new file mode 100644 index 0000000000..06ce0c3b51 --- /dev/null +++ b/vendor/github.com/mgutz/ansi/LICENSE @@ -0,0 +1,9 @@ +The MIT License (MIT) +Copyright (c) 2013 Mario L. Gutierrez + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/vendor/github.com/mgutz/ansi/README.md b/vendor/github.com/mgutz/ansi/README.md new file mode 100644 index 0000000000..8f8e20b7e4 --- /dev/null +++ b/vendor/github.com/mgutz/ansi/README.md @@ -0,0 +1,121 @@ +# ansi + +Package ansi is a small, fast library to create ANSI colored strings and codes. + +## Install + +Get it + +```sh +go get -u github.com/mgutz/ansi +``` + +## Example + +```go +import "github.com/mgutz/ansi" + +// colorize a string, SLOW +msg := ansi.Color("foo", "red+b:white") + +// create a FAST closure function to avoid computation of ANSI code +phosphorize := ansi.ColorFunc("green+h:black") +msg = phosphorize("Bring back the 80s!") +msg2 := phospohorize("Look, I'm a CRT!") + +// cache escape codes and build strings manually +lime := ansi.ColorCode("green+h:black") +reset := ansi.ColorCode("reset") + +fmt.Println(lime, "Bring back the 80s!", reset) +``` + +Other examples + +```go +Color(s, "red") // red +Color(s, "red+b") // red bold +Color(s, "red+B") // red blinking +Color(s, "red+u") // red underline +Color(s, "red+bh") // red bold bright +Color(s, "red:white") // red on white +Color(s, "red+b:white+h") // red bold on white bright +Color(s, "red+B:white+h") // red blink on white bright +Color(s, "off") // turn off ansi codes +``` + +To view color combinations, from project directory in terminal. + +```sh +go test +``` + +## Style format + +```go +"foregroundColor+attributes:backgroundColor+attributes" +``` + +Colors + +* black +* red +* green +* yellow +* blue +* magenta +* cyan +* white +* 0...255 (256 colors) + +Foreground Attributes + +* B = Blink +* b = bold +* h = high intensity (bright) +* i = inverse +* s = strikethrough +* u = underline + +Background Attributes + +* h = high intensity (bright) + +## Constants + +* ansi.Reset +* ansi.DefaultBG +* ansi.DefaultFG +* ansi.Black +* ansi.Red +* ansi.Green +* ansi.Yellow +* ansi.Blue +* ansi.Magenta +* ansi.Cyan +* ansi.White +* ansi.LightBlack +* ansi.LightRed +* ansi.LightGreen +* ansi.LightYellow +* ansi.LightBlue +* ansi.LightMagenta +* ansi.LightCyan +* ansi.LightWhite + +## References + +Wikipedia ANSI escape codes [Colors](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) + +General [tips and formatting](http://misc.flogisoft.com/bash/tip_colors_and_formatting) + +What about support on Windows? Use [colorable by mattn](https://github.com/mattn/go-colorable). +Ansi and colorable are used by [logxi](https://github.com/mgutz/logxi) to support logging in +color on Windows. + +## MIT License + +Copyright (c) 2013 Mario Gutierrez mario@mgutz.com + +See the file LICENSE for copying permission. + diff --git a/vendor/github.com/mgutz/ansi/ansi.go b/vendor/github.com/mgutz/ansi/ansi.go new file mode 100644 index 0000000000..dc0413649e --- /dev/null +++ b/vendor/github.com/mgutz/ansi/ansi.go @@ -0,0 +1,285 @@ +package ansi + +import ( + "bytes" + "fmt" + "strconv" + "strings" +) + +const ( + black = iota + red + green + yellow + blue + magenta + cyan + white + defaultt = 9 + + normalIntensityFG = 30 + highIntensityFG = 90 + normalIntensityBG = 40 + highIntensityBG = 100 + + start = "\033[" + bold = "1;" + blink = "5;" + underline = "4;" + inverse = "7;" + strikethrough = "9;" + + // Reset is the ANSI reset escape sequence + Reset = "\033[0m" + // DefaultBG is the default background + DefaultBG = "\033[49m" + // DefaultFG is the default foreground + DefaultFG = "\033[39m" +) + +// Black FG +var Black string + +// Red FG +var Red string + +// Green FG +var Green string + +// Yellow FG +var Yellow string + +// Blue FG +var Blue string + +// Magenta FG +var Magenta string + +// Cyan FG +var Cyan string + +// White FG +var White string + +// LightBlack FG +var LightBlack string + +// LightRed FG +var LightRed string + +// LightGreen FG +var LightGreen string + +// LightYellow FG +var LightYellow string + +// LightBlue FG +var LightBlue string + +// LightMagenta FG +var LightMagenta string + +// LightCyan FG +var LightCyan string + +// LightWhite FG +var LightWhite string + +var ( + plain = false + // Colors maps common color names to their ANSI color code. + Colors = map[string]int{ + "black": black, + "red": red, + "green": green, + "yellow": yellow, + "blue": blue, + "magenta": magenta, + "cyan": cyan, + "white": white, + "default": defaultt, + } +) + +func init() { + for i := 0; i < 256; i++ { + Colors[strconv.Itoa(i)] = i + } + + Black = ColorCode("black") + Red = ColorCode("red") + Green = ColorCode("green") + Yellow = ColorCode("yellow") + Blue = ColorCode("blue") + Magenta = ColorCode("magenta") + Cyan = ColorCode("cyan") + White = ColorCode("white") + LightBlack = ColorCode("black+h") + LightRed = ColorCode("red+h") + LightGreen = ColorCode("green+h") + LightYellow = ColorCode("yellow+h") + LightBlue = ColorCode("blue+h") + LightMagenta = ColorCode("magenta+h") + LightCyan = ColorCode("cyan+h") + LightWhite = ColorCode("white+h") +} + +// ColorCode returns the ANSI color color code for style. +func ColorCode(style string) string { + return colorCode(style).String() +} + +// Gets the ANSI color code for a style. +func colorCode(style string) *bytes.Buffer { + buf := bytes.NewBufferString("") + if plain || style == "" { + return buf + } + if style == "reset" { + buf.WriteString(Reset) + return buf + } else if style == "off" { + return buf + } + + foregroundBackground := strings.Split(style, ":") + foreground := strings.Split(foregroundBackground[0], "+") + fgKey := foreground[0] + fg := Colors[fgKey] + fgStyle := "" + if len(foreground) > 1 { + fgStyle = foreground[1] + } + + bg, bgStyle := "", "" + + if len(foregroundBackground) > 1 { + background := strings.Split(foregroundBackground[1], "+") + bg = background[0] + if len(background) > 1 { + bgStyle = background[1] + } + } + + buf.WriteString(start) + base := normalIntensityFG + if len(fgStyle) > 0 { + if strings.Contains(fgStyle, "b") { + buf.WriteString(bold) + } + if strings.Contains(fgStyle, "B") { + buf.WriteString(blink) + } + if strings.Contains(fgStyle, "u") { + buf.WriteString(underline) + } + if strings.Contains(fgStyle, "i") { + buf.WriteString(inverse) + } + if strings.Contains(fgStyle, "s") { + buf.WriteString(strikethrough) + } + if strings.Contains(fgStyle, "h") { + base = highIntensityFG + } + } + + // if 256-color + n, err := strconv.Atoi(fgKey) + if err == nil { + fmt.Fprintf(buf, "38;5;%d;", n) + } else { + fmt.Fprintf(buf, "%d;", base+fg) + } + + base = normalIntensityBG + if len(bg) > 0 { + if strings.Contains(bgStyle, "h") { + base = highIntensityBG + } + // if 256-color + n, err := strconv.Atoi(bg) + if err == nil { + fmt.Fprintf(buf, "48;5;%d;", n) + } else { + fmt.Fprintf(buf, "%d;", base+Colors[bg]) + } + } + + // remove last ";" + buf.Truncate(buf.Len() - 1) + buf.WriteRune('m') + return buf +} + +// Color colors a string based on the ANSI color code for style. +func Color(s, style string) string { + if plain || len(style) < 1 { + return s + } + buf := colorCode(style) + buf.WriteString(s) + buf.WriteString(Reset) + return buf.String() +} + +// ColorFunc creates a closure to avoid computation ANSI color code. +func ColorFunc(style string) func(string) string { + if style == "" { + return func(s string) string { + return s + } + } + color := ColorCode(style) + return func(s string) string { + if plain || s == "" { + return s + } + buf := bytes.NewBufferString(color) + buf.WriteString(s) + buf.WriteString(Reset) + result := buf.String() + return result + } +} + +// DisableColors disables ANSI color codes. The default is false (colors are on). +func DisableColors(disable bool) { + plain = disable + if plain { + Black = "" + Red = "" + Green = "" + Yellow = "" + Blue = "" + Magenta = "" + Cyan = "" + White = "" + LightBlack = "" + LightRed = "" + LightGreen = "" + LightYellow = "" + LightBlue = "" + LightMagenta = "" + LightCyan = "" + LightWhite = "" + } else { + Black = ColorCode("black") + Red = ColorCode("red") + Green = ColorCode("green") + Yellow = ColorCode("yellow") + Blue = ColorCode("blue") + Magenta = ColorCode("magenta") + Cyan = ColorCode("cyan") + White = ColorCode("white") + LightBlack = ColorCode("black+h") + LightRed = ColorCode("red+h") + LightGreen = ColorCode("green+h") + LightYellow = ColorCode("yellow+h") + LightBlue = ColorCode("blue+h") + LightMagenta = ColorCode("magenta+h") + LightCyan = ColorCode("cyan+h") + LightWhite = ColorCode("white+h") + } +} diff --git a/vendor/github.com/mgutz/ansi/doc.go b/vendor/github.com/mgutz/ansi/doc.go new file mode 100644 index 0000000000..43c217e11d --- /dev/null +++ b/vendor/github.com/mgutz/ansi/doc.go @@ -0,0 +1,65 @@ +/* +Package ansi is a small, fast library to create ANSI colored strings and codes. + +Installation + + # this installs the color viewer and the package + go get -u github.com/mgutz/ansi/cmd/ansi-mgutz + +Example + + // colorize a string, SLOW + msg := ansi.Color("foo", "red+b:white") + + // create a closure to avoid recalculating ANSI code compilation + phosphorize := ansi.ColorFunc("green+h:black") + msg = phosphorize("Bring back the 80s!") + msg2 := phospohorize("Look, I'm a CRT!") + + // cache escape codes and build strings manually + lime := ansi.ColorCode("green+h:black") + reset := ansi.ColorCode("reset") + + fmt.Println(lime, "Bring back the 80s!", reset) + +Other examples + + Color(s, "red") // red + Color(s, "red+b") // red bold + Color(s, "red+B") // red blinking + Color(s, "red+u") // red underline + Color(s, "red+bh") // red bold bright + Color(s, "red:white") // red on white + Color(s, "red+b:white+h") // red bold on white bright + Color(s, "red+B:white+h") // red blink on white bright + +To view color combinations, from terminal + + ansi-mgutz + +Style format + + "foregroundColor+attributes:backgroundColor+attributes" + +Colors + + black + red + green + yellow + blue + magenta + cyan + white + +Attributes + + b = bold foreground + B = Blink foreground + u = underline foreground + h = high intensity (bright) foreground, background + i = inverse + +Wikipedia ANSI escape codes [Colors](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) +*/ +package ansi diff --git a/vendor/github.com/mgutz/ansi/print.go b/vendor/github.com/mgutz/ansi/print.go new file mode 100644 index 0000000000..806f436bb3 --- /dev/null +++ b/vendor/github.com/mgutz/ansi/print.go @@ -0,0 +1,57 @@ +package ansi + +import ( + "fmt" + "sort" + + colorable "github.com/mattn/go-colorable" +) + +// PrintStyles prints all style combinations to the terminal. +func PrintStyles() { + // for compatibility with Windows, not needed for *nix + stdout := colorable.NewColorableStdout() + + bgColors := []string{ + "", + ":black", + ":red", + ":green", + ":yellow", + ":blue", + ":magenta", + ":cyan", + ":white", + } + + keys := make([]string, 0, len(Colors)) + for k := range Colors { + keys = append(keys, k) + } + + sort.Sort(sort.StringSlice(keys)) + + for _, fg := range keys { + for _, bg := range bgColors { + fmt.Fprintln(stdout, padColor(fg, []string{"" + bg, "+b" + bg, "+bh" + bg, "+u" + bg})) + fmt.Fprintln(stdout, padColor(fg, []string{"+s" + bg, "+i" + bg})) + fmt.Fprintln(stdout, padColor(fg, []string{"+uh" + bg, "+B" + bg, "+Bb" + bg /* backgrounds */, "" + bg + "+h"})) + fmt.Fprintln(stdout, padColor(fg, []string{"+b" + bg + "+h", "+bh" + bg + "+h", "+u" + bg + "+h", "+uh" + bg + "+h"})) + } + } +} + +func pad(s string, length int) string { + for len(s) < length { + s += " " + } + return s +} + +func padColor(color string, styles []string) string { + buffer := "" + for _, style := range styles { + buffer += Color(pad(color+style, 20), color+style) + } + return buffer +} diff --git a/vendor/github.com/mitchellh/ioprogress/LICENSE b/vendor/github.com/mitchellh/ioprogress/LICENSE new file mode 100644 index 0000000000..2298515904 --- /dev/null +++ b/vendor/github.com/mitchellh/ioprogress/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Mitchell Hashimoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/mitchellh/ioprogress/README.md b/vendor/github.com/mitchellh/ioprogress/README.md new file mode 100644 index 0000000000..3d291e9d14 --- /dev/null +++ b/vendor/github.com/mitchellh/ioprogress/README.md @@ -0,0 +1,42 @@ +# ioprogress + +ioprogress is a Go (golang) library with implementations of `io.Reader` +and `io.Writer` that draws progress bars. The primary use case for these +are for CLI applications but alternate progress bar writers can be supplied +for alternate environments. + +## Example + +![Progress](http://g.recordit.co/GO5HxT16QH.gif) + +## Installation + +Standard `go get`: + +``` +$ go get github.com/mitchellh/ioprogress +``` + +## Usage + +Here is an example of outputting a basic progress bar to the CLI as +we're "downloading" from some other `io.Reader` (perhaps from a network +connection): + +```go +// Imagine this came from some external source, such as a network connection, +// and that we have the full size of it, such as from a Content-Length HTTP +// header. +var r io.Reader + +// Create the progress reader +progressR := &ioprogress.Reader{ + Reader: r, + Size: rSize, +} + +// Copy all of the reader to some local file f. As it copies, the +// progressR will write progress to the terminal on os.Stdout. This is +// customizable. +io.Copy(f, progressR) +``` diff --git a/vendor/github.com/mitchellh/ioprogress/draw.go b/vendor/github.com/mitchellh/ioprogress/draw.go new file mode 100644 index 0000000000..83b7305a02 --- /dev/null +++ b/vendor/github.com/mitchellh/ioprogress/draw.go @@ -0,0 +1,104 @@ +package ioprogress + +import ( + "fmt" + "io" + "os" + "strings" +) + +// DrawFunc is the callback type for drawing progress. +type DrawFunc func(int64, int64) error + +// DrawTextFormatFunc is a callback used by DrawFuncs that draw text in +// order to format the text into some more human friendly format. +type DrawTextFormatFunc func(int64, int64) string + +var defaultDrawFunc DrawFunc + +func init() { + defaultDrawFunc = DrawTerminal(os.Stdout) +} + +// DrawTerminal returns a DrawFunc that draws a progress bar to an io.Writer +// that is assumed to be a terminal (and therefore respects carriage returns). +func DrawTerminal(w io.Writer) DrawFunc { + return DrawTerminalf(w, func(progress, total int64) string { + return fmt.Sprintf("%d/%d", progress, total) + }) +} + +// DrawTerminalf returns a DrawFunc that draws a progress bar to an io.Writer +// that is formatted with the given formatting function. +func DrawTerminalf(w io.Writer, f DrawTextFormatFunc) DrawFunc { + var maxLength int + + return func(progress, total int64) error { + if progress == -1 && total == -1 { + _, err := fmt.Fprintf(w, "\n") + return err + } + + // Make sure we pad it to the max length we've ever drawn so that + // we don't have trailing characters. + line := f(progress, total) + if len(line) < maxLength { + line = fmt.Sprintf( + "%s%s", + line, + strings.Repeat(" ", maxLength-len(line))) + } + maxLength = len(line) + + _, err := fmt.Fprint(w, line+"\r") + return err + } +} + +var byteUnits = []string{"B", "KB", "MB", "GB", "TB", "PB"} + +// DrawTextFormatBytes is a DrawTextFormatFunc that formats the progress +// and total into human-friendly byte formats. +func DrawTextFormatBytes(progress, total int64) string { + return fmt.Sprintf("%s/%s", byteUnitStr(progress), byteUnitStr(total)) +} + +// DrawTextFormatBar returns a DrawTextFormatFunc that draws a progress +// bar with the given width (in characters). This can be used in conjunction +// with another DrawTextFormatFunc to create a progress bar with bytes, for +// example: +// +// bar := DrawTextFormatBar(20) +// func(progress, total int64) string { +// return fmt.Sprintf( +// "%s %s", +// bar(progress, total), +// DrawTextFormatBytes(progress, total)) +// } +// +func DrawTextFormatBar(width int64) DrawTextFormatFunc { + width -= 2 + + return func(progress, total int64) string { + current := int64((float64(progress) / float64(total)) * float64(width)) + return fmt.Sprintf( + "[%s%s]", + strings.Repeat("=", int(current)), + strings.Repeat(" ", int(width-current))) + } +} + +func byteUnitStr(n int64) string { + var unit string + size := float64(n) + for i := 1; i < len(byteUnits); i++ { + if size < 1000 { + unit = byteUnits[i-1] + break + } + + size = size / 1000 + } + + return fmt.Sprintf("%.3g %s", size, unit) +} diff --git a/vendor/github.com/mitchellh/ioprogress/reader.go b/vendor/github.com/mitchellh/ioprogress/reader.go new file mode 100644 index 0000000000..7d52731e2d --- /dev/null +++ b/vendor/github.com/mitchellh/ioprogress/reader.go @@ -0,0 +1,107 @@ +package ioprogress + +import ( + "io" + "time" +) + +// Reader is an implementation of io.Reader that draws the progress of +// reading some data. +type Reader struct { + // Reader is the underlying reader to read from + Reader io.Reader + + // Size is the total size of the data coming out of the reader. + Size int64 + + // DrawFunc is the callback to invoke to draw the progress bar. By + // default, this will be DrawTerminal(os.Stdout). + // + // DrawInterval is the minimum time to wait between reads to update the + // progress bar. + DrawFunc DrawFunc + DrawInterval time.Duration + + progress int64 + lastDraw time.Time +} + +// Read reads from the underlying reader and invokes the DrawFunc if +// appropriate. The DrawFunc is executed when there is data that is +// read (progress is made) and at least DrawInterval time has passed. +func (r *Reader) Read(p []byte) (int, error) { + // If we haven't drawn before, initialize the progress bar + if r.lastDraw.IsZero() { + r.initProgress() + } + + // Read from the underlying source + n, err := r.Reader.Read(p) + + // Always increment the progress even if there was an error + r.progress += int64(n) + + // If we don't have any errors, then draw the progress. If we are + // at the end of the data, then finish the progress. + if err == nil { + // Only draw if we read data or we've never read data before (to + // initialize the progress bar). + if n > 0 { + r.drawProgress() + } + } + if err == io.EOF { + r.finishProgress() + } + + return n, err +} + +func (r *Reader) drawProgress() { + // If we've drawn before, then make sure that the draw interval + // has passed before we draw again. + interval := r.DrawInterval + if interval == 0 { + interval = time.Second + } + if !r.lastDraw.IsZero() { + nextDraw := r.lastDraw.Add(interval) + if time.Now().Before(nextDraw) { + return + } + } + + // Draw + f := r.drawFunc() + f(r.progress, r.Size) + + // Record this draw so that we don't draw again really quickly + r.lastDraw = time.Now() +} + +func (r *Reader) finishProgress() { + f := r.drawFunc() + f(r.progress, r.Size) + + // Print a newline + f(-1, -1) + + // Reset lastDraw so we don't finish again + var zeroDraw time.Time + r.lastDraw = zeroDraw +} + +func (r *Reader) initProgress() { + var zeroDraw time.Time + r.lastDraw = zeroDraw + r.drawProgress() + r.lastDraw = zeroDraw +} + +func (r *Reader) drawFunc() DrawFunc { + if r.DrawFunc == nil { + return defaultDrawFunc + } + + return r.DrawFunc +} diff --git a/vendor/github.com/moby/spdystream/CONTRIBUTING.md b/vendor/github.com/moby/spdystream/CONTRIBUTING.md new file mode 100644 index 0000000000..d4eddcc539 --- /dev/null +++ b/vendor/github.com/moby/spdystream/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# Contributing to SpdyStream + +Want to hack on spdystream? Awesome! Here are instructions to get you +started. + +SpdyStream is a part of the [Docker](https://docker.io) project, and follows +the same rules and principles. If you're already familiar with the way +Docker does things, you'll feel right at home. + +Otherwise, go read +[Docker's contributions guidelines](https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md). + +Happy hacking! diff --git a/vendor/github.com/moby/spdystream/LICENSE b/vendor/github.com/moby/spdystream/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/github.com/moby/spdystream/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/moby/spdystream/MAINTAINERS b/vendor/github.com/moby/spdystream/MAINTAINERS new file mode 100644 index 0000000000..26e5ec828a --- /dev/null +++ b/vendor/github.com/moby/spdystream/MAINTAINERS @@ -0,0 +1,40 @@ +# Spdystream maintainers file +# +# This file describes who runs the moby/spdystream project and how. +# This is a living document - if you see something out of date or missing, speak up! +# +# It is structured to be consumable by both humans and programs. +# To extract its contents programmatically, use any TOML-compliant parser. +# +# This file is compiled into the MAINTAINERS file in docker/opensource. +# +[Org] + [Org."Core maintainers"] + people = [ + "adisky", + "dims", + "dmcgowan", + ] + +[people] + +# A reference list of all people associated with the project. +# All other sections should refer to people by their canonical key +# in the people section. + + # ADD YOURSELF HERE IN ALPHABETICAL ORDER + + [people.adisky] + Name = "Aditi Sharma" + Email = "adi.sky17@gmail.com" + GitHub = "adisky" + + [people.dims] + Name = "Davanum Srinivas" + Email = "davanum@gmail.com" + GitHub = "dims" + + [people.dmcgowan] + Name = "Derek McGowan" + Email = "derek@mcg.dev" + GitHub = "dmcgowan" diff --git a/vendor/github.com/moby/spdystream/NOTICE b/vendor/github.com/moby/spdystream/NOTICE new file mode 100644 index 0000000000..b9b11c9ab7 --- /dev/null +++ b/vendor/github.com/moby/spdystream/NOTICE @@ -0,0 +1,5 @@ +SpdyStream +Copyright 2014-2021 Docker Inc. + +This product includes software developed at +Docker Inc. (https://www.docker.com/). diff --git a/vendor/github.com/moby/spdystream/README.md b/vendor/github.com/moby/spdystream/README.md new file mode 100644 index 0000000000..b84e983439 --- /dev/null +++ b/vendor/github.com/moby/spdystream/README.md @@ -0,0 +1,77 @@ +# SpdyStream + +A multiplexed stream library using spdy + +## Usage + +Client example (connecting to mirroring server without auth) + +```go +package main + +import ( + "fmt" + "github.com/moby/spdystream" + "net" + "net/http" +) + +func main() { + conn, err := net.Dial("tcp", "localhost:8080") + if err != nil { + panic(err) + } + spdyConn, err := spdystream.NewConnection(conn, false) + if err != nil { + panic(err) + } + go spdyConn.Serve(spdystream.NoOpStreamHandler) + stream, err := spdyConn.CreateStream(http.Header{}, nil, false) + if err != nil { + panic(err) + } + + stream.Wait() + + fmt.Fprint(stream, "Writing to stream") + + buf := make([]byte, 25) + stream.Read(buf) + fmt.Println(string(buf)) + + stream.Close() +} +``` + +Server example (mirroring server without auth) + +```go +package main + +import ( + "github.com/moby/spdystream" + "net" +) + +func main() { + listener, err := net.Listen("tcp", "localhost:8080") + if err != nil { + panic(err) + } + for { + conn, err := listener.Accept() + if err != nil { + panic(err) + } + spdyConn, err := spdystream.NewConnection(conn, true) + if err != nil { + panic(err) + } + go spdyConn.Serve(spdystream.MirrorStreamHandler) + } +} +``` + +## Copyright and license + +Copyright 2013-2021 Docker, inc. Released under the [Apache 2.0 license](LICENSE). diff --git a/vendor/github.com/moby/spdystream/connection.go b/vendor/github.com/moby/spdystream/connection.go new file mode 100644 index 0000000000..d906bb05ce --- /dev/null +++ b/vendor/github.com/moby/spdystream/connection.go @@ -0,0 +1,972 @@ +/* + Copyright 2014-2021 Docker Inc. + + 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. +*/ + +package spdystream + +import ( + "errors" + "fmt" + "io" + "net" + "net/http" + "sync" + "time" + + "github.com/moby/spdystream/spdy" +) + +var ( + ErrInvalidStreamId = errors.New("Invalid stream id") + ErrTimeout = errors.New("Timeout occurred") + ErrReset = errors.New("Stream reset") + ErrWriteClosedStream = errors.New("Write on closed stream") +) + +const ( + FRAME_WORKERS = 5 + QUEUE_SIZE = 50 +) + +type StreamHandler func(stream *Stream) + +type AuthHandler func(header http.Header, slot uint8, parent uint32) bool + +type idleAwareFramer struct { + f *spdy.Framer + conn *Connection + writeLock sync.Mutex + resetChan chan struct{} + setTimeoutLock sync.Mutex + setTimeoutChan chan time.Duration + timeout time.Duration +} + +func newIdleAwareFramer(framer *spdy.Framer) *idleAwareFramer { + iaf := &idleAwareFramer{ + f: framer, + resetChan: make(chan struct{}, 2), + // setTimeoutChan needs to be buffered to avoid deadlocks when calling setIdleTimeout at about + // the same time the connection is being closed + setTimeoutChan: make(chan time.Duration, 1), + } + return iaf +} + +func (i *idleAwareFramer) monitor() { + var ( + timer *time.Timer + expired <-chan time.Time + resetChan = i.resetChan + setTimeoutChan = i.setTimeoutChan + ) +Loop: + for { + select { + case timeout := <-i.setTimeoutChan: + i.timeout = timeout + if timeout == 0 { + if timer != nil { + timer.Stop() + } + } else { + if timer == nil { + timer = time.NewTimer(timeout) + expired = timer.C + } else { + timer.Reset(timeout) + } + } + case <-resetChan: + if timer != nil && i.timeout > 0 { + timer.Reset(i.timeout) + } + case <-expired: + i.conn.streamCond.L.Lock() + streams := i.conn.streams + i.conn.streams = make(map[spdy.StreamId]*Stream) + i.conn.streamCond.Broadcast() + i.conn.streamCond.L.Unlock() + go func() { + for _, stream := range streams { + stream.resetStream() + } + i.conn.Close() + }() + case <-i.conn.closeChan: + if timer != nil { + timer.Stop() + } + + // Start a goroutine to drain resetChan. This is needed because we've seen + // some unit tests with large numbers of goroutines get into a situation + // where resetChan fills up, at least 1 call to Write() is still trying to + // send to resetChan, the connection gets closed, and this case statement + // attempts to grab the write lock that Write() already has, causing a + // deadlock. + // + // See https://github.com/moby/spdystream/issues/49 for more details. + go func() { + for range resetChan { + } + }() + + go func() { + for range setTimeoutChan { + } + }() + + i.writeLock.Lock() + close(resetChan) + i.resetChan = nil + i.writeLock.Unlock() + + i.setTimeoutLock.Lock() + close(i.setTimeoutChan) + i.setTimeoutChan = nil + i.setTimeoutLock.Unlock() + + break Loop + } + } + + // Drain resetChan + for range resetChan { + } +} + +func (i *idleAwareFramer) WriteFrame(frame spdy.Frame) error { + i.writeLock.Lock() + defer i.writeLock.Unlock() + if i.resetChan == nil { + return io.EOF + } + err := i.f.WriteFrame(frame) + if err != nil { + return err + } + + i.resetChan <- struct{}{} + + return nil +} + +func (i *idleAwareFramer) ReadFrame() (spdy.Frame, error) { + frame, err := i.f.ReadFrame() + if err != nil { + return nil, err + } + + // resetChan should never be closed since it is only closed + // when the connection has closed its closeChan. This closure + // only occurs after all Reads have finished + // TODO (dmcgowan): refactor relationship into connection + i.resetChan <- struct{}{} + + return frame, nil +} + +func (i *idleAwareFramer) setIdleTimeout(timeout time.Duration) { + i.setTimeoutLock.Lock() + defer i.setTimeoutLock.Unlock() + + if i.setTimeoutChan == nil { + return + } + + i.setTimeoutChan <- timeout +} + +type Connection struct { + conn net.Conn + framer *idleAwareFramer + + closeChan chan bool + goneAway bool + lastStreamChan chan<- *Stream + goAwayTimeout time.Duration + closeTimeout time.Duration + + streamLock *sync.RWMutex + streamCond *sync.Cond + streams map[spdy.StreamId]*Stream + + nextIdLock sync.Mutex + receiveIdLock sync.Mutex + nextStreamId spdy.StreamId + receivedStreamId spdy.StreamId + + pingIdLock sync.Mutex + pingId uint32 + pingChans map[uint32]chan error + + shutdownLock sync.Mutex + shutdownChan chan error + hasShutdown bool + + // for testing https://github.com/moby/spdystream/pull/56 + dataFrameHandler func(*spdy.DataFrame) error +} + +// NewConnection creates a new spdy connection from an existing +// network connection. +func NewConnection(conn net.Conn, server bool) (*Connection, error) { + framer, framerErr := spdy.NewFramer(conn, conn) + if framerErr != nil { + return nil, framerErr + } + idleAwareFramer := newIdleAwareFramer(framer) + var sid spdy.StreamId + var rid spdy.StreamId + var pid uint32 + if server { + sid = 2 + rid = 1 + pid = 2 + } else { + sid = 1 + rid = 2 + pid = 1 + } + + streamLock := new(sync.RWMutex) + streamCond := sync.NewCond(streamLock) + + session := &Connection{ + conn: conn, + framer: idleAwareFramer, + + closeChan: make(chan bool), + goAwayTimeout: time.Duration(0), + closeTimeout: time.Duration(0), + + streamLock: streamLock, + streamCond: streamCond, + streams: make(map[spdy.StreamId]*Stream), + nextStreamId: sid, + receivedStreamId: rid, + + pingId: pid, + pingChans: make(map[uint32]chan error), + + shutdownChan: make(chan error), + } + session.dataFrameHandler = session.handleDataFrame + idleAwareFramer.conn = session + go idleAwareFramer.monitor() + + return session, nil +} + +// Ping sends a ping frame across the connection and +// returns the response time +func (s *Connection) Ping() (time.Duration, error) { + pid := s.pingId + s.pingIdLock.Lock() + if s.pingId > 0x7ffffffe { + s.pingId = s.pingId - 0x7ffffffe + } else { + s.pingId = s.pingId + 2 + } + s.pingIdLock.Unlock() + pingChan := make(chan error) + s.pingChans[pid] = pingChan + defer delete(s.pingChans, pid) + + frame := &spdy.PingFrame{Id: pid} + startTime := time.Now() + writeErr := s.framer.WriteFrame(frame) + if writeErr != nil { + return time.Duration(0), writeErr + } + select { + case <-s.closeChan: + return time.Duration(0), errors.New("connection closed") + case err, ok := <-pingChan: + if ok && err != nil { + return time.Duration(0), err + } + break + } + return time.Since(startTime), nil +} + +// Serve handles frames sent from the server, including reply frames +// which are needed to fully initiate connections. Both clients and servers +// should call Serve in a separate goroutine before creating streams. +func (s *Connection) Serve(newHandler StreamHandler) { + // use a WaitGroup to wait for all frames to be drained after receiving + // go-away. + var wg sync.WaitGroup + + // Parition queues to ensure stream frames are handled + // by the same worker, ensuring order is maintained + frameQueues := make([]*PriorityFrameQueue, FRAME_WORKERS) + for i := 0; i < FRAME_WORKERS; i++ { + frameQueues[i] = NewPriorityFrameQueue(QUEUE_SIZE) + + // Ensure frame queue is drained when connection is closed + go func(frameQueue *PriorityFrameQueue) { + <-s.closeChan + frameQueue.Drain() + }(frameQueues[i]) + + wg.Add(1) + go func(frameQueue *PriorityFrameQueue) { + // let the WaitGroup know this worker is done + defer wg.Done() + + s.frameHandler(frameQueue, newHandler) + }(frameQueues[i]) + } + + var ( + partitionRoundRobin int + goAwayFrame *spdy.GoAwayFrame + ) +Loop: + for { + readFrame, err := s.framer.ReadFrame() + if err != nil { + if err != io.EOF { + debugMessage("frame read error: %s", err) + } else { + debugMessage("(%p) EOF received", s) + } + break + } + var priority uint8 + var partition int + switch frame := readFrame.(type) { + case *spdy.SynStreamFrame: + if s.checkStreamFrame(frame) { + priority = frame.Priority + partition = int(frame.StreamId % FRAME_WORKERS) + debugMessage("(%p) Add stream frame: %d ", s, frame.StreamId) + s.addStreamFrame(frame) + } else { + debugMessage("(%p) Rejected stream frame: %d ", s, frame.StreamId) + continue + } + case *spdy.SynReplyFrame: + priority = s.getStreamPriority(frame.StreamId) + partition = int(frame.StreamId % FRAME_WORKERS) + case *spdy.DataFrame: + priority = s.getStreamPriority(frame.StreamId) + partition = int(frame.StreamId % FRAME_WORKERS) + case *spdy.RstStreamFrame: + priority = s.getStreamPriority(frame.StreamId) + partition = int(frame.StreamId % FRAME_WORKERS) + case *spdy.HeadersFrame: + priority = s.getStreamPriority(frame.StreamId) + partition = int(frame.StreamId % FRAME_WORKERS) + case *spdy.PingFrame: + priority = 0 + partition = partitionRoundRobin + partitionRoundRobin = (partitionRoundRobin + 1) % FRAME_WORKERS + case *spdy.GoAwayFrame: + // hold on to the go away frame and exit the loop + goAwayFrame = frame + break Loop + default: + priority = 7 + partition = partitionRoundRobin + partitionRoundRobin = (partitionRoundRobin + 1) % FRAME_WORKERS + } + frameQueues[partition].Push(readFrame, priority) + } + close(s.closeChan) + + // wait for all frame handler workers to indicate they've drained their queues + // before handling the go away frame + wg.Wait() + + if goAwayFrame != nil { + s.handleGoAwayFrame(goAwayFrame) + } + + // now it's safe to close remote channels and empty s.streams + s.streamCond.L.Lock() + // notify streams that they're now closed, which will + // unblock any stream Read() calls + for _, stream := range s.streams { + stream.closeRemoteChannels() + } + s.streams = make(map[spdy.StreamId]*Stream) + s.streamCond.Broadcast() + s.streamCond.L.Unlock() +} + +func (s *Connection) frameHandler(frameQueue *PriorityFrameQueue, newHandler StreamHandler) { + for { + popFrame := frameQueue.Pop() + if popFrame == nil { + return + } + + var frameErr error + switch frame := popFrame.(type) { + case *spdy.SynStreamFrame: + frameErr = s.handleStreamFrame(frame, newHandler) + case *spdy.SynReplyFrame: + frameErr = s.handleReplyFrame(frame) + case *spdy.DataFrame: + frameErr = s.dataFrameHandler(frame) + case *spdy.RstStreamFrame: + frameErr = s.handleResetFrame(frame) + case *spdy.HeadersFrame: + frameErr = s.handleHeaderFrame(frame) + case *spdy.PingFrame: + frameErr = s.handlePingFrame(frame) + case *spdy.GoAwayFrame: + frameErr = s.handleGoAwayFrame(frame) + default: + frameErr = fmt.Errorf("unhandled frame type: %T", frame) + } + + if frameErr != nil { + debugMessage("frame handling error: %s", frameErr) + } + } +} + +func (s *Connection) getStreamPriority(streamId spdy.StreamId) uint8 { + stream, streamOk := s.getStream(streamId) + if !streamOk { + return 7 + } + return stream.priority +} + +func (s *Connection) addStreamFrame(frame *spdy.SynStreamFrame) { + var parent *Stream + if frame.AssociatedToStreamId != spdy.StreamId(0) { + parent, _ = s.getStream(frame.AssociatedToStreamId) + } + + stream := &Stream{ + streamId: frame.StreamId, + parent: parent, + conn: s, + startChan: make(chan error), + headers: frame.Headers, + finished: (frame.CFHeader.Flags & spdy.ControlFlagUnidirectional) != 0x00, + replyCond: sync.NewCond(new(sync.Mutex)), + dataChan: make(chan []byte), + headerChan: make(chan http.Header), + closeChan: make(chan bool), + priority: frame.Priority, + } + if frame.CFHeader.Flags&spdy.ControlFlagFin != 0x00 { + stream.closeRemoteChannels() + } + + s.addStream(stream) +} + +// checkStreamFrame checks to see if a stream frame is allowed. +// If the stream is invalid, then a reset frame with protocol error +// will be returned. +func (s *Connection) checkStreamFrame(frame *spdy.SynStreamFrame) bool { + s.receiveIdLock.Lock() + defer s.receiveIdLock.Unlock() + if s.goneAway { + return false + } + validationErr := s.validateStreamId(frame.StreamId) + if validationErr != nil { + go func() { + resetErr := s.sendResetFrame(spdy.ProtocolError, frame.StreamId) + if resetErr != nil { + debugMessage("reset error: %s", resetErr) + } + }() + return false + } + return true +} + +func (s *Connection) handleStreamFrame(frame *spdy.SynStreamFrame, newHandler StreamHandler) error { + stream, ok := s.getStream(frame.StreamId) + if !ok { + return fmt.Errorf("Missing stream: %d", frame.StreamId) + } + + newHandler(stream) + + return nil +} + +func (s *Connection) handleReplyFrame(frame *spdy.SynReplyFrame) error { + debugMessage("(%p) Reply frame received for %d", s, frame.StreamId) + stream, streamOk := s.getStream(frame.StreamId) + if !streamOk { + debugMessage("Reply frame gone away for %d", frame.StreamId) + // Stream has already gone away + return nil + } + if stream.replied { + // Stream has already received reply + return nil + } + stream.replied = true + + // TODO Check for error + if (frame.CFHeader.Flags & spdy.ControlFlagFin) != 0x00 { + s.remoteStreamFinish(stream) + } + + close(stream.startChan) + + return nil +} + +func (s *Connection) handleResetFrame(frame *spdy.RstStreamFrame) error { + stream, streamOk := s.getStream(frame.StreamId) + if !streamOk { + // Stream has already been removed + return nil + } + s.removeStream(stream) + stream.closeRemoteChannels() + + if !stream.replied { + stream.replied = true + stream.startChan <- ErrReset + close(stream.startChan) + } + + stream.finishLock.Lock() + stream.finished = true + stream.finishLock.Unlock() + + return nil +} + +func (s *Connection) handleHeaderFrame(frame *spdy.HeadersFrame) error { + stream, streamOk := s.getStream(frame.StreamId) + if !streamOk { + // Stream has already gone away + return nil + } + if !stream.replied { + // No reply received...Protocol error? + return nil + } + + // TODO limit headers while not blocking (use buffered chan or goroutine?) + select { + case <-stream.closeChan: + return nil + case stream.headerChan <- frame.Headers: + } + + if (frame.CFHeader.Flags & spdy.ControlFlagFin) != 0x00 { + s.remoteStreamFinish(stream) + } + + return nil +} + +func (s *Connection) handleDataFrame(frame *spdy.DataFrame) error { + debugMessage("(%p) Data frame received for %d", s, frame.StreamId) + stream, streamOk := s.getStream(frame.StreamId) + if !streamOk { + debugMessage("(%p) Data frame gone away for %d", s, frame.StreamId) + // Stream has already gone away + return nil + } + if !stream.replied { + debugMessage("(%p) Data frame not replied %d", s, frame.StreamId) + // No reply received...Protocol error? + return nil + } + + debugMessage("(%p) (%d) Data frame handling", stream, stream.streamId) + if len(frame.Data) > 0 { + stream.dataLock.RLock() + select { + case <-stream.closeChan: + debugMessage("(%p) (%d) Data frame not sent (stream shut down)", stream, stream.streamId) + case stream.dataChan <- frame.Data: + debugMessage("(%p) (%d) Data frame sent", stream, stream.streamId) + } + stream.dataLock.RUnlock() + } + if (frame.Flags & spdy.DataFlagFin) != 0x00 { + s.remoteStreamFinish(stream) + } + return nil +} + +func (s *Connection) handlePingFrame(frame *spdy.PingFrame) error { + if s.pingId&0x01 != frame.Id&0x01 { + return s.framer.WriteFrame(frame) + } + pingChan, pingOk := s.pingChans[frame.Id] + if pingOk { + close(pingChan) + } + return nil +} + +func (s *Connection) handleGoAwayFrame(frame *spdy.GoAwayFrame) error { + debugMessage("(%p) Go away received", s) + s.receiveIdLock.Lock() + if s.goneAway { + s.receiveIdLock.Unlock() + return nil + } + s.goneAway = true + s.receiveIdLock.Unlock() + + if s.lastStreamChan != nil { + stream, _ := s.getStream(frame.LastGoodStreamId) + go func() { + s.lastStreamChan <- stream + }() + } + + // Do not block frame handler waiting for closure + go s.shutdown(s.goAwayTimeout) + + return nil +} + +func (s *Connection) remoteStreamFinish(stream *Stream) { + stream.closeRemoteChannels() + + stream.finishLock.Lock() + if stream.finished { + // Stream is fully closed, cleanup + s.removeStream(stream) + } + stream.finishLock.Unlock() +} + +// CreateStream creates a new spdy stream using the parameters for +// creating the stream frame. The stream frame will be sent upon +// calling this function, however this function does not wait for +// the reply frame. If waiting for the reply is desired, use +// the stream Wait or WaitTimeout function on the stream returned +// by this function. +func (s *Connection) CreateStream(headers http.Header, parent *Stream, fin bool) (*Stream, error) { + // MUST synchronize stream creation (all the way to writing the frame) + // as stream IDs **MUST** increase monotonically. + s.nextIdLock.Lock() + defer s.nextIdLock.Unlock() + + streamId := s.getNextStreamId() + if streamId == 0 { + return nil, fmt.Errorf("Unable to get new stream id") + } + + stream := &Stream{ + streamId: streamId, + parent: parent, + conn: s, + startChan: make(chan error), + headers: headers, + dataChan: make(chan []byte), + headerChan: make(chan http.Header), + closeChan: make(chan bool), + } + + debugMessage("(%p) (%p) Create stream", s, stream) + + s.addStream(stream) + + return stream, s.sendStream(stream, fin) +} + +func (s *Connection) shutdown(closeTimeout time.Duration) { + // TODO Ensure this isn't called multiple times + s.shutdownLock.Lock() + if s.hasShutdown { + s.shutdownLock.Unlock() + return + } + s.hasShutdown = true + s.shutdownLock.Unlock() + + var timeout <-chan time.Time + if closeTimeout > time.Duration(0) { + timeout = time.After(closeTimeout) + } + streamsClosed := make(chan bool) + + go func() { + s.streamCond.L.Lock() + for len(s.streams) > 0 { + debugMessage("Streams opened: %d, %#v", len(s.streams), s.streams) + s.streamCond.Wait() + } + s.streamCond.L.Unlock() + close(streamsClosed) + }() + + var err error + select { + case <-streamsClosed: + // No active streams, close should be safe + err = s.conn.Close() + case <-timeout: + // Force ungraceful close + err = s.conn.Close() + // Wait for cleanup to clear active streams + <-streamsClosed + } + + if err != nil { + duration := 10 * time.Minute + time.AfterFunc(duration, func() { + select { + case err, ok := <-s.shutdownChan: + if ok { + debugMessage("Unhandled close error after %s: %s", duration, err) + } + default: + } + }) + s.shutdownChan <- err + } + close(s.shutdownChan) +} + +// Closes spdy connection by sending GoAway frame and initiating shutdown +func (s *Connection) Close() error { + s.receiveIdLock.Lock() + if s.goneAway { + s.receiveIdLock.Unlock() + return nil + } + s.goneAway = true + s.receiveIdLock.Unlock() + + var lastStreamId spdy.StreamId + if s.receivedStreamId > 2 { + lastStreamId = s.receivedStreamId - 2 + } + + goAwayFrame := &spdy.GoAwayFrame{ + LastGoodStreamId: lastStreamId, + Status: spdy.GoAwayOK, + } + + err := s.framer.WriteFrame(goAwayFrame) + go s.shutdown(s.closeTimeout) + if err != nil { + return err + } + + return nil +} + +// CloseWait closes the connection and waits for shutdown +// to finish. Note the underlying network Connection +// is not closed until the end of shutdown. +func (s *Connection) CloseWait() error { + closeErr := s.Close() + if closeErr != nil { + return closeErr + } + shutdownErr, ok := <-s.shutdownChan + if ok { + return shutdownErr + } + return nil +} + +// Wait waits for the connection to finish shutdown or for +// the wait timeout duration to expire. This needs to be +// called either after Close has been called or the GOAWAYFRAME +// has been received. If the wait timeout is 0, this function +// will block until shutdown finishes. If wait is never called +// and a shutdown error occurs, that error will be logged as an +// unhandled error. +func (s *Connection) Wait(waitTimeout time.Duration) error { + var timeout <-chan time.Time + if waitTimeout > time.Duration(0) { + timeout = time.After(waitTimeout) + } + + select { + case err, ok := <-s.shutdownChan: + if ok { + return err + } + case <-timeout: + return ErrTimeout + } + return nil +} + +// NotifyClose registers a channel to be called when the remote +// peer inidicates connection closure. The last stream to be +// received by the remote will be sent on the channel. The notify +// timeout will determine the duration between go away received +// and the connection being closed. +func (s *Connection) NotifyClose(c chan<- *Stream, timeout time.Duration) { + s.goAwayTimeout = timeout + s.lastStreamChan = c +} + +// SetCloseTimeout sets the amount of time close will wait for +// streams to finish before terminating the underlying network +// connection. Setting the timeout to 0 will cause close to +// wait forever, which is the default. +func (s *Connection) SetCloseTimeout(timeout time.Duration) { + s.closeTimeout = timeout +} + +// SetIdleTimeout sets the amount of time the connection may sit idle before +// it is forcefully terminated. +func (s *Connection) SetIdleTimeout(timeout time.Duration) { + s.framer.setIdleTimeout(timeout) +} + +func (s *Connection) sendHeaders(headers http.Header, stream *Stream, fin bool) error { + var flags spdy.ControlFlags + if fin { + flags = spdy.ControlFlagFin + } + + headerFrame := &spdy.HeadersFrame{ + StreamId: stream.streamId, + Headers: headers, + CFHeader: spdy.ControlFrameHeader{Flags: flags}, + } + + return s.framer.WriteFrame(headerFrame) +} + +func (s *Connection) sendReply(headers http.Header, stream *Stream, fin bool) error { + var flags spdy.ControlFlags + if fin { + flags = spdy.ControlFlagFin + } + + replyFrame := &spdy.SynReplyFrame{ + StreamId: stream.streamId, + Headers: headers, + CFHeader: spdy.ControlFrameHeader{Flags: flags}, + } + + return s.framer.WriteFrame(replyFrame) +} + +func (s *Connection) sendResetFrame(status spdy.RstStreamStatus, streamId spdy.StreamId) error { + resetFrame := &spdy.RstStreamFrame{ + StreamId: streamId, + Status: status, + } + + return s.framer.WriteFrame(resetFrame) +} + +func (s *Connection) sendReset(status spdy.RstStreamStatus, stream *Stream) error { + return s.sendResetFrame(status, stream.streamId) +} + +func (s *Connection) sendStream(stream *Stream, fin bool) error { + var flags spdy.ControlFlags + if fin { + flags = spdy.ControlFlagFin + stream.finished = true + } + + var parentId spdy.StreamId + if stream.parent != nil { + parentId = stream.parent.streamId + } + + streamFrame := &spdy.SynStreamFrame{ + StreamId: spdy.StreamId(stream.streamId), + AssociatedToStreamId: spdy.StreamId(parentId), + Headers: stream.headers, + CFHeader: spdy.ControlFrameHeader{Flags: flags}, + } + + return s.framer.WriteFrame(streamFrame) +} + +// getNextStreamId returns the next sequential id +// every call should produce a unique value or an error +func (s *Connection) getNextStreamId() spdy.StreamId { + sid := s.nextStreamId + if sid > 0x7fffffff { + return 0 + } + s.nextStreamId = s.nextStreamId + 2 + return sid +} + +// PeekNextStreamId returns the next sequential id and keeps the next id untouched +func (s *Connection) PeekNextStreamId() spdy.StreamId { + sid := s.nextStreamId + return sid +} + +func (s *Connection) validateStreamId(rid spdy.StreamId) error { + if rid > 0x7fffffff || rid < s.receivedStreamId { + return ErrInvalidStreamId + } + s.receivedStreamId = rid + 2 + return nil +} + +func (s *Connection) addStream(stream *Stream) { + s.streamCond.L.Lock() + s.streams[stream.streamId] = stream + debugMessage("(%p) (%p) Stream added, broadcasting: %d", s, stream, stream.streamId) + s.streamCond.Broadcast() + s.streamCond.L.Unlock() +} + +func (s *Connection) removeStream(stream *Stream) { + s.streamCond.L.Lock() + delete(s.streams, stream.streamId) + debugMessage("(%p) (%p) Stream removed, broadcasting: %d", s, stream, stream.streamId) + s.streamCond.Broadcast() + s.streamCond.L.Unlock() +} + +func (s *Connection) getStream(streamId spdy.StreamId) (stream *Stream, ok bool) { + s.streamLock.RLock() + stream, ok = s.streams[streamId] + s.streamLock.RUnlock() + return +} + +// FindStream looks up the given stream id and either waits for the +// stream to be found or returns nil if the stream id is no longer +// valid. +func (s *Connection) FindStream(streamId uint32) *Stream { + var stream *Stream + var ok bool + s.streamCond.L.Lock() + stream, ok = s.streams[spdy.StreamId(streamId)] + debugMessage("(%p) Found stream %d? %t", s, spdy.StreamId(streamId), ok) + for !ok && streamId >= uint32(s.receivedStreamId) { + s.streamCond.Wait() + stream, ok = s.streams[spdy.StreamId(streamId)] + } + s.streamCond.L.Unlock() + return stream +} + +func (s *Connection) CloseChan() <-chan bool { + return s.closeChan +} diff --git a/vendor/github.com/moby/spdystream/go.mod b/vendor/github.com/moby/spdystream/go.mod new file mode 100644 index 0000000000..d9b9ad59c7 --- /dev/null +++ b/vendor/github.com/moby/spdystream/go.mod @@ -0,0 +1,5 @@ +module github.com/moby/spdystream + +go 1.13 + +require github.com/gorilla/websocket v1.4.2 diff --git a/vendor/github.com/moby/spdystream/go.sum b/vendor/github.com/moby/spdystream/go.sum new file mode 100644 index 0000000000..85efffd996 --- /dev/null +++ b/vendor/github.com/moby/spdystream/go.sum @@ -0,0 +1,2 @@ +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= diff --git a/vendor/github.com/moby/spdystream/handlers.go b/vendor/github.com/moby/spdystream/handlers.go new file mode 100644 index 0000000000..d68f61f812 --- /dev/null +++ b/vendor/github.com/moby/spdystream/handlers.go @@ -0,0 +1,52 @@ +/* + Copyright 2014-2021 Docker Inc. + + 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. +*/ + +package spdystream + +import ( + "io" + "net/http" +) + +// MirrorStreamHandler mirrors all streams. +func MirrorStreamHandler(stream *Stream) { + replyErr := stream.SendReply(http.Header{}, false) + if replyErr != nil { + return + } + + go func() { + io.Copy(stream, stream) + stream.Close() + }() + go func() { + for { + header, receiveErr := stream.ReceiveHeader() + if receiveErr != nil { + return + } + sendErr := stream.SendHeader(header, false) + if sendErr != nil { + return + } + } + }() +} + +// NoopStreamHandler does nothing when stream connects. +func NoOpStreamHandler(stream *Stream) { + stream.SendReply(http.Header{}, false) +} diff --git a/vendor/github.com/moby/spdystream/priority.go b/vendor/github.com/moby/spdystream/priority.go new file mode 100644 index 0000000000..d8eb3516ca --- /dev/null +++ b/vendor/github.com/moby/spdystream/priority.go @@ -0,0 +1,114 @@ +/* + Copyright 2014-2021 Docker Inc. + + 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. +*/ + +package spdystream + +import ( + "container/heap" + "sync" + + "github.com/moby/spdystream/spdy" +) + +type prioritizedFrame struct { + frame spdy.Frame + priority uint8 + insertId uint64 +} + +type frameQueue []*prioritizedFrame + +func (fq frameQueue) Len() int { + return len(fq) +} + +func (fq frameQueue) Less(i, j int) bool { + if fq[i].priority == fq[j].priority { + return fq[i].insertId < fq[j].insertId + } + return fq[i].priority < fq[j].priority +} + +func (fq frameQueue) Swap(i, j int) { + fq[i], fq[j] = fq[j], fq[i] +} + +func (fq *frameQueue) Push(x interface{}) { + *fq = append(*fq, x.(*prioritizedFrame)) +} + +func (fq *frameQueue) Pop() interface{} { + old := *fq + n := len(old) + *fq = old[0 : n-1] + return old[n-1] +} + +type PriorityFrameQueue struct { + queue *frameQueue + c *sync.Cond + size int + nextInsertId uint64 + drain bool +} + +func NewPriorityFrameQueue(size int) *PriorityFrameQueue { + queue := make(frameQueue, 0, size) + heap.Init(&queue) + + return &PriorityFrameQueue{ + queue: &queue, + size: size, + c: sync.NewCond(&sync.Mutex{}), + } +} + +func (q *PriorityFrameQueue) Push(frame spdy.Frame, priority uint8) { + q.c.L.Lock() + defer q.c.L.Unlock() + for q.queue.Len() >= q.size { + q.c.Wait() + } + pFrame := &prioritizedFrame{ + frame: frame, + priority: priority, + insertId: q.nextInsertId, + } + q.nextInsertId = q.nextInsertId + 1 + heap.Push(q.queue, pFrame) + q.c.Signal() +} + +func (q *PriorityFrameQueue) Pop() spdy.Frame { + q.c.L.Lock() + defer q.c.L.Unlock() + for q.queue.Len() == 0 { + if q.drain { + return nil + } + q.c.Wait() + } + frame := heap.Pop(q.queue).(*prioritizedFrame).frame + q.c.Signal() + return frame +} + +func (q *PriorityFrameQueue) Drain() { + q.c.L.Lock() + defer q.c.L.Unlock() + q.drain = true + q.c.Broadcast() +} diff --git a/vendor/github.com/moby/spdystream/spdy/dictionary.go b/vendor/github.com/moby/spdystream/spdy/dictionary.go new file mode 100644 index 0000000000..392232f174 --- /dev/null +++ b/vendor/github.com/moby/spdystream/spdy/dictionary.go @@ -0,0 +1,203 @@ +/* + Copyright 2014-2021 Docker Inc. + + 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. +*/ + +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package spdy + +// headerDictionary is the dictionary sent to the zlib compressor/decompressor. +var headerDictionary = []byte{ + 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, + 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, + 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, + 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, + 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, + 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, + 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, + 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, + 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, + 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, + 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, + 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, + 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, + 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, + 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, + 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, + 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, + 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, + 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, + 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, + 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, + 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, + 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, + 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, + 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, + 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, + 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, + 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, + 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, + 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, + 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, + 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, + 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, + 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, + 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, + 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, + 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, + 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, + 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, + 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, + 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, + 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, + 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, + 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, + 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, + 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, + 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, + 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, + 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, + 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, + 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, + 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, + 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, + 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, + 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, + 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, + 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, + 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, + 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, + 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, + 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, + 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, + 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, + 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, + 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, + 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, + 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, + 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, + 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, + 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, + 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, + 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, + 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, + 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, + 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, + 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, + 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, + 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, + 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, + 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, + 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, + 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, + 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, + 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, + 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, + 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, + 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, + 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, + 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, + 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, + 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, + 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, + 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, + 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, + 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, + 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, + 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, + 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, + 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, + 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, + 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, + 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, + 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, + 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, + 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, + 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, + 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, + 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, + 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, + 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, + 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, + 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, + 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, + 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, + 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, + 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, + 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, + 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, + 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, + 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e, +} diff --git a/vendor/github.com/moby/spdystream/spdy/read.go b/vendor/github.com/moby/spdystream/spdy/read.go new file mode 100644 index 0000000000..75ea045b8e --- /dev/null +++ b/vendor/github.com/moby/spdystream/spdy/read.go @@ -0,0 +1,364 @@ +/* + Copyright 2014-2021 Docker Inc. + + 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. +*/ + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package spdy + +import ( + "compress/zlib" + "encoding/binary" + "io" + "net/http" + "strings" +) + +func (frame *SynStreamFrame) read(h ControlFrameHeader, f *Framer) error { + return f.readSynStreamFrame(h, frame) +} + +func (frame *SynReplyFrame) read(h ControlFrameHeader, f *Framer) error { + return f.readSynReplyFrame(h, frame) +} + +func (frame *RstStreamFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { + return err + } + if frame.Status == 0 { + return &Error{InvalidControlFrame, frame.StreamId} + } + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + return nil +} + +func (frame *SettingsFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + var numSettings uint32 + if err := binary.Read(f.r, binary.BigEndian, &numSettings); err != nil { + return err + } + frame.FlagIdValues = make([]SettingsFlagIdValue, numSettings) + for i := uint32(0); i < numSettings; i++ { + if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Id); err != nil { + return err + } + frame.FlagIdValues[i].Flag = SettingsFlag((frame.FlagIdValues[i].Id & 0xff000000) >> 24) + frame.FlagIdValues[i].Id &= 0xffffff + if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Value); err != nil { + return err + } + } + return nil +} + +func (frame *PingFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.Id); err != nil { + return err + } + if frame.Id == 0 { + return &Error{ZeroStreamId, 0} + } + if frame.CFHeader.Flags != 0 { + return &Error{InvalidControlFrame, StreamId(frame.Id)} + } + return nil +} + +func (frame *GoAwayFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.LastGoodStreamId); err != nil { + return err + } + if frame.CFHeader.Flags != 0 { + return &Error{InvalidControlFrame, frame.LastGoodStreamId} + } + if frame.CFHeader.length != 8 { + return &Error{InvalidControlFrame, frame.LastGoodStreamId} + } + if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { + return err + } + return nil +} + +func (frame *HeadersFrame) read(h ControlFrameHeader, f *Framer) error { + return f.readHeadersFrame(h, frame) +} + +func (frame *WindowUpdateFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + if frame.CFHeader.Flags != 0 { + return &Error{InvalidControlFrame, frame.StreamId} + } + if frame.CFHeader.length != 8 { + return &Error{InvalidControlFrame, frame.StreamId} + } + if err := binary.Read(f.r, binary.BigEndian, &frame.DeltaWindowSize); err != nil { + return err + } + return nil +} + +func newControlFrame(frameType ControlFrameType) (controlFrame, error) { + ctor, ok := cframeCtor[frameType] + if !ok { + return nil, &Error{Err: InvalidControlFrame} + } + return ctor(), nil +} + +var cframeCtor = map[ControlFrameType]func() controlFrame{ + TypeSynStream: func() controlFrame { return new(SynStreamFrame) }, + TypeSynReply: func() controlFrame { return new(SynReplyFrame) }, + TypeRstStream: func() controlFrame { return new(RstStreamFrame) }, + TypeSettings: func() controlFrame { return new(SettingsFrame) }, + TypePing: func() controlFrame { return new(PingFrame) }, + TypeGoAway: func() controlFrame { return new(GoAwayFrame) }, + TypeHeaders: func() controlFrame { return new(HeadersFrame) }, + TypeWindowUpdate: func() controlFrame { return new(WindowUpdateFrame) }, +} + +func (f *Framer) uncorkHeaderDecompressor(payloadSize int64) error { + if f.headerDecompressor != nil { + f.headerReader.N = payloadSize + return nil + } + f.headerReader = io.LimitedReader{R: f.r, N: payloadSize} + decompressor, err := zlib.NewReaderDict(&f.headerReader, []byte(headerDictionary)) + if err != nil { + return err + } + f.headerDecompressor = decompressor + return nil +} + +// ReadFrame reads SPDY encoded data and returns a decompressed Frame. +func (f *Framer) ReadFrame() (Frame, error) { + var firstWord uint32 + if err := binary.Read(f.r, binary.BigEndian, &firstWord); err != nil { + return nil, err + } + if firstWord&0x80000000 != 0 { + frameType := ControlFrameType(firstWord & 0xffff) + version := uint16(firstWord >> 16 & 0x7fff) + return f.parseControlFrame(version, frameType) + } + return f.parseDataFrame(StreamId(firstWord & 0x7fffffff)) +} + +func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) (Frame, error) { + var length uint32 + if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { + return nil, err + } + flags := ControlFlags((length & 0xff000000) >> 24) + length &= 0xffffff + header := ControlFrameHeader{version, frameType, flags, length} + cframe, err := newControlFrame(frameType) + if err != nil { + return nil, err + } + if err = cframe.read(header, f); err != nil { + return nil, err + } + return cframe, nil +} + +func parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error) { + var numHeaders uint32 + if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil { + return nil, err + } + var e error + h := make(http.Header, int(numHeaders)) + for i := 0; i < int(numHeaders); i++ { + var length uint32 + if err := binary.Read(r, binary.BigEndian, &length); err != nil { + return nil, err + } + nameBytes := make([]byte, length) + if _, err := io.ReadFull(r, nameBytes); err != nil { + return nil, err + } + name := string(nameBytes) + if name != strings.ToLower(name) { + e = &Error{UnlowercasedHeaderName, streamId} + name = strings.ToLower(name) + } + if h[name] != nil { + e = &Error{DuplicateHeaders, streamId} + } + if err := binary.Read(r, binary.BigEndian, &length); err != nil { + return nil, err + } + value := make([]byte, length) + if _, err := io.ReadFull(r, value); err != nil { + return nil, err + } + valueList := strings.Split(string(value), headerValueSeparator) + for _, v := range valueList { + h.Add(name, v) + } + } + if e != nil { + return h, e + } + return h, nil +} + +func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame) error { + frame.CFHeader = h + var err error + if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + if err = binary.Read(f.r, binary.BigEndian, &frame.AssociatedToStreamId); err != nil { + return err + } + if err = binary.Read(f.r, binary.BigEndian, &frame.Priority); err != nil { + return err + } + frame.Priority >>= 5 + if err = binary.Read(f.r, binary.BigEndian, &frame.Slot); err != nil { + return err + } + reader := f.r + if !f.headerCompressionDisabled { + err := f.uncorkHeaderDecompressor(int64(h.length - 10)) + if err != nil { + return err + } + reader = f.headerDecompressor + } + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } + if err != nil { + return err + } + for h := range frame.Headers { + if invalidReqHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + return nil +} + +func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) error { + frame.CFHeader = h + var err error + if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + reader := f.r + if !f.headerCompressionDisabled { + err := f.uncorkHeaderDecompressor(int64(h.length - 4)) + if err != nil { + return err + } + reader = f.headerDecompressor + } + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } + if err != nil { + return err + } + for h := range frame.Headers { + if invalidRespHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + return nil +} + +func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) error { + frame.CFHeader = h + var err error + if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + reader := f.r + if !f.headerCompressionDisabled { + err := f.uncorkHeaderDecompressor(int64(h.length - 4)) + if err != nil { + return err + } + reader = f.headerDecompressor + } + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } + if err != nil { + return err + } + var invalidHeaders map[string]bool + if frame.StreamId%2 == 0 { + invalidHeaders = invalidReqHeaders + } else { + invalidHeaders = invalidRespHeaders + } + for h := range frame.Headers { + if invalidHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + return nil +} + +func (f *Framer) parseDataFrame(streamId StreamId) (*DataFrame, error) { + var length uint32 + if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { + return nil, err + } + var frame DataFrame + frame.StreamId = streamId + frame.Flags = DataFlags(length >> 24) + length &= 0xffffff + frame.Data = make([]byte, length) + if _, err := io.ReadFull(f.r, frame.Data); err != nil { + return nil, err + } + if frame.StreamId == 0 { + return nil, &Error{ZeroStreamId, 0} + } + return &frame, nil +} diff --git a/vendor/github.com/moby/spdystream/spdy/types.go b/vendor/github.com/moby/spdystream/spdy/types.go new file mode 100644 index 0000000000..a254a43ab9 --- /dev/null +++ b/vendor/github.com/moby/spdystream/spdy/types.go @@ -0,0 +1,291 @@ +/* + Copyright 2014-2021 Docker Inc. + + 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. +*/ + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package spdy implements the SPDY protocol (currently SPDY/3), described in +// http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3. +package spdy + +import ( + "bytes" + "compress/zlib" + "io" + "net/http" +) + +// Version is the protocol version number that this package implements. +const Version = 3 + +// ControlFrameType stores the type field in a control frame header. +type ControlFrameType uint16 + +const ( + TypeSynStream ControlFrameType = 0x0001 + TypeSynReply ControlFrameType = 0x0002 + TypeRstStream ControlFrameType = 0x0003 + TypeSettings ControlFrameType = 0x0004 + TypePing ControlFrameType = 0x0006 + TypeGoAway ControlFrameType = 0x0007 + TypeHeaders ControlFrameType = 0x0008 + TypeWindowUpdate ControlFrameType = 0x0009 +) + +// ControlFlags are the flags that can be set on a control frame. +type ControlFlags uint8 + +const ( + ControlFlagFin ControlFlags = 0x01 + ControlFlagUnidirectional ControlFlags = 0x02 + ControlFlagSettingsClearSettings ControlFlags = 0x01 +) + +// DataFlags are the flags that can be set on a data frame. +type DataFlags uint8 + +const ( + DataFlagFin DataFlags = 0x01 +) + +// MaxDataLength is the maximum number of bytes that can be stored in one frame. +const MaxDataLength = 1<<24 - 1 + +// headerValueSepator separates multiple header values. +const headerValueSeparator = "\x00" + +// Frame is a single SPDY frame in its unpacked in-memory representation. Use +// Framer to read and write it. +type Frame interface { + write(f *Framer) error +} + +// ControlFrameHeader contains all the fields in a control frame header, +// in its unpacked in-memory representation. +type ControlFrameHeader struct { + // Note, high bit is the "Control" bit. + version uint16 // spdy version number + frameType ControlFrameType + Flags ControlFlags + length uint32 // length of data field +} + +type controlFrame interface { + Frame + read(h ControlFrameHeader, f *Framer) error +} + +// StreamId represents a 31-bit value identifying the stream. +type StreamId uint32 + +// SynStreamFrame is the unpacked, in-memory representation of a SYN_STREAM +// frame. +type SynStreamFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + AssociatedToStreamId StreamId // stream id for a stream which this stream is associated to + Priority uint8 // priority of this frame (3-bit) + Slot uint8 // index in the server's credential vector of the client certificate + Headers http.Header +} + +// SynReplyFrame is the unpacked, in-memory representation of a SYN_REPLY frame. +type SynReplyFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + Headers http.Header +} + +// RstStreamStatus represents the status that led to a RST_STREAM. +type RstStreamStatus uint32 + +const ( + ProtocolError RstStreamStatus = iota + 1 + InvalidStream + RefusedStream + UnsupportedVersion + Cancel + InternalError + FlowControlError + StreamInUse + StreamAlreadyClosed + InvalidCredentials + FrameTooLarge +) + +// RstStreamFrame is the unpacked, in-memory representation of a RST_STREAM +// frame. +type RstStreamFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + Status RstStreamStatus +} + +// SettingsFlag represents a flag in a SETTINGS frame. +type SettingsFlag uint8 + +const ( + FlagSettingsPersistValue SettingsFlag = 0x1 + FlagSettingsPersisted SettingsFlag = 0x2 +) + +// SettingsFlag represents the id of an id/value pair in a SETTINGS frame. +type SettingsId uint32 + +const ( + SettingsUploadBandwidth SettingsId = iota + 1 + SettingsDownloadBandwidth + SettingsRoundTripTime + SettingsMaxConcurrentStreams + SettingsCurrentCwnd + SettingsDownloadRetransRate + SettingsInitialWindowSize + SettingsClientCretificateVectorSize +) + +// SettingsFlagIdValue is the unpacked, in-memory representation of the +// combined flag/id/value for a setting in a SETTINGS frame. +type SettingsFlagIdValue struct { + Flag SettingsFlag + Id SettingsId + Value uint32 +} + +// SettingsFrame is the unpacked, in-memory representation of a SPDY +// SETTINGS frame. +type SettingsFrame struct { + CFHeader ControlFrameHeader + FlagIdValues []SettingsFlagIdValue +} + +// PingFrame is the unpacked, in-memory representation of a PING frame. +type PingFrame struct { + CFHeader ControlFrameHeader + Id uint32 // unique id for this ping, from server is even, from client is odd. +} + +// GoAwayStatus represents the status in a GoAwayFrame. +type GoAwayStatus uint32 + +const ( + GoAwayOK GoAwayStatus = iota + GoAwayProtocolError + GoAwayInternalError +) + +// GoAwayFrame is the unpacked, in-memory representation of a GOAWAY frame. +type GoAwayFrame struct { + CFHeader ControlFrameHeader + LastGoodStreamId StreamId // last stream id which was accepted by sender + Status GoAwayStatus +} + +// HeadersFrame is the unpacked, in-memory representation of a HEADERS frame. +type HeadersFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + Headers http.Header +} + +// WindowUpdateFrame is the unpacked, in-memory representation of a +// WINDOW_UPDATE frame. +type WindowUpdateFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + DeltaWindowSize uint32 // additional number of bytes to existing window size +} + +// TODO: Implement credential frame and related methods. + +// DataFrame is the unpacked, in-memory representation of a DATA frame. +type DataFrame struct { + // Note, high bit is the "Control" bit. Should be 0 for data frames. + StreamId StreamId + Flags DataFlags + Data []byte // payload data of this frame +} + +// A SPDY specific error. +type ErrorCode string + +const ( + UnlowercasedHeaderName ErrorCode = "header was not lowercased" + DuplicateHeaders ErrorCode = "multiple headers with same name" + WrongCompressedPayloadSize ErrorCode = "compressed payload size was incorrect" + UnknownFrameType ErrorCode = "unknown frame type" + InvalidControlFrame ErrorCode = "invalid control frame" + InvalidDataFrame ErrorCode = "invalid data frame" + InvalidHeaderPresent ErrorCode = "frame contained invalid header" + ZeroStreamId ErrorCode = "stream id zero is disallowed" +) + +// Error contains both the type of error and additional values. StreamId is 0 +// if Error is not associated with a stream. +type Error struct { + Err ErrorCode + StreamId StreamId +} + +func (e *Error) Error() string { + return string(e.Err) +} + +var invalidReqHeaders = map[string]bool{ + "Connection": true, + "Host": true, + "Keep-Alive": true, + "Proxy-Connection": true, + "Transfer-Encoding": true, +} + +var invalidRespHeaders = map[string]bool{ + "Connection": true, + "Keep-Alive": true, + "Proxy-Connection": true, + "Transfer-Encoding": true, +} + +// Framer handles serializing/deserializing SPDY frames, including compressing/ +// decompressing payloads. +type Framer struct { + headerCompressionDisabled bool + w io.Writer + headerBuf *bytes.Buffer + headerCompressor *zlib.Writer + r io.Reader + headerReader io.LimitedReader + headerDecompressor io.ReadCloser +} + +// NewFramer allocates a new Framer for a given SPDY connection, represented by +// a io.Writer and io.Reader. Note that Framer will read and write individual fields +// from/to the Reader and Writer, so the caller should pass in an appropriately +// buffered implementation to optimize performance. +func NewFramer(w io.Writer, r io.Reader) (*Framer, error) { + compressBuf := new(bytes.Buffer) + compressor, err := zlib.NewWriterLevelDict(compressBuf, zlib.BestCompression, []byte(headerDictionary)) + if err != nil { + return nil, err + } + framer := &Framer{ + w: w, + headerBuf: compressBuf, + headerCompressor: compressor, + r: r, + } + return framer, nil +} diff --git a/vendor/github.com/moby/spdystream/spdy/write.go b/vendor/github.com/moby/spdystream/spdy/write.go new file mode 100644 index 0000000000..ab6d91f3b8 --- /dev/null +++ b/vendor/github.com/moby/spdystream/spdy/write.go @@ -0,0 +1,334 @@ +/* + Copyright 2014-2021 Docker Inc. + + 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. +*/ + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package spdy + +import ( + "encoding/binary" + "io" + "net/http" + "strings" +) + +func (frame *SynStreamFrame) write(f *Framer) error { + return f.writeSynStreamFrame(frame) +} + +func (frame *SynReplyFrame) write(f *Framer) error { + return f.writeSynReplyFrame(frame) +} + +func (frame *RstStreamFrame) write(f *Framer) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeRstStream + frame.CFHeader.Flags = 0 + frame.CFHeader.length = 8 + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if frame.Status == 0 { + return &Error{InvalidControlFrame, frame.StreamId} + } + if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { + return + } + return +} + +func (frame *SettingsFrame) write(f *Framer) (err error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSettings + frame.CFHeader.length = uint32(len(frame.FlagIdValues)*8 + 4) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, uint32(len(frame.FlagIdValues))); err != nil { + return + } + for _, flagIdValue := range frame.FlagIdValues { + flagId := uint32(flagIdValue.Flag)<<24 | uint32(flagIdValue.Id) + if err = binary.Write(f.w, binary.BigEndian, flagId); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, flagIdValue.Value); err != nil { + return + } + } + return +} + +func (frame *PingFrame) write(f *Framer) (err error) { + if frame.Id == 0 { + return &Error{ZeroStreamId, 0} + } + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypePing + frame.CFHeader.Flags = 0 + frame.CFHeader.length = 4 + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.Id); err != nil { + return + } + return +} + +func (frame *GoAwayFrame) write(f *Framer) (err error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeGoAway + frame.CFHeader.Flags = 0 + frame.CFHeader.length = 8 + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.LastGoodStreamId); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { + return + } + return nil +} + +func (frame *HeadersFrame) write(f *Framer) error { + return f.writeHeadersFrame(frame) +} + +func (frame *WindowUpdateFrame) write(f *Framer) (err error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeWindowUpdate + frame.CFHeader.Flags = 0 + frame.CFHeader.length = 8 + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.DeltaWindowSize); err != nil { + return + } + return nil +} + +func (frame *DataFrame) write(f *Framer) error { + return f.writeDataFrame(frame) +} + +// WriteFrame writes a frame. +func (f *Framer) WriteFrame(frame Frame) error { + return frame.write(f) +} + +func writeControlFrameHeader(w io.Writer, h ControlFrameHeader) error { + if err := binary.Write(w, binary.BigEndian, 0x8000|h.version); err != nil { + return err + } + if err := binary.Write(w, binary.BigEndian, h.frameType); err != nil { + return err + } + flagsAndLength := uint32(h.Flags)<<24 | h.length + if err := binary.Write(w, binary.BigEndian, flagsAndLength); err != nil { + return err + } + return nil +} + +func writeHeaderValueBlock(w io.Writer, h http.Header) (n int, err error) { + n = 0 + if err = binary.Write(w, binary.BigEndian, uint32(len(h))); err != nil { + return + } + n += 2 + for name, values := range h { + if err = binary.Write(w, binary.BigEndian, uint32(len(name))); err != nil { + return + } + n += 2 + name = strings.ToLower(name) + if _, err = io.WriteString(w, name); err != nil { + return + } + n += len(name) + v := strings.Join(values, headerValueSeparator) + if err = binary.Write(w, binary.BigEndian, uint32(len(v))); err != nil { + return + } + n += 2 + if _, err = io.WriteString(w, v); err != nil { + return + } + n += len(v) + } + return +} + +func (f *Framer) writeSynStreamFrame(frame *SynStreamFrame) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + // Marshal the headers. + var writer io.Writer = f.headerBuf + if !f.headerCompressionDisabled { + writer = f.headerCompressor + } + if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { + return + } + if !f.headerCompressionDisabled { + f.headerCompressor.Flush() + } + + // Set ControlFrameHeader. + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSynStream + frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 10) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.AssociatedToStreamId); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.Priority<<5); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.Slot); err != nil { + return err + } + if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { + return err + } + f.headerBuf.Reset() + return nil +} + +func (f *Framer) writeSynReplyFrame(frame *SynReplyFrame) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + // Marshal the headers. + var writer io.Writer = f.headerBuf + if !f.headerCompressionDisabled { + writer = f.headerCompressor + } + if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { + return + } + if !f.headerCompressionDisabled { + f.headerCompressor.Flush() + } + + // Set ControlFrameHeader. + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSynReply + frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { + return + } + f.headerBuf.Reset() + return +} + +func (f *Framer) writeHeadersFrame(frame *HeadersFrame) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + // Marshal the headers. + var writer io.Writer = f.headerBuf + if !f.headerCompressionDisabled { + writer = f.headerCompressor + } + if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { + return + } + if !f.headerCompressionDisabled { + f.headerCompressor.Flush() + } + + // Set ControlFrameHeader. + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeHeaders + frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { + return + } + f.headerBuf.Reset() + return +} + +func (f *Framer) writeDataFrame(frame *DataFrame) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + if frame.StreamId&0x80000000 != 0 || len(frame.Data) > MaxDataLength { + return &Error{InvalidDataFrame, frame.StreamId} + } + + // Serialize frame to Writer. + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + flagsAndLength := uint32(frame.Flags)<<24 | uint32(len(frame.Data)) + if err = binary.Write(f.w, binary.BigEndian, flagsAndLength); err != nil { + return + } + if _, err = f.w.Write(frame.Data); err != nil { + return + } + return nil +} diff --git a/vendor/github.com/moby/spdystream/stream.go b/vendor/github.com/moby/spdystream/stream.go new file mode 100644 index 0000000000..404e3c02df --- /dev/null +++ b/vendor/github.com/moby/spdystream/stream.go @@ -0,0 +1,343 @@ +/* + Copyright 2014-2021 Docker Inc. + + 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. +*/ + +package spdystream + +import ( + "errors" + "fmt" + "io" + "net" + "net/http" + "sync" + "time" + + "github.com/moby/spdystream/spdy" +) + +var ( + ErrUnreadPartialData = errors.New("unread partial data") +) + +type Stream struct { + streamId spdy.StreamId + parent *Stream + conn *Connection + startChan chan error + + dataLock sync.RWMutex + dataChan chan []byte + unread []byte + + priority uint8 + headers http.Header + headerChan chan http.Header + finishLock sync.Mutex + finished bool + replyCond *sync.Cond + replied bool + closeLock sync.Mutex + closeChan chan bool +} + +// WriteData writes data to stream, sending a dataframe per call +func (s *Stream) WriteData(data []byte, fin bool) error { + s.waitWriteReply() + var flags spdy.DataFlags + + if fin { + flags = spdy.DataFlagFin + s.finishLock.Lock() + if s.finished { + s.finishLock.Unlock() + return ErrWriteClosedStream + } + s.finished = true + s.finishLock.Unlock() + } + + dataFrame := &spdy.DataFrame{ + StreamId: s.streamId, + Flags: flags, + Data: data, + } + + debugMessage("(%p) (%d) Writing data frame", s, s.streamId) + return s.conn.framer.WriteFrame(dataFrame) +} + +// Write writes bytes to a stream, calling write data for each call. +func (s *Stream) Write(data []byte) (n int, err error) { + err = s.WriteData(data, false) + if err == nil { + n = len(data) + } + return +} + +// Read reads bytes from a stream, a single read will never get more +// than what is sent on a single data frame, but a multiple calls to +// read may get data from the same data frame. +func (s *Stream) Read(p []byte) (n int, err error) { + if s.unread == nil { + select { + case <-s.closeChan: + return 0, io.EOF + case read, ok := <-s.dataChan: + if !ok { + return 0, io.EOF + } + s.unread = read + } + } + n = copy(p, s.unread) + if n < len(s.unread) { + s.unread = s.unread[n:] + } else { + s.unread = nil + } + return +} + +// ReadData reads an entire data frame and returns the byte array +// from the data frame. If there is unread data from the result +// of a Read call, this function will return an ErrUnreadPartialData. +func (s *Stream) ReadData() ([]byte, error) { + debugMessage("(%p) Reading data from %d", s, s.streamId) + if s.unread != nil { + return nil, ErrUnreadPartialData + } + select { + case <-s.closeChan: + return nil, io.EOF + case read, ok := <-s.dataChan: + if !ok { + return nil, io.EOF + } + return read, nil + } +} + +func (s *Stream) waitWriteReply() { + if s.replyCond != nil { + s.replyCond.L.Lock() + for !s.replied { + s.replyCond.Wait() + } + s.replyCond.L.Unlock() + } +} + +// Wait waits for the stream to receive a reply. +func (s *Stream) Wait() error { + return s.WaitTimeout(time.Duration(0)) +} + +// WaitTimeout waits for the stream to receive a reply or for timeout. +// When the timeout is reached, ErrTimeout will be returned. +func (s *Stream) WaitTimeout(timeout time.Duration) error { + var timeoutChan <-chan time.Time + if timeout > time.Duration(0) { + timeoutChan = time.After(timeout) + } + + select { + case err := <-s.startChan: + if err != nil { + return err + } + break + case <-timeoutChan: + return ErrTimeout + } + return nil +} + +// Close closes the stream by sending an empty data frame with the +// finish flag set, indicating this side is finished with the stream. +func (s *Stream) Close() error { + select { + case <-s.closeChan: + // Stream is now fully closed + s.conn.removeStream(s) + default: + break + } + return s.WriteData([]byte{}, true) +} + +// Reset sends a reset frame, putting the stream into the fully closed state. +func (s *Stream) Reset() error { + s.conn.removeStream(s) + return s.resetStream() +} + +func (s *Stream) resetStream() error { + // Always call closeRemoteChannels, even if s.finished is already true. + // This makes it so that stream.Close() followed by stream.Reset() allows + // stream.Read() to unblock. + s.closeRemoteChannels() + + s.finishLock.Lock() + if s.finished { + s.finishLock.Unlock() + return nil + } + s.finished = true + s.finishLock.Unlock() + + resetFrame := &spdy.RstStreamFrame{ + StreamId: s.streamId, + Status: spdy.Cancel, + } + return s.conn.framer.WriteFrame(resetFrame) +} + +// CreateSubStream creates a stream using the current as the parent +func (s *Stream) CreateSubStream(headers http.Header, fin bool) (*Stream, error) { + return s.conn.CreateStream(headers, s, fin) +} + +// SetPriority sets the stream priority, does not affect the +// remote priority of this stream after Open has been called. +// Valid values are 0 through 7, 0 being the highest priority +// and 7 the lowest. +func (s *Stream) SetPriority(priority uint8) { + s.priority = priority +} + +// SendHeader sends a header frame across the stream +func (s *Stream) SendHeader(headers http.Header, fin bool) error { + return s.conn.sendHeaders(headers, s, fin) +} + +// SendReply sends a reply on a stream, only valid to be called once +// when handling a new stream +func (s *Stream) SendReply(headers http.Header, fin bool) error { + if s.replyCond == nil { + return errors.New("cannot reply on initiated stream") + } + s.replyCond.L.Lock() + defer s.replyCond.L.Unlock() + if s.replied { + return nil + } + + err := s.conn.sendReply(headers, s, fin) + if err != nil { + return err + } + + s.replied = true + s.replyCond.Broadcast() + return nil +} + +// Refuse sends a reset frame with the status refuse, only +// valid to be called once when handling a new stream. This +// may be used to indicate that a stream is not allowed +// when http status codes are not being used. +func (s *Stream) Refuse() error { + if s.replied { + return nil + } + s.replied = true + return s.conn.sendReset(spdy.RefusedStream, s) +} + +// Cancel sends a reset frame with the status canceled. This +// can be used at any time by the creator of the Stream to +// indicate the stream is no longer needed. +func (s *Stream) Cancel() error { + return s.conn.sendReset(spdy.Cancel, s) +} + +// ReceiveHeader receives a header sent on the other side +// of the stream. This function will block until a header +// is received or stream is closed. +func (s *Stream) ReceiveHeader() (http.Header, error) { + select { + case <-s.closeChan: + break + case header, ok := <-s.headerChan: + if !ok { + return nil, fmt.Errorf("header chan closed") + } + return header, nil + } + return nil, fmt.Errorf("stream closed") +} + +// Parent returns the parent stream +func (s *Stream) Parent() *Stream { + return s.parent +} + +// Headers returns the headers used to create the stream +func (s *Stream) Headers() http.Header { + return s.headers +} + +// String returns the string version of stream using the +// streamId to uniquely identify the stream +func (s *Stream) String() string { + return fmt.Sprintf("stream:%d", s.streamId) +} + +// Identifier returns a 32 bit identifier for the stream +func (s *Stream) Identifier() uint32 { + return uint32(s.streamId) +} + +// IsFinished returns whether the stream has finished +// sending data +func (s *Stream) IsFinished() bool { + return s.finished +} + +// Implement net.Conn interface + +func (s *Stream) LocalAddr() net.Addr { + return s.conn.conn.LocalAddr() +} + +func (s *Stream) RemoteAddr() net.Addr { + return s.conn.conn.RemoteAddr() +} + +// TODO set per stream values instead of connection-wide + +func (s *Stream) SetDeadline(t time.Time) error { + return s.conn.conn.SetDeadline(t) +} + +func (s *Stream) SetReadDeadline(t time.Time) error { + return s.conn.conn.SetReadDeadline(t) +} + +func (s *Stream) SetWriteDeadline(t time.Time) error { + return s.conn.conn.SetWriteDeadline(t) +} + +func (s *Stream) closeRemoteChannels() { + s.closeLock.Lock() + defer s.closeLock.Unlock() + select { + case <-s.closeChan: + default: + close(s.closeChan) + } +} diff --git a/vendor/github.com/moby/spdystream/utils.go b/vendor/github.com/moby/spdystream/utils.go new file mode 100644 index 0000000000..e9f7fffd60 --- /dev/null +++ b/vendor/github.com/moby/spdystream/utils.go @@ -0,0 +1,32 @@ +/* + Copyright 2014-2021 Docker Inc. + + 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. +*/ + +package spdystream + +import ( + "log" + "os" +) + +var ( + DEBUG = os.Getenv("DEBUG") +) + +func debugMessage(fmt string, args ...interface{}) { + if DEBUG != "" { + log.Printf(fmt, args...) + } +} diff --git a/vendor/github.com/moby/sys/mount/LICENSE b/vendor/github.com/moby/sys/mount/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/github.com/moby/sys/mount/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/moby/sys/mount/doc.go b/vendor/github.com/moby/sys/mount/doc.go new file mode 100644 index 0000000000..86c2e01bbd --- /dev/null +++ b/vendor/github.com/moby/sys/mount/doc.go @@ -0,0 +1,4 @@ +// Package mount provides a set of functions to mount and unmount mounts. +// +// Currently it supports Linux. For historical reasons, there is also some support for FreeBSD. +package mount diff --git a/vendor/github.com/moby/sys/mount/flags_bsd.go b/vendor/github.com/moby/sys/mount/flags_bsd.go new file mode 100644 index 0000000000..27d8440aab --- /dev/null +++ b/vendor/github.com/moby/sys/mount/flags_bsd.go @@ -0,0 +1,45 @@ +// +build freebsd openbsd + +package mount + +import "golang.org/x/sys/unix" + +const ( + // RDONLY will mount the filesystem as read-only. + RDONLY = unix.MNT_RDONLY + + // NOSUID will not allow set-user-identifier or set-group-identifier bits to + // take effect. + NOSUID = unix.MNT_NOSUID + + // NOEXEC will not allow execution of any binaries on the mounted file system. + NOEXEC = unix.MNT_NOEXEC + + // SYNCHRONOUS will allow any I/O to the file system to be done synchronously. + SYNCHRONOUS = unix.MNT_SYNCHRONOUS + + // NOATIME will not update the file access time when reading from a file. + NOATIME = unix.MNT_NOATIME +) + +// These flags are unsupported. +const ( + BIND = 0 + DIRSYNC = 0 + MANDLOCK = 0 + NODEV = 0 + NODIRATIME = 0 + UNBINDABLE = 0 + RUNBINDABLE = 0 + PRIVATE = 0 + RPRIVATE = 0 + SHARED = 0 + RSHARED = 0 + SLAVE = 0 + RSLAVE = 0 + RBIND = 0 + RELATIME = 0 + REMOUNT = 0 + STRICTATIME = 0 + mntDetach = 0 +) diff --git a/vendor/github.com/moby/sys/mount/flags_linux.go b/vendor/github.com/moby/sys/mount/flags_linux.go new file mode 100644 index 0000000000..0425d0dd63 --- /dev/null +++ b/vendor/github.com/moby/sys/mount/flags_linux.go @@ -0,0 +1,87 @@ +package mount + +import ( + "golang.org/x/sys/unix" +) + +const ( + // RDONLY will mount the file system read-only. + RDONLY = unix.MS_RDONLY + + // NOSUID will not allow set-user-identifier or set-group-identifier bits to + // take effect. + NOSUID = unix.MS_NOSUID + + // NODEV will not interpret character or block special devices on the file + // system. + NODEV = unix.MS_NODEV + + // NOEXEC will not allow execution of any binaries on the mounted file system. + NOEXEC = unix.MS_NOEXEC + + // SYNCHRONOUS will allow I/O to the file system to be done synchronously. + SYNCHRONOUS = unix.MS_SYNCHRONOUS + + // DIRSYNC will force all directory updates within the file system to be done + // synchronously. This affects the following system calls: create, link, + // unlink, symlink, mkdir, rmdir, mknod and rename. + DIRSYNC = unix.MS_DIRSYNC + + // REMOUNT will attempt to remount an already-mounted file system. This is + // commonly used to change the mount flags for a file system, especially to + // make a readonly file system writeable. It does not change device or mount + // point. + REMOUNT = unix.MS_REMOUNT + + // MANDLOCK will force mandatory locks on a filesystem. + MANDLOCK = unix.MS_MANDLOCK + + // NOATIME will not update the file access time when reading from a file. + NOATIME = unix.MS_NOATIME + + // NODIRATIME will not update the directory access time. + NODIRATIME = unix.MS_NODIRATIME + + // BIND remounts a subtree somewhere else. + BIND = unix.MS_BIND + + // RBIND remounts a subtree and all possible submounts somewhere else. + RBIND = unix.MS_BIND | unix.MS_REC + + // UNBINDABLE creates a mount which cannot be cloned through a bind operation. + UNBINDABLE = unix.MS_UNBINDABLE + + // RUNBINDABLE marks the entire mount tree as UNBINDABLE. + RUNBINDABLE = unix.MS_UNBINDABLE | unix.MS_REC + + // PRIVATE creates a mount which carries no propagation abilities. + PRIVATE = unix.MS_PRIVATE + + // RPRIVATE marks the entire mount tree as PRIVATE. + RPRIVATE = unix.MS_PRIVATE | unix.MS_REC + + // SLAVE creates a mount which receives propagation from its master, but not + // vice versa. + SLAVE = unix.MS_SLAVE + + // RSLAVE marks the entire mount tree as SLAVE. + RSLAVE = unix.MS_SLAVE | unix.MS_REC + + // SHARED creates a mount which provides the ability to create mirrors of + // that mount such that mounts and unmounts within any of the mirrors + // propagate to the other mirrors. + SHARED = unix.MS_SHARED + + // RSHARED marks the entire mount tree as SHARED. + RSHARED = unix.MS_SHARED | unix.MS_REC + + // RELATIME updates inode access times relative to modify or change time. + RELATIME = unix.MS_RELATIME + + // STRICTATIME allows to explicitly request full atime updates. This makes + // it possible for the kernel to default to relatime or noatime but still + // allow userspace to override it. + STRICTATIME = unix.MS_STRICTATIME + + mntDetach = unix.MNT_DETACH +) diff --git a/vendor/github.com/moby/sys/mount/flags_unix.go b/vendor/github.com/moby/sys/mount/flags_unix.go new file mode 100644 index 0000000000..995d72807c --- /dev/null +++ b/vendor/github.com/moby/sys/mount/flags_unix.go @@ -0,0 +1,139 @@ +// +build !darwin,!windows + +package mount + +import ( + "fmt" + "strings" +) + +var flags = map[string]struct { + clear bool + flag int +}{ + "defaults": {false, 0}, + "ro": {false, RDONLY}, + "rw": {true, RDONLY}, + "suid": {true, NOSUID}, + "nosuid": {false, NOSUID}, + "dev": {true, NODEV}, + "nodev": {false, NODEV}, + "exec": {true, NOEXEC}, + "noexec": {false, NOEXEC}, + "sync": {false, SYNCHRONOUS}, + "async": {true, SYNCHRONOUS}, + "dirsync": {false, DIRSYNC}, + "remount": {false, REMOUNT}, + "mand": {false, MANDLOCK}, + "nomand": {true, MANDLOCK}, + "atime": {true, NOATIME}, + "noatime": {false, NOATIME}, + "diratime": {true, NODIRATIME}, + "nodiratime": {false, NODIRATIME}, + "bind": {false, BIND}, + "rbind": {false, RBIND}, + "unbindable": {false, UNBINDABLE}, + "runbindable": {false, RUNBINDABLE}, + "private": {false, PRIVATE}, + "rprivate": {false, RPRIVATE}, + "shared": {false, SHARED}, + "rshared": {false, RSHARED}, + "slave": {false, SLAVE}, + "rslave": {false, RSLAVE}, + "relatime": {false, RELATIME}, + "norelatime": {true, RELATIME}, + "strictatime": {false, STRICTATIME}, + "nostrictatime": {true, STRICTATIME}, +} + +var validFlags = map[string]bool{ + "": true, + "size": true, + "mode": true, + "uid": true, + "gid": true, + "nr_inodes": true, + "nr_blocks": true, + "mpol": true, +} + +var propagationFlags = map[string]bool{ + "bind": true, + "rbind": true, + "unbindable": true, + "runbindable": true, + "private": true, + "rprivate": true, + "shared": true, + "rshared": true, + "slave": true, + "rslave": true, +} + +// MergeTmpfsOptions merge mount options to make sure there is no duplicate. +func MergeTmpfsOptions(options []string) ([]string, error) { + // We use collisions maps to remove duplicates. + // For flag, the key is the flag value (the key for propagation flag is -1) + // For data=value, the key is the data + flagCollisions := map[int]bool{} + dataCollisions := map[string]bool{} + + var newOptions []string + // We process in reverse order + for i := len(options) - 1; i >= 0; i-- { + option := options[i] + if option == "defaults" { + continue + } + if f, ok := flags[option]; ok && f.flag != 0 { + // There is only one propagation mode + key := f.flag + if propagationFlags[option] { + key = -1 + } + // Check to see if there is collision for flag + if !flagCollisions[key] { + // We prepend the option and add to collision map + newOptions = append([]string{option}, newOptions...) + flagCollisions[key] = true + } + continue + } + opt := strings.SplitN(option, "=", 2) + if len(opt) != 2 || !validFlags[opt[0]] { + return nil, fmt.Errorf("Invalid tmpfs option %q", opt) + } + if !dataCollisions[opt[0]] { + // We prepend the option and add to collision map + newOptions = append([]string{option}, newOptions...) + dataCollisions[opt[0]] = true + } + } + + return newOptions, nil +} + +// Parse fstab type mount options into mount() flags +// and device specific data +func parseOptions(options string) (int, string) { + var ( + flag int + data []string + ) + + for _, o := range strings.Split(options, ",") { + // If the option does not exist in the flags table or the flag + // is not supported on the platform, + // then it is a data value for a specific fs type + if f, exists := flags[o]; exists && f.flag != 0 { + if f.clear { + flag &= ^f.flag + } else { + flag |= f.flag + } + } else { + data = append(data, o) + } + } + return flag, strings.Join(data, ",") +} diff --git a/vendor/github.com/moby/sys/mount/go.mod b/vendor/github.com/moby/sys/mount/go.mod new file mode 100644 index 0000000000..e1e03a3769 --- /dev/null +++ b/vendor/github.com/moby/sys/mount/go.mod @@ -0,0 +1,8 @@ +module github.com/moby/sys/mount + +go 1.14 + +require ( + github.com/moby/sys/mountinfo v0.4.0 + golang.org/x/sys v0.0.0-20200922070232-aee5d888a860 +) diff --git a/vendor/github.com/moby/sys/mount/go.sum b/vendor/github.com/moby/sys/mount/go.sum new file mode 100644 index 0000000000..7c39d597b6 --- /dev/null +++ b/vendor/github.com/moby/sys/mount/go.sum @@ -0,0 +1,5 @@ +github.com/moby/sys/mountinfo v0.4.0 h1:1KInV3Huv18akCu58V7lzNlt+jFmqlu1EaErnEHE/VM= +github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200922070232-aee5d888a860 h1:YEu4SMq7D0cmT7CBbXfcH0NZeuChAXwsHe/9XueUO6o= +golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/moby/sys/mount/mount_errors.go b/vendor/github.com/moby/sys/mount/mount_errors.go new file mode 100644 index 0000000000..936a26373b --- /dev/null +++ b/vendor/github.com/moby/sys/mount/mount_errors.go @@ -0,0 +1,46 @@ +// +build !windows + +package mount + +import "strconv" + +// mountError records an error from mount or unmount operation +type mountError struct { + op string + source, target string + flags uintptr + data string + err error +} + +func (e *mountError) Error() string { + out := e.op + " " + + if e.source != "" { + out += e.source + ":" + e.target + } else { + out += e.target + } + + if e.flags != uintptr(0) { + out += ", flags: 0x" + strconv.FormatUint(uint64(e.flags), 16) + } + if e.data != "" { + out += ", data: " + e.data + } + + out += ": " + e.err.Error() + return out +} + +// Cause returns the underlying cause of the error. +// This is a convention used in github.com/pkg/errors +func (e *mountError) Cause() error { + return e.err +} + +// Unwrap returns the underlying error. +// This is a convention used in golang 1.13+ +func (e *mountError) Unwrap() error { + return e.err +} diff --git a/vendor/github.com/moby/sys/mount/mount_unix.go b/vendor/github.com/moby/sys/mount/mount_unix.go new file mode 100644 index 0000000000..a250bfc80a --- /dev/null +++ b/vendor/github.com/moby/sys/mount/mount_unix.go @@ -0,0 +1,87 @@ +// +build !darwin,!windows + +package mount + +import ( + "fmt" + "sort" + + "github.com/moby/sys/mountinfo" + "golang.org/x/sys/unix" +) + +// Mount will mount filesystem according to the specified configuration. +// Options must be specified like the mount or fstab unix commands: +// "opt1=val1,opt2=val2". See flags.go for supported option flags. +func Mount(device, target, mType, options string) error { + flag, data := parseOptions(options) + return mount(device, target, mType, uintptr(flag), data) +} + +// Unmount lazily unmounts a filesystem on supported platforms, otherwise does +// a normal unmount. If target is not a mount point, no error is returned. +func Unmount(target string) error { + err := unix.Unmount(target, mntDetach) + if err == nil || err == unix.EINVAL { + // Ignore "not mounted" error here. Note the same error + // can be returned if flags are invalid, so this code + // assumes that the flags value is always correct. + return nil + } + + return &mountError{ + op: "umount", + target: target, + flags: uintptr(mntDetach), + err: err, + } +} + +// RecursiveUnmount unmounts the target and all mounts underneath, starting +// with the deepest mount first. The argument does not have to be a mount +// point itself. +func RecursiveUnmount(target string) error { + // Fast path, works if target is a mount point that can be unmounted. + // On Linux, mntDetach flag ensures a recursive unmount. For other + // platforms, if there are submounts, we'll get EBUSY (and fall back + // to the slow path). NOTE we do not ignore EINVAL here as target might + // not be a mount point itself (but there can be mounts underneath). + if err := unix.Unmount(target, mntDetach); err == nil { + return nil + } + + // Slow path: get all submounts, sort, unmount one by one. + mounts, err := mountinfo.GetMounts(mountinfo.PrefixFilter(target)) + if err != nil { + return err + } + + // Make the deepest mount be first + sort.Slice(mounts, func(i, j int) bool { + return len(mounts[i].Mountpoint) > len(mounts[j].Mountpoint) + }) + + var ( + suberr error + lastMount = len(mounts) - 1 + ) + for i, m := range mounts { + err = Unmount(m.Mountpoint) + if err != nil { + if i == lastMount { + if suberr != nil { + return fmt.Errorf("%w (possible cause: %s)", err, suberr) + } + return err + } + // This is a submount, we can ignore the error for now, + // the final unmount will fail if this is a real problem. + // With that in mind, the _first_ failed unmount error + // might be the real error cause, so let's keep it. + if suberr == nil { + suberr = err + } + } + } + return nil +} diff --git a/vendor/github.com/moby/sys/mount/mounter_bsd.go b/vendor/github.com/moby/sys/mount/mounter_bsd.go new file mode 100644 index 0000000000..656b762fe7 --- /dev/null +++ b/vendor/github.com/moby/sys/mount/mounter_bsd.go @@ -0,0 +1,61 @@ +// +build freebsd,cgo openbsd,cgo + +package mount + +/* +#include +#include +#include +#include +#include +#include +*/ +import "C" + +import ( + "strings" + "syscall" + "unsafe" +) + +func allocateIOVecs(options []string) []C.struct_iovec { + out := make([]C.struct_iovec, len(options)) + for i, option := range options { + out[i].iov_base = unsafe.Pointer(C.CString(option)) + out[i].iov_len = C.size_t(len(option) + 1) + } + return out +} + +func mount(device, target, mType string, flag uintptr, data string) error { + isNullFS := false + + xs := strings.Split(data, ",") + for _, x := range xs { + if x == "bind" { + isNullFS = true + } + } + + options := []string{"fspath", target} + if isNullFS { + options = append(options, "fstype", "nullfs", "target", device) + } else { + options = append(options, "fstype", mType, "from", device) + } + rawOptions := allocateIOVecs(options) + for _, rawOption := range rawOptions { + defer C.free(rawOption.iov_base) + } + + if errno := C.nmount(&rawOptions[0], C.uint(len(options)), C.int(flag)); errno != 0 { + return &mountError{ + op: "mount", + source: device, + target: target, + flags: flag, + err: syscall.Errno(errno), + } + } + return nil +} diff --git a/vendor/github.com/moby/sys/mount/mounter_linux.go b/vendor/github.com/moby/sys/mount/mounter_linux.go new file mode 100644 index 0000000000..0c477cc3d7 --- /dev/null +++ b/vendor/github.com/moby/sys/mount/mounter_linux.go @@ -0,0 +1,73 @@ +package mount + +import ( + "golang.org/x/sys/unix" +) + +const ( + // ptypes is the set propagation types. + ptypes = unix.MS_SHARED | unix.MS_PRIVATE | unix.MS_SLAVE | unix.MS_UNBINDABLE + + // pflags is the full set valid flags for a change propagation call. + pflags = ptypes | unix.MS_REC | unix.MS_SILENT + + // broflags is the combination of bind and read only + broflags = unix.MS_BIND | unix.MS_RDONLY +) + +// isremount returns true if either device name or flags identify a remount request, false otherwise. +func isremount(device string, flags uintptr) bool { + switch { + // We treat device "" and "none" as a remount request to provide compatibility with + // requests that don't explicitly set MS_REMOUNT such as those manipulating bind mounts. + case flags&unix.MS_REMOUNT != 0, device == "", device == "none": + return true + default: + return false + } +} + +func mount(device, target, mType string, flags uintptr, data string) error { + oflags := flags &^ ptypes + if !isremount(device, flags) || data != "" { + // Initial call applying all non-propagation flags for mount + // or remount with changed data + if err := unix.Mount(device, target, mType, oflags, data); err != nil { + return &mountError{ + op: "mount", + source: device, + target: target, + flags: oflags, + data: data, + err: err, + } + } + } + + if flags&ptypes != 0 { + // Change the propagation type. + if err := unix.Mount("", target, "", flags&pflags, ""); err != nil { + return &mountError{ + op: "remount", + target: target, + flags: flags & pflags, + err: err, + } + } + } + + if oflags&broflags == broflags { + // Remount the bind to apply read only. + if err := unix.Mount("", target, "", oflags|unix.MS_REMOUNT, ""); err != nil { + return &mountError{ + op: "remount-ro", + target: target, + flags: oflags | unix.MS_REMOUNT, + err: err, + } + + } + } + + return nil +} diff --git a/vendor/github.com/moby/sys/mount/mounter_unsupported.go b/vendor/github.com/moby/sys/mount/mounter_unsupported.go new file mode 100644 index 0000000000..e7ff5bd9ff --- /dev/null +++ b/vendor/github.com/moby/sys/mount/mounter_unsupported.go @@ -0,0 +1,7 @@ +// +build !linux,!freebsd,!openbsd,!windows freebsd,!cgo openbsd,!cgo + +package mount + +func mount(device, target, mType string, flag uintptr, data string) error { + panic("cgo required on freebsd and openbsd") +} diff --git a/vendor/github.com/moby/sys/mount/sharedsubtree_linux.go b/vendor/github.com/moby/sys/mount/sharedsubtree_linux.go new file mode 100644 index 0000000000..948e6bacd8 --- /dev/null +++ b/vendor/github.com/moby/sys/mount/sharedsubtree_linux.go @@ -0,0 +1,73 @@ +package mount + +import "github.com/moby/sys/mountinfo" + +// MakeShared ensures a mounted filesystem has the SHARED mount option enabled. +// See the supported options in flags.go for further reference. +func MakeShared(mountPoint string) error { + return ensureMountedAs(mountPoint, SHARED) +} + +// MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled. +// See the supported options in flags.go for further reference. +func MakeRShared(mountPoint string) error { + return ensureMountedAs(mountPoint, RSHARED) +} + +// MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled. +// See the supported options in flags.go for further reference. +func MakePrivate(mountPoint string) error { + return ensureMountedAs(mountPoint, PRIVATE) +} + +// MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option +// enabled. See the supported options in flags.go for further reference. +func MakeRPrivate(mountPoint string) error { + return ensureMountedAs(mountPoint, RPRIVATE) +} + +// MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled. +// See the supported options in flags.go for further reference. +func MakeSlave(mountPoint string) error { + return ensureMountedAs(mountPoint, SLAVE) +} + +// MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled. +// See the supported options in flags.go for further reference. +func MakeRSlave(mountPoint string) error { + return ensureMountedAs(mountPoint, RSLAVE) +} + +// MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option +// enabled. See the supported options in flags.go for further reference. +func MakeUnbindable(mountPoint string) error { + return ensureMountedAs(mountPoint, UNBINDABLE) +} + +// MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount +// option enabled. See the supported options in flags.go for further reference. +func MakeRUnbindable(mountPoint string) error { + return ensureMountedAs(mountPoint, RUNBINDABLE) +} + +// MakeMount ensures that the file or directory given is a mount point, +// bind mounting it to itself it case it is not. +func MakeMount(mnt string) error { + mounted, err := mountinfo.Mounted(mnt) + if err != nil { + return err + } + if mounted { + return nil + } + + return mount(mnt, mnt, "none", uintptr(BIND), "") +} + +func ensureMountedAs(mnt string, flags int) error { + if err := MakeMount(mnt); err != nil { + return err + } + + return mount("", mnt, "none", uintptr(flags), "") +} diff --git a/vendor/github.com/moby/sys/mountinfo/LICENSE b/vendor/github.com/moby/sys/mountinfo/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/github.com/moby/sys/mountinfo/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/moby/sys/mountinfo/doc.go b/vendor/github.com/moby/sys/mountinfo/doc.go new file mode 100644 index 0000000000..b80e05efd0 --- /dev/null +++ b/vendor/github.com/moby/sys/mountinfo/doc.go @@ -0,0 +1,44 @@ +// Package mountinfo provides a set of functions to retrieve information about OS mounts. +// +// Currently it supports Linux. For historical reasons, there is also some support for FreeBSD and OpenBSD, +// and a shallow implementation for Windows, but in general this is Linux-only package, so +// the rest of the document only applies to Linux, unless explicitly specified otherwise. +// +// In Linux, information about mounts seen by the current process is available from +// /proc/self/mountinfo. Note that due to mount namespaces, different processes can +// see different mounts. A per-process mountinfo table is available from /proc//mountinfo, +// where is a numerical process identifier. +// +// In general, /proc is not a very efficient interface, and mountinfo is not an exception. +// For example, there is no way to get information about a specific mount point (i.e. it +// is all-or-nothing). This package tries to hide the /proc ineffectiveness by using +// parse filters while reading mountinfo. A filter can skip some entries, or stop +// processing the rest of the file once the needed information is found. +// +// For mountinfo filters that accept path as an argument, the path must be absolute, +// having all symlinks resolved, and being cleaned (i.e. no extra slashes or dots). +// One way to achieve all of the above is to employ filepath.Abs followed by +// filepath.EvalSymlinks (the latter calls filepath.Clean on the result so +// there is no need to explicitly call filepath.Clean). +// +// NOTE that in many cases there is no need to consult mountinfo at all. Here are some +// of the cases where mountinfo should not be parsed: +// +// 1. Before performing a mount. Usually, this is not needed, but if required (say to +// prevent over-mounts), to check whether a directory is mounted, call os.Lstat +// on it and its parent directory, and compare their st.Sys().(*syscall.Stat_t).Dev +// fields -- if they differ, then the directory is the mount point. NOTE this does +// not work for bind mounts. Optionally, the filesystem type can also be checked +// by calling unix.Statfs and checking the Type field (i.e. filesystem type). +// +// 2. After performing a mount. If there is no error returned, the mount succeeded; +// checking the mount table for a new mount is redundant and expensive. +// +// 3. Before performing an unmount. It is more efficient to do an unmount and ignore +// a specific error (EINVAL) which tells the directory is not mounted. +// +// 4. After performing an unmount. If there is no error returned, the unmount succeeded. +// +// 5. To find the mount point root of a specific directory. You can perform os.Stat() +// on the directory and traverse up until the Dev field of a parent directory differs. +package mountinfo diff --git a/vendor/github.com/moby/sys/mountinfo/go.mod b/vendor/github.com/moby/sys/mountinfo/go.mod new file mode 100644 index 0000000000..1cc3efcf74 --- /dev/null +++ b/vendor/github.com/moby/sys/mountinfo/go.mod @@ -0,0 +1,5 @@ +module github.com/moby/sys/mountinfo + +go 1.16 + +require golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 diff --git a/vendor/github.com/moby/sys/mountinfo/go.sum b/vendor/github.com/moby/sys/mountinfo/go.sum new file mode 100644 index 0000000000..c257a6a29d --- /dev/null +++ b/vendor/github.com/moby/sys/mountinfo/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/vendor/github.com/moby/sys/mountinfo/mounted_linux.go b/vendor/github.com/moby/sys/mountinfo/mounted_linux.go new file mode 100644 index 0000000000..5c9e3e30e6 --- /dev/null +++ b/vendor/github.com/moby/sys/mountinfo/mounted_linux.go @@ -0,0 +1,57 @@ +package mountinfo + +import ( + "os" + "path/filepath" + + "golang.org/x/sys/unix" +) + +// mountedByOpenat2 is a method of detecting a mount that works for all kinds +// of mounts (incl. bind mounts), but requires a recent (v5.6+) linux kernel. +func mountedByOpenat2(path string) (bool, error) { + dir, last := filepath.Split(path) + + dirfd, err := unix.Openat2(unix.AT_FDCWD, dir, &unix.OpenHow{ + Flags: unix.O_PATH | unix.O_CLOEXEC, + }) + if err != nil { + return false, &os.PathError{Op: "openat2", Path: dir, Err: err} + } + fd, err := unix.Openat2(dirfd, last, &unix.OpenHow{ + Flags: unix.O_PATH | unix.O_CLOEXEC | unix.O_NOFOLLOW, + Resolve: unix.RESOLVE_NO_XDEV, + }) + _ = unix.Close(dirfd) + switch err { //nolint:errorlint // unix errors are bare + case nil: // definitely not a mount + _ = unix.Close(fd) + return false, nil + case unix.EXDEV: // definitely a mount + return true, nil + } + // not sure + return false, &os.PathError{Op: "openat2", Path: path, Err: err} +} + +func mounted(path string) (bool, error) { + path, err := normalizePath(path) + if err != nil { + return false, err + } + // Try a fast path, using openat2() with RESOLVE_NO_XDEV. + mounted, err := mountedByOpenat2(path) + if err == nil { + return mounted, nil + } + // Another fast path: compare st.st_dev fields. + mounted, err = mountedByStat(path) + // This does not work for bind mounts, so false negative + // is possible, therefore only trust if return is true. + if mounted && err == nil { + return mounted, nil + } + + // Fallback to parsing mountinfo + return mountedByMountinfo(path) +} diff --git a/vendor/github.com/moby/sys/mountinfo/mounted_unix.go b/vendor/github.com/moby/sys/mountinfo/mounted_unix.go new file mode 100644 index 0000000000..45ddad236f --- /dev/null +++ b/vendor/github.com/moby/sys/mountinfo/mounted_unix.go @@ -0,0 +1,54 @@ +//go:build linux || (freebsd && cgo) || (openbsd && cgo) || (darwin && cgo) +// +build linux freebsd,cgo openbsd,cgo darwin,cgo + +package mountinfo + +import ( + "fmt" + "os" + "path/filepath" + + "golang.org/x/sys/unix" +) + +func mountedByStat(path string) (bool, error) { + var st unix.Stat_t + + if err := unix.Lstat(path, &st); err != nil { + return false, &os.PathError{Op: "stat", Path: path, Err: err} + } + dev := st.Dev + parent := filepath.Dir(path) + if err := unix.Lstat(parent, &st); err != nil { + return false, &os.PathError{Op: "stat", Path: parent, Err: err} + } + if dev != st.Dev { + // Device differs from that of parent, + // so definitely a mount point. + return true, nil + } + // NB: this does not detect bind mounts on Linux. + return false, nil +} + +func normalizePath(path string) (realPath string, err error) { + if realPath, err = filepath.Abs(path); err != nil { + return "", fmt.Errorf("unable to get absolute path for %q: %w", path, err) + } + if realPath, err = filepath.EvalSymlinks(realPath); err != nil { + return "", fmt.Errorf("failed to canonicalise path for %q: %w", path, err) + } + if _, err := os.Stat(realPath); err != nil { + return "", fmt.Errorf("failed to stat target of %q: %w", path, err) + } + return realPath, nil +} + +func mountedByMountinfo(path string) (bool, error) { + entries, err := GetMounts(SingleEntryFilter(path)) + if err != nil { + return false, err + } + + return len(entries) > 0, nil +} diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo.go b/vendor/github.com/moby/sys/mountinfo/mountinfo.go new file mode 100644 index 0000000000..9867a66dd8 --- /dev/null +++ b/vendor/github.com/moby/sys/mountinfo/mountinfo.go @@ -0,0 +1,67 @@ +package mountinfo + +import ( + "os" +) + +// GetMounts retrieves a list of mounts for the current running process, +// with an optional filter applied (use nil for no filter). +func GetMounts(f FilterFunc) ([]*Info, error) { + return parseMountTable(f) +} + +// Mounted determines if a specified path is a mount point. In case of any +// error, false (and an error) is returned. +// +// The non-existent path returns an error. If a caller is not interested +// in this particular error, it should handle it separately using e.g. +// errors.Is(err, os.ErrNotExist). +func Mounted(path string) (bool, error) { + // root is always mounted + if path == string(os.PathSeparator) { + return true, nil + } + return mounted(path) +} + +// Info reveals information about a particular mounted filesystem. This +// struct is populated from the content in the /proc//mountinfo file. +type Info struct { + // ID is a unique identifier of the mount (may be reused after umount). + ID int + + // Parent is the ID of the parent mount (or of self for the root + // of this mount namespace's mount tree). + Parent int + + // Major and Minor are the major and the minor components of the Dev + // field of unix.Stat_t structure returned by unix.*Stat calls for + // files on this filesystem. + Major, Minor int + + // Root is the pathname of the directory in the filesystem which forms + // the root of this mount. + Root string + + // Mountpoint is the pathname of the mount point relative to the + // process's root directory. + Mountpoint string + + // Options is a comma-separated list of mount options. + Options string + + // Optional are zero or more fields of the form "tag[:value]", + // separated by a space. Currently, the possible optional fields are + // "shared", "master", "propagate_from", and "unbindable". For more + // information, see mount_namespaces(7) Linux man page. + Optional string + + // FSType is the filesystem type in the form "type[.subtype]". + FSType string + + // Source is filesystem-specific information, or "none". + Source string + + // VFSOptions is a comma-separated list of superblock options. + VFSOptions string +} diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_bsd.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_bsd.go new file mode 100644 index 0000000000..d5513a26d2 --- /dev/null +++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_bsd.go @@ -0,0 +1,72 @@ +//go:build (freebsd && cgo) || (openbsd && cgo) || (darwin && cgo) +// +build freebsd,cgo openbsd,cgo darwin,cgo + +package mountinfo + +/* +#include +#include +#include +*/ +import "C" + +import ( + "fmt" + "reflect" + "unsafe" +) + +// parseMountTable returns information about mounted filesystems +func parseMountTable(filter FilterFunc) ([]*Info, error) { + var rawEntries *C.struct_statfs + + count := int(C.getmntinfo(&rawEntries, C.MNT_WAIT)) + if count == 0 { + return nil, fmt.Errorf("failed to call getmntinfo") + } + + var entries []C.struct_statfs + header := (*reflect.SliceHeader)(unsafe.Pointer(&entries)) + header.Cap = count + header.Len = count + header.Data = uintptr(unsafe.Pointer(rawEntries)) + + var out []*Info + for _, entry := range entries { + var mountinfo Info + var skip, stop bool + mountinfo.Mountpoint = C.GoString(&entry.f_mntonname[0]) + mountinfo.FSType = C.GoString(&entry.f_fstypename[0]) + mountinfo.Source = C.GoString(&entry.f_mntfromname[0]) + + if filter != nil { + // filter out entries we're not interested in + skip, stop = filter(&mountinfo) + if skip { + continue + } + } + + out = append(out, &mountinfo) + if stop { + break + } + } + return out, nil +} + +func mounted(path string) (bool, error) { + path, err := normalizePath(path) + if err != nil { + return false, err + } + // Fast path: compare st.st_dev fields. + // This should always work for FreeBSD and OpenBSD. + mounted, err := mountedByStat(path) + if err == nil { + return mounted, nil + } + + // Fallback to parsing mountinfo + return mountedByMountinfo(path) +} diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_filters.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_filters.go new file mode 100644 index 0000000000..16079c3c54 --- /dev/null +++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_filters.go @@ -0,0 +1,63 @@ +package mountinfo + +import "strings" + +// FilterFunc is a type defining a callback function for GetMount(), +// used to filter out mountinfo entries we're not interested in, +// and/or stop further processing if we found what we wanted. +// +// It takes a pointer to the Info struct (fully populated with all available +// fields on the GOOS platform), and returns two booleans: +// +// skip: true if the entry should be skipped; +// +// stop: true if parsing should be stopped after the entry. +type FilterFunc func(*Info) (skip, stop bool) + +// PrefixFilter discards all entries whose mount points do not start with, or +// are equal to the path specified in prefix. The prefix path must be absolute, +// have all symlinks resolved, and cleaned (i.e. no extra slashes or dots). +// +// PrefixFilter treats prefix as a path, not a partial prefix, which means that +// given "/foo", "/foo/bar" and "/foobar" entries, PrefixFilter("/foo") returns +// "/foo" and "/foo/bar", and discards "/foobar". +func PrefixFilter(prefix string) FilterFunc { + return func(m *Info) (bool, bool) { + skip := !strings.HasPrefix(m.Mountpoint+"/", prefix+"/") + return skip, false + } +} + +// SingleEntryFilter looks for a specific entry. +func SingleEntryFilter(mp string) FilterFunc { + return func(m *Info) (bool, bool) { + if m.Mountpoint == mp { + return false, true // don't skip, stop now + } + return true, false // skip, keep going + } +} + +// ParentsFilter returns all entries whose mount points +// can be parents of a path specified, discarding others. +// +// For example, given /var/lib/docker/something, entries +// like /var/lib/docker, /var and / are returned. +func ParentsFilter(path string) FilterFunc { + return func(m *Info) (bool, bool) { + skip := !strings.HasPrefix(path, m.Mountpoint) + return skip, false + } +} + +// FSTypeFilter returns all entries that match provided fstype(s). +func FSTypeFilter(fstype ...string) FilterFunc { + return func(m *Info) (bool, bool) { + for _, t := range fstype { + if m.FSType == t { + return false, false // don't skip, keep going + } + } + return true, false // skip, keep going + } +} diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go new file mode 100644 index 0000000000..59332b07bf --- /dev/null +++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go @@ -0,0 +1,214 @@ +package mountinfo + +import ( + "bufio" + "fmt" + "io" + "os" + "strconv" + "strings" +) + +// GetMountsFromReader retrieves a list of mounts from the +// reader provided, with an optional filter applied (use nil +// for no filter). This can be useful in tests or benchmarks +// that provide fake mountinfo data, or when a source other +// than /proc/self/mountinfo needs to be read from. +// +// This function is Linux-specific. +func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) { + s := bufio.NewScanner(r) + out := []*Info{} + for s.Scan() { + var err error + + /* + See http://man7.org/linux/man-pages/man5/proc.5.html + + 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue + (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11) + + (1) mount ID: unique identifier of the mount (may be reused after umount) + (2) parent ID: ID of parent (or of self for the top of the mount tree) + (3) major:minor: value of st_dev for files on filesystem + (4) root: root of the mount within the filesystem + (5) mount point: mount point relative to the process's root + (6) mount options: per mount options + (7) optional fields: zero or more fields of the form "tag[:value]" + (8) separator: marks the end of the optional fields + (9) filesystem type: name of filesystem of the form "type[.subtype]" + (10) mount source: filesystem specific information or "none" + (11) super options: per super block options + + In other words, we have: + * 6 mandatory fields (1)..(6) + * 0 or more optional fields (7) + * a separator field (8) + * 3 mandatory fields (9)..(11) + */ + + text := s.Text() + fields := strings.Split(text, " ") + numFields := len(fields) + if numFields < 10 { + // should be at least 10 fields + return nil, fmt.Errorf("parsing '%s' failed: not enough fields (%d)", text, numFields) + } + + // separator field + sepIdx := numFields - 4 + // In Linux <= 3.9 mounting a cifs with spaces in a share + // name (like "//srv/My Docs") _may_ end up having a space + // in the last field of mountinfo (like "unc=//serv/My Docs"). + // Since kernel 3.10-rc1, cifs option "unc=" is ignored, + // so spaces should not appear. + // + // Check for a separator, and work around the spaces bug + for fields[sepIdx] != "-" { + sepIdx-- + if sepIdx == 5 { + return nil, fmt.Errorf("parsing '%s' failed: missing - separator", text) + } + } + + p := &Info{} + + p.Mountpoint, err = unescape(fields[4]) + if err != nil { + return nil, fmt.Errorf("parsing '%s' failed: mount point: %w", fields[4], err) + } + p.FSType, err = unescape(fields[sepIdx+1]) + if err != nil { + return nil, fmt.Errorf("parsing '%s' failed: fstype: %w", fields[sepIdx+1], err) + } + p.Source, err = unescape(fields[sepIdx+2]) + if err != nil { + return nil, fmt.Errorf("parsing '%s' failed: source: %w", fields[sepIdx+2], err) + } + p.VFSOptions = fields[sepIdx+3] + + // ignore any numbers parsing errors, as there should not be any + p.ID, _ = strconv.Atoi(fields[0]) + p.Parent, _ = strconv.Atoi(fields[1]) + mm := strings.SplitN(fields[2], ":", 3) + if len(mm) != 2 { + return nil, fmt.Errorf("parsing '%s' failed: unexpected major:minor pair %s", text, mm) + } + p.Major, _ = strconv.Atoi(mm[0]) + p.Minor, _ = strconv.Atoi(mm[1]) + + p.Root, err = unescape(fields[3]) + if err != nil { + return nil, fmt.Errorf("parsing '%s' failed: root: %w", fields[3], err) + } + + p.Options = fields[5] + + // zero or more optional fields + p.Optional = strings.Join(fields[6:sepIdx], " ") + + // Run the filter after parsing all fields. + var skip, stop bool + if filter != nil { + skip, stop = filter(p) + if skip { + continue + } + } + + out = append(out, p) + if stop { + break + } + } + if err := s.Err(); err != nil { + return nil, err + } + return out, nil +} + +func parseMountTable(filter FilterFunc) ([]*Info, error) { + f, err := os.Open("/proc/self/mountinfo") + if err != nil { + return nil, err + } + defer f.Close() + + return GetMountsFromReader(f, filter) +} + +// PidMountInfo retrieves the list of mounts from a given process' mount +// namespace. Unless there is a need to get mounts from a mount namespace +// different from that of a calling process, use GetMounts. +// +// This function is Linux-specific. +// +// Deprecated: this will be removed before v1; use GetMountsFromReader with +// opened /proc//mountinfo as an argument instead. +func PidMountInfo(pid int) ([]*Info, error) { + f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid)) + if err != nil { + return nil, err + } + defer f.Close() + + return GetMountsFromReader(f, nil) +} + +// A few specific characters in mountinfo path entries (root and mountpoint) +// are escaped using a backslash followed by a character's ascii code in octal. +// +// space -- as \040 +// tab (aka \t) -- as \011 +// newline (aka \n) -- as \012 +// backslash (aka \\) -- as \134 +// +// This function converts path from mountinfo back, i.e. it unescapes the above sequences. +func unescape(path string) (string, error) { + // try to avoid copying + if strings.IndexByte(path, '\\') == -1 { + return path, nil + } + + // The following code is UTF-8 transparent as it only looks for some + // specific characters (backslash and 0..7) with values < utf8.RuneSelf, + // and everything else is passed through as is. + buf := make([]byte, len(path)) + bufLen := 0 + for i := 0; i < len(path); i++ { + if path[i] != '\\' { + buf[bufLen] = path[i] + bufLen++ + continue + } + s := path[i:] + if len(s) < 4 { + // too short + return "", fmt.Errorf("bad escape sequence %q: too short", s) + } + c := s[1] + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7': + v := c - '0' + for j := 2; j < 4; j++ { // one digit already; two more + if s[j] < '0' || s[j] > '7' { + return "", fmt.Errorf("bad escape sequence %q: not a digit", s[:3]) + } + x := s[j] - '0' + v = (v << 3) | x + } + if v > 255 { + return "", fmt.Errorf("bad escape sequence %q: out of range" + s[:3]) + } + buf[bufLen] = v + bufLen++ + i += 3 + continue + default: + return "", fmt.Errorf("bad escape sequence %q: not a digit" + s[:3]) + + } + } + + return string(buf[:bufLen]), nil +} diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go new file mode 100644 index 0000000000..95769a76da --- /dev/null +++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go @@ -0,0 +1,19 @@ +//go:build (!windows && !linux && !freebsd && !openbsd && !darwin) || (freebsd && !cgo) || (openbsd && !cgo) || (darwin && !cgo) +// +build !windows,!linux,!freebsd,!openbsd,!darwin freebsd,!cgo openbsd,!cgo darwin,!cgo + +package mountinfo + +import ( + "fmt" + "runtime" +) + +var errNotImplemented = fmt.Errorf("not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) + +func parseMountTable(_ FilterFunc) ([]*Info, error) { + return nil, errNotImplemented +} + +func mounted(path string) (bool, error) { + return false, errNotImplemented +} diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_windows.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_windows.go new file mode 100644 index 0000000000..13fad165e5 --- /dev/null +++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_windows.go @@ -0,0 +1,10 @@ +package mountinfo + +func parseMountTable(_ FilterFunc) ([]*Info, error) { + // Do NOT return an error! + return nil, nil +} + +func mounted(_ string) (bool, error) { + return false, nil +} diff --git a/vendor/github.com/moby/term/.gitignore b/vendor/github.com/moby/term/.gitignore new file mode 100644 index 0000000000..b0747ff010 --- /dev/null +++ b/vendor/github.com/moby/term/.gitignore @@ -0,0 +1,8 @@ +# if you want to ignore files created by your editor/tools, consider using a +# global .gitignore or .git/info/exclude see https://help.github.com/articles/ignoring-files +.* +!.github +!.gitignore +profile.out +# support running go modules in vendor mode for local development +vendor/ diff --git a/vendor/github.com/moby/term/LICENSE b/vendor/github.com/moby/term/LICENSE new file mode 100644 index 0000000000..6d8d58fb67 --- /dev/null +++ b/vendor/github.com/moby/term/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright 2013-2018 Docker, Inc. + + 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 + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/moby/term/README.md b/vendor/github.com/moby/term/README.md new file mode 100644 index 0000000000..0ce92cc339 --- /dev/null +++ b/vendor/github.com/moby/term/README.md @@ -0,0 +1,36 @@ +# term - utilities for dealing with terminals + +![Test](https://github.com/moby/term/workflows/Test/badge.svg) [![GoDoc](https://godoc.org/github.com/moby/term?status.svg)](https://godoc.org/github.com/moby/term) [![Go Report Card](https://goreportcard.com/badge/github.com/moby/term)](https://goreportcard.com/report/github.com/moby/term) + +term provides structures and helper functions to work with terminal (state, sizes). + +#### Using term + +```go +package main + +import ( + "log" + "os" + + "github.com/moby/term" +) + +func main() { + fd := os.Stdin.Fd() + if term.IsTerminal(fd) { + ws, err := term.GetWinsize(fd) + if err != nil { + log.Fatalf("term.GetWinsize: %s", err) + } + log.Printf("%d:%d\n", ws.Height, ws.Width) + } +} +``` + +## Contributing + +Want to hack on term? [Docker's contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md) apply. + +## Copyright and license +Code and documentation copyright 2015 Docker, inc. Code released under the Apache 2.0 license. Docs released under Creative commons. diff --git a/vendor/github.com/moby/term/ascii.go b/vendor/github.com/moby/term/ascii.go new file mode 100644 index 0000000000..55873c0556 --- /dev/null +++ b/vendor/github.com/moby/term/ascii.go @@ -0,0 +1,66 @@ +package term + +import ( + "fmt" + "strings" +) + +// ASCII list the possible supported ASCII key sequence +var ASCII = []string{ + "ctrl-@", + "ctrl-a", + "ctrl-b", + "ctrl-c", + "ctrl-d", + "ctrl-e", + "ctrl-f", + "ctrl-g", + "ctrl-h", + "ctrl-i", + "ctrl-j", + "ctrl-k", + "ctrl-l", + "ctrl-m", + "ctrl-n", + "ctrl-o", + "ctrl-p", + "ctrl-q", + "ctrl-r", + "ctrl-s", + "ctrl-t", + "ctrl-u", + "ctrl-v", + "ctrl-w", + "ctrl-x", + "ctrl-y", + "ctrl-z", + "ctrl-[", + "ctrl-\\", + "ctrl-]", + "ctrl-^", + "ctrl-_", +} + +// ToBytes converts a string representing a suite of key-sequence to the corresponding ASCII code. +func ToBytes(keys string) ([]byte, error) { + codes := []byte{} +next: + for _, key := range strings.Split(keys, ",") { + if len(key) != 1 { + for code, ctrl := range ASCII { + if ctrl == key { + codes = append(codes, byte(code)) + continue next + } + } + if key == "DEL" { + codes = append(codes, 127) + } else { + return nil, fmt.Errorf("Unknown character: '%s'", key) + } + } else { + codes = append(codes, key[0]) + } + } + return codes, nil +} diff --git a/vendor/github.com/moby/term/go.mod b/vendor/github.com/moby/term/go.mod new file mode 100644 index 0000000000..541f2d429f --- /dev/null +++ b/vendor/github.com/moby/term/go.mod @@ -0,0 +1,12 @@ +module github.com/moby/term + +go 1.13 + +require ( + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 + github.com/creack/pty v1.1.11 + github.com/google/go-cmp v0.4.0 + github.com/pkg/errors v0.9.1 // indirect + golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 + gotest.tools/v3 v3.0.2 +) diff --git a/vendor/github.com/moby/term/go.sum b/vendor/github.com/moby/term/go.sum new file mode 100644 index 0000000000..f270b174ca --- /dev/null +++ b/vendor/github.com/moby/term/go.sum @@ -0,0 +1,23 @@ +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gotest.tools/v3 v3.0.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= diff --git a/vendor/github.com/moby/term/proxy.go b/vendor/github.com/moby/term/proxy.go new file mode 100644 index 0000000000..c47756b89a --- /dev/null +++ b/vendor/github.com/moby/term/proxy.go @@ -0,0 +1,88 @@ +package term + +import ( + "io" +) + +// EscapeError is special error which returned by a TTY proxy reader's Read() +// method in case its detach escape sequence is read. +type EscapeError struct{} + +func (EscapeError) Error() string { + return "read escape sequence" +} + +// escapeProxy is used only for attaches with a TTY. It is used to proxy +// stdin keypresses from the underlying reader and look for the passed in +// escape key sequence to signal a detach. +type escapeProxy struct { + escapeKeys []byte + escapeKeyPos int + r io.Reader + buf []byte +} + +// NewEscapeProxy returns a new TTY proxy reader which wraps the given reader +// and detects when the specified escape keys are read, in which case the Read +// method will return an error of type EscapeError. +func NewEscapeProxy(r io.Reader, escapeKeys []byte) io.Reader { + return &escapeProxy{ + escapeKeys: escapeKeys, + r: r, + } +} + +func (r *escapeProxy) Read(buf []byte) (n int, err error) { + if len(r.escapeKeys) > 0 && r.escapeKeyPos == len(r.escapeKeys) { + return 0, EscapeError{} + } + + if len(r.buf) > 0 { + n = copy(buf, r.buf) + r.buf = r.buf[n:] + } + + nr, err := r.r.Read(buf[n:]) + n += nr + if len(r.escapeKeys) == 0 { + return n, err + } + + for i := 0; i < n; i++ { + if buf[i] == r.escapeKeys[r.escapeKeyPos] { + r.escapeKeyPos++ + + // Check if the full escape sequence is matched. + if r.escapeKeyPos == len(r.escapeKeys) { + n = i + 1 - r.escapeKeyPos + if n < 0 { + n = 0 + } + return n, EscapeError{} + } + continue + } + + // If we need to prepend a partial escape sequence from the previous + // read, make sure the new buffer size doesn't exceed len(buf). + // Otherwise, preserve any extra data in a buffer for the next read. + if i < r.escapeKeyPos { + preserve := make([]byte, 0, r.escapeKeyPos+n) + preserve = append(preserve, r.escapeKeys[:r.escapeKeyPos]...) + preserve = append(preserve, buf[:n]...) + n = copy(buf, preserve) + i += r.escapeKeyPos + r.buf = append(r.buf, preserve[n:]...) + } + r.escapeKeyPos = 0 + } + + // If we're in the middle of reading an escape sequence, make sure we don't + // let the caller read it. If later on we find that this is not the escape + // sequence, we'll prepend it back to buf. + n -= r.escapeKeyPos + if n < 0 { + n = 0 + } + return n, err +} diff --git a/vendor/github.com/moby/term/tc.go b/vendor/github.com/moby/term/tc.go new file mode 100644 index 0000000000..65556027a6 --- /dev/null +++ b/vendor/github.com/moby/term/tc.go @@ -0,0 +1,19 @@ +// +build !windows + +package term + +import ( + "golang.org/x/sys/unix" +) + +func tcget(fd uintptr) (*Termios, error) { + p, err := unix.IoctlGetTermios(int(fd), getTermios) + if err != nil { + return nil, err + } + return p, nil +} + +func tcset(fd uintptr, p *Termios) error { + return unix.IoctlSetTermios(int(fd), setTermios, p) +} diff --git a/vendor/github.com/moby/term/term.go b/vendor/github.com/moby/term/term.go new file mode 100644 index 0000000000..29c6acf1c7 --- /dev/null +++ b/vendor/github.com/moby/term/term.go @@ -0,0 +1,120 @@ +// +build !windows + +// Package term provides structures and helper functions to work with +// terminal (state, sizes). +package term + +import ( + "errors" + "fmt" + "io" + "os" + "os/signal" + + "golang.org/x/sys/unix" +) + +var ( + // ErrInvalidState is returned if the state of the terminal is invalid. + ErrInvalidState = errors.New("Invalid terminal state") +) + +// State represents the state of the terminal. +type State struct { + termios Termios +} + +// Winsize represents the size of the terminal window. +type Winsize struct { + Height uint16 + Width uint16 + x uint16 + y uint16 +} + +// StdStreams returns the standard streams (stdin, stdout, stderr). +func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { + return os.Stdin, os.Stdout, os.Stderr +} + +// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal. +func GetFdInfo(in interface{}) (uintptr, bool) { + var inFd uintptr + var isTerminalIn bool + if file, ok := in.(*os.File); ok { + inFd = file.Fd() + isTerminalIn = IsTerminal(inFd) + } + return inFd, isTerminalIn +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd uintptr) bool { + _, err := tcget(fd) + return err == nil +} + +// RestoreTerminal restores the terminal connected to the given file descriptor +// to a previous state. +func RestoreTerminal(fd uintptr, state *State) error { + if state == nil { + return ErrInvalidState + } + return tcset(fd, &state.termios) +} + +// SaveState saves the state of the terminal connected to the given file descriptor. +func SaveState(fd uintptr) (*State, error) { + termios, err := tcget(fd) + if err != nil { + return nil, err + } + return &State{termios: *termios}, nil +} + +// DisableEcho applies the specified state to the terminal connected to the file +// descriptor, with echo disabled. +func DisableEcho(fd uintptr, state *State) error { + newState := state.termios + newState.Lflag &^= unix.ECHO + + if err := tcset(fd, &newState); err != nil { + return err + } + handleInterrupt(fd, state) + return nil +} + +// SetRawTerminal puts the terminal connected to the given file descriptor into +// raw mode and returns the previous state. On UNIX, this puts both the input +// and output into raw mode. On Windows, it only puts the input into raw mode. +func SetRawTerminal(fd uintptr) (*State, error) { + oldState, err := MakeRaw(fd) + if err != nil { + return nil, err + } + handleInterrupt(fd, oldState) + return oldState, err +} + +// SetRawTerminalOutput puts the output of terminal connected to the given file +// descriptor into raw mode. On UNIX, this does nothing and returns nil for the +// state. On Windows, it disables LF -> CRLF translation. +func SetRawTerminalOutput(fd uintptr) (*State, error) { + return nil, nil +} + +func handleInterrupt(fd uintptr, state *State) { + sigchan := make(chan os.Signal, 1) + signal.Notify(sigchan, os.Interrupt) + go func() { + for range sigchan { + // quit cleanly and the new terminal item is on a new line + fmt.Println() + signal.Stop(sigchan) + close(sigchan) + RestoreTerminal(fd, state) + os.Exit(1) + } + }() +} diff --git a/vendor/github.com/moby/term/term_windows.go b/vendor/github.com/moby/term/term_windows.go new file mode 100644 index 0000000000..ba82960d4a --- /dev/null +++ b/vendor/github.com/moby/term/term_windows.go @@ -0,0 +1,231 @@ +package term + +import ( + "io" + "os" + "os/signal" + + windowsconsole "github.com/moby/term/windows" + "golang.org/x/sys/windows" +) + +// State holds the console mode for the terminal. +type State struct { + mode uint32 +} + +// Winsize is used for window size. +type Winsize struct { + Height uint16 + Width uint16 +} + +// vtInputSupported is true if winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported by the console +var vtInputSupported bool + +// StdStreams returns the standard streams (stdin, stdout, stderr). +func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { + // Turn on VT handling on all std handles, if possible. This might + // fail, in which case we will fall back to terminal emulation. + var ( + emulateStdin, emulateStdout, emulateStderr bool + + mode uint32 + ) + + fd := windows.Handle(os.Stdin.Fd()) + if err := windows.GetConsoleMode(fd, &mode); err == nil { + // Validate that winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it. + if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil { + emulateStdin = true + } else { + vtInputSupported = true + } + // Unconditionally set the console mode back even on failure because SetConsoleMode + // remembers invalid bits on input handles. + _ = windows.SetConsoleMode(fd, mode) + } + + fd = windows.Handle(os.Stdout.Fd()) + if err := windows.GetConsoleMode(fd, &mode); err == nil { + // Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it. + if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING|windows.DISABLE_NEWLINE_AUTO_RETURN); err != nil { + emulateStdout = true + } else { + _ = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) + } + } + + fd = windows.Handle(os.Stderr.Fd()) + if err := windows.GetConsoleMode(fd, &mode); err == nil { + // Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it. + if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING|windows.DISABLE_NEWLINE_AUTO_RETURN); err != nil { + emulateStderr = true + } else { + _ = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) + } + } + + // Temporarily use STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and + // STD_ERROR_HANDLE from syscall rather than x/sys/windows as long as + // go-ansiterm hasn't switch to x/sys/windows. + // TODO: switch back to x/sys/windows once go-ansiterm has switched + if emulateStdin { + h := uint32(windows.STD_INPUT_HANDLE) + stdIn = windowsconsole.NewAnsiReader(int(h)) + } else { + stdIn = os.Stdin + } + + if emulateStdout { + h := uint32(windows.STD_OUTPUT_HANDLE) + stdOut = windowsconsole.NewAnsiWriter(int(h)) + } else { + stdOut = os.Stdout + } + + if emulateStderr { + h := uint32(windows.STD_ERROR_HANDLE) + stdErr = windowsconsole.NewAnsiWriter(int(h)) + } else { + stdErr = os.Stderr + } + + return +} + +// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal. +func GetFdInfo(in interface{}) (uintptr, bool) { + return windowsconsole.GetHandleInfo(in) +} + +// GetWinsize returns the window size based on the specified file descriptor. +func GetWinsize(fd uintptr) (*Winsize, error) { + var info windows.ConsoleScreenBufferInfo + if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil { + return nil, err + } + + winsize := &Winsize{ + Width: uint16(info.Window.Right - info.Window.Left + 1), + Height: uint16(info.Window.Bottom - info.Window.Top + 1), + } + + return winsize, nil +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd uintptr) bool { + var mode uint32 + err := windows.GetConsoleMode(windows.Handle(fd), &mode) + return err == nil +} + +// RestoreTerminal restores the terminal connected to the given file descriptor +// to a previous state. +func RestoreTerminal(fd uintptr, state *State) error { + return windows.SetConsoleMode(windows.Handle(fd), state.mode) +} + +// SaveState saves the state of the terminal connected to the given file descriptor. +func SaveState(fd uintptr) (*State, error) { + var mode uint32 + + if err := windows.GetConsoleMode(windows.Handle(fd), &mode); err != nil { + return nil, err + } + + return &State{mode: mode}, nil +} + +// DisableEcho disables echo for the terminal connected to the given file descriptor. +// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx +func DisableEcho(fd uintptr, state *State) error { + mode := state.mode + mode &^= windows.ENABLE_ECHO_INPUT + mode |= windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT + err := windows.SetConsoleMode(windows.Handle(fd), mode) + if err != nil { + return err + } + + // Register an interrupt handler to catch and restore prior state + restoreAtInterrupt(fd, state) + return nil +} + +// SetRawTerminal puts the terminal connected to the given file descriptor into +// raw mode and returns the previous state. On UNIX, this puts both the input +// and output into raw mode. On Windows, it only puts the input into raw mode. +func SetRawTerminal(fd uintptr) (*State, error) { + state, err := MakeRaw(fd) + if err != nil { + return nil, err + } + + // Register an interrupt handler to catch and restore prior state + restoreAtInterrupt(fd, state) + return state, err +} + +// SetRawTerminalOutput puts the output of terminal connected to the given file +// descriptor into raw mode. On UNIX, this does nothing and returns nil for the +// state. On Windows, it disables LF -> CRLF translation. +func SetRawTerminalOutput(fd uintptr) (*State, error) { + state, err := SaveState(fd) + if err != nil { + return nil, err + } + + // Ignore failures, since winterm.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this + // version of Windows. + _ = windows.SetConsoleMode(windows.Handle(fd), state.mode|windows.DISABLE_NEWLINE_AUTO_RETURN) + return state, err +} + +// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be restored. +func MakeRaw(fd uintptr) (*State, error) { + state, err := SaveState(fd) + if err != nil { + return nil, err + } + + mode := state.mode + + // See + // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx + // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx + + // Disable these modes + mode &^= windows.ENABLE_ECHO_INPUT + mode &^= windows.ENABLE_LINE_INPUT + mode &^= windows.ENABLE_MOUSE_INPUT + mode &^= windows.ENABLE_WINDOW_INPUT + mode &^= windows.ENABLE_PROCESSED_INPUT + + // Enable these modes + mode |= windows.ENABLE_EXTENDED_FLAGS + mode |= windows.ENABLE_INSERT_MODE + mode |= windows.ENABLE_QUICK_EDIT_MODE + if vtInputSupported { + mode |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT + } + + err = windows.SetConsoleMode(windows.Handle(fd), mode) + if err != nil { + return nil, err + } + return state, nil +} + +func restoreAtInterrupt(fd uintptr, state *State) { + sigchan := make(chan os.Signal, 1) + signal.Notify(sigchan, os.Interrupt) + + go func() { + _ = <-sigchan + _ = RestoreTerminal(fd, state) + os.Exit(0) + }() +} diff --git a/vendor/github.com/moby/term/termios.go b/vendor/github.com/moby/term/termios.go new file mode 100644 index 0000000000..0f028e2273 --- /dev/null +++ b/vendor/github.com/moby/term/termios.go @@ -0,0 +1,35 @@ +// +build !windows + +package term + +import ( + "golang.org/x/sys/unix" +) + +// Termios is the Unix API for terminal I/O. +type Termios = unix.Termios + +// MakeRaw puts the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd uintptr) (*State, error) { + termios, err := tcget(fd) + if err != nil { + return nil, err + } + + oldState := State{termios: *termios} + + termios.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON) + termios.Oflag &^= unix.OPOST + termios.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN) + termios.Cflag &^= (unix.CSIZE | unix.PARENB) + termios.Cflag |= unix.CS8 + termios.Cc[unix.VMIN] = 1 + termios.Cc[unix.VTIME] = 0 + + if err := tcset(fd, termios); err != nil { + return nil, err + } + return &oldState, nil +} diff --git a/vendor/github.com/moby/term/termios_bsd.go b/vendor/github.com/moby/term/termios_bsd.go new file mode 100644 index 0000000000..922dd4baab --- /dev/null +++ b/vendor/github.com/moby/term/termios_bsd.go @@ -0,0 +1,12 @@ +// +build darwin freebsd openbsd netbsd + +package term + +import ( + "golang.org/x/sys/unix" +) + +const ( + getTermios = unix.TIOCGETA + setTermios = unix.TIOCSETA +) diff --git a/vendor/github.com/moby/term/termios_nonbsd.go b/vendor/github.com/moby/term/termios_nonbsd.go new file mode 100644 index 0000000000..038fd61ba1 --- /dev/null +++ b/vendor/github.com/moby/term/termios_nonbsd.go @@ -0,0 +1,12 @@ +//+build !darwin,!freebsd,!netbsd,!openbsd,!windows + +package term + +import ( + "golang.org/x/sys/unix" +) + +const ( + getTermios = unix.TCGETS + setTermios = unix.TCSETS +) diff --git a/vendor/github.com/moby/term/windows/ansi_reader.go b/vendor/github.com/moby/term/windows/ansi_reader.go new file mode 100644 index 0000000000..155251521b --- /dev/null +++ b/vendor/github.com/moby/term/windows/ansi_reader.go @@ -0,0 +1,252 @@ +// +build windows + +package windowsconsole + +import ( + "bytes" + "errors" + "fmt" + "io" + "os" + "strings" + "unsafe" + + ansiterm "github.com/Azure/go-ansiterm" + "github.com/Azure/go-ansiterm/winterm" +) + +const ( + escapeSequence = ansiterm.KEY_ESC_CSI +) + +// ansiReader wraps a standard input file (e.g., os.Stdin) providing ANSI sequence translation. +type ansiReader struct { + file *os.File + fd uintptr + buffer []byte + cbBuffer int + command []byte +} + +// NewAnsiReader returns an io.ReadCloser that provides VT100 terminal emulation on top of a +// Windows console input handle. +func NewAnsiReader(nFile int) io.ReadCloser { + file, fd := winterm.GetStdFile(nFile) + return &ansiReader{ + file: file, + fd: fd, + command: make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH), + buffer: make([]byte, 0), + } +} + +// Close closes the wrapped file. +func (ar *ansiReader) Close() (err error) { + return ar.file.Close() +} + +// Fd returns the file descriptor of the wrapped file. +func (ar *ansiReader) Fd() uintptr { + return ar.fd +} + +// Read reads up to len(p) bytes of translated input events into p. +func (ar *ansiReader) Read(p []byte) (int, error) { + if len(p) == 0 { + return 0, nil + } + + // Previously read bytes exist, read as much as we can and return + if len(ar.buffer) > 0 { + originalLength := len(ar.buffer) + copiedLength := copy(p, ar.buffer) + + if copiedLength == originalLength { + ar.buffer = make([]byte, 0, len(p)) + } else { + ar.buffer = ar.buffer[copiedLength:] + } + + return copiedLength, nil + } + + // Read and translate key events + events, err := readInputEvents(ar, len(p)) + if err != nil { + return 0, err + } else if len(events) == 0 { + return 0, nil + } + + keyBytes := translateKeyEvents(events, []byte(escapeSequence)) + + // Save excess bytes and right-size keyBytes + if len(keyBytes) > len(p) { + ar.buffer = keyBytes[len(p):] + keyBytes = keyBytes[:len(p)] + } else if len(keyBytes) == 0 { + return 0, nil + } + + copiedLength := copy(p, keyBytes) + if copiedLength != len(keyBytes) { + return 0, errors.New("unexpected copy length encountered") + } + + return copiedLength, nil +} + +// readInputEvents polls until at least one event is available. +func readInputEvents(ar *ansiReader, maxBytes int) ([]winterm.INPUT_RECORD, error) { + // Determine the maximum number of records to retrieve + // -- Cast around the type system to obtain the size of a single INPUT_RECORD. + // unsafe.Sizeof requires an expression vs. a type-reference; the casting + // tricks the type system into believing it has such an expression. + recordSize := int(unsafe.Sizeof(*((*winterm.INPUT_RECORD)(unsafe.Pointer(&maxBytes))))) + countRecords := maxBytes / recordSize + if countRecords > ansiterm.MAX_INPUT_EVENTS { + countRecords = ansiterm.MAX_INPUT_EVENTS + } else if countRecords == 0 { + countRecords = 1 + } + + // Wait for and read input events + events := make([]winterm.INPUT_RECORD, countRecords) + nEvents := uint32(0) + eventsExist, err := winterm.WaitForSingleObject(ar.fd, winterm.WAIT_INFINITE) + if err != nil { + return nil, err + } + + if eventsExist { + err = winterm.ReadConsoleInput(ar.fd, events, &nEvents) + if err != nil { + return nil, err + } + } + + // Return a slice restricted to the number of returned records + return events[:nEvents], nil +} + +// KeyEvent Translation Helpers + +var arrowKeyMapPrefix = map[uint16]string{ + winterm.VK_UP: "%s%sA", + winterm.VK_DOWN: "%s%sB", + winterm.VK_RIGHT: "%s%sC", + winterm.VK_LEFT: "%s%sD", +} + +var keyMapPrefix = map[uint16]string{ + winterm.VK_UP: "\x1B[%sA", + winterm.VK_DOWN: "\x1B[%sB", + winterm.VK_RIGHT: "\x1B[%sC", + winterm.VK_LEFT: "\x1B[%sD", + winterm.VK_HOME: "\x1B[1%s~", // showkey shows ^[[1 + winterm.VK_END: "\x1B[4%s~", // showkey shows ^[[4 + winterm.VK_INSERT: "\x1B[2%s~", + winterm.VK_DELETE: "\x1B[3%s~", + winterm.VK_PRIOR: "\x1B[5%s~", + winterm.VK_NEXT: "\x1B[6%s~", + winterm.VK_F1: "", + winterm.VK_F2: "", + winterm.VK_F3: "\x1B[13%s~", + winterm.VK_F4: "\x1B[14%s~", + winterm.VK_F5: "\x1B[15%s~", + winterm.VK_F6: "\x1B[17%s~", + winterm.VK_F7: "\x1B[18%s~", + winterm.VK_F8: "\x1B[19%s~", + winterm.VK_F9: "\x1B[20%s~", + winterm.VK_F10: "\x1B[21%s~", + winterm.VK_F11: "\x1B[23%s~", + winterm.VK_F12: "\x1B[24%s~", +} + +// translateKeyEvents converts the input events into the appropriate ANSI string. +func translateKeyEvents(events []winterm.INPUT_RECORD, escapeSequence []byte) []byte { + var buffer bytes.Buffer + for _, event := range events { + if event.EventType == winterm.KEY_EVENT && event.KeyEvent.KeyDown != 0 { + buffer.WriteString(keyToString(&event.KeyEvent, escapeSequence)) + } + } + + return buffer.Bytes() +} + +// keyToString maps the given input event record to the corresponding string. +func keyToString(keyEvent *winterm.KEY_EVENT_RECORD, escapeSequence []byte) string { + if keyEvent.UnicodeChar == 0 { + return formatVirtualKey(keyEvent.VirtualKeyCode, keyEvent.ControlKeyState, escapeSequence) + } + + _, alt, control := getControlKeys(keyEvent.ControlKeyState) + if control { + // TODO(azlinux): Implement following control sequences + // -D Signals the end of input from the keyboard; also exits current shell. + // -H Deletes the first character to the left of the cursor. Also called the ERASE key. + // -Q Restarts printing after it has been stopped with -s. + // -S Suspends printing on the screen (does not stop the program). + // -U Deletes all characters on the current line. Also called the KILL key. + // -E Quits current command and creates a core + + } + + // +Key generates ESC N Key + if !control && alt { + return ansiterm.KEY_ESC_N + strings.ToLower(string(keyEvent.UnicodeChar)) + } + + return string(keyEvent.UnicodeChar) +} + +// formatVirtualKey converts a virtual key (e.g., up arrow) into the appropriate ANSI string. +func formatVirtualKey(key uint16, controlState uint32, escapeSequence []byte) string { + shift, alt, control := getControlKeys(controlState) + modifier := getControlKeysModifier(shift, alt, control) + + if format, ok := arrowKeyMapPrefix[key]; ok { + return fmt.Sprintf(format, escapeSequence, modifier) + } + + if format, ok := keyMapPrefix[key]; ok { + return fmt.Sprintf(format, modifier) + } + + return "" +} + +// getControlKeys extracts the shift, alt, and ctrl key states. +func getControlKeys(controlState uint32) (shift, alt, control bool) { + shift = 0 != (controlState & winterm.SHIFT_PRESSED) + alt = 0 != (controlState & (winterm.LEFT_ALT_PRESSED | winterm.RIGHT_ALT_PRESSED)) + control = 0 != (controlState & (winterm.LEFT_CTRL_PRESSED | winterm.RIGHT_CTRL_PRESSED)) + return shift, alt, control +} + +// getControlKeysModifier returns the ANSI modifier for the given combination of control keys. +func getControlKeysModifier(shift, alt, control bool) string { + if shift && alt && control { + return ansiterm.KEY_CONTROL_PARAM_8 + } + if alt && control { + return ansiterm.KEY_CONTROL_PARAM_7 + } + if shift && control { + return ansiterm.KEY_CONTROL_PARAM_6 + } + if control { + return ansiterm.KEY_CONTROL_PARAM_5 + } + if shift && alt { + return ansiterm.KEY_CONTROL_PARAM_4 + } + if alt { + return ansiterm.KEY_CONTROL_PARAM_3 + } + if shift { + return ansiterm.KEY_CONTROL_PARAM_2 + } + return "" +} diff --git a/vendor/github.com/moby/term/windows/ansi_writer.go b/vendor/github.com/moby/term/windows/ansi_writer.go new file mode 100644 index 0000000000..ccb5ef0775 --- /dev/null +++ b/vendor/github.com/moby/term/windows/ansi_writer.go @@ -0,0 +1,56 @@ +// +build windows + +package windowsconsole + +import ( + "io" + "os" + + ansiterm "github.com/Azure/go-ansiterm" + "github.com/Azure/go-ansiterm/winterm" +) + +// ansiWriter wraps a standard output file (e.g., os.Stdout) providing ANSI sequence translation. +type ansiWriter struct { + file *os.File + fd uintptr + infoReset *winterm.CONSOLE_SCREEN_BUFFER_INFO + command []byte + escapeSequence []byte + inAnsiSequence bool + parser *ansiterm.AnsiParser +} + +// NewAnsiWriter returns an io.Writer that provides VT100 terminal emulation on top of a +// Windows console output handle. +func NewAnsiWriter(nFile int) io.Writer { + file, fd := winterm.GetStdFile(nFile) + info, err := winterm.GetConsoleScreenBufferInfo(fd) + if err != nil { + return nil + } + + parser := ansiterm.CreateParser("Ground", winterm.CreateWinEventHandler(fd, file)) + + return &ansiWriter{ + file: file, + fd: fd, + infoReset: info, + command: make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH), + escapeSequence: []byte(ansiterm.KEY_ESC_CSI), + parser: parser, + } +} + +func (aw *ansiWriter) Fd() uintptr { + return aw.fd +} + +// Write writes len(p) bytes from p to the underlying data stream. +func (aw *ansiWriter) Write(p []byte) (total int, err error) { + if len(p) == 0 { + return 0, nil + } + + return aw.parser.Parse(p) +} diff --git a/vendor/github.com/moby/term/windows/console.go b/vendor/github.com/moby/term/windows/console.go new file mode 100644 index 0000000000..993694ddcd --- /dev/null +++ b/vendor/github.com/moby/term/windows/console.go @@ -0,0 +1,39 @@ +// +build windows + +package windowsconsole + +import ( + "os" + + "golang.org/x/sys/windows" +) + +// GetHandleInfo returns file descriptor and bool indicating whether the file is a console. +func GetHandleInfo(in interface{}) (uintptr, bool) { + switch t := in.(type) { + case *ansiReader: + return t.Fd(), true + case *ansiWriter: + return t.Fd(), true + } + + var inFd uintptr + var isTerminal bool + + if file, ok := in.(*os.File); ok { + inFd = file.Fd() + isTerminal = isConsole(inFd) + } + return inFd, isTerminal +} + +// IsConsole returns true if the given file descriptor is a Windows Console. +// The code assumes that GetConsoleMode will return an error for file descriptors that are not a console. +// Deprecated: use golang.org/x/sys/windows.GetConsoleMode() or golang.org/x/term.IsTerminal() +var IsConsole = isConsole + +func isConsole(fd uintptr) bool { + var mode uint32 + err := windows.GetConsoleMode(windows.Handle(fd), &mode) + return err == nil +} diff --git a/vendor/github.com/moby/term/windows/doc.go b/vendor/github.com/moby/term/windows/doc.go new file mode 100644 index 0000000000..54265fffaf --- /dev/null +++ b/vendor/github.com/moby/term/windows/doc.go @@ -0,0 +1,5 @@ +// These files implement ANSI-aware input and output streams for use by the Docker Windows client. +// When asked for the set of standard streams (e.g., stdin, stdout, stderr), the code will create +// and return pseudo-streams that convert ANSI sequences to / from Windows Console API calls. + +package windowsconsole diff --git a/vendor/github.com/moby/term/winsize.go b/vendor/github.com/moby/term/winsize.go new file mode 100644 index 0000000000..1ef98d5996 --- /dev/null +++ b/vendor/github.com/moby/term/winsize.go @@ -0,0 +1,20 @@ +// +build !windows + +package term + +import ( + "golang.org/x/sys/unix" +) + +// GetWinsize returns the window size based on the specified file descriptor. +func GetWinsize(fd uintptr) (*Winsize, error) { + uws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ) + ws := &Winsize{Height: uws.Row, Width: uws.Col, x: uws.Xpixel, y: uws.Ypixel} + return ws, err +} + +// SetWinsize tries to set the specified window size for the specified file descriptor. +func SetWinsize(fd uintptr, ws *Winsize) error { + uws := &unix.Winsize{Row: ws.Height, Col: ws.Width, Xpixel: ws.x, Ypixel: ws.y} + return unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, uws) +} diff --git a/vendor/github.com/morikuni/aec/LICENSE b/vendor/github.com/morikuni/aec/LICENSE new file mode 100644 index 0000000000..1c26401641 --- /dev/null +++ b/vendor/github.com/morikuni/aec/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Taihei Morikuni + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/morikuni/aec/README.md b/vendor/github.com/morikuni/aec/README.md new file mode 100644 index 0000000000..3cbc4343ee --- /dev/null +++ b/vendor/github.com/morikuni/aec/README.md @@ -0,0 +1,178 @@ +# aec + +[![GoDoc](https://godoc.org/github.com/morikuni/aec?status.svg)](https://godoc.org/github.com/morikuni/aec) + +Go wrapper for ANSI escape code. + +## Install + +```bash +go get github.com/morikuni/aec +``` + +## Features + +ANSI escape codes depend on terminal environment. +Some of these features may not work. +Check supported Font-Style/Font-Color features with [checkansi](./checkansi). + +[Wikipedia](https://en.wikipedia.org/wiki/ANSI_escape_code) for more detail. + +### Cursor + +- `Up(n)` +- `Down(n)` +- `Right(n)` +- `Left(n)` +- `NextLine(n)` +- `PreviousLine(n)` +- `Column(col)` +- `Position(row, col)` +- `Save` +- `Restore` +- `Hide` +- `Show` +- `Report` + +### Erase + +- `EraseDisplay(mode)` +- `EraseLine(mode)` + +### Scroll + +- `ScrollUp(n)` +- `ScrollDown(n)` + +### Font Style + +- `Bold` +- `Faint` +- `Italic` +- `Underline` +- `BlinkSlow` +- `BlinkRapid` +- `Inverse` +- `Conceal` +- `CrossOut` +- `Frame` +- `Encircle` +- `Overline` + +### Font Color + +Foreground color. + +- `DefaultF` +- `BlackF` +- `RedF` +- `GreenF` +- `YellowF` +- `BlueF` +- `MagentaF` +- `CyanF` +- `WhiteF` +- `LightBlackF` +- `LightRedF` +- `LightGreenF` +- `LightYellowF` +- `LightBlueF` +- `LightMagentaF` +- `LightCyanF` +- `LightWhiteF` +- `Color3BitF(color)` +- `Color8BitF(color)` +- `FullColorF(r, g, b)` + +Background color. + +- `DefaultB` +- `BlackB` +- `RedB` +- `GreenB` +- `YellowB` +- `BlueB` +- `MagentaB` +- `CyanB` +- `WhiteB` +- `LightBlackB` +- `LightRedB` +- `LightGreenB` +- `LightYellowB` +- `LightBlueB` +- `LightMagentaB` +- `LightCyanB` +- `LightWhiteB` +- `Color3BitB(color)` +- `Color8BitB(color)` +- `FullColorB(r, g, b)` + +### Color Converter + +24bit RGB color to ANSI color. + +- `NewRGB3Bit(r, g, b)` +- `NewRGB8Bit(r, g, b)` + +### Builder + +To mix these features. + +```go +custom := aec.EmptyBuilder.Right(2).RGB8BitF(128, 255, 64).RedB().ANSI +custom.Apply("Hello World") +``` + +## Usage + +1. Create ANSI by `aec.XXX().With(aec.YYY())` or `aec.EmptyBuilder.XXX().YYY().ANSI` +2. Print ANSI by `fmt.Print(ansi, "some string", aec.Reset)` or `fmt.Print(ansi.Apply("some string"))` + +`aec.Reset` should be added when using font style or font color features. + +## Example + +Simple progressbar. + +![sample](./sample.gif) + +```go +package main + +import ( + "fmt" + "strings" + "time" + + "github.com/morikuni/aec" +) + +func main() { + const n = 20 + builder := aec.EmptyBuilder + + up2 := aec.Up(2) + col := aec.Column(n + 2) + bar := aec.Color8BitF(aec.NewRGB8Bit(64, 255, 64)) + label := builder.LightRedF().Underline().With(col).Right(1).ANSI + + // for up2 + fmt.Println() + fmt.Println() + + for i := 0; i <= n; i++ { + fmt.Print(up2) + fmt.Println(label.Apply(fmt.Sprint(i, "/", n))) + fmt.Print("[") + fmt.Print(bar.Apply(strings.Repeat("=", i))) + fmt.Println(col.Apply("]")) + time.Sleep(100 * time.Millisecond) + } +} +``` + +## License + +[MIT](./LICENSE) + + diff --git a/vendor/github.com/morikuni/aec/aec.go b/vendor/github.com/morikuni/aec/aec.go new file mode 100644 index 0000000000..566be6eb1e --- /dev/null +++ b/vendor/github.com/morikuni/aec/aec.go @@ -0,0 +1,137 @@ +package aec + +import "fmt" + +// EraseMode is listed in a variable EraseModes. +type EraseMode uint + +var ( + // EraseModes is a list of EraseMode. + EraseModes struct { + // All erase all. + All EraseMode + + // Head erase to head. + Head EraseMode + + // Tail erase to tail. + Tail EraseMode + } + + // Save saves the cursor position. + Save ANSI + + // Restore restores the cursor position. + Restore ANSI + + // Hide hides the cursor. + Hide ANSI + + // Show shows the cursor. + Show ANSI + + // Report reports the cursor position. + Report ANSI +) + +// Up moves up the cursor. +func Up(n uint) ANSI { + if n == 0 { + return empty + } + return newAnsi(fmt.Sprintf(esc+"%dA", n)) +} + +// Down moves down the cursor. +func Down(n uint) ANSI { + if n == 0 { + return empty + } + return newAnsi(fmt.Sprintf(esc+"%dB", n)) +} + +// Right moves right the cursor. +func Right(n uint) ANSI { + if n == 0 { + return empty + } + return newAnsi(fmt.Sprintf(esc+"%dC", n)) +} + +// Left moves left the cursor. +func Left(n uint) ANSI { + if n == 0 { + return empty + } + return newAnsi(fmt.Sprintf(esc+"%dD", n)) +} + +// NextLine moves down the cursor to head of a line. +func NextLine(n uint) ANSI { + if n == 0 { + return empty + } + return newAnsi(fmt.Sprintf(esc+"%dE", n)) +} + +// PreviousLine moves up the cursor to head of a line. +func PreviousLine(n uint) ANSI { + if n == 0 { + return empty + } + return newAnsi(fmt.Sprintf(esc+"%dF", n)) +} + +// Column set the cursor position to a given column. +func Column(col uint) ANSI { + return newAnsi(fmt.Sprintf(esc+"%dG", col)) +} + +// Position set the cursor position to a given absolute position. +func Position(row, col uint) ANSI { + return newAnsi(fmt.Sprintf(esc+"%d;%dH", row, col)) +} + +// EraseDisplay erases display by given EraseMode. +func EraseDisplay(m EraseMode) ANSI { + return newAnsi(fmt.Sprintf(esc+"%dJ", m)) +} + +// EraseLine erases lines by given EraseMode. +func EraseLine(m EraseMode) ANSI { + return newAnsi(fmt.Sprintf(esc+"%dK", m)) +} + +// ScrollUp scrolls up the page. +func ScrollUp(n int) ANSI { + if n == 0 { + return empty + } + return newAnsi(fmt.Sprintf(esc+"%dS", n)) +} + +// ScrollDown scrolls down the page. +func ScrollDown(n int) ANSI { + if n == 0 { + return empty + } + return newAnsi(fmt.Sprintf(esc+"%dT", n)) +} + +func init() { + EraseModes = struct { + All EraseMode + Head EraseMode + Tail EraseMode + }{ + Tail: 0, + Head: 1, + All: 2, + } + + Save = newAnsi(esc + "s") + Restore = newAnsi(esc + "u") + Hide = newAnsi(esc + "?25l") + Show = newAnsi(esc + "?25h") + Report = newAnsi(esc + "6n") +} diff --git a/vendor/github.com/morikuni/aec/ansi.go b/vendor/github.com/morikuni/aec/ansi.go new file mode 100644 index 0000000000..e60722e6e6 --- /dev/null +++ b/vendor/github.com/morikuni/aec/ansi.go @@ -0,0 +1,59 @@ +package aec + +import ( + "fmt" + "strings" +) + +const esc = "\x1b[" + +// Reset resets SGR effect. +const Reset string = "\x1b[0m" + +var empty = newAnsi("") + +// ANSI represents ANSI escape code. +type ANSI interface { + fmt.Stringer + + // With adapts given ANSIs. + With(...ANSI) ANSI + + // Apply wraps given string in ANSI. + Apply(string) string +} + +type ansiImpl string + +func newAnsi(s string) *ansiImpl { + r := ansiImpl(s) + return &r +} + +func (a *ansiImpl) With(ansi ...ANSI) ANSI { + return concat(append([]ANSI{a}, ansi...)) +} + +func (a *ansiImpl) Apply(s string) string { + return a.String() + s + Reset +} + +func (a *ansiImpl) String() string { + return string(*a) +} + +// Apply wraps given string in ANSIs. +func Apply(s string, ansi ...ANSI) string { + if len(ansi) == 0 { + return s + } + return concat(ansi).Apply(s) +} + +func concat(ansi []ANSI) ANSI { + strs := make([]string, 0, len(ansi)) + for _, p := range ansi { + strs = append(strs, p.String()) + } + return newAnsi(strings.Join(strs, "")) +} diff --git a/vendor/github.com/morikuni/aec/builder.go b/vendor/github.com/morikuni/aec/builder.go new file mode 100644 index 0000000000..13bd002d4e --- /dev/null +++ b/vendor/github.com/morikuni/aec/builder.go @@ -0,0 +1,388 @@ +package aec + +// Builder is a lightweight syntax to construct customized ANSI. +type Builder struct { + ANSI ANSI +} + +// EmptyBuilder is an initialized Builder. +var EmptyBuilder *Builder + +// NewBuilder creates a Builder from existing ANSI. +func NewBuilder(a ...ANSI) *Builder { + return &Builder{concat(a)} +} + +// With is a syntax for With. +func (builder *Builder) With(a ...ANSI) *Builder { + return NewBuilder(builder.ANSI.With(a...)) +} + +// Up is a syntax for Up. +func (builder *Builder) Up(n uint) *Builder { + return builder.With(Up(n)) +} + +// Down is a syntax for Down. +func (builder *Builder) Down(n uint) *Builder { + return builder.With(Down(n)) +} + +// Right is a syntax for Right. +func (builder *Builder) Right(n uint) *Builder { + return builder.With(Right(n)) +} + +// Left is a syntax for Left. +func (builder *Builder) Left(n uint) *Builder { + return builder.With(Left(n)) +} + +// NextLine is a syntax for NextLine. +func (builder *Builder) NextLine(n uint) *Builder { + return builder.With(NextLine(n)) +} + +// PreviousLine is a syntax for PreviousLine. +func (builder *Builder) PreviousLine(n uint) *Builder { + return builder.With(PreviousLine(n)) +} + +// Column is a syntax for Column. +func (builder *Builder) Column(col uint) *Builder { + return builder.With(Column(col)) +} + +// Position is a syntax for Position. +func (builder *Builder) Position(row, col uint) *Builder { + return builder.With(Position(row, col)) +} + +// EraseDisplay is a syntax for EraseDisplay. +func (builder *Builder) EraseDisplay(m EraseMode) *Builder { + return builder.With(EraseDisplay(m)) +} + +// EraseLine is a syntax for EraseLine. +func (builder *Builder) EraseLine(m EraseMode) *Builder { + return builder.With(EraseLine(m)) +} + +// ScrollUp is a syntax for ScrollUp. +func (builder *Builder) ScrollUp(n int) *Builder { + return builder.With(ScrollUp(n)) +} + +// ScrollDown is a syntax for ScrollDown. +func (builder *Builder) ScrollDown(n int) *Builder { + return builder.With(ScrollDown(n)) +} + +// Save is a syntax for Save. +func (builder *Builder) Save() *Builder { + return builder.With(Save) +} + +// Restore is a syntax for Restore. +func (builder *Builder) Restore() *Builder { + return builder.With(Restore) +} + +// Hide is a syntax for Hide. +func (builder *Builder) Hide() *Builder { + return builder.With(Hide) +} + +// Show is a syntax for Show. +func (builder *Builder) Show() *Builder { + return builder.With(Show) +} + +// Report is a syntax for Report. +func (builder *Builder) Report() *Builder { + return builder.With(Report) +} + +// Bold is a syntax for Bold. +func (builder *Builder) Bold() *Builder { + return builder.With(Bold) +} + +// Faint is a syntax for Faint. +func (builder *Builder) Faint() *Builder { + return builder.With(Faint) +} + +// Italic is a syntax for Italic. +func (builder *Builder) Italic() *Builder { + return builder.With(Italic) +} + +// Underline is a syntax for Underline. +func (builder *Builder) Underline() *Builder { + return builder.With(Underline) +} + +// BlinkSlow is a syntax for BlinkSlow. +func (builder *Builder) BlinkSlow() *Builder { + return builder.With(BlinkSlow) +} + +// BlinkRapid is a syntax for BlinkRapid. +func (builder *Builder) BlinkRapid() *Builder { + return builder.With(BlinkRapid) +} + +// Inverse is a syntax for Inverse. +func (builder *Builder) Inverse() *Builder { + return builder.With(Inverse) +} + +// Conceal is a syntax for Conceal. +func (builder *Builder) Conceal() *Builder { + return builder.With(Conceal) +} + +// CrossOut is a syntax for CrossOut. +func (builder *Builder) CrossOut() *Builder { + return builder.With(CrossOut) +} + +// BlackF is a syntax for BlackF. +func (builder *Builder) BlackF() *Builder { + return builder.With(BlackF) +} + +// RedF is a syntax for RedF. +func (builder *Builder) RedF() *Builder { + return builder.With(RedF) +} + +// GreenF is a syntax for GreenF. +func (builder *Builder) GreenF() *Builder { + return builder.With(GreenF) +} + +// YellowF is a syntax for YellowF. +func (builder *Builder) YellowF() *Builder { + return builder.With(YellowF) +} + +// BlueF is a syntax for BlueF. +func (builder *Builder) BlueF() *Builder { + return builder.With(BlueF) +} + +// MagentaF is a syntax for MagentaF. +func (builder *Builder) MagentaF() *Builder { + return builder.With(MagentaF) +} + +// CyanF is a syntax for CyanF. +func (builder *Builder) CyanF() *Builder { + return builder.With(CyanF) +} + +// WhiteF is a syntax for WhiteF. +func (builder *Builder) WhiteF() *Builder { + return builder.With(WhiteF) +} + +// DefaultF is a syntax for DefaultF. +func (builder *Builder) DefaultF() *Builder { + return builder.With(DefaultF) +} + +// BlackB is a syntax for BlackB. +func (builder *Builder) BlackB() *Builder { + return builder.With(BlackB) +} + +// RedB is a syntax for RedB. +func (builder *Builder) RedB() *Builder { + return builder.With(RedB) +} + +// GreenB is a syntax for GreenB. +func (builder *Builder) GreenB() *Builder { + return builder.With(GreenB) +} + +// YellowB is a syntax for YellowB. +func (builder *Builder) YellowB() *Builder { + return builder.With(YellowB) +} + +// BlueB is a syntax for BlueB. +func (builder *Builder) BlueB() *Builder { + return builder.With(BlueB) +} + +// MagentaB is a syntax for MagentaB. +func (builder *Builder) MagentaB() *Builder { + return builder.With(MagentaB) +} + +// CyanB is a syntax for CyanB. +func (builder *Builder) CyanB() *Builder { + return builder.With(CyanB) +} + +// WhiteB is a syntax for WhiteB. +func (builder *Builder) WhiteB() *Builder { + return builder.With(WhiteB) +} + +// DefaultB is a syntax for DefaultB. +func (builder *Builder) DefaultB() *Builder { + return builder.With(DefaultB) +} + +// Frame is a syntax for Frame. +func (builder *Builder) Frame() *Builder { + return builder.With(Frame) +} + +// Encircle is a syntax for Encircle. +func (builder *Builder) Encircle() *Builder { + return builder.With(Encircle) +} + +// Overline is a syntax for Overline. +func (builder *Builder) Overline() *Builder { + return builder.With(Overline) +} + +// LightBlackF is a syntax for LightBlueF. +func (builder *Builder) LightBlackF() *Builder { + return builder.With(LightBlackF) +} + +// LightRedF is a syntax for LightRedF. +func (builder *Builder) LightRedF() *Builder { + return builder.With(LightRedF) +} + +// LightGreenF is a syntax for LightGreenF. +func (builder *Builder) LightGreenF() *Builder { + return builder.With(LightGreenF) +} + +// LightYellowF is a syntax for LightYellowF. +func (builder *Builder) LightYellowF() *Builder { + return builder.With(LightYellowF) +} + +// LightBlueF is a syntax for LightBlueF. +func (builder *Builder) LightBlueF() *Builder { + return builder.With(LightBlueF) +} + +// LightMagentaF is a syntax for LightMagentaF. +func (builder *Builder) LightMagentaF() *Builder { + return builder.With(LightMagentaF) +} + +// LightCyanF is a syntax for LightCyanF. +func (builder *Builder) LightCyanF() *Builder { + return builder.With(LightCyanF) +} + +// LightWhiteF is a syntax for LightWhiteF. +func (builder *Builder) LightWhiteF() *Builder { + return builder.With(LightWhiteF) +} + +// LightBlackB is a syntax for LightBlackB. +func (builder *Builder) LightBlackB() *Builder { + return builder.With(LightBlackB) +} + +// LightRedB is a syntax for LightRedB. +func (builder *Builder) LightRedB() *Builder { + return builder.With(LightRedB) +} + +// LightGreenB is a syntax for LightGreenB. +func (builder *Builder) LightGreenB() *Builder { + return builder.With(LightGreenB) +} + +// LightYellowB is a syntax for LightYellowB. +func (builder *Builder) LightYellowB() *Builder { + return builder.With(LightYellowB) +} + +// LightBlueB is a syntax for LightBlueB. +func (builder *Builder) LightBlueB() *Builder { + return builder.With(LightBlueB) +} + +// LightMagentaB is a syntax for LightMagentaB. +func (builder *Builder) LightMagentaB() *Builder { + return builder.With(LightMagentaB) +} + +// LightCyanB is a syntax for LightCyanB. +func (builder *Builder) LightCyanB() *Builder { + return builder.With(LightCyanB) +} + +// LightWhiteB is a syntax for LightWhiteB. +func (builder *Builder) LightWhiteB() *Builder { + return builder.With(LightWhiteB) +} + +// Color3BitF is a syntax for Color3BitF. +func (builder *Builder) Color3BitF(c RGB3Bit) *Builder { + return builder.With(Color3BitF(c)) +} + +// Color3BitB is a syntax for Color3BitB. +func (builder *Builder) Color3BitB(c RGB3Bit) *Builder { + return builder.With(Color3BitB(c)) +} + +// Color8BitF is a syntax for Color8BitF. +func (builder *Builder) Color8BitF(c RGB8Bit) *Builder { + return builder.With(Color8BitF(c)) +} + +// Color8BitB is a syntax for Color8BitB. +func (builder *Builder) Color8BitB(c RGB8Bit) *Builder { + return builder.With(Color8BitB(c)) +} + +// FullColorF is a syntax for FullColorF. +func (builder *Builder) FullColorF(r, g, b uint8) *Builder { + return builder.With(FullColorF(r, g, b)) +} + +// FullColorB is a syntax for FullColorB. +func (builder *Builder) FullColorB(r, g, b uint8) *Builder { + return builder.With(FullColorB(r, g, b)) +} + +// RGB3BitF is a syntax for Color3BitF with NewRGB3Bit. +func (builder *Builder) RGB3BitF(r, g, b uint8) *Builder { + return builder.Color3BitF(NewRGB3Bit(r, g, b)) +} + +// RGB3BitB is a syntax for Color3BitB with NewRGB3Bit. +func (builder *Builder) RGB3BitB(r, g, b uint8) *Builder { + return builder.Color3BitB(NewRGB3Bit(r, g, b)) +} + +// RGB8BitF is a syntax for Color8BitF with NewRGB8Bit. +func (builder *Builder) RGB8BitF(r, g, b uint8) *Builder { + return builder.Color8BitF(NewRGB8Bit(r, g, b)) +} + +// RGB8BitB is a syntax for Color8BitB with NewRGB8Bit. +func (builder *Builder) RGB8BitB(r, g, b uint8) *Builder { + return builder.Color8BitB(NewRGB8Bit(r, g, b)) +} + +func init() { + EmptyBuilder = &Builder{empty} +} diff --git a/vendor/github.com/morikuni/aec/sample.gif b/vendor/github.com/morikuni/aec/sample.gif new file mode 100644 index 0000000000000000000000000000000000000000..c6c613bb70645efa4e184726060ea1ff981eeceb GIT binary patch literal 12548 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 Y(?6j)4M_L*quqaULH)mVw5$IA0FB)@mjD0& literal 0 HcmV?d00001 diff --git a/vendor/github.com/morikuni/aec/sgr.go b/vendor/github.com/morikuni/aec/sgr.go new file mode 100644 index 0000000000..0ba3464e6d --- /dev/null +++ b/vendor/github.com/morikuni/aec/sgr.go @@ -0,0 +1,202 @@ +package aec + +import ( + "fmt" +) + +// RGB3Bit is a 3bit RGB color. +type RGB3Bit uint8 + +// RGB8Bit is a 8bit RGB color. +type RGB8Bit uint8 + +func newSGR(n uint) ANSI { + return newAnsi(fmt.Sprintf(esc+"%dm", n)) +} + +// NewRGB3Bit create a RGB3Bit from given RGB. +func NewRGB3Bit(r, g, b uint8) RGB3Bit { + return RGB3Bit((r >> 7) | ((g >> 6) & 0x2) | ((b >> 5) & 0x4)) +} + +// NewRGB8Bit create a RGB8Bit from given RGB. +func NewRGB8Bit(r, g, b uint8) RGB8Bit { + return RGB8Bit(16 + 36*(r/43) + 6*(g/43) + b/43) +} + +// Color3BitF set the foreground color of text. +func Color3BitF(c RGB3Bit) ANSI { + return newAnsi(fmt.Sprintf(esc+"%dm", c+30)) +} + +// Color3BitB set the background color of text. +func Color3BitB(c RGB3Bit) ANSI { + return newAnsi(fmt.Sprintf(esc+"%dm", c+40)) +} + +// Color8BitF set the foreground color of text. +func Color8BitF(c RGB8Bit) ANSI { + return newAnsi(fmt.Sprintf(esc+"38;5;%dm", c)) +} + +// Color8BitB set the background color of text. +func Color8BitB(c RGB8Bit) ANSI { + return newAnsi(fmt.Sprintf(esc+"48;5;%dm", c)) +} + +// FullColorF set the foreground color of text. +func FullColorF(r, g, b uint8) ANSI { + return newAnsi(fmt.Sprintf(esc+"38;2;%d;%d;%dm", r, g, b)) +} + +// FullColorB set the foreground color of text. +func FullColorB(r, g, b uint8) ANSI { + return newAnsi(fmt.Sprintf(esc+"48;2;%d;%d;%dm", r, g, b)) +} + +// Style +var ( + // Bold set the text style to bold or increased intensity. + Bold ANSI + + // Faint set the text style to faint. + Faint ANSI + + // Italic set the text style to italic. + Italic ANSI + + // Underline set the text style to underline. + Underline ANSI + + // BlinkSlow set the text style to slow blink. + BlinkSlow ANSI + + // BlinkRapid set the text style to rapid blink. + BlinkRapid ANSI + + // Inverse swap the foreground color and background color. + Inverse ANSI + + // Conceal set the text style to conceal. + Conceal ANSI + + // CrossOut set the text style to crossed out. + CrossOut ANSI + + // Frame set the text style to framed. + Frame ANSI + + // Encircle set the text style to encircled. + Encircle ANSI + + // Overline set the text style to overlined. + Overline ANSI +) + +// Foreground color of text. +var ( + // DefaultF is the default color of foreground. + DefaultF ANSI + + // Normal color + BlackF ANSI + RedF ANSI + GreenF ANSI + YellowF ANSI + BlueF ANSI + MagentaF ANSI + CyanF ANSI + WhiteF ANSI + + // Light color + LightBlackF ANSI + LightRedF ANSI + LightGreenF ANSI + LightYellowF ANSI + LightBlueF ANSI + LightMagentaF ANSI + LightCyanF ANSI + LightWhiteF ANSI +) + +// Background color of text. +var ( + // DefaultB is the default color of background. + DefaultB ANSI + + // Normal color + BlackB ANSI + RedB ANSI + GreenB ANSI + YellowB ANSI + BlueB ANSI + MagentaB ANSI + CyanB ANSI + WhiteB ANSI + + // Light color + LightBlackB ANSI + LightRedB ANSI + LightGreenB ANSI + LightYellowB ANSI + LightBlueB ANSI + LightMagentaB ANSI + LightCyanB ANSI + LightWhiteB ANSI +) + +func init() { + Bold = newSGR(1) + Faint = newSGR(2) + Italic = newSGR(3) + Underline = newSGR(4) + BlinkSlow = newSGR(5) + BlinkRapid = newSGR(6) + Inverse = newSGR(7) + Conceal = newSGR(8) + CrossOut = newSGR(9) + + BlackF = newSGR(30) + RedF = newSGR(31) + GreenF = newSGR(32) + YellowF = newSGR(33) + BlueF = newSGR(34) + MagentaF = newSGR(35) + CyanF = newSGR(36) + WhiteF = newSGR(37) + + DefaultF = newSGR(39) + + BlackB = newSGR(40) + RedB = newSGR(41) + GreenB = newSGR(42) + YellowB = newSGR(43) + BlueB = newSGR(44) + MagentaB = newSGR(45) + CyanB = newSGR(46) + WhiteB = newSGR(47) + + DefaultB = newSGR(49) + + Frame = newSGR(51) + Encircle = newSGR(52) + Overline = newSGR(53) + + LightBlackF = newSGR(90) + LightRedF = newSGR(91) + LightGreenF = newSGR(92) + LightYellowF = newSGR(93) + LightBlueF = newSGR(94) + LightMagentaF = newSGR(95) + LightCyanF = newSGR(96) + LightWhiteF = newSGR(97) + + LightBlackB = newSGR(100) + LightRedB = newSGR(101) + LightGreenB = newSGR(102) + LightYellowB = newSGR(103) + LightBlueB = newSGR(104) + LightMagentaB = newSGR(105) + LightCyanB = newSGR(106) + LightWhiteB = newSGR(107) +} diff --git a/vendor/github.com/opencontainers/go-digest/.mailmap b/vendor/github.com/opencontainers/go-digest/.mailmap new file mode 100644 index 0000000000..eaf8b2f9e6 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/.mailmap @@ -0,0 +1,4 @@ +Aaron Lehmann +Derek McGowan +Stephen J Day +Haibing Zhou diff --git a/vendor/github.com/opencontainers/go-digest/.pullapprove.yml b/vendor/github.com/opencontainers/go-digest/.pullapprove.yml new file mode 100644 index 0000000000..b6165f83ca --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/.pullapprove.yml @@ -0,0 +1,28 @@ +version: 2 + +requirements: + signed_off_by: + required: true + +always_pending: + title_regex: '^WIP' + explanation: 'Work in progress...' + +group_defaults: + required: 2 + approve_by_comment: + enabled: true + approve_regex: '^LGTM' + reject_regex: '^Rejected' + reset_on_push: + enabled: true + author_approval: + ignored: true + conditions: + branches: + - master + +groups: + go-digest: + teams: + - go-digest-maintainers diff --git a/vendor/github.com/opencontainers/go-digest/.travis.yml b/vendor/github.com/opencontainers/go-digest/.travis.yml new file mode 100644 index 0000000000..5775f885c1 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/.travis.yml @@ -0,0 +1,5 @@ +language: go +go: + - 1.12.x + - 1.13.x + - master diff --git a/vendor/github.com/opencontainers/go-digest/CONTRIBUTING.md b/vendor/github.com/opencontainers/go-digest/CONTRIBUTING.md new file mode 100644 index 0000000000..e4d962ac16 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/CONTRIBUTING.md @@ -0,0 +1,72 @@ +# Contributing to Docker open source projects + +Want to hack on this project? Awesome! Here are instructions to get you started. + +This project is a part of the [Docker](https://www.docker.com) project, and follows +the same rules and principles. If you're already familiar with the way +Docker does things, you'll feel right at home. + +Otherwise, go read Docker's +[contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), +[issue triaging](https://github.com/docker/docker/blob/master/project/ISSUE-TRIAGE.md), +[review process](https://github.com/docker/docker/blob/master/project/REVIEWING.md) and +[branches and tags](https://github.com/docker/docker/blob/master/project/BRANCHES-AND-TAGS.md). + +For an in-depth description of our contribution process, visit the +contributors guide: [Understand how to contribute](https://docs.docker.com/opensource/workflow/make-a-contribution/) + +### Sign your work + +The sign-off is a simple line at the end of the explanation for the patch. Your +signature certifies that you wrote the patch or otherwise have the right to pass +it on as an open-source patch. The rules are pretty simple: if you can certify +the below (from [developercertificate.org](http://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +1 Letterman Drive +Suite D4700 +San Francisco, CA, 94129 + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +Then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +Use your real name (sorry, no pseudonyms or anonymous contributions.) + +If you set your `user.name` and `user.email` git configs, you can sign your +commit automatically with `git commit -s`. diff --git a/vendor/github.com/opencontainers/go-digest/LICENSE b/vendor/github.com/opencontainers/go-digest/LICENSE new file mode 100644 index 0000000000..3ac8ab6487 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/LICENSE @@ -0,0 +1,192 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + Copyright 2019, 2020 OCI Contributors + Copyright 2016 Docker, Inc. + + 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 + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/opencontainers/go-digest/LICENSE.docs b/vendor/github.com/opencontainers/go-digest/LICENSE.docs new file mode 100644 index 0000000000..e26cd4fc8e --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/LICENSE.docs @@ -0,0 +1,425 @@ +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + + including for purposes of Section 3(b); and + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public licenses. +Notwithstanding, Creative Commons may elect to apply one of its public +licenses to material it publishes and in those instances will be +considered the "Licensor." Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the public +licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/vendor/github.com/opencontainers/go-digest/MAINTAINERS b/vendor/github.com/opencontainers/go-digest/MAINTAINERS new file mode 100644 index 0000000000..843b1b2061 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/MAINTAINERS @@ -0,0 +1,5 @@ +Derek McGowan (@dmcgowan) +Stephen Day (@stevvooe) +Vincent Batts (@vbatts) +Akihiro Suda (@AkihiroSuda) +Sebastiaan van Stijn (@thaJeztah) diff --git a/vendor/github.com/opencontainers/go-digest/README.md b/vendor/github.com/opencontainers/go-digest/README.md new file mode 100644 index 0000000000..a11287207e --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/README.md @@ -0,0 +1,96 @@ +# go-digest + +[![GoDoc](https://godoc.org/github.com/opencontainers/go-digest?status.svg)](https://godoc.org/github.com/opencontainers/go-digest) [![Go Report Card](https://goreportcard.com/badge/github.com/opencontainers/go-digest)](https://goreportcard.com/report/github.com/opencontainers/go-digest) [![Build Status](https://travis-ci.org/opencontainers/go-digest.svg?branch=master)](https://travis-ci.org/opencontainers/go-digest) + +Common digest package used across the container ecosystem. + +Please see the [godoc](https://godoc.org/github.com/opencontainers/go-digest) for more information. + +# What is a digest? + +A digest is just a [hash](https://en.wikipedia.org/wiki/Hash_function). + +The most common use case for a digest is to create a content identifier for use in [Content Addressable Storage](https://en.wikipedia.org/wiki/Content-addressable_storage) systems: + +```go +id := digest.FromBytes([]byte("my content")) +``` + +In the example above, the id can be used to uniquely identify the byte slice "my content". +This allows two disparate applications to agree on a verifiable identifier without having to trust one another. + +An identifying digest can be verified, as follows: + +```go +if id != digest.FromBytes([]byte("my content")) { + return errors.New("the content has changed!") +} +``` + +A `Verifier` type can be used to handle cases where an `io.Reader` makes more sense: + +```go +rd := getContent() +verifier := id.Verifier() +io.Copy(verifier, rd) + +if !verifier.Verified() { + return errors.New("the content has changed!") +} +``` + +Using [Merkle DAGs](https://en.wikipedia.org/wiki/Merkle_tree), this can power a rich, safe, content distribution system. + +# Usage + +While the [godoc](https://godoc.org/github.com/opencontainers/go-digest) is considered the best resource, a few important items need to be called out when using this package. + +1. Make sure to import the hash implementations into your application or the package will panic. + You should have something like the following in the main (or other entrypoint) of your application: + + ```go + import ( + _ "crypto/sha256" + _ "crypto/sha512" + ) + ``` + This may seem inconvenient but it allows you replace the hash + implementations with others, such as https://github.com/stevvooe/resumable. + +2. Even though `digest.Digest` may be assemblable as a string, _always_ verify your input with `digest.Parse` or use `Digest.Validate` when accepting untrusted input. + While there are measures to avoid common problems, this will ensure you have valid digests in the rest of your application. + +3. While alternative encodings of hash values (digests) are possible (for example, base64), this package deals exclusively with hex-encoded digests. + +# Stability + +The Go API, at this stage, is considered stable, unless otherwise noted. + +As always, before using a package export, read the [godoc](https://godoc.org/github.com/opencontainers/go-digest). + +# Contributing + +This package is considered fairly complete. +It has been in production in thousands (millions?) of deployments and is fairly battle-hardened. +New additions will be met with skepticism. +If you think there is a missing feature, please file a bug clearly describing the problem and the alternatives you tried before submitting a PR. + +## Code of Conduct + +Participation in the OpenContainers community is governed by [OpenContainer's Code of Conduct][code-of-conduct]. + +## Security + +If you find an issue, please follow the [security][security] protocol to report it. + +# Copyright and license + +Copyright © 2019, 2020 OCI Contributors +Copyright © 2016 Docker, Inc. +All rights reserved, except as follows. +Code is released under the [Apache 2.0 license](LICENSE). +This `README.md` file and the [`CONTRIBUTING.md`](CONTRIBUTING.md) file are licensed under the Creative Commons Attribution 4.0 International License under the terms and conditions set forth in the file [`LICENSE.docs`](LICENSE.docs). +You may obtain a duplicate copy of the same license, titled CC BY-SA 4.0, at http://creativecommons.org/licenses/by-sa/4.0/. + +[security]: https://github.com/opencontainers/org/blob/master/security +[code-of-conduct]: https://github.com/opencontainers/org/blob/master/CODE_OF_CONDUCT.md diff --git a/vendor/github.com/opencontainers/go-digest/algorithm.go b/vendor/github.com/opencontainers/go-digest/algorithm.go new file mode 100644 index 0000000000..490951dc3f --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/algorithm.go @@ -0,0 +1,193 @@ +// Copyright 2019, 2020 OCI Contributors +// Copyright 2017 Docker, Inc. +// +// 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 +// +// https://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. + +package digest + +import ( + "crypto" + "fmt" + "hash" + "io" + "regexp" +) + +// Algorithm identifies and implementation of a digester by an identifier. +// Note the that this defines both the hash algorithm used and the string +// encoding. +type Algorithm string + +// supported digest types +const ( + SHA256 Algorithm = "sha256" // sha256 with hex encoding (lower case only) + SHA384 Algorithm = "sha384" // sha384 with hex encoding (lower case only) + SHA512 Algorithm = "sha512" // sha512 with hex encoding (lower case only) + + // Canonical is the primary digest algorithm used with the distribution + // project. Other digests may be used but this one is the primary storage + // digest. + Canonical = SHA256 +) + +var ( + // TODO(stevvooe): Follow the pattern of the standard crypto package for + // registration of digests. Effectively, we are a registerable set and + // common symbol access. + + // algorithms maps values to hash.Hash implementations. Other algorithms + // may be available but they cannot be calculated by the digest package. + algorithms = map[Algorithm]crypto.Hash{ + SHA256: crypto.SHA256, + SHA384: crypto.SHA384, + SHA512: crypto.SHA512, + } + + // anchoredEncodedRegexps contains anchored regular expressions for hex-encoded digests. + // Note that /A-F/ disallowed. + anchoredEncodedRegexps = map[Algorithm]*regexp.Regexp{ + SHA256: regexp.MustCompile(`^[a-f0-9]{64}$`), + SHA384: regexp.MustCompile(`^[a-f0-9]{96}$`), + SHA512: regexp.MustCompile(`^[a-f0-9]{128}$`), + } +) + +// Available returns true if the digest type is available for use. If this +// returns false, Digester and Hash will return nil. +func (a Algorithm) Available() bool { + h, ok := algorithms[a] + if !ok { + return false + } + + // check availability of the hash, as well + return h.Available() +} + +func (a Algorithm) String() string { + return string(a) +} + +// Size returns number of bytes returned by the hash. +func (a Algorithm) Size() int { + h, ok := algorithms[a] + if !ok { + return 0 + } + return h.Size() +} + +// Set implemented to allow use of Algorithm as a command line flag. +func (a *Algorithm) Set(value string) error { + if value == "" { + *a = Canonical + } else { + // just do a type conversion, support is queried with Available. + *a = Algorithm(value) + } + + if !a.Available() { + return ErrDigestUnsupported + } + + return nil +} + +// Digester returns a new digester for the specified algorithm. If the algorithm +// does not have a digester implementation, nil will be returned. This can be +// checked by calling Available before calling Digester. +func (a Algorithm) Digester() Digester { + return &digester{ + alg: a, + hash: a.Hash(), + } +} + +// Hash returns a new hash as used by the algorithm. If not available, the +// method will panic. Check Algorithm.Available() before calling. +func (a Algorithm) Hash() hash.Hash { + if !a.Available() { + // Empty algorithm string is invalid + if a == "" { + panic(fmt.Sprintf("empty digest algorithm, validate before calling Algorithm.Hash()")) + } + + // NOTE(stevvooe): A missing hash is usually a programming error that + // must be resolved at compile time. We don't import in the digest + // package to allow users to choose their hash implementation (such as + // when using stevvooe/resumable or a hardware accelerated package). + // + // Applications that may want to resolve the hash at runtime should + // call Algorithm.Available before call Algorithm.Hash(). + panic(fmt.Sprintf("%v not available (make sure it is imported)", a)) + } + + return algorithms[a].New() +} + +// Encode encodes the raw bytes of a digest, typically from a hash.Hash, into +// the encoded portion of the digest. +func (a Algorithm) Encode(d []byte) string { + // TODO(stevvooe): Currently, all algorithms use a hex encoding. When we + // add support for back registration, we can modify this accordingly. + return fmt.Sprintf("%x", d) +} + +// FromReader returns the digest of the reader using the algorithm. +func (a Algorithm) FromReader(rd io.Reader) (Digest, error) { + digester := a.Digester() + + if _, err := io.Copy(digester.Hash(), rd); err != nil { + return "", err + } + + return digester.Digest(), nil +} + +// FromBytes digests the input and returns a Digest. +func (a Algorithm) FromBytes(p []byte) Digest { + digester := a.Digester() + + if _, err := digester.Hash().Write(p); err != nil { + // Writes to a Hash should never fail. None of the existing + // hash implementations in the stdlib or hashes vendored + // here can return errors from Write. Having a panic in this + // condition instead of having FromBytes return an error value + // avoids unnecessary error handling paths in all callers. + panic("write to hash function returned error: " + err.Error()) + } + + return digester.Digest() +} + +// FromString digests the string input and returns a Digest. +func (a Algorithm) FromString(s string) Digest { + return a.FromBytes([]byte(s)) +} + +// Validate validates the encoded portion string +func (a Algorithm) Validate(encoded string) error { + r, ok := anchoredEncodedRegexps[a] + if !ok { + return ErrDigestUnsupported + } + // Digests much always be hex-encoded, ensuring that their hex portion will + // always be size*2 + if a.Size()*2 != len(encoded) { + return ErrDigestInvalidLength + } + if r.MatchString(encoded) { + return nil + } + return ErrDigestInvalidFormat +} diff --git a/vendor/github.com/opencontainers/go-digest/digest.go b/vendor/github.com/opencontainers/go-digest/digest.go new file mode 100644 index 0000000000..518b5e7154 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/digest.go @@ -0,0 +1,157 @@ +// Copyright 2019, 2020 OCI Contributors +// Copyright 2017 Docker, Inc. +// +// 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 +// +// https://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. + +package digest + +import ( + "fmt" + "hash" + "io" + "regexp" + "strings" +) + +// Digest allows simple protection of hex formatted digest strings, prefixed +// by their algorithm. Strings of type Digest have some guarantee of being in +// the correct format and it provides quick access to the components of a +// digest string. +// +// The following is an example of the contents of Digest types: +// +// sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc +// +// This allows to abstract the digest behind this type and work only in those +// terms. +type Digest string + +// NewDigest returns a Digest from alg and a hash.Hash object. +func NewDigest(alg Algorithm, h hash.Hash) Digest { + return NewDigestFromBytes(alg, h.Sum(nil)) +} + +// NewDigestFromBytes returns a new digest from the byte contents of p. +// Typically, this can come from hash.Hash.Sum(...) or xxx.SumXXX(...) +// functions. This is also useful for rebuilding digests from binary +// serializations. +func NewDigestFromBytes(alg Algorithm, p []byte) Digest { + return NewDigestFromEncoded(alg, alg.Encode(p)) +} + +// NewDigestFromHex is deprecated. Please use NewDigestFromEncoded. +func NewDigestFromHex(alg, hex string) Digest { + return NewDigestFromEncoded(Algorithm(alg), hex) +} + +// NewDigestFromEncoded returns a Digest from alg and the encoded digest. +func NewDigestFromEncoded(alg Algorithm, encoded string) Digest { + return Digest(fmt.Sprintf("%s:%s", alg, encoded)) +} + +// DigestRegexp matches valid digest types. +var DigestRegexp = regexp.MustCompile(`[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+`) + +// DigestRegexpAnchored matches valid digest types, anchored to the start and end of the match. +var DigestRegexpAnchored = regexp.MustCompile(`^` + DigestRegexp.String() + `$`) + +var ( + // ErrDigestInvalidFormat returned when digest format invalid. + ErrDigestInvalidFormat = fmt.Errorf("invalid checksum digest format") + + // ErrDigestInvalidLength returned when digest has invalid length. + ErrDigestInvalidLength = fmt.Errorf("invalid checksum digest length") + + // ErrDigestUnsupported returned when the digest algorithm is unsupported. + ErrDigestUnsupported = fmt.Errorf("unsupported digest algorithm") +) + +// Parse parses s and returns the validated digest object. An error will +// be returned if the format is invalid. +func Parse(s string) (Digest, error) { + d := Digest(s) + return d, d.Validate() +} + +// FromReader consumes the content of rd until io.EOF, returning canonical digest. +func FromReader(rd io.Reader) (Digest, error) { + return Canonical.FromReader(rd) +} + +// FromBytes digests the input and returns a Digest. +func FromBytes(p []byte) Digest { + return Canonical.FromBytes(p) +} + +// FromString digests the input and returns a Digest. +func FromString(s string) Digest { + return Canonical.FromString(s) +} + +// Validate checks that the contents of d is a valid digest, returning an +// error if not. +func (d Digest) Validate() error { + s := string(d) + i := strings.Index(s, ":") + if i <= 0 || i+1 == len(s) { + return ErrDigestInvalidFormat + } + algorithm, encoded := Algorithm(s[:i]), s[i+1:] + if !algorithm.Available() { + if !DigestRegexpAnchored.MatchString(s) { + return ErrDigestInvalidFormat + } + return ErrDigestUnsupported + } + return algorithm.Validate(encoded) +} + +// Algorithm returns the algorithm portion of the digest. This will panic if +// the underlying digest is not in a valid format. +func (d Digest) Algorithm() Algorithm { + return Algorithm(d[:d.sepIndex()]) +} + +// Verifier returns a writer object that can be used to verify a stream of +// content against the digest. If the digest is invalid, the method will panic. +func (d Digest) Verifier() Verifier { + return hashVerifier{ + hash: d.Algorithm().Hash(), + digest: d, + } +} + +// Encoded returns the encoded portion of the digest. This will panic if the +// underlying digest is not in a valid format. +func (d Digest) Encoded() string { + return string(d[d.sepIndex()+1:]) +} + +// Hex is deprecated. Please use Digest.Encoded. +func (d Digest) Hex() string { + return d.Encoded() +} + +func (d Digest) String() string { + return string(d) +} + +func (d Digest) sepIndex() int { + i := strings.Index(string(d), ":") + + if i < 0 { + panic(fmt.Sprintf("no ':' separator in digest %q", d)) + } + + return i +} diff --git a/vendor/github.com/opencontainers/go-digest/digester.go b/vendor/github.com/opencontainers/go-digest/digester.go new file mode 100644 index 0000000000..ede9077571 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/digester.go @@ -0,0 +1,40 @@ +// Copyright 2019, 2020 OCI Contributors +// Copyright 2017 Docker, Inc. +// +// 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 +// +// https://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. + +package digest + +import "hash" + +// Digester calculates the digest of written data. Writes should go directly +// to the return value of Hash, while calling Digest will return the current +// value of the digest. +type Digester interface { + Hash() hash.Hash // provides direct access to underlying hash instance. + Digest() Digest +} + +// digester provides a simple digester definition that embeds a hasher. +type digester struct { + alg Algorithm + hash hash.Hash +} + +func (d *digester) Hash() hash.Hash { + return d.hash +} + +func (d *digester) Digest() Digest { + return NewDigest(d.alg, d.hash) +} diff --git a/vendor/github.com/opencontainers/go-digest/doc.go b/vendor/github.com/opencontainers/go-digest/doc.go new file mode 100644 index 0000000000..83d3a936ca --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/doc.go @@ -0,0 +1,62 @@ +// Copyright 2019, 2020 OCI Contributors +// Copyright 2017 Docker, Inc. +// +// 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 +// +// https://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. + +// Package digest provides a generalized type to opaquely represent message +// digests and their operations within the registry. The Digest type is +// designed to serve as a flexible identifier in a content-addressable system. +// More importantly, it provides tools and wrappers to work with +// hash.Hash-based digests with little effort. +// +// Basics +// +// The format of a digest is simply a string with two parts, dubbed the +// "algorithm" and the "digest", separated by a colon: +// +// : +// +// An example of a sha256 digest representation follows: +// +// sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc +// +// The "algorithm" portion defines both the hashing algorithm used to calculate +// the digest and the encoding of the resulting digest, which defaults to "hex" +// if not otherwise specified. Currently, all supported algorithms have their +// digests encoded in hex strings. +// +// In the example above, the string "sha256" is the algorithm and the hex bytes +// are the "digest". +// +// Because the Digest type is simply a string, once a valid Digest is +// obtained, comparisons are cheap, quick and simple to express with the +// standard equality operator. +// +// Verification +// +// The main benefit of using the Digest type is simple verification against a +// given digest. The Verifier interface, modeled after the stdlib hash.Hash +// interface, provides a common write sink for digest verification. After +// writing is complete, calling the Verifier.Verified method will indicate +// whether or not the stream of bytes matches the target digest. +// +// Missing Features +// +// In addition to the above, we intend to add the following features to this +// package: +// +// 1. A Digester type that supports write sink digest calculation. +// +// 2. Suspend and resume of ongoing digest calculations to support efficient digest verification in the registry. +// +package digest diff --git a/vendor/github.com/opencontainers/go-digest/go.mod b/vendor/github.com/opencontainers/go-digest/go.mod new file mode 100644 index 0000000000..cf5d7b1d2d --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/go.mod @@ -0,0 +1,3 @@ +module github.com/opencontainers/go-digest + +go 1.13 diff --git a/vendor/github.com/opencontainers/go-digest/verifiers.go b/vendor/github.com/opencontainers/go-digest/verifiers.go new file mode 100644 index 0000000000..afef506f46 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/verifiers.go @@ -0,0 +1,46 @@ +// Copyright 2019, 2020 OCI Contributors +// Copyright 2017 Docker, Inc. +// +// 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 +// +// https://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. + +package digest + +import ( + "hash" + "io" +) + +// Verifier presents a general verification interface to be used with message +// digests and other byte stream verifications. Users instantiate a Verifier +// from one of the various methods, write the data under test to it then check +// the result with the Verified method. +type Verifier interface { + io.Writer + + // Verified will return true if the content written to Verifier matches + // the digest. + Verified() bool +} + +type hashVerifier struct { + digest Digest + hash hash.Hash +} + +func (hv hashVerifier) Write(p []byte) (n int, err error) { + return hv.hash.Write(p) +} + +func (hv hashVerifier) Verified() bool { + return hv.digest == NewDigest(hv.digest.Algorithm(), hv.hash) +} diff --git a/vendor/github.com/opencontainers/image-spec/LICENSE b/vendor/github.com/opencontainers/image-spec/LICENSE new file mode 100644 index 0000000000..9fdc20fdb6 --- /dev/null +++ b/vendor/github.com/opencontainers/image-spec/LICENSE @@ -0,0 +1,191 @@ + + 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 + + Copyright 2016 The Linux Foundation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/v1/annotations.go b/vendor/github.com/opencontainers/image-spec/specs-go/v1/annotations.go new file mode 100644 index 0000000000..581cf7cdfa --- /dev/null +++ b/vendor/github.com/opencontainers/image-spec/specs-go/v1/annotations.go @@ -0,0 +1,62 @@ +// Copyright 2016 The Linux Foundation +// +// 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. + +package v1 + +const ( + // AnnotationCreated is the annotation key for the date and time on which the image was built (date-time string as defined by RFC 3339). + AnnotationCreated = "org.opencontainers.image.created" + + // AnnotationAuthors is the annotation key for the contact details of the people or organization responsible for the image (freeform string). + AnnotationAuthors = "org.opencontainers.image.authors" + + // AnnotationURL is the annotation key for the URL to find more information on the image. + AnnotationURL = "org.opencontainers.image.url" + + // AnnotationDocumentation is the annotation key for the URL to get documentation on the image. + AnnotationDocumentation = "org.opencontainers.image.documentation" + + // AnnotationSource is the annotation key for the URL to get source code for building the image. + AnnotationSource = "org.opencontainers.image.source" + + // AnnotationVersion is the annotation key for the version of the packaged software. + // The version MAY match a label or tag in the source code repository. + // The version MAY be Semantic versioning-compatible. + AnnotationVersion = "org.opencontainers.image.version" + + // AnnotationRevision is the annotation key for the source control revision identifier for the packaged software. + AnnotationRevision = "org.opencontainers.image.revision" + + // AnnotationVendor is the annotation key for the name of the distributing entity, organization or individual. + AnnotationVendor = "org.opencontainers.image.vendor" + + // AnnotationLicenses is the annotation key for the license(s) under which contained software is distributed as an SPDX License Expression. + AnnotationLicenses = "org.opencontainers.image.licenses" + + // AnnotationRefName is the annotation key for the name of the reference for a target. + // SHOULD only be considered valid when on descriptors on `index.json` within image layout. + AnnotationRefName = "org.opencontainers.image.ref.name" + + // AnnotationTitle is the annotation key for the human-readable title of the image. + AnnotationTitle = "org.opencontainers.image.title" + + // AnnotationDescription is the annotation key for the human-readable description of the software packaged in the image. + AnnotationDescription = "org.opencontainers.image.description" + + // AnnotationBaseImageDigest is the annotation key for the digest of the image's base image. + AnnotationBaseImageDigest = "org.opencontainers.image.base.digest" + + // AnnotationBaseImageName is the annotation key for the image reference of the image's base image. + AnnotationBaseImageName = "org.opencontainers.image.base.name" +) diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/v1/config.go b/vendor/github.com/opencontainers/image-spec/specs-go/v1/config.go new file mode 100644 index 0000000000..ffff4b6d18 --- /dev/null +++ b/vendor/github.com/opencontainers/image-spec/specs-go/v1/config.go @@ -0,0 +1,114 @@ +// Copyright 2016 The Linux Foundation +// +// 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. + +package v1 + +import ( + "time" + + digest "github.com/opencontainers/go-digest" +) + +// ImageConfig defines the execution parameters which should be used as a base when running a container using an image. +type ImageConfig struct { + // User defines the username or UID which the process in the container should run as. + User string `json:"User,omitempty"` + + // ExposedPorts a set of ports to expose from a container running this image. + ExposedPorts map[string]struct{} `json:"ExposedPorts,omitempty"` + + // Env is a list of environment variables to be used in a container. + Env []string `json:"Env,omitempty"` + + // Entrypoint defines a list of arguments to use as the command to execute when the container starts. + Entrypoint []string `json:"Entrypoint,omitempty"` + + // Cmd defines the default arguments to the entrypoint of the container. + Cmd []string `json:"Cmd,omitempty"` + + // Volumes is a set of directories describing where the process is likely write data specific to a container instance. + Volumes map[string]struct{} `json:"Volumes,omitempty"` + + // WorkingDir sets the current working directory of the entrypoint process in the container. + WorkingDir string `json:"WorkingDir,omitempty"` + + // Labels contains arbitrary metadata for the container. + Labels map[string]string `json:"Labels,omitempty"` + + // StopSignal contains the system call signal that will be sent to the container to exit. + StopSignal string `json:"StopSignal,omitempty"` +} + +// RootFS describes a layer content addresses +type RootFS struct { + // Type is the type of the rootfs. + Type string `json:"type"` + + // DiffIDs is an array of layer content hashes (DiffIDs), in order from bottom-most to top-most. + DiffIDs []digest.Digest `json:"diff_ids"` +} + +// History describes the history of a layer. +type History struct { + // Created is the combined date and time at which the layer was created, formatted as defined by RFC 3339, section 5.6. + Created *time.Time `json:"created,omitempty"` + + // CreatedBy is the command which created the layer. + CreatedBy string `json:"created_by,omitempty"` + + // Author is the author of the build point. + Author string `json:"author,omitempty"` + + // Comment is a custom message set when creating the layer. + Comment string `json:"comment,omitempty"` + + // EmptyLayer is used to mark if the history item created a filesystem diff. + EmptyLayer bool `json:"empty_layer,omitempty"` +} + +// Image is the JSON structure which describes some basic information about the image. +// This provides the `application/vnd.oci.image.config.v1+json` mediatype when marshalled to JSON. +type Image struct { + // Created is the combined date and time at which the image was created, formatted as defined by RFC 3339, section 5.6. + Created *time.Time `json:"created,omitempty"` + + // Author defines the name and/or email address of the person or entity which created and is responsible for maintaining the image. + Author string `json:"author,omitempty"` + + // Architecture is the CPU architecture which the binaries in this image are built to run on. + Architecture string `json:"architecture"` + + // Variant is the variant of the specified CPU architecture which image binaries are intended to run on. + Variant string `json:"variant,omitempty"` + + // OS is the name of the operating system which the image is built to run on. + OS string `json:"os"` + + // OSVersion is an optional field specifying the operating system + // version, for example on Windows `10.0.14393.1066`. + OSVersion string `json:"os.version,omitempty"` + + // OSFeatures is an optional field specifying an array of strings, + // each listing a required OS feature (for example on Windows `win32k`). + OSFeatures []string `json:"os.features,omitempty"` + + // Config defines the execution parameters which should be used as a base when running a container using the image. + Config ImageConfig `json:"config,omitempty"` + + // RootFS references the layer content addresses used by the image. + RootFS RootFS `json:"rootfs"` + + // History describes the history of each layer. + History []History `json:"history,omitempty"` +} diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/v1/descriptor.go b/vendor/github.com/opencontainers/image-spec/specs-go/v1/descriptor.go new file mode 100644 index 0000000000..6e442a0853 --- /dev/null +++ b/vendor/github.com/opencontainers/image-spec/specs-go/v1/descriptor.go @@ -0,0 +1,64 @@ +// Copyright 2016 The Linux Foundation +// +// 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. + +package v1 + +import digest "github.com/opencontainers/go-digest" + +// Descriptor describes the disposition of targeted content. +// This structure provides `application/vnd.oci.descriptor.v1+json` mediatype +// when marshalled to JSON. +type Descriptor struct { + // MediaType is the media type of the object this schema refers to. + MediaType string `json:"mediaType,omitempty"` + + // Digest is the digest of the targeted content. + Digest digest.Digest `json:"digest"` + + // Size specifies the size in bytes of the blob. + Size int64 `json:"size"` + + // URLs specifies a list of URLs from which this object MAY be downloaded + URLs []string `json:"urls,omitempty"` + + // Annotations contains arbitrary metadata relating to the targeted content. + Annotations map[string]string `json:"annotations,omitempty"` + + // Platform describes the platform which the image in the manifest runs on. + // + // This should only be used when referring to a manifest. + Platform *Platform `json:"platform,omitempty"` +} + +// Platform describes the platform which the image in the manifest runs on. +type Platform struct { + // Architecture field specifies the CPU architecture, for example + // `amd64` or `ppc64`. + Architecture string `json:"architecture"` + + // OS specifies the operating system, for example `linux` or `windows`. + OS string `json:"os"` + + // OSVersion is an optional field specifying the operating system + // version, for example on Windows `10.0.14393.1066`. + OSVersion string `json:"os.version,omitempty"` + + // OSFeatures is an optional field specifying an array of strings, + // each listing a required OS feature (for example on Windows `win32k`). + OSFeatures []string `json:"os.features,omitempty"` + + // Variant is an optional field specifying a variant of the CPU, for + // example `v7` to specify ARMv7 when architecture is `arm`. + Variant string `json:"variant,omitempty"` +} diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/v1/index.go b/vendor/github.com/opencontainers/image-spec/specs-go/v1/index.go new file mode 100644 index 0000000000..ed4a56e59e --- /dev/null +++ b/vendor/github.com/opencontainers/image-spec/specs-go/v1/index.go @@ -0,0 +1,32 @@ +// Copyright 2016 The Linux Foundation +// +// 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. + +package v1 + +import "github.com/opencontainers/image-spec/specs-go" + +// Index references manifests for various platforms. +// This structure provides `application/vnd.oci.image.index.v1+json` mediatype when marshalled to JSON. +type Index struct { + specs.Versioned + + // MediaType specifies the type of this document data structure e.g. `application/vnd.oci.image.index.v1+json` + MediaType string `json:"mediaType,omitempty"` + + // Manifests references platform specific manifests. + Manifests []Descriptor `json:"manifests"` + + // Annotations contains arbitrary metadata for the image index. + Annotations map[string]string `json:"annotations,omitempty"` +} diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/v1/layout.go b/vendor/github.com/opencontainers/image-spec/specs-go/v1/layout.go new file mode 100644 index 0000000000..fc79e9e0d1 --- /dev/null +++ b/vendor/github.com/opencontainers/image-spec/specs-go/v1/layout.go @@ -0,0 +1,28 @@ +// Copyright 2016 The Linux Foundation +// +// 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. + +package v1 + +const ( + // ImageLayoutFile is the file name of oci image layout file + ImageLayoutFile = "oci-layout" + // ImageLayoutVersion is the version of ImageLayout + ImageLayoutVersion = "1.0.0" +) + +// ImageLayout is the structure in the "oci-layout" file, found in the root +// of an OCI Image-layout directory. +type ImageLayout struct { + Version string `json:"imageLayoutVersion"` +} diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/v1/manifest.go b/vendor/github.com/opencontainers/image-spec/specs-go/v1/manifest.go new file mode 100644 index 0000000000..8212d520c0 --- /dev/null +++ b/vendor/github.com/opencontainers/image-spec/specs-go/v1/manifest.go @@ -0,0 +1,35 @@ +// Copyright 2016 The Linux Foundation +// +// 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. + +package v1 + +import "github.com/opencontainers/image-spec/specs-go" + +// Manifest provides `application/vnd.oci.image.manifest.v1+json` mediatype structure when marshalled to JSON. +type Manifest struct { + specs.Versioned + + // MediaType specifies the type of this document data structure e.g. `application/vnd.oci.image.manifest.v1+json` + MediaType string `json:"mediaType,omitempty"` + + // Config references a configuration object for a container, by digest. + // The referenced configuration object is a JSON blob that the runtime uses to set up the container. + Config Descriptor `json:"config"` + + // Layers is an indexed list of layers referenced by the manifest. + Layers []Descriptor `json:"layers"` + + // Annotations contains arbitrary metadata for the image manifest. + Annotations map[string]string `json:"annotations,omitempty"` +} diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/v1/mediatype.go b/vendor/github.com/opencontainers/image-spec/specs-go/v1/mediatype.go new file mode 100644 index 0000000000..4f35ac134f --- /dev/null +++ b/vendor/github.com/opencontainers/image-spec/specs-go/v1/mediatype.go @@ -0,0 +1,57 @@ +// Copyright 2016 The Linux Foundation +// +// 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. + +package v1 + +const ( + // MediaTypeDescriptor specifies the media type for a content descriptor. + MediaTypeDescriptor = "application/vnd.oci.descriptor.v1+json" + + // MediaTypeLayoutHeader specifies the media type for the oci-layout. + MediaTypeLayoutHeader = "application/vnd.oci.layout.header.v1+json" + + // MediaTypeImageManifest specifies the media type for an image manifest. + MediaTypeImageManifest = "application/vnd.oci.image.manifest.v1+json" + + // MediaTypeImageIndex specifies the media type for an image index. + MediaTypeImageIndex = "application/vnd.oci.image.index.v1+json" + + // MediaTypeImageLayer is the media type used for layers referenced by the manifest. + MediaTypeImageLayer = "application/vnd.oci.image.layer.v1.tar" + + // MediaTypeImageLayerGzip is the media type used for gzipped layers + // referenced by the manifest. + MediaTypeImageLayerGzip = "application/vnd.oci.image.layer.v1.tar+gzip" + + // MediaTypeImageLayerZstd is the media type used for zstd compressed + // layers referenced by the manifest. + MediaTypeImageLayerZstd = "application/vnd.oci.image.layer.v1.tar+zstd" + + // MediaTypeImageLayerNonDistributable is the media type for layers referenced by + // the manifest but with distribution restrictions. + MediaTypeImageLayerNonDistributable = "application/vnd.oci.image.layer.nondistributable.v1.tar" + + // MediaTypeImageLayerNonDistributableGzip is the media type for + // gzipped layers referenced by the manifest but with distribution + // restrictions. + MediaTypeImageLayerNonDistributableGzip = "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip" + + // MediaTypeImageLayerNonDistributableZstd is the media type for zstd + // compressed layers referenced by the manifest but with distribution + // restrictions. + MediaTypeImageLayerNonDistributableZstd = "application/vnd.oci.image.layer.nondistributable.v1.tar+zstd" + + // MediaTypeImageConfig specifies the media type for the image configuration. + MediaTypeImageConfig = "application/vnd.oci.image.config.v1+json" +) diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/version.go b/vendor/github.com/opencontainers/image-spec/specs-go/version.go new file mode 100644 index 0000000000..31f99cf645 --- /dev/null +++ b/vendor/github.com/opencontainers/image-spec/specs-go/version.go @@ -0,0 +1,32 @@ +// Copyright 2016 The Linux Foundation +// +// 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. + +package specs + +import "fmt" + +const ( + // VersionMajor is for an API incompatible changes + VersionMajor = 1 + // VersionMinor is for functionality in a backwards-compatible manner + VersionMinor = 0 + // VersionPatch is for backwards-compatible bug fixes + VersionPatch = 2 + + // VersionDev indicates development branch. Releases will be empty string. + VersionDev = "-dev" +) + +// Version is the specification version that the package types support. +var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev) diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/versioned.go b/vendor/github.com/opencontainers/image-spec/specs-go/versioned.go new file mode 100644 index 0000000000..58a1510f33 --- /dev/null +++ b/vendor/github.com/opencontainers/image-spec/specs-go/versioned.go @@ -0,0 +1,23 @@ +// Copyright 2016 The Linux Foundation +// +// 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. + +package specs + +// Versioned provides a struct with the manifest schemaVersion and mediaType. +// Incoming content with unknown schema version can be decoded against this +// struct to check the version. +type Versioned struct { + // SchemaVersion is the image manifest schema that this image follows + SchemaVersion int `json:"schemaVersion"` +} diff --git a/vendor/github.com/opencontainers/runc/LICENSE b/vendor/github.com/opencontainers/runc/LICENSE new file mode 100644 index 0000000000..27448585ad --- /dev/null +++ b/vendor/github.com/opencontainers/runc/LICENSE @@ -0,0 +1,191 @@ + + 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 + + Copyright 2014 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/opencontainers/runc/NOTICE b/vendor/github.com/opencontainers/runc/NOTICE new file mode 100644 index 0000000000..5c97abce4b --- /dev/null +++ b/vendor/github.com/opencontainers/runc/NOTICE @@ -0,0 +1,17 @@ +runc + +Copyright 2012-2015 Docker, Inc. + +This product includes software developed at Docker, Inc. (http://www.docker.com). + +The following is courtesy of our legal counsel: + + +Use and transfer of Docker may be subject to certain restrictions by the +United States and other governments. +It is your responsibility to ensure that your use and/or transfer does not +violate applicable laws. + +For more information, please see http://www.bis.doc.gov + +See also http://www.apache.org/dev/crypto.html and/or seek legal counsel. diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go new file mode 100644 index 0000000000..f95c1409fc --- /dev/null +++ b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go @@ -0,0 +1,157 @@ +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +// +build darwin dragonfly freebsd linux netbsd openbsd solaris + +package user + +import ( + "io" + "os" + "strconv" + + "golang.org/x/sys/unix" +) + +// Unix-specific path to the passwd and group formatted files. +const ( + unixPasswdPath = "/etc/passwd" + unixGroupPath = "/etc/group" +) + +// LookupUser looks up a user by their username in /etc/passwd. If the user +// cannot be found (or there is no /etc/passwd file on the filesystem), then +// LookupUser returns an error. +func LookupUser(username string) (User, error) { + return lookupUserFunc(func(u User) bool { + return u.Name == username + }) +} + +// LookupUid looks up a user by their user id in /etc/passwd. If the user cannot +// be found (or there is no /etc/passwd file on the filesystem), then LookupId +// returns an error. +func LookupUid(uid int) (User, error) { + return lookupUserFunc(func(u User) bool { + return u.Uid == uid + }) +} + +func lookupUserFunc(filter func(u User) bool) (User, error) { + // Get operating system-specific passwd reader-closer. + passwd, err := GetPasswd() + if err != nil { + return User{}, err + } + defer passwd.Close() + + // Get the users. + users, err := ParsePasswdFilter(passwd, filter) + if err != nil { + return User{}, err + } + + // No user entries found. + if len(users) == 0 { + return User{}, ErrNoPasswdEntries + } + + // Assume the first entry is the "correct" one. + return users[0], nil +} + +// LookupGroup looks up a group by its name in /etc/group. If the group cannot +// be found (or there is no /etc/group file on the filesystem), then LookupGroup +// returns an error. +func LookupGroup(groupname string) (Group, error) { + return lookupGroupFunc(func(g Group) bool { + return g.Name == groupname + }) +} + +// LookupGid looks up a group by its group id in /etc/group. If the group cannot +// be found (or there is no /etc/group file on the filesystem), then LookupGid +// returns an error. +func LookupGid(gid int) (Group, error) { + return lookupGroupFunc(func(g Group) bool { + return g.Gid == gid + }) +} + +func lookupGroupFunc(filter func(g Group) bool) (Group, error) { + // Get operating system-specific group reader-closer. + group, err := GetGroup() + if err != nil { + return Group{}, err + } + defer group.Close() + + // Get the users. + groups, err := ParseGroupFilter(group, filter) + if err != nil { + return Group{}, err + } + + // No user entries found. + if len(groups) == 0 { + return Group{}, ErrNoGroupEntries + } + + // Assume the first entry is the "correct" one. + return groups[0], nil +} + +func GetPasswdPath() (string, error) { + return unixPasswdPath, nil +} + +func GetPasswd() (io.ReadCloser, error) { + return os.Open(unixPasswdPath) +} + +func GetGroupPath() (string, error) { + return unixGroupPath, nil +} + +func GetGroup() (io.ReadCloser, error) { + return os.Open(unixGroupPath) +} + +// CurrentUser looks up the current user by their user id in /etc/passwd. If the +// user cannot be found (or there is no /etc/passwd file on the filesystem), +// then CurrentUser returns an error. +func CurrentUser() (User, error) { + return LookupUid(unix.Getuid()) +} + +// CurrentGroup looks up the current user's group by their primary group id's +// entry in /etc/passwd. If the group cannot be found (or there is no +// /etc/group file on the filesystem), then CurrentGroup returns an error. +func CurrentGroup() (Group, error) { + return LookupGid(unix.Getgid()) +} + +func currentUserSubIDs(fileName string) ([]SubID, error) { + u, err := CurrentUser() + if err != nil { + return nil, err + } + filter := func(entry SubID) bool { + return entry.Name == u.Name || entry.Name == strconv.Itoa(u.Uid) + } + return ParseSubIDFileFilter(fileName, filter) +} + +func CurrentUserSubUIDs() ([]SubID, error) { + return currentUserSubIDs("/etc/subuid") +} + +func CurrentUserSubGIDs() ([]SubID, error) { + return currentUserSubIDs("/etc/subgid") +} + +func CurrentProcessUIDMap() ([]IDMap, error) { + return ParseIDMapFile("/proc/self/uid_map") +} + +func CurrentProcessGIDMap() ([]IDMap, error) { + return ParseIDMapFile("/proc/self/gid_map") +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/user.go b/vendor/github.com/opencontainers/runc/libcontainer/user/user.go new file mode 100644 index 0000000000..2473c5eadd --- /dev/null +++ b/vendor/github.com/opencontainers/runc/libcontainer/user/user.go @@ -0,0 +1,605 @@ +package user + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "os" + "strconv" + "strings" +) + +const ( + minID = 0 + maxID = 1<<31 - 1 // for 32-bit systems compatibility +) + +var ( + // ErrNoPasswdEntries is returned if no matching entries were found in /etc/group. + ErrNoPasswdEntries = errors.New("no matching entries in passwd file") + // ErrNoGroupEntries is returned if no matching entries were found in /etc/passwd. + ErrNoGroupEntries = errors.New("no matching entries in group file") + // ErrRange is returned if a UID or GID is outside of the valid range. + ErrRange = fmt.Errorf("uids and gids must be in range %d-%d", minID, maxID) +) + +type User struct { + Name string + Pass string + Uid int + Gid int + Gecos string + Home string + Shell string +} + +type Group struct { + Name string + Pass string + Gid int + List []string +} + +// SubID represents an entry in /etc/sub{u,g}id +type SubID struct { + Name string + SubID int64 + Count int64 +} + +// IDMap represents an entry in /proc/PID/{u,g}id_map +type IDMap struct { + ID int64 + ParentID int64 + Count int64 +} + +func parseLine(line []byte, v ...interface{}) { + parseParts(bytes.Split(line, []byte(":")), v...) +} + +func parseParts(parts [][]byte, v ...interface{}) { + if len(parts) == 0 { + return + } + + for i, p := range parts { + // Ignore cases where we don't have enough fields to populate the arguments. + // Some configuration files like to misbehave. + if len(v) <= i { + break + } + + // Use the type of the argument to figure out how to parse it, scanf() style. + // This is legit. + switch e := v[i].(type) { + case *string: + *e = string(p) + case *int: + // "numbers", with conversion errors ignored because of some misbehaving configuration files. + *e, _ = strconv.Atoi(string(p)) + case *int64: + *e, _ = strconv.ParseInt(string(p), 10, 64) + case *[]string: + // Comma-separated lists. + if len(p) != 0 { + *e = strings.Split(string(p), ",") + } else { + *e = []string{} + } + default: + // Someone goof'd when writing code using this function. Scream so they can hear us. + panic(fmt.Sprintf("parseLine only accepts {*string, *int, *int64, *[]string} as arguments! %#v is not a pointer!", e)) + } + } +} + +func ParsePasswdFile(path string) ([]User, error) { + passwd, err := os.Open(path) + if err != nil { + return nil, err + } + defer passwd.Close() + return ParsePasswd(passwd) +} + +func ParsePasswd(passwd io.Reader) ([]User, error) { + return ParsePasswdFilter(passwd, nil) +} + +func ParsePasswdFileFilter(path string, filter func(User) bool) ([]User, error) { + passwd, err := os.Open(path) + if err != nil { + return nil, err + } + defer passwd.Close() + return ParsePasswdFilter(passwd, filter) +} + +func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) { + if r == nil { + return nil, errors.New("nil source for passwd-formatted data") + } + + var ( + s = bufio.NewScanner(r) + out = []User{} + ) + + for s.Scan() { + line := bytes.TrimSpace(s.Bytes()) + if len(line) == 0 { + continue + } + + // see: man 5 passwd + // name:password:UID:GID:GECOS:directory:shell + // Name:Pass:Uid:Gid:Gecos:Home:Shell + // root:x:0:0:root:/root:/bin/bash + // adm:x:3:4:adm:/var/adm:/bin/false + p := User{} + parseLine(line, &p.Name, &p.Pass, &p.Uid, &p.Gid, &p.Gecos, &p.Home, &p.Shell) + + if filter == nil || filter(p) { + out = append(out, p) + } + } + if err := s.Err(); err != nil { + return nil, err + } + + return out, nil +} + +func ParseGroupFile(path string) ([]Group, error) { + group, err := os.Open(path) + if err != nil { + return nil, err + } + + defer group.Close() + return ParseGroup(group) +} + +func ParseGroup(group io.Reader) ([]Group, error) { + return ParseGroupFilter(group, nil) +} + +func ParseGroupFileFilter(path string, filter func(Group) bool) ([]Group, error) { + group, err := os.Open(path) + if err != nil { + return nil, err + } + defer group.Close() + return ParseGroupFilter(group, filter) +} + +func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) { + if r == nil { + return nil, errors.New("nil source for group-formatted data") + } + rd := bufio.NewReader(r) + out := []Group{} + + // Read the file line-by-line. + for { + var ( + isPrefix bool + wholeLine []byte + err error + ) + + // Read the next line. We do so in chunks (as much as reader's + // buffer is able to keep), check if we read enough columns + // already on each step and store final result in wholeLine. + for { + var line []byte + line, isPrefix, err = rd.ReadLine() + + if err != nil { + // We should return no error if EOF is reached + // without a match. + if err == io.EOF { //nolint:errorlint // comparison with io.EOF is legit, https://github.com/polyfloyd/go-errorlint/pull/12 + err = nil + } + return out, err + } + + // Simple common case: line is short enough to fit in a + // single reader's buffer. + if !isPrefix && len(wholeLine) == 0 { + wholeLine = line + break + } + + wholeLine = append(wholeLine, line...) + + // Check if we read the whole line already. + if !isPrefix { + break + } + } + + // There's no spec for /etc/passwd or /etc/group, but we try to follow + // the same rules as the glibc parser, which allows comments and blank + // space at the beginning of a line. + wholeLine = bytes.TrimSpace(wholeLine) + if len(wholeLine) == 0 || wholeLine[0] == '#' { + continue + } + + // see: man 5 group + // group_name:password:GID:user_list + // Name:Pass:Gid:List + // root:x:0:root + // adm:x:4:root,adm,daemon + p := Group{} + parseLine(wholeLine, &p.Name, &p.Pass, &p.Gid, &p.List) + + if filter == nil || filter(p) { + out = append(out, p) + } + } +} + +type ExecUser struct { + Uid int + Gid int + Sgids []int + Home string +} + +// GetExecUserPath is a wrapper for GetExecUser. It reads data from each of the +// given file paths and uses that data as the arguments to GetExecUser. If the +// files cannot be opened for any reason, the error is ignored and a nil +// io.Reader is passed instead. +func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) { + var passwd, group io.Reader + + if passwdFile, err := os.Open(passwdPath); err == nil { + passwd = passwdFile + defer passwdFile.Close() + } + + if groupFile, err := os.Open(groupPath); err == nil { + group = groupFile + defer groupFile.Close() + } + + return GetExecUser(userSpec, defaults, passwd, group) +} + +// GetExecUser parses a user specification string (using the passwd and group +// readers as sources for /etc/passwd and /etc/group data, respectively). In +// the case of blank fields or missing data from the sources, the values in +// defaults is used. +// +// GetExecUser will return an error if a user or group literal could not be +// found in any entry in passwd and group respectively. +// +// Examples of valid user specifications are: +// * "" +// * "user" +// * "uid" +// * "user:group" +// * "uid:gid +// * "user:gid" +// * "uid:group" +// +// It should be noted that if you specify a numeric user or group id, they will +// not be evaluated as usernames (only the metadata will be filled). So attempting +// to parse a user with user.Name = "1337" will produce the user with a UID of +// 1337. +func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (*ExecUser, error) { + if defaults == nil { + defaults = new(ExecUser) + } + + // Copy over defaults. + user := &ExecUser{ + Uid: defaults.Uid, + Gid: defaults.Gid, + Sgids: defaults.Sgids, + Home: defaults.Home, + } + + // Sgids slice *cannot* be nil. + if user.Sgids == nil { + user.Sgids = []int{} + } + + // Allow for userArg to have either "user" syntax, or optionally "user:group" syntax + var userArg, groupArg string + parseLine([]byte(userSpec), &userArg, &groupArg) + + // Convert userArg and groupArg to be numeric, so we don't have to execute + // Atoi *twice* for each iteration over lines. + uidArg, uidErr := strconv.Atoi(userArg) + gidArg, gidErr := strconv.Atoi(groupArg) + + // Find the matching user. + users, err := ParsePasswdFilter(passwd, func(u User) bool { + if userArg == "" { + // Default to current state of the user. + return u.Uid == user.Uid + } + + if uidErr == nil { + // If the userArg is numeric, always treat it as a UID. + return uidArg == u.Uid + } + + return u.Name == userArg + }) + + // If we can't find the user, we have to bail. + if err != nil && passwd != nil { + if userArg == "" { + userArg = strconv.Itoa(user.Uid) + } + return nil, fmt.Errorf("unable to find user %s: %w", userArg, err) + } + + var matchedUserName string + if len(users) > 0 { + // First match wins, even if there's more than one matching entry. + matchedUserName = users[0].Name + user.Uid = users[0].Uid + user.Gid = users[0].Gid + user.Home = users[0].Home + } else if userArg != "" { + // If we can't find a user with the given username, the only other valid + // option is if it's a numeric username with no associated entry in passwd. + + if uidErr != nil { + // Not numeric. + return nil, fmt.Errorf("unable to find user %s: %w", userArg, ErrNoPasswdEntries) + } + user.Uid = uidArg + + // Must be inside valid uid range. + if user.Uid < minID || user.Uid > maxID { + return nil, ErrRange + } + + // Okay, so it's numeric. We can just roll with this. + } + + // On to the groups. If we matched a username, we need to do this because of + // the supplementary group IDs. + if groupArg != "" || matchedUserName != "" { + groups, err := ParseGroupFilter(group, func(g Group) bool { + // If the group argument isn't explicit, we'll just search for it. + if groupArg == "" { + // Check if user is a member of this group. + for _, u := range g.List { + if u == matchedUserName { + return true + } + } + return false + } + + if gidErr == nil { + // If the groupArg is numeric, always treat it as a GID. + return gidArg == g.Gid + } + + return g.Name == groupArg + }) + if err != nil && group != nil { + return nil, fmt.Errorf("unable to find groups for spec %v: %w", matchedUserName, err) + } + + // Only start modifying user.Gid if it is in explicit form. + if groupArg != "" { + if len(groups) > 0 { + // First match wins, even if there's more than one matching entry. + user.Gid = groups[0].Gid + } else { + // If we can't find a group with the given name, the only other valid + // option is if it's a numeric group name with no associated entry in group. + + if gidErr != nil { + // Not numeric. + return nil, fmt.Errorf("unable to find group %s: %w", groupArg, ErrNoGroupEntries) + } + user.Gid = gidArg + + // Must be inside valid gid range. + if user.Gid < minID || user.Gid > maxID { + return nil, ErrRange + } + + // Okay, so it's numeric. We can just roll with this. + } + } else if len(groups) > 0 { + // Supplementary group ids only make sense if in the implicit form. + user.Sgids = make([]int, len(groups)) + for i, group := range groups { + user.Sgids[i] = group.Gid + } + } + } + + return user, nil +} + +// GetAdditionalGroups looks up a list of groups by name or group id +// against the given /etc/group formatted data. If a group name cannot +// be found, an error will be returned. If a group id cannot be found, +// or the given group data is nil, the id will be returned as-is +// provided it is in the legal range. +func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, error) { + groups := []Group{} + if group != nil { + var err error + groups, err = ParseGroupFilter(group, func(g Group) bool { + for _, ag := range additionalGroups { + if g.Name == ag || strconv.Itoa(g.Gid) == ag { + return true + } + } + return false + }) + if err != nil { + return nil, fmt.Errorf("Unable to find additional groups %v: %w", additionalGroups, err) + } + } + + gidMap := make(map[int]struct{}) + for _, ag := range additionalGroups { + var found bool + for _, g := range groups { + // if we found a matched group either by name or gid, take the + // first matched as correct + if g.Name == ag || strconv.Itoa(g.Gid) == ag { + if _, ok := gidMap[g.Gid]; !ok { + gidMap[g.Gid] = struct{}{} + found = true + break + } + } + } + // we asked for a group but didn't find it. let's check to see + // if we wanted a numeric group + if !found { + gid, err := strconv.ParseInt(ag, 10, 64) + if err != nil { + // Not a numeric ID either. + return nil, fmt.Errorf("Unable to find group %s: %w", ag, ErrNoGroupEntries) + } + // Ensure gid is inside gid range. + if gid < minID || gid > maxID { + return nil, ErrRange + } + gidMap[int(gid)] = struct{}{} + } + } + gids := []int{} + for gid := range gidMap { + gids = append(gids, gid) + } + return gids, nil +} + +// GetAdditionalGroupsPath is a wrapper around GetAdditionalGroups +// that opens the groupPath given and gives it as an argument to +// GetAdditionalGroups. +func GetAdditionalGroupsPath(additionalGroups []string, groupPath string) ([]int, error) { + var group io.Reader + + if groupFile, err := os.Open(groupPath); err == nil { + group = groupFile + defer groupFile.Close() + } + return GetAdditionalGroups(additionalGroups, group) +} + +func ParseSubIDFile(path string) ([]SubID, error) { + subid, err := os.Open(path) + if err != nil { + return nil, err + } + defer subid.Close() + return ParseSubID(subid) +} + +func ParseSubID(subid io.Reader) ([]SubID, error) { + return ParseSubIDFilter(subid, nil) +} + +func ParseSubIDFileFilter(path string, filter func(SubID) bool) ([]SubID, error) { + subid, err := os.Open(path) + if err != nil { + return nil, err + } + defer subid.Close() + return ParseSubIDFilter(subid, filter) +} + +func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) { + if r == nil { + return nil, errors.New("nil source for subid-formatted data") + } + + var ( + s = bufio.NewScanner(r) + out = []SubID{} + ) + + for s.Scan() { + line := bytes.TrimSpace(s.Bytes()) + if len(line) == 0 { + continue + } + + // see: man 5 subuid + p := SubID{} + parseLine(line, &p.Name, &p.SubID, &p.Count) + + if filter == nil || filter(p) { + out = append(out, p) + } + } + if err := s.Err(); err != nil { + return nil, err + } + + return out, nil +} + +func ParseIDMapFile(path string) ([]IDMap, error) { + r, err := os.Open(path) + if err != nil { + return nil, err + } + defer r.Close() + return ParseIDMap(r) +} + +func ParseIDMap(r io.Reader) ([]IDMap, error) { + return ParseIDMapFilter(r, nil) +} + +func ParseIDMapFileFilter(path string, filter func(IDMap) bool) ([]IDMap, error) { + r, err := os.Open(path) + if err != nil { + return nil, err + } + defer r.Close() + return ParseIDMapFilter(r, filter) +} + +func ParseIDMapFilter(r io.Reader, filter func(IDMap) bool) ([]IDMap, error) { + if r == nil { + return nil, errors.New("nil source for idmap-formatted data") + } + + var ( + s = bufio.NewScanner(r) + out = []IDMap{} + ) + + for s.Scan() { + line := bytes.TrimSpace(s.Bytes()) + if len(line) == 0 { + continue + } + + // see: man 7 user_namespaces + p := IDMap{} + parseParts(bytes.Fields(line), &p.ID, &p.ParentID, &p.Count) + + if filter == nil || filter(p) { + out = append(out, p) + } + } + if err := s.Err(); err != nil { + return nil, err + } + + return out, nil +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/user_fuzzer.go b/vendor/github.com/opencontainers/runc/libcontainer/user/user_fuzzer.go new file mode 100644 index 0000000000..e018eae614 --- /dev/null +++ b/vendor/github.com/opencontainers/runc/libcontainer/user/user_fuzzer.go @@ -0,0 +1,43 @@ +//go:build gofuzz +// +build gofuzz + +package user + +import ( + "io" + "strings" +) + +func IsDivisbleBy(n int, divisibleby int) bool { + return (n % divisibleby) == 0 +} + +func FuzzUser(data []byte) int { + if len(data) == 0 { + return -1 + } + if !IsDivisbleBy(len(data), 5) { + return -1 + } + + var divided [][]byte + + chunkSize := len(data) / 5 + + for i := 0; i < len(data); i += chunkSize { + end := i + chunkSize + + divided = append(divided, data[i:end]) + } + + _, _ = ParsePasswdFilter(strings.NewReader(string(divided[0])), nil) + + var passwd, group io.Reader + + group = strings.NewReader(string(divided[1])) + _, _ = GetAdditionalGroups([]string{string(divided[2])}, group) + + passwd = strings.NewReader(string(divided[3])) + _, _ = GetExecUser(string(divided[4]), nil, passwd, group) + return 1 +} diff --git a/vendor/github.com/opencontainers/runtime-spec/LICENSE b/vendor/github.com/opencontainers/runtime-spec/LICENSE new file mode 100644 index 0000000000..bdc403653e --- /dev/null +++ b/vendor/github.com/opencontainers/runtime-spec/LICENSE @@ -0,0 +1,191 @@ + + 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 + + Copyright 2015 The Linux Foundation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go new file mode 100644 index 0000000000..6a7a91e559 --- /dev/null +++ b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go @@ -0,0 +1,700 @@ +package specs + +import "os" + +// Spec is the base configuration for the container. +type Spec struct { + // Version of the Open Container Initiative Runtime Specification with which the bundle complies. + Version string `json:"ociVersion"` + // Process configures the container process. + Process *Process `json:"process,omitempty"` + // Root configures the container's root filesystem. + Root *Root `json:"root,omitempty"` + // Hostname configures the container's hostname. + Hostname string `json:"hostname,omitempty"` + // Mounts configures additional mounts (on top of Root). + Mounts []Mount `json:"mounts,omitempty"` + // Hooks configures callbacks for container lifecycle events. + Hooks *Hooks `json:"hooks,omitempty" platform:"linux,solaris"` + // Annotations contains arbitrary metadata for the container. + Annotations map[string]string `json:"annotations,omitempty"` + + // Linux is platform-specific configuration for Linux based containers. + Linux *Linux `json:"linux,omitempty" platform:"linux"` + // Solaris is platform-specific configuration for Solaris based containers. + Solaris *Solaris `json:"solaris,omitempty" platform:"solaris"` + // Windows is platform-specific configuration for Windows based containers. + Windows *Windows `json:"windows,omitempty" platform:"windows"` + // VM specifies configuration for virtual-machine-based containers. + VM *VM `json:"vm,omitempty" platform:"vm"` +} + +// Process contains information to start a specific application inside the container. +type Process struct { + // Terminal creates an interactive terminal for the container. + Terminal bool `json:"terminal,omitempty"` + // ConsoleSize specifies the size of the console. + ConsoleSize *Box `json:"consoleSize,omitempty"` + // User specifies user information for the process. + User User `json:"user"` + // Args specifies the binary and arguments for the application to execute. + Args []string `json:"args,omitempty"` + // CommandLine specifies the full command line for the application to execute on Windows. + CommandLine string `json:"commandLine,omitempty" platform:"windows"` + // Env populates the process environment for the process. + Env []string `json:"env,omitempty"` + // Cwd is the current working directory for the process and must be + // relative to the container's root. + Cwd string `json:"cwd"` + // Capabilities are Linux capabilities that are kept for the process. + Capabilities *LinuxCapabilities `json:"capabilities,omitempty" platform:"linux"` + // Rlimits specifies rlimit options to apply to the process. + Rlimits []POSIXRlimit `json:"rlimits,omitempty" platform:"linux,solaris"` + // NoNewPrivileges controls whether additional privileges could be gained by processes in the container. + NoNewPrivileges bool `json:"noNewPrivileges,omitempty" platform:"linux"` + // ApparmorProfile specifies the apparmor profile for the container. + ApparmorProfile string `json:"apparmorProfile,omitempty" platform:"linux"` + // Specify an oom_score_adj for the container. + OOMScoreAdj *int `json:"oomScoreAdj,omitempty" platform:"linux"` + // SelinuxLabel specifies the selinux context that the container process is run as. + SelinuxLabel string `json:"selinuxLabel,omitempty" platform:"linux"` +} + +// LinuxCapabilities specifies the list of allowed capabilities that are kept for a process. +// http://man7.org/linux/man-pages/man7/capabilities.7.html +type LinuxCapabilities struct { + // Bounding is the set of capabilities checked by the kernel. + Bounding []string `json:"bounding,omitempty" platform:"linux"` + // Effective is the set of capabilities checked by the kernel. + Effective []string `json:"effective,omitempty" platform:"linux"` + // Inheritable is the capabilities preserved across execve. + Inheritable []string `json:"inheritable,omitempty" platform:"linux"` + // Permitted is the limiting superset for effective capabilities. + Permitted []string `json:"permitted,omitempty" platform:"linux"` + // Ambient is the ambient set of capabilities that are kept. + Ambient []string `json:"ambient,omitempty" platform:"linux"` +} + +// Box specifies dimensions of a rectangle. Used for specifying the size of a console. +type Box struct { + // Height is the vertical dimension of a box. + Height uint `json:"height"` + // Width is the horizontal dimension of a box. + Width uint `json:"width"` +} + +// User specifies specific user (and group) information for the container process. +type User struct { + // UID is the user id. + UID uint32 `json:"uid" platform:"linux,solaris"` + // GID is the group id. + GID uint32 `json:"gid" platform:"linux,solaris"` + // Umask is the umask for the init process. + Umask *uint32 `json:"umask,omitempty" platform:"linux,solaris"` + // AdditionalGids are additional group ids set for the container's process. + AdditionalGids []uint32 `json:"additionalGids,omitempty" platform:"linux,solaris"` + // Username is the user name. + Username string `json:"username,omitempty" platform:"windows"` +} + +// Root contains information about the container's root filesystem on the host. +type Root struct { + // Path is the absolute path to the container's root filesystem. + Path string `json:"path"` + // Readonly makes the root filesystem for the container readonly before the process is executed. + Readonly bool `json:"readonly,omitempty"` +} + +// Mount specifies a mount for a container. +type Mount struct { + // Destination is the absolute path where the mount will be placed in the container. + Destination string `json:"destination"` + // Type specifies the mount kind. + Type string `json:"type,omitempty" platform:"linux,solaris"` + // Source specifies the source path of the mount. + Source string `json:"source,omitempty"` + // Options are fstab style mount options. + Options []string `json:"options,omitempty"` +} + +// Hook specifies a command that is run at a particular event in the lifecycle of a container +type Hook struct { + Path string `json:"path"` + Args []string `json:"args,omitempty"` + Env []string `json:"env,omitempty"` + Timeout *int `json:"timeout,omitempty"` +} + +// Hooks specifies a command that is run in the container at a particular event in the lifecycle of a container +// Hooks for container setup and teardown +type Hooks struct { + // Prestart is Deprecated. Prestart is a list of hooks to be run before the container process is executed. + // It is called in the Runtime Namespace + Prestart []Hook `json:"prestart,omitempty"` + // CreateRuntime is a list of hooks to be run after the container has been created but before pivot_root or any equivalent operation has been called + // It is called in the Runtime Namespace + CreateRuntime []Hook `json:"createRuntime,omitempty"` + // CreateContainer is a list of hooks to be run after the container has been created but before pivot_root or any equivalent operation has been called + // It is called in the Container Namespace + CreateContainer []Hook `json:"createContainer,omitempty"` + // StartContainer is a list of hooks to be run after the start operation is called but before the container process is started + // It is called in the Container Namespace + StartContainer []Hook `json:"startContainer,omitempty"` + // Poststart is a list of hooks to be run after the container process is started. + // It is called in the Runtime Namespace + Poststart []Hook `json:"poststart,omitempty"` + // Poststop is a list of hooks to be run after the container process exits. + // It is called in the Runtime Namespace + Poststop []Hook `json:"poststop,omitempty"` +} + +// Linux contains platform-specific configuration for Linux based containers. +type Linux struct { + // UIDMapping specifies user mappings for supporting user namespaces. + UIDMappings []LinuxIDMapping `json:"uidMappings,omitempty"` + // GIDMapping specifies group mappings for supporting user namespaces. + GIDMappings []LinuxIDMapping `json:"gidMappings,omitempty"` + // Sysctl are a set of key value pairs that are set for the container on start + Sysctl map[string]string `json:"sysctl,omitempty"` + // Resources contain cgroup information for handling resource constraints + // for the container + Resources *LinuxResources `json:"resources,omitempty"` + // CgroupsPath specifies the path to cgroups that are created and/or joined by the container. + // The path is expected to be relative to the cgroups mountpoint. + // If resources are specified, the cgroups at CgroupsPath will be updated based on resources. + CgroupsPath string `json:"cgroupsPath,omitempty"` + // Namespaces contains the namespaces that are created and/or joined by the container + Namespaces []LinuxNamespace `json:"namespaces,omitempty"` + // Devices are a list of device nodes that are created for the container + Devices []LinuxDevice `json:"devices,omitempty"` + // Seccomp specifies the seccomp security settings for the container. + Seccomp *LinuxSeccomp `json:"seccomp,omitempty"` + // RootfsPropagation is the rootfs mount propagation mode for the container. + RootfsPropagation string `json:"rootfsPropagation,omitempty"` + // MaskedPaths masks over the provided paths inside the container. + MaskedPaths []string `json:"maskedPaths,omitempty"` + // ReadonlyPaths sets the provided paths as RO inside the container. + ReadonlyPaths []string `json:"readonlyPaths,omitempty"` + // MountLabel specifies the selinux context for the mounts in the container. + MountLabel string `json:"mountLabel,omitempty"` + // IntelRdt contains Intel Resource Director Technology (RDT) information for + // handling resource constraints (e.g., L3 cache, memory bandwidth) for the container + IntelRdt *LinuxIntelRdt `json:"intelRdt,omitempty"` + // Personality contains configuration for the Linux personality syscall + Personality *LinuxPersonality `json:"personality,omitempty"` +} + +// LinuxNamespace is the configuration for a Linux namespace +type LinuxNamespace struct { + // Type is the type of namespace + Type LinuxNamespaceType `json:"type"` + // Path is a path to an existing namespace persisted on disk that can be joined + // and is of the same type + Path string `json:"path,omitempty"` +} + +// LinuxNamespaceType is one of the Linux namespaces +type LinuxNamespaceType string + +const ( + // PIDNamespace for isolating process IDs + PIDNamespace LinuxNamespaceType = "pid" + // NetworkNamespace for isolating network devices, stacks, ports, etc + NetworkNamespace LinuxNamespaceType = "network" + // MountNamespace for isolating mount points + MountNamespace LinuxNamespaceType = "mount" + // IPCNamespace for isolating System V IPC, POSIX message queues + IPCNamespace LinuxNamespaceType = "ipc" + // UTSNamespace for isolating hostname and NIS domain name + UTSNamespace LinuxNamespaceType = "uts" + // UserNamespace for isolating user and group IDs + UserNamespace LinuxNamespaceType = "user" + // CgroupNamespace for isolating cgroup hierarchies + CgroupNamespace LinuxNamespaceType = "cgroup" +) + +// LinuxIDMapping specifies UID/GID mappings +type LinuxIDMapping struct { + // ContainerID is the starting UID/GID in the container + ContainerID uint32 `json:"containerID"` + // HostID is the starting UID/GID on the host to be mapped to 'ContainerID' + HostID uint32 `json:"hostID"` + // Size is the number of IDs to be mapped + Size uint32 `json:"size"` +} + +// POSIXRlimit type and restrictions +type POSIXRlimit struct { + // Type of the rlimit to set + Type string `json:"type"` + // Hard is the hard limit for the specified type + Hard uint64 `json:"hard"` + // Soft is the soft limit for the specified type + Soft uint64 `json:"soft"` +} + +// LinuxHugepageLimit structure corresponds to limiting kernel hugepages +type LinuxHugepageLimit struct { + // Pagesize is the hugepage size + // Format: "B' (e.g. 64KB, 2MB, 1GB, etc.) + Pagesize string `json:"pageSize"` + // Limit is the limit of "hugepagesize" hugetlb usage + Limit uint64 `json:"limit"` +} + +// LinuxInterfacePriority for network interfaces +type LinuxInterfacePriority struct { + // Name is the name of the network interface + Name string `json:"name"` + // Priority for the interface + Priority uint32 `json:"priority"` +} + +// linuxBlockIODevice holds major:minor format supported in blkio cgroup +type linuxBlockIODevice struct { + // Major is the device's major number. + Major int64 `json:"major"` + // Minor is the device's minor number. + Minor int64 `json:"minor"` +} + +// LinuxWeightDevice struct holds a `major:minor weight` pair for weightDevice +type LinuxWeightDevice struct { + linuxBlockIODevice + // Weight is the bandwidth rate for the device. + Weight *uint16 `json:"weight,omitempty"` + // LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, CFQ scheduler only + LeafWeight *uint16 `json:"leafWeight,omitempty"` +} + +// LinuxThrottleDevice struct holds a `major:minor rate_per_second` pair +type LinuxThrottleDevice struct { + linuxBlockIODevice + // Rate is the IO rate limit per cgroup per device + Rate uint64 `json:"rate"` +} + +// LinuxBlockIO for Linux cgroup 'blkio' resource management +type LinuxBlockIO struct { + // Specifies per cgroup weight + Weight *uint16 `json:"weight,omitempty"` + // Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, CFQ scheduler only + LeafWeight *uint16 `json:"leafWeight,omitempty"` + // Weight per cgroup per device, can override BlkioWeight + WeightDevice []LinuxWeightDevice `json:"weightDevice,omitempty"` + // IO read rate limit per cgroup per device, bytes per second + ThrottleReadBpsDevice []LinuxThrottleDevice `json:"throttleReadBpsDevice,omitempty"` + // IO write rate limit per cgroup per device, bytes per second + ThrottleWriteBpsDevice []LinuxThrottleDevice `json:"throttleWriteBpsDevice,omitempty"` + // IO read rate limit per cgroup per device, IO per second + ThrottleReadIOPSDevice []LinuxThrottleDevice `json:"throttleReadIOPSDevice,omitempty"` + // IO write rate limit per cgroup per device, IO per second + ThrottleWriteIOPSDevice []LinuxThrottleDevice `json:"throttleWriteIOPSDevice,omitempty"` +} + +// LinuxMemory for Linux cgroup 'memory' resource management +type LinuxMemory struct { + // Memory limit (in bytes). + Limit *int64 `json:"limit,omitempty"` + // Memory reservation or soft_limit (in bytes). + Reservation *int64 `json:"reservation,omitempty"` + // Total memory limit (memory + swap). + Swap *int64 `json:"swap,omitempty"` + // Kernel memory limit (in bytes). + Kernel *int64 `json:"kernel,omitempty"` + // Kernel memory limit for tcp (in bytes) + KernelTCP *int64 `json:"kernelTCP,omitempty"` + // How aggressive the kernel will swap memory pages. + Swappiness *uint64 `json:"swappiness,omitempty"` + // DisableOOMKiller disables the OOM killer for out of memory conditions + DisableOOMKiller *bool `json:"disableOOMKiller,omitempty"` + // Enables hierarchical memory accounting + UseHierarchy *bool `json:"useHierarchy,omitempty"` +} + +// LinuxCPU for Linux cgroup 'cpu' resource management +type LinuxCPU struct { + // CPU shares (relative weight (ratio) vs. other cgroups with cpu shares). + Shares *uint64 `json:"shares,omitempty"` + // CPU hardcap limit (in usecs). Allowed cpu time in a given period. + Quota *int64 `json:"quota,omitempty"` + // CPU period to be used for hardcapping (in usecs). + Period *uint64 `json:"period,omitempty"` + // How much time realtime scheduling may use (in usecs). + RealtimeRuntime *int64 `json:"realtimeRuntime,omitempty"` + // CPU period to be used for realtime scheduling (in usecs). + RealtimePeriod *uint64 `json:"realtimePeriod,omitempty"` + // CPUs to use within the cpuset. Default is to use any CPU available. + Cpus string `json:"cpus,omitempty"` + // List of memory nodes in the cpuset. Default is to use any available memory node. + Mems string `json:"mems,omitempty"` +} + +// LinuxPids for Linux cgroup 'pids' resource management (Linux 4.3) +type LinuxPids struct { + // Maximum number of PIDs. Default is "no limit". + Limit int64 `json:"limit"` +} + +// LinuxNetwork identification and priority configuration +type LinuxNetwork struct { + // Set class identifier for container's network packets + ClassID *uint32 `json:"classID,omitempty"` + // Set priority of network traffic for container + Priorities []LinuxInterfacePriority `json:"priorities,omitempty"` +} + +// LinuxRdma for Linux cgroup 'rdma' resource management (Linux 4.11) +type LinuxRdma struct { + // Maximum number of HCA handles that can be opened. Default is "no limit". + HcaHandles *uint32 `json:"hcaHandles,omitempty"` + // Maximum number of HCA objects that can be created. Default is "no limit". + HcaObjects *uint32 `json:"hcaObjects,omitempty"` +} + +// LinuxResources has container runtime resource constraints +type LinuxResources struct { + // Devices configures the device allowlist. + Devices []LinuxDeviceCgroup `json:"devices,omitempty"` + // Memory restriction configuration + Memory *LinuxMemory `json:"memory,omitempty"` + // CPU resource restriction configuration + CPU *LinuxCPU `json:"cpu,omitempty"` + // Task resource restriction configuration. + Pids *LinuxPids `json:"pids,omitempty"` + // BlockIO restriction configuration + BlockIO *LinuxBlockIO `json:"blockIO,omitempty"` + // Hugetlb limit (in bytes) + HugepageLimits []LinuxHugepageLimit `json:"hugepageLimits,omitempty"` + // Network restriction configuration + Network *LinuxNetwork `json:"network,omitempty"` + // Rdma resource restriction configuration. + // Limits are a set of key value pairs that define RDMA resource limits, + // where the key is device name and value is resource limits. + Rdma map[string]LinuxRdma `json:"rdma,omitempty"` + // Unified resources. + Unified map[string]string `json:"unified,omitempty"` +} + +// LinuxDevice represents the mknod information for a Linux special device file +type LinuxDevice struct { + // Path to the device. + Path string `json:"path"` + // Device type, block, char, etc. + Type string `json:"type"` + // Major is the device's major number. + Major int64 `json:"major"` + // Minor is the device's minor number. + Minor int64 `json:"minor"` + // FileMode permission bits for the device. + FileMode *os.FileMode `json:"fileMode,omitempty"` + // UID of the device. + UID *uint32 `json:"uid,omitempty"` + // Gid of the device. + GID *uint32 `json:"gid,omitempty"` +} + +// LinuxDeviceCgroup represents a device rule for the devices specified to +// the device controller +type LinuxDeviceCgroup struct { + // Allow or deny + Allow bool `json:"allow"` + // Device type, block, char, etc. + Type string `json:"type,omitempty"` + // Major is the device's major number. + Major *int64 `json:"major,omitempty"` + // Minor is the device's minor number. + Minor *int64 `json:"minor,omitempty"` + // Cgroup access permissions format, rwm. + Access string `json:"access,omitempty"` +} + +// LinuxPersonalityDomain refers to a personality domain. +type LinuxPersonalityDomain string + +// LinuxPersonalityFlag refers to an additional personality flag. None are currently defined. +type LinuxPersonalityFlag string + +// Define domain and flags for Personality +const ( + // PerLinux is the standard Linux personality + PerLinux LinuxPersonalityDomain = "LINUX" + // PerLinux32 sets personality to 32 bit + PerLinux32 LinuxPersonalityDomain = "LINUX32" +) + +// LinuxPersonality represents the Linux personality syscall input +type LinuxPersonality struct { + // Domain for the personality + Domain LinuxPersonalityDomain `json:"domain"` + // Additional flags + Flags []LinuxPersonalityFlag `json:"flags,omitempty"` +} + +// Solaris contains platform-specific configuration for Solaris application containers. +type Solaris struct { + // SMF FMRI which should go "online" before we start the container process. + Milestone string `json:"milestone,omitempty"` + // Maximum set of privileges any process in this container can obtain. + LimitPriv string `json:"limitpriv,omitempty"` + // The maximum amount of shared memory allowed for this container. + MaxShmMemory string `json:"maxShmMemory,omitempty"` + // Specification for automatic creation of network resources for this container. + Anet []SolarisAnet `json:"anet,omitempty"` + // Set limit on the amount of CPU time that can be used by container. + CappedCPU *SolarisCappedCPU `json:"cappedCPU,omitempty"` + // The physical and swap caps on the memory that can be used by this container. + CappedMemory *SolarisCappedMemory `json:"cappedMemory,omitempty"` +} + +// SolarisCappedCPU allows users to set limit on the amount of CPU time that can be used by container. +type SolarisCappedCPU struct { + Ncpus string `json:"ncpus,omitempty"` +} + +// SolarisCappedMemory allows users to set the physical and swap caps on the memory that can be used by this container. +type SolarisCappedMemory struct { + Physical string `json:"physical,omitempty"` + Swap string `json:"swap,omitempty"` +} + +// SolarisAnet provides the specification for automatic creation of network resources for this container. +type SolarisAnet struct { + // Specify a name for the automatically created VNIC datalink. + Linkname string `json:"linkname,omitempty"` + // Specify the link over which the VNIC will be created. + Lowerlink string `json:"lowerLink,omitempty"` + // The set of IP addresses that the container can use. + Allowedaddr string `json:"allowedAddress,omitempty"` + // Specifies whether allowedAddress limitation is to be applied to the VNIC. + Configallowedaddr string `json:"configureAllowedAddress,omitempty"` + // The value of the optional default router. + Defrouter string `json:"defrouter,omitempty"` + // Enable one or more types of link protection. + Linkprotection string `json:"linkProtection,omitempty"` + // Set the VNIC's macAddress + Macaddress string `json:"macAddress,omitempty"` +} + +// Windows defines the runtime configuration for Windows based containers, including Hyper-V containers. +type Windows struct { + // LayerFolders contains a list of absolute paths to directories containing image layers. + LayerFolders []string `json:"layerFolders"` + // Devices are the list of devices to be mapped into the container. + Devices []WindowsDevice `json:"devices,omitempty"` + // Resources contains information for handling resource constraints for the container. + Resources *WindowsResources `json:"resources,omitempty"` + // CredentialSpec contains a JSON object describing a group Managed Service Account (gMSA) specification. + CredentialSpec interface{} `json:"credentialSpec,omitempty"` + // Servicing indicates if the container is being started in a mode to apply a Windows Update servicing operation. + Servicing bool `json:"servicing,omitempty"` + // IgnoreFlushesDuringBoot indicates if the container is being started in a mode where disk writes are not flushed during its boot process. + IgnoreFlushesDuringBoot bool `json:"ignoreFlushesDuringBoot,omitempty"` + // HyperV contains information for running a container with Hyper-V isolation. + HyperV *WindowsHyperV `json:"hyperv,omitempty"` + // Network restriction configuration. + Network *WindowsNetwork `json:"network,omitempty"` +} + +// WindowsDevice represents information about a host device to be mapped into the container. +type WindowsDevice struct { + // Device identifier: interface class GUID, etc. + ID string `json:"id"` + // Device identifier type: "class", etc. + IDType string `json:"idType"` +} + +// WindowsResources has container runtime resource constraints for containers running on Windows. +type WindowsResources struct { + // Memory restriction configuration. + Memory *WindowsMemoryResources `json:"memory,omitempty"` + // CPU resource restriction configuration. + CPU *WindowsCPUResources `json:"cpu,omitempty"` + // Storage restriction configuration. + Storage *WindowsStorageResources `json:"storage,omitempty"` +} + +// WindowsMemoryResources contains memory resource management settings. +type WindowsMemoryResources struct { + // Memory limit in bytes. + Limit *uint64 `json:"limit,omitempty"` +} + +// WindowsCPUResources contains CPU resource management settings. +type WindowsCPUResources struct { + // Number of CPUs available to the container. + Count *uint64 `json:"count,omitempty"` + // CPU shares (relative weight to other containers with cpu shares). + Shares *uint16 `json:"shares,omitempty"` + // Specifies the portion of processor cycles that this container can use as a percentage times 100. + Maximum *uint16 `json:"maximum,omitempty"` +} + +// WindowsStorageResources contains storage resource management settings. +type WindowsStorageResources struct { + // Specifies maximum Iops for the system drive. + Iops *uint64 `json:"iops,omitempty"` + // Specifies maximum bytes per second for the system drive. + Bps *uint64 `json:"bps,omitempty"` + // Sandbox size specifies the minimum size of the system drive in bytes. + SandboxSize *uint64 `json:"sandboxSize,omitempty"` +} + +// WindowsNetwork contains network settings for Windows containers. +type WindowsNetwork struct { + // List of HNS endpoints that the container should connect to. + EndpointList []string `json:"endpointList,omitempty"` + // Specifies if unqualified DNS name resolution is allowed. + AllowUnqualifiedDNSQuery bool `json:"allowUnqualifiedDNSQuery,omitempty"` + // Comma separated list of DNS suffixes to use for name resolution. + DNSSearchList []string `json:"DNSSearchList,omitempty"` + // Name (ID) of the container that we will share with the network stack. + NetworkSharedContainerName string `json:"networkSharedContainerName,omitempty"` + // name (ID) of the network namespace that will be used for the container. + NetworkNamespace string `json:"networkNamespace,omitempty"` +} + +// WindowsHyperV contains information for configuring a container to run with Hyper-V isolation. +type WindowsHyperV struct { + // UtilityVMPath is an optional path to the image used for the Utility VM. + UtilityVMPath string `json:"utilityVMPath,omitempty"` +} + +// VM contains information for virtual-machine-based containers. +type VM struct { + // Hypervisor specifies hypervisor-related configuration for virtual-machine-based containers. + Hypervisor VMHypervisor `json:"hypervisor,omitempty"` + // Kernel specifies kernel-related configuration for virtual-machine-based containers. + Kernel VMKernel `json:"kernel"` + // Image specifies guest image related configuration for virtual-machine-based containers. + Image VMImage `json:"image,omitempty"` +} + +// VMHypervisor contains information about the hypervisor to use for a virtual machine. +type VMHypervisor struct { + // Path is the host path to the hypervisor used to manage the virtual machine. + Path string `json:"path"` + // Parameters specifies parameters to pass to the hypervisor. + Parameters []string `json:"parameters,omitempty"` +} + +// VMKernel contains information about the kernel to use for a virtual machine. +type VMKernel struct { + // Path is the host path to the kernel used to boot the virtual machine. + Path string `json:"path"` + // Parameters specifies parameters to pass to the kernel. + Parameters []string `json:"parameters,omitempty"` + // InitRD is the host path to an initial ramdisk to be used by the kernel. + InitRD string `json:"initrd,omitempty"` +} + +// VMImage contains information about the virtual machine root image. +type VMImage struct { + // Path is the host path to the root image that the VM kernel would boot into. + Path string `json:"path"` + // Format is the root image format type (e.g. "qcow2", "raw", "vhd", etc). + Format string `json:"format"` +} + +// LinuxSeccomp represents syscall restrictions +type LinuxSeccomp struct { + DefaultAction LinuxSeccompAction `json:"defaultAction"` + DefaultErrnoRet *uint `json:"defaultErrnoRet,omitempty"` + Architectures []Arch `json:"architectures,omitempty"` + Flags []LinuxSeccompFlag `json:"flags,omitempty"` + ListenerPath string `json:"listenerPath,omitempty"` + ListenerMetadata string `json:"listenerMetadata,omitempty"` + Syscalls []LinuxSyscall `json:"syscalls,omitempty"` +} + +// Arch used for additional architectures +type Arch string + +// LinuxSeccompFlag is a flag to pass to seccomp(2). +type LinuxSeccompFlag string + +// Additional architectures permitted to be used for system calls +// By default only the native architecture of the kernel is permitted +const ( + ArchX86 Arch = "SCMP_ARCH_X86" + ArchX86_64 Arch = "SCMP_ARCH_X86_64" + ArchX32 Arch = "SCMP_ARCH_X32" + ArchARM Arch = "SCMP_ARCH_ARM" + ArchAARCH64 Arch = "SCMP_ARCH_AARCH64" + ArchMIPS Arch = "SCMP_ARCH_MIPS" + ArchMIPS64 Arch = "SCMP_ARCH_MIPS64" + ArchMIPS64N32 Arch = "SCMP_ARCH_MIPS64N32" + ArchMIPSEL Arch = "SCMP_ARCH_MIPSEL" + ArchMIPSEL64 Arch = "SCMP_ARCH_MIPSEL64" + ArchMIPSEL64N32 Arch = "SCMP_ARCH_MIPSEL64N32" + ArchPPC Arch = "SCMP_ARCH_PPC" + ArchPPC64 Arch = "SCMP_ARCH_PPC64" + ArchPPC64LE Arch = "SCMP_ARCH_PPC64LE" + ArchS390 Arch = "SCMP_ARCH_S390" + ArchS390X Arch = "SCMP_ARCH_S390X" + ArchPARISC Arch = "SCMP_ARCH_PARISC" + ArchPARISC64 Arch = "SCMP_ARCH_PARISC64" + ArchRISCV64 Arch = "SCMP_ARCH_RISCV64" +) + +// LinuxSeccompAction taken upon Seccomp rule match +type LinuxSeccompAction string + +// Define actions for Seccomp rules +const ( + ActKill LinuxSeccompAction = "SCMP_ACT_KILL" + ActKillProcess LinuxSeccompAction = "SCMP_ACT_KILL_PROCESS" + ActKillThread LinuxSeccompAction = "SCMP_ACT_KILL_THREAD" + ActTrap LinuxSeccompAction = "SCMP_ACT_TRAP" + ActErrno LinuxSeccompAction = "SCMP_ACT_ERRNO" + ActTrace LinuxSeccompAction = "SCMP_ACT_TRACE" + ActAllow LinuxSeccompAction = "SCMP_ACT_ALLOW" + ActLog LinuxSeccompAction = "SCMP_ACT_LOG" + ActNotify LinuxSeccompAction = "SCMP_ACT_NOTIFY" +) + +// LinuxSeccompOperator used to match syscall arguments in Seccomp +type LinuxSeccompOperator string + +// Define operators for syscall arguments in Seccomp +const ( + OpNotEqual LinuxSeccompOperator = "SCMP_CMP_NE" + OpLessThan LinuxSeccompOperator = "SCMP_CMP_LT" + OpLessEqual LinuxSeccompOperator = "SCMP_CMP_LE" + OpEqualTo LinuxSeccompOperator = "SCMP_CMP_EQ" + OpGreaterEqual LinuxSeccompOperator = "SCMP_CMP_GE" + OpGreaterThan LinuxSeccompOperator = "SCMP_CMP_GT" + OpMaskedEqual LinuxSeccompOperator = "SCMP_CMP_MASKED_EQ" +) + +// LinuxSeccompArg used for matching specific syscall arguments in Seccomp +type LinuxSeccompArg struct { + Index uint `json:"index"` + Value uint64 `json:"value"` + ValueTwo uint64 `json:"valueTwo,omitempty"` + Op LinuxSeccompOperator `json:"op"` +} + +// LinuxSyscall is used to match a syscall in Seccomp +type LinuxSyscall struct { + Names []string `json:"names"` + Action LinuxSeccompAction `json:"action"` + ErrnoRet *uint `json:"errnoRet,omitempty"` + Args []LinuxSeccompArg `json:"args,omitempty"` +} + +// LinuxIntelRdt has container runtime resource constraints for Intel RDT +// CAT and MBA features which introduced in Linux 4.10 and 4.12 kernel +type LinuxIntelRdt struct { + // The identity for RDT Class of Service + ClosID string `json:"closID,omitempty"` + // The schema for L3 cache id and capacity bitmask (CBM) + // Format: "L3:=;=;..." + L3CacheSchema string `json:"l3CacheSchema,omitempty"` + + // The schema of memory bandwidth per L3 cache id + // Format: "MB:=bandwidth0;=bandwidth1;..." + // The unit of memory bandwidth is specified in "percentages" by + // default, and in "MBps" if MBA Software Controller is enabled. + MemBwSchema string `json:"memBwSchema,omitempty"` +} diff --git a/vendor/github.com/opencontainers/runtime-spec/specs-go/state.go b/vendor/github.com/opencontainers/runtime-spec/specs-go/state.go new file mode 100644 index 0000000000..7c010d4fe7 --- /dev/null +++ b/vendor/github.com/opencontainers/runtime-spec/specs-go/state.go @@ -0,0 +1,56 @@ +package specs + +// ContainerState represents the state of a container. +type ContainerState string + +const ( + // StateCreating indicates that the container is being created + StateCreating ContainerState = "creating" + + // StateCreated indicates that the runtime has finished the create operation + StateCreated ContainerState = "created" + + // StateRunning indicates that the container process has executed the + // user-specified program but has not exited + StateRunning ContainerState = "running" + + // StateStopped indicates that the container process has exited + StateStopped ContainerState = "stopped" +) + +// State holds information about the runtime state of the container. +type State struct { + // Version is the version of the specification that is supported. + Version string `json:"ociVersion"` + // ID is the container ID + ID string `json:"id"` + // Status is the runtime status of the container. + Status ContainerState `json:"status"` + // Pid is the process ID for the container process. + Pid int `json:"pid,omitempty"` + // Bundle is the path to the container's bundle directory. + Bundle string `json:"bundle"` + // Annotations are key values associated with the container. + Annotations map[string]string `json:"annotations,omitempty"` +} + +const ( + // SeccompFdName is the name of the seccomp notify file descriptor. + SeccompFdName string = "seccompFd" +) + +// ContainerProcessState holds information about the state of a container process. +type ContainerProcessState struct { + // Version is the version of the specification that is supported. + Version string `json:"ociVersion"` + // Fds is a string array containing the names of the file descriptors passed. + // The index of the name in this array corresponds to index of the file + // descriptor in the `SCM_RIGHTS` array. + Fds []string `json:"fds"` + // Pid is the process ID as seen by the runtime. + Pid int `json:"pid"` + // Opaque metadata. + Metadata string `json:"metadata,omitempty"` + // State of the container. + State State `json:"state"` +} diff --git a/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go b/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go new file mode 100644 index 0000000000..596af0c2fd --- /dev/null +++ b/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go @@ -0,0 +1,18 @@ +package specs + +import "fmt" + +const ( + // VersionMajor is for an API incompatible changes + VersionMajor = 1 + // VersionMinor is for functionality in a backwards-compatible manner + VersionMinor = 0 + // VersionPatch is for backwards-compatible bug fixes + VersionPatch = 2 + + // VersionDev indicates development branch. Releases will be empty string. + VersionDev = "-dev" +) + +// Version is the specification version that the package types support. +var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev) diff --git a/vendor/github.com/opencontainers/selinux/LICENSE b/vendor/github.com/opencontainers/selinux/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/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/vendor/github.com/opencontainers/selinux/go-selinux/doc.go b/vendor/github.com/opencontainers/selinux/go-selinux/doc.go new file mode 100644 index 0000000000..0ac7d819e6 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/go-selinux/doc.go @@ -0,0 +1,14 @@ +/* +Package selinux provides a high-level interface for interacting with selinux. + +Usage: + + import "github.com/opencontainers/selinux/go-selinux" + + // Ensure that selinux is enforcing mode. + if selinux.EnforceMode() != selinux.Enforcing { + selinux.SetEnforceMode(selinux.Enforcing) + } + +*/ +package selinux diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/label/label.go b/vendor/github.com/opencontainers/selinux/go-selinux/label/label.go new file mode 100644 index 0000000000..fea096c180 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/go-selinux/label/label.go @@ -0,0 +1,97 @@ +package label + +import ( + "fmt" + + "github.com/opencontainers/selinux/go-selinux" +) + +// Deprecated: use selinux.ROFileLabel +var ROMountLabel = selinux.ROFileLabel + +// SetProcessLabel takes a process label and tells the kernel to assign the +// label to the next program executed by the current process. +// Deprecated: use selinux.SetExecLabel +var SetProcessLabel = selinux.SetExecLabel + +// ProcessLabel returns the process label that the kernel will assign +// to the next program executed by the current process. If "" is returned +// this indicates that the default labeling will happen for the process. +// Deprecated: use selinux.ExecLabel +var ProcessLabel = selinux.ExecLabel + +// SetSocketLabel takes a process label and tells the kernel to assign the +// label to the next socket that gets created +// Deprecated: use selinux.SetSocketLabel +var SetSocketLabel = selinux.SetSocketLabel + +// SocketLabel retrieves the current default socket label setting +// Deprecated: use selinux.SocketLabel +var SocketLabel = selinux.SocketLabel + +// SetKeyLabel takes a process label and tells the kernel to assign the +// label to the next kernel keyring that gets created +// Deprecated: use selinux.SetKeyLabel +var SetKeyLabel = selinux.SetKeyLabel + +// KeyLabel retrieves the current default kernel keyring label setting +// Deprecated: use selinux.KeyLabel +var KeyLabel = selinux.KeyLabel + +// FileLabel returns the label for specified path +// Deprecated: use selinux.FileLabel +var FileLabel = selinux.FileLabel + +// PidLabel will return the label of the process running with the specified pid +// Deprecated: use selinux.PidLabel +var PidLabel = selinux.PidLabel + +// Init initialises the labeling system +func Init() { + _ = selinux.GetEnabled() +} + +// ClearLabels will clear all reserved labels +// Deprecated: use selinux.ClearLabels +var ClearLabels = selinux.ClearLabels + +// ReserveLabel will record the fact that the MCS label has already been used. +// This will prevent InitLabels from using the MCS label in a newly created +// container +// Deprecated: use selinux.ReserveLabel +func ReserveLabel(label string) error { + selinux.ReserveLabel(label) + return nil +} + +// ReleaseLabel will remove the reservation of the MCS label. +// This will allow InitLabels to use the MCS label in a newly created +// containers +// Deprecated: use selinux.ReleaseLabel +func ReleaseLabel(label string) error { + selinux.ReleaseLabel(label) + return nil +} + +// DupSecOpt takes a process label and returns security options that +// can be used to set duplicate labels on future container processes +// Deprecated: use selinux.DupSecOpt +var DupSecOpt = selinux.DupSecOpt + +// FormatMountLabel returns a string to be used by the mount command. +// The format of this string will be used to alter the labeling of the mountpoint. +// The string returned is suitable to be used as the options field of the mount command. +// If you need to have additional mount point options, you can pass them in as +// the first parameter. Second parameter is the label that you wish to apply +// to all content in the mount point. +func FormatMountLabel(src, mountLabel string) string { + if mountLabel != "" { + switch src { + case "": + src = fmt.Sprintf("context=%q", mountLabel) + default: + src = fmt.Sprintf("%s,context=%q", src, mountLabel) + } + } + return src +} diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_linux.go b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_linux.go new file mode 100644 index 0000000000..12de0ae5d6 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_linux.go @@ -0,0 +1,196 @@ +package label + +import ( + "errors" + "fmt" + "os" + "os/user" + "strings" + + "github.com/opencontainers/selinux/go-selinux" +) + +// Valid Label Options +var validOptions = map[string]bool{ + "disable": true, + "type": true, + "filetype": true, + "user": true, + "role": true, + "level": true, +} + +var ErrIncompatibleLabel = errors.New("Bad SELinux option z and Z can not be used together") + +// InitLabels returns the process label and file labels to be used within +// the container. A list of options can be passed into this function to alter +// the labels. The labels returned will include a random MCS String, that is +// guaranteed to be unique. +// If the disabled flag is passed in, the process label will not be set, but the mount label will be set +// to the container_file label with the maximum category. This label is not usable by any confined label. +func InitLabels(options []string) (plabel string, mlabel string, retErr error) { + if !selinux.GetEnabled() { + return "", "", nil + } + processLabel, mountLabel := selinux.ContainerLabels() + if processLabel != "" { + defer func() { + if retErr != nil { + selinux.ReleaseLabel(mountLabel) + } + }() + pcon, err := selinux.NewContext(processLabel) + if err != nil { + return "", "", err + } + mcsLevel := pcon["level"] + mcon, err := selinux.NewContext(mountLabel) + if err != nil { + return "", "", err + } + for _, opt := range options { + if opt == "disable" { + selinux.ReleaseLabel(mountLabel) + return "", selinux.PrivContainerMountLabel(), nil + } + if i := strings.Index(opt, ":"); i == -1 { + return "", "", fmt.Errorf("Bad label option %q, valid options 'disable' or \n'user, role, level, type, filetype' followed by ':' and a value", opt) + } + con := strings.SplitN(opt, ":", 2) + if !validOptions[con[0]] { + return "", "", fmt.Errorf("Bad label option %q, valid options 'disable, user, role, level, type, filetype'", con[0]) + } + if con[0] == "filetype" { + mcon["type"] = con[1] + continue + } + pcon[con[0]] = con[1] + if con[0] == "level" || con[0] == "user" { + mcon[con[0]] = con[1] + } + } + if pcon.Get() != processLabel { + if pcon["level"] != mcsLevel { + selinux.ReleaseLabel(processLabel) + } + processLabel = pcon.Get() + selinux.ReserveLabel(processLabel) + } + mountLabel = mcon.Get() + } + return processLabel, mountLabel, nil +} + +// Deprecated: The GenLabels function is only to be used during the transition +// to the official API. Use InitLabels(strings.Fields(options)) instead. +func GenLabels(options string) (string, string, error) { + return InitLabels(strings.Fields(options)) +} + +// SetFileLabel modifies the "path" label to the specified file label +func SetFileLabel(path string, fileLabel string) error { + if !selinux.GetEnabled() || fileLabel == "" { + return nil + } + return selinux.SetFileLabel(path, fileLabel) +} + +// SetFileCreateLabel tells the kernel the label for all files to be created +func SetFileCreateLabel(fileLabel string) error { + if !selinux.GetEnabled() { + return nil + } + return selinux.SetFSCreateLabel(fileLabel) +} + +// Relabel changes the label of path and all the entries beneath the path. +// It changes the MCS label to s0 if shared is true. +// This will allow all containers to share the content. +// +// The path itself is guaranteed to be relabeled last. +func Relabel(path string, fileLabel string, shared bool) error { + if !selinux.GetEnabled() || fileLabel == "" { + return nil + } + + exclude_paths := map[string]bool{ + "/": true, + "/bin": true, + "/boot": true, + "/dev": true, + "/etc": true, + "/etc/passwd": true, + "/etc/pki": true, + "/etc/shadow": true, + "/home": true, + "/lib": true, + "/lib64": true, + "/media": true, + "/opt": true, + "/proc": true, + "/root": true, + "/run": true, + "/sbin": true, + "/srv": true, + "/sys": true, + "/tmp": true, + "/usr": true, + "/var": true, + "/var/lib": true, + "/var/log": true, + } + + if home := os.Getenv("HOME"); home != "" { + exclude_paths[home] = true + } + + if sudoUser := os.Getenv("SUDO_USER"); sudoUser != "" { + if usr, err := user.Lookup(sudoUser); err == nil { + exclude_paths[usr.HomeDir] = true + } + } + + if path != "/" { + path = strings.TrimSuffix(path, "/") + } + if exclude_paths[path] { + return fmt.Errorf("SELinux relabeling of %s is not allowed", path) + } + + if shared { + c, err := selinux.NewContext(fileLabel) + if err != nil { + return err + } + + c["level"] = "s0" + fileLabel = c.Get() + } + if err := selinux.Chcon(path, fileLabel, true); err != nil { + return err + } + return nil +} + +// DisableSecOpt returns a security opt that can disable labeling +// support for future container processes +// Deprecated: use selinux.DisableSecOpt +var DisableSecOpt = selinux.DisableSecOpt + +// Validate checks that the label does not include unexpected options +func Validate(label string) error { + if strings.Contains(label, "z") && strings.Contains(label, "Z") { + return ErrIncompatibleLabel + } + return nil +} + +// RelabelNeeded checks whether the user requested a relabel +func RelabelNeeded(label string) bool { + return strings.Contains(label, "z") || strings.Contains(label, "Z") +} + +// IsShared checks that the label includes a "shared" mark +func IsShared(label string) bool { + return strings.Contains(label, "z") +} diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_stub.go b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_stub.go new file mode 100644 index 0000000000..02d206239c --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_stub.go @@ -0,0 +1,49 @@ +// +build !linux + +package label + +// InitLabels returns the process label and file labels to be used within +// the container. A list of options can be passed into this function to alter +// the labels. +func InitLabels(options []string) (string, string, error) { + return "", "", nil +} + +// Deprecated: The GenLabels function is only to be used during the transition +// to the official API. Use InitLabels(strings.Fields(options)) instead. +func GenLabels(options string) (string, string, error) { + return "", "", nil +} + +func SetFileLabel(path string, fileLabel string) error { + return nil +} + +func SetFileCreateLabel(fileLabel string) error { + return nil +} + +func Relabel(path string, fileLabel string, shared bool) error { + return nil +} + +// DisableSecOpt returns a security opt that can disable labeling +// support for future container processes +func DisableSecOpt() []string { + return nil +} + +// Validate checks that the label does not include unexpected options +func Validate(label string) error { + return nil +} + +// RelabelNeeded checks whether the user requested a relabel +func RelabelNeeded(label string) bool { + return false +} + +// IsShared checks that the label includes a "shared" mark +func IsShared(label string) bool { + return false +} diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/rchcon.go b/vendor/github.com/opencontainers/selinux/go-selinux/rchcon.go new file mode 100644 index 0000000000..897ecbac41 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/go-selinux/rchcon.go @@ -0,0 +1,22 @@ +// +build linux,go1.16 + +package selinux + +import ( + "errors" + "io/fs" + "os" + + "github.com/opencontainers/selinux/pkg/pwalkdir" +) + +func rchcon(fpath, label string) error { + return pwalkdir.Walk(fpath, func(p string, _ fs.DirEntry, _ error) error { + e := setFileLabel(p, label) + // Walk a file tree can race with removal, so ignore ENOENT. + if errors.Is(e, os.ErrNotExist) { + return nil + } + return e + }) +} diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/rchcon_go115.go b/vendor/github.com/opencontainers/selinux/go-selinux/rchcon_go115.go new file mode 100644 index 0000000000..2c8b033ce0 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/go-selinux/rchcon_go115.go @@ -0,0 +1,21 @@ +// +build linux,!go1.16 + +package selinux + +import ( + "errors" + "os" + + "github.com/opencontainers/selinux/pkg/pwalk" +) + +func rchcon(fpath, label string) error { + return pwalk.Walk(fpath, func(p string, _ os.FileInfo, _ error) error { + e := setFileLabel(p, label) + // Walk a file tree can race with removal, so ignore ENOENT. + if errors.Is(e, os.ErrNotExist) { + return nil + } + return e + }) +} diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go new file mode 100644 index 0000000000..5a59d151f6 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go @@ -0,0 +1,304 @@ +package selinux + +import ( + "errors" +) + +const ( + // Enforcing constant indicate SELinux is in enforcing mode + Enforcing = 1 + // Permissive constant to indicate SELinux is in permissive mode + Permissive = 0 + // Disabled constant to indicate SELinux is disabled + Disabled = -1 + // maxCategory is the maximum number of categories used within containers + maxCategory = 1024 + // DefaultCategoryRange is the upper bound on the category range + DefaultCategoryRange = uint32(maxCategory) +) + +var ( + // ErrMCSAlreadyExists is returned when trying to allocate a duplicate MCS. + ErrMCSAlreadyExists = errors.New("MCS label already exists") + // ErrEmptyPath is returned when an empty path has been specified. + ErrEmptyPath = errors.New("empty path") + + // InvalidLabel is returned when an invalid label is specified. + InvalidLabel = errors.New("Invalid Label") + + // ErrIncomparable is returned two levels are not comparable + ErrIncomparable = errors.New("incomparable levels") + // ErrLevelSyntax is returned when a sensitivity or category do not have correct syntax in a level + ErrLevelSyntax = errors.New("invalid level syntax") + + // ErrContextMissing is returned if a requested context is not found in a file. + ErrContextMissing = errors.New("context does not have a match") + // ErrVerifierNil is returned when a context verifier function is nil. + ErrVerifierNil = errors.New("verifier function is nil") + + // CategoryRange allows the upper bound on the category range to be adjusted + CategoryRange = DefaultCategoryRange + + privContainerMountLabel string +) + +// Context is a representation of the SELinux label broken into 4 parts +type Context map[string]string + +// SetDisabled disables SELinux support for the package +func SetDisabled() { + setDisabled() +} + +// GetEnabled returns whether SELinux is currently enabled. +func GetEnabled() bool { + return getEnabled() +} + +// ClassIndex returns the int index for an object class in the loaded policy, +// or -1 and an error +func ClassIndex(class string) (int, error) { + return classIndex(class) +} + +// SetFileLabel sets the SELinux label for this path, following symlinks, +// or returns an error. +func SetFileLabel(fpath string, label string) error { + return setFileLabel(fpath, label) +} + +// LsetFileLabel sets the SELinux label for this path, not following symlinks, +// or returns an error. +func LsetFileLabel(fpath string, label string) error { + return lSetFileLabel(fpath, label) +} + +// FileLabel returns the SELinux label for this path, following symlinks, +// or returns an error. +func FileLabel(fpath string) (string, error) { + return fileLabel(fpath) +} + +// LfileLabel returns the SELinux label for this path, not following symlinks, +// or returns an error. +func LfileLabel(fpath string) (string, error) { + return lFileLabel(fpath) +} + +// SetFSCreateLabel tells the kernel what label to use for all file system objects +// created by this task. +// Set the label to an empty string to return to the default label. Calls to SetFSCreateLabel +// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until file system +// objects created by this task are finished to guarantee another goroutine does not migrate +// to the current thread before execution is complete. +func SetFSCreateLabel(label string) error { + return setFSCreateLabel(label) +} + +// FSCreateLabel returns the default label the kernel which the kernel is using +// for file system objects created by this task. "" indicates default. +func FSCreateLabel() (string, error) { + return fsCreateLabel() +} + +// CurrentLabel returns the SELinux label of the current process thread, or an error. +func CurrentLabel() (string, error) { + return currentLabel() +} + +// PidLabel returns the SELinux label of the given pid, or an error. +func PidLabel(pid int) (string, error) { + return pidLabel(pid) +} + +// ExecLabel returns the SELinux label that the kernel will use for any programs +// that are executed by the current process thread, or an error. +func ExecLabel() (string, error) { + return execLabel() +} + +// CanonicalizeContext takes a context string and writes it to the kernel +// the function then returns the context that the kernel will use. Use this +// function to check if two contexts are equivalent +func CanonicalizeContext(val string) (string, error) { + return canonicalizeContext(val) +} + +// ComputeCreateContext requests the type transition from source to target for +// class from the kernel. +func ComputeCreateContext(source string, target string, class string) (string, error) { + return computeCreateContext(source, target, class) +} + +// CalculateGlbLub computes the glb (greatest lower bound) and lub (least upper bound) +// of a source and target range. +// The glblub is calculated as the greater of the low sensitivities and +// the lower of the high sensitivities and the and of each category bitset. +func CalculateGlbLub(sourceRange, targetRange string) (string, error) { + return calculateGlbLub(sourceRange, targetRange) +} + +// SetExecLabel sets the SELinux label that the kernel will use for any programs +// that are executed by the current process thread, or an error. Calls to SetExecLabel +// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until execution +// of the program is finished to guarantee another goroutine does not migrate to the current +// thread before execution is complete. +func SetExecLabel(label string) error { + return setExecLabel(label) +} + +// SetTaskLabel sets the SELinux label for the current thread, or an error. +// This requires the dyntransition permission. Calls to SetTaskLabel should +// be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() to guarantee +// the current thread does not run in a new mislabeled thread. +func SetTaskLabel(label string) error { + return setTaskLabel(label) +} + +// SetSocketLabel takes a process label and tells the kernel to assign the +// label to the next socket that gets created. Calls to SetSocketLabel +// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until +// the the socket is created to guarantee another goroutine does not migrate +// to the current thread before execution is complete. +func SetSocketLabel(label string) error { + return setSocketLabel(label) +} + +// SocketLabel retrieves the current socket label setting +func SocketLabel() (string, error) { + return socketLabel() +} + +// PeerLabel retrieves the label of the client on the other side of a socket +func PeerLabel(fd uintptr) (string, error) { + return peerLabel(fd) +} + +// SetKeyLabel takes a process label and tells the kernel to assign the +// label to the next kernel keyring that gets created. Calls to SetKeyLabel +// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until +// the kernel keyring is created to guarantee another goroutine does not migrate +// to the current thread before execution is complete. +func SetKeyLabel(label string) error { + return setKeyLabel(label) +} + +// KeyLabel retrieves the current kernel keyring label setting +func KeyLabel() (string, error) { + return keyLabel() +} + +// Get returns the Context as a string +func (c Context) Get() string { + return c.get() +} + +// NewContext creates a new Context struct from the specified label +func NewContext(label string) (Context, error) { + return newContext(label) +} + +// ClearLabels clears all reserved labels +func ClearLabels() { + clearLabels() +} + +// ReserveLabel reserves the MLS/MCS level component of the specified label +func ReserveLabel(label string) { + reserveLabel(label) +} + +// EnforceMode returns the current SELinux mode Enforcing, Permissive, Disabled +func EnforceMode() int { + return enforceMode() +} + +// SetEnforceMode sets the current SELinux mode Enforcing, Permissive. +// Disabled is not valid, since this needs to be set at boot time. +func SetEnforceMode(mode int) error { + return setEnforceMode(mode) +} + +// DefaultEnforceMode returns the systems default SELinux mode Enforcing, +// Permissive or Disabled. Note this is is just the default at boot time. +// EnforceMode tells you the systems current mode. +func DefaultEnforceMode() int { + return defaultEnforceMode() +} + +// ReleaseLabel un-reserves the MLS/MCS Level field of the specified label, +// allowing it to be used by another process. +func ReleaseLabel(label string) { + releaseLabel(label) +} + +// ROFileLabel returns the specified SELinux readonly file label +func ROFileLabel() string { + return roFileLabel() +} + +// KVMContainerLabels returns the default processLabel and mountLabel to be used +// for kvm containers by the calling process. +func KVMContainerLabels() (string, string) { + return kvmContainerLabels() +} + +// InitContainerLabels returns the default processLabel and file labels to be +// used for containers running an init system like systemd by the calling process. +func InitContainerLabels() (string, string) { + return initContainerLabels() +} + +// ContainerLabels returns an allocated processLabel and fileLabel to be used for +// container labeling by the calling process. +func ContainerLabels() (processLabel string, fileLabel string) { + return containerLabels() +} + +// SecurityCheckContext validates that the SELinux label is understood by the kernel +func SecurityCheckContext(val string) error { + return securityCheckContext(val) +} + +// CopyLevel returns a label with the MLS/MCS level from src label replaced on +// the dest label. +func CopyLevel(src, dest string) (string, error) { + return copyLevel(src, dest) +} + +// Chcon changes the fpath file object to the SELinux label label. +// If fpath is a directory and recurse is true, then Chcon walks the +// directory tree setting the label. +// +// The fpath itself is guaranteed to be relabeled last. +func Chcon(fpath string, label string, recurse bool) error { + return chcon(fpath, label, recurse) +} + +// DupSecOpt takes an SELinux process label and returns security options that +// can be used to set the SELinux Type and Level for future container processes. +func DupSecOpt(src string) ([]string, error) { + return dupSecOpt(src) +} + +// DisableSecOpt returns a security opt that can be used to disable SELinux +// labeling support for future container processes. +func DisableSecOpt() []string { + return disableSecOpt() +} + +// GetDefaultContextWithLevel gets a single context for the specified SELinux user +// identity that is reachable from the specified scon context. The context is based +// on the per-user /etc/selinux/{SELINUXTYPE}/contexts/users/ if it exists, +// and falls back to the global /etc/selinux/{SELINUXTYPE}/contexts/default_contexts +// file. +func GetDefaultContextWithLevel(user, level, scon string) (string, error) { + return getDefaultContextWithLevel(user, level, scon) +} + +// PrivContainerMountLabel returns mount label for privileged containers +func PrivContainerMountLabel() string { + // Make sure label is initialized. + _ = label("") + return privContainerMountLabel +} diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go new file mode 100644 index 0000000000..ee602ab96d --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go @@ -0,0 +1,1262 @@ +package selinux + +import ( + "bufio" + "bytes" + "crypto/rand" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "math/big" + "os" + "path" + "path/filepath" + "strconv" + "strings" + "sync" + + "golang.org/x/sys/unix" +) + +const ( + minSensLen = 2 + contextFile = "/usr/share/containers/selinux/contexts" + selinuxDir = "/etc/selinux/" + selinuxUsersDir = "contexts/users" + defaultContexts = "contexts/default_contexts" + selinuxConfig = selinuxDir + "config" + selinuxfsMount = "/sys/fs/selinux" + selinuxTypeTag = "SELINUXTYPE" + selinuxTag = "SELINUX" + xattrNameSelinux = "security.selinux" +) + +type selinuxState struct { + enabledSet bool + enabled bool + selinuxfsOnce sync.Once + selinuxfs string + mcsList map[string]bool + sync.Mutex +} + +type level struct { + sens uint + cats *big.Int +} + +type mlsRange struct { + low *level + high *level +} + +type defaultSECtx struct { + user, level, scon string + userRdr, defaultRdr io.Reader + + verifier func(string) error +} + +type levelItem byte + +const ( + sensitivity levelItem = 's' + category levelItem = 'c' +) + +var ( + readOnlyFileLabel string + state = selinuxState{ + mcsList: make(map[string]bool), + } + + // for attrPath() + attrPathOnce sync.Once + haveThreadSelf bool + + // for policyRoot() + policyRootOnce sync.Once + policyRootVal string + + // for label() + loadLabelsOnce sync.Once + labels map[string]string +) + +func policyRoot() string { + policyRootOnce.Do(func() { + policyRootVal = filepath.Join(selinuxDir, readConfig(selinuxTypeTag)) + }) + + return policyRootVal +} + +func (s *selinuxState) setEnable(enabled bool) bool { + s.Lock() + defer s.Unlock() + s.enabledSet = true + s.enabled = enabled + return s.enabled +} + +func (s *selinuxState) getEnabled() bool { + s.Lock() + enabled := s.enabled + enabledSet := s.enabledSet + s.Unlock() + if enabledSet { + return enabled + } + + enabled = false + if fs := getSelinuxMountPoint(); fs != "" { + if con, _ := CurrentLabel(); con != "kernel" { + enabled = true + } + } + return s.setEnable(enabled) +} + +// setDisabled disables SELinux support for the package +func setDisabled() { + state.setEnable(false) +} + +func verifySELinuxfsMount(mnt string) bool { + var buf unix.Statfs_t + for { + err := unix.Statfs(mnt, &buf) + if err == nil { + break + } + if err == unix.EAGAIN || err == unix.EINTR { //nolint:errorlint // unix errors are bare + continue + } + return false + } + + if uint32(buf.Type) != uint32(unix.SELINUX_MAGIC) { + return false + } + if (buf.Flags & unix.ST_RDONLY) != 0 { + return false + } + + return true +} + +func findSELinuxfs() string { + // fast path: check the default mount first + if verifySELinuxfsMount(selinuxfsMount) { + return selinuxfsMount + } + + // check if selinuxfs is available before going the slow path + fs, err := ioutil.ReadFile("/proc/filesystems") + if err != nil { + return "" + } + if !bytes.Contains(fs, []byte("\tselinuxfs\n")) { + return "" + } + + // slow path: try to find among the mounts + f, err := os.Open("/proc/self/mountinfo") + if err != nil { + return "" + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for { + mnt := findSELinuxfsMount(scanner) + if mnt == "" { // error or not found + return "" + } + if verifySELinuxfsMount(mnt) { + return mnt + } + } +} + +// findSELinuxfsMount returns a next selinuxfs mount point found, +// if there is one, or an empty string in case of EOF or error. +func findSELinuxfsMount(s *bufio.Scanner) string { + for s.Scan() { + txt := s.Bytes() + // The first field after - is fs type. + // Safe as spaces in mountpoints are encoded as \040 + if !bytes.Contains(txt, []byte(" - selinuxfs ")) { + continue + } + const mPos = 5 // mount point is 5th field + fields := bytes.SplitN(txt, []byte(" "), mPos+1) + if len(fields) < mPos+1 { + continue + } + return string(fields[mPos-1]) + } + + return "" +} + +func (s *selinuxState) getSELinuxfs() string { + s.selinuxfsOnce.Do(func() { + s.selinuxfs = findSELinuxfs() + }) + + return s.selinuxfs +} + +// getSelinuxMountPoint returns the path to the mountpoint of an selinuxfs +// filesystem or an empty string if no mountpoint is found. Selinuxfs is +// a proc-like pseudo-filesystem that exposes the SELinux policy API to +// processes. The existence of an selinuxfs mount is used to determine +// whether SELinux is currently enabled or not. +func getSelinuxMountPoint() string { + return state.getSELinuxfs() +} + +// getEnabled returns whether SELinux is currently enabled. +func getEnabled() bool { + return state.getEnabled() +} + +func readConfig(target string) string { + in, err := os.Open(selinuxConfig) + if err != nil { + return "" + } + defer in.Close() + + scanner := bufio.NewScanner(in) + + for scanner.Scan() { + line := bytes.TrimSpace(scanner.Bytes()) + if len(line) == 0 { + // Skip blank lines + continue + } + if line[0] == ';' || line[0] == '#' { + // Skip comments + continue + } + fields := bytes.SplitN(line, []byte{'='}, 2) + if len(fields) != 2 { + continue + } + if bytes.Equal(fields[0], []byte(target)) { + return string(bytes.Trim(fields[1], `"`)) + } + } + return "" +} + +func isProcHandle(fh *os.File) error { + var buf unix.Statfs_t + + for { + err := unix.Fstatfs(int(fh.Fd()), &buf) + if err == nil { + break + } + if err != unix.EINTR { //nolint:errorlint // unix errors are bare + return &os.PathError{Op: "fstatfs", Path: fh.Name(), Err: err} + } + } + if buf.Type != unix.PROC_SUPER_MAGIC { + return fmt.Errorf("file %q is not on procfs", fh.Name()) + } + + return nil +} + +func readCon(fpath string) (string, error) { + if fpath == "" { + return "", ErrEmptyPath + } + + in, err := os.Open(fpath) + if err != nil { + return "", err + } + defer in.Close() + + if err := isProcHandle(in); err != nil { + return "", err + } + return readConFd(in) +} + +func readConFd(in *os.File) (string, error) { + data, err := ioutil.ReadAll(in) + if err != nil { + return "", err + } + return string(bytes.TrimSuffix(data, []byte{0})), nil +} + +// classIndex returns the int index for an object class in the loaded policy, +// or -1 and an error +func classIndex(class string) (int, error) { + permpath := fmt.Sprintf("class/%s/index", class) + indexpath := filepath.Join(getSelinuxMountPoint(), permpath) + + indexB, err := ioutil.ReadFile(indexpath) + if err != nil { + return -1, err + } + index, err := strconv.Atoi(string(indexB)) + if err != nil { + return -1, err + } + + return index, nil +} + +// lSetFileLabel sets the SELinux label for this path, not following symlinks, +// or returns an error. +func lSetFileLabel(fpath string, label string) error { + if fpath == "" { + return ErrEmptyPath + } + for { + err := unix.Lsetxattr(fpath, xattrNameSelinux, []byte(label), 0) + if err == nil { + break + } + if err != unix.EINTR { //nolint:errorlint // unix errors are bare + return &os.PathError{Op: "lsetxattr", Path: fpath, Err: err} + } + } + + return nil +} + +// setFileLabel sets the SELinux label for this path, following symlinks, +// or returns an error. +func setFileLabel(fpath string, label string) error { + if fpath == "" { + return ErrEmptyPath + } + for { + err := unix.Setxattr(fpath, xattrNameSelinux, []byte(label), 0) + if err == nil { + break + } + if err != unix.EINTR { //nolint:errorlint // unix errors are bare + return &os.PathError{Op: "setxattr", Path: fpath, Err: err} + } + } + + return nil +} + +// fileLabel returns the SELinux label for this path, following symlinks, +// or returns an error. +func fileLabel(fpath string) (string, error) { + if fpath == "" { + return "", ErrEmptyPath + } + + label, err := getxattr(fpath, xattrNameSelinux) + if err != nil { + return "", &os.PathError{Op: "getxattr", Path: fpath, Err: err} + } + // Trim the NUL byte at the end of the byte buffer, if present. + if len(label) > 0 && label[len(label)-1] == '\x00' { + label = label[:len(label)-1] + } + return string(label), nil +} + +// lFileLabel returns the SELinux label for this path, not following symlinks, +// or returns an error. +func lFileLabel(fpath string) (string, error) { + if fpath == "" { + return "", ErrEmptyPath + } + + label, err := lgetxattr(fpath, xattrNameSelinux) + if err != nil { + return "", &os.PathError{Op: "lgetxattr", Path: fpath, Err: err} + } + // Trim the NUL byte at the end of the byte buffer, if present. + if len(label) > 0 && label[len(label)-1] == '\x00' { + label = label[:len(label)-1] + } + return string(label), nil +} + +// setFSCreateLabel tells kernel the label to create all file system objects +// created by this task. Setting label="" to return to default. +func setFSCreateLabel(label string) error { + return writeAttr("fscreate", label) +} + +// fsCreateLabel returns the default label the kernel which the kernel is using +// for file system objects created by this task. "" indicates default. +func fsCreateLabel() (string, error) { + return readAttr("fscreate") +} + +// currentLabel returns the SELinux label of the current process thread, or an error. +func currentLabel() (string, error) { + return readAttr("current") +} + +// pidLabel returns the SELinux label of the given pid, or an error. +func pidLabel(pid int) (string, error) { + return readCon(fmt.Sprintf("/proc/%d/attr/current", pid)) +} + +// ExecLabel returns the SELinux label that the kernel will use for any programs +// that are executed by the current process thread, or an error. +func execLabel() (string, error) { + return readAttr("exec") +} + +func writeCon(fpath, val string) error { + if fpath == "" { + return ErrEmptyPath + } + if val == "" { + if !getEnabled() { + return nil + } + } + + out, err := os.OpenFile(fpath, os.O_WRONLY, 0) + if err != nil { + return err + } + defer out.Close() + + if err := isProcHandle(out); err != nil { + return err + } + + if val != "" { + _, err = out.Write([]byte(val)) + } else { + _, err = out.Write(nil) + } + if err != nil { + return err + } + return nil +} + +func attrPath(attr string) string { + // Linux >= 3.17 provides this + const threadSelfPrefix = "/proc/thread-self/attr" + + attrPathOnce.Do(func() { + st, err := os.Stat(threadSelfPrefix) + if err == nil && st.Mode().IsDir() { + haveThreadSelf = true + } + }) + + if haveThreadSelf { + return path.Join(threadSelfPrefix, attr) + } + + return path.Join("/proc/self/task/", strconv.Itoa(unix.Gettid()), "/attr/", attr) +} + +func readAttr(attr string) (string, error) { + return readCon(attrPath(attr)) +} + +func writeAttr(attr, val string) error { + return writeCon(attrPath(attr), val) +} + +// canonicalizeContext takes a context string and writes it to the kernel +// the function then returns the context that the kernel will use. Use this +// function to check if two contexts are equivalent +func canonicalizeContext(val string) (string, error) { + return readWriteCon(filepath.Join(getSelinuxMountPoint(), "context"), val) +} + +// computeCreateContext requests the type transition from source to target for +// class from the kernel. +func computeCreateContext(source string, target string, class string) (string, error) { + classidx, err := classIndex(class) + if err != nil { + return "", err + } + + return readWriteCon(filepath.Join(getSelinuxMountPoint(), "create"), fmt.Sprintf("%s %s %d", source, target, classidx)) +} + +// catsToBitset stores categories in a bitset. +func catsToBitset(cats string) (*big.Int, error) { + bitset := new(big.Int) + + catlist := strings.Split(cats, ",") + for _, r := range catlist { + ranges := strings.SplitN(r, ".", 2) + if len(ranges) > 1 { + catstart, err := parseLevelItem(ranges[0], category) + if err != nil { + return nil, err + } + catend, err := parseLevelItem(ranges[1], category) + if err != nil { + return nil, err + } + for i := catstart; i <= catend; i++ { + bitset.SetBit(bitset, int(i), 1) + } + } else { + cat, err := parseLevelItem(ranges[0], category) + if err != nil { + return nil, err + } + bitset.SetBit(bitset, int(cat), 1) + } + } + + return bitset, nil +} + +// parseLevelItem parses and verifies that a sensitivity or category are valid +func parseLevelItem(s string, sep levelItem) (uint, error) { + if len(s) < minSensLen || levelItem(s[0]) != sep { + return 0, ErrLevelSyntax + } + val, err := strconv.ParseUint(s[1:], 10, 32) + if err != nil { + return 0, err + } + + return uint(val), nil +} + +// parseLevel fills a level from a string that contains +// a sensitivity and categories +func (l *level) parseLevel(levelStr string) error { + lvl := strings.SplitN(levelStr, ":", 2) + sens, err := parseLevelItem(lvl[0], sensitivity) + if err != nil { + return fmt.Errorf("failed to parse sensitivity: %w", err) + } + l.sens = sens + if len(lvl) > 1 { + cats, err := catsToBitset(lvl[1]) + if err != nil { + return fmt.Errorf("failed to parse categories: %w", err) + } + l.cats = cats + } + + return nil +} + +// rangeStrToMLSRange marshals a string representation of a range. +func rangeStrToMLSRange(rangeStr string) (*mlsRange, error) { + mlsRange := &mlsRange{} + levelSlice := strings.SplitN(rangeStr, "-", 2) + + switch len(levelSlice) { + // rangeStr that has a low and a high level, e.g. s4:c0.c1023-s6:c0.c1023 + case 2: + mlsRange.high = &level{} + if err := mlsRange.high.parseLevel(levelSlice[1]); err != nil { + return nil, fmt.Errorf("failed to parse high level %q: %w", levelSlice[1], err) + } + fallthrough + // rangeStr that is single level, e.g. s6:c0,c3,c5,c30.c1023 + case 1: + mlsRange.low = &level{} + if err := mlsRange.low.parseLevel(levelSlice[0]); err != nil { + return nil, fmt.Errorf("failed to parse low level %q: %w", levelSlice[0], err) + } + } + + if mlsRange.high == nil { + mlsRange.high = mlsRange.low + } + + return mlsRange, nil +} + +// bitsetToStr takes a category bitset and returns it in the +// canonical selinux syntax +func bitsetToStr(c *big.Int) string { + var str string + + length := 0 + for i := int(c.TrailingZeroBits()); i < c.BitLen(); i++ { + if c.Bit(i) == 0 { + continue + } + if length == 0 { + if str != "" { + str += "," + } + str += "c" + strconv.Itoa(i) + } + if c.Bit(i+1) == 1 { + length++ + continue + } + if length == 1 { + str += ",c" + strconv.Itoa(i) + } else if length > 1 { + str += ".c" + strconv.Itoa(i) + } + length = 0 + } + + return str +} + +func (l1 *level) equal(l2 *level) bool { + if l2 == nil || l1 == nil { + return l1 == l2 + } + if l1.sens != l2.sens { + return false + } + if l2.cats == nil || l1.cats == nil { + return l2.cats == l1.cats + } + return l1.cats.Cmp(l2.cats) == 0 +} + +// String returns an mlsRange as a string. +func (m mlsRange) String() string { + low := "s" + strconv.Itoa(int(m.low.sens)) + if m.low.cats != nil && m.low.cats.BitLen() > 0 { + low += ":" + bitsetToStr(m.low.cats) + } + + if m.low.equal(m.high) { + return low + } + + high := "s" + strconv.Itoa(int(m.high.sens)) + if m.high.cats != nil && m.high.cats.BitLen() > 0 { + high += ":" + bitsetToStr(m.high.cats) + } + + return low + "-" + high +} + +func max(a, b uint) uint { + if a > b { + return a + } + return b +} + +func min(a, b uint) uint { + if a < b { + return a + } + return b +} + +// calculateGlbLub computes the glb (greatest lower bound) and lub (least upper bound) +// of a source and target range. +// The glblub is calculated as the greater of the low sensitivities and +// the lower of the high sensitivities and the and of each category bitset. +func calculateGlbLub(sourceRange, targetRange string) (string, error) { + s, err := rangeStrToMLSRange(sourceRange) + if err != nil { + return "", err + } + t, err := rangeStrToMLSRange(targetRange) + if err != nil { + return "", err + } + + if s.high.sens < t.low.sens || t.high.sens < s.low.sens { + /* these ranges have no common sensitivities */ + return "", ErrIncomparable + } + + outrange := &mlsRange{low: &level{}, high: &level{}} + + /* take the greatest of the low */ + outrange.low.sens = max(s.low.sens, t.low.sens) + + /* take the least of the high */ + outrange.high.sens = min(s.high.sens, t.high.sens) + + /* find the intersecting categories */ + if s.low.cats != nil && t.low.cats != nil { + outrange.low.cats = new(big.Int) + outrange.low.cats.And(s.low.cats, t.low.cats) + } + if s.high.cats != nil && t.high.cats != nil { + outrange.high.cats = new(big.Int) + outrange.high.cats.And(s.high.cats, t.high.cats) + } + + return outrange.String(), nil +} + +func readWriteCon(fpath string, val string) (string, error) { + if fpath == "" { + return "", ErrEmptyPath + } + f, err := os.OpenFile(fpath, os.O_RDWR, 0) + if err != nil { + return "", err + } + defer f.Close() + + _, err = f.Write([]byte(val)) + if err != nil { + return "", err + } + + return readConFd(f) +} + +// setExecLabel sets the SELinux label that the kernel will use for any programs +// that are executed by the current process thread, or an error. +func setExecLabel(label string) error { + return writeAttr("exec", label) +} + +// setTaskLabel sets the SELinux label for the current thread, or an error. +// This requires the dyntransition permission. +func setTaskLabel(label string) error { + return writeAttr("current", label) +} + +// setSocketLabel takes a process label and tells the kernel to assign the +// label to the next socket that gets created +func setSocketLabel(label string) error { + return writeAttr("sockcreate", label) +} + +// socketLabel retrieves the current socket label setting +func socketLabel() (string, error) { + return readAttr("sockcreate") +} + +// peerLabel retrieves the label of the client on the other side of a socket +func peerLabel(fd uintptr) (string, error) { + label, err := unix.GetsockoptString(int(fd), unix.SOL_SOCKET, unix.SO_PEERSEC) + if err != nil { + return "", &os.PathError{Op: "getsockopt", Path: "fd " + strconv.Itoa(int(fd)), Err: err} + } + return label, nil +} + +// setKeyLabel takes a process label and tells the kernel to assign the +// label to the next kernel keyring that gets created +func setKeyLabel(label string) error { + err := writeCon("/proc/self/attr/keycreate", label) + if errors.Is(err, os.ErrNotExist) { + return nil + } + if label == "" && errors.Is(err, os.ErrPermission) { + return nil + } + return err +} + +// keyLabel retrieves the current kernel keyring label setting +func keyLabel() (string, error) { + return readCon("/proc/self/attr/keycreate") +} + +// get returns the Context as a string +func (c Context) get() string { + if level := c["level"]; level != "" { + return c["user"] + ":" + c["role"] + ":" + c["type"] + ":" + level + } + return c["user"] + ":" + c["role"] + ":" + c["type"] +} + +// newContext creates a new Context struct from the specified label +func newContext(label string) (Context, error) { + c := make(Context) + + if len(label) != 0 { + con := strings.SplitN(label, ":", 4) + if len(con) < 3 { + return c, InvalidLabel + } + c["user"] = con[0] + c["role"] = con[1] + c["type"] = con[2] + if len(con) > 3 { + c["level"] = con[3] + } + } + return c, nil +} + +// clearLabels clears all reserved labels +func clearLabels() { + state.Lock() + state.mcsList = make(map[string]bool) + state.Unlock() +} + +// reserveLabel reserves the MLS/MCS level component of the specified label +func reserveLabel(label string) { + if len(label) != 0 { + con := strings.SplitN(label, ":", 4) + if len(con) > 3 { + _ = mcsAdd(con[3]) + } + } +} + +func selinuxEnforcePath() string { + return path.Join(getSelinuxMountPoint(), "enforce") +} + +// enforceMode returns the current SELinux mode Enforcing, Permissive, Disabled +func enforceMode() int { + var enforce int + + enforceB, err := ioutil.ReadFile(selinuxEnforcePath()) + if err != nil { + return -1 + } + enforce, err = strconv.Atoi(string(enforceB)) + if err != nil { + return -1 + } + return enforce +} + +// setEnforceMode sets the current SELinux mode Enforcing, Permissive. +// Disabled is not valid, since this needs to be set at boot time. +func setEnforceMode(mode int) error { + return ioutil.WriteFile(selinuxEnforcePath(), []byte(strconv.Itoa(mode)), 0o644) +} + +// defaultEnforceMode returns the systems default SELinux mode Enforcing, +// Permissive or Disabled. Note this is is just the default at boot time. +// EnforceMode tells you the systems current mode. +func defaultEnforceMode() int { + switch readConfig(selinuxTag) { + case "enforcing": + return Enforcing + case "permissive": + return Permissive + } + return Disabled +} + +func mcsAdd(mcs string) error { + if mcs == "" { + return nil + } + state.Lock() + defer state.Unlock() + if state.mcsList[mcs] { + return ErrMCSAlreadyExists + } + state.mcsList[mcs] = true + return nil +} + +func mcsDelete(mcs string) { + if mcs == "" { + return + } + state.Lock() + defer state.Unlock() + state.mcsList[mcs] = false +} + +func intToMcs(id int, catRange uint32) string { + var ( + SETSIZE = int(catRange) + TIER = SETSIZE + ORD = id + ) + + if id < 1 || id > 523776 { + return "" + } + + for ORD > TIER { + ORD -= TIER + TIER-- + } + TIER = SETSIZE - TIER + ORD += TIER + return fmt.Sprintf("s0:c%d,c%d", TIER, ORD) +} + +func uniqMcs(catRange uint32) string { + var ( + n uint32 + c1, c2 uint32 + mcs string + ) + + for { + _ = binary.Read(rand.Reader, binary.LittleEndian, &n) + c1 = n % catRange + _ = binary.Read(rand.Reader, binary.LittleEndian, &n) + c2 = n % catRange + if c1 == c2 { + continue + } else if c1 > c2 { + c1, c2 = c2, c1 + } + mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2) + if err := mcsAdd(mcs); err != nil { + continue + } + break + } + return mcs +} + +// releaseLabel un-reserves the MLS/MCS Level field of the specified label, +// allowing it to be used by another process. +func releaseLabel(label string) { + if len(label) != 0 { + con := strings.SplitN(label, ":", 4) + if len(con) > 3 { + mcsDelete(con[3]) + } + } +} + +// roFileLabel returns the specified SELinux readonly file label +func roFileLabel() string { + return readOnlyFileLabel +} + +func openContextFile() (*os.File, error) { + if f, err := os.Open(contextFile); err == nil { + return f, nil + } + return os.Open(filepath.Join(policyRoot(), "/contexts/lxc_contexts")) +} + +func loadLabels() { + labels = make(map[string]string) + in, err := openContextFile() + if err != nil { + return + } + defer in.Close() + + scanner := bufio.NewScanner(in) + + for scanner.Scan() { + line := bytes.TrimSpace(scanner.Bytes()) + if len(line) == 0 { + // Skip blank lines + continue + } + if line[0] == ';' || line[0] == '#' { + // Skip comments + continue + } + fields := bytes.SplitN(line, []byte{'='}, 2) + if len(fields) != 2 { + continue + } + key, val := bytes.TrimSpace(fields[0]), bytes.TrimSpace(fields[1]) + labels[string(key)] = string(bytes.Trim(val, `"`)) + } + + con, _ := NewContext(labels["file"]) + con["level"] = fmt.Sprintf("s0:c%d,c%d", maxCategory-2, maxCategory-1) + privContainerMountLabel = con.get() + reserveLabel(privContainerMountLabel) +} + +func label(key string) string { + loadLabelsOnce.Do(func() { + loadLabels() + }) + return labels[key] +} + +// kvmContainerLabels returns the default processLabel and mountLabel to be used +// for kvm containers by the calling process. +func kvmContainerLabels() (string, string) { + processLabel := label("kvm_process") + if processLabel == "" { + processLabel = label("process") + } + + return addMcs(processLabel, label("file")) +} + +// initContainerLabels returns the default processLabel and file labels to be +// used for containers running an init system like systemd by the calling process. +func initContainerLabels() (string, string) { + processLabel := label("init_process") + if processLabel == "" { + processLabel = label("process") + } + + return addMcs(processLabel, label("file")) +} + +// containerLabels returns an allocated processLabel and fileLabel to be used for +// container labeling by the calling process. +func containerLabels() (processLabel string, fileLabel string) { + if !getEnabled() { + return "", "" + } + + processLabel = label("process") + fileLabel = label("file") + readOnlyFileLabel = label("ro_file") + + if processLabel == "" || fileLabel == "" { + return "", fileLabel + } + + if readOnlyFileLabel == "" { + readOnlyFileLabel = fileLabel + } + + return addMcs(processLabel, fileLabel) +} + +func addMcs(processLabel, fileLabel string) (string, string) { + scon, _ := NewContext(processLabel) + if scon["level"] != "" { + mcs := uniqMcs(CategoryRange) + scon["level"] = mcs + processLabel = scon.Get() + scon, _ = NewContext(fileLabel) + scon["level"] = mcs + fileLabel = scon.Get() + } + return processLabel, fileLabel +} + +// securityCheckContext validates that the SELinux label is understood by the kernel +func securityCheckContext(val string) error { + return ioutil.WriteFile(path.Join(getSelinuxMountPoint(), "context"), []byte(val), 0o644) +} + +// copyLevel returns a label with the MLS/MCS level from src label replaced on +// the dest label. +func copyLevel(src, dest string) (string, error) { + if src == "" { + return "", nil + } + if err := SecurityCheckContext(src); err != nil { + return "", err + } + if err := SecurityCheckContext(dest); err != nil { + return "", err + } + scon, err := NewContext(src) + if err != nil { + return "", err + } + tcon, err := NewContext(dest) + if err != nil { + return "", err + } + mcsDelete(tcon["level"]) + _ = mcsAdd(scon["level"]) + tcon["level"] = scon["level"] + return tcon.Get(), nil +} + +// Prevent users from relabeling system files +func badPrefix(fpath string) error { + if fpath == "" { + return ErrEmptyPath + } + + badPrefixes := []string{"/usr"} + for _, prefix := range badPrefixes { + if strings.HasPrefix(fpath, prefix) { + return fmt.Errorf("relabeling content in %s is not allowed", prefix) + } + } + return nil +} + +// chcon changes the fpath file object to the SELinux label label. +// If fpath is a directory and recurse is true, then chcon walks the +// directory tree setting the label. +func chcon(fpath string, label string, recurse bool) error { + if fpath == "" { + return ErrEmptyPath + } + if label == "" { + return nil + } + if err := badPrefix(fpath); err != nil { + return err + } + + if !recurse { + return setFileLabel(fpath, label) + } + + return rchcon(fpath, label) +} + +// dupSecOpt takes an SELinux process label and returns security options that +// can be used to set the SELinux Type and Level for future container processes. +func dupSecOpt(src string) ([]string, error) { + if src == "" { + return nil, nil + } + con, err := NewContext(src) + if err != nil { + return nil, err + } + if con["user"] == "" || + con["role"] == "" || + con["type"] == "" { + return nil, nil + } + dup := []string{ + "user:" + con["user"], + "role:" + con["role"], + "type:" + con["type"], + } + + if con["level"] != "" { + dup = append(dup, "level:"+con["level"]) + } + + return dup, nil +} + +// disableSecOpt returns a security opt that can be used to disable SELinux +// labeling support for future container processes. +func disableSecOpt() []string { + return []string{"disable"} +} + +// findUserInContext scans the reader for a valid SELinux context +// match that is verified with the verifier. Invalid contexts are +// skipped. It returns a matched context or an empty string if no +// match is found. If a scanner error occurs, it is returned. +func findUserInContext(context Context, r io.Reader, verifier func(string) error) (string, error) { + fromRole := context["role"] + fromType := context["type"] + scanner := bufio.NewScanner(r) + + for scanner.Scan() { + fromConns := strings.Fields(scanner.Text()) + if len(fromConns) == 0 { + // Skip blank lines + continue + } + + line := fromConns[0] + + if line[0] == ';' || line[0] == '#' { + // Skip comments + continue + } + + // user context files contexts are formatted as + // role_r:type_t:s0 where the user is missing. + lineArr := strings.SplitN(line, ":", 4) + // skip context with typo, or role and type do not match + if len(lineArr) != 3 || + lineArr[0] != fromRole || + lineArr[1] != fromType { + continue + } + + for _, cc := range fromConns[1:] { + toConns := strings.SplitN(cc, ":", 4) + if len(toConns) != 3 { + continue + } + + context["role"] = toConns[0] + context["type"] = toConns[1] + + outConn := context.get() + if err := verifier(outConn); err != nil { + continue + } + + return outConn, nil + } + } + if err := scanner.Err(); err != nil { + return "", fmt.Errorf("failed to scan for context: %w", err) + } + + return "", nil +} + +func getDefaultContextFromReaders(c *defaultSECtx) (string, error) { + if c.verifier == nil { + return "", ErrVerifierNil + } + + context, err := newContext(c.scon) + if err != nil { + return "", fmt.Errorf("failed to create label for %s: %w", c.scon, err) + } + + // set so the verifier validates the matched context with the provided user and level. + context["user"] = c.user + context["level"] = c.level + + conn, err := findUserInContext(context, c.userRdr, c.verifier) + if err != nil { + return "", err + } + + if conn != "" { + return conn, nil + } + + conn, err = findUserInContext(context, c.defaultRdr, c.verifier) + if err != nil { + return "", err + } + + if conn != "" { + return conn, nil + } + + return "", fmt.Errorf("context %q not found: %w", c.scon, ErrContextMissing) +} + +func getDefaultContextWithLevel(user, level, scon string) (string, error) { + userPath := filepath.Join(policyRoot(), selinuxUsersDir, user) + fu, err := os.Open(userPath) + if err != nil { + return "", err + } + defer fu.Close() + + defaultPath := filepath.Join(policyRoot(), defaultContexts) + fd, err := os.Open(defaultPath) + if err != nil { + return "", err + } + defer fd.Close() + + c := defaultSECtx{ + user: user, + level: level, + scon: scon, + userRdr: fu, + defaultRdr: fd, + verifier: securityCheckContext, + } + + return getDefaultContextFromReaders(&c) +} diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go new file mode 100644 index 0000000000..78743b020c --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go @@ -0,0 +1,164 @@ +// +build !linux + +package selinux + +func setDisabled() { +} + +func getEnabled() bool { + return false +} + +func classIndex(class string) (int, error) { + return -1, nil +} + +func setFileLabel(fpath string, label string) error { + return nil +} + +func lSetFileLabel(fpath string, label string) error { + return nil +} + +func fileLabel(fpath string) (string, error) { + return "", nil +} + +func lFileLabel(fpath string) (string, error) { + return "", nil +} + +func setFSCreateLabel(label string) error { + return nil +} + +func fsCreateLabel() (string, error) { + return "", nil +} + +func currentLabel() (string, error) { + return "", nil +} + +func pidLabel(pid int) (string, error) { + return "", nil +} + +func execLabel() (string, error) { + return "", nil +} + +func canonicalizeContext(val string) (string, error) { + return "", nil +} + +func computeCreateContext(source string, target string, class string) (string, error) { + return "", nil +} + +func calculateGlbLub(sourceRange, targetRange string) (string, error) { + return "", nil +} + +func setExecLabel(label string) error { + return nil +} + +func setTaskLabel(label string) error { + return nil +} + +func setSocketLabel(label string) error { + return nil +} + +func socketLabel() (string, error) { + return "", nil +} + +func peerLabel(fd uintptr) (string, error) { + return "", nil +} + +func setKeyLabel(label string) error { + return nil +} + +func keyLabel() (string, error) { + return "", nil +} + +func (c Context) get() string { + return "" +} + +func newContext(label string) (Context, error) { + c := make(Context) + return c, nil +} + +func clearLabels() { +} + +func reserveLabel(label string) { +} + +func enforceMode() int { + return Disabled +} + +func setEnforceMode(mode int) error { + return nil +} + +func defaultEnforceMode() int { + return Disabled +} + +func releaseLabel(label string) { +} + +func roFileLabel() string { + return "" +} + +func kvmContainerLabels() (string, string) { + return "", "" +} + +func initContainerLabels() (string, string) { + return "", "" +} + +func containerLabels() (processLabel string, fileLabel string) { + return "", "" +} + +func securityCheckContext(val string) error { + return nil +} + +func copyLevel(src, dest string) (string, error) { + return "", nil +} + +func chcon(fpath string, label string, recurse bool) error { + return nil +} + +func dupSecOpt(src string) ([]string, error) { + return nil, nil +} + +func disableSecOpt() []string { + return []string{"disable"} +} + +func getDefaultContextWithLevel(user, level, scon string) (string, error) { + return "", nil +} + +func label(_ string) string { + return "" +} diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/xattrs_linux.go b/vendor/github.com/opencontainers/selinux/go-selinux/xattrs_linux.go new file mode 100644 index 0000000000..9e473ca168 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/go-selinux/xattrs_linux.go @@ -0,0 +1,71 @@ +package selinux + +import ( + "golang.org/x/sys/unix" +) + +// lgetxattr returns a []byte slice containing the value of +// an extended attribute attr set for path. +func lgetxattr(path, attr string) ([]byte, error) { + // Start with a 128 length byte array + dest := make([]byte, 128) + sz, errno := doLgetxattr(path, attr, dest) + for errno == unix.ERANGE { //nolint:errorlint // unix errors are bare + // Buffer too small, use zero-sized buffer to get the actual size + sz, errno = doLgetxattr(path, attr, []byte{}) + if errno != nil { + return nil, errno + } + + dest = make([]byte, sz) + sz, errno = doLgetxattr(path, attr, dest) + } + if errno != nil { + return nil, errno + } + + return dest[:sz], nil +} + +// doLgetxattr is a wrapper that retries on EINTR +func doLgetxattr(path, attr string, dest []byte) (int, error) { + for { + sz, err := unix.Lgetxattr(path, attr, dest) + if err != unix.EINTR { //nolint:errorlint // unix errors are bare + return sz, err + } + } +} + +// getxattr returns a []byte slice containing the value of +// an extended attribute attr set for path. +func getxattr(path, attr string) ([]byte, error) { + // Start with a 128 length byte array + dest := make([]byte, 128) + sz, errno := dogetxattr(path, attr, dest) + for errno == unix.ERANGE { //nolint:errorlint // unix errors are bare + // Buffer too small, use zero-sized buffer to get the actual size + sz, errno = dogetxattr(path, attr, []byte{}) + if errno != nil { + return nil, errno + } + + dest = make([]byte, sz) + sz, errno = dogetxattr(path, attr, dest) + } + if errno != nil { + return nil, errno + } + + return dest[:sz], nil +} + +// dogetxattr is a wrapper that retries on EINTR +func dogetxattr(path, attr string, dest []byte) (int, error) { + for { + sz, err := unix.Getxattr(path, attr, dest) + if err != unix.EINTR { //nolint:errorlint // unix errors are bare + return sz, err + } + } +} diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md b/vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md new file mode 100644 index 0000000000..7e78dce015 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md @@ -0,0 +1,48 @@ +## pwalk: parallel implementation of filepath.Walk + +This is a wrapper for [filepath.Walk](https://pkg.go.dev/path/filepath?tab=doc#Walk) +which may speed it up by calling multiple callback functions (WalkFunc) in parallel, +utilizing goroutines. + +By default, it utilizes 2\*runtime.NumCPU() goroutines for callbacks. +This can be changed by using WalkN function which has the additional +parameter, specifying the number of goroutines (concurrency). + +### pwalk vs pwalkdir + +This package is deprecated in favor of +[pwalkdir](https://pkg.go.dev/github.com/opencontainers/selinux/pkg/pwalkdir), +which is faster, but requires at least Go 1.16. + +### Caveats + +Please note the following limitations of this code: + +* Unlike filepath.Walk, the order of calls is non-deterministic; + +* Only primitive error handling is supported: + + * filepath.SkipDir is not supported; + + * no errors are ever passed to WalkFunc; + + * once any error is returned from any WalkFunc instance, no more new calls + to WalkFunc are made, and the error is returned to the caller of Walk; + + * if more than one walkFunc instance will return an error, only one + of such errors will be propagated and returned by Walk, others + will be silently discarded. + +### Documentation + +For the official documentation, see +https://pkg.go.dev/github.com/opencontainers/selinux/pkg/pwalk?tab=doc + +### Benchmarks + +For a WalkFunc that consists solely of the return statement, this +implementation is about 10% slower than the standard library's +filepath.Walk. + +Otherwise (if a WalkFunc is doing something) this is usually faster, +except when the WalkN(..., 1) is used. diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go b/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go new file mode 100644 index 0000000000..202c80da59 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go @@ -0,0 +1,115 @@ +package pwalk + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "sync" +) + +type WalkFunc = filepath.WalkFunc + +// Walk is a wrapper for filepath.Walk which can call multiple walkFn +// in parallel, allowing to handle each item concurrently. A maximum of +// twice the runtime.NumCPU() walkFn will be called at any one time. +// If you want to change the maximum, use WalkN instead. +// +// The order of calls is non-deterministic. +// +// Note that this implementation only supports primitive error handling: +// +// - no errors are ever passed to walkFn; +// +// - once a walkFn returns any error, all further processing stops +// and the error is returned to the caller of Walk; +// +// - filepath.SkipDir is not supported; +// +// - if more than one walkFn instance will return an error, only one +// of such errors will be propagated and returned by Walk, others +// will be silently discarded. +func Walk(root string, walkFn WalkFunc) error { + return WalkN(root, walkFn, runtime.NumCPU()*2) +} + +// WalkN is a wrapper for filepath.Walk which can call multiple walkFn +// in parallel, allowing to handle each item concurrently. A maximum of +// num walkFn will be called at any one time. +// +// Please see Walk documentation for caveats of using this function. +func WalkN(root string, walkFn WalkFunc, num int) error { + // make sure limit is sensible + if num < 1 { + return fmt.Errorf("walk(%q): num must be > 0", root) + } + + files := make(chan *walkArgs, 2*num) + errCh := make(chan error, 1) // get the first error, ignore others + + // Start walking a tree asap + var ( + err error + wg sync.WaitGroup + + rootLen = len(root) + rootEntry *walkArgs + ) + wg.Add(1) + go func() { + err = filepath.Walk(root, func(p string, info os.FileInfo, err error) error { + if err != nil { + close(files) + return err + } + if len(p) == rootLen { + // Root entry is processed separately below. + rootEntry = &walkArgs{path: p, info: &info} + return nil + } + // add a file to the queue unless a callback sent an error + select { + case e := <-errCh: + close(files) + return e + default: + files <- &walkArgs{path: p, info: &info} + return nil + } + }) + if err == nil { + close(files) + } + wg.Done() + }() + + wg.Add(num) + for i := 0; i < num; i++ { + go func() { + for file := range files { + if e := walkFn(file.path, *file.info, nil); e != nil { + select { + case errCh <- e: // sent ok + default: // buffer full + } + } + } + wg.Done() + }() + } + + wg.Wait() + + if err == nil { + err = walkFn(rootEntry.path, *rootEntry.info, nil) + } + + return err +} + +// walkArgs holds the arguments that were passed to the Walk or WalkN +// functions. +type walkArgs struct { + path string + info *os.FileInfo +} diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/README.md b/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/README.md new file mode 100644 index 0000000000..068ac40056 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/README.md @@ -0,0 +1,54 @@ +## pwalkdir: parallel implementation of filepath.WalkDir + +This is a wrapper for [filepath.WalkDir](https://pkg.go.dev/path/filepath#WalkDir) +which may speed it up by calling multiple callback functions (WalkDirFunc) +in parallel, utilizing goroutines. + +By default, it utilizes 2\*runtime.NumCPU() goroutines for callbacks. +This can be changed by using WalkN function which has the additional +parameter, specifying the number of goroutines (concurrency). + +### pwalk vs pwalkdir + +This package is very similar to +[pwalk](https://pkg.go.dev/github.com/opencontainers/selinux/pkg/pwalkdir), +but utilizes `filepath.WalkDir` (added to Go 1.16), which does not call stat(2) +on every entry and is therefore faster (up to 3x, depending on usage scenario). + +Users who are OK with requiring Go 1.16+ should switch to this +implementation. + +### Caveats + +Please note the following limitations of this code: + +* Unlike filepath.WalkDir, the order of calls is non-deterministic; + +* Only primitive error handling is supported: + + * fs.SkipDir is not supported; + + * no errors are ever passed to WalkDirFunc; + + * once any error is returned from any walkDirFunc instance, no more calls + to WalkDirFunc are made, and the error is returned to the caller of WalkDir; + + * if more than one WalkDirFunc instance will return an error, only one + of such errors will be propagated to and returned by WalkDir, others + will be silently discarded. + +### Documentation + +For the official documentation, see +https://pkg.go.dev/github.com/opencontainers/selinux/pkg/pwalkdir + +### Benchmarks + +For a WalkDirFunc that consists solely of the return statement, this +implementation is about 15% slower than the standard library's +filepath.WalkDir. + +Otherwise (if a WalkDirFunc is actually doing something) this is usually +faster, except when the WalkDirN(..., 1) is used. Run `go test -bench .` +to see how different operations can benefit from it, as well as how the +level of paralellism affects the speed. diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/pwalkdir.go b/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/pwalkdir.go new file mode 100644 index 0000000000..a5796b2c4f --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/pwalkdir.go @@ -0,0 +1,116 @@ +//go:build go1.16 +// +build go1.16 + +package pwalkdir + +import ( + "fmt" + "io/fs" + "path/filepath" + "runtime" + "sync" +) + +// Walk is a wrapper for filepath.WalkDir which can call multiple walkFn +// in parallel, allowing to handle each item concurrently. A maximum of +// twice the runtime.NumCPU() walkFn will be called at any one time. +// If you want to change the maximum, use WalkN instead. +// +// The order of calls is non-deterministic. +// +// Note that this implementation only supports primitive error handling: +// +// - no errors are ever passed to walkFn; +// +// - once a walkFn returns any error, all further processing stops +// and the error is returned to the caller of Walk; +// +// - filepath.SkipDir is not supported; +// +// - if more than one walkFn instance will return an error, only one +// of such errors will be propagated and returned by Walk, others +// will be silently discarded. +func Walk(root string, walkFn fs.WalkDirFunc) error { + return WalkN(root, walkFn, runtime.NumCPU()*2) +} + +// WalkN is a wrapper for filepath.WalkDir which can call multiple walkFn +// in parallel, allowing to handle each item concurrently. A maximum of +// num walkFn will be called at any one time. +// +// Please see Walk documentation for caveats of using this function. +func WalkN(root string, walkFn fs.WalkDirFunc, num int) error { + // make sure limit is sensible + if num < 1 { + return fmt.Errorf("walk(%q): num must be > 0", root) + } + + files := make(chan *walkArgs, 2*num) + errCh := make(chan error, 1) // Get the first error, ignore others. + + // Start walking a tree asap. + var ( + err error + wg sync.WaitGroup + + rootLen = len(root) + rootEntry *walkArgs + ) + wg.Add(1) + go func() { + err = filepath.WalkDir(root, func(p string, entry fs.DirEntry, err error) error { + if err != nil { + close(files) + return err + } + if len(p) == rootLen { + // Root entry is processed separately below. + rootEntry = &walkArgs{path: p, entry: entry} + return nil + } + // Add a file to the queue unless a callback sent an error. + select { + case e := <-errCh: + close(files) + return e + default: + files <- &walkArgs{path: p, entry: entry} + return nil + } + }) + if err == nil { + close(files) + } + wg.Done() + }() + + wg.Add(num) + for i := 0; i < num; i++ { + go func() { + for file := range files { + if e := walkFn(file.path, file.entry, nil); e != nil { + select { + case errCh <- e: // sent ok + default: // buffer full + } + } + } + wg.Done() + }() + } + + wg.Wait() + + if err == nil { + err = walkFn(rootEntry.path, rootEntry.entry, nil) + } + + return err +} + +// walkArgs holds the arguments that were passed to the Walk or WalkN +// functions. +type walkArgs struct { + path string + entry fs.DirEntry +} diff --git a/vendor/github.com/openshift/source-to-image/AUTHORS b/vendor/github.com/openshift/source-to-image/AUTHORS new file mode 100644 index 0000000000..2e54fb9f78 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/AUTHORS @@ -0,0 +1,10 @@ +Krishna Raman +Paul Morie +Andy Goldstein +Ben Parees +Michal Fojtik +Dan McPherson +Cesar Wong +Maciej Szulik +Jim Minter +Adam Kaplan diff --git a/vendor/github.com/openshift/source-to-image/LICENSE b/vendor/github.com/openshift/source-to-image/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/openshift/source-to-image/pkg/api/constants/doc.go b/vendor/github.com/openshift/source-to-image/pkg/api/constants/doc.go new file mode 100644 index 0000000000..d5b259eb84 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/api/constants/doc.go @@ -0,0 +1,2 @@ +// Package constants provides constants used across s2i. +package constants diff --git a/vendor/github.com/openshift/source-to-image/pkg/api/constants/env.go b/vendor/github.com/openshift/source-to-image/pkg/api/constants/env.go new file mode 100644 index 0000000000..ff3448bd9d --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/api/constants/env.go @@ -0,0 +1,13 @@ +package constants + +// Deprecated Docker ENV variables +const ( + // LocationEnvironment is the environment variable that specifies where to place artifacts in a builder image. + // + // DEPRECATED - use DestinationLabel instead. + LocationEnvironment = "STI_LOCATION" + // ScriptsURLEnvironment is the environment variable name that specifies where to look for S2I scripts. + // + // DEPRECATED - use ScriptsURLLabel instead. + ScriptsURLEnvironment = "STI_SCRIPTS_URL" +) diff --git a/vendor/github.com/openshift/source-to-image/pkg/api/constants/labels.go b/vendor/github.com/openshift/source-to-image/pkg/api/constants/labels.go new file mode 100644 index 0000000000..828ba41534 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/api/constants/labels.go @@ -0,0 +1,78 @@ +package constants + +// Docker image label constants +const ( + // OpenshiftNamespace is the namespace for Docker image labels used by OpenShift Origin. + OpenshiftNamespace = "io.openshift." + + // DefaultNamespace is the default namespace for Docker image labels used and produced by S2I. + DefaultNamespace = OpenshiftNamespace + "s2i." + + // KubernetesNamespace is the namespace for Docker image labels used by Kubernetes. + KubernetesNamespace = "io.k8s." + + // KubernetesDescriptionLabel is the Docker image LABEL that provides an image description to Kubernetes. + KubernetesDescriptionLabel = KubernetesNamespace + "description" + + // KubernetesDisplayNameLabel is the Docker image LABEL that provides a human-readable name for the image to Kubernetes. + KubernetesDisplayNameLabel = KubernetesNamespace + "display-name" + + // AssembleInputFilesLabel is the Docker image LABEL that tells S2I which files wil be copied from builder to a runtime image. + AssembleInputFilesLabel = DefaultNamespace + "assemble-input-files" + + // AssembleRuntimeUserLabel is the Docker image label that tells S2I which user should execute the assemble-runtime scripts. + AssembleRuntimeUserLabel = DefaultNamespace + "assemble-runtime-user" + + // AssembleUserLabel is the Docker image label that tells S2I which user should execute the assemble scripts. + AssembleUserLabel = DefaultNamespace + "assemble-user" + + buildNamespace = DefaultNamespace + "build." + + // BuildCommitRefLabel is the Docker image LABEL that S2I uses to record the source commit used to produce the S2I image. + // + // During a rebuild, this label is used by S2I to check out the appropriate commit from the source repository. + BuildCommitRefLabel = buildNamespace + "commit.ref" + + // BuildImageLabel is the Docker image LABEL that S2I uses to record the builder image used to produce the S2I image. + // + // During a rebuild, this label is used by S2I to pull the appropriate builder image. + BuildImageLabel = buildNamespace + "image" + + // BuildSourceLocationLabel is the Docker image LABEL that S2I uses to record the URL of the source repository used to produce the S2I image. + // + // During a rebuild, this label is used by S2I to clone the appropriate source code repository. + BuildSourceLocationLabel = buildNamespace + "source-location" + + // BuildSourceContextDir is the Docker image LABEL that S2I uses to record the context directory in the source code repository to use for the build. + // + // During a rebuild, this label is used by S2I to set the context directory within the source code for the S2I build. + BuildSourceContextDirLabel = buildNamespace + "source-context-dir" + + // TODO: Deprecate BuilderVersionLabel? + + // BuilderBaseVersionLabel is the Docker image LABEL that tells S2I the version of the base image used by the builder image. + BuilderBaseVersionLabel = OpenshiftNamespace + "builder-base-version" + + // BuilderVersionLable is the Docker image LABEL that tells S2I the version of the builder image. + BuilderVersionLabel = OpenshiftNamespace + "builder-version" + + // DestinationLabel is the Docker image LABEL that tells S2I where to place the artifacts (scripts, sources) in the builder image. + DestinationLabel = DefaultNamespace + "destination" + + // ScriptsURLLabel is the Docker image LABEL that tells S2I where to look for the S2I scripts. + // This label is also copied into the output image. + ScriptsURLLabel = DefaultNamespace + "scripts-url" +) + +// Deprecated Docker image labels +const ( + // DeprecatedScriptsURLLabel is the Docker image LABEL that previously told S2I where to look for the S2I scripts. + // + // DEPRECATED - use ScriptsURLLabel instead. + DeprecatedScriptsURLLabel = "io.s2i.scripts-url" + + // DeprecatedDestinationLabel is the Docker image LABEL that previously told S2I where to place the artifacts in the builder image. + // + // DEPRECATED - use DestinationLabel instead. + DeprecatedDestinationLabel = "io.s2i.destination" +) diff --git a/vendor/github.com/openshift/source-to-image/pkg/api/constants/scripts.go b/vendor/github.com/openshift/source-to-image/pkg/api/constants/scripts.go new file mode 100644 index 0000000000..8f18e919b1 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/api/constants/scripts.go @@ -0,0 +1,53 @@ +package constants + +import ( + "os" +) + +const ( + // Assemble is the name of the script responsible for the build process of the resulting image. + Assemble = "assemble" + + // AssembleRuntime is the name of the script responsible for the preparation process of the resulting image. + AssembleRuntime = "assemble-runtime" + + // Run is the name of the script responsible for running the final application. + Run = "run" + + // SaveArtifacts is the name of the script responsible for storing dependencies etc. between builds. + SaveArtifacts = "save-artifacts" + + // Usage is the name of the script responsible for printing the builder image's short info. + Usage = "usage" + + // Environment contains list of key value pairs that will be set during the STI build. + // Users can use this file to provide extra configuration depending on the builder image used. + Environment = "environment" + + // UserScripts is the location of scripts downloaded from user provided URL (-s flag). + UserScripts = "downloads" + string(os.PathSeparator) + "scripts" + + // DefaultScripts is the location of scripts downloaded from default location (io.openshift.s2i.scripts-url label). + DefaultScripts = "downloads" + string(os.PathSeparator) + "defaultScripts" + + // SourceScripts is the location of scripts downloaded with application sources. + SourceScripts = "upload" + string(os.PathSeparator) + "src" + string(os.PathSeparator) + ".s2i" + string(os.PathSeparator) + "bin" + + // UploadScripts is the location of scripts that will be uploaded to the image during STI build. + UploadScripts = "upload" + string(os.PathSeparator) + "scripts" + + // Source is the location of application sources. + Source = "upload" + string(os.PathSeparator) + "src" + + // Injections is the location of injection content (configmaps+secrets). + Injections = "upload" + string(os.PathSeparator) + "injections" + + // ContextTmp is the location of applications sources off of a supplied context dir + ContextTmp = "upload" + string(os.PathSeparator) + "tmp" + + // RuntimeArtifactsDir is the location of application artifacts and scripts that will be copied into a runtime image. + RuntimeArtifactsDir = "upload" + string(os.PathSeparator) + "runtimeArtifacts" + + // IgnoreFile is the s2i version for ignore files like we see with .gitignore or .dockerignore .. initial impl mirrors documented .dockerignore capabilities + IgnoreFile = ".s2iignore" +) diff --git a/vendor/github.com/openshift/source-to-image/pkg/api/doc.go b/vendor/github.com/openshift/source-to-image/pkg/api/doc.go new file mode 100644 index 0000000000..a6ec5fe373 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/api/doc.go @@ -0,0 +1,2 @@ +// Package api provides types used for processing s2i builds. +package api diff --git a/vendor/github.com/openshift/source-to-image/pkg/api/helpers.go b/vendor/github.com/openshift/source-to-image/pkg/api/helpers.go new file mode 100644 index 0000000000..eb7a06cb0c --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/api/helpers.go @@ -0,0 +1,42 @@ +package api + +import "time" + +// RecordStageAndStepInfo records details about each build stage and step +func RecordStageAndStepInfo(stages []StageInfo, stageName StageName, stepName StepName, startTime time.Time, endTime time.Time) []StageInfo { + // Make sure that the stages slice is initialized + if len(stages) == 0 { + stages = make([]StageInfo, 0) + } + + // If the stage already exists update the endTime and Duration, and append the new step. + for stageKey, stageVal := range stages { + if stageVal.Name == stageName { + stages[stageKey].DurationMilliseconds = endTime.Sub(stages[stageKey].StartTime).Nanoseconds() / int64(time.Millisecond) + if len(stages[stageKey].Steps) == 0 { + stages[stageKey].Steps = make([]StepInfo, 0) + } + stages[stageKey].Steps = append(stages[stageKey].Steps, StepInfo{ + Name: stepName, + StartTime: startTime, + DurationMilliseconds: endTime.Sub(startTime).Nanoseconds() / int64(time.Millisecond), + }) + return stages + } + } + + // If the stageName does not exist, add it to the slice along with the new step. + steps := make([]StepInfo, 0) + steps = append(steps, StepInfo{ + Name: stepName, + StartTime: startTime, + DurationMilliseconds: endTime.Sub(startTime).Nanoseconds() / int64(time.Millisecond), + }) + stages = append(stages, StageInfo{ + Name: stageName, + StartTime: startTime, + DurationMilliseconds: endTime.Sub(startTime).Nanoseconds() / int64(time.Millisecond), + Steps: steps, + }) + return stages +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/api/types.go b/vendor/github.com/openshift/source-to-image/pkg/api/types.go new file mode 100644 index 0000000000..626c94898e --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/api/types.go @@ -0,0 +1,638 @@ +package api + +import ( + "errors" + "fmt" + "net/url" + "path/filepath" + "strings" + "time" + + "github.com/openshift/source-to-image/pkg/scm/git" + utillog "github.com/openshift/source-to-image/pkg/util/log" + "github.com/openshift/source-to-image/pkg/util/user" +) + +var log = utillog.StderrLog + +// invalidFilenameCharacters contains a list of character we consider malicious +// when injecting the directories into containers. +const invalidFilenameCharacters = `;*?"<>|%#$!+{}&[],"'` + "`" + +const ( + // PullAlways means that we always attempt to pull the latest image. + PullAlways PullPolicy = "always" + + // PullNever means that we never pull an image, but only use a local image. + PullNever PullPolicy = "never" + + // PullIfNotPresent means that we pull if the image isn't present on disk. + PullIfNotPresent PullPolicy = "if-not-present" + + // DefaultBuilderPullPolicy specifies the default pull policy to use + DefaultBuilderPullPolicy = PullIfNotPresent + + // DefaultRuntimeImagePullPolicy specifies the default pull policy to use. + DefaultRuntimeImagePullPolicy = PullIfNotPresent + + // DefaultPreviousImagePullPolicy specifies policy for pulling the previously + // build Docker image when doing incremental build + DefaultPreviousImagePullPolicy = PullIfNotPresent +) + +// Config contains essential fields for performing build. +type Config struct { + // DisplayName is a result image display-name label. This defaults to the + // output image name. + DisplayName string + + // Description is a result image description label. The default is no + // description. + Description string + + // BuilderImage describes which image is used for building the result images. + BuilderImage string + + // BuilderImageVersion provides optional version information about the builder image. + BuilderImageVersion string + + // BuilderBaseImageVersion provides optional version information about the builder base image. + BuilderBaseImageVersion string + + // RuntimeImage specifies the image that will be a base for resulting image + // and will be used for running an application. By default, BuilderImage is + // used for building and running, but the latter may be overridden. + RuntimeImage string + + // RuntimeImagePullPolicy specifies when to pull a runtime image. + RuntimeImagePullPolicy PullPolicy + + // RuntimeAuthentication holds the authentication information for pulling the + // runtime Docker images from private repositories. + RuntimeAuthentication AuthConfig + + // RuntimeArtifacts specifies a list of source/destination pairs that will + // be copied from builder to a runtime image. Source can be a file or + // directory. Destination must be a directory. Regardless whether it + // is an absolute or relative path, it will be placed into image's WORKDIR. + // Destination also can be empty or equals to ".", in this case it just + // refers to a root of WORKDIR. + // In case it's empty, S2I will try to get this list from + // io.openshift.s2i.assemble-input-files label on a RuntimeImage. + RuntimeArtifacts VolumeList + + // DockerConfig describes how to access host docker daemon. + DockerConfig *DockerConfig + + // DockerCfgPath provides the path to the .dockercfg file + DockerCfgPath string + + // PullAuthentication holds the authentication information for pulling the + // Docker images from private repositories + PullAuthentication AuthConfig + + // IncrementalAuthentication holds the authentication information for pulling the + // previous image from private repositories + IncrementalAuthentication AuthConfig + + // DockerNetworkMode is used to set the docker network setting to --net=container: + // when the builder is invoked from a container. + DockerNetworkMode DockerNetworkMode + + // PreserveWorkingDir describes if working directory should be left after processing. + PreserveWorkingDir bool + + // IgnoreSubmodules determines whether we will attempt to pull in submodules + // (via --recursive or submodule init) + IgnoreSubmodules bool + + // Source URL describing the location of sources used to build the result image. + Source *git.URL + + // Tag is a result image tag name. + Tag string + + // BuilderPullPolicy specifies when to pull the builder image + BuilderPullPolicy PullPolicy + + // PreviousImagePullPolicy specifies when to pull the previously build image + // when doing incremental build + PreviousImagePullPolicy PullPolicy + + // Incremental describes whether to try to perform incremental build. + Incremental bool + + // IncrementalFromTag sets an alternative image tag to look for existing + // artifacts. Tag is used by default if this is not set. + IncrementalFromTag string + + // RemovePreviousImage describes if previous image should be removed after successful build. + // This applies only to incremental builds. + RemovePreviousImage bool + + // Environment is a map of environment variables to be passed to the image. + Environment EnvironmentList + + // EnvironmentFile provides the path to a file with list of environment + // variables. + EnvironmentFile string + + // LabelNamespace provides the namespace under which the labels will be generated. + LabelNamespace string + + // CallbackURL is a URL which is called upon successful build to inform about that fact. + CallbackURL string + + // ScriptsURL is a URL describing where to fetch the S2I scripts from during build process. + // This url can be a reference within the builder image if the scheme is specified as image:// + ScriptsURL string + + // BuilderImageLabels is a map containing the builder image labels for possible adjustment of fields + // on this object. + BuilderImageLabels map[string]string + + // Destination specifies a location where the untar operation will place its artifacts. + Destination string + + // WorkingDir describes temporary directory used for downloading sources, scripts and tar operations. + WorkingDir string + + // WorkingSourceDir describes the subdirectory off of WorkingDir set up during the repo download + // that is later used as the root for ignore processing + WorkingSourceDir string + + // LayeredBuild describes if this is build which layered scripts and sources on top of BuilderImage. + LayeredBuild bool + + // Operate quietly. Progress and assemble script output are not reported, only fatal errors. + // (default: false). + Quiet bool + + // ForceCopy results in only the file SCM plugin being used (i.e. no `git clone`); allows for empty directories to be included + // in resulting image (since git does not support that). + // (default: false). + ForceCopy bool + + // Specify a relative directory inside the application repository that should + // be used as a root directory for the application. + ContextDir string + + // AllowedUIDs is a list of user ranges of users allowed to run the builder image. + // If a range is specified and the builder (or runtime) image uses a non-numeric + // user or a user that is outside the specified range, then the build fails. + AllowedUIDs user.RangeList + + // AssembleUser specifies the user to run the assemble script in container + AssembleUser string + + // RunImage will trigger a "docker run ..." invocation of the produced image so the user + // can see if it operates as he would expect + RunImage bool + + // Usage allows for properly shortcircuiting s2i logic when `s2i usage` is invoked + Usage bool + + // Injections specifies a list source/destination folders that are injected to + // the container that runs assemble. + // All files we inject will be truncated after the assemble script finishes. + Injections VolumeList + + // CGroupLimits describes the cgroups limits that will be applied to any containers + // run by s2i. + CGroupLimits *CGroupLimits + + // DropCapabilities contains a list of capabilities to drop when executing containers + DropCapabilities []string + + // ScriptDownloadProxyConfig optionally specifies the http and https proxy + // to use when downloading scripts + ScriptDownloadProxyConfig *ProxyConfig + + // ExcludeRegExp contains a string representation of the regular expression desired for + // deciding which files to exclude from the tar stream + ExcludeRegExp string + + // BlockOnBuild prevents s2i from performing a docker build operation + // if one is necessary to execute ONBUILD commands, or to layer source code into + // the container for images that don't have a tar binary available, if the + // image contains ONBUILD commands that would be executed. + BlockOnBuild bool + + // HasOnBuild will be set to true if the builder image contains ONBUILD instructions + HasOnBuild bool + + // BuildVolumes specifies a list of volumes to mount to container running the + // build. + BuildVolumes []string + + // Labels specify labels and their values to be applied to the resulting image. Label keys + // must have non-zero length. The labels defined here override generated labels in case + // they have the same name. + Labels map[string]string + + // SourceInfo provides the info about the source to be built rather than relying + // on the Downloader to retrieve it. + SourceInfo *git.SourceInfo + + // SecurityOpt are passed as options to the docker containers launched by s2i. + SecurityOpt []string + + // KeepSymlinks indicates to copy symlinks as symlinks. Default behavior is to follow + // symlinks and copy files by content. + KeepSymlinks bool + + // AsDockerfile indicates the path where the Dockerfile should be written instead of building + // a new image. + AsDockerfile string + + // ImageWorkDir is the default working directory for the builder image. + ImageWorkDir string + + // ImageScriptsURL is the default location to find the assemble/run scripts for a builder image. + // This url can be a reference within the builder image if the scheme is specified as image:// + ImageScriptsURL string + // AddHost Add a line to /etc/hosts for test purpose or private use in LAN. Its format is host:IP,muliple hosts can be added by using multiple --add-host + AddHost []string + + // AssembleRuntimeUser specifies the user to run the assemble-runtime script in container + AssembleRuntimeUser string +} + +// EnvironmentSpec specifies a single environment variable. +type EnvironmentSpec struct { + Name string + Value string +} + +// EnvironmentList contains list of environment variables. +type EnvironmentList []EnvironmentSpec + +// ProxyConfig holds proxy configuration. +type ProxyConfig struct { + HTTPProxy *url.URL + HTTPSProxy *url.URL +} + +// CGroupLimits holds limits used to constrain container resources. +type CGroupLimits struct { + MemoryLimitBytes int64 + CPUShares int64 + CPUPeriod int64 + CPUQuota int64 + MemorySwap int64 + Parent string +} + +// VolumeSpec represents a single volume mount point. +type VolumeSpec struct { + // Source is a reference to the volume source. + Source string + // Destination is the path to mount the volume to - absolute or relative. + Destination string + // Keep indicates if the mounted data should be kept in the final image. + Keep bool +} + +// VolumeList contains list of VolumeSpec. +type VolumeList []VolumeSpec + +// DockerConfig contains the configuration for a Docker connection. +type DockerConfig struct { + // Endpoint is the docker network endpoint or socket + Endpoint string + + // CertFile is the certificate file path for a TLS connection + CertFile string + + // KeyFile is the key file path for a TLS connection + KeyFile string + + // CAFile is the certificate authority file path for a TLS connection + CAFile string + + // UseTLS indicates if TLS must be used + UseTLS bool + + // TLSVerify indicates if TLS peer must be verified + TLSVerify bool +} + +// AuthConfig is our abstraction of the Registry authorization information for whatever +// docker client we happen to be based on +type AuthConfig struct { + Username string + Password string + Email string + ServerAddress string +} + +// ContainerConfig is the abstraction of the docker client provider (formerly go-dockerclient, now either +// engine-api or kube docker client) container.Config type that is leveraged by s2i or origin +type ContainerConfig struct { + Labels map[string]string + Env []string +} + +// Image is the abstraction of the docker client provider (formerly go-dockerclient, now either +// engine-api or kube docker client) Image type that is leveraged by s2i or origin +type Image struct { + ID string + *ContainerConfig + Config *ContainerConfig +} + +// Result structure contains information from build process. +type Result struct { + + // Success describes whether the build was successful. + Success bool + + // Messages is a list of messages from build process. + Messages []string + + // WorkingDir describes temporary directory used for downloading sources, scripts and tar operations. + WorkingDir string + + // ImageID describes resulting image ID. + ImageID string + + // BuildInfo holds information about the result of a build. + BuildInfo BuildInfo +} + +// BuildInfo contains information about the build process. +type BuildInfo struct { + // Stages contains details about each build stage. + Stages []StageInfo + + // FailureReason is a camel case reason that is used by the machine to reply + // back to the OpenShift builder with information why any of the steps in the + // build failed. + FailureReason FailureReason +} + +// StageInfo contains details about a build stage. +type StageInfo struct { + // Name is the identifier for each build stage. + Name StageName + + // StartTime identifies when this stage started. + StartTime time.Time + + // DurationMilliseconds identifies how long this stage ran. + DurationMilliseconds int64 + + // Steps contains details about each build step within a build stage. + Steps []StepInfo +} + +// StageName is the identifier for each build stage. +type StageName string + +// Valid StageNames +const ( + // StagePullImages pulls the docker images. + StagePullImages StageName = "PullImages" + + //StageAssemble runs the assemble steps. + StageAssemble StageName = "Assemble" + + // StageBuild builds the source. + StageBuild StageName = "Build" + + // StageCommit commits the container. + StageCommit StageName = "CommitContainer" + + // StageRetrieve retrieves artifacts. + StageRetrieve StageName = "RetrieveArtifacts" +) + +// StepInfo contains details about a build step. +type StepInfo struct { + // Name is the identifier for each build step. + Name StepName + + // StartTime identifies when this step started. + StartTime time.Time + + // DurationMilliseconds identifies how long this step ran. + DurationMilliseconds int64 +} + +// StepName is the identifier for each build step. +type StepName string + +// Valid StepNames +const ( + // StepPullBuilderImage pulls the builder image. + StepPullBuilderImage StepName = "PullBuilderImage" + + // StepPullPreviousImage pulls the previous image for an incremental build. + StepPullPreviousImage StepName = "PullPreviousImage" + + // StepPullRuntimeImage pull the runtime image. + StepPullRuntimeImage StepName = "PullRuntimeImage" + + // StepAssembleBuildScripts runs the assemble scripts. + StepAssembleBuildScripts StepName = "AssembleBuildScripts" + + // StepBuildDockerImage builds the Docker image for layered builds. + StepBuildDockerImage StepName = "BuildDockerImage" + + // StepCommitContainer commits the container to the builder image. + StepCommitContainer StepName = "CommitContainer" + + // StepRetrievePreviousArtifacts restores archived artifacts from the previous build. + StepRetrievePreviousArtifacts StepName = "RetrievePreviousArtifacts" +) + +// StepFailureReason holds the type of failure that occurred during the build +// process. +type StepFailureReason string + +// StepFailureMessage holds the detailed message of a failure. +type StepFailureMessage string + +// FailureReason holds the type of failure that occurred during the build +// process. +type FailureReason struct { + Reason StepFailureReason + Message StepFailureMessage +} + +// InstallResult structure describes the result of install operation +type InstallResult struct { + + // Script describes which script this result refers to + Script string + + // URL describes from where the script was taken + URL string + + // Downloaded describes if download operation happened, this will be true for + // external scripts, but false for scripts from inside the image + Downloaded bool + + // Installed describes if script was installed to upload directory + Installed bool + + // Error describes last error encountered during install operation + Error error + + // FailedSources is a list of sources that were attempted but failed + // when downloading this script + FailedSources []string +} + +// DockerNetworkMode specifies the network mode setting for the docker container +type DockerNetworkMode string + +const ( + // DockerNetworkModeHost places the container in the default (host) network namespace. + DockerNetworkModeHost DockerNetworkMode = "host" + // DockerNetworkModeBridge instructs docker to create a network namespace for this container connected to the docker0 bridge via a veth-pair. + DockerNetworkModeBridge DockerNetworkMode = "bridge" + // DockerNetworkModeContainerPrefix is the string prefix used by NewDockerNetworkModeContainer. + DockerNetworkModeContainerPrefix string = "container:" + // DockerNetworkModeNetworkNamespacePrefix is the string prefix used when sharing a namespace from a CRI-O container. + DockerNetworkModeNetworkNamespacePrefix string = "netns:" +) + +// NewDockerNetworkModeContainer creates a DockerNetworkMode value which instructs docker to place the container in the network namespace of an existing container. +// It can be used, for instance, to place the s2i container in the network namespace of the infrastructure container of a k8s pod. +func NewDockerNetworkModeContainer(id string) DockerNetworkMode { + return DockerNetworkMode(DockerNetworkModeContainerPrefix + id) +} + +// PullPolicy specifies a type for the method used to retrieve the Docker image +type PullPolicy string + +// String implements the String() function of pflags.Value so this can be used as +// command line parameter. +// This method is really used just to show the default value when printing help. +// It will not default the configuration. +func (p *PullPolicy) String() string { + if len(string(*p)) == 0 { + return string(DefaultBuilderPullPolicy) + } + return string(*p) +} + +// Type implements the Type() function of pflags.Value interface +func (p *PullPolicy) Type() string { + return "string" +} + +// Set implements the Set() function of pflags.Value interface +// The valid options are "always", "never" or "if-not-present" +func (p *PullPolicy) Set(v string) error { + switch v { + case "always": + *p = PullAlways + case "never": + *p = PullNever + case "if-not-present": + *p = PullIfNotPresent + default: + return fmt.Errorf("invalid value %q, valid values are: always, never or if-not-present", v) + } + return nil +} + +// IsInvalidFilename verifies if the provided filename contains malicious +// characters. +func IsInvalidFilename(name string) bool { + return strings.ContainsAny(name, invalidFilenameCharacters) +} + +// Set implements the Set() function of pflags.Value interface. +// This function parses the string that contains source:destination pair. +// When the destination is not specified, the source get copied into current +// working directory in container. +func (l *VolumeList) Set(value string) error { + volumes := strings.Split(value, ";") + newVols := make([]VolumeSpec, len(volumes)) + for i, v := range volumes { + spec, err := l.parseSpec(v) + if err != nil { + return err + } + newVols[i] = *spec + } + *l = append(*l, newVols...) + return nil +} + +func (l *VolumeList) parseSpec(value string) (*VolumeSpec, error) { + if len(value) == 0 { + return nil, errors.New("invalid format, must be source:destination") + } + var mount []string + pos := strings.LastIndex(value, ":") + if pos == -1 { + mount = []string{value, ""} + } else { + mount = []string{value[:pos], value[pos+1:]} + } + mount[0] = strings.Trim(mount[0], `"'`) + mount[1] = strings.Trim(mount[1], `"'`) + s := &VolumeSpec{Source: filepath.Clean(mount[0]), Destination: filepath.ToSlash(filepath.Clean(mount[1]))} + if IsInvalidFilename(s.Source) || IsInvalidFilename(s.Destination) { + return nil, fmt.Errorf("invalid characters in filename: %q", value) + } + return s, nil +} + +// String implements the String() function of pflags.Value interface. +func (l *VolumeList) String() string { + result := []string{} + for _, i := range *l { + result = append(result, strings.Join([]string{i.Source, i.Destination}, ":")) + } + return strings.Join(result, ",") +} + +// Type implements the Type() function of pflags.Value interface. +func (l *VolumeList) Type() string { + return "string" +} + +// Set implements the Set() function of pflags.Value interface. +func (e *EnvironmentList) Set(value string) error { + parts := strings.SplitN(value, "=", 2) + if len(parts) != 2 || len(parts[0]) == 0 { + return fmt.Errorf("invalid environment format %q, must be NAME=VALUE", value) + } + if strings.Contains(parts[1], ",") && strings.Contains(parts[1], "=") { + log.Warningf("DEPRECATED: Use multiple -e flags to specify multiple environment variables instead of comma (%q)", value) + } + *e = append(*e, EnvironmentSpec{ + Name: strings.TrimSpace(parts[0]), + Value: strings.TrimSpace(parts[1]), + }) + return nil +} + +// String implements the String() function of pflags.Value interface. +func (e *EnvironmentList) String() string { + result := []string{} + for _, i := range *e { + result = append(result, strings.Join([]string{i.Name, i.Value}, "=")) + } + return strings.Join(result, ",") +} + +// Type implements the Type() function of pflags.Value interface. +func (e *EnvironmentList) Type() string { + return "string" +} + +// AsBinds converts the list of volume definitions to go-dockerclient compatible +// list of bind mounts. +func (l *VolumeList) AsBinds() []string { + result := make([]string, len(*l)) + for index, v := range *l { + result[index] = strings.Join([]string{v.Source, v.Destination}, ":") + } + return result +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/api/validation/validation.go b/vendor/github.com/openshift/source-to-image/pkg/api/validation/validation.go new file mode 100644 index 0000000000..f965fcf7d9 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/api/validation/validation.go @@ -0,0 +1,116 @@ +package validation + +import ( + "fmt" + "strings" + + "github.com/docker/distribution/reference" + + "github.com/openshift/source-to-image/pkg/api" +) + +// ValidateConfig returns a list of error from validation. +func ValidateConfig(config *api.Config) []Error { + allErrs := []Error{} + if len(config.BuilderImage) == 0 { + allErrs = append(allErrs, NewFieldRequired("builderImage")) + } + switch config.BuilderPullPolicy { + case api.PullNever, api.PullAlways, api.PullIfNotPresent: + default: + allErrs = append(allErrs, NewFieldInvalidValue("builderPullPolicy")) + } + if config.DockerConfig == nil || len(config.DockerConfig.Endpoint) == 0 { + allErrs = append(allErrs, NewFieldRequired("dockerConfig.endpoint")) + } + if config.DockerNetworkMode != "" && !validateDockerNetworkMode(config.DockerNetworkMode) { + allErrs = append(allErrs, NewFieldInvalidValue("dockerNetworkMode")) + } + if config.Labels != nil { + for k := range config.Labels { + if len(k) == 0 { + allErrs = append(allErrs, NewFieldInvalidValue("labels")) + } + } + } + if config.Tag != "" { + if err := validateDockerReference(config.Tag); err != nil { + allErrs = append(allErrs, NewFieldInvalidValueWithReason("tag", err.Error())) + } + } + return allErrs +} + +// validateDockerNetworkMode checks wether the network mode conforms to the docker remote API specification (v1.19) +// Supported values are: bridge, host, container:, and netns:/proc//ns/net +func validateDockerNetworkMode(mode api.DockerNetworkMode) bool { + switch mode { + case api.DockerNetworkModeBridge, api.DockerNetworkModeHost: + return true + } + if strings.HasPrefix(string(mode), api.DockerNetworkModeContainerPrefix) { + return true + } + if strings.HasPrefix(string(mode), api.DockerNetworkModeNetworkNamespacePrefix) { + return true + } + return false +} + +func validateDockerReference(ref string) error { + _, err := reference.Parse(ref) + return err +} + +// NewFieldRequired returns a *ValidationError indicating "value required" +func NewFieldRequired(field string) Error { + return Error{Type: ErrorTypeRequired, Field: field} +} + +// NewFieldInvalidValue returns a ValidationError indicating "invalid value" +func NewFieldInvalidValue(field string) Error { + return Error{Type: ErrorInvalidValue, Field: field} +} + +// NewFieldInvalidValueWithReason returns a ValidationError indicating "invalid value" and a reason for the error +func NewFieldInvalidValueWithReason(field, reason string) Error { + return Error{Type: ErrorInvalidValue, Field: field, Reason: reason} +} + +// ErrorType is a machine readable value providing more detail about why a field +// is invalid. +type ErrorType string + +const ( + // ErrorTypeRequired is used to report required values that are not provided + // (e.g. empty strings, null values, or empty arrays). + ErrorTypeRequired ErrorType = "FieldValueRequired" + + // ErrorInvalidValue is used to report values that do not conform to the + // expected schema. + ErrorInvalidValue ErrorType = "InvalidValue" +) + +// Error is an implementation of the 'error' interface, which represents an +// error of validation. +type Error struct { + Type ErrorType + Field string + Reason string +} + +func (v Error) Error() string { + var msg string + switch v.Type { + case ErrorInvalidValue: + msg = fmt.Sprintf("Invalid value specified for %q", v.Field) + case ErrorTypeRequired: + msg = fmt.Sprintf("Required value not specified for %q", v.Field) + default: + msg = fmt.Sprintf("%s: %s", v.Type, v.Field) + } + if len(v.Reason) > 0 { + msg = fmt.Sprintf("%s: %s", msg, v.Reason) + } + return msg +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/build/cleanup.go b/vendor/github.com/openshift/source-to-image/pkg/build/cleanup.go new file mode 100644 index 0000000000..dcb972d6cc --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/build/cleanup.go @@ -0,0 +1,47 @@ +package build + +import ( + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/docker" + "github.com/openshift/source-to-image/pkg/util/fs" + utillog "github.com/openshift/source-to-image/pkg/util/log" +) + +var log = utillog.StderrLog + +// DefaultCleaner provides a cleaner for most STI build use-cases. It cleans the +// temporary directories created by STI build and it also cleans the temporary +// Docker images produced by LayeredBuild +type DefaultCleaner struct { + fs fs.FileSystem + docker docker.Docker +} + +// NewDefaultCleaner creates a new instance of the default Cleaner implementation +func NewDefaultCleaner(fs fs.FileSystem, docker docker.Docker) Cleaner { + return &DefaultCleaner{ + fs: fs, + docker: docker, + } +} + +// Cleanup removes the temporary directories where the sources were stored for build. +func (c *DefaultCleaner) Cleanup(config *api.Config) { + if config.PreserveWorkingDir { + log.V(2).Infof("Temporary directory %q will be saved, not deleted", config.WorkingDir) + } else { + log.V(2).Infof("Removing temporary directory %s", config.WorkingDir) + if err := c.fs.RemoveDirectory(config.WorkingDir); err != nil { + log.Warningf("Error removing temporary directory %q: %v", config.WorkingDir, err) + } + } + if config.LayeredBuild { + // config.LayeredBuild is true only when layered build was finished successfully. + // Also in this case config.BuilderImage contains name of the new just built image, + // not the original one that was specified by the user. + log.V(2).Infof("Removing temporary image %s", config.BuilderImage) + if err := c.docker.RemoveImage(config.BuilderImage); err != nil { + log.Warningf("Error removing temporary image %s: %v", config.BuilderImage, err) + } + } +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/build/config.go b/vendor/github.com/openshift/source-to-image/pkg/build/config.go new file mode 100644 index 0000000000..27353e1ff1 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/build/config.go @@ -0,0 +1,59 @@ +package build + +import ( + "errors" + "fmt" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/api/constants" + "github.com/openshift/source-to-image/pkg/docker" + "github.com/openshift/source-to-image/pkg/scm/git" +) + +// GenerateConfigFromLabels generates the S2I Config struct from the Docker +// image labels. +func GenerateConfigFromLabels(config *api.Config, metadata *docker.PullResult) error { + if config == nil { + return errors.New("config must be provided to GenerateConfigFromLabels") + } + if metadata == nil { + return errors.New("image metadata must be provided to GenerateConfigFromLabels") + } + + labels := metadata.Image.Config.Labels + + if builderVersion, ok := labels[constants.BuilderVersionLabel]; ok { + config.BuilderImageVersion = builderVersion + config.BuilderBaseImageVersion = labels[constants.BuilderBaseVersionLabel] + } + + config.ScriptsURL = labels[constants.ScriptsURLLabel] + if len(config.ScriptsURL) == 0 { + // FIXME: Backward compatibility + config.ScriptsURL = labels[constants.DeprecatedScriptsURLLabel] + } + + config.Description = labels[constants.KubernetesDescriptionLabel] + config.DisplayName = labels[constants.KubernetesDisplayNameLabel] + + if builder, ok := labels[constants.BuildImageLabel]; ok { + config.BuilderImage = builder + } else { + return fmt.Errorf("required label %q not found in image", constants.BuildImageLabel) + } + + if repo, ok := labels[constants.BuildSourceLocationLabel]; ok { + source, err := git.Parse(repo) + if err != nil { + return fmt.Errorf("couldn't parse label %q value %s: %v", constants.BuildSourceLocationLabel, repo, err) + } + config.Source = source + } else { + return fmt.Errorf("required label %q not found in image", constants.BuildSourceLocationLabel) + } + + config.ContextDir = labels[constants.BuildSourceContextDirLabel] + config.Source.URL.Fragment = labels[constants.BuildCommitRefLabel] + + return nil +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/build/interfaces.go b/vendor/github.com/openshift/source-to-image/pkg/build/interfaces.go new file mode 100644 index 0000000000..f9f72bbf73 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/build/interfaces.go @@ -0,0 +1,73 @@ +package build + +import ( + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/scm/git" +) + +// Builder is the interface that provides basic methods all implementation +// should have. +// Build method executes the build based on Request and returns the Result. +type Builder interface { + Build(*api.Config) (*api.Result, error) +} + +// Preparer provides the Prepare method for builders that need to prepare source +// code before it gets passed to the build. +type Preparer interface { + Prepare(*api.Config) error +} + +// Cleaner provides the Cleanup method for builders that need to cleanup +// temporary containers or directories after build execution finish. +type Cleaner interface { + Cleanup(*api.Config) +} + +// IncrementalBuilder provides methods that is used for builders that implements +// the 'incremental' build workflow. +// The Exists method checks if the artifacts from the previous build exists +// and if they can be used in the current build. +// The Save method stores the artifacts for the next build. +type IncrementalBuilder interface { + Exists(*api.Config) bool + Save(*api.Config) error +} + +// ScriptsHandler provides an interface for executing the scripts +type ScriptsHandler interface { + Execute(string, string, *api.Config) error +} + +// Downloader provides methods for downloading the application source code +type Downloader interface { + Download(*api.Config) (*git.SourceInfo, error) +} + +// Ignorer provides ignore file processing on source tree +// NOTE: raised to this level for possible future extensions to +// support say both .gitignore and .dockerignore level functionality +// ( currently do .dockerignore) +type Ignorer interface { + Ignore(*api.Config) error +} + +// SourceHandler is a wrapper for STI strategy Downloader and Preparer which +// allows to use Download and Prepare functions from the STI strategy. +type SourceHandler interface { + Downloader + Preparer + Ignorer +} + +// LayeredDockerBuilder represents a minimal Docker builder interface that is +// used to execute the layered Docker build with the application source. +type LayeredDockerBuilder interface { + Builder +} + +// Overrides are interfaces that may be passed into build strategies to +// alter the behavior of a strategy. +type Overrides struct { + Downloader Downloader +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/build/strategies/doc.go b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/doc.go new file mode 100644 index 0000000000..a935cbee47 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/doc.go @@ -0,0 +1 @@ +package strategies diff --git a/vendor/github.com/openshift/source-to-image/pkg/build/strategies/dockerfile/dockerfile.go b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/dockerfile/dockerfile.go new file mode 100644 index 0000000000..d8e66009b7 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/dockerfile/dockerfile.go @@ -0,0 +1,494 @@ +package dockerfile + +import ( + "bytes" + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/api/constants" + "github.com/openshift/source-to-image/pkg/build" + s2ierr "github.com/openshift/source-to-image/pkg/errors" + "github.com/openshift/source-to-image/pkg/ignore" + "github.com/openshift/source-to-image/pkg/scm" + "github.com/openshift/source-to-image/pkg/scm/downloaders/file" + "github.com/openshift/source-to-image/pkg/scm/git" + "github.com/openshift/source-to-image/pkg/scripts" + "github.com/openshift/source-to-image/pkg/util" + "github.com/openshift/source-to-image/pkg/util/fs" + utillog "github.com/openshift/source-to-image/pkg/util/log" + utilstatus "github.com/openshift/source-to-image/pkg/util/status" + "github.com/openshift/source-to-image/pkg/util/user" +) + +const ( + defaultDestination = "/tmp" + defaultScriptsDir = "/usr/libexec/s2i" +) + +var ( + log = utillog.StderrLog + + // List of directories that needs to be present inside working dir + workingDirs = []string{ + constants.UploadScripts, + constants.Source, + constants.DefaultScripts, + constants.UserScripts, + } +) + +// Dockerfile builders produce a Dockerfile rather than an image. +// Building the dockerfile w/ the right context will result in +// an application image being produced. +type Dockerfile struct { + fs fs.FileSystem + uploadScriptsDir string + uploadSrcDir string + sourceInfo *git.SourceInfo + result *api.Result + ignorer build.Ignorer +} + +// New creates a Dockerfile builder. +func New(config *api.Config, fs fs.FileSystem) (*Dockerfile, error) { + return &Dockerfile{ + fs: fs, + // where we will get the assemble/run scripts from on the host machine, + // if any are provided. + uploadScriptsDir: constants.UploadScripts, + uploadSrcDir: constants.Source, + result: &api.Result{}, + ignorer: &ignore.DockerIgnorer{}, + }, nil +} + +// Build produces a Dockerfile that when run with the correct filesystem +// context, will produce the application image. +func (builder *Dockerfile) Build(config *api.Config) (*api.Result, error) { + + // Handle defaulting of the configuration that is unique to the dockerfile strategy + if strings.HasSuffix(config.AsDockerfile, string(os.PathSeparator)) { + config.AsDockerfile = config.AsDockerfile + "Dockerfile" + } + if len(config.AssembleUser) == 0 { + config.AssembleUser = "1001" + } + if !user.IsUserAllowed(config.AssembleUser, &config.AllowedUIDs) { + builder.setFailureReason(utilstatus.ReasonAssembleUserForbidden, utilstatus.ReasonMessageAssembleUserForbidden) + return builder.result, s2ierr.NewUserNotAllowedError(config.AssembleUser, false) + } + + dir, _ := filepath.Split(config.AsDockerfile) + if len(dir) == 0 { + dir = "." + } + config.PreserveWorkingDir = true + config.WorkingDir = dir + + if config.BuilderImage == "" { + builder.setFailureReason(utilstatus.ReasonGenericS2IBuildFailed, utilstatus.ReasonMessageGenericS2iBuildFailed) + return builder.result, errors.New("builder image name cannot be empty") + } + + if err := builder.Prepare(config); err != nil { + return builder.result, err + } + + if err := builder.CreateDockerfile(config); err != nil { + builder.setFailureReason(utilstatus.ReasonDockerfileCreateFailed, utilstatus.ReasonMessageDockerfileCreateFailed) + return builder.result, err + } + + builder.result.Success = true + + return builder.result, nil +} + +// CreateDockerfile takes the various inputs and creates the Dockerfile used by +// the docker cmd to create the image produced by s2i. +func (builder *Dockerfile) CreateDockerfile(config *api.Config) error { + log.V(4).Infof("Constructing image build context directory at %s", config.WorkingDir) + buffer := bytes.Buffer{} + + if len(config.ImageWorkDir) == 0 { + config.ImageWorkDir = "/opt/app-root/src" + } + + imageUser := config.AssembleUser + + // where files will land inside the new image. + scriptsDestDir := filepath.Join(getDestination(config), "scripts") + sourceDestDir := filepath.Join(getDestination(config), "src") + artifactsDestDir := filepath.Join(getDestination(config), "artifacts") + artifactsTar := sanitize(filepath.ToSlash(filepath.Join(defaultDestination, "artifacts.tar"))) + // hasAllScripts indicates that we blindly trust all scripts are provided in the image scripts dir + imageScriptsDir, hasAllScripts := getImageScriptsDir(config) + var providedScripts map[string]bool + if !hasAllScripts { + providedScripts = scanScripts(filepath.Join(config.WorkingDir, builder.uploadScriptsDir)) + } + + if config.Incremental { + imageTag := util.FirstNonEmpty(config.IncrementalFromTag, config.Tag) + if len(imageTag) == 0 { + return errors.New("Image tag is missing for incremental build") + } + // Incremental builds run via a multistage Dockerfile + buffer.WriteString(fmt.Sprintf("FROM %s as cached\n", imageTag)) + var artifactsScript string + if _, provided := providedScripts[constants.SaveArtifacts]; provided { + // switch to root to COPY and chown content + log.V(2).Infof("Override save-artifacts script is included in directory %q", builder.uploadScriptsDir) + buffer.WriteString("# Copying in override save-artifacts script\n") + buffer.WriteString("USER root\n") + artifactsScript = sanitize(filepath.ToSlash(filepath.Join(scriptsDestDir, "save-artifacts"))) + uploadScript := sanitize(filepath.ToSlash(filepath.Join(builder.uploadScriptsDir, "save-artifacts"))) + buffer.WriteString(fmt.Sprintf("COPY %s %s\n", uploadScript, artifactsScript)) + buffer.WriteString(fmt.Sprintf("RUN chown %s:0 %s\n", sanitize(imageUser), artifactsScript)) + } else { + buffer.WriteString(fmt.Sprintf("# Save-artifacts script sourced from builder image based on user input or image metadata.\n")) + artifactsScript = sanitize(filepath.ToSlash(filepath.Join(imageScriptsDir, "save-artifacts"))) + } + // switch to the image user if it is not root + if len(imageUser) > 0 && imageUser != "root" { + buffer.WriteString(fmt.Sprintf("USER %s\n", imageUser)) + } + buffer.WriteString(fmt.Sprintf("RUN if [ -s %[1]s ]; then %[1]s > %[2]s; else touch %[2]s; fi\n", artifactsScript, artifactsTar)) + } + + // main stage of the Dockerfile + buffer.WriteString(fmt.Sprintf("FROM %s\n", config.BuilderImage)) + + imageLabels := util.GenerateOutputImageLabels(builder.sourceInfo, config) + for k, v := range config.Labels { + imageLabels[k] = v + } + + if len(config.ScriptsURL) > 0 { + imageLabels[constants.ScriptsURLLabel] = config.ScriptsURL + } + + if len(config.Destination) > 0 { + imageLabels[constants.DestinationLabel] = config.Destination + } + + if len(imageLabels) > 0 { + first := true + buffer.WriteString("LABEL ") + for k, v := range imageLabels { + if !first { + buffer.WriteString(fmt.Sprintf(" \\\n ")) + } + buffer.WriteString(fmt.Sprintf("%q=%q", k, v)) + first = false + } + buffer.WriteString("\n") + } + + env := createBuildEnvironment(config.WorkingDir, config.Environment) + buffer.WriteString(fmt.Sprintf("%s", env)) + + // run as root to COPY and chown source content + buffer.WriteString("USER root\n") + chownList := make([]string, 0) + + if config.Incremental { + // COPY artifacts.tar from the `cached` stage + buffer.WriteString(fmt.Sprintf("COPY --from=cached %[1]s %[1]s\n", artifactsTar)) + chownList = append(chownList, artifactsTar) + } + + if len(providedScripts) > 0 { + // Only COPY scripts dir if required scripts are present and needed. + // Even if the "scripts" dir exists, the COPY would fail if it was empty. + log.V(2).Infof("Override scripts are included in directory %q", builder.uploadScriptsDir) + scriptsDest := sanitize(filepath.ToSlash(scriptsDestDir)) + buffer.WriteString("# Copying in override assemble/run scripts\n") + buffer.WriteString(fmt.Sprintf("COPY %s %s\n", sanitize(filepath.ToSlash(builder.uploadScriptsDir)), scriptsDest)) + chownList = append(chownList, scriptsDest) + } + + // copy in the user's source code. + buffer.WriteString("# Copying in source code\n") + sourceDest := sanitize(filepath.ToSlash(sourceDestDir)) + buffer.WriteString(fmt.Sprintf("COPY %s %s\n", sanitize(filepath.ToSlash(builder.uploadSrcDir)), sourceDest)) + chownList = append(chownList, sourceDest) + + // add injections + log.V(4).Infof("Processing injected inputs: %#v", config.Injections) + config.Injections = util.FixInjectionsWithRelativePath(config.ImageWorkDir, config.Injections) + log.V(4).Infof("Processed injected inputs: %#v", config.Injections) + + if len(config.Injections) > 0 { + buffer.WriteString("# Copying in injected content\n") + } + for _, injection := range config.Injections { + src := sanitize(filepath.ToSlash(filepath.Join(constants.Injections, injection.Source))) + dest := sanitize(filepath.ToSlash(injection.Destination)) + buffer.WriteString(fmt.Sprintf("COPY %s %s\n", src, dest)) + chownList = append(chownList, dest) + } + + // chown directories COPYed to image + if len(chownList) > 0 { + buffer.WriteString("# Change file ownership to the assemble user. Builder image must support chown command.\n") + buffer.WriteString(fmt.Sprintf("RUN chown -R %s:0", sanitize(imageUser))) + for _, dir := range chownList { + buffer.WriteString(fmt.Sprintf(" %s", dir)) + } + buffer.WriteString("\n") + } + + // run remaining commands as the image user + if len(imageUser) > 0 && imageUser != "root" { + buffer.WriteString(fmt.Sprintf("USER %s\n", imageUser)) + } + + if config.Incremental { + buffer.WriteString("# Extract artifact content\n") + buffer.WriteString(fmt.Sprintf("RUN if [ -s %[1]s ]; then mkdir -p %[2]s; tar -xf %[1]s -C %[2]s; fi && \\\n", artifactsTar, sanitize(filepath.ToSlash(artifactsDestDir)))) + buffer.WriteString(fmt.Sprintf(" rm %s\n", artifactsTar)) + } + + if _, provided := providedScripts[constants.Assemble]; provided { + buffer.WriteString(fmt.Sprintf("RUN %s\n", sanitize(filepath.ToSlash(filepath.Join(scriptsDestDir, "assemble"))))) + } else { + buffer.WriteString(fmt.Sprintf("# Assemble script sourced from builder image based on user input or image metadata.\n")) + buffer.WriteString(fmt.Sprintf("# If this file does not exist in the image, the build will fail.\n")) + buffer.WriteString(fmt.Sprintf("RUN %s\n", sanitize(filepath.ToSlash(filepath.Join(imageScriptsDir, "assemble"))))) + } + + filesToDelete, err := util.ListFilesToTruncate(builder.fs, config.Injections) + if err != nil { + return err + } + if len(filesToDelete) > 0 { + wroteRun := false + buffer.WriteString("# Cleaning up injected secret content\n") + for _, file := range filesToDelete { + if !wroteRun { + buffer.WriteString(fmt.Sprintf("RUN rm %s", file)) + wroteRun = true + continue + } + buffer.WriteString(fmt.Sprintf(" && \\\n")) + buffer.WriteString(fmt.Sprintf(" rm %s", file)) + } + buffer.WriteString("\n") + } + + if _, provided := providedScripts[constants.Run]; provided { + buffer.WriteString(fmt.Sprintf("CMD %s\n", sanitize(filepath.ToSlash(filepath.Join(scriptsDestDir, "run"))))) + } else { + buffer.WriteString(fmt.Sprintf("# Run script sourced from builder image based on user input or image metadata.\n")) + buffer.WriteString(fmt.Sprintf("# If this file does not exist in the image, the build will fail.\n")) + buffer.WriteString(fmt.Sprintf("CMD %s\n", sanitize(filepath.ToSlash(filepath.Join(imageScriptsDir, "run"))))) + } + + if err := builder.fs.WriteFile(filepath.Join(config.AsDockerfile), buffer.Bytes()); err != nil { + return err + } + log.V(2).Infof("Wrote custom Dockerfile to %s", config.AsDockerfile) + return nil +} + +// Prepare prepares the source code and tar for build. +// NOTE: this func serves both the sti and onbuild strategies, as the OnBuild +// struct Build func leverages the STI struct Prepare func directly below. +func (builder *Dockerfile) Prepare(config *api.Config) error { + var err error + + if len(config.WorkingDir) == 0 { + if config.WorkingDir, err = builder.fs.CreateWorkingDirectory(); err != nil { + builder.setFailureReason(utilstatus.ReasonFSOperationFailed, utilstatus.ReasonMessageFSOperationFailed) + return err + } + } + + builder.result.WorkingDir = config.WorkingDir + + // Setup working directories + for _, v := range workingDirs { + if err = builder.fs.MkdirAllWithPermissions(filepath.Join(config.WorkingDir, v), 0755); err != nil { + builder.setFailureReason(utilstatus.ReasonFSOperationFailed, utilstatus.ReasonMessageFSOperationFailed) + return err + } + } + + // Default - install scripts specified by image metadata. + // Typically this will point to an image:// URL, and no scripts are downloaded. + // However, if builder image labels are specified, we'll go with those and not the default + if config.BuilderImageLabels == nil { + builder.installScripts(config.ImageScriptsURL, config) + } + + // Fetch sources, since their .s2i/bin might contain s2i scripts which override defaults. + if config.Source != nil { + downloader, err := scm.DownloaderForSource(builder.fs, config.Source, config.ForceCopy) + if err != nil { + builder.setFailureReason(utilstatus.ReasonFetchSourceFailed, utilstatus.ReasonMessageFetchSourceFailed) + return err + } + if builder.sourceInfo, err = downloader.Download(config); err != nil { + builder.setFailureReason(utilstatus.ReasonFetchSourceFailed, utilstatus.ReasonMessageFetchSourceFailed) + switch err.(type) { + case file.RecursiveCopyError: + return fmt.Errorf("input source directory contains the target directory for the build, check that your Dockerfile output path does not reside within your input source path: %v", err) + } + return err + } + if config.SourceInfo != nil { + builder.sourceInfo = config.SourceInfo + } + } + + // Install scripts provided by user, overriding all others. + // This _could_ be an image:// URL, which would override any scripts above. + urlScripts := builder.installScripts(config.ScriptsURL, config) + // If a ScriptsURL was specified, but no scripts were downloaded from it, throw an error + if len(config.ScriptsURL) > 0 { + failedCount := 0 + for _, result := range urlScripts { + if util.Includes(result.FailedSources, scripts.ScriptURLHandler) { + failedCount++ + } + } + if failedCount == len(urlScripts) { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonScriptsFetchFailed, + utilstatus.ReasonMessageScriptsFetchFailed, + ) + return fmt.Errorf("could not download any scripts from URL %v", config.ScriptsURL) + } + } + + // Stage any injection(secrets) content into the working dir so the dockerfile can reference it. + for i, injection := range config.Injections { + // strip the C: from windows paths because it's not valid in the middle of a path + // like upload/injections/C:/tempdir/injection1 + trimmedSrc := strings.TrimPrefix(injection.Source, filepath.VolumeName(injection.Source)) + dst := filepath.Join(config.WorkingDir, constants.Injections, trimmedSrc) + log.V(4).Infof("Copying injection content from %s to %s", injection.Source, dst) + if err := builder.fs.CopyContents(injection.Source, dst, nil); err != nil { + builder.setFailureReason(utilstatus.ReasonGenericS2IBuildFailed, utilstatus.ReasonMessageGenericS2iBuildFailed) + return err + } + config.Injections[i].Source = trimmedSrc + } + + // see if there is a .s2iignore file, and if so, read in the patterns and then + // search and delete on them. + err = builder.ignorer.Ignore(config) + if err != nil { + builder.setFailureReason(utilstatus.ReasonGenericS2IBuildFailed, utilstatus.ReasonMessageGenericS2iBuildFailed) + return err + } + return nil +} + +// installScripts installs scripts at the provided URL to the Dockerfile context +func (builder *Dockerfile) installScripts(scriptsURL string, config *api.Config) []api.InstallResult { + scriptInstaller := scripts.NewInstaller( + "", + scriptsURL, + config.ScriptDownloadProxyConfig, + nil, + api.AuthConfig{}, + builder.fs, + config, + ) + + // all scripts are optional, we trust the image contains scripts if we don't find them + // in the source repo. + return scriptInstaller.InstallOptional(append(scripts.RequiredScripts, scripts.OptionalScripts...), config.WorkingDir) +} + +// setFailureReason sets the builder's failure reason with the given reason and message. +func (builder *Dockerfile) setFailureReason(reason api.StepFailureReason, message api.StepFailureMessage) { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason(reason, message) +} + +// getDestination returns the destination directory from the config. +func getDestination(config *api.Config) string { + destination := config.Destination + if len(destination) == 0 { + destination = defaultDestination + } + return destination +} + +// getImageScriptsDir returns the directory containing the builder image scripts and a bool +// indicating that the directory is expected to contain all s2i scripts +func getImageScriptsDir(config *api.Config) (string, bool) { + if strings.HasPrefix(config.ScriptsURL, "image://") { + return strings.TrimPrefix(config.ScriptsURL, "image://"), true + } + scriptsURL, _ := util.AdjustConfigWithImageLabels(config) + if strings.HasPrefix(scriptsURL, "image://") { + return strings.TrimPrefix(scriptsURL, "image://"), true + } + if strings.HasPrefix(config.ImageScriptsURL, "image://") { + return strings.TrimPrefix(config.ImageScriptsURL, "image://"), false + } + return defaultScriptsDir, false +} + +// scanScripts returns a map of provided s2i scripts +func scanScripts(name string) map[string]bool { + scriptsMap := make(map[string]bool) + items, err := ioutil.ReadDir(name) + if os.IsNotExist(err) { + log.Warningf("Unable to access directory %q: %v", name, err) + } + if err != nil || len(items) == 0 { + return scriptsMap + } + + assembleProvided := false + runProvided := false + saveArtifactsProvided := false + for _, f := range items { + log.V(2).Infof("found override script file %s", f.Name()) + if f.Name() == constants.Run { + runProvided = true + scriptsMap[constants.Run] = true + } else if f.Name() == constants.Assemble { + assembleProvided = true + scriptsMap[constants.Assemble] = true + } else if f.Name() == constants.SaveArtifacts { + saveArtifactsProvided = true + scriptsMap[constants.SaveArtifacts] = true + } + if runProvided && assembleProvided && saveArtifactsProvided { + break + } + } + return scriptsMap +} + +func includes(arr []string, str string) bool { + for _, s := range arr { + if s == str { + return true + } + } + return false +} + +func sanitize(s string) string { + return strings.Replace(s, "\n", "\\n", -1) +} + +func createBuildEnvironment(sourcePath string, cfgEnv api.EnvironmentList) string { + s2iEnv, err := scripts.GetEnvironment(filepath.Join(sourcePath, constants.Source)) + if err != nil { + log.V(3).Infof("No user environment provided (%v)", err) + } + + return scripts.ConvertEnvironmentToDocker(append(s2iEnv, cfgEnv...)) +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/build/strategies/layered/layered.go b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/layered/layered.go new file mode 100644 index 0000000000..96e14603c7 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/layered/layered.go @@ -0,0 +1,227 @@ +package layered + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path" + "path/filepath" + "regexp" + "time" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/api/constants" + "github.com/openshift/source-to-image/pkg/build" + "github.com/openshift/source-to-image/pkg/docker" + s2ierr "github.com/openshift/source-to-image/pkg/errors" + "github.com/openshift/source-to-image/pkg/tar" + "github.com/openshift/source-to-image/pkg/util/fs" + utillog "github.com/openshift/source-to-image/pkg/util/log" + utilstatus "github.com/openshift/source-to-image/pkg/util/status" +) + +var log = utillog.StderrLog + +const defaultDestination = "/tmp" + +// A Layered builder builds images by first performing a docker build to inject +// (layer) the source code and s2i scripts into the builder image, prior to +// running the new image with the assemble script. This is necessary when the +// builder image does not include "sh" and "tar" as those tools are needed +// during the normal source injection process. +type Layered struct { + config *api.Config + docker docker.Docker + fs fs.FileSystem + tar tar.Tar + scripts build.ScriptsHandler + hasOnBuild bool +} + +// New creates a Layered builder. +func New(client docker.Client, config *api.Config, fs fs.FileSystem, scripts build.ScriptsHandler, overrides build.Overrides) (*Layered, error) { + excludePattern, err := regexp.Compile(config.ExcludeRegExp) + if err != nil { + return nil, err + } + + d := docker.New(client, config.PullAuthentication) + tarHandler := tar.New(fs) + tarHandler.SetExclusionPattern(excludePattern) + + return &Layered{ + docker: d, + config: config, + fs: fs, + tar: tarHandler, + scripts: scripts, + }, nil +} + +// getDestination returns the destination directory from the config. +func getDestination(config *api.Config) string { + destination := config.Destination + if len(destination) == 0 { + destination = defaultDestination + } + return destination +} + +// checkValidDirWithContents returns true if the parameter provided is a valid, +// accessible and non-empty directory. +func checkValidDirWithContents(name string) bool { + items, err := ioutil.ReadDir(name) + if os.IsNotExist(err) { + log.Warningf("Unable to access directory %q: %v", name, err) + } + return !(err != nil || len(items) == 0) +} + +// CreateDockerfile takes the various inputs and creates the Dockerfile used by +// the docker cmd to create the image produced by s2i. +func (builder *Layered) CreateDockerfile(config *api.Config) error { + buffer := bytes.Buffer{} + + user, err := builder.docker.GetImageUser(builder.config.BuilderImage) + if err != nil { + return err + } + + scriptsDir := filepath.Join(getDestination(config), "scripts") + sourcesDir := filepath.Join(getDestination(config), "src") + + uploadScriptsDir := path.Join(config.WorkingDir, constants.UploadScripts) + + buffer.WriteString(fmt.Sprintf("FROM %s\n", builder.config.BuilderImage)) + // only COPY scripts dir if required scripts are present, i.e. the dir is not empty; + // even if the "scripts" dir exists, the COPY would fail if it was empty + scriptsIncluded := checkValidDirWithContents(uploadScriptsDir) + if scriptsIncluded { + log.V(2).Infof("The scripts are included in %q directory", uploadScriptsDir) + buffer.WriteString(fmt.Sprintf("COPY scripts %s\n", filepath.ToSlash(scriptsDir))) + } else { + // if an err on reading or opening dir, can't copy it + log.V(2).Infof("Could not gather scripts from the directory %q", uploadScriptsDir) + } + buffer.WriteString(fmt.Sprintf("COPY src %s\n", filepath.ToSlash(sourcesDir))) + + //TODO: We need to account for images that may not have chown. There is a proposal + // to specify the owner for COPY here: https://github.com/docker/docker/pull/28499 + if len(user) > 0 { + buffer.WriteString("USER root\n") + if scriptsIncluded { + buffer.WriteString(fmt.Sprintf("RUN chown -R %s -- %s %s\n", user, filepath.ToSlash(scriptsDir), filepath.ToSlash(sourcesDir))) + } else { + buffer.WriteString(fmt.Sprintf("RUN chown -R %s -- %s\n", user, filepath.ToSlash(sourcesDir))) + } + buffer.WriteString(fmt.Sprintf("USER %s\n", user)) + } + + uploadDir := filepath.Join(builder.config.WorkingDir, "upload") + if err := builder.fs.WriteFile(filepath.Join(uploadDir, "Dockerfile"), buffer.Bytes()); err != nil { + return err + } + log.V(2).Infof("Writing custom Dockerfile to %s", uploadDir) + return nil +} + +// Build handles the `docker build` equivalent execution, returning the +// success/failure details. +func (builder *Layered) Build(config *api.Config) (*api.Result, error) { + buildResult := &api.Result{} + + if config.HasOnBuild && config.BlockOnBuild { + buildResult.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonOnBuildForbidden, + utilstatus.ReasonMessageOnBuildForbidden, + ) + return buildResult, errors.New("builder image uses ONBUILD instructions but ONBUILD is not allowed") + } + + if config.BuilderImage == "" { + buildResult.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return buildResult, errors.New("builder image name cannot be empty") + } + + if err := builder.CreateDockerfile(config); err != nil { + buildResult.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonDockerfileCreateFailed, + utilstatus.ReasonMessageDockerfileCreateFailed, + ) + return buildResult, err + } + + log.V(2).Info("Creating application source code image") + tarStream := builder.tar.CreateTarStreamReader(filepath.Join(config.WorkingDir, "upload"), false) + defer tarStream.Close() + + newBuilderImage := fmt.Sprintf("s2i-layered-temp-image-%d", time.Now().UnixNano()) + + outReader, outWriter := io.Pipe() + opts := docker.BuildImageOptions{ + Name: newBuilderImage, + Stdin: tarStream, + Stdout: outWriter, + CGroupLimits: config.CGroupLimits, + } + docker.StreamContainerIO(outReader, nil, func(s string) { log.V(2).Info(s) }) + + log.V(2).Infof("Building new image %s with scripts and sources already inside", newBuilderImage) + startTime := time.Now() + err := builder.docker.BuildImage(opts) + buildResult.BuildInfo.Stages = api.RecordStageAndStepInfo(buildResult.BuildInfo.Stages, api.StageBuild, api.StepBuildDockerImage, startTime, time.Now()) + if err != nil { + buildResult.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonDockerImageBuildFailed, + utilstatus.ReasonMessageDockerImageBuildFailed, + ) + return buildResult, err + } + + // upon successful build we need to modify current config + builder.config.LayeredBuild = true + // new image name + builder.config.BuilderImage = newBuilderImage + // see CreateDockerfile, conditional copy, location of scripts + scriptsIncluded := checkValidDirWithContents(path.Join(config.WorkingDir, constants.UploadScripts)) + log.V(2).Infof("Scripts dir has contents %v", scriptsIncluded) + if scriptsIncluded { + builder.config.ScriptsURL = "image://" + path.Join(getDestination(config), "scripts") + } else { + var err error + builder.config.ScriptsURL, err = builder.docker.GetScriptsURL(newBuilderImage) + if err != nil { + buildResult.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return buildResult, err + } + } + + log.V(2).Infof("Building %s using sti-enabled image", builder.config.Tag) + startTime = time.Now() + err = builder.scripts.Execute(constants.Assemble, config.AssembleUser, builder.config) + buildResult.BuildInfo.Stages = api.RecordStageAndStepInfo(buildResult.BuildInfo.Stages, api.StageAssemble, api.StepAssembleBuildScripts, startTime, time.Now()) + if err != nil { + buildResult.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonAssembleFailed, + utilstatus.ReasonMessageAssembleFailed, + ) + switch e := err.(type) { + case s2ierr.ContainerError: + return buildResult, s2ierr.NewAssembleError(builder.config.Tag, e.Output, e) + default: + return buildResult, err + } + } + buildResult.Success = true + + return buildResult, nil +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/build/strategies/onbuild/entrypoint.go b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/onbuild/entrypoint.go new file mode 100644 index 0000000000..d24b49af75 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/onbuild/entrypoint.go @@ -0,0 +1,60 @@ +package onbuild + +import ( + "errors" + "path/filepath" + "regexp" + + "github.com/openshift/source-to-image/pkg/util/fs" + utillog "github.com/openshift/source-to-image/pkg/util/log" +) + +var log = utillog.StderrLog + +var validEntrypoints = []*regexp.Regexp{ + regexp.MustCompile(`^run(\.sh)?$`), + regexp.MustCompile(`^start(\.sh)?$`), + regexp.MustCompile(`^exec(\.sh)?$`), + regexp.MustCompile(`^execute(\.sh)?$`), +} + +// GuessEntrypoint tries to guess the valid entrypoint from the source code +// repository. The valid entrypoints are defined above (run,start,exec,execute) +func GuessEntrypoint(fs fs.FileSystem, sourceDir string) (string, error) { + files, err := fs.ReadDir(sourceDir) + if err != nil { + return "", err + } + for _, f := range files { + if f.IsDir() || !f.Mode().IsRegular() { + continue + } + if isValidEntrypoint(fs, filepath.Join(sourceDir, f.Name())) { + log.V(2).Infof("Found valid ENTRYPOINT: %s", f.Name()) + return f.Name(), nil + } + } + return "", errors.New("no valid entrypoint specified") +} + +// isValidEntrypoint checks if the given file exists and if it is a regular +// file. Valid ENTRYPOINT must be an executable file, so the executable bit must +// be set. +func isValidEntrypoint(fs fs.FileSystem, path string) bool { + stat, err := fs.Stat(path) + if err != nil { + return false + } + found := false + for _, pattern := range validEntrypoints { + if pattern.MatchString(stat.Name()) { + found = true + break + } + } + if !found { + return false + } + mode := stat.Mode() + return mode&0111 != 0 +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/build/strategies/onbuild/onbuild.go b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/onbuild/onbuild.go new file mode 100644 index 0000000000..441ee95b62 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/onbuild/onbuild.go @@ -0,0 +1,195 @@ +package onbuild + +import ( + "bytes" + "fmt" + "io" + "os" + "path/filepath" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/api/constants" + "github.com/openshift/source-to-image/pkg/build" + "github.com/openshift/source-to-image/pkg/build/strategies/sti" + "github.com/openshift/source-to-image/pkg/docker" + "github.com/openshift/source-to-image/pkg/ignore" + "github.com/openshift/source-to-image/pkg/scm" + "github.com/openshift/source-to-image/pkg/scm/git" + "github.com/openshift/source-to-image/pkg/scripts" + "github.com/openshift/source-to-image/pkg/tar" + "github.com/openshift/source-to-image/pkg/util/cmd" + "github.com/openshift/source-to-image/pkg/util/fs" + utilstatus "github.com/openshift/source-to-image/pkg/util/status" +) + +// OnBuild strategy executes the simple Docker build in case the image does not +// support STI scripts but has ONBUILD instructions recorded. +type OnBuild struct { + docker docker.Docker + git git.Git + fs fs.FileSystem + tar tar.Tar + source build.SourceHandler + garbage build.Cleaner +} + +type onBuildSourceHandler struct { + build.Downloader + build.Preparer + build.Ignorer +} + +// New returns a new instance of OnBuild builder +func New(client docker.Client, config *api.Config, fs fs.FileSystem, overrides build.Overrides) (*OnBuild, error) { + dockerHandler := docker.New(client, config.PullAuthentication) + builder := &OnBuild{ + docker: dockerHandler, + git: git.New(fs, cmd.NewCommandRunner()), + fs: fs, + tar: tar.New(fs), + } + // Use STI Prepare() and download the 'run' script optionally. + s, err := sti.New(client, config, fs, overrides) + if err != nil { + return nil, err + } + s.SetScripts([]string{}, []string{constants.Assemble, constants.Run}) + + downloader := overrides.Downloader + if downloader == nil { + downloader, err = scm.DownloaderForSource(builder.fs, config.Source, config.ForceCopy) + if err != nil { + return nil, err + } + } + + builder.source = onBuildSourceHandler{ + Downloader: downloader, + Preparer: s, + Ignorer: &ignore.DockerIgnorer{}, + } + + builder.garbage = build.NewDefaultCleaner(builder.fs, builder.docker) + return builder, nil +} + +// Build executes the ONBUILD kind of build +func (builder *OnBuild) Build(config *api.Config) (*api.Result, error) { + buildResult := &api.Result{} + + if config.BlockOnBuild { + buildResult.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonOnBuildForbidden, + utilstatus.ReasonMessageOnBuildForbidden, + ) + return buildResult, fmt.Errorf("builder image uses ONBUILD instructions but ONBUILD is not allowed") + } + log.V(2).Info("Preparing the source code for build") + // Change the installation directory for this config to store scripts inside + // the application root directory. + if err := builder.source.Prepare(config); err != nil { + return buildResult, err + } + + // If necessary, copy the STI scripts into application root directory + builder.copySTIScripts(config) + + log.V(2).Info("Creating application Dockerfile") + if err := builder.CreateDockerfile(config); err != nil { + buildResult.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonDockerfileCreateFailed, + utilstatus.ReasonMessageDockerfileCreateFailed, + ) + return buildResult, err + } + + log.V(2).Info("Creating application source code image") + tarStream := builder.tar.CreateTarStreamReader(filepath.Join(config.WorkingDir, "upload", "src"), false) + defer tarStream.Close() + + outReader, outWriter := io.Pipe() + go io.Copy(os.Stdout, outReader) + + opts := docker.BuildImageOptions{ + Name: config.Tag, + Stdin: tarStream, + Stdout: outWriter, + CGroupLimits: config.CGroupLimits, + } + + log.V(2).Info("Building the application source") + if err := builder.docker.BuildImage(opts); err != nil { + buildResult.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonDockerImageBuildFailed, + utilstatus.ReasonMessageDockerImageBuildFailed, + ) + return buildResult, err + } + + log.V(2).Info("Cleaning up temporary containers") + builder.garbage.Cleanup(config) + + var imageID string + var err error + if len(opts.Name) > 0 { + if imageID, err = builder.docker.GetImageID(opts.Name); err != nil { + buildResult.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return buildResult, err + } + } + + return &api.Result{ + Success: true, + WorkingDir: config.WorkingDir, + ImageID: imageID, + }, nil +} + +// CreateDockerfile creates the ONBUILD Dockerfile +func (builder *OnBuild) CreateDockerfile(config *api.Config) error { + buffer := bytes.Buffer{} + uploadDir := filepath.Join(config.WorkingDir, "upload", "src") + buffer.WriteString(fmt.Sprintf("FROM %s\n", config.BuilderImage)) + entrypoint, err := GuessEntrypoint(builder.fs, uploadDir) + if err != nil { + return err + } + env, err := scripts.GetEnvironment(filepath.Join(config.WorkingDir, constants.Source)) + if err != nil { + log.V(1).Infof("Environment: %v", err) + } else { + buffer.WriteString(scripts.ConvertEnvironmentToDocker(env)) + } + // If there is an assemble script present, run it as part of the build process + // as the last thing. + if builder.hasAssembleScript(config) { + buffer.WriteString("RUN sh assemble\n") + } + // FIXME: This assumes that the WORKDIR is set to the application source root + // directory. + buffer.WriteString(fmt.Sprintf(`ENTRYPOINT ["./%s"]`+"\n", entrypoint)) + return builder.fs.WriteFile(filepath.Join(uploadDir, "Dockerfile"), buffer.Bytes()) +} + +func (builder *OnBuild) copySTIScripts(config *api.Config) { + scriptsPath := filepath.Join(config.WorkingDir, "upload", "scripts") + sourcePath := filepath.Join(config.WorkingDir, "upload", "src") + if _, err := builder.fs.Stat(filepath.Join(scriptsPath, constants.Run)); err == nil { + log.V(3).Info("Found S2I 'run' script, copying to application source dir") + builder.fs.Copy(filepath.Join(scriptsPath, constants.Run), filepath.Join(sourcePath, constants.Run), nil) + } + if _, err := builder.fs.Stat(filepath.Join(scriptsPath, constants.Assemble)); err == nil { + log.V(3).Info("Found S2I 'assemble' script, copying to application source dir") + builder.fs.Copy(filepath.Join(scriptsPath, constants.Assemble), filepath.Join(sourcePath, constants.Assemble), nil) + } +} + +// hasAssembleScript checks if the the assemble script is available +func (builder *OnBuild) hasAssembleScript(config *api.Config) bool { + assemblePath := filepath.Join(config.WorkingDir, "upload", "src", constants.Assemble) + _, err := builder.fs.Stat(assemblePath) + return err == nil +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/build/strategies/sti/postexecutorstep.go b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/sti/postexecutorstep.go new file mode 100644 index 0000000000..19c7417e8a --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/sti/postexecutorstep.go @@ -0,0 +1,574 @@ +package sti + +import ( + "archive/tar" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "path" + "path/filepath" + "strings" + "time" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/api/constants" + dockerpkg "github.com/openshift/source-to-image/pkg/docker" + s2ierr "github.com/openshift/source-to-image/pkg/errors" + s2itar "github.com/openshift/source-to-image/pkg/tar" + "github.com/openshift/source-to-image/pkg/util" + "github.com/openshift/source-to-image/pkg/util/fs" + utilstatus "github.com/openshift/source-to-image/pkg/util/status" +) + +const maximumLabelSize = 10240 + +type postExecutorStepContext struct { + // id of the previous image that we're holding because after committing the image, we'll lose it. + // Used only when build is incremental and RemovePreviousImage setting is enabled. + // See also: storePreviousImageStep and removePreviousImageStep + previousImageID string + + // Container id that will be committed. + // See also: commitImageStep + containerID string + + // Path to a directory in the image where scripts (for example, "run") will be placed. + // This location will be used for generation of the CMD directive. + // See also: commitImageStep + destination string + + // Image id created by committing the container. + // See also: commitImageStep and reportAboutSuccessStep + imageID string + + // Labels that will be passed to a callback. + // These labels are added to the image during commit. + // See also: commitImageStep and STI.Build() + labels map[string]string +} + +type postExecutorStep interface { + execute(*postExecutorStepContext) error +} + +type storePreviousImageStep struct { + builder *STI + docker dockerpkg.Docker +} + +func (step *storePreviousImageStep) execute(ctx *postExecutorStepContext) error { + if step.builder.incremental && step.builder.config.RemovePreviousImage { + log.V(3).Info("Executing step: store previous image") + ctx.previousImageID = step.getPreviousImage() + return nil + } + + log.V(3).Info("Skipping step: store previous image") + return nil +} + +func (step *storePreviousImageStep) getPreviousImage() string { + previousImageID, err := step.docker.GetImageID(step.builder.config.Tag) + if err != nil { + log.V(0).Infof("error: Error retrieving previous image's (%v) metadata: %v", step.builder.config.Tag, err) + return "" + } + return previousImageID +} + +type removePreviousImageStep struct { + builder *STI + docker dockerpkg.Docker +} + +func (step *removePreviousImageStep) execute(ctx *postExecutorStepContext) error { + if step.builder.incremental && step.builder.config.RemovePreviousImage { + log.V(3).Info("Executing step: remove previous image") + step.removePreviousImage(ctx.previousImageID) + return nil + } + + log.V(3).Info("Skipping step: remove previous image") + return nil +} + +func (step *removePreviousImageStep) removePreviousImage(previousImageID string) { + if previousImageID == "" { + return + } + + log.V(1).Infof("Removing previously-tagged image %s", previousImageID) + if err := step.docker.RemoveImage(previousImageID); err != nil { + log.V(0).Infof("error: Unable to remove previous image: %v", err) + } +} + +type commitImageStep struct { + image string + builder *STI + docker dockerpkg.Docker + fs fs.FileSystem + tar s2itar.Tar +} + +func (step *commitImageStep) execute(ctx *postExecutorStepContext) error { + log.V(3).Infof("Executing step: commit image") + + user, err := step.docker.GetImageUser(step.image) + if err != nil { + return fmt.Errorf("could not get user of %q image: %v", step.image, err) + } + + cmd := createCommandForExecutingRunScript(step.builder.scriptsURL, ctx.destination) + + if err = checkAndGetNewLabels(step.builder, step.docker, step.tar, ctx.containerID); err != nil { + return fmt.Errorf("could not check for new labels for %q image: %v", step.image, err) + } + + ctx.labels = createLabelsForResultingImage(step.builder, step.docker, step.image) + + if err = checkLabelSize(ctx.labels); err != nil { + return fmt.Errorf("label validation failed for %q image: %v", step.image, err) + } + + // Set the image entrypoint back to its original value on commit, the running + // container has "env" as its entrypoint and we don't want to commit that. + entrypoint, err := step.docker.GetImageEntrypoint(step.image) + if err != nil { + return fmt.Errorf("could not get entrypoint of %q image: %v", step.image, err) + } + // If the image has no explicit entrypoint, set it to an empty array + // so we don't default to leaving the entrypoint as "env" upon commit. + if entrypoint == nil { + entrypoint = []string{} + } + startTime := time.Now() + ctx.imageID, err = commitContainer( + step.docker, + ctx.containerID, + cmd, + user, + step.builder.config.Tag, + step.builder.env, + entrypoint, + ctx.labels, + ) + step.builder.result.BuildInfo.Stages = api.RecordStageAndStepInfo(step.builder.result.BuildInfo.Stages, api.StageCommit, api.StepCommitContainer, startTime, time.Now()) + if err != nil { + step.builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonCommitContainerFailed, + utilstatus.ReasonMessageCommitContainerFailed, + ) + return err + } + + return nil +} + +type downloadFilesFromBuilderImageStep struct { + builder *STI + docker dockerpkg.Docker + fs fs.FileSystem + tar s2itar.Tar +} + +func (step *downloadFilesFromBuilderImageStep) execute(ctx *postExecutorStepContext) error { + log.V(3).Info("Executing step: download files from the builder image") + + artifactsDir := filepath.Join(step.builder.config.WorkingDir, constants.RuntimeArtifactsDir) + if err := step.fs.Mkdir(artifactsDir); err != nil { + step.builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonFSOperationFailed, + utilstatus.ReasonMessageFSOperationFailed, + ) + return fmt.Errorf("could not create directory %q: %v", artifactsDir, err) + } + + for _, artifact := range step.builder.config.RuntimeArtifacts { + if err := step.downloadAndExtractFile(artifact.Source, artifactsDir, ctx.containerID); err != nil { + step.builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonRuntimeArtifactsFetchFailed, + utilstatus.ReasonMessageRuntimeArtifactsFetchFailed, + ) + return err + } + + // for mapping like "/tmp/foo.txt -> app" we should create "app" and move "foo.txt" to that directory + dstSubDir := path.Clean(artifact.Destination) + if dstSubDir != "." && dstSubDir != "/" { + dstDir := filepath.Join(artifactsDir, dstSubDir) + log.V(5).Infof("Creating directory %q", dstDir) + if err := step.fs.MkdirAll(dstDir); err != nil { + step.builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonFSOperationFailed, + utilstatus.ReasonMessageFSOperationFailed, + ) + return fmt.Errorf("could not create directory %q: %v", dstDir, err) + } + + currentFile := filepath.Base(artifact.Source) + oldFile := filepath.Join(artifactsDir, currentFile) + newFile := filepath.Join(artifactsDir, dstSubDir, currentFile) + log.V(5).Infof("Renaming %q to %q", oldFile, newFile) + if err := step.fs.Rename(oldFile, newFile); err != nil { + step.builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonFSOperationFailed, + utilstatus.ReasonMessageFSOperationFailed, + ) + return fmt.Errorf("could not rename %q -> %q: %v", oldFile, newFile, err) + } + } + } + + return nil +} + +func (step *downloadFilesFromBuilderImageStep) downloadAndExtractFile(artifactPath, artifactsDir, containerID string) error { + if res, err := downloadAndExtractFileFromContainer(step.docker, step.tar, artifactPath, artifactsDir, containerID); err != nil { + step.builder.result.BuildInfo.FailureReason = res + return err + } + return nil +} + +type startRuntimeImageAndUploadFilesStep struct { + builder *STI + docker dockerpkg.Docker + fs fs.FileSystem +} + +func (step *startRuntimeImageAndUploadFilesStep) execute(ctx *postExecutorStepContext) error { + log.V(3).Info("Executing step: start runtime image and upload files") + + fd, err := ioutil.TempFile("", "s2i-upload-done") + if err != nil { + step.builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return err + } + fd.Close() + lastFilePath := fd.Name() + defer func() { + os.Remove(lastFilePath) + }() + + lastFileDstPath := "/tmp/" + filepath.Base(lastFilePath) + + outReader, outWriter := io.Pipe() + errReader, errWriter := io.Pipe() + + artifactsDir := filepath.Join(step.builder.config.WorkingDir, constants.RuntimeArtifactsDir) + + // We copy scripts to a directory with artifacts to upload files in one shot + for _, script := range []string{constants.AssembleRuntime, constants.Run} { + // scripts must be inside of "scripts" subdir, see createCommandForExecutingRunScript() + destinationDir := filepath.Join(artifactsDir, "scripts") + err = step.copyScriptIfNeeded(script, destinationDir) + if err != nil { + step.builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return err + } + } + + image := step.builder.config.RuntimeImage + workDir, err := step.docker.GetImageWorkdir(image) + if err != nil { + step.builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return fmt.Errorf("could not get working dir of %q image: %v", image, err) + } + + commandBaseDir := filepath.Join(workDir, "scripts") + useExternalAssembleScript := step.builder.externalScripts[constants.AssembleRuntime] + if !useExternalAssembleScript { + // script already inside of the image + var scriptsURL string + scriptsURL, err = step.docker.GetScriptsURL(image) + if err != nil { + step.builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return err + } + if len(scriptsURL) == 0 { + step.builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return fmt.Errorf("could not determine scripts URL for image %q", image) + } + commandBaseDir = strings.TrimPrefix(scriptsURL, "image://") + } + + cmd := fmt.Sprintf( + "while [ ! -f %q ]; do sleep 0.5; done; %s/%s; exit $?", + lastFileDstPath, + commandBaseDir, + constants.AssembleRuntime, + ) + + opts := dockerpkg.RunContainerOptions{ + Image: image, + PullImage: false, // The PullImage is false because we've already pulled the image + CommandExplicit: []string{"/bin/sh", "-c", cmd}, + Stdout: outWriter, + Stderr: errWriter, + NetworkMode: string(step.builder.config.DockerNetworkMode), + CGroupLimits: step.builder.config.CGroupLimits, + CapDrop: step.builder.config.DropCapabilities, + PostExec: step.builder.postExecutor, + Env: step.builder.env, + User: step.builder.config.AssembleRuntimeUser, + } + + opts.OnStart = func(containerID string) error { + setStandardPerms := func(writer io.Writer) s2itar.Writer { + return s2itar.ChmodAdapter{Writer: tar.NewWriter(writer), NewFileMode: 0644, NewExecFileMode: 0755, NewDirMode: 0755} + } + + log.V(5).Infof("Uploading directory %q -> %q", artifactsDir, workDir) + onStartErr := step.docker.UploadToContainerWithTarWriter(step.fs, artifactsDir, workDir, containerID, setStandardPerms) + if onStartErr != nil { + return fmt.Errorf("could not upload directory (%q -> %q) into container %s: %v", artifactsDir, workDir, containerID, err) + } + + log.V(5).Infof("Uploading file %q -> %q", lastFilePath, lastFileDstPath) + onStartErr = step.docker.UploadToContainerWithTarWriter(step.fs, lastFilePath, lastFileDstPath, containerID, setStandardPerms) + if onStartErr != nil { + return fmt.Errorf("could not upload file (%q -> %q) into container %s: %v", lastFilePath, lastFileDstPath, containerID, err) + } + + return onStartErr + } + + dockerpkg.StreamContainerIO(outReader, nil, func(s string) { log.V(0).Info(s) }) + + errOutput := "" + c := dockerpkg.StreamContainerIO(errReader, &errOutput, func(s string) { log.Info(s) }) + + // switch to the next stage of post executors steps + step.builder.postExecutorStage++ + + err = step.docker.RunContainer(opts) + if e, ok := err.(s2ierr.ContainerError); ok { + // Must wait for StreamContainerIO goroutine above to exit before reading errOutput. + <-c + err = s2ierr.NewContainerError(image, e.ErrorCode, errOutput+e.Output) + } + + return err +} + +func (step *startRuntimeImageAndUploadFilesStep) copyScriptIfNeeded(script, destinationDir string) error { + useExternalScript := step.builder.externalScripts[script] + if useExternalScript { + src := filepath.Join(step.builder.config.WorkingDir, constants.UploadScripts, script) + dst := filepath.Join(destinationDir, script) + log.V(5).Infof("Copying file %q -> %q", src, dst) + if err := step.fs.MkdirAll(destinationDir); err != nil { + return fmt.Errorf("could not create directory %q: %v", destinationDir, err) + } + if err := step.fs.Copy(src, dst, nil); err != nil { + return fmt.Errorf("could not copy file (%q -> %q): %v", src, dst, err) + } + } + return nil +} + +type reportSuccessStep struct { + builder *STI +} + +func (step *reportSuccessStep) execute(ctx *postExecutorStepContext) error { + log.V(3).Info("Executing step: report success") + + step.builder.result.Success = true + step.builder.result.ImageID = ctx.imageID + + log.V(3).Infof("Successfully built %s", util.FirstNonEmpty(step.builder.config.Tag, ctx.imageID)) + + return nil +} + +// shared methods + +func commitContainer(docker dockerpkg.Docker, containerID, cmd, user, tag string, env, entrypoint []string, labels map[string]string) (string, error) { + opts := dockerpkg.CommitContainerOptions{ + Command: []string{cmd}, + Env: env, + Entrypoint: entrypoint, + ContainerID: containerID, + Repository: tag, + User: user, + Labels: labels, + } + + imageID, err := docker.CommitContainer(opts) + if err != nil { + return "", s2ierr.NewCommitError(tag, err) + } + + return imageID, nil +} + +func createLabelsForResultingImage(builder *STI, docker dockerpkg.Docker, baseImage string) map[string]string { + generatedLabels := util.GenerateOutputImageLabels(builder.sourceInfo, builder.config) + + existingLabels, err := docker.GetLabels(baseImage) + if err != nil { + log.V(0).Infof("error: Unable to read existing labels from the base image %s", baseImage) + } + + configLabels := builder.config.Labels + newLabels := builder.newLabels + + return mergeLabels(existingLabels, generatedLabels, configLabels, newLabels) +} + +func mergeLabels(labels ...map[string]string) map[string]string { + mergedLabels := map[string]string{} + + for _, labelMap := range labels { + for k, v := range labelMap { + mergedLabels[k] = v + } + } + return mergedLabels +} + +func createCommandForExecutingRunScript(scriptsURL map[string]string, location string) string { + cmd := scriptsURL[constants.Run] + if strings.HasPrefix(cmd, "image://") { + // scripts from inside of the image, we need to strip the image part + // NOTE: We use path.Join instead of filepath.Join to avoid converting the + // path to UNC (Windows) format as we always run this inside container. + cmd = strings.TrimPrefix(cmd, "image://") + } else { + // external scripts, in which case we're taking the directory to which they + // were extracted and append scripts dir and name + cmd = path.Join(location, "scripts", constants.Run) + } + return cmd +} + +func downloadAndExtractFileFromContainer(docker dockerpkg.Docker, tar s2itar.Tar, sourcePath, destinationPath, containerID string) (api.FailureReason, error) { + log.V(5).Infof("Downloading file %q", sourcePath) + + fd, err := ioutil.TempFile(destinationPath, "s2i-runtime-artifact") + if err != nil { + res := utilstatus.NewFailureReason( + utilstatus.ReasonFSOperationFailed, + utilstatus.ReasonMessageFSOperationFailed, + ) + return res, fmt.Errorf("could not create temporary file for runtime artifact: %v", err) + } + defer func() { + fd.Close() + os.Remove(fd.Name()) + }() + + if err := docker.DownloadFromContainer(sourcePath, fd, containerID); err != nil { + res := utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return res, fmt.Errorf("could not download file (%q -> %q) from container %s: %v", sourcePath, fd.Name(), containerID, err) + } + + // after writing to the file descriptor we need to rewind pointer to the beginning of the file before next reading + if _, err := fd.Seek(0, io.SeekStart); err != nil { + res := utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return res, fmt.Errorf("could not seek to the beginning of the file %q: %v", fd.Name(), err) + } + + if err := tar.ExtractTarStream(destinationPath, fd); err != nil { + res := utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return res, fmt.Errorf("could not extract artifact %q into the directory %q: %v", sourcePath, destinationPath, err) + } + + return utilstatus.NewFailureReason("", ""), nil +} + +func checkLabelSize(labels map[string]string) error { + var sum = 0 + for k, v := range labels { + sum += len(k) + len(v) + } + + if sum > maximumLabelSize { + return fmt.Errorf("label size '%d' exceeds the maximum limit '%d'", sum, maximumLabelSize) + } + + return nil +} + +// check for new labels and apply to the output image. +func checkAndGetNewLabels(builder *STI, docker dockerpkg.Docker, tar s2itar.Tar, containerID string) error { + log.V(3).Infof("Checking for new Labels to apply... ") + + // metadata filename and its path inside the container + metadataFilename := "image_metadata.json" + sourceFilepath := filepath.Join("/tmp/.s2i", metadataFilename) + + // create the 'downloadPath' folder if it doesn't exist + downloadPath := filepath.Join(builder.config.WorkingDir, "metadata") + log.V(3).Infof("Creating the download path '%s'", downloadPath) + if err := os.MkdirAll(downloadPath, 0700); err != nil { + log.Errorf("Error creating dir %q for '%s': %v", downloadPath, metadataFilename, err) + return err + } + + // download & extract the file from container + if _, err := downloadAndExtractFileFromContainer(docker, tar, sourceFilepath, downloadPath, containerID); err != nil { + log.V(3).Infof("unable to download and extract '%s' ... continuing", metadataFilename) + return nil + } + + // open the file + filePath := filepath.Join(downloadPath, metadataFilename) + fd, err := os.Open(filePath) + if fd == nil || err != nil { + return fmt.Errorf("unable to open file '%s' : %v", downloadPath, err) + } + defer fd.Close() + + // read the file to a string + str, err := ioutil.ReadAll(fd) + if err != nil { + return fmt.Errorf("error reading file '%s' in to a string: %v", filePath, err) + } + log.V(3).Infof("new Labels File contents : \n%s\n", str) + + // string into a map + var data map[string]interface{} + + if err = json.Unmarshal([]byte(str), &data); err != nil { + return fmt.Errorf("JSON Unmarshal Error with '%s' file : %v", metadataFilename, err) + } + + // update newLabels[] + labels := data["labels"] + for _, l := range labels.([]interface{}) { + for k, v := range l.(map[string]interface{}) { + builder.newLabels[k] = v.(string) + } + } + + return nil +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/build/strategies/sti/sti.go b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/sti/sti.go new file mode 100644 index 0000000000..f3073e29ef --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/sti/sti.go @@ -0,0 +1,822 @@ +package sti + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path" + "path/filepath" + "regexp" + "strings" + "time" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/api/constants" + "github.com/openshift/source-to-image/pkg/build" + "github.com/openshift/source-to-image/pkg/build/strategies/layered" + dockerpkg "github.com/openshift/source-to-image/pkg/docker" + s2ierr "github.com/openshift/source-to-image/pkg/errors" + "github.com/openshift/source-to-image/pkg/ignore" + "github.com/openshift/source-to-image/pkg/scm" + "github.com/openshift/source-to-image/pkg/scm/git" + "github.com/openshift/source-to-image/pkg/scripts" + "github.com/openshift/source-to-image/pkg/tar" + "github.com/openshift/source-to-image/pkg/util" + "github.com/openshift/source-to-image/pkg/util/cmd" + "github.com/openshift/source-to-image/pkg/util/fs" + utillog "github.com/openshift/source-to-image/pkg/util/log" + utilstatus "github.com/openshift/source-to-image/pkg/util/status" +) + +const ( + injectionResultFile = "/tmp/injection-result" + rmInjectionsScript = "/tmp/rm-injections" +) + +var ( + log = utillog.StderrLog + + // List of directories that needs to be present inside working dir + workingDirs = []string{ + constants.UploadScripts, + constants.Source, + constants.DefaultScripts, + constants.UserScripts, + } + + errMissingRequirements = errors.New("missing requirements") +) + +// STI strategy executes the S2I build. +// For more details about S2I, visit https://github.com/openshift/source-to-image +type STI struct { + config *api.Config + result *api.Result + postExecutor dockerpkg.PostExecutor + installer scripts.Installer + runtimeInstaller scripts.Installer + git git.Git + fs fs.FileSystem + tar tar.Tar + docker dockerpkg.Docker + incrementalDocker dockerpkg.Docker + runtimeDocker dockerpkg.Docker + callbackInvoker util.CallbackInvoker + requiredScripts []string + optionalScripts []string + optionalRuntimeScripts []string + externalScripts map[string]bool + installedScripts map[string]bool + scriptsURL map[string]string + incremental bool + sourceInfo *git.SourceInfo + env []string + newLabels map[string]string + + // Interfaces + preparer build.Preparer + ignorer build.Ignorer + artifacts build.IncrementalBuilder + scripts build.ScriptsHandler + source build.Downloader + garbage build.Cleaner + layered build.Builder + + // post executors steps + postExecutorStage int + postExecutorFirstStageSteps []postExecutorStep + postExecutorSecondStageSteps []postExecutorStep + postExecutorStepsContext *postExecutorStepContext +} + +// New returns the instance of STI builder strategy for the given config. +// If the layeredBuilder parameter is specified, then the builder provided will +// be used for the case that the base Docker image does not have 'tar' or 'bash' +// installed. +func New(client dockerpkg.Client, config *api.Config, fs fs.FileSystem, overrides build.Overrides) (*STI, error) { + excludePattern, err := regexp.Compile(config.ExcludeRegExp) + if err != nil { + return nil, err + } + + docker := dockerpkg.New(client, config.PullAuthentication) + var incrementalDocker dockerpkg.Docker + if config.Incremental { + incrementalDocker = dockerpkg.New(client, config.IncrementalAuthentication) + } + + inst := scripts.NewInstaller( + config.BuilderImage, + config.ScriptsURL, + config.ScriptDownloadProxyConfig, + docker, + config.PullAuthentication, + fs, + config, + ) + tarHandler := tar.NewParanoid(fs) + tarHandler.SetExclusionPattern(excludePattern) + + builder := &STI{ + installer: inst, + config: config, + docker: docker, + incrementalDocker: incrementalDocker, + git: git.New(fs, cmd.NewCommandRunner()), + fs: fs, + tar: tarHandler, + callbackInvoker: util.NewCallbackInvoker(), + requiredScripts: scripts.RequiredScripts, + optionalScripts: scripts.OptionalScripts, + optionalRuntimeScripts: []string{constants.AssembleRuntime}, + externalScripts: map[string]bool{}, + installedScripts: map[string]bool{}, + scriptsURL: map[string]string{}, + newLabels: map[string]string{}, + } + + if len(config.RuntimeImage) > 0 { + builder.runtimeDocker = dockerpkg.New(client, config.RuntimeAuthentication) + + builder.runtimeInstaller = scripts.NewInstaller( + config.RuntimeImage, + config.ScriptsURL, + config.ScriptDownloadProxyConfig, + builder.runtimeDocker, + config.RuntimeAuthentication, + builder.fs, + config, + ) + } + + // The sources are downloaded using the Git downloader. + // TODO: Add more SCM in future. + // TODO: explicit decision made to customize processing for usage specifically vs. + // leveraging overrides; also, we ultimately want to simplify s2i usage a good bit, + // which would lead to replacing this quick short circuit (so this change is tactical) + builder.source = overrides.Downloader + if builder.source == nil && !config.Usage { + downloader, err := scm.DownloaderForSource(builder.fs, config.Source, config.ForceCopy) + if err != nil { + return nil, err + } + builder.source = downloader + } + builder.garbage = build.NewDefaultCleaner(builder.fs, builder.docker) + + builder.layered, err = layered.New(client, config, builder.fs, builder, overrides) + if err != nil { + return nil, err + } + + // Set interfaces + builder.preparer = builder + // later on, if we support say .gitignore func in addition to .dockerignore + // func, setting ignorer will be based on config setting + builder.ignorer = &ignore.DockerIgnorer{} + builder.artifacts = builder + builder.scripts = builder + builder.postExecutor = builder + builder.initPostExecutorSteps() + + return builder, nil +} + +// Build processes a Request and returns a *api.Result and an error. +// An error represents a failure performing the build rather than a failure +// of the build itself. Callers should check the Success field of the result +// to determine whether a build succeeded or not. +func (builder *STI) Build(config *api.Config) (*api.Result, error) { + builder.result = &api.Result{} + + if len(builder.config.CallbackURL) > 0 { + defer func() { + builder.result.Messages = builder.callbackInvoker.ExecuteCallback( + builder.config.CallbackURL, + builder.result.Success, + builder.postExecutorStepsContext.labels, + builder.result.Messages, + ) + }() + } + defer builder.garbage.Cleanup(config) + + log.V(1).Infof("Preparing to build %s", config.Tag) + if err := builder.preparer.Prepare(config); err != nil { + return builder.result, err + } + + if builder.incremental = builder.artifacts.Exists(config); builder.incremental { + tag := util.FirstNonEmpty(config.IncrementalFromTag, config.Tag) + log.V(1).Infof("Existing image for tag %s detected for incremental build", tag) + } else { + log.V(1).Info("Clean build will be performed") + } + + log.V(2).Infof("Performing source build from %s", config.Source) + if builder.incremental { + if err := builder.artifacts.Save(config); err != nil { + log.Warning("Clean build will be performed because of error saving previous build artifacts") + log.V(2).Infof("error: %v", err) + } + } + + if len(config.AssembleUser) > 0 { + log.V(1).Infof("Running %q in %q as %q user", constants.Assemble, config.Tag, config.AssembleUser) + } else { + log.V(1).Infof("Running %q in %q", constants.Assemble, config.Tag) + } + startTime := time.Now() + if err := builder.scripts.Execute(constants.Assemble, config.AssembleUser, config); err != nil { + if err == errMissingRequirements { + log.V(1).Info("Image is missing basic requirements (sh or tar), layered build will be performed") + return builder.layered.Build(config) + } + if e, ok := err.(s2ierr.ContainerError); ok { + if !isMissingRequirements(e.Output) { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonAssembleFailed, + utilstatus.ReasonMessageAssembleFailed, + ) + return builder.result, err + } + log.V(1).Info("Image is missing basic requirements (sh or tar), layered build will be performed") + buildResult, err := builder.layered.Build(config) + return buildResult, err + } + + return builder.result, err + } + builder.result.BuildInfo.Stages = api.RecordStageAndStepInfo(builder.result.BuildInfo.Stages, api.StageAssemble, api.StepAssembleBuildScripts, startTime, time.Now()) + builder.result.Success = true + + return builder.result, nil +} + +// Prepare prepares the source code and tar for build. +// NOTE: this func serves both the sti and onbuild strategies, as the OnBuild +// struct Build func leverages the STI struct Prepare func directly below. +func (builder *STI) Prepare(config *api.Config) error { + var err error + if builder.result == nil { + builder.result = &api.Result{} + } + + if len(config.WorkingDir) == 0 { + if config.WorkingDir, err = builder.fs.CreateWorkingDirectory(); err != nil { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonFSOperationFailed, + utilstatus.ReasonMessageFSOperationFailed, + ) + return err + } + } + + builder.result.WorkingDir = config.WorkingDir + + if len(config.RuntimeImage) > 0 { + startTime := time.Now() + dockerpkg.GetRuntimeImage(builder.runtimeDocker, config) + builder.result.BuildInfo.Stages = api.RecordStageAndStepInfo(builder.result.BuildInfo.Stages, api.StagePullImages, api.StepPullRuntimeImage, startTime, time.Now()) + + if err != nil { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonPullRuntimeImageFailed, + utilstatus.ReasonMessagePullRuntimeImageFailed, + ) + log.Errorf("Unable to pull runtime image %q: %v", config.RuntimeImage, err) + return err + } + + // user didn't specify mapping, let's take it from the runtime image then + if len(builder.config.RuntimeArtifacts) == 0 { + var mapping string + mapping, err = builder.docker.GetAssembleInputFiles(config.RuntimeImage) + if err != nil { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonInvalidArtifactsMapping, + utilstatus.ReasonMessageInvalidArtifactsMapping, + ) + return err + } + if len(mapping) == 0 { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return errors.New("no runtime artifacts to copy were specified") + } + for _, value := range strings.Split(mapping, ";") { + if err = builder.config.RuntimeArtifacts.Set(value); err != nil { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return fmt.Errorf("could not parse %q label with value %q on image %q: %v", + constants.AssembleInputFilesLabel, mapping, config.RuntimeImage, err) + } + } + } + + if len(config.AssembleRuntimeUser) == 0 { + if config.AssembleRuntimeUser, err = builder.docker.GetAssembleRuntimeUser(config.RuntimeImage); err != nil { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return fmt.Errorf("could not get %q label value on image %q: %v", + constants.AssembleRuntimeUserLabel, config.RuntimeImage, err) + } + } + + // we're validating values here to be sure that we're handling both of the cases of the invocation: + // from main() and as a method from OpenShift + for _, volumeSpec := range builder.config.RuntimeArtifacts { + var volumeErr error + + switch { + case !path.IsAbs(filepath.ToSlash(volumeSpec.Source)): + volumeErr = fmt.Errorf("invalid runtime artifacts mapping: %q -> %q: source must be an absolute path", volumeSpec.Source, volumeSpec.Destination) + case path.IsAbs(volumeSpec.Destination): + volumeErr = fmt.Errorf("invalid runtime artifacts mapping: %q -> %q: destination must be a relative path", volumeSpec.Source, volumeSpec.Destination) + case strings.HasPrefix(volumeSpec.Destination, ".."): + volumeErr = fmt.Errorf("invalid runtime artifacts mapping: %q -> %q: destination cannot start with '..'", volumeSpec.Source, volumeSpec.Destination) + default: + continue + } + if volumeErr != nil { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonInvalidArtifactsMapping, + utilstatus.ReasonMessageInvalidArtifactsMapping, + ) + return volumeErr + } + } + } + + // Setup working directories + for _, v := range workingDirs { + if err = builder.fs.MkdirAllWithPermissions(filepath.Join(config.WorkingDir, v), 0755); err != nil { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonFSOperationFailed, + utilstatus.ReasonMessageFSOperationFailed, + ) + return err + } + } + + // fetch sources, for their .s2i/bin might contain s2i scripts + if config.Source != nil { + if builder.sourceInfo, err = builder.source.Download(config); err != nil { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonFetchSourceFailed, + utilstatus.ReasonMessageFetchSourceFailed, + ) + return err + } + if config.SourceInfo != nil { + builder.sourceInfo = config.SourceInfo + } + } + + // get the scripts + required, err := builder.installer.InstallRequired(builder.requiredScripts, config.WorkingDir) + if err != nil { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonInstallScriptsFailed, + utilstatus.ReasonMessageInstallScriptsFailed, + ) + return err + } + optional := builder.installer.InstallOptional(builder.optionalScripts, config.WorkingDir) + + requiredAndOptional := append(required, optional...) + + if len(config.RuntimeImage) > 0 && builder.runtimeInstaller != nil { + optionalRuntime := builder.runtimeInstaller.InstallOptional(builder.optionalRuntimeScripts, config.WorkingDir) + requiredAndOptional = append(requiredAndOptional, optionalRuntime...) + } + + // If a ScriptsURL was specified, but no scripts were downloaded from it, throw an error + if len(config.ScriptsURL) > 0 { + failedCount := 0 + for _, result := range requiredAndOptional { + if util.Includes(result.FailedSources, scripts.ScriptURLHandler) { + failedCount++ + } + } + if failedCount == len(requiredAndOptional) { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonScriptsFetchFailed, + utilstatus.ReasonMessageScriptsFetchFailed, + ) + return fmt.Errorf("could not download any scripts from URL %v", config.ScriptsURL) + } + } + + for _, r := range requiredAndOptional { + if r.Error != nil { + log.Warningf("Error getting %v from %s: %v", r.Script, r.URL, r.Error) + continue + } + + builder.externalScripts[r.Script] = r.Downloaded + builder.installedScripts[r.Script] = r.Installed + builder.scriptsURL[r.Script] = r.URL + } + + // see if there is a .s2iignore file, and if so, read in the patterns an then + // search and delete on + return builder.ignorer.Ignore(config) +} + +// SetScripts allows to override default required and optional scripts +func (builder *STI) SetScripts(required, optional []string) { + builder.requiredScripts = required + builder.optionalScripts = optional +} + +// PostExecute allows to execute post-build actions after the Docker +// container execution finishes. +func (builder *STI) PostExecute(containerID, destination string) error { + builder.postExecutorStepsContext.containerID = containerID + builder.postExecutorStepsContext.destination = destination + + stageSteps := builder.postExecutorFirstStageSteps + if builder.postExecutorStage > 0 { + stageSteps = builder.postExecutorSecondStageSteps + } + + for _, step := range stageSteps { + if err := step.execute(builder.postExecutorStepsContext); err != nil { + log.V(0).Info("error: Execution of post execute step failed") + return err + } + } + + return nil +} + +// CreateBuildEnvironment constructs the environment variables to be provided to the assemble +// script and committed in the new image. +func CreateBuildEnvironment(sourcePath string, cfgEnv api.EnvironmentList) []string { + s2iEnv, err := scripts.GetEnvironment(filepath.Join(sourcePath, constants.Source)) + if err != nil { + log.V(3).Infof("No user environment provided (%v)", err) + } + + return append(scripts.ConvertEnvironmentList(s2iEnv), scripts.ConvertEnvironmentList(cfgEnv)...) +} + +// Exists determines if the current build supports incremental workflow. +// It checks if the previous image exists in the system and if so, then it +// verifies that the save-artifacts script is present. +func (builder *STI) Exists(config *api.Config) bool { + if !config.Incremental { + return false + } + + policy := config.PreviousImagePullPolicy + if len(policy) == 0 { + policy = api.DefaultPreviousImagePullPolicy + } + + tag := util.FirstNonEmpty(config.IncrementalFromTag, config.Tag) + + startTime := time.Now() + result, err := dockerpkg.PullImage(tag, builder.incrementalDocker, policy) + builder.result.BuildInfo.Stages = api.RecordStageAndStepInfo(builder.result.BuildInfo.Stages, api.StagePullImages, api.StepPullPreviousImage, startTime, time.Now()) + + if err != nil { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonPullPreviousImageFailed, + utilstatus.ReasonMessagePullPreviousImageFailed, + ) + log.V(2).Infof("Unable to pull previously built image %q: %v", tag, err) + return false + } + + return result.Image != nil && builder.installedScripts[constants.SaveArtifacts] +} + +// Save extracts and restores the build artifacts from the previous build to +// the current build. +func (builder *STI) Save(config *api.Config) (err error) { + artifactTmpDir := filepath.Join(config.WorkingDir, "upload", "artifacts") + if builder.result == nil { + builder.result = &api.Result{} + } + + if err = builder.fs.Mkdir(artifactTmpDir); err != nil { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonFSOperationFailed, + utilstatus.ReasonMessageFSOperationFailed, + ) + return err + } + + image := util.FirstNonEmpty(config.IncrementalFromTag, config.Tag) + + outReader, outWriter := io.Pipe() + errReader, errWriter := io.Pipe() + log.V(1).Infof("Saving build artifacts from image %s to path %s", image, artifactTmpDir) + extractFunc := func(string) error { + startTime := time.Now() + extractErr := builder.tar.ExtractTarStream(artifactTmpDir, outReader) + io.Copy(ioutil.Discard, outReader) // must ensure reader from container is drained + builder.result.BuildInfo.Stages = api.RecordStageAndStepInfo(builder.result.BuildInfo.Stages, api.StageRetrieve, api.StepRetrievePreviousArtifacts, startTime, time.Now()) + + if extractErr != nil { + builder.fs.RemoveDirectory(artifactTmpDir) + } + + return extractErr + } + + user := config.AssembleUser + if len(user) == 0 { + user, err = builder.docker.GetImageUser(image) + if err != nil { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return err + } + log.V(3).Infof("The assemble user is not set, defaulting to %q user", user) + } else { + log.V(3).Infof("Using assemble user %q to extract artifacts", user) + } + + opts := dockerpkg.RunContainerOptions{ + Image: image, + User: user, + ExternalScripts: builder.externalScripts[constants.SaveArtifacts], + ScriptsURL: config.ScriptsURL, + Destination: config.Destination, + PullImage: false, + Command: constants.SaveArtifacts, + Stdout: outWriter, + Stderr: errWriter, + OnStart: extractFunc, + NetworkMode: string(config.DockerNetworkMode), + CGroupLimits: config.CGroupLimits, + CapDrop: config.DropCapabilities, + Binds: config.BuildVolumes, + SecurityOpt: config.SecurityOpt, + AddHost: config.AddHost, + } + + dockerpkg.StreamContainerIO(errReader, nil, func(s string) { log.Info(s) }) + err = builder.docker.RunContainer(opts) + if e, ok := err.(s2ierr.ContainerError); ok { + err = s2ierr.NewSaveArtifactsError(image, e.Output, err) + } + + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return err +} + +// Execute runs the specified STI script in the builder image. +func (builder *STI) Execute(command string, user string, config *api.Config) error { + log.V(2).Infof("Using image name %s", config.BuilderImage) + + // Ensure that the builder image is present in the local Docker daemon. + // The image should have been pulled when the strategy was created, so + // this should be a quick inspect of the existing image. However, if + // the image has been deleted since the strategy was created, this will ensure + // it exists before executing a script on it. + builder.docker.CheckAndPullImage(config.BuilderImage) + + // we can't invoke this method before (for example in New() method) + // because of later initialization of config.WorkingDir + builder.env = CreateBuildEnvironment(config.WorkingDir, config.Environment) + + errOutput := "" + outReader, outWriter := io.Pipe() + errReader, errWriter := io.Pipe() + externalScripts := builder.externalScripts[command] + // if LayeredBuild is called then all the scripts will be placed inside the image + if config.LayeredBuild { + externalScripts = false + } + + opts := dockerpkg.RunContainerOptions{ + Image: config.BuilderImage, + Stdout: outWriter, + Stderr: errWriter, + // The PullImage is false because the PullImage function should be called + // before we run the container + PullImage: false, + ExternalScripts: externalScripts, + ScriptsURL: config.ScriptsURL, + Destination: config.Destination, + Command: command, + Env: builder.env, + User: user, + PostExec: builder.postExecutor, + NetworkMode: string(config.DockerNetworkMode), + CGroupLimits: config.CGroupLimits, + CapDrop: config.DropCapabilities, + Binds: config.BuildVolumes, + SecurityOpt: config.SecurityOpt, + AddHost: config.AddHost, + } + + // If there are injections specified, override the original assemble script + // and wait till all injections are uploaded into the container that runs the + // assemble script. + injectionError := make(chan error) + if len(config.Injections) > 0 && command == constants.Assemble { + workdir, err := builder.docker.GetImageWorkdir(config.BuilderImage) + if err != nil { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return err + } + config.Injections = util.FixInjectionsWithRelativePath(workdir, config.Injections) + truncatedFiles, err := util.ListFilesToTruncate(builder.fs, config.Injections) + if err != nil { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonInstallScriptsFailed, + utilstatus.ReasonMessageInstallScriptsFailed, + ) + return err + } + rmScript, err := util.CreateTruncateFilesScript(truncatedFiles, rmInjectionsScript) + if len(rmScript) != 0 { + defer os.Remove(rmScript) + } + if err != nil { + builder.result.BuildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return err + } + opts.CommandOverrides = func(cmd string) string { + // If an s2i build has injections, the s2i container's main command must be altered to + // do the following: + // 1) Wait for the injections to be uploaded + // 2) Check if there were any errors uploading the injections + // 3) Run the injection removal script after `assemble` completes + // + // The injectionResultFile should always be uploaded to the s2i container after the + // injected volumes are added. If this file is non-empty, it indicates that an error + // occurred during the injection process and the s2i build should fail. + return fmt.Sprintf("while [ ! -f %[1]q ]; do sleep 0.5; done; if [ -s %[1]q ]; then exit 1; fi; %[2]s; result=$?; . %[3]s; exit $result", + injectionResultFile, cmd, rmInjectionsScript) + } + originalOnStart := opts.OnStart + opts.OnStart = func(containerID string) error { + defer close(injectionError) + injectErr := builder.uploadInjections(config, rmScript, containerID) + if err := builder.uploadInjectionResult(injectErr, containerID); err != nil { + injectionError <- err + return err + } + if originalOnStart != nil { + return originalOnStart(containerID) + } + return nil + } + } else { + close(injectionError) + } + + if !config.LayeredBuild { + r, w := io.Pipe() + opts.Stdin = r + + go func() { + // Wait for the injections to complete and check the error. Do not start + // streaming the sources when the injection failed. + if <-injectionError != nil { + w.Close() + return + } + log.V(2).Info("starting the source uploading ...") + uploadDir := filepath.Join(config.WorkingDir, "upload") + w.CloseWithError(builder.tar.CreateTarStream(uploadDir, false, w)) + }() + } + + dockerpkg.StreamContainerIO(outReader, nil, func(s string) { + if !config.Quiet { + log.Info(strings.TrimSpace(s)) + } + }) + + c := dockerpkg.StreamContainerIO(errReader, &errOutput, func(s string) { log.Info(s) }) + + err := builder.docker.RunContainer(opts) + if err != nil { + // Must wait for StreamContainerIO goroutine above to exit before reading errOutput. + <-c + + if isMissingRequirements(errOutput) { + err = errMissingRequirements + } else if e, ok := err.(s2ierr.ContainerError); ok { + err = s2ierr.NewContainerError(config.BuilderImage, e.ErrorCode, errOutput+e.Output) + } + } + + return err +} + +// uploadInjections uploads the injected volumes to the s2i container, along with the source +// removal script to truncate volumes that should not be kept. +func (builder *STI) uploadInjections(config *api.Config, rmScript, containerID string) error { + log.V(2).Info("starting the injections uploading ...") + for _, s := range config.Injections { + if err := builder.docker.UploadToContainer(builder.fs, s.Source, s.Destination, containerID); err != nil { + return util.HandleInjectionError(s, err) + } + } + if err := builder.docker.UploadToContainer(builder.fs, rmScript, rmInjectionsScript, containerID); err != nil { + return util.HandleInjectionError(api.VolumeSpec{Source: rmScript, Destination: rmInjectionsScript}, err) + } + return nil +} + +func (builder *STI) initPostExecutorSteps() { + builder.postExecutorStepsContext = &postExecutorStepContext{} + if len(builder.config.RuntimeImage) == 0 { + builder.postExecutorFirstStageSteps = []postExecutorStep{ + &storePreviousImageStep{ + builder: builder, + docker: builder.docker, + }, + &commitImageStep{ + image: builder.config.BuilderImage, + builder: builder, + docker: builder.docker, + fs: builder.fs, + tar: builder.tar, + }, + &reportSuccessStep{ + builder: builder, + }, + &removePreviousImageStep{ + builder: builder, + docker: builder.docker, + }, + } + } else { + builder.postExecutorFirstStageSteps = []postExecutorStep{ + &downloadFilesFromBuilderImageStep{ + builder: builder, + docker: builder.docker, + fs: builder.fs, + tar: builder.tar, + }, + &startRuntimeImageAndUploadFilesStep{ + builder: builder, + docker: builder.docker, + fs: builder.fs, + }, + } + builder.postExecutorSecondStageSteps = []postExecutorStep{ + &commitImageStep{ + image: builder.config.RuntimeImage, + builder: builder, + docker: builder.docker, + tar: builder.tar, + }, + &reportSuccessStep{ + builder: builder, + }, + } + } +} + +// uploadInjectionResult uploads a result file to the s2i container, indicating +// that the injections have completed. If a non-nil error is passed in, it is returned +// to ensure the error status of the injection upload is reported. +func (builder *STI) uploadInjectionResult(startErr error, containerID string) error { + resultFile, err := util.CreateInjectionResultFile(startErr) + if len(resultFile) > 0 { + defer os.Remove(resultFile) + } + if err != nil { + return err + } + err = builder.docker.UploadToContainer(builder.fs, resultFile, injectionResultFile, containerID) + if err != nil { + return util.HandleInjectionError(api.VolumeSpec{Source: resultFile, Destination: injectionResultFile}, err) + } + return startErr +} + +func isMissingRequirements(text string) bool { + tarCommand, _ := regexp.MatchString(`.*tar.*not found`, text) + shCommand, _ := regexp.MatchString(`.*/bin/sh.*no such file or directory`, text) + return tarCommand || shCommand +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/build/strategies/sti/usage.go b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/sti/usage.go new file mode 100644 index 0000000000..081e67e34a --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/sti/usage.go @@ -0,0 +1,52 @@ +package sti + +import ( + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/api/constants" + "github.com/openshift/source-to-image/pkg/build" + "github.com/openshift/source-to-image/pkg/docker" + "github.com/openshift/source-to-image/pkg/util/fs" +) + +// UsageHandler handles a config to display usage +type usageHandler interface { + build.ScriptsHandler + build.Preparer + SetScripts([]string, []string) +} + +// Usage display usage information about a particular build image +type Usage struct { + handler usageHandler + garbage build.Cleaner + config *api.Config +} + +// NewUsage creates a new instance of the default Usage implementation +func NewUsage(client docker.Client, config *api.Config) (*Usage, error) { + b, err := New(client, config, fs.NewFileSystem(), build.Overrides{}) + if err != nil { + return nil, err + } + usage := Usage{ + handler: b, + config: config, + garbage: b.garbage, + } + return &usage, nil +} + +// Show starts the builder container and invokes the usage script on it +// to print usage information for the script. +func (u *Usage) Show() error { + b := u.handler + defer u.garbage.Cleanup(u.config) + + b.SetScripts([]string{constants.Usage}, []string{}) + + if err := b.Prepare(u.config); err != nil { + return err + } + + return b.Execute(constants.Usage, "", u.config) +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/build/strategies/strategies.go b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/strategies.go new file mode 100644 index 0000000000..9738dbc39c --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/build/strategies/strategies.go @@ -0,0 +1,82 @@ +package strategies + +import ( + "time" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/build" + "github.com/openshift/source-to-image/pkg/build/strategies/dockerfile" + "github.com/openshift/source-to-image/pkg/build/strategies/onbuild" + "github.com/openshift/source-to-image/pkg/build/strategies/sti" + "github.com/openshift/source-to-image/pkg/docker" + "github.com/openshift/source-to-image/pkg/util/fs" + utilstatus "github.com/openshift/source-to-image/pkg/util/status" +) + +// Strategy creates the appropriate build strategy for the provided config, using +// the overrides provided. Not all strategies support all overrides. +func Strategy(client docker.Client, config *api.Config, overrides build.Overrides) (build.Builder, api.BuildInfo, error) { + var builder build.Builder + var buildInfo api.BuildInfo + var err error + + fileSystem := fs.NewFileSystem() + + startTime := time.Now() + + if len(config.AsDockerfile) != 0 { + builder, err = dockerfile.New(config, fileSystem) + if err != nil { + buildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return nil, buildInfo, err + } + return builder, buildInfo, nil + } + + dkr := docker.New(client, config.PullAuthentication) + image, err := docker.GetBuilderImage(dkr, config) + buildInfo.Stages = api.RecordStageAndStepInfo(buildInfo.Stages, api.StagePullImages, api.StepPullBuilderImage, startTime, time.Now()) + if err != nil { + buildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonPullBuilderImageFailed, + utilstatus.ReasonMessagePullBuilderImageFailed, + ) + return nil, buildInfo, err + } + config.HasOnBuild = image.OnBuild + + if config.AssembleUser, err = docker.GetAssembleUser(dkr, config); err != nil { + buildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonPullBuilderImageFailed, + utilstatus.ReasonMessagePullBuilderImageFailed, + ) + return nil, buildInfo, err + } + + // if we're blocking onbuild, just do a normal s2i build flow + // which won't do a docker build and invoke the onbuild commands + if image.OnBuild && !config.BlockOnBuild { + builder, err = onbuild.New(client, config, fileSystem, overrides) + if err != nil { + buildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return nil, buildInfo, err + } + return builder, buildInfo, nil + } + + builder, err = sti.New(client, config, fileSystem, overrides) + if err != nil { + buildInfo.FailureReason = utilstatus.NewFailureReason( + utilstatus.ReasonGenericS2IBuildFailed, + utilstatus.ReasonMessageGenericS2iBuildFailed, + ) + return nil, buildInfo, err + } + return builder, buildInfo, err +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/docker/doc.go b/vendor/github.com/openshift/source-to-image/pkg/docker/doc.go new file mode 100644 index 0000000000..747f685163 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/docker/doc.go @@ -0,0 +1,3 @@ +// Package docker implements Docker operations used by the S2I builder and +// executor. +package docker diff --git a/vendor/github.com/openshift/source-to-image/pkg/docker/docker.go b/vendor/github.com/openshift/source-to-image/pkg/docker/docker.go new file mode 100644 index 0000000000..48fcbbef85 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/docker/docker.go @@ -0,0 +1,1149 @@ +package docker + +import ( + "archive/tar" + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "math/rand" + "net/http" + "os" + "path" + "path/filepath" + "runtime" + "strings" + "syscall" + "time" + + dockertypes "github.com/docker/docker/api/types" + dockercontainer "github.com/docker/docker/api/types/container" + dockernetwork "github.com/docker/docker/api/types/network" + dockerapi "github.com/docker/docker/client" + dockermessage "github.com/docker/docker/pkg/jsonmessage" + dockerstdcopy "github.com/docker/docker/pkg/stdcopy" + "github.com/docker/go-connections/tlsconfig" + dockerspecs "github.com/opencontainers/image-spec/specs-go/v1" + "golang.org/x/net/context" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/api/constants" + s2ierr "github.com/openshift/source-to-image/pkg/errors" + s2itar "github.com/openshift/source-to-image/pkg/tar" + "github.com/openshift/source-to-image/pkg/util" + "github.com/openshift/source-to-image/pkg/util/fs" + "github.com/openshift/source-to-image/pkg/util/interrupt" +) + +const ( + // DefaultDestination is the destination where the artifacts will be placed + // if DestinationLabel was not specified. + DefaultDestination = "/tmp" + // DefaultTag is the image tag, being applied if none is specified. + DefaultTag = "latest" + + // DefaultDockerTimeout specifies a timeout for Docker API calls. When this + // timeout is reached, certain Docker API calls might error out. + DefaultDockerTimeout = 2 * time.Minute + + // DefaultShmSize is the default shared memory size to use (in bytes) if not specified. + DefaultShmSize = int64(1024 * 1024 * 64) + // DefaultPullRetryDelay is the default pull image retry interval + DefaultPullRetryDelay = 5 * time.Second + // DefaultPullRetryCount is the default pull image retry times + DefaultPullRetryCount = 6 +) + +var ( + // RetriableErrors is a set of strings that indicate that an retriable error occurred. + RetriableErrors = []string{ + "ping attempt failed with error", + "is already in progress", + "connection reset by peer", + "transport closed before response was received", + "connection refused", + } +) + +// containerNamePrefix prefixes the name of containers launched by S2I. We +// cannot reuse the prefix "k8s" because we don't want the containers to be +// managed by a kubelet. +const containerNamePrefix = "s2i" + +// containerName creates names for Docker containers launched by S2I. It is +// meant to resemble Kubernetes' pkg/kubelet/dockertools.BuildDockerName. +func containerName(image string) string { + //Initialize seed + rand.Seed(time.Now().UnixNano()) + uid := fmt.Sprintf("%08x", rand.Uint32()) + // Replace invalid characters for container name with underscores. + image = strings.Map(func(r rune) rune { + if ('0' <= r && r <= '9') || ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') { + return r + } + return '_' + }, image) + return fmt.Sprintf("%s_%s_%s", containerNamePrefix, image, uid) +} + +// Docker is the interface between STI and the docker engine-api. +// It contains higher level operations called from the STI +// build or usage commands +type Docker interface { + IsImageInLocalRegistry(name string) (bool, error) + IsImageOnBuild(string) bool + GetOnBuild(string) ([]string, error) + RemoveContainer(id string) error + GetScriptsURL(name string) (string, error) + GetAssembleInputFiles(string) (string, error) + GetAssembleRuntimeUser(string) (string, error) + RunContainer(opts RunContainerOptions) error + GetImageID(name string) (string, error) + GetImageWorkdir(name string) (string, error) + CommitContainer(opts CommitContainerOptions) (string, error) + RemoveImage(name string) error + CheckImage(name string) (*api.Image, error) + PullImage(name string) (*api.Image, error) + CheckAndPullImage(name string) (*api.Image, error) + BuildImage(opts BuildImageOptions) error + GetImageUser(name string) (string, error) + GetImageEntrypoint(name string) ([]string, error) + GetLabels(name string) (map[string]string, error) + UploadToContainer(fs fs.FileSystem, srcPath, destPath, container string) error + UploadToContainerWithTarWriter(fs fs.FileSystem, srcPath, destPath, container string, makeTarWriter func(io.Writer) s2itar.Writer) error + DownloadFromContainer(containerPath string, w io.Writer, container string) error + Version() (dockertypes.Version, error) + CheckReachable() error +} + +// Client contains all methods used when interacting directly with docker engine-api +type Client interface { + ContainerAttach(ctx context.Context, container string, options dockertypes.ContainerAttachOptions) (dockertypes.HijackedResponse, error) + ContainerCommit(ctx context.Context, container string, options dockertypes.ContainerCommitOptions) (dockertypes.IDResponse, error) + ContainerCreate(ctx context.Context, config *dockercontainer.Config, hostConfig *dockercontainer.HostConfig, networkingConfig *dockernetwork.NetworkingConfig, platform *dockerspecs.Platform, containerName string) (dockercontainer.ContainerCreateCreatedBody, error) + ContainerInspect(ctx context.Context, container string) (dockertypes.ContainerJSON, error) + ContainerRemove(ctx context.Context, container string, options dockertypes.ContainerRemoveOptions) error + ContainerStart(ctx context.Context, container string, options dockertypes.ContainerStartOptions) error + ContainerKill(ctx context.Context, container, signal string) error + ContainerWait(ctx context.Context, container string, condition dockercontainer.WaitCondition) (<-chan dockercontainer.ContainerWaitOKBody, <-chan error) + CopyToContainer(ctx context.Context, container, path string, content io.Reader, opts dockertypes.CopyToContainerOptions) error + CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, dockertypes.ContainerPathStat, error) + ImageBuild(ctx context.Context, buildContext io.Reader, options dockertypes.ImageBuildOptions) (dockertypes.ImageBuildResponse, error) + ImageInspectWithRaw(ctx context.Context, image string) (dockertypes.ImageInspect, []byte, error) + ImagePull(ctx context.Context, ref string, options dockertypes.ImagePullOptions) (io.ReadCloser, error) + ImageRemove(ctx context.Context, image string, options dockertypes.ImageRemoveOptions) ([]dockertypes.ImageDeleteResponseItem, error) + ServerVersion(ctx context.Context) (dockertypes.Version, error) +} + +type stiDocker struct { + client Client + pullAuth dockertypes.AuthConfig +} + +// InspectImage returns the image information and its raw representation. +func (d stiDocker) InspectImage(name string) (*dockertypes.ImageInspect, error) { + ctx, cancel := getDefaultContext() + defer cancel() + resp, _, err := d.client.ImageInspectWithRaw(ctx, name) + if err != nil { + return nil, err + } + return &resp, nil +} + +// PostExecutor is an interface which provides a PostExecute function +type PostExecutor interface { + PostExecute(containerID, destination string) error +} + +// PullResult is the result returned by the PullImage function +type PullResult struct { + OnBuild bool + Image *api.Image +} + +// RunContainerOptions are options passed in to the RunContainer method +type RunContainerOptions struct { + Image string + PullImage bool + PullAuth api.AuthConfig + ExternalScripts bool + ScriptsURL string + Destination string + Env []string + AddHost []string + // Entrypoint will be used to override the default entrypoint + // for the image if it has one. If the image has no entrypoint, + // this value is ignored. + Entrypoint []string + Stdin io.ReadCloser + Stdout io.WriteCloser + Stderr io.WriteCloser + OnStart func(containerID string) error + PostExec PostExecutor + TargetImage bool + NetworkMode string + User string + CGroupLimits *api.CGroupLimits + CapDrop []string + Binds []string + Command string + CommandOverrides func(originalCmd string) string + // CommandExplicit provides a full control on the CMD directive. + // It won't modified in any way and will be passed to the docker as-is. + // Use this option when you want to use arbitrary command as CMD directive. + // In this case you can't use Command because 1) it's just a string + // 2) it will be modified by prepending base dir and cleaned by the path.Join(). + // You also can't use CommandOverrides because 1) it's a string + // 2) it only gets applied when Command equals to "assemble" or "usage" script + // AND script is inside of the tar archive. + CommandExplicit []string + // SecurityOpt is passed through as security options to the underlying container. + SecurityOpt []string +} + +// asDockerConfig converts a RunContainerOptions into a Config understood by the +// docker client +func (rco RunContainerOptions) asDockerConfig() dockercontainer.Config { + return dockercontainer.Config{ + Image: getImageName(rco.Image), + User: rco.User, + Env: rco.Env, + Entrypoint: rco.Entrypoint, + OpenStdin: rco.Stdin != nil, + StdinOnce: rco.Stdin != nil, + AttachStdout: rco.Stdout != nil, + } +} + +// asDockerHostConfig converts a RunContainerOptions into a HostConfig +// understood by the docker client +func (rco RunContainerOptions) asDockerHostConfig() dockercontainer.HostConfig { + hostConfig := dockercontainer.HostConfig{ + CapDrop: rco.CapDrop, + PublishAllPorts: rco.TargetImage, + NetworkMode: dockercontainer.NetworkMode(rco.NetworkMode), + Binds: rco.Binds, + ExtraHosts: rco.AddHost, + SecurityOpt: rco.SecurityOpt, + } + if rco.CGroupLimits != nil { + hostConfig.Resources.Memory = rco.CGroupLimits.MemoryLimitBytes + hostConfig.Resources.MemorySwap = rco.CGroupLimits.MemorySwap + hostConfig.Resources.CgroupParent = rco.CGroupLimits.Parent + } + return hostConfig +} + +// asDockerCreateContainerOptions converts a RunContainerOptions into a +// ContainerCreateConfig understood by the docker client +func (rco RunContainerOptions) asDockerCreateContainerOptions() dockertypes.ContainerCreateConfig { + config := rco.asDockerConfig() + hostConfig := rco.asDockerHostConfig() + return dockertypes.ContainerCreateConfig{ + Name: containerName(rco.Image), + Config: &config, + HostConfig: &hostConfig, + } +} + +// asDockerAttachToContainerOptions converts a RunContainerOptions into a +// ContainerAttachOptions understood by the docker client +func (rco RunContainerOptions) asDockerAttachToContainerOptions() dockertypes.ContainerAttachOptions { + return dockertypes.ContainerAttachOptions{ + Stdin: rco.Stdin != nil, + Stdout: rco.Stdout != nil, + Stderr: rco.Stderr != nil, + Stream: rco.Stdout != nil, + } +} + +// CommitContainerOptions are options passed in to the CommitContainer method +type CommitContainerOptions struct { + ContainerID string + Repository string + User string + Command []string + Env []string + Entrypoint []string + Labels map[string]string +} + +// BuildImageOptions are options passed in to the BuildImage method +type BuildImageOptions struct { + Name string + Stdin io.Reader + Stdout io.WriteCloser + CGroupLimits *api.CGroupLimits +} + +// NewEngineAPIClient creates a new Docker engine API client +func NewEngineAPIClient(config *api.DockerConfig) (*dockerapi.Client, error) { + var httpClient *http.Client + + if config.UseTLS || config.TLSVerify { + tlscOptions := tlsconfig.Options{ + InsecureSkipVerify: !config.TLSVerify, + } + + if _, err := os.Stat(config.CAFile); !os.IsNotExist(err) { + tlscOptions.CAFile = config.CAFile + } + if _, err := os.Stat(config.CertFile); !os.IsNotExist(err) { + tlscOptions.CertFile = config.CertFile + } + if _, err := os.Stat(config.KeyFile); !os.IsNotExist(err) { + tlscOptions.KeyFile = config.KeyFile + } + + tlsc, err := tlsconfig.Client(tlscOptions) + if err != nil { + return nil, err + } + + httpClient = &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: tlsc, + }, + } + } + return dockerapi.NewClient(config.Endpoint, os.Getenv("DOCKER_API_VERSION"), httpClient, nil) +} + +// New creates a new implementation of the STI Docker interface +func New(client Client, auth api.AuthConfig) Docker { + return &stiDocker{ + client: client, + pullAuth: dockertypes.AuthConfig{ + Username: auth.Username, + Password: auth.Password, + Email: auth.Email, + ServerAddress: auth.ServerAddress, + }, + } +} + +func getDefaultContext() (context.Context, context.CancelFunc) { + // the intention is: all docker API calls with the exception of known long- + // running calls (ContainerWait, ImagePull, ImageBuild, ImageCommit) must complete within a + // certain timeout otherwise we bail. + return context.WithTimeout(context.Background(), DefaultDockerTimeout) +} + +// GetImageWorkdir returns the WORKDIR property for the given image name. +// When the WORKDIR is not set or empty, return "/" instead. +func (d *stiDocker) GetImageWorkdir(name string) (string, error) { + resp, err := d.InspectImage(name) + if err != nil { + return "", err + } + workdir := resp.Config.WorkingDir + if len(workdir) == 0 { + // This is a default destination used by UploadToContainer when the WORKDIR + // is not set or it is empty. To show user where the injections will end up, + // we set this to "/". + workdir = "/" + } + return workdir, nil +} + +// GetImageEntrypoint returns the ENTRYPOINT property for the given image name. +func (d *stiDocker) GetImageEntrypoint(name string) ([]string, error) { + image, err := d.InspectImage(name) + if err != nil { + return nil, err + } + return image.Config.Entrypoint, nil +} + +// UploadToContainer uploads artifacts to the container. +func (d *stiDocker) UploadToContainer(fs fs.FileSystem, src, dest, container string) error { + makeWorldWritable := func(writer io.Writer) s2itar.Writer { + return s2itar.ChmodAdapter{Writer: tar.NewWriter(writer), NewFileMode: 0666, NewExecFileMode: 0666, NewDirMode: 0777} + } + + return d.UploadToContainerWithTarWriter(fs, src, dest, container, makeWorldWritable) +} + +// UploadToContainerWithTarWriter uploads artifacts to the container. +// If the source is a directory, then all files and sub-folders are copied into +// the destination (which has to be directory as well). +// If the source is a single file, then the file copied into destination (which +// has to be full path to a file inside the container). +func (d *stiDocker) UploadToContainerWithTarWriter(fs fs.FileSystem, src, dest, container string, makeTarWriter func(io.Writer) s2itar.Writer) error { + destPath := filepath.Dir(dest) + r, w := io.Pipe() + go func() { + tarWriter := makeTarWriter(w) + tarWriter = s2itar.RenameAdapter{Writer: tarWriter, Old: filepath.Base(src), New: filepath.Base(dest)} + + err := s2itar.New(fs).CreateTarStreamToTarWriter(src, true, tarWriter, nil) + if err == nil { + err = tarWriter.Close() + } + + w.CloseWithError(err) + }() + log.V(3).Infof("Uploading %q to %q ...", src, destPath) + ctx, cancel := getDefaultContext() + defer cancel() + err := d.client.CopyToContainer(ctx, container, destPath, r, dockertypes.CopyToContainerOptions{}) + if err != nil { + log.V(0).Infof("error: Uploading to container failed: %v", err) + } + return err +} + +// DownloadFromContainer downloads file (or directory) from the container. +func (d *stiDocker) DownloadFromContainer(containerPath string, w io.Writer, container string) error { + ctx, cancel := getDefaultContext() + defer cancel() + readCloser, _, err := d.client.CopyFromContainer(ctx, container, containerPath) + if err != nil { + return err + } + defer readCloser.Close() + _, err = io.Copy(w, readCloser) + return err +} + +// IsImageInLocalRegistry determines whether the supplied image is in the local registry. +func (d *stiDocker) IsImageInLocalRegistry(name string) (bool, error) { + name = getImageName(name) + resp, err := d.InspectImage(name) + if resp != nil { + return true, nil + } + if err != nil && !dockerapi.IsErrNotFound(err) { + return false, s2ierr.NewInspectImageError(name, err) + } + return false, nil +} + +// GetImageUser finds and retrieves the user associated with +// an image if one has been specified +func (d *stiDocker) GetImageUser(name string) (string, error) { + name = getImageName(name) + resp, err := d.InspectImage(name) + if err != nil { + log.V(4).Infof("error inspecting image %s: %v", name, err) + return "", s2ierr.NewInspectImageError(name, err) + } + user := resp.Config.User + return user, nil +} + +// Version returns information of the docker client and server host +func (d *stiDocker) Version() (dockertypes.Version, error) { + ctx, cancel := getDefaultContext() + defer cancel() + return d.client.ServerVersion(ctx) +} + +// IsImageOnBuild provides information about whether the Docker image has +// OnBuild instruction recorded in the Image Config. +func (d *stiDocker) IsImageOnBuild(name string) bool { + onbuild, err := d.GetOnBuild(name) + return err == nil && len(onbuild) > 0 +} + +// GetOnBuild returns the set of ONBUILD Dockerfile commands to execute +// for the given image +func (d *stiDocker) GetOnBuild(name string) ([]string, error) { + name = getImageName(name) + resp, err := d.InspectImage(name) + if err != nil { + log.V(4).Infof("error inspecting image %s: %v", name, err) + return nil, s2ierr.NewInspectImageError(name, err) + } + return resp.Config.OnBuild, nil +} + +// CheckAndPullImage pulls an image into the local registry if not present +// and returns the image metadata +func (d *stiDocker) CheckAndPullImage(name string) (*api.Image, error) { + name = getImageName(name) + displayName := name + + if !log.Is(3) { + // For less verbose log levels (less than 3), shorten long iamge names like: + // "centos/php-56-centos7@sha256:51c3e2b08bd9fadefccd6ec42288680d6d7f861bdbfbd2d8d24960621e4e27f5" + // to include just enough characters to differentiate the build from others in the docker repository: + // "centos/php-56-centos7@sha256:51c3e2b08bd..." + // 18 characters is somewhat arbitrary, but should be enough to avoid a name collision. + split := strings.Split(name, "@") + if len(split) > 1 && len(split[1]) > 18 { + displayName = split[0] + "@" + split[1][:18] + "..." + } + } + + image, err := d.CheckImage(name) + if err != nil && !strings.Contains(err.(s2ierr.Error).Details.Error(), "No such image") { + return nil, err + } + if image == nil { + log.V(1).Infof("Image %q not available locally, pulling ...", displayName) + return d.PullImage(name) + } + + log.V(3).Infof("Using locally available image %q", displayName) + return image, nil +} + +// CheckImage checks image from the local registry. +func (d *stiDocker) CheckImage(name string) (*api.Image, error) { + name = getImageName(name) + inspect, err := d.InspectImage(name) + if err != nil { + log.V(4).Infof("error inspecting image %s: %v", name, err) + return nil, s2ierr.NewInspectImageError(name, err) + } + if inspect != nil { + image := &api.Image{} + updateImageWithInspect(image, inspect) + return image, nil + } + + return nil, nil +} + +func base64EncodeAuth(auth dockertypes.AuthConfig) (string, error) { + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(auth); err != nil { + return "", err + } + return base64.URLEncoding.EncodeToString(buf.Bytes()), nil +} + +// PullImage pulls an image into the local registry +func (d *stiDocker) PullImage(name string) (*api.Image, error) { + name = getImageName(name) + + // RegistryAuth is the base64 encoded credentials for the registry + base64Auth, err := base64EncodeAuth(d.pullAuth) + if err != nil { + return nil, s2ierr.NewPullImageError(name, err) + } + var retriableError = false + + for retries := 0; retries <= DefaultPullRetryCount; retries++ { + err = util.TimeoutAfter(DefaultDockerTimeout, fmt.Sprintf("pulling image %q", name), func(timer *time.Timer) error { + resp, pullErr := d.client.ImagePull(context.Background(), name, dockertypes.ImagePullOptions{RegistryAuth: base64Auth}) + if pullErr != nil { + return pullErr + } + defer resp.Close() + + decoder := json.NewDecoder(resp) + for { + if !timer.Stop() { + return &util.TimeoutError{} + } + timer.Reset(DefaultDockerTimeout) + + var msg dockermessage.JSONMessage + pullErr = decoder.Decode(&msg) + if pullErr == io.EOF { + return nil + } + if pullErr != nil { + return pullErr + } + + if msg.Error != nil { + return msg.Error + } + if msg.Progress != nil { + log.V(4).Infof("pulling image %s: %s", name, msg.Progress.String()) + } + } + }) + if err == nil { + break + } + log.V(0).Infof("pulling image error : %v", err) + errMsg := fmt.Sprintf("%s", err) + for _, errorString := range RetriableErrors { + if strings.Contains(errMsg, errorString) { + retriableError = true + break + } + } + + if !retriableError { + return nil, s2ierr.NewPullImageError(name, err) + } + + log.V(0).Infof("retrying in %s ...", DefaultPullRetryDelay) + time.Sleep(DefaultPullRetryDelay) + } + + inspectResp, err := d.InspectImage(name) + if err != nil { + return nil, s2ierr.NewPullImageError(name, err) + } + if inspectResp != nil { + image := &api.Image{} + updateImageWithInspect(image, inspectResp) + return image, nil + } + return nil, nil +} + +func updateImageWithInspect(image *api.Image, inspect *dockertypes.ImageInspect) { + image.ID = inspect.ID + if inspect.Config != nil { + image.Config = &api.ContainerConfig{ + Labels: inspect.Config.Labels, + Env: inspect.Config.Env, + } + } + if inspect.ContainerConfig != nil { + image.ContainerConfig = &api.ContainerConfig{ + Labels: inspect.ContainerConfig.Labels, + Env: inspect.ContainerConfig.Env, + } + } +} + +// RemoveContainer removes a container and its associated volumes. +func (d *stiDocker) RemoveContainer(id string) error { + ctx, cancel := getDefaultContext() + defer cancel() + opts := dockertypes.ContainerRemoveOptions{ + RemoveVolumes: true, + } + return d.client.ContainerRemove(ctx, id, opts) +} + +// KillContainer kills a container. +func (d *stiDocker) KillContainer(id string) error { + ctx, cancel := getDefaultContext() + defer cancel() + return d.client.ContainerKill(ctx, id, "SIGKILL") +} + +// GetLabels retrieves the labels of the given image. +func (d *stiDocker) GetLabels(name string) (map[string]string, error) { + name = getImageName(name) + resp, err := d.InspectImage(name) + if err != nil { + log.V(4).Infof("error inspecting image %s: %v", name, err) + return nil, s2ierr.NewInspectImageError(name, err) + } + return resp.Config.Labels, nil +} + +// getImageName checks the image name and adds DefaultTag if none is specified +func getImageName(name string) string { + _, tag, id := parseRepositoryTag(name) + if len(tag) == 0 && len(id) == 0 { + //_, tag, _ := parseRepositoryTag(name) + //if len(tag) == 0 { + return strings.Join([]string{name, DefaultTag}, ":") + } + + return name +} + +// getLabel gets label's value from the image metadata +func getLabel(image *api.Image, name string) string { + if value, ok := image.Config.Labels[name]; ok { + return value + } + return "" +} + +// getVariable gets environment variable's value from the image metadata +func getVariable(image *api.Image, name string) string { + envName := name + "=" + for _, v := range image.Config.Env { + if strings.HasPrefix(v, envName) { + return strings.TrimSpace(v[len(envName):]) + } + } + + return "" +} + +// GetScriptsURL finds a scripts-url label on the given image. +func (d *stiDocker) GetScriptsURL(image string) (string, error) { + imageMetadata, err := d.CheckAndPullImage(image) + if err != nil { + return "", err + } + + return getScriptsURL(imageMetadata), nil +} + +// GetAssembleInputFiles finds a io.openshift.s2i.assemble-input-files label on the given image. +func (d *stiDocker) GetAssembleInputFiles(image string) (string, error) { + imageMetadata, err := d.CheckAndPullImage(image) + if err != nil { + return "", err + } + + label := getLabel(imageMetadata, constants.AssembleInputFilesLabel) + if len(label) == 0 { + log.V(0).Infof("warning: Image %q does not contain a value for the %s label", image, constants.AssembleInputFilesLabel) + } else { + log.V(3).Infof("Image %q contains %s set to %q", image, constants.AssembleInputFilesLabel, label) + } + return label, nil +} + +// GetAssembleRuntimeUser finds a io.openshift.s2i.assemble-runtime-user label on the given image. +func (d *stiDocker) GetAssembleRuntimeUser(image string) (string, error) { + imageMetadata, err := d.CheckAndPullImage(image) + if err != nil { + return "", err + } + return getLabel(imageMetadata, constants.AssembleRuntimeUserLabel), nil +} + +// getScriptsURL finds a scripts url label in the image metadata +func getScriptsURL(image *api.Image) string { + if image == nil { + return "" + } + scriptsURL := getLabel(image, constants.ScriptsURLLabel) + + // For backward compatibility, support the old label schema + if len(scriptsURL) == 0 { + scriptsURL = getLabel(image, constants.DeprecatedScriptsURLLabel) + if len(scriptsURL) > 0 { + log.V(0).Infof("warning: Image %s uses deprecated label '%s', please migrate it to %s instead!", + image.ID, constants.DeprecatedScriptsURLLabel, constants.ScriptsURLLabel) + } + } + if len(scriptsURL) == 0 { + scriptsURL = getVariable(image, constants.ScriptsURLEnvironment) + if len(scriptsURL) != 0 { + log.V(0).Infof("warning: Image %s uses deprecated environment variable %s, please migrate it to %s label instead!", + image.ID, constants.ScriptsURLEnvironment, constants.ScriptsURLLabel) + } + } + if len(scriptsURL) == 0 { + log.V(0).Infof("warning: Image %s does not contain a value for the %s label", image.ID, constants.ScriptsURLLabel) + } else { + log.V(2).Infof("Image %s contains %s set to %q", image.ID, constants.ScriptsURLLabel, scriptsURL) + } + + return scriptsURL +} + +// getDestination finds a destination label in the image metadata +func getDestination(image *api.Image) string { + if val := getLabel(image, constants.DestinationLabel); len(val) != 0 { + return val + } + // For backward compatibility, support the old label schema + if val := getLabel(image, constants.DeprecatedDestinationLabel); len(val) != 0 { + log.V(0).Infof("warning: Image %s uses deprecated label '%s', please migrate it to %s instead!", + image.ID, constants.DeprecatedDestinationLabel, constants.DestinationLabel) + return val + } + if val := getVariable(image, constants.LocationEnvironment); len(val) != 0 { + log.V(0).Infof("warning: Image %s uses deprecated environment variable %s, please migrate it to %s label instead!", + image.ID, constants.LocationEnvironment, constants.DestinationLabel) + return val + } + + // default directory if none is specified + return DefaultDestination +} + +func constructCommand(opts RunContainerOptions, imageMetadata *api.Image, tarDestination string) []string { + // base directory for all S2I commands + commandBaseDir := determineCommandBaseDir(opts, imageMetadata, tarDestination) + + // NOTE: We use path.Join instead of filepath.Join to avoid converting the + // path to UNC (Windows) format as we always run this inside container. + binaryToRun := path.Join(commandBaseDir, opts.Command) + + // when calling assemble script with Stdin parameter set (the tar file) + // we need to first untar the whole archive and only then call the assemble script + if opts.Stdin != nil && (opts.Command == constants.Assemble || opts.Command == constants.Usage) { + untarAndRun := fmt.Sprintf("tar -C %s -xf - && %s", tarDestination, binaryToRun) + + resultedCommand := untarAndRun + if opts.CommandOverrides != nil { + resultedCommand = opts.CommandOverrides(untarAndRun) + } + return []string{"/bin/sh", "-c", resultedCommand} + } + + return []string{binaryToRun} +} + +func determineTarDestinationDir(opts RunContainerOptions, imageMetadata *api.Image) string { + if len(opts.Destination) != 0 { + return opts.Destination + } + return getDestination(imageMetadata) +} + +func determineCommandBaseDir(opts RunContainerOptions, imageMetadata *api.Image, tarDestination string) string { + if opts.ExternalScripts { + // for external scripts we must always append 'scripts' because this is + // the default subdirectory inside tar for them + // NOTE: We use path.Join instead of filepath.Join to avoid converting the + // path to UNC (Windows) format as we always run this inside container. + log.V(2).Infof("Both scripts and untarred source will be placed in '%s'", tarDestination) + return path.Join(tarDestination, "scripts") + } + + // for internal scripts we can have separate path for scripts and untar operation destination + scriptsURL := opts.ScriptsURL + if len(scriptsURL) == 0 { + scriptsURL = getScriptsURL(imageMetadata) + } + + commandBaseDir := strings.TrimPrefix(scriptsURL, "image://") + log.V(2).Infof("Base directory for S2I scripts is '%s'. Untarring destination is '%s'.", + commandBaseDir, tarDestination) + + return commandBaseDir +} + +// dumpContainerInfo dumps information about a running container (port/IP/etc). +func dumpContainerInfo(container dockercontainer.ContainerCreateCreatedBody, d *stiDocker, image string) { + ctx, cancel := getDefaultContext() + defer cancel() + + containerJSON, err := d.client.ContainerInspect(ctx, container.ID) + if err != nil { + return + } + + liveports := "\n\nPort Bindings: " + for port, bindings := range containerJSON.NetworkSettings.NetworkSettingsBase.Ports { + liveports = liveports + "\n Container Port: " + string(port) + liveports = liveports + "\n Public Host / Port Mappings:" + for _, binding := range bindings { + liveports = liveports + "\n IP: " + binding.HostIP + " Port: " + binding.HostPort + } + } + liveports = liveports + "\n" + log.V(0).Infof("\n\n\n\n\nThe image %s has been started in container %s as a result of the --run=true option. The container's stdout/stderr will be redirected to this command's log output to help you validate its behavior. You can also inspect the container with docker commands if you like. If the container is set up to stay running, you will have to Ctrl-C to exit this command, which should also stop the container %s. This particular invocation attempts to run with the port mappings %+v \n\n\n\n\n", image, container.ID, container.ID, liveports) +} + +// redirectResponseToOutputStream handles incoming streamed data from a +// container on a "hijacked" connection. If tty is true, expect multiplexed +// streams. Rules: 1) if you ask for streamed data from a container, you have +// to read it, otherwise sooner or later the container will block writing it. +// 2) if you're receiving multiplexed data, you have to actively read both +// streams in parallel, otherwise in the case of non-interleaved data, you, and +// then the container, will block. +func (d *stiDocker) redirectResponseToOutputStream(tty bool, outputStream, errorStream io.Writer, resp io.Reader) error { + if outputStream == nil { + outputStream = ioutil.Discard + } + if errorStream == nil { + errorStream = ioutil.Discard + } + var err error + if tty { + _, err = io.Copy(outputStream, resp) + } else { + _, err = dockerstdcopy.StdCopy(outputStream, errorStream, resp) + } + return err +} + +// holdHijackedConnection pumps data up to the container's stdin, and runs a +// goroutine to pump data down from the container's stdout and stderr. it holds +// open the HijackedResponse until all of this is done. Caller's responsibility +// to close resp, as well as outputStream and errorStream if appropriate. +func (d *stiDocker) holdHijackedConnection(tty bool, opts *RunContainerOptions, resp dockertypes.HijackedResponse) error { + receiveStdout := make(chan error, 1) + if opts.Stdout != nil || opts.Stderr != nil { + go func() { + err := d.redirectResponseToOutputStream(tty, opts.Stdout, opts.Stderr, resp.Reader) + if opts.Stdout != nil { + opts.Stdout.Close() + opts.Stdout = nil + } + if opts.Stderr != nil { + opts.Stderr.Close() + opts.Stderr = nil + } + receiveStdout <- err + }() + } else { + receiveStdout <- nil + } + + if opts.Stdin != nil { + _, err := io.Copy(resp.Conn, opts.Stdin) + opts.Stdin.Close() + opts.Stdin = nil + if err != nil { + <-receiveStdout + return err + } + } + err := resp.CloseWrite() + if err != nil { + <-receiveStdout + return err + } + + // Hang around until the streaming is over - either when the server closes + // the connection, or someone locally closes resp. + return <-receiveStdout +} + +// RunContainer creates and starts a container using the image specified in opts +// with the ability to stream input and/or output. Any non-nil +// opts.Std{in,out,err} will be closed upon return. +func (d *stiDocker) RunContainer(opts RunContainerOptions) error { + // Guarantee that Std{in,out,err} are closed upon return, including under + // error circumstances. In normal circumstances, holdHijackedConnection + // should do this for us. + defer func() { + if opts.Stdin != nil { + opts.Stdin.Close() + } + if opts.Stdout != nil { + opts.Stdout.Close() + } + if opts.Stderr != nil { + opts.Stderr.Close() + } + }() + + createOpts := opts.asDockerCreateContainerOptions() + + // get info about the specified image + image := createOpts.Config.Image + inspect, err := d.InspectImage(image) + imageMetadata := &api.Image{} + if err == nil { + updateImageWithInspect(imageMetadata, inspect) + if opts.PullImage { + _, err = d.CheckAndPullImage(image) + } + } + if err != nil { + log.V(0).Infof("error: Unable to get image metadata for %s: %v", image, err) + return err + } + + entrypoint, err := d.GetImageEntrypoint(image) + if err != nil { + return fmt.Errorf("could not get entrypoint of %q image: %v", image, err) + } + + // If the image has an entrypoint already defined, + // it will be overridden either by DefaultEntrypoint, + // or by the value in opts.Entrypoint. + // If the image does not have an entrypoint, but + // opts.Entrypoint is supplied, opts.Entrypoint will + // be respected. + if len(entrypoint) != 0 && len(opts.Entrypoint) == 0 { + opts.Entrypoint = DefaultEntrypoint + } + + // tarDestination will be passed as location to PostExecute function + // and will be used as the prefix for the CMD (scripts/run) + var tarDestination string + + var cmd []string + if !opts.TargetImage { + if len(opts.CommandExplicit) != 0 { + cmd = opts.CommandExplicit + } else { + tarDestination = determineTarDestinationDir(opts, imageMetadata) + cmd = constructCommand(opts, imageMetadata, tarDestination) + } + log.V(5).Infof("Setting %q command for container ...", strings.Join(cmd, " ")) + } + createOpts.Config.Cmd = cmd + + if createOpts.HostConfig != nil && createOpts.HostConfig.ShmSize <= 0 { + createOpts.HostConfig.ShmSize = DefaultShmSize + } + + // Create a new container. + log.V(2).Infof("Creating container with options {Name:%q Config:%+v HostConfig:%+v} ...", createOpts.Name, *util.SafeForLoggingContainerConfig(createOpts.Config), createOpts.HostConfig) + ctx, cancel := getDefaultContext() + defer cancel() + container, err := d.client.ContainerCreate(ctx, createOpts.Config, createOpts.HostConfig, createOpts.NetworkingConfig, nil, createOpts.Name) + if err != nil { + return err + } + + // Container was created, so we defer its removal, and also remove it if we get a SIGINT/SIGTERM/SIGQUIT/SIGHUP. + removeContainer := func() { + log.V(4).Infof("Removing container %q ...", container.ID) + + killErr := d.KillContainer(container.ID) + + if removeErr := d.RemoveContainer(container.ID); removeErr != nil { + if killErr != nil { + log.V(0).Infof("warning: Failed to kill container %q: %v", container.ID, killErr) + } + log.V(0).Infof("warning: Failed to remove container %q: %v", container.ID, removeErr) + } else { + log.V(4).Infof("Removed container %q", container.ID) + } + } + dumpStack := func(signal os.Signal) { + if signal == syscall.SIGQUIT { + buf := make([]byte, 1<<16) + runtime.Stack(buf, true) + fmt.Printf("%s", buf) + } + os.Exit(2) + } + return interrupt.New(dumpStack, removeContainer).Run(func() error { + log.V(2).Infof("Attaching to container %q ...", container.ID) + ctx, cancel := getDefaultContext() + defer cancel() + resp, err := d.client.ContainerAttach(ctx, container.ID, opts.asDockerAttachToContainerOptions()) + if err != nil { + log.V(0).Infof("error: Unable to attach to container %q: %v", container.ID, err) + return err + } + defer resp.Close() + + // Start the container + log.V(2).Infof("Starting container %q ...", container.ID) + ctx, cancel = getDefaultContext() + defer cancel() + err = d.client.ContainerStart(ctx, container.ID, dockertypes.ContainerStartOptions{}) + if err != nil { + return err + } + + // Run OnStart hook if defined. OnStart might block, so we run it in a + // new goroutine, and wait for it to be done later on. + onStartDone := make(chan error, 1) + if opts.OnStart != nil { + go func() { + onStartDone <- opts.OnStart(container.ID) + }() + } + + if opts.TargetImage { + // When TargetImage is true, we're dealing with an invocation of `s2i build ... --run` + // so this will, e.g., run a web server and block until the user interrupts it (or + // the container exits normally). dump port/etc information for the user. + dumpContainerInfo(container, d, image) + } + + err = d.holdHijackedConnection(false, &opts, resp) + if err != nil { + return err + } + + // Return an error if the exit code of the container is + // non-zero. + log.V(4).Infof("Waiting for container %q to stop ...", container.ID) + waitC, errC := d.client.ContainerWait(context.Background(), container.ID, dockercontainer.WaitConditionNotRunning) + select { + case result := <-waitC: + if result.StatusCode != 0 { + var output string + jsonOutput, _ := d.client.ContainerInspect(ctx, container.ID) + if err == nil && jsonOutput.ContainerJSONBase != nil && jsonOutput.ContainerJSONBase.State != nil { + state := jsonOutput.ContainerJSONBase.State + output = fmt.Sprintf("Status: %s, Error: %s, OOMKilled: %v, Dead: %v", state.Status, state.Error, state.OOMKilled, state.Dead) + } + return s2ierr.NewContainerError(container.ID, int(result.StatusCode), output) + } + case err := <-errC: + return fmt.Errorf("waiting for container %q to stop: %v", container.ID, err) + } + + // OnStart must be done before we move on. + if opts.OnStart != nil { + if err = <-onStartDone; err != nil { + return err + } + } + // Run PostExec hook if defined. + if opts.PostExec != nil { + log.V(2).Infof("Invoking PostExecute function") + if err = opts.PostExec.PostExecute(container.ID, tarDestination); err != nil { + return err + } + } + return nil + }) +} + +// GetImageID retrieves the ID of the image identified by name +func (d *stiDocker) GetImageID(name string) (string, error) { + name = getImageName(name) + image, err := d.InspectImage(name) + if err != nil { + return "", err + } + return image.ID, nil +} + +// CommitContainer commits a container to an image with a specific tag. +// The new image ID is returned +func (d *stiDocker) CommitContainer(opts CommitContainerOptions) (string, error) { + dockerOpts := dockertypes.ContainerCommitOptions{ + Reference: opts.Repository, + } + if opts.Command != nil || opts.Entrypoint != nil { + config := dockercontainer.Config{ + Cmd: opts.Command, + Entrypoint: opts.Entrypoint, + Env: opts.Env, + Labels: opts.Labels, + User: opts.User, + } + dockerOpts.Config = &config + log.V(2).Infof("Committing container with dockerOpts: %+v, config: %+v", dockerOpts, *util.SafeForLoggingContainerConfig(&config)) + } + + resp, err := d.client.ContainerCommit(context.Background(), opts.ContainerID, dockerOpts) + if err == nil { + return resp.ID, nil + } + return "", err +} + +// RemoveImage removes the image with specified ID +func (d *stiDocker) RemoveImage(imageID string) error { + ctx, cancel := getDefaultContext() + defer cancel() + _, err := d.client.ImageRemove(ctx, imageID, dockertypes.ImageRemoveOptions{}) + return err +} + +// BuildImage builds the image according to specified options +func (d *stiDocker) BuildImage(opts BuildImageOptions) error { + dockerOpts := dockertypes.ImageBuildOptions{ + Tags: []string{opts.Name}, + NoCache: true, + SuppressOutput: false, + Remove: true, + ForceRemove: true, + } + if opts.CGroupLimits != nil { + dockerOpts.Memory = opts.CGroupLimits.MemoryLimitBytes + dockerOpts.MemorySwap = opts.CGroupLimits.MemorySwap + dockerOpts.CgroupParent = opts.CGroupLimits.Parent + } + log.V(2).Infof("Building container using config: %+v", dockerOpts) + resp, err := d.client.ImageBuild(context.Background(), opts.Stdin, dockerOpts) + if err != nil { + return err + } + defer resp.Body.Close() + // since can't pass in output stream to engine-api, need to copy contents of + // the output stream they create into our output stream + _, err = io.Copy(opts.Stdout, resp.Body) + if opts.Stdout != nil { + opts.Stdout.Close() + } + return err +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/docker/fake_docker.go b/vendor/github.com/openshift/source-to-image/pkg/docker/fake_docker.go new file mode 100644 index 0000000000..06878ff7a8 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/docker/fake_docker.go @@ -0,0 +1,229 @@ +package docker + +import ( + "errors" + "io" + "io/ioutil" + + dockertypes "github.com/docker/docker/api/types" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/tar" + "github.com/openshift/source-to-image/pkg/util/fs" +) + +// FakeDocker provides a fake docker interface +type FakeDocker struct { + LocalRegistryImage string + LocalRegistryResult bool + LocalRegistryError error + RemoveContainerID string + RemoveContainerError error + DefaultURLImage string + DefaultURLResult string + DefaultURLError error + AssembleInputFilesResult string + AssembleInputFilesError error + AssembleRuntimeUserResult string + AssembleRuntimeUserError error + RunContainerOpts RunContainerOptions + RunContainerError error + RunContainerErrorBeforeStart bool + RunContainerContainerID string + RunContainerCmd []string + GetImageIDImage string + GetImageIDResult string + GetImageIDError error + GetImageUserImage string + GetImageUserResult string + GetImageUserError error + GetImageEntrypointResult []string + GetImageEntrypointError error + CommitContainerOpts CommitContainerOptions + CommitContainerResult string + CommitContainerError error + RemoveImageName string + RemoveImageError error + BuildImageOpts BuildImageOptions + BuildImageError error + PullResult bool + PullError error + OnBuildImage string + OnBuildResult []string + OnBuildError error + IsOnBuildResult bool + IsOnBuildImage string + Labels map[string]string + LabelsError error +} + +// IsImageInLocalRegistry checks if the image exists in the fake local registry +func (f *FakeDocker) IsImageInLocalRegistry(imageName string) (bool, error) { + f.LocalRegistryImage = imageName + return f.LocalRegistryResult, f.LocalRegistryError +} + +// IsImageOnBuild returns true if the builder has onbuild instructions +func (f *FakeDocker) IsImageOnBuild(imageName string) bool { + f.IsOnBuildImage = imageName + return f.IsOnBuildResult +} + +// Version returns information of the docker client and server host +func (f *FakeDocker) Version() (dockertypes.Version, error) { + return dockertypes.Version{}, nil +} + +// GetImageWorkdir returns the workdir +func (f *FakeDocker) GetImageWorkdir(name string) (string, error) { + return "/", nil +} + +// GetOnBuild returns the list of onbuild instructions for the given image +func (f *FakeDocker) GetOnBuild(imageName string) ([]string, error) { + f.OnBuildImage = imageName + return f.OnBuildResult, f.OnBuildError +} + +// RemoveContainer removes a fake Docker container +func (f *FakeDocker) RemoveContainer(id string) error { + f.RemoveContainerID = id + return f.RemoveContainerError +} + +// KillContainer kills a fake container +func (f *FakeDocker) KillContainer(id string) error { + return nil +} + +// GetScriptsURL returns a default STI scripts URL +func (f *FakeDocker) GetScriptsURL(image string) (string, error) { + f.DefaultURLImage = image + return f.DefaultURLResult, f.DefaultURLError +} + +// GetAssembleInputFiles finds a io.openshift.s2i.assemble-input-files label on the given image. +func (f *FakeDocker) GetAssembleInputFiles(image string) (string, error) { + return f.AssembleInputFilesResult, f.AssembleInputFilesError +} + +// GetAssembleRuntimeUser finds a io.openshift.s2i.assemble-runtime-user label on the given image. +func (f *FakeDocker) GetAssembleRuntimeUser(image string) (string, error) { + return f.AssembleRuntimeUserResult, f.AssembleRuntimeUserError +} + +// RunContainer runs a fake Docker container +func (f *FakeDocker) RunContainer(opts RunContainerOptions) error { + f.RunContainerOpts = opts + if f.RunContainerErrorBeforeStart { + return f.RunContainerError + } + if opts.Stdout != nil { + opts.Stdout.Close() + } + if opts.Stderr != nil { + opts.Stderr.Close() + } + if opts.OnStart != nil { + if err := opts.OnStart(""); err != nil { + return err + } + } + if opts.Stdin != nil { + _, err := io.Copy(ioutil.Discard, opts.Stdin) + if err != nil { + return err + } + } + if opts.PostExec != nil { + opts.PostExec.PostExecute(f.RunContainerContainerID, string(opts.Command)) + } + return f.RunContainerError +} + +// UploadToContainer uploads artifacts to the container. +func (f *FakeDocker) UploadToContainer(fs fs.FileSystem, srcPath, destPath, container string) error { + return nil +} + +// UploadToContainerWithTarWriter uploads artifacts to the container. +func (f *FakeDocker) UploadToContainerWithTarWriter(fs fs.FileSystem, srcPath, destPath, container string, makeTarWriter func(io.Writer) tar.Writer) error { + return errors.New("not implemented") +} + +// DownloadFromContainer downloads file (or directory) from the container. +func (f *FakeDocker) DownloadFromContainer(containerPath string, w io.Writer, container string) error { + return errors.New("not implemented") +} + +// GetImageID returns a fake Docker image ID +func (f *FakeDocker) GetImageID(image string) (string, error) { + f.GetImageIDImage = image + return f.GetImageIDResult, f.GetImageIDError +} + +// GetImageUser returns a fake user +func (f *FakeDocker) GetImageUser(image string) (string, error) { + f.GetImageUserImage = image + return f.GetImageUserResult, f.GetImageUserError +} + +// GetImageEntrypoint returns an empty entrypoint +func (f *FakeDocker) GetImageEntrypoint(image string) ([]string, error) { + return f.GetImageEntrypointResult, f.GetImageEntrypointError +} + +// CommitContainer commits a fake Docker container +func (f *FakeDocker) CommitContainer(opts CommitContainerOptions) (string, error) { + f.CommitContainerOpts = opts + return f.CommitContainerResult, f.CommitContainerError +} + +// RemoveImage removes a fake Docker image +func (f *FakeDocker) RemoveImage(name string) error { + f.RemoveImageName = name + return f.RemoveImageError +} + +// CheckImage checks image in local registry +func (f *FakeDocker) CheckImage(name string) (*api.Image, error) { + return nil, nil +} + +// PullImage pulls a fake docker image +func (f *FakeDocker) PullImage(imageName string) (*api.Image, error) { + if f.PullResult { + return &api.Image{}, nil + } + return nil, f.PullError +} + +// CheckAndPullImage pulls a fake docker image +func (f *FakeDocker) CheckAndPullImage(name string) (*api.Image, error) { + if f.PullResult { + return &api.Image{}, nil + } + return nil, f.PullError +} + +// BuildImage builds image +func (f *FakeDocker) BuildImage(opts BuildImageOptions) error { + f.BuildImageOpts = opts + if opts.Stdin != nil { + _, err := io.Copy(ioutil.Discard, opts.Stdin) + if err != nil { + return err + } + } + return f.BuildImageError +} + +// GetLabels returns the labels of the image +func (f *FakeDocker) GetLabels(name string) (map[string]string, error) { + return f.Labels, f.LabelsError +} + +// CheckReachable returns if the Docker daemon is reachable from s2i +func (f *FakeDocker) CheckReachable() error { + return nil +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/docker/util.go b/vendor/github.com/openshift/source-to-image/pkg/docker/util.go new file mode 100644 index 0000000000..1fd3216555 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/docker/util.go @@ -0,0 +1,465 @@ +package docker + +import ( + "bufio" + "bytes" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/docker/distribution/reference" + cliconfig "github.com/docker/docker/cli/config" + "github.com/docker/docker/client" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/api/constants" + s2ierr "github.com/openshift/source-to-image/pkg/errors" + utillog "github.com/openshift/source-to-image/pkg/util/log" + "github.com/openshift/source-to-image/pkg/util/user" +) + +var ( + // log is a placeholder until the builders pass an output stream down client + // facing libraries should not be using log + log = utillog.StderrLog + + // DefaultEntrypoint is the default entry point used when starting containers + DefaultEntrypoint = []string{"/usr/bin/env"} +) + +// AuthConfigurations maps a registry name to an AuthConfig, as used for +// example in the .dockercfg file +type AuthConfigurations struct { + Configs map[string]api.AuthConfig +} + +type dockerConfig struct { + Auth string `json:"auth"` + Email string `json:"email"` +} + +const ( + // maxErrorOutput is the maximum length of the error output saved for + // processing + maxErrorOutput = 1024 + defaultRegistry = "https://index.docker.io/v1/" +) + +// GetImageRegistryAuth retrieves the appropriate docker client authentication +// object for a given image name and a given set of client authentication +// objects. +func GetImageRegistryAuth(auths *AuthConfigurations, imageName string) api.AuthConfig { + log.V(5).Infof("Getting docker credentials for %s", imageName) + if auths == nil { + return api.AuthConfig{} + } + ref, err := parseNamedDockerImageReference(imageName) + if err != nil { + log.V(0).Infof("error: Failed to parse docker reference %s", imageName) + return api.AuthConfig{} + } + if ref.Registry != "" { + if auth, ok := auths.Configs[ref.Registry]; ok { + log.V(5).Infof("Using %s[%s] credentials for pulling %s", auth.Email, ref.Registry, imageName) + return auth + } + } + if auth, ok := auths.Configs[defaultRegistry]; ok { + log.V(5).Infof("Using %s credentials for pulling %s", auth.Email, imageName) + return auth + } + return api.AuthConfig{} +} + +// namedDockerImageReference points to a Docker image. +type namedDockerImageReference struct { + Registry string + Namespace string + Name string + Tag string + ID string +} + +// parseNamedDockerImageReference parses a Docker pull spec string into a +// NamedDockerImageReference. +func parseNamedDockerImageReference(spec string) (namedDockerImageReference, error) { + var ref namedDockerImageReference + + namedRef, err := reference.ParseNormalizedNamed(spec) + if err != nil { + return ref, err + } + + name := namedRef.Name() + i := strings.IndexRune(name, '/') + if i == -1 || (!strings.ContainsAny(name[:i], ":.") && name[:i] != "localhost") { + ref.Name = name + } else { + ref.Registry, ref.Name = name[:i], name[i+1:] + } + + if named, ok := namedRef.(reference.NamedTagged); ok { + ref.Tag = named.Tag() + } + + if named, ok := namedRef.(reference.Canonical); ok { + ref.ID = named.Digest().String() + } + + // It's not enough just to use the reference.ParseNamed(). We have to fill + // ref.Namespace from ref.Name + if i := strings.IndexRune(ref.Name, '/'); i != -1 { + ref.Namespace, ref.Name = ref.Name[:i], ref.Name[i+1:] + } + + return ref, nil +} + +// LoadImageRegistryAuth loads and returns the set of client auth objects from +// a docker config json file. +func LoadImageRegistryAuth(dockerCfg io.Reader) *AuthConfigurations { + auths, err := NewAuthConfigurations(dockerCfg) + if err != nil { + log.V(0).Infof("error: Unable to load docker config: %v", err) + return nil + } + return auths +} + +// begin next 3 methods borrowed from go-dockerclient + +// NewAuthConfigurations finishes creating the auth config array s2i pulls from +// any auth config file it is pointed to when started from the command line +func NewAuthConfigurations(r io.Reader) (*AuthConfigurations, error) { + var auth *AuthConfigurations + confs, err := parseDockerConfig(r) + if err != nil { + return nil, err + } + auth, err = authConfigs(confs) + if err != nil { + return nil, err + } + return auth, nil +} + +// parseDockerConfig does the json unmarshalling of the data we read from the +// file +func parseDockerConfig(r io.Reader) (map[string]dockerConfig, error) { + buf := new(bytes.Buffer) + buf.ReadFrom(r) + byteData := buf.Bytes() + + confsWrapper := struct { + Auths map[string]dockerConfig `json:"auths"` + }{} + if err := json.Unmarshal(byteData, &confsWrapper); err == nil { + if len(confsWrapper.Auths) > 0 { + return confsWrapper.Auths, nil + } + } + + var confs map[string]dockerConfig + if err := json.Unmarshal(byteData, &confs); err != nil { + return nil, err + } + return confs, nil +} + +// authConfigs converts a dockerConfigs map to a AuthConfigurations object. +func authConfigs(confs map[string]dockerConfig) (*AuthConfigurations, error) { + c := &AuthConfigurations{ + Configs: make(map[string]api.AuthConfig), + } + for reg, conf := range confs { + if len(conf.Auth) == 0 { + continue + } + data, err := base64.StdEncoding.DecodeString(conf.Auth) + if err != nil { + return nil, err + } + userpass := strings.SplitN(string(data), ":", 2) + if len(userpass) != 2 { + return nil, fmt.Errorf("cannot parse username/password from %s", userpass) + } + c.Configs[reg] = api.AuthConfig{ + Email: conf.Email, + Username: userpass[0], + Password: userpass[1], + ServerAddress: reg, + } + } + return c, nil +} + +// end block of 3 methods borrowed from go-dockerclient + +// StreamContainerIO starts a goroutine to take data from the reader and +// redirect it to the log function (typically we pass in glog.Error for stderr +// and glog.Info for stdout. The caller should wrap glog functions in a closure +// to ensure accurate line numbers are reported: +// https://github.com/openshift/source-to-image/issues/558 . +// StreamContainerIO returns a channel which is closed after the reader is +// closed. +func StreamContainerIO(r io.Reader, errOutput *string, logFn func(string)) <-chan struct{} { + c := make(chan struct{}, 1) + go func() { + reader := bufio.NewReader(r) + for { + text, err := reader.ReadString('\n') + if text != "" { + logFn(text) + } + if errOutput != nil && len(*errOutput) < maxErrorOutput { + *errOutput += text + "\n" + } + if err != nil { + if log.Is(2) && err != io.EOF { + log.V(0).Infof("error: Error reading docker stdout/stderr: %#v", err) + } + break + } + } + close(c) + }() + return c +} + +// TODO remove (base, tag, id) +func parseRepositoryTag(repos string) (string, string, string) { + n := strings.Index(repos, "@") + if n >= 0 { + parts := strings.Split(repos, "@") + return parts[0], "", parts[1] + } + n = strings.LastIndex(repos, ":") + if n < 0 { + return repos, "", "" + } + if tag := repos[n+1:]; !strings.Contains(tag, "/") { + return repos[:n], tag, "" + } + return repos, "", "" +} + +// PullImage pulls the Docker image specified by name taking the pull policy +// into the account. +func PullImage(name string, d Docker, policy api.PullPolicy) (*PullResult, error) { + if len(policy) == 0 { + return nil, errors.New("the policy for pull image must be set") + } + + var ( + image *api.Image + err error + ) + switch policy { + case api.PullIfNotPresent: + image, err = d.CheckAndPullImage(name) + case api.PullAlways: + log.Infof("Pulling image %q ...", name) + image, err = d.PullImage(name) + case api.PullNever: + log.Infof("Checking if image %q is available locally ...", name) + image, err = d.CheckImage(name) + } + return &PullResult{Image: image, OnBuild: d.IsImageOnBuild(name)}, err +} + +// CheckAllowedUser retrieves the execution users for a Docker image and +// checks that user against an allowed range of uids. +// - If the range of users is not empty, then the user on the Docker image +// needs to be a numeric user +// - The user's uid must be contained by the range(s) specified by the uids +// Rangelist +// - If build image uses an assemble user (via a command override or an +// image label), that user must be within the allowed range of uids. +// - If the image contains ONBUILD instructions and those instructions also +// contain any USER directives, then all users specified by those USER directives +// must meet the uid range criteria as well. +func CheckAllowedUser(d Docker, imageName string, uids user.RangeList, isOnbuild bool, assembleUserConfig string) error { + if uids == nil || uids.Empty() { + return nil + } + + // OnBuild users always need to be checked for layered builds + // Only return error if a user is not allowed, otherwise continue + if isOnbuild { + onBuildUsers, err := extractOnBuildUsers(d, imageName) + if err != nil { + return err + } + for _, usr := range onBuildUsers { + if !user.IsUserAllowed(usr, &uids) { + return s2ierr.NewUserNotAllowedError(imageName, true) + } + } + } + + // P1: Assemble user configuration + if len(assembleUserConfig) > 0 { + if !user.IsUserAllowed(assembleUserConfig, &uids) { + // Pass in the override, since assembleUser can come from the image label + return s2ierr.NewAssembleUserNotAllowedError(imageName, true) + } + return nil + } + + // P2: Assemble user label in image + assembleUser, err := extractAssembleUser(d, imageName) + if err != nil { + return err + } + if len(assembleUser) > 0 { + if !user.IsUserAllowed(extractUser(assembleUser), &uids) { + // Pass in the override, since assembleUser can come from the image label + return s2ierr.NewAssembleUserNotAllowedError(imageName, false) + } + return nil + } + + // Default - image user + imageUser, err := extractImageUser(d, imageName) + if err != nil { + return err + } + if !user.IsUserAllowed(imageUser, &uids) { + return s2ierr.NewUserNotAllowedError(imageName, false) + } + + return nil +} + +func extractUser(userSpec string) string { + if strings.Contains(userSpec, ":") { + parts := strings.SplitN(userSpec, ":", 2) + return strings.TrimSpace(parts[0]) + } + return strings.TrimSpace(userSpec) +} + +func extractImageUser(d Docker, imageName string) (string, error) { + imageUserSpec, err := d.GetImageUser(imageName) + if err != nil { + return "", err + } + imageUser := extractUser(imageUserSpec) + return imageUser, nil +} + +var dockerLineDelim = regexp.MustCompile(`[\t\v\f\r ]+`) + +// extractOnBuildUsers checks a list of Docker ONBUILD instructions for user +// directives. It returns a list of users specified in the ONBUILD directives. +func extractOnBuildUsers(d Docker, imageName string) ([]string, error) { + cmds, err := d.GetOnBuild(imageName) + var users []string + if err != nil { + return users, err + } + for _, line := range cmds { + parts := dockerLineDelim.Split(line, 2) + if strings.ToLower(parts[0]) != "user" { + continue + } + users = append(users, extractUser(parts[1])) + } + return users, nil +} + +// CheckReachable returns if the Docker daemon is reachable from s2i +func (d *stiDocker) CheckReachable() error { + _, err := d.Version() + return err +} + +func pullAndCheck(image string, docker Docker, pullPolicy api.PullPolicy, config *api.Config) (*PullResult, error) { + r, err := PullImage(image, docker, pullPolicy) + if err != nil { + return nil, err + } + + err = CheckAllowedUser(docker, image, config.AllowedUIDs, r.OnBuild, config.AssembleUser) + if err != nil { + return nil, err + } + + return r, nil +} + +// GetBuilderImage processes the config and performs operations necessary to +// make the Docker image specified as BuilderImage available locally. It +// returns information about the base image, containing metadata necessary for +// choosing the right STI build strategy. +func GetBuilderImage(docker Docker, config *api.Config) (*PullResult, error) { + return pullAndCheck(config.BuilderImage, docker, config.BuilderPullPolicy, config) +} + +// GetRebuildImage obtains the metadata information for the image specified in +// a s2i rebuild operation. Assumptions are made that the build is available +// locally since it should have been previously built. +func GetRebuildImage(docker Docker, config *api.Config) (*PullResult, error) { + return pullAndCheck(config.Tag, docker, config.BuilderPullPolicy, config) +} + +// GetRuntimeImage processes the config and performs operations necessary to +// make the Docker image specified as RuntimeImage available locally. +func GetRuntimeImage(docker Docker, config *api.Config) error { + _, err := pullAndCheck(config.RuntimeImage, docker, config.RuntimeImagePullPolicy, config) + return err +} + +// GetDefaultDockerConfig checks relevant Docker environment variables to +// provide defaults for our command line flags +func GetDefaultDockerConfig() *api.DockerConfig { + cfg := &api.DockerConfig{} + + if cfg.Endpoint = os.Getenv("DOCKER_HOST"); cfg.Endpoint == "" { + cfg.Endpoint = client.DefaultDockerHost + } + + certPath := os.Getenv("DOCKER_CERT_PATH") + if certPath == "" { + certPath = cliconfig.Dir() + } + + cfg.CertFile = filepath.Join(certPath, "cert.pem") + cfg.KeyFile = filepath.Join(certPath, "key.pem") + cfg.CAFile = filepath.Join(certPath, "ca.pem") + + if tlsVerify := os.Getenv("DOCKER_TLS_VERIFY"); tlsVerify != "" { + cfg.TLSVerify = true + } + + if useTLS := os.Getenv("DOCKER_TLS"); useTLS != "" { + cfg.UseTLS = true + } + + return cfg +} + +// GetAssembleUser finds an assemble user on the given image. +// This functions receives the config to check if the AssembleUser was defined in command line +// If the cmd is blank, it tries to fetch the value from the Builder Image defined Label (assemble-user) +// Otherwise it follows the common flow, using the USER defined in Dockerfile +func GetAssembleUser(docker Docker, config *api.Config) (string, error) { + if len(config.AssembleUser) > 0 { + return config.AssembleUser, nil + } + return extractAssembleUser(docker, config.BuilderImage) +} + +func extractAssembleUser(docker Docker, imageName string) (string, error) { + imageData, err := docker.GetLabels(imageName) + if err != nil { + return "", err + } + return imageData[constants.AssembleUserLabel], nil +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/errors/doc.go b/vendor/github.com/openshift/source-to-image/pkg/errors/doc.go new file mode 100644 index 0000000000..fde460fd21 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/errors/doc.go @@ -0,0 +1,3 @@ +// Contains error definitions for errors thrown throughout the STI code + +package errors diff --git a/vendor/github.com/openshift/source-to-image/pkg/errors/errors.go b/vendor/github.com/openshift/source-to-image/pkg/errors/errors.go new file mode 100644 index 0000000000..47258d35d4 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/errors/errors.go @@ -0,0 +1,304 @@ +package errors + +import ( + "fmt" + "os" + + "github.com/openshift/source-to-image/pkg/api/constants" + utillog "github.com/openshift/source-to-image/pkg/util/log" +) + +// Common S2I errors +const ( + InspectImageError int = 1 + iota + PullImageError + SaveArtifactsError + AssembleError + WorkdirError + BuildError + TarTimeoutError + DownloadError + ScriptsInsideImageError + InstallError + InstallErrorRequired + URLHandlerError + STIContainerError + SourcePathError + UserNotAllowedError + EmptyGitRepositoryError +) + +// Error represents an error thrown during S2I execution +type Error struct { + Message string + Details error + ErrorCode int + Suggestion string +} + +// ContainerError is an error returned when a container exits with a non-zero code. +// ExitCode contains the exit code from the container +type ContainerError struct { + Message string + Output string + ErrorCode int + Suggestion string + ExitCode int +} + +// Error returns a string for a given error +func (s Error) Error() string { + return s.Message +} + +// Error returns a string for the given error +func (s ContainerError) Error() string { + return s.Message +} + +// NewInspectImageError returns a new error which indicates there was a problem +// inspecting the image +func NewInspectImageError(name string, err error) error { + return Error{ + Message: fmt.Sprintf("unable to get metadata for %s", name), + Details: err, + ErrorCode: InspectImageError, + Suggestion: "check image name", + } +} + +// NewPullImageError returns a new error which indicates there was a problem +// pulling the image +func NewPullImageError(name string, err error) error { + return Error{ + Message: fmt.Sprintf("unable to get %s", name), + Details: err, + ErrorCode: PullImageError, + Suggestion: fmt.Sprintf("check image name, or if using a local image set the builder image pull policy to %q", "never"), + } +} + +// NewSaveArtifactsError returns a new error which indicates there was a problem +// calling save-artifacts script +func NewSaveArtifactsError(name, output string, err error) error { + return Error{ + Message: fmt.Sprintf("saving artifacts for %s failed:\n%s", name, output), + Details: err, + ErrorCode: SaveArtifactsError, + Suggestion: "check the save-artifacts script for errors", + } +} + +// NewAssembleError returns a new error which indicates there was a problem +// running assemble script +func NewAssembleError(name, output string, err error) error { + return Error{ + Message: fmt.Sprintf("assemble for %s failed:\n%s", name, output), + Details: err, + ErrorCode: AssembleError, + Suggestion: "check the assemble script output for errors", + } +} + +// NewWorkDirError returns a new error which indicates there was a problem +// when creating working directory +func NewWorkDirError(dir string, err error) error { + return Error{ + Message: fmt.Sprintf("creating temporary directory %s failed", dir), + Details: err, + ErrorCode: WorkdirError, + Suggestion: "check if you have access to your system's temporary directory", + } +} + +// NewBuildError returns a new error which indicates there was a problem +// building the image +func NewBuildError(name string, err error) error { + return Error{ + Message: fmt.Sprintf("building %s failed", name), + Details: err, + ErrorCode: BuildError, + Suggestion: "check the build output for errors", + } +} + +// NewCommitError returns a new error which indicates there was a problem +// committing the image +func NewCommitError(name string, err error) error { + return Error{ + Message: fmt.Sprintf("building %s failed when committing the image due to error: %v", name, err), + Details: err, + ErrorCode: BuildError, + Suggestion: "check the build output for errors", + } +} + +// NewTarTimeoutError returns a new error which indicates there was a problem +// when sending or receiving tar stream +func NewTarTimeoutError() error { + return Error{ + Message: fmt.Sprintf("timeout waiting for tar stream"), + Details: nil, + ErrorCode: TarTimeoutError, + Suggestion: "check the Source-To-Image scripts if it accepts tar stream for assemble and sends for save-artifacts", + } +} + +// NewDownloadError returns a new error which indicates there was a problem +// when downloading a file +func NewDownloadError(url string, code int) error { + return Error{ + Message: fmt.Sprintf("failed to retrieve %s, response code %d", url, code), + Details: nil, + ErrorCode: DownloadError, + Suggestion: "check the availability of the address", + } +} + +// NewScriptsInsideImageError returns a new error which informs of scripts +// being placed inside the image +func NewScriptsInsideImageError(url string) error { + return Error{ + Message: fmt.Sprintf("scripts inside the image: %s", url), + Details: nil, + ErrorCode: ScriptsInsideImageError, + Suggestion: "", + } +} + +// NewInstallError returns a new error which indicates there was a problem +// when downloading a script +func NewInstallError(script string) error { + return Error{ + Message: fmt.Sprintf("failed to install %v", script), + Details: nil, + ErrorCode: InstallError, + Suggestion: fmt.Sprintf("set the scripts URL parameter with the location of the S2I scripts, or check if the image has the %q label set", constants.ScriptsURLLabel), + } +} + +// NewInstallRequiredError returns a new error which indicates there was a problem +// when downloading a required script +func NewInstallRequiredError(scripts []string, label string) error { + return Error{ + Message: fmt.Sprintf("failed to install %v", scripts), + Details: nil, + ErrorCode: InstallErrorRequired, + Suggestion: fmt.Sprintf("set the scripts URL parameter with the location of the S2I scripts, or check if the image has the %q label set", constants.ScriptsURLLabel), + } +} + +// NewURLHandlerError returns a new error which indicates there was a problem +// when trying to read scripts URL +func NewURLHandlerError(url string) error { + return Error{ + Message: fmt.Sprintf("no URL handler for %s", url), + Details: nil, + ErrorCode: URLHandlerError, + Suggestion: "check the URL", + } +} + +// NewContainerError return a new error which indicates there was a problem +// invoking command inside container +func NewContainerError(name string, code int, output string) error { + return ContainerError{ + Message: fmt.Sprintf("non-zero (%d) exit code from %s", code, name), + Output: output, + ErrorCode: STIContainerError, + Suggestion: "check the container logs for more information on the failure", + ExitCode: code, + } +} + +// NewSourcePathError returns a new error which indicates there was a problem +// when accessing the source code from the local filesystem +func NewSourcePathError(path string) error { + return Error{ + Message: fmt.Sprintf("Local filesystem source path does not exist: %s", path), + Details: nil, + ErrorCode: SourcePathError, + Suggestion: "check the source code path on the local filesystem", + } +} + +// NewUserNotAllowedError returns a new error that indicates that the build +// could not run because the image uses a user outside of the range of allowed users +func NewUserNotAllowedError(image string, onbuild bool) error { + var msg string + if onbuild { + msg = fmt.Sprintf("image %q includes at least one ONBUILD instruction that sets the user to a user that is not allowed", image) + } else { + msg = fmt.Sprintf("image %q must specify a user that is numeric and within the range of allowed users", image) + } + return Error{ + Message: msg, + ErrorCode: UserNotAllowedError, + Suggestion: fmt.Sprintf("modify image %q to use a numeric user within the allowed range, or build without the allowed UIDs paremeter set", image), + } +} + +// NewAssembleUserNotAllowedError returns a new error that indicates that the build +// could not run because the build or image uses an assemble user outside of the range +// of allowed users. +func NewAssembleUserNotAllowedError(image string, usesConfig bool) error { + var msg, suggestion string + if usesConfig { + msg = "assemble user must be numeric and within the range of allowed users" + suggestion = "build without the allowed UIDs or assemble user configurations set" + } else { + msg = fmt.Sprintf("image %q includes the %q label whose value is not within the allowed range", image, constants.AssembleUserLabel) + suggestion = fmt.Sprintf("modify the %q label in image %q to use a numeric user within the allowed range, or build without the allowed UIDs configuration set", constants.AssembleUserLabel, image) + } + return Error{ + Message: msg, + ErrorCode: UserNotAllowedError, + Suggestion: suggestion, + } +} + +// NewEmptyGitRepositoryError returns a new error which indicates that a found +// .git directory has no tracking information, e.g. if the user simply used +// `git init` and forgot about the repository +func NewEmptyGitRepositoryError(source string) error { + return Error{ + Message: fmt.Sprintf("The git repository \"%s\" has no tracking information or commits", source), + ErrorCode: EmptyGitRepositoryError, + Suggestion: "Either commit files to the Git repository, remove the .git directory from the project, or force copy of source files to ignore the repository.", + } +} + +// log is a placeholder until the builders pass an output stream down +// client facing libraries should not be using log +var log = utillog.StderrLog + +// CheckError checks input error. +// 1. if the input error is nil, the function does nothing but return. +// 2. if the input error is a kind of Error which is thrown during S2I execution, +// the function handle it with Suggestion and Details. +// 3. if the input error is a kind of system Error which is unknown, the function exit with 1. +func CheckError(err error) { + if err == nil { + return + } + + if e, ok := err.(Error); ok { + log.Errorf("An error occurred: %v", e) + log.Errorf("Suggested solution: %v", e.Suggestion) + if e.Details != nil { + log.V(1).Infof("Details: %v", e.Details) + } + log.Error("If the problem persists consult the docs at https://github.com/openshift/source-to-image/tree/master/docs. " + + "Eventually reach us on freenode #openshift or file an issue at https://github.com/openshift/source-to-image/issues " + + "providing us with a log from your build using log output level 3.") + os.Exit(e.ErrorCode) + } else { + log.Errorf("An error occurred: %v", err) + os.Exit(1) + } +} + +// UsageError checks command usage error. +func UsageError(msg string) error { + return fmt.Errorf("%s", msg) +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/ignore/doc.go b/vendor/github.com/openshift/source-to-image/pkg/ignore/doc.go new file mode 100644 index 0000000000..7883c76d94 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/ignore/doc.go @@ -0,0 +1,3 @@ +// implements an interface around providing ignore file capabillities like seen in .dockerignore or .gitignore + +package ignore diff --git a/vendor/github.com/openshift/source-to-image/pkg/ignore/ignore.go b/vendor/github.com/openshift/source-to-image/pkg/ignore/ignore.go new file mode 100644 index 0000000000..cbc116824d --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/ignore/ignore.go @@ -0,0 +1,136 @@ +package ignore + +import ( + "bufio" + "io" + "os" + "path/filepath" + "strings" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/api/constants" + utillog "github.com/openshift/source-to-image/pkg/util/log" +) + +var log = utillog.StderrLog + +// DockerIgnorer ignores files based on the contents of the .s2iignore file +type DockerIgnorer struct{} + +// Ignore removes files from the workspace based on the contents of the +// .s2iignore file +func (b *DockerIgnorer) Ignore(config *api.Config) error { + /* + so, to duplicate the .dockerignore capabilities (https://docs.docker.com/reference/builder/#dockerignore-file) + we have a flow that follows: + 0) First note, .dockerignore rules are NOT recursive (unlike .gitignore) .. you have to list subdir explicitly + 1) Read in the exclusion patterns + 2) Skip over comments (noted by #) + 3) note overrides (via exclamation sign i.e. !) and reinstate files (don't remove) as needed + 4) leverage Glob matching to build list, as .dockerignore is documented as following filepath.Match / filepath.Glob + 5) del files + 1 to 4 is in getListOfFilesToIgnore + */ + filesToDel, lerr := b.GetListOfFilesToIgnore(config.WorkingSourceDir) + if lerr != nil { + return lerr + } + + if filesToDel == nil { + return nil + } + + // delete compiled list of files + for _, fileToDel := range filesToDel { + log.V(5).Infof("attempting to remove file %s \n", fileToDel) + rerr := os.RemoveAll(fileToDel) + if rerr != nil { + log.Errorf("error removing file %s because of %v \n", fileToDel, rerr) + return rerr + } + } + + return nil +} + +// GetListOfFilesToIgnore returns list of files from the workspace based on the contents of the +// .s2iignore file +func (b *DockerIgnorer) GetListOfFilesToIgnore(workingDir string) (map[string]string, error) { + path := filepath.Join(workingDir, constants.IgnoreFile) + file, err := os.Open(path) + if err != nil { + if !os.IsNotExist(err) { + log.Errorf("Ignore processing, problem opening %s because of %v\n", path, err) + return nil, err + } + log.V(4).Info(".s2iignore file does not exist") + return nil, nil + } + defer file.Close() + + filesToDel := make(map[string]string) + scanner := bufio.NewScanner(file) + for scanner.Scan() { + filespec := strings.Trim(scanner.Text(), " ") + + if len(filespec) == 0 { + continue + } + + if strings.HasPrefix(filespec, "#") { + continue + } + + log.V(4).Infof(".s2iignore lists a file spec of %s \n", filespec) + + if strings.HasPrefix(filespec, "!") { + //remove any existing files to del that the override covers + // and patterns later on that undo this take precedence + + // first, remove ! ... note, replace ! with */ did not have + // expected effect with filepath.Match + filespec = strings.Replace(filespec, "!", "", 1) + + // iterate through and determine ones to leave in + dontDel := []string{} + for candidate := range filesToDel { + compare := filepath.Join(workingDir, filespec) + log.V(5).Infof("For %s and %s see if it matches the spec %s which means that we leave in\n", filespec, candidate, compare) + leaveIn, _ := filepath.Match(compare, candidate) + if leaveIn { + log.V(5).Infof("Not removing %s \n", candidate) + dontDel = append(dontDel, candidate) + } else { + log.V(5).Infof("No match for %s and %s \n", filespec, candidate) + } + } + + // now remove any matches from files to delete list + for _, leaveIn := range dontDel { + delete(filesToDel, leaveIn) + } + continue + } + + globspec := filepath.Join(workingDir, filespec) + log.V(4).Infof("using globspec %s \n", globspec) + list, gerr := filepath.Glob(globspec) + if gerr != nil { + log.V(4).Infof("Glob failed with %v \n", gerr) + } else { + for _, globresult := range list { + log.V(5).Infof("Glob result %s \n", globresult) + filesToDel[globresult] = globresult + + } + } + + } + + if err := scanner.Err(); err != nil && err != io.EOF { + log.Errorf("Problem processing .s2iignore %v \n", err) + return nil, err + } + + return filesToDel, nil +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/scm/doc.go b/vendor/github.com/openshift/source-to-image/pkg/scm/doc.go new file mode 100644 index 0000000000..7f1c5d2d77 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/scm/doc.go @@ -0,0 +1 @@ +package scm diff --git a/vendor/github.com/openshift/source-to-image/pkg/scm/downloaders/empty/noop.go b/vendor/github.com/openshift/source-to-image/pkg/scm/downloaders/empty/noop.go new file mode 100644 index 0000000000..87262ade33 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/scm/downloaders/empty/noop.go @@ -0,0 +1,21 @@ +package empty + +import ( + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/scm/git" + utillog "github.com/openshift/source-to-image/pkg/util/log" +) + +var log = utillog.StderrLog + +// Noop is for build configs with an empty Source definition, where +// the assemble script is responsible for retrieving source +type Noop struct { +} + +// Download is a no-op downloader so that Noop satisfies build.Downloader +func (n *Noop) Download(config *api.Config) (*git.SourceInfo, error) { + log.V(1).Info("No source location defined (the assemble script is responsible for obtaining the source)") + + return &git.SourceInfo{}, nil +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/scm/downloaders/file/download.go b/vendor/github.com/openshift/source-to-image/pkg/scm/downloaders/file/download.go new file mode 100644 index 0000000000..acf2333554 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/scm/downloaders/file/download.go @@ -0,0 +1,69 @@ +package file + +import ( + "fmt" + "path/filepath" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/api/constants" + "github.com/openshift/source-to-image/pkg/ignore" + "github.com/openshift/source-to-image/pkg/scm/git" + "github.com/openshift/source-to-image/pkg/util/fs" + utillog "github.com/openshift/source-to-image/pkg/util/log" +) + +var log = utillog.StderrLog + +// RecursiveCopyError indicates a copy operation failed because the destination is within the copy's source tree. +type RecursiveCopyError struct { + error +} + +// File represents a simplest possible Downloader implementation where the +// sources are just copied from local directory. +type File struct { + fs.FileSystem +} + +// Download copies sources from a local directory into the working directory. +// Caller guarantees that config.Source.IsLocal() is true. +func (f *File) Download(config *api.Config) (*git.SourceInfo, error) { + config.WorkingSourceDir = filepath.Join(config.WorkingDir, constants.Source) + + copySrc := config.Source.LocalPath() + if len(config.ContextDir) > 0 { + copySrc = filepath.Join(copySrc, config.ContextDir) + } + + log.V(1).Infof("Copying sources from %q to %q", copySrc, config.WorkingSourceDir) + absWorkingSourceDir, err := filepath.Abs(config.WorkingSourceDir) + if err != nil { + return nil, err + } + absCopySrc, err := filepath.Abs(copySrc) + if err != nil { + return nil, err + } + if filepath.HasPrefix(absWorkingSourceDir, absCopySrc) { + return nil, RecursiveCopyError{error: fmt.Errorf("recursive copy requested, source directory %q contains the target directory %q", copySrc, config.WorkingSourceDir)} + } + + di := ignore.DockerIgnorer{} + filesToIgnore, lerr := di.GetListOfFilesToIgnore(copySrc) + if lerr != nil { + return nil, lerr + } + + if copySrc != config.WorkingSourceDir { + f.KeepSymlinks(config.KeepSymlinks) + err := f.CopyContents(copySrc, config.WorkingSourceDir, filesToIgnore) + if err != nil { + return nil, err + } + } + + return &git.SourceInfo{ + Location: config.Source.LocalPath(), + ContextDir: config.ContextDir, + }, nil +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/scm/downloaders/git/clone.go b/vendor/github.com/openshift/source-to-image/pkg/scm/downloaders/git/clone.go new file mode 100644 index 0000000000..ff944368e2 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/scm/downloaders/git/clone.go @@ -0,0 +1,94 @@ +package git + +import ( + "os" + "path/filepath" + "runtime" + + "k8s.io/klog/v2" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/api/constants" + "github.com/openshift/source-to-image/pkg/scm/git" + "github.com/openshift/source-to-image/pkg/util/fs" +) + +// Clone knows how to clone a Git repository. +type Clone struct { + git.Git + fs.FileSystem +} + +// Download downloads the application source code from the Git repository +// and checkout the Ref specified in the config. +func (c *Clone) Download(config *api.Config) (*git.SourceInfo, error) { + targetSourceDir := filepath.Join(config.WorkingDir, constants.Source) + config.WorkingSourceDir = targetSourceDir + + ref := config.Source.URL.Fragment + if ref == "" { + ref = "HEAD" + } + + if len(config.ContextDir) > 0 { + targetSourceDir = filepath.Join(config.WorkingDir, constants.ContextTmp) + klog.V(1).Infof("Downloading %q (%q) ...", config.Source, config.ContextDir) + } else { + klog.V(1).Infof("Downloading %q ...", config.Source) + } + + if !config.IgnoreSubmodules { + klog.V(2).Infof("Cloning sources into %q", targetSourceDir) + } else { + klog.V(2).Infof("Cloning sources (ignoring submodules) into %q", targetSourceDir) + } + + cloneConfig := git.CloneConfig{Quiet: true} + err := c.Clone(config.Source, targetSourceDir, cloneConfig) + if err != nil { + klog.V(0).Infof("error: git clone failed: %v", err) + return nil, err + } + + err = c.Checkout(targetSourceDir, ref) + if err != nil { + return nil, err + } + klog.V(1).Infof("Checked out %q", ref) + if !config.IgnoreSubmodules { + err = c.SubmoduleUpdate(targetSourceDir, true, true) + if err != nil { + return nil, err + } + klog.V(1).Infof("Updated submodules for %q", ref) + } + + // Record Git's knowledge about file permissions + if runtime.GOOS == "windows" { + filemodes, err := c.LsTree(filepath.Join(targetSourceDir, config.ContextDir), ref, true) + if err != nil { + return nil, err + } + for _, filemode := range filemodes { + c.Chmod(filepath.Join(targetSourceDir, config.ContextDir, filemode.Name()), os.FileMode(filemode.Mode())&os.ModePerm) + } + } + + info := c.GetInfo(targetSourceDir) + if len(config.ContextDir) > 0 { + originalTargetDir := filepath.Join(config.WorkingDir, constants.Source) + c.RemoveDirectory(originalTargetDir) + path := filepath.Join(targetSourceDir, config.ContextDir) + err := c.CopyContents(path, originalTargetDir, nil) + if err != nil { + return nil, err + } + c.RemoveDirectory(targetSourceDir) + } + + if len(config.ContextDir) > 0 { + info.ContextDir = config.ContextDir + } + + return info, nil +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/scm/git/git.go b/vendor/github.com/openshift/source-to-image/pkg/scm/git/git.go new file mode 100644 index 0000000000..a0b83fc335 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/scm/git/git.go @@ -0,0 +1,316 @@ +package git + +import ( + "bufio" + "bytes" + "fmt" + "os" + "os/exec" + "path/filepath" + "regexp" + "strconv" + "strings" + + "github.com/openshift/source-to-image/pkg/util/cmd" + "github.com/openshift/source-to-image/pkg/util/cygpath" + "github.com/openshift/source-to-image/pkg/util/fs" + utillog "github.com/openshift/source-to-image/pkg/util/log" +) + +var log = utillog.StderrLog +var lsTreeRegexp = regexp.MustCompile("([0-7]{6}) [^ ]+ [0-9a-f]{40}\t(.*)") + +// Git is an interface used by main STI code to extract/checkout git repositories +type Git interface { + Clone(source *URL, target string, opts CloneConfig) error + Checkout(repo, ref string) error + SubmoduleUpdate(repo string, init, recursive bool) error + LsTree(repo, ref string, recursive bool) ([]os.FileInfo, error) + GetInfo(string) *SourceInfo +} + +// New returns a new instance of the default implementation of the Git interface +func New(fs fs.FileSystem, runner cmd.CommandRunner) Git { + return &stiGit{ + FileSystem: fs, + CommandRunner: runner, + } +} + +type stiGit struct { + fs.FileSystem + cmd.CommandRunner +} + +func cloneConfigToArgs(opts CloneConfig) []string { + result := []string{} + if opts.Quiet { + result = append(result, "--quiet") + } + if opts.Recursive { + result = append(result, "--recursive") + } + return result +} + +// followGitSubmodule looks at a .git /file/ and tries to retrieve from inside +// it the gitdir value, which is supposed to indicate the location of the +// corresponding .git /directory/. Note: the gitdir value should point directly +// to the corresponding .git directory even in the case of nested submodules. +func followGitSubmodule(fs fs.FileSystem, gitPath string) (string, error) { + f, err := os.Open(gitPath) + if err != nil { + return "", err + } + defer f.Close() + + sc := bufio.NewScanner(f) + if sc.Scan() { + s := sc.Text() + + if strings.HasPrefix(s, "gitdir: ") { + newGitPath := s[8:] + + if !filepath.IsAbs(newGitPath) { + newGitPath = filepath.Join(filepath.Dir(gitPath), newGitPath) + } + + fi, err := fs.Stat(newGitPath) + if err != nil && !os.IsNotExist(err) { + return "", err + } + if os.IsNotExist(err) || !fi.IsDir() { + return "", fmt.Errorf("gitdir link in .git file %q is invalid", gitPath) + } + return newGitPath, nil + } + } + + return "", fmt.Errorf("unable to parse .git file %q", gitPath) +} + +// IsLocalNonBareGitRepository returns true if dir hosts a non-bare git +// repository, i.e. it contains a ".git" subdirectory or file (submodule case). +func IsLocalNonBareGitRepository(fs fs.FileSystem, dir string) (bool, error) { + _, err := fs.Stat(filepath.Join(dir, ".git")) + if os.IsNotExist(err) { + return false, nil + } + if err != nil { + return false, err + } + return true, nil +} + +// LocalNonBareGitRepositoryIsEmpty returns true if the non-bare git repository +// at dir has no refs or objects. It also handles the case of dir being a +// checked out git submodule. +func LocalNonBareGitRepositoryIsEmpty(fs fs.FileSystem, dir string) (bool, error) { + gitPath := filepath.Join(dir, ".git") + + fi, err := fs.Stat(gitPath) + if err != nil { + return false, err + } + + if !fi.IsDir() { + gitPath, err = followGitSubmodule(fs, gitPath) + if err != nil { + return false, err + } + } + + // Search for any file in .git/{objects,refs}. We don't just search the + // base .git directory because of the hook samples that are normally + // generated with `git init` + found := false + for _, dir := range []string{"objects", "refs"} { + err := fs.Walk(filepath.Join(gitPath, dir), func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if !info.IsDir() { + found = true + } + + if found { + return filepath.SkipDir + } + + return nil + }) + + if err != nil { + return false, err + } + + if found { + return false, nil + } + } + + return true, nil +} + +// HasGitBinary checks if the 'git' binary is available on the system +func HasGitBinary() bool { + _, err := exec.LookPath("git") + return err == nil +} + +// Clone clones a git repository to a specific target directory. +func (h *stiGit) Clone(src *URL, target string, c CloneConfig) error { + var err error + + source := *src + + if cygpath.UsingCygwinGit { + if source.IsLocal() { + source.URL.Path, err = cygpath.ToSlashCygwin(source.LocalPath()) + if err != nil { + return err + } + } + + target, err = cygpath.ToSlashCygwin(target) + if err != nil { + return err + } + } + + cloneArgs := append([]string{"clone"}, cloneConfigToArgs(c)...) + cloneArgs = append(cloneArgs, []string{source.StringNoFragment(), target}...) + stderr := &bytes.Buffer{} + opts := cmd.CommandOpts{Stderr: stderr} + err = h.RunWithOptions(opts, "git", cloneArgs...) + if err != nil { + log.Errorf("Clone failed: source %s, target %s, with output %q", source, target, stderr.String()) + return err + } + return nil +} + +// Checkout checks out a specific branch reference of a given git repository +func (h *stiGit) Checkout(repo, ref string) error { + opts := cmd.CommandOpts{ + Stdout: os.Stdout, + Stderr: os.Stderr, + Dir: repo, + } + if log.Is(1) { + return h.RunWithOptions(opts, "git", "checkout", "--quiet", ref) + } + return h.RunWithOptions(opts, "git", "checkout", ref) +} + +// SubmoduleInit initializes/clones submodules +func (h *stiGit) SubmoduleInit(repo string) error { + opts := cmd.CommandOpts{ + Stdout: os.Stdout, + Stderr: os.Stderr, + Dir: repo, + } + return h.RunWithOptions(opts, "git", "submodule", "init") +} + +// SubmoduleUpdate checks out submodules to their correct version. +// Optionally also inits submodules, optionally operates recursively. +func (h *stiGit) SubmoduleUpdate(repo string, init, recursive bool) error { + updateArgs := []string{"submodule", "update"} + if init { + updateArgs = append(updateArgs, "--init") + } + if recursive { + updateArgs = append(updateArgs, "--recursive") + } + + opts := cmd.CommandOpts{ + Stdout: os.Stdout, + Stderr: os.Stderr, + Dir: repo, + } + return h.RunWithOptions(opts, "git", updateArgs...) +} + +// LsTree returns a slice of os.FileInfo objects populated with the paths and +// file modes of files known to Git. This is used on Windows systems where the +// executable mode metadata is lost on git checkout. +func (h *stiGit) LsTree(repo, ref string, recursive bool) ([]os.FileInfo, error) { + args := []string{"ls-tree", ref} + if recursive { + args = append(args, "-r") + } + + opts := cmd.CommandOpts{ + Dir: repo, + } + + r, err := h.StartWithStdoutPipe(opts, "git", args...) + if err != nil { + return nil, err + } + + submodules := []string{} + rv := []os.FileInfo{} + scanner := bufio.NewScanner(r) + for scanner.Scan() { + text := scanner.Text() + m := lsTreeRegexp.FindStringSubmatch(text) + if m == nil { + return nil, fmt.Errorf("unparsable response %q from git ls-files", text) + } + mode, _ := strconv.ParseInt(m[1], 8, 0) + path := m[2] + if recursive && mode == 0160000 { // S_IFGITLINK + submodules = append(submodules, filepath.Join(repo, path)) + continue + } + rv = append(rv, &fs.FileInfo{FileMode: os.FileMode(mode), FileName: path}) + } + err = scanner.Err() + if err != nil { + h.Wait() + return nil, err + } + + err = h.Wait() + if err != nil { + return nil, err + } + + for _, submodule := range submodules { + rrv, err := h.LsTree(submodule, "HEAD", recursive) + if err != nil { + return nil, err + } + rv = append(rv, rrv...) + } + + return rv, nil +} + +// GetInfo retrieves the information about the source code and commit +func (h *stiGit) GetInfo(repo string) *SourceInfo { + git := func(arg ...string) string { + command := exec.Command("git", arg...) + command.Dir = repo + out, err := command.CombinedOutput() + if err != nil { + log.V(1).Infof("Error executing 'git %#v': %s (%v)", arg, out, err) + return "" + } + return strings.TrimSpace(string(out)) + } + return &SourceInfo{ + Location: git("config", "--get", "remote.origin.url"), + Ref: git("rev-parse", "--abbrev-ref", "HEAD"), + CommitID: git("rev-parse", "--verify", "HEAD"), + AuthorName: git("--no-pager", "show", "-s", "--format=%an", "HEAD"), + AuthorEmail: git("--no-pager", "show", "-s", "--format=%ae", "HEAD"), + CommitterName: git("--no-pager", "show", "-s", "--format=%cn", "HEAD"), + CommitterEmail: git("--no-pager", "show", "-s", "--format=%ce", "HEAD"), + Date: git("--no-pager", "show", "-s", "--format=%ad", "HEAD"), + Message: git("--no-pager", "show", "-s", "--format=%<(80,trunc)%s", "HEAD"), + } +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/scm/git/testhelpers.go b/vendor/github.com/openshift/source-to-image/pkg/scm/git/testhelpers.go new file mode 100644 index 0000000000..8b1b0cb9b3 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/scm/git/testhelpers.go @@ -0,0 +1,86 @@ +package git + +import ( + "io/ioutil" + "os" + "path/filepath" + + "github.com/openshift/source-to-image/pkg/util/cmd" + "github.com/openshift/source-to-image/pkg/util/cygpath" +) + +// CreateLocalGitDirectory creates a git directory with a commit +func CreateLocalGitDirectory() (string, error) { + cr := cmd.NewCommandRunner() + + dir, err := CreateEmptyLocalGitDirectory() + if err != nil { + return "", err + } + + f, err := os.Create(filepath.Join(dir, "testfile")) + if err != nil { + return "", err + } + f.Close() + + err = cr.RunWithOptions(cmd.CommandOpts{Dir: dir}, "git", "add", ".") + if err != nil { + return "", err + } + + err = cr.RunWithOptions(cmd.CommandOpts{Dir: dir, EnvAppend: []string{"GIT_AUTHOR_NAME=test", "GIT_AUTHOR_EMAIL=test@test", "GIT_COMMITTER_NAME=test", "GIT_COMMITTER_EMAIL=test@test"}}, "git", "commit", "-m", "testcommit") + if err != nil { + return "", err + } + + return dir, nil +} + +// CreateEmptyLocalGitDirectory creates a git directory with no checkin yet +func CreateEmptyLocalGitDirectory() (string, error) { + cr := cmd.NewCommandRunner() + + dir, err := ioutil.TempDir(os.TempDir(), "gitdir-s2i-test") + if err != nil { + return "", err + } + + err = cr.RunWithOptions(cmd.CommandOpts{Dir: dir}, "git", "init") + if err != nil { + return "", err + } + + return dir, nil +} + +// CreateLocalGitDirectoryWithSubmodule creates a git directory with a submodule +func CreateLocalGitDirectoryWithSubmodule() (string, error) { + cr := cmd.NewCommandRunner() + + submodule, err := CreateLocalGitDirectory() + if err != nil { + return "", err + } + defer os.RemoveAll(submodule) + + if cygpath.UsingCygwinGit { + var err error + submodule, err = cygpath.ToSlashCygwin(submodule) + if err != nil { + return "", err + } + } + + dir, err := CreateEmptyLocalGitDirectory() + if err != nil { + return "", err + } + + err = cr.RunWithOptions(cmd.CommandOpts{Dir: dir}, "git", "submodule", "add", submodule, "submodule") + if err != nil { + return "", err + } + + return dir, nil +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/scm/git/types.go b/vendor/github.com/openshift/source-to-image/pkg/scm/git/types.go new file mode 100644 index 0000000000..d6e03a0bd1 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/scm/git/types.go @@ -0,0 +1,51 @@ +package git + +// CloneConfig specifies the options used when cloning the application source +// code. +type CloneConfig struct { + Recursive bool + Quiet bool +} + +// SourceInfo stores information about the source code +type SourceInfo struct { + // Ref represents a commit SHA-1, valid Git branch name or a Git tag + // The output image will contain this information as 'io.openshift.build.commit.ref' label. + Ref string + + // CommitID represents an arbitrary extended object reference in Git as SHA-1 + // The output image will contain this information as 'io.openshift.build.commit.id' label. + CommitID string + + // Date contains a date when the committer created the commit. + // The output image will contain this information as 'io.openshift.build.commit.date' label. + Date string + + // AuthorName contains the name of the author + // The output image will contain this information (along with AuthorEmail) as 'io.openshift.build.commit.author' label. + AuthorName string + + // AuthorEmail contains the e-mail of the author + // The output image will contain this information (along with AuthorName) as 'io.openshift.build.commit.author' lablel. + AuthorEmail string + + // CommitterName contains the name of the committer + CommitterName string + + // CommitterEmail contains the e-mail of the committer + CommitterEmail string + + // Message represents the first 80 characters from the commit message. + // The output image will contain this information as 'io.openshift.build.commit.message' label. + Message string + + // Location contains a valid URL to the original repository. + // The output image will contain this information as 'io.openshift.build.source-location' label. + Location string + + // ContextDir contains path inside the Location directory that + // contains the application source code. + // The output image will contain this information as 'io.openshift.build.source-context-dir' + // label. + ContextDir string +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/scm/git/url.go b/vendor/github.com/openshift/source-to-image/pkg/scm/git/url.go new file mode 100644 index 0000000000..15f2cc3dc2 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/scm/git/url.go @@ -0,0 +1,194 @@ +package git + +import ( + "fmt" + "net/url" + "path/filepath" + "regexp" + "runtime" + "strings" +) + +// According to git-clone(1), a "Git URL" can be in one of three broad types: +// 1) A standards-compliant URL. +// a) The scheme may be followed by '://', +// e.g. https://github.com/openshift/origin, file:///foo/bar, etc. In +// this case, note that among other things, a standards-compliant file URL +// must have an empty host part, an absolute path and no backslashes. The +// Git for Windows URL parser accepts many non-compliant URLs, but we +// don't. +// b) Alternatively, the scheme may be followed by '::', in which case it is +// treated as an transport/opaque address pair, e.g. +// http::http://github.com/openshift/origin.git . +// 2) The "alternative scp-like syntax", including a ':' with no preceding '/', +// but not of the form C:... on Windows, e.g. +// git@github.com:openshift/origin, etc. +// 3) An OS-specific relative or absolute local file path, e.g. foo/bar, +// C:\foo\bar, etc. +// +// We extend all of the above URL types to additionally accept an optional +// appended #fragment, which is given to specify a git reference. +// +// The git client allows Git URL rewriting rules to be defined. The meaning of +// a Git URL cannot be 100% guaranteed without consulting the rewriting rules. + +// URLType indicates the type of the URL (see above) +type URLType int + +const ( + // URLTypeURL is the URL type (see above) + URLTypeURL URLType = iota + // URLTypeSCP is the SCP type (see above) + URLTypeSCP + // URLTypeLocal is the local type (see above) + URLTypeLocal +) + +// String returns a string representation of the URLType +func (t URLType) String() string { + switch t { + case URLTypeURL: + return "URLTypeURL" + case URLTypeSCP: + return "URLTypeSCP" + case URLTypeLocal: + return "URLTypeLocal" + } + panic("unknown URLType") +} + +// GoString returns a Go string representation of the URLType +func (t URLType) GoString() string { + return t.String() +} + +// URL represents a "Git URL" +type URL struct { + URL url.URL + Type URLType +} + +var urlSchemeRegexp = regexp.MustCompile("(?i)^[a-z][-a-z0-9+.]*:") // matches scheme: according to RFC3986 +var dosDriveRegexp = regexp.MustCompile("(?i)^[a-z]:") +var scpRegexp = regexp.MustCompile("^" + + "(?:([^@/]*)@)?" + // user@ (optional) + "([^/]*):" + // host: + "(.*)" + // path + "$") + +func splitOnByte(s string, c byte) (string, string) { + if i := strings.IndexByte(s, c); i != -1 { + return s[:i], s[i+1:] + } + return s, "" +} + +// Parse parses a "Git URL" +func Parse(rawurl string) (*URL, error) { + if urlSchemeRegexp.MatchString(rawurl) && + (runtime.GOOS != "windows" || !dosDriveRegexp.MatchString(rawurl)) { + u, err := url.Parse(rawurl) + if err != nil { + return nil, err + } + if u.Scheme == "file" && u.Opaque == "" { + if u.Host != "" { + return nil, fmt.Errorf("file url %q has non-empty host %q", rawurl, u.Host) + } + if runtime.GOOS == "windows" && (len(u.Path) == 0 || !filepath.IsAbs(u.Path[1:])) { + return nil, fmt.Errorf("file url %q has non-absolute path %q", rawurl, u.Path) + } + } + + return &URL{ + URL: *u, + Type: URLTypeURL, + }, nil + } + + s, fragment := splitOnByte(rawurl, '#') + + if m := scpRegexp.FindStringSubmatch(s); m != nil && + (runtime.GOOS != "windows" || !dosDriveRegexp.MatchString(s)) { + u := &url.URL{ + Host: m[2], + Path: m[3], + Fragment: fragment, + } + if m[1] != "" { + u.User = url.User(m[1]) + } + + return &URL{ + URL: *u, + Type: URLTypeSCP, + }, nil + } + + return &URL{ + URL: url.URL{ + Path: s, + Fragment: fragment, + }, + Type: URLTypeLocal, + }, nil +} + +// MustParse parses a "Git URL" and panics on failure +func MustParse(rawurl string) *URL { + u, err := Parse(rawurl) + if err != nil { + panic(err) + } + return u +} + +// String returns a string representation of the URL +func (u URL) String() string { + var s string + switch u.Type { + case URLTypeURL: + return u.URL.String() + case URLTypeSCP: + if u.URL.User != nil { + s = u.URL.User.Username() + "@" + } + s += u.URL.Host + ":" + u.URL.Path + case URLTypeLocal: + s = u.URL.Path + } + if u.URL.RawQuery != "" { + s += "?" + u.URL.RawQuery + } + if u.URL.Fragment != "" { + s += "#" + u.URL.Fragment + } + return s +} + +// StringNoFragment returns a string representation of the URL without its +// fragment +func (u URL) StringNoFragment() string { + u.URL.Fragment = "" + return u.String() +} + +// IsLocal returns true if the Git URL refers to a local repository +func (u URL) IsLocal() bool { + return u.Type == URLTypeLocal || (u.Type == URLTypeURL && u.URL.Scheme == "file" && u.URL.Opaque == "") +} + +// LocalPath returns the path to a local repository in OS-native format. It is +// assumed that IsLocal() is true +func (u URL) LocalPath() string { + switch { + case u.Type == URLTypeLocal: + return u.URL.Path + case u.Type == URLTypeURL && u.URL.Scheme == "file" && u.URL.Opaque == "": + if runtime.GOOS == "windows" && len(u.URL.Path) > 0 && u.URL.Path[0] == '/' { + return filepath.FromSlash(u.URL.Path[1:]) + } + return filepath.FromSlash(u.URL.Path) + } + panic("LocalPath called on non-local URL") +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/scm/scm.go b/vendor/github.com/openshift/source-to-image/pkg/scm/scm.go new file mode 100644 index 0000000000..f204775d4a --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/scm/scm.go @@ -0,0 +1,53 @@ +package scm + +import ( + "github.com/openshift/source-to-image/pkg/build" + "github.com/openshift/source-to-image/pkg/errors" + "github.com/openshift/source-to-image/pkg/scm/downloaders/empty" + "github.com/openshift/source-to-image/pkg/scm/downloaders/file" + gitdownloader "github.com/openshift/source-to-image/pkg/scm/downloaders/git" + "github.com/openshift/source-to-image/pkg/scm/git" + "github.com/openshift/source-to-image/pkg/util/cmd" + "github.com/openshift/source-to-image/pkg/util/fs" + utillog "github.com/openshift/source-to-image/pkg/util/log" +) + +var log = utillog.StderrLog + +// DownloaderForSource determines what SCM plugin should be used for downloading +// the sources from the repository. +func DownloaderForSource(fs fs.FileSystem, s *git.URL, forceCopy bool) (build.Downloader, error) { + log.V(4).Infof("DownloadForSource %s", s) + + if s == nil { + return &empty.Noop{}, nil + } + + if s.IsLocal() { + if forceCopy { + return &file.File{FileSystem: fs}, nil + } + + isLocalNonBareGitRepo, err := git.IsLocalNonBareGitRepository(fs, s.LocalPath()) + if err != nil { + return nil, err + } + if !isLocalNonBareGitRepo { + return &file.File{FileSystem: fs}, nil + } + + isEmpty, err := git.LocalNonBareGitRepositoryIsEmpty(fs, s.LocalPath()) + if err != nil { + return nil, err + } + if isEmpty { + return nil, errors.NewEmptyGitRepositoryError(s.LocalPath()) + } + + if !git.HasGitBinary() { + return &file.File{FileSystem: fs}, nil + } + } + + return &gitdownloader.Clone{Git: git.New(fs, cmd.NewCommandRunner()), FileSystem: fs}, nil +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/scripts/doc.go b/vendor/github.com/openshift/source-to-image/pkg/scripts/doc.go new file mode 100644 index 0000000000..6cda5fb811 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/scripts/doc.go @@ -0,0 +1,3 @@ +// Script installer for STI. Implements a script downloader and installer. + +package scripts diff --git a/vendor/github.com/openshift/source-to-image/pkg/scripts/download.go b/vendor/github.com/openshift/source-to-image/pkg/scripts/download.go new file mode 100644 index 0000000000..8e40071d52 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/scripts/download.go @@ -0,0 +1,151 @@ +package scripts + +import ( + "io" + "net/http" + "net/url" + "os" + "path/filepath" + "sync" + + "github.com/openshift/source-to-image/pkg/api" + s2ierr "github.com/openshift/source-to-image/pkg/errors" + "github.com/openshift/source-to-image/pkg/scm/git" + utillog "github.com/openshift/source-to-image/pkg/util/log" +) + +var log = utillog.StderrLog + +// Downloader downloads the specified URL to the target file location +type Downloader interface { + Download(url *url.URL, target string) (*git.SourceInfo, error) +} + +// schemeReader creates an io.Reader from the given url. +type schemeReader interface { + Read(*url.URL) (io.ReadCloser, error) +} + +type downloader struct { + schemeReaders map[string]schemeReader +} + +// NewDownloader creates an instance of the default Downloader implementation +func NewDownloader(proxyConfig *api.ProxyConfig) Downloader { + httpReader := NewHTTPURLReader(proxyConfig) + return &downloader{ + schemeReaders: map[string]schemeReader{ + "http": httpReader, + "https": httpReader, + "file": &FileURLReader{}, + "image": &ImageReader{}, + }, + } +} + +// Download downloads the file pointed to by URL into local targetFile +// Returns information a boolean flag informing whether any download/copy operation +// happened and an error if there was a problem during that operation +func (d *downloader) Download(url *url.URL, targetFile string) (*git.SourceInfo, error) { + r := d.schemeReaders[url.Scheme] + info := &git.SourceInfo{} + if r == nil { + log.Errorf("No URL handler found for %s", url.String()) + return nil, s2ierr.NewURLHandlerError(url.String()) + } + + reader, err := r.Read(url) + if err != nil { + return nil, err + } + defer reader.Close() + + out, err := os.Create(targetFile) + defer out.Close() + + if err != nil { + log.Errorf("Unable to create target file %s (%v)", targetFile, err) + return nil, err + } + + if _, err = io.Copy(out, reader); err != nil { + os.Remove(targetFile) + log.Warningf("Skipping file %s due to error copying from source: %v", targetFile, err) + return nil, err + } + + log.V(2).Infof("Downloaded '%s'", url.String()) + info.Location = url.String() + return info, nil +} + +// HTTPURLReader retrieves a response from a given HTTP(S) URL. +type HTTPURLReader struct { + Get func(url string) (*http.Response, error) +} + +var transportMap map[api.ProxyConfig]*http.Transport +var transportMapMutex sync.Mutex + +func init() { + transportMap = make(map[api.ProxyConfig]*http.Transport) +} + +// NewHTTPURLReader returns a new HTTPURLReader. +func NewHTTPURLReader(proxyConfig *api.ProxyConfig) *HTTPURLReader { + getFunc := http.Get + if proxyConfig != nil { + transportMapMutex.Lock() + transport, ok := transportMap[*proxyConfig] + if !ok { + transport = &http.Transport{ + Proxy: func(req *http.Request) (*url.URL, error) { + if proxyConfig.HTTPSProxy != nil && req.URL.Scheme == "https" { + return proxyConfig.HTTPSProxy, nil + } + return proxyConfig.HTTPProxy, nil + }, + } + transportMap[*proxyConfig] = transport + } + transportMapMutex.Unlock() + client := &http.Client{ + Transport: transport, + } + getFunc = client.Get + } + return &HTTPURLReader{Get: getFunc} +} + +// Read produces an io.Reader from an http(s) URL. +func (h *HTTPURLReader) Read(url *url.URL) (io.ReadCloser, error) { + resp, err := h.Get(url.String()) + if err != nil { + if resp != nil { + defer resp.Body.Close() + } + return nil, err + } + if resp.StatusCode == 200 || resp.StatusCode == 201 { + return resp.Body, nil + } + return nil, s2ierr.NewDownloadError(url.String(), resp.StatusCode) +} + +// FileURLReader opens a specified file and returns its stream +type FileURLReader struct{} + +// Read produces an io.Reader from a file URL +func (*FileURLReader) Read(url *url.URL) (io.ReadCloser, error) { + // for some reason url.Host may contain information about the ./ or ../ when + // specifying relative path, thus using that value as well + return os.Open(filepath.Join(url.Host, url.Path)) +} + +// ImageReader just returns information the URL is from inside the image +type ImageReader struct{} + +// Read throws Not implemented error +func (*ImageReader) Read(url *url.URL) (io.ReadCloser, error) { + return nil, s2ierr.NewScriptsInsideImageError(url.String()) +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/scripts/environment.go b/vendor/github.com/openshift/source-to-image/pkg/scripts/environment.go new file mode 100644 index 0000000000..8cf83bf5a6 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/scripts/environment.go @@ -0,0 +1,77 @@ +package scripts + +import ( + "bufio" + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/api/constants" +) + +// GetEnvironment gets the .s2i/environment file located in the sources and +// parse it into EnvironmentList. +func GetEnvironment(path string) (api.EnvironmentList, error) { + envPath := filepath.Join(path, ".s2i", constants.Environment) + if _, err := os.Stat(envPath); os.IsNotExist(err) { + return nil, errors.New("no environment file found in application sources") + } + + f, err := os.Open(envPath) + if err != nil { + return nil, errors.New("unable to read custom environment file") + } + defer f.Close() + + result := api.EnvironmentList{} + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + s := scanner.Text() + // Allow for comments in environment file + if strings.HasPrefix(s, "#") { + continue + } + result.Set(s) + } + + log.V(1).Infof("Setting %d environment variables provided by environment file in sources", len(result)) + return result, scanner.Err() +} + +// ConvertEnvironmentList converts the EnvironmentList to "key=val" strings. +func ConvertEnvironmentList(env api.EnvironmentList) (result []string) { + for _, e := range env { + result = append(result, fmt.Sprintf("%s=%s", e.Name, e.Value)) + } + return +} + +// ConvertEnvironmentToDocker converts the EnvironmentList into Dockerfile format. +func ConvertEnvironmentToDocker(env api.EnvironmentList) (result string) { + for i, e := range env { + if i == 0 { + result += fmt.Sprintf("ENV %s=\"%s\"", e.Name, escape(e.Value)) + } else { + result += fmt.Sprintf(" \\\n %s=\"%s\"", e.Name, escape(e.Value)) + } + } + result += "\n" + return +} + +// escape returns the passed-in value, escaped so that it will not undergo +// expansion in an ENV instruction. +func escape(value string) string { + result := "" + for _, ch := range value { + if strings.ContainsRune(`$"\`, ch) { + result += `\` + } + result += string(ch) + } + return result +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/scripts/install.go b/vendor/github.com/openshift/source-to-image/pkg/scripts/install.go new file mode 100644 index 0000000000..1777aba441 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/scripts/install.go @@ -0,0 +1,285 @@ +package scripts + +import ( + "fmt" + "net/url" + "path/filepath" + "strings" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/api/constants" + "github.com/openshift/source-to-image/pkg/docker" + s2ierr "github.com/openshift/source-to-image/pkg/errors" + "github.com/openshift/source-to-image/pkg/util" + "github.com/openshift/source-to-image/pkg/util/fs" +) + +// Installer interface is responsible for installing scripts needed to run the +// build. +type Installer interface { + InstallRequired(scripts []string, dstDir string) ([]api.InstallResult, error) + InstallOptional(scripts []string, dstDir string) []api.InstallResult +} + +// ScriptHandler provides an interface for various scripts source handlers. +type ScriptHandler interface { + Get(script string) *api.InstallResult + Install(*api.InstallResult) error + SetDestinationDir(string) + String() string +} + +// URLScriptHandler handles script download using URL. +type URLScriptHandler struct { + URL string + DestinationDir string + Download Downloader + FS fs.FileSystem + Name string +} + +const ( + sourcesRootAbbrev = "" + + // ScriptURLHandler is the name of the script URL handler + ScriptURLHandler = "script URL handler" + + // ImageURLHandler is the name of the image URL handler + ImageURLHandler = "image URL handler" + + // SourceHandler is the name of the source script handler + SourceHandler = "source handler" +) + +var ( + // RequiredScripts must be present to do an s2i build + RequiredScripts = []string{constants.Assemble, constants.Run} + + // OptionalScripts may be provided when doing an s2i build + OptionalScripts = []string{constants.SaveArtifacts} +) + +// SetDestinationDir sets the destination where the scripts should be +// downloaded. +func (s *URLScriptHandler) SetDestinationDir(baseDir string) { + s.DestinationDir = baseDir +} + +// String implements the String() function. +func (s *URLScriptHandler) String() string { + return s.Name +} + +// Get parses the provided URL and the script name. +func (s *URLScriptHandler) Get(script string) *api.InstallResult { + if len(s.URL) == 0 { + return nil + } + scriptURL, err := url.ParseRequestURI(s.URL + "/" + script) + if err != nil { + log.Infof("invalid script url %q: %v", s.URL, err) + return nil + } + return &api.InstallResult{ + Script: script, + URL: scriptURL.String(), + } +} + +// Install downloads the script and fix its permissions. +func (s *URLScriptHandler) Install(r *api.InstallResult) error { + downloadURL, err := url.Parse(r.URL) + if err != nil { + return err + } + dst := filepath.Join(s.DestinationDir, constants.UploadScripts, r.Script) + if _, err := s.Download.Download(downloadURL, dst); err != nil { + if e, ok := err.(s2ierr.Error); ok { + if e.ErrorCode == s2ierr.ScriptsInsideImageError { + r.Installed = true + return nil + } + } + return err + } + if err := s.FS.Chmod(dst, 0755); err != nil { + return err + } + r.Installed = true + r.Downloaded = true + return nil +} + +// SourceScriptHandler handles the case when the scripts are contained in the +// source code directory. +type SourceScriptHandler struct { + DestinationDir string + fs fs.FileSystem +} + +// Get verifies if the script is present in the source directory and get the +// installation result. +func (s *SourceScriptHandler) Get(script string) *api.InstallResult { + location := filepath.Join(s.DestinationDir, constants.SourceScripts, script) + if s.fs.Exists(location) { + return &api.InstallResult{Script: script, URL: location} + } + // TODO: The '.sti/bin' path inside the source code directory is deprecated + // and this should (and will) be removed soon. + location = filepath.FromSlash(strings.Replace(filepath.ToSlash(location), "s2i/bin", "sti/bin", 1)) + if s.fs.Exists(location) { + log.Info("DEPRECATED: Use .s2i/bin instead of .sti/bin") + return &api.InstallResult{Script: script, URL: location} + } + return nil +} + +// String implements the String() function. +func (s *SourceScriptHandler) String() string { + return SourceHandler +} + +// Install copies the script into upload directory and fix its permissions. +func (s *SourceScriptHandler) Install(r *api.InstallResult) error { + dst := filepath.Join(s.DestinationDir, constants.UploadScripts, r.Script) + if err := s.fs.Rename(r.URL, dst); err != nil { + return err + } + if err := s.fs.Chmod(dst, 0755); err != nil { + return err + } + // Make the path to scripts nicer in logs + parts := strings.Split(filepath.ToSlash(r.URL), "/") + if len(parts) > 3 { + r.URL = filepath.FromSlash(sourcesRootAbbrev + "/" + strings.Join(parts[len(parts)-3:], "/")) + } + r.Installed = true + r.Downloaded = true + return nil +} + +// SetDestinationDir sets the directory where the scripts should be uploaded. +// In case of SourceScriptHandler this is a source directory root. +func (s *SourceScriptHandler) SetDestinationDir(baseDir string) { + s.DestinationDir = baseDir +} + +// ScriptSourceManager manages various script handlers. +type ScriptSourceManager interface { + Add(ScriptHandler) + SetDownloader(Downloader) + Installer +} + +// DefaultScriptSourceManager manages the default script lookup and installation +// for source-to-image. +type DefaultScriptSourceManager struct { + Image string + ScriptsURL string + download Downloader + docker docker.Docker + dockerAuth api.AuthConfig + sources []ScriptHandler + fs fs.FileSystem +} + +// Add registers a new script source handler. +func (m *DefaultScriptSourceManager) Add(s ScriptHandler) { + if len(m.sources) == 0 { + m.sources = []ScriptHandler{} + } + m.sources = append(m.sources, s) +} + +// NewInstaller returns a new instance of the default Installer implementation +func NewInstaller(image string, scriptsURL string, proxyConfig *api.ProxyConfig, docker docker.Docker, auth api.AuthConfig, fs fs.FileSystem, config *api.Config) Installer { + m := DefaultScriptSourceManager{ + Image: image, + ScriptsURL: scriptsURL, + dockerAuth: auth, + docker: docker, + fs: fs, + download: NewDownloader(proxyConfig), + } + // Order is important here, first we try to get the scripts from provided URL, + // then we look into sources and check for .s2i/bin scripts. + if len(m.ScriptsURL) > 0 { + m.Add(&URLScriptHandler{URL: m.ScriptsURL, Download: m.download, FS: m.fs, Name: ScriptURLHandler}) + } + + m.Add(&SourceScriptHandler{fs: m.fs}) + + if m.docker != nil { + // If the detection handlers above fail, try to get the script url from the + // docker image itself. + defaultURL, err := m.docker.GetScriptsURL(m.Image) + if err == nil && defaultURL != "" { + m.Add(&URLScriptHandler{URL: defaultURL, Download: m.download, FS: m.fs, Name: ImageURLHandler}) + } + } else { + // this means we are doing a s2i build with --as-dockerfile + // so lets see if we found builder image labels + scriptsURL, _ := util.AdjustConfigWithImageLabels(config) + if len(scriptsURL) > 0 { + m.Add(&URLScriptHandler{URL: scriptsURL, Download: m.download, FS: m.fs, Name: ScriptURLHandler}) + } + } + return &m +} + +// InstallRequired Downloads and installs required scripts into dstDir, the result is a +// map of scripts with detailed information about each of the scripts install process +// with error if installing some of them failed +func (m *DefaultScriptSourceManager) InstallRequired(scripts []string, dstDir string) ([]api.InstallResult, error) { + result := m.InstallOptional(scripts, dstDir) + failedScripts := []string{} + var err error + for _, r := range result { + if r.Error != nil { + failedScripts = append(failedScripts, r.Script) + } + } + if len(failedScripts) > 0 { + err = s2ierr.NewInstallRequiredError(failedScripts, constants.ScriptsURLLabel) + } + return result, err +} + +// InstallOptional downloads and installs a set of scripts into dstDir, the result is a +// map of scripts with detailed information about each of the scripts install process +func (m *DefaultScriptSourceManager) InstallOptional(scripts []string, dstDir string) []api.InstallResult { + result := []api.InstallResult{} + for _, script := range scripts { + installed := false + failedSources := []string{} + for _, e := range m.sources { + detected := false + h := e.(ScriptHandler) + h.SetDestinationDir(dstDir) + if r := h.Get(script); r != nil { + if err := h.Install(r); err != nil { + failedSources = append(failedSources, h.String()) + // all this means is this source didn't have this particular script + log.V(4).Infof("script %q found by the %s, but failed to install: %v", script, h, err) + } else { + r.FailedSources = failedSources + result = append(result, *r) + installed = true + detected = true + log.V(4).Infof("Using %q installed from %q", script, r.URL) + } + } + if detected { + break + } + } + if !installed { + result = append(result, api.InstallResult{ + FailedSources: failedSources, + Script: script, + Error: fmt.Errorf("script %q not installed", script), + }) + } + } + return result +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/tar/doc.go b/vendor/github.com/openshift/source-to-image/pkg/tar/doc.go new file mode 100644 index 0000000000..130bcc9405 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/tar/doc.go @@ -0,0 +1,3 @@ +// Provides a Tar interface with methods to create and extract tar archives + +package tar diff --git a/vendor/github.com/openshift/source-to-image/pkg/tar/tar.go b/vendor/github.com/openshift/source-to-image/pkg/tar/tar.go new file mode 100644 index 0000000000..78ea4dcc8c --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/tar/tar.go @@ -0,0 +1,494 @@ +package tar + +import ( + "archive/tar" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" + "time" + + s2ierr "github.com/openshift/source-to-image/pkg/errors" + "github.com/openshift/source-to-image/pkg/util" + "github.com/openshift/source-to-image/pkg/util/fs" + utillog "github.com/openshift/source-to-image/pkg/util/log" +) + +var log = utillog.StderrLog + +// defaultTimeout is the amount of time that the untar will wait for a tar +// stream to extract a single file. A timeout is needed to guard against broken +// connections in which it would wait for a long time to untar and nothing would happen +const defaultTimeout = 300 * time.Second + +// DefaultExclusionPattern is the pattern of files that will not be included in a tar +// file when creating one. By default it is any file inside a .git metadata directory +var DefaultExclusionPattern = regexp.MustCompile(`(^|/)\.git(/|$)`) + +// Tar can create and extract tar files used in an STI build +type Tar interface { + // SetExclusionPattern sets the exclusion pattern for tar + // creation + SetExclusionPattern(*regexp.Regexp) + + // CreateTarFile creates a tar file in the base directory + // using the contents of dir directory + // The name of the new tar file is returned if successful + CreateTarFile(base, dir string) (string, error) + + // CreateTarStreamToTarWriter creates a tar from the given directory + // and streams it to the given writer. + // An error is returned if an error occurs during streaming. + // Archived file names are written to the logger if provided + CreateTarStreamToTarWriter(dir string, includeDirInPath bool, writer Writer, logger io.Writer) error + + // CreateTarStream creates a tar from the given directory + // and streams it to the given writer. + // An error is returned if an error occurs during streaming. + CreateTarStream(dir string, includeDirInPath bool, writer io.Writer) error + + // CreateTarStreamReader returns an io.ReadCloser from which a tar stream can be + // read. The tar stream is created using CreateTarStream. + CreateTarStreamReader(dir string, includeDirInPath bool) io.ReadCloser + + // ExtractTarStream extracts files from a given tar stream. + // Times out if reading from the stream for any given file + // exceeds the value of timeout. + ExtractTarStream(dir string, reader io.Reader) error + + // ExtractTarStreamWithLogging extracts files from a given tar stream. + // Times out if reading from the stream for any given file + // exceeds the value of timeout. + // Extracted file names are written to the logger if provided. + ExtractTarStreamWithLogging(dir string, reader io.Reader, logger io.Writer) error + + // ExtractTarStreamFromTarReader extracts files from a given tar stream. + // Times out if reading from the stream for any given file + // exceeds the value of timeout. + // Extracted file names are written to the logger if provided. + ExtractTarStreamFromTarReader(dir string, tarReader Reader, logger io.Writer) error +} + +// Reader is an interface which tar.Reader implements. +type Reader interface { + io.Reader + Next() (*tar.Header, error) +} + +// Writer is an interface which tar.Writer implements. +type Writer interface { + io.WriteCloser + Flush() error + WriteHeader(hdr *tar.Header) error +} + +// ChmodAdapter changes the mode of files and directories inline as a tarfile is +// being written +type ChmodAdapter struct { + Writer + NewFileMode int64 + NewExecFileMode int64 + NewDirMode int64 +} + +// WriteHeader changes the mode of files and directories inline as a tarfile is +// being written +func (a ChmodAdapter) WriteHeader(hdr *tar.Header) error { + if hdr.FileInfo().Mode()&os.ModeSymlink == 0 { + newMode := hdr.Mode &^ 0777 + if hdr.FileInfo().IsDir() { + newMode |= a.NewDirMode + } else if hdr.FileInfo().Mode()&0010 != 0 { // S_IXUSR + newMode |= a.NewExecFileMode + } else { + newMode |= a.NewFileMode + } + hdr.Mode = newMode + } + return a.Writer.WriteHeader(hdr) +} + +// RenameAdapter renames files and directories inline as a tarfile is being +// written +type RenameAdapter struct { + Writer + Old string + New string +} + +// WriteHeader renames files and directories inline as a tarfile is being +// written +func (a RenameAdapter) WriteHeader(hdr *tar.Header) error { + if hdr.Name == a.Old { + hdr.Name = a.New + } else if strings.HasPrefix(hdr.Name, a.Old+"/") { + hdr.Name = a.New + hdr.Name[len(a.Old):] + } + + return a.Writer.WriteHeader(hdr) +} + +// New creates a new Tar +func New(fs fs.FileSystem) Tar { + return &stiTar{ + FileSystem: fs, + exclude: DefaultExclusionPattern, + timeout: defaultTimeout, + } +} + +// NewWithTimeout creates a new Tar with the provided timeout extracting files. +func NewWithTimeout(fs fs.FileSystem, timeout time.Duration) Tar { + return &stiTar{ + FileSystem: fs, + exclude: DefaultExclusionPattern, + timeout: timeout, + } +} + +// NewParanoid creates a new Tar that has restrictions +// on what it can do while extracting files. +func NewParanoid(fs fs.FileSystem) Tar { + return &stiTar{ + FileSystem: fs, + exclude: DefaultExclusionPattern, + timeout: defaultTimeout, + disallowOverwrite: true, + disallowOutsidePaths: true, + disallowSpecialFiles: true, + } +} + +// NewParanoidWithTimeout creates a new Tar with the provided timeout extracting files. +// It has restrictions on what it can do while extracting files. +func NewParanoidWithTimeout(fs fs.FileSystem, timeout time.Duration) Tar { + return &stiTar{ + FileSystem: fs, + exclude: DefaultExclusionPattern, + timeout: timeout, + disallowOverwrite: true, + disallowOutsidePaths: true, + disallowSpecialFiles: true, + } +} + +// stiTar is an implementation of the Tar interface +type stiTar struct { + fs.FileSystem + timeout time.Duration + exclude *regexp.Regexp + includeDirInPath bool + disallowOverwrite bool + disallowOutsidePaths bool + disallowSpecialFiles bool +} + +// SetExclusionPattern sets the exclusion pattern for tar creation. The +// exclusion pattern always uses UNIX-style (/) path separators, even on +// Windows. +func (t *stiTar) SetExclusionPattern(p *regexp.Regexp) { + t.exclude = p +} + +// CreateTarFile creates a tar file from the given directory +// while excluding files that match the given exclusion pattern +// It returns the name of the created file +func (t *stiTar) CreateTarFile(base, dir string) (string, error) { + tarFile, err := ioutil.TempFile(base, "tar") + defer tarFile.Close() + if err != nil { + return "", err + } + if err = t.CreateTarStream(dir, false, tarFile); err != nil { + return "", err + } + return tarFile.Name(), nil +} + +func (t *stiTar) shouldExclude(path string) bool { + return t.exclude != nil && t.exclude.String() != "" && t.exclude.MatchString(filepath.ToSlash(path)) +} + +// CreateTarStream calls CreateTarStreamToTarWriter with a nil logger +func (t *stiTar) CreateTarStream(dir string, includeDirInPath bool, writer io.Writer) error { + tarWriter := tar.NewWriter(writer) + defer tarWriter.Close() + + return t.CreateTarStreamToTarWriter(dir, includeDirInPath, tarWriter, nil) +} + +// CreateTarStreamReader returns an io.ReadCloser from which a tar stream can be +// read. The tar stream is created using CreateTarStream. +func (t *stiTar) CreateTarStreamReader(dir string, includeDirInPath bool) io.ReadCloser { + r, w := io.Pipe() + go func() { + w.CloseWithError(t.CreateTarStream(dir, includeDirInPath, w)) + }() + return r +} + +// CreateTarStreamToTarWriter creates a tar stream on the given writer from +// the given directory while excluding files that match the given +// exclusion pattern. +func (t *stiTar) CreateTarStreamToTarWriter(dir string, includeDirInPath bool, tarWriter Writer, logger io.Writer) error { + dir = filepath.Clean(dir) // remove relative paths and extraneous slashes + log.V(5).Infof("Adding %q to tar ...", dir) + err := t.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + // on Windows, directory symlinks report as a directory and as a symlink. + // They should be treated as symlinks. + if !t.shouldExclude(path) { + // if file is a link just writing header info is enough + if info.Mode()&os.ModeSymlink != 0 { + if dir == path { + return nil + } + if err = t.writeTarHeader(tarWriter, dir, path, info, includeDirInPath, logger); err != nil { + log.Errorf("Error writing header for %q: %v", info.Name(), err) + } + // on Windows, filepath.Walk recurses into directory symlinks when it + // shouldn't. https://github.com/golang/go/issues/17540 + if err == nil && info.Mode()&os.ModeDir != 0 { + return filepath.SkipDir + } + return err + } + if info.IsDir() { + if dir == path { + return nil + } + if err = t.writeTarHeader(tarWriter, dir, path, info, includeDirInPath, logger); err != nil { + log.Errorf("Error writing header for %q: %v", info.Name(), err) + } + return err + } + + // regular files are copied into tar, if accessible + file, err := os.Open(path) + if err != nil { + log.Errorf("Ignoring file %s: %v", path, err) + return nil + } + defer file.Close() + if err = t.writeTarHeader(tarWriter, dir, path, info, includeDirInPath, logger); err != nil { + log.Errorf("Error writing header for %q: %v", info.Name(), err) + return err + } + if _, err = io.Copy(tarWriter, file); err != nil { + log.Errorf("Error copying file %q to tar: %v", path, err) + return err + } + } + return nil + }) + + if err != nil { + log.Errorf("Error writing tar: %v", err) + return err + } + + return nil +} + +// writeTarHeader writes tar header for given file, returns error if operation fails +func (t *stiTar) writeTarHeader(tarWriter Writer, dir string, path string, info os.FileInfo, includeDirInPath bool, logger io.Writer) error { + var ( + link string + err error + ) + if info.Mode()&os.ModeSymlink != 0 { + link, err = os.Readlink(path) + if err != nil { + return err + } + } + header, err := tar.FileInfoHeader(info, link) + if err != nil { + return err + } + // on Windows, tar.FileInfoHeader incorrectly interprets directory symlinks + // as directories. https://github.com/golang/go/issues/17541 + if info.Mode()&os.ModeSymlink != 0 && info.Mode()&os.ModeDir != 0 { + header.Typeflag = tar.TypeSymlink + header.Mode &^= 040000 // c_ISDIR + header.Mode |= 0120000 // c_ISLNK + header.Linkname = link + } + prefix := dir + if includeDirInPath { + prefix = filepath.Dir(prefix) + } + fileName := path + if prefix != "." { + fileName = path[1+len(prefix):] + } + header.Name = filepath.ToSlash(fileName) + header.Linkname = filepath.ToSlash(header.Linkname) + // Force the header format to PAX to support UTF-8 filenames + // and use the same format throughout the entire tar file. + header.Format = tar.FormatPAX + logFile(logger, header.Name) + log.V(5).Infof("Adding to tar: %s as %s", path, header.Name) + return tarWriter.WriteHeader(header) +} + +// ExtractTarStream calls ExtractTarStreamFromTarReader with a default reader and nil logger +func (t *stiTar) ExtractTarStream(dir string, reader io.Reader) error { + tarReader := tar.NewReader(reader) + return t.ExtractTarStreamFromTarReader(dir, tarReader, nil) +} + +// ExtractTarStreamWithLogging calls ExtractTarStreamFromTarReader with a default reader +func (t *stiTar) ExtractTarStreamWithLogging(dir string, reader io.Reader, logger io.Writer) error { + tarReader := tar.NewReader(reader) + return t.ExtractTarStreamFromTarReader(dir, tarReader, logger) +} + +// ExtractTarStreamFromTarReader extracts files from a given tar stream. +// Times out if reading from the stream for any given file +// exceeds the value of timeout +func (t *stiTar) ExtractTarStreamFromTarReader(dir string, tarReader Reader, logger io.Writer) error { + err := util.TimeoutAfter(t.timeout, "", func(timeoutTimer *time.Timer) error { + for { + header, err := tarReader.Next() + if !timeoutTimer.Stop() { + return &util.TimeoutError{} + } + timeoutTimer.Reset(t.timeout) + if err == io.EOF { + return nil + } + if err != nil { + log.Errorf("Error reading next tar header: %v", err) + return err + } + + if t.disallowSpecialFiles { + switch header.Typeflag { + case tar.TypeReg, tar.TypeRegA, tar.TypeLink, tar.TypeSymlink, tar.TypeDir, tar.TypeGNUSparse: + default: + log.Warningf("Skipping special file %s, type: %v", header.Name, header.Typeflag) + continue + } + } + + p := header.Name + if t.disallowOutsidePaths { + p = filepath.Clean(filepath.Join(dir, p)) + if !strings.HasPrefix(p, dir) { + log.Warningf("Skipping relative path file in tar: %s", header.Name) + continue + } + } + + if header.FileInfo().IsDir() { + dirPath := filepath.Join(dir, filepath.Clean(header.Name)) + log.V(3).Infof("Creating directory %s", dirPath) + if err = os.MkdirAll(dirPath, 0700); err != nil { + log.Errorf("Error creating dir %q: %v", dirPath, err) + return err + } + t.Chmod(dirPath, header.FileInfo().Mode()) + } else { + fileDir := filepath.Dir(header.Name) + dirPath := filepath.Join(dir, filepath.Clean(fileDir)) + log.V(3).Infof("Creating directory %s", dirPath) + if err = os.MkdirAll(dirPath, 0700); err != nil { + log.Errorf("Error creating dir %q: %v", dirPath, err) + return err + } + if header.Typeflag == tar.TypeSymlink { + if err := t.extractLink(dir, header, tarReader); err != nil { + log.Errorf("Error extracting link %q: %v", header.Name, err) + return err + } + continue + } + logFile(logger, header.Name) + if err := t.extractFile(dir, header, tarReader); err != nil { + log.Errorf("Error extracting file %q: %v", header.Name, err) + return err + } + } + } + }) + + if err != nil { + log.Errorf("Error extracting tar stream: %v", err) + } else { + log.V(2).Info("Done extracting tar stream") + } + + if util.IsTimeoutError(err) { + err = s2ierr.NewTarTimeoutError() + } + + return err +} + +func (t *stiTar) extractLink(dir string, header *tar.Header, tarReader io.Reader) error { + dest := filepath.Join(dir, header.Name) + source := header.Linkname + + if t.disallowOutsidePaths { + target := filepath.Clean(filepath.Join(dest, "..", source)) + if !strings.HasPrefix(target, dir) { + log.Warningf("Skipping symlink that points to relative path: %s", header.Linkname) + return nil + } + } + + if t.disallowOverwrite { + if _, err := os.Stat(dest); !os.IsNotExist(err) { + log.Warningf("Refusing to overwrite existing file: %s", dest) + return nil + } + } + + log.V(3).Infof("Creating symbolic link from %q to %q", dest, source) + + // TODO: set mtime for symlink (unfortunately we can't use os.Chtimes() and probably should use syscall) + return os.Symlink(source, dest) +} + +func (t *stiTar) extractFile(dir string, header *tar.Header, tarReader io.Reader) error { + path := filepath.Join(dir, header.Name) + if t.disallowOverwrite { + if _, err := os.Stat(path); !os.IsNotExist(err) { + log.Warningf("Refusing to overwrite existing file: %s", path) + return nil + } + } + + log.V(3).Infof("Creating %s", path) + + file, err := os.Create(path) + if err != nil { + return err + } + // The file times need to be modified after it's been closed thus this function + // is deferred after the file close (LIFO order for defer) + defer os.Chtimes(path, time.Now(), header.FileInfo().ModTime()) + defer file.Close() + log.V(3).Infof("Extracting/writing %s", path) + written, err := io.Copy(file, tarReader) + if err != nil { + return err + } + if written != header.Size { + return fmt.Errorf("wrote %d bytes, expected to write %d", written, header.Size) + } + return t.Chmod(path, header.FileInfo().Mode()) +} + +func logFile(logger io.Writer, name string) { + if logger == nil { + return + } + fmt.Fprintf(logger, "%s\n", name) +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/callback.go b/vendor/github.com/openshift/source-to-image/pkg/util/callback.go new file mode 100644 index 0000000000..fe21d0b685 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/callback.go @@ -0,0 +1,70 @@ +package util + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" +) + +// CallbackInvoker posts results to a callback URL when a STI build is done. +type CallbackInvoker interface { + ExecuteCallback(callbackURL string, success bool, labels map[string]string, messages []string) []string +} + +// NewCallbackInvoker creates an instance of the default CallbackInvoker implementation +func NewCallbackInvoker() CallbackInvoker { + invoker := &callbackInvoker{} + invoker.postFunc = invoker.httpPost + return invoker +} + +type callbackInvoker struct { + postFunc func(url, contentType string, body io.Reader) (resp *http.Response, err error) +} + +// ExecuteCallback prepares a JSON payload and posts it to the specified callback URL +func (c *callbackInvoker) ExecuteCallback(callbackURL string, success bool, labels map[string]string, messages []string) []string { + buf := new(bytes.Buffer) + writer := bufio.NewWriter(buf) + + data := map[string]interface{}{ + "success": success, + } + + if len(labels) > 0 { + data["labels"] = labels + } + + jsonBuffer := new(bytes.Buffer) + writer = bufio.NewWriter(jsonBuffer) + jsonWriter := json.NewEncoder(writer) + jsonWriter.Encode(data) + writer.Flush() + + var ( + resp *http.Response + err error + ) + for retries := 0; retries < 3; retries++ { + resp, err = c.postFunc(callbackURL, "application/json", jsonBuffer) + if err != nil { + errorMessage := fmt.Sprintf("Unable to invoke callback: %v", err) + messages = append(messages, errorMessage) + } + if resp != nil { + if resp.StatusCode >= 300 { + errorMessage := fmt.Sprintf("Callback returned with error code: %d", resp.StatusCode) + messages = append(messages, errorMessage) + } + break + } + } + return messages +} + +func (*callbackInvoker) httpPost(url, contentType string, body io.Reader) (resp *http.Response, err error) { + return http.Post(url, contentType, body) +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/cmd/cmd.go b/vendor/github.com/openshift/source-to-image/pkg/util/cmd/cmd.go new file mode 100644 index 0000000000..ddd74084f4 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/cmd/cmd.go @@ -0,0 +1,84 @@ +package cmd + +import ( + "io" + "os" + "os/exec" +) + +// CommandOpts contains options to attach Stdout/err to a command to run +// or set its initial directory +type CommandOpts struct { + Stdout io.Writer + Stderr io.Writer + Dir string + EnvAppend []string +} + +// CommandRunner executes OS commands with the given parameters and options +type CommandRunner interface { + RunWithOptions(opts CommandOpts, name string, arg ...string) error + Run(name string, arg ...string) error + StartWithStdoutPipe(opts CommandOpts, name string, arg ...string) (io.ReadCloser, error) + Wait() error +} + +// NewCommandRunner creates a new instance of the default implementation of +// CommandRunner +func NewCommandRunner() CommandRunner { + return &runner{} +} + +type runner struct { + cmd *exec.Cmd +} + +// RunWithOptions runs a command with the provided options +func (c *runner) RunWithOptions(opts CommandOpts, name string, arg ...string) error { + cmd := exec.Command(name, arg...) + if opts.Stdout != nil { + cmd.Stdout = opts.Stdout + } + if opts.Stderr != nil { + cmd.Stderr = opts.Stderr + } + if opts.Dir != "" { + cmd.Dir = opts.Dir + } + if len(opts.EnvAppend) > 0 { + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, opts.EnvAppend...) + } + return cmd.Run() +} + +// Run executes a command with default options +func (c *runner) Run(name string, arg ...string) error { + return c.RunWithOptions(CommandOpts{}, name, arg...) +} + +// StartWithStdoutPipe executes a command returning a ReadCloser connected to +// the command's stdout. +func (c *runner) StartWithStdoutPipe(opts CommandOpts, name string, arg ...string) (io.ReadCloser, error) { + c.cmd = exec.Command(name, arg...) + if opts.Stderr != nil { + c.cmd.Stderr = opts.Stderr + } + if opts.Dir != "" { + c.cmd.Dir = opts.Dir + } + if len(opts.EnvAppend) > 0 { + c.cmd.Env = os.Environ() + c.cmd.Env = append(c.cmd.Env, opts.EnvAppend...) + } + r, err := c.cmd.StdoutPipe() + if err != nil { + return nil, err + } + return r, c.cmd.Start() +} + +// Wait waits for the command to exit. +func (c *runner) Wait() error { + return c.cmd.Wait() +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/cygpath/cygpath.go b/vendor/github.com/openshift/source-to-image/pkg/util/cygpath/cygpath.go new file mode 100644 index 0000000000..83e16ff637 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/cygpath/cygpath.go @@ -0,0 +1,40 @@ +package cygpath + +import ( + "os/exec" + "path/filepath" + "runtime" + "strings" +) + +// UsingCygwinGit indicates whether we believe the host's git utility is from +// Cygwin (expects Windows paths as /cygdrive/c/dir/file) or not (expects paths +// in host-native format). +var UsingCygwinGit = isUsingCygwinGit() + +func isUsingCygwinGit() bool { + if runtime.GOOS == "windows" { + // If we find the cygpath utility (which translates between UNIX-style and + // Windows-style paths) in the same directory as git, assume we're using + // Cygwin. + cygpath, err := exec.LookPath("cygpath") + if err != nil { + return false + } + var git string + git, err = exec.LookPath("git") + if err == nil && filepath.Dir(cygpath) == filepath.Dir(git) { + return true + } + } + return false +} + +// ToSlashCygwin converts a path to a format suitable for sending to a Cygwin +// binary - `/dir/file` on UNIX-style systems; `c:\dir\file` on Windows without +// Cygwin; `/cygdrive/c/dir/file` on Windows with Cygwin. +func ToSlashCygwin(path string) (string, error) { + cmd := exec.Command("cygpath", path) + out, err := cmd.Output() + return strings.TrimRight(string(out), "\n"), err +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/doc.go b/vendor/github.com/openshift/source-to-image/pkg/util/doc.go new file mode 100644 index 0000000000..f7c1dc4244 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/doc.go @@ -0,0 +1,4 @@ +// Contains utilities for executing callbacks, executing commands, downloading files, +// and working with the file system + +package util diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/env.go b/vendor/github.com/openshift/source-to-image/pkg/util/env.go new file mode 100644 index 0000000000..45d2d20f60 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/env.go @@ -0,0 +1,76 @@ +package util + +import ( + "bufio" + "fmt" + "net/url" + "os" + "regexp" + "strings" +) + +// case insensitively match all key=value variables containing the word "proxy" +var proxyRegex = regexp.MustCompile("(?i).*proxy.*") + +// ReadEnvironmentFile reads the content for a file that contains a list of +// environment variables and values. The key-pairs are separated by a new line +// character. The file can also have comments (both '#' and '//' are supported). +func ReadEnvironmentFile(path string) (map[string]string, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + result := map[string]string{} + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + s := strings.TrimSpace(scanner.Text()) + // Allow for comments in environment file + if strings.HasPrefix(s, "#") || strings.HasPrefix(s, "//") { + continue + } + parts := strings.SplitN(s, "=", 2) + if len(parts) != 2 { + continue + } + result[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) + } + + return result, scanner.Err() +} + +// SafeForLoggingEnv attempts to strip sensitive information from proxy +// environment variable strings in key=value form. +func SafeForLoggingEnv(env []string) []string { + newEnv := make([]string, len(env)) + copy(newEnv, env) + for i, entry := range newEnv { + parts := strings.SplitN(entry, "=", 2) + if !proxyRegex.MatchString(parts[0]) { + continue + } + newVal, _ := SafeForLoggingURL(parts[1]) + newEnv[i] = fmt.Sprintf("%s=%s", parts[0], newVal) + } + return newEnv +} + +// SafeForLoggingURL removes the user:password section of +// a url if present. If not present or the value is unparseable, +// the value is returned unchanged. +func SafeForLoggingURL(input string) (string, error) { + u, err := url.Parse(input) + if err != nil { + return input, err + } + if u.User == nil { + return input, nil + } + if _, passwordSet := u.User.Password(); passwordSet { + // wipe out the user info from the url. + u.User = url.User("redacted") + } + return u.String(), nil +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/fs/fs.go b/vendor/github.com/openshift/source-to-image/pkg/util/fs/fs.go new file mode 100644 index 0000000000..92d01303a2 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/fs/fs.go @@ -0,0 +1,407 @@ +package fs + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "sync" + "time" + + s2ierr "github.com/openshift/source-to-image/pkg/errors" + utillog "github.com/openshift/source-to-image/pkg/util/log" +) + +var log = utillog.StderrLog + +// FileSystem allows STI to work with the file system and +// perform tasks such as creating and deleting directories +type FileSystem interface { + Chmod(file string, mode os.FileMode) error + Rename(from, to string) error + MkdirAll(dirname string) error + MkdirAllWithPermissions(dirname string, perm os.FileMode) error + Mkdir(dirname string) error + Exists(file string) bool + Copy(sourcePath, targetPath string, filesToIgnore map[string]string) error + CopyContents(sourcePath, targetPath string, filesToIgnore map[string]string) error + RemoveDirectory(dir string) error + CreateWorkingDirectory() (string, error) + Open(file string) (io.ReadCloser, error) + Create(file string) (io.WriteCloser, error) + WriteFile(file string, data []byte) error + ReadDir(string) ([]os.FileInfo, error) + Stat(string) (os.FileInfo, error) + Lstat(string) (os.FileInfo, error) + Walk(string, filepath.WalkFunc) error + Readlink(string) (string, error) + Symlink(string, string) error + KeepSymlinks(bool) + ShouldKeepSymlinks() bool +} + +// NewFileSystem creates a new instance of the default FileSystem +// implementation +func NewFileSystem() FileSystem { + return &fs{ + fileModes: make(map[string]os.FileMode), + keepSymlinks: false, + } +} + +type fs struct { + // on Windows, fileModes is used to track the UNIX file mode of every file we + // work with; m is used to synchronize access to fileModes. + fileModes map[string]os.FileMode + m sync.Mutex + keepSymlinks bool +} + +// FileInfo is a struct which implements os.FileInfo. We use it (a) for test +// purposes, and (b) because we enrich the FileMode on Windows systems +type FileInfo struct { + FileName string + FileSize int64 + FileMode os.FileMode + FileModTime time.Time + FileIsDir bool + FileSys interface{} +} + +// Name retuns the filename of fi +func (fi *FileInfo) Name() string { + return fi.FileName +} + +// Size returns the file size of fi +func (fi *FileInfo) Size() int64 { + return fi.FileSize +} + +// Mode returns the file mode of fi +func (fi *FileInfo) Mode() os.FileMode { + return fi.FileMode +} + +// ModTime returns the file modification time of fi +func (fi *FileInfo) ModTime() time.Time { + return fi.FileModTime +} + +// IsDir returns true if fi refers to a directory +func (fi *FileInfo) IsDir() bool { + return fi.FileIsDir +} + +// Sys returns the sys interface of fi +func (fi *FileInfo) Sys() interface{} { + return fi.FileSys +} + +func copyFileInfo(src os.FileInfo) *FileInfo { + return &FileInfo{ + FileName: src.Name(), + FileSize: src.Size(), + FileMode: src.Mode(), + FileModTime: src.ModTime(), + FileIsDir: src.IsDir(), + FileSys: src.Sys(), + } +} + +// Stat returns a FileInfo describing the named file. +func (h *fs) Stat(path string) (os.FileInfo, error) { + fi, err := os.Stat(path) + if runtime.GOOS == "windows" && err == nil { + fi = h.enrichFileInfo(path, fi) + } + return fi, err +} + +// Lstat returns a FileInfo describing the named file (not following symlinks). +func (h *fs) Lstat(path string) (os.FileInfo, error) { + fi, err := os.Lstat(path) + if runtime.GOOS == "windows" && err == nil { + fi = h.enrichFileInfo(path, fi) + } + return fi, err +} + +// ReadDir reads the directory named by dirname and returns a list of directory +// entries sorted by filename. +func (h *fs) ReadDir(path string) ([]os.FileInfo, error) { + fis, err := ioutil.ReadDir(path) + if runtime.GOOS == "windows" && err == nil { + h.enrichFileInfos(path, fis) + } + return fis, err +} + +// Chmod sets the file mode +func (h *fs) Chmod(file string, mode os.FileMode) error { + err := os.Chmod(file, mode) + if runtime.GOOS == "windows" && err == nil { + h.m.Lock() + h.fileModes[file] = mode + h.m.Unlock() + return nil + } + return err +} + +// Rename renames or moves a file +func (h *fs) Rename(from, to string) error { + return os.Rename(from, to) +} + +// MkdirAll creates the directory and all its parents +func (h *fs) MkdirAll(dirname string) error { + return os.MkdirAll(dirname, 0700) +} + +// MkdirAllWithPermissions creates the directory and all its parents with the provided permissions +func (h *fs) MkdirAllWithPermissions(dirname string, perm os.FileMode) error { + return os.MkdirAll(dirname, perm) +} + +// Mkdir creates the specified directory +func (h *fs) Mkdir(dirname string) error { + return os.Mkdir(dirname, 0700) +} + +// Exists determines whether the given file exists +func (h *fs) Exists(file string) bool { + _, err := h.Stat(file) + return err == nil +} + +// Copy copies the source to a destination. +// If the source is a file, then the destination has to be a file as well, +// otherwise you will get an error. +// If the source is a directory, then the destination has to be a directory and +// we copy the content of the source directory to destination directory +// recursively. +func (h *fs) Copy(source string, dest string, filesToIgnore map[string]string) (err error) { + return doCopy(h, source, dest, filesToIgnore) +} + +// KeepSymlinks configures fs to copy symlinks from src as symlinks to dst. +// Default behavior is to follow symlinks and copy files by content. +func (h *fs) KeepSymlinks(k bool) { + h.keepSymlinks = k +} + +// ShouldKeepSymlinks is exported only due to the design of fs util package +// and how the tests are structured. It indicates whether the implementation +// should copy symlinks as symlinks or follow symlinks and copy by content. +func (h *fs) ShouldKeepSymlinks() bool { + return h.keepSymlinks +} + +// If src is symlink and symlink copy has been enabled, copy as a symlink. +// Otherwise ignore symlink and let rest of the code follow the symlink +// and copy the content of the file +func handleSymlink(h FileSystem, source, dest string) (bool, error) { + lstatinfo, lstaterr := h.Lstat(source) + _, staterr := h.Stat(source) + if lstaterr == nil && + lstatinfo.Mode()&os.ModeSymlink != 0 { + if os.IsNotExist(staterr) { + log.V(5).Infof("(broken) L %q -> %q", source, dest) + } else if h.ShouldKeepSymlinks() { + log.V(5).Infof("L %q -> %q", source, dest) + } else { + // symlink not handled here, will copy the file content + return false, nil + } + linkdest, err := h.Readlink(source) + if err != nil { + return true, err + } + return true, h.Symlink(linkdest, dest) + } + // symlink not handled here, will copy the file content + return false, nil +} + +func doCopy(h FileSystem, source, dest string, filesToIgnore map[string]string) error { + if handled, err := handleSymlink(h, source, dest); handled || err != nil { + return err + } + sourcefile, err := h.Open(source) + if err != nil { + return err + } + defer sourcefile.Close() + sourceinfo, err := h.Stat(source) + if err != nil { + return err + } + + if sourceinfo.IsDir() { + _, ok := filesToIgnore[source] + if ok { + log.V(5).Infof("Directory %q ignored", source) + return nil + } + log.V(5).Infof("D %q -> %q", source, dest) + return h.CopyContents(source, dest, filesToIgnore) + } + + destinfo, _ := h.Stat(dest) + if destinfo != nil && destinfo.IsDir() { + return fmt.Errorf("destination must be full path to a file, not directory") + } + destfile, err := h.Create(dest) + if err != nil { + return err + } + defer destfile.Close() + _, ok := filesToIgnore[source] + if ok { + log.V(5).Infof("File %q ignored", source) + return nil + } + log.V(5).Infof("F %q -> %q", source, dest) + if _, err := io.Copy(destfile, sourcefile); err != nil { + return err + } + + return h.Chmod(dest, sourceinfo.Mode()) +} + +// CopyContents copies the content of the source directory to a destination +// directory. +// If the destination directory does not exists, it will be created. +// The source directory itself will not be copied, only its content. If you +// want this behavior, the destination must include the source directory name. +// It will skip any files provided in filesToIgnore from being copied +func (h *fs) CopyContents(src string, dest string, filesToIgnore map[string]string) (err error) { + sourceinfo, err := h.Stat(src) + if err != nil { + return err + } + if err = os.MkdirAll(dest, sourceinfo.Mode()); err != nil { + return err + } + directory, err := os.Open(src) + if err != nil { + return err + } + defer directory.Close() + objects, err := directory.Readdir(-1) + if err != nil { + return err + } + + for _, obj := range objects { + source := filepath.Join(src, obj.Name()) + destination := filepath.Join(dest, obj.Name()) + if err := h.Copy(source, destination, filesToIgnore); err != nil { + return err + } + } + return +} + +// RemoveDirectory removes the specified directory and all its contents +func (h *fs) RemoveDirectory(dir string) error { + log.V(2).Infof("Removing directory '%s'", dir) + + // HACK: If deleting a directory in windows, call out to the system to do the deletion + // TODO: Remove this workaround when we switch to go 1.7 -- os.RemoveAll should + // be fixed for Windows in that release. https://github.com/golang/go/issues/9606 + if runtime.GOOS == "windows" { + command := exec.Command("cmd.exe", "/c", fmt.Sprintf("rd /s /q %s", dir)) + output, err := command.Output() + if err != nil { + log.Errorf("Error removing directory %q: %v %s", dir, err, string(output)) + return err + } + return nil + } + + err := os.RemoveAll(dir) + if err != nil { + log.Errorf("Error removing directory '%s': %v", dir, err) + } + return err +} + +// CreateWorkingDirectory creates a directory to be used for STI +func (h *fs) CreateWorkingDirectory() (directory string, err error) { + directory, err = ioutil.TempDir("", "s2i") + if err != nil { + return "", s2ierr.NewWorkDirError(directory, err) + } + + return directory, err +} + +// Open opens a file and returns a ReadCloser interface to that file +func (h *fs) Open(filename string) (io.ReadCloser, error) { + return os.Open(filename) +} + +// Create creates a file and returns a WriteCloser interface to that file +func (h *fs) Create(filename string) (io.WriteCloser, error) { + return os.Create(filename) +} + +// WriteFile opens a file and writes data to it, returning error if such +// occurred +func (h *fs) WriteFile(filename string, data []byte) error { + return ioutil.WriteFile(filename, data, 0700) +} + +// Walk walks the file tree rooted at root, calling walkFn for each file or +// directory in the tree, including root. +func (h *fs) Walk(root string, walkFn filepath.WalkFunc) error { + wrapper := func(path string, info os.FileInfo, err error) error { + if runtime.GOOS == "windows" && err == nil { + info = h.enrichFileInfo(path, info) + } + return walkFn(path, info, err) + } + return filepath.Walk(root, wrapper) +} + +// enrichFileInfo is used on Windows. It takes an os.FileInfo object, e.g. as +// returned by os.Stat, and enriches the OS-returned file mode with the "real" +// UNIX file mode, if we know what it is. +func (h *fs) enrichFileInfo(path string, fi os.FileInfo) os.FileInfo { + h.m.Lock() + if mode, ok := h.fileModes[path]; ok { + fi = copyFileInfo(fi) + fi.(*FileInfo).FileMode = mode + } + h.m.Unlock() + return fi +} + +// enrichFileInfos is used on Windows. It takes an array of os.FileInfo +// objects, e.g. as returned by os.ReadDir, and for each file enriches the OS- +// returned file mode with the "real" UNIX file mode, if we know what it is. +func (h *fs) enrichFileInfos(root string, fis []os.FileInfo) { + h.m.Lock() + for i := range fis { + if mode, ok := h.fileModes[filepath.Join(root, fis[i].Name())]; ok { + fis[i] = copyFileInfo(fis[i]) + fis[i].(*FileInfo).FileMode = mode + } + } + h.m.Unlock() +} + +// Readlink reads the destination of a symlink +func (h *fs) Readlink(name string) (string, error) { + return os.Readlink(name) +} + +// Symlink creates a symlink at newname, pointing to oldname +func (h *fs) Symlink(oldname, newname string) error { + return os.Symlink(oldname, newname) +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/injection.go b/vendor/github.com/openshift/source-to-image/pkg/util/injection.go new file mode 100644 index 0000000000..0035bac355 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/injection.go @@ -0,0 +1,156 @@ +package util + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/util/fs" +) + +// FixInjectionsWithRelativePath fixes the injections that does not specify the +// destination directory or the directory is relative to use the provided +// working directory. +func FixInjectionsWithRelativePath(workdir string, injections api.VolumeList) api.VolumeList { + if len(injections) == 0 { + return injections + } + newList := api.VolumeList{} + for _, injection := range injections { + changed := false + if filepath.Clean(filepath.FromSlash(injection.Destination)) == "." { + injection.Destination = filepath.ToSlash(workdir) + changed = true + } + if filepath.ToSlash(injection.Destination)[0] != '/' { + injection.Destination = filepath.ToSlash(filepath.Join(workdir, injection.Destination)) + changed = true + } + if changed { + log.V(5).Infof("Using %q as a destination for injecting %q", injection.Destination, injection.Source) + } + newList = append(newList, injection) + } + return newList +} + +// ListFilesToTruncate returns a flat list of all files that are injected into a +// container which need to be truncated. All files from nested directories are returned in the list. +func ListFilesToTruncate(fs fs.FileSystem, injections api.VolumeList) ([]string, error) { + result := []string{} + for _, s := range injections { + if s.Keep { + continue + } + files, err := ListFiles(fs, s) + if err != nil { + return nil, err + } + result = append(result, files...) + } + return result, nil +} + +// ListFiles returns a flat list of all files injected into a container for the given `VolumeSpec`. +func ListFiles(fs fs.FileSystem, spec api.VolumeSpec) ([]string, error) { + result := []string{} + if _, err := os.Stat(spec.Source); err != nil { + return nil, err + } + err := fs.Walk(spec.Source, func(path string, f os.FileInfo, err error) error { + if err != nil { + return err + } + + // Detected files will be truncated. k8s' AtomicWriter creates + // directories and symlinks to directories in order to inject files. + // An attempt to truncate either a dir or symlink to a dir will fail. + // Thus, we need to dereference symlinks to see if they might point + // to a directory. + // Do not try to simplify this logic to simply return nil if a symlink + // is detected. During the tar transfer to an assemble image, symlinked + // files are turned concrete (i.e. they will be turned into regular files + // containing the content of their target). These newly concrete files + // need to be truncated as well. + + if f.Mode()&os.ModeSymlink != 0 { + linkDest, err := filepath.EvalSymlinks(path) + if err != nil { + return fmt.Errorf("unable to evaluate symlink [%v]: %v", path, err) + } + // Evaluate the destination of the link. + f, err = os.Lstat(linkDest) + if err != nil { + // This is not a fatal error. If AtomicWrite tried multiple times, a symlink might not point + // to a valid destination. + log.Warningf("Unable to lstat symlink destination [%s]->[%s]. err: %v. Partial atomic write?", path, linkDest, err) + return nil + } + } + + if f.IsDir() { + return nil + } + + newPath := filepath.ToSlash(filepath.Join(spec.Destination, strings.TrimPrefix(path, spec.Source))) + result = append(result, newPath) + return nil + }) + if err != nil { + return nil, err + } + return result, nil +} + +// CreateTruncateFilesScript creates a shell script that contains truncation +// of all files we injected into the container. The path to the script is returned. +// When the scriptName is provided, it is also truncated together with all +// secrets. +func CreateTruncateFilesScript(files []string, scriptName string) (string, error) { + rmScript := "set -e\n" + for _, s := range files { + rmScript += fmt.Sprintf("truncate -s0 %q\n", s) + } + + f, err := ioutil.TempFile("", "s2i-injection-remove") + if err != nil { + return "", err + } + if len(scriptName) > 0 { + rmScript += fmt.Sprintf("truncate -s0 %q\n", scriptName) + } + rmScript += "set +e\n" + err = ioutil.WriteFile(f.Name(), []byte(rmScript), 0700) + return f.Name(), err +} + +// CreateInjectionResultFile creates a result file with the message from the provided injection +// error. The path to the result file is returned. If the provided error is nil, an empty file is +// created. +func CreateInjectionResultFile(injectErr error) (string, error) { + f, err := ioutil.TempFile("", "s2i-injection-result") + if err != nil { + return "", err + } + if injectErr != nil { + err = ioutil.WriteFile(f.Name(), []byte(injectErr.Error()), 0700) + } + return f.Name(), err +} + +// HandleInjectionError handles the error caused by injection and provide +// reasonable suggestion to users. +func HandleInjectionError(p api.VolumeSpec, err error) error { + if err == nil { + return nil + } + if strings.Contains(err.Error(), "no such file or directory") { + log.Errorf("The destination directory for %q injection must exist in container (%q)", p.Source, p.Destination) + return err + } + log.Errorf("Error occurred during injecting %q to %q: %v", p.Source, p.Destination, err) + return err +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/interrupt/interrupt.go b/vendor/github.com/openshift/source-to-image/pkg/util/interrupt/interrupt.go new file mode 100644 index 0000000000..997d1843d6 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/interrupt/interrupt.go @@ -0,0 +1,104 @@ +/* +Derived from: + +https://github.com/kubernetes/kubernetes + +Copyright 2016 The Kubernetes Authors All rights reserved. + +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. +*/ + +package interrupt + +import ( + "os" + "os/signal" + "sync" + "syscall" +) + +// terminationSignals are signals that cause the program to exit in the +// supported platforms (linux, darwin, windows). +var terminationSignals = []os.Signal{syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT} + +// A Handler executes notification functions after a critical section (the +// function passed to its Run method), even in the presence of process +// termination via a termination signal. A final handler is executed if a +// termination signal is caught. The handler guarantees that the provided notify +// functions will be called at most once. A Handler is not reusable in the sense +// that its Run method should be called only once: calling it more times will +// not execute any of notify nor final functions. +type Handler struct { + notify []func() + final func(os.Signal) + once sync.Once +} + +// New creates a new Handler. The final function will be called only if a +// termination signal is caught, after all notify functions are run. If final is +// nil, it defaults to os.Exit(2). The final function may call os.Exit if +// exiting is desired. The notify functions will be called when a termination +// signal is caught, or after the argument to the Run method completes. +func New(final func(os.Signal), notify ...func()) *Handler { + return &Handler{ + final: final, + notify: notify, + } +} + +// Run calls fn in the current goroutine, while waiting for a termination signal +// in a separate goroutine. If a signal is caught while this method is running, +// the notify functions will be called sequentially in order, and then the final +// function is called. Otherwise, only the notify functions are called after fn +// returns. The fn function may not complete, for example in case of a call to +// os.Exit. +func (h *Handler) Run(fn func() error) error { + ch := make(chan os.Signal, 1) + signal.Notify(ch, terminationSignals...) + defer close(ch) + defer signal.Stop(ch) + go func() { + sig, ok := <-ch + if !ok { + return + } + h.signal(sig) + }() + defer h.close() + return fn() +} + +// close calls the notify functions, used when no signal was caught and the Run +// method returned. +func (h *Handler) close() { + h.once.Do(func() { + for _, fn := range h.notify { + fn() + } + }) +} + +// signal calls the notify functions and final, used when a signal was caught +// while the Run method was running. If final is nil, os.Exit will be called as +// a default. +func (h *Handler) signal(s os.Signal) { + h.once.Do(func() { + for _, fn := range h.notify { + fn() + } + if h.final == nil { + os.Exit(2) + } + h.final(s) + }) +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/labels.go b/vendor/github.com/openshift/source-to-image/pkg/util/labels.go new file mode 100644 index 0000000000..f63321683c --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/labels.go @@ -0,0 +1,96 @@ +package util + +import ( + "fmt" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/api/constants" + "github.com/openshift/source-to-image/pkg/scm/git" +) + +// GenerateOutputImageLabels generate the labels based on the s2i Config +// and source repository informations. +func GenerateOutputImageLabels(info *git.SourceInfo, config *api.Config) map[string]string { + labels := map[string]string{} + namespace := constants.DefaultNamespace + if len(config.LabelNamespace) > 0 { + namespace = config.LabelNamespace + } + + labels = GenerateLabelsFromConfig(labels, config, namespace) + labels = GenerateLabelsFromSourceInfo(labels, info, namespace) + return labels +} + +// GenerateLabelsFromConfig generate the labels based on build s2i Config +func GenerateLabelsFromConfig(labels map[string]string, config *api.Config, namespace string) map[string]string { + if len(config.Description) > 0 { + labels[constants.KubernetesDescriptionLabel] = config.Description + } + + if len(config.DisplayName) > 0 { + labels[constants.KubernetesDisplayNameLabel] = config.DisplayName + } else if len(config.Tag) > 0 { + labels[constants.KubernetesDisplayNameLabel] = config.Tag + } + + addBuildLabel(labels, "image", config.BuilderImage, namespace) + return labels +} + +// GenerateLabelsFromSourceInfo generate the labels based on the source repository +// informations. +func GenerateLabelsFromSourceInfo(labels map[string]string, info *git.SourceInfo, namespace string) map[string]string { + if info == nil { + log.V(3).Info("Unable to fetch source information, the output image labels will not be set") + return labels + } + + if len(info.AuthorName) > 0 { + author := fmt.Sprintf("%s <%s>", info.AuthorName, info.AuthorEmail) + addBuildLabel(labels, "commit.author", author, namespace) + } + + addBuildLabel(labels, "commit.date", info.Date, namespace) + addBuildLabel(labels, "commit.id", info.CommitID, namespace) + addBuildLabel(labels, "commit.ref", info.Ref, namespace) + addBuildLabel(labels, "commit.message", info.Message, namespace) + addBuildLabel(labels, "source-location", info.Location, namespace) + addBuildLabel(labels, "source-context-dir", info.ContextDir, namespace) + return labels +} + +// AdjustConfigWithImageLabels examines previously retrieved builder image labels and returns +// any specified update to the scripts URL and destination settings. Note, we do +// not set those fields in the Config object itself, so as to not confuse the s2i build case in +// which the script install code expects those values to set via the explicit command line argument +// overrides. Otherwise, we can inadvertently override scripts from the source repo. +func AdjustConfigWithImageLabels(cfg *api.Config) (string, string) { + if cfg.BuilderImageLabels == nil { + return "", "" + } + + scriptsURL, destination := "", "" + if v, ok := cfg.BuilderImageLabels[constants.DeprecatedScriptsURLLabel]; ok { + scriptsURL = v + } + if v, ok := cfg.BuilderImageLabels[constants.ScriptsURLLabel]; ok { + scriptsURL = v + } + + if v, ok := cfg.BuilderImageLabels[constants.DestinationLabel]; ok { + // there is no alternative for destination obtainable from the source git clone + // so we don't track our setting of this field from the builder image labels + destination = v + } + return scriptsURL, destination +} + +// addBuildLabel adds a new "*.build.*" label into map when the +// value of this label is not empty +func addBuildLabel(to map[string]string, key, value, namespace string) { + if len(value) == 0 { + return + } + to[namespace+"build."+key] = value +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/log/log.go b/vendor/github.com/openshift/source-to-image/pkg/util/log/log.go new file mode 100644 index 0000000000..15b458a011 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/log/log.go @@ -0,0 +1,213 @@ +package log + +import ( + "bufio" + "fmt" + "io" + "os" + "strings" + "sync" + + "k8s.io/klog/v2" +) + +// Logger is a simple interface that is roughly equivalent to klog. +type Logger interface { + Is(level int32) bool + V(level int32) VerboseLogger + Infof(format string, args ...interface{}) + Info(args ...interface{}) + Warningf(format string, args ...interface{}) + Warning(args ...interface{}) + Errorf(format string, args ...interface{}) + Error(args ...interface{}) + Fatalf(format string, args ...interface{}) + Fatal(args ...interface{}) +} + +// VerboseLogger is roughly equivalent to klog's Verbose. +type VerboseLogger interface { + Infof(format string, args ...interface{}) + Info(args ...interface{}) +} + +// ToFile creates a logger that will log any items at level or below to file, and defer +// any other output to klog (no matter what the level is). +func ToFile(x io.Writer, level int32) Logger { + return &FileLogger{ + &sync.Mutex{}, + bufio.NewWriter(x), + level, + } +} + +var ( + // None implements the Logger interface but does nothing with the log output. + None Logger = discard{} + // StderrLog implements the Logger interface for stderr. + StderrLog = ToFile(os.Stderr, 2) +) + +// discard is a Logger that outputs nothing. +type discard struct{} + +// Is returns whether the current logging level is greater than or equal to the parameter. +func (discard) Is(level int32) bool { + return false +} + +// V will returns a logger which will discard output if the specified level is greater than the current logging level. +func (discarding discard) V(level int32) VerboseLogger { + return discarding +} + +// Infof records an info log entry. +func (discard) Infof(string, ...interface{}) { +} + +// Info records an info log entry. +func (discard) Info(...interface{}) { +} + +// Errorf records an error log entry. +func (discard) Errorf(string, ...interface{}) { +} + +// Error records an error log entry. +func (discard) Error(...interface{}) { +} + +// Warningf records an warning log entry. +func (discard) Warningf(string, ...interface{}) { +} + +// Warning records an warning log entry. +func (discard) Warning(...interface{}) { +} + +// Fatalf records a fatal log entry. +func (discard) Fatalf(string, ...interface{}) { +} + +// Fatal records a fatal log entry. +func (discard) Fatal(...interface{}) { +} + +// FileLogger logs the provided messages at level or below to the writer, or delegates +// to klog. +type FileLogger struct { + mutex *sync.Mutex + w *bufio.Writer + level int32 +} + +// Is returns whether the current logging level is greater than or equal to the parameter. +func (f *FileLogger) Is(level int32) bool { + return level <= f.level +} + +// V will returns a logger which will discard output if the specified level is greater than the current logging level. +func (f *FileLogger) V(level int32) VerboseLogger { + // Is the loglevel set verbose enough to accept the forthcoming log statement + if klog.V(klog.Level(level)).Enabled() { + return f + } + // Otherwise discard + return None +} + +type severity int32 + +const ( + infoLog severity = iota + warningLog + errorLog + fatalLog +) + +// Elevated logger methods output a detailed prefix for each logging statement. +// At present, we delegate to klog to accomplish this. +type elevated func(int, ...interface{}) + +type severityDetail struct { + prefix string + delegateFn elevated +} + +var severities = []severityDetail{ + infoLog: {"", klog.InfoDepth}, + warningLog: {"WARNING: ", klog.WarningDepth}, + errorLog: {"ERROR: ", klog.ErrorDepth}, + fatalLog: {"FATAL: ", klog.FatalDepth}, +} + +func (f *FileLogger) writeln(sev severity, line string) { + severity := severities[sev] + + // If the loglevel has been elevated above this file logger's verbosity (generally set to 2) + // then delegate ALL messages to elevated logger in order to leverage its file/line/timestamp + // prefix information. + if klog.V(klog.Level(f.level + 1)).Enabled() { + severity.delegateFn(3, line) + } else { + // buf.io is not threadsafe, so serialize access to the stream + f.mutex.Lock() + defer f.mutex.Unlock() + f.w.WriteString(severity.prefix) + f.w.WriteString(line) + if !strings.HasSuffix(line, "\n") { + f.w.WriteByte('\n') + } + f.w.Flush() + } +} + +func (f *FileLogger) outputf(sev severity, format string, args ...interface{}) { + f.writeln(sev, fmt.Sprintf(format, args...)) +} + +func (f *FileLogger) output(sev severity, args ...interface{}) { + f.writeln(sev, fmt.Sprint(args...)) +} + +// Infof records an info log entry. +func (f *FileLogger) Infof(format string, args ...interface{}) { + f.outputf(infoLog, format, args...) +} + +// Info records an info log entry. +func (f *FileLogger) Info(args ...interface{}) { + f.output(infoLog, args...) +} + +// Warningf records an warning log entry. +func (f *FileLogger) Warningf(format string, args ...interface{}) { + f.outputf(warningLog, format, args...) +} + +// Warning records an warning log entry. +func (f *FileLogger) Warning(args ...interface{}) { + f.output(warningLog, args...) +} + +// Errorf records an error log entry. +func (f *FileLogger) Errorf(format string, args ...interface{}) { + f.outputf(errorLog, format, args...) +} + +// Error records an error log entry. +func (f *FileLogger) Error(args ...interface{}) { + f.output(errorLog, args...) +} + +// Fatalf records a fatal log entry and terminates the program. +func (f *FileLogger) Fatalf(format string, args ...interface{}) { + defer os.Exit(1) + f.outputf(fatalLog, format, args...) +} + +// Fatal records a fatal log entry and terminates the program. +func (f *FileLogger) Fatal(args ...interface{}) { + defer os.Exit(1) + f.output(fatalLog, args...) +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/status/build_status.go b/vendor/github.com/openshift/source-to-image/pkg/util/status/build_status.go new file mode 100644 index 0000000000..fc2dc46a93 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/status/build_status.go @@ -0,0 +1,131 @@ +package status + +import ( + "github.com/openshift/source-to-image/pkg/api" +) + +const ( + // ReasonAssembleFailed is the reason associated with the Assemble script + // failing. + ReasonAssembleFailed api.StepFailureReason = "AssembleFailed" + // ReasonMessageAssembleFailed is the message associated with the Assemble + // script failing. + ReasonMessageAssembleFailed api.StepFailureMessage = "Assemble script failed." + + // ReasonPullBuilderImageFailed is the reason associated with failing to pull + // the builder image. + ReasonPullBuilderImageFailed api.StepFailureReason = "PullBuilderImageFailed" + // ReasonMessagePullBuilderImageFailed is the message associated with failing + // to pull the builder image. + ReasonMessagePullBuilderImageFailed api.StepFailureMessage = "Failed to pull builder image." + + // ReasonPullRuntimeImageFailed is the reason associated with failing to pull + // the runtime image. + ReasonPullRuntimeImageFailed api.StepFailureReason = "PullRuntimeImageFailed" + // ReasonMessagePullRuntimeImageFailed is the message associated with failing + // to pull the runtime image. + ReasonMessagePullRuntimeImageFailed api.StepFailureMessage = "Failed to pull runtime image." + + // ReasonPullPreviousImageFailed is the reason associated with failing to + // pull the previous image. + ReasonPullPreviousImageFailed api.StepFailureReason = "PullPreviousImageFailed" + + // ReasonMessagePullPreviousImageFailed is the message associated with + // failing to pull the previous image. + ReasonMessagePullPreviousImageFailed api.StepFailureMessage = "Failed to pull the previous image for incremental build." + + // ReasonCommitContainerFailed is the reason associated with failing to + // commit the container to the final image. + ReasonCommitContainerFailed api.StepFailureReason = "ContainerCommitFailed" + // ReasonMessageCommitContainerFailed is the message associated with failing to + // commit the container to the final image. + ReasonMessageCommitContainerFailed api.StepFailureMessage = "Failed to commit container." + + // ReasonFetchSourceFailed is the reason associated with failing to download + // the source of the build. + ReasonFetchSourceFailed api.StepFailureReason = "FetchSourceFailed" + // ReasonMessageFetchSourceFailed is the message associated with failing to download + // the source of the build. + ReasonMessageFetchSourceFailed api.StepFailureMessage = "Failed to fetch source for build." + + // ReasonDockerImageBuildFailed is the reason associated with a failed + // Docker image build. + ReasonDockerImageBuildFailed api.StepFailureReason = "DockerImageBuildFailed" + // ReasonMessageDockerImageBuildFailed is the message associated with a failed + // Docker image build. + ReasonMessageDockerImageBuildFailed api.StepFailureMessage = "Docker image build failed." + + // ReasonDockerfileCreateFailed is the reason associated with failing to create a + // Dockerfile for a build. + ReasonDockerfileCreateFailed api.StepFailureReason = "DockerFileCreationFailed" + // ReasonMessageDockerfileCreateFailed is the message associated with failing to create a + // Dockerfile for a build. + ReasonMessageDockerfileCreateFailed api.StepFailureMessage = "Failed to create Dockerfile." + + // ReasonInvalidArtifactsMapping is the reason associated with an + // invalid artifacts mapping of files that need to be copied. + ReasonInvalidArtifactsMapping api.StepFailureReason = "InvalidArtifactsMapping" + // ReasonMessageInvalidArtifactsMapping is the message associated with an + // invalid artifacts mapping of files that need to be copied. + ReasonMessageInvalidArtifactsMapping api.StepFailureMessage = "Invalid artifacts mapping specified." + + // ReasonScriptsFetchFailed is the reason associated with a failure to + // download specified scripts in the application image. + ReasonScriptsFetchFailed api.StepFailureReason = "FetchScriptsFailed" + // ReasonMessageScriptsFetchFailed is the message associated with a failure to + // download specified scripts in the application image. + ReasonMessageScriptsFetchFailed api.StepFailureMessage = "Failed to fetch specified scripts." + + // ReasonRuntimeArtifactsFetchFailed is the reason associated with a failure + // to download the specified runtime scripts. + ReasonRuntimeArtifactsFetchFailed api.StepFailureReason = "FetchRuntimeArtifactsFailed" + // ReasonMessageRuntimeArtifactsFetchFailed is the message associated with a + // failure to download the specified runtime scripts in the application + // image. + ReasonMessageRuntimeArtifactsFetchFailed api.StepFailureMessage = "Failed to fetch specified runtime artifacts." + + // ReasonFSOperationFailed is the reason associated with a failed fs + // operation. Create, remove directory, copy file, etc. + ReasonFSOperationFailed api.StepFailureReason = "FileSystemOperationFailed" + // ReasonMessageFSOperationFailed is the message associated with a failed fs + // operation. Create, remove directory, copy file, etc. + ReasonMessageFSOperationFailed api.StepFailureMessage = "Failed to perform filesystem operation." + + // ReasonInstallScriptsFailed is the reason associated with a failure to + // install scripts in the builder image. + ReasonInstallScriptsFailed api.StepFailureReason = "InstallScriptsFailed" + // ReasonMessageInstallScriptsFailed is the message associated with a failure to + // install scripts in the builder image. + ReasonMessageInstallScriptsFailed api.StepFailureMessage = "Failed to install specified scripts." + + // ReasonGenericS2IBuildFailed is the reason associated with a broad range of + // failures. + ReasonGenericS2IBuildFailed api.StepFailureReason = "GenericS2IBuildFailed" + // ReasonMessageGenericS2iBuildFailed is the message associated with a broad + // range of failures. + ReasonMessageGenericS2iBuildFailed api.StepFailureMessage = "Generic S2I Build failure - check S2I logs for details." + + // ReasonOnBuildForbidden is the failure reason associated with an image that + // uses the ONBUILD instruction when it's not allowed. + ReasonOnBuildForbidden api.StepFailureReason = "OnBuildForbidden" + // ReasonMessageOnBuildForbidden is the message associated with an image that + // uses the ONBUILD instruction when it's not allowed. + ReasonMessageOnBuildForbidden api.StepFailureMessage = "ONBUILD instructions not allowed in this context." + + // ReasonAssembleUserForbidden is the failure reason associated with an image that + // uses a forbidden AssembleUser. + ReasonAssembleUserForbidden api.StepFailureReason = "AssembleUserForbidden" + + // ReasonMessageAssembleUserForbidden is the failure reason associated with an image that + // uses a forbidden AssembleUser. + ReasonMessageAssembleUserForbidden api.StepFailureMessage = "Assemble user for S2I build is forbidden." +) + +// NewFailureReason initializes a new failure reason that contains both the +// reason and a message to be displayed. +func NewFailureReason(reason api.StepFailureReason, message api.StepFailureMessage) api.FailureReason { + return api.FailureReason{ + Reason: reason, + Message: message, + } +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/status/doc.go b/vendor/github.com/openshift/source-to-image/pkg/util/status/doc.go new file mode 100644 index 0000000000..9031808333 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/status/doc.go @@ -0,0 +1,3 @@ +// Package status provides functionality to update the status of a build with +// information about failures that occurred. +package status diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/strings.go b/vendor/github.com/openshift/source-to-image/pkg/util/strings.go new file mode 100644 index 0000000000..d3634b63df --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/strings.go @@ -0,0 +1,21 @@ +package util + +// Includes determines if the given string is in the provided slice of strings. +func Includes(arr []string, str string) bool { + for _, s := range arr { + if s == str { + return true + } + } + return false +} + +// FirstNonEmpty returns the first non-empty string in the provided list of strings. +func FirstNonEmpty(args ...string) string { + for _, value := range args { + if len(value) > 0 { + return value + } + } + return "" +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/timeout.go b/vendor/github.com/openshift/source-to-image/pkg/util/timeout.go new file mode 100644 index 0000000000..68a3ae0779 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/timeout.go @@ -0,0 +1,52 @@ +package util + +import ( + "fmt" + "time" +) + +// TimeoutError is error returned after timeout occurred. +type TimeoutError struct { + after time.Duration + message string +} + +// Error implements the Go error interface. +func (t *TimeoutError) Error() string { + if len(t.message) > 0 { + return fmt.Sprintf("%s timed out after %v", t.message, t.after) + } + return fmt.Sprintf("function timed out after %v", t.after) +} + +// TimeoutAfter executes the provided function and returns TimeoutError in the +// case that the execution time of the function exceeded the provided duration. +// The provided function is passed the timer in case it wishes to reset it. If +// so, the following pattern must be used: +// if !timer.Stop() { +// return &TimeoutError{} +// } +// timer.Reset(timeout) +func TimeoutAfter(t time.Duration, errorMsg string, f func(*time.Timer) error) error { + c := make(chan error, 1) + timer := time.NewTimer(t) + go func() { + err := f(timer) + if !IsTimeoutError(err) { + c <- err + } + }() + select { + case err := <-c: + timer.Stop() + return err + case <-timer.C: + return &TimeoutError{after: t, message: errorMsg} + } +} + +// IsTimeoutError checks if the provided error is a TimeoutError. +func IsTimeoutError(e error) bool { + _, ok := e.(*TimeoutError) + return ok +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/user/range.go b/vendor/github.com/openshift/source-to-image/pkg/util/user/range.go new file mode 100644 index 0000000000..ce8245a4ac --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/user/range.go @@ -0,0 +1,170 @@ +package user + +import ( + "errors" + "fmt" + "strconv" + "strings" +) + +// Range errors +var ( + ErrInvalidRange = errors.New("invalid range; a range must consist of positive integers and the upper bound must be greater than or equal to the lower bound") +) + +// ErrParseRange is an error encountered while parsing a Range +type ErrParseRange struct { + cause error +} + +func (e *ErrParseRange) Error() string { + msg := "error parsing range; a range must be of one of the following formats: [n], [-n], [n-], [n:m] where n and m are positive numbers and m is greater than or equal to n" + if e.cause != nil { + msg = fmt.Sprintf("%s: %v", msg, e.cause) + } + return msg +} + +// Range represents a range of user ids. It can be unbound at either end with a nil value +// I both From and To are present, To must be greater than or equal to From. Bounds are inclusive +type Range struct { + from *int + to *int +} + +// NewRange creates a new range with lower and upper bound +func NewRange(from int, to int) (*Range, error) { + return (&rangeBuilder{}).from(from, nil).to(to, nil).Range() +} + +// NewRangeTo creates a new range with only the upper bound +func NewRangeTo(to int) (*Range, error) { + return (&rangeBuilder{}).to(to, nil).Range() +} + +// NewRangeFrom creates a new range with only the lower bound +func NewRangeFrom(from int) (*Range, error) { + return (&rangeBuilder{}).from(from, nil).Range() +} + +func parseInt(str string) (int, error) { + num, err := strconv.Atoi(str) + if err != nil { + return 0, &ErrParseRange{cause: err} + } + return num, nil +} + +type rangeBuilder struct { + r Range + err error +} + +func (b *rangeBuilder) from(num int, err error) *rangeBuilder { + return b.setBound(num, err, &b.r.from) +} + +func (b *rangeBuilder) to(num int, err error) *rangeBuilder { + return b.setBound(num, err, &b.r.to) +} + +func (b *rangeBuilder) setBound(num int, err error, bound **int) *rangeBuilder { + if b.err != nil { + return b + } + if b.err = err; b.err != nil { + return b + } + if num < 0 { + b.err = ErrInvalidRange + return b + } + *bound = &num + return b +} + +// Range returns the completed Range from the rangeBuilder. +func (b *rangeBuilder) Range() (*Range, error) { + if b.err != nil { + return nil, b.err + } + if b.r.from != nil && b.r.to != nil && *b.r.to < *b.r.from { + return nil, ErrInvalidRange + } + return &b.r, nil +} + +// ParseRange creates a Range from a given string +func ParseRange(value string) (*Range, error) { + value = strings.TrimSpace(value) + b := &rangeBuilder{} + if value == "" { + return b.Range() + } + parts := strings.Split(value, "-") + switch len(parts) { + case 1: + num, err := parseInt(parts[0]) + return b.from(num, err).to(num, err).Range() + case 2: + if parts[0] != "" { + b.from(parseInt(parts[0])) + } + if parts[1] != "" { + b.to(parseInt(parts[1])) + } + return b.Range() + default: + return nil, &ErrParseRange{} + } +} + +// Contains returns true if the argument falls inside the Range +func (r *Range) Contains(value int) bool { + if r.from == nil && r.to == nil { + return false + } + if r.from != nil && value < *r.from { + return false + } + if r.to != nil && value > *r.to { + return false + } + return true +} + +// String returns a parse-able string representation of a Range +func (r *Range) String() string { + switch { + case r.from == nil && r.to == nil: + return "" + case r.from == nil: + return fmt.Sprintf("-%d", *r.to) + case r.to == nil: + return fmt.Sprintf("%d-", *r.from) + case *r.from == *r.to: + return fmt.Sprintf("%d", *r.to) + default: + return fmt.Sprintf("%d-%d", *r.from, *r.to) + } +} + +// Type returns the type of a Range object +func (r *Range) Type() string { + return "user.Range" +} + +// Set sets the value of a Range object +func (r *Range) Set(value string) error { + newRange, err := ParseRange(value) + if err != nil { + return err + } + *r = *newRange + return nil +} + +// Empty returns true if the range has no bounds +func (r *Range) Empty() bool { + return r.from == nil && r.to == nil +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/user/rangelist.go b/vendor/github.com/openshift/source-to-image/pkg/util/user/rangelist.go new file mode 100644 index 0000000000..7ec6dfb577 --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/user/rangelist.go @@ -0,0 +1,87 @@ +package user + +import ( + "strconv" + "strings" +) + +// RangeList is a list of user ranges +type RangeList []*Range + +// ParseRangeList parses a string that contains a comma-separated list of ranges +func ParseRangeList(str string) (*RangeList, error) { + rl := RangeList{} + if len(str) == 0 { + return &rl, nil + } + parts := strings.Split(str, ",") + for _, p := range parts { + r, err := ParseRange(p) + if err != nil { + return nil, err + } + rl = append(rl, r) + } + return &rl, nil +} + +// Empty returns true if the RangeList is empty +func (l *RangeList) Empty() bool { + if len(*l) == 0 { + return true + } + for _, r := range *l { + if !r.Empty() { + return false + } + } + return true +} + +// Contains returns true if the uid is contained by any range in the RangeList +func (l *RangeList) Contains(uid int) bool { + for _, r := range *l { + if r.Contains(uid) { + return true + } + } + return false +} + +// Type returns the type of a RangeList object +func (l *RangeList) Type() string { + return "user.RangeList" +} + +// Set sets the value of a RangeList object +func (l *RangeList) Set(value string) error { + newRangeList, err := ParseRangeList(value) + if err != nil { + return err + } + *l = *newRangeList + return nil +} + +// String returns a parseable string representation of a RangeList +func (l *RangeList) String() string { + rangeStrings := []string{} + for _, r := range *l { + rangeStrings = append(rangeStrings, r.String()) + } + return strings.Join(rangeStrings, ",") +} + +// IsUserAllowed checks that the given user is numeric and is +// contained by the given RangeList. Returns true if +// allowed is nil or empty. +func IsUserAllowed(user string, allowed *RangeList) bool { + if allowed == nil || allowed.Empty() { + return true + } + uid, err := strconv.Atoi(user) + if err != nil { + return false + } + return allowed.Contains(uid) +} diff --git a/vendor/github.com/openshift/source-to-image/pkg/util/util.go b/vendor/github.com/openshift/source-to-image/pkg/util/util.go new file mode 100644 index 0000000000..2b133f6bdc --- /dev/null +++ b/vendor/github.com/openshift/source-to-image/pkg/util/util.go @@ -0,0 +1,19 @@ +package util + +import ( + "github.com/docker/docker/api/types/container" + + utillog "github.com/openshift/source-to-image/pkg/util/log" +) + +var log = utillog.StderrLog + +// SafeForLoggingContainerConfig returns a copy of the container.Config object +// with sensitive information (proxy environment variables containing credentials) +// redacted. +func SafeForLoggingContainerConfig(config *container.Config) *container.Config { + strippedEnv := SafeForLoggingEnv(config.Env) + newConfig := *config + newConfig.Env = strippedEnv + return &newConfig +} diff --git a/vendor/github.com/ory/viper/.editorconfig b/vendor/github.com/ory/viper/.editorconfig new file mode 100644 index 0000000000..5efbc97d15 --- /dev/null +++ b/vendor/github.com/ory/viper/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.go] +indent_style = tab + +[{Makefile, *.mk}] +indent_style = tab diff --git a/vendor/github.com/ory/viper/.gitignore b/vendor/github.com/ory/viper/.gitignore new file mode 100644 index 0000000000..0007f978a8 --- /dev/null +++ b/vendor/github.com/ory/viper/.gitignore @@ -0,0 +1,4 @@ +/bin/ +/build/ +/var/ +/vendor/ diff --git a/vendor/github.com/ory/viper/.golangci.yml b/vendor/github.com/ory/viper/.golangci.yml new file mode 100644 index 0000000000..a0755ce7e1 --- /dev/null +++ b/vendor/github.com/ory/viper/.golangci.yml @@ -0,0 +1,27 @@ +linters-settings: + golint: + min-confidence: 0.1 + goimports: + local-prefixes: github.com/spf13/viper + +linters: + enable-all: true + disable: + - funlen + - maligned + + # TODO: fix me + - wsl + - gochecknoinits + - gosimple + - gochecknoglobals + - errcheck + - lll + - godox + - scopelint + - gocyclo + - gocognit + - gocritic + +service: + golangci-lint-version: 1.21.x diff --git a/vendor/github.com/ory/viper/.travis.yml b/vendor/github.com/ory/viper/.travis.yml new file mode 100644 index 0000000000..6043447cf4 --- /dev/null +++ b/vendor/github.com/ory/viper/.travis.yml @@ -0,0 +1,31 @@ +go_import_path: github.com/ory/viper + +language: go + +env: + global: + - GO111MODULE="on" + - GOFLAGS="-mod=readonly" + +go: + - 1.11.x + - 1.12.x + - tip + +os: + - linux + - osx + +matrix: + allow_failures: + - go: tip + fast_finish: true + +script: + - go install ./... + - diff -u <(echo -n) <(gofmt -d .) + - go test -v ./... + +after_success: + - go get -u -d github.com/spf13/hugo + - cd $GOPATH/src/github.com/spf13/hugo && make && ./hugo -s docs && cd - diff --git a/vendor/github.com/ory/viper/LICENSE b/vendor/github.com/ory/viper/LICENSE new file mode 100644 index 0000000000..4527efb9c0 --- /dev/null +++ b/vendor/github.com/ory/viper/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Steve Francia + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/ory/viper/Makefile b/vendor/github.com/ory/viper/Makefile new file mode 100644 index 0000000000..1c2cab03f4 --- /dev/null +++ b/vendor/github.com/ory/viper/Makefile @@ -0,0 +1,76 @@ +# A Self-Documenting Makefile: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html + +OS = $(shell uname | tr A-Z a-z) +export PATH := $(abspath bin/):${PATH} + +# Build variables +BUILD_DIR ?= build +export CGO_ENABLED ?= 0 +export GOOS = $(shell go env GOOS) +ifeq (${VERBOSE}, 1) +ifeq ($(filter -v,${GOARGS}),) + GOARGS += -v +endif +TEST_FORMAT = short-verbose +endif + +# Dependency versions +GOTESTSUM_VERSION = 0.4.0 +GOLANGCI_VERSION = 1.21.0 + +# Add the ability to override some variables +# Use with care +-include override.mk + +.PHONY: clear +clear: ## Clear the working area and the project + rm -rf bin/ + +.PHONY: check +check: test lint ## Run tests and linters + +bin/gotestsum: bin/gotestsum-${GOTESTSUM_VERSION} + @ln -sf gotestsum-${GOTESTSUM_VERSION} bin/gotestsum +bin/gotestsum-${GOTESTSUM_VERSION}: + @mkdir -p bin + curl -L https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_${OS}_amd64.tar.gz | tar -zOxf - gotestsum > ./bin/gotestsum-${GOTESTSUM_VERSION} && chmod +x ./bin/gotestsum-${GOTESTSUM_VERSION} + +TEST_PKGS ?= ./... +.PHONY: test +test: TEST_FORMAT ?= short +test: SHELL = /bin/bash +test: export CGO_ENABLED=1 +test: bin/gotestsum ## Run tests + @mkdir -p ${BUILD_DIR} + bin/gotestsum --no-summary=skipped --junitfile ${BUILD_DIR}/coverage.xml --format ${TEST_FORMAT} -- -race -coverprofile=${BUILD_DIR}/coverage.txt -covermode=atomic $(filter-out -v,${GOARGS}) $(if ${TEST_PKGS},${TEST_PKGS},./...) + +bin/golangci-lint: bin/golangci-lint-${GOLANGCI_VERSION} + @ln -sf golangci-lint-${GOLANGCI_VERSION} bin/golangci-lint +bin/golangci-lint-${GOLANGCI_VERSION}: + @mkdir -p bin + curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b ./bin/ v${GOLANGCI_VERSION} + @mv bin/golangci-lint $@ + +.PHONY: lint +lint: bin/golangci-lint ## Run linter + bin/golangci-lint run + +.PHONY: fix +fix: bin/golangci-lint ## Fix lint violations + bin/golangci-lint run --fix + +# Add custom targets here +-include custom.mk + +.PHONY: list +list: ## List all make targets + @${MAKE} -pRrn : -f $(MAKEFILE_LIST) 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | sort + +.PHONY: help +.DEFAULT_GOAL := help +help: + @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +# Variable outputting/exporting rules +var-%: ; @echo $($*) +varexport-%: ; @echo $*=$($*) diff --git a/vendor/github.com/ory/viper/README.md b/vendor/github.com/ory/viper/README.md new file mode 100644 index 0000000000..8ba4168131 --- /dev/null +++ b/vendor/github.com/ory/viper/README.md @@ -0,0 +1,808 @@ +![viper logo](https://cloud.githubusercontent.com/assets/173412/10886745/998df88a-8151-11e5-9448-4736db51020d.png) + +[![CircleCI](https://circleci.com/gh/ory/viper/tree/master.svg?style=shield)](https://circleci.com/gh/ory/viper/tree/master) + +Go configuration with fangs! + +> This is a fork. It resolves several issues that are left unresolved in [the upstream](https://github.com/ory/viper). +> Issues resolved and features added include: +> +> - Fixed race conditions when reloading configs. +> - Added `HasChanged(key string) bool` which returns true (once!) when a value has changed. +> - Make sure that `viper.AllSettings()` always returns `map[string]interface{}` which was not the case and incompatible + with de/encoders like `json`. + +Many Go projects are built using Viper including: + +* [Hugo](http://gohugo.io) +* [EMC RexRay](http://rexray.readthedocs.org/en/stable/) +* [Imgur’s Incus](https://github.com/Imgur/incus) +* [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack) +* [Docker Notary](https://github.com/docker/Notary) +* [BloomApi](https://www.bloomapi.com/) +* [doctl](https://github.com/digitalocean/doctl) +* [Clairctl](https://github.com/jgsqware/clairctl) +* [Mercure](https://mercure.rocks) + +## Install + +```console +go get -u github.com/ory/viper +``` + + +## What is Viper? + +Viper is a complete configuration solution for Go applications including 12-Factor apps. It is designed +to work within an application, and can handle all types of configuration needs +and formats. It supports: + +* setting defaults +* reading from JSON, TOML, YAML, HCL, envfile and Java properties config files +* live watching and re-reading of config files (optional) +* reading from environment variables +* reading from remote config systems (etcd or Consul), and watching changes +* reading from command line flags +* reading from buffer +* setting explicit values + +Viper can be thought of as a registry for all of your applications configuration needs. + + +## Why Viper? + +When building a modern application, you don’t want to worry about +configuration file formats; you want to focus on building awesome software. +Viper is here to help with that. + +Viper does the following for you: + +1. Find, load, and unmarshal a configuration file in JSON, TOML, YAML, HCL, INI, envfile or Java properties formats. +2. Provide a mechanism to set default values for your different configuration options. +3. Provide a mechanism to set override values for options specified through command line flags. +4. Provide an alias system to easily rename parameters without breaking existing code. +5. Make it easy to tell the difference between when a user has provided a command line or config file which is the same as the default. + +Viper uses the following precedence order. Each item takes precedence over the item below it: + + * explicit call to `Set` + * flag + * env + * config + * key/value store + * default + +**Important:** Viper configuration keys are case insensitive. +There are ongoing discussions about making that optional. + + +## Putting Values into Viper + +### Establishing Defaults + +A good configuration system will support default values. A default value is not +required for a key, but it’s useful in the event that a key hasn't been set via +config file, environment variable, remote configuration or flag. + +Examples: + +```go +viper.SetDefault("ContentDir", "content") +viper.SetDefault("LayoutDir", "layouts") +viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"}) +``` + +### Reading Config Files + +Viper requires minimal configuration so it knows where to look for config files. +Viper supports JSON, TOML, YAML, HCL, INI, envfile and Java Properties files. Viper can search multiple paths, but +currently a single Viper instance only supports a single configuration file. +Viper does not default to any configuration search paths leaving defaults decision +to an application. + +Here is an example of how to use Viper to search for and read a configuration file. +None of the specific paths are required, but at least one path should be provided +where a configuration file is expected. + +```go +viper.SetConfigName("config") // name of config file (without extension) +viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name +viper.AddConfigPath("/etc/appname/") // path to look for the config file in +viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search paths +viper.AddConfigPath(".") // optionally look for config in the working directory +err := viper.ReadInConfig() // Find and read the config file +if err != nil { // Handle errors reading the config file + panic(fmt.Errorf("Fatal error config file: %s \n", err)) +} +``` + +You can handle the specific case where no config file is found like this: + +```go +if err := viper.ReadInConfig(); err != nil { + if _, ok := err.(viper.ConfigFileNotFoundError); ok { + // Config file not found; ignore error if desired + } else { + // Config file was found but another error was produced + } +} + +// Config file found and successfully parsed +``` + +*NOTE [since 1.6]:* You can also have a file without an extension and specify the format programmaticaly. For those configuration files that lie in the home of the user without any extension like `.bashrc` + +### Writing Config Files + +Reading from config files is useful, but at times you want to store all modifications made at run time. +For that, a bunch of commands are available, each with its own purpose: + +* WriteConfig - writes the current viper configuration to the predefined path, if exists. Errors if no predefined path. Will overwrite the current config file, if it exists. +* SafeWriteConfig - writes the current viper configuration to the predefined path. Errors if no predefined path. Will not overwrite the current config file, if it exists. +* WriteConfigAs - writes the current viper configuration to the given filepath. Will overwrite the given file, if it exists. +* SafeWriteConfigAs - writes the current viper configuration to the given filepath. Will not overwrite the given file, if it exists. + +As a rule of the thumb, everything marked with safe won't overwrite any file, but just create if not existent, whilst the default behavior is to create or truncate. + +A small examples section: + +```go +viper.WriteConfig() // writes current config to predefined path set by 'viper.AddConfigPath()' and 'viper.SetConfigName' +viper.SafeWriteConfig() +viper.WriteConfigAs("/path/to/my/.config") +viper.SafeWriteConfigAs("/path/to/my/.config") // will error since it has already been written +viper.SafeWriteConfigAs("/path/to/my/.other_config") +``` + +### Watching and re-reading config files + +Viper supports the ability to have your application live read a config file while running. + +Gone are the days of needing to restart a server to have a config take effect, +viper powered applications can read an update to a config file while running and +not miss a beat. + +Simply tell the viper instance to watchConfig. +Optionally you can provide a function for Viper to run each time a change occurs. + +**Make sure you add all of the configPaths prior to calling `WatchConfig()`** + +```go +viper.WatchConfig() +viper.OnConfigChange(func(e fsnotify.Event) { + fmt.Println("Config file changed:", e.Name) +}) +``` + +### Reading Config from io.Reader + +Viper predefines many configuration sources such as files, environment +variables, flags, and remote K/V store, but you are not bound to them. You can +also implement your own required configuration source and feed it to viper. + +```go +viper.SetConfigType("yaml") // or viper.SetConfigType("YAML") + +// any approach to require this configuration into your program. +var yamlExample = []byte(` +Hacker: true +name: steve +hobbies: +- skateboarding +- snowboarding +- go +clothing: + jacket: leather + trousers: denim +age: 35 +eyes : brown +beard: true +`) + +viper.ReadConfig(bytes.NewBuffer(yamlExample)) + +viper.Get("name") // this would be "steve" +``` + +### Setting Overrides + +These could be from a command line flag, or from your own application logic. + +```go +viper.Set("Verbose", true) +viper.Set("LogFile", LogFile) +``` + +### Registering and Using Aliases + +Aliases permit a single value to be referenced by multiple keys + +```go +viper.RegisterAlias("loud", "Verbose") + +viper.Set("verbose", true) // same result as next line +viper.Set("loud", true) // same result as prior line + +viper.GetBool("loud") // true +viper.GetBool("verbose") // true +``` + +### Working with Environment Variables + +Viper has full support for environment variables. This enables 12 factor +applications out of the box. There are five methods that exist to aid working +with ENV: + + * `AutomaticEnv()` + * `BindEnv(string...) : error` + * `SetEnvPrefix(string)` + * `SetEnvKeyReplacer(string...) *strings.Replacer` + * `AllowEmptyEnv(bool)` + +_When working with ENV variables, it’s important to recognize that Viper +treats ENV variables as case sensitive._ + +Viper provides a mechanism to try to ensure that ENV variables are unique. By +using `SetEnvPrefix`, you can tell Viper to use a prefix while reading from +the environment variables. Both `BindEnv` and `AutomaticEnv` will use this +prefix. + +`BindEnv` takes one or two parameters. The first parameter is the key name, the +second is the name of the environment variable. The name of the environment +variable is case sensitive. If the ENV variable name is not provided, then +Viper will automatically assume that the ENV variable matches the following format: prefix + "_" + the key name in ALL CAPS. When you explicitly provide the ENV variable name (the second parameter), +it **does not** automatically add the prefix. For example if the second parameter is "id", +Viper will look for the ENV variable "ID". + +One important thing to recognize when working with ENV variables is that the +value will be read each time it is accessed. Viper does not fix the value when +the `BindEnv` is called. + +`AutomaticEnv` is a powerful helper especially when combined with +`SetEnvPrefix`. When called, Viper will check for an environment variable any +time a `viper.Get` request is made. It will apply the following rules. It will +check for a environment variable with a name matching the key uppercased and +prefixed with the `EnvPrefix` if set. + +`SetEnvKeyReplacer` allows you to use a `strings.Replacer` object to rewrite Env +keys to an extent. This is useful if you want to use `-` or something in your +`Get()` calls, but want your environmental variables to use `_` delimiters. An +example of using it can be found in `viper_test.go`. + +Alternatively, you can use `EnvKeyReplacer` with `NewWithOptions` factory function. +Unlike `SetEnvKeyReplacer`, it accepts a `StringReplacer` interface allowing you to write custom string replacing logic. + +By default empty environment variables are considered unset and will fall back to +the next configuration source. To treat empty environment variables as set, use +the `AllowEmptyEnv` method. + +#### Env example + +```go +SetEnvPrefix("spf") // will be uppercased automatically +BindEnv("id") + +os.Setenv("SPF_ID", "13") // typically done outside of the app + +id := Get("id") // 13 +``` + +### Working with Flags + +Viper has the ability to bind to flags. Specifically, Viper supports `Pflags` +as used in the [Cobra](https://github.com/spf13/cobra) library. + +Like `BindEnv`, the value is not set when the binding method is called, but when +it is accessed. This means you can bind as early as you want, even in an +`init()` function. + +For individual flags, the `BindPFlag()` method provides this functionality. + +Example: + +```go +serverCmd.Flags().Int("port", 1138, "Port to run Application server on") +viper.BindPFlag("port", serverCmd.Flags().Lookup("port")) +``` + +You can also bind an existing set of pflags (pflag.FlagSet): + +Example: + +```go +pflag.Int("flagname", 1234, "help message for flagname") + +pflag.Parse() +viper.BindPFlags(pflag.CommandLine) + +i := viper.GetInt("flagname") // retrieve values from viper instead of pflag +``` + +The use of [pflag](https://github.com/spf13/pflag/) in Viper does not preclude +the use of other packages that use the [flag](https://golang.org/pkg/flag/) +package from the standard library. The pflag package can handle the flags +defined for the flag package by importing these flags. This is accomplished +by a calling a convenience function provided by the pflag package called +AddGoFlagSet(). + +Example: + +```go +package main + +import ( + "flag" + "github.com/spf13/pflag" +) + +func main() { + + // using standard library "flag" package + flag.Int("flagname", 1234, "help message for flagname") + + pflag.CommandLine.AddGoFlagSet(flag.CommandLine) + pflag.Parse() + viper.BindPFlags(pflag.CommandLine) + + i := viper.GetInt("flagname") // retrieve value from viper + + ... +} +``` + +#### Flag interfaces + +Viper provides two Go interfaces to bind other flag systems if you don’t use `Pflags`. + +`FlagValue` represents a single flag. This is a very simple example on how to implement this interface: + +```go +type myFlag struct {} +func (f myFlag) HasChanged() bool { return false } +func (f myFlag) Name() string { return "my-flag-name" } +func (f myFlag) ValueString() string { return "my-flag-value" } +func (f myFlag) ValueType() string { return "string" } +``` + +Once your flag implements this interface, you can simply tell Viper to bind it: + +```go +viper.BindFlagValue("my-flag-name", myFlag{}) +``` + +`FlagValueSet` represents a group of flags. This is a very simple example on how to implement this interface: + +```go +type myFlagSet struct { + flags []myFlag +} + +func (f myFlagSet) VisitAll(fn func(FlagValue)) { + for _, flag := range flags { + fn(flag) + } +} +``` + +Once your flag set implements this interface, you can simply tell Viper to bind it: + +```go +fSet := myFlagSet{ + flags: []myFlag{myFlag{}, myFlag{}}, +} +viper.BindFlagValues("my-flags", fSet) +``` + +### Remote Key/Value Store Support + +To enable remote support in Viper, do a blank import of the `viper/remote` +package: + +`import _ "github.com/ory/viper/remote"` + +Viper will read a config string (as JSON, TOML, YAML, HCL or envfile) retrieved from a path +in a Key/Value store such as etcd or Consul. These values take precedence over +default values, but are overridden by configuration values retrieved from disk, +flags, or environment variables. + +Viper uses [crypt](https://github.com/bketelsen/crypt) to retrieve +configuration from the K/V store, which means that you can store your +configuration values encrypted and have them automatically decrypted if you have +the correct gpg keyring. Encryption is optional. + +You can use remote configuration in conjunction with local configuration, or +independently of it. + +`crypt` has a command-line helper that you can use to put configurations in your +K/V store. `crypt` defaults to etcd on http://127.0.0.1:4001. + +```bash +$ go get github.com/bketelsen/crypt/bin/crypt +$ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json +``` + +Confirm that your value was set: + +```bash +$ crypt get -plaintext /config/hugo.json +``` + +See the `crypt` documentation for examples of how to set encrypted values, or +how to use Consul. + +### Remote Key/Value Store Example - Unencrypted + +#### etcd +```go +viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json") +viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" +err := viper.ReadRemoteConfig() +``` + +#### Consul +You need to set a key to Consul key/value storage with JSON value containing your desired config. +For example, create a Consul key/value store key `MY_CONSUL_KEY` with value: + +```json +{ + "port": 8080, + "hostname": "myhostname.com" +} +``` + +```go +viper.AddRemoteProvider("consul", "localhost:8500", "MY_CONSUL_KEY") +viper.SetConfigType("json") // Need to explicitly set this to json +err := viper.ReadRemoteConfig() + +fmt.Println(viper.Get("port")) // 8080 +fmt.Println(viper.Get("hostname")) // myhostname.com +``` + +#### Firestore + +```go +viper.AddRemoteProvider("firestore", "google-cloud-project-id", "collection/document") +viper.SetConfigType("json") // Config's format: "json", "toml", "yaml", "yml" +err := viper.ReadRemoteConfig() +``` + +Of course, you're allowed to use `SecureRemoteProvider` also + +### Remote Key/Value Store Example - Encrypted + +```go +viper.AddSecureRemoteProvider("etcd","http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg") +viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" +err := viper.ReadRemoteConfig() +``` + +### Watching Changes in etcd - Unencrypted + +```go +// alternatively, you can create a new viper instance. +var runtime_viper = viper.New() + +runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml") +runtime_viper.SetConfigType("yaml") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" + +// read from remote config the first time. +err := runtime_viper.ReadRemoteConfig() + +// unmarshal config +runtime_viper.Unmarshal(&runtime_conf) + +// open a goroutine to watch remote changes forever +go func(){ + for { + time.Sleep(time.Second * 5) // delay after each request + + // currently, only tested with etcd support + err := runtime_viper.WatchRemoteConfig() + if err != nil { + log.Errorf("unable to read remote config: %v", err) + continue + } + + // unmarshal new config into our runtime config struct. you can also use channel + // to implement a signal to notify the system of the changes + runtime_viper.Unmarshal(&runtime_conf) + } +}() +``` + +## Getting Values From Viper + +In Viper, there are a few ways to get a value depending on the value’s type. +The following functions and methods exist: + + * `Get(key string) : interface{}` + * `GetBool(key string) : bool` + * `GetFloat64(key string) : float64` + * `GetInt(key string) : int` + * `GetIntSlice(key string) : []int` + * `GetString(key string) : string` + * `GetStringMap(key string) : map[string]interface{}` + * `GetStringMapString(key string) : map[string]string` + * `GetStringSlice(key string) : []string` + * `GetTime(key string) : time.Time` + * `GetDuration(key string) : time.Duration` + * `IsSet(key string) : bool` + * `AllSettings() : map[string]interface{}` + +One important thing to recognize is that each Get function will return a zero +value if it’s not found. To check if a given key exists, the `IsSet()` method +has been provided. + +Example: +```go +viper.GetString("logfile") // case-insensitive Setting & Getting +if viper.GetBool("verbose") { + fmt.Println("verbose enabled") +} +``` +### Accessing nested keys + +The accessor methods also accept formatted paths to deeply nested keys. For +example, if the following JSON file is loaded: + +```json +{ + "host": { + "address": "localhost", + "port": 5799 + }, + "datastore": { + "metric": { + "host": "127.0.0.1", + "port": 3099 + }, + "warehouse": { + "host": "198.0.0.1", + "port": 2112 + } + } +} + +``` + +Viper can access a nested field by passing a `.` delimited path of keys: + +```go +GetString("datastore.metric.host") // (returns "127.0.0.1") +``` + +This obeys the precedence rules established above; the search for the path +will cascade through the remaining configuration registries until found. + +For example, given this configuration file, both `datastore.metric.host` and +`datastore.metric.port` are already defined (and may be overridden). If in addition +`datastore.metric.protocol` was defined in the defaults, Viper would also find it. + +However, if `datastore.metric` was overridden (by a flag, an environment variable, +the `Set()` method, …) with an immediate value, then all sub-keys of +`datastore.metric` become undefined, they are “shadowed” by the higher-priority +configuration level. + +Lastly, if there exists a key that matches the delimited key path, its value +will be returned instead. E.g. + +```json +{ + "datastore.metric.host": "0.0.0.0", + "host": { + "address": "localhost", + "port": 5799 + }, + "datastore": { + "metric": { + "host": "127.0.0.1", + "port": 3099 + }, + "warehouse": { + "host": "198.0.0.1", + "port": 2112 + } + } +} + +GetString("datastore.metric.host") // returns "0.0.0.0" +``` + +### Extract sub-tree + +Extract sub-tree from Viper. + +For example, `viper` represents: + +```json +app: + cache1: + max-items: 100 + item-size: 64 + cache2: + max-items: 200 + item-size: 80 +``` + +After executing: + +```go +subv := viper.Sub("app.cache1") +``` + +`subv` represents: + +```json +max-items: 100 +item-size: 64 +``` + +Suppose we have: + +```go +func NewCache(cfg *Viper) *Cache {...} +``` + +which creates a cache based on config information formatted as `subv`. +Now it’s easy to create these 2 caches separately as: + +```go +cfg1 := viper.Sub("app.cache1") +cache1 := NewCache(cfg1) + +cfg2 := viper.Sub("app.cache2") +cache2 := NewCache(cfg2) +``` + +### Unmarshaling + +You also have the option of Unmarshaling all or a specific value to a struct, map, +etc. + +There are two methods to do this: + + * `Unmarshal(rawVal interface{}) : error` + * `UnmarshalKey(key string, rawVal interface{}) : error` + +Example: + +```go +type config struct { + Port int + Name string + PathMap string `mapstructure:"path_map"` +} + +var C config + +err := viper.Unmarshal(&C) +if err != nil { + t.Fatalf("unable to decode into struct, %v", err) +} +``` + +If you want to unmarshal configuration where the keys themselves contain dot (the default key delimiter), +you have to change the delimiter: + +```go +v := viper.NewWithOptions(viper.KeyDelimiter("::")) + +v.SetDefault("chart::values", map[string]interface{}{ + "ingress": map[string]interface{}{ + "annotations": map[string]interface{}{ + "traefik.frontend.rule.type": "PathPrefix", + "traefik.ingress.kubernetes.io/ssl-redirect": "true", + }, + }, +}) + +type config struct { + Chart struct{ + Values map[string]interface{} + } +} + +var C config + +v.Unmarshal(&C) +``` + +Viper also supports unmarshaling into embedded structs: + +```go +/* +Example config: + +module: + enabled: true + token: 89h3f98hbwf987h3f98wenf89ehf +*/ +type config struct { + Module struct { + Enabled bool + + moduleConfig `mapstructure:",squash"` + } +} + +// moduleConfig could be in a module specific package +type moduleConfig struct { + Token string +} + +var C config + +err := viper.Unmarshal(&C) +if err != nil { + t.Fatalf("unable to decode into struct, %v", err) +} +``` + +Viper uses [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) under the hood for unmarshaling values which uses `mapstructure` tags by default. + +### Marshalling to string + +You may need to marshal all the settings held in viper into a string rather than write them to a file. +You can use your favorite format's marshaller with the config returned by `AllSettings()`. + +```go +import ( + yaml "gopkg.in/yaml.v2" + // ... +) + +func yamlStringSettings() string { + c := viper.AllSettings() + bs, err := yaml.Marshal(c) + if err != nil { + log.Fatalf("unable to marshal config to YAML: %v", err) + } + return string(bs) +} +``` + +## Viper or Vipers? + +Viper comes ready to use out of the box. There is no configuration or +initialization needed to begin using Viper. Since most applications will want +to use a single central repository for their configuration, the viper package +provides this. It is similar to a singleton. + +In all of the examples above, they demonstrate using viper in its singleton +style approach. + +### Working with multiple vipers + +You can also create many different vipers for use in your application. Each will +have its own unique set of configurations and values. Each can read from a +different config file, key value store, etc. All of the functions that viper +package supports are mirrored as methods on a viper. + +Example: + +```go +x := viper.New() +y := viper.New() + +x.SetDefault("ContentDir", "content") +y.SetDefault("ContentDir", "foobar") + +//... +``` + +When working with multiple vipers, it is up to the user to keep track of the +different vipers. + +## Q & A + +Q: Why is it called “Viper”? + +A: Viper is designed to be a [companion](http://en.wikipedia.org/wiki/Viper_(G.I._Joe)) +to [Cobra](https://github.com/spf13/cobra). While both can operate completely +independently, together they make a powerful pair to handle much of your +application foundation needs. + +Q: Why is it called “Cobra”? + +A: Is there a better name for a [commander](http://en.wikipedia.org/wiki/Cobra_Commander)? diff --git a/vendor/github.com/ory/viper/flags.go b/vendor/github.com/ory/viper/flags.go new file mode 100644 index 0000000000..b5ddbf5d46 --- /dev/null +++ b/vendor/github.com/ory/viper/flags.go @@ -0,0 +1,57 @@ +package viper + +import "github.com/spf13/pflag" + +// FlagValueSet is an interface that users can implement +// to bind a set of flags to viper. +type FlagValueSet interface { + VisitAll(fn func(FlagValue)) +} + +// FlagValue is an interface that users can implement +// to bind different flags to viper. +type FlagValue interface { + HasChanged() bool + Name() string + ValueString() string + ValueType() string +} + +// pflagValueSet is a wrapper around *pflag.ValueSet +// that implements FlagValueSet. +type pflagValueSet struct { + flags *pflag.FlagSet +} + +// VisitAll iterates over all *pflag.Flag inside the *pflag.FlagSet. +func (p pflagValueSet) VisitAll(fn func(flag FlagValue)) { + p.flags.VisitAll(func(flag *pflag.Flag) { + fn(pflagValue{flag}) + }) +} + +// pflagValue is a wrapper aroung *pflag.flag +// that implements FlagValue +type pflagValue struct { + flag *pflag.Flag +} + +// HasChanged returns whether the flag has changes or not. +func (p pflagValue) HasChanged() bool { + return p.flag.Changed +} + +// Name returns the name of the flag. +func (p pflagValue) Name() string { + return p.flag.Name +} + +// ValueString returns the value of the flag as a string. +func (p pflagValue) ValueString() string { + return p.flag.Value.String() +} + +// ValueType returns the type of the flag as a string. +func (p pflagValue) ValueType() string { + return p.flag.Value.Type() +} diff --git a/vendor/github.com/ory/viper/go.mod b/vendor/github.com/ory/viper/go.mod new file mode 100644 index 0000000000..242a408fec --- /dev/null +++ b/vendor/github.com/ory/viper/go.mod @@ -0,0 +1,27 @@ +module github.com/ory/viper + +go 1.12 + +require ( + github.com/BurntSushi/toml v0.3.1 // indirect + github.com/dgraph-io/ristretto v0.0.1 + github.com/fsnotify/fsnotify v1.4.9 + github.com/hashicorp/hcl v1.0.0 + github.com/kr/pretty v0.2.0 // indirect + github.com/magiconair/properties v1.8.1 + github.com/mitchellh/mapstructure v1.1.2 + github.com/pelletier/go-toml v1.2.0 + github.com/smartystreets/goconvey v1.6.4 // indirect + github.com/spf13/afero v1.1.2 + github.com/spf13/cast v1.3.0 + github.com/spf13/jwalterweatherman v1.0.0 + github.com/spf13/pflag v1.0.3 + github.com/stretchr/testify v1.4.0 + github.com/subosito/gotenv v1.2.0 + golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 // indirect + golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 // indirect + golang.org/x/text v0.3.2 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + gopkg.in/ini.v1 v1.51.0 + gopkg.in/yaml.v2 v2.2.8 +) diff --git a/vendor/github.com/ory/viper/go.sum b/vendor/github.com/ory/viper/go.sum new file mode 100644 index 0000000000..5eafbe08ec --- /dev/null +++ b/vendor/github.com/ory/viper/go.sum @@ -0,0 +1,83 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/ristretto v0.0.1 h1:cJwdnj42uV8Jg4+KLrYovLiCgIfz9wtWm6E6KA+1tLs= +github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/ory/viper/util.go b/vendor/github.com/ory/viper/util.go new file mode 100644 index 0000000000..3b0e6d814d --- /dev/null +++ b/vendor/github.com/ory/viper/util.go @@ -0,0 +1,246 @@ +// Copyright © 2014 Steve Francia . +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// Viper is a application configuration system. +// It believes that applications can be configured a variety of ways +// via flags, ENVIRONMENT variables, configuration files retrieved +// from the file system, or a remote key/value store. + +package viper + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + "unicode" + + "github.com/spf13/afero" + "github.com/spf13/cast" + jww "github.com/spf13/jwalterweatherman" +) + +// ConfigParseError denotes failing to parse configuration file. +type ConfigParseError struct { + err error +} + +// Error returns the formatted configuration error. +func (pe ConfigParseError) Error() string { + return fmt.Sprintf("While parsing config: %s", pe.err.Error()) +} + +// toCaseInsensitiveValue checks if the value is a map; +// if so, create a copy and lower-case the keys recursively. +func toCaseInsensitiveValue(value interface{}) interface{} { + switch v := value.(type) { + case map[interface{}]interface{}: + value = copyAndInsensitiviseMap(cast.ToStringMap(v)) + case map[string]interface{}: + value = copyAndInsensitiviseMap(v) + } + + return value +} + +// copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of +// any map it makes case insensitive. +func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} { + nm := make(map[string]interface{}) + + for key, val := range m { + lkey := strings.ToLower(key) + switch v := val.(type) { + case map[interface{}]interface{}: + nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v)) + case map[string]interface{}: + nm[lkey] = copyAndInsensitiviseMap(v) + default: + nm[lkey] = v + } + } + + return nm +} + +func insensitiviseMap(m map[string]interface{}) { + for key, val := range m { + switch val.(type) { + case map[interface{}]interface{}: + // nested map: cast and recursively insensitivise + val = cast.ToStringMap(val) + insensitiviseMap(val.(map[string]interface{})) + case map[string]interface{}: + // nested map: recursively insensitivise + insensitiviseMap(val.(map[string]interface{})) + } + + lower := strings.ToLower(key) + if key != lower { + // remove old key (not lower-cased) + delete(m, key) + } + // update map + m[lower] = val + } +} + +func absPathify(inPath string) string { + jww.INFO.Println("Trying to resolve absolute path to", inPath) + + if strings.HasPrefix(inPath, "$HOME") { + inPath = userHomeDir() + inPath[5:] + } + + if strings.HasPrefix(inPath, "$") { + end := strings.Index(inPath, string(os.PathSeparator)) + inPath = os.Getenv(inPath[1:end]) + inPath[end:] + } + + if filepath.IsAbs(inPath) { + return filepath.Clean(inPath) + } + + p, err := filepath.Abs(inPath) + if err == nil { + return filepath.Clean(p) + } + + jww.ERROR.Println("Couldn't discover absolute path") + jww.ERROR.Println(err) + return "" +} + +// Check if file Exists +func exists(fs afero.Fs, path string) (bool, error) { + stat, err := fs.Stat(path) + if err == nil { + return !stat.IsDir(), nil + } + if os.IsNotExist(err) { + return false, nil + } + return false, err +} + +func stringInSlice(a string, list []string) bool { + for _, b := range list { + if b == a { + return true + } + } + return false +} + +func userHomeDir() string { + if runtime.GOOS == "windows" { + home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") + if home == "" { + home = os.Getenv("USERPROFILE") + } + return home + } + return os.Getenv("HOME") +} + +func safeMul(a, b uint) uint { + c := a * b + if a > 1 && b > 1 && c/b != a { + return 0 + } + return c +} + +// parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes +func parseSizeInBytes(sizeStr string) uint { + sizeStr = strings.TrimSpace(sizeStr) + lastChar := len(sizeStr) - 1 + multiplier := uint(1) + + if lastChar > 0 { + if sizeStr[lastChar] == 'b' || sizeStr[lastChar] == 'B' { + if lastChar > 1 { + switch unicode.ToLower(rune(sizeStr[lastChar-1])) { + case 'k': + multiplier = 1 << 10 + sizeStr = strings.TrimSpace(sizeStr[:lastChar-1]) + case 'm': + multiplier = 1 << 20 + sizeStr = strings.TrimSpace(sizeStr[:lastChar-1]) + case 'g': + multiplier = 1 << 30 + sizeStr = strings.TrimSpace(sizeStr[:lastChar-1]) + default: + multiplier = 1 + sizeStr = strings.TrimSpace(sizeStr[:lastChar]) + } + } + } + } + + size := cast.ToInt(sizeStr) + if size < 0 { + size = 0 + } + + return safeMul(uint(size), multiplier) +} + +// deepSearch scans deep maps, following the key indexes listed in the +// sequence "path". +// The last value is expected to be another map, and is returned. +// +// In case intermediate keys do not exist, or map to a non-map value, +// a new map is created and inserted, and the search continues from there: +// the initial map "m" may be modified! +func deepSearch(m map[string]interface{}, path []string) map[string]interface{} { + for _, k := range path { + m2, ok := m[k] + if !ok { + // intermediate key does not exist + // => create it and continue from there + m3 := make(map[string]interface{}) + m[k] = m3 + m = m3 + continue + } + m3, ok := m2.(map[string]interface{}) + if !ok { + // intermediate key is a value + // => replace with a new map + m3 = make(map[string]interface{}) + m[k] = m3 + } + // continue search from here + m = m3 + } + return m +} + +// toMapStringInterface is a workaround for https://github.com/ory/viper/issues/730 +// and https://github.com/go-yaml/yaml/issues/139 +func toMapStringInterface(in interface{}) interface{} { + switch t := in.(type) { + case map[string]interface{}: + for k, v := range t { + t[k] = toMapStringInterface(v) + } + return t + case map[interface{}]interface{}: + nt := make(map[string]interface{}) + for k, v := range t { + nt[fmt.Sprintf("%s", k)] = toMapStringInterface(v) + } + return nt + case []interface{}: + for k, v := range t { + t[k] = toMapStringInterface(v) + } + return t + default: + return in + } +} diff --git a/vendor/github.com/ory/viper/viper.go b/vendor/github.com/ory/viper/viper.go new file mode 100644 index 0000000000..3582d22ece --- /dev/null +++ b/vendor/github.com/ory/viper/viper.go @@ -0,0 +1,2277 @@ +// Copyright © 2014 Steve Francia . +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// Viper is an application configuration system. +// It believes that applications can be configured a variety of ways +// via flags, ENVIRONMENT variables, configuration files retrieved +// from the file system, or a remote key/value store. + +// Each item takes precedence over the item below it: + +// overrides +// flag +// env +// config +// key/value store +// default + +package viper + +import ( + "bytes" + "encoding/csv" + "encoding/json" + "errors" + "fmt" + "io" + "log" + "os" + "path/filepath" + "reflect" + "strings" + "sync" + "time" + + "github.com/dgraph-io/ristretto" + + "github.com/fsnotify/fsnotify" + "github.com/hashicorp/hcl" + "github.com/hashicorp/hcl/hcl/printer" + "github.com/magiconair/properties" + "github.com/mitchellh/mapstructure" + "github.com/pelletier/go-toml" + "github.com/spf13/afero" + "github.com/spf13/cast" + jww "github.com/spf13/jwalterweatherman" + "github.com/spf13/pflag" + "github.com/subosito/gotenv" + "gopkg.in/ini.v1" + "gopkg.in/yaml.v2" +) + +// ConfigMarshalError happens when failing to marshal the configuration. +type ConfigMarshalError struct { + err error +} + +// Error returns the formatted configuration error. +func (e ConfigMarshalError) Error() string { + return fmt.Sprintf("While marshaling config: %s", e.err.Error()) +} + +var v *Viper + +type RemoteResponse struct { + Value []byte + Error error +} + +func init() { + v = New() +} + +type remoteConfigFactory interface { + Get(rp RemoteProvider) (io.Reader, error) + Watch(rp RemoteProvider) (io.Reader, error) + WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool) +} + +// RemoteConfig is optional, see the remote package +var RemoteConfig remoteConfigFactory + +// UnsupportedConfigError denotes encountering an unsupported +// configuration filetype. +type UnsupportedConfigError string + +// Error returns the formatted configuration error. +func (str UnsupportedConfigError) Error() string { + return fmt.Sprintf("Unsupported Config Type %q", string(str)) +} + +// UnsupportedRemoteProviderError denotes encountering an unsupported remote +// provider. Currently only etcd and Consul are supported. +type UnsupportedRemoteProviderError string + +// Error returns the formatted remote provider error. +func (str UnsupportedRemoteProviderError) Error() string { + return fmt.Sprintf("Unsupported Remote Provider Type %q", string(str)) +} + +// RemoteConfigError denotes encountering an error while trying to +// pull the configuration from the remote provider. +type RemoteConfigError string + +// Error returns the formatted remote provider error +func (rce RemoteConfigError) Error() string { + return fmt.Sprintf("Remote Configurations Error: %s", string(rce)) +} + +// ConfigFileNotFoundError denotes failing to find configuration file. +type ConfigFileNotFoundError struct { + name, locations string +} + +// Error returns the formatted configuration error. +func (fnfe ConfigFileNotFoundError) Error() string { + return fmt.Sprintf("Config File %q Not Found in %q", fnfe.name, fnfe.locations) +} + +// ConfigFileAlreadyExistsError denotes failure to write new configuration file. +type ConfigFileAlreadyExistsError string + +// Error returns the formatted error when configuration already exists. +func (faee ConfigFileAlreadyExistsError) Error() string { + return fmt.Sprintf("Config File %q Already Exists", string(faee)) +} + +// A DecoderConfigOption can be passed to viper.Unmarshal to configure +// mapstructure.DecoderConfig options +type DecoderConfigOption func(*mapstructure.DecoderConfig) + +// DecodeHook returns a DecoderConfigOption which overrides the default +// DecoderConfig.DecodeHook value, the default is: +// +// mapstructure.ComposeDecodeHookFunc( +// mapstructure.StringToTimeDurationHookFunc(), +// mapstructure.StringToSliceHookFunc(","), +// ) +func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption { + return func(c *mapstructure.DecoderConfig) { + c.DecodeHook = hook + } +} + +// Viper is a prioritized configuration registry. It +// maintains a set of configuration sources, fetches +// values to populate those, and provides them according +// to the source's priority. +// The priority of the sources is the following: +// 1. overrides +// 2. flags +// 3. env. variables +// 4. config file +// 5. key/value store +// 6. defaults +// +// For example, if values from the following sources were loaded: +// +// Defaults : { +// "secret": "", +// "user": "default", +// "endpoint": "https://localhost" +// } +// Config : { +// "user": "root" +// "secret": "defaultsecret" +// } +// Env : { +// "secret": "somesecretkey" +// } +// +// The resulting config will have the following values: +// +// { +// "secret": "somesecretkey", +// "user": "root", +// "endpoint": "https://localhost" +// } +type Viper struct { + // Delimiter that separates a list of keys + // used to access a nested value in one go + keyDelim string + + // A set of paths to look for the config file in + configPaths []string + + // The filesystem to read config from. + fs afero.Fs + + // A set of remote providers to search for the configuration + remoteProviders []*defaultRemoteProvider + + // Name of file to look for inside the path + configName string + configFile string + configType string + configPermissions os.FileMode + configChangedAt time.Time + envPrefix string + + automaticEnvApplied bool + envKeyReplacer StringReplacer + allowEmptyEnv bool + + config map[string]interface{} + override map[string]interface{} + defaults map[string]interface{} + types map[string]interface{} + kvstore map[string]interface{} + pflags map[string]FlagValue + env map[string]string + aliases map[string]string + typeByDefValue bool + + previousValues map[string]interface{} + + // Store read properties on the object so that we can write back in order with comments. + // This will only be used if the configuration read is a properties file. + properties *properties.Properties + + onConfigChange func(fsnotify.Event) + + cache *ristretto.Cache + cacheMaxCost int64 + + lock *sync.RWMutex +} + +// New returns an initialized Viper instance. +func New() *Viper { + v := new(Viper) + v.keyDelim = "." + v.configName = "config" + v.configChangedAt = time.Now() + v.configPermissions = os.FileMode(0644) + v.fs = afero.NewOsFs() + v.config = make(map[string]interface{}) + v.override = make(map[string]interface{}) + v.defaults = make(map[string]interface{}) + v.types = make(map[string]interface{}) + v.kvstore = make(map[string]interface{}) + v.previousValues = make(map[string]interface{}) + v.pflags = make(map[string]FlagValue) + v.env = make(map[string]string) + v.aliases = make(map[string]string) + v.typeByDefValue = false + // v.lock = &lockLogger{new(sync.RWMutex)} + v.lock = new(sync.RWMutex) + + var err error + v.cacheMaxCost = 1 << 20 // 1MB max cache + v.cache, err = ristretto.NewCache(&ristretto.Config{ + NumCounters: 1000, + MaxCost: 1 << 20, + BufferItems: 64, + }) + if err != nil { + // This will only happen if ristretto decides to throw an error based on the given configuration + // in future versions which is unlikely and therefore a panic'able error. + // + // Currently, ristretto will only error if one of the provided settings (NumCounters, MaxCost, BufferItems) + // is <= 0. + panic(fmt.Sprintf("cache options are invalid because: %s", err)) + } + + return v +} + +// Option configures Viper using the functional options paradigm popularized by Rob Pike and Dave Cheney. +// If you're unfamiliar with this style, +// see https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html and +// https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis. +type Option interface { + apply(v *Viper) +} + +type optionFunc func(v *Viper) + +func (fn optionFunc) apply(v *Viper) { + fn(v) +} + +// KeyDelimiter sets the delimiter used for determining key parts. +// By default it's value is ".". +func KeyDelimiter(d string) Option { + return optionFunc(func(v *Viper) { + v.keyDelim = d + }) +} + +// StringReplacer applies a set of replacements to a string. +type StringReplacer interface { + // Replace returns a copy of s with all replacements performed. + Replace(s string) string +} + +// EnvKeyReplacer sets a replacer used for mapping environment variables to internal keys. +func EnvKeyReplacer(r StringReplacer) Option { + return optionFunc(func(v *Viper) { + v.envKeyReplacer = r + }) +} + +// Cache sets Viper's cache (*ristretto.Cache). You must also pass the ristretto.Config +// object for some internal processing. +func Cache(c *ristretto.Cache, cf *ristretto.Config) Option { + return optionFunc(func(v *Viper) { + v.cache = c + v.cacheMaxCost = cf.MaxCost + }) +} + +// NewWithOptions creates a new Viper instance. +func NewWithOptions(opts ...Option) *Viper { + v := New() + + for _, opt := range opts { + opt.apply(v) + } + + return v +} + +// Reset is intended for testing, will reset all to default settings. +// In the public interface for the viper package so applications +// can use it in their testing as well. +func Reset() { + v = New() + SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "dotenv", "env", "ini"} + SupportedRemoteProviders = []string{"etcd", "consul", "firestore"} +} + +type defaultRemoteProvider struct { + provider string + endpoint string + path string + secretKeyring string +} + +func (rp defaultRemoteProvider) Provider() string { + return rp.provider +} + +func (rp defaultRemoteProvider) Endpoint() string { + return rp.endpoint +} + +func (rp defaultRemoteProvider) Path() string { + return rp.path +} + +func (rp defaultRemoteProvider) SecretKeyring() string { + return rp.secretKeyring +} + +// RemoteProvider stores the configuration necessary +// to connect to a remote key/value store. +// Optional secretKeyring to unencrypt encrypted values +// can be provided. +type RemoteProvider interface { + Provider() string + Endpoint() string + Path() string + SecretKeyring() string +} + +// SupportedExts are universally supported extensions. +var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "dotenv", "env", "ini"} + +// SupportedRemoteProviders are universally supported remote providers. +var SupportedRemoteProviders = []string{"etcd", "consul", "firestore"} + +func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) } +func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) { + v.onConfigChange = run +} + +func WatchConfig() { v.WatchConfig() } + +func (v *Viper) WatchConfig() { + initWG := sync.WaitGroup{} + initWG.Add(1) + go func() { + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatal(err) + } + defer watcher.Close() + // we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way + filename, err := v.getConfigFile() + if err != nil { + log.Printf("error: %v\n", err) + initWG.Done() + return + } + + configFile := filepath.Clean(filename) + configDir, _ := filepath.Split(configFile) + realConfigFile, _ := filepath.EvalSymlinks(filename) + + eventsWG := sync.WaitGroup{} + eventsWG.Add(1) + go func() { + for { + select { + case event, ok := <-watcher.Events: + if !ok { // 'Events' channel is closed + eventsWG.Done() + return + } + currentConfigFile, _ := filepath.EvalSymlinks(filename) + // we only care about the config file with the following cases: + // 1 - if the config file was modified or created + // 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement) + const writeOrCreateMask = fsnotify.Write | fsnotify.Create + if (filepath.Clean(event.Name) == configFile && + event.Op&writeOrCreateMask != 0) || + (currentConfigFile != "" && currentConfigFile != realConfigFile) { + realConfigFile = currentConfigFile + err := v.ReadInConfig() + if err != nil { + log.Printf("error reading config file: %v\n", err) + } + if v.onConfigChange != nil { + v.onConfigChange(event) + } + } else if filepath.Clean(event.Name) == configFile && + event.Op&fsnotify.Remove&fsnotify.Remove != 0 { + eventsWG.Done() + return + } + + case err, ok := <-watcher.Errors: + if ok { // 'Errors' channel is not closed + log.Printf("watcher error: %v\n", err) + } + eventsWG.Done() + return + } + } + }() + watcher.Add(configDir) + initWG.Done() // done initializing the watch in this go routine, so the parent routine can move on... + eventsWG.Wait() // now, wait for event loop to end in this go-routine... + }() + initWG.Wait() // make sure that the go routine above fully ended before returning +} + +// SetConfigFile explicitly defines the path, name and extension of the config file. +// Viper will use this and not check any of the config paths. +func SetConfigFile(in string) { v.SetConfigFile(in) } +func (v *Viper) SetConfigFile(in string) { + if in != "" { + v.lock.Lock() + v.configFile = in + v.lock.Unlock() + } +} + +// SetEnvPrefix defines a prefix that ENVIRONMENT variables will use. +// E.g. if your prefix is "spf", the env registry will look for env +// variables that start with "SPF_". +func SetEnvPrefix(in string) { v.SetEnvPrefix(in) } +func (v *Viper) SetEnvPrefix(in string) { + if in != "" { + v.lock.Lock() + v.cache.Clear() + v.envPrefix = in + v.lock.Unlock() + } +} + +func (v *Viper) mergeWithEnvPrefix(in string) string { + if v.envPrefix != "" { + return strings.ToUpper(v.envPrefix + "_" + in) + } + + return strings.ToUpper(in) +} + +// AllowEmptyEnv tells Viper to consider set, +// but empty environment variables as valid values instead of falling back. +// For backward compatibility reasons this is false by default. +func AllowEmptyEnv(allowEmptyEnv bool) { v.AllowEmptyEnv(allowEmptyEnv) } +func (v *Viper) AllowEmptyEnv(allowEmptyEnv bool) { + v.lock.Lock() + v.cache.Clear() + v.allowEmptyEnv = allowEmptyEnv + v.lock.Unlock() +} + +// TODO: should getEnv logic be moved into find(). Can generalize the use of +// rewriting keys many things, Ex: Get('someKey') -> some_key +// (camel case to snake case for JSON keys perhaps) + +// getEnv is a wrapper around os.Getenv which replaces characters in the original +// key. This allows env vars which have different keys than the config object +// keys. +func (v *Viper) getEnv(key string) (string, bool) { + if v.envKeyReplacer != nil { + key = v.envKeyReplacer.Replace(key) + } + + val, ok := os.LookupEnv(key) + + return val, ok && (v.allowEmptyEnv || val != "") +} + +// ConfigFileUsed returns the file used to populate the config registry. +func ConfigFileUsed() string { return v.ConfigFileUsed() } +func (v *Viper) ConfigFileUsed() string { + v.lock.RLock() + defer v.lock.RUnlock() + + return v.configFile +} + +// ConfigChangeAt returns the time of the last config change. +func ConfigChangeAt() time.Time { return v.ConfigChangeAt() } +func (v *Viper) ConfigChangeAt() time.Time { + v.lock.RLock() + defer v.lock.RUnlock() + + return v.configChangedAt +} + +// AddConfigPath adds a path for Viper to search for the config file in. +// Can be called multiple times to define multiple search paths. +func AddConfigPath(in string) { v.AddConfigPath(in) } +func (v *Viper) AddConfigPath(in string) { + if in != "" { + absin := absPathify(in) + jww.INFO.Println("adding", absin, "to paths to search") + v.lock.Lock() + if !stringInSlice(absin, v.configPaths) { + v.cache.Clear() + v.configPaths = append(v.configPaths, absin) + } + v.lock.Unlock() + } +} + +// AddRemoteProvider adds a remote configuration source. +// Remote Providers are searched in the order they are added. +// provider is a string value: "etcd", "consul" or "firestore" are currently supported. +// endpoint is the url. etcd requires http://ip:port consul requires ip:port +// path is the path in the k/v store to retrieve configuration +// To retrieve a config file called myapp.json from /configs/myapp.json +// you should set path to /configs and set config name (SetConfigName()) to +// "myapp" +func AddRemoteProvider(provider, endpoint, path string) error { + return v.AddRemoteProvider(provider, endpoint, path) +} +func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error { + if !stringInSlice(provider, SupportedRemoteProviders) { + return UnsupportedRemoteProviderError(provider) + } + if provider != "" && endpoint != "" { + jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint) + rp := &defaultRemoteProvider{ + endpoint: endpoint, + provider: provider, + path: path, + } + if !v.providerPathExists(rp) { + v.lock.Lock() + v.cache.Clear() + v.remoteProviders = append(v.remoteProviders, rp) + v.lock.Unlock() + } + } + return nil +} + +// AddSecureRemoteProvider adds a remote configuration source. +// Secure Remote Providers are searched in the order they are added. +// provider is a string value: "etcd", "consul" or "firestore" are currently supported. +// endpoint is the url. etcd requires http://ip:port consul requires ip:port +// secretkeyring is the filepath to your openpgp secret keyring. e.g. /etc/secrets/myring.gpg +// path is the path in the k/v store to retrieve configuration +// To retrieve a config file called myapp.json from /configs/myapp.json +// you should set path to /configs and set config name (SetConfigName()) to +// "myapp" +// Secure Remote Providers are implemented with github.com/bketelsen/crypt +func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error { + return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring) +} + +func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error { + if !stringInSlice(provider, SupportedRemoteProviders) { + return UnsupportedRemoteProviderError(provider) + } + if provider != "" && endpoint != "" { + jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint) + rp := &defaultRemoteProvider{ + endpoint: endpoint, + provider: provider, + path: path, + secretKeyring: secretkeyring, + } + if !v.providerPathExists(rp) { + v.lock.Lock() + v.cache.Clear() + v.remoteProviders = append(v.remoteProviders, rp) + v.lock.Unlock() + } + } + return nil +} + +func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool { + v.lock.RLock() + defer v.lock.RUnlock() + for _, y := range v.remoteProviders { + if reflect.DeepEqual(y, p) { + return true + } + } + return false +} + +// searchMap recursively searches for a value for path in source map. +// Returns nil if not found. +// Note: This assumes that the path entries and map keys are lower cased. +func (v *Viper) searchMap(source map[string]interface{}, path []string) interface{} { + if len(path) == 0 { + return source + } + + next, ok := source[path[0]] + if ok { + // Fast path + if len(path) == 1 { + return next + } + + // Nested case + switch next.(type) { + case map[interface{}]interface{}: + return v.searchMap(cast.ToStringMap(next), path[1:]) + case map[string]interface{}: + // Type assertion is safe here since it is only reached + // if the type of `next` is the same as the type being asserted + return v.searchMap(next.(map[string]interface{}), path[1:]) + default: + // got a value but nested key expected, return "nil" for not found + return nil + } + } + return nil +} + +// searchMapWithPathPrefixes recursively searches for a value for path in source map. +// +// While searchMap() considers each path element as a single map key, this +// function searches for, and prioritizes, merged path elements. +// e.g., if in the source, "foo" is defined with a sub-key "bar", and "foo.bar" +// is also defined, this latter value is returned for path ["foo", "bar"]. +// +// This should be useful only at config level (other maps may not contain dots +// in their keys). +// +// Note: This assumes that the path entries and map keys are lower cased. +func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []string) interface{} { + if len(path) == 0 { + return source + } + + // search for path prefixes, starting from the longest one + for i := len(path); i > 0; i-- { + prefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim)) + + next, ok := source[prefixKey] + if ok { + // Fast path + if i == len(path) { + return next + } + + // Nested case + var val interface{} + switch next.(type) { + case map[interface{}]interface{}: + val = v.searchMapWithPathPrefixes(cast.ToStringMap(next), path[i:]) + case map[string]interface{}: + // Type assertion is safe here since it is only reached + // if the type of `next` is the same as the type being asserted + val = v.searchMapWithPathPrefixes(next.(map[string]interface{}), path[i:]) + default: + // got a value but nested key expected, do nothing and look for next prefix + } + if val != nil { + return val + } + } + } + + // not found + return nil +} + +// isPathShadowedInDeepMap makes sure the given path is not shadowed somewhere +// on its path in the map. +// e.g., if "foo.bar" has a value in the given map, it “shadows” +// "foo.bar.baz" in a lower-priority map +func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{}) string { + var parentVal interface{} + for i := 1; i < len(path); i++ { + parentVal = v.searchMap(m, path[0:i]) + if parentVal == nil { + // not found, no need to add more path elements + return "" + } + switch parentVal.(type) { + case map[interface{}]interface{}: + continue + case map[string]interface{}: + continue + default: + // parentVal is a regular value which shadows "path" + return strings.Join(path[0:i], v.keyDelim) + } + } + return "" +} + +// isPathShadowedInFlatMap makes sure the given path is not shadowed somewhere +// in a sub-path of the map. +// e.g., if "foo.bar" has a value in the given map, it “shadows” +// "foo.bar.baz" in a lower-priority map +func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string { + // unify input map + var m map[string]interface{} + switch mi.(type) { + case map[string]string, map[string]FlagValue: + m = cast.ToStringMap(mi) + default: + return "" + } + + // scan paths + var parentKey string + for i := 1; i < len(path); i++ { + parentKey = strings.Join(path[0:i], v.keyDelim) + if _, ok := m[parentKey]; ok { + return parentKey + } + } + return "" +} + +// isPathShadowedInAutoEnv makes sure the given path is not shadowed somewhere +// in the environment, when automatic env is on. +// e.g., if "foo.bar" has a value in the environment, it “shadows” +// "foo.bar.baz" in a lower-priority map +func (v *Viper) isPathShadowedInAutoEnv(path []string) string { + var parentKey string + for i := 1; i < len(path); i++ { + parentKey = strings.Join(path[0:i], v.keyDelim) + if _, ok := v.getEnv(v.mergeWithEnvPrefix(parentKey)); ok { + return parentKey + } + } + return "" +} + +// SetTypeByDefaultValue enables or disables the inference of a key value's +// type when the Get function is used based upon a key's default value as +// opposed to the value returned based on the normal fetch logic. +// +// For example, if a key has a default value of []string{} and the same key +// is set via an environment variable to "a b c", a call to the Get function +// would return a string slice for the key if the key's type is inferred by +// the default value and the Get function would return: +// +// []string {"a", "b", "c"} +// +// Otherwise the Get function would return: +// +// "a b c" +func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) } +func (v *Viper) SetTypeByDefaultValue(enable bool) { + v.lock.Lock() + v.cache.Clear() + v.typeByDefValue = enable + v.lock.Unlock() +} + +// GetViper gets the global Viper instance. +func GetViper() *Viper { + return v +} + +// HasChanged returns true if a key has changed and the change has not been retrieved yet using `Get()` and all +// casters `GetString()`, `GetDuration()`, ... +// +// If the value has not been retrieved at all this will also return true. +func HasChanged(key string) bool { return v.HasChanged(key) } +func (v *Viper) HasChanged(key string) bool { + lcaseKey := strings.ToLower(key) + + v.lock.RLock() + value, ok := v.previousValues[lcaseKey] + v.lock.RUnlock() + if !ok { + return IsSet(key) + } + + // Avoid writing the change with v.find + return !reflect.DeepEqual(value, v.find(lcaseKey, true)) +} + +// HasChangedSinceInit returns true if a key has changed and the change has not been retrieved yet using `Get()` and all +// casters `GetString()`, `GetDuration()`, ... +// +// If the value has not been retrieved before at all this will return false. +func HasChangedSinceInit(key string) bool { return v.HasChangedSinceInit(key) } +func (v *Viper) HasChangedSinceInit(key string) bool { + lcaseKey := strings.ToLower(key) + + v.lock.RLock() + value, ok := v.previousValues[lcaseKey] + v.lock.RUnlock() + if !ok { + return false + } + + // Avoid writing the change with v.find + return !reflect.DeepEqual(value, v.find(lcaseKey, true)) +} + +func castAllSourcesE(t, val interface{}) (interface{}, error) { + switch t.(type) { + case time.Time: + return cast.ToTimeE(val) + case time.Duration: + return cast.ToDurationE(val) + } + + return val, nil +} + +func castStringSourcesE(t interface{}, val string) (interface{}, error) { + switch t.(type) { + case bool: + return cast.ToBoolE(val) + case string: + return val, nil + case int32, int16, int8, int: + return cast.ToIntE(val) + case uint: + return cast.ToUintE(val) + case uint32: + return cast.ToUint32E(val) + case uint64: + return cast.ToUint64E(val) + case int64: + return cast.ToInt64E(val) + case float64, float32: + return cast.ToFloat64E(val) + case []string: + s := strings.TrimPrefix(val, "[") + s = strings.TrimSuffix(s, "]") + res, err := readAsCSV(s) + if err != nil { + return []string{}, err + } + return res, nil + case []int: + s := strings.TrimPrefix(val, "[") + s = strings.TrimSuffix(s, "]") + res, err := readAsCSV(s) + if err != nil { + return []int{}, err + } + return cast.ToIntSliceE(res) + } + + return castAllSourcesE(t, val) +} + +func (v *Viper) getType(lcaseKey string) interface{} { + v.lock.RLock() + defer v.lock.RUnlock() + + path := strings.Split(lcaseKey, v.keyDelim) + + if typeVal := v.searchMap(v.types, path); typeVal != nil { + return typeVal + } else if v.typeByDefValue { + return v.searchMap(v.defaults, path) + } + return nil +} + +// Get can retrieve any value given the key to use. +// Get is case-insensitive for a key. +// Get has the behavior of returning the value associated with the first +// place from where it is set. Viper will check in the following order: +// override, flag, env, config file, key/value store, default +// +// Get returns an interface. For a specific value use one of the Get____ methods. +func GetE(key string) (interface{}, error) { return v.GetE(key) } +func (v *Viper) GetE(key string) (interface{}, error) { + lcaseKey := strings.ToLower(key) + + val, err := v.cachedFindE(lcaseKey, true) + if err != nil { + return val, err + } + + v.lock.Lock() + v.previousValues[lcaseKey] = val + v.lock.Unlock() + + return val, nil +} +func Get(key string) interface{} { return v.Get(key) } +func (v *Viper) Get(key string) interface{} { + val, _ := v.GetE(key) + return val +} + +// Sub returns new Viper instance representing a sub tree of this instance. +// Sub is case-insensitive for a key. +func Sub(key string) *Viper { return v.Sub(key) } +func (v *Viper) Sub(key string) *Viper { + subv := New() + data := v.Get(key) + if data == nil { + return nil + } + + if reflect.TypeOf(data).Kind() == reflect.Map { + subv.config = cast.ToStringMap(data) + return subv + } + return nil +} + +// GetString returns the value associated with the key as a string. +func GetString(key string) string { return v.GetString(key) } +func (v *Viper) GetString(key string) string { + return cast.ToString(v.Get(key)) +} + +// GetBool returns the value associated with the key as a boolean. +func GetBool(key string) bool { return v.GetBool(key) } +func (v *Viper) GetBool(key string) bool { + return cast.ToBool(v.Get(key)) +} + +// GetInt returns the value associated with the key as an integer. +func GetInt(key string) int { return v.GetInt(key) } +func (v *Viper) GetInt(key string) int { + return cast.ToInt(v.Get(key)) +} + +// GetInt32 returns the value associated with the key as an integer. +func GetInt32(key string) int32 { return v.GetInt32(key) } +func (v *Viper) GetInt32(key string) int32 { + return cast.ToInt32(v.Get(key)) +} + +// GetInt64 returns the value associated with the key as an integer. +func GetInt64(key string) int64 { return v.GetInt64(key) } +func (v *Viper) GetInt64(key string) int64 { + return cast.ToInt64(v.Get(key)) +} + +// GetUint returns the value associated with the key as an unsigned integer. +func GetUint(key string) uint { return v.GetUint(key) } +func (v *Viper) GetUint(key string) uint { + return cast.ToUint(v.Get(key)) +} + +// GetUint32 returns the value associated with the key as an unsigned integer. +func GetUint32(key string) uint32 { return v.GetUint32(key) } +func (v *Viper) GetUint32(key string) uint32 { + return cast.ToUint32(v.Get(key)) +} + +// GetUint64 returns the value associated with the key as an unsigned integer. +func GetUint64(key string) uint64 { return v.GetUint64(key) } +func (v *Viper) GetUint64(key string) uint64 { + return cast.ToUint64(v.Get(key)) +} + +// GetFloat64 returns the value associated with the key as a float64. +func GetFloat64(key string) float64 { return v.GetFloat64(key) } +func (v *Viper) GetFloat64(key string) float64 { + return cast.ToFloat64(v.Get(key)) +} + +// GetTime returns the value associated with the key as time. +func GetTime(key string) time.Time { return v.GetTime(key) } +func (v *Viper) GetTime(key string) time.Time { + return cast.ToTime(v.Get(key)) +} + +// GetDuration returns the value associated with the key as a duration. +func GetDuration(key string) time.Duration { return v.GetDuration(key) } +func (v *Viper) GetDuration(key string) time.Duration { + return cast.ToDuration(v.Get(key)) +} + +// GetIntSlice returns the value associated with the key as a slice of int values. +func GetIntSlice(key string) []int { return v.GetIntSlice(key) } +func (v *Viper) GetIntSlice(key string) []int { + return cast.ToIntSlice(v.Get(key)) +} + +// GetStringSlice returns the value associated with the key as a slice of strings. +func GetStringSlice(key string) []string { return v.GetStringSlice(key) } +func (v *Viper) GetStringSlice(key string) []string { + return cast.ToStringSlice(v.Get(key)) +} + +// GetStringMap returns the value associated with the key as a map of interfaces. +func GetStringMap(key string) map[string]interface{} { return v.GetStringMap(key) } +func (v *Viper) GetStringMap(key string) map[string]interface{} { + return cast.ToStringMap(v.Get(key)) +} + +// GetStringMapString returns the value associated with the key as a map of strings. +func GetStringMapString(key string) map[string]string { return v.GetStringMapString(key) } +func (v *Viper) GetStringMapString(key string) map[string]string { + return cast.ToStringMapString(v.Get(key)) +} + +// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings. +func GetStringMapStringSlice(key string) map[string][]string { return v.GetStringMapStringSlice(key) } +func (v *Viper) GetStringMapStringSlice(key string) map[string][]string { + return cast.ToStringMapStringSlice(v.Get(key)) +} + +// GetSizeInBytes returns the size of the value associated with the given key +// in bytes. +func GetSizeInBytes(key string) uint { return v.GetSizeInBytes(key) } +func (v *Viper) GetSizeInBytes(key string) uint { + sizeStr := cast.ToString(v.Get(key)) + return parseSizeInBytes(sizeStr) +} + +// UnmarshalKey takes a single key and unmarshals it into a Struct. +func UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error { + return v.UnmarshalKey(key, rawVal, opts...) +} +func (v *Viper) UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error { + err := decode(v.Get(key), defaultDecoderConfig(rawVal, opts...)) + + if err != nil { + return err + } + + return nil +} + +// Unmarshal unmarshals the config into a Struct. Make sure that the tags +// on the fields of the structure are properly set. +func Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error { + return v.Unmarshal(rawVal, opts...) +} +func (v *Viper) Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error { + err := decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...)) + + if err != nil { + return err + } + + return nil +} + +// defaultDecoderConfig returns default mapsstructure.DecoderConfig with suppot +// of time.Duration values & string slices +func defaultDecoderConfig(output interface{}, opts ...DecoderConfigOption) *mapstructure.DecoderConfig { + c := &mapstructure.DecoderConfig{ + Metadata: nil, + Result: output, + WeaklyTypedInput: true, + DecodeHook: mapstructure.ComposeDecodeHookFunc( + mapstructure.StringToTimeDurationHookFunc(), + mapstructure.StringToSliceHookFunc(","), + ), + } + for _, opt := range opts { + opt(c) + } + return c +} + +// A wrapper around mapstructure.Decode that mimics the WeakDecode functionality +func decode(input interface{}, config *mapstructure.DecoderConfig) error { + decoder, err := mapstructure.NewDecoder(config) + if err != nil { + return err + } + return decoder.Decode(input) +} + +// UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent +// in the destination struct. +func UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error { + return v.UnmarshalExact(rawVal, opts...) +} +func (v *Viper) UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error { + config := defaultDecoderConfig(rawVal, opts...) + config.ErrorUnused = true + + err := decode(v.AllSettings(), config) + + if err != nil { + return err + } + + return nil +} + +// BindPFlags binds a full flag set to the configuration, using each flag's long +// name as the config key. +func BindPFlags(flags *pflag.FlagSet) error { return v.BindPFlags(flags) } +func (v *Viper) BindPFlags(flags *pflag.FlagSet) error { + return v.BindFlagValues(pflagValueSet{flags}) +} + +// BindPFlag binds a specific key to a pflag (as used by cobra). +// Example (where serverCmd is a Cobra instance): +// +// serverCmd.Flags().Int("port", 1138, "Port to run Application server on") +// Viper.BindPFlag("port", serverCmd.Flags().Lookup("port")) +// +func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) } +func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error { + return v.BindFlagValue(key, pflagValue{flag}) +} + +// BindFlagValues binds a full FlagValue set to the configuration, using each flag's long +// name as the config key. +func BindFlagValues(flags FlagValueSet) error { return v.BindFlagValues(flags) } +func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) { + flags.VisitAll(func(flag FlagValue) { + if err = v.BindFlagValue(flag.Name(), flag); err != nil { + return + } + }) + return nil +} + +// BindFlagValue binds a specific key to a FlagValue. +func BindFlagValue(key string, flag FlagValue) error { return v.BindFlagValue(key, flag) } +func (v *Viper) BindFlagValue(key string, flag FlagValue) error { + if flag == nil { + return fmt.Errorf("flag for %q is nil", key) + } + lcaseKey := strings.ToLower(key) + v.lock.Lock() + v.cache.Clear() + v.pflags[lcaseKey] = flag + v.lock.Unlock() + + // this is to maintain backwards compatibility with pflag.ValueType() + // one should use viper.SetType(...) instead + v.lock.RLock() + typ := v.searchMap(v.types, strings.Split(lcaseKey, v.keyDelim)) + v.lock.RUnlock() + // only use the old api if no type was set using SetType(...) + if typ == nil { + switch flag.ValueType() { + case "int", "int8", "int16", "int32", "int64": + v.SetType(key, 0) + case "bool": + v.SetType(key, false) + case "stringSlice": + v.SetType(key, []string{}) + case "intSlice": + v.SetType(key, []int{}) + default: + // unknown type, don't set any + } + } + return nil +} + +// BindEnv binds a Viper key to a ENV variable. +// ENV variables are case sensitive. +// If only a key is provided, it will use the env key matching the key, uppercased. +// EnvPrefix will be used when set when env name is not provided. +func BindEnv(input ...string) error { return v.BindEnv(input...) } +func (v *Viper) BindEnv(input ...string) error { + var key, envkey string + if len(input) == 0 { + return fmt.Errorf("missing key to bind to") + } + + key = strings.ToLower(input[0]) + + if len(input) == 1 { + envkey = v.mergeWithEnvPrefix(key) + } else { + envkey = input[1] + } + + v.lock.Lock() + v.cache.Clear() + v.env[key] = envkey + v.lock.Unlock() + + return nil +} + +// cachedFind uses Viper's cache to find a key's value and `v.find` if it is not available +// in the cache. +func (v *Viper) cachedFindE(lcaseKey string, flagDefault bool) (interface{}, error) { + realKey := v.realKey(lcaseKey) + + v.lock.RLock() + value, found := v.cache.Get(realKey) + v.lock.RUnlock() + if found { + return value, nil + } + + value, castErr := v.findE(realKey, flagDefault) + if castErr != nil { + return value, castErr + } + + v.lock.Lock() + v.cache.Set(realKey, value, 0) + v.lock.Unlock() + + return value, nil +} + +func (v *Viper) cachedFind(lcaseKey string, flagDefault bool) interface{} { + val, _ := v.cachedFindE(lcaseKey, flagDefault) + return val +} + +// Given a key, find the value. +// +// Viper will check to see if an alias exists first. +// Viper will then check in the following order: +// flag, env, config file, key/value store. +// Lastly, if no value was found and flagDefault is true, and if the key +// corresponds to a flag, the flag's default value is returned. +// +// Note: this assumes a lower-cased key given. +func (v *Viper) findE(lcaseKey string, flagDefault bool) (interface{}, error) { + v.lock.RLock() + var ( + val interface{} + exists bool + path = strings.Split(lcaseKey, v.keyDelim) + nested = len(path) > 1 + ) + + // compute the path through the nested maps to the nested value + if nested && v.isPathShadowedInDeepMap(path, castMapStringToMapInterface(v.aliases)) != "" { + v.lock.RUnlock() + return nil, nil + } + v.lock.RUnlock() + + // if the requested key is an alias, then return the proper key + lcaseKey = v.realKey(lcaseKey) + typ := v.getType(lcaseKey) + + v.lock.RLock() + defer v.lock.RUnlock() + path = strings.Split(lcaseKey, v.keyDelim) + nested = len(path) > 1 + + // Set() override first + val = v.searchMap(v.override, path) + if val != nil { + return castAllSourcesE(typ, val) + } + if nested && v.isPathShadowedInDeepMap(path, v.override) != "" { + return nil, nil + } + + // PFlag override next + flag, exists := v.pflags[lcaseKey] + if exists && flag.HasChanged() { + return castStringSourcesE(typ, flag.ValueString()) + } + if nested && v.isPathShadowedInFlatMap(path, v.pflags) != "" { + return nil, nil + } + + // Env override next + if v.automaticEnvApplied { + // even if it hasn't been registered, if automaticEnv is used, + // check any Get request + if val, ok := v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); ok { + return castStringSourcesE(typ, val) + } + if nested && v.isPathShadowedInAutoEnv(path) != "" { + return nil, nil + } + } + envkey, exists := v.env[lcaseKey] + if exists { + if val, ok := v.getEnv(envkey); ok { + return castStringSourcesE(typ, val) + } + } + if nested && v.isPathShadowedInFlatMap(path, v.env) != "" { + return nil, nil + } + + // Config file next + val = v.searchMapWithPathPrefixes(v.config, path) + if val != nil { + return castAllSourcesE(typ, val) + } + if nested && v.isPathShadowedInDeepMap(path, v.config) != "" { + return nil, nil + } + + // K/V store next + val = v.searchMap(v.kvstore, path) + if val != nil { + return castAllSourcesE(typ, val) + } + if nested && v.isPathShadowedInDeepMap(path, v.kvstore) != "" { + return nil, nil + } + + // Default next + val = v.searchMap(v.defaults, path) + if val != nil { + return castAllSourcesE(typ, val) + } + if nested && v.isPathShadowedInDeepMap(path, v.defaults) != "" { + return nil, nil + } + + if flagDefault { + // last chance: if no value is found and a flag does exist for the key, + // get the flag's default value even if the flag's value has not been set. + if flag, exists := v.pflags[lcaseKey]; exists { + return castStringSourcesE(typ, flag.ValueString()) + } + // last item, no need to check shadowing + } + + return nil, nil +} +func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} { + val, _ := v.findE(lcaseKey, flagDefault) + return val +} + +func readAsCSV(val string) ([]string, error) { + if val == "" { + return []string{}, nil + } + stringReader := strings.NewReader(val) + csvReader := csv.NewReader(stringReader) + return csvReader.Read() +} + +// IsSet checks to see if the key has been set in any of the data locations. +// IsSet is case-insensitive for a key. +func IsSet(key string) bool { return v.IsSet(key) } +func (v *Viper) IsSet(key string) bool { + lcaseKey := strings.ToLower(key) + val := v.cachedFind(lcaseKey, false) + return val != nil +} + +// AutomaticEnv has Viper check ENV variables for all. +// keys set in config, default & flags +func AutomaticEnv() { v.AutomaticEnv() } +func (v *Viper) AutomaticEnv() { + v.lock.Lock() + v.cache.Clear() + v.automaticEnvApplied = true + v.lock.Unlock() +} + +// SetEnvKeyReplacer sets the strings.Replacer on the viper object +// Useful for mapping an environmental variable to a key that does +// not match it. +func SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) } +func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) { + v.lock.Lock() + v.cache.Clear() + v.envKeyReplacer = r + v.lock.Unlock() +} + +// RegisterAlias creates an alias that provides another accessor for the same key. +// This enables one to change a name without breaking the application. +func RegisterAlias(alias string, key string) { v.RegisterAlias(alias, key) } +func (v *Viper) RegisterAlias(alias string, key string) { + v.registerAlias(alias, strings.ToLower(key)) +} + +func (v *Viper) registerAlias(alias string, key string) { + alias = strings.ToLower(alias) + if alias != key && alias != v.realKey(key) { + v.lock.RLock() + _, exists := v.aliases[alias] + v.lock.RUnlock() + + if !exists { + v.lock.Lock() + // if we alias something that exists in one of the maps to another + // name, we'll never be able to get that value using the original + // name, so move the config value to the new realkey. + if val, ok := v.config[alias]; ok { + delete(v.config, alias) + v.config[key] = val + } + if val, ok := v.kvstore[alias]; ok { + delete(v.kvstore, alias) + v.kvstore[key] = val + } + if val, ok := v.defaults[alias]; ok { + delete(v.defaults, alias) + v.defaults[key] = val + } + if val, ok := v.override[alias]; ok { + delete(v.override, alias) + v.override[key] = val + } + v.aliases[alias] = key + v.lock.Unlock() + } + } else { + jww.WARN.Println("Creating circular reference alias", alias, key, v.realKey(key)) + } +} + +func (v *Viper) realKey(key string) string { + v.lock.RLock() + newkey, exists := v.aliases[key] + v.lock.RUnlock() + + if exists { + jww.DEBUG.Println("Alias", key, "to", newkey) + return v.realKey(newkey) + } + return key +} + +// InConfig checks to see if the given key (or an alias) is in the config file. +func InConfig(key string) bool { return v.InConfig(key) } +func (v *Viper) InConfig(key string) bool { + // if the requested key is an alias, then return the proper key + key = v.realKey(key) + v.lock.RLock() + _, exists := v.config[key] + v.lock.RUnlock() + return exists +} + +func (v *Viper) setInMap(key string, value interface{}, target map[string]interface{}) { + key = v.realKey(strings.ToLower(key)) + value = toCaseInsensitiveValue(value) + + v.lock.RLock() + path := strings.Split(key, v.keyDelim) + lastKey := strings.ToLower(path[len(path)-1]) + deepestMap := deepSearch(target, path[0:len(path)-1]) + v.lock.RUnlock() + + v.lock.Lock() + v.cache.Clear() + // set innermost value + deepestMap[lastKey] = value + v.lock.Unlock() +} + +// SetDefault sets the default value for this key. +// SetDefault is case-insensitive for a key. +// Default only used when no value is provided by the user via flag, config or ENV. +func SetDefault(key string, value interface{}) { v.SetDefault(key, value) } +func (v *Viper) SetDefault(key string, value interface{}) { + v.setInMap(key, value, v.defaults) +} + +// SetType sets the type for this key. +// This type is used for type conversions, e.g. a slice from an env var +// This function allows the default to be nil while still enabling those type conversions configured +// through SetTypeByDefaultValue +func SetType(key string, t interface{}) { v.SetType(key, t) } +func (v *Viper) SetType(key string, t interface{}) { + v.setInMap(key, t, v.types) +} + +// Set sets the value for the key in the override register. +// Set is case-insensitive for a key. +// Will be used instead of values obtained via +// flags, config file, ENV, default, or key/value store. +func Set(key string, value interface{}) { v.Set(key, value) } +func (v *Viper) Set(key string, value interface{}) { + v.setInMap(key, value, v.override) +} + +// ReadInConfig will discover and load the configuration file from disk +// and key/value stores, searching in one of the defined paths. +func ReadInConfig() error { return v.ReadInConfig() } +func (v *Viper) ReadInConfig() error { + jww.INFO.Println("Attempting to read in config file") + filename, err := v.getConfigFile() + if err != nil { + return err + } + + if !stringInSlice(v.getConfigType(), SupportedExts) { + return UnsupportedConfigError(v.getConfigType()) + } + + jww.DEBUG.Println("Reading file: ", filename) + file, err := afero.ReadFile(v.fs, filename) + if err != nil { + return err + } + + config := make(map[string]interface{}) + + err = v.unmarshalReader(bytes.NewReader(file), config) + if err != nil { + return err + } + + v.lock.Lock() + v.cache.Clear() + v.config = config + v.configChangedAt = time.Now() + v.lock.Unlock() + + return nil +} + +// SetRawConfig overwrites the raw config. +func SetRawConfig(config map[string]interface{}) { v.SetRawConfig(config) } +func (v *Viper) SetRawConfig(config map[string]interface{}) { + v.lock.Lock() + defer v.lock.Unlock() + insensitiviseMap(config) + v.config = config + v.configChangedAt = time.Now() + v.cache.Clear() +} + +// MergeInConfig merges a new configuration with an existing config. +func MergeInConfig() error { return v.MergeInConfig() } +func (v *Viper) MergeInConfig() error { + jww.INFO.Println("Attempting to merge in config file") + filename, err := v.getConfigFile() + if err != nil { + return err + } + + if !stringInSlice(v.getConfigType(), SupportedExts) { + return UnsupportedConfigError(v.getConfigType()) + } + + file, err := afero.ReadFile(v.fs, filename) + if err != nil { + return err + } + + return v.MergeConfig(bytes.NewReader(file)) +} + +// ReadConfig will read a configuration file, setting existing keys to nil if the +// key does not exist in the file. +func ReadConfig(in io.Reader) error { return v.ReadConfig(in) } +func (v *Viper) ReadConfig(in io.Reader) error { + v.lock.Lock() + defer v.lock.Unlock() + v.cache.Clear() + v.config = make(map[string]interface{}) + v.configChangedAt = time.Now() + return v.unmarshalReader(in, v.config) +} + +// MergeConfig merges a new configuration with an existing config. +func MergeConfig(in io.Reader) error { return v.MergeConfig(in) } +func (v *Viper) MergeConfig(in io.Reader) error { + cfg := make(map[string]interface{}) + if err := v.unmarshalReader(in, cfg); err != nil { + return err + } + return v.MergeConfigMap(cfg) +} + +// MergeConfigMap merges the configuration from the map given with an existing config. +// Note that the map given may be modified. +func MergeConfigMap(cfg map[string]interface{}) error { return v.MergeConfigMap(cfg) } +func (v *Viper) MergeConfigMap(cfg map[string]interface{}) error { + v.lock.Lock() + v.cache.Clear() + if v.config == nil { + v.config = make(map[string]interface{}) + } + insensitiviseMap(cfg) + mergeMaps(cfg, v.config, nil) + v.configChangedAt = time.Now() + v.lock.Unlock() + return nil +} + +// WriteConfig writes the current configuration to a file. +func WriteConfig() error { return v.WriteConfig() } +func (v *Viper) WriteConfig() error { + filename, err := v.getConfigFile() + if err != nil { + return err + } + return v.writeConfig(filename, true) +} + +// SafeWriteConfig writes current configuration to file only if the file does not exist. +func SafeWriteConfig() error { return v.SafeWriteConfig() } +func (v *Viper) SafeWriteConfig() error { + if len(v.configPaths) < 1 { + return errors.New("missing configuration for 'configPath'") + } + return v.SafeWriteConfigAs(filepath.Join(v.configPaths[0], v.configName+"."+v.configType)) +} + +// WriteConfigAs writes current configuration to a given filename. +func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) } +func (v *Viper) WriteConfigAs(filename string) error { + return v.writeConfig(filename, true) +} + +// SafeWriteConfigAs writes current configuration to a given filename if it does not exist. +func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) } +func (v *Viper) SafeWriteConfigAs(filename string) error { + alreadyExists, err := afero.Exists(v.fs, filename) + if alreadyExists && err == nil { + return ConfigFileAlreadyExistsError(filename) + } + return v.writeConfig(filename, false) +} + +func (v *Viper) writeConfig(filename string, force bool) error { + jww.INFO.Println("Attempting to write configuration to file.") + var configType string + + ext := filepath.Ext(filename) + if ext != "" { + configType = ext[1:] + } else { + configType = v.configType + } + if configType == "" { + return fmt.Errorf("config type could not be determined for %s", filename) + } + + if !stringInSlice(configType, SupportedExts) { + return UnsupportedConfigError(configType) + } + if v.config == nil { + v.config = make(map[string]interface{}) + } + flags := os.O_CREATE | os.O_TRUNC | os.O_WRONLY + if !force { + flags |= os.O_EXCL + } + f, err := v.fs.OpenFile(filename, flags, v.configPermissions) + if err != nil { + return err + } + defer f.Close() + + if err := v.marshalWriter(f, configType); err != nil { + return err + } + + return f.Sync() +} + +// Unmarshal a Reader into a map. +// Should probably be an unexported function. +func unmarshalReader(in io.Reader, c map[string]interface{}) error { + return v.unmarshalReader(in, c) +} +func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error { + buf := new(bytes.Buffer) + buf.ReadFrom(in) + + switch strings.ToLower(v.getConfigType()) { + case "yaml", "yml": + if err := yaml.Unmarshal(buf.Bytes(), &c); err != nil { + return ConfigParseError{err} + } + + case "json": + if err := json.Unmarshal(buf.Bytes(), &c); err != nil { + return ConfigParseError{err} + } + + case "hcl": + obj, err := hcl.Parse(buf.String()) + if err != nil { + return ConfigParseError{err} + } + if err = hcl.DecodeObject(&c, obj); err != nil { + return ConfigParseError{err} + } + + case "toml": + tree, err := toml.LoadReader(buf) + if err != nil { + return ConfigParseError{err} + } + tmap := tree.ToMap() + for k, v := range tmap { + c[k] = v + } + + case "dotenv", "env": + env, err := gotenv.StrictParse(buf) + if err != nil { + return ConfigParseError{err} + } + for k, v := range env { + c[k] = v + } + + case "properties", "props", "prop": + v.properties = properties.NewProperties() + var err error + if v.properties, err = properties.Load(buf.Bytes(), properties.UTF8); err != nil { + return ConfigParseError{err} + } + for _, key := range v.properties.Keys() { + value, _ := v.properties.Get(key) + // recursively build nested maps + path := strings.Split(key, ".") + lastKey := strings.ToLower(path[len(path)-1]) + deepestMap := deepSearch(c, path[0:len(path)-1]) + // set innermost value + deepestMap[lastKey] = value + } + + case "ini": + cfg := ini.Empty() + err := cfg.Append(buf.Bytes()) + if err != nil { + return ConfigParseError{err} + } + sections := cfg.Sections() + for i := 0; i < len(sections); i++ { + section := sections[i] + keys := section.Keys() + for j := 0; j < len(keys); j++ { + key := keys[j] + value := cfg.Section(section.Name()).Key(key.Name()).String() + c[section.Name()+"."+key.Name()] = value + } + } + } + + insensitiviseMap(c) + return nil +} + +func (v *Viper) marshalWriter(f afero.File, configType string) error { + c := v.AllSettings() + switch configType { + case "json": + b, err := json.MarshalIndent(c, "", " ") + if err != nil { + return ConfigMarshalError{err} + } + _, err = f.WriteString(string(b)) + if err != nil { + return ConfigMarshalError{err} + } + + case "hcl": + b, err := json.Marshal(c) + if err != nil { + return ConfigMarshalError{err} + } + ast, err := hcl.Parse(string(b)) + if err != nil { + return ConfigMarshalError{err} + } + err = printer.Fprint(f, ast.Node) + if err != nil { + return ConfigMarshalError{err} + } + + case "prop", "props", "properties": + if v.properties == nil { + v.properties = properties.NewProperties() + } + p := v.properties + for _, key := range v.AllKeys() { + _, _, err := p.Set(key, v.GetString(key)) + if err != nil { + return ConfigMarshalError{err} + } + } + _, err := p.WriteComment(f, "#", properties.UTF8) + if err != nil { + return ConfigMarshalError{err} + } + + case "dotenv", "env": + lines := []string{} + for _, key := range v.AllKeys() { + envName := strings.ToUpper(strings.Replace(key, ".", "_", -1)) + val := v.Get(key) + lines = append(lines, fmt.Sprintf("%v=%v", envName, val)) + } + s := strings.Join(lines, "\n") + if _, err := f.WriteString(s); err != nil { + return ConfigMarshalError{err} + } + + case "toml": + t, err := toml.TreeFromMap(c) + if err != nil { + return ConfigMarshalError{err} + } + s := t.String() + if _, err := f.WriteString(s); err != nil { + return ConfigMarshalError{err} + } + + case "yaml", "yml": + b, err := yaml.Marshal(c) + if err != nil { + return ConfigMarshalError{err} + } + if _, err = f.WriteString(string(b)); err != nil { + return ConfigMarshalError{err} + } + + case "ini": + keys := v.AllKeys() + cfg := ini.Empty() + ini.PrettyFormat = false + for i := 0; i < len(keys); i++ { + key := keys[i] + lastSep := strings.LastIndex(key, ".") + sectionName := key[:(lastSep)] + keyName := key[(lastSep + 1):] + if sectionName == "default" { + sectionName = "" + } + cfg.Section(sectionName).Key(keyName).SetValue(Get(key).(string)) + } + cfg.WriteTo(f) + } + return nil +} + +func keyExists(k string, m map[string]interface{}) string { + lk := strings.ToLower(k) + for mk := range m { + lmk := strings.ToLower(mk) + if lmk == lk { + return mk + } + } + return "" +} + +func castToMapStringInterface( + src map[interface{}]interface{}) map[string]interface{} { + tgt := map[string]interface{}{} + for k, v := range src { + tgt[fmt.Sprintf("%v", k)] = v + } + return tgt +} + +func castMapStringToMapInterface(src map[string]string) map[string]interface{} { + tgt := map[string]interface{}{} + for k, v := range src { + tgt[k] = v + } + return tgt +} + +func castMapFlagToMapInterface(src map[string]FlagValue) map[string]interface{} { + tgt := map[string]interface{}{} + for k, v := range src { + tgt[k] = v + } + return tgt +} + +// mergeMaps merges two maps. The `itgt` parameter is for handling go-yaml's +// insistence on parsing nested structures as `map[interface{}]interface{}` +// instead of using a `string` as the key for nest structures beyond one level +// deep. Both map types are supported as there is a go-yaml fork that uses +// `map[string]interface{}` instead. +func mergeMaps( + src, tgt map[string]interface{}, itgt map[interface{}]interface{}) { + for sk, sv := range src { + tk := keyExists(sk, tgt) + if tk == "" { + jww.TRACE.Printf("tk=\"\", tgt[%s]=%v", sk, sv) + tgt[sk] = sv + if itgt != nil { + itgt[sk] = sv + } + continue + } + + tv, ok := tgt[tk] + if !ok { + jww.TRACE.Printf("tgt[%s] != ok, tgt[%s]=%v", tk, sk, sv) + tgt[sk] = sv + if itgt != nil { + itgt[sk] = sv + } + continue + } + + svType := reflect.TypeOf(sv) + tvType := reflect.TypeOf(tv) + if svType != tvType { + jww.ERROR.Printf( + "svType != tvType; key=%s, st=%v, tt=%v, sv=%v, tv=%v", + sk, svType, tvType, sv, tv) + continue + } + + jww.TRACE.Printf("processing key=%s, st=%v, tt=%v, sv=%v, tv=%v", + sk, svType, tvType, sv, tv) + + switch ttv := tv.(type) { + case map[interface{}]interface{}: + jww.TRACE.Printf("merging maps (must convert)") + tsv := sv.(map[interface{}]interface{}) + ssv := castToMapStringInterface(tsv) + stv := castToMapStringInterface(ttv) + mergeMaps(ssv, stv, ttv) + case map[string]interface{}: + jww.TRACE.Printf("merging maps") + mergeMaps(sv.(map[string]interface{}), ttv, nil) + default: + jww.TRACE.Printf("setting value") + tgt[tk] = sv + if itgt != nil { + itgt[tk] = sv + } + } + } +} + +// ReadRemoteConfig attempts to get configuration from a remote source +// and read it in the remote configuration registry. +func ReadRemoteConfig() error { return v.ReadRemoteConfig() } +func (v *Viper) ReadRemoteConfig() error { + return v.getKeyValueConfig() +} + +func WatchRemoteConfig() error { return v.WatchRemoteConfig() } +func (v *Viper) WatchRemoteConfig() error { + return v.watchKeyValueConfig() +} + +func (v *Viper) WatchRemoteConfigOnChannel() error { + return v.watchKeyValueConfigOnChannel() +} + +// Retrieve the first found remote configuration. +func (v *Viper) getKeyValueConfig() error { + if RemoteConfig == nil { + return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/ory/viper/remote'") + } + + v.lock.RLock() + for _, rp := range v.remoteProviders { + val, err := v.getRemoteConfig(rp) + if err != nil { + continue + } + v.lock.RUnlock() + v.lock.Lock() + v.kvstore = val + v.lock.Unlock() + return nil + } + v.lock.RUnlock() + return RemoteConfigError("No Files Found") +} + +func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]interface{}, error) { + reader, err := RemoteConfig.Get(provider) + if err != nil { + return nil, err + } + v.lock.RLock() + err = v.unmarshalReader(reader, v.kvstore) + v.lock.RUnlock() + return v.kvstore, err +} + +// Retrieve the first found remote configuration. +func (v *Viper) watchKeyValueConfigOnChannel() error { + for _, rp := range v.remoteProviders { + respc, _ := RemoteConfig.WatchChannel(rp) + // Todo: Add quit channel + go func(rc <-chan *RemoteResponse) { + for { + b := <-rc + reader := bytes.NewReader(b.Value) + v.unmarshalReader(reader, v.kvstore) + } + }(respc) + return nil + } + return RemoteConfigError("No Files Found") +} + +// Retrieve the first found remote configuration. +func (v *Viper) watchKeyValueConfig() error { + for _, rp := range v.remoteProviders { + val, err := v.watchRemoteConfig(rp) + if err != nil { + continue + } + v.kvstore = val + return nil + } + return RemoteConfigError("No Files Found") +} + +func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]interface{}, error) { + reader, err := RemoteConfig.Watch(provider) + if err != nil { + return nil, err + } + err = v.unmarshalReader(reader, v.kvstore) + return v.kvstore, err +} + +// AllKeys returns all keys holding a value, regardless of where they are set. +// Nested keys are returned with a v.keyDelim separator +func AllKeys() []string { return v.AllKeys() } +func (v *Viper) AllKeys() []string { + v.lock.RLock() + defer v.lock.RUnlock() + + m := map[string]bool{} + // add all paths, by order of descending priority to ensure correct shadowing + m = v.flattenAndMergeMap(m, castMapStringToMapInterface(v.aliases), "") + m = v.flattenAndMergeMap(m, v.override, "") + m = v.mergeFlatMap(m, castMapFlagToMapInterface(v.pflags)) + m = v.mergeFlatMap(m, castMapStringToMapInterface(v.env)) + m = v.flattenAndMergeMap(m, v.config, "") + m = v.flattenAndMergeMap(m, v.kvstore, "") + m = v.flattenAndMergeMap(m, v.defaults, "") + + // convert set of paths to list + a := make([]string, 0, len(m)) + for x := range m { + a = append(a, x) + } + return a +} + +// flattenAndMergeMap recursively flattens the given map into a map[string]bool +// of key paths (used as a set, easier to manipulate than a []string): +// - each path is merged into a single key string, delimited with v.keyDelim +// - if a path is shadowed by an earlier value in the initial shadow map, +// it is skipped. +// The resulting set of paths is merged to the given shadow set at the same time. +func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interface{}, prefix string) map[string]bool { + if shadow != nil && prefix != "" && shadow[prefix] { + // prefix is shadowed => nothing more to flatten + return shadow + } + if shadow == nil { + shadow = make(map[string]bool) + } + + var m2 map[string]interface{} + if prefix != "" { + prefix += v.keyDelim + } + for k, val := range m { + fullKey := prefix + k + switch val.(type) { + case map[string]interface{}: + m2 = val.(map[string]interface{}) + case map[interface{}]interface{}: + m2 = cast.ToStringMap(val) + default: + // immediate value + shadow[strings.ToLower(fullKey)] = true + continue + } + // recursively merge to shadow map + shadow = v.flattenAndMergeMap(shadow, m2, fullKey) + } + return shadow +} + +// mergeFlatMap merges the given maps, excluding values of the second map +// shadowed by values from the first map. +func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]interface{}) map[string]bool { + // scan keys +outer: + for k := range m { + path := strings.Split(k, v.keyDelim) + // scan intermediate paths + var parentKey string + for i := 1; i < len(path); i++ { + parentKey = strings.Join(path[0:i], v.keyDelim) + if shadow[parentKey] { + // path is shadowed, continue + continue outer + } + } + // add key + shadow[strings.ToLower(k)] = true + } + return shadow +} + +// AllSettings merges all settings and returns them as a map[string]interface{}. +func AllSettingsE() (map[string]interface{}, error) { return v.AllSettingsE() } +func (v *Viper) AllSettingsE() (m map[string]interface{}, lastErr error) { + m = map[string]interface{}{} + // start from the list of keys, and construct the map one value at a time + for _, k := range v.AllKeys() { + value, err := v.GetE(k) + if err != nil { + // set last err but still continue as we still want to compute the result with the zero value for this specific key + lastErr = err + } + if value == nil { + // should not happen, since AllKeys() returns only keys holding a value, + // check just in case anything changes + continue + } + path := strings.Split(k, v.keyDelim) + lastKey := strings.ToLower(path[len(path)-1]) + deepestMap := deepSearch(m, path[0:len(path)-1]) + // set innermost value + deepestMap[lastKey] = value + } + m = toMapStringInterface(m).(map[string]interface{}) // This is safe because the input is map[string]interface{} + return +} + +func AllSettings() map[string]interface{} { return v.AllSettings() } +func (v *Viper) AllSettings() map[string]interface{} { + m, _ := v.AllSettingsE() + return m +} + +// SetFs sets the filesystem to use to read configuration. +func SetFs(fs afero.Fs) { v.SetFs(fs) } +func (v *Viper) SetFs(fs afero.Fs) { + v.lock.Lock() + v.fs = fs + v.lock.Unlock() +} + +// SetConfigName sets name for the config file. +// Does not include extension. +func SetConfigName(in string) { v.SetConfigName(in) } +func (v *Viper) SetConfigName(in string) { + if in != "" { + v.lock.Lock() + v.configName = in + v.configFile = "" + v.lock.Unlock() + } +} + +// SetConfigType sets the type of the configuration returned by the +// remote source, e.g. "json". +func SetConfigType(in string) { v.SetConfigType(in) } +func (v *Viper) SetConfigType(in string) { + if in != "" { + v.lock.Lock() + v.configType = in + v.lock.Unlock() + } +} + +// SetConfigPermissions sets the permissions for the config file. +func SetConfigPermissions(perm os.FileMode) { v.SetConfigPermissions(perm) } +func (v *Viper) SetConfigPermissions(perm os.FileMode) { + v.lock.Lock() + v.configPermissions = perm.Perm() + v.lock.Unlock() +} + +func (v *Viper) getConfigType() string { + if v.configType != "" { + return v.configType + } + + cf, err := v.getConfigFile() + if err != nil { + return "" + } + + ext := filepath.Ext(cf) + + if len(ext) > 1 { + return ext[1:] + } + + return "" +} + +func (v *Viper) getConfigFile() (string, error) { + if v.configFile == "" { + cf, err := v.findConfigFile() + if err != nil { + return "", err + } + v.configFile = cf + } + return v.configFile, nil +} + +func (v *Viper) searchInPath(in string) (filename string) { + jww.DEBUG.Println("Searching for config in ", in) + for _, ext := range SupportedExts { + jww.DEBUG.Println("Checking for", filepath.Join(in, v.configName+"."+ext)) + if b, _ := exists(v.fs, filepath.Join(in, v.configName+"."+ext)); b { + jww.DEBUG.Println("Found: ", filepath.Join(in, v.configName+"."+ext)) + return filepath.Join(in, v.configName+"."+ext) + } + } + + if v.configType != "" { + if b, _ := exists(v.fs, filepath.Join(in, v.configName)); b { + return filepath.Join(in, v.configName) + } + } + + return "" +} + +// Search all configPaths for any config file. +// Returns the first path that exists (and is a config file). +func (v *Viper) findConfigFile() (string, error) { + jww.INFO.Println("Searching for config in ", v.configPaths) + + for _, cp := range v.configPaths { + file := v.searchInPath(cp) + if file != "" { + return file, nil + } + } + return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)} +} + +// Debug prints all configuration registries for debugging +// purposes. +func Debug() { v.Debug() } +func (v *Viper) Debug() { + v.lock.RLock() + defer v.lock.RUnlock() + fmt.Printf("Aliases:\n%#v\n", v.aliases) + fmt.Printf("Override:\n%#v\n", v.override) + fmt.Printf("PFlags:\n%#v\n", v.pflags) + fmt.Printf("Env:\n%#v\n", v.env) + fmt.Printf("Key/Value Store:\n%#v\n", v.kvstore) + fmt.Printf("Config:\n%#v\n", v.config) + fmt.Printf("Defaults:\n%#v\n", v.defaults) +} diff --git a/vendor/github.com/rivo/tview/CODE_OF_CONDUCT.md b/vendor/github.com/rivo/tview/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..601e63b83a --- /dev/null +++ b/vendor/github.com/rivo/tview/CODE_OF_CONDUCT.md @@ -0,0 +1,73 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, race, +religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at https://rentafounder.com/page/about-me/. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org diff --git a/vendor/github.com/rivo/tview/CONTRIBUTING.md b/vendor/github.com/rivo/tview/CONTRIBUTING.md new file mode 100644 index 0000000000..92f6886d8c --- /dev/null +++ b/vendor/github.com/rivo/tview/CONTRIBUTING.md @@ -0,0 +1,35 @@ +# Contributing to tview + +First of all, thank you for taking the time to contribute. + +The following provides you with some guidance on how to contribute to this project. Mainly, it is meant to save us all some time so please read it, it's not long. + +Please note that this document is work in progress so I might add to it in the future. + +## Issues + +- Please include enough information so everybody understands your request. +- Screenshots or code that illustrates your point always helps. +- It's fine to ask for help. But you should have checked out the [documentation](https://godoc.org/github.com/rivo/tview) first in any case. +- If you request a new feature, state your motivation and share a use case that you faced where you needed that new feature. It should be something that others will also need. + +## Pull Requests + +In my limited time I can spend on this project, I will always go through issues first before looking at pull requests. It takes a _lot_ of time to look at code that you submitted and I may not have that time. So be prepared to have your pull requests lying around for a long time. + +Therefore, if you have a feature request, open an issue first before sending me a pull request, and allow for some discussion. It may save you from writing code that will get rejected. If your case is strong, there is a good chance that I will add the feature for you. + +I'm very picky about the code that goes into this repo. So if you violate any of the following guidelines, there is a good chance I won't merge your pull request. + +- There must be a strong case for your additions/changes, such as: + - Bug fixes + - Features that are needed (see "Issues" above; state your motivation) + - Improvements in stability or performance (if readability does not suffer) +- Your code must follow the structure of the existing code. Don't just patch something on. Try to understand how `tview` is currently designed and follow that design. Your code needs to be consistent with existing code. +- If you're adding code that increases the work required to maintain the project, you must be willing to take responsibility for that extra work. I will ask you to maintain your part of the code in the long run. +- Function/type/variable/constant names must be as descriptive as they are right now. Follow the conventions of the package. +- All functions/types/variables/constants, even private ones, must have comments in good English. These comments must be elaborate enough so that new users of the package understand them and can follow them. Provide examples if you have to. Start all sentences upper-case, as is common in English, and end them with a period. +- A new function should be located close to related functions in the file. For example, `GetColor()` should come after (or before) `SetColor()`. +- Your changes must not decrease the project's [Go Report](https://goreportcard.com/report/github.com/rivo/tview) rating. +- No breaking changes unless there is absolutely no other way. +- If an issue accompanies your pull request, reference it in the PR's comments, e.g. "Fixes #123", so it is closed automatically when the PR is closed. diff --git a/vendor/github.com/rivo/tview/LICENSE.txt b/vendor/github.com/rivo/tview/LICENSE.txt new file mode 100644 index 0000000000..9d6943073c --- /dev/null +++ b/vendor/github.com/rivo/tview/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Oliver Kuederle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/rivo/tview/README.md b/vendor/github.com/rivo/tview/README.md new file mode 100644 index 0000000000..ab1b5218a6 --- /dev/null +++ b/vendor/github.com/rivo/tview/README.md @@ -0,0 +1,116 @@ +# Rich Interactive Widgets for Terminal UIs + +[![PkgGoDev](https://pkg.go.dev/badge/github.com/rivo/tview)](https://pkg.go.dev/github.com/rivo/tview) +[![Go Report](https://img.shields.io/badge/go%20report-A%2B-brightgreen.svg)](https://goreportcard.com/report/github.com/rivo/tview) + +This Go package provides commonly needed components for terminal based user interfaces. + +![Screenshot](tview.gif) + +Among these components are: + +- __Input forms__ (include __input/password fields__, __drop-down selections__, __checkboxes__, and __buttons__) +- Navigable multi-color __text views__ +- Sophisticated navigable __table views__ +- Flexible __tree views__ +- Selectable __lists__ +- __Grid__, __Flexbox__ and __page layouts__ +- Modal __message windows__ +- An __application__ wrapper + +They come with lots of customization options and can be easily extended to fit your needs. + +## Installation + +```bash +go get github.com/rivo/tview +``` + +## Hello World + +This basic example creates a box titled "Hello, World!" and displays it in your terminal: + +```go +package main + +import ( + "github.com/rivo/tview" +) + +func main() { + box := tview.NewBox().SetBorder(true).SetTitle("Hello, world!") + if err := tview.NewApplication().SetRoot(box, true).Run(); err != nil { + panic(err) + } +} +``` + +Check out the [GitHub Wiki](https://github.com/rivo/tview/wiki) for more examples along with screenshots. Or try the examples in the "demos" subdirectory. + +For a presentation highlighting this package, compile and run the program found in the "demos/presentation" subdirectory. + +## Projects using `tview` + +- [IRCCloud Terminal Client](https://github.com/termoose/irccloud) +- [Window manager for `tview`](https://github.com/epiclabs-io/winman) +- [Password manager](https://github.com/7onetella/password) +- [CLI bookmark manager](https://github.com/Endi1/drawer) +- [A caving database interface written in Go](https://github.com/IdlePhysicist/cave-logger) +- [App for rental of electic bikes](https://github.com/MrDienns/bike-commerce) +- [Interactive file browse and exec any command.](https://github.com/bannzai/itree) +- [A simple CRM](https://github.com/broadcastle/crm) +- [Terminal UI for todist](https://github.com/cyberdummy/todoista) +- [Graphical kubectl wrapper](https://github.com/dcaiafa/kpick) +- [Decred Decentralized Exchange ](https://github.com/decred/dcrdex) +- [Kubernetes CLI To Manage Your Clusters In Style! ](https://github.com/derailed/k9s) +- [A CLI file browser for Raspberry PI](https://github.com/destinmoulton/pixi) +- [A tool to manage projects.](https://github.com/divramod/dp) +- [A simple app for BMI monitoring](https://github.com/erleene/go-bmi) +- [Stream TIDAL from command line](https://github.com/godsic/vibe) +- [Secure solution for fully decentralized password management](https://github.com/guillaumemichel/passtor/) +- [A growing collection of convenient little tools to work with systemd services](https://github.com/muesli/service-tools/) +- [A terminal based browser for Redis written in Go](https://github.com/nitishm/redis-terminal) +- [First project for the Computer Networks course.](https://github.com/pablogadhi/XMPPClient) +- [Test your typing speed in the terminal!](https://github.com/shilangyu/typer-go) +- [TUI Client for Docker](https://github.com/skanehira/docui) +- [SSH client using certificates signed by HashiCorp Vault](https://github.com/stephane-martin/vssh) +- [A go terminal based pos software.](https://github.com/thebmw/y2kpos) +- [VMware vCenter Text UI](https://github.com/thebsdbox/vctui) +- [Bookmarks on terminal](https://github.com/tryffel/bookmarker) +- [A UDP testing utility](https://github.com/vaelen/udp-tester) +- [A simple Kanban board for your terminal](https://github.com/witchard/toukan) +- [The personal information dashboard for your terminal. ](https://github.com/wtfutil/wtf) +- [MySQL database to Golang struct](https://github.com/xxjwxc/gormt) +- [Cryptowatch Go SDK](https://github.com/y3sh/cw-sdk-go) +- [Discord, TUI and SIXEL.](https://gitlab.com/diamondburned/6cord) +- [A CLI Audio Player](https://www.github.com/dhulihan/grump) +- [GLab, a GitLab CLI tool](https://gitlab.com/profclems/glab) +- [Browse your AWS ECS Clusters in the Terminal](https://github.com/swartzrock/ecsview) +- [The CLI Task Manager for Geeks](https://github.com/ajaxray/geek-life) +- [Fast disk usage analyzer written in Go](https://github.com/dundee/gdu) +- [Multiplayer Chess On Terminal](https://github.com/qnkhuat/gochess) +- [Scriptable TUI music player](https://github.com/issadarkthing/gomu) +- [MangaDesk : TUI Client for downloading manga to your computer](https://github.com/darylhjd/mangadesk) +- [Go How Much? a Crypto coin price tracking from terminal](https://github.com/ledongthuc/gohowmuch) +- [dbui: Universal CLI for Database Connections](https://github.com/KenanBek/dbui) +- [ssmbrowse: Simple and elegant cli AWS SSM parameter browser](https://github.com/bnaydenov/ssmbrowse) + +## Documentation + +Refer to https://pkg.go.dev/github.com/rivo/tview for the package's documentation. Also check out the [Wiki](https://github.com/rivo/tview/wiki). + +## Dependencies + +This package is based on [github.com/gdamore/tcell](https://github.com/gdamore/tcell) (and its dependencies) as well as on [github.com/rivo/uniseg](https://github.com/rivo/uniseg). + +## Versioning and Backwards-Compatibility + +I try really hard to keep this project backwards compatible. Your software should not break when you upgrade `tview`. But this also means that some of its shortcomings that were present in the initial versions will remain. In addition, at least for the time being, you won't find any version tags in this repo. The newest version should be the one to upgrade to. It has all the bugfixes and latest features. Having said that, backwards compatibility may still break when: + +- a new version of an imported package (most likely [`tcell`](https://github.com/gdamore/tcell)) changes in such a way that forces me to make changes in `tview` as well, +- I fix something that I consider a bug, rather than a feature, something that does not work as originally intended, +- I make changes to "internal" interfaces such as [`Primitive`](https://pkg.go.dev/github.com/rivo/tview#Primitive). You shouldn't need these interfaces unless you're writing your own primitives for `tview`. (Yes, I realize these are public interfaces. This has advantages as well as disadvantages. For the time being, it is what it is.) + +## Your Feedback + +Add your issue here on GitHub. Feel free to get in touch if you have any questions. diff --git a/vendor/github.com/rivo/tview/ansi.go b/vendor/github.com/rivo/tview/ansi.go new file mode 100644 index 0000000000..b63b478ce6 --- /dev/null +++ b/vendor/github.com/rivo/tview/ansi.go @@ -0,0 +1,258 @@ +package tview + +import ( + "bytes" + "fmt" + "io" + "strconv" + "strings" +) + +// The states of the ANSI escape code parser. +const ( + ansiText = iota + ansiEscape + ansiSubstring + ansiControlSequence +) + +// ansi is a io.Writer which translates ANSI escape codes into tview color +// tags. +type ansi struct { + io.Writer + + // Reusable buffers. + buffer *bytes.Buffer // The entire output text of one Write(). + csiParameter, csiIntermediate *bytes.Buffer // Partial CSI strings. + attributes string // The buffer's current text attributes (a tview attribute string). + + // The current state of the parser. One of the ansi constants. + state int +} + +// ANSIWriter returns an io.Writer which translates any ANSI escape codes +// written to it into tview color tags. Other escape codes don't have an effect +// and are simply removed. The translated text is written to the provided +// writer. +func ANSIWriter(writer io.Writer) io.Writer { + return &ansi{ + Writer: writer, + buffer: new(bytes.Buffer), + csiParameter: new(bytes.Buffer), + csiIntermediate: new(bytes.Buffer), + state: ansiText, + } +} + +// Write parses the given text as a string of runes, translates ANSI escape +// codes to color tags and writes them to the output writer. +func (a *ansi) Write(text []byte) (int, error) { + defer func() { + a.buffer.Reset() + }() + + for _, r := range string(text) { + switch a.state { + + // We just entered an escape sequence. + case ansiEscape: + switch r { + case '[': // Control Sequence Introducer. + a.csiParameter.Reset() + a.csiIntermediate.Reset() + a.state = ansiControlSequence + case 'c': // Reset. + fmt.Fprint(a.buffer, "[-:-:-]") + a.state = ansiText + case 'P', ']', 'X', '^', '_': // Substrings and commands. + a.state = ansiSubstring + default: // Ignore. + a.state = ansiText + } + + // CSI Sequences. + case ansiControlSequence: + switch { + case r >= 0x30 && r <= 0x3f: // Parameter bytes. + if _, err := a.csiParameter.WriteRune(r); err != nil { + return 0, err + } + case r >= 0x20 && r <= 0x2f: // Intermediate bytes. + if _, err := a.csiIntermediate.WriteRune(r); err != nil { + return 0, err + } + case r >= 0x40 && r <= 0x7e: // Final byte. + switch r { + case 'E': // Next line. + count, _ := strconv.Atoi(a.csiParameter.String()) + if count == 0 { + count = 1 + } + fmt.Fprint(a.buffer, strings.Repeat("\n", count)) + case 'm': // Select Graphic Rendition. + var background, foreground string + params := a.csiParameter.String() + fields := strings.Split(params, ";") + if len(params) == 0 || len(fields) == 1 && fields[0] == "0" { + // Reset. + a.attributes = "" + if _, err := a.buffer.WriteString("[-:-:-]"); err != nil { + return 0, err + } + break + } + lookupColor := func(colorNumber int) string { + if colorNumber < 0 || colorNumber > 15 { + return "black" + } + return []string{ + "black", + "maroon", + "green", + "olive", + "navy", + "purple", + "teal", + "silver", + "gray", + "red", + "lime", + "yellow", + "blue", + "fuchsia", + "aqua", + "white", + }[colorNumber] + } + FieldLoop: + for index, field := range fields { + switch field { + case "1", "01": + if !strings.ContainsRune(a.attributes, 'b') { + a.attributes += "b" + } + case "2", "02": + if !strings.ContainsRune(a.attributes, 'd') { + a.attributes += "d" + } + case "4", "04": + if !strings.ContainsRune(a.attributes, 'u') { + a.attributes += "u" + } + case "5", "05": + if !strings.ContainsRune(a.attributes, 'l') { + a.attributes += "l" + } + case "22": + if i := strings.IndexRune(a.attributes, 'b'); i >= 0 { + a.attributes = a.attributes[:i] + a.attributes[i+1:] + } + if i := strings.IndexRune(a.attributes, 'd'); i >= 0 { + a.attributes = a.attributes[:i] + a.attributes[i+1:] + } + case "24": + if i := strings.IndexRune(a.attributes, 'u'); i >= 0 { + a.attributes = a.attributes[:i] + a.attributes[i+1:] + } + case "25": + if i := strings.IndexRune(a.attributes, 'l'); i >= 0 { + a.attributes = a.attributes[:i] + a.attributes[i+1:] + } + case "30", "31", "32", "33", "34", "35", "36", "37": + colorNumber, _ := strconv.Atoi(field) + foreground = lookupColor(colorNumber - 30) + case "39": + foreground = "-" + case "40", "41", "42", "43", "44", "45", "46", "47": + colorNumber, _ := strconv.Atoi(field) + background = lookupColor(colorNumber - 40) + case "49": + background = "-" + case "90", "91", "92", "93", "94", "95", "96", "97": + colorNumber, _ := strconv.Atoi(field) + foreground = lookupColor(colorNumber - 82) + case "100", "101", "102", "103", "104", "105", "106", "107": + colorNumber, _ := strconv.Atoi(field) + background = lookupColor(colorNumber - 92) + case "38", "48": + var color string + if len(fields) > index+1 { + if fields[index+1] == "5" && len(fields) > index+2 { // 8-bit colors. + colorNumber, _ := strconv.Atoi(fields[index+2]) + if colorNumber <= 15 { + color = lookupColor(colorNumber) + } else if colorNumber <= 231 { + red := (colorNumber - 16) / 36 + green := ((colorNumber - 16) / 6) % 6 + blue := (colorNumber - 16) % 6 + color = fmt.Sprintf("#%02x%02x%02x", 255*red/5, 255*green/5, 255*blue/5) + } else if colorNumber <= 255 { + grey := 255 * (colorNumber - 232) / 23 + color = fmt.Sprintf("#%02x%02x%02x", grey, grey, grey) + } + } else if fields[index+1] == "2" && len(fields) > index+4 { // 24-bit colors. + red, _ := strconv.Atoi(fields[index+2]) + green, _ := strconv.Atoi(fields[index+3]) + blue, _ := strconv.Atoi(fields[index+4]) + color = fmt.Sprintf("#%02x%02x%02x", red, green, blue) + } + } + if len(color) > 0 { + if field == "38" { + foreground = color + } else { + background = color + } + } + break FieldLoop + } + } + var colon string + if len(a.attributes) > 0 { + colon = ":" + } + if len(foreground) > 0 || len(background) > 0 || len(a.attributes) > 0 { + fmt.Fprintf(a.buffer, "[%s:%s%s%s]", foreground, background, colon, a.attributes) + } + } + a.state = ansiText + default: // Undefined byte. + a.state = ansiText // Abort CSI. + } + + // We just entered a substring/command sequence. + case ansiSubstring: + if r == 27 { // Most likely the end of the substring. + a.state = ansiEscape + } // Ignore all other characters. + + // "ansiText" and all others. + default: + if r == 27 { + // This is the start of an escape sequence. + a.state = ansiEscape + } else { + // Just a regular rune. Send to buffer. + if _, err := a.buffer.WriteRune(r); err != nil { + return 0, err + } + } + } + } + + // Write buffer to target writer. + n, err := a.buffer.WriteTo(a.Writer) + if err != nil { + return int(n), err + } + return len(text), nil +} + +// TranslateANSI replaces ANSI escape sequences found in the provided string +// with tview's color tags and returns the resulting string. +func TranslateANSI(text string) string { + var buffer bytes.Buffer + writer := ANSIWriter(&buffer) + writer.Write([]byte(text)) + return buffer.String() +} diff --git a/vendor/github.com/rivo/tview/application.go b/vendor/github.com/rivo/tview/application.go new file mode 100644 index 0000000000..2bc0646664 --- /dev/null +++ b/vendor/github.com/rivo/tview/application.go @@ -0,0 +1,766 @@ +package tview + +import ( + "sync" + "time" + + "github.com/gdamore/tcell/v2" +) + +const ( + // The size of the event/update/redraw channels. + queueSize = 100 + + // The minimum time between two consecutive redraws. + redrawPause = 50 * time.Millisecond +) + +// DoubleClickInterval specifies the maximum time between clicks to register a +// double click rather than click. +var DoubleClickInterval = 500 * time.Millisecond + +// MouseAction indicates one of the actions the mouse is logically doing. +type MouseAction int16 + +// Available mouse actions. +const ( + MouseMove MouseAction = iota + MouseLeftDown + MouseLeftUp + MouseLeftClick + MouseLeftDoubleClick + MouseMiddleDown + MouseMiddleUp + MouseMiddleClick + MouseMiddleDoubleClick + MouseRightDown + MouseRightUp + MouseRightClick + MouseRightDoubleClick + MouseScrollUp + MouseScrollDown + MouseScrollLeft + MouseScrollRight +) + +// queuedUpdate represented the execution of f queued by +// Application.QueueUpdate(). If "done" is not nil, it receives exactly one +// element after f has executed. +type queuedUpdate struct { + f func() + done chan struct{} +} + +// Application represents the top node of an application. +// +// It is not strictly required to use this class as none of the other classes +// depend on it. However, it provides useful tools to set up an application and +// plays nicely with all widgets. +// +// The following command displays a primitive p on the screen until Ctrl-C is +// pressed: +// +// if err := tview.NewApplication().SetRoot(p, true).Run(); err != nil { +// panic(err) +// } +type Application struct { + sync.RWMutex + + // The application's screen. Apart from Run(), this variable should never be + // set directly. Always use the screenReplacement channel after calling + // Fini(), to set a new screen (or nil to stop the application). + screen tcell.Screen + + // The primitive which currently has the keyboard focus. + focus Primitive + + // The root primitive to be seen on the screen. + root Primitive + + // Whether or not the application resizes the root primitive. + rootFullscreen bool + + // Set to true if mouse events are enabled. + enableMouse bool + + // An optional capture function which receives a key event and returns the + // event to be forwarded to the default input handler (nil if nothing should + // be forwarded). + inputCapture func(event *tcell.EventKey) *tcell.EventKey + + // An optional callback function which is invoked just before the root + // primitive is drawn. + beforeDraw func(screen tcell.Screen) bool + + // An optional callback function which is invoked after the root primitive + // was drawn. + afterDraw func(screen tcell.Screen) + + // Used to send screen events from separate goroutine to main event loop + events chan tcell.Event + + // Functions queued from goroutines, used to serialize updates to primitives. + updates chan queuedUpdate + + // An object that the screen variable will be set to after Fini() was called. + // Use this channel to set a new screen object for the application + // (screen.Init() and draw() will be called implicitly). A value of nil will + // stop the application. + screenReplacement chan tcell.Screen + + // An optional capture function which receives a mouse event and returns the + // event to be forwarded to the default mouse handler (nil if nothing should + // be forwarded). + mouseCapture func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction) + + mouseCapturingPrimitive Primitive // A Primitive returned by a MouseHandler which will capture future mouse events. + lastMouseX, lastMouseY int // The last position of the mouse. + mouseDownX, mouseDownY int // The position of the mouse when its button was last pressed. + lastMouseClick time.Time // The time when a mouse button was last clicked. + lastMouseButtons tcell.ButtonMask // The last mouse button state. +} + +// NewApplication creates and returns a new application. +func NewApplication() *Application { + return &Application{ + events: make(chan tcell.Event, queueSize), + updates: make(chan queuedUpdate, queueSize), + screenReplacement: make(chan tcell.Screen, 1), + } +} + +// SetInputCapture sets a function which captures all key events before they are +// forwarded to the key event handler of the primitive which currently has +// focus. This function can then choose to forward that key event (or a +// different one) by returning it or stop the key event processing by returning +// nil. +// +// Note that this also affects the default event handling of the application +// itself: Such a handler can intercept the Ctrl-C event which closes the +// application. +func (a *Application) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Application { + a.inputCapture = capture + return a +} + +// GetInputCapture returns the function installed with SetInputCapture() or nil +// if no such function has been installed. +func (a *Application) GetInputCapture() func(event *tcell.EventKey) *tcell.EventKey { + return a.inputCapture +} + +// SetMouseCapture sets a function which captures mouse events (consisting of +// the original tcell mouse event and the semantic mouse action) before they are +// forwarded to the appropriate mouse event handler. This function can then +// choose to forward that event (or a different one) by returning it or stop +// the event processing by returning a nil mouse event. +func (a *Application) SetMouseCapture(capture func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction)) *Application { + a.mouseCapture = capture + return a +} + +// GetMouseCapture returns the function installed with SetMouseCapture() or nil +// if no such function has been installed. +func (a *Application) GetMouseCapture() func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction) { + return a.mouseCapture +} + +// SetScreen allows you to provide your own tcell.Screen object. For most +// applications, this is not needed and you should be familiar with +// tcell.Screen when using this function. +// +// This function is typically called before the first call to Run(). Init() need +// not be called on the screen. +func (a *Application) SetScreen(screen tcell.Screen) *Application { + if screen == nil { + return a // Invalid input. Do nothing. + } + + a.Lock() + if a.screen == nil { + // Run() has not been called yet. + a.screen = screen + a.Unlock() + return a + } + + // Run() is already in progress. Exchange screen. + oldScreen := a.screen + a.Unlock() + oldScreen.Fini() + a.screenReplacement <- screen + + return a +} + +// EnableMouse enables mouse events or disables them (if "false" is provided). +func (a *Application) EnableMouse(enable bool) *Application { + a.Lock() + defer a.Unlock() + if enable != a.enableMouse && a.screen != nil { + if enable { + a.screen.EnableMouse() + } else { + a.screen.DisableMouse() + } + } + a.enableMouse = enable + return a +} + +// Run starts the application and thus the event loop. This function returns +// when Stop() was called. +func (a *Application) Run() error { + var ( + err, appErr error + lastRedraw time.Time // The time the screen was last redrawn. + redrawTimer *time.Timer // A timer to schedule the next redraw. + ) + a.Lock() + + // Make a screen if there is none yet. + if a.screen == nil { + a.screen, err = tcell.NewScreen() + if err != nil { + a.Unlock() + return err + } + if err = a.screen.Init(); err != nil { + a.Unlock() + return err + } + if a.enableMouse { + a.screen.EnableMouse() + } + } + + // We catch panics to clean up because they mess up the terminal. + defer func() { + if p := recover(); p != nil { + if a.screen != nil { + a.screen.Fini() + } + panic(p) + } + }() + + // Draw the screen for the first time. + a.Unlock() + a.draw() + + // Separate loop to wait for screen events. + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + for { + a.RLock() + screen := a.screen + a.RUnlock() + if screen == nil { + // We have no screen. Let's stop. + a.QueueEvent(nil) + break + } + + // Wait for next event and queue it. + event := screen.PollEvent() + if event != nil { + // Regular event. Queue. + a.QueueEvent(event) + continue + } + + // A screen was finalized (event is nil). Wait for a new scren. + screen = <-a.screenReplacement + if screen == nil { + // No new screen. We're done. + a.QueueEvent(nil) + return + } + + // We have a new screen. Keep going. + a.Lock() + a.screen = screen + enableMouse := a.enableMouse + a.Unlock() + + // Initialize and draw this screen. + if err := screen.Init(); err != nil { + panic(err) + } + if enableMouse { + screen.EnableMouse() + } + a.draw() + } + }() + + // Start event loop. +EventLoop: + for { + select { + case event := <-a.events: + if event == nil { + break EventLoop + } + + switch event := event.(type) { + case *tcell.EventKey: + a.RLock() + root := a.root + inputCapture := a.inputCapture + a.RUnlock() + + // Intercept keys. + var draw bool + if inputCapture != nil { + event = inputCapture(event) + if event == nil { + a.draw() + continue // Don't forward event. + } + draw = true + } + + // Ctrl-C closes the application. + if event.Key() == tcell.KeyCtrlC { + a.Stop() + break + } + + // Pass other key events to the root primitive. + if root != nil && root.HasFocus() { + if handler := root.InputHandler(); handler != nil { + handler(event, func(p Primitive) { + a.SetFocus(p) + }) + draw = true + } + } + + // Redraw. + if draw { + a.draw() + } + case *tcell.EventResize: + if time.Since(lastRedraw) < redrawPause { + if redrawTimer != nil { + redrawTimer.Stop() + } + redrawTimer = time.AfterFunc(redrawPause, func() { + a.events <- event + }) + } + a.RLock() + screen := a.screen + a.RUnlock() + if screen == nil { + continue + } + lastRedraw = time.Now() + screen.Clear() + a.draw() + case *tcell.EventMouse: + consumed, isMouseDownAction := a.fireMouseActions(event) + if consumed { + a.draw() + } + a.lastMouseButtons = event.Buttons() + if isMouseDownAction { + a.mouseDownX, a.mouseDownY = event.Position() + } + case *tcell.EventError: + appErr = event + a.Stop() + } + + // If we have updates, now is the time to execute them. + case update := <-a.updates: + update.f() + if update.done != nil { + update.done <- struct{}{} + } + } + } + + // Wait for the event loop to finish. + wg.Wait() + a.screen = nil + + return appErr +} + +// fireMouseActions analyzes the provided mouse event, derives mouse actions +// from it and then forwards them to the corresponding primitives. +func (a *Application) fireMouseActions(event *tcell.EventMouse) (consumed, isMouseDownAction bool) { + // We want to relay follow-up events to the same target primitive. + var targetPrimitive Primitive + + // Helper function to fire a mouse action. + fire := func(action MouseAction) { + switch action { + case MouseLeftDown, MouseMiddleDown, MouseRightDown: + isMouseDownAction = true + } + + // Intercept event. + if a.mouseCapture != nil { + event, action = a.mouseCapture(event, action) + if event == nil { + consumed = true + return // Don't forward event. + } + } + + // Determine the target primitive. + var primitive, capturingPrimitive Primitive + if a.mouseCapturingPrimitive != nil { + primitive = a.mouseCapturingPrimitive + targetPrimitive = a.mouseCapturingPrimitive + } else if targetPrimitive != nil { + primitive = targetPrimitive + } else { + primitive = a.root + } + if primitive != nil { + if handler := primitive.MouseHandler(); handler != nil { + var wasConsumed bool + wasConsumed, capturingPrimitive = handler(action, event, func(p Primitive) { + a.SetFocus(p) + }) + if wasConsumed { + consumed = true + } + } + } + a.mouseCapturingPrimitive = capturingPrimitive + } + + x, y := event.Position() + buttons := event.Buttons() + clickMoved := x != a.mouseDownX || y != a.mouseDownY + buttonChanges := buttons ^ a.lastMouseButtons + + if x != a.lastMouseX || y != a.lastMouseY { + fire(MouseMove) + a.lastMouseX = x + a.lastMouseY = y + } + + for _, buttonEvent := range []struct { + button tcell.ButtonMask + down, up, click, dclick MouseAction + }{ + {tcell.ButtonPrimary, MouseLeftDown, MouseLeftUp, MouseLeftClick, MouseLeftDoubleClick}, + {tcell.ButtonMiddle, MouseMiddleDown, MouseMiddleUp, MouseMiddleClick, MouseMiddleDoubleClick}, + {tcell.ButtonSecondary, MouseRightDown, MouseRightUp, MouseRightClick, MouseRightDoubleClick}, + } { + if buttonChanges&buttonEvent.button != 0 { + if buttons&buttonEvent.button != 0 { + fire(buttonEvent.down) + } else { + fire(buttonEvent.up) + if !clickMoved { + if a.lastMouseClick.Add(DoubleClickInterval).Before(time.Now()) { + fire(buttonEvent.click) + a.lastMouseClick = time.Now() + } else { + fire(buttonEvent.dclick) + a.lastMouseClick = time.Time{} // reset + } + } + } + } + } + + for _, wheelEvent := range []struct { + button tcell.ButtonMask + action MouseAction + }{ + {tcell.WheelUp, MouseScrollUp}, + {tcell.WheelDown, MouseScrollDown}, + {tcell.WheelLeft, MouseScrollLeft}, + {tcell.WheelRight, MouseScrollRight}} { + if buttons&wheelEvent.button != 0 { + fire(wheelEvent.action) + } + } + + return consumed, isMouseDownAction +} + +// Stop stops the application, causing Run() to return. +func (a *Application) Stop() { + a.Lock() + defer a.Unlock() + screen := a.screen + if screen == nil { + return + } + a.screen = nil + screen.Fini() + a.screenReplacement <- nil +} + +// Suspend temporarily suspends the application by exiting terminal UI mode and +// invoking the provided function "f". When "f" returns, terminal UI mode is +// entered again and the application resumes. +// +// A return value of true indicates that the application was suspended and "f" +// was called. If false is returned, the application was already suspended, +// terminal UI mode was not exited, and "f" was not called. +func (a *Application) Suspend(f func()) bool { + a.RLock() + screen := a.screen + a.RUnlock() + if screen == nil { + return false // Screen has not yet been initialized. + } + + // Enter suspended mode. + if err := screen.Suspend(); err != nil { + return false // Suspension failed. + } + + // Wait for "f" to return. + f() + + // If the screen object has changed in the meantime, we need to do more. + a.RLock() + defer a.RUnlock() + if a.screen != screen { + // Calling Stop() while in suspend mode currently still leads to a + // panic, see https://github.com/gdamore/tcell/issues/440. + screen.Fini() + if a.screen == nil { + return true // If stop was called (a.screen is nil), we're done already. + } + } else { + // It hasn't changed. Resume. + screen.Resume() // Not much we can do in case of an error. + } + + // Continue application loop. + return true +} + +// Draw refreshes the screen (during the next update cycle). It calls the Draw() +// function of the application's root primitive and then syncs the screen +// buffer. It is almost never necessary to call this function. It can actually +// deadlock your application if you call it from the main thread (e.g. in a +// callback function of a widget). Please see +// https://github.com/rivo/tview/wiki/Concurrency for details. +func (a *Application) Draw() *Application { + a.QueueUpdate(func() { + a.draw() + }) + return a +} + +// ForceDraw refreshes the screen immediately. Use this function with caution as +// it may lead to race conditions with updates to primitives in other +// goroutines. It is always preferrable to use Draw() instead. Never call this +// function from a goroutine. +// +// It is safe to call this function during queued updates and direct event +// handling. +func (a *Application) ForceDraw() *Application { + return a.draw() +} + +// draw actually does what Draw() promises to do. +func (a *Application) draw() *Application { + a.Lock() + defer a.Unlock() + + screen := a.screen + root := a.root + fullscreen := a.rootFullscreen + before := a.beforeDraw + after := a.afterDraw + + // Maybe we're not ready yet or not anymore. + if screen == nil || root == nil { + return a + } + + // Resize if requested. + if fullscreen && root != nil { + width, height := screen.Size() + root.SetRect(0, 0, width, height) + } + + // Call before handler if there is one. + if before != nil { + if before(screen) { + screen.Show() + return a + } + } + + // Draw all primitives. + root.Draw(screen) + + // Call after handler if there is one. + if after != nil { + after(screen) + } + + // Sync screen. + screen.Show() + + return a +} + +// Sync forces a full re-sync of the screen buffer with the actual screen during +// the next event cycle. This is useful for when the terminal screen is +// corrupted so you may want to offer your users a keyboard shortcut to refresh +// the screen. +func (a *Application) Sync() *Application { + a.updates <- queuedUpdate{f: func() { + a.RLock() + screen := a.screen + a.RUnlock() + if screen == nil { + return + } + screen.Sync() + }} + return a +} + +// SetBeforeDrawFunc installs a callback function which is invoked just before +// the root primitive is drawn during screen updates. If the function returns +// true, drawing will not continue, i.e. the root primitive will not be drawn +// (and an after-draw-handler will not be called). +// +// Note that the screen is not cleared by the application. To clear the screen, +// you may call screen.Clear(). +// +// Provide nil to uninstall the callback function. +func (a *Application) SetBeforeDrawFunc(handler func(screen tcell.Screen) bool) *Application { + a.beforeDraw = handler + return a +} + +// GetBeforeDrawFunc returns the callback function installed with +// SetBeforeDrawFunc() or nil if none has been installed. +func (a *Application) GetBeforeDrawFunc() func(screen tcell.Screen) bool { + return a.beforeDraw +} + +// SetAfterDrawFunc installs a callback function which is invoked after the root +// primitive was drawn during screen updates. +// +// Provide nil to uninstall the callback function. +func (a *Application) SetAfterDrawFunc(handler func(screen tcell.Screen)) *Application { + a.afterDraw = handler + return a +} + +// GetAfterDrawFunc returns the callback function installed with +// SetAfterDrawFunc() or nil if none has been installed. +func (a *Application) GetAfterDrawFunc() func(screen tcell.Screen) { + return a.afterDraw +} + +// SetRoot sets the root primitive for this application. If "fullscreen" is set +// to true, the root primitive's position will be changed to fill the screen. +// +// This function must be called at least once or nothing will be displayed when +// the application starts. +// +// It also calls SetFocus() on the primitive. +func (a *Application) SetRoot(root Primitive, fullscreen bool) *Application { + a.Lock() + a.root = root + a.rootFullscreen = fullscreen + if a.screen != nil { + a.screen.Clear() + } + a.Unlock() + + a.SetFocus(root) + + return a +} + +// ResizeToFullScreen resizes the given primitive such that it fills the entire +// screen. +func (a *Application) ResizeToFullScreen(p Primitive) *Application { + a.RLock() + width, height := a.screen.Size() + a.RUnlock() + p.SetRect(0, 0, width, height) + return a +} + +// SetFocus sets the focus on a new primitive. All key events will be redirected +// to that primitive. Callers must ensure that the primitive will handle key +// events. +// +// Blur() will be called on the previously focused primitive. Focus() will be +// called on the new primitive. +func (a *Application) SetFocus(p Primitive) *Application { + a.Lock() + if a.focus != nil { + a.focus.Blur() + } + a.focus = p + if a.screen != nil { + a.screen.HideCursor() + } + a.Unlock() + if p != nil { + p.Focus(func(p Primitive) { + a.SetFocus(p) + }) + } + + return a +} + +// GetFocus returns the primitive which has the current focus. If none has it, +// nil is returned. +func (a *Application) GetFocus() Primitive { + a.RLock() + defer a.RUnlock() + return a.focus +} + +// QueueUpdate is used to synchronize access to primitives from non-main +// goroutines. The provided function will be executed as part of the event loop +// and thus will not cause race conditions with other such update functions or +// the Draw() function. +// +// Note that Draw() is not implicitly called after the execution of f as that +// may not be desirable. You can call Draw() from f if the screen should be +// refreshed after each update. Alternatively, use QueueUpdateDraw() to follow +// up with an immediate refresh of the screen. +// +// This function returns after f has executed. +func (a *Application) QueueUpdate(f func()) *Application { + ch := make(chan struct{}) + a.updates <- queuedUpdate{f: f, done: ch} + <-ch + return a +} + +// QueueUpdateDraw works like QueueUpdate() except it refreshes the screen +// immediately after executing f. +func (a *Application) QueueUpdateDraw(f func()) *Application { + a.QueueUpdate(func() { + f() + a.draw() + }) + return a +} + +// QueueEvent sends an event to the Application event loop. +// +// It is not recommended for event to be nil. +func (a *Application) QueueEvent(event tcell.Event) *Application { + a.events <- event + return a +} diff --git a/vendor/github.com/rivo/tview/borders.go b/vendor/github.com/rivo/tview/borders.go new file mode 100644 index 0000000000..946c878346 --- /dev/null +++ b/vendor/github.com/rivo/tview/borders.go @@ -0,0 +1,45 @@ +package tview + +// Borders defines various borders used when primitives are drawn. +// These may be changed to accommodate a different look and feel. +var Borders = struct { + Horizontal rune + Vertical rune + TopLeft rune + TopRight rune + BottomLeft rune + BottomRight rune + + LeftT rune + RightT rune + TopT rune + BottomT rune + Cross rune + + HorizontalFocus rune + VerticalFocus rune + TopLeftFocus rune + TopRightFocus rune + BottomLeftFocus rune + BottomRightFocus rune +}{ + Horizontal: BoxDrawingsLightHorizontal, + Vertical: BoxDrawingsLightVertical, + TopLeft: BoxDrawingsLightDownAndRight, + TopRight: BoxDrawingsLightDownAndLeft, + BottomLeft: BoxDrawingsLightUpAndRight, + BottomRight: BoxDrawingsLightUpAndLeft, + + LeftT: BoxDrawingsLightVerticalAndRight, + RightT: BoxDrawingsLightVerticalAndLeft, + TopT: BoxDrawingsLightDownAndHorizontal, + BottomT: BoxDrawingsLightUpAndHorizontal, + Cross: BoxDrawingsLightVerticalAndHorizontal, + + HorizontalFocus: BoxDrawingsDoubleHorizontal, + VerticalFocus: BoxDrawingsDoubleVertical, + TopLeftFocus: BoxDrawingsDoubleDownAndRight, + TopRightFocus: BoxDrawingsDoubleDownAndLeft, + BottomLeftFocus: BoxDrawingsDoubleUpAndRight, + BottomRightFocus: BoxDrawingsDoubleUpAndLeft, +} diff --git a/vendor/github.com/rivo/tview/box.go b/vendor/github.com/rivo/tview/box.go new file mode 100644 index 0000000000..fbd0c1806a --- /dev/null +++ b/vendor/github.com/rivo/tview/box.go @@ -0,0 +1,414 @@ +package tview + +import ( + "github.com/gdamore/tcell/v2" +) + +// Box implements the Primitive interface with an empty background and optional +// elements such as a border and a title. Box itself does not hold any content +// but serves as the superclass of all other primitives. Subclasses add their +// own content, typically (but not necessarily) keeping their content within the +// box's rectangle. +// +// Box provides a number of utility functions available to all primitives. +// +// See https://github.com/rivo/tview/wiki/Box for an example. +type Box struct { + // The position of the rect. + x, y, width, height int + + // The inner rect reserved for the box's content. + innerX, innerY, innerWidth, innerHeight int + + // Border padding. + paddingTop, paddingBottom, paddingLeft, paddingRight int + + // The box's background color. + backgroundColor tcell.Color + + // If set to true, the background of this box is not cleared while drawing. + dontClear bool + + // Whether or not a border is drawn, reducing the box's space for content by + // two in width and height. + border bool + + // The border style. + borderStyle tcell.Style + + // The title. Only visible if there is a border, too. + title string + + // The color of the title. + titleColor tcell.Color + + // The alignment of the title. + titleAlign int + + // Whether or not this box has focus. + hasFocus bool + + // An optional capture function which receives a key event and returns the + // event to be forwarded to the primitive's default input handler (nil if + // nothing should be forwarded). + inputCapture func(event *tcell.EventKey) *tcell.EventKey + + // An optional function which is called before the box is drawn. + draw func(screen tcell.Screen, x, y, width, height int) (int, int, int, int) + + // An optional capture function which receives a mouse event and returns the + // event to be forwarded to the primitive's default mouse event handler (at + // least one nil if nothing should be forwarded). + mouseCapture func(action MouseAction, event *tcell.EventMouse) (MouseAction, *tcell.EventMouse) +} + +// NewBox returns a Box without a border. +func NewBox() *Box { + b := &Box{ + width: 15, + height: 10, + innerX: -1, // Mark as uninitialized. + backgroundColor: Styles.PrimitiveBackgroundColor, + borderStyle: tcell.StyleDefault.Foreground(Styles.BorderColor).Background(Styles.PrimitiveBackgroundColor), + titleColor: Styles.TitleColor, + titleAlign: AlignCenter, + } + return b +} + +// SetBorderPadding sets the size of the borders around the box content. +func (b *Box) SetBorderPadding(top, bottom, left, right int) *Box { + b.paddingTop, b.paddingBottom, b.paddingLeft, b.paddingRight = top, bottom, left, right + return b +} + +// GetRect returns the current position of the rectangle, x, y, width, and +// height. +func (b *Box) GetRect() (int, int, int, int) { + return b.x, b.y, b.width, b.height +} + +// GetInnerRect returns the position of the inner rectangle (x, y, width, +// height), without the border and without any padding. Width and height values +// will clamp to 0 and thus never be negative. +func (b *Box) GetInnerRect() (int, int, int, int) { + if b.innerX >= 0 { + return b.innerX, b.innerY, b.innerWidth, b.innerHeight + } + x, y, width, height := b.GetRect() + if b.border { + x++ + y++ + width -= 2 + height -= 2 + } + x, y, width, height = x+b.paddingLeft, + y+b.paddingTop, + width-b.paddingLeft-b.paddingRight, + height-b.paddingTop-b.paddingBottom + if width < 0 { + width = 0 + } + if height < 0 { + height = 0 + } + return x, y, width, height +} + +// SetRect sets a new position of the primitive. Note that this has no effect +// if this primitive is part of a layout (e.g. Flex, Grid) or if it was added +// like this: +// +// application.SetRoot(b, true) +func (b *Box) SetRect(x, y, width, height int) { + b.x = x + b.y = y + b.width = width + b.height = height + b.innerX = -1 // Mark inner rect as uninitialized. +} + +// SetDrawFunc sets a callback function which is invoked after the box primitive +// has been drawn. This allows you to add a more individual style to the box +// (and all primitives which extend it). +// +// The function is provided with the box's dimensions (set via SetRect()). It +// must return the box's inner dimensions (x, y, width, height) which will be +// returned by GetInnerRect(), used by descendent primitives to draw their own +// content. +func (b *Box) SetDrawFunc(handler func(screen tcell.Screen, x, y, width, height int) (int, int, int, int)) *Box { + b.draw = handler + return b +} + +// GetDrawFunc returns the callback function which was installed with +// SetDrawFunc() or nil if no such function has been installed. +func (b *Box) GetDrawFunc() func(screen tcell.Screen, x, y, width, height int) (int, int, int, int) { + return b.draw +} + +// WrapInputHandler wraps an input handler (see InputHandler()) with the +// functionality to capture input (see SetInputCapture()) before passing it +// on to the provided (default) input handler. +// +// This is only meant to be used by subclassing primitives. +func (b *Box) WrapInputHandler(inputHandler func(*tcell.EventKey, func(p Primitive))) func(*tcell.EventKey, func(p Primitive)) { + return func(event *tcell.EventKey, setFocus func(p Primitive)) { + if b.inputCapture != nil { + event = b.inputCapture(event) + } + if event != nil && inputHandler != nil { + inputHandler(event, setFocus) + } + } +} + +// InputHandler returns nil. +func (b *Box) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { + return b.WrapInputHandler(nil) +} + +// SetInputCapture installs a function which captures key events before they are +// forwarded to the primitive's default key event handler. This function can +// then choose to forward that key event (or a different one) to the default +// handler by returning it. If nil is returned, the default handler will not +// be called. +// +// Providing a nil handler will remove a previously existing handler. +// +// Note that this function will not have an effect on primitives composed of +// other primitives, such as Form, Flex, or Grid. Key events are only captured +// by the primitives that have focus (e.g. InputField) and only one primitive +// can have focus at a time. Composing primitives such as Form pass the focus on +// to their contained primitives and thus never receive any key events +// themselves. Therefore, they cannot intercept key events. +func (b *Box) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Box { + b.inputCapture = capture + return b +} + +// GetInputCapture returns the function installed with SetInputCapture() or nil +// if no such function has been installed. +func (b *Box) GetInputCapture() func(event *tcell.EventKey) *tcell.EventKey { + return b.inputCapture +} + +// WrapMouseHandler wraps a mouse event handler (see MouseHandler()) with the +// functionality to capture mouse events (see SetMouseCapture()) before passing +// them on to the provided (default) event handler. +// +// This is only meant to be used by subclassing primitives. +func (b *Box) WrapMouseHandler(mouseHandler func(MouseAction, *tcell.EventMouse, func(p Primitive)) (bool, Primitive)) func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + return func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + if b.mouseCapture != nil { + action, event = b.mouseCapture(action, event) + } + if event != nil && mouseHandler != nil { + consumed, capture = mouseHandler(action, event, setFocus) + } + return + } +} + +// MouseHandler returns nil. +func (b *Box) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + return b.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + if action == MouseLeftClick && b.InRect(event.Position()) { + setFocus(b) + consumed = true + } + return + }) +} + +// SetMouseCapture sets a function which captures mouse events (consisting of +// the original tcell mouse event and the semantic mouse action) before they are +// forwarded to the primitive's default mouse event handler. This function can +// then choose to forward that event (or a different one) by returning it or +// returning a nil mouse event, in which case the default handler will not be +// called. +// +// Providing a nil handler will remove a previously existing handler. +func (b *Box) SetMouseCapture(capture func(action MouseAction, event *tcell.EventMouse) (MouseAction, *tcell.EventMouse)) *Box { + b.mouseCapture = capture + return b +} + +// InRect returns true if the given coordinate is within the bounds of the box's +// rectangle. +func (b *Box) InRect(x, y int) bool { + rectX, rectY, width, height := b.GetRect() + return x >= rectX && x < rectX+width && y >= rectY && y < rectY+height +} + +// GetMouseCapture returns the function installed with SetMouseCapture() or nil +// if no such function has been installed. +func (b *Box) GetMouseCapture() func(action MouseAction, event *tcell.EventMouse) (MouseAction, *tcell.EventMouse) { + return b.mouseCapture +} + +// SetBackgroundColor sets the box's background color. +func (b *Box) SetBackgroundColor(color tcell.Color) *Box { + b.backgroundColor = color + b.borderStyle = b.borderStyle.Background(color) + return b +} + +// SetBorder sets the flag indicating whether or not the box should have a +// border. +func (b *Box) SetBorder(show bool) *Box { + b.border = show + return b +} + +// SetBorderColor sets the box's border color. +func (b *Box) SetBorderColor(color tcell.Color) *Box { + b.borderStyle = b.borderStyle.Foreground(color) + return b +} + +// SetBorderAttributes sets the border's style attributes. You can combine +// different attributes using bitmask operations: +// +// box.SetBorderAttributes(tcell.AttrUnderline | tcell.AttrBold) +func (b *Box) SetBorderAttributes(attr tcell.AttrMask) *Box { + b.borderStyle = b.borderStyle.Attributes(attr) + return b +} + +// GetBorderAttributes returns the border's style attributes. +func (b *Box) GetBorderAttributes() tcell.AttrMask { + _, _, attr := b.borderStyle.Decompose() + return attr +} + +// GetBorderColor returns the box's border color. +func (b *Box) GetBorderColor() tcell.Color { + color, _, _ := b.borderStyle.Decompose() + return color +} + +// GetBackgroundColor returns the box's background color. +func (b *Box) GetBackgroundColor() tcell.Color { + return b.backgroundColor +} + +// SetTitle sets the box's title. +func (b *Box) SetTitle(title string) *Box { + b.title = title + return b +} + +// GetTitle returns the box's current title. +func (b *Box) GetTitle() string { + return b.title +} + +// SetTitleColor sets the box's title color. +func (b *Box) SetTitleColor(color tcell.Color) *Box { + b.titleColor = color + return b +} + +// SetTitleAlign sets the alignment of the title, one of AlignLeft, AlignCenter, +// or AlignRight. +func (b *Box) SetTitleAlign(align int) *Box { + b.titleAlign = align + return b +} + +// Draw draws this primitive onto the screen. +func (b *Box) Draw(screen tcell.Screen) { + b.DrawForSubclass(screen, b) +} + +// DrawForSubclass draws this box under the assumption that primitive p is a +// subclass of this box. This is needed e.g. to draw proper box frames which +// depend on the subclass's focus. +// +// Only call this function from your own custom primitives. It is not needed in +// applications that have no custom primitives. +func (b *Box) DrawForSubclass(screen tcell.Screen, p Primitive) { + // Don't draw anything if there is no space. + if b.width <= 0 || b.height <= 0 { + return + } + + def := tcell.StyleDefault + + // Fill background. + background := def.Background(b.backgroundColor) + if !b.dontClear { + for y := b.y; y < b.y+b.height; y++ { + for x := b.x; x < b.x+b.width; x++ { + screen.SetContent(x, y, ' ', nil, background) + } + } + } + + // Draw border. + if b.border && b.width >= 2 && b.height >= 2 { + var vertical, horizontal, topLeft, topRight, bottomLeft, bottomRight rune + if p.HasFocus() { + horizontal = Borders.HorizontalFocus + vertical = Borders.VerticalFocus + topLeft = Borders.TopLeftFocus + topRight = Borders.TopRightFocus + bottomLeft = Borders.BottomLeftFocus + bottomRight = Borders.BottomRightFocus + } else { + horizontal = Borders.Horizontal + vertical = Borders.Vertical + topLeft = Borders.TopLeft + topRight = Borders.TopRight + bottomLeft = Borders.BottomLeft + bottomRight = Borders.BottomRight + } + for x := b.x + 1; x < b.x+b.width-1; x++ { + screen.SetContent(x, b.y, horizontal, nil, b.borderStyle) + screen.SetContent(x, b.y+b.height-1, horizontal, nil, b.borderStyle) + } + for y := b.y + 1; y < b.y+b.height-1; y++ { + screen.SetContent(b.x, y, vertical, nil, b.borderStyle) + screen.SetContent(b.x+b.width-1, y, vertical, nil, b.borderStyle) + } + screen.SetContent(b.x, b.y, topLeft, nil, b.borderStyle) + screen.SetContent(b.x+b.width-1, b.y, topRight, nil, b.borderStyle) + screen.SetContent(b.x, b.y+b.height-1, bottomLeft, nil, b.borderStyle) + screen.SetContent(b.x+b.width-1, b.y+b.height-1, bottomRight, nil, b.borderStyle) + + // Draw title. + if b.title != "" && b.width >= 4 { + printed, _ := Print(screen, b.title, b.x+1, b.y, b.width-2, b.titleAlign, b.titleColor) + if len(b.title)-printed > 0 && printed > 0 { + _, _, style, _ := screen.GetContent(b.x+b.width-2, b.y) + fg, _, _ := style.Decompose() + Print(screen, string(SemigraphicsHorizontalEllipsis), b.x+b.width-2, b.y, 1, AlignLeft, fg) + } + } + } + + // Call custom draw function. + if b.draw != nil { + b.innerX, b.innerY, b.innerWidth, b.innerHeight = b.draw(screen, b.x, b.y, b.width, b.height) + } else { + // Remember the inner rect. + b.innerX = -1 + b.innerX, b.innerY, b.innerWidth, b.innerHeight = b.GetInnerRect() + } +} + +// Focus is called when this primitive receives focus. +func (b *Box) Focus(delegate func(p Primitive)) { + b.hasFocus = true +} + +// Blur is called when this primitive loses focus. +func (b *Box) Blur() { + b.hasFocus = false +} + +// HasFocus returns whether or not this primitive has focus. +func (b *Box) HasFocus() bool { + return b.hasFocus +} diff --git a/vendor/github.com/rivo/tview/button.go b/vendor/github.com/rivo/tview/button.go new file mode 100644 index 0000000000..97e9ff5aab --- /dev/null +++ b/vendor/github.com/rivo/tview/button.go @@ -0,0 +1,157 @@ +package tview + +import ( + "github.com/gdamore/tcell/v2" +) + +// Button is labeled box that triggers an action when selected. +// +// See https://github.com/rivo/tview/wiki/Button for an example. +type Button struct { + *Box + + // The text to be displayed before the input area. + label string + + // The label color. + labelColor tcell.Color + + // The label color when the button is in focus. + labelColorActivated tcell.Color + + // The background color when the button is in focus. + backgroundColorActivated tcell.Color + + // An optional function which is called when the button was selected. + selected func() + + // An optional function which is called when the user leaves the button. A + // key is provided indicating which key was pressed to leave (tab or backtab). + blur func(tcell.Key) +} + +// NewButton returns a new input field. +func NewButton(label string) *Button { + box := NewBox().SetBackgroundColor(Styles.ContrastBackgroundColor) + box.SetRect(0, 0, TaggedStringWidth(label)+4, 1) + return &Button{ + Box: box, + label: label, + labelColor: Styles.PrimaryTextColor, + labelColorActivated: Styles.InverseTextColor, + backgroundColorActivated: Styles.PrimaryTextColor, + } +} + +// SetLabel sets the button text. +func (b *Button) SetLabel(label string) *Button { + b.label = label + return b +} + +// GetLabel returns the button text. +func (b *Button) GetLabel() string { + return b.label +} + +// SetLabelColor sets the color of the button text. +func (b *Button) SetLabelColor(color tcell.Color) *Button { + b.labelColor = color + return b +} + +// SetLabelColorActivated sets the color of the button text when the button is +// in focus. +func (b *Button) SetLabelColorActivated(color tcell.Color) *Button { + b.labelColorActivated = color + return b +} + +// SetBackgroundColorActivated sets the background color of the button text when +// the button is in focus. +func (b *Button) SetBackgroundColorActivated(color tcell.Color) *Button { + b.backgroundColorActivated = color + return b +} + +// SetSelectedFunc sets a handler which is called when the button was selected. +func (b *Button) SetSelectedFunc(handler func()) *Button { + b.selected = handler + return b +} + +// SetBlurFunc sets a handler which is called when the user leaves the button. +// The callback function is provided with the key that was pressed, which is one +// of the following: +// +// - KeyEscape: Leaving the button with no specific direction. +// - KeyTab: Move to the next field. +// - KeyBacktab: Move to the previous field. +func (b *Button) SetBlurFunc(handler func(key tcell.Key)) *Button { + b.blur = handler + return b +} + +// Draw draws this primitive onto the screen. +func (b *Button) Draw(screen tcell.Screen) { + // Draw the box. + borderColor := b.GetBorderColor() + backgroundColor := b.GetBackgroundColor() + if b.HasFocus() { + b.SetBackgroundColor(b.backgroundColorActivated) + b.SetBorderColor(b.labelColorActivated) + defer func() { + b.SetBorderColor(borderColor) + }() + } + b.Box.DrawForSubclass(screen, b) + b.backgroundColor = backgroundColor + + // Draw label. + x, y, width, height := b.GetInnerRect() + if width > 0 && height > 0 { + y = y + height/2 + labelColor := b.labelColor + if b.HasFocus() { + labelColor = b.labelColorActivated + } + Print(screen, b.label, x, y, width, AlignCenter, labelColor) + } +} + +// InputHandler returns the handler for this primitive. +func (b *Button) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { + return b.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { + // Process key event. + switch key := event.Key(); key { + case tcell.KeyEnter: // Selected. + if b.selected != nil { + b.selected() + } + case tcell.KeyBacktab, tcell.KeyTab, tcell.KeyEscape: // Leave. No action. + if b.blur != nil { + b.blur(key) + } + } + }) +} + +// MouseHandler returns the mouse handler for this primitive. +func (b *Button) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + return b.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + if !b.InRect(event.Position()) { + return false, nil + } + + // Process mouse event. + if action == MouseLeftClick { + setFocus(b) + if b.selected != nil { + b.selected() + } + consumed = true + } + + return + }) +} diff --git a/vendor/github.com/rivo/tview/checkbox.go b/vendor/github.com/rivo/tview/checkbox.go new file mode 100644 index 0000000000..d9a8633186 --- /dev/null +++ b/vendor/github.com/rivo/tview/checkbox.go @@ -0,0 +1,240 @@ +package tview + +import ( + "strings" + + "github.com/gdamore/tcell/v2" +) + +// Checkbox implements a simple box for boolean values which can be checked and +// unchecked. +// +// See https://github.com/rivo/tview/wiki/Checkbox for an example. +type Checkbox struct { + *Box + + // Whether or not this box is checked. + checked bool + + // The text to be displayed before the input area. + label string + + // The screen width of the label area. A value of 0 means use the width of + // the label text. + labelWidth int + + // The label color. + labelColor tcell.Color + + // The background color of the input area. + fieldBackgroundColor tcell.Color + + // The text color of the input area. + fieldTextColor tcell.Color + + // The string use to display a checked box. + checkedString string + + // An optional function which is called when the user changes the checked + // state of this checkbox. + changed func(checked bool) + + // An optional function which is called when the user indicated that they + // are done entering text. The key which was pressed is provided (tab, + // shift-tab, or escape). + done func(tcell.Key) + + // A callback function set by the Form class and called when the user leaves + // this form item. + finished func(tcell.Key) +} + +// NewCheckbox returns a new input field. +func NewCheckbox() *Checkbox { + return &Checkbox{ + Box: NewBox(), + labelColor: Styles.SecondaryTextColor, + fieldBackgroundColor: Styles.ContrastBackgroundColor, + fieldTextColor: Styles.PrimaryTextColor, + checkedString: "X", + } +} + +// SetChecked sets the state of the checkbox. +func (c *Checkbox) SetChecked(checked bool) *Checkbox { + c.checked = checked + return c +} + +// IsChecked returns whether or not the box is checked. +func (c *Checkbox) IsChecked() bool { + return c.checked +} + +// SetLabel sets the text to be displayed before the input area. +func (c *Checkbox) SetLabel(label string) *Checkbox { + c.label = label + return c +} + +// GetLabel returns the text to be displayed before the input area. +func (c *Checkbox) GetLabel() string { + return c.label +} + +// SetLabelWidth sets the screen width of the label. A value of 0 will cause the +// primitive to use the width of the label string. +func (c *Checkbox) SetLabelWidth(width int) *Checkbox { + c.labelWidth = width + return c +} + +// SetLabelColor sets the color of the label. +func (c *Checkbox) SetLabelColor(color tcell.Color) *Checkbox { + c.labelColor = color + return c +} + +// SetFieldBackgroundColor sets the background color of the input area. +func (c *Checkbox) SetFieldBackgroundColor(color tcell.Color) *Checkbox { + c.fieldBackgroundColor = color + return c +} + +// SetFieldTextColor sets the text color of the input area. +func (c *Checkbox) SetFieldTextColor(color tcell.Color) *Checkbox { + c.fieldTextColor = color + return c +} + +// SetCheckedString sets the string to be displayed when the checkbox is +// checked (defaults to "X"). +func (c *Checkbox) SetCheckedString(checked string) *Checkbox { + c.checkedString = checked + return c +} + +// SetFormAttributes sets attributes shared by all form items. +func (c *Checkbox) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem { + c.labelWidth = labelWidth + c.labelColor = labelColor + c.backgroundColor = bgColor + c.fieldTextColor = fieldTextColor + c.fieldBackgroundColor = fieldBgColor + return c +} + +// GetFieldWidth returns this primitive's field width. +func (c *Checkbox) GetFieldWidth() int { + return 1 +} + +// SetChangedFunc sets a handler which is called when the checked state of this +// checkbox was changed by the user. The handler function receives the new +// state. +func (c *Checkbox) SetChangedFunc(handler func(checked bool)) *Checkbox { + c.changed = handler + return c +} + +// SetDoneFunc sets a handler which is called when the user is done using the +// checkbox. The callback function is provided with the key that was pressed, +// which is one of the following: +// +// - KeyEscape: Abort text input. +// - KeyTab: Move to the next field. +// - KeyBacktab: Move to the previous field. +func (c *Checkbox) SetDoneFunc(handler func(key tcell.Key)) *Checkbox { + c.done = handler + return c +} + +// SetFinishedFunc sets a callback invoked when the user leaves this form item. +func (c *Checkbox) SetFinishedFunc(handler func(key tcell.Key)) FormItem { + c.finished = handler + return c +} + +// Draw draws this primitive onto the screen. +func (c *Checkbox) Draw(screen tcell.Screen) { + c.Box.DrawForSubclass(screen, c) + + // Prepare + x, y, width, height := c.GetInnerRect() + rightLimit := x + width + if height < 1 || rightLimit <= x { + return + } + + // Draw label. + if c.labelWidth > 0 { + labelWidth := c.labelWidth + if labelWidth > rightLimit-x { + labelWidth = rightLimit - x + } + Print(screen, c.label, x, y, labelWidth, AlignLeft, c.labelColor) + x += labelWidth + } else { + _, drawnWidth := Print(screen, c.label, x, y, rightLimit-x, AlignLeft, c.labelColor) + x += drawnWidth + } + + // Draw checkbox. + fieldStyle := tcell.StyleDefault.Background(c.fieldBackgroundColor).Foreground(c.fieldTextColor) + if c.HasFocus() { + fieldStyle = fieldStyle.Background(c.fieldTextColor).Foreground(c.fieldBackgroundColor) + } + checkboxWidth := stringWidth(c.checkedString) + checkedString := c.checkedString + if !c.checked { + checkedString = strings.Repeat(" ", checkboxWidth) + } + printWithStyle(screen, checkedString, x, y, 0, checkboxWidth, AlignLeft, fieldStyle, false) +} + +// InputHandler returns the handler for this primitive. +func (c *Checkbox) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { + return c.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { + // Process key event. + switch key := event.Key(); key { + case tcell.KeyRune, tcell.KeyEnter: // Check. + if key == tcell.KeyRune && event.Rune() != ' ' { + break + } + c.checked = !c.checked + if c.changed != nil { + c.changed(c.checked) + } + case tcell.KeyTab, tcell.KeyBacktab, tcell.KeyEscape: // We're done. + if c.done != nil { + c.done(key) + } + if c.finished != nil { + c.finished(key) + } + } + }) +} + +// MouseHandler returns the mouse handler for this primitive. +func (c *Checkbox) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + return c.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + x, y := event.Position() + _, rectY, _, _ := c.GetInnerRect() + if !c.InRect(x, y) { + return false, nil + } + + // Process mouse event. + if action == MouseLeftClick && y == rectY { + setFocus(c) + c.checked = !c.checked + if c.changed != nil { + c.changed(c.checked) + } + consumed = true + } + + return + }) +} diff --git a/vendor/github.com/rivo/tview/doc.go b/vendor/github.com/rivo/tview/doc.go new file mode 100644 index 0000000000..1e1141281c --- /dev/null +++ b/vendor/github.com/rivo/tview/doc.go @@ -0,0 +1,179 @@ +/* +Package tview implements rich widgets for terminal based user interfaces. The +widgets provided with this package are useful for data exploration and data +entry. + +Widgets + +The package implements the following widgets: + + - TextView: A scrollable window that display multi-colored text. Text may also + be highlighted. + - Table: A scrollable display of tabular data. Table cells, rows, or columns + may also be highlighted. + - TreeView: A scrollable display for hierarchical data. Tree nodes can be + highlighted, collapsed, expanded, and more. + - List: A navigable text list with optional keyboard shortcuts. + - InputField: One-line input fields to enter text. + - DropDown: Drop-down selection fields. + - Checkbox: Selectable checkbox for boolean values. + - Button: Buttons which get activated when the user selects them. + - Form: Forms composed of input fields, drop down selections, checkboxes, and + buttons. + - Modal: A centered window with a text message and one or more buttons. + - Grid: A grid based layout manager. + - Flex: A Flexbox based layout manager. + - Pages: A page based layout manager. + +The package also provides Application which is used to poll the event queue and +draw widgets on screen. + +Hello World + +The following is a very basic example showing a box with the title "Hello, +world!": + + package main + + import ( + "github.com/rivo/tview" + ) + + func main() { + box := tview.NewBox().SetBorder(true).SetTitle("Hello, world!") + if err := tview.NewApplication().SetRoot(box, true).Run(); err != nil { + panic(err) + } + } + +First, we create a box primitive with a border and a title. Then we create an +application, set the box as its root primitive, and run the event loop. The +application exits when the application's Stop() function is called or when +Ctrl-C is pressed. + +If we have a primitive which consumes key presses, we call the application's +SetFocus() function to redirect all key presses to that primitive. Most +primitives then offer ways to install handlers that allow you to react to any +actions performed on them. + +More Demos + +You will find more demos in the "demos" subdirectory. It also contains a +presentation (written using tview) which gives an overview of the different +widgets and how they can be used. + +Colors + +Throughout this package, colors are specified using the tcell.Color type. +Functions such as tcell.GetColor(), tcell.NewHexColor(), and tcell.NewRGBColor() +can be used to create colors from W3C color names or RGB values. + +Almost all strings which are displayed can contain color tags. Color tags are +W3C color names or six hexadecimal digits following a hash tag, wrapped in +square brackets. Examples: + + This is a [red]warning[white]! + The sky is [#8080ff]blue[#ffffff]. + +A color tag changes the color of the characters following that color tag. This +applies to almost everything from box titles, list text, form item labels, to +table cells. In a TextView, this functionality has to be switched on explicitly. +See the TextView documentation for more information. + +Color tags may contain not just the foreground (text) color but also the +background color and additional flags. In fact, the full definition of a color +tag is as follows: + + [::] + +Each of the three fields can be left blank and trailing fields can be omitted. +(Empty square brackets "[]", however, are not considered color tags.) Colors +that are not specified will be left unchanged. A field with just a dash ("-") +means "reset to default". + +You can specify the following flags (some flags may not be supported by your +terminal): + + l: blink + b: bold + d: dim + r: reverse (switch foreground and background color) + u: underline + +Examples: + + [yellow]Yellow text + [yellow:red]Yellow text on red background + [:red]Red background, text color unchanged + [yellow::u]Yellow text underlined + [::bl]Bold, blinking text + [::-]Colors unchanged, flags reset + [-]Reset foreground color + [-:-:-]Reset everything + [:]No effect + []Not a valid color tag, will print square brackets as they are + +In the rare event that you want to display a string such as "[red]" or +"[#00ff1a]" without applying its effect, you need to put an opening square +bracket before the closing square bracket. Note that the text inside the +brackets will be matched less strictly than region or colors tags. I.e. any +character that may be used in color or region tags will be recognized. Examples: + + [red[] will be output as [red] + ["123"[] will be output as ["123"] + [#6aff00[[] will be output as [#6aff00[] + [a#"[[[] will be output as [a#"[[] + [] will be output as [] (see color tags above) + [[] will be output as [[] (not an escaped tag) + +You can use the Escape() function to insert brackets automatically where needed. + +Styles + +When primitives are instantiated, they are initialized with colors taken from +the global Styles variable. You may change this variable to adapt the look and +feel of the primitives to your preferred style. + +Unicode Support + +This package supports unicode characters including wide characters. + +Concurrency + +Many functions in this package are not thread-safe. For many applications, this +may not be an issue: If your code makes changes in response to key events, it +will execute in the main goroutine and thus will not cause any race conditions. + +If you access your primitives from other goroutines, however, you will need to +synchronize execution. The easiest way to do this is to call +Application.QueueUpdate() or Application.QueueUpdateDraw() (see the function +documentation for details): + + go func() { + app.QueueUpdateDraw(func() { + table.SetCellSimple(0, 0, "Foo bar") + }) + }() + +One exception to this is the io.Writer interface implemented by TextView. You +can safely write to a TextView from any goroutine. See the TextView +documentation for details. + +You can also call Application.Draw() from any goroutine without having to wrap +it in QueueUpdate(). And, as mentioned above, key event callbacks are executed +in the main goroutine and thus should not use QueueUpdate() as that may lead to +deadlocks. + +Type Hierarchy + +All widgets listed above contain the Box type. All of Box's functions are +therefore available for all widgets, too. + +All widgets also implement the Primitive interface. + +The tview package is based on https://github.com/gdamore/tcell. It uses types +and constants from that package (e.g. colors and keyboard values). + +This package does not process mouse input (yet). +*/ +package tview diff --git a/vendor/github.com/rivo/tview/dropdown.go b/vendor/github.com/rivo/tview/dropdown.go new file mode 100644 index 0000000000..65995deda0 --- /dev/null +++ b/vendor/github.com/rivo/tview/dropdown.go @@ -0,0 +1,564 @@ +package tview + +import ( + "strings" + + "github.com/gdamore/tcell/v2" +) + +// dropDownOption is one option that can be selected in a drop-down primitive. +type dropDownOption struct { + Text string // The text to be displayed in the drop-down. + Selected func() // The (optional) callback for when this option was selected. +} + +// DropDown implements a selection widget whose options become visible in a +// drop-down list when activated. +// +// See https://github.com/rivo/tview/wiki/DropDown for an example. +type DropDown struct { + *Box + + // The options from which the user can choose. + options []*dropDownOption + + // Strings to be placed before and after each drop-down option. + optionPrefix, optionSuffix string + + // The index of the currently selected option. Negative if no option is + // currently selected. + currentOption int + + // Strings to be placed beefore and after the current option. + currentOptionPrefix, currentOptionSuffix string + + // The text to be displayed when no option has yet been selected. + noSelection string + + // Set to true if the options are visible and selectable. + open bool + + // The runes typed so far to directly access one of the list items. + prefix string + + // The list element for the options. + list *List + + // The text to be displayed before the input area. + label string + + // The label color. + labelColor tcell.Color + + // The background color of the input area. + fieldBackgroundColor tcell.Color + + // The text color of the input area. + fieldTextColor tcell.Color + + // The color for prefixes. + prefixTextColor tcell.Color + + // The screen width of the label area. A value of 0 means use the width of + // the label text. + labelWidth int + + // The screen width of the input area. A value of 0 means extend as much as + // possible. + fieldWidth int + + // An optional function which is called when the user indicated that they + // are done selecting options. The key which was pressed is provided (tab, + // shift-tab, or escape). + done func(tcell.Key) + + // A callback function set by the Form class and called when the user leaves + // this form item. + finished func(tcell.Key) + + // A callback function which is called when the user changes the drop-down's + // selection. + selected func(text string, index int) + + dragging bool // Set to true when mouse dragging is in progress. +} + +// NewDropDown returns a new drop-down. +func NewDropDown() *DropDown { + list := NewList() + list.ShowSecondaryText(false). + SetMainTextColor(Styles.PrimitiveBackgroundColor). + SetSelectedTextColor(Styles.PrimitiveBackgroundColor). + SetSelectedBackgroundColor(Styles.PrimaryTextColor). + SetHighlightFullLine(true). + SetBackgroundColor(Styles.MoreContrastBackgroundColor) + + d := &DropDown{ + Box: NewBox(), + currentOption: -1, + list: list, + labelColor: Styles.SecondaryTextColor, + fieldBackgroundColor: Styles.ContrastBackgroundColor, + fieldTextColor: Styles.PrimaryTextColor, + prefixTextColor: Styles.ContrastSecondaryTextColor, + } + + return d +} + +// SetCurrentOption sets the index of the currently selected option. This may +// be a negative value to indicate that no option is currently selected. Calling +// this function will also trigger the "selected" callback (if there is one). +func (d *DropDown) SetCurrentOption(index int) *DropDown { + if index >= 0 && index < len(d.options) { + d.currentOption = index + d.list.SetCurrentItem(index) + if d.selected != nil { + d.selected(d.options[index].Text, index) + } + if d.options[index].Selected != nil { + d.options[index].Selected() + } + } else { + d.currentOption = -1 + d.list.SetCurrentItem(0) // Set to 0 because -1 means "last item". + if d.selected != nil { + d.selected("", -1) + } + } + return d +} + +// GetCurrentOption returns the index of the currently selected option as well +// as its text. If no option was selected, -1 and an empty string is returned. +func (d *DropDown) GetCurrentOption() (int, string) { + var text string + if d.currentOption >= 0 && d.currentOption < len(d.options) { + text = d.options[d.currentOption].Text + } + return d.currentOption, text +} + +// SetTextOptions sets the text to be placed before and after each drop-down +// option (prefix/suffix), the text placed before and after the currently +// selected option (currentPrefix/currentSuffix) as well as the text to be +// displayed when no option is currently selected. Per default, all of these +// strings are empty. +func (d *DropDown) SetTextOptions(prefix, suffix, currentPrefix, currentSuffix, noSelection string) *DropDown { + d.currentOptionPrefix = currentPrefix + d.currentOptionSuffix = currentSuffix + d.noSelection = noSelection + d.optionPrefix = prefix + d.optionSuffix = suffix + for index := 0; index < d.list.GetItemCount(); index++ { + d.list.SetItemText(index, prefix+d.options[index].Text+suffix, "") + } + return d +} + +// SetLabel sets the text to be displayed before the input area. +func (d *DropDown) SetLabel(label string) *DropDown { + d.label = label + return d +} + +// GetLabel returns the text to be displayed before the input area. +func (d *DropDown) GetLabel() string { + return d.label +} + +// SetLabelWidth sets the screen width of the label. A value of 0 will cause the +// primitive to use the width of the label string. +func (d *DropDown) SetLabelWidth(width int) *DropDown { + d.labelWidth = width + return d +} + +// SetLabelColor sets the color of the label. +func (d *DropDown) SetLabelColor(color tcell.Color) *DropDown { + d.labelColor = color + return d +} + +// SetFieldBackgroundColor sets the background color of the options area. +func (d *DropDown) SetFieldBackgroundColor(color tcell.Color) *DropDown { + d.fieldBackgroundColor = color + return d +} + +// SetFieldTextColor sets the text color of the options area. +func (d *DropDown) SetFieldTextColor(color tcell.Color) *DropDown { + d.fieldTextColor = color + return d +} + +// SetPrefixTextColor sets the color of the prefix string. The prefix string is +// shown when the user starts typing text, which directly selects the first +// option that starts with the typed string. +func (d *DropDown) SetPrefixTextColor(color tcell.Color) *DropDown { + d.prefixTextColor = color + return d +} + +// SetListStyles sets the styles of the items in the drop-down list (unselected +// as well as selected items). Style attributes are currently ignored but may be +// used in the future. +func (d *DropDown) SetListStyles(unselected, selected tcell.Style) *DropDown { + fg, bg, _ := unselected.Decompose() + d.list.SetMainTextColor(fg).SetBackgroundColor(bg) + fg, bg, _ = selected.Decompose() + d.list.SetSelectedTextColor(fg).SetSelectedBackgroundColor(bg) + return d +} + +// SetFormAttributes sets attributes shared by all form items. +func (d *DropDown) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem { + d.labelWidth = labelWidth + d.labelColor = labelColor + d.backgroundColor = bgColor + d.fieldTextColor = fieldTextColor + d.fieldBackgroundColor = fieldBgColor + return d +} + +// SetFieldWidth sets the screen width of the options area. A value of 0 means +// extend to as long as the longest option text. +func (d *DropDown) SetFieldWidth(width int) *DropDown { + d.fieldWidth = width + return d +} + +// GetFieldWidth returns this primitive's field screen width. +func (d *DropDown) GetFieldWidth() int { + if d.fieldWidth > 0 { + return d.fieldWidth + } + fieldWidth := 0 + for _, option := range d.options { + width := TaggedStringWidth(option.Text) + if width > fieldWidth { + fieldWidth = width + } + } + return fieldWidth +} + +// AddOption adds a new selectable option to this drop-down. The "selected" +// callback is called when this option was selected. It may be nil. +func (d *DropDown) AddOption(text string, selected func()) *DropDown { + d.options = append(d.options, &dropDownOption{Text: text, Selected: selected}) + d.list.AddItem(d.optionPrefix+text+d.optionSuffix, "", 0, nil) + return d +} + +// SetOptions replaces all current options with the ones provided and installs +// one callback function which is called when one of the options is selected. +// It will be called with the option's text and its index into the options +// slice. The "selected" parameter may be nil. +func (d *DropDown) SetOptions(texts []string, selected func(text string, index int)) *DropDown { + d.list.Clear() + d.options = nil + for index, text := range texts { + func(t string, i int) { + d.AddOption(text, nil) + }(text, index) + } + d.selected = selected + return d +} + +// SetSelectedFunc sets a handler which is called when the user changes the +// drop-down's option. This handler will be called in addition and prior to +// an option's optional individual handler. The handler is provided with the +// selected option's text and index. If "no option" was selected, these values +// are an empty string and -1. +func (d *DropDown) SetSelectedFunc(handler func(text string, index int)) *DropDown { + d.selected = handler + return d +} + +// SetDoneFunc sets a handler which is called when the user is done selecting +// options. The callback function is provided with the key that was pressed, +// which is one of the following: +// +// - KeyEscape: Abort selection. +// - KeyTab: Move to the next field. +// - KeyBacktab: Move to the previous field. +func (d *DropDown) SetDoneFunc(handler func(key tcell.Key)) *DropDown { + d.done = handler + return d +} + +// SetFinishedFunc sets a callback invoked when the user leaves this form item. +func (d *DropDown) SetFinishedFunc(handler func(key tcell.Key)) FormItem { + d.finished = handler + return d +} + +// Draw draws this primitive onto the screen. +func (d *DropDown) Draw(screen tcell.Screen) { + d.Box.DrawForSubclass(screen, d) + + // Prepare. + x, y, width, height := d.GetInnerRect() + rightLimit := x + width + if height < 1 || rightLimit <= x { + return + } + + // Draw label. + if d.labelWidth > 0 { + labelWidth := d.labelWidth + if labelWidth > rightLimit-x { + labelWidth = rightLimit - x + } + Print(screen, d.label, x, y, labelWidth, AlignLeft, d.labelColor) + x += labelWidth + } else { + _, drawnWidth := Print(screen, d.label, x, y, rightLimit-x, AlignLeft, d.labelColor) + x += drawnWidth + } + + // What's the longest option text? + maxWidth := 0 + optionWrapWidth := TaggedStringWidth(d.optionPrefix + d.optionSuffix) + for _, option := range d.options { + strWidth := TaggedStringWidth(option.Text) + optionWrapWidth + if strWidth > maxWidth { + maxWidth = strWidth + } + } + + // Draw selection area. + fieldWidth := d.fieldWidth + if fieldWidth == 0 { + fieldWidth = maxWidth + if d.currentOption < 0 { + noSelectionWidth := TaggedStringWidth(d.noSelection) + if noSelectionWidth > fieldWidth { + fieldWidth = noSelectionWidth + } + } else if d.currentOption < len(d.options) { + currentOptionWidth := TaggedStringWidth(d.currentOptionPrefix + d.options[d.currentOption].Text + d.currentOptionSuffix) + if currentOptionWidth > fieldWidth { + fieldWidth = currentOptionWidth + } + } + } + if rightLimit-x < fieldWidth { + fieldWidth = rightLimit - x + } + fieldStyle := tcell.StyleDefault.Background(d.fieldBackgroundColor) + if d.HasFocus() && !d.open { + fieldStyle = fieldStyle.Background(d.fieldTextColor) + } + for index := 0; index < fieldWidth; index++ { + screen.SetContent(x+index, y, ' ', nil, fieldStyle) + } + + // Draw selected text. + if d.open && len(d.prefix) > 0 { + // Show the prefix. + currentOptionPrefixWidth := TaggedStringWidth(d.currentOptionPrefix) + prefixWidth := stringWidth(d.prefix) + listItemText := d.options[d.list.GetCurrentItem()].Text + Print(screen, d.currentOptionPrefix, x, y, fieldWidth, AlignLeft, d.fieldTextColor) + Print(screen, d.prefix, x+currentOptionPrefixWidth, y, fieldWidth-currentOptionPrefixWidth, AlignLeft, d.prefixTextColor) + if len(d.prefix) < len(listItemText) { + Print(screen, listItemText[len(d.prefix):]+d.currentOptionSuffix, x+prefixWidth+currentOptionPrefixWidth, y, fieldWidth-prefixWidth-currentOptionPrefixWidth, AlignLeft, d.fieldTextColor) + } + } else { + color := d.fieldTextColor + text := d.noSelection + if d.currentOption >= 0 && d.currentOption < len(d.options) { + text = d.currentOptionPrefix + d.options[d.currentOption].Text + d.currentOptionSuffix + } + // Just show the current selection. + if d.HasFocus() && !d.open { + color = d.fieldBackgroundColor + } + Print(screen, text, x, y, fieldWidth, AlignLeft, color) + } + + // Draw options list. + if d.HasFocus() && d.open { + // We prefer to drop down but if there is no space, maybe drop up? + lx := x + ly := y + 1 + lwidth := maxWidth + lheight := len(d.options) + _, sheight := screen.Size() + if ly+lheight >= sheight && ly-2 > lheight-ly { + ly = y - lheight + if ly < 0 { + ly = 0 + } + } + if ly+lheight >= sheight { + lheight = sheight - ly + } + d.list.SetRect(lx, ly, lwidth, lheight) + d.list.Draw(screen) + } +} + +// InputHandler returns the handler for this primitive. +func (d *DropDown) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { + return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { + // If the list has focus, let it process its own key events. + if d.list.HasFocus() { + if handler := d.list.InputHandler(); handler != nil { + handler(event, setFocus) + } + return + } + + // Process key event. + switch key := event.Key(); key { + case tcell.KeyEnter, tcell.KeyRune, tcell.KeyDown: + d.prefix = "" + + // If the first key was a letter already, it becomes part of the prefix. + if r := event.Rune(); key == tcell.KeyRune && r != ' ' { + d.prefix += string(r) + d.evalPrefix() + } + + d.openList(setFocus) + case tcell.KeyEscape, tcell.KeyTab, tcell.KeyBacktab: + if d.done != nil { + d.done(key) + } + if d.finished != nil { + d.finished(key) + } + } + }) +} + +// evalPrefix selects an item in the drop-down list based on the current prefix. +func (d *DropDown) evalPrefix() { + if len(d.prefix) > 0 { + for index, option := range d.options { + if strings.HasPrefix(strings.ToLower(option.Text), d.prefix) { + d.list.SetCurrentItem(index) + return + } + } + + // Prefix does not match any item. Remove last rune. + r := []rune(d.prefix) + d.prefix = string(r[:len(r)-1]) + } +} + +// openList hands control over to the embedded List primitive. +func (d *DropDown) openList(setFocus func(Primitive)) { + d.open = true + optionBefore := d.currentOption + + d.list.SetSelectedFunc(func(index int, mainText, secondaryText string, shortcut rune) { + if d.dragging { + return // If we're dragging the mouse, we don't want to trigger any events. + } + + // An option was selected. Close the list again. + d.currentOption = index + d.closeList(setFocus) + + // Trigger "selected" event. + if d.selected != nil { + d.selected(d.options[d.currentOption].Text, d.currentOption) + } + if d.options[d.currentOption].Selected != nil { + d.options[d.currentOption].Selected() + } + }).SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + if event.Key() == tcell.KeyRune { + d.prefix += string(event.Rune()) + d.evalPrefix() + } else if event.Key() == tcell.KeyBackspace || event.Key() == tcell.KeyBackspace2 { + if len(d.prefix) > 0 { + r := []rune(d.prefix) + d.prefix = string(r[:len(r)-1]) + } + d.evalPrefix() + } else if event.Key() == tcell.KeyEscape { + d.currentOption = optionBefore + d.closeList(setFocus) + } else { + d.prefix = "" + } + + return event + }) + + setFocus(d.list) +} + +// closeList closes the embedded List element by hiding it and removing focus +// from it. +func (d *DropDown) closeList(setFocus func(Primitive)) { + d.open = false + if d.list.HasFocus() { + setFocus(d) + } +} + +// Focus is called by the application when the primitive receives focus. +func (d *DropDown) Focus(delegate func(p Primitive)) { + d.Box.Focus(delegate) + if d.open { + delegate(d.list) + } +} + +// HasFocus returns whether or not this primitive has focus. +func (d *DropDown) HasFocus() bool { + if d.open { + return d.list.HasFocus() + } + return d.hasFocus +} + +// MouseHandler returns the mouse handler for this primitive. +func (d *DropDown) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + return d.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + // Was the mouse event in the drop-down box itself (or on its label)? + x, y := event.Position() + rectX, rectY, rectWidth, _ := d.GetInnerRect() + inRect := y == rectY && x >= rectX && x < rectX+rectWidth + if !d.open && !inRect { + return d.InRect(x, y), nil // No, and it's not expanded either. Ignore. + } + + // Handle dragging. Clicks are implicitly handled by this logic. + switch action { + case MouseLeftDown: + consumed = d.open || inRect + capture = d + if !d.open { + d.openList(setFocus) + d.dragging = true + } else if consumed, _ := d.list.MouseHandler()(MouseLeftClick, event, setFocus); !consumed { + d.closeList(setFocus) // Close drop-down if clicked outside of it. + } + case MouseMove: + if d.dragging { + // We pretend it's a left click so we can see the selection during + // dragging. Because we don't act upon it, it's not a problem. + d.list.MouseHandler()(MouseLeftClick, event, setFocus) + consumed = true + capture = d + } + case MouseLeftUp: + if d.dragging { + d.dragging = false + d.list.MouseHandler()(MouseLeftClick, event, setFocus) + consumed = true + } + } + + return + }) +} diff --git a/vendor/github.com/rivo/tview/flex.go b/vendor/github.com/rivo/tview/flex.go new file mode 100644 index 0000000000..b4fb2daa27 --- /dev/null +++ b/vendor/github.com/rivo/tview/flex.go @@ -0,0 +1,239 @@ +package tview + +import ( + "github.com/gdamore/tcell/v2" +) + +// Configuration values. +const ( + FlexRow = iota + FlexColumn +) + +// flexItem holds layout options for one item. +type flexItem struct { + Item Primitive // The item to be positioned. May be nil for an empty item. + FixedSize int // The item's fixed size which may not be changed, 0 if it has no fixed size. + Proportion int // The item's proportion. + Focus bool // Whether or not this item attracts the layout's focus. +} + +// Flex is a basic implementation of the Flexbox layout. The contained +// primitives are arranged horizontally or vertically. The way they are +// distributed along that dimension depends on their layout settings, which is +// either a fixed length or a proportional length. See AddItem() for details. +// +// See https://github.com/rivo/tview/wiki/Flex for an example. +type Flex struct { + *Box + + // The items to be positioned. + items []*flexItem + + // FlexRow or FlexColumn. + direction int + + // If set to true, Flex will use the entire screen as its available space + // instead its box dimensions. + fullScreen bool +} + +// NewFlex returns a new flexbox layout container with no primitives and its +// direction set to FlexColumn. To add primitives to this layout, see AddItem(). +// To change the direction, see SetDirection(). +// +// Note that Box, the superclass of Flex, will not clear its contents so that +// any nil flex items will leave their background unchanged. To clear a Flex's +// background before any items are drawn, set it to a box with the desired +// color: +// +// flex.Box = NewBox() +func NewFlex() *Flex { + f := &Flex{ + direction: FlexColumn, + } + f.Box = NewBox() + f.Box.dontClear = true + return f +} + +// SetDirection sets the direction in which the contained primitives are +// distributed. This can be either FlexColumn (default) or FlexRow. +func (f *Flex) SetDirection(direction int) *Flex { + f.direction = direction + return f +} + +// SetFullScreen sets the flag which, when true, causes the flex layout to use +// the entire screen space instead of whatever size it is currently assigned to. +func (f *Flex) SetFullScreen(fullScreen bool) *Flex { + f.fullScreen = fullScreen + return f +} + +// AddItem adds a new item to the container. The "fixedSize" argument is a width +// or height that may not be changed by the layout algorithm. A value of 0 means +// that its size is flexible and may be changed. The "proportion" argument +// defines the relative size of the item compared to other flexible-size items. +// For example, items with a proportion of 2 will be twice as large as items +// with a proportion of 1. The proportion must be at least 1 if fixedSize == 0 +// (ignored otherwise). +// +// If "focus" is set to true, the item will receive focus when the Flex +// primitive receives focus. If multiple items have the "focus" flag set to +// true, the first one will receive focus. +// +// You can provide a nil value for the primitive. This will still consume screen +// space but nothing will be drawn. +func (f *Flex) AddItem(item Primitive, fixedSize, proportion int, focus bool) *Flex { + f.items = append(f.items, &flexItem{Item: item, FixedSize: fixedSize, Proportion: proportion, Focus: focus}) + return f +} + +// RemoveItem removes all items for the given primitive from the container, +// keeping the order of the remaining items intact. +func (f *Flex) RemoveItem(p Primitive) *Flex { + for index := len(f.items) - 1; index >= 0; index-- { + if f.items[index].Item == p { + f.items = append(f.items[:index], f.items[index+1:]...) + } + } + return f +} + +// Clear removes all items from the container. +func (f *Flex) Clear() *Flex { + f.items = nil + return f +} + +// ResizeItem sets a new size for the item(s) with the given primitive. If there +// are multiple Flex items with the same primitive, they will all receive the +// same size. For details regarding the size parameters, see AddItem(). +func (f *Flex) ResizeItem(p Primitive, fixedSize, proportion int) *Flex { + for _, item := range f.items { + if item.Item == p { + item.FixedSize = fixedSize + item.Proportion = proportion + } + } + return f +} + +// Draw draws this primitive onto the screen. +func (f *Flex) Draw(screen tcell.Screen) { + f.Box.DrawForSubclass(screen, f) + + // Calculate size and position of the items. + + // Do we use the entire screen? + if f.fullScreen { + width, height := screen.Size() + f.SetRect(0, 0, width, height) + } + + // How much space can we distribute? + x, y, width, height := f.GetInnerRect() + var proportionSum int + distSize := width + if f.direction == FlexRow { + distSize = height + } + for _, item := range f.items { + if item.FixedSize > 0 { + distSize -= item.FixedSize + } else { + proportionSum += item.Proportion + } + } + + // Calculate positions and draw items. + pos := x + if f.direction == FlexRow { + pos = y + } + for _, item := range f.items { + size := item.FixedSize + if size <= 0 { + if proportionSum > 0 { + size = distSize * item.Proportion / proportionSum + distSize -= size + proportionSum -= item.Proportion + } else { + size = 0 + } + } + if item.Item != nil { + if f.direction == FlexColumn { + item.Item.SetRect(pos, y, size, height) + } else { + item.Item.SetRect(x, pos, width, size) + } + } + pos += size + + if item.Item != nil { + if item.Item.HasFocus() { + defer item.Item.Draw(screen) + } else { + item.Item.Draw(screen) + } + } + } +} + +// Focus is called when this primitive receives focus. +func (f *Flex) Focus(delegate func(p Primitive)) { + for _, item := range f.items { + if item.Item != nil && item.Focus { + delegate(item.Item) + return + } + } +} + +// HasFocus returns whether or not this primitive has focus. +func (f *Flex) HasFocus() bool { + for _, item := range f.items { + if item.Item != nil && item.Item.HasFocus() { + return true + } + } + return false +} + +// MouseHandler returns the mouse handler for this primitive. +func (f *Flex) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + return f.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + if !f.InRect(event.Position()) { + return false, nil + } + + // Pass mouse events along to the first child item that takes it. + for _, item := range f.items { + if item.Item == nil { + continue + } + consumed, capture = item.Item.MouseHandler()(action, event, setFocus) + if consumed { + return + } + } + + return + }) +} + +// InputHandler returns the handler for this primitive. +func (f *Flex) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { + return f.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { + for _, item := range f.items { + if item.Item != nil && item.Item.HasFocus() { + if handler := item.Item.InputHandler(); handler != nil { + handler(event, setFocus) + return + } + } + } + }) +} diff --git a/vendor/github.com/rivo/tview/form.go b/vendor/github.com/rivo/tview/form.go new file mode 100644 index 0000000000..018277a9f8 --- /dev/null +++ b/vendor/github.com/rivo/tview/form.go @@ -0,0 +1,679 @@ +package tview + +import ( + "github.com/gdamore/tcell/v2" +) + +// DefaultFormFieldWidth is the default field screen width of form elements +// whose field width is flexible (0). This is used in the Form class for +// horizontal layouts. +var DefaultFormFieldWidth = 10 + +// FormItem is the interface all form items must implement to be able to be +// included in a form. +type FormItem interface { + Primitive + + // GetLabel returns the item's label text. + GetLabel() string + + // SetFormAttributes sets a number of item attributes at once. + SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem + + // GetFieldWidth returns the width of the form item's field (the area which + // is manipulated by the user) in number of screen cells. A value of 0 + // indicates the the field width is flexible and may use as much space as + // required. + GetFieldWidth() int + + // SetFinishedFunc sets the handler function for when the user finished + // entering data into the item. The handler may receive events for the + // Enter key (we're done), the Escape key (cancel input), the Tab key (move to + // next field), and the Backtab key (move to previous field). + SetFinishedFunc(handler func(key tcell.Key)) FormItem +} + +// Form allows you to combine multiple one-line form elements into a vertical +// or horizontal layout. Form elements include types such as InputField or +// Checkbox. These elements can be optionally followed by one or more buttons +// for which you can define form-wide actions (e.g. Save, Clear, Cancel). +// +// See https://github.com/rivo/tview/wiki/Form for an example. +type Form struct { + *Box + + // The items of the form (one row per item). + items []FormItem + + // The buttons of the form. + buttons []*Button + + // If set to true, instead of position items and buttons from top to bottom, + // they are positioned from left to right. + horizontal bool + + // The alignment of the buttons. + buttonsAlign int + + // The number of empty rows between items. + itemPadding int + + // The index of the item or button which has focus. (Items are counted first, + // buttons are counted last.) This is only used when the form itself receives + // focus so that the last element that had focus keeps it. + focusedElement int + + // The label color. + labelColor tcell.Color + + // The background color of the input area. + fieldBackgroundColor tcell.Color + + // The text color of the input area. + fieldTextColor tcell.Color + + // The background color of the buttons. + buttonBackgroundColor tcell.Color + + // The color of the button text. + buttonTextColor tcell.Color + + // An optional function which is called when the user hits Escape. + cancel func() +} + +// NewForm returns a new form. +func NewForm() *Form { + box := NewBox().SetBorderPadding(1, 1, 1, 1) + + f := &Form{ + Box: box, + itemPadding: 1, + labelColor: Styles.SecondaryTextColor, + fieldBackgroundColor: Styles.ContrastBackgroundColor, + fieldTextColor: Styles.PrimaryTextColor, + buttonBackgroundColor: Styles.ContrastBackgroundColor, + buttonTextColor: Styles.PrimaryTextColor, + } + + return f +} + +// SetItemPadding sets the number of empty rows between form items for vertical +// layouts and the number of empty cells between form items for horizontal +// layouts. +func (f *Form) SetItemPadding(padding int) *Form { + f.itemPadding = padding + return f +} + +// SetHorizontal sets the direction the form elements are laid out. If set to +// true, instead of positioning them from top to bottom (the default), they are +// positioned from left to right, moving into the next row if there is not +// enough space. +func (f *Form) SetHorizontal(horizontal bool) *Form { + f.horizontal = horizontal + return f +} + +// SetLabelColor sets the color of the labels. +func (f *Form) SetLabelColor(color tcell.Color) *Form { + f.labelColor = color + return f +} + +// SetFieldBackgroundColor sets the background color of the input areas. +func (f *Form) SetFieldBackgroundColor(color tcell.Color) *Form { + f.fieldBackgroundColor = color + return f +} + +// SetFieldTextColor sets the text color of the input areas. +func (f *Form) SetFieldTextColor(color tcell.Color) *Form { + f.fieldTextColor = color + return f +} + +// SetButtonsAlign sets how the buttons align horizontally, one of AlignLeft +// (the default), AlignCenter, and AlignRight. This is only +func (f *Form) SetButtonsAlign(align int) *Form { + f.buttonsAlign = align + return f +} + +// SetButtonBackgroundColor sets the background color of the buttons. +func (f *Form) SetButtonBackgroundColor(color tcell.Color) *Form { + f.buttonBackgroundColor = color + return f +} + +// SetButtonTextColor sets the color of the button texts. +func (f *Form) SetButtonTextColor(color tcell.Color) *Form { + f.buttonTextColor = color + return f +} + +// SetFocus shifts the focus to the form element with the given index, counting +// non-button items first and buttons last. Note that this index is only used +// when the form itself receives focus. +func (f *Form) SetFocus(index int) *Form { + if index < 0 { + f.focusedElement = 0 + } else if index >= len(f.items)+len(f.buttons) { + f.focusedElement = len(f.items) + len(f.buttons) + } else { + f.focusedElement = index + } + return f +} + +// AddInputField adds an input field to the form. It has a label, an optional +// initial value, a field width (a value of 0 extends it as far as possible), +// an optional accept function to validate the item's value (set to nil to +// accept any text), and an (optional) callback function which is invoked when +// the input field's text has changed. +func (f *Form) AddInputField(label, value string, fieldWidth int, accept func(textToCheck string, lastChar rune) bool, changed func(text string)) *Form { + f.items = append(f.items, NewInputField(). + SetLabel(label). + SetText(value). + SetFieldWidth(fieldWidth). + SetAcceptanceFunc(accept). + SetChangedFunc(changed)) + return f +} + +// AddPasswordField adds a password field to the form. This is similar to an +// input field except that the user's input not shown. Instead, a "mask" +// character is displayed. The password field has a label, an optional initial +// value, a field width (a value of 0 extends it as far as possible), and an +// (optional) callback function which is invoked when the input field's text has +// changed. +func (f *Form) AddPasswordField(label, value string, fieldWidth int, mask rune, changed func(text string)) *Form { + if mask == 0 { + mask = '*' + } + f.items = append(f.items, NewInputField(). + SetLabel(label). + SetText(value). + SetFieldWidth(fieldWidth). + SetMaskCharacter(mask). + SetChangedFunc(changed)) + return f +} + +// AddDropDown adds a drop-down element to the form. It has a label, options, +// and an (optional) callback function which is invoked when an option was +// selected. The initial option may be a negative value to indicate that no +// option is currently selected. +func (f *Form) AddDropDown(label string, options []string, initialOption int, selected func(option string, optionIndex int)) *Form { + f.items = append(f.items, NewDropDown(). + SetLabel(label). + SetOptions(options, selected). + SetCurrentOption(initialOption)) + return f +} + +// AddCheckbox adds a checkbox to the form. It has a label, an initial state, +// and an (optional) callback function which is invoked when the state of the +// checkbox was changed by the user. +func (f *Form) AddCheckbox(label string, checked bool, changed func(checked bool)) *Form { + f.items = append(f.items, NewCheckbox(). + SetLabel(label). + SetChecked(checked). + SetChangedFunc(changed)) + return f +} + +// AddButton adds a new button to the form. The "selected" function is called +// when the user selects this button. It may be nil. +func (f *Form) AddButton(label string, selected func()) *Form { + f.buttons = append(f.buttons, NewButton(label).SetSelectedFunc(selected)) + return f +} + +// GetButton returns the button at the specified 0-based index. Note that +// buttons have been specially prepared for this form and modifying some of +// their attributes may have unintended side effects. +func (f *Form) GetButton(index int) *Button { + return f.buttons[index] +} + +// RemoveButton removes the button at the specified position, starting with 0 +// for the button that was added first. +func (f *Form) RemoveButton(index int) *Form { + f.buttons = append(f.buttons[:index], f.buttons[index+1:]...) + return f +} + +// GetButtonCount returns the number of buttons in this form. +func (f *Form) GetButtonCount() int { + return len(f.buttons) +} + +// GetButtonIndex returns the index of the button with the given label, starting +// with 0 for the button that was added first. If no such label was found, -1 +// is returned. +func (f *Form) GetButtonIndex(label string) int { + for index, button := range f.buttons { + if button.GetLabel() == label { + return index + } + } + return -1 +} + +// Clear removes all input elements from the form, including the buttons if +// specified. +func (f *Form) Clear(includeButtons bool) *Form { + f.items = nil + if includeButtons { + f.ClearButtons() + } + f.focusedElement = 0 + return f +} + +// ClearButtons removes all buttons from the form. +func (f *Form) ClearButtons() *Form { + f.buttons = nil + return f +} + +// AddFormItem adds a new item to the form. This can be used to add your own +// objects to the form. Note, however, that the Form class will override some +// of its attributes to make it work in the form context. Specifically, these +// are: +// +// - The label width +// - The label color +// - The background color +// - The field text color +// - The field background color +func (f *Form) AddFormItem(item FormItem) *Form { + f.items = append(f.items, item) + return f +} + +// GetFormItemCount returns the number of items in the form (not including the +// buttons). +func (f *Form) GetFormItemCount() int { + return len(f.items) +} + +// GetFormItem returns the form item at the given position, starting with index +// 0. Elements are referenced in the order they were added. Buttons are not +// included. +func (f *Form) GetFormItem(index int) FormItem { + return f.items[index] +} + +// RemoveFormItem removes the form element at the given position, starting with +// index 0. Elements are referenced in the order they were added. Buttons are +// not included. +func (f *Form) RemoveFormItem(index int) *Form { + f.items = append(f.items[:index], f.items[index+1:]...) + return f +} + +// GetFormItemByLabel returns the first form element with the given label. If +// no such element is found, nil is returned. Buttons are not searched and will +// therefore not be returned. +func (f *Form) GetFormItemByLabel(label string) FormItem { + for _, item := range f.items { + if item.GetLabel() == label { + return item + } + } + return nil +} + +// GetFormItemIndex returns the index of the first form element with the given +// label. If no such element is found, -1 is returned. Buttons are not searched +// and will therefore not be returned. +func (f *Form) GetFormItemIndex(label string) int { + for index, item := range f.items { + if item.GetLabel() == label { + return index + } + } + return -1 +} + +// GetFocusedItemIndex returns the indices of the form element or button which +// currently has focus. If they don't, -1 is returned resepectively. +func (f *Form) GetFocusedItemIndex() (formItem, button int) { + index := f.focusIndex() + if index < 0 { + return -1, -1 + } + if index < len(f.items) { + return index, -1 + } + return -1, index - len(f.items) +} + +// SetCancelFunc sets a handler which is called when the user hits the Escape +// key. +func (f *Form) SetCancelFunc(callback func()) *Form { + f.cancel = callback + return f +} + +// Draw draws this primitive onto the screen. +func (f *Form) Draw(screen tcell.Screen) { + f.Box.DrawForSubclass(screen, f) + + // Determine the actual item that has focus. + if index := f.focusIndex(); index >= 0 { + f.focusedElement = index + } + + // Determine the dimensions. + x, y, width, height := f.GetInnerRect() + topLimit := y + bottomLimit := y + height + rightLimit := x + width + startX := x + + // Find the longest label. + var maxLabelWidth int + for _, item := range f.items { + labelWidth := TaggedStringWidth(item.GetLabel()) + if labelWidth > maxLabelWidth { + maxLabelWidth = labelWidth + } + } + maxLabelWidth++ // Add one space. + + // Calculate positions of form items. + positions := make([]struct{ x, y, width, height int }, len(f.items)+len(f.buttons)) + var focusedPosition struct{ x, y, width, height int } + for index, item := range f.items { + // Calculate the space needed. + labelWidth := TaggedStringWidth(item.GetLabel()) + var itemWidth int + if f.horizontal { + fieldWidth := item.GetFieldWidth() + if fieldWidth == 0 { + fieldWidth = DefaultFormFieldWidth + } + labelWidth++ + itemWidth = labelWidth + fieldWidth + } else { + // We want all fields to align vertically. + labelWidth = maxLabelWidth + itemWidth = width + } + + // Advance to next line if there is no space. + if f.horizontal && x+labelWidth+1 >= rightLimit { + x = startX + y += 2 + } + + // Adjust the item's attributes. + if x+itemWidth >= rightLimit { + itemWidth = rightLimit - x + } + item.SetFormAttributes( + labelWidth, + f.labelColor, + f.backgroundColor, + f.fieldTextColor, + f.fieldBackgroundColor, + ) + + // Save position. + positions[index].x = x + positions[index].y = y + positions[index].width = itemWidth + positions[index].height = 1 + if item.HasFocus() { + focusedPosition = positions[index] + } + + // Advance to next item. + if f.horizontal { + x += itemWidth + f.itemPadding + } else { + y += 1 + f.itemPadding + } + } + + // How wide are the buttons? + buttonWidths := make([]int, len(f.buttons)) + buttonsWidth := 0 + for index, button := range f.buttons { + w := TaggedStringWidth(button.GetLabel()) + 4 + buttonWidths[index] = w + buttonsWidth += w + 1 + } + buttonsWidth-- + + // Where do we place them? + if !f.horizontal && x+buttonsWidth < rightLimit { + if f.buttonsAlign == AlignRight { + x = rightLimit - buttonsWidth + } else if f.buttonsAlign == AlignCenter { + x = (x + rightLimit - buttonsWidth) / 2 + } + + // In vertical layouts, buttons always appear after an empty line. + if f.itemPadding == 0 { + y++ + } + } + + // Calculate positions of buttons. + for index, button := range f.buttons { + space := rightLimit - x + buttonWidth := buttonWidths[index] + if f.horizontal { + if space < buttonWidth-4 { + x = startX + y += 2 + space = width + } + } else { + if space < 1 { + break // No space for this button anymore. + } + } + if buttonWidth > space { + buttonWidth = space + } + button.SetLabelColor(f.buttonTextColor). + SetLabelColorActivated(f.buttonBackgroundColor). + SetBackgroundColorActivated(f.buttonTextColor). + SetBackgroundColor(f.buttonBackgroundColor) + + buttonIndex := index + len(f.items) + positions[buttonIndex].x = x + positions[buttonIndex].y = y + positions[buttonIndex].width = buttonWidth + positions[buttonIndex].height = 1 + + if button.HasFocus() { + focusedPosition = positions[buttonIndex] + } + + x += buttonWidth + 1 + } + + // Determine vertical offset based on the position of the focused item. + var offset int + if focusedPosition.y+focusedPosition.height > bottomLimit { + offset = focusedPosition.y + focusedPosition.height - bottomLimit + if focusedPosition.y-offset < topLimit { + offset = focusedPosition.y - topLimit + } + } + + // Draw items. + for index, item := range f.items { + // Set position. + y := positions[index].y - offset + height := positions[index].height + item.SetRect(positions[index].x, y, positions[index].width, height) + + // Is this item visible? + if y+height <= topLimit || y >= bottomLimit { + continue + } + + // Draw items with focus last (in case of overlaps). + if item.HasFocus() { + defer item.Draw(screen) + } else { + item.Draw(screen) + } + } + + // Draw buttons. + for index, button := range f.buttons { + // Set position. + buttonIndex := index + len(f.items) + y := positions[buttonIndex].y - offset + height := positions[buttonIndex].height + button.SetRect(positions[buttonIndex].x, y, positions[buttonIndex].width, height) + + // Is this button visible? + if y+height <= topLimit || y >= bottomLimit { + continue + } + + // Draw button. + button.Draw(screen) + } +} + +// Focus is called by the application when the primitive receives focus. +func (f *Form) Focus(delegate func(p Primitive)) { + if len(f.items)+len(f.buttons) == 0 { + f.hasFocus = true + return + } + f.hasFocus = false + + // Hand on the focus to one of our child elements. + if f.focusedElement < 0 || f.focusedElement >= len(f.items)+len(f.buttons) { + f.focusedElement = 0 + } + handler := func(key tcell.Key) { + switch key { + case tcell.KeyTab, tcell.KeyEnter: + f.focusedElement++ + f.Focus(delegate) + case tcell.KeyBacktab: + f.focusedElement-- + if f.focusedElement < 0 { + f.focusedElement = len(f.items) + len(f.buttons) - 1 + } + f.Focus(delegate) + case tcell.KeyEscape: + if f.cancel != nil { + f.cancel() + } else { + f.focusedElement = 0 + f.Focus(delegate) + } + } + } + + if f.focusedElement < len(f.items) { + // We're selecting an item. + item := f.items[f.focusedElement] + item.SetFinishedFunc(handler) + delegate(item) + } else { + // We're selecting a button. + button := f.buttons[f.focusedElement-len(f.items)] + button.SetBlurFunc(handler) + delegate(button) + } +} + +// HasFocus returns whether or not this primitive has focus. +func (f *Form) HasFocus() bool { + if f.hasFocus { + return true + } + return f.focusIndex() >= 0 +} + +// focusIndex returns the index of the currently focused item, counting form +// items first, then buttons. A negative value indicates that no containeed item +// has focus. +func (f *Form) focusIndex() int { + for index, item := range f.items { + if item.HasFocus() { + return index + } + } + for index, button := range f.buttons { + if button.HasFocus() { + return len(f.items) + index + } + } + return -1 +} + +// MouseHandler returns the mouse handler for this primitive. +func (f *Form) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + return f.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + // At the end, update f.focusedElement and prepare current item/button. + defer func() { + if consumed { + index := f.focusIndex() + if index >= 0 { + f.focusedElement = index + } + } + }() + + // Determine items to pass mouse events to. + for _, item := range f.items { + consumed, capture = item.MouseHandler()(action, event, setFocus) + if consumed { + return + } + } + for _, button := range f.buttons { + consumed, capture = button.MouseHandler()(action, event, setFocus) + if consumed { + return + } + } + + // A mouse click anywhere else will return the focus to the last selected + // element. + if action == MouseLeftClick && f.InRect(event.Position()) { + consumed = true + } + + return + }) +} + +// InputHandler returns the handler for this primitive. +func (f *Form) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { + return f.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { + for _, item := range f.items { + if item != nil && item.HasFocus() { + if handler := item.InputHandler(); handler != nil { + handler(event, setFocus) + return + } + } + } + + for _, button := range f.buttons { + if button.HasFocus() { + if handler := button.InputHandler(); handler != nil { + handler(event, setFocus) + return + } + } + } + }) +} diff --git a/vendor/github.com/rivo/tview/frame.go b/vendor/github.com/rivo/tview/frame.go new file mode 100644 index 0000000000..47e56405f8 --- /dev/null +++ b/vendor/github.com/rivo/tview/frame.go @@ -0,0 +1,192 @@ +package tview + +import ( + "github.com/gdamore/tcell/v2" +) + +// frameText holds information about a line of text shown in the frame. +type frameText struct { + Text string // The text to be displayed. + Header bool // true = place in header, false = place in footer. + Align int // One of the Align constants. + Color tcell.Color // The text color. +} + +// Frame is a wrapper which adds space around another primitive. In addition, +// the top area (header) and the bottom area (footer) may also contain text. +// +// See https://github.com/rivo/tview/wiki/Frame for an example. +type Frame struct { + *Box + + // The contained primitive. May be nil. + primitive Primitive + + // The lines of text to be displayed. + text []*frameText + + // Border spacing. + top, bottom, header, footer, left, right int +} + +// NewFrame returns a new frame around the given primitive. The primitive's +// size will be changed to fit within this frame. The primitive may be nil, in +// which case no other primitive is embedded in the frame. +func NewFrame(primitive Primitive) *Frame { + box := NewBox() + + f := &Frame{ + Box: box, + primitive: primitive, + top: 1, + bottom: 1, + header: 1, + footer: 1, + left: 1, + right: 1, + } + + return f +} + +// AddText adds text to the frame. Set "header" to true if the text is to appear +// in the header, above the contained primitive. Set it to false for it to +// appear in the footer, below the contained primitive. "align" must be one of +// the Align constants. Rows in the header are printed top to bottom, rows in +// the footer are printed bottom to top. Note that long text can overlap as +// different alignments will be placed on the same row. +func (f *Frame) AddText(text string, header bool, align int, color tcell.Color) *Frame { + f.text = append(f.text, &frameText{ + Text: text, + Header: header, + Align: align, + Color: color, + }) + return f +} + +// Clear removes all text from the frame. +func (f *Frame) Clear() *Frame { + f.text = nil + return f +} + +// SetBorders sets the width of the frame borders as well as "header" and +// "footer", the vertical space between the header and footer text and the +// contained primitive (does not apply if there is no text). +func (f *Frame) SetBorders(top, bottom, header, footer, left, right int) *Frame { + f.top, f.bottom, f.header, f.footer, f.left, f.right = top, bottom, header, footer, left, right + return f +} + +// Draw draws this primitive onto the screen. +func (f *Frame) Draw(screen tcell.Screen) { + f.Box.DrawForSubclass(screen, f) + + // Calculate start positions. + x, top, width, height := f.GetInnerRect() + bottom := top + height - 1 + x += f.left + top += f.top + bottom -= f.bottom + width -= f.left + f.right + if width <= 0 || top >= bottom { + return // No space left. + } + + // Draw text. + var rows [6]int // top-left, top-center, top-right, bottom-left, bottom-center, bottom-right. + topMax := top + bottomMin := bottom + for _, text := range f.text { + // Where do we place this text? + var y int + if text.Header { + y = top + rows[text.Align] + rows[text.Align]++ + if y >= bottomMin { + continue + } + if y+1 > topMax { + topMax = y + 1 + } + } else { + y = bottom - rows[3+text.Align] + rows[3+text.Align]++ + if y <= topMax { + continue + } + if y-1 < bottomMin { + bottomMin = y - 1 + } + } + + // Draw text. + Print(screen, text.Text, x, y, width, text.Align, text.Color) + } + + // Set the size of the contained primitive. + if f.primitive != nil { + if topMax > top { + top = topMax + f.header + } + if bottomMin < bottom { + bottom = bottomMin - f.footer + } + if top > bottom { + return // No space for the primitive. + } + f.primitive.SetRect(x, top, width, bottom+1-top) + + // Finally, draw the contained primitive. + f.primitive.Draw(screen) + } +} + +// Focus is called when this primitive receives focus. +func (f *Frame) Focus(delegate func(p Primitive)) { + if f.primitive != nil { + delegate(f.primitive) + } else { + f.hasFocus = true + } +} + +// HasFocus returns whether or not this primitive has focus. +func (f *Frame) HasFocus() bool { + if f.primitive == nil { + return f.hasFocus + } + return f.primitive.HasFocus() +} + +// MouseHandler returns the mouse handler for this primitive. +func (f *Frame) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + return f.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + if !f.InRect(event.Position()) { + return false, nil + } + + // Pass mouse events on to contained primitive. + if f.primitive != nil { + return f.primitive.MouseHandler()(action, event, setFocus) + } + + return false, nil + }) +} + +// InputHandler returns the handler for this primitive. +func (f *Frame) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { + return f.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { + if f.primitive == nil { + return + } + if f.primitive.HasFocus() { + if handler := f.primitive.InputHandler(); handler != nil { + handler(event, setFocus) + return + } + } + }) +} diff --git a/vendor/github.com/rivo/tview/go.mod b/vendor/github.com/rivo/tview/go.mod new file mode 100644 index 0000000000..fd5eb3b4f5 --- /dev/null +++ b/vendor/github.com/rivo/tview/go.mod @@ -0,0 +1,13 @@ +module github.com/rivo/tview + +go 1.12 + +require ( + github.com/gdamore/tcell/v2 v2.3.3 + github.com/lucasb-eyer/go-colorful v1.2.0 + github.com/mattn/go-runewidth v0.0.10 + github.com/rivo/uniseg v0.2.0 + golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 // indirect + golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect + golang.org/x/text v0.3.5 // indirect +) diff --git a/vendor/github.com/rivo/tview/go.sum b/vendor/github.com/rivo/tview/go.sum new file mode 100644 index 0000000000..430da15cff --- /dev/null +++ b/vendor/github.com/rivo/tview/go.sum @@ -0,0 +1,26 @@ +github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell/v2 v2.2.0 h1:vSyEgKwraXPSOkvCk7IwOSyX+Pv3V2cV9CikJMXg4U4= +github.com/gdamore/tcell/v2 v2.2.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= +github.com/gdamore/tcell/v2 v2.2.1 h1:Gt8wk0jd5pIK2CyXNo/fqwxNWf726j1lQjEDdfbnqTc= +github.com/gdamore/tcell/v2 v2.2.1/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= +github.com/gdamore/tcell/v2 v2.3.3 h1:RKoI6OcqYrr/Do8yHZklecdGzDTJH9ACKdfECbRdw3M= +github.com/gdamore/tcell/v2 v2.3.3/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/vendor/github.com/rivo/tview/grid.go b/vendor/github.com/rivo/tview/grid.go new file mode 100644 index 0000000000..60417f3754 --- /dev/null +++ b/vendor/github.com/rivo/tview/grid.go @@ -0,0 +1,693 @@ +package tview + +import ( + "math" + + "github.com/gdamore/tcell/v2" +) + +// gridItem represents one primitive and its possible position on a grid. +type gridItem struct { + Item Primitive // The item to be positioned. May be nil for an empty item. + Row, Column int // The top-left grid cell where the item is placed. + Width, Height int // The number of rows and columns the item occupies. + MinGridWidth, MinGridHeight int // The minimum grid width/height for which this item is visible. + Focus bool // Whether or not this item attracts the layout's focus. + + visible bool // Whether or not this item was visible the last time the grid was drawn. + x, y, w, h int // The last position of the item relative to the top-left corner of the grid. Undefined if visible is false. +} + +// Grid is an implementation of a grid-based layout. It works by defining the +// size of the rows and columns, then placing primitives into the grid. +// +// Some settings can lead to the grid exceeding its available space. SetOffset() +// can then be used to scroll in steps of rows and columns. These offset values +// can also be controlled with the arrow keys (or the "g","G", "j", "k", "h", +// and "l" keys) while the grid has focus and none of its contained primitives +// do. +// +// See https://github.com/rivo/tview/wiki/Grid for an example. +type Grid struct { + *Box + + // The items to be positioned. + items []*gridItem + + // The definition of the rows and columns of the grid. See + // SetRows()/SetColumns() for details. + rows, columns []int + + // The minimum sizes for rows and columns. + minWidth, minHeight int + + // The size of the gaps between neighboring primitives. This is automatically + // set to 1 if borders is true. + gapRows, gapColumns int + + // The number of rows and columns skipped before drawing the top-left corner + // of the grid. + rowOffset, columnOffset int + + // Whether or not borders are drawn around grid items. If this is set to true, + // a gap size of 1 is automatically assumed (which is filled with the border + // graphics). + borders bool + + // The color of the borders around grid items. + bordersColor tcell.Color +} + +// NewGrid returns a new grid-based layout container with no initial primitives. +// +// Note that Box, the superclass of Grid, will be transparent so that any grid +// areas not covered by any primitives will leave their background unchanged. To +// clear a Grid's background before any items are drawn, reset its Box to one +// with the desired color: +// +// grid.Box = NewBox() +func NewGrid() *Grid { + g := &Grid{ + bordersColor: Styles.GraphicsColor, + } + g.Box = NewBox() + g.Box.dontClear = true + return g +} + +// SetColumns defines how the columns of the grid are distributed. Each value +// defines the size of one column, starting with the leftmost column. Values +// greater 0 represent absolute column widths (gaps not included). Values less +// or equal 0 represent proportional column widths or fractions of the remaining +// free space, where 0 is treated the same as -1. That is, a column with a value +// of -3 will have three times the width of a column with a value of -1 (or 0). +// The minimum width set with SetMinSize() is always observed. +// +// Primitives may extend beyond the columns defined explicitly with this +// function. A value of 0 is assumed for any undefined column. In fact, if you +// never call this function, all columns occupied by primitives will have the +// same width. On the other hand, unoccupied columns defined with this function +// will always take their place. +// +// Assuming a total width of the grid of 100 cells and a minimum width of 0, the +// following call will result in columns with widths of 30, 10, 15, 15, and 30 +// cells: +// +// grid.SetColumns(30, 10, -1, -1, -2) +// +// If a primitive were then placed in the 6th and 7th column, the resulting +// widths would be: 30, 10, 10, 10, 20, 10, and 10 cells. +// +// If you then called SetMinSize() as follows: +// +// grid.SetMinSize(15, 20) +// +// The resulting widths would be: 30, 15, 15, 15, 20, 15, and 15 cells, a total +// of 125 cells, 25 cells wider than the available grid width. +func (g *Grid) SetColumns(columns ...int) *Grid { + g.columns = columns + return g +} + +// SetRows defines how the rows of the grid are distributed. These values behave +// the same as the column values provided with SetColumns(), see there for a +// definition and examples. +// +// The provided values correspond to row heights, the first value defining +// the height of the topmost row. +func (g *Grid) SetRows(rows ...int) *Grid { + g.rows = rows + return g +} + +// SetSize is a shortcut for SetRows() and SetColumns() where all row and column +// values are set to the given size values. See SetColumns() for details on sizes. +func (g *Grid) SetSize(numRows, numColumns, rowSize, columnSize int) *Grid { + g.rows = make([]int, numRows) + for index := range g.rows { + g.rows[index] = rowSize + } + g.columns = make([]int, numColumns) + for index := range g.columns { + g.columns[index] = columnSize + } + return g +} + +// SetMinSize sets an absolute minimum width for rows and an absolute minimum +// height for columns. Panics if negative values are provided. +func (g *Grid) SetMinSize(row, column int) *Grid { + if row < 0 || column < 0 { + panic("Invalid minimum row/column size") + } + g.minHeight, g.minWidth = row, column + return g +} + +// SetGap sets the size of the gaps between neighboring primitives on the grid. +// If borders are drawn (see SetBorders()), these values are ignored and a gap +// of 1 is assumed. Panics if negative values are provided. +func (g *Grid) SetGap(row, column int) *Grid { + if row < 0 || column < 0 { + panic("Invalid gap size") + } + g.gapRows, g.gapColumns = row, column + return g +} + +// SetBorders sets whether or not borders are drawn around grid items. Setting +// this value to true will cause the gap values (see SetGap()) to be ignored and +// automatically assumed to be 1 where the border graphics are drawn. +func (g *Grid) SetBorders(borders bool) *Grid { + g.borders = borders + return g +} + +// SetBordersColor sets the color of the item borders. +func (g *Grid) SetBordersColor(color tcell.Color) *Grid { + g.bordersColor = color + return g +} + +// AddItem adds a primitive and its position to the grid. The top-left corner +// of the primitive will be located in the top-left corner of the grid cell at +// the given row and column and will span "rowSpan" rows and "colSpan" columns. +// For example, for a primitive to occupy rows 2, 3, and 4 and columns 5 and 6: +// +// grid.AddItem(p, 2, 5, 3, 2, 0, 0, true) +// +// If rowSpan or colSpan is 0, the primitive will not be drawn. +// +// You can add the same primitive multiple times with different grid positions. +// The minGridWidth and minGridHeight values will then determine which of those +// positions will be used. This is similar to CSS media queries. These minimum +// values refer to the overall size of the grid. If multiple items for the same +// primitive apply, the one that has at least one highest minimum value will be +// used, or the primitive added last if those values are the same. Example: +// +// grid.AddItem(p, 0, 0, 0, 0, 0, 0, true). // Hide in small grids. +// AddItem(p, 0, 0, 1, 2, 100, 0, true). // One-column layout for medium grids. +// AddItem(p, 1, 1, 3, 2, 300, 0, true) // Multi-column layout for large grids. +// +// To use the same grid layout for all sizes, simply set minGridWidth and +// minGridHeight to 0. +// +// If the item's focus is set to true, it will receive focus when the grid +// receives focus. If there are multiple items with a true focus flag, the last +// visible one that was added will receive focus. +func (g *Grid) AddItem(p Primitive, row, column, rowSpan, colSpan, minGridHeight, minGridWidth int, focus bool) *Grid { + g.items = append(g.items, &gridItem{ + Item: p, + Row: row, + Column: column, + Height: rowSpan, + Width: colSpan, + MinGridHeight: minGridHeight, + MinGridWidth: minGridWidth, + Focus: focus, + }) + return g +} + +// RemoveItem removes all items for the given primitive from the grid, keeping +// the order of the remaining items intact. +func (g *Grid) RemoveItem(p Primitive) *Grid { + for index := len(g.items) - 1; index >= 0; index-- { + if g.items[index].Item == p { + g.items = append(g.items[:index], g.items[index+1:]...) + } + } + return g +} + +// Clear removes all items from the grid. +func (g *Grid) Clear() *Grid { + g.items = nil + return g +} + +// SetOffset sets the number of rows and columns which are skipped before +// drawing the first grid cell in the top-left corner. As the grid will never +// completely move off the screen, these values may be adjusted the next time +// the grid is drawn. The actual position of the grid may also be adjusted such +// that contained primitives that have focus remain visible. +func (g *Grid) SetOffset(rows, columns int) *Grid { + g.rowOffset, g.columnOffset = rows, columns + return g +} + +// GetOffset returns the current row and column offset (see SetOffset() for +// details). +func (g *Grid) GetOffset() (rows, columns int) { + return g.rowOffset, g.columnOffset +} + +// Focus is called when this primitive receives focus. +func (g *Grid) Focus(delegate func(p Primitive)) { + for _, item := range g.items { + if item.Focus { + delegate(item.Item) + return + } + } + g.hasFocus = true +} + +// Blur is called when this primitive loses focus. +func (g *Grid) Blur() { + g.hasFocus = false +} + +// HasFocus returns whether or not this primitive has focus. +func (g *Grid) HasFocus() bool { + for _, item := range g.items { + if item.visible && item.Item.HasFocus() { + return true + } + } + return g.hasFocus +} + +// InputHandler returns the handler for this primitive. +func (g *Grid) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { + return g.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { + if !g.hasFocus { + // Pass event on to child primitive. + for _, item := range g.items { + if item != nil && item.Item.HasFocus() { + if handler := item.Item.InputHandler(); handler != nil { + handler(event, setFocus) + return + } + } + } + return + } + + // Process our own key events if we have direct focus. + switch event.Key() { + case tcell.KeyRune: + switch event.Rune() { + case 'g': + g.rowOffset, g.columnOffset = 0, 0 + case 'G': + g.rowOffset = math.MaxInt32 + case 'j': + g.rowOffset++ + case 'k': + g.rowOffset-- + case 'h': + g.columnOffset-- + case 'l': + g.columnOffset++ + } + case tcell.KeyHome: + g.rowOffset, g.columnOffset = 0, 0 + case tcell.KeyEnd: + g.rowOffset = math.MaxInt32 + case tcell.KeyUp: + g.rowOffset-- + case tcell.KeyDown: + g.rowOffset++ + case tcell.KeyLeft: + g.columnOffset-- + case tcell.KeyRight: + g.columnOffset++ + } + }) +} + +// Draw draws this primitive onto the screen. +func (g *Grid) Draw(screen tcell.Screen) { + g.Box.DrawForSubclass(screen, g) + x, y, width, height := g.GetInnerRect() + screenWidth, screenHeight := screen.Size() + + // Make a list of items which apply. + items := make(map[Primitive]*gridItem) + for _, item := range g.items { + item.visible = false + if item.Width <= 0 || item.Height <= 0 || width < item.MinGridWidth || height < item.MinGridHeight { + continue + } + previousItem, ok := items[item.Item] + if ok && item.MinGridWidth < previousItem.MinGridWidth && item.MinGridHeight < previousItem.MinGridHeight { + continue + } + items[item.Item] = item + } + + // How many rows and columns do we have? + rows := len(g.rows) + columns := len(g.columns) + for _, item := range items { + rowEnd := item.Row + item.Height + if rowEnd > rows { + rows = rowEnd + } + columnEnd := item.Column + item.Width + if columnEnd > columns { + columns = columnEnd + } + } + if rows == 0 || columns == 0 { + return // No content. + } + + // Where are they located? + rowPos := make([]int, rows) + rowHeight := make([]int, rows) + columnPos := make([]int, columns) + columnWidth := make([]int, columns) + + // How much space do we distribute? + remainingWidth := width + remainingHeight := height + proportionalWidth := 0 + proportionalHeight := 0 + for index, row := range g.rows { + if row > 0 { + if row < g.minHeight { + row = g.minHeight + } + remainingHeight -= row + rowHeight[index] = row + } else if row == 0 { + proportionalHeight++ + } else { + proportionalHeight += -row + } + } + for index, column := range g.columns { + if column > 0 { + if column < g.minWidth { + column = g.minWidth + } + remainingWidth -= column + columnWidth[index] = column + } else if column == 0 { + proportionalWidth++ + } else { + proportionalWidth += -column + } + } + if g.borders { + remainingHeight -= rows + 1 + remainingWidth -= columns + 1 + } else { + remainingHeight -= (rows - 1) * g.gapRows + remainingWidth -= (columns - 1) * g.gapColumns + } + if rows > len(g.rows) { + proportionalHeight += rows - len(g.rows) + } + if columns > len(g.columns) { + proportionalWidth += columns - len(g.columns) + } + + // Distribute proportional rows/columns. + for index := 0; index < rows; index++ { + row := 0 + if index < len(g.rows) { + row = g.rows[index] + } + if row > 0 { + continue // Not proportional. We already know the width. + } else if row == 0 { + row = 1 + } else { + row = -row + } + rowAbs := row * remainingHeight / proportionalHeight + remainingHeight -= rowAbs + proportionalHeight -= row + if rowAbs < g.minHeight { + rowAbs = g.minHeight + } + rowHeight[index] = rowAbs + } + for index := 0; index < columns; index++ { + column := 0 + if index < len(g.columns) { + column = g.columns[index] + } + if column > 0 { + continue // Not proportional. We already know the height. + } else if column == 0 { + column = 1 + } else { + column = -column + } + columnAbs := column * remainingWidth / proportionalWidth + remainingWidth -= columnAbs + proportionalWidth -= column + if columnAbs < g.minWidth { + columnAbs = g.minWidth + } + columnWidth[index] = columnAbs + } + + // Calculate row/column positions. + var columnX, rowY int + if g.borders { + columnX++ + rowY++ + } + for index, row := range rowHeight { + rowPos[index] = rowY + gap := g.gapRows + if g.borders { + gap = 1 + } + rowY += row + gap + } + for index, column := range columnWidth { + columnPos[index] = columnX + gap := g.gapColumns + if g.borders { + gap = 1 + } + columnX += column + gap + } + + // Calculate primitive positions. + var focus *gridItem // The item which has focus. + for primitive, item := range items { + px := columnPos[item.Column] + py := rowPos[item.Row] + var pw, ph int + for index := 0; index < item.Height; index++ { + ph += rowHeight[item.Row+index] + } + for index := 0; index < item.Width; index++ { + pw += columnWidth[item.Column+index] + } + if g.borders { + pw += item.Width - 1 + ph += item.Height - 1 + } else { + pw += (item.Width - 1) * g.gapColumns + ph += (item.Height - 1) * g.gapRows + } + item.x, item.y, item.w, item.h = px, py, pw, ph + item.visible = true + if primitive.HasFocus() { + focus = item + } + } + + // Calculate screen offsets. + var offsetX, offsetY int + add := 1 + if !g.borders { + add = g.gapRows + } + for index, height := range rowHeight { + if index >= g.rowOffset { + break + } + offsetY += height + add + } + if !g.borders { + add = g.gapColumns + } + for index, width := range columnWidth { + if index >= g.columnOffset { + break + } + offsetX += width + add + } + + // Line up the last row/column with the end of the available area. + var border int + if g.borders { + border = 1 + } + last := len(rowPos) - 1 + if rowPos[last]+rowHeight[last]+border-offsetY < height { + offsetY = rowPos[last] - height + rowHeight[last] + border + } + last = len(columnPos) - 1 + if columnPos[last]+columnWidth[last]+border-offsetX < width { + offsetX = columnPos[last] - width + columnWidth[last] + border + } + + // The focused item must be within the visible area. + if focus != nil { + if focus.y+focus.h-offsetY >= height { + offsetY = focus.y - height + focus.h + } + if focus.y-offsetY < 0 { + offsetY = focus.y + } + if focus.x+focus.w-offsetX >= width { + offsetX = focus.x - width + focus.w + } + if focus.x-offsetX < 0 { + offsetX = focus.x + } + } + + // Adjust row/column offsets based on this value. + var from, to int + for index, pos := range rowPos { + if pos-offsetY < 0 { + from = index + 1 + } + if pos-offsetY < height { + to = index + } + } + if g.rowOffset < from { + g.rowOffset = from + } + if g.rowOffset > to { + g.rowOffset = to + } + from, to = 0, 0 + for index, pos := range columnPos { + if pos-offsetX < 0 { + from = index + 1 + } + if pos-offsetX < width { + to = index + } + } + if g.columnOffset < from { + g.columnOffset = from + } + if g.columnOffset > to { + g.columnOffset = to + } + + // Draw primitives and borders. + borderStyle := tcell.StyleDefault.Background(g.backgroundColor).Foreground(g.bordersColor) + for primitive, item := range items { + // Final primitive position. + if !item.visible { + continue + } + item.x -= offsetX + item.y -= offsetY + if item.x >= width || item.x+item.w <= 0 || item.y >= height || item.y+item.h <= 0 { + item.visible = false + continue + } + if item.x+item.w > width { + item.w = width - item.x + } + if item.y+item.h > height { + item.h = height - item.y + } + if item.x < 0 { + item.w += item.x + item.x = 0 + } + if item.y < 0 { + item.h += item.y + item.y = 0 + } + if item.w <= 0 || item.h <= 0 { + item.visible = false + continue + } + item.x += x + item.y += y + primitive.SetRect(item.x, item.y, item.w, item.h) + + // Draw primitive. + if item == focus { + defer primitive.Draw(screen) + } else { + primitive.Draw(screen) + } + + // Draw border around primitive. + if g.borders { + for bx := item.x; bx < item.x+item.w; bx++ { // Top/bottom lines. + if bx < 0 || bx >= screenWidth { + continue + } + by := item.y - 1 + if by >= 0 && by < screenHeight { + PrintJoinedSemigraphics(screen, bx, by, Borders.Horizontal, borderStyle) + } + by = item.y + item.h + if by >= 0 && by < screenHeight { + PrintJoinedSemigraphics(screen, bx, by, Borders.Horizontal, borderStyle) + } + } + for by := item.y; by < item.y+item.h; by++ { // Left/right lines. + if by < 0 || by >= screenHeight { + continue + } + bx := item.x - 1 + if bx >= 0 && bx < screenWidth { + PrintJoinedSemigraphics(screen, bx, by, Borders.Vertical, borderStyle) + } + bx = item.x + item.w + if bx >= 0 && bx < screenWidth { + PrintJoinedSemigraphics(screen, bx, by, Borders.Vertical, borderStyle) + } + } + bx, by := item.x-1, item.y-1 // Top-left corner. + if bx >= 0 && bx < screenWidth && by >= 0 && by < screenHeight { + PrintJoinedSemigraphics(screen, bx, by, Borders.TopLeft, borderStyle) + } + bx, by = item.x+item.w, item.y-1 // Top-right corner. + if bx >= 0 && bx < screenWidth && by >= 0 && by < screenHeight { + PrintJoinedSemigraphics(screen, bx, by, Borders.TopRight, borderStyle) + } + bx, by = item.x-1, item.y+item.h // Bottom-left corner. + if bx >= 0 && bx < screenWidth && by >= 0 && by < screenHeight { + PrintJoinedSemigraphics(screen, bx, by, Borders.BottomLeft, borderStyle) + } + bx, by = item.x+item.w, item.y+item.h // Bottom-right corner. + if bx >= 0 && bx < screenWidth && by >= 0 && by < screenHeight { + PrintJoinedSemigraphics(screen, bx, by, Borders.BottomRight, borderStyle) + } + } + } +} + +// MouseHandler returns the mouse handler for this primitive. +func (g *Grid) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + return g.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + if !g.InRect(event.Position()) { + return false, nil + } + + // Pass mouse events along to the first child item that takes it. + for _, item := range g.items { + if item.Item == nil { + continue + } + consumed, capture = item.Item.MouseHandler()(action, event, setFocus) + if consumed { + return + } + } + + return + }) +} diff --git a/vendor/github.com/rivo/tview/inputfield.go b/vendor/github.com/rivo/tview/inputfield.go new file mode 100644 index 0000000000..d1e33942dc --- /dev/null +++ b/vendor/github.com/rivo/tview/inputfield.go @@ -0,0 +1,646 @@ +package tview + +import ( + "math" + "regexp" + "strings" + "sync" + "unicode/utf8" + + "github.com/gdamore/tcell/v2" +) + +// InputField is a one-line box (three lines if there is a title) where the +// user can enter text. Use SetAcceptanceFunc() to accept or reject input, +// SetChangedFunc() to listen for changes, and SetMaskCharacter() to hide input +// from onlookers (e.g. for password input). +// +// The following keys can be used for navigation and editing: +// +// - Left arrow: Move left by one character. +// - Right arrow: Move right by one character. +// - Home, Ctrl-A, Alt-a: Move to the beginning of the line. +// - End, Ctrl-E, Alt-e: Move to the end of the line. +// - Alt-left, Alt-b: Move left by one word. +// - Alt-right, Alt-f: Move right by one word. +// - Backspace: Delete the character before the cursor. +// - Delete: Delete the character after the cursor. +// - Ctrl-K: Delete from the cursor to the end of the line. +// - Ctrl-W: Delete the last word before the cursor. +// - Ctrl-U: Delete the entire line. +// +// See https://github.com/rivo/tview/wiki/InputField for an example. +type InputField struct { + *Box + + // The text that was entered. + text string + + // The text to be displayed before the input area. + label string + + // The text to be displayed in the input area when "text" is empty. + placeholder string + + // The label color. + labelColor tcell.Color + + // The background color of the input area. + fieldBackgroundColor tcell.Color + + // The text color of the input area. + fieldTextColor tcell.Color + + // The text color of the placeholder. + placeholderTextColor tcell.Color + + // The screen width of the label area. A value of 0 means use the width of + // the label text. + labelWidth int + + // The screen width of the input area. A value of 0 means extend as much as + // possible. + fieldWidth int + + // A character to mask entered text (useful for password fields). A value of 0 + // disables masking. + maskCharacter rune + + // The cursor position as a byte index into the text string. + cursorPos int + + // An optional autocomplete function which receives the current text of the + // input field and returns a slice of strings to be displayed in a drop-down + // selection. + autocomplete func(text string) []string + + // The List object which shows the selectable autocomplete entries. If not + // nil, the list's main texts represent the current autocomplete entries. + autocompleteList *List + autocompleteListMutex sync.Mutex + + // An optional function which may reject the last character that was entered. + accept func(text string, ch rune) bool + + // An optional function which is called when the input has changed. + changed func(text string) + + // An optional function which is called when the user indicated that they + // are done entering text. The key which was pressed is provided (tab, + // shift-tab, enter, or escape). + done func(tcell.Key) + + // A callback function set by the Form class and called when the user leaves + // this form item. + finished func(tcell.Key) + + fieldX int // The x-coordinate of the input field as determined during the last call to Draw(). + offset int // The number of bytes of the text string skipped ahead while drawing. +} + +// NewInputField returns a new input field. +func NewInputField() *InputField { + return &InputField{ + Box: NewBox(), + labelColor: Styles.SecondaryTextColor, + fieldBackgroundColor: Styles.ContrastBackgroundColor, + fieldTextColor: Styles.PrimaryTextColor, + placeholderTextColor: Styles.ContrastSecondaryTextColor, + } +} + +// SetText sets the current text of the input field. +func (i *InputField) SetText(text string) *InputField { + i.text = text + i.cursorPos = len(text) + if i.changed != nil { + i.changed(text) + } + return i +} + +// GetText returns the current text of the input field. +func (i *InputField) GetText() string { + return i.text +} + +// SetLabel sets the text to be displayed before the input area. +func (i *InputField) SetLabel(label string) *InputField { + i.label = label + return i +} + +// GetLabel returns the text to be displayed before the input area. +func (i *InputField) GetLabel() string { + return i.label +} + +// SetLabelWidth sets the screen width of the label. A value of 0 will cause the +// primitive to use the width of the label string. +func (i *InputField) SetLabelWidth(width int) *InputField { + i.labelWidth = width + return i +} + +// SetPlaceholder sets the text to be displayed when the input text is empty. +func (i *InputField) SetPlaceholder(text string) *InputField { + i.placeholder = text + return i +} + +// SetLabelColor sets the color of the label. +func (i *InputField) SetLabelColor(color tcell.Color) *InputField { + i.labelColor = color + return i +} + +// SetFieldBackgroundColor sets the background color of the input area. +func (i *InputField) SetFieldBackgroundColor(color tcell.Color) *InputField { + i.fieldBackgroundColor = color + return i +} + +// SetFieldTextColor sets the text color of the input area. +func (i *InputField) SetFieldTextColor(color tcell.Color) *InputField { + i.fieldTextColor = color + return i +} + +// SetPlaceholderTextColor sets the text color of placeholder text. +func (i *InputField) SetPlaceholderTextColor(color tcell.Color) *InputField { + i.placeholderTextColor = color + return i +} + +// SetFormAttributes sets attributes shared by all form items. +func (i *InputField) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem { + i.labelWidth = labelWidth + i.labelColor = labelColor + i.backgroundColor = bgColor + i.fieldTextColor = fieldTextColor + i.fieldBackgroundColor = fieldBgColor + return i +} + +// SetFieldWidth sets the screen width of the input area. A value of 0 means +// extend as much as possible. +func (i *InputField) SetFieldWidth(width int) *InputField { + i.fieldWidth = width + return i +} + +// GetFieldWidth returns this primitive's field width. +func (i *InputField) GetFieldWidth() int { + return i.fieldWidth +} + +// SetMaskCharacter sets a character that masks user input on a screen. A value +// of 0 disables masking. +func (i *InputField) SetMaskCharacter(mask rune) *InputField { + i.maskCharacter = mask + return i +} + +// SetAutocompleteFunc sets an autocomplete callback function which may return +// strings to be selected from a drop-down based on the current text of the +// input field. The drop-down appears only if len(entries) > 0. The callback is +// invoked in this function and whenever the current text changes or when +// Autocomplete() is called. Entries are cleared when the user selects an entry +// or presses Escape. +func (i *InputField) SetAutocompleteFunc(callback func(currentText string) (entries []string)) *InputField { + i.autocomplete = callback + i.Autocomplete() + return i +} + +// Autocomplete invokes the autocomplete callback (if there is one). If the +// length of the returned autocomplete entries slice is greater than 0, the +// input field will present the user with a corresponding drop-down list the +// next time the input field is drawn. +// +// It is safe to call this function from any goroutine. Note that the input +// field is not redrawn automatically unless called from the main goroutine +// (e.g. in response to events). +func (i *InputField) Autocomplete() *InputField { + i.autocompleteListMutex.Lock() + defer i.autocompleteListMutex.Unlock() + if i.autocomplete == nil { + return i + } + + // Do we have any autocomplete entries? + entries := i.autocomplete(i.text) + if len(entries) == 0 { + // No entries, no list. + i.autocompleteList = nil + return i + } + + // Make a list if we have none. + if i.autocompleteList == nil { + i.autocompleteList = NewList() + i.autocompleteList.ShowSecondaryText(false). + SetMainTextColor(Styles.PrimitiveBackgroundColor). + SetSelectedTextColor(Styles.PrimitiveBackgroundColor). + SetSelectedBackgroundColor(Styles.PrimaryTextColor). + SetHighlightFullLine(true). + SetBackgroundColor(Styles.MoreContrastBackgroundColor) + } + + // Fill it with the entries. + currentEntry := -1 + suffixLength := 9999 // I'm just waiting for the day somebody opens an issue with this number being too small. + i.autocompleteList.Clear() + for index, entry := range entries { + i.autocompleteList.AddItem(entry, "", 0, nil) + if strings.HasPrefix(entry, i.text) && len(entry)-len(i.text) < suffixLength { + currentEntry = index + suffixLength = len(i.text) - len(entry) + } + } + + // Set the selection if we have one. + if currentEntry >= 0 { + i.autocompleteList.SetCurrentItem(currentEntry) + } + + return i +} + +// SetAcceptanceFunc sets a handler which may reject the last character that was +// entered (by returning false). +// +// This package defines a number of variables prefixed with InputField which may +// be used for common input (e.g. numbers, maximum text length). +func (i *InputField) SetAcceptanceFunc(handler func(textToCheck string, lastChar rune) bool) *InputField { + i.accept = handler + return i +} + +// SetChangedFunc sets a handler which is called whenever the text of the input +// field has changed. It receives the current text (after the change). +func (i *InputField) SetChangedFunc(handler func(text string)) *InputField { + i.changed = handler + return i +} + +// SetDoneFunc sets a handler which is called when the user is done entering +// text. The callback function is provided with the key that was pressed, which +// is one of the following: +// +// - KeyEnter: Done entering text. +// - KeyEscape: Abort text input. +// - KeyTab: Move to the next field. +// - KeyBacktab: Move to the previous field. +func (i *InputField) SetDoneFunc(handler func(key tcell.Key)) *InputField { + i.done = handler + return i +} + +// SetFinishedFunc sets a callback invoked when the user leaves this form item. +func (i *InputField) SetFinishedFunc(handler func(key tcell.Key)) FormItem { + i.finished = handler + return i +} + +// Draw draws this primitive onto the screen. +func (i *InputField) Draw(screen tcell.Screen) { + i.Box.DrawForSubclass(screen, i) + + // Prepare + x, y, width, height := i.GetInnerRect() + rightLimit := x + width + if height < 1 || rightLimit <= x { + return + } + + // Draw label. + if i.labelWidth > 0 { + labelWidth := i.labelWidth + if labelWidth > rightLimit-x { + labelWidth = rightLimit - x + } + Print(screen, i.label, x, y, labelWidth, AlignLeft, i.labelColor) + x += labelWidth + } else { + _, drawnWidth := Print(screen, i.label, x, y, rightLimit-x, AlignLeft, i.labelColor) + x += drawnWidth + } + + // Draw input area. + i.fieldX = x + fieldWidth := i.fieldWidth + if fieldWidth == 0 { + fieldWidth = math.MaxInt32 + } + if rightLimit-x < fieldWidth { + fieldWidth = rightLimit - x + } + fieldStyle := tcell.StyleDefault.Background(i.fieldBackgroundColor) + for index := 0; index < fieldWidth; index++ { + screen.SetContent(x+index, y, ' ', nil, fieldStyle) + } + + // Text. + var cursorScreenPos int + text := i.text + if text == "" && i.placeholder != "" { + // Draw placeholder text. + Print(screen, Escape(i.placeholder), x, y, fieldWidth, AlignLeft, i.placeholderTextColor) + i.offset = 0 + } else { + // Draw entered text. + if i.maskCharacter > 0 { + text = strings.Repeat(string(i.maskCharacter), utf8.RuneCountInString(i.text)) + } + if fieldWidth >= stringWidth(text) { + // We have enough space for the full text. + Print(screen, Escape(text), x, y, fieldWidth, AlignLeft, i.fieldTextColor) + i.offset = 0 + iterateString(text, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { + if textPos >= i.cursorPos { + return true + } + cursorScreenPos += screenWidth + return false + }) + } else { + // The text doesn't fit. Where is the cursor? + if i.cursorPos < 0 { + i.cursorPos = 0 + } else if i.cursorPos > len(text) { + i.cursorPos = len(text) + } + // Shift the text so the cursor is inside the field. + var shiftLeft int + if i.offset > i.cursorPos { + i.offset = i.cursorPos + } else if subWidth := stringWidth(text[i.offset:i.cursorPos]); subWidth > fieldWidth-1 { + shiftLeft = subWidth - fieldWidth + 1 + } + currentOffset := i.offset + iterateString(text, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { + if textPos >= currentOffset { + if shiftLeft > 0 { + i.offset = textPos + textWidth + shiftLeft -= screenWidth + } else { + if textPos+textWidth > i.cursorPos { + return true + } + cursorScreenPos += screenWidth + } + } + return false + }) + Print(screen, Escape(text[i.offset:]), x, y, fieldWidth, AlignLeft, i.fieldTextColor) + } + } + + // Draw autocomplete list. + i.autocompleteListMutex.Lock() + defer i.autocompleteListMutex.Unlock() + if i.autocompleteList != nil { + // How much space do we need? + lheight := i.autocompleteList.GetItemCount() + lwidth := 0 + for index := 0; index < lheight; index++ { + entry, _ := i.autocompleteList.GetItemText(index) + width := TaggedStringWidth(entry) + if width > lwidth { + lwidth = width + } + } + + // We prefer to drop down but if there is no space, maybe drop up? + lx := x + ly := y + 1 + _, sheight := screen.Size() + if ly+lheight >= sheight && ly-2 > lheight-ly { + ly = y - lheight + if ly < 0 { + ly = 0 + } + } + if ly+lheight >= sheight { + lheight = sheight - ly + } + i.autocompleteList.SetRect(lx, ly, lwidth, lheight) + i.autocompleteList.Draw(screen) + } + + // Set cursor. + if i.HasFocus() { + screen.ShowCursor(x+cursorScreenPos, y) + } +} + +// InputHandler returns the handler for this primitive. +func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { + return i.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { + // Trigger changed events. + currentText := i.text + defer func() { + if i.text != currentText { + i.Autocomplete() + if i.changed != nil { + i.changed(i.text) + } + } + }() + + // Movement functions. + home := func() { i.cursorPos = 0 } + end := func() { i.cursorPos = len(i.text) } + moveLeft := func() { + iterateStringReverse(i.text[:i.cursorPos], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { + i.cursorPos -= textWidth + return true + }) + } + moveRight := func() { + iterateString(i.text[i.cursorPos:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { + i.cursorPos += textWidth + return true + }) + } + moveWordLeft := func() { + i.cursorPos = len(regexp.MustCompile(`\S+\s*$`).ReplaceAllString(i.text[:i.cursorPos], "")) + } + moveWordRight := func() { + i.cursorPos = len(i.text) - len(regexp.MustCompile(`^\s*\S+\s*`).ReplaceAllString(i.text[i.cursorPos:], "")) + } + + // Add character function. Returns whether or not the rune character is + // accepted. + add := func(r rune) bool { + newText := i.text[:i.cursorPos] + string(r) + i.text[i.cursorPos:] + if i.accept != nil && !i.accept(newText, r) { + return false + } + i.text = newText + i.cursorPos += len(string(r)) + return true + } + + // Change the autocomplete selection. + autocompleteSelect := func(offset int) { + count := i.autocompleteList.GetItemCount() + newEntry := i.autocompleteList.GetCurrentItem() + offset + if newEntry >= count { + newEntry = 0 + } else if newEntry < 0 { + newEntry = count - 1 + } + i.autocompleteList.SetCurrentItem(newEntry) + currentText, _ = i.autocompleteList.GetItemText(newEntry) // Don't trigger changed function twice. + currentText = stripTags(currentText) + i.SetText(currentText) + } + + // Finish up. + finish := func(key tcell.Key) { + if i.done != nil { + i.done(key) + } + if i.finished != nil { + i.finished(key) + } + } + + // Process key event. + i.autocompleteListMutex.Lock() + defer i.autocompleteListMutex.Unlock() + switch key := event.Key(); key { + case tcell.KeyRune: // Regular character. + if event.Modifiers()&tcell.ModAlt > 0 { + // We accept some Alt- key combinations. + switch event.Rune() { + case 'a': // Home. + home() + case 'e': // End. + end() + case 'b': // Move word left. + moveWordLeft() + case 'f': // Move word right. + moveWordRight() + default: + if !add(event.Rune()) { + return + } + } + } else { + // Other keys are simply accepted as regular characters. + if !add(event.Rune()) { + return + } + } + case tcell.KeyCtrlU: // Delete all. + i.text = "" + i.cursorPos = 0 + case tcell.KeyCtrlK: // Delete until the end of the line. + i.text = i.text[:i.cursorPos] + case tcell.KeyCtrlW: // Delete last word. + lastWord := regexp.MustCompile(`\S+\s*$`) + newText := lastWord.ReplaceAllString(i.text[:i.cursorPos], "") + i.text[i.cursorPos:] + i.cursorPos -= len(i.text) - len(newText) + i.text = newText + case tcell.KeyBackspace, tcell.KeyBackspace2: // Delete character before the cursor. + iterateStringReverse(i.text[:i.cursorPos], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { + i.text = i.text[:textPos] + i.text[textPos+textWidth:] + i.cursorPos -= textWidth + return true + }) + if i.offset >= i.cursorPos { + i.offset = 0 + } + case tcell.KeyDelete, tcell.KeyCtrlD: // Delete character after the cursor. + iterateString(i.text[i.cursorPos:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { + i.text = i.text[:i.cursorPos] + i.text[i.cursorPos+textWidth:] + return true + }) + case tcell.KeyLeft: + if event.Modifiers()&tcell.ModAlt > 0 { + moveWordLeft() + } else { + moveLeft() + } + case tcell.KeyCtrlB: + moveLeft() + case tcell.KeyRight: + if event.Modifiers()&tcell.ModAlt > 0 { + moveWordRight() + } else { + moveRight() + } + case tcell.KeyCtrlF: + moveRight() + case tcell.KeyHome, tcell.KeyCtrlA: + home() + case tcell.KeyEnd, tcell.KeyCtrlE: + end() + case tcell.KeyEnter: + if i.autocompleteList != nil { + autocompleteSelect(0) + i.autocompleteList = nil + } else { + finish(key) + } + case tcell.KeyEscape: + if i.autocompleteList != nil { + i.autocompleteList = nil + } else { + finish(key) + } + case tcell.KeyTab: + if i.autocompleteList != nil { + autocompleteSelect(0) + } else { + finish(key) + } + case tcell.KeyDown: + if i.autocompleteList != nil { + autocompleteSelect(1) + } else { + finish(key) + } + case tcell.KeyUp, tcell.KeyBacktab: // Autocomplete selection. + if i.autocompleteList != nil { + autocompleteSelect(-1) + } else { + finish(key) + } + } + }) +} + +// MouseHandler returns the mouse handler for this primitive. +func (i *InputField) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + return i.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + x, y := event.Position() + _, rectY, _, _ := i.GetInnerRect() + if !i.InRect(x, y) { + return false, nil + } + + // Process mouse event. + if action == MouseLeftClick && y == rectY { + // Determine where to place the cursor. + if x >= i.fieldX { + if !iterateString(i.text[i.offset:], func(main rune, comb []rune, textPos int, textWidth int, screenPos int, screenWidth int) bool { + if x-i.fieldX < screenPos+screenWidth { + i.cursorPos = textPos + i.offset + return true + } + return false + }) { + i.cursorPos = len(i.text) + } + } + setFocus(i) + consumed = true + } + + return + }) +} diff --git a/vendor/github.com/rivo/tview/list.go b/vendor/github.com/rivo/tview/list.go new file mode 100644 index 0000000000..e3f088ab2c --- /dev/null +++ b/vendor/github.com/rivo/tview/list.go @@ -0,0 +1,705 @@ +package tview + +import ( + "fmt" + "strings" + + "github.com/gdamore/tcell/v2" +) + +// listItem represents one item in a List. +type listItem struct { + MainText string // The main text of the list item. + SecondaryText string // A secondary text to be shown underneath the main text. + Shortcut rune // The key to select the list item directly, 0 if there is no shortcut. + Selected func() // The optional function which is called when the item is selected. +} + +// List displays rows of items, each of which can be selected. +// +// See https://github.com/rivo/tview/wiki/List for an example. +type List struct { + *Box + + // The items of the list. + items []*listItem + + // The index of the currently selected item. + currentItem int + + // Whether or not to show the secondary item texts. + showSecondaryText bool + + // The item main text color. + mainTextColor tcell.Color + + // The item secondary text color. + secondaryTextColor tcell.Color + + // The item shortcut text color. + shortcutColor tcell.Color + + // The text color for selected items. + selectedTextColor tcell.Color + + // The background color for selected items. + selectedBackgroundColor tcell.Color + + // If true, the selection is only shown when the list has focus. + selectedFocusOnly bool + + // If true, the entire row is highlighted when selected. + highlightFullLine bool + + // Whether or not navigating the list will wrap around. + wrapAround bool + + // The number of list items skipped at the top before the first item is + // drawn. + itemOffset int + + // The number of cells skipped on the left side of an item text. Shortcuts + // are not affected. + horizontalOffset int + + // Set to true if a currently visible item flows over the right border of + // the box. This is set by the Draw() function. It determines the behaviour + // of the right arrow key. + overflowing bool + + // An optional function which is called when the user has navigated to a list + // item. + changed func(index int, mainText, secondaryText string, shortcut rune) + + // An optional function which is called when a list item was selected. This + // function will be called even if the list item defines its own callback. + selected func(index int, mainText, secondaryText string, shortcut rune) + + // An optional function which is called when the user presses the Escape key. + done func() +} + +// NewList returns a new form. +func NewList() *List { + return &List{ + Box: NewBox(), + showSecondaryText: true, + wrapAround: true, + mainTextColor: Styles.PrimaryTextColor, + secondaryTextColor: Styles.TertiaryTextColor, + shortcutColor: Styles.SecondaryTextColor, + selectedTextColor: Styles.PrimitiveBackgroundColor, + selectedBackgroundColor: Styles.PrimaryTextColor, + } +} + +// SetCurrentItem sets the currently selected item by its index, starting at 0 +// for the first item. If a negative index is provided, items are referred to +// from the back (-1 = last item, -2 = second-to-last item, and so on). Out of +// range indices are clamped to the beginning/end. +// +// Calling this function triggers a "changed" event if the selection changes. +func (l *List) SetCurrentItem(index int) *List { + if index < 0 { + index = len(l.items) + index + } + if index >= len(l.items) { + index = len(l.items) - 1 + } + if index < 0 { + index = 0 + } + + if index != l.currentItem && l.changed != nil { + item := l.items[index] + l.changed(index, item.MainText, item.SecondaryText, item.Shortcut) + } + + l.currentItem = index + + return l +} + +// GetCurrentItem returns the index of the currently selected list item, +// starting at 0 for the first item. +func (l *List) GetCurrentItem() int { + return l.currentItem +} + +// SetOffset sets the number of items to be skipped (vertically) as well as the +// number of cells skipped horizontally when the list is drawn. Note that one +// item corresponds to two rows when there are secondary texts. Shortcuts are +// always drawn. +// +// These values may change when the list is drawn to ensure the currently +// selected item is visible and item texts move out of view. Users can also +// modify these values by interacting with the list. +func (l *List) SetOffset(items, horizontal int) *List { + l.itemOffset = items + l.horizontalOffset = horizontal + return l +} + +// GetOffset returns the number of items skipped while drawing, as well as the +// number of cells item text is moved to the left. See also SetOffset() for more +// information on these values. +func (l *List) GetOffset() (int, int) { + return l.itemOffset, l.horizontalOffset +} + +// RemoveItem removes the item with the given index (starting at 0) from the +// list. If a negative index is provided, items are referred to from the back +// (-1 = last item, -2 = second-to-last item, and so on). Out of range indices +// are clamped to the beginning/end, i.e. unless the list is empty, an item is +// always removed. +// +// The currently selected item is shifted accordingly. If it is the one that is +// removed, a "changed" event is fired. +func (l *List) RemoveItem(index int) *List { + if len(l.items) == 0 { + return l + } + + // Adjust index. + if index < 0 { + index = len(l.items) + index + } + if index >= len(l.items) { + index = len(l.items) - 1 + } + if index < 0 { + index = 0 + } + + // Remove item. + l.items = append(l.items[:index], l.items[index+1:]...) + + // If there is nothing left, we're done. + if len(l.items) == 0 { + return l + } + + // Shift current item. + previousCurrentItem := l.currentItem + if l.currentItem >= index { + l.currentItem-- + } + + // Fire "changed" event for removed items. + if previousCurrentItem == index && l.changed != nil { + item := l.items[l.currentItem] + l.changed(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut) + } + + return l +} + +// SetMainTextColor sets the color of the items' main text. +func (l *List) SetMainTextColor(color tcell.Color) *List { + l.mainTextColor = color + return l +} + +// SetSecondaryTextColor sets the color of the items' secondary text. +func (l *List) SetSecondaryTextColor(color tcell.Color) *List { + l.secondaryTextColor = color + return l +} + +// SetShortcutColor sets the color of the items' shortcut. +func (l *List) SetShortcutColor(color tcell.Color) *List { + l.shortcutColor = color + return l +} + +// SetSelectedTextColor sets the text color of selected items. +func (l *List) SetSelectedTextColor(color tcell.Color) *List { + l.selectedTextColor = color + return l +} + +// SetSelectedBackgroundColor sets the background color of selected items. +func (l *List) SetSelectedBackgroundColor(color tcell.Color) *List { + l.selectedBackgroundColor = color + return l +} + +// SetSelectedFocusOnly sets a flag which determines when the currently selected +// list item is highlighted. If set to true, selected items are only highlighted +// when the list has focus. If set to false, they are always highlighted. +func (l *List) SetSelectedFocusOnly(focusOnly bool) *List { + l.selectedFocusOnly = focusOnly + return l +} + +// SetHighlightFullLine sets a flag which determines whether the colored +// background of selected items spans the entire width of the view. If set to +// true, the highlight spans the entire view. If set to false, only the text of +// the selected item from beginning to end is highlighted. +func (l *List) SetHighlightFullLine(highlight bool) *List { + l.highlightFullLine = highlight + return l +} + +// ShowSecondaryText determines whether or not to show secondary item texts. +func (l *List) ShowSecondaryText(show bool) *List { + l.showSecondaryText = show + return l +} + +// SetWrapAround sets the flag that determines whether navigating the list will +// wrap around. That is, navigating downwards on the last item will move the +// selection to the first item (similarly in the other direction). If set to +// false, the selection won't change when navigating downwards on the last item +// or navigating upwards on the first item. +func (l *List) SetWrapAround(wrapAround bool) *List { + l.wrapAround = wrapAround + return l +} + +// SetChangedFunc sets the function which is called when the user navigates to +// a list item. The function receives the item's index in the list of items +// (starting with 0), its main text, secondary text, and its shortcut rune. +// +// This function is also called when the first item is added or when +// SetCurrentItem() is called. +func (l *List) SetChangedFunc(handler func(index int, mainText string, secondaryText string, shortcut rune)) *List { + l.changed = handler + return l +} + +// SetSelectedFunc sets the function which is called when the user selects a +// list item by pressing Enter on the current selection. The function receives +// the item's index in the list of items (starting with 0), its main text, +// secondary text, and its shortcut rune. +func (l *List) SetSelectedFunc(handler func(int, string, string, rune)) *List { + l.selected = handler + return l +} + +// SetDoneFunc sets a function which is called when the user presses the Escape +// key. +func (l *List) SetDoneFunc(handler func()) *List { + l.done = handler + return l +} + +// AddItem calls InsertItem() with an index of -1. +func (l *List) AddItem(mainText, secondaryText string, shortcut rune, selected func()) *List { + l.InsertItem(-1, mainText, secondaryText, shortcut, selected) + return l +} + +// InsertItem adds a new item to the list at the specified index. An index of 0 +// will insert the item at the beginning, an index of 1 before the second item, +// and so on. An index of GetItemCount() or higher will insert the item at the +// end of the list. Negative indices are also allowed: An index of -1 will +// insert the item at the end of the list, an index of -2 before the last item, +// and so on. An index of -GetItemCount()-1 or lower will insert the item at the +// beginning. +// +// An item has a main text which will be highlighted when selected. It also has +// a secondary text which is shown underneath the main text (if it is set to +// visible) but which may remain empty. +// +// The shortcut is a key binding. If the specified rune is entered, the item +// is selected immediately. Set to 0 for no binding. +// +// The "selected" callback will be invoked when the user selects the item. You +// may provide nil if no such callback is needed or if all events are handled +// through the selected callback set with SetSelectedFunc(). +// +// The currently selected item will shift its position accordingly. If the list +// was previously empty, a "changed" event is fired because the new item becomes +// selected. +func (l *List) InsertItem(index int, mainText, secondaryText string, shortcut rune, selected func()) *List { + item := &listItem{ + MainText: mainText, + SecondaryText: secondaryText, + Shortcut: shortcut, + Selected: selected, + } + + // Shift index to range. + if index < 0 { + index = len(l.items) + index + 1 + } + if index < 0 { + index = 0 + } else if index > len(l.items) { + index = len(l.items) + } + + // Shift current item. + if l.currentItem < len(l.items) && l.currentItem >= index { + l.currentItem++ + } + + // Insert item (make space for the new item, then shift and insert). + l.items = append(l.items, nil) + if index < len(l.items)-1 { // -1 because l.items has already grown by one item. + copy(l.items[index+1:], l.items[index:]) + } + l.items[index] = item + + // Fire a "change" event for the first item in the list. + if len(l.items) == 1 && l.changed != nil { + item := l.items[0] + l.changed(0, item.MainText, item.SecondaryText, item.Shortcut) + } + + return l +} + +// GetItemCount returns the number of items in the list. +func (l *List) GetItemCount() int { + return len(l.items) +} + +// GetItemText returns an item's texts (main and secondary). Panics if the index +// is out of range. +func (l *List) GetItemText(index int) (main, secondary string) { + return l.items[index].MainText, l.items[index].SecondaryText +} + +// SetItemText sets an item's main and secondary text. Panics if the index is +// out of range. +func (l *List) SetItemText(index int, main, secondary string) *List { + item := l.items[index] + item.MainText = main + item.SecondaryText = secondary + return l +} + +// FindItems searches the main and secondary texts for the given strings and +// returns a list of item indices in which those strings are found. One of the +// two search strings may be empty, it will then be ignored. Indices are always +// returned in ascending order. +// +// If mustContainBoth is set to true, mainSearch must be contained in the main +// text AND secondarySearch must be contained in the secondary text. If it is +// false, only one of the two search strings must be contained. +// +// Set ignoreCase to true for case-insensitive search. +func (l *List) FindItems(mainSearch, secondarySearch string, mustContainBoth, ignoreCase bool) (indices []int) { + if mainSearch == "" && secondarySearch == "" { + return + } + + if ignoreCase { + mainSearch = strings.ToLower(mainSearch) + secondarySearch = strings.ToLower(secondarySearch) + } + + for index, item := range l.items { + mainText := item.MainText + secondaryText := item.SecondaryText + if ignoreCase { + mainText = strings.ToLower(mainText) + secondaryText = strings.ToLower(secondaryText) + } + + // strings.Contains() always returns true for a "" search. + mainContained := strings.Contains(mainText, mainSearch) + secondaryContained := strings.Contains(secondaryText, secondarySearch) + if mustContainBoth && mainContained && secondaryContained || + !mustContainBoth && (mainText != "" && mainContained || secondaryText != "" && secondaryContained) { + indices = append(indices, index) + } + } + + return +} + +// Clear removes all items from the list. +func (l *List) Clear() *List { + l.items = nil + l.currentItem = 0 + return l +} + +// Draw draws this primitive onto the screen. +func (l *List) Draw(screen tcell.Screen) { + l.Box.DrawForSubclass(screen, l) + + // Determine the dimensions. + x, y, width, height := l.GetInnerRect() + bottomLimit := y + height + _, totalHeight := screen.Size() + if bottomLimit > totalHeight { + bottomLimit = totalHeight + } + + // Do we show any shortcuts? + var showShortcuts bool + for _, item := range l.items { + if item.Shortcut != 0 { + showShortcuts = true + x += 4 + width -= 4 + break + } + } + + // Adjust offset to keep the current selection in view. + if l.currentItem < l.itemOffset { + l.itemOffset = l.currentItem + } else if l.showSecondaryText { + if 2*(l.currentItem-l.itemOffset) >= height-1 { + l.itemOffset = (2*l.currentItem + 3 - height) / 2 + } + } else { + if l.currentItem-l.itemOffset >= height { + l.itemOffset = l.currentItem + 1 - height + } + } + if l.horizontalOffset < 0 { + l.horizontalOffset = 0 + } + + // Draw the list items. + var ( + maxWidth int // The maximum printed item width. + overflowing bool // Whether a text's end exceeds the right border. + ) + for index, item := range l.items { + if index < l.itemOffset { + continue + } + + if y >= bottomLimit { + break + } + + // Shortcuts. + if showShortcuts && item.Shortcut != 0 { + Print(screen, fmt.Sprintf("(%s)", string(item.Shortcut)), x-5, y, 4, AlignRight, l.shortcutColor) + } + + // Main text. + _, printedWidth, _, end := printWithStyle(screen, item.MainText, x, y, l.horizontalOffset, width, AlignLeft, tcell.StyleDefault.Foreground(l.mainTextColor), true) + if printedWidth > maxWidth { + maxWidth = printedWidth + } + if end < len(item.MainText) { + overflowing = true + } + + // Background color of selected text. + if index == l.currentItem && (!l.selectedFocusOnly || l.HasFocus()) { + textWidth := width + if !l.highlightFullLine { + if w := TaggedStringWidth(item.MainText); w < textWidth { + textWidth = w + } + } + + for bx := 0; bx < textWidth; bx++ { + m, c, style, _ := screen.GetContent(x+bx, y) + fg, _, _ := style.Decompose() + if fg == l.mainTextColor { + fg = l.selectedTextColor + } + style = style.Background(l.selectedBackgroundColor).Foreground(fg) + screen.SetContent(x+bx, y, m, c, style) + } + } + + y++ + + if y >= bottomLimit { + break + } + + // Secondary text. + if l.showSecondaryText { + _, printedWidth, _, end := printWithStyle(screen, item.SecondaryText, x, y, l.horizontalOffset, width, AlignLeft, tcell.StyleDefault.Foreground(l.secondaryTextColor), true) + if printedWidth > maxWidth { + maxWidth = printedWidth + } + if end < len(item.SecondaryText) { + overflowing = true + } + y++ + } + } + + // We don't want the item text to get out of view. If the horizontal offset + // is too high, we reset it and redraw. (That should be about as efficient + // as calculating everything up front.) + if l.horizontalOffset > 0 && maxWidth < width { + l.horizontalOffset -= width - maxWidth + l.Draw(screen) + } + l.overflowing = overflowing +} + +// InputHandler returns the handler for this primitive. +func (l *List) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { + return l.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { + if event.Key() == tcell.KeyEscape { + if l.done != nil { + l.done() + } + return + } else if len(l.items) == 0 { + return + } + + previousItem := l.currentItem + + switch key := event.Key(); key { + case tcell.KeyTab, tcell.KeyDown: + l.currentItem++ + case tcell.KeyBacktab, tcell.KeyUp: + l.currentItem-- + case tcell.KeyRight: + if l.overflowing { + l.horizontalOffset += 2 // We shift by 2 to account for two-cell characters. + } else { + l.currentItem++ + } + case tcell.KeyLeft: + if l.horizontalOffset > 0 { + l.horizontalOffset -= 2 + } else { + l.currentItem-- + } + case tcell.KeyHome: + l.currentItem = 0 + case tcell.KeyEnd: + l.currentItem = len(l.items) - 1 + case tcell.KeyPgDn: + _, _, _, height := l.GetInnerRect() + l.currentItem += height + if l.currentItem >= len(l.items) { + l.currentItem = len(l.items) - 1 + } + case tcell.KeyPgUp: + _, _, _, height := l.GetInnerRect() + l.currentItem -= height + if l.currentItem < 0 { + l.currentItem = 0 + } + case tcell.KeyEnter: + if l.currentItem >= 0 && l.currentItem < len(l.items) { + item := l.items[l.currentItem] + if item.Selected != nil { + item.Selected() + } + if l.selected != nil { + l.selected(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut) + } + } + case tcell.KeyRune: + ch := event.Rune() + if ch != ' ' { + // It's not a space bar. Is it a shortcut? + var found bool + for index, item := range l.items { + if item.Shortcut == ch { + // We have a shortcut. + found = true + l.currentItem = index + break + } + } + if !found { + break + } + } + item := l.items[l.currentItem] + if item.Selected != nil { + item.Selected() + } + if l.selected != nil { + l.selected(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut) + } + } + + if l.currentItem < 0 { + if l.wrapAround { + l.currentItem = len(l.items) - 1 + } else { + l.currentItem = 0 + } + } else if l.currentItem >= len(l.items) { + if l.wrapAround { + l.currentItem = 0 + } else { + l.currentItem = len(l.items) - 1 + } + } + + if l.currentItem != previousItem && l.currentItem < len(l.items) && l.changed != nil { + item := l.items[l.currentItem] + l.changed(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut) + } + }) +} + +// indexAtPoint returns the index of the list item found at the given position +// or a negative value if there is no such list item. +func (l *List) indexAtPoint(x, y int) int { + rectX, rectY, width, height := l.GetInnerRect() + if rectX < 0 || rectX >= rectX+width || y < rectY || y >= rectY+height { + return -1 + } + + index := y - rectY + if l.showSecondaryText { + index /= 2 + } + index += l.itemOffset + + if index >= len(l.items) { + return -1 + } + return index +} + +// MouseHandler returns the mouse handler for this primitive. +func (l *List) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + return l.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + if !l.InRect(event.Position()) { + return false, nil + } + + // Process mouse event. + switch action { + case MouseLeftClick: + setFocus(l) + index := l.indexAtPoint(event.Position()) + if index != -1 { + item := l.items[index] + if item.Selected != nil { + item.Selected() + } + if l.selected != nil { + l.selected(index, item.MainText, item.SecondaryText, item.Shortcut) + } + if index != l.currentItem && l.changed != nil { + l.changed(index, item.MainText, item.SecondaryText, item.Shortcut) + } + l.currentItem = index + } + consumed = true + case MouseScrollUp: + if l.itemOffset > 0 { + l.itemOffset-- + } + consumed = true + case MouseScrollDown: + lines := len(l.items) - l.itemOffset + if l.showSecondaryText { + lines *= 2 + } + if _, _, _, height := l.GetInnerRect(); lines > height { + l.itemOffset++ + } + consumed = true + } + + return + }) +} diff --git a/vendor/github.com/rivo/tview/modal.go b/vendor/github.com/rivo/tview/modal.go new file mode 100644 index 0000000000..4e9aa5ceff --- /dev/null +++ b/vendor/github.com/rivo/tview/modal.go @@ -0,0 +1,201 @@ +package tview + +import ( + "github.com/gdamore/tcell/v2" +) + +// Modal is a centered message window used to inform the user or prompt them +// for an immediate decision. It needs to have at least one button (added via +// AddButtons()) or it will never disappear. +// +// See https://github.com/rivo/tview/wiki/Modal for an example. +type Modal struct { + *Box + + // The frame embedded in the modal. + frame *Frame + + // The form embedded in the modal's frame. + form *Form + + // The message text (original, not word-wrapped). + text string + + // The text color. + textColor tcell.Color + + // The optional callback for when the user clicked one of the buttons. It + // receives the index of the clicked button and the button's label. + done func(buttonIndex int, buttonLabel string) +} + +// NewModal returns a new modal message window. +func NewModal() *Modal { + m := &Modal{ + Box: NewBox(), + textColor: Styles.PrimaryTextColor, + } + m.form = NewForm(). + SetButtonsAlign(AlignCenter). + SetButtonBackgroundColor(Styles.PrimitiveBackgroundColor). + SetButtonTextColor(Styles.PrimaryTextColor) + m.form.SetBackgroundColor(Styles.ContrastBackgroundColor).SetBorderPadding(0, 0, 0, 0) + m.form.SetCancelFunc(func() { + if m.done != nil { + m.done(-1, "") + } + }) + m.frame = NewFrame(m.form).SetBorders(0, 0, 1, 0, 0, 0) + m.frame.SetBorder(true). + SetBackgroundColor(Styles.ContrastBackgroundColor). + SetBorderPadding(1, 1, 1, 1) + return m +} + +// SetBackgroundColor sets the color of the modal frame background. +func (m *Modal) SetBackgroundColor(color tcell.Color) *Modal { + m.form.SetBackgroundColor(color) + m.frame.SetBackgroundColor(color) + return m +} + +// SetTextColor sets the color of the message text. +func (m *Modal) SetTextColor(color tcell.Color) *Modal { + m.textColor = color + return m +} + +// SetButtonBackgroundColor sets the background color of the buttons. +func (m *Modal) SetButtonBackgroundColor(color tcell.Color) *Modal { + m.form.SetButtonBackgroundColor(color) + return m +} + +// SetButtonTextColor sets the color of the button texts. +func (m *Modal) SetButtonTextColor(color tcell.Color) *Modal { + m.form.SetButtonTextColor(color) + return m +} + +// SetDoneFunc sets a handler which is called when one of the buttons was +// pressed. It receives the index of the button as well as its label text. The +// handler is also called when the user presses the Escape key. The index will +// then be negative and the label text an emptry string. +func (m *Modal) SetDoneFunc(handler func(buttonIndex int, buttonLabel string)) *Modal { + m.done = handler + return m +} + +// SetText sets the message text of the window. The text may contain line +// breaks. Note that words are wrapped, too, based on the final size of the +// window. +func (m *Modal) SetText(text string) *Modal { + m.text = text + return m +} + +// AddButtons adds buttons to the window. There must be at least one button and +// a "done" handler so the window can be closed again. +func (m *Modal) AddButtons(labels []string) *Modal { + for index, label := range labels { + func(i int, l string) { + m.form.AddButton(label, func() { + if m.done != nil { + m.done(i, l) + } + }) + button := m.form.GetButton(m.form.GetButtonCount() - 1) + button.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + switch event.Key() { + case tcell.KeyDown, tcell.KeyRight: + return tcell.NewEventKey(tcell.KeyTab, 0, tcell.ModNone) + case tcell.KeyUp, tcell.KeyLeft: + return tcell.NewEventKey(tcell.KeyBacktab, 0, tcell.ModNone) + } + return event + }) + }(index, label) + } + return m +} + +// ClearButtons removes all buttons from the window. +func (m *Modal) ClearButtons() *Modal { + m.form.ClearButtons() + return m +} + +// SetFocus shifts the focus to the button with the given index. +func (m *Modal) SetFocus(index int) *Modal { + m.form.SetFocus(index) + return m +} + +// Focus is called when this primitive receives focus. +func (m *Modal) Focus(delegate func(p Primitive)) { + delegate(m.form) +} + +// HasFocus returns whether or not this primitive has focus. +func (m *Modal) HasFocus() bool { + return m.form.HasFocus() +} + +// Draw draws this primitive onto the screen. +func (m *Modal) Draw(screen tcell.Screen) { + // Calculate the width of this modal. + buttonsWidth := 0 + for _, button := range m.form.buttons { + buttonsWidth += TaggedStringWidth(button.label) + 4 + 2 + } + buttonsWidth -= 2 + screenWidth, screenHeight := screen.Size() + width := screenWidth / 3 + if width < buttonsWidth { + width = buttonsWidth + } + // width is now without the box border. + + // Reset the text and find out how wide it is. + m.frame.Clear() + lines := WordWrap(m.text, width) + for _, line := range lines { + m.frame.AddText(line, true, AlignCenter, m.textColor) + } + + // Set the modal's position and size. + height := len(lines) + 6 + width += 4 + x := (screenWidth - width) / 2 + y := (screenHeight - height) / 2 + m.SetRect(x, y, width, height) + + // Draw the frame. + m.frame.SetRect(x, y, width, height) + m.frame.Draw(screen) +} + +// MouseHandler returns the mouse handler for this primitive. +func (m *Modal) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + return m.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + // Pass mouse events on to the form. + consumed, capture = m.form.MouseHandler()(action, event, setFocus) + if !consumed && action == MouseLeftClick && m.InRect(event.Position()) { + setFocus(m) + consumed = true + } + return + }) +} + +// InputHandler returns the handler for this primitive. +func (m *Modal) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { + return m.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { + if m.frame.HasFocus() { + if handler := m.frame.InputHandler(); handler != nil { + handler(event, setFocus) + return + } + } + }) +} diff --git a/vendor/github.com/rivo/tview/pages.go b/vendor/github.com/rivo/tview/pages.go new file mode 100644 index 0000000000..32c933e1b2 --- /dev/null +++ b/vendor/github.com/rivo/tview/pages.go @@ -0,0 +1,315 @@ +package tview + +import ( + "github.com/gdamore/tcell/v2" +) + +// page represents one page of a Pages object. +type page struct { + Name string // The page's name. + Item Primitive // The page's primitive. + Resize bool // Whether or not to resize the page when it is drawn. + Visible bool // Whether or not this page is visible. +} + +// Pages is a container for other primitives laid out on top of each other, +// overlapping or not. It is often used as the application's root primitive. It +// allows to easily switch the visibility of the contained primitives. +// +// See https://github.com/rivo/tview/wiki/Pages for an example. +type Pages struct { + *Box + + // The contained pages. (Visible) pages are drawn from back to front. + pages []*page + + // We keep a reference to the function which allows us to set the focus to + // a newly visible page. + setFocus func(p Primitive) + + // An optional handler which is called whenever the visibility or the order of + // pages changes. + changed func() +} + +// NewPages returns a new Pages object. +func NewPages() *Pages { + p := &Pages{ + Box: NewBox(), + } + return p +} + +// SetChangedFunc sets a handler which is called whenever the visibility or the +// order of any visible pages changes. This can be used to redraw the pages. +func (p *Pages) SetChangedFunc(handler func()) *Pages { + p.changed = handler + return p +} + +// GetPageCount returns the number of pages currently stored in this object. +func (p *Pages) GetPageCount() int { + return len(p.pages) +} + +// AddPage adds a new page with the given name and primitive. If there was +// previously a page with the same name, it is overwritten. Leaving the name +// empty may cause conflicts in other functions so always specify a non-empty +// name. +// +// Visible pages will be drawn in the order they were added (unless that order +// was changed in one of the other functions). If "resize" is set to true, the +// primitive will be set to the size available to the Pages primitive whenever +// the pages are drawn. +func (p *Pages) AddPage(name string, item Primitive, resize, visible bool) *Pages { + hasFocus := p.HasFocus() + for index, pg := range p.pages { + if pg.Name == name { + p.pages = append(p.pages[:index], p.pages[index+1:]...) + break + } + } + p.pages = append(p.pages, &page{Item: item, Name: name, Resize: resize, Visible: visible}) + if p.changed != nil { + p.changed() + } + if hasFocus { + p.Focus(p.setFocus) + } + return p +} + +// AddAndSwitchToPage calls AddPage(), then SwitchToPage() on that newly added +// page. +func (p *Pages) AddAndSwitchToPage(name string, item Primitive, resize bool) *Pages { + p.AddPage(name, item, resize, true) + p.SwitchToPage(name) + return p +} + +// RemovePage removes the page with the given name. If that page was the only +// visible page, visibility is assigned to the last page. +func (p *Pages) RemovePage(name string) *Pages { + var isVisible bool + hasFocus := p.HasFocus() + for index, page := range p.pages { + if page.Name == name { + isVisible = page.Visible + p.pages = append(p.pages[:index], p.pages[index+1:]...) + if page.Visible && p.changed != nil { + p.changed() + } + break + } + } + if isVisible { + for index, page := range p.pages { + if index < len(p.pages)-1 { + if page.Visible { + break // There is a remaining visible page. + } + } else { + page.Visible = true // We need at least one visible page. + } + } + } + if hasFocus { + p.Focus(p.setFocus) + } + return p +} + +// HasPage returns true if a page with the given name exists in this object. +func (p *Pages) HasPage(name string) bool { + for _, page := range p.pages { + if page.Name == name { + return true + } + } + return false +} + +// ShowPage sets a page's visibility to "true" (in addition to any other pages +// which are already visible). +func (p *Pages) ShowPage(name string) *Pages { + for _, page := range p.pages { + if page.Name == name { + page.Visible = true + if p.changed != nil { + p.changed() + } + break + } + } + if p.HasFocus() { + p.Focus(p.setFocus) + } + return p +} + +// HidePage sets a page's visibility to "false". +func (p *Pages) HidePage(name string) *Pages { + for _, page := range p.pages { + if page.Name == name { + page.Visible = false + if p.changed != nil { + p.changed() + } + break + } + } + if p.HasFocus() { + p.Focus(p.setFocus) + } + return p +} + +// SwitchToPage sets a page's visibility to "true" and all other pages' +// visibility to "false". +func (p *Pages) SwitchToPage(name string) *Pages { + for _, page := range p.pages { + if page.Name == name { + page.Visible = true + } else { + page.Visible = false + } + } + if p.changed != nil { + p.changed() + } + if p.HasFocus() { + p.Focus(p.setFocus) + } + return p +} + +// SendToFront changes the order of the pages such that the page with the given +// name comes last, causing it to be drawn last with the next update (if +// visible). +func (p *Pages) SendToFront(name string) *Pages { + for index, page := range p.pages { + if page.Name == name { + if index < len(p.pages)-1 { + p.pages = append(append(p.pages[:index], p.pages[index+1:]...), page) + } + if page.Visible && p.changed != nil { + p.changed() + } + break + } + } + if p.HasFocus() { + p.Focus(p.setFocus) + } + return p +} + +// SendToBack changes the order of the pages such that the page with the given +// name comes first, causing it to be drawn first with the next update (if +// visible). +func (p *Pages) SendToBack(name string) *Pages { + for index, pg := range p.pages { + if pg.Name == name { + if index > 0 { + p.pages = append(append([]*page{pg}, p.pages[:index]...), p.pages[index+1:]...) + } + if pg.Visible && p.changed != nil { + p.changed() + } + break + } + } + if p.HasFocus() { + p.Focus(p.setFocus) + } + return p +} + +// GetFrontPage returns the front-most visible page. If there are no visible +// pages, ("", nil) is returned. +func (p *Pages) GetFrontPage() (name string, item Primitive) { + for index := len(p.pages) - 1; index >= 0; index-- { + if p.pages[index].Visible { + return p.pages[index].Name, p.pages[index].Item + } + } + return +} + +// HasFocus returns whether or not this primitive has focus. +func (p *Pages) HasFocus() bool { + for _, page := range p.pages { + if page.Item.HasFocus() { + return true + } + } + return false +} + +// Focus is called by the application when the primitive receives focus. +func (p *Pages) Focus(delegate func(p Primitive)) { + if delegate == nil { + return // We cannot delegate so we cannot focus. + } + p.setFocus = delegate + var topItem Primitive + for _, page := range p.pages { + if page.Visible { + topItem = page.Item + } + } + if topItem != nil { + delegate(topItem) + } +} + +// Draw draws this primitive onto the screen. +func (p *Pages) Draw(screen tcell.Screen) { + p.Box.DrawForSubclass(screen, p) + for _, page := range p.pages { + if !page.Visible { + continue + } + if page.Resize { + x, y, width, height := p.GetInnerRect() + page.Item.SetRect(x, y, width, height) + } + page.Item.Draw(screen) + } +} + +// MouseHandler returns the mouse handler for this primitive. +func (p *Pages) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + return p.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + if !p.InRect(event.Position()) { + return false, nil + } + + // Pass mouse events along to the last visible page item that takes it. + for index := len(p.pages) - 1; index >= 0; index-- { + page := p.pages[index] + if page.Visible { + consumed, capture = page.Item.MouseHandler()(action, event, setFocus) + if consumed { + return + } + } + } + + return + }) +} + +// InputHandler returns the handler for this primitive. +func (p *Pages) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { + return p.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { + for _, page := range p.pages { + if page.Item.HasFocus() { + if handler := page.Item.InputHandler(); handler != nil { + handler(event, setFocus) + return + } + } + } + }) +} diff --git a/vendor/github.com/rivo/tview/primitive.go b/vendor/github.com/rivo/tview/primitive.go new file mode 100644 index 0000000000..71a4ce9eec --- /dev/null +++ b/vendor/github.com/rivo/tview/primitive.go @@ -0,0 +1,58 @@ +package tview + +import "github.com/gdamore/tcell/v2" + +// Primitive is the top-most interface for all graphical primitives. +type Primitive interface { + // Draw draws this primitive onto the screen. Implementers can call the + // screen's ShowCursor() function but should only do so when they have focus. + // (They will need to keep track of this themselves.) + Draw(screen tcell.Screen) + + // GetRect returns the current position of the primitive, x, y, width, and + // height. + GetRect() (int, int, int, int) + + // SetRect sets a new position of the primitive. + SetRect(x, y, width, height int) + + // InputHandler returns a handler which receives key events when it has focus. + // It is called by the Application class. + // + // A value of nil may also be returned, in which case this primitive cannot + // receive focus and will not process any key events. + // + // The handler will receive the key event and a function that allows it to + // set the focus to a different primitive, so that future key events are sent + // to that primitive. + // + // The Application's Draw() function will be called automatically after the + // handler returns. + // + // The Box class provides functionality to intercept keyboard input. If you + // subclass from Box, it is recommended that you wrap your handler using + // Box.WrapInputHandler() so you inherit that functionality. + InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) + + // Focus is called by the application when the primitive receives focus. + // Implementers may call delegate() to pass the focus on to another primitive. + Focus(delegate func(p Primitive)) + + // HasFocus determines if the primitive has focus. This function must return + // true also if one of this primitive's child elements has focus. + HasFocus() bool + + // Blur is called by the application when the primitive loses focus. + Blur() + + // MouseHandler returns a handler which receives mouse events. + // It is called by the Application class. + // + // A value of nil may also be returned to stop the downward propagation of + // mouse events. + // + // The Box class provides functionality to intercept mouse events. If you + // subclass from Box, it is recommended that you wrap your handler using + // Box.WrapMouseHandler() so you inherit that functionality. + MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) +} diff --git a/vendor/github.com/rivo/tview/semigraphics.go b/vendor/github.com/rivo/tview/semigraphics.go new file mode 100644 index 0000000000..c7607a2700 --- /dev/null +++ b/vendor/github.com/rivo/tview/semigraphics.go @@ -0,0 +1,294 @@ +package tview + +import "github.com/gdamore/tcell/v2" + +// Semigraphics provides an easy way to access unicode characters for drawing. +// +// Named like the unicode characters, 'Semigraphics'-prefix used if unicode block +// isn't prefixed itself. +const ( + // Block: General Punctation U+2000-U+206F (http://unicode.org/charts/PDF/U2000.pdf) + SemigraphicsHorizontalEllipsis rune = '\u2026' // … + + // Block: Box Drawing U+2500-U+257F (http://unicode.org/charts/PDF/U2500.pdf) + BoxDrawingsLightHorizontal rune = '\u2500' // ─ + BoxDrawingsHeavyHorizontal rune = '\u2501' // ━ + BoxDrawingsLightVertical rune = '\u2502' // │ + BoxDrawingsHeavyVertical rune = '\u2503' // ┃ + BoxDrawingsLightTripleDashHorizontal rune = '\u2504' // ┄ + BoxDrawingsHeavyTripleDashHorizontal rune = '\u2505' // ┅ + BoxDrawingsLightTripleDashVertical rune = '\u2506' // ┆ + BoxDrawingsHeavyTripleDashVertical rune = '\u2507' // ┇ + BoxDrawingsLightQuadrupleDashHorizontal rune = '\u2508' // ┈ + BoxDrawingsHeavyQuadrupleDashHorizontal rune = '\u2509' // ┉ + BoxDrawingsLightQuadrupleDashVertical rune = '\u250a' // ┊ + BoxDrawingsHeavyQuadrupleDashVertical rune = '\u250b' // ┋ + BoxDrawingsLightDownAndRight rune = '\u250c' // ┌ + BoxDrawingsDownLighAndRightHeavy rune = '\u250d' // ┍ + BoxDrawingsDownHeavyAndRightLight rune = '\u250e' // ┎ + BoxDrawingsHeavyDownAndRight rune = '\u250f' // ┏ + BoxDrawingsLightDownAndLeft rune = '\u2510' // ┐ + BoxDrawingsDownLighAndLeftHeavy rune = '\u2511' // ┑ + BoxDrawingsDownHeavyAndLeftLight rune = '\u2512' // ┒ + BoxDrawingsHeavyDownAndLeft rune = '\u2513' // ┓ + BoxDrawingsLightUpAndRight rune = '\u2514' // └ + BoxDrawingsUpLightAndRightHeavy rune = '\u2515' // ┕ + BoxDrawingsUpHeavyAndRightLight rune = '\u2516' // ┖ + BoxDrawingsHeavyUpAndRight rune = '\u2517' // ┗ + BoxDrawingsLightUpAndLeft rune = '\u2518' // ┘ + BoxDrawingsUpLightAndLeftHeavy rune = '\u2519' // ┙ + BoxDrawingsUpHeavyAndLeftLight rune = '\u251a' // ┚ + BoxDrawingsHeavyUpAndLeft rune = '\u251b' // ┛ + BoxDrawingsLightVerticalAndRight rune = '\u251c' // ├ + BoxDrawingsVerticalLightAndRightHeavy rune = '\u251d' // ┝ + BoxDrawingsUpHeavyAndRightDownLight rune = '\u251e' // ┞ + BoxDrawingsDownHeacyAndRightUpLight rune = '\u251f' // ┟ + BoxDrawingsVerticalHeavyAndRightLight rune = '\u2520' // ┠ + BoxDrawingsDownLightAnbdRightUpHeavy rune = '\u2521' // ┡ + BoxDrawingsUpLightAndRightDownHeavy rune = '\u2522' // ┢ + BoxDrawingsHeavyVerticalAndRight rune = '\u2523' // ┣ + BoxDrawingsLightVerticalAndLeft rune = '\u2524' // ┤ + BoxDrawingsVerticalLightAndLeftHeavy rune = '\u2525' // ┥ + BoxDrawingsUpHeavyAndLeftDownLight rune = '\u2526' // ┦ + BoxDrawingsDownHeavyAndLeftUpLight rune = '\u2527' // ┧ + BoxDrawingsVerticalheavyAndLeftLight rune = '\u2528' // ┨ + BoxDrawingsDownLightAndLeftUpHeavy rune = '\u2529' // ┨ + BoxDrawingsUpLightAndLeftDownHeavy rune = '\u252a' // ┪ + BoxDrawingsHeavyVerticalAndLeft rune = '\u252b' // ┫ + BoxDrawingsLightDownAndHorizontal rune = '\u252c' // ┬ + BoxDrawingsLeftHeavyAndRightDownLight rune = '\u252d' // ┭ + BoxDrawingsRightHeavyAndLeftDownLight rune = '\u252e' // ┮ + BoxDrawingsDownLightAndHorizontalHeavy rune = '\u252f' // ┯ + BoxDrawingsDownHeavyAndHorizontalLight rune = '\u2530' // ┰ + BoxDrawingsRightLightAndLeftDownHeavy rune = '\u2531' // ┱ + BoxDrawingsLeftLightAndRightDownHeavy rune = '\u2532' // ┲ + BoxDrawingsHeavyDownAndHorizontal rune = '\u2533' // ┳ + BoxDrawingsLightUpAndHorizontal rune = '\u2534' // ┴ + BoxDrawingsLeftHeavyAndRightUpLight rune = '\u2535' // ┵ + BoxDrawingsRightHeavyAndLeftUpLight rune = '\u2536' // ┶ + BoxDrawingsUpLightAndHorizontalHeavy rune = '\u2537' // ┷ + BoxDrawingsUpHeavyAndHorizontalLight rune = '\u2538' // ┸ + BoxDrawingsRightLightAndLeftUpHeavy rune = '\u2539' // ┹ + BoxDrawingsLeftLightAndRightUpHeavy rune = '\u253a' // ┺ + BoxDrawingsHeavyUpAndHorizontal rune = '\u253b' // ┻ + BoxDrawingsLightVerticalAndHorizontal rune = '\u253c' // ┼ + BoxDrawingsLeftHeavyAndRightVerticalLight rune = '\u253d' // ┽ + BoxDrawingsRightHeavyAndLeftVerticalLight rune = '\u253e' // ┾ + BoxDrawingsVerticalLightAndHorizontalHeavy rune = '\u253f' // ┿ + BoxDrawingsUpHeavyAndDownHorizontalLight rune = '\u2540' // ╀ + BoxDrawingsDownHeavyAndUpHorizontalLight rune = '\u2541' // ╁ + BoxDrawingsVerticalHeavyAndHorizontalLight rune = '\u2542' // ╂ + BoxDrawingsLeftUpHeavyAndRightDownLight rune = '\u2543' // ╃ + BoxDrawingsRightUpHeavyAndLeftDownLight rune = '\u2544' // ╄ + BoxDrawingsLeftDownHeavyAndRightUpLight rune = '\u2545' // ╅ + BoxDrawingsRightDownHeavyAndLeftUpLight rune = '\u2546' // ╆ + BoxDrawingsDownLightAndUpHorizontalHeavy rune = '\u2547' // ╇ + BoxDrawingsUpLightAndDownHorizontalHeavy rune = '\u2548' // ╈ + BoxDrawingsRightLightAndLeftVerticalHeavy rune = '\u2549' // ╉ + BoxDrawingsLeftLightAndRightVerticalHeavy rune = '\u254a' // ╊ + BoxDrawingsHeavyVerticalAndHorizontal rune = '\u254b' // ╋ + BoxDrawingsLightDoubleDashHorizontal rune = '\u254c' // ╌ + BoxDrawingsHeavyDoubleDashHorizontal rune = '\u254d' // ╍ + BoxDrawingsLightDoubleDashVertical rune = '\u254e' // ╎ + BoxDrawingsHeavyDoubleDashVertical rune = '\u254f' // ╏ + BoxDrawingsDoubleHorizontal rune = '\u2550' // ═ + BoxDrawingsDoubleVertical rune = '\u2551' // ║ + BoxDrawingsDownSingleAndRightDouble rune = '\u2552' // ╒ + BoxDrawingsDownDoubleAndRightSingle rune = '\u2553' // ╓ + BoxDrawingsDoubleDownAndRight rune = '\u2554' // ╔ + BoxDrawingsDownSingleAndLeftDouble rune = '\u2555' // ╕ + BoxDrawingsDownDoubleAndLeftSingle rune = '\u2556' // ╖ + BoxDrawingsDoubleDownAndLeft rune = '\u2557' // ╗ + BoxDrawingsUpSingleAndRightDouble rune = '\u2558' // ╘ + BoxDrawingsUpDoubleAndRightSingle rune = '\u2559' // ╙ + BoxDrawingsDoubleUpAndRight rune = '\u255a' // ╚ + BoxDrawingsUpSingleAndLeftDouble rune = '\u255b' // ╛ + BoxDrawingsUpDobuleAndLeftSingle rune = '\u255c' // ╜ + BoxDrawingsDoubleUpAndLeft rune = '\u255d' // ╝ + BoxDrawingsVerticalSingleAndRightDouble rune = '\u255e' // ╞ + BoxDrawingsVerticalDoubleAndRightSingle rune = '\u255f' // ╟ + BoxDrawingsDoubleVerticalAndRight rune = '\u2560' // ╠ + BoxDrawingsVerticalSingleAndLeftDouble rune = '\u2561' // ╡ + BoxDrawingsVerticalDoubleAndLeftSingle rune = '\u2562' // ╢ + BoxDrawingsDoubleVerticalAndLeft rune = '\u2563' // ╣ + BoxDrawingsDownSingleAndHorizontalDouble rune = '\u2564' // ╤ + BoxDrawingsDownDoubleAndHorizontalSingle rune = '\u2565' // ╥ + BoxDrawingsDoubleDownAndHorizontal rune = '\u2566' // ╦ + BoxDrawingsUpSingleAndHorizontalDouble rune = '\u2567' // ╧ + BoxDrawingsUpDoubleAndHorizontalSingle rune = '\u2568' // ╨ + BoxDrawingsDoubleUpAndHorizontal rune = '\u2569' // ╩ + BoxDrawingsVerticalSingleAndHorizontalDouble rune = '\u256a' // ╪ + BoxDrawingsVerticalDoubleAndHorizontalSingle rune = '\u256b' // ╫ + BoxDrawingsDoubleVerticalAndHorizontal rune = '\u256c' // ╬ + BoxDrawingsLightArcDownAndRight rune = '\u256d' // ╭ + BoxDrawingsLightArcDownAndLeft rune = '\u256e' // ╮ + BoxDrawingsLightArcUpAndLeft rune = '\u256f' // ╯ + BoxDrawingsLightArcUpAndRight rune = '\u2570' // ╰ + BoxDrawingsLightDiagonalUpperRightToLowerLeft rune = '\u2571' // ╱ + BoxDrawingsLightDiagonalUpperLeftToLowerRight rune = '\u2572' // ╲ + BoxDrawingsLightDiagonalCross rune = '\u2573' // ╳ + BoxDrawingsLightLeft rune = '\u2574' // ╴ + BoxDrawingsLightUp rune = '\u2575' // ╵ + BoxDrawingsLightRight rune = '\u2576' // ╶ + BoxDrawingsLightDown rune = '\u2577' // ╷ + BoxDrawingsHeavyLeft rune = '\u2578' // ╸ + BoxDrawingsHeavyUp rune = '\u2579' // ╹ + BoxDrawingsHeavyRight rune = '\u257a' // ╺ + BoxDrawingsHeavyDown rune = '\u257b' // ╻ + BoxDrawingsLightLeftAndHeavyRight rune = '\u257c' // ╼ + BoxDrawingsLightUpAndHeavyDown rune = '\u257d' // ╽ + BoxDrawingsHeavyLeftAndLightRight rune = '\u257e' // ╾ + BoxDrawingsHeavyUpAndLightDown rune = '\u257f' // ╿ +) + +// SemigraphicJoints is a map for joining semigraphic (or otherwise) runes. +// So far only light lines are supported but if you want to change the border +// styling you need to provide the joints, too. +// The matching will be sorted ascending by rune value, so you don't need to +// provide all rune combinations, +// e.g. (─) + (│) = (┼) will also match (│) + (─) = (┼) +var SemigraphicJoints = map[string]rune{ + // (─) + (│) = (┼) + string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightVertical}): BoxDrawingsLightVerticalAndHorizontal, + // (─) + (┌) = (┬) + string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightDownAndRight}): BoxDrawingsLightDownAndHorizontal, + // (─) + (┐) = (┬) + string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightDownAndLeft}): BoxDrawingsLightDownAndHorizontal, + // (─) + (└) = (┴) + string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightUpAndRight}): BoxDrawingsLightUpAndHorizontal, + // (─) + (┘) = (┴) + string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightUpAndLeft}): BoxDrawingsLightUpAndHorizontal, + // (─) + (├) = (┼) + string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightVerticalAndRight}): BoxDrawingsLightVerticalAndHorizontal, + // (─) + (┤) = (┼) + string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightVerticalAndLeft}): BoxDrawingsLightVerticalAndHorizontal, + // (─) + (┬) = (┬) + string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightDownAndHorizontal}): BoxDrawingsLightDownAndHorizontal, + // (─) + (┴) = (┴) + string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightUpAndHorizontal, + // (─) + (┼) = (┼) + string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + + // (│) + (┌) = (├) + string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightDownAndRight}): BoxDrawingsLightVerticalAndRight, + // (│) + (┐) = (┤) + string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightDownAndLeft}): BoxDrawingsLightVerticalAndLeft, + // (│) + (└) = (├) + string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightUpAndRight}): BoxDrawingsLightVerticalAndRight, + // (│) + (┘) = (┤) + string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightUpAndLeft}): BoxDrawingsLightVerticalAndLeft, + // (│) + (├) = (├) + string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightVerticalAndRight}): BoxDrawingsLightVerticalAndRight, + // (│) + (┤) = (┤) + string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightVerticalAndLeft}): BoxDrawingsLightVerticalAndLeft, + // (│) + (┬) = (┼) + string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightDownAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + // (│) + (┴) = (┼) + string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + // (│) + (┼) = (┼) + string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + + // (┌) + (┐) = (┬) + string([]rune{BoxDrawingsLightDownAndRight, BoxDrawingsLightDownAndLeft}): BoxDrawingsLightDownAndHorizontal, + // (┌) + (└) = (├) + string([]rune{BoxDrawingsLightDownAndRight, BoxDrawingsLightUpAndRight}): BoxDrawingsLightVerticalAndRight, + // (┌) + (┘) = (┼) + string([]rune{BoxDrawingsLightDownAndRight, BoxDrawingsLightUpAndLeft}): BoxDrawingsLightVerticalAndHorizontal, + // (┌) + (├) = (├) + string([]rune{BoxDrawingsLightDownAndRight, BoxDrawingsLightVerticalAndRight}): BoxDrawingsLightVerticalAndRight, + // (┌) + (┤) = (┼) + string([]rune{BoxDrawingsLightDownAndRight, BoxDrawingsLightVerticalAndLeft}): BoxDrawingsLightVerticalAndHorizontal, + // (┌) + (┬) = (┬) + string([]rune{BoxDrawingsLightDownAndRight, BoxDrawingsLightDownAndHorizontal}): BoxDrawingsLightDownAndHorizontal, + // (┌) + (┴) = (┼) + string([]rune{BoxDrawingsLightDownAndRight, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + // (┌) + (┴) = (┼) + string([]rune{BoxDrawingsLightDownAndRight, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + + // (┐) + (└) = (┼) + string([]rune{BoxDrawingsLightDownAndLeft, BoxDrawingsLightUpAndRight}): BoxDrawingsLightVerticalAndHorizontal, + // (┐) + (┘) = (┤) + string([]rune{BoxDrawingsLightDownAndLeft, BoxDrawingsLightUpAndLeft}): BoxDrawingsLightVerticalAndLeft, + // (┐) + (├) = (┼) + string([]rune{BoxDrawingsLightDownAndLeft, BoxDrawingsLightVerticalAndRight}): BoxDrawingsLightVerticalAndHorizontal, + // (┐) + (┤) = (┤) + string([]rune{BoxDrawingsLightDownAndLeft, BoxDrawingsLightVerticalAndLeft}): BoxDrawingsLightVerticalAndLeft, + // (┐) + (┬) = (┬) + string([]rune{BoxDrawingsLightDownAndLeft, BoxDrawingsLightDownAndHorizontal}): BoxDrawingsLightDownAndHorizontal, + // (┐) + (┴) = (┼) + string([]rune{BoxDrawingsLightDownAndLeft, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + // (┐) + (┼) = (┼) + string([]rune{BoxDrawingsLightDownAndLeft, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + + // (└) + (┘) = (┴) + string([]rune{BoxDrawingsLightUpAndRight, BoxDrawingsLightUpAndLeft}): BoxDrawingsLightUpAndHorizontal, + // (└) + (├) = (├) + string([]rune{BoxDrawingsLightUpAndRight, BoxDrawingsLightVerticalAndRight}): BoxDrawingsLightVerticalAndRight, + // (└) + (┤) = (┼) + string([]rune{BoxDrawingsLightUpAndRight, BoxDrawingsLightVerticalAndLeft}): BoxDrawingsLightVerticalAndHorizontal, + // (└) + (┬) = (┼) + string([]rune{BoxDrawingsLightUpAndRight, BoxDrawingsLightDownAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + // (└) + (┴) = (┴) + string([]rune{BoxDrawingsLightUpAndRight, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightUpAndHorizontal, + // (└) + (┼) = (┼) + string([]rune{BoxDrawingsLightUpAndRight, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + + // (┘) + (├) = (┼) + string([]rune{BoxDrawingsLightUpAndLeft, BoxDrawingsLightVerticalAndRight}): BoxDrawingsLightVerticalAndHorizontal, + // (┘) + (┤) = (┤) + string([]rune{BoxDrawingsLightUpAndLeft, BoxDrawingsLightVerticalAndLeft}): BoxDrawingsLightVerticalAndLeft, + // (┘) + (┬) = (┼) + string([]rune{BoxDrawingsLightUpAndLeft, BoxDrawingsLightDownAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + // (┘) + (┴) = (┴) + string([]rune{BoxDrawingsLightUpAndLeft, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightUpAndHorizontal, + // (┘) + (┼) = (┼) + string([]rune{BoxDrawingsLightUpAndLeft, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + + // (├) + (┤) = (┼) + string([]rune{BoxDrawingsLightVerticalAndRight, BoxDrawingsLightVerticalAndLeft}): BoxDrawingsLightVerticalAndHorizontal, + // (├) + (┬) = (┼) + string([]rune{BoxDrawingsLightVerticalAndRight, BoxDrawingsLightDownAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + // (├) + (┴) = (┼) + string([]rune{BoxDrawingsLightVerticalAndRight, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + // (├) + (┼) = (┼) + string([]rune{BoxDrawingsLightVerticalAndRight, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + + // (┤) + (┬) = (┼) + string([]rune{BoxDrawingsLightVerticalAndLeft, BoxDrawingsLightDownAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + // (┤) + (┴) = (┼) + string([]rune{BoxDrawingsLightVerticalAndLeft, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + // (┤) + (┼) = (┼) + string([]rune{BoxDrawingsLightVerticalAndLeft, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + + // (┬) + (┴) = (┼) + string([]rune{BoxDrawingsLightDownAndHorizontal, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + // (┬) + (┼) = (┼) + string([]rune{BoxDrawingsLightDownAndHorizontal, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, + + // (┴) + (┼) = (┼) + string([]rune{BoxDrawingsLightUpAndHorizontal, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, +} + +// PrintJoinedSemigraphics prints a semigraphics rune into the screen at the given +// position with the given style, joining it with any existing semigraphics +// rune.At this point, only regular single line borders are supported. +func PrintJoinedSemigraphics(screen tcell.Screen, x, y int, ch rune, style tcell.Style) { + previous, _, _, _ := screen.GetContent(x, y) + + // What's the resulting rune? + var result rune + if ch == previous { + result = ch + } else { + if ch < previous { + previous, ch = ch, previous + } + result = SemigraphicJoints[string([]rune{previous, ch})] + } + if result == 0 { + result = ch + } + + // We only print something if we have something. + screen.SetContent(x, y, result, nil, style) +} diff --git a/vendor/github.com/rivo/tview/styles.go b/vendor/github.com/rivo/tview/styles.go new file mode 100644 index 0000000000..16f1fefbb2 --- /dev/null +++ b/vendor/github.com/rivo/tview/styles.go @@ -0,0 +1,35 @@ +package tview + +import "github.com/gdamore/tcell/v2" + +// Theme defines the colors used when primitives are initialized. +type Theme struct { + PrimitiveBackgroundColor tcell.Color // Main background color for primitives. + ContrastBackgroundColor tcell.Color // Background color for contrasting elements. + MoreContrastBackgroundColor tcell.Color // Background color for even more contrasting elements. + BorderColor tcell.Color // Box borders. + TitleColor tcell.Color // Box titles. + GraphicsColor tcell.Color // Graphics. + PrimaryTextColor tcell.Color // Primary text. + SecondaryTextColor tcell.Color // Secondary text (e.g. labels). + TertiaryTextColor tcell.Color // Tertiary text (e.g. subtitles, notes). + InverseTextColor tcell.Color // Text on primary-colored backgrounds. + ContrastSecondaryTextColor tcell.Color // Secondary text on ContrastBackgroundColor-colored backgrounds. +} + +// Styles defines the theme for applications. The default is for a black +// background and some basic colors: black, white, yellow, green, cyan, and +// blue. +var Styles = Theme{ + PrimitiveBackgroundColor: tcell.ColorBlack, + ContrastBackgroundColor: tcell.ColorBlue, + MoreContrastBackgroundColor: tcell.ColorGreen, + BorderColor: tcell.ColorWhite, + TitleColor: tcell.ColorWhite, + GraphicsColor: tcell.ColorWhite, + PrimaryTextColor: tcell.ColorWhite, + SecondaryTextColor: tcell.ColorYellow, + TertiaryTextColor: tcell.ColorGreen, + InverseTextColor: tcell.ColorBlue, + ContrastSecondaryTextColor: tcell.ColorDarkCyan, +} diff --git a/vendor/github.com/rivo/tview/table.go b/vendor/github.com/rivo/tview/table.go new file mode 100644 index 0000000000..83951408fb --- /dev/null +++ b/vendor/github.com/rivo/tview/table.go @@ -0,0 +1,1298 @@ +package tview + +import ( + "sort" + + "github.com/gdamore/tcell/v2" + colorful "github.com/lucasb-eyer/go-colorful" +) + +// TableCell represents one cell inside a Table. You can instantiate this type +// directly but all colors (background and text) will be set to their default +// which is black. +type TableCell struct { + // The reference object. + Reference interface{} + + // The text to be displayed in the table cell. + Text string + + // The alignment of the cell text. One of AlignLeft (default), AlignCenter, + // or AlignRight. + Align int + + // The maximum width of the cell in screen space. This is used to give a + // column a maximum width. Any cell text whose screen width exceeds this width + // is cut off. Set to 0 if there is no maximum width. + MaxWidth int + + // If the total table width is less than the available width, this value is + // used to add extra width to a column. See SetExpansion() for details. + Expansion int + + // The color of the cell text. + Color tcell.Color + + // The background color of the cell. + BackgroundColor tcell.Color + + // If set to true, the BackgroundColor is not used and the cell will have + // the background color of the table. + Transparent bool + + // The style attributes of the cell. + Attributes tcell.AttrMask + + // If set to true, this cell cannot be selected. + NotSelectable bool + + // An optional handler for mouse clicks. This also fires if the cell is not + // selectable. If true is returned, no additional "selected" event is fired + // on selectable cells. + Clicked func() bool + + // The position and width of the cell the last time table was drawn. + x, y, width int +} + +// NewTableCell returns a new table cell with sensible defaults. That is, left +// aligned text with the primary text color (see Styles) and a transparent +// background (using the background of the Table). +func NewTableCell(text string) *TableCell { + return &TableCell{ + Text: text, + Align: AlignLeft, + Color: Styles.PrimaryTextColor, + BackgroundColor: Styles.PrimitiveBackgroundColor, + Transparent: true, + } +} + +// SetText sets the cell's text. +func (c *TableCell) SetText(text string) *TableCell { + c.Text = text + return c +} + +// SetAlign sets the cell's text alignment, one of AlignLeft, AlignCenter, or +// AlignRight. +func (c *TableCell) SetAlign(align int) *TableCell { + c.Align = align + return c +} + +// SetMaxWidth sets maximum width of the cell in screen space. This is used to +// give a column a maximum width. Any cell text whose screen width exceeds this +// width is cut off. Set to 0 if there is no maximum width. +func (c *TableCell) SetMaxWidth(maxWidth int) *TableCell { + c.MaxWidth = maxWidth + return c +} + +// SetExpansion sets the value by which the column of this cell expands if the +// available width for the table is more than the table width (prior to applying +// this expansion value). This is a proportional value. The amount of unused +// horizontal space is divided into widths to be added to each column. How much +// extra width a column receives depends on the expansion value: A value of 0 +// (the default) will not cause the column to increase in width. Other values +// are proportional, e.g. a value of 2 will cause a column to grow by twice +// the amount of a column with a value of 1. +// +// Since this value affects an entire column, the maximum over all visible cells +// in that column is used. +// +// This function panics if a negative value is provided. +func (c *TableCell) SetExpansion(expansion int) *TableCell { + if expansion < 0 { + panic("Table cell expansion values may not be negative") + } + c.Expansion = expansion + return c +} + +// SetTextColor sets the cell's text color. +func (c *TableCell) SetTextColor(color tcell.Color) *TableCell { + c.Color = color + return c +} + +// SetBackgroundColor sets the cell's background color. This will also cause the +// cell's Transparent flag to be set to "false". +func (c *TableCell) SetBackgroundColor(color tcell.Color) *TableCell { + c.BackgroundColor = color + c.Transparent = false + return c +} + +// SetTransparency sets the background transparency of this cell. A value of +// "true" will cause the cell to use the table's background color. A value of +// "false" will cause it to use its own background color. +func (c *TableCell) SetTransparency(transparent bool) *TableCell { + c.Transparent = transparent + return c +} + +// SetAttributes sets the cell's text attributes. You can combine different +// attributes using bitmask operations: +// +// cell.SetAttributes(tcell.AttrUnderline | tcell.AttrBold) +func (c *TableCell) SetAttributes(attr tcell.AttrMask) *TableCell { + c.Attributes = attr + return c +} + +// SetStyle sets the cell's style (foreground color, background color, and +// attributes) all at once. +func (c *TableCell) SetStyle(style tcell.Style) *TableCell { + c.Color, c.BackgroundColor, c.Attributes = style.Decompose() + return c +} + +// SetSelectable sets whether or not this cell can be selected by the user. +func (c *TableCell) SetSelectable(selectable bool) *TableCell { + c.NotSelectable = !selectable + return c +} + +// SetReference allows you to store a reference of any type in this cell. This +// will allow you to establish a mapping between the cell and your +// actual data. +func (c *TableCell) SetReference(reference interface{}) *TableCell { + c.Reference = reference + return c +} + +// GetReference returns this cell's reference object. +func (c *TableCell) GetReference() interface{} { + return c.Reference +} + +// GetLastPosition returns the position of the table cell the last time it was +// drawn on screen. If the cell is not on screen, the return values are +// undefined. +// +// Because the Table class will attempt to keep selected cells on screen, this +// function is most useful in response to a "selected" event (see +// SetSelectedFunc()) or a "selectionChanged" event (see +// SetSelectionChangedFunc()). +func (c *TableCell) GetLastPosition() (x, y, width int) { + return c.x, c.y, c.width +} + +// SetClickedFunc sets a handler which fires when this cell is clicked. This is +// independent of whether the cell is selectable or not. But for selectable +// cells, if the function returns "true", the "selected" event is not fired. +func (c *TableCell) SetClickedFunc(clicked func() bool) *TableCell { + c.Clicked = clicked + return c +} + +// Table visualizes two-dimensional data consisting of rows and columns. Each +// Table cell is defined via SetCell() by the TableCell type. They can be added +// dynamically to the table and changed any time. +// +// The most compact display of a table is without borders. Each row will then +// occupy one row on screen and columns are separated by the rune defined via +// SetSeparator() (a space character by default). +// +// When borders are turned on (via SetBorders()), each table cell is surrounded +// by lines. Therefore one table row will require two rows on screen. +// +// Columns will use as much horizontal space as they need. You can constrain +// their size with the MaxWidth parameter of the TableCell type. +// +// Fixed Columns +// +// You can define fixed rows and rolumns via SetFixed(). They will always stay +// in their place, even when the table is scrolled. Fixed rows are always the +// top rows. Fixed columns are always the leftmost columns. +// +// Selections +// +// You can call SetSelectable() to set columns and/or rows to "selectable". If +// the flag is set only for columns, entire columns can be selected by the user. +// If it is set only for rows, entire rows can be selected. If both flags are +// set, individual cells can be selected. The "selected" handler set via +// SetSelectedFunc() is invoked when the user presses Enter on a selection. +// +// Navigation +// +// If the table extends beyond the available space, it can be navigated with +// key bindings similar to Vim: +// +// - h, left arrow: Move left by one column. +// - l, right arrow: Move right by one column. +// - j, down arrow: Move down by one row. +// - k, up arrow: Move up by one row. +// - g, home: Move to the top. +// - G, end: Move to the bottom. +// - Ctrl-F, page down: Move down by one page. +// - Ctrl-B, page up: Move up by one page. +// +// When there is no selection, this affects the entire table (except for fixed +// rows and columns). When there is a selection, the user moves the selection. +// The class will attempt to keep the selection from moving out of the screen. +// +// Use SetInputCapture() to override or modify keyboard input. +// +// See https://github.com/rivo/tview/wiki/Table for an example. +type Table struct { + *Box + + // Whether or not this table has borders around each cell. + borders bool + + // The color of the borders or the separator. + bordersColor tcell.Color + + // If there are no borders, the column separator. + separator rune + + // The cells of the table. Rows first, then columns. + cells [][]*TableCell + + // The rightmost column in the data set. + lastColumn int + + // If true, when calculating the widths of the columns, all rows are evaluated + // instead of only the visible ones. + evaluateAllRows bool + + // The number of fixed rows / columns. + fixedRows, fixedColumns int + + // Whether or not rows or columns can be selected. If both are set to true, + // cells can be selected. + rowsSelectable, columnsSelectable bool + + // The currently selected row and column. + selectedRow, selectedColumn int + + // The number of rows/columns by which the table is scrolled down/to the + // right. + rowOffset, columnOffset int + + // If set to true, the table's last row will always be visible. + trackEnd bool + + // The number of visible rows the last time the table was drawn. + visibleRows int + + // The indices of the visible columns as of the last time the table was drawn. + visibleColumnIndices []int + + // The net widths of the visible columns as of the last time the table was + // drawn. + visibleColumnWidths []int + + // The style of the selected rows. If this value is the empty struct, + // selected rows are simply inverted. + selectedStyle tcell.Style + + // An optional function which gets called when the user presses Enter on a + // selected cell. If entire rows selected, the column value is undefined. + // Likewise for entire columns. + selected func(row, column int) + + // An optional function which gets called when the user changes the selection. + // If entire rows selected, the column value is undefined. + // Likewise for entire columns. + selectionChanged func(row, column int) + + // An optional function which gets called when the user presses Escape, Tab, + // or Backtab. Also when the user presses Enter if nothing is selectable. + done func(key tcell.Key) +} + +// NewTable returns a new table. +func NewTable() *Table { + return &Table{ + Box: NewBox(), + bordersColor: Styles.GraphicsColor, + separator: ' ', + lastColumn: -1, + } +} + +// Clear removes all table data. +func (t *Table) Clear() *Table { + t.cells = nil + t.lastColumn = -1 + return t +} + +// SetBorders sets whether or not each cell in the table is surrounded by a +// border. +func (t *Table) SetBorders(show bool) *Table { + t.borders = show + return t +} + +// SetBordersColor sets the color of the cell borders. +func (t *Table) SetBordersColor(color tcell.Color) *Table { + t.bordersColor = color + return t +} + +// SetSelectedStyle sets a specific style for selected cells. If no such style +// is set, per default, selected cells are inverted (i.e. their foreground and +// background colors are swapped). +// +// To reset a previous setting to its default, make the following call: +// +// table.SetSelectedStyle(tcell.Style{}) +func (t *Table) SetSelectedStyle(style tcell.Style) *Table { + t.selectedStyle = style + return t +} + +// SetSeparator sets the character used to fill the space between two +// neighboring cells. This is a space character ' ' per default but you may +// want to set it to Borders.Vertical (or any other rune) if the column +// separation should be more visible. If cell borders are activated, this is +// ignored. +// +// Separators have the same color as borders. +func (t *Table) SetSeparator(separator rune) *Table { + t.separator = separator + return t +} + +// SetFixed sets the number of fixed rows and columns which are always visible +// even when the rest of the cells are scrolled out of view. Rows are always the +// top-most ones. Columns are always the left-most ones. +func (t *Table) SetFixed(rows, columns int) *Table { + t.fixedRows, t.fixedColumns = rows, columns + return t +} + +// SetSelectable sets the flags which determine what can be selected in a table. +// There are three selection modi: +// +// - rows = false, columns = false: Nothing can be selected. +// - rows = true, columns = false: Rows can be selected. +// - rows = false, columns = true: Columns can be selected. +// - rows = true, columns = true: Individual cells can be selected. +func (t *Table) SetSelectable(rows, columns bool) *Table { + t.rowsSelectable, t.columnsSelectable = rows, columns + return t +} + +// GetSelectable returns what can be selected in a table. Refer to +// SetSelectable() for details. +func (t *Table) GetSelectable() (rows, columns bool) { + return t.rowsSelectable, t.columnsSelectable +} + +// GetSelection returns the position of the current selection. +// If entire rows are selected, the column index is undefined. +// Likewise for entire columns. +func (t *Table) GetSelection() (row, column int) { + return t.selectedRow, t.selectedColumn +} + +// Select sets the selected cell. Depending on the selection settings +// specified via SetSelectable(), this may be an entire row or column, or even +// ignored completely. The "selection changed" event is fired if such a callback +// is available (even if the selection ends up being the same as before and even +// if cells are not selectable). +func (t *Table) Select(row, column int) *Table { + t.selectedRow, t.selectedColumn = row, column + if t.selectionChanged != nil { + t.selectionChanged(row, column) + } + return t +} + +// SetOffset sets how many rows and columns should be skipped when drawing the +// table. This is useful for large tables that do not fit on the screen. +// Navigating a selection can change these values. +// +// Fixed rows and columns are never skipped. +func (t *Table) SetOffset(row, column int) *Table { + t.rowOffset, t.columnOffset = row, column + t.trackEnd = false + return t +} + +// GetOffset returns the current row and column offset. This indicates how many +// rows and columns the table is scrolled down and to the right. +func (t *Table) GetOffset() (row, column int) { + return t.rowOffset, t.columnOffset +} + +// SetEvaluateAllRows sets a flag which determines the rows to be evaluated when +// calculating the widths of the table's columns. When false, only visible rows +// are evaluated. When true, all rows in the table are evaluated. +// +// Set this flag to true to avoid shifting column widths when the table is +// scrolled. (May be slower for large tables.) +func (t *Table) SetEvaluateAllRows(all bool) *Table { + t.evaluateAllRows = all + return t +} + +// SetSelectedFunc sets a handler which is called whenever the user presses the +// Enter key on a selected cell/row/column. The handler receives the position of +// the selection and its cell contents. If entire rows are selected, the column +// index is undefined. Likewise for entire columns. +func (t *Table) SetSelectedFunc(handler func(row, column int)) *Table { + t.selected = handler + return t +} + +// SetSelectionChangedFunc sets a handler which is called whenever the current +// selection changes. The handler receives the position of the new selection. +// If entire rows are selected, the column index is undefined. Likewise for +// entire columns. +func (t *Table) SetSelectionChangedFunc(handler func(row, column int)) *Table { + t.selectionChanged = handler + return t +} + +// SetDoneFunc sets a handler which is called whenever the user presses the +// Escape, Tab, or Backtab key. If nothing is selected, it is also called when +// user presses the Enter key (because pressing Enter on a selection triggers +// the "selected" handler set via SetSelectedFunc()). +func (t *Table) SetDoneFunc(handler func(key tcell.Key)) *Table { + t.done = handler + return t +} + +// SetCell sets the content of a cell the specified position. It is ok to +// directly instantiate a TableCell object. If the cell has content, at least +// the Text and Color fields should be set. +// +// Note that setting cells in previously unknown rows and columns will +// automatically extend the internal table representation with empty TableCell +// objects, e.g. starting with a row of 100,000 will immediately create 100,000 +// empty rows. +// +// To avoid unnecessary garbage collection, fill columns from left to right. +func (t *Table) SetCell(row, column int, cell *TableCell) *Table { + if row >= len(t.cells) { + t.cells = append(t.cells, make([][]*TableCell, row-len(t.cells)+1)...) + } + rowLen := len(t.cells[row]) + if column >= rowLen { + t.cells[row] = append(t.cells[row], make([]*TableCell, column-rowLen+1)...) + for c := rowLen; c < column; c++ { + t.cells[row][c] = &TableCell{} + } + } + t.cells[row][column] = cell + if column > t.lastColumn { + t.lastColumn = column + } + return t +} + +// SetCellSimple calls SetCell() with the given text, left-aligned, in white. +func (t *Table) SetCellSimple(row, column int, text string) *Table { + t.SetCell(row, column, NewTableCell(text)) + return t +} + +// GetCell returns the contents of the cell at the specified position. A valid +// TableCell object is always returned but it will be uninitialized if the cell +// was not previously set. Such an uninitialized object will not automatically +// be inserted. Therefore, repeated calls to this function may return different +// pointers for uninitialized cells. +func (t *Table) GetCell(row, column int) *TableCell { + if row >= len(t.cells) || column >= len(t.cells[row]) { + return &TableCell{} + } + return t.cells[row][column] +} + +// RemoveRow removes the row at the given position from the table. If there is +// no such row, this has no effect. +func (t *Table) RemoveRow(row int) *Table { + if row < 0 || row >= len(t.cells) { + return t + } + + t.cells = append(t.cells[:row], t.cells[row+1:]...) + + return t +} + +// RemoveColumn removes the column at the given position from the table. If +// there is no such column, this has no effect. +func (t *Table) RemoveColumn(column int) *Table { + for row := range t.cells { + if column < 0 || column >= len(t.cells[row]) { + continue + } + t.cells[row] = append(t.cells[row][:column], t.cells[row][column+1:]...) + } + + return t +} + +// InsertRow inserts a row before the row with the given index. Cells on the +// given row and below will be shifted to the bottom by one row. If "row" is +// equal or larger than the current number of rows, this function has no effect. +func (t *Table) InsertRow(row int) *Table { + if row >= len(t.cells) { + return t + } + t.cells = append(t.cells, nil) // Extend by one. + copy(t.cells[row+1:], t.cells[row:]) // Shift down. + t.cells[row] = nil // New row is uninitialized. + return t +} + +// InsertColumn inserts a column before the column with the given index. Cells +// in the given column and to its right will be shifted to the right by one +// column. Rows that have fewer initialized cells than "column" will remain +// unchanged. +func (t *Table) InsertColumn(column int) *Table { + for row := range t.cells { + if column >= len(t.cells[row]) { + continue + } + t.cells[row] = append(t.cells[row], nil) // Extend by one. + copy(t.cells[row][column+1:], t.cells[row][column:]) // Shift to the right. + t.cells[row][column] = &TableCell{} // New element is an uninitialized table cell. + } + return t +} + +// GetRowCount returns the number of rows in the table. +func (t *Table) GetRowCount() int { + return len(t.cells) +} + +// GetColumnCount returns the (maximum) number of columns in the table. +func (t *Table) GetColumnCount() int { + if len(t.cells) == 0 { + return 0 + } + return t.lastColumn + 1 +} + +// cellAt returns the row and column located at the given screen coordinates. +// Each returned value may be negative if there is no row and/or cell. This +// function will also process coordinates outside the table's inner rectangle so +// callers will need to check for bounds themselves. +func (t *Table) cellAt(x, y int) (row, column int) { + rectX, rectY, _, _ := t.GetInnerRect() + + // Determine row as seen on screen. + if t.borders { + row = (y - rectY - 1) / 2 + } else { + row = y - rectY + } + + // Respect fixed rows and row offset. + if row >= 0 { + if row >= t.fixedRows { + row += t.rowOffset + } + if row >= len(t.cells) { + row = -1 + } + } + + // Saerch for the clicked column. + column = -1 + if x >= rectX { + columnX := rectX + if t.borders { + columnX++ + } + for index, width := range t.visibleColumnWidths { + columnX += width + 1 + if x < columnX { + column = t.visibleColumnIndices[index] + break + } + } + } + + return +} + +// ScrollToBeginning scrolls the table to the beginning to that the top left +// corner of the table is shown. Note that this position may be corrected if +// there is a selection. +func (t *Table) ScrollToBeginning() *Table { + t.trackEnd = false + t.columnOffset = 0 + t.rowOffset = 0 + return t +} + +// ScrollToEnd scrolls the table to the beginning to that the bottom left corner +// of the table is shown. Adding more rows to the table will cause it to +// automatically scroll with the new data. Note that this position may be +// corrected if there is a selection. +func (t *Table) ScrollToEnd() *Table { + t.trackEnd = true + t.columnOffset = 0 + t.rowOffset = len(t.cells) + return t +} + +// Draw draws this primitive onto the screen. +func (t *Table) Draw(screen tcell.Screen) { + t.Box.DrawForSubclass(screen, t) + + // What's our available screen space? + _, totalHeight := screen.Size() + x, y, width, height := t.GetInnerRect() + if t.borders { + t.visibleRows = height / 2 + } else { + t.visibleRows = height + } + + // Return the cell at the specified position (nil if it doesn't exist). + getCell := func(row, column int) *TableCell { + if row < 0 || column < 0 || row >= len(t.cells) || column >= len(t.cells[row]) { + return nil + } + return t.cells[row][column] + } + + // If this cell is not selectable, find the next one. + if t.rowsSelectable || t.columnsSelectable { + if t.selectedColumn < 0 { + t.selectedColumn = 0 + } + if t.selectedRow < 0 { + t.selectedRow = 0 + } + for t.selectedRow < len(t.cells) { + cell := getCell(t.selectedRow, t.selectedColumn) + if cell != nil && !cell.NotSelectable { + break + } + t.selectedColumn++ + if t.selectedColumn > t.lastColumn { + t.selectedColumn = 0 + t.selectedRow++ + } + } + } + + // Clamp row offsets. + if t.rowsSelectable { + if t.selectedRow >= t.fixedRows && t.selectedRow < t.fixedRows+t.rowOffset { + t.rowOffset = t.selectedRow - t.fixedRows + t.trackEnd = false + } + if t.borders { + if 2*(t.selectedRow+1-t.rowOffset) >= height { + t.rowOffset = t.selectedRow + 1 - height/2 + t.trackEnd = false + } + } else { + if t.selectedRow+1-t.rowOffset >= height { + t.rowOffset = t.selectedRow + 1 - height + t.trackEnd = false + } + } + } + if t.borders { + if 2*(len(t.cells)-t.rowOffset) < height { + t.trackEnd = true + } + } else { + if len(t.cells)-t.rowOffset < height { + t.trackEnd = true + } + } + if t.trackEnd { + if t.borders { + t.rowOffset = len(t.cells) - height/2 + } else { + t.rowOffset = len(t.cells) - height + } + } + if t.rowOffset < 0 { + t.rowOffset = 0 + } + + // Clamp column offset. (Only left side here. The right side is more + // difficult and we'll do it below.) + if t.columnsSelectable && t.selectedColumn >= t.fixedColumns && t.selectedColumn < t.fixedColumns+t.columnOffset { + t.columnOffset = t.selectedColumn - t.fixedColumns + } + if t.columnOffset < 0 { + t.columnOffset = 0 + } + if t.selectedColumn < 0 { + t.selectedColumn = 0 + } + + // Determine the indices and widths of the columns and rows which fit on the + // screen. + var ( + columns, rows, allRows, widths []int + tableHeight, tableWidth int + ) + rowStep := 1 + if t.borders { + rowStep = 2 // With borders, every table row takes two screen rows. + tableWidth = 1 // We start at the second character because of the left table border. + } + if t.evaluateAllRows { + allRows = make([]int, len(t.cells)) + for row := range t.cells { + allRows[row] = row + } + } + indexRow := func(row int) bool { // Determine if this row is visible, store its index. + if tableHeight >= height { + return false + } + rows = append(rows, row) + tableHeight += rowStep + return true + } + for row := 0; row < t.fixedRows && row < len(t.cells); row++ { // Do the fixed rows first. + if !indexRow(row) { + break + } + } + for row := t.fixedRows + t.rowOffset; row < len(t.cells); row++ { // Then the remaining rows. + if !indexRow(row) { + break + } + } + var ( + skipped, lastTableWidth, expansionTotal int + expansions []int + ) +ColumnLoop: + for column := 0; ; column++ { + // If we've moved beyond the right border, we stop or skip a column. + for tableWidth-1 >= width { // -1 because we include one extra column if the separator falls on the right end of the box. + // We've moved beyond the available space. + if column < t.fixedColumns { + break ColumnLoop // We're in the fixed area. We're done. + } + if !t.columnsSelectable && skipped >= t.columnOffset { + break ColumnLoop // There is no selection and we've already reached the offset. + } + if t.columnsSelectable && t.selectedColumn-skipped == t.fixedColumns { + break ColumnLoop // The selected column reached the leftmost point before disappearing. + } + if t.columnsSelectable && skipped >= t.columnOffset && + (t.selectedColumn < column && lastTableWidth < width-1 && tableWidth < width-1 || t.selectedColumn < column-1) { + break ColumnLoop // We've skipped as many as requested and the selection is visible. + } + if len(columns) <= t.fixedColumns { + break // Nothing to skip. + } + + // We need to skip a column. + skipped++ + lastTableWidth -= widths[t.fixedColumns] + 1 + tableWidth -= widths[t.fixedColumns] + 1 + columns = append(columns[:t.fixedColumns], columns[t.fixedColumns+1:]...) + widths = append(widths[:t.fixedColumns], widths[t.fixedColumns+1:]...) + expansions = append(expansions[:t.fixedColumns], expansions[t.fixedColumns+1:]...) + } + + // What's this column's width (without expansion)? + maxWidth := -1 + expansion := 0 + evaluationRows := rows + if t.evaluateAllRows { + evaluationRows = allRows + } + for _, row := range evaluationRows { + if cell := getCell(row, column); cell != nil { + _, _, _, _, _, _, cellWidth := decomposeString(cell.Text, true, false) + if cell.MaxWidth > 0 && cell.MaxWidth < cellWidth { + cellWidth = cell.MaxWidth + } + if cellWidth > maxWidth { + maxWidth = cellWidth + } + if cell.Expansion > expansion { + expansion = cell.Expansion + } + } + } + if maxWidth < 0 { + break // No more cells found in this column. + } + + // Store new column info at the end. + columns = append(columns, column) + widths = append(widths, maxWidth) + lastTableWidth = tableWidth + tableWidth += maxWidth + 1 + expansions = append(expansions, expansion) + expansionTotal += expansion + } + t.columnOffset = skipped + + // If we have space left, distribute it. + if tableWidth < width { + toDistribute := width - tableWidth + for index, expansion := range expansions { + if expansionTotal <= 0 { + break + } + expWidth := toDistribute * expansion / expansionTotal + widths[index] += expWidth + toDistribute -= expWidth + expansionTotal -= expansion + } + } + + // Helper function which draws border runes. + borderStyle := tcell.StyleDefault.Background(t.backgroundColor).Foreground(t.bordersColor) + drawBorder := func(colX, rowY int, ch rune) { + screen.SetContent(x+colX, y+rowY, ch, nil, borderStyle) + } + + // Draw the cells (and borders). + var columnX int + if !t.borders { + columnX-- + } + for columnIndex, column := range columns { + columnWidth := widths[columnIndex] + for rowY, row := range rows { + if t.borders { + // Draw borders. + rowY *= 2 + for pos := 0; pos < columnWidth && columnX+1+pos < width; pos++ { + drawBorder(columnX+pos+1, rowY, Borders.Horizontal) + } + ch := Borders.Cross + if columnIndex == 0 { + if rowY == 0 { + ch = Borders.TopLeft + } else { + ch = Borders.LeftT + } + } else if rowY == 0 { + ch = Borders.TopT + } + drawBorder(columnX, rowY, ch) + rowY++ + if rowY >= height || y+rowY >= totalHeight { + break // No space for the text anymore. + } + drawBorder(columnX, rowY, Borders.Vertical) + } else if columnIndex > 0 { + // Draw separator. + drawBorder(columnX, rowY, t.separator) + } + + // Get the cell. + cell := getCell(row, column) + if cell == nil { + continue + } + + // Draw text. + finalWidth := columnWidth + if columnX+1+columnWidth >= width { + finalWidth = width - columnX - 1 + } + cell.x, cell.y, cell.width = x+columnX+1, y+rowY, finalWidth + _, printed, _, _ := printWithStyle(screen, cell.Text, x+columnX+1, y+rowY, 0, finalWidth, cell.Align, tcell.StyleDefault.Foreground(cell.Color).Attributes(cell.Attributes), true) + if TaggedStringWidth(cell.Text)-printed > 0 && printed > 0 { + _, _, style, _ := screen.GetContent(x+columnX+finalWidth, y+rowY) + printWithStyle(screen, string(SemigraphicsHorizontalEllipsis), x+columnX+finalWidth, 0, y+rowY, 1, AlignLeft, style, false) + } + } + + // Draw bottom border. + if rowY := 2 * len(rows); t.borders && rowY < height { + for pos := 0; pos < columnWidth && columnX+1+pos < width; pos++ { + drawBorder(columnX+pos+1, rowY, Borders.Horizontal) + } + ch := Borders.BottomT + if columnIndex == 0 { + ch = Borders.BottomLeft + } + drawBorder(columnX, rowY, ch) + } + + columnX += columnWidth + 1 + } + + // Draw right border. + if t.borders && len(t.cells) > 0 && columnX < width { + for rowY := range rows { + rowY *= 2 + if rowY+1 < height { + drawBorder(columnX, rowY+1, Borders.Vertical) + } + ch := Borders.RightT + if rowY == 0 { + ch = Borders.TopRight + } + drawBorder(columnX, rowY, ch) + } + if rowY := 2 * len(rows); rowY < height { + drawBorder(columnX, rowY, Borders.BottomRight) + } + } + + // Helper function which colors the background of a box. + // backgroundTransparent == true => Don't modify background color (when invert == false). + // textTransparent == true => Don't modify text color (when invert == false). + // attr == 0 => Don't change attributes. + // invert == true => Ignore attr, set text to backgroundColor or t.backgroundColor; + // set background to textColor. + colorBackground := func(fromX, fromY, w, h int, backgroundColor, textColor tcell.Color, backgroundTransparent, textTransparent bool, attr tcell.AttrMask, invert bool) { + for by := 0; by < h && fromY+by < y+height; by++ { + for bx := 0; bx < w && fromX+bx < x+width; bx++ { + m, c, style, _ := screen.GetContent(fromX+bx, fromY+by) + fg, bg, a := style.Decompose() + if invert { + style = style.Background(textColor).Foreground(backgroundColor) + } else { + if !backgroundTransparent { + bg = backgroundColor + } + if !textTransparent { + fg = textColor + } + if attr != 0 { + a = attr + } + style = style.Background(bg).Foreground(fg).Attributes(a) + } + screen.SetContent(fromX+bx, fromY+by, m, c, style) + } + } + } + + // Color the cell backgrounds. To avoid undesirable artefacts, we combine + // the drawing of a cell by background color, selected cells last. + type cellInfo struct { + x, y, w, h int + cell *TableCell + selected bool + } + cellsByBackgroundColor := make(map[tcell.Color][]*cellInfo) + var backgroundColors []tcell.Color + for rowY, row := range rows { + columnX := 0 + rowSelected := t.rowsSelectable && !t.columnsSelectable && row == t.selectedRow + for columnIndex, column := range columns { + columnWidth := widths[columnIndex] + cell := getCell(row, column) + if cell == nil { + continue + } + bx, by, bw, bh := x+columnX, y+rowY, columnWidth+1, 1 + if t.borders { + by = y + rowY*2 + bw++ + bh = 3 + } + columnSelected := t.columnsSelectable && !t.rowsSelectable && column == t.selectedColumn + cellSelected := !cell.NotSelectable && (columnSelected || rowSelected || t.rowsSelectable && t.columnsSelectable && column == t.selectedColumn && row == t.selectedRow) + entries, ok := cellsByBackgroundColor[cell.BackgroundColor] + cellsByBackgroundColor[cell.BackgroundColor] = append(entries, &cellInfo{ + x: bx, + y: by, + w: bw, + h: bh, + cell: cell, + selected: cellSelected, + }) + if !ok { + backgroundColors = append(backgroundColors, cell.BackgroundColor) + } + columnX += columnWidth + 1 + } + } + sort.Slice(backgroundColors, func(i int, j int) bool { + // Draw brightest colors last (i.e. on top). + r, g, b := backgroundColors[i].RGB() + c := colorful.Color{R: float64(r) / 255, G: float64(g) / 255, B: float64(b) / 255} + _, _, li := c.Hcl() + r, g, b = backgroundColors[j].RGB() + c = colorful.Color{R: float64(r) / 255, G: float64(g) / 255, B: float64(b) / 255} + _, _, lj := c.Hcl() + return li < lj + }) + selFg, selBg, selAttr := t.selectedStyle.Decompose() + for _, bgColor := range backgroundColors { + entries := cellsByBackgroundColor[bgColor] + for _, info := range entries { + if info.selected { + if t.selectedStyle != (tcell.Style{}) { + defer colorBackground(info.x, info.y, info.w, info.h, selBg, selFg, false, false, selAttr, false) + } else { + defer colorBackground(info.x, info.y, info.w, info.h, bgColor, info.cell.Color, false, false, 0, true) + } + } else { + colorBackground(info.x, info.y, info.w, info.h, bgColor, info.cell.Color, info.cell.Transparent, true, 0, false) + } + } + } + + // Remember column infos. + t.visibleColumnIndices, t.visibleColumnWidths = columns, widths +} + +// InputHandler returns the handler for this primitive. +func (t *Table) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { + return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { + key := event.Key() + + if (!t.rowsSelectable && !t.columnsSelectable && key == tcell.KeyEnter) || + key == tcell.KeyEscape || + key == tcell.KeyTab || + key == tcell.KeyBacktab { + if t.done != nil { + t.done(key) + } + return + } + + // Movement functions. + previouslySelectedRow, previouslySelectedColumn := t.selectedRow, t.selectedColumn + var ( + getCell = func(row, column int) *TableCell { + if row < 0 || column < 0 || row >= len(t.cells) || column >= len(t.cells[row]) { + return nil + } + return t.cells[row][column] + } + + previous = func() { + for t.selectedRow >= 0 { + cell := getCell(t.selectedRow, t.selectedColumn) + if cell != nil && !cell.NotSelectable { + return + } + t.selectedColumn-- + if t.selectedColumn < 0 { + t.selectedColumn = t.lastColumn + t.selectedRow-- + } + } + } + + next = func() { + if t.selectedColumn > t.lastColumn { + t.selectedColumn = 0 + t.selectedRow++ + if t.selectedRow >= len(t.cells) { + t.selectedRow = len(t.cells) - 1 + } + } + for t.selectedRow < len(t.cells) { + cell := getCell(t.selectedRow, t.selectedColumn) + if cell != nil && !cell.NotSelectable { + return + } + t.selectedColumn++ + if t.selectedColumn > t.lastColumn { + t.selectedColumn = 0 + t.selectedRow++ + } + } + t.selectedColumn = t.lastColumn + t.selectedRow = len(t.cells) - 1 + previous() + } + + home = func() { + if t.rowsSelectable { + t.selectedRow = 0 + t.selectedColumn = 0 + next() + } else { + t.trackEnd = false + t.rowOffset = 0 + t.columnOffset = 0 + } + } + + end = func() { + if t.rowsSelectable { + t.selectedRow = len(t.cells) - 1 + t.selectedColumn = t.lastColumn + previous() + } else { + t.trackEnd = true + t.columnOffset = 0 + } + } + + down = func() { + if t.rowsSelectable { + t.selectedRow++ + if t.selectedRow >= len(t.cells) { + t.selectedRow = len(t.cells) - 1 + } + next() + } else { + t.rowOffset++ + } + } + + up = func() { + if t.rowsSelectable { + t.selectedRow-- + if t.selectedRow < 0 { + t.selectedRow = 0 + } + previous() + } else { + t.trackEnd = false + t.rowOffset-- + } + } + + left = func() { + if t.columnsSelectable { + t.selectedColumn-- + if t.selectedColumn < 0 { + t.selectedColumn = 0 + } + previous() + } else { + t.columnOffset-- + } + } + + right = func() { + if t.columnsSelectable { + t.selectedColumn++ + next() + } else { + t.columnOffset++ + } + } + + pageDown = func() { + offsetAmount := t.visibleRows - t.fixedRows + if offsetAmount < 0 { + offsetAmount = 0 + } + + if t.rowsSelectable { + t.selectedRow += offsetAmount + if t.selectedRow >= len(t.cells) { + t.selectedRow = len(t.cells) - 1 + } + next() + } else { + t.rowOffset += offsetAmount + } + } + + pageUp = func() { + offsetAmount := t.visibleRows - t.fixedRows + if offsetAmount < 0 { + offsetAmount = 0 + } + + if t.rowsSelectable { + t.selectedRow -= offsetAmount + if t.selectedRow < 0 { + t.selectedRow = 0 + } + previous() + } else { + t.trackEnd = false + t.rowOffset -= offsetAmount + } + } + ) + + switch key { + case tcell.KeyRune: + switch event.Rune() { + case 'g': + home() + case 'G': + end() + case 'j': + down() + case 'k': + up() + case 'h': + left() + case 'l': + right() + } + case tcell.KeyHome: + home() + case tcell.KeyEnd: + end() + case tcell.KeyUp: + up() + case tcell.KeyDown: + down() + case tcell.KeyLeft: + left() + case tcell.KeyRight: + right() + case tcell.KeyPgDn, tcell.KeyCtrlF: + pageDown() + case tcell.KeyPgUp, tcell.KeyCtrlB: + pageUp() + case tcell.KeyEnter: + if (t.rowsSelectable || t.columnsSelectable) && t.selected != nil { + t.selected(t.selectedRow, t.selectedColumn) + } + } + + // If the selection has changed, notify the handler. + if t.selectionChanged != nil && + (t.rowsSelectable && previouslySelectedRow != t.selectedRow || + t.columnsSelectable && previouslySelectedColumn != t.selectedColumn) { + t.selectionChanged(t.selectedRow, t.selectedColumn) + } + }) +} + +// MouseHandler returns the mouse handler for this primitive. +func (t *Table) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + return t.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + x, y := event.Position() + if !t.InRect(x, y) { + return false, nil + } + + switch action { + case MouseLeftClick: + selectEvent := true + row, column := t.cellAt(x, y) + if row >= 0 && row < len(t.cells) && column >= 0 { + cells := t.cells[row] + if column < len(cells) { + cell := cells[column] + if cell != nil && cell.Clicked != nil { + if noSelect := cell.Clicked(); noSelect { + selectEvent = false + } + } + } + } + if selectEvent && (t.rowsSelectable || t.columnsSelectable) { + t.Select(row, column) + } + setFocus(t) + consumed = true + case MouseScrollUp: + t.trackEnd = false + t.rowOffset-- + consumed = true + case MouseScrollDown: + t.rowOffset++ + consumed = true + } + + return + }) +} diff --git a/vendor/github.com/rivo/tview/textview.go b/vendor/github.com/rivo/tview/textview.go new file mode 100644 index 0000000000..fa7c662f8c --- /dev/null +++ b/vendor/github.com/rivo/tview/textview.go @@ -0,0 +1,1261 @@ +package tview + +import ( + "bytes" + "fmt" + "regexp" + "strings" + "sync" + "unicode/utf8" + + "github.com/gdamore/tcell/v2" + colorful "github.com/lucasb-eyer/go-colorful" + runewidth "github.com/mattn/go-runewidth" + "github.com/rivo/uniseg" +) + +var ( + openColorRegex = regexp.MustCompile(`\[([a-zA-Z]*|#[0-9a-zA-Z]*)$`) + openRegionRegex = regexp.MustCompile(`\["[a-zA-Z0-9_,;: \-\.]*"?$`) + newLineRegex = regexp.MustCompile(`\r?\n`) + + // TabSize is the number of spaces with which a tab character will be replaced. + TabSize = 4 +) + +// textViewIndex contains information about a line displayed in the text view. +type textViewIndex struct { + Line int // The index into the "buffer" slice. + Pos int // The index into the "buffer" string (byte position). + NextPos int // The (byte) index of the next line start within this buffer string. + Width int // The screen width of this line. + ForegroundColor string // The starting foreground color ("" = don't change, "-" = reset). + BackgroundColor string // The starting background color ("" = don't change, "-" = reset). + Attributes string // The starting attributes ("" = don't change, "-" = reset). + Region string // The starting region ID. +} + +// textViewRegion contains information about a region. +type textViewRegion struct { + // The region ID. + ID string + + // The starting and end screen position of the region as determined the last + // time Draw() was called. A negative value indicates out-of-rect positions. + FromX, FromY, ToX, ToY int +} + +// TextView is a box which displays text. It implements the io.Writer interface +// so you can stream text to it. This does not trigger a redraw automatically +// but if a handler is installed via SetChangedFunc(), you can cause it to be +// redrawn. (See SetChangedFunc() for more details.) +// +// Navigation +// +// If the text view is scrollable (the default), text is kept in a buffer which +// may be larger than the screen and can be navigated similarly to Vim: +// +// - h, left arrow: Move left. +// - l, right arrow: Move right. +// - j, down arrow: Move down. +// - k, up arrow: Move up. +// - g, home: Move to the top. +// - G, end: Move to the bottom. +// - Ctrl-F, page down: Move down by one page. +// - Ctrl-B, page up: Move up by one page. +// +// If the text is not scrollable, any text above the top visible line is +// discarded. +// +// Use SetInputCapture() to override or modify keyboard input. +// +// Colors +// +// If dynamic colors are enabled via SetDynamicColors(), text color can be +// changed dynamically by embedding color strings in square brackets. This works +// the same way as anywhere else. Please see the package documentation for more +// information. +// +// Regions and Highlights +// +// If regions are enabled via SetRegions(), you can define text regions within +// the text and assign region IDs to them. Text regions start with region tags. +// Region tags are square brackets that contain a region ID in double quotes, +// for example: +// +// We define a ["rg"]region[""] here. +// +// A text region ends with the next region tag. Tags with no region ID ([""]) +// don't start new regions. They can therefore be used to mark the end of a +// region. Region IDs must satisfy the following regular expression: +// +// [a-zA-Z0-9_,;: \-\.]+ +// +// Regions can be highlighted by calling the Highlight() function with one or +// more region IDs. This can be used to display search results, for example. +// +// The ScrollToHighlight() function can be used to jump to the currently +// highlighted region once when the text view is drawn the next time. +// +// See https://github.com/rivo/tview/wiki/TextView for an example. +type TextView struct { + sync.Mutex + *Box + + // The text buffer. + buffer []string + + // The last bytes that have been received but are not part of the buffer yet. + recentBytes []byte + + // The processed line index. This is nil if the buffer has changed and needs + // to be re-indexed. + index []*textViewIndex + + // The text alignment, one of AlignLeft, AlignCenter, or AlignRight. + align int + + // Information about visible regions as of the last call to Draw(). + regionInfos []*textViewRegion + + // Indices into the "index" slice which correspond to the first line of the + // first highlight and the last line of the last highlight. This is calculated + // during re-indexing. Set to -1 if there is no current highlight. + fromHighlight, toHighlight int + + // The screen space column of the highlight in its first line. Set to -1 if + // there is no current highlight. + posHighlight int + + // A set of region IDs that are currently highlighted. + highlights map[string]struct{} + + // The last width for which the current text view is drawn. + lastWidth int + + // The screen width of the longest line in the index (not the buffer). + longestLine int + + // The index of the first line shown in the text view. + lineOffset int + + // If set to true, the text view will always remain at the end of the content. + trackEnd bool + + // The number of characters to be skipped on each line (not in wrap mode). + columnOffset int + + // The maximum number of lines kept in the line index, effectively the + // latest word-wrapped lines. Ignored if 0. + maxLines int + + // The height of the content the last time the text view was drawn. + pageSize int + + // If set to true, the text view will keep a buffer of text which can be + // navigated when the text is longer than what fits into the box. + scrollable bool + + // If set to true, lines that are longer than the available width are wrapped + // onto the next line. If set to false, any characters beyond the available + // width are discarded. + wrap bool + + // If set to true and if wrap is also true, lines are split at spaces or + // after punctuation characters. + wordWrap bool + + // The (starting) color of the text. + textColor tcell.Color + + // If set to true, the text color can be changed dynamically by piping color + // strings in square brackets to the text view. + dynamicColors bool + + // If set to true, region tags can be used to define regions. + regions bool + + // A temporary flag which, when true, will automatically bring the current + // highlight(s) into the visible screen. + scrollToHighlights bool + + // If true, setting new highlights will be a XOR instead of an overwrite + // operation. + toggleHighlights bool + + // An optional function which is called when the content of the text view has + // changed. + changed func() + + // An optional function which is called when the user presses one of the + // following keys: Escape, Enter, Tab, Backtab. + done func(tcell.Key) + + // An optional function which is called when one or more regions were + // highlighted. + highlighted func(added, removed, remaining []string) +} + +// NewTextView returns a new text view. +func NewTextView() *TextView { + return &TextView{ + Box: NewBox(), + highlights: make(map[string]struct{}), + lineOffset: -1, + scrollable: true, + align: AlignLeft, + wrap: true, + textColor: Styles.PrimaryTextColor, + regions: false, + dynamicColors: false, + } +} + +// SetScrollable sets the flag that decides whether or not the text view is +// scrollable. If true, text is kept in a buffer and can be navigated. If false, +// the last line will always be visible. +func (t *TextView) SetScrollable(scrollable bool) *TextView { + t.scrollable = scrollable + if !scrollable { + t.trackEnd = true + } + return t +} + +// SetWrap sets the flag that, if true, leads to lines that are longer than the +// available width being wrapped onto the next line. If false, any characters +// beyond the available width are not displayed. +func (t *TextView) SetWrap(wrap bool) *TextView { + if t.wrap != wrap { + t.index = nil + } + t.wrap = wrap + return t +} + +// SetWordWrap sets the flag that, if true and if the "wrap" flag is also true +// (see SetWrap()), wraps the line at spaces or after punctuation marks. Note +// that trailing spaces will not be printed. +// +// This flag is ignored if the "wrap" flag is false. +func (t *TextView) SetWordWrap(wrapOnWords bool) *TextView { + if t.wordWrap != wrapOnWords { + t.index = nil + } + t.wordWrap = wrapOnWords + return t +} + +// SetMaxLines sets the maximum number of lines for this text view. Lines at the +// beginning of the text will be discarded when the text view is drawn, so as to +// remain below this value. Broken lines via word wrapping are counted +// individually. +// +// Note that GetText() will return the shortened text and may start with color +// and/or region tags that were open at the cutoff point. +// +// A value of 0 (the default) will keep all lines in place. +func (t *TextView) SetMaxLines(maxLines int) *TextView { + t.maxLines = maxLines + return t +} + +// SetTextAlign sets the text alignment within the text view. This must be +// either AlignLeft, AlignCenter, or AlignRight. +func (t *TextView) SetTextAlign(align int) *TextView { + if t.align != align { + t.index = nil + } + t.align = align + return t +} + +// SetTextColor sets the initial color of the text (which can be changed +// dynamically by sending color strings in square brackets to the text view if +// dynamic colors are enabled). +func (t *TextView) SetTextColor(color tcell.Color) *TextView { + t.textColor = color + return t +} + +// SetText sets the text of this text view to the provided string. Previously +// contained text will be removed. +func (t *TextView) SetText(text string) *TextView { + t.Clear() + fmt.Fprint(t, text) + return t +} + +// GetText returns the current text of this text view. If "stripAllTags" is set +// to true, any region/color tags are stripped from the text. +func (t *TextView) GetText(stripAllTags bool) string { + // Get the buffer. + buffer := make([]string, len(t.buffer), len(t.buffer)+1) + copy(buffer, t.buffer) + if !stripAllTags { + buffer = append(buffer, string(t.recentBytes)) + } + + // Add newlines again. + text := strings.Join(buffer, "\n") + + // Strip from tags if required. + if stripAllTags { + if t.regions { + text = regionPattern.ReplaceAllString(text, "") + } + if t.dynamicColors { + text = stripTags(text) + } + if t.regions && !t.dynamicColors { + text = escapePattern.ReplaceAllString(text, `[$1$2]`) + } + } + + return text +} + +// SetDynamicColors sets the flag that allows the text color to be changed +// dynamically. See class description for details. +func (t *TextView) SetDynamicColors(dynamic bool) *TextView { + if t.dynamicColors != dynamic { + t.index = nil + } + t.dynamicColors = dynamic + return t +} + +// SetRegions sets the flag that allows to define regions in the text. See class +// description for details. +func (t *TextView) SetRegions(regions bool) *TextView { + if t.regions != regions { + t.index = nil + } + t.regions = regions + return t +} + +// SetChangedFunc sets a handler function which is called when the text of the +// text view has changed. This is useful when text is written to this io.Writer +// in a separate goroutine. Doing so does not automatically cause the screen to +// be refreshed so you may want to use the "changed" handler to redraw the +// screen. +// +// Note that to avoid race conditions or deadlocks, there are a few rules you +// should follow: +// +// - You can call Application.Draw() from this handler. +// - You can call TextView.HasFocus() from this handler. +// - During the execution of this handler, access to any other variables from +// this primitive or any other primitive must be queued using +// Application.QueueUpdate(). +// +// See package description for details on dealing with concurrency. +func (t *TextView) SetChangedFunc(handler func()) *TextView { + t.changed = handler + return t +} + +// SetDoneFunc sets a handler which is called when the user presses on the +// following keys: Escape, Enter, Tab, Backtab. The key is passed to the +// handler. +func (t *TextView) SetDoneFunc(handler func(key tcell.Key)) *TextView { + t.done = handler + return t +} + +// SetHighlightedFunc sets a handler which is called when the list of currently +// highlighted regions change. It receives a list of region IDs which were newly +// highlighted, those that are not highlighted anymore, and those that remain +// highlighted. +// +// Note that because regions are only determined during drawing, this function +// can only fire for regions that have existed during the last call to Draw(). +func (t *TextView) SetHighlightedFunc(handler func(added, removed, remaining []string)) *TextView { + t.highlighted = handler + return t +} + +// ScrollTo scrolls to the specified row and column (both starting with 0). +func (t *TextView) ScrollTo(row, column int) *TextView { + if !t.scrollable { + return t + } + t.lineOffset = row + t.columnOffset = column + t.trackEnd = false + return t +} + +// ScrollToBeginning scrolls to the top left corner of the text if the text view +// is scrollable. +func (t *TextView) ScrollToBeginning() *TextView { + if !t.scrollable { + return t + } + t.trackEnd = false + t.lineOffset = 0 + t.columnOffset = 0 + return t +} + +// ScrollToEnd scrolls to the bottom left corner of the text if the text view +// is scrollable. Adding new rows to the end of the text view will cause it to +// scroll with the new data. +func (t *TextView) ScrollToEnd() *TextView { + if !t.scrollable { + return t + } + t.trackEnd = true + t.columnOffset = 0 + return t +} + +// GetScrollOffset returns the number of rows and columns that are skipped at +// the top left corner when the text view has been scrolled. +func (t *TextView) GetScrollOffset() (row, column int) { + return t.lineOffset, t.columnOffset +} + +// Clear removes all text from the buffer. +func (t *TextView) Clear() *TextView { + t.buffer = nil + t.recentBytes = nil + t.index = nil + return t +} + +// Highlight specifies which regions should be highlighted. If highlight +// toggling is set to true (see SetToggleHighlights()), the highlight of the +// provided regions is toggled (highlighted regions are un-highlighted and vice +// versa). If toggling is set to false, the provided regions are highlighted and +// all other regions will not be highlighted (you may also provide nil to turn +// off all highlights). +// +// For more information on regions, see class description. Empty region strings +// are ignored. +// +// Text in highlighted regions will be drawn inverted, i.e. with their +// background and foreground colors swapped. +func (t *TextView) Highlight(regionIDs ...string) *TextView { + // Toggle highlights. + if t.toggleHighlights { + var newIDs []string + HighlightLoop: + for regionID := range t.highlights { + for _, id := range regionIDs { + if regionID == id { + continue HighlightLoop + } + } + newIDs = append(newIDs, regionID) + } + for _, regionID := range regionIDs { + if _, ok := t.highlights[regionID]; !ok { + newIDs = append(newIDs, regionID) + } + } + regionIDs = newIDs + } // Now we have a list of region IDs that end up being highlighted. + + // Determine added and removed regions. + var added, removed, remaining []string + if t.highlighted != nil { + for _, regionID := range regionIDs { + if _, ok := t.highlights[regionID]; ok { + remaining = append(remaining, regionID) + delete(t.highlights, regionID) + } else { + added = append(added, regionID) + } + } + for regionID := range t.highlights { + removed = append(removed, regionID) + } + } + + // Make new selection. + t.highlights = make(map[string]struct{}) + for _, id := range regionIDs { + if id == "" { + continue + } + t.highlights[id] = struct{}{} + } + t.index = nil + + // Notify. + if t.highlighted != nil && len(added) > 0 || len(removed) > 0 { + t.highlighted(added, removed, remaining) + } + + return t +} + +// GetHighlights returns the IDs of all currently highlighted regions. +func (t *TextView) GetHighlights() (regionIDs []string) { + for id := range t.highlights { + regionIDs = append(regionIDs, id) + } + return +} + +// SetToggleHighlights sets a flag to determine how regions are highlighted. +// When set to true, the Highlight() function (or a mouse click) will toggle the +// provided/selected regions. When set to false, Highlight() (or a mouse click) +// will simply highlight the provided regions. +func (t *TextView) SetToggleHighlights(toggle bool) *TextView { + t.toggleHighlights = toggle + return t +} + +// ScrollToHighlight will cause the visible area to be scrolled so that the +// highlighted regions appear in the visible area of the text view. This +// repositioning happens the next time the text view is drawn. It happens only +// once so you will need to call this function repeatedly to always keep +// highlighted regions in view. +// +// Nothing happens if there are no highlighted regions or if the text view is +// not scrollable. +func (t *TextView) ScrollToHighlight() *TextView { + if len(t.highlights) == 0 || !t.scrollable || !t.regions { + return t + } + t.index = nil + t.scrollToHighlights = true + t.trackEnd = false + return t +} + +// GetRegionText returns the text of the region with the given ID. If dynamic +// colors are enabled, color tags are stripped from the text. Newlines are +// always returned as '\n' runes. +// +// If the region does not exist or if regions are turned off, an empty string +// is returned. +func (t *TextView) GetRegionText(regionID string) string { + if !t.regions || regionID == "" { + return "" + } + + var ( + buffer bytes.Buffer + currentRegionID string + ) + + for _, str := range t.buffer { + // Find all color tags in this line. + var colorTagIndices [][]int + if t.dynamicColors { + colorTagIndices = colorPattern.FindAllStringIndex(str, -1) + } + + // Find all regions in this line. + var ( + regionIndices [][]int + regions [][]string + ) + if t.regions { + regionIndices = regionPattern.FindAllStringIndex(str, -1) + regions = regionPattern.FindAllStringSubmatch(str, -1) + } + + // Analyze this line. + var currentTag, currentRegion int + for pos, ch := range str { + // Skip any color tags. + if currentTag < len(colorTagIndices) && pos >= colorTagIndices[currentTag][0] && pos < colorTagIndices[currentTag][1] { + if pos == colorTagIndices[currentTag][1]-1 { + currentTag++ + } + if colorTagIndices[currentTag][1]-colorTagIndices[currentTag][0] > 2 { + continue + } + } + + // Skip any regions. + if currentRegion < len(regionIndices) && pos >= regionIndices[currentRegion][0] && pos < regionIndices[currentRegion][1] { + if pos == regionIndices[currentRegion][1]-1 { + if currentRegionID == regionID { + // This is the end of the requested region. We're done. + return buffer.String() + } + currentRegionID = regions[currentRegion][1] + currentRegion++ + } + continue + } + + // Add this rune. + if currentRegionID == regionID { + buffer.WriteRune(ch) + } + } + + // Add newline. + if currentRegionID == regionID { + buffer.WriteRune('\n') + } + } + + return escapePattern.ReplaceAllString(buffer.String(), `[$1$2]`) +} + +// Focus is called when this primitive receives focus. +func (t *TextView) Focus(delegate func(p Primitive)) { + // Implemented here with locking because this is used by layout primitives. + t.Lock() + defer t.Unlock() + t.hasFocus = true +} + +// HasFocus returns whether or not this primitive has focus. +func (t *TextView) HasFocus() bool { + // Implemented here with locking because this may be used in the "changed" + // callback. + t.Lock() + defer t.Unlock() + return t.hasFocus +} + +// Write lets us implement the io.Writer interface. Tab characters will be +// replaced with TabSize space characters. A "\n" or "\r\n" will be interpreted +// as a new line. +func (t *TextView) Write(p []byte) (n int, err error) { + // Notify at the end. + t.Lock() + changed := t.changed + t.Unlock() + if changed != nil { + defer func() { + // We always call the "changed" function in a separate goroutine to avoid + // deadlocks. + go changed() + }() + } + + t.Lock() + defer t.Unlock() + + // Copy data over. + newBytes := append(t.recentBytes, p...) + t.recentBytes = nil + + // If we have a trailing invalid UTF-8 byte, we'll wait. + if r, _ := utf8.DecodeLastRune(p); r == utf8.RuneError { + t.recentBytes = newBytes + return len(p), nil + } + + // If we have a trailing open dynamic color, exclude it. + if t.dynamicColors { + location := openColorRegex.FindIndex(newBytes) + if location != nil { + t.recentBytes = newBytes[location[0]:] + newBytes = newBytes[:location[0]] + } + } + + // If we have a trailing open region, exclude it. + if t.regions { + location := openRegionRegex.FindIndex(newBytes) + if location != nil { + t.recentBytes = newBytes[location[0]:] + newBytes = newBytes[:location[0]] + } + } + + // Transform the new bytes into strings. + newBytes = bytes.Replace(newBytes, []byte{'\t'}, bytes.Repeat([]byte{' '}, TabSize), -1) + for index, line := range newLineRegex.Split(string(newBytes), -1) { + if index == 0 { + if len(t.buffer) == 0 { + t.buffer = []string{line} + } else { + t.buffer[len(t.buffer)-1] += line + } + } else { + t.buffer = append(t.buffer, line) + } + } + + // Reset the index. + t.index = nil + + return len(p), nil +} + +// reindexBuffer re-indexes the buffer such that we can use it to easily draw +// the buffer onto the screen. Each line in the index will contain a pointer +// into the buffer from which on we will print text. It will also contain the +// colors, attributes, and region with which the line starts. +// +// If maxLines is greater than 0, any extra lines will be dropped from the +// buffer. +func (t *TextView) reindexBuffer(width int) { + if t.index != nil { + return // Nothing has changed. We can still use the current index. + } + t.index = nil + t.fromHighlight, t.toHighlight, t.posHighlight = -1, -1, -1 + + // If there's no space, there's no index. + if width < 1 { + return + } + + // Initial states. + regionID := "" + var ( + highlighted bool + foregroundColor, backgroundColor, attributes string + ) + + // Go through each line in the buffer. + for bufferIndex, str := range t.buffer { + colorTagIndices, colorTags, regionIndices, regions, escapeIndices, strippedStr, _ := decomposeString(str, t.dynamicColors, t.regions) + + // Split the line if required. + var splitLines []string + str = strippedStr + if t.wrap && len(str) > 0 { + for len(str) > 0 { + extract := runewidth.Truncate(str, width, "") + if len(extract) == 0 { + // We'll extract at least one grapheme cluster. + gr := uniseg.NewGraphemes(str) + gr.Next() + _, to := gr.Positions() + extract = str[:to] + } + if t.wordWrap && len(extract) < len(str) { + // Add any spaces from the next line. + if spaces := spacePattern.FindStringIndex(str[len(extract):]); spaces != nil && spaces[0] == 0 { + extract = str[:len(extract)+spaces[1]] + } + + // Can we split before the mandatory end? + matches := boundaryPattern.FindAllStringIndex(extract, -1) + if len(matches) > 0 { + // Yes. Let's split there. + extract = extract[:matches[len(matches)-1][1]] + } + } + splitLines = append(splitLines, extract) + str = str[len(extract):] + } + } else { + // No need to split the line. + splitLines = []string{str} + } + + // Create index from split lines. + var originalPos, colorPos, regionPos, escapePos int + for _, splitLine := range splitLines { + line := &textViewIndex{ + Line: bufferIndex, + Pos: originalPos, + ForegroundColor: foregroundColor, + BackgroundColor: backgroundColor, + Attributes: attributes, + Region: regionID, + } + + // Shift original position with tags. + lineLength := len(splitLine) + remainingLength := lineLength + tagEnd := originalPos + totalTagLength := 0 + for { + // Which tag comes next? + nextTag := make([][3]int, 0, 3) + if colorPos < len(colorTagIndices) { + nextTag = append(nextTag, [3]int{colorTagIndices[colorPos][0], colorTagIndices[colorPos][1], 0}) // 0 = color tag. + } + if regionPos < len(regionIndices) { + nextTag = append(nextTag, [3]int{regionIndices[regionPos][0], regionIndices[regionPos][1], 1}) // 1 = region tag. + } + if escapePos < len(escapeIndices) { + nextTag = append(nextTag, [3]int{escapeIndices[escapePos][0], escapeIndices[escapePos][1], 2}) // 2 = escape tag. + } + minPos := -1 + tagIndex := -1 + for index, pair := range nextTag { + if minPos < 0 || pair[0] < minPos { + minPos = pair[0] + tagIndex = index + } + } + + // Is the next tag in range? + if tagIndex < 0 || minPos > tagEnd+remainingLength { + break // No. We're done with this line. + } + + // Advance. + strippedTagStart := nextTag[tagIndex][0] - originalPos - totalTagLength + tagEnd = nextTag[tagIndex][1] + tagLength := tagEnd - nextTag[tagIndex][0] + if nextTag[tagIndex][2] == 2 { + tagLength = 1 + } + totalTagLength += tagLength + remainingLength = lineLength - (tagEnd - originalPos - totalTagLength) + + // Process the tag. + switch nextTag[tagIndex][2] { + case 0: + // Process color tags. + foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colorTags[colorPos]) + colorPos++ + case 1: + // Process region tags. + regionID = regions[regionPos][1] + _, highlighted = t.highlights[regionID] + + // Update highlight range. + if highlighted { + line := len(t.index) + if t.fromHighlight < 0 { + t.fromHighlight, t.toHighlight = line, line + t.posHighlight = stringWidth(splitLine[:strippedTagStart]) + } else if line > t.toHighlight { + t.toHighlight = line + } + } + + regionPos++ + case 2: + // Process escape tags. + escapePos++ + } + } + + // Advance to next line. + originalPos += lineLength + totalTagLength + + // Append this line. + line.NextPos = originalPos + line.Width = stringWidth(splitLine) + t.index = append(t.index, line) + } + + // Word-wrapped lines may have trailing whitespace. Remove it. + if t.wrap && t.wordWrap { + for _, line := range t.index { + str := t.buffer[line.Line][line.Pos:line.NextPos] + spaces := spacePattern.FindAllStringIndex(str, -1) + if spaces != nil && spaces[len(spaces)-1][1] == len(str) { + oldNextPos := line.NextPos + line.NextPos -= spaces[len(spaces)-1][1] - spaces[len(spaces)-1][0] + line.Width -= stringWidth(t.buffer[line.Line][line.NextPos:oldNextPos]) + } + } + } + } + + // Drop lines beyond maxLines. + if t.maxLines > 0 && len(t.index) > t.maxLines { + removedLines := len(t.index) - t.maxLines + + // Adjust the index. + t.index = t.index[removedLines:] + if t.fromHighlight >= 0 { + t.fromHighlight -= removedLines + if t.fromHighlight < 0 { + t.fromHighlight = 0 + } + } + if t.toHighlight >= 0 { + t.toHighlight -= removedLines + if t.toHighlight < 0 { + t.fromHighlight, t.toHighlight, t.posHighlight = -1, -1, -1 + } + } + bufferShift := t.index[0].Line + for _, line := range t.index { + line.Line -= bufferShift + } + + // Adjust the original buffer. + t.buffer = t.buffer[bufferShift:] + var prefix string + if t.index[0].ForegroundColor != "" || t.index[0].BackgroundColor != "" || t.index[0].Attributes != "" { + prefix = fmt.Sprintf("[%s:%s:%s]", t.index[0].ForegroundColor, t.index[0].BackgroundColor, t.index[0].Attributes) + } + if t.index[0].Region != "" { + prefix += fmt.Sprintf(`["%s"]`, t.index[0].Region) + } + posShift := t.index[0].Pos + t.buffer[0] = prefix + t.buffer[0][posShift:] + t.lineOffset -= removedLines + if t.lineOffset < 0 { + t.lineOffset = 0 + } + + // Adjust positions of first buffer line. + posShift -= len(prefix) + for _, line := range t.index { + if line.Line != 0 { + break + } + line.Pos -= posShift + line.NextPos -= posShift + } + } + + // Calculate longest line. + t.longestLine = 0 + for _, line := range t.index { + if line.Width > t.longestLine { + t.longestLine = line.Width + } + } +} + +// Draw draws this primitive onto the screen. +func (t *TextView) Draw(screen tcell.Screen) { + t.Box.DrawForSubclass(screen, t) + t.Lock() + defer t.Unlock() + totalWidth, totalHeight := screen.Size() + + // Get the available size. + x, y, width, height := t.GetInnerRect() + t.pageSize = height + + // If the width has changed, we need to reindex. + if width != t.lastWidth && t.wrap { + t.index = nil + } + t.lastWidth = width + + // Re-index. + t.reindexBuffer(width) + if t.regions { + t.regionInfos = nil + } + + // If we don't have an index, there's nothing to draw. + if t.index == nil { + return + } + + // Move to highlighted regions. + if t.regions && t.scrollToHighlights && t.fromHighlight >= 0 { + // Do we fit the entire height? + if t.toHighlight-t.fromHighlight+1 < height { + // Yes, let's center the highlights. + t.lineOffset = (t.fromHighlight + t.toHighlight - height) / 2 + } else { + // No, let's move to the start of the highlights. + t.lineOffset = t.fromHighlight + } + + // If the highlight is too far to the right, move it to the middle. + if t.posHighlight-t.columnOffset > 3*width/4 { + t.columnOffset = t.posHighlight - width/2 + } + + // If the highlight is off-screen on the left, move it on-screen. + if t.posHighlight-t.columnOffset < 0 { + t.columnOffset = t.posHighlight - width/4 + } + } + t.scrollToHighlights = false + + // Adjust line offset. + if t.lineOffset+height > len(t.index) { + t.trackEnd = true + } + if t.trackEnd { + t.lineOffset = len(t.index) - height + } + if t.lineOffset < 0 { + t.lineOffset = 0 + } + + // Adjust column offset. + if t.align == AlignLeft { + if t.columnOffset+width > t.longestLine { + t.columnOffset = t.longestLine - width + } + if t.columnOffset < 0 { + t.columnOffset = 0 + } + } else if t.align == AlignRight { + if t.columnOffset-width < -t.longestLine { + t.columnOffset = width - t.longestLine + } + if t.columnOffset > 0 { + t.columnOffset = 0 + } + } else { // AlignCenter. + half := (t.longestLine - width) / 2 + if half > 0 { + if t.columnOffset > half { + t.columnOffset = half + } + if t.columnOffset < -half { + t.columnOffset = -half + } + } else { + t.columnOffset = 0 + } + } + + // Draw the buffer. + defaultStyle := tcell.StyleDefault.Foreground(t.textColor).Background(t.backgroundColor) + for line := t.lineOffset; line < len(t.index); line++ { + // Are we done? + if line-t.lineOffset >= height || y+line-t.lineOffset >= totalHeight { + break + } + + // Get the text for this line. + index := t.index[line] + text := t.buffer[index.Line][index.Pos:index.NextPos] + foregroundColor := index.ForegroundColor + backgroundColor := index.BackgroundColor + attributes := index.Attributes + regionID := index.Region + if t.regions { + if len(t.regionInfos) > 0 && t.regionInfos[len(t.regionInfos)-1].ID != regionID { + // End last region. + t.regionInfos[len(t.regionInfos)-1].ToX = x + t.regionInfos[len(t.regionInfos)-1].ToY = y + line - t.lineOffset + } + if regionID != "" && (len(t.regionInfos) == 0 || t.regionInfos[len(t.regionInfos)-1].ID != regionID) { + // Start a new region. + t.regionInfos = append(t.regionInfos, &textViewRegion{ + ID: regionID, + FromX: x, + FromY: y + line - t.lineOffset, + ToX: -1, + ToY: -1, + }) + } + } + + // Process tags. + colorTagIndices, colorTags, regionIndices, regions, escapeIndices, strippedText, _ := decomposeString(text, t.dynamicColors, t.regions) + + // Calculate the position of the line. + var skip, posX int + if t.align == AlignLeft { + posX = -t.columnOffset + } else if t.align == AlignRight { + posX = width - index.Width - t.columnOffset + } else { // AlignCenter. + posX = (width-index.Width)/2 - t.columnOffset + } + if posX < 0 { + skip = -posX + posX = 0 + } + + // Print the line. + if y+line-t.lineOffset >= 0 { + var colorPos, regionPos, escapePos, tagOffset, skipped int + iterateString(strippedText, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { + // Process tags. + for { + if colorPos < len(colorTags) && textPos+tagOffset >= colorTagIndices[colorPos][0] && textPos+tagOffset < colorTagIndices[colorPos][1] { + // Get the color. + foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colorTags[colorPos]) + tagOffset += colorTagIndices[colorPos][1] - colorTagIndices[colorPos][0] + colorPos++ + } else if regionPos < len(regionIndices) && textPos+tagOffset >= regionIndices[regionPos][0] && textPos+tagOffset < regionIndices[regionPos][1] { + // Get the region. + if regionID != "" && len(t.regionInfos) > 0 && t.regionInfos[len(t.regionInfos)-1].ID == regionID { + // End last region. + t.regionInfos[len(t.regionInfos)-1].ToX = x + posX + t.regionInfos[len(t.regionInfos)-1].ToY = y + line - t.lineOffset + } + regionID = regions[regionPos][1] + if regionID != "" { + // Start new region. + t.regionInfos = append(t.regionInfos, &textViewRegion{ + ID: regionID, + FromX: x + posX, + FromY: y + line - t.lineOffset, + ToX: -1, + ToY: -1, + }) + } + tagOffset += regionIndices[regionPos][1] - regionIndices[regionPos][0] + regionPos++ + } else { + break + } + } + + // Skip the second-to-last character of an escape tag. + if escapePos < len(escapeIndices) && textPos+tagOffset == escapeIndices[escapePos][1]-2 { + tagOffset++ + escapePos++ + } + + // Mix the existing style with the new style. + style := overlayStyle(defaultStyle, foregroundColor, backgroundColor, attributes) + + // Do we highlight this character? + var highlighted bool + if regionID != "" { + if _, ok := t.highlights[regionID]; ok { + highlighted = true + } + } + if highlighted { + fg, bg, _ := style.Decompose() + if bg == t.backgroundColor { + r, g, b := fg.RGB() + c := colorful.Color{R: float64(r) / 255, G: float64(g) / 255, B: float64(b) / 255} + _, _, li := c.Hcl() + if li < .5 { + bg = tcell.ColorWhite + } else { + bg = tcell.ColorBlack + } + } + style = style.Background(fg).Foreground(bg) + } + + // Skip to the right. + if !t.wrap && skipped < skip { + skipped += screenWidth + return false + } + + // Stop at the right border. + if posX+screenWidth > width || x+posX >= totalWidth { + return true + } + + // Draw the character. + for offset := screenWidth - 1; offset >= 0; offset-- { + if offset == 0 { + screen.SetContent(x+posX+offset, y+line-t.lineOffset, main, comb, style) + } else { + screen.SetContent(x+posX+offset, y+line-t.lineOffset, ' ', nil, style) + } + } + + // Advance. + posX += screenWidth + return false + }) + } + } + + // If this view is not scrollable, we'll purge the buffer of lines that have + // scrolled out of view. + if !t.scrollable && t.lineOffset > 0 { + if t.lineOffset >= len(t.index) { + t.buffer = nil + } else { + t.buffer = t.buffer[t.index[t.lineOffset].Line:] + } + t.index = nil + t.lineOffset = 0 + } +} + +// InputHandler returns the handler for this primitive. +func (t *TextView) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { + return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { + key := event.Key() + + if key == tcell.KeyEscape || key == tcell.KeyEnter || key == tcell.KeyTab || key == tcell.KeyBacktab { + if t.done != nil { + t.done(key) + } + return + } + + if !t.scrollable { + return + } + + switch key { + case tcell.KeyRune: + switch event.Rune() { + case 'g': // Home. + t.trackEnd = false + t.lineOffset = 0 + t.columnOffset = 0 + case 'G': // End. + t.trackEnd = true + t.columnOffset = 0 + case 'j': // Down. + t.lineOffset++ + case 'k': // Up. + t.trackEnd = false + t.lineOffset-- + case 'h': // Left. + t.columnOffset-- + case 'l': // Right. + t.columnOffset++ + } + case tcell.KeyHome: + t.trackEnd = false + t.lineOffset = 0 + t.columnOffset = 0 + case tcell.KeyEnd: + t.trackEnd = true + t.columnOffset = 0 + case tcell.KeyUp: + t.trackEnd = false + t.lineOffset-- + case tcell.KeyDown: + t.lineOffset++ + case tcell.KeyLeft: + t.columnOffset-- + case tcell.KeyRight: + t.columnOffset++ + case tcell.KeyPgDn, tcell.KeyCtrlF: + t.lineOffset += t.pageSize + case tcell.KeyPgUp, tcell.KeyCtrlB: + t.trackEnd = false + t.lineOffset -= t.pageSize + } + }) +} + +// MouseHandler returns the mouse handler for this primitive. +func (t *TextView) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + return t.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + x, y := event.Position() + if !t.InRect(x, y) { + return false, nil + } + + switch action { + case MouseLeftClick: + if t.regions { + // Find a region to highlight. + for _, region := range t.regionInfos { + if y == region.FromY && x < region.FromX || + y == region.ToY && x >= region.ToX || + region.FromY >= 0 && y < region.FromY || + region.ToY >= 0 && y > region.ToY { + continue + } + t.Highlight(region.ID) + break + } + } + setFocus(t) + consumed = true + case MouseScrollUp: + t.trackEnd = false + t.lineOffset-- + consumed = true + case MouseScrollDown: + t.lineOffset++ + consumed = true + } + + return + }) +} diff --git a/vendor/github.com/rivo/tview/treeview.go b/vendor/github.com/rivo/tview/treeview.go new file mode 100644 index 0000000000..fb9a2ab701 --- /dev/null +++ b/vendor/github.com/rivo/tview/treeview.go @@ -0,0 +1,826 @@ +package tview + +import ( + "github.com/gdamore/tcell/v2" +) + +// Tree navigation events. +const ( + treeNone int = iota + treeHome + treeEnd + treeUp + treeDown + treePageUp + treePageDown + treeParent + treeChild + treeScrollUp // Move up without changing the selection, even when off screen. + treeScrollDown +) + +// TreeNode represents one node in a tree view. +type TreeNode struct { + // The reference object. + reference interface{} + + // This node's child nodes. + children []*TreeNode + + // The item's text. + text string + + // The text color. + color tcell.Color + + // Whether or not this node can be selected. + selectable bool + + // Whether or not this node's children should be displayed. + expanded bool + + // The additional horizontal indent of this node's text. + indent int + + // An optional function which is called when the user selects this node. + selected func() + + // The hierarchy level (0 for the root, 1 for its children, and so on). This + // is only up to date immediately after a call to process() (e.g. via + // Draw()). + level int + + // Temporary member variables. + parent *TreeNode // The parent node (nil for the root). + graphicsX int // The x-coordinate of the left-most graphics rune. + textX int // The x-coordinate of the first rune of the text. +} + +// NewTreeNode returns a new tree node. +func NewTreeNode(text string) *TreeNode { + return &TreeNode{ + text: text, + color: Styles.PrimaryTextColor, + indent: 2, + expanded: true, + selectable: true, + } +} + +// Walk traverses this node's subtree in depth-first, pre-order (NLR) order and +// calls the provided callback function on each traversed node (which includes +// this node) with the traversed node and its parent node (nil for this node). +// The callback returns whether traversal should continue with the traversed +// node's child nodes (true) or not recurse any deeper (false). +func (n *TreeNode) Walk(callback func(node, parent *TreeNode) bool) *TreeNode { + n.parent = nil + nodes := []*TreeNode{n} + for len(nodes) > 0 { + // Pop the top node and process it. + node := nodes[len(nodes)-1] + nodes = nodes[:len(nodes)-1] + if !callback(node, node.parent) { + // Don't add any children. + continue + } + + // Add children in reverse order. + for index := len(node.children) - 1; index >= 0; index-- { + node.children[index].parent = node + nodes = append(nodes, node.children[index]) + } + } + + return n +} + +// SetReference allows you to store a reference of any type in this node. This +// will allow you to establish a mapping between the TreeView hierarchy and your +// internal tree structure. +func (n *TreeNode) SetReference(reference interface{}) *TreeNode { + n.reference = reference + return n +} + +// GetReference returns this node's reference object. +func (n *TreeNode) GetReference() interface{} { + return n.reference +} + +// SetChildren sets this node's child nodes. +func (n *TreeNode) SetChildren(childNodes []*TreeNode) *TreeNode { + n.children = childNodes + return n +} + +// GetText returns this node's text. +func (n *TreeNode) GetText() string { + return n.text +} + +// GetChildren returns this node's children. +func (n *TreeNode) GetChildren() []*TreeNode { + return n.children +} + +// ClearChildren removes all child nodes from this node. +func (n *TreeNode) ClearChildren() *TreeNode { + n.children = nil + return n +} + +// AddChild adds a new child node to this node. +func (n *TreeNode) AddChild(node *TreeNode) *TreeNode { + n.children = append(n.children, node) + return n +} + +// RemoveChild removes a child node from this node. If the child node cannot be +// found, nothing happens. +func (n *TreeNode) RemoveChild(node *TreeNode) *TreeNode { + for index, child := range n.children { + if child == node { + n.children = append(n.children[:index], n.children[index+1:]...) + break + } + } + return n +} + +// SetSelectable sets a flag indicating whether this node can be selected by +// the user. +func (n *TreeNode) SetSelectable(selectable bool) *TreeNode { + n.selectable = selectable + return n +} + +// SetSelectedFunc sets a function which is called when the user selects this +// node by hitting Enter when it is selected. +func (n *TreeNode) SetSelectedFunc(handler func()) *TreeNode { + n.selected = handler + return n +} + +// SetExpanded sets whether or not this node's child nodes should be displayed. +func (n *TreeNode) SetExpanded(expanded bool) *TreeNode { + n.expanded = expanded + return n +} + +// Expand makes the child nodes of this node appear. +func (n *TreeNode) Expand() *TreeNode { + n.expanded = true + return n +} + +// Collapse makes the child nodes of this node disappear. +func (n *TreeNode) Collapse() *TreeNode { + n.expanded = false + return n +} + +// ExpandAll expands this node and all descendent nodes. +func (n *TreeNode) ExpandAll() *TreeNode { + n.Walk(func(node, parent *TreeNode) bool { + node.expanded = true + return true + }) + return n +} + +// CollapseAll collapses this node and all descendent nodes. +func (n *TreeNode) CollapseAll() *TreeNode { + n.Walk(func(node, parent *TreeNode) bool { + n.expanded = false + return true + }) + return n +} + +// IsExpanded returns whether the child nodes of this node are visible. +func (n *TreeNode) IsExpanded() bool { + return n.expanded +} + +// SetText sets the node's text which is displayed. +func (n *TreeNode) SetText(text string) *TreeNode { + n.text = text + return n +} + +// GetColor returns the node's color. +func (n *TreeNode) GetColor() tcell.Color { + return n.color +} + +// SetColor sets the node's text color. +func (n *TreeNode) SetColor(color tcell.Color) *TreeNode { + n.color = color + return n +} + +// SetIndent sets an additional indentation for this node's text. A value of 0 +// keeps the text as far left as possible with a minimum of line graphics. Any +// value greater than that moves the text to the right. +func (n *TreeNode) SetIndent(indent int) *TreeNode { + n.indent = indent + return n +} + +// GetLevel returns the node's level within the hierarchy, where 0 corresponds +// to the root node, 1 corresponds to its children, and so on. This is only +// guaranteed to be up to date immediately after the tree that contains this +// node is drawn. +func (n *TreeNode) GetLevel() int { + return n.level +} + +// TreeView displays tree structures. A tree consists of nodes (TreeNode +// objects) where each node has zero or more child nodes and exactly one parent +// node (except for the root node which has no parent node). +// +// The SetRoot() function is used to specify the root of the tree. Other nodes +// are added locally to the root node or any of its descendents. See the +// TreeNode documentation for details on node attributes. (You can use +// SetReference() to store a reference to nodes of your own tree structure.) +// +// Nodes can be selected by calling SetCurrentNode(). The user can navigate the +// selection or the tree by using the following keys: +// +// - j, down arrow, right arrow: Move (the selection) down by one node. +// - k, up arrow, left arrow: Move (the selection) up by one node. +// - g, home: Move (the selection) to the top. +// - G, end: Move (the selection) to the bottom. +// - J: Move (the selection) up one level. +// - K: Move (the selection) down one level (if it is shown). +// - Ctrl-F, page down: Move (the selection) down by one page. +// - Ctrl-B, page up: Move (the selection) up by one page. +// +// Selected nodes can trigger the "selected" callback when the user hits Enter. +// +// The root node corresponds to level 0, its children correspond to level 1, +// their children to level 2, and so on. Per default, the first level that is +// displayed is 0, i.e. the root node. You can call SetTopLevel() to hide +// levels. +// +// If graphics are turned on (see SetGraphics()), lines indicate the tree's +// hierarchy. Alternative (or additionally), you can set different prefixes +// using SetPrefixes() for different levels, for example to display hierarchical +// bullet point lists. +// +// See https://github.com/rivo/tview/wiki/TreeView for an example. +type TreeView struct { + *Box + + // The root node. + root *TreeNode + + // The currently selected node or nil if no node is selected. + currentNode *TreeNode + + // The movement to be performed during the call to Draw(), one of the + // constants defined above. + movement int + + // The top hierarchical level shown. (0 corresponds to the root level.) + topLevel int + + // Strings drawn before the nodes, based on their level. + prefixes []string + + // Vertical scroll offset. + offsetY int + + // If set to true, all node texts will be aligned horizontally. + align bool + + // If set to true, the tree structure is drawn using lines. + graphics bool + + // The color of the lines. + graphicsColor tcell.Color + + // An optional function which is called when the user has navigated to a new + // tree node. + changed func(node *TreeNode) + + // An optional function which is called when a tree item was selected. + selected func(node *TreeNode) + + // An optional function which is called when the user moves away from this + // primitive. + done func(key tcell.Key) + + // The visible nodes, top-down, as set by process(). + nodes []*TreeNode +} + +// NewTreeView returns a new tree view. +func NewTreeView() *TreeView { + return &TreeView{ + Box: NewBox(), + graphics: true, + graphicsColor: Styles.GraphicsColor, + } +} + +// SetRoot sets the root node of the tree. +func (t *TreeView) SetRoot(root *TreeNode) *TreeView { + t.root = root + return t +} + +// GetRoot returns the root node of the tree. If no such node was previously +// set, nil is returned. +func (t *TreeView) GetRoot() *TreeNode { + return t.root +} + +// SetCurrentNode sets the currently selected node. Provide nil to clear all +// selections. Selected nodes must be visible and selectable, or else the +// selection will be changed to the top-most selectable and visible node. +// +// This function does NOT trigger the "changed" callback. +func (t *TreeView) SetCurrentNode(node *TreeNode) *TreeView { + t.currentNode = node + return t +} + +// GetCurrentNode returns the currently selected node or nil of no node is +// currently selected. +func (t *TreeView) GetCurrentNode() *TreeNode { + return t.currentNode +} + +// SetTopLevel sets the first tree level that is visible with 0 referring to the +// root, 1 to the root's child nodes, and so on. Nodes above the top level are +// not displayed. +func (t *TreeView) SetTopLevel(topLevel int) *TreeView { + t.topLevel = topLevel + return t +} + +// SetPrefixes defines the strings drawn before the nodes' texts. This is a +// slice of strings where each element corresponds to a node's hierarchy level, +// i.e. 0 for the root, 1 for the root's children, and so on (levels will +// cycle). +// +// For example, to display a hierarchical list with bullet points: +// +// treeView.SetGraphics(false). +// SetPrefixes([]string{"* ", "- ", "x "}) +func (t *TreeView) SetPrefixes(prefixes []string) *TreeView { + t.prefixes = prefixes + return t +} + +// SetAlign controls the horizontal alignment of the node texts. If set to true, +// all texts except that of top-level nodes will be placed in the same column. +// If set to false, they will indent with the hierarchy. +func (t *TreeView) SetAlign(align bool) *TreeView { + t.align = align + return t +} + +// SetGraphics sets a flag which determines whether or not line graphics are +// drawn to illustrate the tree's hierarchy. +func (t *TreeView) SetGraphics(showGraphics bool) *TreeView { + t.graphics = showGraphics + return t +} + +// SetGraphicsColor sets the colors of the lines used to draw the tree structure. +func (t *TreeView) SetGraphicsColor(color tcell.Color) *TreeView { + t.graphicsColor = color + return t +} + +// SetChangedFunc sets the function which is called when the user navigates to +// a new tree node. +func (t *TreeView) SetChangedFunc(handler func(node *TreeNode)) *TreeView { + t.changed = handler + return t +} + +// SetSelectedFunc sets the function which is called when the user selects a +// node by pressing Enter on the current selection. +func (t *TreeView) SetSelectedFunc(handler func(node *TreeNode)) *TreeView { + t.selected = handler + return t +} + +// SetDoneFunc sets a handler which is called whenever the user presses the +// Escape, Tab, or Backtab key. +func (t *TreeView) SetDoneFunc(handler func(key tcell.Key)) *TreeView { + t.done = handler + return t +} + +// GetScrollOffset returns the number of node rows that were skipped at the top +// of the tree view. Note that when the user navigates the tree view, this value +// is only updated after the tree view has been redrawn. +func (t *TreeView) GetScrollOffset() int { + return t.offsetY +} + +// GetRowCount returns the number of "visible" nodes. This includes nodes which +// fall outside the tree view's box but notably does not include the children +// of collapsed nodes. Note that this value is only up to date after the tree +// view has been drawn. +func (t *TreeView) GetRowCount() int { + return len(t.nodes) +} + +// process builds the visible tree, populates the "nodes" slice, and processes +// pending selection actions. +func (t *TreeView) process() { + _, _, _, height := t.GetInnerRect() + + // Determine visible nodes and their placement. + var graphicsOffset, maxTextX, parentSelectedIndex int + t.nodes = nil + if t.root == nil { + return + } + selectedIndex := -1 + topLevelGraphicsX := -1 + if t.graphics { + graphicsOffset = 1 + } + t.root.Walk(func(node, parent *TreeNode) bool { + // Set node attributes. + node.parent = parent + if parent == nil { + node.level = 0 + node.graphicsX = 0 + node.textX = 0 + } else { + node.level = parent.level + 1 + node.graphicsX = parent.textX + node.textX = node.graphicsX + graphicsOffset + node.indent + } + if !t.graphics && t.align { + // Without graphics, we align nodes on the first column. + node.textX = 0 + } + if node.level == t.topLevel { + // No graphics for top level nodes. + node.graphicsX = 0 + node.textX = 0 + } + + // Add the node to the list. + if node.level >= t.topLevel { + // This node will be visible. + if node.textX > maxTextX { + maxTextX = node.textX + } + if node == t.currentNode && node.selectable { + selectedIndex = len(t.nodes) + } + + // Maybe we want to skip this level. + if t.topLevel == node.level && (topLevelGraphicsX < 0 || node.graphicsX < topLevelGraphicsX) { + topLevelGraphicsX = node.graphicsX + } + + t.nodes = append(t.nodes, node) + } + + // Keep track of the parent of the selected node. + if selectedIndex < 0 && node.selectable && len(node.children) > 0 && node.expanded { + parentSelectedIndex = len(t.nodes) - 1 + } + + // Recurse if desired. + return node.expanded + }) + + // Post-process positions. + for _, node := range t.nodes { + // If text must align, we correct the positions. + if t.align && node.level > t.topLevel { + node.textX = maxTextX + } + + // If we skipped levels, shift to the left. + if topLevelGraphicsX > 0 { + node.graphicsX -= topLevelGraphicsX + node.textX -= topLevelGraphicsX + } + } + + // Process selection. (Also trigger events if necessary.) + if selectedIndex >= 0 { + // Move the selection. + newSelectedIndex := selectedIndex + MovementSwitch: + switch t.movement { + case treeUp: + for newSelectedIndex > 0 { + newSelectedIndex-- + if t.nodes[newSelectedIndex].selectable { + break MovementSwitch + } + } + newSelectedIndex = selectedIndex + case treeDown: + for newSelectedIndex < len(t.nodes)-1 { + newSelectedIndex++ + if t.nodes[newSelectedIndex].selectable { + break MovementSwitch + } + } + newSelectedIndex = selectedIndex + case treeHome: + for newSelectedIndex = 0; newSelectedIndex < len(t.nodes); newSelectedIndex++ { + if t.nodes[newSelectedIndex].selectable { + break MovementSwitch + } + } + newSelectedIndex = selectedIndex + case treeEnd: + for newSelectedIndex = len(t.nodes) - 1; newSelectedIndex >= 0; newSelectedIndex-- { + if t.nodes[newSelectedIndex].selectable { + break MovementSwitch + } + } + newSelectedIndex = selectedIndex + case treePageDown: + if newSelectedIndex+height < len(t.nodes) { + newSelectedIndex += height + } else { + newSelectedIndex = len(t.nodes) - 1 + } + for ; newSelectedIndex < len(t.nodes); newSelectedIndex++ { + if t.nodes[newSelectedIndex].selectable { + break MovementSwitch + } + } + newSelectedIndex = selectedIndex + case treePageUp: + if newSelectedIndex >= height { + newSelectedIndex -= height + } else { + newSelectedIndex = 0 + } + for ; newSelectedIndex >= 0; newSelectedIndex-- { + if t.nodes[newSelectedIndex].selectable { + break MovementSwitch + } + } + newSelectedIndex = selectedIndex + case treeParent: + newSelectedIndex = parentSelectedIndex + case treeChild: + for newSelectedIndex < len(t.nodes)-1 { + newSelectedIndex++ + if t.nodes[newSelectedIndex].selectable && t.nodes[newSelectedIndex].parent == t.nodes[selectedIndex] { + break MovementSwitch + } + } + newSelectedIndex = selectedIndex + } + t.currentNode = t.nodes[newSelectedIndex] + if newSelectedIndex != selectedIndex { + t.movement = treeNone + if t.changed != nil { + t.changed(t.currentNode) + } + } + selectedIndex = newSelectedIndex + + // Move selection into viewport. + if t.movement != treeScrollDown && t.movement != treeScrollUp { + if selectedIndex-t.offsetY >= height { + t.offsetY = selectedIndex - height + 1 + } + if selectedIndex < t.offsetY { + t.offsetY = selectedIndex + } + } + } else { + // If selection is not visible or selectable, select the first candidate. + if t.currentNode != nil { + for index, node := range t.nodes { + if node.selectable { + selectedIndex = index + t.currentNode = node + break + } + } + } + if selectedIndex < 0 { + t.currentNode = nil + } + } +} + +// Draw draws this primitive onto the screen. +func (t *TreeView) Draw(screen tcell.Screen) { + t.Box.DrawForSubclass(screen, t) + if t.root == nil { + return + } + _, totalHeight := screen.Size() + + t.process() + + // Scroll the tree. + x, y, width, height := t.GetInnerRect() + switch t.movement { + case treeUp, treeScrollUp: + t.offsetY-- + case treeDown, treeScrollDown: + t.offsetY++ + case treeHome: + t.offsetY = 0 + case treeEnd: + t.offsetY = len(t.nodes) + case treePageUp: + t.offsetY -= height + case treePageDown: + t.offsetY += height + } + t.movement = treeNone + + // Fix invalid offsets. + if t.offsetY >= len(t.nodes)-height { + t.offsetY = len(t.nodes) - height + } + if t.offsetY < 0 { + t.offsetY = 0 + } + + // Draw the tree. + posY := y + lineStyle := tcell.StyleDefault.Background(t.backgroundColor).Foreground(t.graphicsColor) + for index, node := range t.nodes { + // Skip invisible parts. + if posY >= y+height+1 || posY >= totalHeight { + break + } + if index < t.offsetY { + continue + } + + // Draw the graphics. + if t.graphics { + // Draw ancestor branches. + ancestor := node.parent + for ancestor != nil && ancestor.parent != nil && ancestor.parent.level >= t.topLevel { + if ancestor.graphicsX >= width { + continue + } + + // Draw a branch if this ancestor is not a last child. + if ancestor.parent.children[len(ancestor.parent.children)-1] != ancestor { + if posY-1 >= y && ancestor.textX > ancestor.graphicsX { + PrintJoinedSemigraphics(screen, x+ancestor.graphicsX, posY-1, Borders.Vertical, lineStyle) + } + if posY < y+height { + screen.SetContent(x+ancestor.graphicsX, posY, Borders.Vertical, nil, lineStyle) + } + } + ancestor = ancestor.parent + } + + if node.textX > node.graphicsX && node.graphicsX < width { + // Connect to the node above. + if posY-1 >= y && t.nodes[index-1].graphicsX <= node.graphicsX && t.nodes[index-1].textX > node.graphicsX { + PrintJoinedSemigraphics(screen, x+node.graphicsX, posY-1, Borders.TopLeft, lineStyle) + } + + // Join this node. + if posY < y+height { + screen.SetContent(x+node.graphicsX, posY, Borders.BottomLeft, nil, lineStyle) + for pos := node.graphicsX + 1; pos < node.textX && pos < width; pos++ { + screen.SetContent(x+pos, posY, Borders.Horizontal, nil, lineStyle) + } + } + } + } + + // Draw the prefix and the text. + if node.textX < width && posY < y+height { + // Prefix. + var prefixWidth int + if len(t.prefixes) > 0 { + _, prefixWidth = Print(screen, t.prefixes[(node.level-t.topLevel)%len(t.prefixes)], x+node.textX, posY, width-node.textX, AlignLeft, node.color) + } + + // Text. + if node.textX+prefixWidth < width { + style := tcell.StyleDefault.Background(t.backgroundColor).Foreground(node.color) + if node == t.currentNode { + style = tcell.StyleDefault.Background(node.color).Foreground(t.backgroundColor) + } + printWithStyle(screen, node.text, x+node.textX+prefixWidth, posY, 0, width-node.textX-prefixWidth, AlignLeft, style, false) + } + } + + // Advance. + posY++ + } +} + +// InputHandler returns the handler for this primitive. +func (t *TreeView) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { + return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { + selectNode := func() { + node := t.currentNode + if node != nil { + if t.selected != nil { + t.selected(node) + } + if node.selected != nil { + node.selected() + } + } + } + + // Because the tree is flattened into a list only at drawing time, we also + // postpone the (selection) movement to drawing time. + switch key := event.Key(); key { + case tcell.KeyTab, tcell.KeyBacktab, tcell.KeyEscape: + if t.done != nil { + t.done(key) + } + case tcell.KeyDown, tcell.KeyRight: + t.movement = treeDown + case tcell.KeyUp, tcell.KeyLeft: + t.movement = treeUp + case tcell.KeyHome: + t.movement = treeHome + case tcell.KeyEnd: + t.movement = treeEnd + case tcell.KeyPgDn, tcell.KeyCtrlF: + t.movement = treePageDown + case tcell.KeyPgUp, tcell.KeyCtrlB: + t.movement = treePageUp + case tcell.KeyRune: + switch event.Rune() { + case 'g': + t.movement = treeHome + case 'G': + t.movement = treeEnd + case 'j': + t.movement = treeDown + case 'J': + t.movement = treeChild + case 'k': + t.movement = treeUp + case 'K': + t.movement = treeParent + case ' ': + selectNode() + } + case tcell.KeyEnter: + selectNode() + } + + t.process() + }) +} + +// MouseHandler returns the mouse handler for this primitive. +func (t *TreeView) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + return t.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { + x, y := event.Position() + if !t.InRect(x, y) { + return false, nil + } + + switch action { + case MouseLeftClick: + setFocus(t) + _, rectY, _, _ := t.GetInnerRect() + y += t.offsetY - rectY + if y >= 0 && y < len(t.nodes) { + node := t.nodes[y] + if node.selectable { + previousNode := t.currentNode + t.currentNode = node + if previousNode != node && t.changed != nil { + t.changed(node) + } + if t.selected != nil { + t.selected(node) + } + if node.selected != nil { + node.selected() + } + } + } + consumed = true + case MouseScrollUp: + t.movement = treeScrollUp + consumed = true + case MouseScrollDown: + t.movement = treeScrollDown + consumed = true + } + + return + }) +} diff --git a/vendor/github.com/rivo/tview/tview.gif b/vendor/github.com/rivo/tview/tview.gif new file mode 100644 index 0000000000000000000000000000000000000000..0583d7b95ca4da412e5d7491221366a1a3f7d64b GIT binary patch literal 2226085 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ĵxT}f<`tmCb#T5`LAX_DjhAko@3Z$&t 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

tAsEhi!lt_gag)FL@0a8Bj=rtq775 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≧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

V(ua#LDomiop1( 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�MKf`bq0E6~ZR9(wn>{>KXXSN8O~*k%dS&Bb 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

    |B~br0D=$sT~e^cWw?|EAKtf&5W;6= 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ɕ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 z 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%0D)nKxI<67@|(3soCNOo=E8 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%0D5fhmeFPMp6dXB4`zX8@&>)qMGR&ZhW~lXiw^i4 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

    $J7G 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

    _4g&@Vt1e8u9L5wxqz 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%0ASLJH*VUBRV_e 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 E0A$d1(EtDd literal 0 HcmV?d00001 diff --git a/vendor/github.com/rivo/tview/util.go b/vendor/github.com/rivo/tview/util.go new file mode 100644 index 0000000000..440c7bea56 --- /dev/null +++ b/vendor/github.com/rivo/tview/util.go @@ -0,0 +1,658 @@ +package tview + +import ( + "math" + "regexp" + "sort" + "strconv" + + "github.com/gdamore/tcell/v2" + runewidth "github.com/mattn/go-runewidth" + "github.com/rivo/uniseg" +) + +// Text alignment within a box. +const ( + AlignLeft = iota + AlignCenter + AlignRight +) + +// Common regular expressions. +var ( + colorPattern = regexp.MustCompile(`\[([a-zA-Z]+|#[0-9a-zA-Z]{6}|\-)?(:([a-zA-Z]+|#[0-9a-zA-Z]{6}|\-)?(:([lbdru]+|\-)?)?)?\]`) + regionPattern = regexp.MustCompile(`\["([a-zA-Z0-9_,;: \-\.]*)"\]`) + escapePattern = regexp.MustCompile(`\[([a-zA-Z0-9_,;: \-\."#]+)\[(\[*)\]`) + nonEscapePattern = regexp.MustCompile(`(\[[a-zA-Z0-9_,;: \-\."#]+\[*)\]`) + boundaryPattern = regexp.MustCompile(`(([,\.\-:;!\?&#+]|\n)[ \t\f\r]*|([ \t\f\r]+))`) + spacePattern = regexp.MustCompile(`\s+`) +) + +// Positions of substrings in regular expressions. +const ( + colorForegroundPos = 1 + colorBackgroundPos = 3 + colorFlagPos = 5 +) + +// Predefined InputField acceptance functions. +var ( + // InputFieldInteger accepts integers. + InputFieldInteger func(text string, ch rune) bool + + // InputFieldFloat accepts floating-point numbers. + InputFieldFloat func(text string, ch rune) bool + + // InputFieldMaxLength returns an input field accept handler which accepts + // input strings up to a given length. Use it like this: + // + // inputField.SetAcceptanceFunc(InputFieldMaxLength(10)) // Accept up to 10 characters. + InputFieldMaxLength func(maxLength int) func(text string, ch rune) bool +) + +// Package initialization. +func init() { + // Initialize the predefined input field handlers. + InputFieldInteger = func(text string, ch rune) bool { + if text == "-" { + return true + } + _, err := strconv.Atoi(text) + return err == nil + } + InputFieldFloat = func(text string, ch rune) bool { + if text == "-" || text == "." || text == "-." { + return true + } + _, err := strconv.ParseFloat(text, 64) + return err == nil + } + InputFieldMaxLength = func(maxLength int) func(text string, ch rune) bool { + return func(text string, ch rune) bool { + return len([]rune(text)) <= maxLength + } + } +} + +// styleFromTag takes the given style, defined by a foreground color (fgColor), +// a background color (bgColor), and style attributes, and modifies it based on +// the substrings (tagSubstrings) extracted by the regular expression for color +// tags. The new colors and attributes are returned where empty strings mean +// "don't modify" and a dash ("-") means "reset to default". +func styleFromTag(fgColor, bgColor, attributes string, tagSubstrings []string) (newFgColor, newBgColor, newAttributes string) { + if tagSubstrings[colorForegroundPos] != "" { + color := tagSubstrings[colorForegroundPos] + if color == "-" { + fgColor = "-" + } else if color != "" { + fgColor = color + } + } + + if tagSubstrings[colorBackgroundPos-1] != "" { + color := tagSubstrings[colorBackgroundPos] + if color == "-" { + bgColor = "-" + } else if color != "" { + bgColor = color + } + } + + if tagSubstrings[colorFlagPos-1] != "" { + flags := tagSubstrings[colorFlagPos] + if flags == "-" { + attributes = "-" + } else if flags != "" { + attributes = flags + } + } + + return fgColor, bgColor, attributes +} + +// overlayStyle calculates a new style based on "style" and applying tag-based +// colors/attributes to it (see also styleFromTag()). +func overlayStyle(style tcell.Style, fgColor, bgColor, attributes string) tcell.Style { + _, _, defAttr := style.Decompose() + + if fgColor != "" && fgColor != "-" { + style = style.Foreground(tcell.GetColor(fgColor)) + } + + if bgColor != "" && bgColor != "-" { + style = style.Background(tcell.GetColor(bgColor)) + } + + if attributes == "-" { + style = style.Bold(defAttr&tcell.AttrBold > 0) + style = style.Blink(defAttr&tcell.AttrBlink > 0) + style = style.Reverse(defAttr&tcell.AttrReverse > 0) + style = style.Underline(defAttr&tcell.AttrUnderline > 0) + style = style.Dim(defAttr&tcell.AttrDim > 0) + } else if attributes != "" { + style = style.Normal() + for _, flag := range attributes { + switch flag { + case 'l': + style = style.Blink(true) + case 'b': + style = style.Bold(true) + case 'd': + style = style.Dim(true) + case 'r': + style = style.Reverse(true) + case 'u': + style = style.Underline(true) + } + } + } + + return style +} + +// decomposeString returns information about a string which may contain color +// tags or region tags, depending on which ones are requested to be found. It +// returns the indices of the color tags (as returned by +// re.FindAllStringIndex()), the color tags themselves (as returned by +// re.FindAllStringSubmatch()), the indices of region tags and the region tags +// themselves, the indices of an escaped tags (only if at least color tags or +// region tags are requested), the string stripped by any tags and escaped, and +// the screen width of the stripped string. +func decomposeString(text string, findColors, findRegions bool) (colorIndices [][]int, colors [][]string, regionIndices [][]int, regions [][]string, escapeIndices [][]int, stripped string, width int) { + // Shortcut for the trivial case. + if !findColors && !findRegions { + return nil, nil, nil, nil, nil, text, stringWidth(text) + } + + // Get positions of any tags. + if findColors { + colorIndices = colorPattern.FindAllStringIndex(text, -1) + colors = colorPattern.FindAllStringSubmatch(text, -1) + } + if findRegions { + regionIndices = regionPattern.FindAllStringIndex(text, -1) + regions = regionPattern.FindAllStringSubmatch(text, -1) + } + escapeIndices = escapePattern.FindAllStringIndex(text, -1) + + // Because the color pattern detects empty tags, we need to filter them out. + for i := len(colorIndices) - 1; i >= 0; i-- { + if colorIndices[i][1]-colorIndices[i][0] == 2 { + colorIndices = append(colorIndices[:i], colorIndices[i+1:]...) + colors = append(colors[:i], colors[i+1:]...) + } + } + + // Make a (sorted) list of all tags. + allIndices := make([][3]int, 0, len(colorIndices)+len(regionIndices)+len(escapeIndices)) + for indexType, index := range [][][]int{colorIndices, regionIndices, escapeIndices} { + for _, tag := range index { + allIndices = append(allIndices, [3]int{tag[0], tag[1], indexType}) + } + } + sort.Slice(allIndices, func(i int, j int) bool { + return allIndices[i][0] < allIndices[j][0] + }) + + // Remove the tags from the original string. + var from int + buf := make([]byte, 0, len(text)) + for _, indices := range allIndices { + if indices[2] == 2 { // Escape sequences are not simply removed. + buf = append(buf, []byte(text[from:indices[1]-2])...) + buf = append(buf, ']') + from = indices[1] + } else { + buf = append(buf, []byte(text[from:indices[0]])...) + from = indices[1] + } + } + buf = append(buf, text[from:]...) + stripped = string(buf) + + // Get the width of the stripped string. + width = stringWidth(stripped) + + return +} + +// Print prints text onto the screen into the given box at (x,y,maxWidth,1), +// not exceeding that box. "align" is one of AlignLeft, AlignCenter, or +// AlignRight. The screen's background color will not be changed. +// +// You can change the colors and text styles mid-text by inserting a color tag. +// See the package description for details. +// +// Returns the number of actual bytes of the text printed (including color tags) +// and the actual width used for the printed runes. +func Print(screen tcell.Screen, text string, x, y, maxWidth, align int, color tcell.Color) (int, int) { + bytes, width, _, _ := printWithStyle(screen, text, x, y, 0, maxWidth, align, tcell.StyleDefault.Foreground(color), true) + return bytes, width +} + +// printWithStyle works like Print() but it takes a style instead of just a +// foreground color. The skipWidth parameter specifies the number of cells +// skipped at the beginning of the text. It also returns the start and end index +// (exclusively) of the text actually printed. If maintainBackground is "true", +// The existing screen background is not changed (i.e. the style's background +// color is ignored). +func printWithStyle(screen tcell.Screen, text string, x, y, skipWidth, maxWidth, align int, style tcell.Style, maintainBackground bool) (int, int, int, int) { + totalWidth, totalHeight := screen.Size() + if maxWidth <= 0 || len(text) == 0 || y < 0 || y >= totalHeight { + return 0, 0, 0, 0 + } + + // Decompose the text. + colorIndices, colors, _, _, escapeIndices, strippedText, strippedWidth := decomposeString(text, true, false) + + // We want to reduce all alignments to AlignLeft. + if align == AlignRight { + if strippedWidth-skipWidth <= maxWidth { + // There's enough space for the entire text. + return printWithStyle(screen, text, x+maxWidth-strippedWidth+skipWidth, y, skipWidth, maxWidth, AlignLeft, style, maintainBackground) + } + // Trim characters off the beginning. + var ( + bytes, width, colorPos, escapePos, tagOffset, from, to int + foregroundColor, backgroundColor, attributes string + ) + originalStyle := style + iterateString(strippedText, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { + // Update color/escape tag offset and style. + if colorPos < len(colorIndices) && textPos+tagOffset >= colorIndices[colorPos][0] && textPos+tagOffset < colorIndices[colorPos][1] { + foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colors[colorPos]) + style = overlayStyle(originalStyle, foregroundColor, backgroundColor, attributes) + tagOffset += colorIndices[colorPos][1] - colorIndices[colorPos][0] + colorPos++ + } + if escapePos < len(escapeIndices) && textPos+tagOffset >= escapeIndices[escapePos][0] && textPos+tagOffset < escapeIndices[escapePos][1] { + tagOffset++ + escapePos++ + } + if strippedWidth-screenPos <= maxWidth { + // We chopped off enough. + if escapePos > 0 && textPos+tagOffset-1 >= escapeIndices[escapePos-1][0] && textPos+tagOffset-1 < escapeIndices[escapePos-1][1] { + // Unescape open escape sequences. + escapeCharPos := escapeIndices[escapePos-1][1] - 2 + text = text[:escapeCharPos] + text[escapeCharPos+1:] + } + // Print and return. + bytes, width, from, to = printWithStyle(screen, text[textPos+tagOffset:], x, y, 0, maxWidth, AlignLeft, style, maintainBackground) + from += textPos + tagOffset + to += textPos + tagOffset + return true + } + return false + }) + return bytes, width, from, to + } else if align == AlignCenter { + if strippedWidth-skipWidth == maxWidth { + // Use the exact space. + return printWithStyle(screen, text, x, y, skipWidth, maxWidth, AlignLeft, style, maintainBackground) + } else if strippedWidth-skipWidth < maxWidth { + // We have more space than we need. + half := (maxWidth - strippedWidth + skipWidth) / 2 + return printWithStyle(screen, text, x+half, y, skipWidth, maxWidth-half, AlignLeft, style, maintainBackground) + } else { + // Chop off runes until we have a perfect fit. + var choppedLeft, choppedRight, leftIndex, rightIndex int + rightIndex = len(strippedText) + for rightIndex-1 > leftIndex && strippedWidth-skipWidth-choppedLeft-choppedRight > maxWidth { + if skipWidth > 0 || choppedLeft < choppedRight { + // Iterate on the left by one character. + iterateString(strippedText[leftIndex:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { + if skipWidth > 0 { + skipWidth -= screenWidth + strippedWidth -= screenWidth + } else { + choppedLeft += screenWidth + } + leftIndex += textWidth + return true + }) + } else { + // Iterate on the right by one character. + iterateStringReverse(strippedText[leftIndex:rightIndex], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { + choppedRight += screenWidth + rightIndex -= textWidth + return true + }) + } + } + + // Add tag offsets and determine start style. + var ( + colorPos, escapePos, tagOffset int + foregroundColor, backgroundColor, attributes string + ) + originalStyle := style + for index := range strippedText { + // We only need the offset of the left index. + if index > leftIndex { + // We're done. + if escapePos > 0 && leftIndex+tagOffset-1 >= escapeIndices[escapePos-1][0] && leftIndex+tagOffset-1 < escapeIndices[escapePos-1][1] { + // Unescape open escape sequences. + escapeCharPos := escapeIndices[escapePos-1][1] - 2 + text = text[:escapeCharPos] + text[escapeCharPos+1:] + } + break + } + + // Update color/escape tag offset. + if colorPos < len(colorIndices) && index+tagOffset >= colorIndices[colorPos][0] && index+tagOffset < colorIndices[colorPos][1] { + if index <= leftIndex { + foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colors[colorPos]) + style = overlayStyle(originalStyle, foregroundColor, backgroundColor, attributes) + } + tagOffset += colorIndices[colorPos][1] - colorIndices[colorPos][0] + colorPos++ + } + if escapePos < len(escapeIndices) && index+tagOffset >= escapeIndices[escapePos][0] && index+tagOffset < escapeIndices[escapePos][1] { + tagOffset++ + escapePos++ + } + } + bytes, width, from, to := printWithStyle(screen, text[leftIndex+tagOffset:], x, y, 0, maxWidth, AlignLeft, style, maintainBackground) + from += leftIndex + tagOffset + to += leftIndex + tagOffset + return bytes, width, from, to + } + } + + // Draw text. + var ( + drawn, drawnWidth, colorPos, escapePos, tagOffset, from, to int + foregroundColor, backgroundColor, attributes string + ) + iterateString(strippedText, func(main rune, comb []rune, textPos, length, screenPos, screenWidth int) bool { + // Skip character if necessary. + if skipWidth > 0 { + skipWidth -= screenWidth + from = textPos + length + to = from + return false + } + + // Only continue if there is still space. + if drawnWidth+screenWidth > maxWidth || x+drawnWidth >= totalWidth { + return true + } + + // Handle color tags. + for colorPos < len(colorIndices) && textPos+tagOffset >= colorIndices[colorPos][0] && textPos+tagOffset < colorIndices[colorPos][1] { + foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colors[colorPos]) + tagOffset += colorIndices[colorPos][1] - colorIndices[colorPos][0] + colorPos++ + } + + // Handle escape tags. + if escapePos < len(escapeIndices) && textPos+tagOffset >= escapeIndices[escapePos][0] && textPos+tagOffset < escapeIndices[escapePos][1] { + if textPos+tagOffset == escapeIndices[escapePos][1]-2 { + tagOffset++ + escapePos++ + } + } + + // Memorize positions. + to = textPos + length + + // Print the rune sequence. + finalX := x + drawnWidth + finalStyle := style + if maintainBackground { + _, _, existingStyle, _ := screen.GetContent(finalX, y) + _, background, _ := existingStyle.Decompose() + finalStyle = finalStyle.Background(background) + } + finalStyle = overlayStyle(finalStyle, foregroundColor, backgroundColor, attributes) + for offset := screenWidth - 1; offset >= 0; offset-- { + // To avoid undesired effects, we populate all cells. + if offset == 0 { + screen.SetContent(finalX+offset, y, main, comb, finalStyle) + } else { + screen.SetContent(finalX+offset, y, ' ', nil, finalStyle) + } + } + + // Advance. + drawn += length + drawnWidth += screenWidth + + return false + }) + + return drawn + tagOffset + len(escapeIndices), drawnWidth, from, to +} + +// PrintSimple prints white text to the screen at the given position. +func PrintSimple(screen tcell.Screen, text string, x, y int) { + Print(screen, text, x, y, math.MaxInt32, AlignLeft, Styles.PrimaryTextColor) +} + +// TaggedStringWidth returns the width of the given string needed to print it on +// screen. The text may contain color tags which are not counted. +func TaggedStringWidth(text string) int { + _, _, _, _, _, _, width := decomposeString(text, true, false) + return width +} + +// stringWidth returns the number of horizontal cells needed to print the given +// text. It splits the text into its grapheme clusters, calculates each +// cluster's width, and adds them up to a total. +func stringWidth(text string) (width int) { + g := uniseg.NewGraphemes(text) + for g.Next() { + var chWidth int + for _, r := range g.Runes() { + chWidth = runewidth.RuneWidth(r) + if chWidth > 0 { + break // Our best guess at this point is to use the width of the first non-zero-width rune. + } + } + width += chWidth + } + return +} + +// WordWrap splits a text such that each resulting line does not exceed the +// given screen width. Possible split points are after any punctuation or +// whitespace. Whitespace after split points will be dropped. +// +// This function considers color tags to have no width. +// +// Text is always split at newline characters ('\n'). +func WordWrap(text string, width int) (lines []string) { + colorTagIndices, _, _, _, escapeIndices, strippedText, _ := decomposeString(text, true, false) + + // Find candidate breakpoints. + breakpoints := boundaryPattern.FindAllStringSubmatchIndex(strippedText, -1) + // Results in one entry for each candidate. Each entry is an array a of + // indices into strippedText where a[6] < 0 for newline/punctuation matches + // and a[4] < 0 for whitespace matches. + + // Process stripped text one character at a time. + var ( + colorPos, escapePos, breakpointPos, tagOffset int + lastBreakpoint, lastContinuation, currentLineStart int + lineWidth, overflow int + forceBreak bool + ) + unescape := func(substr string, startIndex int) string { + // A helper function to unescape escaped tags. + for index := escapePos; index >= 0; index-- { + if index < len(escapeIndices) && startIndex > escapeIndices[index][0] && startIndex < escapeIndices[index][1]-1 { + pos := escapeIndices[index][1] - 2 - startIndex + return substr[:pos] + substr[pos+1:] + } + } + return substr + } + iterateString(strippedText, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { + // Handle tags. + for { + if colorPos < len(colorTagIndices) && textPos+tagOffset >= colorTagIndices[colorPos][0] && textPos+tagOffset < colorTagIndices[colorPos][1] { + // Colour tags. + tagOffset += colorTagIndices[colorPos][1] - colorTagIndices[colorPos][0] + colorPos++ + } else if escapePos < len(escapeIndices) && textPos+tagOffset == escapeIndices[escapePos][1]-2 { + // Escape tags. + tagOffset++ + escapePos++ + } else { + break + } + } + + // Is this a breakpoint? + if breakpointPos < len(breakpoints) && textPos+tagOffset == breakpoints[breakpointPos][0] { + // Yes, it is. Set up breakpoint infos depending on its type. + lastBreakpoint = breakpoints[breakpointPos][0] + tagOffset + lastContinuation = breakpoints[breakpointPos][1] + tagOffset + overflow = 0 + forceBreak = main == '\n' + if breakpoints[breakpointPos][6] < 0 && !forceBreak { + lastBreakpoint++ // Don't skip punctuation. + } + breakpointPos++ + } + + // Check if a break is warranted. + if forceBreak || lineWidth > 0 && lineWidth+screenWidth > width { + breakpoint := lastBreakpoint + continuation := lastContinuation + if forceBreak { + breakpoint = textPos + tagOffset + continuation = textPos + tagOffset + 1 + lastBreakpoint = 0 + overflow = 0 + } else if lastBreakpoint <= currentLineStart { + breakpoint = textPos + tagOffset + continuation = textPos + tagOffset + overflow = 0 + } + lines = append(lines, unescape(text[currentLineStart:breakpoint], currentLineStart)) + currentLineStart, lineWidth, forceBreak = continuation, overflow, false + } + + // Remember the characters since the last breakpoint. + if lastBreakpoint > 0 && lastContinuation <= textPos+tagOffset { + overflow += screenWidth + } + + // Advance. + lineWidth += screenWidth + + // But if we're still inside a breakpoint, skip next character (whitespace). + if textPos+tagOffset < currentLineStart { + lineWidth -= screenWidth + } + + return false + }) + + // Flush the rest. + if currentLineStart < len(text) { + lines = append(lines, unescape(text[currentLineStart:], currentLineStart)) + } + + return +} + +// Escape escapes the given text such that color and/or region tags are not +// recognized and substituted by the print functions of this package. For +// example, to include a tag-like string in a box title or in a TextView: +// +// box.SetTitle(tview.Escape("[squarebrackets]")) +// fmt.Fprint(textView, tview.Escape(`["quoted"]`)) +func Escape(text string) string { + return nonEscapePattern.ReplaceAllString(text, "$1[]") +} + +// iterateString iterates through the given string one printed character at a +// time. For each such character, the callback function is called with the +// Unicode code points of the character (the first rune and any combining runes +// which may be nil if there aren't any), the starting position (in bytes) +// within the original string, its length in bytes, the screen position of the +// character, and the screen width of it. The iteration stops if the callback +// returns true. This function returns true if the iteration was stopped before +// the last character. +func iterateString(text string, callback func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool) bool { + var screenPos int + + gr := uniseg.NewGraphemes(text) + for gr.Next() { + r := gr.Runes() + from, to := gr.Positions() + width := stringWidth(gr.Str()) + var comb []rune + if len(r) > 1 { + comb = r[1:] + } + + if callback(r[0], comb, from, to-from, screenPos, width) { + return true + } + + screenPos += width + } + + return false +} + +// iterateStringReverse iterates through the given string in reverse, starting +// from the end of the string, one printed character at a time. For each such +// character, the callback function is called with the Unicode code points of +// the character (the first rune and any combining runes which may be nil if +// there aren't any), the starting position (in bytes) within the original +// string, its length in bytes, the screen position of the character, and the +// screen width of it. The iteration stops if the callback returns true. This +// function returns true if the iteration was stopped before the last character. +func iterateStringReverse(text string, callback func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool) bool { + type cluster struct { + main rune + comb []rune + textPos, textWidth, screenPos, screenWidth int + } + + // Create the grapheme clusters. + var clusters []cluster + iterateString(text, func(main rune, comb []rune, textPos int, textWidth int, screenPos int, screenWidth int) bool { + clusters = append(clusters, cluster{ + main: main, + comb: comb, + textPos: textPos, + textWidth: textWidth, + screenPos: screenPos, + screenWidth: screenWidth, + }) + return false + }) + + // Iterate in reverse. + for index := len(clusters) - 1; index >= 0; index-- { + if callback( + clusters[index].main, + clusters[index].comb, + clusters[index].textPos, + clusters[index].textWidth, + clusters[index].screenPos, + clusters[index].screenWidth, + ) { + return true + } + } + + return false +} + +// stripTags strips colour tags from the given string. (Region tags are not +// stripped.) +func stripTags(text string) string { + stripped := colorPattern.ReplaceAllStringFunc(text, func(match string) string { + if len(match) > 2 { + return "" + } + return match + }) + return escapePattern.ReplaceAllString(stripped, `[$1$2]`) +} diff --git a/vendor/github.com/rivo/uniseg/LICENSE.txt b/vendor/github.com/rivo/uniseg/LICENSE.txt new file mode 100644 index 0000000000..5040f1ef80 --- /dev/null +++ b/vendor/github.com/rivo/uniseg/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Oliver Kuederle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/rivo/uniseg/README.md b/vendor/github.com/rivo/uniseg/README.md new file mode 100644 index 0000000000..f8da293e15 --- /dev/null +++ b/vendor/github.com/rivo/uniseg/README.md @@ -0,0 +1,62 @@ +# Unicode Text Segmentation for Go + +[![Godoc Reference](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/rivo/uniseg) +[![Go Report](https://img.shields.io/badge/go%20report-A%2B-brightgreen.svg)](https://goreportcard.com/report/github.com/rivo/uniseg) + +This Go package implements Unicode Text Segmentation according to [Unicode Standard Annex #29](http://unicode.org/reports/tr29/) (Unicode version 12.0.0). + +At this point, only the determination of grapheme cluster boundaries is implemented. + +## Background + +In Go, [strings are read-only slices of bytes](https://blog.golang.org/strings). They can be turned into Unicode code points using the `for` loop or by casting: `[]rune(str)`. However, multiple code points may be combined into one user-perceived character or what the Unicode specification calls "grapheme cluster". Here are some examples: + +|String|Bytes (UTF-8)|Code points (runes)|Grapheme clusters| +|-|-|-|-| +|Käse|6 bytes: `4b 61 cc 88 73 65`|5 code points: `4b 61 308 73 65`|4 clusters: `[4b],[61 308],[73],[65]`| +|🏳️‍🌈|14 bytes: `f0 9f 8f b3 ef b8 8f e2 80 8d f0 9f 8c 88`|4 code points: `1f3f3 fe0f 200d 1f308`|1 cluster: `[1f3f3 fe0f 200d 1f308]`| +|🇩🇪|8 bytes: `f0 9f 87 a9 f0 9f 87 aa`|2 code points: `1f1e9 1f1ea`|1 cluster: `[1f1e9 1f1ea]`| + +This package provides a tool to iterate over these grapheme clusters. This may be used to determine the number of user-perceived characters, to split strings in their intended places, or to extract individual characters which form a unit. + +## Installation + +```bash +go get github.com/rivo/uniseg +``` + +## Basic Example + +```go +package uniseg + +import ( + "fmt" + + "github.com/rivo/uniseg" +) + +func main() { + gr := uniseg.NewGraphemes("👍🏼!") + for gr.Next() { + fmt.Printf("%x ", gr.Runes()) + } + // Output: [1f44d 1f3fc] [21] +} +``` + +## Documentation + +Refer to https://godoc.org/github.com/rivo/uniseg for the package's documentation. + +## Dependencies + +This package does not depend on any packages outside the standard library. + +## Your Feedback + +Add your issue here on GitHub. Feel free to get in touch if you have any questions. + +## Version + +Version tags will be introduced once Golang modules are official. Consider this version 0.1. diff --git a/vendor/github.com/rivo/uniseg/doc.go b/vendor/github.com/rivo/uniseg/doc.go new file mode 100644 index 0000000000..60c737d7b3 --- /dev/null +++ b/vendor/github.com/rivo/uniseg/doc.go @@ -0,0 +1,8 @@ +/* +Package uniseg implements Unicode Text Segmentation according to Unicode +Standard Annex #29 (http://unicode.org/reports/tr29/). + +At this point, only the determination of grapheme cluster boundaries is +implemented. +*/ +package uniseg diff --git a/vendor/github.com/rivo/uniseg/go.mod b/vendor/github.com/rivo/uniseg/go.mod new file mode 100644 index 0000000000..a54280b2de --- /dev/null +++ b/vendor/github.com/rivo/uniseg/go.mod @@ -0,0 +1,3 @@ +module github.com/rivo/uniseg + +go 1.12 diff --git a/vendor/github.com/rivo/uniseg/grapheme.go b/vendor/github.com/rivo/uniseg/grapheme.go new file mode 100644 index 0000000000..207157f5e4 --- /dev/null +++ b/vendor/github.com/rivo/uniseg/grapheme.go @@ -0,0 +1,268 @@ +package uniseg + +import "unicode/utf8" + +// The states of the grapheme cluster parser. +const ( + grAny = iota + grCR + grControlLF + grL + grLVV + grLVTT + grPrepend + grExtendedPictographic + grExtendedPictographicZWJ + grRIOdd + grRIEven +) + +// The grapheme cluster parser's breaking instructions. +const ( + grNoBoundary = iota + grBoundary +) + +// The grapheme cluster parser's state transitions. Maps (state, property) to +// (new state, breaking instruction, rule number). The breaking instruction +// always refers to the boundary between the last and next code point. +// +// This map is queried as follows: +// +// 1. Find specific state + specific property. Stop if found. +// 2. Find specific state + any property. +// 3. Find any state + specific property. +// 4. If only (2) or (3) (but not both) was found, stop. +// 5. If both (2) and (3) were found, use state and breaking instruction from +// the transition with the lower rule number, prefer (3) if rule numbers +// are equal. Stop. +// 6. Assume grAny and grBoundary. +var grTransitions = map[[2]int][3]int{ + // GB5 + {grAny, prCR}: {grCR, grBoundary, 50}, + {grAny, prLF}: {grControlLF, grBoundary, 50}, + {grAny, prControl}: {grControlLF, grBoundary, 50}, + + // GB4 + {grCR, prAny}: {grAny, grBoundary, 40}, + {grControlLF, prAny}: {grAny, grBoundary, 40}, + + // GB3. + {grCR, prLF}: {grAny, grNoBoundary, 30}, + + // GB6. + {grAny, prL}: {grL, grBoundary, 9990}, + {grL, prL}: {grL, grNoBoundary, 60}, + {grL, prV}: {grLVV, grNoBoundary, 60}, + {grL, prLV}: {grLVV, grNoBoundary, 60}, + {grL, prLVT}: {grLVTT, grNoBoundary, 60}, + + // GB7. + {grAny, prLV}: {grLVV, grBoundary, 9990}, + {grAny, prV}: {grLVV, grBoundary, 9990}, + {grLVV, prV}: {grLVV, grNoBoundary, 70}, + {grLVV, prT}: {grLVTT, grNoBoundary, 70}, + + // GB8. + {grAny, prLVT}: {grLVTT, grBoundary, 9990}, + {grAny, prT}: {grLVTT, grBoundary, 9990}, + {grLVTT, prT}: {grLVTT, grNoBoundary, 80}, + + // GB9. + {grAny, prExtend}: {grAny, grNoBoundary, 90}, + {grAny, prZWJ}: {grAny, grNoBoundary, 90}, + + // GB9a. + {grAny, prSpacingMark}: {grAny, grNoBoundary, 91}, + + // GB9b. + {grAny, prPreprend}: {grPrepend, grBoundary, 9990}, + {grPrepend, prAny}: {grAny, grNoBoundary, 92}, + + // GB11. + {grAny, prExtendedPictographic}: {grExtendedPictographic, grBoundary, 9990}, + {grExtendedPictographic, prExtend}: {grExtendedPictographic, grNoBoundary, 110}, + {grExtendedPictographic, prZWJ}: {grExtendedPictographicZWJ, grNoBoundary, 110}, + {grExtendedPictographicZWJ, prExtendedPictographic}: {grExtendedPictographic, grNoBoundary, 110}, + + // GB12 / GB13. + {grAny, prRegionalIndicator}: {grRIOdd, grBoundary, 9990}, + {grRIOdd, prRegionalIndicator}: {grRIEven, grNoBoundary, 120}, + {grRIEven, prRegionalIndicator}: {grRIOdd, grBoundary, 120}, +} + +// Graphemes implements an iterator over Unicode extended grapheme clusters, +// specified in the Unicode Standard Annex #29. Grapheme clusters correspond to +// "user-perceived characters". These characters often consist of multiple +// code points (e.g. the "woman kissing woman" emoji consists of 8 code points: +// woman + ZWJ + heavy black heart (2 code points) + ZWJ + kiss mark + ZWJ + +// woman) and the rules described in Annex #29 must be applied to group those +// code points into clusters perceived by the user as one character. +type Graphemes struct { + // The code points over which this class iterates. + codePoints []rune + + // The (byte-based) indices of the code points into the original string plus + // len(original string). Thus, len(indices) = len(codePoints) + 1. + indices []int + + // The current grapheme cluster to be returned. These are indices into + // codePoints/indices. If start == end, we either haven't started iterating + // yet (0) or the iteration has already completed (1). + start, end int + + // The index of the next code point to be parsed. + pos int + + // The current state of the code point parser. + state int +} + +// NewGraphemes returns a new grapheme cluster iterator. +func NewGraphemes(s string) *Graphemes { + l := utf8.RuneCountInString(s) + codePoints := make([]rune, l) + indices := make([]int, l+1) + i := 0 + for pos, r := range s { + codePoints[i] = r + indices[i] = pos + i++ + } + indices[l] = len(s) + g := &Graphemes{ + codePoints: codePoints, + indices: indices, + } + g.Next() // Parse ahead. + return g +} + +// Next advances the iterator by one grapheme cluster and returns false if no +// clusters are left. This function must be called before the first cluster is +// accessed. +func (g *Graphemes) Next() bool { + g.start = g.end + + // The state transition gives us a boundary instruction BEFORE the next code + // point so we always need to stay ahead by one code point. + + // Parse the next code point. + for g.pos <= len(g.codePoints) { + // GB2. + if g.pos == len(g.codePoints) { + g.end = g.pos + g.pos++ + break + } + + // Determine the property of the next character. + nextProperty := property(g.codePoints[g.pos]) + g.pos++ + + // Find the applicable transition. + var boundary bool + transition, ok := grTransitions[[2]int{g.state, nextProperty}] + if ok { + // We have a specific transition. We'll use it. + g.state = transition[0] + boundary = transition[1] == grBoundary + } else { + // No specific transition found. Try the less specific ones. + transAnyProp, okAnyProp := grTransitions[[2]int{g.state, prAny}] + transAnyState, okAnyState := grTransitions[[2]int{grAny, nextProperty}] + if okAnyProp && okAnyState { + // Both apply. We'll use a mix (see comments for grTransitions). + g.state = transAnyState[0] + boundary = transAnyState[1] == grBoundary + if transAnyProp[2] < transAnyState[2] { + g.state = transAnyProp[0] + boundary = transAnyProp[1] == grBoundary + } + } else if okAnyProp { + // We only have a specific state. + g.state = transAnyProp[0] + boundary = transAnyProp[1] == grBoundary + // This branch will probably never be reached because okAnyState will + // always be true given the current transition map. But we keep it here + // for future modifications to the transition map where this may not be + // true anymore. + } else if okAnyState { + // We only have a specific property. + g.state = transAnyState[0] + boundary = transAnyState[1] == grBoundary + } else { + // No known transition. GB999: Any x Any. + g.state = grAny + boundary = true + } + } + + // If we found a cluster boundary, let's stop here. The current cluster will + // be the one that just ended. + if g.pos-1 == 0 /* GB1 */ || boundary { + g.end = g.pos - 1 + break + } + } + + return g.start != g.end +} + +// Runes returns a slice of runes (code points) which corresponds to the current +// grapheme cluster. If the iterator is already past the end or Next() has not +// yet been called, nil is returned. +func (g *Graphemes) Runes() []rune { + if g.start == g.end { + return nil + } + return g.codePoints[g.start:g.end] +} + +// Str returns a substring of the original string which corresponds to the +// current grapheme cluster. If the iterator is already past the end or Next() +// has not yet been called, an empty string is returned. +func (g *Graphemes) Str() string { + if g.start == g.end { + return "" + } + return string(g.codePoints[g.start:g.end]) +} + +// Bytes returns a byte slice which corresponds to the current grapheme cluster. +// If the iterator is already past the end or Next() has not yet been called, +// nil is returned. +func (g *Graphemes) Bytes() []byte { + if g.start == g.end { + return nil + } + return []byte(string(g.codePoints[g.start:g.end])) +} + +// Positions returns the interval of the current grapheme cluster as byte +// positions into the original string. The first returned value "from" indexes +// the first byte and the second returned value "to" indexes the first byte that +// is not included anymore, i.e. str[from:to] is the current grapheme cluster of +// the original string "str". If Next() has not yet been called, both values are +// 0. If the iterator is already past the end, both values are 1. +func (g *Graphemes) Positions() (int, int) { + return g.indices[g.start], g.indices[g.end] +} + +// Reset puts the iterator into its initial state such that the next call to +// Next() sets it to the first grapheme cluster again. +func (g *Graphemes) Reset() { + g.start, g.end, g.pos, g.state = 0, 0, 0, grAny + g.Next() // Parse ahead again. +} + +// GraphemeClusterCount returns the number of user-perceived characters +// (grapheme clusters) for the given string. To calculate this number, it +// iterates through the string using the Graphemes iterator. +func GraphemeClusterCount(s string) (n int) { + g := NewGraphemes(s) + for g.Next() { + n++ + } + return +} diff --git a/vendor/github.com/rivo/uniseg/properties.go b/vendor/github.com/rivo/uniseg/properties.go new file mode 100644 index 0000000000..a75ab58839 --- /dev/null +++ b/vendor/github.com/rivo/uniseg/properties.go @@ -0,0 +1,1658 @@ +package uniseg + +// The unicode properties. Only the ones needed in the context of this package +// are included. +const ( + prAny = iota + prPreprend + prCR + prLF + prControl + prExtend + prRegionalIndicator + prSpacingMark + prL + prV + prT + prLV + prLVT + prZWJ + prExtendedPictographic +) + +// Maps code point ranges to their properties. In the context of this package, +// any code point that is not contained may map to "prAny". The code point +// ranges in this slice are numerically sorted. +// +// These ranges were taken from +// http://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakProperty.txt +// as well as +// https://unicode.org/Public/emoji/latest/emoji-data.txt +// ("Extended_Pictographic" only) on March 11, 2019. See +// https://www.unicode.org/license.html for the Unicode license agreement. +var codePoints = [][3]int{ + {0x0000, 0x0009, prControl}, // Cc [10] .. + {0x000A, 0x000A, prLF}, // Cc + {0x000B, 0x000C, prControl}, // Cc [2] .. + {0x000D, 0x000D, prCR}, // Cc + {0x000E, 0x001F, prControl}, // Cc [18] .. + {0x007F, 0x009F, prControl}, // Cc [33] .. + {0x00A9, 0x00A9, prExtendedPictographic}, // 1.1 [1] (©️) copyright + {0x00AD, 0x00AD, prControl}, // Cf SOFT HYPHEN + {0x00AE, 0x00AE, prExtendedPictographic}, // 1.1 [1] (®️) registered + {0x0300, 0x036F, prExtend}, // Mn [112] COMBINING GRAVE ACCENT..COMBINING LATIN SMALL LETTER X + {0x0483, 0x0487, prExtend}, // Mn [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE + {0x0488, 0x0489, prExtend}, // Me [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN + {0x0591, 0x05BD, prExtend}, // Mn [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG + {0x05BF, 0x05BF, prExtend}, // Mn HEBREW POINT RAFE + {0x05C1, 0x05C2, prExtend}, // Mn [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT + {0x05C4, 0x05C5, prExtend}, // Mn [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT + {0x05C7, 0x05C7, prExtend}, // Mn HEBREW POINT QAMATS QATAN + {0x0600, 0x0605, prPreprend}, // Cf [6] ARABIC NUMBER SIGN..ARABIC NUMBER MARK ABOVE + {0x0610, 0x061A, prExtend}, // Mn [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA + {0x061C, 0x061C, prControl}, // Cf ARABIC LETTER MARK + {0x064B, 0x065F, prExtend}, // Mn [21] ARABIC FATHATAN..ARABIC WAVY HAMZA BELOW + {0x0670, 0x0670, prExtend}, // Mn ARABIC LETTER SUPERSCRIPT ALEF + {0x06D6, 0x06DC, prExtend}, // Mn [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN + {0x06DD, 0x06DD, prPreprend}, // Cf ARABIC END OF AYAH + {0x06DF, 0x06E4, prExtend}, // Mn [6] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MADDA + {0x06E7, 0x06E8, prExtend}, // Mn [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON + {0x06EA, 0x06ED, prExtend}, // Mn [4] ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM + {0x070F, 0x070F, prPreprend}, // Cf SYRIAC ABBREVIATION MARK + {0x0711, 0x0711, prExtend}, // Mn SYRIAC LETTER SUPERSCRIPT ALAPH + {0x0730, 0x074A, prExtend}, // Mn [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH + {0x07A6, 0x07B0, prExtend}, // Mn [11] THAANA ABAFILI..THAANA SUKUN + {0x07EB, 0x07F3, prExtend}, // Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE + {0x07FD, 0x07FD, prExtend}, // Mn NKO DANTAYALAN + {0x0816, 0x0819, prExtend}, // Mn [4] SAMARITAN MARK IN..SAMARITAN MARK DAGESH + {0x081B, 0x0823, prExtend}, // Mn [9] SAMARITAN MARK EPENTHETIC YUT..SAMARITAN VOWEL SIGN A + {0x0825, 0x0827, prExtend}, // Mn [3] SAMARITAN VOWEL SIGN SHORT A..SAMARITAN VOWEL SIGN U + {0x0829, 0x082D, prExtend}, // Mn [5] SAMARITAN VOWEL SIGN LONG I..SAMARITAN MARK NEQUDAA + {0x0859, 0x085B, prExtend}, // Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK + {0x08D3, 0x08E1, prExtend}, // Mn [15] ARABIC SMALL LOW WAW..ARABIC SMALL HIGH SIGN SAFHA + {0x08E2, 0x08E2, prPreprend}, // Cf ARABIC DISPUTED END OF AYAH + {0x08E3, 0x0902, prExtend}, // Mn [32] ARABIC TURNED DAMMA BELOW..DEVANAGARI SIGN ANUSVARA + {0x0903, 0x0903, prSpacingMark}, // Mc DEVANAGARI SIGN VISARGA + {0x093A, 0x093A, prExtend}, // Mn DEVANAGARI VOWEL SIGN OE + {0x093B, 0x093B, prSpacingMark}, // Mc DEVANAGARI VOWEL SIGN OOE + {0x093C, 0x093C, prExtend}, // Mn DEVANAGARI SIGN NUKTA + {0x093E, 0x0940, prSpacingMark}, // Mc [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II + {0x0941, 0x0948, prExtend}, // Mn [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI + {0x0949, 0x094C, prSpacingMark}, // Mc [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU + {0x094D, 0x094D, prExtend}, // Mn DEVANAGARI SIGN VIRAMA + {0x094E, 0x094F, prSpacingMark}, // Mc [2] DEVANAGARI VOWEL SIGN PRISHTHAMATRA E..DEVANAGARI VOWEL SIGN AW + {0x0951, 0x0957, prExtend}, // Mn [7] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI VOWEL SIGN UUE + {0x0962, 0x0963, prExtend}, // Mn [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL + {0x0981, 0x0981, prExtend}, // Mn BENGALI SIGN CANDRABINDU + {0x0982, 0x0983, prSpacingMark}, // Mc [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA + {0x09BC, 0x09BC, prExtend}, // Mn BENGALI SIGN NUKTA + {0x09BE, 0x09BE, prExtend}, // Mc BENGALI VOWEL SIGN AA + {0x09BF, 0x09C0, prSpacingMark}, // Mc [2] BENGALI VOWEL SIGN I..BENGALI VOWEL SIGN II + {0x09C1, 0x09C4, prExtend}, // Mn [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR + {0x09C7, 0x09C8, prSpacingMark}, // Mc [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI + {0x09CB, 0x09CC, prSpacingMark}, // Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU + {0x09CD, 0x09CD, prExtend}, // Mn BENGALI SIGN VIRAMA + {0x09D7, 0x09D7, prExtend}, // Mc BENGALI AU LENGTH MARK + {0x09E2, 0x09E3, prExtend}, // Mn [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL + {0x09FE, 0x09FE, prExtend}, // Mn BENGALI SANDHI MARK + {0x0A01, 0x0A02, prExtend}, // Mn [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI + {0x0A03, 0x0A03, prSpacingMark}, // Mc GURMUKHI SIGN VISARGA + {0x0A3C, 0x0A3C, prExtend}, // Mn GURMUKHI SIGN NUKTA + {0x0A3E, 0x0A40, prSpacingMark}, // Mc [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II + {0x0A41, 0x0A42, prExtend}, // Mn [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU + {0x0A47, 0x0A48, prExtend}, // Mn [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI + {0x0A4B, 0x0A4D, prExtend}, // Mn [3] GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA + {0x0A51, 0x0A51, prExtend}, // Mn GURMUKHI SIGN UDAAT + {0x0A70, 0x0A71, prExtend}, // Mn [2] GURMUKHI TIPPI..GURMUKHI ADDAK + {0x0A75, 0x0A75, prExtend}, // Mn GURMUKHI SIGN YAKASH + {0x0A81, 0x0A82, prExtend}, // Mn [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA + {0x0A83, 0x0A83, prSpacingMark}, // Mc GUJARATI SIGN VISARGA + {0x0ABC, 0x0ABC, prExtend}, // Mn GUJARATI SIGN NUKTA + {0x0ABE, 0x0AC0, prSpacingMark}, // Mc [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II + {0x0AC1, 0x0AC5, prExtend}, // Mn [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E + {0x0AC7, 0x0AC8, prExtend}, // Mn [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI + {0x0AC9, 0x0AC9, prSpacingMark}, // Mc GUJARATI VOWEL SIGN CANDRA O + {0x0ACB, 0x0ACC, prSpacingMark}, // Mc [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU + {0x0ACD, 0x0ACD, prExtend}, // Mn GUJARATI SIGN VIRAMA + {0x0AE2, 0x0AE3, prExtend}, // Mn [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL + {0x0AFA, 0x0AFF, prExtend}, // Mn [6] GUJARATI SIGN SUKUN..GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE + {0x0B01, 0x0B01, prExtend}, // Mn ORIYA SIGN CANDRABINDU + {0x0B02, 0x0B03, prSpacingMark}, // Mc [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA + {0x0B3C, 0x0B3C, prExtend}, // Mn ORIYA SIGN NUKTA + {0x0B3E, 0x0B3E, prExtend}, // Mc ORIYA VOWEL SIGN AA + {0x0B3F, 0x0B3F, prExtend}, // Mn ORIYA VOWEL SIGN I + {0x0B40, 0x0B40, prSpacingMark}, // Mc ORIYA VOWEL SIGN II + {0x0B41, 0x0B44, prExtend}, // Mn [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR + {0x0B47, 0x0B48, prSpacingMark}, // Mc [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI + {0x0B4B, 0x0B4C, prSpacingMark}, // Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU + {0x0B4D, 0x0B4D, prExtend}, // Mn ORIYA SIGN VIRAMA + {0x0B56, 0x0B56, prExtend}, // Mn ORIYA AI LENGTH MARK + {0x0B57, 0x0B57, prExtend}, // Mc ORIYA AU LENGTH MARK + {0x0B62, 0x0B63, prExtend}, // Mn [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL + {0x0B82, 0x0B82, prExtend}, // Mn TAMIL SIGN ANUSVARA + {0x0BBE, 0x0BBE, prExtend}, // Mc TAMIL VOWEL SIGN AA + {0x0BBF, 0x0BBF, prSpacingMark}, // Mc TAMIL VOWEL SIGN I + {0x0BC0, 0x0BC0, prExtend}, // Mn TAMIL VOWEL SIGN II + {0x0BC1, 0x0BC2, prSpacingMark}, // Mc [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU + {0x0BC6, 0x0BC8, prSpacingMark}, // Mc [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI + {0x0BCA, 0x0BCC, prSpacingMark}, // Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU + {0x0BCD, 0x0BCD, prExtend}, // Mn TAMIL SIGN VIRAMA + {0x0BD7, 0x0BD7, prExtend}, // Mc TAMIL AU LENGTH MARK + {0x0C00, 0x0C00, prExtend}, // Mn TELUGU SIGN COMBINING CANDRABINDU ABOVE + {0x0C01, 0x0C03, prSpacingMark}, // Mc [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA + {0x0C04, 0x0C04, prExtend}, // Mn TELUGU SIGN COMBINING ANUSVARA ABOVE + {0x0C3E, 0x0C40, prExtend}, // Mn [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II + {0x0C41, 0x0C44, prSpacingMark}, // Mc [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR + {0x0C46, 0x0C48, prExtend}, // Mn [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI + {0x0C4A, 0x0C4D, prExtend}, // Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA + {0x0C55, 0x0C56, prExtend}, // Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK + {0x0C62, 0x0C63, prExtend}, // Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL + {0x0C81, 0x0C81, prExtend}, // Mn KANNADA SIGN CANDRABINDU + {0x0C82, 0x0C83, prSpacingMark}, // Mc [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA + {0x0CBC, 0x0CBC, prExtend}, // Mn KANNADA SIGN NUKTA + {0x0CBE, 0x0CBE, prSpacingMark}, // Mc KANNADA VOWEL SIGN AA + {0x0CBF, 0x0CBF, prExtend}, // Mn KANNADA VOWEL SIGN I + {0x0CC0, 0x0CC1, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN U + {0x0CC2, 0x0CC2, prExtend}, // Mc KANNADA VOWEL SIGN UU + {0x0CC3, 0x0CC4, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN VOCALIC R..KANNADA VOWEL SIGN VOCALIC RR + {0x0CC6, 0x0CC6, prExtend}, // Mn KANNADA VOWEL SIGN E + {0x0CC7, 0x0CC8, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI + {0x0CCA, 0x0CCB, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO + {0x0CCC, 0x0CCD, prExtend}, // Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA + {0x0CD5, 0x0CD6, prExtend}, // Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK + {0x0CE2, 0x0CE3, prExtend}, // Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL + {0x0D00, 0x0D01, prExtend}, // Mn [2] MALAYALAM SIGN COMBINING ANUSVARA ABOVE..MALAYALAM SIGN CANDRABINDU + {0x0D02, 0x0D03, prSpacingMark}, // Mc [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA + {0x0D3B, 0x0D3C, prExtend}, // Mn [2] MALAYALAM SIGN VERTICAL BAR VIRAMA..MALAYALAM SIGN CIRCULAR VIRAMA + {0x0D3E, 0x0D3E, prExtend}, // Mc MALAYALAM VOWEL SIGN AA + {0x0D3F, 0x0D40, prSpacingMark}, // Mc [2] MALAYALAM VOWEL SIGN I..MALAYALAM VOWEL SIGN II + {0x0D41, 0x0D44, prExtend}, // Mn [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR + {0x0D46, 0x0D48, prSpacingMark}, // Mc [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI + {0x0D4A, 0x0D4C, prSpacingMark}, // Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU + {0x0D4D, 0x0D4D, prExtend}, // Mn MALAYALAM SIGN VIRAMA + {0x0D4E, 0x0D4E, prPreprend}, // Lo MALAYALAM LETTER DOT REPH + {0x0D57, 0x0D57, prExtend}, // Mc MALAYALAM AU LENGTH MARK + {0x0D62, 0x0D63, prExtend}, // Mn [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL + {0x0D82, 0x0D83, prSpacingMark}, // Mc [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA + {0x0DCA, 0x0DCA, prExtend}, // Mn SINHALA SIGN AL-LAKUNA + {0x0DCF, 0x0DCF, prExtend}, // Mc SINHALA VOWEL SIGN AELA-PILLA + {0x0DD0, 0x0DD1, prSpacingMark}, // Mc [2] SINHALA VOWEL SIGN KETTI AEDA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA + {0x0DD2, 0x0DD4, prExtend}, // Mn [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA + {0x0DD6, 0x0DD6, prExtend}, // Mn SINHALA VOWEL SIGN DIGA PAA-PILLA + {0x0DD8, 0x0DDE, prSpacingMark}, // Mc [7] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA + {0x0DDF, 0x0DDF, prExtend}, // Mc SINHALA VOWEL SIGN GAYANUKITTA + {0x0DF2, 0x0DF3, prSpacingMark}, // Mc [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA + {0x0E31, 0x0E31, prExtend}, // Mn THAI CHARACTER MAI HAN-AKAT + {0x0E33, 0x0E33, prSpacingMark}, // Lo THAI CHARACTER SARA AM + {0x0E34, 0x0E3A, prExtend}, // Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU + {0x0E47, 0x0E4E, prExtend}, // Mn [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN + {0x0EB1, 0x0EB1, prExtend}, // Mn LAO VOWEL SIGN MAI KAN + {0x0EB3, 0x0EB3, prSpacingMark}, // Lo LAO VOWEL SIGN AM + {0x0EB4, 0x0EBC, prExtend}, // Mn [9] LAO VOWEL SIGN I..LAO SEMIVOWEL SIGN LO + {0x0EC8, 0x0ECD, prExtend}, // Mn [6] LAO TONE MAI EK..LAO NIGGAHITA + {0x0F18, 0x0F19, prExtend}, // Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS + {0x0F35, 0x0F35, prExtend}, // Mn TIBETAN MARK NGAS BZUNG NYI ZLA + {0x0F37, 0x0F37, prExtend}, // Mn TIBETAN MARK NGAS BZUNG SGOR RTAGS + {0x0F39, 0x0F39, prExtend}, // Mn TIBETAN MARK TSA -PHRU + {0x0F3E, 0x0F3F, prSpacingMark}, // Mc [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES + {0x0F71, 0x0F7E, prExtend}, // Mn [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO + {0x0F7F, 0x0F7F, prSpacingMark}, // Mc TIBETAN SIGN RNAM BCAD + {0x0F80, 0x0F84, prExtend}, // Mn [5] TIBETAN VOWEL SIGN REVERSED I..TIBETAN MARK HALANTA + {0x0F86, 0x0F87, prExtend}, // Mn [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS + {0x0F8D, 0x0F97, prExtend}, // Mn [11] TIBETAN SUBJOINED SIGN LCE TSA CAN..TIBETAN SUBJOINED LETTER JA + {0x0F99, 0x0FBC, prExtend}, // Mn [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA + {0x0FC6, 0x0FC6, prExtend}, // Mn TIBETAN SYMBOL PADMA GDAN + {0x102D, 0x1030, prExtend}, // Mn [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU + {0x1031, 0x1031, prSpacingMark}, // Mc MYANMAR VOWEL SIGN E + {0x1032, 0x1037, prExtend}, // Mn [6] MYANMAR VOWEL SIGN AI..MYANMAR SIGN DOT BELOW + {0x1039, 0x103A, prExtend}, // Mn [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT + {0x103B, 0x103C, prSpacingMark}, // Mc [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA + {0x103D, 0x103E, prExtend}, // Mn [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA + {0x1056, 0x1057, prSpacingMark}, // Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR + {0x1058, 0x1059, prExtend}, // Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL + {0x105E, 0x1060, prExtend}, // Mn [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA + {0x1071, 0x1074, prExtend}, // Mn [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE + {0x1082, 0x1082, prExtend}, // Mn MYANMAR CONSONANT SIGN SHAN MEDIAL WA + {0x1084, 0x1084, prSpacingMark}, // Mc MYANMAR VOWEL SIGN SHAN E + {0x1085, 0x1086, prExtend}, // Mn [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y + {0x108D, 0x108D, prExtend}, // Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE + {0x109D, 0x109D, prExtend}, // Mn MYANMAR VOWEL SIGN AITON AI + {0x1100, 0x115F, prL}, // Lo [96] HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG FILLER + {0x1160, 0x11A7, prV}, // Lo [72] HANGUL JUNGSEONG FILLER..HANGUL JUNGSEONG O-YAE + {0x11A8, 0x11FF, prT}, // Lo [88] HANGUL JONGSEONG KIYEOK..HANGUL JONGSEONG SSANGNIEUN + {0x135D, 0x135F, prExtend}, // Mn [3] ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK..ETHIOPIC COMBINING GEMINATION MARK + {0x1712, 0x1714, prExtend}, // Mn [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA + {0x1732, 0x1734, prExtend}, // Mn [3] HANUNOO VOWEL SIGN I..HANUNOO SIGN PAMUDPOD + {0x1752, 0x1753, prExtend}, // Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U + {0x1772, 0x1773, prExtend}, // Mn [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U + {0x17B4, 0x17B5, prExtend}, // Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA + {0x17B6, 0x17B6, prSpacingMark}, // Mc KHMER VOWEL SIGN AA + {0x17B7, 0x17BD, prExtend}, // Mn [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA + {0x17BE, 0x17C5, prSpacingMark}, // Mc [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU + {0x17C6, 0x17C6, prExtend}, // Mn KHMER SIGN NIKAHIT + {0x17C7, 0x17C8, prSpacingMark}, // Mc [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU + {0x17C9, 0x17D3, prExtend}, // Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT + {0x17DD, 0x17DD, prExtend}, // Mn KHMER SIGN ATTHACAN + {0x180B, 0x180D, prExtend}, // Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE + {0x180E, 0x180E, prControl}, // Cf MONGOLIAN VOWEL SEPARATOR + {0x1885, 0x1886, prExtend}, // Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA + {0x18A9, 0x18A9, prExtend}, // Mn MONGOLIAN LETTER ALI GALI DAGALGA + {0x1920, 0x1922, prExtend}, // Mn [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U + {0x1923, 0x1926, prSpacingMark}, // Mc [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU + {0x1927, 0x1928, prExtend}, // Mn [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O + {0x1929, 0x192B, prSpacingMark}, // Mc [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA + {0x1930, 0x1931, prSpacingMark}, // Mc [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA + {0x1932, 0x1932, prExtend}, // Mn LIMBU SMALL LETTER ANUSVARA + {0x1933, 0x1938, prSpacingMark}, // Mc [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA + {0x1939, 0x193B, prExtend}, // Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I + {0x1A17, 0x1A18, prExtend}, // Mn [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U + {0x1A19, 0x1A1A, prSpacingMark}, // Mc [2] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN O + {0x1A1B, 0x1A1B, prExtend}, // Mn BUGINESE VOWEL SIGN AE + {0x1A55, 0x1A55, prSpacingMark}, // Mc TAI THAM CONSONANT SIGN MEDIAL RA + {0x1A56, 0x1A56, prExtend}, // Mn TAI THAM CONSONANT SIGN MEDIAL LA + {0x1A57, 0x1A57, prSpacingMark}, // Mc TAI THAM CONSONANT SIGN LA TANG LAI + {0x1A58, 0x1A5E, prExtend}, // Mn [7] TAI THAM SIGN MAI KANG LAI..TAI THAM CONSONANT SIGN SA + {0x1A60, 0x1A60, prExtend}, // Mn TAI THAM SIGN SAKOT + {0x1A62, 0x1A62, prExtend}, // Mn TAI THAM VOWEL SIGN MAI SAT + {0x1A65, 0x1A6C, prExtend}, // Mn [8] TAI THAM VOWEL SIGN I..TAI THAM VOWEL SIGN OA BELOW + {0x1A6D, 0x1A72, prSpacingMark}, // Mc [6] TAI THAM VOWEL SIGN OY..TAI THAM VOWEL SIGN THAM AI + {0x1A73, 0x1A7C, prExtend}, // Mn [10] TAI THAM VOWEL SIGN OA ABOVE..TAI THAM SIGN KHUEN-LUE KARAN + {0x1A7F, 0x1A7F, prExtend}, // Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT + {0x1AB0, 0x1ABD, prExtend}, // Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW + {0x1ABE, 0x1ABE, prExtend}, // Me COMBINING PARENTHESES OVERLAY + {0x1B00, 0x1B03, prExtend}, // Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG + {0x1B04, 0x1B04, prSpacingMark}, // Mc BALINESE SIGN BISAH + {0x1B34, 0x1B34, prExtend}, // Mn BALINESE SIGN REREKAN + {0x1B35, 0x1B35, prExtend}, // Mc BALINESE VOWEL SIGN TEDUNG + {0x1B36, 0x1B3A, prExtend}, // Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA + {0x1B3B, 0x1B3B, prSpacingMark}, // Mc BALINESE VOWEL SIGN RA REPA TEDUNG + {0x1B3C, 0x1B3C, prExtend}, // Mn BALINESE VOWEL SIGN LA LENGA + {0x1B3D, 0x1B41, prSpacingMark}, // Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG + {0x1B42, 0x1B42, prExtend}, // Mn BALINESE VOWEL SIGN PEPET + {0x1B43, 0x1B44, prSpacingMark}, // Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG + {0x1B6B, 0x1B73, prExtend}, // Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG + {0x1B80, 0x1B81, prExtend}, // Mn [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR + {0x1B82, 0x1B82, prSpacingMark}, // Mc SUNDANESE SIGN PANGWISAD + {0x1BA1, 0x1BA1, prSpacingMark}, // Mc SUNDANESE CONSONANT SIGN PAMINGKAL + {0x1BA2, 0x1BA5, prExtend}, // Mn [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU + {0x1BA6, 0x1BA7, prSpacingMark}, // Mc [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG + {0x1BA8, 0x1BA9, prExtend}, // Mn [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG + {0x1BAA, 0x1BAA, prSpacingMark}, // Mc SUNDANESE SIGN PAMAAEH + {0x1BAB, 0x1BAD, prExtend}, // Mn [3] SUNDANESE SIGN VIRAMA..SUNDANESE CONSONANT SIGN PASANGAN WA + {0x1BE6, 0x1BE6, prExtend}, // Mn BATAK SIGN TOMPI + {0x1BE7, 0x1BE7, prSpacingMark}, // Mc BATAK VOWEL SIGN E + {0x1BE8, 0x1BE9, prExtend}, // Mn [2] BATAK VOWEL SIGN PAKPAK E..BATAK VOWEL SIGN EE + {0x1BEA, 0x1BEC, prSpacingMark}, // Mc [3] BATAK VOWEL SIGN I..BATAK VOWEL SIGN O + {0x1BED, 0x1BED, prExtend}, // Mn BATAK VOWEL SIGN KARO O + {0x1BEE, 0x1BEE, prSpacingMark}, // Mc BATAK VOWEL SIGN U + {0x1BEF, 0x1BF1, prExtend}, // Mn [3] BATAK VOWEL SIGN U FOR SIMALUNGUN SA..BATAK CONSONANT SIGN H + {0x1BF2, 0x1BF3, prSpacingMark}, // Mc [2] BATAK PANGOLAT..BATAK PANONGONAN + {0x1C24, 0x1C2B, prSpacingMark}, // Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU + {0x1C2C, 0x1C33, prExtend}, // Mn [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T + {0x1C34, 0x1C35, prSpacingMark}, // Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG + {0x1C36, 0x1C37, prExtend}, // Mn [2] LEPCHA SIGN RAN..LEPCHA SIGN NUKTA + {0x1CD0, 0x1CD2, prExtend}, // Mn [3] VEDIC TONE KARSHANA..VEDIC TONE PRENKHA + {0x1CD4, 0x1CE0, prExtend}, // Mn [13] VEDIC SIGN YAJURVEDIC MIDLINE SVARITA..VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA + {0x1CE1, 0x1CE1, prSpacingMark}, // Mc VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA + {0x1CE2, 0x1CE8, prExtend}, // Mn [7] VEDIC SIGN VISARGA SVARITA..VEDIC SIGN VISARGA ANUDATTA WITH TAIL + {0x1CED, 0x1CED, prExtend}, // Mn VEDIC SIGN TIRYAK + {0x1CF4, 0x1CF4, prExtend}, // Mn VEDIC TONE CANDRA ABOVE + {0x1CF7, 0x1CF7, prSpacingMark}, // Mc VEDIC SIGN ATIKRAMA + {0x1CF8, 0x1CF9, prExtend}, // Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE + {0x1DC0, 0x1DF9, prExtend}, // Mn [58] COMBINING DOTTED GRAVE ACCENT..COMBINING WIDE INVERTED BRIDGE BELOW + {0x1DFB, 0x1DFF, prExtend}, // Mn [5] COMBINING DELETION MARK..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW + {0x200B, 0x200B, prControl}, // Cf ZERO WIDTH SPACE + {0x200C, 0x200C, prExtend}, // Cf ZERO WIDTH NON-JOINER + {0x200D, 0x200D, prZWJ}, // Cf ZERO WIDTH JOINER + {0x200E, 0x200F, prControl}, // Cf [2] LEFT-TO-RIGHT MARK..RIGHT-TO-LEFT MARK + {0x2028, 0x2028, prControl}, // Zl LINE SEPARATOR + {0x2029, 0x2029, prControl}, // Zp PARAGRAPH SEPARATOR + {0x202A, 0x202E, prControl}, // Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE + {0x203C, 0x203C, prExtendedPictographic}, // 1.1 [1] (‼️) double exclamation mark + {0x2049, 0x2049, prExtendedPictographic}, // 3.0 [1] (⁉️) exclamation question mark + {0x2060, 0x2064, prControl}, // Cf [5] WORD JOINER..INVISIBLE PLUS + {0x2065, 0x2065, prControl}, // Cn + {0x2066, 0x206F, prControl}, // Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES + {0x20D0, 0x20DC, prExtend}, // Mn [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE + {0x20DD, 0x20E0, prExtend}, // Me [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH + {0x20E1, 0x20E1, prExtend}, // Mn COMBINING LEFT RIGHT ARROW ABOVE + {0x20E2, 0x20E4, prExtend}, // Me [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE + {0x20E5, 0x20F0, prExtend}, // Mn [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE + {0x2122, 0x2122, prExtendedPictographic}, // 1.1 [1] (™️) trade mark + {0x2139, 0x2139, prExtendedPictographic}, // 3.0 [1] (ℹ️) information + {0x2194, 0x2199, prExtendedPictographic}, // 1.1 [6] (↔️..↙️) left-right arrow..down-left arrow + {0x21A9, 0x21AA, prExtendedPictographic}, // 1.1 [2] (↩️..↪️) right arrow curving left..left arrow curving right + {0x231A, 0x231B, prExtendedPictographic}, // 1.1 [2] (⌚..⌛) watch..hourglass done + {0x2328, 0x2328, prExtendedPictographic}, // 1.1 [1] (⌨️) keyboard + {0x2388, 0x2388, prExtendedPictographic}, // 3.0 [1] (⎈) HELM SYMBOL + {0x23CF, 0x23CF, prExtendedPictographic}, // 4.0 [1] (⏏️) eject button + {0x23E9, 0x23F3, prExtendedPictographic}, // 6.0 [11] (⏩..⏳) fast-forward button..hourglass not done + {0x23F8, 0x23FA, prExtendedPictographic}, // 7.0 [3] (⏸️..⏺️) pause button..record button + {0x24C2, 0x24C2, prExtendedPictographic}, // 1.1 [1] (Ⓜ️) circled M + {0x25AA, 0x25AB, prExtendedPictographic}, // 1.1 [2] (▪️..▫️) black small square..white small square + {0x25B6, 0x25B6, prExtendedPictographic}, // 1.1 [1] (▶️) play button + {0x25C0, 0x25C0, prExtendedPictographic}, // 1.1 [1] (◀️) reverse button + {0x25FB, 0x25FE, prExtendedPictographic}, // 3.2 [4] (◻️..◾) white medium square..black medium-small square + {0x2600, 0x2605, prExtendedPictographic}, // 1.1 [6] (☀️..★) sun..BLACK STAR + {0x2607, 0x2612, prExtendedPictographic}, // 1.1 [12] (☇..☒) LIGHTNING..BALLOT BOX WITH X + {0x2614, 0x2615, prExtendedPictographic}, // 4.0 [2] (☔..☕) umbrella with rain drops..hot beverage + {0x2616, 0x2617, prExtendedPictographic}, // 3.2 [2] (☖..☗) WHITE SHOGI PIECE..BLACK SHOGI PIECE + {0x2618, 0x2618, prExtendedPictographic}, // 4.1 [1] (☘️) shamrock + {0x2619, 0x2619, prExtendedPictographic}, // 3.0 [1] (☙) REVERSED ROTATED FLORAL HEART BULLET + {0x261A, 0x266F, prExtendedPictographic}, // 1.1 [86] (☚..♯) BLACK LEFT POINTING INDEX..MUSIC SHARP SIGN + {0x2670, 0x2671, prExtendedPictographic}, // 3.0 [2] (♰..♱) WEST SYRIAC CROSS..EAST SYRIAC CROSS + {0x2672, 0x267D, prExtendedPictographic}, // 3.2 [12] (♲..♽) UNIVERSAL RECYCLING SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL + {0x267E, 0x267F, prExtendedPictographic}, // 4.1 [2] (♾️..♿) infinity..wheelchair symbol + {0x2680, 0x2685, prExtendedPictographic}, // 3.2 [6] (⚀..⚅) DIE FACE-1..DIE FACE-6 + {0x2690, 0x2691, prExtendedPictographic}, // 4.0 [2] (⚐..⚑) WHITE FLAG..BLACK FLAG + {0x2692, 0x269C, prExtendedPictographic}, // 4.1 [11] (⚒️..⚜️) hammer and pick..fleur-de-lis + {0x269D, 0x269D, prExtendedPictographic}, // 5.1 [1] (⚝) OUTLINED WHITE STAR + {0x269E, 0x269F, prExtendedPictographic}, // 5.2 [2] (⚞..⚟) THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT + {0x26A0, 0x26A1, prExtendedPictographic}, // 4.0 [2] (⚠️..⚡) warning..high voltage + {0x26A2, 0x26B1, prExtendedPictographic}, // 4.1 [16] (⚢..⚱️) DOUBLED FEMALE SIGN..funeral urn + {0x26B2, 0x26B2, prExtendedPictographic}, // 5.0 [1] (⚲) NEUTER + {0x26B3, 0x26BC, prExtendedPictographic}, // 5.1 [10] (⚳..⚼) CERES..SESQUIQUADRATE + {0x26BD, 0x26BF, prExtendedPictographic}, // 5.2 [3] (⚽..⚿) soccer ball..SQUARED KEY + {0x26C0, 0x26C3, prExtendedPictographic}, // 5.1 [4] (⛀..⛃) WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING + {0x26C4, 0x26CD, prExtendedPictographic}, // 5.2 [10] (⛄..⛍) snowman without snow..DISABLED CAR + {0x26CE, 0x26CE, prExtendedPictographic}, // 6.0 [1] (⛎) Ophiuchus + {0x26CF, 0x26E1, prExtendedPictographic}, // 5.2 [19] (⛏️..⛡) pick..RESTRICTED LEFT ENTRY-2 + {0x26E2, 0x26E2, prExtendedPictographic}, // 6.0 [1] (⛢) ASTRONOMICAL SYMBOL FOR URANUS + {0x26E3, 0x26E3, prExtendedPictographic}, // 5.2 [1] (⛣) HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE + {0x26E4, 0x26E7, prExtendedPictographic}, // 6.0 [4] (⛤..⛧) PENTAGRAM..INVERTED PENTAGRAM + {0x26E8, 0x26FF, prExtendedPictographic}, // 5.2 [24] (⛨..⛿) BLACK CROSS ON SHIELD..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE + {0x2700, 0x2700, prExtendedPictographic}, // 7.0 [1] (✀) BLACK SAFETY SCISSORS + {0x2701, 0x2704, prExtendedPictographic}, // 1.1 [4] (✁..✄) UPPER BLADE SCISSORS..WHITE SCISSORS + {0x2705, 0x2705, prExtendedPictographic}, // 6.0 [1] (✅) check mark button + {0x2708, 0x2709, prExtendedPictographic}, // 1.1 [2] (✈️..✉️) airplane..envelope + {0x270A, 0x270B, prExtendedPictographic}, // 6.0 [2] (✊..✋) raised fist..raised hand + {0x270C, 0x2712, prExtendedPictographic}, // 1.1 [7] (✌️..✒️) victory hand..black nib + {0x2714, 0x2714, prExtendedPictographic}, // 1.1 [1] (✔️) check mark + {0x2716, 0x2716, prExtendedPictographic}, // 1.1 [1] (✖️) multiplication sign + {0x271D, 0x271D, prExtendedPictographic}, // 1.1 [1] (✝️) latin cross + {0x2721, 0x2721, prExtendedPictographic}, // 1.1 [1] (✡️) star of David + {0x2728, 0x2728, prExtendedPictographic}, // 6.0 [1] (✨) sparkles + {0x2733, 0x2734, prExtendedPictographic}, // 1.1 [2] (✳️..✴️) eight-spoked asterisk..eight-pointed star + {0x2744, 0x2744, prExtendedPictographic}, // 1.1 [1] (❄️) snowflake + {0x2747, 0x2747, prExtendedPictographic}, // 1.1 [1] (❇️) sparkle + {0x274C, 0x274C, prExtendedPictographic}, // 6.0 [1] (❌) cross mark + {0x274E, 0x274E, prExtendedPictographic}, // 6.0 [1] (❎) cross mark button + {0x2753, 0x2755, prExtendedPictographic}, // 6.0 [3] (❓..❕) question mark..white exclamation mark + {0x2757, 0x2757, prExtendedPictographic}, // 5.2 [1] (❗) exclamation mark + {0x2763, 0x2767, prExtendedPictographic}, // 1.1 [5] (❣️..❧) heart exclamation..ROTATED FLORAL HEART BULLET + {0x2795, 0x2797, prExtendedPictographic}, // 6.0 [3] (➕..➗) plus sign..division sign + {0x27A1, 0x27A1, prExtendedPictographic}, // 1.1 [1] (➡️) right arrow + {0x27B0, 0x27B0, prExtendedPictographic}, // 6.0 [1] (➰) curly loop + {0x27BF, 0x27BF, prExtendedPictographic}, // 6.0 [1] (➿) double curly loop + {0x2934, 0x2935, prExtendedPictographic}, // 3.2 [2] (⤴️..⤵️) right arrow curving up..right arrow curving down + {0x2B05, 0x2B07, prExtendedPictographic}, // 4.0 [3] (⬅️..⬇️) left arrow..down arrow + {0x2B1B, 0x2B1C, prExtendedPictographic}, // 5.1 [2] (⬛..⬜) black large square..white large square + {0x2B50, 0x2B50, prExtendedPictographic}, // 5.1 [1] (⭐) star + {0x2B55, 0x2B55, prExtendedPictographic}, // 5.2 [1] (⭕) hollow red circle + {0x2CEF, 0x2CF1, prExtend}, // Mn [3] COPTIC COMBINING NI ABOVE..COPTIC COMBINING SPIRITUS LENIS + {0x2D7F, 0x2D7F, prExtend}, // Mn TIFINAGH CONSONANT JOINER + {0x2DE0, 0x2DFF, prExtend}, // Mn [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS + {0x302A, 0x302D, prExtend}, // Mn [4] IDEOGRAPHIC LEVEL TONE MARK..IDEOGRAPHIC ENTERING TONE MARK + {0x302E, 0x302F, prExtend}, // Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK + {0x3030, 0x3030, prExtendedPictographic}, // 1.1 [1] (〰️) wavy dash + {0x303D, 0x303D, prExtendedPictographic}, // 3.2 [1] (〽️) part alternation mark + {0x3099, 0x309A, prExtend}, // Mn [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK + {0x3297, 0x3297, prExtendedPictographic}, // 1.1 [1] (㊗️) Japanese “congratulations” button + {0x3299, 0x3299, prExtendedPictographic}, // 1.1 [1] (㊙️) Japanese “secret” button + {0xA66F, 0xA66F, prExtend}, // Mn COMBINING CYRILLIC VZMET + {0xA670, 0xA672, prExtend}, // Me [3] COMBINING CYRILLIC TEN MILLIONS SIGN..COMBINING CYRILLIC THOUSAND MILLIONS SIGN + {0xA674, 0xA67D, prExtend}, // Mn [10] COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC PAYEROK + {0xA69E, 0xA69F, prExtend}, // Mn [2] COMBINING CYRILLIC LETTER EF..COMBINING CYRILLIC LETTER IOTIFIED E + {0xA6F0, 0xA6F1, prExtend}, // Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS + {0xA802, 0xA802, prExtend}, // Mn SYLOTI NAGRI SIGN DVISVARA + {0xA806, 0xA806, prExtend}, // Mn SYLOTI NAGRI SIGN HASANTA + {0xA80B, 0xA80B, prExtend}, // Mn SYLOTI NAGRI SIGN ANUSVARA + {0xA823, 0xA824, prSpacingMark}, // Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I + {0xA825, 0xA826, prExtend}, // Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E + {0xA827, 0xA827, prSpacingMark}, // Mc SYLOTI NAGRI VOWEL SIGN OO + {0xA880, 0xA881, prSpacingMark}, // Mc [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA + {0xA8B4, 0xA8C3, prSpacingMark}, // Mc [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU + {0xA8C4, 0xA8C5, prExtend}, // Mn [2] SAURASHTRA SIGN VIRAMA..SAURASHTRA SIGN CANDRABINDU + {0xA8E0, 0xA8F1, prExtend}, // Mn [18] COMBINING DEVANAGARI DIGIT ZERO..COMBINING DEVANAGARI SIGN AVAGRAHA + {0xA8FF, 0xA8FF, prExtend}, // Mn DEVANAGARI VOWEL SIGN AY + {0xA926, 0xA92D, prExtend}, // Mn [8] KAYAH LI VOWEL UE..KAYAH LI TONE CALYA PLOPHU + {0xA947, 0xA951, prExtend}, // Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R + {0xA952, 0xA953, prSpacingMark}, // Mc [2] REJANG CONSONANT SIGN H..REJANG VIRAMA + {0xA960, 0xA97C, prL}, // Lo [29] HANGUL CHOSEONG TIKEUT-MIEUM..HANGUL CHOSEONG SSANGYEORINHIEUH + {0xA980, 0xA982, prExtend}, // Mn [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN LAYAR + {0xA983, 0xA983, prSpacingMark}, // Mc JAVANESE SIGN WIGNYAN + {0xA9B3, 0xA9B3, prExtend}, // Mn JAVANESE SIGN CECAK TELU + {0xA9B4, 0xA9B5, prSpacingMark}, // Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG + {0xA9B6, 0xA9B9, prExtend}, // Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT + {0xA9BA, 0xA9BB, prSpacingMark}, // Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE + {0xA9BC, 0xA9BD, prExtend}, // Mn [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET + {0xA9BE, 0xA9C0, prSpacingMark}, // Mc [3] JAVANESE CONSONANT SIGN PENGKAL..JAVANESE PANGKON + {0xA9E5, 0xA9E5, prExtend}, // Mn MYANMAR SIGN SHAN SAW + {0xAA29, 0xAA2E, prExtend}, // Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE + {0xAA2F, 0xAA30, prSpacingMark}, // Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI + {0xAA31, 0xAA32, prExtend}, // Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE + {0xAA33, 0xAA34, prSpacingMark}, // Mc [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA + {0xAA35, 0xAA36, prExtend}, // Mn [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA + {0xAA43, 0xAA43, prExtend}, // Mn CHAM CONSONANT SIGN FINAL NG + {0xAA4C, 0xAA4C, prExtend}, // Mn CHAM CONSONANT SIGN FINAL M + {0xAA4D, 0xAA4D, prSpacingMark}, // Mc CHAM CONSONANT SIGN FINAL H + {0xAA7C, 0xAA7C, prExtend}, // Mn MYANMAR SIGN TAI LAING TONE-2 + {0xAAB0, 0xAAB0, prExtend}, // Mn TAI VIET MAI KANG + {0xAAB2, 0xAAB4, prExtend}, // Mn [3] TAI VIET VOWEL I..TAI VIET VOWEL U + {0xAAB7, 0xAAB8, prExtend}, // Mn [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA + {0xAABE, 0xAABF, prExtend}, // Mn [2] TAI VIET VOWEL AM..TAI VIET TONE MAI EK + {0xAAC1, 0xAAC1, prExtend}, // Mn TAI VIET TONE MAI THO + {0xAAEB, 0xAAEB, prSpacingMark}, // Mc MEETEI MAYEK VOWEL SIGN II + {0xAAEC, 0xAAED, prExtend}, // Mn [2] MEETEI MAYEK VOWEL SIGN UU..MEETEI MAYEK VOWEL SIGN AAI + {0xAAEE, 0xAAEF, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN AU..MEETEI MAYEK VOWEL SIGN AAU + {0xAAF5, 0xAAF5, prSpacingMark}, // Mc MEETEI MAYEK VOWEL SIGN VISARGA + {0xAAF6, 0xAAF6, prExtend}, // Mn MEETEI MAYEK VIRAMA + {0xABE3, 0xABE4, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP + {0xABE5, 0xABE5, prExtend}, // Mn MEETEI MAYEK VOWEL SIGN ANAP + {0xABE6, 0xABE7, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN YENAP..MEETEI MAYEK VOWEL SIGN SOUNAP + {0xABE8, 0xABE8, prExtend}, // Mn MEETEI MAYEK VOWEL SIGN UNAP + {0xABE9, 0xABEA, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN CHEINAP..MEETEI MAYEK VOWEL SIGN NUNG + {0xABEC, 0xABEC, prSpacingMark}, // Mc MEETEI MAYEK LUM IYEK + {0xABED, 0xABED, prExtend}, // Mn MEETEI MAYEK APUN IYEK + {0xAC00, 0xAC00, prLV}, // Lo HANGUL SYLLABLE GA + {0xAC01, 0xAC1B, prLVT}, // Lo [27] HANGUL SYLLABLE GAG..HANGUL SYLLABLE GAH + {0xAC1C, 0xAC1C, prLV}, // Lo HANGUL SYLLABLE GAE + {0xAC1D, 0xAC37, prLVT}, // Lo [27] HANGUL SYLLABLE GAEG..HANGUL SYLLABLE GAEH + {0xAC38, 0xAC38, prLV}, // Lo HANGUL SYLLABLE GYA + {0xAC39, 0xAC53, prLVT}, // Lo [27] HANGUL SYLLABLE GYAG..HANGUL SYLLABLE GYAH + {0xAC54, 0xAC54, prLV}, // Lo HANGUL SYLLABLE GYAE + {0xAC55, 0xAC6F, prLVT}, // Lo [27] HANGUL SYLLABLE GYAEG..HANGUL SYLLABLE GYAEH + {0xAC70, 0xAC70, prLV}, // Lo HANGUL SYLLABLE GEO + {0xAC71, 0xAC8B, prLVT}, // Lo [27] HANGUL SYLLABLE GEOG..HANGUL SYLLABLE GEOH + {0xAC8C, 0xAC8C, prLV}, // Lo HANGUL SYLLABLE GE + {0xAC8D, 0xACA7, prLVT}, // Lo [27] HANGUL SYLLABLE GEG..HANGUL SYLLABLE GEH + {0xACA8, 0xACA8, prLV}, // Lo HANGUL SYLLABLE GYEO + {0xACA9, 0xACC3, prLVT}, // Lo [27] HANGUL SYLLABLE GYEOG..HANGUL SYLLABLE GYEOH + {0xACC4, 0xACC4, prLV}, // Lo HANGUL SYLLABLE GYE + {0xACC5, 0xACDF, prLVT}, // Lo [27] HANGUL SYLLABLE GYEG..HANGUL SYLLABLE GYEH + {0xACE0, 0xACE0, prLV}, // Lo HANGUL SYLLABLE GO + {0xACE1, 0xACFB, prLVT}, // Lo [27] HANGUL SYLLABLE GOG..HANGUL SYLLABLE GOH + {0xACFC, 0xACFC, prLV}, // Lo HANGUL SYLLABLE GWA + {0xACFD, 0xAD17, prLVT}, // Lo [27] HANGUL SYLLABLE GWAG..HANGUL SYLLABLE GWAH + {0xAD18, 0xAD18, prLV}, // Lo HANGUL SYLLABLE GWAE + {0xAD19, 0xAD33, prLVT}, // Lo [27] HANGUL SYLLABLE GWAEG..HANGUL SYLLABLE GWAEH + {0xAD34, 0xAD34, prLV}, // Lo HANGUL SYLLABLE GOE + {0xAD35, 0xAD4F, prLVT}, // Lo [27] HANGUL SYLLABLE GOEG..HANGUL SYLLABLE GOEH + {0xAD50, 0xAD50, prLV}, // Lo HANGUL SYLLABLE GYO + {0xAD51, 0xAD6B, prLVT}, // Lo [27] HANGUL SYLLABLE GYOG..HANGUL SYLLABLE GYOH + {0xAD6C, 0xAD6C, prLV}, // Lo HANGUL SYLLABLE GU + {0xAD6D, 0xAD87, prLVT}, // Lo [27] HANGUL SYLLABLE GUG..HANGUL SYLLABLE GUH + {0xAD88, 0xAD88, prLV}, // Lo HANGUL SYLLABLE GWEO + {0xAD89, 0xADA3, prLVT}, // Lo [27] HANGUL SYLLABLE GWEOG..HANGUL SYLLABLE GWEOH + {0xADA4, 0xADA4, prLV}, // Lo HANGUL SYLLABLE GWE + {0xADA5, 0xADBF, prLVT}, // Lo [27] HANGUL SYLLABLE GWEG..HANGUL SYLLABLE GWEH + {0xADC0, 0xADC0, prLV}, // Lo HANGUL SYLLABLE GWI + {0xADC1, 0xADDB, prLVT}, // Lo [27] HANGUL SYLLABLE GWIG..HANGUL SYLLABLE GWIH + {0xADDC, 0xADDC, prLV}, // Lo HANGUL SYLLABLE GYU + {0xADDD, 0xADF7, prLVT}, // Lo [27] HANGUL SYLLABLE GYUG..HANGUL SYLLABLE GYUH + {0xADF8, 0xADF8, prLV}, // Lo HANGUL SYLLABLE GEU + {0xADF9, 0xAE13, prLVT}, // Lo [27] HANGUL SYLLABLE GEUG..HANGUL SYLLABLE GEUH + {0xAE14, 0xAE14, prLV}, // Lo HANGUL SYLLABLE GYI + {0xAE15, 0xAE2F, prLVT}, // Lo [27] HANGUL SYLLABLE GYIG..HANGUL SYLLABLE GYIH + {0xAE30, 0xAE30, prLV}, // Lo HANGUL SYLLABLE GI + {0xAE31, 0xAE4B, prLVT}, // Lo [27] HANGUL SYLLABLE GIG..HANGUL SYLLABLE GIH + {0xAE4C, 0xAE4C, prLV}, // Lo HANGUL SYLLABLE GGA + {0xAE4D, 0xAE67, prLVT}, // Lo [27] HANGUL SYLLABLE GGAG..HANGUL SYLLABLE GGAH + {0xAE68, 0xAE68, prLV}, // Lo HANGUL SYLLABLE GGAE + {0xAE69, 0xAE83, prLVT}, // Lo [27] HANGUL SYLLABLE GGAEG..HANGUL SYLLABLE GGAEH + {0xAE84, 0xAE84, prLV}, // Lo HANGUL SYLLABLE GGYA + {0xAE85, 0xAE9F, prLVT}, // Lo [27] HANGUL SYLLABLE GGYAG..HANGUL SYLLABLE GGYAH + {0xAEA0, 0xAEA0, prLV}, // Lo HANGUL SYLLABLE GGYAE + {0xAEA1, 0xAEBB, prLVT}, // Lo [27] HANGUL SYLLABLE GGYAEG..HANGUL SYLLABLE GGYAEH + {0xAEBC, 0xAEBC, prLV}, // Lo HANGUL SYLLABLE GGEO + {0xAEBD, 0xAED7, prLVT}, // Lo [27] HANGUL SYLLABLE GGEOG..HANGUL SYLLABLE GGEOH + {0xAED8, 0xAED8, prLV}, // Lo HANGUL SYLLABLE GGE + {0xAED9, 0xAEF3, prLVT}, // Lo [27] HANGUL SYLLABLE GGEG..HANGUL SYLLABLE GGEH + {0xAEF4, 0xAEF4, prLV}, // Lo HANGUL SYLLABLE GGYEO + {0xAEF5, 0xAF0F, prLVT}, // Lo [27] HANGUL SYLLABLE GGYEOG..HANGUL SYLLABLE GGYEOH + {0xAF10, 0xAF10, prLV}, // Lo HANGUL SYLLABLE GGYE + {0xAF11, 0xAF2B, prLVT}, // Lo [27] HANGUL SYLLABLE GGYEG..HANGUL SYLLABLE GGYEH + {0xAF2C, 0xAF2C, prLV}, // Lo HANGUL SYLLABLE GGO + {0xAF2D, 0xAF47, prLVT}, // Lo [27] HANGUL SYLLABLE GGOG..HANGUL SYLLABLE GGOH + {0xAF48, 0xAF48, prLV}, // Lo HANGUL SYLLABLE GGWA + {0xAF49, 0xAF63, prLVT}, // Lo [27] HANGUL SYLLABLE GGWAG..HANGUL SYLLABLE GGWAH + {0xAF64, 0xAF64, prLV}, // Lo HANGUL SYLLABLE GGWAE + {0xAF65, 0xAF7F, prLVT}, // Lo [27] HANGUL SYLLABLE GGWAEG..HANGUL SYLLABLE GGWAEH + {0xAF80, 0xAF80, prLV}, // Lo HANGUL SYLLABLE GGOE + {0xAF81, 0xAF9B, prLVT}, // Lo [27] HANGUL SYLLABLE GGOEG..HANGUL SYLLABLE GGOEH + {0xAF9C, 0xAF9C, prLV}, // Lo HANGUL SYLLABLE GGYO + {0xAF9D, 0xAFB7, prLVT}, // Lo [27] HANGUL SYLLABLE GGYOG..HANGUL SYLLABLE GGYOH + {0xAFB8, 0xAFB8, prLV}, // Lo HANGUL SYLLABLE GGU + {0xAFB9, 0xAFD3, prLVT}, // Lo [27] HANGUL SYLLABLE GGUG..HANGUL SYLLABLE GGUH + {0xAFD4, 0xAFD4, prLV}, // Lo HANGUL SYLLABLE GGWEO + {0xAFD5, 0xAFEF, prLVT}, // Lo [27] HANGUL SYLLABLE GGWEOG..HANGUL SYLLABLE GGWEOH + {0xAFF0, 0xAFF0, prLV}, // Lo HANGUL SYLLABLE GGWE + {0xAFF1, 0xB00B, prLVT}, // Lo [27] HANGUL SYLLABLE GGWEG..HANGUL SYLLABLE GGWEH + {0xB00C, 0xB00C, prLV}, // Lo HANGUL SYLLABLE GGWI + {0xB00D, 0xB027, prLVT}, // Lo [27] HANGUL SYLLABLE GGWIG..HANGUL SYLLABLE GGWIH + {0xB028, 0xB028, prLV}, // Lo HANGUL SYLLABLE GGYU + {0xB029, 0xB043, prLVT}, // Lo [27] HANGUL SYLLABLE GGYUG..HANGUL SYLLABLE GGYUH + {0xB044, 0xB044, prLV}, // Lo HANGUL SYLLABLE GGEU + {0xB045, 0xB05F, prLVT}, // Lo [27] HANGUL SYLLABLE GGEUG..HANGUL SYLLABLE GGEUH + {0xB060, 0xB060, prLV}, // Lo HANGUL SYLLABLE GGYI + {0xB061, 0xB07B, prLVT}, // Lo [27] HANGUL SYLLABLE GGYIG..HANGUL SYLLABLE GGYIH + {0xB07C, 0xB07C, prLV}, // Lo HANGUL SYLLABLE GGI + {0xB07D, 0xB097, prLVT}, // Lo [27] HANGUL SYLLABLE GGIG..HANGUL SYLLABLE GGIH + {0xB098, 0xB098, prLV}, // Lo HANGUL SYLLABLE NA + {0xB099, 0xB0B3, prLVT}, // Lo [27] HANGUL SYLLABLE NAG..HANGUL SYLLABLE NAH + {0xB0B4, 0xB0B4, prLV}, // Lo HANGUL SYLLABLE NAE + {0xB0B5, 0xB0CF, prLVT}, // Lo [27] HANGUL SYLLABLE NAEG..HANGUL SYLLABLE NAEH + {0xB0D0, 0xB0D0, prLV}, // Lo HANGUL SYLLABLE NYA + {0xB0D1, 0xB0EB, prLVT}, // Lo [27] HANGUL SYLLABLE NYAG..HANGUL SYLLABLE NYAH + {0xB0EC, 0xB0EC, prLV}, // Lo HANGUL SYLLABLE NYAE + {0xB0ED, 0xB107, prLVT}, // Lo [27] HANGUL SYLLABLE NYAEG..HANGUL SYLLABLE NYAEH + {0xB108, 0xB108, prLV}, // Lo HANGUL SYLLABLE NEO + {0xB109, 0xB123, prLVT}, // Lo [27] HANGUL SYLLABLE NEOG..HANGUL SYLLABLE NEOH + {0xB124, 0xB124, prLV}, // Lo HANGUL SYLLABLE NE + {0xB125, 0xB13F, prLVT}, // Lo [27] HANGUL SYLLABLE NEG..HANGUL SYLLABLE NEH + {0xB140, 0xB140, prLV}, // Lo HANGUL SYLLABLE NYEO + {0xB141, 0xB15B, prLVT}, // Lo [27] HANGUL SYLLABLE NYEOG..HANGUL SYLLABLE NYEOH + {0xB15C, 0xB15C, prLV}, // Lo HANGUL SYLLABLE NYE + {0xB15D, 0xB177, prLVT}, // Lo [27] HANGUL SYLLABLE NYEG..HANGUL SYLLABLE NYEH + {0xB178, 0xB178, prLV}, // Lo HANGUL SYLLABLE NO + {0xB179, 0xB193, prLVT}, // Lo [27] HANGUL SYLLABLE NOG..HANGUL SYLLABLE NOH + {0xB194, 0xB194, prLV}, // Lo HANGUL SYLLABLE NWA + {0xB195, 0xB1AF, prLVT}, // Lo [27] HANGUL SYLLABLE NWAG..HANGUL SYLLABLE NWAH + {0xB1B0, 0xB1B0, prLV}, // Lo HANGUL SYLLABLE NWAE + {0xB1B1, 0xB1CB, prLVT}, // Lo [27] HANGUL SYLLABLE NWAEG..HANGUL SYLLABLE NWAEH + {0xB1CC, 0xB1CC, prLV}, // Lo HANGUL SYLLABLE NOE + {0xB1CD, 0xB1E7, prLVT}, // Lo [27] HANGUL SYLLABLE NOEG..HANGUL SYLLABLE NOEH + {0xB1E8, 0xB1E8, prLV}, // Lo HANGUL SYLLABLE NYO + {0xB1E9, 0xB203, prLVT}, // Lo [27] HANGUL SYLLABLE NYOG..HANGUL SYLLABLE NYOH + {0xB204, 0xB204, prLV}, // Lo HANGUL SYLLABLE NU + {0xB205, 0xB21F, prLVT}, // Lo [27] HANGUL SYLLABLE NUG..HANGUL SYLLABLE NUH + {0xB220, 0xB220, prLV}, // Lo HANGUL SYLLABLE NWEO + {0xB221, 0xB23B, prLVT}, // Lo [27] HANGUL SYLLABLE NWEOG..HANGUL SYLLABLE NWEOH + {0xB23C, 0xB23C, prLV}, // Lo HANGUL SYLLABLE NWE + {0xB23D, 0xB257, prLVT}, // Lo [27] HANGUL SYLLABLE NWEG..HANGUL SYLLABLE NWEH + {0xB258, 0xB258, prLV}, // Lo HANGUL SYLLABLE NWI + {0xB259, 0xB273, prLVT}, // Lo [27] HANGUL SYLLABLE NWIG..HANGUL SYLLABLE NWIH + {0xB274, 0xB274, prLV}, // Lo HANGUL SYLLABLE NYU + {0xB275, 0xB28F, prLVT}, // Lo [27] HANGUL SYLLABLE NYUG..HANGUL SYLLABLE NYUH + {0xB290, 0xB290, prLV}, // Lo HANGUL SYLLABLE NEU + {0xB291, 0xB2AB, prLVT}, // Lo [27] HANGUL SYLLABLE NEUG..HANGUL SYLLABLE NEUH + {0xB2AC, 0xB2AC, prLV}, // Lo HANGUL SYLLABLE NYI + {0xB2AD, 0xB2C7, prLVT}, // Lo [27] HANGUL SYLLABLE NYIG..HANGUL SYLLABLE NYIH + {0xB2C8, 0xB2C8, prLV}, // Lo HANGUL SYLLABLE NI + {0xB2C9, 0xB2E3, prLVT}, // Lo [27] HANGUL SYLLABLE NIG..HANGUL SYLLABLE NIH + {0xB2E4, 0xB2E4, prLV}, // Lo HANGUL SYLLABLE DA + {0xB2E5, 0xB2FF, prLVT}, // Lo [27] HANGUL SYLLABLE DAG..HANGUL SYLLABLE DAH + {0xB300, 0xB300, prLV}, // Lo HANGUL SYLLABLE DAE + {0xB301, 0xB31B, prLVT}, // Lo [27] HANGUL SYLLABLE DAEG..HANGUL SYLLABLE DAEH + {0xB31C, 0xB31C, prLV}, // Lo HANGUL SYLLABLE DYA + {0xB31D, 0xB337, prLVT}, // Lo [27] HANGUL SYLLABLE DYAG..HANGUL SYLLABLE DYAH + {0xB338, 0xB338, prLV}, // Lo HANGUL SYLLABLE DYAE + {0xB339, 0xB353, prLVT}, // Lo [27] HANGUL SYLLABLE DYAEG..HANGUL SYLLABLE DYAEH + {0xB354, 0xB354, prLV}, // Lo HANGUL SYLLABLE DEO + {0xB355, 0xB36F, prLVT}, // Lo [27] HANGUL SYLLABLE DEOG..HANGUL SYLLABLE DEOH + {0xB370, 0xB370, prLV}, // Lo HANGUL SYLLABLE DE + {0xB371, 0xB38B, prLVT}, // Lo [27] HANGUL SYLLABLE DEG..HANGUL SYLLABLE DEH + {0xB38C, 0xB38C, prLV}, // Lo HANGUL SYLLABLE DYEO + {0xB38D, 0xB3A7, prLVT}, // Lo [27] HANGUL SYLLABLE DYEOG..HANGUL SYLLABLE DYEOH + {0xB3A8, 0xB3A8, prLV}, // Lo HANGUL SYLLABLE DYE + {0xB3A9, 0xB3C3, prLVT}, // Lo [27] HANGUL SYLLABLE DYEG..HANGUL SYLLABLE DYEH + {0xB3C4, 0xB3C4, prLV}, // Lo HANGUL SYLLABLE DO + {0xB3C5, 0xB3DF, prLVT}, // Lo [27] HANGUL SYLLABLE DOG..HANGUL SYLLABLE DOH + {0xB3E0, 0xB3E0, prLV}, // Lo HANGUL SYLLABLE DWA + {0xB3E1, 0xB3FB, prLVT}, // Lo [27] HANGUL SYLLABLE DWAG..HANGUL SYLLABLE DWAH + {0xB3FC, 0xB3FC, prLV}, // Lo HANGUL SYLLABLE DWAE + {0xB3FD, 0xB417, prLVT}, // Lo [27] HANGUL SYLLABLE DWAEG..HANGUL SYLLABLE DWAEH + {0xB418, 0xB418, prLV}, // Lo HANGUL SYLLABLE DOE + {0xB419, 0xB433, prLVT}, // Lo [27] HANGUL SYLLABLE DOEG..HANGUL SYLLABLE DOEH + {0xB434, 0xB434, prLV}, // Lo HANGUL SYLLABLE DYO + {0xB435, 0xB44F, prLVT}, // Lo [27] HANGUL SYLLABLE DYOG..HANGUL SYLLABLE DYOH + {0xB450, 0xB450, prLV}, // Lo HANGUL SYLLABLE DU + {0xB451, 0xB46B, prLVT}, // Lo [27] HANGUL SYLLABLE DUG..HANGUL SYLLABLE DUH + {0xB46C, 0xB46C, prLV}, // Lo HANGUL SYLLABLE DWEO + {0xB46D, 0xB487, prLVT}, // Lo [27] HANGUL SYLLABLE DWEOG..HANGUL SYLLABLE DWEOH + {0xB488, 0xB488, prLV}, // Lo HANGUL SYLLABLE DWE + {0xB489, 0xB4A3, prLVT}, // Lo [27] HANGUL SYLLABLE DWEG..HANGUL SYLLABLE DWEH + {0xB4A4, 0xB4A4, prLV}, // Lo HANGUL SYLLABLE DWI + {0xB4A5, 0xB4BF, prLVT}, // Lo [27] HANGUL SYLLABLE DWIG..HANGUL SYLLABLE DWIH + {0xB4C0, 0xB4C0, prLV}, // Lo HANGUL SYLLABLE DYU + {0xB4C1, 0xB4DB, prLVT}, // Lo [27] HANGUL SYLLABLE DYUG..HANGUL SYLLABLE DYUH + {0xB4DC, 0xB4DC, prLV}, // Lo HANGUL SYLLABLE DEU + {0xB4DD, 0xB4F7, prLVT}, // Lo [27] HANGUL SYLLABLE DEUG..HANGUL SYLLABLE DEUH + {0xB4F8, 0xB4F8, prLV}, // Lo HANGUL SYLLABLE DYI + {0xB4F9, 0xB513, prLVT}, // Lo [27] HANGUL SYLLABLE DYIG..HANGUL SYLLABLE DYIH + {0xB514, 0xB514, prLV}, // Lo HANGUL SYLLABLE DI + {0xB515, 0xB52F, prLVT}, // Lo [27] HANGUL SYLLABLE DIG..HANGUL SYLLABLE DIH + {0xB530, 0xB530, prLV}, // Lo HANGUL SYLLABLE DDA + {0xB531, 0xB54B, prLVT}, // Lo [27] HANGUL SYLLABLE DDAG..HANGUL SYLLABLE DDAH + {0xB54C, 0xB54C, prLV}, // Lo HANGUL SYLLABLE DDAE + {0xB54D, 0xB567, prLVT}, // Lo [27] HANGUL SYLLABLE DDAEG..HANGUL SYLLABLE DDAEH + {0xB568, 0xB568, prLV}, // Lo HANGUL SYLLABLE DDYA + {0xB569, 0xB583, prLVT}, // Lo [27] HANGUL SYLLABLE DDYAG..HANGUL SYLLABLE DDYAH + {0xB584, 0xB584, prLV}, // Lo HANGUL SYLLABLE DDYAE + {0xB585, 0xB59F, prLVT}, // Lo [27] HANGUL SYLLABLE DDYAEG..HANGUL SYLLABLE DDYAEH + {0xB5A0, 0xB5A0, prLV}, // Lo HANGUL SYLLABLE DDEO + {0xB5A1, 0xB5BB, prLVT}, // Lo [27] HANGUL SYLLABLE DDEOG..HANGUL SYLLABLE DDEOH + {0xB5BC, 0xB5BC, prLV}, // Lo HANGUL SYLLABLE DDE + {0xB5BD, 0xB5D7, prLVT}, // Lo [27] HANGUL SYLLABLE DDEG..HANGUL SYLLABLE DDEH + {0xB5D8, 0xB5D8, prLV}, // Lo HANGUL SYLLABLE DDYEO + {0xB5D9, 0xB5F3, prLVT}, // Lo [27] HANGUL SYLLABLE DDYEOG..HANGUL SYLLABLE DDYEOH + {0xB5F4, 0xB5F4, prLV}, // Lo HANGUL SYLLABLE DDYE + {0xB5F5, 0xB60F, prLVT}, // Lo [27] HANGUL SYLLABLE DDYEG..HANGUL SYLLABLE DDYEH + {0xB610, 0xB610, prLV}, // Lo HANGUL SYLLABLE DDO + {0xB611, 0xB62B, prLVT}, // Lo [27] HANGUL SYLLABLE DDOG..HANGUL SYLLABLE DDOH + {0xB62C, 0xB62C, prLV}, // Lo HANGUL SYLLABLE DDWA + {0xB62D, 0xB647, prLVT}, // Lo [27] HANGUL SYLLABLE DDWAG..HANGUL SYLLABLE DDWAH + {0xB648, 0xB648, prLV}, // Lo HANGUL SYLLABLE DDWAE + {0xB649, 0xB663, prLVT}, // Lo [27] HANGUL SYLLABLE DDWAEG..HANGUL SYLLABLE DDWAEH + {0xB664, 0xB664, prLV}, // Lo HANGUL SYLLABLE DDOE + {0xB665, 0xB67F, prLVT}, // Lo [27] HANGUL SYLLABLE DDOEG..HANGUL SYLLABLE DDOEH + {0xB680, 0xB680, prLV}, // Lo HANGUL SYLLABLE DDYO + {0xB681, 0xB69B, prLVT}, // Lo [27] HANGUL SYLLABLE DDYOG..HANGUL SYLLABLE DDYOH + {0xB69C, 0xB69C, prLV}, // Lo HANGUL SYLLABLE DDU + {0xB69D, 0xB6B7, prLVT}, // Lo [27] HANGUL SYLLABLE DDUG..HANGUL SYLLABLE DDUH + {0xB6B8, 0xB6B8, prLV}, // Lo HANGUL SYLLABLE DDWEO + {0xB6B9, 0xB6D3, prLVT}, // Lo [27] HANGUL SYLLABLE DDWEOG..HANGUL SYLLABLE DDWEOH + {0xB6D4, 0xB6D4, prLV}, // Lo HANGUL SYLLABLE DDWE + {0xB6D5, 0xB6EF, prLVT}, // Lo [27] HANGUL SYLLABLE DDWEG..HANGUL SYLLABLE DDWEH + {0xB6F0, 0xB6F0, prLV}, // Lo HANGUL SYLLABLE DDWI + {0xB6F1, 0xB70B, prLVT}, // Lo [27] HANGUL SYLLABLE DDWIG..HANGUL SYLLABLE DDWIH + {0xB70C, 0xB70C, prLV}, // Lo HANGUL SYLLABLE DDYU + {0xB70D, 0xB727, prLVT}, // Lo [27] HANGUL SYLLABLE DDYUG..HANGUL SYLLABLE DDYUH + {0xB728, 0xB728, prLV}, // Lo HANGUL SYLLABLE DDEU + {0xB729, 0xB743, prLVT}, // Lo [27] HANGUL SYLLABLE DDEUG..HANGUL SYLLABLE DDEUH + {0xB744, 0xB744, prLV}, // Lo HANGUL SYLLABLE DDYI + {0xB745, 0xB75F, prLVT}, // Lo [27] HANGUL SYLLABLE DDYIG..HANGUL SYLLABLE DDYIH + {0xB760, 0xB760, prLV}, // Lo HANGUL SYLLABLE DDI + {0xB761, 0xB77B, prLVT}, // Lo [27] HANGUL SYLLABLE DDIG..HANGUL SYLLABLE DDIH + {0xB77C, 0xB77C, prLV}, // Lo HANGUL SYLLABLE RA + {0xB77D, 0xB797, prLVT}, // Lo [27] HANGUL SYLLABLE RAG..HANGUL SYLLABLE RAH + {0xB798, 0xB798, prLV}, // Lo HANGUL SYLLABLE RAE + {0xB799, 0xB7B3, prLVT}, // Lo [27] HANGUL SYLLABLE RAEG..HANGUL SYLLABLE RAEH + {0xB7B4, 0xB7B4, prLV}, // Lo HANGUL SYLLABLE RYA + {0xB7B5, 0xB7CF, prLVT}, // Lo [27] HANGUL SYLLABLE RYAG..HANGUL SYLLABLE RYAH + {0xB7D0, 0xB7D0, prLV}, // Lo HANGUL SYLLABLE RYAE + {0xB7D1, 0xB7EB, prLVT}, // Lo [27] HANGUL SYLLABLE RYAEG..HANGUL SYLLABLE RYAEH + {0xB7EC, 0xB7EC, prLV}, // Lo HANGUL SYLLABLE REO + {0xB7ED, 0xB807, prLVT}, // Lo [27] HANGUL SYLLABLE REOG..HANGUL SYLLABLE REOH + {0xB808, 0xB808, prLV}, // Lo HANGUL SYLLABLE RE + {0xB809, 0xB823, prLVT}, // Lo [27] HANGUL SYLLABLE REG..HANGUL SYLLABLE REH + {0xB824, 0xB824, prLV}, // Lo HANGUL SYLLABLE RYEO + {0xB825, 0xB83F, prLVT}, // Lo [27] HANGUL SYLLABLE RYEOG..HANGUL SYLLABLE RYEOH + {0xB840, 0xB840, prLV}, // Lo HANGUL SYLLABLE RYE + {0xB841, 0xB85B, prLVT}, // Lo [27] HANGUL SYLLABLE RYEG..HANGUL SYLLABLE RYEH + {0xB85C, 0xB85C, prLV}, // Lo HANGUL SYLLABLE RO + {0xB85D, 0xB877, prLVT}, // Lo [27] HANGUL SYLLABLE ROG..HANGUL SYLLABLE ROH + {0xB878, 0xB878, prLV}, // Lo HANGUL SYLLABLE RWA + {0xB879, 0xB893, prLVT}, // Lo [27] HANGUL SYLLABLE RWAG..HANGUL SYLLABLE RWAH + {0xB894, 0xB894, prLV}, // Lo HANGUL SYLLABLE RWAE + {0xB895, 0xB8AF, prLVT}, // Lo [27] HANGUL SYLLABLE RWAEG..HANGUL SYLLABLE RWAEH + {0xB8B0, 0xB8B0, prLV}, // Lo HANGUL SYLLABLE ROE + {0xB8B1, 0xB8CB, prLVT}, // Lo [27] HANGUL SYLLABLE ROEG..HANGUL SYLLABLE ROEH + {0xB8CC, 0xB8CC, prLV}, // Lo HANGUL SYLLABLE RYO + {0xB8CD, 0xB8E7, prLVT}, // Lo [27] HANGUL SYLLABLE RYOG..HANGUL SYLLABLE RYOH + {0xB8E8, 0xB8E8, prLV}, // Lo HANGUL SYLLABLE RU + {0xB8E9, 0xB903, prLVT}, // Lo [27] HANGUL SYLLABLE RUG..HANGUL SYLLABLE RUH + {0xB904, 0xB904, prLV}, // Lo HANGUL SYLLABLE RWEO + {0xB905, 0xB91F, prLVT}, // Lo [27] HANGUL SYLLABLE RWEOG..HANGUL SYLLABLE RWEOH + {0xB920, 0xB920, prLV}, // Lo HANGUL SYLLABLE RWE + {0xB921, 0xB93B, prLVT}, // Lo [27] HANGUL SYLLABLE RWEG..HANGUL SYLLABLE RWEH + {0xB93C, 0xB93C, prLV}, // Lo HANGUL SYLLABLE RWI + {0xB93D, 0xB957, prLVT}, // Lo [27] HANGUL SYLLABLE RWIG..HANGUL SYLLABLE RWIH + {0xB958, 0xB958, prLV}, // Lo HANGUL SYLLABLE RYU + {0xB959, 0xB973, prLVT}, // Lo [27] HANGUL SYLLABLE RYUG..HANGUL SYLLABLE RYUH + {0xB974, 0xB974, prLV}, // Lo HANGUL SYLLABLE REU + {0xB975, 0xB98F, prLVT}, // Lo [27] HANGUL SYLLABLE REUG..HANGUL SYLLABLE REUH + {0xB990, 0xB990, prLV}, // Lo HANGUL SYLLABLE RYI + {0xB991, 0xB9AB, prLVT}, // Lo [27] HANGUL SYLLABLE RYIG..HANGUL SYLLABLE RYIH + {0xB9AC, 0xB9AC, prLV}, // Lo HANGUL SYLLABLE RI + {0xB9AD, 0xB9C7, prLVT}, // Lo [27] HANGUL SYLLABLE RIG..HANGUL SYLLABLE RIH + {0xB9C8, 0xB9C8, prLV}, // Lo HANGUL SYLLABLE MA + {0xB9C9, 0xB9E3, prLVT}, // Lo [27] HANGUL SYLLABLE MAG..HANGUL SYLLABLE MAH + {0xB9E4, 0xB9E4, prLV}, // Lo HANGUL SYLLABLE MAE + {0xB9E5, 0xB9FF, prLVT}, // Lo [27] HANGUL SYLLABLE MAEG..HANGUL SYLLABLE MAEH + {0xBA00, 0xBA00, prLV}, // Lo HANGUL SYLLABLE MYA + {0xBA01, 0xBA1B, prLVT}, // Lo [27] HANGUL SYLLABLE MYAG..HANGUL SYLLABLE MYAH + {0xBA1C, 0xBA1C, prLV}, // Lo HANGUL SYLLABLE MYAE + {0xBA1D, 0xBA37, prLVT}, // Lo [27] HANGUL SYLLABLE MYAEG..HANGUL SYLLABLE MYAEH + {0xBA38, 0xBA38, prLV}, // Lo HANGUL SYLLABLE MEO + {0xBA39, 0xBA53, prLVT}, // Lo [27] HANGUL SYLLABLE MEOG..HANGUL SYLLABLE MEOH + {0xBA54, 0xBA54, prLV}, // Lo HANGUL SYLLABLE ME + {0xBA55, 0xBA6F, prLVT}, // Lo [27] HANGUL SYLLABLE MEG..HANGUL SYLLABLE MEH + {0xBA70, 0xBA70, prLV}, // Lo HANGUL SYLLABLE MYEO + {0xBA71, 0xBA8B, prLVT}, // Lo [27] HANGUL SYLLABLE MYEOG..HANGUL SYLLABLE MYEOH + {0xBA8C, 0xBA8C, prLV}, // Lo HANGUL SYLLABLE MYE + {0xBA8D, 0xBAA7, prLVT}, // Lo [27] HANGUL SYLLABLE MYEG..HANGUL SYLLABLE MYEH + {0xBAA8, 0xBAA8, prLV}, // Lo HANGUL SYLLABLE MO + {0xBAA9, 0xBAC3, prLVT}, // Lo [27] HANGUL SYLLABLE MOG..HANGUL SYLLABLE MOH + {0xBAC4, 0xBAC4, prLV}, // Lo HANGUL SYLLABLE MWA + {0xBAC5, 0xBADF, prLVT}, // Lo [27] HANGUL SYLLABLE MWAG..HANGUL SYLLABLE MWAH + {0xBAE0, 0xBAE0, prLV}, // Lo HANGUL SYLLABLE MWAE + {0xBAE1, 0xBAFB, prLVT}, // Lo [27] HANGUL SYLLABLE MWAEG..HANGUL SYLLABLE MWAEH + {0xBAFC, 0xBAFC, prLV}, // Lo HANGUL SYLLABLE MOE + {0xBAFD, 0xBB17, prLVT}, // Lo [27] HANGUL SYLLABLE MOEG..HANGUL SYLLABLE MOEH + {0xBB18, 0xBB18, prLV}, // Lo HANGUL SYLLABLE MYO + {0xBB19, 0xBB33, prLVT}, // Lo [27] HANGUL SYLLABLE MYOG..HANGUL SYLLABLE MYOH + {0xBB34, 0xBB34, prLV}, // Lo HANGUL SYLLABLE MU + {0xBB35, 0xBB4F, prLVT}, // Lo [27] HANGUL SYLLABLE MUG..HANGUL SYLLABLE MUH + {0xBB50, 0xBB50, prLV}, // Lo HANGUL SYLLABLE MWEO + {0xBB51, 0xBB6B, prLVT}, // Lo [27] HANGUL SYLLABLE MWEOG..HANGUL SYLLABLE MWEOH + {0xBB6C, 0xBB6C, prLV}, // Lo HANGUL SYLLABLE MWE + {0xBB6D, 0xBB87, prLVT}, // Lo [27] HANGUL SYLLABLE MWEG..HANGUL SYLLABLE MWEH + {0xBB88, 0xBB88, prLV}, // Lo HANGUL SYLLABLE MWI + {0xBB89, 0xBBA3, prLVT}, // Lo [27] HANGUL SYLLABLE MWIG..HANGUL SYLLABLE MWIH + {0xBBA4, 0xBBA4, prLV}, // Lo HANGUL SYLLABLE MYU + {0xBBA5, 0xBBBF, prLVT}, // Lo [27] HANGUL SYLLABLE MYUG..HANGUL SYLLABLE MYUH + {0xBBC0, 0xBBC0, prLV}, // Lo HANGUL SYLLABLE MEU + {0xBBC1, 0xBBDB, prLVT}, // Lo [27] HANGUL SYLLABLE MEUG..HANGUL SYLLABLE MEUH + {0xBBDC, 0xBBDC, prLV}, // Lo HANGUL SYLLABLE MYI + {0xBBDD, 0xBBF7, prLVT}, // Lo [27] HANGUL SYLLABLE MYIG..HANGUL SYLLABLE MYIH + {0xBBF8, 0xBBF8, prLV}, // Lo HANGUL SYLLABLE MI + {0xBBF9, 0xBC13, prLVT}, // Lo [27] HANGUL SYLLABLE MIG..HANGUL SYLLABLE MIH + {0xBC14, 0xBC14, prLV}, // Lo HANGUL SYLLABLE BA + {0xBC15, 0xBC2F, prLVT}, // Lo [27] HANGUL SYLLABLE BAG..HANGUL SYLLABLE BAH + {0xBC30, 0xBC30, prLV}, // Lo HANGUL SYLLABLE BAE + {0xBC31, 0xBC4B, prLVT}, // Lo [27] HANGUL SYLLABLE BAEG..HANGUL SYLLABLE BAEH + {0xBC4C, 0xBC4C, prLV}, // Lo HANGUL SYLLABLE BYA + {0xBC4D, 0xBC67, prLVT}, // Lo [27] HANGUL SYLLABLE BYAG..HANGUL SYLLABLE BYAH + {0xBC68, 0xBC68, prLV}, // Lo HANGUL SYLLABLE BYAE + {0xBC69, 0xBC83, prLVT}, // Lo [27] HANGUL SYLLABLE BYAEG..HANGUL SYLLABLE BYAEH + {0xBC84, 0xBC84, prLV}, // Lo HANGUL SYLLABLE BEO + {0xBC85, 0xBC9F, prLVT}, // Lo [27] HANGUL SYLLABLE BEOG..HANGUL SYLLABLE BEOH + {0xBCA0, 0xBCA0, prLV}, // Lo HANGUL SYLLABLE BE + {0xBCA1, 0xBCBB, prLVT}, // Lo [27] HANGUL SYLLABLE BEG..HANGUL SYLLABLE BEH + {0xBCBC, 0xBCBC, prLV}, // Lo HANGUL SYLLABLE BYEO + {0xBCBD, 0xBCD7, prLVT}, // Lo [27] HANGUL SYLLABLE BYEOG..HANGUL SYLLABLE BYEOH + {0xBCD8, 0xBCD8, prLV}, // Lo HANGUL SYLLABLE BYE + {0xBCD9, 0xBCF3, prLVT}, // Lo [27] HANGUL SYLLABLE BYEG..HANGUL SYLLABLE BYEH + {0xBCF4, 0xBCF4, prLV}, // Lo HANGUL SYLLABLE BO + {0xBCF5, 0xBD0F, prLVT}, // Lo [27] HANGUL SYLLABLE BOG..HANGUL SYLLABLE BOH + {0xBD10, 0xBD10, prLV}, // Lo HANGUL SYLLABLE BWA + {0xBD11, 0xBD2B, prLVT}, // Lo [27] HANGUL SYLLABLE BWAG..HANGUL SYLLABLE BWAH + {0xBD2C, 0xBD2C, prLV}, // Lo HANGUL SYLLABLE BWAE + {0xBD2D, 0xBD47, prLVT}, // Lo [27] HANGUL SYLLABLE BWAEG..HANGUL SYLLABLE BWAEH + {0xBD48, 0xBD48, prLV}, // Lo HANGUL SYLLABLE BOE + {0xBD49, 0xBD63, prLVT}, // Lo [27] HANGUL SYLLABLE BOEG..HANGUL SYLLABLE BOEH + {0xBD64, 0xBD64, prLV}, // Lo HANGUL SYLLABLE BYO + {0xBD65, 0xBD7F, prLVT}, // Lo [27] HANGUL SYLLABLE BYOG..HANGUL SYLLABLE BYOH + {0xBD80, 0xBD80, prLV}, // Lo HANGUL SYLLABLE BU + {0xBD81, 0xBD9B, prLVT}, // Lo [27] HANGUL SYLLABLE BUG..HANGUL SYLLABLE BUH + {0xBD9C, 0xBD9C, prLV}, // Lo HANGUL SYLLABLE BWEO + {0xBD9D, 0xBDB7, prLVT}, // Lo [27] HANGUL SYLLABLE BWEOG..HANGUL SYLLABLE BWEOH + {0xBDB8, 0xBDB8, prLV}, // Lo HANGUL SYLLABLE BWE + {0xBDB9, 0xBDD3, prLVT}, // Lo [27] HANGUL SYLLABLE BWEG..HANGUL SYLLABLE BWEH + {0xBDD4, 0xBDD4, prLV}, // Lo HANGUL SYLLABLE BWI + {0xBDD5, 0xBDEF, prLVT}, // Lo [27] HANGUL SYLLABLE BWIG..HANGUL SYLLABLE BWIH + {0xBDF0, 0xBDF0, prLV}, // Lo HANGUL SYLLABLE BYU + {0xBDF1, 0xBE0B, prLVT}, // Lo [27] HANGUL SYLLABLE BYUG..HANGUL SYLLABLE BYUH + {0xBE0C, 0xBE0C, prLV}, // Lo HANGUL SYLLABLE BEU + {0xBE0D, 0xBE27, prLVT}, // Lo [27] HANGUL SYLLABLE BEUG..HANGUL SYLLABLE BEUH + {0xBE28, 0xBE28, prLV}, // Lo HANGUL SYLLABLE BYI + {0xBE29, 0xBE43, prLVT}, // Lo [27] HANGUL SYLLABLE BYIG..HANGUL SYLLABLE BYIH + {0xBE44, 0xBE44, prLV}, // Lo HANGUL SYLLABLE BI + {0xBE45, 0xBE5F, prLVT}, // Lo [27] HANGUL SYLLABLE BIG..HANGUL SYLLABLE BIH + {0xBE60, 0xBE60, prLV}, // Lo HANGUL SYLLABLE BBA + {0xBE61, 0xBE7B, prLVT}, // Lo [27] HANGUL SYLLABLE BBAG..HANGUL SYLLABLE BBAH + {0xBE7C, 0xBE7C, prLV}, // Lo HANGUL SYLLABLE BBAE + {0xBE7D, 0xBE97, prLVT}, // Lo [27] HANGUL SYLLABLE BBAEG..HANGUL SYLLABLE BBAEH + {0xBE98, 0xBE98, prLV}, // Lo HANGUL SYLLABLE BBYA + {0xBE99, 0xBEB3, prLVT}, // Lo [27] HANGUL SYLLABLE BBYAG..HANGUL SYLLABLE BBYAH + {0xBEB4, 0xBEB4, prLV}, // Lo HANGUL SYLLABLE BBYAE + {0xBEB5, 0xBECF, prLVT}, // Lo [27] HANGUL SYLLABLE BBYAEG..HANGUL SYLLABLE BBYAEH + {0xBED0, 0xBED0, prLV}, // Lo HANGUL SYLLABLE BBEO + {0xBED1, 0xBEEB, prLVT}, // Lo [27] HANGUL SYLLABLE BBEOG..HANGUL SYLLABLE BBEOH + {0xBEEC, 0xBEEC, prLV}, // Lo HANGUL SYLLABLE BBE + {0xBEED, 0xBF07, prLVT}, // Lo [27] HANGUL SYLLABLE BBEG..HANGUL SYLLABLE BBEH + {0xBF08, 0xBF08, prLV}, // Lo HANGUL SYLLABLE BBYEO + {0xBF09, 0xBF23, prLVT}, // Lo [27] HANGUL SYLLABLE BBYEOG..HANGUL SYLLABLE BBYEOH + {0xBF24, 0xBF24, prLV}, // Lo HANGUL SYLLABLE BBYE + {0xBF25, 0xBF3F, prLVT}, // Lo [27] HANGUL SYLLABLE BBYEG..HANGUL SYLLABLE BBYEH + {0xBF40, 0xBF40, prLV}, // Lo HANGUL SYLLABLE BBO + {0xBF41, 0xBF5B, prLVT}, // Lo [27] HANGUL SYLLABLE BBOG..HANGUL SYLLABLE BBOH + {0xBF5C, 0xBF5C, prLV}, // Lo HANGUL SYLLABLE BBWA + {0xBF5D, 0xBF77, prLVT}, // Lo [27] HANGUL SYLLABLE BBWAG..HANGUL SYLLABLE BBWAH + {0xBF78, 0xBF78, prLV}, // Lo HANGUL SYLLABLE BBWAE + {0xBF79, 0xBF93, prLVT}, // Lo [27] HANGUL SYLLABLE BBWAEG..HANGUL SYLLABLE BBWAEH + {0xBF94, 0xBF94, prLV}, // Lo HANGUL SYLLABLE BBOE + {0xBF95, 0xBFAF, prLVT}, // Lo [27] HANGUL SYLLABLE BBOEG..HANGUL SYLLABLE BBOEH + {0xBFB0, 0xBFB0, prLV}, // Lo HANGUL SYLLABLE BBYO + {0xBFB1, 0xBFCB, prLVT}, // Lo [27] HANGUL SYLLABLE BBYOG..HANGUL SYLLABLE BBYOH + {0xBFCC, 0xBFCC, prLV}, // Lo HANGUL SYLLABLE BBU + {0xBFCD, 0xBFE7, prLVT}, // Lo [27] HANGUL SYLLABLE BBUG..HANGUL SYLLABLE BBUH + {0xBFE8, 0xBFE8, prLV}, // Lo HANGUL SYLLABLE BBWEO + {0xBFE9, 0xC003, prLVT}, // Lo [27] HANGUL SYLLABLE BBWEOG..HANGUL SYLLABLE BBWEOH + {0xC004, 0xC004, prLV}, // Lo HANGUL SYLLABLE BBWE + {0xC005, 0xC01F, prLVT}, // Lo [27] HANGUL SYLLABLE BBWEG..HANGUL SYLLABLE BBWEH + {0xC020, 0xC020, prLV}, // Lo HANGUL SYLLABLE BBWI + {0xC021, 0xC03B, prLVT}, // Lo [27] HANGUL SYLLABLE BBWIG..HANGUL SYLLABLE BBWIH + {0xC03C, 0xC03C, prLV}, // Lo HANGUL SYLLABLE BBYU + {0xC03D, 0xC057, prLVT}, // Lo [27] HANGUL SYLLABLE BBYUG..HANGUL SYLLABLE BBYUH + {0xC058, 0xC058, prLV}, // Lo HANGUL SYLLABLE BBEU + {0xC059, 0xC073, prLVT}, // Lo [27] HANGUL SYLLABLE BBEUG..HANGUL SYLLABLE BBEUH + {0xC074, 0xC074, prLV}, // Lo HANGUL SYLLABLE BBYI + {0xC075, 0xC08F, prLVT}, // Lo [27] HANGUL SYLLABLE BBYIG..HANGUL SYLLABLE BBYIH + {0xC090, 0xC090, prLV}, // Lo HANGUL SYLLABLE BBI + {0xC091, 0xC0AB, prLVT}, // Lo [27] HANGUL SYLLABLE BBIG..HANGUL SYLLABLE BBIH + {0xC0AC, 0xC0AC, prLV}, // Lo HANGUL SYLLABLE SA + {0xC0AD, 0xC0C7, prLVT}, // Lo [27] HANGUL SYLLABLE SAG..HANGUL SYLLABLE SAH + {0xC0C8, 0xC0C8, prLV}, // Lo HANGUL SYLLABLE SAE + {0xC0C9, 0xC0E3, prLVT}, // Lo [27] HANGUL SYLLABLE SAEG..HANGUL SYLLABLE SAEH + {0xC0E4, 0xC0E4, prLV}, // Lo HANGUL SYLLABLE SYA + {0xC0E5, 0xC0FF, prLVT}, // Lo [27] HANGUL SYLLABLE SYAG..HANGUL SYLLABLE SYAH + {0xC100, 0xC100, prLV}, // Lo HANGUL SYLLABLE SYAE + {0xC101, 0xC11B, prLVT}, // Lo [27] HANGUL SYLLABLE SYAEG..HANGUL SYLLABLE SYAEH + {0xC11C, 0xC11C, prLV}, // Lo HANGUL SYLLABLE SEO + {0xC11D, 0xC137, prLVT}, // Lo [27] HANGUL SYLLABLE SEOG..HANGUL SYLLABLE SEOH + {0xC138, 0xC138, prLV}, // Lo HANGUL SYLLABLE SE + {0xC139, 0xC153, prLVT}, // Lo [27] HANGUL SYLLABLE SEG..HANGUL SYLLABLE SEH + {0xC154, 0xC154, prLV}, // Lo HANGUL SYLLABLE SYEO + {0xC155, 0xC16F, prLVT}, // Lo [27] HANGUL SYLLABLE SYEOG..HANGUL SYLLABLE SYEOH + {0xC170, 0xC170, prLV}, // Lo HANGUL SYLLABLE SYE + {0xC171, 0xC18B, prLVT}, // Lo [27] HANGUL SYLLABLE SYEG..HANGUL SYLLABLE SYEH + {0xC18C, 0xC18C, prLV}, // Lo HANGUL SYLLABLE SO + {0xC18D, 0xC1A7, prLVT}, // Lo [27] HANGUL SYLLABLE SOG..HANGUL SYLLABLE SOH + {0xC1A8, 0xC1A8, prLV}, // Lo HANGUL SYLLABLE SWA + {0xC1A9, 0xC1C3, prLVT}, // Lo [27] HANGUL SYLLABLE SWAG..HANGUL SYLLABLE SWAH + {0xC1C4, 0xC1C4, prLV}, // Lo HANGUL SYLLABLE SWAE + {0xC1C5, 0xC1DF, prLVT}, // Lo [27] HANGUL SYLLABLE SWAEG..HANGUL SYLLABLE SWAEH + {0xC1E0, 0xC1E0, prLV}, // Lo HANGUL SYLLABLE SOE + {0xC1E1, 0xC1FB, prLVT}, // Lo [27] HANGUL SYLLABLE SOEG..HANGUL SYLLABLE SOEH + {0xC1FC, 0xC1FC, prLV}, // Lo HANGUL SYLLABLE SYO + {0xC1FD, 0xC217, prLVT}, // Lo [27] HANGUL SYLLABLE SYOG..HANGUL SYLLABLE SYOH + {0xC218, 0xC218, prLV}, // Lo HANGUL SYLLABLE SU + {0xC219, 0xC233, prLVT}, // Lo [27] HANGUL SYLLABLE SUG..HANGUL SYLLABLE SUH + {0xC234, 0xC234, prLV}, // Lo HANGUL SYLLABLE SWEO + {0xC235, 0xC24F, prLVT}, // Lo [27] HANGUL SYLLABLE SWEOG..HANGUL SYLLABLE SWEOH + {0xC250, 0xC250, prLV}, // Lo HANGUL SYLLABLE SWE + {0xC251, 0xC26B, prLVT}, // Lo [27] HANGUL SYLLABLE SWEG..HANGUL SYLLABLE SWEH + {0xC26C, 0xC26C, prLV}, // Lo HANGUL SYLLABLE SWI + {0xC26D, 0xC287, prLVT}, // Lo [27] HANGUL SYLLABLE SWIG..HANGUL SYLLABLE SWIH + {0xC288, 0xC288, prLV}, // Lo HANGUL SYLLABLE SYU + {0xC289, 0xC2A3, prLVT}, // Lo [27] HANGUL SYLLABLE SYUG..HANGUL SYLLABLE SYUH + {0xC2A4, 0xC2A4, prLV}, // Lo HANGUL SYLLABLE SEU + {0xC2A5, 0xC2BF, prLVT}, // Lo [27] HANGUL SYLLABLE SEUG..HANGUL SYLLABLE SEUH + {0xC2C0, 0xC2C0, prLV}, // Lo HANGUL SYLLABLE SYI + {0xC2C1, 0xC2DB, prLVT}, // Lo [27] HANGUL SYLLABLE SYIG..HANGUL SYLLABLE SYIH + {0xC2DC, 0xC2DC, prLV}, // Lo HANGUL SYLLABLE SI + {0xC2DD, 0xC2F7, prLVT}, // Lo [27] HANGUL SYLLABLE SIG..HANGUL SYLLABLE SIH + {0xC2F8, 0xC2F8, prLV}, // Lo HANGUL SYLLABLE SSA + {0xC2F9, 0xC313, prLVT}, // Lo [27] HANGUL SYLLABLE SSAG..HANGUL SYLLABLE SSAH + {0xC314, 0xC314, prLV}, // Lo HANGUL SYLLABLE SSAE + {0xC315, 0xC32F, prLVT}, // Lo [27] HANGUL SYLLABLE SSAEG..HANGUL SYLLABLE SSAEH + {0xC330, 0xC330, prLV}, // Lo HANGUL SYLLABLE SSYA + {0xC331, 0xC34B, prLVT}, // Lo [27] HANGUL SYLLABLE SSYAG..HANGUL SYLLABLE SSYAH + {0xC34C, 0xC34C, prLV}, // Lo HANGUL SYLLABLE SSYAE + {0xC34D, 0xC367, prLVT}, // Lo [27] HANGUL SYLLABLE SSYAEG..HANGUL SYLLABLE SSYAEH + {0xC368, 0xC368, prLV}, // Lo HANGUL SYLLABLE SSEO + {0xC369, 0xC383, prLVT}, // Lo [27] HANGUL SYLLABLE SSEOG..HANGUL SYLLABLE SSEOH + {0xC384, 0xC384, prLV}, // Lo HANGUL SYLLABLE SSE + {0xC385, 0xC39F, prLVT}, // Lo [27] HANGUL SYLLABLE SSEG..HANGUL SYLLABLE SSEH + {0xC3A0, 0xC3A0, prLV}, // Lo HANGUL SYLLABLE SSYEO + {0xC3A1, 0xC3BB, prLVT}, // Lo [27] HANGUL SYLLABLE SSYEOG..HANGUL SYLLABLE SSYEOH + {0xC3BC, 0xC3BC, prLV}, // Lo HANGUL SYLLABLE SSYE + {0xC3BD, 0xC3D7, prLVT}, // Lo [27] HANGUL SYLLABLE SSYEG..HANGUL SYLLABLE SSYEH + {0xC3D8, 0xC3D8, prLV}, // Lo HANGUL SYLLABLE SSO + {0xC3D9, 0xC3F3, prLVT}, // Lo [27] HANGUL SYLLABLE SSOG..HANGUL SYLLABLE SSOH + {0xC3F4, 0xC3F4, prLV}, // Lo HANGUL SYLLABLE SSWA + {0xC3F5, 0xC40F, prLVT}, // Lo [27] HANGUL SYLLABLE SSWAG..HANGUL SYLLABLE SSWAH + {0xC410, 0xC410, prLV}, // Lo HANGUL SYLLABLE SSWAE + {0xC411, 0xC42B, prLVT}, // Lo [27] HANGUL SYLLABLE SSWAEG..HANGUL SYLLABLE SSWAEH + {0xC42C, 0xC42C, prLV}, // Lo HANGUL SYLLABLE SSOE + {0xC42D, 0xC447, prLVT}, // Lo [27] HANGUL SYLLABLE SSOEG..HANGUL SYLLABLE SSOEH + {0xC448, 0xC448, prLV}, // Lo HANGUL SYLLABLE SSYO + {0xC449, 0xC463, prLVT}, // Lo [27] HANGUL SYLLABLE SSYOG..HANGUL SYLLABLE SSYOH + {0xC464, 0xC464, prLV}, // Lo HANGUL SYLLABLE SSU + {0xC465, 0xC47F, prLVT}, // Lo [27] HANGUL SYLLABLE SSUG..HANGUL SYLLABLE SSUH + {0xC480, 0xC480, prLV}, // Lo HANGUL SYLLABLE SSWEO + {0xC481, 0xC49B, prLVT}, // Lo [27] HANGUL SYLLABLE SSWEOG..HANGUL SYLLABLE SSWEOH + {0xC49C, 0xC49C, prLV}, // Lo HANGUL SYLLABLE SSWE + {0xC49D, 0xC4B7, prLVT}, // Lo [27] HANGUL SYLLABLE SSWEG..HANGUL SYLLABLE SSWEH + {0xC4B8, 0xC4B8, prLV}, // Lo HANGUL SYLLABLE SSWI + {0xC4B9, 0xC4D3, prLVT}, // Lo [27] HANGUL SYLLABLE SSWIG..HANGUL SYLLABLE SSWIH + {0xC4D4, 0xC4D4, prLV}, // Lo HANGUL SYLLABLE SSYU + {0xC4D5, 0xC4EF, prLVT}, // Lo [27] HANGUL SYLLABLE SSYUG..HANGUL SYLLABLE SSYUH + {0xC4F0, 0xC4F0, prLV}, // Lo HANGUL SYLLABLE SSEU + {0xC4F1, 0xC50B, prLVT}, // Lo [27] HANGUL SYLLABLE SSEUG..HANGUL SYLLABLE SSEUH + {0xC50C, 0xC50C, prLV}, // Lo HANGUL SYLLABLE SSYI + {0xC50D, 0xC527, prLVT}, // Lo [27] HANGUL SYLLABLE SSYIG..HANGUL SYLLABLE SSYIH + {0xC528, 0xC528, prLV}, // Lo HANGUL SYLLABLE SSI + {0xC529, 0xC543, prLVT}, // Lo [27] HANGUL SYLLABLE SSIG..HANGUL SYLLABLE SSIH + {0xC544, 0xC544, prLV}, // Lo HANGUL SYLLABLE A + {0xC545, 0xC55F, prLVT}, // Lo [27] HANGUL SYLLABLE AG..HANGUL SYLLABLE AH + {0xC560, 0xC560, prLV}, // Lo HANGUL SYLLABLE AE + {0xC561, 0xC57B, prLVT}, // Lo [27] HANGUL SYLLABLE AEG..HANGUL SYLLABLE AEH + {0xC57C, 0xC57C, prLV}, // Lo HANGUL SYLLABLE YA + {0xC57D, 0xC597, prLVT}, // Lo [27] HANGUL SYLLABLE YAG..HANGUL SYLLABLE YAH + {0xC598, 0xC598, prLV}, // Lo HANGUL SYLLABLE YAE + {0xC599, 0xC5B3, prLVT}, // Lo [27] HANGUL SYLLABLE YAEG..HANGUL SYLLABLE YAEH + {0xC5B4, 0xC5B4, prLV}, // Lo HANGUL SYLLABLE EO + {0xC5B5, 0xC5CF, prLVT}, // Lo [27] HANGUL SYLLABLE EOG..HANGUL SYLLABLE EOH + {0xC5D0, 0xC5D0, prLV}, // Lo HANGUL SYLLABLE E + {0xC5D1, 0xC5EB, prLVT}, // Lo [27] HANGUL SYLLABLE EG..HANGUL SYLLABLE EH + {0xC5EC, 0xC5EC, prLV}, // Lo HANGUL SYLLABLE YEO + {0xC5ED, 0xC607, prLVT}, // Lo [27] HANGUL SYLLABLE YEOG..HANGUL SYLLABLE YEOH + {0xC608, 0xC608, prLV}, // Lo HANGUL SYLLABLE YE + {0xC609, 0xC623, prLVT}, // Lo [27] HANGUL SYLLABLE YEG..HANGUL SYLLABLE YEH + {0xC624, 0xC624, prLV}, // Lo HANGUL SYLLABLE O + {0xC625, 0xC63F, prLVT}, // Lo [27] HANGUL SYLLABLE OG..HANGUL SYLLABLE OH + {0xC640, 0xC640, prLV}, // Lo HANGUL SYLLABLE WA + {0xC641, 0xC65B, prLVT}, // Lo [27] HANGUL SYLLABLE WAG..HANGUL SYLLABLE WAH + {0xC65C, 0xC65C, prLV}, // Lo HANGUL SYLLABLE WAE + {0xC65D, 0xC677, prLVT}, // Lo [27] HANGUL SYLLABLE WAEG..HANGUL SYLLABLE WAEH + {0xC678, 0xC678, prLV}, // Lo HANGUL SYLLABLE OE + {0xC679, 0xC693, prLVT}, // Lo [27] HANGUL SYLLABLE OEG..HANGUL SYLLABLE OEH + {0xC694, 0xC694, prLV}, // Lo HANGUL SYLLABLE YO + {0xC695, 0xC6AF, prLVT}, // Lo [27] HANGUL SYLLABLE YOG..HANGUL SYLLABLE YOH + {0xC6B0, 0xC6B0, prLV}, // Lo HANGUL SYLLABLE U + {0xC6B1, 0xC6CB, prLVT}, // Lo [27] HANGUL SYLLABLE UG..HANGUL SYLLABLE UH + {0xC6CC, 0xC6CC, prLV}, // Lo HANGUL SYLLABLE WEO + {0xC6CD, 0xC6E7, prLVT}, // Lo [27] HANGUL SYLLABLE WEOG..HANGUL SYLLABLE WEOH + {0xC6E8, 0xC6E8, prLV}, // Lo HANGUL SYLLABLE WE + {0xC6E9, 0xC703, prLVT}, // Lo [27] HANGUL SYLLABLE WEG..HANGUL SYLLABLE WEH + {0xC704, 0xC704, prLV}, // Lo HANGUL SYLLABLE WI + {0xC705, 0xC71F, prLVT}, // Lo [27] HANGUL SYLLABLE WIG..HANGUL SYLLABLE WIH + {0xC720, 0xC720, prLV}, // Lo HANGUL SYLLABLE YU + {0xC721, 0xC73B, prLVT}, // Lo [27] HANGUL SYLLABLE YUG..HANGUL SYLLABLE YUH + {0xC73C, 0xC73C, prLV}, // Lo HANGUL SYLLABLE EU + {0xC73D, 0xC757, prLVT}, // Lo [27] HANGUL SYLLABLE EUG..HANGUL SYLLABLE EUH + {0xC758, 0xC758, prLV}, // Lo HANGUL SYLLABLE YI + {0xC759, 0xC773, prLVT}, // Lo [27] HANGUL SYLLABLE YIG..HANGUL SYLLABLE YIH + {0xC774, 0xC774, prLV}, // Lo HANGUL SYLLABLE I + {0xC775, 0xC78F, prLVT}, // Lo [27] HANGUL SYLLABLE IG..HANGUL SYLLABLE IH + {0xC790, 0xC790, prLV}, // Lo HANGUL SYLLABLE JA + {0xC791, 0xC7AB, prLVT}, // Lo [27] HANGUL SYLLABLE JAG..HANGUL SYLLABLE JAH + {0xC7AC, 0xC7AC, prLV}, // Lo HANGUL SYLLABLE JAE + {0xC7AD, 0xC7C7, prLVT}, // Lo [27] HANGUL SYLLABLE JAEG..HANGUL SYLLABLE JAEH + {0xC7C8, 0xC7C8, prLV}, // Lo HANGUL SYLLABLE JYA + {0xC7C9, 0xC7E3, prLVT}, // Lo [27] HANGUL SYLLABLE JYAG..HANGUL SYLLABLE JYAH + {0xC7E4, 0xC7E4, prLV}, // Lo HANGUL SYLLABLE JYAE + {0xC7E5, 0xC7FF, prLVT}, // Lo [27] HANGUL SYLLABLE JYAEG..HANGUL SYLLABLE JYAEH + {0xC800, 0xC800, prLV}, // Lo HANGUL SYLLABLE JEO + {0xC801, 0xC81B, prLVT}, // Lo [27] HANGUL SYLLABLE JEOG..HANGUL SYLLABLE JEOH + {0xC81C, 0xC81C, prLV}, // Lo HANGUL SYLLABLE JE + {0xC81D, 0xC837, prLVT}, // Lo [27] HANGUL SYLLABLE JEG..HANGUL SYLLABLE JEH + {0xC838, 0xC838, prLV}, // Lo HANGUL SYLLABLE JYEO + {0xC839, 0xC853, prLVT}, // Lo [27] HANGUL SYLLABLE JYEOG..HANGUL SYLLABLE JYEOH + {0xC854, 0xC854, prLV}, // Lo HANGUL SYLLABLE JYE + {0xC855, 0xC86F, prLVT}, // Lo [27] HANGUL SYLLABLE JYEG..HANGUL SYLLABLE JYEH + {0xC870, 0xC870, prLV}, // Lo HANGUL SYLLABLE JO + {0xC871, 0xC88B, prLVT}, // Lo [27] HANGUL SYLLABLE JOG..HANGUL SYLLABLE JOH + {0xC88C, 0xC88C, prLV}, // Lo HANGUL SYLLABLE JWA + {0xC88D, 0xC8A7, prLVT}, // Lo [27] HANGUL SYLLABLE JWAG..HANGUL SYLLABLE JWAH + {0xC8A8, 0xC8A8, prLV}, // Lo HANGUL SYLLABLE JWAE + {0xC8A9, 0xC8C3, prLVT}, // Lo [27] HANGUL SYLLABLE JWAEG..HANGUL SYLLABLE JWAEH + {0xC8C4, 0xC8C4, prLV}, // Lo HANGUL SYLLABLE JOE + {0xC8C5, 0xC8DF, prLVT}, // Lo [27] HANGUL SYLLABLE JOEG..HANGUL SYLLABLE JOEH + {0xC8E0, 0xC8E0, prLV}, // Lo HANGUL SYLLABLE JYO + {0xC8E1, 0xC8FB, prLVT}, // Lo [27] HANGUL SYLLABLE JYOG..HANGUL SYLLABLE JYOH + {0xC8FC, 0xC8FC, prLV}, // Lo HANGUL SYLLABLE JU + {0xC8FD, 0xC917, prLVT}, // Lo [27] HANGUL SYLLABLE JUG..HANGUL SYLLABLE JUH + {0xC918, 0xC918, prLV}, // Lo HANGUL SYLLABLE JWEO + {0xC919, 0xC933, prLVT}, // Lo [27] HANGUL SYLLABLE JWEOG..HANGUL SYLLABLE JWEOH + {0xC934, 0xC934, prLV}, // Lo HANGUL SYLLABLE JWE + {0xC935, 0xC94F, prLVT}, // Lo [27] HANGUL SYLLABLE JWEG..HANGUL SYLLABLE JWEH + {0xC950, 0xC950, prLV}, // Lo HANGUL SYLLABLE JWI + {0xC951, 0xC96B, prLVT}, // Lo [27] HANGUL SYLLABLE JWIG..HANGUL SYLLABLE JWIH + {0xC96C, 0xC96C, prLV}, // Lo HANGUL SYLLABLE JYU + {0xC96D, 0xC987, prLVT}, // Lo [27] HANGUL SYLLABLE JYUG..HANGUL SYLLABLE JYUH + {0xC988, 0xC988, prLV}, // Lo HANGUL SYLLABLE JEU + {0xC989, 0xC9A3, prLVT}, // Lo [27] HANGUL SYLLABLE JEUG..HANGUL SYLLABLE JEUH + {0xC9A4, 0xC9A4, prLV}, // Lo HANGUL SYLLABLE JYI + {0xC9A5, 0xC9BF, prLVT}, // Lo [27] HANGUL SYLLABLE JYIG..HANGUL SYLLABLE JYIH + {0xC9C0, 0xC9C0, prLV}, // Lo HANGUL SYLLABLE JI + {0xC9C1, 0xC9DB, prLVT}, // Lo [27] HANGUL SYLLABLE JIG..HANGUL SYLLABLE JIH + {0xC9DC, 0xC9DC, prLV}, // Lo HANGUL SYLLABLE JJA + {0xC9DD, 0xC9F7, prLVT}, // Lo [27] HANGUL SYLLABLE JJAG..HANGUL SYLLABLE JJAH + {0xC9F8, 0xC9F8, prLV}, // Lo HANGUL SYLLABLE JJAE + {0xC9F9, 0xCA13, prLVT}, // Lo [27] HANGUL SYLLABLE JJAEG..HANGUL SYLLABLE JJAEH + {0xCA14, 0xCA14, prLV}, // Lo HANGUL SYLLABLE JJYA + {0xCA15, 0xCA2F, prLVT}, // Lo [27] HANGUL SYLLABLE JJYAG..HANGUL SYLLABLE JJYAH + {0xCA30, 0xCA30, prLV}, // Lo HANGUL SYLLABLE JJYAE + {0xCA31, 0xCA4B, prLVT}, // Lo [27] HANGUL SYLLABLE JJYAEG..HANGUL SYLLABLE JJYAEH + {0xCA4C, 0xCA4C, prLV}, // Lo HANGUL SYLLABLE JJEO + {0xCA4D, 0xCA67, prLVT}, // Lo [27] HANGUL SYLLABLE JJEOG..HANGUL SYLLABLE JJEOH + {0xCA68, 0xCA68, prLV}, // Lo HANGUL SYLLABLE JJE + {0xCA69, 0xCA83, prLVT}, // Lo [27] HANGUL SYLLABLE JJEG..HANGUL SYLLABLE JJEH + {0xCA84, 0xCA84, prLV}, // Lo HANGUL SYLLABLE JJYEO + {0xCA85, 0xCA9F, prLVT}, // Lo [27] HANGUL SYLLABLE JJYEOG..HANGUL SYLLABLE JJYEOH + {0xCAA0, 0xCAA0, prLV}, // Lo HANGUL SYLLABLE JJYE + {0xCAA1, 0xCABB, prLVT}, // Lo [27] HANGUL SYLLABLE JJYEG..HANGUL SYLLABLE JJYEH + {0xCABC, 0xCABC, prLV}, // Lo HANGUL SYLLABLE JJO + {0xCABD, 0xCAD7, prLVT}, // Lo [27] HANGUL SYLLABLE JJOG..HANGUL SYLLABLE JJOH + {0xCAD8, 0xCAD8, prLV}, // Lo HANGUL SYLLABLE JJWA + {0xCAD9, 0xCAF3, prLVT}, // Lo [27] HANGUL SYLLABLE JJWAG..HANGUL SYLLABLE JJWAH + {0xCAF4, 0xCAF4, prLV}, // Lo HANGUL SYLLABLE JJWAE + {0xCAF5, 0xCB0F, prLVT}, // Lo [27] HANGUL SYLLABLE JJWAEG..HANGUL SYLLABLE JJWAEH + {0xCB10, 0xCB10, prLV}, // Lo HANGUL SYLLABLE JJOE + {0xCB11, 0xCB2B, prLVT}, // Lo [27] HANGUL SYLLABLE JJOEG..HANGUL SYLLABLE JJOEH + {0xCB2C, 0xCB2C, prLV}, // Lo HANGUL SYLLABLE JJYO + {0xCB2D, 0xCB47, prLVT}, // Lo [27] HANGUL SYLLABLE JJYOG..HANGUL SYLLABLE JJYOH + {0xCB48, 0xCB48, prLV}, // Lo HANGUL SYLLABLE JJU + {0xCB49, 0xCB63, prLVT}, // Lo [27] HANGUL SYLLABLE JJUG..HANGUL SYLLABLE JJUH + {0xCB64, 0xCB64, prLV}, // Lo HANGUL SYLLABLE JJWEO + {0xCB65, 0xCB7F, prLVT}, // Lo [27] HANGUL SYLLABLE JJWEOG..HANGUL SYLLABLE JJWEOH + {0xCB80, 0xCB80, prLV}, // Lo HANGUL SYLLABLE JJWE + {0xCB81, 0xCB9B, prLVT}, // Lo [27] HANGUL SYLLABLE JJWEG..HANGUL SYLLABLE JJWEH + {0xCB9C, 0xCB9C, prLV}, // Lo HANGUL SYLLABLE JJWI + {0xCB9D, 0xCBB7, prLVT}, // Lo [27] HANGUL SYLLABLE JJWIG..HANGUL SYLLABLE JJWIH + {0xCBB8, 0xCBB8, prLV}, // Lo HANGUL SYLLABLE JJYU + {0xCBB9, 0xCBD3, prLVT}, // Lo [27] HANGUL SYLLABLE JJYUG..HANGUL SYLLABLE JJYUH + {0xCBD4, 0xCBD4, prLV}, // Lo HANGUL SYLLABLE JJEU + {0xCBD5, 0xCBEF, prLVT}, // Lo [27] HANGUL SYLLABLE JJEUG..HANGUL SYLLABLE JJEUH + {0xCBF0, 0xCBF0, prLV}, // Lo HANGUL SYLLABLE JJYI + {0xCBF1, 0xCC0B, prLVT}, // Lo [27] HANGUL SYLLABLE JJYIG..HANGUL SYLLABLE JJYIH + {0xCC0C, 0xCC0C, prLV}, // Lo HANGUL SYLLABLE JJI + {0xCC0D, 0xCC27, prLVT}, // Lo [27] HANGUL SYLLABLE JJIG..HANGUL SYLLABLE JJIH + {0xCC28, 0xCC28, prLV}, // Lo HANGUL SYLLABLE CA + {0xCC29, 0xCC43, prLVT}, // Lo [27] HANGUL SYLLABLE CAG..HANGUL SYLLABLE CAH + {0xCC44, 0xCC44, prLV}, // Lo HANGUL SYLLABLE CAE + {0xCC45, 0xCC5F, prLVT}, // Lo [27] HANGUL SYLLABLE CAEG..HANGUL SYLLABLE CAEH + {0xCC60, 0xCC60, prLV}, // Lo HANGUL SYLLABLE CYA + {0xCC61, 0xCC7B, prLVT}, // Lo [27] HANGUL SYLLABLE CYAG..HANGUL SYLLABLE CYAH + {0xCC7C, 0xCC7C, prLV}, // Lo HANGUL SYLLABLE CYAE + {0xCC7D, 0xCC97, prLVT}, // Lo [27] HANGUL SYLLABLE CYAEG..HANGUL SYLLABLE CYAEH + {0xCC98, 0xCC98, prLV}, // Lo HANGUL SYLLABLE CEO + {0xCC99, 0xCCB3, prLVT}, // Lo [27] HANGUL SYLLABLE CEOG..HANGUL SYLLABLE CEOH + {0xCCB4, 0xCCB4, prLV}, // Lo HANGUL SYLLABLE CE + {0xCCB5, 0xCCCF, prLVT}, // Lo [27] HANGUL SYLLABLE CEG..HANGUL SYLLABLE CEH + {0xCCD0, 0xCCD0, prLV}, // Lo HANGUL SYLLABLE CYEO + {0xCCD1, 0xCCEB, prLVT}, // Lo [27] HANGUL SYLLABLE CYEOG..HANGUL SYLLABLE CYEOH + {0xCCEC, 0xCCEC, prLV}, // Lo HANGUL SYLLABLE CYE + {0xCCED, 0xCD07, prLVT}, // Lo [27] HANGUL SYLLABLE CYEG..HANGUL SYLLABLE CYEH + {0xCD08, 0xCD08, prLV}, // Lo HANGUL SYLLABLE CO + {0xCD09, 0xCD23, prLVT}, // Lo [27] HANGUL SYLLABLE COG..HANGUL SYLLABLE COH + {0xCD24, 0xCD24, prLV}, // Lo HANGUL SYLLABLE CWA + {0xCD25, 0xCD3F, prLVT}, // Lo [27] HANGUL SYLLABLE CWAG..HANGUL SYLLABLE CWAH + {0xCD40, 0xCD40, prLV}, // Lo HANGUL SYLLABLE CWAE + {0xCD41, 0xCD5B, prLVT}, // Lo [27] HANGUL SYLLABLE CWAEG..HANGUL SYLLABLE CWAEH + {0xCD5C, 0xCD5C, prLV}, // Lo HANGUL SYLLABLE COE + {0xCD5D, 0xCD77, prLVT}, // Lo [27] HANGUL SYLLABLE COEG..HANGUL SYLLABLE COEH + {0xCD78, 0xCD78, prLV}, // Lo HANGUL SYLLABLE CYO + {0xCD79, 0xCD93, prLVT}, // Lo [27] HANGUL SYLLABLE CYOG..HANGUL SYLLABLE CYOH + {0xCD94, 0xCD94, prLV}, // Lo HANGUL SYLLABLE CU + {0xCD95, 0xCDAF, prLVT}, // Lo [27] HANGUL SYLLABLE CUG..HANGUL SYLLABLE CUH + {0xCDB0, 0xCDB0, prLV}, // Lo HANGUL SYLLABLE CWEO + {0xCDB1, 0xCDCB, prLVT}, // Lo [27] HANGUL SYLLABLE CWEOG..HANGUL SYLLABLE CWEOH + {0xCDCC, 0xCDCC, prLV}, // Lo HANGUL SYLLABLE CWE + {0xCDCD, 0xCDE7, prLVT}, // Lo [27] HANGUL SYLLABLE CWEG..HANGUL SYLLABLE CWEH + {0xCDE8, 0xCDE8, prLV}, // Lo HANGUL SYLLABLE CWI + {0xCDE9, 0xCE03, prLVT}, // Lo [27] HANGUL SYLLABLE CWIG..HANGUL SYLLABLE CWIH + {0xCE04, 0xCE04, prLV}, // Lo HANGUL SYLLABLE CYU + {0xCE05, 0xCE1F, prLVT}, // Lo [27] HANGUL SYLLABLE CYUG..HANGUL SYLLABLE CYUH + {0xCE20, 0xCE20, prLV}, // Lo HANGUL SYLLABLE CEU + {0xCE21, 0xCE3B, prLVT}, // Lo [27] HANGUL SYLLABLE CEUG..HANGUL SYLLABLE CEUH + {0xCE3C, 0xCE3C, prLV}, // Lo HANGUL SYLLABLE CYI + {0xCE3D, 0xCE57, prLVT}, // Lo [27] HANGUL SYLLABLE CYIG..HANGUL SYLLABLE CYIH + {0xCE58, 0xCE58, prLV}, // Lo HANGUL SYLLABLE CI + {0xCE59, 0xCE73, prLVT}, // Lo [27] HANGUL SYLLABLE CIG..HANGUL SYLLABLE CIH + {0xCE74, 0xCE74, prLV}, // Lo HANGUL SYLLABLE KA + {0xCE75, 0xCE8F, prLVT}, // Lo [27] HANGUL SYLLABLE KAG..HANGUL SYLLABLE KAH + {0xCE90, 0xCE90, prLV}, // Lo HANGUL SYLLABLE KAE + {0xCE91, 0xCEAB, prLVT}, // Lo [27] HANGUL SYLLABLE KAEG..HANGUL SYLLABLE KAEH + {0xCEAC, 0xCEAC, prLV}, // Lo HANGUL SYLLABLE KYA + {0xCEAD, 0xCEC7, prLVT}, // Lo [27] HANGUL SYLLABLE KYAG..HANGUL SYLLABLE KYAH + {0xCEC8, 0xCEC8, prLV}, // Lo HANGUL SYLLABLE KYAE + {0xCEC9, 0xCEE3, prLVT}, // Lo [27] HANGUL SYLLABLE KYAEG..HANGUL SYLLABLE KYAEH + {0xCEE4, 0xCEE4, prLV}, // Lo HANGUL SYLLABLE KEO + {0xCEE5, 0xCEFF, prLVT}, // Lo [27] HANGUL SYLLABLE KEOG..HANGUL SYLLABLE KEOH + {0xCF00, 0xCF00, prLV}, // Lo HANGUL SYLLABLE KE + {0xCF01, 0xCF1B, prLVT}, // Lo [27] HANGUL SYLLABLE KEG..HANGUL SYLLABLE KEH + {0xCF1C, 0xCF1C, prLV}, // Lo HANGUL SYLLABLE KYEO + {0xCF1D, 0xCF37, prLVT}, // Lo [27] HANGUL SYLLABLE KYEOG..HANGUL SYLLABLE KYEOH + {0xCF38, 0xCF38, prLV}, // Lo HANGUL SYLLABLE KYE + {0xCF39, 0xCF53, prLVT}, // Lo [27] HANGUL SYLLABLE KYEG..HANGUL SYLLABLE KYEH + {0xCF54, 0xCF54, prLV}, // Lo HANGUL SYLLABLE KO + {0xCF55, 0xCF6F, prLVT}, // Lo [27] HANGUL SYLLABLE KOG..HANGUL SYLLABLE KOH + {0xCF70, 0xCF70, prLV}, // Lo HANGUL SYLLABLE KWA + {0xCF71, 0xCF8B, prLVT}, // Lo [27] HANGUL SYLLABLE KWAG..HANGUL SYLLABLE KWAH + {0xCF8C, 0xCF8C, prLV}, // Lo HANGUL SYLLABLE KWAE + {0xCF8D, 0xCFA7, prLVT}, // Lo [27] HANGUL SYLLABLE KWAEG..HANGUL SYLLABLE KWAEH + {0xCFA8, 0xCFA8, prLV}, // Lo HANGUL SYLLABLE KOE + {0xCFA9, 0xCFC3, prLVT}, // Lo [27] HANGUL SYLLABLE KOEG..HANGUL SYLLABLE KOEH + {0xCFC4, 0xCFC4, prLV}, // Lo HANGUL SYLLABLE KYO + {0xCFC5, 0xCFDF, prLVT}, // Lo [27] HANGUL SYLLABLE KYOG..HANGUL SYLLABLE KYOH + {0xCFE0, 0xCFE0, prLV}, // Lo HANGUL SYLLABLE KU + {0xCFE1, 0xCFFB, prLVT}, // Lo [27] HANGUL SYLLABLE KUG..HANGUL SYLLABLE KUH + {0xCFFC, 0xCFFC, prLV}, // Lo HANGUL SYLLABLE KWEO + {0xCFFD, 0xD017, prLVT}, // Lo [27] HANGUL SYLLABLE KWEOG..HANGUL SYLLABLE KWEOH + {0xD018, 0xD018, prLV}, // Lo HANGUL SYLLABLE KWE + {0xD019, 0xD033, prLVT}, // Lo [27] HANGUL SYLLABLE KWEG..HANGUL SYLLABLE KWEH + {0xD034, 0xD034, prLV}, // Lo HANGUL SYLLABLE KWI + {0xD035, 0xD04F, prLVT}, // Lo [27] HANGUL SYLLABLE KWIG..HANGUL SYLLABLE KWIH + {0xD050, 0xD050, prLV}, // Lo HANGUL SYLLABLE KYU + {0xD051, 0xD06B, prLVT}, // Lo [27] HANGUL SYLLABLE KYUG..HANGUL SYLLABLE KYUH + {0xD06C, 0xD06C, prLV}, // Lo HANGUL SYLLABLE KEU + {0xD06D, 0xD087, prLVT}, // Lo [27] HANGUL SYLLABLE KEUG..HANGUL SYLLABLE KEUH + {0xD088, 0xD088, prLV}, // Lo HANGUL SYLLABLE KYI + {0xD089, 0xD0A3, prLVT}, // Lo [27] HANGUL SYLLABLE KYIG..HANGUL SYLLABLE KYIH + {0xD0A4, 0xD0A4, prLV}, // Lo HANGUL SYLLABLE KI + {0xD0A5, 0xD0BF, prLVT}, // Lo [27] HANGUL SYLLABLE KIG..HANGUL SYLLABLE KIH + {0xD0C0, 0xD0C0, prLV}, // Lo HANGUL SYLLABLE TA + {0xD0C1, 0xD0DB, prLVT}, // Lo [27] HANGUL SYLLABLE TAG..HANGUL SYLLABLE TAH + {0xD0DC, 0xD0DC, prLV}, // Lo HANGUL SYLLABLE TAE + {0xD0DD, 0xD0F7, prLVT}, // Lo [27] HANGUL SYLLABLE TAEG..HANGUL SYLLABLE TAEH + {0xD0F8, 0xD0F8, prLV}, // Lo HANGUL SYLLABLE TYA + {0xD0F9, 0xD113, prLVT}, // Lo [27] HANGUL SYLLABLE TYAG..HANGUL SYLLABLE TYAH + {0xD114, 0xD114, prLV}, // Lo HANGUL SYLLABLE TYAE + {0xD115, 0xD12F, prLVT}, // Lo [27] HANGUL SYLLABLE TYAEG..HANGUL SYLLABLE TYAEH + {0xD130, 0xD130, prLV}, // Lo HANGUL SYLLABLE TEO + {0xD131, 0xD14B, prLVT}, // Lo [27] HANGUL SYLLABLE TEOG..HANGUL SYLLABLE TEOH + {0xD14C, 0xD14C, prLV}, // Lo HANGUL SYLLABLE TE + {0xD14D, 0xD167, prLVT}, // Lo [27] HANGUL SYLLABLE TEG..HANGUL SYLLABLE TEH + {0xD168, 0xD168, prLV}, // Lo HANGUL SYLLABLE TYEO + {0xD169, 0xD183, prLVT}, // Lo [27] HANGUL SYLLABLE TYEOG..HANGUL SYLLABLE TYEOH + {0xD184, 0xD184, prLV}, // Lo HANGUL SYLLABLE TYE + {0xD185, 0xD19F, prLVT}, // Lo [27] HANGUL SYLLABLE TYEG..HANGUL SYLLABLE TYEH + {0xD1A0, 0xD1A0, prLV}, // Lo HANGUL SYLLABLE TO + {0xD1A1, 0xD1BB, prLVT}, // Lo [27] HANGUL SYLLABLE TOG..HANGUL SYLLABLE TOH + {0xD1BC, 0xD1BC, prLV}, // Lo HANGUL SYLLABLE TWA + {0xD1BD, 0xD1D7, prLVT}, // Lo [27] HANGUL SYLLABLE TWAG..HANGUL SYLLABLE TWAH + {0xD1D8, 0xD1D8, prLV}, // Lo HANGUL SYLLABLE TWAE + {0xD1D9, 0xD1F3, prLVT}, // Lo [27] HANGUL SYLLABLE TWAEG..HANGUL SYLLABLE TWAEH + {0xD1F4, 0xD1F4, prLV}, // Lo HANGUL SYLLABLE TOE + {0xD1F5, 0xD20F, prLVT}, // Lo [27] HANGUL SYLLABLE TOEG..HANGUL SYLLABLE TOEH + {0xD210, 0xD210, prLV}, // Lo HANGUL SYLLABLE TYO + {0xD211, 0xD22B, prLVT}, // Lo [27] HANGUL SYLLABLE TYOG..HANGUL SYLLABLE TYOH + {0xD22C, 0xD22C, prLV}, // Lo HANGUL SYLLABLE TU + {0xD22D, 0xD247, prLVT}, // Lo [27] HANGUL SYLLABLE TUG..HANGUL SYLLABLE TUH + {0xD248, 0xD248, prLV}, // Lo HANGUL SYLLABLE TWEO + {0xD249, 0xD263, prLVT}, // Lo [27] HANGUL SYLLABLE TWEOG..HANGUL SYLLABLE TWEOH + {0xD264, 0xD264, prLV}, // Lo HANGUL SYLLABLE TWE + {0xD265, 0xD27F, prLVT}, // Lo [27] HANGUL SYLLABLE TWEG..HANGUL SYLLABLE TWEH + {0xD280, 0xD280, prLV}, // Lo HANGUL SYLLABLE TWI + {0xD281, 0xD29B, prLVT}, // Lo [27] HANGUL SYLLABLE TWIG..HANGUL SYLLABLE TWIH + {0xD29C, 0xD29C, prLV}, // Lo HANGUL SYLLABLE TYU + {0xD29D, 0xD2B7, prLVT}, // Lo [27] HANGUL SYLLABLE TYUG..HANGUL SYLLABLE TYUH + {0xD2B8, 0xD2B8, prLV}, // Lo HANGUL SYLLABLE TEU + {0xD2B9, 0xD2D3, prLVT}, // Lo [27] HANGUL SYLLABLE TEUG..HANGUL SYLLABLE TEUH + {0xD2D4, 0xD2D4, prLV}, // Lo HANGUL SYLLABLE TYI + {0xD2D5, 0xD2EF, prLVT}, // Lo [27] HANGUL SYLLABLE TYIG..HANGUL SYLLABLE TYIH + {0xD2F0, 0xD2F0, prLV}, // Lo HANGUL SYLLABLE TI + {0xD2F1, 0xD30B, prLVT}, // Lo [27] HANGUL SYLLABLE TIG..HANGUL SYLLABLE TIH + {0xD30C, 0xD30C, prLV}, // Lo HANGUL SYLLABLE PA + {0xD30D, 0xD327, prLVT}, // Lo [27] HANGUL SYLLABLE PAG..HANGUL SYLLABLE PAH + {0xD328, 0xD328, prLV}, // Lo HANGUL SYLLABLE PAE + {0xD329, 0xD343, prLVT}, // Lo [27] HANGUL SYLLABLE PAEG..HANGUL SYLLABLE PAEH + {0xD344, 0xD344, prLV}, // Lo HANGUL SYLLABLE PYA + {0xD345, 0xD35F, prLVT}, // Lo [27] HANGUL SYLLABLE PYAG..HANGUL SYLLABLE PYAH + {0xD360, 0xD360, prLV}, // Lo HANGUL SYLLABLE PYAE + {0xD361, 0xD37B, prLVT}, // Lo [27] HANGUL SYLLABLE PYAEG..HANGUL SYLLABLE PYAEH + {0xD37C, 0xD37C, prLV}, // Lo HANGUL SYLLABLE PEO + {0xD37D, 0xD397, prLVT}, // Lo [27] HANGUL SYLLABLE PEOG..HANGUL SYLLABLE PEOH + {0xD398, 0xD398, prLV}, // Lo HANGUL SYLLABLE PE + {0xD399, 0xD3B3, prLVT}, // Lo [27] HANGUL SYLLABLE PEG..HANGUL SYLLABLE PEH + {0xD3B4, 0xD3B4, prLV}, // Lo HANGUL SYLLABLE PYEO + {0xD3B5, 0xD3CF, prLVT}, // Lo [27] HANGUL SYLLABLE PYEOG..HANGUL SYLLABLE PYEOH + {0xD3D0, 0xD3D0, prLV}, // Lo HANGUL SYLLABLE PYE + {0xD3D1, 0xD3EB, prLVT}, // Lo [27] HANGUL SYLLABLE PYEG..HANGUL SYLLABLE PYEH + {0xD3EC, 0xD3EC, prLV}, // Lo HANGUL SYLLABLE PO + {0xD3ED, 0xD407, prLVT}, // Lo [27] HANGUL SYLLABLE POG..HANGUL SYLLABLE POH + {0xD408, 0xD408, prLV}, // Lo HANGUL SYLLABLE PWA + {0xD409, 0xD423, prLVT}, // Lo [27] HANGUL SYLLABLE PWAG..HANGUL SYLLABLE PWAH + {0xD424, 0xD424, prLV}, // Lo HANGUL SYLLABLE PWAE + {0xD425, 0xD43F, prLVT}, // Lo [27] HANGUL SYLLABLE PWAEG..HANGUL SYLLABLE PWAEH + {0xD440, 0xD440, prLV}, // Lo HANGUL SYLLABLE POE + {0xD441, 0xD45B, prLVT}, // Lo [27] HANGUL SYLLABLE POEG..HANGUL SYLLABLE POEH + {0xD45C, 0xD45C, prLV}, // Lo HANGUL SYLLABLE PYO + {0xD45D, 0xD477, prLVT}, // Lo [27] HANGUL SYLLABLE PYOG..HANGUL SYLLABLE PYOH + {0xD478, 0xD478, prLV}, // Lo HANGUL SYLLABLE PU + {0xD479, 0xD493, prLVT}, // Lo [27] HANGUL SYLLABLE PUG..HANGUL SYLLABLE PUH + {0xD494, 0xD494, prLV}, // Lo HANGUL SYLLABLE PWEO + {0xD495, 0xD4AF, prLVT}, // Lo [27] HANGUL SYLLABLE PWEOG..HANGUL SYLLABLE PWEOH + {0xD4B0, 0xD4B0, prLV}, // Lo HANGUL SYLLABLE PWE + {0xD4B1, 0xD4CB, prLVT}, // Lo [27] HANGUL SYLLABLE PWEG..HANGUL SYLLABLE PWEH + {0xD4CC, 0xD4CC, prLV}, // Lo HANGUL SYLLABLE PWI + {0xD4CD, 0xD4E7, prLVT}, // Lo [27] HANGUL SYLLABLE PWIG..HANGUL SYLLABLE PWIH + {0xD4E8, 0xD4E8, prLV}, // Lo HANGUL SYLLABLE PYU + {0xD4E9, 0xD503, prLVT}, // Lo [27] HANGUL SYLLABLE PYUG..HANGUL SYLLABLE PYUH + {0xD504, 0xD504, prLV}, // Lo HANGUL SYLLABLE PEU + {0xD505, 0xD51F, prLVT}, // Lo [27] HANGUL SYLLABLE PEUG..HANGUL SYLLABLE PEUH + {0xD520, 0xD520, prLV}, // Lo HANGUL SYLLABLE PYI + {0xD521, 0xD53B, prLVT}, // Lo [27] HANGUL SYLLABLE PYIG..HANGUL SYLLABLE PYIH + {0xD53C, 0xD53C, prLV}, // Lo HANGUL SYLLABLE PI + {0xD53D, 0xD557, prLVT}, // Lo [27] HANGUL SYLLABLE PIG..HANGUL SYLLABLE PIH + {0xD558, 0xD558, prLV}, // Lo HANGUL SYLLABLE HA + {0xD559, 0xD573, prLVT}, // Lo [27] HANGUL SYLLABLE HAG..HANGUL SYLLABLE HAH + {0xD574, 0xD574, prLV}, // Lo HANGUL SYLLABLE HAE + {0xD575, 0xD58F, prLVT}, // Lo [27] HANGUL SYLLABLE HAEG..HANGUL SYLLABLE HAEH + {0xD590, 0xD590, prLV}, // Lo HANGUL SYLLABLE HYA + {0xD591, 0xD5AB, prLVT}, // Lo [27] HANGUL SYLLABLE HYAG..HANGUL SYLLABLE HYAH + {0xD5AC, 0xD5AC, prLV}, // Lo HANGUL SYLLABLE HYAE + {0xD5AD, 0xD5C7, prLVT}, // Lo [27] HANGUL SYLLABLE HYAEG..HANGUL SYLLABLE HYAEH + {0xD5C8, 0xD5C8, prLV}, // Lo HANGUL SYLLABLE HEO + {0xD5C9, 0xD5E3, prLVT}, // Lo [27] HANGUL SYLLABLE HEOG..HANGUL SYLLABLE HEOH + {0xD5E4, 0xD5E4, prLV}, // Lo HANGUL SYLLABLE HE + {0xD5E5, 0xD5FF, prLVT}, // Lo [27] HANGUL SYLLABLE HEG..HANGUL SYLLABLE HEH + {0xD600, 0xD600, prLV}, // Lo HANGUL SYLLABLE HYEO + {0xD601, 0xD61B, prLVT}, // Lo [27] HANGUL SYLLABLE HYEOG..HANGUL SYLLABLE HYEOH + {0xD61C, 0xD61C, prLV}, // Lo HANGUL SYLLABLE HYE + {0xD61D, 0xD637, prLVT}, // Lo [27] HANGUL SYLLABLE HYEG..HANGUL SYLLABLE HYEH + {0xD638, 0xD638, prLV}, // Lo HANGUL SYLLABLE HO + {0xD639, 0xD653, prLVT}, // Lo [27] HANGUL SYLLABLE HOG..HANGUL SYLLABLE HOH + {0xD654, 0xD654, prLV}, // Lo HANGUL SYLLABLE HWA + {0xD655, 0xD66F, prLVT}, // Lo [27] HANGUL SYLLABLE HWAG..HANGUL SYLLABLE HWAH + {0xD670, 0xD670, prLV}, // Lo HANGUL SYLLABLE HWAE + {0xD671, 0xD68B, prLVT}, // Lo [27] HANGUL SYLLABLE HWAEG..HANGUL SYLLABLE HWAEH + {0xD68C, 0xD68C, prLV}, // Lo HANGUL SYLLABLE HOE + {0xD68D, 0xD6A7, prLVT}, // Lo [27] HANGUL SYLLABLE HOEG..HANGUL SYLLABLE HOEH + {0xD6A8, 0xD6A8, prLV}, // Lo HANGUL SYLLABLE HYO + {0xD6A9, 0xD6C3, prLVT}, // Lo [27] HANGUL SYLLABLE HYOG..HANGUL SYLLABLE HYOH + {0xD6C4, 0xD6C4, prLV}, // Lo HANGUL SYLLABLE HU + {0xD6C5, 0xD6DF, prLVT}, // Lo [27] HANGUL SYLLABLE HUG..HANGUL SYLLABLE HUH + {0xD6E0, 0xD6E0, prLV}, // Lo HANGUL SYLLABLE HWEO + {0xD6E1, 0xD6FB, prLVT}, // Lo [27] HANGUL SYLLABLE HWEOG..HANGUL SYLLABLE HWEOH + {0xD6FC, 0xD6FC, prLV}, // Lo HANGUL SYLLABLE HWE + {0xD6FD, 0xD717, prLVT}, // Lo [27] HANGUL SYLLABLE HWEG..HANGUL SYLLABLE HWEH + {0xD718, 0xD718, prLV}, // Lo HANGUL SYLLABLE HWI + {0xD719, 0xD733, prLVT}, // Lo [27] HANGUL SYLLABLE HWIG..HANGUL SYLLABLE HWIH + {0xD734, 0xD734, prLV}, // Lo HANGUL SYLLABLE HYU + {0xD735, 0xD74F, prLVT}, // Lo [27] HANGUL SYLLABLE HYUG..HANGUL SYLLABLE HYUH + {0xD750, 0xD750, prLV}, // Lo HANGUL SYLLABLE HEU + {0xD751, 0xD76B, prLVT}, // Lo [27] HANGUL SYLLABLE HEUG..HANGUL SYLLABLE HEUH + {0xD76C, 0xD76C, prLV}, // Lo HANGUL SYLLABLE HYI + {0xD76D, 0xD787, prLVT}, // Lo [27] HANGUL SYLLABLE HYIG..HANGUL SYLLABLE HYIH + {0xD788, 0xD788, prLV}, // Lo HANGUL SYLLABLE HI + {0xD789, 0xD7A3, prLVT}, // Lo [27] HANGUL SYLLABLE HIG..HANGUL SYLLABLE HIH + {0xD7B0, 0xD7C6, prV}, // Lo [23] HANGUL JUNGSEONG O-YEO..HANGUL JUNGSEONG ARAEA-E + {0xD7CB, 0xD7FB, prT}, // Lo [49] HANGUL JONGSEONG NIEUN-RIEUL..HANGUL JONGSEONG PHIEUPH-THIEUTH + {0xFB1E, 0xFB1E, prExtend}, // Mn HEBREW POINT JUDEO-SPANISH VARIKA + {0xFE00, 0xFE0F, prExtend}, // Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16 + {0xFE20, 0xFE2F, prExtend}, // Mn [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITLO RIGHT HALF + {0xFEFF, 0xFEFF, prControl}, // Cf ZERO WIDTH NO-BREAK SPACE + {0xFF9E, 0xFF9F, prExtend}, // Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK + {0xFFF0, 0xFFF8, prControl}, // Cn [9] .. + {0xFFF9, 0xFFFB, prControl}, // Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR + {0x101FD, 0x101FD, prExtend}, // Mn PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE + {0x102E0, 0x102E0, prExtend}, // Mn COPTIC EPACT THOUSANDS MARK + {0x10376, 0x1037A, prExtend}, // Mn [5] COMBINING OLD PERMIC LETTER AN..COMBINING OLD PERMIC LETTER SII + {0x10A01, 0x10A03, prExtend}, // Mn [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R + {0x10A05, 0x10A06, prExtend}, // Mn [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O + {0x10A0C, 0x10A0F, prExtend}, // Mn [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA + {0x10A38, 0x10A3A, prExtend}, // Mn [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW + {0x10A3F, 0x10A3F, prExtend}, // Mn KHAROSHTHI VIRAMA + {0x10AE5, 0x10AE6, prExtend}, // Mn [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW + {0x10D24, 0x10D27, prExtend}, // Mn [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI + {0x10F46, 0x10F50, prExtend}, // Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW + {0x11000, 0x11000, prSpacingMark}, // Mc BRAHMI SIGN CANDRABINDU + {0x11001, 0x11001, prExtend}, // Mn BRAHMI SIGN ANUSVARA + {0x11002, 0x11002, prSpacingMark}, // Mc BRAHMI SIGN VISARGA + {0x11038, 0x11046, prExtend}, // Mn [15] BRAHMI VOWEL SIGN AA..BRAHMI VIRAMA + {0x1107F, 0x11081, prExtend}, // Mn [3] BRAHMI NUMBER JOINER..KAITHI SIGN ANUSVARA + {0x11082, 0x11082, prSpacingMark}, // Mc KAITHI SIGN VISARGA + {0x110B0, 0x110B2, prSpacingMark}, // Mc [3] KAITHI VOWEL SIGN AA..KAITHI VOWEL SIGN II + {0x110B3, 0x110B6, prExtend}, // Mn [4] KAITHI VOWEL SIGN U..KAITHI VOWEL SIGN AI + {0x110B7, 0x110B8, prSpacingMark}, // Mc [2] KAITHI VOWEL SIGN O..KAITHI VOWEL SIGN AU + {0x110B9, 0x110BA, prExtend}, // Mn [2] KAITHI SIGN VIRAMA..KAITHI SIGN NUKTA + {0x110BD, 0x110BD, prPreprend}, // Cf KAITHI NUMBER SIGN + {0x110CD, 0x110CD, prPreprend}, // Cf KAITHI NUMBER SIGN ABOVE + {0x11100, 0x11102, prExtend}, // Mn [3] CHAKMA SIGN CANDRABINDU..CHAKMA SIGN VISARGA + {0x11127, 0x1112B, prExtend}, // Mn [5] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN UU + {0x1112C, 0x1112C, prSpacingMark}, // Mc CHAKMA VOWEL SIGN E + {0x1112D, 0x11134, prExtend}, // Mn [8] CHAKMA VOWEL SIGN AI..CHAKMA MAAYYAA + {0x11145, 0x11146, prSpacingMark}, // Mc [2] CHAKMA VOWEL SIGN AA..CHAKMA VOWEL SIGN EI + {0x11173, 0x11173, prExtend}, // Mn MAHAJANI SIGN NUKTA + {0x11180, 0x11181, prExtend}, // Mn [2] SHARADA SIGN CANDRABINDU..SHARADA SIGN ANUSVARA + {0x11182, 0x11182, prSpacingMark}, // Mc SHARADA SIGN VISARGA + {0x111B3, 0x111B5, prSpacingMark}, // Mc [3] SHARADA VOWEL SIGN AA..SHARADA VOWEL SIGN II + {0x111B6, 0x111BE, prExtend}, // Mn [9] SHARADA VOWEL SIGN U..SHARADA VOWEL SIGN O + {0x111BF, 0x111C0, prSpacingMark}, // Mc [2] SHARADA VOWEL SIGN AU..SHARADA SIGN VIRAMA + {0x111C2, 0x111C3, prPreprend}, // Lo [2] SHARADA SIGN JIHVAMULIYA..SHARADA SIGN UPADHMANIYA + {0x111C9, 0x111CC, prExtend}, // Mn [4] SHARADA SANDHI MARK..SHARADA EXTRA SHORT VOWEL MARK + {0x1122C, 0x1122E, prSpacingMark}, // Mc [3] KHOJKI VOWEL SIGN AA..KHOJKI VOWEL SIGN II + {0x1122F, 0x11231, prExtend}, // Mn [3] KHOJKI VOWEL SIGN U..KHOJKI VOWEL SIGN AI + {0x11232, 0x11233, prSpacingMark}, // Mc [2] KHOJKI VOWEL SIGN O..KHOJKI VOWEL SIGN AU + {0x11234, 0x11234, prExtend}, // Mn KHOJKI SIGN ANUSVARA + {0x11235, 0x11235, prSpacingMark}, // Mc KHOJKI SIGN VIRAMA + {0x11236, 0x11237, prExtend}, // Mn [2] KHOJKI SIGN NUKTA..KHOJKI SIGN SHADDA + {0x1123E, 0x1123E, prExtend}, // Mn KHOJKI SIGN SUKUN + {0x112DF, 0x112DF, prExtend}, // Mn KHUDAWADI SIGN ANUSVARA + {0x112E0, 0x112E2, prSpacingMark}, // Mc [3] KHUDAWADI VOWEL SIGN AA..KHUDAWADI VOWEL SIGN II + {0x112E3, 0x112EA, prExtend}, // Mn [8] KHUDAWADI VOWEL SIGN U..KHUDAWADI SIGN VIRAMA + {0x11300, 0x11301, prExtend}, // Mn [2] GRANTHA SIGN COMBINING ANUSVARA ABOVE..GRANTHA SIGN CANDRABINDU + {0x11302, 0x11303, prSpacingMark}, // Mc [2] GRANTHA SIGN ANUSVARA..GRANTHA SIGN VISARGA + {0x1133B, 0x1133C, prExtend}, // Mn [2] COMBINING BINDU BELOW..GRANTHA SIGN NUKTA + {0x1133E, 0x1133E, prExtend}, // Mc GRANTHA VOWEL SIGN AA + {0x1133F, 0x1133F, prSpacingMark}, // Mc GRANTHA VOWEL SIGN I + {0x11340, 0x11340, prExtend}, // Mn GRANTHA VOWEL SIGN II + {0x11341, 0x11344, prSpacingMark}, // Mc [4] GRANTHA VOWEL SIGN U..GRANTHA VOWEL SIGN VOCALIC RR + {0x11347, 0x11348, prSpacingMark}, // Mc [2] GRANTHA VOWEL SIGN EE..GRANTHA VOWEL SIGN AI + {0x1134B, 0x1134D, prSpacingMark}, // Mc [3] GRANTHA VOWEL SIGN OO..GRANTHA SIGN VIRAMA + {0x11357, 0x11357, prExtend}, // Mc GRANTHA AU LENGTH MARK + {0x11362, 0x11363, prSpacingMark}, // Mc [2] GRANTHA VOWEL SIGN VOCALIC L..GRANTHA VOWEL SIGN VOCALIC LL + {0x11366, 0x1136C, prExtend}, // Mn [7] COMBINING GRANTHA DIGIT ZERO..COMBINING GRANTHA DIGIT SIX + {0x11370, 0x11374, prExtend}, // Mn [5] COMBINING GRANTHA LETTER A..COMBINING GRANTHA LETTER PA + {0x11435, 0x11437, prSpacingMark}, // Mc [3] NEWA VOWEL SIGN AA..NEWA VOWEL SIGN II + {0x11438, 0x1143F, prExtend}, // Mn [8] NEWA VOWEL SIGN U..NEWA VOWEL SIGN AI + {0x11440, 0x11441, prSpacingMark}, // Mc [2] NEWA VOWEL SIGN O..NEWA VOWEL SIGN AU + {0x11442, 0x11444, prExtend}, // Mn [3] NEWA SIGN VIRAMA..NEWA SIGN ANUSVARA + {0x11445, 0x11445, prSpacingMark}, // Mc NEWA SIGN VISARGA + {0x11446, 0x11446, prExtend}, // Mn NEWA SIGN NUKTA + {0x1145E, 0x1145E, prExtend}, // Mn NEWA SANDHI MARK + {0x114B0, 0x114B0, prExtend}, // Mc TIRHUTA VOWEL SIGN AA + {0x114B1, 0x114B2, prSpacingMark}, // Mc [2] TIRHUTA VOWEL SIGN I..TIRHUTA VOWEL SIGN II + {0x114B3, 0x114B8, prExtend}, // Mn [6] TIRHUTA VOWEL SIGN U..TIRHUTA VOWEL SIGN VOCALIC LL + {0x114B9, 0x114B9, prSpacingMark}, // Mc TIRHUTA VOWEL SIGN E + {0x114BA, 0x114BA, prExtend}, // Mn TIRHUTA VOWEL SIGN SHORT E + {0x114BB, 0x114BC, prSpacingMark}, // Mc [2] TIRHUTA VOWEL SIGN AI..TIRHUTA VOWEL SIGN O + {0x114BD, 0x114BD, prExtend}, // Mc TIRHUTA VOWEL SIGN SHORT O + {0x114BE, 0x114BE, prSpacingMark}, // Mc TIRHUTA VOWEL SIGN AU + {0x114BF, 0x114C0, prExtend}, // Mn [2] TIRHUTA SIGN CANDRABINDU..TIRHUTA SIGN ANUSVARA + {0x114C1, 0x114C1, prSpacingMark}, // Mc TIRHUTA SIGN VISARGA + {0x114C2, 0x114C3, prExtend}, // Mn [2] TIRHUTA SIGN VIRAMA..TIRHUTA SIGN NUKTA + {0x115AF, 0x115AF, prExtend}, // Mc SIDDHAM VOWEL SIGN AA + {0x115B0, 0x115B1, prSpacingMark}, // Mc [2] SIDDHAM VOWEL SIGN I..SIDDHAM VOWEL SIGN II + {0x115B2, 0x115B5, prExtend}, // Mn [4] SIDDHAM VOWEL SIGN U..SIDDHAM VOWEL SIGN VOCALIC RR + {0x115B8, 0x115BB, prSpacingMark}, // Mc [4] SIDDHAM VOWEL SIGN E..SIDDHAM VOWEL SIGN AU + {0x115BC, 0x115BD, prExtend}, // Mn [2] SIDDHAM SIGN CANDRABINDU..SIDDHAM SIGN ANUSVARA + {0x115BE, 0x115BE, prSpacingMark}, // Mc SIDDHAM SIGN VISARGA + {0x115BF, 0x115C0, prExtend}, // Mn [2] SIDDHAM SIGN VIRAMA..SIDDHAM SIGN NUKTA + {0x115DC, 0x115DD, prExtend}, // Mn [2] SIDDHAM VOWEL SIGN ALTERNATE U..SIDDHAM VOWEL SIGN ALTERNATE UU + {0x11630, 0x11632, prSpacingMark}, // Mc [3] MODI VOWEL SIGN AA..MODI VOWEL SIGN II + {0x11633, 0x1163A, prExtend}, // Mn [8] MODI VOWEL SIGN U..MODI VOWEL SIGN AI + {0x1163B, 0x1163C, prSpacingMark}, // Mc [2] MODI VOWEL SIGN O..MODI VOWEL SIGN AU + {0x1163D, 0x1163D, prExtend}, // Mn MODI SIGN ANUSVARA + {0x1163E, 0x1163E, prSpacingMark}, // Mc MODI SIGN VISARGA + {0x1163F, 0x11640, prExtend}, // Mn [2] MODI SIGN VIRAMA..MODI SIGN ARDHACANDRA + {0x116AB, 0x116AB, prExtend}, // Mn TAKRI SIGN ANUSVARA + {0x116AC, 0x116AC, prSpacingMark}, // Mc TAKRI SIGN VISARGA + {0x116AD, 0x116AD, prExtend}, // Mn TAKRI VOWEL SIGN AA + {0x116AE, 0x116AF, prSpacingMark}, // Mc [2] TAKRI VOWEL SIGN I..TAKRI VOWEL SIGN II + {0x116B0, 0x116B5, prExtend}, // Mn [6] TAKRI VOWEL SIGN U..TAKRI VOWEL SIGN AU + {0x116B6, 0x116B6, prSpacingMark}, // Mc TAKRI SIGN VIRAMA + {0x116B7, 0x116B7, prExtend}, // Mn TAKRI SIGN NUKTA + {0x1171D, 0x1171F, prExtend}, // Mn [3] AHOM CONSONANT SIGN MEDIAL LA..AHOM CONSONANT SIGN MEDIAL LIGATING RA + {0x11720, 0x11721, prSpacingMark}, // Mc [2] AHOM VOWEL SIGN A..AHOM VOWEL SIGN AA + {0x11722, 0x11725, prExtend}, // Mn [4] AHOM VOWEL SIGN I..AHOM VOWEL SIGN UU + {0x11726, 0x11726, prSpacingMark}, // Mc AHOM VOWEL SIGN E + {0x11727, 0x1172B, prExtend}, // Mn [5] AHOM VOWEL SIGN AW..AHOM SIGN KILLER + {0x1182C, 0x1182E, prSpacingMark}, // Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II + {0x1182F, 0x11837, prExtend}, // Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA + {0x11838, 0x11838, prSpacingMark}, // Mc DOGRA SIGN VISARGA + {0x11839, 0x1183A, prExtend}, // Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN NUKTA + {0x119D1, 0x119D3, prSpacingMark}, // Mc [3] NANDINAGARI VOWEL SIGN AA..NANDINAGARI VOWEL SIGN II + {0x119D4, 0x119D7, prExtend}, // Mn [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR + {0x119DA, 0x119DB, prExtend}, // Mn [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI + {0x119DC, 0x119DF, prSpacingMark}, // Mc [4] NANDINAGARI VOWEL SIGN O..NANDINAGARI SIGN VISARGA + {0x119E0, 0x119E0, prExtend}, // Mn NANDINAGARI SIGN VIRAMA + {0x119E4, 0x119E4, prSpacingMark}, // Mc NANDINAGARI VOWEL SIGN PRISHTHAMATRA E + {0x11A01, 0x11A0A, prExtend}, // Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK + {0x11A33, 0x11A38, prExtend}, // Mn [6] ZANABAZAR SQUARE FINAL CONSONANT MARK..ZANABAZAR SQUARE SIGN ANUSVARA + {0x11A39, 0x11A39, prSpacingMark}, // Mc ZANABAZAR SQUARE SIGN VISARGA + {0x11A3A, 0x11A3A, prPreprend}, // Lo ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA + {0x11A3B, 0x11A3E, prExtend}, // Mn [4] ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA..ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA + {0x11A47, 0x11A47, prExtend}, // Mn ZANABAZAR SQUARE SUBJOINER + {0x11A51, 0x11A56, prExtend}, // Mn [6] SOYOMBO VOWEL SIGN I..SOYOMBO VOWEL SIGN OE + {0x11A57, 0x11A58, prSpacingMark}, // Mc [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU + {0x11A59, 0x11A5B, prExtend}, // Mn [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK + {0x11A84, 0x11A89, prPreprend}, // Lo [6] SOYOMBO SIGN JIHVAMULIYA..SOYOMBO CLUSTER-INITIAL LETTER SA + {0x11A8A, 0x11A96, prExtend}, // Mn [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA + {0x11A97, 0x11A97, prSpacingMark}, // Mc SOYOMBO SIGN VISARGA + {0x11A98, 0x11A99, prExtend}, // Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER + {0x11C2F, 0x11C2F, prSpacingMark}, // Mc BHAIKSUKI VOWEL SIGN AA + {0x11C30, 0x11C36, prExtend}, // Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L + {0x11C38, 0x11C3D, prExtend}, // Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA + {0x11C3E, 0x11C3E, prSpacingMark}, // Mc BHAIKSUKI SIGN VISARGA + {0x11C3F, 0x11C3F, prExtend}, // Mn BHAIKSUKI SIGN VIRAMA + {0x11C92, 0x11CA7, prExtend}, // Mn [22] MARCHEN SUBJOINED LETTER KA..MARCHEN SUBJOINED LETTER ZA + {0x11CA9, 0x11CA9, prSpacingMark}, // Mc MARCHEN SUBJOINED LETTER YA + {0x11CAA, 0x11CB0, prExtend}, // Mn [7] MARCHEN SUBJOINED LETTER RA..MARCHEN VOWEL SIGN AA + {0x11CB1, 0x11CB1, prSpacingMark}, // Mc MARCHEN VOWEL SIGN I + {0x11CB2, 0x11CB3, prExtend}, // Mn [2] MARCHEN VOWEL SIGN U..MARCHEN VOWEL SIGN E + {0x11CB4, 0x11CB4, prSpacingMark}, // Mc MARCHEN VOWEL SIGN O + {0x11CB5, 0x11CB6, prExtend}, // Mn [2] MARCHEN SIGN ANUSVARA..MARCHEN SIGN CANDRABINDU + {0x11D31, 0x11D36, prExtend}, // Mn [6] MASARAM GONDI VOWEL SIGN AA..MASARAM GONDI VOWEL SIGN VOCALIC R + {0x11D3A, 0x11D3A, prExtend}, // Mn MASARAM GONDI VOWEL SIGN E + {0x11D3C, 0x11D3D, prExtend}, // Mn [2] MASARAM GONDI VOWEL SIGN AI..MASARAM GONDI VOWEL SIGN O + {0x11D3F, 0x11D45, prExtend}, // Mn [7] MASARAM GONDI VOWEL SIGN AU..MASARAM GONDI VIRAMA + {0x11D46, 0x11D46, prPreprend}, // Lo MASARAM GONDI REPHA + {0x11D47, 0x11D47, prExtend}, // Mn MASARAM GONDI RA-KARA + {0x11D8A, 0x11D8E, prSpacingMark}, // Mc [5] GUNJALA GONDI VOWEL SIGN AA..GUNJALA GONDI VOWEL SIGN UU + {0x11D90, 0x11D91, prExtend}, // Mn [2] GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI + {0x11D93, 0x11D94, prSpacingMark}, // Mc [2] GUNJALA GONDI VOWEL SIGN OO..GUNJALA GONDI VOWEL SIGN AU + {0x11D95, 0x11D95, prExtend}, // Mn GUNJALA GONDI SIGN ANUSVARA + {0x11D96, 0x11D96, prSpacingMark}, // Mc GUNJALA GONDI SIGN VISARGA + {0x11D97, 0x11D97, prExtend}, // Mn GUNJALA GONDI VIRAMA + {0x11EF3, 0x11EF4, prExtend}, // Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U + {0x11EF5, 0x11EF6, prSpacingMark}, // Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O + {0x13430, 0x13438, prControl}, // Cf [9] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END SEGMENT + {0x16AF0, 0x16AF4, prExtend}, // Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE + {0x16B30, 0x16B36, prExtend}, // Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM + {0x16F4F, 0x16F4F, prExtend}, // Mn MIAO SIGN CONSONANT MODIFIER BAR + {0x16F51, 0x16F87, prSpacingMark}, // Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI + {0x16F8F, 0x16F92, prExtend}, // Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW + {0x1BC9D, 0x1BC9E, prExtend}, // Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK + {0x1BCA0, 0x1BCA3, prControl}, // Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP + {0x1D165, 0x1D165, prExtend}, // Mc MUSICAL SYMBOL COMBINING STEM + {0x1D166, 0x1D166, prSpacingMark}, // Mc MUSICAL SYMBOL COMBINING SPRECHGESANG STEM + {0x1D167, 0x1D169, prExtend}, // Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3 + {0x1D16D, 0x1D16D, prSpacingMark}, // Mc MUSICAL SYMBOL COMBINING AUGMENTATION DOT + {0x1D16E, 0x1D172, prExtend}, // Mc [5] MUSICAL SYMBOL COMBINING FLAG-1..MUSICAL SYMBOL COMBINING FLAG-5 + {0x1D173, 0x1D17A, prControl}, // Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE + {0x1D17B, 0x1D182, prExtend}, // Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE + {0x1D185, 0x1D18B, prExtend}, // Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE + {0x1D1AA, 0x1D1AD, prExtend}, // Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO + {0x1D242, 0x1D244, prExtend}, // Mn [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME + {0x1DA00, 0x1DA36, prExtend}, // Mn [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN + {0x1DA3B, 0x1DA6C, prExtend}, // Mn [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT + {0x1DA75, 0x1DA75, prExtend}, // Mn SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS + {0x1DA84, 0x1DA84, prExtend}, // Mn SIGNWRITING LOCATION HEAD NECK + {0x1DA9B, 0x1DA9F, prExtend}, // Mn [5] SIGNWRITING FILL MODIFIER-2..SIGNWRITING FILL MODIFIER-6 + {0x1DAA1, 0x1DAAF, prExtend}, // Mn [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16 + {0x1E000, 0x1E006, prExtend}, // Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE + {0x1E008, 0x1E018, prExtend}, // Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU + {0x1E01B, 0x1E021, prExtend}, // Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI + {0x1E023, 0x1E024, prExtend}, // Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS + {0x1E026, 0x1E02A, prExtend}, // Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA + {0x1E130, 0x1E136, prExtend}, // Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D + {0x1E2EC, 0x1E2EF, prExtend}, // Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI + {0x1E8D0, 0x1E8D6, prExtend}, // Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS + {0x1E944, 0x1E94A, prExtend}, // Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA + {0x1F000, 0x1F02B, prExtendedPictographic}, // 5.1 [44] (🀀..🀫) MAHJONG TILE EAST WIND..MAHJONG TILE BACK + {0x1F02C, 0x1F02F, prExtendedPictographic}, // NA [4] (🀬..🀯) .. + {0x1F030, 0x1F093, prExtendedPictographic}, // 5.1[100] (🀰..🂓) DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06 + {0x1F094, 0x1F09F, prExtendedPictographic}, // NA [12] (🂔..🂟) .. + {0x1F0A0, 0x1F0AE, prExtendedPictographic}, // 6.0 [15] (🂠..🂮) PLAYING CARD BACK..PLAYING CARD KING OF SPADES + {0x1F0AF, 0x1F0B0, prExtendedPictographic}, // NA [2] (🂯..🂰) .. + {0x1F0B1, 0x1F0BE, prExtendedPictographic}, // 6.0 [14] (🂱..🂾) PLAYING CARD ACE OF HEARTS..PLAYING CARD KING OF HEARTS + {0x1F0BF, 0x1F0BF, prExtendedPictographic}, // 7.0 [1] (🂿) PLAYING CARD RED JOKER + {0x1F0C0, 0x1F0C0, prExtendedPictographic}, // NA [1] (🃀) + {0x1F0C1, 0x1F0CF, prExtendedPictographic}, // 6.0 [15] (🃁..🃏) PLAYING CARD ACE OF DIAMONDS..joker + {0x1F0D0, 0x1F0D0, prExtendedPictographic}, // NA [1] (🃐) + {0x1F0D1, 0x1F0DF, prExtendedPictographic}, // 6.0 [15] (🃑..🃟) PLAYING CARD ACE OF CLUBS..PLAYING CARD WHITE JOKER + {0x1F0E0, 0x1F0F5, prExtendedPictographic}, // 7.0 [22] (🃠..🃵) PLAYING CARD FOOL..PLAYING CARD TRUMP-21 + {0x1F0F6, 0x1F0FF, prExtendedPictographic}, // NA [10] (🃶..🃿) .. + {0x1F10D, 0x1F10F, prExtendedPictographic}, // NA [3] (🄍..🄏) .. + {0x1F12F, 0x1F12F, prExtendedPictographic}, // 11.0 [1] (🄯) COPYLEFT SYMBOL + {0x1F16C, 0x1F16C, prExtendedPictographic}, // 12.0 [1] (🅬) RAISED MR SIGN + {0x1F16D, 0x1F16F, prExtendedPictographic}, // NA [3] (🅭..🅯) .. + {0x1F170, 0x1F171, prExtendedPictographic}, // 6.0 [2] (🅰️..🅱️) A button (blood type)..B button (blood type) + {0x1F17E, 0x1F17E, prExtendedPictographic}, // 6.0 [1] (🅾️) O button (blood type) + {0x1F17F, 0x1F17F, prExtendedPictographic}, // 5.2 [1] (🅿️) P button + {0x1F18E, 0x1F18E, prExtendedPictographic}, // 6.0 [1] (🆎) AB button (blood type) + {0x1F191, 0x1F19A, prExtendedPictographic}, // 6.0 [10] (🆑..🆚) CL button..VS button + {0x1F1AD, 0x1F1E5, prExtendedPictographic}, // NA [57] (🆭..🇥) .. + {0x1F1E6, 0x1F1FF, prRegionalIndicator}, // So [26] REGIONAL INDICATOR SYMBOL LETTER A..REGIONAL INDICATOR SYMBOL LETTER Z + {0x1F201, 0x1F202, prExtendedPictographic}, // 6.0 [2] (🈁..🈂️) Japanese “here” button..Japanese “service charge” button + {0x1F203, 0x1F20F, prExtendedPictographic}, // NA [13] (🈃..🈏) .. + {0x1F21A, 0x1F21A, prExtendedPictographic}, // 5.2 [1] (🈚) Japanese “free of charge” button + {0x1F22F, 0x1F22F, prExtendedPictographic}, // 5.2 [1] (🈯) Japanese “reserved” button + {0x1F232, 0x1F23A, prExtendedPictographic}, // 6.0 [9] (🈲..🈺) Japanese “prohibited” button..Japanese “open for business” button + {0x1F23C, 0x1F23F, prExtendedPictographic}, // NA [4] (🈼..🈿) .. + {0x1F249, 0x1F24F, prExtendedPictographic}, // NA [7] (🉉..🉏) .. + {0x1F250, 0x1F251, prExtendedPictographic}, // 6.0 [2] (🉐..🉑) Japanese “bargain” button..Japanese “acceptable” button + {0x1F252, 0x1F25F, prExtendedPictographic}, // NA [14] (🉒..🉟) .. + {0x1F260, 0x1F265, prExtendedPictographic}, // 10.0 [6] (🉠..🉥) ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI + {0x1F266, 0x1F2FF, prExtendedPictographic}, // NA[154] (🉦..🋿) .. + {0x1F300, 0x1F320, prExtendedPictographic}, // 6.0 [33] (🌀..🌠) cyclone..shooting star + {0x1F321, 0x1F32C, prExtendedPictographic}, // 7.0 [12] (🌡️..🌬️) thermometer..wind face + {0x1F32D, 0x1F32F, prExtendedPictographic}, // 8.0 [3] (🌭..🌯) hot dog..burrito + {0x1F330, 0x1F335, prExtendedPictographic}, // 6.0 [6] (🌰..🌵) chestnut..cactus + {0x1F336, 0x1F336, prExtendedPictographic}, // 7.0 [1] (🌶️) hot pepper + {0x1F337, 0x1F37C, prExtendedPictographic}, // 6.0 [70] (🌷..🍼) tulip..baby bottle + {0x1F37D, 0x1F37D, prExtendedPictographic}, // 7.0 [1] (🍽️) fork and knife with plate + {0x1F37E, 0x1F37F, prExtendedPictographic}, // 8.0 [2] (🍾..🍿) bottle with popping cork..popcorn + {0x1F380, 0x1F393, prExtendedPictographic}, // 6.0 [20] (🎀..🎓) ribbon..graduation cap + {0x1F394, 0x1F39F, prExtendedPictographic}, // 7.0 [12] (🎔..🎟️) HEART WITH TIP ON THE LEFT..admission tickets + {0x1F3A0, 0x1F3C4, prExtendedPictographic}, // 6.0 [37] (🎠..🏄) carousel horse..person surfing + {0x1F3C5, 0x1F3C5, prExtendedPictographic}, // 7.0 [1] (🏅) sports medal + {0x1F3C6, 0x1F3CA, prExtendedPictographic}, // 6.0 [5] (🏆..🏊) trophy..person swimming + {0x1F3CB, 0x1F3CE, prExtendedPictographic}, // 7.0 [4] (🏋️..🏎️) person lifting weights..racing car + {0x1F3CF, 0x1F3D3, prExtendedPictographic}, // 8.0 [5] (🏏..🏓) cricket game..ping pong + {0x1F3D4, 0x1F3DF, prExtendedPictographic}, // 7.0 [12] (🏔️..🏟️) snow-capped mountain..stadium + {0x1F3E0, 0x1F3F0, prExtendedPictographic}, // 6.0 [17] (🏠..🏰) house..castle + {0x1F3F1, 0x1F3F7, prExtendedPictographic}, // 7.0 [7] (🏱..🏷️) WHITE PENNANT..label + {0x1F3F8, 0x1F3FA, prExtendedPictographic}, // 8.0 [3] (🏸..🏺) badminton..amphora + {0x1F3FB, 0x1F3FF, prExtend}, // Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6 + {0x1F400, 0x1F43E, prExtendedPictographic}, // 6.0 [63] (🐀..🐾) rat..paw prints + {0x1F43F, 0x1F43F, prExtendedPictographic}, // 7.0 [1] (🐿️) chipmunk + {0x1F440, 0x1F440, prExtendedPictographic}, // 6.0 [1] (👀) eyes + {0x1F441, 0x1F441, prExtendedPictographic}, // 7.0 [1] (👁️) eye + {0x1F442, 0x1F4F7, prExtendedPictographic}, // 6.0[182] (👂..📷) ear..camera + {0x1F4F8, 0x1F4F8, prExtendedPictographic}, // 7.0 [1] (📸) camera with flash + {0x1F4F9, 0x1F4FC, prExtendedPictographic}, // 6.0 [4] (📹..📼) video camera..videocassette + {0x1F4FD, 0x1F4FE, prExtendedPictographic}, // 7.0 [2] (📽️..📾) film projector..PORTABLE STEREO + {0x1F4FF, 0x1F4FF, prExtendedPictographic}, // 8.0 [1] (📿) prayer beads + {0x1F500, 0x1F53D, prExtendedPictographic}, // 6.0 [62] (🔀..🔽) shuffle tracks button..downwards button + {0x1F546, 0x1F54A, prExtendedPictographic}, // 7.0 [5] (🕆..🕊️) WHITE LATIN CROSS..dove + {0x1F54B, 0x1F54F, prExtendedPictographic}, // 8.0 [5] (🕋..🕏) kaaba..BOWL OF HYGIEIA + {0x1F550, 0x1F567, prExtendedPictographic}, // 6.0 [24] (🕐..🕧) one o’clock..twelve-thirty + {0x1F568, 0x1F579, prExtendedPictographic}, // 7.0 [18] (🕨..🕹️) RIGHT SPEAKER..joystick + {0x1F57A, 0x1F57A, prExtendedPictographic}, // 9.0 [1] (🕺) man dancing + {0x1F57B, 0x1F5A3, prExtendedPictographic}, // 7.0 [41] (🕻..🖣) LEFT HAND TELEPHONE RECEIVER..BLACK DOWN POINTING BACKHAND INDEX + {0x1F5A4, 0x1F5A4, prExtendedPictographic}, // 9.0 [1] (🖤) black heart + {0x1F5A5, 0x1F5FA, prExtendedPictographic}, // 7.0 [86] (🖥️..🗺️) desktop computer..world map + {0x1F5FB, 0x1F5FF, prExtendedPictographic}, // 6.0 [5] (🗻..🗿) mount fuji..moai + {0x1F600, 0x1F600, prExtendedPictographic}, // 6.1 [1] (😀) grinning face + {0x1F601, 0x1F610, prExtendedPictographic}, // 6.0 [16] (😁..😐) beaming face with smiling eyes..neutral face + {0x1F611, 0x1F611, prExtendedPictographic}, // 6.1 [1] (😑) expressionless face + {0x1F612, 0x1F614, prExtendedPictographic}, // 6.0 [3] (😒..😔) unamused face..pensive face + {0x1F615, 0x1F615, prExtendedPictographic}, // 6.1 [1] (😕) confused face + {0x1F616, 0x1F616, prExtendedPictographic}, // 6.0 [1] (😖) confounded face + {0x1F617, 0x1F617, prExtendedPictographic}, // 6.1 [1] (😗) kissing face + {0x1F618, 0x1F618, prExtendedPictographic}, // 6.0 [1] (😘) face blowing a kiss + {0x1F619, 0x1F619, prExtendedPictographic}, // 6.1 [1] (😙) kissing face with smiling eyes + {0x1F61A, 0x1F61A, prExtendedPictographic}, // 6.0 [1] (😚) kissing face with closed eyes + {0x1F61B, 0x1F61B, prExtendedPictographic}, // 6.1 [1] (😛) face with tongue + {0x1F61C, 0x1F61E, prExtendedPictographic}, // 6.0 [3] (😜..😞) winking face with tongue..disappointed face + {0x1F61F, 0x1F61F, prExtendedPictographic}, // 6.1 [1] (😟) worried face + {0x1F620, 0x1F625, prExtendedPictographic}, // 6.0 [6] (😠..😥) angry face..sad but relieved face + {0x1F626, 0x1F627, prExtendedPictographic}, // 6.1 [2] (😦..😧) frowning face with open mouth..anguished face + {0x1F628, 0x1F62B, prExtendedPictographic}, // 6.0 [4] (😨..😫) fearful face..tired face + {0x1F62C, 0x1F62C, prExtendedPictographic}, // 6.1 [1] (😬) grimacing face + {0x1F62D, 0x1F62D, prExtendedPictographic}, // 6.0 [1] (😭) loudly crying face + {0x1F62E, 0x1F62F, prExtendedPictographic}, // 6.1 [2] (😮..😯) face with open mouth..hushed face + {0x1F630, 0x1F633, prExtendedPictographic}, // 6.0 [4] (😰..😳) anxious face with sweat..flushed face + {0x1F634, 0x1F634, prExtendedPictographic}, // 6.1 [1] (😴) sleeping face + {0x1F635, 0x1F640, prExtendedPictographic}, // 6.0 [12] (😵..🙀) dizzy face..weary cat + {0x1F641, 0x1F642, prExtendedPictographic}, // 7.0 [2] (🙁..🙂) slightly frowning face..slightly smiling face + {0x1F643, 0x1F644, prExtendedPictographic}, // 8.0 [2] (🙃..🙄) upside-down face..face with rolling eyes + {0x1F645, 0x1F64F, prExtendedPictographic}, // 6.0 [11] (🙅..🙏) person gesturing NO..folded hands + {0x1F680, 0x1F6C5, prExtendedPictographic}, // 6.0 [70] (🚀..🛅) rocket..left luggage + {0x1F6C6, 0x1F6CF, prExtendedPictographic}, // 7.0 [10] (🛆..🛏️) TRIANGLE WITH ROUNDED CORNERS..bed + {0x1F6D0, 0x1F6D0, prExtendedPictographic}, // 8.0 [1] (🛐) place of worship + {0x1F6D1, 0x1F6D2, prExtendedPictographic}, // 9.0 [2] (🛑..🛒) stop sign..shopping cart + {0x1F6D3, 0x1F6D4, prExtendedPictographic}, // 10.0 [2] (🛓..🛔) STUPA..PAGODA + {0x1F6D5, 0x1F6D5, prExtendedPictographic}, // 12.0 [1] (🛕) hindu temple + {0x1F6D6, 0x1F6DF, prExtendedPictographic}, // NA [10] (🛖..🛟) .. + {0x1F6E0, 0x1F6EC, prExtendedPictographic}, // 7.0 [13] (🛠️..🛬) hammer and wrench..airplane arrival + {0x1F6ED, 0x1F6EF, prExtendedPictographic}, // NA [3] (🛭..🛯) .. + {0x1F6F0, 0x1F6F3, prExtendedPictographic}, // 7.0 [4] (🛰️..🛳️) satellite..passenger ship + {0x1F6F4, 0x1F6F6, prExtendedPictographic}, // 9.0 [3] (🛴..🛶) kick scooter..canoe + {0x1F6F7, 0x1F6F8, prExtendedPictographic}, // 10.0 [2] (🛷..🛸) sled..flying saucer + {0x1F6F9, 0x1F6F9, prExtendedPictographic}, // 11.0 [1] (🛹) skateboard + {0x1F6FA, 0x1F6FA, prExtendedPictographic}, // 12.0 [1] (🛺) auto rickshaw + {0x1F6FB, 0x1F6FF, prExtendedPictographic}, // NA [5] (🛻..🛿) .. + {0x1F774, 0x1F77F, prExtendedPictographic}, // NA [12] (🝴..🝿) .. + {0x1F7D5, 0x1F7D8, prExtendedPictographic}, // 11.0 [4] (🟕..🟘) CIRCLED TRIANGLE..NEGATIVE CIRCLED SQUARE + {0x1F7D9, 0x1F7DF, prExtendedPictographic}, // NA [7] (🟙..🟟) .. + {0x1F7E0, 0x1F7EB, prExtendedPictographic}, // 12.0 [12] (🟠..🟫) orange circle..brown square + {0x1F7EC, 0x1F7FF, prExtendedPictographic}, // NA [20] (🟬..🟿) .. + {0x1F80C, 0x1F80F, prExtendedPictographic}, // NA [4] (🠌..🠏) .. + {0x1F848, 0x1F84F, prExtendedPictographic}, // NA [8] (🡈..🡏) .. + {0x1F85A, 0x1F85F, prExtendedPictographic}, // NA [6] (🡚..🡟) .. + {0x1F888, 0x1F88F, prExtendedPictographic}, // NA [8] (🢈..🢏) .. + {0x1F8AE, 0x1F8FF, prExtendedPictographic}, // NA [82] (🢮..🣿) .. + {0x1F90C, 0x1F90C, prExtendedPictographic}, // NA [1] (🤌) + {0x1F90D, 0x1F90F, prExtendedPictographic}, // 12.0 [3] (🤍..🤏) white heart..pinching hand + {0x1F910, 0x1F918, prExtendedPictographic}, // 8.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns + {0x1F919, 0x1F91E, prExtendedPictographic}, // 9.0 [6] (🤙..🤞) call me hand..crossed fingers + {0x1F91F, 0x1F91F, prExtendedPictographic}, // 10.0 [1] (🤟) love-you gesture + {0x1F920, 0x1F927, prExtendedPictographic}, // 9.0 [8] (🤠..🤧) cowboy hat face..sneezing face + {0x1F928, 0x1F92F, prExtendedPictographic}, // 10.0 [8] (🤨..🤯) face with raised eyebrow..exploding head + {0x1F930, 0x1F930, prExtendedPictographic}, // 9.0 [1] (🤰) pregnant woman + {0x1F931, 0x1F932, prExtendedPictographic}, // 10.0 [2] (🤱..🤲) breast-feeding..palms up together + {0x1F933, 0x1F93A, prExtendedPictographic}, // 9.0 [8] (🤳..🤺) selfie..person fencing + {0x1F93C, 0x1F93E, prExtendedPictographic}, // 9.0 [3] (🤼..🤾) people wrestling..person playing handball + {0x1F93F, 0x1F93F, prExtendedPictographic}, // 12.0 [1] (🤿) diving mask + {0x1F940, 0x1F945, prExtendedPictographic}, // 9.0 [6] (🥀..🥅) wilted flower..goal net + {0x1F947, 0x1F94B, prExtendedPictographic}, // 9.0 [5] (🥇..🥋) 1st place medal..martial arts uniform + {0x1F94C, 0x1F94C, prExtendedPictographic}, // 10.0 [1] (🥌) curling stone + {0x1F94D, 0x1F94F, prExtendedPictographic}, // 11.0 [3] (🥍..🥏) lacrosse..flying disc + {0x1F950, 0x1F95E, prExtendedPictographic}, // 9.0 [15] (🥐..🥞) croissant..pancakes + {0x1F95F, 0x1F96B, prExtendedPictographic}, // 10.0 [13] (🥟..🥫) dumpling..canned food + {0x1F96C, 0x1F970, prExtendedPictographic}, // 11.0 [5] (🥬..🥰) leafy green..smiling face with hearts + {0x1F971, 0x1F971, prExtendedPictographic}, // 12.0 [1] (🥱) yawning face + {0x1F972, 0x1F972, prExtendedPictographic}, // NA [1] (🥲) + {0x1F973, 0x1F976, prExtendedPictographic}, // 11.0 [4] (🥳..🥶) partying face..cold face + {0x1F977, 0x1F979, prExtendedPictographic}, // NA [3] (🥷..🥹) .. + {0x1F97A, 0x1F97A, prExtendedPictographic}, // 11.0 [1] (🥺) pleading face + {0x1F97B, 0x1F97B, prExtendedPictographic}, // 12.0 [1] (🥻) sari + {0x1F97C, 0x1F97F, prExtendedPictographic}, // 11.0 [4] (🥼..🥿) lab coat..flat shoe + {0x1F980, 0x1F984, prExtendedPictographic}, // 8.0 [5] (🦀..🦄) crab..unicorn + {0x1F985, 0x1F991, prExtendedPictographic}, // 9.0 [13] (🦅..🦑) eagle..squid + {0x1F992, 0x1F997, prExtendedPictographic}, // 10.0 [6] (🦒..🦗) giraffe..cricket + {0x1F998, 0x1F9A2, prExtendedPictographic}, // 11.0 [11] (🦘..🦢) kangaroo..swan + {0x1F9A3, 0x1F9A4, prExtendedPictographic}, // NA [2] (🦣..🦤) .. + {0x1F9A5, 0x1F9AA, prExtendedPictographic}, // 12.0 [6] (🦥..🦪) sloth..oyster + {0x1F9AB, 0x1F9AD, prExtendedPictographic}, // NA [3] (🦫..🦭) .. + {0x1F9AE, 0x1F9AF, prExtendedPictographic}, // 12.0 [2] (🦮..🦯) guide dog..probing cane + {0x1F9B0, 0x1F9B9, prExtendedPictographic}, // 11.0 [10] (🦰..🦹) red hair..supervillain + {0x1F9BA, 0x1F9BF, prExtendedPictographic}, // 12.0 [6] (🦺..🦿) safety vest..mechanical leg + {0x1F9C0, 0x1F9C0, prExtendedPictographic}, // 8.0 [1] (🧀) cheese wedge + {0x1F9C1, 0x1F9C2, prExtendedPictographic}, // 11.0 [2] (🧁..🧂) cupcake..salt + {0x1F9C3, 0x1F9CA, prExtendedPictographic}, // 12.0 [8] (🧃..🧊) beverage box..ice cube + {0x1F9CB, 0x1F9CC, prExtendedPictographic}, // NA [2] (🧋..🧌) .. + {0x1F9CD, 0x1F9CF, prExtendedPictographic}, // 12.0 [3] (🧍..🧏) person standing..deaf person + {0x1F9D0, 0x1F9E6, prExtendedPictographic}, // 10.0 [23] (🧐..🧦) face with monocle..socks + {0x1F9E7, 0x1F9FF, prExtendedPictographic}, // 11.0 [25] (🧧..🧿) red envelope..nazar amulet + {0x1FA00, 0x1FA53, prExtendedPictographic}, // 12.0 [84] (🨀..🩓) NEUTRAL CHESS KING..BLACK CHESS KNIGHT-BISHOP + {0x1FA54, 0x1FA5F, prExtendedPictographic}, // NA [12] (🩔..🩟) .. + {0x1FA60, 0x1FA6D, prExtendedPictographic}, // 11.0 [14] (🩠..🩭) XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER + {0x1FA6E, 0x1FA6F, prExtendedPictographic}, // NA [2] (🩮..🩯) .. + {0x1FA70, 0x1FA73, prExtendedPictographic}, // 12.0 [4] (🩰..🩳) ballet shoes..shorts + {0x1FA74, 0x1FA77, prExtendedPictographic}, // NA [4] (🩴..🩷) .. + {0x1FA78, 0x1FA7A, prExtendedPictographic}, // 12.0 [3] (🩸..🩺) drop of blood..stethoscope + {0x1FA7B, 0x1FA7F, prExtendedPictographic}, // NA [5] (🩻..🩿) .. + {0x1FA80, 0x1FA82, prExtendedPictographic}, // 12.0 [3] (🪀..🪂) yo-yo..parachute + {0x1FA83, 0x1FA8F, prExtendedPictographic}, // NA [13] (🪃..🪏) .. + {0x1FA90, 0x1FA95, prExtendedPictographic}, // 12.0 [6] (🪐..🪕) ringed planet..banjo + {0x1FA96, 0x1FFFD, prExtendedPictographic}, // NA[1384] (🪖..🿽) .. + {0xE0000, 0xE0000, prControl}, // Cn + {0xE0001, 0xE0001, prControl}, // Cf LANGUAGE TAG + {0xE0002, 0xE001F, prControl}, // Cn [30] .. + {0xE0020, 0xE007F, prExtend}, // Cf [96] TAG SPACE..CANCEL TAG + {0xE0080, 0xE00FF, prControl}, // Cn [128] .. + {0xE0100, 0xE01EF, prExtend}, // Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 + {0xE01F0, 0xE0FFF, prControl}, // Cn [3600] .. +} + +// property returns the Unicode property value (see constants above) of the +// given code point. +func property(r rune) int { + // Run a binary search. + from := 0 + to := len(codePoints) + for to > from { + middle := (from + to) / 2 + cpRange := codePoints[middle] + if int(r) < cpRange[0] { + to = middle + continue + } + if int(r) > cpRange[1] { + from = middle + 1 + continue + } + return cpRange[2] + } + return prAny +} diff --git a/vendor/github.com/sabhiram/go-gitignore/.gitignore b/vendor/github.com/sabhiram/go-gitignore/.gitignore new file mode 100644 index 0000000000..0e919aff1c --- /dev/null +++ b/vendor/github.com/sabhiram/go-gitignore/.gitignore @@ -0,0 +1,28 @@ +# Package test fixtures +test_fixtures + +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + diff --git a/vendor/github.com/sabhiram/go-gitignore/.travis.yml b/vendor/github.com/sabhiram/go-gitignore/.travis.yml new file mode 100644 index 0000000000..24ddadf1bf --- /dev/null +++ b/vendor/github.com/sabhiram/go-gitignore/.travis.yml @@ -0,0 +1,18 @@ +language: go + +go: + - 1.3 + - tip + +env: + - "PATH=$HOME/gopath/bin:$PATH" + +before_install: + - go get github.com/stretchr/testify/assert + - go get github.com/axw/gocov/gocov + - go get github.com/mattn/goveralls + - if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi + +script: + - go test -v -covermode=count -coverprofile=coverage.out + - goveralls -coverprofile=coverage.out -service travis-ci -repotoken $COVERALLS_TOKEN diff --git a/vendor/github.com/sabhiram/go-gitignore/LICENSE b/vendor/github.com/sabhiram/go-gitignore/LICENSE new file mode 100644 index 0000000000..c606f49e5c --- /dev/null +++ b/vendor/github.com/sabhiram/go-gitignore/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Shaba Abhiram + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/sabhiram/go-gitignore/README.md b/vendor/github.com/sabhiram/go-gitignore/README.md new file mode 100644 index 0000000000..9440d6dd41 --- /dev/null +++ b/vendor/github.com/sabhiram/go-gitignore/README.md @@ -0,0 +1,15 @@ +# go-gitignore + +[![Build Status](https://travis-ci.org/sabhiram/go-gitignore.svg)](https://travis-ci.org/sabhiram/go-gitignore) [![Coverage Status](https://coveralls.io/repos/github/sabhiram/go-gitignore/badge.svg?branch=master)](https://coveralls.io/github/sabhiram/go-gitignore?branch=master) + +A gitignore parser for `Go` + +## Install + +```shell +go get github.com/sabhiram/go-gitignore +``` + +## Usage + +For a quick sample of how to use this library, check out the tests under `ignore_test.go`. diff --git a/vendor/github.com/sabhiram/go-gitignore/ignore.go b/vendor/github.com/sabhiram/go-gitignore/ignore.go new file mode 100644 index 0000000000..a155113cac --- /dev/null +++ b/vendor/github.com/sabhiram/go-gitignore/ignore.go @@ -0,0 +1,225 @@ +/* +ignore is a library which returns a new ignorer object which can +test against various paths. This is particularly useful when trying +to filter files based on a .gitignore document + +The rules for parsing the input file are the same as the ones listed +in the Git docs here: http://git-scm.com/docs/gitignore + +The summarized version of the same has been copied here: + + 1. A blank line matches no files, so it can serve as a separator + for readability. + 2. A line starting with # serves as a comment. Put a backslash ("\") + in front of the first hash for patterns that begin with a hash. + 3. Trailing spaces are ignored unless they are quoted with backslash ("\"). + 4. An optional prefix "!" which negates the pattern; any matching file + excluded by a previous pattern will become included again. It is not + possible to re-include a file if a parent directory of that file is + excluded. Git doesn’t list excluded directories for performance reasons, + so any patterns on contained files have no effect, no matter where they + are defined. Put a backslash ("\") in front of the first "!" for + patterns that begin with a literal "!", for example, "\!important!.txt". + 5. If the pattern ends with a slash, it is removed for the purpose of the + following description, but it would only find a match with a directory. + In other words, foo/ will match a directory foo and paths underneath it, + but will not match a regular file or a symbolic link foo (this is + consistent with the way how pathspec works in general in Git). + 6. If the pattern does not contain a slash /, Git treats it as a shell glob + pattern and checks for a match against the pathname relative to the + location of the .gitignore file (relative to the toplevel of the work + tree if not from a .gitignore file). + 7. Otherwise, Git treats the pattern as a shell glob suitable for + consumption by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the + pattern will not match a / in the pathname. For example, + "Documentation/*.html" matches "Documentation/git.html" but not + "Documentation/ppc/ppc.html" or "tools/perf/Documentation/perf.html". + 8. A leading slash matches the beginning of the pathname. For example, + "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c". + 9. Two consecutive asterisks ("**") in patterns matched against full + pathname may have special meaning: + i. A leading "**" followed by a slash means match in all directories. + For example, "** /foo" matches file or directory "foo" anywhere, + the same as pattern "foo". "** /foo/bar" matches file or directory + "bar" anywhere that is directly under directory "foo". + ii. A trailing "/**" matches everything inside. For example, "abc/**" + matches all files inside directory "abc", relative to the location + of the .gitignore file, with infinite depth. + iii. A slash followed by two consecutive asterisks then a slash matches + zero or more directories. For example, "a/** /b" matches "a/b", + "a/x/b", "a/x/y/b" and so on. + iv. Other consecutive asterisks are considered invalid. */ +package ignore + +import ( + "io/ioutil" + "os" + "regexp" + "strings" +) + +//////////////////////////////////////////////////////////// + +// IgnoreParser is an interface with `MatchesPaths`. +type IgnoreParser interface { + MatchesPath(f string) bool +} + +//////////////////////////////////////////////////////////// + +// This function pretty much attempts to mimic the parsing rules +// listed above at the start of this file +func getPatternFromLine(line string) (*regexp.Regexp, bool) { + // Trim OS-specific carriage returns. + line = strings.TrimRight(line, "\r") + + // Strip comments [Rule 2] + if strings.HasPrefix(line, `#`) { + return nil, false + } + + // Trim string [Rule 3] + // TODO: Handle [Rule 3], when the " " is escaped with a \ + line = strings.Trim(line, " ") + + // Exit for no-ops and return nil which will prevent us from + // appending a pattern against this line + if line == "" { + return nil, false + } + + // TODO: Handle [Rule 4] which negates the match for patterns leading with "!" + negatePattern := false + if line[0] == '!' { + negatePattern = true + line = line[1:] + } + + // Handle [Rule 2, 4], when # or ! is escaped with a \ + // Handle [Rule 4] once we tag negatePattern, strip the leading ! char + if regexp.MustCompile(`^(\#|\!)`).MatchString(line) { + line = line[1:] + } + + // If we encounter a foo/*.blah in a folder, prepend the / char + if regexp.MustCompile(`([^\/+])/.*\*\.`).MatchString(line) && line[0] != '/' { + line = "/" + line + } + + // Handle escaping the "." char + line = regexp.MustCompile(`\.`).ReplaceAllString(line, `\.`) + + magicStar := "#$~" + + // Handle "/**/" usage + if strings.HasPrefix(line, "/**/") { + line = line[1:] + } + line = regexp.MustCompile(`/\*\*/`).ReplaceAllString(line, `(/|/.+/)`) + line = regexp.MustCompile(`\*\*/`).ReplaceAllString(line, `(|.`+magicStar+`/)`) + line = regexp.MustCompile(`/\*\*`).ReplaceAllString(line, `(|/.`+magicStar+`)`) + + // Handle escaping the "*" char + line = regexp.MustCompile(`\\\*`).ReplaceAllString(line, `\`+magicStar) + line = regexp.MustCompile(`\*`).ReplaceAllString(line, `([^/]*)`) + + // Handle escaping the "?" char + line = strings.Replace(line, "?", `\?`, -1) + + line = strings.Replace(line, magicStar, "*", -1) + + // Temporary regex + var expr = "" + if strings.HasSuffix(line, "/") { + expr = line + "(|.*)$" + } else { + expr = line + "(|/.*)$" + } + if strings.HasPrefix(expr, "/") { + expr = "^(|/)" + expr[1:] + } else { + expr = "^(|.*/)" + expr + } + pattern, _ := regexp.Compile(expr) + + return pattern, negatePattern +} + +//////////////////////////////////////////////////////////// + +// ignorePattern encapsulates a pattern and if it is a negated pattern. +type ignorePattern struct { + pattern *regexp.Regexp + negate bool +} + +// GitIgnore wraps a list of ignore pattern. +type GitIgnore struct { + patterns []*ignorePattern +} + +// CompileIgnoreLines accepts a variadic set of strings, and returns a GitIgnore +// instance which converts and appends the lines in the input to regexp.Regexp +// patterns held within the GitIgnore objects "patterns" field. +func CompileIgnoreLines(lines ...string) *GitIgnore { + gi := &GitIgnore{} + for _, line := range lines { + pattern, negatePattern := getPatternFromLine(line) + if pattern != nil { + ip := &ignorePattern{pattern, negatePattern} + gi.patterns = append(gi.patterns, ip) + } + } + return gi +} + +// CompileIgnoreFile uses an ignore file as the input, parses the lines out of +// the file and invokes the CompileIgnoreLines method. +func CompileIgnoreFile(fpath string) (*GitIgnore, error) { + bs, err := ioutil.ReadFile(fpath) + if err != nil { + return nil, err + } + + s := strings.Split(string(bs), "\n") + return CompileIgnoreLines(s...), nil +} + +// CompileIgnoreFileAndLines accepts a ignore file as the input, parses the +// lines out of the file and invokes the CompileIgnoreLines method with +// additional lines. +func CompileIgnoreFileAndLines(fpath string, lines ...string) (*GitIgnore, error) { + bs, err := ioutil.ReadFile(fpath) + if err != nil { + return nil, err + } + + gi := CompileIgnoreLines(append(strings.Split(string(bs), "\n"), lines...)...) + return gi, nil +} + +//////////////////////////////////////////////////////////// + +// MatchesPath returns true if the given GitIgnore structure would target +// a given path string `f`. +func (gi *GitIgnore) MatchesPath(f string) bool { + // Replace OS-specific path separator. + f = strings.Replace(f, string(os.PathSeparator), "/", -1) + + matchesPath := false + for _, ip := range gi.patterns { + if ip.pattern.MatchString(f) { + // If this is a regular target (not negated with a gitignore + // exclude "!" etc) + if !ip.negate { + matchesPath = true + } else if matchesPath { + // Negated pattern, and matchesPath is already set + matchesPath = false + } + } + } + return matchesPath +} + +//////////////////////////////////////////////////////////// diff --git a/vendor/github.com/sabhiram/go-gitignore/version_gen.go b/vendor/github.com/sabhiram/go-gitignore/version_gen.go new file mode 100644 index 0000000000..7504effe03 --- /dev/null +++ b/vendor/github.com/sabhiram/go-gitignore/version_gen.go @@ -0,0 +1,12 @@ +package ignore + +// WARNING: Auto generated version file. Do not edit this file by hand. +// WARNING: go get github.com/sabhiram/gover to manage this file. +// Version: 1.1.0 +const ( + Major = 1 + Minor = 1 + Patch = 0 + + Version = "1.1.0" +) diff --git a/vendor/github.com/sirupsen/logrus/.gitignore b/vendor/github.com/sirupsen/logrus/.gitignore new file mode 100644 index 0000000000..1fb13abebe --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/.gitignore @@ -0,0 +1,4 @@ +logrus +vendor + +.idea/ diff --git a/vendor/github.com/sirupsen/logrus/.golangci.yml b/vendor/github.com/sirupsen/logrus/.golangci.yml new file mode 100644 index 0000000000..65dc285037 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/.golangci.yml @@ -0,0 +1,40 @@ +run: + # do not run on test files yet + tests: false + +# all available settings of specific linters +linters-settings: + errcheck: + # report about not checking of errors in type assetions: `a := b.(MyStruct)`; + # default is false: such cases aren't reported by default. + check-type-assertions: false + + # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; + # default is false: such cases aren't reported by default. + check-blank: false + + lll: + line-length: 100 + tab-width: 4 + + prealloc: + simple: false + range-loops: false + for-loops: false + + whitespace: + multi-if: false # Enforces newlines (or comments) after every multi-line if statement + multi-func: false # Enforces newlines (or comments) after every multi-line function signature + +linters: + enable: + - megacheck + - govet + disable: + - maligned + - prealloc + disable-all: false + presets: + - bugs + - unused + fast: false diff --git a/vendor/github.com/sirupsen/logrus/.travis.yml b/vendor/github.com/sirupsen/logrus/.travis.yml new file mode 100644 index 0000000000..c1dbd5a3a3 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/.travis.yml @@ -0,0 +1,15 @@ +language: go +go_import_path: github.com/sirupsen/logrus +git: + depth: 1 +env: + - GO111MODULE=on +go: 1.15.x +os: linux +install: + - ./travis/install.sh +script: + - cd ci + - go run mage.go -v -w ../ crossBuild + - go run mage.go -v -w ../ lint + - go run mage.go -v -w ../ test diff --git a/vendor/github.com/sirupsen/logrus/CHANGELOG.md b/vendor/github.com/sirupsen/logrus/CHANGELOG.md new file mode 100644 index 0000000000..7567f61289 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/CHANGELOG.md @@ -0,0 +1,259 @@ +# 1.8.1 +Code quality: + * move magefile in its own subdir/submodule to remove magefile dependency on logrus consumer + * improve timestamp format documentation + +Fixes: + * fix race condition on logger hooks + + +# 1.8.0 + +Correct versioning number replacing v1.7.1. + +# 1.7.1 + +Beware this release has introduced a new public API and its semver is therefore incorrect. + +Code quality: + * use go 1.15 in travis + * use magefile as task runner + +Fixes: + * small fixes about new go 1.13 error formatting system + * Fix for long time race condiction with mutating data hooks + +Features: + * build support for zos + +# 1.7.0 +Fixes: + * the dependency toward a windows terminal library has been removed + +Features: + * a new buffer pool management API has been added + * a set of `Fn()` functions have been added + +# 1.6.0 +Fixes: + * end of line cleanup + * revert the entry concurrency bug fix whic leads to deadlock under some circumstances + * update dependency on go-windows-terminal-sequences to fix a crash with go 1.14 + +Features: + * add an option to the `TextFormatter` to completely disable fields quoting + +# 1.5.0 +Code quality: + * add golangci linter run on travis + +Fixes: + * add mutex for hooks concurrent access on `Entry` data + * caller function field for go1.14 + * fix build issue for gopherjs target + +Feature: + * add an hooks/writer sub-package whose goal is to split output on different stream depending on the trace level + * add a `DisableHTMLEscape` option in the `JSONFormatter` + * add `ForceQuote` and `PadLevelText` options in the `TextFormatter` + +# 1.4.2 + * Fixes build break for plan9, nacl, solaris +# 1.4.1 +This new release introduces: + * Enhance TextFormatter to not print caller information when they are empty (#944) + * Remove dependency on golang.org/x/crypto (#932, #943) + +Fixes: + * Fix Entry.WithContext method to return a copy of the initial entry (#941) + +# 1.4.0 +This new release introduces: + * Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848). + * Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter` (#909, #911) + * Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919). + +Fixes: + * Fix wrong method calls `Logger.Print` and `Logger.Warningln` (#893). + * Update `Entry.Logf` to not do string formatting unless the log level is enabled (#903) + * Fix infinite recursion on unknown `Level.String()` (#907) + * Fix race condition in `getCaller` (#916). + + +# 1.3.0 +This new release introduces: + * Log, Logf, Logln functions for Logger and Entry that take a Level + +Fixes: + * Building prometheus node_exporter on AIX (#840) + * Race condition in TextFormatter (#468) + * Travis CI import path (#868) + * Remove coloured output on Windows (#862) + * Pointer to func as field in JSONFormatter (#870) + * Properly marshal Levels (#873) + +# 1.2.0 +This new release introduces: + * A new method `SetReportCaller` in the `Logger` to enable the file, line and calling function from which the trace has been issued + * A new trace level named `Trace` whose level is below `Debug` + * A configurable exit function to be called upon a Fatal trace + * The `Level` object now implements `encoding.TextUnmarshaler` interface + +# 1.1.1 +This is a bug fix release. + * fix the build break on Solaris + * don't drop a whole trace in JSONFormatter when a field param is a function pointer which can not be serialized + +# 1.1.0 +This new release introduces: + * several fixes: + * a fix for a race condition on entry formatting + * proper cleanup of previously used entries before putting them back in the pool + * the extra new line at the end of message in text formatter has been removed + * a new global public API to check if a level is activated: IsLevelEnabled + * the following methods have been added to the Logger object + * IsLevelEnabled + * SetFormatter + * SetOutput + * ReplaceHooks + * introduction of go module + * an indent configuration for the json formatter + * output colour support for windows + * the field sort function is now configurable for text formatter + * the CLICOLOR and CLICOLOR\_FORCE environment variable support in text formater + +# 1.0.6 + +This new release introduces: + * a new api WithTime which allows to easily force the time of the log entry + which is mostly useful for logger wrapper + * a fix reverting the immutability of the entry given as parameter to the hooks + a new configuration field of the json formatter in order to put all the fields + in a nested dictionnary + * a new SetOutput method in the Logger + * a new configuration of the textformatter to configure the name of the default keys + * a new configuration of the text formatter to disable the level truncation + +# 1.0.5 + +* Fix hooks race (#707) +* Fix panic deadlock (#695) + +# 1.0.4 + +* Fix race when adding hooks (#612) +* Fix terminal check in AppEngine (#635) + +# 1.0.3 + +* Replace example files with testable examples + +# 1.0.2 + +* bug: quote non-string values in text formatter (#583) +* Make (*Logger) SetLevel a public method + +# 1.0.1 + +* bug: fix escaping in text formatter (#575) + +# 1.0.0 + +* Officially changed name to lower-case +* bug: colors on Windows 10 (#541) +* bug: fix race in accessing level (#512) + +# 0.11.5 + +* feature: add writer and writerlevel to entry (#372) + +# 0.11.4 + +* bug: fix undefined variable on solaris (#493) + +# 0.11.3 + +* formatter: configure quoting of empty values (#484) +* formatter: configure quoting character (default is `"`) (#484) +* bug: fix not importing io correctly in non-linux environments (#481) + +# 0.11.2 + +* bug: fix windows terminal detection (#476) + +# 0.11.1 + +* bug: fix tty detection with custom out (#471) + +# 0.11.0 + +* performance: Use bufferpool to allocate (#370) +* terminal: terminal detection for app-engine (#343) +* feature: exit handler (#375) + +# 0.10.0 + +* feature: Add a test hook (#180) +* feature: `ParseLevel` is now case-insensitive (#326) +* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308) +* performance: avoid re-allocations on `WithFields` (#335) + +# 0.9.0 + +* logrus/text_formatter: don't emit empty msg +* logrus/hooks/airbrake: move out of main repository +* logrus/hooks/sentry: move out of main repository +* logrus/hooks/papertrail: move out of main repository +* logrus/hooks/bugsnag: move out of main repository +* logrus/core: run tests with `-race` +* logrus/core: detect TTY based on `stderr` +* logrus/core: support `WithError` on logger +* logrus/core: Solaris support + +# 0.8.7 + +* logrus/core: fix possible race (#216) +* logrus/doc: small typo fixes and doc improvements + + +# 0.8.6 + +* hooks/raven: allow passing an initialized client + +# 0.8.5 + +* logrus/core: revert #208 + +# 0.8.4 + +* formatter/text: fix data race (#218) + +# 0.8.3 + +* logrus/core: fix entry log level (#208) +* logrus/core: improve performance of text formatter by 40% +* logrus/core: expose `LevelHooks` type +* logrus/core: add support for DragonflyBSD and NetBSD +* formatter/text: print structs more verbosely + +# 0.8.2 + +* logrus: fix more Fatal family functions + +# 0.8.1 + +* logrus: fix not exiting on `Fatalf` and `Fatalln` + +# 0.8.0 + +* logrus: defaults to stderr instead of stdout +* hooks/sentry: add special field for `*http.Request` +* formatter/text: ignore Windows for colors + +# 0.7.3 + +* formatter/\*: allow configuration of timestamp layout + +# 0.7.2 + +* formatter/text: Add configuration option for time format (#158) diff --git a/vendor/github.com/sirupsen/logrus/LICENSE b/vendor/github.com/sirupsen/logrus/LICENSE new file mode 100644 index 0000000000..f090cb42f3 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Simon Eskildsen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/sirupsen/logrus/README.md b/vendor/github.com/sirupsen/logrus/README.md new file mode 100644 index 0000000000..5152b6aa40 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/README.md @@ -0,0 +1,513 @@ +# Logrus :walrus: [![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/sirupsen/logrus?status.svg)](https://godoc.org/github.com/sirupsen/logrus) + +Logrus is a structured logger for Go (golang), completely API compatible with +the standard library logger. + +**Logrus is in maintenance-mode.** We will not be introducing new features. It's +simply too hard to do in a way that won't break many people's projects, which is +the last thing you want from your Logging library (again...). + +This does not mean Logrus is dead. Logrus will continue to be maintained for +security, (backwards compatible) bug fixes, and performance (where we are +limited by the interface). + +I believe Logrus' biggest contribution is to have played a part in today's +widespread use of structured logging in Golang. There doesn't seem to be a +reason to do a major, breaking iteration into Logrus V2, since the fantastic Go +community has built those independently. Many fantastic alternatives have sprung +up. Logrus would look like those, had it been re-designed with what we know +about structured logging in Go today. Check out, for example, +[Zerolog][zerolog], [Zap][zap], and [Apex][apex]. + +[zerolog]: https://github.com/rs/zerolog +[zap]: https://github.com/uber-go/zap +[apex]: https://github.com/apex/log + +**Seeing weird case-sensitive problems?** It's in the past been possible to +import Logrus as both upper- and lower-case. Due to the Go package environment, +this caused issues in the community and we needed a standard. Some environments +experienced problems with the upper-case variant, so the lower-case was decided. +Everything using `logrus` will need to use the lower-case: +`github.com/sirupsen/logrus`. Any package that isn't, should be changed. + +To fix Glide, see [these +comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437). +For an in-depth explanation of the casing issue, see [this +comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276). + +Nicely color-coded in development (when a TTY is attached, otherwise just +plain text): + +![Colored](http://i.imgur.com/PY7qMwd.png) + +With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash +or Splunk: + +```json +{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the +ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"} + +{"level":"warning","msg":"The group's number increased tremendously!", +"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"} + +{"animal":"walrus","level":"info","msg":"A giant walrus appears!", +"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"} + +{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.", +"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"} + +{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true, +"time":"2014-03-10 19:57:38.562543128 -0400 EDT"} +``` + +With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not +attached, the output is compatible with the +[logfmt](http://godoc.org/github.com/kr/logfmt) format: + +```text +time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8 +time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10 +time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true +time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4 +time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009 +time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true +``` +To ensure this behaviour even if a TTY is attached, set your formatter as follows: + +```go + log.SetFormatter(&log.TextFormatter{ + DisableColors: true, + FullTimestamp: true, + }) +``` + +#### Logging Method Name + +If you wish to add the calling method as a field, instruct the logger via: +```go +log.SetReportCaller(true) +``` +This adds the caller as 'method' like so: + +```json +{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by", +"time":"2014-03-10 19:57:38.562543129 -0400 EDT"} +``` + +```text +time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin +``` +Note that this does add measurable overhead - the cost will depend on the version of Go, but is +between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your +environment via benchmarks: +``` +go test -bench=.*CallerTracing +``` + + +#### Case-sensitivity + +The organization's name was changed to lower-case--and this will not be changed +back. If you are getting import conflicts due to case sensitivity, please use +the lower-case import: `github.com/sirupsen/logrus`. + +#### Example + +The simplest way to use Logrus is simply the package-level exported logger: + +```go +package main + +import ( + log "github.com/sirupsen/logrus" +) + +func main() { + log.WithFields(log.Fields{ + "animal": "walrus", + }).Info("A walrus appears") +} +``` + +Note that it's completely api-compatible with the stdlib logger, so you can +replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"` +and you'll now have the flexibility of Logrus. You can customize it all you +want: + +```go +package main + +import ( + "os" + log "github.com/sirupsen/logrus" +) + +func init() { + // Log as JSON instead of the default ASCII formatter. + log.SetFormatter(&log.JSONFormatter{}) + + // Output to stdout instead of the default stderr + // Can be any io.Writer, see below for File example + log.SetOutput(os.Stdout) + + // Only log the warning severity or above. + log.SetLevel(log.WarnLevel) +} + +func main() { + log.WithFields(log.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") + + log.WithFields(log.Fields{ + "omg": true, + "number": 122, + }).Warn("The group's number increased tremendously!") + + log.WithFields(log.Fields{ + "omg": true, + "number": 100, + }).Fatal("The ice breaks!") + + // A common pattern is to re-use fields between logging statements by re-using + // the logrus.Entry returned from WithFields() + contextLogger := log.WithFields(log.Fields{ + "common": "this is a common field", + "other": "I also should be logged always", + }) + + contextLogger.Info("I'll be logged with common and other field") + contextLogger.Info("Me too") +} +``` + +For more advanced usage such as logging to multiple locations from the same +application, you can also create an instance of the `logrus` Logger: + +```go +package main + +import ( + "os" + "github.com/sirupsen/logrus" +) + +// Create a new instance of the logger. You can have any number of instances. +var log = logrus.New() + +func main() { + // The API for setting attributes is a little different than the package level + // exported logger. See Godoc. + log.Out = os.Stdout + + // You could set this to any `io.Writer` such as a file + // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + // if err == nil { + // log.Out = file + // } else { + // log.Info("Failed to log to file, using default stderr") + // } + + log.WithFields(logrus.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") +} +``` + +#### Fields + +Logrus encourages careful, structured logging through logging fields instead of +long, unparseable error messages. For example, instead of: `log.Fatalf("Failed +to send event %s to topic %s with key %d")`, you should log the much more +discoverable: + +```go +log.WithFields(log.Fields{ + "event": event, + "topic": topic, + "key": key, +}).Fatal("Failed to send event") +``` + +We've found this API forces you to think about logging in a way that produces +much more useful logging messages. We've been in countless situations where just +a single added field to a log statement that was already there would've saved us +hours. The `WithFields` call is optional. + +In general, with Logrus using any of the `printf`-family functions should be +seen as a hint you should add a field, however, you can still use the +`printf`-family functions with Logrus. + +#### Default Fields + +Often it's helpful to have fields _always_ attached to log statements in an +application or parts of one. For example, you may want to always log the +`request_id` and `user_ip` in the context of a request. Instead of writing +`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on +every line, you can create a `logrus.Entry` to pass around instead: + +```go +requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip}) +requestLogger.Info("something happened on that request") # will log request_id and user_ip +requestLogger.Warn("something not great happened") +``` + +#### Hooks + +You can add hooks for logging levels. For example to send errors to an exception +tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to +multiple places simultaneously, e.g. syslog. + +Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in +`init`: + +```go +import ( + log "github.com/sirupsen/logrus" + "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake" + logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" + "log/syslog" +) + +func init() { + + // Use the Airbrake hook to report errors that have Error severity or above to + // an exception tracker. You can create custom hooks, see the Hooks section. + log.AddHook(airbrake.NewHook(123, "xyz", "production")) + + hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") + if err != nil { + log.Error("Unable to connect to local syslog daemon") + } else { + log.AddHook(hook) + } +} +``` +Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md). + +A list of currently known service hooks can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks) + + +#### Level logging + +Logrus has seven logging levels: Trace, Debug, Info, Warning, Error, Fatal and Panic. + +```go +log.Trace("Something very low level.") +log.Debug("Useful debugging information.") +log.Info("Something noteworthy happened!") +log.Warn("You should probably take a look at this.") +log.Error("Something failed but I'm not quitting.") +// Calls os.Exit(1) after logging +log.Fatal("Bye.") +// Calls panic() after logging +log.Panic("I'm bailing.") +``` + +You can set the logging level on a `Logger`, then it will only log entries with +that severity or anything above it: + +```go +// Will log anything that is info or above (warn, error, fatal, panic). Default. +log.SetLevel(log.InfoLevel) +``` + +It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose +environment if your application has that. + +#### Entries + +Besides the fields added with `WithField` or `WithFields` some fields are +automatically added to all logging events: + +1. `time`. The timestamp when the entry was created. +2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after + the `AddFields` call. E.g. `Failed to send event.` +3. `level`. The logging level. E.g. `info`. + +#### Environments + +Logrus has no notion of environment. + +If you wish for hooks and formatters to only be used in specific environments, +you should handle that yourself. For example, if your application has a global +variable `Environment`, which is a string representation of the environment you +could do: + +```go +import ( + log "github.com/sirupsen/logrus" +) + +init() { + // do something here to set environment depending on an environment variable + // or command-line flag + if Environment == "production" { + log.SetFormatter(&log.JSONFormatter{}) + } else { + // The TextFormatter is default, you don't actually have to do this. + log.SetFormatter(&log.TextFormatter{}) + } +} +``` + +This configuration is how `logrus` was intended to be used, but JSON in +production is mostly only useful if you do log aggregation with tools like +Splunk or Logstash. + +#### Formatters + +The built-in logging formatters are: + +* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise + without colors. + * *Note:* to force colored output when there is no TTY, set the `ForceColors` + field to `true`. To force no colored output even if there is a TTY set the + `DisableColors` field to `true`. For Windows, see + [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable). + * When colors are enabled, levels are truncated to 4 characters by default. To disable + truncation set the `DisableLevelTruncation` field to `true`. + * When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text. + * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter). +* `logrus.JSONFormatter`. Logs fields as JSON. + * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter). + +Third party logging formatters: + +* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine. +* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html). +* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events. +* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout. +* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the Power of Zalgo. +* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure. +* [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Sava log to files. +* [`caption-json-formatter`](https://github.com/nolleh/caption_json_formatter). logrus's message json formatter with human-readable caption added. + +You can define your formatter by implementing the `Formatter` interface, +requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a +`Fields` type (`map[string]interface{}`) with all your fields as well as the +default ones (see Entries section above): + +```go +type MyJSONFormatter struct { +} + +log.SetFormatter(new(MyJSONFormatter)) + +func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) { + // Note this doesn't include Time, Level and Message which are available on + // the Entry. Consult `godoc` on information about those fields or read the + // source of the official loggers. + serialized, err := json.Marshal(entry.Data) + if err != nil { + return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err) + } + return append(serialized, '\n'), nil +} +``` + +#### Logger as an `io.Writer` + +Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it. + +```go +w := logger.Writer() +defer w.Close() + +srv := http.Server{ + // create a stdlib log.Logger that writes to + // logrus.Logger. + ErrorLog: log.New(w, "", 0), +} +``` + +Each line written to that writer will be printed the usual way, using formatters +and hooks. The level for those entries is `info`. + +This means that we can override the standard library logger easily: + +```go +logger := logrus.New() +logger.Formatter = &logrus.JSONFormatter{} + +// Use logrus for standard log output +// Note that `log` here references stdlib's log +// Not logrus imported under the name `log`. +log.SetOutput(logger.Writer()) +``` + +#### Rotation + +Log rotation is not provided with Logrus. Log rotation should be done by an +external program (like `logrotate(8)`) that can compress and delete old log +entries. It should not be a feature of the application-level logger. + +#### Tools + +| Tool | Description | +| ---- | ----------- | +|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will be generated with different configs in different environments.| +|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) | + +#### Testing + +Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides: + +* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just adds the `test` hook +* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any): + +```go +import( + "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestSomething(t*testing.T){ + logger, hook := test.NewNullLogger() + logger.Error("Helloerror") + + assert.Equal(t, 1, len(hook.Entries)) + assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level) + assert.Equal(t, "Helloerror", hook.LastEntry().Message) + + hook.Reset() + assert.Nil(t, hook.LastEntry()) +} +``` + +#### Fatal handlers + +Logrus can register one or more functions that will be called when any `fatal` +level message is logged. The registered handlers will be executed before +logrus performs an `os.Exit(1)`. This behavior may be helpful if callers need +to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted. + +``` +... +handler := func() { + // gracefully shutdown something... +} +logrus.RegisterExitHandler(handler) +... +``` + +#### Thread safety + +By default, Logger is protected by a mutex for concurrent writes. The mutex is held when calling hooks and writing logs. +If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking. + +Situation when locking is not needed includes: + +* You have no hooks registered, or hooks calling is already thread-safe. + +* Writing to logger.Out is already thread-safe, for example: + + 1) logger.Out is protected by locks. + + 2) logger.Out is an os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allows multi-thread/multi-process writing) + + (Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/) diff --git a/vendor/github.com/sirupsen/logrus/alt_exit.go b/vendor/github.com/sirupsen/logrus/alt_exit.go new file mode 100644 index 0000000000..8fd189e1cc --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/alt_exit.go @@ -0,0 +1,76 @@ +package logrus + +// The following code was sourced and modified from the +// https://github.com/tebeka/atexit package governed by the following license: +// +// Copyright (c) 2012 Miki Tebeka . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import ( + "fmt" + "os" +) + +var handlers = []func(){} + +func runHandler(handler func()) { + defer func() { + if err := recover(); err != nil { + fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err) + } + }() + + handler() +} + +func runHandlers() { + for _, handler := range handlers { + runHandler(handler) + } +} + +// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code) +func Exit(code int) { + runHandlers() + os.Exit(code) +} + +// RegisterExitHandler appends a Logrus Exit handler to the list of handlers, +// call logrus.Exit to invoke all handlers. The handlers will also be invoked when +// any Fatal log entry is made. +// +// This method is useful when a caller wishes to use logrus to log a fatal +// message but also needs to gracefully shutdown. An example usecase could be +// closing database connections, or sending a alert that the application is +// closing. +func RegisterExitHandler(handler func()) { + handlers = append(handlers, handler) +} + +// DeferExitHandler prepends a Logrus Exit handler to the list of handlers, +// call logrus.Exit to invoke all handlers. The handlers will also be invoked when +// any Fatal log entry is made. +// +// This method is useful when a caller wishes to use logrus to log a fatal +// message but also needs to gracefully shutdown. An example usecase could be +// closing database connections, or sending a alert that the application is +// closing. +func DeferExitHandler(handler func()) { + handlers = append([]func(){handler}, handlers...) +} diff --git a/vendor/github.com/sirupsen/logrus/appveyor.yml b/vendor/github.com/sirupsen/logrus/appveyor.yml new file mode 100644 index 0000000000..df9d65c3a5 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/appveyor.yml @@ -0,0 +1,14 @@ +version: "{build}" +platform: x64 +clone_folder: c:\gopath\src\github.com\sirupsen\logrus +environment: + GOPATH: c:\gopath +branches: + only: + - master +install: + - set PATH=%GOPATH%\bin;c:\go\bin;%PATH% + - go version +build_script: + - go get -t + - go test diff --git a/vendor/github.com/sirupsen/logrus/buffer_pool.go b/vendor/github.com/sirupsen/logrus/buffer_pool.go new file mode 100644 index 0000000000..4545dec07d --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/buffer_pool.go @@ -0,0 +1,52 @@ +package logrus + +import ( + "bytes" + "sync" +) + +var ( + bufferPool BufferPool +) + +type BufferPool interface { + Put(*bytes.Buffer) + Get() *bytes.Buffer +} + +type defaultPool struct { + pool *sync.Pool +} + +func (p *defaultPool) Put(buf *bytes.Buffer) { + p.pool.Put(buf) +} + +func (p *defaultPool) Get() *bytes.Buffer { + return p.pool.Get().(*bytes.Buffer) +} + +func getBuffer() *bytes.Buffer { + return bufferPool.Get() +} + +func putBuffer(buf *bytes.Buffer) { + buf.Reset() + bufferPool.Put(buf) +} + +// SetBufferPool allows to replace the default logrus buffer pool +// to better meets the specific needs of an application. +func SetBufferPool(bp BufferPool) { + bufferPool = bp +} + +func init() { + SetBufferPool(&defaultPool{ + pool: &sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, + }, + }) +} diff --git a/vendor/github.com/sirupsen/logrus/doc.go b/vendor/github.com/sirupsen/logrus/doc.go new file mode 100644 index 0000000000..da67aba06d --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/doc.go @@ -0,0 +1,26 @@ +/* +Package logrus is a structured logger for Go, completely API compatible with the standard library logger. + + +The simplest way to use Logrus is simply the package-level exported logger: + + package main + + import ( + log "github.com/sirupsen/logrus" + ) + + func main() { + log.WithFields(log.Fields{ + "animal": "walrus", + "number": 1, + "size": 10, + }).Info("A walrus appears") + } + +Output: + time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10 + +For a full guide visit https://github.com/sirupsen/logrus +*/ +package logrus diff --git a/vendor/github.com/sirupsen/logrus/entry.go b/vendor/github.com/sirupsen/logrus/entry.go new file mode 100644 index 0000000000..07a1e5fa72 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/entry.go @@ -0,0 +1,431 @@ +package logrus + +import ( + "bytes" + "context" + "fmt" + "os" + "reflect" + "runtime" + "strings" + "sync" + "time" +) + +var ( + + // qualified package name, cached at first use + logrusPackage string + + // Positions in the call stack when tracing to report the calling method + minimumCallerDepth int + + // Used for caller information initialisation + callerInitOnce sync.Once +) + +const ( + maximumCallerDepth int = 25 + knownLogrusFrames int = 4 +) + +func init() { + // start at the bottom of the stack before the package-name cache is primed + minimumCallerDepth = 1 +} + +// Defines the key when adding errors using WithError. +var ErrorKey = "error" + +// An entry is the final or intermediate Logrus logging entry. It contains all +// the fields passed with WithField{,s}. It's finally logged when Trace, Debug, +// Info, Warn, Error, Fatal or Panic is called on it. These objects can be +// reused and passed around as much as you wish to avoid field duplication. +type Entry struct { + Logger *Logger + + // Contains all the fields set by the user. + Data Fields + + // Time at which the log entry was created + Time time.Time + + // Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic + // This field will be set on entry firing and the value will be equal to the one in Logger struct field. + Level Level + + // Calling method, with package name + Caller *runtime.Frame + + // Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic + Message string + + // When formatter is called in entry.log(), a Buffer may be set to entry + Buffer *bytes.Buffer + + // Contains the context set by the user. Useful for hook processing etc. + Context context.Context + + // err may contain a field formatting error + err string +} + +func NewEntry(logger *Logger) *Entry { + return &Entry{ + Logger: logger, + // Default is three fields, plus one optional. Give a little extra room. + Data: make(Fields, 6), + } +} + +func (entry *Entry) Dup() *Entry { + data := make(Fields, len(entry.Data)) + for k, v := range entry.Data { + data[k] = v + } + return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err} +} + +// Returns the bytes representation of this entry from the formatter. +func (entry *Entry) Bytes() ([]byte, error) { + return entry.Logger.Formatter.Format(entry) +} + +// Returns the string representation from the reader and ultimately the +// formatter. +func (entry *Entry) String() (string, error) { + serialized, err := entry.Bytes() + if err != nil { + return "", err + } + str := string(serialized) + return str, nil +} + +// Add an error as single field (using the key defined in ErrorKey) to the Entry. +func (entry *Entry) WithError(err error) *Entry { + return entry.WithField(ErrorKey, err) +} + +// Add a context to the Entry. +func (entry *Entry) WithContext(ctx context.Context) *Entry { + dataCopy := make(Fields, len(entry.Data)) + for k, v := range entry.Data { + dataCopy[k] = v + } + return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx} +} + +// Add a single field to the Entry. +func (entry *Entry) WithField(key string, value interface{}) *Entry { + return entry.WithFields(Fields{key: value}) +} + +// Add a map of fields to the Entry. +func (entry *Entry) WithFields(fields Fields) *Entry { + data := make(Fields, len(entry.Data)+len(fields)) + for k, v := range entry.Data { + data[k] = v + } + fieldErr := entry.err + for k, v := range fields { + isErrField := false + if t := reflect.TypeOf(v); t != nil { + switch { + case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func: + isErrField = true + } + } + if isErrField { + tmp := fmt.Sprintf("can not add field %q", k) + if fieldErr != "" { + fieldErr = entry.err + ", " + tmp + } else { + fieldErr = tmp + } + } else { + data[k] = v + } + } + return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context} +} + +// Overrides the time of the Entry. +func (entry *Entry) WithTime(t time.Time) *Entry { + dataCopy := make(Fields, len(entry.Data)) + for k, v := range entry.Data { + dataCopy[k] = v + } + return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context} +} + +// getPackageName reduces a fully qualified function name to the package name +// There really ought to be to be a better way... +func getPackageName(f string) string { + for { + lastPeriod := strings.LastIndex(f, ".") + lastSlash := strings.LastIndex(f, "/") + if lastPeriod > lastSlash { + f = f[:lastPeriod] + } else { + break + } + } + + return f +} + +// getCaller retrieves the name of the first non-logrus calling function +func getCaller() *runtime.Frame { + // cache this package's fully-qualified name + callerInitOnce.Do(func() { + pcs := make([]uintptr, maximumCallerDepth) + _ = runtime.Callers(0, pcs) + + // dynamic get the package name and the minimum caller depth + for i := 0; i < maximumCallerDepth; i++ { + funcName := runtime.FuncForPC(pcs[i]).Name() + if strings.Contains(funcName, "getCaller") { + logrusPackage = getPackageName(funcName) + break + } + } + + minimumCallerDepth = knownLogrusFrames + }) + + // Restrict the lookback frames to avoid runaway lookups + pcs := make([]uintptr, maximumCallerDepth) + depth := runtime.Callers(minimumCallerDepth, pcs) + frames := runtime.CallersFrames(pcs[:depth]) + + for f, again := frames.Next(); again; f, again = frames.Next() { + pkg := getPackageName(f.Function) + + // If the caller isn't part of this package, we're done + if pkg != logrusPackage { + return &f //nolint:scopelint + } + } + + // if we got here, we failed to find the caller's context + return nil +} + +func (entry Entry) HasCaller() (has bool) { + return entry.Logger != nil && + entry.Logger.ReportCaller && + entry.Caller != nil +} + +func (entry *Entry) log(level Level, msg string) { + var buffer *bytes.Buffer + + newEntry := entry.Dup() + + if newEntry.Time.IsZero() { + newEntry.Time = time.Now() + } + + newEntry.Level = level + newEntry.Message = msg + + newEntry.Logger.mu.Lock() + reportCaller := newEntry.Logger.ReportCaller + newEntry.Logger.mu.Unlock() + + if reportCaller { + newEntry.Caller = getCaller() + } + + newEntry.fireHooks() + + buffer = getBuffer() + defer func() { + newEntry.Buffer = nil + putBuffer(buffer) + }() + buffer.Reset() + newEntry.Buffer = buffer + + newEntry.write() + + newEntry.Buffer = nil + + // To avoid Entry#log() returning a value that only would make sense for + // panic() to use in Entry#Panic(), we avoid the allocation by checking + // directly here. + if level <= PanicLevel { + panic(newEntry) + } +} + +func (entry *Entry) fireHooks() { + var tmpHooks LevelHooks + entry.Logger.mu.Lock() + tmpHooks = make(LevelHooks, len(entry.Logger.Hooks)) + for k, v := range entry.Logger.Hooks { + tmpHooks[k] = v + } + entry.Logger.mu.Unlock() + + err := tmpHooks.Fire(entry.Level, entry) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) + } +} + +func (entry *Entry) write() { + serialized, err := entry.Logger.Formatter.Format(entry) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) + return + } + entry.Logger.mu.Lock() + defer entry.Logger.mu.Unlock() + if _, err := entry.Logger.Out.Write(serialized); err != nil { + fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) + } +} + +func (entry *Entry) Log(level Level, args ...interface{}) { + if entry.Logger.IsLevelEnabled(level) { + entry.log(level, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Trace(args ...interface{}) { + entry.Log(TraceLevel, args...) +} + +func (entry *Entry) Debug(args ...interface{}) { + entry.Log(DebugLevel, args...) +} + +func (entry *Entry) Print(args ...interface{}) { + entry.Info(args...) +} + +func (entry *Entry) Info(args ...interface{}) { + entry.Log(InfoLevel, args...) +} + +func (entry *Entry) Warn(args ...interface{}) { + entry.Log(WarnLevel, args...) +} + +func (entry *Entry) Warning(args ...interface{}) { + entry.Warn(args...) +} + +func (entry *Entry) Error(args ...interface{}) { + entry.Log(ErrorLevel, args...) +} + +func (entry *Entry) Fatal(args ...interface{}) { + entry.Log(FatalLevel, args...) + entry.Logger.Exit(1) +} + +func (entry *Entry) Panic(args ...interface{}) { + entry.Log(PanicLevel, args...) +} + +// Entry Printf family functions + +func (entry *Entry) Logf(level Level, format string, args ...interface{}) { + if entry.Logger.IsLevelEnabled(level) { + entry.Log(level, fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Tracef(format string, args ...interface{}) { + entry.Logf(TraceLevel, format, args...) +} + +func (entry *Entry) Debugf(format string, args ...interface{}) { + entry.Logf(DebugLevel, format, args...) +} + +func (entry *Entry) Infof(format string, args ...interface{}) { + entry.Logf(InfoLevel, format, args...) +} + +func (entry *Entry) Printf(format string, args ...interface{}) { + entry.Infof(format, args...) +} + +func (entry *Entry) Warnf(format string, args ...interface{}) { + entry.Logf(WarnLevel, format, args...) +} + +func (entry *Entry) Warningf(format string, args ...interface{}) { + entry.Warnf(format, args...) +} + +func (entry *Entry) Errorf(format string, args ...interface{}) { + entry.Logf(ErrorLevel, format, args...) +} + +func (entry *Entry) Fatalf(format string, args ...interface{}) { + entry.Logf(FatalLevel, format, args...) + entry.Logger.Exit(1) +} + +func (entry *Entry) Panicf(format string, args ...interface{}) { + entry.Logf(PanicLevel, format, args...) +} + +// Entry Println family functions + +func (entry *Entry) Logln(level Level, args ...interface{}) { + if entry.Logger.IsLevelEnabled(level) { + entry.Log(level, entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Traceln(args ...interface{}) { + entry.Logln(TraceLevel, args...) +} + +func (entry *Entry) Debugln(args ...interface{}) { + entry.Logln(DebugLevel, args...) +} + +func (entry *Entry) Infoln(args ...interface{}) { + entry.Logln(InfoLevel, args...) +} + +func (entry *Entry) Println(args ...interface{}) { + entry.Infoln(args...) +} + +func (entry *Entry) Warnln(args ...interface{}) { + entry.Logln(WarnLevel, args...) +} + +func (entry *Entry) Warningln(args ...interface{}) { + entry.Warnln(args...) +} + +func (entry *Entry) Errorln(args ...interface{}) { + entry.Logln(ErrorLevel, args...) +} + +func (entry *Entry) Fatalln(args ...interface{}) { + entry.Logln(FatalLevel, args...) + entry.Logger.Exit(1) +} + +func (entry *Entry) Panicln(args ...interface{}) { + entry.Logln(PanicLevel, args...) +} + +// Sprintlnn => Sprint no newline. This is to get the behavior of how +// fmt.Sprintln where spaces are always added between operands, regardless of +// their type. Instead of vendoring the Sprintln implementation to spare a +// string allocation, we do the simplest thing. +func (entry *Entry) sprintlnn(args ...interface{}) string { + msg := fmt.Sprintln(args...) + return msg[:len(msg)-1] +} diff --git a/vendor/github.com/sirupsen/logrus/exported.go b/vendor/github.com/sirupsen/logrus/exported.go new file mode 100644 index 0000000000..017c30ce67 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/exported.go @@ -0,0 +1,270 @@ +package logrus + +import ( + "context" + "io" + "time" +) + +var ( + // std is the name of the standard logger in stdlib `log` + std = New() +) + +func StandardLogger() *Logger { + return std +} + +// SetOutput sets the standard logger output. +func SetOutput(out io.Writer) { + std.SetOutput(out) +} + +// SetFormatter sets the standard logger formatter. +func SetFormatter(formatter Formatter) { + std.SetFormatter(formatter) +} + +// SetReportCaller sets whether the standard logger will include the calling +// method as a field. +func SetReportCaller(include bool) { + std.SetReportCaller(include) +} + +// SetLevel sets the standard logger level. +func SetLevel(level Level) { + std.SetLevel(level) +} + +// GetLevel returns the standard logger level. +func GetLevel() Level { + return std.GetLevel() +} + +// IsLevelEnabled checks if the log level of the standard logger is greater than the level param +func IsLevelEnabled(level Level) bool { + return std.IsLevelEnabled(level) +} + +// AddHook adds a hook to the standard logger hooks. +func AddHook(hook Hook) { + std.AddHook(hook) +} + +// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key. +func WithError(err error) *Entry { + return std.WithField(ErrorKey, err) +} + +// WithContext creates an entry from the standard logger and adds a context to it. +func WithContext(ctx context.Context) *Entry { + return std.WithContext(ctx) +} + +// WithField creates an entry from the standard logger and adds a field to +// it. If you want multiple fields, use `WithFields`. +// +// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal +// or Panic on the Entry it returns. +func WithField(key string, value interface{}) *Entry { + return std.WithField(key, value) +} + +// WithFields creates an entry from the standard logger and adds multiple +// fields to it. This is simply a helper for `WithField`, invoking it +// once for each field. +// +// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal +// or Panic on the Entry it returns. +func WithFields(fields Fields) *Entry { + return std.WithFields(fields) +} + +// WithTime creates an entry from the standard logger and overrides the time of +// logs generated with it. +// +// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal +// or Panic on the Entry it returns. +func WithTime(t time.Time) *Entry { + return std.WithTime(t) +} + +// Trace logs a message at level Trace on the standard logger. +func Trace(args ...interface{}) { + std.Trace(args...) +} + +// Debug logs a message at level Debug on the standard logger. +func Debug(args ...interface{}) { + std.Debug(args...) +} + +// Print logs a message at level Info on the standard logger. +func Print(args ...interface{}) { + std.Print(args...) +} + +// Info logs a message at level Info on the standard logger. +func Info(args ...interface{}) { + std.Info(args...) +} + +// Warn logs a message at level Warn on the standard logger. +func Warn(args ...interface{}) { + std.Warn(args...) +} + +// Warning logs a message at level Warn on the standard logger. +func Warning(args ...interface{}) { + std.Warning(args...) +} + +// Error logs a message at level Error on the standard logger. +func Error(args ...interface{}) { + std.Error(args...) +} + +// Panic logs a message at level Panic on the standard logger. +func Panic(args ...interface{}) { + std.Panic(args...) +} + +// Fatal logs a message at level Fatal on the standard logger then the process will exit with status set to 1. +func Fatal(args ...interface{}) { + std.Fatal(args...) +} + +// TraceFn logs a message from a func at level Trace on the standard logger. +func TraceFn(fn LogFunction) { + std.TraceFn(fn) +} + +// DebugFn logs a message from a func at level Debug on the standard logger. +func DebugFn(fn LogFunction) { + std.DebugFn(fn) +} + +// PrintFn logs a message from a func at level Info on the standard logger. +func PrintFn(fn LogFunction) { + std.PrintFn(fn) +} + +// InfoFn logs a message from a func at level Info on the standard logger. +func InfoFn(fn LogFunction) { + std.InfoFn(fn) +} + +// WarnFn logs a message from a func at level Warn on the standard logger. +func WarnFn(fn LogFunction) { + std.WarnFn(fn) +} + +// WarningFn logs a message from a func at level Warn on the standard logger. +func WarningFn(fn LogFunction) { + std.WarningFn(fn) +} + +// ErrorFn logs a message from a func at level Error on the standard logger. +func ErrorFn(fn LogFunction) { + std.ErrorFn(fn) +} + +// PanicFn logs a message from a func at level Panic on the standard logger. +func PanicFn(fn LogFunction) { + std.PanicFn(fn) +} + +// FatalFn logs a message from a func at level Fatal on the standard logger then the process will exit with status set to 1. +func FatalFn(fn LogFunction) { + std.FatalFn(fn) +} + +// Tracef logs a message at level Trace on the standard logger. +func Tracef(format string, args ...interface{}) { + std.Tracef(format, args...) +} + +// Debugf logs a message at level Debug on the standard logger. +func Debugf(format string, args ...interface{}) { + std.Debugf(format, args...) +} + +// Printf logs a message at level Info on the standard logger. +func Printf(format string, args ...interface{}) { + std.Printf(format, args...) +} + +// Infof logs a message at level Info on the standard logger. +func Infof(format string, args ...interface{}) { + std.Infof(format, args...) +} + +// Warnf logs a message at level Warn on the standard logger. +func Warnf(format string, args ...interface{}) { + std.Warnf(format, args...) +} + +// Warningf logs a message at level Warn on the standard logger. +func Warningf(format string, args ...interface{}) { + std.Warningf(format, args...) +} + +// Errorf logs a message at level Error on the standard logger. +func Errorf(format string, args ...interface{}) { + std.Errorf(format, args...) +} + +// Panicf logs a message at level Panic on the standard logger. +func Panicf(format string, args ...interface{}) { + std.Panicf(format, args...) +} + +// Fatalf logs a message at level Fatal on the standard logger then the process will exit with status set to 1. +func Fatalf(format string, args ...interface{}) { + std.Fatalf(format, args...) +} + +// Traceln logs a message at level Trace on the standard logger. +func Traceln(args ...interface{}) { + std.Traceln(args...) +} + +// Debugln logs a message at level Debug on the standard logger. +func Debugln(args ...interface{}) { + std.Debugln(args...) +} + +// Println logs a message at level Info on the standard logger. +func Println(args ...interface{}) { + std.Println(args...) +} + +// Infoln logs a message at level Info on the standard logger. +func Infoln(args ...interface{}) { + std.Infoln(args...) +} + +// Warnln logs a message at level Warn on the standard logger. +func Warnln(args ...interface{}) { + std.Warnln(args...) +} + +// Warningln logs a message at level Warn on the standard logger. +func Warningln(args ...interface{}) { + std.Warningln(args...) +} + +// Errorln logs a message at level Error on the standard logger. +func Errorln(args ...interface{}) { + std.Errorln(args...) +} + +// Panicln logs a message at level Panic on the standard logger. +func Panicln(args ...interface{}) { + std.Panicln(args...) +} + +// Fatalln logs a message at level Fatal on the standard logger then the process will exit with status set to 1. +func Fatalln(args ...interface{}) { + std.Fatalln(args...) +} diff --git a/vendor/github.com/sirupsen/logrus/formatter.go b/vendor/github.com/sirupsen/logrus/formatter.go new file mode 100644 index 0000000000..408883773e --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/formatter.go @@ -0,0 +1,78 @@ +package logrus + +import "time" + +// Default key names for the default fields +const ( + defaultTimestampFormat = time.RFC3339 + FieldKeyMsg = "msg" + FieldKeyLevel = "level" + FieldKeyTime = "time" + FieldKeyLogrusError = "logrus_error" + FieldKeyFunc = "func" + FieldKeyFile = "file" +) + +// The Formatter interface is used to implement a custom Formatter. It takes an +// `Entry`. It exposes all the fields, including the default ones: +// +// * `entry.Data["msg"]`. The message passed from Info, Warn, Error .. +// * `entry.Data["time"]`. The timestamp. +// * `entry.Data["level"]. The level the entry was logged at. +// +// Any additional fields added with `WithField` or `WithFields` are also in +// `entry.Data`. Format is expected to return an array of bytes which are then +// logged to `logger.Out`. +type Formatter interface { + Format(*Entry) ([]byte, error) +} + +// This is to not silently overwrite `time`, `msg`, `func` and `level` fields when +// dumping it. If this code wasn't there doing: +// +// logrus.WithField("level", 1).Info("hello") +// +// Would just silently drop the user provided level. Instead with this code +// it'll logged as: +// +// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} +// +// It's not exported because it's still using Data in an opinionated way. It's to +// avoid code duplication between the two default formatters. +func prefixFieldClashes(data Fields, fieldMap FieldMap, reportCaller bool) { + timeKey := fieldMap.resolve(FieldKeyTime) + if t, ok := data[timeKey]; ok { + data["fields."+timeKey] = t + delete(data, timeKey) + } + + msgKey := fieldMap.resolve(FieldKeyMsg) + if m, ok := data[msgKey]; ok { + data["fields."+msgKey] = m + delete(data, msgKey) + } + + levelKey := fieldMap.resolve(FieldKeyLevel) + if l, ok := data[levelKey]; ok { + data["fields."+levelKey] = l + delete(data, levelKey) + } + + logrusErrKey := fieldMap.resolve(FieldKeyLogrusError) + if l, ok := data[logrusErrKey]; ok { + data["fields."+logrusErrKey] = l + delete(data, logrusErrKey) + } + + // If reportCaller is not set, 'func' will not conflict. + if reportCaller { + funcKey := fieldMap.resolve(FieldKeyFunc) + if l, ok := data[funcKey]; ok { + data["fields."+funcKey] = l + } + fileKey := fieldMap.resolve(FieldKeyFile) + if l, ok := data[fileKey]; ok { + data["fields."+fileKey] = l + } + } +} diff --git a/vendor/github.com/sirupsen/logrus/go.mod b/vendor/github.com/sirupsen/logrus/go.mod new file mode 100644 index 0000000000..b3919d5eab --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/go.mod @@ -0,0 +1,10 @@ +module github.com/sirupsen/logrus + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.2.2 + golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 +) + +go 1.13 diff --git a/vendor/github.com/sirupsen/logrus/go.sum b/vendor/github.com/sirupsen/logrus/go.sum new file mode 100644 index 0000000000..694c18b845 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/go.sum @@ -0,0 +1,8 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/sirupsen/logrus/hooks.go b/vendor/github.com/sirupsen/logrus/hooks.go new file mode 100644 index 0000000000..3f151cdc39 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/hooks.go @@ -0,0 +1,34 @@ +package logrus + +// A hook to be fired when logging on the logging levels returned from +// `Levels()` on your implementation of the interface. Note that this is not +// fired in a goroutine or a channel with workers, you should handle such +// functionality yourself if your call is non-blocking and you don't wish for +// the logging calls for levels returned from `Levels()` to block. +type Hook interface { + Levels() []Level + Fire(*Entry) error +} + +// Internal type for storing the hooks on a logger instance. +type LevelHooks map[Level][]Hook + +// Add a hook to an instance of logger. This is called with +// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface. +func (hooks LevelHooks) Add(hook Hook) { + for _, level := range hook.Levels() { + hooks[level] = append(hooks[level], hook) + } +} + +// Fire all the hooks for the passed level. Used by `entry.log` to fire +// appropriate hooks for a log entry. +func (hooks LevelHooks) Fire(level Level, entry *Entry) error { + for _, hook := range hooks[level] { + if err := hook.Fire(entry); err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/sirupsen/logrus/json_formatter.go b/vendor/github.com/sirupsen/logrus/json_formatter.go new file mode 100644 index 0000000000..c96dc5636b --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/json_formatter.go @@ -0,0 +1,128 @@ +package logrus + +import ( + "bytes" + "encoding/json" + "fmt" + "runtime" +) + +type fieldKey string + +// FieldMap allows customization of the key names for default fields. +type FieldMap map[fieldKey]string + +func (f FieldMap) resolve(key fieldKey) string { + if k, ok := f[key]; ok { + return k + } + + return string(key) +} + +// JSONFormatter formats logs into parsable json +type JSONFormatter struct { + // TimestampFormat sets the format used for marshaling timestamps. + // The format to use is the same than for time.Format or time.Parse from the standard + // library. + // The standard Library already provides a set of predefined format. + TimestampFormat string + + // DisableTimestamp allows disabling automatic timestamps in output + DisableTimestamp bool + + // DisableHTMLEscape allows disabling html escaping in output + DisableHTMLEscape bool + + // DataKey allows users to put all the log entry parameters into a nested dictionary at a given key. + DataKey string + + // FieldMap allows users to customize the names of keys for default fields. + // As an example: + // formatter := &JSONFormatter{ + // FieldMap: FieldMap{ + // FieldKeyTime: "@timestamp", + // FieldKeyLevel: "@level", + // FieldKeyMsg: "@message", + // FieldKeyFunc: "@caller", + // }, + // } + FieldMap FieldMap + + // CallerPrettyfier can be set by the user to modify the content + // of the function and file keys in the json data when ReportCaller is + // activated. If any of the returned value is the empty string the + // corresponding key will be removed from json fields. + CallerPrettyfier func(*runtime.Frame) (function string, file string) + + // PrettyPrint will indent all json logs + PrettyPrint bool +} + +// Format renders a single log entry +func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { + data := make(Fields, len(entry.Data)+4) + for k, v := range entry.Data { + switch v := v.(type) { + case error: + // Otherwise errors are ignored by `encoding/json` + // https://github.com/sirupsen/logrus/issues/137 + data[k] = v.Error() + default: + data[k] = v + } + } + + if f.DataKey != "" { + newData := make(Fields, 4) + newData[f.DataKey] = data + data = newData + } + + prefixFieldClashes(data, f.FieldMap, entry.HasCaller()) + + timestampFormat := f.TimestampFormat + if timestampFormat == "" { + timestampFormat = defaultTimestampFormat + } + + if entry.err != "" { + data[f.FieldMap.resolve(FieldKeyLogrusError)] = entry.err + } + if !f.DisableTimestamp { + data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat) + } + data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message + data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() + if entry.HasCaller() { + funcVal := entry.Caller.Function + fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + if f.CallerPrettyfier != nil { + funcVal, fileVal = f.CallerPrettyfier(entry.Caller) + } + if funcVal != "" { + data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal + } + if fileVal != "" { + data[f.FieldMap.resolve(FieldKeyFile)] = fileVal + } + } + + var b *bytes.Buffer + if entry.Buffer != nil { + b = entry.Buffer + } else { + b = &bytes.Buffer{} + } + + encoder := json.NewEncoder(b) + encoder.SetEscapeHTML(!f.DisableHTMLEscape) + if f.PrettyPrint { + encoder.SetIndent("", " ") + } + if err := encoder.Encode(data); err != nil { + return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err) + } + + return b.Bytes(), nil +} diff --git a/vendor/github.com/sirupsen/logrus/logger.go b/vendor/github.com/sirupsen/logrus/logger.go new file mode 100644 index 0000000000..337704457a --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/logger.go @@ -0,0 +1,404 @@ +package logrus + +import ( + "context" + "io" + "os" + "sync" + "sync/atomic" + "time" +) + +// LogFunction For big messages, it can be more efficient to pass a function +// and only call it if the log level is actually enables rather than +// generating the log message and then checking if the level is enabled +type LogFunction func() []interface{} + +type Logger struct { + // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a + // file, or leave it default which is `os.Stderr`. You can also set this to + // something more adventurous, such as logging to Kafka. + Out io.Writer + // Hooks for the logger instance. These allow firing events based on logging + // levels and log entries. For example, to send errors to an error tracking + // service, log to StatsD or dump the core on fatal errors. + Hooks LevelHooks + // All log entries pass through the formatter before logged to Out. The + // included formatters are `TextFormatter` and `JSONFormatter` for which + // TextFormatter is the default. In development (when a TTY is attached) it + // logs with colors, but to a file it wouldn't. You can easily implement your + // own that implements the `Formatter` interface, see the `README` or included + // formatters for examples. + Formatter Formatter + + // Flag for whether to log caller info (off by default) + ReportCaller bool + + // The logging level the logger should log at. This is typically (and defaults + // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be + // logged. + Level Level + // Used to sync writing to the log. Locking is enabled by Default + mu MutexWrap + // Reusable empty entry + entryPool sync.Pool + // Function to exit the application, defaults to `os.Exit()` + ExitFunc exitFunc +} + +type exitFunc func(int) + +type MutexWrap struct { + lock sync.Mutex + disabled bool +} + +func (mw *MutexWrap) Lock() { + if !mw.disabled { + mw.lock.Lock() + } +} + +func (mw *MutexWrap) Unlock() { + if !mw.disabled { + mw.lock.Unlock() + } +} + +func (mw *MutexWrap) Disable() { + mw.disabled = true +} + +// Creates a new logger. Configuration should be set by changing `Formatter`, +// `Out` and `Hooks` directly on the default logger instance. You can also just +// instantiate your own: +// +// var log = &logrus.Logger{ +// Out: os.Stderr, +// Formatter: new(logrus.TextFormatter), +// Hooks: make(logrus.LevelHooks), +// Level: logrus.DebugLevel, +// } +// +// It's recommended to make this a global instance called `log`. +func New() *Logger { + return &Logger{ + Out: os.Stderr, + Formatter: new(TextFormatter), + Hooks: make(LevelHooks), + Level: InfoLevel, + ExitFunc: os.Exit, + ReportCaller: false, + } +} + +func (logger *Logger) newEntry() *Entry { + entry, ok := logger.entryPool.Get().(*Entry) + if ok { + return entry + } + return NewEntry(logger) +} + +func (logger *Logger) releaseEntry(entry *Entry) { + entry.Data = map[string]interface{}{} + logger.entryPool.Put(entry) +} + +// WithField allocates a new entry and adds a field to it. +// Debug, Print, Info, Warn, Error, Fatal or Panic must be then applied to +// this new returned entry. +// If you want multiple fields, use `WithFields`. +func (logger *Logger) WithField(key string, value interface{}) *Entry { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + return entry.WithField(key, value) +} + +// Adds a struct of fields to the log entry. All it does is call `WithField` for +// each `Field`. +func (logger *Logger) WithFields(fields Fields) *Entry { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + return entry.WithFields(fields) +} + +// Add an error as single field to the log entry. All it does is call +// `WithError` for the given `error`. +func (logger *Logger) WithError(err error) *Entry { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + return entry.WithError(err) +} + +// Add a context to the log entry. +func (logger *Logger) WithContext(ctx context.Context) *Entry { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + return entry.WithContext(ctx) +} + +// Overrides the time of the log entry. +func (logger *Logger) WithTime(t time.Time) *Entry { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + return entry.WithTime(t) +} + +func (logger *Logger) Logf(level Level, format string, args ...interface{}) { + if logger.IsLevelEnabled(level) { + entry := logger.newEntry() + entry.Logf(level, format, args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Tracef(format string, args ...interface{}) { + logger.Logf(TraceLevel, format, args...) +} + +func (logger *Logger) Debugf(format string, args ...interface{}) { + logger.Logf(DebugLevel, format, args...) +} + +func (logger *Logger) Infof(format string, args ...interface{}) { + logger.Logf(InfoLevel, format, args...) +} + +func (logger *Logger) Printf(format string, args ...interface{}) { + entry := logger.newEntry() + entry.Printf(format, args...) + logger.releaseEntry(entry) +} + +func (logger *Logger) Warnf(format string, args ...interface{}) { + logger.Logf(WarnLevel, format, args...) +} + +func (logger *Logger) Warningf(format string, args ...interface{}) { + logger.Warnf(format, args...) +} + +func (logger *Logger) Errorf(format string, args ...interface{}) { + logger.Logf(ErrorLevel, format, args...) +} + +func (logger *Logger) Fatalf(format string, args ...interface{}) { + logger.Logf(FatalLevel, format, args...) + logger.Exit(1) +} + +func (logger *Logger) Panicf(format string, args ...interface{}) { + logger.Logf(PanicLevel, format, args...) +} + +func (logger *Logger) Log(level Level, args ...interface{}) { + if logger.IsLevelEnabled(level) { + entry := logger.newEntry() + entry.Log(level, args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) LogFn(level Level, fn LogFunction) { + if logger.IsLevelEnabled(level) { + entry := logger.newEntry() + entry.Log(level, fn()...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Trace(args ...interface{}) { + logger.Log(TraceLevel, args...) +} + +func (logger *Logger) Debug(args ...interface{}) { + logger.Log(DebugLevel, args...) +} + +func (logger *Logger) Info(args ...interface{}) { + logger.Log(InfoLevel, args...) +} + +func (logger *Logger) Print(args ...interface{}) { + entry := logger.newEntry() + entry.Print(args...) + logger.releaseEntry(entry) +} + +func (logger *Logger) Warn(args ...interface{}) { + logger.Log(WarnLevel, args...) +} + +func (logger *Logger) Warning(args ...interface{}) { + logger.Warn(args...) +} + +func (logger *Logger) Error(args ...interface{}) { + logger.Log(ErrorLevel, args...) +} + +func (logger *Logger) Fatal(args ...interface{}) { + logger.Log(FatalLevel, args...) + logger.Exit(1) +} + +func (logger *Logger) Panic(args ...interface{}) { + logger.Log(PanicLevel, args...) +} + +func (logger *Logger) TraceFn(fn LogFunction) { + logger.LogFn(TraceLevel, fn) +} + +func (logger *Logger) DebugFn(fn LogFunction) { + logger.LogFn(DebugLevel, fn) +} + +func (logger *Logger) InfoFn(fn LogFunction) { + logger.LogFn(InfoLevel, fn) +} + +func (logger *Logger) PrintFn(fn LogFunction) { + entry := logger.newEntry() + entry.Print(fn()...) + logger.releaseEntry(entry) +} + +func (logger *Logger) WarnFn(fn LogFunction) { + logger.LogFn(WarnLevel, fn) +} + +func (logger *Logger) WarningFn(fn LogFunction) { + logger.WarnFn(fn) +} + +func (logger *Logger) ErrorFn(fn LogFunction) { + logger.LogFn(ErrorLevel, fn) +} + +func (logger *Logger) FatalFn(fn LogFunction) { + logger.LogFn(FatalLevel, fn) + logger.Exit(1) +} + +func (logger *Logger) PanicFn(fn LogFunction) { + logger.LogFn(PanicLevel, fn) +} + +func (logger *Logger) Logln(level Level, args ...interface{}) { + if logger.IsLevelEnabled(level) { + entry := logger.newEntry() + entry.Logln(level, args...) + logger.releaseEntry(entry) + } +} + +func (logger *Logger) Traceln(args ...interface{}) { + logger.Logln(TraceLevel, args...) +} + +func (logger *Logger) Debugln(args ...interface{}) { + logger.Logln(DebugLevel, args...) +} + +func (logger *Logger) Infoln(args ...interface{}) { + logger.Logln(InfoLevel, args...) +} + +func (logger *Logger) Println(args ...interface{}) { + entry := logger.newEntry() + entry.Println(args...) + logger.releaseEntry(entry) +} + +func (logger *Logger) Warnln(args ...interface{}) { + logger.Logln(WarnLevel, args...) +} + +func (logger *Logger) Warningln(args ...interface{}) { + logger.Warnln(args...) +} + +func (logger *Logger) Errorln(args ...interface{}) { + logger.Logln(ErrorLevel, args...) +} + +func (logger *Logger) Fatalln(args ...interface{}) { + logger.Logln(FatalLevel, args...) + logger.Exit(1) +} + +func (logger *Logger) Panicln(args ...interface{}) { + logger.Logln(PanicLevel, args...) +} + +func (logger *Logger) Exit(code int) { + runHandlers() + if logger.ExitFunc == nil { + logger.ExitFunc = os.Exit + } + logger.ExitFunc(code) +} + +//When file is opened with appending mode, it's safe to +//write concurrently to a file (within 4k message on Linux). +//In these cases user can choose to disable the lock. +func (logger *Logger) SetNoLock() { + logger.mu.Disable() +} + +func (logger *Logger) level() Level { + return Level(atomic.LoadUint32((*uint32)(&logger.Level))) +} + +// SetLevel sets the logger level. +func (logger *Logger) SetLevel(level Level) { + atomic.StoreUint32((*uint32)(&logger.Level), uint32(level)) +} + +// GetLevel returns the logger level. +func (logger *Logger) GetLevel() Level { + return logger.level() +} + +// AddHook adds a hook to the logger hooks. +func (logger *Logger) AddHook(hook Hook) { + logger.mu.Lock() + defer logger.mu.Unlock() + logger.Hooks.Add(hook) +} + +// IsLevelEnabled checks if the log level of the logger is greater than the level param +func (logger *Logger) IsLevelEnabled(level Level) bool { + return logger.level() >= level +} + +// SetFormatter sets the logger formatter. +func (logger *Logger) SetFormatter(formatter Formatter) { + logger.mu.Lock() + defer logger.mu.Unlock() + logger.Formatter = formatter +} + +// SetOutput sets the logger output. +func (logger *Logger) SetOutput(output io.Writer) { + logger.mu.Lock() + defer logger.mu.Unlock() + logger.Out = output +} + +func (logger *Logger) SetReportCaller(reportCaller bool) { + logger.mu.Lock() + defer logger.mu.Unlock() + logger.ReportCaller = reportCaller +} + +// ReplaceHooks replaces the logger hooks and returns the old ones +func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks { + logger.mu.Lock() + oldHooks := logger.Hooks + logger.Hooks = hooks + logger.mu.Unlock() + return oldHooks +} diff --git a/vendor/github.com/sirupsen/logrus/logrus.go b/vendor/github.com/sirupsen/logrus/logrus.go new file mode 100644 index 0000000000..2f16224cb9 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/logrus.go @@ -0,0 +1,186 @@ +package logrus + +import ( + "fmt" + "log" + "strings" +) + +// Fields type, used to pass to `WithFields`. +type Fields map[string]interface{} + +// Level type +type Level uint32 + +// Convert the Level to a string. E.g. PanicLevel becomes "panic". +func (level Level) String() string { + if b, err := level.MarshalText(); err == nil { + return string(b) + } else { + return "unknown" + } +} + +// ParseLevel takes a string level and returns the Logrus log level constant. +func ParseLevel(lvl string) (Level, error) { + switch strings.ToLower(lvl) { + case "panic": + return PanicLevel, nil + case "fatal": + return FatalLevel, nil + case "error": + return ErrorLevel, nil + case "warn", "warning": + return WarnLevel, nil + case "info": + return InfoLevel, nil + case "debug": + return DebugLevel, nil + case "trace": + return TraceLevel, nil + } + + var l Level + return l, fmt.Errorf("not a valid logrus Level: %q", lvl) +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (level *Level) UnmarshalText(text []byte) error { + l, err := ParseLevel(string(text)) + if err != nil { + return err + } + + *level = l + + return nil +} + +func (level Level) MarshalText() ([]byte, error) { + switch level { + case TraceLevel: + return []byte("trace"), nil + case DebugLevel: + return []byte("debug"), nil + case InfoLevel: + return []byte("info"), nil + case WarnLevel: + return []byte("warning"), nil + case ErrorLevel: + return []byte("error"), nil + case FatalLevel: + return []byte("fatal"), nil + case PanicLevel: + return []byte("panic"), nil + } + + return nil, fmt.Errorf("not a valid logrus level %d", level) +} + +// A constant exposing all logging levels +var AllLevels = []Level{ + PanicLevel, + FatalLevel, + ErrorLevel, + WarnLevel, + InfoLevel, + DebugLevel, + TraceLevel, +} + +// These are the different logging levels. You can set the logging level to log +// on your instance of logger, obtained with `logrus.New()`. +const ( + // PanicLevel level, highest level of severity. Logs and then calls panic with the + // message passed to Debug, Info, ... + PanicLevel Level = iota + // FatalLevel level. Logs and then calls `logger.Exit(1)`. It will exit even if the + // logging level is set to Panic. + FatalLevel + // ErrorLevel level. Logs. Used for errors that should definitely be noted. + // Commonly used for hooks to send errors to an error tracking service. + ErrorLevel + // WarnLevel level. Non-critical entries that deserve eyes. + WarnLevel + // InfoLevel level. General operational entries about what's going on inside the + // application. + InfoLevel + // DebugLevel level. Usually only enabled when debugging. Very verbose logging. + DebugLevel + // TraceLevel level. Designates finer-grained informational events than the Debug. + TraceLevel +) + +// Won't compile if StdLogger can't be realized by a log.Logger +var ( + _ StdLogger = &log.Logger{} + _ StdLogger = &Entry{} + _ StdLogger = &Logger{} +) + +// StdLogger is what your logrus-enabled library should take, that way +// it'll accept a stdlib logger and a logrus logger. There's no standard +// interface, this is the closest we get, unfortunately. +type StdLogger interface { + Print(...interface{}) + Printf(string, ...interface{}) + Println(...interface{}) + + Fatal(...interface{}) + Fatalf(string, ...interface{}) + Fatalln(...interface{}) + + Panic(...interface{}) + Panicf(string, ...interface{}) + Panicln(...interface{}) +} + +// The FieldLogger interface generalizes the Entry and Logger types +type FieldLogger interface { + WithField(key string, value interface{}) *Entry + WithFields(fields Fields) *Entry + WithError(err error) *Entry + + Debugf(format string, args ...interface{}) + Infof(format string, args ...interface{}) + Printf(format string, args ...interface{}) + Warnf(format string, args ...interface{}) + Warningf(format string, args ...interface{}) + Errorf(format string, args ...interface{}) + Fatalf(format string, args ...interface{}) + Panicf(format string, args ...interface{}) + + Debug(args ...interface{}) + Info(args ...interface{}) + Print(args ...interface{}) + Warn(args ...interface{}) + Warning(args ...interface{}) + Error(args ...interface{}) + Fatal(args ...interface{}) + Panic(args ...interface{}) + + Debugln(args ...interface{}) + Infoln(args ...interface{}) + Println(args ...interface{}) + Warnln(args ...interface{}) + Warningln(args ...interface{}) + Errorln(args ...interface{}) + Fatalln(args ...interface{}) + Panicln(args ...interface{}) + + // IsDebugEnabled() bool + // IsInfoEnabled() bool + // IsWarnEnabled() bool + // IsErrorEnabled() bool + // IsFatalEnabled() bool + // IsPanicEnabled() bool +} + +// Ext1FieldLogger (the first extension to FieldLogger) is superfluous, it is +// here for consistancy. Do not use. Use Logger or Entry instead. +type Ext1FieldLogger interface { + FieldLogger + Tracef(format string, args ...interface{}) + Trace(args ...interface{}) + Traceln(args ...interface{}) +} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go b/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go new file mode 100644 index 0000000000..2403de9819 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go @@ -0,0 +1,11 @@ +// +build appengine + +package logrus + +import ( + "io" +) + +func checkIfTerminal(w io.Writer) bool { + return true +} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go b/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go new file mode 100644 index 0000000000..499789984d --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go @@ -0,0 +1,13 @@ +// +build darwin dragonfly freebsd netbsd openbsd +// +build !js + +package logrus + +import "golang.org/x/sys/unix" + +const ioctlReadTermios = unix.TIOCGETA + +func isTerminal(fd int) bool { + _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) + return err == nil +} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_js.go b/vendor/github.com/sirupsen/logrus/terminal_check_js.go new file mode 100644 index 0000000000..ebdae3ec62 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/terminal_check_js.go @@ -0,0 +1,7 @@ +// +build js + +package logrus + +func isTerminal(fd int) bool { + return false +} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go b/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go new file mode 100644 index 0000000000..97af92c68e --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go @@ -0,0 +1,11 @@ +// +build js nacl plan9 + +package logrus + +import ( + "io" +) + +func checkIfTerminal(w io.Writer) bool { + return false +} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go b/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go new file mode 100644 index 0000000000..3293fb3caa --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go @@ -0,0 +1,17 @@ +// +build !appengine,!js,!windows,!nacl,!plan9 + +package logrus + +import ( + "io" + "os" +) + +func checkIfTerminal(w io.Writer) bool { + switch v := w.(type) { + case *os.File: + return isTerminal(int(v.Fd())) + default: + return false + } +} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go b/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go new file mode 100644 index 0000000000..f6710b3bd0 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go @@ -0,0 +1,11 @@ +package logrus + +import ( + "golang.org/x/sys/unix" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +func isTerminal(fd int) bool { + _, err := unix.IoctlGetTermio(fd, unix.TCGETA) + return err == nil +} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_unix.go b/vendor/github.com/sirupsen/logrus/terminal_check_unix.go new file mode 100644 index 0000000000..04748b8515 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/terminal_check_unix.go @@ -0,0 +1,13 @@ +// +build linux aix zos +// +build !js + +package logrus + +import "golang.org/x/sys/unix" + +const ioctlReadTermios = unix.TCGETS + +func isTerminal(fd int) bool { + _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) + return err == nil +} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_windows.go b/vendor/github.com/sirupsen/logrus/terminal_check_windows.go new file mode 100644 index 0000000000..2879eb50ea --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/terminal_check_windows.go @@ -0,0 +1,27 @@ +// +build !appengine,!js,windows + +package logrus + +import ( + "io" + "os" + + "golang.org/x/sys/windows" +) + +func checkIfTerminal(w io.Writer) bool { + switch v := w.(type) { + case *os.File: + handle := windows.Handle(v.Fd()) + var mode uint32 + if err := windows.GetConsoleMode(handle, &mode); err != nil { + return false + } + mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING + if err := windows.SetConsoleMode(handle, mode); err != nil { + return false + } + return true + } + return false +} diff --git a/vendor/github.com/sirupsen/logrus/text_formatter.go b/vendor/github.com/sirupsen/logrus/text_formatter.go new file mode 100644 index 0000000000..be2c6efe5e --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/text_formatter.go @@ -0,0 +1,339 @@ +package logrus + +import ( + "bytes" + "fmt" + "os" + "runtime" + "sort" + "strconv" + "strings" + "sync" + "time" + "unicode/utf8" +) + +const ( + red = 31 + yellow = 33 + blue = 36 + gray = 37 +) + +var baseTimestamp time.Time + +func init() { + baseTimestamp = time.Now() +} + +// TextFormatter formats logs into text +type TextFormatter struct { + // Set to true to bypass checking for a TTY before outputting colors. + ForceColors bool + + // Force disabling colors. + DisableColors bool + + // Force quoting of all values + ForceQuote bool + + // DisableQuote disables quoting for all values. + // DisableQuote will have a lower priority than ForceQuote. + // If both of them are set to true, quote will be forced on all values. + DisableQuote bool + + // Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/ + EnvironmentOverrideColors bool + + // Disable timestamp logging. useful when output is redirected to logging + // system that already adds timestamps. + DisableTimestamp bool + + // Enable logging the full timestamp when a TTY is attached instead of just + // the time passed since beginning of execution. + FullTimestamp bool + + // TimestampFormat to use for display when a full timestamp is printed. + // The format to use is the same than for time.Format or time.Parse from the standard + // library. + // The standard Library already provides a set of predefined format. + TimestampFormat string + + // The fields are sorted by default for a consistent output. For applications + // that log extremely frequently and don't use the JSON formatter this may not + // be desired. + DisableSorting bool + + // The keys sorting function, when uninitialized it uses sort.Strings. + SortingFunc func([]string) + + // Disables the truncation of the level text to 4 characters. + DisableLevelTruncation bool + + // PadLevelText Adds padding the level text so that all the levels output at the same length + // PadLevelText is a superset of the DisableLevelTruncation option + PadLevelText bool + + // QuoteEmptyFields will wrap empty fields in quotes if true + QuoteEmptyFields bool + + // Whether the logger's out is to a terminal + isTerminal bool + + // FieldMap allows users to customize the names of keys for default fields. + // As an example: + // formatter := &TextFormatter{ + // FieldMap: FieldMap{ + // FieldKeyTime: "@timestamp", + // FieldKeyLevel: "@level", + // FieldKeyMsg: "@message"}} + FieldMap FieldMap + + // CallerPrettyfier can be set by the user to modify the content + // of the function and file keys in the data when ReportCaller is + // activated. If any of the returned value is the empty string the + // corresponding key will be removed from fields. + CallerPrettyfier func(*runtime.Frame) (function string, file string) + + terminalInitOnce sync.Once + + // The max length of the level text, generated dynamically on init + levelTextMaxLength int +} + +func (f *TextFormatter) init(entry *Entry) { + if entry.Logger != nil { + f.isTerminal = checkIfTerminal(entry.Logger.Out) + } + // Get the max length of the level text + for _, level := range AllLevels { + levelTextLength := utf8.RuneCount([]byte(level.String())) + if levelTextLength > f.levelTextMaxLength { + f.levelTextMaxLength = levelTextLength + } + } +} + +func (f *TextFormatter) isColored() bool { + isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows")) + + if f.EnvironmentOverrideColors { + switch force, ok := os.LookupEnv("CLICOLOR_FORCE"); { + case ok && force != "0": + isColored = true + case ok && force == "0", os.Getenv("CLICOLOR") == "0": + isColored = false + } + } + + return isColored && !f.DisableColors +} + +// Format renders a single log entry +func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { + data := make(Fields) + for k, v := range entry.Data { + data[k] = v + } + prefixFieldClashes(data, f.FieldMap, entry.HasCaller()) + keys := make([]string, 0, len(data)) + for k := range data { + keys = append(keys, k) + } + + var funcVal, fileVal string + + fixedKeys := make([]string, 0, 4+len(data)) + if !f.DisableTimestamp { + fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime)) + } + fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLevel)) + if entry.Message != "" { + fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyMsg)) + } + if entry.err != "" { + fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError)) + } + if entry.HasCaller() { + if f.CallerPrettyfier != nil { + funcVal, fileVal = f.CallerPrettyfier(entry.Caller) + } else { + funcVal = entry.Caller.Function + fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + } + + if funcVal != "" { + fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc)) + } + if fileVal != "" { + fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFile)) + } + } + + if !f.DisableSorting { + if f.SortingFunc == nil { + sort.Strings(keys) + fixedKeys = append(fixedKeys, keys...) + } else { + if !f.isColored() { + fixedKeys = append(fixedKeys, keys...) + f.SortingFunc(fixedKeys) + } else { + f.SortingFunc(keys) + } + } + } else { + fixedKeys = append(fixedKeys, keys...) + } + + var b *bytes.Buffer + if entry.Buffer != nil { + b = entry.Buffer + } else { + b = &bytes.Buffer{} + } + + f.terminalInitOnce.Do(func() { f.init(entry) }) + + timestampFormat := f.TimestampFormat + if timestampFormat == "" { + timestampFormat = defaultTimestampFormat + } + if f.isColored() { + f.printColored(b, entry, keys, data, timestampFormat) + } else { + + for _, key := range fixedKeys { + var value interface{} + switch { + case key == f.FieldMap.resolve(FieldKeyTime): + value = entry.Time.Format(timestampFormat) + case key == f.FieldMap.resolve(FieldKeyLevel): + value = entry.Level.String() + case key == f.FieldMap.resolve(FieldKeyMsg): + value = entry.Message + case key == f.FieldMap.resolve(FieldKeyLogrusError): + value = entry.err + case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller(): + value = funcVal + case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller(): + value = fileVal + default: + value = data[key] + } + f.appendKeyValue(b, key, value) + } + } + + b.WriteByte('\n') + return b.Bytes(), nil +} + +func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) { + var levelColor int + switch entry.Level { + case DebugLevel, TraceLevel: + levelColor = gray + case WarnLevel: + levelColor = yellow + case ErrorLevel, FatalLevel, PanicLevel: + levelColor = red + case InfoLevel: + levelColor = blue + default: + levelColor = blue + } + + levelText := strings.ToUpper(entry.Level.String()) + if !f.DisableLevelTruncation && !f.PadLevelText { + levelText = levelText[0:4] + } + if f.PadLevelText { + // Generates the format string used in the next line, for example "%-6s" or "%-7s". + // Based on the max level text length. + formatString := "%-" + strconv.Itoa(f.levelTextMaxLength) + "s" + // Formats the level text by appending spaces up to the max length, for example: + // - "INFO " + // - "WARNING" + levelText = fmt.Sprintf(formatString, levelText) + } + + // Remove a single newline if it already exists in the message to keep + // the behavior of logrus text_formatter the same as the stdlib log package + entry.Message = strings.TrimSuffix(entry.Message, "\n") + + caller := "" + if entry.HasCaller() { + funcVal := fmt.Sprintf("%s()", entry.Caller.Function) + fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + + if f.CallerPrettyfier != nil { + funcVal, fileVal = f.CallerPrettyfier(entry.Caller) + } + + if fileVal == "" { + caller = funcVal + } else if funcVal == "" { + caller = fileVal + } else { + caller = fileVal + " " + funcVal + } + } + + switch { + case f.DisableTimestamp: + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message) + case !f.FullTimestamp: + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message) + default: + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message) + } + for _, k := range keys { + v := data[k] + fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k) + f.appendValue(b, v) + } +} + +func (f *TextFormatter) needsQuoting(text string) bool { + if f.ForceQuote { + return true + } + if f.QuoteEmptyFields && len(text) == 0 { + return true + } + if f.DisableQuote { + return false + } + for _, ch := range text { + if !((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || + ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') { + return true + } + } + return false +} + +func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { + if b.Len() > 0 { + b.WriteByte(' ') + } + b.WriteString(key) + b.WriteByte('=') + f.appendValue(b, value) +} + +func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { + stringVal, ok := value.(string) + if !ok { + stringVal = fmt.Sprint(value) + } + + if !f.needsQuoting(stringVal) { + b.WriteString(stringVal) + } else { + b.WriteString(fmt.Sprintf("%q", stringVal)) + } +} diff --git a/vendor/github.com/sirupsen/logrus/writer.go b/vendor/github.com/sirupsen/logrus/writer.go new file mode 100644 index 0000000000..72e8e3a1b6 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/writer.go @@ -0,0 +1,70 @@ +package logrus + +import ( + "bufio" + "io" + "runtime" +) + +// Writer at INFO level. See WriterLevel for details. +func (logger *Logger) Writer() *io.PipeWriter { + return logger.WriterLevel(InfoLevel) +} + +// WriterLevel returns an io.Writer that can be used to write arbitrary text to +// the logger at the given log level. Each line written to the writer will be +// printed in the usual way using formatters and hooks. The writer is part of an +// io.Pipe and it is the callers responsibility to close the writer when done. +// This can be used to override the standard library logger easily. +func (logger *Logger) WriterLevel(level Level) *io.PipeWriter { + return NewEntry(logger).WriterLevel(level) +} + +func (entry *Entry) Writer() *io.PipeWriter { + return entry.WriterLevel(InfoLevel) +} + +func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { + reader, writer := io.Pipe() + + var printFunc func(args ...interface{}) + + switch level { + case TraceLevel: + printFunc = entry.Trace + case DebugLevel: + printFunc = entry.Debug + case InfoLevel: + printFunc = entry.Info + case WarnLevel: + printFunc = entry.Warn + case ErrorLevel: + printFunc = entry.Error + case FatalLevel: + printFunc = entry.Fatal + case PanicLevel: + printFunc = entry.Panic + default: + printFunc = entry.Print + } + + go entry.writerScanner(reader, printFunc) + runtime.SetFinalizer(writer, writerFinalizer) + + return writer +} + +func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) { + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + printFunc(scanner.Text()) + } + if err := scanner.Err(); err != nil { + entry.Errorf("Error while reading from Writer: %s", err) + } + reader.Close() +} + +func writerFinalizer(writer *io.PipeWriter) { + writer.Close() +} diff --git a/vendor/github.com/src-d/gcfg/LICENSE b/vendor/github.com/src-d/gcfg/LICENSE new file mode 100644 index 0000000000..87a5cede33 --- /dev/null +++ b/vendor/github.com/src-d/gcfg/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2012 Péter Surányi. Portions Copyright (c) 2009 The Go +Authors. 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. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +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 +OWNER 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/vendor/github.com/src-d/gcfg/README b/vendor/github.com/src-d/gcfg/README new file mode 100644 index 0000000000..1ff233a529 --- /dev/null +++ b/vendor/github.com/src-d/gcfg/README @@ -0,0 +1,4 @@ +Gcfg reads INI-style configuration files into Go structs; +supports user-defined types and subsections. + +Package docs: https://godoc.org/gopkg.in/gcfg.v1 diff --git a/vendor/github.com/src-d/gcfg/doc.go b/vendor/github.com/src-d/gcfg/doc.go new file mode 100644 index 0000000000..2edcb41a08 --- /dev/null +++ b/vendor/github.com/src-d/gcfg/doc.go @@ -0,0 +1,145 @@ +// Package gcfg reads "INI-style" text-based configuration files with +// "name=value" pairs grouped into sections (gcfg files). +// +// This package is still a work in progress; see the sections below for planned +// changes. +// +// Syntax +// +// The syntax is based on that used by git config: +// http://git-scm.com/docs/git-config#_syntax . +// There are some (planned) differences compared to the git config format: +// - improve data portability: +// - must be encoded in UTF-8 (for now) and must not contain the 0 byte +// - include and "path" type is not supported +// (path type may be implementable as a user-defined type) +// - internationalization +// - section and variable names can contain unicode letters, unicode digits +// (as defined in http://golang.org/ref/spec#Characters ) and hyphens +// (U+002D), starting with a unicode letter +// - disallow potentially ambiguous or misleading definitions: +// - `[sec.sub]` format is not allowed (deprecated in gitconfig) +// - `[sec ""]` is not allowed +// - use `[sec]` for section name "sec" and empty subsection name +// - (planned) within a single file, definitions must be contiguous for each: +// - section: '[secA]' -> '[secB]' -> '[secA]' is an error +// - subsection: '[sec "A"]' -> '[sec "B"]' -> '[sec "A"]' is an error +// - multivalued variable: 'multi=a' -> 'other=x' -> 'multi=b' is an error +// +// Data structure +// +// The functions in this package read values into a user-defined struct. +// Each section corresponds to a struct field in the config struct, and each +// variable in a section corresponds to a data field in the section struct. +// The mapping of each section or variable name to fields is done either based +// on the "gcfg" struct tag or by matching the name of the section or variable, +// ignoring case. In the latter case, hyphens '-' in section and variable names +// correspond to underscores '_' in field names. +// Fields must be exported; to use a section or variable name starting with a +// letter that is neither upper- or lower-case, prefix the field name with 'X'. +// (See https://code.google.com/p/go/issues/detail?id=5763#c4 .) +// +// For sections with subsections, the corresponding field in config must be a +// map, rather than a struct, with string keys and pointer-to-struct values. +// Values for subsection variables are stored in the map with the subsection +// name used as the map key. +// (Note that unlike section and variable names, subsection names are case +// sensitive.) +// When using a map, and there is a section with the same section name but +// without a subsection name, its values are stored with the empty string used +// as the key. +// It is possible to provide default values for subsections in the section +// "default-" (or by setting values in the corresponding struct +// field "Default_"). +// +// The functions in this package panic if config is not a pointer to a struct, +// or when a field is not of a suitable type (either a struct or a map with +// string keys and pointer-to-struct values). +// +// Parsing of values +// +// The section structs in the config struct may contain single-valued or +// multi-valued variables. Variables of unnamed slice type (that is, a type +// starting with `[]`) are treated as multi-value; all others (including named +// slice types) are treated as single-valued variables. +// +// Single-valued variables are handled based on the type as follows. +// Unnamed pointer types (that is, types starting with `*`) are dereferenced, +// and if necessary, a new instance is allocated. +// +// For types implementing the encoding.TextUnmarshaler interface, the +// UnmarshalText method is used to set the value. Implementing this method is +// the recommended way for parsing user-defined types. +// +// For fields of string kind, the value string is assigned to the field, after +// unquoting and unescaping as needed. +// For fields of bool kind, the field is set to true if the value is "true", +// "yes", "on" or "1", and set to false if the value is "false", "no", "off" or +// "0", ignoring case. In addition, single-valued bool fields can be specified +// with a "blank" value (variable name without equals sign and value); in such +// case the value is set to true. +// +// Predefined integer types [u]int(|8|16|32|64) and big.Int are parsed as +// decimal or hexadecimal (if having '0x' prefix). (This is to prevent +// unintuitively handling zero-padded numbers as octal.) Other types having +// [u]int* as the underlying type, such as os.FileMode and uintptr allow +// decimal, hexadecimal, or octal values. +// Parsing mode for integer types can be overridden using the struct tag option +// ",int=mode" where mode is a combination of the 'd', 'h', and 'o' characters +// (each standing for decimal, hexadecimal, and octal, respectively.) +// +// All other types are parsed using fmt.Sscanf with the "%v" verb. +// +// For multi-valued variables, each individual value is parsed as above and +// appended to the slice. If the first value is specified as a "blank" value +// (variable name without equals sign and value), a new slice is allocated; +// that is any values previously set in the slice will be ignored. +// +// The types subpackage for provides helpers for parsing "enum-like" and integer +// types. +// +// Error handling +// +// There are 3 types of errors: +// +// - programmer errors / panics: +// - invalid configuration structure +// - data errors: +// - fatal errors: +// - invalid configuration syntax +// - warnings: +// - data that doesn't belong to any part of the config structure +// +// Programmer errors trigger panics. These are should be fixed by the programmer +// before releasing code that uses gcfg. +// +// Data errors cause gcfg to return a non-nil error value. This includes the +// case when there are extra unknown key-value definitions in the configuration +// data (extra data). +// However, in some occasions it is desirable to be able to proceed in +// situations when the only data error is that of extra data. +// These errors are handled at a different (warning) priority and can be +// filtered out programmatically. To ignore extra data warnings, wrap the +// gcfg.Read*Into invocation into a call to gcfg.FatalOnly. +// +// TODO +// +// The following is a list of changes under consideration: +// - documentation +// - self-contained syntax documentation +// - more practical examples +// - move TODOs to issue tracker (eventually) +// - syntax +// - reconsider valid escape sequences +// (gitconfig doesn't support \r in value, \t in subsection name, etc.) +// - reading / parsing gcfg files +// - define internal representation structure +// - support multiple inputs (readers, strings, files) +// - support declaring encoding (?) +// - support varying fields sets for subsections (?) +// - writing gcfg files +// - error handling +// - make error context accessible programmatically? +// - limit input size? +// +package gcfg // import "github.com/src-d/gcfg" diff --git a/vendor/github.com/src-d/gcfg/errors.go b/vendor/github.com/src-d/gcfg/errors.go new file mode 100644 index 0000000000..853c76021d --- /dev/null +++ b/vendor/github.com/src-d/gcfg/errors.go @@ -0,0 +1,41 @@ +package gcfg + +import ( + "gopkg.in/warnings.v0" +) + +// FatalOnly filters the results of a Read*Into invocation and returns only +// fatal errors. That is, errors (warnings) indicating data for unknown +// sections / variables is ignored. Example invocation: +// +// err := gcfg.FatalOnly(gcfg.ReadFileInto(&cfg, configFile)) +// if err != nil { +// ... +// +func FatalOnly(err error) error { + return warnings.FatalOnly(err) +} + +func isFatal(err error) bool { + _, ok := err.(extraData) + return !ok +} + +type extraData struct { + section string + subsection *string + variable *string +} + +func (e extraData) Error() string { + s := "can't store data at section \"" + e.section + "\"" + if e.subsection != nil { + s += ", subsection \"" + *e.subsection + "\"" + } + if e.variable != nil { + s += ", variable \"" + *e.variable + "\"" + } + return s +} + +var _ error = extraData{} diff --git a/vendor/github.com/src-d/gcfg/go1_0.go b/vendor/github.com/src-d/gcfg/go1_0.go new file mode 100644 index 0000000000..6670210791 --- /dev/null +++ b/vendor/github.com/src-d/gcfg/go1_0.go @@ -0,0 +1,7 @@ +// +build !go1.2 + +package gcfg + +type textUnmarshaler interface { + UnmarshalText(text []byte) error +} diff --git a/vendor/github.com/src-d/gcfg/go1_2.go b/vendor/github.com/src-d/gcfg/go1_2.go new file mode 100644 index 0000000000..6f5843bc7c --- /dev/null +++ b/vendor/github.com/src-d/gcfg/go1_2.go @@ -0,0 +1,9 @@ +// +build go1.2 + +package gcfg + +import ( + "encoding" +) + +type textUnmarshaler encoding.TextUnmarshaler diff --git a/vendor/github.com/src-d/gcfg/read.go b/vendor/github.com/src-d/gcfg/read.go new file mode 100644 index 0000000000..fff0448c78 --- /dev/null +++ b/vendor/github.com/src-d/gcfg/read.go @@ -0,0 +1,273 @@ +package gcfg + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "strings" + + "github.com/src-d/gcfg/scanner" + "github.com/src-d/gcfg/token" + "gopkg.in/warnings.v0" +) + +var unescape = map[rune]rune{'\\': '\\', '"': '"', 'n': '\n', 't': '\t', 'b': '\b'} + +// no error: invalid literals should be caught by scanner +func unquote(s string) string { + u, q, esc := make([]rune, 0, len(s)), false, false + for _, c := range s { + if esc { + uc, ok := unescape[c] + switch { + case ok: + u = append(u, uc) + fallthrough + case !q && c == '\n': + esc = false + continue + } + panic("invalid escape sequence") + } + switch c { + case '"': + q = !q + case '\\': + esc = true + default: + u = append(u, c) + } + } + if q { + panic("missing end quote") + } + if esc { + panic("invalid escape sequence") + } + return string(u) +} + +func read(c *warnings.Collector, callback func(string, string, string, string, bool) error, + fset *token.FileSet, file *token.File, src []byte) error { + // + var s scanner.Scanner + var errs scanner.ErrorList + s.Init(file, src, func(p token.Position, m string) { errs.Add(p, m) }, 0) + sect, sectsub := "", "" + pos, tok, lit := s.Scan() + errfn := func(msg string) error { + return fmt.Errorf("%s: %s", fset.Position(pos), msg) + } + for { + if errs.Len() > 0 { + if err := c.Collect(errs.Err()); err != nil { + return err + } + } + switch tok { + case token.EOF: + return nil + case token.EOL, token.COMMENT: + pos, tok, lit = s.Scan() + case token.LBRACK: + pos, tok, lit = s.Scan() + if errs.Len() > 0 { + if err := c.Collect(errs.Err()); err != nil { + return err + } + } + if tok != token.IDENT { + if err := c.Collect(errfn("expected section name")); err != nil { + return err + } + } + sect, sectsub = lit, "" + pos, tok, lit = s.Scan() + if errs.Len() > 0 { + if err := c.Collect(errs.Err()); err != nil { + return err + } + } + if tok == token.STRING { + sectsub = unquote(lit) + if sectsub == "" { + if err := c.Collect(errfn("empty subsection name")); err != nil { + return err + } + } + pos, tok, lit = s.Scan() + if errs.Len() > 0 { + if err := c.Collect(errs.Err()); err != nil { + return err + } + } + } + if tok != token.RBRACK { + if sectsub == "" { + if err := c.Collect(errfn("expected subsection name or right bracket")); err != nil { + return err + } + } + if err := c.Collect(errfn("expected right bracket")); err != nil { + return err + } + } + pos, tok, lit = s.Scan() + if tok != token.EOL && tok != token.EOF && tok != token.COMMENT { + if err := c.Collect(errfn("expected EOL, EOF, or comment")); err != nil { + return err + } + } + // If a section/subsection header was found, ensure a + // container object is created, even if there are no + // variables further down. + err := c.Collect(callback(sect, sectsub, "", "", true)) + if err != nil { + return err + } + case token.IDENT: + if sect == "" { + if err := c.Collect(errfn("expected section header")); err != nil { + return err + } + } + n := lit + pos, tok, lit = s.Scan() + if errs.Len() > 0 { + return errs.Err() + } + blank, v := tok == token.EOF || tok == token.EOL || tok == token.COMMENT, "" + if !blank { + if tok != token.ASSIGN { + if err := c.Collect(errfn("expected '='")); err != nil { + return err + } + } + pos, tok, lit = s.Scan() + if errs.Len() > 0 { + if err := c.Collect(errs.Err()); err != nil { + return err + } + } + if tok != token.STRING { + if err := c.Collect(errfn("expected value")); err != nil { + return err + } + } + v = unquote(lit) + pos, tok, lit = s.Scan() + if errs.Len() > 0 { + if err := c.Collect(errs.Err()); err != nil { + return err + } + } + if tok != token.EOL && tok != token.EOF && tok != token.COMMENT { + if err := c.Collect(errfn("expected EOL, EOF, or comment")); err != nil { + return err + } + } + } + err := c.Collect(callback(sect, sectsub, n, v, blank)) + if err != nil { + return err + } + default: + if sect == "" { + if err := c.Collect(errfn("expected section header")); err != nil { + return err + } + } + if err := c.Collect(errfn("expected section header or variable declaration")); err != nil { + return err + } + } + } + panic("never reached") +} + +func readInto(config interface{}, fset *token.FileSet, file *token.File, + src []byte) error { + // + c := warnings.NewCollector(isFatal) + firstPassCallback := func(s string, ss string, k string, v string, bv bool) error { + return set(c, config, s, ss, k, v, bv, false) + } + err := read(c, firstPassCallback, fset, file, src) + if err != nil { + return err + } + secondPassCallback := func(s string, ss string, k string, v string, bv bool) error { + return set(c, config, s, ss, k, v, bv, true) + } + err = read(c, secondPassCallback, fset, file, src) + if err != nil { + return err + } + return c.Done() +} + +// ReadWithCallback reads gcfg formatted data from reader and calls +// callback with each section and option found. +// +// Callback is called with section, subsection, option key, option value +// and blank value flag as arguments. +// +// When a section is found, callback is called with nil subsection, option key +// and option value. +// +// When a subsection is found, callback is called with nil option key and +// option value. +// +// If blank value flag is true, it means that the value was not set for an option +// (as opposed to set to empty string). +// +// If callback returns an error, ReadWithCallback terminates with an error too. +func ReadWithCallback(reader io.Reader, callback func(string, string, string, string, bool) error) error { + src, err := ioutil.ReadAll(reader) + if err != nil { + return err + } + + fset := token.NewFileSet() + file := fset.AddFile("", fset.Base(), len(src)) + c := warnings.NewCollector(isFatal) + + return read(c, callback, fset, file, src) +} + +// ReadInto reads gcfg formatted data from reader and sets the values into the +// corresponding fields in config. +func ReadInto(config interface{}, reader io.Reader) error { + src, err := ioutil.ReadAll(reader) + if err != nil { + return err + } + fset := token.NewFileSet() + file := fset.AddFile("", fset.Base(), len(src)) + return readInto(config, fset, file, src) +} + +// ReadStringInto reads gcfg formatted data from str and sets the values into +// the corresponding fields in config. +func ReadStringInto(config interface{}, str string) error { + r := strings.NewReader(str) + return ReadInto(config, r) +} + +// ReadFileInto reads gcfg formatted data from the file filename and sets the +// values into the corresponding fields in config. +func ReadFileInto(config interface{}, filename string) error { + f, err := os.Open(filename) + if err != nil { + return err + } + defer f.Close() + src, err := ioutil.ReadAll(f) + if err != nil { + return err + } + fset := token.NewFileSet() + file := fset.AddFile(filename, fset.Base(), len(src)) + return readInto(config, fset, file, src) +} diff --git a/vendor/github.com/src-d/gcfg/scanner/errors.go b/vendor/github.com/src-d/gcfg/scanner/errors.go new file mode 100644 index 0000000000..f3fcecacbb --- /dev/null +++ b/vendor/github.com/src-d/gcfg/scanner/errors.go @@ -0,0 +1,121 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package scanner + +import ( + "fmt" + "io" + "sort" +) + +import ( + "github.com/src-d/gcfg/token" +) + +// In an ErrorList, an error is represented by an *Error. +// The position Pos, if valid, points to the beginning of +// the offending token, and the error condition is described +// by Msg. +// +type Error struct { + Pos token.Position + Msg string +} + +// Error implements the error interface. +func (e Error) Error() string { + if e.Pos.Filename != "" || e.Pos.IsValid() { + // don't print "" + // TODO(gri) reconsider the semantics of Position.IsValid + return e.Pos.String() + ": " + e.Msg + } + return e.Msg +} + +// ErrorList is a list of *Errors. +// The zero value for an ErrorList is an empty ErrorList ready to use. +// +type ErrorList []*Error + +// Add adds an Error with given position and error message to an ErrorList. +func (p *ErrorList) Add(pos token.Position, msg string) { + *p = append(*p, &Error{pos, msg}) +} + +// Reset resets an ErrorList to no errors. +func (p *ErrorList) Reset() { *p = (*p)[0:0] } + +// ErrorList implements the sort Interface. +func (p ErrorList) Len() int { return len(p) } +func (p ErrorList) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + +func (p ErrorList) Less(i, j int) bool { + e := &p[i].Pos + f := &p[j].Pos + if e.Filename < f.Filename { + return true + } + if e.Filename == f.Filename { + return e.Offset < f.Offset + } + return false +} + +// Sort sorts an ErrorList. *Error entries are sorted by position, +// other errors are sorted by error message, and before any *Error +// entry. +// +func (p ErrorList) Sort() { + sort.Sort(p) +} + +// RemoveMultiples sorts an ErrorList and removes all but the first error per line. +func (p *ErrorList) RemoveMultiples() { + sort.Sort(p) + var last token.Position // initial last.Line is != any legal error line + i := 0 + for _, e := range *p { + if e.Pos.Filename != last.Filename || e.Pos.Line != last.Line { + last = e.Pos + (*p)[i] = e + i++ + } + } + (*p) = (*p)[0:i] +} + +// An ErrorList implements the error interface. +func (p ErrorList) Error() string { + switch len(p) { + case 0: + return "no errors" + case 1: + return p[0].Error() + } + return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1) +} + +// Err returns an error equivalent to this error list. +// If the list is empty, Err returns nil. +func (p ErrorList) Err() error { + if len(p) == 0 { + return nil + } + return p +} + +// PrintError is a utility function that prints a list of errors to w, +// one error per line, if the err parameter is an ErrorList. Otherwise +// it prints the err string. +// +func PrintError(w io.Writer, err error) { + if list, ok := err.(ErrorList); ok { + for _, e := range list { + fmt.Fprintf(w, "%s\n", e) + } + } else if err != nil { + fmt.Fprintf(w, "%s\n", err) + } +} diff --git a/vendor/github.com/src-d/gcfg/scanner/scanner.go b/vendor/github.com/src-d/gcfg/scanner/scanner.go new file mode 100644 index 0000000000..b1eef06f69 --- /dev/null +++ b/vendor/github.com/src-d/gcfg/scanner/scanner.go @@ -0,0 +1,342 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package scanner implements a scanner for gcfg configuration text. +// It takes a []byte as source which can then be tokenized +// through repeated calls to the Scan method. +// +// Note that the API for the scanner package may change to accommodate new +// features or implementation changes in gcfg. +// +package scanner + +import ( + "fmt" + "path/filepath" + "unicode" + "unicode/utf8" +) + +import ( + "github.com/src-d/gcfg/token" +) + +// An ErrorHandler may be provided to Scanner.Init. If a syntax error is +// encountered and a handler was installed, the handler is called with a +// position and an error message. The position points to the beginning of +// the offending token. +// +type ErrorHandler func(pos token.Position, msg string) + +// A Scanner holds the scanner's internal state while processing +// a given text. It can be allocated as part of another data +// structure but must be initialized via Init before use. +// +type Scanner struct { + // immutable state + file *token.File // source file handle + dir string // directory portion of file.Name() + src []byte // source + err ErrorHandler // error reporting; or nil + mode Mode // scanning mode + + // scanning state + ch rune // current character + offset int // character offset + rdOffset int // reading offset (position after current character) + lineOffset int // current line offset + nextVal bool // next token is expected to be a value + + // public state - ok to modify + ErrorCount int // number of errors encountered +} + +// Read the next Unicode char into s.ch. +// s.ch < 0 means end-of-file. +// +func (s *Scanner) next() { + if s.rdOffset < len(s.src) { + s.offset = s.rdOffset + if s.ch == '\n' { + s.lineOffset = s.offset + s.file.AddLine(s.offset) + } + r, w := rune(s.src[s.rdOffset]), 1 + switch { + case r == 0: + s.error(s.offset, "illegal character NUL") + case r >= 0x80: + // not ASCII + r, w = utf8.DecodeRune(s.src[s.rdOffset:]) + if r == utf8.RuneError && w == 1 { + s.error(s.offset, "illegal UTF-8 encoding") + } + } + s.rdOffset += w + s.ch = r + } else { + s.offset = len(s.src) + if s.ch == '\n' { + s.lineOffset = s.offset + s.file.AddLine(s.offset) + } + s.ch = -1 // eof + } +} + +// A mode value is a set of flags (or 0). +// They control scanner behavior. +// +type Mode uint + +const ( + ScanComments Mode = 1 << iota // return comments as COMMENT tokens +) + +// Init prepares the scanner s to tokenize the text src by setting the +// scanner at the beginning of src. The scanner uses the file set file +// for position information and it adds line information for each line. +// It is ok to re-use the same file when re-scanning the same file as +// line information which is already present is ignored. Init causes a +// panic if the file size does not match the src size. +// +// Calls to Scan will invoke the error handler err if they encounter a +// syntax error and err is not nil. Also, for each error encountered, +// the Scanner field ErrorCount is incremented by one. The mode parameter +// determines how comments are handled. +// +// Note that Init may call err if there is an error in the first character +// of the file. +// +func (s *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode Mode) { + // Explicitly initialize all fields since a scanner may be reused. + if file.Size() != len(src) { + panic(fmt.Sprintf("file size (%d) does not match src len (%d)", file.Size(), len(src))) + } + s.file = file + s.dir, _ = filepath.Split(file.Name()) + s.src = src + s.err = err + s.mode = mode + + s.ch = ' ' + s.offset = 0 + s.rdOffset = 0 + s.lineOffset = 0 + s.ErrorCount = 0 + s.nextVal = false + + s.next() +} + +func (s *Scanner) error(offs int, msg string) { + if s.err != nil { + s.err(s.file.Position(s.file.Pos(offs)), msg) + } + s.ErrorCount++ +} + +func (s *Scanner) scanComment() string { + // initial [;#] already consumed + offs := s.offset - 1 // position of initial [;#] + + for s.ch != '\n' && s.ch >= 0 { + s.next() + } + return string(s.src[offs:s.offset]) +} + +func isLetter(ch rune) bool { + return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch >= 0x80 && unicode.IsLetter(ch) +} + +func isDigit(ch rune) bool { + return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch) +} + +func (s *Scanner) scanIdentifier() string { + offs := s.offset + for isLetter(s.ch) || isDigit(s.ch) || s.ch == '-' { + s.next() + } + return string(s.src[offs:s.offset]) +} + +func (s *Scanner) scanEscape(val bool) { + offs := s.offset + ch := s.ch + s.next() // always make progress + switch ch { + case '\\', '"': + // ok + case 'n', 't', 'b': + if val { + break // ok + } + fallthrough + default: + s.error(offs, "unknown escape sequence") + } +} + +func (s *Scanner) scanString() string { + // '"' opening already consumed + offs := s.offset - 1 + + for s.ch != '"' { + ch := s.ch + s.next() + if ch == '\n' || ch < 0 { + s.error(offs, "string not terminated") + break + } + if ch == '\\' { + s.scanEscape(false) + } + } + + s.next() + + return string(s.src[offs:s.offset]) +} + +func stripCR(b []byte) []byte { + c := make([]byte, len(b)) + i := 0 + for _, ch := range b { + if ch != '\r' { + c[i] = ch + i++ + } + } + return c[:i] +} + +func (s *Scanner) scanValString() string { + offs := s.offset + + hasCR := false + end := offs + inQuote := false +loop: + for inQuote || s.ch >= 0 && s.ch != '\n' && s.ch != ';' && s.ch != '#' { + ch := s.ch + s.next() + switch { + case inQuote && ch == '\\': + s.scanEscape(true) + case !inQuote && ch == '\\': + if s.ch == '\r' { + hasCR = true + s.next() + } + if s.ch != '\n' { + s.scanEscape(true) + } else { + s.next() + } + case ch == '"': + inQuote = !inQuote + case ch == '\r': + hasCR = true + case ch < 0 || inQuote && ch == '\n': + s.error(offs, "string not terminated") + break loop + } + if inQuote || !isWhiteSpace(ch) { + end = s.offset + } + } + + lit := s.src[offs:end] + if hasCR { + lit = stripCR(lit) + } + + return string(lit) +} + +func isWhiteSpace(ch rune) bool { + return ch == ' ' || ch == '\t' || ch == '\r' +} + +func (s *Scanner) skipWhitespace() { + for isWhiteSpace(s.ch) { + s.next() + } +} + +// Scan scans the next token and returns the token position, the token, +// and its literal string if applicable. The source end is indicated by +// token.EOF. +// +// If the returned token is a literal (token.IDENT, token.STRING) or +// token.COMMENT, the literal string has the corresponding value. +// +// If the returned token is token.ILLEGAL, the literal string is the +// offending character. +// +// In all other cases, Scan returns an empty literal string. +// +// For more tolerant parsing, Scan will return a valid token if +// possible even if a syntax error was encountered. Thus, even +// if the resulting token sequence contains no illegal tokens, +// a client may not assume that no error occurred. Instead it +// must check the scanner's ErrorCount or the number of calls +// of the error handler, if there was one installed. +// +// Scan adds line information to the file added to the file +// set with Init. Token positions are relative to that file +// and thus relative to the file set. +// +func (s *Scanner) Scan() (pos token.Pos, tok token.Token, lit string) { +scanAgain: + s.skipWhitespace() + + // current token start + pos = s.file.Pos(s.offset) + + // determine token value + switch ch := s.ch; { + case s.nextVal: + lit = s.scanValString() + tok = token.STRING + s.nextVal = false + case isLetter(ch): + lit = s.scanIdentifier() + tok = token.IDENT + default: + s.next() // always make progress + switch ch { + case -1: + tok = token.EOF + case '\n': + tok = token.EOL + case '"': + tok = token.STRING + lit = s.scanString() + case '[': + tok = token.LBRACK + case ']': + tok = token.RBRACK + case ';', '#': + // comment + lit = s.scanComment() + if s.mode&ScanComments == 0 { + // skip comment + goto scanAgain + } + tok = token.COMMENT + case '=': + tok = token.ASSIGN + s.nextVal = true + default: + s.error(s.file.Offset(pos), fmt.Sprintf("illegal character %#U", ch)) + tok = token.ILLEGAL + lit = string(ch) + } + } + + return +} diff --git a/vendor/github.com/src-d/gcfg/set.go b/vendor/github.com/src-d/gcfg/set.go new file mode 100644 index 0000000000..771258f0ef --- /dev/null +++ b/vendor/github.com/src-d/gcfg/set.go @@ -0,0 +1,332 @@ +package gcfg + +import ( + "bytes" + "encoding/gob" + "fmt" + "math/big" + "reflect" + "strings" + "unicode" + "unicode/utf8" + + "github.com/src-d/gcfg/types" + "gopkg.in/warnings.v0" +) + +type tag struct { + ident string + intMode string +} + +func newTag(ts string) tag { + t := tag{} + s := strings.Split(ts, ",") + t.ident = s[0] + for _, tse := range s[1:] { + if strings.HasPrefix(tse, "int=") { + t.intMode = tse[len("int="):] + } + } + return t +} + +func fieldFold(v reflect.Value, name string) (reflect.Value, tag) { + var n string + r0, _ := utf8.DecodeRuneInString(name) + if unicode.IsLetter(r0) && !unicode.IsLower(r0) && !unicode.IsUpper(r0) { + n = "X" + } + n += strings.Replace(name, "-", "_", -1) + f, ok := v.Type().FieldByNameFunc(func(fieldName string) bool { + if !v.FieldByName(fieldName).CanSet() { + return false + } + f, _ := v.Type().FieldByName(fieldName) + t := newTag(f.Tag.Get("gcfg")) + if t.ident != "" { + return strings.EqualFold(t.ident, name) + } + return strings.EqualFold(n, fieldName) + }) + if !ok { + return reflect.Value{}, tag{} + } + return v.FieldByName(f.Name), newTag(f.Tag.Get("gcfg")) +} + +type setter func(destp interface{}, blank bool, val string, t tag) error + +var errUnsupportedType = fmt.Errorf("unsupported type") +var errBlankUnsupported = fmt.Errorf("blank value not supported for type") + +var setters = []setter{ + typeSetter, textUnmarshalerSetter, kindSetter, scanSetter, +} + +func textUnmarshalerSetter(d interface{}, blank bool, val string, t tag) error { + dtu, ok := d.(textUnmarshaler) + if !ok { + return errUnsupportedType + } + if blank { + return errBlankUnsupported + } + return dtu.UnmarshalText([]byte(val)) +} + +func boolSetter(d interface{}, blank bool, val string, t tag) error { + if blank { + reflect.ValueOf(d).Elem().Set(reflect.ValueOf(true)) + return nil + } + b, err := types.ParseBool(val) + if err == nil { + reflect.ValueOf(d).Elem().Set(reflect.ValueOf(b)) + } + return err +} + +func intMode(mode string) types.IntMode { + var m types.IntMode + if strings.ContainsAny(mode, "dD") { + m |= types.Dec + } + if strings.ContainsAny(mode, "hH") { + m |= types.Hex + } + if strings.ContainsAny(mode, "oO") { + m |= types.Oct + } + return m +} + +var typeModes = map[reflect.Type]types.IntMode{ + reflect.TypeOf(int(0)): types.Dec | types.Hex, + reflect.TypeOf(int8(0)): types.Dec | types.Hex, + reflect.TypeOf(int16(0)): types.Dec | types.Hex, + reflect.TypeOf(int32(0)): types.Dec | types.Hex, + reflect.TypeOf(int64(0)): types.Dec | types.Hex, + reflect.TypeOf(uint(0)): types.Dec | types.Hex, + reflect.TypeOf(uint8(0)): types.Dec | types.Hex, + reflect.TypeOf(uint16(0)): types.Dec | types.Hex, + reflect.TypeOf(uint32(0)): types.Dec | types.Hex, + reflect.TypeOf(uint64(0)): types.Dec | types.Hex, + // use default mode (allow dec/hex/oct) for uintptr type + reflect.TypeOf(big.Int{}): types.Dec | types.Hex, +} + +func intModeDefault(t reflect.Type) types.IntMode { + m, ok := typeModes[t] + if !ok { + m = types.Dec | types.Hex | types.Oct + } + return m +} + +func intSetter(d interface{}, blank bool, val string, t tag) error { + if blank { + return errBlankUnsupported + } + mode := intMode(t.intMode) + if mode == 0 { + mode = intModeDefault(reflect.TypeOf(d).Elem()) + } + return types.ParseInt(d, val, mode) +} + +func stringSetter(d interface{}, blank bool, val string, t tag) error { + if blank { + return errBlankUnsupported + } + dsp, ok := d.(*string) + if !ok { + return errUnsupportedType + } + *dsp = val + return nil +} + +var kindSetters = map[reflect.Kind]setter{ + reflect.String: stringSetter, + reflect.Bool: boolSetter, + reflect.Int: intSetter, + reflect.Int8: intSetter, + reflect.Int16: intSetter, + reflect.Int32: intSetter, + reflect.Int64: intSetter, + reflect.Uint: intSetter, + reflect.Uint8: intSetter, + reflect.Uint16: intSetter, + reflect.Uint32: intSetter, + reflect.Uint64: intSetter, + reflect.Uintptr: intSetter, +} + +var typeSetters = map[reflect.Type]setter{ + reflect.TypeOf(big.Int{}): intSetter, +} + +func typeSetter(d interface{}, blank bool, val string, tt tag) error { + t := reflect.ValueOf(d).Type().Elem() + setter, ok := typeSetters[t] + if !ok { + return errUnsupportedType + } + return setter(d, blank, val, tt) +} + +func kindSetter(d interface{}, blank bool, val string, tt tag) error { + k := reflect.ValueOf(d).Type().Elem().Kind() + setter, ok := kindSetters[k] + if !ok { + return errUnsupportedType + } + return setter(d, blank, val, tt) +} + +func scanSetter(d interface{}, blank bool, val string, tt tag) error { + if blank { + return errBlankUnsupported + } + return types.ScanFully(d, val, 'v') +} + +func newValue(c *warnings.Collector, sect string, vCfg reflect.Value, + vType reflect.Type) (reflect.Value, error) { + // + pv := reflect.New(vType) + dfltName := "default-" + sect + dfltField, _ := fieldFold(vCfg, dfltName) + var err error + if dfltField.IsValid() { + b := bytes.NewBuffer(nil) + ge := gob.NewEncoder(b) + if err = c.Collect(ge.EncodeValue(dfltField)); err != nil { + return pv, err + } + gd := gob.NewDecoder(bytes.NewReader(b.Bytes())) + if err = c.Collect(gd.DecodeValue(pv.Elem())); err != nil { + return pv, err + } + } + return pv, nil +} + +func set(c *warnings.Collector, cfg interface{}, sect, sub, name string, + value string, blankValue bool, subsectPass bool) error { + // + vPCfg := reflect.ValueOf(cfg) + if vPCfg.Kind() != reflect.Ptr || vPCfg.Elem().Kind() != reflect.Struct { + panic(fmt.Errorf("config must be a pointer to a struct")) + } + vCfg := vPCfg.Elem() + vSect, _ := fieldFold(vCfg, sect) + if !vSect.IsValid() { + err := extraData{section: sect} + return c.Collect(err) + } + isSubsect := vSect.Kind() == reflect.Map + if subsectPass != isSubsect { + return nil + } + if isSubsect { + vst := vSect.Type() + if vst.Key().Kind() != reflect.String || + vst.Elem().Kind() != reflect.Ptr || + vst.Elem().Elem().Kind() != reflect.Struct { + panic(fmt.Errorf("map field for section must have string keys and "+ + " pointer-to-struct values: section %q", sect)) + } + if vSect.IsNil() { + vSect.Set(reflect.MakeMap(vst)) + } + k := reflect.ValueOf(sub) + pv := vSect.MapIndex(k) + if !pv.IsValid() { + vType := vSect.Type().Elem().Elem() + var err error + if pv, err = newValue(c, sect, vCfg, vType); err != nil { + return err + } + vSect.SetMapIndex(k, pv) + } + vSect = pv.Elem() + } else if vSect.Kind() != reflect.Struct { + panic(fmt.Errorf("field for section must be a map or a struct: "+ + "section %q", sect)) + } else if sub != "" { + err := extraData{section: sect, subsection: &sub} + return c.Collect(err) + } + // Empty name is a special value, meaning that only the + // section/subsection object is to be created, with no values set. + if name == "" { + return nil + } + vVar, t := fieldFold(vSect, name) + if !vVar.IsValid() { + var err error + if isSubsect { + err = extraData{section: sect, subsection: &sub, variable: &name} + } else { + err = extraData{section: sect, variable: &name} + } + return c.Collect(err) + } + // vVal is either single-valued var, or newly allocated value within multi-valued var + var vVal reflect.Value + // multi-value if unnamed slice type + isMulti := vVar.Type().Name() == "" && vVar.Kind() == reflect.Slice || + vVar.Type().Name() == "" && vVar.Kind() == reflect.Ptr && vVar.Type().Elem().Name() == "" && vVar.Type().Elem().Kind() == reflect.Slice + if isMulti && vVar.Kind() == reflect.Ptr { + if vVar.IsNil() { + vVar.Set(reflect.New(vVar.Type().Elem())) + } + vVar = vVar.Elem() + } + if isMulti && blankValue { + vVar.Set(reflect.Zero(vVar.Type())) + return nil + } + if isMulti { + vVal = reflect.New(vVar.Type().Elem()).Elem() + } else { + vVal = vVar + } + isDeref := vVal.Type().Name() == "" && vVal.Type().Kind() == reflect.Ptr + isNew := isDeref && vVal.IsNil() + // vAddr is address of value to set (dereferenced & allocated as needed) + var vAddr reflect.Value + switch { + case isNew: + vAddr = reflect.New(vVal.Type().Elem()) + case isDeref && !isNew: + vAddr = vVal + default: + vAddr = vVal.Addr() + } + vAddrI := vAddr.Interface() + err, ok := error(nil), false + for _, s := range setters { + err = s(vAddrI, blankValue, value, t) + if err == nil { + ok = true + break + } + if err != errUnsupportedType { + return err + } + } + if !ok { + // in case all setters returned errUnsupportedType + return err + } + if isNew { // set reference if it was dereferenced and newly allocated + vVal.Set(vAddr) + } + if isMulti { // append if multi-valued + vVar.Set(reflect.Append(vVar, vVal)) + } + return nil +} diff --git a/vendor/github.com/src-d/gcfg/token/position.go b/vendor/github.com/src-d/gcfg/token/position.go new file mode 100644 index 0000000000..fc45c1e769 --- /dev/null +++ b/vendor/github.com/src-d/gcfg/token/position.go @@ -0,0 +1,435 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO(gri) consider making this a separate package outside the go directory. + +package token + +import ( + "fmt" + "sort" + "sync" +) + +// ----------------------------------------------------------------------------- +// Positions + +// Position describes an arbitrary source position +// including the file, line, and column location. +// A Position is valid if the line number is > 0. +// +type Position struct { + Filename string // filename, if any + Offset int // offset, starting at 0 + Line int // line number, starting at 1 + Column int // column number, starting at 1 (character count) +} + +// IsValid returns true if the position is valid. +func (pos *Position) IsValid() bool { return pos.Line > 0 } + +// String returns a string in one of several forms: +// +// file:line:column valid position with file name +// line:column valid position without file name +// file invalid position with file name +// - invalid position without file name +// +func (pos Position) String() string { + s := pos.Filename + if pos.IsValid() { + if s != "" { + s += ":" + } + s += fmt.Sprintf("%d:%d", pos.Line, pos.Column) + } + if s == "" { + s = "-" + } + return s +} + +// Pos is a compact encoding of a source position within a file set. +// It can be converted into a Position for a more convenient, but much +// larger, representation. +// +// The Pos value for a given file is a number in the range [base, base+size], +// where base and size are specified when adding the file to the file set via +// AddFile. +// +// To create the Pos value for a specific source offset, first add +// the respective file to the current file set (via FileSet.AddFile) +// and then call File.Pos(offset) for that file. Given a Pos value p +// for a specific file set fset, the corresponding Position value is +// obtained by calling fset.Position(p). +// +// Pos values can be compared directly with the usual comparison operators: +// If two Pos values p and q are in the same file, comparing p and q is +// equivalent to comparing the respective source file offsets. If p and q +// are in different files, p < q is true if the file implied by p was added +// to the respective file set before the file implied by q. +// +type Pos int + +// The zero value for Pos is NoPos; there is no file and line information +// associated with it, and NoPos().IsValid() is false. NoPos is always +// smaller than any other Pos value. The corresponding Position value +// for NoPos is the zero value for Position. +// +const NoPos Pos = 0 + +// IsValid returns true if the position is valid. +func (p Pos) IsValid() bool { + return p != NoPos +} + +// ----------------------------------------------------------------------------- +// File + +// A File is a handle for a file belonging to a FileSet. +// A File has a name, size, and line offset table. +// +type File struct { + set *FileSet + name string // file name as provided to AddFile + base int // Pos value range for this file is [base...base+size] + size int // file size as provided to AddFile + + // lines and infos are protected by set.mutex + lines []int + infos []lineInfo +} + +// Name returns the file name of file f as registered with AddFile. +func (f *File) Name() string { + return f.name +} + +// Base returns the base offset of file f as registered with AddFile. +func (f *File) Base() int { + return f.base +} + +// Size returns the size of file f as registered with AddFile. +func (f *File) Size() int { + return f.size +} + +// LineCount returns the number of lines in file f. +func (f *File) LineCount() int { + f.set.mutex.RLock() + n := len(f.lines) + f.set.mutex.RUnlock() + return n +} + +// AddLine adds the line offset for a new line. +// The line offset must be larger than the offset for the previous line +// and smaller than the file size; otherwise the line offset is ignored. +// +func (f *File) AddLine(offset int) { + f.set.mutex.Lock() + if i := len(f.lines); (i == 0 || f.lines[i-1] < offset) && offset < f.size { + f.lines = append(f.lines, offset) + } + f.set.mutex.Unlock() +} + +// SetLines sets the line offsets for a file and returns true if successful. +// The line offsets are the offsets of the first character of each line; +// for instance for the content "ab\nc\n" the line offsets are {0, 3}. +// An empty file has an empty line offset table. +// Each line offset must be larger than the offset for the previous line +// and smaller than the file size; otherwise SetLines fails and returns +// false. +// +func (f *File) SetLines(lines []int) bool { + // verify validity of lines table + size := f.size + for i, offset := range lines { + if i > 0 && offset <= lines[i-1] || size <= offset { + return false + } + } + + // set lines table + f.set.mutex.Lock() + f.lines = lines + f.set.mutex.Unlock() + return true +} + +// SetLinesForContent sets the line offsets for the given file content. +func (f *File) SetLinesForContent(content []byte) { + var lines []int + line := 0 + for offset, b := range content { + if line >= 0 { + lines = append(lines, line) + } + line = -1 + if b == '\n' { + line = offset + 1 + } + } + + // set lines table + f.set.mutex.Lock() + f.lines = lines + f.set.mutex.Unlock() +} + +// A lineInfo object describes alternative file and line number +// information (such as provided via a //line comment in a .go +// file) for a given file offset. +type lineInfo struct { + // fields are exported to make them accessible to gob + Offset int + Filename string + Line int +} + +// AddLineInfo adds alternative file and line number information for +// a given file offset. The offset must be larger than the offset for +// the previously added alternative line info and smaller than the +// file size; otherwise the information is ignored. +// +// AddLineInfo is typically used to register alternative position +// information for //line filename:line comments in source files. +// +func (f *File) AddLineInfo(offset int, filename string, line int) { + f.set.mutex.Lock() + if i := len(f.infos); i == 0 || f.infos[i-1].Offset < offset && offset < f.size { + f.infos = append(f.infos, lineInfo{offset, filename, line}) + } + f.set.mutex.Unlock() +} + +// Pos returns the Pos value for the given file offset; +// the offset must be <= f.Size(). +// f.Pos(f.Offset(p)) == p. +// +func (f *File) Pos(offset int) Pos { + if offset > f.size { + panic("illegal file offset") + } + return Pos(f.base + offset) +} + +// Offset returns the offset for the given file position p; +// p must be a valid Pos value in that file. +// f.Offset(f.Pos(offset)) == offset. +// +func (f *File) Offset(p Pos) int { + if int(p) < f.base || int(p) > f.base+f.size { + panic("illegal Pos value") + } + return int(p) - f.base +} + +// Line returns the line number for the given file position p; +// p must be a Pos value in that file or NoPos. +// +func (f *File) Line(p Pos) int { + // TODO(gri) this can be implemented much more efficiently + return f.Position(p).Line +} + +func searchLineInfos(a []lineInfo, x int) int { + return sort.Search(len(a), func(i int) bool { return a[i].Offset > x }) - 1 +} + +// info returns the file name, line, and column number for a file offset. +func (f *File) info(offset int) (filename string, line, column int) { + filename = f.name + if i := searchInts(f.lines, offset); i >= 0 { + line, column = i+1, offset-f.lines[i]+1 + } + if len(f.infos) > 0 { + // almost no files have extra line infos + if i := searchLineInfos(f.infos, offset); i >= 0 { + alt := &f.infos[i] + filename = alt.Filename + if i := searchInts(f.lines, alt.Offset); i >= 0 { + line += alt.Line - i - 1 + } + } + } + return +} + +func (f *File) position(p Pos) (pos Position) { + offset := int(p) - f.base + pos.Offset = offset + pos.Filename, pos.Line, pos.Column = f.info(offset) + return +} + +// Position returns the Position value for the given file position p; +// p must be a Pos value in that file or NoPos. +// +func (f *File) Position(p Pos) (pos Position) { + if p != NoPos { + if int(p) < f.base || int(p) > f.base+f.size { + panic("illegal Pos value") + } + pos = f.position(p) + } + return +} + +// ----------------------------------------------------------------------------- +// FileSet + +// A FileSet represents a set of source files. +// Methods of file sets are synchronized; multiple goroutines +// may invoke them concurrently. +// +type FileSet struct { + mutex sync.RWMutex // protects the file set + base int // base offset for the next file + files []*File // list of files in the order added to the set + last *File // cache of last file looked up +} + +// NewFileSet creates a new file set. +func NewFileSet() *FileSet { + s := new(FileSet) + s.base = 1 // 0 == NoPos + return s +} + +// Base returns the minimum base offset that must be provided to +// AddFile when adding the next file. +// +func (s *FileSet) Base() int { + s.mutex.RLock() + b := s.base + s.mutex.RUnlock() + return b + +} + +// AddFile adds a new file with a given filename, base offset, and file size +// to the file set s and returns the file. Multiple files may have the same +// name. The base offset must not be smaller than the FileSet's Base(), and +// size must not be negative. +// +// Adding the file will set the file set's Base() value to base + size + 1 +// as the minimum base value for the next file. The following relationship +// exists between a Pos value p for a given file offset offs: +// +// int(p) = base + offs +// +// with offs in the range [0, size] and thus p in the range [base, base+size]. +// For convenience, File.Pos may be used to create file-specific position +// values from a file offset. +// +func (s *FileSet) AddFile(filename string, base, size int) *File { + s.mutex.Lock() + defer s.mutex.Unlock() + if base < s.base || size < 0 { + panic("illegal base or size") + } + // base >= s.base && size >= 0 + f := &File{s, filename, base, size, []int{0}, nil} + base += size + 1 // +1 because EOF also has a position + if base < 0 { + panic("token.Pos offset overflow (> 2G of source code in file set)") + } + // add the file to the file set + s.base = base + s.files = append(s.files, f) + s.last = f + return f +} + +// Iterate calls f for the files in the file set in the order they were added +// until f returns false. +// +func (s *FileSet) Iterate(f func(*File) bool) { + for i := 0; ; i++ { + var file *File + s.mutex.RLock() + if i < len(s.files) { + file = s.files[i] + } + s.mutex.RUnlock() + if file == nil || !f(file) { + break + } + } +} + +func searchFiles(a []*File, x int) int { + return sort.Search(len(a), func(i int) bool { return a[i].base > x }) - 1 +} + +func (s *FileSet) file(p Pos) *File { + // common case: p is in last file + if f := s.last; f != nil && f.base <= int(p) && int(p) <= f.base+f.size { + return f + } + // p is not in last file - search all files + if i := searchFiles(s.files, int(p)); i >= 0 { + f := s.files[i] + // f.base <= int(p) by definition of searchFiles + if int(p) <= f.base+f.size { + s.last = f + return f + } + } + return nil +} + +// File returns the file that contains the position p. +// If no such file is found (for instance for p == NoPos), +// the result is nil. +// +func (s *FileSet) File(p Pos) (f *File) { + if p != NoPos { + s.mutex.RLock() + f = s.file(p) + s.mutex.RUnlock() + } + return +} + +// Position converts a Pos in the fileset into a general Position. +func (s *FileSet) Position(p Pos) (pos Position) { + if p != NoPos { + s.mutex.RLock() + if f := s.file(p); f != nil { + pos = f.position(p) + } + s.mutex.RUnlock() + } + return +} + +// ----------------------------------------------------------------------------- +// Helper functions + +func searchInts(a []int, x int) int { + // This function body is a manually inlined version of: + // + // return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1 + // + // With better compiler optimizations, this may not be needed in the + // future, but at the moment this change improves the go/printer + // benchmark performance by ~30%. This has a direct impact on the + // speed of gofmt and thus seems worthwhile (2011-04-29). + // TODO(gri): Remove this when compilers have caught up. + i, j := 0, len(a) + for i < j { + h := i + (j-i)/2 // avoid overflow when computing h + // i ≤ h < j + if a[h] <= x { + i = h + 1 + } else { + j = h + } + } + return i - 1 +} diff --git a/vendor/github.com/src-d/gcfg/token/serialize.go b/vendor/github.com/src-d/gcfg/token/serialize.go new file mode 100644 index 0000000000..4adc8f9e33 --- /dev/null +++ b/vendor/github.com/src-d/gcfg/token/serialize.go @@ -0,0 +1,56 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package token + +type serializedFile struct { + // fields correspond 1:1 to fields with same (lower-case) name in File + Name string + Base int + Size int + Lines []int + Infos []lineInfo +} + +type serializedFileSet struct { + Base int + Files []serializedFile +} + +// Read calls decode to deserialize a file set into s; s must not be nil. +func (s *FileSet) Read(decode func(interface{}) error) error { + var ss serializedFileSet + if err := decode(&ss); err != nil { + return err + } + + s.mutex.Lock() + s.base = ss.Base + files := make([]*File, len(ss.Files)) + for i := 0; i < len(ss.Files); i++ { + f := &ss.Files[i] + files[i] = &File{s, f.Name, f.Base, f.Size, f.Lines, f.Infos} + } + s.files = files + s.last = nil + s.mutex.Unlock() + + return nil +} + +// Write calls encode to serialize the file set s. +func (s *FileSet) Write(encode func(interface{}) error) error { + var ss serializedFileSet + + s.mutex.Lock() + ss.Base = s.base + files := make([]serializedFile, len(s.files)) + for i, f := range s.files { + files[i] = serializedFile{f.name, f.base, f.size, f.lines, f.infos} + } + ss.Files = files + s.mutex.Unlock() + + return encode(ss) +} diff --git a/vendor/github.com/src-d/gcfg/token/token.go b/vendor/github.com/src-d/gcfg/token/token.go new file mode 100644 index 0000000000..b3c7c83fa9 --- /dev/null +++ b/vendor/github.com/src-d/gcfg/token/token.go @@ -0,0 +1,83 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package token defines constants representing the lexical tokens of the gcfg +// configuration syntax and basic operations on tokens (printing, predicates). +// +// Note that the API for the token package may change to accommodate new +// features or implementation changes in gcfg. +// +package token + +import "strconv" + +// Token is the set of lexical tokens of the gcfg configuration syntax. +type Token int + +// The list of tokens. +const ( + // Special tokens + ILLEGAL Token = iota + EOF + COMMENT + + literal_beg + // Identifiers and basic type literals + // (these tokens stand for classes of literals) + IDENT // section-name, variable-name + STRING // "subsection-name", variable value + literal_end + + operator_beg + // Operators and delimiters + ASSIGN // = + LBRACK // [ + RBRACK // ] + EOL // \n + operator_end +) + +var tokens = [...]string{ + ILLEGAL: "ILLEGAL", + + EOF: "EOF", + COMMENT: "COMMENT", + + IDENT: "IDENT", + STRING: "STRING", + + ASSIGN: "=", + LBRACK: "[", + RBRACK: "]", + EOL: "\n", +} + +// String returns the string corresponding to the token tok. +// For operators and delimiters, the string is the actual token character +// sequence (e.g., for the token ASSIGN, the string is "="). For all other +// tokens the string corresponds to the token constant name (e.g. for the +// token IDENT, the string is "IDENT"). +// +func (tok Token) String() string { + s := "" + if 0 <= tok && tok < Token(len(tokens)) { + s = tokens[tok] + } + if s == "" { + s = "token(" + strconv.Itoa(int(tok)) + ")" + } + return s +} + +// Predicates + +// IsLiteral returns true for tokens corresponding to identifiers +// and basic type literals; it returns false otherwise. +// +func (tok Token) IsLiteral() bool { return literal_beg < tok && tok < literal_end } + +// IsOperator returns true for tokens corresponding to operators and +// delimiters; it returns false otherwise. +// +func (tok Token) IsOperator() bool { return operator_beg < tok && tok < operator_end } diff --git a/vendor/github.com/src-d/gcfg/types/bool.go b/vendor/github.com/src-d/gcfg/types/bool.go new file mode 100644 index 0000000000..8dcae0d8cf --- /dev/null +++ b/vendor/github.com/src-d/gcfg/types/bool.go @@ -0,0 +1,23 @@ +package types + +// BoolValues defines the name and value mappings for ParseBool. +var BoolValues = map[string]interface{}{ + "true": true, "yes": true, "on": true, "1": true, + "false": false, "no": false, "off": false, "0": false, +} + +var boolParser = func() *EnumParser { + ep := &EnumParser{} + ep.AddVals(BoolValues) + return ep +}() + +// ParseBool parses bool values according to the definitions in BoolValues. +// Parsing is case-insensitive. +func ParseBool(s string) (bool, error) { + v, err := boolParser.Parse(s) + if err != nil { + return false, err + } + return v.(bool), nil +} diff --git a/vendor/github.com/src-d/gcfg/types/doc.go b/vendor/github.com/src-d/gcfg/types/doc.go new file mode 100644 index 0000000000..9f9c345f6e --- /dev/null +++ b/vendor/github.com/src-d/gcfg/types/doc.go @@ -0,0 +1,4 @@ +// Package types defines helpers for type conversions. +// +// The API for this package is not finalized yet. +package types diff --git a/vendor/github.com/src-d/gcfg/types/enum.go b/vendor/github.com/src-d/gcfg/types/enum.go new file mode 100644 index 0000000000..1a0c7ef453 --- /dev/null +++ b/vendor/github.com/src-d/gcfg/types/enum.go @@ -0,0 +1,44 @@ +package types + +import ( + "fmt" + "reflect" + "strings" +) + +// EnumParser parses "enum" values; i.e. a predefined set of strings to +// predefined values. +type EnumParser struct { + Type string // type name; if not set, use type of first value added + CaseMatch bool // if true, matching of strings is case-sensitive + // PrefixMatch bool + vals map[string]interface{} +} + +// AddVals adds strings and values to an EnumParser. +func (ep *EnumParser) AddVals(vals map[string]interface{}) { + if ep.vals == nil { + ep.vals = make(map[string]interface{}) + } + for k, v := range vals { + if ep.Type == "" { + ep.Type = reflect.TypeOf(v).Name() + } + if !ep.CaseMatch { + k = strings.ToLower(k) + } + ep.vals[k] = v + } +} + +// Parse parses the string and returns the value or an error. +func (ep EnumParser) Parse(s string) (interface{}, error) { + if !ep.CaseMatch { + s = strings.ToLower(s) + } + v, ok := ep.vals[s] + if !ok { + return false, fmt.Errorf("failed to parse %s %#q", ep.Type, s) + } + return v, nil +} diff --git a/vendor/github.com/src-d/gcfg/types/int.go b/vendor/github.com/src-d/gcfg/types/int.go new file mode 100644 index 0000000000..af7e75c125 --- /dev/null +++ b/vendor/github.com/src-d/gcfg/types/int.go @@ -0,0 +1,86 @@ +package types + +import ( + "fmt" + "strings" +) + +// An IntMode is a mode for parsing integer values, representing a set of +// accepted bases. +type IntMode uint8 + +// IntMode values for ParseInt; can be combined using binary or. +const ( + Dec IntMode = 1 << iota + Hex + Oct +) + +// String returns a string representation of IntMode; e.g. `IntMode(Dec|Hex)`. +func (m IntMode) String() string { + var modes []string + if m&Dec != 0 { + modes = append(modes, "Dec") + } + if m&Hex != 0 { + modes = append(modes, "Hex") + } + if m&Oct != 0 { + modes = append(modes, "Oct") + } + return "IntMode(" + strings.Join(modes, "|") + ")" +} + +var errIntAmbig = fmt.Errorf("ambiguous integer value; must include '0' prefix") + +func prefix0(val string) bool { + return strings.HasPrefix(val, "0") || strings.HasPrefix(val, "-0") +} + +func prefix0x(val string) bool { + return strings.HasPrefix(val, "0x") || strings.HasPrefix(val, "-0x") +} + +// ParseInt parses val using mode into intptr, which must be a pointer to an +// integer kind type. Non-decimal value require prefix `0` or `0x` in the cases +// when mode permits ambiguity of base; otherwise the prefix can be omitted. +func ParseInt(intptr interface{}, val string, mode IntMode) error { + val = strings.TrimSpace(val) + verb := byte(0) + switch mode { + case Dec: + verb = 'd' + case Dec + Hex: + if prefix0x(val) { + verb = 'v' + } else { + verb = 'd' + } + case Dec + Oct: + if prefix0(val) && !prefix0x(val) { + verb = 'v' + } else { + verb = 'd' + } + case Dec + Hex + Oct: + verb = 'v' + case Hex: + if prefix0x(val) { + verb = 'v' + } else { + verb = 'x' + } + case Oct: + verb = 'o' + case Hex + Oct: + if prefix0(val) { + verb = 'v' + } else { + return errIntAmbig + } + } + if verb == 0 { + panic("unsupported mode") + } + return ScanFully(intptr, val, verb) +} diff --git a/vendor/github.com/src-d/gcfg/types/scan.go b/vendor/github.com/src-d/gcfg/types/scan.go new file mode 100644 index 0000000000..db2f6ed3ca --- /dev/null +++ b/vendor/github.com/src-d/gcfg/types/scan.go @@ -0,0 +1,23 @@ +package types + +import ( + "fmt" + "io" + "reflect" +) + +// ScanFully uses fmt.Sscanf with verb to fully scan val into ptr. +func ScanFully(ptr interface{}, val string, verb byte) error { + t := reflect.ValueOf(ptr).Elem().Type() + // attempt to read extra bytes to make sure the value is consumed + var b []byte + n, err := fmt.Sscanf(val, "%"+string(verb)+"%s", ptr, &b) + switch { + case n < 1 || n == 1 && err != io.EOF: + return fmt.Errorf("failed to parse %q as %v: %v", val, t, err) + case n > 1: + return fmt.Errorf("failed to parse %q as %v: extra characters %q", val, t, string(b)) + } + // n == 1 && err == io.EOF + return nil +} diff --git a/vendor/github.com/stoewer/go-strcase/.gitignore b/vendor/github.com/stoewer/go-strcase/.gitignore new file mode 100644 index 0000000000..db5247b944 --- /dev/null +++ b/vendor/github.com/stoewer/go-strcase/.gitignore @@ -0,0 +1,17 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +vendor +doc + +# Temporary files +*~ +*.swp + +# Editor and IDE config +.idea +*.iml +.vscode diff --git a/vendor/github.com/stoewer/go-strcase/.golangci.yml b/vendor/github.com/stoewer/go-strcase/.golangci.yml new file mode 100644 index 0000000000..7f98d55c42 --- /dev/null +++ b/vendor/github.com/stoewer/go-strcase/.golangci.yml @@ -0,0 +1,26 @@ +run: + deadline: 10m + +linters: + enable: + - dupl + - goconst + - gocyclo + - godox + - gosec + - interfacer + - lll + - maligned + - misspell + - prealloc + - stylecheck + - unconvert + - unparam + - errcheck + - golint + - gofmt + disable: [] + fast: false + +issues: + exclude-use-default: false diff --git a/vendor/github.com/stoewer/go-strcase/LICENSE b/vendor/github.com/stoewer/go-strcase/LICENSE new file mode 100644 index 0000000000..a105a3819a --- /dev/null +++ b/vendor/github.com/stoewer/go-strcase/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017, Adrian Stoewer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/stoewer/go-strcase/README.md b/vendor/github.com/stoewer/go-strcase/README.md new file mode 100644 index 0000000000..0e8635d801 --- /dev/null +++ b/vendor/github.com/stoewer/go-strcase/README.md @@ -0,0 +1,50 @@ +[![CircleCI](https://circleci.com/gh/stoewer/go-strcase/tree/master.svg?style=svg)](https://circleci.com/gh/stoewer/go-strcase/tree/master) +[![codecov](https://codecov.io/gh/stoewer/go-strcase/branch/master/graph/badge.svg)](https://codecov.io/gh/stoewer/go-strcase) +[![GoDoc](https://godoc.org/github.com/stoewer/go-strcase?status.svg)](https://pkg.go.dev/github.com/stoewer/go-strcase) +--- + +Go strcase +========== + +The package `strcase` converts between different kinds of naming formats such as camel case +(`CamelCase`), snake case (`snake_case`) or kebab case (`kebab-case`). +The package is designed to work only with strings consisting of standard ASCII letters. +Unicode is currently not supported. + +Versioning and stability +------------------------ + +Although the master branch is supposed to remain always backward compatible, the repository +contains version tags in order to support vendoring tools. +The tag names follow semantic versioning conventions and have the following format `v1.0.0`. +This package supports Go modules introduced with version 1.11. + +Example +------- + +```go +import "github.com/stoewer/go-strcase" + +var snake = strcase.SnakeCase("CamelCase") +``` + +Dependencies +------------ + +### Build dependencies + +* none + +### Test dependencies + +* `github.com/stretchr/testify` + +Run linters and unit tests +-------------------------- + +To run the static code analysis, linters and tests use the following commands: + +``` +golangci-lint run --config .golangci.yml ./... +go test ./... +``` diff --git a/vendor/github.com/stoewer/go-strcase/camel.go b/vendor/github.com/stoewer/go-strcase/camel.go new file mode 100644 index 0000000000..5c233cc8f1 --- /dev/null +++ b/vendor/github.com/stoewer/go-strcase/camel.go @@ -0,0 +1,37 @@ +// Copyright (c) 2017, A. Stoewer +// All rights reserved. + +package strcase + +import ( + "strings" +) + +// UpperCamelCase converts a string into camel case starting with a upper case letter. +func UpperCamelCase(s string) string { + return camelCase(s, true) +} + +// LowerCamelCase converts a string into camel case starting with a lower case letter. +func LowerCamelCase(s string) string { + return camelCase(s, false) +} + +func camelCase(s string, upper bool) string { + s = strings.TrimSpace(s) + buffer := make([]rune, 0, len(s)) + + stringIter(s, func(prev, curr, next rune) { + if !isDelimiter(curr) { + if isDelimiter(prev) || (upper && prev == 0) { + buffer = append(buffer, toUpper(curr)) + } else if isLower(prev) { + buffer = append(buffer, curr) + } else { + buffer = append(buffer, toLower(curr)) + } + } + }) + + return string(buffer) +} diff --git a/vendor/github.com/stoewer/go-strcase/doc.go b/vendor/github.com/stoewer/go-strcase/doc.go new file mode 100644 index 0000000000..3e441ca3ef --- /dev/null +++ b/vendor/github.com/stoewer/go-strcase/doc.go @@ -0,0 +1,8 @@ +// Copyright (c) 2017, A. Stoewer +// All rights reserved. + +// Package strcase converts between different kinds of naming formats such as camel case +// (CamelCase), snake case (snake_case) or kebab case (kebab-case). The package is designed +// to work only with strings consisting of standard ASCII letters. Unicode is currently not +// supported. +package strcase diff --git a/vendor/github.com/stoewer/go-strcase/go.mod b/vendor/github.com/stoewer/go-strcase/go.mod new file mode 100644 index 0000000000..8a360abe14 --- /dev/null +++ b/vendor/github.com/stoewer/go-strcase/go.mod @@ -0,0 +1,5 @@ +module github.com/stoewer/go-strcase + +go 1.11 + +require github.com/stretchr/testify v1.5.1 diff --git a/vendor/github.com/stoewer/go-strcase/go.sum b/vendor/github.com/stoewer/go-strcase/go.sum new file mode 100644 index 0000000000..331fa69822 --- /dev/null +++ b/vendor/github.com/stoewer/go-strcase/go.sum @@ -0,0 +1,11 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/stoewer/go-strcase/helper.go b/vendor/github.com/stoewer/go-strcase/helper.go new file mode 100644 index 0000000000..ecad589143 --- /dev/null +++ b/vendor/github.com/stoewer/go-strcase/helper.go @@ -0,0 +1,71 @@ +// Copyright (c) 2017, A. Stoewer +// All rights reserved. + +package strcase + +// isLower checks if a character is lower case. More precisely it evaluates if it is +// in the range of ASCII character 'a' to 'z'. +func isLower(ch rune) bool { + return ch >= 'a' && ch <= 'z' +} + +// toLower converts a character in the range of ASCII characters 'A' to 'Z' to its lower +// case counterpart. Other characters remain the same. +func toLower(ch rune) rune { + if ch >= 'A' && ch <= 'Z' { + return ch + 32 + } + return ch +} + +// isLower checks if a character is upper case. More precisely it evaluates if it is +// in the range of ASCII characters 'A' to 'Z'. +func isUpper(ch rune) bool { + return ch >= 'A' && ch <= 'Z' +} + +// toLower converts a character in the range of ASCII characters 'a' to 'z' to its lower +// case counterpart. Other characters remain the same. +func toUpper(ch rune) rune { + if ch >= 'a' && ch <= 'z' { + return ch - 32 + } + return ch +} + +// isSpace checks if a character is some kind of whitespace. +func isSpace(ch rune) bool { + return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' +} + +// isDelimiter checks if a character is some kind of whitespace or '_' or '-'. +func isDelimiter(ch rune) bool { + return ch == '-' || ch == '_' || isSpace(ch) +} + +// iterFunc is a callback that is called fro a specific position in a string. Its arguments are the +// rune at the respective string position as well as the previous and the next rune. If curr is at the +// first position of the string prev is zero. If curr is at the end of the string next is zero. +type iterFunc func(prev, curr, next rune) + +// stringIter iterates over a string, invoking the callback for every single rune in the string. +func stringIter(s string, callback iterFunc) { + var prev rune + var curr rune + for _, next := range s { + if curr == 0 { + prev = curr + curr = next + continue + } + + callback(prev, curr, next) + + prev = curr + curr = next + } + + if len(s) > 0 { + callback(prev, curr, 0) + } +} diff --git a/vendor/github.com/stoewer/go-strcase/kebab.go b/vendor/github.com/stoewer/go-strcase/kebab.go new file mode 100644 index 0000000000..e9a6487579 --- /dev/null +++ b/vendor/github.com/stoewer/go-strcase/kebab.go @@ -0,0 +1,14 @@ +// Copyright (c) 2017, A. Stoewer +// All rights reserved. + +package strcase + +// KebabCase converts a string into kebab case. +func KebabCase(s string) string { + return delimiterCase(s, '-', false) +} + +// UpperKebabCase converts a string into kebab case with capital letters. +func UpperKebabCase(s string) string { + return delimiterCase(s, '-', true) +} diff --git a/vendor/github.com/stoewer/go-strcase/snake.go b/vendor/github.com/stoewer/go-strcase/snake.go new file mode 100644 index 0000000000..1b216e20cf --- /dev/null +++ b/vendor/github.com/stoewer/go-strcase/snake.go @@ -0,0 +1,58 @@ +// Copyright (c) 2017, A. Stoewer +// All rights reserved. + +package strcase + +import ( + "strings" +) + +// SnakeCase converts a string into snake case. +func SnakeCase(s string) string { + return delimiterCase(s, '_', false) +} + +// UpperSnakeCase converts a string into snake case with capital letters. +func UpperSnakeCase(s string) string { + return delimiterCase(s, '_', true) +} + +// delimiterCase converts a string into snake_case or kebab-case depending on the delimiter passed +// as second argument. When upperCase is true the result will be UPPER_SNAKE_CASE or UPPER-KEBAB-CASE. +func delimiterCase(s string, delimiter rune, upperCase bool) string { + s = strings.TrimSpace(s) + buffer := make([]rune, 0, len(s)+3) + + adjustCase := toLower + if upperCase { + adjustCase = toUpper + } + + var prev rune + var curr rune + for _, next := range s { + if isDelimiter(curr) { + if !isDelimiter(prev) { + buffer = append(buffer, delimiter) + } + } else if isUpper(curr) { + if isLower(prev) || (isUpper(prev) && isLower(next)) { + buffer = append(buffer, delimiter) + } + buffer = append(buffer, adjustCase(curr)) + } else if curr != 0 { + buffer = append(buffer, adjustCase(curr)) + } + prev = curr + curr = next + } + + if len(s) > 0 { + if isUpper(curr) && isLower(prev) && prev != 0 { + buffer = append(buffer, delimiter) + } + buffer = append(buffer, adjustCase(curr)) + } + + return string(buffer) +} diff --git a/vendor/github.com/syndtr/gocapability/LICENSE b/vendor/github.com/syndtr/gocapability/LICENSE new file mode 100644 index 0000000000..80dd96de77 --- /dev/null +++ b/vendor/github.com/syndtr/gocapability/LICENSE @@ -0,0 +1,24 @@ +Copyright 2013 Suryandaru Triandana +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/vendor/github.com/syndtr/gocapability/capability/capability.go b/vendor/github.com/syndtr/gocapability/capability/capability.go new file mode 100644 index 0000000000..61a90775e5 --- /dev/null +++ b/vendor/github.com/syndtr/gocapability/capability/capability.go @@ -0,0 +1,133 @@ +// Copyright (c) 2013, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Package capability provides utilities for manipulating POSIX capabilities. +package capability + +type Capabilities interface { + // Get check whether a capability present in the given + // capabilities set. The 'which' value should be one of EFFECTIVE, + // PERMITTED, INHERITABLE, BOUNDING or AMBIENT. + Get(which CapType, what Cap) bool + + // Empty check whether all capability bits of the given capabilities + // set are zero. The 'which' value should be one of EFFECTIVE, + // PERMITTED, INHERITABLE, BOUNDING or AMBIENT. + Empty(which CapType) bool + + // Full check whether all capability bits of the given capabilities + // set are one. The 'which' value should be one of EFFECTIVE, + // PERMITTED, INHERITABLE, BOUNDING or AMBIENT. + Full(which CapType) bool + + // Set sets capabilities of the given capabilities sets. The + // 'which' value should be one or combination (OR'ed) of EFFECTIVE, + // PERMITTED, INHERITABLE, BOUNDING or AMBIENT. + Set(which CapType, caps ...Cap) + + // Unset unsets capabilities of the given capabilities sets. The + // 'which' value should be one or combination (OR'ed) of EFFECTIVE, + // PERMITTED, INHERITABLE, BOUNDING or AMBIENT. + Unset(which CapType, caps ...Cap) + + // Fill sets all bits of the given capabilities kind to one. The + // 'kind' value should be one or combination (OR'ed) of CAPS, + // BOUNDS or AMBS. + Fill(kind CapType) + + // Clear sets all bits of the given capabilities kind to zero. The + // 'kind' value should be one or combination (OR'ed) of CAPS, + // BOUNDS or AMBS. + Clear(kind CapType) + + // String return current capabilities state of the given capabilities + // set as string. The 'which' value should be one of EFFECTIVE, + // PERMITTED, INHERITABLE BOUNDING or AMBIENT + StringCap(which CapType) string + + // String return current capabilities state as string. + String() string + + // Load load actual capabilities value. This will overwrite all + // outstanding changes. + Load() error + + // Apply apply the capabilities settings, so all changes will take + // effect. + Apply(kind CapType) error +} + +// NewPid initializes a new Capabilities object for given pid when +// it is nonzero, or for the current process if pid is 0. +// +// Deprecated: Replace with NewPid2. For example, replace: +// +// c, err := NewPid(0) +// if err != nil { +// return err +// } +// +// with: +// +// c, err := NewPid2(0) +// if err != nil { +// return err +// } +// err = c.Load() +// if err != nil { +// return err +// } +func NewPid(pid int) (Capabilities, error) { + c, err := newPid(pid) + if err != nil { + return c, err + } + err = c.Load() + return c, err +} + +// NewPid2 initializes a new Capabilities object for given pid when +// it is nonzero, or for the current process if pid is 0. This +// does not load the process's current capabilities; to do that you +// must call Load explicitly. +func NewPid2(pid int) (Capabilities, error) { + return newPid(pid) +} + +// NewFile initializes a new Capabilities object for given file path. +// +// Deprecated: Replace with NewFile2. For example, replace: +// +// c, err := NewFile(path) +// if err != nil { +// return err +// } +// +// with: +// +// c, err := NewFile2(path) +// if err != nil { +// return err +// } +// err = c.Load() +// if err != nil { +// return err +// } +func NewFile(path string) (Capabilities, error) { + c, err := newFile(path) + if err != nil { + return c, err + } + err = c.Load() + return c, err +} + +// NewFile2 creates a new initialized Capabilities object for given +// file path. This does not load the process's current capabilities; +// to do that you must call Load explicitly. +func NewFile2(path string) (Capabilities, error) { + return newFile(path) +} diff --git a/vendor/github.com/syndtr/gocapability/capability/capability_linux.go b/vendor/github.com/syndtr/gocapability/capability/capability_linux.go new file mode 100644 index 0000000000..1567dc8104 --- /dev/null +++ b/vendor/github.com/syndtr/gocapability/capability/capability_linux.go @@ -0,0 +1,642 @@ +// Copyright (c) 2013, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package capability + +import ( + "bufio" + "errors" + "fmt" + "io" + "os" + "strings" + "syscall" +) + +var errUnknownVers = errors.New("unknown capability version") + +const ( + linuxCapVer1 = 0x19980330 + linuxCapVer2 = 0x20071026 + linuxCapVer3 = 0x20080522 +) + +var ( + capVers uint32 + capLastCap Cap +) + +func init() { + var hdr capHeader + capget(&hdr, nil) + capVers = hdr.version + + if initLastCap() == nil { + CAP_LAST_CAP = capLastCap + if capLastCap > 31 { + capUpperMask = (uint32(1) << (uint(capLastCap) - 31)) - 1 + } else { + capUpperMask = 0 + } + } +} + +func initLastCap() error { + if capLastCap != 0 { + return nil + } + + f, err := os.Open("/proc/sys/kernel/cap_last_cap") + if err != nil { + return err + } + defer f.Close() + + var b []byte = make([]byte, 11) + _, err = f.Read(b) + if err != nil { + return err + } + + fmt.Sscanf(string(b), "%d", &capLastCap) + + return nil +} + +func mkStringCap(c Capabilities, which CapType) (ret string) { + for i, first := Cap(0), true; i <= CAP_LAST_CAP; i++ { + if !c.Get(which, i) { + continue + } + if first { + first = false + } else { + ret += ", " + } + ret += i.String() + } + return +} + +func mkString(c Capabilities, max CapType) (ret string) { + ret = "{" + for i := CapType(1); i <= max; i <<= 1 { + ret += " " + i.String() + "=\"" + if c.Empty(i) { + ret += "empty" + } else if c.Full(i) { + ret += "full" + } else { + ret += c.StringCap(i) + } + ret += "\"" + } + ret += " }" + return +} + +func newPid(pid int) (c Capabilities, err error) { + switch capVers { + case linuxCapVer1: + p := new(capsV1) + p.hdr.version = capVers + p.hdr.pid = int32(pid) + c = p + case linuxCapVer2, linuxCapVer3: + p := new(capsV3) + p.hdr.version = capVers + p.hdr.pid = int32(pid) + c = p + default: + err = errUnknownVers + return + } + return +} + +type capsV1 struct { + hdr capHeader + data capData +} + +func (c *capsV1) Get(which CapType, what Cap) bool { + if what > 32 { + return false + } + + switch which { + case EFFECTIVE: + return (1< 32 { + continue + } + + if which&EFFECTIVE != 0 { + c.data.effective |= 1 << uint(what) + } + if which&PERMITTED != 0 { + c.data.permitted |= 1 << uint(what) + } + if which&INHERITABLE != 0 { + c.data.inheritable |= 1 << uint(what) + } + } +} + +func (c *capsV1) Unset(which CapType, caps ...Cap) { + for _, what := range caps { + if what > 32 { + continue + } + + if which&EFFECTIVE != 0 { + c.data.effective &= ^(1 << uint(what)) + } + if which&PERMITTED != 0 { + c.data.permitted &= ^(1 << uint(what)) + } + if which&INHERITABLE != 0 { + c.data.inheritable &= ^(1 << uint(what)) + } + } +} + +func (c *capsV1) Fill(kind CapType) { + if kind&CAPS == CAPS { + c.data.effective = 0x7fffffff + c.data.permitted = 0x7fffffff + c.data.inheritable = 0 + } +} + +func (c *capsV1) Clear(kind CapType) { + if kind&CAPS == CAPS { + c.data.effective = 0 + c.data.permitted = 0 + c.data.inheritable = 0 + } +} + +func (c *capsV1) StringCap(which CapType) (ret string) { + return mkStringCap(c, which) +} + +func (c *capsV1) String() (ret string) { + return mkString(c, BOUNDING) +} + +func (c *capsV1) Load() (err error) { + return capget(&c.hdr, &c.data) +} + +func (c *capsV1) Apply(kind CapType) error { + if kind&CAPS == CAPS { + return capset(&c.hdr, &c.data) + } + return nil +} + +type capsV3 struct { + hdr capHeader + data [2]capData + bounds [2]uint32 + ambient [2]uint32 +} + +func (c *capsV3) Get(which CapType, what Cap) bool { + var i uint + if what > 31 { + i = uint(what) >> 5 + what %= 32 + } + + switch which { + case EFFECTIVE: + return (1< 31 { + i = uint(what) >> 5 + what %= 32 + } + + if which&EFFECTIVE != 0 { + c.data[i].effective |= 1 << uint(what) + } + if which&PERMITTED != 0 { + c.data[i].permitted |= 1 << uint(what) + } + if which&INHERITABLE != 0 { + c.data[i].inheritable |= 1 << uint(what) + } + if which&BOUNDING != 0 { + c.bounds[i] |= 1 << uint(what) + } + if which&AMBIENT != 0 { + c.ambient[i] |= 1 << uint(what) + } + } +} + +func (c *capsV3) Unset(which CapType, caps ...Cap) { + for _, what := range caps { + var i uint + if what > 31 { + i = uint(what) >> 5 + what %= 32 + } + + if which&EFFECTIVE != 0 { + c.data[i].effective &= ^(1 << uint(what)) + } + if which&PERMITTED != 0 { + c.data[i].permitted &= ^(1 << uint(what)) + } + if which&INHERITABLE != 0 { + c.data[i].inheritable &= ^(1 << uint(what)) + } + if which&BOUNDING != 0 { + c.bounds[i] &= ^(1 << uint(what)) + } + if which&AMBIENT != 0 { + c.ambient[i] &= ^(1 << uint(what)) + } + } +} + +func (c *capsV3) Fill(kind CapType) { + if kind&CAPS == CAPS { + c.data[0].effective = 0xffffffff + c.data[0].permitted = 0xffffffff + c.data[0].inheritable = 0 + c.data[1].effective = 0xffffffff + c.data[1].permitted = 0xffffffff + c.data[1].inheritable = 0 + } + + if kind&BOUNDS == BOUNDS { + c.bounds[0] = 0xffffffff + c.bounds[1] = 0xffffffff + } + if kind&AMBS == AMBS { + c.ambient[0] = 0xffffffff + c.ambient[1] = 0xffffffff + } +} + +func (c *capsV3) Clear(kind CapType) { + if kind&CAPS == CAPS { + c.data[0].effective = 0 + c.data[0].permitted = 0 + c.data[0].inheritable = 0 + c.data[1].effective = 0 + c.data[1].permitted = 0 + c.data[1].inheritable = 0 + } + + if kind&BOUNDS == BOUNDS { + c.bounds[0] = 0 + c.bounds[1] = 0 + } + if kind&AMBS == AMBS { + c.ambient[0] = 0 + c.ambient[1] = 0 + } +} + +func (c *capsV3) StringCap(which CapType) (ret string) { + return mkStringCap(c, which) +} + +func (c *capsV3) String() (ret string) { + return mkString(c, BOUNDING) +} + +func (c *capsV3) Load() (err error) { + err = capget(&c.hdr, &c.data[0]) + if err != nil { + return + } + + var status_path string + + if c.hdr.pid == 0 { + status_path = fmt.Sprintf("/proc/self/status") + } else { + status_path = fmt.Sprintf("/proc/%d/status", c.hdr.pid) + } + + f, err := os.Open(status_path) + if err != nil { + return + } + b := bufio.NewReader(f) + for { + line, e := b.ReadString('\n') + if e != nil { + if e != io.EOF { + err = e + } + break + } + if strings.HasPrefix(line, "CapB") { + fmt.Sscanf(line[4:], "nd: %08x%08x", &c.bounds[1], &c.bounds[0]) + continue + } + if strings.HasPrefix(line, "CapA") { + fmt.Sscanf(line[4:], "mb: %08x%08x", &c.ambient[1], &c.ambient[0]) + continue + } + } + f.Close() + + return +} + +func (c *capsV3) Apply(kind CapType) (err error) { + if kind&BOUNDS == BOUNDS { + var data [2]capData + err = capget(&c.hdr, &data[0]) + if err != nil { + return + } + if (1< 31 { + if c.data.version == 1 { + return false + } + i = uint(what) >> 5 + what %= 32 + } + + switch which { + case EFFECTIVE: + return (1< 31 { + if c.data.version == 1 { + continue + } + i = uint(what) >> 5 + what %= 32 + } + + if which&EFFECTIVE != 0 { + c.data.effective[i] |= 1 << uint(what) + } + if which&PERMITTED != 0 { + c.data.data[i].permitted |= 1 << uint(what) + } + if which&INHERITABLE != 0 { + c.data.data[i].inheritable |= 1 << uint(what) + } + } +} + +func (c *capsFile) Unset(which CapType, caps ...Cap) { + for _, what := range caps { + var i uint + if what > 31 { + if c.data.version == 1 { + continue + } + i = uint(what) >> 5 + what %= 32 + } + + if which&EFFECTIVE != 0 { + c.data.effective[i] &= ^(1 << uint(what)) + } + if which&PERMITTED != 0 { + c.data.data[i].permitted &= ^(1 << uint(what)) + } + if which&INHERITABLE != 0 { + c.data.data[i].inheritable &= ^(1 << uint(what)) + } + } +} + +func (c *capsFile) Fill(kind CapType) { + if kind&CAPS == CAPS { + c.data.effective[0] = 0xffffffff + c.data.data[0].permitted = 0xffffffff + c.data.data[0].inheritable = 0 + if c.data.version == 2 { + c.data.effective[1] = 0xffffffff + c.data.data[1].permitted = 0xffffffff + c.data.data[1].inheritable = 0 + } + } +} + +func (c *capsFile) Clear(kind CapType) { + if kind&CAPS == CAPS { + c.data.effective[0] = 0 + c.data.data[0].permitted = 0 + c.data.data[0].inheritable = 0 + if c.data.version == 2 { + c.data.effective[1] = 0 + c.data.data[1].permitted = 0 + c.data.data[1].inheritable = 0 + } + } +} + +func (c *capsFile) StringCap(which CapType) (ret string) { + return mkStringCap(c, which) +} + +func (c *capsFile) String() (ret string) { + return mkString(c, INHERITABLE) +} + +func (c *capsFile) Load() (err error) { + return getVfsCap(c.path, &c.data) +} + +func (c *capsFile) Apply(kind CapType) (err error) { + if kind&CAPS == CAPS { + return setVfsCap(c.path, &c.data) + } + return +} diff --git a/vendor/github.com/syndtr/gocapability/capability/capability_noop.go b/vendor/github.com/syndtr/gocapability/capability/capability_noop.go new file mode 100644 index 0000000000..9bb3070c5e --- /dev/null +++ b/vendor/github.com/syndtr/gocapability/capability/capability_noop.go @@ -0,0 +1,19 @@ +// Copyright (c) 2013, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// +build !linux + +package capability + +import "errors" + +func newPid(pid int) (Capabilities, error) { + return nil, errors.New("not supported") +} + +func newFile(path string) (Capabilities, error) { + return nil, errors.New("not supported") +} diff --git a/vendor/github.com/syndtr/gocapability/capability/enum.go b/vendor/github.com/syndtr/gocapability/capability/enum.go new file mode 100644 index 0000000000..ad10785314 --- /dev/null +++ b/vendor/github.com/syndtr/gocapability/capability/enum.go @@ -0,0 +1,309 @@ +// Copyright (c) 2013, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package capability + +type CapType uint + +func (c CapType) String() string { + switch c { + case EFFECTIVE: + return "effective" + case PERMITTED: + return "permitted" + case INHERITABLE: + return "inheritable" + case BOUNDING: + return "bounding" + case CAPS: + return "caps" + case AMBIENT: + return "ambient" + } + return "unknown" +} + +const ( + EFFECTIVE CapType = 1 << iota + PERMITTED + INHERITABLE + BOUNDING + AMBIENT + + CAPS = EFFECTIVE | PERMITTED | INHERITABLE + BOUNDS = BOUNDING + AMBS = AMBIENT +) + +//go:generate go run enumgen/gen.go +type Cap int + +// POSIX-draft defined capabilities and Linux extensions. +// +// Defined in https://github.com/torvalds/linux/blob/master/include/uapi/linux/capability.h +const ( + // In a system with the [_POSIX_CHOWN_RESTRICTED] option defined, this + // overrides the restriction of changing file ownership and group + // ownership. + CAP_CHOWN = Cap(0) + + // Override all DAC access, including ACL execute access if + // [_POSIX_ACL] is defined. Excluding DAC access covered by + // CAP_LINUX_IMMUTABLE. + CAP_DAC_OVERRIDE = Cap(1) + + // Overrides all DAC restrictions regarding read and search on files + // and directories, including ACL restrictions if [_POSIX_ACL] is + // defined. Excluding DAC access covered by CAP_LINUX_IMMUTABLE. + CAP_DAC_READ_SEARCH = Cap(2) + + // Overrides all restrictions about allowed operations on files, where + // file owner ID must be equal to the user ID, except where CAP_FSETID + // is applicable. It doesn't override MAC and DAC restrictions. + CAP_FOWNER = Cap(3) + + // Overrides the following restrictions that the effective user ID + // shall match the file owner ID when setting the S_ISUID and S_ISGID + // bits on that file; that the effective group ID (or one of the + // supplementary group IDs) shall match the file owner ID when setting + // the S_ISGID bit on that file; that the S_ISUID and S_ISGID bits are + // cleared on successful return from chown(2) (not implemented). + CAP_FSETID = Cap(4) + + // Overrides the restriction that the real or effective user ID of a + // process sending a signal must match the real or effective user ID + // of the process receiving the signal. + CAP_KILL = Cap(5) + + // Allows setgid(2) manipulation + // Allows setgroups(2) + // Allows forged gids on socket credentials passing. + CAP_SETGID = Cap(6) + + // Allows set*uid(2) manipulation (including fsuid). + // Allows forged pids on socket credentials passing. + CAP_SETUID = Cap(7) + + // Linux-specific capabilities + + // Without VFS support for capabilities: + // Transfer any capability in your permitted set to any pid, + // remove any capability in your permitted set from any pid + // With VFS support for capabilities (neither of above, but) + // Add any capability from current's capability bounding set + // to the current process' inheritable set + // Allow taking bits out of capability bounding set + // Allow modification of the securebits for a process + CAP_SETPCAP = Cap(8) + + // Allow modification of S_IMMUTABLE and S_APPEND file attributes + CAP_LINUX_IMMUTABLE = Cap(9) + + // Allows binding to TCP/UDP sockets below 1024 + // Allows binding to ATM VCIs below 32 + CAP_NET_BIND_SERVICE = Cap(10) + + // Allow broadcasting, listen to multicast + CAP_NET_BROADCAST = Cap(11) + + // Allow interface configuration + // Allow administration of IP firewall, masquerading and accounting + // Allow setting debug option on sockets + // Allow modification of routing tables + // Allow setting arbitrary process / process group ownership on + // sockets + // Allow binding to any address for transparent proxying (also via NET_RAW) + // Allow setting TOS (type of service) + // Allow setting promiscuous mode + // Allow clearing driver statistics + // Allow multicasting + // Allow read/write of device-specific registers + // Allow activation of ATM control sockets + CAP_NET_ADMIN = Cap(12) + + // Allow use of RAW sockets + // Allow use of PACKET sockets + // Allow binding to any address for transparent proxying (also via NET_ADMIN) + CAP_NET_RAW = Cap(13) + + // Allow locking of shared memory segments + // Allow mlock and mlockall (which doesn't really have anything to do + // with IPC) + CAP_IPC_LOCK = Cap(14) + + // Override IPC ownership checks + CAP_IPC_OWNER = Cap(15) + + // Insert and remove kernel modules - modify kernel without limit + CAP_SYS_MODULE = Cap(16) + + // Allow ioperm/iopl access + // Allow sending USB messages to any device via /proc/bus/usb + CAP_SYS_RAWIO = Cap(17) + + // Allow use of chroot() + CAP_SYS_CHROOT = Cap(18) + + // Allow ptrace() of any process + CAP_SYS_PTRACE = Cap(19) + + // Allow configuration of process accounting + CAP_SYS_PACCT = Cap(20) + + // Allow configuration of the secure attention key + // Allow administration of the random device + // Allow examination and configuration of disk quotas + // Allow setting the domainname + // Allow setting the hostname + // Allow calling bdflush() + // Allow mount() and umount(), setting up new smb connection + // Allow some autofs root ioctls + // Allow nfsservctl + // Allow VM86_REQUEST_IRQ + // Allow to read/write pci config on alpha + // Allow irix_prctl on mips (setstacksize) + // Allow flushing all cache on m68k (sys_cacheflush) + // Allow removing semaphores + // Used instead of CAP_CHOWN to "chown" IPC message queues, semaphores + // and shared memory + // Allow locking/unlocking of shared memory segment + // Allow turning swap on/off + // Allow forged pids on socket credentials passing + // Allow setting readahead and flushing buffers on block devices + // Allow setting geometry in floppy driver + // Allow turning DMA on/off in xd driver + // Allow administration of md devices (mostly the above, but some + // extra ioctls) + // Allow tuning the ide driver + // Allow access to the nvram device + // Allow administration of apm_bios, serial and bttv (TV) device + // Allow manufacturer commands in isdn CAPI support driver + // Allow reading non-standardized portions of pci configuration space + // Allow DDI debug ioctl on sbpcd driver + // Allow setting up serial ports + // Allow sending raw qic-117 commands + // Allow enabling/disabling tagged queuing on SCSI controllers and sending + // arbitrary SCSI commands + // Allow setting encryption key on loopback filesystem + // Allow setting zone reclaim policy + // Allow everything under CAP_BPF and CAP_PERFMON for backward compatibility + CAP_SYS_ADMIN = Cap(21) + + // Allow use of reboot() + CAP_SYS_BOOT = Cap(22) + + // Allow raising priority and setting priority on other (different + // UID) processes + // Allow use of FIFO and round-robin (realtime) scheduling on own + // processes and setting the scheduling algorithm used by another + // process. + // Allow setting cpu affinity on other processes + CAP_SYS_NICE = Cap(23) + + // Override resource limits. Set resource limits. + // Override quota limits. + // Override reserved space on ext2 filesystem + // Modify data journaling mode on ext3 filesystem (uses journaling + // resources) + // NOTE: ext2 honors fsuid when checking for resource overrides, so + // you can override using fsuid too + // Override size restrictions on IPC message queues + // Allow more than 64hz interrupts from the real-time clock + // Override max number of consoles on console allocation + // Override max number of keymaps + // Control memory reclaim behavior + CAP_SYS_RESOURCE = Cap(24) + + // Allow manipulation of system clock + // Allow irix_stime on mips + // Allow setting the real-time clock + CAP_SYS_TIME = Cap(25) + + // Allow configuration of tty devices + // Allow vhangup() of tty + CAP_SYS_TTY_CONFIG = Cap(26) + + // Allow the privileged aspects of mknod() + CAP_MKNOD = Cap(27) + + // Allow taking of leases on files + CAP_LEASE = Cap(28) + + CAP_AUDIT_WRITE = Cap(29) + CAP_AUDIT_CONTROL = Cap(30) + CAP_SETFCAP = Cap(31) + + // Override MAC access. + // The base kernel enforces no MAC policy. + // An LSM may enforce a MAC policy, and if it does and it chooses + // to implement capability based overrides of that policy, this is + // the capability it should use to do so. + CAP_MAC_OVERRIDE = Cap(32) + + // Allow MAC configuration or state changes. + // The base kernel requires no MAC configuration. + // An LSM may enforce a MAC policy, and if it does and it chooses + // to implement capability based checks on modifications to that + // policy or the data required to maintain it, this is the + // capability it should use to do so. + CAP_MAC_ADMIN = Cap(33) + + // Allow configuring the kernel's syslog (printk behaviour) + CAP_SYSLOG = Cap(34) + + // Allow triggering something that will wake the system + CAP_WAKE_ALARM = Cap(35) + + // Allow preventing system suspends + CAP_BLOCK_SUSPEND = Cap(36) + + // Allow reading the audit log via multicast netlink socket + CAP_AUDIT_READ = Cap(37) + + // Allow system performance and observability privileged operations + // using perf_events, i915_perf and other kernel subsystems + CAP_PERFMON = Cap(38) + + // CAP_BPF allows the following BPF operations: + // - Creating all types of BPF maps + // - Advanced verifier features + // - Indirect variable access + // - Bounded loops + // - BPF to BPF function calls + // - Scalar precision tracking + // - Larger complexity limits + // - Dead code elimination + // - And potentially other features + // - Loading BPF Type Format (BTF) data + // - Retrieve xlated and JITed code of BPF programs + // - Use bpf_spin_lock() helper + // + // CAP_PERFMON relaxes the verifier checks further: + // - BPF progs can use of pointer-to-integer conversions + // - speculation attack hardening measures are bypassed + // - bpf_probe_read to read arbitrary kernel memory is allowed + // - bpf_trace_printk to print kernel memory is allowed + // + // CAP_SYS_ADMIN is required to use bpf_probe_write_user. + // + // CAP_SYS_ADMIN is required to iterate system wide loaded + // programs, maps, links, BTFs and convert their IDs to file descriptors. + // + // CAP_PERFMON and CAP_BPF are required to load tracing programs. + // CAP_NET_ADMIN and CAP_BPF are required to load networking programs. + CAP_BPF = Cap(39) + + // Allow checkpoint/restore related operations. + // Introduced in kernel 5.9 + CAP_CHECKPOINT_RESTORE = Cap(40) +) + +var ( + // Highest valid capability of the running kernel. + CAP_LAST_CAP = Cap(63) + + capUpperMask = ^uint32(0) +) diff --git a/vendor/github.com/syndtr/gocapability/capability/enum_gen.go b/vendor/github.com/syndtr/gocapability/capability/enum_gen.go new file mode 100644 index 0000000000..2ff9bf4d88 --- /dev/null +++ b/vendor/github.com/syndtr/gocapability/capability/enum_gen.go @@ -0,0 +1,138 @@ +// generated file; DO NOT EDIT - use go generate in directory with source + +package capability + +func (c Cap) String() string { + switch c { + case CAP_CHOWN: + return "chown" + case CAP_DAC_OVERRIDE: + return "dac_override" + case CAP_DAC_READ_SEARCH: + return "dac_read_search" + case CAP_FOWNER: + return "fowner" + case CAP_FSETID: + return "fsetid" + case CAP_KILL: + return "kill" + case CAP_SETGID: + return "setgid" + case CAP_SETUID: + return "setuid" + case CAP_SETPCAP: + return "setpcap" + case CAP_LINUX_IMMUTABLE: + return "linux_immutable" + case CAP_NET_BIND_SERVICE: + return "net_bind_service" + case CAP_NET_BROADCAST: + return "net_broadcast" + case CAP_NET_ADMIN: + return "net_admin" + case CAP_NET_RAW: + return "net_raw" + case CAP_IPC_LOCK: + return "ipc_lock" + case CAP_IPC_OWNER: + return "ipc_owner" + case CAP_SYS_MODULE: + return "sys_module" + case CAP_SYS_RAWIO: + return "sys_rawio" + case CAP_SYS_CHROOT: + return "sys_chroot" + case CAP_SYS_PTRACE: + return "sys_ptrace" + case CAP_SYS_PACCT: + return "sys_pacct" + case CAP_SYS_ADMIN: + return "sys_admin" + case CAP_SYS_BOOT: + return "sys_boot" + case CAP_SYS_NICE: + return "sys_nice" + case CAP_SYS_RESOURCE: + return "sys_resource" + case CAP_SYS_TIME: + return "sys_time" + case CAP_SYS_TTY_CONFIG: + return "sys_tty_config" + case CAP_MKNOD: + return "mknod" + case CAP_LEASE: + return "lease" + case CAP_AUDIT_WRITE: + return "audit_write" + case CAP_AUDIT_CONTROL: + return "audit_control" + case CAP_SETFCAP: + return "setfcap" + case CAP_MAC_OVERRIDE: + return "mac_override" + case CAP_MAC_ADMIN: + return "mac_admin" + case CAP_SYSLOG: + return "syslog" + case CAP_WAKE_ALARM: + return "wake_alarm" + case CAP_BLOCK_SUSPEND: + return "block_suspend" + case CAP_AUDIT_READ: + return "audit_read" + case CAP_PERFMON: + return "perfmon" + case CAP_BPF: + return "bpf" + case CAP_CHECKPOINT_RESTORE: + return "checkpoint_restore" + } + return "unknown" +} + +// List returns list of all supported capabilities +func List() []Cap { + return []Cap{ + CAP_CHOWN, + CAP_DAC_OVERRIDE, + CAP_DAC_READ_SEARCH, + CAP_FOWNER, + CAP_FSETID, + CAP_KILL, + CAP_SETGID, + CAP_SETUID, + CAP_SETPCAP, + CAP_LINUX_IMMUTABLE, + CAP_NET_BIND_SERVICE, + CAP_NET_BROADCAST, + CAP_NET_ADMIN, + CAP_NET_RAW, + CAP_IPC_LOCK, + CAP_IPC_OWNER, + CAP_SYS_MODULE, + CAP_SYS_RAWIO, + CAP_SYS_CHROOT, + CAP_SYS_PTRACE, + CAP_SYS_PACCT, + CAP_SYS_ADMIN, + CAP_SYS_BOOT, + CAP_SYS_NICE, + CAP_SYS_RESOURCE, + CAP_SYS_TIME, + CAP_SYS_TTY_CONFIG, + CAP_MKNOD, + CAP_LEASE, + CAP_AUDIT_WRITE, + CAP_AUDIT_CONTROL, + CAP_SETFCAP, + CAP_MAC_OVERRIDE, + CAP_MAC_ADMIN, + CAP_SYSLOG, + CAP_WAKE_ALARM, + CAP_BLOCK_SUSPEND, + CAP_AUDIT_READ, + CAP_PERFMON, + CAP_BPF, + CAP_CHECKPOINT_RESTORE, + } +} diff --git a/vendor/github.com/syndtr/gocapability/capability/syscall_linux.go b/vendor/github.com/syndtr/gocapability/capability/syscall_linux.go new file mode 100644 index 0000000000..3d2bf6927f --- /dev/null +++ b/vendor/github.com/syndtr/gocapability/capability/syscall_linux.go @@ -0,0 +1,154 @@ +// Copyright (c) 2013, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package capability + +import ( + "syscall" + "unsafe" +) + +type capHeader struct { + version uint32 + pid int32 +} + +type capData struct { + effective uint32 + permitted uint32 + inheritable uint32 +} + +func capget(hdr *capHeader, data *capData) (err error) { + _, _, e1 := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0) + if e1 != 0 { + err = e1 + } + return +} + +func capset(hdr *capHeader, data *capData) (err error) { + _, _, e1 := syscall.Syscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// not yet in syscall +const ( + pr_CAP_AMBIENT = 47 + pr_CAP_AMBIENT_IS_SET = uintptr(1) + pr_CAP_AMBIENT_RAISE = uintptr(2) + pr_CAP_AMBIENT_LOWER = uintptr(3) + pr_CAP_AMBIENT_CLEAR_ALL = uintptr(4) +) + +func prctl(option int, arg2, arg3, arg4, arg5 uintptr) (err error) { + _, _, e1 := syscall.Syscall6(syscall.SYS_PRCTL, uintptr(option), arg2, arg3, arg4, arg5, 0) + if e1 != 0 { + err = e1 + } + return +} + +const ( + vfsXattrName = "security.capability" + + vfsCapVerMask = 0xff000000 + vfsCapVer1 = 0x01000000 + vfsCapVer2 = 0x02000000 + + vfsCapFlagMask = ^vfsCapVerMask + vfsCapFlageffective = 0x000001 + + vfscapDataSizeV1 = 4 * (1 + 2*1) + vfscapDataSizeV2 = 4 * (1 + 2*2) +) + +type vfscapData struct { + magic uint32 + data [2]struct { + permitted uint32 + inheritable uint32 + } + effective [2]uint32 + version int8 +} + +var ( + _vfsXattrName *byte +) + +func init() { + _vfsXattrName, _ = syscall.BytePtrFromString(vfsXattrName) +} + +func getVfsCap(path string, dest *vfscapData) (err error) { + var _p0 *byte + _p0, err = syscall.BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(dest)), vfscapDataSizeV2, 0, 0) + if e1 != 0 { + if e1 == syscall.ENODATA { + dest.version = 2 + return + } + err = e1 + } + switch dest.magic & vfsCapVerMask { + case vfsCapVer1: + dest.version = 1 + if r0 != vfscapDataSizeV1 { + return syscall.EINVAL + } + dest.data[1].permitted = 0 + dest.data[1].inheritable = 0 + case vfsCapVer2: + dest.version = 2 + if r0 != vfscapDataSizeV2 { + return syscall.EINVAL + } + default: + return syscall.EINVAL + } + if dest.magic&vfsCapFlageffective != 0 { + dest.effective[0] = dest.data[0].permitted | dest.data[0].inheritable + dest.effective[1] = dest.data[1].permitted | dest.data[1].inheritable + } else { + dest.effective[0] = 0 + dest.effective[1] = 0 + } + return +} + +func setVfsCap(path string, data *vfscapData) (err error) { + var _p0 *byte + _p0, err = syscall.BytePtrFromString(path) + if err != nil { + return + } + var size uintptr + if data.version == 1 { + data.magic = vfsCapVer1 + size = vfscapDataSizeV1 + } else if data.version == 2 { + data.magic = vfsCapVer2 + if data.effective[0] != 0 || data.effective[1] != 0 { + data.magic |= vfsCapFlageffective + } + size = vfscapDataSizeV2 + } else { + return syscall.EINVAL + } + _, _, e1 := syscall.Syscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(data)), size, 0, 0) + if e1 != 0 { + err = e1 + } + return +} diff --git a/vendor/github.com/tektoncd/cli/LICENSE b/vendor/github.com/tektoncd/cli/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/tektoncd/cli/pkg/actions/create.go b/vendor/github.com/tektoncd/cli/pkg/actions/create.go new file mode 100644 index 0000000000..2a5c464776 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/actions/create.go @@ -0,0 +1,36 @@ +// Copyright © 2020 The Tekton Authors. +// +// 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. + +package actions + +import ( + "context" + + "github.com/tektoncd/cli/pkg/cli" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func Create(gr schema.GroupVersionResource, clients *cli.Clients, object *unstructured.Unstructured, ns string, op metav1.CreateOptions) (*unstructured.Unstructured, error) { + gvr, err := GetGroupVersionResource(gr, clients.Tekton.Discovery()) + if err != nil { + return nil, err + } + obj, err := clients.Dynamic.Resource(*gvr).Namespace(ns).Create(context.Background(), object, op) + if err != nil { + return nil, err + } + return obj, nil +} diff --git a/vendor/github.com/tektoncd/cli/pkg/actions/delete.go b/vendor/github.com/tektoncd/cli/pkg/actions/delete.go new file mode 100644 index 0000000000..8ffd87a96c --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/actions/delete.go @@ -0,0 +1,38 @@ +// Copyright © 2020 The Tekton Authors. +// +// 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. + +package actions + +import ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" + "k8s.io/client-go/dynamic" +) + +func Delete(gr schema.GroupVersionResource, dynamic dynamic.Interface, discovery discovery.DiscoveryInterface, objname, ns string, op metav1.DeleteOptions) error { + gvr, err := GetGroupVersionResource(gr, discovery) + if err != nil { + return err + } + + err = dynamic.Resource(*gvr).Namespace(ns).Delete(context.Background(), objname, op) + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/tektoncd/cli/pkg/actions/get.go b/vendor/github.com/tektoncd/cli/pkg/actions/get.go new file mode 100644 index 0000000000..5b5ea7ae2c --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/actions/get.go @@ -0,0 +1,51 @@ +// Copyright © 2019-2020 The Tekton Authors. +// +// 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. + +package actions + +import ( + "context" + "io" + + "github.com/tektoncd/cli/pkg/printer" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + cliopts "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/client-go/discovery" + "k8s.io/client-go/dynamic" +) + +func PrintObject(groupResource schema.GroupVersionResource, obj string, w io.Writer, dynamic dynamic.Interface, discovery discovery.DiscoveryInterface, f *cliopts.PrintFlags, ns string) error { + res, err := Get(groupResource, dynamic, discovery, obj, ns, metav1.GetOptions{}) + if err != nil { + return err + } + + return printer.PrintObject(w, res, f) +} + +func Get(gr schema.GroupVersionResource, dynamic dynamic.Interface, discovery discovery.DiscoveryInterface, objname, ns string, op metav1.GetOptions) (*unstructured.Unstructured, error) { + gvr, err := GetGroupVersionResource(gr, discovery) + if err != nil { + return nil, err + } + + obj, err := dynamic.Resource(*gvr).Namespace(ns).Get(context.Background(), objname, op) + if err != nil { + return nil, err + } + + return obj, nil +} diff --git a/vendor/github.com/tektoncd/cli/pkg/actions/gvr.go b/vendor/github.com/tektoncd/cli/pkg/actions/gvr.go new file mode 100644 index 0000000000..5d87058c30 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/actions/gvr.go @@ -0,0 +1,55 @@ +// Copyright © 2020 The Tekton Authors. +// +// 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. + +package actions + +import ( + "sync" + + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" + "k8s.io/client-go/restmapper" +) + +var ( + doOnce sync.Once + apiGroupRes []*restmapper.APIGroupResources +) + +func GetGroupVersionResource(gr schema.GroupVersionResource, discovery discovery.DiscoveryInterface) (*schema.GroupVersionResource, error) { + var err error + doOnce.Do(func() { + err = InitializeAPIGroupRes(discovery) + }) + if err != nil { + return nil, err + } + + rm := restmapper.NewDiscoveryRESTMapper(apiGroupRes) + gvr, err := rm.ResourceFor(gr) + if err != nil { + return nil, err + } + + return &gvr, nil +} + +func InitializeAPIGroupRes(discovery discovery.DiscoveryInterface) error { + var err error + apiGroupRes, err = restmapper.GetAPIGroupResources(discovery) + if err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/tektoncd/cli/pkg/actions/list.go b/vendor/github.com/tektoncd/cli/pkg/actions/list.go new file mode 100644 index 0000000000..e5ff7dd936 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/actions/list.go @@ -0,0 +1,51 @@ +// Copyright © 2020 The Tekton Authors. +// +// 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. + +package actions + +import ( + "context" + "io" + + "github.com/tektoncd/cli/pkg/printer" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + cliopts "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/client-go/discovery" + "k8s.io/client-go/dynamic" +) + +func PrintObjects(groupResource schema.GroupVersionResource, w io.Writer, dynamic dynamic.Interface, discovery discovery.DiscoveryInterface, f *cliopts.PrintFlags, ns string) error { + allres, err := List(groupResource, dynamic, discovery, ns, metav1.ListOptions{}) + if err != nil { + return err + } + + return printer.PrintObject(w, allres, f) +} + +func List(gr schema.GroupVersionResource, dynamic dynamic.Interface, discovery discovery.DiscoveryInterface, ns string, op metav1.ListOptions) (*unstructured.UnstructuredList, error) { + gvr, err := GetGroupVersionResource(gr, discovery) + if err != nil { + return nil, err + } + + allRes, err := dynamic.Resource(*gvr).Namespace(ns).List(context.Background(), op) + if err != nil { + return nil, err + } + + return allRes, nil +} diff --git a/vendor/github.com/tektoncd/cli/pkg/actions/patch.go b/vendor/github.com/tektoncd/cli/pkg/actions/patch.go new file mode 100644 index 0000000000..7dcfacaa68 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/actions/patch.go @@ -0,0 +1,38 @@ +// Copyright © 2019-2020 The Tekton Authors. +// +// 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. + +package actions + +import ( + "context" + + "github.com/tektoncd/cli/pkg/cli" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" +) + +func Patch(gr schema.GroupVersionResource, clients *cli.Clients, objName string, data []byte, opt metav1.PatchOptions, ns string) (*unstructured.Unstructured, error) { + gvr, err := GetGroupVersionResource(gr, clients.Tekton.Discovery()) + if err != nil { + return nil, err + } + patchedObj, err := clients.Dynamic.Resource(*gvr).Namespace(ns).Patch(context.Background(), objName, types.JSONPatchType, data, opt) + if err != nil { + return nil, err + } + + return patchedObj, nil +} diff --git a/vendor/github.com/tektoncd/cli/pkg/actions/watch.go b/vendor/github.com/tektoncd/cli/pkg/actions/watch.go new file mode 100644 index 0000000000..398ec0085e --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/actions/watch.go @@ -0,0 +1,38 @@ +// Copyright © 2019-2020 The Tekton Authors. +// +// 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. + +package actions + +import ( + "context" + + "github.com/tektoncd/cli/pkg/cli" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" +) + +func Watch(gr schema.GroupVersionResource, clients *cli.Clients, ns string, op metav1.ListOptions) (watch.Interface, error) { + gvr, err := GetGroupVersionResource(gr, clients.Tekton.Discovery()) + if err != nil { + return nil, err + } + + watch, err := clients.Dynamic.Resource(*gvr).Namespace(ns).Watch(context.Background(), op) + if err != nil { + return nil, err + } + + return watch, nil +} diff --git a/vendor/github.com/tektoncd/cli/pkg/cli/interface.go b/vendor/github.com/tektoncd/cli/pkg/cli/interface.go new file mode 100644 index 0000000000..44aa762708 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/cli/interface.go @@ -0,0 +1,65 @@ +// Copyright © 2019 The Tekton Authors. +// +// 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. + +package cli + +import ( + "io" + "net/http" + + "github.com/jonboulle/clockwork" + "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + versionedResource "github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned" + versionedTriggers "github.com/tektoncd/triggers/pkg/client/clientset/versioned" + "k8s.io/client-go/dynamic" + k8s "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" +) + +type Stream struct { + In io.Reader + Out io.Writer + Err io.Writer +} + +type Clients struct { + Tekton versioned.Interface + Kube k8s.Interface + Triggers versionedTriggers.Interface + Resource versionedResource.Interface + HTTPClient http.Client + Dynamic dynamic.Interface +} + +// Params interface provides +type Params interface { + // SetKubeConfigPath uses the kubeconfig path to instantiate tekton + // returned by Clientset function + SetKubeConfigPath(string) + // SetKubeContext extends the specificity of the above SetKubeConfigPath + // by using a context other than the default context in the given kubeconfig + SetKubeContext(string) + Clients(...*rest.Config) (*Clients, error) + KubeClient() (k8s.Interface, error) + + // SetNamespace can be used to store the namespace parameter that is needed + // by most commands + SetNamespace(string) + Namespace() string + + // SetNoColour set colouring or not + SetNoColour(bool) + + Time() clockwork.Clock +} diff --git a/vendor/github.com/tektoncd/cli/pkg/cli/params.go b/vendor/github.com/tektoncd/cli/pkg/cli/params.go new file mode 100644 index 0000000000..14e3284d58 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/cli/params.go @@ -0,0 +1,201 @@ +// Copyright © 2019 The Tekton Authors. +// +// 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. + +package cli + +import ( + "github.com/fatih/color" + "github.com/jonboulle/clockwork" + "github.com/pkg/errors" + "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + versionedResource "github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned" + versionedTriggers "github.com/tektoncd/triggers/pkg/client/clientset/versioned" + "k8s.io/client-go/dynamic" + k8s "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +type TektonParams struct { + clients *Clients + kubeConfigPath string + kubeContext string + namespace string +} + +// ensure that TektonParams complies with cli.Params interface +var _ Params = (*TektonParams)(nil) + +func (p *TektonParams) SetKubeConfigPath(path string) { + p.kubeConfigPath = path +} + +func (p *TektonParams) SetKubeContext(context string) { + p.kubeContext = context +} + +func (p *TektonParams) tektonClient(config *rest.Config) (versioned.Interface, error) { + cs, err := versioned.NewForConfig(config) + if err != nil { + return nil, err + } + + return cs, nil +} + +func (p *TektonParams) triggersClient(config *rest.Config) (versionedTriggers.Interface, error) { + cs, err := versionedTriggers.NewForConfig(config) + if err != nil { + return nil, err + } + + return cs, nil +} + +func (p *TektonParams) resourceClient(config *rest.Config) (versionedResource.Interface, error) { + cs, err := versionedResource.NewForConfig(config) + if err != nil { + return nil, err + } + + return cs, nil +} + +// Set kube client based on config +func (p *TektonParams) kubeClient(config *rest.Config) (k8s.Interface, error) { + k8scs, err := k8s.NewForConfig(config) + if err != nil { + return nil, errors.Wrapf(err, "failed to create k8s client from config") + } + + return k8scs, nil +} + +func (p *TektonParams) dynamicClient(config *rest.Config) (dynamic.Interface, error) { + dynamicClient, err := dynamic.NewForConfig(config) + if err != nil { + return nil, errors.Wrapf(err, "failed to create dynamic client from config") + + } + return dynamicClient, err +} + +// Only returns kube client, not tekton client +func (p *TektonParams) KubeClient() (k8s.Interface, error) { + + config, err := p.config() + if err != nil { + return nil, err + } + + kube, err := p.kubeClient(config) + + if err != nil { + return nil, err + } + + return kube, nil +} + +func (p *TektonParams) Clients(cfg ...*rest.Config) (*Clients, error) { + if p.clients != nil { + return p.clients, nil + } + var config *rest.Config + + if len(cfg) != 0 && cfg[0] != nil { + config = cfg[0] + } else { + defaultConfig, err := p.config() + if err != nil { + return nil, err + } + config = defaultConfig + } + + tekton, err := p.tektonClient(config) + if err != nil { + return nil, err + } + + resource, err := p.resourceClient(config) + if err != nil { + return nil, err + } + + triggers, err := p.triggersClient(config) + if err != nil { + return nil, err + } + + kube, err := p.kubeClient(config) + if err != nil { + return nil, err + } + + dynamic, err := p.dynamicClient(config) + if err != nil { + return nil, err + } + + p.clients = &Clients{ + Tekton: tekton, + Kube: kube, + Resource: resource, + Triggers: triggers, + Dynamic: dynamic, + } + + return p.clients, nil +} + +func (p *TektonParams) config() (*rest.Config, error) { + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + if p.kubeConfigPath != "" { + loadingRules.ExplicitPath = p.kubeConfigPath + } + configOverrides := &clientcmd.ConfigOverrides{} + if p.kubeContext != "" { + configOverrides.CurrentContext = p.kubeContext + } + kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) + if p.namespace == "" { + namespace, _, err := kubeConfig.Namespace() + if err != nil { + return nil, errors.Wrap(err, "Couldn't get kubeConfiguration namespace") + } + p.namespace = namespace + } + config, err := kubeConfig.ClientConfig() + if err != nil { + return nil, errors.Wrap(err, "Parsing kubeconfig failed") + } + return config, nil +} + +func (p *TektonParams) SetNoColour(b bool) { + color.NoColor = b +} + +func (p *TektonParams) SetNamespace(ns string) { + p.namespace = ns +} + +func (p *TektonParams) Namespace() string { + return p.namespace +} + +func (p *TektonParams) Time() clockwork.Clock { + return clockwork.NewRealClock() +} diff --git a/vendor/github.com/tektoncd/cli/pkg/formatted/address.go b/vendor/github.com/tektoncd/cli/pkg/formatted/address.go new file mode 100644 index 0000000000..8d377dd305 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/formatted/address.go @@ -0,0 +1,23 @@ +// Copyright © 2020 The Tekton Authors. +// +// 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. + +package formatted + +func FormatAddress(addressURL string) string { + if addressURL == "" { + return "---" + } + + return addressURL +} diff --git a/vendor/github.com/tektoncd/cli/pkg/formatted/color.go b/vendor/github.com/tektoncd/cli/pkg/formatted/color.go new file mode 100644 index 0000000000..9d56736a8d --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/formatted/color.go @@ -0,0 +1,176 @@ +// Copyright © 2019 The Tekton Authors. +// +// 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. + +package formatted + +import ( + "fmt" + "io" + "sync" + "sync/atomic" + + "github.com/fatih/color" +) + +var ( + // Palette of colors for rainbow's tasks, Red is avoided as keeping it for errors + palette = []color.Attribute{ + color.FgHiGreen, + color.FgHiYellow, + color.FgHiBlue, + color.FgHiMagenta, + color.FgHiCyan, + } +) + +// DecorateAttr decorate strings with a color or an emoji, respecting the user +// preference if no colour needed. +func DecorateAttr(attrString, message string) string { + if color.NoColor { + return message + } + + switch attrString { + case "bullet": + return fmt.Sprintf("∙ %s", message) + case "check": + return "✔ ️" + case "resources": + return "📦 " + case "params": + return "⚓ " + case "tasks": + return "🗒 " + case "pipelineruns": + return "⛩ " + case "status": + return "🌡️ " + case "inputresources": + return "📨 " + case "outputresources": + return "📡 " + case "steps": + return "🦶 " + case "message": + return "💌 " + case "taskruns": + return "🗂 " + case "sidecars": + return "🚗 " + case "results": + return "📝 " + case "workspaces": + return "📂 " + case "skippedtasks": + return "⏭️ " + } + + attr := color.Reset + switch attrString { + case "underline": + attr = color.Underline + case "underline bold": + return color.New(color.Underline).Add(color.Bold).Sprintf(message) + case "bold": + attr = color.Bold + case "yellow": + attr = color.FgHiYellow + case "green": + attr = color.FgHiGreen + case "red": + attr = color.FgHiRed + case "blue": + attr = color.FgHiBlue + case "magenta": + attr = color.FgHiMagenta + case "cyan": + attr = color.FgHiCyan + case "black": + attr = color.FgHiBlack + case "white": + attr = color.FgHiWhite + } + + return color.New(attr).Sprintf(message) +} + +type atomicCounter struct { + value uint32 + threshold int +} + +func (c *atomicCounter) next() int { + v := atomic.AddUint32(&c.value, 1) + next := int(v-1) % c.threshold + atomic.CompareAndSwapUint32(&c.value, uint32(c.threshold), 0) + return next + +} + +type rainbow struct { + cache sync.Map + counter atomicCounter +} + +func newRainbow() *rainbow { + return &rainbow{ + counter: atomicCounter{threshold: len(palette)}, + } +} + +func (r *rainbow) get(x string) color.Attribute { + if value, ok := r.cache.Load(x); ok { + return value.(color.Attribute) + } + + clr := palette[r.counter.next()] + r.cache.Store(x, clr) + return clr +} + +// Fprintf formats according to a format specifier and writes to w. +// the first argument is a label to keep the same colour on. +func (r *rainbow) Fprintf(label string, w io.Writer, format string, args ...interface{}) { + attribute := r.get(label) + crainbow := color.Set(attribute).Add(color.Bold) + crainbow.Fprintf(w, format, args...) +} + +// Color formatter to print the colored output on streams +type Color struct { + Rainbow *rainbow + + red *color.Color + blue *color.Color +} + +// NewColor returns a new instance color formatter +func NewColor() *Color { + return &Color{ + Rainbow: newRainbow(), + + red: color.New(color.FgRed), + blue: color.New(color.FgBlue), + } +} + +// PrintRed prints the formatted content to given destination in red color +func (c *Color) PrintRed(w io.Writer, format string, args ...interface{}) { + c.red.Fprintf(w, format, args...) +} + +// Error prints the formatted content to given destination in red color +func (c *Color) Error(w io.Writer, format string, args ...interface{}) { + c.PrintRed(w, format, args...) +} diff --git a/vendor/github.com/tektoncd/cli/pkg/formatted/completion.go b/vendor/github.com/tektoncd/cli/pkg/formatted/completion.go new file mode 100644 index 0000000000..e093d57070 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/formatted/completion.go @@ -0,0 +1,31 @@ +package formatted + +import ( + "os/exec" + "strings" + + "github.com/spf13/cobra" +) + +// GetObjectsWithKubectl return completions with kubectl, we are doing this with +// kubectl since we have caching and without it completion is way too slow +func GetObjectsWithKubectl(obj string) []string { + out, err := exec.Command("kubectl", "get", obj, "-o=jsonpath={range .items[*]}{.metadata.name} {end}").Output() + if err != nil { + return nil + } + return strings.Fields(string(out)) +} + +// BaseCompletion return a completion for a kubernetes object using Kubectl +func BaseCompletion(target string, args []string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return GetObjectsWithKubectl(target), cobra.ShellCompDirectiveNoFileComp +} + +// ParentCompletion do completion of command to the Parent +func ParentCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return BaseCompletion(cmd.Parent().Name(), args) +} diff --git a/vendor/github.com/tektoncd/cli/pkg/formatted/conditions.go b/vendor/github.com/tektoncd/cli/pkg/formatted/conditions.go new file mode 100644 index 0000000000..eb13346e5b --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/formatted/conditions.go @@ -0,0 +1,33 @@ +// Copyright © 2020 The Tekton Authors. +// +// 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. + +package formatted + +import ( + "strings" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" +) + +// TaskConditions formats a list of conditions +func TaskConditions(conditions []v1beta1.PipelineTaskCondition) string { + if len(conditions) == 0 { + return "---" + } + var conditionRefs []string + for _, c := range conditions { + conditionRefs = append(conditionRefs, c.ConditionRef) + } + return strings.Join(conditionRefs, ", ") +} diff --git a/vendor/github.com/tektoncd/cli/pkg/formatted/description.go b/vendor/github.com/tektoncd/cli/pkg/formatted/description.go new file mode 100644 index 0000000000..a5fdad8a52 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/formatted/description.go @@ -0,0 +1,24 @@ +// Copyright © 2019 The Tekton Authors. +// +// 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. + +package formatted + +// If description is longer than 20 char then it will return +// initial 20 chars suffixed by ... +func FormatDesc(desc string) string { + if len(desc) > 20 { + return desc[0:19] + "..." + } + return desc +} diff --git a/vendor/github.com/tektoncd/cli/pkg/formatted/k8s.go b/vendor/github.com/tektoncd/cli/pkg/formatted/k8s.go new file mode 100644 index 0000000000..57a87c05a4 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/formatted/k8s.go @@ -0,0 +1,83 @@ +// Copyright © 2019 The Tekton Authors. +// +// 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. + +package formatted + +import ( + "fmt" + "sync/atomic" + + "github.com/fatih/color" + corev1 "k8s.io/api/core/v1" + "knative.dev/pkg/apis/duck/v1beta1" +) + +var ConditionColor = map[string]color.Attribute{ + "Failed": color.FgHiRed, + "Succeeded": color.FgHiGreen, + "Running": color.FgHiBlue, + "Cancelled": color.FgHiYellow, + "Completed": color.FgHiMagenta, + "Pending": color.FgHiBlue, + "Started": color.FgHiCyan, +} + +var stepCounter uint64 + +// ColorStatus Get a status coloured +func ColorStatus(status string) string { + return color.New(ConditionColor[status]).Sprint(status) +} + +// AutoStepName when our stepName is empty return a generated name as generated +// on pipeLine +func AutoStepName(stepName string) string { + unnamedStep := fmt.Sprintf("unnamed-%d", stepCounter) + atomic.AddUint64(&stepCounter, 1) + if stepName != "" { + return stepName + } + return unnamedStep +} + +// Condition returns a human readable text based on the status of the Condition +func Condition(c v1beta1.Conditions) string { + var status string + if len(c) == 0 { + return "---" + } + + switch c[0].Status { + case corev1.ConditionFalse: + status = "Failed" + case corev1.ConditionTrue: + status = "Succeeded" + case corev1.ConditionUnknown: + status = "Running" + } + + if c[0].Reason != "" && c[0].Reason != status { + switch c[0].Reason { + case "PipelineRunCancelled", "TaskRunCancelled", "Cancelled": + return ColorStatus("Cancelled") + "(" + c[0].Reason + ")" + case "PipelineRunStopping", "TaskRunStopping": + return ColorStatus("Failed") + "(" + c[0].Reason + ")" + case "CreateContainerConfigError", "ExceededNodeResources", "ExceededResourceQuota": + return ColorStatus("Pending") + "(" + c[0].Reason + ")" + default: + return ColorStatus(status) + "(" + c[0].Reason + ")" + } + } + return ColorStatus(status) +} diff --git a/vendor/github.com/tektoncd/cli/pkg/formatted/param.go b/vendor/github.com/tektoncd/cli/pkg/formatted/param.go new file mode 100644 index 0000000000..7667ad56b8 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/formatted/param.go @@ -0,0 +1,91 @@ +// Copyright © 2020 The Tekton Authors. +// +// 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. + +package formatted + +import ( + "fmt" + "strings" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" +) + +// Param returns params with their values. If user value is not defined then returns default value, +// if default value is not defined then returns param's type +func Param(params []v1beta1.Param, paramSpec []v1beta1.ParamSpec) string { + if len(params) == 0 { + return "---" + } + var str string + for i, param := range params { + if param.Value.Type == "string" { + paramValue := CheckParamDefaultValue(param.Value.StringVal, paramSpec) + if i == len(params)-1 { + str += fmt.Sprintf("%s: %s", param.Name, paramValue) + } else { + str += fmt.Sprintf("%s: %s, ", param.Name, paramValue) + } + } else { + paramValues := " [" + for j, pv := range param.Value.ArrayVal { + pv = CheckParamDefaultValue(pv, paramSpec) + if j == len(param.Value.ArrayVal)-1 { + paramValues += " " + pv + " ]" + } else { + paramValues += " " + pv + "," + } + } + if i == len(params)-1 { + str += fmt.Sprintf("%s:%s", param.Name, paramValues) + } else { + str += fmt.Sprintf("%s:%s, ", param.Name, paramValues) + } + } + } + return str +} + +// CheckParamDefaultValue returns param's value if defined, if not then checks for default value +// If default value is not defined then returns param's type +func CheckParamDefaultValue(param string, paramSpec []v1beta1.ParamSpec) string { + if strings.ContainsAny(param, "$") { + paramValue := "" + replacer := strings.NewReplacer("$", "", "(", "", ")", "", "params.", "") + paramName := replacer.Replace(param) + for _, spec := range paramSpec { + if spec.Name == paramName { + if spec.Default == nil { + paramValue = string(spec.Type) + break + } + if spec.Default.Type == "string" { + paramValue = spec.Default.StringVal + } else if spec.Default.Type == "array" { + pv := "" + for k, val := range spec.Default.ArrayVal { + if k == 0 { + pv += val + } else { + pv += " " + val + } + } + paramValue = pv + } + break + } + } + return paramValue + } + return param +} diff --git a/vendor/github.com/tektoncd/cli/pkg/formatted/results.go b/vendor/github.com/tektoncd/cli/pkg/formatted/results.go new file mode 100644 index 0000000000..1aaa281c65 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/formatted/results.go @@ -0,0 +1,23 @@ +// Copyright © 2020 The Tekton Authors. +// +// 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. + +package formatted + +import "strings" + +// Result will format a given result value +func Result(value string) string { + // remove trailing new-line from value + return strings.TrimSuffix(value, "\n") +} diff --git a/vendor/github.com/tektoncd/cli/pkg/formatted/task.go b/vendor/github.com/tektoncd/cli/pkg/formatted/task.go new file mode 100644 index 0000000000..df86cb4e62 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/formatted/task.go @@ -0,0 +1,26 @@ +// Copyright © 2021 The Tekton Authors. +// +// 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. + +package formatted + +import ( + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" +) + +func GetTaskRefName(task *v1beta1.PipelineTask) string { + if task.TaskRef != nil { + return task.TaskRef.Name + } + return "EMBEDDED" +} diff --git a/vendor/github.com/tektoncd/cli/pkg/formatted/time.go b/vendor/github.com/tektoncd/cli/pkg/formatted/time.go new file mode 100644 index 0000000000..136ae99216 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/formatted/time.go @@ -0,0 +1,47 @@ +// Copyright © 2019 The Tekton Authors. +// +// 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. + +package formatted + +import ( + "github.com/hako/durafmt" + "github.com/jonboulle/clockwork" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func Age(t *metav1.Time, c clockwork.Clock) string { + if t.IsZero() { + return "---" + } + + dur := c.Since(t.Time) + return durafmt.ParseShort(dur).String() + " ago" +} + +func Duration(t1, t2 *metav1.Time) string { + if t1.IsZero() || t2.IsZero() { + return "---" + } + + dur := t2.Time.Sub(t1.Time) + return durafmt.ParseShort(dur).String() +} + +func Timeout(t *metav1.Duration) string { + if t == nil { + return "---" + } + + return durafmt.Parse(t.Duration).String() +} diff --git a/vendor/github.com/tektoncd/cli/pkg/formatted/version.go b/vendor/github.com/tektoncd/cli/pkg/formatted/version.go new file mode 100644 index 0000000000..dad09df867 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/formatted/version.go @@ -0,0 +1,22 @@ +// Copyright © 2021 The Tekton Authors. +// +// 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. + +package formatted + +func FindVersion(version map[string]string) string { + if _, ok := version["app.kubernetes.io/version"]; ok { + return version["app.kubernetes.io/version"] + } + return "" +} diff --git a/vendor/github.com/tektoncd/cli/pkg/formatted/workspace.go b/vendor/github.com/tektoncd/cli/pkg/formatted/workspace.go new file mode 100644 index 0000000000..ce08df3894 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/formatted/workspace.go @@ -0,0 +1,91 @@ +// Copyright © 2020 The Tekton Authors. +// +// 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. + +package formatted + +import ( + "fmt" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + corev1 "k8s.io/api/core/v1" +) + +func Workspace(ws v1beta1.WorkspaceBinding) string { + if ws.VolumeClaimTemplate != nil { + return "VolumeClaimTemplate" + } + if ws.PersistentVolumeClaim != nil { + claimName := ws.PersistentVolumeClaim.ClaimName + return fmt.Sprintf("PersistentVolumeClaim (claimName=%s)", claimName) + } + if ws.EmptyDir != nil { + dirType := getWorkspaceEmptyDir(ws.EmptyDir) + return fmt.Sprintf("EmptyDir (emptyDir=%s)", dirType) + } + if ws.ConfigMap != nil { + cm := getWorkspaceConfig(ws.ConfigMap) + return fmt.Sprintf("ConfigMap (%s)", cm) + } + if ws.Secret != nil { + secret := getWorkspaceSecret(ws.Secret) + return fmt.Sprintf("Secret (%s)", secret) + } + return "" +} + +func getWorkspaceEmptyDir(ed *corev1.EmptyDirVolumeSource) string { + sM := ed.Medium + var dirType string + if sM == corev1.StorageMediumDefault { + dirType = "" + } + if sM == corev1.StorageMediumMemory { + dirType = "Memory" + } + if sM == corev1.StorageMediumHugePages { + dirType = "HugePages" + } + return dirType +} + +func getWorkspaceConfig(cm *corev1.ConfigMapVolumeSource) string { + cmName := cm.LocalObjectReference.Name + cmItems := cm.Items + str := fmt.Sprintf("config=%s", cmName) + if len(cmItems) != 0 { + str = fmt.Sprintf("%s%s", str, getItems(cmItems)) + } + return str +} + +func getWorkspaceSecret(secret *corev1.SecretVolumeSource) string { + secretName := secret.SecretName + secretItems := secret.Items + str := fmt.Sprintf("secret=%s", secretName) + if len(secretItems) != 0 { + str = fmt.Sprintf("%s%s", str, getItems(secretItems)) + } + return str +} + +func getItems(items []corev1.KeyToPath) string { + str := "" + for i := range items { + kp := items[i] + key := kp.Key + value := kp.Path + str = fmt.Sprintf("%s,item=%s=%s", str, key, value) + } + return str +} diff --git a/vendor/github.com/tektoncd/cli/pkg/pipelinerun/pipelinerun.go b/vendor/github.com/tektoncd/cli/pkg/pipelinerun/pipelinerun.go new file mode 100644 index 0000000000..bae2788e44 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/pipelinerun/pipelinerun.go @@ -0,0 +1,211 @@ +// Copyright © 2019 The Tekton Authors. +// +// 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. + +package pipelinerun + +import ( + "context" + "encoding/json" + "fmt" + "os" + + "github.com/tektoncd/cli/pkg/actions" + "github.com/tektoncd/cli/pkg/cli" + "github.com/tektoncd/cli/pkg/formatted" + prsort "github.com/tektoncd/cli/pkg/pipelinerun/sort" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" +) + +var prGroupResource = schema.GroupVersionResource{Group: "tekton.dev", Resource: "pipelineruns"} + +// GetAllPipelineRuns returns all pipelinesruns running in a namespace +func GetAllPipelineRuns(p cli.Params, opts metav1.ListOptions, limit int) ([]string, error) { + cs, err := p.Clients() + if err != nil { + return nil, err + } + + runs, err := List(cs, opts, p.Namespace()) + if err != nil { + return nil, err + } + + runslen := len(runs.Items) + if runslen > 1 { + prsort.SortByStartTime(runs.Items) + } + + if limit > runslen { + limit = runslen + } + + ret := []string{} + for i, run := range runs.Items { + if i < limit { + ret = append(ret, run.ObjectMeta.Name+" started "+formatted.Age(run.Status.StartTime, p.Time())) + } + } + return ret, nil +} + +func List(c *cli.Clients, opts metav1.ListOptions, ns string) (*v1beta1.PipelineRunList, error) { + unstructuredPR, err := actions.List(prGroupResource, c.Dynamic, c.Tekton.Discovery(), ns, opts) + if err != nil { + return nil, err + } + + var runs *v1beta1.PipelineRunList + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredPR.UnstructuredContent(), &runs); err != nil { + return nil, err + } + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to list pipelineruns from %s namespace \n", ns) + return nil, err + } + + return runs, nil +} + +// It will fetch the resource based on the api available and return v1beta1 form +func Get(c *cli.Clients, prname string, opts metav1.GetOptions, ns string) (*v1beta1.PipelineRun, error) { + gvr, err := actions.GetGroupVersionResource(prGroupResource, c.Tekton.Discovery()) + if err != nil { + return nil, err + } + + if gvr.Version == "v1alpha1" { + pipelinerun, err := getV1alpha1(c, prname, opts, ns) + if err != nil { + return nil, err + } + var pipelinerunConverted v1beta1.PipelineRun + err = pipelinerun.ConvertTo(context.Background(), &pipelinerunConverted) + if err != nil { + return nil, err + } + return &pipelinerunConverted, nil + } + return GetV1beta1(c, prname, opts, ns) +} + +// It will fetch the resource in v1beta1 struct format +func GetV1beta1(c *cli.Clients, prname string, opts metav1.GetOptions, ns string) (*v1beta1.PipelineRun, error) { + unstructuredPR, err := actions.Get(prGroupResource, c.Dynamic, c.Tekton.Discovery(), prname, ns, opts) + if err != nil { + return nil, err + } + + var pipelinerun *v1beta1.PipelineRun + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredPR.UnstructuredContent(), &pipelinerun); err != nil { + fmt.Fprintf(os.Stderr, "Failed to get pipelinerun from %s namespace \n", ns) + return nil, err + } + return pipelinerun, nil +} + +// It will fetch the resource in v1alpha1 struct format +func getV1alpha1(c *cli.Clients, prname string, opts metav1.GetOptions, ns string) (*v1alpha1.PipelineRun, error) { + unstructuredPR, err := actions.Get(prGroupResource, c.Dynamic, c.Tekton.Discovery(), prname, ns, opts) + if err != nil { + return nil, err + } + + var pipelinerun *v1alpha1.PipelineRun + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredPR.UnstructuredContent(), &pipelinerun); err != nil { + fmt.Fprintf(os.Stderr, "Failed to get pipelinerun from %s namespace \n", ns) + return nil, err + } + return pipelinerun, nil +} + +func Watch(c *cli.Clients, opts metav1.ListOptions, ns string) (watch.Interface, error) { + watch, err := actions.Watch(prGroupResource, c, ns, opts) + if err != nil { + return nil, err + } + return watch, nil +} + +type patchStringValue struct { + Op string `json:"op"` + Path string `json:"path"` + Value string `json:"value"` +} + +func Cancel(c *cli.Clients, prname string, opts metav1.PatchOptions, cancelStatus, ns string) (*v1beta1.PipelineRun, error) { + payload := []patchStringValue{{ + Op: "replace", + Path: "/spec/status", + Value: cancelStatus, + }} + + data, _ := json.Marshal(payload) + prGroupResource := schema.GroupVersionResource{Group: "tekton.dev", Resource: "pipelineruns"} + unstructuredPR, err := actions.Patch(prGroupResource, c, prname, data, opts, ns) + if err != nil { + return nil, err + } + + var pipelinerun *v1beta1.PipelineRun + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredPR.UnstructuredContent(), &pipelinerun); err != nil { + return nil, err + } + + return pipelinerun, nil +} + +// It will create the resource based on the api available. +func Create(c *cli.Clients, pr *v1beta1.PipelineRun, opts metav1.CreateOptions, ns string) (*v1beta1.PipelineRun, error) { + gvr, err := actions.GetGroupVersionResource(prGroupResource, c.Tekton.Discovery()) + if err != nil { + return nil, err + } + + if gvr.Version == "v1alpha1" { + var pipelinerunConverted v1alpha1.PipelineRun + err = pipelinerunConverted.ConvertFrom(context.Background(), pr) + if err != nil { + return nil, err + } + pipelinerunConverted.Kind = "PipelineRun" + pipelinerunConverted.APIVersion = "tekton.dev/v1alpha1" + return createUnstructured(&pipelinerunConverted, c, opts, ns, gvr) + } + return createUnstructured(pr, c, opts, ns, gvr) +} + +func createUnstructured(obj runtime.Object, c *cli.Clients, opts metav1.CreateOptions, ns string, resource *schema.GroupVersionResource) (*v1beta1.PipelineRun, error) { + object, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) + unstructuredPR := &unstructured.Unstructured{ + Object: object, + } + + newUnstructuredPR, err := actions.Create(*resource, c, unstructuredPR, ns, opts) + if err != nil { + return nil, err + } + + var pipelinerun *v1beta1.PipelineRun + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(newUnstructuredPR.UnstructuredContent(), &pipelinerun); err != nil { + return nil, err + } + + return pipelinerun, nil +} diff --git a/vendor/github.com/tektoncd/cli/pkg/pipelinerun/sort/by_namespace.go b/vendor/github.com/tektoncd/cli/pkg/pipelinerun/sort/by_namespace.go new file mode 100644 index 0000000000..1c1ccb8048 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/pipelinerun/sort/by_namespace.go @@ -0,0 +1,48 @@ +// Copyright © 2020 The Tekton Authors. +// +// 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. + +package pipelinerun + +import ( + "sort" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" +) + +func SortByNamespace(prs []v1beta1.PipelineRun) { + sort.Sort(byNamespace(prs)) +} + +type byNamespace []v1beta1.PipelineRun + +func (prs byNamespace) compareNamespace(ins, jns string) (lt, eq bool) { + lt, eq = ins < jns, ins == jns + return lt, eq +} + +func (prs byNamespace) Len() int { return len(prs) } +func (prs byNamespace) Swap(i, j int) { prs[i], prs[j] = prs[j], prs[i] } +func (prs byNamespace) Less(i, j int) bool { + var lt, eq bool + if lt, eq = prs.compareNamespace(prs[i].Namespace, prs[j].Namespace); eq { + if prs[j].Status.StartTime == nil { + return false + } + if prs[i].Status.StartTime == nil { + return true + } + return prs[j].Status.StartTime.Before(prs[i].Status.StartTime) + } + return lt +} diff --git a/vendor/github.com/tektoncd/cli/pkg/pipelinerun/sort/by_start_time.go b/vendor/github.com/tektoncd/cli/pkg/pipelinerun/sort/by_start_time.go new file mode 100644 index 0000000000..ce34a92d0d --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/pipelinerun/sort/by_start_time.go @@ -0,0 +1,39 @@ +// Copyright © 2020 The Tekton Authors. +// +// 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. + +package pipelinerun + +import ( + "sort" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" +) + +func SortByStartTime(prs []v1beta1.PipelineRun) { + sort.Sort(byStartTime(prs)) +} + +type byStartTime []v1beta1.PipelineRun + +func (prs byStartTime) Len() int { return len(prs) } +func (prs byStartTime) Swap(i, j int) { prs[i], prs[j] = prs[j], prs[i] } +func (prs byStartTime) Less(i, j int) bool { + if prs[j].Status.StartTime == nil { + return false + } + if prs[i].Status.StartTime == nil { + return true + } + return prs[j].Status.StartTime.Before(prs[i].Status.StartTime) +} diff --git a/vendor/github.com/tektoncd/cli/pkg/pipelinerun/tracker.go b/vendor/github.com/tektoncd/cli/pkg/pipelinerun/tracker.go new file mode 100644 index 0000000000..e04c57f4af --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/pipelinerun/tracker.go @@ -0,0 +1,166 @@ +// Copyright © 2019 The Tekton Authors. +// +// 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. + +package pipelinerun + +import ( + "context" + "time" + + "github.com/tektoncd/cli/pkg/actions" + trh "github.com/tektoncd/cli/pkg/taskrun" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + informers "github.com/tektoncd/pipeline/pkg/client/informers/externalversions" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/tools/cache" +) + +// Tracker tracks the progress of a PipelineRun +type Tracker struct { + Name string + Ns string + Tekton versioned.Interface + ongoingTasks map[string]bool +} + +// NewTracker returns a new instance of Tracker +func NewTracker(name string, ns string, tekton versioned.Interface) *Tracker { + return &Tracker{ + Name: name, + Ns: ns, + Tekton: tekton, + ongoingTasks: map[string]bool{}, + } +} + +// Monitor to observe the progress of PipelineRun. It emits +// an event upon starting of a new Pipeline's Task. +// allowed containers the name of the Pipeline tasks, which used as filter +// limit the events to only those tasks +func (t *Tracker) Monitor(allowed []string) <-chan []trh.Run { + + factory := informers.NewSharedInformerFactoryWithOptions( + t.Tekton, + time.Second*10, + informers.WithNamespace(t.Ns), + informers.WithTweakListOptions(pipelinerunOpts(t.Name))) + + gvr, _ := actions.GetGroupVersionResource( + schema.GroupVersionResource{Group: "tekton.dev", Resource: "pipelineruns"}, + t.Tekton.Discovery(), + ) + + genericInformer, _ := factory.ForResource(*gvr) + informer := genericInformer.Informer() + + stopC := make(chan struct{}) + trC := make(chan []trh.Run) + + go func() { + <-stopC + close(trC) + }() + + eventHandler := func(obj interface{}) { + var pipelinerunConverted v1beta1.PipelineRun + switch gvr.Version { + case "v1alpha1": + pr, ok := obj.(*v1alpha1.PipelineRun) + if !ok || pr == nil { + return + } + if err := pr.ConvertTo(context.Background(), &pipelinerunConverted); err != nil { + return + } + case "v1beta1": + pr, ok := obj.(*v1beta1.PipelineRun) + if !ok || pr == nil { + return + } + pr.DeepCopyInto(&pipelinerunConverted) + } + + trC <- t.findNewTaskruns(&pipelinerunConverted, allowed) + + if hasCompleted(&pipelinerunConverted) { + close(stopC) // should close trC + } + } + + informer.AddEventHandler( + cache.ResourceEventHandlerFuncs{ + AddFunc: eventHandler, + UpdateFunc: func(_, newObj interface{}) { eventHandler(newObj) }, + }, + ) + + factory.Start(stopC) + factory.WaitForCacheSync(stopC) + + return trC +} + +func pipelinerunOpts(name string) func(opts *metav1.ListOptions) { + return func(opts *metav1.ListOptions) { + opts.FieldSelector = fields.OneTermEqualSelector("metadata.name", name).String() + } + +} + +// handles changes to pipelinerun and pushes the Run information to the +// channel if the task is new and is in the allowed list of tasks +// returns true if the pipelinerun has finished +func (t *Tracker) findNewTaskruns(pr *v1beta1.PipelineRun, allowed []string) []trh.Run { + ret := []trh.Run{} + for tr, trs := range pr.Status.TaskRuns { + retries := 0 + if pr.Status.PipelineSpec != nil { + for _, pipelineTask := range pr.Status.PipelineSpec.Tasks { + if trs.PipelineTaskName == pipelineTask.Name { + retries = pipelineTask.Retries + } + } + } + run := trh.Run{Name: tr, Task: trs.PipelineTaskName, Retries: retries} + + if t.loggingInProgress(tr) || + !trh.HasScheduled(trs) || + trh.IsFiltered(run, allowed) { + continue + } + + t.ongoingTasks[tr] = true + ret = append(ret, run) + } + + return ret +} + +func hasCompleted(pr *v1beta1.PipelineRun) bool { + if len(pr.Status.Conditions) == 0 { + return false + } + + return pr.Status.Conditions[0].Status != corev1.ConditionUnknown +} + +func (t *Tracker) loggingInProgress(tr string) bool { + _, ok := t.ongoingTasks[tr] + return ok +} diff --git a/vendor/github.com/tektoncd/cli/pkg/printer/printer.go b/vendor/github.com/tektoncd/cli/pkg/printer/printer.go new file mode 100644 index 0000000000..b3942fbf70 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/printer/printer.go @@ -0,0 +1,30 @@ +// Copyright © 2019 The Tekton Authors. +// +// 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. + +package printer + +import ( + "io" + + "k8s.io/apimachinery/pkg/runtime" + cliopts "k8s.io/cli-runtime/pkg/genericclioptions" +) + +func PrintObject(out io.Writer, o runtime.Object, f *cliopts.PrintFlags) error { + printer, err := f.ToPrinter() + if err != nil { + return err + } + return printer.PrintObj(o, out) +} diff --git a/vendor/github.com/tektoncd/cli/pkg/task/task.go b/vendor/github.com/tektoncd/cli/pkg/task/task.go new file mode 100644 index 0000000000..156a72c972 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/task/task.go @@ -0,0 +1,211 @@ +// Copyright © 2019 The Tekton Authors. +// +// 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. + +package task + +import ( + "context" + "fmt" + "os" + + "github.com/tektoncd/cli/pkg/actions" + "github.com/tektoncd/cli/pkg/cli" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var taskGroupResource = schema.GroupVersionResource{Group: "tekton.dev", Resource: "tasks"} + +func GetAllTaskNames(p cli.Params) ([]string, error) { + cs, err := p.Clients() + if err != nil { + return nil, err + } + + ps, err := List(cs, metav1.ListOptions{}, p.Namespace()) + if err != nil { + return nil, err + } + + ret := []string{} + for _, item := range ps.Items { + ret = append(ret, item.ObjectMeta.Name) + } + return ret, nil +} + +func List(c *cli.Clients, opts metav1.ListOptions, ns string) (*v1beta1.TaskList, error) { + unstructuredT, err := actions.List(taskGroupResource, c.Dynamic, c.Tekton.Discovery(), ns, opts) + if err != nil { + return nil, err + } + + var tasks *v1beta1.TaskList + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredT.UnstructuredContent(), &tasks); err != nil { + return nil, err + } + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to list tasks from %s namespace \n", ns) + return nil, err + } + + return tasks, nil +} + +// It will fetch the resource based on the api available and return v1beta1 form +func Get(c *cli.Clients, taskname string, opts metav1.GetOptions, ns string) (*v1beta1.Task, error) { + gvr, err := actions.GetGroupVersionResource(taskGroupResource, c.Tekton.Discovery()) + if err != nil { + return nil, err + } + + if gvr.Version == "v1alpha1" { + task, err := getV1alpha1(c, taskname, opts, ns) + if err != nil { + return nil, err + } + var taskConverted v1beta1.Task + err = task.ConvertTo(context.Background(), &taskConverted) + if err != nil { + return nil, err + } + return &taskConverted, nil + } + return GetV1beta1(c, taskname, opts, ns) +} + +// It will fetch the resource in v1beta1 struct format +func GetV1beta1(c *cli.Clients, taskname string, opts metav1.GetOptions, ns string) (*v1beta1.Task, error) { + unstructuredT, err := actions.Get(taskGroupResource, c.Dynamic, c.Tekton.Discovery(), taskname, ns, opts) + if err != nil { + return nil, err + } + + var task *v1beta1.Task + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredT.UnstructuredContent(), &task); err != nil { + fmt.Fprintf(os.Stderr, "failed to get task from %s namespace \n", ns) + return nil, err + } + return task, nil +} + +// It will fetch the resource in v1alpha1 struct format +func getV1alpha1(c *cli.Clients, taskname string, opts metav1.GetOptions, ns string) (*v1alpha1.Task, error) { + unstructuredT, err := actions.Get(taskGroupResource, c.Dynamic, c.Tekton.Discovery(), taskname, ns, opts) + if err != nil { + return nil, err + } + + var task *v1alpha1.Task + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredT.UnstructuredContent(), &task); err != nil { + fmt.Fprintf(os.Stderr, "failed to get task from %s namespace \n", ns) + return nil, err + } + return task, nil +} + +// this will convert v1beta1 TaskSpec to v1alpha1 TaskSpec +func SpecConvertFrom(spec *v1beta1.TaskSpec) *v1alpha1.TaskSpec { + downTaskSpec := &v1alpha1.TaskSpec{} + if spec != nil { + downTaskSpec.Steps = spec.Steps + downTaskSpec.Volumes = spec.Volumes + downTaskSpec.StepTemplate = spec.StepTemplate + downTaskSpec.Sidecars = spec.Sidecars + downTaskSpec.Workspaces = spec.Workspaces + downTaskSpec.Results = spec.Results + downTaskSpec.Description = spec.Description + if spec.Resources != nil { + if len(spec.Resources.Inputs) > 0 { + if downTaskSpec.Inputs == nil { + downTaskSpec.Inputs = &v1alpha1.Inputs{} + } + downTaskSpec.Inputs.Resources = make([]v1alpha1.TaskResource, len(spec.Resources.Inputs)) + for i, resource := range spec.Resources.Inputs { + downTaskSpec.Inputs.Resources[i] = v1alpha1.TaskResource{ + ResourceDeclaration: v1alpha1.ResourceDeclaration{ + Name: resource.Name, + Type: resource.Type, + Description: resource.Description, + TargetPath: resource.TargetPath, + Optional: resource.Optional, + }, + } + } + } + + if len(spec.Resources.Outputs) > 0 { + if downTaskSpec.Outputs == nil { + downTaskSpec.Outputs = &v1alpha1.Outputs{} + } + downTaskSpec.Outputs.Resources = make([]v1alpha1.TaskResource, len(spec.Resources.Outputs)) + for i, resource := range spec.Resources.Outputs { + downTaskSpec.Outputs.Resources[i] = v1alpha1.TaskResource{ + ResourceDeclaration: v1alpha1.ResourceDeclaration{ + Name: resource.Name, + Type: resource.Type, + Description: resource.Description, + TargetPath: resource.TargetPath, + Optional: resource.Optional, + }, + } + } + } + } + + if spec.Params != nil && len(spec.Params) > 0 { + if downTaskSpec.Inputs == nil { + downTaskSpec.Inputs = &v1alpha1.Inputs{} + } + downTaskSpec.Inputs.Params = make([]v1alpha1.ParamSpec, len(spec.Params)) + for i, param := range spec.Params { + downTaskSpec.Inputs.Params[i] = *param.DeepCopy() + } + } + } + + return downTaskSpec +} + +func Create(c *cli.Clients, t *v1beta1.Task, opts metav1.CreateOptions, ns string) (*v1beta1.Task, error) { + _, err := actions.GetGroupVersionResource(taskGroupResource, c.Tekton.Discovery()) + if err != nil { + return nil, err + } + + return createUnstructured(t, c, opts, ns) +} + +func createUnstructured(obj runtime.Object, c *cli.Clients, opts metav1.CreateOptions, ns string) (*v1beta1.Task, error) { + object, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) + if err != nil { + return nil, err + } + unstructuredT := &unstructured.Unstructured{ + Object: object, + } + newUnstructuredT, err := actions.Create(taskGroupResource, c, unstructuredT, ns, opts) + if err != nil { + return nil, err + } + var task *v1beta1.Task + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(newUnstructuredT.UnstructuredContent(), &task); err != nil { + return nil, err + } + return task, nil +} diff --git a/vendor/github.com/tektoncd/cli/pkg/task/tasklastrun.go b/vendor/github.com/tektoncd/cli/pkg/task/tasklastrun.go new file mode 100644 index 0000000000..de54ff424f --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/task/tasklastrun.go @@ -0,0 +1,74 @@ +// Copyright © 2019 The Tekton Authors. +// +// 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. + +package task + +import ( + "fmt" + + "github.com/tektoncd/cli/pkg/cli" + trlist "github.com/tektoncd/cli/pkg/taskrun/list" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// LastRun returns the last taskrun for a given task/clustertask +func LastRun(cs *cli.Clients, resourceName, ns, kind string) (*v1beta1.TaskRun, error) { + options := metav1.ListOptions{} + + // change the label value to clusterTask if the resource is ClusterTask + label := "task" + if kind == "ClusterTask" { + label = "clusterTask" + } + + if resourceName != "" { + options = metav1.ListOptions{ + LabelSelector: fmt.Sprintf("tekton.dev/%s=%s", label, resourceName), + } + } + + runs, err := trlist.TaskRuns(cs, options, ns) + if err != nil { + return nil, err + } + + if len(runs.Items) == 0 { + return nil, fmt.Errorf("no TaskRuns related to %s %s found in namespace %s", kind, resourceName, ns) + } + + if kind == "Task" { + runs.Items = FilterByRef(runs.Items, kind) + } + + latest := runs.Items[0] + for _, run := range runs.Items { + if run.CreationTimestamp.Time.After(latest.CreationTimestamp.Time) { + latest = run + } + } + + return &latest, nil +} + +// this will filter the taskrun which have reference to Task or ClusterTask +func FilterByRef(taskruns []v1beta1.TaskRun, kind string) []v1beta1.TaskRun { + var filtered []v1beta1.TaskRun + for _, taskrun := range taskruns { + if string(taskrun.Spec.TaskRef.Kind) == kind { + filtered = append(filtered, taskrun) + } + } + return filtered +} diff --git a/vendor/github.com/tektoncd/cli/pkg/taskrun/create.go b/vendor/github.com/tektoncd/cli/pkg/taskrun/create.go new file mode 100644 index 0000000000..c05a9afc49 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/taskrun/create.go @@ -0,0 +1,127 @@ +// Copyright © 2020 The Tekton Authors. +// +// 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. + +package taskrun + +import ( + "github.com/tektoncd/cli/pkg/actions" + "github.com/tektoncd/cli/pkg/cli" + "github.com/tektoncd/cli/pkg/task" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +func Create(c *cli.Clients, tr *v1beta1.TaskRun, opts metav1.CreateOptions, ns string) (*v1beta1.TaskRun, error) { + trGVR, err := actions.GetGroupVersionResource(trGroupResource, c.Tekton.Discovery()) + if err != nil { + return nil, err + } + + if trGVR.Version == "v1alpha1" { + v1alpha1TaskRun := ConvertFrom(tr) + if err != nil { + return nil, err + } + return createUnstructured(v1alpha1TaskRun, c, opts, ns) + } + + return createUnstructured(tr, c, opts, ns) +} + +func createUnstructured(obj runtime.Object, c *cli.Clients, opts metav1.CreateOptions, ns string) (*v1beta1.TaskRun, error) { + object, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) + unstructuredTR := &unstructured.Unstructured{ + Object: object, + } + newUnstructuredTR, err := actions.Create(trGroupResource, c, unstructuredTR, ns, opts) + if err != nil { + return nil, err + } + var taskrun *v1beta1.TaskRun + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(newUnstructuredTR.UnstructuredContent(), &taskrun); err != nil { + return nil, err + } + return taskrun, nil +} + +// convert v1beta1 Taskrun to v1alpha1 Taskrun to support backward compatibility +func ConvertFrom(tr *v1beta1.TaskRun) *v1alpha1.TaskRun { + downtr := &v1alpha1.TaskRun{} + downtr.Kind = "TaskRun" + downtr.APIVersion = "tekton.dev/v1alpha1" + downtr.ObjectMeta = tr.ObjectMeta + downtr.Status = tr.Status + if tr.Spec.Resources != nil { + if len(tr.Spec.Resources.Inputs) > 0 { + if downtr.Spec.Inputs == nil { + downtr.Spec.Inputs = &v1alpha1.TaskRunInputs{} + } + downtr.Spec.Inputs.Resources = make([]v1alpha1.TaskResourceBinding, len(tr.Spec.Resources.Inputs)) + for i, resource := range tr.Spec.Resources.Inputs { + downtr.Spec.Inputs.Resources[i] = v1alpha1.TaskResourceBinding{ + PipelineResourceBinding: v1alpha1.PipelineResourceBinding{ + Name: resource.Name, + ResourceRef: resource.ResourceRef, + ResourceSpec: resource.ResourceSpec, + }, + Paths: resource.Paths, + } + } + } + + if len(tr.Spec.Resources.Outputs) > 0 { + if downtr.Spec.Outputs == nil { + downtr.Spec.Outputs = &v1alpha1.TaskRunOutputs{} + } + downtr.Spec.Outputs.Resources = make([]v1alpha1.TaskResourceBinding, len(tr.Spec.Resources.Outputs)) + for i, resource := range tr.Spec.Resources.Outputs { + downtr.Spec.Outputs.Resources[i] = v1alpha1.TaskResourceBinding{ + PipelineResourceBinding: v1alpha1.PipelineResourceBinding{ + Name: resource.Name, + ResourceRef: resource.ResourceRef, + ResourceSpec: resource.ResourceSpec, + }, + Paths: resource.Paths, + } + } + } + } + + if tr.Spec.Params != nil && len(tr.Spec.Params) > 0 { + if downtr.Spec.Inputs == nil { + downtr.Spec.Inputs = &v1alpha1.TaskRunInputs{} + } + downtr.Spec.Inputs.Params = make([]v1alpha1.Param, len(tr.Spec.Params)) + for i, param := range tr.Spec.Params { + downtr.Spec.Inputs.Params[i] = *param.DeepCopy() + } + } + + if tr.Spec.TaskSpec != nil { + downtr.Spec.TaskSpec = &v1alpha1.TaskSpec{} + downtr.Spec.TaskSpec = task.SpecConvertFrom(tr.Spec.TaskSpec) + } + + downtr.Spec.TaskRef = tr.Spec.TaskRef + downtr.Spec.ServiceAccountName = tr.Spec.ServiceAccountName + downtr.Spec.Status = tr.Spec.Status + downtr.Spec.PodTemplate = tr.Spec.PodTemplate + downtr.Spec.Workspaces = tr.Spec.Workspaces + downtr.Spec.Timeout = tr.Spec.Timeout + + return downtr +} diff --git a/vendor/github.com/tektoncd/cli/pkg/taskrun/get.go b/vendor/github.com/tektoncd/cli/pkg/taskrun/get.go new file mode 100644 index 0000000000..9231b61730 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/taskrun/get.go @@ -0,0 +1,77 @@ +// Copyright © 2019-2020 The Tekton Authors. +// +// 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. + +package taskrun + +import ( + "context" + "fmt" + + "github.com/tektoncd/cli/pkg/actions" + "github.com/tektoncd/cli/pkg/cli" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// It will fetch the resource based on the api available and return v1beta1 form +func Get(c *cli.Clients, trname string, opts metav1.GetOptions, ns string) (*v1beta1.TaskRun, error) { + gvr, err := actions.GetGroupVersionResource(trGroupResource, c.Tekton.Discovery()) + if err != nil { + return nil, err + } + + if gvr.Version == "v1alpha1" { + taskrun, err := getV1alpha1(c, trname, opts, ns) + if err != nil { + return nil, err + } + var taskrunConverted v1beta1.TaskRun + err = taskrun.ConvertTo(context.Background(), &taskrunConverted) + if err != nil { + return nil, err + } + return &taskrunConverted, nil + } + return GetV1beta1(c, trname, opts, ns) +} + +// It will fetch the resource in v1beta1 struct format +func GetV1beta1(c *cli.Clients, trname string, opts metav1.GetOptions, ns string) (*v1beta1.TaskRun, error) { + unstructuredTR, err := actions.Get(trGroupResource, c.Dynamic, c.Tekton.Discovery(), trname, ns, opts) + if err != nil { + return nil, err + } + + var taskrun *v1beta1.TaskRun + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredTR.UnstructuredContent(), &taskrun); err != nil { + return nil, fmt.Errorf("failed to get TaskRun from namespace %s", ns) + } + return taskrun, nil +} + +// It will fetch the resource in v1alpha1 struct format +func getV1alpha1(c *cli.Clients, trname string, opts metav1.GetOptions, ns string) (*v1alpha1.TaskRun, error) { + unstructuredTR, err := actions.Get(trGroupResource, c.Dynamic, c.Tekton.Discovery(), trname, ns, opts) + if err != nil { + return nil, err + } + + var taskrun *v1alpha1.TaskRun + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredTR.UnstructuredContent(), &taskrun); err != nil { + return nil, fmt.Errorf("failed to get TaskRun from namespace %s", ns) + } + return taskrun, nil +} diff --git a/vendor/github.com/tektoncd/cli/pkg/taskrun/list/list.go b/vendor/github.com/tektoncd/cli/pkg/taskrun/list/list.go new file mode 100644 index 0000000000..7cb7d633ca --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/taskrun/list/list.go @@ -0,0 +1,89 @@ +// Copyright © 2019 The Tekton Authors. +// +// 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. + +package list + +import ( + "context" + "fmt" + "os" + + "github.com/tektoncd/cli/pkg/actions" + "github.com/tektoncd/cli/pkg/cli" + "github.com/tektoncd/cli/pkg/formatted" + trsort "github.com/tektoncd/cli/pkg/taskrun/sort" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func GetAllTaskRuns(p cli.Params, opts metav1.ListOptions, limit int) ([]string, error) { + cs, err := p.Clients() + if err != nil { + return nil, err + } + + runs, err := TaskRuns(cs, opts, p.Namespace()) + if err != nil { + return nil, err + } + + trsort.SortByStartTime(runs.Items) + runslen := len(runs.Items) + if limit > runslen { + limit = runslen + } + + ret := []string{} + for i, run := range runs.Items { + if i < limit { + ret = append(ret, run.ObjectMeta.Name+" started "+formatted.Age(run.Status.StartTime, p.Time())) + } + } + return ret, nil +} + +func TaskRuns(c *cli.Clients, opts metav1.ListOptions, ns string) (*v1beta1.TaskRunList, error) { + + trGroupResource := schema.GroupVersionResource{Group: "tekton.dev", Resource: "taskruns"} + unstructuredTRL, err := actions.List(trGroupResource, c.Dynamic, c.Tekton.Discovery(), ns, opts) + if err != nil { + return nil, err + } + + for i, tr := range unstructuredTRL.Items { + if tr.GroupVersionKind().Version == "v1alpha1" { + var v1alpha1TR *v1alpha1.TaskRun + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(tr.UnstructuredContent(), &v1alpha1TR); err != nil { + return nil, err + } + updatedTaskRun := v1beta1.TaskRun{} + err := v1alpha1TR.ConvertTo(context.Background(), &updatedTaskRun) + if err != nil { + fmt.Fprintln(os.Stdout, err) + } + object, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(&updatedTaskRun) + unstructuredTRL.Items[i] = unstructured.Unstructured{Object: object} + } + } + var runs *v1beta1.TaskRunList + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredTRL.UnstructuredContent(), &runs); err != nil { + return nil, err + } + + return runs, nil +} diff --git a/vendor/github.com/tektoncd/cli/pkg/taskrun/patch.go b/vendor/github.com/tektoncd/cli/pkg/taskrun/patch.go new file mode 100644 index 0000000000..932a9ac213 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/taskrun/patch.go @@ -0,0 +1,52 @@ +// Copyright © 2019-2020 The Tekton Authors. +// +// 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. + +package taskrun + +import ( + "encoding/json" + + "github.com/tektoncd/cli/pkg/actions" + "github.com/tektoncd/cli/pkg/cli" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +type patchStringValue struct { + Op string `json:"op"` + Path string `json:"path"` + Value string `json:"value"` +} + +func Patch(c *cli.Clients, trname string, opts metav1.PatchOptions, ns string) (*v1beta1.TaskRun, error) { + payload := []patchStringValue{{ + Op: "replace", + Path: "/spec/status", + Value: v1beta1.TaskRunSpecStatusCancelled, + }} + + data, _ := json.Marshal(payload) + unstructuredTR, err := actions.Patch(trGroupResource, c, trname, data, opts, ns) + if err != nil { + return nil, err + } + + var taskrun *v1beta1.TaskRun + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredTR.UnstructuredContent(), &taskrun); err != nil { + return nil, err + } + + return taskrun, nil +} diff --git a/vendor/github.com/tektoncd/cli/pkg/taskrun/sort/by_namespace.go b/vendor/github.com/tektoncd/cli/pkg/taskrun/sort/by_namespace.go new file mode 100644 index 0000000000..204b64887d --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/taskrun/sort/by_namespace.go @@ -0,0 +1,48 @@ +// Copyright © 2020 The Tekton Authors. +// +// 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. + +package taskrun + +import ( + "sort" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" +) + +func SortByNamespace(trs []v1beta1.TaskRun) { + sort.Sort(byNamespace(trs)) +} + +type byNamespace []v1beta1.TaskRun + +func (trs byNamespace) compareNamespace(ins, jns string) (lt, eq bool) { + lt, eq = ins < jns, ins == jns + return lt, eq +} + +func (trs byNamespace) Len() int { return len(trs) } +func (trs byNamespace) Swap(i, j int) { trs[i], trs[j] = trs[j], trs[i] } +func (trs byNamespace) Less(i, j int) bool { + var lt, eq bool + if lt, eq = trs.compareNamespace(trs[i].Namespace, trs[j].Namespace); eq { + if trs[j].Status.StartTime == nil { + return false + } + if trs[i].Status.StartTime == nil { + return true + } + return trs[j].Status.StartTime.Before(trs[i].Status.StartTime) + } + return lt +} diff --git a/vendor/github.com/tektoncd/cli/pkg/taskrun/sort/by_start_time.go b/vendor/github.com/tektoncd/cli/pkg/taskrun/sort/by_start_time.go new file mode 100644 index 0000000000..ab2a14ad54 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/taskrun/sort/by_start_time.go @@ -0,0 +1,39 @@ +// Copyright © 2020 The Tekton Authors. +// +// 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. + +package taskrun + +import ( + "sort" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" +) + +func SortByStartTime(trs []v1beta1.TaskRun) { + sort.Sort(byStartTime(trs)) +} + +type byStartTime []v1beta1.TaskRun + +func (trs byStartTime) Len() int { return len(trs) } +func (trs byStartTime) Swap(i, j int) { trs[i], trs[j] = trs[j], trs[i] } +func (trs byStartTime) Less(i, j int) bool { + if trs[j].Status.StartTime == nil { + return false + } + if trs[i].Status.StartTime == nil { + return true + } + return trs[j].Status.StartTime.Before(trs[i].Status.StartTime) +} diff --git a/vendor/github.com/tektoncd/cli/pkg/taskrun/taskrun.go b/vendor/github.com/tektoncd/cli/pkg/taskrun/taskrun.go new file mode 100644 index 0000000000..0e2ec2e007 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/taskrun/taskrun.go @@ -0,0 +1,109 @@ +// Copyright © 2019 The Tekton Authors. +// +// 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. + +package taskrun + +import ( + "sort" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var trGroupResource = schema.GroupVersionResource{Group: "tekton.dev", Resource: "taskruns"} + +type Run struct { + Name string + Task string + Retries int + StartTime *metav1.Time + CompletionTime *metav1.Time +} + +type Runs []Run + +func (r Runs) Len() int { + return len(r) +} + +func (r Runs) Less(i, j int) bool { + if r[i].CompletionTime != nil && r[j].CompletionTime != nil { + return r[i].CompletionTime.Before(r[j].CompletionTime) + } + return r[i].StartTime.Before(r[j].StartTime) +} + +func (r Runs) Swap(i, j int) { + r[i], r[j] = r[j], r[i] +} + +func IsFiltered(tr Run, allowed []string) bool { + trs := []Run{tr} + return len(Filter(trs, allowed)) == 0 +} + +func HasScheduled(trs *v1alpha1.PipelineRunTaskRunStatus) bool { + if trs.Status != nil { + return trs.Status.PodName != "" + } + return false +} + +func Filter(trs []Run, ts []string) []Run { + if len(ts) == 0 { + return trs + } + + filter := map[string]bool{} + for _, t := range ts { + filter[t] = true + } + + filtered := []Run{} + for _, tr := range trs { + if filter[tr.Task] { + filtered = append(filtered, tr) + } + } + + return filtered +} + +type taskRunMap map[string]*v1beta1.PipelineRunTaskRunStatus + +func SortTasksBySpecOrder(pipelineTasks []v1beta1.PipelineTask, pipelinesTaskRuns taskRunMap) []Run { + trNames := map[string]string{} + + for name, t := range pipelinesTaskRuns { + trNames[t.PipelineTaskName] = name + } + trs := Runs{} + + for _, ts := range pipelineTasks { + if n, ok := trNames[ts.Name]; ok { + trStatusFields := pipelinesTaskRuns[n].Status.TaskRunStatusFields + trs = append(trs, Run{ + Task: ts.Name, + Name: n, + Retries: ts.Retries, + StartTime: trStatusFields.StartTime, + CompletionTime: trStatusFields.CompletionTime, + }) + } + } + sort.Sort(trs) + return trs +} diff --git a/vendor/github.com/tektoncd/cli/pkg/taskrun/watch.go b/vendor/github.com/tektoncd/cli/pkg/taskrun/watch.go new file mode 100644 index 0000000000..227a10cd80 --- /dev/null +++ b/vendor/github.com/tektoncd/cli/pkg/taskrun/watch.go @@ -0,0 +1,17 @@ +package taskrun + +import ( + "github.com/tektoncd/cli/pkg/actions" + "github.com/tektoncd/cli/pkg/cli" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" +) + +func Watch(c *cli.Clients, opts metav1.ListOptions, ns string) (watch.Interface, error) { + watch, err := actions.Watch(trGroupResource, c, ns, opts) + if err != nil { + return nil, err + } + + return watch, nil +} diff --git a/vendor/github.com/tektoncd/pipeline/LICENSE b/vendor/github.com/tektoncd/pipeline/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/config/artifact_bucket.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/config/artifact_bucket.go new file mode 100644 index 0000000000..abdfc6e7d8 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/config/artifact_bucket.go @@ -0,0 +1,112 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package config + +import ( + "os" + + corev1 "k8s.io/api/core/v1" +) + +const ( + // BucketLocationKey is the name of the configmap entry that specifies + // loction of the bucket. + BucketLocationKey = "location" + + // BucketServiceAccountSecretNameKey is the name of the configmap entry that specifies + // the name of the secret that will provide the servie account with bucket access. + // This secret must have a key called serviceaccount that will have a value with + // the service account with access to the bucket + BucketServiceAccountSecretNameKey = "bucket.service.account.secret.name" + + // BucketServiceAccountSecretKeyKey is the name of the configmap entry that specifies + // the secret key that will have a value with the service account json with access + // to the bucket + BucketServiceAccountSecretKeyKey = "bucket.service.account.secret.key" + + // DefaultBucketServiceFieldName defaults to a gcs bucket + DefaultBucketServiceFieldName = "GOOGLE_APPLICATION_CREDENTIALS" + + // BucketServiceAccountFieldNameKey is the name of the configmap entry that specifies + // the field name that should be used for the service account. + // Valid values: GOOGLE_APPLICATION_CREDENTIALS, BOTO_CONFIG. + BucketServiceAccountFieldNameKey = "bucket.service.account.field.name" +) + +// ArtifactBucket holds the configurations for the artifacts PVC +// +k8s:deepcopy-gen=true +type ArtifactBucket struct { + Location string + ServiceAccountSecretName string + ServiceAccountSecretKey string + ServiceAccountFieldName string +} + +// GetArtifactBucketConfigName returns the name of the configmap containing all +// customizations for the storage bucket. +func GetArtifactBucketConfigName() string { + if e := os.Getenv("CONFIG_ARTIFACT_BUCKET_NAME"); e != "" { + return e + } + return "config-artifact-bucket" +} + +// Equals returns true if two Configs are identical +func (cfg *ArtifactBucket) Equals(other *ArtifactBucket) bool { + if cfg == nil && other == nil { + return true + } + + if cfg == nil || other == nil { + return false + } + + return other.Location == cfg.Location && + other.ServiceAccountSecretName == cfg.ServiceAccountSecretName && + other.ServiceAccountSecretKey == cfg.ServiceAccountSecretKey && + other.ServiceAccountFieldName == cfg.ServiceAccountFieldName +} + +// NewArtifactBucketFromMap returns a Config given a map corresponding to a ConfigMap +func NewArtifactBucketFromMap(cfgMap map[string]string) (*ArtifactBucket, error) { + tc := ArtifactBucket{ + ServiceAccountFieldName: DefaultBucketServiceFieldName, + } + + if location, ok := cfgMap[BucketLocationKey]; ok { + tc.Location = location + } + + if serviceAccountSecretName, ok := cfgMap[BucketServiceAccountSecretNameKey]; ok { + tc.ServiceAccountSecretName = serviceAccountSecretName + } + + if serviceAccountSecretKey, ok := cfgMap[BucketServiceAccountSecretKeyKey]; ok { + tc.ServiceAccountSecretKey = serviceAccountSecretKey + } + + if serviceAccountFieldName, ok := cfgMap[BucketServiceAccountFieldNameKey]; ok { + tc.ServiceAccountFieldName = serviceAccountFieldName + } + + return &tc, nil +} + +// NewArtifactBucketFromConfigMap returns a Config for the given configmap +func NewArtifactBucketFromConfigMap(config *corev1.ConfigMap) (*ArtifactBucket, error) { + return NewArtifactBucketFromMap(config.Data) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/config/artifact_pvc.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/config/artifact_pvc.go new file mode 100644 index 0000000000..5434353d39 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/config/artifact_pvc.go @@ -0,0 +1,86 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package config + +import ( + "os" + + corev1 "k8s.io/api/core/v1" +) + +const ( + // DefaultPVCSize is the default size of the PVC to create + DefaultPVCSize = "5Gi" + + // PVCSizeKey is the name of the configmap entry that specifies the size of the PVC to create + PVCSizeKey = "size" + + // PVCStorageClassNameKey is the name of the configmap entry that specifies the storage class of the PVC to create + PVCStorageClassNameKey = "storageClassName" +) + +// ArtifactPVC holds the configurations for the artifacts PVC +// +k8s:deepcopy-gen=true +type ArtifactPVC struct { + Size string + StorageClassName string +} + +// GetArtifactPVCConfigName returns the name of the configmap containing all +// customizations for the storage PVC. +func GetArtifactPVCConfigName() string { + if e := os.Getenv("CONFIG_ARTIFACT_PVC_NAME"); e != "" { + return e + } + return "config-artifact-pvc" +} + +// Equals returns true if two Configs are identical +func (cfg *ArtifactPVC) Equals(other *ArtifactPVC) bool { + if cfg == nil && other == nil { + return true + } + + if cfg == nil || other == nil { + return false + } + + return other.Size == cfg.Size && + other.StorageClassName == cfg.StorageClassName +} + +// NewArtifactPVCFromMap returns a Config given a map corresponding to a ConfigMap +func NewArtifactPVCFromMap(cfgMap map[string]string) (*ArtifactPVC, error) { + tc := ArtifactPVC{ + Size: DefaultPVCSize, + } + + if size, ok := cfgMap[PVCSizeKey]; ok { + tc.Size = size + } + + if storageClassName, ok := cfgMap[PVCStorageClassNameKey]; ok { + tc.StorageClassName = storageClassName + } + + return &tc, nil +} + +// NewArtifactPVCFromConfigMap returns a Config for the given configmap +func NewArtifactPVCFromConfigMap(config *corev1.ConfigMap) (*ArtifactPVC, error) { + return NewArtifactPVCFromMap(config.Data) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/config/default.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/config/default.go new file mode 100644 index 0000000000..2b263d3eec --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/config/default.go @@ -0,0 +1,134 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package config + +import ( + "fmt" + "os" + "strconv" + "time" + + "github.com/ghodss/yaml" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/pod" + corev1 "k8s.io/api/core/v1" +) + +const ( + // DefaultTimeoutMinutes is used when no timeout is specified. + DefaultTimeoutMinutes = 60 + // NoTimeoutDuration is used when a pipeline or task should never time out. + NoTimeoutDuration = 0 * time.Minute + // DefaultServiceAccountValue is the SA used when one is not specified. + DefaultServiceAccountValue = "default" + // DefaultManagedByLabelValue is the value for the managed-by label that is used by default. + DefaultManagedByLabelValue = "tekton-pipelines" + // DefaultCloudEventSinkValue is the default value for cloud event sinks. + DefaultCloudEventSinkValue = "" + + defaultTimeoutMinutesKey = "default-timeout-minutes" + defaultServiceAccountKey = "default-service-account" + defaultManagedByLabelValueKey = "default-managed-by-label-value" + defaultPodTemplateKey = "default-pod-template" + defaultCloudEventsSinkKey = "default-cloud-events-sink" + defaultTaskRunWorkspaceBinding = "default-task-run-workspace-binding" +) + +// Defaults holds the default configurations +// +k8s:deepcopy-gen=true +type Defaults struct { + DefaultTimeoutMinutes int + DefaultServiceAccount string + DefaultManagedByLabelValue string + DefaultPodTemplate *pod.Template + DefaultCloudEventsSink string + DefaultTaskRunWorkspaceBinding string +} + +// GetDefaultsConfigName returns the name of the configmap containing all +// defined defaults. +func GetDefaultsConfigName() string { + if e := os.Getenv("CONFIG_DEFAULTS_NAME"); e != "" { + return e + } + return "config-defaults" +} + +// Equals returns true if two Configs are identical +func (cfg *Defaults) Equals(other *Defaults) bool { + if cfg == nil && other == nil { + return true + } + + if cfg == nil || other == nil { + return false + } + + return other.DefaultTimeoutMinutes == cfg.DefaultTimeoutMinutes && + other.DefaultServiceAccount == cfg.DefaultServiceAccount && + other.DefaultManagedByLabelValue == cfg.DefaultManagedByLabelValue && + other.DefaultPodTemplate.Equals(cfg.DefaultPodTemplate) && + other.DefaultCloudEventsSink == cfg.DefaultCloudEventsSink && + other.DefaultTaskRunWorkspaceBinding == cfg.DefaultTaskRunWorkspaceBinding +} + +// NewDefaultsFromMap returns a Config given a map corresponding to a ConfigMap +func NewDefaultsFromMap(cfgMap map[string]string) (*Defaults, error) { + tc := Defaults{ + DefaultTimeoutMinutes: DefaultTimeoutMinutes, + DefaultServiceAccount: DefaultServiceAccountValue, + DefaultManagedByLabelValue: DefaultManagedByLabelValue, + DefaultCloudEventsSink: DefaultCloudEventSinkValue, + } + + if defaultTimeoutMin, ok := cfgMap[defaultTimeoutMinutesKey]; ok { + timeout, err := strconv.ParseInt(defaultTimeoutMin, 10, 0) + if err != nil { + return nil, fmt.Errorf("failed parsing tracing config %q", defaultTimeoutMinutesKey) + } + tc.DefaultTimeoutMinutes = int(timeout) + } + + if defaultServiceAccount, ok := cfgMap[defaultServiceAccountKey]; ok { + tc.DefaultServiceAccount = defaultServiceAccount + } + + if defaultManagedByLabelValue, ok := cfgMap[defaultManagedByLabelValueKey]; ok { + tc.DefaultManagedByLabelValue = defaultManagedByLabelValue + } + + if defaultPodTemplate, ok := cfgMap[defaultPodTemplateKey]; ok { + var podTemplate pod.Template + if err := yaml.Unmarshal([]byte(defaultPodTemplate), &podTemplate); err != nil { + return nil, fmt.Errorf("failed to unmarshal %v", defaultPodTemplate) + } + tc.DefaultPodTemplate = &podTemplate + } + + if defaultCloudEventsSink, ok := cfgMap[defaultCloudEventsSinkKey]; ok { + tc.DefaultCloudEventsSink = defaultCloudEventsSink + } + + if bindingYAML, ok := cfgMap[defaultTaskRunWorkspaceBinding]; ok { + tc.DefaultTaskRunWorkspaceBinding = bindingYAML + } + return &tc, nil +} + +// NewDefaultsFromConfigMap returns a Config for the given configmap +func NewDefaultsFromConfigMap(config *corev1.ConfigMap) (*Defaults, error) { + return NewDefaultsFromMap(config.Data) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/config/doc.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/config/doc.go new file mode 100644 index 0000000000..98c3e77f14 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/config/doc.go @@ -0,0 +1,17 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package config diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/config/feature_flags.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/config/feature_flags.go new file mode 100644 index 0000000000..dab18dad45 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/config/feature_flags.go @@ -0,0 +1,163 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package config + +import ( + "fmt" + "os" + "strconv" + "strings" + + corev1 "k8s.io/api/core/v1" +) + +const ( + // StableAPIFields is the value used for "enable-api-fields" when only stable APIs should be usable. + StableAPIFields = "stable" + // AlphaAPIFields is the value used for "enable-api-fields" when alpha APIs should be usable as well. + AlphaAPIFields = "alpha" + // DefaultDisableAffinityAssistant is the default value for "disable-affinity-assistant". + DefaultDisableAffinityAssistant = false + // DefaultDisableCredsInit is the default value for "disable-creds-init". + DefaultDisableCredsInit = false + // DefaultRunningInEnvWithInjectedSidecars is the default value for "running-in-environment-with-injected-sidecars". + DefaultRunningInEnvWithInjectedSidecars = true + // DefaultRequireGitSSHSecretKnownHosts is the default value for "require-git-ssh-secret-known-hosts". + DefaultRequireGitSSHSecretKnownHosts = false + // DefaultEnableTektonOciBundles is the default value for "enable-tekton-oci-bundles". + DefaultEnableTektonOciBundles = false + // DefaultEnableCustomTasks is the default value for "enable-custom-tasks". + DefaultEnableCustomTasks = false + // DefaultScopeWhenExpressionsToTask is the default value for "scope-when-expressions-to-task". + DefaultScopeWhenExpressionsToTask = true + // DefaultEnableAPIFields is the default value for "enable-api-fields". + DefaultEnableAPIFields = StableAPIFields + // DefaultSendCloudEventsForRuns is the default value for "send-cloudevents-for-runs". + DefaultSendCloudEventsForRuns = false + + disableAffinityAssistantKey = "disable-affinity-assistant" + disableCredsInitKey = "disable-creds-init" + runningInEnvWithInjectedSidecarsKey = "running-in-environment-with-injected-sidecars" + requireGitSSHSecretKnownHostsKey = "require-git-ssh-secret-known-hosts" // nolint: gosec + enableTektonOCIBundles = "enable-tekton-oci-bundles" + enableCustomTasks = "enable-custom-tasks" + enableAPIFields = "enable-api-fields" + scopeWhenExpressionsToTask = "scope-when-expressions-to-task" + sendCloudEventsForRuns = "send-cloudevents-for-runs" +) + +// FeatureFlags holds the features configurations +// +k8s:deepcopy-gen=true +type FeatureFlags struct { + DisableAffinityAssistant bool + DisableCredsInit bool + RunningInEnvWithInjectedSidecars bool + RequireGitSSHSecretKnownHosts bool + EnableTektonOCIBundles bool + EnableCustomTasks bool + ScopeWhenExpressionsToTask bool + EnableAPIFields string + SendCloudEventsForRuns bool +} + +// GetFeatureFlagsConfigName returns the name of the configmap containing all +// feature flags. +func GetFeatureFlagsConfigName() string { + if e := os.Getenv("CONFIG_FEATURE_FLAGS_NAME"); e != "" { + return e + } + return "feature-flags" +} + +// NewFeatureFlagsFromMap returns a Config given a map corresponding to a ConfigMap +func NewFeatureFlagsFromMap(cfgMap map[string]string) (*FeatureFlags, error) { + setFeature := func(key string, defaultValue bool, feature *bool) error { + if cfg, ok := cfgMap[key]; ok { + value, err := strconv.ParseBool(cfg) + if err != nil { + return fmt.Errorf("failed parsing feature flags config %q: %v", cfg, err) + } + *feature = value + return nil + } + *feature = defaultValue + return nil + } + + tc := FeatureFlags{} + if err := setFeature(disableAffinityAssistantKey, DefaultDisableAffinityAssistant, &tc.DisableAffinityAssistant); err != nil { + return nil, err + } + if err := setFeature(disableCredsInitKey, DefaultDisableCredsInit, &tc.DisableCredsInit); err != nil { + return nil, err + } + if err := setFeature(runningInEnvWithInjectedSidecarsKey, DefaultRunningInEnvWithInjectedSidecars, &tc.RunningInEnvWithInjectedSidecars); err != nil { + return nil, err + } + if err := setFeature(requireGitSSHSecretKnownHostsKey, DefaultRequireGitSSHSecretKnownHosts, &tc.RequireGitSSHSecretKnownHosts); err != nil { + return nil, err + } + if err := setFeature(scopeWhenExpressionsToTask, DefaultScopeWhenExpressionsToTask, &tc.ScopeWhenExpressionsToTask); err != nil { + return nil, err + } + if err := setEnabledAPIFields(cfgMap, DefaultEnableAPIFields, &tc.EnableAPIFields); err != nil { + return nil, err + } + if err := setFeature(sendCloudEventsForRuns, DefaultSendCloudEventsForRuns, &tc.SendCloudEventsForRuns); err != nil { + return nil, err + } + + // Given that they are alpha features, Tekton Bundles and Custom Tasks should be switched on if + // enable-api-fields is "alpha". If enable-api-fields is not "alpha" then fall back to the value of + // each feature's individual flag. + // + // Note: the user cannot enable "alpha" while disabling bundles or custom tasks - that would + // defeat the purpose of having a single shared gate for all alpha features. + if tc.EnableAPIFields == AlphaAPIFields { + tc.EnableTektonOCIBundles = true + tc.EnableCustomTasks = true + } else { + if err := setFeature(enableTektonOCIBundles, DefaultEnableTektonOciBundles, &tc.EnableTektonOCIBundles); err != nil { + return nil, err + } + if err := setFeature(enableCustomTasks, DefaultEnableCustomTasks, &tc.EnableCustomTasks); err != nil { + return nil, err + } + } + return &tc, nil +} + +// setEnabledAPIFields sets the "enable-api-fields" flag based on the content of a given map. +// If the feature gate is invalid or missing then an error is returned. +func setEnabledAPIFields(cfgMap map[string]string, defaultValue string, feature *string) error { + value := defaultValue + if cfg, ok := cfgMap[enableAPIFields]; ok { + value = strings.ToLower(cfg) + } + switch value { + case AlphaAPIFields, StableAPIFields: + *feature = value + default: + return fmt.Errorf("invalid value for feature flag %q: %q", enableAPIFields, value) + } + return nil +} + +// NewFeatureFlagsFromConfigMap returns a Config for the given configmap +func NewFeatureFlagsFromConfigMap(config *corev1.ConfigMap) (*FeatureFlags, error) { + return NewFeatureFlagsFromMap(config.Data) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/config/metrics.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/config/metrics.go new file mode 100644 index 0000000000..43471fd574 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/config/metrics.go @@ -0,0 +1,144 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package config + +import ( + corev1 "k8s.io/api/core/v1" + "knative.dev/pkg/metrics" +) + +const ( + // metricsTaskrunLevel determines to what level to aggregate metrics + // for taskrun + metricsTaskrunLevelKey = "metrics.taskrun.level" + + // metricsPipelinerunLevel determines to what level to aggregate metrics + // for pipelinerun + metricsPipelinerunLevelKey = "metrics.pipelinerun.level" + // metricsDurationTaskrunType determines what type of + // metrics to use for aggregating duration for taskrun + metricsDurationTaskrunType = "metrics.taskrun.duration-type" + // metricsDurationPipelinerunType determines what type of + // metrics to use for aggregating duration for pipelinerun + metricsDurationPipelinerunType = "metrics.pipelinerun.duration-type" + + // DefaultTaskrunLevel determines to what level to aggregate metrics + // when it isn't specified in configmap + DefaultTaskrunLevel = TaskrunLevelAtTask + // TaskrunLevelAtTaskrun specify that aggregation will be done at + // taskrun level + TaskrunLevelAtTaskrun = "taskrun" + // TaskrunLevelAtTask specify that aggregation will be done at task level + TaskrunLevelAtTask = "task" + // TaskrunLevelAtNS specify that aggregation will be done at namespace level + TaskrunLevelAtNS = "namespace" + // DefaultPipelinerunLevel determines to what level to aggregate metrics + // when it isn't specified in configmap + DefaultPipelinerunLevel = PipelinerunLevelAtPipeline + // PipelinerunLevelAtPipelinerun specify that aggregation will be done at + // pipelienrun level + PipelinerunLevelAtPipelinerun = "pipelinerun" + // PipelinerunLevelAtPipeline specify that aggregation will be done at + // pipeline level + PipelinerunLevelAtPipeline = "pipeline" + // PipelinerunLevelAtNS specify that aggregation will be done at + // namespace level + PipelinerunLevelAtNS = "namespace" + + // DefaultDurationTaskrunType determines what type + // of metrics to use when we don't specify one in + // configmap + DefaultDurationTaskrunType = "histogram" + // DurationTaskrunTypeHistogram specify that histogram + // type metrics need to be use for Duration of Taskrun + DurationTaskrunTypeHistogram = "histogram" + // DurationTaskrunTypeLastValue specify that lastValue or + // gauge type metrics need to be use for Duration of Taskrun + DurationTaskrunTypeLastValue = "lastvalue" + + // DefaultDurationPipelinerunType determines what type + // of metrics to use when we don't specify one in + // configmap + DefaultDurationPipelinerunType = "histogram" + // DurationPipelinerunTypeHistogram specify that histogram + // type metrics need to be use for Duration of Pipelinerun + DurationPipelinerunTypeHistogram = "histogram" + // DurationPipelinerunTypeLastValue specify that lastValue or + // gauge type metrics need to be use for Duration of Pipelinerun + DurationPipelinerunTypeLastValue = "lastvalue" +) + +// Metrics holds the configurations for the metrics +// +k8s:deepcopy-gen=true +type Metrics struct { + TaskrunLevel string + PipelinerunLevel string + DurationTaskrunType string + DurationPipelinerunType string +} + +// GetMetricsConfigName returns the name of the configmap containing all +// customizations for the storage bucket. +func GetMetricsConfigName() string { + return metrics.ConfigMapName() +} + +// Equals returns true if two Configs are identical +func (cfg *Metrics) Equals(other *Metrics) bool { + if cfg == nil && other == nil { + return true + } + + if cfg == nil || other == nil { + return false + } + + return other.TaskrunLevel == cfg.TaskrunLevel && + other.PipelinerunLevel == cfg.PipelinerunLevel && + other.DurationTaskrunType == cfg.DurationTaskrunType && + other.DurationPipelinerunType == cfg.DurationPipelinerunType +} + +// newMetricsFromMap returns a Config given a map corresponding to a ConfigMap +func newMetricsFromMap(cfgMap map[string]string) (*Metrics, error) { + tc := Metrics{ + TaskrunLevel: DefaultTaskrunLevel, + PipelinerunLevel: DefaultPipelinerunLevel, + DurationTaskrunType: DefaultDurationTaskrunType, + DurationPipelinerunType: DefaultDurationPipelinerunType, + } + + if taskrunLevel, ok := cfgMap[metricsTaskrunLevelKey]; ok { + tc.TaskrunLevel = taskrunLevel + } + + if pipelinerunLevel, ok := cfgMap[metricsPipelinerunLevelKey]; ok { + tc.PipelinerunLevel = pipelinerunLevel + } + if durationTaskrun, ok := cfgMap[metricsDurationTaskrunType]; ok { + tc.DurationTaskrunType = durationTaskrun + } + if durationPipelienrun, ok := cfgMap[metricsDurationPipelinerunType]; ok { + tc.DurationPipelinerunType = durationPipelienrun + } + return &tc, nil +} + +// NewMetricsFromConfigMap returns a Config for the given configmap +func NewMetricsFromConfigMap(config *corev1.ConfigMap) (*Metrics, error) { + return newMetricsFromMap(config.Data) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/config/store.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/config/store.go new file mode 100644 index 0000000000..6bf1b56b95 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/config/store.go @@ -0,0 +1,133 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package config + +import ( + "context" + + "knative.dev/pkg/configmap" +) + +type cfgKey struct{} + +// Config holds the collection of configurations that we attach to contexts. +// +k8s:deepcopy-gen=false +type Config struct { + Defaults *Defaults + FeatureFlags *FeatureFlags + ArtifactBucket *ArtifactBucket + ArtifactPVC *ArtifactPVC + Metrics *Metrics +} + +// FromContext extracts a Config from the provided context. +func FromContext(ctx context.Context) *Config { + x, ok := ctx.Value(cfgKey{}).(*Config) + if ok { + return x + } + return nil +} + +// FromContextOrDefaults is like FromContext, but when no Config is attached it +// returns a Config populated with the defaults for each of the Config fields. +func FromContextOrDefaults(ctx context.Context) *Config { + if cfg := FromContext(ctx); cfg != nil { + return cfg + } + defaults, _ := NewDefaultsFromMap(map[string]string{}) + featureFlags, _ := NewFeatureFlagsFromMap(map[string]string{}) + artifactBucket, _ := NewArtifactBucketFromMap(map[string]string{}) + artifactPVC, _ := NewArtifactPVCFromMap(map[string]string{}) + metrics, _ := newMetricsFromMap(map[string]string{}) + return &Config{ + Defaults: defaults, + FeatureFlags: featureFlags, + ArtifactBucket: artifactBucket, + ArtifactPVC: artifactPVC, + Metrics: metrics, + } +} + +// ToContext attaches the provided Config to the provided context, returning the +// new context with the Config attached. +func ToContext(ctx context.Context, c *Config) context.Context { + return context.WithValue(ctx, cfgKey{}, c) +} + +// Store is a typed wrapper around configmap.Untyped store to handle our configmaps. +// +k8s:deepcopy-gen=false +type Store struct { + *configmap.UntypedStore +} + +// NewStore creates a new store of Configs and optionally calls functions when ConfigMaps are updated. +func NewStore(logger configmap.Logger, onAfterStore ...func(name string, value interface{})) *Store { + store := &Store{ + UntypedStore: configmap.NewUntypedStore( + "defaults/features/artifacts", + logger, + configmap.Constructors{ + GetDefaultsConfigName(): NewDefaultsFromConfigMap, + GetFeatureFlagsConfigName(): NewFeatureFlagsFromConfigMap, + GetArtifactBucketConfigName(): NewArtifactBucketFromConfigMap, + GetArtifactPVCConfigName(): NewArtifactPVCFromConfigMap, + GetMetricsConfigName(): NewMetricsFromConfigMap, + }, + onAfterStore..., + ), + } + + return store +} + +// ToContext attaches the current Config state to the provided context. +func (s *Store) ToContext(ctx context.Context) context.Context { + return ToContext(ctx, s.Load()) +} + +// Load creates a Config from the current config state of the Store. +func (s *Store) Load() *Config { + defaults := s.UntypedLoad(GetDefaultsConfigName()) + if defaults == nil { + defaults, _ = NewDefaultsFromMap(map[string]string{}) + } + featureFlags := s.UntypedLoad(GetFeatureFlagsConfigName()) + if featureFlags == nil { + featureFlags, _ = NewFeatureFlagsFromMap(map[string]string{}) + } + artifactBucket := s.UntypedLoad(GetArtifactBucketConfigName()) + if artifactBucket == nil { + artifactBucket, _ = NewArtifactBucketFromMap(map[string]string{}) + } + artifactPVC := s.UntypedLoad(GetArtifactPVCConfigName()) + if artifactPVC == nil { + artifactPVC, _ = NewArtifactPVCFromMap(map[string]string{}) + } + + metrics := s.UntypedLoad(GetMetricsConfigName()) + if metrics == nil { + metrics, _ = newMetricsFromMap(map[string]string{}) + } + return &Config{ + Defaults: defaults.(*Defaults).DeepCopy(), + FeatureFlags: featureFlags.(*FeatureFlags).DeepCopy(), + ArtifactBucket: artifactBucket.(*ArtifactBucket).DeepCopy(), + ArtifactPVC: artifactPVC.(*ArtifactPVC).DeepCopy(), + Metrics: metrics.(*Metrics).DeepCopy(), + } +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/config/zz_generated.deepcopy.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/config/zz_generated.deepcopy.go new file mode 100644 index 0000000000..2a269a49bb --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/config/zz_generated.deepcopy.go @@ -0,0 +1,111 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package config + +import ( + pod "github.com/tektoncd/pipeline/pkg/apis/pipeline/pod" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ArtifactBucket) DeepCopyInto(out *ArtifactBucket) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArtifactBucket. +func (in *ArtifactBucket) DeepCopy() *ArtifactBucket { + if in == nil { + return nil + } + out := new(ArtifactBucket) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ArtifactPVC) DeepCopyInto(out *ArtifactPVC) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArtifactPVC. +func (in *ArtifactPVC) DeepCopy() *ArtifactPVC { + if in == nil { + return nil + } + out := new(ArtifactPVC) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Defaults) DeepCopyInto(out *Defaults) { + *out = *in + if in.DefaultPodTemplate != nil { + in, out := &in.DefaultPodTemplate, &out.DefaultPodTemplate + *out = new(pod.Template) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Defaults. +func (in *Defaults) DeepCopy() *Defaults { + if in == nil { + return nil + } + out := new(Defaults) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FeatureFlags) DeepCopyInto(out *FeatureFlags) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeatureFlags. +func (in *FeatureFlags) DeepCopy() *FeatureFlags { + if in == nil { + return nil + } + out := new(FeatureFlags) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Metrics) DeepCopyInto(out *Metrics) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Metrics. +func (in *Metrics) DeepCopy() *Metrics { + if in == nil { + return nil + } + out := new(Metrics) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/controller.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/controller.go new file mode 100644 index 0000000000..abeb5e3cf9 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/controller.go @@ -0,0 +1,39 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package pipeline + +const ( + // PipelineRunControllerName holds the name of the PipelineRun controller + // nolint: revive + PipelineRunControllerName = "PipelineRun" + + // PipelineControllerName holds the name of the Pipeline controller + // nolint: revive + PipelineControllerName = "Pipeline" + + // TaskRunControllerName holds the name of the TaskRun controller + TaskRunControllerName = "TaskRun" + + // TaskControllerName holds the name of the Task controller + TaskControllerName = "Task" + + // ClusterTaskControllerName holds the name of the Task controller + ClusterTaskControllerName = "ClusterTask" + + // RunControllerName holds the name of the Custom Task controller + RunControllerName = "Run" +) diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/images.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/images.go new file mode 100644 index 0000000000..e40ebfe563 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/images.go @@ -0,0 +1,77 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package pipeline + +import ( + "fmt" + "sort" +) + +// Images holds the images reference for a number of container images used +// across tektoncd pipelines. +type Images struct { + // EntrypointImage is container image containing our entrypoint binary. + EntrypointImage string + // NopImage is the container image used to kill sidecars. + NopImage string + // GitImage is the container image with Git that we use to implement the Git source step. + GitImage string + // KubeconfigWriterImage is the container image containing our kubeconfig writer binary. + KubeconfigWriterImage string + // ShellImage is the container image containing bash shell. + ShellImage string + // ShellImageWin is the container image containing powershell. + ShellImageWin string + // GsutilImage is the container image containing gsutil. + GsutilImage string + // PRImage is the container image that we use to implement the PR source step. + PRImage string + // ImageDigestExporterImage is the container image containing our image digest exporter binary. + ImageDigestExporterImage string + // WorkingDirInitImage is the container image containing our working dir init binary. + WorkingDirInitImage string + + // NOTE: Make sure to add any new images to Validate below! +} + +// Validate returns an error if any image is not set. +func (i Images) Validate() error { + var unset []string + for _, f := range []struct { + v, name string + }{ + {i.EntrypointImage, "entrypoint-image"}, + {i.NopImage, "nop-image"}, + {i.GitImage, "git-image"}, + {i.KubeconfigWriterImage, "kubeconfig-writer-image"}, + {i.ShellImage, "shell-image"}, + {i.ShellImageWin, "shell-image-win"}, + {i.GsutilImage, "gsutil-image"}, + {i.PRImage, "pr-image"}, + {i.ImageDigestExporterImage, "imagedigest-exporter-image"}, + {i.WorkingDirInitImage, "workingdirinit-image"}, + } { + if f.v == "" { + unset = append(unset, f.name) + } + } + if len(unset) > 0 { + sort.Strings(unset) + return fmt.Errorf("found unset image flags: %s", unset) + } + return nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/options.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/options.go new file mode 100644 index 0000000000..2e75adca4c --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/options.go @@ -0,0 +1,23 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package pipeline + +// Options holds options passed to the Tekton Pipeline controllers +// typically via command-line flags. +type Options struct { + Images Images +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/paths.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/paths.go new file mode 100644 index 0000000000..5a15b2014e --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/paths.go @@ -0,0 +1,31 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package pipeline + +const ( + // WorkspaceDir is the root directory used for PipelineResources and (by default) Workspaces + WorkspaceDir = "/workspace" + // DefaultResultPath is the path for a task result to create the result file + DefaultResultPath = "/tekton/results" + // HomeDir is the HOME directory of PipelineResources + HomeDir = "/tekton/home" + // CredsDir is the directory where credentials are placed to meet the legacy credentials + // helpers image (aka "creds-init") contract + CredsDir = "/tekton/creds" + // StepsDir is the directory used for a step to store any metadata related to the step + StepsDir = "/tekton/steps" +) diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/pod/doc.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/pod/doc.go new file mode 100644 index 0000000000..2187544efa --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/pod/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// +k8s:openapi-gen=true + +// Package pod contains non-versioned pod configuration +package pod diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/pod/template.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/pod/template.go new file mode 100644 index 0000000000..c5bf5493cc --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/pod/template.go @@ -0,0 +1,125 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package pod + +import ( + "reflect" + + corev1 "k8s.io/api/core/v1" +) + +// Template holds pod specific configuration +// +k8s:deepcopy-gen=true +// +k8s:openapi-gen=true +type Template struct { + // NodeSelector is a selector which must be true for the pod to fit on a node. + // Selector which must match a node's labels for the pod to be scheduled on that node. + // More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + // +optional + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + + // If specified, the pod's tolerations. + // +optional + Tolerations []corev1.Toleration `json:"tolerations,omitempty"` + + // If specified, the pod's scheduling constraints + // +optional + Affinity *corev1.Affinity `json:"affinity,omitempty"` + + // SecurityContext holds pod-level security attributes and common container settings. + // Optional: Defaults to empty. See type description for default values of each field. + // +optional + SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` + + // List of volumes that can be mounted by containers belonging to the pod. + // More info: https://kubernetes.io/docs/concepts/storage/volumes + // +optional + // +patchMergeKey=name + // +patchStrategy=merge,retainKeys + Volumes []corev1.Volume `json:"volumes,omitempty" patchStrategy:"merge,retainKeys" patchMergeKey:"name" protobuf:"bytes,1,rep,name=volumes"` + + // RuntimeClassName refers to a RuntimeClass object in the node.k8s.io + // group, which should be used to run this pod. If no RuntimeClass resource + // matches the named class, the pod will not be run. If unset or empty, the + // "legacy" RuntimeClass will be used, which is an implicit class with an + // empty definition that uses the default runtime handler. + // More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md + // This is a beta feature as of Kubernetes v1.14. + // +optional + RuntimeClassName *string `json:"runtimeClassName,omitempty" protobuf:"bytes,2,opt,name=runtimeClassName"` + + // AutomountServiceAccountToken indicates whether pods running as this + // service account should have an API token automatically mounted. + // +optional + AutomountServiceAccountToken *bool `json:"automountServiceAccountToken,omitempty" protobuf:"varint,3,opt,name=automountServiceAccountToken"` + + // Set DNS policy for the pod. Defaults to "ClusterFirst". Valid values are + // 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig + // will be merged with the policy selected with DNSPolicy. + // +optional + DNSPolicy *corev1.DNSPolicy `json:"dnsPolicy,omitempty" protobuf:"bytes,4,opt,name=dnsPolicy,casttype=k8s.io/api/core/v1.DNSPolicy"` + + // Specifies the DNS parameters of a pod. + // Parameters specified here will be merged to the generated DNS + // configuration based on DNSPolicy. + // +optional + DNSConfig *corev1.PodDNSConfig `json:"dnsConfig,omitempty" protobuf:"bytes,5,opt,name=dnsConfig"` + + // EnableServiceLinks indicates whether information about services should be injected into pod's + // environment variables, matching the syntax of Docker links. + // Optional: Defaults to true. + // +optional + EnableServiceLinks *bool `json:"enableServiceLinks,omitempty" protobuf:"varint,6,opt,name=enableServiceLinks"` + + // If specified, indicates the pod's priority. "system-node-critical" and + // "system-cluster-critical" are two special keywords which indicate the + // highest priorities with the former being the highest priority. Any other + // name must be defined by creating a PriorityClass object with that name. + // If not specified, the pod priority will be default or zero if there is no + // default. + // +optional + PriorityClassName *string `json:"priorityClassName,omitempty" protobuf:"bytes,7,opt,name=priorityClassName"` + // SchedulerName specifies the scheduler to be used to dispatch the Pod + // +optional + SchedulerName string `json:"schedulerName,omitempty"` + + // ImagePullSecrets gives the name of the secret used by the pod to pull the image if specified + // +optional + ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` + + // HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts + // file if specified. This is only valid for non-hostNetwork pods. + // +optional + HostAliases []corev1.HostAlias `json:"hostAliases,omitempty"` + + // HostNetwork specifies whether the pod may use the node network namespace + // +optional + HostNetwork bool `json:"hostNetwork,omitempty"` +} + +// Equals checks if this Template is identical to the given Template. +func (tpl *Template) Equals(other *Template) bool { + if tpl == nil && other == nil { + return true + } + + if tpl == nil || other == nil { + return false + } + + return reflect.DeepEqual(tpl, other) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/pod/zz_generated.deepcopy.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/pod/zz_generated.deepcopy.go new file mode 100644 index 0000000000..b746aea320 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/pod/zz_generated.deepcopy.go @@ -0,0 +1,115 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package pod + +import ( + v1 "k8s.io/api/core/v1" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Template) DeepCopyInto(out *Template) { + *out = *in + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(v1.Affinity) + (*in).DeepCopyInto(*out) + } + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(v1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]v1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.RuntimeClassName != nil { + in, out := &in.RuntimeClassName, &out.RuntimeClassName + *out = new(string) + **out = **in + } + if in.AutomountServiceAccountToken != nil { + in, out := &in.AutomountServiceAccountToken, &out.AutomountServiceAccountToken + *out = new(bool) + **out = **in + } + if in.DNSPolicy != nil { + in, out := &in.DNSPolicy, &out.DNSPolicy + *out = new(v1.DNSPolicy) + **out = **in + } + if in.DNSConfig != nil { + in, out := &in.DNSConfig, &out.DNSConfig + *out = new(v1.PodDNSConfig) + (*in).DeepCopyInto(*out) + } + if in.EnableServiceLinks != nil { + in, out := &in.EnableServiceLinks, &out.EnableServiceLinks + *out = new(bool) + **out = **in + } + if in.PriorityClassName != nil { + in, out := &in.PriorityClassName, &out.PriorityClassName + *out = new(string) + **out = **in + } + if in.ImagePullSecrets != nil { + in, out := &in.ImagePullSecrets, &out.ImagePullSecrets + *out = make([]v1.LocalObjectReference, len(*in)) + copy(*out, *in) + } + if in.HostAliases != nil { + in, out := &in.HostAliases, &out.HostAliases + *out = make([]v1.HostAlias, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Template. +func (in *Template) DeepCopy() *Template { + if in == nil { + return nil + } + out := new(Template) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/register.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/register.go new file mode 100644 index 0000000000..97ee57fe8c --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/register.go @@ -0,0 +1,99 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package pipeline + +import "k8s.io/apimachinery/pkg/runtime/schema" + +const ( + // GroupName is the Kubernetes resource group name for Pipeline types. + GroupName = "tekton.dev" + + // ClusterTaskLabelKey is used as the label identifier for a ClusterTask + ClusterTaskLabelKey = GroupName + "/clusterTask" + + // TaskLabelKey is used as the label identifier for a Task + TaskLabelKey = GroupName + "/task" + + // TaskRunLabelKey is used as the label identifier for a TaskRun + TaskRunLabelKey = GroupName + "/taskRun" + + // PipelineLabelKey is used as the label identifier for a Pipeline + PipelineLabelKey = GroupName + "/pipeline" + + // PipelineRunLabelKey is used as the label identifier for a PipelineRun + PipelineRunLabelKey = GroupName + "/pipelineRun" + + // PipelineTaskLabelKey is used as the label identifier for a PipelineTask + PipelineTaskLabelKey = GroupName + "/pipelineTask" + + // ConditionCheckKey is used as the label identifier for a ConditionCheck + ConditionCheckKey = GroupName + "/conditionCheck" + + // ConditionNameKey is used as the label identifier for a Condition + ConditionNameKey = GroupName + "/conditionName" + + // RunKey is used as the label identifier for a Run + RunKey = GroupName + "/run" + + // MemberOfLabelKey is used as the label identifier for a PipelineTask + // Set to Tasks/Finally depending on the position of the PipelineTask + MemberOfLabelKey = GroupName + "/memberOf" +) + +var ( + // TaskResource represents a Tekton Task + TaskResource = schema.GroupResource{ + Group: GroupName, + Resource: "tasks", + } + // ClusterTaskResource represents a Tekton ClusterTask + ClusterTaskResource = schema.GroupResource{ + Group: GroupName, + Resource: "clustertasks", + } + // TaskRunResource represents a Tekton TaskRun + TaskRunResource = schema.GroupResource{ + Group: GroupName, + Resource: "taskruns", + } + // RunResource represents a Tekton Run + RunResource = schema.GroupResource{ + Group: GroupName, + Resource: "runs", + } + // PipelineResource represents a Tekton Pipeline + PipelineResource = schema.GroupResource{ + Group: GroupName, + Resource: "pipelines", + } + // PipelineRunResource represents a Tekton PipelineRun + PipelineRunResource = schema.GroupResource{ + Group: GroupName, + Resource: "pipelineruns", + } + + // PipelineResourceResource represents a Tekton PipelineResource + PipelineResourceResource = schema.GroupResource{ + Group: GroupName, + Resource: "pipelineresources", + } + // ConditionResource represents a Tekton Condition + ConditionResource = schema.GroupResource{ + Group: GroupName, + Resource: "conditions", + } +) diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/storagetypes.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/storagetypes.go new file mode 100644 index 0000000000..edccac25bf --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/storagetypes.go @@ -0,0 +1,25 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package pipeline + +const ( + // ArtifactStorageBucketType holds the name of the PipelineResource type for a bucket + ArtifactStorageBucketType = "bucket" + + // ArtifactStoragePVCType holds the name of the PipelineResource type for a pvc + ArtifactStoragePVCType = "pvc" +) diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/cluster_task_conversion.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/cluster_task_conversion.go new file mode 100644 index 0000000000..976add0d50 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/cluster_task_conversion.go @@ -0,0 +1,49 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "knative.dev/pkg/apis" +) + +var _ apis.Convertible = (*ClusterTask)(nil) + +// ConvertTo implements api.Convertible +func (ct *ClusterTask) ConvertTo(ctx context.Context, obj apis.Convertible) error { + switch sink := obj.(type) { + case *v1beta1.ClusterTask: + sink.ObjectMeta = ct.ObjectMeta + return ct.Spec.ConvertTo(ctx, &sink.Spec) + default: + return fmt.Errorf("unknown version, got: %T", sink) + } +} + +// ConvertFrom implements api.Convertible +func (ct *ClusterTask) ConvertFrom(ctx context.Context, obj apis.Convertible) error { + switch source := obj.(type) { + case *v1beta1.ClusterTask: + ct.ObjectMeta = source.ObjectMeta + return ct.Spec.ConvertFrom(ctx, &source.Spec) + default: + return fmt.Errorf("unknown version, got: %T", ct) + } +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/cluster_task_defaults.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/cluster_task_defaults.go new file mode 100644 index 0000000000..fe18e3e697 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/cluster_task_defaults.go @@ -0,0 +1,30 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + + "knative.dev/pkg/apis" +) + +var _ apis.Defaultable = (*ClusterTask)(nil) + +// SetDefaults sets the default values for the ClusterTask's Spec. +func (t *ClusterTask) SetDefaults(ctx context.Context) { + t.Spec.SetDefaults(ctx) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/cluster_task_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/cluster_task_types.go new file mode 100644 index 0000000000..1b2ebd2785 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/cluster_task_types.go @@ -0,0 +1,64 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +genclient:noStatus +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterTask is a Task with a cluster scope. ClusterTasks are used to +// represent Tasks that should be publicly addressable from any namespace in the +// cluster. +type ClusterTask struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec holds the desired state of the Task from the client + // +optional + Spec TaskSpec `json:"spec,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterTaskList contains a list of ClusterTask. +type ClusterTaskList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClusterTask `json:"items"` +} + +// TaskSpec returns the ClusterTask's Spec. +func (t *ClusterTask) TaskSpec() TaskSpec { + return t.Spec +} + +// TaskMetadata returns the ObjectMeta for the ClusterTask. +func (t *ClusterTask) TaskMetadata() metav1.ObjectMeta { + return t.ObjectMeta +} + +// Copy returns a DeepCopy of the ClusterTask. +func (t *ClusterTask) Copy() TaskObject { + return t.DeepCopy() +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/cluster_task_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/cluster_task_validation.go new file mode 100644 index 0000000000..77adb5ec12 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/cluster_task_validation.go @@ -0,0 +1,37 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + + "github.com/tektoncd/pipeline/pkg/apis/validate" + "knative.dev/pkg/apis" +) + +var _ apis.Validatable = (*ClusterTask)(nil) + +// Validate performs validation of the metadata and spec of this ClusterTask. +func (t *ClusterTask) Validate(ctx context.Context) *apis.FieldError { + if err := validate.ObjectMetadata(t.GetObjectMeta()); err != nil { + return err.ViaField("metadata") + } + if apis.IsInDelete(ctx) { + return nil + } + return t.Spec.Validate(ctx) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/condition_defaults.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/condition_defaults.go new file mode 100644 index 0000000000..dcf7601d88 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/condition_defaults.go @@ -0,0 +1,37 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + + "knative.dev/pkg/apis" +) + +var _ apis.Defaultable = (*Condition)(nil) + +// SetDefaults sets the Condition's Spec's default values. +func (c *Condition) SetDefaults(ctx context.Context) { + c.Spec.SetDefaults(ctx) +} + +// SetDefaults sets defaults for all params on the ConditionSpec. +func (cs *ConditionSpec) SetDefaults(ctx context.Context) { + for i := range cs.Params { + cs.Params[i].SetDefaults(ctx) + } +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/condition_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/condition_types.go new file mode 100644 index 0000000000..ad4ab611d6 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/condition_types.go @@ -0,0 +1,101 @@ +/* +Copyright 2019-2020 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +// +genclient +// +genclient:noStatus +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Condition declares a step that is used to gate the execution of a Task in a Pipeline. +// A condition execution (ConditionCheck) evaluates to either true or false +// +k8s:openapi-gen=true +type Condition struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata"` + + // Spec holds the desired state of the Condition from the client + // +optional + Spec ConditionSpec `json:"spec"` +} + +// ConditionCheckStatus defines the observed state of ConditionCheck +type ConditionCheckStatus = v1beta1.ConditionCheckStatus + +// ConditionCheckStatusFields holds the fields of ConfigurationCheck's status. +// This is defined separately and inlined so that other types can readily +// consume these fields via duck typing. +type ConditionCheckStatusFields = v1beta1.ConditionCheckStatusFields + +// ConditionSpec defines the desired state of the Condition +type ConditionSpec struct { + // Check declares container whose exit code determines where a condition is true or false + Check Step `json:"check,omitempty"` + + // Description is a user-facing description of the condition that may be + // used to populate a UI. + // +optional + Description string `json:"description,omitempty"` + + // Params is an optional set of parameters which must be supplied by the user when a Condition + // is evaluated + // +optional + Params []ParamSpec `json:"params,omitempty"` + + // Resources is a list of the ConditionResources required to run the condition. + // +optional + Resources []ResourceDeclaration `json:"resources,omitempty"` +} + +// ConditionCheck represents a single evaluation of a Condition step. +type ConditionCheck TaskRun + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ConditionList contains a list of Conditions +type ConditionList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []Condition `json:"items"` +} + +// NewConditionCheck creates a new ConditionCheck from a given TaskRun. +func NewConditionCheck(tr *TaskRun) *ConditionCheck { + if tr == nil { + return nil + } + + cc := ConditionCheck(*tr) + return &cc +} + +// IsDone returns true if the ConditionCheck's status indicates that it is done. +func (cc *ConditionCheck) IsDone() bool { + return !cc.Status.GetCondition(apis.ConditionSucceeded).IsUnknown() +} + +// IsSuccessful returns true if the ConditionCheck's status indicates that it is done. +func (cc *ConditionCheck) IsSuccessful() bool { + return cc.Status.GetCondition(apis.ConditionSucceeded).IsTrue() +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/condition_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/condition_validation.go new file mode 100644 index 0000000000..daa8b75fc8 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/condition_validation.go @@ -0,0 +1,59 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + + "github.com/tektoncd/pipeline/pkg/apis/validate" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/util/validation" + "knative.dev/pkg/apis" +) + +var _ apis.Validatable = (*Condition)(nil) + +// Validate performs validation on the Condition's metadata and spec +func (c Condition) Validate(ctx context.Context) *apis.FieldError { + if err := validate.ObjectMetadata(c.GetObjectMeta()); err != nil { + return err.ViaField("metadata") + } + if apis.IsInDelete(ctx) { + return nil + } + return c.Spec.Validate(ctx).ViaField("Spec") +} + +// Validate makes sure the ConditionSpec is actually configured and that its name is a valid DNS label, +// and finally validates its steps. +func (cs *ConditionSpec) Validate(ctx context.Context) *apis.FieldError { + if equality.Semantic.DeepEqual(cs, ConditionSpec{}) { + return apis.ErrMissingField(apis.CurrentField) + } + + // Validate condition check name + if errs := validation.IsDNS1123Label(cs.Check.Name); cs.Check.Name != "" && len(errs) > 0 { + return &apis.FieldError{ + Message: fmt.Sprintf("invalid value %q", cs.Check.Name), + Paths: []string{"Check.name"}, + Details: "Condition check name must be a valid DNS Label, For more info refer to https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + } + } + + return validateSteps([]Step{cs.Check}).ViaField("Check") +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/container_replacements.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/container_replacements.go new file mode 100644 index 0000000000..63606afc6a --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/container_replacements.go @@ -0,0 +1,74 @@ +/* + Copyright 2019 The Tekton Authors + + 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. +*/ + +package v1alpha1 + +import ( + "github.com/tektoncd/pipeline/pkg/substitution" + corev1 "k8s.io/api/core/v1" +) + +// ApplyContainerReplacements replaces ${...} expressions in the container's name, image, args, env, command, workingDir, +// and volumes. +func ApplyContainerReplacements(step *corev1.Container, stringReplacements map[string]string, arrayReplacements map[string][]string) { + step.Name = substitution.ApplyReplacements(step.Name, stringReplacements) + step.Image = substitution.ApplyReplacements(step.Image, stringReplacements) + + // Use ApplyArrayReplacements here, as additional args may be added via an array parameter. + var newArgs []string + for _, a := range step.Args { + newArgs = append(newArgs, substitution.ApplyArrayReplacements(a, stringReplacements, arrayReplacements)...) + } + step.Args = newArgs + + for ie, e := range step.Env { + step.Env[ie].Value = substitution.ApplyReplacements(e.Value, stringReplacements) + if step.Env[ie].ValueFrom != nil { + if e.ValueFrom.SecretKeyRef != nil { + step.Env[ie].ValueFrom.SecretKeyRef.LocalObjectReference.Name = substitution.ApplyReplacements(e.ValueFrom.SecretKeyRef.LocalObjectReference.Name, stringReplacements) + step.Env[ie].ValueFrom.SecretKeyRef.Key = substitution.ApplyReplacements(e.ValueFrom.SecretKeyRef.Key, stringReplacements) + } + if e.ValueFrom.ConfigMapKeyRef != nil { + step.Env[ie].ValueFrom.ConfigMapKeyRef.LocalObjectReference.Name = substitution.ApplyReplacements(e.ValueFrom.ConfigMapKeyRef.LocalObjectReference.Name, stringReplacements) + step.Env[ie].ValueFrom.ConfigMapKeyRef.Key = substitution.ApplyReplacements(e.ValueFrom.ConfigMapKeyRef.Key, stringReplacements) + } + } + } + + for ie, e := range step.EnvFrom { + step.EnvFrom[ie].Prefix = substitution.ApplyReplacements(e.Prefix, stringReplacements) + if e.ConfigMapRef != nil { + step.EnvFrom[ie].ConfigMapRef.LocalObjectReference.Name = substitution.ApplyReplacements(e.ConfigMapRef.LocalObjectReference.Name, stringReplacements) + } + if e.SecretRef != nil { + step.EnvFrom[ie].SecretRef.LocalObjectReference.Name = substitution.ApplyReplacements(e.SecretRef.LocalObjectReference.Name, stringReplacements) + } + } + step.WorkingDir = substitution.ApplyReplacements(step.WorkingDir, stringReplacements) + + // Use ApplyArrayReplacements here, as additional commands may be added via an array parameter. + var newCommand []string + for _, c := range step.Command { + newCommand = append(newCommand, substitution.ApplyArrayReplacements(c, stringReplacements, arrayReplacements)...) + } + step.Command = newCommand + + for iv, v := range step.VolumeMounts { + step.VolumeMounts[iv].Name = substitution.ApplyReplacements(v.Name, stringReplacements) + step.VolumeMounts[iv].MountPath = substitution.ApplyReplacements(v.MountPath, stringReplacements) + step.VolumeMounts[iv].SubPath = substitution.ApplyReplacements(v.SubPath, stringReplacements) + } +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/conversion_error.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/conversion_error.go new file mode 100644 index 0000000000..2ebdba1a1f --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/conversion_error.go @@ -0,0 +1,31 @@ +/* +Copyright 2020 The Tekton Authors. + +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. +*/ + +package v1alpha1 + +import ( + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "knative.dev/pkg/apis" +) + +const ( + // ConditionTypeConvertible is a Warning condition that is set on + // resources when they cannot be converted to warn of a forthcoming + // breakage. + ConditionTypeConvertible apis.ConditionType = v1beta1.ConditionTypeConvertible + // ConversionErrorFieldNotAvailableMsg Conversion Error message for a field not available in v1alpha1 + ConversionErrorFieldNotAvailableMsg = "the specified field/section is not available in v1alpha1" +) diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/doc.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/doc.go new file mode 100644 index 0000000000..288d138dd4 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/doc.go @@ -0,0 +1,23 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Package v1alpha1 contains API Schema definitions for the pipeline v1alpha1 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=github.com/tektoncd/pipeline/pkg/apis/pipeline +// +k8s:defaulter-gen=TypeMeta +// +groupName=tekton.dev +package v1alpha1 diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/param_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/param_types.go new file mode 100644 index 0000000000..69b21947c6 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/param_types.go @@ -0,0 +1,49 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" +) + +// ParamSpec defines arbitrary parameters needed beyond typed inputs (such as +// resources). Parameter values are provided by users as inputs on a TaskRun +// or PipelineRun. +type ParamSpec = v1beta1.ParamSpec + +// Param declares an ArrayOrString to use for the parameter called name. +type Param = v1beta1.Param + +// ParamType indicates the type of an input parameter; +// Used to distinguish between a single string and an array of strings. +type ParamType = v1beta1.ParamType + +// Valid ParamTypes: +const ( + ParamTypeString ParamType = v1beta1.ParamTypeString + ParamTypeArray ParamType = v1beta1.ParamTypeArray +) + +// AllParamTypes can be used for ParamType validation. +var AllParamTypes = v1beta1.AllParamTypes + +// ArrayOrString is modeled after IntOrString in kubernetes/apimachinery: + +// ArrayOrString is a type that can hold a single string or string array. +// Used in JSON unmarshalling so that a single JSON field can accept +// either an individual string or an array of strings. +type ArrayOrString = v1beta1.ArrayOrString diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_conversion.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_conversion.go new file mode 100644 index 0000000000..06b5e60a99 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_conversion.go @@ -0,0 +1,177 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +const finallyAnnotationKey = "tekton.dev/v1beta1Finally" + +var _ apis.Convertible = (*Pipeline)(nil) + +// ConvertTo implements api.Convertible +func (p *Pipeline) ConvertTo(ctx context.Context, obj apis.Convertible) error { + switch sink := obj.(type) { + case *v1beta1.Pipeline: + sink.ObjectMeta = p.ObjectMeta + if err := p.Spec.ConvertTo(ctx, &sink.Spec); err != nil { + return err + } + if err := deserializeFinally(&sink.ObjectMeta, &sink.Spec); err != nil { + return err + } + if err := v1beta1.ValidatePipelineTasks(ctx, sink.Spec.Tasks, sink.Spec.Finally); err != nil { + return fmt.Errorf("error converting finally annotation into beta field: %w", err) + } + default: + return fmt.Errorf("unknown version, got: %T", sink) + } + return nil +} + +// ConvertTo implements api.Convertible +func (ps *PipelineSpec) ConvertTo(ctx context.Context, sink *v1beta1.PipelineSpec) error { + sink.Resources = ps.Resources + sink.Params = ps.Params + sink.Workspaces = ps.Workspaces + sink.Description = ps.Description + if len(ps.Tasks) > 0 { + sink.Tasks = make([]v1beta1.PipelineTask, len(ps.Tasks)) + for i := range ps.Tasks { + if err := ps.Tasks[i].ConvertTo(ctx, &sink.Tasks[i]); err != nil { + return err + } + } + } + sink.Finally = nil + return nil +} + +// ConvertTo implements api.Convertible +func (pt *PipelineTask) ConvertTo(ctx context.Context, sink *v1beta1.PipelineTask) error { + sink.Name = pt.Name + sink.TaskRef = pt.TaskRef + if pt.TaskSpec != nil { + sink.TaskSpec = &v1beta1.EmbeddedTask{TaskSpec: v1beta1.TaskSpec{}} + if err := pt.TaskSpec.ConvertTo(ctx, &sink.TaskSpec.TaskSpec); err != nil { + return err + } + } + sink.Conditions = pt.Conditions + sink.Retries = pt.Retries + sink.RunAfter = pt.RunAfter + sink.Resources = pt.Resources + sink.Params = pt.Params + sink.Workspaces = pt.Workspaces + sink.Timeout = pt.Timeout + return nil +} + +// ConvertFrom implements api.Convertible +func (p *Pipeline) ConvertFrom(ctx context.Context, obj apis.Convertible) error { + switch source := obj.(type) { + case *v1beta1.Pipeline: + p.ObjectMeta = source.ObjectMeta + if err := serializeFinally(&p.ObjectMeta, source.Spec.Finally); err != nil { + return err + } + return p.Spec.ConvertFrom(ctx, source.Spec) + default: + return fmt.Errorf("unknown version, got: %T", p) + } +} + +// ConvertFrom implements api.Convertible +func (ps *PipelineSpec) ConvertFrom(ctx context.Context, source v1beta1.PipelineSpec) error { + ps.Resources = source.Resources + ps.Params = source.Params + ps.Workspaces = source.Workspaces + ps.Description = source.Description + if len(source.Tasks) > 0 { + ps.Tasks = make([]PipelineTask, len(source.Tasks)) + for i := range source.Tasks { + if err := ps.Tasks[i].ConvertFrom(ctx, source.Tasks[i]); err != nil { + return err + } + } + } + return nil +} + +// ConvertFrom implements api.Convertible +func (pt *PipelineTask) ConvertFrom(ctx context.Context, source v1beta1.PipelineTask) error { + pt.Name = source.Name + pt.TaskRef = source.TaskRef + if source.TaskSpec != nil { + pt.TaskSpec = &TaskSpec{} + if err := pt.TaskSpec.ConvertFrom(ctx, &source.TaskSpec.TaskSpec); err != nil { + return err + } + } + pt.Conditions = source.Conditions + pt.Retries = source.Retries + pt.RunAfter = source.RunAfter + pt.Resources = source.Resources + pt.Params = source.Params + pt.Workspaces = source.Workspaces + pt.Timeout = source.Timeout + return nil +} + +// serializeFinally serializes a list of Finally Tasks to the annotations +// of an object's metadata section. This can then be used to re-instantiate +// the Finally Tasks when converting back up to v1beta1 and beyond. +func serializeFinally(meta *metav1.ObjectMeta, finally []v1beta1.PipelineTask) error { + if len(finally) != 0 { + b, err := json.Marshal(finally) + if err != nil { + return err + } + if meta.Annotations == nil { + meta.Annotations = make(map[string]string) + } + meta.Annotations[finallyAnnotationKey] = string(b) + } + return nil +} + +// deserializeFinally populates a PipelineSpec's Finally list +// from an annotation found on resources that have been previously +// converted down from v1beta1 to v1alpha1. +func deserializeFinally(meta *metav1.ObjectMeta, spec *v1beta1.PipelineSpec) error { + if meta.Annotations != nil { + if str, ok := meta.Annotations[finallyAnnotationKey]; ok { + finally := []v1beta1.PipelineTask{} + if err := json.Unmarshal([]byte(str), &finally); err != nil { + return fmt.Errorf("error converting finally annotation into beta field: %w", err) + } + delete(meta.Annotations, finallyAnnotationKey) + if len(meta.Annotations) == 0 { + meta.Annotations = nil + } + spec.Finally = finally + } + } + return nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_defaults.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_defaults.go new file mode 100644 index 0000000000..2bb226c034 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_defaults.go @@ -0,0 +1,47 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + + "knative.dev/pkg/apis" +) + +var _ apis.Defaultable = (*Pipeline)(nil) + +// SetDefaults sets default values on the Pipeline's Spec +func (p *Pipeline) SetDefaults(ctx context.Context) { + p.Spec.SetDefaults(ctx) +} + +// SetDefaults sets default values for the PipelineSpec's Params and Tasks +func (ps *PipelineSpec) SetDefaults(ctx context.Context) { + for _, pt := range ps.Tasks { + if pt.TaskRef != nil { + if pt.TaskRef.Kind == "" { + pt.TaskRef.Kind = NamespacedTaskKind + } + } + if pt.TaskSpec != nil { + pt.TaskSpec.SetDefaults(ctx) + } + } + for i := range ps.Params { + ps.Params[i].SetDefaults(ctx) + } +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_interface.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_interface.go new file mode 100644 index 0000000000..70e14bb774 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_interface.go @@ -0,0 +1,26 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// PipelineObject is implemented by Pipeline and ClusterPipeline +type PipelineObject interface { + PipelineMetadata() metav1.ObjectMeta + PipelineSpec() PipelineSpec + Copy() PipelineObject +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_resource_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_resource_types.go new file mode 100644 index 0000000000..d35e41bf1b --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_resource_types.go @@ -0,0 +1,74 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + resource "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" +) + +// PipelineResourceType represents the type of endpoint the pipelineResource is, so that the +// controller will know this pipelineResource should be fetched and optionally what +// additional metatdata should be provided for it. +type PipelineResourceType = resource.PipelineResourceType + +const ( + // PipelineResourceTypeGit indicates that this source is a Git repo. + PipelineResourceTypeGit PipelineResourceType = resource.PipelineResourceTypeGit + + // PipelineResourceTypeStorage indicates that this source is a storage blob resource. + PipelineResourceTypeStorage PipelineResourceType = resource.PipelineResourceTypeStorage + + // PipelineResourceTypeImage indicates that this source is a docker Image. + PipelineResourceTypeImage PipelineResourceType = resource.PipelineResourceTypeImage + + // PipelineResourceTypeCluster indicates that this source is a k8s cluster Image. + PipelineResourceTypeCluster PipelineResourceType = resource.PipelineResourceTypeCluster + + // PipelineResourceTypePullRequest indicates that this source is a SCM Pull Request. + PipelineResourceTypePullRequest PipelineResourceType = resource.PipelineResourceTypePullRequest + + // PipelineResourceTypeCloudEvent indicates that this source is a cloud event URI + PipelineResourceTypeCloudEvent PipelineResourceType = resource.PipelineResourceTypeCloudEvent +) + +// AllResourceTypes can be used for validation to check if a provided Resource type is one of the known types. +var AllResourceTypes = resource.AllResourceTypes + +// PipelineResource describes a resource that is an input to or output from a +// Task. +// +type PipelineResource = resource.PipelineResource + +// PipelineResourceSpec defines an individual resources used in the pipeline. +type PipelineResourceSpec = resource.PipelineResourceSpec + +// SecretParam indicates which secret can be used to populate a field of the resource +type SecretParam = resource.SecretParam + +// ResourceParam declares a string value to use for the parameter called Name, and is used in +// the specific context of PipelineResources. +type ResourceParam = resource.ResourceParam + +// ResourceDeclaration defines an input or output PipelineResource declared as a requirement +// by another type such as a Task or Condition. The Name field will be used to refer to these +// PipelineResources within the type's definition, and when provided as an Input, the Name will be the +// path to the volume mounted containing this PipelineResource as an input (e.g. +// an input Resource named `workspace` will be mounted at `/workspace`). +type ResourceDeclaration = resource.ResourceDeclaration + +// PipelineResourceList contains a list of PipelineResources +type PipelineResourceList = resource.PipelineResourceList diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_types.go new file mode 100644 index 0000000000..e02a19d862 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_types.go @@ -0,0 +1,259 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "github.com/tektoncd/pipeline/pkg/reconciler/pipeline/dag" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// PipelineSpec defines the desired state of Pipeline. +type PipelineSpec struct { + // Description is a user-facing description of the pipeline that may be + // used to populate a UI. + // +optional + Description string `json:"description,omitempty"` + // Resources declares the names and types of the resources given to the + // Pipeline's tasks as inputs and outputs. + Resources []PipelineDeclaredResource `json:"resources,omitempty"` + // Tasks declares the graph of Tasks that execute when this Pipeline is run. + Tasks []PipelineTask `json:"tasks,omitempty"` + // Params declares a list of input parameters that must be supplied when + // this Pipeline is run. + Params []ParamSpec `json:"params,omitempty"` + // Workspaces declares a set of named workspaces that are expected to be + // provided by a PipelineRun. + // +optional + Workspaces []PipelineWorkspaceDeclaration `json:"workspaces,omitempty"` + // Results are values that this pipeline can output once run + // +optional + Results []PipelineResult `json:"results,omitempty"` +} + +// PipelineResult used to describe the results of a pipeline +type PipelineResult = v1beta1.PipelineResult + +// Check that Pipeline may be validated and defaulted. + +// TaskKind defines the type of Task used by the pipeline. +type TaskKind = v1beta1.TaskKind + +const ( + // NamespacedTaskKind indicates that the task type has a namepace scope. + NamespacedTaskKind TaskKind = v1beta1.NamespacedTaskKind + // ClusterTaskKind indicates that task type has a cluster scope. + ClusterTaskKind TaskKind = v1beta1.ClusterTaskKind +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +genclient:noStatus + +// Pipeline describes a list of Tasks to execute. It expresses how outputs +// of tasks feed into inputs of subsequent tasks. +// +k8s:openapi-gen=true +type Pipeline struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec holds the desired state of the Pipeline from the client + // +optional + Spec PipelineSpec `json:"spec"` + + // Status is deprecated. + // It usually is used to communicate the observed state of the Pipeline from + // the controller, but was unused as there is no controller for Pipeline. + // +optional + Status *PipelineStatus `json:"status,omitempty"` +} + +// PipelineStatus does not contain anything because Pipelines on their own +// do not have a status, they just hold data which is later used by a +// PipelineRun. +// Deprecated +type PipelineStatus struct { +} + +// PipelineMetadata returns the Pipeline's ObjectMeta, implementing PipelineObject. +func (p *Pipeline) PipelineMetadata() metav1.ObjectMeta { + return p.ObjectMeta +} + +// PipelineSpec returns the Pipeline's Spec, implementing PipelineObject. +func (p *Pipeline) PipelineSpec() PipelineSpec { + return p.Spec +} + +// Copy returns a deep copy of the Pipeline, implementing PipelineObject. +func (p *Pipeline) Copy() PipelineObject { + return p.DeepCopy() +} + +// PipelineTask defines a task in a Pipeline, passing inputs from both +// Params and from the output of previous tasks. +type PipelineTask struct { + // Name is the name of this task within the context of a Pipeline. Name is + // used as a coordinate with the `from` and `runAfter` fields to establish + // the execution order of tasks relative to one another. + Name string `json:"name,omitempty"` + + // TaskRef is a reference to a task definition. + // +optional + TaskRef *TaskRef `json:"taskRef,omitempty"` + + // TaskSpec is specification of a task + // +optional + TaskSpec *TaskSpec `json:"taskSpec,omitempty"` + + // Conditions is a list of conditions that need to be true for the task to run + // +optional + Conditions []PipelineTaskCondition `json:"conditions,omitempty"` + + // Retries represents how many times this task should be retried in case of task failure: ConditionSucceeded set to False + // +optional + Retries int `json:"retries,omitempty"` + + // RunAfter is the list of PipelineTask names that should be executed before + // this Task executes. (Used to force a specific ordering in graph execution.) + // +optional + RunAfter []string `json:"runAfter,omitempty"` + + // Resources declares the resources given to this task as inputs and + // outputs. + // +optional + Resources *PipelineTaskResources `json:"resources,omitempty"` + // Parameters declares parameters passed to this task. + // +optional + Params []Param `json:"params,omitempty"` + + // Workspaces maps workspaces from the pipeline spec to the workspaces + // declared in the Task. + // +optional + Workspaces []WorkspacePipelineTaskBinding `json:"workspaces,omitempty"` + + // Time after which the TaskRun times out. Defaults to 1 hour. + // Specified TaskRun timeout should be less than 24h. + // Refer Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` +} + +// HashKey is used as the key for this PipelineTask in the DAG +func (pt PipelineTask) HashKey() string { + return pt.Name +} + +// Deps returns all other PipelineTask dependencies of this PipelineTask, based on resource usage or ordering +func (pt PipelineTask) Deps() []string { + deps := []string{} + deps = append(deps, pt.RunAfter...) + if pt.Resources != nil { + for _, rd := range pt.Resources.Inputs { + deps = append(deps, rd.From...) + } + } + // Add any dependents from conditional resources. + for _, cond := range pt.Conditions { + for _, rd := range cond.Resources { + deps = append(deps, rd.From...) + } + for _, param := range cond.Params { + expressions, ok := v1beta1.GetVarSubstitutionExpressionsForParam(param) + if ok { + resultRefs := v1beta1.NewResultRefs(expressions) + for _, resultRef := range resultRefs { + deps = append(deps, resultRef.PipelineTask) + } + } + } + } + // Add any dependents from task results + for _, param := range pt.Params { + expressions, ok := v1beta1.GetVarSubstitutionExpressionsForParam(param) + if ok { + resultRefs := v1beta1.NewResultRefs(expressions) + for _, resultRef := range resultRefs { + deps = append(deps, resultRef.PipelineTask) + } + } + } + + return deps +} + +// PipelineTaskList is a list of PipelineTasks +type PipelineTaskList []PipelineTask + +// Items returns a slice of all tasks in the PipelineTaskList, converted to dag.Tasks +func (l PipelineTaskList) Items() []dag.Task { + tasks := []dag.Task{} + for _, t := range l { + tasks = append(tasks, dag.Task(t)) + } + return tasks +} + +// Deps returns a map with key as name of a pipelineTask and value as a list of its dependencies +func (l PipelineTaskList) Deps() map[string][]string { + deps := map[string][]string{} + for _, pt := range l { + deps[pt.HashKey()] = pt.Deps() + } + return deps +} + +// PipelineTaskParam is used to provide arbitrary string parameters to a Task. +type PipelineTaskParam = v1beta1.PipelineTaskParam + +// PipelineTaskCondition allows a PipelineTask to declare a Condition to be evaluated before +// the Task is run. +type PipelineTaskCondition = v1beta1.PipelineTaskCondition + +// PipelineDeclaredResource is used by a Pipeline to declare the types of the +// PipelineResources that it will required to run and names which can be used to +// refer to these PipelineResources in PipelineTaskResourceBindings. +type PipelineDeclaredResource = v1beta1.PipelineDeclaredResource + +// PipelineTaskResources allows a Pipeline to declare how its DeclaredPipelineResources +// should be provided to a Task as its inputs and outputs. +type PipelineTaskResources = v1beta1.PipelineTaskResources + +// PipelineTaskInputResource maps the name of a declared PipelineResource input +// dependency in a Task to the resource in the Pipeline's DeclaredPipelineResources +// that should be used. This input may come from a previous task. +type PipelineTaskInputResource = v1beta1.PipelineTaskInputResource + +// PipelineTaskOutputResource maps the name of a declared PipelineResource output +// dependency in a Task to the resource in the Pipeline's DeclaredPipelineResources +// that should be used. +type PipelineTaskOutputResource = v1beta1.PipelineTaskOutputResource + +// TaskRef can be used to refer to a specific instance of a task. +// Copied from CrossVersionObjectReference: https://github.com/kubernetes/kubernetes/blob/169df7434155cbbc22f1532cba8e0a9588e29ad8/pkg/apis/autoscaling/types.go#L64 +type TaskRef = v1beta1.TaskRef + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PipelineList contains a list of Pipeline +type PipelineList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []Pipeline `json:"items"` +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_validation.go new file mode 100644 index 0000000000..7492a4d0c2 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipeline_validation.go @@ -0,0 +1,335 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + "strings" + + "github.com/tektoncd/pipeline/pkg/apis/validate" + "github.com/tektoncd/pipeline/pkg/list" + "github.com/tektoncd/pipeline/pkg/reconciler/pipeline/dag" + "github.com/tektoncd/pipeline/pkg/substitution" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/validation" + "knative.dev/pkg/apis" +) + +var _ apis.Validatable = (*Pipeline)(nil) + +// Validate checks that the Pipeline structure is valid but does not validate +// that any references resources exist, that is done at run time. +func (p *Pipeline) Validate(ctx context.Context) *apis.FieldError { + if err := validate.ObjectMetadata(p.GetObjectMeta()); err != nil { + return err.ViaField("metadata") + } + if apis.IsInDelete(ctx) { + return nil + } + return p.Spec.Validate(ctx) +} + +func validateDeclaredResources(ps *PipelineSpec) error { + encountered := sets.NewString() + for _, r := range ps.Resources { + if encountered.Has(r.Name) { + return fmt.Errorf("resource with name %q appears more than once", r.Name) + } + encountered.Insert(r.Name) + } + required := []string{} + for _, t := range ps.Tasks { + if t.Resources != nil { + for _, input := range t.Resources.Inputs { + required = append(required, input.Resource) + } + for _, output := range t.Resources.Outputs { + required = append(required, output.Resource) + } + } + + for _, condition := range t.Conditions { + for _, cr := range condition.Resources { + required = append(required, cr.Resource) + } + } + } + + provided := make([]string, 0, len(ps.Resources)) + for _, resource := range ps.Resources { + provided = append(provided, resource.Name) + } + missing := list.DiffLeft(required, provided) + if len(missing) > 0 { + return fmt.Errorf("pipeline declared resources didn't match usage in Tasks: Didn't provide required values: %s", missing) + } + + return nil +} + +func isOutput(outputs []PipelineTaskOutputResource, resource string) bool { + for _, output := range outputs { + if output.Resource == resource { + return true + } + } + return false +} + +// validateFrom ensures that the `from` values make sense: that they rely on values from Tasks +// that ran previously, and that the PipelineResource is actually an output of the Task it should come from. +func validateFrom(tasks []PipelineTask) *apis.FieldError { + taskOutputs := map[string][]PipelineTaskOutputResource{} + for _, task := range tasks { + var to []PipelineTaskOutputResource + if task.Resources != nil { + to = make([]PipelineTaskOutputResource, len(task.Resources.Outputs)) + copy(to, task.Resources.Outputs) + } + taskOutputs[task.Name] = to + } + for _, t := range tasks { + inputResources := []PipelineTaskInputResource{} + if t.Resources != nil { + inputResources = append(inputResources, t.Resources.Inputs...) + } + + for _, c := range t.Conditions { + inputResources = append(inputResources, c.Resources...) + } + + for _, rd := range inputResources { + for _, pt := range rd.From { + outputs, found := taskOutputs[pt] + if !found { + return apis.ErrInvalidValue(fmt.Sprintf("expected resource %s to be from task %s, but task %s doesn't exist", rd.Resource, pt, pt), + "spec.tasks.resources.inputs.from") + } + if !isOutput(outputs, rd.Resource) { + return apis.ErrInvalidValue(fmt.Sprintf("the resource %s from %s must be an output but is an input", rd.Resource, pt), + "spec.tasks.resources.inputs.from") + } + } + } + } + return nil +} + +// validateGraph ensures the Pipeline's dependency Graph (DAG) make sense: that there is no dependency +// cycle or that they rely on values from Tasks that ran previously, and that the PipelineResource +// is actually an output of the Task it should come from. +func validateGraph(tasks []PipelineTask) error { + if _, err := dag.Build(PipelineTaskList(tasks), PipelineTaskList(tasks).Deps()); err != nil { + return err + } + return nil +} + +// Validate checks that taskNames in the Pipeline are valid and that the graph +// of Tasks expressed in the Pipeline makes sense. +func (ps *PipelineSpec) Validate(ctx context.Context) *apis.FieldError { + if equality.Semantic.DeepEqual(ps, &PipelineSpec{}) { + return apis.ErrGeneric("expected at least one, got none", "spec.description", "spec.params", "spec.resources", "spec.tasks", "spec.workspaces") + } + + // PipelineTask must have a valid unique label and at least one of taskRef or taskSpec should be specified + if err := validatePipelineTasks(ctx, ps.Tasks); err != nil { + return err + } + + // All declared resources should be used, and the Pipeline shouldn't try to use any resources + // that aren't declared + if err := validateDeclaredResources(ps); err != nil { + return apis.ErrInvalidValue(err.Error(), "spec.resources") + } + + // The from values should make sense + if err := validateFrom(ps.Tasks); err != nil { + return err + } + + // Validate the pipeline task graph + if err := validateGraph(ps.Tasks); err != nil { + return apis.ErrInvalidValue(err.Error(), "spec.tasks") + } + + // The parameter variables should be valid + if err := validatePipelineParameterVariables(ps.Tasks, ps.Params); err != nil { + return err + } + + // Validate the pipeline's workspaces. + return validatePipelineWorkspaces(ps.Workspaces, ps.Tasks) +} + +func validatePipelineTasks(ctx context.Context, tasks []PipelineTask) *apis.FieldError { + // Names cannot be duplicated + taskNames := sets.NewString() + var err *apis.FieldError + for i, t := range tasks { + if err = validatePipelineTaskName(ctx, "spec.tasks", i, t, taskNames); err != nil { + return err + } + } + return nil +} + +func validatePipelineTaskName(ctx context.Context, prefix string, i int, t PipelineTask, taskNames sets.String) *apis.FieldError { + if errs := validation.IsDNS1123Label(t.Name); len(errs) > 0 { + return &apis.FieldError{ + Message: fmt.Sprintf("invalid value %q", t.Name), + Paths: []string{fmt.Sprintf(prefix+"[%d].name", i)}, + Details: "Pipeline Task name must be a valid DNS Label." + + "For more info refer to https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + } + } + // can't have both taskRef and taskSpec at the same time + if (t.TaskRef != nil && t.TaskRef.Name != "") && t.TaskSpec != nil { + return apis.ErrMultipleOneOf(fmt.Sprintf(prefix+"[%d].taskRef", i), fmt.Sprintf(prefix+"[%d].taskSpec", i)) + } + // Check that one of TaskRef and TaskSpec is present + if (t.TaskRef == nil || (t.TaskRef != nil && t.TaskRef.Name == "")) && t.TaskSpec == nil { + return apis.ErrMissingOneOf(fmt.Sprintf(prefix+"[%d].taskRef", i), fmt.Sprintf(prefix+"[%d].taskSpec", i)) + } + // Validate TaskSpec if it's present + if t.TaskSpec != nil { + if err := t.TaskSpec.Validate(ctx); err != nil { + return err + } + } + if t.TaskRef != nil && t.TaskRef.Name != "" { + // Task names are appended to the container name, which must exist and + // must be a valid k8s name + if errSlice := validation.IsQualifiedName(t.Name); len(errSlice) != 0 { + return apis.ErrInvalidValue(strings.Join(errSlice, ","), fmt.Sprintf(prefix+"[%d].name", i)) + } + // TaskRef name must be a valid k8s name + if errSlice := validation.IsQualifiedName(t.TaskRef.Name); len(errSlice) != 0 { + return apis.ErrInvalidValue(strings.Join(errSlice, ","), fmt.Sprintf(prefix+"[%d].taskRef.name", i)) + } + if _, ok := taskNames[t.Name]; ok { + return apis.ErrMultipleOneOf(fmt.Sprintf(prefix+"[%d].name", i)) + } + taskNames[t.Name] = struct{}{} + } + return nil +} + +func validatePipelineWorkspaces(wss []PipelineWorkspaceDeclaration, pts []PipelineTask) *apis.FieldError { + // Workspace names must be non-empty and unique. + wsTable := sets.NewString() + for i, ws := range wss { + if ws.Name == "" { + return apis.ErrInvalidValue(fmt.Sprintf("workspace %d has empty name", i), "spec.workspaces") + } + if wsTable.Has(ws.Name) { + return apis.ErrInvalidValue(fmt.Sprintf("workspace with name %q appears more than once", ws.Name), "spec.workspaces") + } + wsTable.Insert(ws.Name) + } + + // Any workspaces used in PipelineTasks should have their name declared in the Pipeline's + // Workspaces list. + for ptIdx, pt := range pts { + for wsIdx, ws := range pt.Workspaces { + if _, ok := wsTable[ws.Workspace]; !ok { + return apis.ErrInvalidValue( + fmt.Sprintf("pipeline task %q expects workspace with name %q but none exists in pipeline spec", pt.Name, ws.Workspace), + fmt.Sprintf("spec.tasks[%d].workspaces[%d]", ptIdx, wsIdx), + ) + } + } + } + return nil +} + +func validatePipelineParameterVariables(tasks []PipelineTask, params []ParamSpec) *apis.FieldError { + parameterNames := sets.NewString() + arrayParameterNames := sets.NewString() + + for _, p := range params { + // Verify that p is a valid type. + validType := false + for _, allowedType := range AllParamTypes { + if p.Type == allowedType { + validType = true + } + } + if !validType { + return apis.ErrInvalidValue(string(p.Type), fmt.Sprintf("spec.params.%s.type", p.Name)) + } + + // If a default value is provided, ensure its type matches param's declared type. + if (p.Default != nil) && (p.Default.Type != p.Type) { + return &apis.FieldError{ + Message: fmt.Sprintf( + "\"%v\" type does not match default value's type: \"%v\"", p.Type, p.Default.Type), + Paths: []string{ + fmt.Sprintf("spec.params.%s.type", p.Name), + fmt.Sprintf("spec.params.%s.default.type", p.Name), + }, + } + } + + // Add parameter name to parameterNames, and to arrayParameterNames if type is array. + parameterNames.Insert(p.Name) + if p.Type == ParamTypeArray { + arrayParameterNames.Insert(p.Name) + } + } + + return validatePipelineVariables(tasks, "params", parameterNames, arrayParameterNames) +} + +func validatePipelineVariables(tasks []PipelineTask, prefix string, paramNames sets.String, arrayParamNames sets.String) *apis.FieldError { + for _, task := range tasks { + for _, param := range task.Params { + if param.Value.Type == ParamTypeString { + if err := validatePipelineVariable(fmt.Sprintf("param[%s]", param.Name), param.Value.StringVal, prefix, paramNames); err != nil { + return err + } + if err := validatePipelineNoArrayReferenced(fmt.Sprintf("param[%s]", param.Name), param.Value.StringVal, prefix, arrayParamNames); err != nil { + return err + } + } else { + for _, arrayElement := range param.Value.ArrayVal { + if err := validatePipelineVariable(fmt.Sprintf("param[%s]", param.Name), arrayElement, prefix, paramNames); err != nil { + return err + } + if err := validatePipelineArraysIsolated(fmt.Sprintf("param[%s]", param.Name), arrayElement, prefix, arrayParamNames); err != nil { + return err + } + } + } + } + } + return nil +} + +func validatePipelineVariable(name, value, prefix string, vars sets.String) *apis.FieldError { + return substitution.ValidateVariable(name, value, prefix, "task parameter", "pipelinespec.params", vars) +} + +func validatePipelineNoArrayReferenced(name, value, prefix string, vars sets.String) *apis.FieldError { + return substitution.ValidateVariableProhibited(name, value, prefix, "task parameter", "pipelinespec.params", vars) +} + +func validatePipelineArraysIsolated(name, value, prefix string, vars sets.String) *apis.FieldError { + return substitution.ValidateVariableIsolated(name, value, prefix, "task parameter", "pipelinespec.params", vars) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipelinerun_conversion.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipelinerun_conversion.go new file mode 100644 index 0000000000..995f444e2c --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipelinerun_conversion.go @@ -0,0 +1,116 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "knative.dev/pkg/apis" +) + +var _ apis.Convertible = (*PipelineRun)(nil) + +// ConvertTo implements api.Convertible +func (pr *PipelineRun) ConvertTo(ctx context.Context, obj apis.Convertible) error { + switch sink := obj.(type) { + case *v1beta1.PipelineRun: + sink.ObjectMeta = pr.ObjectMeta + if err := pr.Spec.ConvertTo(ctx, &sink.Spec); err != nil { + return err + } + sink.Status = pr.Status + + spec := &v1beta1.PipelineSpec{} + if err := deserializeFinally(&sink.ObjectMeta, spec); err != nil { + return err + } + if len(spec.Finally) > 0 { + if sink.Spec.PipelineSpec == nil { + sink.Spec.PipelineSpec = spec + } else { + sink.Spec.PipelineSpec.Finally = spec.Finally + } + } + return nil + default: + return fmt.Errorf("unknown version, got: %T", sink) + } +} + +// ConvertTo implements api.Convertible +func (prs *PipelineRunSpec) ConvertTo(ctx context.Context, sink *v1beta1.PipelineRunSpec) error { + sink.PipelineRef = prs.PipelineRef + if prs.PipelineSpec != nil { + sink.PipelineSpec = &v1beta1.PipelineSpec{} + if err := prs.PipelineSpec.ConvertTo(ctx, sink.PipelineSpec); err != nil { + return err + } + } + sink.Resources = prs.Resources + sink.Params = prs.Params + sink.ServiceAccountName = prs.ServiceAccountName + sink.ServiceAccountNames = prs.ServiceAccountNames + sink.Status = prs.Status + sink.Timeout = prs.Timeout + sink.PodTemplate = prs.PodTemplate + sink.Workspaces = prs.Workspaces + return nil +} + +// ConvertFrom implements api.Convertible +func (pr *PipelineRun) ConvertFrom(ctx context.Context, obj apis.Convertible) error { + switch source := obj.(type) { + case *v1beta1.PipelineRun: + pr.ObjectMeta = source.ObjectMeta + if err := pr.Spec.ConvertFrom(ctx, &source.Spec); err != nil { + return err + } + pr.Status = source.Status + + ps := source.Spec.PipelineSpec + if ps != nil && ps.Finally != nil { + if err := serializeFinally(&pr.ObjectMeta, ps.Finally); err != nil { + return err + } + } + return nil + default: + return fmt.Errorf("unknown version, got: %T", pr) + } +} + +// ConvertFrom implements api.Convertible +func (prs *PipelineRunSpec) ConvertFrom(ctx context.Context, source *v1beta1.PipelineRunSpec) error { + prs.PipelineRef = source.PipelineRef + if source.PipelineSpec != nil { + prs.PipelineSpec = &PipelineSpec{} + if err := prs.PipelineSpec.ConvertFrom(ctx, *source.PipelineSpec); err != nil { + return err + } + } + prs.Resources = source.Resources + prs.Params = source.Params + prs.ServiceAccountName = source.ServiceAccountName + prs.ServiceAccountNames = source.ServiceAccountNames + prs.Status = source.Status + prs.Timeout = source.Timeout + prs.PodTemplate = source.PodTemplate + prs.Workspaces = source.Workspaces + return nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipelinerun_defaults.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipelinerun_defaults.go new file mode 100644 index 0000000000..ce1f35ad08 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipelinerun_defaults.go @@ -0,0 +1,55 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + "time" + + "github.com/tektoncd/pipeline/pkg/apis/config" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +var _ apis.Defaultable = (*PipelineRun)(nil) + +// SetDefaults implements apis.Defaultable +func (pr *PipelineRun) SetDefaults(ctx context.Context) { + pr.Spec.SetDefaults(ctx) +} + +// SetDefaults implements apis.Defaultable +func (prs *PipelineRunSpec) SetDefaults(ctx context.Context) { + cfg := config.FromContextOrDefaults(ctx) + if prs.Timeout == nil { + prs.Timeout = &metav1.Duration{Duration: time.Duration(cfg.Defaults.DefaultTimeoutMinutes) * time.Minute} + } + + defaultSA := cfg.Defaults.DefaultServiceAccount + if prs.ServiceAccountName == "" && defaultSA != "" { + prs.ServiceAccountName = defaultSA + } + + defaultPodTemplate := cfg.Defaults.DefaultPodTemplate + if prs.PodTemplate == nil { + prs.PodTemplate = defaultPodTemplate + } + + if prs.PipelineSpec != nil { + prs.PipelineSpec.SetDefaults(ctx) + } +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipelinerun_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipelinerun_types.go new file mode 100644 index 0000000000..2d67bdd76a --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipelinerun_types.go @@ -0,0 +1,223 @@ +/* +Copyright 2019-2020 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "fmt" + + "github.com/tektoncd/pipeline/pkg/apis/config" + "github.com/tektoncd/pipeline/pkg/apis/pipeline" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/clock" + "knative.dev/pkg/apis" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PipelineRun represents a single execution of a Pipeline. PipelineRuns are how +// the graph of Tasks declared in a Pipeline are executed; they specify inputs +// to Pipelines such as parameter values and capture operational aspects of the +// Tasks execution such as service account and tolerations. Creating a +// PipelineRun creates TaskRuns for Tasks in the referenced Pipeline. +// +// +k8s:openapi-gen=true +type PipelineRun struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +optional + Spec PipelineRunSpec `json:"spec,omitempty"` + // +optional + Status PipelineRunStatus `json:"status,omitempty"` +} + +// GetName returns the PipelineRun's name +func (pr *PipelineRun) GetName() string { + return pr.ObjectMeta.GetName() +} + +// PipelineRunSpec defines the desired state of PipelineRun +type PipelineRunSpec struct { + // +optional + PipelineRef *PipelineRef `json:"pipelineRef,omitempty"` + // +optional + PipelineSpec *PipelineSpec `json:"pipelineSpec,omitempty"` + // Resources is a list of bindings specifying which actual instances of + // PipelineResources to use for the resources the Pipeline has declared + // it needs. + Resources []PipelineResourceBinding `json:"resources,omitempty"` + // Params is a list of parameter names and values. + Params []Param `json:"params,omitempty"` + // +optional + ServiceAccountName string `json:"serviceAccountName,omitempty"` + // +optional + ServiceAccountNames []PipelineRunSpecServiceAccountName `json:"serviceAccountNames,omitempty"` + // Used for cancelling a pipelinerun (and maybe more later on) + // +optional + Status PipelineRunSpecStatus `json:"status,omitempty"` + // Time after which the Pipeline times out. Defaults to never. + // Refer to Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + // PodTemplate holds pod specific configuration + PodTemplate *PodTemplate `json:"podTemplate,omitempty"` + // Workspaces holds a set of workspace bindings that must match names + // with those declared in the pipeline. + // +optional + Workspaces []WorkspaceBinding `json:"workspaces,omitempty"` + // TaskRunSpecs holds a set of task specific specs + // +optional + TaskRunSpecs []PipelineTaskRunSpec `json:"taskRunSpecs,omitempty"` +} + +// PipelineRunSpecStatus defines the pipelinerun spec status the user can provide +type PipelineRunSpecStatus = v1beta1.PipelineRunSpecStatus + +const ( + // PipelineRunSpecStatusCancelled indicates that the user wants to cancel the task, + // if not already cancelled or terminated + PipelineRunSpecStatusCancelled = v1beta1.PipelineRunSpecStatusCancelledDeprecated +) + +// PipelineResourceRef can be used to refer to a specific instance of a Resource +type PipelineResourceRef = v1beta1.PipelineResourceRef + +// PipelineRef can be used to refer to a specific instance of a Pipeline. +// Copied from CrossVersionObjectReference: https://github.com/kubernetes/kubernetes/blob/169df7434155cbbc22f1532cba8e0a9588e29ad8/pkg/apis/autoscaling/types.go#L64 +type PipelineRef = v1beta1.PipelineRef + +// PipelineRunStatus defines the observed state of PipelineRun +type PipelineRunStatus = v1beta1.PipelineRunStatus + +// PipelineRunStatusFields holds the fields of PipelineRunStatus' status. +// This is defined separately and inlined so that other types can readily +// consume these fields via duck typing. +type PipelineRunStatusFields = v1beta1.PipelineRunStatusFields + +// PipelineRunTaskRunStatus contains the name of the PipelineTask for this TaskRun and the TaskRun's Status +type PipelineRunTaskRunStatus = v1beta1.PipelineRunTaskRunStatus + +// PipelineRunSpecServiceAccountName can be used to configure specific +// ServiceAccountName for a concrete Task +type PipelineRunSpecServiceAccountName = v1beta1.PipelineRunSpecServiceAccountName + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PipelineRunList contains a list of PipelineRun +type PipelineRunList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []PipelineRun `json:"items,omitempty"` +} + +// PipelineTaskRun reports the results of running a step in the Task. Each +// task has the potential to succeed or fail (based on the exit code) +// and produces logs. +type PipelineTaskRun = v1beta1.PipelineTaskRun + +// GetGroupVersionKind implements kmeta.OwnerRefable. +func (*PipelineRun) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind(pipeline.PipelineRunControllerName) +} + +// IsDone returns true if the PipelineRun's status indicates that it is done. +func (pr *PipelineRun) IsDone() bool { + return !pr.Status.GetCondition(apis.ConditionSucceeded).IsUnknown() +} + +// HasStarted function check whether pipelinerun has valid start time set in its status +func (pr *PipelineRun) HasStarted() bool { + return pr.Status.StartTime != nil && !pr.Status.StartTime.IsZero() +} + +// IsCancelled returns true if the PipelineRun's spec status is set to Cancelled state +func (pr *PipelineRun) IsCancelled() bool { + return pr.Spec.Status == PipelineRunSpecStatusCancelled +} + +// GetRunKey return the pipelinerun key for timeout handler map +func (pr *PipelineRun) GetRunKey() string { + // The address of the pointer is a threadsafe unique identifier for the pipelinerun + return fmt.Sprintf("%s/%p", pipeline.PipelineRunControllerName, pr) +} + +// IsTimedOut returns true if a pipelinerun has exceeded its spec.Timeout based on its status.Timeout +func (pr *PipelineRun) IsTimedOut(c clock.PassiveClock) bool { + pipelineTimeout := pr.Spec.Timeout + startTime := pr.Status.StartTime + + if !startTime.IsZero() && pipelineTimeout != nil { + timeout := pipelineTimeout.Duration + if timeout == config.NoTimeoutDuration { + return false + } + runtime := c.Since(startTime.Time) + if runtime > timeout { + return true + } + } + return false +} + +// GetServiceAccountName returns the service account name for a given +// PipelineTask if configured, otherwise it returns the PipelineRun's serviceAccountName. +func (pr *PipelineRun) GetServiceAccountName(pipelineTaskName string) string { + serviceAccountName := pr.Spec.ServiceAccountName + for _, sa := range pr.Spec.ServiceAccountNames { + if sa.TaskName == pipelineTaskName { + serviceAccountName = sa.ServiceAccountName + } + } + return serviceAccountName +} + +// HasVolumeClaimTemplate returns true if PipelineRun contains volumeClaimTemplates that is +// used for creating PersistentVolumeClaims with an OwnerReference for each run +func (pr *PipelineRun) HasVolumeClaimTemplate() bool { + for _, ws := range pr.Spec.Workspaces { + if ws.VolumeClaimTemplate != nil { + return true + } + } + return false +} + +// PipelineTaskRunSpec holds task specific specs +type PipelineTaskRunSpec struct { + PipelineTaskName string `json:"pipelineTaskName,omitempty"` + TaskServiceAccountName string `json:"taskServiceAccountName,omitempty"` + TaskPodTemplate *PodTemplate `json:"taskPodTemplate,omitempty"` +} + +// GetTaskRunSpecs returns the task specific spec for a given +// PipelineTask if configured, otherwise it returns the PipelineRun's default. +func (pr *PipelineRun) GetTaskRunSpecs(pipelineTaskName string) (string, *PodTemplate) { + serviceAccountName := pr.GetServiceAccountName(pipelineTaskName) + taskPodTemplate := pr.Spec.PodTemplate + for _, task := range pr.Spec.TaskRunSpecs { + if task.PipelineTaskName == pipelineTaskName { + taskPodTemplate = task.TaskPodTemplate + serviceAccountName = task.TaskServiceAccountName + } + } + return serviceAccountName, taskPodTemplate +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipelinerun_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipelinerun_validation.go new file mode 100644 index 0000000000..f6396cb433 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pipelinerun_validation.go @@ -0,0 +1,85 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + + "github.com/tektoncd/pipeline/pkg/apis/validate" + "k8s.io/apimachinery/pkg/api/equality" + "knative.dev/pkg/apis" +) + +var _ apis.Validatable = (*PipelineRun)(nil) + +// Validate pipelinerun +func (pr *PipelineRun) Validate(ctx context.Context) *apis.FieldError { + if err := validate.ObjectMetadata(pr.GetObjectMeta()).ViaField("metadata"); err != nil { + return err + } + if apis.IsInDelete(ctx) { + return nil + } + return pr.Spec.Validate(ctx) +} + +// Validate pipelinerun spec +func (ps *PipelineRunSpec) Validate(ctx context.Context) *apis.FieldError { + if equality.Semantic.DeepEqual(ps, &PipelineRunSpec{}) { + return apis.ErrMissingField("spec") + } + + // can't have both pipelineRef and pipelineSpec at the same time + if (ps.PipelineRef != nil && ps.PipelineRef.Name != "") && ps.PipelineSpec != nil { + return apis.ErrDisallowedFields("spec.pipelineref", "spec.pipelinespec") + } + + // Check that one of PipelineRef and PipelineSpec is present + if (ps.PipelineRef == nil || (ps.PipelineRef != nil && ps.PipelineRef.Name == "")) && ps.PipelineSpec == nil { + return apis.ErrMissingField("spec.pipelineref.name", "spec.pipelinespec") + } + + // Validate PipelineSpec if it's present + if ps.PipelineSpec != nil { + if err := ps.PipelineSpec.Validate(ctx); err != nil { + return err + } + } + + if ps.Timeout != nil { + // timeout should be a valid duration of at least 0. + if ps.Timeout.Duration < 0 { + return apis.ErrInvalidValue(fmt.Sprintf("%s should be >= 0", ps.Timeout.Duration.String()), "spec.timeout") + } + } + + if ps.Workspaces != nil { + wsNames := make(map[string]int) + for idx, ws := range ps.Workspaces { + if prevIdx, alreadyExists := wsNames[ws.Name]; alreadyExists { + return &apis.FieldError{ + Message: fmt.Sprintf("workspace %q provided by pipelinerun more than once, at index %d and %d", ws.Name, prevIdx, idx), + Paths: []string{"spec.workspaces"}, + } + } + wsNames[ws.Name] = idx + } + } + + return nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pod.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pod.go new file mode 100644 index 0000000000..156c66b2d5 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/pod.go @@ -0,0 +1,8 @@ +package v1alpha1 + +import ( + "github.com/tektoncd/pipeline/pkg/apis/pipeline/pod" +) + +// PodTemplate holds pod specific configuration +type PodTemplate = pod.Template diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/register.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/register.go new file mode 100644 index 0000000000..78b59bfdce --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/register.go @@ -0,0 +1,68 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "github.com/tektoncd/pipeline/pkg/apis/pipeline" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: pipeline.GroupName, Version: "v1alpha1"} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + + // AddToScheme adds Build types to the scheme. + AddToScheme = schemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &Task{}, + &TaskList{}, + &Condition{}, + &ConditionList{}, + &ClusterTask{}, + &ClusterTaskList{}, + &TaskRun{}, + &TaskRunList{}, + &Pipeline{}, + &PipelineList{}, + &PipelineRun{}, + &PipelineRunList{}, + &PipelineResource{}, + &PipelineResourceList{}, + &Run{}, + &RunList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/resource_paths.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/resource_paths.go new file mode 100644 index 0000000000..6aa94913b0 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/resource_paths.go @@ -0,0 +1,40 @@ +/* + Copyright 2019 The Tekton Authors + + 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. +*/ + +package v1alpha1 + +import "path/filepath" + +// InputResourcePath returns the path where the given input resource +// will get mounted in a Pod +func InputResourcePath(r ResourceDeclaration) string { + return path("/workspace", r) +} + +// OutputResourcePath returns the path to the output resource in a Pod +func OutputResourcePath(r ResourceDeclaration) string { + return path("/workspace/output", r) +} + +func path(root string, r ResourceDeclaration) string { + if r.TargetPath != "" { + if filepath.IsAbs(r.TargetPath) { + return r.TargetPath + } + return filepath.Join("/workspace", r.TargetPath) + } + return filepath.Join(root, r.Name) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/resource_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/resource_types.go new file mode 100644 index 0000000000..d987494e17 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/resource_types.go @@ -0,0 +1,109 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "fmt" + + "github.com/google/go-cmp/cmp" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" +) + +// PipelineResourceInterface interface to be implemented by different PipelineResource types +type PipelineResourceInterface interface { + // GetName returns the name of this PipelineResource instance. + GetName() string + // GetType returns the type of this PipelineResource (often a super type, e.g. in the case of storage). + GetType() PipelineResourceType + // Replacements returns all the attributes that this PipelineResource has that + // can be used for variable replacement. + Replacements() map[string]string + // GetOutputTaskModifier returns the TaskModifier instance that should be used on a Task + // in order to add this kind of resource when it is being used as an output. + GetOutputTaskModifier(ts *TaskSpec, path string) (TaskModifier, error) + // GetInputTaskModifier returns the TaskModifier instance that should be used on a Task + // in order to add this kind of resource when it is being used as an input. + GetInputTaskModifier(ts *TaskSpec, path string) (TaskModifier, error) +} + +// TaskModifier is an interface to be implemented by different PipelineResources +type TaskModifier = v1beta1.TaskModifier + +// InternalTaskModifier implements TaskModifier for resources that are built-in to Tekton Pipelines. +type InternalTaskModifier = v1beta1.InternalTaskModifier + +func checkStepNotAlreadyAdded(s Step, steps []Step) error { + for _, step := range steps { + if s.Name == step.Name { + return fmt.Errorf("Step %s cannot be added again", step.Name) + } + } + return nil +} + +// ApplyTaskModifier applies a modifier to the task by appending and prepending steps and volumes. +// If steps with the same name exist in ts an error will be returned. If identical Volumes have +// been added, they will not be added again. If Volumes with the same name but different contents +// have been added, an error will be returned. +// FIXME(vdemeester) de-duplicate this +func ApplyTaskModifier(ts *TaskSpec, tm TaskModifier) error { + steps := tm.GetStepsToPrepend() + for _, step := range steps { + if err := checkStepNotAlreadyAdded(step, ts.Steps); err != nil { + return err + } + } + ts.Steps = append(steps, ts.Steps...) + + steps = tm.GetStepsToAppend() + for _, step := range steps { + if err := checkStepNotAlreadyAdded(step, ts.Steps); err != nil { + return err + } + } + ts.Steps = append(ts.Steps, steps...) + + volumes := tm.GetVolumes() + for _, volume := range volumes { + var alreadyAdded bool + for _, v := range ts.Volumes { + if volume.Name == v.Name { + // If a Volume with the same name but different contents has already been added, we can't add both + if d := cmp.Diff(volume, v); d != "" { + return fmt.Errorf("tried to add volume %s already added but with different contents", volume.Name) + } + // If an identical Volume has already been added, don't add it again + alreadyAdded = true + } + } + if !alreadyAdded { + ts.Volumes = append(ts.Volumes, volume) + } + } + + return nil +} + +// PipelineResourceBinding connects a reference to an instance of a PipelineResource +// with a PipelineResource dependency that the Pipeline has declared +type PipelineResourceBinding = v1beta1.PipelineResourceBinding + +// PipelineResourceResult used to export the image name and digest as json +type PipelineResourceResult = v1beta1.PipelineResourceResult + +// ResultType used to find out whether a PipelineResourceResult is from a task result or not +type ResultType = v1beta1.ResultType diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/run_defaults.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/run_defaults.go new file mode 100644 index 0000000000..5511a36cdf --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/run_defaults.go @@ -0,0 +1,45 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + + "github.com/tektoncd/pipeline/pkg/apis/config" + "knative.dev/pkg/apis" +) + +var _ apis.Defaultable = (*Run)(nil) + +// SetDefaults implements apis.Defaultable +func (r *Run) SetDefaults(ctx context.Context) { + ctx = apis.WithinParent(ctx, r.ObjectMeta) + r.Spec.SetDefaults(apis.WithinSpec(ctx)) +} + +// SetDefaults implements apis.Defaultable +func (rs *RunSpec) SetDefaults(ctx context.Context) { + cfg := config.FromContextOrDefaults(ctx) + defaultSA := cfg.Defaults.DefaultServiceAccount + if rs.ServiceAccountName == "" && defaultSA != "" { + rs.ServiceAccountName = defaultSA + } + defaultPodTemplate := cfg.Defaults.DefaultPodTemplate + if rs.PodTemplate == nil { + rs.PodTemplate = defaultPodTemplate + } +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/run_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/run_types.go new file mode 100644 index 0000000000..74e3e0b09d --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/run_types.go @@ -0,0 +1,234 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "fmt" + "time" + + apisconfig "github.com/tektoncd/pipeline/pkg/apis/config" + "github.com/tektoncd/pipeline/pkg/apis/pipeline" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + runv1alpha1 "github.com/tektoncd/pipeline/pkg/apis/run/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/clock" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" +) + +// EmbeddedRunSpec allows custom task definitions to be embedded +type EmbeddedRunSpec struct { + runtime.TypeMeta `json:",inline"` + + // +optional + Metadata v1beta1.PipelineTaskMetadata `json:"metadata,omitempty"` + + // Spec is a specification of a custom task + // +optional + Spec runtime.RawExtension `json:"spec,omitempty"` +} + +// RunSpec defines the desired state of Run +type RunSpec struct { + // +optional + Ref *TaskRef `json:"ref,omitempty"` + + // Spec is a specification of a custom task + // +optional + Spec *EmbeddedRunSpec `json:"spec,omitempty"` + + // +optional + Params []v1beta1.Param `json:"params,omitempty"` + + // Used for cancelling a run (and maybe more later on) + // +optional + Status RunSpecStatus `json:"status,omitempty"` + + // Used for propagating retries count to custom tasks + // +optional + Retries int `json:"retries,omitempty"` + + // +optional + ServiceAccountName string `json:"serviceAccountName"` + + // PodTemplate holds pod specific configuration + // +optional + PodTemplate *PodTemplate `json:"podTemplate,omitempty"` + + // Time after which the custom-task times out. + // Refer Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + + // Workspaces is a list of WorkspaceBindings from volumes to workspaces. + // +optional + Workspaces []v1beta1.WorkspaceBinding `json:"workspaces,omitempty"` +} + +// RunSpecStatus defines the taskrun spec status the user can provide +type RunSpecStatus string + +const ( + // RunSpecStatusCancelled indicates that the user wants to cancel the run, + // if not already cancelled or terminated + RunSpecStatusCancelled RunSpecStatus = "RunCancelled" +) + +// GetParam gets the Param from the RunSpec with the given name +// TODO(jasonhall): Move this to a Params type so other code can use it? +func (rs RunSpec) GetParam(name string) *v1beta1.Param { + for _, p := range rs.Params { + if p.Name == name { + return &p + } + } + return nil +} + +const ( + // RunReasonCancelled must be used in the Condition Reason to indicate that a Run was cancelled. + RunReasonCancelled = "RunCancelled" + // RunReasonTimedOut must be used in the Condition Reason to indicate that a Run was timed out. + RunReasonTimedOut = "RunTimedOut" + // RunReasonWorkspaceNotSupported can be used in the Condition Reason to indicate that the + // Run contains a workspace which is not supported by this custom task. + RunReasonWorkspaceNotSupported = "RunWorkspaceNotSupported" + // RunReasonPodTemplateNotSupported can be used in the Condition Reason to indicate that the + // Run contains a pod template which is not supported by this custom task. + RunReasonPodTemplateNotSupported = "RunPodTemplateNotSupported" +) + +// RunStatus defines the observed state of Run. +type RunStatus = runv1alpha1.RunStatus + +var runCondSet = apis.NewBatchConditionSet() + +// GetConditionSet retrieves the condition set for this resource. Implements +// the KRShaped interface. +func (r *Run) GetConditionSet() apis.ConditionSet { return runCondSet } + +// GetStatus retrieves the status of the Parallel. Implements the KRShaped +// interface. +func (r *Run) GetStatus() *duckv1.Status { return &r.Status.Status } + +// RunStatusFields holds the fields of Run's status. This is defined +// separately and inlined so that other types can readily consume these fields +// via duck typing. +type RunStatusFields = runv1alpha1.RunStatusFields + +// RunResult used to describe the results of a task +type RunResult = runv1alpha1.RunResult + +// +genclient +// +genreconciler +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Run represents a single execution of a Custom Task. +// +// +k8s:openapi-gen=true +type Run struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +optional + Spec RunSpec `json:"spec,omitempty"` + // +optional + Status RunStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RunList contains a list of Run +type RunList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []Run `json:"items"` +} + +// GetStatusCondition returns the task run status as a ConditionAccessor +func (r *Run) GetStatusCondition() apis.ConditionAccessor { + return &r.Status +} + +// GetGroupVersionKind implements kmeta.OwnerRefable. +func (*Run) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind(pipeline.RunControllerName) +} + +// HasPipelineRunOwnerReference returns true of Run has +// owner reference of type PipelineRun +func (r *Run) HasPipelineRunOwnerReference() bool { + for _, ref := range r.GetOwnerReferences() { + if ref.Kind == pipeline.PipelineRunControllerName { + return true + } + } + return false +} + +// IsCancelled returns true if the Run's spec status is set to Cancelled state +func (r *Run) IsCancelled() bool { + return r.Spec.Status == RunSpecStatusCancelled +} + +// IsDone returns true if the Run's status indicates that it is done. +func (r *Run) IsDone() bool { + return !r.Status.GetCondition(apis.ConditionSucceeded).IsUnknown() +} + +// HasStarted function check whether taskrun has valid start time set in its status +func (r *Run) HasStarted() bool { + return r.Status.StartTime != nil && !r.Status.StartTime.IsZero() +} + +// IsSuccessful returns true if the Run's status indicates that it is done. +func (r *Run) IsSuccessful() bool { + return r.Status.GetCondition(apis.ConditionSucceeded).IsTrue() +} + +// GetRunKey return the run's key for timeout handler map +func (r *Run) GetRunKey() string { + // The address of the pointer is a threadsafe unique identifier for the run + return fmt.Sprintf("%s/%p", "Run", r) +} + +// HasTimedOut returns true if the Run's running time is beyond the allowed timeout +func (r *Run) HasTimedOut(c clock.PassiveClock) bool { + if r.Status.StartTime == nil || r.Status.StartTime.IsZero() { + return false + } + timeout := r.GetTimeout() + // If timeout is set to 0 or defaulted to 0, there is no timeout. + if timeout == apisconfig.NoTimeoutDuration { + return false + } + runtime := c.Since(r.Status.StartTime.Time) + return runtime > timeout +} + +// GetTimeout returns the timeout for this run, or the default if not configured +func (r *Run) GetTimeout() time.Duration { + // Use the platform default if no timeout is set + if r.Spec.Timeout == nil { + return apisconfig.DefaultTimeoutMinutes * time.Minute + } + return r.Spec.Timeout.Duration +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/run_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/run_validation.go new file mode 100644 index 0000000000..483c918de6 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/run_validation.go @@ -0,0 +1,74 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + + "github.com/tektoncd/pipeline/pkg/apis/validate" + "k8s.io/apimachinery/pkg/api/equality" + "knative.dev/pkg/apis" +) + +var _ apis.Validatable = (*Run)(nil) + +// Validate taskrun +func (r *Run) Validate(ctx context.Context) *apis.FieldError { + if err := validate.ObjectMetadata(r.GetObjectMeta()).ViaField("metadata"); err != nil { + return err + } + if apis.IsInDelete(ctx) { + return nil + } + return r.Spec.Validate(ctx) +} + +// Validate Run spec +func (rs *RunSpec) Validate(ctx context.Context) *apis.FieldError { + // this covers the case rs.Ref == nil && rs.Spec == nil + if equality.Semantic.DeepEqual(rs, &RunSpec{}) { + return apis.ErrMissingField("spec") + } + + if rs.Ref != nil && rs.Spec != nil { + return apis.ErrMultipleOneOf("spec.ref", "spec.spec") + } + if rs.Ref == nil && rs.Spec == nil { + return apis.ErrMissingOneOf("spec.ref", "spec.spec") + } + if rs.Ref != nil { + if rs.Ref.APIVersion == "" { + return apis.ErrMissingField("spec.ref.apiVersion") + } + if rs.Ref.Kind == "" { + return apis.ErrMissingField("spec.ref.kind") + } + } + if rs.Spec != nil { + if rs.Spec.APIVersion == "" { + return apis.ErrMissingField("spec.spec.apiVersion") + } + if rs.Spec.Kind == "" { + return apis.ErrMissingField("spec.spec.kind") + } + } + if err := validateParameters("spec.params", rs.Params); err != nil { + return err + } + + return validateWorkspaceBindings(ctx, rs.Workspaces) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/step_replacements.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/step_replacements.go new file mode 100644 index 0000000000..5a484579ec --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/step_replacements.go @@ -0,0 +1,27 @@ +/* + Copyright 2019 The Tekton Authors + + 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. +*/ + +package v1alpha1 + +import ( + "github.com/tektoncd/pipeline/pkg/substitution" +) + +// ApplyStepReplacements applies variable interpolation on a Step. +func ApplyStepReplacements(step *Step, stringReplacements map[string]string, arrayReplacements map[string][]string) { + step.Script = substitution.ApplyReplacements(step.Script, stringReplacements) + ApplyContainerReplacements(&step.Container, stringReplacements, arrayReplacements) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/storage_resource.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/storage_resource.go new file mode 100644 index 0000000000..6f636bfb71 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/storage_resource.go @@ -0,0 +1,24 @@ +/* +Copyright 2019-2020 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + resource "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" +) + +// PipelineResourceTypeGCS is the subtype for the GCSResources, which is backed by a GCS blob/directory. +const PipelineResourceTypeGCS PipelineResourceType = resource.PipelineResourceTypeGCS diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_conversion.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_conversion.go new file mode 100644 index 0000000000..027d1493a5 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_conversion.go @@ -0,0 +1,127 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "knative.dev/pkg/apis" +) + +var _ apis.Convertible = (*Task)(nil) + +// ConvertTo implements api.Convertible +func (t *Task) ConvertTo(ctx context.Context, obj apis.Convertible) error { + switch sink := obj.(type) { + case *v1beta1.Task: + sink.ObjectMeta = t.ObjectMeta + return t.Spec.ConvertTo(ctx, &sink.Spec) + default: + return fmt.Errorf("unknown version, got: %T", sink) + } +} + +// ConvertTo implements api.Convertible +func (ts *TaskSpec) ConvertTo(ctx context.Context, sink *v1beta1.TaskSpec) error { + sink.Steps = ts.Steps + sink.Volumes = ts.Volumes + sink.StepTemplate = ts.StepTemplate + sink.Sidecars = ts.Sidecars + sink.Workspaces = ts.Workspaces + sink.Results = ts.Results + sink.Resources = ts.Resources + sink.Params = ts.Params + sink.Description = ts.Description + if ts.Inputs != nil { + if len(ts.Inputs.Params) > 0 && len(ts.Params) > 0 { + // This shouldn't happen as it shouldn't pass validation + return apis.ErrMultipleOneOf("inputs.params", "params") + } + if len(ts.Inputs.Params) > 0 { + sink.Params = make([]v1beta1.ParamSpec, len(ts.Inputs.Params)) + for i, param := range ts.Inputs.Params { + sink.Params[i] = *param.DeepCopy() + } + } + if len(ts.Inputs.Resources) > 0 { + if sink.Resources == nil { + sink.Resources = &v1beta1.TaskResources{} + } + if len(ts.Inputs.Resources) > 0 && ts.Resources != nil && len(ts.Resources.Inputs) > 0 { + // This shouldn't happen as it shouldn't pass validation but just in case + return apis.ErrMultipleOneOf("inputs.resources", "resources.inputs") + } + sink.Resources.Inputs = make([]v1beta1.TaskResource, len(ts.Inputs.Resources)) + for i, resource := range ts.Inputs.Resources { + sink.Resources.Inputs[i] = v1beta1.TaskResource{ResourceDeclaration: v1beta1.ResourceDeclaration{ + Name: resource.Name, + Type: resource.Type, + Description: resource.Description, + TargetPath: resource.TargetPath, + Optional: resource.Optional, + }} + } + } + } + if ts.Outputs != nil && len(ts.Outputs.Resources) > 0 { + if sink.Resources == nil { + sink.Resources = &v1beta1.TaskResources{} + } + if len(ts.Outputs.Resources) > 0 && ts.Resources != nil && len(ts.Resources.Outputs) > 0 { + // This shouldn't happen as it shouldn't pass validation but just in case + return apis.ErrMultipleOneOf("outputs.resources", "resources.outputs") + } + sink.Resources.Outputs = make([]v1beta1.TaskResource, len(ts.Outputs.Resources)) + for i, resource := range ts.Outputs.Resources { + sink.Resources.Outputs[i] = v1beta1.TaskResource{ResourceDeclaration: v1beta1.ResourceDeclaration{ + Name: resource.Name, + Type: resource.Type, + Description: resource.Description, + TargetPath: resource.TargetPath, + Optional: resource.Optional, + }} + } + } + return nil +} + +// ConvertFrom implements api.Convertible +func (t *Task) ConvertFrom(ctx context.Context, obj apis.Convertible) error { + switch source := obj.(type) { + case *v1beta1.Task: + t.ObjectMeta = source.ObjectMeta + return t.Spec.ConvertFrom(ctx, &source.Spec) + default: + return fmt.Errorf("unknown version, got: %T", t) + } +} + +// ConvertFrom implements api.Convertible +func (ts *TaskSpec) ConvertFrom(ctx context.Context, source *v1beta1.TaskSpec) error { + ts.Steps = source.Steps + ts.Volumes = source.Volumes + ts.StepTemplate = source.StepTemplate + ts.Sidecars = source.Sidecars + ts.Workspaces = source.Workspaces + ts.Results = source.Results + ts.Params = source.Params + ts.Resources = source.Resources + ts.Description = source.Description + return nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_defaults.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_defaults.go new file mode 100644 index 0000000000..d61bc7b7cf --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_defaults.go @@ -0,0 +1,47 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + + "knative.dev/pkg/apis" +) + +var _ apis.Defaultable = (*Task)(nil) + +// SetDefaults implements apis.Defaultable +func (t *Task) SetDefaults(ctx context.Context) { + t.Spec.SetDefaults(ctx) +} + +// SetDefaults set any defaults for the task spec +func (ts *TaskSpec) SetDefaults(ctx context.Context) { + for i := range ts.Params { + ts.Params[i].SetDefaults(ctx) + } + if ts.Inputs != nil { + ts.Inputs.SetDefaults(ctx) + } +} + +// SetDefaults implements apis.Defaultable +func (inputs *Inputs) SetDefaults(ctx context.Context) { + for i := range inputs.Params { + inputs.Params[i].SetDefaults(ctx) + } +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_interface.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_interface.go new file mode 100644 index 0000000000..d545dddd31 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_interface.go @@ -0,0 +1,26 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// TaskObject is implemented by Task and ClusterTask +type TaskObject interface { + TaskMetadata() metav1.ObjectMeta + TaskSpec() TaskSpec + Copy() TaskObject +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_types.go new file mode 100644 index 0000000000..1121e93c78 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_types.go @@ -0,0 +1,143 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" +) + +const ( + // TaskRunResultType default task run result value + TaskRunResultType ResultType = v1beta1.TaskRunResultType + // PipelineResourceResultType default pipeline result value + PipelineResourceResultType ResultType = v1beta1.PipelineResourceResultType + // UnknownResultType default unknown result type value + UnknownResultType ResultType = v1beta1.UnknownResultType +) + +// TaskSpec returns the task's spec +func (t *Task) TaskSpec() TaskSpec { + return t.Spec +} + +// TaskMetadata returns the task's ObjectMeta +func (t *Task) TaskMetadata() metav1.ObjectMeta { + return t.ObjectMeta +} + +// Copy returns a deep copy of the task +func (t *Task) Copy() TaskObject { + return t.DeepCopy() +} + +// TaskSpec defines the desired state of Task. +type TaskSpec struct { + v1beta1.TaskSpec `json:",inline"` + + // Inputs is an optional set of parameters and resources which must be + // supplied by the user when a Task is executed by a TaskRun. + // +optional + Inputs *Inputs `json:"inputs,omitempty"` + // Outputs is an optional set of resources and results produced when this + // Task is run. + // +optional + Outputs *Outputs `json:"outputs,omitempty"` +} + +// TaskResult used to describe the results of a task +type TaskResult = v1beta1.TaskResult + +// Step embeds the Container type, which allows it to include fields not +// provided by Container. +type Step = v1beta1.Step + +// Sidecar has nearly the same data structure as Step, consisting of a Container and an optional Script, but does not have the ability to timeout. +type Sidecar = v1beta1.Sidecar + +// +genclient +// +genclient:noStatus +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Task represents a collection of sequential steps that are run as part of a +// Pipeline using a set of inputs and producing a set of outputs. Tasks execute +// when TaskRuns are created that provide the input parameters and resources and +// output resources the Task requires. +// +// +k8s:openapi-gen=true +type Task struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata"` + + // Spec holds the desired state of the Task from the client + // +optional + Spec TaskSpec `json:"spec"` +} + +// Inputs are the requirements that a task needs to run a Build. +type Inputs struct { + // Resources is a list of the input resources required to run the task. + // Resources are represented in TaskRuns as bindings to instances of + // PipelineResources. + // +optional + Resources []TaskResource `json:"resources,omitempty"` + // Params is a list of input parameters required to run the task. Params + // must be supplied as inputs in TaskRuns unless they declare a default + // value. + // +optional + Params []ParamSpec `json:"params,omitempty"` +} + +// TaskResource defines an input or output Resource declared as a requirement +// by a Task. The Name field will be used to refer to these Resources within +// the Task definition, and when provided as an Input, the Name will be the +// path to the volume mounted containing this Resource as an input (e.g. +// an input Resource named `workspace` will be mounted at `/workspace`). +type TaskResource = v1beta1.TaskResource + +// Outputs allow a task to declare what data the Build/Task will be producing, +// i.e. results such as logs and artifacts such as images. +type Outputs struct { + // +optional + Results []TestResult `json:"results,omitempty"` + // +optional + Resources []TaskResource `json:"resources,omitempty"` +} + +// TestResult allows a task to specify the location where test logs +// can be found and what format they will be in. +type TestResult struct { + // Name declares the name by which a result is referenced in the Task's + // definition. Results may be referenced by name in the definition of a + // Task's steps. + Name string `json:"name"` + // TODO: maybe this is an enum with types like "go test", "junit", etc. + Format string `json:"format"` + Path string `json:"path"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// TaskList contains a list of Task +type TaskList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []Task `json:"items"` +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_validation.go new file mode 100644 index 0000000000..94a13a6918 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/task_validation.go @@ -0,0 +1,437 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + "path/filepath" + "strings" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "github.com/tektoncd/pipeline/pkg/apis/validate" + "github.com/tektoncd/pipeline/pkg/substitution" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/validation" + "knative.dev/pkg/apis" +) + +var _ apis.Validatable = (*Task)(nil) + +// Validate implements apis.Validatable +func (t *Task) Validate(ctx context.Context) *apis.FieldError { + if err := validate.ObjectMetadata(t.GetObjectMeta()); err != nil { + return err.ViaField("metadata") + } + if apis.IsInDelete(ctx) { + return nil + } + return t.Spec.Validate(ctx) +} + +// Validate implements apis.Validatable +func (ts *TaskSpec) Validate(ctx context.Context) *apis.FieldError { + + if len(ts.Steps) == 0 { + return apis.ErrMissingField("steps") + } + if err := ValidateVolumes(ts.Volumes).ViaField("volumes"); err != nil { + return err + } + if err := validateDeclaredWorkspaces(ts.Workspaces, ts.Steps, ts.StepTemplate); err != nil { + return err + } + mergedSteps, err := v1beta1.MergeStepsWithStepTemplate(ts.StepTemplate, ts.Steps) + if err != nil { + return &apis.FieldError{ + Message: fmt.Sprintf("error merging step template and steps: %s", err), + Paths: []string{"stepTemplate"}, + } + } + + if err := validateSteps(mergedSteps).ViaField("steps"); err != nil { + return err + } + + if ts.Inputs != nil { + if len(ts.Inputs.Params) > 0 && len(ts.Params) > 0 { + return apis.ErrMultipleOneOf("inputs.params", "params") + } + if ts.Resources != nil && len(ts.Resources.Inputs) > 0 && len(ts.Inputs.Resources) > 0 { + return apis.ErrMultipleOneOf("inputs.resources", "resources.inputs") + } + } + if ts.Outputs != nil { + if ts.Resources != nil && len(ts.Resources.Outputs) > 0 && len(ts.Outputs.Resources) > 0 { + return apis.ErrMultipleOneOf("outputs.resources", "resources.outputs") + } + } + + // Validate Resources declaration + if err := ts.Resources.Validate(ctx); err != nil { + return err + } + // Validate that the parameters type are correct + if err := v1beta1.ValidateParameterTypes(ts.Params); err != nil { + return err + } + + // A task doesn't have to have inputs or outputs, but if it does they must be valid. + // A task can't duplicate input or output names. + // Deprecated + if ts.Inputs != nil { + for _, resource := range ts.Inputs.Resources { + if err := validateResourceType(resource, fmt.Sprintf("taskspec.Inputs.Resources.%s.Type", resource.Name)); err != nil { + return err + } + } + if err := checkForDuplicates(ts.Inputs.Resources, "taskspec.Inputs.Resources.Name"); err != nil { + return err + } + if err := validateInputParameterTypes(ts.Inputs); err != nil { + return err + } + } + // Deprecated + if ts.Outputs != nil { + for _, resource := range ts.Outputs.Resources { + if err := validateResourceType(resource, fmt.Sprintf("taskspec.Outputs.Resources.%s.Type", resource.Name)); err != nil { + return err + } + } + if err := checkForDuplicates(ts.Outputs.Resources, "taskspec.Outputs.Resources.Name"); err != nil { + return err + } + } + + // Validate task step names + for _, step := range ts.Steps { + if errs := validation.IsDNS1123Label(step.Name); step.Name != "" && len(errs) > 0 { + return &apis.FieldError{ + Message: fmt.Sprintf("invalid value %q", step.Name), + Paths: []string{"taskspec.steps.name"}, + Details: "Task step name must be a valid DNS Label, For more info refer to https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + } + } + } + + if err := v1beta1.ValidateParameterVariables(ts.Steps, ts.Params); err != nil { + return err + } + // Deprecated + if err := validateInputParameterVariables(ts.Steps, ts.Inputs, ts.Params); err != nil { + return err + } + + if err := v1beta1.ValidateResourcesVariables(ts.Steps, ts.Resources); err != nil { + return err + } + // Deprecated + return validateResourceVariables(ts.Steps, ts.Inputs, ts.Outputs, ts.Resources) +} + +// validateDeclaredWorkspaces will make sure that the declared workspaces do not try to use +// a mount path which conflicts with any other declared workspaces, with the explicitly +// declared volume mounts, or with the stepTemplate. The names must also be unique. +func validateDeclaredWorkspaces(workspaces []WorkspaceDeclaration, steps []Step, stepTemplate *corev1.Container) *apis.FieldError { + mountPaths := sets.NewString() + for _, step := range steps { + for _, vm := range step.VolumeMounts { + mountPaths.Insert(filepath.Clean(vm.MountPath)) + } + } + if stepTemplate != nil { + for _, vm := range stepTemplate.VolumeMounts { + mountPaths.Insert(filepath.Clean(vm.MountPath)) + } + } + + wsNames := sets.NewString() + for _, w := range workspaces { + // Workspace names must be unique + if wsNames.Has(w.Name) { + return &apis.FieldError{ + Message: fmt.Sprintf("workspace name %q must be unique", w.Name), + Paths: []string{"workspaces.name"}, + } + } + wsNames.Insert(w.Name) + // Workspaces must not try to use mount paths that are already used + mountPath := filepath.Clean(w.GetMountPath()) + if mountPaths.Has(mountPath) { + return &apis.FieldError{ + Message: fmt.Sprintf("workspace mount path %q must be unique", mountPath), + Paths: []string{"workspaces.mountpath"}, + } + } + mountPaths.Insert(mountPath) + } + return nil +} + +// ValidateVolumes validates a slice of volumes to make sure there are no duplicate names +func ValidateVolumes(volumes []corev1.Volume) *apis.FieldError { + // Task must not have duplicate volume names. + vols := sets.NewString() + for _, v := range volumes { + if vols.Has(v.Name) { + return &apis.FieldError{ + Message: fmt.Sprintf("multiple volumes with same name %q", v.Name), + Paths: []string{"name"}, + } + } + vols.Insert(v.Name) + } + return nil +} + +func validateSteps(steps []Step) *apis.FieldError { + // Task must not have duplicate step names. + names := sets.NewString() + for idx, s := range steps { + if s.Image == "" { + return apis.ErrMissingField("Image") + } + + if s.Script != "" { + if len(s.Command) > 0 { + return &apis.FieldError{ + Message: fmt.Sprintf("step %d script cannot be used with command", idx), + Paths: []string{"script"}, + } + } + } + + if s.Name != "" { + if names.Has(s.Name) { + return apis.ErrInvalidValue(s.Name, "name") + } + names.Insert(s.Name) + } + + for _, vm := range s.VolumeMounts { + if strings.HasPrefix(vm.MountPath, "/tekton/") && + !strings.HasPrefix(vm.MountPath, "/tekton/home") { + return &apis.FieldError{ + Message: fmt.Sprintf("step %d volumeMount cannot be mounted under /tekton/ (volumeMount %q mounted at %q)", idx, vm.Name, vm.MountPath), + Paths: []string{"volumeMounts.mountPath"}, + } + } + if strings.HasPrefix(vm.Name, "tekton-internal-") { + return &apis.FieldError{ + Message: fmt.Sprintf(`step %d volumeMount name %q cannot start with "tekton-internal-"`, idx, vm.Name), + Paths: []string{"volumeMounts.name"}, + } + } + } + } + return nil +} + +func validateInputParameterTypes(inputs *Inputs) *apis.FieldError { + for _, p := range inputs.Params { + // Ensure param has a valid type. + validType := false + for _, allowedType := range AllParamTypes { + if p.Type == allowedType { + validType = true + } + } + if !validType { + return apis.ErrInvalidValue(p.Type, fmt.Sprintf("taskspec.inputs.params.%s.type", p.Name)) + } + + // If a default value is provided, ensure its type matches param's declared type. + if (p.Default != nil) && (p.Default.Type != p.Type) { + return &apis.FieldError{ + Message: fmt.Sprintf( + "\"%v\" type does not match default value's type: \"%v\"", p.Type, p.Default.Type), + Paths: []string{ + fmt.Sprintf("taskspec.inputs.params.%s.type", p.Name), + fmt.Sprintf("taskspec.inputs.params.%s.default.type", p.Name), + }, + } + } + } + return nil +} + +func validateInputParameterVariables(steps []Step, inputs *Inputs, params []v1beta1.ParamSpec) *apis.FieldError { + parameterNames := sets.NewString() + arrayParameterNames := sets.NewString() + + for _, p := range params { + parameterNames.Insert(p.Name) + if p.Type == ParamTypeArray { + arrayParameterNames.Insert(p.Name) + } + } + // Deprecated + if inputs != nil { + for _, p := range inputs.Params { + parameterNames.Insert(p.Name) + if p.Type == ParamTypeArray { + arrayParameterNames.Insert(p.Name) + } + } + } + + if err := validateVariables(steps, "params", parameterNames); err != nil { + return err + } + return validateArrayUsage(steps, "params", arrayParameterNames) +} + +func validateResourceVariables(steps []Step, inputs *Inputs, outputs *Outputs, resources *v1beta1.TaskResources) *apis.FieldError { + resourceNames := sets.NewString() + if resources != nil { + for _, r := range resources.Inputs { + resourceNames.Insert(r.Name) + } + for _, r := range resources.Outputs { + resourceNames.Insert(r.Name) + } + } + // Deprecated + if inputs != nil { + for _, r := range inputs.Resources { + resourceNames.Insert(r.Name) + } + } + // Deprecated + if outputs != nil { + for _, r := range outputs.Resources { + resourceNames.Insert(r.Name) + } + } + return validateVariables(steps, "resources", resourceNames) +} + +func validateArrayUsage(steps []Step, prefix string, vars sets.String) *apis.FieldError { + for _, step := range steps { + if err := validateTaskNoArrayReferenced("name", step.Name, prefix, vars); err != nil { + return err + } + if err := validateTaskNoArrayReferenced("image", step.Image, prefix, vars); err != nil { + return err + } + if err := validateTaskNoArrayReferenced("workingDir", step.WorkingDir, prefix, vars); err != nil { + return err + } + for i, cmd := range step.Command { + if err := validateTaskArraysIsolated(fmt.Sprintf("command[%d]", i), cmd, prefix, vars); err != nil { + return err + } + } + for i, arg := range step.Args { + if err := validateTaskArraysIsolated(fmt.Sprintf("arg[%d]", i), arg, prefix, vars); err != nil { + return err + } + } + for _, env := range step.Env { + if err := validateTaskNoArrayReferenced(fmt.Sprintf("env[%s]", env.Name), env.Value, prefix, vars); err != nil { + return err + } + } + for i, v := range step.VolumeMounts { + if err := validateTaskNoArrayReferenced(fmt.Sprintf("volumeMount[%d].Name", i), v.Name, prefix, vars); err != nil { + return err + } + if err := validateTaskNoArrayReferenced(fmt.Sprintf("volumeMount[%d].MountPath", i), v.MountPath, prefix, vars); err != nil { + return err + } + if err := validateTaskNoArrayReferenced(fmt.Sprintf("volumeMount[%d].SubPath", i), v.SubPath, prefix, vars); err != nil { + return err + } + } + } + return nil +} + +func validateVariables(steps []Step, prefix string, vars sets.String) *apis.FieldError { + for _, step := range steps { + if err := validateTaskVariable("name", step.Name, prefix, vars); err != nil { + return err + } + if err := validateTaskVariable("image", step.Image, prefix, vars); err != nil { + return err + } + if err := validateTaskVariable("workingDir", step.WorkingDir, prefix, vars); err != nil { + return err + } + for i, cmd := range step.Command { + if err := validateTaskVariable(fmt.Sprintf("command[%d]", i), cmd, prefix, vars); err != nil { + return err + } + } + for i, arg := range step.Args { + if err := validateTaskVariable(fmt.Sprintf("arg[%d]", i), arg, prefix, vars); err != nil { + return err + } + } + for _, env := range step.Env { + if err := validateTaskVariable(fmt.Sprintf("env[%s]", env.Name), env.Value, prefix, vars); err != nil { + return err + } + } + for i, v := range step.VolumeMounts { + if err := validateTaskVariable(fmt.Sprintf("volumeMount[%d].Name", i), v.Name, prefix, vars); err != nil { + return err + } + if err := validateTaskVariable(fmt.Sprintf("volumeMount[%d].MountPath", i), v.MountPath, prefix, vars); err != nil { + return err + } + if err := validateTaskVariable(fmt.Sprintf("volumeMount[%d].SubPath", i), v.SubPath, prefix, vars); err != nil { + return err + } + } + } + return nil +} + +func validateTaskVariable(name, value, prefix string, vars sets.String) *apis.FieldError { + return substitution.ValidateVariable(name, value, "(?:inputs|outputs)."+prefix, "step", "taskspec.steps", vars) +} + +func validateTaskNoArrayReferenced(name, value, prefix string, arrayNames sets.String) *apis.FieldError { + return substitution.ValidateVariableProhibited(name, value, "(?:inputs|outputs)."+prefix, "step", "taskspec.steps", arrayNames) +} + +func validateTaskArraysIsolated(name, value, prefix string, arrayNames sets.String) *apis.FieldError { + return substitution.ValidateVariableIsolated(name, value, "(?:inputs|outputs)."+prefix, "step", "taskspec.steps", arrayNames) +} + +func checkForDuplicates(resources []TaskResource, path string) *apis.FieldError { + encountered := sets.NewString() + for _, r := range resources { + if encountered.Has(strings.ToLower(r.Name)) { + return apis.ErrMultipleOneOf(path) + } + encountered.Insert(strings.ToLower(r.Name)) + } + return nil +} + +func validateResourceType(r TaskResource, path string) *apis.FieldError { + for _, allowed := range AllResourceTypes { + if r.Type == allowed { + return nil + } + } + return apis.ErrInvalidValue(r.Type, path) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/taskrun_conversion.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/taskrun_conversion.go new file mode 100644 index 0000000000..e41b49ae1e --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/taskrun_conversion.go @@ -0,0 +1,149 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "knative.dev/pkg/apis" +) + +var _ apis.Convertible = (*TaskRun)(nil) + +// ConvertTo implements api.Convertible +func (tr *TaskRun) ConvertTo(ctx context.Context, obj apis.Convertible) error { + switch sink := obj.(type) { + case *v1beta1.TaskRun: + sink.ObjectMeta = tr.ObjectMeta + if err := tr.Spec.ConvertTo(ctx, &sink.Spec); err != nil { + return err + } + sink.Status = tr.Status + return nil + default: + return fmt.Errorf("unknown version, got: %T", sink) + } +} + +// ConvertTo implements api.Convertible +func (trs *TaskRunSpec) ConvertTo(ctx context.Context, sink *v1beta1.TaskRunSpec) error { + sink.ServiceAccountName = trs.ServiceAccountName + sink.TaskRef = trs.TaskRef + if trs.TaskSpec != nil { + sink.TaskSpec = &v1beta1.TaskSpec{} + if err := trs.TaskSpec.ConvertTo(ctx, sink.TaskSpec); err != nil { + return err + } + } + sink.Status = trs.Status + sink.Timeout = trs.Timeout + sink.PodTemplate = trs.PodTemplate + sink.Workspaces = trs.Workspaces + sink.Params = trs.Params + sink.Resources = trs.Resources + // Deprecated fields + if trs.Inputs != nil { + if len(trs.Inputs.Params) > 0 && len(trs.Params) > 0 { + // This shouldn't happen as it shouldn't pass validation + return apis.ErrMultipleOneOf("inputs.params", "params") + } + if len(trs.Inputs.Params) > 0 { + sink.Params = make([]v1beta1.Param, len(trs.Inputs.Params)) + for i, param := range trs.Inputs.Params { + sink.Params[i] = *param.DeepCopy() + } + } + if len(trs.Inputs.Resources) > 0 { + if sink.Resources == nil { + sink.Resources = &v1beta1.TaskRunResources{} + } + if len(trs.Inputs.Resources) > 0 && trs.Resources != nil && len(trs.Resources.Inputs) > 0 { + // This shouldn't happen as it shouldn't pass validation but just in case + return apis.ErrMultipleOneOf("inputs.resources", "resources.inputs") + } + sink.Resources.Inputs = make([]v1beta1.TaskResourceBinding, len(trs.Inputs.Resources)) + for i, resource := range trs.Inputs.Resources { + sink.Resources.Inputs[i] = v1beta1.TaskResourceBinding{ + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + Name: resource.Name, + ResourceRef: resource.ResourceRef, + ResourceSpec: resource.ResourceSpec, + }, + Paths: resource.Paths, + } + } + } + } + + if trs.Outputs != nil && len(trs.Outputs.Resources) > 0 { + if sink.Resources == nil { + sink.Resources = &v1beta1.TaskRunResources{} + } + if len(trs.Outputs.Resources) > 0 && trs.Resources != nil && len(trs.Resources.Outputs) > 0 { + // This shouldn't happen as it shouldn't pass validation but just in case + return apis.ErrMultipleOneOf("outputs.resources", "resources.outputs") + } + sink.Resources.Outputs = make([]v1beta1.TaskResourceBinding, len(trs.Outputs.Resources)) + for i, resource := range trs.Outputs.Resources { + sink.Resources.Outputs[i] = v1beta1.TaskResourceBinding{ + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + Name: resource.Name, + ResourceRef: resource.ResourceRef, + ResourceSpec: resource.ResourceSpec, + }, + Paths: resource.Paths, + } + } + } + return nil +} + +// ConvertFrom implements api.Convertible +func (tr *TaskRun) ConvertFrom(ctx context.Context, obj apis.Convertible) error { + switch source := obj.(type) { + case *v1beta1.TaskRun: + tr.ObjectMeta = source.ObjectMeta + if err := tr.Spec.ConvertFrom(ctx, &source.Spec); err != nil { + return err + } + tr.Status = source.Status + return nil + default: + return fmt.Errorf("unknown version, got: %T", tr) + } +} + +// ConvertFrom implements api.Convertible +func (trs *TaskRunSpec) ConvertFrom(ctx context.Context, source *v1beta1.TaskRunSpec) error { + trs.ServiceAccountName = source.ServiceAccountName + trs.TaskRef = source.TaskRef + if source.TaskSpec != nil { + trs.TaskSpec = &TaskSpec{} + if err := trs.TaskSpec.ConvertFrom(ctx, source.TaskSpec); err != nil { + return err + } + } + trs.Status = source.Status + trs.Timeout = source.Timeout + trs.PodTemplate = source.PodTemplate + trs.Workspaces = source.Workspaces + trs.Params = source.Params + trs.Resources = source.Resources + return nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/taskrun_defaults.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/taskrun_defaults.go new file mode 100644 index 0000000000..871703ca84 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/taskrun_defaults.go @@ -0,0 +1,74 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + "time" + + "github.com/tektoncd/pipeline/pkg/apis/config" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +var _ apis.Defaultable = (*TaskRun)(nil) + +// ManagedByLabelKey is the label key used to mark what is managing this resource +const ManagedByLabelKey = "app.kubernetes.io/managed-by" + +// SetDefaults implements apis.Defaultable +func (tr *TaskRun) SetDefaults(ctx context.Context) { + ctx = apis.WithinParent(ctx, tr.ObjectMeta) + tr.Spec.SetDefaults(apis.WithinSpec(ctx)) + + // If the TaskRun doesn't have a managed-by label, apply the default + // specified in the config. + cfg := config.FromContextOrDefaults(ctx) + if tr.ObjectMeta.Labels == nil { + tr.ObjectMeta.Labels = map[string]string{} + } + if _, found := tr.ObjectMeta.Labels[ManagedByLabelKey]; !found { + tr.ObjectMeta.Labels[ManagedByLabelKey] = cfg.Defaults.DefaultManagedByLabelValue + } +} + +// SetDefaults implements apis.Defaultable +func (trs *TaskRunSpec) SetDefaults(ctx context.Context) { + cfg := config.FromContextOrDefaults(ctx) + if trs.TaskRef != nil && trs.TaskRef.Kind == "" { + trs.TaskRef.Kind = NamespacedTaskKind + } + + if trs.Timeout == nil { + trs.Timeout = &metav1.Duration{Duration: time.Duration(cfg.Defaults.DefaultTimeoutMinutes) * time.Minute} + } + + defaultSA := cfg.Defaults.DefaultServiceAccount + if trs.ServiceAccountName == "" && defaultSA != "" { + trs.ServiceAccountName = defaultSA + } + + defaultPodTemplate := cfg.Defaults.DefaultPodTemplate + if trs.PodTemplate == nil { + trs.PodTemplate = defaultPodTemplate + } + + // If this taskrun has an embedded task, apply the usual task defaults + if trs.TaskSpec != nil { + trs.TaskSpec.SetDefaults(ctx) + } +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/taskrun_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/taskrun_types.go new file mode 100644 index 0000000000..6f6af8cb47 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/taskrun_types.go @@ -0,0 +1,266 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "fmt" + "time" + + apisconfig "github.com/tektoncd/pipeline/pkg/apis/config" + "github.com/tektoncd/pipeline/pkg/apis/pipeline" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/clock" + "knative.dev/pkg/apis" +) + +// TaskRunSpec defines the desired state of TaskRun +type TaskRunSpec struct { + // +optional + ServiceAccountName string `json:"serviceAccountName"` + // no more than one of the TaskRef and TaskSpec may be specified. + // +optional + TaskRef *TaskRef `json:"taskRef,omitempty"` + // +optional + TaskSpec *TaskSpec `json:"taskSpec,omitempty"` + // Used for cancelling a taskrun (and maybe more later on) + // +optional + Status TaskRunSpecStatus `json:"status,omitempty"` + // Time after which the build times out. Defaults to 10 minutes. + // Specified build timeout should be less than 24h. + // Refer Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + // PodTemplate holds pod specific configuration + // +optional + PodTemplate *PodTemplate `json:"podTemplate,omitempty"` + // Workspaces is a list of WorkspaceBindings from volumes to workspaces. + // +optional + Workspaces []WorkspaceBinding `json:"workspaces,omitempty"` + // From v1beta1 + // +optional + Params []Param `json:"params,omitempty"` + // +optional + Resources *v1beta1.TaskRunResources `json:"resources,omitempty"` + // Deprecated + // +optional + Inputs *TaskRunInputs `json:"inputs,omitempty"` + // +optional + Outputs *TaskRunOutputs `json:"outputs,omitempty"` +} + +// TaskRunSpecStatus defines the taskrun spec status the user can provide +type TaskRunSpecStatus = v1beta1.TaskRunSpecStatus + +const ( + // TaskRunSpecStatusCancelled indicates that the user wants to cancel the task, + // if not already cancelled or terminated + TaskRunSpecStatusCancelled = v1beta1.TaskRunSpecStatusCancelled + + // TaskRunReasonCancelled indicates that the TaskRun has been cancelled + // because it was requested so by the user + TaskRunReasonCancelled = v1beta1.TaskRunSpecStatusCancelled +) + +// TaskRunInputs holds the input values that this task was invoked with. +type TaskRunInputs struct { + // +optional + Resources []TaskResourceBinding `json:"resources,omitempty"` + // +optional + Params []Param `json:"params,omitempty"` +} + +// TaskResourceBinding points to the PipelineResource that +// will be used for the Task input or output called Name. +type TaskResourceBinding = v1beta1.TaskResourceBinding + +// TaskRunOutputs holds the output values that this task was invoked with. +type TaskRunOutputs struct { + // +optional + Resources []TaskResourceBinding `json:"resources,omitempty"` +} + +// TaskRunStatus defines the observed state of TaskRun +type TaskRunStatus = v1beta1.TaskRunStatus + +// TaskRunStatusFields holds the fields of TaskRun's status. This is defined +// separately and inlined so that other types can readily consume these fields +// via duck typing. +type TaskRunStatusFields = v1beta1.TaskRunStatusFields + +// TaskRunResult used to describe the results of a task +type TaskRunResult = v1beta1.TaskRunResult + +// StepState reports the results of running a step in the Task. +type StepState = v1beta1.StepState + +// SidecarState reports the results of sidecar in the Task. +type SidecarState = v1beta1.SidecarState + +// CloudEventDelivery is the target of a cloud event along with the state of +// delivery. +type CloudEventDelivery = v1beta1.CloudEventDelivery + +// CloudEventCondition is a string that represents the condition of the event. +type CloudEventCondition = v1beta1.CloudEventCondition + +const ( + // CloudEventConditionUnknown means that the condition for the event to be + // triggered was not met yet, or we don't know the state yet. + CloudEventConditionUnknown CloudEventCondition = v1beta1.CloudEventConditionUnknown + // CloudEventConditionSent means that the event was sent successfully + CloudEventConditionSent CloudEventCondition = v1beta1.CloudEventConditionSent + // CloudEventConditionFailed means that there was one or more attempts to + // send the event, and none was successful so far. + CloudEventConditionFailed CloudEventCondition = v1beta1.CloudEventConditionFailed +) + +// CloudEventDeliveryState reports the state of a cloud event to be sent. +type CloudEventDeliveryState = v1beta1.CloudEventDeliveryState + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// TaskRun represents a single execution of a Task. TaskRuns are how the steps +// specified in a Task are executed; they specify the parameters and resources +// used to run the steps in a Task. +// +// +k8s:openapi-gen=true +type TaskRun struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +optional + Spec TaskRunSpec `json:"spec,omitempty"` + // +optional + Status TaskRunStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// TaskRunList contains a list of TaskRun +type TaskRunList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []TaskRun `json:"items"` +} + +// GetGroupVersionKind implements kmeta.OwnerRefable. +func (*TaskRun) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind(pipeline.TaskRunControllerName) +} + +// GetPipelineRunPVCName for taskrun gets pipelinerun +func (tr *TaskRun) GetPipelineRunPVCName() string { + if tr == nil { + return "" + } + for _, ref := range tr.GetOwnerReferences() { + if ref.Kind == pipeline.PipelineRunControllerName { + return fmt.Sprintf("%s-pvc", ref.Name) + } + } + return "" +} + +// HasPipelineRunOwnerReference returns true of TaskRun has +// owner reference of type PipelineRun +func (tr *TaskRun) HasPipelineRunOwnerReference() bool { + for _, ref := range tr.GetOwnerReferences() { + if ref.Kind == pipeline.PipelineRunControllerName { + return true + } + } + return false +} + +// IsDone returns true if the TaskRun's status indicates that it is done. +func (tr *TaskRun) IsDone() bool { + return !tr.Status.GetCondition(apis.ConditionSucceeded).IsUnknown() +} + +// HasStarted function check whether taskrun has valid start time set in its status +func (tr *TaskRun) HasStarted() bool { + return tr.Status.StartTime != nil && !tr.Status.StartTime.IsZero() +} + +// IsSuccessful returns true if the TaskRun's status indicates that it is done. +func (tr *TaskRun) IsSuccessful() bool { + return tr.Status.GetCondition(apis.ConditionSucceeded).IsTrue() +} + +// IsCancelled returns true if the TaskRun's spec status is set to Cancelled state +func (tr *TaskRun) IsCancelled() bool { + return tr.Spec.Status == TaskRunSpecStatusCancelled +} + +// HasTimedOut returns true if the TaskRun runtime is beyond the allowed timeout +func (tr *TaskRun) HasTimedOut(c clock.PassiveClock) bool { + if tr.Status.StartTime.IsZero() { + return false + } + timeout := tr.GetTimeout() + // If timeout is set to 0 or defaulted to 0, there is no timeout. + if timeout == apisconfig.NoTimeoutDuration { + return false + } + runtime := c.Since(tr.Status.StartTime.Time) + return runtime > timeout +} + +// GetTimeout returns the timeout for the TaskRun, or the default if not specified +func (tr *TaskRun) GetTimeout() time.Duration { + // Use the platform default is no timeout is set + if tr.Spec.Timeout == nil { + return apisconfig.DefaultTimeoutMinutes * time.Minute + } + return tr.Spec.Timeout.Duration +} + +// GetRunKey return the taskrun key for timeout handler map +func (tr *TaskRun) GetRunKey() string { + // The address of the pointer is a threadsafe unique identifier for the taskrun + return fmt.Sprintf("%s/%p", "TaskRun", tr) +} + +// IsPartOfPipeline return true if TaskRun is a part of a Pipeline. +// It also return the name of Pipeline and PipelineRun +func (tr *TaskRun) IsPartOfPipeline() (bool, string, string) { + if tr == nil || len(tr.Labels) == 0 { + return false, "", "" + } + + if pl, ok := tr.Labels[pipeline.PipelineLabelKey]; ok { + return true, pl, tr.Labels[pipeline.PipelineRunLabelKey] + } + + return false, "", "" +} + +// HasVolumeClaimTemplate returns true if TaskRun contains volumeClaimTemplates that is +// used for creating PersistentVolumeClaims with an OwnerReference for each run +func (tr *TaskRun) HasVolumeClaimTemplate() bool { + for _, ws := range tr.Spec.Workspaces { + if ws.VolumeClaimTemplate != nil { + return true + } + } + return false +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/taskrun_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/taskrun_validation.go new file mode 100644 index 0000000000..a898c21d6d --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/taskrun_validation.go @@ -0,0 +1,174 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + "strings" + + "github.com/tektoncd/pipeline/pkg/apis/validate" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/util/sets" + "knative.dev/pkg/apis" +) + +var _ apis.Validatable = (*TaskRun)(nil) + +// Validate taskrun +func (tr *TaskRun) Validate(ctx context.Context) *apis.FieldError { + if err := validate.ObjectMetadata(tr.GetObjectMeta()).ViaField("metadata"); err != nil { + return err + } + if apis.IsInDelete(ctx) { + return nil + } + return tr.Spec.Validate(ctx) +} + +// Validate taskrun spec +func (ts *TaskRunSpec) Validate(ctx context.Context) *apis.FieldError { + if equality.Semantic.DeepEqual(ts, &TaskRunSpec{}) { + return apis.ErrMissingField("spec") + } + + // can't have both taskRef and taskSpec at the same time + if (ts.TaskRef != nil && ts.TaskRef.Name != "") && ts.TaskSpec != nil { + return apis.ErrDisallowedFields("spec.taskref", "spec.taskspec") + } + + // Check that one of TaskRef and TaskSpec is present + if (ts.TaskRef == nil || (ts.TaskRef != nil && ts.TaskRef.Name == "")) && ts.TaskSpec == nil { + return apis.ErrMissingField("spec.taskref.name", "spec.taskspec") + } + + // Validate TaskSpec if it's present + if ts.TaskSpec != nil { + if err := ts.TaskSpec.Validate(ctx); err != nil { + return err + } + } + + // Deprecated + // check for input resources + if ts.Inputs != nil { + if err := ts.Inputs.Validate(ctx, "spec.Inputs"); err != nil { + return err + } + } + + // Deprecated + // check for output resources + if ts.Outputs != nil { + if err := ts.Outputs.Validate(ctx, "spec.Outputs"); err != nil { + return err + } + } + + // Validate Resources + if err := ts.Resources.Validate(ctx); err != nil { + return err + } + + if err := validateWorkspaceBindings(ctx, ts.Workspaces); err != nil { + return err + } + if err := validateParameters("spec.inputs.params", ts.Params); err != nil { + return err + } + + if ts.Timeout != nil { + // timeout should be a valid duration of at least 0. + if ts.Timeout.Duration < 0 { + return apis.ErrInvalidValue(fmt.Sprintf("%s should be >= 0", ts.Timeout.Duration.String()), "spec.timeout") + } + } + + return nil +} + +// Validate implements apis.Validatable +func (i TaskRunInputs) Validate(ctx context.Context, path string) *apis.FieldError { + if err := validatePipelineResources(ctx, i.Resources, fmt.Sprintf("%s.Resources.Name", path)); err != nil { + return err + } + return validateParameters("spec.inputs.params", i.Params) +} + +// Validate implements apis.Validatable +func (o TaskRunOutputs) Validate(ctx context.Context, path string) *apis.FieldError { + return validatePipelineResources(ctx, o.Resources, fmt.Sprintf("%s.Resources.Name", path)) +} + +// validateWorkspaceBindings makes sure the volumes provided for the Task's declared workspaces make sense. +func validateWorkspaceBindings(ctx context.Context, wb []WorkspaceBinding) *apis.FieldError { + seen := sets.NewString() + for _, w := range wb { + if seen.Has(w.Name) { + return apis.ErrMultipleOneOf("spec.workspaces.name") + } + seen.Insert(w.Name) + + if err := w.Validate(ctx).ViaField("workspace"); err != nil { + return err + } + } + + return nil +} + +// validatePipelineResources validates that +// 1. resource is not declared more than once +// 2. if both resource reference and resource spec is defined at the same time +// 3. at least resource ref or resource spec is defined +func validatePipelineResources(ctx context.Context, resources []TaskResourceBinding, path string) *apis.FieldError { + encountered := sets.NewString() + for _, r := range resources { + // We should provide only one binding for each resource required by the Task. + name := strings.ToLower(r.Name) + if encountered.Has(strings.ToLower(name)) { + return apis.ErrMultipleOneOf(path) + } + encountered.Insert(name) + // Check that both resource ref and resource Spec are not present + if r.ResourceRef != nil && r.ResourceSpec != nil { + return apis.ErrDisallowedFields(fmt.Sprintf("%s.ResourceRef", path), fmt.Sprintf("%s.ResourceSpec", path)) + } + // Check that one of resource ref and resource Spec is present + if (r.ResourceRef == nil || r.ResourceRef.Name == "") && r.ResourceSpec == nil { + return apis.ErrMissingField(fmt.Sprintf("%s.ResourceRef", path), fmt.Sprintf("%s.ResourceSpec", path)) + } + if r.ResourceSpec != nil && r.ResourceSpec.Validate(ctx) != nil { + return r.ResourceSpec.Validate(ctx) + } + } + + return nil +} + +// TODO(jasonhall): Share this with v1beta1/taskrun_validation.go +func validateParameters(path string, params []Param) *apis.FieldError { + // Template must not duplicate parameter names. + seen := sets.NewString() + for _, p := range params { + if seen.Has(strings.ToLower(p.Name)) { + return apis.ErrMultipleOneOf(path) + } + seen.Insert(p.Name) + } + return nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/workspace_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/workspace_types.go new file mode 100644 index 0000000000..4dfd2c5a67 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/workspace_types.go @@ -0,0 +1,35 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" +) + +// WorkspaceDeclaration is a declaration of a volume that a Task requires. +type WorkspaceDeclaration = v1beta1.WorkspaceDeclaration + +// WorkspaceBinding maps a Task's declared workspace to a Volume. +type WorkspaceBinding = v1beta1.WorkspaceBinding + +// PipelineWorkspaceDeclaration creates a named slot in a Pipeline that a PipelineRun +// is expected to populate with a workspace binding. +type PipelineWorkspaceDeclaration = v1beta1.PipelineWorkspaceDeclaration + +// WorkspacePipelineTaskBinding describes how a workspace passed into the pipeline should be +// mapped to a task's declared workspace. +type WorkspacePipelineTaskBinding = v1beta1.WorkspacePipelineTaskBinding diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/workspace_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/workspace_validation.go new file mode 100644 index 0000000000..7536360d31 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/workspace_validation.go @@ -0,0 +1,17 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..f537fa4b5b --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,1024 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + pod "github.com/tektoncd/pipeline/pkg/apis/pipeline/pod" + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + resourcev1alpha1 "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterTask) DeepCopyInto(out *ClusterTask) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterTask. +func (in *ClusterTask) DeepCopy() *ClusterTask { + if in == nil { + return nil + } + out := new(ClusterTask) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterTask) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterTaskList) DeepCopyInto(out *ClusterTaskList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterTask, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterTaskList. +func (in *ClusterTaskList) DeepCopy() *ClusterTaskList { + if in == nil { + return nil + } + out := new(ClusterTaskList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterTaskList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Condition) DeepCopyInto(out *Condition) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. +func (in *Condition) DeepCopy() *Condition { + if in == nil { + return nil + } + out := new(Condition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Condition) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConditionCheck) DeepCopyInto(out *ConditionCheck) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConditionCheck. +func (in *ConditionCheck) DeepCopy() *ConditionCheck { + if in == nil { + return nil + } + out := new(ConditionCheck) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConditionList) DeepCopyInto(out *ConditionList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConditionList. +func (in *ConditionList) DeepCopy() *ConditionList { + if in == nil { + return nil + } + out := new(ConditionList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ConditionList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConditionSpec) DeepCopyInto(out *ConditionSpec) { + *out = *in + in.Check.DeepCopyInto(&out.Check) + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]v1beta1.ParamSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make([]resourcev1alpha1.ResourceDeclaration, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConditionSpec. +func (in *ConditionSpec) DeepCopy() *ConditionSpec { + if in == nil { + return nil + } + out := new(ConditionSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EmbeddedRunSpec) DeepCopyInto(out *EmbeddedRunSpec) { + *out = *in + out.TypeMeta = in.TypeMeta + in.Metadata.DeepCopyInto(&out.Metadata) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EmbeddedRunSpec. +func (in *EmbeddedRunSpec) DeepCopy() *EmbeddedRunSpec { + if in == nil { + return nil + } + out := new(EmbeddedRunSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Inputs) DeepCopyInto(out *Inputs) { + *out = *in + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make([]v1beta1.TaskResource, len(*in)) + copy(*out, *in) + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]v1beta1.ParamSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Inputs. +func (in *Inputs) DeepCopy() *Inputs { + if in == nil { + return nil + } + out := new(Inputs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Outputs) DeepCopyInto(out *Outputs) { + *out = *in + if in.Results != nil { + in, out := &in.Results, &out.Results + *out = make([]TestResult, len(*in)) + copy(*out, *in) + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make([]v1beta1.TaskResource, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Outputs. +func (in *Outputs) DeepCopy() *Outputs { + if in == nil { + return nil + } + out := new(Outputs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Pipeline) DeepCopyInto(out *Pipeline) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(PipelineStatus) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Pipeline. +func (in *Pipeline) DeepCopy() *Pipeline { + if in == nil { + return nil + } + out := new(Pipeline) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Pipeline) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineList) DeepCopyInto(out *PipelineList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Pipeline, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineList. +func (in *PipelineList) DeepCopy() *PipelineList { + if in == nil { + return nil + } + out := new(PipelineList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PipelineList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineRun) DeepCopyInto(out *PipelineRun) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineRun. +func (in *PipelineRun) DeepCopy() *PipelineRun { + if in == nil { + return nil + } + out := new(PipelineRun) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PipelineRun) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineRunList) DeepCopyInto(out *PipelineRunList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PipelineRun, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineRunList. +func (in *PipelineRunList) DeepCopy() *PipelineRunList { + if in == nil { + return nil + } + out := new(PipelineRunList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PipelineRunList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineRunSpec) DeepCopyInto(out *PipelineRunSpec) { + *out = *in + if in.PipelineRef != nil { + in, out := &in.PipelineRef, &out.PipelineRef + *out = new(v1beta1.PipelineRef) + (*in).DeepCopyInto(*out) + } + if in.PipelineSpec != nil { + in, out := &in.PipelineSpec, &out.PipelineSpec + *out = new(PipelineSpec) + (*in).DeepCopyInto(*out) + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make([]v1beta1.PipelineResourceBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]v1beta1.Param, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ServiceAccountNames != nil { + in, out := &in.ServiceAccountNames, &out.ServiceAccountNames + *out = make([]v1beta1.PipelineRunSpecServiceAccountName, len(*in)) + copy(*out, *in) + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } + if in.PodTemplate != nil { + in, out := &in.PodTemplate, &out.PodTemplate + *out = new(pod.Template) + (*in).DeepCopyInto(*out) + } + if in.Workspaces != nil { + in, out := &in.Workspaces, &out.Workspaces + *out = make([]v1beta1.WorkspaceBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.TaskRunSpecs != nil { + in, out := &in.TaskRunSpecs, &out.TaskRunSpecs + *out = make([]PipelineTaskRunSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineRunSpec. +func (in *PipelineRunSpec) DeepCopy() *PipelineRunSpec { + if in == nil { + return nil + } + out := new(PipelineRunSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineSpec) DeepCopyInto(out *PipelineSpec) { + *out = *in + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make([]v1beta1.PipelineDeclaredResource, len(*in)) + copy(*out, *in) + } + if in.Tasks != nil { + in, out := &in.Tasks, &out.Tasks + *out = make([]PipelineTask, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]v1beta1.ParamSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Workspaces != nil { + in, out := &in.Workspaces, &out.Workspaces + *out = make([]v1beta1.PipelineWorkspaceDeclaration, len(*in)) + copy(*out, *in) + } + if in.Results != nil { + in, out := &in.Results, &out.Results + *out = make([]v1beta1.PipelineResult, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineSpec. +func (in *PipelineSpec) DeepCopy() *PipelineSpec { + if in == nil { + return nil + } + out := new(PipelineSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineStatus) DeepCopyInto(out *PipelineStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineStatus. +func (in *PipelineStatus) DeepCopy() *PipelineStatus { + if in == nil { + return nil + } + out := new(PipelineStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineTask) DeepCopyInto(out *PipelineTask) { + *out = *in + if in.TaskRef != nil { + in, out := &in.TaskRef, &out.TaskRef + *out = new(v1beta1.TaskRef) + (*in).DeepCopyInto(*out) + } + if in.TaskSpec != nil { + in, out := &in.TaskSpec, &out.TaskSpec + *out = new(TaskSpec) + (*in).DeepCopyInto(*out) + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1beta1.PipelineTaskCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.RunAfter != nil { + in, out := &in.RunAfter, &out.RunAfter + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(v1beta1.PipelineTaskResources) + (*in).DeepCopyInto(*out) + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]v1beta1.Param, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Workspaces != nil { + in, out := &in.Workspaces, &out.Workspaces + *out = make([]v1beta1.WorkspacePipelineTaskBinding, len(*in)) + copy(*out, *in) + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineTask. +func (in *PipelineTask) DeepCopy() *PipelineTask { + if in == nil { + return nil + } + out := new(PipelineTask) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in PipelineTaskList) DeepCopyInto(out *PipelineTaskList) { + { + in := &in + *out = make(PipelineTaskList, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineTaskList. +func (in PipelineTaskList) DeepCopy() PipelineTaskList { + if in == nil { + return nil + } + out := new(PipelineTaskList) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineTaskRunSpec) DeepCopyInto(out *PipelineTaskRunSpec) { + *out = *in + if in.TaskPodTemplate != nil { + in, out := &in.TaskPodTemplate, &out.TaskPodTemplate + *out = new(pod.Template) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineTaskRunSpec. +func (in *PipelineTaskRunSpec) DeepCopy() *PipelineTaskRunSpec { + if in == nil { + return nil + } + out := new(PipelineTaskRunSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Run) DeepCopyInto(out *Run) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Run. +func (in *Run) DeepCopy() *Run { + if in == nil { + return nil + } + out := new(Run) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Run) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RunList) DeepCopyInto(out *RunList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Run, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunList. +func (in *RunList) DeepCopy() *RunList { + if in == nil { + return nil + } + out := new(RunList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RunList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RunSpec) DeepCopyInto(out *RunSpec) { + *out = *in + if in.Ref != nil { + in, out := &in.Ref, &out.Ref + *out = new(v1beta1.TaskRef) + (*in).DeepCopyInto(*out) + } + if in.Spec != nil { + in, out := &in.Spec, &out.Spec + *out = new(EmbeddedRunSpec) + (*in).DeepCopyInto(*out) + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]v1beta1.Param, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.PodTemplate != nil { + in, out := &in.PodTemplate, &out.PodTemplate + *out = new(pod.Template) + (*in).DeepCopyInto(*out) + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } + if in.Workspaces != nil { + in, out := &in.Workspaces, &out.Workspaces + *out = make([]v1beta1.WorkspaceBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunSpec. +func (in *RunSpec) DeepCopy() *RunSpec { + if in == nil { + return nil + } + out := new(RunSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Task) DeepCopyInto(out *Task) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Task. +func (in *Task) DeepCopy() *Task { + if in == nil { + return nil + } + out := new(Task) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Task) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskList) DeepCopyInto(out *TaskList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Task, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskList. +func (in *TaskList) DeepCopy() *TaskList { + if in == nil { + return nil + } + out := new(TaskList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TaskList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRun) DeepCopyInto(out *TaskRun) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRun. +func (in *TaskRun) DeepCopy() *TaskRun { + if in == nil { + return nil + } + out := new(TaskRun) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TaskRun) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunInputs) DeepCopyInto(out *TaskRunInputs) { + *out = *in + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make([]v1beta1.TaskResourceBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]v1beta1.Param, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunInputs. +func (in *TaskRunInputs) DeepCopy() *TaskRunInputs { + if in == nil { + return nil + } + out := new(TaskRunInputs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunList) DeepCopyInto(out *TaskRunList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]TaskRun, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunList. +func (in *TaskRunList) DeepCopy() *TaskRunList { + if in == nil { + return nil + } + out := new(TaskRunList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TaskRunList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunOutputs) DeepCopyInto(out *TaskRunOutputs) { + *out = *in + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make([]v1beta1.TaskResourceBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunOutputs. +func (in *TaskRunOutputs) DeepCopy() *TaskRunOutputs { + if in == nil { + return nil + } + out := new(TaskRunOutputs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunSpec) DeepCopyInto(out *TaskRunSpec) { + *out = *in + if in.TaskRef != nil { + in, out := &in.TaskRef, &out.TaskRef + *out = new(v1beta1.TaskRef) + (*in).DeepCopyInto(*out) + } + if in.TaskSpec != nil { + in, out := &in.TaskSpec, &out.TaskSpec + *out = new(TaskSpec) + (*in).DeepCopyInto(*out) + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } + if in.PodTemplate != nil { + in, out := &in.PodTemplate, &out.PodTemplate + *out = new(pod.Template) + (*in).DeepCopyInto(*out) + } + if in.Workspaces != nil { + in, out := &in.Workspaces, &out.Workspaces + *out = make([]v1beta1.WorkspaceBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]v1beta1.Param, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(v1beta1.TaskRunResources) + (*in).DeepCopyInto(*out) + } + if in.Inputs != nil { + in, out := &in.Inputs, &out.Inputs + *out = new(TaskRunInputs) + (*in).DeepCopyInto(*out) + } + if in.Outputs != nil { + in, out := &in.Outputs, &out.Outputs + *out = new(TaskRunOutputs) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunSpec. +func (in *TaskRunSpec) DeepCopy() *TaskRunSpec { + if in == nil { + return nil + } + out := new(TaskRunSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskSpec) DeepCopyInto(out *TaskSpec) { + *out = *in + in.TaskSpec.DeepCopyInto(&out.TaskSpec) + if in.Inputs != nil { + in, out := &in.Inputs, &out.Inputs + *out = new(Inputs) + (*in).DeepCopyInto(*out) + } + if in.Outputs != nil { + in, out := &in.Outputs, &out.Outputs + *out = new(Outputs) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskSpec. +func (in *TaskSpec) DeepCopy() *TaskSpec { + if in == nil { + return nil + } + out := new(TaskSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TestResult) DeepCopyInto(out *TestResult) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TestResult. +func (in *TestResult) DeepCopy() *TestResult { + if in == nil { + return nil + } + out := new(TestResult) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/cluster_task_conversion.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/cluster_task_conversion.go new file mode 100644 index 0000000000..fb07cc0035 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/cluster_task_conversion.go @@ -0,0 +1,36 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + + "knative.dev/pkg/apis" +) + +var _ apis.Convertible = (*ClusterTask)(nil) + +// ConvertTo implements api.Convertible +func (ct *ClusterTask) ConvertTo(ctx context.Context, sink apis.Convertible) error { + return fmt.Errorf("v1beta1 is the highest known version, got: %T", sink) +} + +// ConvertFrom implements api.Convertible +func (ct *ClusterTask) ConvertFrom(ctx context.Context, source apis.Convertible) error { + return fmt.Errorf("v1beta1 is the highest know version, got: %T", source) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/cluster_task_defaults.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/cluster_task_defaults.go new file mode 100644 index 0000000000..fda6d76ead --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/cluster_task_defaults.go @@ -0,0 +1,30 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + + "knative.dev/pkg/apis" +) + +var _ apis.Defaultable = (*ClusterTask)(nil) + +// SetDefaults sets the default values for the ClusterTask's Spec. +func (t *ClusterTask) SetDefaults(ctx context.Context) { + t.Spec.SetDefaults(ctx) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/cluster_task_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/cluster_task_types.go new file mode 100644 index 0000000000..183b71347c --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/cluster_task_types.go @@ -0,0 +1,75 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "github.com/tektoncd/pipeline/pkg/apis/pipeline" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "knative.dev/pkg/kmeta" +) + +// +genclient +// +genclient:noStatus +// +genclient:nonNamespaced +// +genreconciler:krshapedlogic=false +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterTask is a Task with a cluster scope. ClusterTasks are used to +// represent Tasks that should be publicly addressable from any namespace in the +// cluster. +type ClusterTask struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec holds the desired state of the Task from the client + // +optional + Spec TaskSpec `json:"spec,omitempty"` +} + +var _ kmeta.OwnerRefable = (*ClusterTask)(nil) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterTaskList contains a list of ClusterTask +type ClusterTaskList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClusterTask `json:"items"` +} + +// TaskSpec returns the ClusterTask's Spec +func (t *ClusterTask) TaskSpec() TaskSpec { + return t.Spec +} + +// TaskMetadata returns the ObjectMeta for the ClusterTask +func (t *ClusterTask) TaskMetadata() metav1.ObjectMeta { + return t.ObjectMeta +} + +// Copy returns a DeepCopy of the ClusterTask +func (t *ClusterTask) Copy() TaskObject { + return t.DeepCopy() +} + +// GetGroupVersionKind implements kmeta.OwnerRefable. +func (*ClusterTask) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind(pipeline.ClusterTaskControllerName) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/cluster_task_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/cluster_task_validation.go new file mode 100644 index 0000000000..afeba8d78d --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/cluster_task_validation.go @@ -0,0 +1,35 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + + "github.com/tektoncd/pipeline/pkg/apis/validate" + "knative.dev/pkg/apis" +) + +var _ apis.Validatable = (*ClusterTask)(nil) + +// Validate performs validation of the metadata and spec of this ClusterTask. +func (t *ClusterTask) Validate(ctx context.Context) *apis.FieldError { + errs := validate.ObjectMetadata(t.GetObjectMeta()).ViaField("metadata") + if apis.IsInDelete(ctx) { + return nil + } + return errs.Also(t.Spec.Validate(apis.WithinSpec(ctx)).ViaField("spec")) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/condition_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/condition_types.go new file mode 100644 index 0000000000..6139a33122 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/condition_types.go @@ -0,0 +1,75 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" + duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1" +) + +// ConditionCheck represents a single evaluation of a Condition step. +type ConditionCheck TaskRun + +// NewConditionCheck creates a new ConditionCheck from a given TaskRun +func NewConditionCheck(tr *TaskRun) *ConditionCheck { + if tr == nil { + return nil + } + + cc := ConditionCheck(*tr) + return &cc +} + +// IsDone returns true if the ConditionCheck's status indicates that it is done. +func (cc *ConditionCheck) IsDone() bool { + return !cc.Status.GetCondition(apis.ConditionSucceeded).IsUnknown() +} + +// IsSuccessful returns true if the ConditionCheck's status indicates that it is done. +func (cc *ConditionCheck) IsSuccessful() bool { + return cc.Status.GetCondition(apis.ConditionSucceeded).IsTrue() +} + +// ConditionCheckStatus defines the observed state of ConditionCheck +type ConditionCheckStatus struct { + duckv1beta1.Status `json:",inline"` + + // ConditionCheckStatusFields inlines the status fields. + ConditionCheckStatusFields `json:",inline"` +} + +// ConditionCheckStatusFields holds the fields of ConfigurationCheck's status. +// This is defined separately and inlined so that other types can readily +// consume these fields via duck typing. +type ConditionCheckStatusFields struct { + // PodName is the name of the pod responsible for executing this condition check. + PodName string `json:"podName"` + + // StartTime is the time the check is actually started. + // +optional + StartTime *metav1.Time `json:"startTime,omitempty"` + + // CompletionTime is the time the check pod completed. + // +optional + CompletionTime *metav1.Time `json:"completionTime,omitempty"` + + // Check describes the state of the check container. + // +optional + Check corev1.ContainerState `json:"check,omitempty"` +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/container_replacements.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/container_replacements.go new file mode 100644 index 0000000000..30af4e11e8 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/container_replacements.go @@ -0,0 +1,74 @@ +/* + Copyright 2019 The Tekton Authors + + 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. +*/ + +package v1beta1 + +import ( + "github.com/tektoncd/pipeline/pkg/substitution" + corev1 "k8s.io/api/core/v1" +) + +// applyContainerReplacements applies variable interpolation on a Container (subset of a Step). +func applyContainerReplacements(step *corev1.Container, stringReplacements map[string]string, arrayReplacements map[string][]string) { + step.Name = substitution.ApplyReplacements(step.Name, stringReplacements) + step.Image = substitution.ApplyReplacements(step.Image, stringReplacements) + step.ImagePullPolicy = corev1.PullPolicy(substitution.ApplyReplacements(string(step.ImagePullPolicy), stringReplacements)) + + // Use ApplyArrayReplacements here, as additional args may be added via an array parameter. + var newArgs []string + for _, a := range step.Args { + newArgs = append(newArgs, substitution.ApplyArrayReplacements(a, stringReplacements, arrayReplacements)...) + } + step.Args = newArgs + + for ie, e := range step.Env { + step.Env[ie].Value = substitution.ApplyReplacements(e.Value, stringReplacements) + if step.Env[ie].ValueFrom != nil { + if e.ValueFrom.SecretKeyRef != nil { + step.Env[ie].ValueFrom.SecretKeyRef.LocalObjectReference.Name = substitution.ApplyReplacements(e.ValueFrom.SecretKeyRef.LocalObjectReference.Name, stringReplacements) + step.Env[ie].ValueFrom.SecretKeyRef.Key = substitution.ApplyReplacements(e.ValueFrom.SecretKeyRef.Key, stringReplacements) + } + if e.ValueFrom.ConfigMapKeyRef != nil { + step.Env[ie].ValueFrom.ConfigMapKeyRef.LocalObjectReference.Name = substitution.ApplyReplacements(e.ValueFrom.ConfigMapKeyRef.LocalObjectReference.Name, stringReplacements) + step.Env[ie].ValueFrom.ConfigMapKeyRef.Key = substitution.ApplyReplacements(e.ValueFrom.ConfigMapKeyRef.Key, stringReplacements) + } + } + } + + for ie, e := range step.EnvFrom { + step.EnvFrom[ie].Prefix = substitution.ApplyReplacements(e.Prefix, stringReplacements) + if e.ConfigMapRef != nil { + step.EnvFrom[ie].ConfigMapRef.LocalObjectReference.Name = substitution.ApplyReplacements(e.ConfigMapRef.LocalObjectReference.Name, stringReplacements) + } + if e.SecretRef != nil { + step.EnvFrom[ie].SecretRef.LocalObjectReference.Name = substitution.ApplyReplacements(e.SecretRef.LocalObjectReference.Name, stringReplacements) + } + } + step.WorkingDir = substitution.ApplyReplacements(step.WorkingDir, stringReplacements) + + // Use ApplyArrayReplacements here, as additional commands may be added via an array parameter. + var newCommand []string + for _, c := range step.Command { + newCommand = append(newCommand, substitution.ApplyArrayReplacements(c, stringReplacements, arrayReplacements)...) + } + step.Command = newCommand + + for iv, v := range step.VolumeMounts { + step.VolumeMounts[iv].Name = substitution.ApplyReplacements(v.Name, stringReplacements) + step.VolumeMounts[iv].MountPath = substitution.ApplyReplacements(v.MountPath, stringReplacements) + step.VolumeMounts[iv].SubPath = substitution.ApplyReplacements(v.SubPath, stringReplacements) + } +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/conversion_error.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/conversion_error.go new file mode 100644 index 0000000000..b56da51dc3 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/conversion_error.go @@ -0,0 +1,28 @@ +/* +Copyright 2020 The Tekton Authors. + +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. +*/ + +package v1beta1 + +import ( + "knative.dev/pkg/apis" +) + +const ( + // ConditionTypeConvertible is a Warning condition that is set on + // resources when they cannot be converted to warn of a forthcoming + // breakage. + ConditionTypeConvertible apis.ConditionType = "Convertible" +) diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/doc.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/doc.go new file mode 100644 index 0000000000..2340321bbd --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/doc.go @@ -0,0 +1,23 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Package v1beta1 contains API Schema definitions for the pipeline v1beta1 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=github.com/tektoncd/pipeline/pkg/apis/pipeline +// +k8s:defaulter-gen=TypeMeta +// +groupName=tekton.dev +package v1beta1 diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/merge.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/merge.go new file mode 100644 index 0000000000..f200c99e3e --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/merge.go @@ -0,0 +1,173 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "encoding/json" + + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/strategicpatch" +) + +// mergeData is used to store the intermediate data needed to merge an object +// with a template. It's provided to avoid repeatedly re-serializing the template. +// +k8s:openapi-gen=false +type mergeData struct { + emptyJSON []byte + templateJSON []byte + patchSchema strategicpatch.PatchMetaFromStruct +} + +// MergeStepsWithStepTemplate takes a possibly nil container template and a +// list of steps, merging each of the steps with the container template, if +// it's not nil, and returning the resulting list. +func MergeStepsWithStepTemplate(template *v1.Container, steps []Step) ([]Step, error) { + if template == nil { + return steps, nil + } + + md, err := getMergeData(template, &v1.Container{}) + if err != nil { + return nil, err + } + + for i, s := range steps { + merged := v1.Container{} + err := mergeObjWithTemplateBytes(md, &s.Container, &merged) + if err != nil { + return nil, err + } + + // If the container's args is nil, reset it to empty instead + if merged.Args == nil && s.Args != nil { + merged.Args = []string{} + } + + // Pass through original step Script, for later conversion. + steps[i] = Step{Container: merged, Script: s.Script, OnError: s.OnError, Timeout: s.Timeout} + } + return steps, nil +} + +// MergeStepsWithOverrides takes a possibly nil list of overrides and a +// list of steps, merging each of the steps with the overrides' resource requirements, if +// it's not nil, and returning the resulting list. +func MergeStepsWithOverrides(steps []Step, overrides []TaskRunStepOverride) ([]Step, error) { + stepNameToOverride := make(map[string]TaskRunStepOverride, len(overrides)) + for _, o := range overrides { + stepNameToOverride[o.Name] = o + } + for i, s := range steps { + o, found := stepNameToOverride[s.Name] + if !found { + continue + } + merged := v1.ResourceRequirements{} + err := mergeObjWithTemplate(&s.Resources, &o.Resources, &merged) + if err != nil { + return nil, err + } + steps[i].Container.Resources = merged + } + return steps, nil +} + +// MergeSidecarsWithOverrides takes a possibly nil list of overrides and a +// list of sidecars, merging each of the sidecars with the overrides' resource requirements, if +// it's not nil, and returning the resulting list. +func MergeSidecarsWithOverrides(sidecars []Sidecar, overrides []TaskRunSidecarOverride) ([]Sidecar, error) { + if len(overrides) == 0 { + return sidecars, nil + } + sidecarNameToOverride := make(map[string]TaskRunSidecarOverride, len(overrides)) + for _, o := range overrides { + sidecarNameToOverride[o.Name] = o + } + for i, s := range sidecars { + o, found := sidecarNameToOverride[s.Name] + if !found { + continue + } + merged := v1.ResourceRequirements{} + err := mergeObjWithTemplate(&s.Resources, &o.Resources, &merged) + if err != nil { + return nil, err + } + sidecars[i].Container.Resources = merged + } + return sidecars, nil +} + +// mergeObjWithTemplate merges obj with template and updates out to reflect the merged result. +// template, obj, and out should point to the same type. out points to the zero value of that type. +func mergeObjWithTemplate(template, obj, out interface{}) error { + md, err := getMergeData(template, out) + if err != nil { + return err + } + return mergeObjWithTemplateBytes(md, obj, out) +} + +// getMergeData serializes the template and empty object to get the intermediate results necessary for +// merging an object of the same type with this template. +// This function is provided to avoid repeatedly serializing an identical template. +func getMergeData(template, empty interface{}) (*mergeData, error) { + // We need JSON bytes to generate a patch to merge the object + // onto the template, so marshal the template. + templateJSON, err := json.Marshal(template) + if err != nil { + return nil, err + } + // We need to do a three-way merge to actually merge the template and + // object, so we need an empty object as the "original" + emptyJSON, err := json.Marshal(empty) + if err != nil { + return nil, err + } + // Get the patch meta, which is needed for generating and applying the merge patch. + patchSchema, err := strategicpatch.NewPatchMetaFromStruct(template) + if err != nil { + return nil, err + } + return &mergeData{templateJSON: templateJSON, emptyJSON: emptyJSON, patchSchema: patchSchema}, nil +} + +// mergeObjWithTemplateBytes merges obj with md's template JSON and updates out to reflect the merged result. +// out is a pointer to the zero value of obj's type. +// This function is provided to avoid repeatedly serializing an identical template. +func mergeObjWithTemplateBytes(md *mergeData, obj, out interface{}) error { + // Marshal the object to JSON + objAsJSON, err := json.Marshal(obj) + if err != nil { + return err + } + // Create a merge patch, with the empty JSON as the original, the object JSON as the modified, and the template + // JSON as the current - this lets us do a deep merge of the template and object, with awareness of + // the "patchMerge" tags. + patch, err := strategicpatch.CreateThreeWayMergePatch(md.emptyJSON, objAsJSON, md.templateJSON, md.patchSchema, true) + if err != nil { + return err + } + + // Actually apply the merge patch to the template JSON. + mergedAsJSON, err := strategicpatch.StrategicMergePatchUsingLookupPatchMeta(md.templateJSON, patch, md.patchSchema) + if err != nil { + return err + } + // Unmarshal the merged JSON to a pointer, and return it. + return json.Unmarshal(mergedAsJSON, out) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/openapi_generated.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/openapi_generated.go new file mode 100644 index 0000000000..529e69d5de --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/openapi_generated.go @@ -0,0 +1,4938 @@ +// +build !ignore_autogenerated + +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by openapi-gen. DO NOT EDIT. + +// This file was autogenerated by openapi-gen. Do not edit it manually! + +package v1beta1 + +import ( + common "k8s.io/kube-openapi/pkg/common" + spec "k8s.io/kube-openapi/pkg/validation/spec" +) + +func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { + return map[string]common.OpenAPIDefinition{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/pod.Template": schema_pkg_apis_pipeline_pod_Template(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ArrayOrString": schema_pkg_apis_pipeline_v1beta1_ArrayOrString(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.CloudEventDelivery": schema_pkg_apis_pipeline_v1beta1_CloudEventDelivery(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.CloudEventDeliveryState": schema_pkg_apis_pipeline_v1beta1_CloudEventDeliveryState(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ClusterTask": schema_pkg_apis_pipeline_v1beta1_ClusterTask(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ClusterTaskList": schema_pkg_apis_pipeline_v1beta1_ClusterTaskList(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ConditionCheck": schema_pkg_apis_pipeline_v1beta1_ConditionCheck(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ConditionCheckStatus": schema_pkg_apis_pipeline_v1beta1_ConditionCheckStatus(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ConditionCheckStatusFields": schema_pkg_apis_pipeline_v1beta1_ConditionCheckStatusFields(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.EmbeddedTask": schema_pkg_apis_pipeline_v1beta1_EmbeddedTask(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.InternalTaskModifier": schema_pkg_apis_pipeline_v1beta1_InternalTaskModifier(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Param": schema_pkg_apis_pipeline_v1beta1_Param(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ParamSpec": schema_pkg_apis_pipeline_v1beta1_ParamSpec(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Pipeline": schema_pkg_apis_pipeline_v1beta1_Pipeline(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineDeclaredResource": schema_pkg_apis_pipeline_v1beta1_PipelineDeclaredResource(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineList": schema_pkg_apis_pipeline_v1beta1_PipelineList(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRef": schema_pkg_apis_pipeline_v1beta1_PipelineRef(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResourceBinding": schema_pkg_apis_pipeline_v1beta1_PipelineResourceBinding(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResourceRef": schema_pkg_apis_pipeline_v1beta1_PipelineResourceRef(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResourceResult": schema_pkg_apis_pipeline_v1beta1_PipelineResourceResult(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResult": schema_pkg_apis_pipeline_v1beta1_PipelineResult(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRun": schema_pkg_apis_pipeline_v1beta1_PipelineRun(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunConditionCheckStatus": schema_pkg_apis_pipeline_v1beta1_PipelineRunConditionCheckStatus(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunList": schema_pkg_apis_pipeline_v1beta1_PipelineRunList(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunResult": schema_pkg_apis_pipeline_v1beta1_PipelineRunResult(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunRunStatus": schema_pkg_apis_pipeline_v1beta1_PipelineRunRunStatus(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunSpec": schema_pkg_apis_pipeline_v1beta1_PipelineRunSpec(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunSpecServiceAccountName": schema_pkg_apis_pipeline_v1beta1_PipelineRunSpecServiceAccountName(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunStatus": schema_pkg_apis_pipeline_v1beta1_PipelineRunStatus(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunStatusFields": schema_pkg_apis_pipeline_v1beta1_PipelineRunStatusFields(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunTaskRunStatus": schema_pkg_apis_pipeline_v1beta1_PipelineRunTaskRunStatus(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineSpec": schema_pkg_apis_pipeline_v1beta1_PipelineSpec(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTask": schema_pkg_apis_pipeline_v1beta1_PipelineTask(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskCondition": schema_pkg_apis_pipeline_v1beta1_PipelineTaskCondition(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskInputResource": schema_pkg_apis_pipeline_v1beta1_PipelineTaskInputResource(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskMetadata": schema_pkg_apis_pipeline_v1beta1_PipelineTaskMetadata(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskOutputResource": schema_pkg_apis_pipeline_v1beta1_PipelineTaskOutputResource(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskParam": schema_pkg_apis_pipeline_v1beta1_PipelineTaskParam(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskResources": schema_pkg_apis_pipeline_v1beta1_PipelineTaskResources(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskRun": schema_pkg_apis_pipeline_v1beta1_PipelineTaskRun(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskRunSpec": schema_pkg_apis_pipeline_v1beta1_PipelineTaskRunSpec(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineWorkspaceDeclaration": schema_pkg_apis_pipeline_v1beta1_PipelineWorkspaceDeclaration(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ResolverParam": schema_pkg_apis_pipeline_v1beta1_ResolverParam(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ResolverRef": schema_pkg_apis_pipeline_v1beta1_ResolverRef(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ResultRef": schema_pkg_apis_pipeline_v1beta1_ResultRef(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Sidecar": schema_pkg_apis_pipeline_v1beta1_Sidecar(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.SidecarState": schema_pkg_apis_pipeline_v1beta1_SidecarState(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.SkippedTask": schema_pkg_apis_pipeline_v1beta1_SkippedTask(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Step": schema_pkg_apis_pipeline_v1beta1_Step(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.StepState": schema_pkg_apis_pipeline_v1beta1_StepState(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Task": schema_pkg_apis_pipeline_v1beta1_Task(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskList": schema_pkg_apis_pipeline_v1beta1_TaskList(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRef": schema_pkg_apis_pipeline_v1beta1_TaskRef(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResource": schema_pkg_apis_pipeline_v1beta1_TaskResource(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResourceBinding": schema_pkg_apis_pipeline_v1beta1_TaskResourceBinding(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResources": schema_pkg_apis_pipeline_v1beta1_TaskResources(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResult": schema_pkg_apis_pipeline_v1beta1_TaskResult(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRun": schema_pkg_apis_pipeline_v1beta1_TaskRun(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunDebug": schema_pkg_apis_pipeline_v1beta1_TaskRunDebug(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunInputs": schema_pkg_apis_pipeline_v1beta1_TaskRunInputs(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunList": schema_pkg_apis_pipeline_v1beta1_TaskRunList(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunOutputs": schema_pkg_apis_pipeline_v1beta1_TaskRunOutputs(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunResources": schema_pkg_apis_pipeline_v1beta1_TaskRunResources(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunResult": schema_pkg_apis_pipeline_v1beta1_TaskRunResult(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunSidecarOverride": schema_pkg_apis_pipeline_v1beta1_TaskRunSidecarOverride(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunSpec": schema_pkg_apis_pipeline_v1beta1_TaskRunSpec(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStatus": schema_pkg_apis_pipeline_v1beta1_TaskRunStatus(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStatusFields": schema_pkg_apis_pipeline_v1beta1_TaskRunStatusFields(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStepOverride": schema_pkg_apis_pipeline_v1beta1_TaskRunStepOverride(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskSpec": schema_pkg_apis_pipeline_v1beta1_TaskSpec(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TimeoutFields": schema_pkg_apis_pipeline_v1beta1_TimeoutFields(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WhenExpression": schema_pkg_apis_pipeline_v1beta1_WhenExpression(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspaceBinding": schema_pkg_apis_pipeline_v1beta1_WorkspaceBinding(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspaceDeclaration": schema_pkg_apis_pipeline_v1beta1_WorkspaceDeclaration(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspacePipelineTaskBinding": schema_pkg_apis_pipeline_v1beta1_WorkspacePipelineTaskBinding(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspaceUsage": schema_pkg_apis_pipeline_v1beta1_WorkspaceUsage(ref), + "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.PipelineResource": schema_pkg_apis_resource_v1alpha1_PipelineResource(ref), + "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.PipelineResourceList": schema_pkg_apis_resource_v1alpha1_PipelineResourceList(ref), + "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.PipelineResourceSpec": schema_pkg_apis_resource_v1alpha1_PipelineResourceSpec(ref), + "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.PipelineResourceStatus": schema_pkg_apis_resource_v1alpha1_PipelineResourceStatus(ref), + "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.ResourceDeclaration": schema_pkg_apis_resource_v1alpha1_ResourceDeclaration(ref), + "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.ResourceParam": schema_pkg_apis_resource_v1alpha1_ResourceParam(ref), + "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.SecretParam": schema_pkg_apis_resource_v1alpha1_SecretParam(ref), + } +} + +func schema_pkg_apis_pipeline_pod_Template(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodTemplate holds pod specific configuration", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "nodeSelector": { + SchemaProps: spec.SchemaProps{ + Description: "NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "tolerations": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the pod's tolerations.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.Toleration"), + }, + }, + }, + }, + }, + "affinity": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the pod's scheduling constraints", + Ref: ref("k8s.io/api/core/v1.Affinity"), + }, + }, + "securityContext": { + SchemaProps: spec.SchemaProps{ + Description: "SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field.", + Ref: ref("k8s.io/api/core/v1.PodSecurityContext"), + }, + }, + "volumes": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge,retainKeys", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.Volume"), + }, + }, + }, + }, + }, + "runtimeClassName": { + SchemaProps: spec.SchemaProps{ + Description: "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md This is a beta feature as of Kubernetes v1.14.", + Type: []string{"string"}, + Format: "", + }, + }, + "automountServiceAccountToken": { + SchemaProps: spec.SchemaProps{ + Description: "AutomountServiceAccountToken indicates whether pods running as this service account should have an API token automatically mounted.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "dnsPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Set DNS policy for the pod. Defaults to \"ClusterFirst\". Valid values are 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy.", + Type: []string{"string"}, + Format: "", + }, + }, + "dnsConfig": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies the DNS parameters of a pod. Parameters specified here will be merged to the generated DNS configuration based on DNSPolicy.", + Ref: ref("k8s.io/api/core/v1.PodDNSConfig"), + }, + }, + "enableServiceLinks": { + SchemaProps: spec.SchemaProps{ + Description: "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "priorityClassName": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default.", + Type: []string{"string"}, + Format: "", + }, + }, + "schedulerName": { + SchemaProps: spec.SchemaProps{ + Description: "SchedulerName specifies the scheduler to be used to dispatch the Pod", + Type: []string{"string"}, + Format: "", + }, + }, + "imagePullSecrets": { + SchemaProps: spec.SchemaProps{ + Description: "ImagePullSecrets gives the name of the secret used by the pod to pull the image if specified", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + }, + }, + }, + "hostAliases": { + SchemaProps: spec.SchemaProps{ + Description: "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.HostAlias"), + }, + }, + }, + }, + }, + "hostNetwork": { + SchemaProps: spec.SchemaProps{ + Description: "HostNetwork specifies whether the pod may use the node network namespace", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_ArrayOrString(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ArrayOrString is a type that can hold a single string or string array. Used in JSON unmarshalling so that a single JSON field can accept either an individual string or an array of strings.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "stringVal": { + SchemaProps: spec.SchemaProps{ + Description: "Represents the stored type of ArrayOrString.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "arrayVal": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"type", "stringVal", "arrayVal"}, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_CloudEventDelivery(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CloudEventDelivery is the target of a cloud event along with the state of delivery.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "target": { + SchemaProps: spec.SchemaProps{ + Description: "Target points to an addressable", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.CloudEventDeliveryState"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.CloudEventDeliveryState"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_CloudEventDeliveryState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CloudEventDeliveryState reports the state of a cloud event to be sent.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "condition": { + SchemaProps: spec.SchemaProps{ + Description: "Current status", + Type: []string{"string"}, + Format: "", + }, + }, + "sentAt": { + SchemaProps: spec.SchemaProps{ + Description: "SentAt is the time at which the last attempt to send the event was made", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Error is the text of error (if any)", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "retryCount": { + SchemaProps: spec.SchemaProps{ + Description: "RetryCount is the number of attempts of sending the cloud event", + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"message", "retryCount"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_ClusterTask(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterTask is a Task with a cluster scope. ClusterTasks are used to represent Tasks that should be publicly addressable from any namespace in the cluster.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec holds the desired state of the Task from the client", + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskSpec"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_ClusterTaskList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterTaskList contains a list of ClusterTask", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ClusterTask"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ClusterTask", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_ConditionCheck(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ConditionCheck represents a single evaluation of a Condition step.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunSpec", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_ConditionCheckStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ConditionCheckStatus defines the observed state of ConditionCheck", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "observedGeneration": { + SchemaProps: spec.SchemaProps{ + Description: "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Conditions the latest available observations of a resource's current state.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("knative.dev/pkg/apis.Condition"), + }, + }, + }, + }, + }, + "annotations": { + SchemaProps: spec.SchemaProps{ + Description: "Annotations is additional Status fields for the Resource to save some additional State as well as convey more information to the user. This is roughly akin to Annotations on any k8s resource, just the reconciler conveying richer information outwards.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "podName": { + SchemaProps: spec.SchemaProps{ + Description: "PodName is the name of the pod responsible for executing this condition check.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "startTime": { + SchemaProps: spec.SchemaProps{ + Description: "StartTime is the time the check is actually started.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "completionTime": { + SchemaProps: spec.SchemaProps{ + Description: "CompletionTime is the time the check pod completed.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "check": { + SchemaProps: spec.SchemaProps{ + Description: "Check describes the state of the check container.", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ContainerState"), + }, + }, + }, + Required: []string{"podName"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerState", "k8s.io/apimachinery/pkg/apis/meta/v1.Time", "knative.dev/pkg/apis.Condition"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_ConditionCheckStatusFields(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ConditionCheckStatusFields holds the fields of ConfigurationCheck's status. This is defined separately and inlined so that other types can readily consume these fields via duck typing.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "podName": { + SchemaProps: spec.SchemaProps{ + Description: "PodName is the name of the pod responsible for executing this condition check.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "startTime": { + SchemaProps: spec.SchemaProps{ + Description: "StartTime is the time the check is actually started.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "completionTime": { + SchemaProps: spec.SchemaProps{ + Description: "CompletionTime is the time the check pod completed.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "check": { + SchemaProps: spec.SchemaProps{ + Description: "Check describes the state of the check container.", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ContainerState"), + }, + }, + }, + Required: []string{"podName"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerState", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_EmbeddedTask(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EmbeddedTask is used to define a Task inline within a Pipeline's PipelineTasks.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec is a specification of a custom task", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskMetadata"), + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources is a list input and output resource to run the task Resources are represented in TaskRuns as bindings to instances of PipelineResources.", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResources"), + }, + }, + "params": { + SchemaProps: spec.SchemaProps{ + Description: "Params is a list of input parameters required to run the task. Params must be supplied as inputs in TaskRuns unless they declare a default value.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ParamSpec"), + }, + }, + }, + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a user-facing description of the task that may be used to populate a UI.", + Type: []string{"string"}, + Format: "", + }, + }, + "steps": { + SchemaProps: spec.SchemaProps{ + Description: "Steps are the steps of the build; each step is run sequentially with the source mounted into /workspace.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Step"), + }, + }, + }, + }, + }, + "volumes": { + SchemaProps: spec.SchemaProps{ + Description: "Volumes is a collection of volumes that are available to mount into the steps of the build.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.Volume"), + }, + }, + }, + }, + }, + "stepTemplate": { + SchemaProps: spec.SchemaProps{ + Description: "StepTemplate can be used as the basis for all step containers within the Task, so that the steps inherit settings on the base container.", + Ref: ref("k8s.io/api/core/v1.Container"), + }, + }, + "sidecars": { + SchemaProps: spec.SchemaProps{ + Description: "Sidecars are run alongside the Task's step containers. They begin before the steps start and end after the steps complete.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Sidecar"), + }, + }, + }, + }, + }, + "workspaces": { + SchemaProps: spec.SchemaProps{ + Description: "Workspaces are the volumes that this Task requires.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspaceDeclaration"), + }, + }, + }, + }, + }, + "results": { + SchemaProps: spec.SchemaProps{ + Description: "Results are values that this Task can output", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResult"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ParamSpec", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskMetadata", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Sidecar", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Step", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResources", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResult", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspaceDeclaration", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_InternalTaskModifier(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "InternalTaskModifier implements TaskModifier for resources that are built-in to Tekton Pipelines.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "StepsToPrepend": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Step"), + }, + }, + }, + }, + }, + "StepsToAppend": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Step"), + }, + }, + }, + }, + }, + "Volumes": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.Volume"), + }, + }, + }, + }, + }, + }, + Required: []string{"StepsToPrepend", "StepsToAppend", "Volumes"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Step", "k8s.io/api/core/v1.Volume"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_Param(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Param declares an ArrayOrString to use for the parameter called name.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ArrayOrString"), + }, + }, + }, + Required: []string{"name", "value"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ArrayOrString"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_ParamSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ParamSpec defines arbitrary parameters needed beyond typed inputs (such as resources). Parameter values are provided by users as inputs on a TaskRun or PipelineRun.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name declares the name by which a parameter is referenced.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type is the user-specified type of the parameter. The possible types are currently \"string\" and \"array\", and \"string\" is the default.", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a user-facing description of the parameter that may be used to populate a UI.", + Type: []string{"string"}, + Format: "", + }, + }, + "default": { + SchemaProps: spec.SchemaProps{ + Description: "Default is the value a parameter takes if no input value is supplied. If default is set, a Task may be executed without a supplied value for the parameter.", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ArrayOrString"), + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ArrayOrString"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_Pipeline(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Pipeline describes a list of Tasks to execute. It expresses how outputs of tasks feed into inputs of subsequent tasks.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec holds the desired state of the Pipeline from the client", + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineSpec"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineDeclaredResource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineDeclaredResource is used by a Pipeline to declare the types of the PipelineResources that it will required to run and names which can be used to refer to these PipelineResources in PipelineTaskResourceBindings.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name that will be used by the Pipeline to refer to this resource. It does not directly correspond to the name of any PipelineResources Task inputs or outputs, and it does not correspond to the actual names of the PipelineResources that will be bound in the PipelineRun.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type is the type of the PipelineResource.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Optional declares the resource as optional. optional: true - the resource is considered optional optional: false - the resource is considered required (default/equivalent of not specifying it)", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"name", "type"}, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineList contains a list of Pipeline", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Pipeline"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Pipeline", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineRef(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineRef can be used to refer to a specific instance of a Pipeline. Copied from CrossVersionObjectReference: https://github.com/kubernetes/kubernetes/blob/169df7434155cbbc22f1532cba8e0a9588e29ad8/pkg/apis/autoscaling/types.go#L64", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "API version of the referent", + Type: []string{"string"}, + Format: "", + }, + }, + "bundle": { + SchemaProps: spec.SchemaProps{ + Description: "Bundle url reference to a Tekton Bundle.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineResourceBinding(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineResourceBinding connects a reference to an instance of a PipelineResource with a PipelineResource dependency that the Pipeline has declared", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the PipelineResource in the Pipeline's declaration", + Type: []string{"string"}, + Format: "", + }, + }, + "resourceRef": { + SchemaProps: spec.SchemaProps{ + Description: "ResourceRef is a reference to the instance of the actual PipelineResource that should be used", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResourceRef"), + }, + }, + "resourceSpec": { + SchemaProps: spec.SchemaProps{ + Description: "ResourceSpec is specification of a resource that should be created and consumed by the task", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.PipelineResourceSpec"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResourceRef", "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.PipelineResourceSpec"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineResourceRef(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineResourceRef can be used to refer to a specific instance of a Resource", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "API version of the referent", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineResourceResult(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineResourceResult used to export the image name and digest as json", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "resourceName": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "resourceRef": { + SchemaProps: spec.SchemaProps{ + Description: "The field ResourceRef should be deprecated and removed in the next API version. See https://github.com/tektoncd/pipeline/issues/2694 for more information.", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResourceRef"), + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"key", "value"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResourceRef"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineResult(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineResult used to describe the results of a pipeline", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name the given name", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a human-readable description of the result", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Value the expression used to retrieve the value", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "value"}, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineRun(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineRun represents a single execution of a Pipeline. PipelineRuns are how the graph of Tasks declared in a Pipeline are executed; they specify inputs to Pipelines such as parameter values and capture operational aspects of the Tasks execution such as service account and tolerations. Creating a PipelineRun creates TaskRuns for Tasks in the referenced Pipeline.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunSpec", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineRunConditionCheckStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineRunConditionCheckStatus returns the condition check status", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "conditionName": { + SchemaProps: spec.SchemaProps{ + Description: "ConditionName is the name of the Condition", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status is the ConditionCheckStatus for the corresponding ConditionCheck", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ConditionCheckStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ConditionCheckStatus"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineRunList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineRunList contains a list of PipelineRun", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRun"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRun", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineRunResult(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineRunResult used to describe the results of a pipeline", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the result's name as declared by the Pipeline", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Value is the result returned from the execution of this PipelineRun", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "value"}, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineRunRunStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineRunRunStatus contains the name of the PipelineTask for this Run and the Run's Status", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "pipelineTaskName": { + SchemaProps: spec.SchemaProps{ + Description: "PipelineTaskName is the name of the PipelineTask.", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status is the RunStatus for the corresponding Run", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/run/v1alpha1.RunStatus"), + }, + }, + "whenExpressions": { + SchemaProps: spec.SchemaProps{ + Description: "WhenExpressions is the list of checks guarding the execution of the PipelineTask", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WhenExpression"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WhenExpression", "github.com/tektoncd/pipeline/pkg/apis/run/v1alpha1.RunStatus"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineRunSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineRunSpec defines the desired state of PipelineRun", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "pipelineRef": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRef"), + }, + }, + "pipelineSpec": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineSpec"), + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources is a list of bindings specifying which actual instances of PipelineResources to use for the resources the Pipeline has declared it needs.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResourceBinding"), + }, + }, + }, + }, + }, + "params": { + SchemaProps: spec.SchemaProps{ + Description: "Params is a list of parameter names and values.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Param"), + }, + }, + }, + }, + }, + "serviceAccountName": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "serviceAccountNames": { + SchemaProps: spec.SchemaProps{ + Description: "Deprecated: use taskRunSpecs.ServiceAccountName instead", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunSpecServiceAccountName"), + }, + }, + }, + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Used for cancelling a pipelinerun (and maybe more later on)", + Type: []string{"string"}, + Format: "", + }, + }, + "timeouts": { + SchemaProps: spec.SchemaProps{ + Description: "This is an alpha field. You must set the \"enable-api-fields\" feature flag to \"alpha\" for this field to be supported.\n\nTime after which the Pipeline times out. Currently three keys are accepted in the map pipeline, tasks and finally with Timeouts.pipeline >= Timeouts.tasks + Timeouts.finally", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TimeoutFields"), + }, + }, + "timeout": { + SchemaProps: spec.SchemaProps{ + Description: "Time after which the Pipeline times out. Defaults to never. Refer to Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"), + }, + }, + "podTemplate": { + SchemaProps: spec.SchemaProps{ + Description: "PodTemplate holds pod specific configuration", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/pod.Template"), + }, + }, + "workspaces": { + SchemaProps: spec.SchemaProps{ + Description: "Workspaces holds a set of workspace bindings that must match names with those declared in the pipeline.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspaceBinding"), + }, + }, + }, + }, + }, + "taskRunSpecs": { + SchemaProps: spec.SchemaProps{ + Description: "TaskRunSpecs holds a set of runtime specs", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskRunSpec"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/pod.Template", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Param", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRef", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResourceBinding", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunSpecServiceAccountName", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineSpec", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskRunSpec", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TimeoutFields", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspaceBinding", "k8s.io/apimachinery/pkg/apis/meta/v1.Duration"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineRunSpecServiceAccountName(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineRunSpecServiceAccountName can be used to configure specific ServiceAccountName for a concrete Task", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "taskName": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "serviceAccountName": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineRunStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineRunStatus defines the observed state of PipelineRun", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "observedGeneration": { + SchemaProps: spec.SchemaProps{ + Description: "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Conditions the latest available observations of a resource's current state.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("knative.dev/pkg/apis.Condition"), + }, + }, + }, + }, + }, + "annotations": { + SchemaProps: spec.SchemaProps{ + Description: "Annotations is additional Status fields for the Resource to save some additional State as well as convey more information to the user. This is roughly akin to Annotations on any k8s resource, just the reconciler conveying richer information outwards.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "startTime": { + SchemaProps: spec.SchemaProps{ + Description: "StartTime is the time the PipelineRun is actually started.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "completionTime": { + SchemaProps: spec.SchemaProps{ + Description: "CompletionTime is the time the PipelineRun completed.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "taskRuns": { + SchemaProps: spec.SchemaProps{ + Description: "map of PipelineRunTaskRunStatus with the taskRun name as the key", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunTaskRunStatus"), + }, + }, + }, + }, + }, + "runs": { + SchemaProps: spec.SchemaProps{ + Description: "map of PipelineRunRunStatus with the run name as the key", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunRunStatus"), + }, + }, + }, + }, + }, + "pipelineResults": { + SchemaProps: spec.SchemaProps{ + Description: "PipelineResults are the list of results written out by the pipeline task's containers", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunResult"), + }, + }, + }, + }, + }, + "pipelineSpec": { + SchemaProps: spec.SchemaProps{ + Description: "PipelineRunSpec contains the exact spec used to instantiate the run", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineSpec"), + }, + }, + "skippedTasks": { + SchemaProps: spec.SchemaProps{ + Description: "list of tasks that were skipped due to when expressions evaluating to false", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.SkippedTask"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunResult", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunRunStatus", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunTaskRunStatus", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineSpec", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.SkippedTask", "k8s.io/apimachinery/pkg/apis/meta/v1.Time", "knative.dev/pkg/apis.Condition"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineRunStatusFields(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineRunStatusFields holds the fields of PipelineRunStatus' status. This is defined separately and inlined so that other types can readily consume these fields via duck typing.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "startTime": { + SchemaProps: spec.SchemaProps{ + Description: "StartTime is the time the PipelineRun is actually started.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "completionTime": { + SchemaProps: spec.SchemaProps{ + Description: "CompletionTime is the time the PipelineRun completed.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "taskRuns": { + SchemaProps: spec.SchemaProps{ + Description: "map of PipelineRunTaskRunStatus with the taskRun name as the key", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunTaskRunStatus"), + }, + }, + }, + }, + }, + "runs": { + SchemaProps: spec.SchemaProps{ + Description: "map of PipelineRunRunStatus with the run name as the key", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunRunStatus"), + }, + }, + }, + }, + }, + "pipelineResults": { + SchemaProps: spec.SchemaProps{ + Description: "PipelineResults are the list of results written out by the pipeline task's containers", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunResult"), + }, + }, + }, + }, + }, + "pipelineSpec": { + SchemaProps: spec.SchemaProps{ + Description: "PipelineRunSpec contains the exact spec used to instantiate the run", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineSpec"), + }, + }, + "skippedTasks": { + SchemaProps: spec.SchemaProps{ + Description: "list of tasks that were skipped due to when expressions evaluating to false", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.SkippedTask"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunResult", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunRunStatus", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunTaskRunStatus", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineSpec", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.SkippedTask", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineRunTaskRunStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineRunTaskRunStatus contains the name of the PipelineTask for this TaskRun and the TaskRun's Status", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "pipelineTaskName": { + SchemaProps: spec.SchemaProps{ + Description: "PipelineTaskName is the name of the PipelineTask.", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status is the TaskRunStatus for the corresponding TaskRun", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStatus"), + }, + }, + "conditionChecks": { + SchemaProps: spec.SchemaProps{ + Description: "ConditionChecks maps the name of a condition check to its Status", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunConditionCheckStatus"), + }, + }, + }, + }, + }, + "whenExpressions": { + SchemaProps: spec.SchemaProps{ + Description: "WhenExpressions is the list of checks guarding the execution of the PipelineTask", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WhenExpression"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineRunConditionCheckStatus", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStatus", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WhenExpression"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineSpec defines the desired state of Pipeline.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a user-facing description of the pipeline that may be used to populate a UI.", + Type: []string{"string"}, + Format: "", + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources declares the names and types of the resources given to the Pipeline's tasks as inputs and outputs.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineDeclaredResource"), + }, + }, + }, + }, + }, + "tasks": { + SchemaProps: spec.SchemaProps{ + Description: "Tasks declares the graph of Tasks that execute when this Pipeline is run.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTask"), + }, + }, + }, + }, + }, + "params": { + SchemaProps: spec.SchemaProps{ + Description: "Params declares a list of input parameters that must be supplied when this Pipeline is run.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ParamSpec"), + }, + }, + }, + }, + }, + "workspaces": { + SchemaProps: spec.SchemaProps{ + Description: "Workspaces declares a set of named workspaces that are expected to be provided by a PipelineRun.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineWorkspaceDeclaration"), + }, + }, + }, + }, + }, + "results": { + SchemaProps: spec.SchemaProps{ + Description: "Results are values that this pipeline can output once run", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResult"), + }, + }, + }, + }, + }, + "finally": { + SchemaProps: spec.SchemaProps{ + Description: "Finally declares the list of Tasks that execute just before leaving the Pipeline i.e. either after all Tasks are finished executing successfully or after a failure which would result in ending the Pipeline", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTask"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ParamSpec", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineDeclaredResource", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResult", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTask", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineWorkspaceDeclaration"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineTask(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineTask defines a task in a Pipeline, passing inputs from both Params and from the output of previous tasks.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of this task within the context of a Pipeline. Name is used as a coordinate with the `from` and `runAfter` fields to establish the execution order of tasks relative to one another.", + Type: []string{"string"}, + Format: "", + }, + }, + "taskRef": { + SchemaProps: spec.SchemaProps{ + Description: "TaskRef is a reference to a task definition.", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRef"), + }, + }, + "taskSpec": { + SchemaProps: spec.SchemaProps{ + Description: "TaskSpec is a specification of a task", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.EmbeddedTask"), + }, + }, + "conditions": { + SchemaProps: spec.SchemaProps{ + Description: "Conditions is a list of conditions that need to be true for the task to run Conditions are deprecated, use WhenExpressions instead", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskCondition"), + }, + }, + }, + }, + }, + "when": { + SchemaProps: spec.SchemaProps{ + Description: "WhenExpressions is a list of when expressions that need to be true for the task to run", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WhenExpression"), + }, + }, + }, + }, + }, + "retries": { + SchemaProps: spec.SchemaProps{ + Description: "Retries represents how many times this task should be retried in case of task failure: ConditionSucceeded set to False", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "runAfter": { + SchemaProps: spec.SchemaProps{ + Description: "RunAfter is the list of PipelineTask names that should be executed before this Task executes. (Used to force a specific ordering in graph execution.)", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources declares the resources given to this task as inputs and outputs.", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskResources"), + }, + }, + "params": { + SchemaProps: spec.SchemaProps{ + Description: "Parameters declares parameters passed to this task.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Param"), + }, + }, + }, + }, + }, + "matrix": { + SchemaProps: spec.SchemaProps{ + Description: "Matrix declares parameters used to fan out this task.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Param"), + }, + }, + }, + }, + }, + "workspaces": { + SchemaProps: spec.SchemaProps{ + Description: "Workspaces maps workspaces from the pipeline spec to the workspaces declared in the Task.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspacePipelineTaskBinding"), + }, + }, + }, + }, + }, + "timeout": { + SchemaProps: spec.SchemaProps{ + Description: "Time after which the TaskRun times out. Defaults to 1 hour. Specified TaskRun timeout should be less than 24h. Refer Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.EmbeddedTask", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Param", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskCondition", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskResources", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRef", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WhenExpression", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspacePipelineTaskBinding", "k8s.io/apimachinery/pkg/apis/meta/v1.Duration"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineTaskCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineTaskCondition allows a PipelineTask to declare a Condition to be evaluated before the Task is run.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "conditionRef": { + SchemaProps: spec.SchemaProps{ + Description: "ConditionRef is the name of the Condition to use for the conditionCheck", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "params": { + SchemaProps: spec.SchemaProps{ + Description: "Params declare parameters passed to this Condition", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Param"), + }, + }, + }, + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources declare the resources provided to this Condition as input", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskInputResource"), + }, + }, + }, + }, + }, + }, + Required: []string{"conditionRef"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Param", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskInputResource"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineTaskInputResource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineTaskInputResource maps the name of a declared PipelineResource input dependency in a Task to the resource in the Pipeline's DeclaredPipelineResources that should be used. This input may come from a previous task.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the PipelineResource as declared by the Task.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Description: "Resource is the name of the DeclaredPipelineResource to use.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "from": { + SchemaProps: spec.SchemaProps{ + Description: "From is the list of PipelineTask names that the resource has to come from. (Implies an ordering in the execution graph.)", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"name", "resource"}, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineTaskMetadata(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineTaskMetadata contains the labels or annotations for an EmbeddedTask", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "labels": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "annotations": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineTaskOutputResource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineTaskOutputResource maps the name of a declared PipelineResource output dependency in a Task to the resource in the Pipeline's DeclaredPipelineResources that should be used.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the PipelineResource as declared by the Task.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Description: "Resource is the name of the DeclaredPipelineResource to use.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "resource"}, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineTaskParam(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineTaskParam is used to provide arbitrary string parameters to a Task.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "value"}, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineTaskResources(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineTaskResources allows a Pipeline to declare how its DeclaredPipelineResources should be provided to a Task as its inputs and outputs.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "inputs": { + SchemaProps: spec.SchemaProps{ + Description: "Inputs holds the mapping from the PipelineResources declared in DeclaredPipelineResources to the input PipelineResources required by the Task.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskInputResource"), + }, + }, + }, + }, + }, + "outputs": { + SchemaProps: spec.SchemaProps{ + Description: "Outputs holds the mapping from the PipelineResources declared in DeclaredPipelineResources to the input PipelineResources required by the Task.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskOutputResource"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskInputResource", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineTaskOutputResource"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineTaskRun(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineTaskRun reports the results of running a step in the Task. Each task has the potential to succeed or fail (based on the exit code) and produces logs.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineTaskRunSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineTaskRunSpec can be used to configure specific specs for a concrete Task", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "pipelineTaskName": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "taskServiceAccountName": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "taskPodTemplate": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/pod.Template"), + }, + }, + "stepOverrides": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStepOverride"), + }, + }, + }, + }, + }, + "sidecarOverrides": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunSidecarOverride"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/pod.Template", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunSidecarOverride", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStepOverride"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_PipelineWorkspaceDeclaration(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "WorkspacePipelineDeclaration creates a named slot in a Pipeline that a PipelineRun is expected to populate with a workspace binding. Deprecated: use PipelineWorkspaceDeclaration type instead", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of a workspace to be provided by a PipelineRun.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a human readable string describing how the workspace will be used in the Pipeline. It can be useful to include a bit of detail about which tasks are intended to have access to the data on the workspace.", + Type: []string{"string"}, + Format: "", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Optional marks a Workspace as not being required in PipelineRuns. By default this field is false and so declared workspaces are required.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_ResolverParam(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResolverParam is a single parameter passed to a resolver.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "Name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the parameter that will be passed to the resolver.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "Value": { + SchemaProps: spec.SchemaProps{ + Description: "Value is the string value of the parameter that will be passed to the resolver.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"Name", "Value"}, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_ResolverRef(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResolverRef can be used to refer to a Pipeline or Task in a remote location like a git repo. This feature is in alpha and these fields are only available when the alpha feature gate is enabled.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "resolver": { + SchemaProps: spec.SchemaProps{ + Description: "Resolver is the name of the resolver that should perform resolution of the referenced Tekton resource, such as \"git\".", + Type: []string{"string"}, + Format: "", + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Description: "Resource contains the parameters used to identify the referenced Tekton resource. Example entries might include \"repo\" or \"path\" but the set of params ultimately depends on the chosen resolver.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ResolverParam"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ResolverParam"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_ResultRef(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResultRef is a type that represents a reference to a task run result", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "PipelineTask": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "Result": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"PipelineTask", "Result"}, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_Sidecar(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Sidecar has nearly the same data structure as Step, consisting of a Container and an optional Script, but does not have the ability to timeout.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "image": { + SchemaProps: spec.SchemaProps{ + Description: "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.", + Type: []string{"string"}, + Format: "", + }, + }, + "command": { + SchemaProps: spec.SchemaProps{ + Description: "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "args": { + SchemaProps: spec.SchemaProps{ + Description: "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "workingDir": { + SchemaProps: spec.SchemaProps{ + Description: "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "ports": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "containerPort", + "protocol", + }, + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "containerPort", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ContainerPort"), + }, + }, + }, + }, + }, + "envFrom": { + SchemaProps: spec.SchemaProps{ + Description: "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.EnvFromSource"), + }, + }, + }, + }, + }, + "env": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of environment variables to set in the container. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.EnvVar"), + }, + }, + }, + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, + "volumeMounts": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "mountPath", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Pod volumes to mount into the container's filesystem. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.VolumeMount"), + }, + }, + }, + }, + }, + "volumeDevices": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "devicePath", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "volumeDevices is the list of block devices to be used by the container.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.VolumeDevice"), + }, + }, + }, + }, + }, + "livenessProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "readinessProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "startupProbe": { + SchemaProps: spec.SchemaProps{ + Description: "StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "lifecycle": { + SchemaProps: spec.SchemaProps{ + Description: "Actions that the management system should take in response to container lifecycle events. Cannot be updated.", + Ref: ref("k8s.io/api/core/v1.Lifecycle"), + }, + }, + "terminationMessagePath": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "terminationMessagePolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "imagePullPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", + Type: []string{"string"}, + Format: "", + }, + }, + "securityContext": { + SchemaProps: spec.SchemaProps{ + Description: "SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/", + Ref: ref("k8s.io/api/core/v1.SecurityContext"), + }, + }, + "stdin": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "stdinOnce": { + SchemaProps: spec.SchemaProps{ + Description: "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", + Type: []string{"boolean"}, + Format: "", + }, + }, + "tty": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "script": { + SchemaProps: spec.SchemaProps{ + Description: "Script is the contents of an executable file to execute.\n\nIf Script is not empty, the Step cannot have an Command or Args.", + Type: []string{"string"}, + Format: "", + }, + }, + "workspaces": { + SchemaProps: spec.SchemaProps{ + Description: "This is an alpha field. You must set the \"enable-api-fields\" feature flag to \"alpha\" for this field to be supported.\n\nWorkspaces is a list of workspaces from the Task that this Sidecar wants exclusive access to. Adding a workspace to this list means that any other Step or Sidecar that does not also request this Workspace will not have access to it.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspaceUsage"), + }, + }, + }, + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspaceUsage", "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_SidecarState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SidecarState reports the results of running a sidecar in a Task.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "waiting": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a waiting container", + Ref: ref("k8s.io/api/core/v1.ContainerStateWaiting"), + }, + }, + "running": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a running container", + Ref: ref("k8s.io/api/core/v1.ContainerStateRunning"), + }, + }, + "terminated": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a terminated container", + Ref: ref("k8s.io/api/core/v1.ContainerStateTerminated"), + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "container": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "imageID": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerStateRunning", "k8s.io/api/core/v1.ContainerStateTerminated", "k8s.io/api/core/v1.ContainerStateWaiting"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_SkippedTask(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SkippedTask is used to describe the Tasks that were skipped due to their When Expressions evaluating to False. This is a struct because we are looking into including more details about the When Expressions that caused this Task to be skipped.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the Pipeline Task name", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "whenExpressions": { + SchemaProps: spec.SchemaProps{ + Description: "WhenExpressions is the list of checks guarding the execution of the PipelineTask", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WhenExpression"), + }, + }, + }, + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WhenExpression"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_Step(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Step embeds the Container type, which allows it to include fields not provided by Container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "image": { + SchemaProps: spec.SchemaProps{ + Description: "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.", + Type: []string{"string"}, + Format: "", + }, + }, + "command": { + SchemaProps: spec.SchemaProps{ + Description: "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "args": { + SchemaProps: spec.SchemaProps{ + Description: "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "workingDir": { + SchemaProps: spec.SchemaProps{ + Description: "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "ports": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "containerPort", + "protocol", + }, + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "containerPort", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ContainerPort"), + }, + }, + }, + }, + }, + "envFrom": { + SchemaProps: spec.SchemaProps{ + Description: "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.EnvFromSource"), + }, + }, + }, + }, + }, + "env": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of environment variables to set in the container. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.EnvVar"), + }, + }, + }, + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, + "volumeMounts": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "mountPath", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Pod volumes to mount into the container's filesystem. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.VolumeMount"), + }, + }, + }, + }, + }, + "volumeDevices": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "devicePath", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "volumeDevices is the list of block devices to be used by the container.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.VolumeDevice"), + }, + }, + }, + }, + }, + "livenessProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "readinessProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "startupProbe": { + SchemaProps: spec.SchemaProps{ + Description: "StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "lifecycle": { + SchemaProps: spec.SchemaProps{ + Description: "Actions that the management system should take in response to container lifecycle events. Cannot be updated.", + Ref: ref("k8s.io/api/core/v1.Lifecycle"), + }, + }, + "terminationMessagePath": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "terminationMessagePolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "imagePullPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", + Type: []string{"string"}, + Format: "", + }, + }, + "securityContext": { + SchemaProps: spec.SchemaProps{ + Description: "SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/", + Ref: ref("k8s.io/api/core/v1.SecurityContext"), + }, + }, + "stdin": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "stdinOnce": { + SchemaProps: spec.SchemaProps{ + Description: "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", + Type: []string{"boolean"}, + Format: "", + }, + }, + "tty": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "script": { + SchemaProps: spec.SchemaProps{ + Description: "Script is the contents of an executable file to execute.\n\nIf Script is not empty, the Step cannot have an Command and the Args will be passed to the Script.", + Type: []string{"string"}, + Format: "", + }, + }, + "timeout": { + SchemaProps: spec.SchemaProps{ + Description: "Timeout is the time after which the step times out. Defaults to never. Refer to Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"), + }, + }, + "workspaces": { + SchemaProps: spec.SchemaProps{ + Description: "This is an alpha field. You must set the \"enable-api-fields\" feature flag to \"alpha\" for this field to be supported.\n\nWorkspaces is a list of workspaces from the Task that this Step wants exclusive access to. Adding a workspace to this list means that any other Step or Sidecar that does not also request this Workspace will not have access to it.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspaceUsage"), + }, + }, + }, + }, + }, + "onError": { + SchemaProps: spec.SchemaProps{ + Description: "OnError defines the exiting behavior of a container on error can be set to [ continue | stopAndFail ] stopAndFail indicates exit the taskRun if the container exits with non-zero exit code continue indicates continue executing the rest of the steps irrespective of the container exit code", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspaceUsage", "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount", "k8s.io/apimachinery/pkg/apis/meta/v1.Duration"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_StepState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "StepState reports the results of running a step in a Task.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "waiting": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a waiting container", + Ref: ref("k8s.io/api/core/v1.ContainerStateWaiting"), + }, + }, + "running": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a running container", + Ref: ref("k8s.io/api/core/v1.ContainerStateRunning"), + }, + }, + "terminated": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a terminated container", + Ref: ref("k8s.io/api/core/v1.ContainerStateTerminated"), + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "container": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "imageID": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerStateRunning", "k8s.io/api/core/v1.ContainerStateTerminated", "k8s.io/api/core/v1.ContainerStateWaiting"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_Task(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Task represents a collection of sequential steps that are run as part of a Pipeline using a set of inputs and producing a set of outputs. Tasks execute when TaskRuns are created that provide the input parameters and resources and output resources the Task requires.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec holds the desired state of the Task from the client", + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskSpec"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskList contains a list of Task", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Task"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Task", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskRef(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRef can be used to refer to a specific instance of a task. Copied from CrossVersionObjectReference: https://github.com/kubernetes/kubernetes/blob/169df7434155cbbc22f1532cba8e0a9588e29ad8/pkg/apis/autoscaling/types.go#L64", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names", + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "TaskKind indicates the kind of the task, namespaced or cluster scoped.", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "API version of the referent", + Type: []string{"string"}, + Format: "", + }, + }, + "bundle": { + SchemaProps: spec.SchemaProps{ + Description: "Bundle url reference to a Tekton Bundle.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskResource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskResource defines an input or output Resource declared as a requirement by a Task. The Name field will be used to refer to these Resources within the Task definition, and when provided as an Input, the Name will be the path to the volume mounted containing this Resource as an input (e.g. an input Resource named `workspace` will be mounted at `/workspace`).", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name declares the name by which a resource is referenced in the definition. Resources may be referenced by name in the definition of a Task's steps.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type is the type of this resource;", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a user-facing description of the declared resource that may be used to populate a UI.", + Type: []string{"string"}, + Format: "", + }, + }, + "targetPath": { + SchemaProps: spec.SchemaProps{ + Description: "TargetPath is the path in workspace directory where the resource will be copied.", + Type: []string{"string"}, + Format: "", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Optional declares the resource as optional. By default optional is set to false which makes a resource required. optional: true - the resource is considered optional optional: false - the resource is considered required (equivalent of not specifying it)", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"name", "type"}, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskResourceBinding(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskResourceBinding points to the PipelineResource that will be used for the Task input or output called Name.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the PipelineResource in the Pipeline's declaration", + Type: []string{"string"}, + Format: "", + }, + }, + "resourceRef": { + SchemaProps: spec.SchemaProps{ + Description: "ResourceRef is a reference to the instance of the actual PipelineResource that should be used", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResourceRef"), + }, + }, + "resourceSpec": { + SchemaProps: spec.SchemaProps{ + Description: "ResourceSpec is specification of a resource that should be created and consumed by the task", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.PipelineResourceSpec"), + }, + }, + "paths": { + SchemaProps: spec.SchemaProps{ + Description: "Paths will probably be removed in #1284, and then PipelineResourceBinding can be used instead. The optional Path field corresponds to a path on disk at which the Resource can be found (used when providing the resource via mounted volume, overriding the default logic to fetch the Resource).", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResourceRef", "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.PipelineResourceSpec"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskResources(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskResources allows a Pipeline to declare how its DeclaredPipelineResources should be provided to a Task as its inputs and outputs.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "inputs": { + SchemaProps: spec.SchemaProps{ + Description: "Inputs holds the mapping from the PipelineResources declared in DeclaredPipelineResources to the input PipelineResources required by the Task.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResource"), + }, + }, + }, + }, + }, + "outputs": { + SchemaProps: spec.SchemaProps{ + Description: "Outputs holds the mapping from the PipelineResources declared in DeclaredPipelineResources to the input PipelineResources required by the Task.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResource"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResource"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskResult(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskResult used to describe the results of a task", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name the given name", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a human-readable description of the result", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskRun(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRun represents a single execution of a Task. TaskRuns are how the steps specified in a Task are executed; they specify the parameters and resources used to run the steps in a Task.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunSpec", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskRunDebug(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunDebug defines the breakpoint config for a particular TaskRun", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "breakpoint": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskRunInputs(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunInputs holds the input values that this task was invoked with.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "resources": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResourceBinding"), + }, + }, + }, + }, + }, + "params": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Param"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Param", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResourceBinding"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskRunList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunList contains a list of TaskRun", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRun"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRun", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskRunOutputs(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunOutputs holds the output values that this task was invoked with.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "resources": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResourceBinding"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResourceBinding"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskRunResources(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunResources allows a TaskRun to declare inputs and outputs TaskResourceBinding", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "inputs": { + SchemaProps: spec.SchemaProps{ + Description: "Inputs holds the inputs resources this task was invoked with", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResourceBinding"), + }, + }, + }, + }, + }, + "outputs": { + SchemaProps: spec.SchemaProps{ + Description: "Outputs holds the inputs resources this task was invoked with", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResourceBinding"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResourceBinding"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskRunResult(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunResult used to describe the results of a task", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name the given name", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Value the given value of the result", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "value"}, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskRunSidecarOverride(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunSidecarOverride is used to override the values of a Sidecar in the corresponding Task.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "Name": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the Sidecar to override.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "Resources": { + SchemaProps: spec.SchemaProps{ + Description: "The resource requirements to apply to the Sidecar.", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, + }, + Required: []string{"Name", "Resources"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ResourceRequirements"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskRunSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunSpec defines the desired state of TaskRun", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "debug": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunDebug"), + }, + }, + "params": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Param"), + }, + }, + }, + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunResources"), + }, + }, + "serviceAccountName": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "taskRef": { + SchemaProps: spec.SchemaProps{ + Description: "no more than one of the TaskRef and TaskSpec may be specified.", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRef"), + }, + }, + "taskSpec": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Used for cancelling a taskrun (and maybe more later on)", + Type: []string{"string"}, + Format: "", + }, + }, + "timeout": { + SchemaProps: spec.SchemaProps{ + Description: "Time after which the build times out. Defaults to 1 hour. Specified build timeout should be less than 24h. Refer Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"), + }, + }, + "podTemplate": { + SchemaProps: spec.SchemaProps{ + Description: "PodTemplate holds pod specific configuration", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/pod.Template"), + }, + }, + "workspaces": { + SchemaProps: spec.SchemaProps{ + Description: "Workspaces is a list of WorkspaceBindings from volumes to workspaces.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspaceBinding"), + }, + }, + }, + }, + }, + "stepOverrides": { + SchemaProps: spec.SchemaProps{ + Description: "Overrides to apply to Steps in this TaskRun. If a field is specified in both a Step and a StepOverride, the value from the StepOverride will be used. This field is only supported when the alpha feature gate is enabled.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStepOverride"), + }, + }, + }, + }, + }, + "sidecarOverrides": { + SchemaProps: spec.SchemaProps{ + Description: "Overrides to apply to Sidecars in this TaskRun. If a field is specified in both a Sidecar and a SidecarOverride, the value from the SidecarOverride will be used. This field is only supported when the alpha feature gate is enabled.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunSidecarOverride"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/pod.Template", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Param", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRef", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunDebug", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunResources", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunSidecarOverride", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStepOverride", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskSpec", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspaceBinding", "k8s.io/apimachinery/pkg/apis/meta/v1.Duration"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskRunStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunStatus defines the observed state of TaskRun", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "observedGeneration": { + SchemaProps: spec.SchemaProps{ + Description: "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Conditions the latest available observations of a resource's current state.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("knative.dev/pkg/apis.Condition"), + }, + }, + }, + }, + }, + "annotations": { + SchemaProps: spec.SchemaProps{ + Description: "Annotations is additional Status fields for the Resource to save some additional State as well as convey more information to the user. This is roughly akin to Annotations on any k8s resource, just the reconciler conveying richer information outwards.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "podName": { + SchemaProps: spec.SchemaProps{ + Description: "PodName is the name of the pod responsible for executing this task's steps.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "startTime": { + SchemaProps: spec.SchemaProps{ + Description: "StartTime is the time the build is actually started.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "completionTime": { + SchemaProps: spec.SchemaProps{ + Description: "CompletionTime is the time the build completed.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "steps": { + SchemaProps: spec.SchemaProps{ + Description: "Steps describes the state of each build step container.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.StepState"), + }, + }, + }, + }, + }, + "cloudEvents": { + SchemaProps: spec.SchemaProps{ + Description: "CloudEvents describe the state of each cloud event requested via a CloudEventResource.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.CloudEventDelivery"), + }, + }, + }, + }, + }, + "retriesStatus": { + SchemaProps: spec.SchemaProps{ + Description: "RetriesStatus contains the history of TaskRunStatus in case of a retry in order to keep record of failures. All TaskRunStatus stored in RetriesStatus will have no date within the RetriesStatus as is redundant.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStatus"), + }, + }, + }, + }, + }, + "resourcesResult": { + SchemaProps: spec.SchemaProps{ + Description: "Results from Resources built during the taskRun. currently includes the digest of build container images", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResourceResult"), + }, + }, + }, + }, + }, + "taskResults": { + SchemaProps: spec.SchemaProps{ + Description: "TaskRunResults are the list of results written out by the task's containers", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunResult"), + }, + }, + }, + }, + }, + "sidecars": { + SchemaProps: spec.SchemaProps{ + Description: "The list has one entry per sidecar in the manifest. Each entry is represents the imageid of the corresponding sidecar.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.SidecarState"), + }, + }, + }, + }, + }, + "taskSpec": { + SchemaProps: spec.SchemaProps{ + Description: "TaskSpec contains the Spec from the dereferenced Task definition used to instantiate this TaskRun.", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskSpec"), + }, + }, + }, + Required: []string{"podName"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.CloudEventDelivery", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResourceResult", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.SidecarState", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.StepState", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunResult", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStatus", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.Time", "knative.dev/pkg/apis.Condition"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskRunStatusFields(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunStatusFields holds the fields of TaskRun's status. This is defined separately and inlined so that other types can readily consume these fields via duck typing.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "podName": { + SchemaProps: spec.SchemaProps{ + Description: "PodName is the name of the pod responsible for executing this task's steps.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "startTime": { + SchemaProps: spec.SchemaProps{ + Description: "StartTime is the time the build is actually started.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "completionTime": { + SchemaProps: spec.SchemaProps{ + Description: "CompletionTime is the time the build completed.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "steps": { + SchemaProps: spec.SchemaProps{ + Description: "Steps describes the state of each build step container.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.StepState"), + }, + }, + }, + }, + }, + "cloudEvents": { + SchemaProps: spec.SchemaProps{ + Description: "CloudEvents describe the state of each cloud event requested via a CloudEventResource.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.CloudEventDelivery"), + }, + }, + }, + }, + }, + "retriesStatus": { + SchemaProps: spec.SchemaProps{ + Description: "RetriesStatus contains the history of TaskRunStatus in case of a retry in order to keep record of failures. All TaskRunStatus stored in RetriesStatus will have no date within the RetriesStatus as is redundant.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStatus"), + }, + }, + }, + }, + }, + "resourcesResult": { + SchemaProps: spec.SchemaProps{ + Description: "Results from Resources built during the taskRun. currently includes the digest of build container images", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResourceResult"), + }, + }, + }, + }, + }, + "taskResults": { + SchemaProps: spec.SchemaProps{ + Description: "TaskRunResults are the list of results written out by the task's containers", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunResult"), + }, + }, + }, + }, + }, + "sidecars": { + SchemaProps: spec.SchemaProps{ + Description: "The list has one entry per sidecar in the manifest. Each entry is represents the imageid of the corresponding sidecar.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.SidecarState"), + }, + }, + }, + }, + }, + "taskSpec": { + SchemaProps: spec.SchemaProps{ + Description: "TaskSpec contains the Spec from the dereferenced Task definition used to instantiate this TaskRun.", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskSpec"), + }, + }, + }, + Required: []string{"podName"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.CloudEventDelivery", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.PipelineResourceResult", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.SidecarState", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.StepState", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunResult", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskRunStatus", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskRunStepOverride(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunStepOverride is used to override the values of a Step in the corresponding Task.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "Name": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the Step to override.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "Resources": { + SchemaProps: spec.SchemaProps{ + Description: "The resource requirements to apply to the Step.", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, + }, + Required: []string{"Name", "Resources"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ResourceRequirements"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TaskSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskSpec defines the desired state of Task.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources is a list input and output resource to run the task Resources are represented in TaskRuns as bindings to instances of PipelineResources.", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResources"), + }, + }, + "params": { + SchemaProps: spec.SchemaProps{ + Description: "Params is a list of input parameters required to run the task. Params must be supplied as inputs in TaskRuns unless they declare a default value.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ParamSpec"), + }, + }, + }, + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a user-facing description of the task that may be used to populate a UI.", + Type: []string{"string"}, + Format: "", + }, + }, + "steps": { + SchemaProps: spec.SchemaProps{ + Description: "Steps are the steps of the build; each step is run sequentially with the source mounted into /workspace.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Step"), + }, + }, + }, + }, + }, + "volumes": { + SchemaProps: spec.SchemaProps{ + Description: "Volumes is a collection of volumes that are available to mount into the steps of the build.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.Volume"), + }, + }, + }, + }, + }, + "stepTemplate": { + SchemaProps: spec.SchemaProps{ + Description: "StepTemplate can be used as the basis for all step containers within the Task, so that the steps inherit settings on the base container.", + Ref: ref("k8s.io/api/core/v1.Container"), + }, + }, + "sidecars": { + SchemaProps: spec.SchemaProps{ + Description: "Sidecars are run alongside the Task's step containers. They begin before the steps start and end after the steps complete.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Sidecar"), + }, + }, + }, + }, + }, + "workspaces": { + SchemaProps: spec.SchemaProps{ + Description: "Workspaces are the volumes that this Task requires.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspaceDeclaration"), + }, + }, + }, + }, + }, + "results": { + SchemaProps: spec.SchemaProps{ + Description: "Results are values that this Task can output", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResult"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.ParamSpec", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Sidecar", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Step", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResources", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.TaskResult", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.WorkspaceDeclaration", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.Volume"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_TimeoutFields(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TimeoutFields allows granular specification of pipeline, task, and finally timeouts", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "pipeline": { + SchemaProps: spec.SchemaProps{ + Description: "Pipeline sets the maximum allowed duration for execution of the entire pipeline. The sum of individual timeouts for tasks and finally must not exceed this value.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"), + }, + }, + "tasks": { + SchemaProps: spec.SchemaProps{ + Description: "Tasks sets the maximum allowed duration of this pipeline's tasks", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"), + }, + }, + "finally": { + SchemaProps: spec.SchemaProps{ + Description: "Finally sets the maximum allowed duration of this pipeline's finally", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Duration"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_WhenExpression(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "WhenExpression allows a PipelineTask to declare expressions to be evaluated before the Task is run to determine whether the Task should be executed or skipped", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "input": { + SchemaProps: spec.SchemaProps{ + Description: "Input is the string for guard checking which can be a static input or an output from a parent Task", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "operator": { + SchemaProps: spec.SchemaProps{ + Description: "Operator that represents an Input's relationship to the values", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "values": { + SchemaProps: spec.SchemaProps{ + Description: "Values is an array of strings, which is compared against the input, for guard checking It must be non-empty", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"input", "operator", "values"}, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_WorkspaceBinding(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "WorkspaceBinding maps a Task's declared workspace to a Volume.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the workspace populated by the volume.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "subPath": { + SchemaProps: spec.SchemaProps{ + Description: "SubPath is optionally a directory on the volume which should be used for this binding (i.e. the volume will be mounted at this sub directory).", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeClaimTemplate": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeClaimTemplate is a template for a claim that will be created in the same namespace. The PipelineRun controller is responsible for creating a unique claim for each instance of PipelineRun.", + Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaim"), + }, + }, + "persistentVolumeClaim": { + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. Either this OR EmptyDir can be used.", + Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource"), + }, + }, + "emptyDir": { + SchemaProps: spec.SchemaProps{ + Description: "EmptyDir represents a temporary directory that shares a Task's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir Either this OR PersistentVolumeClaim can be used.", + Ref: ref("k8s.io/api/core/v1.EmptyDirVolumeSource"), + }, + }, + "configMap": { + SchemaProps: spec.SchemaProps{ + Description: "ConfigMap represents a configMap that should populate this workspace.", + Ref: ref("k8s.io/api/core/v1.ConfigMapVolumeSource"), + }, + }, + "secret": { + SchemaProps: spec.SchemaProps{ + Description: "Secret represents a secret that should populate this workspace.", + Ref: ref("k8s.io/api/core/v1.SecretVolumeSource"), + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ConfigMapVolumeSource", "k8s.io/api/core/v1.EmptyDirVolumeSource", "k8s.io/api/core/v1.PersistentVolumeClaim", "k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource", "k8s.io/api/core/v1.SecretVolumeSource"}, + } +} + +func schema_pkg_apis_pipeline_v1beta1_WorkspaceDeclaration(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "WorkspaceDeclaration is a declaration of a volume that a Task requires.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name by which you can bind the volume at runtime.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is an optional human readable description of this volume.", + Type: []string{"string"}, + Format: "", + }, + }, + "mountPath": { + SchemaProps: spec.SchemaProps{ + Description: "MountPath overrides the directory that the volume will be made available at.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly dictates whether a mounted volume is writable. By default this field is false and so mounted volumes are writable.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Optional marks a Workspace as not being required in TaskRuns. By default this field is false and so declared workspaces are required.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_WorkspacePipelineTaskBinding(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "WorkspacePipelineTaskBinding describes how a workspace passed into the pipeline should be mapped to a task's declared workspace.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the workspace as declared by the task", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "workspace": { + SchemaProps: spec.SchemaProps{ + Description: "Workspace is the name of the workspace declared by the pipeline", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "subPath": { + SchemaProps: spec.SchemaProps{ + Description: "SubPath is optionally a directory on the volume which should be used for this binding (i.e. the volume will be mounted at this sub directory).", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "workspace"}, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1beta1_WorkspaceUsage(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "WorkspaceUsage is used by a Step or Sidecar to declare that it wants isolated access to a Workspace defined in a Task.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the workspace this Step or Sidecar wants access to.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "mountPath": { + SchemaProps: spec.SchemaProps{ + Description: "MountPath is the path that the workspace should be mounted to inside the Step or Sidecar, overriding any MountPath specified in the Task's WorkspaceDeclaration.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "mountPath"}, + }, + }, + } +} + +func schema_pkg_apis_resource_v1alpha1_PipelineResource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineResource describes a resource that is an input to or output from a Task.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec holds the desired state of the PipelineResource from the client", + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.PipelineResourceSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status is deprecated. It usually is used to communicate the observed state of the PipelineResource from the controller, but was unused as there is no controller for PipelineResource.", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.PipelineResourceStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.PipelineResourceSpec", "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.PipelineResourceStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_resource_v1alpha1_PipelineResourceList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineResourceList contains a list of PipelineResources", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.PipelineResource"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.PipelineResource", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_resource_v1alpha1_PipelineResourceSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineResourceSpec defines an individual resources used in the pipeline.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a user-facing description of the resource that may be used to populate a UI.", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "params": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.ResourceParam"), + }, + }, + }, + }, + }, + "secrets": { + SchemaProps: spec.SchemaProps{ + Description: "Secrets to fetch to populate some of resource fields", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.SecretParam"), + }, + }, + }, + }, + }, + }, + Required: []string{"type", "params"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.ResourceParam", "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1.SecretParam"}, + } +} + +func schema_pkg_apis_resource_v1alpha1_PipelineResourceStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineResourceStatus does not contain anything because PipelineResources on their own do not have a status Deprecated", + Type: []string{"object"}, + }, + }, + } +} + +func schema_pkg_apis_resource_v1alpha1_ResourceDeclaration(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResourceDeclaration defines an input or output PipelineResource declared as a requirement by another type such as a Task or Condition. The Name field will be used to refer to these PipelineResources within the type's definition, and when provided as an Input, the Name will be the path to the volume mounted containing this PipelineResource as an input (e.g. an input Resource named `workspace` will be mounted at `/workspace`).", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name declares the name by which a resource is referenced in the definition. Resources may be referenced by name in the definition of a Task's steps.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type is the type of this resource;", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a user-facing description of the declared resource that may be used to populate a UI.", + Type: []string{"string"}, + Format: "", + }, + }, + "targetPath": { + SchemaProps: spec.SchemaProps{ + Description: "TargetPath is the path in workspace directory where the resource will be copied.", + Type: []string{"string"}, + Format: "", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Optional declares the resource as optional. By default optional is set to false which makes a resource required. optional: true - the resource is considered optional optional: false - the resource is considered required (equivalent of not specifying it)", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"name", "type"}, + }, + }, + } +} + +func schema_pkg_apis_resource_v1alpha1_ResourceParam(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResourceParam declares a string value to use for the parameter called Name, and is used in the specific context of PipelineResources.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "value"}, + }, + }, + } +} + +func schema_pkg_apis_resource_v1alpha1_SecretParam(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SecretParam indicates which secret can be used to populate a field of the resource", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "fieldName": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "secretKey": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "secretName": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"fieldName", "secretKey", "secretName"}, + }, + }, + } +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/param_context.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/param_context.go new file mode 100644 index 0000000000..a2d3882f31 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/param_context.go @@ -0,0 +1,168 @@ +// Copyright 2021 The Tekton Authors +// +// 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. + +package v1beta1 + +import ( + "context" + "fmt" +) + +// paramCtxKey is the unique type for referencing param information from +// a context.Context. See [context.Context.Value](https://pkg.go.dev/context#Context) +// for more details. +// +// +k8s:openapi-gen=false +type paramCtxKeyType struct{} + +var ( + paramCtxKey = paramCtxKeyType{} +) + +// paramCtxVal is the data type stored in the param context. +// This maps param names -> ParamSpec. +type paramCtxVal map[string]ParamSpec + +// addContextParams adds the given Params to the param context. This only +// preserves the fields included in ParamSpec - Name and Type. +func addContextParams(ctx context.Context, in []Param) context.Context { + if in == nil { + return ctx + } + + out := paramCtxVal{} + // Copy map to ensure that contexts are unique. + v := ctx.Value(paramCtxKey) + if v != nil { + for n, cps := range v.(paramCtxVal) { + out[n] = cps + } + } + for _, p := range in { + // The user may have omitted type data. Fill this in in to normalize data. + if v := p.Value; v.Type == "" { + if len(v.ArrayVal) > 0 { + p.Value.Type = ParamTypeArray + } + if v.StringVal != "" { + p.Value.Type = ParamTypeString + } + } + out[p.Name] = ParamSpec{ + Name: p.Name, + Type: p.Value.Type, + } + } + return context.WithValue(ctx, paramCtxKey, out) +} + +// addContextParamSpec adds the given ParamSpecs to the param context. +func addContextParamSpec(ctx context.Context, in []ParamSpec) context.Context { + if in == nil { + return ctx + } + + out := paramCtxVal{} + // Copy map to ensure that contexts are unique. + v := ctx.Value(paramCtxKey) + if v != nil { + for n, ps := range v.(paramCtxVal) { + out[n] = ps + } + } + for _, p := range in { + cps := ParamSpec{ + Name: p.Name, + Type: p.Type, + Description: p.Description, + Default: p.Default, + } + out[p.Name] = cps + } + return context.WithValue(ctx, paramCtxKey, out) +} + +// getContextParams returns the current context parameters overlayed with a +// given set of params. Overrides should generally be the current layer you +// are trying to evaluate. Any context params not in the overrides will default +// to a generic pass-through param of the given type (i.e. $(params.name) or +// $(params.name[*])). +func getContextParams(ctx context.Context, overlays ...Param) []Param { + pv := paramCtxVal{} + v := ctx.Value(paramCtxKey) + if v == nil && len(overlays) == 0 { + return nil + } + if v != nil { + pv = v.(paramCtxVal) + } + out := make([]Param, 0, len(pv)) + + // Overlays take precedence over any context params. Keep track of + // these and automatically add them to the output. + overrideSet := make(map[string]Param, len(overlays)) + for _, p := range overlays { + overrideSet[p.Name] = p + out = append(out, p) + } + + // Include the rest of the context params. + for _, ps := range pv { + // Don't do anything for any overlay params - these are already + // included. + if _, ok := overrideSet[ps.Name]; ok { + continue + } + + // If there is no overlay, pass through the param to the next level. + // e.g. for strings $(params.name), for arrays $(params.name[*]). + p := Param{ + Name: ps.Name, + } + if ps.Type == ParamTypeString { + p.Value = ArrayOrString{ + Type: ParamTypeString, + StringVal: fmt.Sprintf("$(params.%s)", ps.Name), + } + } else { + p.Value = ArrayOrString{ + Type: ParamTypeArray, + ArrayVal: []string{fmt.Sprintf("$(params.%s[*])", ps.Name)}, + } + } + out = append(out, p) + } + + return out +} + +// getContextParamSpecs returns the current context ParamSpecs. +func getContextParamSpecs(ctx context.Context) []ParamSpec { + v := ctx.Value(paramCtxKey) + if v == nil { + return nil + } + + pv := v.(paramCtxVal) + out := make([]ParamSpec, 0, len(pv)) + for _, ps := range pv { + out = append(out, ParamSpec{ + Name: ps.Name, + Type: ps.Type, + Description: ps.Description, + Default: ps.Default, + }) + } + return out +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/param_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/param_types.go new file mode 100644 index 0000000000..259293bf37 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/param_types.go @@ -0,0 +1,189 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "encoding/json" + "fmt" + "strings" + + resource "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" + "github.com/tektoncd/pipeline/pkg/substitution" + "k8s.io/apimachinery/pkg/util/sets" + "knative.dev/pkg/apis" +) + +// ParamsPrefix is the prefix used in $(...) expressions referring to parameters +const ParamsPrefix = "params" + +// ParamSpec defines arbitrary parameters needed beyond typed inputs (such as +// resources). Parameter values are provided by users as inputs on a TaskRun +// or PipelineRun. +type ParamSpec struct { + // Name declares the name by which a parameter is referenced. + Name string `json:"name"` + // Type is the user-specified type of the parameter. The possible types + // are currently "string" and "array", and "string" is the default. + // +optional + Type ParamType `json:"type,omitempty"` + // Description is a user-facing description of the parameter that may be + // used to populate a UI. + // +optional + Description string `json:"description,omitempty"` + // Default is the value a parameter takes if no input value is supplied. If + // default is set, a Task may be executed without a supplied value for the + // parameter. + // +optional + Default *ArrayOrString `json:"default,omitempty"` +} + +// SetDefaults set the default type +func (pp *ParamSpec) SetDefaults(ctx context.Context) { + if pp != nil && pp.Type == "" { + if pp.Default != nil { + // propagate the parsed ArrayOrString's type to the parent ParamSpec's type + if pp.Default.Type != "" { + // propagate the default type if specified + pp.Type = pp.Default.Type + } else { + // determine the type based on the array or string values when default value is specified but not the type + if pp.Default.ArrayVal != nil { + pp.Type = ParamTypeArray + } else { + pp.Type = ParamTypeString + } + } + } else { + // ParamTypeString is the default value (when no type can be inferred from the default value) + pp.Type = ParamTypeString + } + } +} + +// ResourceParam declares a string value to use for the parameter called Name, and is used in +// the specific context of PipelineResources. +type ResourceParam = resource.ResourceParam + +// Param declares an ArrayOrString to use for the parameter called name. +type Param struct { + Name string `json:"name"` + Value ArrayOrString `json:"value"` +} + +// ParamType indicates the type of an input parameter; +// Used to distinguish between a single string and an array of strings. +type ParamType string + +// Valid ParamTypes: +const ( + ParamTypeString ParamType = "string" + ParamTypeArray ParamType = "array" +) + +// AllParamTypes can be used for ParamType validation. +var AllParamTypes = []ParamType{ParamTypeString, ParamTypeArray} + +// ArrayOrString is modeled after IntOrString in kubernetes/apimachinery: + +// ArrayOrString is a type that can hold a single string or string array. +// Used in JSON unmarshalling so that a single JSON field can accept +// either an individual string or an array of strings. +type ArrayOrString struct { + Type ParamType `json:"type"` // Represents the stored type of ArrayOrString. + StringVal string `json:"stringVal"` + ArrayVal []string `json:"arrayVal"` +} + +// UnmarshalJSON implements the json.Unmarshaller interface. +func (arrayOrString *ArrayOrString) UnmarshalJSON(value []byte) error { + if value[0] == '"' { + arrayOrString.Type = ParamTypeString + return json.Unmarshal(value, &arrayOrString.StringVal) + } + arrayOrString.Type = ParamTypeArray + return json.Unmarshal(value, &arrayOrString.ArrayVal) +} + +// MarshalJSON implements the json.Marshaller interface. +func (arrayOrString ArrayOrString) MarshalJSON() ([]byte, error) { + switch arrayOrString.Type { + case ParamTypeString: + return json.Marshal(arrayOrString.StringVal) + case ParamTypeArray: + return json.Marshal(arrayOrString.ArrayVal) + default: + return []byte{}, fmt.Errorf("impossible ArrayOrString.Type: %q", arrayOrString.Type) + } +} + +// ApplyReplacements applyes replacements for ArrayOrString type +func (arrayOrString *ArrayOrString) ApplyReplacements(stringReplacements map[string]string, arrayReplacements map[string][]string) { + if arrayOrString.Type == ParamTypeString { + arrayOrString.StringVal = substitution.ApplyReplacements(arrayOrString.StringVal, stringReplacements) + } else { + var newArrayVal []string + for _, v := range arrayOrString.ArrayVal { + newArrayVal = append(newArrayVal, substitution.ApplyArrayReplacements(v, stringReplacements, arrayReplacements)...) + } + arrayOrString.ArrayVal = newArrayVal + } +} + +// NewArrayOrString creates an ArrayOrString of type ParamTypeString or ParamTypeArray, based on +// how many inputs are given (>1 input will create an array, not string). +func NewArrayOrString(value string, values ...string) *ArrayOrString { + if len(values) > 0 { + return &ArrayOrString{ + Type: ParamTypeArray, + ArrayVal: append([]string{value}, values...), + } + } + return &ArrayOrString{ + Type: ParamTypeString, + StringVal: value, + } +} + +// ArrayReference returns the name of the parameter from array parameter reference +// returns arrayParam from $(params.arrayParam[*]) +func ArrayReference(a string) string { + return strings.TrimSuffix(strings.TrimPrefix(a, "$("+ParamsPrefix+"."), "[*])") +} + +func validatePipelineParametersVariablesInTaskParameters(params []Param, prefix string, paramNames sets.String, arrayParamNames sets.String) (errs *apis.FieldError) { + for _, param := range params { + if param.Value.Type == ParamTypeString { + errs = errs.Also(validateStringVariable(param.Value.StringVal, prefix, paramNames, arrayParamNames).ViaFieldKey("params", param.Name)) + } else { + for idx, arrayElement := range param.Value.ArrayVal { + errs = errs.Also(validateArrayVariable(arrayElement, prefix, paramNames, arrayParamNames).ViaFieldIndex("value", idx).ViaFieldKey("params", param.Name)) + } + } + } + return errs +} + +func validateStringVariable(value, prefix string, stringVars sets.String, arrayVars sets.String) *apis.FieldError { + errs := substitution.ValidateVariableP(value, prefix, stringVars) + return errs.Also(substitution.ValidateVariableProhibitedP(value, prefix, arrayVars)) +} + +func validateArrayVariable(value, prefix string, stringVars sets.String, arrayVars sets.String) *apis.FieldError { + errs := substitution.ValidateVariableP(value, prefix, stringVars) + return errs.Also(substitution.ValidateVariableIsolatedP(value, prefix, arrayVars)) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_conversion.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_conversion.go new file mode 100644 index 0000000000..73da674ac9 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_conversion.go @@ -0,0 +1,36 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + + "knative.dev/pkg/apis" +) + +var _ apis.Convertible = (*Pipeline)(nil) + +// ConvertTo implements api.Convertible +func (p *Pipeline) ConvertTo(ctx context.Context, sink apis.Convertible) error { + return fmt.Errorf("v1beta1 is the highest known version, got: %T", sink) +} + +// ConvertFrom implements api.Convertible +func (p *Pipeline) ConvertFrom(ctx context.Context, source apis.Convertible) error { + return fmt.Errorf("v1beta1 is the highest know version, got: %T", source) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_defaults.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_defaults.go new file mode 100644 index 0000000000..af9cf387c9 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_defaults.go @@ -0,0 +1,89 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + + "knative.dev/pkg/apis" +) + +var _ apis.Defaultable = (*Pipeline)(nil) + +// SetDefaults sets default values on the Pipeline's Spec +func (p *Pipeline) SetDefaults(ctx context.Context) { + p.Spec.SetDefaults(ctx) +} + +// SetDefaults sets default values for the PipelineSpec's Params, Tasks, and Finally +func (ps *PipelineSpec) SetDefaults(ctx context.Context) { + for i := range ps.Params { + ps.Params[i].SetDefaults(ctx) + } + + for _, pt := range ps.Tasks { + if pt.TaskRef != nil { + if pt.TaskRef.Kind == "" { + pt.TaskRef.Kind = NamespacedTaskKind + } + } + if pt.TaskSpec != nil { + pt.TaskSpec.SetDefaults(ctx) + } + } + + for _, ft := range ps.Finally { + ctx := ctx // Ensure local scoping per Task + if ft.TaskRef != nil { + if ft.TaskRef.Kind == "" { + ft.TaskRef.Kind = NamespacedTaskKind + } + } + if ft.TaskSpec != nil { + ft.TaskSpec.SetDefaults(ctx) + } + } +} + +// applyImplicitParams propagates implicit params from the parent context +// through the Pipeline and underlying specs. +func (ps *PipelineSpec) applyImplicitParams(ctx context.Context) { + ctx = addContextParamSpec(ctx, ps.Params) + ps.Params = getContextParamSpecs(ctx) + + for i, pt := range ps.Tasks { + ctx := ctx // Ensure local scoping per Task + + // Only propagate param context to the spec - ref params should + // still be explicitly set. + if pt.TaskSpec != nil { + ctx = addContextParams(ctx, pt.Params) + ps.Tasks[i].Params = getContextParams(ctx, pt.Params...) + pt.TaskSpec.applyImplicitParams(ctx) + } + } + + for i, ft := range ps.Finally { + ctx := ctx // Ensure local scoping per Task + + if ft.TaskSpec != nil { + ctx = addContextParams(ctx, ft.Params) + ps.Finally[i].Params = getContextParams(ctx, ft.Params...) + ft.TaskSpec.applyImplicitParams(ctx) + } + } +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_interface.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_interface.go new file mode 100644 index 0000000000..fb21e16daf --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_interface.go @@ -0,0 +1,30 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +// PipelineObject is implemented by Pipeline and ClusterPipeline +type PipelineObject interface { + apis.Defaultable + PipelineMetadata() metav1.ObjectMeta + PipelineSpec() PipelineSpec + Copy() PipelineObject +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_types.go new file mode 100644 index 0000000000..87bbd3dac6 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_types.go @@ -0,0 +1,611 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + "strings" + + "github.com/google/go-containerregistry/pkg/name" + "github.com/tektoncd/pipeline/pkg/apis/config" + "github.com/tektoncd/pipeline/pkg/apis/pipeline" + + "github.com/tektoncd/pipeline/pkg/reconciler/pipeline/dag" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/validation" + "knative.dev/pkg/apis" + "knative.dev/pkg/kmeta" +) + +const ( + // PipelineTasksAggregateStatus is a param representing aggregate status of all dag pipelineTasks + PipelineTasksAggregateStatus = "tasks.status" + // PipelineTasks is a value representing a task is a member of "tasks" section of the pipeline + PipelineTasks = "tasks" + // PipelineFinallyTasks is a value representing a task is a member of "finally" section of the pipeline + PipelineFinallyTasks = "finally" +) + +// +genclient +// +genclient:noStatus +// +genreconciler:krshapedlogic=false +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Pipeline describes a list of Tasks to execute. It expresses how outputs +// of tasks feed into inputs of subsequent tasks. +// +k8s:openapi-gen=true +type Pipeline struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec holds the desired state of the Pipeline from the client + // +optional + Spec PipelineSpec `json:"spec"` +} + +var _ kmeta.OwnerRefable = (*Pipeline)(nil) + +// PipelineMetadata returns the Pipeline's ObjectMeta, implementing PipelineObject +func (p *Pipeline) PipelineMetadata() metav1.ObjectMeta { + return p.ObjectMeta +} + +// PipelineSpec returns the Pipeline's Spec, implementing PipelineObject +func (p *Pipeline) PipelineSpec() PipelineSpec { + return p.Spec +} + +// Copy returns a deep copy of the Pipeline, implementing PipelineObject +func (p *Pipeline) Copy() PipelineObject { + return p.DeepCopy() +} + +// GetGroupVersionKind implements kmeta.OwnerRefable. +func (*Pipeline) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind(pipeline.PipelineControllerName) +} + +// PipelineSpec defines the desired state of Pipeline. +type PipelineSpec struct { + // Description is a user-facing description of the pipeline that may be + // used to populate a UI. + // +optional + Description string `json:"description,omitempty"` + // Resources declares the names and types of the resources given to the + // Pipeline's tasks as inputs and outputs. + Resources []PipelineDeclaredResource `json:"resources,omitempty"` + // Tasks declares the graph of Tasks that execute when this Pipeline is run. + Tasks []PipelineTask `json:"tasks,omitempty"` + // Params declares a list of input parameters that must be supplied when + // this Pipeline is run. + Params []ParamSpec `json:"params,omitempty"` + // Workspaces declares a set of named workspaces that are expected to be + // provided by a PipelineRun. + // +optional + Workspaces []PipelineWorkspaceDeclaration `json:"workspaces,omitempty"` + // Results are values that this pipeline can output once run + // +optional + Results []PipelineResult `json:"results,omitempty"` + // Finally declares the list of Tasks that execute just before leaving the Pipeline + // i.e. either after all Tasks are finished executing successfully + // or after a failure which would result in ending the Pipeline + Finally []PipelineTask `json:"finally,omitempty"` +} + +// PipelineResult used to describe the results of a pipeline +type PipelineResult struct { + // Name the given name + Name string `json:"name"` + + // Description is a human-readable description of the result + // +optional + Description string `json:"description"` + + // Value the expression used to retrieve the value + Value string `json:"value"` +} + +// PipelineTaskMetadata contains the labels or annotations for an EmbeddedTask +type PipelineTaskMetadata struct { + // +optional + Labels map[string]string `json:"labels,omitempty"` + + // +optional + Annotations map[string]string `json:"annotations,omitempty"` +} + +// EmbeddedTask is used to define a Task inline within a Pipeline's PipelineTasks. +type EmbeddedTask struct { + // +optional + runtime.TypeMeta `json:",inline,omitempty"` + + // Spec is a specification of a custom task + // +optional + Spec runtime.RawExtension `json:"spec,omitempty"` + + // +optional + Metadata PipelineTaskMetadata `json:"metadata,omitempty"` + + // TaskSpec is a specification of a task + // +optional + TaskSpec `json:",inline,omitempty"` +} + +// PipelineTask defines a task in a Pipeline, passing inputs from both +// Params and from the output of previous tasks. +type PipelineTask struct { + // Name is the name of this task within the context of a Pipeline. Name is + // used as a coordinate with the `from` and `runAfter` fields to establish + // the execution order of tasks relative to one another. + Name string `json:"name,omitempty"` + + // TaskRef is a reference to a task definition. + // +optional + TaskRef *TaskRef `json:"taskRef,omitempty"` + + // TaskSpec is a specification of a task + // +optional + TaskSpec *EmbeddedTask `json:"taskSpec,omitempty"` + + // Conditions is a list of conditions that need to be true for the task to run + // Conditions are deprecated, use WhenExpressions instead + // +optional + Conditions []PipelineTaskCondition `json:"conditions,omitempty"` + + // WhenExpressions is a list of when expressions that need to be true for the task to run + // +optional + WhenExpressions WhenExpressions `json:"when,omitempty"` + + // Retries represents how many times this task should be retried in case of task failure: ConditionSucceeded set to False + // +optional + Retries int `json:"retries,omitempty"` + + // RunAfter is the list of PipelineTask names that should be executed before + // this Task executes. (Used to force a specific ordering in graph execution.) + // +optional + RunAfter []string `json:"runAfter,omitempty"` + + // Resources declares the resources given to this task as inputs and + // outputs. + // +optional + Resources *PipelineTaskResources `json:"resources,omitempty"` + + // Parameters declares parameters passed to this task. + // +optional + Params []Param `json:"params,omitempty"` + + // Matrix declares parameters used to fan out this task. + // +optional + Matrix []Param `json:"matrix,omitempty"` + + // Workspaces maps workspaces from the pipeline spec to the workspaces + // declared in the Task. + // +optional + Workspaces []WorkspacePipelineTaskBinding `json:"workspaces,omitempty"` + + // Time after which the TaskRun times out. Defaults to 1 hour. + // Specified TaskRun timeout should be less than 24h. + // Refer Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` +} + +// validateRefOrSpec validates at least one of taskRef or taskSpec is specified +func (pt PipelineTask) validateRefOrSpec() (errs *apis.FieldError) { + // can't have both taskRef and taskSpec at the same time + if pt.TaskRef != nil && pt.TaskSpec != nil { + errs = errs.Also(apis.ErrMultipleOneOf("taskRef", "taskSpec")) + } + // Check that one of TaskRef and TaskSpec is present + if pt.TaskRef == nil && pt.TaskSpec == nil { + errs = errs.Also(apis.ErrMissingOneOf("taskRef", "taskSpec")) + } + return errs +} + +// validateCustomTask validates custom task specifications - checking kind and fail if not yet supported features specified +func (pt PipelineTask) validateCustomTask() (errs *apis.FieldError) { + if pt.TaskRef != nil && pt.TaskRef.Kind == "" { + errs = errs.Also(apis.ErrInvalidValue("custom task ref must specify kind", "taskRef.kind")) + } + if pt.TaskSpec != nil && pt.TaskSpec.Kind == "" { + errs = errs.Also(apis.ErrInvalidValue("custom task spec must specify kind", "taskSpec.kind")) + } + if pt.TaskRef != nil && pt.TaskRef.APIVersion == "" { + errs = errs.Also(apis.ErrInvalidValue("custom task ref must specify apiVersion", "taskRef.apiVersion")) + } + if pt.TaskSpec != nil && pt.TaskSpec.APIVersion == "" { + errs = errs.Also(apis.ErrInvalidValue("custom task spec must specify apiVersion", "taskSpec.apiVersion")) + } + + // Conditions are deprecated so the effort to support them with custom tasks is not justified. + // When expressions should be used instead. + if len(pt.Conditions) > 0 { + errs = errs.Also(apis.ErrInvalidValue("custom tasks do not support conditions - use when expressions instead", "conditions")) + } + // TODO(#3133): Support these features if possible. + if pt.Resources != nil { + errs = errs.Also(apis.ErrInvalidValue("custom tasks do not support PipelineResources", "resources")) + } + return errs +} + +// validateBundle validates bundle specifications - checking name and bundle +func (pt PipelineTask) validateBundle() (errs *apis.FieldError) { + // bundle requires a TaskRef to be specified + if (pt.TaskRef != nil && pt.TaskRef.Bundle != "") && pt.TaskRef.Name == "" { + errs = errs.Also(apis.ErrMissingField("taskRef.name")) + } + // If a bundle url is specified, ensure it is parsable + if pt.TaskRef != nil && pt.TaskRef.Bundle != "" { + if _, err := name.ParseReference(pt.TaskRef.Bundle); err != nil { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("invalid bundle reference (%s)", err.Error()), "taskRef.bundle")) + } + } + return errs +} + +// validateTask validates a pipeline task or a final task for taskRef and taskSpec +func (pt PipelineTask) validateTask(ctx context.Context) (errs *apis.FieldError) { + // Validate TaskSpec if it's present + if pt.TaskSpec != nil { + errs = errs.Also(pt.TaskSpec.Validate(ctx).ViaField("taskSpec")) + } + if pt.TaskRef != nil { + if pt.TaskRef.Name != "" { + // TaskRef name must be a valid k8s name + if errSlice := validation.IsQualifiedName(pt.TaskRef.Name); len(errSlice) != 0 { + errs = errs.Also(apis.ErrInvalidValue(strings.Join(errSlice, ","), "name")) + } + } else { + errs = errs.Also(apis.ErrInvalidValue("taskRef must specify name", "taskRef.name")) + } + // fail if bundle is present when EnableTektonOCIBundles feature flag is off (as it won't be allowed nor used) + if pt.TaskRef.Bundle != "" { + errs = errs.Also(apis.ErrDisallowedFields("taskref.bundle")) + } + } + return errs +} + +func (pt *PipelineTask) validateMatrix(ctx context.Context) (errs *apis.FieldError) { + if len(pt.Matrix) != 0 { + // This is an alpha feature and will fail validation if it's used in a pipeline spec + // when the enable-api-fields feature gate is anything but "alpha". + errs = errs.Also(ValidateEnabledAPIFields(ctx, "matrix", config.AlphaAPIFields)) + } + return errs +} + +func (pt *PipelineTask) validateExecutionStatusVariablesDisallowed() (errs *apis.FieldError) { + for _, param := range pt.Params { + if expressions, ok := GetVarSubstitutionExpressionsForParam(param); ok { + errs = errs.Also(validateContainsExecutionStatusVariablesDisallowed(expressions, "value"). + ViaFieldKey("params", param.Name)) + } + } + for i, we := range pt.WhenExpressions { + if expressions, ok := we.GetVarSubstitutionExpressions(); ok { + errs = errs.Also(validateContainsExecutionStatusVariablesDisallowed(expressions, ""). + ViaFieldIndex("when", i)) + } + } + return errs +} + +func (pt *PipelineTask) validateExecutionStatusVariablesAllowed(ptNames sets.String) (errs *apis.FieldError) { + for _, param := range pt.Params { + if expressions, ok := GetVarSubstitutionExpressionsForParam(param); ok { + errs = errs.Also(validateExecutionStatusVariablesExpressions(expressions, ptNames, "value"). + ViaFieldKey("params", param.Name)) + } + } + for i, we := range pt.WhenExpressions { + if expressions, ok := we.GetVarSubstitutionExpressions(); ok { + errs = errs.Also(validateExecutionStatusVariablesExpressions(expressions, ptNames, ""). + ViaFieldIndex("when", i)) + } + } + return errs +} + +func validateContainsExecutionStatusVariablesDisallowed(expressions []string, path string) (errs *apis.FieldError) { + if containsExecutionStatusReferences(expressions) { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("pipeline tasks can not refer to execution status"+ + " of any other pipeline task or aggregate status of tasks"), path)) + } + return errs +} + +func containsExecutionStatusReferences(expressions []string) bool { + // validate tasks.pipelineTask.status/tasks.status if this expression is not a result reference + if !LooksLikeContainsResultRefs(expressions) { + for _, e := range expressions { + // check if it contains context variable accessing execution status - $(tasks.taskname.status) + // or an aggregate status - $(tasks.status) + if containsExecutionStatusRef(e) { + return true + } + } + } + return false +} + +func validateExecutionStatusVariablesExpressions(expressions []string, ptNames sets.String, fieldPath string) (errs *apis.FieldError) { + // validate tasks.pipelineTask.status if this expression is not a result reference + if !LooksLikeContainsResultRefs(expressions) { + for _, expression := range expressions { + // its a reference to aggregate status of dag tasks - $(tasks.status) + if expression == PipelineTasksAggregateStatus { + continue + } + // check if it contains context variable accessing execution status - $(tasks.taskname.status) + if containsExecutionStatusRef(expression) { + // strip tasks. and .status from tasks.taskname.status to further verify task name + pt := strings.TrimSuffix(strings.TrimPrefix(expression, "tasks."), ".status") + // report an error if the task name does not exist in the list of dag tasks + if !ptNames.Has(pt) { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("pipeline task %s is not defined in the pipeline", pt), fieldPath)) + } + } + } + } + return errs +} + +func (pt *PipelineTask) validateWorkspaces(workspaceNames sets.String) (errs *apis.FieldError) { + for i, ws := range pt.Workspaces { + if !workspaceNames.Has(ws.Workspace) { + errs = errs.Also(apis.ErrInvalidValue( + fmt.Sprintf("pipeline task %q expects workspace with name %q but none exists in pipeline spec", pt.Name, ws.Workspace), + "", + ).ViaFieldIndex("workspaces", i)) + } + } + return errs +} + +// TaskSpecMetadata returns the metadata of the PipelineTask's EmbeddedTask spec. +func (pt *PipelineTask) TaskSpecMetadata() PipelineTaskMetadata { + return pt.TaskSpec.Metadata +} + +// HashKey is the name of the PipelineTask, and is used as the key for this PipelineTask in the DAG +func (pt PipelineTask) HashKey() string { + return pt.Name +} + +// ValidateName checks whether the PipelineTask's name is a valid DNS label +func (pt PipelineTask) ValidateName() *apis.FieldError { + if err := validation.IsDNS1123Label(pt.Name); len(err) > 0 { + return &apis.FieldError{ + Message: fmt.Sprintf("invalid value %q", pt.Name), + Paths: []string{"name"}, + Details: "Pipeline Task name must be a valid DNS Label." + + "For more info refer to https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + } + } + return nil +} + +// Validate classifies whether a task is a custom task, bundle, or a regular task(dag/final) +// calls the validation routine based on the type of the task +func (pt PipelineTask) Validate(ctx context.Context) (errs *apis.FieldError) { + errs = errs.Also(pt.validateRefOrSpec()) + cfg := config.FromContextOrDefaults(ctx) + // If EnableCustomTasks feature flag is on, validate custom task specifications + // pipeline task having taskRef with APIVersion is classified as custom task + switch { + case cfg.FeatureFlags.EnableCustomTasks && pt.TaskRef != nil && pt.TaskRef.APIVersion != "": + errs = errs.Also(pt.validateCustomTask()) + case cfg.FeatureFlags.EnableCustomTasks && pt.TaskSpec != nil && pt.TaskSpec.APIVersion != "": + errs = errs.Also(pt.validateCustomTask()) + // If EnableTektonOCIBundles feature flag is on, validate bundle specifications + case cfg.FeatureFlags.EnableTektonOCIBundles && pt.TaskRef != nil && pt.TaskRef.Bundle != "": + errs = errs.Also(pt.validateBundle()) + default: + errs = errs.Also(pt.validateTask(ctx)) + } + return +} + +// Deps returns all other PipelineTask dependencies of this PipelineTask, based on resource usage or ordering +func (pt PipelineTask) Deps() []string { + deps := []string{} + + deps = append(deps, pt.resourceDeps()...) + deps = append(deps, pt.orderingDeps()...) + + uniqueDeps := sets.NewString() + for _, w := range deps { + if uniqueDeps.Has(w) { + continue + } + uniqueDeps.Insert(w) + } + + return uniqueDeps.List() +} + +func (pt PipelineTask) resourceDeps() []string { + resourceDeps := []string{} + if pt.Resources != nil { + for _, rd := range pt.Resources.Inputs { + resourceDeps = append(resourceDeps, rd.From...) + } + } + + // Add any dependents from conditional resources. + for _, cond := range pt.Conditions { + for _, rd := range cond.Resources { + resourceDeps = append(resourceDeps, rd.From...) + } + } + + // Add any dependents from result references. + for _, ref := range PipelineTaskResultRefs(&pt) { + resourceDeps = append(resourceDeps, ref.PipelineTask) + } + + return resourceDeps +} + +func (pt PipelineTask) orderingDeps() []string { + orderingDeps := []string{} + for _, runAfter := range pt.RunAfter { + orderingDeps = append(orderingDeps, runAfter) + } + return orderingDeps +} + +// PipelineTaskList is a list of PipelineTasks +type PipelineTaskList []PipelineTask + +// Deps returns a map with key as name of a pipelineTask and value as a list of its dependencies +func (l PipelineTaskList) Deps() map[string][]string { + deps := map[string][]string{} + for _, pt := range l { + // get the list of deps for this pipelineTask + d := pt.Deps() + // add the pipelineTask into the map if it has any deps + if len(d) > 0 { + deps[pt.HashKey()] = d + } + } + return deps +} + +// Items returns a slice of all tasks in the PipelineTaskList, converted to dag.Tasks +func (l PipelineTaskList) Items() []dag.Task { + tasks := []dag.Task{} + for _, t := range l { + tasks = append(tasks, dag.Task(t)) + } + return tasks +} + +// Names returns a set of pipeline task names from the given list of pipeline tasks +func (l PipelineTaskList) Names() sets.String { + names := sets.String{} + for _, pt := range l { + names.Insert(pt.Name) + } + return names +} + +// Validate a list of pipeline tasks including custom task and bundles +func (l PipelineTaskList) Validate(ctx context.Context, taskNames sets.String, path string) (errs *apis.FieldError) { + for i, t := range l { + // validate pipeline task name + errs = errs.Also(t.ValidateName().ViaFieldIndex(path, i)) + // names cannot be duplicated - checking that pipelineTask names are unique + if _, ok := taskNames[t.Name]; ok { + errs = errs.Also(apis.ErrMultipleOneOf("name").ViaFieldIndex(path, i)) + } + taskNames.Insert(t.Name) + // validate custom task, bundle, dag, or final task + errs = errs.Also(t.Validate(ctx).ViaFieldIndex(path, i)) + } + return errs +} + +// PipelineTaskParam is used to provide arbitrary string parameters to a Task. +type PipelineTaskParam struct { + Name string `json:"name"` + Value string `json:"value"` +} + +// PipelineTaskCondition allows a PipelineTask to declare a Condition to be evaluated before +// the Task is run. +type PipelineTaskCondition struct { + // ConditionRef is the name of the Condition to use for the conditionCheck + ConditionRef string `json:"conditionRef"` + + // Params declare parameters passed to this Condition + // +optional + Params []Param `json:"params,omitempty"` + + // Resources declare the resources provided to this Condition as input + Resources []PipelineTaskInputResource `json:"resources,omitempty"` +} + +// PipelineDeclaredResource is used by a Pipeline to declare the types of the +// PipelineResources that it will required to run and names which can be used to +// refer to these PipelineResources in PipelineTaskResourceBindings. +type PipelineDeclaredResource struct { + // Name is the name that will be used by the Pipeline to refer to this resource. + // It does not directly correspond to the name of any PipelineResources Task + // inputs or outputs, and it does not correspond to the actual names of the + // PipelineResources that will be bound in the PipelineRun. + Name string `json:"name"` + // Type is the type of the PipelineResource. + Type PipelineResourceType `json:"type"` + // Optional declares the resource as optional. + // optional: true - the resource is considered optional + // optional: false - the resource is considered required (default/equivalent of not specifying it) + Optional bool `json:"optional,omitempty"` +} + +// PipelineTaskResources allows a Pipeline to declare how its DeclaredPipelineResources +// should be provided to a Task as its inputs and outputs. +type PipelineTaskResources struct { + // Inputs holds the mapping from the PipelineResources declared in + // DeclaredPipelineResources to the input PipelineResources required by the Task. + Inputs []PipelineTaskInputResource `json:"inputs,omitempty"` + // Outputs holds the mapping from the PipelineResources declared in + // DeclaredPipelineResources to the input PipelineResources required by the Task. + Outputs []PipelineTaskOutputResource `json:"outputs,omitempty"` +} + +// PipelineTaskInputResource maps the name of a declared PipelineResource input +// dependency in a Task to the resource in the Pipeline's DeclaredPipelineResources +// that should be used. This input may come from a previous task. +type PipelineTaskInputResource struct { + // Name is the name of the PipelineResource as declared by the Task. + Name string `json:"name"` + // Resource is the name of the DeclaredPipelineResource to use. + Resource string `json:"resource"` + // From is the list of PipelineTask names that the resource has to come from. + // (Implies an ordering in the execution graph.) + // +optional + From []string `json:"from,omitempty"` +} + +// PipelineTaskOutputResource maps the name of a declared PipelineResource output +// dependency in a Task to the resource in the Pipeline's DeclaredPipelineResources +// that should be used. +type PipelineTaskOutputResource struct { + // Name is the name of the PipelineResource as declared by the Task. + Name string `json:"name"` + // Resource is the name of the DeclaredPipelineResource to use. + Resource string `json:"resource"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PipelineList contains a list of Pipeline +type PipelineList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []Pipeline `json:"items"` +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_validation.go new file mode 100644 index 0000000000..ba1560e050 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipeline_validation.go @@ -0,0 +1,489 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + "strings" + + "github.com/tektoncd/pipeline/pkg/apis/validate" + "github.com/tektoncd/pipeline/pkg/list" + "github.com/tektoncd/pipeline/pkg/reconciler/pipeline/dag" + "github.com/tektoncd/pipeline/pkg/substitution" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/util/sets" + "knative.dev/pkg/apis" +) + +var _ apis.Validatable = (*Pipeline)(nil) + +// Validate checks that the Pipeline structure is valid but does not validate +// that any references resources exist, that is done at run time. +func (p *Pipeline) Validate(ctx context.Context) *apis.FieldError { + errs := validate.ObjectMetadata(p.GetObjectMeta()).ViaField("metadata") + if apis.IsInDelete(ctx) { + return nil + } + return errs.Also(p.Spec.Validate(apis.WithinSpec(ctx)).ViaField("spec")) +} + +// Validate checks that taskNames in the Pipeline are valid and that the graph +// of Tasks expressed in the Pipeline makes sense. +func (ps *PipelineSpec) Validate(ctx context.Context) (errs *apis.FieldError) { + if equality.Semantic.DeepEqual(ps, &PipelineSpec{}) { + errs = errs.Also(apis.ErrGeneric("expected at least one, got none", "description", "params", "resources", "tasks", "workspaces")) + } + // PipelineTask must have a valid unique label and at least one of taskRef or taskSpec should be specified + errs = errs.Also(ValidatePipelineTasks(ctx, ps.Tasks, ps.Finally)) + // All declared resources should be used, and the Pipeline shouldn't try to use any resources + // that aren't declared + errs = errs.Also(validateDeclaredResources(ps.Resources, ps.Tasks, ps.Finally)) + // The from values should make sense + errs = errs.Also(validateFrom(ps.Tasks)) + // Validate the pipeline task graph + errs = errs.Also(validateGraph(ps.Tasks)) + errs = errs.Also(validateParamResults(ps.Tasks)) + // The parameter variables should be valid + errs = errs.Also(validatePipelineParameterVariables(ps.Tasks, ps.Params).ViaField("tasks")) + errs = errs.Also(validatePipelineParameterVariables(ps.Finally, ps.Params).ViaField("finally")) + errs = errs.Also(validatePipelineContextVariables(ps.Tasks).ViaField("tasks")) + errs = errs.Also(validatePipelineContextVariables(ps.Finally).ViaField("finally")) + errs = errs.Also(validateExecutionStatusVariables(ps.Tasks, ps.Finally)) + // Validate the pipeline's workspaces. + errs = errs.Also(validatePipelineWorkspacesDeclarations(ps.Workspaces)) + errs = errs.Also(validatePipelineWorkspacesUsage(ps.Workspaces, ps.Tasks).ViaField("tasks")) + errs = errs.Also(validatePipelineWorkspacesUsage(ps.Workspaces, ps.Finally).ViaField("finally")) + // Validate the pipeline's results + errs = errs.Also(validatePipelineResults(ps.Results)) + errs = errs.Also(validateTasksAndFinallySection(ps)) + errs = errs.Also(validateFinalTasks(ps.Tasks, ps.Finally)) + errs = errs.Also(validateWhenExpressions(ps.Tasks, ps.Finally)) + errs = errs.Also(validateMatrix(ctx, ps.Tasks).ViaField("tasks")) + errs = errs.Also(validateMatrix(ctx, ps.Finally).ViaField("finally")) + return errs +} + +// ValidatePipelineTasks ensures that pipeline tasks has unique label, pipeline tasks has specified one of +// taskRef or taskSpec, and in case of a pipeline task with taskRef, it has a reference to a valid task (task name) +func ValidatePipelineTasks(ctx context.Context, tasks []PipelineTask, finalTasks []PipelineTask) *apis.FieldError { + taskNames := sets.NewString() + var errs *apis.FieldError + errs = errs.Also(PipelineTaskList(tasks).Validate(ctx, taskNames, "tasks")) + errs = errs.Also(PipelineTaskList(finalTasks).Validate(ctx, taskNames, "finally")) + return errs +} + +// validatePipelineWorkspacesDeclarations validates the specified workspaces, ensuring having unique name without any +// empty string, +func validatePipelineWorkspacesDeclarations(wss []PipelineWorkspaceDeclaration) (errs *apis.FieldError) { + // Workspace names must be non-empty and unique. + wsTable := sets.NewString() + for i, ws := range wss { + if ws.Name == "" { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("workspace %d has empty name", i), + "").ViaFieldIndex("workspaces", i)) + } + if wsTable.Has(ws.Name) { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("workspace with name %q appears more than once", ws.Name), + "").ViaFieldIndex("workspaces", i)) + } + wsTable.Insert(ws.Name) + } + return errs +} + +// validatePipelineWorkspacesUsage validates that all the referenced workspaces (by pipeline tasks) are specified in +// the pipeline +func validatePipelineWorkspacesUsage(wss []PipelineWorkspaceDeclaration, pts []PipelineTask) (errs *apis.FieldError) { + workspaceNames := sets.NewString() + for _, ws := range wss { + workspaceNames.Insert(ws.Name) + } + // Any workspaces used in PipelineTasks should have their name declared in the Pipeline's Workspaces list. + for i, pt := range pts { + errs = errs.Also(pt.validateWorkspaces(workspaceNames).ViaIndex(i)) + } + return errs +} + +// validatePipelineParameterVariables validates parameters with those specified by each pipeline task, +// (1) it validates the type of parameter is either string or array (2) parameter default value matches +// with the type of that param (3) ensures that the referenced param variable is defined is part of the param declarations +func validatePipelineParameterVariables(tasks []PipelineTask, params []ParamSpec) (errs *apis.FieldError) { + parameterNames := sets.NewString() + arrayParameterNames := sets.NewString() + + for _, p := range params { + // Verify that p is a valid type. + validType := false + for _, allowedType := range AllParamTypes { + if p.Type == allowedType { + validType = true + } + } + if !validType { + errs = errs.Also(apis.ErrInvalidValue(string(p.Type), "type").ViaFieldKey("params", p.Name)) + } + + // If a default value is provided, ensure its type matches param's declared type. + if (p.Default != nil) && (p.Default.Type != p.Type) { + errs = errs.Also(apis.ErrGeneric(fmt.Sprintf("\"%v\" type does not match default value's type: \"%v\"", p.Type, p.Default.Type), + "type", "default.type").ViaFieldKey("params", p.Name)) + } + + if parameterNames.Has(p.Name) { + errs = errs.Also(apis.ErrGeneric("parameter appears more than once", "").ViaFieldKey("params", p.Name)) + } + // Add parameter name to parameterNames, and to arrayParameterNames if type is array. + parameterNames.Insert(p.Name) + if p.Type == ParamTypeArray { + arrayParameterNames.Insert(p.Name) + } + } + + return errs.Also(validatePipelineParametersVariables(tasks, "params", parameterNames, arrayParameterNames)) +} + +func validatePipelineParametersVariables(tasks []PipelineTask, prefix string, paramNames sets.String, arrayParamNames sets.String) (errs *apis.FieldError) { + for idx, task := range tasks { + errs = errs.Also(validatePipelineParametersVariablesInTaskParameters(task.Params, prefix, paramNames, arrayParamNames).ViaIndex(idx)) + errs = errs.Also(task.WhenExpressions.validatePipelineParametersVariables(prefix, paramNames, arrayParamNames).ViaIndex(idx)) + } + return errs +} + +func validatePipelineContextVariables(tasks []PipelineTask) *apis.FieldError { + pipelineRunContextNames := sets.NewString().Insert( + "name", + "namespace", + "uid", + ) + pipelineContextNames := sets.NewString().Insert( + "name", + ) + pipelineTaskContextNames := sets.NewString().Insert( + "retries", + ) + var paramValues []string + for _, task := range tasks { + for _, param := range append(task.Params, task.Matrix...) { + paramValues = append(paramValues, param.Value.StringVal) + paramValues = append(paramValues, param.Value.ArrayVal...) + } + } + errs := validatePipelineContextVariablesInParamValues(paramValues, "context\\.pipelineRun", pipelineRunContextNames). + Also(validatePipelineContextVariablesInParamValues(paramValues, "context\\.pipeline", pipelineContextNames)). + Also(validatePipelineContextVariablesInParamValues(paramValues, "context\\.pipelineTask", pipelineTaskContextNames)) + return errs +} + +func containsExecutionStatusRef(p string) bool { + if strings.HasPrefix(p, "tasks.") && strings.HasSuffix(p, ".status") { + return true + } + return false +} + +// validate dag pipeline tasks, task params can not access execution status of any other task +// dag tasks cannot have param value as $(tasks.pipelineTask.status) +func validateExecutionStatusVariablesInTasks(tasks []PipelineTask) (errs *apis.FieldError) { + for idx, t := range tasks { + errs = errs.Also(t.validateExecutionStatusVariablesDisallowed()).ViaIndex(idx) + } + return errs +} + +// validate finally tasks accessing execution status of a dag task specified in the pipeline +// $(tasks.pipelineTask.status) is invalid if pipelineTask is not defined as a dag task +func validateExecutionStatusVariablesInFinally(tasksNames sets.String, finally []PipelineTask) (errs *apis.FieldError) { + for idx, t := range finally { + errs = errs.Also(t.validateExecutionStatusVariablesAllowed(tasksNames).ViaIndex(idx)) + } + return errs +} + +func validateExecutionStatusVariables(tasks []PipelineTask, finallyTasks []PipelineTask) (errs *apis.FieldError) { + errs = errs.Also(validateExecutionStatusVariablesInTasks(tasks).ViaField("tasks")) + errs = errs.Also(validateExecutionStatusVariablesInFinally(PipelineTaskList(tasks).Names(), finallyTasks).ViaField("finally")) + return errs +} + +func validatePipelineContextVariablesInParamValues(paramValues []string, prefix string, contextNames sets.String) (errs *apis.FieldError) { + for _, paramValue := range paramValues { + errs = errs.Also(substitution.ValidateVariableP(paramValue, prefix, contextNames).ViaField("value")) + } + return errs +} + +// validateParamResults ensures that task result variables are properly configured +func validateParamResults(tasks []PipelineTask) (errs *apis.FieldError) { + for idx, task := range tasks { + for _, param := range task.Params { + expressions, ok := GetVarSubstitutionExpressionsForParam(param) + if ok { + if LooksLikeContainsResultRefs(expressions) { + expressions = filter(expressions, looksLikeResultRef) + resultRefs := NewResultRefs(expressions) + if len(expressions) != len(resultRefs) { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("expected all of the expressions %v to be result expressions but only %v were", expressions, resultRefs), + "value").ViaFieldKey("params", param.Name).ViaFieldIndex("tasks", idx)) + } + } + } + } + } + return errs +} + +func filter(arr []string, cond func(string) bool) []string { + result := []string{} + for i := range arr { + if cond(arr[i]) { + result = append(result, arr[i]) + } + } + return result +} + +// validatePipelineResults ensure that pipeline result variables are properly configured +func validatePipelineResults(results []PipelineResult) (errs *apis.FieldError) { + for idx, result := range results { + expressions, ok := GetVarSubstitutionExpressionsForPipelineResult(result) + if ok { + if LooksLikeContainsResultRefs(expressions) { + expressions = filter(expressions, looksLikeResultRef) + resultRefs := NewResultRefs(expressions) + if len(expressions) != len(resultRefs) { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("expected all of the expressions %v to be result expressions but only %v were", expressions, resultRefs), + "value").ViaFieldIndex("results", idx)) + } + } + } + } + + return errs +} + +func validateTasksAndFinallySection(ps *PipelineSpec) *apis.FieldError { + if len(ps.Finally) != 0 && len(ps.Tasks) == 0 { + return apis.ErrInvalidValue(fmt.Sprintf("spec.tasks is empty but spec.finally has %d tasks", len(ps.Finally)), "finally") + } + return nil +} + +func validateFinalTasks(tasks []PipelineTask, finalTasks []PipelineTask) (errs *apis.FieldError) { + for idx, f := range finalTasks { + if len(f.RunAfter) != 0 { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("no runAfter allowed under spec.finally, final task %s has runAfter specified", f.Name), "").ViaFieldIndex("finally", idx)) + } + if len(f.Conditions) != 0 { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("no conditions allowed under spec.finally, final task %s has conditions specified", f.Name), "").ViaFieldIndex("finally", idx)) + } + } + + ts := PipelineTaskList(tasks).Names() + fts := PipelineTaskList(finalTasks).Names() + + errs = errs.Also(validateTaskResultReferenceInFinallyTasks(finalTasks, ts, fts)) + errs = errs.Also(validateTasksInputFrom(finalTasks).ViaField("finally")) + + return errs +} + +func validateTaskResultReferenceInFinallyTasks(finalTasks []PipelineTask, ts sets.String, fts sets.String) (errs *apis.FieldError) { + for idx, t := range finalTasks { + for _, p := range t.Params { + if expressions, ok := GetVarSubstitutionExpressionsForParam(p); ok { + errs = errs.Also(validateResultsVariablesExpressionsInFinally(expressions, ts, fts, "value").ViaFieldKey( + "params", p.Name).ViaFieldIndex("finally", idx)) + } + } + for i, we := range t.WhenExpressions { + if expressions, ok := we.GetVarSubstitutionExpressions(); ok { + errs = errs.Also(validateResultsVariablesExpressionsInFinally(expressions, ts, fts, "").ViaFieldIndex( + "when", i).ViaFieldIndex("finally", idx)) + } + } + } + return errs +} + +func validateResultsVariablesExpressionsInFinally(expressions []string, pipelineTasksNames sets.String, finalTasksNames sets.String, fieldPath string) (errs *apis.FieldError) { + if LooksLikeContainsResultRefs(expressions) { + resultRefs := NewResultRefs(expressions) + for _, resultRef := range resultRefs { + pt := resultRef.PipelineTask + if finalTasksNames.Has(pt) { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("invalid task result reference, "+ + "final task has task result reference from a final task %s", pt), fieldPath)) + } else if !pipelineTasksNames.Has(resultRef.PipelineTask) { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("invalid task result reference, "+ + "final task has task result reference from a task %s which is not defined in the pipeline", pt), fieldPath)) + } + } + } + return errs +} + +func validateTasksInputFrom(tasks []PipelineTask) (errs *apis.FieldError) { + for idx, t := range tasks { + inputResources := []PipelineTaskInputResource{} + if t.Resources != nil { + inputResources = append(inputResources, t.Resources.Inputs...) + } + for i, rd := range inputResources { + if len(rd.From) != 0 { + errs = errs.Also(apis.ErrGeneric(fmt.Sprintf("no from allowed under inputs,"+ + " final task %s has from specified", rd.Name), "").ViaFieldIndex("inputs", i).ViaField("resources").ViaIndex(idx)) + } + } + } + return errs +} + +func validateWhenExpressions(tasks []PipelineTask, finalTasks []PipelineTask) (errs *apis.FieldError) { + for i, t := range tasks { + errs = errs.Also(validateOneOfWhenExpressionsOrConditions(t).ViaFieldIndex("tasks", i)) + errs = errs.Also(t.WhenExpressions.validate().ViaFieldIndex("tasks", i)) + } + for i, t := range finalTasks { + errs = errs.Also(t.WhenExpressions.validate().ViaFieldIndex("finally", i)) + } + return errs +} + +func validateOneOfWhenExpressionsOrConditions(t PipelineTask) *apis.FieldError { + if t.WhenExpressions != nil && t.Conditions != nil { + return apis.ErrMultipleOneOf("when", "conditions") + } + return nil +} + +// validateDeclaredResources ensures that the specified resources have unique names and +// validates that all the resources referenced by pipeline tasks are declared in the pipeline +func validateDeclaredResources(resources []PipelineDeclaredResource, tasks []PipelineTask, finalTasks []PipelineTask) *apis.FieldError { + encountered := sets.NewString() + for _, r := range resources { + if encountered.Has(r.Name) { + return apis.ErrInvalidValue(fmt.Sprintf("resource with name %q appears more than once", r.Name), "resources") + } + encountered.Insert(r.Name) + } + required := []string{} + for _, t := range tasks { + if t.Resources != nil { + for _, input := range t.Resources.Inputs { + required = append(required, input.Resource) + } + for _, output := range t.Resources.Outputs { + required = append(required, output.Resource) + } + } + + for _, condition := range t.Conditions { + for _, cr := range condition.Resources { + required = append(required, cr.Resource) + } + } + } + for _, t := range finalTasks { + if t.Resources != nil { + for _, input := range t.Resources.Inputs { + required = append(required, input.Resource) + } + for _, output := range t.Resources.Outputs { + required = append(required, output.Resource) + } + } + } + + provided := make([]string, 0, len(resources)) + for _, resource := range resources { + provided = append(provided, resource.Name) + } + missing := list.DiffLeft(required, provided) + if len(missing) > 0 { + return apis.ErrInvalidValue(fmt.Sprintf("pipeline declared resources didn't match usage in Tasks: Didn't provide required values: %s", missing), "resources") + } + return nil +} + +func isOutput(outputs []PipelineTaskOutputResource, resource string) bool { + for _, output := range outputs { + if output.Resource == resource { + return true + } + } + return false +} + +// validateFrom ensures that the `from` values make sense: that they rely on values from Tasks +// that ran previously, and that the PipelineResource is actually an output of the Task it should come from. +func validateFrom(tasks []PipelineTask) (errs *apis.FieldError) { + taskOutputs := map[string][]PipelineTaskOutputResource{} + for _, task := range tasks { + var to []PipelineTaskOutputResource + if task.Resources != nil { + to = make([]PipelineTaskOutputResource, len(task.Resources.Outputs)) + copy(to, task.Resources.Outputs) + } + taskOutputs[task.Name] = to + } + for i, t := range tasks { + inputResources := []PipelineTaskInputResource{} + if t.Resources != nil { + inputResources = append(inputResources, t.Resources.Inputs...) + } + + for _, c := range t.Conditions { + inputResources = append(inputResources, c.Resources...) + } + + for j, rd := range inputResources { + for _, pt := range rd.From { + outputs, found := taskOutputs[pt] + if !found { + return apis.ErrInvalidValue(fmt.Sprintf("expected resource %s to be from task %s, but task %s doesn't exist", rd.Resource, pt, pt), + "from").ViaFieldIndex("inputs", j).ViaField("resources").ViaFieldIndex("tasks", i) + } + if !isOutput(outputs, rd.Resource) { + return apis.ErrInvalidValue(fmt.Sprintf("the resource %s from %s must be an output but is an input", rd.Resource, pt), + "from").ViaFieldIndex("inputs", j).ViaField("resources").ViaFieldIndex("tasks", i) + } + } + } + } + return errs +} + +// validateGraph ensures the Pipeline's dependency Graph (DAG) make sense: that there is no dependency +// cycle or that they rely on values from Tasks that ran previously, and that the PipelineResource +// is actually an output of the Task it should come from. +func validateGraph(tasks []PipelineTask) *apis.FieldError { + if _, err := dag.Build(PipelineTaskList(tasks), PipelineTaskList(tasks).Deps()); err != nil { + return apis.ErrInvalidValue(err.Error(), "tasks") + } + return nil +} + +func validateMatrix(ctx context.Context, tasks []PipelineTask) (errs *apis.FieldError) { + for idx, task := range tasks { + errs = errs.Also(task.validateMatrix(ctx).ViaIndex(idx)) + } + return errs +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelineref_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelineref_validation.go new file mode 100644 index 0000000000..1a61b7b219 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelineref_validation.go @@ -0,0 +1,88 @@ +/* +Copyright 2022 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + + "github.com/google/go-containerregistry/pkg/name" + "github.com/tektoncd/pipeline/pkg/apis/config" + "knative.dev/pkg/apis" +) + +// Validate ensures that a supplied PipelineRef field is populated +// correctly. No errors are returned for a nil PipelineRef. +func (ref *PipelineRef) Validate(ctx context.Context) (errs *apis.FieldError) { + cfg := config.FromContextOrDefaults(ctx) + if ref == nil { + return + } + if cfg.FeatureFlags.EnableAPIFields == config.AlphaAPIFields { + errs = errs.Also(ref.validateAlphaRef(ctx)) + } else { + errs = errs.Also(ref.validateInTreeRef(ctx)) + } + return +} + +// validateInTreeRef returns errors if the given pipelineRef is not +// valid for Pipelines' built-in resolution machinery. +func (ref *PipelineRef) validateInTreeRef(ctx context.Context) (errs *apis.FieldError) { + cfg := config.FromContextOrDefaults(ctx) + if ref.Resolver != "" { + errs = errs.Also(apis.ErrDisallowedFields("resolver")) + } + if ref.Resource != nil { + errs = errs.Also(apis.ErrDisallowedFields("resource")) + } + if ref.Name == "" { + errs = errs.Also(apis.ErrMissingField("name")) + } + if cfg.FeatureFlags.EnableTektonOCIBundles { + if ref.Bundle != "" && ref.Name == "" { + errs = errs.Also(apis.ErrMissingField("name")) + } + if ref.Bundle != "" { + if _, err := name.ParseReference(ref.Bundle); err != nil { + errs = errs.Also(apis.ErrInvalidValue("invalid bundle reference", "bundle", err.Error())) + } + } + } else if ref.Bundle != "" { + errs = errs.Also(apis.ErrDisallowedFields("bundle")) + } + return +} + +// validateAlphaRef ensures that the user has passed either a +// valid remote resource reference or a valid in-tree resource reference, +// but not both. +func (ref *PipelineRef) validateAlphaRef(ctx context.Context) (errs *apis.FieldError) { + switch { + case ref.Resolver == "" && ref.Resource != nil: + errs = errs.Also(apis.ErrMissingField("resolver")) + case ref.Resolver == "": + errs = errs.Also(ref.validateInTreeRef(ctx)) + default: + if ref.Name != "" { + errs = errs.Also(apis.ErrMultipleOneOf("name", "resolver")) + } + if ref.Bundle != "" { + errs = errs.Also(apis.ErrMultipleOneOf("bundle", "resolver")) + } + } + return +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelinerun_conversion.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelinerun_conversion.go new file mode 100644 index 0000000000..e213f10eec --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelinerun_conversion.go @@ -0,0 +1,36 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + + "knative.dev/pkg/apis" +) + +var _ apis.Convertible = (*PipelineRun)(nil) + +// ConvertTo implements api.Convertible +func (pr *PipelineRun) ConvertTo(ctx context.Context, sink apis.Convertible) error { + return fmt.Errorf("v1beta1 is the highest known version, got: %T", sink) +} + +// ConvertFrom implements api.Convertible +func (pr *PipelineRun) ConvertFrom(ctx context.Context, source apis.Convertible) error { + return fmt.Errorf("v1beta1 is the highest know version, got: %T", source) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelinerun_defaults.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelinerun_defaults.go new file mode 100644 index 0000000000..38df794b85 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelinerun_defaults.go @@ -0,0 +1,60 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "time" + + "github.com/tektoncd/pipeline/pkg/apis/config" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +var _ apis.Defaultable = (*PipelineRun)(nil) + +// SetDefaults implements apis.Defaultable +func (pr *PipelineRun) SetDefaults(ctx context.Context) { + pr.Spec.SetDefaults(ctx) +} + +// SetDefaults implements apis.Defaultable +func (prs *PipelineRunSpec) SetDefaults(ctx context.Context) { + cfg := config.FromContextOrDefaults(ctx) + if prs.Timeout == nil && prs.Timeouts == nil { + prs.Timeout = &metav1.Duration{Duration: time.Duration(cfg.Defaults.DefaultTimeoutMinutes) * time.Minute} + } + + if prs.Timeouts != nil && prs.Timeouts.Pipeline == nil { + prs.Timeouts.Pipeline = &metav1.Duration{Duration: time.Duration(cfg.Defaults.DefaultTimeoutMinutes) * time.Minute} + } + + defaultSA := cfg.Defaults.DefaultServiceAccount + if prs.ServiceAccountName == "" && defaultSA != "" { + prs.ServiceAccountName = defaultSA + } + + defaultPodTemplate := cfg.Defaults.DefaultPodTemplate + prs.PodTemplate = mergePodTemplateWithDefault(prs.PodTemplate, defaultPodTemplate) + + if prs.PipelineSpec != nil { + prs.PipelineSpec.SetDefaults(ctx) + if config.FromContextOrDefaults(ctx).FeatureFlags.EnableAPIFields == "alpha" { + prs.PipelineSpec.applyImplicitParams(addContextParams(ctx, prs.Params)) + } + } +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelinerun_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelinerun_types.go new file mode 100644 index 0000000000..31b64ef959 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelinerun_types.go @@ -0,0 +1,542 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "time" + + "github.com/tektoncd/pipeline/pkg/apis/config" + apisconfig "github.com/tektoncd/pipeline/pkg/apis/config" + "github.com/tektoncd/pipeline/pkg/apis/pipeline" + runv1alpha1 "github.com/tektoncd/pipeline/pkg/apis/run/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/clock" + "knative.dev/pkg/apis" + duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1" +) + +// +genclient +// +genreconciler:krshapedlogic=false +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PipelineRun represents a single execution of a Pipeline. PipelineRuns are how +// the graph of Tasks declared in a Pipeline are executed; they specify inputs +// to Pipelines such as parameter values and capture operational aspects of the +// Tasks execution such as service account and tolerations. Creating a +// PipelineRun creates TaskRuns for Tasks in the referenced Pipeline. +// +// +k8s:openapi-gen=true +type PipelineRun struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +optional + Spec PipelineRunSpec `json:"spec,omitempty"` + // +optional + Status PipelineRunStatus `json:"status,omitempty"` +} + +// GetName Returns the name of the PipelineRun +func (pr *PipelineRun) GetName() string { + return pr.ObjectMeta.GetName() +} + +// GetStatusCondition returns the task run status as a ConditionAccessor +func (pr *PipelineRun) GetStatusCondition() apis.ConditionAccessor { + return &pr.Status +} + +// GetGroupVersionKind implements kmeta.OwnerRefable. +func (*PipelineRun) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind(pipeline.PipelineRunControllerName) +} + +// IsDone returns true if the PipelineRun's status indicates that it is done. +func (pr *PipelineRun) IsDone() bool { + return !pr.Status.GetCondition(apis.ConditionSucceeded).IsUnknown() +} + +// HasStarted function check whether pipelinerun has valid start time set in its status +func (pr *PipelineRun) HasStarted() bool { + return pr.Status.StartTime != nil && !pr.Status.StartTime.IsZero() +} + +// IsCancelled returns true if the PipelineRun's spec status is set to Cancelled state +func (pr *PipelineRun) IsCancelled() bool { + return pr.Spec.Status == PipelineRunSpecStatusCancelled || pr.Spec.Status == PipelineRunSpecStatusCancelledDeprecated +} + +// IsGracefullyCancelled returns true if the PipelineRun's spec status is set to CancelledRunFinally state +func (pr *PipelineRun) IsGracefullyCancelled() bool { + return pr.Spec.Status == PipelineRunSpecStatusCancelledRunFinally +} + +// IsGracefullyStopped returns true if the PipelineRun's spec status is set to StoppedRunFinally state +func (pr *PipelineRun) IsGracefullyStopped() bool { + return pr.Spec.Status == PipelineRunSpecStatusStoppedRunFinally +} + +// PipelineTimeout returns the the applicable timeout for the PipelineRun +func (pr *PipelineRun) PipelineTimeout(ctx context.Context) time.Duration { + if pr.Spec.Timeout != nil { + return pr.Spec.Timeout.Duration + } + if pr.Spec.Timeouts != nil && pr.Spec.Timeouts.Pipeline != nil { + return pr.Spec.Timeouts.Pipeline.Duration + } + return time.Duration(config.FromContextOrDefaults(ctx).Defaults.DefaultTimeoutMinutes) * time.Minute +} + +// TasksTimeout returns the the tasks timeout for the PipelineRun, if set, +// or the tasks timeout computed from the Pipeline and Finally timeouts, if those are set. +func (pr *PipelineRun) TasksTimeout() *metav1.Duration { + t := pr.Spec.Timeouts + if t == nil { + return nil + } + if t.Tasks != nil { + return t.Tasks + } + if t.Pipeline != nil && t.Finally != nil { + if t.Pipeline.Duration == apisconfig.NoTimeoutDuration || t.Finally.Duration == apisconfig.NoTimeoutDuration { + return nil + } + return &metav1.Duration{Duration: (t.Pipeline.Duration - t.Finally.Duration)} + } + return nil +} + +// FinallyTimeout returns the the finally timeout for the PipelineRun, if set, +// or the finally timeout computed from the Pipeline and Tasks timeouts, if those are set. +func (pr *PipelineRun) FinallyTimeout() *metav1.Duration { + t := pr.Spec.Timeouts + if t == nil { + return nil + } + if t.Finally != nil { + return t.Finally + } + if t.Pipeline != nil && t.Tasks != nil { + if t.Pipeline.Duration == apisconfig.NoTimeoutDuration || t.Tasks.Duration == apisconfig.NoTimeoutDuration { + return nil + } + return &metav1.Duration{Duration: (t.Pipeline.Duration - t.Tasks.Duration)} + } + return nil +} + +// IsPending returns true if the PipelineRun's spec status is set to Pending state +func (pr *PipelineRun) IsPending() bool { + return pr.Spec.Status == PipelineRunSpecStatusPending +} + +// GetNamespacedName returns a k8s namespaced name that identifies this PipelineRun +func (pr *PipelineRun) GetNamespacedName() types.NamespacedName { + return types.NamespacedName{Namespace: pr.Namespace, Name: pr.Name} +} + +// HasTimedOut returns true if a pipelinerun has exceeded its spec.Timeout based on its status.Timeout +func (pr *PipelineRun) HasTimedOut(ctx context.Context, c clock.PassiveClock) bool { + timeout := pr.PipelineTimeout(ctx) + startTime := pr.Status.StartTime + + if !startTime.IsZero() { + if timeout == config.NoTimeoutDuration { + return false + } + runtime := c.Since(startTime.Time) + if runtime > timeout { + return true + } + } + return false +} + +// GetServiceAccountName returns the service account name for a given +// PipelineTask if configured, otherwise it returns the PipelineRun's serviceAccountName. +func (pr *PipelineRun) GetServiceAccountName(pipelineTaskName string) string { + serviceAccountName := pr.Spec.ServiceAccountName + for _, sa := range pr.Spec.ServiceAccountNames { + if sa.TaskName == pipelineTaskName { + serviceAccountName = sa.ServiceAccountName + } + } + return serviceAccountName +} + +// HasVolumeClaimTemplate returns true if PipelineRun contains volumeClaimTemplates that is +// used for creating PersistentVolumeClaims with an OwnerReference for each run +func (pr *PipelineRun) HasVolumeClaimTemplate() bool { + for _, ws := range pr.Spec.Workspaces { + if ws.VolumeClaimTemplate != nil { + return true + } + } + return false +} + +// PipelineRunSpec defines the desired state of PipelineRun +type PipelineRunSpec struct { + // +optional + PipelineRef *PipelineRef `json:"pipelineRef,omitempty"` + // +optional + PipelineSpec *PipelineSpec `json:"pipelineSpec,omitempty"` + // Resources is a list of bindings specifying which actual instances of + // PipelineResources to use for the resources the Pipeline has declared + // it needs. + Resources []PipelineResourceBinding `json:"resources,omitempty"` + // Params is a list of parameter names and values. + Params []Param `json:"params,omitempty"` + // +optional + ServiceAccountName string `json:"serviceAccountName,omitempty"` + + // Deprecated: use taskRunSpecs.ServiceAccountName instead + // +optional + ServiceAccountNames []PipelineRunSpecServiceAccountName `json:"serviceAccountNames,omitempty"` + // Used for cancelling a pipelinerun (and maybe more later on) + // +optional + Status PipelineRunSpecStatus `json:"status,omitempty"` + // This is an alpha field. You must set the "enable-api-fields" feature flag to "alpha" + // for this field to be supported. + // + // Time after which the Pipeline times out. + // Currently three keys are accepted in the map + // pipeline, tasks and finally + // with Timeouts.pipeline >= Timeouts.tasks + Timeouts.finally + // +optional + Timeouts *TimeoutFields `json:"timeouts,omitempty"` + // Time after which the Pipeline times out. Defaults to never. + // Refer to Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + // PodTemplate holds pod specific configuration + PodTemplate *PodTemplate `json:"podTemplate,omitempty"` + // Workspaces holds a set of workspace bindings that must match names + // with those declared in the pipeline. + // +optional + Workspaces []WorkspaceBinding `json:"workspaces,omitempty"` + // TaskRunSpecs holds a set of runtime specs + // +optional + TaskRunSpecs []PipelineTaskRunSpec `json:"taskRunSpecs,omitempty"` +} + +// TimeoutFields allows granular specification of pipeline, task, and finally timeouts +type TimeoutFields struct { + // Pipeline sets the maximum allowed duration for execution of the entire pipeline. The sum of individual timeouts for tasks and finally must not exceed this value. + Pipeline *metav1.Duration `json:"pipeline,omitempty"` + // Tasks sets the maximum allowed duration of this pipeline's tasks + Tasks *metav1.Duration `json:"tasks,omitempty"` + // Finally sets the maximum allowed duration of this pipeline's finally + Finally *metav1.Duration `json:"finally,omitempty"` +} + +// PipelineRunSpecStatus defines the pipelinerun spec status the user can provide +type PipelineRunSpecStatus string + +const ( + // PipelineRunSpecStatusCancelledDeprecated Deprecated: indicates that the user wants to cancel the task, + // if not already cancelled or terminated (replaced by "Cancelled") + PipelineRunSpecStatusCancelledDeprecated = "PipelineRunCancelled" + + // PipelineRunSpecStatusCancelled indicates that the user wants to cancel the task, + // if not already cancelled or terminated + PipelineRunSpecStatusCancelled = "Cancelled" + + // PipelineRunSpecStatusCancelledRunFinally indicates that the user wants to cancel the pipeline run, + // if not already cancelled or terminated, but ensure finally is run normally + PipelineRunSpecStatusCancelledRunFinally = "CancelledRunFinally" + + // PipelineRunSpecStatusStoppedRunFinally indicates that the user wants to stop the pipeline run, + // wait for already running tasks to be completed and run finally + // if not already cancelled or terminated + PipelineRunSpecStatusStoppedRunFinally = "StoppedRunFinally" + + // PipelineRunSpecStatusPending indicates that the user wants to postpone starting a PipelineRun + // until some condition is met + PipelineRunSpecStatusPending = "PipelineRunPending" +) + +// PipelineRef can be used to refer to a specific instance of a Pipeline. +// Copied from CrossVersionObjectReference: https://github.com/kubernetes/kubernetes/blob/169df7434155cbbc22f1532cba8e0a9588e29ad8/pkg/apis/autoscaling/types.go#L64 +type PipelineRef struct { + // Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names + Name string `json:"name,omitempty"` + // API version of the referent + // +optional + APIVersion string `json:"apiVersion,omitempty"` + // Bundle url reference to a Tekton Bundle. + // +optional + Bundle string `json:"bundle,omitempty"` + + // ResolverRef allows referencing a Pipeline in a remote location + // like a git repo. This field is only supported when the alpha + // feature gate is enabled. + // +optional + ResolverRef `json:",omitempty"` +} + +// PipelineRunStatus defines the observed state of PipelineRun +type PipelineRunStatus struct { + duckv1beta1.Status `json:",inline"` + + // PipelineRunStatusFields inlines the status fields. + PipelineRunStatusFields `json:",inline"` +} + +// PipelineRunReason represents a reason for the pipeline run "Succeeded" condition +type PipelineRunReason string + +const ( + // PipelineRunReasonStarted is the reason set when the PipelineRun has just started + PipelineRunReasonStarted PipelineRunReason = "Started" + // PipelineRunReasonRunning is the reason set when the PipelineRun is running + PipelineRunReasonRunning PipelineRunReason = "Running" + // PipelineRunReasonSuccessful is the reason set when the PipelineRun completed successfully + PipelineRunReasonSuccessful PipelineRunReason = "Succeeded" + // PipelineRunReasonCompleted is the reason set when the PipelineRun completed successfully with one or more skipped Tasks + PipelineRunReasonCompleted PipelineRunReason = "Completed" + // PipelineRunReasonFailed is the reason set when the PipelineRun completed with a failure + PipelineRunReasonFailed PipelineRunReason = "Failed" + // PipelineRunReasonCancelled is the reason set when the PipelineRun cancelled by the user + // This reason may be found with a corev1.ConditionFalse status, if the cancellation was processed successfully + // This reason may be found with a corev1.ConditionUnknown status, if the cancellation is being processed or failed + PipelineRunReasonCancelled PipelineRunReason = "Cancelled" + // PipelineRunReasonPending is the reason set when the PipelineRun is in the pending state + PipelineRunReasonPending PipelineRunReason = "PipelineRunPending" + // PipelineRunReasonTimedOut is the reason set when the PipelineRun has timed out + PipelineRunReasonTimedOut PipelineRunReason = "PipelineRunTimeout" + // PipelineRunReasonStopping indicates that no new Tasks will be scheduled by the controller, and the + // pipeline will stop once all running tasks complete their work + PipelineRunReasonStopping PipelineRunReason = "PipelineRunStopping" + // PipelineRunReasonCancelledRunningFinally indicates that pipeline has been gracefully cancelled + // and no new Tasks will be scheduled by the controller, but final tasks are now running + PipelineRunReasonCancelledRunningFinally PipelineRunReason = "CancelledRunningFinally" + // PipelineRunReasonStoppedRunningFinally indicates that pipeline has been gracefully stopped + // and no new Tasks will be scheduled by the controller, but final tasks are now running + PipelineRunReasonStoppedRunningFinally PipelineRunReason = "StoppedRunningFinally" +) + +func (t PipelineRunReason) String() string { + return string(t) +} + +var pipelineRunCondSet = apis.NewBatchConditionSet() + +// GetCondition returns the Condition matching the given type. +func (pr *PipelineRunStatus) GetCondition(t apis.ConditionType) *apis.Condition { + return pipelineRunCondSet.Manage(pr).GetCondition(t) +} + +// InitializeConditions will set all conditions in pipelineRunCondSet to unknown for the PipelineRun +// and set the started time to the current time +func (pr *PipelineRunStatus) InitializeConditions(c clock.PassiveClock) { + started := false + if pr.TaskRuns == nil { + pr.TaskRuns = make(map[string]*PipelineRunTaskRunStatus) + } + if pr.Runs == nil { + pr.Runs = make(map[string]*PipelineRunRunStatus) + } + if pr.StartTime.IsZero() { + pr.StartTime = &metav1.Time{Time: c.Now()} + started = true + } + conditionManager := pipelineRunCondSet.Manage(pr) + conditionManager.InitializeConditions() + // Ensure the started reason is set for the "Succeeded" condition + if started { + initialCondition := conditionManager.GetCondition(apis.ConditionSucceeded) + initialCondition.Reason = PipelineRunReasonStarted.String() + conditionManager.SetCondition(*initialCondition) + } +} + +// SetCondition sets the condition, unsetting previous conditions with the same +// type as necessary. +func (pr *PipelineRunStatus) SetCondition(newCond *apis.Condition) { + if newCond != nil { + pipelineRunCondSet.Manage(pr).SetCondition(*newCond) + } +} + +// MarkSucceeded changes the Succeeded condition to True with the provided reason and message. +func (pr *PipelineRunStatus) MarkSucceeded(reason, messageFormat string, messageA ...interface{}) { + pipelineRunCondSet.Manage(pr).MarkTrueWithReason(apis.ConditionSucceeded, reason, messageFormat, messageA...) + succeeded := pr.GetCondition(apis.ConditionSucceeded) + pr.CompletionTime = &succeeded.LastTransitionTime.Inner +} + +// MarkFailed changes the Succeeded condition to False with the provided reason and message. +func (pr *PipelineRunStatus) MarkFailed(reason, messageFormat string, messageA ...interface{}) { + pipelineRunCondSet.Manage(pr).MarkFalse(apis.ConditionSucceeded, reason, messageFormat, messageA...) + succeeded := pr.GetCondition(apis.ConditionSucceeded) + pr.CompletionTime = &succeeded.LastTransitionTime.Inner +} + +// MarkRunning changes the Succeeded condition to Unknown with the provided reason and message. +func (pr *PipelineRunStatus) MarkRunning(reason, messageFormat string, messageA ...interface{}) { + pipelineRunCondSet.Manage(pr).MarkUnknown(apis.ConditionSucceeded, reason, messageFormat, messageA...) +} + +// PipelineRunStatusFields holds the fields of PipelineRunStatus' status. +// This is defined separately and inlined so that other types can readily +// consume these fields via duck typing. +type PipelineRunStatusFields struct { + // StartTime is the time the PipelineRun is actually started. + // +optional + StartTime *metav1.Time `json:"startTime,omitempty"` + + // CompletionTime is the time the PipelineRun completed. + // +optional + CompletionTime *metav1.Time `json:"completionTime,omitempty"` + + // map of PipelineRunTaskRunStatus with the taskRun name as the key + // +optional + TaskRuns map[string]*PipelineRunTaskRunStatus `json:"taskRuns,omitempty"` + + // map of PipelineRunRunStatus with the run name as the key + // +optional + Runs map[string]*PipelineRunRunStatus `json:"runs,omitempty"` + + // PipelineResults are the list of results written out by the pipeline task's containers + // +optional + PipelineResults []PipelineRunResult `json:"pipelineResults,omitempty"` + + // PipelineRunSpec contains the exact spec used to instantiate the run + PipelineSpec *PipelineSpec `json:"pipelineSpec,omitempty"` + + // list of tasks that were skipped due to when expressions evaluating to false + // +optional + SkippedTasks []SkippedTask `json:"skippedTasks,omitempty"` +} + +// SkippedTask is used to describe the Tasks that were skipped due to their When Expressions +// evaluating to False. This is a struct because we are looking into including more details +// about the When Expressions that caused this Task to be skipped. +type SkippedTask struct { + // Name is the Pipeline Task name + Name string `json:"name"` + // WhenExpressions is the list of checks guarding the execution of the PipelineTask + // +optional + WhenExpressions []WhenExpression `json:"whenExpressions,omitempty"` +} + +// PipelineRunResult used to describe the results of a pipeline +type PipelineRunResult struct { + // Name is the result's name as declared by the Pipeline + Name string `json:"name"` + + // Value is the result returned from the execution of this PipelineRun + Value string `json:"value"` +} + +// PipelineRunTaskRunStatus contains the name of the PipelineTask for this TaskRun and the TaskRun's Status +type PipelineRunTaskRunStatus struct { + // PipelineTaskName is the name of the PipelineTask. + PipelineTaskName string `json:"pipelineTaskName,omitempty"` + // Status is the TaskRunStatus for the corresponding TaskRun + // +optional + Status *TaskRunStatus `json:"status,omitempty"` + // ConditionChecks maps the name of a condition check to its Status + // +optional + ConditionChecks map[string]*PipelineRunConditionCheckStatus `json:"conditionChecks,omitempty"` + // WhenExpressions is the list of checks guarding the execution of the PipelineTask + // +optional + WhenExpressions []WhenExpression `json:"whenExpressions,omitempty"` +} + +// PipelineRunRunStatus contains the name of the PipelineTask for this Run and the Run's Status +type PipelineRunRunStatus struct { + // PipelineTaskName is the name of the PipelineTask. + PipelineTaskName string `json:"pipelineTaskName,omitempty"` + // Status is the RunStatus for the corresponding Run + // +optional + Status *runv1alpha1.RunStatus `json:"status,omitempty"` + // WhenExpressions is the list of checks guarding the execution of the PipelineTask + // +optional + WhenExpressions []WhenExpression `json:"whenExpressions,omitempty"` +} + +// PipelineRunConditionCheckStatus returns the condition check status +type PipelineRunConditionCheckStatus struct { + // ConditionName is the name of the Condition + ConditionName string `json:"conditionName,omitempty"` + // Status is the ConditionCheckStatus for the corresponding ConditionCheck + // +optional + Status *ConditionCheckStatus `json:"status,omitempty"` +} + +// PipelineRunSpecServiceAccountName can be used to configure specific +// ServiceAccountName for a concrete Task +type PipelineRunSpecServiceAccountName struct { + TaskName string `json:"taskName,omitempty"` + ServiceAccountName string `json:"serviceAccountName,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PipelineRunList contains a list of PipelineRun +type PipelineRunList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []PipelineRun `json:"items,omitempty"` +} + +// PipelineTaskRun reports the results of running a step in the Task. Each +// task has the potential to succeed or fail (based on the exit code) +// and produces logs. +type PipelineTaskRun struct { + Name string `json:"name,omitempty"` +} + +// PipelineTaskRunSpec can be used to configure specific +// specs for a concrete Task +type PipelineTaskRunSpec struct { + PipelineTaskName string `json:"pipelineTaskName,omitempty"` + TaskServiceAccountName string `json:"taskServiceAccountName,omitempty"` + TaskPodTemplate *PodTemplate `json:"taskPodTemplate,omitempty"` + StepOverrides []TaskRunStepOverride `json:"stepOverrides,omitempty"` + SidecarOverrides []TaskRunSidecarOverride `json:"sidecarOverrides,omitempty"` +} + +// GetTaskRunSpec returns the task specific spec for a given +// PipelineTask if configured, otherwise it returns the PipelineRun's default. +func (pr *PipelineRun) GetTaskRunSpec(pipelineTaskName string) PipelineTaskRunSpec { + s := PipelineTaskRunSpec{ + PipelineTaskName: pipelineTaskName, + TaskServiceAccountName: pr.GetServiceAccountName(pipelineTaskName), + TaskPodTemplate: pr.Spec.PodTemplate, + } + for _, task := range pr.Spec.TaskRunSpecs { + if task.PipelineTaskName == pipelineTaskName { + if task.TaskPodTemplate != nil { + s.TaskPodTemplate = task.TaskPodTemplate + } + if task.TaskServiceAccountName != "" { + s.TaskServiceAccountName = task.TaskServiceAccountName + } + s.StepOverrides = task.StepOverrides + s.SidecarOverrides = task.SidecarOverrides + } + } + return s +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelinerun_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelinerun_validation.go new file mode 100644 index 0000000000..715384fee2 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pipelinerun_validation.go @@ -0,0 +1,214 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + "time" + + "github.com/tektoncd/pipeline/pkg/apis/config" + apisconfig "github.com/tektoncd/pipeline/pkg/apis/config" + "github.com/tektoncd/pipeline/pkg/apis/validate" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +var _ apis.Validatable = (*PipelineRun)(nil) + +// Validate pipelinerun +func (pr *PipelineRun) Validate(ctx context.Context) *apis.FieldError { + errs := validate.ObjectMetadata(pr.GetObjectMeta()).ViaField("metadata") + + if apis.IsInDelete(ctx) { + return nil + } + + if pr.IsPending() && pr.HasStarted() { + errs = errs.Also(apis.ErrInvalidValue("PipelineRun cannot be Pending after it is started", "spec.status")) + } + + return errs.Also(pr.Spec.Validate(apis.WithinSpec(ctx)).ViaField("spec")) +} + +// Validate pipelinerun spec +func (ps *PipelineRunSpec) Validate(ctx context.Context) (errs *apis.FieldError) { + // Must have exactly one of pipelineRef and pipelineSpec. + if ps.PipelineRef == nil && ps.PipelineSpec == nil { + errs = errs.Also(apis.ErrMissingOneOf("pipelineRef", "pipelineSpec")) + } + if ps.PipelineRef != nil && ps.PipelineSpec != nil { + errs = errs.Also(apis.ErrMultipleOneOf("pipelineRef", "pipelineSpec")) + } + + // Validate PipelineRef if it's present + if ps.PipelineRef != nil { + errs = errs.Also(ps.PipelineRef.Validate(ctx).ViaField("pipelineRef")) + } + + // Validate PipelineSpec if it's present + if ps.PipelineSpec != nil { + errs = errs.Also(ps.PipelineSpec.Validate(ctx).ViaField("pipelineSpec")) + } + + if ps.Timeout != nil { + // timeout should be a valid duration of at least 0. + if ps.Timeout.Duration < 0 { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s should be >= 0", ps.Timeout.Duration.String()), "timeout")) + } + } + + // This is an alpha feature and will fail validation if it's used in a pipelinerun spec + // when the enable-api-fields feature gate is anything but "alpha". + if ps.Timeouts != nil { + if ps.Timeout != nil { + // can't have both at the same time + errs = errs.Also(apis.ErrDisallowedFields("timeout", "timeouts")) + } + + errs = errs.Also(ValidateEnabledAPIFields(ctx, "timeouts", config.AlphaAPIFields)) + + // tasks timeout should be a valid duration of at least 0. + errs = errs.Also(validateTimeoutDuration("tasks", ps.Timeouts.Tasks)) + + // finally timeout should be a valid duration of at least 0. + errs = errs.Also(validateTimeoutDuration("finally", ps.Timeouts.Finally)) + + // pipeline timeout should be a valid duration of at least 0. + errs = errs.Also(validateTimeoutDuration("pipeline", ps.Timeouts.Pipeline)) + + if ps.Timeouts.Pipeline != nil { + errs = errs.Also(ps.validatePipelineTimeout(ps.Timeouts.Pipeline.Duration, "should be <= pipeline duration")) + } else { + defaultTimeout := time.Duration(config.FromContextOrDefaults(ctx).Defaults.DefaultTimeoutMinutes) + errs = errs.Also(ps.validatePipelineTimeout(defaultTimeout, "should be <= default timeout duration")) + } + } + + errs = errs.Also(validateSpecStatus(ctx, ps.Status)) + + if ps.Workspaces != nil { + wsNames := make(map[string]int) + for idx, ws := range ps.Workspaces { + errs = errs.Also(ws.Validate(ctx).ViaFieldIndex("workspaces", idx)) + if prevIdx, alreadyExists := wsNames[ws.Name]; alreadyExists { + errs = errs.Also(apis.ErrGeneric(fmt.Sprintf("workspace %q provided by pipelinerun more than once, at index %d and %d", ws.Name, prevIdx, idx), "name").ViaFieldIndex("workspaces", idx)) + } + wsNames[ws.Name] = idx + } + } + + for idx, trs := range ps.TaskRunSpecs { + errs = errs.Also(validateTaskRunSpec(ctx, trs).ViaIndex(idx).ViaField("taskRunSpecs")) + } + + return errs +} + +func validateSpecStatus(ctx context.Context, status PipelineRunSpecStatus) *apis.FieldError { + switch status { + case "": + return nil + case PipelineRunSpecStatusPending, + PipelineRunSpecStatusCancelledDeprecated: + return nil + case PipelineRunSpecStatusCancelled, + PipelineRunSpecStatusCancelledRunFinally, + PipelineRunSpecStatusStoppedRunFinally: + return ValidateEnabledAPIFields(ctx, "graceful termination", "alpha") + } + + cfg := config.FromContextOrDefaults(ctx) + if cfg.FeatureFlags.EnableAPIFields == config.AlphaAPIFields { + return apis.ErrInvalidValue(fmt.Sprintf("%s should be %s, %s, %s or %s", status, + PipelineRunSpecStatusCancelled, + PipelineRunSpecStatusCancelledRunFinally, + PipelineRunSpecStatusStoppedRunFinally, + PipelineRunSpecStatusPending), "status") + } + return apis.ErrInvalidValue(fmt.Sprintf("%s should be %s or %s", status, + PipelineRunSpecStatusCancelledDeprecated, + PipelineRunSpecStatusPending), "status") +} + +func validateTimeoutDuration(field string, d *metav1.Duration) (errs *apis.FieldError) { + if d != nil && d.Duration < 0 { + fieldPath := fmt.Sprintf("timeouts.%s", field) + return errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s should be >= 0", d.Duration.String()), fieldPath)) + } + return nil +} + +func (ps *PipelineRunSpec) validatePipelineTimeout(timeout time.Duration, errorMsg string) (errs *apis.FieldError) { + if ps.Timeouts.Tasks != nil { + tasksTimeoutErr := false + tasksTimeoutStr := ps.Timeouts.Tasks.Duration.String() + if ps.Timeouts.Tasks.Duration > timeout { + tasksTimeoutErr = true + } + if ps.Timeouts.Tasks.Duration == apisconfig.NoTimeoutDuration && timeout != apisconfig.NoTimeoutDuration { + tasksTimeoutErr = true + tasksTimeoutStr += " (no timeout)" + } + if tasksTimeoutErr { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s %s", tasksTimeoutStr, errorMsg), "timeouts.tasks")) + } + } + + if ps.Timeouts.Finally != nil { + finallyTimeoutErr := false + finallyTimeoutStr := ps.Timeouts.Finally.Duration.String() + if ps.Timeouts.Finally.Duration > timeout { + finallyTimeoutErr = true + } + if ps.Timeouts.Finally.Duration == apisconfig.NoTimeoutDuration && timeout != apisconfig.NoTimeoutDuration { + finallyTimeoutErr = true + finallyTimeoutStr += " (no timeout)" + } + if finallyTimeoutErr { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s %s", finallyTimeoutStr, errorMsg), "timeouts.finally")) + } + } + + if ps.Timeouts.Tasks != nil && ps.Timeouts.Finally != nil { + if ps.Timeouts.Tasks.Duration+ps.Timeouts.Finally.Duration > timeout { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s + %s %s", ps.Timeouts.Tasks.Duration.String(), ps.Timeouts.Finally.Duration.String(), errorMsg), "timeouts.tasks")) + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s + %s %s", ps.Timeouts.Tasks.Duration.String(), ps.Timeouts.Finally.Duration.String(), errorMsg), "timeouts.finally")) + } + } + return errs +} + +func validateTaskRunSpec(ctx context.Context, trs PipelineTaskRunSpec) (errs *apis.FieldError) { + cfg := config.FromContextOrDefaults(ctx) + if cfg.FeatureFlags.EnableAPIFields == config.AlphaAPIFields { + if trs.StepOverrides != nil { + errs = errs.Also(validateStepOverrides(trs.StepOverrides).ViaField("stepOverrides")) + } + if trs.SidecarOverrides != nil { + errs = errs.Also(validateSidecarOverrides(trs.SidecarOverrides).ViaField("sidecarOverrides")) + } + } else { + if trs.StepOverrides != nil { + errs = errs.Also(apis.ErrDisallowedFields("stepOverrides")) + } + if trs.SidecarOverrides != nil { + errs = errs.Also(apis.ErrDisallowedFields("sidecarOverrides")) + } + } + return errs +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pod.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pod.go new file mode 100644 index 0000000000..d4e16486e9 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/pod.go @@ -0,0 +1,22 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import "github.com/tektoncd/pipeline/pkg/apis/pipeline/pod" + +// PodTemplate holds pod specific configuration +type PodTemplate = pod.Template diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/register.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/register.go new file mode 100644 index 0000000000..4d7de9d224 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/register.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "github.com/tektoncd/pipeline/pkg/apis/pipeline" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: pipeline.GroupName, Version: "v1beta1"} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + + // AddToScheme adds Build types to the scheme. + AddToScheme = schemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &Task{}, + &TaskList{}, + &Pipeline{}, + &PipelineList{}, + &ClusterTask{}, + &ClusterTaskList{}, + &TaskRun{}, + &TaskRunList{}, + &PipelineRun{}, + &PipelineRunList{}, + ) + // &Condition{}, + // &ConditionList{}, + + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resolver_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resolver_types.go new file mode 100644 index 0000000000..41ebde1126 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resolver_types.go @@ -0,0 +1,47 @@ +/* +Copyright 2022 The Tekton Authors + +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. +*/ + +package v1beta1 + +// ResolverName is the name of a resolver from which a resource can be +// requested. +type ResolverName string + +// ResolverRef can be used to refer to a Pipeline or Task in a remote +// location like a git repo. This feature is in alpha and these fields +// are only available when the alpha feature gate is enabled. +type ResolverRef struct { + // Resolver is the name of the resolver that should perform + // resolution of the referenced Tekton resource, such as "git". + // +optional + Resolver ResolverName `json:"resolver,omitempty"` + // Resource contains the parameters used to identify the + // referenced Tekton resource. Example entries might include + // "repo" or "path" but the set of params ultimately depends on + // the chosen resolver. + // +optional + Resource []ResolverParam `json:"resource,omitempty"` +} + +// ResolverParam is a single parameter passed to a resolver. +type ResolverParam struct { + // Name is the name of the parameter that will be passed to the + // resolver. + Name string + // Value is the string value of the parameter that will be + // passed to the resolver. + Value string +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resource_paths.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resource_paths.go new file mode 100644 index 0000000000..260a2ed5f9 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resource_paths.go @@ -0,0 +1,40 @@ +/* + Copyright 2019 The Tekton Authors + + 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. +*/ + +package v1beta1 + +import "path/filepath" + +// InputResourcePath returns the path where the given input resource +// will get mounted in a Pod +func InputResourcePath(r ResourceDeclaration) string { + return path("/workspace", r) +} + +// OutputResourcePath returns the path to the output resource in a Pod +func OutputResourcePath(r ResourceDeclaration) string { + return path("/workspace/output", r) +} + +func path(root string, r ResourceDeclaration) string { + if r.TargetPath != "" { + if filepath.IsAbs(r.TargetPath) { + return r.TargetPath + } + return filepath.Join("/workspace", r.TargetPath) + } + return filepath.Join(root, r.Name) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resource_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resource_types.go new file mode 100644 index 0000000000..9b33e4823b --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resource_types.go @@ -0,0 +1,281 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "encoding/json" + "fmt" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/go-multierror" + resource "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" + v1 "k8s.io/api/core/v1" +) + +// PipelineResourceType represents the type of endpoint the pipelineResource is, so that the +// controller will know this pipelineResource should be fetched and optionally what +// additional metatdata should be provided for it. +type PipelineResourceType = resource.PipelineResourceType + +var ( + // AllowedOutputResources are the resource types that can be used as outputs + AllowedOutputResources = resource.AllowedOutputResources +) + +const ( + // PipelineResourceTypeGit indicates that this source is a GitHub repo. + PipelineResourceTypeGit PipelineResourceType = resource.PipelineResourceTypeGit + + // PipelineResourceTypeStorage indicates that this source is a storage blob resource. + PipelineResourceTypeStorage PipelineResourceType = resource.PipelineResourceTypeStorage + + // PipelineResourceTypeImage indicates that this source is a docker Image. + PipelineResourceTypeImage PipelineResourceType = resource.PipelineResourceTypeImage + + // PipelineResourceTypeCluster indicates that this source is a k8s cluster Image. + PipelineResourceTypeCluster PipelineResourceType = resource.PipelineResourceTypeCluster + + // PipelineResourceTypePullRequest indicates that this source is a SCM Pull Request. + PipelineResourceTypePullRequest PipelineResourceType = resource.PipelineResourceTypePullRequest + + // PipelineResourceTypeCloudEvent indicates that this source is a cloud event URI + PipelineResourceTypeCloudEvent PipelineResourceType = resource.PipelineResourceTypeCloudEvent +) + +// AllResourceTypes can be used for validation to check if a provided Resource type is one of the known types. +var AllResourceTypes = resource.AllResourceTypes + +// TaskResources allows a Pipeline to declare how its DeclaredPipelineResources +// should be provided to a Task as its inputs and outputs. +type TaskResources struct { + // Inputs holds the mapping from the PipelineResources declared in + // DeclaredPipelineResources to the input PipelineResources required by the Task. + Inputs []TaskResource `json:"inputs,omitempty"` + // Outputs holds the mapping from the PipelineResources declared in + // DeclaredPipelineResources to the input PipelineResources required by the Task. + Outputs []TaskResource `json:"outputs,omitempty"` +} + +// TaskResource defines an input or output Resource declared as a requirement +// by a Task. The Name field will be used to refer to these Resources within +// the Task definition, and when provided as an Input, the Name will be the +// path to the volume mounted containing this Resource as an input (e.g. +// an input Resource named `workspace` will be mounted at `/workspace`). +type TaskResource struct { + ResourceDeclaration `json:",inline"` +} + +// TaskRunResources allows a TaskRun to declare inputs and outputs TaskResourceBinding +type TaskRunResources struct { + // Inputs holds the inputs resources this task was invoked with + Inputs []TaskResourceBinding `json:"inputs,omitempty"` + // Outputs holds the inputs resources this task was invoked with + Outputs []TaskResourceBinding `json:"outputs,omitempty"` +} + +// TaskResourceBinding points to the PipelineResource that +// will be used for the Task input or output called Name. +type TaskResourceBinding struct { + PipelineResourceBinding `json:",inline"` + // Paths will probably be removed in #1284, and then PipelineResourceBinding can be used instead. + // The optional Path field corresponds to a path on disk at which the Resource can be found + // (used when providing the resource via mounted volume, overriding the default logic to fetch the Resource). + // +optional + Paths []string `json:"paths,omitempty"` +} + +// ResourceDeclaration defines an input or output PipelineResource declared as a requirement +// by another type such as a Task or Condition. The Name field will be used to refer to these +// PipelineResources within the type's definition, and when provided as an Input, the Name will be the +// path to the volume mounted containing this PipelineResource as an input (e.g. +// an input Resource named `workspace` will be mounted at `/workspace`). +type ResourceDeclaration = resource.ResourceDeclaration + +// PipelineResourceBinding connects a reference to an instance of a PipelineResource +// with a PipelineResource dependency that the Pipeline has declared +type PipelineResourceBinding struct { + // Name is the name of the PipelineResource in the Pipeline's declaration + Name string `json:"name,omitempty"` + // ResourceRef is a reference to the instance of the actual PipelineResource + // that should be used + // +optional + ResourceRef *PipelineResourceRef `json:"resourceRef,omitempty"` + + // ResourceSpec is specification of a resource that should be created and + // consumed by the task + // +optional + ResourceSpec *resource.PipelineResourceSpec `json:"resourceSpec,omitempty"` +} + +// PipelineResourceResult used to export the image name and digest as json +type PipelineResourceResult struct { + Key string `json:"key"` + Value string `json:"value"` + ResourceName string `json:"resourceName,omitempty"` + // The field ResourceRef should be deprecated and removed in the next API version. + // See https://github.com/tektoncd/pipeline/issues/2694 for more information. + ResourceRef *PipelineResourceRef `json:"resourceRef,omitempty"` + ResultType ResultType `json:"type,omitempty"` +} + +// ResultType used to find out whether a PipelineResourceResult is from a task result or not +type ResultType int + +// UnmarshalJSON unmarshals either an int or a string into a ResultType. String +// ResultTypes were removed because they made JSON messages bigger, which in +// turn limited the amount of space in termination messages for task results. String +// support is maintained for backwards compatibility - the Pipelines controller could +// be stopped midway through TaskRun execution, updated with support for int in place +// of string, and then fail the running TaskRun because it doesn't know how to interpret +// the string value that the TaskRun's entrypoint will emit when it completes. +func (r *ResultType) UnmarshalJSON(data []byte) error { + + var asInt int + var intErr error + + if err := json.Unmarshal(data, &asInt); err != nil { + intErr = err + } else { + *r = ResultType(asInt) + return nil + } + + var asString string + + if err := json.Unmarshal(data, &asString); err != nil { + return fmt.Errorf("unsupported value type, neither int nor string: %v", multierror.Append(intErr, err).ErrorOrNil()) + } + + switch asString { + case "TaskRunResult": + *r = TaskRunResultType + case "PipelineResourceResult": + *r = PipelineResourceResultType + case "InternalTektonResult": + *r = InternalTektonResultType + default: + *r = UnknownResultType + } + + return nil +} + +// PipelineResourceRef can be used to refer to a specific instance of a Resource +type PipelineResourceRef struct { + // Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names + Name string `json:"name,omitempty"` + // API version of the referent + // +optional + APIVersion string `json:"apiVersion,omitempty"` +} + +// PipelineResourceInterface interface to be implemented by different PipelineResource types +type PipelineResourceInterface interface { + // GetName returns the name of this PipelineResource instance. + GetName() string + // GetType returns the type of this PipelineResource (often a super type, e.g. in the case of storage). + GetType() PipelineResourceType + // Replacements returns all the attributes that this PipelineResource has that + // can be used for variable replacement. + Replacements() map[string]string + // GetOutputTaskModifier returns the TaskModifier instance that should be used on a Task + // in order to add this kind of resource when it is being used as an output. + GetOutputTaskModifier(ts *TaskSpec, path string) (TaskModifier, error) + // GetInputTaskModifier returns the TaskModifier instance that should be used on a Task + // in order to add this kind of resource when it is being used as an input. + GetInputTaskModifier(ts *TaskSpec, path string) (TaskModifier, error) +} + +// TaskModifier is an interface to be implemented by different PipelineResources +type TaskModifier interface { + GetStepsToPrepend() []Step + GetStepsToAppend() []Step + GetVolumes() []v1.Volume +} + +// InternalTaskModifier implements TaskModifier for resources that are built-in to Tekton Pipelines. +type InternalTaskModifier struct { + StepsToPrepend []Step + StepsToAppend []Step + Volumes []v1.Volume +} + +// GetStepsToPrepend returns a set of Steps to prepend to the Task. +func (tm *InternalTaskModifier) GetStepsToPrepend() []Step { + return tm.StepsToPrepend +} + +// GetStepsToAppend returns a set of Steps to append to the Task. +func (tm *InternalTaskModifier) GetStepsToAppend() []Step { + return tm.StepsToAppend +} + +// GetVolumes returns a set of Volumes to prepend to the Task pod. +func (tm *InternalTaskModifier) GetVolumes() []v1.Volume { + return tm.Volumes +} + +// ApplyTaskModifier applies a modifier to the task by appending and prepending steps and volumes. +// If steps with the same name exist in ts an error will be returned. If identical Volumes have +// been added, they will not be added again. If Volumes with the same name but different contents +// have been added, an error will be returned. +func ApplyTaskModifier(ts *TaskSpec, tm TaskModifier) error { + steps := tm.GetStepsToPrepend() + for _, step := range steps { + if err := checkStepNotAlreadyAdded(step, ts.Steps); err != nil { + return err + } + } + ts.Steps = append(steps, ts.Steps...) + + steps = tm.GetStepsToAppend() + for _, step := range steps { + if err := checkStepNotAlreadyAdded(step, ts.Steps); err != nil { + return err + } + } + ts.Steps = append(ts.Steps, steps...) + + volumes := tm.GetVolumes() + for _, volume := range volumes { + var alreadyAdded bool + for _, v := range ts.Volumes { + if volume.Name == v.Name { + // If a Volume with the same name but different contents has already been added, we can't add both + if d := cmp.Diff(volume, v); d != "" { + return fmt.Errorf("tried to add volume %s already added but with different contents", volume.Name) + } + // If an identical Volume has already been added, don't add it again + alreadyAdded = true + } + } + if !alreadyAdded { + ts.Volumes = append(ts.Volumes, volume) + } + } + + return nil +} + +func checkStepNotAlreadyAdded(s Step, steps []Step) error { + for _, step := range steps { + if s.Name == step.Name { + return fmt.Errorf("Step %s cannot be added again", step.Name) + } + } + return nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resource_types_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resource_types_validation.go new file mode 100644 index 0000000000..2c453f136a --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resource_types_validation.go @@ -0,0 +1,102 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + "strings" + + "k8s.io/apimachinery/pkg/util/sets" + "knative.dev/pkg/apis" +) + +// Validate implements apis.Validatable +func (tr *TaskResources) Validate(ctx context.Context) (errs *apis.FieldError) { + if tr != nil { + errs = errs.Also(validateTaskResources(tr.Inputs).ViaField("inputs")) + errs = errs.Also(validateTaskResources(tr.Outputs).ViaField("outputs")) + } + return errs +} + +func validateTaskResources(resources []TaskResource) (errs *apis.FieldError) { + for idx, resource := range resources { + errs = errs.Also(validateResourceType(resource, fmt.Sprintf("%s.type", resource.Name))).ViaIndex(idx) + } + return errs.Also(checkForDuplicates(resources, "name")) +} + +func checkForDuplicates(resources []TaskResource, path string) *apis.FieldError { + encountered := sets.NewString() + for _, r := range resources { + if encountered.Has(strings.ToLower(r.Name)) { + return apis.ErrMultipleOneOf(path) + } + encountered.Insert(strings.ToLower(r.Name)) + } + return nil +} + +func validateResourceType(r TaskResource, path string) *apis.FieldError { + for _, allowed := range AllResourceTypes { + if r.Type == allowed { + return nil + } + } + return apis.ErrInvalidValue(r.Type, path) +} + +// Validate implements apis.Validatable +func (tr *TaskRunResources) Validate(ctx context.Context) *apis.FieldError { + if tr == nil { + return nil + } + if err := validateTaskRunResources(ctx, tr.Inputs, "spec.resources.inputs.name"); err != nil { + return err + } + return validateTaskRunResources(ctx, tr.Outputs, "spec.resources.outputs.name") +} + +// validateTaskRunResources validates that +// 1. resource is not declared more than once +// 2. if both resource reference and resource spec is defined at the same time +// 3. at least resource ref or resource spec is defined +func validateTaskRunResources(ctx context.Context, resources []TaskResourceBinding, path string) *apis.FieldError { + encountered := sets.NewString() + for _, r := range resources { + // We should provide only one binding for each resource required by the Task. + name := strings.ToLower(r.Name) + if encountered.Has(strings.ToLower(name)) { + return apis.ErrMultipleOneOf(path) + } + encountered.Insert(name) + // Check that both resource ref and resource Spec are not present + if r.ResourceRef != nil && r.ResourceSpec != nil { + return apis.ErrDisallowedFields(fmt.Sprintf("%s.resourceRef", path), fmt.Sprintf("%s.resourceSpec", path)) + } + // Check that one of resource ref and resource Spec is present + if (r.ResourceRef == nil || r.ResourceRef.Name == "") && r.ResourceSpec == nil { + return apis.ErrMissingField(fmt.Sprintf("%s.resourceRef", path), fmt.Sprintf("%s.resourceSpec", path)) + } + if r.ResourceSpec != nil && r.ResourceSpec.Validate(ctx) != nil { + return r.ResourceSpec.Validate(ctx) + } + + } + return nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resultref.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resultref.go new file mode 100644 index 0000000000..20e9c11382 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/resultref.go @@ -0,0 +1,155 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "fmt" + "regexp" + "strings" +) + +// ResultRef is a type that represents a reference to a task run result +type ResultRef struct { + PipelineTask string + Result string +} + +const ( + resultExpressionFormat = "tasks..results." + // ResultTaskPart Constant used to define the "tasks" part of a pipeline result reference + ResultTaskPart = "tasks" + // ResultResultPart Constant used to define the "results" part of a pipeline result reference + ResultResultPart = "results" + // TODO(#2462) use one regex across all substitutions + variableSubstitutionFormat = `\$\([_a-zA-Z0-9.-]+(\.[_a-zA-Z0-9.-]+)*\)` + // ResultNameFormat Constant used to define the the regex Result.Name should follow + ResultNameFormat = `^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$` +) + +var variableSubstitutionRegex = regexp.MustCompile(variableSubstitutionFormat) +var resultNameFormatRegex = regexp.MustCompile(ResultNameFormat) + +// NewResultRefs extracts all ResultReferences from a param or a pipeline result. +// If the ResultReference can be extracted, they are returned. Expressions which are not +// results are ignored. +func NewResultRefs(expressions []string) []*ResultRef { + var resultRefs []*ResultRef + for _, expression := range expressions { + pipelineTask, result, err := parseExpression(expression) + // If the expression isn't a result but is some other expression, + // parseExpression will return an error, in which case we just skip that expression, + // since although it's not a result ref, it might be some other kind of reference + if err == nil { + resultRefs = append(resultRefs, &ResultRef{ + PipelineTask: pipelineTask, + Result: result, + }) + } + } + return resultRefs +} + +// LooksLikeContainsResultRefs attempts to check if param or a pipeline result looks like it contains any +// result references. +// This is useful if we want to make sure the param looks like a ResultReference before +// performing strict validation +func LooksLikeContainsResultRefs(expressions []string) bool { + for _, expression := range expressions { + if looksLikeResultRef(expression) { + return true + } + } + return false +} + +// looksLikeResultRef attempts to check if the given string looks like it contains any +// result references. Returns true if it does, false otherwise +func looksLikeResultRef(expression string) bool { + return strings.HasPrefix(expression, "task") && strings.Contains(expression, ".result") +} + +// GetVarSubstitutionExpressionsForParam extracts all the value between "$(" and ")"" for a parameter +func GetVarSubstitutionExpressionsForParam(param Param) ([]string, bool) { + var allExpressions []string + switch param.Value.Type { + case ParamTypeArray: + // array type + for _, value := range param.Value.ArrayVal { + allExpressions = append(allExpressions, validateString(value)...) + } + case ParamTypeString: + // string type + allExpressions = append(allExpressions, validateString(param.Value.StringVal)...) + default: + return nil, false + } + return allExpressions, len(allExpressions) != 0 +} + +// GetVarSubstitutionExpressionsForPipelineResult extracts all the value between "$(" and ")"" for a pipeline result +func GetVarSubstitutionExpressionsForPipelineResult(result PipelineResult) ([]string, bool) { + allExpressions := validateString(result.Value) + return allExpressions, len(allExpressions) != 0 +} + +func validateString(value string) []string { + expressions := variableSubstitutionRegex.FindAllString(value, -1) + if expressions == nil { + return nil + } + var result []string + for _, expression := range expressions { + result = append(result, stripVarSubExpression(expression)) + } + return result +} + +func stripVarSubExpression(expression string) string { + return strings.TrimSuffix(strings.TrimPrefix(expression, "$("), ")") +} + +func parseExpression(substitutionExpression string) (string, string, error) { + subExpressions := strings.Split(substitutionExpression, ".") + if len(subExpressions) != 4 || subExpressions[0] != ResultTaskPart || subExpressions[2] != ResultResultPart { + return "", "", fmt.Errorf("Must be of the form %q", resultExpressionFormat) + } + return subExpressions[1], subExpressions[3], nil +} + +// PipelineTaskResultRefs walks all the places a result reference can be used +// in a PipelineTask and returns a list of any references that are found. +func PipelineTaskResultRefs(pt *PipelineTask) []*ResultRef { + refs := []*ResultRef{} + for _, condition := range pt.Conditions { + for _, p := range condition.Params { + expressions, _ := GetVarSubstitutionExpressionsForParam(p) + refs = append(refs, NewResultRefs(expressions)...) + } + } + + for _, p := range pt.Params { + expressions, _ := GetVarSubstitutionExpressionsForParam(p) + refs = append(refs, NewResultRefs(expressions)...) + } + + for _, whenExpression := range pt.WhenExpressions { + expressions, _ := whenExpression.GetVarSubstitutionExpressions() + refs = append(refs, NewResultRefs(expressions)...) + } + + return refs +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/sidecar_replacements.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/sidecar_replacements.go new file mode 100644 index 0000000000..bf41f46338 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/sidecar_replacements.go @@ -0,0 +1,27 @@ +/* + Copyright 2020 The Tekton Authors + + 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. +*/ + +package v1beta1 + +import ( + "github.com/tektoncd/pipeline/pkg/substitution" +) + +// ApplySidecarReplacements applies variable interpolation on a Sidecar. +func ApplySidecarReplacements(sidecar *Sidecar, stringReplacements map[string]string, arrayReplacements map[string][]string) { + sidecar.Script = substitution.ApplyReplacements(sidecar.Script, stringReplacements) + applyContainerReplacements(&sidecar.Container, stringReplacements, arrayReplacements) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/step_replacements.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/step_replacements.go new file mode 100644 index 0000000000..66ef5e3a7f --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/step_replacements.go @@ -0,0 +1,27 @@ +/* + Copyright 2019 The Tekton Authors + + 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. +*/ + +package v1beta1 + +import ( + "github.com/tektoncd/pipeline/pkg/substitution" +) + +// ApplyStepReplacements applies variable interpolation on a Step. +func ApplyStepReplacements(step *Step, stringReplacements map[string]string, arrayReplacements map[string][]string) { + step.Script = substitution.ApplyReplacements(step.Script, stringReplacements) + applyContainerReplacements(&step.Container, stringReplacements, arrayReplacements) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/swagger.json b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/swagger.json new file mode 100644 index 0000000000..c4bedc35f5 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/swagger.json @@ -0,0 +1,2757 @@ +{ + "swagger": "2.0", + "info": { + "description": "Tekton Pipeline", + "title": "Tekton", + "version": "v0.17.2" + }, + "paths": {}, + "definitions": { + "pod.Template": { + "description": "PodTemplate holds pod specific configuration", + "type": "object", + "properties": { + "affinity": { + "description": "If specified, the pod's scheduling constraints", + "$ref": "#/definitions/v1.Affinity" + }, + "automountServiceAccountToken": { + "description": "AutomountServiceAccountToken indicates whether pods running as this service account should have an API token automatically mounted.", + "type": "boolean" + }, + "dnsConfig": { + "description": "Specifies the DNS parameters of a pod. Parameters specified here will be merged to the generated DNS configuration based on DNSPolicy.", + "$ref": "#/definitions/v1.PodDNSConfig" + }, + "dnsPolicy": { + "description": "Set DNS policy for the pod. Defaults to \"ClusterFirst\". Valid values are 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy.", + "type": "string" + }, + "enableServiceLinks": { + "description": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.", + "type": "boolean" + }, + "hostAliases": { + "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.HostAlias" + } + }, + "hostNetwork": { + "description": "HostNetwork specifies whether the pod may use the node network namespace", + "type": "boolean" + }, + "imagePullSecrets": { + "description": "ImagePullSecrets gives the name of the secret used by the pod to pull the image if specified", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.LocalObjectReference" + } + }, + "nodeSelector": { + "description": "NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/", + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "priorityClassName": { + "description": "If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default.", + "type": "string" + }, + "runtimeClassName": { + "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md This is a beta feature as of Kubernetes v1.14.", + "type": "string" + }, + "schedulerName": { + "description": "SchedulerName specifies the scheduler to be used to dispatch the Pod", + "type": "string" + }, + "securityContext": { + "description": "SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field.", + "$ref": "#/definitions/v1.PodSecurityContext" + }, + "tolerations": { + "description": "If specified, the pod's tolerations.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.Toleration" + } + }, + "volumes": { + "description": "List of volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.Volume" + }, + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge,retainKeys" + } + } + }, + "v1alpha1.PipelineResource": { + "description": "PipelineResource describes a resource that is an input to or output from a Task.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ObjectMeta" + }, + "spec": { + "description": "Spec holds the desired state of the PipelineResource from the client", + "default": {}, + "$ref": "#/definitions/v1alpha1.PipelineResourceSpec" + }, + "status": { + "description": "Status is deprecated. It usually is used to communicate the observed state of the PipelineResource from the controller, but was unused as there is no controller for PipelineResource.", + "$ref": "#/definitions/v1alpha1.PipelineResourceStatus" + } + } + }, + "v1alpha1.PipelineResourceList": { + "description": "PipelineResourceList contains a list of PipelineResources", + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1alpha1.PipelineResource" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ListMeta" + } + } + }, + "v1alpha1.PipelineResourceSpec": { + "description": "PipelineResourceSpec defines an individual resources used in the pipeline.", + "type": "object", + "required": [ + "type", + "params" + ], + "properties": { + "description": { + "description": "Description is a user-facing description of the resource that may be used to populate a UI.", + "type": "string" + }, + "params": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1alpha1.ResourceParam" + } + }, + "secrets": { + "description": "Secrets to fetch to populate some of resource fields", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1alpha1.SecretParam" + } + }, + "type": { + "type": "string", + "default": "" + } + } + }, + "v1alpha1.PipelineResourceStatus": { + "description": "PipelineResourceStatus does not contain anything because PipelineResources on their own do not have a status Deprecated", + "type": "object" + }, + "v1alpha1.ResourceDeclaration": { + "description": "ResourceDeclaration defines an input or output PipelineResource declared as a requirement by another type such as a Task or Condition. The Name field will be used to refer to these PipelineResources within the type's definition, and when provided as an Input, the Name will be the path to the volume mounted containing this PipelineResource as an input (e.g. an input Resource named `workspace` will be mounted at `/workspace`).", + "type": "object", + "required": [ + "name", + "type" + ], + "properties": { + "description": { + "description": "Description is a user-facing description of the declared resource that may be used to populate a UI.", + "type": "string" + }, + "name": { + "description": "Name declares the name by which a resource is referenced in the definition. Resources may be referenced by name in the definition of a Task's steps.", + "type": "string", + "default": "" + }, + "optional": { + "description": "Optional declares the resource as optional. By default optional is set to false which makes a resource required. optional: true - the resource is considered optional optional: false - the resource is considered required (equivalent of not specifying it)", + "type": "boolean" + }, + "targetPath": { + "description": "TargetPath is the path in workspace directory where the resource will be copied.", + "type": "string" + }, + "type": { + "description": "Type is the type of this resource;", + "type": "string", + "default": "" + } + } + }, + "v1alpha1.ResourceParam": { + "description": "ResourceParam declares a string value to use for the parameter called Name, and is used in the specific context of PipelineResources.", + "type": "object", + "required": [ + "name", + "value" + ], + "properties": { + "name": { + "type": "string", + "default": "" + }, + "value": { + "type": "string", + "default": "" + } + } + }, + "v1alpha1.SecretParam": { + "description": "SecretParam indicates which secret can be used to populate a field of the resource", + "type": "object", + "required": [ + "fieldName", + "secretKey", + "secretName" + ], + "properties": { + "fieldName": { + "type": "string", + "default": "" + }, + "secretKey": { + "type": "string", + "default": "" + }, + "secretName": { + "type": "string", + "default": "" + } + } + }, + "v1beta1.ArrayOrString": { + "description": "ArrayOrString is a type that can hold a single string or string array. Used in JSON unmarshalling so that a single JSON field can accept either an individual string or an array of strings.", + "type": "object", + "required": [ + "type", + "stringVal", + "arrayVal" + ], + "properties": { + "arrayVal": { + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "stringVal": { + "description": "Represents the stored type of ArrayOrString.", + "type": "string", + "default": "" + }, + "type": { + "type": "string", + "default": "" + } + } + }, + "v1beta1.CloudEventDelivery": { + "description": "CloudEventDelivery is the target of a cloud event along with the state of delivery.", + "type": "object", + "properties": { + "status": { + "default": {}, + "$ref": "#/definitions/v1beta1.CloudEventDeliveryState" + }, + "target": { + "description": "Target points to an addressable", + "type": "string" + } + } + }, + "v1beta1.CloudEventDeliveryState": { + "description": "CloudEventDeliveryState reports the state of a cloud event to be sent.", + "type": "object", + "required": [ + "message", + "retryCount" + ], + "properties": { + "condition": { + "description": "Current status", + "type": "string" + }, + "message": { + "description": "Error is the text of error (if any)", + "type": "string", + "default": "" + }, + "retryCount": { + "description": "RetryCount is the number of attempts of sending the cloud event", + "type": "integer", + "format": "int32", + "default": 0 + }, + "sentAt": { + "description": "SentAt is the time at which the last attempt to send the event was made", + "$ref": "#/definitions/v1.Time" + } + } + }, + "v1beta1.ClusterTask": { + "description": "ClusterTask is a Task with a cluster scope. ClusterTasks are used to represent Tasks that should be publicly addressable from any namespace in the cluster.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ObjectMeta" + }, + "spec": { + "description": "Spec holds the desired state of the Task from the client", + "default": {}, + "$ref": "#/definitions/v1beta1.TaskSpec" + } + } + }, + "v1beta1.ClusterTaskList": { + "description": "ClusterTaskList contains a list of ClusterTask", + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.ClusterTask" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ListMeta" + } + } + }, + "v1beta1.ConditionCheck": { + "description": "ConditionCheck represents a single evaluation of a Condition step.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ObjectMeta" + }, + "spec": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunSpec" + }, + "status": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunStatus" + } + } + }, + "v1beta1.ConditionCheckStatus": { + "description": "ConditionCheckStatus defines the observed state of ConditionCheck", + "type": "object", + "required": [ + "podName" + ], + "properties": { + "annotations": { + "description": "Annotations is additional Status fields for the Resource to save some additional State as well as convey more information to the user. This is roughly akin to Annotations on any k8s resource, just the reconciler conveying richer information outwards.", + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "check": { + "description": "Check describes the state of the check container.", + "default": {}, + "$ref": "#/definitions/v1.ContainerState" + }, + "completionTime": { + "description": "CompletionTime is the time the check pod completed.", + "$ref": "#/definitions/v1.Time" + }, + "conditions": { + "description": "Conditions the latest available observations of a resource's current state.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/knative.Condition" + }, + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "observedGeneration": { + "description": "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", + "type": "integer", + "format": "int64" + }, + "podName": { + "description": "PodName is the name of the pod responsible for executing this condition check.", + "type": "string", + "default": "" + }, + "startTime": { + "description": "StartTime is the time the check is actually started.", + "$ref": "#/definitions/v1.Time" + } + } + }, + "v1beta1.ConditionCheckStatusFields": { + "description": "ConditionCheckStatusFields holds the fields of ConfigurationCheck's status. This is defined separately and inlined so that other types can readily consume these fields via duck typing.", + "type": "object", + "required": [ + "podName" + ], + "properties": { + "check": { + "description": "Check describes the state of the check container.", + "default": {}, + "$ref": "#/definitions/v1.ContainerState" + }, + "completionTime": { + "description": "CompletionTime is the time the check pod completed.", + "$ref": "#/definitions/v1.Time" + }, + "podName": { + "description": "PodName is the name of the pod responsible for executing this condition check.", + "type": "string", + "default": "" + }, + "startTime": { + "description": "StartTime is the time the check is actually started.", + "$ref": "#/definitions/v1.Time" + } + } + }, + "v1beta1.EmbeddedTask": { + "description": "EmbeddedTask is used to define a Task inline within a Pipeline's PipelineTasks.", + "type": "object", + "properties": { + "apiVersion": { + "type": "string" + }, + "description": { + "description": "Description is a user-facing description of the task that may be used to populate a UI.", + "type": "string" + }, + "kind": { + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineTaskMetadata" + }, + "params": { + "description": "Params is a list of input parameters required to run the task. Params must be supplied as inputs in TaskRuns unless they declare a default value.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.ParamSpec" + } + }, + "resources": { + "description": "Resources is a list input and output resource to run the task Resources are represented in TaskRuns as bindings to instances of PipelineResources.", + "$ref": "#/definitions/v1beta1.TaskResources" + }, + "results": { + "description": "Results are values that this Task can output", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskResult" + } + }, + "sidecars": { + "description": "Sidecars are run alongside the Task's step containers. They begin before the steps start and end after the steps complete.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Sidecar" + } + }, + "spec": { + "description": "Spec is a specification of a custom task", + "default": {}, + "$ref": "#/definitions/k8s.io.apimachinery.pkg.runtime.RawExtension" + }, + "stepTemplate": { + "description": "StepTemplate can be used as the basis for all step containers within the Task, so that the steps inherit settings on the base container.", + "$ref": "#/definitions/v1.Container" + }, + "steps": { + "description": "Steps are the steps of the build; each step is run sequentially with the source mounted into /workspace.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Step" + } + }, + "volumes": { + "description": "Volumes is a collection of volumes that are available to mount into the steps of the build.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.Volume" + } + }, + "workspaces": { + "description": "Workspaces are the volumes that this Task requires.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WorkspaceDeclaration" + } + } + } + }, + "v1beta1.InternalTaskModifier": { + "description": "InternalTaskModifier implements TaskModifier for resources that are built-in to Tekton Pipelines.", + "type": "object", + "required": [ + "StepsToPrepend", + "StepsToAppend", + "Volumes" + ], + "properties": { + "StepsToAppend": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Step" + } + }, + "StepsToPrepend": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Step" + } + }, + "Volumes": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.Volume" + } + } + } + }, + "v1beta1.Param": { + "description": "Param declares an ArrayOrString to use for the parameter called name.", + "type": "object", + "required": [ + "name", + "value" + ], + "properties": { + "name": { + "type": "string", + "default": "" + }, + "value": { + "default": {}, + "$ref": "#/definitions/v1beta1.ArrayOrString" + } + } + }, + "v1beta1.ParamSpec": { + "description": "ParamSpec defines arbitrary parameters needed beyond typed inputs (such as resources). Parameter values are provided by users as inputs on a TaskRun or PipelineRun.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "default": { + "description": "Default is the value a parameter takes if no input value is supplied. If default is set, a Task may be executed without a supplied value for the parameter.", + "$ref": "#/definitions/v1beta1.ArrayOrString" + }, + "description": { + "description": "Description is a user-facing description of the parameter that may be used to populate a UI.", + "type": "string" + }, + "name": { + "description": "Name declares the name by which a parameter is referenced.", + "type": "string", + "default": "" + }, + "type": { + "description": "Type is the user-specified type of the parameter. The possible types are currently \"string\" and \"array\", and \"string\" is the default.", + "type": "string" + } + } + }, + "v1beta1.Pipeline": { + "description": "Pipeline describes a list of Tasks to execute. It expresses how outputs of tasks feed into inputs of subsequent tasks.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ObjectMeta" + }, + "spec": { + "description": "Spec holds the desired state of the Pipeline from the client", + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineSpec" + } + } + }, + "v1beta1.PipelineDeclaredResource": { + "description": "PipelineDeclaredResource is used by a Pipeline to declare the types of the PipelineResources that it will required to run and names which can be used to refer to these PipelineResources in PipelineTaskResourceBindings.", + "type": "object", + "required": [ + "name", + "type" + ], + "properties": { + "name": { + "description": "Name is the name that will be used by the Pipeline to refer to this resource. It does not directly correspond to the name of any PipelineResources Task inputs or outputs, and it does not correspond to the actual names of the PipelineResources that will be bound in the PipelineRun.", + "type": "string", + "default": "" + }, + "optional": { + "description": "Optional declares the resource as optional. optional: true - the resource is considered optional optional: false - the resource is considered required (default/equivalent of not specifying it)", + "type": "boolean" + }, + "type": { + "description": "Type is the type of the PipelineResource.", + "type": "string", + "default": "" + } + } + }, + "v1beta1.PipelineList": { + "description": "PipelineList contains a list of Pipeline", + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Pipeline" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ListMeta" + } + } + }, + "v1beta1.PipelineRef": { + "description": "PipelineRef can be used to refer to a specific instance of a Pipeline. Copied from CrossVersionObjectReference: https://github.com/kubernetes/kubernetes/blob/169df7434155cbbc22f1532cba8e0a9588e29ad8/pkg/apis/autoscaling/types.go#L64", + "type": "object", + "properties": { + "apiVersion": { + "description": "API version of the referent", + "type": "string" + }, + "bundle": { + "description": "Bundle url reference to a Tekton Bundle.", + "type": "string" + }, + "name": { + "description": "Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names", + "type": "string" + } + } + }, + "v1beta1.PipelineResourceBinding": { + "description": "PipelineResourceBinding connects a reference to an instance of a PipelineResource with a PipelineResource dependency that the Pipeline has declared", + "type": "object", + "properties": { + "name": { + "description": "Name is the name of the PipelineResource in the Pipeline's declaration", + "type": "string" + }, + "resourceRef": { + "description": "ResourceRef is a reference to the instance of the actual PipelineResource that should be used", + "$ref": "#/definitions/v1beta1.PipelineResourceRef" + }, + "resourceSpec": { + "description": "ResourceSpec is specification of a resource that should be created and consumed by the task", + "$ref": "#/definitions/v1alpha1.PipelineResourceSpec" + } + } + }, + "v1beta1.PipelineResourceRef": { + "description": "PipelineResourceRef can be used to refer to a specific instance of a Resource", + "type": "object", + "properties": { + "apiVersion": { + "description": "API version of the referent", + "type": "string" + }, + "name": { + "description": "Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names", + "type": "string" + } + } + }, + "v1beta1.PipelineResourceResult": { + "description": "PipelineResourceResult used to export the image name and digest as json", + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string", + "default": "" + }, + "resourceName": { + "type": "string" + }, + "resourceRef": { + "description": "The field ResourceRef should be deprecated and removed in the next API version. See https://github.com/tektoncd/pipeline/issues/2694 for more information.", + "$ref": "#/definitions/v1beta1.PipelineResourceRef" + }, + "type": { + "type": "integer", + "format": "int32" + }, + "value": { + "type": "string", + "default": "" + } + } + }, + "v1beta1.PipelineResult": { + "description": "PipelineResult used to describe the results of a pipeline", + "type": "object", + "required": [ + "name", + "value" + ], + "properties": { + "description": { + "description": "Description is a human-readable description of the result", + "type": "string", + "default": "" + }, + "name": { + "description": "Name the given name", + "type": "string", + "default": "" + }, + "value": { + "description": "Value the expression used to retrieve the value", + "type": "string", + "default": "" + } + } + }, + "v1beta1.PipelineRun": { + "description": "PipelineRun represents a single execution of a Pipeline. PipelineRuns are how the graph of Tasks declared in a Pipeline are executed; they specify inputs to Pipelines such as parameter values and capture operational aspects of the Tasks execution such as service account and tolerations. Creating a PipelineRun creates TaskRuns for Tasks in the referenced Pipeline.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ObjectMeta" + }, + "spec": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineRunSpec" + }, + "status": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineRunStatus" + } + } + }, + "v1beta1.PipelineRunConditionCheckStatus": { + "description": "PipelineRunConditionCheckStatus returns the condition check status", + "type": "object", + "properties": { + "conditionName": { + "description": "ConditionName is the name of the Condition", + "type": "string" + }, + "status": { + "description": "Status is the ConditionCheckStatus for the corresponding ConditionCheck", + "$ref": "#/definitions/v1beta1.ConditionCheckStatus" + } + } + }, + "v1beta1.PipelineRunList": { + "description": "PipelineRunList contains a list of PipelineRun", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineRun" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ListMeta" + } + } + }, + "v1beta1.PipelineRunResult": { + "description": "PipelineRunResult used to describe the results of a pipeline", + "type": "object", + "required": [ + "name", + "value" + ], + "properties": { + "name": { + "description": "Name is the result's name as declared by the Pipeline", + "type": "string", + "default": "" + }, + "value": { + "description": "Value is the result returned from the execution of this PipelineRun", + "type": "string", + "default": "" + } + } + }, + "v1beta1.PipelineRunRunStatus": { + "description": "PipelineRunRunStatus contains the name of the PipelineTask for this Run and the Run's Status", + "type": "object", + "properties": { + "pipelineTaskName": { + "description": "PipelineTaskName is the name of the PipelineTask.", + "type": "string" + }, + "status": { + "description": "Status is the RunStatus for the corresponding Run", + "$ref": "#/definitions/github.com.tektoncd.pipeline.pkg.apis.run.v1alpha1.RunStatus" + }, + "whenExpressions": { + "description": "WhenExpressions is the list of checks guarding the execution of the PipelineTask", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WhenExpression" + } + } + } + }, + "v1beta1.PipelineRunSpec": { + "description": "PipelineRunSpec defines the desired state of PipelineRun", + "type": "object", + "properties": { + "params": { + "description": "Params is a list of parameter names and values.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Param" + } + }, + "pipelineRef": { + "$ref": "#/definitions/v1beta1.PipelineRef" + }, + "pipelineSpec": { + "$ref": "#/definitions/v1beta1.PipelineSpec" + }, + "podTemplate": { + "description": "PodTemplate holds pod specific configuration", + "$ref": "#/definitions/pod.Template" + }, + "resources": { + "description": "Resources is a list of bindings specifying which actual instances of PipelineResources to use for the resources the Pipeline has declared it needs.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineResourceBinding" + } + }, + "serviceAccountName": { + "type": "string" + }, + "serviceAccountNames": { + "description": "Deprecated: use taskRunSpecs.ServiceAccountName instead", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineRunSpecServiceAccountName" + } + }, + "status": { + "description": "Used for cancelling a pipelinerun (and maybe more later on)", + "type": "string" + }, + "taskRunSpecs": { + "description": "TaskRunSpecs holds a set of runtime specs", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineTaskRunSpec" + } + }, + "timeout": { + "description": "Time after which the Pipeline times out. Defaults to never. Refer to Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration", + "$ref": "#/definitions/v1.Duration" + }, + "timeouts": { + "description": "This is an alpha field. You must set the \"enable-api-fields\" feature flag to \"alpha\" for this field to be supported.\n\nTime after which the Pipeline times out. Currently three keys are accepted in the map pipeline, tasks and finally with Timeouts.pipeline \u003e= Timeouts.tasks + Timeouts.finally", + "$ref": "#/definitions/v1beta1.TimeoutFields" + }, + "workspaces": { + "description": "Workspaces holds a set of workspace bindings that must match names with those declared in the pipeline.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WorkspaceBinding" + } + } + } + }, + "v1beta1.PipelineRunSpecServiceAccountName": { + "description": "PipelineRunSpecServiceAccountName can be used to configure specific ServiceAccountName for a concrete Task", + "type": "object", + "properties": { + "serviceAccountName": { + "type": "string" + }, + "taskName": { + "type": "string" + } + } + }, + "v1beta1.PipelineRunStatus": { + "description": "PipelineRunStatus defines the observed state of PipelineRun", + "type": "object", + "properties": { + "annotations": { + "description": "Annotations is additional Status fields for the Resource to save some additional State as well as convey more information to the user. This is roughly akin to Annotations on any k8s resource, just the reconciler conveying richer information outwards.", + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "completionTime": { + "description": "CompletionTime is the time the PipelineRun completed.", + "$ref": "#/definitions/v1.Time" + }, + "conditions": { + "description": "Conditions the latest available observations of a resource's current state.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/knative.Condition" + }, + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "observedGeneration": { + "description": "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", + "type": "integer", + "format": "int64" + }, + "pipelineResults": { + "description": "PipelineResults are the list of results written out by the pipeline task's containers", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineRunResult" + } + }, + "pipelineSpec": { + "description": "PipelineRunSpec contains the exact spec used to instantiate the run", + "$ref": "#/definitions/v1beta1.PipelineSpec" + }, + "runs": { + "description": "map of PipelineRunRunStatus with the run name as the key", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v1beta1.PipelineRunRunStatus" + } + }, + "skippedTasks": { + "description": "list of tasks that were skipped due to when expressions evaluating to false", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.SkippedTask" + } + }, + "startTime": { + "description": "StartTime is the time the PipelineRun is actually started.", + "$ref": "#/definitions/v1.Time" + }, + "taskRuns": { + "description": "map of PipelineRunTaskRunStatus with the taskRun name as the key", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v1beta1.PipelineRunTaskRunStatus" + } + } + } + }, + "v1beta1.PipelineRunStatusFields": { + "description": "PipelineRunStatusFields holds the fields of PipelineRunStatus' status. This is defined separately and inlined so that other types can readily consume these fields via duck typing.", + "type": "object", + "properties": { + "completionTime": { + "description": "CompletionTime is the time the PipelineRun completed.", + "$ref": "#/definitions/v1.Time" + }, + "pipelineResults": { + "description": "PipelineResults are the list of results written out by the pipeline task's containers", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineRunResult" + } + }, + "pipelineSpec": { + "description": "PipelineRunSpec contains the exact spec used to instantiate the run", + "$ref": "#/definitions/v1beta1.PipelineSpec" + }, + "runs": { + "description": "map of PipelineRunRunStatus with the run name as the key", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v1beta1.PipelineRunRunStatus" + } + }, + "skippedTasks": { + "description": "list of tasks that were skipped due to when expressions evaluating to false", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.SkippedTask" + } + }, + "startTime": { + "description": "StartTime is the time the PipelineRun is actually started.", + "$ref": "#/definitions/v1.Time" + }, + "taskRuns": { + "description": "map of PipelineRunTaskRunStatus with the taskRun name as the key", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v1beta1.PipelineRunTaskRunStatus" + } + } + } + }, + "v1beta1.PipelineRunTaskRunStatus": { + "description": "PipelineRunTaskRunStatus contains the name of the PipelineTask for this TaskRun and the TaskRun's Status", + "type": "object", + "properties": { + "conditionChecks": { + "description": "ConditionChecks maps the name of a condition check to its Status", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v1beta1.PipelineRunConditionCheckStatus" + } + }, + "pipelineTaskName": { + "description": "PipelineTaskName is the name of the PipelineTask.", + "type": "string" + }, + "status": { + "description": "Status is the TaskRunStatus for the corresponding TaskRun", + "$ref": "#/definitions/v1beta1.TaskRunStatus" + }, + "whenExpressions": { + "description": "WhenExpressions is the list of checks guarding the execution of the PipelineTask", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WhenExpression" + } + } + } + }, + "v1beta1.PipelineSpec": { + "description": "PipelineSpec defines the desired state of Pipeline.", + "type": "object", + "properties": { + "description": { + "description": "Description is a user-facing description of the pipeline that may be used to populate a UI.", + "type": "string" + }, + "finally": { + "description": "Finally declares the list of Tasks that execute just before leaving the Pipeline i.e. either after all Tasks are finished executing successfully or after a failure which would result in ending the Pipeline", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineTask" + } + }, + "params": { + "description": "Params declares a list of input parameters that must be supplied when this Pipeline is run.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.ParamSpec" + } + }, + "resources": { + "description": "Resources declares the names and types of the resources given to the Pipeline's tasks as inputs and outputs.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineDeclaredResource" + } + }, + "results": { + "description": "Results are values that this pipeline can output once run", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineResult" + } + }, + "tasks": { + "description": "Tasks declares the graph of Tasks that execute when this Pipeline is run.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineTask" + } + }, + "workspaces": { + "description": "Workspaces declares a set of named workspaces that are expected to be provided by a PipelineRun.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineWorkspaceDeclaration" + } + } + } + }, + "v1beta1.PipelineTask": { + "description": "PipelineTask defines a task in a Pipeline, passing inputs from both Params and from the output of previous tasks.", + "type": "object", + "properties": { + "conditions": { + "description": "Conditions is a list of conditions that need to be true for the task to run Conditions are deprecated, use WhenExpressions instead", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineTaskCondition" + } + }, + "matrix": { + "description": "Matrix declares parameters used to fan out this task.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Param" + } + }, + "name": { + "description": "Name is the name of this task within the context of a Pipeline. Name is used as a coordinate with the `from` and `runAfter` fields to establish the execution order of tasks relative to one another.", + "type": "string" + }, + "params": { + "description": "Parameters declares parameters passed to this task.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Param" + } + }, + "resources": { + "description": "Resources declares the resources given to this task as inputs and outputs.", + "$ref": "#/definitions/v1beta1.PipelineTaskResources" + }, + "retries": { + "description": "Retries represents how many times this task should be retried in case of task failure: ConditionSucceeded set to False", + "type": "integer", + "format": "int32" + }, + "runAfter": { + "description": "RunAfter is the list of PipelineTask names that should be executed before this Task executes. (Used to force a specific ordering in graph execution.)", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "taskRef": { + "description": "TaskRef is a reference to a task definition.", + "$ref": "#/definitions/v1beta1.TaskRef" + }, + "taskSpec": { + "description": "TaskSpec is a specification of a task", + "$ref": "#/definitions/v1beta1.EmbeddedTask" + }, + "timeout": { + "description": "Time after which the TaskRun times out. Defaults to 1 hour. Specified TaskRun timeout should be less than 24h. Refer Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration", + "$ref": "#/definitions/v1.Duration" + }, + "when": { + "description": "WhenExpressions is a list of when expressions that need to be true for the task to run", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WhenExpression" + } + }, + "workspaces": { + "description": "Workspaces maps workspaces from the pipeline spec to the workspaces declared in the Task.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WorkspacePipelineTaskBinding" + } + } + } + }, + "v1beta1.PipelineTaskCondition": { + "description": "PipelineTaskCondition allows a PipelineTask to declare a Condition to be evaluated before the Task is run.", + "type": "object", + "required": [ + "conditionRef" + ], + "properties": { + "conditionRef": { + "description": "ConditionRef is the name of the Condition to use for the conditionCheck", + "type": "string", + "default": "" + }, + "params": { + "description": "Params declare parameters passed to this Condition", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Param" + } + }, + "resources": { + "description": "Resources declare the resources provided to this Condition as input", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineTaskInputResource" + } + } + } + }, + "v1beta1.PipelineTaskInputResource": { + "description": "PipelineTaskInputResource maps the name of a declared PipelineResource input dependency in a Task to the resource in the Pipeline's DeclaredPipelineResources that should be used. This input may come from a previous task.", + "type": "object", + "required": [ + "name", + "resource" + ], + "properties": { + "from": { + "description": "From is the list of PipelineTask names that the resource has to come from. (Implies an ordering in the execution graph.)", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "name": { + "description": "Name is the name of the PipelineResource as declared by the Task.", + "type": "string", + "default": "" + }, + "resource": { + "description": "Resource is the name of the DeclaredPipelineResource to use.", + "type": "string", + "default": "" + } + } + }, + "v1beta1.PipelineTaskMetadata": { + "description": "PipelineTaskMetadata contains the labels or annotations for an EmbeddedTask", + "type": "object", + "properties": { + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + } + } + }, + "v1beta1.PipelineTaskOutputResource": { + "description": "PipelineTaskOutputResource maps the name of a declared PipelineResource output dependency in a Task to the resource in the Pipeline's DeclaredPipelineResources that should be used.", + "type": "object", + "required": [ + "name", + "resource" + ], + "properties": { + "name": { + "description": "Name is the name of the PipelineResource as declared by the Task.", + "type": "string", + "default": "" + }, + "resource": { + "description": "Resource is the name of the DeclaredPipelineResource to use.", + "type": "string", + "default": "" + } + } + }, + "v1beta1.PipelineTaskParam": { + "description": "PipelineTaskParam is used to provide arbitrary string parameters to a Task.", + "type": "object", + "required": [ + "name", + "value" + ], + "properties": { + "name": { + "type": "string", + "default": "" + }, + "value": { + "type": "string", + "default": "" + } + } + }, + "v1beta1.PipelineTaskResources": { + "description": "PipelineTaskResources allows a Pipeline to declare how its DeclaredPipelineResources should be provided to a Task as its inputs and outputs.", + "type": "object", + "properties": { + "inputs": { + "description": "Inputs holds the mapping from the PipelineResources declared in DeclaredPipelineResources to the input PipelineResources required by the Task.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineTaskInputResource" + } + }, + "outputs": { + "description": "Outputs holds the mapping from the PipelineResources declared in DeclaredPipelineResources to the input PipelineResources required by the Task.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineTaskOutputResource" + } + } + } + }, + "v1beta1.PipelineTaskRun": { + "description": "PipelineTaskRun reports the results of running a step in the Task. Each task has the potential to succeed or fail (based on the exit code) and produces logs.", + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "v1beta1.PipelineTaskRunSpec": { + "description": "PipelineTaskRunSpec can be used to configure specific specs for a concrete Task", + "type": "object", + "properties": { + "pipelineTaskName": { + "type": "string" + }, + "sidecarOverrides": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunSidecarOverride" + } + }, + "stepOverrides": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunStepOverride" + } + }, + "taskPodTemplate": { + "$ref": "#/definitions/pod.Template" + }, + "taskServiceAccountName": { + "type": "string" + } + } + }, + "v1beta1.PipelineWorkspaceDeclaration": { + "description": "WorkspacePipelineDeclaration creates a named slot in a Pipeline that a PipelineRun is expected to populate with a workspace binding. Deprecated: use PipelineWorkspaceDeclaration type instead", + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "Description is a human readable string describing how the workspace will be used in the Pipeline. It can be useful to include a bit of detail about which tasks are intended to have access to the data on the workspace.", + "type": "string" + }, + "name": { + "description": "Name is the name of a workspace to be provided by a PipelineRun.", + "type": "string", + "default": "" + }, + "optional": { + "description": "Optional marks a Workspace as not being required in PipelineRuns. By default this field is false and so declared workspaces are required.", + "type": "boolean" + } + } + }, + "v1beta1.ResolverParam": { + "description": "ResolverParam is a single parameter passed to a resolver.", + "type": "object", + "required": [ + "Name", + "Value" + ], + "properties": { + "Name": { + "description": "Name is the name of the parameter that will be passed to the resolver.", + "type": "string", + "default": "" + }, + "Value": { + "description": "Value is the string value of the parameter that will be passed to the resolver.", + "type": "string", + "default": "" + } + } + }, + "v1beta1.ResolverRef": { + "description": "ResolverRef can be used to refer to a Pipeline or Task in a remote location like a git repo. This feature is in alpha and these fields are only available when the alpha feature gate is enabled.", + "type": "object", + "properties": { + "resolver": { + "description": "Resolver is the name of the resolver that should perform resolution of the referenced Tekton resource, such as \"git\".", + "type": "string" + }, + "resource": { + "description": "Resource contains the parameters used to identify the referenced Tekton resource. Example entries might include \"repo\" or \"path\" but the set of params ultimately depends on the chosen resolver.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.ResolverParam" + } + } + } + }, + "v1beta1.ResultRef": { + "description": "ResultRef is a type that represents a reference to a task run result", + "type": "object", + "required": [ + "PipelineTask", + "Result" + ], + "properties": { + "PipelineTask": { + "type": "string", + "default": "" + }, + "Result": { + "type": "string", + "default": "" + } + } + }, + "v1beta1.Sidecar": { + "description": "Sidecar has nearly the same data structure as Step, consisting of a Container and an optional Script, but does not have the ability to timeout.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "args": { + "description": "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "command": { + "description": "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "env": { + "description": "List of environment variables to set in the container. Cannot be updated.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.EnvVar" + }, + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "envFrom": { + "description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.EnvFromSource" + } + }, + "image": { + "description": "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.", + "type": "string" + }, + "imagePullPolicy": { + "description": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", + "type": "string" + }, + "lifecycle": { + "description": "Actions that the management system should take in response to container lifecycle events. Cannot be updated.", + "$ref": "#/definitions/v1.Lifecycle" + }, + "livenessProbe": { + "description": "Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "$ref": "#/definitions/v1.Probe" + }, + "name": { + "description": "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.", + "type": "string", + "default": "" + }, + "ports": { + "description": "List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Cannot be updated.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.ContainerPort" + }, + "x-kubernetes-list-map-keys": [ + "containerPort", + "protocol" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "containerPort", + "x-kubernetes-patch-strategy": "merge" + }, + "readinessProbe": { + "description": "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "$ref": "#/definitions/v1.Probe" + }, + "resources": { + "description": "Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/", + "default": {}, + "$ref": "#/definitions/v1.ResourceRequirements" + }, + "script": { + "description": "Script is the contents of an executable file to execute.\n\nIf Script is not empty, the Step cannot have an Command or Args.", + "type": "string" + }, + "securityContext": { + "description": "SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/", + "$ref": "#/definitions/v1.SecurityContext" + }, + "startupProbe": { + "description": "StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "$ref": "#/definitions/v1.Probe" + }, + "stdin": { + "description": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", + "type": "boolean" + }, + "stdinOnce": { + "description": "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", + "type": "boolean" + }, + "terminationMessagePath": { + "description": "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", + "type": "string" + }, + "terminationMessagePolicy": { + "description": "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", + "type": "string" + }, + "tty": { + "description": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", + "type": "boolean" + }, + "volumeDevices": { + "description": "volumeDevices is the list of block devices to be used by the container.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.VolumeDevice" + }, + "x-kubernetes-patch-merge-key": "devicePath", + "x-kubernetes-patch-strategy": "merge" + }, + "volumeMounts": { + "description": "Pod volumes to mount into the container's filesystem. Cannot be updated.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.VolumeMount" + }, + "x-kubernetes-patch-merge-key": "mountPath", + "x-kubernetes-patch-strategy": "merge" + }, + "workingDir": { + "description": "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", + "type": "string" + }, + "workspaces": { + "description": "This is an alpha field. You must set the \"enable-api-fields\" feature flag to \"alpha\" for this field to be supported.\n\nWorkspaces is a list of workspaces from the Task that this Sidecar wants exclusive access to. Adding a workspace to this list means that any other Step or Sidecar that does not also request this Workspace will not have access to it.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WorkspaceUsage" + } + } + } + }, + "v1beta1.SidecarState": { + "description": "SidecarState reports the results of running a sidecar in a Task.", + "type": "object", + "properties": { + "container": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "name": { + "type": "string" + }, + "running": { + "description": "Details about a running container", + "$ref": "#/definitions/v1.ContainerStateRunning" + }, + "terminated": { + "description": "Details about a terminated container", + "$ref": "#/definitions/v1.ContainerStateTerminated" + }, + "waiting": { + "description": "Details about a waiting container", + "$ref": "#/definitions/v1.ContainerStateWaiting" + } + } + }, + "v1beta1.SkippedTask": { + "description": "SkippedTask is used to describe the Tasks that were skipped due to their When Expressions evaluating to False. This is a struct because we are looking into including more details about the When Expressions that caused this Task to be skipped.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "description": "Name is the Pipeline Task name", + "type": "string", + "default": "" + }, + "whenExpressions": { + "description": "WhenExpressions is the list of checks guarding the execution of the PipelineTask", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WhenExpression" + } + } + } + }, + "v1beta1.Step": { + "description": "Step embeds the Container type, which allows it to include fields not provided by Container.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "args": { + "description": "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "command": { + "description": "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "env": { + "description": "List of environment variables to set in the container. Cannot be updated.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.EnvVar" + }, + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "envFrom": { + "description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.EnvFromSource" + } + }, + "image": { + "description": "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.", + "type": "string" + }, + "imagePullPolicy": { + "description": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", + "type": "string" + }, + "lifecycle": { + "description": "Actions that the management system should take in response to container lifecycle events. Cannot be updated.", + "$ref": "#/definitions/v1.Lifecycle" + }, + "livenessProbe": { + "description": "Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "$ref": "#/definitions/v1.Probe" + }, + "name": { + "description": "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.", + "type": "string", + "default": "" + }, + "onError": { + "description": "OnError defines the exiting behavior of a container on error can be set to [ continue | stopAndFail ] stopAndFail indicates exit the taskRun if the container exits with non-zero exit code continue indicates continue executing the rest of the steps irrespective of the container exit code", + "type": "string" + }, + "ports": { + "description": "List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Cannot be updated.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.ContainerPort" + }, + "x-kubernetes-list-map-keys": [ + "containerPort", + "protocol" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "containerPort", + "x-kubernetes-patch-strategy": "merge" + }, + "readinessProbe": { + "description": "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "$ref": "#/definitions/v1.Probe" + }, + "resources": { + "description": "Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/", + "default": {}, + "$ref": "#/definitions/v1.ResourceRequirements" + }, + "script": { + "description": "Script is the contents of an executable file to execute.\n\nIf Script is not empty, the Step cannot have an Command and the Args will be passed to the Script.", + "type": "string" + }, + "securityContext": { + "description": "SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/", + "$ref": "#/definitions/v1.SecurityContext" + }, + "startupProbe": { + "description": "StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "$ref": "#/definitions/v1.Probe" + }, + "stdin": { + "description": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", + "type": "boolean" + }, + "stdinOnce": { + "description": "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", + "type": "boolean" + }, + "terminationMessagePath": { + "description": "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", + "type": "string" + }, + "terminationMessagePolicy": { + "description": "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", + "type": "string" + }, + "timeout": { + "description": "Timeout is the time after which the step times out. Defaults to never. Refer to Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration", + "$ref": "#/definitions/v1.Duration" + }, + "tty": { + "description": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", + "type": "boolean" + }, + "volumeDevices": { + "description": "volumeDevices is the list of block devices to be used by the container.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.VolumeDevice" + }, + "x-kubernetes-patch-merge-key": "devicePath", + "x-kubernetes-patch-strategy": "merge" + }, + "volumeMounts": { + "description": "Pod volumes to mount into the container's filesystem. Cannot be updated.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.VolumeMount" + }, + "x-kubernetes-patch-merge-key": "mountPath", + "x-kubernetes-patch-strategy": "merge" + }, + "workingDir": { + "description": "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", + "type": "string" + }, + "workspaces": { + "description": "This is an alpha field. You must set the \"enable-api-fields\" feature flag to \"alpha\" for this field to be supported.\n\nWorkspaces is a list of workspaces from the Task that this Step wants exclusive access to. Adding a workspace to this list means that any other Step or Sidecar that does not also request this Workspace will not have access to it.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WorkspaceUsage" + } + } + } + }, + "v1beta1.StepState": { + "description": "StepState reports the results of running a step in a Task.", + "type": "object", + "properties": { + "container": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "name": { + "type": "string" + }, + "running": { + "description": "Details about a running container", + "$ref": "#/definitions/v1.ContainerStateRunning" + }, + "terminated": { + "description": "Details about a terminated container", + "$ref": "#/definitions/v1.ContainerStateTerminated" + }, + "waiting": { + "description": "Details about a waiting container", + "$ref": "#/definitions/v1.ContainerStateWaiting" + } + } + }, + "v1beta1.Task": { + "description": "Task represents a collection of sequential steps that are run as part of a Pipeline using a set of inputs and producing a set of outputs. Tasks execute when TaskRuns are created that provide the input parameters and resources and output resources the Task requires.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ObjectMeta" + }, + "spec": { + "description": "Spec holds the desired state of the Task from the client", + "default": {}, + "$ref": "#/definitions/v1beta1.TaskSpec" + } + } + }, + "v1beta1.TaskList": { + "description": "TaskList contains a list of Task", + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Task" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ListMeta" + } + } + }, + "v1beta1.TaskRef": { + "description": "TaskRef can be used to refer to a specific instance of a task. Copied from CrossVersionObjectReference: https://github.com/kubernetes/kubernetes/blob/169df7434155cbbc22f1532cba8e0a9588e29ad8/pkg/apis/autoscaling/types.go#L64", + "type": "object", + "properties": { + "apiVersion": { + "description": "API version of the referent", + "type": "string" + }, + "bundle": { + "description": "Bundle url reference to a Tekton Bundle.", + "type": "string" + }, + "kind": { + "description": "TaskKind indicates the kind of the task, namespaced or cluster scoped.", + "type": "string" + }, + "name": { + "description": "Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names", + "type": "string" + } + } + }, + "v1beta1.TaskResource": { + "description": "TaskResource defines an input or output Resource declared as a requirement by a Task. The Name field will be used to refer to these Resources within the Task definition, and when provided as an Input, the Name will be the path to the volume mounted containing this Resource as an input (e.g. an input Resource named `workspace` will be mounted at `/workspace`).", + "type": "object", + "required": [ + "name", + "type" + ], + "properties": { + "description": { + "description": "Description is a user-facing description of the declared resource that may be used to populate a UI.", + "type": "string" + }, + "name": { + "description": "Name declares the name by which a resource is referenced in the definition. Resources may be referenced by name in the definition of a Task's steps.", + "type": "string", + "default": "" + }, + "optional": { + "description": "Optional declares the resource as optional. By default optional is set to false which makes a resource required. optional: true - the resource is considered optional optional: false - the resource is considered required (equivalent of not specifying it)", + "type": "boolean" + }, + "targetPath": { + "description": "TargetPath is the path in workspace directory where the resource will be copied.", + "type": "string" + }, + "type": { + "description": "Type is the type of this resource;", + "type": "string", + "default": "" + } + } + }, + "v1beta1.TaskResourceBinding": { + "description": "TaskResourceBinding points to the PipelineResource that will be used for the Task input or output called Name.", + "type": "object", + "properties": { + "name": { + "description": "Name is the name of the PipelineResource in the Pipeline's declaration", + "type": "string" + }, + "paths": { + "description": "Paths will probably be removed in #1284, and then PipelineResourceBinding can be used instead. The optional Path field corresponds to a path on disk at which the Resource can be found (used when providing the resource via mounted volume, overriding the default logic to fetch the Resource).", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "resourceRef": { + "description": "ResourceRef is a reference to the instance of the actual PipelineResource that should be used", + "$ref": "#/definitions/v1beta1.PipelineResourceRef" + }, + "resourceSpec": { + "description": "ResourceSpec is specification of a resource that should be created and consumed by the task", + "$ref": "#/definitions/v1alpha1.PipelineResourceSpec" + } + } + }, + "v1beta1.TaskResources": { + "description": "TaskResources allows a Pipeline to declare how its DeclaredPipelineResources should be provided to a Task as its inputs and outputs.", + "type": "object", + "properties": { + "inputs": { + "description": "Inputs holds the mapping from the PipelineResources declared in DeclaredPipelineResources to the input PipelineResources required by the Task.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskResource" + } + }, + "outputs": { + "description": "Outputs holds the mapping from the PipelineResources declared in DeclaredPipelineResources to the input PipelineResources required by the Task.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskResource" + } + } + } + }, + "v1beta1.TaskResult": { + "description": "TaskResult used to describe the results of a task", + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "Description is a human-readable description of the result", + "type": "string", + "default": "" + }, + "name": { + "description": "Name the given name", + "type": "string", + "default": "" + } + } + }, + "v1beta1.TaskRun": { + "description": "TaskRun represents a single execution of a Task. TaskRuns are how the steps specified in a Task are executed; they specify the parameters and resources used to run the steps in a Task.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ObjectMeta" + }, + "spec": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunSpec" + }, + "status": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunStatus" + } + } + }, + "v1beta1.TaskRunDebug": { + "description": "TaskRunDebug defines the breakpoint config for a particular TaskRun", + "type": "object", + "properties": { + "breakpoint": { + "type": "array", + "items": { + "type": "string", + "default": "" + } + } + } + }, + "v1beta1.TaskRunInputs": { + "description": "TaskRunInputs holds the input values that this task was invoked with.", + "type": "object", + "properties": { + "params": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Param" + } + }, + "resources": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskResourceBinding" + } + } + } + }, + "v1beta1.TaskRunList": { + "description": "TaskRunList contains a list of TaskRun", + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRun" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ListMeta" + } + } + }, + "v1beta1.TaskRunOutputs": { + "description": "TaskRunOutputs holds the output values that this task was invoked with.", + "type": "object", + "properties": { + "resources": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskResourceBinding" + } + } + } + }, + "v1beta1.TaskRunResources": { + "description": "TaskRunResources allows a TaskRun to declare inputs and outputs TaskResourceBinding", + "type": "object", + "properties": { + "inputs": { + "description": "Inputs holds the inputs resources this task was invoked with", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskResourceBinding" + } + }, + "outputs": { + "description": "Outputs holds the inputs resources this task was invoked with", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskResourceBinding" + } + } + } + }, + "v1beta1.TaskRunResult": { + "description": "TaskRunResult used to describe the results of a task", + "type": "object", + "required": [ + "name", + "value" + ], + "properties": { + "name": { + "description": "Name the given name", + "type": "string", + "default": "" + }, + "value": { + "description": "Value the given value of the result", + "type": "string", + "default": "" + } + } + }, + "v1beta1.TaskRunSidecarOverride": { + "description": "TaskRunSidecarOverride is used to override the values of a Sidecar in the corresponding Task.", + "type": "object", + "required": [ + "Name", + "Resources" + ], + "properties": { + "Name": { + "description": "The name of the Sidecar to override.", + "type": "string", + "default": "" + }, + "Resources": { + "description": "The resource requirements to apply to the Sidecar.", + "default": {}, + "$ref": "#/definitions/v1.ResourceRequirements" + } + } + }, + "v1beta1.TaskRunSpec": { + "description": "TaskRunSpec defines the desired state of TaskRun", + "type": "object", + "properties": { + "debug": { + "$ref": "#/definitions/v1beta1.TaskRunDebug" + }, + "params": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Param" + } + }, + "podTemplate": { + "description": "PodTemplate holds pod specific configuration", + "$ref": "#/definitions/pod.Template" + }, + "resources": { + "$ref": "#/definitions/v1beta1.TaskRunResources" + }, + "serviceAccountName": { + "type": "string", + "default": "" + }, + "sidecarOverrides": { + "description": "Overrides to apply to Sidecars in this TaskRun. If a field is specified in both a Sidecar and a SidecarOverride, the value from the SidecarOverride will be used. This field is only supported when the alpha feature gate is enabled.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunSidecarOverride" + } + }, + "status": { + "description": "Used for cancelling a taskrun (and maybe more later on)", + "type": "string" + }, + "stepOverrides": { + "description": "Overrides to apply to Steps in this TaskRun. If a field is specified in both a Step and a StepOverride, the value from the StepOverride will be used. This field is only supported when the alpha feature gate is enabled.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunStepOverride" + } + }, + "taskRef": { + "description": "no more than one of the TaskRef and TaskSpec may be specified.", + "$ref": "#/definitions/v1beta1.TaskRef" + }, + "taskSpec": { + "$ref": "#/definitions/v1beta1.TaskSpec" + }, + "timeout": { + "description": "Time after which the build times out. Defaults to 1 hour. Specified build timeout should be less than 24h. Refer Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration", + "$ref": "#/definitions/v1.Duration" + }, + "workspaces": { + "description": "Workspaces is a list of WorkspaceBindings from volumes to workspaces.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WorkspaceBinding" + } + } + } + }, + "v1beta1.TaskRunStatus": { + "description": "TaskRunStatus defines the observed state of TaskRun", + "type": "object", + "required": [ + "podName" + ], + "properties": { + "annotations": { + "description": "Annotations is additional Status fields for the Resource to save some additional State as well as convey more information to the user. This is roughly akin to Annotations on any k8s resource, just the reconciler conveying richer information outwards.", + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "cloudEvents": { + "description": "CloudEvents describe the state of each cloud event requested via a CloudEventResource.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.CloudEventDelivery" + } + }, + "completionTime": { + "description": "CompletionTime is the time the build completed.", + "$ref": "#/definitions/v1.Time" + }, + "conditions": { + "description": "Conditions the latest available observations of a resource's current state.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/knative.Condition" + }, + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "observedGeneration": { + "description": "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", + "type": "integer", + "format": "int64" + }, + "podName": { + "description": "PodName is the name of the pod responsible for executing this task's steps.", + "type": "string", + "default": "" + }, + "resourcesResult": { + "description": "Results from Resources built during the taskRun. currently includes the digest of build container images", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineResourceResult" + } + }, + "retriesStatus": { + "description": "RetriesStatus contains the history of TaskRunStatus in case of a retry in order to keep record of failures. All TaskRunStatus stored in RetriesStatus will have no date within the RetriesStatus as is redundant.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunStatus" + } + }, + "sidecars": { + "description": "The list has one entry per sidecar in the manifest. Each entry is represents the imageid of the corresponding sidecar.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.SidecarState" + } + }, + "startTime": { + "description": "StartTime is the time the build is actually started.", + "$ref": "#/definitions/v1.Time" + }, + "steps": { + "description": "Steps describes the state of each build step container.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.StepState" + } + }, + "taskResults": { + "description": "TaskRunResults are the list of results written out by the task's containers", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunResult" + } + }, + "taskSpec": { + "description": "TaskSpec contains the Spec from the dereferenced Task definition used to instantiate this TaskRun.", + "$ref": "#/definitions/v1beta1.TaskSpec" + } + } + }, + "v1beta1.TaskRunStatusFields": { + "description": "TaskRunStatusFields holds the fields of TaskRun's status. This is defined separately and inlined so that other types can readily consume these fields via duck typing.", + "type": "object", + "required": [ + "podName" + ], + "properties": { + "cloudEvents": { + "description": "CloudEvents describe the state of each cloud event requested via a CloudEventResource.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.CloudEventDelivery" + } + }, + "completionTime": { + "description": "CompletionTime is the time the build completed.", + "$ref": "#/definitions/v1.Time" + }, + "podName": { + "description": "PodName is the name of the pod responsible for executing this task's steps.", + "type": "string", + "default": "" + }, + "resourcesResult": { + "description": "Results from Resources built during the taskRun. currently includes the digest of build container images", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineResourceResult" + } + }, + "retriesStatus": { + "description": "RetriesStatus contains the history of TaskRunStatus in case of a retry in order to keep record of failures. All TaskRunStatus stored in RetriesStatus will have no date within the RetriesStatus as is redundant.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunStatus" + } + }, + "sidecars": { + "description": "The list has one entry per sidecar in the manifest. Each entry is represents the imageid of the corresponding sidecar.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.SidecarState" + } + }, + "startTime": { + "description": "StartTime is the time the build is actually started.", + "$ref": "#/definitions/v1.Time" + }, + "steps": { + "description": "Steps describes the state of each build step container.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.StepState" + } + }, + "taskResults": { + "description": "TaskRunResults are the list of results written out by the task's containers", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunResult" + } + }, + "taskSpec": { + "description": "TaskSpec contains the Spec from the dereferenced Task definition used to instantiate this TaskRun.", + "$ref": "#/definitions/v1beta1.TaskSpec" + } + } + }, + "v1beta1.TaskRunStepOverride": { + "description": "TaskRunStepOverride is used to override the values of a Step in the corresponding Task.", + "type": "object", + "required": [ + "Name", + "Resources" + ], + "properties": { + "Name": { + "description": "The name of the Step to override.", + "type": "string", + "default": "" + }, + "Resources": { + "description": "The resource requirements to apply to the Step.", + "default": {}, + "$ref": "#/definitions/v1.ResourceRequirements" + } + } + }, + "v1beta1.TaskSpec": { + "description": "TaskSpec defines the desired state of Task.", + "type": "object", + "properties": { + "description": { + "description": "Description is a user-facing description of the task that may be used to populate a UI.", + "type": "string" + }, + "params": { + "description": "Params is a list of input parameters required to run the task. Params must be supplied as inputs in TaskRuns unless they declare a default value.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.ParamSpec" + } + }, + "resources": { + "description": "Resources is a list input and output resource to run the task Resources are represented in TaskRuns as bindings to instances of PipelineResources.", + "$ref": "#/definitions/v1beta1.TaskResources" + }, + "results": { + "description": "Results are values that this Task can output", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskResult" + } + }, + "sidecars": { + "description": "Sidecars are run alongside the Task's step containers. They begin before the steps start and end after the steps complete.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Sidecar" + } + }, + "stepTemplate": { + "description": "StepTemplate can be used as the basis for all step containers within the Task, so that the steps inherit settings on the base container.", + "$ref": "#/definitions/v1.Container" + }, + "steps": { + "description": "Steps are the steps of the build; each step is run sequentially with the source mounted into /workspace.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Step" + } + }, + "volumes": { + "description": "Volumes is a collection of volumes that are available to mount into the steps of the build.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.Volume" + } + }, + "workspaces": { + "description": "Workspaces are the volumes that this Task requires.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WorkspaceDeclaration" + } + } + } + }, + "v1beta1.TimeoutFields": { + "description": "TimeoutFields allows granular specification of pipeline, task, and finally timeouts", + "type": "object", + "properties": { + "finally": { + "description": "Finally sets the maximum allowed duration of this pipeline's finally", + "$ref": "#/definitions/v1.Duration" + }, + "pipeline": { + "description": "Pipeline sets the maximum allowed duration for execution of the entire pipeline. The sum of individual timeouts for tasks and finally must not exceed this value.", + "$ref": "#/definitions/v1.Duration" + }, + "tasks": { + "description": "Tasks sets the maximum allowed duration of this pipeline's tasks", + "$ref": "#/definitions/v1.Duration" + } + } + }, + "v1beta1.WhenExpression": { + "description": "WhenExpression allows a PipelineTask to declare expressions to be evaluated before the Task is run to determine whether the Task should be executed or skipped", + "type": "object", + "required": [ + "input", + "operator", + "values" + ], + "properties": { + "input": { + "description": "Input is the string for guard checking which can be a static input or an output from a parent Task", + "type": "string", + "default": "" + }, + "operator": { + "description": "Operator that represents an Input's relationship to the values", + "type": "string", + "default": "" + }, + "values": { + "description": "Values is an array of strings, which is compared against the input, for guard checking It must be non-empty", + "type": "array", + "items": { + "type": "string", + "default": "" + } + } + } + }, + "v1beta1.WorkspaceBinding": { + "description": "WorkspaceBinding maps a Task's declared workspace to a Volume.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "configMap": { + "description": "ConfigMap represents a configMap that should populate this workspace.", + "$ref": "#/definitions/v1.ConfigMapVolumeSource" + }, + "emptyDir": { + "description": "EmptyDir represents a temporary directory that shares a Task's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir Either this OR PersistentVolumeClaim can be used.", + "$ref": "#/definitions/v1.EmptyDirVolumeSource" + }, + "name": { + "description": "Name is the name of the workspace populated by the volume.", + "type": "string", + "default": "" + }, + "persistentVolumeClaim": { + "description": "PersistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. Either this OR EmptyDir can be used.", + "$ref": "#/definitions/v1.PersistentVolumeClaimVolumeSource" + }, + "secret": { + "description": "Secret represents a secret that should populate this workspace.", + "$ref": "#/definitions/v1.SecretVolumeSource" + }, + "subPath": { + "description": "SubPath is optionally a directory on the volume which should be used for this binding (i.e. the volume will be mounted at this sub directory).", + "type": "string" + }, + "volumeClaimTemplate": { + "description": "VolumeClaimTemplate is a template for a claim that will be created in the same namespace. The PipelineRun controller is responsible for creating a unique claim for each instance of PipelineRun.", + "$ref": "#/definitions/v1.PersistentVolumeClaim" + } + } + }, + "v1beta1.WorkspaceDeclaration": { + "description": "WorkspaceDeclaration is a declaration of a volume that a Task requires.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "Description is an optional human readable description of this volume.", + "type": "string" + }, + "mountPath": { + "description": "MountPath overrides the directory that the volume will be made available at.", + "type": "string" + }, + "name": { + "description": "Name is the name by which you can bind the volume at runtime.", + "type": "string", + "default": "" + }, + "optional": { + "description": "Optional marks a Workspace as not being required in TaskRuns. By default this field is false and so declared workspaces are required.", + "type": "boolean" + }, + "readOnly": { + "description": "ReadOnly dictates whether a mounted volume is writable. By default this field is false and so mounted volumes are writable.", + "type": "boolean" + } + } + }, + "v1beta1.WorkspacePipelineTaskBinding": { + "description": "WorkspacePipelineTaskBinding describes how a workspace passed into the pipeline should be mapped to a task's declared workspace.", + "type": "object", + "required": [ + "name", + "workspace" + ], + "properties": { + "name": { + "description": "Name is the name of the workspace as declared by the task", + "type": "string", + "default": "" + }, + "subPath": { + "description": "SubPath is optionally a directory on the volume which should be used for this binding (i.e. the volume will be mounted at this sub directory).", + "type": "string" + }, + "workspace": { + "description": "Workspace is the name of the workspace declared by the pipeline", + "type": "string", + "default": "" + } + } + }, + "v1beta1.WorkspaceUsage": { + "description": "WorkspaceUsage is used by a Step or Sidecar to declare that it wants isolated access to a Workspace defined in a Task.", + "type": "object", + "required": [ + "name", + "mountPath" + ], + "properties": { + "mountPath": { + "description": "MountPath is the path that the workspace should be mounted to inside the Step or Sidecar, overriding any MountPath specified in the Task's WorkspaceDeclaration.", + "type": "string", + "default": "" + }, + "name": { + "description": "Name is the name of the workspace this Step or Sidecar wants access to.", + "type": "string", + "default": "" + } + } + } + } +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_conversion.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_conversion.go new file mode 100644 index 0000000000..646a740915 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_conversion.go @@ -0,0 +1,36 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + + "knative.dev/pkg/apis" +) + +var _ apis.Convertible = (*Task)(nil) + +// ConvertTo implements api.Convertible +func (t *Task) ConvertTo(ctx context.Context, sink apis.Convertible) error { + return fmt.Errorf("v1beta1 is the highest known version, got: %T", sink) +} + +// ConvertFrom implements api.Convertible +func (t *Task) ConvertFrom(ctx context.Context, source apis.Convertible) error { + return fmt.Errorf("v1beta1 is the highest know version, got: %T", source) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_defaults.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_defaults.go new file mode 100644 index 0000000000..8703a41ad6 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_defaults.go @@ -0,0 +1,44 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + + "knative.dev/pkg/apis" +) + +var _ apis.Defaultable = (*Task)(nil) + +// SetDefaults implements apis.Defaultable +func (t *Task) SetDefaults(ctx context.Context) { + t.Spec.SetDefaults(ctx) +} + +// SetDefaults set any defaults for the task spec +func (ts *TaskSpec) SetDefaults(ctx context.Context) { + for i := range ts.Params { + ts.Params[i].SetDefaults(ctx) + } +} + +// applyImplicitParams propagates implicit params from the parent context +// through the Task. +func (ts *TaskSpec) applyImplicitParams(ctx context.Context) { + ctx = addContextParamSpec(ctx, ts.Params) + ts.Params = getContextParamSpecs(ctx) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_interface.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_interface.go new file mode 100644 index 0000000000..ac018081be --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_interface.go @@ -0,0 +1,30 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +// TaskObject is implemented by Task and ClusterTask +type TaskObject interface { + apis.Defaultable + TaskMetadata() metav1.ObjectMeta + TaskSpec() TaskSpec + Copy() TaskObject +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_types.go new file mode 100644 index 0000000000..5731a7263f --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_types.go @@ -0,0 +1,227 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "github.com/tektoncd/pipeline/pkg/apis/pipeline" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "knative.dev/pkg/kmeta" +) + +const ( + // TaskRunResultType default task run result value + TaskRunResultType ResultType = 1 + // PipelineResourceResultType default pipeline result value + PipelineResourceResultType = 2 + // InternalTektonResultType default internal tekton result value + InternalTektonResultType = 3 + // UnknownResultType default unknown result type value + UnknownResultType = 10 +) + +// +genclient +// +genclient:noStatus +// +genreconciler:krshapedlogic=false +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Task represents a collection of sequential steps that are run as part of a +// Pipeline using a set of inputs and producing a set of outputs. Tasks execute +// when TaskRuns are created that provide the input parameters and resources and +// output resources the Task requires. +// +// +k8s:openapi-gen=true +type Task struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata"` + + // Spec holds the desired state of the Task from the client + // +optional + Spec TaskSpec `json:"spec"` +} + +var _ kmeta.OwnerRefable = (*Task)(nil) + +// TaskSpec returns the task's spec +func (t *Task) TaskSpec() TaskSpec { + return t.Spec +} + +// TaskMetadata returns the task's ObjectMeta +func (t *Task) TaskMetadata() metav1.ObjectMeta { + return t.ObjectMeta +} + +// Copy returns a deep copy of the task +func (t *Task) Copy() TaskObject { + return t.DeepCopy() +} + +// GetGroupVersionKind implements kmeta.OwnerRefable. +func (*Task) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind(pipeline.TaskControllerName) +} + +// TaskSpec defines the desired state of Task. +type TaskSpec struct { + // Resources is a list input and output resource to run the task + // Resources are represented in TaskRuns as bindings to instances of + // PipelineResources. + // +optional + Resources *TaskResources `json:"resources,omitempty"` + + // Params is a list of input parameters required to run the task. Params + // must be supplied as inputs in TaskRuns unless they declare a default + // value. + // +optional + Params []ParamSpec `json:"params,omitempty"` + + // Description is a user-facing description of the task that may be + // used to populate a UI. + // +optional + Description string `json:"description,omitempty"` + + // Steps are the steps of the build; each step is run sequentially with the + // source mounted into /workspace. + Steps []Step `json:"steps,omitempty"` + + // Volumes is a collection of volumes that are available to mount into the + // steps of the build. + Volumes []corev1.Volume `json:"volumes,omitempty"` + + // StepTemplate can be used as the basis for all step containers within the + // Task, so that the steps inherit settings on the base container. + StepTemplate *corev1.Container `json:"stepTemplate,omitempty"` + + // Sidecars are run alongside the Task's step containers. They begin before + // the steps start and end after the steps complete. + Sidecars []Sidecar `json:"sidecars,omitempty"` + + // Workspaces are the volumes that this Task requires. + Workspaces []WorkspaceDeclaration `json:"workspaces,omitempty"` + + // Results are values that this Task can output + Results []TaskResult `json:"results,omitempty"` +} + +// TaskResult used to describe the results of a task +type TaskResult struct { + // Name the given name + Name string `json:"name"` + + // Description is a human-readable description of the result + // +optional + Description string `json:"description"` +} + +// Step embeds the Container type, which allows it to include fields not +// provided by Container. +type Step struct { + corev1.Container `json:",inline"` + + // Script is the contents of an executable file to execute. + // + // If Script is not empty, the Step cannot have an Command and the Args will be passed to the Script. + // +optional + Script string `json:"script,omitempty"` + + // Timeout is the time after which the step times out. Defaults to never. + // Refer to Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + + // This is an alpha field. You must set the "enable-api-fields" feature flag to "alpha" + // for this field to be supported. + // + // Workspaces is a list of workspaces from the Task that this Step wants + // exclusive access to. Adding a workspace to this list means that any + // other Step or Sidecar that does not also request this Workspace will + // not have access to it. + // +optional + Workspaces []WorkspaceUsage `json:"workspaces,omitempty"` + + // OnError defines the exiting behavior of a container on error + // can be set to [ continue | stopAndFail ] + // stopAndFail indicates exit the taskRun if the container exits with non-zero exit code + // continue indicates continue executing the rest of the steps irrespective of the container exit code + OnError string `json:"onError,omitempty"` +} + +// Sidecar has nearly the same data structure as Step, consisting of a Container and an optional Script, but does not have the ability to timeout. +type Sidecar struct { + corev1.Container `json:",inline"` + + // Script is the contents of an executable file to execute. + // + // If Script is not empty, the Step cannot have an Command or Args. + // +optional + Script string `json:"script,omitempty"` + + // This is an alpha field. You must set the "enable-api-fields" feature flag to "alpha" + // for this field to be supported. + // + // Workspaces is a list of workspaces from the Task that this Sidecar wants + // exclusive access to. Adding a workspace to this list means that any + // other Step or Sidecar that does not also request this Workspace will + // not have access to it. + // +optional + Workspaces []WorkspaceUsage `json:"workspaces,omitempty"` +} + +// TaskList contains a list of Task +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type TaskList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []Task `json:"items"` +} + +// TaskRef can be used to refer to a specific instance of a task. +// Copied from CrossVersionObjectReference: https://github.com/kubernetes/kubernetes/blob/169df7434155cbbc22f1532cba8e0a9588e29ad8/pkg/apis/autoscaling/types.go#L64 +type TaskRef struct { + // Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names + Name string `json:"name,omitempty"` + // TaskKind indicates the kind of the task, namespaced or cluster scoped. + Kind TaskKind `json:"kind,omitempty"` + // API version of the referent + // +optional + APIVersion string `json:"apiVersion,omitempty"` + // Bundle url reference to a Tekton Bundle. + // +optional + Bundle string `json:"bundle,omitempty"` + + // ResolverRef allows referencing a Task in a remote location + // like a git repo. This field is only supported when the alpha + // feature gate is enabled. + // +optional + ResolverRef `json:",omitempty"` +} + +// Check that Pipeline may be validated and defaulted. + +// TaskKind defines the type of Task used by the pipeline. +type TaskKind string + +const ( + // NamespacedTaskKind indicates that the task type has a namespaced scope. + NamespacedTaskKind TaskKind = "Task" + // ClusterTaskKind indicates that task type has a cluster scope. + ClusterTaskKind TaskKind = "ClusterTask" +) diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_validation.go new file mode 100644 index 0000000000..e6baaed04e --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/task_validation.go @@ -0,0 +1,400 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + "path/filepath" + "strings" + "time" + + "github.com/tektoncd/pipeline/pkg/apis/config" + "github.com/tektoncd/pipeline/pkg/apis/validate" + "github.com/tektoncd/pipeline/pkg/substitution" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/validation" + "knative.dev/pkg/apis" +) + +var _ apis.Validatable = (*Task)(nil) + +// Validate implements apis.Validatable +func (t *Task) Validate(ctx context.Context) *apis.FieldError { + errs := validate.ObjectMetadata(t.GetObjectMeta()).ViaField("metadata") + if apis.IsInDelete(ctx) { + return nil + } + return errs.Also(t.Spec.Validate(apis.WithinSpec(ctx)).ViaField("spec")) +} + +// Validate implements apis.Validatable +func (ts *TaskSpec) Validate(ctx context.Context) (errs *apis.FieldError) { + if len(ts.Steps) == 0 { + errs = errs.Also(apis.ErrMissingField("steps")) + } + errs = errs.Also(ValidateVolumes(ts.Volumes).ViaField("volumes")) + errs = errs.Also(validateDeclaredWorkspaces(ts.Workspaces, ts.Steps, ts.StepTemplate).ViaField("workspaces")) + errs = errs.Also(validateWorkspaceUsages(ctx, ts)) + mergedSteps, err := MergeStepsWithStepTemplate(ts.StepTemplate, ts.Steps) + if err != nil { + errs = errs.Also(&apis.FieldError{ + Message: fmt.Sprintf("error merging step template and steps: %s", err), + Paths: []string{"stepTemplate"}, + Details: err.Error(), + }) + } + + errs = errs.Also(validateSteps(ctx, mergedSteps).ViaField("steps")) + errs = errs.Also(ts.Resources.Validate(ctx).ViaField("resources")) + errs = errs.Also(ValidateParameterTypes(ts.Params).ViaField("params")) + errs = errs.Also(ValidateParameterVariables(ts.Steps, ts.Params)) + errs = errs.Also(ValidateResourcesVariables(ts.Steps, ts.Resources)) + errs = errs.Also(validateTaskContextVariables(ts.Steps)) + errs = errs.Also(validateResults(ctx, ts.Results).ViaField("results")) + return errs +} + +func validateResults(ctx context.Context, results []TaskResult) (errs *apis.FieldError) { + for index, result := range results { + errs = errs.Also(result.Validate(ctx).ViaIndex(index)) + } + return errs +} + +// Validate implements apis.Validatable +func (tr TaskResult) Validate(_ context.Context) *apis.FieldError { + if !resultNameFormatRegex.MatchString(tr.Name) { + return apis.ErrInvalidKeyName(tr.Name, "name", fmt.Sprintf("Name must consist of alphanumeric characters, '-', '_', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my-name', or 'my_name', regex used for validation is '%s')", ResultNameFormat)) + } + return nil +} + +// a mount path which conflicts with any other declared workspaces, with the explicitly +// declared volume mounts, or with the stepTemplate. The names must also be unique. +func validateDeclaredWorkspaces(workspaces []WorkspaceDeclaration, steps []Step, stepTemplate *corev1.Container) (errs *apis.FieldError) { + mountPaths := sets.NewString() + for _, step := range steps { + for _, vm := range step.VolumeMounts { + mountPaths.Insert(filepath.Clean(vm.MountPath)) + } + } + if stepTemplate != nil { + for _, vm := range stepTemplate.VolumeMounts { + mountPaths.Insert(filepath.Clean(vm.MountPath)) + } + } + + wsNames := sets.NewString() + for idx, w := range workspaces { + // Workspace names must be unique + if wsNames.Has(w.Name) { + errs = errs.Also(apis.ErrGeneric(fmt.Sprintf("workspace name %q must be unique", w.Name), "name").ViaIndex(idx)) + } else { + wsNames.Insert(w.Name) + } + // Workspaces must not try to use mount paths that are already used + mountPath := filepath.Clean(w.GetMountPath()) + if _, ok := mountPaths[mountPath]; ok { + errs = errs.Also(apis.ErrGeneric(fmt.Sprintf("workspace mount path %q must be unique", mountPath), "mountpath").ViaIndex(idx)) + } + mountPaths[mountPath] = struct{}{} + } + return errs +} + +// validateWorkspaceUsages checks that all WorkspaceUsage objects in Steps +// refer to workspaces that are defined in the Task. +// +// This is an alpha feature and will fail validation if it's used by a step +// or sidecar when the enable-api-fields feature gate is anything but "alpha". +func validateWorkspaceUsages(ctx context.Context, ts *TaskSpec) (errs *apis.FieldError) { + workspaces := ts.Workspaces + steps := ts.Steps + sidecars := ts.Sidecars + + wsNames := sets.NewString() + for _, w := range workspaces { + wsNames.Insert(w.Name) + } + + for stepIdx, step := range steps { + if len(step.Workspaces) != 0 { + errs = errs.Also(ValidateEnabledAPIFields(ctx, "step workspaces", config.AlphaAPIFields).ViaIndex(stepIdx).ViaField("steps")) + } + for workspaceIdx, w := range step.Workspaces { + if !wsNames.Has(w.Name) { + errs = errs.Also(apis.ErrGeneric(fmt.Sprintf("undefined workspace %q", w.Name), "name").ViaIndex(workspaceIdx).ViaField("workspaces").ViaIndex(stepIdx).ViaField("steps")) + } + } + } + + for sidecarIdx, sidecar := range sidecars { + if len(sidecar.Workspaces) != 0 { + errs = errs.Also(ValidateEnabledAPIFields(ctx, "sidecar workspaces", config.AlphaAPIFields).ViaIndex(sidecarIdx).ViaField("sidecars")) + } + for workspaceIdx, w := range sidecar.Workspaces { + if !wsNames.Has(w.Name) { + errs = errs.Also(apis.ErrGeneric(fmt.Sprintf("undefined workspace %q", w.Name), "name").ViaIndex(workspaceIdx).ViaField("workspaces").ViaIndex(sidecarIdx).ViaField("sidecars")) + } + } + } + + return errs +} + +// ValidateVolumes validates a slice of volumes to make sure there are no dupilcate names +func ValidateVolumes(volumes []corev1.Volume) (errs *apis.FieldError) { + // Task must not have duplicate volume names. + vols := sets.NewString() + for idx, v := range volumes { + if vols.Has(v.Name) { + errs = errs.Also(apis.ErrGeneric(fmt.Sprintf("multiple volumes with same name %q", v.Name), "name").ViaIndex(idx)) + } else { + vols.Insert(v.Name) + } + } + return errs +} + +func validateSteps(ctx context.Context, steps []Step) (errs *apis.FieldError) { + // Task must not have duplicate step names. + names := sets.NewString() + for idx, s := range steps { + errs = errs.Also(validateStep(ctx, s, names).ViaIndex(idx)) + } + return errs +} + +func validateStep(ctx context.Context, s Step, names sets.String) (errs *apis.FieldError) { + if s.Image == "" { + errs = errs.Also(apis.ErrMissingField("Image")) + } + + if s.Script != "" { + if len(s.Command) > 0 { + errs = errs.Also(&apis.FieldError{ + Message: fmt.Sprintf("script cannot be used with command"), + Paths: []string{"script"}, + }) + } + } + + if s.Name != "" { + if names.Has(s.Name) { + errs = errs.Also(apis.ErrInvalidValue(s.Name, "name")) + } + if e := validation.IsDNS1123Label(s.Name); len(e) > 0 { + errs = errs.Also(&apis.FieldError{ + Message: fmt.Sprintf("invalid value %q", s.Name), + Paths: []string{"name"}, + Details: "Task step name must be a valid DNS Label, For more info refer to https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + }) + } + names.Insert(s.Name) + } + + if s.Timeout != nil { + if s.Timeout.Duration < time.Duration(0) { + return apis.ErrInvalidValue(s.Timeout.Duration, "negative timeout") + } + } + + for j, vm := range s.VolumeMounts { + if strings.HasPrefix(vm.MountPath, "/tekton/") && + !strings.HasPrefix(vm.MountPath, "/tekton/home") { + errs = errs.Also(apis.ErrGeneric(fmt.Sprintf("volumeMount cannot be mounted under /tekton/ (volumeMount %q mounted at %q)", vm.Name, vm.MountPath), "mountPath").ViaFieldIndex("volumeMounts", j)) + } + if strings.HasPrefix(vm.Name, "tekton-internal-") { + errs = errs.Also(apis.ErrGeneric(fmt.Sprintf(`volumeMount name %q cannot start with "tekton-internal-"`, vm.Name), "name").ViaFieldIndex("volumeMounts", j)) + } + } + + if s.OnError != "" { + if s.OnError != "continue" && s.OnError != "stopAndFail" { + errs = errs.Also(&apis.FieldError{ + Message: fmt.Sprintf("invalid value: %v", s.OnError), + Paths: []string{"onError"}, + Details: "Task step onError must be either continue or stopAndFail", + }) + } + } + + if s.Script != "" { + cleaned := strings.TrimSpace(s.Script) + if strings.HasPrefix(cleaned, "#!win") { + errs = errs.Also(ValidateEnabledAPIFields(ctx, "windows script support", config.AlphaAPIFields).ViaField("script")) + } + } + return errs +} + +// ValidateParameterTypes validates all the types within a slice of ParamSpecs +func ValidateParameterTypes(params []ParamSpec) (errs *apis.FieldError) { + for _, p := range params { + errs = errs.Also(p.ValidateType()) + } + return errs +} + +// ValidateType checks that the type of a ParamSpec is allowed and its default value matches that type +func (p ParamSpec) ValidateType() *apis.FieldError { + // Ensure param has a valid type. + validType := false + for _, allowedType := range AllParamTypes { + if p.Type == allowedType { + validType = true + } + } + if !validType { + return apis.ErrInvalidValue(p.Type, fmt.Sprintf("%s.type", p.Name)) + } + + // If a default value is provided, ensure its type matches param's declared type. + if (p.Default != nil) && (p.Default.Type != p.Type) { + return &apis.FieldError{ + Message: fmt.Sprintf( + "\"%v\" type does not match default value's type: \"%v\"", p.Type, p.Default.Type), + Paths: []string{ + fmt.Sprintf("%s.type", p.Name), + fmt.Sprintf("%s.default.type", p.Name), + }, + } + } + return nil +} + +// ValidateParameterVariables validates all variables within a slice of ParamSpecs against a slice of Steps +func ValidateParameterVariables(steps []Step, params []ParamSpec) *apis.FieldError { + parameterNames := sets.NewString() + arrayParameterNames := sets.NewString() + + for _, p := range params { + parameterNames.Insert(p.Name) + if p.Type == ParamTypeArray { + arrayParameterNames.Insert(p.Name) + } + } + + errs := validateVariables(steps, "params", parameterNames) + return errs.Also(validateArrayUsage(steps, "params", arrayParameterNames)) +} + +func validateTaskContextVariables(steps []Step) *apis.FieldError { + taskRunContextNames := sets.NewString().Insert( + "name", + "namespace", + "uid", + ) + taskContextNames := sets.NewString().Insert( + "name", + "retry-count", + ) + errs := validateVariables(steps, "context\\.taskRun", taskRunContextNames) + return errs.Also(validateVariables(steps, "context\\.task", taskContextNames)) +} + +// ValidateResourcesVariables validates all variables within a TaskResources against a slice of Steps +func ValidateResourcesVariables(steps []Step, resources *TaskResources) *apis.FieldError { + if resources == nil { + return nil + } + resourceNames := sets.NewString() + if resources.Inputs != nil { + for _, r := range resources.Inputs { + resourceNames.Insert(r.Name) + } + } + if resources.Outputs != nil { + for _, r := range resources.Outputs { + resourceNames.Insert(r.Name) + } + } + return validateVariables(steps, "resources.(?:inputs|outputs)", resourceNames) +} + +func validateArrayUsage(steps []Step, prefix string, vars sets.String) (errs *apis.FieldError) { + for idx, step := range steps { + errs = errs.Also(validateStepArrayUsage(step, prefix, vars)).ViaFieldIndex("steps", idx) + } + return errs +} + +func validateStepArrayUsage(step Step, prefix string, vars sets.String) *apis.FieldError { + errs := validateTaskNoArrayReferenced(step.Name, prefix, vars).ViaField("name") + errs = errs.Also(validateTaskNoArrayReferenced(step.Image, prefix, vars).ViaField("image")) + errs = errs.Also(validateTaskNoArrayReferenced(step.WorkingDir, prefix, vars).ViaField("workingDir")) + errs = errs.Also(validateTaskNoArrayReferenced(step.Script, prefix, vars).ViaField("script")) + for i, cmd := range step.Command { + errs = errs.Also(validateTaskArraysIsolated(cmd, prefix, vars).ViaFieldIndex("command", i)) + } + for i, arg := range step.Args { + errs = errs.Also(validateTaskArraysIsolated(arg, prefix, vars).ViaFieldIndex("args", i)) + + } + for _, env := range step.Env { + errs = errs.Also(validateTaskNoArrayReferenced(env.Value, prefix, vars).ViaFieldKey("env", env.Name)) + } + for i, v := range step.VolumeMounts { + errs = errs.Also(validateTaskNoArrayReferenced(v.Name, prefix, vars).ViaField("name").ViaFieldIndex("volumeMount", i)) + errs = errs.Also(validateTaskNoArrayReferenced(v.MountPath, prefix, vars).ViaField("mountPath").ViaFieldIndex("volumeMount", i)) + errs = errs.Also(validateTaskNoArrayReferenced(v.SubPath, prefix, vars).ViaField("subPath").ViaFieldIndex("volumeMount", i)) + } + return errs +} + +func validateVariables(steps []Step, prefix string, vars sets.String) (errs *apis.FieldError) { + for idx, step := range steps { + errs = errs.Also(validateStepVariables(step, prefix, vars).ViaFieldIndex("steps", idx)) + } + return errs +} + +func validateStepVariables(step Step, prefix string, vars sets.String) *apis.FieldError { + errs := validateTaskVariable(step.Name, prefix, vars).ViaField("name") + errs = errs.Also(validateTaskVariable(step.Image, prefix, vars).ViaField("image")) + errs = errs.Also(validateTaskVariable(step.WorkingDir, prefix, vars).ViaField("workingDir")) + errs = errs.Also(validateTaskVariable(step.Script, prefix, vars).ViaField("script")) + for i, cmd := range step.Command { + errs = errs.Also(validateTaskVariable(cmd, prefix, vars).ViaFieldIndex("command", i)) + } + for i, arg := range step.Args { + errs = errs.Also(validateTaskVariable(arg, prefix, vars).ViaFieldIndex("args", i)) + } + for _, env := range step.Env { + errs = errs.Also(validateTaskVariable(env.Value, prefix, vars).ViaFieldKey("env", env.Name)) + } + for i, v := range step.VolumeMounts { + errs = errs.Also(validateTaskVariable(v.Name, prefix, vars).ViaField("name").ViaFieldIndex("volumeMount", i)) + errs = errs.Also(validateTaskVariable(v.MountPath, prefix, vars).ViaField("MountPath").ViaFieldIndex("volumeMount", i)) + errs = errs.Also(validateTaskVariable(v.SubPath, prefix, vars).ViaField("SubPath").ViaFieldIndex("volumeMount", i)) + } + return errs +} + +func validateTaskVariable(value, prefix string, vars sets.String) *apis.FieldError { + return substitution.ValidateVariableP(value, prefix, vars) +} + +func validateTaskNoArrayReferenced(value, prefix string, arrayNames sets.String) *apis.FieldError { + return substitution.ValidateVariableProhibitedP(value, prefix, arrayNames) +} + +func validateTaskArraysIsolated(value, prefix string, arrayNames sets.String) *apis.FieldError { + return substitution.ValidateVariableIsolatedP(value, prefix, arrayNames) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskref_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskref_validation.go new file mode 100644 index 0000000000..bb26215ae7 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskref_validation.go @@ -0,0 +1,88 @@ +/* +Copyright 2022 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + + "github.com/google/go-containerregistry/pkg/name" + "github.com/tektoncd/pipeline/pkg/apis/config" + "knative.dev/pkg/apis" +) + +// Validate ensures that a supplied TaskRef field is populated +// correctly. No errors are returned for a nil TaskRef. +func (ref *TaskRef) Validate(ctx context.Context) (errs *apis.FieldError) { + cfg := config.FromContextOrDefaults(ctx) + if ref == nil { + return + } + if cfg.FeatureFlags.EnableAPIFields == config.AlphaAPIFields { + errs = errs.Also(ref.validateAlphaRef(ctx)) + } else { + errs = errs.Also(ref.validateInTreeRef(ctx)) + } + return +} + +// validateInTreeRef returns errors if the given taskRef is not valid for +// Pipelines' built-in resolution machinery. +func (ref *TaskRef) validateInTreeRef(ctx context.Context) (errs *apis.FieldError) { + cfg := config.FromContextOrDefaults(ctx) + if ref.Resolver != "" { + errs = errs.Also(apis.ErrDisallowedFields("resolver")) + } + if ref.Resource != nil { + errs = errs.Also(apis.ErrDisallowedFields("resource")) + } + if ref.Name == "" { + errs = errs.Also(apis.ErrMissingField("name")) + } + if cfg.FeatureFlags.EnableTektonOCIBundles { + if ref.Bundle != "" && ref.Name == "" { + errs = errs.Also(apis.ErrMissingField("name")) + } + if ref.Bundle != "" { + if _, err := name.ParseReference(ref.Bundle); err != nil { + errs = errs.Also(apis.ErrInvalidValue("invalid bundle reference", "bundle", err.Error())) + } + } + } else if ref.Bundle != "" { + errs = errs.Also(apis.ErrDisallowedFields("bundle")) + } + return +} + +// validateAlphaRef ensures that the user has passed either a +// valid remote resource reference or a valid in-tree resource reference, +// but not both. +func (ref *TaskRef) validateAlphaRef(ctx context.Context) (errs *apis.FieldError) { + switch { + case ref.Resolver == "" && ref.Resource != nil: + errs = errs.Also(apis.ErrMissingField("resolver")) + case ref.Resolver == "": + errs = errs.Also(ref.validateInTreeRef(ctx)) + default: + if ref.Name != "" { + errs = errs.Also(apis.ErrMultipleOneOf("name", "resolver")) + } + if ref.Bundle != "" { + errs = errs.Also(apis.ErrMultipleOneOf("bundle", "resolver")) + } + } + return +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskrun_conversion.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskrun_conversion.go new file mode 100644 index 0000000000..654ac4ffc5 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskrun_conversion.go @@ -0,0 +1,36 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + + "knative.dev/pkg/apis" +) + +var _ apis.Convertible = (*TaskRun)(nil) + +// ConvertTo implements api.Convertible +func (tr *TaskRun) ConvertTo(ctx context.Context, sink apis.Convertible) error { + return fmt.Errorf("v1beta1 is the highest known version, got: %T", sink) +} + +// ConvertFrom implements api.Convertible +func (tr *TaskRun) ConvertFrom(ctx context.Context, source apis.Convertible) error { + return fmt.Errorf("v1beta1 is the highest know version, got: %T", source) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskrun_defaults.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskrun_defaults.go new file mode 100644 index 0000000000..28ed8f2eba --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskrun_defaults.go @@ -0,0 +1,135 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "time" + + "github.com/tektoncd/pipeline/pkg/apis/config" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +var _ apis.Defaultable = (*TaskRun)(nil) + +// ManagedByLabelKey is the label key used to mark what is managing this resource +const ManagedByLabelKey = "app.kubernetes.io/managed-by" + +// SetDefaults implements apis.Defaultable +func (tr *TaskRun) SetDefaults(ctx context.Context) { + ctx = apis.WithinParent(ctx, tr.ObjectMeta) + tr.Spec.SetDefaults(ctx) + + // If the TaskRun doesn't have a managed-by label, apply the default + // specified in the config. + cfg := config.FromContextOrDefaults(ctx) + if tr.ObjectMeta.Labels == nil { + tr.ObjectMeta.Labels = map[string]string{} + } + if _, found := tr.ObjectMeta.Labels[ManagedByLabelKey]; !found { + tr.ObjectMeta.Labels[ManagedByLabelKey] = cfg.Defaults.DefaultManagedByLabelValue + } +} + +// SetDefaults implements apis.Defaultable +func (trs *TaskRunSpec) SetDefaults(ctx context.Context) { + cfg := config.FromContextOrDefaults(ctx) + if trs.TaskRef != nil && trs.TaskRef.Kind == "" { + trs.TaskRef.Kind = NamespacedTaskKind + } + + if trs.Timeout == nil { + trs.Timeout = &metav1.Duration{Duration: time.Duration(cfg.Defaults.DefaultTimeoutMinutes) * time.Minute} + } + + defaultSA := cfg.Defaults.DefaultServiceAccount + if trs.ServiceAccountName == "" && defaultSA != "" { + trs.ServiceAccountName = defaultSA + } + + defaultPodTemplate := cfg.Defaults.DefaultPodTemplate + trs.PodTemplate = mergePodTemplateWithDefault(trs.PodTemplate, defaultPodTemplate) + + // If this taskrun has an embedded task, apply the usual task defaults + if trs.TaskSpec != nil { + trs.TaskSpec.SetDefaults(ctx) + + if config.FromContextOrDefaults(ctx).FeatureFlags.EnableAPIFields == "alpha" { + trs.TaskSpec.applyImplicitParams(addContextParams(ctx, trs.Params)) + } + } +} + +func mergePodTemplateWithDefault(tpl, defaultTpl *PodTemplate) *PodTemplate { + switch { + case defaultTpl == nil: + // No configured default, just return the template + return tpl + case tpl == nil: + // No template, just return the default template + return defaultTpl + default: + // Otherwise, merge fields + if tpl.NodeSelector == nil { + tpl.NodeSelector = defaultTpl.NodeSelector + } + if tpl.Tolerations == nil { + tpl.Tolerations = defaultTpl.Tolerations + } + if tpl.Affinity == nil { + tpl.Affinity = defaultTpl.Affinity + } + if tpl.SecurityContext == nil { + tpl.SecurityContext = defaultTpl.SecurityContext + } + if tpl.Volumes == nil { + tpl.Volumes = defaultTpl.Volumes + } + if tpl.RuntimeClassName == nil { + tpl.RuntimeClassName = defaultTpl.RuntimeClassName + } + if tpl.AutomountServiceAccountToken == nil { + tpl.AutomountServiceAccountToken = defaultTpl.AutomountServiceAccountToken + } + if tpl.DNSPolicy == nil { + tpl.DNSPolicy = defaultTpl.DNSPolicy + } + if tpl.DNSConfig == nil { + tpl.DNSConfig = defaultTpl.DNSConfig + } + if tpl.EnableServiceLinks == nil { + tpl.EnableServiceLinks = defaultTpl.EnableServiceLinks + } + if tpl.PriorityClassName == nil { + tpl.PriorityClassName = defaultTpl.PriorityClassName + } + if tpl.SchedulerName == "" { + tpl.SchedulerName = defaultTpl.SchedulerName + } + if tpl.ImagePullSecrets == nil { + tpl.ImagePullSecrets = defaultTpl.ImagePullSecrets + } + if tpl.HostAliases == nil { + tpl.HostAliases = defaultTpl.HostAliases + } + if tpl.HostNetwork == false && defaultTpl.HostNetwork == true { + tpl.HostNetwork = true + } + return tpl + } +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskrun_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskrun_types.go new file mode 100644 index 0000000000..71d7e567b9 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskrun_types.go @@ -0,0 +1,467 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + "time" + + "github.com/tektoncd/pipeline/pkg/apis/config" + apisconfig "github.com/tektoncd/pipeline/pkg/apis/config" + "github.com/tektoncd/pipeline/pkg/apis/pipeline" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/clock" + "knative.dev/pkg/apis" + duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1" +) + +// TaskRunSpec defines the desired state of TaskRun +type TaskRunSpec struct { + // +optional + Debug *TaskRunDebug `json:"debug,omitempty"` + // +optional + Params []Param `json:"params,omitempty"` + // +optional + Resources *TaskRunResources `json:"resources,omitempty"` + // +optional + ServiceAccountName string `json:"serviceAccountName"` + // no more than one of the TaskRef and TaskSpec may be specified. + // +optional + TaskRef *TaskRef `json:"taskRef,omitempty"` + // +optional + TaskSpec *TaskSpec `json:"taskSpec,omitempty"` + // Used for cancelling a taskrun (and maybe more later on) + // +optional + Status TaskRunSpecStatus `json:"status,omitempty"` + // Time after which the build times out. Defaults to 1 hour. + // Specified build timeout should be less than 24h. + // Refer Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + // PodTemplate holds pod specific configuration + PodTemplate *PodTemplate `json:"podTemplate,omitempty"` + // Workspaces is a list of WorkspaceBindings from volumes to workspaces. + // +optional + Workspaces []WorkspaceBinding `json:"workspaces,omitempty"` + // Overrides to apply to Steps in this TaskRun. + // If a field is specified in both a Step and a StepOverride, + // the value from the StepOverride will be used. + // This field is only supported when the alpha feature gate is enabled. + // +optional + StepOverrides []TaskRunStepOverride `json:"stepOverrides,omitempty"` + // Overrides to apply to Sidecars in this TaskRun. + // If a field is specified in both a Sidecar and a SidecarOverride, + // the value from the SidecarOverride will be used. + // This field is only supported when the alpha feature gate is enabled. + // +optional + SidecarOverrides []TaskRunSidecarOverride `json:"sidecarOverrides,omitempty"` +} + +// TaskRunSpecStatus defines the taskrun spec status the user can provide +type TaskRunSpecStatus string + +const ( + // TaskRunSpecStatusCancelled indicates that the user wants to cancel the task, + // if not already cancelled or terminated + TaskRunSpecStatusCancelled = "TaskRunCancelled" +) + +// TaskRunDebug defines the breakpoint config for a particular TaskRun +type TaskRunDebug struct { + // +optional + Breakpoint []string `json:"breakpoint,omitempty"` +} + +// TaskRunInputs holds the input values that this task was invoked with. +type TaskRunInputs struct { + // +optional + Resources []TaskResourceBinding `json:"resources,omitempty"` + // +optional + Params []Param `json:"params,omitempty"` +} + +// TaskRunOutputs holds the output values that this task was invoked with. +type TaskRunOutputs struct { + // +optional + Resources []TaskResourceBinding `json:"resources,omitempty"` +} + +var taskRunCondSet = apis.NewBatchConditionSet() + +// TaskRunStatus defines the observed state of TaskRun +type TaskRunStatus struct { + duckv1beta1.Status `json:",inline"` + + // TaskRunStatusFields inlines the status fields. + TaskRunStatusFields `json:",inline"` +} + +// TaskRunReason is an enum used to store all TaskRun reason for +// the Succeeded condition that are controlled by the TaskRun itself. Failure +// reasons that emerge from underlying resources are not included here +type TaskRunReason string + +const ( + // TaskRunReasonStarted is the reason set when the TaskRun has just started + TaskRunReasonStarted TaskRunReason = "Started" + // TaskRunReasonRunning is the reason set when the TaskRun is running + TaskRunReasonRunning TaskRunReason = "Running" + // TaskRunReasonSuccessful is the reason set when the TaskRun completed successfully + TaskRunReasonSuccessful TaskRunReason = "Succeeded" + // TaskRunReasonFailed is the reason set when the TaskRun completed with a failure + TaskRunReasonFailed TaskRunReason = "Failed" + // TaskRunReasonCancelled is the reason set when the Taskrun is cancelled by the user + TaskRunReasonCancelled TaskRunReason = "TaskRunCancelled" + // TaskRunReasonTimedOut is the reason set when the Taskrun has timed out + TaskRunReasonTimedOut TaskRunReason = "TaskRunTimeout" +) + +func (t TaskRunReason) String() string { + return string(t) +} + +// GetStartedReason returns the reason set to the "Succeeded" condition when +// InitializeConditions is invoked +func (trs *TaskRunStatus) GetStartedReason() string { + return TaskRunReasonStarted.String() +} + +// GetRunningReason returns the reason set to the "Succeeded" condition when +// the TaskRun starts running. This is used indicate that the resource +// could be validated is starting to perform its job. +func (trs *TaskRunStatus) GetRunningReason() string { + return TaskRunReasonRunning.String() +} + +// MarkResourceOngoing sets the ConditionSucceeded condition to ConditionUnknown +// with the reason and message. +func (trs *TaskRunStatus) MarkResourceOngoing(reason TaskRunReason, message string) { + taskRunCondSet.Manage(trs).SetCondition(apis.Condition{ + Type: apis.ConditionSucceeded, + Status: corev1.ConditionUnknown, + Reason: reason.String(), + Message: message, + }) +} + +// MarkResourceFailed sets the ConditionSucceeded condition to ConditionFalse +// based on an error that occurred and a reason +func (trs *TaskRunStatus) MarkResourceFailed(reason TaskRunReason, err error) { + taskRunCondSet.Manage(trs).SetCondition(apis.Condition{ + Type: apis.ConditionSucceeded, + Status: corev1.ConditionFalse, + Reason: reason.String(), + Message: err.Error(), + }) + succeeded := trs.GetCondition(apis.ConditionSucceeded) + trs.CompletionTime = &succeeded.LastTransitionTime.Inner +} + +// TaskRunStatusFields holds the fields of TaskRun's status. This is defined +// separately and inlined so that other types can readily consume these fields +// via duck typing. +type TaskRunStatusFields struct { + // PodName is the name of the pod responsible for executing this task's steps. + PodName string `json:"podName"` + + // StartTime is the time the build is actually started. + // +optional + StartTime *metav1.Time `json:"startTime,omitempty"` + + // CompletionTime is the time the build completed. + // +optional + CompletionTime *metav1.Time `json:"completionTime,omitempty"` + + // Steps describes the state of each build step container. + // +optional + Steps []StepState `json:"steps,omitempty"` + + // CloudEvents describe the state of each cloud event requested via a + // CloudEventResource. + // +optional + CloudEvents []CloudEventDelivery `json:"cloudEvents,omitempty"` + + // RetriesStatus contains the history of TaskRunStatus in case of a retry in order to keep record of failures. + // All TaskRunStatus stored in RetriesStatus will have no date within the RetriesStatus as is redundant. + // +optional + RetriesStatus []TaskRunStatus `json:"retriesStatus,omitempty"` + + // Results from Resources built during the taskRun. currently includes + // the digest of build container images + // +optional + ResourcesResult []PipelineResourceResult `json:"resourcesResult,omitempty"` + + // TaskRunResults are the list of results written out by the task's containers + // +optional + TaskRunResults []TaskRunResult `json:"taskResults,omitempty"` + + // The list has one entry per sidecar in the manifest. Each entry is + // represents the imageid of the corresponding sidecar. + Sidecars []SidecarState `json:"sidecars,omitempty"` + + // TaskSpec contains the Spec from the dereferenced Task definition used to instantiate this TaskRun. + TaskSpec *TaskSpec `json:"taskSpec,omitempty"` +} + +// TaskRunResult used to describe the results of a task +type TaskRunResult struct { + // Name the given name + Name string `json:"name"` + + // Value the given value of the result + Value string `json:"value"` +} + +// TaskRunStepOverride is used to override the values of a Step in the corresponding Task. +type TaskRunStepOverride struct { + // The name of the Step to override. + Name string + // The resource requirements to apply to the Step. + Resources corev1.ResourceRequirements +} + +// TaskRunSidecarOverride is used to override the values of a Sidecar in the corresponding Task. +type TaskRunSidecarOverride struct { + // The name of the Sidecar to override. + Name string + // The resource requirements to apply to the Sidecar. + Resources corev1.ResourceRequirements +} + +// GetGroupVersionKind implements kmeta.OwnerRefable. +func (*TaskRun) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind(pipeline.TaskRunControllerName) +} + +// GetStatusCondition returns the task run status as a ConditionAccessor +func (tr *TaskRun) GetStatusCondition() apis.ConditionAccessor { + return &tr.Status +} + +// GetCondition returns the Condition matching the given type. +func (trs *TaskRunStatus) GetCondition(t apis.ConditionType) *apis.Condition { + return taskRunCondSet.Manage(trs).GetCondition(t) +} + +// InitializeConditions will set all conditions in taskRunCondSet to unknown for the TaskRun +// and set the started time to the current time +func (trs *TaskRunStatus) InitializeConditions() { + started := false + if trs.StartTime.IsZero() { + trs.StartTime = &metav1.Time{Time: time.Now()} + started = true + } + conditionManager := taskRunCondSet.Manage(trs) + conditionManager.InitializeConditions() + // Ensure the started reason is set for the "Succeeded" condition + if started { + initialCondition := conditionManager.GetCondition(apis.ConditionSucceeded) + initialCondition.Reason = TaskRunReasonStarted.String() + conditionManager.SetCondition(*initialCondition) + } +} + +// SetCondition sets the condition, unsetting previous conditions with the same +// type as necessary. +func (trs *TaskRunStatus) SetCondition(newCond *apis.Condition) { + if newCond != nil { + taskRunCondSet.Manage(trs).SetCondition(*newCond) + } +} + +// StepState reports the results of running a step in a Task. +type StepState struct { + corev1.ContainerState `json:",inline"` + Name string `json:"name,omitempty"` + ContainerName string `json:"container,omitempty"` + ImageID string `json:"imageID,omitempty"` +} + +// SidecarState reports the results of running a sidecar in a Task. +type SidecarState struct { + corev1.ContainerState `json:",inline"` + Name string `json:"name,omitempty"` + ContainerName string `json:"container,omitempty"` + ImageID string `json:"imageID,omitempty"` +} + +// CloudEventDelivery is the target of a cloud event along with the state of +// delivery. +type CloudEventDelivery struct { + // Target points to an addressable + Target string `json:"target,omitempty"` + Status CloudEventDeliveryState `json:"status,omitempty"` +} + +// CloudEventCondition is a string that represents the condition of the event. +type CloudEventCondition string + +const ( + // CloudEventConditionUnknown means that the condition for the event to be + // triggered was not met yet, or we don't know the state yet. + CloudEventConditionUnknown CloudEventCondition = "Unknown" + // CloudEventConditionSent means that the event was sent successfully + CloudEventConditionSent CloudEventCondition = "Sent" + // CloudEventConditionFailed means that there was one or more attempts to + // send the event, and none was successful so far. + CloudEventConditionFailed CloudEventCondition = "Failed" +) + +// CloudEventDeliveryState reports the state of a cloud event to be sent. +type CloudEventDeliveryState struct { + // Current status + Condition CloudEventCondition `json:"condition,omitempty"` + // SentAt is the time at which the last attempt to send the event was made + // +optional + SentAt *metav1.Time `json:"sentAt,omitempty"` + // Error is the text of error (if any) + Error string `json:"message"` + // RetryCount is the number of attempts of sending the cloud event + RetryCount int32 `json:"retryCount"` +} + +// +genclient +// +genreconciler:krshapedlogic=false +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// TaskRun represents a single execution of a Task. TaskRuns are how the steps +// specified in a Task are executed; they specify the parameters and resources +// used to run the steps in a Task. +// +// +k8s:openapi-gen=true +type TaskRun struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +optional + Spec TaskRunSpec `json:"spec,omitempty"` + // +optional + Status TaskRunStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// TaskRunList contains a list of TaskRun +type TaskRunList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []TaskRun `json:"items"` +} + +// GetPipelineRunPVCName for taskrun gets pipelinerun +func (tr *TaskRun) GetPipelineRunPVCName() string { + if tr == nil { + return "" + } + for _, ref := range tr.GetOwnerReferences() { + if ref.Kind == pipeline.PipelineRunControllerName { + return fmt.Sprintf("%s-pvc", ref.Name) + } + } + return "" +} + +// HasPipelineRunOwnerReference returns true of TaskRun has +// owner reference of type PipelineRun +func (tr *TaskRun) HasPipelineRunOwnerReference() bool { + for _, ref := range tr.GetOwnerReferences() { + if ref.Kind == pipeline.PipelineRunControllerName { + return true + } + } + return false +} + +// IsDone returns true if the TaskRun's status indicates that it is done. +func (tr *TaskRun) IsDone() bool { + return !tr.Status.GetCondition(apis.ConditionSucceeded).IsUnknown() +} + +// HasStarted function check whether taskrun has valid start time set in its status +func (tr *TaskRun) HasStarted() bool { + return tr.Status.StartTime != nil && !tr.Status.StartTime.IsZero() +} + +// IsSuccessful returns true if the TaskRun's status indicates that it is done. +func (tr *TaskRun) IsSuccessful() bool { + return tr.Status.GetCondition(apis.ConditionSucceeded).IsTrue() +} + +// IsCancelled returns true if the TaskRun's spec status is set to Cancelled state +func (tr *TaskRun) IsCancelled() bool { + return tr.Spec.Status == TaskRunSpecStatusCancelled +} + +// HasTimedOut returns true if the TaskRun runtime is beyond the allowed timeout +func (tr *TaskRun) HasTimedOut(ctx context.Context, c clock.PassiveClock) bool { + if tr.Status.StartTime.IsZero() { + return false + } + timeout := tr.GetTimeout(ctx) + // If timeout is set to 0 or defaulted to 0, there is no timeout. + if timeout == apisconfig.NoTimeoutDuration { + return false + } + runtime := c.Since(tr.Status.StartTime.Time) + return runtime > timeout +} + +// GetTimeout returns the timeout for the TaskRun, or the default if not specified +func (tr *TaskRun) GetTimeout(ctx context.Context) time.Duration { + // Use the platform default is no timeout is set + if tr.Spec.Timeout == nil { + defaultTimeout := time.Duration(config.FromContextOrDefaults(ctx).Defaults.DefaultTimeoutMinutes) + return defaultTimeout * time.Minute + } + return tr.Spec.Timeout.Duration +} + +// GetNamespacedName returns a k8s namespaced name that identifies this TaskRun +func (tr *TaskRun) GetNamespacedName() types.NamespacedName { + return types.NamespacedName{Namespace: tr.Namespace, Name: tr.Name} +} + +// IsPartOfPipeline return true if TaskRun is a part of a Pipeline. +// It also return the name of Pipeline and PipelineRun +func (tr *TaskRun) IsPartOfPipeline() (bool, string, string) { + if tr == nil || len(tr.Labels) == 0 { + return false, "", "" + } + + if pl, ok := tr.Labels[pipeline.PipelineLabelKey]; ok { + return true, pl, tr.Labels[pipeline.PipelineRunLabelKey] + } + + return false, "", "" +} + +// HasVolumeClaimTemplate returns true if TaskRun contains volumeClaimTemplates that is +// used for creating PersistentVolumeClaims with an OwnerReference for each run +func (tr *TaskRun) HasVolumeClaimTemplate() bool { + for _, ws := range tr.Spec.Workspaces { + if ws.VolumeClaimTemplate != nil { + return true + } + } + return false +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskrun_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskrun_validation.go new file mode 100644 index 0000000000..761c944666 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/taskrun_validation.go @@ -0,0 +1,165 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + "strings" + + "github.com/tektoncd/pipeline/pkg/apis/config" + "github.com/tektoncd/pipeline/pkg/apis/validate" + "k8s.io/apimachinery/pkg/util/sets" + "knative.dev/pkg/apis" +) + +var _ apis.Validatable = (*TaskRun)(nil) + +// Validate taskrun +func (tr *TaskRun) Validate(ctx context.Context) *apis.FieldError { + errs := validate.ObjectMetadata(tr.GetObjectMeta()).ViaField("metadata") + if apis.IsInDelete(ctx) { + return nil + } + return errs.Also(tr.Spec.Validate(apis.WithinSpec(ctx)).ViaField("spec")) +} + +// Validate taskrun spec +func (ts *TaskRunSpec) Validate(ctx context.Context) (errs *apis.FieldError) { + // Must have exactly one of taskRef and taskSpec. + if ts.TaskRef == nil && ts.TaskSpec == nil { + errs = errs.Also(apis.ErrMissingOneOf("taskRef", "taskSpec")) + } + if ts.TaskRef != nil && ts.TaskSpec != nil { + errs = errs.Also(apis.ErrMultipleOneOf("taskRef", "taskSpec")) + } + // Validate TaskRef if it's present. + if ts.TaskRef != nil { + errs = errs.Also(ts.TaskRef.Validate(ctx).ViaField("taskRef")) + } + // Validate TaskSpec if it's present. + if ts.TaskSpec != nil { + errs = errs.Also(ts.TaskSpec.Validate(ctx).ViaField("taskSpec")) + } + + errs = errs.Also(validateParameters(ts.Params).ViaField("params")) + errs = errs.Also(validateWorkspaceBindings(ctx, ts.Workspaces).ViaField("workspaces")) + errs = errs.Also(ts.Resources.Validate(ctx).ViaField("resources")) + if ts.Debug != nil { + errs = errs.Also(ValidateEnabledAPIFields(ctx, "debug", config.AlphaAPIFields).ViaField("debug")) + errs = errs.Also(validateDebug(ts.Debug).ViaField("debug")) + } + if ts.StepOverrides != nil { + errs = errs.Also(ValidateEnabledAPIFields(ctx, "stepOverrides", config.AlphaAPIFields).ViaField("stepOverrides")) + errs = errs.Also(validateStepOverrides(ts.StepOverrides).ViaField("stepOverrides")) + } + if ts.SidecarOverrides != nil { + errs = errs.Also(ValidateEnabledAPIFields(ctx, "sidecarOverrides", config.AlphaAPIFields).ViaField("sidecarOverrides")) + errs = errs.Also(validateSidecarOverrides(ts.SidecarOverrides).ViaField("sidecarOverrides")) + } + + if ts.Status != "" { + if ts.Status != TaskRunSpecStatusCancelled { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s should be %s", ts.Status, TaskRunSpecStatusCancelled), "status")) + } + } + if ts.Timeout != nil { + // timeout should be a valid duration of at least 0. + if ts.Timeout.Duration < 0 { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s should be >= 0", ts.Timeout.Duration.String()), "timeout")) + } + } + + return errs +} + +// validateDebug +func validateDebug(db *TaskRunDebug) (errs *apis.FieldError) { + breakpointOnFailure := "onFailure" + validBreakpoints := sets.NewString() + validBreakpoints.Insert(breakpointOnFailure) + + for _, b := range db.Breakpoint { + if !validBreakpoints.Has(b) { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s is not a valid breakpoint. Available valid breakpoints include %s", b, validBreakpoints.List()), "breakpoint")) + } + } + return errs +} + +// validateWorkspaceBindings makes sure the volumes provided for the Task's declared workspaces make sense. +func validateWorkspaceBindings(ctx context.Context, wb []WorkspaceBinding) (errs *apis.FieldError) { + var names []string + for idx, w := range wb { + names = append(names, w.Name) + errs = errs.Also(w.Validate(ctx).ViaIndex(idx)) + } + errs = errs.Also(validateNoDuplicateNames(names, true)) + return errs +} + +func validateParameters(params []Param) (errs *apis.FieldError) { + var names []string + for _, p := range params { + names = append(names, p.Name) + } + return validateNoDuplicateNames(names, false) +} + +func validateStepOverrides(overrides []TaskRunStepOverride) (errs *apis.FieldError) { + var names []string + for i, o := range overrides { + if o.Name == "" { + errs = errs.Also(apis.ErrMissingField("name").ViaIndex(i)) + } else { + names = append(names, o.Name) + } + } + errs = errs.Also(validateNoDuplicateNames(names, true)) + return errs +} + +func validateSidecarOverrides(overrides []TaskRunSidecarOverride) (errs *apis.FieldError) { + var names []string + for i, o := range overrides { + if o.Name == "" { + errs = errs.Also(apis.ErrMissingField("name").ViaIndex(i)) + } else { + names = append(names, o.Name) + } + } + errs = errs.Also(validateNoDuplicateNames(names, true)) + return errs +} + +// validateNoDuplicateNames returns an error for each name that is repeated in names. +// Case insensitive. +// If byIndex is true, the error will be reported by index instead of by key. +func validateNoDuplicateNames(names []string, byIndex bool) (errs *apis.FieldError) { + seen := sets.NewString() + for i, n := range names { + if seen.Has(strings.ToLower(n)) { + if byIndex { + errs = errs.Also(apis.ErrMultipleOneOf("name").ViaIndex(i)) + } else { + errs = errs.Also(apis.ErrMultipleOneOf("name").ViaKey(n)) + } + } + seen.Insert(n) + } + return errs +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/version_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/version_validation.go new file mode 100644 index 0000000000..4d58eeb755 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/version_validation.go @@ -0,0 +1,38 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + + "github.com/tektoncd/pipeline/pkg/apis/config" + "knative.dev/pkg/apis" +) + +// ValidateEnabledAPIFields checks that the enable-api-fields feature gate is set +// to the wantVersion value and, if not, returns an error stating which feature +// is dependent on the version and what the current version actually is. +func ValidateEnabledAPIFields(ctx context.Context, featureName, wantVersion string) *apis.FieldError { + currentVersion := config.FromContextOrDefaults(ctx).FeatureFlags.EnableAPIFields + if currentVersion != wantVersion { + var errs *apis.FieldError + message := fmt.Sprintf(`%s requires "enable-api-fields" feature gate to be %q but it is %q`, featureName, wantVersion, currentVersion) + return errs.Also(apis.ErrGeneric(message)) + } + return nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/when_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/when_types.go new file mode 100644 index 0000000000..929ae8b438 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/when_types.go @@ -0,0 +1,109 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "fmt" + + "github.com/tektoncd/pipeline/pkg/substitution" + "k8s.io/apimachinery/pkg/selection" +) + +// WhenExpression allows a PipelineTask to declare expressions to be evaluated before the Task is run +// to determine whether the Task should be executed or skipped +type WhenExpression struct { + // Input is the string for guard checking which can be a static input or an output from a parent Task + Input string `json:"input"` + + // Operator that represents an Input's relationship to the values + Operator selection.Operator `json:"operator"` + + // Values is an array of strings, which is compared against the input, for guard checking + // It must be non-empty + Values []string `json:"values"` +} + +func (we *WhenExpression) isInputInValues() bool { + for i := range we.Values { + if we.Values[i] == we.Input { + return true + } + } + return false +} + +func (we *WhenExpression) isTrue() bool { + if we.Operator == selection.In { + return we.isInputInValues() + } + // selection.NotIn + return !we.isInputInValues() +} + +func (we *WhenExpression) applyReplacements(replacements map[string]string, arrayReplacements map[string][]string) WhenExpression { + replacedInput := substitution.ApplyReplacements(we.Input, replacements) + + var replacedValues []string + for _, val := range we.Values { + // arrayReplacements holds a list of array parameters with a pattern - params.arrayParam1 + // array params are referenced using $(params.arrayParam1[*]) + // check if the param exist in the arrayReplacements to replace it with a list of values + if _, ok := arrayReplacements[fmt.Sprintf("%s.%s", ParamsPrefix, ArrayReference(val))]; ok { + replacedValues = append(replacedValues, substitution.ApplyArrayReplacements(val, replacements, arrayReplacements)...) + } else { + replacedValues = append(replacedValues, substitution.ApplyReplacements(val, replacements)) + } + } + + return WhenExpression{Input: replacedInput, Operator: we.Operator, Values: replacedValues} +} + +// GetVarSubstitutionExpressions extracts all the values between "$(" and ")" in a When Expression +func (we *WhenExpression) GetVarSubstitutionExpressions() ([]string, bool) { + var allExpressions []string + allExpressions = append(allExpressions, validateString(we.Input)...) + for _, value := range we.Values { + allExpressions = append(allExpressions, validateString(value)...) + } + return allExpressions, len(allExpressions) != 0 +} + +// WhenExpressions are used to specify whether a Task should be executed or skipped +// All of them need to evaluate to True for a guarded Task to be executed. +type WhenExpressions []WhenExpression + +// AllowsExecution evaluates an Input's relationship to an array of Values, based on the Operator, +// to determine whether all the When Expressions are True. If they are all True, the guarded Task is +// executed, otherwise it is skipped. +func (wes WhenExpressions) AllowsExecution() bool { + for _, we := range wes { + if !we.isTrue() { + return false + } + } + return true +} + +// ReplaceWhenExpressionsVariables interpolates variables, such as Parameters and Results, in +// the Input and Values. +func (wes WhenExpressions) ReplaceWhenExpressionsVariables(replacements map[string]string, arrayReplacements map[string][]string) WhenExpressions { + replaced := wes + for i := range wes { + replaced[i] = wes[i].applyReplacements(replacements, arrayReplacements) + } + return replaced +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/when_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/when_validation.go new file mode 100644 index 0000000000..316dbf10f2 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/when_validation.go @@ -0,0 +1,92 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "fmt" + "strings" + + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/selection" + "k8s.io/apimachinery/pkg/util/sets" + "knative.dev/pkg/apis" +) + +var validWhenOperators = []string{ + string(selection.In), + string(selection.NotIn), +} + +func (wes WhenExpressions) validate() *apis.FieldError { + errs := wes.validateWhenExpressionsFields().ViaField("when") + return errs.Also(wes.validateTaskResultsVariables().ViaField("when")) +} + +func (wes WhenExpressions) validateWhenExpressionsFields() (errs *apis.FieldError) { + for idx, we := range wes { + errs = errs.Also(we.validateWhenExpressionFields().ViaIndex(idx)) + } + return errs +} + +func (we *WhenExpression) validateWhenExpressionFields() *apis.FieldError { + if equality.Semantic.DeepEqual(we, &WhenExpression{}) || we == nil { + return apis.ErrMissingField(apis.CurrentField) + } + if !sets.NewString(validWhenOperators...).Has(string(we.Operator)) { + message := fmt.Sprintf("operator %q is not recognized. valid operators: %s", we.Operator, strings.Join(validWhenOperators, ",")) + return apis.ErrInvalidValue(message, apis.CurrentField) + } + if len(we.Values) == 0 { + return apis.ErrInvalidValue("expecting non-empty values field", apis.CurrentField) + } + return nil +} + +func (wes WhenExpressions) validateTaskResultsVariables() *apis.FieldError { + for idx, we := range wes { + expressions, ok := we.GetVarSubstitutionExpressions() + if ok { + if LooksLikeContainsResultRefs(expressions) { + expressions = filter(expressions, looksLikeResultRef) + resultRefs := NewResultRefs(expressions) + if len(expressions) != len(resultRefs) { + message := fmt.Sprintf("expected all of the expressions %v to be result expressions but only %v were", expressions, resultRefs) + return apis.ErrInvalidValue(message, apis.CurrentField).ViaIndex(idx) + } + } + } + } + return nil +} + +func (wes WhenExpressions) validatePipelineParametersVariables(prefix string, paramNames sets.String, arrayParamNames sets.String) (errs *apis.FieldError) { + for idx, we := range wes { + errs = errs.Also(validateStringVariable(we.Input, prefix, paramNames, arrayParamNames).ViaField("input").ViaFieldIndex("when", idx)) + for _, val := range we.Values { + // one of the values could be a reference to an array param, such as, $(params.foo[*]) + // extract the variable name from the pattern $(params.foo[*]), if the variable name matches with one of the array params + // validate the param as an array variable otherwise, validate it as a string variable + if arrayParamNames.Has(ArrayReference(val)) { + errs = errs.Also(validateArrayVariable(val, prefix, paramNames, arrayParamNames).ViaField("values").ViaFieldIndex("when", idx)) + } else { + errs = errs.Also(validateStringVariable(val, prefix, paramNames, arrayParamNames).ViaField("values").ViaFieldIndex("when", idx)) + } + } + } + return errs +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/workspace_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/workspace_types.go new file mode 100644 index 0000000000..8f45bfb36c --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/workspace_types.go @@ -0,0 +1,123 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "path/filepath" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline" + corev1 "k8s.io/api/core/v1" +) + +// WorkspaceDeclaration is a declaration of a volume that a Task requires. +type WorkspaceDeclaration struct { + // Name is the name by which you can bind the volume at runtime. + Name string `json:"name"` + // Description is an optional human readable description of this volume. + // +optional + Description string `json:"description,omitempty"` + // MountPath overrides the directory that the volume will be made available at. + // +optional + MountPath string `json:"mountPath,omitempty"` + // ReadOnly dictates whether a mounted volume is writable. By default this + // field is false and so mounted volumes are writable. + ReadOnly bool `json:"readOnly,omitempty"` + // Optional marks a Workspace as not being required in TaskRuns. By default + // this field is false and so declared workspaces are required. + Optional bool `json:"optional,omitempty"` +} + +// GetMountPath returns the mountPath for w which is the MountPath if provided or the +// default if not. +func (w *WorkspaceDeclaration) GetMountPath() string { + if w.MountPath != "" { + return w.MountPath + } + return filepath.Join(pipeline.WorkspaceDir, w.Name) +} + +// WorkspaceBinding maps a Task's declared workspace to a Volume. +type WorkspaceBinding struct { + // Name is the name of the workspace populated by the volume. + Name string `json:"name"` + // SubPath is optionally a directory on the volume which should be used + // for this binding (i.e. the volume will be mounted at this sub directory). + // +optional + SubPath string `json:"subPath,omitempty"` + // VolumeClaimTemplate is a template for a claim that will be created in the same namespace. + // The PipelineRun controller is responsible for creating a unique claim for each instance of PipelineRun. + // +optional + VolumeClaimTemplate *corev1.PersistentVolumeClaim `json:"volumeClaimTemplate,omitempty"` + // PersistentVolumeClaimVolumeSource represents a reference to a + // PersistentVolumeClaim in the same namespace. Either this OR EmptyDir can be used. + // +optional + PersistentVolumeClaim *corev1.PersistentVolumeClaimVolumeSource `json:"persistentVolumeClaim,omitempty"` + // EmptyDir represents a temporary directory that shares a Task's lifetime. + // More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + // Either this OR PersistentVolumeClaim can be used. + // +optional + EmptyDir *corev1.EmptyDirVolumeSource `json:"emptyDir,omitempty"` + // ConfigMap represents a configMap that should populate this workspace. + // +optional + ConfigMap *corev1.ConfigMapVolumeSource `json:"configMap,omitempty"` + // Secret represents a secret that should populate this workspace. + // +optional + Secret *corev1.SecretVolumeSource `json:"secret,omitempty"` +} + +// WorkspacePipelineDeclaration creates a named slot in a Pipeline that a PipelineRun +// is expected to populate with a workspace binding. +// Deprecated: use PipelineWorkspaceDeclaration type instead +type WorkspacePipelineDeclaration = PipelineWorkspaceDeclaration + +// PipelineWorkspaceDeclaration creates a named slot in a Pipeline that a PipelineRun +// is expected to populate with a workspace binding. +type PipelineWorkspaceDeclaration struct { + // Name is the name of a workspace to be provided by a PipelineRun. + Name string `json:"name"` + // Description is a human readable string describing how the workspace will be + // used in the Pipeline. It can be useful to include a bit of detail about which + // tasks are intended to have access to the data on the workspace. + // +optional + Description string `json:"description,omitempty"` + // Optional marks a Workspace as not being required in PipelineRuns. By default + // this field is false and so declared workspaces are required. + Optional bool `json:"optional,omitempty"` +} + +// WorkspacePipelineTaskBinding describes how a workspace passed into the pipeline should be +// mapped to a task's declared workspace. +type WorkspacePipelineTaskBinding struct { + // Name is the name of the workspace as declared by the task + Name string `json:"name"` + // Workspace is the name of the workspace declared by the pipeline + Workspace string `json:"workspace"` + // SubPath is optionally a directory on the volume which should be used + // for this binding (i.e. the volume will be mounted at this sub directory). + // +optional + SubPath string `json:"subPath,omitempty"` +} + +// WorkspaceUsage is used by a Step or Sidecar to declare that it wants isolated access +// to a Workspace defined in a Task. +type WorkspaceUsage struct { + // Name is the name of the workspace this Step or Sidecar wants access to. + Name string `json:"name"` + // MountPath is the path that the workspace should be mounted to inside the Step or Sidecar, + // overriding any MountPath specified in the Task's WorkspaceDeclaration. + MountPath string `json:"mountPath"` +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/workspace_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/workspace_validation.go new file mode 100644 index 0000000000..1aff7f0440 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/workspace_validation.go @@ -0,0 +1,92 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + + "k8s.io/apimachinery/pkg/api/equality" + "knative.dev/pkg/apis" +) + +// allVolumeSourceFields is a list of all the volume source field paths that a +// WorkspaceBinding may include. +var allVolumeSourceFields = []string{ + "persistentvolumeclaim", + "volumeclaimtemplate", + "emptydir", + "configmap", + "secret", +} + +// Validate looks at the Volume provided in wb and makes sure that it is valid. +// This means that only one VolumeSource can be specified, and also that the +// supported VolumeSource is itself valid. +func (b *WorkspaceBinding) Validate(ctx context.Context) *apis.FieldError { + if equality.Semantic.DeepEqual(b, &WorkspaceBinding{}) || b == nil { + return apis.ErrMissingField(apis.CurrentField) + } + + numSources := b.numSources() + + if numSources > 1 { + return apis.ErrMultipleOneOf(allVolumeSourceFields...) + } + + if numSources == 0 { + return apis.ErrMissingOneOf(allVolumeSourceFields...) + } + + // For a PersistentVolumeClaim to work, you must at least provide the name of the PVC to use. + if b.PersistentVolumeClaim != nil && b.PersistentVolumeClaim.ClaimName == "" { + return apis.ErrMissingField("persistentvolumeclaim.claimname") + } + + // For a ConfigMap to work, you must provide the name of the ConfigMap to use. + if b.ConfigMap != nil && b.ConfigMap.LocalObjectReference.Name == "" { + return apis.ErrMissingField("configmap.name") + } + + // For a Secret to work, you must provide the name of the Secret to use. + if b.Secret != nil && b.Secret.SecretName == "" { + return apis.ErrMissingField("secret.secretName") + } + + return nil +} + +// numSources returns the total number of volume sources that this WorkspaceBinding +// has been configured with. +func (b *WorkspaceBinding) numSources() int { + n := 0 + if b.VolumeClaimTemplate != nil { + n++ + } + if b.PersistentVolumeClaim != nil { + n++ + } + if b.EmptyDir != nil { + n++ + } + if b.ConfigMap != nil { + n++ + } + if b.Secret != nil { + n++ + } + return n +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/zz_generated.deepcopy.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..ed83610fdf --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,2103 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1beta1 + +import ( + pod "github.com/tektoncd/pipeline/pkg/apis/pipeline/pod" + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" + runv1alpha1 "github.com/tektoncd/pipeline/pkg/apis/run/v1alpha1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ArrayOrString) DeepCopyInto(out *ArrayOrString) { + *out = *in + if in.ArrayVal != nil { + in, out := &in.ArrayVal, &out.ArrayVal + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArrayOrString. +func (in *ArrayOrString) DeepCopy() *ArrayOrString { + if in == nil { + return nil + } + out := new(ArrayOrString) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudEventDelivery) DeepCopyInto(out *CloudEventDelivery) { + *out = *in + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudEventDelivery. +func (in *CloudEventDelivery) DeepCopy() *CloudEventDelivery { + if in == nil { + return nil + } + out := new(CloudEventDelivery) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudEventDeliveryState) DeepCopyInto(out *CloudEventDeliveryState) { + *out = *in + if in.SentAt != nil { + in, out := &in.SentAt, &out.SentAt + *out = (*in).DeepCopy() + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudEventDeliveryState. +func (in *CloudEventDeliveryState) DeepCopy() *CloudEventDeliveryState { + if in == nil { + return nil + } + out := new(CloudEventDeliveryState) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterTask) DeepCopyInto(out *ClusterTask) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterTask. +func (in *ClusterTask) DeepCopy() *ClusterTask { + if in == nil { + return nil + } + out := new(ClusterTask) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterTask) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterTaskList) DeepCopyInto(out *ClusterTaskList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterTask, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterTaskList. +func (in *ClusterTaskList) DeepCopy() *ClusterTaskList { + if in == nil { + return nil + } + out := new(ClusterTaskList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterTaskList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConditionCheck) DeepCopyInto(out *ConditionCheck) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConditionCheck. +func (in *ConditionCheck) DeepCopy() *ConditionCheck { + if in == nil { + return nil + } + out := new(ConditionCheck) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConditionCheckStatus) DeepCopyInto(out *ConditionCheckStatus) { + *out = *in + in.Status.DeepCopyInto(&out.Status) + in.ConditionCheckStatusFields.DeepCopyInto(&out.ConditionCheckStatusFields) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConditionCheckStatus. +func (in *ConditionCheckStatus) DeepCopy() *ConditionCheckStatus { + if in == nil { + return nil + } + out := new(ConditionCheckStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConditionCheckStatusFields) DeepCopyInto(out *ConditionCheckStatusFields) { + *out = *in + if in.StartTime != nil { + in, out := &in.StartTime, &out.StartTime + *out = (*in).DeepCopy() + } + if in.CompletionTime != nil { + in, out := &in.CompletionTime, &out.CompletionTime + *out = (*in).DeepCopy() + } + in.Check.DeepCopyInto(&out.Check) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConditionCheckStatusFields. +func (in *ConditionCheckStatusFields) DeepCopy() *ConditionCheckStatusFields { + if in == nil { + return nil + } + out := new(ConditionCheckStatusFields) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EmbeddedTask) DeepCopyInto(out *EmbeddedTask) { + *out = *in + out.TypeMeta = in.TypeMeta + in.Spec.DeepCopyInto(&out.Spec) + in.Metadata.DeepCopyInto(&out.Metadata) + in.TaskSpec.DeepCopyInto(&out.TaskSpec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EmbeddedTask. +func (in *EmbeddedTask) DeepCopy() *EmbeddedTask { + if in == nil { + return nil + } + out := new(EmbeddedTask) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InternalTaskModifier) DeepCopyInto(out *InternalTaskModifier) { + *out = *in + if in.StepsToPrepend != nil { + in, out := &in.StepsToPrepend, &out.StepsToPrepend + *out = make([]Step, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.StepsToAppend != nil { + in, out := &in.StepsToAppend, &out.StepsToAppend + *out = make([]Step, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]v1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InternalTaskModifier. +func (in *InternalTaskModifier) DeepCopy() *InternalTaskModifier { + if in == nil { + return nil + } + out := new(InternalTaskModifier) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Param) DeepCopyInto(out *Param) { + *out = *in + in.Value.DeepCopyInto(&out.Value) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Param. +func (in *Param) DeepCopy() *Param { + if in == nil { + return nil + } + out := new(Param) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ParamSpec) DeepCopyInto(out *ParamSpec) { + *out = *in + if in.Default != nil { + in, out := &in.Default, &out.Default + *out = new(ArrayOrString) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ParamSpec. +func (in *ParamSpec) DeepCopy() *ParamSpec { + if in == nil { + return nil + } + out := new(ParamSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Pipeline) DeepCopyInto(out *Pipeline) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Pipeline. +func (in *Pipeline) DeepCopy() *Pipeline { + if in == nil { + return nil + } + out := new(Pipeline) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Pipeline) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineDeclaredResource) DeepCopyInto(out *PipelineDeclaredResource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineDeclaredResource. +func (in *PipelineDeclaredResource) DeepCopy() *PipelineDeclaredResource { + if in == nil { + return nil + } + out := new(PipelineDeclaredResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineList) DeepCopyInto(out *PipelineList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Pipeline, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineList. +func (in *PipelineList) DeepCopy() *PipelineList { + if in == nil { + return nil + } + out := new(PipelineList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PipelineList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineRef) DeepCopyInto(out *PipelineRef) { + *out = *in + in.ResolverRef.DeepCopyInto(&out.ResolverRef) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineRef. +func (in *PipelineRef) DeepCopy() *PipelineRef { + if in == nil { + return nil + } + out := new(PipelineRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineResourceBinding) DeepCopyInto(out *PipelineResourceBinding) { + *out = *in + if in.ResourceRef != nil { + in, out := &in.ResourceRef, &out.ResourceRef + *out = new(PipelineResourceRef) + **out = **in + } + if in.ResourceSpec != nil { + in, out := &in.ResourceSpec, &out.ResourceSpec + *out = new(v1alpha1.PipelineResourceSpec) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineResourceBinding. +func (in *PipelineResourceBinding) DeepCopy() *PipelineResourceBinding { + if in == nil { + return nil + } + out := new(PipelineResourceBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineResourceRef) DeepCopyInto(out *PipelineResourceRef) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineResourceRef. +func (in *PipelineResourceRef) DeepCopy() *PipelineResourceRef { + if in == nil { + return nil + } + out := new(PipelineResourceRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineResourceResult) DeepCopyInto(out *PipelineResourceResult) { + *out = *in + if in.ResourceRef != nil { + in, out := &in.ResourceRef, &out.ResourceRef + *out = new(PipelineResourceRef) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineResourceResult. +func (in *PipelineResourceResult) DeepCopy() *PipelineResourceResult { + if in == nil { + return nil + } + out := new(PipelineResourceResult) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineResult) DeepCopyInto(out *PipelineResult) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineResult. +func (in *PipelineResult) DeepCopy() *PipelineResult { + if in == nil { + return nil + } + out := new(PipelineResult) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineRun) DeepCopyInto(out *PipelineRun) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineRun. +func (in *PipelineRun) DeepCopy() *PipelineRun { + if in == nil { + return nil + } + out := new(PipelineRun) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PipelineRun) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineRunConditionCheckStatus) DeepCopyInto(out *PipelineRunConditionCheckStatus) { + *out = *in + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(ConditionCheckStatus) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineRunConditionCheckStatus. +func (in *PipelineRunConditionCheckStatus) DeepCopy() *PipelineRunConditionCheckStatus { + if in == nil { + return nil + } + out := new(PipelineRunConditionCheckStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineRunList) DeepCopyInto(out *PipelineRunList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PipelineRun, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineRunList. +func (in *PipelineRunList) DeepCopy() *PipelineRunList { + if in == nil { + return nil + } + out := new(PipelineRunList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PipelineRunList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineRunResult) DeepCopyInto(out *PipelineRunResult) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineRunResult. +func (in *PipelineRunResult) DeepCopy() *PipelineRunResult { + if in == nil { + return nil + } + out := new(PipelineRunResult) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineRunRunStatus) DeepCopyInto(out *PipelineRunRunStatus) { + *out = *in + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(runv1alpha1.RunStatus) + (*in).DeepCopyInto(*out) + } + if in.WhenExpressions != nil { + in, out := &in.WhenExpressions, &out.WhenExpressions + *out = make([]WhenExpression, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineRunRunStatus. +func (in *PipelineRunRunStatus) DeepCopy() *PipelineRunRunStatus { + if in == nil { + return nil + } + out := new(PipelineRunRunStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineRunSpec) DeepCopyInto(out *PipelineRunSpec) { + *out = *in + if in.PipelineRef != nil { + in, out := &in.PipelineRef, &out.PipelineRef + *out = new(PipelineRef) + (*in).DeepCopyInto(*out) + } + if in.PipelineSpec != nil { + in, out := &in.PipelineSpec, &out.PipelineSpec + *out = new(PipelineSpec) + (*in).DeepCopyInto(*out) + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make([]PipelineResourceBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]Param, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ServiceAccountNames != nil { + in, out := &in.ServiceAccountNames, &out.ServiceAccountNames + *out = make([]PipelineRunSpecServiceAccountName, len(*in)) + copy(*out, *in) + } + if in.Timeouts != nil { + in, out := &in.Timeouts, &out.Timeouts + *out = new(TimeoutFields) + (*in).DeepCopyInto(*out) + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(metav1.Duration) + **out = **in + } + if in.PodTemplate != nil { + in, out := &in.PodTemplate, &out.PodTemplate + *out = new(pod.Template) + (*in).DeepCopyInto(*out) + } + if in.Workspaces != nil { + in, out := &in.Workspaces, &out.Workspaces + *out = make([]WorkspaceBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.TaskRunSpecs != nil { + in, out := &in.TaskRunSpecs, &out.TaskRunSpecs + *out = make([]PipelineTaskRunSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineRunSpec. +func (in *PipelineRunSpec) DeepCopy() *PipelineRunSpec { + if in == nil { + return nil + } + out := new(PipelineRunSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineRunSpecServiceAccountName) DeepCopyInto(out *PipelineRunSpecServiceAccountName) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineRunSpecServiceAccountName. +func (in *PipelineRunSpecServiceAccountName) DeepCopy() *PipelineRunSpecServiceAccountName { + if in == nil { + return nil + } + out := new(PipelineRunSpecServiceAccountName) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineRunStatus) DeepCopyInto(out *PipelineRunStatus) { + *out = *in + in.Status.DeepCopyInto(&out.Status) + in.PipelineRunStatusFields.DeepCopyInto(&out.PipelineRunStatusFields) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineRunStatus. +func (in *PipelineRunStatus) DeepCopy() *PipelineRunStatus { + if in == nil { + return nil + } + out := new(PipelineRunStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineRunStatusFields) DeepCopyInto(out *PipelineRunStatusFields) { + *out = *in + if in.StartTime != nil { + in, out := &in.StartTime, &out.StartTime + *out = (*in).DeepCopy() + } + if in.CompletionTime != nil { + in, out := &in.CompletionTime, &out.CompletionTime + *out = (*in).DeepCopy() + } + if in.TaskRuns != nil { + in, out := &in.TaskRuns, &out.TaskRuns + *out = make(map[string]*PipelineRunTaskRunStatus, len(*in)) + for key, val := range *in { + var outVal *PipelineRunTaskRunStatus + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = new(PipelineRunTaskRunStatus) + (*in).DeepCopyInto(*out) + } + (*out)[key] = outVal + } + } + if in.Runs != nil { + in, out := &in.Runs, &out.Runs + *out = make(map[string]*PipelineRunRunStatus, len(*in)) + for key, val := range *in { + var outVal *PipelineRunRunStatus + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = new(PipelineRunRunStatus) + (*in).DeepCopyInto(*out) + } + (*out)[key] = outVal + } + } + if in.PipelineResults != nil { + in, out := &in.PipelineResults, &out.PipelineResults + *out = make([]PipelineRunResult, len(*in)) + copy(*out, *in) + } + if in.PipelineSpec != nil { + in, out := &in.PipelineSpec, &out.PipelineSpec + *out = new(PipelineSpec) + (*in).DeepCopyInto(*out) + } + if in.SkippedTasks != nil { + in, out := &in.SkippedTasks, &out.SkippedTasks + *out = make([]SkippedTask, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineRunStatusFields. +func (in *PipelineRunStatusFields) DeepCopy() *PipelineRunStatusFields { + if in == nil { + return nil + } + out := new(PipelineRunStatusFields) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineRunTaskRunStatus) DeepCopyInto(out *PipelineRunTaskRunStatus) { + *out = *in + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(TaskRunStatus) + (*in).DeepCopyInto(*out) + } + if in.ConditionChecks != nil { + in, out := &in.ConditionChecks, &out.ConditionChecks + *out = make(map[string]*PipelineRunConditionCheckStatus, len(*in)) + for key, val := range *in { + var outVal *PipelineRunConditionCheckStatus + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = new(PipelineRunConditionCheckStatus) + (*in).DeepCopyInto(*out) + } + (*out)[key] = outVal + } + } + if in.WhenExpressions != nil { + in, out := &in.WhenExpressions, &out.WhenExpressions + *out = make([]WhenExpression, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineRunTaskRunStatus. +func (in *PipelineRunTaskRunStatus) DeepCopy() *PipelineRunTaskRunStatus { + if in == nil { + return nil + } + out := new(PipelineRunTaskRunStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineSpec) DeepCopyInto(out *PipelineSpec) { + *out = *in + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make([]PipelineDeclaredResource, len(*in)) + copy(*out, *in) + } + if in.Tasks != nil { + in, out := &in.Tasks, &out.Tasks + *out = make([]PipelineTask, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]ParamSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Workspaces != nil { + in, out := &in.Workspaces, &out.Workspaces + *out = make([]PipelineWorkspaceDeclaration, len(*in)) + copy(*out, *in) + } + if in.Results != nil { + in, out := &in.Results, &out.Results + *out = make([]PipelineResult, len(*in)) + copy(*out, *in) + } + if in.Finally != nil { + in, out := &in.Finally, &out.Finally + *out = make([]PipelineTask, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineSpec. +func (in *PipelineSpec) DeepCopy() *PipelineSpec { + if in == nil { + return nil + } + out := new(PipelineSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineTask) DeepCopyInto(out *PipelineTask) { + *out = *in + if in.TaskRef != nil { + in, out := &in.TaskRef, &out.TaskRef + *out = new(TaskRef) + (*in).DeepCopyInto(*out) + } + if in.TaskSpec != nil { + in, out := &in.TaskSpec, &out.TaskSpec + *out = new(EmbeddedTask) + (*in).DeepCopyInto(*out) + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]PipelineTaskCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.WhenExpressions != nil { + in, out := &in.WhenExpressions, &out.WhenExpressions + *out = make(WhenExpressions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.RunAfter != nil { + in, out := &in.RunAfter, &out.RunAfter + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(PipelineTaskResources) + (*in).DeepCopyInto(*out) + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]Param, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Matrix != nil { + in, out := &in.Matrix, &out.Matrix + *out = make([]Param, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Workspaces != nil { + in, out := &in.Workspaces, &out.Workspaces + *out = make([]WorkspacePipelineTaskBinding, len(*in)) + copy(*out, *in) + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(metav1.Duration) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineTask. +func (in *PipelineTask) DeepCopy() *PipelineTask { + if in == nil { + return nil + } + out := new(PipelineTask) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineTaskCondition) DeepCopyInto(out *PipelineTaskCondition) { + *out = *in + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]Param, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make([]PipelineTaskInputResource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineTaskCondition. +func (in *PipelineTaskCondition) DeepCopy() *PipelineTaskCondition { + if in == nil { + return nil + } + out := new(PipelineTaskCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineTaskInputResource) DeepCopyInto(out *PipelineTaskInputResource) { + *out = *in + if in.From != nil { + in, out := &in.From, &out.From + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineTaskInputResource. +func (in *PipelineTaskInputResource) DeepCopy() *PipelineTaskInputResource { + if in == nil { + return nil + } + out := new(PipelineTaskInputResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in PipelineTaskList) DeepCopyInto(out *PipelineTaskList) { + { + in := &in + *out = make(PipelineTaskList, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineTaskList. +func (in PipelineTaskList) DeepCopy() PipelineTaskList { + if in == nil { + return nil + } + out := new(PipelineTaskList) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineTaskMetadata) DeepCopyInto(out *PipelineTaskMetadata) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineTaskMetadata. +func (in *PipelineTaskMetadata) DeepCopy() *PipelineTaskMetadata { + if in == nil { + return nil + } + out := new(PipelineTaskMetadata) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineTaskOutputResource) DeepCopyInto(out *PipelineTaskOutputResource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineTaskOutputResource. +func (in *PipelineTaskOutputResource) DeepCopy() *PipelineTaskOutputResource { + if in == nil { + return nil + } + out := new(PipelineTaskOutputResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineTaskParam) DeepCopyInto(out *PipelineTaskParam) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineTaskParam. +func (in *PipelineTaskParam) DeepCopy() *PipelineTaskParam { + if in == nil { + return nil + } + out := new(PipelineTaskParam) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineTaskResources) DeepCopyInto(out *PipelineTaskResources) { + *out = *in + if in.Inputs != nil { + in, out := &in.Inputs, &out.Inputs + *out = make([]PipelineTaskInputResource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Outputs != nil { + in, out := &in.Outputs, &out.Outputs + *out = make([]PipelineTaskOutputResource, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineTaskResources. +func (in *PipelineTaskResources) DeepCopy() *PipelineTaskResources { + if in == nil { + return nil + } + out := new(PipelineTaskResources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineTaskRun) DeepCopyInto(out *PipelineTaskRun) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineTaskRun. +func (in *PipelineTaskRun) DeepCopy() *PipelineTaskRun { + if in == nil { + return nil + } + out := new(PipelineTaskRun) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineTaskRunSpec) DeepCopyInto(out *PipelineTaskRunSpec) { + *out = *in + if in.TaskPodTemplate != nil { + in, out := &in.TaskPodTemplate, &out.TaskPodTemplate + *out = new(pod.Template) + (*in).DeepCopyInto(*out) + } + if in.StepOverrides != nil { + in, out := &in.StepOverrides, &out.StepOverrides + *out = make([]TaskRunStepOverride, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SidecarOverrides != nil { + in, out := &in.SidecarOverrides, &out.SidecarOverrides + *out = make([]TaskRunSidecarOverride, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineTaskRunSpec. +func (in *PipelineTaskRunSpec) DeepCopy() *PipelineTaskRunSpec { + if in == nil { + return nil + } + out := new(PipelineTaskRunSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineWorkspaceDeclaration) DeepCopyInto(out *PipelineWorkspaceDeclaration) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineWorkspaceDeclaration. +func (in *PipelineWorkspaceDeclaration) DeepCopy() *PipelineWorkspaceDeclaration { + if in == nil { + return nil + } + out := new(PipelineWorkspaceDeclaration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResolverParam) DeepCopyInto(out *ResolverParam) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolverParam. +func (in *ResolverParam) DeepCopy() *ResolverParam { + if in == nil { + return nil + } + out := new(ResolverParam) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResolverRef) DeepCopyInto(out *ResolverRef) { + *out = *in + if in.Resource != nil { + in, out := &in.Resource, &out.Resource + *out = make([]ResolverParam, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolverRef. +func (in *ResolverRef) DeepCopy() *ResolverRef { + if in == nil { + return nil + } + out := new(ResolverRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResultRef) DeepCopyInto(out *ResultRef) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResultRef. +func (in *ResultRef) DeepCopy() *ResultRef { + if in == nil { + return nil + } + out := new(ResultRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Sidecar) DeepCopyInto(out *Sidecar) { + *out = *in + in.Container.DeepCopyInto(&out.Container) + if in.Workspaces != nil { + in, out := &in.Workspaces, &out.Workspaces + *out = make([]WorkspaceUsage, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Sidecar. +func (in *Sidecar) DeepCopy() *Sidecar { + if in == nil { + return nil + } + out := new(Sidecar) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SidecarState) DeepCopyInto(out *SidecarState) { + *out = *in + in.ContainerState.DeepCopyInto(&out.ContainerState) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SidecarState. +func (in *SidecarState) DeepCopy() *SidecarState { + if in == nil { + return nil + } + out := new(SidecarState) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SkippedTask) DeepCopyInto(out *SkippedTask) { + *out = *in + if in.WhenExpressions != nil { + in, out := &in.WhenExpressions, &out.WhenExpressions + *out = make([]WhenExpression, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SkippedTask. +func (in *SkippedTask) DeepCopy() *SkippedTask { + if in == nil { + return nil + } + out := new(SkippedTask) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Step) DeepCopyInto(out *Step) { + *out = *in + in.Container.DeepCopyInto(&out.Container) + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(metav1.Duration) + **out = **in + } + if in.Workspaces != nil { + in, out := &in.Workspaces, &out.Workspaces + *out = make([]WorkspaceUsage, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Step. +func (in *Step) DeepCopy() *Step { + if in == nil { + return nil + } + out := new(Step) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StepState) DeepCopyInto(out *StepState) { + *out = *in + in.ContainerState.DeepCopyInto(&out.ContainerState) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StepState. +func (in *StepState) DeepCopy() *StepState { + if in == nil { + return nil + } + out := new(StepState) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Task) DeepCopyInto(out *Task) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Task. +func (in *Task) DeepCopy() *Task { + if in == nil { + return nil + } + out := new(Task) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Task) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskList) DeepCopyInto(out *TaskList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Task, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskList. +func (in *TaskList) DeepCopy() *TaskList { + if in == nil { + return nil + } + out := new(TaskList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TaskList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRef) DeepCopyInto(out *TaskRef) { + *out = *in + in.ResolverRef.DeepCopyInto(&out.ResolverRef) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRef. +func (in *TaskRef) DeepCopy() *TaskRef { + if in == nil { + return nil + } + out := new(TaskRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskResource) DeepCopyInto(out *TaskResource) { + *out = *in + out.ResourceDeclaration = in.ResourceDeclaration + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskResource. +func (in *TaskResource) DeepCopy() *TaskResource { + if in == nil { + return nil + } + out := new(TaskResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskResourceBinding) DeepCopyInto(out *TaskResourceBinding) { + *out = *in + in.PipelineResourceBinding.DeepCopyInto(&out.PipelineResourceBinding) + if in.Paths != nil { + in, out := &in.Paths, &out.Paths + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskResourceBinding. +func (in *TaskResourceBinding) DeepCopy() *TaskResourceBinding { + if in == nil { + return nil + } + out := new(TaskResourceBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskResources) DeepCopyInto(out *TaskResources) { + *out = *in + if in.Inputs != nil { + in, out := &in.Inputs, &out.Inputs + *out = make([]TaskResource, len(*in)) + copy(*out, *in) + } + if in.Outputs != nil { + in, out := &in.Outputs, &out.Outputs + *out = make([]TaskResource, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskResources. +func (in *TaskResources) DeepCopy() *TaskResources { + if in == nil { + return nil + } + out := new(TaskResources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskResult) DeepCopyInto(out *TaskResult) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskResult. +func (in *TaskResult) DeepCopy() *TaskResult { + if in == nil { + return nil + } + out := new(TaskResult) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRun) DeepCopyInto(out *TaskRun) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRun. +func (in *TaskRun) DeepCopy() *TaskRun { + if in == nil { + return nil + } + out := new(TaskRun) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TaskRun) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunDebug) DeepCopyInto(out *TaskRunDebug) { + *out = *in + if in.Breakpoint != nil { + in, out := &in.Breakpoint, &out.Breakpoint + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunDebug. +func (in *TaskRunDebug) DeepCopy() *TaskRunDebug { + if in == nil { + return nil + } + out := new(TaskRunDebug) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunInputs) DeepCopyInto(out *TaskRunInputs) { + *out = *in + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make([]TaskResourceBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]Param, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunInputs. +func (in *TaskRunInputs) DeepCopy() *TaskRunInputs { + if in == nil { + return nil + } + out := new(TaskRunInputs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunList) DeepCopyInto(out *TaskRunList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]TaskRun, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunList. +func (in *TaskRunList) DeepCopy() *TaskRunList { + if in == nil { + return nil + } + out := new(TaskRunList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TaskRunList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunOutputs) DeepCopyInto(out *TaskRunOutputs) { + *out = *in + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make([]TaskResourceBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunOutputs. +func (in *TaskRunOutputs) DeepCopy() *TaskRunOutputs { + if in == nil { + return nil + } + out := new(TaskRunOutputs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunResources) DeepCopyInto(out *TaskRunResources) { + *out = *in + if in.Inputs != nil { + in, out := &in.Inputs, &out.Inputs + *out = make([]TaskResourceBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Outputs != nil { + in, out := &in.Outputs, &out.Outputs + *out = make([]TaskResourceBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunResources. +func (in *TaskRunResources) DeepCopy() *TaskRunResources { + if in == nil { + return nil + } + out := new(TaskRunResources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunResult) DeepCopyInto(out *TaskRunResult) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunResult. +func (in *TaskRunResult) DeepCopy() *TaskRunResult { + if in == nil { + return nil + } + out := new(TaskRunResult) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunSidecarOverride) DeepCopyInto(out *TaskRunSidecarOverride) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunSidecarOverride. +func (in *TaskRunSidecarOverride) DeepCopy() *TaskRunSidecarOverride { + if in == nil { + return nil + } + out := new(TaskRunSidecarOverride) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunSpec) DeepCopyInto(out *TaskRunSpec) { + *out = *in + if in.Debug != nil { + in, out := &in.Debug, &out.Debug + *out = new(TaskRunDebug) + (*in).DeepCopyInto(*out) + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]Param, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(TaskRunResources) + (*in).DeepCopyInto(*out) + } + if in.TaskRef != nil { + in, out := &in.TaskRef, &out.TaskRef + *out = new(TaskRef) + (*in).DeepCopyInto(*out) + } + if in.TaskSpec != nil { + in, out := &in.TaskSpec, &out.TaskSpec + *out = new(TaskSpec) + (*in).DeepCopyInto(*out) + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(metav1.Duration) + **out = **in + } + if in.PodTemplate != nil { + in, out := &in.PodTemplate, &out.PodTemplate + *out = new(pod.Template) + (*in).DeepCopyInto(*out) + } + if in.Workspaces != nil { + in, out := &in.Workspaces, &out.Workspaces + *out = make([]WorkspaceBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.StepOverrides != nil { + in, out := &in.StepOverrides, &out.StepOverrides + *out = make([]TaskRunStepOverride, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SidecarOverrides != nil { + in, out := &in.SidecarOverrides, &out.SidecarOverrides + *out = make([]TaskRunSidecarOverride, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunSpec. +func (in *TaskRunSpec) DeepCopy() *TaskRunSpec { + if in == nil { + return nil + } + out := new(TaskRunSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunStatus) DeepCopyInto(out *TaskRunStatus) { + *out = *in + in.Status.DeepCopyInto(&out.Status) + in.TaskRunStatusFields.DeepCopyInto(&out.TaskRunStatusFields) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunStatus. +func (in *TaskRunStatus) DeepCopy() *TaskRunStatus { + if in == nil { + return nil + } + out := new(TaskRunStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunStatusFields) DeepCopyInto(out *TaskRunStatusFields) { + *out = *in + if in.StartTime != nil { + in, out := &in.StartTime, &out.StartTime + *out = (*in).DeepCopy() + } + if in.CompletionTime != nil { + in, out := &in.CompletionTime, &out.CompletionTime + *out = (*in).DeepCopy() + } + if in.Steps != nil { + in, out := &in.Steps, &out.Steps + *out = make([]StepState, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.CloudEvents != nil { + in, out := &in.CloudEvents, &out.CloudEvents + *out = make([]CloudEventDelivery, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.RetriesStatus != nil { + in, out := &in.RetriesStatus, &out.RetriesStatus + *out = make([]TaskRunStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ResourcesResult != nil { + in, out := &in.ResourcesResult, &out.ResourcesResult + *out = make([]PipelineResourceResult, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.TaskRunResults != nil { + in, out := &in.TaskRunResults, &out.TaskRunResults + *out = make([]TaskRunResult, len(*in)) + copy(*out, *in) + } + if in.Sidecars != nil { + in, out := &in.Sidecars, &out.Sidecars + *out = make([]SidecarState, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.TaskSpec != nil { + in, out := &in.TaskSpec, &out.TaskSpec + *out = new(TaskSpec) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunStatusFields. +func (in *TaskRunStatusFields) DeepCopy() *TaskRunStatusFields { + if in == nil { + return nil + } + out := new(TaskRunStatusFields) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunStepOverride) DeepCopyInto(out *TaskRunStepOverride) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunStepOverride. +func (in *TaskRunStepOverride) DeepCopy() *TaskRunStepOverride { + if in == nil { + return nil + } + out := new(TaskRunStepOverride) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskSpec) DeepCopyInto(out *TaskSpec) { + *out = *in + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(TaskResources) + (*in).DeepCopyInto(*out) + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]ParamSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Steps != nil { + in, out := &in.Steps, &out.Steps + *out = make([]Step, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]v1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.StepTemplate != nil { + in, out := &in.StepTemplate, &out.StepTemplate + *out = new(v1.Container) + (*in).DeepCopyInto(*out) + } + if in.Sidecars != nil { + in, out := &in.Sidecars, &out.Sidecars + *out = make([]Sidecar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Workspaces != nil { + in, out := &in.Workspaces, &out.Workspaces + *out = make([]WorkspaceDeclaration, len(*in)) + copy(*out, *in) + } + if in.Results != nil { + in, out := &in.Results, &out.Results + *out = make([]TaskResult, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskSpec. +func (in *TaskSpec) DeepCopy() *TaskSpec { + if in == nil { + return nil + } + out := new(TaskSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TimeoutFields) DeepCopyInto(out *TimeoutFields) { + *out = *in + if in.Pipeline != nil { + in, out := &in.Pipeline, &out.Pipeline + *out = new(metav1.Duration) + **out = **in + } + if in.Tasks != nil { + in, out := &in.Tasks, &out.Tasks + *out = new(metav1.Duration) + **out = **in + } + if in.Finally != nil { + in, out := &in.Finally, &out.Finally + *out = new(metav1.Duration) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TimeoutFields. +func (in *TimeoutFields) DeepCopy() *TimeoutFields { + if in == nil { + return nil + } + out := new(TimeoutFields) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WhenExpression) DeepCopyInto(out *WhenExpression) { + *out = *in + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WhenExpression. +func (in *WhenExpression) DeepCopy() *WhenExpression { + if in == nil { + return nil + } + out := new(WhenExpression) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in WhenExpressions) DeepCopyInto(out *WhenExpressions) { + { + in := &in + *out = make(WhenExpressions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WhenExpressions. +func (in WhenExpressions) DeepCopy() WhenExpressions { + if in == nil { + return nil + } + out := new(WhenExpressions) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceBinding) DeepCopyInto(out *WorkspaceBinding) { + *out = *in + if in.VolumeClaimTemplate != nil { + in, out := &in.VolumeClaimTemplate, &out.VolumeClaimTemplate + *out = new(v1.PersistentVolumeClaim) + (*in).DeepCopyInto(*out) + } + if in.PersistentVolumeClaim != nil { + in, out := &in.PersistentVolumeClaim, &out.PersistentVolumeClaim + *out = new(v1.PersistentVolumeClaimVolumeSource) + **out = **in + } + if in.EmptyDir != nil { + in, out := &in.EmptyDir, &out.EmptyDir + *out = new(v1.EmptyDirVolumeSource) + (*in).DeepCopyInto(*out) + } + if in.ConfigMap != nil { + in, out := &in.ConfigMap, &out.ConfigMap + *out = new(v1.ConfigMapVolumeSource) + (*in).DeepCopyInto(*out) + } + if in.Secret != nil { + in, out := &in.Secret, &out.Secret + *out = new(v1.SecretVolumeSource) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceBinding. +func (in *WorkspaceBinding) DeepCopy() *WorkspaceBinding { + if in == nil { + return nil + } + out := new(WorkspaceBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceDeclaration) DeepCopyInto(out *WorkspaceDeclaration) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceDeclaration. +func (in *WorkspaceDeclaration) DeepCopy() *WorkspaceDeclaration { + if in == nil { + return nil + } + out := new(WorkspaceDeclaration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspacePipelineTaskBinding) DeepCopyInto(out *WorkspacePipelineTaskBinding) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspacePipelineTaskBinding. +func (in *WorkspacePipelineTaskBinding) DeepCopy() *WorkspacePipelineTaskBinding { + if in == nil { + return nil + } + out := new(WorkspacePipelineTaskBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceUsage) DeepCopyInto(out *WorkspaceUsage) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceUsage. +func (in *WorkspaceUsage) DeepCopy() *WorkspaceUsage { + if in == nil { + return nil + } + out := new(WorkspaceUsage) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/doc.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/doc.go new file mode 100644 index 0000000000..b2c8398ab3 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/doc.go @@ -0,0 +1,23 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Package v1alpha1 contains API Schema definitions for the pipeline v1alpha1 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=github.com/tektoncd/pipeline/pkg/apis/resource +// +k8s:defaulter-gen=TypeMeta +// +groupName=tekton.dev +package v1alpha1 diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/pipeline_resource_defaults.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/pipeline_resource_defaults.go new file mode 100644 index 0000000000..faf03c08c1 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/pipeline_resource_defaults.go @@ -0,0 +1,34 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + + "knative.dev/pkg/apis" +) + +var _ apis.Defaultable = (*PipelineResource)(nil) + +// SetDefaults implements api.Defaultable +func (t *PipelineResource) SetDefaults(ctx context.Context) { + t.Spec.SetDefaults(ctx) +} + +// SetDefaults implements api.Defaultable +func (ts *PipelineResourceSpec) SetDefaults(ctx context.Context) { +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/pipeline_resource_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/pipeline_resource_types.go new file mode 100644 index 0000000000..208c2b9ff5 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/pipeline_resource_types.go @@ -0,0 +1,153 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// PipelineResourceType represents the type of endpoint the pipelineResource is, so that the +// controller will know this pipelineResource shouldx be fetched and optionally what +// additional metatdata should be provided for it. +type PipelineResourceType = string + +var ( + // AllowedOutputResources are the resource types that can be used as outputs + AllowedOutputResources = map[PipelineResourceType]bool{ + PipelineResourceTypeStorage: true, + PipelineResourceTypeGit: true, + } +) + +const ( + // PipelineResourceTypeGit indicates that this source is a GitHub repo. + PipelineResourceTypeGit PipelineResourceType = "git" + + // PipelineResourceTypeStorage indicates that this source is a storage blob resource. + PipelineResourceTypeStorage PipelineResourceType = "storage" + + // PipelineResourceTypeImage indicates that this source is a docker Image. + PipelineResourceTypeImage PipelineResourceType = "image" + + // PipelineResourceTypeCluster indicates that this source is a k8s cluster Image. + PipelineResourceTypeCluster PipelineResourceType = "cluster" + + // PipelineResourceTypePullRequest indicates that this source is a SCM Pull Request. + PipelineResourceTypePullRequest PipelineResourceType = "pullRequest" + + // PipelineResourceTypeCloudEvent indicates that this source is a cloud event URI + PipelineResourceTypeCloudEvent PipelineResourceType = "cloudEvent" + + // PipelineResourceTypeGCS is the subtype for the GCSResources, which is backed by a GCS blob/directory. + PipelineResourceTypeGCS PipelineResourceType = "gcs" +) + +// AllResourceTypes can be used for validation to check if a provided Resource type is one of the known types. +var AllResourceTypes = []PipelineResourceType{PipelineResourceTypeGit, PipelineResourceTypeStorage, PipelineResourceTypeImage, PipelineResourceTypeCluster, PipelineResourceTypePullRequest, PipelineResourceTypeCloudEvent} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +genclient:noStatus + +// PipelineResource describes a resource that is an input to or output from a +// Task. +// +// +k8s:openapi-gen=true +type PipelineResource struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec holds the desired state of the PipelineResource from the client + Spec PipelineResourceSpec `json:"spec,omitempty"` + + // Status is deprecated. + // It usually is used to communicate the observed state of the PipelineResource from + // the controller, but was unused as there is no controller for PipelineResource. + // +optional + Status *PipelineResourceStatus `json:"status,omitempty"` +} + +// PipelineResourceStatus does not contain anything because PipelineResources on their own +// do not have a status +// Deprecated +type PipelineResourceStatus struct { +} + +// PipelineResourceSpec defines an individual resources used in the pipeline. +type PipelineResourceSpec struct { + // Description is a user-facing description of the resource that may be + // used to populate a UI. + // +optional + Description string `json:"description,omitempty"` + Type PipelineResourceType `json:"type"` + Params []ResourceParam `json:"params"` + // Secrets to fetch to populate some of resource fields + // +optional + SecretParams []SecretParam `json:"secrets,omitempty"` +} + +// SecretParam indicates which secret can be used to populate a field of the resource +type SecretParam struct { + FieldName string `json:"fieldName"` + SecretKey string `json:"secretKey"` + SecretName string `json:"secretName"` +} + +// ResourceParam declares a string value to use for the parameter called Name, and is used in +// the specific context of PipelineResources. +type ResourceParam struct { + Name string `json:"name"` + Value string `json:"value"` +} + +// ResourceDeclaration defines an input or output PipelineResource declared as a requirement +// by another type such as a Task or Condition. The Name field will be used to refer to these +// PipelineResources within the type's definition, and when provided as an Input, the Name will be the +// path to the volume mounted containing this PipelineResource as an input (e.g. +// an input Resource named `workspace` will be mounted at `/workspace`). +type ResourceDeclaration struct { + // Name declares the name by which a resource is referenced in the + // definition. Resources may be referenced by name in the definition of a + // Task's steps. + Name string `json:"name"` + // Type is the type of this resource; + Type PipelineResourceType `json:"type"` + // Description is a user-facing description of the declared resource that may be + // used to populate a UI. + // +optional + Description string `json:"description,omitempty"` + // TargetPath is the path in workspace directory where the resource + // will be copied. + // +optional + TargetPath string `json:"targetPath,omitempty"` + // Optional declares the resource as optional. + // By default optional is set to false which makes a resource required. + // optional: true - the resource is considered optional + // optional: false - the resource is considered required (equivalent of not specifying it) + Optional bool `json:"optional,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PipelineResourceList contains a list of PipelineResources +type PipelineResourceList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []PipelineResource `json:"items"` +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/pipelineresource_validation.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/pipelineresource_validation.go new file mode 100644 index 0000000000..5feb1afe28 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/pipelineresource_validation.go @@ -0,0 +1,157 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + "net/url" + "strconv" + "strings" + + "github.com/tektoncd/pipeline/pkg/apis/validate" + "k8s.io/apimachinery/pkg/api/equality" + "knative.dev/pkg/apis" +) + +var _ apis.Validatable = (*PipelineResource)(nil) + +// Validate validates the PipelineResource's ObjectMeta and Spec +func (r *PipelineResource) Validate(ctx context.Context) *apis.FieldError { + if err := validate.ObjectMetadata(r.GetObjectMeta()); err != nil { + return err.ViaField("metadata") + } + if apis.IsInDelete(ctx) { + return nil + } + return r.Spec.Validate(ctx) +} + +// Validate validates the PipelineResourceSpec based on its type +func (rs *PipelineResourceSpec) Validate(ctx context.Context) *apis.FieldError { + if equality.Semantic.DeepEqual(rs, &PipelineResourceSpec{}) { + return apis.ErrMissingField("spec.type") + } + if rs.Type == PipelineResourceTypeCluster { + var authFound, cadataFound, clientKeyDataFound, clientCertificateDataFound, isInsecure bool + for _, param := range rs.Params { + switch { + case strings.EqualFold(param.Name, "URL"): + if err := validateURL(param.Value, "URL"); err != nil { + return err + } + case strings.EqualFold(param.Name, "Username"): + authFound = true + case strings.EqualFold(param.Name, "CAData"): + authFound = true + cadataFound = true + case strings.EqualFold(param.Name, "ClientKeyData"): + clientKeyDataFound = true + case strings.EqualFold(param.Name, "ClientCertificateData"): + clientCertificateDataFound = true + case strings.EqualFold(param.Name, "Token"): + authFound = true + case strings.EqualFold(param.Name, "insecure"): + b, _ := strconv.ParseBool(param.Value) + isInsecure = b + } + } + + for _, secret := range rs.SecretParams { + switch { + case strings.EqualFold(secret.FieldName, "Username"): + authFound = true + case strings.EqualFold(secret.FieldName, "CAData"): + authFound = true + cadataFound = true + } + } + // if both clientKeyData and clientCertificateData found + if clientCertificateDataFound && clientKeyDataFound { + authFound = true + } + + // One auth method must be supplied + if !(authFound) { + return apis.ErrMissingField("username or CAData or token param or clientKeyData or ClientCertificateData") + } + if !cadataFound && !isInsecure { + return apis.ErrMissingField("CAData param") + } + } + if rs.Type == PipelineResourceTypeStorage { + foundTypeParam := false + var location string + for _, param := range rs.Params { + switch { + case strings.EqualFold(param.Name, "type"): + if !AllowedStorageType(param.Value) { + return apis.ErrInvalidValue(param.Value, "spec.params.type") + } + foundTypeParam = true + case strings.EqualFold(param.Name, "Location"): + location = param.Value + } + } + + if !foundTypeParam { + return apis.ErrMissingField("spec.params.type") + } + if location == "" { + return apis.ErrMissingField("spec.params.location") + } + } + + if rs.Type == PipelineResourceTypePullRequest { + if err := validatePullRequest(rs); err != nil { + return err + } + } + + for _, allowedType := range AllResourceTypes { + if allowedType == rs.Type { + return nil + } + } + + return apis.ErrInvalidValue("spec.type", rs.Type) +} + +// AllowedStorageType returns true if the provided string can be used as a storage type, and false otherwise +func AllowedStorageType(gotType string) bool { + return gotType == PipelineResourceTypeGCS +} + +func validateURL(u, path string) *apis.FieldError { + if u == "" { + return nil + } + _, err := url.ParseRequestURI(u) + if err != nil { + return apis.ErrInvalidValue(u, path) + } + return nil +} + +func validatePullRequest(s *PipelineResourceSpec) *apis.FieldError { + for _, param := range s.SecretParams { + if param.FieldName != "authToken" { + return apis.ErrInvalidValue(fmt.Sprintf("invalid field name %q in secret parameter. Expected %q", param.FieldName, "authToken"), "spec.secrets.fieldName") + } + } + return nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/register.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/register.go new file mode 100644 index 0000000000..78367093f3 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/register.go @@ -0,0 +1,54 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "github.com/tektoncd/pipeline/pkg/apis/pipeline" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: pipeline.GroupName, Version: "v1alpha1"} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + + // AddToScheme adds Build types to the scheme. + AddToScheme = schemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &PipelineResource{}, + &PipelineResourceList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..5f6ff368b6 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,181 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineResource) DeepCopyInto(out *PipelineResource) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(PipelineResourceStatus) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineResource. +func (in *PipelineResource) DeepCopy() *PipelineResource { + if in == nil { + return nil + } + out := new(PipelineResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PipelineResource) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineResourceList) DeepCopyInto(out *PipelineResourceList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PipelineResource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineResourceList. +func (in *PipelineResourceList) DeepCopy() *PipelineResourceList { + if in == nil { + return nil + } + out := new(PipelineResourceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PipelineResourceList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineResourceSpec) DeepCopyInto(out *PipelineResourceSpec) { + *out = *in + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]ResourceParam, len(*in)) + copy(*out, *in) + } + if in.SecretParams != nil { + in, out := &in.SecretParams, &out.SecretParams + *out = make([]SecretParam, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineResourceSpec. +func (in *PipelineResourceSpec) DeepCopy() *PipelineResourceSpec { + if in == nil { + return nil + } + out := new(PipelineResourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineResourceStatus) DeepCopyInto(out *PipelineResourceStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineResourceStatus. +func (in *PipelineResourceStatus) DeepCopy() *PipelineResourceStatus { + if in == nil { + return nil + } + out := new(PipelineResourceStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceDeclaration) DeepCopyInto(out *ResourceDeclaration) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceDeclaration. +func (in *ResourceDeclaration) DeepCopy() *ResourceDeclaration { + if in == nil { + return nil + } + out := new(ResourceDeclaration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceParam) DeepCopyInto(out *ResourceParam) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceParam. +func (in *ResourceParam) DeepCopy() *ResourceParam { + if in == nil { + return nil + } + out := new(ResourceParam) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretParam) DeepCopyInto(out *SecretParam) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretParam. +func (in *SecretParam) DeepCopy() *SecretParam { + if in == nil { + return nil + } + out := new(SecretParam) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/run/v1alpha1/runstatus_types.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/run/v1alpha1/runstatus_types.go new file mode 100644 index 0000000000..b01e869317 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/run/v1alpha1/runstatus_types.go @@ -0,0 +1,148 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "encoding/json" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" +) + +// This package exists to avoid an import cycle between v1alpha1 and v1beta1. +// It contains common definitions needed by v1alpha1.Run and v1beta1.PipelineRun. + +// +k8s:deepcopy-gen=true + +// RunStatus defines the observed state of Run +type RunStatus struct { + duckv1.Status `json:",inline"` + + // RunStatusFields inlines the status fields. + RunStatusFields `json:",inline"` +} + +// +k8s:deepcopy-gen=true + +// RunStatusFields holds the fields of Run's status. This is defined +// separately and inlined so that other types can readily consume these fields +// via duck typing. +type RunStatusFields struct { + // StartTime is the time the build is actually started. + // +optional + StartTime *metav1.Time `json:"startTime,omitempty"` + + // CompletionTime is the time the build completed. + // +optional + CompletionTime *metav1.Time `json:"completionTime,omitempty"` + + // Results reports any output result values to be consumed by later + // tasks in a pipeline. + // +optional + Results []RunResult `json:"results,omitempty"` + + // RetriesStatus contains the history of RunStatus, in case of a retry. + // +optional + RetriesStatus []RunStatus `json:"retriesStatus,omitempty"` + + // ExtraFields holds arbitrary fields provided by the custom task + // controller. + ExtraFields runtime.RawExtension `json:"extraFields,omitempty"` +} + +// RunResult used to describe the results of a task +type RunResult struct { + // Name the given name + Name string `json:"name"` + // Value the given value of the result + Value string `json:"value"` +} + +var runCondSet = apis.NewBatchConditionSet() + +// GetCondition returns the Condition matching the given type. +func (r *RunStatus) GetCondition(t apis.ConditionType) *apis.Condition { + return runCondSet.Manage(r).GetCondition(t) +} + +// InitializeConditions will set all conditions in runCondSet to unknown for the PipelineRun +// and set the started time to the current time +func (r *RunStatus) InitializeConditions() { + started := false + if r.StartTime.IsZero() { + r.StartTime = &metav1.Time{Time: time.Now()} + started = true + } + conditionManager := runCondSet.Manage(r) + conditionManager.InitializeConditions() + // Ensure the started reason is set for the "Succeeded" condition + if started { + initialCondition := conditionManager.GetCondition(apis.ConditionSucceeded) + initialCondition.Reason = "Started" + conditionManager.SetCondition(*initialCondition) + } +} + +// SetCondition sets the condition, unsetting previous conditions with the same +// type as necessary. +func (r *RunStatus) SetCondition(newCond *apis.Condition) { + if newCond != nil { + runCondSet.Manage(r).SetCondition(*newCond) + } +} + +// MarkRunSucceeded changes the Succeeded condition to True with the provided reason and message. +func (r *RunStatus) MarkRunSucceeded(reason, messageFormat string, messageA ...interface{}) { + runCondSet.Manage(r).MarkTrueWithReason(apis.ConditionSucceeded, reason, messageFormat, messageA...) + succeeded := r.GetCondition(apis.ConditionSucceeded) + r.CompletionTime = &succeeded.LastTransitionTime.Inner +} + +// MarkRunFailed changes the Succeeded condition to False with the provided reason and message. +func (r *RunStatus) MarkRunFailed(reason, messageFormat string, messageA ...interface{}) { + runCondSet.Manage(r).MarkFalse(apis.ConditionSucceeded, reason, messageFormat, messageA...) + succeeded := r.GetCondition(apis.ConditionSucceeded) + r.CompletionTime = &succeeded.LastTransitionTime.Inner +} + +// MarkRunRunning changes the Succeeded condition to Unknown with the provided reason and message. +func (r *RunStatus) MarkRunRunning(reason, messageFormat string, messageA ...interface{}) { + runCondSet.Manage(r).MarkUnknown(apis.ConditionSucceeded, reason, messageFormat, messageA...) +} + +// DecodeExtraFields deserializes the extra fields in the Run status. +func (r *RunStatus) DecodeExtraFields(into interface{}) error { + if len(r.ExtraFields.Raw) == 0 { + return nil + } + return json.Unmarshal(r.ExtraFields.Raw, into) +} + +// EncodeExtraFields serializes the extra fields in the Run status. +func (r *RunStatus) EncodeExtraFields(from interface{}) error { + data, err := json.Marshal(from) + if err != nil { + return err + } + r.ExtraFields = runtime.RawExtension{ + Raw: data, + } + return nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/run/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/run/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..c4552c6ddd --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/run/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,77 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RunStatus) DeepCopyInto(out *RunStatus) { + *out = *in + in.Status.DeepCopyInto(&out.Status) + in.RunStatusFields.DeepCopyInto(&out.RunStatusFields) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunStatus. +func (in *RunStatus) DeepCopy() *RunStatus { + if in == nil { + return nil + } + out := new(RunStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RunStatusFields) DeepCopyInto(out *RunStatusFields) { + *out = *in + if in.StartTime != nil { + in, out := &in.StartTime, &out.StartTime + *out = (*in).DeepCopy() + } + if in.CompletionTime != nil { + in, out := &in.CompletionTime, &out.CompletionTime + *out = (*in).DeepCopy() + } + if in.Results != nil { + in, out := &in.Results, &out.Results + *out = make([]RunResult, len(*in)) + copy(*out, *in) + } + if in.RetriesStatus != nil { + in, out := &in.RetriesStatus, &out.RetriesStatus + *out = make([]RunStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.ExtraFields.DeepCopyInto(&out.ExtraFields) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunStatusFields. +func (in *RunStatusFields) DeepCopy() *RunStatusFields { + if in == nil { + return nil + } + out := new(RunStatusFields) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/apis/validate/metadata.go b/vendor/github.com/tektoncd/pipeline/pkg/apis/validate/metadata.go new file mode 100644 index 0000000000..d105c5cc36 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/apis/validate/metadata.go @@ -0,0 +1,48 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package validate + +import ( + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/validation" + "knative.dev/pkg/apis" +) + +// MaxLength is the maximum length that an object's name can be +const MaxLength = validation.DNS1123LabelMaxLength + +// ObjectMetadata validates that the given object's name is a valid DNS name and isn't longer than the max length +func ObjectMetadata(meta metav1.Object) *apis.FieldError { + name := meta.GetName() + + if err := validation.IsDNS1123Subdomain(name); len(err) > 0 { + return &apis.FieldError{ + Message: fmt.Sprintf("invalid resource name %q: must be a valid DNS label", name), + Paths: []string{"name"}, + } + } + + if len(name) > MaxLength { + return &apis.FieldError{ + Message: "Invalid resource name: length must be no more than 63 characters", + Paths: []string{"name"}, + } + } + return nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/clientset.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/clientset.go new file mode 100644 index 0000000000..f746204501 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/clientset.go @@ -0,0 +1,111 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package versioned + +import ( + "fmt" + + tektonv1alpha1 "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1" + tektonv1beta1 "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1" + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + TektonV1alpha1() tektonv1alpha1.TektonV1alpha1Interface + TektonV1beta1() tektonv1beta1.TektonV1beta1Interface +} + +// Clientset contains the clients for groups. Each group has exactly one +// version included in a Clientset. +type Clientset struct { + *discovery.DiscoveryClient + tektonV1alpha1 *tektonv1alpha1.TektonV1alpha1Client + tektonV1beta1 *tektonv1beta1.TektonV1beta1Client +} + +// TektonV1alpha1 retrieves the TektonV1alpha1Client +func (c *Clientset) TektonV1alpha1() tektonv1alpha1.TektonV1alpha1Interface { + return c.tektonV1alpha1 +} + +// TektonV1beta1 retrieves the TektonV1beta1Client +func (c *Clientset) TektonV1beta1() tektonv1beta1.TektonV1beta1Interface { + return c.tektonV1beta1 +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +// If config's RateLimiter is not set and QPS and Burst are acceptable, +// NewForConfig will generate a rate-limiter in configShallowCopy. +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + if configShallowCopy.Burst <= 0 { + return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") + } + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + var cs Clientset + var err error + cs.tektonV1alpha1, err = tektonv1alpha1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + cs.tektonV1beta1, err = tektonv1beta1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + var cs Clientset + cs.tektonV1alpha1 = tektonv1alpha1.NewForConfigOrDie(c) + cs.tektonV1beta1 = tektonv1beta1.NewForConfigOrDie(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) + return &cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.tektonV1alpha1 = tektonv1alpha1.New(c) + cs.tektonV1beta1 = tektonv1beta1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/doc.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/doc.go new file mode 100644 index 0000000000..0d13552ae2 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated clientset. +package versioned diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme/doc.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme/doc.go new file mode 100644 index 0000000000..0fb16cc056 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package contains the scheme of the automatically generated clientset. +package scheme diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme/register.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme/register.go new file mode 100644 index 0000000000..757e6eb815 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme/register.go @@ -0,0 +1,58 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package scheme + +import ( + tektonv1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + tektonv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var Scheme = runtime.NewScheme() +var Codecs = serializer.NewCodecFactory(Scheme) +var ParameterCodec = runtime.NewParameterCodec(Scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + tektonv1alpha1.AddToScheme, + tektonv1beta1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(Scheme)) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/clustertask.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/clustertask.go new file mode 100644 index 0000000000..570202483b --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/clustertask.go @@ -0,0 +1,168 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + scheme "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ClusterTasksGetter has a method to return a ClusterTaskInterface. +// A group's client should implement this interface. +type ClusterTasksGetter interface { + ClusterTasks() ClusterTaskInterface +} + +// ClusterTaskInterface has methods to work with ClusterTask resources. +type ClusterTaskInterface interface { + Create(ctx context.Context, clusterTask *v1alpha1.ClusterTask, opts v1.CreateOptions) (*v1alpha1.ClusterTask, error) + Update(ctx context.Context, clusterTask *v1alpha1.ClusterTask, opts v1.UpdateOptions) (*v1alpha1.ClusterTask, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ClusterTask, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ClusterTaskList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterTask, err error) + ClusterTaskExpansion +} + +// clusterTasks implements ClusterTaskInterface +type clusterTasks struct { + client rest.Interface +} + +// newClusterTasks returns a ClusterTasks +func newClusterTasks(c *TektonV1alpha1Client) *clusterTasks { + return &clusterTasks{ + client: c.RESTClient(), + } +} + +// Get takes name of the clusterTask, and returns the corresponding clusterTask object, and an error if there is any. +func (c *clusterTasks) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterTask, err error) { + result = &v1alpha1.ClusterTask{} + err = c.client.Get(). + Resource("clustertasks"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ClusterTasks that match those selectors. +func (c *clusterTasks) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ClusterTaskList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.ClusterTaskList{} + err = c.client.Get(). + Resource("clustertasks"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested clusterTasks. +func (c *clusterTasks) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("clustertasks"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a clusterTask and creates it. Returns the server's representation of the clusterTask, and an error, if there is any. +func (c *clusterTasks) Create(ctx context.Context, clusterTask *v1alpha1.ClusterTask, opts v1.CreateOptions) (result *v1alpha1.ClusterTask, err error) { + result = &v1alpha1.ClusterTask{} + err = c.client.Post(). + Resource("clustertasks"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterTask). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a clusterTask and updates it. Returns the server's representation of the clusterTask, and an error, if there is any. +func (c *clusterTasks) Update(ctx context.Context, clusterTask *v1alpha1.ClusterTask, opts v1.UpdateOptions) (result *v1alpha1.ClusterTask, err error) { + result = &v1alpha1.ClusterTask{} + err = c.client.Put(). + Resource("clustertasks"). + Name(clusterTask.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterTask). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the clusterTask and deletes it. Returns an error if one occurs. +func (c *clusterTasks) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Resource("clustertasks"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *clusterTasks) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("clustertasks"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched clusterTask. +func (c *clusterTasks) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterTask, err error) { + result = &v1alpha1.ClusterTask{} + err = c.client.Patch(pt). + Resource("clustertasks"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/condition.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/condition.go new file mode 100644 index 0000000000..ab26eea6eb --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/condition.go @@ -0,0 +1,178 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + scheme "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ConditionsGetter has a method to return a ConditionInterface. +// A group's client should implement this interface. +type ConditionsGetter interface { + Conditions(namespace string) ConditionInterface +} + +// ConditionInterface has methods to work with Condition resources. +type ConditionInterface interface { + Create(ctx context.Context, condition *v1alpha1.Condition, opts v1.CreateOptions) (*v1alpha1.Condition, error) + Update(ctx context.Context, condition *v1alpha1.Condition, opts v1.UpdateOptions) (*v1alpha1.Condition, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.Condition, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ConditionList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Condition, err error) + ConditionExpansion +} + +// conditions implements ConditionInterface +type conditions struct { + client rest.Interface + ns string +} + +// newConditions returns a Conditions +func newConditions(c *TektonV1alpha1Client, namespace string) *conditions { + return &conditions{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the condition, and returns the corresponding condition object, and an error if there is any. +func (c *conditions) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Condition, err error) { + result = &v1alpha1.Condition{} + err = c.client.Get(). + Namespace(c.ns). + Resource("conditions"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Conditions that match those selectors. +func (c *conditions) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ConditionList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.ConditionList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("conditions"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested conditions. +func (c *conditions) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("conditions"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a condition and creates it. Returns the server's representation of the condition, and an error, if there is any. +func (c *conditions) Create(ctx context.Context, condition *v1alpha1.Condition, opts v1.CreateOptions) (result *v1alpha1.Condition, err error) { + result = &v1alpha1.Condition{} + err = c.client.Post(). + Namespace(c.ns). + Resource("conditions"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(condition). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a condition and updates it. Returns the server's representation of the condition, and an error, if there is any. +func (c *conditions) Update(ctx context.Context, condition *v1alpha1.Condition, opts v1.UpdateOptions) (result *v1alpha1.Condition, err error) { + result = &v1alpha1.Condition{} + err = c.client.Put(). + Namespace(c.ns). + Resource("conditions"). + Name(condition.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(condition). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the condition and deletes it. Returns an error if one occurs. +func (c *conditions) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("conditions"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *conditions) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("conditions"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched condition. +func (c *conditions) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Condition, err error) { + result = &v1alpha1.Condition{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("conditions"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/doc.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/doc.go new file mode 100644 index 0000000000..69ed294b82 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/generated_expansion.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/generated_expansion.go new file mode 100644 index 0000000000..6942df0f45 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/generated_expansion.go @@ -0,0 +1,33 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +type ClusterTaskExpansion interface{} + +type ConditionExpansion interface{} + +type PipelineExpansion interface{} + +type PipelineRunExpansion interface{} + +type RunExpansion interface{} + +type TaskExpansion interface{} + +type TaskRunExpansion interface{} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipeline.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipeline.go new file mode 100644 index 0000000000..cc0a370f77 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipeline.go @@ -0,0 +1,178 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + scheme "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// PipelinesGetter has a method to return a PipelineInterface. +// A group's client should implement this interface. +type PipelinesGetter interface { + Pipelines(namespace string) PipelineInterface +} + +// PipelineInterface has methods to work with Pipeline resources. +type PipelineInterface interface { + Create(ctx context.Context, pipeline *v1alpha1.Pipeline, opts v1.CreateOptions) (*v1alpha1.Pipeline, error) + Update(ctx context.Context, pipeline *v1alpha1.Pipeline, opts v1.UpdateOptions) (*v1alpha1.Pipeline, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.Pipeline, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.PipelineList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Pipeline, err error) + PipelineExpansion +} + +// pipelines implements PipelineInterface +type pipelines struct { + client rest.Interface + ns string +} + +// newPipelines returns a Pipelines +func newPipelines(c *TektonV1alpha1Client, namespace string) *pipelines { + return &pipelines{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the pipeline, and returns the corresponding pipeline object, and an error if there is any. +func (c *pipelines) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Pipeline, err error) { + result = &v1alpha1.Pipeline{} + err = c.client.Get(). + Namespace(c.ns). + Resource("pipelines"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Pipelines that match those selectors. +func (c *pipelines) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.PipelineList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.PipelineList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("pipelines"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested pipelines. +func (c *pipelines) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("pipelines"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a pipeline and creates it. Returns the server's representation of the pipeline, and an error, if there is any. +func (c *pipelines) Create(ctx context.Context, pipeline *v1alpha1.Pipeline, opts v1.CreateOptions) (result *v1alpha1.Pipeline, err error) { + result = &v1alpha1.Pipeline{} + err = c.client.Post(). + Namespace(c.ns). + Resource("pipelines"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(pipeline). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a pipeline and updates it. Returns the server's representation of the pipeline, and an error, if there is any. +func (c *pipelines) Update(ctx context.Context, pipeline *v1alpha1.Pipeline, opts v1.UpdateOptions) (result *v1alpha1.Pipeline, err error) { + result = &v1alpha1.Pipeline{} + err = c.client.Put(). + Namespace(c.ns). + Resource("pipelines"). + Name(pipeline.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(pipeline). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the pipeline and deletes it. Returns an error if one occurs. +func (c *pipelines) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("pipelines"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *pipelines) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("pipelines"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched pipeline. +func (c *pipelines) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Pipeline, err error) { + result = &v1alpha1.Pipeline{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("pipelines"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipeline_client.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipeline_client.go new file mode 100644 index 0000000000..5797a3003a --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipeline_client.go @@ -0,0 +1,119 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme" + rest "k8s.io/client-go/rest" +) + +type TektonV1alpha1Interface interface { + RESTClient() rest.Interface + ClusterTasksGetter + ConditionsGetter + PipelinesGetter + PipelineRunsGetter + RunsGetter + TasksGetter + TaskRunsGetter +} + +// TektonV1alpha1Client is used to interact with features provided by the tekton.dev group. +type TektonV1alpha1Client struct { + restClient rest.Interface +} + +func (c *TektonV1alpha1Client) ClusterTasks() ClusterTaskInterface { + return newClusterTasks(c) +} + +func (c *TektonV1alpha1Client) Conditions(namespace string) ConditionInterface { + return newConditions(c, namespace) +} + +func (c *TektonV1alpha1Client) Pipelines(namespace string) PipelineInterface { + return newPipelines(c, namespace) +} + +func (c *TektonV1alpha1Client) PipelineRuns(namespace string) PipelineRunInterface { + return newPipelineRuns(c, namespace) +} + +func (c *TektonV1alpha1Client) Runs(namespace string) RunInterface { + return newRuns(c, namespace) +} + +func (c *TektonV1alpha1Client) Tasks(namespace string) TaskInterface { + return newTasks(c, namespace) +} + +func (c *TektonV1alpha1Client) TaskRuns(namespace string) TaskRunInterface { + return newTaskRuns(c, namespace) +} + +// NewForConfig creates a new TektonV1alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*TektonV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &TektonV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new TektonV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *TektonV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new TektonV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *TektonV1alpha1Client { + return &TektonV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *TektonV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipelinerun.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipelinerun.go new file mode 100644 index 0000000000..cdf2a1e367 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipelinerun.go @@ -0,0 +1,195 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + scheme "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// PipelineRunsGetter has a method to return a PipelineRunInterface. +// A group's client should implement this interface. +type PipelineRunsGetter interface { + PipelineRuns(namespace string) PipelineRunInterface +} + +// PipelineRunInterface has methods to work with PipelineRun resources. +type PipelineRunInterface interface { + Create(ctx context.Context, pipelineRun *v1alpha1.PipelineRun, opts v1.CreateOptions) (*v1alpha1.PipelineRun, error) + Update(ctx context.Context, pipelineRun *v1alpha1.PipelineRun, opts v1.UpdateOptions) (*v1alpha1.PipelineRun, error) + UpdateStatus(ctx context.Context, pipelineRun *v1alpha1.PipelineRun, opts v1.UpdateOptions) (*v1alpha1.PipelineRun, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.PipelineRun, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.PipelineRunList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.PipelineRun, err error) + PipelineRunExpansion +} + +// pipelineRuns implements PipelineRunInterface +type pipelineRuns struct { + client rest.Interface + ns string +} + +// newPipelineRuns returns a PipelineRuns +func newPipelineRuns(c *TektonV1alpha1Client, namespace string) *pipelineRuns { + return &pipelineRuns{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the pipelineRun, and returns the corresponding pipelineRun object, and an error if there is any. +func (c *pipelineRuns) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.PipelineRun, err error) { + result = &v1alpha1.PipelineRun{} + err = c.client.Get(). + Namespace(c.ns). + Resource("pipelineruns"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of PipelineRuns that match those selectors. +func (c *pipelineRuns) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.PipelineRunList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.PipelineRunList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("pipelineruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested pipelineRuns. +func (c *pipelineRuns) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("pipelineruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a pipelineRun and creates it. Returns the server's representation of the pipelineRun, and an error, if there is any. +func (c *pipelineRuns) Create(ctx context.Context, pipelineRun *v1alpha1.PipelineRun, opts v1.CreateOptions) (result *v1alpha1.PipelineRun, err error) { + result = &v1alpha1.PipelineRun{} + err = c.client.Post(). + Namespace(c.ns). + Resource("pipelineruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(pipelineRun). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a pipelineRun and updates it. Returns the server's representation of the pipelineRun, and an error, if there is any. +func (c *pipelineRuns) Update(ctx context.Context, pipelineRun *v1alpha1.PipelineRun, opts v1.UpdateOptions) (result *v1alpha1.PipelineRun, err error) { + result = &v1alpha1.PipelineRun{} + err = c.client.Put(). + Namespace(c.ns). + Resource("pipelineruns"). + Name(pipelineRun.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(pipelineRun). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *pipelineRuns) UpdateStatus(ctx context.Context, pipelineRun *v1alpha1.PipelineRun, opts v1.UpdateOptions) (result *v1alpha1.PipelineRun, err error) { + result = &v1alpha1.PipelineRun{} + err = c.client.Put(). + Namespace(c.ns). + Resource("pipelineruns"). + Name(pipelineRun.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(pipelineRun). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the pipelineRun and deletes it. Returns an error if one occurs. +func (c *pipelineRuns) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("pipelineruns"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *pipelineRuns) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("pipelineruns"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched pipelineRun. +func (c *pipelineRuns) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.PipelineRun, err error) { + result = &v1alpha1.PipelineRun{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("pipelineruns"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/run.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/run.go new file mode 100644 index 0000000000..417dd82120 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/run.go @@ -0,0 +1,195 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + scheme "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// RunsGetter has a method to return a RunInterface. +// A group's client should implement this interface. +type RunsGetter interface { + Runs(namespace string) RunInterface +} + +// RunInterface has methods to work with Run resources. +type RunInterface interface { + Create(ctx context.Context, run *v1alpha1.Run, opts v1.CreateOptions) (*v1alpha1.Run, error) + Update(ctx context.Context, run *v1alpha1.Run, opts v1.UpdateOptions) (*v1alpha1.Run, error) + UpdateStatus(ctx context.Context, run *v1alpha1.Run, opts v1.UpdateOptions) (*v1alpha1.Run, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.Run, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.RunList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Run, err error) + RunExpansion +} + +// runs implements RunInterface +type runs struct { + client rest.Interface + ns string +} + +// newRuns returns a Runs +func newRuns(c *TektonV1alpha1Client, namespace string) *runs { + return &runs{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the run, and returns the corresponding run object, and an error if there is any. +func (c *runs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Run, err error) { + result = &v1alpha1.Run{} + err = c.client.Get(). + Namespace(c.ns). + Resource("runs"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Runs that match those selectors. +func (c *runs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.RunList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.RunList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("runs"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested runs. +func (c *runs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("runs"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a run and creates it. Returns the server's representation of the run, and an error, if there is any. +func (c *runs) Create(ctx context.Context, run *v1alpha1.Run, opts v1.CreateOptions) (result *v1alpha1.Run, err error) { + result = &v1alpha1.Run{} + err = c.client.Post(). + Namespace(c.ns). + Resource("runs"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(run). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a run and updates it. Returns the server's representation of the run, and an error, if there is any. +func (c *runs) Update(ctx context.Context, run *v1alpha1.Run, opts v1.UpdateOptions) (result *v1alpha1.Run, err error) { + result = &v1alpha1.Run{} + err = c.client.Put(). + Namespace(c.ns). + Resource("runs"). + Name(run.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(run). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *runs) UpdateStatus(ctx context.Context, run *v1alpha1.Run, opts v1.UpdateOptions) (result *v1alpha1.Run, err error) { + result = &v1alpha1.Run{} + err = c.client.Put(). + Namespace(c.ns). + Resource("runs"). + Name(run.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(run). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the run and deletes it. Returns an error if one occurs. +func (c *runs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("runs"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *runs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("runs"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched run. +func (c *runs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Run, err error) { + result = &v1alpha1.Run{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("runs"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/task.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/task.go new file mode 100644 index 0000000000..ba5b46d27d --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/task.go @@ -0,0 +1,178 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + scheme "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// TasksGetter has a method to return a TaskInterface. +// A group's client should implement this interface. +type TasksGetter interface { + Tasks(namespace string) TaskInterface +} + +// TaskInterface has methods to work with Task resources. +type TaskInterface interface { + Create(ctx context.Context, task *v1alpha1.Task, opts v1.CreateOptions) (*v1alpha1.Task, error) + Update(ctx context.Context, task *v1alpha1.Task, opts v1.UpdateOptions) (*v1alpha1.Task, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.Task, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.TaskList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Task, err error) + TaskExpansion +} + +// tasks implements TaskInterface +type tasks struct { + client rest.Interface + ns string +} + +// newTasks returns a Tasks +func newTasks(c *TektonV1alpha1Client, namespace string) *tasks { + return &tasks{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the task, and returns the corresponding task object, and an error if there is any. +func (c *tasks) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Task, err error) { + result = &v1alpha1.Task{} + err = c.client.Get(). + Namespace(c.ns). + Resource("tasks"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Tasks that match those selectors. +func (c *tasks) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.TaskList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.TaskList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("tasks"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested tasks. +func (c *tasks) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("tasks"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a task and creates it. Returns the server's representation of the task, and an error, if there is any. +func (c *tasks) Create(ctx context.Context, task *v1alpha1.Task, opts v1.CreateOptions) (result *v1alpha1.Task, err error) { + result = &v1alpha1.Task{} + err = c.client.Post(). + Namespace(c.ns). + Resource("tasks"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(task). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a task and updates it. Returns the server's representation of the task, and an error, if there is any. +func (c *tasks) Update(ctx context.Context, task *v1alpha1.Task, opts v1.UpdateOptions) (result *v1alpha1.Task, err error) { + result = &v1alpha1.Task{} + err = c.client.Put(). + Namespace(c.ns). + Resource("tasks"). + Name(task.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(task). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the task and deletes it. Returns an error if one occurs. +func (c *tasks) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("tasks"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *tasks) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("tasks"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched task. +func (c *tasks) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Task, err error) { + result = &v1alpha1.Task{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("tasks"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/taskrun.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/taskrun.go new file mode 100644 index 0000000000..d3e7f1b704 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/taskrun.go @@ -0,0 +1,195 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + scheme "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// TaskRunsGetter has a method to return a TaskRunInterface. +// A group's client should implement this interface. +type TaskRunsGetter interface { + TaskRuns(namespace string) TaskRunInterface +} + +// TaskRunInterface has methods to work with TaskRun resources. +type TaskRunInterface interface { + Create(ctx context.Context, taskRun *v1alpha1.TaskRun, opts v1.CreateOptions) (*v1alpha1.TaskRun, error) + Update(ctx context.Context, taskRun *v1alpha1.TaskRun, opts v1.UpdateOptions) (*v1alpha1.TaskRun, error) + UpdateStatus(ctx context.Context, taskRun *v1alpha1.TaskRun, opts v1.UpdateOptions) (*v1alpha1.TaskRun, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.TaskRun, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.TaskRunList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TaskRun, err error) + TaskRunExpansion +} + +// taskRuns implements TaskRunInterface +type taskRuns struct { + client rest.Interface + ns string +} + +// newTaskRuns returns a TaskRuns +func newTaskRuns(c *TektonV1alpha1Client, namespace string) *taskRuns { + return &taskRuns{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the taskRun, and returns the corresponding taskRun object, and an error if there is any. +func (c *taskRuns) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.TaskRun, err error) { + result = &v1alpha1.TaskRun{} + err = c.client.Get(). + Namespace(c.ns). + Resource("taskruns"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of TaskRuns that match those selectors. +func (c *taskRuns) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.TaskRunList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.TaskRunList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("taskruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested taskRuns. +func (c *taskRuns) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("taskruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a taskRun and creates it. Returns the server's representation of the taskRun, and an error, if there is any. +func (c *taskRuns) Create(ctx context.Context, taskRun *v1alpha1.TaskRun, opts v1.CreateOptions) (result *v1alpha1.TaskRun, err error) { + result = &v1alpha1.TaskRun{} + err = c.client.Post(). + Namespace(c.ns). + Resource("taskruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(taskRun). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a taskRun and updates it. Returns the server's representation of the taskRun, and an error, if there is any. +func (c *taskRuns) Update(ctx context.Context, taskRun *v1alpha1.TaskRun, opts v1.UpdateOptions) (result *v1alpha1.TaskRun, err error) { + result = &v1alpha1.TaskRun{} + err = c.client.Put(). + Namespace(c.ns). + Resource("taskruns"). + Name(taskRun.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(taskRun). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *taskRuns) UpdateStatus(ctx context.Context, taskRun *v1alpha1.TaskRun, opts v1.UpdateOptions) (result *v1alpha1.TaskRun, err error) { + result = &v1alpha1.TaskRun{} + err = c.client.Put(). + Namespace(c.ns). + Resource("taskruns"). + Name(taskRun.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(taskRun). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the taskRun and deletes it. Returns an error if one occurs. +func (c *taskRuns) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("taskruns"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *taskRuns) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("taskruns"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched taskRun. +func (c *taskRuns) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TaskRun, err error) { + result = &v1alpha1.TaskRun{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("taskruns"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/clustertask.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/clustertask.go new file mode 100644 index 0000000000..80c11ff5a9 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/clustertask.go @@ -0,0 +1,168 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + "time" + + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + scheme "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ClusterTasksGetter has a method to return a ClusterTaskInterface. +// A group's client should implement this interface. +type ClusterTasksGetter interface { + ClusterTasks() ClusterTaskInterface +} + +// ClusterTaskInterface has methods to work with ClusterTask resources. +type ClusterTaskInterface interface { + Create(ctx context.Context, clusterTask *v1beta1.ClusterTask, opts v1.CreateOptions) (*v1beta1.ClusterTask, error) + Update(ctx context.Context, clusterTask *v1beta1.ClusterTask, opts v1.UpdateOptions) (*v1beta1.ClusterTask, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.ClusterTask, error) + List(ctx context.Context, opts v1.ListOptions) (*v1beta1.ClusterTaskList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.ClusterTask, err error) + ClusterTaskExpansion +} + +// clusterTasks implements ClusterTaskInterface +type clusterTasks struct { + client rest.Interface +} + +// newClusterTasks returns a ClusterTasks +func newClusterTasks(c *TektonV1beta1Client) *clusterTasks { + return &clusterTasks{ + client: c.RESTClient(), + } +} + +// Get takes name of the clusterTask, and returns the corresponding clusterTask object, and an error if there is any. +func (c *clusterTasks) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.ClusterTask, err error) { + result = &v1beta1.ClusterTask{} + err = c.client.Get(). + Resource("clustertasks"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ClusterTasks that match those selectors. +func (c *clusterTasks) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.ClusterTaskList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1beta1.ClusterTaskList{} + err = c.client.Get(). + Resource("clustertasks"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested clusterTasks. +func (c *clusterTasks) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("clustertasks"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a clusterTask and creates it. Returns the server's representation of the clusterTask, and an error, if there is any. +func (c *clusterTasks) Create(ctx context.Context, clusterTask *v1beta1.ClusterTask, opts v1.CreateOptions) (result *v1beta1.ClusterTask, err error) { + result = &v1beta1.ClusterTask{} + err = c.client.Post(). + Resource("clustertasks"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterTask). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a clusterTask and updates it. Returns the server's representation of the clusterTask, and an error, if there is any. +func (c *clusterTasks) Update(ctx context.Context, clusterTask *v1beta1.ClusterTask, opts v1.UpdateOptions) (result *v1beta1.ClusterTask, err error) { + result = &v1beta1.ClusterTask{} + err = c.client.Put(). + Resource("clustertasks"). + Name(clusterTask.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterTask). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the clusterTask and deletes it. Returns an error if one occurs. +func (c *clusterTasks) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Resource("clustertasks"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *clusterTasks) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("clustertasks"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched clusterTask. +func (c *clusterTasks) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.ClusterTask, err error) { + result = &v1beta1.ClusterTask{} + err = c.client.Patch(pt). + Resource("clustertasks"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/doc.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/doc.go new file mode 100644 index 0000000000..acfb8c0b67 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1beta1 diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/generated_expansion.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/generated_expansion.go new file mode 100644 index 0000000000..83951f9851 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/generated_expansion.go @@ -0,0 +1,29 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +type ClusterTaskExpansion interface{} + +type PipelineExpansion interface{} + +type PipelineRunExpansion interface{} + +type TaskExpansion interface{} + +type TaskRunExpansion interface{} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/pipeline.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/pipeline.go new file mode 100644 index 0000000000..1bfe8d58ac --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/pipeline.go @@ -0,0 +1,178 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + "time" + + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + scheme "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// PipelinesGetter has a method to return a PipelineInterface. +// A group's client should implement this interface. +type PipelinesGetter interface { + Pipelines(namespace string) PipelineInterface +} + +// PipelineInterface has methods to work with Pipeline resources. +type PipelineInterface interface { + Create(ctx context.Context, pipeline *v1beta1.Pipeline, opts v1.CreateOptions) (*v1beta1.Pipeline, error) + Update(ctx context.Context, pipeline *v1beta1.Pipeline, opts v1.UpdateOptions) (*v1beta1.Pipeline, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.Pipeline, error) + List(ctx context.Context, opts v1.ListOptions) (*v1beta1.PipelineList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.Pipeline, err error) + PipelineExpansion +} + +// pipelines implements PipelineInterface +type pipelines struct { + client rest.Interface + ns string +} + +// newPipelines returns a Pipelines +func newPipelines(c *TektonV1beta1Client, namespace string) *pipelines { + return &pipelines{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the pipeline, and returns the corresponding pipeline object, and an error if there is any. +func (c *pipelines) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.Pipeline, err error) { + result = &v1beta1.Pipeline{} + err = c.client.Get(). + Namespace(c.ns). + Resource("pipelines"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Pipelines that match those selectors. +func (c *pipelines) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.PipelineList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1beta1.PipelineList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("pipelines"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested pipelines. +func (c *pipelines) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("pipelines"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a pipeline and creates it. Returns the server's representation of the pipeline, and an error, if there is any. +func (c *pipelines) Create(ctx context.Context, pipeline *v1beta1.Pipeline, opts v1.CreateOptions) (result *v1beta1.Pipeline, err error) { + result = &v1beta1.Pipeline{} + err = c.client.Post(). + Namespace(c.ns). + Resource("pipelines"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(pipeline). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a pipeline and updates it. Returns the server's representation of the pipeline, and an error, if there is any. +func (c *pipelines) Update(ctx context.Context, pipeline *v1beta1.Pipeline, opts v1.UpdateOptions) (result *v1beta1.Pipeline, err error) { + result = &v1beta1.Pipeline{} + err = c.client.Put(). + Namespace(c.ns). + Resource("pipelines"). + Name(pipeline.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(pipeline). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the pipeline and deletes it. Returns an error if one occurs. +func (c *pipelines) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("pipelines"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *pipelines) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("pipelines"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched pipeline. +func (c *pipelines) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.Pipeline, err error) { + result = &v1beta1.Pipeline{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("pipelines"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/pipeline_client.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/pipeline_client.go new file mode 100644 index 0000000000..9c3335eb47 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/pipeline_client.go @@ -0,0 +1,109 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +import ( + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme" + rest "k8s.io/client-go/rest" +) + +type TektonV1beta1Interface interface { + RESTClient() rest.Interface + ClusterTasksGetter + PipelinesGetter + PipelineRunsGetter + TasksGetter + TaskRunsGetter +} + +// TektonV1beta1Client is used to interact with features provided by the tekton.dev group. +type TektonV1beta1Client struct { + restClient rest.Interface +} + +func (c *TektonV1beta1Client) ClusterTasks() ClusterTaskInterface { + return newClusterTasks(c) +} + +func (c *TektonV1beta1Client) Pipelines(namespace string) PipelineInterface { + return newPipelines(c, namespace) +} + +func (c *TektonV1beta1Client) PipelineRuns(namespace string) PipelineRunInterface { + return newPipelineRuns(c, namespace) +} + +func (c *TektonV1beta1Client) Tasks(namespace string) TaskInterface { + return newTasks(c, namespace) +} + +func (c *TektonV1beta1Client) TaskRuns(namespace string) TaskRunInterface { + return newTaskRuns(c, namespace) +} + +// NewForConfig creates a new TektonV1beta1Client for the given config. +func NewForConfig(c *rest.Config) (*TektonV1beta1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &TektonV1beta1Client{client}, nil +} + +// NewForConfigOrDie creates a new TektonV1beta1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *TektonV1beta1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new TektonV1beta1Client for the given RESTClient. +func New(c rest.Interface) *TektonV1beta1Client { + return &TektonV1beta1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1beta1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *TektonV1beta1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/pipelinerun.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/pipelinerun.go new file mode 100644 index 0000000000..5399858ff1 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/pipelinerun.go @@ -0,0 +1,195 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + "time" + + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + scheme "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// PipelineRunsGetter has a method to return a PipelineRunInterface. +// A group's client should implement this interface. +type PipelineRunsGetter interface { + PipelineRuns(namespace string) PipelineRunInterface +} + +// PipelineRunInterface has methods to work with PipelineRun resources. +type PipelineRunInterface interface { + Create(ctx context.Context, pipelineRun *v1beta1.PipelineRun, opts v1.CreateOptions) (*v1beta1.PipelineRun, error) + Update(ctx context.Context, pipelineRun *v1beta1.PipelineRun, opts v1.UpdateOptions) (*v1beta1.PipelineRun, error) + UpdateStatus(ctx context.Context, pipelineRun *v1beta1.PipelineRun, opts v1.UpdateOptions) (*v1beta1.PipelineRun, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.PipelineRun, error) + List(ctx context.Context, opts v1.ListOptions) (*v1beta1.PipelineRunList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.PipelineRun, err error) + PipelineRunExpansion +} + +// pipelineRuns implements PipelineRunInterface +type pipelineRuns struct { + client rest.Interface + ns string +} + +// newPipelineRuns returns a PipelineRuns +func newPipelineRuns(c *TektonV1beta1Client, namespace string) *pipelineRuns { + return &pipelineRuns{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the pipelineRun, and returns the corresponding pipelineRun object, and an error if there is any. +func (c *pipelineRuns) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.PipelineRun, err error) { + result = &v1beta1.PipelineRun{} + err = c.client.Get(). + Namespace(c.ns). + Resource("pipelineruns"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of PipelineRuns that match those selectors. +func (c *pipelineRuns) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.PipelineRunList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1beta1.PipelineRunList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("pipelineruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested pipelineRuns. +func (c *pipelineRuns) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("pipelineruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a pipelineRun and creates it. Returns the server's representation of the pipelineRun, and an error, if there is any. +func (c *pipelineRuns) Create(ctx context.Context, pipelineRun *v1beta1.PipelineRun, opts v1.CreateOptions) (result *v1beta1.PipelineRun, err error) { + result = &v1beta1.PipelineRun{} + err = c.client.Post(). + Namespace(c.ns). + Resource("pipelineruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(pipelineRun). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a pipelineRun and updates it. Returns the server's representation of the pipelineRun, and an error, if there is any. +func (c *pipelineRuns) Update(ctx context.Context, pipelineRun *v1beta1.PipelineRun, opts v1.UpdateOptions) (result *v1beta1.PipelineRun, err error) { + result = &v1beta1.PipelineRun{} + err = c.client.Put(). + Namespace(c.ns). + Resource("pipelineruns"). + Name(pipelineRun.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(pipelineRun). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *pipelineRuns) UpdateStatus(ctx context.Context, pipelineRun *v1beta1.PipelineRun, opts v1.UpdateOptions) (result *v1beta1.PipelineRun, err error) { + result = &v1beta1.PipelineRun{} + err = c.client.Put(). + Namespace(c.ns). + Resource("pipelineruns"). + Name(pipelineRun.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(pipelineRun). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the pipelineRun and deletes it. Returns an error if one occurs. +func (c *pipelineRuns) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("pipelineruns"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *pipelineRuns) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("pipelineruns"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched pipelineRun. +func (c *pipelineRuns) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.PipelineRun, err error) { + result = &v1beta1.PipelineRun{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("pipelineruns"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/task.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/task.go new file mode 100644 index 0000000000..a024ed714a --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/task.go @@ -0,0 +1,178 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + "time" + + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + scheme "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// TasksGetter has a method to return a TaskInterface. +// A group's client should implement this interface. +type TasksGetter interface { + Tasks(namespace string) TaskInterface +} + +// TaskInterface has methods to work with Task resources. +type TaskInterface interface { + Create(ctx context.Context, task *v1beta1.Task, opts v1.CreateOptions) (*v1beta1.Task, error) + Update(ctx context.Context, task *v1beta1.Task, opts v1.UpdateOptions) (*v1beta1.Task, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.Task, error) + List(ctx context.Context, opts v1.ListOptions) (*v1beta1.TaskList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.Task, err error) + TaskExpansion +} + +// tasks implements TaskInterface +type tasks struct { + client rest.Interface + ns string +} + +// newTasks returns a Tasks +func newTasks(c *TektonV1beta1Client, namespace string) *tasks { + return &tasks{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the task, and returns the corresponding task object, and an error if there is any. +func (c *tasks) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.Task, err error) { + result = &v1beta1.Task{} + err = c.client.Get(). + Namespace(c.ns). + Resource("tasks"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Tasks that match those selectors. +func (c *tasks) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.TaskList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1beta1.TaskList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("tasks"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested tasks. +func (c *tasks) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("tasks"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a task and creates it. Returns the server's representation of the task, and an error, if there is any. +func (c *tasks) Create(ctx context.Context, task *v1beta1.Task, opts v1.CreateOptions) (result *v1beta1.Task, err error) { + result = &v1beta1.Task{} + err = c.client.Post(). + Namespace(c.ns). + Resource("tasks"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(task). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a task and updates it. Returns the server's representation of the task, and an error, if there is any. +func (c *tasks) Update(ctx context.Context, task *v1beta1.Task, opts v1.UpdateOptions) (result *v1beta1.Task, err error) { + result = &v1beta1.Task{} + err = c.client.Put(). + Namespace(c.ns). + Resource("tasks"). + Name(task.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(task). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the task and deletes it. Returns an error if one occurs. +func (c *tasks) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("tasks"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *tasks) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("tasks"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched task. +func (c *tasks) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.Task, err error) { + result = &v1beta1.Task{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("tasks"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/taskrun.go b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/taskrun.go new file mode 100644 index 0000000000..5611be7d66 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1/taskrun.go @@ -0,0 +1,195 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + "time" + + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + scheme "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// TaskRunsGetter has a method to return a TaskRunInterface. +// A group's client should implement this interface. +type TaskRunsGetter interface { + TaskRuns(namespace string) TaskRunInterface +} + +// TaskRunInterface has methods to work with TaskRun resources. +type TaskRunInterface interface { + Create(ctx context.Context, taskRun *v1beta1.TaskRun, opts v1.CreateOptions) (*v1beta1.TaskRun, error) + Update(ctx context.Context, taskRun *v1beta1.TaskRun, opts v1.UpdateOptions) (*v1beta1.TaskRun, error) + UpdateStatus(ctx context.Context, taskRun *v1beta1.TaskRun, opts v1.UpdateOptions) (*v1beta1.TaskRun, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.TaskRun, error) + List(ctx context.Context, opts v1.ListOptions) (*v1beta1.TaskRunList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.TaskRun, err error) + TaskRunExpansion +} + +// taskRuns implements TaskRunInterface +type taskRuns struct { + client rest.Interface + ns string +} + +// newTaskRuns returns a TaskRuns +func newTaskRuns(c *TektonV1beta1Client, namespace string) *taskRuns { + return &taskRuns{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the taskRun, and returns the corresponding taskRun object, and an error if there is any. +func (c *taskRuns) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.TaskRun, err error) { + result = &v1beta1.TaskRun{} + err = c.client.Get(). + Namespace(c.ns). + Resource("taskruns"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of TaskRuns that match those selectors. +func (c *taskRuns) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.TaskRunList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1beta1.TaskRunList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("taskruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested taskRuns. +func (c *taskRuns) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("taskruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a taskRun and creates it. Returns the server's representation of the taskRun, and an error, if there is any. +func (c *taskRuns) Create(ctx context.Context, taskRun *v1beta1.TaskRun, opts v1.CreateOptions) (result *v1beta1.TaskRun, err error) { + result = &v1beta1.TaskRun{} + err = c.client.Post(). + Namespace(c.ns). + Resource("taskruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(taskRun). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a taskRun and updates it. Returns the server's representation of the taskRun, and an error, if there is any. +func (c *taskRuns) Update(ctx context.Context, taskRun *v1beta1.TaskRun, opts v1.UpdateOptions) (result *v1beta1.TaskRun, err error) { + result = &v1beta1.TaskRun{} + err = c.client.Put(). + Namespace(c.ns). + Resource("taskruns"). + Name(taskRun.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(taskRun). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *taskRuns) UpdateStatus(ctx context.Context, taskRun *v1beta1.TaskRun, opts v1.UpdateOptions) (result *v1beta1.TaskRun, err error) { + result = &v1beta1.TaskRun{} + err = c.client.Put(). + Namespace(c.ns). + Resource("taskruns"). + Name(taskRun.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(taskRun). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the taskRun and deletes it. Returns an error if one occurs. +func (c *taskRuns) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("taskruns"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *taskRuns) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("taskruns"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched taskRun. +func (c *taskRuns) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.TaskRun, err error) { + result = &v1beta1.TaskRun{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("taskruns"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/factory.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/factory.go new file mode 100644 index 0000000000..691cee85d3 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/factory.go @@ -0,0 +1,180 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + reflect "reflect" + sync "sync" + time "time" + + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" + pipeline "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// SharedInformerOption defines the functional option type for SharedInformerFactory. +type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory + +type sharedInformerFactory struct { + client versioned.Interface + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc + lock sync.Mutex + defaultResync time.Duration + customResync map[reflect.Type]time.Duration + + informers map[reflect.Type]cache.SharedIndexInformer + // startedInformers is used for tracking which informers have been started. + // This allows Start() to be called multiple times safely. + startedInformers map[reflect.Type]bool +} + +// WithCustomResyncConfig sets a custom resync period for the specified informer types. +func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + for k, v := range resyncConfig { + factory.customResync[reflect.TypeOf(k)] = v + } + return factory + } +} + +// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. +func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.tweakListOptions = tweakListOptions + return factory + } +} + +// WithNamespace limits the SharedInformerFactory to the specified namespace. +func WithNamespace(namespace string) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.namespace = namespace + return factory + } +} + +// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. +func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync) +} + +// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. +// Listers obtained via this SharedInformerFactory will be subject to the same filters +// as specified here. +// Deprecated: Please use NewSharedInformerFactoryWithOptions instead +func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) +} + +// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. +func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { + factory := &sharedInformerFactory{ + client: client, + namespace: v1.NamespaceAll, + defaultResync: defaultResync, + informers: make(map[reflect.Type]cache.SharedIndexInformer), + startedInformers: make(map[reflect.Type]bool), + customResync: make(map[reflect.Type]time.Duration), + } + + // Apply all options + for _, opt := range options { + factory = opt(factory) + } + + return factory +} + +// Start initializes all requested informers. +func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { + f.lock.Lock() + defer f.lock.Unlock() + + for informerType, informer := range f.informers { + if !f.startedInformers[informerType] { + go informer.Run(stopCh) + f.startedInformers[informerType] = true + } + } +} + +// WaitForCacheSync waits for all started informers' cache were synced. +func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { + informers := func() map[reflect.Type]cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informers := map[reflect.Type]cache.SharedIndexInformer{} + for informerType, informer := range f.informers { + if f.startedInformers[informerType] { + informers[informerType] = informer + } + } + return informers + }() + + res := map[reflect.Type]bool{} + for informType, informer := range informers { + res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) + } + return res +} + +// InternalInformerFor returns the SharedIndexInformer for obj using an internal +// client. +func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informerType := reflect.TypeOf(obj) + informer, exists := f.informers[informerType] + if exists { + return informer + } + + resyncPeriod, exists := f.customResync[informerType] + if !exists { + resyncPeriod = f.defaultResync + } + + informer = newFunc(f.client, resyncPeriod) + f.informers[informerType] = informer + + return informer +} + +// SharedInformerFactory provides shared informers for resources in all known +// API group versions. +type SharedInformerFactory interface { + internalinterfaces.SharedInformerFactory + ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + + Tekton() pipeline.Interface +} + +func (f *sharedInformerFactory) Tekton() pipeline.Interface { + return pipeline.New(f, f.namespace, f.tweakListOptions) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/generic.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/generic.go new file mode 100644 index 0000000000..3d05774d1c --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/generic.go @@ -0,0 +1,87 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + "fmt" + + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// GenericInformer is type of SharedIndexInformer which will locate and delegate to other +// sharedInformers based on type +type GenericInformer interface { + Informer() cache.SharedIndexInformer + Lister() cache.GenericLister +} + +type genericInformer struct { + informer cache.SharedIndexInformer + resource schema.GroupResource +} + +// Informer returns the SharedIndexInformer. +func (f *genericInformer) Informer() cache.SharedIndexInformer { + return f.informer +} + +// Lister returns the GenericLister. +func (f *genericInformer) Lister() cache.GenericLister { + return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) +} + +// ForResource gives generic access to a shared informer of the matching type +// TODO extend this to unknown resources with a client pool +func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { + switch resource { + // Group=tekton.dev, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithResource("clustertasks"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1alpha1().ClusterTasks().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("conditions"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1alpha1().Conditions().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("pipelines"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1alpha1().Pipelines().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("pipelineruns"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1alpha1().PipelineRuns().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("runs"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1alpha1().Runs().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("tasks"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1alpha1().Tasks().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("taskruns"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1alpha1().TaskRuns().Informer()}, nil + + // Group=tekton.dev, Version=v1beta1 + case v1beta1.SchemeGroupVersion.WithResource("clustertasks"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1beta1().ClusterTasks().Informer()}, nil + case v1beta1.SchemeGroupVersion.WithResource("pipelines"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1beta1().Pipelines().Informer()}, nil + case v1beta1.SchemeGroupVersion.WithResource("pipelineruns"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1beta1().PipelineRuns().Informer()}, nil + case v1beta1.SchemeGroupVersion.WithResource("tasks"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1beta1().Tasks().Informer()}, nil + case v1beta1.SchemeGroupVersion.WithResource("taskruns"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1beta1().TaskRuns().Informer()}, nil + + } + + return nil, fmt.Errorf("no informer found for %v", resource) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go new file mode 100644 index 0000000000..bd0f308ab9 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -0,0 +1,40 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package internalinterfaces + +import ( + time "time" + + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + cache "k8s.io/client-go/tools/cache" +) + +// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. +type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer + +// SharedInformerFactory a small interface to allow for adding an informer without an import cycle +type SharedInformerFactory interface { + Start(stopCh <-chan struct{}) + InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer +} + +// TweakListOptionsFunc is a function that transforms a v1.ListOptions. +type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/interface.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/interface.go new file mode 100644 index 0000000000..bd4ec535fb --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/interface.go @@ -0,0 +1,54 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package pipeline + +import ( + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1" + v1beta1 "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + // V1alpha1 provides access to shared informers for resources in V1alpha1. + V1alpha1() v1alpha1.Interface + // V1beta1 provides access to shared informers for resources in V1beta1. + V1beta1() v1beta1.Interface +} + +type group struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// V1alpha1 returns a new v1alpha1.Interface. +func (g *group) V1alpha1() v1alpha1.Interface { + return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) +} + +// V1beta1 returns a new v1beta1.Interface. +func (g *group) V1beta1() v1beta1.Interface { + return v1beta1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/clustertask.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/clustertask.go new file mode 100644 index 0000000000..3cdc89018a --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/clustertask.go @@ -0,0 +1,89 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + pipelinev1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ClusterTaskInformer provides access to a shared informer and lister for +// ClusterTasks. +type ClusterTaskInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.ClusterTaskLister +} + +type clusterTaskInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewClusterTaskInformer constructs a new informer for ClusterTask type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewClusterTaskInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredClusterTaskInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredClusterTaskInformer constructs a new informer for ClusterTask type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredClusterTaskInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1alpha1().ClusterTasks().List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1alpha1().ClusterTasks().Watch(context.TODO(), options) + }, + }, + &pipelinev1alpha1.ClusterTask{}, + resyncPeriod, + indexers, + ) +} + +func (f *clusterTaskInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredClusterTaskInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *clusterTaskInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&pipelinev1alpha1.ClusterTask{}, f.defaultInformer) +} + +func (f *clusterTaskInformer) Lister() v1alpha1.ClusterTaskLister { + return v1alpha1.NewClusterTaskLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/condition.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/condition.go new file mode 100644 index 0000000000..5ee887d5f4 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/condition.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + pipelinev1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ConditionInformer provides access to a shared informer and lister for +// Conditions. +type ConditionInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.ConditionLister +} + +type conditionInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewConditionInformer constructs a new informer for Condition type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewConditionInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredConditionInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredConditionInformer constructs a new informer for Condition type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredConditionInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1alpha1().Conditions(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1alpha1().Conditions(namespace).Watch(context.TODO(), options) + }, + }, + &pipelinev1alpha1.Condition{}, + resyncPeriod, + indexers, + ) +} + +func (f *conditionInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredConditionInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *conditionInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&pipelinev1alpha1.Condition{}, f.defaultInformer) +} + +func (f *conditionInformer) Lister() v1alpha1.ConditionLister { + return v1alpha1.NewConditionLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/interface.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/interface.go new file mode 100644 index 0000000000..5b9882c30d --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/interface.go @@ -0,0 +1,87 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // ClusterTasks returns a ClusterTaskInformer. + ClusterTasks() ClusterTaskInformer + // Conditions returns a ConditionInformer. + Conditions() ConditionInformer + // Pipelines returns a PipelineInformer. + Pipelines() PipelineInformer + // PipelineRuns returns a PipelineRunInformer. + PipelineRuns() PipelineRunInformer + // Runs returns a RunInformer. + Runs() RunInformer + // Tasks returns a TaskInformer. + Tasks() TaskInformer + // TaskRuns returns a TaskRunInformer. + TaskRuns() TaskRunInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// ClusterTasks returns a ClusterTaskInformer. +func (v *version) ClusterTasks() ClusterTaskInformer { + return &clusterTaskInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + +// Conditions returns a ConditionInformer. +func (v *version) Conditions() ConditionInformer { + return &conditionInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// Pipelines returns a PipelineInformer. +func (v *version) Pipelines() PipelineInformer { + return &pipelineInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// PipelineRuns returns a PipelineRunInformer. +func (v *version) PipelineRuns() PipelineRunInformer { + return &pipelineRunInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// Runs returns a RunInformer. +func (v *version) Runs() RunInformer { + return &runInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// Tasks returns a TaskInformer. +func (v *version) Tasks() TaskInformer { + return &taskInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// TaskRuns returns a TaskRunInformer. +func (v *version) TaskRuns() TaskRunInformer { + return &taskRunInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/pipeline.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/pipeline.go new file mode 100644 index 0000000000..848ec0e548 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/pipeline.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + pipelinev1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// PipelineInformer provides access to a shared informer and lister for +// Pipelines. +type PipelineInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.PipelineLister +} + +type pipelineInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewPipelineInformer constructs a new informer for Pipeline type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewPipelineInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredPipelineInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredPipelineInformer constructs a new informer for Pipeline type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredPipelineInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1alpha1().Pipelines(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1alpha1().Pipelines(namespace).Watch(context.TODO(), options) + }, + }, + &pipelinev1alpha1.Pipeline{}, + resyncPeriod, + indexers, + ) +} + +func (f *pipelineInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredPipelineInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *pipelineInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&pipelinev1alpha1.Pipeline{}, f.defaultInformer) +} + +func (f *pipelineInformer) Lister() v1alpha1.PipelineLister { + return v1alpha1.NewPipelineLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/pipelinerun.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/pipelinerun.go new file mode 100644 index 0000000000..a8d5013806 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/pipelinerun.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + pipelinev1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// PipelineRunInformer provides access to a shared informer and lister for +// PipelineRuns. +type PipelineRunInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.PipelineRunLister +} + +type pipelineRunInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewPipelineRunInformer constructs a new informer for PipelineRun type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewPipelineRunInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredPipelineRunInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredPipelineRunInformer constructs a new informer for PipelineRun type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredPipelineRunInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1alpha1().PipelineRuns(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1alpha1().PipelineRuns(namespace).Watch(context.TODO(), options) + }, + }, + &pipelinev1alpha1.PipelineRun{}, + resyncPeriod, + indexers, + ) +} + +func (f *pipelineRunInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredPipelineRunInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *pipelineRunInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&pipelinev1alpha1.PipelineRun{}, f.defaultInformer) +} + +func (f *pipelineRunInformer) Lister() v1alpha1.PipelineRunLister { + return v1alpha1.NewPipelineRunLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/run.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/run.go new file mode 100644 index 0000000000..a89050ce02 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/run.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + pipelinev1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// RunInformer provides access to a shared informer and lister for +// Runs. +type RunInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.RunLister +} + +type runInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewRunInformer constructs a new informer for Run type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewRunInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredRunInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredRunInformer constructs a new informer for Run type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredRunInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1alpha1().Runs(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1alpha1().Runs(namespace).Watch(context.TODO(), options) + }, + }, + &pipelinev1alpha1.Run{}, + resyncPeriod, + indexers, + ) +} + +func (f *runInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredRunInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *runInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&pipelinev1alpha1.Run{}, f.defaultInformer) +} + +func (f *runInformer) Lister() v1alpha1.RunLister { + return v1alpha1.NewRunLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/task.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/task.go new file mode 100644 index 0000000000..4e5320f568 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/task.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + pipelinev1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// TaskInformer provides access to a shared informer and lister for +// Tasks. +type TaskInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.TaskLister +} + +type taskInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewTaskInformer constructs a new informer for Task type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewTaskInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredTaskInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredTaskInformer constructs a new informer for Task type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredTaskInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1alpha1().Tasks(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1alpha1().Tasks(namespace).Watch(context.TODO(), options) + }, + }, + &pipelinev1alpha1.Task{}, + resyncPeriod, + indexers, + ) +} + +func (f *taskInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredTaskInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *taskInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&pipelinev1alpha1.Task{}, f.defaultInformer) +} + +func (f *taskInformer) Lister() v1alpha1.TaskLister { + return v1alpha1.NewTaskLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/taskrun.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/taskrun.go new file mode 100644 index 0000000000..41c3ae610e --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1/taskrun.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + pipelinev1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// TaskRunInformer provides access to a shared informer and lister for +// TaskRuns. +type TaskRunInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.TaskRunLister +} + +type taskRunInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewTaskRunInformer constructs a new informer for TaskRun type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewTaskRunInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredTaskRunInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredTaskRunInformer constructs a new informer for TaskRun type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredTaskRunInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1alpha1().TaskRuns(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1alpha1().TaskRuns(namespace).Watch(context.TODO(), options) + }, + }, + &pipelinev1alpha1.TaskRun{}, + resyncPeriod, + indexers, + ) +} + +func (f *taskRunInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredTaskRunInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *taskRunInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&pipelinev1alpha1.TaskRun{}, f.defaultInformer) +} + +func (f *taskRunInformer) Lister() v1alpha1.TaskRunLister { + return v1alpha1.NewTaskRunLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/clustertask.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/clustertask.go new file mode 100644 index 0000000000..8c006c47df --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/clustertask.go @@ -0,0 +1,89 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + time "time" + + pipelinev1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" + v1beta1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ClusterTaskInformer provides access to a shared informer and lister for +// ClusterTasks. +type ClusterTaskInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1beta1.ClusterTaskLister +} + +type clusterTaskInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewClusterTaskInformer constructs a new informer for ClusterTask type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewClusterTaskInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredClusterTaskInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredClusterTaskInformer constructs a new informer for ClusterTask type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredClusterTaskInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1beta1().ClusterTasks().List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1beta1().ClusterTasks().Watch(context.TODO(), options) + }, + }, + &pipelinev1beta1.ClusterTask{}, + resyncPeriod, + indexers, + ) +} + +func (f *clusterTaskInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredClusterTaskInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *clusterTaskInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&pipelinev1beta1.ClusterTask{}, f.defaultInformer) +} + +func (f *clusterTaskInformer) Lister() v1beta1.ClusterTaskLister { + return v1beta1.NewClusterTaskLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/interface.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/interface.go new file mode 100644 index 0000000000..37b4f5364c --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/interface.go @@ -0,0 +1,73 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1beta1 + +import ( + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // ClusterTasks returns a ClusterTaskInformer. + ClusterTasks() ClusterTaskInformer + // Pipelines returns a PipelineInformer. + Pipelines() PipelineInformer + // PipelineRuns returns a PipelineRunInformer. + PipelineRuns() PipelineRunInformer + // Tasks returns a TaskInformer. + Tasks() TaskInformer + // TaskRuns returns a TaskRunInformer. + TaskRuns() TaskRunInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// ClusterTasks returns a ClusterTaskInformer. +func (v *version) ClusterTasks() ClusterTaskInformer { + return &clusterTaskInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + +// Pipelines returns a PipelineInformer. +func (v *version) Pipelines() PipelineInformer { + return &pipelineInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// PipelineRuns returns a PipelineRunInformer. +func (v *version) PipelineRuns() PipelineRunInformer { + return &pipelineRunInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// Tasks returns a TaskInformer. +func (v *version) Tasks() TaskInformer { + return &taskInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// TaskRuns returns a TaskRunInformer. +func (v *version) TaskRuns() TaskRunInformer { + return &taskRunInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/pipeline.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/pipeline.go new file mode 100644 index 0000000000..1223fe0d49 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/pipeline.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + time "time" + + pipelinev1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" + v1beta1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// PipelineInformer provides access to a shared informer and lister for +// Pipelines. +type PipelineInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1beta1.PipelineLister +} + +type pipelineInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewPipelineInformer constructs a new informer for Pipeline type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewPipelineInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredPipelineInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredPipelineInformer constructs a new informer for Pipeline type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredPipelineInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1beta1().Pipelines(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1beta1().Pipelines(namespace).Watch(context.TODO(), options) + }, + }, + &pipelinev1beta1.Pipeline{}, + resyncPeriod, + indexers, + ) +} + +func (f *pipelineInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredPipelineInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *pipelineInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&pipelinev1beta1.Pipeline{}, f.defaultInformer) +} + +func (f *pipelineInformer) Lister() v1beta1.PipelineLister { + return v1beta1.NewPipelineLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/pipelinerun.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/pipelinerun.go new file mode 100644 index 0000000000..e0c03cc004 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/pipelinerun.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + time "time" + + pipelinev1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" + v1beta1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// PipelineRunInformer provides access to a shared informer and lister for +// PipelineRuns. +type PipelineRunInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1beta1.PipelineRunLister +} + +type pipelineRunInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewPipelineRunInformer constructs a new informer for PipelineRun type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewPipelineRunInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredPipelineRunInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredPipelineRunInformer constructs a new informer for PipelineRun type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredPipelineRunInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1beta1().PipelineRuns(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1beta1().PipelineRuns(namespace).Watch(context.TODO(), options) + }, + }, + &pipelinev1beta1.PipelineRun{}, + resyncPeriod, + indexers, + ) +} + +func (f *pipelineRunInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredPipelineRunInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *pipelineRunInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&pipelinev1beta1.PipelineRun{}, f.defaultInformer) +} + +func (f *pipelineRunInformer) Lister() v1beta1.PipelineRunLister { + return v1beta1.NewPipelineRunLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/task.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/task.go new file mode 100644 index 0000000000..33c525760d --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/task.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + time "time" + + pipelinev1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" + v1beta1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// TaskInformer provides access to a shared informer and lister for +// Tasks. +type TaskInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1beta1.TaskLister +} + +type taskInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewTaskInformer constructs a new informer for Task type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewTaskInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredTaskInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredTaskInformer constructs a new informer for Task type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredTaskInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1beta1().Tasks(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1beta1().Tasks(namespace).Watch(context.TODO(), options) + }, + }, + &pipelinev1beta1.Task{}, + resyncPeriod, + indexers, + ) +} + +func (f *taskInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredTaskInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *taskInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&pipelinev1beta1.Task{}, f.defaultInformer) +} + +func (f *taskInformer) Lister() v1beta1.TaskLister { + return v1beta1.NewTaskLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/taskrun.go b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/taskrun.go new file mode 100644 index 0000000000..8a026d765c --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1/taskrun.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + time "time" + + pipelinev1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" + v1beta1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// TaskRunInformer provides access to a shared informer and lister for +// TaskRuns. +type TaskRunInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1beta1.TaskRunLister +} + +type taskRunInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewTaskRunInformer constructs a new informer for TaskRun type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewTaskRunInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredTaskRunInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredTaskRunInformer constructs a new informer for TaskRun type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredTaskRunInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1beta1().TaskRuns(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1beta1().TaskRuns(namespace).Watch(context.TODO(), options) + }, + }, + &pipelinev1beta1.TaskRun{}, + resyncPeriod, + indexers, + ) +} + +func (f *taskRunInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredTaskRunInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *taskRunInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&pipelinev1beta1.TaskRun{}, f.defaultInformer) +} + +func (f *taskRunInformer) Lister() v1beta1.TaskRunLister { + return v1beta1.NewTaskRunLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/clustertask.go b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/clustertask.go new file mode 100644 index 0000000000..7344d81a74 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/clustertask.go @@ -0,0 +1,68 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ClusterTaskLister helps list ClusterTasks. +// All objects returned here must be treated as read-only. +type ClusterTaskLister interface { + // List lists all ClusterTasks in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.ClusterTask, err error) + // Get retrieves the ClusterTask from the index for a given name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.ClusterTask, error) + ClusterTaskListerExpansion +} + +// clusterTaskLister implements the ClusterTaskLister interface. +type clusterTaskLister struct { + indexer cache.Indexer +} + +// NewClusterTaskLister returns a new ClusterTaskLister. +func NewClusterTaskLister(indexer cache.Indexer) ClusterTaskLister { + return &clusterTaskLister{indexer: indexer} +} + +// List lists all ClusterTasks in the indexer. +func (s *clusterTaskLister) List(selector labels.Selector) (ret []*v1alpha1.ClusterTask, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.ClusterTask)) + }) + return ret, err +} + +// Get retrieves the ClusterTask from the index for a given name. +func (s *clusterTaskLister) Get(name string) (*v1alpha1.ClusterTask, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("clustertask"), name) + } + return obj.(*v1alpha1.ClusterTask), nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/condition.go b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/condition.go new file mode 100644 index 0000000000..ca835eb2c5 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/condition.go @@ -0,0 +1,99 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ConditionLister helps list Conditions. +// All objects returned here must be treated as read-only. +type ConditionLister interface { + // List lists all Conditions in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Condition, err error) + // Conditions returns an object that can list and get Conditions. + Conditions(namespace string) ConditionNamespaceLister + ConditionListerExpansion +} + +// conditionLister implements the ConditionLister interface. +type conditionLister struct { + indexer cache.Indexer +} + +// NewConditionLister returns a new ConditionLister. +func NewConditionLister(indexer cache.Indexer) ConditionLister { + return &conditionLister{indexer: indexer} +} + +// List lists all Conditions in the indexer. +func (s *conditionLister) List(selector labels.Selector) (ret []*v1alpha1.Condition, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Condition)) + }) + return ret, err +} + +// Conditions returns an object that can list and get Conditions. +func (s *conditionLister) Conditions(namespace string) ConditionNamespaceLister { + return conditionNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// ConditionNamespaceLister helps list and get Conditions. +// All objects returned here must be treated as read-only. +type ConditionNamespaceLister interface { + // List lists all Conditions in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Condition, err error) + // Get retrieves the Condition from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.Condition, error) + ConditionNamespaceListerExpansion +} + +// conditionNamespaceLister implements the ConditionNamespaceLister +// interface. +type conditionNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Conditions in the indexer for a given namespace. +func (s conditionNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Condition, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Condition)) + }) + return ret, err +} + +// Get retrieves the Condition from the indexer for a given namespace and name. +func (s conditionNamespaceLister) Get(name string) (*v1alpha1.Condition, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("condition"), name) + } + return obj.(*v1alpha1.Condition), nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/expansion_generated.go b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/expansion_generated.go new file mode 100644 index 0000000000..fc4bd080d6 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/expansion_generated.go @@ -0,0 +1,71 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +// ClusterTaskListerExpansion allows custom methods to be added to +// ClusterTaskLister. +type ClusterTaskListerExpansion interface{} + +// ConditionListerExpansion allows custom methods to be added to +// ConditionLister. +type ConditionListerExpansion interface{} + +// ConditionNamespaceListerExpansion allows custom methods to be added to +// ConditionNamespaceLister. +type ConditionNamespaceListerExpansion interface{} + +// PipelineListerExpansion allows custom methods to be added to +// PipelineLister. +type PipelineListerExpansion interface{} + +// PipelineNamespaceListerExpansion allows custom methods to be added to +// PipelineNamespaceLister. +type PipelineNamespaceListerExpansion interface{} + +// PipelineRunListerExpansion allows custom methods to be added to +// PipelineRunLister. +type PipelineRunListerExpansion interface{} + +// PipelineRunNamespaceListerExpansion allows custom methods to be added to +// PipelineRunNamespaceLister. +type PipelineRunNamespaceListerExpansion interface{} + +// RunListerExpansion allows custom methods to be added to +// RunLister. +type RunListerExpansion interface{} + +// RunNamespaceListerExpansion allows custom methods to be added to +// RunNamespaceLister. +type RunNamespaceListerExpansion interface{} + +// TaskListerExpansion allows custom methods to be added to +// TaskLister. +type TaskListerExpansion interface{} + +// TaskNamespaceListerExpansion allows custom methods to be added to +// TaskNamespaceLister. +type TaskNamespaceListerExpansion interface{} + +// TaskRunListerExpansion allows custom methods to be added to +// TaskRunLister. +type TaskRunListerExpansion interface{} + +// TaskRunNamespaceListerExpansion allows custom methods to be added to +// TaskRunNamespaceLister. +type TaskRunNamespaceListerExpansion interface{} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/pipeline.go b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/pipeline.go new file mode 100644 index 0000000000..80c75c9981 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/pipeline.go @@ -0,0 +1,99 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// PipelineLister helps list Pipelines. +// All objects returned here must be treated as read-only. +type PipelineLister interface { + // List lists all Pipelines in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Pipeline, err error) + // Pipelines returns an object that can list and get Pipelines. + Pipelines(namespace string) PipelineNamespaceLister + PipelineListerExpansion +} + +// pipelineLister implements the PipelineLister interface. +type pipelineLister struct { + indexer cache.Indexer +} + +// NewPipelineLister returns a new PipelineLister. +func NewPipelineLister(indexer cache.Indexer) PipelineLister { + return &pipelineLister{indexer: indexer} +} + +// List lists all Pipelines in the indexer. +func (s *pipelineLister) List(selector labels.Selector) (ret []*v1alpha1.Pipeline, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Pipeline)) + }) + return ret, err +} + +// Pipelines returns an object that can list and get Pipelines. +func (s *pipelineLister) Pipelines(namespace string) PipelineNamespaceLister { + return pipelineNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// PipelineNamespaceLister helps list and get Pipelines. +// All objects returned here must be treated as read-only. +type PipelineNamespaceLister interface { + // List lists all Pipelines in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Pipeline, err error) + // Get retrieves the Pipeline from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.Pipeline, error) + PipelineNamespaceListerExpansion +} + +// pipelineNamespaceLister implements the PipelineNamespaceLister +// interface. +type pipelineNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Pipelines in the indexer for a given namespace. +func (s pipelineNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Pipeline, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Pipeline)) + }) + return ret, err +} + +// Get retrieves the Pipeline from the indexer for a given namespace and name. +func (s pipelineNamespaceLister) Get(name string) (*v1alpha1.Pipeline, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("pipeline"), name) + } + return obj.(*v1alpha1.Pipeline), nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/pipelinerun.go b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/pipelinerun.go new file mode 100644 index 0000000000..105871d970 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/pipelinerun.go @@ -0,0 +1,99 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// PipelineRunLister helps list PipelineRuns. +// All objects returned here must be treated as read-only. +type PipelineRunLister interface { + // List lists all PipelineRuns in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.PipelineRun, err error) + // PipelineRuns returns an object that can list and get PipelineRuns. + PipelineRuns(namespace string) PipelineRunNamespaceLister + PipelineRunListerExpansion +} + +// pipelineRunLister implements the PipelineRunLister interface. +type pipelineRunLister struct { + indexer cache.Indexer +} + +// NewPipelineRunLister returns a new PipelineRunLister. +func NewPipelineRunLister(indexer cache.Indexer) PipelineRunLister { + return &pipelineRunLister{indexer: indexer} +} + +// List lists all PipelineRuns in the indexer. +func (s *pipelineRunLister) List(selector labels.Selector) (ret []*v1alpha1.PipelineRun, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.PipelineRun)) + }) + return ret, err +} + +// PipelineRuns returns an object that can list and get PipelineRuns. +func (s *pipelineRunLister) PipelineRuns(namespace string) PipelineRunNamespaceLister { + return pipelineRunNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// PipelineRunNamespaceLister helps list and get PipelineRuns. +// All objects returned here must be treated as read-only. +type PipelineRunNamespaceLister interface { + // List lists all PipelineRuns in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.PipelineRun, err error) + // Get retrieves the PipelineRun from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.PipelineRun, error) + PipelineRunNamespaceListerExpansion +} + +// pipelineRunNamespaceLister implements the PipelineRunNamespaceLister +// interface. +type pipelineRunNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all PipelineRuns in the indexer for a given namespace. +func (s pipelineRunNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.PipelineRun, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.PipelineRun)) + }) + return ret, err +} + +// Get retrieves the PipelineRun from the indexer for a given namespace and name. +func (s pipelineRunNamespaceLister) Get(name string) (*v1alpha1.PipelineRun, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("pipelinerun"), name) + } + return obj.(*v1alpha1.PipelineRun), nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/run.go b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/run.go new file mode 100644 index 0000000000..3ce91c00ba --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/run.go @@ -0,0 +1,99 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// RunLister helps list Runs. +// All objects returned here must be treated as read-only. +type RunLister interface { + // List lists all Runs in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Run, err error) + // Runs returns an object that can list and get Runs. + Runs(namespace string) RunNamespaceLister + RunListerExpansion +} + +// runLister implements the RunLister interface. +type runLister struct { + indexer cache.Indexer +} + +// NewRunLister returns a new RunLister. +func NewRunLister(indexer cache.Indexer) RunLister { + return &runLister{indexer: indexer} +} + +// List lists all Runs in the indexer. +func (s *runLister) List(selector labels.Selector) (ret []*v1alpha1.Run, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Run)) + }) + return ret, err +} + +// Runs returns an object that can list and get Runs. +func (s *runLister) Runs(namespace string) RunNamespaceLister { + return runNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// RunNamespaceLister helps list and get Runs. +// All objects returned here must be treated as read-only. +type RunNamespaceLister interface { + // List lists all Runs in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Run, err error) + // Get retrieves the Run from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.Run, error) + RunNamespaceListerExpansion +} + +// runNamespaceLister implements the RunNamespaceLister +// interface. +type runNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Runs in the indexer for a given namespace. +func (s runNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Run, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Run)) + }) + return ret, err +} + +// Get retrieves the Run from the indexer for a given namespace and name. +func (s runNamespaceLister) Get(name string) (*v1alpha1.Run, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("run"), name) + } + return obj.(*v1alpha1.Run), nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/task.go b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/task.go new file mode 100644 index 0000000000..2c60610afe --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/task.go @@ -0,0 +1,99 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// TaskLister helps list Tasks. +// All objects returned here must be treated as read-only. +type TaskLister interface { + // List lists all Tasks in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Task, err error) + // Tasks returns an object that can list and get Tasks. + Tasks(namespace string) TaskNamespaceLister + TaskListerExpansion +} + +// taskLister implements the TaskLister interface. +type taskLister struct { + indexer cache.Indexer +} + +// NewTaskLister returns a new TaskLister. +func NewTaskLister(indexer cache.Indexer) TaskLister { + return &taskLister{indexer: indexer} +} + +// List lists all Tasks in the indexer. +func (s *taskLister) List(selector labels.Selector) (ret []*v1alpha1.Task, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Task)) + }) + return ret, err +} + +// Tasks returns an object that can list and get Tasks. +func (s *taskLister) Tasks(namespace string) TaskNamespaceLister { + return taskNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// TaskNamespaceLister helps list and get Tasks. +// All objects returned here must be treated as read-only. +type TaskNamespaceLister interface { + // List lists all Tasks in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.Task, err error) + // Get retrieves the Task from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.Task, error) + TaskNamespaceListerExpansion +} + +// taskNamespaceLister implements the TaskNamespaceLister +// interface. +type taskNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Tasks in the indexer for a given namespace. +func (s taskNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Task, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Task)) + }) + return ret, err +} + +// Get retrieves the Task from the indexer for a given namespace and name. +func (s taskNamespaceLister) Get(name string) (*v1alpha1.Task, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("task"), name) + } + return obj.(*v1alpha1.Task), nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/taskrun.go b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/taskrun.go new file mode 100644 index 0000000000..933cc94286 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1/taskrun.go @@ -0,0 +1,99 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// TaskRunLister helps list TaskRuns. +// All objects returned here must be treated as read-only. +type TaskRunLister interface { + // List lists all TaskRuns in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.TaskRun, err error) + // TaskRuns returns an object that can list and get TaskRuns. + TaskRuns(namespace string) TaskRunNamespaceLister + TaskRunListerExpansion +} + +// taskRunLister implements the TaskRunLister interface. +type taskRunLister struct { + indexer cache.Indexer +} + +// NewTaskRunLister returns a new TaskRunLister. +func NewTaskRunLister(indexer cache.Indexer) TaskRunLister { + return &taskRunLister{indexer: indexer} +} + +// List lists all TaskRuns in the indexer. +func (s *taskRunLister) List(selector labels.Selector) (ret []*v1alpha1.TaskRun, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.TaskRun)) + }) + return ret, err +} + +// TaskRuns returns an object that can list and get TaskRuns. +func (s *taskRunLister) TaskRuns(namespace string) TaskRunNamespaceLister { + return taskRunNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// TaskRunNamespaceLister helps list and get TaskRuns. +// All objects returned here must be treated as read-only. +type TaskRunNamespaceLister interface { + // List lists all TaskRuns in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.TaskRun, err error) + // Get retrieves the TaskRun from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.TaskRun, error) + TaskRunNamespaceListerExpansion +} + +// taskRunNamespaceLister implements the TaskRunNamespaceLister +// interface. +type taskRunNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all TaskRuns in the indexer for a given namespace. +func (s taskRunNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.TaskRun, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.TaskRun)) + }) + return ret, err +} + +// Get retrieves the TaskRun from the indexer for a given namespace and name. +func (s taskRunNamespaceLister) Get(name string) (*v1alpha1.TaskRun, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("taskrun"), name) + } + return obj.(*v1alpha1.TaskRun), nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/clustertask.go b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/clustertask.go new file mode 100644 index 0000000000..bc88cfb67e --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/clustertask.go @@ -0,0 +1,68 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1beta1 + +import ( + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ClusterTaskLister helps list ClusterTasks. +// All objects returned here must be treated as read-only. +type ClusterTaskLister interface { + // List lists all ClusterTasks in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1beta1.ClusterTask, err error) + // Get retrieves the ClusterTask from the index for a given name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1beta1.ClusterTask, error) + ClusterTaskListerExpansion +} + +// clusterTaskLister implements the ClusterTaskLister interface. +type clusterTaskLister struct { + indexer cache.Indexer +} + +// NewClusterTaskLister returns a new ClusterTaskLister. +func NewClusterTaskLister(indexer cache.Indexer) ClusterTaskLister { + return &clusterTaskLister{indexer: indexer} +} + +// List lists all ClusterTasks in the indexer. +func (s *clusterTaskLister) List(selector labels.Selector) (ret []*v1beta1.ClusterTask, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1beta1.ClusterTask)) + }) + return ret, err +} + +// Get retrieves the ClusterTask from the index for a given name. +func (s *clusterTaskLister) Get(name string) (*v1beta1.ClusterTask, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1beta1.Resource("clustertask"), name) + } + return obj.(*v1beta1.ClusterTask), nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/expansion_generated.go b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/expansion_generated.go new file mode 100644 index 0000000000..3da70592c5 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/expansion_generated.go @@ -0,0 +1,55 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1beta1 + +// ClusterTaskListerExpansion allows custom methods to be added to +// ClusterTaskLister. +type ClusterTaskListerExpansion interface{} + +// PipelineListerExpansion allows custom methods to be added to +// PipelineLister. +type PipelineListerExpansion interface{} + +// PipelineNamespaceListerExpansion allows custom methods to be added to +// PipelineNamespaceLister. +type PipelineNamespaceListerExpansion interface{} + +// PipelineRunListerExpansion allows custom methods to be added to +// PipelineRunLister. +type PipelineRunListerExpansion interface{} + +// PipelineRunNamespaceListerExpansion allows custom methods to be added to +// PipelineRunNamespaceLister. +type PipelineRunNamespaceListerExpansion interface{} + +// TaskListerExpansion allows custom methods to be added to +// TaskLister. +type TaskListerExpansion interface{} + +// TaskNamespaceListerExpansion allows custom methods to be added to +// TaskNamespaceLister. +type TaskNamespaceListerExpansion interface{} + +// TaskRunListerExpansion allows custom methods to be added to +// TaskRunLister. +type TaskRunListerExpansion interface{} + +// TaskRunNamespaceListerExpansion allows custom methods to be added to +// TaskRunNamespaceLister. +type TaskRunNamespaceListerExpansion interface{} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/pipeline.go b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/pipeline.go new file mode 100644 index 0000000000..cd85d324fe --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/pipeline.go @@ -0,0 +1,99 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1beta1 + +import ( + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// PipelineLister helps list Pipelines. +// All objects returned here must be treated as read-only. +type PipelineLister interface { + // List lists all Pipelines in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1beta1.Pipeline, err error) + // Pipelines returns an object that can list and get Pipelines. + Pipelines(namespace string) PipelineNamespaceLister + PipelineListerExpansion +} + +// pipelineLister implements the PipelineLister interface. +type pipelineLister struct { + indexer cache.Indexer +} + +// NewPipelineLister returns a new PipelineLister. +func NewPipelineLister(indexer cache.Indexer) PipelineLister { + return &pipelineLister{indexer: indexer} +} + +// List lists all Pipelines in the indexer. +func (s *pipelineLister) List(selector labels.Selector) (ret []*v1beta1.Pipeline, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1beta1.Pipeline)) + }) + return ret, err +} + +// Pipelines returns an object that can list and get Pipelines. +func (s *pipelineLister) Pipelines(namespace string) PipelineNamespaceLister { + return pipelineNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// PipelineNamespaceLister helps list and get Pipelines. +// All objects returned here must be treated as read-only. +type PipelineNamespaceLister interface { + // List lists all Pipelines in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1beta1.Pipeline, err error) + // Get retrieves the Pipeline from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1beta1.Pipeline, error) + PipelineNamespaceListerExpansion +} + +// pipelineNamespaceLister implements the PipelineNamespaceLister +// interface. +type pipelineNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Pipelines in the indexer for a given namespace. +func (s pipelineNamespaceLister) List(selector labels.Selector) (ret []*v1beta1.Pipeline, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1beta1.Pipeline)) + }) + return ret, err +} + +// Get retrieves the Pipeline from the indexer for a given namespace and name. +func (s pipelineNamespaceLister) Get(name string) (*v1beta1.Pipeline, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1beta1.Resource("pipeline"), name) + } + return obj.(*v1beta1.Pipeline), nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/pipelinerun.go b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/pipelinerun.go new file mode 100644 index 0000000000..31b7ebb953 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/pipelinerun.go @@ -0,0 +1,99 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1beta1 + +import ( + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// PipelineRunLister helps list PipelineRuns. +// All objects returned here must be treated as read-only. +type PipelineRunLister interface { + // List lists all PipelineRuns in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1beta1.PipelineRun, err error) + // PipelineRuns returns an object that can list and get PipelineRuns. + PipelineRuns(namespace string) PipelineRunNamespaceLister + PipelineRunListerExpansion +} + +// pipelineRunLister implements the PipelineRunLister interface. +type pipelineRunLister struct { + indexer cache.Indexer +} + +// NewPipelineRunLister returns a new PipelineRunLister. +func NewPipelineRunLister(indexer cache.Indexer) PipelineRunLister { + return &pipelineRunLister{indexer: indexer} +} + +// List lists all PipelineRuns in the indexer. +func (s *pipelineRunLister) List(selector labels.Selector) (ret []*v1beta1.PipelineRun, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1beta1.PipelineRun)) + }) + return ret, err +} + +// PipelineRuns returns an object that can list and get PipelineRuns. +func (s *pipelineRunLister) PipelineRuns(namespace string) PipelineRunNamespaceLister { + return pipelineRunNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// PipelineRunNamespaceLister helps list and get PipelineRuns. +// All objects returned here must be treated as read-only. +type PipelineRunNamespaceLister interface { + // List lists all PipelineRuns in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1beta1.PipelineRun, err error) + // Get retrieves the PipelineRun from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1beta1.PipelineRun, error) + PipelineRunNamespaceListerExpansion +} + +// pipelineRunNamespaceLister implements the PipelineRunNamespaceLister +// interface. +type pipelineRunNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all PipelineRuns in the indexer for a given namespace. +func (s pipelineRunNamespaceLister) List(selector labels.Selector) (ret []*v1beta1.PipelineRun, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1beta1.PipelineRun)) + }) + return ret, err +} + +// Get retrieves the PipelineRun from the indexer for a given namespace and name. +func (s pipelineRunNamespaceLister) Get(name string) (*v1beta1.PipelineRun, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1beta1.Resource("pipelinerun"), name) + } + return obj.(*v1beta1.PipelineRun), nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/task.go b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/task.go new file mode 100644 index 0000000000..b13ba17e04 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/task.go @@ -0,0 +1,99 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1beta1 + +import ( + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// TaskLister helps list Tasks. +// All objects returned here must be treated as read-only. +type TaskLister interface { + // List lists all Tasks in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1beta1.Task, err error) + // Tasks returns an object that can list and get Tasks. + Tasks(namespace string) TaskNamespaceLister + TaskListerExpansion +} + +// taskLister implements the TaskLister interface. +type taskLister struct { + indexer cache.Indexer +} + +// NewTaskLister returns a new TaskLister. +func NewTaskLister(indexer cache.Indexer) TaskLister { + return &taskLister{indexer: indexer} +} + +// List lists all Tasks in the indexer. +func (s *taskLister) List(selector labels.Selector) (ret []*v1beta1.Task, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1beta1.Task)) + }) + return ret, err +} + +// Tasks returns an object that can list and get Tasks. +func (s *taskLister) Tasks(namespace string) TaskNamespaceLister { + return taskNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// TaskNamespaceLister helps list and get Tasks. +// All objects returned here must be treated as read-only. +type TaskNamespaceLister interface { + // List lists all Tasks in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1beta1.Task, err error) + // Get retrieves the Task from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1beta1.Task, error) + TaskNamespaceListerExpansion +} + +// taskNamespaceLister implements the TaskNamespaceLister +// interface. +type taskNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Tasks in the indexer for a given namespace. +func (s taskNamespaceLister) List(selector labels.Selector) (ret []*v1beta1.Task, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1beta1.Task)) + }) + return ret, err +} + +// Get retrieves the Task from the indexer for a given namespace and name. +func (s taskNamespaceLister) Get(name string) (*v1beta1.Task, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1beta1.Resource("task"), name) + } + return obj.(*v1beta1.Task), nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/taskrun.go b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/taskrun.go new file mode 100644 index 0000000000..310c3700f2 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1/taskrun.go @@ -0,0 +1,99 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1beta1 + +import ( + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// TaskRunLister helps list TaskRuns. +// All objects returned here must be treated as read-only. +type TaskRunLister interface { + // List lists all TaskRuns in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1beta1.TaskRun, err error) + // TaskRuns returns an object that can list and get TaskRuns. + TaskRuns(namespace string) TaskRunNamespaceLister + TaskRunListerExpansion +} + +// taskRunLister implements the TaskRunLister interface. +type taskRunLister struct { + indexer cache.Indexer +} + +// NewTaskRunLister returns a new TaskRunLister. +func NewTaskRunLister(indexer cache.Indexer) TaskRunLister { + return &taskRunLister{indexer: indexer} +} + +// List lists all TaskRuns in the indexer. +func (s *taskRunLister) List(selector labels.Selector) (ret []*v1beta1.TaskRun, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1beta1.TaskRun)) + }) + return ret, err +} + +// TaskRuns returns an object that can list and get TaskRuns. +func (s *taskRunLister) TaskRuns(namespace string) TaskRunNamespaceLister { + return taskRunNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// TaskRunNamespaceLister helps list and get TaskRuns. +// All objects returned here must be treated as read-only. +type TaskRunNamespaceLister interface { + // List lists all TaskRuns in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1beta1.TaskRun, err error) + // Get retrieves the TaskRun from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1beta1.TaskRun, error) + TaskRunNamespaceListerExpansion +} + +// taskRunNamespaceLister implements the TaskRunNamespaceLister +// interface. +type taskRunNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all TaskRuns in the indexer for a given namespace. +func (s taskRunNamespaceLister) List(selector labels.Selector) (ret []*v1beta1.TaskRun, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1beta1.TaskRun)) + }) + return ret, err +} + +// Get retrieves the TaskRun from the indexer for a given namespace and name. +func (s taskRunNamespaceLister) Get(name string) (*v1beta1.TaskRun, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1beta1.Resource("taskrun"), name) + } + return obj.(*v1beta1.TaskRun), nil +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/clientset.go b/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/clientset.go new file mode 100644 index 0000000000..4591a51c7c --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/clientset.go @@ -0,0 +1,97 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package versioned + +import ( + "fmt" + + tektonv1alpha1 "github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1" + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + TektonV1alpha1() tektonv1alpha1.TektonV1alpha1Interface +} + +// Clientset contains the clients for groups. Each group has exactly one +// version included in a Clientset. +type Clientset struct { + *discovery.DiscoveryClient + tektonV1alpha1 *tektonv1alpha1.TektonV1alpha1Client +} + +// TektonV1alpha1 retrieves the TektonV1alpha1Client +func (c *Clientset) TektonV1alpha1() tektonv1alpha1.TektonV1alpha1Interface { + return c.tektonV1alpha1 +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +// If config's RateLimiter is not set and QPS and Burst are acceptable, +// NewForConfig will generate a rate-limiter in configShallowCopy. +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + if configShallowCopy.Burst <= 0 { + return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") + } + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + var cs Clientset + var err error + cs.tektonV1alpha1, err = tektonv1alpha1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + var cs Clientset + cs.tektonV1alpha1 = tektonv1alpha1.NewForConfigOrDie(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) + return &cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.tektonV1alpha1 = tektonv1alpha1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/doc.go b/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/doc.go new file mode 100644 index 0000000000..0d13552ae2 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated clientset. +package versioned diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/scheme/doc.go b/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/scheme/doc.go new file mode 100644 index 0000000000..0fb16cc056 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/scheme/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package contains the scheme of the automatically generated clientset. +package scheme diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/scheme/register.go b/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/scheme/register.go new file mode 100644 index 0000000000..2eb6b537e5 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/scheme/register.go @@ -0,0 +1,56 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package scheme + +import ( + tektonv1alpha1 "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var Scheme = runtime.NewScheme() +var Codecs = serializer.NewCodecFactory(Scheme) +var ParameterCodec = runtime.NewParameterCodec(Scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + tektonv1alpha1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(Scheme)) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1/doc.go b/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1/doc.go new file mode 100644 index 0000000000..69ed294b82 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1/generated_expansion.go b/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1/generated_expansion.go new file mode 100644 index 0000000000..1536d99ffb --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1/generated_expansion.go @@ -0,0 +1,21 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +type PipelineResourceExpansion interface{} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1/pipelineresource.go b/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1/pipelineresource.go new file mode 100644 index 0000000000..ec03040eeb --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1/pipelineresource.go @@ -0,0 +1,178 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" + scheme "github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// PipelineResourcesGetter has a method to return a PipelineResourceInterface. +// A group's client should implement this interface. +type PipelineResourcesGetter interface { + PipelineResources(namespace string) PipelineResourceInterface +} + +// PipelineResourceInterface has methods to work with PipelineResource resources. +type PipelineResourceInterface interface { + Create(ctx context.Context, pipelineResource *v1alpha1.PipelineResource, opts v1.CreateOptions) (*v1alpha1.PipelineResource, error) + Update(ctx context.Context, pipelineResource *v1alpha1.PipelineResource, opts v1.UpdateOptions) (*v1alpha1.PipelineResource, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.PipelineResource, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.PipelineResourceList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.PipelineResource, err error) + PipelineResourceExpansion +} + +// pipelineResources implements PipelineResourceInterface +type pipelineResources struct { + client rest.Interface + ns string +} + +// newPipelineResources returns a PipelineResources +func newPipelineResources(c *TektonV1alpha1Client, namespace string) *pipelineResources { + return &pipelineResources{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the pipelineResource, and returns the corresponding pipelineResource object, and an error if there is any. +func (c *pipelineResources) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.PipelineResource, err error) { + result = &v1alpha1.PipelineResource{} + err = c.client.Get(). + Namespace(c.ns). + Resource("pipelineresources"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of PipelineResources that match those selectors. +func (c *pipelineResources) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.PipelineResourceList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.PipelineResourceList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("pipelineresources"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested pipelineResources. +func (c *pipelineResources) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("pipelineresources"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a pipelineResource and creates it. Returns the server's representation of the pipelineResource, and an error, if there is any. +func (c *pipelineResources) Create(ctx context.Context, pipelineResource *v1alpha1.PipelineResource, opts v1.CreateOptions) (result *v1alpha1.PipelineResource, err error) { + result = &v1alpha1.PipelineResource{} + err = c.client.Post(). + Namespace(c.ns). + Resource("pipelineresources"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(pipelineResource). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a pipelineResource and updates it. Returns the server's representation of the pipelineResource, and an error, if there is any. +func (c *pipelineResources) Update(ctx context.Context, pipelineResource *v1alpha1.PipelineResource, opts v1.UpdateOptions) (result *v1alpha1.PipelineResource, err error) { + result = &v1alpha1.PipelineResource{} + err = c.client.Put(). + Namespace(c.ns). + Resource("pipelineresources"). + Name(pipelineResource.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(pipelineResource). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the pipelineResource and deletes it. Returns an error if one occurs. +func (c *pipelineResources) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("pipelineresources"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *pipelineResources) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("pipelineresources"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched pipelineResource. +func (c *pipelineResources) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.PipelineResource, err error) { + result = &v1alpha1.PipelineResource{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("pipelineresources"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1/resource_client.go b/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1/resource_client.go new file mode 100644 index 0000000000..1340710d34 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1/resource_client.go @@ -0,0 +1,89 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" + "github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/scheme" + rest "k8s.io/client-go/rest" +) + +type TektonV1alpha1Interface interface { + RESTClient() rest.Interface + PipelineResourcesGetter +} + +// TektonV1alpha1Client is used to interact with features provided by the tekton.dev group. +type TektonV1alpha1Client struct { + restClient rest.Interface +} + +func (c *TektonV1alpha1Client) PipelineResources(namespace string) PipelineResourceInterface { + return newPipelineResources(c, namespace) +} + +// NewForConfig creates a new TektonV1alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*TektonV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &TektonV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new TektonV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *TektonV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new TektonV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *TektonV1alpha1Client { + return &TektonV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *TektonV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/list/diff.go b/vendor/github.com/tektoncd/pipeline/pkg/list/diff.go new file mode 100644 index 0000000000..d5461fa28b --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/list/diff.go @@ -0,0 +1,52 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package list + +import "fmt" + +// IsSame will return an error indicating if there are extra or missing strings +// between the required and provided strings, or will return no error if the two +// contain the same values. +func IsSame(required, provided []string) error { + missing := DiffLeft(required, provided) + if len(missing) > 0 { + return fmt.Errorf("didn't provide required values: %s", missing) + } + extra := DiffLeft(provided, required) + if len(extra) > 0 { + return fmt.Errorf("provided extra values: %s", extra) + } + return nil +} + +// DiffLeft will return all strings which are in the left slice of strings but +// not in the right. +func DiffLeft(left, right []string) []string { + extra := []string{} + for _, s := range left { + found := false + for _, s2 := range right { + if s == s2 { + found = true + } + } + if !found { + extra = append(extra, s) + } + } + return extra +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/reconciler/pipeline/dag/dag.go b/vendor/github.com/tektoncd/pipeline/pkg/reconciler/pipeline/dag/dag.go new file mode 100644 index 0000000000..7bec296049 --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/reconciler/pipeline/dag/dag.go @@ -0,0 +1,218 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package dag + +import ( + "errors" + "fmt" + "strings" + + "github.com/tektoncd/pipeline/pkg/list" + "k8s.io/apimachinery/pkg/util/sets" +) + +// Task is an interface for all types that could be in a DAG +type Task interface { + HashKey() string + Deps() []string +} + +// Tasks is an interface for lists of types that could be in a DAG +type Tasks interface { + Items() []Task +} + +// Node represents a Task in a pipeline. +type Node struct { + // Task represent the PipelineTask in Pipeline + Task Task + // Prev represent all the Previous task Nodes for the current Task + Prev []*Node + // Next represent all the Next task Nodes for the current Task + Next []*Node +} + +// Graph represents the Pipeline Graph +type Graph struct { + // Nodes represent map of PipelineTask name to Node in Pipeline Graph + Nodes map[string]*Node +} + +// Returns an empty Pipeline Graph +func newGraph() *Graph { + return &Graph{Nodes: map[string]*Node{}} +} + +func (g *Graph) addPipelineTask(t Task) (*Node, error) { + if _, ok := g.Nodes[t.HashKey()]; ok { + return nil, errors.New("duplicate pipeline task") + } + newNode := &Node{ + Task: t, + } + g.Nodes[t.HashKey()] = newNode + return newNode, nil +} + +// Build returns a valid pipeline Graph. Returns error if the pipeline is invalid +func Build(tasks Tasks, deps map[string][]string) (*Graph, error) { + d := newGraph() + + // Add all Tasks mentioned in the `PipelineSpec` + for _, pt := range tasks.Items() { + if _, err := d.addPipelineTask(pt); err != nil { + return nil, fmt.Errorf("task %s is already present in Graph, can't add it again: %w", pt.HashKey(), err) + } + } + + // Process all from and runAfter constraints to add task dependency + for pt, taskDeps := range deps { + for _, previousTask := range taskDeps { + if err := addLink(pt, previousTask, d.Nodes); err != nil { + return nil, fmt.Errorf("couldn't add link between %s and %s: %w", pt, previousTask, err) + } + } + } + return d, nil +} + +// GetSchedulable returns a set of PipelineTask names that can be scheduled, +// given a list of successfully finished doneTasks. It returns tasks which have +// all dependencies marked as done, and thus can be scheduled. If the specified +// doneTasks are invalid (i.e. if it is indicated that a Task is done, but the +// previous Tasks are not done), an error is returned. +func GetSchedulable(g *Graph, doneTasks ...string) (sets.String, error) { + roots := getRoots(g) + tm := sets.NewString(doneTasks...) + d := sets.NewString() + + visited := sets.NewString() + for _, root := range roots { + schedulable := findSchedulable(root, visited, tm) + for _, task := range schedulable { + d.Insert(task.HashKey()) + } + } + + var visitedNames []string + for v := range visited { + visitedNames = append(visitedNames, v) + } + + notVisited := list.DiffLeft(doneTasks, visitedNames) + if len(notVisited) > 0 { + return nil, fmt.Errorf("invalid list of done tasks; some tasks were indicated completed without ancestors being done: %v", notVisited) + } + + return d, nil +} + +func linkPipelineTasks(prev *Node, next *Node) error { + // Check for self cycle + if prev.Task.HashKey() == next.Task.HashKey() { + return fmt.Errorf("cycle detected; task %q depends on itself", next.Task.HashKey()) + } + // Check if we are adding cycles. + path := []string{next.Task.HashKey(), prev.Task.HashKey()} + if err := lookForNode(prev.Prev, path, next.Task.HashKey()); err != nil { + return fmt.Errorf("cycle detected: %w", err) + } + next.Prev = append(next.Prev, prev) + prev.Next = append(prev.Next, next) + return nil +} + +func lookForNode(nodes []*Node, path []string, next string) error { + for _, n := range nodes { + path = append(path, n.Task.HashKey()) + if n.Task.HashKey() == next { + return errors.New(getVisitedPath(path)) + } + if err := lookForNode(n.Prev, path, next); err != nil { + return err + } + } + return nil +} + +func getVisitedPath(path []string) string { + // Reverse the path since we traversed the Graph using prev pointers. + for i := len(path)/2 - 1; i >= 0; i-- { + opp := len(path) - 1 - i + path[i], path[opp] = path[opp], path[i] + } + return strings.Join(path, " -> ") +} + +func addLink(pt string, previousTask string, nodes map[string]*Node) error { + prev, ok := nodes[previousTask] + if !ok { + return fmt.Errorf("task %s depends on %s but %s wasn't present in Pipeline", pt, previousTask, previousTask) + } + next := nodes[pt] + if err := linkPipelineTasks(prev, next); err != nil { + return fmt.Errorf("couldn't create link from %s to %s: %w", prev.Task.HashKey(), next.Task.HashKey(), err) + } + return nil +} + +func getRoots(g *Graph) []*Node { + n := []*Node{} + for _, node := range g.Nodes { + if len(node.Prev) == 0 { + n = append(n, node) + } + } + return n +} + +func findSchedulable(n *Node, visited sets.String, doneTasks sets.String) []Task { + if visited.Has(n.Task.HashKey()) { + return []Task{} + } + visited.Insert(n.Task.HashKey()) + if doneTasks.Has(n.Task.HashKey()) { + schedulable := []Task{} + // This one is done! Take note of it and look at the next candidate + for _, next := range n.Next { + if _, ok := visited[next.Task.HashKey()]; !ok { + schedulable = append(schedulable, findSchedulable(next, visited, doneTasks)...) + } + } + return schedulable + } + // This one isn't done! Return it if it's schedulable + if isSchedulable(doneTasks, n.Prev) { + // FIXME(vdemeester) + return []Task{n.Task} + } + // This one isn't done, but it also isn't ready to schedule + return []Task{} +} + +func isSchedulable(doneTasks sets.String, prevs []*Node) bool { + if len(prevs) == 0 { + return true + } + collected := []string{} + for _, n := range prevs { + if doneTasks.Has(n.Task.HashKey()) { + collected = append(collected, n.Task.HashKey()) + } + } + return len(collected) == len(prevs) +} diff --git a/vendor/github.com/tektoncd/pipeline/pkg/substitution/substitution.go b/vendor/github.com/tektoncd/pipeline/pkg/substitution/substitution.go new file mode 100644 index 0000000000..1f8c9110fb --- /dev/null +++ b/vendor/github.com/tektoncd/pipeline/pkg/substitution/substitution.go @@ -0,0 +1,209 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package substitution + +import ( + "fmt" + "regexp" + "strings" + + "k8s.io/apimachinery/pkg/util/sets" + "knative.dev/pkg/apis" +) + +const parameterSubstitution = `[_a-zA-Z][_a-zA-Z0-9.-]*(\[\*\])?` + +const braceMatchingRegex = "(\\$(\\(%s\\.(?P%s)\\)))" + +// ValidateVariable makes sure all variables in the provided string are known +func ValidateVariable(name, value, prefix, locationName, path string, vars sets.String) *apis.FieldError { + if vs, present := extractVariablesFromString(value, prefix); present { + for _, v := range vs { + v = strings.TrimSuffix(v, "[*]") + if !vars.Has(v) { + return &apis.FieldError{ + Message: fmt.Sprintf("non-existent variable in %q for %s %s", value, locationName, name), + Paths: []string{path + "." + name}, + } + } + } + } + return nil +} + +// ValidateVariableP makes sure all variables for a parameter in the provided string are known +func ValidateVariableP(value, prefix string, vars sets.String) *apis.FieldError { + if vs, present := extractVariablesFromString(value, prefix); present { + for _, v := range vs { + v = strings.TrimSuffix(v, "[*]") + if !vars.Has(v) { + return &apis.FieldError{ + Message: fmt.Sprintf("non-existent variable in %q", value), + // Empty path is required to make the `ViaField`, … work + Paths: []string{""}, + } + } + } + } + return nil +} + +// ValidateVariableProhibited verifies that variables matching the relevant string expressions do not reference any of the names present in vars. +func ValidateVariableProhibited(name, value, prefix, locationName, path string, vars sets.String) *apis.FieldError { + if vs, present := extractVariablesFromString(value, prefix); present { + for _, v := range vs { + v = strings.TrimSuffix(v, "[*]") + if vars.Has(v) { + return &apis.FieldError{ + Message: fmt.Sprintf("variable type invalid in %q for %s %s", value, locationName, name), + Paths: []string{path + "." + name}, + } + } + } + } + return nil +} + +// ValidateVariableProhibitedP verifies that variables for a parameter matching the relevant string expressions do not reference any of the names present in vars. +func ValidateVariableProhibitedP(value, prefix string, vars sets.String) *apis.FieldError { + if vs, present := extractVariablesFromString(value, prefix); present { + for _, v := range vs { + v = strings.TrimSuffix(v, "[*]") + if vars.Has(v) { + return &apis.FieldError{ + Message: fmt.Sprintf("variable type invalid in %q", value), + // Empty path is required to make the `ViaField`, … work + Paths: []string{""}, + } + } + } + } + return nil +} + +// ValidateVariableIsolated verifies that variables matching the relevant string expressions are completely isolated if present. +func ValidateVariableIsolated(name, value, prefix, locationName, path string, vars sets.String) *apis.FieldError { + if vs, present := extractVariablesFromString(value, prefix); present { + firstMatch, _ := extractExpressionFromString(value, prefix) + for _, v := range vs { + v = strings.TrimSuffix(v, "[*]") + if vars.Has(v) { + if len(value) != len(firstMatch) { + return &apis.FieldError{ + Message: fmt.Sprintf("variable is not properly isolated in %q for %s %s", value, locationName, name), + Paths: []string{path + "." + name}, + } + } + } + } + } + return nil +} + +// ValidateVariableIsolatedP verifies that variables matching the relevant string expressions are completely isolated if present. +func ValidateVariableIsolatedP(value, prefix string, vars sets.String) *apis.FieldError { + if vs, present := extractVariablesFromString(value, prefix); present { + firstMatch, _ := extractExpressionFromString(value, prefix) + for _, v := range vs { + v = strings.TrimSuffix(v, "[*]") + if vars.Has(v) { + if len(value) != len(firstMatch) { + return &apis.FieldError{ + Message: fmt.Sprintf("variable is not properly isolated in %q", value), + // Empty path is required to make the `ViaField`, … work + Paths: []string{""}, + } + } + } + } + } + return nil +} + +// Extract a the first full string expressions found (e.g "$(input.params.foo)"). Return +// "" and false if nothing is found. +func extractExpressionFromString(s, prefix string) (string, bool) { + pattern := fmt.Sprintf(braceMatchingRegex, prefix, parameterSubstitution) + re := regexp.MustCompile(pattern) + match := re.FindStringSubmatch(s) + if match == nil { + return "", false + } + return match[0], true +} + +func extractVariablesFromString(s, prefix string) ([]string, bool) { + pattern := fmt.Sprintf(braceMatchingRegex, prefix, parameterSubstitution) + re := regexp.MustCompile(pattern) + matches := re.FindAllStringSubmatch(s, -1) + if len(matches) == 0 { + return []string{}, false + } + vars := make([]string, len(matches)) + for i, match := range matches { + groups := matchGroups(match, re) + // foo -> foo + // foo.bar -> foo + // foo.bar.baz -> foo + vars[i] = strings.SplitN(groups["var"], ".", 2)[0] + } + return vars, true +} + +func matchGroups(matches []string, pattern *regexp.Regexp) map[string]string { + groups := make(map[string]string) + for i, name := range pattern.SubexpNames()[1:] { + groups[name] = matches[i+1] + } + return groups +} + +// ApplyReplacements applies string replacements +func ApplyReplacements(in string, replacements map[string]string) string { + replacementsList := []string{} + for k, v := range replacements { + replacementsList = append(replacementsList, fmt.Sprintf("$(%s)", k), v) + } + // strings.Replacer does all replacements in one pass, preventing multiple replacements + // See #2093 for an explanation on why we need to do this. + replacer := strings.NewReplacer(replacementsList...) + return replacer.Replace(in) +} + +// ApplyArrayReplacements takes an input string, and output an array of strings related to possible arrayReplacements. If there aren't any +// areas where the input can be split up via arrayReplacements, then just return an array with a single element, +// which is ApplyReplacements(in, replacements). +func ApplyArrayReplacements(in string, stringReplacements map[string]string, arrayReplacements map[string][]string) []string { + for k, v := range arrayReplacements { + stringToReplace := fmt.Sprintf("$(%s)", k) + + // If the input string matches a replacement's key (without padding characters), return the corresponding array. + // Note that the webhook should prevent all instances where this could evaluate to false. + if (strings.Count(in, stringToReplace) == 1) && len(in) == len(stringToReplace) { + return v + } + + // same replace logic for star array expressions + starStringtoReplace := fmt.Sprintf("$(%s[*])", k) + if (strings.Count(in, starStringtoReplace) == 1) && len(in) == len(starStringtoReplace) { + return v + } + } + + // Otherwise return a size-1 array containing the input string with standard stringReplacements applied. + return []string{ApplyReplacements(in, stringReplacements)} +} diff --git a/vendor/github.com/tektoncd/triggers/LICENSE b/vendor/github.com/tektoncd/triggers/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/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/vendor/github.com/tektoncd/triggers/pkg/apis/config/allowed_types.go b/vendor/github.com/tektoncd/triggers/pkg/apis/config/allowed_types.go new file mode 100644 index 0000000000..9e26e25847 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/config/allowed_types.go @@ -0,0 +1,30 @@ +package config + +import ( + pipelinev1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + pipelinev1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var Decoder runtime.Decoder + +// TODO(dibyom): We should have a way of configuring this instead of an init function? +func init() { + scheme := runtime.NewScheme() + utilruntime.Must(pipelinev1alpha1.AddToScheme(scheme)) + utilruntime.Must(pipelinev1beta1.AddToScheme(scheme)) + codec := serializer.NewCodecFactory(scheme) + Decoder = codec.UniversalDecoder( + pipelinev1alpha1.SchemeGroupVersion, + pipelinev1beta1.SchemeGroupVersion, + ) +} + +// EnsureAllowedType returns nil if the resourceTemplate has an apiVersion +// and kind field set to one of the allowed ones. +func EnsureAllowedType(rt runtime.RawExtension) error { + _, err := runtime.Decode(Decoder, rt.Raw) + return err +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/config/default.go b/vendor/github.com/tektoncd/triggers/pkg/apis/config/default.go new file mode 100644 index 0000000000..a946323687 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/config/default.go @@ -0,0 +1,74 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package config + +import ( + "os" + + corev1 "k8s.io/api/core/v1" +) + +const ( + defaultServiceAccountKey = "default-service-account" + DefaultServiceAccountValue = "default" +) + +// Defaults holds the default configurations +// +k8s:deepcopy-gen=true +type Defaults struct { + DefaultServiceAccount string +} + +// GetDefaultsConfigName returns the name of the configmap containing all +// defined defaults. +func GetDefaultsConfigName() string { + if e := os.Getenv("CONFIG_DEFAULTS_NAME"); e != "" { + return e + } + return "config-defaults-triggers" +} + +// Equals returns true if two Configs are identical +func (cfg *Defaults) Equals(other *Defaults) bool { + if cfg == nil && other == nil { + return true + } + + if cfg == nil || other == nil { + return false + } + + return other.DefaultServiceAccount == cfg.DefaultServiceAccount +} + +// NewDefaultsFromMap returns a Config given a map corresponding to a ConfigMap +func NewDefaultsFromMap(cfgMap map[string]string) (*Defaults, error) { + tc := Defaults{ + DefaultServiceAccount: DefaultServiceAccountValue, + } + + if defaultServiceAccount, ok := cfgMap[defaultServiceAccountKey]; ok { + tc.DefaultServiceAccount = defaultServiceAccount + } + + return &tc, nil +} + +// NewDefaultsFromConfigMap returns a Config for the given configmap +func NewDefaultsFromConfigMap(config *corev1.ConfigMap) (*Defaults, error) { + return NewDefaultsFromMap(config.Data) +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/config/doc.go b/vendor/github.com/tektoncd/triggers/pkg/apis/config/doc.go new file mode 100644 index 0000000000..9d5d164660 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/config/doc.go @@ -0,0 +1,17 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package config diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/config/feature_flags.go b/vendor/github.com/tektoncd/triggers/pkg/apis/config/feature_flags.go new file mode 100644 index 0000000000..f6d84cbd9f --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/config/feature_flags.go @@ -0,0 +1,103 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package config + +import ( + "fmt" + "os" + "regexp" + "strings" + + corev1 "k8s.io/api/core/v1" +) + +const ( + StableAPIFieldValue = "stable" + AlphaAPIFieldValue = "alpha" + enableAPIFieldsKey = "enable-api-fields" + DefaultEnableAPIFields = StableAPIFieldValue + + labelsExclusionPattern = "labels-exclusion-pattern" +) + +// FeatureFlags holds the features configurations +// +k8s:deepcopy-gen=true +type FeatureFlags struct { + // EnableAPIFields determines which gated features are enabled. + // Acceptable values are "stable" or "alpha". Defaults to "stable" + EnableAPIFields string + // LabelsExclusionPattern determines the regex pattern to use to exclude + // labels being propagated to resources created by the EventListener + LabelsExclusionPattern string +} + +// GetFeatureFlagsConfigName returns the name of the configmap containing all +// feature flags. +func GetFeatureFlagsConfigName() string { + if e := os.Getenv("CONFIG_FEATURE_FLAGS_NAME"); e != "" { + return e + } + return "feature-flags-triggers" +} + +// NewFeatureFlagsFromMap returns a Config given a map corresponding to a ConfigMap +func NewFeatureFlagsFromMap(cfgMap map[string]string) (*FeatureFlags, error) { + tc := FeatureFlags{} + var err error + if tc.EnableAPIFields, err = getEnabledAPI(cfgMap); err != nil { + return nil, err + } + + if tc.LabelsExclusionPattern, err = getLabelsExclusionPattern(cfgMap); err != nil { + return nil, err + } + + return &tc, nil +} + +// getLabelsExclusionPattern gets the "labels-exclusion-pattern" flag based on the content of a given map. +// If the feature gate is not defined then we ignore it, if the pattern is not +// valid regex then we return error +func getLabelsExclusionPattern(cfgMap map[string]string) (string, error) { + + if pattern, ok := cfgMap[labelsExclusionPattern]; ok { + if _, err := regexp.Compile(pattern); err != nil { + return "", fmt.Errorf("invalid value for feature flag %q: %q", labelsExclusionPattern, pattern) + } + return pattern, nil + } + + return "", nil +} + +// getEnabledAPI gets the "enable-api-fields" flag based on the content of a given map. +// If the feature gate is invalid or missing then an error is returned. +func getEnabledAPI(cfgMap map[string]string) (string, error) { + value := DefaultEnableAPIFields + if cfg, ok := cfgMap[enableAPIFieldsKey]; ok { + value = strings.ToLower(cfg) + } + if value != AlphaAPIFieldValue && value != StableAPIFieldValue { + return "", fmt.Errorf("invalid value for feature flag %q: %q", enableAPIFieldsKey, value) + } + return value, nil +} + +// NewFeatureFlagsFromConfigMap returns a Config for the given configmap +func NewFeatureFlagsFromConfigMap(config *corev1.ConfigMap) (*FeatureFlags, error) { + return NewFeatureFlagsFromMap(config.Data) +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/config/store.go b/vendor/github.com/tektoncd/triggers/pkg/apis/config/store.go new file mode 100644 index 0000000000..84c836ef6c --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/config/store.go @@ -0,0 +1,106 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package config + +import ( + "context" + + "knative.dev/pkg/configmap" +) + +type cfgKey struct{} + +// Config holds the collection of configurations that we attach to contexts. +// +k8s:deepcopy-gen=false +type Config struct { + Defaults *Defaults + FeatureFlags *FeatureFlags +} + +// FromContext extracts a Config from the provided context. +func FromContext(ctx context.Context) *Config { + x, ok := ctx.Value(cfgKey{}).(*Config) + if ok { + return x + } + return nil +} + +// FromContextOrDefaults is like FromContext, but when no Config is attached it +// returns a Config populated with the defaults for each of the Config fields. +func FromContextOrDefaults(ctx context.Context) *Config { + if cfg := FromContext(ctx); cfg != nil { + return cfg + } + defaults, _ := NewDefaultsFromMap(map[string]string{}) + featureFlags, _ := NewFeatureFlagsFromMap(map[string]string{}) + return &Config{ + Defaults: defaults, + FeatureFlags: featureFlags, + } +} + +// ToContext attaches the provided Config to the provided context, returning the +// new context with the Config attached. +func ToContext(ctx context.Context, c *Config) context.Context { + return context.WithValue(ctx, cfgKey{}, c) +} + +// Store is a typed wrapper around configmap.Untyped store to handle our configmaps. +// +k8s:deepcopy-gen=false +type Store struct { + *configmap.UntypedStore +} + +// NewStore creates a new store of Configs and optionally calls functions when ConfigMaps are updated. +func NewStore(logger configmap.Logger, onAfterStore ...func(name string, value interface{})) *Store { + store := &Store{ + UntypedStore: configmap.NewUntypedStore( + "defaults/features/artifacts", + logger, + configmap.Constructors{ + GetFeatureFlagsConfigName(): NewFeatureFlagsFromConfigMap, + GetDefaultsConfigName(): NewDefaultsFromConfigMap, + }, + onAfterStore..., + ), + } + + return store +} + +// ToContext attaches the current Config state to the provided context. +func (s *Store) ToContext(ctx context.Context) context.Context { + return ToContext(ctx, s.Load()) +} + +// Load creates a Config from the current config state of the Store. +func (s *Store) Load() *Config { + defaults := s.UntypedLoad(GetDefaultsConfigName()) + if defaults == nil { + defaults, _ = NewDefaultsFromMap(map[string]string{}) + } + featureFlags := s.UntypedLoad(GetFeatureFlagsConfigName()) + if featureFlags == nil { + featureFlags, _ = NewFeatureFlagsFromMap(map[string]string{}) + } + + return &Config{ + Defaults: defaults.(*Defaults).DeepCopy(), + FeatureFlags: featureFlags.(*FeatureFlags).DeepCopy(), + } +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/config/zz_generated.deepcopy.go b/vendor/github.com/tektoncd/triggers/pkg/apis/config/zz_generated.deepcopy.go new file mode 100644 index 0000000000..fd5084233e --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/config/zz_generated.deepcopy.go @@ -0,0 +1,54 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package config + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Defaults) DeepCopyInto(out *Defaults) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Defaults. +func (in *Defaults) DeepCopy() *Defaults { + if in == nil { + return nil + } + out := new(Defaults) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FeatureFlags) DeepCopyInto(out *FeatureFlags) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeatureFlags. +func (in *FeatureFlags) DeepCopy() *FeatureFlags { + if in == nil { + return nil + } + out := new(FeatureFlags) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/contexts/contexts.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/contexts/contexts.go new file mode 100644 index 0000000000..72c25971e4 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/contexts/contexts.go @@ -0,0 +1,38 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package contexts + +import "context" + +// upgradeViaDefaultingKey is used as the key in a context.Context. +// This variable doesn't really matter, so it can be a total random name. +// Setting this key indicates that default values for a resource should be +// updated to new values. This is used to ensure non breaking updates when +// a default value of a resource changes or when a field is removed. +type upgradeViaDefaultingKey struct{} + +// WithUpgradeViaDefaulting sets the upgradeViaDefaultingKey on the context +// indicating that default values for a resource should be updated to new values. +func WithUpgradeViaDefaulting(ctx context.Context) context.Context { + return context.WithValue(ctx, upgradeViaDefaultingKey{}, struct{}{}) +} + +// IsUpgradeViaDefaulting checks if the upgradeViaDefaultingKey is set on +// the context. +func IsUpgradeViaDefaulting(ctx context.Context) bool { + return ctx.Value(upgradeViaDefaultingKey{}) != nil +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/register.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/register.go new file mode 100644 index 0000000000..f3b2a0612a --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/register.go @@ -0,0 +1,18 @@ +package triggers + +const ( + // GroupName is the Kubernetes resource group name for Tekton types. + GroupName = "triggers.tekton.dev" + + // EventListenerLabelKey is used as the label identifier for an EventListener. + EventListenerLabelKey = "/eventlistener" + + // EventIDLabelKey is used as the label identifier for an EventListener event. + EventIDLabelKey = "/triggers-eventid" + + // TriggerLabelKey is used as the label identifier for a Trigger + TriggerLabelKey = "/trigger" + + // TriggerGroupLabelKey is used as a label identifier for a TriggerGroup + TriggerGroupLabelKey = "/triggergroup" +) diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_interceptor_defaults.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_interceptor_defaults.go new file mode 100644 index 0000000000..198d18556d --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_interceptor_defaults.go @@ -0,0 +1,34 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + + "github.com/tektoncd/triggers/pkg/apis/triggers/contexts" +) + +// SetDefaults sets the defaults on the object. +func (it *ClusterInterceptor) SetDefaults(ctx context.Context) { + if contexts.IsUpgradeViaDefaulting(ctx) { + if svc := it.Spec.ClientConfig.Service; svc != nil { + if svc.Port == nil { + svc.Port = &defaultPort + } + } + } +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_interceptor_types.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_interceptor_types.go new file mode 100644 index 0000000000..6e6f86ff71 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_interceptor_types.go @@ -0,0 +1,125 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "errors" + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" +) + +// Check that EventListener may be validated and defaulted. +var _ apis.Validatable = (*ClusterInterceptor)(nil) +var _ apis.Defaultable = (*ClusterInterceptor)(nil) + +// +genclient +// +genclient:nonNamespaced +// +genreconciler:krshapedlogic=false +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +k8s:openapi-gen=true +// ClusterInterceptor describes a pluggable interceptor including configuration +// such as the fields it accepts and its deployment address. The type is based on +// the Validating/MutatingWebhookConfiguration types for configuring AdmissionWebhooks +type ClusterInterceptor struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ClusterInterceptorSpec `json:"spec"` + // +optional + Status ClusterInterceptorStatus `json:"status"` +} + +// ClusterInterceptorSpec describes the Spec for an ClusterInterceptor +type ClusterInterceptorSpec struct { + ClientConfig ClientConfig `json:"clientConfig"` +} + +// ClusterInterceptorStatus holds the status of the ClusterInterceptor +// +k8s:deepcopy-gen=true +type ClusterInterceptorStatus struct { + duckv1.Status `json:",inline"` + + // ClusterInterceptor is Addressable and exposes the URL where the Interceptor is running + duckv1.AddressStatus `json:",inline"` +} + +// ClientConfig describes how a client can communicate with the Interceptor +type ClientConfig struct { + // URL is a fully formed URL pointing to the interceptor + // Mutually exclusive with Service + URL *apis.URL `json:"url,omitempty"` + + // Service is a reference to a Service object where the interceptor is running + // Mutually exclusive with URL + Service *ServiceReference `json:"service,omitempty"` +} + +var defaultPort = int32(80) + +// ServiceReference is a reference to a Service object +// with an optional path +type ServiceReference struct { + // Name is the name of the service + Name string `json:"name"` + + // Namespace is the namespace of the service + Namespace string `json:"namespace"` + + // Path is an optional URL path + // +optional + Path string `json:"path,omitempty"` + + // Port is a valid port number + Port *int32 `json:"port,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// ClusterInterceptorList contains a list of ClusterInterceptor +// We don't use this but it's required for certain codegen features. +type ClusterInterceptorList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClusterInterceptor `json:"items"` +} + +var ErrNilURL = errors.New("interceptor URL was nil") + +// ResolveAddress returns the URL where the interceptor is running using its clientConfig +func (it *ClusterInterceptor) ResolveAddress() (*apis.URL, error) { + if url := it.Spec.ClientConfig.URL; url != nil { + return url, nil + } + svc := it.Spec.ClientConfig.Service + if svc == nil { + return nil, ErrNilURL + } + port := defaultPort + if svc.Port != nil { + port = *svc.Port + } + url := &apis.URL{ + Scheme: "http", // TODO: Support HTTPs if caBundle is present + Host: fmt.Sprintf("%s.%s.svc:%d", svc.Name, svc.Namespace, port), + Path: svc.Path, + } + return url, nil +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_interceptor_validation.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_interceptor_validation.go new file mode 100644 index 0000000000..f8bf39e99d --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_interceptor_validation.go @@ -0,0 +1,46 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + + "knative.dev/pkg/apis" +) + +// Validate ClusterInterceptor +func (it *ClusterInterceptor) Validate(ctx context.Context) *apis.FieldError { + if apis.IsInDelete(ctx) { + return nil + } + return it.Spec.validate(ctx) +} + +func (s *ClusterInterceptorSpec) validate(ctx context.Context) (errs *apis.FieldError) { + if s.ClientConfig.URL != nil && s.ClientConfig.Service != nil { + errs = errs.Also(apis.ErrMultipleOneOf("spec.clientConfig.url", "spec.clientConfig.service")) + } + if svc := s.ClientConfig.Service; svc != nil { + if svc.Namespace == "" { + errs = errs.Also(apis.ErrMissingField("spec.clientConfig.service.namespace")) + } + if svc.Name == "" { + errs = errs.Also(apis.ErrMissingField("spec.clientConfig.service.name")) + } + } + return errs +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_trigger_binding_defaults.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_trigger_binding_defaults.go new file mode 100644 index 0000000000..eae67beee3 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_trigger_binding_defaults.go @@ -0,0 +1,24 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" +) + +// SetDefaults initializes ClusterTriggerBinding ctb with its default values. +func (ctb *ClusterTriggerBinding) SetDefaults(ctx context.Context) {} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_trigger_binding_types.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_trigger_binding_types.go new file mode 100644 index 0000000000..10b9d5a480 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_trigger_binding_types.go @@ -0,0 +1,69 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +// Check that ClusterTriggerBinding may be validated and defaulted. +var _ apis.Validatable = (*ClusterTriggerBinding)(nil) +var _ apis.Defaultable = (*ClusterTriggerBinding)(nil) + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +k8s:openapi-gen=true + +// ClusterTriggerBinding is a TriggerBinding with a cluster scope. +// ClusterTriggerBindings are used to represent TriggerBindings that +// should be publicly addressable from any namespace in the cluster. +type ClusterTriggerBinding struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec holds the desired state of the ClusterTriggerBinding from the client + // +optional + Spec TriggerBindingSpec `json:"spec,omitempty"` + + // +optional + Status TriggerBindingStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterTriggerBindingList contains a list of ClusterTriggerBinding +type ClusterTriggerBindingList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClusterTriggerBinding `json:"items"` +} + +func (ctb *ClusterTriggerBinding) TriggerBindingSpec() TriggerBindingSpec { + return ctb.Spec +} + +func (ctb *ClusterTriggerBinding) TriggerBindingMetadata() metav1.ObjectMeta { + return ctb.ObjectMeta +} + +func (ctb *ClusterTriggerBinding) Copy() TriggerBindingInterface { + return ctb.DeepCopy() +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_trigger_binding_validation.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_trigger_binding_validation.go new file mode 100644 index 0000000000..0c410a3517 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/cluster_trigger_binding_validation.go @@ -0,0 +1,34 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + + "github.com/tektoncd/pipeline/pkg/apis/validate" + "knative.dev/pkg/apis" +) + +func (ctb *ClusterTriggerBinding) Validate(ctx context.Context) *apis.FieldError { + if err := validate.ObjectMetadata(ctb.GetObjectMeta()); err != nil { + return err.ViaField("metadata") + } + if apis.IsInDelete(ctx) { + return nil + } + return ctb.Spec.Validate(ctx) +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/doc.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/doc.go new file mode 100644 index 0000000000..e4c24171be --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/doc.go @@ -0,0 +1,23 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Package v1alpha1 contains API Schema definitions for the triggers v1alpha1 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=github.com/tektoncd/triggers/pkg/apis/triggers +// +k8s:defaulter-gen=TypeMeta +// +groupName=triggers.tekton.dev +package v1alpha1 diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/event_listener_defaults.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/event_listener_defaults.go new file mode 100644 index 0000000000..6753b8b0f6 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/event_listener_defaults.go @@ -0,0 +1,55 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + + "github.com/tektoncd/triggers/pkg/apis/config" + "github.com/tektoncd/triggers/pkg/apis/triggers/contexts" + "knative.dev/pkg/logging" +) + +// SetDefaults sets the defaults on the object. +func (el *EventListener) SetDefaults(ctx context.Context) { + cfg := config.FromContextOrDefaults(ctx) + if contexts.IsUpgradeViaDefaulting(ctx) { + defaultSA := cfg.Defaults.DefaultServiceAccount + if el.Spec.ServiceAccountName == "" && defaultSA != "" { + el.Spec.ServiceAccountName = defaultSA + } + // set defaults + if el.Spec.Resources.KubernetesResource != nil { + if el.Spec.Resources.KubernetesResource.Replicas != nil && *el.Spec.Resources.KubernetesResource.Replicas == 0 { + *el.Spec.Resources.KubernetesResource.Replicas = 1 + } + } + + for i, t := range el.Spec.Triggers { + triggerSpecBindingArray(el.Spec.Triggers[i].Bindings).defaultBindings() + for _, ti := range t.Interceptors { + ti.defaultInterceptorKind() + if err := ti.updateCoreInterceptors(); err != nil { + // The err only happens due to malformed JSON and should never really happen + // We can't return an error here, so print out the error + logger := logging.FromContext(ctx) + logger.Errorf("failed to setDefaults for trigger: %s; err: %s", t.Name, err) + } + } + } + } +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/event_listener_types.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/event_listener_types.go new file mode 100644 index 0000000000..9994fbbb48 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/event_listener_types.go @@ -0,0 +1,324 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "fmt" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" + duckv1alpha1 "knative.dev/pkg/apis/duck/v1alpha1" + "knative.dev/pkg/apis/duck/v1beta1" + "knative.dev/pkg/kmeta" +) + +// Check that EventListener may be validated and defaulted. +var _ apis.Validatable = (*EventListener)(nil) +var _ apis.Defaultable = (*EventListener)(nil) + +// +genclient +// +genreconciler:krshapedlogic=false +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// EventListener exposes a service to accept HTTP event payloads. +// +// +k8s:openapi-gen=true +type EventListener struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + // Spec holds the desired state of the EventListener from the client + // +optional + Spec EventListenerSpec `json:"spec"` + // +optional + Status EventListenerStatus `json:"status,omitempty"` +} + +var _ kmeta.OwnerRefable = (*EventListener)(nil) + +// EventListenerSpec defines the desired state of the EventListener, represented +// by a list of Triggers. +type EventListenerSpec struct { + ServiceAccountName string `json:"serviceAccountName,omitempty"` + // +listType=atomic + Triggers []EventListenerTrigger `json:"triggers"` + NamespaceSelector NamespaceSelector `json:"namespaceSelector,omitempty"` + LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"` + Resources Resources `json:"resources,omitempty"` +} + +type Resources struct { + KubernetesResource *KubernetesResource `json:"kubernetesResource,omitempty"` + CustomResource *CustomResource `json:"customResource,omitempty"` +} + +type CustomResource struct { + runtime.RawExtension `json:",inline"` +} + +type KubernetesResource struct { + Replicas *int32 `json:"replicas,omitempty"` + ServiceType corev1.ServiceType `json:"serviceType,omitempty"` + duckv1.WithPodSpec `json:"spec,omitempty"` +} + +// EventListenerTrigger represents a connection between TriggerBinding, Params, +// and TriggerTemplate; TriggerBinding provides extracted values for +// TriggerTemplate to then create resources from. TriggerRef can also be +// provided instead of TriggerBinding, Interceptors and TriggerTemplate +type EventListenerTrigger struct { + // +listType=atomic + Bindings []*EventListenerBinding `json:"bindings,omitempty"` + Template *EventListenerTemplate `json:"template,omitempty"` + TriggerRef string `json:"triggerRef,omitempty"` + // +optional + Name string `json:"name,omitempty"` + // +listType=atomic + Interceptors []*EventInterceptor `json:"interceptors,omitempty"` + // ServiceAccountName optionally associates credentials with each trigger; + // more granular authorization for + // who is allowed to utilize the associated pipeline + // vs. defaulting to whatever permissions are associated + // with the entire EventListener and associated sink facilitates + // multi-tenant model based scenarios + // +optional + ServiceAccountName string `json:"serviceAccountName,omitempty"` +} + +// EventInterceptor provides a hook to intercept and pre-process events +type EventInterceptor = TriggerInterceptor + +// SecretRef contains the information required to reference a single secret string +// This is needed because the other secretRef types are not cross-namespace and do not +// actually contain the "SecretName" field, which allows us to access a single secret value. +type SecretRef struct { + SecretKey string `json:"secretKey,omitempty"` + SecretName string `json:"secretName,omitempty"` +} + +// EventListenerBinding refers to a particular TriggerBinding or ClusterTriggerBindingresource. +type EventListenerBinding = TriggerSpecBinding + +// EventListenerTemplate refers to a particular TriggerTemplate resource. +type EventListenerTemplate = TriggerSpecTemplate + +// EventListenerList contains a list of TriggerBinding +// +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type EventListenerList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []EventListener `json:"items"` +} + +// EventListenerStatus holds the status of the EventListener +// +k8s:deepcopy-gen=true +type EventListenerStatus struct { + duckv1.Status `json:",inline"` + + // EventListener is Addressable. It currently exposes the service DNS + // address of the the EventListener sink + duckv1alpha1.AddressStatus `json:",inline"` + + // Configuration stores configuration for the EventListener service + Configuration EventListenerConfig `json:"configuration"` +} + +// EventListenerConfig stores configuration for resources generated by the +// EventListener +type EventListenerConfig struct { + // GeneratedResourceName is the name given to all resources reconciled by + // the EventListener + GeneratedResourceName string `json:"generatedName"` +} + +// NamespaceSelector is a selector for selecting either all namespaces or a +// list of namespaces. +// +k8s:openapi-gen=true +type NamespaceSelector struct { + // List of namespace names. + // +listType=atomic + MatchNames []string `json:"matchNames,omitempty"` +} + +// The conditions that are internally resolved by the EventListener reconciler +const ( + // ServiceExists is the ConditionType set on the EventListener, which + // specifies Service existence. + ServiceExists apis.ConditionType = "Service" + // DeploymentExists is the ConditionType set on the EventListener, which + // specifies Deployment existence. + DeploymentExists apis.ConditionType = "Deployment" +) + +// Check that EventListener may be validated and defaulted. +// TriggerBindingKind defines the type of TriggerBinding used by the EventListener. +type TriggerBindingKind string + +const ( + // NamespacedTriggerBindingKind indicates that triggerbinding type has a namespace scope. + NamespacedTriggerBindingKind TriggerBindingKind = "TriggerBinding" + // ClusterTriggerBindingKind indicates that triggerbinding type has a cluster scope. + ClusterTriggerBindingKind TriggerBindingKind = "ClusterTriggerBinding" +) + +var eventListenerCondSet = apis.NewLivingConditionSet( + ServiceExists, + DeploymentExists, +) + +// GetGroupVersionKind implements kmeta.OwnerRefable +func (el *EventListener) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("EventListener") +} + +// GetCondition returns the Condition matching the given type. +func (els *EventListenerStatus) GetCondition(t apis.ConditionType) *apis.Condition { + return eventListenerCondSet.Manage(els).GetCondition(t) +} + +// SetCondition sets the condition, unsetting previous conditions with the same +// type as necessary. This is a local change and needs to be persisted to the +// K8s API elsewhere. +func (els *EventListenerStatus) SetCondition(newCond *apis.Condition) { + if newCond != nil { + // TODO: Should the ConditionManager be set somewhere? + eventListenerCondSet.Manage(els).SetCondition(*newCond) + } +} + +func (els *EventListenerStatus) SetReadyCondition() { + for _, ct := range []apis.ConditionType{ + ServiceExists, + DeploymentExists, + apis.ConditionType(appsv1.DeploymentProgressing), + apis.ConditionType(appsv1.DeploymentAvailable)} { + if sc := els.GetCondition(ct); sc != nil { + if sc.Status != corev1.ConditionTrue { + els.SetCondition(&apis.Condition{ + Type: apis.ConditionReady, + Status: corev1.ConditionFalse, + Message: fmt.Sprintf("Condition %s has status: %s with message: %s", sc.Type, sc.Status, sc.Message), + }) + return + } + } + } + els.SetCondition(&apis.Condition{ + Type: apis.ConditionReady, + Status: corev1.ConditionTrue, + Message: "EventListener is ready", + }) +} + +// SetDeploymentConditions sets the Deployment conditions on the EventListener, +// which is a reflection of the actual Deployment status. +func (els *EventListenerStatus) SetDeploymentConditions(deploymentConditions []appsv1.DeploymentCondition) { + // Manually remove the DeploymentReplicaFailure condition since it does + // not always exist and would stay around otherwise + replicaFailureIndex := -1 + for i := range els.Conditions { + if els.Conditions[i].Type == apis.ConditionType(appsv1.DeploymentReplicaFailure) { + replicaFailureIndex = i + break + } + } + if replicaFailureIndex != -1 { + els.Conditions = append(els.Conditions[:replicaFailureIndex], els.Conditions[replicaFailureIndex+1:]...) + } + for _, cond := range deploymentConditions { + els.SetCondition(&apis.Condition{ + Type: apis.ConditionType(cond.Type), + Status: cond.Status, + Reason: cond.Reason, + Message: cond.Message, + }) + } +} + +func (els *EventListenerStatus) SetConditionsForDynamicObjects(conditions v1beta1.Conditions) { + for _, cond := range conditions { + els.SetCondition(&apis.Condition{ + Type: cond.Type, + Status: cond.Status, + Reason: cond.Reason, + Message: cond.Message, + }) + } + + els.SetCondition(&apis.Condition{ + Type: apis.ConditionReady, + Status: corev1.ConditionTrue, + Message: "EventListener is ready", + }) +} + +// SetExistsCondition simplifies setting the exists conditions on the +// EventListenerStatus. +func (els *EventListenerStatus) SetExistsCondition(cond apis.ConditionType, err error) { + if err != nil { + els.SetCondition(&apis.Condition{ + Type: cond, + Status: corev1.ConditionFalse, + Message: err.Error(), + }) + } else { + els.SetCondition(&apis.Condition{ + Type: cond, + Status: corev1.ConditionTrue, + Message: fmt.Sprintf("%s exists", cond), + }) + } +} + +// InitializeConditions will set all conditions in eventListenerCondSet to false +// for the EventListener. This does not use the InitializeCondition() provided +// by the conditionsImpl to avoid setting the happy condition. This is a local +// change and needs to be persisted to the K8s API elsewhere. +func (els *EventListenerStatus) InitializeConditions() { + for _, condition := range []apis.ConditionType{ + ServiceExists, + DeploymentExists, + apis.ConditionReady, + } { + els.SetCondition(&apis.Condition{ + Type: condition, + Status: corev1.ConditionFalse, + }) + } +} + +// SetAddress sets the address (as part of Addressable contract) and marks the correct condition. +func (els *EventListenerStatus) SetAddress(hostname string) { + if els.Address == nil { + els.Address = &duckv1alpha1.Addressable{} + } + if hostname != "" { + els.Address.URL = &apis.URL{ + Scheme: "http", + Host: hostname, + } + } else { + els.Address.URL = nil + } +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/event_listener_validation.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/event_listener_validation.go new file mode 100644 index 0000000000..95d13bba75 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/event_listener_validation.go @@ -0,0 +1,299 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + + "github.com/tektoncd/triggers/pkg/apis/triggers" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/validation" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" +) + +var ( + reservedEnvVars = sets.NewString( + "TLS_CERT", + "TLS_KEY", + ) +) + +// Validate EventListener. +func (e *EventListener) Validate(ctx context.Context) *apis.FieldError { + var errs *apis.FieldError + if len(e.ObjectMeta.Name) > 60 { + // Since `el-` is added as the prefix of EventListener services, the name of EventListener must be no more than 60 characters long. + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("eventListener name '%s' must be no more than 60 characters long", e.ObjectMeta.Name), "metadata.name")) + } + + if len(e.ObjectMeta.Annotations) != 0 { + errs = errs.Also(triggers.ValidateAnnotations(e.ObjectMeta.Annotations)) + } + + if apis.IsInDelete(ctx) { + return nil + } + return errs.Also(e.Spec.validate(ctx)) +} + +func (s *EventListenerSpec) validate(ctx context.Context) (errs *apis.FieldError) { + if s.LabelSelector == nil && len(s.NamespaceSelector.MatchNames) == 0 && len(s.Triggers) == 0 { + return apis.ErrMissingOneOf("spec.labelSelector", "spec.namespaceSelector", "spec.triggers") + } + + for i, trigger := range s.Triggers { + errs = errs.Also(trigger.validate(ctx).ViaField(fmt.Sprintf("spec.triggers[%d]", i))) + } + + // Both Kubernetes and Custom resource can't be present at the same time + if s.Resources.KubernetesResource != nil && s.Resources.CustomResource != nil { + return apis.ErrMultipleOneOf("spec.resources.kubernetesResource", "spec.resources.customResource") + } + + if s.Resources.KubernetesResource != nil { + errs = errs.Also(validateKubernetesObject(s.Resources.KubernetesResource).ViaField("spec.resources.kubernetesResource")) + } + + if s.Resources.CustomResource != nil { + errs = errs.Also(validateCustomObject(s.Resources.CustomResource).ViaField("spec.resources.customResource")) + } + return errs +} + +func validateCustomObject(customData *CustomResource) (errs *apis.FieldError) { + orig := duckv1.WithPod{} + decoder := json.NewDecoder(bytes.NewBuffer(customData.RawExtension.Raw)) + + if err := decoder.Decode(&orig); err != nil { + errs = errs.Also(apis.ErrInvalidValue(err, "spec")) + } + + if len(orig.Spec.Template.Spec.Containers) > 1 { + errs = errs.Also(apis.ErrMultipleOneOf("containers").ViaField("spec.template.spec")) + } + errs = errs.Also(apis.CheckDisallowedFields(orig.Spec.Template.Spec, + *podSpecMask(&orig.Spec.Template.Spec)).ViaField("spec.template.spec")) + + // bounded by condition because containers fields are optional so there is a chance that containers can be nil. + if len(orig.Spec.Template.Spec.Containers) == 1 { + errs = errs.Also(apis.CheckDisallowedFields(orig.Spec.Template.Spec.Containers[0], + *containerFieldMask(&orig.Spec.Template.Spec.Containers[0])).ViaField("spec.template.spec.containers[0]")) + // validate env + errs = errs.Also(validateEnv(orig.Spec.Template.Spec.Containers[0].Env).ViaField("spec.template.spec.containers[0].env")) + } + + return errs +} + +func validateKubernetesObject(orig *KubernetesResource) (errs *apis.FieldError) { + if orig.Replicas != nil { + if *orig.Replicas < 0 { + errs = errs.Also(apis.ErrInvalidValue(*orig.Replicas, "spec.replicas")) + } + } + if len(orig.Template.Spec.Containers) > 1 { + errs = errs.Also(apis.ErrMultipleOneOf("containers").ViaField("spec.template.spec")) + } + errs = errs.Also(apis.CheckDisallowedFields(orig.Template.Spec, + *podSpecMask(&orig.Template.Spec)).ViaField("spec.template.spec")) + + // bounded by condition because containers fields are optional so there is a chance that containers can be nil. + if len(orig.Template.Spec.Containers) == 1 { + errs = errs.Also(apis.CheckDisallowedFields(orig.Template.Spec.Containers[0], + *containerFieldMask(&orig.Template.Spec.Containers[0])).ViaField("spec.template.spec.containers[0]")) + // validate env + errs = errs.Also(validateEnv(orig.Template.Spec.Containers[0].Env).ViaField("spec.template.spec.containers[0].env")) + } + + return errs +} + +func validateEnv(envVars []corev1.EnvVar) (errs *apis.FieldError) { + var ( + count = 0 + envValue string + ) + for i, env := range envVars { + errs = errs.Also(validateEnvVar(env).ViaIndex(i)) + if reservedEnvVars.Has(env.Name) { + count++ + envValue = env.Name + } + } + // This is to make sure both TLS_CERT and TLS_KEY is set for tls connection + if count == 1 { + errs = errs.Also(&apis.FieldError{ + Message: fmt.Sprintf("Expected env's are TLS_CERT and TLS_KEY, but got only one env %s", envValue), + }) + } + return errs +} + +func validateEnvVar(env corev1.EnvVar) (errs *apis.FieldError) { + errs = errs.Also(apis.CheckDisallowedFields(env, *envVarMask(&env))) + + return errs.Also(validateEnvValueFrom(env.ValueFrom).ViaField("valueFrom")) +} + +func validateEnvValueFrom(source *corev1.EnvVarSource) *apis.FieldError { + if source == nil { + return nil + } + return apis.CheckDisallowedFields(*source, *envVarSourceMask(source)) +} + +// envVarSourceMask performs a _shallow_ copy of the Kubernetes EnvVarSource object to a new +// Kubernetes EnvVarSource object bringing over only the fields allowed in the Triggers EventListener API. +func envVarSourceMask(in *corev1.EnvVarSource) *corev1.EnvVarSource { + if in == nil { + return nil + } + out := new(corev1.EnvVarSource) + // Allowed fields + out.SecretKeyRef = in.SecretKeyRef + + // Disallowed fields + out.ConfigMapKeyRef = nil + out.FieldRef = nil + out.ResourceFieldRef = nil + + return out +} + +// envVarMask performs a _shallow_ copy of the Kubernetes EnvVar object to a new +// Kubernetes EnvVar object bringing over only the fields allowed in the Triggers EventListener API. +func envVarMask(in *corev1.EnvVar) *corev1.EnvVar { + if in == nil { + return nil + } + out := new(corev1.EnvVar) + // Allowed fields + out.Name = in.Name + out.ValueFrom = in.ValueFrom + + // Disallowed fields + out.Value = "" + + return out +} + +func containerFieldMask(in *corev1.Container) *corev1.Container { + out := new(corev1.Container) + out.Resources = in.Resources + out.Env = in.Env + + // Disallowed fields + // This list clarifies which all container attributes are not allowed. + out.Name = "" + out.Image = "" + out.Args = nil + out.Ports = nil + out.LivenessProbe = nil + out.ReadinessProbe = nil + out.StartupProbe = nil + out.Command = nil + out.VolumeMounts = nil + out.ImagePullPolicy = "" + out.Lifecycle = nil + out.Stdin = false + out.StdinOnce = false + out.TerminationMessagePath = "" + out.TerminationMessagePolicy = "" + out.WorkingDir = "" + out.TTY = false + out.VolumeDevices = nil + out.EnvFrom = nil + + return out +} + +// podSpecMask performs a _shallow_ copy of the Kubernetes PodSpec object to a new +// Kubernetes PodSpec object bringing over only the fields allowed in the Triggers EvenListener. +func podSpecMask(in *corev1.PodSpec) *corev1.PodSpec { + out := new(corev1.PodSpec) + + // Allowed fields + out.ServiceAccountName = in.ServiceAccountName + out.Containers = in.Containers + out.Tolerations = in.Tolerations + out.NodeSelector = in.NodeSelector + + // Disallowed fields + // This list clarifies which all podspec fields are not allowed. + out.Volumes = nil + out.EnableServiceLinks = nil + out.ImagePullSecrets = nil + out.InitContainers = nil + out.RestartPolicy = "" + out.TerminationGracePeriodSeconds = nil + out.ActiveDeadlineSeconds = nil + out.DNSPolicy = "" + out.AutomountServiceAccountToken = nil + out.NodeName = "" + out.HostNetwork = false + out.HostPID = false + out.HostIPC = false + out.ShareProcessNamespace = nil + out.SecurityContext = nil + out.Hostname = "" + out.Subdomain = "" + out.Affinity = nil + out.SchedulerName = "" + out.HostAliases = nil + out.PriorityClassName = "" + out.Priority = nil + out.DNSConfig = nil + out.ReadinessGates = nil + out.RuntimeClassName = nil + + return out +} + +func (t *EventListenerTrigger) validate(ctx context.Context) (errs *apis.FieldError) { + if t.Template == nil && t.TriggerRef == "" { + errs = errs.Also(apis.ErrMissingOneOf("template", "triggerRef")) + } + + if t.TriggerRef != "" && (t.Template != nil || t.Bindings != nil || t.Interceptors != nil) { + errs = errs.Also(apis.ErrMultipleOneOf("triggerRef", "template or bindings or interceptors")) + } + + // Validate optional Bindings + errs = errs.Also(triggerSpecBindingArray(t.Bindings).validate(ctx)) + if t.Template != nil { + // Validate required TriggerTemplate + errs = errs.Also(t.Template.validate(ctx)) + } + + // Validate optional Interceptors + for i, interceptor := range t.Interceptors { + errs = errs.Also(interceptor.validate(ctx).ViaField(fmt.Sprintf("interceptors[%d]", i))) + } + + // The trigger name is added as a label value for 'tekton.dev/trigger' so it must follow the k8s label guidelines: + // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set + if err := validation.IsValidLabelValue(t.Name); len(err) > 0 { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("trigger name '%s' must be a valid label value", t.Name), "name")) + } + + return errs +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/interceptor_types.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/interceptor_types.go new file mode 100644 index 0000000000..a87a53e0dc --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/interceptor_types.go @@ -0,0 +1,88 @@ +package v1alpha1 + +import ( + "context" + "fmt" + "strings" + + "google.golang.org/grpc/codes" +) + +type InterceptorInterface interface { + // Process executes the given InterceptorRequest. Simply getting a non-nil InterceptorResponse back is not sufficient + // to determine if the interceptor processing was successful. Instead use the InterceptorResponse.Status.Continue to + // see if processing should continue and InterceptorResponse.Status.Code to distinguish between the kinds of errors + // (i.e user errors vs system errors) + Process(ctx context.Context, r *InterceptorRequest) *InterceptorResponse +} + +// Do not generate DeepCopy(). See #827 +// +k8s:deepcopy-gen=false +type InterceptorRequest struct { + // Body is the incoming HTTP event body. We use a "string" representation of the JSON body + // in order to preserve the body exactly as it was sent (including spaces etc.). This is necessary + // for some interceptors e.g. GitHub for validating the body with a signature. While []byte can also + // store an exact representation of the body, `json.Marshal` will compact []byte to a base64 encoded + // string which means that we will lose the spaces any time we marshal this struct. + Body string `json:"body,omitempty"` + + // Header are the headers for the incoming HTTP event + Header map[string][]string `json:"header,omitempty"` + + // Extensions are extra values that are added by previous interceptors in a chain + Extensions map[string]interface{} `json:"extensions,omitempty"` + + // InterceptorParams are the user specified params for interceptor in the Trigger + InterceptorParams map[string]interface{} `json:"interceptor_params,omitempty"` + + // Context contains additional metadata about the event being processed + Context *TriggerContext `json:"context"` +} + +type TriggerContext struct { + // EventURL is the URL of the incoming event + EventURL string `json:"event_url,omitempty"` + // EventID is a unique ID assigned by Triggers to each event + EventID string `json:"event_id,omitempty"` + // TriggerID is of the form namespace/$ns/triggers/$name + TriggerID string `json:"trigger_id,omitempty"` +} + +// Do not generate Deepcopy(). See #827 +// +k8s:deepcopy-gen=false +type InterceptorResponse struct { + // Extensions are additional fields that is added to the interceptor event. + Extensions map[string]interface{} `json:"extensions,omitempty"` + // Continue indicates if the EventListener should continue processing the Trigger or not + Continue bool `json:"continue"` // Don't add omitempty -- it will remove the continue field when the value is false. + // Status is an Error status containing details on any interceptor processing errors + Status Status `json:"status"` +} + +type Status struct { + // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + Code codes.Code `json:"code,omitempty"` + // A developer-facing error message, which should be in English. + Message string `json:"message,omitempty"` +} + +func (s Status) Err() StatusError { + return StatusError{s: s} +} + +type StatusError struct { + s Status +} + +func (s StatusError) Error() string { + return fmt.Sprintf("rpc error: code = %s desc = %s", s.s.Code, s.s.Message) +} + +func ParseTriggerID(triggerID string) (namespace, name string) { + splits := strings.Split(triggerID, "/") + if len(splits) != 4 { + return + } + + return splits[1], splits[3] +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/param.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/param.go new file mode 100644 index 0000000000..4fae706dfe --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/param.go @@ -0,0 +1,21 @@ +package v1alpha1 + +// ParamSpec defines an arbitrary named input whose value can be supplied by a +// `Param`. +type ParamSpec struct { + // Name declares the name by which a parameter is referenced. + Name string `json:"name"` + // Description is a user-facing description of the parameter that may be + // used to populate a UI. + // +optional + Description string `json:"description,omitempty"` + // Default is the value a parameter takes if no input value via a Param is supplied. + // +optional + Default *string `json:"default,omitempty"` +} + +// Param defines a string value to be used for a ParamSpec with the same name. +type Param struct { + Name string `json:"name"` + Value string `json:"value"` +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/register.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/register.go new file mode 100644 index 0000000000..e9c006a70b --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/register.go @@ -0,0 +1,64 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "github.com/tektoncd/triggers/pkg/apis/triggers" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: triggers.GroupName, Version: "v1alpha1"} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + + // AddToScheme adds Build types to the scheme. + AddToScheme = schemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &ClusterInterceptor{}, + &ClusterInterceptorList{}, + &ClusterTriggerBinding{}, + &ClusterTriggerBindingList{}, + &EventListener{}, + &EventListenerList{}, + &TriggerBinding{}, + &TriggerBindingList{}, + &TriggerTemplate{}, + &TriggerTemplateList{}, + &Trigger{}, + &TriggerList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_binding_defaults.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_binding_defaults.go new file mode 100644 index 0000000000..5ed3a3a847 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_binding_defaults.go @@ -0,0 +1,24 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" +) + +// SetDefaults initializes TriggerBinding tb with its default values. +func (tb *TriggerBinding) SetDefaults(ctx context.Context) {} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_binding_interface.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_binding_interface.go new file mode 100644 index 0000000000..c7ab5fd901 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_binding_interface.go @@ -0,0 +1,26 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// TriggerBindingInterface is implemented by TriggerBinding and ClusterTriggerBinding +type TriggerBindingInterface interface { + TriggerBindingMetadata() metav1.ObjectMeta + TriggerBindingSpec() TriggerBindingSpec + Copy() TriggerBindingInterface +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_binding_types.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_binding_types.go new file mode 100644 index 0000000000..6d35dfc91e --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_binding_types.go @@ -0,0 +1,77 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +// Check that TriggerBinding may be validated and defaulted. +var _ apis.Validatable = (*TriggerBinding)(nil) +var _ apis.Defaultable = (*TriggerBinding)(nil) + +func (tb *TriggerBinding) TriggerBindingSpec() TriggerBindingSpec { + return tb.Spec +} + +func (tb *TriggerBinding) TriggerBindingMetadata() metav1.ObjectMeta { + return tb.ObjectMeta +} + +func (tb *TriggerBinding) Copy() TriggerBindingInterface { + return tb.DeepCopy() +} + +// TriggerBindingSpec defines the desired state of the TriggerBinding. +type TriggerBindingSpec struct { + // Params defines the parameter mapping from the given input event. + // +listType=atomic + Params []Param `json:"params,omitempty"` +} + +// TriggerBindingStatus defines the observed state of TriggerBinding. +type TriggerBindingStatus struct{} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// TriggerBinding defines a mapping of an input event to parameters. This is used +// to extract information from events to be passed to TriggerTemplates within a +// Trigger. +// +k8s:openapi-gen=true +type TriggerBinding struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + // Spec holds the desired state of the TriggerBinding + // +optional + Spec TriggerBindingSpec `json:"spec"` + // +optional + Status TriggerBindingStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// TriggerBindingList contains a list of TriggerBindings. +// We don't use this but it's required for certain codegen features. +type TriggerBindingList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []TriggerBinding `json:"items"` +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_binding_validation.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_binding_validation.go new file mode 100644 index 0000000000..cef6d15a89 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_binding_validation.go @@ -0,0 +1,98 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + "strings" + + "github.com/tektoncd/pipeline/pkg/apis/validate" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/util/sets" + "knative.dev/pkg/apis" +) + +// Validate TriggerBinding. +func (tb *TriggerBinding) Validate(ctx context.Context) (errs *apis.FieldError) { + errs = validate.ObjectMetadata(tb.GetObjectMeta()).ViaField("metadata") + + if apis.IsInDelete(ctx) { + return nil + } + return errs.Also(tb.Spec.Validate(ctx).ViaField("spec")) +} + +// Validate TriggerBindingSpec. +func (s *TriggerBindingSpec) Validate(ctx context.Context) (errs *apis.FieldError) { + if equality.Semantic.DeepEqual(s, &TriggerBindingSpec{}) { + return errs.Also(apis.ErrMissingField(apis.CurrentField)) + } + return errs.Also(validateParams(s.Params).ViaField("params")) +} + +func validateParams(params []Param) *apis.FieldError { + // Ensure there aren't multiple params with the same name. + seen := sets.NewString() + for i, param := range params { + if seen.Has(param.Name) { + return apis.ErrMultipleOneOf(fmt.Sprintf("[%d].name", i)) + } + seen.Insert(param.Name) + errs := validateParamValue(param.Value).ViaField(fmt.Sprintf("[%d]", i)) + if errs != nil { + return errs + } + } + return nil +} + +func validateParamValue(in string) *apis.FieldError { + if !strings.Contains(in, "$(") { + return nil + } + // Splits string on $( to find potential Tekton expressions + maybeExpressions := strings.Split(in, "$(") + terminated := true + for _, e := range maybeExpressions[1:] { // Split always returns at least one element + // Iterate until we find the first unbalanced ) + numOpenBrackets := 0 + if !terminated { + return apis.ErrInvalidValue(in, "value") + } + terminated = false + for _, ch := range e { + switch ch { + case '(': + numOpenBrackets++ + case ')': + numOpenBrackets-- + if numOpenBrackets < 0 { + terminated = true + } + default: + continue + } + if numOpenBrackets < 0 { + terminated = true + break + } + } + } + return nil + +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_defaults.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_defaults.go new file mode 100644 index 0000000000..93b20d060f --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_defaults.go @@ -0,0 +1,54 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + + "github.com/tektoncd/triggers/pkg/apis/triggers/contexts" + "knative.dev/pkg/logging" +) + +type triggerSpecBindingArray []*TriggerSpecBinding + +// SetDefaults sets the defaults on the object. +func (t *Trigger) SetDefaults(ctx context.Context) { + if !contexts.IsUpgradeViaDefaulting(ctx) { + return + } + triggerSpecBindingArray(t.Spec.Bindings).defaultBindings() + for _, ti := range t.Spec.Interceptors { + ti.defaultInterceptorKind() + if err := ti.updateCoreInterceptors(); err != nil { + // The err only happens due to malformed JSON and should never really happen + // We can't return an error here, so print out the error + logger := logging.FromContext(ctx) + logger.Errorf("failed to setDefaults for trigger: %s; err: %s", t.Name, err) + } + } +} + +// set default TriggerBinding kind for Bindings in TriggerSpec +func (t triggerSpecBindingArray) defaultBindings() { + if len(t) > 0 { + for _, b := range t { + if b.Kind == "" { + b.Kind = NamespacedTriggerBindingKind + } + } + } +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_template_defaults.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_template_defaults.go new file mode 100644 index 0000000000..2e705888fc --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_template_defaults.go @@ -0,0 +1,24 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" +) + +// SetDefaults initializes TriggerTemplate with default values. +func (tt *TriggerTemplate) SetDefaults(ctx context.Context) {} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_template_types.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_template_types.go new file mode 100644 index 0000000000..0a5fa61138 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_template_types.go @@ -0,0 +1,69 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "knative.dev/pkg/apis" +) + +// Check that TriggerTemplate may be validated and defaulted. +var _ apis.Validatable = (*TriggerTemplate)(nil) +var _ apis.Defaultable = (*TriggerTemplate)(nil) + +// TriggerTemplateSpec holds the desired state of TriggerTemplate +type TriggerTemplateSpec struct { + // +listType=atomic + Params []ParamSpec `json:"params,omitempty"` + // +listType=atomic + ResourceTemplates []TriggerResourceTemplate `json:"resourcetemplates,omitempty"` +} + +// TriggerResourceTemplate describes a resource to create +type TriggerResourceTemplate struct { + runtime.RawExtension `json:",inline"` +} + +// TriggerTemplateStatus describes the desired state of TriggerTemplate +type TriggerTemplateStatus struct{} + +// TriggerTemplate takes parameters and uses them to create CRDs +// +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +k8s:openapi-gen=true +type TriggerTemplate struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + // Spec holds the desired state of the TriggerTemplate from the client + // +optional + Spec TriggerTemplateSpec `json:"spec"` + // +optional + Status TriggerTemplateStatus `json:"status,omitempty"` +} + +// TriggerTemplateList contains a list of TriggerTemplate +// +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type TriggerTemplateList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []TriggerTemplate `json:"items"` +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_template_validation.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_template_validation.go new file mode 100644 index 0000000000..4d220883cd --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_template_validation.go @@ -0,0 +1,109 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + "regexp" + "strings" + + "github.com/tektoncd/pipeline/pkg/apis/validate" + "github.com/tektoncd/triggers/pkg/apis/config" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/sets" + "knative.dev/pkg/apis" +) + +// paramsRegexp captures TriggerTemplate parameter names $(tt.params.NAME) +var paramsRegexp = regexp.MustCompile(`\$\(tt.params.(?P[_a-zA-Z][_a-zA-Z0-9.-]*)\)`) + +// Validate validates a TriggerTemplate. +func (t *TriggerTemplate) Validate(ctx context.Context) *apis.FieldError { + errs := validate.ObjectMetadata(t.GetObjectMeta()).ViaField("metadata") + if apis.IsInDelete(ctx) { + return nil + } + return errs.Also(t.Spec.validate(ctx).ViaField("spec")) +} + +// Validate validates a TriggerTemplateSpec. +func (s *TriggerTemplateSpec) validate(ctx context.Context) (errs *apis.FieldError) { + if equality.Semantic.DeepEqual(s, &TriggerTemplateSpec{}) { + errs = errs.Also(apis.ErrMissingField(apis.CurrentField)) + } + if len(s.ResourceTemplates) == 0 { + errs = errs.Also(apis.ErrMissingField("resourcetemplates")) + } + errs = errs.Also(validateResourceTemplates(s.ResourceTemplates).ViaField("resourcetemplates")) + errs = errs.Also(verifyParamDeclarations(s.Params, s.ResourceTemplates).ViaField("resourcetemplates")) + return errs +} + +func validateResourceTemplates(templates []TriggerResourceTemplate) (errs *apis.FieldError) { + for i, trt := range templates { + if err := config.EnsureAllowedType(trt.RawExtension); err != nil { + if runtime.IsMissingVersion(err) { + errs = errs.Also(apis.ErrMissingField(fmt.Sprintf("[%d].apiVersion", i))) + } + if runtime.IsMissingKind(err) { + errs = errs.Also(apis.ErrMissingField(fmt.Sprintf("[%d].kind", i))) + } + if runtime.IsNotRegisteredError(err) { + errStr := err.Error() + if strings.Contains(errStr, "in scheme") { + // not registered error messages currently include the scheme variable location in your file, + // which can of course change if you move the location of the variable in your file. + // So will filter it out here to facilitate our unit testing, as the scheme location is not + // useful for our purposes. + errStr = errStr[:strings.Index(errStr, " in scheme")] + } + errs = errs.Also(apis.ErrInvalidValue( + errStr, + fmt.Sprintf("[%d]", i))) + } + // we allow structural errors because of param substitution + } + } + return errs +} + +// Verify every param in the ResourceTemplates is declared with a ParamSpec +func verifyParamDeclarations(params []ParamSpec, templates []TriggerResourceTemplate) *apis.FieldError { + declaredParamNames := sets.NewString() + for _, param := range params { + declaredParamNames.Insert(param.Name) + } + for i, template := range templates { + // Get all params in the template $(tt.params.NAME) + templateParams := paramsRegexp.FindAllSubmatch(template.RawExtension.Raw, -1) + for _, templateParam := range templateParams { + templateParamName := string(templateParam[1]) + if !declaredParamNames.Has(templateParamName) { + fieldErr := apis.ErrInvalidValue( + fmt.Sprintf("undeclared param '$(tt.params.%s)'", templateParamName), + fmt.Sprintf("[%d]", i), + ) + fieldErr.Details = fmt.Sprintf("'$(tt.params.%s)' must be declared in spec.params", templateParamName) + return fieldErr + } + } + } + + return nil +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_types.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_types.go new file mode 100644 index 0000000000..4ffab5a356 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_types.go @@ -0,0 +1,292 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "encoding/json" + "fmt" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + corev1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +// TriggerSpec represents a connection between TriggerSpecBinding, +// and TriggerSpecTemplate; TriggerSpecBinding provides extracted values for +// TriggerSpecTemplate to then create resources from. +type TriggerSpec struct { + // +listType=atomic + Bindings []*TriggerSpecBinding `json:"bindings"` + Template TriggerSpecTemplate `json:"template"` + // +optional + Name string `json:"name,omitempty"` + // +listType=atomic + Interceptors []*TriggerInterceptor `json:"interceptors,omitempty"` + // ServiceAccountName optionally associates credentials with each trigger; + // Unlike EventListeners, this should be scoped to the same namespace + // as the Trigger itself + // +optional + ServiceAccountName string `json:"serviceAccountName,omitempty"` +} + +type TriggerSpecTemplate struct { + Ref *string `json:"ref,omitempty"` + APIVersion string `json:"apiversion,omitempty"` + Spec *TriggerTemplateSpec `json:"spec,omitempty"` +} + +type TriggerSpecBinding struct { + // Name is the name of the binding param + // Mutually exclusive with Ref + Name string `json:"name,omitempty"` + // Value is the value of the binding param. Can contain JSONPath + // Has to be pointer since "" is a valid value + // Required if Name is also specified. + Value *string `json:"value,omitempty"` + + // Ref is a reference to a TriggerBinding kind. + // Mutually exclusive with Name + Ref string `json:"ref,omitempty"` + + // Kind can only be provided if Ref is also provided. Defaults to TriggerBinding + Kind TriggerBindingKind `json:"kind,omitempty"` + + // APIVersion of the binding ref + APIVersion string `json:"apiversion,omitempty"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Trigger defines a mapping of an input event to parameters. This is used +// to extract information from events to be passed to TriggerTemplates within a +// Trigger. +// +k8s:openapi-gen=true +type Trigger struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + // Spec holds the desired state of the Trigger + // +optional + Spec TriggerSpec `json:"spec"` +} + +// TriggerInterceptor provides a hook to intercept and pre-process events +type TriggerInterceptor struct { + // Optional name to identify the current interceptor configuration + Name *string `json:"name,omitempty"` + // Ref refers to the Interceptor to use + Ref InterceptorRef `json:"ref"` + // Params are the params to send to the interceptor + // +listType=atomic + Params []InterceptorParams `json:"params,omitempty"` + + // WebhookInterceptor refers to an old style webhook interceptor service + Webhook *WebhookInterceptor `json:"webhook,omitempty"` + + // Deprecated old fields below + DeprecatedGitHub *GitHubInterceptor `json:"github,omitempty"` + DeprecatedGitLab *GitLabInterceptor `json:"gitlab,omitempty"` + DeprecatedCEL *CELInterceptor `json:"cel,omitempty"` + DeprecatedBitbucket *BitbucketInterceptor `json:"bitbucket,omitempty"` +} + +// InterceptorParams defines a key-value pair that can be passed on an interceptor +type InterceptorParams struct { + Name string `json:"name"` + Value apiextensionsv1.JSON `json:"value"` +} + +// InterceptorRef provides a Reference to a ClusterInterceptor +type InterceptorRef struct { + // Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names + Name string `json:"name,omitempty"` + // InterceptorKind indicates the kind of the Interceptor, namespaced or cluster scoped. + // Currently only InterceptorKind is ClusterInterceptor, so the only valid value + // is the default one + // +optional + Kind InterceptorKind `json:"kind,omitempty"` + // API version of the referent + // +optional + APIVersion string `json:"apiVersion,omitempty"` +} + +// InterceptorKind defines the type of Interceptor used by the Trigger. +type InterceptorKind string + +const ( + // ClusterTaskKind indicates that task type has a cluster scope. + ClusterInterceptorKind InterceptorKind = "ClusterInterceptor" +) + +func (ti *TriggerInterceptor) defaultInterceptorKind() { + if ti.Ref.Kind == "" { + ti.Ref.Kind = ClusterInterceptorKind + } +} + +func (ti *TriggerInterceptor) updateCoreInterceptors() error { + if ti == nil { + return nil + } + if ti.Ref.Name != "" { + return nil + } + ti.Ref.Name = ti.GetName() + ti.Params = []InterceptorParams{} + switch ti.Ref.Name { + case "bitbucket": + if err := addToParams(&ti.Params, "secretRef", ti.DeprecatedBitbucket.SecretRef); err != nil { + return err + } + if err := addToParams(&ti.Params, "eventTypes", ti.DeprecatedBitbucket.EventTypes); err != nil { + return err + } + ti.DeprecatedBitbucket = nil + case "gitlab": + if err := addToParams(&ti.Params, "secretRef", ti.DeprecatedGitLab.SecretRef); err != nil { + return err + } + if err := addToParams(&ti.Params, "eventTypes", ti.DeprecatedGitLab.EventTypes); err != nil { + return err + } + ti.DeprecatedGitLab = nil + case "github": + if err := addToParams(&ti.Params, "secretRef", ti.DeprecatedGitHub.SecretRef); err != nil { + return err + } + if err := addToParams(&ti.Params, "eventTypes", ti.DeprecatedGitHub.EventTypes); err != nil { + return err + } + ti.DeprecatedGitHub = nil + case "cel": + if err := addToParams(&ti.Params, "filter", ti.DeprecatedCEL.Filter); err != nil { + return err + } + if err := addToParams(&ti.Params, "overlays", ti.DeprecatedCEL.Overlays); err != nil { + return err + } + ti.DeprecatedCEL = nil + } + return nil +} + +func addToParams(params *[]InterceptorParams, name string, val interface{}) error { + if val == nil { + return nil + } + v, err := toV1JSON(val) + if err != nil { + return err + } + *params = append(*params, InterceptorParams{ + Name: name, + Value: v, + }) + return nil +} + +func toV1JSON(v interface{}) (apiextensionsv1.JSON, error) { + b, err := json.Marshal(v) + if err != nil { + return apiextensionsv1.JSON{}, fmt.Errorf("json.Marshal() failed: %s", err) + } + return apiextensionsv1.JSON{ + Raw: b, + }, nil +} + +// GetName returns the name for the given interceptor +func (ti *TriggerInterceptor) GetName() string { + // This is temporary until we implement #869 + name := "" + switch { + case ti.Ref.Name != "": + name = ti.Ref.Name + case ti.DeprecatedBitbucket != nil: + name = "bitbucket" + case ti.DeprecatedCEL != nil: + name = "cel" + case ti.DeprecatedGitHub != nil: + name = "github" + case ti.DeprecatedGitLab != nil: + name = "gitlab" + } + return name +} + +// WebhookInterceptor provides a webhook to intercept and pre-process events +type WebhookInterceptor struct { + // ObjectRef is a reference to an object that will resolve to a cluster DNS + // name to use as the EventInterceptor. Either objectRef or url can be specified + // +optional + ObjectRef *corev1.ObjectReference `json:"objectRef,omitempty"` + // +optional + URL *apis.URL `json:"url,omitempty"` + // Header is a group of key-value pairs that can be appended to the + // interceptor request headers. This allows the interceptor to make + // decisions specific to an EventListenerTrigger. + // +listType=atomic + Header []v1beta1.Param `json:"header,omitempty"` +} + +// BitbucketInterceptor provides a webhook to intercept and pre-process events +type BitbucketInterceptor struct { + SecretRef *SecretRef `json:"secretRef,omitempty"` + // +listType=atomic + EventTypes []string `json:"eventTypes,omitempty"` +} + +// GitHubInterceptor provides a webhook to intercept and pre-process events +type GitHubInterceptor struct { + SecretRef *SecretRef `json:"secretRef,omitempty"` + // +listType=atomic + EventTypes []string `json:"eventTypes,omitempty"` +} + +// GitLabInterceptor provides a webhook to intercept and pre-process events +type GitLabInterceptor struct { + SecretRef *SecretRef `json:"secretRef,omitempty"` + // +listType=atomic + EventTypes []string `json:"eventTypes,omitempty"` +} + +// CELInterceptor provides a webhook to intercept and pre-process events +type CELInterceptor struct { + Filter string `json:"filter,omitempty"` + // +listType=atomic + Overlays []CELOverlay `json:"overlays,omitempty"` +} + +// CELOverlay provides a way to modify the request body using DeprecatedCEL expressions +type CELOverlay struct { + Key string `json:"key,omitempty"` + Expression string `json:"expression,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// TriggerList contains a list of Triggers. +// We don't use this but it's required for certain codegen features. +type TriggerList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []Trigger `json:"items"` +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_types_convert.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_types_convert.go new file mode 100644 index 0000000000..c59c2daabe --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_types_convert.go @@ -0,0 +1,41 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "encoding/json" +) + +// ToEventListenerTrigger converts a TriggerSpec into an EventListenerTrigger. +// This is primarily for compatibility between CRD and non-CRD types so that +// underlying libraries can reuse existing code. +func ToEventListenerTrigger(in TriggerSpec) (EventListenerTrigger, error) { + var out EventListenerTrigger + + // Use json Marshalling in order to be field agnostic. Since TriggerSpec + // is a subset of the existing EventListenerTrigger type, and should always + // contain the same field labels, this should be safe to do. + b, err := json.Marshal(in) + if err != nil { + return out, err + } + + if err := json.Unmarshal(b, &out); err != nil { + return out, err + } + return out, nil +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_validation.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_validation.go new file mode 100644 index 0000000000..e892d41ada --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/trigger_validation.go @@ -0,0 +1,171 @@ +/* +Copyright 2020 The Tekton Authors + +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. +*/ + +package v1alpha1 + +import ( + "context" + "fmt" + "net/http" + + "github.com/google/cel-go/cel" + pipelinev1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "github.com/tektoncd/pipeline/pkg/apis/validate" + "knative.dev/pkg/apis" +) + +// Validate validates a Trigger +func (t *Trigger) Validate(ctx context.Context) *apis.FieldError { + errs := validate.ObjectMetadata(t.GetObjectMeta()).ViaField("metadata") + if apis.IsInDelete(ctx) { + return nil + } + return errs.Also(t.Spec.validate(ctx).ViaField("spec")) +} + +func (t *TriggerSpec) validate(ctx context.Context) *apis.FieldError { + // Validate optional Bindings + errs := triggerSpecBindingArray(t.Bindings).validate(ctx) + // Validate required TriggerTemplate + errs = errs.Also(t.Template.validate(ctx)) + + // Validate optional Interceptors + for i, interceptor := range t.Interceptors { + errs = errs.Also(interceptor.validate(ctx).ViaField(fmt.Sprintf("interceptors[%d]", i))) + } + + return errs +} + +func (t TriggerSpecTemplate) validate(ctx context.Context) (errs *apis.FieldError) { + // Optional explicit match + if t.APIVersion != "" { + if t.APIVersion != "v1alpha1" { + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("invalid apiVersion"), "template.apiVersion")) + } + } + + switch { + case t.Spec != nil && t.Ref != nil: + errs = errs.Also(apis.ErrMultipleOneOf("template.spec", "template.ref")) + case t.Spec == nil && t.Ref == nil: + errs = errs.Also(apis.ErrMissingOneOf("template.spec", "template.ref")) + case t.Spec != nil: + errs = errs.Also(t.Spec.validate(ctx)) + case t.Ref == nil || *t.Ref == "": + errs = errs.Also(apis.ErrMissingField("template.ref")) + } + return errs +} + +func (t triggerSpecBindingArray) validate(ctx context.Context) (errs *apis.FieldError) { + for i, b := range t { + switch { + case b.Ref != "": + switch { + case b.Name != "": // Cannot specify both Ref and Name + errs = errs.Also(apis.ErrMultipleOneOf(fmt.Sprintf("bindings[%d].Ref", i), fmt.Sprintf("bindings[%d].Name", i))) + case b.Kind != NamespacedTriggerBindingKind && b.Kind != ClusterTriggerBindingKind: // Kind must be valid + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("invalid kind"), fmt.Sprintf("bindings[%d].kind", i))) + } + case b.Name != "": + if b.Value == nil { // Value is mandatory if Name is specified + errs = errs.Also(apis.ErrMissingField(fmt.Sprintf("bindings[%d].Value", i))) + } + default: + errs = errs.Also(apis.ErrMissingOneOf(fmt.Sprintf("bindings[%d].Ref", i), fmt.Sprintf("bindings[%d].Spec", i), fmt.Sprintf("bindings[%d].Name", i))) + } + } + return errs +} + +func (i *TriggerInterceptor) validate(ctx context.Context) (errs *apis.FieldError) { + if i.Webhook == nil && i.DeprecatedGitHub == nil && i.DeprecatedGitLab == nil && i.DeprecatedCEL == nil && i.DeprecatedBitbucket == nil { + if i.Ref.Name == "" { // Check to see if Interceptor referenced using Ref + errs = errs.Also(apis.ErrMissingField("interceptor")) + } + } + + // Enforce oneof + numSet := 0 + if i.Webhook != nil { + numSet++ + } + if i.DeprecatedGitHub != nil { + numSet++ + } + if i.DeprecatedGitLab != nil { + numSet++ + } + if i.DeprecatedBitbucket != nil { + numSet++ + } + + if numSet > 1 { + errs = errs.Also(apis.ErrMultipleOneOf("interceptor.webhook", "interceptor.github", "interceptor.gitlab")) + } + + if i.Webhook != nil { + if i.Webhook.ObjectRef == nil || i.Webhook.ObjectRef.Name == "" { + errs = errs.Also(apis.ErrMissingField("interceptor.webhook.objectRef")) + } + w := i.Webhook + if w.ObjectRef.Kind != "Service" { + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("invalid kind"), "interceptor.webhook.objectRef.kind")) + } + + // Optional explicit match + if w.ObjectRef.APIVersion != "v1" { + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("invalid apiVersion"), "interceptor.webhook.objectRef.apiVersion")) + } + + for i, header := range w.Header { + // Enforce non-empty canonical header keys + if len(header.Name) == 0 || http.CanonicalHeaderKey(header.Name) != header.Name { + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("invalid header name"), fmt.Sprintf("interceptor.webhook.header[%d].name", i))) + } + // Enforce non-empty header values + if header.Value.Type == pipelinev1.ParamTypeString { + if len(header.Value.StringVal) == 0 { + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("invalid header value"), fmt.Sprintf("interceptor.webhook.header[%d].value", i))) + } + } else if len(header.Value.ArrayVal) == 0 { + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("invalid header value"), fmt.Sprintf("interceptor.webhook.header[%d].value", i))) + } + } + } + + if i.DeprecatedCEL != nil { + if i.DeprecatedCEL.Filter == "" && len(i.DeprecatedCEL.Overlays) == 0 { + errs = errs.Also(apis.ErrMultipleOneOf("cel.filter", "cel.overlays")) + } + env, err := cel.NewEnv() + if err != nil { + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("failed to create a DeprecatedCEL env: %s", err), "cel.filter")) + } + if i.DeprecatedCEL.Filter != "" { + if _, issues := env.Parse(i.DeprecatedCEL.Filter); issues != nil && issues.Err() != nil { + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("failed to parse the DeprecatedCEL filter: %s", issues.Err()), "cel.filter")) + } + } + for _, v := range i.DeprecatedCEL.Overlays { + if _, issues := env.Parse(v.Expression); issues != nil && issues.Err() != nil { + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("failed to parse the DeprecatedCEL overlay: %s", issues.Err()), "cel.overlay")) + } + } + } + return errs +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..bb9915334e --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,1194 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + apis "knative.dev/pkg/apis" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BitbucketInterceptor) DeepCopyInto(out *BitbucketInterceptor) { + *out = *in + if in.SecretRef != nil { + in, out := &in.SecretRef, &out.SecretRef + *out = new(SecretRef) + **out = **in + } + if in.EventTypes != nil { + in, out := &in.EventTypes, &out.EventTypes + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BitbucketInterceptor. +func (in *BitbucketInterceptor) DeepCopy() *BitbucketInterceptor { + if in == nil { + return nil + } + out := new(BitbucketInterceptor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CELInterceptor) DeepCopyInto(out *CELInterceptor) { + *out = *in + if in.Overlays != nil { + in, out := &in.Overlays, &out.Overlays + *out = make([]CELOverlay, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CELInterceptor. +func (in *CELInterceptor) DeepCopy() *CELInterceptor { + if in == nil { + return nil + } + out := new(CELInterceptor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CELOverlay) DeepCopyInto(out *CELOverlay) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CELOverlay. +func (in *CELOverlay) DeepCopy() *CELOverlay { + if in == nil { + return nil + } + out := new(CELOverlay) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClientConfig) DeepCopyInto(out *ClientConfig) { + *out = *in + if in.URL != nil { + in, out := &in.URL, &out.URL + *out = new(apis.URL) + (*in).DeepCopyInto(*out) + } + if in.Service != nil { + in, out := &in.Service, &out.Service + *out = new(ServiceReference) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientConfig. +func (in *ClientConfig) DeepCopy() *ClientConfig { + if in == nil { + return nil + } + out := new(ClientConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterInterceptor) DeepCopyInto(out *ClusterInterceptor) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterInterceptor. +func (in *ClusterInterceptor) DeepCopy() *ClusterInterceptor { + if in == nil { + return nil + } + out := new(ClusterInterceptor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterInterceptor) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterInterceptorList) DeepCopyInto(out *ClusterInterceptorList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterInterceptor, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterInterceptorList. +func (in *ClusterInterceptorList) DeepCopy() *ClusterInterceptorList { + if in == nil { + return nil + } + out := new(ClusterInterceptorList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterInterceptorList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterInterceptorSpec) DeepCopyInto(out *ClusterInterceptorSpec) { + *out = *in + in.ClientConfig.DeepCopyInto(&out.ClientConfig) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterInterceptorSpec. +func (in *ClusterInterceptorSpec) DeepCopy() *ClusterInterceptorSpec { + if in == nil { + return nil + } + out := new(ClusterInterceptorSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterInterceptorStatus) DeepCopyInto(out *ClusterInterceptorStatus) { + *out = *in + in.Status.DeepCopyInto(&out.Status) + in.AddressStatus.DeepCopyInto(&out.AddressStatus) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterInterceptorStatus. +func (in *ClusterInterceptorStatus) DeepCopy() *ClusterInterceptorStatus { + if in == nil { + return nil + } + out := new(ClusterInterceptorStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterTriggerBinding) DeepCopyInto(out *ClusterTriggerBinding) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterTriggerBinding. +func (in *ClusterTriggerBinding) DeepCopy() *ClusterTriggerBinding { + if in == nil { + return nil + } + out := new(ClusterTriggerBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterTriggerBinding) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterTriggerBindingList) DeepCopyInto(out *ClusterTriggerBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterTriggerBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterTriggerBindingList. +func (in *ClusterTriggerBindingList) DeepCopy() *ClusterTriggerBindingList { + if in == nil { + return nil + } + out := new(ClusterTriggerBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterTriggerBindingList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CustomResource) DeepCopyInto(out *CustomResource) { + *out = *in + in.RawExtension.DeepCopyInto(&out.RawExtension) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResource. +func (in *CustomResource) DeepCopy() *CustomResource { + if in == nil { + return nil + } + out := new(CustomResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EventListener) DeepCopyInto(out *EventListener) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventListener. +func (in *EventListener) DeepCopy() *EventListener { + if in == nil { + return nil + } + out := new(EventListener) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EventListener) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EventListenerConfig) DeepCopyInto(out *EventListenerConfig) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventListenerConfig. +func (in *EventListenerConfig) DeepCopy() *EventListenerConfig { + if in == nil { + return nil + } + out := new(EventListenerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EventListenerList) DeepCopyInto(out *EventListenerList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]EventListener, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventListenerList. +func (in *EventListenerList) DeepCopy() *EventListenerList { + if in == nil { + return nil + } + out := new(EventListenerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EventListenerList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EventListenerSpec) DeepCopyInto(out *EventListenerSpec) { + *out = *in + if in.Triggers != nil { + in, out := &in.Triggers, &out.Triggers + *out = make([]EventListenerTrigger, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.NamespaceSelector.DeepCopyInto(&out.NamespaceSelector) + if in.LabelSelector != nil { + in, out := &in.LabelSelector, &out.LabelSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + in.Resources.DeepCopyInto(&out.Resources) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventListenerSpec. +func (in *EventListenerSpec) DeepCopy() *EventListenerSpec { + if in == nil { + return nil + } + out := new(EventListenerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EventListenerStatus) DeepCopyInto(out *EventListenerStatus) { + *out = *in + in.Status.DeepCopyInto(&out.Status) + in.AddressStatus.DeepCopyInto(&out.AddressStatus) + out.Configuration = in.Configuration + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventListenerStatus. +func (in *EventListenerStatus) DeepCopy() *EventListenerStatus { + if in == nil { + return nil + } + out := new(EventListenerStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EventListenerTrigger) DeepCopyInto(out *EventListenerTrigger) { + *out = *in + if in.Bindings != nil { + in, out := &in.Bindings, &out.Bindings + *out = make([]*TriggerSpecBinding, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TriggerSpecBinding) + (*in).DeepCopyInto(*out) + } + } + } + if in.Template != nil { + in, out := &in.Template, &out.Template + *out = new(TriggerSpecTemplate) + (*in).DeepCopyInto(*out) + } + if in.Interceptors != nil { + in, out := &in.Interceptors, &out.Interceptors + *out = make([]*TriggerInterceptor, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TriggerInterceptor) + (*in).DeepCopyInto(*out) + } + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventListenerTrigger. +func (in *EventListenerTrigger) DeepCopy() *EventListenerTrigger { + if in == nil { + return nil + } + out := new(EventListenerTrigger) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitHubInterceptor) DeepCopyInto(out *GitHubInterceptor) { + *out = *in + if in.SecretRef != nil { + in, out := &in.SecretRef, &out.SecretRef + *out = new(SecretRef) + **out = **in + } + if in.EventTypes != nil { + in, out := &in.EventTypes, &out.EventTypes + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitHubInterceptor. +func (in *GitHubInterceptor) DeepCopy() *GitHubInterceptor { + if in == nil { + return nil + } + out := new(GitHubInterceptor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitLabInterceptor) DeepCopyInto(out *GitLabInterceptor) { + *out = *in + if in.SecretRef != nil { + in, out := &in.SecretRef, &out.SecretRef + *out = new(SecretRef) + **out = **in + } + if in.EventTypes != nil { + in, out := &in.EventTypes, &out.EventTypes + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitLabInterceptor. +func (in *GitLabInterceptor) DeepCopy() *GitLabInterceptor { + if in == nil { + return nil + } + out := new(GitLabInterceptor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InterceptorParams) DeepCopyInto(out *InterceptorParams) { + *out = *in + in.Value.DeepCopyInto(&out.Value) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InterceptorParams. +func (in *InterceptorParams) DeepCopy() *InterceptorParams { + if in == nil { + return nil + } + out := new(InterceptorParams) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InterceptorRef) DeepCopyInto(out *InterceptorRef) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InterceptorRef. +func (in *InterceptorRef) DeepCopy() *InterceptorRef { + if in == nil { + return nil + } + out := new(InterceptorRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesResource) DeepCopyInto(out *KubernetesResource) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + in.WithPodSpec.DeepCopyInto(&out.WithPodSpec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesResource. +func (in *KubernetesResource) DeepCopy() *KubernetesResource { + if in == nil { + return nil + } + out := new(KubernetesResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NamespaceSelector) DeepCopyInto(out *NamespaceSelector) { + *out = *in + if in.MatchNames != nil { + in, out := &in.MatchNames, &out.MatchNames + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceSelector. +func (in *NamespaceSelector) DeepCopy() *NamespaceSelector { + if in == nil { + return nil + } + out := new(NamespaceSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Param) DeepCopyInto(out *Param) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Param. +func (in *Param) DeepCopy() *Param { + if in == nil { + return nil + } + out := new(Param) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ParamSpec) DeepCopyInto(out *ParamSpec) { + *out = *in + if in.Default != nil { + in, out := &in.Default, &out.Default + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ParamSpec. +func (in *ParamSpec) DeepCopy() *ParamSpec { + if in == nil { + return nil + } + out := new(ParamSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + if in.KubernetesResource != nil { + in, out := &in.KubernetesResource, &out.KubernetesResource + *out = new(KubernetesResource) + (*in).DeepCopyInto(*out) + } + if in.CustomResource != nil { + in, out := &in.CustomResource, &out.CustomResource + *out = new(CustomResource) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretRef) DeepCopyInto(out *SecretRef) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretRef. +func (in *SecretRef) DeepCopy() *SecretRef { + if in == nil { + return nil + } + out := new(SecretRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceReference) DeepCopyInto(out *ServiceReference) { + *out = *in + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(int32) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceReference. +func (in *ServiceReference) DeepCopy() *ServiceReference { + if in == nil { + return nil + } + out := new(ServiceReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Status) DeepCopyInto(out *Status) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Status. +func (in *Status) DeepCopy() *Status { + if in == nil { + return nil + } + out := new(Status) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StatusError) DeepCopyInto(out *StatusError) { + *out = *in + out.s = in.s + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StatusError. +func (in *StatusError) DeepCopy() *StatusError { + if in == nil { + return nil + } + out := new(StatusError) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Trigger) DeepCopyInto(out *Trigger) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Trigger. +func (in *Trigger) DeepCopy() *Trigger { + if in == nil { + return nil + } + out := new(Trigger) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Trigger) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerBinding) DeepCopyInto(out *TriggerBinding) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerBinding. +func (in *TriggerBinding) DeepCopy() *TriggerBinding { + if in == nil { + return nil + } + out := new(TriggerBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TriggerBinding) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerBindingList) DeepCopyInto(out *TriggerBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]TriggerBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerBindingList. +func (in *TriggerBindingList) DeepCopy() *TriggerBindingList { + if in == nil { + return nil + } + out := new(TriggerBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TriggerBindingList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerBindingSpec) DeepCopyInto(out *TriggerBindingSpec) { + *out = *in + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]Param, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerBindingSpec. +func (in *TriggerBindingSpec) DeepCopy() *TriggerBindingSpec { + if in == nil { + return nil + } + out := new(TriggerBindingSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerBindingStatus) DeepCopyInto(out *TriggerBindingStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerBindingStatus. +func (in *TriggerBindingStatus) DeepCopy() *TriggerBindingStatus { + if in == nil { + return nil + } + out := new(TriggerBindingStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerContext) DeepCopyInto(out *TriggerContext) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerContext. +func (in *TriggerContext) DeepCopy() *TriggerContext { + if in == nil { + return nil + } + out := new(TriggerContext) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerInterceptor) DeepCopyInto(out *TriggerInterceptor) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } + out.Ref = in.Ref + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]InterceptorParams, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Webhook != nil { + in, out := &in.Webhook, &out.Webhook + *out = new(WebhookInterceptor) + (*in).DeepCopyInto(*out) + } + if in.DeprecatedGitHub != nil { + in, out := &in.DeprecatedGitHub, &out.DeprecatedGitHub + *out = new(GitHubInterceptor) + (*in).DeepCopyInto(*out) + } + if in.DeprecatedGitLab != nil { + in, out := &in.DeprecatedGitLab, &out.DeprecatedGitLab + *out = new(GitLabInterceptor) + (*in).DeepCopyInto(*out) + } + if in.DeprecatedCEL != nil { + in, out := &in.DeprecatedCEL, &out.DeprecatedCEL + *out = new(CELInterceptor) + (*in).DeepCopyInto(*out) + } + if in.DeprecatedBitbucket != nil { + in, out := &in.DeprecatedBitbucket, &out.DeprecatedBitbucket + *out = new(BitbucketInterceptor) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerInterceptor. +func (in *TriggerInterceptor) DeepCopy() *TriggerInterceptor { + if in == nil { + return nil + } + out := new(TriggerInterceptor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerList) DeepCopyInto(out *TriggerList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Trigger, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerList. +func (in *TriggerList) DeepCopy() *TriggerList { + if in == nil { + return nil + } + out := new(TriggerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TriggerList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerResourceTemplate) DeepCopyInto(out *TriggerResourceTemplate) { + *out = *in + in.RawExtension.DeepCopyInto(&out.RawExtension) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerResourceTemplate. +func (in *TriggerResourceTemplate) DeepCopy() *TriggerResourceTemplate { + if in == nil { + return nil + } + out := new(TriggerResourceTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerSpec) DeepCopyInto(out *TriggerSpec) { + *out = *in + if in.Bindings != nil { + in, out := &in.Bindings, &out.Bindings + *out = make([]*TriggerSpecBinding, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TriggerSpecBinding) + (*in).DeepCopyInto(*out) + } + } + } + in.Template.DeepCopyInto(&out.Template) + if in.Interceptors != nil { + in, out := &in.Interceptors, &out.Interceptors + *out = make([]*TriggerInterceptor, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TriggerInterceptor) + (*in).DeepCopyInto(*out) + } + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerSpec. +func (in *TriggerSpec) DeepCopy() *TriggerSpec { + if in == nil { + return nil + } + out := new(TriggerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerSpecBinding) DeepCopyInto(out *TriggerSpecBinding) { + *out = *in + if in.Value != nil { + in, out := &in.Value, &out.Value + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerSpecBinding. +func (in *TriggerSpecBinding) DeepCopy() *TriggerSpecBinding { + if in == nil { + return nil + } + out := new(TriggerSpecBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerSpecTemplate) DeepCopyInto(out *TriggerSpecTemplate) { + *out = *in + if in.Ref != nil { + in, out := &in.Ref, &out.Ref + *out = new(string) + **out = **in + } + if in.Spec != nil { + in, out := &in.Spec, &out.Spec + *out = new(TriggerTemplateSpec) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerSpecTemplate. +func (in *TriggerSpecTemplate) DeepCopy() *TriggerSpecTemplate { + if in == nil { + return nil + } + out := new(TriggerSpecTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerTemplate) DeepCopyInto(out *TriggerTemplate) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerTemplate. +func (in *TriggerTemplate) DeepCopy() *TriggerTemplate { + if in == nil { + return nil + } + out := new(TriggerTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TriggerTemplate) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerTemplateList) DeepCopyInto(out *TriggerTemplateList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]TriggerTemplate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerTemplateList. +func (in *TriggerTemplateList) DeepCopy() *TriggerTemplateList { + if in == nil { + return nil + } + out := new(TriggerTemplateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TriggerTemplateList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerTemplateSpec) DeepCopyInto(out *TriggerTemplateSpec) { + *out = *in + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]ParamSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ResourceTemplates != nil { + in, out := &in.ResourceTemplates, &out.ResourceTemplates + *out = make([]TriggerResourceTemplate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerTemplateSpec. +func (in *TriggerTemplateSpec) DeepCopy() *TriggerTemplateSpec { + if in == nil { + return nil + } + out := new(TriggerTemplateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerTemplateStatus) DeepCopyInto(out *TriggerTemplateStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerTemplateStatus. +func (in *TriggerTemplateStatus) DeepCopy() *TriggerTemplateStatus { + if in == nil { + return nil + } + out := new(TriggerTemplateStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebhookInterceptor) DeepCopyInto(out *WebhookInterceptor) { + *out = *in + if in.ObjectRef != nil { + in, out := &in.ObjectRef, &out.ObjectRef + *out = new(corev1.ObjectReference) + **out = **in + } + if in.URL != nil { + in, out := &in.URL, &out.URL + *out = new(apis.URL) + (*in).DeepCopyInto(*out) + } + if in.Header != nil { + in, out := &in.Header, &out.Header + *out = make([]v1beta1.Param, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookInterceptor. +func (in *WebhookInterceptor) DeepCopy() *WebhookInterceptor { + if in == nil { + return nil + } + out := new(WebhookInterceptor) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/cluster_trigger_binding_defaults.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/cluster_trigger_binding_defaults.go new file mode 100644 index 0000000000..9d1badc009 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/cluster_trigger_binding_defaults.go @@ -0,0 +1,24 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" +) + +// SetDefaults initializes ClusterTriggerBinding ctb with its default values. +func (ctb *ClusterTriggerBinding) SetDefaults(ctx context.Context) {} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/cluster_trigger_binding_types.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/cluster_trigger_binding_types.go new file mode 100644 index 0000000000..c9ad3a5286 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/cluster_trigger_binding_types.go @@ -0,0 +1,69 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +// Check that ClusterTriggerBinding may be validated and defaulted. +var _ apis.Validatable = (*ClusterTriggerBinding)(nil) +var _ apis.Defaultable = (*ClusterTriggerBinding)(nil) + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +k8s:openapi-gen=true + +// ClusterTriggerBinding is a TriggerBinding with a cluster scope. +// ClusterTriggerBindings are used to represent TriggerBindings that +// should be publicly addressable from any namespace in the cluster. +type ClusterTriggerBinding struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec holds the desired state of the ClusterTriggerBinding from the client + // +optional + Spec TriggerBindingSpec `json:"spec,omitempty"` + + // +optional + Status TriggerBindingStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterTriggerBindingList contains a list of ClusterTriggerBinding +type ClusterTriggerBindingList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClusterTriggerBinding `json:"items"` +} + +func (ctb *ClusterTriggerBinding) TriggerBindingSpec() TriggerBindingSpec { + return ctb.Spec +} + +func (ctb *ClusterTriggerBinding) TriggerBindingMetadata() metav1.ObjectMeta { + return ctb.ObjectMeta +} + +func (ctb *ClusterTriggerBinding) Copy() TriggerBindingInterface { + return ctb.DeepCopy() +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/cluster_trigger_binding_validation.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/cluster_trigger_binding_validation.go new file mode 100644 index 0000000000..d892ffd857 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/cluster_trigger_binding_validation.go @@ -0,0 +1,34 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + + "github.com/tektoncd/pipeline/pkg/apis/validate" + "knative.dev/pkg/apis" +) + +func (ctb *ClusterTriggerBinding) Validate(ctx context.Context) *apis.FieldError { + if err := validate.ObjectMetadata(ctb.GetObjectMeta()); err != nil { + return err.ViaField("metadata") + } + if apis.IsInDelete(ctx) { + return nil + } + return ctb.Spec.Validate(ctx) +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/doc.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/doc.go new file mode 100644 index 0000000000..933741aa31 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/doc.go @@ -0,0 +1,23 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +// package v1beta1 contains API Schema definitions for the triggers v1beta1 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=github.com/tektoncd/triggers/pkg/apis/triggers +// +k8s:defaulter-gen=TypeMeta +// +groupName=triggers.tekton.dev +package v1beta1 diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/event_listener_defaults.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/event_listener_defaults.go new file mode 100644 index 0000000000..5d6df5c378 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/event_listener_defaults.go @@ -0,0 +1,49 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + + "github.com/tektoncd/triggers/pkg/apis/config" + "github.com/tektoncd/triggers/pkg/apis/triggers/contexts" +) + +// SetDefaults sets the defaults on the object. +func (el *EventListener) SetDefaults(ctx context.Context) { + cfg := config.FromContextOrDefaults(ctx) + if contexts.IsUpgradeViaDefaulting(ctx) { + defaultSA := cfg.Defaults.DefaultServiceAccount + // set defaults + if el.Spec.ServiceAccountName == "" && defaultSA != "" { + el.Spec.ServiceAccountName = defaultSA + } + + if el.Spec.Resources.KubernetesResource != nil { + if el.Spec.Resources.KubernetesResource.Replicas != nil && *el.Spec.Resources.KubernetesResource.Replicas == 0 { + *el.Spec.Resources.KubernetesResource.Replicas = 1 + } + } + + for i, t := range el.Spec.Triggers { + triggerSpecBindingArray(el.Spec.Triggers[i].Bindings).defaultBindings() + for _, ti := range t.Interceptors { + ti.defaultInterceptorKind() + } + } + } +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/event_listener_types.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/event_listener_types.go new file mode 100644 index 0000000000..cd6f8512fe --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/event_listener_types.go @@ -0,0 +1,355 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "fmt" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" + "knative.dev/pkg/apis/duck/v1beta1" + "knative.dev/pkg/kmeta" +) + +// Check that EventListener may be validated and defaulted. +var _ apis.Validatable = (*EventListener)(nil) +var _ apis.Defaultable = (*EventListener)(nil) + +// +genclient +// +genreconciler:krshapedlogic=false +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// EventListener exposes a service to accept HTTP event payloads. +// +// +k8s:openapi-gen=true +type EventListener struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + // Spec holds the desired state of the EventListener from the client + // +optional + Spec EventListenerSpec `json:"spec"` + // +optional + Status EventListenerStatus `json:"status,omitempty"` +} + +var _ kmeta.OwnerRefable = (*EventListener)(nil) + +// EventListenerSpec defines the desired state of the EventListener, represented +// by a list of Triggers. +type EventListenerSpec struct { + ServiceAccountName string `json:"serviceAccountName,omitempty"` + // +listType=atomic + Triggers []EventListenerTrigger `json:"triggers,omitempty"` + // Trigger groups allow for centralized processing of an interceptor chain + // +listType=atomic + TriggerGroups []EventListenerTriggerGroup `json:"triggerGroups,omitempty"` + NamespaceSelector NamespaceSelector `json:"namespaceSelector,omitempty"` + LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"` + Resources Resources `json:"resources,omitempty"` + CloudEventURI string `json:"cloudEventURI,omitempty"` +} + +type Resources struct { + KubernetesResource *KubernetesResource `json:"kubernetesResource,omitempty"` + CustomResource *CustomResource `json:"customResource,omitempty"` +} + +type CustomResource struct { + runtime.RawExtension `json:",inline"` +} + +type KubernetesResource struct { + Replicas *int32 `json:"replicas,omitempty"` + ServiceType corev1.ServiceType `json:"serviceType,omitempty"` + ServicePort *int32 `json:"servicePort,omitempty"` + duckv1.WithPodSpec `json:"spec,omitempty"` +} + +type PodTemplate struct { + // If specified, the pod's tolerations. + // +optional + // +listType=atomic + Tolerations []corev1.Toleration `json:"tolerations,omitempty"` + + // NodeSelector is a selector which must be true for the pod to fit on a node. + // Selector which must match a node's labels for the pod to be scheduled on that node. + // More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + // +optional + NodeSelector map[string]string `json:"nodeSelector,omitempty"` +} + +// EventListenerTrigger represents a connection between TriggerBinding, Params, +// and TriggerTemplate; TriggerBinding provides extracted values for +// TriggerTemplate to then create resources from. TriggerRef can also be +// provided instead of TriggerBinding, Interceptors and TriggerTemplate +type EventListenerTrigger struct { + // +listType=atomic + Bindings []*EventListenerBinding `json:"bindings,omitempty"` + Template *EventListenerTemplate `json:"template,omitempty"` + TriggerRef string `json:"triggerRef,omitempty"` + // +optional + Name string `json:"name,omitempty"` + // +listType=atomic + Interceptors []*EventInterceptor `json:"interceptors,omitempty"` + // ServiceAccountName optionally associates credentials with each trigger; + // more granular authorization for + // who is allowed to utilize the associated pipeline + // vs. defaulting to whatever permissions are associated + // with the entire EventListener and associated sink facilitates + // multi-tenant model based scenarios + // +optional + ServiceAccountName string `json:"serviceAccountName,omitempty"` +} + +// EventListenerTriggerGroup defines a group of Triggers that share a common set of interceptors +type EventListenerTriggerGroup struct { + Name string `json:"name"` + // +listType=atomic + Interceptors []*TriggerInterceptor `json:"interceptors"` + TriggerSelector EventListenerTriggerSelector `json:"triggerSelector"` +} + +// EventListenerTriggerSelector defines ways to select a group of triggers using their metadata +type EventListenerTriggerSelector struct { + NamespaceSelector NamespaceSelector `json:"namespaceSelector,omitempty"` + LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"` +} + +// EventInterceptor provides a hook to intercept and pre-process events +type EventInterceptor = TriggerInterceptor + +// SecretRef contains the information required to reference a single secret string +// This is needed because the other secretRef types are not cross-namespace and do not +// actually contain the "SecretName" field, which allows us to access a single secret value. +type SecretRef struct { + SecretKey string `json:"secretKey,omitempty"` + SecretName string `json:"secretName,omitempty"` +} + +// EventListenerBinding refers to a particular TriggerBinding or ClusterTriggerBinding resource. +type EventListenerBinding = TriggerSpecBinding + +// EventListenerTemplate refers to a particular TriggerTemplate resource. +type EventListenerTemplate = TriggerSpecTemplate + +// EventListenerList contains a list of TriggerBinding +// +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type EventListenerList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []EventListener `json:"items"` +} + +// EventListenerStatus holds the status of the EventListener +// +k8s:deepcopy-gen=true +type EventListenerStatus struct { + duckv1.Status `json:",inline"` + + // EventListener is Addressable. It currently exposes the service DNS + // address of the the EventListener sink + v1beta1.AddressStatus `json:",inline"` + + // Configuration stores configuration for the EventListener service + Configuration EventListenerConfig `json:"configuration"` +} + +// EventListenerConfig stores configuration for resources generated by the +// EventListener +type EventListenerConfig struct { + // GeneratedResourceName is the name given to all resources reconciled by + // the EventListener + GeneratedResourceName string `json:"generatedName"` +} + +// NamespaceSelector is a selector for selecting either all namespaces or a +// list of namespaces. +// +k8s:openapi-gen=true +type NamespaceSelector struct { + // List of namespace names. + // +listType=atomic + MatchNames []string `json:"matchNames,omitempty"` +} + +// The conditions that are internally resolved by the EventListener reconciler +const ( + // ServiceExists is the ConditionType set on the EventListener, which + // specifies Service existence. + ServiceExists apis.ConditionType = "Service" + // DeploymentExists is the ConditionType set on the EventListener, which + // specifies Deployment existence. + DeploymentExists apis.ConditionType = "Deployment" +) + +// Check that EventListener may be validated and defaulted. +// TriggerBindingKind defines the type of TriggerBinding used by the EventListener. +type TriggerBindingKind string + +const ( + // NamespacedTriggerBindingKind indicates that triggerbinding type has a namespace scope. + NamespacedTriggerBindingKind TriggerBindingKind = "TriggerBinding" + // ClusterTriggerBindingKind indicates that triggerbinding type has a cluster scope. + ClusterTriggerBindingKind TriggerBindingKind = "ClusterTriggerBinding" +) + +var eventListenerCondSet = apis.NewLivingConditionSet( + ServiceExists, + DeploymentExists, +) + +// GetGroupVersionKind implements kmeta.OwnerRefable +func (el *EventListener) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("EventListener") +} + +// GetCondition returns the Condition matching the given type. +func (els *EventListenerStatus) GetCondition(t apis.ConditionType) *apis.Condition { + return eventListenerCondSet.Manage(els).GetCondition(t) +} + +// SetCondition sets the condition, unsetting previous conditions with the same +// type as necessary. This is a local change and needs to be persisted to the +// K8s API elsewhere. +func (els *EventListenerStatus) SetCondition(newCond *apis.Condition) { + if newCond != nil { + // TODO: Should the ConditionManager be set somewhere? + eventListenerCondSet.Manage(els).SetCondition(*newCond) + } +} + +func (els *EventListenerStatus) SetReadyCondition() { + for _, ct := range []apis.ConditionType{ + ServiceExists, + DeploymentExists, + apis.ConditionType(appsv1.DeploymentProgressing), + apis.ConditionType(appsv1.DeploymentAvailable)} { + if sc := els.GetCondition(ct); sc != nil { + if sc.Status != corev1.ConditionTrue { + els.SetCondition(&apis.Condition{ + Type: apis.ConditionReady, + Status: corev1.ConditionFalse, + Message: fmt.Sprintf("Condition %s has status: %s with message: %s", sc.Type, sc.Status, sc.Message), + }) + return + } + } + } + els.SetCondition(&apis.Condition{ + Type: apis.ConditionReady, + Status: corev1.ConditionTrue, + Message: "EventListener is ready", + }) +} + +// SetDeploymentConditions sets the Deployment conditions on the EventListener, +// which is a reflection of the actual Deployment status. +func (els *EventListenerStatus) SetDeploymentConditions(deploymentConditions []appsv1.DeploymentCondition) { + // Manually remove the DeploymentReplicaFailure condition since it does + // not always exist and would stay around otherwise + replicaFailureIndex := -1 + for i := range els.Conditions { + if els.Conditions[i].Type == apis.ConditionType(appsv1.DeploymentReplicaFailure) { + replicaFailureIndex = i + break + } + } + if replicaFailureIndex != -1 { + els.Conditions = append(els.Conditions[:replicaFailureIndex], els.Conditions[replicaFailureIndex+1:]...) + } + for _, cond := range deploymentConditions { + els.SetCondition(&apis.Condition{ + Type: apis.ConditionType(cond.Type), + Status: cond.Status, + Reason: cond.Reason, + Message: cond.Message, + }) + } +} + +func (els *EventListenerStatus) SetConditionsForDynamicObjects(conditions v1beta1.Conditions) { + for _, cond := range conditions { + els.SetCondition(&apis.Condition{ + Type: cond.Type, + Status: cond.Status, + Reason: cond.Reason, + Message: cond.Message, + }) + } + + els.SetCondition(&apis.Condition{ + Type: apis.ConditionReady, + Status: corev1.ConditionTrue, + Message: "EventListener is ready", + }) +} + +// SetExistsCondition simplifies setting the exists conditions on the +// EventListenerStatus. +func (els *EventListenerStatus) SetExistsCondition(cond apis.ConditionType, err error) { + if err != nil { + els.SetCondition(&apis.Condition{ + Type: cond, + Status: corev1.ConditionFalse, + Message: err.Error(), + }) + } else { + els.SetCondition(&apis.Condition{ + Type: cond, + Status: corev1.ConditionTrue, + Message: fmt.Sprintf("%s exists", cond), + }) + } +} + +// InitializeConditions will set all conditions in eventListenerCondSet to false +// for the EventListener. This does not use the InitializeCondition() provided +// by the conditionsImpl to avoid setting the happy condition. This is a local +// change and needs to be persisted to the K8s API elsewhere. +func (els *EventListenerStatus) InitializeConditions() { + for _, condition := range []apis.ConditionType{ + ServiceExists, + DeploymentExists, + apis.ConditionReady, + } { + els.SetCondition(&apis.Condition{ + Type: condition, + Status: corev1.ConditionFalse, + }) + } +} + +// SetAddress sets the address (as part of Addressable contract) and marks the correct condition. +func (els *EventListenerStatus) SetAddress(hostname string) { + if els.Address == nil { + els.Address = &v1beta1.Addressable{} + } + if hostname != "" { + els.Address.URL = &apis.URL{ + Scheme: "http", + Host: hostname, + } + } else { + els.Address.URL = nil + } +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/event_listener_validation.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/event_listener_validation.go new file mode 100644 index 0000000000..cfda8fd369 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/event_listener_validation.go @@ -0,0 +1,329 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + + "github.com/tektoncd/triggers/pkg/apis/config" + + "github.com/tektoncd/triggers/pkg/apis/triggers" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/validation" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" +) + +var ( + reservedEnvVars = sets.NewString( + "TLS_CERT", + "TLS_KEY", + ) +) + +// Validate EventListener. +func (e *EventListener) Validate(ctx context.Context) *apis.FieldError { + var errs *apis.FieldError + if len(e.ObjectMeta.Name) > 60 { + // Since `el-` is added as the prefix of EventListener services, the name of EventListener must be no more than 60 characters long. + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("eventListener name '%s' must be no more than 60 characters long", e.ObjectMeta.Name), "metadata.name")) + } + + if len(e.GetObjectMeta().GetAnnotations()) != 0 { + errs = errs.Also(triggers.ValidateAnnotations(e.GetObjectMeta().GetAnnotations())) + } + + if apis.IsInDelete(ctx) { + return nil + } + return errs.Also(e.Spec.validate(ctx)) +} + +func (s *EventListenerSpec) validate(ctx context.Context) (errs *apis.FieldError) { + if s.LabelSelector == nil && len(s.NamespaceSelector.MatchNames) == 0 && len(s.TriggerGroups) == 0 && len(s.Triggers) == 0 { + return apis.ErrMissingOneOf("spec.labelSelector", "spec.namespaceSelector", "spec.triggerGroups", "spec.triggers") + } + + for i, trigger := range s.Triggers { + errs = errs.Also(trigger.validate(ctx).ViaField(fmt.Sprintf("spec.triggers[%d]", i))) + } + + // Both Kubernetes and Custom resource can't be present at the same time + if s.Resources.KubernetesResource != nil && s.Resources.CustomResource != nil { + return apis.ErrMultipleOneOf("spec.resources.kubernetesResource", "spec.resources.customResource") + } + + if s.Resources.KubernetesResource != nil { + errs = errs.Also(validateKubernetesObject(s.Resources.KubernetesResource).ViaField("spec.resources.kubernetesResource")) + } + + if s.Resources.CustomResource != nil { + errs = errs.Also(validateCustomObject(s.Resources.CustomResource).ViaField("spec.resources.customResource")) + } + + if len(s.TriggerGroups) > 0 { + err := ValidateEnabledAPIFields(ctx, "spec.triggerGroups", config.AlphaAPIFieldValue) + if err != nil { + errs = errs.Also(err) + } else { + for i, group := range s.TriggerGroups { + errs = errs.Also(group.validate(ctx).ViaField(fmt.Sprintf("spec.triggerGroups[%d]", i))) + } + } + } + + if s.CloudEventURI != "" { + err := ValidateEnabledAPIFields(ctx, "spec.CloudEventURI", config.AlphaAPIFieldValue) + if err != nil { + errs = errs.Also(err) + } + } + return errs +} + +func (g *EventListenerTriggerGroup) validate(ctx context.Context) (errs *apis.FieldError) { + if g.TriggerSelector.LabelSelector == nil && len(g.TriggerSelector.NamespaceSelector.MatchNames) == 0 { + errs = errs.Also(apis.ErrMissingOneOf("triggerSelector.labelSelector", "triggerSelector.namespaceSelector")) + } + if len(g.Interceptors) == 0 { + errs = errs.Also(apis.ErrMissingField("interceptors")) + } + return errs +} + +func validateCustomObject(customData *CustomResource) (errs *apis.FieldError) { + orig := duckv1.WithPod{} + decoder := json.NewDecoder(bytes.NewBuffer(customData.RawExtension.Raw)) + + if err := decoder.Decode(&orig); err != nil { + errs = errs.Also(apis.ErrInvalidValue(err, "spec")) + } + + if len(orig.Spec.Template.Spec.Containers) > 1 { + errs = errs.Also(apis.ErrMultipleOneOf("containers").ViaField("spec.template.spec")) + } + errs = errs.Also(apis.CheckDisallowedFields(orig.Spec.Template.Spec, + *podSpecMask(&orig.Spec.Template.Spec)).ViaField("spec.template.spec")) + + // bounded by condition because containers fields are optional so there is a chance that containers can be nil. + if len(orig.Spec.Template.Spec.Containers) == 1 { + errs = errs.Also(apis.CheckDisallowedFields(orig.Spec.Template.Spec.Containers[0], + *containerFieldMask(&orig.Spec.Template.Spec.Containers[0])).ViaField("spec.template.spec.containers[0]")) + // validate env + errs = errs.Also(validateEnv(orig.Spec.Template.Spec.Containers[0].Env).ViaField("spec.template.spec.containers[0].env")) + } + + return errs +} + +func validateKubernetesObject(orig *KubernetesResource) (errs *apis.FieldError) { + if orig.Replicas != nil { + if *orig.Replicas < 0 { + errs = errs.Also(apis.ErrInvalidValue(*orig.Replicas, "spec.replicas")) + } + } + if len(orig.Template.Spec.Containers) > 1 { + errs = errs.Also(apis.ErrMultipleOneOf("containers").ViaField("spec.template.spec")) + } + errs = errs.Also(apis.CheckDisallowedFields(orig.Template.Spec, + *podSpecMask(&orig.Template.Spec)).ViaField("spec.template.spec")) + + // bounded by condition because containers fields are optional so there is a chance that containers can be nil. + if len(orig.Template.Spec.Containers) == 1 { + errs = errs.Also(apis.CheckDisallowedFields(orig.Template.Spec.Containers[0], + *containerFieldMask(&orig.Template.Spec.Containers[0])).ViaField("spec.template.spec.containers[0]")) + // validate env + errs = errs.Also(validateEnv(orig.Template.Spec.Containers[0].Env).ViaField("spec.template.spec.containers[0].env")) + } + + return errs +} + +func validateEnv(envVars []corev1.EnvVar) (errs *apis.FieldError) { + var ( + count = 0 + envValue string + ) + for i, env := range envVars { + errs = errs.Also(validateEnvVar(env).ViaIndex(i)) + if reservedEnvVars.Has(env.Name) { + count++ + envValue = env.Name + } + } + // This is to make sure both TLS_CERT and TLS_KEY is set for tls connection + if count == 1 { + errs = errs.Also(&apis.FieldError{ + Message: fmt.Sprintf("Expected env's are TLS_CERT and TLS_KEY, but got only one env %s", envValue), + }) + } + return errs +} + +func validateEnvVar(env corev1.EnvVar) (errs *apis.FieldError) { + errs = errs.Also(apis.CheckDisallowedFields(env, *envVarMask(&env))) + + return errs.Also(validateEnvValueFrom(env.ValueFrom).ViaField("valueFrom")) +} + +func validateEnvValueFrom(source *corev1.EnvVarSource) *apis.FieldError { + if source == nil { + return nil + } + return apis.CheckDisallowedFields(*source, *envVarSourceMask(source)) +} + +// envVarSourceMask performs a _shallow_ copy of the Kubernetes EnvVarSource object to a new +// Kubernetes EnvVarSource object bringing over only the fields allowed in the Triggers EventListener API. +func envVarSourceMask(in *corev1.EnvVarSource) *corev1.EnvVarSource { + if in == nil { + return nil + } + out := new(corev1.EnvVarSource) + // Allowed fields + out.SecretKeyRef = in.SecretKeyRef + + // Disallowed fields + out.ConfigMapKeyRef = nil + out.FieldRef = nil + out.ResourceFieldRef = nil + + return out +} + +// envVarMask performs a _shallow_ copy of the Kubernetes EnvVar object to a new +// Kubernetes EnvVar object bringing over only the fields allowed in the Triggers EventListener API. +func envVarMask(in *corev1.EnvVar) *corev1.EnvVar { + if in == nil { + return nil + } + out := new(corev1.EnvVar) + // Allowed fields + out.Name = in.Name + out.ValueFrom = in.ValueFrom + + // Disallowed fields + out.Value = "" + + return out +} + +func containerFieldMask(in *corev1.Container) *corev1.Container { + out := new(corev1.Container) + out.Resources = in.Resources + out.Env = in.Env + + // Disallowed fields + // This list clarifies which all container attributes are not allowed. + out.Name = "" + out.Image = "" + out.Args = nil + out.Ports = nil + out.LivenessProbe = nil + out.ReadinessProbe = nil + out.StartupProbe = nil + out.Command = nil + out.VolumeMounts = nil + out.ImagePullPolicy = "" + out.Lifecycle = nil + out.Stdin = false + out.StdinOnce = false + out.TerminationMessagePath = "" + out.TerminationMessagePolicy = "" + out.WorkingDir = "" + out.TTY = false + out.VolumeDevices = nil + out.EnvFrom = nil + + return out +} + +// podSpecMask performs a _shallow_ copy of the Kubernetes PodSpec object to a new +// Kubernetes PodSpec object bringing over only the fields allowed in the Triggers EvenListener. +func podSpecMask(in *corev1.PodSpec) *corev1.PodSpec { + out := new(corev1.PodSpec) + + // Allowed fields + out.ServiceAccountName = in.ServiceAccountName + out.Containers = in.Containers + out.Tolerations = in.Tolerations + out.NodeSelector = in.NodeSelector + + // Disallowed fields + // This list clarifies which all podspec fields are not allowed. + out.Volumes = nil + out.EnableServiceLinks = nil + out.ImagePullSecrets = nil + out.InitContainers = nil + out.RestartPolicy = "" + out.TerminationGracePeriodSeconds = nil + out.ActiveDeadlineSeconds = nil + out.DNSPolicy = "" + out.AutomountServiceAccountToken = nil + out.NodeName = "" + out.HostNetwork = false + out.HostPID = false + out.HostIPC = false + out.ShareProcessNamespace = nil + out.SecurityContext = nil + out.Hostname = "" + out.Subdomain = "" + out.Affinity = nil + out.SchedulerName = "" + out.HostAliases = nil + out.PriorityClassName = "" + out.Priority = nil + out.DNSConfig = nil + out.ReadinessGates = nil + out.RuntimeClassName = nil + + return out +} + +func (t *EventListenerTrigger) validate(ctx context.Context) (errs *apis.FieldError) { + if t.Template == nil && t.TriggerRef == "" { + errs = errs.Also(apis.ErrMissingOneOf("template", "triggerRef")) + } + + if t.TriggerRef != "" && (t.Template != nil || t.Bindings != nil || t.Interceptors != nil) { + errs = errs.Also(apis.ErrMultipleOneOf("triggerRef", "template or bindings or interceptors")) + } + + // Validate optional Bindings + errs = errs.Also(triggerSpecBindingArray(t.Bindings).validate(ctx)) + if t.Template != nil { + // Validate required TriggerTemplate + errs = errs.Also(t.Template.validate(ctx)) + } + + // Validate optional Interceptors + for i, interceptor := range t.Interceptors { + errs = errs.Also(interceptor.validate(ctx).ViaField(fmt.Sprintf("interceptors[%d]", i))) + } + + // The trigger name is added as a label value for 'tekton.dev/trigger' so it must follow the k8s label guidelines: + // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set + if err := validation.IsValidLabelValue(t.Name); len(err) > 0 { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("trigger name '%s' must be a valid label value", t.Name), "name")) + } + + return errs +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/interceptor_types.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/interceptor_types.go new file mode 100644 index 0000000000..b5b739d925 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/interceptor_types.go @@ -0,0 +1,88 @@ +package v1beta1 + +import ( + "context" + "fmt" + "strings" + + "google.golang.org/grpc/codes" +) + +type InterceptorInterface interface { + // Process executes the given InterceptorRequest. Simply getting a non-nil InterceptorResponse back is not sufficient + // to determine if the interceptor processing was successful. Instead use the InterceptorResponse.Status.Continue to + // see if processing should continue and InterceptorResponse.Status.Code to distinguish between the kinds of errors + // (i.e user errors vs system errors) + Process(ctx context.Context, r *InterceptorRequest) *InterceptorResponse +} + +// Do not generate DeepCopy(). See #827 +// +k8s:deepcopy-gen=false +type InterceptorRequest struct { + // Body is the incoming HTTP event body. We use a "string" representation of the JSON body + // in order to preserve the body exactly as it was sent (including spaces etc.). This is necessary + // for some interceptors e.g. GitHub for validating the body with a signature. While []byte can also + // store an exact representation of the body, `json.Marshal` will compact []byte to a base64 encoded + // string which means that we will lose the spaces any time we marshal this struct. + Body string `json:"body,omitempty"` + + // Header are the headers for the incoming HTTP event + Header map[string][]string `json:"header,omitempty"` + + // Extensions are extra values that are added by previous interceptors in a chain + Extensions map[string]interface{} `json:"extensions,omitempty"` + + // InterceptorParams are the user specified params for interceptor in the Trigger + InterceptorParams map[string]interface{} `json:"interceptor_params,omitempty"` + + // Context contains additional metadata about the event being processed + Context *TriggerContext `json:"context"` +} + +type TriggerContext struct { + // EventURL is the URL of the incoming event + EventURL string `json:"event_url,omitempty"` + // EventID is a unique ID assigned by Triggers to each event + EventID string `json:"event_id,omitempty"` + // TriggerID is of the form namespace/$ns/triggers/$name + TriggerID string `json:"trigger_id,omitempty"` +} + +// Do not generate Deepcopy(). See #827 +// +k8s:deepcopy-gen=false +type InterceptorResponse struct { + // Extensions are additional fields that is added to the interceptor event. + Extensions map[string]interface{} `json:"extensions,omitempty"` + // Continue indicates if the EventListener should continue processing the Trigger or not + Continue bool `json:"continue"` // Don't add omitempty -- it will remove the continue field when the value is false. + // Status is an Error status containing details on any interceptor processing errors + Status Status `json:"status"` +} + +type Status struct { + // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + Code codes.Code `json:"code,omitempty"` + // A developer-facing error message, which should be in English. + Message string `json:"message,omitempty"` +} + +func (s Status) Err() StatusError { + return StatusError{s: s} +} + +type StatusError struct { + s Status +} + +func (s StatusError) Error() string { + return fmt.Sprintf("rpc error: code = %s desc = %s", s.s.Code, s.s.Message) +} + +func ParseTriggerID(triggerID string) (namespace, name string) { + splits := strings.Split(triggerID, "/") + if len(splits) != 4 { + return + } + + return splits[1], splits[3] +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/openapi_generated.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/openapi_generated.go new file mode 100644 index 0000000000..7f3076d96b --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/openapi_generated.go @@ -0,0 +1,1937 @@ +// +build !ignore_autogenerated + +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by openapi-gen. DO NOT EDIT. + +// This file was autogenerated by openapi-gen. Do not edit it manually! + +package v1beta1 + +import ( + common "k8s.io/kube-openapi/pkg/common" + spec "k8s.io/kube-openapi/pkg/validation/spec" +) + +func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { + return map[string]common.OpenAPIDefinition{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.BitbucketInterceptor": schema_pkg_apis_triggers_v1beta1_BitbucketInterceptor(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.CELInterceptor": schema_pkg_apis_triggers_v1beta1_CELInterceptor(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.CELOverlay": schema_pkg_apis_triggers_v1beta1_CELOverlay(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.ClusterTriggerBinding": schema_pkg_apis_triggers_v1beta1_ClusterTriggerBinding(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.ClusterTriggerBindingList": schema_pkg_apis_triggers_v1beta1_ClusterTriggerBindingList(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.CustomResource": schema_pkg_apis_triggers_v1beta1_CustomResource(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListener": schema_pkg_apis_triggers_v1beta1_EventListener(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerConfig": schema_pkg_apis_triggers_v1beta1_EventListenerConfig(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerList": schema_pkg_apis_triggers_v1beta1_EventListenerList(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerSpec": schema_pkg_apis_triggers_v1beta1_EventListenerSpec(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerStatus": schema_pkg_apis_triggers_v1beta1_EventListenerStatus(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerTrigger": schema_pkg_apis_triggers_v1beta1_EventListenerTrigger(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerTriggerGroup": schema_pkg_apis_triggers_v1beta1_EventListenerTriggerGroup(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerTriggerSelector": schema_pkg_apis_triggers_v1beta1_EventListenerTriggerSelector(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.GitHubInterceptor": schema_pkg_apis_triggers_v1beta1_GitHubInterceptor(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.GitLabInterceptor": schema_pkg_apis_triggers_v1beta1_GitLabInterceptor(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.InterceptorParams": schema_pkg_apis_triggers_v1beta1_InterceptorParams(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.InterceptorRef": schema_pkg_apis_triggers_v1beta1_InterceptorRef(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.InterceptorRequest": schema_pkg_apis_triggers_v1beta1_InterceptorRequest(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.InterceptorResponse": schema_pkg_apis_triggers_v1beta1_InterceptorResponse(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.KubernetesResource": schema_pkg_apis_triggers_v1beta1_KubernetesResource(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.NamespaceSelector": schema_pkg_apis_triggers_v1beta1_NamespaceSelector(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.Param": schema_pkg_apis_triggers_v1beta1_Param(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.ParamSpec": schema_pkg_apis_triggers_v1beta1_ParamSpec(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.PodTemplate": schema_pkg_apis_triggers_v1beta1_PodTemplate(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.Resources": schema_pkg_apis_triggers_v1beta1_Resources(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.SecretRef": schema_pkg_apis_triggers_v1beta1_SecretRef(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.Status": schema_pkg_apis_triggers_v1beta1_Status(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.StatusError": schema_pkg_apis_triggers_v1beta1_StatusError(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.Trigger": schema_pkg_apis_triggers_v1beta1_Trigger(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerBinding": schema_pkg_apis_triggers_v1beta1_TriggerBinding(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerBindingList": schema_pkg_apis_triggers_v1beta1_TriggerBindingList(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerBindingSpec": schema_pkg_apis_triggers_v1beta1_TriggerBindingSpec(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerBindingStatus": schema_pkg_apis_triggers_v1beta1_TriggerBindingStatus(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerContext": schema_pkg_apis_triggers_v1beta1_TriggerContext(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerInterceptor": schema_pkg_apis_triggers_v1beta1_TriggerInterceptor(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerList": schema_pkg_apis_triggers_v1beta1_TriggerList(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerResourceTemplate": schema_pkg_apis_triggers_v1beta1_TriggerResourceTemplate(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerSpec": schema_pkg_apis_triggers_v1beta1_TriggerSpec(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerSpecBinding": schema_pkg_apis_triggers_v1beta1_TriggerSpecBinding(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerSpecTemplate": schema_pkg_apis_triggers_v1beta1_TriggerSpecTemplate(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerTemplate": schema_pkg_apis_triggers_v1beta1_TriggerTemplate(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerTemplateList": schema_pkg_apis_triggers_v1beta1_TriggerTemplateList(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerTemplateSpec": schema_pkg_apis_triggers_v1beta1_TriggerTemplateSpec(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerTemplateStatus": schema_pkg_apis_triggers_v1beta1_TriggerTemplateStatus(ref), + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.WebhookInterceptor": schema_pkg_apis_triggers_v1beta1_WebhookInterceptor(ref), + } +} + +func schema_pkg_apis_triggers_v1beta1_BitbucketInterceptor(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "BitbucketInterceptor provides a webhook to intercept and pre-process events", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "secretRef": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.SecretRef"), + }, + }, + "eventTypes": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.SecretRef"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_CELInterceptor(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CELInterceptor provides a webhook to intercept and pre-process events", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "filter": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "overlays": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.CELOverlay"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.CELOverlay"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_CELOverlay(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CELOverlay provides a way to modify the request body using CEL expressions", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "expression": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_triggers_v1beta1_ClusterTriggerBinding(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterTriggerBinding is a TriggerBinding with a cluster scope. ClusterTriggerBindings are used to represent TriggerBindings that should be publicly addressable from any namespace in the cluster.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec holds the desired state of the ClusterTriggerBinding from the client", + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerBindingSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerBindingStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerBindingSpec", "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerBindingStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_ClusterTriggerBindingList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterTriggerBindingList contains a list of ClusterTriggerBinding", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.ClusterTriggerBinding"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.ClusterTriggerBinding", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_CustomResource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + }, + }, + } +} + +func schema_pkg_apis_triggers_v1beta1_EventListener(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EventListener exposes a service to accept HTTP event payloads.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec holds the desired state of the EventListener from the client", + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerSpec", "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_EventListenerConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EventListenerConfig stores configuration for resources generated by the EventListener", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "generatedName": { + SchemaProps: spec.SchemaProps{ + Description: "GeneratedResourceName is the name given to all resources reconciled by the EventListener", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"generatedName"}, + }, + }, + } +} + +func schema_pkg_apis_triggers_v1beta1_EventListenerList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EventListenerList contains a list of TriggerBinding", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListener"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListener", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_EventListenerSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EventListenerSpec defines the desired state of the EventListener, represented by a list of Triggers.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "serviceAccountName": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "triggers": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerTrigger"), + }, + }, + }, + }, + }, + "triggerGroups": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Trigger groups allow for centralized processing of an interceptor chain", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerTriggerGroup"), + }, + }, + }, + }, + }, + "namespaceSelector": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.NamespaceSelector"), + }, + }, + "labelSelector": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.Resources"), + }, + }, + "cloudEventURI": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerTrigger", "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerTriggerGroup", "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.NamespaceSelector", "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.Resources", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_EventListenerStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EventListenerStatus holds the status of the EventListener", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "observedGeneration": { + SchemaProps: spec.SchemaProps{ + Description: "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Conditions the latest available observations of a resource's current state.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("knative.dev/pkg/apis.Condition"), + }, + }, + }, + }, + }, + "annotations": { + SchemaProps: spec.SchemaProps{ + Description: "Annotations is additional Status fields for the Resource to save some additional State as well as convey more information to the user. This is roughly akin to Annotations on any k8s resource, just the reconciler conveying richer information outwards.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "address": { + SchemaProps: spec.SchemaProps{ + Ref: ref("knative.dev/pkg/apis/duck/v1beta1.Addressable"), + }, + }, + "configuration": { + SchemaProps: spec.SchemaProps{ + Description: "Configuration stores configuration for the EventListener service", + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerConfig"), + }, + }, + }, + Required: []string{"configuration"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerConfig", "knative.dev/pkg/apis.Condition", "knative.dev/pkg/apis/duck/v1beta1.Addressable"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_EventListenerTrigger(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EventListenerTrigger represents a connection between TriggerBinding, Params, and TriggerTemplate; TriggerBinding provides extracted values for TriggerTemplate to then create resources from. TriggerRef can also be provided instead of TriggerBinding, Interceptors and TriggerTemplate", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "bindings": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerSpecBinding"), + }, + }, + }, + }, + }, + "template": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerSpecTemplate"), + }, + }, + "triggerRef": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "interceptors": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerInterceptor"), + }, + }, + }, + }, + }, + "serviceAccountName": { + SchemaProps: spec.SchemaProps{ + Description: "ServiceAccountName optionally associates credentials with each trigger; more granular authorization for who is allowed to utilize the associated pipeline vs. defaulting to whatever permissions are associated with the entire EventListener and associated sink facilitates multi-tenant model based scenarios", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerInterceptor", "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerSpecBinding", "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerSpecTemplate"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_EventListenerTriggerGroup(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EventListenerTriggerGroup defines a group of Triggers that share a common set of interceptors", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "interceptors": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerInterceptor"), + }, + }, + }, + }, + }, + "triggerSelector": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerTriggerSelector"), + }, + }, + }, + Required: []string{"name", "interceptors", "triggerSelector"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.EventListenerTriggerSelector", "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerInterceptor"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_EventListenerTriggerSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EventListenerTriggerSelector defines ways to select a group of triggers using their metadata", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "namespaceSelector": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.NamespaceSelector"), + }, + }, + "labelSelector": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.NamespaceSelector", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_GitHubInterceptor(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GitHubInterceptor provides a webhook to intercept and pre-process events", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "secretRef": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.SecretRef"), + }, + }, + "eventTypes": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.SecretRef"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_GitLabInterceptor(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GitLabInterceptor provides a webhook to intercept and pre-process events", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "secretRef": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.SecretRef"), + }, + }, + "eventTypes": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.SecretRef"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_InterceptorParams(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "InterceptorParams defines a key-value pair that can be passed on an interceptor", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON"), + }, + }, + }, + Required: []string{"name", "value"}, + }, + }, + Dependencies: []string{ + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_InterceptorRef(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "InterceptorRef provides a Reference to a ClusterInterceptor", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names", + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "InterceptorKind indicates the kind of the Interceptor, namespaced or cluster scoped. Currently only InterceptorKind is ClusterInterceptor, so the only valid value is the default one", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "API version of the referent", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_triggers_v1beta1_InterceptorRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Do not generate DeepCopy(). See #827", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "body": { + SchemaProps: spec.SchemaProps{ + Description: "Body is the incoming HTTP event body. We use a \"string\" representation of the JSON body in order to preserve the body exactly as it was sent (including spaces etc.). This is necessary for some interceptors e.g. GitHub for validating the body with a signature. While []byte can also store an exact representation of the body, `json.Marshal` will compact []byte to a base64 encoded string which means that we will lose the spaces any time we marshal this struct.", + Type: []string{"string"}, + Format: "", + }, + }, + "header": { + SchemaProps: spec.SchemaProps{ + Description: "Header are the headers for the incoming HTTP event", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + "extensions": { + SchemaProps: spec.SchemaProps{ + Description: "Extensions are extra values that are added by previous interceptors in a chain", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + "interceptor_params": { + SchemaProps: spec.SchemaProps{ + Description: "InterceptorParams are the user specified params for interceptor in the Trigger", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + "context": { + SchemaProps: spec.SchemaProps{ + Description: "Context contains additional metadata about the event being processed", + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerContext"), + }, + }, + }, + Required: []string{"context"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerContext"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_InterceptorResponse(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Do not generate Deepcopy(). See #827", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "extensions": { + SchemaProps: spec.SchemaProps{ + Description: "Extensions are additional fields that is added to the interceptor event.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + "continue": { + SchemaProps: spec.SchemaProps{ + Description: "Continue indicates if the EventListener should continue processing the Trigger or not", + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status is an Error status containing details on any interceptor processing errors", + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.Status"), + }, + }, + }, + Required: []string{"continue", "status"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.Status"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_KubernetesResource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "replicas": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int32", + }, + }, + "serviceType": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "servicePort": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int32", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("knative.dev/pkg/apis/duck/v1.WithPodSpec"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "knative.dev/pkg/apis/duck/v1.WithPodSpec"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_NamespaceSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NamespaceSelector is a selector for selecting either all namespaces or a list of namespaces.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "matchNames": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of namespace names.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_triggers_v1beta1_Param(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Param defines a string value to be used for a ParamSpec with the same name.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "value"}, + }, + }, + } +} + +func schema_pkg_apis_triggers_v1beta1_ParamSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ParamSpec defines an arbitrary named input whose value can be supplied by a `Param`.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name declares the name by which a parameter is referenced.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a user-facing description of the parameter that may be used to populate a UI.", + Type: []string{"string"}, + Format: "", + }, + }, + "default": { + SchemaProps: spec.SchemaProps{ + Description: "Default is the value a parameter takes if no input value via a Param is supplied.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + } +} + +func schema_pkg_apis_triggers_v1beta1_PodTemplate(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "tolerations": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "If specified, the pod's tolerations.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.Toleration"), + }, + }, + }, + }, + }, + "nodeSelector": { + SchemaProps: spec.SchemaProps{ + Description: "NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Toleration"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_Resources(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kubernetesResource": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.KubernetesResource"), + }, + }, + "customResource": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.CustomResource"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.CustomResource", "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.KubernetesResource"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_SecretRef(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SecretRef contains the information required to reference a single secret string This is needed because the other secretRef types are not cross-namespace and do not actually contain the \"SecretName\" field, which allows us to access a single secret value.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "secretKey": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "secretName": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_triggers_v1beta1_Status(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "code": { + SchemaProps: spec.SchemaProps{ + Description: "The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code].", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "A developer-facing error message, which should be in English.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_triggers_v1beta1_StatusError(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "s": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.Status"), + }, + }, + }, + Required: []string{"s"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.Status"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_Trigger(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Trigger defines a mapping of an input event to parameters. This is used to extract information from events to be passed to TriggerTemplates within a Trigger.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec holds the desired state of the Trigger", + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerSpec"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_TriggerBinding(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TriggerBinding defines a mapping of an input event to parameters. This is used to extract information from events to be passed to TriggerTemplates within a Trigger.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec holds the desired state of the TriggerBinding", + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerBindingSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerBindingStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerBindingSpec", "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerBindingStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_TriggerBindingList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TriggerBindingList contains a list of TriggerBindings. We don't use this but it's required for certain codegen features.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerBinding"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerBinding", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_TriggerBindingSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TriggerBindingSpec defines the desired state of the TriggerBinding.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "params": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Params defines the parameter mapping from the given input event.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.Param"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.Param"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_TriggerBindingStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TriggerBindingStatus defines the observed state of TriggerBinding.", + Type: []string{"object"}, + }, + }, + } +} + +func schema_pkg_apis_triggers_v1beta1_TriggerContext(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "event_url": { + SchemaProps: spec.SchemaProps{ + Description: "EventURL is the URL of the incoming event", + Type: []string{"string"}, + Format: "", + }, + }, + "event_id": { + SchemaProps: spec.SchemaProps{ + Description: "EventID is a unique ID assigned by Triggers to each event", + Type: []string{"string"}, + Format: "", + }, + }, + "trigger_id": { + SchemaProps: spec.SchemaProps{ + Description: "TriggerID is of the form namespace/$ns/triggers/$name", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_triggers_v1beta1_TriggerInterceptor(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TriggerInterceptor provides a hook to intercept and pre-process events", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Optional name to identify the current interceptor configuration", + Type: []string{"string"}, + Format: "", + }, + }, + "ref": { + SchemaProps: spec.SchemaProps{ + Description: "Ref refers to the Interceptor to use", + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.InterceptorRef"), + }, + }, + "params": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Params are the params to send to the interceptor", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.InterceptorParams"), + }, + }, + }, + }, + }, + "webhook": { + SchemaProps: spec.SchemaProps{ + Description: "WebhookInterceptor refers to an old style webhook interceptor service", + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.WebhookInterceptor"), + }, + }, + }, + Required: []string{"ref"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.InterceptorParams", "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.InterceptorRef", "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.WebhookInterceptor"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_TriggerList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TriggerList contains a list of Triggers. We don't use this but it's required for certain codegen features.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.Trigger"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.Trigger", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_TriggerResourceTemplate(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TriggerResourceTemplate describes a resource to create", + Type: []string{"object"}, + }, + }, + } +} + +func schema_pkg_apis_triggers_v1beta1_TriggerSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TriggerSpec represents a connection between TriggerSpecBinding, and TriggerSpecTemplate; TriggerSpecBinding provides extracted values for TriggerSpecTemplate to then create resources from.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "bindings": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerSpecBinding"), + }, + }, + }, + }, + }, + "template": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerSpecTemplate"), + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "interceptors": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerInterceptor"), + }, + }, + }, + }, + }, + "serviceAccountName": { + SchemaProps: spec.SchemaProps{ + Description: "ServiceAccountName optionally associates credentials with each trigger; Unlike EventListeners, this should be scoped to the same namespace as the Trigger itself", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"bindings", "template"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerInterceptor", "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerSpecBinding", "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerSpecTemplate"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_TriggerSpecBinding(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the binding param Mutually exclusive with Ref", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Value is the value of the binding param. Can contain JSONPath Has to be pointer since \"\" is a valid value Required if Name is also specified.", + Type: []string{"string"}, + Format: "", + }, + }, + "ref": { + SchemaProps: spec.SchemaProps{ + Description: "Ref is a reference to a TriggerBinding kind. Mutually exclusive with Name", + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind can only be provided if Ref is also provided. Defaults to TriggerBinding", + Type: []string{"string"}, + Format: "", + }, + }, + "apiversion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion of the binding ref", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_triggers_v1beta1_TriggerSpecTemplate(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "ref": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "apiversion": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerTemplateSpec"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerTemplateSpec"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_TriggerTemplate(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TriggerTemplate takes parameters and uses them to create CRDs", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec holds the desired state of the TriggerTemplate from the client", + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerTemplateSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerTemplateStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerTemplateSpec", "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerTemplateStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_TriggerTemplateList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TriggerTemplateList contains a list of TriggerTemplate", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerTemplate"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerTemplate", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_TriggerTemplateSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TriggerTemplateSpec holds the desired state of TriggerTemplate", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "params": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.ParamSpec"), + }, + }, + }, + }, + }, + "resourcetemplates": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerResourceTemplate"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.ParamSpec", "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1.TriggerResourceTemplate"}, + } +} + +func schema_pkg_apis_triggers_v1beta1_TriggerTemplateStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TriggerTemplateStatus describes the desired state of TriggerTemplate", + Type: []string{"object"}, + }, + }, + } +} + +func schema_pkg_apis_triggers_v1beta1_WebhookInterceptor(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "WebhookInterceptor provides a webhook to intercept and pre-process events", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "objectRef": { + SchemaProps: spec.SchemaProps{ + Description: "ObjectRef is a reference to an object that will resolve to a cluster DNS name to use as the EventInterceptor. Either objectRef or url can be specified", + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + "url": { + SchemaProps: spec.SchemaProps{ + Ref: ref("knative.dev/pkg/apis.URL"), + }, + }, + "header": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Header is a group of key-value pairs that can be appended to the interceptor request headers. This allows the interceptor to make decisions specific to an EventListenerTrigger.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Param"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1.Param", "k8s.io/api/core/v1.ObjectReference", "knative.dev/pkg/apis.URL"}, + } +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/param.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/param.go new file mode 100644 index 0000000000..b026c50272 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/param.go @@ -0,0 +1,21 @@ +package v1beta1 + +// ParamSpec defines an arbitrary named input whose value can be supplied by a +// `Param`. +type ParamSpec struct { + // Name declares the name by which a parameter is referenced. + Name string `json:"name"` + // Description is a user-facing description of the parameter that may be + // used to populate a UI. + // +optional + Description string `json:"description,omitempty"` + // Default is the value a parameter takes if no input value via a Param is supplied. + // +optional + Default *string `json:"default,omitempty"` +} + +// Param defines a string value to be used for a ParamSpec with the same name. +type Param struct { + Name string `json:"name"` + Value string `json:"value"` +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/register.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/register.go new file mode 100644 index 0000000000..d44525d0d8 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/register.go @@ -0,0 +1,62 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "github.com/tektoncd/triggers/pkg/apis/triggers" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: triggers.GroupName, Version: "v1beta1"} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + + // AddToScheme adds Build types to the scheme. + AddToScheme = schemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &ClusterTriggerBinding{}, + &ClusterTriggerBindingList{}, + &EventListener{}, + &EventListenerList{}, + &TriggerBinding{}, + &TriggerBindingList{}, + &TriggerTemplate{}, + &TriggerTemplateList{}, + &Trigger{}, + &TriggerList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/swagger.json b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/swagger.json new file mode 100644 index 0000000000..209fae22df --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/swagger.json @@ -0,0 +1,2749 @@ +{ + "swagger": "2.0", + "info": { + "description": "Tekton Pipeline", + "title": "Tekton", + "version": "v0.17.2" + }, + "paths": {}, + "definitions": { + "pod.Template": { + "description": "PodTemplate holds pod specific configuration", + "type": "object", + "properties": { + "affinity": { + "description": "If specified, the pod's scheduling constraints", + "$ref": "#/definitions/v1.Affinity" + }, + "automountServiceAccountToken": { + "description": "AutomountServiceAccountToken indicates whether pods running as this service account should have an API token automatically mounted.", + "type": "boolean" + }, + "dnsConfig": { + "description": "Specifies the DNS parameters of a pod. Parameters specified here will be merged to the generated DNS configuration based on DNSPolicy.", + "$ref": "#/definitions/v1.PodDNSConfig" + }, + "dnsPolicy": { + "description": "Set DNS policy for the pod. Defaults to \"ClusterFirst\". Valid values are 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy.", + "type": "string" + }, + "enableServiceLinks": { + "description": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.", + "type": "boolean" + }, + "hostAliases": { + "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.HostAlias" + } + }, + "hostNetwork": { + "description": "HostNetwork specifies whether the pod may use the node network namespace", + "type": "boolean" + }, + "imagePullSecrets": { + "description": "ImagePullSecrets gives the name of the secret used by the pod to pull the image if specified", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.LocalObjectReference" + } + }, + "nodeSelector": { + "description": "NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/", + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "priorityClassName": { + "description": "If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default.", + "type": "string" + }, + "runtimeClassName": { + "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md This is a beta feature as of Kubernetes v1.14.", + "type": "string" + }, + "schedulerName": { + "description": "SchedulerName specifies the scheduler to be used to dispatch the Pod", + "type": "string" + }, + "securityContext": { + "description": "SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field.", + "$ref": "#/definitions/v1.PodSecurityContext" + }, + "tolerations": { + "description": "If specified, the pod's tolerations.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.Toleration" + } + }, + "volumes": { + "description": "List of volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.Volume" + }, + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge,retainKeys" + } + } + }, + "v1alpha1.PipelineResource": { + "description": "PipelineResource describes a resource that is an input to or output from a Task.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ObjectMeta" + }, + "spec": { + "description": "Spec holds the desired state of the PipelineResource from the client", + "default": {}, + "$ref": "#/definitions/v1alpha1.PipelineResourceSpec" + }, + "status": { + "description": "Status is deprecated. It usually is used to communicate the observed state of the PipelineResource from the controller, but was unused as there is no controller for PipelineResource.", + "$ref": "#/definitions/v1alpha1.PipelineResourceStatus" + } + } + }, + "v1alpha1.PipelineResourceList": { + "description": "PipelineResourceList contains a list of PipelineResources", + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1alpha1.PipelineResource" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ListMeta" + } + } + }, + "v1alpha1.PipelineResourceSpec": { + "description": "PipelineResourceSpec defines an individual resources used in the pipeline.", + "type": "object", + "required": [ + "type", + "params" + ], + "properties": { + "description": { + "description": "Description is a user-facing description of the resource that may be used to populate a UI.", + "type": "string" + }, + "params": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1alpha1.ResourceParam" + } + }, + "secrets": { + "description": "Secrets to fetch to populate some of resource fields", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1alpha1.SecretParam" + } + }, + "type": { + "type": "string", + "default": "" + } + } + }, + "v1alpha1.PipelineResourceStatus": { + "description": "PipelineResourceStatus does not contain anything because PipelineResources on their own do not have a status Deprecated", + "type": "object" + }, + "v1alpha1.ResourceDeclaration": { + "description": "ResourceDeclaration defines an input or output PipelineResource declared as a requirement by another type such as a Task or Condition. The Name field will be used to refer to these PipelineResources within the type's definition, and when provided as an Input, the Name will be the path to the volume mounted containing this PipelineResource as an input (e.g. an input Resource named `workspace` will be mounted at `/workspace`).", + "type": "object", + "required": [ + "name", + "type" + ], + "properties": { + "description": { + "description": "Description is a user-facing description of the declared resource that may be used to populate a UI.", + "type": "string" + }, + "name": { + "description": "Name declares the name by which a resource is referenced in the definition. Resources may be referenced by name in the definition of a Task's steps.", + "type": "string", + "default": "" + }, + "optional": { + "description": "Optional declares the resource as optional. By default optional is set to false which makes a resource required. optional: true - the resource is considered optional optional: false - the resource is considered required (equivalent of not specifying it)", + "type": "boolean" + }, + "targetPath": { + "description": "TargetPath is the path in workspace directory where the resource will be copied.", + "type": "string" + }, + "type": { + "description": "Type is the type of this resource;", + "type": "string", + "default": "" + } + } + }, + "v1alpha1.ResourceParam": { + "description": "ResourceParam declares a string value to use for the parameter called Name, and is used in the specific context of PipelineResources.", + "type": "object", + "required": [ + "name", + "value" + ], + "properties": { + "name": { + "type": "string", + "default": "" + }, + "value": { + "type": "string", + "default": "" + } + } + }, + "v1alpha1.SecretParam": { + "description": "SecretParam indicates which secret can be used to populate a field of the resource", + "type": "object", + "required": [ + "fieldName", + "secretKey", + "secretName" + ], + "properties": { + "fieldName": { + "type": "string", + "default": "" + }, + "secretKey": { + "type": "string", + "default": "" + }, + "secretName": { + "type": "string", + "default": "" + } + } + }, + "v1beta1.ArrayOrString": { + "description": "ArrayOrString is a type that can hold a single string or string array. Used in JSON unmarshalling so that a single JSON field can accept either an individual string or an array of strings.", + "type": "object", + "required": [ + "type", + "stringVal", + "arrayVal" + ], + "properties": { + "arrayVal": { + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "stringVal": { + "description": "Represents the stored type of ArrayOrString.", + "type": "string", + "default": "" + }, + "type": { + "type": "string", + "default": "" + } + } + }, + "v1beta1.CloudEventDelivery": { + "description": "CloudEventDelivery is the target of a cloud event along with the state of delivery.", + "type": "object", + "properties": { + "status": { + "default": {}, + "$ref": "#/definitions/v1beta1.CloudEventDeliveryState" + }, + "target": { + "description": "Target points to an addressable", + "type": "string" + } + } + }, + "v1beta1.CloudEventDeliveryState": { + "description": "CloudEventDeliveryState reports the state of a cloud event to be sent.", + "type": "object", + "required": [ + "message", + "retryCount" + ], + "properties": { + "condition": { + "description": "Current status", + "type": "string" + }, + "message": { + "description": "Error is the text of error (if any)", + "type": "string", + "default": "" + }, + "retryCount": { + "description": "RetryCount is the number of attempts of sending the cloud event", + "type": "integer", + "format": "int32", + "default": 0 + }, + "sentAt": { + "description": "SentAt is the time at which the last attempt to send the event was made", + "$ref": "#/definitions/v1.Time" + } + } + }, + "v1beta1.ClusterTask": { + "description": "ClusterTask is a Task with a cluster scope. ClusterTasks are used to represent Tasks that should be publicly addressable from any namespace in the cluster.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ObjectMeta" + }, + "spec": { + "description": "Spec holds the desired state of the Task from the client", + "default": {}, + "$ref": "#/definitions/v1beta1.TaskSpec" + } + } + }, + "v1beta1.ClusterTaskList": { + "description": "ClusterTaskList contains a list of ClusterTask", + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.ClusterTask" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ListMeta" + } + } + }, + "v1beta1.ConditionCheck": { + "description": "ConditionCheck represents a single evaluation of a Condition step.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ObjectMeta" + }, + "spec": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunSpec" + }, + "status": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunStatus" + } + } + }, + "v1beta1.ConditionCheckStatus": { + "description": "ConditionCheckStatus defines the observed state of ConditionCheck", + "type": "object", + "required": [ + "podName" + ], + "properties": { + "annotations": { + "description": "Annotations is additional Status fields for the Resource to save some additional State as well as convey more information to the user. This is roughly akin to Annotations on any k8s resource, just the reconciler conveying richer information outwards.", + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "check": { + "description": "Check describes the state of the check container.", + "default": {}, + "$ref": "#/definitions/v1.ContainerState" + }, + "completionTime": { + "description": "CompletionTime is the time the check pod completed.", + "$ref": "#/definitions/v1.Time" + }, + "conditions": { + "description": "Conditions the latest available observations of a resource's current state.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/knative.Condition" + }, + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "observedGeneration": { + "description": "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", + "type": "integer", + "format": "int64" + }, + "podName": { + "description": "PodName is the name of the pod responsible for executing this condition check.", + "type": "string", + "default": "" + }, + "startTime": { + "description": "StartTime is the time the check is actually started.", + "$ref": "#/definitions/v1.Time" + } + } + }, + "v1beta1.ConditionCheckStatusFields": { + "description": "ConditionCheckStatusFields holds the fields of ConfigurationCheck's status. This is defined separately and inlined so that other types can readily consume these fields via duck typing.", + "type": "object", + "required": [ + "podName" + ], + "properties": { + "check": { + "description": "Check describes the state of the check container.", + "default": {}, + "$ref": "#/definitions/v1.ContainerState" + }, + "completionTime": { + "description": "CompletionTime is the time the check pod completed.", + "$ref": "#/definitions/v1.Time" + }, + "podName": { + "description": "PodName is the name of the pod responsible for executing this condition check.", + "type": "string", + "default": "" + }, + "startTime": { + "description": "StartTime is the time the check is actually started.", + "$ref": "#/definitions/v1.Time" + } + } + }, + "v1beta1.EmbeddedTask": { + "description": "EmbeddedTask is used to define a Task inline within a Pipeline's PipelineTasks.", + "type": "object", + "properties": { + "apiVersion": { + "type": "string" + }, + "description": { + "description": "Description is a user-facing description of the task that may be used to populate a UI.", + "type": "string" + }, + "kind": { + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineTaskMetadata" + }, + "params": { + "description": "Params is a list of input parameters required to run the task. Params must be supplied as inputs in TaskRuns unless they declare a default value.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.ParamSpec" + } + }, + "resources": { + "description": "Resources is a list input and output resource to run the task Resources are represented in TaskRuns as bindings to instances of PipelineResources.", + "$ref": "#/definitions/v1beta1.TaskResources" + }, + "results": { + "description": "Results are values that this Task can output", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskResult" + } + }, + "sidecars": { + "description": "Sidecars are run alongside the Task's step containers. They begin before the steps start and end after the steps complete.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Sidecar" + } + }, + "spec": { + "description": "Spec is a specification of a custom task", + "default": {}, + "$ref": "#/definitions/k8s.io.apimachinery.pkg.runtime.RawExtension" + }, + "stepTemplate": { + "description": "StepTemplate can be used as the basis for all step containers within the Task, so that the steps inherit settings on the base container.", + "$ref": "#/definitions/v1.Container" + }, + "steps": { + "description": "Steps are the steps of the build; each step is run sequentially with the source mounted into /workspace.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Step" + } + }, + "volumes": { + "description": "Volumes is a collection of volumes that are available to mount into the steps of the build.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.Volume" + } + }, + "workspaces": { + "description": "Workspaces are the volumes that this Task requires.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WorkspaceDeclaration" + } + } + } + }, + "v1beta1.InternalTaskModifier": { + "description": "InternalTaskModifier implements TaskModifier for resources that are built-in to Tekton Pipelines.", + "type": "object", + "required": [ + "StepsToPrepend", + "StepsToAppend", + "Volumes" + ], + "properties": { + "StepsToAppend": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Step" + } + }, + "StepsToPrepend": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Step" + } + }, + "Volumes": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.Volume" + } + } + } + }, + "v1beta1.Param": { + "description": "Param declares an ArrayOrString to use for the parameter called name.", + "type": "object", + "required": [ + "name", + "value" + ], + "properties": { + "name": { + "type": "string", + "default": "" + }, + "value": { + "default": {}, + "$ref": "#/definitions/v1beta1.ArrayOrString" + } + } + }, + "v1beta1.ParamSpec": { + "description": "ParamSpec defines arbitrary parameters needed beyond typed inputs (such as resources). Parameter values are provided by users as inputs on a TaskRun or PipelineRun.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "default": { + "description": "Default is the value a parameter takes if no input value is supplied. If default is set, a Task may be executed without a supplied value for the parameter.", + "$ref": "#/definitions/v1beta1.ArrayOrString" + }, + "description": { + "description": "Description is a user-facing description of the parameter that may be used to populate a UI.", + "type": "string" + }, + "name": { + "description": "Name declares the name by which a parameter is referenced.", + "type": "string", + "default": "" + }, + "type": { + "description": "Type is the user-specified type of the parameter. The possible types are currently \"string\" and \"array\", and \"string\" is the default.", + "type": "string" + } + } + }, + "v1beta1.Pipeline": { + "description": "Pipeline describes a list of Tasks to execute. It expresses how outputs of tasks feed into inputs of subsequent tasks.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ObjectMeta" + }, + "spec": { + "description": "Spec holds the desired state of the Pipeline from the client", + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineSpec" + } + } + }, + "v1beta1.PipelineDeclaredResource": { + "description": "PipelineDeclaredResource is used by a Pipeline to declare the types of the PipelineResources that it will required to run and names which can be used to refer to these PipelineResources in PipelineTaskResourceBindings.", + "type": "object", + "required": [ + "name", + "type" + ], + "properties": { + "name": { + "description": "Name is the name that will be used by the Pipeline to refer to this resource. It does not directly correspond to the name of any PipelineResources Task inputs or outputs, and it does not correspond to the actual names of the PipelineResources that will be bound in the PipelineRun.", + "type": "string", + "default": "" + }, + "optional": { + "description": "Optional declares the resource as optional. optional: true - the resource is considered optional optional: false - the resource is considered required (default/equivalent of not specifying it)", + "type": "boolean" + }, + "type": { + "description": "Type is the type of the PipelineResource.", + "type": "string", + "default": "" + } + } + }, + "v1beta1.PipelineList": { + "description": "PipelineList contains a list of Pipeline", + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Pipeline" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ListMeta" + } + } + }, + "v1beta1.PipelineRef": { + "description": "PipelineRef can be used to refer to a specific instance of a Pipeline. Copied from CrossVersionObjectReference: https://github.com/kubernetes/kubernetes/blob/169df7434155cbbc22f1532cba8e0a9588e29ad8/pkg/apis/autoscaling/types.go#L64", + "type": "object", + "properties": { + "apiVersion": { + "description": "API version of the referent", + "type": "string" + }, + "bundle": { + "description": "Bundle url reference to a Tekton Bundle.", + "type": "string" + }, + "name": { + "description": "Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names", + "type": "string" + } + } + }, + "v1beta1.PipelineResourceBinding": { + "description": "PipelineResourceBinding connects a reference to an instance of a PipelineResource with a PipelineResource dependency that the Pipeline has declared", + "type": "object", + "properties": { + "name": { + "description": "Name is the name of the PipelineResource in the Pipeline's declaration", + "type": "string" + }, + "resourceRef": { + "description": "ResourceRef is a reference to the instance of the actual PipelineResource that should be used", + "$ref": "#/definitions/v1beta1.PipelineResourceRef" + }, + "resourceSpec": { + "description": "ResourceSpec is specification of a resource that should be created and consumed by the task", + "$ref": "#/definitions/v1alpha1.PipelineResourceSpec" + } + } + }, + "v1beta1.PipelineResourceRef": { + "description": "PipelineResourceRef can be used to refer to a specific instance of a Resource", + "type": "object", + "properties": { + "apiVersion": { + "description": "API version of the referent", + "type": "string" + }, + "name": { + "description": "Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names", + "type": "string" + } + } + }, + "v1beta1.PipelineResourceResult": { + "description": "PipelineResourceResult used to export the image name and digest as json", + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string", + "default": "" + }, + "resourceName": { + "type": "string" + }, + "resourceRef": { + "description": "The field ResourceRef should be deprecated and removed in the next API version. See https://github.com/tektoncd/pipeline/issues/2694 for more information.", + "$ref": "#/definitions/v1beta1.PipelineResourceRef" + }, + "type": { + "type": "integer", + "format": "int32" + }, + "value": { + "type": "string", + "default": "" + } + } + }, + "v1beta1.PipelineResult": { + "description": "PipelineResult used to describe the results of a pipeline", + "type": "object", + "required": [ + "name", + "value" + ], + "properties": { + "description": { + "description": "Description is a human-readable description of the result", + "type": "string", + "default": "" + }, + "name": { + "description": "Name the given name", + "type": "string", + "default": "" + }, + "value": { + "description": "Value the expression used to retrieve the value", + "type": "string", + "default": "" + } + } + }, + "v1beta1.PipelineRun": { + "description": "PipelineRun represents a single execution of a Pipeline. PipelineRuns are how the graph of Tasks declared in a Pipeline are executed; they specify inputs to Pipelines such as parameter values and capture operational aspects of the Tasks execution such as service account and tolerations. Creating a PipelineRun creates TaskRuns for Tasks in the referenced Pipeline.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ObjectMeta" + }, + "spec": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineRunSpec" + }, + "status": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineRunStatus" + } + } + }, + "v1beta1.PipelineRunConditionCheckStatus": { + "description": "PipelineRunConditionCheckStatus returns the condition check status", + "type": "object", + "properties": { + "conditionName": { + "description": "ConditionName is the name of the Condition", + "type": "string" + }, + "status": { + "description": "Status is the ConditionCheckStatus for the corresponding ConditionCheck", + "$ref": "#/definitions/v1beta1.ConditionCheckStatus" + } + } + }, + "v1beta1.PipelineRunList": { + "description": "PipelineRunList contains a list of PipelineRun", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineRun" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ListMeta" + } + } + }, + "v1beta1.PipelineRunResult": { + "description": "PipelineRunResult used to describe the results of a pipeline", + "type": "object", + "required": [ + "name", + "value" + ], + "properties": { + "name": { + "description": "Name is the result's name as declared by the Pipeline", + "type": "string", + "default": "" + }, + "value": { + "description": "Value is the result returned from the execution of this PipelineRun", + "type": "string", + "default": "" + } + } + }, + "v1beta1.PipelineRunRunStatus": { + "description": "PipelineRunRunStatus contains the name of the PipelineTask for this Run and the Run's Status", + "type": "object", + "properties": { + "pipelineTaskName": { + "description": "PipelineTaskName is the name of the PipelineTask.", + "type": "string" + }, + "status": { + "description": "Status is the RunStatus for the corresponding Run", + "$ref": "#/definitions/github.com.tektoncd.pipeline.pkg.apis.run.v1alpha1.RunStatus" + }, + "whenExpressions": { + "description": "WhenExpressions is the list of checks guarding the execution of the PipelineTask", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WhenExpression" + } + } + } + }, + "v1beta1.PipelineRunSpec": { + "description": "PipelineRunSpec defines the desired state of PipelineRun", + "type": "object", + "properties": { + "params": { + "description": "Params is a list of parameter names and values.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Param" + } + }, + "pipelineRef": { + "$ref": "#/definitions/v1beta1.PipelineRef" + }, + "pipelineSpec": { + "$ref": "#/definitions/v1beta1.PipelineSpec" + }, + "podTemplate": { + "description": "PodTemplate holds pod specific configuration", + "$ref": "#/definitions/pod.Template" + }, + "resources": { + "description": "Resources is a list of bindings specifying which actual instances of PipelineResources to use for the resources the Pipeline has declared it needs.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineResourceBinding" + } + }, + "serviceAccountName": { + "type": "string" + }, + "serviceAccountNames": { + "description": "Deprecated: use taskRunSpecs.ServiceAccountName instead", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineRunSpecServiceAccountName" + } + }, + "status": { + "description": "Used for cancelling a pipelinerun (and maybe more later on)", + "type": "string" + }, + "taskRunSpecs": { + "description": "TaskRunSpecs holds a set of runtime specs", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineTaskRunSpec" + } + }, + "timeout": { + "description": "Time after which the Pipeline times out. Defaults to never. Refer to Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration", + "$ref": "#/definitions/v1.Duration" + }, + "timeouts": { + "description": "This is an alpha field. You must set the \"enable-api-fields\" feature flag to \"alpha\" for this field to be supported.\n\nTime after which the Pipeline times out. Currently three keys are accepted in the map pipeline, tasks and finally with Timeouts.pipeline \u003e= Timeouts.tasks + Timeouts.finally", + "$ref": "#/definitions/v1beta1.TimeoutFields" + }, + "workspaces": { + "description": "Workspaces holds a set of workspace bindings that must match names with those declared in the pipeline.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WorkspaceBinding" + } + } + } + }, + "v1beta1.PipelineRunSpecServiceAccountName": { + "description": "PipelineRunSpecServiceAccountName can be used to configure specific ServiceAccountName for a concrete Task", + "type": "object", + "properties": { + "serviceAccountName": { + "type": "string" + }, + "taskName": { + "type": "string" + } + } + }, + "v1beta1.PipelineRunStatus": { + "description": "PipelineRunStatus defines the observed state of PipelineRun", + "type": "object", + "properties": { + "annotations": { + "description": "Annotations is additional Status fields for the Resource to save some additional State as well as convey more information to the user. This is roughly akin to Annotations on any k8s resource, just the reconciler conveying richer information outwards.", + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "completionTime": { + "description": "CompletionTime is the time the PipelineRun completed.", + "$ref": "#/definitions/v1.Time" + }, + "conditions": { + "description": "Conditions the latest available observations of a resource's current state.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/knative.Condition" + }, + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "observedGeneration": { + "description": "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", + "type": "integer", + "format": "int64" + }, + "pipelineResults": { + "description": "PipelineResults are the list of results written out by the pipeline task's containers", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineRunResult" + } + }, + "pipelineSpec": { + "description": "PipelineRunSpec contains the exact spec used to instantiate the run", + "$ref": "#/definitions/v1beta1.PipelineSpec" + }, + "runs": { + "description": "map of PipelineRunRunStatus with the run name as the key", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v1beta1.PipelineRunRunStatus" + } + }, + "skippedTasks": { + "description": "list of tasks that were skipped due to when expressions evaluating to false", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.SkippedTask" + } + }, + "startTime": { + "description": "StartTime is the time the PipelineRun is actually started.", + "$ref": "#/definitions/v1.Time" + }, + "taskRuns": { + "description": "map of PipelineRunTaskRunStatus with the taskRun name as the key", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v1beta1.PipelineRunTaskRunStatus" + } + } + } + }, + "v1beta1.PipelineRunStatusFields": { + "description": "PipelineRunStatusFields holds the fields of PipelineRunStatus' status. This is defined separately and inlined so that other types can readily consume these fields via duck typing.", + "type": "object", + "properties": { + "completionTime": { + "description": "CompletionTime is the time the PipelineRun completed.", + "$ref": "#/definitions/v1.Time" + }, + "pipelineResults": { + "description": "PipelineResults are the list of results written out by the pipeline task's containers", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineRunResult" + } + }, + "pipelineSpec": { + "description": "PipelineRunSpec contains the exact spec used to instantiate the run", + "$ref": "#/definitions/v1beta1.PipelineSpec" + }, + "runs": { + "description": "map of PipelineRunRunStatus with the run name as the key", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v1beta1.PipelineRunRunStatus" + } + }, + "skippedTasks": { + "description": "list of tasks that were skipped due to when expressions evaluating to false", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.SkippedTask" + } + }, + "startTime": { + "description": "StartTime is the time the PipelineRun is actually started.", + "$ref": "#/definitions/v1.Time" + }, + "taskRuns": { + "description": "map of PipelineRunTaskRunStatus with the taskRun name as the key", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v1beta1.PipelineRunTaskRunStatus" + } + } + } + }, + "v1beta1.PipelineRunTaskRunStatus": { + "description": "PipelineRunTaskRunStatus contains the name of the PipelineTask for this TaskRun and the TaskRun's Status", + "type": "object", + "properties": { + "conditionChecks": { + "description": "ConditionChecks maps the name of a condition check to its Status", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v1beta1.PipelineRunConditionCheckStatus" + } + }, + "pipelineTaskName": { + "description": "PipelineTaskName is the name of the PipelineTask.", + "type": "string" + }, + "status": { + "description": "Status is the TaskRunStatus for the corresponding TaskRun", + "$ref": "#/definitions/v1beta1.TaskRunStatus" + }, + "whenExpressions": { + "description": "WhenExpressions is the list of checks guarding the execution of the PipelineTask", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WhenExpression" + } + } + } + }, + "v1beta1.PipelineSpec": { + "description": "PipelineSpec defines the desired state of Pipeline.", + "type": "object", + "properties": { + "description": { + "description": "Description is a user-facing description of the pipeline that may be used to populate a UI.", + "type": "string" + }, + "finally": { + "description": "Finally declares the list of Tasks that execute just before leaving the Pipeline i.e. either after all Tasks are finished executing successfully or after a failure which would result in ending the Pipeline", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineTask" + } + }, + "params": { + "description": "Params declares a list of input parameters that must be supplied when this Pipeline is run.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.ParamSpec" + } + }, + "resources": { + "description": "Resources declares the names and types of the resources given to the Pipeline's tasks as inputs and outputs.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineDeclaredResource" + } + }, + "results": { + "description": "Results are values that this pipeline can output once run", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineResult" + } + }, + "tasks": { + "description": "Tasks declares the graph of Tasks that execute when this Pipeline is run.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineTask" + } + }, + "workspaces": { + "description": "Workspaces declares a set of named workspaces that are expected to be provided by a PipelineRun.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineWorkspaceDeclaration" + } + } + } + }, + "v1beta1.PipelineTask": { + "description": "PipelineTask defines a task in a Pipeline, passing inputs from both Params and from the output of previous tasks.", + "type": "object", + "properties": { + "conditions": { + "description": "Conditions is a list of conditions that need to be true for the task to run Conditions are deprecated, use WhenExpressions instead", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineTaskCondition" + } + }, + "name": { + "description": "Name is the name of this task within the context of a Pipeline. Name is used as a coordinate with the `from` and `runAfter` fields to establish the execution order of tasks relative to one another.", + "type": "string" + }, + "params": { + "description": "Parameters declares parameters passed to this task.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Param" + } + }, + "resources": { + "description": "Resources declares the resources given to this task as inputs and outputs.", + "$ref": "#/definitions/v1beta1.PipelineTaskResources" + }, + "retries": { + "description": "Retries represents how many times this task should be retried in case of task failure: ConditionSucceeded set to False", + "type": "integer", + "format": "int32" + }, + "runAfter": { + "description": "RunAfter is the list of PipelineTask names that should be executed before this Task executes. (Used to force a specific ordering in graph execution.)", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "taskRef": { + "description": "TaskRef is a reference to a task definition.", + "$ref": "#/definitions/v1beta1.TaskRef" + }, + "taskSpec": { + "description": "TaskSpec is a specification of a task", + "$ref": "#/definitions/v1beta1.EmbeddedTask" + }, + "timeout": { + "description": "Time after which the TaskRun times out. Defaults to 1 hour. Specified TaskRun timeout should be less than 24h. Refer Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration", + "$ref": "#/definitions/v1.Duration" + }, + "when": { + "description": "WhenExpressions is a list of when expressions that need to be true for the task to run", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WhenExpression" + } + }, + "workspaces": { + "description": "Workspaces maps workspaces from the pipeline spec to the workspaces declared in the Task.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WorkspacePipelineTaskBinding" + } + } + } + }, + "v1beta1.PipelineTaskCondition": { + "description": "PipelineTaskCondition allows a PipelineTask to declare a Condition to be evaluated before the Task is run.", + "type": "object", + "required": [ + "conditionRef" + ], + "properties": { + "conditionRef": { + "description": "ConditionRef is the name of the Condition to use for the conditionCheck", + "type": "string", + "default": "" + }, + "params": { + "description": "Params declare parameters passed to this Condition", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Param" + } + }, + "resources": { + "description": "Resources declare the resources provided to this Condition as input", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineTaskInputResource" + } + } + } + }, + "v1beta1.PipelineTaskInputResource": { + "description": "PipelineTaskInputResource maps the name of a declared PipelineResource input dependency in a Task to the resource in the Pipeline's DeclaredPipelineResources that should be used. This input may come from a previous task.", + "type": "object", + "required": [ + "name", + "resource" + ], + "properties": { + "from": { + "description": "From is the list of PipelineTask names that the resource has to come from. (Implies an ordering in the execution graph.)", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "name": { + "description": "Name is the name of the PipelineResource as declared by the Task.", + "type": "string", + "default": "" + }, + "resource": { + "description": "Resource is the name of the DeclaredPipelineResource to use.", + "type": "string", + "default": "" + } + } + }, + "v1beta1.PipelineTaskMetadata": { + "description": "PipelineTaskMetadata contains the labels or annotations for an EmbeddedTask", + "type": "object", + "properties": { + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + } + } + }, + "v1beta1.PipelineTaskOutputResource": { + "description": "PipelineTaskOutputResource maps the name of a declared PipelineResource output dependency in a Task to the resource in the Pipeline's DeclaredPipelineResources that should be used.", + "type": "object", + "required": [ + "name", + "resource" + ], + "properties": { + "name": { + "description": "Name is the name of the PipelineResource as declared by the Task.", + "type": "string", + "default": "" + }, + "resource": { + "description": "Resource is the name of the DeclaredPipelineResource to use.", + "type": "string", + "default": "" + } + } + }, + "v1beta1.PipelineTaskParam": { + "description": "PipelineTaskParam is used to provide arbitrary string parameters to a Task.", + "type": "object", + "required": [ + "name", + "value" + ], + "properties": { + "name": { + "type": "string", + "default": "" + }, + "value": { + "type": "string", + "default": "" + } + } + }, + "v1beta1.PipelineTaskResources": { + "description": "PipelineTaskResources allows a Pipeline to declare how its DeclaredPipelineResources should be provided to a Task as its inputs and outputs.", + "type": "object", + "properties": { + "inputs": { + "description": "Inputs holds the mapping from the PipelineResources declared in DeclaredPipelineResources to the input PipelineResources required by the Task.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineTaskInputResource" + } + }, + "outputs": { + "description": "Outputs holds the mapping from the PipelineResources declared in DeclaredPipelineResources to the input PipelineResources required by the Task.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineTaskOutputResource" + } + } + } + }, + "v1beta1.PipelineTaskRun": { + "description": "PipelineTaskRun reports the results of running a step in the Task. Each task has the potential to succeed or fail (based on the exit code) and produces logs.", + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "v1beta1.PipelineTaskRunSpec": { + "description": "PipelineTaskRunSpec can be used to configure specific specs for a concrete Task", + "type": "object", + "properties": { + "pipelineTaskName": { + "type": "string" + }, + "sidecarOverrides": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunSidecarOverride" + } + }, + "stepOverrides": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunStepOverride" + } + }, + "taskPodTemplate": { + "$ref": "#/definitions/pod.Template" + }, + "taskServiceAccountName": { + "type": "string" + } + } + }, + "v1beta1.PipelineWorkspaceDeclaration": { + "description": "WorkspacePipelineDeclaration creates a named slot in a Pipeline that a PipelineRun is expected to populate with a workspace binding. Deprecated: use PipelineWorkspaceDeclaration type instead", + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "Description is a human readable string describing how the workspace will be used in the Pipeline. It can be useful to include a bit of detail about which tasks are intended to have access to the data on the workspace.", + "type": "string" + }, + "name": { + "description": "Name is the name of a workspace to be provided by a PipelineRun.", + "type": "string", + "default": "" + }, + "optional": { + "description": "Optional marks a Workspace as not being required in PipelineRuns. By default this field is false and so declared workspaces are required.", + "type": "boolean" + } + } + }, + "v1beta1.ResolverParam": { + "description": "ResolverParam is a single parameter passed to a resolver.", + "type": "object", + "required": [ + "Name", + "Value" + ], + "properties": { + "Name": { + "description": "Name is the name of the parameter that will be passed to the resolver.", + "type": "string", + "default": "" + }, + "Value": { + "description": "Value is the string value of the parameter that will be passed to the resolver.", + "type": "string", + "default": "" + } + } + }, + "v1beta1.ResolverRef": { + "description": "ResolverRef can be used to refer to a Pipeline or Task in a remote location like a git repo. This feature is in alpha and these fields are only available when the alpha feature gate is enabled.", + "type": "object", + "properties": { + "resolver": { + "description": "Resolver is the name of the resolver that should perform resolution of the referenced Tekton resource, such as \"git\".", + "type": "string" + }, + "resource": { + "description": "Resource contains the parameters used to identify the referenced Tekton resource. Example entries might include \"repo\" or \"path\" but the set of params ultimately depends on the chosen resolver.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.ResolverParam" + } + } + } + }, + "v1beta1.ResultRef": { + "description": "ResultRef is a type that represents a reference to a task run result", + "type": "object", + "required": [ + "PipelineTask", + "Result" + ], + "properties": { + "PipelineTask": { + "type": "string", + "default": "" + }, + "Result": { + "type": "string", + "default": "" + } + } + }, + "v1beta1.Sidecar": { + "description": "Sidecar has nearly the same data structure as Step, consisting of a Container and an optional Script, but does not have the ability to timeout.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "args": { + "description": "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "command": { + "description": "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "env": { + "description": "List of environment variables to set in the container. Cannot be updated.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.EnvVar" + }, + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "envFrom": { + "description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.EnvFromSource" + } + }, + "image": { + "description": "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.", + "type": "string" + }, + "imagePullPolicy": { + "description": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", + "type": "string" + }, + "lifecycle": { + "description": "Actions that the management system should take in response to container lifecycle events. Cannot be updated.", + "$ref": "#/definitions/v1.Lifecycle" + }, + "livenessProbe": { + "description": "Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "$ref": "#/definitions/v1.Probe" + }, + "name": { + "description": "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.", + "type": "string", + "default": "" + }, + "ports": { + "description": "List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Cannot be updated.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.ContainerPort" + }, + "x-kubernetes-list-map-keys": [ + "containerPort", + "protocol" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "containerPort", + "x-kubernetes-patch-strategy": "merge" + }, + "readinessProbe": { + "description": "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "$ref": "#/definitions/v1.Probe" + }, + "resources": { + "description": "Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/", + "default": {}, + "$ref": "#/definitions/v1.ResourceRequirements" + }, + "script": { + "description": "Script is the contents of an executable file to execute.\n\nIf Script is not empty, the Step cannot have an Command or Args.", + "type": "string" + }, + "securityContext": { + "description": "SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/", + "$ref": "#/definitions/v1.SecurityContext" + }, + "startupProbe": { + "description": "StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "$ref": "#/definitions/v1.Probe" + }, + "stdin": { + "description": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", + "type": "boolean" + }, + "stdinOnce": { + "description": "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", + "type": "boolean" + }, + "terminationMessagePath": { + "description": "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", + "type": "string" + }, + "terminationMessagePolicy": { + "description": "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", + "type": "string" + }, + "tty": { + "description": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", + "type": "boolean" + }, + "volumeDevices": { + "description": "volumeDevices is the list of block devices to be used by the container.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.VolumeDevice" + }, + "x-kubernetes-patch-merge-key": "devicePath", + "x-kubernetes-patch-strategy": "merge" + }, + "volumeMounts": { + "description": "Pod volumes to mount into the container's filesystem. Cannot be updated.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.VolumeMount" + }, + "x-kubernetes-patch-merge-key": "mountPath", + "x-kubernetes-patch-strategy": "merge" + }, + "workingDir": { + "description": "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", + "type": "string" + }, + "workspaces": { + "description": "This is an alpha field. You must set the \"enable-api-fields\" feature flag to \"alpha\" for this field to be supported.\n\nWorkspaces is a list of workspaces from the Task that this Sidecar wants exclusive access to. Adding a workspace to this list means that any other Step or Sidecar that does not also request this Workspace will not have access to it.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WorkspaceUsage" + } + } + } + }, + "v1beta1.SidecarState": { + "description": "SidecarState reports the results of running a sidecar in a Task.", + "type": "object", + "properties": { + "container": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "name": { + "type": "string" + }, + "running": { + "description": "Details about a running container", + "$ref": "#/definitions/v1.ContainerStateRunning" + }, + "terminated": { + "description": "Details about a terminated container", + "$ref": "#/definitions/v1.ContainerStateTerminated" + }, + "waiting": { + "description": "Details about a waiting container", + "$ref": "#/definitions/v1.ContainerStateWaiting" + } + } + }, + "v1beta1.SkippedTask": { + "description": "SkippedTask is used to describe the Tasks that were skipped due to their When Expressions evaluating to False. This is a struct because we are looking into including more details about the When Expressions that caused this Task to be skipped.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "description": "Name is the Pipeline Task name", + "type": "string", + "default": "" + }, + "whenExpressions": { + "description": "WhenExpressions is the list of checks guarding the execution of the PipelineTask", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WhenExpression" + } + } + } + }, + "v1beta1.Step": { + "description": "Step embeds the Container type, which allows it to include fields not provided by Container.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "args": { + "description": "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "command": { + "description": "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "env": { + "description": "List of environment variables to set in the container. Cannot be updated.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.EnvVar" + }, + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "envFrom": { + "description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.EnvFromSource" + } + }, + "image": { + "description": "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.", + "type": "string" + }, + "imagePullPolicy": { + "description": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", + "type": "string" + }, + "lifecycle": { + "description": "Actions that the management system should take in response to container lifecycle events. Cannot be updated.", + "$ref": "#/definitions/v1.Lifecycle" + }, + "livenessProbe": { + "description": "Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "$ref": "#/definitions/v1.Probe" + }, + "name": { + "description": "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.", + "type": "string", + "default": "" + }, + "onError": { + "description": "OnError defines the exiting behavior of a container on error can be set to [ continue | stopAndFail ] stopAndFail indicates exit the taskRun if the container exits with non-zero exit code continue indicates continue executing the rest of the steps irrespective of the container exit code", + "type": "string" + }, + "ports": { + "description": "List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Cannot be updated.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.ContainerPort" + }, + "x-kubernetes-list-map-keys": [ + "containerPort", + "protocol" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "containerPort", + "x-kubernetes-patch-strategy": "merge" + }, + "readinessProbe": { + "description": "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "$ref": "#/definitions/v1.Probe" + }, + "resources": { + "description": "Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/", + "default": {}, + "$ref": "#/definitions/v1.ResourceRequirements" + }, + "script": { + "description": "Script is the contents of an executable file to execute.\n\nIf Script is not empty, the Step cannot have an Command and the Args will be passed to the Script.", + "type": "string" + }, + "securityContext": { + "description": "SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/", + "$ref": "#/definitions/v1.SecurityContext" + }, + "startupProbe": { + "description": "StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "$ref": "#/definitions/v1.Probe" + }, + "stdin": { + "description": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", + "type": "boolean" + }, + "stdinOnce": { + "description": "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", + "type": "boolean" + }, + "terminationMessagePath": { + "description": "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", + "type": "string" + }, + "terminationMessagePolicy": { + "description": "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", + "type": "string" + }, + "timeout": { + "description": "Timeout is the time after which the step times out. Defaults to never. Refer to Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration", + "$ref": "#/definitions/v1.Duration" + }, + "tty": { + "description": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", + "type": "boolean" + }, + "volumeDevices": { + "description": "volumeDevices is the list of block devices to be used by the container.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.VolumeDevice" + }, + "x-kubernetes-patch-merge-key": "devicePath", + "x-kubernetes-patch-strategy": "merge" + }, + "volumeMounts": { + "description": "Pod volumes to mount into the container's filesystem. Cannot be updated.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.VolumeMount" + }, + "x-kubernetes-patch-merge-key": "mountPath", + "x-kubernetes-patch-strategy": "merge" + }, + "workingDir": { + "description": "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", + "type": "string" + }, + "workspaces": { + "description": "This is an alpha field. You must set the \"enable-api-fields\" feature flag to \"alpha\" for this field to be supported.\n\nWorkspaces is a list of workspaces from the Task that this Step wants exclusive access to. Adding a workspace to this list means that any other Step or Sidecar that does not also request this Workspace will not have access to it.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WorkspaceUsage" + } + } + } + }, + "v1beta1.StepState": { + "description": "StepState reports the results of running a step in a Task.", + "type": "object", + "properties": { + "container": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "name": { + "type": "string" + }, + "running": { + "description": "Details about a running container", + "$ref": "#/definitions/v1.ContainerStateRunning" + }, + "terminated": { + "description": "Details about a terminated container", + "$ref": "#/definitions/v1.ContainerStateTerminated" + }, + "waiting": { + "description": "Details about a waiting container", + "$ref": "#/definitions/v1.ContainerStateWaiting" + } + } + }, + "v1beta1.Task": { + "description": "Task represents a collection of sequential steps that are run as part of a Pipeline using a set of inputs and producing a set of outputs. Tasks execute when TaskRuns are created that provide the input parameters and resources and output resources the Task requires.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ObjectMeta" + }, + "spec": { + "description": "Spec holds the desired state of the Task from the client", + "default": {}, + "$ref": "#/definitions/v1beta1.TaskSpec" + } + } + }, + "v1beta1.TaskList": { + "description": "TaskList contains a list of Task", + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Task" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ListMeta" + } + } + }, + "v1beta1.TaskRef": { + "description": "TaskRef can be used to refer to a specific instance of a task. Copied from CrossVersionObjectReference: https://github.com/kubernetes/kubernetes/blob/169df7434155cbbc22f1532cba8e0a9588e29ad8/pkg/apis/autoscaling/types.go#L64", + "type": "object", + "properties": { + "apiVersion": { + "description": "API version of the referent", + "type": "string" + }, + "bundle": { + "description": "Bundle url reference to a Tekton Bundle.", + "type": "string" + }, + "kind": { + "description": "TaskKind indicates the kind of the task, namespaced or cluster scoped.", + "type": "string" + }, + "name": { + "description": "Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names", + "type": "string" + } + } + }, + "v1beta1.TaskResource": { + "description": "TaskResource defines an input or output Resource declared as a requirement by a Task. The Name field will be used to refer to these Resources within the Task definition, and when provided as an Input, the Name will be the path to the volume mounted containing this Resource as an input (e.g. an input Resource named `workspace` will be mounted at `/workspace`).", + "type": "object", + "required": [ + "name", + "type" + ], + "properties": { + "description": { + "description": "Description is a user-facing description of the declared resource that may be used to populate a UI.", + "type": "string" + }, + "name": { + "description": "Name declares the name by which a resource is referenced in the definition. Resources may be referenced by name in the definition of a Task's steps.", + "type": "string", + "default": "" + }, + "optional": { + "description": "Optional declares the resource as optional. By default optional is set to false which makes a resource required. optional: true - the resource is considered optional optional: false - the resource is considered required (equivalent of not specifying it)", + "type": "boolean" + }, + "targetPath": { + "description": "TargetPath is the path in workspace directory where the resource will be copied.", + "type": "string" + }, + "type": { + "description": "Type is the type of this resource;", + "type": "string", + "default": "" + } + } + }, + "v1beta1.TaskResourceBinding": { + "description": "TaskResourceBinding points to the PipelineResource that will be used for the Task input or output called Name.", + "type": "object", + "properties": { + "name": { + "description": "Name is the name of the PipelineResource in the Pipeline's declaration", + "type": "string" + }, + "paths": { + "description": "Paths will probably be removed in #1284, and then PipelineResourceBinding can be used instead. The optional Path field corresponds to a path on disk at which the Resource can be found (used when providing the resource via mounted volume, overriding the default logic to fetch the Resource).", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "resourceRef": { + "description": "ResourceRef is a reference to the instance of the actual PipelineResource that should be used", + "$ref": "#/definitions/v1beta1.PipelineResourceRef" + }, + "resourceSpec": { + "description": "ResourceSpec is specification of a resource that should be created and consumed by the task", + "$ref": "#/definitions/v1alpha1.PipelineResourceSpec" + } + } + }, + "v1beta1.TaskResources": { + "description": "TaskResources allows a Pipeline to declare how its DeclaredPipelineResources should be provided to a Task as its inputs and outputs.", + "type": "object", + "properties": { + "inputs": { + "description": "Inputs holds the mapping from the PipelineResources declared in DeclaredPipelineResources to the input PipelineResources required by the Task.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskResource" + } + }, + "outputs": { + "description": "Outputs holds the mapping from the PipelineResources declared in DeclaredPipelineResources to the input PipelineResources required by the Task.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskResource" + } + } + } + }, + "v1beta1.TaskResult": { + "description": "TaskResult used to describe the results of a task", + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "Description is a human-readable description of the result", + "type": "string", + "default": "" + }, + "name": { + "description": "Name the given name", + "type": "string", + "default": "" + } + } + }, + "v1beta1.TaskRun": { + "description": "TaskRun represents a single execution of a Task. TaskRuns are how the steps specified in a Task are executed; they specify the parameters and resources used to run the steps in a Task.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ObjectMeta" + }, + "spec": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunSpec" + }, + "status": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunStatus" + } + } + }, + "v1beta1.TaskRunDebug": { + "description": "TaskRunDebug defines the breakpoint config for a particular TaskRun", + "type": "object", + "properties": { + "breakpoint": { + "type": "array", + "items": { + "type": "string", + "default": "" + } + } + } + }, + "v1beta1.TaskRunInputs": { + "description": "TaskRunInputs holds the input values that this task was invoked with.", + "type": "object", + "properties": { + "params": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Param" + } + }, + "resources": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskResourceBinding" + } + } + } + }, + "v1beta1.TaskRunList": { + "description": "TaskRunList contains a list of TaskRun", + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRun" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ListMeta" + } + } + }, + "v1beta1.TaskRunOutputs": { + "description": "TaskRunOutputs holds the output values that this task was invoked with.", + "type": "object", + "properties": { + "resources": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskResourceBinding" + } + } + } + }, + "v1beta1.TaskRunResources": { + "description": "TaskRunResources allows a TaskRun to declare inputs and outputs TaskResourceBinding", + "type": "object", + "properties": { + "inputs": { + "description": "Inputs holds the inputs resources this task was invoked with", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskResourceBinding" + } + }, + "outputs": { + "description": "Outputs holds the inputs resources this task was invoked with", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskResourceBinding" + } + } + } + }, + "v1beta1.TaskRunResult": { + "description": "TaskRunResult used to describe the results of a task", + "type": "object", + "required": [ + "name", + "value" + ], + "properties": { + "name": { + "description": "Name the given name", + "type": "string", + "default": "" + }, + "value": { + "description": "Value the given value of the result", + "type": "string", + "default": "" + } + } + }, + "v1beta1.TaskRunSidecarOverride": { + "description": "TaskRunSidecarOverride is used to override the values of a Sidecar in the corresponding Task.", + "type": "object", + "required": [ + "Name", + "Resources" + ], + "properties": { + "Name": { + "description": "The name of the Sidecar to override.", + "type": "string", + "default": "" + }, + "Resources": { + "description": "The resource requirements to apply to the Sidecar.", + "default": {}, + "$ref": "#/definitions/v1.ResourceRequirements" + } + } + }, + "v1beta1.TaskRunSpec": { + "description": "TaskRunSpec defines the desired state of TaskRun", + "type": "object", + "properties": { + "debug": { + "$ref": "#/definitions/v1beta1.TaskRunDebug" + }, + "params": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Param" + } + }, + "podTemplate": { + "description": "PodTemplate holds pod specific configuration", + "$ref": "#/definitions/pod.Template" + }, + "resources": { + "$ref": "#/definitions/v1beta1.TaskRunResources" + }, + "serviceAccountName": { + "type": "string", + "default": "" + }, + "sidecarOverrides": { + "description": "Overrides to apply to Sidecars in this TaskRun. If a field is specified in both a Sidecar and a SidecarOverride, the value from the SidecarOverride will be used. This field is only supported when the alpha feature gate is enabled.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunSidecarOverride" + } + }, + "status": { + "description": "Used for cancelling a taskrun (and maybe more later on)", + "type": "string" + }, + "stepOverrides": { + "description": "Overrides to apply to Steps in this TaskRun. If a field is specified in both a Step and a StepOverride, the value from the StepOverride will be used. This field is only supported when the alpha feature gate is enabled.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunStepOverride" + } + }, + "taskRef": { + "description": "no more than one of the TaskRef and TaskSpec may be specified.", + "$ref": "#/definitions/v1beta1.TaskRef" + }, + "taskSpec": { + "$ref": "#/definitions/v1beta1.TaskSpec" + }, + "timeout": { + "description": "Time after which the build times out. Defaults to 1 hour. Specified build timeout should be less than 24h. Refer Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration", + "$ref": "#/definitions/v1.Duration" + }, + "workspaces": { + "description": "Workspaces is a list of WorkspaceBindings from volumes to workspaces.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WorkspaceBinding" + } + } + } + }, + "v1beta1.TaskRunStatus": { + "description": "TaskRunStatus defines the observed state of TaskRun", + "type": "object", + "required": [ + "podName" + ], + "properties": { + "annotations": { + "description": "Annotations is additional Status fields for the Resource to save some additional State as well as convey more information to the user. This is roughly akin to Annotations on any k8s resource, just the reconciler conveying richer information outwards.", + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "cloudEvents": { + "description": "CloudEvents describe the state of each cloud event requested via a CloudEventResource.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.CloudEventDelivery" + } + }, + "completionTime": { + "description": "CompletionTime is the time the build completed.", + "$ref": "#/definitions/v1.Time" + }, + "conditions": { + "description": "Conditions the latest available observations of a resource's current state.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/knative.Condition" + }, + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "observedGeneration": { + "description": "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", + "type": "integer", + "format": "int64" + }, + "podName": { + "description": "PodName is the name of the pod responsible for executing this task's steps.", + "type": "string", + "default": "" + }, + "resourcesResult": { + "description": "Results from Resources built during the taskRun. currently includes the digest of build container images", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineResourceResult" + } + }, + "retriesStatus": { + "description": "RetriesStatus contains the history of TaskRunStatus in case of a retry in order to keep record of failures. All TaskRunStatus stored in RetriesStatus will have no date within the RetriesStatus as is redundant.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunStatus" + } + }, + "sidecars": { + "description": "The list has one entry per sidecar in the manifest. Each entry is represents the imageid of the corresponding sidecar.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.SidecarState" + } + }, + "startTime": { + "description": "StartTime is the time the build is actually started.", + "$ref": "#/definitions/v1.Time" + }, + "steps": { + "description": "Steps describes the state of each build step container.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.StepState" + } + }, + "taskResults": { + "description": "TaskRunResults are the list of results written out by the task's containers", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunResult" + } + }, + "taskSpec": { + "description": "TaskSpec contains the Spec from the dereferenced Task definition used to instantiate this TaskRun.", + "$ref": "#/definitions/v1beta1.TaskSpec" + } + } + }, + "v1beta1.TaskRunStatusFields": { + "description": "TaskRunStatusFields holds the fields of TaskRun's status. This is defined separately and inlined so that other types can readily consume these fields via duck typing.", + "type": "object", + "required": [ + "podName" + ], + "properties": { + "cloudEvents": { + "description": "CloudEvents describe the state of each cloud event requested via a CloudEventResource.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.CloudEventDelivery" + } + }, + "completionTime": { + "description": "CompletionTime is the time the build completed.", + "$ref": "#/definitions/v1.Time" + }, + "podName": { + "description": "PodName is the name of the pod responsible for executing this task's steps.", + "type": "string", + "default": "" + }, + "resourcesResult": { + "description": "Results from Resources built during the taskRun. currently includes the digest of build container images", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.PipelineResourceResult" + } + }, + "retriesStatus": { + "description": "RetriesStatus contains the history of TaskRunStatus in case of a retry in order to keep record of failures. All TaskRunStatus stored in RetriesStatus will have no date within the RetriesStatus as is redundant.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunStatus" + } + }, + "sidecars": { + "description": "The list has one entry per sidecar in the manifest. Each entry is represents the imageid of the corresponding sidecar.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.SidecarState" + } + }, + "startTime": { + "description": "StartTime is the time the build is actually started.", + "$ref": "#/definitions/v1.Time" + }, + "steps": { + "description": "Steps describes the state of each build step container.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.StepState" + } + }, + "taskResults": { + "description": "TaskRunResults are the list of results written out by the task's containers", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskRunResult" + } + }, + "taskSpec": { + "description": "TaskSpec contains the Spec from the dereferenced Task definition used to instantiate this TaskRun.", + "$ref": "#/definitions/v1beta1.TaskSpec" + } + } + }, + "v1beta1.TaskRunStepOverride": { + "description": "TaskRunStepOverride is used to override the values of a Step in the corresponding Task.", + "type": "object", + "required": [ + "Name", + "Resources" + ], + "properties": { + "Name": { + "description": "The name of the Step to override.", + "type": "string", + "default": "" + }, + "Resources": { + "description": "The resource requirements to apply to the Step.", + "default": {}, + "$ref": "#/definitions/v1.ResourceRequirements" + } + } + }, + "v1beta1.TaskSpec": { + "description": "TaskSpec defines the desired state of Task.", + "type": "object", + "properties": { + "description": { + "description": "Description is a user-facing description of the task that may be used to populate a UI.", + "type": "string" + }, + "params": { + "description": "Params is a list of input parameters required to run the task. Params must be supplied as inputs in TaskRuns unless they declare a default value.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.ParamSpec" + } + }, + "resources": { + "description": "Resources is a list input and output resource to run the task Resources are represented in TaskRuns as bindings to instances of PipelineResources.", + "$ref": "#/definitions/v1beta1.TaskResources" + }, + "results": { + "description": "Results are values that this Task can output", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.TaskResult" + } + }, + "sidecars": { + "description": "Sidecars are run alongside the Task's step containers. They begin before the steps start and end after the steps complete.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Sidecar" + } + }, + "stepTemplate": { + "description": "StepTemplate can be used as the basis for all step containers within the Task, so that the steps inherit settings on the base container.", + "$ref": "#/definitions/v1.Container" + }, + "steps": { + "description": "Steps are the steps of the build; each step is run sequentially with the source mounted into /workspace.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.Step" + } + }, + "volumes": { + "description": "Volumes is a collection of volumes that are available to mount into the steps of the build.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.Volume" + } + }, + "workspaces": { + "description": "Workspaces are the volumes that this Task requires.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1beta1.WorkspaceDeclaration" + } + } + } + }, + "v1beta1.TimeoutFields": { + "description": "TimeoutFields allows granular specification of pipeline, task, and finally timeouts", + "type": "object", + "properties": { + "finally": { + "description": "Finally sets the maximum allowed duration of this pipeline's finally", + "$ref": "#/definitions/v1.Duration" + }, + "pipeline": { + "description": "Pipeline sets the maximum allowed duration for execution of the entire pipeline. The sum of individual timeouts for tasks and finally must not exceed this value.", + "$ref": "#/definitions/v1.Duration" + }, + "tasks": { + "description": "Tasks sets the maximum allowed duration of this pipeline's tasks", + "$ref": "#/definitions/v1.Duration" + } + } + }, + "v1beta1.WhenExpression": { + "description": "WhenExpression allows a PipelineTask to declare expressions to be evaluated before the Task is run to determine whether the Task should be executed or skipped", + "type": "object", + "required": [ + "input", + "operator", + "values" + ], + "properties": { + "input": { + "description": "Input is the string for guard checking which can be a static input or an output from a parent Task", + "type": "string", + "default": "" + }, + "operator": { + "description": "Operator that represents an Input's relationship to the values", + "type": "string", + "default": "" + }, + "values": { + "description": "Values is an array of strings, which is compared against the input, for guard checking It must be non-empty", + "type": "array", + "items": { + "type": "string", + "default": "" + } + } + } + }, + "v1beta1.WorkspaceBinding": { + "description": "WorkspaceBinding maps a Task's declared workspace to a Volume.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "configMap": { + "description": "ConfigMap represents a configMap that should populate this workspace.", + "$ref": "#/definitions/v1.ConfigMapVolumeSource" + }, + "emptyDir": { + "description": "EmptyDir represents a temporary directory that shares a Task's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir Either this OR PersistentVolumeClaim can be used.", + "$ref": "#/definitions/v1.EmptyDirVolumeSource" + }, + "name": { + "description": "Name is the name of the workspace populated by the volume.", + "type": "string", + "default": "" + }, + "persistentVolumeClaim": { + "description": "PersistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. Either this OR EmptyDir can be used.", + "$ref": "#/definitions/v1.PersistentVolumeClaimVolumeSource" + }, + "secret": { + "description": "Secret represents a secret that should populate this workspace.", + "$ref": "#/definitions/v1.SecretVolumeSource" + }, + "subPath": { + "description": "SubPath is optionally a directory on the volume which should be used for this binding (i.e. the volume will be mounted at this sub directory).", + "type": "string" + }, + "volumeClaimTemplate": { + "description": "VolumeClaimTemplate is a template for a claim that will be created in the same namespace. The PipelineRun controller is responsible for creating a unique claim for each instance of PipelineRun.", + "$ref": "#/definitions/v1.PersistentVolumeClaim" + } + } + }, + "v1beta1.WorkspaceDeclaration": { + "description": "WorkspaceDeclaration is a declaration of a volume that a Task requires.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "Description is an optional human readable description of this volume.", + "type": "string" + }, + "mountPath": { + "description": "MountPath overrides the directory that the volume will be made available at.", + "type": "string" + }, + "name": { + "description": "Name is the name by which you can bind the volume at runtime.", + "type": "string", + "default": "" + }, + "optional": { + "description": "Optional marks a Workspace as not being required in TaskRuns. By default this field is false and so declared workspaces are required.", + "type": "boolean" + }, + "readOnly": { + "description": "ReadOnly dictates whether a mounted volume is writable. By default this field is false and so mounted volumes are writable.", + "type": "boolean" + } + } + }, + "v1beta1.WorkspacePipelineTaskBinding": { + "description": "WorkspacePipelineTaskBinding describes how a workspace passed into the pipeline should be mapped to a task's declared workspace.", + "type": "object", + "required": [ + "name", + "workspace" + ], + "properties": { + "name": { + "description": "Name is the name of the workspace as declared by the task", + "type": "string", + "default": "" + }, + "subPath": { + "description": "SubPath is optionally a directory on the volume which should be used for this binding (i.e. the volume will be mounted at this sub directory).", + "type": "string" + }, + "workspace": { + "description": "Workspace is the name of the workspace declared by the pipeline", + "type": "string", + "default": "" + } + } + }, + "v1beta1.WorkspaceUsage": { + "description": "WorkspaceUsage is used by a Step or Sidecar to declare that it wants isolated access to a Workspace defined in a Task.", + "type": "object", + "required": [ + "name", + "mountPath" + ], + "properties": { + "mountPath": { + "description": "MountPath is the path that the workspace should be mounted to inside the Step or Sidecar, overriding any MountPath specified in the Task's WorkspaceDeclaration.", + "type": "string", + "default": "" + }, + "name": { + "description": "Name is the name of the workspace this Step or Sidecar wants access to.", + "type": "string", + "default": "" + } + } + } + } +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_binding_defaults.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_binding_defaults.go new file mode 100644 index 0000000000..1bd52d80fe --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_binding_defaults.go @@ -0,0 +1,24 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" +) + +// SetDefaults initializes TriggerBinding tb with its default values. +func (tb *TriggerBinding) SetDefaults(ctx context.Context) {} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_binding_interface.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_binding_interface.go new file mode 100644 index 0000000000..f147a41b98 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_binding_interface.go @@ -0,0 +1,26 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// TriggerBindingInterface is implemented by TriggerBinding and ClusterTriggerBinding +type TriggerBindingInterface interface { + TriggerBindingMetadata() metav1.ObjectMeta + TriggerBindingSpec() TriggerBindingSpec + Copy() TriggerBindingInterface +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_binding_types.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_binding_types.go new file mode 100644 index 0000000000..7bce134dbf --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_binding_types.go @@ -0,0 +1,77 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +// Check that TriggerBinding may be validated and defaulted. +var _ apis.Validatable = (*TriggerBinding)(nil) +var _ apis.Defaultable = (*TriggerBinding)(nil) + +func (tb *TriggerBinding) TriggerBindingSpec() TriggerBindingSpec { + return tb.Spec +} + +func (tb *TriggerBinding) TriggerBindingMetadata() metav1.ObjectMeta { + return tb.ObjectMeta +} + +func (tb *TriggerBinding) Copy() TriggerBindingInterface { + return tb.DeepCopy() +} + +// TriggerBindingSpec defines the desired state of the TriggerBinding. +type TriggerBindingSpec struct { + // Params defines the parameter mapping from the given input event. + // +listType=atomic + Params []Param `json:"params,omitempty"` +} + +// TriggerBindingStatus defines the observed state of TriggerBinding. +type TriggerBindingStatus struct{} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// TriggerBinding defines a mapping of an input event to parameters. This is used +// to extract information from events to be passed to TriggerTemplates within a +// Trigger. +// +k8s:openapi-gen=true +type TriggerBinding struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + // Spec holds the desired state of the TriggerBinding + // +optional + Spec TriggerBindingSpec `json:"spec"` + // +optional + Status TriggerBindingStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// TriggerBindingList contains a list of TriggerBindings. +// We don't use this but it's required for certain codegen features. +type TriggerBindingList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []TriggerBinding `json:"items"` +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_binding_validation.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_binding_validation.go new file mode 100644 index 0000000000..0f6346f403 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_binding_validation.go @@ -0,0 +1,97 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + "strings" + + "github.com/tektoncd/pipeline/pkg/apis/validate" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/util/sets" + "knative.dev/pkg/apis" +) + +// Validate TriggerBinding. +func (tb *TriggerBinding) Validate(ctx context.Context) *apis.FieldError { + errs := validate.ObjectMetadata(tb.GetObjectMeta()).ViaField("metadata") + if apis.IsInDelete(ctx) { + return nil + } + return errs.Also(tb.Spec.Validate(ctx).ViaField("spec")) +} + +// Validate TriggerBindingSpec. +func (s *TriggerBindingSpec) Validate(ctx context.Context) (errs *apis.FieldError) { + if equality.Semantic.DeepEqual(s, &TriggerBindingSpec{}) { + return errs.Also(apis.ErrMissingField(apis.CurrentField)) + } + return errs.Also(validateParams(s.Params).ViaField("params")) +} + +func validateParams(params []Param) *apis.FieldError { + // Ensure there aren't multiple params with the same name. + seen := sets.NewString() + for i, param := range params { + if seen.Has(param.Name) { + return apis.ErrMultipleOneOf(fmt.Sprintf("[%d].name", i)) + } + seen.Insert(param.Name) + errs := validateParamValue(param.Value).ViaField(fmt.Sprintf("[%d]", i)) + if errs != nil { + return errs + } + } + return nil +} + +func validateParamValue(in string) *apis.FieldError { + if !strings.Contains(in, "$(") { + return nil + } + // Splits string on $( to find potential Tekton expressions + maybeExpressions := strings.Split(in, "$(") + terminated := true + for _, e := range maybeExpressions[1:] { // Split always returns at least one element + // Iterate until we find the first unbalanced ) + numOpenBrackets := 0 + if !terminated { + return apis.ErrInvalidValue(in, "value") + } + terminated = false + for _, ch := range e { + switch ch { + case '(': + numOpenBrackets++ + case ')': + numOpenBrackets-- + if numOpenBrackets < 0 { + terminated = true + } + default: + continue + } + if numOpenBrackets < 0 { + terminated = true + break + } + } + } + return nil + +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_defaults.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_defaults.go new file mode 100644 index 0000000000..cc1fb85eba --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_defaults.go @@ -0,0 +1,47 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + + "github.com/tektoncd/triggers/pkg/apis/triggers/contexts" +) + +type triggerSpecBindingArray []*TriggerSpecBinding + +// SetDefaults sets the defaults on the object. +func (t *Trigger) SetDefaults(ctx context.Context) { + if !contexts.IsUpgradeViaDefaulting(ctx) { + return + } + triggerSpecBindingArray(t.Spec.Bindings).defaultBindings() + for _, ti := range t.Spec.Interceptors { + ti.defaultInterceptorKind() + } +} + +// set default TriggerBinding kind for Bindings in TriggerSpec +func (t triggerSpecBindingArray) defaultBindings() { + if len(t) > 0 { + for _, b := range t { + if b.Kind == "" { + b.Kind = NamespacedTriggerBindingKind + } + } + } +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_template_defaults.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_template_defaults.go new file mode 100644 index 0000000000..de7097e8a2 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_template_defaults.go @@ -0,0 +1,24 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" +) + +// SetDefaults initializes TriggerTemplate with default values. +func (tt *TriggerTemplate) SetDefaults(ctx context.Context) {} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_template_types.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_template_types.go new file mode 100644 index 0000000000..78689cdb82 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_template_types.go @@ -0,0 +1,69 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "knative.dev/pkg/apis" +) + +// Check that TriggerTemplate may be validated and defaulted. +var _ apis.Validatable = (*TriggerTemplate)(nil) +var _ apis.Defaultable = (*TriggerTemplate)(nil) + +// TriggerTemplateSpec holds the desired state of TriggerTemplate +type TriggerTemplateSpec struct { + // +listType=atomic + Params []ParamSpec `json:"params,omitempty"` + // +listType=atomic + ResourceTemplates []TriggerResourceTemplate `json:"resourcetemplates,omitempty"` +} + +// TriggerResourceTemplate describes a resource to create +type TriggerResourceTemplate struct { + runtime.RawExtension `json:",inline"` +} + +// TriggerTemplateStatus describes the desired state of TriggerTemplate +type TriggerTemplateStatus struct{} + +// TriggerTemplate takes parameters and uses them to create CRDs +// +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +k8s:openapi-gen=true +type TriggerTemplate struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + // Spec holds the desired state of the TriggerTemplate from the client + // +optional + Spec TriggerTemplateSpec `json:"spec"` + // +optional + Status TriggerTemplateStatus `json:"status,omitempty"` +} + +// TriggerTemplateList contains a list of TriggerTemplate +// +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type TriggerTemplateList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []TriggerTemplate `json:"items"` +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_template_validation.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_template_validation.go new file mode 100644 index 0000000000..858aa3f176 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_template_validation.go @@ -0,0 +1,109 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + "regexp" + "strings" + + "github.com/tektoncd/pipeline/pkg/apis/validate" + "github.com/tektoncd/triggers/pkg/apis/config" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/sets" + "knative.dev/pkg/apis" +) + +// paramsRegexp captures TriggerTemplate parameter names $(tt.params.NAME) +var paramsRegexp = regexp.MustCompile(`\$\(tt.params.(?P[_a-zA-Z][_a-zA-Z0-9.-]*)\)`) + +// Validate validates a TriggerTemplate. +func (t *TriggerTemplate) Validate(ctx context.Context) *apis.FieldError { + errs := validate.ObjectMetadata(t.GetObjectMeta()).ViaField("metadata") + if apis.IsInDelete(ctx) { + return nil + } + return errs.Also(t.Spec.validate(ctx).ViaField("spec")) +} + +// Validate validates a TriggerTemplateSpec. +func (s *TriggerTemplateSpec) validate(ctx context.Context) (errs *apis.FieldError) { + if equality.Semantic.DeepEqual(s, &TriggerTemplateSpec{}) { + errs = errs.Also(apis.ErrMissingField(apis.CurrentField)) + } + if len(s.ResourceTemplates) == 0 { + errs = errs.Also(apis.ErrMissingField("resourcetemplates")) + } + errs = errs.Also(validateResourceTemplates(s.ResourceTemplates).ViaField("resourcetemplates")) + errs = errs.Also(verifyParamDeclarations(s.Params, s.ResourceTemplates).ViaField("resourcetemplates")) + return errs +} + +func validateResourceTemplates(templates []TriggerResourceTemplate) (errs *apis.FieldError) { + for i, trt := range templates { + if err := config.EnsureAllowedType(trt.RawExtension); err != nil { + if runtime.IsMissingVersion(err) { + errs = errs.Also(apis.ErrMissingField(fmt.Sprintf("[%d].apiVersion", i))) + } + if runtime.IsMissingKind(err) { + errs = errs.Also(apis.ErrMissingField(fmt.Sprintf("[%d].kind", i))) + } + if runtime.IsNotRegisteredError(err) { + errStr := err.Error() + if strings.Contains(errStr, "in scheme") { + // not registered error messages currently include the scheme variable location in your file, + // which can of course change if you move the location of the variable in your file. + // So will filter it out here to facilitate our unit testing, as the scheme location is not + // useful for our purposes. + errStr = errStr[:strings.Index(errStr, " in scheme")] + } + errs = errs.Also(apis.ErrInvalidValue( + errStr, + fmt.Sprintf("[%d]", i))) + } + // we allow structural errors because of param substitution + } + } + return errs +} + +// Verify every param in the ResourceTemplates is declared with a ParamSpec +func verifyParamDeclarations(params []ParamSpec, templates []TriggerResourceTemplate) *apis.FieldError { + declaredParamNames := sets.NewString() + for _, param := range params { + declaredParamNames.Insert(param.Name) + } + for i, template := range templates { + // Get all params in the template $(tt.params.NAME) + templateParams := paramsRegexp.FindAllSubmatch(template.RawExtension.Raw, -1) + for _, templateParam := range templateParams { + templateParamName := string(templateParam[1]) + if !declaredParamNames.Has(templateParamName) { + fieldErr := apis.ErrInvalidValue( + fmt.Sprintf("undeclared param '$(tt.params.%s)'", templateParamName), + fmt.Sprintf("[%d]", i), + ) + fieldErr.Details = fmt.Sprintf("'$(tt.params.%s)' must be declared in spec.params", templateParamName) + return fieldErr + } + } + } + + return nil +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_types.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_types.go new file mode 100644 index 0000000000..8fec734523 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_types.go @@ -0,0 +1,201 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + corev1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +// TriggerSpec represents a connection between TriggerSpecBinding, +// and TriggerSpecTemplate; TriggerSpecBinding provides extracted values for +// TriggerSpecTemplate to then create resources from. +type TriggerSpec struct { + // +listType=atomic + Bindings []*TriggerSpecBinding `json:"bindings"` + Template TriggerSpecTemplate `json:"template"` + // +optional + Name string `json:"name,omitempty"` + // +listType=atomic + Interceptors []*TriggerInterceptor `json:"interceptors,omitempty"` + // ServiceAccountName optionally associates credentials with each trigger; + // Unlike EventListeners, this should be scoped to the same namespace + // as the Trigger itself + // +optional + ServiceAccountName string `json:"serviceAccountName,omitempty"` +} + +type TriggerSpecTemplate struct { + Ref *string `json:"ref,omitempty"` + APIVersion string `json:"apiversion,omitempty"` + Spec *TriggerTemplateSpec `json:"spec,omitempty"` +} + +type TriggerSpecBinding struct { + // Name is the name of the binding param + // Mutually exclusive with Ref + Name string `json:"name,omitempty"` + // Value is the value of the binding param. Can contain JSONPath + // Has to be pointer since "" is a valid value + // Required if Name is also specified. + Value *string `json:"value,omitempty"` + + // Ref is a reference to a TriggerBinding kind. + // Mutually exclusive with Name + Ref string `json:"ref,omitempty"` + + // Kind can only be provided if Ref is also provided. Defaults to TriggerBinding + Kind TriggerBindingKind `json:"kind,omitempty"` + + // APIVersion of the binding ref + APIVersion string `json:"apiversion,omitempty"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Trigger defines a mapping of an input event to parameters. This is used +// to extract information from events to be passed to TriggerTemplates within a +// Trigger. +// +k8s:openapi-gen=true +type Trigger struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + // Spec holds the desired state of the Trigger + // +optional + Spec TriggerSpec `json:"spec"` +} + +// TriggerInterceptor provides a hook to intercept and pre-process events +type TriggerInterceptor struct { + // Optional name to identify the current interceptor configuration + Name *string `json:"name,omitempty"` + // Ref refers to the Interceptor to use + Ref InterceptorRef `json:"ref"` + // Params are the params to send to the interceptor + // +listType=atomic + Params []InterceptorParams `json:"params,omitempty"` + + // WebhookInterceptor refers to an old style webhook interceptor service + Webhook *WebhookInterceptor `json:"webhook,omitempty"` +} + +// InterceptorParams defines a key-value pair that can be passed on an interceptor +type InterceptorParams struct { + Name string `json:"name"` + Value apiextensionsv1.JSON `json:"value"` +} + +// InterceptorRef provides a Reference to a ClusterInterceptor +type InterceptorRef struct { + // Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names + Name string `json:"name,omitempty"` + // InterceptorKind indicates the kind of the Interceptor, namespaced or cluster scoped. + // Currently only InterceptorKind is ClusterInterceptor, so the only valid value + // is the default one + // +optional + Kind InterceptorKind `json:"kind,omitempty"` + // API version of the referent + // +optional + APIVersion string `json:"apiVersion,omitempty"` +} + +// InterceptorKind defines the type of Interceptor used by the Trigger. +type InterceptorKind string + +const ( + // ClusterTaskKind indicates that task type has a cluster scope. + ClusterInterceptorKind InterceptorKind = "ClusterInterceptor" +) + +func (ti *TriggerInterceptor) defaultInterceptorKind() { + if ti.Ref.Kind == "" { + ti.Ref.Kind = ClusterInterceptorKind + } +} + +// GetName returns the name for the given interceptor +func (ti *TriggerInterceptor) GetName() string { + if ti.Ref.Name != "" { + return ti.Ref.Name + } + return "" +} + +// WebhookInterceptor provides a webhook to intercept and pre-process events +type WebhookInterceptor struct { + // ObjectRef is a reference to an object that will resolve to a cluster DNS + // name to use as the EventInterceptor. Either objectRef or url can be specified + // +optional + ObjectRef *corev1.ObjectReference `json:"objectRef,omitempty"` + // +optional + URL *apis.URL `json:"url,omitempty"` + // Header is a group of key-value pairs that can be appended to the + // interceptor request headers. This allows the interceptor to make + // decisions specific to an EventListenerTrigger. + // +listType=atomic + Header []v1beta1.Param `json:"header,omitempty"` +} + +// BitbucketInterceptor provides a webhook to intercept and pre-process events +type BitbucketInterceptor struct { + SecretRef *SecretRef `json:"secretRef,omitempty"` + // +listType=atomic + EventTypes []string `json:"eventTypes,omitempty"` +} + +// GitHubInterceptor provides a webhook to intercept and pre-process events +type GitHubInterceptor struct { + SecretRef *SecretRef `json:"secretRef,omitempty"` + // +listType=atomic + EventTypes []string `json:"eventTypes,omitempty"` +} + +// GitLabInterceptor provides a webhook to intercept and pre-process events +type GitLabInterceptor struct { + SecretRef *SecretRef `json:"secretRef,omitempty"` + // +listType=atomic + EventTypes []string `json:"eventTypes,omitempty"` +} + +// CELInterceptor provides a webhook to intercept and pre-process events +type CELInterceptor struct { + Filter string `json:"filter,omitempty"` + // +listType=atomic + Overlays []CELOverlay `json:"overlays,omitempty"` +} + +// CELOverlay provides a way to modify the request body using CEL expressions +type CELOverlay struct { + Key string `json:"key,omitempty"` + Expression string `json:"expression,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// TriggerList contains a list of Triggers. +// We don't use this but it's required for certain codegen features. +type TriggerList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []Trigger `json:"items"` +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_types_convert.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_types_convert.go new file mode 100644 index 0000000000..59082a4e7b --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_types_convert.go @@ -0,0 +1,41 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "encoding/json" +) + +// ToEventListenerTrigger converts a TriggerSpec into an EventListenerTrigger. +// This is primarily for compatibility between CRD and non-CRD types so that +// underlying libraries can reuse existing code. +func ToEventListenerTrigger(in TriggerSpec) (EventListenerTrigger, error) { + var out EventListenerTrigger + + // Use json Marshalling in order to be field agnostic. Since TriggerSpec + // is a subset of the existing EventListenerTrigger type, and should always + // contain the same field labels, this should be safe to do. + b, err := json.Marshal(in) + if err != nil { + return out, err + } + + if err := json.Unmarshal(b, &out); err != nil { + return out, err + } + return out, nil +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_validation.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_validation.go new file mode 100644 index 0000000000..1a5642f2f5 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/trigger_validation.go @@ -0,0 +1,136 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + "net/http" + + pipelinev1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "github.com/tektoncd/pipeline/pkg/apis/validate" + "knative.dev/pkg/apis" +) + +// Validate validates a Trigger +func (t *Trigger) Validate(ctx context.Context) *apis.FieldError { + errs := validate.ObjectMetadata(t.GetObjectMeta()).ViaField("metadata") + if apis.IsInDelete(ctx) { + return nil + } + return errs.Also(t.Spec.validate(ctx).ViaField("spec")) +} + +func (t *TriggerSpec) validate(ctx context.Context) *apis.FieldError { + // Validate optional Bindings + errs := triggerSpecBindingArray(t.Bindings).validate(ctx) + // Validate required TriggerTemplate + errs = errs.Also(t.Template.validate(ctx)) + + // Validate optional Interceptors + for i, interceptor := range t.Interceptors { + errs = errs.Also(interceptor.validate(ctx).ViaField(fmt.Sprintf("interceptors[%d]", i))) + } + + return errs +} + +func (t TriggerSpecTemplate) validate(ctx context.Context) (errs *apis.FieldError) { + // Optional explicit match + if t.APIVersion != "" { + if t.APIVersion != "v1alpha1" && t.APIVersion != "v1beta1" { + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("invalid apiVersion"), "template.apiVersion")) + } + } + + switch { + case t.Spec != nil && t.Ref != nil: + errs = errs.Also(apis.ErrMultipleOneOf("template.spec", "template.ref")) + case t.Spec == nil && t.Ref == nil: + errs = errs.Also(apis.ErrMissingOneOf("template.spec", "template.ref")) + case t.Spec != nil: + errs = errs.Also(t.Spec.validate(ctx)) + case t.Ref == nil || *t.Ref == "": + errs = errs.Also(apis.ErrMissingField("template.ref")) + } + return errs +} + +func (t triggerSpecBindingArray) validate(ctx context.Context) (errs *apis.FieldError) { + for i, b := range t { + switch { + case b.Ref != "": + switch { + case b.Name != "": // Cannot specify both Ref and Name + errs = errs.Also(apis.ErrMultipleOneOf(fmt.Sprintf("bindings[%d].ref", i), fmt.Sprintf("bindings[%d].name", i))) + case b.Kind != NamespacedTriggerBindingKind && b.Kind != ClusterTriggerBindingKind: // Kind must be valid + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("invalid kind"), fmt.Sprintf("bindings[%d].kind", i))) + } + case b.Name != "": + if b.Value == nil { // Value is mandatory if Name is specified + errs = errs.Also(apis.ErrMissingField(fmt.Sprintf("bindings[%d].value", i))) + } + default: + errs = errs.Also(apis.ErrMissingOneOf(fmt.Sprintf("bindings[%d].ref", i), fmt.Sprintf("bindings[%d].spec", i), fmt.Sprintf("bindings[%d].name", i))) + } + } + return errs +} + +func (i *TriggerInterceptor) validate(ctx context.Context) (errs *apis.FieldError) { + if i.Webhook == nil { + if i.Ref.Name == "" { // Check to see if Interceptor referenced using Ref + errs = errs.Also(apis.ErrMissingField("interceptor")) + } + } + + if i.Webhook != nil { // TODO: This should be an error? + w := i.Webhook + if i.Webhook.ObjectRef == nil { + errs = errs.Also(apis.ErrMissingField("interceptor.webhook.objectRef")) + } else { + if w.ObjectRef.Kind == "" { + errs = errs.Also(apis.ErrMissingField("interceptor.webhook.objectRef.kind")) + } else if w.ObjectRef.Kind != "Service" { + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("invalid kind"), "interceptor.webhook.objectRef.kind")) + } + + // Optional explicit match + if w.ObjectRef.APIVersion == "" { + errs = errs.Also(apis.ErrMissingField("interceptor.webhook.objectRef.apiVersion")) + } else if w.ObjectRef.APIVersion != "v1" { + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("invalid apiVersion"), "interceptor.webhook.objectRef.apiVersion")) + } + } + + for i, header := range w.Header { + // Enforce non-empty canonical header keys + if len(header.Name) == 0 || http.CanonicalHeaderKey(header.Name) != header.Name { + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("invalid header name"), fmt.Sprintf("interceptor.webhook.header[%d].name", i))) + } + // Enforce non-empty header values + if header.Value.Type == pipelinev1.ParamTypeString { + if len(header.Value.StringVal) == 0 { + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("invalid header value"), fmt.Sprintf("interceptor.webhook.header[%d].value", i))) + } + } else if len(header.Value.ArrayVal) == 0 { + errs = errs.Also(apis.ErrInvalidValue(fmt.Errorf("invalid header value"), fmt.Sprintf("interceptor.webhook.header[%d].value", i))) + } + } + } + return errs +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/version_validation.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/version_validation.go new file mode 100644 index 0000000000..33039bf2bc --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/version_validation.go @@ -0,0 +1,38 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package v1beta1 + +import ( + "context" + "fmt" + + "github.com/tektoncd/triggers/pkg/apis/config" + "knative.dev/pkg/apis" +) + +// ValidateEnabledAPIFields checks that the enable-api-fields feature gate is set +// to the wantVersion value and, if not, returns an error stating which feature +// is dependent on the version and what the current version actually is. +func ValidateEnabledAPIFields(ctx context.Context, featureName, wantVersion string) *apis.FieldError { + currentVersion := config.FromContextOrDefaults(ctx).FeatureFlags.EnableAPIFields + if currentVersion != wantVersion { + var errs *apis.FieldError + message := fmt.Sprintf(`%s requires "enable-api-fields" feature gate to be %q but it is %q`, featureName, wantVersion, currentVersion) + return errs.Also(apis.ErrGeneric(message)) + } + return nil +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/zz_generated.deepcopy.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..1f5268311f --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,1123 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1beta1 + +import ( + pipelinev1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + apis "knative.dev/pkg/apis" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BitbucketInterceptor) DeepCopyInto(out *BitbucketInterceptor) { + *out = *in + if in.SecretRef != nil { + in, out := &in.SecretRef, &out.SecretRef + *out = new(SecretRef) + **out = **in + } + if in.EventTypes != nil { + in, out := &in.EventTypes, &out.EventTypes + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BitbucketInterceptor. +func (in *BitbucketInterceptor) DeepCopy() *BitbucketInterceptor { + if in == nil { + return nil + } + out := new(BitbucketInterceptor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CELInterceptor) DeepCopyInto(out *CELInterceptor) { + *out = *in + if in.Overlays != nil { + in, out := &in.Overlays, &out.Overlays + *out = make([]CELOverlay, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CELInterceptor. +func (in *CELInterceptor) DeepCopy() *CELInterceptor { + if in == nil { + return nil + } + out := new(CELInterceptor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CELOverlay) DeepCopyInto(out *CELOverlay) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CELOverlay. +func (in *CELOverlay) DeepCopy() *CELOverlay { + if in == nil { + return nil + } + out := new(CELOverlay) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterTriggerBinding) DeepCopyInto(out *ClusterTriggerBinding) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterTriggerBinding. +func (in *ClusterTriggerBinding) DeepCopy() *ClusterTriggerBinding { + if in == nil { + return nil + } + out := new(ClusterTriggerBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterTriggerBinding) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterTriggerBindingList) DeepCopyInto(out *ClusterTriggerBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterTriggerBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterTriggerBindingList. +func (in *ClusterTriggerBindingList) DeepCopy() *ClusterTriggerBindingList { + if in == nil { + return nil + } + out := new(ClusterTriggerBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterTriggerBindingList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CustomResource) DeepCopyInto(out *CustomResource) { + *out = *in + in.RawExtension.DeepCopyInto(&out.RawExtension) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResource. +func (in *CustomResource) DeepCopy() *CustomResource { + if in == nil { + return nil + } + out := new(CustomResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EventListener) DeepCopyInto(out *EventListener) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventListener. +func (in *EventListener) DeepCopy() *EventListener { + if in == nil { + return nil + } + out := new(EventListener) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EventListener) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EventListenerConfig) DeepCopyInto(out *EventListenerConfig) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventListenerConfig. +func (in *EventListenerConfig) DeepCopy() *EventListenerConfig { + if in == nil { + return nil + } + out := new(EventListenerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EventListenerList) DeepCopyInto(out *EventListenerList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]EventListener, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventListenerList. +func (in *EventListenerList) DeepCopy() *EventListenerList { + if in == nil { + return nil + } + out := new(EventListenerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EventListenerList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EventListenerSpec) DeepCopyInto(out *EventListenerSpec) { + *out = *in + if in.Triggers != nil { + in, out := &in.Triggers, &out.Triggers + *out = make([]EventListenerTrigger, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.TriggerGroups != nil { + in, out := &in.TriggerGroups, &out.TriggerGroups + *out = make([]EventListenerTriggerGroup, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.NamespaceSelector.DeepCopyInto(&out.NamespaceSelector) + if in.LabelSelector != nil { + in, out := &in.LabelSelector, &out.LabelSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + in.Resources.DeepCopyInto(&out.Resources) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventListenerSpec. +func (in *EventListenerSpec) DeepCopy() *EventListenerSpec { + if in == nil { + return nil + } + out := new(EventListenerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EventListenerStatus) DeepCopyInto(out *EventListenerStatus) { + *out = *in + in.Status.DeepCopyInto(&out.Status) + in.AddressStatus.DeepCopyInto(&out.AddressStatus) + out.Configuration = in.Configuration + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventListenerStatus. +func (in *EventListenerStatus) DeepCopy() *EventListenerStatus { + if in == nil { + return nil + } + out := new(EventListenerStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EventListenerTrigger) DeepCopyInto(out *EventListenerTrigger) { + *out = *in + if in.Bindings != nil { + in, out := &in.Bindings, &out.Bindings + *out = make([]*TriggerSpecBinding, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TriggerSpecBinding) + (*in).DeepCopyInto(*out) + } + } + } + if in.Template != nil { + in, out := &in.Template, &out.Template + *out = new(TriggerSpecTemplate) + (*in).DeepCopyInto(*out) + } + if in.Interceptors != nil { + in, out := &in.Interceptors, &out.Interceptors + *out = make([]*TriggerInterceptor, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TriggerInterceptor) + (*in).DeepCopyInto(*out) + } + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventListenerTrigger. +func (in *EventListenerTrigger) DeepCopy() *EventListenerTrigger { + if in == nil { + return nil + } + out := new(EventListenerTrigger) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EventListenerTriggerGroup) DeepCopyInto(out *EventListenerTriggerGroup) { + *out = *in + if in.Interceptors != nil { + in, out := &in.Interceptors, &out.Interceptors + *out = make([]*TriggerInterceptor, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TriggerInterceptor) + (*in).DeepCopyInto(*out) + } + } + } + in.TriggerSelector.DeepCopyInto(&out.TriggerSelector) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventListenerTriggerGroup. +func (in *EventListenerTriggerGroup) DeepCopy() *EventListenerTriggerGroup { + if in == nil { + return nil + } + out := new(EventListenerTriggerGroup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EventListenerTriggerSelector) DeepCopyInto(out *EventListenerTriggerSelector) { + *out = *in + in.NamespaceSelector.DeepCopyInto(&out.NamespaceSelector) + if in.LabelSelector != nil { + in, out := &in.LabelSelector, &out.LabelSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventListenerTriggerSelector. +func (in *EventListenerTriggerSelector) DeepCopy() *EventListenerTriggerSelector { + if in == nil { + return nil + } + out := new(EventListenerTriggerSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitHubInterceptor) DeepCopyInto(out *GitHubInterceptor) { + *out = *in + if in.SecretRef != nil { + in, out := &in.SecretRef, &out.SecretRef + *out = new(SecretRef) + **out = **in + } + if in.EventTypes != nil { + in, out := &in.EventTypes, &out.EventTypes + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitHubInterceptor. +func (in *GitHubInterceptor) DeepCopy() *GitHubInterceptor { + if in == nil { + return nil + } + out := new(GitHubInterceptor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitLabInterceptor) DeepCopyInto(out *GitLabInterceptor) { + *out = *in + if in.SecretRef != nil { + in, out := &in.SecretRef, &out.SecretRef + *out = new(SecretRef) + **out = **in + } + if in.EventTypes != nil { + in, out := &in.EventTypes, &out.EventTypes + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitLabInterceptor. +func (in *GitLabInterceptor) DeepCopy() *GitLabInterceptor { + if in == nil { + return nil + } + out := new(GitLabInterceptor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InterceptorParams) DeepCopyInto(out *InterceptorParams) { + *out = *in + in.Value.DeepCopyInto(&out.Value) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InterceptorParams. +func (in *InterceptorParams) DeepCopy() *InterceptorParams { + if in == nil { + return nil + } + out := new(InterceptorParams) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InterceptorRef) DeepCopyInto(out *InterceptorRef) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InterceptorRef. +func (in *InterceptorRef) DeepCopy() *InterceptorRef { + if in == nil { + return nil + } + out := new(InterceptorRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesResource) DeepCopyInto(out *KubernetesResource) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.ServicePort != nil { + in, out := &in.ServicePort, &out.ServicePort + *out = new(int32) + **out = **in + } + in.WithPodSpec.DeepCopyInto(&out.WithPodSpec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesResource. +func (in *KubernetesResource) DeepCopy() *KubernetesResource { + if in == nil { + return nil + } + out := new(KubernetesResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NamespaceSelector) DeepCopyInto(out *NamespaceSelector) { + *out = *in + if in.MatchNames != nil { + in, out := &in.MatchNames, &out.MatchNames + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceSelector. +func (in *NamespaceSelector) DeepCopy() *NamespaceSelector { + if in == nil { + return nil + } + out := new(NamespaceSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Param) DeepCopyInto(out *Param) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Param. +func (in *Param) DeepCopy() *Param { + if in == nil { + return nil + } + out := new(Param) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ParamSpec) DeepCopyInto(out *ParamSpec) { + *out = *in + if in.Default != nil { + in, out := &in.Default, &out.Default + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ParamSpec. +func (in *ParamSpec) DeepCopy() *ParamSpec { + if in == nil { + return nil + } + out := new(ParamSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodTemplate) DeepCopyInto(out *PodTemplate) { + *out = *in + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]corev1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodTemplate. +func (in *PodTemplate) DeepCopy() *PodTemplate { + if in == nil { + return nil + } + out := new(PodTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + if in.KubernetesResource != nil { + in, out := &in.KubernetesResource, &out.KubernetesResource + *out = new(KubernetesResource) + (*in).DeepCopyInto(*out) + } + if in.CustomResource != nil { + in, out := &in.CustomResource, &out.CustomResource + *out = new(CustomResource) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretRef) DeepCopyInto(out *SecretRef) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretRef. +func (in *SecretRef) DeepCopy() *SecretRef { + if in == nil { + return nil + } + out := new(SecretRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Status) DeepCopyInto(out *Status) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Status. +func (in *Status) DeepCopy() *Status { + if in == nil { + return nil + } + out := new(Status) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StatusError) DeepCopyInto(out *StatusError) { + *out = *in + out.s = in.s + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StatusError. +func (in *StatusError) DeepCopy() *StatusError { + if in == nil { + return nil + } + out := new(StatusError) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Trigger) DeepCopyInto(out *Trigger) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Trigger. +func (in *Trigger) DeepCopy() *Trigger { + if in == nil { + return nil + } + out := new(Trigger) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Trigger) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerBinding) DeepCopyInto(out *TriggerBinding) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerBinding. +func (in *TriggerBinding) DeepCopy() *TriggerBinding { + if in == nil { + return nil + } + out := new(TriggerBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TriggerBinding) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerBindingList) DeepCopyInto(out *TriggerBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]TriggerBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerBindingList. +func (in *TriggerBindingList) DeepCopy() *TriggerBindingList { + if in == nil { + return nil + } + out := new(TriggerBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TriggerBindingList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerBindingSpec) DeepCopyInto(out *TriggerBindingSpec) { + *out = *in + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]Param, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerBindingSpec. +func (in *TriggerBindingSpec) DeepCopy() *TriggerBindingSpec { + if in == nil { + return nil + } + out := new(TriggerBindingSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerBindingStatus) DeepCopyInto(out *TriggerBindingStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerBindingStatus. +func (in *TriggerBindingStatus) DeepCopy() *TriggerBindingStatus { + if in == nil { + return nil + } + out := new(TriggerBindingStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerContext) DeepCopyInto(out *TriggerContext) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerContext. +func (in *TriggerContext) DeepCopy() *TriggerContext { + if in == nil { + return nil + } + out := new(TriggerContext) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerInterceptor) DeepCopyInto(out *TriggerInterceptor) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } + out.Ref = in.Ref + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]InterceptorParams, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Webhook != nil { + in, out := &in.Webhook, &out.Webhook + *out = new(WebhookInterceptor) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerInterceptor. +func (in *TriggerInterceptor) DeepCopy() *TriggerInterceptor { + if in == nil { + return nil + } + out := new(TriggerInterceptor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerList) DeepCopyInto(out *TriggerList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Trigger, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerList. +func (in *TriggerList) DeepCopy() *TriggerList { + if in == nil { + return nil + } + out := new(TriggerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TriggerList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerResourceTemplate) DeepCopyInto(out *TriggerResourceTemplate) { + *out = *in + in.RawExtension.DeepCopyInto(&out.RawExtension) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerResourceTemplate. +func (in *TriggerResourceTemplate) DeepCopy() *TriggerResourceTemplate { + if in == nil { + return nil + } + out := new(TriggerResourceTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerSpec) DeepCopyInto(out *TriggerSpec) { + *out = *in + if in.Bindings != nil { + in, out := &in.Bindings, &out.Bindings + *out = make([]*TriggerSpecBinding, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TriggerSpecBinding) + (*in).DeepCopyInto(*out) + } + } + } + in.Template.DeepCopyInto(&out.Template) + if in.Interceptors != nil { + in, out := &in.Interceptors, &out.Interceptors + *out = make([]*TriggerInterceptor, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TriggerInterceptor) + (*in).DeepCopyInto(*out) + } + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerSpec. +func (in *TriggerSpec) DeepCopy() *TriggerSpec { + if in == nil { + return nil + } + out := new(TriggerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerSpecBinding) DeepCopyInto(out *TriggerSpecBinding) { + *out = *in + if in.Value != nil { + in, out := &in.Value, &out.Value + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerSpecBinding. +func (in *TriggerSpecBinding) DeepCopy() *TriggerSpecBinding { + if in == nil { + return nil + } + out := new(TriggerSpecBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerSpecTemplate) DeepCopyInto(out *TriggerSpecTemplate) { + *out = *in + if in.Ref != nil { + in, out := &in.Ref, &out.Ref + *out = new(string) + **out = **in + } + if in.Spec != nil { + in, out := &in.Spec, &out.Spec + *out = new(TriggerTemplateSpec) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerSpecTemplate. +func (in *TriggerSpecTemplate) DeepCopy() *TriggerSpecTemplate { + if in == nil { + return nil + } + out := new(TriggerSpecTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerTemplate) DeepCopyInto(out *TriggerTemplate) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerTemplate. +func (in *TriggerTemplate) DeepCopy() *TriggerTemplate { + if in == nil { + return nil + } + out := new(TriggerTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TriggerTemplate) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerTemplateList) DeepCopyInto(out *TriggerTemplateList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]TriggerTemplate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerTemplateList. +func (in *TriggerTemplateList) DeepCopy() *TriggerTemplateList { + if in == nil { + return nil + } + out := new(TriggerTemplateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TriggerTemplateList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerTemplateSpec) DeepCopyInto(out *TriggerTemplateSpec) { + *out = *in + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]ParamSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ResourceTemplates != nil { + in, out := &in.ResourceTemplates, &out.ResourceTemplates + *out = make([]TriggerResourceTemplate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerTemplateSpec. +func (in *TriggerTemplateSpec) DeepCopy() *TriggerTemplateSpec { + if in == nil { + return nil + } + out := new(TriggerTemplateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerTemplateStatus) DeepCopyInto(out *TriggerTemplateStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerTemplateStatus. +func (in *TriggerTemplateStatus) DeepCopy() *TriggerTemplateStatus { + if in == nil { + return nil + } + out := new(TriggerTemplateStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebhookInterceptor) DeepCopyInto(out *WebhookInterceptor) { + *out = *in + if in.ObjectRef != nil { + in, out := &in.ObjectRef, &out.ObjectRef + *out = new(corev1.ObjectReference) + **out = **in + } + if in.URL != nil { + in, out := &in.URL, &out.URL + *out = new(apis.URL) + (*in).DeepCopyInto(*out) + } + if in.Header != nil { + in, out := &in.Header, &out.Header + *out = make([]pipelinev1beta1.Param, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookInterceptor. +func (in *WebhookInterceptor) DeepCopy() *WebhookInterceptor { + if in == nil { + return nil + } + out := new(WebhookInterceptor) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/validation.go b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/validation.go new file mode 100644 index 0000000000..58c7f0e36d --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/apis/triggers/validation.go @@ -0,0 +1,39 @@ +/* +Copyright 2021 The Tekton Authors + +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. +*/ + +package triggers + +import ( + "fmt" + + "knative.dev/pkg/apis" +) + +const ( + PayloadValidationAnnotation = "tekton.dev/payload-validation" +) + +func ValidateAnnotations(annotations map[string]string) *apis.FieldError { + var errs *apis.FieldError + + if value, ok := annotations[PayloadValidationAnnotation]; ok { + if value != "true" && value != "false" { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s annotation must have value 'true' or 'false'", PayloadValidationAnnotation), "metadata.annotations")) + } + } + + return errs +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/clientset.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/clientset.go new file mode 100644 index 0000000000..47bd4716ae --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/clientset.go @@ -0,0 +1,111 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package versioned + +import ( + "fmt" + + triggersv1alpha1 "github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1" + triggersv1beta1 "github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1" + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + TriggersV1alpha1() triggersv1alpha1.TriggersV1alpha1Interface + TriggersV1beta1() triggersv1beta1.TriggersV1beta1Interface +} + +// Clientset contains the clients for groups. Each group has exactly one +// version included in a Clientset. +type Clientset struct { + *discovery.DiscoveryClient + triggersV1alpha1 *triggersv1alpha1.TriggersV1alpha1Client + triggersV1beta1 *triggersv1beta1.TriggersV1beta1Client +} + +// TriggersV1alpha1 retrieves the TriggersV1alpha1Client +func (c *Clientset) TriggersV1alpha1() triggersv1alpha1.TriggersV1alpha1Interface { + return c.triggersV1alpha1 +} + +// TriggersV1beta1 retrieves the TriggersV1beta1Client +func (c *Clientset) TriggersV1beta1() triggersv1beta1.TriggersV1beta1Interface { + return c.triggersV1beta1 +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +// If config's RateLimiter is not set and QPS and Burst are acceptable, +// NewForConfig will generate a rate-limiter in configShallowCopy. +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + if configShallowCopy.Burst <= 0 { + return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") + } + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + var cs Clientset + var err error + cs.triggersV1alpha1, err = triggersv1alpha1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + cs.triggersV1beta1, err = triggersv1beta1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + var cs Clientset + cs.triggersV1alpha1 = triggersv1alpha1.NewForConfigOrDie(c) + cs.triggersV1beta1 = triggersv1beta1.NewForConfigOrDie(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) + return &cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.triggersV1alpha1 = triggersv1alpha1.New(c) + cs.triggersV1beta1 = triggersv1beta1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/doc.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/doc.go new file mode 100644 index 0000000000..c166b8dc10 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated clientset. +package versioned diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme/doc.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme/doc.go new file mode 100644 index 0000000000..c543eeb57c --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package contains the scheme of the automatically generated clientset. +package scheme diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme/register.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme/register.go new file mode 100644 index 0000000000..b3ba74848a --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme/register.go @@ -0,0 +1,58 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package scheme + +import ( + triggersv1alpha1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1" + triggersv1beta1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var Scheme = runtime.NewScheme() +var Codecs = serializer.NewCodecFactory(Scheme) +var ParameterCodec = runtime.NewParameterCodec(Scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + triggersv1alpha1.AddToScheme, + triggersv1beta1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(Scheme)) +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/clusterinterceptor.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/clusterinterceptor.go new file mode 100644 index 0000000000..312d0d9b5a --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/clusterinterceptor.go @@ -0,0 +1,184 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1" + scheme "github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ClusterInterceptorsGetter has a method to return a ClusterInterceptorInterface. +// A group's client should implement this interface. +type ClusterInterceptorsGetter interface { + ClusterInterceptors() ClusterInterceptorInterface +} + +// ClusterInterceptorInterface has methods to work with ClusterInterceptor resources. +type ClusterInterceptorInterface interface { + Create(ctx context.Context, clusterInterceptor *v1alpha1.ClusterInterceptor, opts v1.CreateOptions) (*v1alpha1.ClusterInterceptor, error) + Update(ctx context.Context, clusterInterceptor *v1alpha1.ClusterInterceptor, opts v1.UpdateOptions) (*v1alpha1.ClusterInterceptor, error) + UpdateStatus(ctx context.Context, clusterInterceptor *v1alpha1.ClusterInterceptor, opts v1.UpdateOptions) (*v1alpha1.ClusterInterceptor, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ClusterInterceptor, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ClusterInterceptorList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterInterceptor, err error) + ClusterInterceptorExpansion +} + +// clusterInterceptors implements ClusterInterceptorInterface +type clusterInterceptors struct { + client rest.Interface +} + +// newClusterInterceptors returns a ClusterInterceptors +func newClusterInterceptors(c *TriggersV1alpha1Client) *clusterInterceptors { + return &clusterInterceptors{ + client: c.RESTClient(), + } +} + +// Get takes name of the clusterInterceptor, and returns the corresponding clusterInterceptor object, and an error if there is any. +func (c *clusterInterceptors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterInterceptor, err error) { + result = &v1alpha1.ClusterInterceptor{} + err = c.client.Get(). + Resource("clusterinterceptors"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ClusterInterceptors that match those selectors. +func (c *clusterInterceptors) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ClusterInterceptorList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.ClusterInterceptorList{} + err = c.client.Get(). + Resource("clusterinterceptors"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested clusterInterceptors. +func (c *clusterInterceptors) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("clusterinterceptors"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a clusterInterceptor and creates it. Returns the server's representation of the clusterInterceptor, and an error, if there is any. +func (c *clusterInterceptors) Create(ctx context.Context, clusterInterceptor *v1alpha1.ClusterInterceptor, opts v1.CreateOptions) (result *v1alpha1.ClusterInterceptor, err error) { + result = &v1alpha1.ClusterInterceptor{} + err = c.client.Post(). + Resource("clusterinterceptors"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterInterceptor). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a clusterInterceptor and updates it. Returns the server's representation of the clusterInterceptor, and an error, if there is any. +func (c *clusterInterceptors) Update(ctx context.Context, clusterInterceptor *v1alpha1.ClusterInterceptor, opts v1.UpdateOptions) (result *v1alpha1.ClusterInterceptor, err error) { + result = &v1alpha1.ClusterInterceptor{} + err = c.client.Put(). + Resource("clusterinterceptors"). + Name(clusterInterceptor.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterInterceptor). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *clusterInterceptors) UpdateStatus(ctx context.Context, clusterInterceptor *v1alpha1.ClusterInterceptor, opts v1.UpdateOptions) (result *v1alpha1.ClusterInterceptor, err error) { + result = &v1alpha1.ClusterInterceptor{} + err = c.client.Put(). + Resource("clusterinterceptors"). + Name(clusterInterceptor.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterInterceptor). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the clusterInterceptor and deletes it. Returns an error if one occurs. +func (c *clusterInterceptors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Resource("clusterinterceptors"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *clusterInterceptors) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("clusterinterceptors"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched clusterInterceptor. +func (c *clusterInterceptors) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterInterceptor, err error) { + result = &v1alpha1.ClusterInterceptor{} + err = c.client.Patch(pt). + Resource("clusterinterceptors"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/clustertriggerbinding.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/clustertriggerbinding.go new file mode 100644 index 0000000000..2d7df16612 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/clustertriggerbinding.go @@ -0,0 +1,184 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1" + scheme "github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ClusterTriggerBindingsGetter has a method to return a ClusterTriggerBindingInterface. +// A group's client should implement this interface. +type ClusterTriggerBindingsGetter interface { + ClusterTriggerBindings() ClusterTriggerBindingInterface +} + +// ClusterTriggerBindingInterface has methods to work with ClusterTriggerBinding resources. +type ClusterTriggerBindingInterface interface { + Create(ctx context.Context, clusterTriggerBinding *v1alpha1.ClusterTriggerBinding, opts v1.CreateOptions) (*v1alpha1.ClusterTriggerBinding, error) + Update(ctx context.Context, clusterTriggerBinding *v1alpha1.ClusterTriggerBinding, opts v1.UpdateOptions) (*v1alpha1.ClusterTriggerBinding, error) + UpdateStatus(ctx context.Context, clusterTriggerBinding *v1alpha1.ClusterTriggerBinding, opts v1.UpdateOptions) (*v1alpha1.ClusterTriggerBinding, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ClusterTriggerBinding, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ClusterTriggerBindingList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterTriggerBinding, err error) + ClusterTriggerBindingExpansion +} + +// clusterTriggerBindings implements ClusterTriggerBindingInterface +type clusterTriggerBindings struct { + client rest.Interface +} + +// newClusterTriggerBindings returns a ClusterTriggerBindings +func newClusterTriggerBindings(c *TriggersV1alpha1Client) *clusterTriggerBindings { + return &clusterTriggerBindings{ + client: c.RESTClient(), + } +} + +// Get takes name of the clusterTriggerBinding, and returns the corresponding clusterTriggerBinding object, and an error if there is any. +func (c *clusterTriggerBindings) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterTriggerBinding, err error) { + result = &v1alpha1.ClusterTriggerBinding{} + err = c.client.Get(). + Resource("clustertriggerbindings"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ClusterTriggerBindings that match those selectors. +func (c *clusterTriggerBindings) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ClusterTriggerBindingList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.ClusterTriggerBindingList{} + err = c.client.Get(). + Resource("clustertriggerbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested clusterTriggerBindings. +func (c *clusterTriggerBindings) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("clustertriggerbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a clusterTriggerBinding and creates it. Returns the server's representation of the clusterTriggerBinding, and an error, if there is any. +func (c *clusterTriggerBindings) Create(ctx context.Context, clusterTriggerBinding *v1alpha1.ClusterTriggerBinding, opts v1.CreateOptions) (result *v1alpha1.ClusterTriggerBinding, err error) { + result = &v1alpha1.ClusterTriggerBinding{} + err = c.client.Post(). + Resource("clustertriggerbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterTriggerBinding). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a clusterTriggerBinding and updates it. Returns the server's representation of the clusterTriggerBinding, and an error, if there is any. +func (c *clusterTriggerBindings) Update(ctx context.Context, clusterTriggerBinding *v1alpha1.ClusterTriggerBinding, opts v1.UpdateOptions) (result *v1alpha1.ClusterTriggerBinding, err error) { + result = &v1alpha1.ClusterTriggerBinding{} + err = c.client.Put(). + Resource("clustertriggerbindings"). + Name(clusterTriggerBinding.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterTriggerBinding). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *clusterTriggerBindings) UpdateStatus(ctx context.Context, clusterTriggerBinding *v1alpha1.ClusterTriggerBinding, opts v1.UpdateOptions) (result *v1alpha1.ClusterTriggerBinding, err error) { + result = &v1alpha1.ClusterTriggerBinding{} + err = c.client.Put(). + Resource("clustertriggerbindings"). + Name(clusterTriggerBinding.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterTriggerBinding). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the clusterTriggerBinding and deletes it. Returns an error if one occurs. +func (c *clusterTriggerBindings) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Resource("clustertriggerbindings"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *clusterTriggerBindings) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("clustertriggerbindings"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched clusterTriggerBinding. +func (c *clusterTriggerBindings) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterTriggerBinding, err error) { + result = &v1alpha1.ClusterTriggerBinding{} + err = c.client.Patch(pt). + Resource("clustertriggerbindings"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/doc.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/doc.go new file mode 100644 index 0000000000..8151c2ea65 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/eventlistener.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/eventlistener.go new file mode 100644 index 0000000000..80abe22dff --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/eventlistener.go @@ -0,0 +1,195 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1" + scheme "github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// EventListenersGetter has a method to return a EventListenerInterface. +// A group's client should implement this interface. +type EventListenersGetter interface { + EventListeners(namespace string) EventListenerInterface +} + +// EventListenerInterface has methods to work with EventListener resources. +type EventListenerInterface interface { + Create(ctx context.Context, eventListener *v1alpha1.EventListener, opts v1.CreateOptions) (*v1alpha1.EventListener, error) + Update(ctx context.Context, eventListener *v1alpha1.EventListener, opts v1.UpdateOptions) (*v1alpha1.EventListener, error) + UpdateStatus(ctx context.Context, eventListener *v1alpha1.EventListener, opts v1.UpdateOptions) (*v1alpha1.EventListener, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.EventListener, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.EventListenerList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.EventListener, err error) + EventListenerExpansion +} + +// eventListeners implements EventListenerInterface +type eventListeners struct { + client rest.Interface + ns string +} + +// newEventListeners returns a EventListeners +func newEventListeners(c *TriggersV1alpha1Client, namespace string) *eventListeners { + return &eventListeners{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the eventListener, and returns the corresponding eventListener object, and an error if there is any. +func (c *eventListeners) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.EventListener, err error) { + result = &v1alpha1.EventListener{} + err = c.client.Get(). + Namespace(c.ns). + Resource("eventlisteners"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of EventListeners that match those selectors. +func (c *eventListeners) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.EventListenerList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.EventListenerList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("eventlisteners"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested eventListeners. +func (c *eventListeners) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("eventlisteners"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a eventListener and creates it. Returns the server's representation of the eventListener, and an error, if there is any. +func (c *eventListeners) Create(ctx context.Context, eventListener *v1alpha1.EventListener, opts v1.CreateOptions) (result *v1alpha1.EventListener, err error) { + result = &v1alpha1.EventListener{} + err = c.client.Post(). + Namespace(c.ns). + Resource("eventlisteners"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(eventListener). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a eventListener and updates it. Returns the server's representation of the eventListener, and an error, if there is any. +func (c *eventListeners) Update(ctx context.Context, eventListener *v1alpha1.EventListener, opts v1.UpdateOptions) (result *v1alpha1.EventListener, err error) { + result = &v1alpha1.EventListener{} + err = c.client.Put(). + Namespace(c.ns). + Resource("eventlisteners"). + Name(eventListener.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(eventListener). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *eventListeners) UpdateStatus(ctx context.Context, eventListener *v1alpha1.EventListener, opts v1.UpdateOptions) (result *v1alpha1.EventListener, err error) { + result = &v1alpha1.EventListener{} + err = c.client.Put(). + Namespace(c.ns). + Resource("eventlisteners"). + Name(eventListener.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(eventListener). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the eventListener and deletes it. Returns an error if one occurs. +func (c *eventListeners) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("eventlisteners"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *eventListeners) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("eventlisteners"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched eventListener. +func (c *eventListeners) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.EventListener, err error) { + result = &v1alpha1.EventListener{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("eventlisteners"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/generated_expansion.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/generated_expansion.go new file mode 100644 index 0000000000..c7ab02c6f0 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/generated_expansion.go @@ -0,0 +1,31 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +type ClusterInterceptorExpansion interface{} + +type ClusterTriggerBindingExpansion interface{} + +type EventListenerExpansion interface{} + +type TriggerExpansion interface{} + +type TriggerBindingExpansion interface{} + +type TriggerTemplateExpansion interface{} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/trigger.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/trigger.go new file mode 100644 index 0000000000..d3c83838eb --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/trigger.go @@ -0,0 +1,178 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1" + scheme "github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// TriggersGetter has a method to return a TriggerInterface. +// A group's client should implement this interface. +type TriggersGetter interface { + Triggers(namespace string) TriggerInterface +} + +// TriggerInterface has methods to work with Trigger resources. +type TriggerInterface interface { + Create(ctx context.Context, trigger *v1alpha1.Trigger, opts v1.CreateOptions) (*v1alpha1.Trigger, error) + Update(ctx context.Context, trigger *v1alpha1.Trigger, opts v1.UpdateOptions) (*v1alpha1.Trigger, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.Trigger, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.TriggerList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Trigger, err error) + TriggerExpansion +} + +// triggers implements TriggerInterface +type triggers struct { + client rest.Interface + ns string +} + +// newTriggers returns a Triggers +func newTriggers(c *TriggersV1alpha1Client, namespace string) *triggers { + return &triggers{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the trigger, and returns the corresponding trigger object, and an error if there is any. +func (c *triggers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Trigger, err error) { + result = &v1alpha1.Trigger{} + err = c.client.Get(). + Namespace(c.ns). + Resource("triggers"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Triggers that match those selectors. +func (c *triggers) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.TriggerList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.TriggerList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("triggers"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested triggers. +func (c *triggers) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("triggers"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a trigger and creates it. Returns the server's representation of the trigger, and an error, if there is any. +func (c *triggers) Create(ctx context.Context, trigger *v1alpha1.Trigger, opts v1.CreateOptions) (result *v1alpha1.Trigger, err error) { + result = &v1alpha1.Trigger{} + err = c.client.Post(). + Namespace(c.ns). + Resource("triggers"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(trigger). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a trigger and updates it. Returns the server's representation of the trigger, and an error, if there is any. +func (c *triggers) Update(ctx context.Context, trigger *v1alpha1.Trigger, opts v1.UpdateOptions) (result *v1alpha1.Trigger, err error) { + result = &v1alpha1.Trigger{} + err = c.client.Put(). + Namespace(c.ns). + Resource("triggers"). + Name(trigger.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(trigger). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the trigger and deletes it. Returns an error if one occurs. +func (c *triggers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("triggers"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *triggers) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("triggers"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched trigger. +func (c *triggers) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Trigger, err error) { + result = &v1alpha1.Trigger{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("triggers"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/triggerbinding.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/triggerbinding.go new file mode 100644 index 0000000000..3257960f02 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/triggerbinding.go @@ -0,0 +1,195 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1" + scheme "github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// TriggerBindingsGetter has a method to return a TriggerBindingInterface. +// A group's client should implement this interface. +type TriggerBindingsGetter interface { + TriggerBindings(namespace string) TriggerBindingInterface +} + +// TriggerBindingInterface has methods to work with TriggerBinding resources. +type TriggerBindingInterface interface { + Create(ctx context.Context, triggerBinding *v1alpha1.TriggerBinding, opts v1.CreateOptions) (*v1alpha1.TriggerBinding, error) + Update(ctx context.Context, triggerBinding *v1alpha1.TriggerBinding, opts v1.UpdateOptions) (*v1alpha1.TriggerBinding, error) + UpdateStatus(ctx context.Context, triggerBinding *v1alpha1.TriggerBinding, opts v1.UpdateOptions) (*v1alpha1.TriggerBinding, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.TriggerBinding, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.TriggerBindingList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TriggerBinding, err error) + TriggerBindingExpansion +} + +// triggerBindings implements TriggerBindingInterface +type triggerBindings struct { + client rest.Interface + ns string +} + +// newTriggerBindings returns a TriggerBindings +func newTriggerBindings(c *TriggersV1alpha1Client, namespace string) *triggerBindings { + return &triggerBindings{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the triggerBinding, and returns the corresponding triggerBinding object, and an error if there is any. +func (c *triggerBindings) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.TriggerBinding, err error) { + result = &v1alpha1.TriggerBinding{} + err = c.client.Get(). + Namespace(c.ns). + Resource("triggerbindings"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of TriggerBindings that match those selectors. +func (c *triggerBindings) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.TriggerBindingList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.TriggerBindingList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("triggerbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested triggerBindings. +func (c *triggerBindings) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("triggerbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a triggerBinding and creates it. Returns the server's representation of the triggerBinding, and an error, if there is any. +func (c *triggerBindings) Create(ctx context.Context, triggerBinding *v1alpha1.TriggerBinding, opts v1.CreateOptions) (result *v1alpha1.TriggerBinding, err error) { + result = &v1alpha1.TriggerBinding{} + err = c.client.Post(). + Namespace(c.ns). + Resource("triggerbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(triggerBinding). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a triggerBinding and updates it. Returns the server's representation of the triggerBinding, and an error, if there is any. +func (c *triggerBindings) Update(ctx context.Context, triggerBinding *v1alpha1.TriggerBinding, opts v1.UpdateOptions) (result *v1alpha1.TriggerBinding, err error) { + result = &v1alpha1.TriggerBinding{} + err = c.client.Put(). + Namespace(c.ns). + Resource("triggerbindings"). + Name(triggerBinding.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(triggerBinding). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *triggerBindings) UpdateStatus(ctx context.Context, triggerBinding *v1alpha1.TriggerBinding, opts v1.UpdateOptions) (result *v1alpha1.TriggerBinding, err error) { + result = &v1alpha1.TriggerBinding{} + err = c.client.Put(). + Namespace(c.ns). + Resource("triggerbindings"). + Name(triggerBinding.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(triggerBinding). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the triggerBinding and deletes it. Returns an error if one occurs. +func (c *triggerBindings) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("triggerbindings"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *triggerBindings) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("triggerbindings"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched triggerBinding. +func (c *triggerBindings) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TriggerBinding, err error) { + result = &v1alpha1.TriggerBinding{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("triggerbindings"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/triggers_client.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/triggers_client.go new file mode 100644 index 0000000000..f9c007c797 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/triggers_client.go @@ -0,0 +1,114 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1" + "github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme" + rest "k8s.io/client-go/rest" +) + +type TriggersV1alpha1Interface interface { + RESTClient() rest.Interface + ClusterInterceptorsGetter + ClusterTriggerBindingsGetter + EventListenersGetter + TriggersGetter + TriggerBindingsGetter + TriggerTemplatesGetter +} + +// TriggersV1alpha1Client is used to interact with features provided by the triggers.tekton.dev group. +type TriggersV1alpha1Client struct { + restClient rest.Interface +} + +func (c *TriggersV1alpha1Client) ClusterInterceptors() ClusterInterceptorInterface { + return newClusterInterceptors(c) +} + +func (c *TriggersV1alpha1Client) ClusterTriggerBindings() ClusterTriggerBindingInterface { + return newClusterTriggerBindings(c) +} + +func (c *TriggersV1alpha1Client) EventListeners(namespace string) EventListenerInterface { + return newEventListeners(c, namespace) +} + +func (c *TriggersV1alpha1Client) Triggers(namespace string) TriggerInterface { + return newTriggers(c, namespace) +} + +func (c *TriggersV1alpha1Client) TriggerBindings(namespace string) TriggerBindingInterface { + return newTriggerBindings(c, namespace) +} + +func (c *TriggersV1alpha1Client) TriggerTemplates(namespace string) TriggerTemplateInterface { + return newTriggerTemplates(c, namespace) +} + +// NewForConfig creates a new TriggersV1alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*TriggersV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &TriggersV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new TriggersV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *TriggersV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new TriggersV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *TriggersV1alpha1Client { + return &TriggersV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *TriggersV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/triggertemplate.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/triggertemplate.go new file mode 100644 index 0000000000..7632675de6 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1alpha1/triggertemplate.go @@ -0,0 +1,195 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1" + scheme "github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// TriggerTemplatesGetter has a method to return a TriggerTemplateInterface. +// A group's client should implement this interface. +type TriggerTemplatesGetter interface { + TriggerTemplates(namespace string) TriggerTemplateInterface +} + +// TriggerTemplateInterface has methods to work with TriggerTemplate resources. +type TriggerTemplateInterface interface { + Create(ctx context.Context, triggerTemplate *v1alpha1.TriggerTemplate, opts v1.CreateOptions) (*v1alpha1.TriggerTemplate, error) + Update(ctx context.Context, triggerTemplate *v1alpha1.TriggerTemplate, opts v1.UpdateOptions) (*v1alpha1.TriggerTemplate, error) + UpdateStatus(ctx context.Context, triggerTemplate *v1alpha1.TriggerTemplate, opts v1.UpdateOptions) (*v1alpha1.TriggerTemplate, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.TriggerTemplate, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.TriggerTemplateList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TriggerTemplate, err error) + TriggerTemplateExpansion +} + +// triggerTemplates implements TriggerTemplateInterface +type triggerTemplates struct { + client rest.Interface + ns string +} + +// newTriggerTemplates returns a TriggerTemplates +func newTriggerTemplates(c *TriggersV1alpha1Client, namespace string) *triggerTemplates { + return &triggerTemplates{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the triggerTemplate, and returns the corresponding triggerTemplate object, and an error if there is any. +func (c *triggerTemplates) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.TriggerTemplate, err error) { + result = &v1alpha1.TriggerTemplate{} + err = c.client.Get(). + Namespace(c.ns). + Resource("triggertemplates"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of TriggerTemplates that match those selectors. +func (c *triggerTemplates) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.TriggerTemplateList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.TriggerTemplateList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("triggertemplates"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested triggerTemplates. +func (c *triggerTemplates) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("triggertemplates"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a triggerTemplate and creates it. Returns the server's representation of the triggerTemplate, and an error, if there is any. +func (c *triggerTemplates) Create(ctx context.Context, triggerTemplate *v1alpha1.TriggerTemplate, opts v1.CreateOptions) (result *v1alpha1.TriggerTemplate, err error) { + result = &v1alpha1.TriggerTemplate{} + err = c.client.Post(). + Namespace(c.ns). + Resource("triggertemplates"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(triggerTemplate). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a triggerTemplate and updates it. Returns the server's representation of the triggerTemplate, and an error, if there is any. +func (c *triggerTemplates) Update(ctx context.Context, triggerTemplate *v1alpha1.TriggerTemplate, opts v1.UpdateOptions) (result *v1alpha1.TriggerTemplate, err error) { + result = &v1alpha1.TriggerTemplate{} + err = c.client.Put(). + Namespace(c.ns). + Resource("triggertemplates"). + Name(triggerTemplate.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(triggerTemplate). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *triggerTemplates) UpdateStatus(ctx context.Context, triggerTemplate *v1alpha1.TriggerTemplate, opts v1.UpdateOptions) (result *v1alpha1.TriggerTemplate, err error) { + result = &v1alpha1.TriggerTemplate{} + err = c.client.Put(). + Namespace(c.ns). + Resource("triggertemplates"). + Name(triggerTemplate.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(triggerTemplate). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the triggerTemplate and deletes it. Returns an error if one occurs. +func (c *triggerTemplates) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("triggertemplates"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *triggerTemplates) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("triggertemplates"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched triggerTemplate. +func (c *triggerTemplates) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TriggerTemplate, err error) { + result = &v1alpha1.TriggerTemplate{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("triggertemplates"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/clustertriggerbinding.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/clustertriggerbinding.go new file mode 100644 index 0000000000..6b93992c39 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/clustertriggerbinding.go @@ -0,0 +1,184 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + "time" + + v1beta1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1" + scheme "github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ClusterTriggerBindingsGetter has a method to return a ClusterTriggerBindingInterface. +// A group's client should implement this interface. +type ClusterTriggerBindingsGetter interface { + ClusterTriggerBindings() ClusterTriggerBindingInterface +} + +// ClusterTriggerBindingInterface has methods to work with ClusterTriggerBinding resources. +type ClusterTriggerBindingInterface interface { + Create(ctx context.Context, clusterTriggerBinding *v1beta1.ClusterTriggerBinding, opts v1.CreateOptions) (*v1beta1.ClusterTriggerBinding, error) + Update(ctx context.Context, clusterTriggerBinding *v1beta1.ClusterTriggerBinding, opts v1.UpdateOptions) (*v1beta1.ClusterTriggerBinding, error) + UpdateStatus(ctx context.Context, clusterTriggerBinding *v1beta1.ClusterTriggerBinding, opts v1.UpdateOptions) (*v1beta1.ClusterTriggerBinding, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.ClusterTriggerBinding, error) + List(ctx context.Context, opts v1.ListOptions) (*v1beta1.ClusterTriggerBindingList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.ClusterTriggerBinding, err error) + ClusterTriggerBindingExpansion +} + +// clusterTriggerBindings implements ClusterTriggerBindingInterface +type clusterTriggerBindings struct { + client rest.Interface +} + +// newClusterTriggerBindings returns a ClusterTriggerBindings +func newClusterTriggerBindings(c *TriggersV1beta1Client) *clusterTriggerBindings { + return &clusterTriggerBindings{ + client: c.RESTClient(), + } +} + +// Get takes name of the clusterTriggerBinding, and returns the corresponding clusterTriggerBinding object, and an error if there is any. +func (c *clusterTriggerBindings) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.ClusterTriggerBinding, err error) { + result = &v1beta1.ClusterTriggerBinding{} + err = c.client.Get(). + Resource("clustertriggerbindings"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ClusterTriggerBindings that match those selectors. +func (c *clusterTriggerBindings) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.ClusterTriggerBindingList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1beta1.ClusterTriggerBindingList{} + err = c.client.Get(). + Resource("clustertriggerbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested clusterTriggerBindings. +func (c *clusterTriggerBindings) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("clustertriggerbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a clusterTriggerBinding and creates it. Returns the server's representation of the clusterTriggerBinding, and an error, if there is any. +func (c *clusterTriggerBindings) Create(ctx context.Context, clusterTriggerBinding *v1beta1.ClusterTriggerBinding, opts v1.CreateOptions) (result *v1beta1.ClusterTriggerBinding, err error) { + result = &v1beta1.ClusterTriggerBinding{} + err = c.client.Post(). + Resource("clustertriggerbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterTriggerBinding). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a clusterTriggerBinding and updates it. Returns the server's representation of the clusterTriggerBinding, and an error, if there is any. +func (c *clusterTriggerBindings) Update(ctx context.Context, clusterTriggerBinding *v1beta1.ClusterTriggerBinding, opts v1.UpdateOptions) (result *v1beta1.ClusterTriggerBinding, err error) { + result = &v1beta1.ClusterTriggerBinding{} + err = c.client.Put(). + Resource("clustertriggerbindings"). + Name(clusterTriggerBinding.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterTriggerBinding). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *clusterTriggerBindings) UpdateStatus(ctx context.Context, clusterTriggerBinding *v1beta1.ClusterTriggerBinding, opts v1.UpdateOptions) (result *v1beta1.ClusterTriggerBinding, err error) { + result = &v1beta1.ClusterTriggerBinding{} + err = c.client.Put(). + Resource("clustertriggerbindings"). + Name(clusterTriggerBinding.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterTriggerBinding). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the clusterTriggerBinding and deletes it. Returns an error if one occurs. +func (c *clusterTriggerBindings) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Resource("clustertriggerbindings"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *clusterTriggerBindings) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("clustertriggerbindings"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched clusterTriggerBinding. +func (c *clusterTriggerBindings) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.ClusterTriggerBinding, err error) { + result = &v1beta1.ClusterTriggerBinding{} + err = c.client.Patch(pt). + Resource("clustertriggerbindings"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/doc.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/doc.go new file mode 100644 index 0000000000..ff6e6bc255 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1beta1 diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/eventlistener.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/eventlistener.go new file mode 100644 index 0000000000..41bdccafd1 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/eventlistener.go @@ -0,0 +1,195 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + "time" + + v1beta1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1" + scheme "github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// EventListenersGetter has a method to return a EventListenerInterface. +// A group's client should implement this interface. +type EventListenersGetter interface { + EventListeners(namespace string) EventListenerInterface +} + +// EventListenerInterface has methods to work with EventListener resources. +type EventListenerInterface interface { + Create(ctx context.Context, eventListener *v1beta1.EventListener, opts v1.CreateOptions) (*v1beta1.EventListener, error) + Update(ctx context.Context, eventListener *v1beta1.EventListener, opts v1.UpdateOptions) (*v1beta1.EventListener, error) + UpdateStatus(ctx context.Context, eventListener *v1beta1.EventListener, opts v1.UpdateOptions) (*v1beta1.EventListener, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.EventListener, error) + List(ctx context.Context, opts v1.ListOptions) (*v1beta1.EventListenerList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.EventListener, err error) + EventListenerExpansion +} + +// eventListeners implements EventListenerInterface +type eventListeners struct { + client rest.Interface + ns string +} + +// newEventListeners returns a EventListeners +func newEventListeners(c *TriggersV1beta1Client, namespace string) *eventListeners { + return &eventListeners{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the eventListener, and returns the corresponding eventListener object, and an error if there is any. +func (c *eventListeners) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.EventListener, err error) { + result = &v1beta1.EventListener{} + err = c.client.Get(). + Namespace(c.ns). + Resource("eventlisteners"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of EventListeners that match those selectors. +func (c *eventListeners) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.EventListenerList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1beta1.EventListenerList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("eventlisteners"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested eventListeners. +func (c *eventListeners) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("eventlisteners"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a eventListener and creates it. Returns the server's representation of the eventListener, and an error, if there is any. +func (c *eventListeners) Create(ctx context.Context, eventListener *v1beta1.EventListener, opts v1.CreateOptions) (result *v1beta1.EventListener, err error) { + result = &v1beta1.EventListener{} + err = c.client.Post(). + Namespace(c.ns). + Resource("eventlisteners"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(eventListener). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a eventListener and updates it. Returns the server's representation of the eventListener, and an error, if there is any. +func (c *eventListeners) Update(ctx context.Context, eventListener *v1beta1.EventListener, opts v1.UpdateOptions) (result *v1beta1.EventListener, err error) { + result = &v1beta1.EventListener{} + err = c.client.Put(). + Namespace(c.ns). + Resource("eventlisteners"). + Name(eventListener.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(eventListener). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *eventListeners) UpdateStatus(ctx context.Context, eventListener *v1beta1.EventListener, opts v1.UpdateOptions) (result *v1beta1.EventListener, err error) { + result = &v1beta1.EventListener{} + err = c.client.Put(). + Namespace(c.ns). + Resource("eventlisteners"). + Name(eventListener.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(eventListener). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the eventListener and deletes it. Returns an error if one occurs. +func (c *eventListeners) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("eventlisteners"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *eventListeners) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("eventlisteners"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched eventListener. +func (c *eventListeners) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.EventListener, err error) { + result = &v1beta1.EventListener{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("eventlisteners"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/generated_expansion.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/generated_expansion.go new file mode 100644 index 0000000000..3a762d0915 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/generated_expansion.go @@ -0,0 +1,29 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +type ClusterTriggerBindingExpansion interface{} + +type EventListenerExpansion interface{} + +type TriggerExpansion interface{} + +type TriggerBindingExpansion interface{} + +type TriggerTemplateExpansion interface{} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/trigger.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/trigger.go new file mode 100644 index 0000000000..217b60e08f --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/trigger.go @@ -0,0 +1,178 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + "time" + + v1beta1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1" + scheme "github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// TriggersGetter has a method to return a TriggerInterface. +// A group's client should implement this interface. +type TriggersGetter interface { + Triggers(namespace string) TriggerInterface +} + +// TriggerInterface has methods to work with Trigger resources. +type TriggerInterface interface { + Create(ctx context.Context, trigger *v1beta1.Trigger, opts v1.CreateOptions) (*v1beta1.Trigger, error) + Update(ctx context.Context, trigger *v1beta1.Trigger, opts v1.UpdateOptions) (*v1beta1.Trigger, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.Trigger, error) + List(ctx context.Context, opts v1.ListOptions) (*v1beta1.TriggerList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.Trigger, err error) + TriggerExpansion +} + +// triggers implements TriggerInterface +type triggers struct { + client rest.Interface + ns string +} + +// newTriggers returns a Triggers +func newTriggers(c *TriggersV1beta1Client, namespace string) *triggers { + return &triggers{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the trigger, and returns the corresponding trigger object, and an error if there is any. +func (c *triggers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.Trigger, err error) { + result = &v1beta1.Trigger{} + err = c.client.Get(). + Namespace(c.ns). + Resource("triggers"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Triggers that match those selectors. +func (c *triggers) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.TriggerList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1beta1.TriggerList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("triggers"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested triggers. +func (c *triggers) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("triggers"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a trigger and creates it. Returns the server's representation of the trigger, and an error, if there is any. +func (c *triggers) Create(ctx context.Context, trigger *v1beta1.Trigger, opts v1.CreateOptions) (result *v1beta1.Trigger, err error) { + result = &v1beta1.Trigger{} + err = c.client.Post(). + Namespace(c.ns). + Resource("triggers"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(trigger). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a trigger and updates it. Returns the server's representation of the trigger, and an error, if there is any. +func (c *triggers) Update(ctx context.Context, trigger *v1beta1.Trigger, opts v1.UpdateOptions) (result *v1beta1.Trigger, err error) { + result = &v1beta1.Trigger{} + err = c.client.Put(). + Namespace(c.ns). + Resource("triggers"). + Name(trigger.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(trigger). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the trigger and deletes it. Returns an error if one occurs. +func (c *triggers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("triggers"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *triggers) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("triggers"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched trigger. +func (c *triggers) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.Trigger, err error) { + result = &v1beta1.Trigger{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("triggers"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/triggerbinding.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/triggerbinding.go new file mode 100644 index 0000000000..d67727b720 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/triggerbinding.go @@ -0,0 +1,195 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + "time" + + v1beta1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1" + scheme "github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// TriggerBindingsGetter has a method to return a TriggerBindingInterface. +// A group's client should implement this interface. +type TriggerBindingsGetter interface { + TriggerBindings(namespace string) TriggerBindingInterface +} + +// TriggerBindingInterface has methods to work with TriggerBinding resources. +type TriggerBindingInterface interface { + Create(ctx context.Context, triggerBinding *v1beta1.TriggerBinding, opts v1.CreateOptions) (*v1beta1.TriggerBinding, error) + Update(ctx context.Context, triggerBinding *v1beta1.TriggerBinding, opts v1.UpdateOptions) (*v1beta1.TriggerBinding, error) + UpdateStatus(ctx context.Context, triggerBinding *v1beta1.TriggerBinding, opts v1.UpdateOptions) (*v1beta1.TriggerBinding, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.TriggerBinding, error) + List(ctx context.Context, opts v1.ListOptions) (*v1beta1.TriggerBindingList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.TriggerBinding, err error) + TriggerBindingExpansion +} + +// triggerBindings implements TriggerBindingInterface +type triggerBindings struct { + client rest.Interface + ns string +} + +// newTriggerBindings returns a TriggerBindings +func newTriggerBindings(c *TriggersV1beta1Client, namespace string) *triggerBindings { + return &triggerBindings{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the triggerBinding, and returns the corresponding triggerBinding object, and an error if there is any. +func (c *triggerBindings) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.TriggerBinding, err error) { + result = &v1beta1.TriggerBinding{} + err = c.client.Get(). + Namespace(c.ns). + Resource("triggerbindings"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of TriggerBindings that match those selectors. +func (c *triggerBindings) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.TriggerBindingList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1beta1.TriggerBindingList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("triggerbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested triggerBindings. +func (c *triggerBindings) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("triggerbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a triggerBinding and creates it. Returns the server's representation of the triggerBinding, and an error, if there is any. +func (c *triggerBindings) Create(ctx context.Context, triggerBinding *v1beta1.TriggerBinding, opts v1.CreateOptions) (result *v1beta1.TriggerBinding, err error) { + result = &v1beta1.TriggerBinding{} + err = c.client.Post(). + Namespace(c.ns). + Resource("triggerbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(triggerBinding). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a triggerBinding and updates it. Returns the server's representation of the triggerBinding, and an error, if there is any. +func (c *triggerBindings) Update(ctx context.Context, triggerBinding *v1beta1.TriggerBinding, opts v1.UpdateOptions) (result *v1beta1.TriggerBinding, err error) { + result = &v1beta1.TriggerBinding{} + err = c.client.Put(). + Namespace(c.ns). + Resource("triggerbindings"). + Name(triggerBinding.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(triggerBinding). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *triggerBindings) UpdateStatus(ctx context.Context, triggerBinding *v1beta1.TriggerBinding, opts v1.UpdateOptions) (result *v1beta1.TriggerBinding, err error) { + result = &v1beta1.TriggerBinding{} + err = c.client.Put(). + Namespace(c.ns). + Resource("triggerbindings"). + Name(triggerBinding.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(triggerBinding). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the triggerBinding and deletes it. Returns an error if one occurs. +func (c *triggerBindings) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("triggerbindings"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *triggerBindings) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("triggerbindings"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched triggerBinding. +func (c *triggerBindings) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.TriggerBinding, err error) { + result = &v1beta1.TriggerBinding{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("triggerbindings"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/triggers_client.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/triggers_client.go new file mode 100644 index 0000000000..e15bd6e25f --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/triggers_client.go @@ -0,0 +1,109 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +import ( + v1beta1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1" + "github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme" + rest "k8s.io/client-go/rest" +) + +type TriggersV1beta1Interface interface { + RESTClient() rest.Interface + ClusterTriggerBindingsGetter + EventListenersGetter + TriggersGetter + TriggerBindingsGetter + TriggerTemplatesGetter +} + +// TriggersV1beta1Client is used to interact with features provided by the triggers.tekton.dev group. +type TriggersV1beta1Client struct { + restClient rest.Interface +} + +func (c *TriggersV1beta1Client) ClusterTriggerBindings() ClusterTriggerBindingInterface { + return newClusterTriggerBindings(c) +} + +func (c *TriggersV1beta1Client) EventListeners(namespace string) EventListenerInterface { + return newEventListeners(c, namespace) +} + +func (c *TriggersV1beta1Client) Triggers(namespace string) TriggerInterface { + return newTriggers(c, namespace) +} + +func (c *TriggersV1beta1Client) TriggerBindings(namespace string) TriggerBindingInterface { + return newTriggerBindings(c, namespace) +} + +func (c *TriggersV1beta1Client) TriggerTemplates(namespace string) TriggerTemplateInterface { + return newTriggerTemplates(c, namespace) +} + +// NewForConfig creates a new TriggersV1beta1Client for the given config. +func NewForConfig(c *rest.Config) (*TriggersV1beta1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &TriggersV1beta1Client{client}, nil +} + +// NewForConfigOrDie creates a new TriggersV1beta1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *TriggersV1beta1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new TriggersV1beta1Client for the given RESTClient. +func New(c rest.Interface) *TriggersV1beta1Client { + return &TriggersV1beta1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1beta1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *TriggersV1beta1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/triggertemplate.go b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/triggertemplate.go new file mode 100644 index 0000000000..a330099266 --- /dev/null +++ b/vendor/github.com/tektoncd/triggers/pkg/client/clientset/versioned/typed/triggers/v1beta1/triggertemplate.go @@ -0,0 +1,195 @@ +/* +Copyright 2019 The Tekton Authors + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + "time" + + v1beta1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1" + scheme "github.com/tektoncd/triggers/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// TriggerTemplatesGetter has a method to return a TriggerTemplateInterface. +// A group's client should implement this interface. +type TriggerTemplatesGetter interface { + TriggerTemplates(namespace string) TriggerTemplateInterface +} + +// TriggerTemplateInterface has methods to work with TriggerTemplate resources. +type TriggerTemplateInterface interface { + Create(ctx context.Context, triggerTemplate *v1beta1.TriggerTemplate, opts v1.CreateOptions) (*v1beta1.TriggerTemplate, error) + Update(ctx context.Context, triggerTemplate *v1beta1.TriggerTemplate, opts v1.UpdateOptions) (*v1beta1.TriggerTemplate, error) + UpdateStatus(ctx context.Context, triggerTemplate *v1beta1.TriggerTemplate, opts v1.UpdateOptions) (*v1beta1.TriggerTemplate, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.TriggerTemplate, error) + List(ctx context.Context, opts v1.ListOptions) (*v1beta1.TriggerTemplateList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.TriggerTemplate, err error) + TriggerTemplateExpansion +} + +// triggerTemplates implements TriggerTemplateInterface +type triggerTemplates struct { + client rest.Interface + ns string +} + +// newTriggerTemplates returns a TriggerTemplates +func newTriggerTemplates(c *TriggersV1beta1Client, namespace string) *triggerTemplates { + return &triggerTemplates{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the triggerTemplate, and returns the corresponding triggerTemplate object, and an error if there is any. +func (c *triggerTemplates) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.TriggerTemplate, err error) { + result = &v1beta1.TriggerTemplate{} + err = c.client.Get(). + Namespace(c.ns). + Resource("triggertemplates"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of TriggerTemplates that match those selectors. +func (c *triggerTemplates) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.TriggerTemplateList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1beta1.TriggerTemplateList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("triggertemplates"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested triggerTemplates. +func (c *triggerTemplates) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("triggertemplates"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a triggerTemplate and creates it. Returns the server's representation of the triggerTemplate, and an error, if there is any. +func (c *triggerTemplates) Create(ctx context.Context, triggerTemplate *v1beta1.TriggerTemplate, opts v1.CreateOptions) (result *v1beta1.TriggerTemplate, err error) { + result = &v1beta1.TriggerTemplate{} + err = c.client.Post(). + Namespace(c.ns). + Resource("triggertemplates"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(triggerTemplate). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a triggerTemplate and updates it. Returns the server's representation of the triggerTemplate, and an error, if there is any. +func (c *triggerTemplates) Update(ctx context.Context, triggerTemplate *v1beta1.TriggerTemplate, opts v1.UpdateOptions) (result *v1beta1.TriggerTemplate, err error) { + result = &v1beta1.TriggerTemplate{} + err = c.client.Put(). + Namespace(c.ns). + Resource("triggertemplates"). + Name(triggerTemplate.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(triggerTemplate). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *triggerTemplates) UpdateStatus(ctx context.Context, triggerTemplate *v1beta1.TriggerTemplate, opts v1.UpdateOptions) (result *v1beta1.TriggerTemplate, err error) { + result = &v1beta1.TriggerTemplate{} + err = c.client.Put(). + Namespace(c.ns). + Resource("triggertemplates"). + Name(triggerTemplate.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(triggerTemplate). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the triggerTemplate and deletes it. Returns an error if one occurs. +func (c *triggerTemplates) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("triggertemplates"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *triggerTemplates) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("triggertemplates"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched triggerTemplate. +func (c *triggerTemplates) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.TriggerTemplate, err error) { + result = &v1beta1.TriggerTemplate{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("triggertemplates"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/vendor/github.com/vbatts/tar-split/LICENSE b/vendor/github.com/vbatts/tar-split/LICENSE new file mode 100644 index 0000000000..ca03685b15 --- /dev/null +++ b/vendor/github.com/vbatts/tar-split/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2015 Vincent Batts, Raleigh, NC, USA + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. 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. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +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/vendor/github.com/vbatts/tar-split/archive/tar/common.go b/vendor/github.com/vbatts/tar-split/archive/tar/common.go new file mode 100644 index 0000000000..dee9e47e4a --- /dev/null +++ b/vendor/github.com/vbatts/tar-split/archive/tar/common.go @@ -0,0 +1,723 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package tar implements access to tar archives. +// +// Tape archives (tar) are a file format for storing a sequence of files that +// can be read and written in a streaming manner. +// This package aims to cover most variations of the format, +// including those produced by GNU and BSD tar tools. +package tar + +import ( + "errors" + "fmt" + "math" + "os" + "path" + "reflect" + "strconv" + "strings" + "time" +) + +// BUG: Use of the Uid and Gid fields in Header could overflow on 32-bit +// architectures. If a large value is encountered when decoding, the result +// stored in Header will be the truncated version. + +var ( + ErrHeader = errors.New("archive/tar: invalid tar header") + ErrWriteTooLong = errors.New("archive/tar: write too long") + ErrFieldTooLong = errors.New("archive/tar: header field too long") + ErrWriteAfterClose = errors.New("archive/tar: write after close") + errMissData = errors.New("archive/tar: sparse file references non-existent data") + errUnrefData = errors.New("archive/tar: sparse file contains unreferenced data") + errWriteHole = errors.New("archive/tar: write non-NUL byte in sparse hole") +) + +type headerError []string + +func (he headerError) Error() string { + const prefix = "archive/tar: cannot encode header" + var ss []string + for _, s := range he { + if s != "" { + ss = append(ss, s) + } + } + if len(ss) == 0 { + return prefix + } + return fmt.Sprintf("%s: %v", prefix, strings.Join(ss, "; and ")) +} + +// Type flags for Header.Typeflag. +const ( + // Type '0' indicates a regular file. + TypeReg = '0' + TypeRegA = '\x00' // Deprecated: Use TypeReg instead. + + // Type '1' to '6' are header-only flags and may not have a data body. + TypeLink = '1' // Hard link + TypeSymlink = '2' // Symbolic link + TypeChar = '3' // Character device node + TypeBlock = '4' // Block device node + TypeDir = '5' // Directory + TypeFifo = '6' // FIFO node + + // Type '7' is reserved. + TypeCont = '7' + + // Type 'x' is used by the PAX format to store key-value records that + // are only relevant to the next file. + // This package transparently handles these types. + TypeXHeader = 'x' + + // Type 'g' is used by the PAX format to store key-value records that + // are relevant to all subsequent files. + // This package only supports parsing and composing such headers, + // but does not currently support persisting the global state across files. + TypeXGlobalHeader = 'g' + + // Type 'S' indicates a sparse file in the GNU format. + TypeGNUSparse = 'S' + + // Types 'L' and 'K' are used by the GNU format for a meta file + // used to store the path or link name for the next file. + // This package transparently handles these types. + TypeGNULongName = 'L' + TypeGNULongLink = 'K' +) + +// Keywords for PAX extended header records. +const ( + paxNone = "" // Indicates that no PAX key is suitable + paxPath = "path" + paxLinkpath = "linkpath" + paxSize = "size" + paxUid = "uid" + paxGid = "gid" + paxUname = "uname" + paxGname = "gname" + paxMtime = "mtime" + paxAtime = "atime" + paxCtime = "ctime" // Removed from later revision of PAX spec, but was valid + paxCharset = "charset" // Currently unused + paxComment = "comment" // Currently unused + + paxSchilyXattr = "SCHILY.xattr." + + // Keywords for GNU sparse files in a PAX extended header. + paxGNUSparse = "GNU.sparse." + paxGNUSparseNumBlocks = "GNU.sparse.numblocks" + paxGNUSparseOffset = "GNU.sparse.offset" + paxGNUSparseNumBytes = "GNU.sparse.numbytes" + paxGNUSparseMap = "GNU.sparse.map" + paxGNUSparseName = "GNU.sparse.name" + paxGNUSparseMajor = "GNU.sparse.major" + paxGNUSparseMinor = "GNU.sparse.minor" + paxGNUSparseSize = "GNU.sparse.size" + paxGNUSparseRealSize = "GNU.sparse.realsize" +) + +// basicKeys is a set of the PAX keys for which we have built-in support. +// This does not contain "charset" or "comment", which are both PAX-specific, +// so adding them as first-class features of Header is unlikely. +// Users can use the PAXRecords field to set it themselves. +var basicKeys = map[string]bool{ + paxPath: true, paxLinkpath: true, paxSize: true, paxUid: true, paxGid: true, + paxUname: true, paxGname: true, paxMtime: true, paxAtime: true, paxCtime: true, +} + +// A Header represents a single header in a tar archive. +// Some fields may not be populated. +// +// For forward compatibility, users that retrieve a Header from Reader.Next, +// mutate it in some ways, and then pass it back to Writer.WriteHeader +// should do so by creating a new Header and copying the fields +// that they are interested in preserving. +type Header struct { + // Typeflag is the type of header entry. + // The zero value is automatically promoted to either TypeReg or TypeDir + // depending on the presence of a trailing slash in Name. + Typeflag byte + + Name string // Name of file entry + Linkname string // Target name of link (valid for TypeLink or TypeSymlink) + + Size int64 // Logical file size in bytes + Mode int64 // Permission and mode bits + Uid int // User ID of owner + Gid int // Group ID of owner + Uname string // User name of owner + Gname string // Group name of owner + + // If the Format is unspecified, then Writer.WriteHeader rounds ModTime + // to the nearest second and ignores the AccessTime and ChangeTime fields. + // + // To use AccessTime or ChangeTime, specify the Format as PAX or GNU. + // To use sub-second resolution, specify the Format as PAX. + ModTime time.Time // Modification time + AccessTime time.Time // Access time (requires either PAX or GNU support) + ChangeTime time.Time // Change time (requires either PAX or GNU support) + + Devmajor int64 // Major device number (valid for TypeChar or TypeBlock) + Devminor int64 // Minor device number (valid for TypeChar or TypeBlock) + + // Xattrs stores extended attributes as PAX records under the + // "SCHILY.xattr." namespace. + // + // The following are semantically equivalent: + // h.Xattrs[key] = value + // h.PAXRecords["SCHILY.xattr."+key] = value + // + // When Writer.WriteHeader is called, the contents of Xattrs will take + // precedence over those in PAXRecords. + // + // Deprecated: Use PAXRecords instead. + Xattrs map[string]string + + // PAXRecords is a map of PAX extended header records. + // + // User-defined records should have keys of the following form: + // VENDOR.keyword + // Where VENDOR is some namespace in all uppercase, and keyword may + // not contain the '=' character (e.g., "GOLANG.pkg.version"). + // The key and value should be non-empty UTF-8 strings. + // + // When Writer.WriteHeader is called, PAX records derived from the + // other fields in Header take precedence over PAXRecords. + PAXRecords map[string]string + + // Format specifies the format of the tar header. + // + // This is set by Reader.Next as a best-effort guess at the format. + // Since the Reader liberally reads some non-compliant files, + // it is possible for this to be FormatUnknown. + // + // If the format is unspecified when Writer.WriteHeader is called, + // then it uses the first format (in the order of USTAR, PAX, GNU) + // capable of encoding this Header (see Format). + Format Format +} + +// sparseEntry represents a Length-sized fragment at Offset in the file. +type sparseEntry struct{ Offset, Length int64 } + +func (s sparseEntry) endOffset() int64 { return s.Offset + s.Length } + +// A sparse file can be represented as either a sparseDatas or a sparseHoles. +// As long as the total size is known, they are equivalent and one can be +// converted to the other form and back. The various tar formats with sparse +// file support represent sparse files in the sparseDatas form. That is, they +// specify the fragments in the file that has data, and treat everything else as +// having zero bytes. As such, the encoding and decoding logic in this package +// deals with sparseDatas. +// +// However, the external API uses sparseHoles instead of sparseDatas because the +// zero value of sparseHoles logically represents a normal file (i.e., there are +// no holes in it). On the other hand, the zero value of sparseDatas implies +// that the file has no data in it, which is rather odd. +// +// As an example, if the underlying raw file contains the 10-byte data: +// var compactFile = "abcdefgh" +// +// And the sparse map has the following entries: +// var spd sparseDatas = []sparseEntry{ +// {Offset: 2, Length: 5}, // Data fragment for 2..6 +// {Offset: 18, Length: 3}, // Data fragment for 18..20 +// } +// var sph sparseHoles = []sparseEntry{ +// {Offset: 0, Length: 2}, // Hole fragment for 0..1 +// {Offset: 7, Length: 11}, // Hole fragment for 7..17 +// {Offset: 21, Length: 4}, // Hole fragment for 21..24 +// } +// +// Then the content of the resulting sparse file with a Header.Size of 25 is: +// var sparseFile = "\x00"*2 + "abcde" + "\x00"*11 + "fgh" + "\x00"*4 +type ( + sparseDatas []sparseEntry + sparseHoles []sparseEntry +) + +// validateSparseEntries reports whether sp is a valid sparse map. +// It does not matter whether sp represents data fragments or hole fragments. +func validateSparseEntries(sp []sparseEntry, size int64) bool { + // Validate all sparse entries. These are the same checks as performed by + // the BSD tar utility. + if size < 0 { + return false + } + var pre sparseEntry + for _, cur := range sp { + switch { + case cur.Offset < 0 || cur.Length < 0: + return false // Negative values are never okay + case cur.Offset > math.MaxInt64-cur.Length: + return false // Integer overflow with large length + case cur.endOffset() > size: + return false // Region extends beyond the actual size + case pre.endOffset() > cur.Offset: + return false // Regions cannot overlap and must be in order + } + pre = cur + } + return true +} + +// alignSparseEntries mutates src and returns dst where each fragment's +// starting offset is aligned up to the nearest block edge, and each +// ending offset is aligned down to the nearest block edge. +// +// Even though the Go tar Reader and the BSD tar utility can handle entries +// with arbitrary offsets and lengths, the GNU tar utility can only handle +// offsets and lengths that are multiples of blockSize. +func alignSparseEntries(src []sparseEntry, size int64) []sparseEntry { + dst := src[:0] + for _, s := range src { + pos, end := s.Offset, s.endOffset() + pos += blockPadding(+pos) // Round-up to nearest blockSize + if end != size { + end -= blockPadding(-end) // Round-down to nearest blockSize + } + if pos < end { + dst = append(dst, sparseEntry{Offset: pos, Length: end - pos}) + } + } + return dst +} + +// invertSparseEntries converts a sparse map from one form to the other. +// If the input is sparseHoles, then it will output sparseDatas and vice-versa. +// The input must have been already validated. +// +// This function mutates src and returns a normalized map where: +// * adjacent fragments are coalesced together +// * only the last fragment may be empty +// * the endOffset of the last fragment is the total size +func invertSparseEntries(src []sparseEntry, size int64) []sparseEntry { + dst := src[:0] + var pre sparseEntry + for _, cur := range src { + if cur.Length == 0 { + continue // Skip empty fragments + } + pre.Length = cur.Offset - pre.Offset + if pre.Length > 0 { + dst = append(dst, pre) // Only add non-empty fragments + } + pre.Offset = cur.endOffset() + } + pre.Length = size - pre.Offset // Possibly the only empty fragment + return append(dst, pre) +} + +// fileState tracks the number of logical (includes sparse holes) and physical +// (actual in tar archive) bytes remaining for the current file. +// +// Invariant: LogicalRemaining >= PhysicalRemaining +type fileState interface { + LogicalRemaining() int64 + PhysicalRemaining() int64 +} + +// allowedFormats determines which formats can be used. +// The value returned is the logical OR of multiple possible formats. +// If the value is FormatUnknown, then the input Header cannot be encoded +// and an error is returned explaining why. +// +// As a by-product of checking the fields, this function returns paxHdrs, which +// contain all fields that could not be directly encoded. +// A value receiver ensures that this method does not mutate the source Header. +func (h Header) allowedFormats() (format Format, paxHdrs map[string]string, err error) { + format = FormatUSTAR | FormatPAX | FormatGNU + paxHdrs = make(map[string]string) + + var whyNoUSTAR, whyNoPAX, whyNoGNU string + var preferPAX bool // Prefer PAX over USTAR + verifyString := func(s string, size int, name, paxKey string) { + // NUL-terminator is optional for path and linkpath. + // Technically, it is required for uname and gname, + // but neither GNU nor BSD tar checks for it. + tooLong := len(s) > size + allowLongGNU := paxKey == paxPath || paxKey == paxLinkpath + if hasNUL(s) || (tooLong && !allowLongGNU) { + whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%q", name, s) + format.mustNotBe(FormatGNU) + } + if !isASCII(s) || tooLong { + canSplitUSTAR := paxKey == paxPath + if _, _, ok := splitUSTARPath(s); !canSplitUSTAR || !ok { + whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%q", name, s) + format.mustNotBe(FormatUSTAR) + } + if paxKey == paxNone { + whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%q", name, s) + format.mustNotBe(FormatPAX) + } else { + paxHdrs[paxKey] = s + } + } + if v, ok := h.PAXRecords[paxKey]; ok && v == s { + paxHdrs[paxKey] = v + } + } + verifyNumeric := func(n int64, size int, name, paxKey string) { + if !fitsInBase256(size, n) { + whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%d", name, n) + format.mustNotBe(FormatGNU) + } + if !fitsInOctal(size, n) { + whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%d", name, n) + format.mustNotBe(FormatUSTAR) + if paxKey == paxNone { + whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%d", name, n) + format.mustNotBe(FormatPAX) + } else { + paxHdrs[paxKey] = strconv.FormatInt(n, 10) + } + } + if v, ok := h.PAXRecords[paxKey]; ok && v == strconv.FormatInt(n, 10) { + paxHdrs[paxKey] = v + } + } + verifyTime := func(ts time.Time, size int, name, paxKey string) { + if ts.IsZero() { + return // Always okay + } + if !fitsInBase256(size, ts.Unix()) { + whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%v", name, ts) + format.mustNotBe(FormatGNU) + } + isMtime := paxKey == paxMtime + fitsOctal := fitsInOctal(size, ts.Unix()) + if (isMtime && !fitsOctal) || !isMtime { + whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%v", name, ts) + format.mustNotBe(FormatUSTAR) + } + needsNano := ts.Nanosecond() != 0 + if !isMtime || !fitsOctal || needsNano { + preferPAX = true // USTAR may truncate sub-second measurements + if paxKey == paxNone { + whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%v", name, ts) + format.mustNotBe(FormatPAX) + } else { + paxHdrs[paxKey] = formatPAXTime(ts) + } + } + if v, ok := h.PAXRecords[paxKey]; ok && v == formatPAXTime(ts) { + paxHdrs[paxKey] = v + } + } + + // Check basic fields. + var blk block + v7 := blk.V7() + ustar := blk.USTAR() + gnu := blk.GNU() + verifyString(h.Name, len(v7.Name()), "Name", paxPath) + verifyString(h.Linkname, len(v7.LinkName()), "Linkname", paxLinkpath) + verifyString(h.Uname, len(ustar.UserName()), "Uname", paxUname) + verifyString(h.Gname, len(ustar.GroupName()), "Gname", paxGname) + verifyNumeric(h.Mode, len(v7.Mode()), "Mode", paxNone) + verifyNumeric(int64(h.Uid), len(v7.UID()), "Uid", paxUid) + verifyNumeric(int64(h.Gid), len(v7.GID()), "Gid", paxGid) + verifyNumeric(h.Size, len(v7.Size()), "Size", paxSize) + verifyNumeric(h.Devmajor, len(ustar.DevMajor()), "Devmajor", paxNone) + verifyNumeric(h.Devminor, len(ustar.DevMinor()), "Devminor", paxNone) + verifyTime(h.ModTime, len(v7.ModTime()), "ModTime", paxMtime) + verifyTime(h.AccessTime, len(gnu.AccessTime()), "AccessTime", paxAtime) + verifyTime(h.ChangeTime, len(gnu.ChangeTime()), "ChangeTime", paxCtime) + + // Check for header-only types. + var whyOnlyPAX, whyOnlyGNU string + switch h.Typeflag { + case TypeReg, TypeChar, TypeBlock, TypeFifo, TypeGNUSparse: + // Exclude TypeLink and TypeSymlink, since they may reference directories. + if strings.HasSuffix(h.Name, "/") { + return FormatUnknown, nil, headerError{"filename may not have trailing slash"} + } + case TypeXHeader, TypeGNULongName, TypeGNULongLink: + return FormatUnknown, nil, headerError{"cannot manually encode TypeXHeader, TypeGNULongName, or TypeGNULongLink headers"} + case TypeXGlobalHeader: + h2 := Header{Name: h.Name, Typeflag: h.Typeflag, Xattrs: h.Xattrs, PAXRecords: h.PAXRecords, Format: h.Format} + if !reflect.DeepEqual(h, h2) { + return FormatUnknown, nil, headerError{"only PAXRecords should be set for TypeXGlobalHeader"} + } + whyOnlyPAX = "only PAX supports TypeXGlobalHeader" + format.mayOnlyBe(FormatPAX) + } + if !isHeaderOnlyType(h.Typeflag) && h.Size < 0 { + return FormatUnknown, nil, headerError{"negative size on header-only type"} + } + + // Check PAX records. + if len(h.Xattrs) > 0 { + for k, v := range h.Xattrs { + paxHdrs[paxSchilyXattr+k] = v + } + whyOnlyPAX = "only PAX supports Xattrs" + format.mayOnlyBe(FormatPAX) + } + if len(h.PAXRecords) > 0 { + for k, v := range h.PAXRecords { + switch _, exists := paxHdrs[k]; { + case exists: + continue // Do not overwrite existing records + case h.Typeflag == TypeXGlobalHeader: + paxHdrs[k] = v // Copy all records + case !basicKeys[k] && !strings.HasPrefix(k, paxGNUSparse): + paxHdrs[k] = v // Ignore local records that may conflict + } + } + whyOnlyPAX = "only PAX supports PAXRecords" + format.mayOnlyBe(FormatPAX) + } + for k, v := range paxHdrs { + if !validPAXRecord(k, v) { + return FormatUnknown, nil, headerError{fmt.Sprintf("invalid PAX record: %q", k+" = "+v)} + } + } + + // TODO(dsnet): Re-enable this when adding sparse support. + // See https://golang.org/issue/22735 + /* + // Check sparse files. + if len(h.SparseHoles) > 0 || h.Typeflag == TypeGNUSparse { + if isHeaderOnlyType(h.Typeflag) { + return FormatUnknown, nil, headerError{"header-only type cannot be sparse"} + } + if !validateSparseEntries(h.SparseHoles, h.Size) { + return FormatUnknown, nil, headerError{"invalid sparse holes"} + } + if h.Typeflag == TypeGNUSparse { + whyOnlyGNU = "only GNU supports TypeGNUSparse" + format.mayOnlyBe(FormatGNU) + } else { + whyNoGNU = "GNU supports sparse files only with TypeGNUSparse" + format.mustNotBe(FormatGNU) + } + whyNoUSTAR = "USTAR does not support sparse files" + format.mustNotBe(FormatUSTAR) + } + */ + + // Check desired format. + if wantFormat := h.Format; wantFormat != FormatUnknown { + if wantFormat.has(FormatPAX) && !preferPAX { + wantFormat.mayBe(FormatUSTAR) // PAX implies USTAR allowed too + } + format.mayOnlyBe(wantFormat) // Set union of formats allowed and format wanted + } + if format == FormatUnknown { + switch h.Format { + case FormatUSTAR: + err = headerError{"Format specifies USTAR", whyNoUSTAR, whyOnlyPAX, whyOnlyGNU} + case FormatPAX: + err = headerError{"Format specifies PAX", whyNoPAX, whyOnlyGNU} + case FormatGNU: + err = headerError{"Format specifies GNU", whyNoGNU, whyOnlyPAX} + default: + err = headerError{whyNoUSTAR, whyNoPAX, whyNoGNU, whyOnlyPAX, whyOnlyGNU} + } + } + return format, paxHdrs, err +} + +// FileInfo returns an os.FileInfo for the Header. +func (h *Header) FileInfo() os.FileInfo { + return headerFileInfo{h} +} + +// headerFileInfo implements os.FileInfo. +type headerFileInfo struct { + h *Header +} + +func (fi headerFileInfo) Size() int64 { return fi.h.Size } +func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } +func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime } +func (fi headerFileInfo) Sys() interface{} { return fi.h } + +// Name returns the base name of the file. +func (fi headerFileInfo) Name() string { + if fi.IsDir() { + return path.Base(path.Clean(fi.h.Name)) + } + return path.Base(fi.h.Name) +} + +// Mode returns the permission and mode bits for the headerFileInfo. +func (fi headerFileInfo) Mode() (mode os.FileMode) { + // Set file permission bits. + mode = os.FileMode(fi.h.Mode).Perm() + + // Set setuid, setgid and sticky bits. + if fi.h.Mode&c_ISUID != 0 { + mode |= os.ModeSetuid + } + if fi.h.Mode&c_ISGID != 0 { + mode |= os.ModeSetgid + } + if fi.h.Mode&c_ISVTX != 0 { + mode |= os.ModeSticky + } + + // Set file mode bits; clear perm, setuid, setgid, and sticky bits. + switch m := os.FileMode(fi.h.Mode) &^ 07777; m { + case c_ISDIR: + mode |= os.ModeDir + case c_ISFIFO: + mode |= os.ModeNamedPipe + case c_ISLNK: + mode |= os.ModeSymlink + case c_ISBLK: + mode |= os.ModeDevice + case c_ISCHR: + mode |= os.ModeDevice + mode |= os.ModeCharDevice + case c_ISSOCK: + mode |= os.ModeSocket + } + + switch fi.h.Typeflag { + case TypeSymlink: + mode |= os.ModeSymlink + case TypeChar: + mode |= os.ModeDevice + mode |= os.ModeCharDevice + case TypeBlock: + mode |= os.ModeDevice + case TypeDir: + mode |= os.ModeDir + case TypeFifo: + mode |= os.ModeNamedPipe + } + + return mode +} + +// sysStat, if non-nil, populates h from system-dependent fields of fi. +var sysStat func(fi os.FileInfo, h *Header) error + +const ( + // Mode constants from the USTAR spec: + // See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06 + c_ISUID = 04000 // Set uid + c_ISGID = 02000 // Set gid + c_ISVTX = 01000 // Save text (sticky bit) + + // Common Unix mode constants; these are not defined in any common tar standard. + // Header.FileInfo understands these, but FileInfoHeader will never produce these. + c_ISDIR = 040000 // Directory + c_ISFIFO = 010000 // FIFO + c_ISREG = 0100000 // Regular file + c_ISLNK = 0120000 // Symbolic link + c_ISBLK = 060000 // Block special file + c_ISCHR = 020000 // Character special file + c_ISSOCK = 0140000 // Socket +) + +// FileInfoHeader creates a partially-populated Header from fi. +// If fi describes a symlink, FileInfoHeader records link as the link target. +// If fi describes a directory, a slash is appended to the name. +// +// Since os.FileInfo's Name method only returns the base name of +// the file it describes, it may be necessary to modify Header.Name +// to provide the full path name of the file. +func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) { + if fi == nil { + return nil, errors.New("archive/tar: FileInfo is nil") + } + fm := fi.Mode() + h := &Header{ + Name: fi.Name(), + ModTime: fi.ModTime(), + Mode: int64(fm.Perm()), // or'd with c_IS* constants later + } + switch { + case fm.IsRegular(): + h.Typeflag = TypeReg + h.Size = fi.Size() + case fi.IsDir(): + h.Typeflag = TypeDir + h.Name += "/" + case fm&os.ModeSymlink != 0: + h.Typeflag = TypeSymlink + h.Linkname = link + case fm&os.ModeDevice != 0: + if fm&os.ModeCharDevice != 0 { + h.Typeflag = TypeChar + } else { + h.Typeflag = TypeBlock + } + case fm&os.ModeNamedPipe != 0: + h.Typeflag = TypeFifo + case fm&os.ModeSocket != 0: + return nil, fmt.Errorf("archive/tar: sockets not supported") + default: + return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm) + } + if fm&os.ModeSetuid != 0 { + h.Mode |= c_ISUID + } + if fm&os.ModeSetgid != 0 { + h.Mode |= c_ISGID + } + if fm&os.ModeSticky != 0 { + h.Mode |= c_ISVTX + } + // If possible, populate additional fields from OS-specific + // FileInfo fields. + if sys, ok := fi.Sys().(*Header); ok { + // This FileInfo came from a Header (not the OS). Use the + // original Header to populate all remaining fields. + h.Uid = sys.Uid + h.Gid = sys.Gid + h.Uname = sys.Uname + h.Gname = sys.Gname + h.AccessTime = sys.AccessTime + h.ChangeTime = sys.ChangeTime + if sys.Xattrs != nil { + h.Xattrs = make(map[string]string) + for k, v := range sys.Xattrs { + h.Xattrs[k] = v + } + } + if sys.Typeflag == TypeLink { + // hard link + h.Typeflag = TypeLink + h.Size = 0 + h.Linkname = sys.Linkname + } + if sys.PAXRecords != nil { + h.PAXRecords = make(map[string]string) + for k, v := range sys.PAXRecords { + h.PAXRecords[k] = v + } + } + } + if sysStat != nil { + return h, sysStat(fi, h) + } + return h, nil +} + +// isHeaderOnlyType checks if the given type flag is of the type that has no +// data section even if a size is specified. +func isHeaderOnlyType(flag byte) bool { + switch flag { + case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo: + return true + default: + return false + } +} + +func min(a, b int64) int64 { + if a < b { + return a + } + return b +} diff --git a/vendor/github.com/vbatts/tar-split/archive/tar/format.go b/vendor/github.com/vbatts/tar-split/archive/tar/format.go new file mode 100644 index 0000000000..1f89d0c59a --- /dev/null +++ b/vendor/github.com/vbatts/tar-split/archive/tar/format.go @@ -0,0 +1,303 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar + +import "strings" + +// Format represents the tar archive format. +// +// The original tar format was introduced in Unix V7. +// Since then, there have been multiple competing formats attempting to +// standardize or extend the V7 format to overcome its limitations. +// The most common formats are the USTAR, PAX, and GNU formats, +// each with their own advantages and limitations. +// +// The following table captures the capabilities of each format: +// +// | USTAR | PAX | GNU +// ------------------+--------+-----------+---------- +// Name | 256B | unlimited | unlimited +// Linkname | 100B | unlimited | unlimited +// Size | uint33 | unlimited | uint89 +// Mode | uint21 | uint21 | uint57 +// Uid/Gid | uint21 | unlimited | uint57 +// Uname/Gname | 32B | unlimited | 32B +// ModTime | uint33 | unlimited | int89 +// AccessTime | n/a | unlimited | int89 +// ChangeTime | n/a | unlimited | int89 +// Devmajor/Devminor | uint21 | uint21 | uint57 +// ------------------+--------+-----------+---------- +// string encoding | ASCII | UTF-8 | binary +// sub-second times | no | yes | no +// sparse files | no | yes | yes +// +// The table's upper portion shows the Header fields, where each format reports +// the maximum number of bytes allowed for each string field and +// the integer type used to store each numeric field +// (where timestamps are stored as the number of seconds since the Unix epoch). +// +// The table's lower portion shows specialized features of each format, +// such as supported string encodings, support for sub-second timestamps, +// or support for sparse files. +// +// The Writer currently provides no support for sparse files. +type Format int + +// Constants to identify various tar formats. +const ( + // Deliberately hide the meaning of constants from public API. + _ Format = (1 << iota) / 4 // Sequence of 0, 0, 1, 2, 4, 8, etc... + + // FormatUnknown indicates that the format is unknown. + FormatUnknown + + // The format of the original Unix V7 tar tool prior to standardization. + formatV7 + + // FormatUSTAR represents the USTAR header format defined in POSIX.1-1988. + // + // While this format is compatible with most tar readers, + // the format has several limitations making it unsuitable for some usages. + // Most notably, it cannot support sparse files, files larger than 8GiB, + // filenames larger than 256 characters, and non-ASCII filenames. + // + // Reference: + // http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06 + FormatUSTAR + + // FormatPAX represents the PAX header format defined in POSIX.1-2001. + // + // PAX extends USTAR by writing a special file with Typeflag TypeXHeader + // preceding the original header. This file contains a set of key-value + // records, which are used to overcome USTAR's shortcomings, in addition to + // providing the ability to have sub-second resolution for timestamps. + // + // Some newer formats add their own extensions to PAX by defining their + // own keys and assigning certain semantic meaning to the associated values. + // For example, sparse file support in PAX is implemented using keys + // defined by the GNU manual (e.g., "GNU.sparse.map"). + // + // Reference: + // http://pubs.opengroup.org/onlinepubs/009695399/utilities/pax.html + FormatPAX + + // FormatGNU represents the GNU header format. + // + // The GNU header format is older than the USTAR and PAX standards and + // is not compatible with them. The GNU format supports + // arbitrary file sizes, filenames of arbitrary encoding and length, + // sparse files, and other features. + // + // It is recommended that PAX be chosen over GNU unless the target + // application can only parse GNU formatted archives. + // + // Reference: + // https://www.gnu.org/software/tar/manual/html_node/Standard.html + FormatGNU + + // Schily's tar format, which is incompatible with USTAR. + // This does not cover STAR extensions to the PAX format; these fall under + // the PAX format. + formatSTAR + + formatMax +) + +func (f Format) has(f2 Format) bool { return f&f2 != 0 } +func (f *Format) mayBe(f2 Format) { *f |= f2 } +func (f *Format) mayOnlyBe(f2 Format) { *f &= f2 } +func (f *Format) mustNotBe(f2 Format) { *f &^= f2 } + +var formatNames = map[Format]string{ + formatV7: "V7", FormatUSTAR: "USTAR", FormatPAX: "PAX", FormatGNU: "GNU", formatSTAR: "STAR", +} + +func (f Format) String() string { + var ss []string + for f2 := Format(1); f2 < formatMax; f2 <<= 1 { + if f.has(f2) { + ss = append(ss, formatNames[f2]) + } + } + switch len(ss) { + case 0: + return "" + case 1: + return ss[0] + default: + return "(" + strings.Join(ss, " | ") + ")" + } +} + +// Magics used to identify various formats. +const ( + magicGNU, versionGNU = "ustar ", " \x00" + magicUSTAR, versionUSTAR = "ustar\x00", "00" + trailerSTAR = "tar\x00" +) + +// Size constants from various tar specifications. +const ( + blockSize = 512 // Size of each block in a tar stream + nameSize = 100 // Max length of the name field in USTAR format + prefixSize = 155 // Max length of the prefix field in USTAR format +) + +// blockPadding computes the number of bytes needed to pad offset up to the +// nearest block edge where 0 <= n < blockSize. +func blockPadding(offset int64) (n int64) { + return -offset & (blockSize - 1) +} + +var zeroBlock block + +type block [blockSize]byte + +// Convert block to any number of formats. +func (b *block) V7() *headerV7 { return (*headerV7)(b) } +func (b *block) GNU() *headerGNU { return (*headerGNU)(b) } +func (b *block) STAR() *headerSTAR { return (*headerSTAR)(b) } +func (b *block) USTAR() *headerUSTAR { return (*headerUSTAR)(b) } +func (b *block) Sparse() sparseArray { return (sparseArray)(b[:]) } + +// GetFormat checks that the block is a valid tar header based on the checksum. +// It then attempts to guess the specific format based on magic values. +// If the checksum fails, then FormatUnknown is returned. +func (b *block) GetFormat() Format { + // Verify checksum. + var p parser + value := p.parseOctal(b.V7().Chksum()) + chksum1, chksum2 := b.ComputeChecksum() + if p.err != nil || (value != chksum1 && value != chksum2) { + return FormatUnknown + } + + // Guess the magic values. + magic := string(b.USTAR().Magic()) + version := string(b.USTAR().Version()) + trailer := string(b.STAR().Trailer()) + switch { + case magic == magicUSTAR && trailer == trailerSTAR: + return formatSTAR + case magic == magicUSTAR: + return FormatUSTAR | FormatPAX + case magic == magicGNU && version == versionGNU: + return FormatGNU + default: + return formatV7 + } +} + +// SetFormat writes the magic values necessary for specified format +// and then updates the checksum accordingly. +func (b *block) SetFormat(format Format) { + // Set the magic values. + switch { + case format.has(formatV7): + // Do nothing. + case format.has(FormatGNU): + copy(b.GNU().Magic(), magicGNU) + copy(b.GNU().Version(), versionGNU) + case format.has(formatSTAR): + copy(b.STAR().Magic(), magicUSTAR) + copy(b.STAR().Version(), versionUSTAR) + copy(b.STAR().Trailer(), trailerSTAR) + case format.has(FormatUSTAR | FormatPAX): + copy(b.USTAR().Magic(), magicUSTAR) + copy(b.USTAR().Version(), versionUSTAR) + default: + panic("invalid format") + } + + // Update checksum. + // This field is special in that it is terminated by a NULL then space. + var f formatter + field := b.V7().Chksum() + chksum, _ := b.ComputeChecksum() // Possible values are 256..128776 + f.formatOctal(field[:7], chksum) // Never fails since 128776 < 262143 + field[7] = ' ' +} + +// ComputeChecksum computes the checksum for the header block. +// POSIX specifies a sum of the unsigned byte values, but the Sun tar used +// signed byte values. +// We compute and return both. +func (b *block) ComputeChecksum() (unsigned, signed int64) { + for i, c := range b { + if 148 <= i && i < 156 { + c = ' ' // Treat the checksum field itself as all spaces. + } + unsigned += int64(c) + signed += int64(int8(c)) + } + return unsigned, signed +} + +// Reset clears the block with all zeros. +func (b *block) Reset() { + *b = block{} +} + +type headerV7 [blockSize]byte + +func (h *headerV7) Name() []byte { return h[000:][:100] } +func (h *headerV7) Mode() []byte { return h[100:][:8] } +func (h *headerV7) UID() []byte { return h[108:][:8] } +func (h *headerV7) GID() []byte { return h[116:][:8] } +func (h *headerV7) Size() []byte { return h[124:][:12] } +func (h *headerV7) ModTime() []byte { return h[136:][:12] } +func (h *headerV7) Chksum() []byte { return h[148:][:8] } +func (h *headerV7) TypeFlag() []byte { return h[156:][:1] } +func (h *headerV7) LinkName() []byte { return h[157:][:100] } + +type headerGNU [blockSize]byte + +func (h *headerGNU) V7() *headerV7 { return (*headerV7)(h) } +func (h *headerGNU) Magic() []byte { return h[257:][:6] } +func (h *headerGNU) Version() []byte { return h[263:][:2] } +func (h *headerGNU) UserName() []byte { return h[265:][:32] } +func (h *headerGNU) GroupName() []byte { return h[297:][:32] } +func (h *headerGNU) DevMajor() []byte { return h[329:][:8] } +func (h *headerGNU) DevMinor() []byte { return h[337:][:8] } +func (h *headerGNU) AccessTime() []byte { return h[345:][:12] } +func (h *headerGNU) ChangeTime() []byte { return h[357:][:12] } +func (h *headerGNU) Sparse() sparseArray { return (sparseArray)(h[386:][:24*4+1]) } +func (h *headerGNU) RealSize() []byte { return h[483:][:12] } + +type headerSTAR [blockSize]byte + +func (h *headerSTAR) V7() *headerV7 { return (*headerV7)(h) } +func (h *headerSTAR) Magic() []byte { return h[257:][:6] } +func (h *headerSTAR) Version() []byte { return h[263:][:2] } +func (h *headerSTAR) UserName() []byte { return h[265:][:32] } +func (h *headerSTAR) GroupName() []byte { return h[297:][:32] } +func (h *headerSTAR) DevMajor() []byte { return h[329:][:8] } +func (h *headerSTAR) DevMinor() []byte { return h[337:][:8] } +func (h *headerSTAR) Prefix() []byte { return h[345:][:131] } +func (h *headerSTAR) AccessTime() []byte { return h[476:][:12] } +func (h *headerSTAR) ChangeTime() []byte { return h[488:][:12] } +func (h *headerSTAR) Trailer() []byte { return h[508:][:4] } + +type headerUSTAR [blockSize]byte + +func (h *headerUSTAR) V7() *headerV7 { return (*headerV7)(h) } +func (h *headerUSTAR) Magic() []byte { return h[257:][:6] } +func (h *headerUSTAR) Version() []byte { return h[263:][:2] } +func (h *headerUSTAR) UserName() []byte { return h[265:][:32] } +func (h *headerUSTAR) GroupName() []byte { return h[297:][:32] } +func (h *headerUSTAR) DevMajor() []byte { return h[329:][:8] } +func (h *headerUSTAR) DevMinor() []byte { return h[337:][:8] } +func (h *headerUSTAR) Prefix() []byte { return h[345:][:155] } + +type sparseArray []byte + +func (s sparseArray) Entry(i int) sparseElem { return (sparseElem)(s[i*24:]) } +func (s sparseArray) IsExtended() []byte { return s[24*s.MaxEntries():][:1] } +func (s sparseArray) MaxEntries() int { return len(s) / 24 } + +type sparseElem []byte + +func (s sparseElem) Offset() []byte { return s[00:][:12] } +func (s sparseElem) Length() []byte { return s[12:][:12] } diff --git a/vendor/github.com/vbatts/tar-split/archive/tar/reader.go b/vendor/github.com/vbatts/tar-split/archive/tar/reader.go new file mode 100644 index 0000000000..ea64a38207 --- /dev/null +++ b/vendor/github.com/vbatts/tar-split/archive/tar/reader.go @@ -0,0 +1,923 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar + +import ( + "bytes" + "io" + "io/ioutil" + "strconv" + "strings" + "time" +) + +// Reader provides sequential access to the contents of a tar archive. +// Reader.Next advances to the next file in the archive (including the first), +// and then Reader can be treated as an io.Reader to access the file's data. +type Reader struct { + r io.Reader + pad int64 // Amount of padding (ignored) after current file entry + curr fileReader // Reader for current file entry + blk block // Buffer to use as temporary local storage + + // err is a persistent error. + // It is only the responsibility of every exported method of Reader to + // ensure that this error is sticky. + err error + + RawAccounting bool // Whether to enable the access needed to reassemble the tar from raw bytes. Some performance/memory hit for this. + rawBytes *bytes.Buffer // last raw bits +} + +type fileReader interface { + io.Reader + fileState + + WriteTo(io.Writer) (int64, error) +} + +// RawBytes accesses the raw bytes of the archive, apart from the file payload itself. +// This includes the header and padding. +// +// This call resets the current rawbytes buffer +// +// Only when RawAccounting is enabled, otherwise this returns nil +func (tr *Reader) RawBytes() []byte { + if !tr.RawAccounting { + return nil + } + if tr.rawBytes == nil { + tr.rawBytes = bytes.NewBuffer(nil) + } + defer tr.rawBytes.Reset() // if we've read them, then flush them. + + return tr.rawBytes.Bytes() + +} + +// NewReader creates a new Reader reading from r. +func NewReader(r io.Reader) *Reader { + return &Reader{r: r, curr: ®FileReader{r, 0}} +} + +// Next advances to the next entry in the tar archive. +// The Header.Size determines how many bytes can be read for the next file. +// Any remaining data in the current file is automatically discarded. +// +// io.EOF is returned at the end of the input. +func (tr *Reader) Next() (*Header, error) { + if tr.err != nil { + return nil, tr.err + } + hdr, err := tr.next() + tr.err = err + return hdr, err +} + +func (tr *Reader) next() (*Header, error) { + var paxHdrs map[string]string + var gnuLongName, gnuLongLink string + + if tr.RawAccounting { + if tr.rawBytes == nil { + tr.rawBytes = bytes.NewBuffer(nil) + } else { + tr.rawBytes.Reset() + } + } + + // Externally, Next iterates through the tar archive as if it is a series of + // files. Internally, the tar format often uses fake "files" to add meta + // data that describes the next file. These meta data "files" should not + // normally be visible to the outside. As such, this loop iterates through + // one or more "header files" until it finds a "normal file". + format := FormatUSTAR | FormatPAX | FormatGNU + for { + // Discard the remainder of the file and any padding. + if err := discard(tr, tr.curr.PhysicalRemaining()); err != nil { + return nil, err + } + n, err := tryReadFull(tr.r, tr.blk[:tr.pad]) + if err != nil { + return nil, err + } + if tr.RawAccounting { + tr.rawBytes.Write(tr.blk[:n]) + } + tr.pad = 0 + + hdr, rawHdr, err := tr.readHeader() + if err != nil { + return nil, err + } + if err := tr.handleRegularFile(hdr); err != nil { + return nil, err + } + format.mayOnlyBe(hdr.Format) + + // Check for PAX/GNU special headers and files. + switch hdr.Typeflag { + case TypeXHeader, TypeXGlobalHeader: + format.mayOnlyBe(FormatPAX) + paxHdrs, err = parsePAX(tr) + if err != nil { + return nil, err + } + if hdr.Typeflag == TypeXGlobalHeader { + mergePAX(hdr, paxHdrs) + return &Header{ + Name: hdr.Name, + Typeflag: hdr.Typeflag, + Xattrs: hdr.Xattrs, + PAXRecords: hdr.PAXRecords, + Format: format, + }, nil + } + continue // This is a meta header affecting the next header + case TypeGNULongName, TypeGNULongLink: + format.mayOnlyBe(FormatGNU) + realname, err := ioutil.ReadAll(tr) + if err != nil { + return nil, err + } + + if tr.RawAccounting { + tr.rawBytes.Write(realname) + } + + var p parser + switch hdr.Typeflag { + case TypeGNULongName: + gnuLongName = p.parseString(realname) + case TypeGNULongLink: + gnuLongLink = p.parseString(realname) + } + continue // This is a meta header affecting the next header + default: + // The old GNU sparse format is handled here since it is technically + // just a regular file with additional attributes. + + if err := mergePAX(hdr, paxHdrs); err != nil { + return nil, err + } + if gnuLongName != "" { + hdr.Name = gnuLongName + } + if gnuLongLink != "" { + hdr.Linkname = gnuLongLink + } + if hdr.Typeflag == TypeRegA { + if strings.HasSuffix(hdr.Name, "/") { + hdr.Typeflag = TypeDir // Legacy archives use trailing slash for directories + } else { + hdr.Typeflag = TypeReg + } + } + + // The extended headers may have updated the size. + // Thus, setup the regFileReader again after merging PAX headers. + if err := tr.handleRegularFile(hdr); err != nil { + return nil, err + } + + // Sparse formats rely on being able to read from the logical data + // section; there must be a preceding call to handleRegularFile. + if err := tr.handleSparseFile(hdr, rawHdr); err != nil { + return nil, err + } + + // Set the final guess at the format. + if format.has(FormatUSTAR) && format.has(FormatPAX) { + format.mayOnlyBe(FormatUSTAR) + } + hdr.Format = format + return hdr, nil // This is a file, so stop + } + } +} + +// handleRegularFile sets up the current file reader and padding such that it +// can only read the following logical data section. It will properly handle +// special headers that contain no data section. +func (tr *Reader) handleRegularFile(hdr *Header) error { + nb := hdr.Size + if isHeaderOnlyType(hdr.Typeflag) { + nb = 0 + } + if nb < 0 { + return ErrHeader + } + + tr.pad = blockPadding(nb) + tr.curr = ®FileReader{r: tr.r, nb: nb} + return nil +} + +// handleSparseFile checks if the current file is a sparse format of any type +// and sets the curr reader appropriately. +func (tr *Reader) handleSparseFile(hdr *Header, rawHdr *block) error { + var spd sparseDatas + var err error + if hdr.Typeflag == TypeGNUSparse { + spd, err = tr.readOldGNUSparseMap(hdr, rawHdr) + } else { + spd, err = tr.readGNUSparsePAXHeaders(hdr) + } + + // If sp is non-nil, then this is a sparse file. + // Note that it is possible for len(sp) == 0. + if err == nil && spd != nil { + if isHeaderOnlyType(hdr.Typeflag) || !validateSparseEntries(spd, hdr.Size) { + return ErrHeader + } + sph := invertSparseEntries(spd, hdr.Size) + tr.curr = &sparseFileReader{tr.curr, sph, 0} + } + return err +} + +// readGNUSparsePAXHeaders checks the PAX headers for GNU sparse headers. +// If they are found, then this function reads the sparse map and returns it. +// This assumes that 0.0 headers have already been converted to 0.1 headers +// by the PAX header parsing logic. +func (tr *Reader) readGNUSparsePAXHeaders(hdr *Header) (sparseDatas, error) { + // Identify the version of GNU headers. + var is1x0 bool + major, minor := hdr.PAXRecords[paxGNUSparseMajor], hdr.PAXRecords[paxGNUSparseMinor] + switch { + case major == "0" && (minor == "0" || minor == "1"): + is1x0 = false + case major == "1" && minor == "0": + is1x0 = true + case major != "" || minor != "": + return nil, nil // Unknown GNU sparse PAX version + case hdr.PAXRecords[paxGNUSparseMap] != "": + is1x0 = false // 0.0 and 0.1 did not have explicit version records, so guess + default: + return nil, nil // Not a PAX format GNU sparse file. + } + hdr.Format.mayOnlyBe(FormatPAX) + + // Update hdr from GNU sparse PAX headers. + if name := hdr.PAXRecords[paxGNUSparseName]; name != "" { + hdr.Name = name + } + size := hdr.PAXRecords[paxGNUSparseSize] + if size == "" { + size = hdr.PAXRecords[paxGNUSparseRealSize] + } + if size != "" { + n, err := strconv.ParseInt(size, 10, 64) + if err != nil { + return nil, ErrHeader + } + hdr.Size = n + } + + // Read the sparse map according to the appropriate format. + if is1x0 { + return readGNUSparseMap1x0(tr.curr) + } + return readGNUSparseMap0x1(hdr.PAXRecords) +} + +// mergePAX merges paxHdrs into hdr for all relevant fields of Header. +func mergePAX(hdr *Header, paxHdrs map[string]string) (err error) { + for k, v := range paxHdrs { + if v == "" { + continue // Keep the original USTAR value + } + var id64 int64 + switch k { + case paxPath: + hdr.Name = v + case paxLinkpath: + hdr.Linkname = v + case paxUname: + hdr.Uname = v + case paxGname: + hdr.Gname = v + case paxUid: + id64, err = strconv.ParseInt(v, 10, 64) + hdr.Uid = int(id64) // Integer overflow possible + case paxGid: + id64, err = strconv.ParseInt(v, 10, 64) + hdr.Gid = int(id64) // Integer overflow possible + case paxAtime: + hdr.AccessTime, err = parsePAXTime(v) + case paxMtime: + hdr.ModTime, err = parsePAXTime(v) + case paxCtime: + hdr.ChangeTime, err = parsePAXTime(v) + case paxSize: + hdr.Size, err = strconv.ParseInt(v, 10, 64) + default: + if strings.HasPrefix(k, paxSchilyXattr) { + if hdr.Xattrs == nil { + hdr.Xattrs = make(map[string]string) + } + hdr.Xattrs[k[len(paxSchilyXattr):]] = v + } + } + if err != nil { + return ErrHeader + } + } + hdr.PAXRecords = paxHdrs + return nil +} + +// parsePAX parses PAX headers. +// If an extended header (type 'x') is invalid, ErrHeader is returned +func parsePAX(r io.Reader) (map[string]string, error) { + buf, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + // leaving this function for io.Reader makes it more testable + if tr, ok := r.(*Reader); ok && tr.RawAccounting { + if _, err = tr.rawBytes.Write(buf); err != nil { + return nil, err + } + } + sbuf := string(buf) + + // For GNU PAX sparse format 0.0 support. + // This function transforms the sparse format 0.0 headers into format 0.1 + // headers since 0.0 headers were not PAX compliant. + var sparseMap []string + + paxHdrs := make(map[string]string) + for len(sbuf) > 0 { + key, value, residual, err := parsePAXRecord(sbuf) + if err != nil { + return nil, ErrHeader + } + sbuf = residual + + switch key { + case paxGNUSparseOffset, paxGNUSparseNumBytes: + // Validate sparse header order and value. + if (len(sparseMap)%2 == 0 && key != paxGNUSparseOffset) || + (len(sparseMap)%2 == 1 && key != paxGNUSparseNumBytes) || + strings.Contains(value, ",") { + return nil, ErrHeader + } + sparseMap = append(sparseMap, value) + default: + paxHdrs[key] = value + } + } + if len(sparseMap) > 0 { + paxHdrs[paxGNUSparseMap] = strings.Join(sparseMap, ",") + } + return paxHdrs, nil +} + +// readHeader reads the next block header and assumes that the underlying reader +// is already aligned to a block boundary. It returns the raw block of the +// header in case further processing is required. +// +// The err will be set to io.EOF only when one of the following occurs: +// * Exactly 0 bytes are read and EOF is hit. +// * Exactly 1 block of zeros is read and EOF is hit. +// * At least 2 blocks of zeros are read. +func (tr *Reader) readHeader() (*Header, *block, error) { + // Two blocks of zero bytes marks the end of the archive. + n, err := io.ReadFull(tr.r, tr.blk[:]) + if tr.RawAccounting && (err == nil || err == io.EOF) { + tr.rawBytes.Write(tr.blk[:n]) + } + if err != nil { + return nil, nil, err // EOF is okay here; exactly 0 bytes read + } + + if bytes.Equal(tr.blk[:], zeroBlock[:]) { + n, err = io.ReadFull(tr.r, tr.blk[:]) + if tr.RawAccounting && (err == nil || err == io.EOF) { + tr.rawBytes.Write(tr.blk[:n]) + } + if err != nil { + return nil, nil, err // EOF is okay here; exactly 1 block of zeros read + } + if bytes.Equal(tr.blk[:], zeroBlock[:]) { + return nil, nil, io.EOF // normal EOF; exactly 2 block of zeros read + } + return nil, nil, ErrHeader // Zero block and then non-zero block + } + + // Verify the header matches a known format. + format := tr.blk.GetFormat() + if format == FormatUnknown { + return nil, nil, ErrHeader + } + + var p parser + hdr := new(Header) + + // Unpack the V7 header. + v7 := tr.blk.V7() + hdr.Typeflag = v7.TypeFlag()[0] + hdr.Name = p.parseString(v7.Name()) + hdr.Linkname = p.parseString(v7.LinkName()) + hdr.Size = p.parseNumeric(v7.Size()) + hdr.Mode = p.parseNumeric(v7.Mode()) + hdr.Uid = int(p.parseNumeric(v7.UID())) + hdr.Gid = int(p.parseNumeric(v7.GID())) + hdr.ModTime = time.Unix(p.parseNumeric(v7.ModTime()), 0) + + // Unpack format specific fields. + if format > formatV7 { + ustar := tr.blk.USTAR() + hdr.Uname = p.parseString(ustar.UserName()) + hdr.Gname = p.parseString(ustar.GroupName()) + hdr.Devmajor = p.parseNumeric(ustar.DevMajor()) + hdr.Devminor = p.parseNumeric(ustar.DevMinor()) + + var prefix string + switch { + case format.has(FormatUSTAR | FormatPAX): + hdr.Format = format + ustar := tr.blk.USTAR() + prefix = p.parseString(ustar.Prefix()) + + // For Format detection, check if block is properly formatted since + // the parser is more liberal than what USTAR actually permits. + notASCII := func(r rune) bool { return r >= 0x80 } + if bytes.IndexFunc(tr.blk[:], notASCII) >= 0 { + hdr.Format = FormatUnknown // Non-ASCII characters in block. + } + nul := func(b []byte) bool { return int(b[len(b)-1]) == 0 } + if !(nul(v7.Size()) && nul(v7.Mode()) && nul(v7.UID()) && nul(v7.GID()) && + nul(v7.ModTime()) && nul(ustar.DevMajor()) && nul(ustar.DevMinor())) { + hdr.Format = FormatUnknown // Numeric fields must end in NUL + } + case format.has(formatSTAR): + star := tr.blk.STAR() + prefix = p.parseString(star.Prefix()) + hdr.AccessTime = time.Unix(p.parseNumeric(star.AccessTime()), 0) + hdr.ChangeTime = time.Unix(p.parseNumeric(star.ChangeTime()), 0) + case format.has(FormatGNU): + hdr.Format = format + var p2 parser + gnu := tr.blk.GNU() + if b := gnu.AccessTime(); b[0] != 0 { + hdr.AccessTime = time.Unix(p2.parseNumeric(b), 0) + } + if b := gnu.ChangeTime(); b[0] != 0 { + hdr.ChangeTime = time.Unix(p2.parseNumeric(b), 0) + } + + // Prior to Go1.8, the Writer had a bug where it would output + // an invalid tar file in certain rare situations because the logic + // incorrectly believed that the old GNU format had a prefix field. + // This is wrong and leads to an output file that mangles the + // atime and ctime fields, which are often left unused. + // + // In order to continue reading tar files created by former, buggy + // versions of Go, we skeptically parse the atime and ctime fields. + // If we are unable to parse them and the prefix field looks like + // an ASCII string, then we fallback on the pre-Go1.8 behavior + // of treating these fields as the USTAR prefix field. + // + // Note that this will not use the fallback logic for all possible + // files generated by a pre-Go1.8 toolchain. If the generated file + // happened to have a prefix field that parses as valid + // atime and ctime fields (e.g., when they are valid octal strings), + // then it is impossible to distinguish between an valid GNU file + // and an invalid pre-Go1.8 file. + // + // See https://golang.org/issues/12594 + // See https://golang.org/issues/21005 + if p2.err != nil { + hdr.AccessTime, hdr.ChangeTime = time.Time{}, time.Time{} + ustar := tr.blk.USTAR() + if s := p.parseString(ustar.Prefix()); isASCII(s) { + prefix = s + } + hdr.Format = FormatUnknown // Buggy file is not GNU + } + } + if len(prefix) > 0 { + hdr.Name = prefix + "/" + hdr.Name + } + } + return hdr, &tr.blk, p.err +} + +// readOldGNUSparseMap reads the sparse map from the old GNU sparse format. +// The sparse map is stored in the tar header if it's small enough. +// If it's larger than four entries, then one or more extension headers are used +// to store the rest of the sparse map. +// +// The Header.Size does not reflect the size of any extended headers used. +// Thus, this function will read from the raw io.Reader to fetch extra headers. +// This method mutates blk in the process. +func (tr *Reader) readOldGNUSparseMap(hdr *Header, blk *block) (sparseDatas, error) { + // Make sure that the input format is GNU. + // Unfortunately, the STAR format also has a sparse header format that uses + // the same type flag but has a completely different layout. + if blk.GetFormat() != FormatGNU { + return nil, ErrHeader + } + hdr.Format.mayOnlyBe(FormatGNU) + + var p parser + hdr.Size = p.parseNumeric(blk.GNU().RealSize()) + if p.err != nil { + return nil, p.err + } + s := blk.GNU().Sparse() + spd := make(sparseDatas, 0, s.MaxEntries()) + for { + for i := 0; i < s.MaxEntries(); i++ { + // This termination condition is identical to GNU and BSD tar. + if s.Entry(i).Offset()[0] == 0x00 { + break // Don't return, need to process extended headers (even if empty) + } + offset := p.parseNumeric(s.Entry(i).Offset()) + length := p.parseNumeric(s.Entry(i).Length()) + if p.err != nil { + return nil, p.err + } + spd = append(spd, sparseEntry{Offset: offset, Length: length}) + } + + if s.IsExtended()[0] > 0 { + // There are more entries. Read an extension header and parse its entries. + if _, err := mustReadFull(tr.r, blk[:]); err != nil { + return nil, err + } + if tr.RawAccounting { + tr.rawBytes.Write(blk[:]) + } + s = blk.Sparse() + continue + } + return spd, nil // Done + } +} + +// readGNUSparseMap1x0 reads the sparse map as stored in GNU's PAX sparse format +// version 1.0. The format of the sparse map consists of a series of +// newline-terminated numeric fields. The first field is the number of entries +// and is always present. Following this are the entries, consisting of two +// fields (offset, length). This function must stop reading at the end +// boundary of the block containing the last newline. +// +// Note that the GNU manual says that numeric values should be encoded in octal +// format. However, the GNU tar utility itself outputs these values in decimal. +// As such, this library treats values as being encoded in decimal. +func readGNUSparseMap1x0(r io.Reader) (sparseDatas, error) { + var ( + cntNewline int64 + buf bytes.Buffer + blk block + ) + + // feedTokens copies data in blocks from r into buf until there are + // at least cnt newlines in buf. It will not read more blocks than needed. + feedTokens := func(n int64) error { + for cntNewline < n { + if _, err := mustReadFull(r, blk[:]); err != nil { + return err + } + buf.Write(blk[:]) + for _, c := range blk { + if c == '\n' { + cntNewline++ + } + } + } + return nil + } + + // nextToken gets the next token delimited by a newline. This assumes that + // at least one newline exists in the buffer. + nextToken := func() string { + cntNewline-- + tok, _ := buf.ReadString('\n') + return strings.TrimRight(tok, "\n") + } + + // Parse for the number of entries. + // Use integer overflow resistant math to check this. + if err := feedTokens(1); err != nil { + return nil, err + } + numEntries, err := strconv.ParseInt(nextToken(), 10, 0) // Intentionally parse as native int + if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) { + return nil, ErrHeader + } + + // Parse for all member entries. + // numEntries is trusted after this since a potential attacker must have + // committed resources proportional to what this library used. + if err := feedTokens(2 * numEntries); err != nil { + return nil, err + } + spd := make(sparseDatas, 0, numEntries) + for i := int64(0); i < numEntries; i++ { + offset, err1 := strconv.ParseInt(nextToken(), 10, 64) + length, err2 := strconv.ParseInt(nextToken(), 10, 64) + if err1 != nil || err2 != nil { + return nil, ErrHeader + } + spd = append(spd, sparseEntry{Offset: offset, Length: length}) + } + return spd, nil +} + +// readGNUSparseMap0x1 reads the sparse map as stored in GNU's PAX sparse format +// version 0.1. The sparse map is stored in the PAX headers. +func readGNUSparseMap0x1(paxHdrs map[string]string) (sparseDatas, error) { + // Get number of entries. + // Use integer overflow resistant math to check this. + numEntriesStr := paxHdrs[paxGNUSparseNumBlocks] + numEntries, err := strconv.ParseInt(numEntriesStr, 10, 0) // Intentionally parse as native int + if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) { + return nil, ErrHeader + } + + // There should be two numbers in sparseMap for each entry. + sparseMap := strings.Split(paxHdrs[paxGNUSparseMap], ",") + if len(sparseMap) == 1 && sparseMap[0] == "" { + sparseMap = sparseMap[:0] + } + if int64(len(sparseMap)) != 2*numEntries { + return nil, ErrHeader + } + + // Loop through the entries in the sparse map. + // numEntries is trusted now. + spd := make(sparseDatas, 0, numEntries) + for len(sparseMap) >= 2 { + offset, err1 := strconv.ParseInt(sparseMap[0], 10, 64) + length, err2 := strconv.ParseInt(sparseMap[1], 10, 64) + if err1 != nil || err2 != nil { + return nil, ErrHeader + } + spd = append(spd, sparseEntry{Offset: offset, Length: length}) + sparseMap = sparseMap[2:] + } + return spd, nil +} + +// Read reads from the current file in the tar archive. +// It returns (0, io.EOF) when it reaches the end of that file, +// until Next is called to advance to the next file. +// +// If the current file is sparse, then the regions marked as a hole +// are read back as NUL-bytes. +// +// Calling Read on special types like TypeLink, TypeSymlink, TypeChar, +// TypeBlock, TypeDir, and TypeFifo returns (0, io.EOF) regardless of what +// the Header.Size claims. +func (tr *Reader) Read(b []byte) (int, error) { + if tr.err != nil { + return 0, tr.err + } + n, err := tr.curr.Read(b) + if err != nil && err != io.EOF { + tr.err = err + } + return n, err +} + +// writeTo writes the content of the current file to w. +// The bytes written matches the number of remaining bytes in the current file. +// +// If the current file is sparse and w is an io.WriteSeeker, +// then writeTo uses Seek to skip past holes defined in Header.SparseHoles, +// assuming that skipped regions are filled with NULs. +// This always writes the last byte to ensure w is the right size. +// +// TODO(dsnet): Re-export this when adding sparse file support. +// See https://golang.org/issue/22735 +func (tr *Reader) writeTo(w io.Writer) (int64, error) { + if tr.err != nil { + return 0, tr.err + } + n, err := tr.curr.WriteTo(w) + if err != nil { + tr.err = err + } + return n, err +} + +// regFileReader is a fileReader for reading data from a regular file entry. +type regFileReader struct { + r io.Reader // Underlying Reader + nb int64 // Number of remaining bytes to read +} + +func (fr *regFileReader) Read(b []byte) (n int, err error) { + if int64(len(b)) > fr.nb { + b = b[:fr.nb] + } + if len(b) > 0 { + n, err = fr.r.Read(b) + fr.nb -= int64(n) + } + switch { + case err == io.EOF && fr.nb > 0: + return n, io.ErrUnexpectedEOF + case err == nil && fr.nb == 0: + return n, io.EOF + default: + return n, err + } +} + +func (fr *regFileReader) WriteTo(w io.Writer) (int64, error) { + return io.Copy(w, struct{ io.Reader }{fr}) +} + +func (fr regFileReader) LogicalRemaining() int64 { + return fr.nb +} + +func (fr regFileReader) PhysicalRemaining() int64 { + return fr.nb +} + +// sparseFileReader is a fileReader for reading data from a sparse file entry. +type sparseFileReader struct { + fr fileReader // Underlying fileReader + sp sparseHoles // Normalized list of sparse holes + pos int64 // Current position in sparse file +} + +func (sr *sparseFileReader) Read(b []byte) (n int, err error) { + finished := int64(len(b)) >= sr.LogicalRemaining() + if finished { + b = b[:sr.LogicalRemaining()] + } + + b0 := b + endPos := sr.pos + int64(len(b)) + for endPos > sr.pos && err == nil { + var nf int // Bytes read in fragment + holeStart, holeEnd := sr.sp[0].Offset, sr.sp[0].endOffset() + if sr.pos < holeStart { // In a data fragment + bf := b[:min(int64(len(b)), holeStart-sr.pos)] + nf, err = tryReadFull(sr.fr, bf) + } else { // In a hole fragment + bf := b[:min(int64(len(b)), holeEnd-sr.pos)] + nf, err = tryReadFull(zeroReader{}, bf) + } + b = b[nf:] + sr.pos += int64(nf) + if sr.pos >= holeEnd && len(sr.sp) > 1 { + sr.sp = sr.sp[1:] // Ensure last fragment always remains + } + } + + n = len(b0) - len(b) + switch { + case err == io.EOF: + return n, errMissData // Less data in dense file than sparse file + case err != nil: + return n, err + case sr.LogicalRemaining() == 0 && sr.PhysicalRemaining() > 0: + return n, errUnrefData // More data in dense file than sparse file + case finished: + return n, io.EOF + default: + return n, nil + } +} + +func (sr *sparseFileReader) WriteTo(w io.Writer) (n int64, err error) { + ws, ok := w.(io.WriteSeeker) + if ok { + if _, err := ws.Seek(0, io.SeekCurrent); err != nil { + ok = false // Not all io.Seeker can really seek + } + } + if !ok { + return io.Copy(w, struct{ io.Reader }{sr}) + } + + var writeLastByte bool + pos0 := sr.pos + for sr.LogicalRemaining() > 0 && !writeLastByte && err == nil { + var nf int64 // Size of fragment + holeStart, holeEnd := sr.sp[0].Offset, sr.sp[0].endOffset() + if sr.pos < holeStart { // In a data fragment + nf = holeStart - sr.pos + nf, err = io.CopyN(ws, sr.fr, nf) + } else { // In a hole fragment + nf = holeEnd - sr.pos + if sr.PhysicalRemaining() == 0 { + writeLastByte = true + nf-- + } + _, err = ws.Seek(nf, io.SeekCurrent) + } + sr.pos += nf + if sr.pos >= holeEnd && len(sr.sp) > 1 { + sr.sp = sr.sp[1:] // Ensure last fragment always remains + } + } + + // If the last fragment is a hole, then seek to 1-byte before EOF, and + // write a single byte to ensure the file is the right size. + if writeLastByte && err == nil { + _, err = ws.Write([]byte{0}) + sr.pos++ + } + + n = sr.pos - pos0 + switch { + case err == io.EOF: + return n, errMissData // Less data in dense file than sparse file + case err != nil: + return n, err + case sr.LogicalRemaining() == 0 && sr.PhysicalRemaining() > 0: + return n, errUnrefData // More data in dense file than sparse file + default: + return n, nil + } +} + +func (sr sparseFileReader) LogicalRemaining() int64 { + return sr.sp[len(sr.sp)-1].endOffset() - sr.pos +} +func (sr sparseFileReader) PhysicalRemaining() int64 { + return sr.fr.PhysicalRemaining() +} + +type zeroReader struct{} + +func (zeroReader) Read(b []byte) (int, error) { + for i := range b { + b[i] = 0 + } + return len(b), nil +} + +// mustReadFull is like io.ReadFull except it returns +// io.ErrUnexpectedEOF when io.EOF is hit before len(b) bytes are read. +func mustReadFull(r io.Reader, b []byte) (int, error) { + n, err := tryReadFull(r, b) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return n, err +} + +// tryReadFull is like io.ReadFull except it returns +// io.EOF when it is hit before len(b) bytes are read. +func tryReadFull(r io.Reader, b []byte) (n int, err error) { + for len(b) > n && err == nil { + var nn int + nn, err = r.Read(b[n:]) + n += nn + } + if len(b) == n && err == io.EOF { + err = nil + } + return n, err +} + +// discard skips n bytes in r, reporting an error if unable to do so. +func discard(tr *Reader, n int64) error { + var seekSkipped, copySkipped int64 + var err error + r := tr.r + if tr.RawAccounting { + + copySkipped, err = io.CopyN(tr.rawBytes, tr.r, n) + goto out + } + + // If possible, Seek to the last byte before the end of the data section. + // Do this because Seek is often lazy about reporting errors; this will mask + // the fact that the stream may be truncated. We can rely on the + // io.CopyN done shortly afterwards to trigger any IO errors. + if sr, ok := r.(io.Seeker); ok && n > 1 { + // Not all io.Seeker can actually Seek. For example, os.Stdin implements + // io.Seeker, but calling Seek always returns an error and performs + // no action. Thus, we try an innocent seek to the current position + // to see if Seek is really supported. + pos1, err := sr.Seek(0, io.SeekCurrent) + if pos1 >= 0 && err == nil { + // Seek seems supported, so perform the real Seek. + pos2, err := sr.Seek(n-1, io.SeekCurrent) + if pos2 < 0 || err != nil { + return err + } + seekSkipped = pos2 - pos1 + } + } + + copySkipped, err = io.CopyN(ioutil.Discard, r, n-seekSkipped) +out: + if err == io.EOF && seekSkipped+copySkipped < n { + err = io.ErrUnexpectedEOF + } + return err +} diff --git a/vendor/github.com/vbatts/tar-split/archive/tar/stat_actime1.go b/vendor/github.com/vbatts/tar-split/archive/tar/stat_actime1.go new file mode 100644 index 0000000000..cf9cc79c59 --- /dev/null +++ b/vendor/github.com/vbatts/tar-split/archive/tar/stat_actime1.go @@ -0,0 +1,20 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux dragonfly openbsd solaris + +package tar + +import ( + "syscall" + "time" +) + +func statAtime(st *syscall.Stat_t) time.Time { + return time.Unix(st.Atim.Unix()) +} + +func statCtime(st *syscall.Stat_t) time.Time { + return time.Unix(st.Ctim.Unix()) +} diff --git a/vendor/github.com/vbatts/tar-split/archive/tar/stat_actime2.go b/vendor/github.com/vbatts/tar-split/archive/tar/stat_actime2.go new file mode 100644 index 0000000000..6f17dbe307 --- /dev/null +++ b/vendor/github.com/vbatts/tar-split/archive/tar/stat_actime2.go @@ -0,0 +1,20 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd netbsd + +package tar + +import ( + "syscall" + "time" +) + +func statAtime(st *syscall.Stat_t) time.Time { + return time.Unix(st.Atimespec.Unix()) +} + +func statCtime(st *syscall.Stat_t) time.Time { + return time.Unix(st.Ctimespec.Unix()) +} diff --git a/vendor/github.com/vbatts/tar-split/archive/tar/stat_unix.go b/vendor/github.com/vbatts/tar-split/archive/tar/stat_unix.go new file mode 100644 index 0000000000..868105f338 --- /dev/null +++ b/vendor/github.com/vbatts/tar-split/archive/tar/stat_unix.go @@ -0,0 +1,96 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux darwin dragonfly freebsd openbsd netbsd solaris + +package tar + +import ( + "os" + "os/user" + "runtime" + "strconv" + "sync" + "syscall" +) + +func init() { + sysStat = statUnix +} + +// userMap and groupMap caches UID and GID lookups for performance reasons. +// The downside is that renaming uname or gname by the OS never takes effect. +var userMap, groupMap sync.Map // map[int]string + +func statUnix(fi os.FileInfo, h *Header) error { + sys, ok := fi.Sys().(*syscall.Stat_t) + if !ok { + return nil + } + h.Uid = int(sys.Uid) + h.Gid = int(sys.Gid) + + // Best effort at populating Uname and Gname. + // The os/user functions may fail for any number of reasons + // (not implemented on that platform, cgo not enabled, etc). + if u, ok := userMap.Load(h.Uid); ok { + h.Uname = u.(string) + } else if u, err := user.LookupId(strconv.Itoa(h.Uid)); err == nil { + h.Uname = u.Username + userMap.Store(h.Uid, h.Uname) + } + if g, ok := groupMap.Load(h.Gid); ok { + h.Gname = g.(string) + } else if g, err := user.LookupGroupId(strconv.Itoa(h.Gid)); err == nil { + h.Gname = g.Name + groupMap.Store(h.Gid, h.Gname) + } + + h.AccessTime = statAtime(sys) + h.ChangeTime = statCtime(sys) + + // Best effort at populating Devmajor and Devminor. + if h.Typeflag == TypeChar || h.Typeflag == TypeBlock { + dev := uint64(sys.Rdev) // May be int32 or uint32 + switch runtime.GOOS { + case "linux": + // Copied from golang.org/x/sys/unix/dev_linux.go. + major := uint32((dev & 0x00000000000fff00) >> 8) + major |= uint32((dev & 0xfffff00000000000) >> 32) + minor := uint32((dev & 0x00000000000000ff) >> 0) + minor |= uint32((dev & 0x00000ffffff00000) >> 12) + h.Devmajor, h.Devminor = int64(major), int64(minor) + case "darwin": + // Copied from golang.org/x/sys/unix/dev_darwin.go. + major := uint32((dev >> 24) & 0xff) + minor := uint32(dev & 0xffffff) + h.Devmajor, h.Devminor = int64(major), int64(minor) + case "dragonfly": + // Copied from golang.org/x/sys/unix/dev_dragonfly.go. + major := uint32((dev >> 8) & 0xff) + minor := uint32(dev & 0xffff00ff) + h.Devmajor, h.Devminor = int64(major), int64(minor) + case "freebsd": + // Copied from golang.org/x/sys/unix/dev_freebsd.go. + major := uint32((dev >> 8) & 0xff) + minor := uint32(dev & 0xffff00ff) + h.Devmajor, h.Devminor = int64(major), int64(minor) + case "netbsd": + // Copied from golang.org/x/sys/unix/dev_netbsd.go. + major := uint32((dev & 0x000fff00) >> 8) + minor := uint32((dev & 0x000000ff) >> 0) + minor |= uint32((dev & 0xfff00000) >> 12) + h.Devmajor, h.Devminor = int64(major), int64(minor) + case "openbsd": + // Copied from golang.org/x/sys/unix/dev_openbsd.go. + major := uint32((dev & 0x0000ff00) >> 8) + minor := uint32((dev & 0x000000ff) >> 0) + minor |= uint32((dev & 0xffff0000) >> 8) + h.Devmajor, h.Devminor = int64(major), int64(minor) + default: + // TODO: Implement solaris (see https://golang.org/issue/8106) + } + } + return nil +} diff --git a/vendor/github.com/vbatts/tar-split/archive/tar/strconv.go b/vendor/github.com/vbatts/tar-split/archive/tar/strconv.go new file mode 100644 index 0000000000..d144485a49 --- /dev/null +++ b/vendor/github.com/vbatts/tar-split/archive/tar/strconv.go @@ -0,0 +1,326 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar + +import ( + "bytes" + "fmt" + "strconv" + "strings" + "time" +) + +// hasNUL reports whether the NUL character exists within s. +func hasNUL(s string) bool { + return strings.IndexByte(s, 0) >= 0 +} + +// isASCII reports whether the input is an ASCII C-style string. +func isASCII(s string) bool { + for _, c := range s { + if c >= 0x80 || c == 0x00 { + return false + } + } + return true +} + +// toASCII converts the input to an ASCII C-style string. +// This a best effort conversion, so invalid characters are dropped. +func toASCII(s string) string { + if isASCII(s) { + return s + } + b := make([]byte, 0, len(s)) + for _, c := range s { + if c < 0x80 && c != 0x00 { + b = append(b, byte(c)) + } + } + return string(b) +} + +type parser struct { + err error // Last error seen +} + +type formatter struct { + err error // Last error seen +} + +// parseString parses bytes as a NUL-terminated C-style string. +// If a NUL byte is not found then the whole slice is returned as a string. +func (*parser) parseString(b []byte) string { + if i := bytes.IndexByte(b, 0); i >= 0 { + return string(b[:i]) + } + return string(b) +} + +// formatString copies s into b, NUL-terminating if possible. +func (f *formatter) formatString(b []byte, s string) { + if len(s) > len(b) { + f.err = ErrFieldTooLong + } + copy(b, s) + if len(s) < len(b) { + b[len(s)] = 0 + } + + // Some buggy readers treat regular files with a trailing slash + // in the V7 path field as a directory even though the full path + // recorded elsewhere (e.g., via PAX record) contains no trailing slash. + if len(s) > len(b) && b[len(b)-1] == '/' { + n := len(strings.TrimRight(s[:len(b)], "/")) + b[n] = 0 // Replace trailing slash with NUL terminator + } +} + +// fitsInBase256 reports whether x can be encoded into n bytes using base-256 +// encoding. Unlike octal encoding, base-256 encoding does not require that the +// string ends with a NUL character. Thus, all n bytes are available for output. +// +// If operating in binary mode, this assumes strict GNU binary mode; which means +// that the first byte can only be either 0x80 or 0xff. Thus, the first byte is +// equivalent to the sign bit in two's complement form. +func fitsInBase256(n int, x int64) bool { + binBits := uint(n-1) * 8 + return n >= 9 || (x >= -1< 0 && b[0]&0x80 != 0 { + // Handling negative numbers relies on the following identity: + // -a-1 == ^a + // + // If the number is negative, we use an inversion mask to invert the + // data bytes and treat the value as an unsigned number. + var inv byte // 0x00 if positive or zero, 0xff if negative + if b[0]&0x40 != 0 { + inv = 0xff + } + + var x uint64 + for i, c := range b { + c ^= inv // Inverts c only if inv is 0xff, otherwise does nothing + if i == 0 { + c &= 0x7f // Ignore signal bit in first byte + } + if (x >> 56) > 0 { + p.err = ErrHeader // Integer overflow + return 0 + } + x = x<<8 | uint64(c) + } + if (x >> 63) > 0 { + p.err = ErrHeader // Integer overflow + return 0 + } + if inv == 0xff { + return ^int64(x) + } + return int64(x) + } + + // Normal case is base-8 (octal) format. + return p.parseOctal(b) +} + +// formatNumeric encodes x into b using base-8 (octal) encoding if possible. +// Otherwise it will attempt to use base-256 (binary) encoding. +func (f *formatter) formatNumeric(b []byte, x int64) { + if fitsInOctal(len(b), x) { + f.formatOctal(b, x) + return + } + + if fitsInBase256(len(b), x) { + for i := len(b) - 1; i >= 0; i-- { + b[i] = byte(x) + x >>= 8 + } + b[0] |= 0x80 // Highest bit indicates binary format + return + } + + f.formatOctal(b, 0) // Last resort, just write zero + f.err = ErrFieldTooLong +} + +func (p *parser) parseOctal(b []byte) int64 { + // Because unused fields are filled with NULs, we need + // to skip leading NULs. Fields may also be padded with + // spaces or NULs. + // So we remove leading and trailing NULs and spaces to + // be sure. + b = bytes.Trim(b, " \x00") + + if len(b) == 0 { + return 0 + } + x, perr := strconv.ParseUint(p.parseString(b), 8, 64) + if perr != nil { + p.err = ErrHeader + } + return int64(x) +} + +func (f *formatter) formatOctal(b []byte, x int64) { + if !fitsInOctal(len(b), x) { + x = 0 // Last resort, just write zero + f.err = ErrFieldTooLong + } + + s := strconv.FormatInt(x, 8) + // Add leading zeros, but leave room for a NUL. + if n := len(b) - len(s) - 1; n > 0 { + s = strings.Repeat("0", n) + s + } + f.formatString(b, s) +} + +// fitsInOctal reports whether the integer x fits in a field n-bytes long +// using octal encoding with the appropriate NUL terminator. +func fitsInOctal(n int, x int64) bool { + octBits := uint(n-1) * 3 + return x >= 0 && (n >= 22 || x < 1<= 0 { + ss, sn = s[:pos], s[pos+1:] + } + + // Parse the seconds. + secs, err := strconv.ParseInt(ss, 10, 64) + if err != nil { + return time.Time{}, ErrHeader + } + if len(sn) == 0 { + return time.Unix(secs, 0), nil // No sub-second values + } + + // Parse the nanoseconds. + if strings.Trim(sn, "0123456789") != "" { + return time.Time{}, ErrHeader + } + if len(sn) < maxNanoSecondDigits { + sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad + } else { + sn = sn[:maxNanoSecondDigits] // Right truncate + } + nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed + if len(ss) > 0 && ss[0] == '-' { + return time.Unix(secs, -1*nsecs), nil // Negative correction + } + return time.Unix(secs, nsecs), nil +} + +// formatPAXTime converts ts into a time of the form %d.%d as described in the +// PAX specification. This function is capable of negative timestamps. +func formatPAXTime(ts time.Time) (s string) { + secs, nsecs := ts.Unix(), ts.Nanosecond() + if nsecs == 0 { + return strconv.FormatInt(secs, 10) + } + + // If seconds is negative, then perform correction. + sign := "" + if secs < 0 { + sign = "-" // Remember sign + secs = -(secs + 1) // Add a second to secs + nsecs = -(nsecs - 1E9) // Take that second away from nsecs + } + return strings.TrimRight(fmt.Sprintf("%s%d.%09d", sign, secs, nsecs), "0") +} + +// parsePAXRecord parses the input PAX record string into a key-value pair. +// If parsing is successful, it will slice off the currently read record and +// return the remainder as r. +func parsePAXRecord(s string) (k, v, r string, err error) { + // The size field ends at the first space. + sp := strings.IndexByte(s, ' ') + if sp == -1 { + return "", "", s, ErrHeader + } + + // Parse the first token as a decimal integer. + n, perr := strconv.ParseInt(s[:sp], 10, 0) // Intentionally parse as native int + if perr != nil || n < 5 || int64(len(s)) < n { + return "", "", s, ErrHeader + } + + // Extract everything between the space and the final newline. + rec, nl, rem := s[sp+1:n-1], s[n-1:n], s[n:] + if nl != "\n" { + return "", "", s, ErrHeader + } + + // The first equals separates the key from the value. + eq := strings.IndexByte(rec, '=') + if eq == -1 { + return "", "", s, ErrHeader + } + k, v = rec[:eq], rec[eq+1:] + + if !validPAXRecord(k, v) { + return "", "", s, ErrHeader + } + return k, v, rem, nil +} + +// formatPAXRecord formats a single PAX record, prefixing it with the +// appropriate length. +func formatPAXRecord(k, v string) (string, error) { + if !validPAXRecord(k, v) { + return "", ErrHeader + } + + const padding = 3 // Extra padding for ' ', '=', and '\n' + size := len(k) + len(v) + padding + size += len(strconv.Itoa(size)) + record := strconv.Itoa(size) + " " + k + "=" + v + "\n" + + // Final adjustment if adding size field increased the record size. + if len(record) != size { + size = len(record) + record = strconv.Itoa(size) + " " + k + "=" + v + "\n" + } + return record, nil +} + +// validPAXRecord reports whether the key-value pair is valid where each +// record is formatted as: +// "%d %s=%s\n" % (size, key, value) +// +// Keys and values should be UTF-8, but the number of bad writers out there +// forces us to be a more liberal. +// Thus, we only reject all keys with NUL, and only reject NULs in values +// for the PAX version of the USTAR string fields. +// The key must not contain an '=' character. +func validPAXRecord(k, v string) bool { + if k == "" || strings.IndexByte(k, '=') >= 0 { + return false + } + switch k { + case paxPath, paxLinkpath, paxUname, paxGname: + return !hasNUL(v) + default: + return !hasNUL(k) + } +} diff --git a/vendor/github.com/vbatts/tar-split/archive/tar/writer.go b/vendor/github.com/vbatts/tar-split/archive/tar/writer.go new file mode 100644 index 0000000000..e80498d03e --- /dev/null +++ b/vendor/github.com/vbatts/tar-split/archive/tar/writer.go @@ -0,0 +1,653 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar + +import ( + "fmt" + "io" + "path" + "sort" + "strings" + "time" +) + +// Writer provides sequential writing of a tar archive. +// Write.WriteHeader begins a new file with the provided Header, +// and then Writer can be treated as an io.Writer to supply that file's data. +type Writer struct { + w io.Writer + pad int64 // Amount of padding to write after current file entry + curr fileWriter // Writer for current file entry + hdr Header // Shallow copy of Header that is safe for mutations + blk block // Buffer to use as temporary local storage + + // err is a persistent error. + // It is only the responsibility of every exported method of Writer to + // ensure that this error is sticky. + err error +} + +// NewWriter creates a new Writer writing to w. +func NewWriter(w io.Writer) *Writer { + return &Writer{w: w, curr: ®FileWriter{w, 0}} +} + +type fileWriter interface { + io.Writer + fileState + + ReadFrom(io.Reader) (int64, error) +} + +// Flush finishes writing the current file's block padding. +// The current file must be fully written before Flush can be called. +// +// This is unnecessary as the next call to WriteHeader or Close +// will implicitly flush out the file's padding. +func (tw *Writer) Flush() error { + if tw.err != nil { + return tw.err + } + if nb := tw.curr.LogicalRemaining(); nb > 0 { + return fmt.Errorf("archive/tar: missed writing %d bytes", nb) + } + if _, tw.err = tw.w.Write(zeroBlock[:tw.pad]); tw.err != nil { + return tw.err + } + tw.pad = 0 + return nil +} + +// WriteHeader writes hdr and prepares to accept the file's contents. +// The Header.Size determines how many bytes can be written for the next file. +// If the current file is not fully written, then this returns an error. +// This implicitly flushes any padding necessary before writing the header. +func (tw *Writer) WriteHeader(hdr *Header) error { + if err := tw.Flush(); err != nil { + return err + } + tw.hdr = *hdr // Shallow copy of Header + + // Avoid usage of the legacy TypeRegA flag, and automatically promote + // it to use TypeReg or TypeDir. + if tw.hdr.Typeflag == TypeRegA { + if strings.HasSuffix(tw.hdr.Name, "/") { + tw.hdr.Typeflag = TypeDir + } else { + tw.hdr.Typeflag = TypeReg + } + } + + // Round ModTime and ignore AccessTime and ChangeTime unless + // the format is explicitly chosen. + // This ensures nominal usage of WriteHeader (without specifying the format) + // does not always result in the PAX format being chosen, which + // causes a 1KiB increase to every header. + if tw.hdr.Format == FormatUnknown { + tw.hdr.ModTime = tw.hdr.ModTime.Round(time.Second) + tw.hdr.AccessTime = time.Time{} + tw.hdr.ChangeTime = time.Time{} + } + + allowedFormats, paxHdrs, err := tw.hdr.allowedFormats() + switch { + case allowedFormats.has(FormatUSTAR): + tw.err = tw.writeUSTARHeader(&tw.hdr) + return tw.err + case allowedFormats.has(FormatPAX): + tw.err = tw.writePAXHeader(&tw.hdr, paxHdrs) + return tw.err + case allowedFormats.has(FormatGNU): + tw.err = tw.writeGNUHeader(&tw.hdr) + return tw.err + default: + return err // Non-fatal error + } +} + +func (tw *Writer) writeUSTARHeader(hdr *Header) error { + // Check if we can use USTAR prefix/suffix splitting. + var namePrefix string + if prefix, suffix, ok := splitUSTARPath(hdr.Name); ok { + namePrefix, hdr.Name = prefix, suffix + } + + // Pack the main header. + var f formatter + blk := tw.templateV7Plus(hdr, f.formatString, f.formatOctal) + f.formatString(blk.USTAR().Prefix(), namePrefix) + blk.SetFormat(FormatUSTAR) + if f.err != nil { + return f.err // Should never happen since header is validated + } + return tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag) +} + +func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error { + realName, realSize := hdr.Name, hdr.Size + + // TODO(dsnet): Re-enable this when adding sparse support. + // See https://golang.org/issue/22735 + /* + // Handle sparse files. + var spd sparseDatas + var spb []byte + if len(hdr.SparseHoles) > 0 { + sph := append([]sparseEntry{}, hdr.SparseHoles...) // Copy sparse map + sph = alignSparseEntries(sph, hdr.Size) + spd = invertSparseEntries(sph, hdr.Size) + + // Format the sparse map. + hdr.Size = 0 // Replace with encoded size + spb = append(strconv.AppendInt(spb, int64(len(spd)), 10), '\n') + for _, s := range spd { + hdr.Size += s.Length + spb = append(strconv.AppendInt(spb, s.Offset, 10), '\n') + spb = append(strconv.AppendInt(spb, s.Length, 10), '\n') + } + pad := blockPadding(int64(len(spb))) + spb = append(spb, zeroBlock[:pad]...) + hdr.Size += int64(len(spb)) // Accounts for encoded sparse map + + // Add and modify appropriate PAX records. + dir, file := path.Split(realName) + hdr.Name = path.Join(dir, "GNUSparseFile.0", file) + paxHdrs[paxGNUSparseMajor] = "1" + paxHdrs[paxGNUSparseMinor] = "0" + paxHdrs[paxGNUSparseName] = realName + paxHdrs[paxGNUSparseRealSize] = strconv.FormatInt(realSize, 10) + paxHdrs[paxSize] = strconv.FormatInt(hdr.Size, 10) + delete(paxHdrs, paxPath) // Recorded by paxGNUSparseName + } + */ + _ = realSize + + // Write PAX records to the output. + isGlobal := hdr.Typeflag == TypeXGlobalHeader + if len(paxHdrs) > 0 || isGlobal { + // Sort keys for deterministic ordering. + var keys []string + for k := range paxHdrs { + keys = append(keys, k) + } + sort.Strings(keys) + + // Write each record to a buffer. + var buf strings.Builder + for _, k := range keys { + rec, err := formatPAXRecord(k, paxHdrs[k]) + if err != nil { + return err + } + buf.WriteString(rec) + } + + // Write the extended header file. + var name string + var flag byte + if isGlobal { + name = realName + if name == "" { + name = "GlobalHead.0.0" + } + flag = TypeXGlobalHeader + } else { + dir, file := path.Split(realName) + name = path.Join(dir, "PaxHeaders.0", file) + flag = TypeXHeader + } + data := buf.String() + if err := tw.writeRawFile(name, data, flag, FormatPAX); err != nil || isGlobal { + return err // Global headers return here + } + } + + // Pack the main header. + var f formatter // Ignore errors since they are expected + fmtStr := func(b []byte, s string) { f.formatString(b, toASCII(s)) } + blk := tw.templateV7Plus(hdr, fmtStr, f.formatOctal) + blk.SetFormat(FormatPAX) + if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil { + return err + } + + // TODO(dsnet): Re-enable this when adding sparse support. + // See https://golang.org/issue/22735 + /* + // Write the sparse map and setup the sparse writer if necessary. + if len(spd) > 0 { + // Use tw.curr since the sparse map is accounted for in hdr.Size. + if _, err := tw.curr.Write(spb); err != nil { + return err + } + tw.curr = &sparseFileWriter{tw.curr, spd, 0} + } + */ + return nil +} + +func (tw *Writer) writeGNUHeader(hdr *Header) error { + // Use long-link files if Name or Linkname exceeds the field size. + const longName = "././@LongLink" + if len(hdr.Name) > nameSize { + data := hdr.Name + "\x00" + if err := tw.writeRawFile(longName, data, TypeGNULongName, FormatGNU); err != nil { + return err + } + } + if len(hdr.Linkname) > nameSize { + data := hdr.Linkname + "\x00" + if err := tw.writeRawFile(longName, data, TypeGNULongLink, FormatGNU); err != nil { + return err + } + } + + // Pack the main header. + var f formatter // Ignore errors since they are expected + var spd sparseDatas + var spb []byte + blk := tw.templateV7Plus(hdr, f.formatString, f.formatNumeric) + if !hdr.AccessTime.IsZero() { + f.formatNumeric(blk.GNU().AccessTime(), hdr.AccessTime.Unix()) + } + if !hdr.ChangeTime.IsZero() { + f.formatNumeric(blk.GNU().ChangeTime(), hdr.ChangeTime.Unix()) + } + // TODO(dsnet): Re-enable this when adding sparse support. + // See https://golang.org/issue/22735 + /* + if hdr.Typeflag == TypeGNUSparse { + sph := append([]sparseEntry{}, hdr.SparseHoles...) // Copy sparse map + sph = alignSparseEntries(sph, hdr.Size) + spd = invertSparseEntries(sph, hdr.Size) + + // Format the sparse map. + formatSPD := func(sp sparseDatas, sa sparseArray) sparseDatas { + for i := 0; len(sp) > 0 && i < sa.MaxEntries(); i++ { + f.formatNumeric(sa.Entry(i).Offset(), sp[0].Offset) + f.formatNumeric(sa.Entry(i).Length(), sp[0].Length) + sp = sp[1:] + } + if len(sp) > 0 { + sa.IsExtended()[0] = 1 + } + return sp + } + sp2 := formatSPD(spd, blk.GNU().Sparse()) + for len(sp2) > 0 { + var spHdr block + sp2 = formatSPD(sp2, spHdr.Sparse()) + spb = append(spb, spHdr[:]...) + } + + // Update size fields in the header block. + realSize := hdr.Size + hdr.Size = 0 // Encoded size; does not account for encoded sparse map + for _, s := range spd { + hdr.Size += s.Length + } + copy(blk.V7().Size(), zeroBlock[:]) // Reset field + f.formatNumeric(blk.V7().Size(), hdr.Size) + f.formatNumeric(blk.GNU().RealSize(), realSize) + } + */ + blk.SetFormat(FormatGNU) + if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil { + return err + } + + // Write the extended sparse map and setup the sparse writer if necessary. + if len(spd) > 0 { + // Use tw.w since the sparse map is not accounted for in hdr.Size. + if _, err := tw.w.Write(spb); err != nil { + return err + } + tw.curr = &sparseFileWriter{tw.curr, spd, 0} + } + return nil +} + +type ( + stringFormatter func([]byte, string) + numberFormatter func([]byte, int64) +) + +// templateV7Plus fills out the V7 fields of a block using values from hdr. +// It also fills out fields (uname, gname, devmajor, devminor) that are +// shared in the USTAR, PAX, and GNU formats using the provided formatters. +// +// The block returned is only valid until the next call to +// templateV7Plus or writeRawFile. +func (tw *Writer) templateV7Plus(hdr *Header, fmtStr stringFormatter, fmtNum numberFormatter) *block { + tw.blk.Reset() + + modTime := hdr.ModTime + if modTime.IsZero() { + modTime = time.Unix(0, 0) + } + + v7 := tw.blk.V7() + v7.TypeFlag()[0] = hdr.Typeflag + fmtStr(v7.Name(), hdr.Name) + fmtStr(v7.LinkName(), hdr.Linkname) + fmtNum(v7.Mode(), hdr.Mode) + fmtNum(v7.UID(), int64(hdr.Uid)) + fmtNum(v7.GID(), int64(hdr.Gid)) + fmtNum(v7.Size(), hdr.Size) + fmtNum(v7.ModTime(), modTime.Unix()) + + ustar := tw.blk.USTAR() + fmtStr(ustar.UserName(), hdr.Uname) + fmtStr(ustar.GroupName(), hdr.Gname) + fmtNum(ustar.DevMajor(), hdr.Devmajor) + fmtNum(ustar.DevMinor(), hdr.Devminor) + + return &tw.blk +} + +// writeRawFile writes a minimal file with the given name and flag type. +// It uses format to encode the header format and will write data as the body. +// It uses default values for all of the other fields (as BSD and GNU tar does). +func (tw *Writer) writeRawFile(name, data string, flag byte, format Format) error { + tw.blk.Reset() + + // Best effort for the filename. + name = toASCII(name) + if len(name) > nameSize { + name = name[:nameSize] + } + name = strings.TrimRight(name, "/") + + var f formatter + v7 := tw.blk.V7() + v7.TypeFlag()[0] = flag + f.formatString(v7.Name(), name) + f.formatOctal(v7.Mode(), 0) + f.formatOctal(v7.UID(), 0) + f.formatOctal(v7.GID(), 0) + f.formatOctal(v7.Size(), int64(len(data))) // Must be < 8GiB + f.formatOctal(v7.ModTime(), 0) + tw.blk.SetFormat(format) + if f.err != nil { + return f.err // Only occurs if size condition is violated + } + + // Write the header and data. + if err := tw.writeRawHeader(&tw.blk, int64(len(data)), flag); err != nil { + return err + } + _, err := io.WriteString(tw, data) + return err +} + +// writeRawHeader writes the value of blk, regardless of its value. +// It sets up the Writer such that it can accept a file of the given size. +// If the flag is a special header-only flag, then the size is treated as zero. +func (tw *Writer) writeRawHeader(blk *block, size int64, flag byte) error { + if err := tw.Flush(); err != nil { + return err + } + if _, err := tw.w.Write(blk[:]); err != nil { + return err + } + if isHeaderOnlyType(flag) { + size = 0 + } + tw.curr = ®FileWriter{tw.w, size} + tw.pad = blockPadding(size) + return nil +} + +// splitUSTARPath splits a path according to USTAR prefix and suffix rules. +// If the path is not splittable, then it will return ("", "", false). +func splitUSTARPath(name string) (prefix, suffix string, ok bool) { + length := len(name) + if length <= nameSize || !isASCII(name) { + return "", "", false + } else if length > prefixSize+1 { + length = prefixSize + 1 + } else if name[length-1] == '/' { + length-- + } + + i := strings.LastIndex(name[:length], "/") + nlen := len(name) - i - 1 // nlen is length of suffix + plen := i // plen is length of prefix + if i <= 0 || nlen > nameSize || nlen == 0 || plen > prefixSize { + return "", "", false + } + return name[:i], name[i+1:], true +} + +// Write writes to the current file in the tar archive. +// Write returns the error ErrWriteTooLong if more than +// Header.Size bytes are written after WriteHeader. +// +// Calling Write on special types like TypeLink, TypeSymlink, TypeChar, +// TypeBlock, TypeDir, and TypeFifo returns (0, ErrWriteTooLong) regardless +// of what the Header.Size claims. +func (tw *Writer) Write(b []byte) (int, error) { + if tw.err != nil { + return 0, tw.err + } + n, err := tw.curr.Write(b) + if err != nil && err != ErrWriteTooLong { + tw.err = err + } + return n, err +} + +// readFrom populates the content of the current file by reading from r. +// The bytes read must match the number of remaining bytes in the current file. +// +// If the current file is sparse and r is an io.ReadSeeker, +// then readFrom uses Seek to skip past holes defined in Header.SparseHoles, +// assuming that skipped regions are all NULs. +// This always reads the last byte to ensure r is the right size. +// +// TODO(dsnet): Re-export this when adding sparse file support. +// See https://golang.org/issue/22735 +func (tw *Writer) readFrom(r io.Reader) (int64, error) { + if tw.err != nil { + return 0, tw.err + } + n, err := tw.curr.ReadFrom(r) + if err != nil && err != ErrWriteTooLong { + tw.err = err + } + return n, err +} + +// Close closes the tar archive by flushing the padding, and writing the footer. +// If the current file (from a prior call to WriteHeader) is not fully written, +// then this returns an error. +func (tw *Writer) Close() error { + if tw.err == ErrWriteAfterClose { + return nil + } + if tw.err != nil { + return tw.err + } + + // Trailer: two zero blocks. + err := tw.Flush() + for i := 0; i < 2 && err == nil; i++ { + _, err = tw.w.Write(zeroBlock[:]) + } + + // Ensure all future actions are invalid. + tw.err = ErrWriteAfterClose + return err // Report IO errors +} + +// regFileWriter is a fileWriter for writing data to a regular file entry. +type regFileWriter struct { + w io.Writer // Underlying Writer + nb int64 // Number of remaining bytes to write +} + +func (fw *regFileWriter) Write(b []byte) (n int, err error) { + overwrite := int64(len(b)) > fw.nb + if overwrite { + b = b[:fw.nb] + } + if len(b) > 0 { + n, err = fw.w.Write(b) + fw.nb -= int64(n) + } + switch { + case err != nil: + return n, err + case overwrite: + return n, ErrWriteTooLong + default: + return n, nil + } +} + +func (fw *regFileWriter) ReadFrom(r io.Reader) (int64, error) { + return io.Copy(struct{ io.Writer }{fw}, r) +} + +func (fw regFileWriter) LogicalRemaining() int64 { + return fw.nb +} +func (fw regFileWriter) PhysicalRemaining() int64 { + return fw.nb +} + +// sparseFileWriter is a fileWriter for writing data to a sparse file entry. +type sparseFileWriter struct { + fw fileWriter // Underlying fileWriter + sp sparseDatas // Normalized list of data fragments + pos int64 // Current position in sparse file +} + +func (sw *sparseFileWriter) Write(b []byte) (n int, err error) { + overwrite := int64(len(b)) > sw.LogicalRemaining() + if overwrite { + b = b[:sw.LogicalRemaining()] + } + + b0 := b + endPos := sw.pos + int64(len(b)) + for endPos > sw.pos && err == nil { + var nf int // Bytes written in fragment + dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset() + if sw.pos < dataStart { // In a hole fragment + bf := b[:min(int64(len(b)), dataStart-sw.pos)] + nf, err = zeroWriter{}.Write(bf) + } else { // In a data fragment + bf := b[:min(int64(len(b)), dataEnd-sw.pos)] + nf, err = sw.fw.Write(bf) + } + b = b[nf:] + sw.pos += int64(nf) + if sw.pos >= dataEnd && len(sw.sp) > 1 { + sw.sp = sw.sp[1:] // Ensure last fragment always remains + } + } + + n = len(b0) - len(b) + switch { + case err == ErrWriteTooLong: + return n, errMissData // Not possible; implies bug in validation logic + case err != nil: + return n, err + case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0: + return n, errUnrefData // Not possible; implies bug in validation logic + case overwrite: + return n, ErrWriteTooLong + default: + return n, nil + } +} + +func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) { + rs, ok := r.(io.ReadSeeker) + if ok { + if _, err := rs.Seek(0, io.SeekCurrent); err != nil { + ok = false // Not all io.Seeker can really seek + } + } + if !ok { + return io.Copy(struct{ io.Writer }{sw}, r) + } + + var readLastByte bool + pos0 := sw.pos + for sw.LogicalRemaining() > 0 && !readLastByte && err == nil { + var nf int64 // Size of fragment + dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset() + if sw.pos < dataStart { // In a hole fragment + nf = dataStart - sw.pos + if sw.PhysicalRemaining() == 0 { + readLastByte = true + nf-- + } + _, err = rs.Seek(nf, io.SeekCurrent) + } else { // In a data fragment + nf = dataEnd - sw.pos + nf, err = io.CopyN(sw.fw, rs, nf) + } + sw.pos += nf + if sw.pos >= dataEnd && len(sw.sp) > 1 { + sw.sp = sw.sp[1:] // Ensure last fragment always remains + } + } + + // If the last fragment is a hole, then seek to 1-byte before EOF, and + // read a single byte to ensure the file is the right size. + if readLastByte && err == nil { + _, err = mustReadFull(rs, []byte{0}) + sw.pos++ + } + + n = sw.pos - pos0 + switch { + case err == io.EOF: + return n, io.ErrUnexpectedEOF + case err == ErrWriteTooLong: + return n, errMissData // Not possible; implies bug in validation logic + case err != nil: + return n, err + case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0: + return n, errUnrefData // Not possible; implies bug in validation logic + default: + return n, ensureEOF(rs) + } +} + +func (sw sparseFileWriter) LogicalRemaining() int64 { + return sw.sp[len(sw.sp)-1].endOffset() - sw.pos +} +func (sw sparseFileWriter) PhysicalRemaining() int64 { + return sw.fw.PhysicalRemaining() +} + +// zeroWriter may only be written with NULs, otherwise it returns errWriteHole. +type zeroWriter struct{} + +func (zeroWriter) Write(b []byte) (int, error) { + for i, c := range b { + if c != 0 { + return i, errWriteHole + } + } + return len(b), nil +} + +// ensureEOF checks whether r is at EOF, reporting ErrWriteTooLong if not so. +func ensureEOF(r io.Reader) error { + n, err := tryReadFull(r, []byte{0}) + switch { + case n > 0: + return ErrWriteTooLong + case err == io.EOF: + return nil + default: + return err + } +} diff --git a/vendor/golang.org/x/crypto/openpgp/armor/armor.go b/vendor/golang.org/x/crypto/openpgp/armor/armor.go new file mode 100644 index 0000000000..ebc87876e6 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/armor/armor.go @@ -0,0 +1,230 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is +// very similar to PEM except that it has an additional CRC checksum. +// +// Deprecated: this package is unmaintained except for security fixes. New +// applications should consider a more focused, modern alternative to OpenPGP +// for their specific task. If you are required to interoperate with OpenPGP +// systems and need a maintained package, consider a community fork. +// See https://golang.org/issue/44226. +package armor // import "golang.org/x/crypto/openpgp/armor" + +import ( + "bufio" + "bytes" + "encoding/base64" + "golang.org/x/crypto/openpgp/errors" + "io" +) + +// A Block represents an OpenPGP armored structure. +// +// The encoded form is: +// -----BEGIN Type----- +// Headers +// +// base64-encoded Bytes +// '=' base64 encoded checksum +// -----END Type----- +// where Headers is a possibly empty sequence of Key: Value lines. +// +// Since the armored data can be very large, this package presents a streaming +// interface. +type Block struct { + Type string // The type, taken from the preamble (i.e. "PGP SIGNATURE"). + Header map[string]string // Optional headers. + Body io.Reader // A Reader from which the contents can be read + lReader lineReader + oReader openpgpReader +} + +var ArmorCorrupt error = errors.StructuralError("armor invalid") + +const crc24Init = 0xb704ce +const crc24Poly = 0x1864cfb +const crc24Mask = 0xffffff + +// crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1 +func crc24(crc uint32, d []byte) uint32 { + for _, b := range d { + crc ^= uint32(b) << 16 + for i := 0; i < 8; i++ { + crc <<= 1 + if crc&0x1000000 != 0 { + crc ^= crc24Poly + } + } + } + return crc +} + +var armorStart = []byte("-----BEGIN ") +var armorEnd = []byte("-----END ") +var armorEndOfLine = []byte("-----") + +// lineReader wraps a line based reader. It watches for the end of an armor +// block and records the expected CRC value. +type lineReader struct { + in *bufio.Reader + buf []byte + eof bool + crc uint32 + crcSet bool +} + +func (l *lineReader) Read(p []byte) (n int, err error) { + if l.eof { + return 0, io.EOF + } + + if len(l.buf) > 0 { + n = copy(p, l.buf) + l.buf = l.buf[n:] + return + } + + line, isPrefix, err := l.in.ReadLine() + if err != nil { + return + } + if isPrefix { + return 0, ArmorCorrupt + } + + if bytes.HasPrefix(line, armorEnd) { + l.eof = true + return 0, io.EOF + } + + if len(line) == 5 && line[0] == '=' { + // This is the checksum line + var expectedBytes [3]byte + var m int + m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:]) + if m != 3 || err != nil { + return + } + l.crc = uint32(expectedBytes[0])<<16 | + uint32(expectedBytes[1])<<8 | + uint32(expectedBytes[2]) + + line, _, err = l.in.ReadLine() + if err != nil && err != io.EOF { + return + } + if !bytes.HasPrefix(line, armorEnd) { + return 0, ArmorCorrupt + } + + l.eof = true + l.crcSet = true + return 0, io.EOF + } + + if len(line) > 96 { + return 0, ArmorCorrupt + } + + n = copy(p, line) + bytesToSave := len(line) - n + if bytesToSave > 0 { + if cap(l.buf) < bytesToSave { + l.buf = make([]byte, 0, bytesToSave) + } + l.buf = l.buf[0:bytesToSave] + copy(l.buf, line[n:]) + } + + return +} + +// openpgpReader passes Read calls to the underlying base64 decoder, but keeps +// a running CRC of the resulting data and checks the CRC against the value +// found by the lineReader at EOF. +type openpgpReader struct { + lReader *lineReader + b64Reader io.Reader + currentCRC uint32 +} + +func (r *openpgpReader) Read(p []byte) (n int, err error) { + n, err = r.b64Reader.Read(p) + r.currentCRC = crc24(r.currentCRC, p[:n]) + + if err == io.EOF && r.lReader.crcSet && r.lReader.crc != uint32(r.currentCRC&crc24Mask) { + return 0, ArmorCorrupt + } + + return +} + +// Decode reads a PGP armored block from the given Reader. It will ignore +// leading garbage. If it doesn't find a block, it will return nil, io.EOF. The +// given Reader is not usable after calling this function: an arbitrary amount +// of data may have been read past the end of the block. +func Decode(in io.Reader) (p *Block, err error) { + r := bufio.NewReaderSize(in, 100) + var line []byte + ignoreNext := false + +TryNextBlock: + p = nil + + // Skip leading garbage + for { + ignoreThis := ignoreNext + line, ignoreNext, err = r.ReadLine() + if err != nil { + return + } + if ignoreNext || ignoreThis { + continue + } + line = bytes.TrimSpace(line) + if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasPrefix(line, armorStart) { + break + } + } + + p = new(Block) + p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)]) + p.Header = make(map[string]string) + nextIsContinuation := false + var lastKey string + + // Read headers + for { + isContinuation := nextIsContinuation + line, nextIsContinuation, err = r.ReadLine() + if err != nil { + p = nil + return + } + if isContinuation { + p.Header[lastKey] += string(line) + continue + } + line = bytes.TrimSpace(line) + if len(line) == 0 { + break + } + + i := bytes.Index(line, []byte(": ")) + if i == -1 { + goto TryNextBlock + } + lastKey = string(line[:i]) + p.Header[lastKey] = string(line[i+2:]) + } + + p.lReader.in = r + p.oReader.currentCRC = crc24Init + p.oReader.lReader = &p.lReader + p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader) + p.Body = &p.oReader + + return +} diff --git a/vendor/golang.org/x/crypto/openpgp/armor/encode.go b/vendor/golang.org/x/crypto/openpgp/armor/encode.go new file mode 100644 index 0000000000..6f07582c37 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/armor/encode.go @@ -0,0 +1,160 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package armor + +import ( + "encoding/base64" + "io" +) + +var armorHeaderSep = []byte(": ") +var blockEnd = []byte("\n=") +var newline = []byte("\n") +var armorEndOfLineOut = []byte("-----\n") + +// writeSlices writes its arguments to the given Writer. +func writeSlices(out io.Writer, slices ...[]byte) (err error) { + for _, s := range slices { + _, err = out.Write(s) + if err != nil { + return err + } + } + return +} + +// lineBreaker breaks data across several lines, all of the same byte length +// (except possibly the last). Lines are broken with a single '\n'. +type lineBreaker struct { + lineLength int + line []byte + used int + out io.Writer + haveWritten bool +} + +func newLineBreaker(out io.Writer, lineLength int) *lineBreaker { + return &lineBreaker{ + lineLength: lineLength, + line: make([]byte, lineLength), + used: 0, + out: out, + } +} + +func (l *lineBreaker) Write(b []byte) (n int, err error) { + n = len(b) + + if n == 0 { + return + } + + if l.used == 0 && l.haveWritten { + _, err = l.out.Write([]byte{'\n'}) + if err != nil { + return + } + } + + if l.used+len(b) < l.lineLength { + l.used += copy(l.line[l.used:], b) + return + } + + l.haveWritten = true + _, err = l.out.Write(l.line[0:l.used]) + if err != nil { + return + } + excess := l.lineLength - l.used + l.used = 0 + + _, err = l.out.Write(b[0:excess]) + if err != nil { + return + } + + _, err = l.Write(b[excess:]) + return +} + +func (l *lineBreaker) Close() (err error) { + if l.used > 0 { + _, err = l.out.Write(l.line[0:l.used]) + if err != nil { + return + } + } + + return +} + +// encoding keeps track of a running CRC24 over the data which has been written +// to it and outputs a OpenPGP checksum when closed, followed by an armor +// trailer. +// +// It's built into a stack of io.Writers: +// encoding -> base64 encoder -> lineBreaker -> out +type encoding struct { + out io.Writer + breaker *lineBreaker + b64 io.WriteCloser + crc uint32 + blockType []byte +} + +func (e *encoding) Write(data []byte) (n int, err error) { + e.crc = crc24(e.crc, data) + return e.b64.Write(data) +} + +func (e *encoding) Close() (err error) { + err = e.b64.Close() + if err != nil { + return + } + e.breaker.Close() + + var checksumBytes [3]byte + checksumBytes[0] = byte(e.crc >> 16) + checksumBytes[1] = byte(e.crc >> 8) + checksumBytes[2] = byte(e.crc) + + var b64ChecksumBytes [4]byte + base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:]) + + return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine) +} + +// Encode returns a WriteCloser which will encode the data written to it in +// OpenPGP armor. +func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) { + bType := []byte(blockType) + err = writeSlices(out, armorStart, bType, armorEndOfLineOut) + if err != nil { + return + } + + for k, v := range headers { + err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline) + if err != nil { + return + } + } + + _, err = out.Write(newline) + if err != nil { + return + } + + e := &encoding{ + out: out, + breaker: newLineBreaker(out, 64), + crc: crc24Init, + blockType: bType, + } + e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker) + return e, nil +} diff --git a/vendor/golang.org/x/crypto/openpgp/canonical_text.go b/vendor/golang.org/x/crypto/openpgp/canonical_text.go new file mode 100644 index 0000000000..e601e389f1 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/canonical_text.go @@ -0,0 +1,59 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package openpgp + +import "hash" + +// NewCanonicalTextHash reformats text written to it into the canonical +// form and then applies the hash h. See RFC 4880, section 5.2.1. +func NewCanonicalTextHash(h hash.Hash) hash.Hash { + return &canonicalTextHash{h, 0} +} + +type canonicalTextHash struct { + h hash.Hash + s int +} + +var newline = []byte{'\r', '\n'} + +func (cth *canonicalTextHash) Write(buf []byte) (int, error) { + start := 0 + + for i, c := range buf { + switch cth.s { + case 0: + if c == '\r' { + cth.s = 1 + } else if c == '\n' { + cth.h.Write(buf[start:i]) + cth.h.Write(newline) + start = i + 1 + } + case 1: + cth.s = 0 + } + } + + cth.h.Write(buf[start:]) + return len(buf), nil +} + +func (cth *canonicalTextHash) Sum(in []byte) []byte { + return cth.h.Sum(in) +} + +func (cth *canonicalTextHash) Reset() { + cth.h.Reset() + cth.s = 0 +} + +func (cth *canonicalTextHash) Size() int { + return cth.h.Size() +} + +func (cth *canonicalTextHash) BlockSize() int { + return cth.h.BlockSize() +} diff --git a/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go b/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go new file mode 100644 index 0000000000..84396a0896 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go @@ -0,0 +1,130 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package elgamal implements ElGamal encryption, suitable for OpenPGP, +// as specified in "A Public-Key Cryptosystem and a Signature Scheme Based on +// Discrete Logarithms," IEEE Transactions on Information Theory, v. IT-31, +// n. 4, 1985, pp. 469-472. +// +// This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it +// unsuitable for other protocols. RSA should be used in preference in any +// case. +// +// Deprecated: this package was only provided to support ElGamal encryption in +// OpenPGP. The golang.org/x/crypto/openpgp package is now deprecated (see +// https://golang.org/issue/44226), and ElGamal in the OpenPGP ecosystem has +// compatibility and security issues (see https://eprint.iacr.org/2021/923). +// Moreover, this package doesn't protect against side-channel attacks. +package elgamal // import "golang.org/x/crypto/openpgp/elgamal" + +import ( + "crypto/rand" + "crypto/subtle" + "errors" + "io" + "math/big" +) + +// PublicKey represents an ElGamal public key. +type PublicKey struct { + G, P, Y *big.Int +} + +// PrivateKey represents an ElGamal private key. +type PrivateKey struct { + PublicKey + X *big.Int +} + +// Encrypt encrypts the given message to the given public key. The result is a +// pair of integers. Errors can result from reading random, or because msg is +// too large to be encrypted to the public key. +func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err error) { + pLen := (pub.P.BitLen() + 7) / 8 + if len(msg) > pLen-11 { + err = errors.New("elgamal: message too long") + return + } + + // EM = 0x02 || PS || 0x00 || M + em := make([]byte, pLen-1) + em[0] = 2 + ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):] + err = nonZeroRandomBytes(ps, random) + if err != nil { + return + } + em[len(em)-len(msg)-1] = 0 + copy(mm, msg) + + m := new(big.Int).SetBytes(em) + + k, err := rand.Int(random, pub.P) + if err != nil { + return + } + + c1 = new(big.Int).Exp(pub.G, k, pub.P) + s := new(big.Int).Exp(pub.Y, k, pub.P) + c2 = s.Mul(s, m) + c2.Mod(c2, pub.P) + + return +} + +// Decrypt takes two integers, resulting from an ElGamal encryption, and +// returns the plaintext of the message. An error can result only if the +// ciphertext is invalid. Users should keep in mind that this is a padding +// oracle and thus, if exposed to an adaptive chosen ciphertext attack, can +// be used to break the cryptosystem. See ``Chosen Ciphertext Attacks +// Against Protocols Based on the RSA Encryption Standard PKCS #1'', Daniel +// Bleichenbacher, Advances in Cryptology (Crypto '98), +func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err error) { + s := new(big.Int).Exp(c1, priv.X, priv.P) + if s.ModInverse(s, priv.P) == nil { + return nil, errors.New("elgamal: invalid private key") + } + s.Mul(s, c2) + s.Mod(s, priv.P) + em := s.Bytes() + + firstByteIsTwo := subtle.ConstantTimeByteEq(em[0], 2) + + // The remainder of the plaintext must be a string of non-zero random + // octets, followed by a 0, followed by the message. + // lookingForIndex: 1 iff we are still looking for the zero. + // index: the offset of the first zero byte. + var lookingForIndex, index int + lookingForIndex = 1 + + for i := 1; i < len(em); i++ { + equals0 := subtle.ConstantTimeByteEq(em[i], 0) + index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index) + lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex) + } + + if firstByteIsTwo != 1 || lookingForIndex != 0 || index < 9 { + return nil, errors.New("elgamal: decryption error") + } + return em[index+1:], nil +} + +// nonZeroRandomBytes fills the given slice with non-zero random octets. +func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) { + _, err = io.ReadFull(rand, s) + if err != nil { + return + } + + for i := 0; i < len(s); i++ { + for s[i] == 0 { + _, err = io.ReadFull(rand, s[i:i+1]) + if err != nil { + return + } + } + } + + return +} diff --git a/vendor/golang.org/x/crypto/openpgp/errors/errors.go b/vendor/golang.org/x/crypto/openpgp/errors/errors.go new file mode 100644 index 0000000000..1d7a0ea05a --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/errors/errors.go @@ -0,0 +1,78 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package errors contains common error types for the OpenPGP packages. +// +// Deprecated: this package is unmaintained except for security fixes. New +// applications should consider a more focused, modern alternative to OpenPGP +// for their specific task. If you are required to interoperate with OpenPGP +// systems and need a maintained package, consider a community fork. +// See https://golang.org/issue/44226. +package errors // import "golang.org/x/crypto/openpgp/errors" + +import ( + "strconv" +) + +// A StructuralError is returned when OpenPGP data is found to be syntactically +// invalid. +type StructuralError string + +func (s StructuralError) Error() string { + return "openpgp: invalid data: " + string(s) +} + +// UnsupportedError indicates that, although the OpenPGP data is valid, it +// makes use of currently unimplemented features. +type UnsupportedError string + +func (s UnsupportedError) Error() string { + return "openpgp: unsupported feature: " + string(s) +} + +// InvalidArgumentError indicates that the caller is in error and passed an +// incorrect value. +type InvalidArgumentError string + +func (i InvalidArgumentError) Error() string { + return "openpgp: invalid argument: " + string(i) +} + +// SignatureError indicates that a syntactically valid signature failed to +// validate. +type SignatureError string + +func (b SignatureError) Error() string { + return "openpgp: invalid signature: " + string(b) +} + +type keyIncorrectError int + +func (ki keyIncorrectError) Error() string { + return "openpgp: incorrect key" +} + +var ErrKeyIncorrect error = keyIncorrectError(0) + +type unknownIssuerError int + +func (unknownIssuerError) Error() string { + return "openpgp: signature made by unknown entity" +} + +var ErrUnknownIssuer error = unknownIssuerError(0) + +type keyRevokedError int + +func (keyRevokedError) Error() string { + return "openpgp: signature made by revoked key" +} + +var ErrKeyRevoked error = keyRevokedError(0) + +type UnknownPacketTypeError uint8 + +func (upte UnknownPacketTypeError) Error() string { + return "openpgp: unknown packet type: " + strconv.Itoa(int(upte)) +} diff --git a/vendor/golang.org/x/crypto/openpgp/keys.go b/vendor/golang.org/x/crypto/openpgp/keys.go new file mode 100644 index 0000000000..faa2fb3693 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/keys.go @@ -0,0 +1,693 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package openpgp + +import ( + "crypto/rsa" + "io" + "time" + + "golang.org/x/crypto/openpgp/armor" + "golang.org/x/crypto/openpgp/errors" + "golang.org/x/crypto/openpgp/packet" +) + +// PublicKeyType is the armor type for a PGP public key. +var PublicKeyType = "PGP PUBLIC KEY BLOCK" + +// PrivateKeyType is the armor type for a PGP private key. +var PrivateKeyType = "PGP PRIVATE KEY BLOCK" + +// An Entity represents the components of an OpenPGP key: a primary public key +// (which must be a signing key), one or more identities claimed by that key, +// and zero or more subkeys, which may be encryption keys. +type Entity struct { + PrimaryKey *packet.PublicKey + PrivateKey *packet.PrivateKey + Identities map[string]*Identity // indexed by Identity.Name + Revocations []*packet.Signature + Subkeys []Subkey +} + +// An Identity represents an identity claimed by an Entity and zero or more +// assertions by other entities about that claim. +type Identity struct { + Name string // by convention, has the form "Full Name (comment) " + UserId *packet.UserId + SelfSignature *packet.Signature + Signatures []*packet.Signature +} + +// A Subkey is an additional public key in an Entity. Subkeys can be used for +// encryption. +type Subkey struct { + PublicKey *packet.PublicKey + PrivateKey *packet.PrivateKey + Sig *packet.Signature +} + +// A Key identifies a specific public key in an Entity. This is either the +// Entity's primary key or a subkey. +type Key struct { + Entity *Entity + PublicKey *packet.PublicKey + PrivateKey *packet.PrivateKey + SelfSignature *packet.Signature +} + +// A KeyRing provides access to public and private keys. +type KeyRing interface { + // KeysById returns the set of keys that have the given key id. + KeysById(id uint64) []Key + // KeysByIdAndUsage returns the set of keys with the given id + // that also meet the key usage given by requiredUsage. + // The requiredUsage is expressed as the bitwise-OR of + // packet.KeyFlag* values. + KeysByIdUsage(id uint64, requiredUsage byte) []Key + // DecryptionKeys returns all private keys that are valid for + // decryption. + DecryptionKeys() []Key +} + +// primaryIdentity returns the Identity marked as primary or the first identity +// if none are so marked. +func (e *Entity) primaryIdentity() *Identity { + var firstIdentity *Identity + for _, ident := range e.Identities { + if firstIdentity == nil { + firstIdentity = ident + } + if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { + return ident + } + } + return firstIdentity +} + +// encryptionKey returns the best candidate Key for encrypting a message to the +// given Entity. +func (e *Entity) encryptionKey(now time.Time) (Key, bool) { + candidateSubkey := -1 + + // Iterate the keys to find the newest key + var maxTime time.Time + for i, subkey := range e.Subkeys { + if subkey.Sig.FlagsValid && + subkey.Sig.FlagEncryptCommunications && + subkey.PublicKey.PubKeyAlgo.CanEncrypt() && + !subkey.Sig.KeyExpired(now) && + (maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) { + candidateSubkey = i + maxTime = subkey.Sig.CreationTime + } + } + + if candidateSubkey != -1 { + subkey := e.Subkeys[candidateSubkey] + return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true + } + + // If we don't have any candidate subkeys for encryption and + // the primary key doesn't have any usage metadata then we + // assume that the primary key is ok. Or, if the primary key is + // marked as ok to encrypt to, then we can obviously use it. + i := e.primaryIdentity() + if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications && + e.PrimaryKey.PubKeyAlgo.CanEncrypt() && + !i.SelfSignature.KeyExpired(now) { + return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true + } + + // This Entity appears to be signing only. + return Key{}, false +} + +// signingKey return the best candidate Key for signing a message with this +// Entity. +func (e *Entity) signingKey(now time.Time) (Key, bool) { + candidateSubkey := -1 + + for i, subkey := range e.Subkeys { + if subkey.Sig.FlagsValid && + subkey.Sig.FlagSign && + subkey.PublicKey.PubKeyAlgo.CanSign() && + !subkey.Sig.KeyExpired(now) { + candidateSubkey = i + break + } + } + + if candidateSubkey != -1 { + subkey := e.Subkeys[candidateSubkey] + return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true + } + + // If we have no candidate subkey then we assume that it's ok to sign + // with the primary key. + i := e.primaryIdentity() + if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign && + !i.SelfSignature.KeyExpired(now) { + return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true + } + + return Key{}, false +} + +// An EntityList contains one or more Entities. +type EntityList []*Entity + +// KeysById returns the set of keys that have the given key id. +func (el EntityList) KeysById(id uint64) (keys []Key) { + for _, e := range el { + if e.PrimaryKey.KeyId == id { + var selfSig *packet.Signature + for _, ident := range e.Identities { + if selfSig == nil { + selfSig = ident.SelfSignature + } else if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { + selfSig = ident.SelfSignature + break + } + } + keys = append(keys, Key{e, e.PrimaryKey, e.PrivateKey, selfSig}) + } + + for _, subKey := range e.Subkeys { + if subKey.PublicKey.KeyId == id { + keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig}) + } + } + } + return +} + +// KeysByIdAndUsage returns the set of keys with the given id that also meet +// the key usage given by requiredUsage. The requiredUsage is expressed as +// the bitwise-OR of packet.KeyFlag* values. +func (el EntityList) KeysByIdUsage(id uint64, requiredUsage byte) (keys []Key) { + for _, key := range el.KeysById(id) { + if len(key.Entity.Revocations) > 0 { + continue + } + + if key.SelfSignature.RevocationReason != nil { + continue + } + + if key.SelfSignature.FlagsValid && requiredUsage != 0 { + var usage byte + if key.SelfSignature.FlagCertify { + usage |= packet.KeyFlagCertify + } + if key.SelfSignature.FlagSign { + usage |= packet.KeyFlagSign + } + if key.SelfSignature.FlagEncryptCommunications { + usage |= packet.KeyFlagEncryptCommunications + } + if key.SelfSignature.FlagEncryptStorage { + usage |= packet.KeyFlagEncryptStorage + } + if usage&requiredUsage != requiredUsage { + continue + } + } + + keys = append(keys, key) + } + return +} + +// DecryptionKeys returns all private keys that are valid for decryption. +func (el EntityList) DecryptionKeys() (keys []Key) { + for _, e := range el { + for _, subKey := range e.Subkeys { + if subKey.PrivateKey != nil && (!subKey.Sig.FlagsValid || subKey.Sig.FlagEncryptStorage || subKey.Sig.FlagEncryptCommunications) { + keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig}) + } + } + } + return +} + +// ReadArmoredKeyRing reads one or more public/private keys from an armor keyring file. +func ReadArmoredKeyRing(r io.Reader) (EntityList, error) { + block, err := armor.Decode(r) + if err == io.EOF { + return nil, errors.InvalidArgumentError("no armored data found") + } + if err != nil { + return nil, err + } + if block.Type != PublicKeyType && block.Type != PrivateKeyType { + return nil, errors.InvalidArgumentError("expected public or private key block, got: " + block.Type) + } + + return ReadKeyRing(block.Body) +} + +// ReadKeyRing reads one or more public/private keys. Unsupported keys are +// ignored as long as at least a single valid key is found. +func ReadKeyRing(r io.Reader) (el EntityList, err error) { + packets := packet.NewReader(r) + var lastUnsupportedError error + + for { + var e *Entity + e, err = ReadEntity(packets) + if err != nil { + // TODO: warn about skipped unsupported/unreadable keys + if _, ok := err.(errors.UnsupportedError); ok { + lastUnsupportedError = err + err = readToNextPublicKey(packets) + } else if _, ok := err.(errors.StructuralError); ok { + // Skip unreadable, badly-formatted keys + lastUnsupportedError = err + err = readToNextPublicKey(packets) + } + if err == io.EOF { + err = nil + break + } + if err != nil { + el = nil + break + } + } else { + el = append(el, e) + } + } + + if len(el) == 0 && err == nil { + err = lastUnsupportedError + } + return +} + +// readToNextPublicKey reads packets until the start of the entity and leaves +// the first packet of the new entity in the Reader. +func readToNextPublicKey(packets *packet.Reader) (err error) { + var p packet.Packet + for { + p, err = packets.Next() + if err == io.EOF { + return + } else if err != nil { + if _, ok := err.(errors.UnsupportedError); ok { + err = nil + continue + } + return + } + + if pk, ok := p.(*packet.PublicKey); ok && !pk.IsSubkey { + packets.Unread(p) + return + } + } +} + +// ReadEntity reads an entity (public key, identities, subkeys etc) from the +// given Reader. +func ReadEntity(packets *packet.Reader) (*Entity, error) { + e := new(Entity) + e.Identities = make(map[string]*Identity) + + p, err := packets.Next() + if err != nil { + return nil, err + } + + var ok bool + if e.PrimaryKey, ok = p.(*packet.PublicKey); !ok { + if e.PrivateKey, ok = p.(*packet.PrivateKey); !ok { + packets.Unread(p) + return nil, errors.StructuralError("first packet was not a public/private key") + } + e.PrimaryKey = &e.PrivateKey.PublicKey + } + + if !e.PrimaryKey.PubKeyAlgo.CanSign() { + return nil, errors.StructuralError("primary key cannot be used for signatures") + } + + var revocations []*packet.Signature +EachPacket: + for { + p, err := packets.Next() + if err == io.EOF { + break + } else if err != nil { + return nil, err + } + + switch pkt := p.(type) { + case *packet.UserId: + if err := addUserID(e, packets, pkt); err != nil { + return nil, err + } + case *packet.Signature: + if pkt.SigType == packet.SigTypeKeyRevocation { + revocations = append(revocations, pkt) + } else if pkt.SigType == packet.SigTypeDirectSignature { + // TODO: RFC4880 5.2.1 permits signatures + // directly on keys (eg. to bind additional + // revocation keys). + } + // Else, ignoring the signature as it does not follow anything + // we would know to attach it to. + case *packet.PrivateKey: + if pkt.IsSubkey == false { + packets.Unread(p) + break EachPacket + } + err = addSubkey(e, packets, &pkt.PublicKey, pkt) + if err != nil { + return nil, err + } + case *packet.PublicKey: + if pkt.IsSubkey == false { + packets.Unread(p) + break EachPacket + } + err = addSubkey(e, packets, pkt, nil) + if err != nil { + return nil, err + } + default: + // we ignore unknown packets + } + } + + if len(e.Identities) == 0 { + return nil, errors.StructuralError("entity without any identities") + } + + for _, revocation := range revocations { + err = e.PrimaryKey.VerifyRevocationSignature(revocation) + if err == nil { + e.Revocations = append(e.Revocations, revocation) + } else { + // TODO: RFC 4880 5.2.3.15 defines revocation keys. + return nil, errors.StructuralError("revocation signature signed by alternate key") + } + } + + return e, nil +} + +func addUserID(e *Entity, packets *packet.Reader, pkt *packet.UserId) error { + // Make a new Identity object, that we might wind up throwing away. + // We'll only add it if we get a valid self-signature over this + // userID. + identity := new(Identity) + identity.Name = pkt.Id + identity.UserId = pkt + + for { + p, err := packets.Next() + if err == io.EOF { + break + } else if err != nil { + return err + } + + sig, ok := p.(*packet.Signature) + if !ok { + packets.Unread(p) + break + } + + if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId { + if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, e.PrimaryKey, sig); err != nil { + return errors.StructuralError("user ID self-signature invalid: " + err.Error()) + } + identity.SelfSignature = sig + e.Identities[pkt.Id] = identity + } else { + identity.Signatures = append(identity.Signatures, sig) + } + } + + return nil +} + +func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *packet.PrivateKey) error { + var subKey Subkey + subKey.PublicKey = pub + subKey.PrivateKey = priv + + for { + p, err := packets.Next() + if err == io.EOF { + break + } else if err != nil { + return errors.StructuralError("subkey signature invalid: " + err.Error()) + } + + sig, ok := p.(*packet.Signature) + if !ok { + packets.Unread(p) + break + } + + if sig.SigType != packet.SigTypeSubkeyBinding && sig.SigType != packet.SigTypeSubkeyRevocation { + return errors.StructuralError("subkey signature with wrong type") + } + + if err := e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, sig); err != nil { + return errors.StructuralError("subkey signature invalid: " + err.Error()) + } + + switch sig.SigType { + case packet.SigTypeSubkeyRevocation: + subKey.Sig = sig + case packet.SigTypeSubkeyBinding: + + if shouldReplaceSubkeySig(subKey.Sig, sig) { + subKey.Sig = sig + } + } + } + + if subKey.Sig == nil { + return errors.StructuralError("subkey packet not followed by signature") + } + + e.Subkeys = append(e.Subkeys, subKey) + + return nil +} + +func shouldReplaceSubkeySig(existingSig, potentialNewSig *packet.Signature) bool { + if potentialNewSig == nil { + return false + } + + if existingSig == nil { + return true + } + + if existingSig.SigType == packet.SigTypeSubkeyRevocation { + return false // never override a revocation signature + } + + return potentialNewSig.CreationTime.After(existingSig.CreationTime) +} + +const defaultRSAKeyBits = 2048 + +// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a +// single identity composed of the given full name, comment and email, any of +// which may be empty but must not contain any of "()<>\x00". +// If config is nil, sensible defaults will be used. +func NewEntity(name, comment, email string, config *packet.Config) (*Entity, error) { + creationTime := config.Now() + + bits := defaultRSAKeyBits + if config != nil && config.RSABits != 0 { + bits = config.RSABits + } + + uid := packet.NewUserId(name, comment, email) + if uid == nil { + return nil, errors.InvalidArgumentError("user id field contained invalid characters") + } + signingPriv, err := rsa.GenerateKey(config.Random(), bits) + if err != nil { + return nil, err + } + encryptingPriv, err := rsa.GenerateKey(config.Random(), bits) + if err != nil { + return nil, err + } + + e := &Entity{ + PrimaryKey: packet.NewRSAPublicKey(creationTime, &signingPriv.PublicKey), + PrivateKey: packet.NewRSAPrivateKey(creationTime, signingPriv), + Identities: make(map[string]*Identity), + } + isPrimaryId := true + e.Identities[uid.Id] = &Identity{ + Name: uid.Id, + UserId: uid, + SelfSignature: &packet.Signature{ + CreationTime: creationTime, + SigType: packet.SigTypePositiveCert, + PubKeyAlgo: packet.PubKeyAlgoRSA, + Hash: config.Hash(), + IsPrimaryId: &isPrimaryId, + FlagsValid: true, + FlagSign: true, + FlagCertify: true, + IssuerKeyId: &e.PrimaryKey.KeyId, + }, + } + err = e.Identities[uid.Id].SelfSignature.SignUserId(uid.Id, e.PrimaryKey, e.PrivateKey, config) + if err != nil { + return nil, err + } + + // If the user passes in a DefaultHash via packet.Config, + // set the PreferredHash for the SelfSignature. + if config != nil && config.DefaultHash != 0 { + e.Identities[uid.Id].SelfSignature.PreferredHash = []uint8{hashToHashId(config.DefaultHash)} + } + + // Likewise for DefaultCipher. + if config != nil && config.DefaultCipher != 0 { + e.Identities[uid.Id].SelfSignature.PreferredSymmetric = []uint8{uint8(config.DefaultCipher)} + } + + e.Subkeys = make([]Subkey, 1) + e.Subkeys[0] = Subkey{ + PublicKey: packet.NewRSAPublicKey(creationTime, &encryptingPriv.PublicKey), + PrivateKey: packet.NewRSAPrivateKey(creationTime, encryptingPriv), + Sig: &packet.Signature{ + CreationTime: creationTime, + SigType: packet.SigTypeSubkeyBinding, + PubKeyAlgo: packet.PubKeyAlgoRSA, + Hash: config.Hash(), + FlagsValid: true, + FlagEncryptStorage: true, + FlagEncryptCommunications: true, + IssuerKeyId: &e.PrimaryKey.KeyId, + }, + } + e.Subkeys[0].PublicKey.IsSubkey = true + e.Subkeys[0].PrivateKey.IsSubkey = true + err = e.Subkeys[0].Sig.SignKey(e.Subkeys[0].PublicKey, e.PrivateKey, config) + if err != nil { + return nil, err + } + return e, nil +} + +// SerializePrivate serializes an Entity, including private key material, but +// excluding signatures from other entities, to the given Writer. +// Identities and subkeys are re-signed in case they changed since NewEntry. +// If config is nil, sensible defaults will be used. +func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error) { + err = e.PrivateKey.Serialize(w) + if err != nil { + return + } + for _, ident := range e.Identities { + err = ident.UserId.Serialize(w) + if err != nil { + return + } + err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey, config) + if err != nil { + return + } + err = ident.SelfSignature.Serialize(w) + if err != nil { + return + } + } + for _, subkey := range e.Subkeys { + err = subkey.PrivateKey.Serialize(w) + if err != nil { + return + } + err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config) + if err != nil { + return + } + err = subkey.Sig.Serialize(w) + if err != nil { + return + } + } + return nil +} + +// Serialize writes the public part of the given Entity to w, including +// signatures from other entities. No private key material will be output. +func (e *Entity) Serialize(w io.Writer) error { + err := e.PrimaryKey.Serialize(w) + if err != nil { + return err + } + for _, ident := range e.Identities { + err = ident.UserId.Serialize(w) + if err != nil { + return err + } + err = ident.SelfSignature.Serialize(w) + if err != nil { + return err + } + for _, sig := range ident.Signatures { + err = sig.Serialize(w) + if err != nil { + return err + } + } + } + for _, subkey := range e.Subkeys { + err = subkey.PublicKey.Serialize(w) + if err != nil { + return err + } + err = subkey.Sig.Serialize(w) + if err != nil { + return err + } + } + return nil +} + +// SignIdentity adds a signature to e, from signer, attesting that identity is +// associated with e. The provided identity must already be an element of +// e.Identities and the private key of signer must have been decrypted if +// necessary. +// If config is nil, sensible defaults will be used. +func (e *Entity) SignIdentity(identity string, signer *Entity, config *packet.Config) error { + if signer.PrivateKey == nil { + return errors.InvalidArgumentError("signing Entity must have a private key") + } + if signer.PrivateKey.Encrypted { + return errors.InvalidArgumentError("signing Entity's private key must be decrypted") + } + ident, ok := e.Identities[identity] + if !ok { + return errors.InvalidArgumentError("given identity string not found in Entity") + } + + sig := &packet.Signature{ + SigType: packet.SigTypeGenericCert, + PubKeyAlgo: signer.PrivateKey.PubKeyAlgo, + Hash: config.Hash(), + CreationTime: config.Now(), + IssuerKeyId: &signer.PrivateKey.KeyId, + } + if err := sig.SignUserId(identity, e.PrimaryKey, signer.PrivateKey, config); err != nil { + return err + } + ident.Signatures = append(ident.Signatures, sig) + return nil +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/compressed.go b/vendor/golang.org/x/crypto/openpgp/packet/compressed.go new file mode 100644 index 0000000000..e8f0b5caa7 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/compressed.go @@ -0,0 +1,123 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "compress/bzip2" + "compress/flate" + "compress/zlib" + "golang.org/x/crypto/openpgp/errors" + "io" + "strconv" +) + +// Compressed represents a compressed OpenPGP packet. The decompressed contents +// will contain more OpenPGP packets. See RFC 4880, section 5.6. +type Compressed struct { + Body io.Reader +} + +const ( + NoCompression = flate.NoCompression + BestSpeed = flate.BestSpeed + BestCompression = flate.BestCompression + DefaultCompression = flate.DefaultCompression +) + +// CompressionConfig contains compressor configuration settings. +type CompressionConfig struct { + // Level is the compression level to use. It must be set to + // between -1 and 9, with -1 causing the compressor to use the + // default compression level, 0 causing the compressor to use + // no compression and 1 to 9 representing increasing (better, + // slower) compression levels. If Level is less than -1 or + // more then 9, a non-nil error will be returned during + // encryption. See the constants above for convenient common + // settings for Level. + Level int +} + +func (c *Compressed) parse(r io.Reader) error { + var buf [1]byte + _, err := readFull(r, buf[:]) + if err != nil { + return err + } + + switch buf[0] { + case 1: + c.Body = flate.NewReader(r) + case 2: + c.Body, err = zlib.NewReader(r) + case 3: + c.Body = bzip2.NewReader(r) + default: + err = errors.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0]))) + } + + return err +} + +// compressedWriterCloser represents the serialized compression stream +// header and the compressor. Its Close() method ensures that both the +// compressor and serialized stream header are closed. Its Write() +// method writes to the compressor. +type compressedWriteCloser struct { + sh io.Closer // Stream Header + c io.WriteCloser // Compressor +} + +func (cwc compressedWriteCloser) Write(p []byte) (int, error) { + return cwc.c.Write(p) +} + +func (cwc compressedWriteCloser) Close() (err error) { + err = cwc.c.Close() + if err != nil { + return err + } + + return cwc.sh.Close() +} + +// SerializeCompressed serializes a compressed data packet to w and +// returns a WriteCloser to which the literal data packets themselves +// can be written and which MUST be closed on completion. If cc is +// nil, sensible defaults will be used to configure the compression +// algorithm. +func SerializeCompressed(w io.WriteCloser, algo CompressionAlgo, cc *CompressionConfig) (literaldata io.WriteCloser, err error) { + compressed, err := serializeStreamHeader(w, packetTypeCompressed) + if err != nil { + return + } + + _, err = compressed.Write([]byte{uint8(algo)}) + if err != nil { + return + } + + level := DefaultCompression + if cc != nil { + level = cc.Level + } + + var compressor io.WriteCloser + switch algo { + case CompressionZIP: + compressor, err = flate.NewWriter(compressed, level) + case CompressionZLIB: + compressor, err = zlib.NewWriterLevel(compressed, level) + default: + s := strconv.Itoa(int(algo)) + err = errors.UnsupportedError("Unsupported compression algorithm: " + s) + } + if err != nil { + return + } + + literaldata = compressedWriteCloser{compressed, compressor} + + return +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/config.go b/vendor/golang.org/x/crypto/openpgp/packet/config.go new file mode 100644 index 0000000000..c76eecc963 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/config.go @@ -0,0 +1,91 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "crypto" + "crypto/rand" + "io" + "time" +) + +// Config collects a number of parameters along with sensible defaults. +// A nil *Config is valid and results in all default values. +type Config struct { + // Rand provides the source of entropy. + // If nil, the crypto/rand Reader is used. + Rand io.Reader + // DefaultHash is the default hash function to be used. + // If zero, SHA-256 is used. + DefaultHash crypto.Hash + // DefaultCipher is the cipher to be used. + // If zero, AES-128 is used. + DefaultCipher CipherFunction + // Time returns the current time as the number of seconds since the + // epoch. If Time is nil, time.Now is used. + Time func() time.Time + // DefaultCompressionAlgo is the compression algorithm to be + // applied to the plaintext before encryption. If zero, no + // compression is done. + DefaultCompressionAlgo CompressionAlgo + // CompressionConfig configures the compression settings. + CompressionConfig *CompressionConfig + // S2KCount is only used for symmetric encryption. It + // determines the strength of the passphrase stretching when + // the said passphrase is hashed to produce a key. S2KCount + // should be between 1024 and 65011712, inclusive. If Config + // is nil or S2KCount is 0, the value 65536 used. Not all + // values in the above range can be represented. S2KCount will + // be rounded up to the next representable value if it cannot + // be encoded exactly. When set, it is strongly encrouraged to + // use a value that is at least 65536. See RFC 4880 Section + // 3.7.1.3. + S2KCount int + // RSABits is the number of bits in new RSA keys made with NewEntity. + // If zero, then 2048 bit keys are created. + RSABits int +} + +func (c *Config) Random() io.Reader { + if c == nil || c.Rand == nil { + return rand.Reader + } + return c.Rand +} + +func (c *Config) Hash() crypto.Hash { + if c == nil || uint(c.DefaultHash) == 0 { + return crypto.SHA256 + } + return c.DefaultHash +} + +func (c *Config) Cipher() CipherFunction { + if c == nil || uint8(c.DefaultCipher) == 0 { + return CipherAES128 + } + return c.DefaultCipher +} + +func (c *Config) Now() time.Time { + if c == nil || c.Time == nil { + return time.Now() + } + return c.Time() +} + +func (c *Config) Compression() CompressionAlgo { + if c == nil { + return CompressionNone + } + return c.DefaultCompressionAlgo +} + +func (c *Config) PasswordHashIterations() int { + if c == nil || c.S2KCount == 0 { + return 0 + } + return c.S2KCount +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/encrypted_key.go b/vendor/golang.org/x/crypto/openpgp/packet/encrypted_key.go new file mode 100644 index 0000000000..6d7639722c --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/encrypted_key.go @@ -0,0 +1,208 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "crypto" + "crypto/rsa" + "encoding/binary" + "io" + "math/big" + "strconv" + + "golang.org/x/crypto/openpgp/elgamal" + "golang.org/x/crypto/openpgp/errors" +) + +const encryptedKeyVersion = 3 + +// EncryptedKey represents a public-key encrypted session key. See RFC 4880, +// section 5.1. +type EncryptedKey struct { + KeyId uint64 + Algo PublicKeyAlgorithm + CipherFunc CipherFunction // only valid after a successful Decrypt + Key []byte // only valid after a successful Decrypt + + encryptedMPI1, encryptedMPI2 parsedMPI +} + +func (e *EncryptedKey) parse(r io.Reader) (err error) { + var buf [10]byte + _, err = readFull(r, buf[:]) + if err != nil { + return + } + if buf[0] != encryptedKeyVersion { + return errors.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0]))) + } + e.KeyId = binary.BigEndian.Uint64(buf[1:9]) + e.Algo = PublicKeyAlgorithm(buf[9]) + switch e.Algo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r) + if err != nil { + return + } + case PubKeyAlgoElGamal: + e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r) + if err != nil { + return + } + e.encryptedMPI2.bytes, e.encryptedMPI2.bitLength, err = readMPI(r) + if err != nil { + return + } + } + _, err = consumeAll(r) + return +} + +func checksumKeyMaterial(key []byte) uint16 { + var checksum uint16 + for _, v := range key { + checksum += uint16(v) + } + return checksum +} + +// Decrypt decrypts an encrypted session key with the given private key. The +// private key must have been decrypted first. +// If config is nil, sensible defaults will be used. +func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error { + var err error + var b []byte + + // TODO(agl): use session key decryption routines here to avoid + // padding oracle attacks. + switch priv.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + // Supports both *rsa.PrivateKey and crypto.Decrypter + k := priv.PrivateKey.(crypto.Decrypter) + b, err = k.Decrypt(config.Random(), padToKeySize(k.Public().(*rsa.PublicKey), e.encryptedMPI1.bytes), nil) + case PubKeyAlgoElGamal: + c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes) + c2 := new(big.Int).SetBytes(e.encryptedMPI2.bytes) + b, err = elgamal.Decrypt(priv.PrivateKey.(*elgamal.PrivateKey), c1, c2) + default: + err = errors.InvalidArgumentError("cannot decrypted encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo))) + } + + if err != nil { + return err + } + + e.CipherFunc = CipherFunction(b[0]) + e.Key = b[1 : len(b)-2] + expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1]) + checksum := checksumKeyMaterial(e.Key) + if checksum != expectedChecksum { + return errors.StructuralError("EncryptedKey checksum incorrect") + } + + return nil +} + +// Serialize writes the encrypted key packet, e, to w. +func (e *EncryptedKey) Serialize(w io.Writer) error { + var mpiLen int + switch e.Algo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + mpiLen = 2 + len(e.encryptedMPI1.bytes) + case PubKeyAlgoElGamal: + mpiLen = 2 + len(e.encryptedMPI1.bytes) + 2 + len(e.encryptedMPI2.bytes) + default: + return errors.InvalidArgumentError("don't know how to serialize encrypted key type " + strconv.Itoa(int(e.Algo))) + } + + serializeHeader(w, packetTypeEncryptedKey, 1 /* version */ +8 /* key id */ +1 /* algo */ +mpiLen) + + w.Write([]byte{encryptedKeyVersion}) + binary.Write(w, binary.BigEndian, e.KeyId) + w.Write([]byte{byte(e.Algo)}) + + switch e.Algo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + writeMPIs(w, e.encryptedMPI1) + case PubKeyAlgoElGamal: + writeMPIs(w, e.encryptedMPI1, e.encryptedMPI2) + default: + panic("internal error") + } + + return nil +} + +// SerializeEncryptedKey serializes an encrypted key packet to w that contains +// key, encrypted to pub. +// If config is nil, sensible defaults will be used. +func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error { + var buf [10]byte + buf[0] = encryptedKeyVersion + binary.BigEndian.PutUint64(buf[1:9], pub.KeyId) + buf[9] = byte(pub.PubKeyAlgo) + + keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */) + keyBlock[0] = byte(cipherFunc) + copy(keyBlock[1:], key) + checksum := checksumKeyMaterial(key) + keyBlock[1+len(key)] = byte(checksum >> 8) + keyBlock[1+len(key)+1] = byte(checksum) + + switch pub.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + return serializeEncryptedKeyRSA(w, config.Random(), buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) + case PubKeyAlgoElGamal: + return serializeEncryptedKeyElGamal(w, config.Random(), buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock) + case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly: + return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) + } + + return errors.UnsupportedError("encrypting a key to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) +} + +func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub *rsa.PublicKey, keyBlock []byte) error { + cipherText, err := rsa.EncryptPKCS1v15(rand, pub, keyBlock) + if err != nil { + return errors.InvalidArgumentError("RSA encryption failed: " + err.Error()) + } + + packetLen := 10 /* header length */ + 2 /* mpi size */ + len(cipherText) + + err = serializeHeader(w, packetTypeEncryptedKey, packetLen) + if err != nil { + return err + } + _, err = w.Write(header[:]) + if err != nil { + return err + } + return writeMPI(w, 8*uint16(len(cipherText)), cipherText) +} + +func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, pub *elgamal.PublicKey, keyBlock []byte) error { + c1, c2, err := elgamal.Encrypt(rand, pub, keyBlock) + if err != nil { + return errors.InvalidArgumentError("ElGamal encryption failed: " + err.Error()) + } + + packetLen := 10 /* header length */ + packetLen += 2 /* mpi size */ + (c1.BitLen()+7)/8 + packetLen += 2 /* mpi size */ + (c2.BitLen()+7)/8 + + err = serializeHeader(w, packetTypeEncryptedKey, packetLen) + if err != nil { + return err + } + _, err = w.Write(header[:]) + if err != nil { + return err + } + err = writeBig(w, c1) + if err != nil { + return err + } + return writeBig(w, c2) +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/literal.go b/vendor/golang.org/x/crypto/openpgp/packet/literal.go new file mode 100644 index 0000000000..1a9ec6e51e --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/literal.go @@ -0,0 +1,89 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "encoding/binary" + "io" +) + +// LiteralData represents an encrypted file. See RFC 4880, section 5.9. +type LiteralData struct { + IsBinary bool + FileName string + Time uint32 // Unix epoch time. Either creation time or modification time. 0 means undefined. + Body io.Reader +} + +// ForEyesOnly returns whether the contents of the LiteralData have been marked +// as especially sensitive. +func (l *LiteralData) ForEyesOnly() bool { + return l.FileName == "_CONSOLE" +} + +func (l *LiteralData) parse(r io.Reader) (err error) { + var buf [256]byte + + _, err = readFull(r, buf[:2]) + if err != nil { + return + } + + l.IsBinary = buf[0] == 'b' + fileNameLen := int(buf[1]) + + _, err = readFull(r, buf[:fileNameLen]) + if err != nil { + return + } + + l.FileName = string(buf[:fileNameLen]) + + _, err = readFull(r, buf[:4]) + if err != nil { + return + } + + l.Time = binary.BigEndian.Uint32(buf[:4]) + l.Body = r + return +} + +// SerializeLiteral serializes a literal data packet to w and returns a +// WriteCloser to which the data itself can be written and which MUST be closed +// on completion. The fileName is truncated to 255 bytes. +func SerializeLiteral(w io.WriteCloser, isBinary bool, fileName string, time uint32) (plaintext io.WriteCloser, err error) { + var buf [4]byte + buf[0] = 't' + if isBinary { + buf[0] = 'b' + } + if len(fileName) > 255 { + fileName = fileName[:255] + } + buf[1] = byte(len(fileName)) + + inner, err := serializeStreamHeader(w, packetTypeLiteralData) + if err != nil { + return + } + + _, err = inner.Write(buf[:2]) + if err != nil { + return + } + _, err = inner.Write([]byte(fileName)) + if err != nil { + return + } + binary.BigEndian.PutUint32(buf[:], time) + _, err = inner.Write(buf[:]) + if err != nil { + return + } + + plaintext = inner + return +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/ocfb.go b/vendor/golang.org/x/crypto/openpgp/packet/ocfb.go new file mode 100644 index 0000000000..ce2a33a547 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/ocfb.go @@ -0,0 +1,143 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// OpenPGP CFB Mode. http://tools.ietf.org/html/rfc4880#section-13.9 + +package packet + +import ( + "crypto/cipher" +) + +type ocfbEncrypter struct { + b cipher.Block + fre []byte + outUsed int +} + +// An OCFBResyncOption determines if the "resynchronization step" of OCFB is +// performed. +type OCFBResyncOption bool + +const ( + OCFBResync OCFBResyncOption = true + OCFBNoResync OCFBResyncOption = false +) + +// NewOCFBEncrypter returns a cipher.Stream which encrypts data with OpenPGP's +// cipher feedback mode using the given cipher.Block, and an initial amount of +// ciphertext. randData must be random bytes and be the same length as the +// cipher.Block's block size. Resync determines if the "resynchronization step" +// from RFC 4880, 13.9 step 7 is performed. Different parts of OpenPGP vary on +// this point. +func NewOCFBEncrypter(block cipher.Block, randData []byte, resync OCFBResyncOption) (cipher.Stream, []byte) { + blockSize := block.BlockSize() + if len(randData) != blockSize { + return nil, nil + } + + x := &ocfbEncrypter{ + b: block, + fre: make([]byte, blockSize), + outUsed: 0, + } + prefix := make([]byte, blockSize+2) + + block.Encrypt(x.fre, x.fre) + for i := 0; i < blockSize; i++ { + prefix[i] = randData[i] ^ x.fre[i] + } + + block.Encrypt(x.fre, prefix[:blockSize]) + prefix[blockSize] = x.fre[0] ^ randData[blockSize-2] + prefix[blockSize+1] = x.fre[1] ^ randData[blockSize-1] + + if resync { + block.Encrypt(x.fre, prefix[2:]) + } else { + x.fre[0] = prefix[blockSize] + x.fre[1] = prefix[blockSize+1] + x.outUsed = 2 + } + return x, prefix +} + +func (x *ocfbEncrypter) XORKeyStream(dst, src []byte) { + for i := 0; i < len(src); i++ { + if x.outUsed == len(x.fre) { + x.b.Encrypt(x.fre, x.fre) + x.outUsed = 0 + } + + x.fre[x.outUsed] ^= src[i] + dst[i] = x.fre[x.outUsed] + x.outUsed++ + } +} + +type ocfbDecrypter struct { + b cipher.Block + fre []byte + outUsed int +} + +// NewOCFBDecrypter returns a cipher.Stream which decrypts data with OpenPGP's +// cipher feedback mode using the given cipher.Block. Prefix must be the first +// blockSize + 2 bytes of the ciphertext, where blockSize is the cipher.Block's +// block size. If an incorrect key is detected then nil is returned. On +// successful exit, blockSize+2 bytes of decrypted data are written into +// prefix. Resync determines if the "resynchronization step" from RFC 4880, +// 13.9 step 7 is performed. Different parts of OpenPGP vary on this point. +func NewOCFBDecrypter(block cipher.Block, prefix []byte, resync OCFBResyncOption) cipher.Stream { + blockSize := block.BlockSize() + if len(prefix) != blockSize+2 { + return nil + } + + x := &ocfbDecrypter{ + b: block, + fre: make([]byte, blockSize), + outUsed: 0, + } + prefixCopy := make([]byte, len(prefix)) + copy(prefixCopy, prefix) + + block.Encrypt(x.fre, x.fre) + for i := 0; i < blockSize; i++ { + prefixCopy[i] ^= x.fre[i] + } + + block.Encrypt(x.fre, prefix[:blockSize]) + prefixCopy[blockSize] ^= x.fre[0] + prefixCopy[blockSize+1] ^= x.fre[1] + + if prefixCopy[blockSize-2] != prefixCopy[blockSize] || + prefixCopy[blockSize-1] != prefixCopy[blockSize+1] { + return nil + } + + if resync { + block.Encrypt(x.fre, prefix[2:]) + } else { + x.fre[0] = prefix[blockSize] + x.fre[1] = prefix[blockSize+1] + x.outUsed = 2 + } + copy(prefix, prefixCopy) + return x +} + +func (x *ocfbDecrypter) XORKeyStream(dst, src []byte) { + for i := 0; i < len(src); i++ { + if x.outUsed == len(x.fre) { + x.b.Encrypt(x.fre, x.fre) + x.outUsed = 0 + } + + c := src[i] + dst[i] = x.fre[x.outUsed] ^ src[i] + x.fre[x.outUsed] = c + x.outUsed++ + } +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/one_pass_signature.go b/vendor/golang.org/x/crypto/openpgp/packet/one_pass_signature.go new file mode 100644 index 0000000000..1713503395 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/one_pass_signature.go @@ -0,0 +1,73 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "crypto" + "encoding/binary" + "golang.org/x/crypto/openpgp/errors" + "golang.org/x/crypto/openpgp/s2k" + "io" + "strconv" +) + +// OnePassSignature represents a one-pass signature packet. See RFC 4880, +// section 5.4. +type OnePassSignature struct { + SigType SignatureType + Hash crypto.Hash + PubKeyAlgo PublicKeyAlgorithm + KeyId uint64 + IsLast bool +} + +const onePassSignatureVersion = 3 + +func (ops *OnePassSignature) parse(r io.Reader) (err error) { + var buf [13]byte + + _, err = readFull(r, buf[:]) + if err != nil { + return + } + if buf[0] != onePassSignatureVersion { + err = errors.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0]))) + } + + var ok bool + ops.Hash, ok = s2k.HashIdToHash(buf[2]) + if !ok { + return errors.UnsupportedError("hash function: " + strconv.Itoa(int(buf[2]))) + } + + ops.SigType = SignatureType(buf[1]) + ops.PubKeyAlgo = PublicKeyAlgorithm(buf[3]) + ops.KeyId = binary.BigEndian.Uint64(buf[4:12]) + ops.IsLast = buf[12] != 0 + return +} + +// Serialize marshals the given OnePassSignature to w. +func (ops *OnePassSignature) Serialize(w io.Writer) error { + var buf [13]byte + buf[0] = onePassSignatureVersion + buf[1] = uint8(ops.SigType) + var ok bool + buf[2], ok = s2k.HashToHashId(ops.Hash) + if !ok { + return errors.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash))) + } + buf[3] = uint8(ops.PubKeyAlgo) + binary.BigEndian.PutUint64(buf[4:12], ops.KeyId) + if ops.IsLast { + buf[12] = 1 + } + + if err := serializeHeader(w, packetTypeOnePassSignature, len(buf)); err != nil { + return err + } + _, err := w.Write(buf[:]) + return err +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/opaque.go b/vendor/golang.org/x/crypto/openpgp/packet/opaque.go new file mode 100644 index 0000000000..456d807f25 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/opaque.go @@ -0,0 +1,162 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "io" + "io/ioutil" + + "golang.org/x/crypto/openpgp/errors" +) + +// OpaquePacket represents an OpenPGP packet as raw, unparsed data. This is +// useful for splitting and storing the original packet contents separately, +// handling unsupported packet types or accessing parts of the packet not yet +// implemented by this package. +type OpaquePacket struct { + // Packet type + Tag uint8 + // Reason why the packet was parsed opaquely + Reason error + // Binary contents of the packet data + Contents []byte +} + +func (op *OpaquePacket) parse(r io.Reader) (err error) { + op.Contents, err = ioutil.ReadAll(r) + return +} + +// Serialize marshals the packet to a writer in its original form, including +// the packet header. +func (op *OpaquePacket) Serialize(w io.Writer) (err error) { + err = serializeHeader(w, packetType(op.Tag), len(op.Contents)) + if err == nil { + _, err = w.Write(op.Contents) + } + return +} + +// Parse attempts to parse the opaque contents into a structure supported by +// this package. If the packet is not known then the result will be another +// OpaquePacket. +func (op *OpaquePacket) Parse() (p Packet, err error) { + hdr := bytes.NewBuffer(nil) + err = serializeHeader(hdr, packetType(op.Tag), len(op.Contents)) + if err != nil { + op.Reason = err + return op, err + } + p, err = Read(io.MultiReader(hdr, bytes.NewBuffer(op.Contents))) + if err != nil { + op.Reason = err + p = op + } + return +} + +// OpaqueReader reads OpaquePackets from an io.Reader. +type OpaqueReader struct { + r io.Reader +} + +func NewOpaqueReader(r io.Reader) *OpaqueReader { + return &OpaqueReader{r: r} +} + +// Read the next OpaquePacket. +func (or *OpaqueReader) Next() (op *OpaquePacket, err error) { + tag, _, contents, err := readHeader(or.r) + if err != nil { + return + } + op = &OpaquePacket{Tag: uint8(tag), Reason: err} + err = op.parse(contents) + if err != nil { + consumeAll(contents) + } + return +} + +// OpaqueSubpacket represents an unparsed OpenPGP subpacket, +// as found in signature and user attribute packets. +type OpaqueSubpacket struct { + SubType uint8 + Contents []byte +} + +// OpaqueSubpackets extracts opaque, unparsed OpenPGP subpackets from +// their byte representation. +func OpaqueSubpackets(contents []byte) (result []*OpaqueSubpacket, err error) { + var ( + subHeaderLen int + subPacket *OpaqueSubpacket + ) + for len(contents) > 0 { + subHeaderLen, subPacket, err = nextSubpacket(contents) + if err != nil { + break + } + result = append(result, subPacket) + contents = contents[subHeaderLen+len(subPacket.Contents):] + } + return +} + +func nextSubpacket(contents []byte) (subHeaderLen int, subPacket *OpaqueSubpacket, err error) { + // RFC 4880, section 5.2.3.1 + var subLen uint32 + if len(contents) < 1 { + goto Truncated + } + subPacket = &OpaqueSubpacket{} + switch { + case contents[0] < 192: + subHeaderLen = 2 // 1 length byte, 1 subtype byte + if len(contents) < subHeaderLen { + goto Truncated + } + subLen = uint32(contents[0]) + contents = contents[1:] + case contents[0] < 255: + subHeaderLen = 3 // 2 length bytes, 1 subtype + if len(contents) < subHeaderLen { + goto Truncated + } + subLen = uint32(contents[0]-192)<<8 + uint32(contents[1]) + 192 + contents = contents[2:] + default: + subHeaderLen = 6 // 5 length bytes, 1 subtype + if len(contents) < subHeaderLen { + goto Truncated + } + subLen = uint32(contents[1])<<24 | + uint32(contents[2])<<16 | + uint32(contents[3])<<8 | + uint32(contents[4]) + contents = contents[5:] + } + if subLen > uint32(len(contents)) || subLen == 0 { + goto Truncated + } + subPacket.SubType = contents[0] + subPacket.Contents = contents[1:subLen] + return +Truncated: + err = errors.StructuralError("subpacket truncated") + return +} + +func (osp *OpaqueSubpacket) Serialize(w io.Writer) (err error) { + buf := make([]byte, 6) + n := serializeSubpacketLength(buf, len(osp.Contents)+1) + buf[n] = osp.SubType + if _, err = w.Write(buf[:n+1]); err != nil { + return + } + _, err = w.Write(osp.Contents) + return +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/packet.go b/vendor/golang.org/x/crypto/openpgp/packet/packet.go new file mode 100644 index 0000000000..0a19794a8e --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/packet.go @@ -0,0 +1,590 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package packet implements parsing and serialization of OpenPGP packets, as +// specified in RFC 4880. +// +// Deprecated: this package is unmaintained except for security fixes. New +// applications should consider a more focused, modern alternative to OpenPGP +// for their specific task. If you are required to interoperate with OpenPGP +// systems and need a maintained package, consider a community fork. +// See https://golang.org/issue/44226. +package packet // import "golang.org/x/crypto/openpgp/packet" + +import ( + "bufio" + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/rsa" + "io" + "math/big" + "math/bits" + + "golang.org/x/crypto/cast5" + "golang.org/x/crypto/openpgp/errors" +) + +// readFull is the same as io.ReadFull except that reading zero bytes returns +// ErrUnexpectedEOF rather than EOF. +func readFull(r io.Reader, buf []byte) (n int, err error) { + n, err = io.ReadFull(r, buf) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return +} + +// readLength reads an OpenPGP length from r. See RFC 4880, section 4.2.2. +func readLength(r io.Reader) (length int64, isPartial bool, err error) { + var buf [4]byte + _, err = readFull(r, buf[:1]) + if err != nil { + return + } + switch { + case buf[0] < 192: + length = int64(buf[0]) + case buf[0] < 224: + length = int64(buf[0]-192) << 8 + _, err = readFull(r, buf[0:1]) + if err != nil { + return + } + length += int64(buf[0]) + 192 + case buf[0] < 255: + length = int64(1) << (buf[0] & 0x1f) + isPartial = true + default: + _, err = readFull(r, buf[0:4]) + if err != nil { + return + } + length = int64(buf[0])<<24 | + int64(buf[1])<<16 | + int64(buf[2])<<8 | + int64(buf[3]) + } + return +} + +// partialLengthReader wraps an io.Reader and handles OpenPGP partial lengths. +// The continuation lengths are parsed and removed from the stream and EOF is +// returned at the end of the packet. See RFC 4880, section 4.2.2.4. +type partialLengthReader struct { + r io.Reader + remaining int64 + isPartial bool +} + +func (r *partialLengthReader) Read(p []byte) (n int, err error) { + for r.remaining == 0 { + if !r.isPartial { + return 0, io.EOF + } + r.remaining, r.isPartial, err = readLength(r.r) + if err != nil { + return 0, err + } + } + + toRead := int64(len(p)) + if toRead > r.remaining { + toRead = r.remaining + } + + n, err = r.r.Read(p[:int(toRead)]) + r.remaining -= int64(n) + if n < int(toRead) && err == io.EOF { + err = io.ErrUnexpectedEOF + } + return +} + +// partialLengthWriter writes a stream of data using OpenPGP partial lengths. +// See RFC 4880, section 4.2.2.4. +type partialLengthWriter struct { + w io.WriteCloser + lengthByte [1]byte + sentFirst bool + buf []byte +} + +// RFC 4880 4.2.2.4: the first partial length MUST be at least 512 octets long. +const minFirstPartialWrite = 512 + +func (w *partialLengthWriter) Write(p []byte) (n int, err error) { + off := 0 + if !w.sentFirst { + if len(w.buf) > 0 || len(p) < minFirstPartialWrite { + off = len(w.buf) + w.buf = append(w.buf, p...) + if len(w.buf) < minFirstPartialWrite { + return len(p), nil + } + p = w.buf + w.buf = nil + } + w.sentFirst = true + } + + power := uint8(30) + for len(p) > 0 { + l := 1 << power + if len(p) < l { + power = uint8(bits.Len32(uint32(len(p)))) - 1 + l = 1 << power + } + w.lengthByte[0] = 224 + power + _, err = w.w.Write(w.lengthByte[:]) + if err == nil { + var m int + m, err = w.w.Write(p[:l]) + n += m + } + if err != nil { + if n < off { + return 0, err + } + return n - off, err + } + p = p[l:] + } + return n - off, nil +} + +func (w *partialLengthWriter) Close() error { + if len(w.buf) > 0 { + // In this case we can't send a 512 byte packet. + // Just send what we have. + p := w.buf + w.sentFirst = true + w.buf = nil + if _, err := w.Write(p); err != nil { + return err + } + } + + w.lengthByte[0] = 0 + _, err := w.w.Write(w.lengthByte[:]) + if err != nil { + return err + } + return w.w.Close() +} + +// A spanReader is an io.LimitReader, but it returns ErrUnexpectedEOF if the +// underlying Reader returns EOF before the limit has been reached. +type spanReader struct { + r io.Reader + n int64 +} + +func (l *spanReader) Read(p []byte) (n int, err error) { + if l.n <= 0 { + return 0, io.EOF + } + if int64(len(p)) > l.n { + p = p[0:l.n] + } + n, err = l.r.Read(p) + l.n -= int64(n) + if l.n > 0 && err == io.EOF { + err = io.ErrUnexpectedEOF + } + return +} + +// readHeader parses a packet header and returns an io.Reader which will return +// the contents of the packet. See RFC 4880, section 4.2. +func readHeader(r io.Reader) (tag packetType, length int64, contents io.Reader, err error) { + var buf [4]byte + _, err = io.ReadFull(r, buf[:1]) + if err != nil { + return + } + if buf[0]&0x80 == 0 { + err = errors.StructuralError("tag byte does not have MSB set") + return + } + if buf[0]&0x40 == 0 { + // Old format packet + tag = packetType((buf[0] & 0x3f) >> 2) + lengthType := buf[0] & 3 + if lengthType == 3 { + length = -1 + contents = r + return + } + lengthBytes := 1 << lengthType + _, err = readFull(r, buf[0:lengthBytes]) + if err != nil { + return + } + for i := 0; i < lengthBytes; i++ { + length <<= 8 + length |= int64(buf[i]) + } + contents = &spanReader{r, length} + return + } + + // New format packet + tag = packetType(buf[0] & 0x3f) + length, isPartial, err := readLength(r) + if err != nil { + return + } + if isPartial { + contents = &partialLengthReader{ + remaining: length, + isPartial: true, + r: r, + } + length = -1 + } else { + contents = &spanReader{r, length} + } + return +} + +// serializeHeader writes an OpenPGP packet header to w. See RFC 4880, section +// 4.2. +func serializeHeader(w io.Writer, ptype packetType, length int) (err error) { + var buf [6]byte + var n int + + buf[0] = 0x80 | 0x40 | byte(ptype) + if length < 192 { + buf[1] = byte(length) + n = 2 + } else if length < 8384 { + length -= 192 + buf[1] = 192 + byte(length>>8) + buf[2] = byte(length) + n = 3 + } else { + buf[1] = 255 + buf[2] = byte(length >> 24) + buf[3] = byte(length >> 16) + buf[4] = byte(length >> 8) + buf[5] = byte(length) + n = 6 + } + + _, err = w.Write(buf[:n]) + return +} + +// serializeStreamHeader writes an OpenPGP packet header to w where the +// length of the packet is unknown. It returns a io.WriteCloser which can be +// used to write the contents of the packet. See RFC 4880, section 4.2. +func serializeStreamHeader(w io.WriteCloser, ptype packetType) (out io.WriteCloser, err error) { + var buf [1]byte + buf[0] = 0x80 | 0x40 | byte(ptype) + _, err = w.Write(buf[:]) + if err != nil { + return + } + out = &partialLengthWriter{w: w} + return +} + +// Packet represents an OpenPGP packet. Users are expected to try casting +// instances of this interface to specific packet types. +type Packet interface { + parse(io.Reader) error +} + +// consumeAll reads from the given Reader until error, returning the number of +// bytes read. +func consumeAll(r io.Reader) (n int64, err error) { + var m int + var buf [1024]byte + + for { + m, err = r.Read(buf[:]) + n += int64(m) + if err == io.EOF { + err = nil + return + } + if err != nil { + return + } + } +} + +// packetType represents the numeric ids of the different OpenPGP packet types. See +// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-2 +type packetType uint8 + +const ( + packetTypeEncryptedKey packetType = 1 + packetTypeSignature packetType = 2 + packetTypeSymmetricKeyEncrypted packetType = 3 + packetTypeOnePassSignature packetType = 4 + packetTypePrivateKey packetType = 5 + packetTypePublicKey packetType = 6 + packetTypePrivateSubkey packetType = 7 + packetTypeCompressed packetType = 8 + packetTypeSymmetricallyEncrypted packetType = 9 + packetTypeLiteralData packetType = 11 + packetTypeUserId packetType = 13 + packetTypePublicSubkey packetType = 14 + packetTypeUserAttribute packetType = 17 + packetTypeSymmetricallyEncryptedMDC packetType = 18 +) + +// peekVersion detects the version of a public key packet about to +// be read. A bufio.Reader at the original position of the io.Reader +// is returned. +func peekVersion(r io.Reader) (bufr *bufio.Reader, ver byte, err error) { + bufr = bufio.NewReader(r) + var verBuf []byte + if verBuf, err = bufr.Peek(1); err != nil { + return + } + ver = verBuf[0] + return +} + +// Read reads a single OpenPGP packet from the given io.Reader. If there is an +// error parsing a packet, the whole packet is consumed from the input. +func Read(r io.Reader) (p Packet, err error) { + tag, _, contents, err := readHeader(r) + if err != nil { + return + } + + switch tag { + case packetTypeEncryptedKey: + p = new(EncryptedKey) + case packetTypeSignature: + var version byte + // Detect signature version + if contents, version, err = peekVersion(contents); err != nil { + return + } + if version < 4 { + p = new(SignatureV3) + } else { + p = new(Signature) + } + case packetTypeSymmetricKeyEncrypted: + p = new(SymmetricKeyEncrypted) + case packetTypeOnePassSignature: + p = new(OnePassSignature) + case packetTypePrivateKey, packetTypePrivateSubkey: + pk := new(PrivateKey) + if tag == packetTypePrivateSubkey { + pk.IsSubkey = true + } + p = pk + case packetTypePublicKey, packetTypePublicSubkey: + var version byte + if contents, version, err = peekVersion(contents); err != nil { + return + } + isSubkey := tag == packetTypePublicSubkey + if version < 4 { + p = &PublicKeyV3{IsSubkey: isSubkey} + } else { + p = &PublicKey{IsSubkey: isSubkey} + } + case packetTypeCompressed: + p = new(Compressed) + case packetTypeSymmetricallyEncrypted: + p = new(SymmetricallyEncrypted) + case packetTypeLiteralData: + p = new(LiteralData) + case packetTypeUserId: + p = new(UserId) + case packetTypeUserAttribute: + p = new(UserAttribute) + case packetTypeSymmetricallyEncryptedMDC: + se := new(SymmetricallyEncrypted) + se.MDC = true + p = se + default: + err = errors.UnknownPacketTypeError(tag) + } + if p != nil { + err = p.parse(contents) + } + if err != nil { + consumeAll(contents) + } + return +} + +// SignatureType represents the different semantic meanings of an OpenPGP +// signature. See RFC 4880, section 5.2.1. +type SignatureType uint8 + +const ( + SigTypeBinary SignatureType = 0 + SigTypeText = 1 + SigTypeGenericCert = 0x10 + SigTypePersonaCert = 0x11 + SigTypeCasualCert = 0x12 + SigTypePositiveCert = 0x13 + SigTypeSubkeyBinding = 0x18 + SigTypePrimaryKeyBinding = 0x19 + SigTypeDirectSignature = 0x1F + SigTypeKeyRevocation = 0x20 + SigTypeSubkeyRevocation = 0x28 +) + +// PublicKeyAlgorithm represents the different public key system specified for +// OpenPGP. See +// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-12 +type PublicKeyAlgorithm uint8 + +const ( + PubKeyAlgoRSA PublicKeyAlgorithm = 1 + PubKeyAlgoElGamal PublicKeyAlgorithm = 16 + PubKeyAlgoDSA PublicKeyAlgorithm = 17 + // RFC 6637, Section 5. + PubKeyAlgoECDH PublicKeyAlgorithm = 18 + PubKeyAlgoECDSA PublicKeyAlgorithm = 19 + + // Deprecated in RFC 4880, Section 13.5. Use key flags instead. + PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2 + PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3 +) + +// CanEncrypt returns true if it's possible to encrypt a message to a public +// key of the given type. +func (pka PublicKeyAlgorithm) CanEncrypt() bool { + switch pka { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal: + return true + } + return false +} + +// CanSign returns true if it's possible for a public key of the given type to +// sign a message. +func (pka PublicKeyAlgorithm) CanSign() bool { + switch pka { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA: + return true + } + return false +} + +// CipherFunction represents the different block ciphers specified for OpenPGP. See +// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13 +type CipherFunction uint8 + +const ( + Cipher3DES CipherFunction = 2 + CipherCAST5 CipherFunction = 3 + CipherAES128 CipherFunction = 7 + CipherAES192 CipherFunction = 8 + CipherAES256 CipherFunction = 9 +) + +// KeySize returns the key size, in bytes, of cipher. +func (cipher CipherFunction) KeySize() int { + switch cipher { + case Cipher3DES: + return 24 + case CipherCAST5: + return cast5.KeySize + case CipherAES128: + return 16 + case CipherAES192: + return 24 + case CipherAES256: + return 32 + } + return 0 +} + +// blockSize returns the block size, in bytes, of cipher. +func (cipher CipherFunction) blockSize() int { + switch cipher { + case Cipher3DES: + return des.BlockSize + case CipherCAST5: + return 8 + case CipherAES128, CipherAES192, CipherAES256: + return 16 + } + return 0 +} + +// new returns a fresh instance of the given cipher. +func (cipher CipherFunction) new(key []byte) (block cipher.Block) { + switch cipher { + case Cipher3DES: + block, _ = des.NewTripleDESCipher(key) + case CipherCAST5: + block, _ = cast5.NewCipher(key) + case CipherAES128, CipherAES192, CipherAES256: + block, _ = aes.NewCipher(key) + } + return +} + +// readMPI reads a big integer from r. The bit length returned is the bit +// length that was specified in r. This is preserved so that the integer can be +// reserialized exactly. +func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err error) { + var buf [2]byte + _, err = readFull(r, buf[0:]) + if err != nil { + return + } + bitLength = uint16(buf[0])<<8 | uint16(buf[1]) + numBytes := (int(bitLength) + 7) / 8 + mpi = make([]byte, numBytes) + _, err = readFull(r, mpi) + // According to RFC 4880 3.2. we should check that the MPI has no leading + // zeroes (at least when not an encrypted MPI?), but this implementation + // does generate leading zeroes, so we keep accepting them. + return +} + +// writeMPI serializes a big integer to w. +func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err error) { + // Note that we can produce leading zeroes, in violation of RFC 4880 3.2. + // Implementations seem to be tolerant of them, and stripping them would + // make it complex to guarantee matching re-serialization. + _, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)}) + if err == nil { + _, err = w.Write(mpiBytes) + } + return +} + +// writeBig serializes a *big.Int to w. +func writeBig(w io.Writer, i *big.Int) error { + return writeMPI(w, uint16(i.BitLen()), i.Bytes()) +} + +// padToKeySize left-pads a MPI with zeroes to match the length of the +// specified RSA public. +func padToKeySize(pub *rsa.PublicKey, b []byte) []byte { + k := (pub.N.BitLen() + 7) / 8 + if len(b) >= k { + return b + } + bb := make([]byte, k) + copy(bb[len(bb)-len(b):], b) + return bb +} + +// CompressionAlgo Represents the different compression algorithms +// supported by OpenPGP (except for BZIP2, which is not currently +// supported). See Section 9.3 of RFC 4880. +type CompressionAlgo uint8 + +const ( + CompressionNone CompressionAlgo = 0 + CompressionZIP CompressionAlgo = 1 + CompressionZLIB CompressionAlgo = 2 +) diff --git a/vendor/golang.org/x/crypto/openpgp/packet/private_key.go b/vendor/golang.org/x/crypto/openpgp/packet/private_key.go new file mode 100644 index 0000000000..81abb7cef9 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/private_key.go @@ -0,0 +1,385 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "crypto" + "crypto/cipher" + "crypto/dsa" + "crypto/ecdsa" + "crypto/rsa" + "crypto/sha1" + "io" + "io/ioutil" + "math/big" + "strconv" + "time" + + "golang.org/x/crypto/openpgp/elgamal" + "golang.org/x/crypto/openpgp/errors" + "golang.org/x/crypto/openpgp/s2k" +) + +// PrivateKey represents a possibly encrypted private key. See RFC 4880, +// section 5.5.3. +type PrivateKey struct { + PublicKey + Encrypted bool // if true then the private key is unavailable until Decrypt has been called. + encryptedData []byte + cipher CipherFunction + s2k func(out, in []byte) + PrivateKey interface{} // An *{rsa|dsa|ecdsa}.PrivateKey or crypto.Signer/crypto.Decrypter (Decryptor RSA only). + sha1Checksum bool + iv []byte +} + +func NewRSAPrivateKey(creationTime time.Time, priv *rsa.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewRSAPublicKey(creationTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + +func NewDSAPrivateKey(creationTime time.Time, priv *dsa.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewDSAPublicKey(creationTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + +func NewElGamalPrivateKey(creationTime time.Time, priv *elgamal.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewElGamalPublicKey(creationTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + +func NewECDSAPrivateKey(creationTime time.Time, priv *ecdsa.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewECDSAPublicKey(creationTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + +// NewSignerPrivateKey creates a PrivateKey from a crypto.Signer that +// implements RSA or ECDSA. +func NewSignerPrivateKey(creationTime time.Time, signer crypto.Signer) *PrivateKey { + pk := new(PrivateKey) + // In general, the public Keys should be used as pointers. We still + // type-switch on the values, for backwards-compatibility. + switch pubkey := signer.Public().(type) { + case *rsa.PublicKey: + pk.PublicKey = *NewRSAPublicKey(creationTime, pubkey) + case rsa.PublicKey: + pk.PublicKey = *NewRSAPublicKey(creationTime, &pubkey) + case *ecdsa.PublicKey: + pk.PublicKey = *NewECDSAPublicKey(creationTime, pubkey) + case ecdsa.PublicKey: + pk.PublicKey = *NewECDSAPublicKey(creationTime, &pubkey) + default: + panic("openpgp: unknown crypto.Signer type in NewSignerPrivateKey") + } + pk.PrivateKey = signer + return pk +} + +func (pk *PrivateKey) parse(r io.Reader) (err error) { + err = (&pk.PublicKey).parse(r) + if err != nil { + return + } + var buf [1]byte + _, err = readFull(r, buf[:]) + if err != nil { + return + } + + s2kType := buf[0] + + switch s2kType { + case 0: + pk.s2k = nil + pk.Encrypted = false + case 254, 255: + _, err = readFull(r, buf[:]) + if err != nil { + return + } + pk.cipher = CipherFunction(buf[0]) + pk.Encrypted = true + pk.s2k, err = s2k.Parse(r) + if err != nil { + return + } + if s2kType == 254 { + pk.sha1Checksum = true + } + default: + return errors.UnsupportedError("deprecated s2k function in private key") + } + + if pk.Encrypted { + blockSize := pk.cipher.blockSize() + if blockSize == 0 { + return errors.UnsupportedError("unsupported cipher in private key: " + strconv.Itoa(int(pk.cipher))) + } + pk.iv = make([]byte, blockSize) + _, err = readFull(r, pk.iv) + if err != nil { + return + } + } + + pk.encryptedData, err = ioutil.ReadAll(r) + if err != nil { + return + } + + if !pk.Encrypted { + return pk.parsePrivateKey(pk.encryptedData) + } + + return +} + +func mod64kHash(d []byte) uint16 { + var h uint16 + for _, b := range d { + h += uint16(b) + } + return h +} + +func (pk *PrivateKey) Serialize(w io.Writer) (err error) { + // TODO(agl): support encrypted private keys + buf := bytes.NewBuffer(nil) + err = pk.PublicKey.serializeWithoutHeaders(buf) + if err != nil { + return + } + buf.WriteByte(0 /* no encryption */) + + privateKeyBuf := bytes.NewBuffer(nil) + + switch priv := pk.PrivateKey.(type) { + case *rsa.PrivateKey: + err = serializeRSAPrivateKey(privateKeyBuf, priv) + case *dsa.PrivateKey: + err = serializeDSAPrivateKey(privateKeyBuf, priv) + case *elgamal.PrivateKey: + err = serializeElGamalPrivateKey(privateKeyBuf, priv) + case *ecdsa.PrivateKey: + err = serializeECDSAPrivateKey(privateKeyBuf, priv) + default: + err = errors.InvalidArgumentError("unknown private key type") + } + if err != nil { + return + } + + ptype := packetTypePrivateKey + contents := buf.Bytes() + privateKeyBytes := privateKeyBuf.Bytes() + if pk.IsSubkey { + ptype = packetTypePrivateSubkey + } + err = serializeHeader(w, ptype, len(contents)+len(privateKeyBytes)+2) + if err != nil { + return + } + _, err = w.Write(contents) + if err != nil { + return + } + _, err = w.Write(privateKeyBytes) + if err != nil { + return + } + + checksum := mod64kHash(privateKeyBytes) + var checksumBytes [2]byte + checksumBytes[0] = byte(checksum >> 8) + checksumBytes[1] = byte(checksum) + _, err = w.Write(checksumBytes[:]) + + return +} + +func serializeRSAPrivateKey(w io.Writer, priv *rsa.PrivateKey) error { + err := writeBig(w, priv.D) + if err != nil { + return err + } + err = writeBig(w, priv.Primes[1]) + if err != nil { + return err + } + err = writeBig(w, priv.Primes[0]) + if err != nil { + return err + } + return writeBig(w, priv.Precomputed.Qinv) +} + +func serializeDSAPrivateKey(w io.Writer, priv *dsa.PrivateKey) error { + return writeBig(w, priv.X) +} + +func serializeElGamalPrivateKey(w io.Writer, priv *elgamal.PrivateKey) error { + return writeBig(w, priv.X) +} + +func serializeECDSAPrivateKey(w io.Writer, priv *ecdsa.PrivateKey) error { + return writeBig(w, priv.D) +} + +// Decrypt decrypts an encrypted private key using a passphrase. +func (pk *PrivateKey) Decrypt(passphrase []byte) error { + if !pk.Encrypted { + return nil + } + + key := make([]byte, pk.cipher.KeySize()) + pk.s2k(key, passphrase) + block := pk.cipher.new(key) + cfb := cipher.NewCFBDecrypter(block, pk.iv) + + data := make([]byte, len(pk.encryptedData)) + cfb.XORKeyStream(data, pk.encryptedData) + + if pk.sha1Checksum { + if len(data) < sha1.Size { + return errors.StructuralError("truncated private key data") + } + h := sha1.New() + h.Write(data[:len(data)-sha1.Size]) + sum := h.Sum(nil) + if !bytes.Equal(sum, data[len(data)-sha1.Size:]) { + return errors.StructuralError("private key checksum failure") + } + data = data[:len(data)-sha1.Size] + } else { + if len(data) < 2 { + return errors.StructuralError("truncated private key data") + } + var sum uint16 + for i := 0; i < len(data)-2; i++ { + sum += uint16(data[i]) + } + if data[len(data)-2] != uint8(sum>>8) || + data[len(data)-1] != uint8(sum) { + return errors.StructuralError("private key checksum failure") + } + data = data[:len(data)-2] + } + + return pk.parsePrivateKey(data) +} + +func (pk *PrivateKey) parsePrivateKey(data []byte) (err error) { + switch pk.PublicKey.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoRSAEncryptOnly: + return pk.parseRSAPrivateKey(data) + case PubKeyAlgoDSA: + return pk.parseDSAPrivateKey(data) + case PubKeyAlgoElGamal: + return pk.parseElGamalPrivateKey(data) + case PubKeyAlgoECDSA: + return pk.parseECDSAPrivateKey(data) + } + panic("impossible") +} + +func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err error) { + rsaPub := pk.PublicKey.PublicKey.(*rsa.PublicKey) + rsaPriv := new(rsa.PrivateKey) + rsaPriv.PublicKey = *rsaPub + + buf := bytes.NewBuffer(data) + d, _, err := readMPI(buf) + if err != nil { + return + } + p, _, err := readMPI(buf) + if err != nil { + return + } + q, _, err := readMPI(buf) + if err != nil { + return + } + + rsaPriv.D = new(big.Int).SetBytes(d) + rsaPriv.Primes = make([]*big.Int, 2) + rsaPriv.Primes[0] = new(big.Int).SetBytes(p) + rsaPriv.Primes[1] = new(big.Int).SetBytes(q) + if err := rsaPriv.Validate(); err != nil { + return err + } + rsaPriv.Precompute() + pk.PrivateKey = rsaPriv + pk.Encrypted = false + pk.encryptedData = nil + + return nil +} + +func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err error) { + dsaPub := pk.PublicKey.PublicKey.(*dsa.PublicKey) + dsaPriv := new(dsa.PrivateKey) + dsaPriv.PublicKey = *dsaPub + + buf := bytes.NewBuffer(data) + x, _, err := readMPI(buf) + if err != nil { + return + } + + dsaPriv.X = new(big.Int).SetBytes(x) + pk.PrivateKey = dsaPriv + pk.Encrypted = false + pk.encryptedData = nil + + return nil +} + +func (pk *PrivateKey) parseElGamalPrivateKey(data []byte) (err error) { + pub := pk.PublicKey.PublicKey.(*elgamal.PublicKey) + priv := new(elgamal.PrivateKey) + priv.PublicKey = *pub + + buf := bytes.NewBuffer(data) + x, _, err := readMPI(buf) + if err != nil { + return + } + + priv.X = new(big.Int).SetBytes(x) + pk.PrivateKey = priv + pk.Encrypted = false + pk.encryptedData = nil + + return nil +} + +func (pk *PrivateKey) parseECDSAPrivateKey(data []byte) (err error) { + ecdsaPub := pk.PublicKey.PublicKey.(*ecdsa.PublicKey) + + buf := bytes.NewBuffer(data) + d, _, err := readMPI(buf) + if err != nil { + return + } + + pk.PrivateKey = &ecdsa.PrivateKey{ + PublicKey: *ecdsaPub, + D: new(big.Int).SetBytes(d), + } + pk.Encrypted = false + pk.encryptedData = nil + + return nil +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/public_key.go b/vendor/golang.org/x/crypto/openpgp/packet/public_key.go new file mode 100644 index 0000000000..fcd5f52519 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/public_key.go @@ -0,0 +1,753 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "crypto/sha1" + _ "crypto/sha256" + _ "crypto/sha512" + "encoding/binary" + "fmt" + "hash" + "io" + "math/big" + "strconv" + "time" + + "golang.org/x/crypto/openpgp/elgamal" + "golang.org/x/crypto/openpgp/errors" +) + +var ( + // NIST curve P-256 + oidCurveP256 []byte = []byte{0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07} + // NIST curve P-384 + oidCurveP384 []byte = []byte{0x2B, 0x81, 0x04, 0x00, 0x22} + // NIST curve P-521 + oidCurveP521 []byte = []byte{0x2B, 0x81, 0x04, 0x00, 0x23} +) + +const maxOIDLength = 8 + +// ecdsaKey stores the algorithm-specific fields for ECDSA keys. +// as defined in RFC 6637, Section 9. +type ecdsaKey struct { + // oid contains the OID byte sequence identifying the elliptic curve used + oid []byte + // p contains the elliptic curve point that represents the public key + p parsedMPI +} + +// parseOID reads the OID for the curve as defined in RFC 6637, Section 9. +func parseOID(r io.Reader) (oid []byte, err error) { + buf := make([]byte, maxOIDLength) + if _, err = readFull(r, buf[:1]); err != nil { + return + } + oidLen := buf[0] + if int(oidLen) > len(buf) { + err = errors.UnsupportedError("invalid oid length: " + strconv.Itoa(int(oidLen))) + return + } + oid = buf[:oidLen] + _, err = readFull(r, oid) + return +} + +func (f *ecdsaKey) parse(r io.Reader) (err error) { + if f.oid, err = parseOID(r); err != nil { + return err + } + f.p.bytes, f.p.bitLength, err = readMPI(r) + return +} + +func (f *ecdsaKey) serialize(w io.Writer) (err error) { + buf := make([]byte, maxOIDLength+1) + buf[0] = byte(len(f.oid)) + copy(buf[1:], f.oid) + if _, err = w.Write(buf[:len(f.oid)+1]); err != nil { + return + } + return writeMPIs(w, f.p) +} + +func (f *ecdsaKey) newECDSA() (*ecdsa.PublicKey, error) { + var c elliptic.Curve + if bytes.Equal(f.oid, oidCurveP256) { + c = elliptic.P256() + } else if bytes.Equal(f.oid, oidCurveP384) { + c = elliptic.P384() + } else if bytes.Equal(f.oid, oidCurveP521) { + c = elliptic.P521() + } else { + return nil, errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", f.oid)) + } + x, y := elliptic.Unmarshal(c, f.p.bytes) + if x == nil { + return nil, errors.UnsupportedError("failed to parse EC point") + } + return &ecdsa.PublicKey{Curve: c, X: x, Y: y}, nil +} + +func (f *ecdsaKey) byteLen() int { + return 1 + len(f.oid) + 2 + len(f.p.bytes) +} + +type kdfHashFunction byte +type kdfAlgorithm byte + +// ecdhKdf stores key derivation function parameters +// used for ECDH encryption. See RFC 6637, Section 9. +type ecdhKdf struct { + KdfHash kdfHashFunction + KdfAlgo kdfAlgorithm +} + +func (f *ecdhKdf) parse(r io.Reader) (err error) { + buf := make([]byte, 1) + if _, err = readFull(r, buf); err != nil { + return + } + kdfLen := int(buf[0]) + if kdfLen < 3 { + return errors.UnsupportedError("Unsupported ECDH KDF length: " + strconv.Itoa(kdfLen)) + } + buf = make([]byte, kdfLen) + if _, err = readFull(r, buf); err != nil { + return + } + reserved := int(buf[0]) + f.KdfHash = kdfHashFunction(buf[1]) + f.KdfAlgo = kdfAlgorithm(buf[2]) + if reserved != 0x01 { + return errors.UnsupportedError("Unsupported KDF reserved field: " + strconv.Itoa(reserved)) + } + return +} + +func (f *ecdhKdf) serialize(w io.Writer) (err error) { + buf := make([]byte, 4) + // See RFC 6637, Section 9, Algorithm-Specific Fields for ECDH keys. + buf[0] = byte(0x03) // Length of the following fields + buf[1] = byte(0x01) // Reserved for future extensions, must be 1 for now + buf[2] = byte(f.KdfHash) + buf[3] = byte(f.KdfAlgo) + _, err = w.Write(buf[:]) + return +} + +func (f *ecdhKdf) byteLen() int { + return 4 +} + +// PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2. +type PublicKey struct { + CreationTime time.Time + PubKeyAlgo PublicKeyAlgorithm + PublicKey interface{} // *rsa.PublicKey, *dsa.PublicKey or *ecdsa.PublicKey + Fingerprint [20]byte + KeyId uint64 + IsSubkey bool + + n, e, p, q, g, y parsedMPI + + // RFC 6637 fields + ec *ecdsaKey + ecdh *ecdhKdf +} + +// signingKey provides a convenient abstraction over signature verification +// for v3 and v4 public keys. +type signingKey interface { + SerializeSignaturePrefix(io.Writer) + serializeWithoutHeaders(io.Writer) error +} + +func fromBig(n *big.Int) parsedMPI { + return parsedMPI{ + bytes: n.Bytes(), + bitLength: uint16(n.BitLen()), + } +} + +// NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey. +func NewRSAPublicKey(creationTime time.Time, pub *rsa.PublicKey) *PublicKey { + pk := &PublicKey{ + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoRSA, + PublicKey: pub, + n: fromBig(pub.N), + e: fromBig(big.NewInt(int64(pub.E))), + } + + pk.setFingerPrintAndKeyId() + return pk +} + +// NewDSAPublicKey returns a PublicKey that wraps the given dsa.PublicKey. +func NewDSAPublicKey(creationTime time.Time, pub *dsa.PublicKey) *PublicKey { + pk := &PublicKey{ + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoDSA, + PublicKey: pub, + p: fromBig(pub.P), + q: fromBig(pub.Q), + g: fromBig(pub.G), + y: fromBig(pub.Y), + } + + pk.setFingerPrintAndKeyId() + return pk +} + +// NewElGamalPublicKey returns a PublicKey that wraps the given elgamal.PublicKey. +func NewElGamalPublicKey(creationTime time.Time, pub *elgamal.PublicKey) *PublicKey { + pk := &PublicKey{ + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoElGamal, + PublicKey: pub, + p: fromBig(pub.P), + g: fromBig(pub.G), + y: fromBig(pub.Y), + } + + pk.setFingerPrintAndKeyId() + return pk +} + +func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey { + pk := &PublicKey{ + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoECDSA, + PublicKey: pub, + ec: new(ecdsaKey), + } + + switch pub.Curve { + case elliptic.P256(): + pk.ec.oid = oidCurveP256 + case elliptic.P384(): + pk.ec.oid = oidCurveP384 + case elliptic.P521(): + pk.ec.oid = oidCurveP521 + default: + panic("unknown elliptic curve") + } + + pk.ec.p.bytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y) + + // The bit length is 3 (for the 0x04 specifying an uncompressed key) + // plus two field elements (for x and y), which are rounded up to the + // nearest byte. See https://tools.ietf.org/html/rfc6637#section-6 + fieldBytes := (pub.Curve.Params().BitSize + 7) & ^7 + pk.ec.p.bitLength = uint16(3 + fieldBytes + fieldBytes) + + pk.setFingerPrintAndKeyId() + return pk +} + +func (pk *PublicKey) parse(r io.Reader) (err error) { + // RFC 4880, section 5.5.2 + var buf [6]byte + _, err = readFull(r, buf[:]) + if err != nil { + return + } + if buf[0] != 4 { + return errors.UnsupportedError("public key version") + } + pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0) + pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5]) + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + err = pk.parseRSA(r) + case PubKeyAlgoDSA: + err = pk.parseDSA(r) + case PubKeyAlgoElGamal: + err = pk.parseElGamal(r) + case PubKeyAlgoECDSA: + pk.ec = new(ecdsaKey) + if err = pk.ec.parse(r); err != nil { + return err + } + pk.PublicKey, err = pk.ec.newECDSA() + case PubKeyAlgoECDH: + pk.ec = new(ecdsaKey) + if err = pk.ec.parse(r); err != nil { + return + } + pk.ecdh = new(ecdhKdf) + if err = pk.ecdh.parse(r); err != nil { + return + } + // The ECDH key is stored in an ecdsa.PublicKey for convenience. + pk.PublicKey, err = pk.ec.newECDSA() + default: + err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) + } + if err != nil { + return + } + + pk.setFingerPrintAndKeyId() + return +} + +func (pk *PublicKey) setFingerPrintAndKeyId() { + // RFC 4880, section 12.2 + fingerPrint := sha1.New() + pk.SerializeSignaturePrefix(fingerPrint) + pk.serializeWithoutHeaders(fingerPrint) + copy(pk.Fingerprint[:], fingerPrint.Sum(nil)) + pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20]) +} + +// parseRSA parses RSA public key material from the given Reader. See RFC 4880, +// section 5.5.2. +func (pk *PublicKey) parseRSA(r io.Reader) (err error) { + pk.n.bytes, pk.n.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.e.bytes, pk.e.bitLength, err = readMPI(r) + if err != nil { + return + } + + if len(pk.e.bytes) > 3 { + err = errors.UnsupportedError("large public exponent") + return + } + rsa := &rsa.PublicKey{ + N: new(big.Int).SetBytes(pk.n.bytes), + E: 0, + } + for i := 0; i < len(pk.e.bytes); i++ { + rsa.E <<= 8 + rsa.E |= int(pk.e.bytes[i]) + } + pk.PublicKey = rsa + return +} + +// parseDSA parses DSA public key material from the given Reader. See RFC 4880, +// section 5.5.2. +func (pk *PublicKey) parseDSA(r io.Reader) (err error) { + pk.p.bytes, pk.p.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.q.bytes, pk.q.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.g.bytes, pk.g.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.y.bytes, pk.y.bitLength, err = readMPI(r) + if err != nil { + return + } + + dsa := new(dsa.PublicKey) + dsa.P = new(big.Int).SetBytes(pk.p.bytes) + dsa.Q = new(big.Int).SetBytes(pk.q.bytes) + dsa.G = new(big.Int).SetBytes(pk.g.bytes) + dsa.Y = new(big.Int).SetBytes(pk.y.bytes) + pk.PublicKey = dsa + return +} + +// parseElGamal parses ElGamal public key material from the given Reader. See +// RFC 4880, section 5.5.2. +func (pk *PublicKey) parseElGamal(r io.Reader) (err error) { + pk.p.bytes, pk.p.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.g.bytes, pk.g.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.y.bytes, pk.y.bitLength, err = readMPI(r) + if err != nil { + return + } + + elgamal := new(elgamal.PublicKey) + elgamal.P = new(big.Int).SetBytes(pk.p.bytes) + elgamal.G = new(big.Int).SetBytes(pk.g.bytes) + elgamal.Y = new(big.Int).SetBytes(pk.y.bytes) + pk.PublicKey = elgamal + return +} + +// SerializeSignaturePrefix writes the prefix for this public key to the given Writer. +// The prefix is used when calculating a signature over this public key. See +// RFC 4880, section 5.2.4. +func (pk *PublicKey) SerializeSignaturePrefix(h io.Writer) { + var pLength uint16 + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + pLength += 2 + uint16(len(pk.n.bytes)) + pLength += 2 + uint16(len(pk.e.bytes)) + case PubKeyAlgoDSA: + pLength += 2 + uint16(len(pk.p.bytes)) + pLength += 2 + uint16(len(pk.q.bytes)) + pLength += 2 + uint16(len(pk.g.bytes)) + pLength += 2 + uint16(len(pk.y.bytes)) + case PubKeyAlgoElGamal: + pLength += 2 + uint16(len(pk.p.bytes)) + pLength += 2 + uint16(len(pk.g.bytes)) + pLength += 2 + uint16(len(pk.y.bytes)) + case PubKeyAlgoECDSA: + pLength += uint16(pk.ec.byteLen()) + case PubKeyAlgoECDH: + pLength += uint16(pk.ec.byteLen()) + pLength += uint16(pk.ecdh.byteLen()) + default: + panic("unknown public key algorithm") + } + pLength += 6 + h.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)}) + return +} + +func (pk *PublicKey) Serialize(w io.Writer) (err error) { + length := 6 // 6 byte header + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + length += 2 + len(pk.n.bytes) + length += 2 + len(pk.e.bytes) + case PubKeyAlgoDSA: + length += 2 + len(pk.p.bytes) + length += 2 + len(pk.q.bytes) + length += 2 + len(pk.g.bytes) + length += 2 + len(pk.y.bytes) + case PubKeyAlgoElGamal: + length += 2 + len(pk.p.bytes) + length += 2 + len(pk.g.bytes) + length += 2 + len(pk.y.bytes) + case PubKeyAlgoECDSA: + length += pk.ec.byteLen() + case PubKeyAlgoECDH: + length += pk.ec.byteLen() + length += pk.ecdh.byteLen() + default: + panic("unknown public key algorithm") + } + + packetType := packetTypePublicKey + if pk.IsSubkey { + packetType = packetTypePublicSubkey + } + err = serializeHeader(w, packetType, length) + if err != nil { + return + } + return pk.serializeWithoutHeaders(w) +} + +// serializeWithoutHeaders marshals the PublicKey to w in the form of an +// OpenPGP public key packet, not including the packet header. +func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) { + var buf [6]byte + buf[0] = 4 + t := uint32(pk.CreationTime.Unix()) + buf[1] = byte(t >> 24) + buf[2] = byte(t >> 16) + buf[3] = byte(t >> 8) + buf[4] = byte(t) + buf[5] = byte(pk.PubKeyAlgo) + + _, err = w.Write(buf[:]) + if err != nil { + return + } + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + return writeMPIs(w, pk.n, pk.e) + case PubKeyAlgoDSA: + return writeMPIs(w, pk.p, pk.q, pk.g, pk.y) + case PubKeyAlgoElGamal: + return writeMPIs(w, pk.p, pk.g, pk.y) + case PubKeyAlgoECDSA: + return pk.ec.serialize(w) + case PubKeyAlgoECDH: + if err = pk.ec.serialize(w); err != nil { + return + } + return pk.ecdh.serialize(w) + } + return errors.InvalidArgumentError("bad public-key algorithm") +} + +// CanSign returns true iff this public key can generate signatures +func (pk *PublicKey) CanSign() bool { + return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal +} + +// VerifySignature returns nil iff sig is a valid signature, made by this +// public key, of the data hashed into signed. signed is mutated by this call. +func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err error) { + if !pk.CanSign() { + return errors.InvalidArgumentError("public key cannot generate signatures") + } + + signed.Write(sig.HashSuffix) + hashBytes := signed.Sum(nil) + + if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { + return errors.SignatureError("hash tag doesn't match") + } + + if pk.PubKeyAlgo != sig.PubKeyAlgo { + return errors.InvalidArgumentError("public key and signature use different algorithms") + } + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey) + err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.bytes)) + if err != nil { + return errors.SignatureError("RSA verification failure") + } + return nil + case PubKeyAlgoDSA: + dsaPublicKey, _ := pk.PublicKey.(*dsa.PublicKey) + // Need to truncate hashBytes to match FIPS 186-3 section 4.6. + subgroupSize := (dsaPublicKey.Q.BitLen() + 7) / 8 + if len(hashBytes) > subgroupSize { + hashBytes = hashBytes[:subgroupSize] + } + if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.bytes), new(big.Int).SetBytes(sig.DSASigS.bytes)) { + return errors.SignatureError("DSA verification failure") + } + return nil + case PubKeyAlgoECDSA: + ecdsaPublicKey := pk.PublicKey.(*ecdsa.PublicKey) + if !ecdsa.Verify(ecdsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.ECDSASigR.bytes), new(big.Int).SetBytes(sig.ECDSASigS.bytes)) { + return errors.SignatureError("ECDSA verification failure") + } + return nil + default: + return errors.SignatureError("Unsupported public key algorithm used in signature") + } +} + +// VerifySignatureV3 returns nil iff sig is a valid signature, made by this +// public key, of the data hashed into signed. signed is mutated by this call. +func (pk *PublicKey) VerifySignatureV3(signed hash.Hash, sig *SignatureV3) (err error) { + if !pk.CanSign() { + return errors.InvalidArgumentError("public key cannot generate signatures") + } + + suffix := make([]byte, 5) + suffix[0] = byte(sig.SigType) + binary.BigEndian.PutUint32(suffix[1:], uint32(sig.CreationTime.Unix())) + signed.Write(suffix) + hashBytes := signed.Sum(nil) + + if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { + return errors.SignatureError("hash tag doesn't match") + } + + if pk.PubKeyAlgo != sig.PubKeyAlgo { + return errors.InvalidArgumentError("public key and signature use different algorithms") + } + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + rsaPublicKey := pk.PublicKey.(*rsa.PublicKey) + if err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.bytes)); err != nil { + return errors.SignatureError("RSA verification failure") + } + return + case PubKeyAlgoDSA: + dsaPublicKey := pk.PublicKey.(*dsa.PublicKey) + // Need to truncate hashBytes to match FIPS 186-3 section 4.6. + subgroupSize := (dsaPublicKey.Q.BitLen() + 7) / 8 + if len(hashBytes) > subgroupSize { + hashBytes = hashBytes[:subgroupSize] + } + if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.bytes), new(big.Int).SetBytes(sig.DSASigS.bytes)) { + return errors.SignatureError("DSA verification failure") + } + return nil + default: + panic("shouldn't happen") + } +} + +// keySignatureHash returns a Hash of the message that needs to be signed for +// pk to assert a subkey relationship to signed. +func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) { + if !hashFunc.Available() { + return nil, errors.UnsupportedError("hash function") + } + h = hashFunc.New() + + // RFC 4880, section 5.2.4 + pk.SerializeSignaturePrefix(h) + pk.serializeWithoutHeaders(h) + signed.SerializeSignaturePrefix(h) + signed.serializeWithoutHeaders(h) + return +} + +// VerifyKeySignature returns nil iff sig is a valid signature, made by this +// public key, of signed. +func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error { + h, err := keySignatureHash(pk, signed, sig.Hash) + if err != nil { + return err + } + if err = pk.VerifySignature(h, sig); err != nil { + return err + } + + if sig.FlagSign { + // Signing subkeys must be cross-signed. See + // https://www.gnupg.org/faq/subkey-cross-certify.html. + if sig.EmbeddedSignature == nil { + return errors.StructuralError("signing subkey is missing cross-signature") + } + // Verify the cross-signature. This is calculated over the same + // data as the main signature, so we cannot just recursively + // call signed.VerifyKeySignature(...) + if h, err = keySignatureHash(pk, signed, sig.EmbeddedSignature.Hash); err != nil { + return errors.StructuralError("error while hashing for cross-signature: " + err.Error()) + } + if err := signed.VerifySignature(h, sig.EmbeddedSignature); err != nil { + return errors.StructuralError("error while verifying cross-signature: " + err.Error()) + } + } + + return nil +} + +func keyRevocationHash(pk signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) { + if !hashFunc.Available() { + return nil, errors.UnsupportedError("hash function") + } + h = hashFunc.New() + + // RFC 4880, section 5.2.4 + pk.SerializeSignaturePrefix(h) + pk.serializeWithoutHeaders(h) + + return +} + +// VerifyRevocationSignature returns nil iff sig is a valid signature, made by this +// public key. +func (pk *PublicKey) VerifyRevocationSignature(sig *Signature) (err error) { + h, err := keyRevocationHash(pk, sig.Hash) + if err != nil { + return err + } + return pk.VerifySignature(h, sig) +} + +// userIdSignatureHash returns a Hash of the message that needs to be signed +// to assert that pk is a valid key for id. +func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash.Hash, err error) { + if !hashFunc.Available() { + return nil, errors.UnsupportedError("hash function") + } + h = hashFunc.New() + + // RFC 4880, section 5.2.4 + pk.SerializeSignaturePrefix(h) + pk.serializeWithoutHeaders(h) + + var buf [5]byte + buf[0] = 0xb4 + buf[1] = byte(len(id) >> 24) + buf[2] = byte(len(id) >> 16) + buf[3] = byte(len(id) >> 8) + buf[4] = byte(len(id)) + h.Write(buf[:]) + h.Write([]byte(id)) + + return +} + +// VerifyUserIdSignature returns nil iff sig is a valid signature, made by this +// public key, that id is the identity of pub. +func (pk *PublicKey) VerifyUserIdSignature(id string, pub *PublicKey, sig *Signature) (err error) { + h, err := userIdSignatureHash(id, pub, sig.Hash) + if err != nil { + return err + } + return pk.VerifySignature(h, sig) +} + +// VerifyUserIdSignatureV3 returns nil iff sig is a valid signature, made by this +// public key, that id is the identity of pub. +func (pk *PublicKey) VerifyUserIdSignatureV3(id string, pub *PublicKey, sig *SignatureV3) (err error) { + h, err := userIdSignatureV3Hash(id, pub, sig.Hash) + if err != nil { + return err + } + return pk.VerifySignatureV3(h, sig) +} + +// KeyIdString returns the public key's fingerprint in capital hex +// (e.g. "6C7EE1B8621CC013"). +func (pk *PublicKey) KeyIdString() string { + return fmt.Sprintf("%X", pk.Fingerprint[12:20]) +} + +// KeyIdShortString returns the short form of public key's fingerprint +// in capital hex, as shown by gpg --list-keys (e.g. "621CC013"). +func (pk *PublicKey) KeyIdShortString() string { + return fmt.Sprintf("%X", pk.Fingerprint[16:20]) +} + +// A parsedMPI is used to store the contents of a big integer, along with the +// bit length that was specified in the original input. This allows the MPI to +// be reserialized exactly. +type parsedMPI struct { + bytes []byte + bitLength uint16 +} + +// writeMPIs is a utility function for serializing several big integers to the +// given Writer. +func writeMPIs(w io.Writer, mpis ...parsedMPI) (err error) { + for _, mpi := range mpis { + err = writeMPI(w, mpi.bitLength, mpi.bytes) + if err != nil { + return + } + } + return +} + +// BitLength returns the bit length for the given public key. +func (pk *PublicKey) BitLength() (bitLength uint16, err error) { + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + bitLength = pk.n.bitLength + case PubKeyAlgoDSA: + bitLength = pk.p.bitLength + case PubKeyAlgoElGamal: + bitLength = pk.p.bitLength + default: + err = errors.InvalidArgumentError("bad public-key algorithm") + } + return +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/public_key_v3.go b/vendor/golang.org/x/crypto/openpgp/packet/public_key_v3.go new file mode 100644 index 0000000000..5daf7b6cfd --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/public_key_v3.go @@ -0,0 +1,279 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "crypto" + "crypto/md5" + "crypto/rsa" + "encoding/binary" + "fmt" + "hash" + "io" + "math/big" + "strconv" + "time" + + "golang.org/x/crypto/openpgp/errors" +) + +// PublicKeyV3 represents older, version 3 public keys. These keys are less secure and +// should not be used for signing or encrypting. They are supported here only for +// parsing version 3 key material and validating signatures. +// See RFC 4880, section 5.5.2. +type PublicKeyV3 struct { + CreationTime time.Time + DaysToExpire uint16 + PubKeyAlgo PublicKeyAlgorithm + PublicKey *rsa.PublicKey + Fingerprint [16]byte + KeyId uint64 + IsSubkey bool + + n, e parsedMPI +} + +// newRSAPublicKeyV3 returns a PublicKey that wraps the given rsa.PublicKey. +// Included here for testing purposes only. RFC 4880, section 5.5.2: +// "an implementation MUST NOT generate a V3 key, but MAY accept it." +func newRSAPublicKeyV3(creationTime time.Time, pub *rsa.PublicKey) *PublicKeyV3 { + pk := &PublicKeyV3{ + CreationTime: creationTime, + PublicKey: pub, + n: fromBig(pub.N), + e: fromBig(big.NewInt(int64(pub.E))), + } + + pk.setFingerPrintAndKeyId() + return pk +} + +func (pk *PublicKeyV3) parse(r io.Reader) (err error) { + // RFC 4880, section 5.5.2 + var buf [8]byte + if _, err = readFull(r, buf[:]); err != nil { + return + } + if buf[0] < 2 || buf[0] > 3 { + return errors.UnsupportedError("public key version") + } + pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0) + pk.DaysToExpire = binary.BigEndian.Uint16(buf[5:7]) + pk.PubKeyAlgo = PublicKeyAlgorithm(buf[7]) + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + err = pk.parseRSA(r) + default: + err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) + } + if err != nil { + return + } + + pk.setFingerPrintAndKeyId() + return +} + +func (pk *PublicKeyV3) setFingerPrintAndKeyId() { + // RFC 4880, section 12.2 + fingerPrint := md5.New() + fingerPrint.Write(pk.n.bytes) + fingerPrint.Write(pk.e.bytes) + fingerPrint.Sum(pk.Fingerprint[:0]) + pk.KeyId = binary.BigEndian.Uint64(pk.n.bytes[len(pk.n.bytes)-8:]) +} + +// parseRSA parses RSA public key material from the given Reader. See RFC 4880, +// section 5.5.2. +func (pk *PublicKeyV3) parseRSA(r io.Reader) (err error) { + if pk.n.bytes, pk.n.bitLength, err = readMPI(r); err != nil { + return + } + if pk.e.bytes, pk.e.bitLength, err = readMPI(r); err != nil { + return + } + + // RFC 4880 Section 12.2 requires the low 8 bytes of the + // modulus to form the key id. + if len(pk.n.bytes) < 8 { + return errors.StructuralError("v3 public key modulus is too short") + } + if len(pk.e.bytes) > 3 { + err = errors.UnsupportedError("large public exponent") + return + } + rsa := &rsa.PublicKey{N: new(big.Int).SetBytes(pk.n.bytes)} + for i := 0; i < len(pk.e.bytes); i++ { + rsa.E <<= 8 + rsa.E |= int(pk.e.bytes[i]) + } + pk.PublicKey = rsa + return +} + +// SerializeSignaturePrefix writes the prefix for this public key to the given Writer. +// The prefix is used when calculating a signature over this public key. See +// RFC 4880, section 5.2.4. +func (pk *PublicKeyV3) SerializeSignaturePrefix(w io.Writer) { + var pLength uint16 + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + pLength += 2 + uint16(len(pk.n.bytes)) + pLength += 2 + uint16(len(pk.e.bytes)) + default: + panic("unknown public key algorithm") + } + pLength += 6 + w.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)}) + return +} + +func (pk *PublicKeyV3) Serialize(w io.Writer) (err error) { + length := 8 // 8 byte header + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + length += 2 + len(pk.n.bytes) + length += 2 + len(pk.e.bytes) + default: + panic("unknown public key algorithm") + } + + packetType := packetTypePublicKey + if pk.IsSubkey { + packetType = packetTypePublicSubkey + } + if err = serializeHeader(w, packetType, length); err != nil { + return + } + return pk.serializeWithoutHeaders(w) +} + +// serializeWithoutHeaders marshals the PublicKey to w in the form of an +// OpenPGP public key packet, not including the packet header. +func (pk *PublicKeyV3) serializeWithoutHeaders(w io.Writer) (err error) { + var buf [8]byte + // Version 3 + buf[0] = 3 + // Creation time + t := uint32(pk.CreationTime.Unix()) + buf[1] = byte(t >> 24) + buf[2] = byte(t >> 16) + buf[3] = byte(t >> 8) + buf[4] = byte(t) + // Days to expire + buf[5] = byte(pk.DaysToExpire >> 8) + buf[6] = byte(pk.DaysToExpire) + // Public key algorithm + buf[7] = byte(pk.PubKeyAlgo) + + if _, err = w.Write(buf[:]); err != nil { + return + } + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + return writeMPIs(w, pk.n, pk.e) + } + return errors.InvalidArgumentError("bad public-key algorithm") +} + +// CanSign returns true iff this public key can generate signatures +func (pk *PublicKeyV3) CanSign() bool { + return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly +} + +// VerifySignatureV3 returns nil iff sig is a valid signature, made by this +// public key, of the data hashed into signed. signed is mutated by this call. +func (pk *PublicKeyV3) VerifySignatureV3(signed hash.Hash, sig *SignatureV3) (err error) { + if !pk.CanSign() { + return errors.InvalidArgumentError("public key cannot generate signatures") + } + + suffix := make([]byte, 5) + suffix[0] = byte(sig.SigType) + binary.BigEndian.PutUint32(suffix[1:], uint32(sig.CreationTime.Unix())) + signed.Write(suffix) + hashBytes := signed.Sum(nil) + + if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { + return errors.SignatureError("hash tag doesn't match") + } + + if pk.PubKeyAlgo != sig.PubKeyAlgo { + return errors.InvalidArgumentError("public key and signature use different algorithms") + } + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + if err = rsa.VerifyPKCS1v15(pk.PublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes); err != nil { + return errors.SignatureError("RSA verification failure") + } + return + default: + // V3 public keys only support RSA. + panic("shouldn't happen") + } +} + +// VerifyUserIdSignatureV3 returns nil iff sig is a valid signature, made by this +// public key, that id is the identity of pub. +func (pk *PublicKeyV3) VerifyUserIdSignatureV3(id string, pub *PublicKeyV3, sig *SignatureV3) (err error) { + h, err := userIdSignatureV3Hash(id, pk, sig.Hash) + if err != nil { + return err + } + return pk.VerifySignatureV3(h, sig) +} + +// VerifyKeySignatureV3 returns nil iff sig is a valid signature, made by this +// public key, of signed. +func (pk *PublicKeyV3) VerifyKeySignatureV3(signed *PublicKeyV3, sig *SignatureV3) (err error) { + h, err := keySignatureHash(pk, signed, sig.Hash) + if err != nil { + return err + } + return pk.VerifySignatureV3(h, sig) +} + +// userIdSignatureV3Hash returns a Hash of the message that needs to be signed +// to assert that pk is a valid key for id. +func userIdSignatureV3Hash(id string, pk signingKey, hfn crypto.Hash) (h hash.Hash, err error) { + if !hfn.Available() { + return nil, errors.UnsupportedError("hash function") + } + h = hfn.New() + + // RFC 4880, section 5.2.4 + pk.SerializeSignaturePrefix(h) + pk.serializeWithoutHeaders(h) + + h.Write([]byte(id)) + + return +} + +// KeyIdString returns the public key's fingerprint in capital hex +// (e.g. "6C7EE1B8621CC013"). +func (pk *PublicKeyV3) KeyIdString() string { + return fmt.Sprintf("%X", pk.KeyId) +} + +// KeyIdShortString returns the short form of public key's fingerprint +// in capital hex, as shown by gpg --list-keys (e.g. "621CC013"). +func (pk *PublicKeyV3) KeyIdShortString() string { + return fmt.Sprintf("%X", pk.KeyId&0xFFFFFFFF) +} + +// BitLength returns the bit length for the given public key. +func (pk *PublicKeyV3) BitLength() (bitLength uint16, err error) { + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + bitLength = pk.n.bitLength + default: + err = errors.InvalidArgumentError("bad public-key algorithm") + } + return +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/reader.go b/vendor/golang.org/x/crypto/openpgp/packet/reader.go new file mode 100644 index 0000000000..34bc7c613e --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/reader.go @@ -0,0 +1,76 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "golang.org/x/crypto/openpgp/errors" + "io" +) + +// Reader reads packets from an io.Reader and allows packets to be 'unread' so +// that they result from the next call to Next. +type Reader struct { + q []Packet + readers []io.Reader +} + +// New io.Readers are pushed when a compressed or encrypted packet is processed +// and recursively treated as a new source of packets. However, a carefully +// crafted packet can trigger an infinite recursive sequence of packets. See +// http://mumble.net/~campbell/misc/pgp-quine +// https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2013-4402 +// This constant limits the number of recursive packets that may be pushed. +const maxReaders = 32 + +// Next returns the most recently unread Packet, or reads another packet from +// the top-most io.Reader. Unknown packet types are skipped. +func (r *Reader) Next() (p Packet, err error) { + if len(r.q) > 0 { + p = r.q[len(r.q)-1] + r.q = r.q[:len(r.q)-1] + return + } + + for len(r.readers) > 0 { + p, err = Read(r.readers[len(r.readers)-1]) + if err == nil { + return + } + if err == io.EOF { + r.readers = r.readers[:len(r.readers)-1] + continue + } + if _, ok := err.(errors.UnknownPacketTypeError); !ok { + return nil, err + } + } + + return nil, io.EOF +} + +// Push causes the Reader to start reading from a new io.Reader. When an EOF +// error is seen from the new io.Reader, it is popped and the Reader continues +// to read from the next most recent io.Reader. Push returns a StructuralError +// if pushing the reader would exceed the maximum recursion level, otherwise it +// returns nil. +func (r *Reader) Push(reader io.Reader) (err error) { + if len(r.readers) >= maxReaders { + return errors.StructuralError("too many layers of packets") + } + r.readers = append(r.readers, reader) + return nil +} + +// Unread causes the given Packet to be returned from the next call to Next. +func (r *Reader) Unread(p Packet) { + r.q = append(r.q, p) +} + +func NewReader(r io.Reader) *Reader { + return &Reader{ + q: nil, + readers: []io.Reader{r}, + } +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/signature.go b/vendor/golang.org/x/crypto/openpgp/packet/signature.go new file mode 100644 index 0000000000..b2a24a5323 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/signature.go @@ -0,0 +1,731 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "encoding/asn1" + "encoding/binary" + "hash" + "io" + "math/big" + "strconv" + "time" + + "golang.org/x/crypto/openpgp/errors" + "golang.org/x/crypto/openpgp/s2k" +) + +const ( + // See RFC 4880, section 5.2.3.21 for details. + KeyFlagCertify = 1 << iota + KeyFlagSign + KeyFlagEncryptCommunications + KeyFlagEncryptStorage +) + +// Signature represents a signature. See RFC 4880, section 5.2. +type Signature struct { + SigType SignatureType + PubKeyAlgo PublicKeyAlgorithm + Hash crypto.Hash + + // HashSuffix is extra data that is hashed in after the signed data. + HashSuffix []byte + // HashTag contains the first two bytes of the hash for fast rejection + // of bad signed data. + HashTag [2]byte + CreationTime time.Time + + RSASignature parsedMPI + DSASigR, DSASigS parsedMPI + ECDSASigR, ECDSASigS parsedMPI + + // rawSubpackets contains the unparsed subpackets, in order. + rawSubpackets []outputSubpacket + + // The following are optional so are nil when not included in the + // signature. + + SigLifetimeSecs, KeyLifetimeSecs *uint32 + PreferredSymmetric, PreferredHash, PreferredCompression []uint8 + IssuerKeyId *uint64 + IsPrimaryId *bool + + // FlagsValid is set if any flags were given. See RFC 4880, section + // 5.2.3.21 for details. + FlagsValid bool + FlagCertify, FlagSign, FlagEncryptCommunications, FlagEncryptStorage bool + + // RevocationReason is set if this signature has been revoked. + // See RFC 4880, section 5.2.3.23 for details. + RevocationReason *uint8 + RevocationReasonText string + + // MDC is set if this signature has a feature packet that indicates + // support for MDC subpackets. + MDC bool + + // EmbeddedSignature, if non-nil, is a signature of the parent key, by + // this key. This prevents an attacker from claiming another's signing + // subkey as their own. + EmbeddedSignature *Signature + + outSubpackets []outputSubpacket +} + +func (sig *Signature) parse(r io.Reader) (err error) { + // RFC 4880, section 5.2.3 + var buf [5]byte + _, err = readFull(r, buf[:1]) + if err != nil { + return + } + if buf[0] != 4 { + err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0]))) + return + } + + _, err = readFull(r, buf[:5]) + if err != nil { + return + } + sig.SigType = SignatureType(buf[0]) + sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1]) + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA: + default: + err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) + return + } + + var ok bool + sig.Hash, ok = s2k.HashIdToHash(buf[2]) + if !ok { + return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2]))) + } + + hashedSubpacketsLength := int(buf[3])<<8 | int(buf[4]) + l := 6 + hashedSubpacketsLength + sig.HashSuffix = make([]byte, l+6) + sig.HashSuffix[0] = 4 + copy(sig.HashSuffix[1:], buf[:5]) + hashedSubpackets := sig.HashSuffix[6:l] + _, err = readFull(r, hashedSubpackets) + if err != nil { + return + } + // See RFC 4880, section 5.2.4 + trailer := sig.HashSuffix[l:] + trailer[0] = 4 + trailer[1] = 0xff + trailer[2] = uint8(l >> 24) + trailer[3] = uint8(l >> 16) + trailer[4] = uint8(l >> 8) + trailer[5] = uint8(l) + + err = parseSignatureSubpackets(sig, hashedSubpackets, true) + if err != nil { + return + } + + _, err = readFull(r, buf[:2]) + if err != nil { + return + } + unhashedSubpacketsLength := int(buf[0])<<8 | int(buf[1]) + unhashedSubpackets := make([]byte, unhashedSubpacketsLength) + _, err = readFull(r, unhashedSubpackets) + if err != nil { + return + } + err = parseSignatureSubpackets(sig, unhashedSubpackets, false) + if err != nil { + return + } + + _, err = readFull(r, sig.HashTag[:2]) + if err != nil { + return + } + + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r) + case PubKeyAlgoDSA: + sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r) + if err == nil { + sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r) + } + case PubKeyAlgoECDSA: + sig.ECDSASigR.bytes, sig.ECDSASigR.bitLength, err = readMPI(r) + if err == nil { + sig.ECDSASigS.bytes, sig.ECDSASigS.bitLength, err = readMPI(r) + } + default: + panic("unreachable") + } + return +} + +// parseSignatureSubpackets parses subpackets of the main signature packet. See +// RFC 4880, section 5.2.3.1. +func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool) (err error) { + for len(subpackets) > 0 { + subpackets, err = parseSignatureSubpacket(sig, subpackets, isHashed) + if err != nil { + return + } + } + + if sig.CreationTime.IsZero() { + err = errors.StructuralError("no creation time in signature") + } + + return +} + +type signatureSubpacketType uint8 + +const ( + creationTimeSubpacket signatureSubpacketType = 2 + signatureExpirationSubpacket signatureSubpacketType = 3 + keyExpirationSubpacket signatureSubpacketType = 9 + prefSymmetricAlgosSubpacket signatureSubpacketType = 11 + issuerSubpacket signatureSubpacketType = 16 + prefHashAlgosSubpacket signatureSubpacketType = 21 + prefCompressionSubpacket signatureSubpacketType = 22 + primaryUserIdSubpacket signatureSubpacketType = 25 + keyFlagsSubpacket signatureSubpacketType = 27 + reasonForRevocationSubpacket signatureSubpacketType = 29 + featuresSubpacket signatureSubpacketType = 30 + embeddedSignatureSubpacket signatureSubpacketType = 32 +) + +// parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1. +func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err error) { + // RFC 4880, section 5.2.3.1 + var ( + length uint32 + packetType signatureSubpacketType + isCritical bool + ) + switch { + case subpacket[0] < 192: + length = uint32(subpacket[0]) + subpacket = subpacket[1:] + case subpacket[0] < 255: + if len(subpacket) < 2 { + goto Truncated + } + length = uint32(subpacket[0]-192)<<8 + uint32(subpacket[1]) + 192 + subpacket = subpacket[2:] + default: + if len(subpacket) < 5 { + goto Truncated + } + length = uint32(subpacket[1])<<24 | + uint32(subpacket[2])<<16 | + uint32(subpacket[3])<<8 | + uint32(subpacket[4]) + subpacket = subpacket[5:] + } + if length > uint32(len(subpacket)) { + goto Truncated + } + rest = subpacket[length:] + subpacket = subpacket[:length] + if len(subpacket) == 0 { + err = errors.StructuralError("zero length signature subpacket") + return + } + packetType = signatureSubpacketType(subpacket[0] & 0x7f) + isCritical = subpacket[0]&0x80 == 0x80 + subpacket = subpacket[1:] + sig.rawSubpackets = append(sig.rawSubpackets, outputSubpacket{isHashed, packetType, isCritical, subpacket}) + switch packetType { + case creationTimeSubpacket: + if !isHashed { + err = errors.StructuralError("signature creation time in non-hashed area") + return + } + if len(subpacket) != 4 { + err = errors.StructuralError("signature creation time not four bytes") + return + } + t := binary.BigEndian.Uint32(subpacket) + sig.CreationTime = time.Unix(int64(t), 0) + case signatureExpirationSubpacket: + // Signature expiration time, section 5.2.3.10 + if !isHashed { + return + } + if len(subpacket) != 4 { + err = errors.StructuralError("expiration subpacket with bad length") + return + } + sig.SigLifetimeSecs = new(uint32) + *sig.SigLifetimeSecs = binary.BigEndian.Uint32(subpacket) + case keyExpirationSubpacket: + // Key expiration time, section 5.2.3.6 + if !isHashed { + return + } + if len(subpacket) != 4 { + err = errors.StructuralError("key expiration subpacket with bad length") + return + } + sig.KeyLifetimeSecs = new(uint32) + *sig.KeyLifetimeSecs = binary.BigEndian.Uint32(subpacket) + case prefSymmetricAlgosSubpacket: + // Preferred symmetric algorithms, section 5.2.3.7 + if !isHashed { + return + } + sig.PreferredSymmetric = make([]byte, len(subpacket)) + copy(sig.PreferredSymmetric, subpacket) + case issuerSubpacket: + // Issuer, section 5.2.3.5 + if len(subpacket) != 8 { + err = errors.StructuralError("issuer subpacket with bad length") + return + } + sig.IssuerKeyId = new(uint64) + *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket) + case prefHashAlgosSubpacket: + // Preferred hash algorithms, section 5.2.3.8 + if !isHashed { + return + } + sig.PreferredHash = make([]byte, len(subpacket)) + copy(sig.PreferredHash, subpacket) + case prefCompressionSubpacket: + // Preferred compression algorithms, section 5.2.3.9 + if !isHashed { + return + } + sig.PreferredCompression = make([]byte, len(subpacket)) + copy(sig.PreferredCompression, subpacket) + case primaryUserIdSubpacket: + // Primary User ID, section 5.2.3.19 + if !isHashed { + return + } + if len(subpacket) != 1 { + err = errors.StructuralError("primary user id subpacket with bad length") + return + } + sig.IsPrimaryId = new(bool) + if subpacket[0] > 0 { + *sig.IsPrimaryId = true + } + case keyFlagsSubpacket: + // Key flags, section 5.2.3.21 + if !isHashed { + return + } + if len(subpacket) == 0 { + err = errors.StructuralError("empty key flags subpacket") + return + } + sig.FlagsValid = true + if subpacket[0]&KeyFlagCertify != 0 { + sig.FlagCertify = true + } + if subpacket[0]&KeyFlagSign != 0 { + sig.FlagSign = true + } + if subpacket[0]&KeyFlagEncryptCommunications != 0 { + sig.FlagEncryptCommunications = true + } + if subpacket[0]&KeyFlagEncryptStorage != 0 { + sig.FlagEncryptStorage = true + } + case reasonForRevocationSubpacket: + // Reason For Revocation, section 5.2.3.23 + if !isHashed { + return + } + if len(subpacket) == 0 { + err = errors.StructuralError("empty revocation reason subpacket") + return + } + sig.RevocationReason = new(uint8) + *sig.RevocationReason = subpacket[0] + sig.RevocationReasonText = string(subpacket[1:]) + case featuresSubpacket: + // Features subpacket, section 5.2.3.24 specifies a very general + // mechanism for OpenPGP implementations to signal support for new + // features. In practice, the subpacket is used exclusively to + // indicate support for MDC-protected encryption. + sig.MDC = len(subpacket) >= 1 && subpacket[0]&1 == 1 + case embeddedSignatureSubpacket: + // Only usage is in signatures that cross-certify + // signing subkeys. section 5.2.3.26 describes the + // format, with its usage described in section 11.1 + if sig.EmbeddedSignature != nil { + err = errors.StructuralError("Cannot have multiple embedded signatures") + return + } + sig.EmbeddedSignature = new(Signature) + // Embedded signatures are required to be v4 signatures see + // section 12.1. However, we only parse v4 signatures in this + // file anyway. + if err := sig.EmbeddedSignature.parse(bytes.NewBuffer(subpacket)); err != nil { + return nil, err + } + if sigType := sig.EmbeddedSignature.SigType; sigType != SigTypePrimaryKeyBinding { + return nil, errors.StructuralError("cross-signature has unexpected type " + strconv.Itoa(int(sigType))) + } + default: + if isCritical { + err = errors.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType))) + return + } + } + return + +Truncated: + err = errors.StructuralError("signature subpacket truncated") + return +} + +// subpacketLengthLength returns the length, in bytes, of an encoded length value. +func subpacketLengthLength(length int) int { + if length < 192 { + return 1 + } + if length < 16320 { + return 2 + } + return 5 +} + +// serializeSubpacketLength marshals the given length into to. +func serializeSubpacketLength(to []byte, length int) int { + // RFC 4880, Section 4.2.2. + if length < 192 { + to[0] = byte(length) + return 1 + } + if length < 16320 { + length -= 192 + to[0] = byte((length >> 8) + 192) + to[1] = byte(length) + return 2 + } + to[0] = 255 + to[1] = byte(length >> 24) + to[2] = byte(length >> 16) + to[3] = byte(length >> 8) + to[4] = byte(length) + return 5 +} + +// subpacketsLength returns the serialized length, in bytes, of the given +// subpackets. +func subpacketsLength(subpackets []outputSubpacket, hashed bool) (length int) { + for _, subpacket := range subpackets { + if subpacket.hashed == hashed { + length += subpacketLengthLength(len(subpacket.contents) + 1) + length += 1 // type byte + length += len(subpacket.contents) + } + } + return +} + +// serializeSubpackets marshals the given subpackets into to. +func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) { + for _, subpacket := range subpackets { + if subpacket.hashed == hashed { + n := serializeSubpacketLength(to, len(subpacket.contents)+1) + to[n] = byte(subpacket.subpacketType) + to = to[1+n:] + n = copy(to, subpacket.contents) + to = to[n:] + } + } + return +} + +// KeyExpired returns whether sig is a self-signature of a key that has +// expired. +func (sig *Signature) KeyExpired(currentTime time.Time) bool { + if sig.KeyLifetimeSecs == nil { + return false + } + expiry := sig.CreationTime.Add(time.Duration(*sig.KeyLifetimeSecs) * time.Second) + return currentTime.After(expiry) +} + +// buildHashSuffix constructs the HashSuffix member of sig in preparation for signing. +func (sig *Signature) buildHashSuffix() (err error) { + hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true) + + var ok bool + l := 6 + hashedSubpacketsLen + sig.HashSuffix = make([]byte, l+6) + sig.HashSuffix[0] = 4 + sig.HashSuffix[1] = uint8(sig.SigType) + sig.HashSuffix[2] = uint8(sig.PubKeyAlgo) + sig.HashSuffix[3], ok = s2k.HashToHashId(sig.Hash) + if !ok { + sig.HashSuffix = nil + return errors.InvalidArgumentError("hash cannot be represented in OpenPGP: " + strconv.Itoa(int(sig.Hash))) + } + sig.HashSuffix[4] = byte(hashedSubpacketsLen >> 8) + sig.HashSuffix[5] = byte(hashedSubpacketsLen) + serializeSubpackets(sig.HashSuffix[6:l], sig.outSubpackets, true) + trailer := sig.HashSuffix[l:] + trailer[0] = 4 + trailer[1] = 0xff + trailer[2] = byte(l >> 24) + trailer[3] = byte(l >> 16) + trailer[4] = byte(l >> 8) + trailer[5] = byte(l) + return +} + +func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) { + err = sig.buildHashSuffix() + if err != nil { + return + } + + h.Write(sig.HashSuffix) + digest = h.Sum(nil) + copy(sig.HashTag[:], digest) + return +} + +// Sign signs a message with a private key. The hash, h, must contain +// the hash of the message to be signed and will be mutated by this function. +// On success, the signature is stored in sig. Call Serialize to write it out. +// If config is nil, sensible defaults will be used. +func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err error) { + sig.outSubpackets = sig.buildSubpackets() + digest, err := sig.signPrepareHash(h) + if err != nil { + return + } + + switch priv.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + // supports both *rsa.PrivateKey and crypto.Signer + sig.RSASignature.bytes, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash) + sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes)) + case PubKeyAlgoDSA: + dsaPriv := priv.PrivateKey.(*dsa.PrivateKey) + + // Need to truncate hashBytes to match FIPS 186-3 section 4.6. + subgroupSize := (dsaPriv.Q.BitLen() + 7) / 8 + if len(digest) > subgroupSize { + digest = digest[:subgroupSize] + } + r, s, err := dsa.Sign(config.Random(), dsaPriv, digest) + if err == nil { + sig.DSASigR.bytes = r.Bytes() + sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes)) + sig.DSASigS.bytes = s.Bytes() + sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes)) + } + case PubKeyAlgoECDSA: + var r, s *big.Int + if pk, ok := priv.PrivateKey.(*ecdsa.PrivateKey); ok { + // direct support, avoid asn1 wrapping/unwrapping + r, s, err = ecdsa.Sign(config.Random(), pk, digest) + } else { + var b []byte + b, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash) + if err == nil { + r, s, err = unwrapECDSASig(b) + } + } + if err == nil { + sig.ECDSASigR = fromBig(r) + sig.ECDSASigS = fromBig(s) + } + default: + err = errors.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) + } + + return +} + +// unwrapECDSASig parses the two integer components of an ASN.1-encoded ECDSA +// signature. +func unwrapECDSASig(b []byte) (r, s *big.Int, err error) { + var ecsdaSig struct { + R, S *big.Int + } + _, err = asn1.Unmarshal(b, &ecsdaSig) + if err != nil { + return + } + return ecsdaSig.R, ecsdaSig.S, nil +} + +// SignUserId computes a signature from priv, asserting that pub is a valid +// key for the identity id. On success, the signature is stored in sig. Call +// Serialize to write it out. +// If config is nil, sensible defaults will be used. +func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, config *Config) error { + h, err := userIdSignatureHash(id, pub, sig.Hash) + if err != nil { + return err + } + return sig.Sign(h, priv, config) +} + +// SignKey computes a signature from priv, asserting that pub is a subkey. On +// success, the signature is stored in sig. Call Serialize to write it out. +// If config is nil, sensible defaults will be used. +func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config) error { + h, err := keySignatureHash(&priv.PublicKey, pub, sig.Hash) + if err != nil { + return err + } + return sig.Sign(h, priv, config) +} + +// Serialize marshals sig to w. Sign, SignUserId or SignKey must have been +// called first. +func (sig *Signature) Serialize(w io.Writer) (err error) { + if len(sig.outSubpackets) == 0 { + sig.outSubpackets = sig.rawSubpackets + } + if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil && sig.ECDSASigR.bytes == nil { + return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize") + } + + sigLength := 0 + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + sigLength = 2 + len(sig.RSASignature.bytes) + case PubKeyAlgoDSA: + sigLength = 2 + len(sig.DSASigR.bytes) + sigLength += 2 + len(sig.DSASigS.bytes) + case PubKeyAlgoECDSA: + sigLength = 2 + len(sig.ECDSASigR.bytes) + sigLength += 2 + len(sig.ECDSASigS.bytes) + default: + panic("impossible") + } + + unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false) + length := len(sig.HashSuffix) - 6 /* trailer not included */ + + 2 /* length of unhashed subpackets */ + unhashedSubpacketsLen + + 2 /* hash tag */ + sigLength + err = serializeHeader(w, packetTypeSignature, length) + if err != nil { + return + } + + _, err = w.Write(sig.HashSuffix[:len(sig.HashSuffix)-6]) + if err != nil { + return + } + + unhashedSubpackets := make([]byte, 2+unhashedSubpacketsLen) + unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8) + unhashedSubpackets[1] = byte(unhashedSubpacketsLen) + serializeSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false) + + _, err = w.Write(unhashedSubpackets) + if err != nil { + return + } + _, err = w.Write(sig.HashTag[:]) + if err != nil { + return + } + + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + err = writeMPIs(w, sig.RSASignature) + case PubKeyAlgoDSA: + err = writeMPIs(w, sig.DSASigR, sig.DSASigS) + case PubKeyAlgoECDSA: + err = writeMPIs(w, sig.ECDSASigR, sig.ECDSASigS) + default: + panic("impossible") + } + return +} + +// outputSubpacket represents a subpacket to be marshaled. +type outputSubpacket struct { + hashed bool // true if this subpacket is in the hashed area. + subpacketType signatureSubpacketType + isCritical bool + contents []byte +} + +func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) { + creationTime := make([]byte, 4) + binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix())) + subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime}) + + if sig.IssuerKeyId != nil { + keyId := make([]byte, 8) + binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId) + subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, false, keyId}) + } + + if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 { + sigLifetime := make([]byte, 4) + binary.BigEndian.PutUint32(sigLifetime, *sig.SigLifetimeSecs) + subpackets = append(subpackets, outputSubpacket{true, signatureExpirationSubpacket, true, sigLifetime}) + } + + // Key flags may only appear in self-signatures or certification signatures. + + if sig.FlagsValid { + var flags byte + if sig.FlagCertify { + flags |= KeyFlagCertify + } + if sig.FlagSign { + flags |= KeyFlagSign + } + if sig.FlagEncryptCommunications { + flags |= KeyFlagEncryptCommunications + } + if sig.FlagEncryptStorage { + flags |= KeyFlagEncryptStorage + } + subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, false, []byte{flags}}) + } + + // The following subpackets may only appear in self-signatures + + if sig.KeyLifetimeSecs != nil && *sig.KeyLifetimeSecs != 0 { + keyLifetime := make([]byte, 4) + binary.BigEndian.PutUint32(keyLifetime, *sig.KeyLifetimeSecs) + subpackets = append(subpackets, outputSubpacket{true, keyExpirationSubpacket, true, keyLifetime}) + } + + if sig.IsPrimaryId != nil && *sig.IsPrimaryId { + subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}}) + } + + if len(sig.PreferredSymmetric) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefSymmetricAlgosSubpacket, false, sig.PreferredSymmetric}) + } + + if len(sig.PreferredHash) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefHashAlgosSubpacket, false, sig.PreferredHash}) + } + + if len(sig.PreferredCompression) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression}) + } + + return +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/signature_v3.go b/vendor/golang.org/x/crypto/openpgp/packet/signature_v3.go new file mode 100644 index 0000000000..6edff88934 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/signature_v3.go @@ -0,0 +1,146 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "crypto" + "encoding/binary" + "fmt" + "io" + "strconv" + "time" + + "golang.org/x/crypto/openpgp/errors" + "golang.org/x/crypto/openpgp/s2k" +) + +// SignatureV3 represents older version 3 signatures. These signatures are less secure +// than version 4 and should not be used to create new signatures. They are included +// here for backwards compatibility to read and validate with older key material. +// See RFC 4880, section 5.2.2. +type SignatureV3 struct { + SigType SignatureType + CreationTime time.Time + IssuerKeyId uint64 + PubKeyAlgo PublicKeyAlgorithm + Hash crypto.Hash + HashTag [2]byte + + RSASignature parsedMPI + DSASigR, DSASigS parsedMPI +} + +func (sig *SignatureV3) parse(r io.Reader) (err error) { + // RFC 4880, section 5.2.2 + var buf [8]byte + if _, err = readFull(r, buf[:1]); err != nil { + return + } + if buf[0] < 2 || buf[0] > 3 { + err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0]))) + return + } + if _, err = readFull(r, buf[:1]); err != nil { + return + } + if buf[0] != 5 { + err = errors.UnsupportedError( + "invalid hashed material length " + strconv.Itoa(int(buf[0]))) + return + } + + // Read hashed material: signature type + creation time + if _, err = readFull(r, buf[:5]); err != nil { + return + } + sig.SigType = SignatureType(buf[0]) + t := binary.BigEndian.Uint32(buf[1:5]) + sig.CreationTime = time.Unix(int64(t), 0) + + // Eight-octet Key ID of signer. + if _, err = readFull(r, buf[:8]); err != nil { + return + } + sig.IssuerKeyId = binary.BigEndian.Uint64(buf[:]) + + // Public-key and hash algorithm + if _, err = readFull(r, buf[:2]); err != nil { + return + } + sig.PubKeyAlgo = PublicKeyAlgorithm(buf[0]) + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA: + default: + err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) + return + } + var ok bool + if sig.Hash, ok = s2k.HashIdToHash(buf[1]); !ok { + return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2]))) + } + + // Two-octet field holding left 16 bits of signed hash value. + if _, err = readFull(r, sig.HashTag[:2]); err != nil { + return + } + + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r) + case PubKeyAlgoDSA: + if sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r); err != nil { + return + } + sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r) + default: + panic("unreachable") + } + return +} + +// Serialize marshals sig to w. Sign, SignUserId or SignKey must have been +// called first. +func (sig *SignatureV3) Serialize(w io.Writer) (err error) { + buf := make([]byte, 8) + + // Write the sig type and creation time + buf[0] = byte(sig.SigType) + binary.BigEndian.PutUint32(buf[1:5], uint32(sig.CreationTime.Unix())) + if _, err = w.Write(buf[:5]); err != nil { + return + } + + // Write the issuer long key ID + binary.BigEndian.PutUint64(buf[:8], sig.IssuerKeyId) + if _, err = w.Write(buf[:8]); err != nil { + return + } + + // Write public key algorithm, hash ID, and hash value + buf[0] = byte(sig.PubKeyAlgo) + hashId, ok := s2k.HashToHashId(sig.Hash) + if !ok { + return errors.UnsupportedError(fmt.Sprintf("hash function %v", sig.Hash)) + } + buf[1] = hashId + copy(buf[2:4], sig.HashTag[:]) + if _, err = w.Write(buf[:4]); err != nil { + return + } + + if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil { + return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize") + } + + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + err = writeMPIs(w, sig.RSASignature) + case PubKeyAlgoDSA: + err = writeMPIs(w, sig.DSASigR, sig.DSASigS) + default: + panic("impossible") + } + return +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/symmetric_key_encrypted.go b/vendor/golang.org/x/crypto/openpgp/packet/symmetric_key_encrypted.go new file mode 100644 index 0000000000..744c2d2c42 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/symmetric_key_encrypted.go @@ -0,0 +1,155 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "crypto/cipher" + "io" + "strconv" + + "golang.org/x/crypto/openpgp/errors" + "golang.org/x/crypto/openpgp/s2k" +) + +// This is the largest session key that we'll support. Since no 512-bit cipher +// has even been seriously used, this is comfortably large. +const maxSessionKeySizeInBytes = 64 + +// SymmetricKeyEncrypted represents a passphrase protected session key. See RFC +// 4880, section 5.3. +type SymmetricKeyEncrypted struct { + CipherFunc CipherFunction + s2k func(out, in []byte) + encryptedKey []byte +} + +const symmetricKeyEncryptedVersion = 4 + +func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error { + // RFC 4880, section 5.3. + var buf [2]byte + if _, err := readFull(r, buf[:]); err != nil { + return err + } + if buf[0] != symmetricKeyEncryptedVersion { + return errors.UnsupportedError("SymmetricKeyEncrypted version") + } + ske.CipherFunc = CipherFunction(buf[1]) + + if ske.CipherFunc.KeySize() == 0 { + return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1]))) + } + + var err error + ske.s2k, err = s2k.Parse(r) + if err != nil { + return err + } + + encryptedKey := make([]byte, maxSessionKeySizeInBytes) + // The session key may follow. We just have to try and read to find + // out. If it exists then we limit it to maxSessionKeySizeInBytes. + n, err := readFull(r, encryptedKey) + if err != nil && err != io.ErrUnexpectedEOF { + return err + } + + if n != 0 { + if n == maxSessionKeySizeInBytes { + return errors.UnsupportedError("oversized encrypted session key") + } + ske.encryptedKey = encryptedKey[:n] + } + + return nil +} + +// Decrypt attempts to decrypt an encrypted session key and returns the key and +// the cipher to use when decrypting a subsequent Symmetrically Encrypted Data +// packet. +func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunction, error) { + key := make([]byte, ske.CipherFunc.KeySize()) + ske.s2k(key, passphrase) + + if len(ske.encryptedKey) == 0 { + return key, ske.CipherFunc, nil + } + + // the IV is all zeros + iv := make([]byte, ske.CipherFunc.blockSize()) + c := cipher.NewCFBDecrypter(ske.CipherFunc.new(key), iv) + plaintextKey := make([]byte, len(ske.encryptedKey)) + c.XORKeyStream(plaintextKey, ske.encryptedKey) + cipherFunc := CipherFunction(plaintextKey[0]) + if cipherFunc.blockSize() == 0 { + return nil, ske.CipherFunc, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) + } + plaintextKey = plaintextKey[1:] + if l, cipherKeySize := len(plaintextKey), cipherFunc.KeySize(); l != cipherFunc.KeySize() { + return nil, cipherFunc, errors.StructuralError("length of decrypted key (" + strconv.Itoa(l) + ") " + + "not equal to cipher keysize (" + strconv.Itoa(cipherKeySize) + ")") + } + return plaintextKey, cipherFunc, nil +} + +// SerializeSymmetricKeyEncrypted serializes a symmetric key packet to w. The +// packet contains a random session key, encrypted by a key derived from the +// given passphrase. The session key is returned and must be passed to +// SerializeSymmetricallyEncrypted. +// If config is nil, sensible defaults will be used. +func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Config) (key []byte, err error) { + cipherFunc := config.Cipher() + keySize := cipherFunc.KeySize() + if keySize == 0 { + return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) + } + + s2kBuf := new(bytes.Buffer) + keyEncryptingKey := make([]byte, keySize) + // s2k.Serialize salts and stretches the passphrase, and writes the + // resulting key to keyEncryptingKey and the s2k descriptor to s2kBuf. + err = s2k.Serialize(s2kBuf, keyEncryptingKey, config.Random(), passphrase, &s2k.Config{Hash: config.Hash(), S2KCount: config.PasswordHashIterations()}) + if err != nil { + return + } + s2kBytes := s2kBuf.Bytes() + + packetLength := 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize + err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength) + if err != nil { + return + } + + var buf [2]byte + buf[0] = symmetricKeyEncryptedVersion + buf[1] = byte(cipherFunc) + _, err = w.Write(buf[:]) + if err != nil { + return + } + _, err = w.Write(s2kBytes) + if err != nil { + return + } + + sessionKey := make([]byte, keySize) + _, err = io.ReadFull(config.Random(), sessionKey) + if err != nil { + return + } + iv := make([]byte, cipherFunc.blockSize()) + c := cipher.NewCFBEncrypter(cipherFunc.new(keyEncryptingKey), iv) + encryptedCipherAndKey := make([]byte, keySize+1) + c.XORKeyStream(encryptedCipherAndKey, buf[1:]) + c.XORKeyStream(encryptedCipherAndKey[1:], sessionKey) + _, err = w.Write(encryptedCipherAndKey) + if err != nil { + return + } + + key = sessionKey + return +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/symmetrically_encrypted.go b/vendor/golang.org/x/crypto/openpgp/packet/symmetrically_encrypted.go new file mode 100644 index 0000000000..6126030eb9 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/symmetrically_encrypted.go @@ -0,0 +1,290 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "crypto/cipher" + "crypto/sha1" + "crypto/subtle" + "golang.org/x/crypto/openpgp/errors" + "hash" + "io" + "strconv" +) + +// SymmetricallyEncrypted represents a symmetrically encrypted byte string. The +// encrypted contents will consist of more OpenPGP packets. See RFC 4880, +// sections 5.7 and 5.13. +type SymmetricallyEncrypted struct { + MDC bool // true iff this is a type 18 packet and thus has an embedded MAC. + contents io.Reader + prefix []byte +} + +const symmetricallyEncryptedVersion = 1 + +func (se *SymmetricallyEncrypted) parse(r io.Reader) error { + if se.MDC { + // See RFC 4880, section 5.13. + var buf [1]byte + _, err := readFull(r, buf[:]) + if err != nil { + return err + } + if buf[0] != symmetricallyEncryptedVersion { + return errors.UnsupportedError("unknown SymmetricallyEncrypted version") + } + } + se.contents = r + return nil +} + +// Decrypt returns a ReadCloser, from which the decrypted contents of the +// packet can be read. An incorrect key can, with high probability, be detected +// immediately and this will result in a KeyIncorrect error being returned. +func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, error) { + keySize := c.KeySize() + if keySize == 0 { + return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c))) + } + if len(key) != keySize { + return nil, errors.InvalidArgumentError("SymmetricallyEncrypted: incorrect key length") + } + + if se.prefix == nil { + se.prefix = make([]byte, c.blockSize()+2) + _, err := readFull(se.contents, se.prefix) + if err != nil { + return nil, err + } + } else if len(se.prefix) != c.blockSize()+2 { + return nil, errors.InvalidArgumentError("can't try ciphers with different block lengths") + } + + ocfbResync := OCFBResync + if se.MDC { + // MDC packets use a different form of OCFB mode. + ocfbResync = OCFBNoResync + } + + s := NewOCFBDecrypter(c.new(key), se.prefix, ocfbResync) + if s == nil { + return nil, errors.ErrKeyIncorrect + } + + plaintext := cipher.StreamReader{S: s, R: se.contents} + + if se.MDC { + // MDC packets have an embedded hash that we need to check. + h := sha1.New() + h.Write(se.prefix) + return &seMDCReader{in: plaintext, h: h}, nil + } + + // Otherwise, we just need to wrap plaintext so that it's a valid ReadCloser. + return seReader{plaintext}, nil +} + +// seReader wraps an io.Reader with a no-op Close method. +type seReader struct { + in io.Reader +} + +func (ser seReader) Read(buf []byte) (int, error) { + return ser.in.Read(buf) +} + +func (ser seReader) Close() error { + return nil +} + +const mdcTrailerSize = 1 /* tag byte */ + 1 /* length byte */ + sha1.Size + +// An seMDCReader wraps an io.Reader, maintains a running hash and keeps hold +// of the most recent 22 bytes (mdcTrailerSize). Upon EOF, those bytes form an +// MDC packet containing a hash of the previous contents which is checked +// against the running hash. See RFC 4880, section 5.13. +type seMDCReader struct { + in io.Reader + h hash.Hash + trailer [mdcTrailerSize]byte + scratch [mdcTrailerSize]byte + trailerUsed int + error bool + eof bool +} + +func (ser *seMDCReader) Read(buf []byte) (n int, err error) { + if ser.error { + err = io.ErrUnexpectedEOF + return + } + if ser.eof { + err = io.EOF + return + } + + // If we haven't yet filled the trailer buffer then we must do that + // first. + for ser.trailerUsed < mdcTrailerSize { + n, err = ser.in.Read(ser.trailer[ser.trailerUsed:]) + ser.trailerUsed += n + if err == io.EOF { + if ser.trailerUsed != mdcTrailerSize { + n = 0 + err = io.ErrUnexpectedEOF + ser.error = true + return + } + ser.eof = true + n = 0 + return + } + + if err != nil { + n = 0 + return + } + } + + // If it's a short read then we read into a temporary buffer and shift + // the data into the caller's buffer. + if len(buf) <= mdcTrailerSize { + n, err = readFull(ser.in, ser.scratch[:len(buf)]) + copy(buf, ser.trailer[:n]) + ser.h.Write(buf[:n]) + copy(ser.trailer[:], ser.trailer[n:]) + copy(ser.trailer[mdcTrailerSize-n:], ser.scratch[:]) + if n < len(buf) { + ser.eof = true + err = io.EOF + } + return + } + + n, err = ser.in.Read(buf[mdcTrailerSize:]) + copy(buf, ser.trailer[:]) + ser.h.Write(buf[:n]) + copy(ser.trailer[:], buf[n:]) + + if err == io.EOF { + ser.eof = true + } + return +} + +// This is a new-format packet tag byte for a type 19 (MDC) packet. +const mdcPacketTagByte = byte(0x80) | 0x40 | 19 + +func (ser *seMDCReader) Close() error { + if ser.error { + return errors.SignatureError("error during reading") + } + + for !ser.eof { + // We haven't seen EOF so we need to read to the end + var buf [1024]byte + _, err := ser.Read(buf[:]) + if err == io.EOF { + break + } + if err != nil { + return errors.SignatureError("error during reading") + } + } + + if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size { + return errors.SignatureError("MDC packet not found") + } + ser.h.Write(ser.trailer[:2]) + + final := ser.h.Sum(nil) + if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 { + return errors.SignatureError("hash mismatch") + } + return nil +} + +// An seMDCWriter writes through to an io.WriteCloser while maintains a running +// hash of the data written. On close, it emits an MDC packet containing the +// running hash. +type seMDCWriter struct { + w io.WriteCloser + h hash.Hash +} + +func (w *seMDCWriter) Write(buf []byte) (n int, err error) { + w.h.Write(buf) + return w.w.Write(buf) +} + +func (w *seMDCWriter) Close() (err error) { + var buf [mdcTrailerSize]byte + + buf[0] = mdcPacketTagByte + buf[1] = sha1.Size + w.h.Write(buf[:2]) + digest := w.h.Sum(nil) + copy(buf[2:], digest) + + _, err = w.w.Write(buf[:]) + if err != nil { + return + } + return w.w.Close() +} + +// noOpCloser is like an ioutil.NopCloser, but for an io.Writer. +type noOpCloser struct { + w io.Writer +} + +func (c noOpCloser) Write(data []byte) (n int, err error) { + return c.w.Write(data) +} + +func (c noOpCloser) Close() error { + return nil +} + +// SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet +// to w and returns a WriteCloser to which the to-be-encrypted packets can be +// written. +// If config is nil, sensible defaults will be used. +func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte, config *Config) (contents io.WriteCloser, err error) { + if c.KeySize() != len(key) { + return nil, errors.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length") + } + writeCloser := noOpCloser{w} + ciphertext, err := serializeStreamHeader(writeCloser, packetTypeSymmetricallyEncryptedMDC) + if err != nil { + return + } + + _, err = ciphertext.Write([]byte{symmetricallyEncryptedVersion}) + if err != nil { + return + } + + block := c.new(key) + blockSize := block.BlockSize() + iv := make([]byte, blockSize) + _, err = config.Random().Read(iv) + if err != nil { + return + } + s, prefix := NewOCFBEncrypter(block, iv, OCFBNoResync) + _, err = ciphertext.Write(prefix) + if err != nil { + return + } + plaintext := cipher.StreamWriter{S: s, W: ciphertext} + + h := sha1.New() + h.Write(iv) + h.Write(iv[blockSize-2:]) + contents = &seMDCWriter{w: plaintext, h: h} + return +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/userattribute.go b/vendor/golang.org/x/crypto/openpgp/packet/userattribute.go new file mode 100644 index 0000000000..d19ffbc786 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/userattribute.go @@ -0,0 +1,91 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "image" + "image/jpeg" + "io" + "io/ioutil" +) + +const UserAttrImageSubpacket = 1 + +// UserAttribute is capable of storing other types of data about a user +// beyond name, email and a text comment. In practice, user attributes are typically used +// to store a signed thumbnail photo JPEG image of the user. +// See RFC 4880, section 5.12. +type UserAttribute struct { + Contents []*OpaqueSubpacket +} + +// NewUserAttributePhoto creates a user attribute packet +// containing the given images. +func NewUserAttributePhoto(photos ...image.Image) (uat *UserAttribute, err error) { + uat = new(UserAttribute) + for _, photo := range photos { + var buf bytes.Buffer + // RFC 4880, Section 5.12.1. + data := []byte{ + 0x10, 0x00, // Little-endian image header length (16 bytes) + 0x01, // Image header version 1 + 0x01, // JPEG + 0, 0, 0, 0, // 12 reserved octets, must be all zero. + 0, 0, 0, 0, + 0, 0, 0, 0} + if _, err = buf.Write(data); err != nil { + return + } + if err = jpeg.Encode(&buf, photo, nil); err != nil { + return + } + uat.Contents = append(uat.Contents, &OpaqueSubpacket{ + SubType: UserAttrImageSubpacket, + Contents: buf.Bytes()}) + } + return +} + +// NewUserAttribute creates a new user attribute packet containing the given subpackets. +func NewUserAttribute(contents ...*OpaqueSubpacket) *UserAttribute { + return &UserAttribute{Contents: contents} +} + +func (uat *UserAttribute) parse(r io.Reader) (err error) { + // RFC 4880, section 5.13 + b, err := ioutil.ReadAll(r) + if err != nil { + return + } + uat.Contents, err = OpaqueSubpackets(b) + return +} + +// Serialize marshals the user attribute to w in the form of an OpenPGP packet, including +// header. +func (uat *UserAttribute) Serialize(w io.Writer) (err error) { + var buf bytes.Buffer + for _, sp := range uat.Contents { + sp.Serialize(&buf) + } + if err = serializeHeader(w, packetTypeUserAttribute, buf.Len()); err != nil { + return err + } + _, err = w.Write(buf.Bytes()) + return +} + +// ImageData returns zero or more byte slices, each containing +// JPEG File Interchange Format (JFIF), for each photo in the +// user attribute packet. +func (uat *UserAttribute) ImageData() (imageData [][]byte) { + for _, sp := range uat.Contents { + if sp.SubType == UserAttrImageSubpacket && len(sp.Contents) > 16 { + imageData = append(imageData, sp.Contents[16:]) + } + } + return +} diff --git a/vendor/golang.org/x/crypto/openpgp/packet/userid.go b/vendor/golang.org/x/crypto/openpgp/packet/userid.go new file mode 100644 index 0000000000..d6bea7d4ac --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/packet/userid.go @@ -0,0 +1,160 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "io" + "io/ioutil" + "strings" +) + +// UserId contains text that is intended to represent the name and email +// address of the key holder. See RFC 4880, section 5.11. By convention, this +// takes the form "Full Name (Comment) " +type UserId struct { + Id string // By convention, this takes the form "Full Name (Comment) " which is split out in the fields below. + + Name, Comment, Email string +} + +func hasInvalidCharacters(s string) bool { + for _, c := range s { + switch c { + case '(', ')', '<', '>', 0: + return true + } + } + return false +} + +// NewUserId returns a UserId or nil if any of the arguments contain invalid +// characters. The invalid characters are '\x00', '(', ')', '<' and '>' +func NewUserId(name, comment, email string) *UserId { + // RFC 4880 doesn't deal with the structure of userid strings; the + // name, comment and email form is just a convention. However, there's + // no convention about escaping the metacharacters and GPG just refuses + // to create user ids where, say, the name contains a '('. We mirror + // this behaviour. + + if hasInvalidCharacters(name) || hasInvalidCharacters(comment) || hasInvalidCharacters(email) { + return nil + } + + uid := new(UserId) + uid.Name, uid.Comment, uid.Email = name, comment, email + uid.Id = name + if len(comment) > 0 { + if len(uid.Id) > 0 { + uid.Id += " " + } + uid.Id += "(" + uid.Id += comment + uid.Id += ")" + } + if len(email) > 0 { + if len(uid.Id) > 0 { + uid.Id += " " + } + uid.Id += "<" + uid.Id += email + uid.Id += ">" + } + return uid +} + +func (uid *UserId) parse(r io.Reader) (err error) { + // RFC 4880, section 5.11 + b, err := ioutil.ReadAll(r) + if err != nil { + return + } + uid.Id = string(b) + uid.Name, uid.Comment, uid.Email = parseUserId(uid.Id) + return +} + +// Serialize marshals uid to w in the form of an OpenPGP packet, including +// header. +func (uid *UserId) Serialize(w io.Writer) error { + err := serializeHeader(w, packetTypeUserId, len(uid.Id)) + if err != nil { + return err + } + _, err = w.Write([]byte(uid.Id)) + return err +} + +// parseUserId extracts the name, comment and email from a user id string that +// is formatted as "Full Name (Comment) ". +func parseUserId(id string) (name, comment, email string) { + var n, c, e struct { + start, end int + } + var state int + + for offset, rune := range id { + switch state { + case 0: + // Entering name + n.start = offset + state = 1 + fallthrough + case 1: + // In name + if rune == '(' { + state = 2 + n.end = offset + } else if rune == '<' { + state = 5 + n.end = offset + } + case 2: + // Entering comment + c.start = offset + state = 3 + fallthrough + case 3: + // In comment + if rune == ')' { + state = 4 + c.end = offset + } + case 4: + // Between comment and email + if rune == '<' { + state = 5 + } + case 5: + // Entering email + e.start = offset + state = 6 + fallthrough + case 6: + // In email + if rune == '>' { + state = 7 + e.end = offset + } + default: + // After email + } + } + switch state { + case 1: + // ended in the name + n.end = len(id) + case 3: + // ended in comment + c.end = len(id) + case 6: + // ended in email + e.end = len(id) + } + + name = strings.TrimSpace(id[n.start:n.end]) + comment = strings.TrimSpace(id[c.start:c.end]) + email = strings.TrimSpace(id[e.start:e.end]) + return +} diff --git a/vendor/golang.org/x/crypto/openpgp/read.go b/vendor/golang.org/x/crypto/openpgp/read.go new file mode 100644 index 0000000000..48a8931468 --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/read.go @@ -0,0 +1,448 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package openpgp implements high level operations on OpenPGP messages. +// +// Deprecated: this package is unmaintained except for security fixes. New +// applications should consider a more focused, modern alternative to OpenPGP +// for their specific task. If you are required to interoperate with OpenPGP +// systems and need a maintained package, consider a community fork. +// See https://golang.org/issue/44226. +package openpgp // import "golang.org/x/crypto/openpgp" + +import ( + "crypto" + _ "crypto/sha256" + "hash" + "io" + "strconv" + + "golang.org/x/crypto/openpgp/armor" + "golang.org/x/crypto/openpgp/errors" + "golang.org/x/crypto/openpgp/packet" +) + +// SignatureType is the armor type for a PGP signature. +var SignatureType = "PGP SIGNATURE" + +// readArmored reads an armored block with the given type. +func readArmored(r io.Reader, expectedType string) (body io.Reader, err error) { + block, err := armor.Decode(r) + if err != nil { + return + } + + if block.Type != expectedType { + return nil, errors.InvalidArgumentError("expected '" + expectedType + "', got: " + block.Type) + } + + return block.Body, nil +} + +// MessageDetails contains the result of parsing an OpenPGP encrypted and/or +// signed message. +type MessageDetails struct { + IsEncrypted bool // true if the message was encrypted. + EncryptedToKeyIds []uint64 // the list of recipient key ids. + IsSymmetricallyEncrypted bool // true if a passphrase could have decrypted the message. + DecryptedWith Key // the private key used to decrypt the message, if any. + IsSigned bool // true if the message is signed. + SignedByKeyId uint64 // the key id of the signer, if any. + SignedBy *Key // the key of the signer, if available. + LiteralData *packet.LiteralData // the metadata of the contents + UnverifiedBody io.Reader // the contents of the message. + + // If IsSigned is true and SignedBy is non-zero then the signature will + // be verified as UnverifiedBody is read. The signature cannot be + // checked until the whole of UnverifiedBody is read so UnverifiedBody + // must be consumed until EOF before the data can be trusted. Even if a + // message isn't signed (or the signer is unknown) the data may contain + // an authentication code that is only checked once UnverifiedBody has + // been consumed. Once EOF has been seen, the following fields are + // valid. (An authentication code failure is reported as a + // SignatureError error when reading from UnverifiedBody.) + SignatureError error // nil if the signature is good. + Signature *packet.Signature // the signature packet itself, if v4 (default) + SignatureV3 *packet.SignatureV3 // the signature packet if it is a v2 or v3 signature + + decrypted io.ReadCloser +} + +// A PromptFunction is used as a callback by functions that may need to decrypt +// a private key, or prompt for a passphrase. It is called with a list of +// acceptable, encrypted private keys and a boolean that indicates whether a +// passphrase is usable. It should either decrypt a private key or return a +// passphrase to try. If the decrypted private key or given passphrase isn't +// correct, the function will be called again, forever. Any error returned will +// be passed up. +type PromptFunction func(keys []Key, symmetric bool) ([]byte, error) + +// A keyEnvelopePair is used to store a private key with the envelope that +// contains a symmetric key, encrypted with that key. +type keyEnvelopePair struct { + key Key + encryptedKey *packet.EncryptedKey +} + +// ReadMessage parses an OpenPGP message that may be signed and/or encrypted. +// The given KeyRing should contain both public keys (for signature +// verification) and, possibly encrypted, private keys for decrypting. +// If config is nil, sensible defaults will be used. +func ReadMessage(r io.Reader, keyring KeyRing, prompt PromptFunction, config *packet.Config) (md *MessageDetails, err error) { + var p packet.Packet + + var symKeys []*packet.SymmetricKeyEncrypted + var pubKeys []keyEnvelopePair + var se *packet.SymmetricallyEncrypted + + packets := packet.NewReader(r) + md = new(MessageDetails) + md.IsEncrypted = true + + // The message, if encrypted, starts with a number of packets + // containing an encrypted decryption key. The decryption key is either + // encrypted to a public key, or with a passphrase. This loop + // collects these packets. +ParsePackets: + for { + p, err = packets.Next() + if err != nil { + return nil, err + } + switch p := p.(type) { + case *packet.SymmetricKeyEncrypted: + // This packet contains the decryption key encrypted with a passphrase. + md.IsSymmetricallyEncrypted = true + symKeys = append(symKeys, p) + case *packet.EncryptedKey: + // This packet contains the decryption key encrypted to a public key. + md.EncryptedToKeyIds = append(md.EncryptedToKeyIds, p.KeyId) + switch p.Algo { + case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal: + break + default: + continue + } + var keys []Key + if p.KeyId == 0 { + keys = keyring.DecryptionKeys() + } else { + keys = keyring.KeysById(p.KeyId) + } + for _, k := range keys { + pubKeys = append(pubKeys, keyEnvelopePair{k, p}) + } + case *packet.SymmetricallyEncrypted: + se = p + break ParsePackets + case *packet.Compressed, *packet.LiteralData, *packet.OnePassSignature: + // This message isn't encrypted. + if len(symKeys) != 0 || len(pubKeys) != 0 { + return nil, errors.StructuralError("key material not followed by encrypted message") + } + packets.Unread(p) + return readSignedMessage(packets, nil, keyring) + } + } + + var candidates []Key + var decrypted io.ReadCloser + + // Now that we have the list of encrypted keys we need to decrypt at + // least one of them or, if we cannot, we need to call the prompt + // function so that it can decrypt a key or give us a passphrase. +FindKey: + for { + // See if any of the keys already have a private key available + candidates = candidates[:0] + candidateFingerprints := make(map[string]bool) + + for _, pk := range pubKeys { + if pk.key.PrivateKey == nil { + continue + } + if !pk.key.PrivateKey.Encrypted { + if len(pk.encryptedKey.Key) == 0 { + pk.encryptedKey.Decrypt(pk.key.PrivateKey, config) + } + if len(pk.encryptedKey.Key) == 0 { + continue + } + decrypted, err = se.Decrypt(pk.encryptedKey.CipherFunc, pk.encryptedKey.Key) + if err != nil && err != errors.ErrKeyIncorrect { + return nil, err + } + if decrypted != nil { + md.DecryptedWith = pk.key + break FindKey + } + } else { + fpr := string(pk.key.PublicKey.Fingerprint[:]) + if v := candidateFingerprints[fpr]; v { + continue + } + candidates = append(candidates, pk.key) + candidateFingerprints[fpr] = true + } + } + + if len(candidates) == 0 && len(symKeys) == 0 { + return nil, errors.ErrKeyIncorrect + } + + if prompt == nil { + return nil, errors.ErrKeyIncorrect + } + + passphrase, err := prompt(candidates, len(symKeys) != 0) + if err != nil { + return nil, err + } + + // Try the symmetric passphrase first + if len(symKeys) != 0 && passphrase != nil { + for _, s := range symKeys { + key, cipherFunc, err := s.Decrypt(passphrase) + if err == nil { + decrypted, err = se.Decrypt(cipherFunc, key) + if err != nil && err != errors.ErrKeyIncorrect { + return nil, err + } + if decrypted != nil { + break FindKey + } + } + + } + } + } + + md.decrypted = decrypted + if err := packets.Push(decrypted); err != nil { + return nil, err + } + return readSignedMessage(packets, md, keyring) +} + +// readSignedMessage reads a possibly signed message if mdin is non-zero then +// that structure is updated and returned. Otherwise a fresh MessageDetails is +// used. +func readSignedMessage(packets *packet.Reader, mdin *MessageDetails, keyring KeyRing) (md *MessageDetails, err error) { + if mdin == nil { + mdin = new(MessageDetails) + } + md = mdin + + var p packet.Packet + var h hash.Hash + var wrappedHash hash.Hash +FindLiteralData: + for { + p, err = packets.Next() + if err != nil { + return nil, err + } + switch p := p.(type) { + case *packet.Compressed: + if err := packets.Push(p.Body); err != nil { + return nil, err + } + case *packet.OnePassSignature: + if !p.IsLast { + return nil, errors.UnsupportedError("nested signatures") + } + + h, wrappedHash, err = hashForSignature(p.Hash, p.SigType) + if err != nil { + md = nil + return + } + + md.IsSigned = true + md.SignedByKeyId = p.KeyId + keys := keyring.KeysByIdUsage(p.KeyId, packet.KeyFlagSign) + if len(keys) > 0 { + md.SignedBy = &keys[0] + } + case *packet.LiteralData: + md.LiteralData = p + break FindLiteralData + } + } + + if md.SignedBy != nil { + md.UnverifiedBody = &signatureCheckReader{packets, h, wrappedHash, md} + } else if md.decrypted != nil { + md.UnverifiedBody = checkReader{md} + } else { + md.UnverifiedBody = md.LiteralData.Body + } + + return md, nil +} + +// hashForSignature returns a pair of hashes that can be used to verify a +// signature. The signature may specify that the contents of the signed message +// should be preprocessed (i.e. to normalize line endings). Thus this function +// returns two hashes. The second should be used to hash the message itself and +// performs any needed preprocessing. +func hashForSignature(hashId crypto.Hash, sigType packet.SignatureType) (hash.Hash, hash.Hash, error) { + if !hashId.Available() { + return nil, nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hashId))) + } + h := hashId.New() + + switch sigType { + case packet.SigTypeBinary: + return h, h, nil + case packet.SigTypeText: + return h, NewCanonicalTextHash(h), nil + } + + return nil, nil, errors.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(sigType))) +} + +// checkReader wraps an io.Reader from a LiteralData packet. When it sees EOF +// it closes the ReadCloser from any SymmetricallyEncrypted packet to trigger +// MDC checks. +type checkReader struct { + md *MessageDetails +} + +func (cr checkReader) Read(buf []byte) (n int, err error) { + n, err = cr.md.LiteralData.Body.Read(buf) + if err == io.EOF { + mdcErr := cr.md.decrypted.Close() + if mdcErr != nil { + err = mdcErr + } + } + return +} + +// signatureCheckReader wraps an io.Reader from a LiteralData packet and hashes +// the data as it is read. When it sees an EOF from the underlying io.Reader +// it parses and checks a trailing Signature packet and triggers any MDC checks. +type signatureCheckReader struct { + packets *packet.Reader + h, wrappedHash hash.Hash + md *MessageDetails +} + +func (scr *signatureCheckReader) Read(buf []byte) (n int, err error) { + n, err = scr.md.LiteralData.Body.Read(buf) + scr.wrappedHash.Write(buf[:n]) + if err == io.EOF { + var p packet.Packet + p, scr.md.SignatureError = scr.packets.Next() + if scr.md.SignatureError != nil { + return + } + + var ok bool + if scr.md.Signature, ok = p.(*packet.Signature); ok { + scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignature(scr.h, scr.md.Signature) + } else if scr.md.SignatureV3, ok = p.(*packet.SignatureV3); ok { + scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignatureV3(scr.h, scr.md.SignatureV3) + } else { + scr.md.SignatureError = errors.StructuralError("LiteralData not followed by Signature") + return + } + + // The SymmetricallyEncrypted packet, if any, might have an + // unsigned hash of its own. In order to check this we need to + // close that Reader. + if scr.md.decrypted != nil { + mdcErr := scr.md.decrypted.Close() + if mdcErr != nil { + err = mdcErr + } + } + } + return +} + +// CheckDetachedSignature takes a signed file and a detached signature and +// returns the signer if the signature is valid. If the signer isn't known, +// ErrUnknownIssuer is returned. +func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, err error) { + var issuerKeyId uint64 + var hashFunc crypto.Hash + var sigType packet.SignatureType + var keys []Key + var p packet.Packet + + packets := packet.NewReader(signature) + for { + p, err = packets.Next() + if err == io.EOF { + return nil, errors.ErrUnknownIssuer + } + if err != nil { + return nil, err + } + + switch sig := p.(type) { + case *packet.Signature: + if sig.IssuerKeyId == nil { + return nil, errors.StructuralError("signature doesn't have an issuer") + } + issuerKeyId = *sig.IssuerKeyId + hashFunc = sig.Hash + sigType = sig.SigType + case *packet.SignatureV3: + issuerKeyId = sig.IssuerKeyId + hashFunc = sig.Hash + sigType = sig.SigType + default: + return nil, errors.StructuralError("non signature packet found") + } + + keys = keyring.KeysByIdUsage(issuerKeyId, packet.KeyFlagSign) + if len(keys) > 0 { + break + } + } + + if len(keys) == 0 { + panic("unreachable") + } + + h, wrappedHash, err := hashForSignature(hashFunc, sigType) + if err != nil { + return nil, err + } + + if _, err := io.Copy(wrappedHash, signed); err != nil && err != io.EOF { + return nil, err + } + + for _, key := range keys { + switch sig := p.(type) { + case *packet.Signature: + err = key.PublicKey.VerifySignature(h, sig) + case *packet.SignatureV3: + err = key.PublicKey.VerifySignatureV3(h, sig) + default: + panic("unreachable") + } + + if err == nil { + return key.Entity, nil + } + } + + return nil, err +} + +// CheckArmoredDetachedSignature performs the same actions as +// CheckDetachedSignature but expects the signature to be armored. +func CheckArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, err error) { + body, err := readArmored(signature, SignatureType) + if err != nil { + return + } + + return CheckDetachedSignature(keyring, signed, body) +} diff --git a/vendor/golang.org/x/crypto/openpgp/s2k/s2k.go b/vendor/golang.org/x/crypto/openpgp/s2k/s2k.go new file mode 100644 index 0000000000..9de04958ea --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/s2k/s2k.go @@ -0,0 +1,279 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package s2k implements the various OpenPGP string-to-key transforms as +// specified in RFC 4800 section 3.7.1. +// +// Deprecated: this package is unmaintained except for security fixes. New +// applications should consider a more focused, modern alternative to OpenPGP +// for their specific task. If you are required to interoperate with OpenPGP +// systems and need a maintained package, consider a community fork. +// See https://golang.org/issue/44226. +package s2k // import "golang.org/x/crypto/openpgp/s2k" + +import ( + "crypto" + "hash" + "io" + "strconv" + + "golang.org/x/crypto/openpgp/errors" +) + +// Config collects configuration parameters for s2k key-stretching +// transformatioms. A nil *Config is valid and results in all default +// values. Currently, Config is used only by the Serialize function in +// this package. +type Config struct { + // Hash is the default hash function to be used. If + // nil, SHA1 is used. + Hash crypto.Hash + // S2KCount is only used for symmetric encryption. It + // determines the strength of the passphrase stretching when + // the said passphrase is hashed to produce a key. S2KCount + // should be between 1024 and 65011712, inclusive. If Config + // is nil or S2KCount is 0, the value 65536 used. Not all + // values in the above range can be represented. S2KCount will + // be rounded up to the next representable value if it cannot + // be encoded exactly. When set, it is strongly encrouraged to + // use a value that is at least 65536. See RFC 4880 Section + // 3.7.1.3. + S2KCount int +} + +func (c *Config) hash() crypto.Hash { + if c == nil || uint(c.Hash) == 0 { + // SHA1 is the historical default in this package. + return crypto.SHA1 + } + + return c.Hash +} + +func (c *Config) encodedCount() uint8 { + if c == nil || c.S2KCount == 0 { + return 96 // The common case. Correspoding to 65536 + } + + i := c.S2KCount + switch { + // Behave like GPG. Should we make 65536 the lowest value used? + case i < 1024: + i = 1024 + case i > 65011712: + i = 65011712 + } + + return encodeCount(i) +} + +// encodeCount converts an iterative "count" in the range 1024 to +// 65011712, inclusive, to an encoded count. The return value is the +// octet that is actually stored in the GPG file. encodeCount panics +// if i is not in the above range (encodedCount above takes care to +// pass i in the correct range). See RFC 4880 Section 3.7.7.1. +func encodeCount(i int) uint8 { + if i < 1024 || i > 65011712 { + panic("count arg i outside the required range") + } + + for encoded := 0; encoded < 256; encoded++ { + count := decodeCount(uint8(encoded)) + if count >= i { + return uint8(encoded) + } + } + + return 255 +} + +// decodeCount returns the s2k mode 3 iterative "count" corresponding to +// the encoded octet c. +func decodeCount(c uint8) int { + return (16 + int(c&15)) << (uint32(c>>4) + 6) +} + +// Simple writes to out the result of computing the Simple S2K function (RFC +// 4880, section 3.7.1.1) using the given hash and input passphrase. +func Simple(out []byte, h hash.Hash, in []byte) { + Salted(out, h, in, nil) +} + +var zero [1]byte + +// Salted writes to out the result of computing the Salted S2K function (RFC +// 4880, section 3.7.1.2) using the given hash, input passphrase and salt. +func Salted(out []byte, h hash.Hash, in []byte, salt []byte) { + done := 0 + var digest []byte + + for i := 0; done < len(out); i++ { + h.Reset() + for j := 0; j < i; j++ { + h.Write(zero[:]) + } + h.Write(salt) + h.Write(in) + digest = h.Sum(digest[:0]) + n := copy(out[done:], digest) + done += n + } +} + +// Iterated writes to out the result of computing the Iterated and Salted S2K +// function (RFC 4880, section 3.7.1.3) using the given hash, input passphrase, +// salt and iteration count. +func Iterated(out []byte, h hash.Hash, in []byte, salt []byte, count int) { + combined := make([]byte, len(in)+len(salt)) + copy(combined, salt) + copy(combined[len(salt):], in) + + if count < len(combined) { + count = len(combined) + } + + done := 0 + var digest []byte + for i := 0; done < len(out); i++ { + h.Reset() + for j := 0; j < i; j++ { + h.Write(zero[:]) + } + written := 0 + for written < count { + if written+len(combined) > count { + todo := count - written + h.Write(combined[:todo]) + written = count + } else { + h.Write(combined) + written += len(combined) + } + } + digest = h.Sum(digest[:0]) + n := copy(out[done:], digest) + done += n + } +} + +// Parse reads a binary specification for a string-to-key transformation from r +// and returns a function which performs that transform. +func Parse(r io.Reader) (f func(out, in []byte), err error) { + var buf [9]byte + + _, err = io.ReadFull(r, buf[:2]) + if err != nil { + return + } + + hash, ok := HashIdToHash(buf[1]) + if !ok { + return nil, errors.UnsupportedError("hash for S2K function: " + strconv.Itoa(int(buf[1]))) + } + if !hash.Available() { + return nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hash))) + } + h := hash.New() + + switch buf[0] { + case 0: + f := func(out, in []byte) { + Simple(out, h, in) + } + return f, nil + case 1: + _, err = io.ReadFull(r, buf[:8]) + if err != nil { + return + } + f := func(out, in []byte) { + Salted(out, h, in, buf[:8]) + } + return f, nil + case 3: + _, err = io.ReadFull(r, buf[:9]) + if err != nil { + return + } + count := decodeCount(buf[8]) + f := func(out, in []byte) { + Iterated(out, h, in, buf[:8], count) + } + return f, nil + } + + return nil, errors.UnsupportedError("S2K function") +} + +// Serialize salts and stretches the given passphrase and writes the +// resulting key into key. It also serializes an S2K descriptor to +// w. The key stretching can be configured with c, which may be +// nil. In that case, sensible defaults will be used. +func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte, c *Config) error { + var buf [11]byte + buf[0] = 3 /* iterated and salted */ + buf[1], _ = HashToHashId(c.hash()) + salt := buf[2:10] + if _, err := io.ReadFull(rand, salt); err != nil { + return err + } + encodedCount := c.encodedCount() + count := decodeCount(encodedCount) + buf[10] = encodedCount + if _, err := w.Write(buf[:]); err != nil { + return err + } + + Iterated(key, c.hash().New(), passphrase, salt, count) + return nil +} + +// hashToHashIdMapping contains pairs relating OpenPGP's hash identifier with +// Go's crypto.Hash type. See RFC 4880, section 9.4. +var hashToHashIdMapping = []struct { + id byte + hash crypto.Hash + name string +}{ + {1, crypto.MD5, "MD5"}, + {2, crypto.SHA1, "SHA1"}, + {3, crypto.RIPEMD160, "RIPEMD160"}, + {8, crypto.SHA256, "SHA256"}, + {9, crypto.SHA384, "SHA384"}, + {10, crypto.SHA512, "SHA512"}, + {11, crypto.SHA224, "SHA224"}, +} + +// HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP +// hash id. +func HashIdToHash(id byte) (h crypto.Hash, ok bool) { + for _, m := range hashToHashIdMapping { + if m.id == id { + return m.hash, true + } + } + return 0, false +} + +// HashIdToString returns the name of the hash function corresponding to the +// given OpenPGP hash id. +func HashIdToString(id byte) (name string, ok bool) { + for _, m := range hashToHashIdMapping { + if m.id == id { + return m.name, true + } + } + + return "", false +} + +// HashIdToHash returns an OpenPGP hash id which corresponds the given Hash. +func HashToHashId(h crypto.Hash) (id byte, ok bool) { + for _, m := range hashToHashIdMapping { + if m.hash == h { + return m.id, true + } + } + return 0, false +} diff --git a/vendor/golang.org/x/crypto/openpgp/write.go b/vendor/golang.org/x/crypto/openpgp/write.go new file mode 100644 index 0000000000..4ee71784eb --- /dev/null +++ b/vendor/golang.org/x/crypto/openpgp/write.go @@ -0,0 +1,418 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package openpgp + +import ( + "crypto" + "hash" + "io" + "strconv" + "time" + + "golang.org/x/crypto/openpgp/armor" + "golang.org/x/crypto/openpgp/errors" + "golang.org/x/crypto/openpgp/packet" + "golang.org/x/crypto/openpgp/s2k" +) + +// DetachSign signs message with the private key from signer (which must +// already have been decrypted) and writes the signature to w. +// If config is nil, sensible defaults will be used. +func DetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { + return detachSign(w, signer, message, packet.SigTypeBinary, config) +} + +// ArmoredDetachSign signs message with the private key from signer (which +// must already have been decrypted) and writes an armored signature to w. +// If config is nil, sensible defaults will be used. +func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) (err error) { + return armoredDetachSign(w, signer, message, packet.SigTypeBinary, config) +} + +// DetachSignText signs message (after canonicalising the line endings) with +// the private key from signer (which must already have been decrypted) and +// writes the signature to w. +// If config is nil, sensible defaults will be used. +func DetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { + return detachSign(w, signer, message, packet.SigTypeText, config) +} + +// ArmoredDetachSignText signs message (after canonicalising the line endings) +// with the private key from signer (which must already have been decrypted) +// and writes an armored signature to w. +// If config is nil, sensible defaults will be used. +func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { + return armoredDetachSign(w, signer, message, packet.SigTypeText, config) +} + +func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { + out, err := armor.Encode(w, SignatureType, nil) + if err != nil { + return + } + err = detachSign(out, signer, message, sigType, config) + if err != nil { + return + } + return out.Close() +} + +func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { + if signer.PrivateKey == nil { + return errors.InvalidArgumentError("signing key doesn't have a private key") + } + if signer.PrivateKey.Encrypted { + return errors.InvalidArgumentError("signing key is encrypted") + } + + sig := new(packet.Signature) + sig.SigType = sigType + sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo + sig.Hash = config.Hash() + sig.CreationTime = config.Now() + sig.IssuerKeyId = &signer.PrivateKey.KeyId + + h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) + if err != nil { + return + } + io.Copy(wrappedHash, message) + + err = sig.Sign(h, signer.PrivateKey, config) + if err != nil { + return + } + + return sig.Serialize(w) +} + +// FileHints contains metadata about encrypted files. This metadata is, itself, +// encrypted. +type FileHints struct { + // IsBinary can be set to hint that the contents are binary data. + IsBinary bool + // FileName hints at the name of the file that should be written. It's + // truncated to 255 bytes if longer. It may be empty to suggest that the + // file should not be written to disk. It may be equal to "_CONSOLE" to + // suggest the data should not be written to disk. + FileName string + // ModTime contains the modification time of the file, or the zero time if not applicable. + ModTime time.Time +} + +// SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase. +// The resulting WriteCloser must be closed after the contents of the file have +// been written. +// If config is nil, sensible defaults will be used. +func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { + if hints == nil { + hints = &FileHints{} + } + + key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, passphrase, config) + if err != nil { + return + } + w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), key, config) + if err != nil { + return + } + + literaldata := w + if algo := config.Compression(); algo != packet.CompressionNone { + var compConfig *packet.CompressionConfig + if config != nil { + compConfig = config.CompressionConfig + } + literaldata, err = packet.SerializeCompressed(w, algo, compConfig) + if err != nil { + return + } + } + + var epochSeconds uint32 + if !hints.ModTime.IsZero() { + epochSeconds = uint32(hints.ModTime.Unix()) + } + return packet.SerializeLiteral(literaldata, hints.IsBinary, hints.FileName, epochSeconds) +} + +// intersectPreferences mutates and returns a prefix of a that contains only +// the values in the intersection of a and b. The order of a is preserved. +func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) { + var j int + for _, v := range a { + for _, v2 := range b { + if v == v2 { + a[j] = v + j++ + break + } + } + } + + return a[:j] +} + +func hashToHashId(h crypto.Hash) uint8 { + v, ok := s2k.HashToHashId(h) + if !ok { + panic("tried to convert unknown hash") + } + return v +} + +// writeAndSign writes the data as a payload package and, optionally, signs +// it. hints contains optional information, that is also encrypted, +// that aids the recipients in processing the message. The resulting +// WriteCloser must be closed after the contents of the file have been +// written. If config is nil, sensible defaults will be used. +func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { + var signer *packet.PrivateKey + if signed != nil { + signKey, ok := signed.signingKey(config.Now()) + if !ok { + return nil, errors.InvalidArgumentError("no valid signing keys") + } + signer = signKey.PrivateKey + if signer == nil { + return nil, errors.InvalidArgumentError("no private key in signing key") + } + if signer.Encrypted { + return nil, errors.InvalidArgumentError("signing key must be decrypted") + } + } + + var hash crypto.Hash + for _, hashId := range candidateHashes { + if h, ok := s2k.HashIdToHash(hashId); ok && h.Available() { + hash = h + break + } + } + + // If the hash specified by config is a candidate, we'll use that. + if configuredHash := config.Hash(); configuredHash.Available() { + for _, hashId := range candidateHashes { + if h, ok := s2k.HashIdToHash(hashId); ok && h == configuredHash { + hash = h + break + } + } + } + + if hash == 0 { + hashId := candidateHashes[0] + name, ok := s2k.HashIdToString(hashId) + if !ok { + name = "#" + strconv.Itoa(int(hashId)) + } + return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)") + } + + if signer != nil { + ops := &packet.OnePassSignature{ + SigType: packet.SigTypeBinary, + Hash: hash, + PubKeyAlgo: signer.PubKeyAlgo, + KeyId: signer.KeyId, + IsLast: true, + } + if err := ops.Serialize(payload); err != nil { + return nil, err + } + } + + if hints == nil { + hints = &FileHints{} + } + + w := payload + if signer != nil { + // If we need to write a signature packet after the literal + // data then we need to stop literalData from closing + // encryptedData. + w = noOpCloser{w} + + } + var epochSeconds uint32 + if !hints.ModTime.IsZero() { + epochSeconds = uint32(hints.ModTime.Unix()) + } + literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds) + if err != nil { + return nil, err + } + + if signer != nil { + return signatureWriter{payload, literalData, hash, hash.New(), signer, config}, nil + } + return literalData, nil +} + +// Encrypt encrypts a message to a number of recipients and, optionally, signs +// it. hints contains optional information, that is also encrypted, that aids +// the recipients in processing the message. The resulting WriteCloser must +// be closed after the contents of the file have been written. +// If config is nil, sensible defaults will be used. +func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { + if len(to) == 0 { + return nil, errors.InvalidArgumentError("no encryption recipient provided") + } + + // These are the possible ciphers that we'll use for the message. + candidateCiphers := []uint8{ + uint8(packet.CipherAES128), + uint8(packet.CipherAES256), + uint8(packet.CipherCAST5), + } + // These are the possible hash functions that we'll use for the signature. + candidateHashes := []uint8{ + hashToHashId(crypto.SHA256), + hashToHashId(crypto.SHA384), + hashToHashId(crypto.SHA512), + hashToHashId(crypto.SHA1), + hashToHashId(crypto.RIPEMD160), + } + // In the event that a recipient doesn't specify any supported ciphers + // or hash functions, these are the ones that we assume that every + // implementation supports. + defaultCiphers := candidateCiphers[len(candidateCiphers)-1:] + defaultHashes := candidateHashes[len(candidateHashes)-1:] + + encryptKeys := make([]Key, len(to)) + for i := range to { + var ok bool + encryptKeys[i], ok = to[i].encryptionKey(config.Now()) + if !ok { + return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys") + } + + sig := to[i].primaryIdentity().SelfSignature + + preferredSymmetric := sig.PreferredSymmetric + if len(preferredSymmetric) == 0 { + preferredSymmetric = defaultCiphers + } + preferredHashes := sig.PreferredHash + if len(preferredHashes) == 0 { + preferredHashes = defaultHashes + } + candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric) + candidateHashes = intersectPreferences(candidateHashes, preferredHashes) + } + + if len(candidateCiphers) == 0 || len(candidateHashes) == 0 { + return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms") + } + + cipher := packet.CipherFunction(candidateCiphers[0]) + // If the cipher specified by config is a candidate, we'll use that. + configuredCipher := config.Cipher() + for _, c := range candidateCiphers { + cipherFunc := packet.CipherFunction(c) + if cipherFunc == configuredCipher { + cipher = cipherFunc + break + } + } + + symKey := make([]byte, cipher.KeySize()) + if _, err := io.ReadFull(config.Random(), symKey); err != nil { + return nil, err + } + + for _, key := range encryptKeys { + if err := packet.SerializeEncryptedKey(ciphertext, key.PublicKey, cipher, symKey, config); err != nil { + return nil, err + } + } + + payload, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey, config) + if err != nil { + return + } + + return writeAndSign(payload, candidateHashes, signed, hints, config) +} + +// Sign signs a message. The resulting WriteCloser must be closed after the +// contents of the file have been written. hints contains optional information +// that aids the recipients in processing the message. +// If config is nil, sensible defaults will be used. +func Sign(output io.Writer, signed *Entity, hints *FileHints, config *packet.Config) (input io.WriteCloser, err error) { + if signed == nil { + return nil, errors.InvalidArgumentError("no signer provided") + } + + // These are the possible hash functions that we'll use for the signature. + candidateHashes := []uint8{ + hashToHashId(crypto.SHA256), + hashToHashId(crypto.SHA384), + hashToHashId(crypto.SHA512), + hashToHashId(crypto.SHA1), + hashToHashId(crypto.RIPEMD160), + } + defaultHashes := candidateHashes[len(candidateHashes)-1:] + preferredHashes := signed.primaryIdentity().SelfSignature.PreferredHash + if len(preferredHashes) == 0 { + preferredHashes = defaultHashes + } + candidateHashes = intersectPreferences(candidateHashes, preferredHashes) + return writeAndSign(noOpCloser{output}, candidateHashes, signed, hints, config) +} + +// signatureWriter hashes the contents of a message while passing it along to +// literalData. When closed, it closes literalData, writes a signature packet +// to encryptedData and then also closes encryptedData. +type signatureWriter struct { + encryptedData io.WriteCloser + literalData io.WriteCloser + hashType crypto.Hash + h hash.Hash + signer *packet.PrivateKey + config *packet.Config +} + +func (s signatureWriter) Write(data []byte) (int, error) { + s.h.Write(data) + return s.literalData.Write(data) +} + +func (s signatureWriter) Close() error { + sig := &packet.Signature{ + SigType: packet.SigTypeBinary, + PubKeyAlgo: s.signer.PubKeyAlgo, + Hash: s.hashType, + CreationTime: s.config.Now(), + IssuerKeyId: &s.signer.KeyId, + } + + if err := sig.Sign(s.h, s.signer, s.config); err != nil { + return err + } + if err := s.literalData.Close(); err != nil { + return err + } + if err := sig.Serialize(s.encryptedData); err != nil { + return err + } + return s.encryptedData.Close() +} + +// noOpCloser is like an ioutil.NopCloser, but for an io.Writer. +// TODO: we have two of these in OpenPGP packages alone. This probably needs +// to be promoted somewhere more common. +type noOpCloser struct { + w io.Writer +} + +func (c noOpCloser) Write(data []byte) (n int, err error) { + return c.w.Write(data) +} + +func (c noOpCloser) Close() error { + return nil +} diff --git a/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/checked.pb.go b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/checked.pb.go new file mode 100644 index 0000000000..bad8af1497 --- /dev/null +++ b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/checked.pb.go @@ -0,0 +1,1657 @@ +// Copyright 2021 Google LLC +// +// 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.12.2 +// source: google/api/expr/v1alpha1/checked.proto + +package expr + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + structpb "google.golang.org/protobuf/types/known/structpb" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// CEL primitive types. +type Type_PrimitiveType int32 + +const ( + // Unspecified type. + Type_PRIMITIVE_TYPE_UNSPECIFIED Type_PrimitiveType = 0 + // Boolean type. + Type_BOOL Type_PrimitiveType = 1 + // Int64 type. + // + // Proto-based integer values are widened to int64. + Type_INT64 Type_PrimitiveType = 2 + // Uint64 type. + // + // Proto-based unsigned integer values are widened to uint64. + Type_UINT64 Type_PrimitiveType = 3 + // Double type. + // + // Proto-based float values are widened to double values. + Type_DOUBLE Type_PrimitiveType = 4 + // String type. + Type_STRING Type_PrimitiveType = 5 + // Bytes type. + Type_BYTES Type_PrimitiveType = 6 +) + +// Enum value maps for Type_PrimitiveType. +var ( + Type_PrimitiveType_name = map[int32]string{ + 0: "PRIMITIVE_TYPE_UNSPECIFIED", + 1: "BOOL", + 2: "INT64", + 3: "UINT64", + 4: "DOUBLE", + 5: "STRING", + 6: "BYTES", + } + Type_PrimitiveType_value = map[string]int32{ + "PRIMITIVE_TYPE_UNSPECIFIED": 0, + "BOOL": 1, + "INT64": 2, + "UINT64": 3, + "DOUBLE": 4, + "STRING": 5, + "BYTES": 6, + } +) + +func (x Type_PrimitiveType) Enum() *Type_PrimitiveType { + p := new(Type_PrimitiveType) + *p = x + return p +} + +func (x Type_PrimitiveType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Type_PrimitiveType) Descriptor() protoreflect.EnumDescriptor { + return file_google_api_expr_v1alpha1_checked_proto_enumTypes[0].Descriptor() +} + +func (Type_PrimitiveType) Type() protoreflect.EnumType { + return &file_google_api_expr_v1alpha1_checked_proto_enumTypes[0] +} + +func (x Type_PrimitiveType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Type_PrimitiveType.Descriptor instead. +func (Type_PrimitiveType) EnumDescriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_checked_proto_rawDescGZIP(), []int{1, 0} +} + +// Well-known protobuf types treated with first-class support in CEL. +type Type_WellKnownType int32 + +const ( + // Unspecified type. + Type_WELL_KNOWN_TYPE_UNSPECIFIED Type_WellKnownType = 0 + // Well-known protobuf.Any type. + // + // Any types are a polymorphic message type. During type-checking they are + // treated like `DYN` types, but at runtime they are resolved to a specific + // message type specified at evaluation time. + Type_ANY Type_WellKnownType = 1 + // Well-known protobuf.Timestamp type, internally referenced as `timestamp`. + Type_TIMESTAMP Type_WellKnownType = 2 + // Well-known protobuf.Duration type, internally referenced as `duration`. + Type_DURATION Type_WellKnownType = 3 +) + +// Enum value maps for Type_WellKnownType. +var ( + Type_WellKnownType_name = map[int32]string{ + 0: "WELL_KNOWN_TYPE_UNSPECIFIED", + 1: "ANY", + 2: "TIMESTAMP", + 3: "DURATION", + } + Type_WellKnownType_value = map[string]int32{ + "WELL_KNOWN_TYPE_UNSPECIFIED": 0, + "ANY": 1, + "TIMESTAMP": 2, + "DURATION": 3, + } +) + +func (x Type_WellKnownType) Enum() *Type_WellKnownType { + p := new(Type_WellKnownType) + *p = x + return p +} + +func (x Type_WellKnownType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Type_WellKnownType) Descriptor() protoreflect.EnumDescriptor { + return file_google_api_expr_v1alpha1_checked_proto_enumTypes[1].Descriptor() +} + +func (Type_WellKnownType) Type() protoreflect.EnumType { + return &file_google_api_expr_v1alpha1_checked_proto_enumTypes[1] +} + +func (x Type_WellKnownType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Type_WellKnownType.Descriptor instead. +func (Type_WellKnownType) EnumDescriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_checked_proto_rawDescGZIP(), []int{1, 1} +} + +// A CEL expression which has been successfully type checked. +type CheckedExpr struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // A map from expression ids to resolved references. + // + // The following entries are in this table: + // + // - An Ident or Select expression is represented here if it resolves to a + // declaration. For instance, if `a.b.c` is represented by + // `select(select(id(a), b), c)`, and `a.b` resolves to a declaration, + // while `c` is a field selection, then the reference is attached to the + // nested select expression (but not to the id or or the outer select). + // In turn, if `a` resolves to a declaration and `b.c` are field selections, + // the reference is attached to the ident expression. + // - Every Call expression has an entry here, identifying the function being + // called. + // - Every CreateStruct expression for a message has an entry, identifying + // the message. + ReferenceMap map[int64]*Reference `protobuf:"bytes,2,rep,name=reference_map,json=referenceMap,proto3" json:"reference_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // A map from expression ids to types. + // + // Every expression node which has a type different than DYN has a mapping + // here. If an expression has type DYN, it is omitted from this map to save + // space. + TypeMap map[int64]*Type `protobuf:"bytes,3,rep,name=type_map,json=typeMap,proto3" json:"type_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // The source info derived from input that generated the parsed `expr` and + // any optimizations made during the type-checking pass. + SourceInfo *SourceInfo `protobuf:"bytes,5,opt,name=source_info,json=sourceInfo,proto3" json:"source_info,omitempty"` + // The expr version indicates the major / minor version number of the `expr` + // representation. + // + // The most common reason for a version change will be to indicate to the CEL + // runtimes that transformations have been performed on the expr during static + // analysis. In some cases, this will save the runtime the work of applying + // the same or similar transformations prior to evaluation. + ExprVersion string `protobuf:"bytes,6,opt,name=expr_version,json=exprVersion,proto3" json:"expr_version,omitempty"` + // The checked expression. Semantically equivalent to the parsed `expr`, but + // may have structural differences. + Expr *Expr `protobuf:"bytes,4,opt,name=expr,proto3" json:"expr,omitempty"` +} + +func (x *CheckedExpr) Reset() { + *x = CheckedExpr{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CheckedExpr) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CheckedExpr) ProtoMessage() {} + +func (x *CheckedExpr) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CheckedExpr.ProtoReflect.Descriptor instead. +func (*CheckedExpr) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_checked_proto_rawDescGZIP(), []int{0} +} + +func (x *CheckedExpr) GetReferenceMap() map[int64]*Reference { + if x != nil { + return x.ReferenceMap + } + return nil +} + +func (x *CheckedExpr) GetTypeMap() map[int64]*Type { + if x != nil { + return x.TypeMap + } + return nil +} + +func (x *CheckedExpr) GetSourceInfo() *SourceInfo { + if x != nil { + return x.SourceInfo + } + return nil +} + +func (x *CheckedExpr) GetExprVersion() string { + if x != nil { + return x.ExprVersion + } + return "" +} + +func (x *CheckedExpr) GetExpr() *Expr { + if x != nil { + return x.Expr + } + return nil +} + +// Represents a CEL type. +type Type struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The kind of type. + // + // Types that are assignable to TypeKind: + // *Type_Dyn + // *Type_Null + // *Type_Primitive + // *Type_Wrapper + // *Type_WellKnown + // *Type_ListType_ + // *Type_MapType_ + // *Type_Function + // *Type_MessageType + // *Type_TypeParam + // *Type_Type + // *Type_Error + // *Type_AbstractType_ + TypeKind isType_TypeKind `protobuf_oneof:"type_kind"` +} + +func (x *Type) Reset() { + *x = Type{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Type) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Type) ProtoMessage() {} + +func (x *Type) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Type.ProtoReflect.Descriptor instead. +func (*Type) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_checked_proto_rawDescGZIP(), []int{1} +} + +func (m *Type) GetTypeKind() isType_TypeKind { + if m != nil { + return m.TypeKind + } + return nil +} + +func (x *Type) GetDyn() *emptypb.Empty { + if x, ok := x.GetTypeKind().(*Type_Dyn); ok { + return x.Dyn + } + return nil +} + +func (x *Type) GetNull() structpb.NullValue { + if x, ok := x.GetTypeKind().(*Type_Null); ok { + return x.Null + } + return structpb.NullValue_NULL_VALUE +} + +func (x *Type) GetPrimitive() Type_PrimitiveType { + if x, ok := x.GetTypeKind().(*Type_Primitive); ok { + return x.Primitive + } + return Type_PRIMITIVE_TYPE_UNSPECIFIED +} + +func (x *Type) GetWrapper() Type_PrimitiveType { + if x, ok := x.GetTypeKind().(*Type_Wrapper); ok { + return x.Wrapper + } + return Type_PRIMITIVE_TYPE_UNSPECIFIED +} + +func (x *Type) GetWellKnown() Type_WellKnownType { + if x, ok := x.GetTypeKind().(*Type_WellKnown); ok { + return x.WellKnown + } + return Type_WELL_KNOWN_TYPE_UNSPECIFIED +} + +func (x *Type) GetListType() *Type_ListType { + if x, ok := x.GetTypeKind().(*Type_ListType_); ok { + return x.ListType + } + return nil +} + +func (x *Type) GetMapType() *Type_MapType { + if x, ok := x.GetTypeKind().(*Type_MapType_); ok { + return x.MapType + } + return nil +} + +func (x *Type) GetFunction() *Type_FunctionType { + if x, ok := x.GetTypeKind().(*Type_Function); ok { + return x.Function + } + return nil +} + +func (x *Type) GetMessageType() string { + if x, ok := x.GetTypeKind().(*Type_MessageType); ok { + return x.MessageType + } + return "" +} + +func (x *Type) GetTypeParam() string { + if x, ok := x.GetTypeKind().(*Type_TypeParam); ok { + return x.TypeParam + } + return "" +} + +func (x *Type) GetType() *Type { + if x, ok := x.GetTypeKind().(*Type_Type); ok { + return x.Type + } + return nil +} + +func (x *Type) GetError() *emptypb.Empty { + if x, ok := x.GetTypeKind().(*Type_Error); ok { + return x.Error + } + return nil +} + +func (x *Type) GetAbstractType() *Type_AbstractType { + if x, ok := x.GetTypeKind().(*Type_AbstractType_); ok { + return x.AbstractType + } + return nil +} + +type isType_TypeKind interface { + isType_TypeKind() +} + +type Type_Dyn struct { + // Dynamic type. + Dyn *emptypb.Empty `protobuf:"bytes,1,opt,name=dyn,proto3,oneof"` +} + +type Type_Null struct { + // Null value. + Null structpb.NullValue `protobuf:"varint,2,opt,name=null,proto3,enum=google.protobuf.NullValue,oneof"` +} + +type Type_Primitive struct { + // Primitive types: `true`, `1u`, `-2.0`, `'string'`, `b'bytes'`. + Primitive Type_PrimitiveType `protobuf:"varint,3,opt,name=primitive,proto3,enum=google.api.expr.v1alpha1.Type_PrimitiveType,oneof"` +} + +type Type_Wrapper struct { + // Wrapper of a primitive type, e.g. `google.protobuf.Int64Value`. + Wrapper Type_PrimitiveType `protobuf:"varint,4,opt,name=wrapper,proto3,enum=google.api.expr.v1alpha1.Type_PrimitiveType,oneof"` +} + +type Type_WellKnown struct { + // Well-known protobuf type such as `google.protobuf.Timestamp`. + WellKnown Type_WellKnownType `protobuf:"varint,5,opt,name=well_known,json=wellKnown,proto3,enum=google.api.expr.v1alpha1.Type_WellKnownType,oneof"` +} + +type Type_ListType_ struct { + // Parameterized list with elements of `list_type`, e.g. `list`. + ListType *Type_ListType `protobuf:"bytes,6,opt,name=list_type,json=listType,proto3,oneof"` +} + +type Type_MapType_ struct { + // Parameterized map with typed keys and values. + MapType *Type_MapType `protobuf:"bytes,7,opt,name=map_type,json=mapType,proto3,oneof"` +} + +type Type_Function struct { + // Function type. + Function *Type_FunctionType `protobuf:"bytes,8,opt,name=function,proto3,oneof"` +} + +type Type_MessageType struct { + // Protocol buffer message type. + // + // The `message_type` string specifies the qualified message type name. For + // example, `google.plus.Profile`. + MessageType string `protobuf:"bytes,9,opt,name=message_type,json=messageType,proto3,oneof"` +} + +type Type_TypeParam struct { + // Type param type. + // + // The `type_param` string specifies the type parameter name, e.g. `list` + // would be a `list_type` whose element type was a `type_param` type + // named `E`. + TypeParam string `protobuf:"bytes,10,opt,name=type_param,json=typeParam,proto3,oneof"` +} + +type Type_Type struct { + // Type type. + // + // The `type` value specifies the target type. e.g. int is type with a + // target type of `Primitive.INT`. + Type *Type `protobuf:"bytes,11,opt,name=type,proto3,oneof"` +} + +type Type_Error struct { + // Error type. + // + // During type-checking if an expression is an error, its type is propagated + // as the `ERROR` type. This permits the type-checker to discover other + // errors present in the expression. + Error *emptypb.Empty `protobuf:"bytes,12,opt,name=error,proto3,oneof"` +} + +type Type_AbstractType_ struct { + // Abstract, application defined type. + AbstractType *Type_AbstractType `protobuf:"bytes,14,opt,name=abstract_type,json=abstractType,proto3,oneof"` +} + +func (*Type_Dyn) isType_TypeKind() {} + +func (*Type_Null) isType_TypeKind() {} + +func (*Type_Primitive) isType_TypeKind() {} + +func (*Type_Wrapper) isType_TypeKind() {} + +func (*Type_WellKnown) isType_TypeKind() {} + +func (*Type_ListType_) isType_TypeKind() {} + +func (*Type_MapType_) isType_TypeKind() {} + +func (*Type_Function) isType_TypeKind() {} + +func (*Type_MessageType) isType_TypeKind() {} + +func (*Type_TypeParam) isType_TypeKind() {} + +func (*Type_Type) isType_TypeKind() {} + +func (*Type_Error) isType_TypeKind() {} + +func (*Type_AbstractType_) isType_TypeKind() {} + +// Represents a declaration of a named value or function. +// +// A declaration is part of the contract between the expression, the agent +// evaluating that expression, and the caller requesting evaluation. +type Decl struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The fully qualified name of the declaration. + // + // Declarations are organized in containers and this represents the full path + // to the declaration in its container, as in `google.api.expr.Decl`. + // + // Declarations used as [FunctionDecl.Overload][google.api.expr.v1alpha1.Decl.FunctionDecl.Overload] parameters may or may not + // have a name depending on whether the overload is function declaration or a + // function definition containing a result [Expr][google.api.expr.v1alpha1.Expr]. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Required. The declaration kind. + // + // Types that are assignable to DeclKind: + // *Decl_Ident + // *Decl_Function + DeclKind isDecl_DeclKind `protobuf_oneof:"decl_kind"` +} + +func (x *Decl) Reset() { + *x = Decl{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Decl) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Decl) ProtoMessage() {} + +func (x *Decl) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Decl.ProtoReflect.Descriptor instead. +func (*Decl) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_checked_proto_rawDescGZIP(), []int{2} +} + +func (x *Decl) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (m *Decl) GetDeclKind() isDecl_DeclKind { + if m != nil { + return m.DeclKind + } + return nil +} + +func (x *Decl) GetIdent() *Decl_IdentDecl { + if x, ok := x.GetDeclKind().(*Decl_Ident); ok { + return x.Ident + } + return nil +} + +func (x *Decl) GetFunction() *Decl_FunctionDecl { + if x, ok := x.GetDeclKind().(*Decl_Function); ok { + return x.Function + } + return nil +} + +type isDecl_DeclKind interface { + isDecl_DeclKind() +} + +type Decl_Ident struct { + // Identifier declaration. + Ident *Decl_IdentDecl `protobuf:"bytes,2,opt,name=ident,proto3,oneof"` +} + +type Decl_Function struct { + // Function declaration. + Function *Decl_FunctionDecl `protobuf:"bytes,3,opt,name=function,proto3,oneof"` +} + +func (*Decl_Ident) isDecl_DeclKind() {} + +func (*Decl_Function) isDecl_DeclKind() {} + +// Describes a resolved reference to a declaration. +type Reference struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The fully qualified name of the declaration. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // For references to functions, this is a list of `Overload.overload_id` + // values which match according to typing rules. + // + // If the list has more than one element, overload resolution among the + // presented candidates must happen at runtime because of dynamic types. The + // type checker attempts to narrow down this list as much as possible. + // + // Empty if this is not a reference to a [Decl.FunctionDecl][google.api.expr.v1alpha1.Decl.FunctionDecl]. + OverloadId []string `protobuf:"bytes,3,rep,name=overload_id,json=overloadId,proto3" json:"overload_id,omitempty"` + // For references to constants, this may contain the value of the + // constant if known at compile time. + Value *Constant `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *Reference) Reset() { + *x = Reference{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Reference) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Reference) ProtoMessage() {} + +func (x *Reference) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Reference.ProtoReflect.Descriptor instead. +func (*Reference) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_checked_proto_rawDescGZIP(), []int{3} +} + +func (x *Reference) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Reference) GetOverloadId() []string { + if x != nil { + return x.OverloadId + } + return nil +} + +func (x *Reference) GetValue() *Constant { + if x != nil { + return x.Value + } + return nil +} + +// List type with typed elements, e.g. `list`. +type Type_ListType struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The element type. + ElemType *Type `protobuf:"bytes,1,opt,name=elem_type,json=elemType,proto3" json:"elem_type,omitempty"` +} + +func (x *Type_ListType) Reset() { + *x = Type_ListType{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Type_ListType) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Type_ListType) ProtoMessage() {} + +func (x *Type_ListType) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Type_ListType.ProtoReflect.Descriptor instead. +func (*Type_ListType) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_checked_proto_rawDescGZIP(), []int{1, 0} +} + +func (x *Type_ListType) GetElemType() *Type { + if x != nil { + return x.ElemType + } + return nil +} + +// Map type with parameterized key and value types, e.g. `map`. +type Type_MapType struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The type of the key. + KeyType *Type `protobuf:"bytes,1,opt,name=key_type,json=keyType,proto3" json:"key_type,omitempty"` + // The type of the value. + ValueType *Type `protobuf:"bytes,2,opt,name=value_type,json=valueType,proto3" json:"value_type,omitempty"` +} + +func (x *Type_MapType) Reset() { + *x = Type_MapType{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Type_MapType) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Type_MapType) ProtoMessage() {} + +func (x *Type_MapType) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Type_MapType.ProtoReflect.Descriptor instead. +func (*Type_MapType) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_checked_proto_rawDescGZIP(), []int{1, 1} +} + +func (x *Type_MapType) GetKeyType() *Type { + if x != nil { + return x.KeyType + } + return nil +} + +func (x *Type_MapType) GetValueType() *Type { + if x != nil { + return x.ValueType + } + return nil +} + +// Function type with result and arg types. +type Type_FunctionType struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Result type of the function. + ResultType *Type `protobuf:"bytes,1,opt,name=result_type,json=resultType,proto3" json:"result_type,omitempty"` + // Argument types of the function. + ArgTypes []*Type `protobuf:"bytes,2,rep,name=arg_types,json=argTypes,proto3" json:"arg_types,omitempty"` +} + +func (x *Type_FunctionType) Reset() { + *x = Type_FunctionType{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Type_FunctionType) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Type_FunctionType) ProtoMessage() {} + +func (x *Type_FunctionType) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Type_FunctionType.ProtoReflect.Descriptor instead. +func (*Type_FunctionType) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_checked_proto_rawDescGZIP(), []int{1, 2} +} + +func (x *Type_FunctionType) GetResultType() *Type { + if x != nil { + return x.ResultType + } + return nil +} + +func (x *Type_FunctionType) GetArgTypes() []*Type { + if x != nil { + return x.ArgTypes + } + return nil +} + +// Application defined abstract type. +type Type_AbstractType struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The fully qualified name of this abstract type. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Parameter types for this abstract type. + ParameterTypes []*Type `protobuf:"bytes,2,rep,name=parameter_types,json=parameterTypes,proto3" json:"parameter_types,omitempty"` +} + +func (x *Type_AbstractType) Reset() { + *x = Type_AbstractType{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Type_AbstractType) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Type_AbstractType) ProtoMessage() {} + +func (x *Type_AbstractType) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Type_AbstractType.ProtoReflect.Descriptor instead. +func (*Type_AbstractType) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_checked_proto_rawDescGZIP(), []int{1, 3} +} + +func (x *Type_AbstractType) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Type_AbstractType) GetParameterTypes() []*Type { + if x != nil { + return x.ParameterTypes + } + return nil +} + +// Identifier declaration which specifies its type and optional `Expr` value. +// +// An identifier without a value is a declaration that must be provided at +// evaluation time. An identifier with a value should resolve to a constant, +// but may be used in conjunction with other identifiers bound at evaluation +// time. +type Decl_IdentDecl struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Required. The type of the identifier. + Type *Type `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + // The constant value of the identifier. If not specified, the identifier + // must be supplied at evaluation time. + Value *Constant `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + // Documentation string for the identifier. + Doc string `protobuf:"bytes,3,opt,name=doc,proto3" json:"doc,omitempty"` +} + +func (x *Decl_IdentDecl) Reset() { + *x = Decl_IdentDecl{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Decl_IdentDecl) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Decl_IdentDecl) ProtoMessage() {} + +func (x *Decl_IdentDecl) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Decl_IdentDecl.ProtoReflect.Descriptor instead. +func (*Decl_IdentDecl) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_checked_proto_rawDescGZIP(), []int{2, 0} +} + +func (x *Decl_IdentDecl) GetType() *Type { + if x != nil { + return x.Type + } + return nil +} + +func (x *Decl_IdentDecl) GetValue() *Constant { + if x != nil { + return x.Value + } + return nil +} + +func (x *Decl_IdentDecl) GetDoc() string { + if x != nil { + return x.Doc + } + return "" +} + +// Function declaration specifies one or more overloads which indicate the +// function's parameter types and return type. +// +// Functions have no observable side-effects (there may be side-effects like +// logging which are not observable from CEL). +type Decl_FunctionDecl struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Required. List of function overloads, must contain at least one overload. + Overloads []*Decl_FunctionDecl_Overload `protobuf:"bytes,1,rep,name=overloads,proto3" json:"overloads,omitempty"` +} + +func (x *Decl_FunctionDecl) Reset() { + *x = Decl_FunctionDecl{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Decl_FunctionDecl) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Decl_FunctionDecl) ProtoMessage() {} + +func (x *Decl_FunctionDecl) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Decl_FunctionDecl.ProtoReflect.Descriptor instead. +func (*Decl_FunctionDecl) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_checked_proto_rawDescGZIP(), []int{2, 1} +} + +func (x *Decl_FunctionDecl) GetOverloads() []*Decl_FunctionDecl_Overload { + if x != nil { + return x.Overloads + } + return nil +} + +// An overload indicates a function's parameter types and return type, and +// may optionally include a function body described in terms of [Expr][google.api.expr.v1alpha1.Expr] +// values. +// +// Functions overloads are declared in either a function or method +// call-style. For methods, the `params[0]` is the expected type of the +// target receiver. +// +// Overloads must have non-overlapping argument types after erasure of all +// parameterized type variables (similar as type erasure in Java). +type Decl_FunctionDecl_Overload struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Required. Globally unique overload name of the function which reflects + // the function name and argument types. + // + // This will be used by a [Reference][google.api.expr.v1alpha1.Reference] to indicate the `overload_id` that + // was resolved for the function `name`. + OverloadId string `protobuf:"bytes,1,opt,name=overload_id,json=overloadId,proto3" json:"overload_id,omitempty"` + // List of function parameter [Type][google.api.expr.v1alpha1.Type] values. + // + // Param types are disjoint after generic type parameters have been + // replaced with the type `DYN`. Since the `DYN` type is compatible with + // any other type, this means that if `A` is a type parameter, the + // function types `int` and `int` are not disjoint. Likewise, + // `map` is not disjoint from `map`. + // + // When the `result_type` of a function is a generic type param, the + // type param name also appears as the `type` of on at least one params. + Params []*Type `protobuf:"bytes,2,rep,name=params,proto3" json:"params,omitempty"` + // The type param names associated with the function declaration. + // + // For example, `function ex(K key, map map) : V` would yield + // the type params of `K, V`. + TypeParams []string `protobuf:"bytes,3,rep,name=type_params,json=typeParams,proto3" json:"type_params,omitempty"` + // Required. The result type of the function. For example, the operator + // `string.isEmpty()` would have `result_type` of `kind: BOOL`. + ResultType *Type `protobuf:"bytes,4,opt,name=result_type,json=resultType,proto3" json:"result_type,omitempty"` + // Whether the function is to be used in a method call-style `x.f(...)` + // of a function call-style `f(x, ...)`. + // + // For methods, the first parameter declaration, `params[0]` is the + // expected type of the target receiver. + IsInstanceFunction bool `protobuf:"varint,5,opt,name=is_instance_function,json=isInstanceFunction,proto3" json:"is_instance_function,omitempty"` + // Documentation string for the overload. + Doc string `protobuf:"bytes,6,opt,name=doc,proto3" json:"doc,omitempty"` +} + +func (x *Decl_FunctionDecl_Overload) Reset() { + *x = Decl_FunctionDecl_Overload{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Decl_FunctionDecl_Overload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Decl_FunctionDecl_Overload) ProtoMessage() {} + +func (x *Decl_FunctionDecl_Overload) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_checked_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Decl_FunctionDecl_Overload.ProtoReflect.Descriptor instead. +func (*Decl_FunctionDecl_Overload) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_checked_proto_rawDescGZIP(), []int{2, 1, 0} +} + +func (x *Decl_FunctionDecl_Overload) GetOverloadId() string { + if x != nil { + return x.OverloadId + } + return "" +} + +func (x *Decl_FunctionDecl_Overload) GetParams() []*Type { + if x != nil { + return x.Params + } + return nil +} + +func (x *Decl_FunctionDecl_Overload) GetTypeParams() []string { + if x != nil { + return x.TypeParams + } + return nil +} + +func (x *Decl_FunctionDecl_Overload) GetResultType() *Type { + if x != nil { + return x.ResultType + } + return nil +} + +func (x *Decl_FunctionDecl_Overload) GetIsInstanceFunction() bool { + if x != nil { + return x.IsInstanceFunction + } + return false +} + +func (x *Decl_FunctionDecl_Overload) GetDoc() string { + if x != nil { + return x.Doc + } + return "" +} + +var File_google_api_expr_v1alpha1_checked_proto protoreflect.FileDescriptor + +var file_google_api_expr_v1alpha1_checked_proto_rawDesc = []byte{ + 0x0a, 0x26, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x78, 0x70, + 0x72, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x18, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x1a, 0x25, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, + 0x78, 0x70, 0x72, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x73, 0x79, 0x6e, + 0x74, 0x61, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9a, 0x04, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, + 0x45, 0x78, 0x70, 0x72, 0x12, 0x5c, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x45, 0x78, + 0x70, 0x72, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x4d, 0x61, 0x70, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x4d, + 0x61, 0x70, 0x12, 0x4d, 0x0a, 0x08, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x45, 0x78, 0x70, 0x72, 0x2e, 0x54, 0x79, 0x70, 0x65, + 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x74, 0x79, 0x70, 0x65, 0x4d, 0x61, + 0x70, 0x12, 0x45, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x78, 0x70, 0x72, + 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x65, 0x78, 0x70, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x04, 0x65, + 0x78, 0x70, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x52, 0x04, 0x65, 0x78, 0x70, 0x72, 0x1a, + 0x64, 0x0a, 0x11, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x4d, 0x61, 0x70, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x39, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5a, 0x0a, 0x0c, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x61, 0x70, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0xc8, 0x0b, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x03, 0x64, 0x79, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, + 0x00, 0x52, 0x03, 0x64, 0x79, 0x6e, 0x12, 0x30, 0x0a, 0x04, 0x6e, 0x75, 0x6c, 0x6c, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4e, 0x75, 0x6c, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x48, 0x00, 0x52, 0x04, 0x6e, 0x75, 0x6c, 0x6c, 0x12, 0x4c, 0x0a, 0x09, 0x70, 0x72, 0x69, 0x6d, + 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x50, 0x72, 0x69, 0x6d, + 0x69, 0x74, 0x69, 0x76, 0x65, 0x54, 0x79, 0x70, 0x65, 0x48, 0x00, 0x52, 0x09, 0x70, 0x72, 0x69, + 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x48, 0x0a, 0x07, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, + 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x48, 0x00, 0x52, 0x07, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, + 0x12, 0x4d, 0x0a, 0x0a, 0x77, 0x65, 0x6c, 0x6c, 0x5f, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x54, 0x79, 0x70, 0x65, 0x2e, 0x57, 0x65, 0x6c, 0x6c, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x54, 0x79, + 0x70, 0x65, 0x48, 0x00, 0x52, 0x09, 0x77, 0x65, 0x6c, 0x6c, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x12, + 0x46, 0x0a, 0x09, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x79, + 0x70, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x48, 0x00, 0x52, 0x08, 0x6c, + 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x43, 0x0a, 0x08, 0x6d, 0x61, 0x70, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x4d, 0x61, 0x70, 0x54, 0x79, 0x70, + 0x65, 0x48, 0x00, 0x52, 0x07, 0x6d, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x49, 0x0a, 0x08, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x46, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x48, 0x00, 0x52, 0x08, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x0a, + 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x00, 0x52, 0x09, 0x74, 0x79, 0x70, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x12, 0x34, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x48, 0x00, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x12, 0x52, 0x0a, 0x0d, 0x61, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x41, 0x62, 0x73, 0x74, 0x72, + 0x61, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x61, 0x62, 0x73, 0x74, 0x72, + 0x61, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x1a, 0x47, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x3b, 0x0a, 0x09, 0x65, 0x6c, 0x65, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x65, 0x6c, 0x65, 0x6d, 0x54, 0x79, 0x70, 0x65, + 0x1a, 0x83, 0x01, 0x0a, 0x07, 0x4d, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x39, 0x0a, 0x08, + 0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, + 0x6b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x1a, 0x8c, 0x01, 0x0a, 0x0c, 0x46, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3b, 0x0a, 0x09, 0x61, 0x72, 0x67, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x61, 0x72, 0x67, + 0x54, 0x79, 0x70, 0x65, 0x73, 0x1a, 0x6b, 0x0a, 0x0c, 0x41, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x47, 0x0a, 0x0f, 0x70, 0x61, 0x72, + 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x0e, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, + 0x65, 0x73, 0x22, 0x73, 0x0a, 0x0d, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x1a, 0x50, 0x52, 0x49, 0x4d, 0x49, 0x54, 0x49, 0x56, 0x45, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, + 0x44, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x42, 0x4f, 0x4f, 0x4c, 0x10, 0x01, 0x12, 0x09, 0x0a, + 0x05, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x49, 0x4e, 0x54, + 0x36, 0x34, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x4f, 0x55, 0x42, 0x4c, 0x45, 0x10, 0x04, + 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x12, 0x09, 0x0a, 0x05, + 0x42, 0x59, 0x54, 0x45, 0x53, 0x10, 0x06, 0x22, 0x56, 0x0a, 0x0d, 0x57, 0x65, 0x6c, 0x6c, 0x4b, + 0x6e, 0x6f, 0x77, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x57, 0x45, 0x4c, 0x4c, + 0x5f, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, + 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4e, 0x59, + 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x49, 0x4d, 0x45, 0x53, 0x54, 0x41, 0x4d, 0x50, 0x10, + 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x55, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x42, + 0x0b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0xb3, 0x05, 0x0a, + 0x04, 0x44, 0x65, 0x63, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x05, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x6c, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x44, 0x65, + 0x63, 0x6c, 0x48, 0x00, 0x52, 0x05, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x12, 0x49, 0x0a, 0x08, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x6c, 0x2e, 0x46, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x63, 0x6c, 0x48, 0x00, 0x52, 0x08, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x8b, 0x01, 0x0a, 0x09, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x44, 0x65, 0x63, 0x6c, 0x12, 0x32, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x38, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x64, 0x6f, 0x63, 0x1a, 0xee, 0x02, 0x0a, 0x0c, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x44, 0x65, 0x63, 0x6c, 0x12, 0x52, 0x0a, 0x09, 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x6f, 0x61, + 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x6c, 0x2e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x44, 0x65, 0x63, 0x6c, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x09, + 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x1a, 0x89, 0x02, 0x0a, 0x08, 0x4f, 0x76, + 0x65, 0x72, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x6f, + 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x76, 0x65, + 0x72, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x64, 0x12, 0x36, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, + 0x1f, 0x0a, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x12, 0x3f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x73, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x12, 0x69, 0x73, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x46, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x64, 0x6f, 0x63, 0x42, 0x0b, 0x0a, 0x09, 0x64, 0x65, 0x63, 0x6c, 0x5f, 0x6b, 0x69, + 0x6e, 0x64, 0x22, 0x7a, 0x0a, 0x09, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x6f, 0x61, 0x64, 0x5f, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x6f, + 0x61, 0x64, 0x49, 0x64, 0x12, 0x38, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, + 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x6c, + 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x09, + 0x44, 0x65, 0x63, 0x6c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, + 0x65, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, + 0x69, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x78, 0x70, 0x72, 0x2f, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65, 0x78, 0x70, 0x72, 0xf8, 0x01, 0x01, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_google_api_expr_v1alpha1_checked_proto_rawDescOnce sync.Once + file_google_api_expr_v1alpha1_checked_proto_rawDescData = file_google_api_expr_v1alpha1_checked_proto_rawDesc +) + +func file_google_api_expr_v1alpha1_checked_proto_rawDescGZIP() []byte { + file_google_api_expr_v1alpha1_checked_proto_rawDescOnce.Do(func() { + file_google_api_expr_v1alpha1_checked_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_api_expr_v1alpha1_checked_proto_rawDescData) + }) + return file_google_api_expr_v1alpha1_checked_proto_rawDescData +} + +var file_google_api_expr_v1alpha1_checked_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_google_api_expr_v1alpha1_checked_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_google_api_expr_v1alpha1_checked_proto_goTypes = []interface{}{ + (Type_PrimitiveType)(0), // 0: google.api.expr.v1alpha1.Type.PrimitiveType + (Type_WellKnownType)(0), // 1: google.api.expr.v1alpha1.Type.WellKnownType + (*CheckedExpr)(nil), // 2: google.api.expr.v1alpha1.CheckedExpr + (*Type)(nil), // 3: google.api.expr.v1alpha1.Type + (*Decl)(nil), // 4: google.api.expr.v1alpha1.Decl + (*Reference)(nil), // 5: google.api.expr.v1alpha1.Reference + nil, // 6: google.api.expr.v1alpha1.CheckedExpr.ReferenceMapEntry + nil, // 7: google.api.expr.v1alpha1.CheckedExpr.TypeMapEntry + (*Type_ListType)(nil), // 8: google.api.expr.v1alpha1.Type.ListType + (*Type_MapType)(nil), // 9: google.api.expr.v1alpha1.Type.MapType + (*Type_FunctionType)(nil), // 10: google.api.expr.v1alpha1.Type.FunctionType + (*Type_AbstractType)(nil), // 11: google.api.expr.v1alpha1.Type.AbstractType + (*Decl_IdentDecl)(nil), // 12: google.api.expr.v1alpha1.Decl.IdentDecl + (*Decl_FunctionDecl)(nil), // 13: google.api.expr.v1alpha1.Decl.FunctionDecl + (*Decl_FunctionDecl_Overload)(nil), // 14: google.api.expr.v1alpha1.Decl.FunctionDecl.Overload + (*SourceInfo)(nil), // 15: google.api.expr.v1alpha1.SourceInfo + (*Expr)(nil), // 16: google.api.expr.v1alpha1.Expr + (*emptypb.Empty)(nil), // 17: google.protobuf.Empty + (structpb.NullValue)(0), // 18: google.protobuf.NullValue + (*Constant)(nil), // 19: google.api.expr.v1alpha1.Constant +} +var file_google_api_expr_v1alpha1_checked_proto_depIdxs = []int32{ + 6, // 0: google.api.expr.v1alpha1.CheckedExpr.reference_map:type_name -> google.api.expr.v1alpha1.CheckedExpr.ReferenceMapEntry + 7, // 1: google.api.expr.v1alpha1.CheckedExpr.type_map:type_name -> google.api.expr.v1alpha1.CheckedExpr.TypeMapEntry + 15, // 2: google.api.expr.v1alpha1.CheckedExpr.source_info:type_name -> google.api.expr.v1alpha1.SourceInfo + 16, // 3: google.api.expr.v1alpha1.CheckedExpr.expr:type_name -> google.api.expr.v1alpha1.Expr + 17, // 4: google.api.expr.v1alpha1.Type.dyn:type_name -> google.protobuf.Empty + 18, // 5: google.api.expr.v1alpha1.Type.null:type_name -> google.protobuf.NullValue + 0, // 6: google.api.expr.v1alpha1.Type.primitive:type_name -> google.api.expr.v1alpha1.Type.PrimitiveType + 0, // 7: google.api.expr.v1alpha1.Type.wrapper:type_name -> google.api.expr.v1alpha1.Type.PrimitiveType + 1, // 8: google.api.expr.v1alpha1.Type.well_known:type_name -> google.api.expr.v1alpha1.Type.WellKnownType + 8, // 9: google.api.expr.v1alpha1.Type.list_type:type_name -> google.api.expr.v1alpha1.Type.ListType + 9, // 10: google.api.expr.v1alpha1.Type.map_type:type_name -> google.api.expr.v1alpha1.Type.MapType + 10, // 11: google.api.expr.v1alpha1.Type.function:type_name -> google.api.expr.v1alpha1.Type.FunctionType + 3, // 12: google.api.expr.v1alpha1.Type.type:type_name -> google.api.expr.v1alpha1.Type + 17, // 13: google.api.expr.v1alpha1.Type.error:type_name -> google.protobuf.Empty + 11, // 14: google.api.expr.v1alpha1.Type.abstract_type:type_name -> google.api.expr.v1alpha1.Type.AbstractType + 12, // 15: google.api.expr.v1alpha1.Decl.ident:type_name -> google.api.expr.v1alpha1.Decl.IdentDecl + 13, // 16: google.api.expr.v1alpha1.Decl.function:type_name -> google.api.expr.v1alpha1.Decl.FunctionDecl + 19, // 17: google.api.expr.v1alpha1.Reference.value:type_name -> google.api.expr.v1alpha1.Constant + 5, // 18: google.api.expr.v1alpha1.CheckedExpr.ReferenceMapEntry.value:type_name -> google.api.expr.v1alpha1.Reference + 3, // 19: google.api.expr.v1alpha1.CheckedExpr.TypeMapEntry.value:type_name -> google.api.expr.v1alpha1.Type + 3, // 20: google.api.expr.v1alpha1.Type.ListType.elem_type:type_name -> google.api.expr.v1alpha1.Type + 3, // 21: google.api.expr.v1alpha1.Type.MapType.key_type:type_name -> google.api.expr.v1alpha1.Type + 3, // 22: google.api.expr.v1alpha1.Type.MapType.value_type:type_name -> google.api.expr.v1alpha1.Type + 3, // 23: google.api.expr.v1alpha1.Type.FunctionType.result_type:type_name -> google.api.expr.v1alpha1.Type + 3, // 24: google.api.expr.v1alpha1.Type.FunctionType.arg_types:type_name -> google.api.expr.v1alpha1.Type + 3, // 25: google.api.expr.v1alpha1.Type.AbstractType.parameter_types:type_name -> google.api.expr.v1alpha1.Type + 3, // 26: google.api.expr.v1alpha1.Decl.IdentDecl.type:type_name -> google.api.expr.v1alpha1.Type + 19, // 27: google.api.expr.v1alpha1.Decl.IdentDecl.value:type_name -> google.api.expr.v1alpha1.Constant + 14, // 28: google.api.expr.v1alpha1.Decl.FunctionDecl.overloads:type_name -> google.api.expr.v1alpha1.Decl.FunctionDecl.Overload + 3, // 29: google.api.expr.v1alpha1.Decl.FunctionDecl.Overload.params:type_name -> google.api.expr.v1alpha1.Type + 3, // 30: google.api.expr.v1alpha1.Decl.FunctionDecl.Overload.result_type:type_name -> google.api.expr.v1alpha1.Type + 31, // [31:31] is the sub-list for method output_type + 31, // [31:31] is the sub-list for method input_type + 31, // [31:31] is the sub-list for extension type_name + 31, // [31:31] is the sub-list for extension extendee + 0, // [0:31] is the sub-list for field type_name +} + +func init() { file_google_api_expr_v1alpha1_checked_proto_init() } +func file_google_api_expr_v1alpha1_checked_proto_init() { + if File_google_api_expr_v1alpha1_checked_proto != nil { + return + } + file_google_api_expr_v1alpha1_syntax_proto_init() + if !protoimpl.UnsafeEnabled { + file_google_api_expr_v1alpha1_checked_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CheckedExpr); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_checked_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Type); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_checked_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Decl); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_checked_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Reference); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_checked_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Type_ListType); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_checked_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Type_MapType); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_checked_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Type_FunctionType); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_checked_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Type_AbstractType); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_checked_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Decl_IdentDecl); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_checked_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Decl_FunctionDecl); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_checked_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Decl_FunctionDecl_Overload); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_google_api_expr_v1alpha1_checked_proto_msgTypes[1].OneofWrappers = []interface{}{ + (*Type_Dyn)(nil), + (*Type_Null)(nil), + (*Type_Primitive)(nil), + (*Type_Wrapper)(nil), + (*Type_WellKnown)(nil), + (*Type_ListType_)(nil), + (*Type_MapType_)(nil), + (*Type_Function)(nil), + (*Type_MessageType)(nil), + (*Type_TypeParam)(nil), + (*Type_Type)(nil), + (*Type_Error)(nil), + (*Type_AbstractType_)(nil), + } + file_google_api_expr_v1alpha1_checked_proto_msgTypes[2].OneofWrappers = []interface{}{ + (*Decl_Ident)(nil), + (*Decl_Function)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_google_api_expr_v1alpha1_checked_proto_rawDesc, + NumEnums: 2, + NumMessages: 13, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_google_api_expr_v1alpha1_checked_proto_goTypes, + DependencyIndexes: file_google_api_expr_v1alpha1_checked_proto_depIdxs, + EnumInfos: file_google_api_expr_v1alpha1_checked_proto_enumTypes, + MessageInfos: file_google_api_expr_v1alpha1_checked_proto_msgTypes, + }.Build() + File_google_api_expr_v1alpha1_checked_proto = out.File + file_google_api_expr_v1alpha1_checked_proto_rawDesc = nil + file_google_api_expr_v1alpha1_checked_proto_goTypes = nil + file_google_api_expr_v1alpha1_checked_proto_depIdxs = nil +} diff --git a/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/eval.pb.go b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/eval.pb.go new file mode 100644 index 0000000000..678e65dfda --- /dev/null +++ b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/eval.pb.go @@ -0,0 +1,579 @@ +// Copyright 2021 Google LLC +// +// 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.12.2 +// source: google/api/expr/v1alpha1/eval.proto + +package expr + +import ( + reflect "reflect" + sync "sync" + + status "google.golang.org/genproto/googleapis/rpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// The state of an evaluation. +// +// Can represent an inital, partial, or completed state of evaluation. +type EvalState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The unique values referenced in this message. + Values []*ExprValue `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` + // An ordered list of results. + // + // Tracks the flow of evaluation through the expression. + // May be sparse. + Results []*EvalState_Result `protobuf:"bytes,3,rep,name=results,proto3" json:"results,omitempty"` +} + +func (x *EvalState) Reset() { + *x = EvalState{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_eval_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EvalState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EvalState) ProtoMessage() {} + +func (x *EvalState) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_eval_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EvalState.ProtoReflect.Descriptor instead. +func (*EvalState) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_eval_proto_rawDescGZIP(), []int{0} +} + +func (x *EvalState) GetValues() []*ExprValue { + if x != nil { + return x.Values + } + return nil +} + +func (x *EvalState) GetResults() []*EvalState_Result { + if x != nil { + return x.Results + } + return nil +} + +// The value of an evaluated expression. +type ExprValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // An expression can resolve to a value, error or unknown. + // + // Types that are assignable to Kind: + // *ExprValue_Value + // *ExprValue_Error + // *ExprValue_Unknown + Kind isExprValue_Kind `protobuf_oneof:"kind"` +} + +func (x *ExprValue) Reset() { + *x = ExprValue{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_eval_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExprValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExprValue) ProtoMessage() {} + +func (x *ExprValue) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_eval_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExprValue.ProtoReflect.Descriptor instead. +func (*ExprValue) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_eval_proto_rawDescGZIP(), []int{1} +} + +func (m *ExprValue) GetKind() isExprValue_Kind { + if m != nil { + return m.Kind + } + return nil +} + +func (x *ExprValue) GetValue() *Value { + if x, ok := x.GetKind().(*ExprValue_Value); ok { + return x.Value + } + return nil +} + +func (x *ExprValue) GetError() *ErrorSet { + if x, ok := x.GetKind().(*ExprValue_Error); ok { + return x.Error + } + return nil +} + +func (x *ExprValue) GetUnknown() *UnknownSet { + if x, ok := x.GetKind().(*ExprValue_Unknown); ok { + return x.Unknown + } + return nil +} + +type isExprValue_Kind interface { + isExprValue_Kind() +} + +type ExprValue_Value struct { + // A concrete value. + Value *Value `protobuf:"bytes,1,opt,name=value,proto3,oneof"` +} + +type ExprValue_Error struct { + // The set of errors in the critical path of evalution. + // + // Only errors in the critical path are included. For example, + // `( || true) && ` will only result in ``, + // while ` || ` will result in both `` and + // ``. + // + // Errors cause by the presence of other errors are not included in the + // set. For example `.foo`, `foo()`, and ` + 1` will + // only result in ``. + // + // Multiple errors *might* be included when evaluation could result + // in different errors. For example ` + ` and + // `foo(, )` may result in ``, `` or both. + // The exact subset of errors included for this case is unspecified and + // depends on the implementation details of the evaluator. + Error *ErrorSet `protobuf:"bytes,2,opt,name=error,proto3,oneof"` +} + +type ExprValue_Unknown struct { + // The set of unknowns in the critical path of evaluation. + // + // Unknown behaves identically to Error with regards to propagation. + // Specifically, only unknowns in the critical path are included, unknowns + // caused by the presence of other unknowns are not included, and multiple + // unknowns *might* be included included when evaluation could result in + // different unknowns. For example: + // + // ( || true) && -> + // || -> + // .foo -> + // foo() -> + // + -> or + // + // Unknown takes precidence over Error in cases where a `Value` can short + // circuit the result: + // + // || -> + // && -> + // + // Errors take precidence in all other cases: + // + // + -> + // foo(, ) -> + Unknown *UnknownSet `protobuf:"bytes,3,opt,name=unknown,proto3,oneof"` +} + +func (*ExprValue_Value) isExprValue_Kind() {} + +func (*ExprValue_Error) isExprValue_Kind() {} + +func (*ExprValue_Unknown) isExprValue_Kind() {} + +// A set of errors. +// +// The errors included depend on the context. See `ExprValue.error`. +type ErrorSet struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The errors in the set. + Errors []*status.Status `protobuf:"bytes,1,rep,name=errors,proto3" json:"errors,omitempty"` +} + +func (x *ErrorSet) Reset() { + *x = ErrorSet{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_eval_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ErrorSet) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ErrorSet) ProtoMessage() {} + +func (x *ErrorSet) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_eval_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ErrorSet.ProtoReflect.Descriptor instead. +func (*ErrorSet) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_eval_proto_rawDescGZIP(), []int{2} +} + +func (x *ErrorSet) GetErrors() []*status.Status { + if x != nil { + return x.Errors + } + return nil +} + +// A set of expressions for which the value is unknown. +// +// The unknowns included depend on the context. See `ExprValue.unknown`. +type UnknownSet struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The ids of the expressions with unknown values. + Exprs []int64 `protobuf:"varint,1,rep,packed,name=exprs,proto3" json:"exprs,omitempty"` +} + +func (x *UnknownSet) Reset() { + *x = UnknownSet{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_eval_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnknownSet) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnknownSet) ProtoMessage() {} + +func (x *UnknownSet) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_eval_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UnknownSet.ProtoReflect.Descriptor instead. +func (*UnknownSet) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_eval_proto_rawDescGZIP(), []int{3} +} + +func (x *UnknownSet) GetExprs() []int64 { + if x != nil { + return x.Exprs + } + return nil +} + +// A single evalution result. +type EvalState_Result struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The id of the expression this result if for. + Expr int64 `protobuf:"varint,1,opt,name=expr,proto3" json:"expr,omitempty"` + // The index in `values` of the resulting value. + Value int64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *EvalState_Result) Reset() { + *x = EvalState_Result{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_eval_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EvalState_Result) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EvalState_Result) ProtoMessage() {} + +func (x *EvalState_Result) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_eval_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EvalState_Result.ProtoReflect.Descriptor instead. +func (*EvalState_Result) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_eval_proto_rawDescGZIP(), []int{0, 0} +} + +func (x *EvalState_Result) GetExpr() int64 { + if x != nil { + return x.Expr + } + return 0 +} + +func (x *EvalState_Result) GetValue() int64 { + if x != nil { + return x.Value + } + return 0 +} + +var File_google_api_expr_v1alpha1_eval_proto protoreflect.FileDescriptor + +var file_google_api_expr_v1alpha1_eval_proto_rawDesc = []byte{ + 0x0a, 0x23, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x78, 0x70, + 0x72, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x65, 0x76, 0x61, 0x6c, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x18, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, + 0x24, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x78, 0x70, 0x72, + 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x72, 0x70, + 0x63, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc2, + 0x01, 0x0a, 0x09, 0x45, 0x76, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3b, 0x0a, 0x06, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x44, 0x0a, 0x07, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x76, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x1a, + 0x32, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x78, 0x70, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x65, 0x78, 0x70, 0x72, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x22, 0xca, 0x01, 0x0a, 0x09, 0x45, 0x78, 0x70, 0x72, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x37, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, + 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x48, 0x00, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3a, 0x0a, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x53, 0x65, 0x74, 0x48, 0x00, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x40, 0x0a, 0x07, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53, 0x65, 0x74, 0x48, 0x00, 0x52, + 0x07, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x42, 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, + 0x22, 0x36, 0x0a, 0x08, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x53, 0x65, 0x74, 0x12, 0x2a, 0x0a, 0x06, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x22, 0x0a, 0x0a, 0x55, 0x6e, 0x6b, 0x6e, + 0x6f, 0x77, 0x6e, 0x53, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x78, 0x70, 0x72, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x03, 0x52, 0x05, 0x65, 0x78, 0x70, 0x72, 0x73, 0x42, 0x6c, 0x0a, 0x1c, + 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, + 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x09, 0x45, 0x76, + 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x65, 0x6e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x78, 0x70, 0x72, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x3b, 0x65, 0x78, 0x70, 0x72, 0xf8, 0x01, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_google_api_expr_v1alpha1_eval_proto_rawDescOnce sync.Once + file_google_api_expr_v1alpha1_eval_proto_rawDescData = file_google_api_expr_v1alpha1_eval_proto_rawDesc +) + +func file_google_api_expr_v1alpha1_eval_proto_rawDescGZIP() []byte { + file_google_api_expr_v1alpha1_eval_proto_rawDescOnce.Do(func() { + file_google_api_expr_v1alpha1_eval_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_api_expr_v1alpha1_eval_proto_rawDescData) + }) + return file_google_api_expr_v1alpha1_eval_proto_rawDescData +} + +var file_google_api_expr_v1alpha1_eval_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_google_api_expr_v1alpha1_eval_proto_goTypes = []interface{}{ + (*EvalState)(nil), // 0: google.api.expr.v1alpha1.EvalState + (*ExprValue)(nil), // 1: google.api.expr.v1alpha1.ExprValue + (*ErrorSet)(nil), // 2: google.api.expr.v1alpha1.ErrorSet + (*UnknownSet)(nil), // 3: google.api.expr.v1alpha1.UnknownSet + (*EvalState_Result)(nil), // 4: google.api.expr.v1alpha1.EvalState.Result + (*Value)(nil), // 5: google.api.expr.v1alpha1.Value + (*status.Status)(nil), // 6: google.rpc.Status +} +var file_google_api_expr_v1alpha1_eval_proto_depIdxs = []int32{ + 1, // 0: google.api.expr.v1alpha1.EvalState.values:type_name -> google.api.expr.v1alpha1.ExprValue + 4, // 1: google.api.expr.v1alpha1.EvalState.results:type_name -> google.api.expr.v1alpha1.EvalState.Result + 5, // 2: google.api.expr.v1alpha1.ExprValue.value:type_name -> google.api.expr.v1alpha1.Value + 2, // 3: google.api.expr.v1alpha1.ExprValue.error:type_name -> google.api.expr.v1alpha1.ErrorSet + 3, // 4: google.api.expr.v1alpha1.ExprValue.unknown:type_name -> google.api.expr.v1alpha1.UnknownSet + 6, // 5: google.api.expr.v1alpha1.ErrorSet.errors:type_name -> google.rpc.Status + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_google_api_expr_v1alpha1_eval_proto_init() } +func file_google_api_expr_v1alpha1_eval_proto_init() { + if File_google_api_expr_v1alpha1_eval_proto != nil { + return + } + file_google_api_expr_v1alpha1_value_proto_init() + if !protoimpl.UnsafeEnabled { + file_google_api_expr_v1alpha1_eval_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EvalState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_eval_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExprValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_eval_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ErrorSet); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_eval_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UnknownSet); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_eval_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EvalState_Result); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_google_api_expr_v1alpha1_eval_proto_msgTypes[1].OneofWrappers = []interface{}{ + (*ExprValue_Value)(nil), + (*ExprValue_Error)(nil), + (*ExprValue_Unknown)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_google_api_expr_v1alpha1_eval_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_google_api_expr_v1alpha1_eval_proto_goTypes, + DependencyIndexes: file_google_api_expr_v1alpha1_eval_proto_depIdxs, + MessageInfos: file_google_api_expr_v1alpha1_eval_proto_msgTypes, + }.Build() + File_google_api_expr_v1alpha1_eval_proto = out.File + file_google_api_expr_v1alpha1_eval_proto_rawDesc = nil + file_google_api_expr_v1alpha1_eval_proto_goTypes = nil + file_google_api_expr_v1alpha1_eval_proto_depIdxs = nil +} diff --git a/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/explain.pb.go b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/explain.pb.go new file mode 100644 index 0000000000..65be0a031f --- /dev/null +++ b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/explain.pb.go @@ -0,0 +1,275 @@ +// Copyright 2021 Google LLC +// +// 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.12.2 +// source: google/api/expr/v1alpha1/explain.proto + +package expr + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Values of intermediate expressions produced when evaluating expression. +// Deprecated, use `EvalState` instead. +// +// Deprecated: Do not use. +type Explain struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // All of the observed values. + // + // The field value_index is an index in the values list. + // Separating values from steps is needed to remove redundant values. + Values []*Value `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` + // List of steps. + // + // Repeated evaluations of the same expression generate new ExprStep + // instances. The order of such ExprStep instances matches the order of + // elements returned by Comprehension.iter_range. + ExprSteps []*Explain_ExprStep `protobuf:"bytes,2,rep,name=expr_steps,json=exprSteps,proto3" json:"expr_steps,omitempty"` +} + +func (x *Explain) Reset() { + *x = Explain{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_explain_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Explain) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Explain) ProtoMessage() {} + +func (x *Explain) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_explain_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Explain.ProtoReflect.Descriptor instead. +func (*Explain) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_explain_proto_rawDescGZIP(), []int{0} +} + +func (x *Explain) GetValues() []*Value { + if x != nil { + return x.Values + } + return nil +} + +func (x *Explain) GetExprSteps() []*Explain_ExprStep { + if x != nil { + return x.ExprSteps + } + return nil +} + +// ID and value index of one step. +type Explain_ExprStep struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // ID of corresponding Expr node. + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + // Index of the value in the values list. + ValueIndex int32 `protobuf:"varint,2,opt,name=value_index,json=valueIndex,proto3" json:"value_index,omitempty"` +} + +func (x *Explain_ExprStep) Reset() { + *x = Explain_ExprStep{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_explain_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Explain_ExprStep) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Explain_ExprStep) ProtoMessage() {} + +func (x *Explain_ExprStep) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_explain_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Explain_ExprStep.ProtoReflect.Descriptor instead. +func (*Explain_ExprStep) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_explain_proto_rawDescGZIP(), []int{0, 0} +} + +func (x *Explain_ExprStep) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Explain_ExprStep) GetValueIndex() int32 { + if x != nil { + return x.ValueIndex + } + return 0 +} + +var File_google_api_expr_v1alpha1_explain_proto protoreflect.FileDescriptor + +var file_google_api_expr_v1alpha1_explain_proto_rawDesc = []byte{ + 0x0a, 0x26, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x78, 0x70, + 0x72, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x65, 0x78, 0x70, 0x6c, 0x61, + 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x18, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x1a, 0x24, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, + 0x78, 0x70, 0x72, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xce, 0x01, 0x0a, 0x07, 0x45, 0x78, 0x70, + 0x6c, 0x61, 0x69, 0x6e, 0x12, 0x37, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x49, 0x0a, + 0x0a, 0x65, 0x78, 0x70, 0x72, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, + 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, + 0x6c, 0x61, 0x69, 0x6e, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x53, 0x74, 0x65, 0x70, 0x52, 0x09, 0x65, + 0x78, 0x70, 0x72, 0x53, 0x74, 0x65, 0x70, 0x73, 0x1a, 0x3b, 0x0a, 0x08, 0x45, 0x78, 0x70, 0x72, + 0x53, 0x74, 0x65, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x02, 0x18, 0x01, 0x42, 0x6f, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x0c, 0x45, 0x78, 0x70, 0x6c, 0x61, + 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x65, 0x6e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x78, 0x70, 0x72, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x3b, 0x65, 0x78, 0x70, 0x72, 0xf8, 0x01, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_google_api_expr_v1alpha1_explain_proto_rawDescOnce sync.Once + file_google_api_expr_v1alpha1_explain_proto_rawDescData = file_google_api_expr_v1alpha1_explain_proto_rawDesc +) + +func file_google_api_expr_v1alpha1_explain_proto_rawDescGZIP() []byte { + file_google_api_expr_v1alpha1_explain_proto_rawDescOnce.Do(func() { + file_google_api_expr_v1alpha1_explain_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_api_expr_v1alpha1_explain_proto_rawDescData) + }) + return file_google_api_expr_v1alpha1_explain_proto_rawDescData +} + +var file_google_api_expr_v1alpha1_explain_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_google_api_expr_v1alpha1_explain_proto_goTypes = []interface{}{ + (*Explain)(nil), // 0: google.api.expr.v1alpha1.Explain + (*Explain_ExprStep)(nil), // 1: google.api.expr.v1alpha1.Explain.ExprStep + (*Value)(nil), // 2: google.api.expr.v1alpha1.Value +} +var file_google_api_expr_v1alpha1_explain_proto_depIdxs = []int32{ + 2, // 0: google.api.expr.v1alpha1.Explain.values:type_name -> google.api.expr.v1alpha1.Value + 1, // 1: google.api.expr.v1alpha1.Explain.expr_steps:type_name -> google.api.expr.v1alpha1.Explain.ExprStep + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_google_api_expr_v1alpha1_explain_proto_init() } +func file_google_api_expr_v1alpha1_explain_proto_init() { + if File_google_api_expr_v1alpha1_explain_proto != nil { + return + } + file_google_api_expr_v1alpha1_value_proto_init() + if !protoimpl.UnsafeEnabled { + file_google_api_expr_v1alpha1_explain_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Explain); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_explain_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Explain_ExprStep); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_google_api_expr_v1alpha1_explain_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_google_api_expr_v1alpha1_explain_proto_goTypes, + DependencyIndexes: file_google_api_expr_v1alpha1_explain_proto_depIdxs, + MessageInfos: file_google_api_expr_v1alpha1_explain_proto_msgTypes, + }.Build() + File_google_api_expr_v1alpha1_explain_proto = out.File + file_google_api_expr_v1alpha1_explain_proto_rawDesc = nil + file_google_api_expr_v1alpha1_explain_proto_goTypes = nil + file_google_api_expr_v1alpha1_explain_proto_depIdxs = nil +} diff --git a/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/syntax.pb.go b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/syntax.pb.go new file mode 100644 index 0000000000..175dec53c3 --- /dev/null +++ b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/syntax.pb.go @@ -0,0 +1,1685 @@ +// Copyright 2021 Google LLC +// +// 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.12.2 +// source: google/api/expr/v1alpha1/syntax.proto + +package expr + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + durationpb "google.golang.org/protobuf/types/known/durationpb" + structpb "google.golang.org/protobuf/types/known/structpb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// An expression together with source information as returned by the parser. +type ParsedExpr struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The parsed expression. + Expr *Expr `protobuf:"bytes,2,opt,name=expr,proto3" json:"expr,omitempty"` + // The source info derived from input that generated the parsed `expr`. + SourceInfo *SourceInfo `protobuf:"bytes,3,opt,name=source_info,json=sourceInfo,proto3" json:"source_info,omitempty"` +} + +func (x *ParsedExpr) Reset() { + *x = ParsedExpr{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ParsedExpr) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ParsedExpr) ProtoMessage() {} + +func (x *ParsedExpr) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ParsedExpr.ProtoReflect.Descriptor instead. +func (*ParsedExpr) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_syntax_proto_rawDescGZIP(), []int{0} +} + +func (x *ParsedExpr) GetExpr() *Expr { + if x != nil { + return x.Expr + } + return nil +} + +func (x *ParsedExpr) GetSourceInfo() *SourceInfo { + if x != nil { + return x.SourceInfo + } + return nil +} + +// An abstract representation of a common expression. +// +// Expressions are abstractly represented as a collection of identifiers, +// select statements, function calls, literals, and comprehensions. All +// operators with the exception of the '.' operator are modelled as function +// calls. This makes it easy to represent new operators into the existing AST. +// +// All references within expressions must resolve to a [Decl][google.api.expr.v1alpha1.Decl] provided at +// type-check for an expression to be valid. A reference may either be a bare +// identifier `name` or a qualified identifier `google.api.name`. References +// may either refer to a value or a function declaration. +// +// For example, the expression `google.api.name.startsWith('expr')` references +// the declaration `google.api.name` within a [Expr.Select][google.api.expr.v1alpha1.Expr.Select] expression, and +// the function declaration `startsWith`. +type Expr struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Required. An id assigned to this node by the parser which is unique in a + // given expression tree. This is used to associate type information and other + // attributes to a node in the parse tree. + Id int64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` + // Required. Variants of expressions. + // + // Types that are assignable to ExprKind: + // *Expr_ConstExpr + // *Expr_IdentExpr + // *Expr_SelectExpr + // *Expr_CallExpr + // *Expr_ListExpr + // *Expr_StructExpr + // *Expr_ComprehensionExpr + ExprKind isExpr_ExprKind `protobuf_oneof:"expr_kind"` +} + +func (x *Expr) Reset() { + *x = Expr{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Expr) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Expr) ProtoMessage() {} + +func (x *Expr) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Expr.ProtoReflect.Descriptor instead. +func (*Expr) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_syntax_proto_rawDescGZIP(), []int{1} +} + +func (x *Expr) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + +func (m *Expr) GetExprKind() isExpr_ExprKind { + if m != nil { + return m.ExprKind + } + return nil +} + +func (x *Expr) GetConstExpr() *Constant { + if x, ok := x.GetExprKind().(*Expr_ConstExpr); ok { + return x.ConstExpr + } + return nil +} + +func (x *Expr) GetIdentExpr() *Expr_Ident { + if x, ok := x.GetExprKind().(*Expr_IdentExpr); ok { + return x.IdentExpr + } + return nil +} + +func (x *Expr) GetSelectExpr() *Expr_Select { + if x, ok := x.GetExprKind().(*Expr_SelectExpr); ok { + return x.SelectExpr + } + return nil +} + +func (x *Expr) GetCallExpr() *Expr_Call { + if x, ok := x.GetExprKind().(*Expr_CallExpr); ok { + return x.CallExpr + } + return nil +} + +func (x *Expr) GetListExpr() *Expr_CreateList { + if x, ok := x.GetExprKind().(*Expr_ListExpr); ok { + return x.ListExpr + } + return nil +} + +func (x *Expr) GetStructExpr() *Expr_CreateStruct { + if x, ok := x.GetExprKind().(*Expr_StructExpr); ok { + return x.StructExpr + } + return nil +} + +func (x *Expr) GetComprehensionExpr() *Expr_Comprehension { + if x, ok := x.GetExprKind().(*Expr_ComprehensionExpr); ok { + return x.ComprehensionExpr + } + return nil +} + +type isExpr_ExprKind interface { + isExpr_ExprKind() +} + +type Expr_ConstExpr struct { + // A literal expression. + ConstExpr *Constant `protobuf:"bytes,3,opt,name=const_expr,json=constExpr,proto3,oneof"` +} + +type Expr_IdentExpr struct { + // An identifier expression. + IdentExpr *Expr_Ident `protobuf:"bytes,4,opt,name=ident_expr,json=identExpr,proto3,oneof"` +} + +type Expr_SelectExpr struct { + // A field selection expression, e.g. `request.auth`. + SelectExpr *Expr_Select `protobuf:"bytes,5,opt,name=select_expr,json=selectExpr,proto3,oneof"` +} + +type Expr_CallExpr struct { + // A call expression, including calls to predefined functions and operators. + CallExpr *Expr_Call `protobuf:"bytes,6,opt,name=call_expr,json=callExpr,proto3,oneof"` +} + +type Expr_ListExpr struct { + // A list creation expression. + ListExpr *Expr_CreateList `protobuf:"bytes,7,opt,name=list_expr,json=listExpr,proto3,oneof"` +} + +type Expr_StructExpr struct { + // A map or message creation expression. + StructExpr *Expr_CreateStruct `protobuf:"bytes,8,opt,name=struct_expr,json=structExpr,proto3,oneof"` +} + +type Expr_ComprehensionExpr struct { + // A comprehension expression. + ComprehensionExpr *Expr_Comprehension `protobuf:"bytes,9,opt,name=comprehension_expr,json=comprehensionExpr,proto3,oneof"` +} + +func (*Expr_ConstExpr) isExpr_ExprKind() {} + +func (*Expr_IdentExpr) isExpr_ExprKind() {} + +func (*Expr_SelectExpr) isExpr_ExprKind() {} + +func (*Expr_CallExpr) isExpr_ExprKind() {} + +func (*Expr_ListExpr) isExpr_ExprKind() {} + +func (*Expr_StructExpr) isExpr_ExprKind() {} + +func (*Expr_ComprehensionExpr) isExpr_ExprKind() {} + +// Represents a primitive literal. +// +// Named 'Constant' here for backwards compatibility. +// +// This is similar as the primitives supported in the well-known type +// `google.protobuf.Value`, but richer so it can represent CEL's full range of +// primitives. +// +// Lists and structs are not included as constants as these aggregate types may +// contain [Expr][google.api.expr.v1alpha1.Expr] elements which require evaluation and are thus not constant. +// +// Examples of literals include: `"hello"`, `b'bytes'`, `1u`, `4.2`, `-2`, +// `true`, `null`. +type Constant struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Required. The valid constant kinds. + // + // Types that are assignable to ConstantKind: + // *Constant_NullValue + // *Constant_BoolValue + // *Constant_Int64Value + // *Constant_Uint64Value + // *Constant_DoubleValue + // *Constant_StringValue + // *Constant_BytesValue + // *Constant_DurationValue + // *Constant_TimestampValue + ConstantKind isConstant_ConstantKind `protobuf_oneof:"constant_kind"` +} + +func (x *Constant) Reset() { + *x = Constant{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Constant) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Constant) ProtoMessage() {} + +func (x *Constant) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Constant.ProtoReflect.Descriptor instead. +func (*Constant) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_syntax_proto_rawDescGZIP(), []int{2} +} + +func (m *Constant) GetConstantKind() isConstant_ConstantKind { + if m != nil { + return m.ConstantKind + } + return nil +} + +func (x *Constant) GetNullValue() structpb.NullValue { + if x, ok := x.GetConstantKind().(*Constant_NullValue); ok { + return x.NullValue + } + return structpb.NullValue_NULL_VALUE +} + +func (x *Constant) GetBoolValue() bool { + if x, ok := x.GetConstantKind().(*Constant_BoolValue); ok { + return x.BoolValue + } + return false +} + +func (x *Constant) GetInt64Value() int64 { + if x, ok := x.GetConstantKind().(*Constant_Int64Value); ok { + return x.Int64Value + } + return 0 +} + +func (x *Constant) GetUint64Value() uint64 { + if x, ok := x.GetConstantKind().(*Constant_Uint64Value); ok { + return x.Uint64Value + } + return 0 +} + +func (x *Constant) GetDoubleValue() float64 { + if x, ok := x.GetConstantKind().(*Constant_DoubleValue); ok { + return x.DoubleValue + } + return 0 +} + +func (x *Constant) GetStringValue() string { + if x, ok := x.GetConstantKind().(*Constant_StringValue); ok { + return x.StringValue + } + return "" +} + +func (x *Constant) GetBytesValue() []byte { + if x, ok := x.GetConstantKind().(*Constant_BytesValue); ok { + return x.BytesValue + } + return nil +} + +// Deprecated: Do not use. +func (x *Constant) GetDurationValue() *durationpb.Duration { + if x, ok := x.GetConstantKind().(*Constant_DurationValue); ok { + return x.DurationValue + } + return nil +} + +// Deprecated: Do not use. +func (x *Constant) GetTimestampValue() *timestamppb.Timestamp { + if x, ok := x.GetConstantKind().(*Constant_TimestampValue); ok { + return x.TimestampValue + } + return nil +} + +type isConstant_ConstantKind interface { + isConstant_ConstantKind() +} + +type Constant_NullValue struct { + // null value. + NullValue structpb.NullValue `protobuf:"varint,1,opt,name=null_value,json=nullValue,proto3,enum=google.protobuf.NullValue,oneof"` +} + +type Constant_BoolValue struct { + // boolean value. + BoolValue bool `protobuf:"varint,2,opt,name=bool_value,json=boolValue,proto3,oneof"` +} + +type Constant_Int64Value struct { + // int64 value. + Int64Value int64 `protobuf:"varint,3,opt,name=int64_value,json=int64Value,proto3,oneof"` +} + +type Constant_Uint64Value struct { + // uint64 value. + Uint64Value uint64 `protobuf:"varint,4,opt,name=uint64_value,json=uint64Value,proto3,oneof"` +} + +type Constant_DoubleValue struct { + // double value. + DoubleValue float64 `protobuf:"fixed64,5,opt,name=double_value,json=doubleValue,proto3,oneof"` +} + +type Constant_StringValue struct { + // string value. + StringValue string `protobuf:"bytes,6,opt,name=string_value,json=stringValue,proto3,oneof"` +} + +type Constant_BytesValue struct { + // bytes value. + BytesValue []byte `protobuf:"bytes,7,opt,name=bytes_value,json=bytesValue,proto3,oneof"` +} + +type Constant_DurationValue struct { + // protobuf.Duration value. + // + // Deprecated: duration is no longer considered a builtin cel type. + // + // Deprecated: Do not use. + DurationValue *durationpb.Duration `protobuf:"bytes,8,opt,name=duration_value,json=durationValue,proto3,oneof"` +} + +type Constant_TimestampValue struct { + // protobuf.Timestamp value. + // + // Deprecated: timestamp is no longer considered a builtin cel type. + // + // Deprecated: Do not use. + TimestampValue *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=timestamp_value,json=timestampValue,proto3,oneof"` +} + +func (*Constant_NullValue) isConstant_ConstantKind() {} + +func (*Constant_BoolValue) isConstant_ConstantKind() {} + +func (*Constant_Int64Value) isConstant_ConstantKind() {} + +func (*Constant_Uint64Value) isConstant_ConstantKind() {} + +func (*Constant_DoubleValue) isConstant_ConstantKind() {} + +func (*Constant_StringValue) isConstant_ConstantKind() {} + +func (*Constant_BytesValue) isConstant_ConstantKind() {} + +func (*Constant_DurationValue) isConstant_ConstantKind() {} + +func (*Constant_TimestampValue) isConstant_ConstantKind() {} + +// Source information collected at parse time. +type SourceInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The syntax version of the source, e.g. `cel1`. + SyntaxVersion string `protobuf:"bytes,1,opt,name=syntax_version,json=syntaxVersion,proto3" json:"syntax_version,omitempty"` + // The location name. All position information attached to an expression is + // relative to this location. + // + // The location could be a file, UI element, or similar. For example, + // `acme/app/AnvilPolicy.cel`. + Location string `protobuf:"bytes,2,opt,name=location,proto3" json:"location,omitempty"` + // Monotonically increasing list of code point offsets where newlines + // `\n` appear. + // + // The line number of a given position is the index `i` where for a given + // `id` the `line_offsets[i] < id_positions[id] < line_offsets[i+1]`. The + // column may be derivd from `id_positions[id] - line_offsets[i]`. + LineOffsets []int32 `protobuf:"varint,3,rep,packed,name=line_offsets,json=lineOffsets,proto3" json:"line_offsets,omitempty"` + // A map from the parse node id (e.g. `Expr.id`) to the code point offset + // within the source. + Positions map[int64]int32 `protobuf:"bytes,4,rep,name=positions,proto3" json:"positions,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + // A map from the parse node id where a macro replacement was made to the + // call `Expr` that resulted in a macro expansion. + // + // For example, `has(value.field)` is a function call that is replaced by a + // `test_only` field selection in the AST. Likewise, the call + // `list.exists(e, e > 10)` translates to a comprehension expression. The key + // in the map corresponds to the expression id of the expanded macro, and the + // value is the call `Expr` that was replaced. + MacroCalls map[int64]*Expr `protobuf:"bytes,5,rep,name=macro_calls,json=macroCalls,proto3" json:"macro_calls,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *SourceInfo) Reset() { + *x = SourceInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SourceInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SourceInfo) ProtoMessage() {} + +func (x *SourceInfo) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SourceInfo.ProtoReflect.Descriptor instead. +func (*SourceInfo) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_syntax_proto_rawDescGZIP(), []int{3} +} + +func (x *SourceInfo) GetSyntaxVersion() string { + if x != nil { + return x.SyntaxVersion + } + return "" +} + +func (x *SourceInfo) GetLocation() string { + if x != nil { + return x.Location + } + return "" +} + +func (x *SourceInfo) GetLineOffsets() []int32 { + if x != nil { + return x.LineOffsets + } + return nil +} + +func (x *SourceInfo) GetPositions() map[int64]int32 { + if x != nil { + return x.Positions + } + return nil +} + +func (x *SourceInfo) GetMacroCalls() map[int64]*Expr { + if x != nil { + return x.MacroCalls + } + return nil +} + +// A specific position in source. +type SourcePosition struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The soucre location name (e.g. file name). + Location string `protobuf:"bytes,1,opt,name=location,proto3" json:"location,omitempty"` + // The UTF-8 code unit offset. + Offset int32 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` + // The 1-based index of the starting line in the source text + // where the issue occurs, or 0 if unknown. + Line int32 `protobuf:"varint,3,opt,name=line,proto3" json:"line,omitempty"` + // The 0-based index of the starting position within the line of source text + // where the issue occurs. Only meaningful if line is nonzero. + Column int32 `protobuf:"varint,4,opt,name=column,proto3" json:"column,omitempty"` +} + +func (x *SourcePosition) Reset() { + *x = SourcePosition{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SourcePosition) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SourcePosition) ProtoMessage() {} + +func (x *SourcePosition) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SourcePosition.ProtoReflect.Descriptor instead. +func (*SourcePosition) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_syntax_proto_rawDescGZIP(), []int{4} +} + +func (x *SourcePosition) GetLocation() string { + if x != nil { + return x.Location + } + return "" +} + +func (x *SourcePosition) GetOffset() int32 { + if x != nil { + return x.Offset + } + return 0 +} + +func (x *SourcePosition) GetLine() int32 { + if x != nil { + return x.Line + } + return 0 +} + +func (x *SourcePosition) GetColumn() int32 { + if x != nil { + return x.Column + } + return 0 +} + +// An identifier expression. e.g. `request`. +type Expr_Ident struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Required. Holds a single, unqualified identifier, possibly preceded by a + // '.'. + // + // Qualified names are represented by the [Expr.Select][google.api.expr.v1alpha1.Expr.Select] expression. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *Expr_Ident) Reset() { + *x = Expr_Ident{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Expr_Ident) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Expr_Ident) ProtoMessage() {} + +func (x *Expr_Ident) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Expr_Ident.ProtoReflect.Descriptor instead. +func (*Expr_Ident) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_syntax_proto_rawDescGZIP(), []int{1, 0} +} + +func (x *Expr_Ident) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +// A field selection expression. e.g. `request.auth`. +type Expr_Select struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Required. The target of the selection expression. + // + // For example, in the select expression `request.auth`, the `request` + // portion of the expression is the `operand`. + Operand *Expr `protobuf:"bytes,1,opt,name=operand,proto3" json:"operand,omitempty"` + // Required. The name of the field to select. + // + // For example, in the select expression `request.auth`, the `auth` portion + // of the expression would be the `field`. + Field string `protobuf:"bytes,2,opt,name=field,proto3" json:"field,omitempty"` + // Whether the select is to be interpreted as a field presence test. + // + // This results from the macro `has(request.auth)`. + TestOnly bool `protobuf:"varint,3,opt,name=test_only,json=testOnly,proto3" json:"test_only,omitempty"` +} + +func (x *Expr_Select) Reset() { + *x = Expr_Select{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Expr_Select) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Expr_Select) ProtoMessage() {} + +func (x *Expr_Select) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Expr_Select.ProtoReflect.Descriptor instead. +func (*Expr_Select) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_syntax_proto_rawDescGZIP(), []int{1, 1} +} + +func (x *Expr_Select) GetOperand() *Expr { + if x != nil { + return x.Operand + } + return nil +} + +func (x *Expr_Select) GetField() string { + if x != nil { + return x.Field + } + return "" +} + +func (x *Expr_Select) GetTestOnly() bool { + if x != nil { + return x.TestOnly + } + return false +} + +// A call expression, including calls to predefined functions and operators. +// +// For example, `value == 10`, `size(map_value)`. +type Expr_Call struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The target of an method call-style expression. For example, `x` in + // `x.f()`. + Target *Expr `protobuf:"bytes,1,opt,name=target,proto3" json:"target,omitempty"` + // Required. The name of the function or method being called. + Function string `protobuf:"bytes,2,opt,name=function,proto3" json:"function,omitempty"` + // The arguments. + Args []*Expr `protobuf:"bytes,3,rep,name=args,proto3" json:"args,omitempty"` +} + +func (x *Expr_Call) Reset() { + *x = Expr_Call{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Expr_Call) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Expr_Call) ProtoMessage() {} + +func (x *Expr_Call) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Expr_Call.ProtoReflect.Descriptor instead. +func (*Expr_Call) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_syntax_proto_rawDescGZIP(), []int{1, 2} +} + +func (x *Expr_Call) GetTarget() *Expr { + if x != nil { + return x.Target + } + return nil +} + +func (x *Expr_Call) GetFunction() string { + if x != nil { + return x.Function + } + return "" +} + +func (x *Expr_Call) GetArgs() []*Expr { + if x != nil { + return x.Args + } + return nil +} + +// A list creation expression. +// +// Lists may either be homogenous, e.g. `[1, 2, 3]`, or heterogeneous, e.g. +// `dyn([1, 'hello', 2.0])` +type Expr_CreateList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The elements part of the list. + Elements []*Expr `protobuf:"bytes,1,rep,name=elements,proto3" json:"elements,omitempty"` +} + +func (x *Expr_CreateList) Reset() { + *x = Expr_CreateList{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Expr_CreateList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Expr_CreateList) ProtoMessage() {} + +func (x *Expr_CreateList) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Expr_CreateList.ProtoReflect.Descriptor instead. +func (*Expr_CreateList) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_syntax_proto_rawDescGZIP(), []int{1, 3} +} + +func (x *Expr_CreateList) GetElements() []*Expr { + if x != nil { + return x.Elements + } + return nil +} + +// A map or message creation expression. +// +// Maps are constructed as `{'key_name': 'value'}`. Message construction is +// similar, but prefixed with a type name and composed of field ids: +// `types.MyType{field_id: 'value'}`. +type Expr_CreateStruct struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The type name of the message to be created, empty when creating map + // literals. + MessageName string `protobuf:"bytes,1,opt,name=message_name,json=messageName,proto3" json:"message_name,omitempty"` + // The entries in the creation expression. + Entries []*Expr_CreateStruct_Entry `protobuf:"bytes,2,rep,name=entries,proto3" json:"entries,omitempty"` +} + +func (x *Expr_CreateStruct) Reset() { + *x = Expr_CreateStruct{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Expr_CreateStruct) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Expr_CreateStruct) ProtoMessage() {} + +func (x *Expr_CreateStruct) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Expr_CreateStruct.ProtoReflect.Descriptor instead. +func (*Expr_CreateStruct) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_syntax_proto_rawDescGZIP(), []int{1, 4} +} + +func (x *Expr_CreateStruct) GetMessageName() string { + if x != nil { + return x.MessageName + } + return "" +} + +func (x *Expr_CreateStruct) GetEntries() []*Expr_CreateStruct_Entry { + if x != nil { + return x.Entries + } + return nil +} + +// A comprehension expression applied to a list or map. +// +// Comprehensions are not part of the core syntax, but enabled with macros. +// A macro matches a specific call signature within a parsed AST and replaces +// the call with an alternate AST block. Macro expansion happens at parse +// time. +// +// The following macros are supported within CEL: +// +// Aggregate type macros may be applied to all elements in a list or all keys +// in a map: +// +// * `all`, `exists`, `exists_one` - test a predicate expression against +// the inputs and return `true` if the predicate is satisfied for all, +// any, or only one value `list.all(x, x < 10)`. +// * `filter` - test a predicate expression against the inputs and return +// the subset of elements which satisfy the predicate: +// `payments.filter(p, p > 1000)`. +// * `map` - apply an expression to all elements in the input and return the +// output aggregate type: `[1, 2, 3].map(i, i * i)`. +// +// The `has(m.x)` macro tests whether the property `x` is present in struct +// `m`. The semantics of this macro depend on the type of `m`. For proto2 +// messages `has(m.x)` is defined as 'defined, but not set`. For proto3, the +// macro tests whether the property is set to its default. For map and struct +// types, the macro tests whether the property `x` is defined on `m`. +type Expr_Comprehension struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The name of the iteration variable. + IterVar string `protobuf:"bytes,1,opt,name=iter_var,json=iterVar,proto3" json:"iter_var,omitempty"` + // The range over which var iterates. + IterRange *Expr `protobuf:"bytes,2,opt,name=iter_range,json=iterRange,proto3" json:"iter_range,omitempty"` + // The name of the variable used for accumulation of the result. + AccuVar string `protobuf:"bytes,3,opt,name=accu_var,json=accuVar,proto3" json:"accu_var,omitempty"` + // The initial value of the accumulator. + AccuInit *Expr `protobuf:"bytes,4,opt,name=accu_init,json=accuInit,proto3" json:"accu_init,omitempty"` + // An expression which can contain iter_var and accu_var. + // + // Returns false when the result has been computed and may be used as + // a hint to short-circuit the remainder of the comprehension. + LoopCondition *Expr `protobuf:"bytes,5,opt,name=loop_condition,json=loopCondition,proto3" json:"loop_condition,omitempty"` + // An expression which can contain iter_var and accu_var. + // + // Computes the next value of accu_var. + LoopStep *Expr `protobuf:"bytes,6,opt,name=loop_step,json=loopStep,proto3" json:"loop_step,omitempty"` + // An expression which can contain accu_var. + // + // Computes the result. + Result *Expr `protobuf:"bytes,7,opt,name=result,proto3" json:"result,omitempty"` +} + +func (x *Expr_Comprehension) Reset() { + *x = Expr_Comprehension{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Expr_Comprehension) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Expr_Comprehension) ProtoMessage() {} + +func (x *Expr_Comprehension) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Expr_Comprehension.ProtoReflect.Descriptor instead. +func (*Expr_Comprehension) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_syntax_proto_rawDescGZIP(), []int{1, 5} +} + +func (x *Expr_Comprehension) GetIterVar() string { + if x != nil { + return x.IterVar + } + return "" +} + +func (x *Expr_Comprehension) GetIterRange() *Expr { + if x != nil { + return x.IterRange + } + return nil +} + +func (x *Expr_Comprehension) GetAccuVar() string { + if x != nil { + return x.AccuVar + } + return "" +} + +func (x *Expr_Comprehension) GetAccuInit() *Expr { + if x != nil { + return x.AccuInit + } + return nil +} + +func (x *Expr_Comprehension) GetLoopCondition() *Expr { + if x != nil { + return x.LoopCondition + } + return nil +} + +func (x *Expr_Comprehension) GetLoopStep() *Expr { + if x != nil { + return x.LoopStep + } + return nil +} + +func (x *Expr_Comprehension) GetResult() *Expr { + if x != nil { + return x.Result + } + return nil +} + +// Represents an entry. +type Expr_CreateStruct_Entry struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Required. An id assigned to this node by the parser which is unique + // in a given expression tree. This is used to associate type + // information and other attributes to the node. + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + // The `Entry` key kinds. + // + // Types that are assignable to KeyKind: + // *Expr_CreateStruct_Entry_FieldKey + // *Expr_CreateStruct_Entry_MapKey + KeyKind isExpr_CreateStruct_Entry_KeyKind `protobuf_oneof:"key_kind"` + // Required. The value assigned to the key. + Value *Expr `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *Expr_CreateStruct_Entry) Reset() { + *x = Expr_CreateStruct_Entry{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Expr_CreateStruct_Entry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Expr_CreateStruct_Entry) ProtoMessage() {} + +func (x *Expr_CreateStruct_Entry) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_syntax_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Expr_CreateStruct_Entry.ProtoReflect.Descriptor instead. +func (*Expr_CreateStruct_Entry) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_syntax_proto_rawDescGZIP(), []int{1, 4, 0} +} + +func (x *Expr_CreateStruct_Entry) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + +func (m *Expr_CreateStruct_Entry) GetKeyKind() isExpr_CreateStruct_Entry_KeyKind { + if m != nil { + return m.KeyKind + } + return nil +} + +func (x *Expr_CreateStruct_Entry) GetFieldKey() string { + if x, ok := x.GetKeyKind().(*Expr_CreateStruct_Entry_FieldKey); ok { + return x.FieldKey + } + return "" +} + +func (x *Expr_CreateStruct_Entry) GetMapKey() *Expr { + if x, ok := x.GetKeyKind().(*Expr_CreateStruct_Entry_MapKey); ok { + return x.MapKey + } + return nil +} + +func (x *Expr_CreateStruct_Entry) GetValue() *Expr { + if x != nil { + return x.Value + } + return nil +} + +type isExpr_CreateStruct_Entry_KeyKind interface { + isExpr_CreateStruct_Entry_KeyKind() +} + +type Expr_CreateStruct_Entry_FieldKey struct { + // The field key for a message creator statement. + FieldKey string `protobuf:"bytes,2,opt,name=field_key,json=fieldKey,proto3,oneof"` +} + +type Expr_CreateStruct_Entry_MapKey struct { + // The key expression for a map creation statement. + MapKey *Expr `protobuf:"bytes,3,opt,name=map_key,json=mapKey,proto3,oneof"` +} + +func (*Expr_CreateStruct_Entry_FieldKey) isExpr_CreateStruct_Entry_KeyKind() {} + +func (*Expr_CreateStruct_Entry_MapKey) isExpr_CreateStruct_Entry_KeyKind() {} + +var File_google_api_expr_v1alpha1_syntax_proto protoreflect.FileDescriptor + +var file_google_api_expr_v1alpha1_syntax_proto_rawDesc = []byte{ + 0x0a, 0x25, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x78, 0x70, + 0x72, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x73, 0x79, 0x6e, 0x74, 0x61, + 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x18, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0x87, 0x01, 0x0a, 0x0a, 0x50, 0x61, 0x72, 0x73, 0x65, 0x64, 0x45, 0x78, 0x70, 0x72, 0x12, + 0x32, 0x0a, 0x04, 0x65, 0x78, 0x70, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x52, 0x04, 0x65, + 0x78, 0x70, 0x72, 0x12, 0x45, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xdc, 0x0c, 0x0a, 0x04, 0x45, + 0x78, 0x70, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x65, 0x78, 0x70, + 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x09, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x45, 0x78, 0x70, 0x72, 0x12, 0x45, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x2e, 0x49, 0x64, 0x65, + 0x6e, 0x74, 0x48, 0x00, 0x52, 0x09, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x45, 0x78, 0x70, 0x72, 0x12, + 0x48, 0x0a, 0x0b, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x45, 0x78, 0x70, 0x72, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x45, 0x78, 0x70, 0x72, 0x12, 0x42, 0x0a, 0x09, 0x63, 0x61, 0x6c, + 0x6c, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x2e, 0x43, 0x61, 0x6c, + 0x6c, 0x48, 0x00, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x45, 0x78, 0x70, 0x72, 0x12, 0x48, 0x0a, + 0x09, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x29, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, + 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00, 0x52, 0x08, 0x6c, + 0x69, 0x73, 0x74, 0x45, 0x78, 0x70, 0x72, 0x12, 0x4e, 0x0a, 0x0b, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x73, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x45, 0x78, 0x70, 0x72, 0x12, 0x5d, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x72, + 0x65, 0x68, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, + 0x78, 0x70, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x68, 0x65, 0x6e, 0x73, 0x69, 0x6f, + 0x6e, 0x48, 0x00, 0x52, 0x11, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x68, 0x65, 0x6e, 0x73, 0x69, + 0x6f, 0x6e, 0x45, 0x78, 0x70, 0x72, 0x1a, 0x1b, 0x0a, 0x05, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x1a, 0x75, 0x0a, 0x06, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x12, 0x38, 0x0a, + 0x07, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x52, 0x07, + 0x6f, 0x70, 0x65, 0x72, 0x61, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x1b, 0x0a, + 0x09, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x08, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x1a, 0x8e, 0x01, 0x0a, 0x04, 0x43, + 0x61, 0x6c, 0x6c, 0x12, 0x36, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, + 0x78, 0x70, 0x72, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x45, 0x78, 0x70, 0x72, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x1a, 0x48, 0x0a, 0x0a, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x52, 0x08, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0xb4, 0x02, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x07, 0x65, 0x6e, 0x74, + 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x1a, 0xb3, 0x01, 0x0a, 0x05, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x1d, 0x0a, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4b, 0x65, 0x79, 0x12, + 0x39, 0x0a, 0x07, 0x6d, 0x61, 0x70, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, + 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, + 0x48, 0x00, 0x52, 0x06, 0x6d, 0x61, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x42, 0x0a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x1a, 0xfd, 0x02, 0x0a, + 0x0d, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x68, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x19, + 0x0a, 0x08, 0x69, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x69, 0x74, 0x65, 0x72, 0x56, 0x61, 0x72, 0x12, 0x3d, 0x0a, 0x0a, 0x69, 0x74, 0x65, + 0x72, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x52, 0x09, 0x69, + 0x74, 0x65, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x75, + 0x5f, 0x76, 0x61, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x63, 0x75, + 0x56, 0x61, 0x72, 0x12, 0x3b, 0x0a, 0x09, 0x61, 0x63, 0x63, 0x75, 0x5f, 0x69, 0x6e, 0x69, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x52, 0x08, 0x61, 0x63, 0x63, 0x75, 0x49, 0x6e, 0x69, 0x74, + 0x12, 0x45, 0x0a, 0x0e, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x52, 0x0d, 0x6c, 0x6f, 0x6f, 0x70, 0x43, 0x6f, + 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x09, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, + 0x73, 0x74, 0x65, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x52, 0x08, 0x6c, 0x6f, 0x6f, 0x70, + 0x53, 0x74, 0x65, 0x70, 0x12, 0x36, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x45, 0x78, 0x70, 0x72, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x42, 0x0b, 0x0a, 0x09, + 0x65, 0x78, 0x70, 0x72, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0xc1, 0x03, 0x0a, 0x08, 0x43, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x12, 0x3b, 0x0a, 0x0a, 0x6e, 0x75, 0x6c, 0x6c, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4e, 0x75, 0x6c, + 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x09, 0x6e, 0x75, 0x6c, 0x6c, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x21, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x0a, 0x69, 0x6e, 0x74, + 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x23, 0x0a, 0x0c, 0x75, 0x69, 0x6e, 0x74, 0x36, + 0x34, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, + 0x0b, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x23, 0x0a, 0x0c, + 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x01, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x23, 0x0a, 0x0c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x21, 0x0a, 0x0b, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0a, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x46, 0x0a, 0x0e, 0x64, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x02, 0x18, 0x01, + 0x48, 0x00, 0x52, 0x0d, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x49, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x02, 0x18, 0x01, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0f, 0x0a, 0x0d, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0xb9, 0x03, + 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x25, 0x0a, 0x0e, + 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x21, 0x0a, 0x0c, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x05, 0x52, 0x0b, 0x6c, 0x69, 0x6e, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x73, 0x12, 0x51, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x5f, 0x63, + 0x61, 0x6c, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, + 0x2e, 0x4d, 0x61, 0x63, 0x72, 0x6f, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x0a, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x1a, 0x3c, 0x0a, 0x0e, + 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5d, 0x0a, 0x0f, 0x4d, 0x61, + 0x63, 0x72, 0x6f, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x34, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x70, 0x0a, 0x0e, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x6c, + 0x69, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x42, 0x6e, 0x0a, 0x1c, 0x63, + 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, + 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x0b, 0x53, 0x79, 0x6e, + 0x74, 0x61, 0x78, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x65, + 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, + 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x78, 0x70, 0x72, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x3b, 0x65, 0x78, 0x70, 0x72, 0xf8, 0x01, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_google_api_expr_v1alpha1_syntax_proto_rawDescOnce sync.Once + file_google_api_expr_v1alpha1_syntax_proto_rawDescData = file_google_api_expr_v1alpha1_syntax_proto_rawDesc +) + +func file_google_api_expr_v1alpha1_syntax_proto_rawDescGZIP() []byte { + file_google_api_expr_v1alpha1_syntax_proto_rawDescOnce.Do(func() { + file_google_api_expr_v1alpha1_syntax_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_api_expr_v1alpha1_syntax_proto_rawDescData) + }) + return file_google_api_expr_v1alpha1_syntax_proto_rawDescData +} + +var file_google_api_expr_v1alpha1_syntax_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_google_api_expr_v1alpha1_syntax_proto_goTypes = []interface{}{ + (*ParsedExpr)(nil), // 0: google.api.expr.v1alpha1.ParsedExpr + (*Expr)(nil), // 1: google.api.expr.v1alpha1.Expr + (*Constant)(nil), // 2: google.api.expr.v1alpha1.Constant + (*SourceInfo)(nil), // 3: google.api.expr.v1alpha1.SourceInfo + (*SourcePosition)(nil), // 4: google.api.expr.v1alpha1.SourcePosition + (*Expr_Ident)(nil), // 5: google.api.expr.v1alpha1.Expr.Ident + (*Expr_Select)(nil), // 6: google.api.expr.v1alpha1.Expr.Select + (*Expr_Call)(nil), // 7: google.api.expr.v1alpha1.Expr.Call + (*Expr_CreateList)(nil), // 8: google.api.expr.v1alpha1.Expr.CreateList + (*Expr_CreateStruct)(nil), // 9: google.api.expr.v1alpha1.Expr.CreateStruct + (*Expr_Comprehension)(nil), // 10: google.api.expr.v1alpha1.Expr.Comprehension + (*Expr_CreateStruct_Entry)(nil), // 11: google.api.expr.v1alpha1.Expr.CreateStruct.Entry + nil, // 12: google.api.expr.v1alpha1.SourceInfo.PositionsEntry + nil, // 13: google.api.expr.v1alpha1.SourceInfo.MacroCallsEntry + (structpb.NullValue)(0), // 14: google.protobuf.NullValue + (*durationpb.Duration)(nil), // 15: google.protobuf.Duration + (*timestamppb.Timestamp)(nil), // 16: google.protobuf.Timestamp +} +var file_google_api_expr_v1alpha1_syntax_proto_depIdxs = []int32{ + 1, // 0: google.api.expr.v1alpha1.ParsedExpr.expr:type_name -> google.api.expr.v1alpha1.Expr + 3, // 1: google.api.expr.v1alpha1.ParsedExpr.source_info:type_name -> google.api.expr.v1alpha1.SourceInfo + 2, // 2: google.api.expr.v1alpha1.Expr.const_expr:type_name -> google.api.expr.v1alpha1.Constant + 5, // 3: google.api.expr.v1alpha1.Expr.ident_expr:type_name -> google.api.expr.v1alpha1.Expr.Ident + 6, // 4: google.api.expr.v1alpha1.Expr.select_expr:type_name -> google.api.expr.v1alpha1.Expr.Select + 7, // 5: google.api.expr.v1alpha1.Expr.call_expr:type_name -> google.api.expr.v1alpha1.Expr.Call + 8, // 6: google.api.expr.v1alpha1.Expr.list_expr:type_name -> google.api.expr.v1alpha1.Expr.CreateList + 9, // 7: google.api.expr.v1alpha1.Expr.struct_expr:type_name -> google.api.expr.v1alpha1.Expr.CreateStruct + 10, // 8: google.api.expr.v1alpha1.Expr.comprehension_expr:type_name -> google.api.expr.v1alpha1.Expr.Comprehension + 14, // 9: google.api.expr.v1alpha1.Constant.null_value:type_name -> google.protobuf.NullValue + 15, // 10: google.api.expr.v1alpha1.Constant.duration_value:type_name -> google.protobuf.Duration + 16, // 11: google.api.expr.v1alpha1.Constant.timestamp_value:type_name -> google.protobuf.Timestamp + 12, // 12: google.api.expr.v1alpha1.SourceInfo.positions:type_name -> google.api.expr.v1alpha1.SourceInfo.PositionsEntry + 13, // 13: google.api.expr.v1alpha1.SourceInfo.macro_calls:type_name -> google.api.expr.v1alpha1.SourceInfo.MacroCallsEntry + 1, // 14: google.api.expr.v1alpha1.Expr.Select.operand:type_name -> google.api.expr.v1alpha1.Expr + 1, // 15: google.api.expr.v1alpha1.Expr.Call.target:type_name -> google.api.expr.v1alpha1.Expr + 1, // 16: google.api.expr.v1alpha1.Expr.Call.args:type_name -> google.api.expr.v1alpha1.Expr + 1, // 17: google.api.expr.v1alpha1.Expr.CreateList.elements:type_name -> google.api.expr.v1alpha1.Expr + 11, // 18: google.api.expr.v1alpha1.Expr.CreateStruct.entries:type_name -> google.api.expr.v1alpha1.Expr.CreateStruct.Entry + 1, // 19: google.api.expr.v1alpha1.Expr.Comprehension.iter_range:type_name -> google.api.expr.v1alpha1.Expr + 1, // 20: google.api.expr.v1alpha1.Expr.Comprehension.accu_init:type_name -> google.api.expr.v1alpha1.Expr + 1, // 21: google.api.expr.v1alpha1.Expr.Comprehension.loop_condition:type_name -> google.api.expr.v1alpha1.Expr + 1, // 22: google.api.expr.v1alpha1.Expr.Comprehension.loop_step:type_name -> google.api.expr.v1alpha1.Expr + 1, // 23: google.api.expr.v1alpha1.Expr.Comprehension.result:type_name -> google.api.expr.v1alpha1.Expr + 1, // 24: google.api.expr.v1alpha1.Expr.CreateStruct.Entry.map_key:type_name -> google.api.expr.v1alpha1.Expr + 1, // 25: google.api.expr.v1alpha1.Expr.CreateStruct.Entry.value:type_name -> google.api.expr.v1alpha1.Expr + 1, // 26: google.api.expr.v1alpha1.SourceInfo.MacroCallsEntry.value:type_name -> google.api.expr.v1alpha1.Expr + 27, // [27:27] is the sub-list for method output_type + 27, // [27:27] is the sub-list for method input_type + 27, // [27:27] is the sub-list for extension type_name + 27, // [27:27] is the sub-list for extension extendee + 0, // [0:27] is the sub-list for field type_name +} + +func init() { file_google_api_expr_v1alpha1_syntax_proto_init() } +func file_google_api_expr_v1alpha1_syntax_proto_init() { + if File_google_api_expr_v1alpha1_syntax_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_google_api_expr_v1alpha1_syntax_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ParsedExpr); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_syntax_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Expr); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_syntax_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Constant); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_syntax_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SourceInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_syntax_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SourcePosition); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_syntax_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Expr_Ident); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_syntax_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Expr_Select); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_syntax_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Expr_Call); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_syntax_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Expr_CreateList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_syntax_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Expr_CreateStruct); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_syntax_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Expr_Comprehension); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_syntax_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Expr_CreateStruct_Entry); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_google_api_expr_v1alpha1_syntax_proto_msgTypes[1].OneofWrappers = []interface{}{ + (*Expr_ConstExpr)(nil), + (*Expr_IdentExpr)(nil), + (*Expr_SelectExpr)(nil), + (*Expr_CallExpr)(nil), + (*Expr_ListExpr)(nil), + (*Expr_StructExpr)(nil), + (*Expr_ComprehensionExpr)(nil), + } + file_google_api_expr_v1alpha1_syntax_proto_msgTypes[2].OneofWrappers = []interface{}{ + (*Constant_NullValue)(nil), + (*Constant_BoolValue)(nil), + (*Constant_Int64Value)(nil), + (*Constant_Uint64Value)(nil), + (*Constant_DoubleValue)(nil), + (*Constant_StringValue)(nil), + (*Constant_BytesValue)(nil), + (*Constant_DurationValue)(nil), + (*Constant_TimestampValue)(nil), + } + file_google_api_expr_v1alpha1_syntax_proto_msgTypes[11].OneofWrappers = []interface{}{ + (*Expr_CreateStruct_Entry_FieldKey)(nil), + (*Expr_CreateStruct_Entry_MapKey)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_google_api_expr_v1alpha1_syntax_proto_rawDesc, + NumEnums: 0, + NumMessages: 14, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_google_api_expr_v1alpha1_syntax_proto_goTypes, + DependencyIndexes: file_google_api_expr_v1alpha1_syntax_proto_depIdxs, + MessageInfos: file_google_api_expr_v1alpha1_syntax_proto_msgTypes, + }.Build() + File_google_api_expr_v1alpha1_syntax_proto = out.File + file_google_api_expr_v1alpha1_syntax_proto_rawDesc = nil + file_google_api_expr_v1alpha1_syntax_proto_goTypes = nil + file_google_api_expr_v1alpha1_syntax_proto_depIdxs = nil +} diff --git a/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/value.pb.go b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/value.pb.go new file mode 100644 index 0000000000..d41296fe96 --- /dev/null +++ b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/value.pb.go @@ -0,0 +1,720 @@ +// Copyright 2021 Google LLC +// +// 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.12.2 +// source: google/api/expr/v1alpha1/value.proto + +package expr + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" + structpb "google.golang.org/protobuf/types/known/structpb" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Represents a CEL value. +// +// This is similar to `google.protobuf.Value`, but can represent CEL's full +// range of values. +type Value struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Required. The valid kinds of values. + // + // Types that are assignable to Kind: + // *Value_NullValue + // *Value_BoolValue + // *Value_Int64Value + // *Value_Uint64Value + // *Value_DoubleValue + // *Value_StringValue + // *Value_BytesValue + // *Value_EnumValue + // *Value_ObjectValue + // *Value_MapValue + // *Value_ListValue + // *Value_TypeValue + Kind isValue_Kind `protobuf_oneof:"kind"` +} + +func (x *Value) Reset() { + *x = Value{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_value_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Value) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Value) ProtoMessage() {} + +func (x *Value) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_value_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Value.ProtoReflect.Descriptor instead. +func (*Value) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_value_proto_rawDescGZIP(), []int{0} +} + +func (m *Value) GetKind() isValue_Kind { + if m != nil { + return m.Kind + } + return nil +} + +func (x *Value) GetNullValue() structpb.NullValue { + if x, ok := x.GetKind().(*Value_NullValue); ok { + return x.NullValue + } + return structpb.NullValue_NULL_VALUE +} + +func (x *Value) GetBoolValue() bool { + if x, ok := x.GetKind().(*Value_BoolValue); ok { + return x.BoolValue + } + return false +} + +func (x *Value) GetInt64Value() int64 { + if x, ok := x.GetKind().(*Value_Int64Value); ok { + return x.Int64Value + } + return 0 +} + +func (x *Value) GetUint64Value() uint64 { + if x, ok := x.GetKind().(*Value_Uint64Value); ok { + return x.Uint64Value + } + return 0 +} + +func (x *Value) GetDoubleValue() float64 { + if x, ok := x.GetKind().(*Value_DoubleValue); ok { + return x.DoubleValue + } + return 0 +} + +func (x *Value) GetStringValue() string { + if x, ok := x.GetKind().(*Value_StringValue); ok { + return x.StringValue + } + return "" +} + +func (x *Value) GetBytesValue() []byte { + if x, ok := x.GetKind().(*Value_BytesValue); ok { + return x.BytesValue + } + return nil +} + +func (x *Value) GetEnumValue() *EnumValue { + if x, ok := x.GetKind().(*Value_EnumValue); ok { + return x.EnumValue + } + return nil +} + +func (x *Value) GetObjectValue() *anypb.Any { + if x, ok := x.GetKind().(*Value_ObjectValue); ok { + return x.ObjectValue + } + return nil +} + +func (x *Value) GetMapValue() *MapValue { + if x, ok := x.GetKind().(*Value_MapValue); ok { + return x.MapValue + } + return nil +} + +func (x *Value) GetListValue() *ListValue { + if x, ok := x.GetKind().(*Value_ListValue); ok { + return x.ListValue + } + return nil +} + +func (x *Value) GetTypeValue() string { + if x, ok := x.GetKind().(*Value_TypeValue); ok { + return x.TypeValue + } + return "" +} + +type isValue_Kind interface { + isValue_Kind() +} + +type Value_NullValue struct { + // Null value. + NullValue structpb.NullValue `protobuf:"varint,1,opt,name=null_value,json=nullValue,proto3,enum=google.protobuf.NullValue,oneof"` +} + +type Value_BoolValue struct { + // Boolean value. + BoolValue bool `protobuf:"varint,2,opt,name=bool_value,json=boolValue,proto3,oneof"` +} + +type Value_Int64Value struct { + // Signed integer value. + Int64Value int64 `protobuf:"varint,3,opt,name=int64_value,json=int64Value,proto3,oneof"` +} + +type Value_Uint64Value struct { + // Unsigned integer value. + Uint64Value uint64 `protobuf:"varint,4,opt,name=uint64_value,json=uint64Value,proto3,oneof"` +} + +type Value_DoubleValue struct { + // Floating point value. + DoubleValue float64 `protobuf:"fixed64,5,opt,name=double_value,json=doubleValue,proto3,oneof"` +} + +type Value_StringValue struct { + // UTF-8 string value. + StringValue string `protobuf:"bytes,6,opt,name=string_value,json=stringValue,proto3,oneof"` +} + +type Value_BytesValue struct { + // Byte string value. + BytesValue []byte `protobuf:"bytes,7,opt,name=bytes_value,json=bytesValue,proto3,oneof"` +} + +type Value_EnumValue struct { + // An enum value. + EnumValue *EnumValue `protobuf:"bytes,9,opt,name=enum_value,json=enumValue,proto3,oneof"` +} + +type Value_ObjectValue struct { + // The proto message backing an object value. + ObjectValue *anypb.Any `protobuf:"bytes,10,opt,name=object_value,json=objectValue,proto3,oneof"` +} + +type Value_MapValue struct { + // Map value. + MapValue *MapValue `protobuf:"bytes,11,opt,name=map_value,json=mapValue,proto3,oneof"` +} + +type Value_ListValue struct { + // List value. + ListValue *ListValue `protobuf:"bytes,12,opt,name=list_value,json=listValue,proto3,oneof"` +} + +type Value_TypeValue struct { + // Type value. + TypeValue string `protobuf:"bytes,15,opt,name=type_value,json=typeValue,proto3,oneof"` +} + +func (*Value_NullValue) isValue_Kind() {} + +func (*Value_BoolValue) isValue_Kind() {} + +func (*Value_Int64Value) isValue_Kind() {} + +func (*Value_Uint64Value) isValue_Kind() {} + +func (*Value_DoubleValue) isValue_Kind() {} + +func (*Value_StringValue) isValue_Kind() {} + +func (*Value_BytesValue) isValue_Kind() {} + +func (*Value_EnumValue) isValue_Kind() {} + +func (*Value_ObjectValue) isValue_Kind() {} + +func (*Value_MapValue) isValue_Kind() {} + +func (*Value_ListValue) isValue_Kind() {} + +func (*Value_TypeValue) isValue_Kind() {} + +// An enum value. +type EnumValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The fully qualified name of the enum type. + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + // The value of the enum. + Value int32 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *EnumValue) Reset() { + *x = EnumValue{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_value_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EnumValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EnumValue) ProtoMessage() {} + +func (x *EnumValue) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_value_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EnumValue.ProtoReflect.Descriptor instead. +func (*EnumValue) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_value_proto_rawDescGZIP(), []int{1} +} + +func (x *EnumValue) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *EnumValue) GetValue() int32 { + if x != nil { + return x.Value + } + return 0 +} + +// A list. +// +// Wrapped in a message so 'not set' and empty can be differentiated, which is +// required for use in a 'oneof'. +type ListValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The ordered values in the list. + Values []*Value `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` +} + +func (x *ListValue) Reset() { + *x = ListValue{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_value_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListValue) ProtoMessage() {} + +func (x *ListValue) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_value_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListValue.ProtoReflect.Descriptor instead. +func (*ListValue) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_value_proto_rawDescGZIP(), []int{2} +} + +func (x *ListValue) GetValues() []*Value { + if x != nil { + return x.Values + } + return nil +} + +// A map. +// +// Wrapped in a message so 'not set' and empty can be differentiated, which is +// required for use in a 'oneof'. +type MapValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The set of map entries. + // + // CEL has fewer restrictions on keys, so a protobuf map represenation + // cannot be used. + Entries []*MapValue_Entry `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"` +} + +func (x *MapValue) Reset() { + *x = MapValue{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_value_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MapValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MapValue) ProtoMessage() {} + +func (x *MapValue) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_value_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MapValue.ProtoReflect.Descriptor instead. +func (*MapValue) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_value_proto_rawDescGZIP(), []int{3} +} + +func (x *MapValue) GetEntries() []*MapValue_Entry { + if x != nil { + return x.Entries + } + return nil +} + +// An entry in the map. +type MapValue_Entry struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The key. + // + // Must be unique with in the map. + // Currently only boolean, int, uint, and string values can be keys. + Key *Value `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + // The value. + Value *Value `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *MapValue_Entry) Reset() { + *x = MapValue_Entry{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_expr_v1alpha1_value_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MapValue_Entry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MapValue_Entry) ProtoMessage() {} + +func (x *MapValue_Entry) ProtoReflect() protoreflect.Message { + mi := &file_google_api_expr_v1alpha1_value_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MapValue_Entry.ProtoReflect.Descriptor instead. +func (*MapValue_Entry) Descriptor() ([]byte, []int) { + return file_google_api_expr_v1alpha1_value_proto_rawDescGZIP(), []int{3, 0} +} + +func (x *MapValue_Entry) GetKey() *Value { + if x != nil { + return x.Key + } + return nil +} + +func (x *MapValue_Entry) GetValue() *Value { + if x != nil { + return x.Value + } + return nil +} + +var File_google_api_expr_v1alpha1_value_proto protoreflect.FileDescriptor + +var file_google_api_expr_v1alpha1_value_proto_rawDesc = []byte{ + 0x0a, 0x24, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x78, 0x70, + 0x72, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x18, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcd, 0x04, 0x0a, 0x05, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x3b, 0x0a, 0x0a, 0x6e, 0x75, 0x6c, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4e, 0x75, 0x6c, 0x6c, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x09, 0x6e, 0x75, 0x6c, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x1f, 0x0a, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x21, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x0a, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x23, 0x0a, 0x0c, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x0b, 0x75, 0x69, + 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x23, 0x0a, 0x0c, 0x64, 0x6f, 0x75, + 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x48, + 0x00, 0x52, 0x0b, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x23, + 0x0a, 0x0c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x21, 0x0a, 0x0b, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0a, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x44, 0x0a, 0x0a, 0x65, 0x6e, 0x75, 0x6d, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, + 0x00, 0x52, 0x09, 0x65, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x39, 0x0a, 0x0c, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x48, 0x00, 0x52, 0x0b, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x41, 0x0a, 0x09, 0x6d, 0x61, 0x70, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4d, 0x61, 0x70, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, + 0x52, 0x08, 0x6d, 0x61, 0x70, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x44, 0x0a, 0x0a, 0x6c, 0x69, + 0x73, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x09, 0x6c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x1f, 0x0a, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x0f, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x74, 0x79, 0x70, 0x65, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x42, 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0x35, 0x0a, 0x09, 0x45, 0x6e, 0x75, + 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x22, 0x44, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x37, 0x0a, + 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0xc1, 0x01, 0x0a, 0x08, 0x4d, 0x61, 0x70, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x42, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x4d, 0x61, 0x70, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, + 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x1a, 0x71, 0x0a, 0x05, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x31, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x6d, 0x0a, 0x1c, 0x63, 0x6f, + 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, + 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x0a, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x65, 0x78, 0x70, 0x72, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x3b, 0x65, 0x78, 0x70, 0x72, 0xf8, 0x01, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_google_api_expr_v1alpha1_value_proto_rawDescOnce sync.Once + file_google_api_expr_v1alpha1_value_proto_rawDescData = file_google_api_expr_v1alpha1_value_proto_rawDesc +) + +func file_google_api_expr_v1alpha1_value_proto_rawDescGZIP() []byte { + file_google_api_expr_v1alpha1_value_proto_rawDescOnce.Do(func() { + file_google_api_expr_v1alpha1_value_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_api_expr_v1alpha1_value_proto_rawDescData) + }) + return file_google_api_expr_v1alpha1_value_proto_rawDescData +} + +var file_google_api_expr_v1alpha1_value_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_google_api_expr_v1alpha1_value_proto_goTypes = []interface{}{ + (*Value)(nil), // 0: google.api.expr.v1alpha1.Value + (*EnumValue)(nil), // 1: google.api.expr.v1alpha1.EnumValue + (*ListValue)(nil), // 2: google.api.expr.v1alpha1.ListValue + (*MapValue)(nil), // 3: google.api.expr.v1alpha1.MapValue + (*MapValue_Entry)(nil), // 4: google.api.expr.v1alpha1.MapValue.Entry + (structpb.NullValue)(0), // 5: google.protobuf.NullValue + (*anypb.Any)(nil), // 6: google.protobuf.Any +} +var file_google_api_expr_v1alpha1_value_proto_depIdxs = []int32{ + 5, // 0: google.api.expr.v1alpha1.Value.null_value:type_name -> google.protobuf.NullValue + 1, // 1: google.api.expr.v1alpha1.Value.enum_value:type_name -> google.api.expr.v1alpha1.EnumValue + 6, // 2: google.api.expr.v1alpha1.Value.object_value:type_name -> google.protobuf.Any + 3, // 3: google.api.expr.v1alpha1.Value.map_value:type_name -> google.api.expr.v1alpha1.MapValue + 2, // 4: google.api.expr.v1alpha1.Value.list_value:type_name -> google.api.expr.v1alpha1.ListValue + 0, // 5: google.api.expr.v1alpha1.ListValue.values:type_name -> google.api.expr.v1alpha1.Value + 4, // 6: google.api.expr.v1alpha1.MapValue.entries:type_name -> google.api.expr.v1alpha1.MapValue.Entry + 0, // 7: google.api.expr.v1alpha1.MapValue.Entry.key:type_name -> google.api.expr.v1alpha1.Value + 0, // 8: google.api.expr.v1alpha1.MapValue.Entry.value:type_name -> google.api.expr.v1alpha1.Value + 9, // [9:9] is the sub-list for method output_type + 9, // [9:9] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name +} + +func init() { file_google_api_expr_v1alpha1_value_proto_init() } +func file_google_api_expr_v1alpha1_value_proto_init() { + if File_google_api_expr_v1alpha1_value_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_google_api_expr_v1alpha1_value_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Value); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_value_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EnumValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_value_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_value_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MapValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_expr_v1alpha1_value_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MapValue_Entry); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_google_api_expr_v1alpha1_value_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*Value_NullValue)(nil), + (*Value_BoolValue)(nil), + (*Value_Int64Value)(nil), + (*Value_Uint64Value)(nil), + (*Value_DoubleValue)(nil), + (*Value_StringValue)(nil), + (*Value_BytesValue)(nil), + (*Value_EnumValue)(nil), + (*Value_ObjectValue)(nil), + (*Value_MapValue)(nil), + (*Value_ListValue)(nil), + (*Value_TypeValue)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_google_api_expr_v1alpha1_value_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_google_api_expr_v1alpha1_value_proto_goTypes, + DependencyIndexes: file_google_api_expr_v1alpha1_value_proto_depIdxs, + MessageInfos: file_google_api_expr_v1alpha1_value_proto_msgTypes, + }.Build() + File_google_api_expr_v1alpha1_value_proto = out.File + file_google_api_expr_v1alpha1_value_proto_rawDesc = nil + file_google_api_expr_v1alpha1_value_proto_goTypes = nil + file_google_api_expr_v1alpha1_value_proto_depIdxs = nil +} diff --git a/vendor/google.golang.org/protobuf/types/dynamicpb/dynamic.go b/vendor/google.golang.org/protobuf/types/dynamicpb/dynamic.go new file mode 100644 index 0000000000..900b9d2874 --- /dev/null +++ b/vendor/google.golang.org/protobuf/types/dynamicpb/dynamic.go @@ -0,0 +1,713 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package dynamicpb creates protocol buffer messages using runtime type information. +package dynamicpb + +import ( + "math" + + "google.golang.org/protobuf/internal/errors" + pref "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/runtime/protoiface" + "google.golang.org/protobuf/runtime/protoimpl" +) + +// enum is a dynamic protoreflect.Enum. +type enum struct { + num pref.EnumNumber + typ pref.EnumType +} + +func (e enum) Descriptor() pref.EnumDescriptor { return e.typ.Descriptor() } +func (e enum) Type() pref.EnumType { return e.typ } +func (e enum) Number() pref.EnumNumber { return e.num } + +// enumType is a dynamic protoreflect.EnumType. +type enumType struct { + desc pref.EnumDescriptor +} + +// NewEnumType creates a new EnumType with the provided descriptor. +// +// EnumTypes created by this package are equal if their descriptors are equal. +// That is, if ed1 == ed2, then NewEnumType(ed1) == NewEnumType(ed2). +// +// Enum values created by the EnumType are equal if their numbers are equal. +func NewEnumType(desc pref.EnumDescriptor) pref.EnumType { + return enumType{desc} +} + +func (et enumType) New(n pref.EnumNumber) pref.Enum { return enum{n, et} } +func (et enumType) Descriptor() pref.EnumDescriptor { return et.desc } + +// extensionType is a dynamic protoreflect.ExtensionType. +type extensionType struct { + desc extensionTypeDescriptor +} + +// A Message is a dynamically constructed protocol buffer message. +// +// Message implements the proto.Message interface, and may be used with all +// standard proto package functions such as Marshal, Unmarshal, and so forth. +// +// Message also implements the protoreflect.Message interface. See the protoreflect +// package documentation for that interface for how to get and set fields and +// otherwise interact with the contents of a Message. +// +// Reflection API functions which construct messages, such as NewField, +// return new dynamic messages of the appropriate type. Functions which take +// messages, such as Set for a message-value field, will accept any message +// with a compatible type. +// +// Operations which modify a Message are not safe for concurrent use. +type Message struct { + typ messageType + known map[pref.FieldNumber]pref.Value + ext map[pref.FieldNumber]pref.FieldDescriptor + unknown pref.RawFields +} + +var ( + _ pref.Message = (*Message)(nil) + _ pref.ProtoMessage = (*Message)(nil) + _ protoiface.MessageV1 = (*Message)(nil) +) + +// NewMessage creates a new message with the provided descriptor. +func NewMessage(desc pref.MessageDescriptor) *Message { + return &Message{ + typ: messageType{desc}, + known: make(map[pref.FieldNumber]pref.Value), + ext: make(map[pref.FieldNumber]pref.FieldDescriptor), + } +} + +// ProtoMessage implements the legacy message interface. +func (m *Message) ProtoMessage() {} + +// ProtoReflect implements the protoreflect.ProtoMessage interface. +func (m *Message) ProtoReflect() pref.Message { + return m +} + +// String returns a string representation of a message. +func (m *Message) String() string { + return protoimpl.X.MessageStringOf(m) +} + +// Reset clears the message to be empty, but preserves the dynamic message type. +func (m *Message) Reset() { + m.known = make(map[pref.FieldNumber]pref.Value) + m.ext = make(map[pref.FieldNumber]pref.FieldDescriptor) + m.unknown = nil +} + +// Descriptor returns the message descriptor. +func (m *Message) Descriptor() pref.MessageDescriptor { + return m.typ.desc +} + +// Type returns the message type. +func (m *Message) Type() pref.MessageType { + return m.typ +} + +// New returns a newly allocated empty message with the same descriptor. +// See protoreflect.Message for details. +func (m *Message) New() pref.Message { + return m.Type().New() +} + +// Interface returns the message. +// See protoreflect.Message for details. +func (m *Message) Interface() pref.ProtoMessage { + return m +} + +// ProtoMethods is an internal detail of the protoreflect.Message interface. +// Users should never call this directly. +func (m *Message) ProtoMethods() *protoiface.Methods { + return nil +} + +// Range visits every populated field in undefined order. +// See protoreflect.Message for details. +func (m *Message) Range(f func(pref.FieldDescriptor, pref.Value) bool) { + for num, v := range m.known { + fd := m.ext[num] + if fd == nil { + fd = m.Descriptor().Fields().ByNumber(num) + } + if !isSet(fd, v) { + continue + } + if !f(fd, v) { + return + } + } +} + +// Has reports whether a field is populated. +// See protoreflect.Message for details. +func (m *Message) Has(fd pref.FieldDescriptor) bool { + m.checkField(fd) + if fd.IsExtension() && m.ext[fd.Number()] != fd { + return false + } + v, ok := m.known[fd.Number()] + if !ok { + return false + } + return isSet(fd, v) +} + +// Clear clears a field. +// See protoreflect.Message for details. +func (m *Message) Clear(fd pref.FieldDescriptor) { + m.checkField(fd) + num := fd.Number() + delete(m.known, num) + delete(m.ext, num) +} + +// Get returns the value of a field. +// See protoreflect.Message for details. +func (m *Message) Get(fd pref.FieldDescriptor) pref.Value { + m.checkField(fd) + num := fd.Number() + if fd.IsExtension() { + if fd != m.ext[num] { + return fd.(pref.ExtensionTypeDescriptor).Type().Zero() + } + return m.known[num] + } + if v, ok := m.known[num]; ok { + switch { + case fd.IsMap(): + if v.Map().Len() > 0 { + return v + } + case fd.IsList(): + if v.List().Len() > 0 { + return v + } + default: + return v + } + } + switch { + case fd.IsMap(): + return pref.ValueOfMap(&dynamicMap{desc: fd}) + case fd.IsList(): + return pref.ValueOfList(emptyList{desc: fd}) + case fd.Message() != nil: + return pref.ValueOfMessage(&Message{typ: messageType{fd.Message()}}) + case fd.Kind() == pref.BytesKind: + return pref.ValueOfBytes(append([]byte(nil), fd.Default().Bytes()...)) + default: + return fd.Default() + } +} + +// Mutable returns a mutable reference to a repeated, map, or message field. +// See protoreflect.Message for details. +func (m *Message) Mutable(fd pref.FieldDescriptor) pref.Value { + m.checkField(fd) + if !fd.IsMap() && !fd.IsList() && fd.Message() == nil { + panic(errors.New("%v: getting mutable reference to non-composite type", fd.FullName())) + } + if m.known == nil { + panic(errors.New("%v: modification of read-only message", fd.FullName())) + } + num := fd.Number() + if fd.IsExtension() { + if fd != m.ext[num] { + m.ext[num] = fd + m.known[num] = fd.(pref.ExtensionTypeDescriptor).Type().New() + } + return m.known[num] + } + if v, ok := m.known[num]; ok { + return v + } + m.clearOtherOneofFields(fd) + m.known[num] = m.NewField(fd) + if fd.IsExtension() { + m.ext[num] = fd + } + return m.known[num] +} + +// Set stores a value in a field. +// See protoreflect.Message for details. +func (m *Message) Set(fd pref.FieldDescriptor, v pref.Value) { + m.checkField(fd) + if m.known == nil { + panic(errors.New("%v: modification of read-only message", fd.FullName())) + } + if fd.IsExtension() { + isValid := true + switch { + case !fd.(pref.ExtensionTypeDescriptor).Type().IsValidValue(v): + isValid = false + case fd.IsList(): + isValid = v.List().IsValid() + case fd.IsMap(): + isValid = v.Map().IsValid() + case fd.Message() != nil: + isValid = v.Message().IsValid() + } + if !isValid { + panic(errors.New("%v: assigning invalid type %T", fd.FullName(), v.Interface())) + } + m.ext[fd.Number()] = fd + } else { + typecheck(fd, v) + } + m.clearOtherOneofFields(fd) + m.known[fd.Number()] = v +} + +func (m *Message) clearOtherOneofFields(fd pref.FieldDescriptor) { + od := fd.ContainingOneof() + if od == nil { + return + } + num := fd.Number() + for i := 0; i < od.Fields().Len(); i++ { + if n := od.Fields().Get(i).Number(); n != num { + delete(m.known, n) + } + } +} + +// NewField returns a new value for assignable to the field of a given descriptor. +// See protoreflect.Message for details. +func (m *Message) NewField(fd pref.FieldDescriptor) pref.Value { + m.checkField(fd) + switch { + case fd.IsExtension(): + return fd.(pref.ExtensionTypeDescriptor).Type().New() + case fd.IsMap(): + return pref.ValueOfMap(&dynamicMap{ + desc: fd, + mapv: make(map[interface{}]pref.Value), + }) + case fd.IsList(): + return pref.ValueOfList(&dynamicList{desc: fd}) + case fd.Message() != nil: + return pref.ValueOfMessage(NewMessage(fd.Message()).ProtoReflect()) + default: + return fd.Default() + } +} + +// WhichOneof reports which field in a oneof is populated, returning nil if none are populated. +// See protoreflect.Message for details. +func (m *Message) WhichOneof(od pref.OneofDescriptor) pref.FieldDescriptor { + for i := 0; i < od.Fields().Len(); i++ { + fd := od.Fields().Get(i) + if m.Has(fd) { + return fd + } + } + return nil +} + +// GetUnknown returns the raw unknown fields. +// See protoreflect.Message for details. +func (m *Message) GetUnknown() pref.RawFields { + return m.unknown +} + +// SetUnknown sets the raw unknown fields. +// See protoreflect.Message for details. +func (m *Message) SetUnknown(r pref.RawFields) { + if m.known == nil { + panic(errors.New("%v: modification of read-only message", m.typ.desc.FullName())) + } + m.unknown = r +} + +// IsValid reports whether the message is valid. +// See protoreflect.Message for details. +func (m *Message) IsValid() bool { + return m.known != nil +} + +func (m *Message) checkField(fd pref.FieldDescriptor) { + if fd.IsExtension() && fd.ContainingMessage().FullName() == m.Descriptor().FullName() { + if _, ok := fd.(pref.ExtensionTypeDescriptor); !ok { + panic(errors.New("%v: extension field descriptor does not implement ExtensionTypeDescriptor", fd.FullName())) + } + return + } + if fd.Parent() == m.Descriptor() { + return + } + fields := m.Descriptor().Fields() + index := fd.Index() + if index >= fields.Len() || fields.Get(index) != fd { + panic(errors.New("%v: field descriptor does not belong to this message", fd.FullName())) + } +} + +type messageType struct { + desc pref.MessageDescriptor +} + +// NewMessageType creates a new MessageType with the provided descriptor. +// +// MessageTypes created by this package are equal if their descriptors are equal. +// That is, if md1 == md2, then NewMessageType(md1) == NewMessageType(md2). +func NewMessageType(desc pref.MessageDescriptor) pref.MessageType { + return messageType{desc} +} + +func (mt messageType) New() pref.Message { return NewMessage(mt.desc) } +func (mt messageType) Zero() pref.Message { return &Message{typ: messageType{mt.desc}} } +func (mt messageType) Descriptor() pref.MessageDescriptor { return mt.desc } +func (mt messageType) Enum(i int) pref.EnumType { + if ed := mt.desc.Fields().Get(i).Enum(); ed != nil { + return NewEnumType(ed) + } + return nil +} +func (mt messageType) Message(i int) pref.MessageType { + if md := mt.desc.Fields().Get(i).Message(); md != nil { + return NewMessageType(md) + } + return nil +} + +type emptyList struct { + desc pref.FieldDescriptor +} + +func (x emptyList) Len() int { return 0 } +func (x emptyList) Get(n int) pref.Value { panic(errors.New("out of range")) } +func (x emptyList) Set(n int, v pref.Value) { panic(errors.New("modification of immutable list")) } +func (x emptyList) Append(v pref.Value) { panic(errors.New("modification of immutable list")) } +func (x emptyList) AppendMutable() pref.Value { panic(errors.New("modification of immutable list")) } +func (x emptyList) Truncate(n int) { panic(errors.New("modification of immutable list")) } +func (x emptyList) NewElement() pref.Value { return newListEntry(x.desc) } +func (x emptyList) IsValid() bool { return false } + +type dynamicList struct { + desc pref.FieldDescriptor + list []pref.Value +} + +func (x *dynamicList) Len() int { + return len(x.list) +} + +func (x *dynamicList) Get(n int) pref.Value { + return x.list[n] +} + +func (x *dynamicList) Set(n int, v pref.Value) { + typecheckSingular(x.desc, v) + x.list[n] = v +} + +func (x *dynamicList) Append(v pref.Value) { + typecheckSingular(x.desc, v) + x.list = append(x.list, v) +} + +func (x *dynamicList) AppendMutable() pref.Value { + if x.desc.Message() == nil { + panic(errors.New("%v: invalid AppendMutable on list with non-message type", x.desc.FullName())) + } + v := x.NewElement() + x.Append(v) + return v +} + +func (x *dynamicList) Truncate(n int) { + // Zero truncated elements to avoid keeping data live. + for i := n; i < len(x.list); i++ { + x.list[i] = pref.Value{} + } + x.list = x.list[:n] +} + +func (x *dynamicList) NewElement() pref.Value { + return newListEntry(x.desc) +} + +func (x *dynamicList) IsValid() bool { + return true +} + +type dynamicMap struct { + desc pref.FieldDescriptor + mapv map[interface{}]pref.Value +} + +func (x *dynamicMap) Get(k pref.MapKey) pref.Value { return x.mapv[k.Interface()] } +func (x *dynamicMap) Set(k pref.MapKey, v pref.Value) { + typecheckSingular(x.desc.MapKey(), k.Value()) + typecheckSingular(x.desc.MapValue(), v) + x.mapv[k.Interface()] = v +} +func (x *dynamicMap) Has(k pref.MapKey) bool { return x.Get(k).IsValid() } +func (x *dynamicMap) Clear(k pref.MapKey) { delete(x.mapv, k.Interface()) } +func (x *dynamicMap) Mutable(k pref.MapKey) pref.Value { + if x.desc.MapValue().Message() == nil { + panic(errors.New("%v: invalid Mutable on map with non-message value type", x.desc.FullName())) + } + v := x.Get(k) + if !v.IsValid() { + v = x.NewValue() + x.Set(k, v) + } + return v +} +func (x *dynamicMap) Len() int { return len(x.mapv) } +func (x *dynamicMap) NewValue() pref.Value { + if md := x.desc.MapValue().Message(); md != nil { + return pref.ValueOfMessage(NewMessage(md).ProtoReflect()) + } + return x.desc.MapValue().Default() +} +func (x *dynamicMap) IsValid() bool { + return x.mapv != nil +} + +func (x *dynamicMap) Range(f func(pref.MapKey, pref.Value) bool) { + for k, v := range x.mapv { + if !f(pref.ValueOf(k).MapKey(), v) { + return + } + } +} + +func isSet(fd pref.FieldDescriptor, v pref.Value) bool { + switch { + case fd.IsMap(): + return v.Map().Len() > 0 + case fd.IsList(): + return v.List().Len() > 0 + case fd.ContainingOneof() != nil: + return true + case fd.Syntax() == pref.Proto3 && !fd.IsExtension(): + switch fd.Kind() { + case pref.BoolKind: + return v.Bool() + case pref.EnumKind: + return v.Enum() != 0 + case pref.Int32Kind, pref.Sint32Kind, pref.Int64Kind, pref.Sint64Kind, pref.Sfixed32Kind, pref.Sfixed64Kind: + return v.Int() != 0 + case pref.Uint32Kind, pref.Uint64Kind, pref.Fixed32Kind, pref.Fixed64Kind: + return v.Uint() != 0 + case pref.FloatKind, pref.DoubleKind: + return v.Float() != 0 || math.Signbit(v.Float()) + case pref.StringKind: + return v.String() != "" + case pref.BytesKind: + return len(v.Bytes()) > 0 + } + } + return true +} + +func typecheck(fd pref.FieldDescriptor, v pref.Value) { + if err := typeIsValid(fd, v); err != nil { + panic(err) + } +} + +func typeIsValid(fd pref.FieldDescriptor, v pref.Value) error { + switch { + case !v.IsValid(): + return errors.New("%v: assigning invalid value", fd.FullName()) + case fd.IsMap(): + if mapv, ok := v.Interface().(*dynamicMap); !ok || mapv.desc != fd || !mapv.IsValid() { + return errors.New("%v: assigning invalid type %T", fd.FullName(), v.Interface()) + } + return nil + case fd.IsList(): + switch list := v.Interface().(type) { + case *dynamicList: + if list.desc == fd && list.IsValid() { + return nil + } + case emptyList: + if list.desc == fd && list.IsValid() { + return nil + } + } + return errors.New("%v: assigning invalid type %T", fd.FullName(), v.Interface()) + default: + return singularTypeIsValid(fd, v) + } +} + +func typecheckSingular(fd pref.FieldDescriptor, v pref.Value) { + if err := singularTypeIsValid(fd, v); err != nil { + panic(err) + } +} + +func singularTypeIsValid(fd pref.FieldDescriptor, v pref.Value) error { + vi := v.Interface() + var ok bool + switch fd.Kind() { + case pref.BoolKind: + _, ok = vi.(bool) + case pref.EnumKind: + // We could check against the valid set of enum values, but do not. + _, ok = vi.(pref.EnumNumber) + case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind: + _, ok = vi.(int32) + case pref.Uint32Kind, pref.Fixed32Kind: + _, ok = vi.(uint32) + case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind: + _, ok = vi.(int64) + case pref.Uint64Kind, pref.Fixed64Kind: + _, ok = vi.(uint64) + case pref.FloatKind: + _, ok = vi.(float32) + case pref.DoubleKind: + _, ok = vi.(float64) + case pref.StringKind: + _, ok = vi.(string) + case pref.BytesKind: + _, ok = vi.([]byte) + case pref.MessageKind, pref.GroupKind: + var m pref.Message + m, ok = vi.(pref.Message) + if ok && m.Descriptor().FullName() != fd.Message().FullName() { + return errors.New("%v: assigning invalid message type %v", fd.FullName(), m.Descriptor().FullName()) + } + if dm, ok := vi.(*Message); ok && dm.known == nil { + return errors.New("%v: assigning invalid zero-value message", fd.FullName()) + } + } + if !ok { + return errors.New("%v: assigning invalid type %T", fd.FullName(), v.Interface()) + } + return nil +} + +func newListEntry(fd pref.FieldDescriptor) pref.Value { + switch fd.Kind() { + case pref.BoolKind: + return pref.ValueOfBool(false) + case pref.EnumKind: + return pref.ValueOfEnum(fd.Enum().Values().Get(0).Number()) + case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind: + return pref.ValueOfInt32(0) + case pref.Uint32Kind, pref.Fixed32Kind: + return pref.ValueOfUint32(0) + case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind: + return pref.ValueOfInt64(0) + case pref.Uint64Kind, pref.Fixed64Kind: + return pref.ValueOfUint64(0) + case pref.FloatKind: + return pref.ValueOfFloat32(0) + case pref.DoubleKind: + return pref.ValueOfFloat64(0) + case pref.StringKind: + return pref.ValueOfString("") + case pref.BytesKind: + return pref.ValueOfBytes(nil) + case pref.MessageKind, pref.GroupKind: + return pref.ValueOfMessage(NewMessage(fd.Message()).ProtoReflect()) + } + panic(errors.New("%v: unknown kind %v", fd.FullName(), fd.Kind())) +} + +// NewExtensionType creates a new ExtensionType with the provided descriptor. +// +// Dynamic ExtensionTypes with the same descriptor compare as equal. That is, +// if xd1 == xd2, then NewExtensionType(xd1) == NewExtensionType(xd2). +// +// The InterfaceOf and ValueOf methods of the extension type are defined as: +// +// func (xt extensionType) ValueOf(iv interface{}) protoreflect.Value { +// return protoreflect.ValueOf(iv) +// } +// +// func (xt extensionType) InterfaceOf(v protoreflect.Value) interface{} { +// return v.Interface() +// } +// +// The Go type used by the proto.GetExtension and proto.SetExtension functions +// is determined by these methods, and is therefore equivalent to the Go type +// used to represent a protoreflect.Value. See the protoreflect.Value +// documentation for more details. +func NewExtensionType(desc pref.ExtensionDescriptor) pref.ExtensionType { + if xt, ok := desc.(pref.ExtensionTypeDescriptor); ok { + desc = xt.Descriptor() + } + return extensionType{extensionTypeDescriptor{desc}} +} + +func (xt extensionType) New() pref.Value { + switch { + case xt.desc.IsMap(): + return pref.ValueOfMap(&dynamicMap{ + desc: xt.desc, + mapv: make(map[interface{}]pref.Value), + }) + case xt.desc.IsList(): + return pref.ValueOfList(&dynamicList{desc: xt.desc}) + case xt.desc.Message() != nil: + return pref.ValueOfMessage(NewMessage(xt.desc.Message())) + default: + return xt.desc.Default() + } +} + +func (xt extensionType) Zero() pref.Value { + switch { + case xt.desc.IsMap(): + return pref.ValueOfMap(&dynamicMap{desc: xt.desc}) + case xt.desc.Cardinality() == pref.Repeated: + return pref.ValueOfList(emptyList{desc: xt.desc}) + case xt.desc.Message() != nil: + return pref.ValueOfMessage(&Message{typ: messageType{xt.desc.Message()}}) + default: + return xt.desc.Default() + } +} + +func (xt extensionType) TypeDescriptor() pref.ExtensionTypeDescriptor { + return xt.desc +} + +func (xt extensionType) ValueOf(iv interface{}) pref.Value { + v := pref.ValueOf(iv) + typecheck(xt.desc, v) + return v +} + +func (xt extensionType) InterfaceOf(v pref.Value) interface{} { + typecheck(xt.desc, v) + return v.Interface() +} + +func (xt extensionType) IsValidInterface(iv interface{}) bool { + return typeIsValid(xt.desc, pref.ValueOf(iv)) == nil +} + +func (xt extensionType) IsValidValue(v pref.Value) bool { + return typeIsValid(xt.desc, v) == nil +} + +type extensionTypeDescriptor struct { + pref.ExtensionDescriptor +} + +func (xt extensionTypeDescriptor) Type() pref.ExtensionType { + return extensionType{xt} +} + +func (xt extensionTypeDescriptor) Descriptor() pref.ExtensionDescriptor { + return xt.ExtensionDescriptor +} diff --git a/vendor/google.golang.org/protobuf/types/known/emptypb/empty.pb.go b/vendor/google.golang.org/protobuf/types/known/emptypb/empty.pb.go new file mode 100644 index 0000000000..e7fcea31f6 --- /dev/null +++ b/vendor/google.golang.org/protobuf/types/known/emptypb/empty.pb.go @@ -0,0 +1,168 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// 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 +// OWNER 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google/protobuf/empty.proto + +package emptypb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +// The JSON representation for `Empty` is empty JSON object `{}`. +type Empty struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Empty) Reset() { + *x = Empty{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_empty_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_empty_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_google_protobuf_empty_proto_rawDescGZIP(), []int{0} +} + +var File_google_protobuf_empty_proto protoreflect.FileDescriptor + +var file_google_protobuf_empty_proto_rawDesc = []byte{ + 0x0a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x22, 0x07, + 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x7d, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x42, 0x0a, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x6b, + 0x6e, 0x6f, 0x77, 0x6e, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x70, 0x62, 0xf8, 0x01, 0x01, 0xa2, + 0x02, 0x03, 0x47, 0x50, 0x42, 0xaa, 0x02, 0x1e, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x57, 0x65, 0x6c, 0x6c, 0x4b, 0x6e, 0x6f, 0x77, + 0x6e, 0x54, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_google_protobuf_empty_proto_rawDescOnce sync.Once + file_google_protobuf_empty_proto_rawDescData = file_google_protobuf_empty_proto_rawDesc +) + +func file_google_protobuf_empty_proto_rawDescGZIP() []byte { + file_google_protobuf_empty_proto_rawDescOnce.Do(func() { + file_google_protobuf_empty_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_protobuf_empty_proto_rawDescData) + }) + return file_google_protobuf_empty_proto_rawDescData +} + +var file_google_protobuf_empty_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_google_protobuf_empty_proto_goTypes = []interface{}{ + (*Empty)(nil), // 0: google.protobuf.Empty +} +var file_google_protobuf_empty_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_google_protobuf_empty_proto_init() } +func file_google_protobuf_empty_proto_init() { + if File_google_protobuf_empty_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_google_protobuf_empty_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Empty); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_google_protobuf_empty_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_google_protobuf_empty_proto_goTypes, + DependencyIndexes: file_google_protobuf_empty_proto_depIdxs, + MessageInfos: file_google_protobuf_empty_proto_msgTypes, + }.Build() + File_google_protobuf_empty_proto = out.File + file_google_protobuf_empty_proto_rawDesc = nil + file_google_protobuf_empty_proto_goTypes = nil + file_google_protobuf_empty_proto_depIdxs = nil +} diff --git a/vendor/google.golang.org/protobuf/types/known/structpb/struct.pb.go b/vendor/google.golang.org/protobuf/types/known/structpb/struct.pb.go new file mode 100644 index 0000000000..586690522a --- /dev/null +++ b/vendor/google.golang.org/protobuf/types/known/structpb/struct.pb.go @@ -0,0 +1,810 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// 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 +// OWNER 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google/protobuf/struct.proto + +// Package structpb contains generated types for google/protobuf/struct.proto. +// +// The messages (i.e., Value, Struct, and ListValue) defined in struct.proto are +// used to represent arbitrary JSON. The Value message represents a JSON value, +// the Struct message represents a JSON object, and the ListValue message +// represents a JSON array. See https://json.org for more information. +// +// The Value, Struct, and ListValue types have generated MarshalJSON and +// UnmarshalJSON methods such that they serialize JSON equivalent to what the +// messages themselves represent. Use of these types with the +// "google.golang.org/protobuf/encoding/protojson" package +// ensures that they will be serialized as their JSON equivalent. +// +// +// Conversion to and from a Go interface +// +// The standard Go "encoding/json" package has functionality to serialize +// arbitrary types to a large degree. The Value.AsInterface, Struct.AsMap, and +// ListValue.AsSlice methods can convert the protobuf message representation into +// a form represented by interface{}, map[string]interface{}, and []interface{}. +// This form can be used with other packages that operate on such data structures +// and also directly with the standard json package. +// +// In order to convert the interface{}, map[string]interface{}, and []interface{} +// forms back as Value, Struct, and ListValue messages, use the NewStruct, +// NewList, and NewValue constructor functions. +// +// +// Example usage +// +// Consider the following example JSON object: +// +// { +// "firstName": "John", +// "lastName": "Smith", +// "isAlive": true, +// "age": 27, +// "address": { +// "streetAddress": "21 2nd Street", +// "city": "New York", +// "state": "NY", +// "postalCode": "10021-3100" +// }, +// "phoneNumbers": [ +// { +// "type": "home", +// "number": "212 555-1234" +// }, +// { +// "type": "office", +// "number": "646 555-4567" +// } +// ], +// "children": [], +// "spouse": null +// } +// +// To construct a Value message representing the above JSON object: +// +// m, err := structpb.NewValue(map[string]interface{}{ +// "firstName": "John", +// "lastName": "Smith", +// "isAlive": true, +// "age": 27, +// "address": map[string]interface{}{ +// "streetAddress": "21 2nd Street", +// "city": "New York", +// "state": "NY", +// "postalCode": "10021-3100", +// }, +// "phoneNumbers": []interface{}{ +// map[string]interface{}{ +// "type": "home", +// "number": "212 555-1234", +// }, +// map[string]interface{}{ +// "type": "office", +// "number": "646 555-4567", +// }, +// }, +// "children": []interface{}{}, +// "spouse": nil, +// }) +// if err != nil { +// ... // handle error +// } +// ... // make use of m as a *structpb.Value +// +package structpb + +import ( + base64 "encoding/base64" + protojson "google.golang.org/protobuf/encoding/protojson" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + math "math" + reflect "reflect" + sync "sync" + utf8 "unicode/utf8" +) + +// `NullValue` is a singleton enumeration to represent the null value for the +// `Value` type union. +// +// The JSON representation for `NullValue` is JSON `null`. +type NullValue int32 + +const ( + // Null value. + NullValue_NULL_VALUE NullValue = 0 +) + +// Enum value maps for NullValue. +var ( + NullValue_name = map[int32]string{ + 0: "NULL_VALUE", + } + NullValue_value = map[string]int32{ + "NULL_VALUE": 0, + } +) + +func (x NullValue) Enum() *NullValue { + p := new(NullValue) + *p = x + return p +} + +func (x NullValue) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (NullValue) Descriptor() protoreflect.EnumDescriptor { + return file_google_protobuf_struct_proto_enumTypes[0].Descriptor() +} + +func (NullValue) Type() protoreflect.EnumType { + return &file_google_protobuf_struct_proto_enumTypes[0] +} + +func (x NullValue) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use NullValue.Descriptor instead. +func (NullValue) EnumDescriptor() ([]byte, []int) { + return file_google_protobuf_struct_proto_rawDescGZIP(), []int{0} +} + +// `Struct` represents a structured data value, consisting of fields +// which map to dynamically typed values. In some languages, `Struct` +// might be supported by a native representation. For example, in +// scripting languages like JS a struct is represented as an +// object. The details of that representation are described together +// with the proto support for the language. +// +// The JSON representation for `Struct` is JSON object. +type Struct struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Unordered map of dynamically typed values. + Fields map[string]*Value `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +// NewStruct constructs a Struct from a general-purpose Go map. +// The map keys must be valid UTF-8. +// The map values are converted using NewValue. +func NewStruct(v map[string]interface{}) (*Struct, error) { + x := &Struct{Fields: make(map[string]*Value, len(v))} + for k, v := range v { + if !utf8.ValidString(k) { + return nil, protoimpl.X.NewError("invalid UTF-8 in string: %q", k) + } + var err error + x.Fields[k], err = NewValue(v) + if err != nil { + return nil, err + } + } + return x, nil +} + +// AsMap converts x to a general-purpose Go map. +// The map values are converted by calling Value.AsInterface. +func (x *Struct) AsMap() map[string]interface{} { + vs := make(map[string]interface{}) + for k, v := range x.GetFields() { + vs[k] = v.AsInterface() + } + return vs +} + +func (x *Struct) MarshalJSON() ([]byte, error) { + return protojson.Marshal(x) +} + +func (x *Struct) UnmarshalJSON(b []byte) error { + return protojson.Unmarshal(b, x) +} + +func (x *Struct) Reset() { + *x = Struct{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_struct_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Struct) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Struct) ProtoMessage() {} + +func (x *Struct) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_struct_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Struct.ProtoReflect.Descriptor instead. +func (*Struct) Descriptor() ([]byte, []int) { + return file_google_protobuf_struct_proto_rawDescGZIP(), []int{0} +} + +func (x *Struct) GetFields() map[string]*Value { + if x != nil { + return x.Fields + } + return nil +} + +// `Value` represents a dynamically typed value which can be either +// null, a number, a string, a boolean, a recursive struct value, or a +// list of values. A producer of value is expected to set one of that +// variants, absence of any variant indicates an error. +// +// The JSON representation for `Value` is JSON value. +type Value struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The kind of value. + // + // Types that are assignable to Kind: + // *Value_NullValue + // *Value_NumberValue + // *Value_StringValue + // *Value_BoolValue + // *Value_StructValue + // *Value_ListValue + Kind isValue_Kind `protobuf_oneof:"kind"` +} + +// NewValue constructs a Value from a general-purpose Go interface. +// +// ╔════════════════════════╤════════════════════════════════════════════╗ +// ║ Go type │ Conversion ║ +// ╠════════════════════════╪════════════════════════════════════════════╣ +// ║ nil │ stored as NullValue ║ +// ║ bool │ stored as BoolValue ║ +// ║ int, int32, int64 │ stored as NumberValue ║ +// ║ uint, uint32, uint64 │ stored as NumberValue ║ +// ║ float32, float64 │ stored as NumberValue ║ +// ║ string │ stored as StringValue; must be valid UTF-8 ║ +// ║ []byte │ stored as StringValue; base64-encoded ║ +// ║ map[string]interface{} │ stored as StructValue ║ +// ║ []interface{} │ stored as ListValue ║ +// ╚════════════════════════╧════════════════════════════════════════════╝ +// +// When converting an int64 or uint64 to a NumberValue, numeric precision loss +// is possible since they are stored as a float64. +func NewValue(v interface{}) (*Value, error) { + switch v := v.(type) { + case nil: + return NewNullValue(), nil + case bool: + return NewBoolValue(v), nil + case int: + return NewNumberValue(float64(v)), nil + case int32: + return NewNumberValue(float64(v)), nil + case int64: + return NewNumberValue(float64(v)), nil + case uint: + return NewNumberValue(float64(v)), nil + case uint32: + return NewNumberValue(float64(v)), nil + case uint64: + return NewNumberValue(float64(v)), nil + case float32: + return NewNumberValue(float64(v)), nil + case float64: + return NewNumberValue(float64(v)), nil + case string: + if !utf8.ValidString(v) { + return nil, protoimpl.X.NewError("invalid UTF-8 in string: %q", v) + } + return NewStringValue(v), nil + case []byte: + s := base64.StdEncoding.EncodeToString(v) + return NewStringValue(s), nil + case map[string]interface{}: + v2, err := NewStruct(v) + if err != nil { + return nil, err + } + return NewStructValue(v2), nil + case []interface{}: + v2, err := NewList(v) + if err != nil { + return nil, err + } + return NewListValue(v2), nil + default: + return nil, protoimpl.X.NewError("invalid type: %T", v) + } +} + +// NewNullValue constructs a new null Value. +func NewNullValue() *Value { + return &Value{Kind: &Value_NullValue{NullValue: NullValue_NULL_VALUE}} +} + +// NewBoolValue constructs a new boolean Value. +func NewBoolValue(v bool) *Value { + return &Value{Kind: &Value_BoolValue{BoolValue: v}} +} + +// NewNumberValue constructs a new number Value. +func NewNumberValue(v float64) *Value { + return &Value{Kind: &Value_NumberValue{NumberValue: v}} +} + +// NewStringValue constructs a new string Value. +func NewStringValue(v string) *Value { + return &Value{Kind: &Value_StringValue{StringValue: v}} +} + +// NewStructValue constructs a new struct Value. +func NewStructValue(v *Struct) *Value { + return &Value{Kind: &Value_StructValue{StructValue: v}} +} + +// NewListValue constructs a new list Value. +func NewListValue(v *ListValue) *Value { + return &Value{Kind: &Value_ListValue{ListValue: v}} +} + +// AsInterface converts x to a general-purpose Go interface. +// +// Calling Value.MarshalJSON and "encoding/json".Marshal on this output produce +// semantically equivalent JSON (assuming no errors occur). +// +// Floating-point values (i.e., "NaN", "Infinity", and "-Infinity") are +// converted as strings to remain compatible with MarshalJSON. +func (x *Value) AsInterface() interface{} { + switch v := x.GetKind().(type) { + case *Value_NumberValue: + if v != nil { + switch { + case math.IsNaN(v.NumberValue): + return "NaN" + case math.IsInf(v.NumberValue, +1): + return "Infinity" + case math.IsInf(v.NumberValue, -1): + return "-Infinity" + default: + return v.NumberValue + } + } + case *Value_StringValue: + if v != nil { + return v.StringValue + } + case *Value_BoolValue: + if v != nil { + return v.BoolValue + } + case *Value_StructValue: + if v != nil { + return v.StructValue.AsMap() + } + case *Value_ListValue: + if v != nil { + return v.ListValue.AsSlice() + } + } + return nil +} + +func (x *Value) MarshalJSON() ([]byte, error) { + return protojson.Marshal(x) +} + +func (x *Value) UnmarshalJSON(b []byte) error { + return protojson.Unmarshal(b, x) +} + +func (x *Value) Reset() { + *x = Value{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_struct_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Value) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Value) ProtoMessage() {} + +func (x *Value) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_struct_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Value.ProtoReflect.Descriptor instead. +func (*Value) Descriptor() ([]byte, []int) { + return file_google_protobuf_struct_proto_rawDescGZIP(), []int{1} +} + +func (m *Value) GetKind() isValue_Kind { + if m != nil { + return m.Kind + } + return nil +} + +func (x *Value) GetNullValue() NullValue { + if x, ok := x.GetKind().(*Value_NullValue); ok { + return x.NullValue + } + return NullValue_NULL_VALUE +} + +func (x *Value) GetNumberValue() float64 { + if x, ok := x.GetKind().(*Value_NumberValue); ok { + return x.NumberValue + } + return 0 +} + +func (x *Value) GetStringValue() string { + if x, ok := x.GetKind().(*Value_StringValue); ok { + return x.StringValue + } + return "" +} + +func (x *Value) GetBoolValue() bool { + if x, ok := x.GetKind().(*Value_BoolValue); ok { + return x.BoolValue + } + return false +} + +func (x *Value) GetStructValue() *Struct { + if x, ok := x.GetKind().(*Value_StructValue); ok { + return x.StructValue + } + return nil +} + +func (x *Value) GetListValue() *ListValue { + if x, ok := x.GetKind().(*Value_ListValue); ok { + return x.ListValue + } + return nil +} + +type isValue_Kind interface { + isValue_Kind() +} + +type Value_NullValue struct { + // Represents a null value. + NullValue NullValue `protobuf:"varint,1,opt,name=null_value,json=nullValue,proto3,enum=google.protobuf.NullValue,oneof"` +} + +type Value_NumberValue struct { + // Represents a double value. + NumberValue float64 `protobuf:"fixed64,2,opt,name=number_value,json=numberValue,proto3,oneof"` +} + +type Value_StringValue struct { + // Represents a string value. + StringValue string `protobuf:"bytes,3,opt,name=string_value,json=stringValue,proto3,oneof"` +} + +type Value_BoolValue struct { + // Represents a boolean value. + BoolValue bool `protobuf:"varint,4,opt,name=bool_value,json=boolValue,proto3,oneof"` +} + +type Value_StructValue struct { + // Represents a structured value. + StructValue *Struct `protobuf:"bytes,5,opt,name=struct_value,json=structValue,proto3,oneof"` +} + +type Value_ListValue struct { + // Represents a repeated `Value`. + ListValue *ListValue `protobuf:"bytes,6,opt,name=list_value,json=listValue,proto3,oneof"` +} + +func (*Value_NullValue) isValue_Kind() {} + +func (*Value_NumberValue) isValue_Kind() {} + +func (*Value_StringValue) isValue_Kind() {} + +func (*Value_BoolValue) isValue_Kind() {} + +func (*Value_StructValue) isValue_Kind() {} + +func (*Value_ListValue) isValue_Kind() {} + +// `ListValue` is a wrapper around a repeated field of values. +// +// The JSON representation for `ListValue` is JSON array. +type ListValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Repeated field of dynamically typed values. + Values []*Value `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` +} + +// NewList constructs a ListValue from a general-purpose Go slice. +// The slice elements are converted using NewValue. +func NewList(v []interface{}) (*ListValue, error) { + x := &ListValue{Values: make([]*Value, len(v))} + for i, v := range v { + var err error + x.Values[i], err = NewValue(v) + if err != nil { + return nil, err + } + } + return x, nil +} + +// AsSlice converts x to a general-purpose Go slice. +// The slice elements are converted by calling Value.AsInterface. +func (x *ListValue) AsSlice() []interface{} { + vs := make([]interface{}, len(x.GetValues())) + for i, v := range x.GetValues() { + vs[i] = v.AsInterface() + } + return vs +} + +func (x *ListValue) MarshalJSON() ([]byte, error) { + return protojson.Marshal(x) +} + +func (x *ListValue) UnmarshalJSON(b []byte) error { + return protojson.Unmarshal(b, x) +} + +func (x *ListValue) Reset() { + *x = ListValue{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_struct_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListValue) ProtoMessage() {} + +func (x *ListValue) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_struct_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListValue.ProtoReflect.Descriptor instead. +func (*ListValue) Descriptor() ([]byte, []int) { + return file_google_protobuf_struct_proto_rawDescGZIP(), []int{2} +} + +func (x *ListValue) GetValues() []*Value { + if x != nil { + return x.Values + } + return nil +} + +var File_google_protobuf_struct_proto protoreflect.FileDescriptor + +var file_google_protobuf_struct_proto_rawDesc = []byte{ + 0x0a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x22, + 0x98, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x3b, 0x0a, 0x06, 0x66, 0x69, + 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x51, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb2, 0x02, 0x0a, 0x05, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3b, 0x0a, 0x0a, 0x6e, 0x75, 0x6c, 0x6c, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4e, 0x75, 0x6c, 0x6c, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x09, 0x6e, 0x75, 0x6c, 0x6c, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x23, 0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x23, 0x0a, 0x0c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, + 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x0a, 0x62, + 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, + 0x00, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3c, 0x0a, 0x0c, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x73, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3b, 0x0a, 0x0a, 0x6c, 0x69, + 0x73, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x09, 0x6c, 0x69, + 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, + 0x3b, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2e, 0x0a, 0x06, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2a, 0x1b, 0x0a, 0x09, + 0x4e, 0x75, 0x6c, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x55, 0x4c, + 0x4c, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x00, 0x42, 0x7f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x42, 0x0b, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, + 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, + 0x72, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2f, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x70, 0x62, + 0xf8, 0x01, 0x01, 0xa2, 0x02, 0x03, 0x47, 0x50, 0x42, 0xaa, 0x02, 0x1e, 0x47, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x57, 0x65, 0x6c, 0x6c, + 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_google_protobuf_struct_proto_rawDescOnce sync.Once + file_google_protobuf_struct_proto_rawDescData = file_google_protobuf_struct_proto_rawDesc +) + +func file_google_protobuf_struct_proto_rawDescGZIP() []byte { + file_google_protobuf_struct_proto_rawDescOnce.Do(func() { + file_google_protobuf_struct_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_protobuf_struct_proto_rawDescData) + }) + return file_google_protobuf_struct_proto_rawDescData +} + +var file_google_protobuf_struct_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_google_protobuf_struct_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_google_protobuf_struct_proto_goTypes = []interface{}{ + (NullValue)(0), // 0: google.protobuf.NullValue + (*Struct)(nil), // 1: google.protobuf.Struct + (*Value)(nil), // 2: google.protobuf.Value + (*ListValue)(nil), // 3: google.protobuf.ListValue + nil, // 4: google.protobuf.Struct.FieldsEntry +} +var file_google_protobuf_struct_proto_depIdxs = []int32{ + 4, // 0: google.protobuf.Struct.fields:type_name -> google.protobuf.Struct.FieldsEntry + 0, // 1: google.protobuf.Value.null_value:type_name -> google.protobuf.NullValue + 1, // 2: google.protobuf.Value.struct_value:type_name -> google.protobuf.Struct + 3, // 3: google.protobuf.Value.list_value:type_name -> google.protobuf.ListValue + 2, // 4: google.protobuf.ListValue.values:type_name -> google.protobuf.Value + 2, // 5: google.protobuf.Struct.FieldsEntry.value:type_name -> google.protobuf.Value + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_google_protobuf_struct_proto_init() } +func file_google_protobuf_struct_proto_init() { + if File_google_protobuf_struct_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_google_protobuf_struct_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Struct); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_protobuf_struct_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Value); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_protobuf_struct_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_google_protobuf_struct_proto_msgTypes[1].OneofWrappers = []interface{}{ + (*Value_NullValue)(nil), + (*Value_NumberValue)(nil), + (*Value_StringValue)(nil), + (*Value_BoolValue)(nil), + (*Value_StructValue)(nil), + (*Value_ListValue)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_google_protobuf_struct_proto_rawDesc, + NumEnums: 1, + NumMessages: 4, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_google_protobuf_struct_proto_goTypes, + DependencyIndexes: file_google_protobuf_struct_proto_depIdxs, + EnumInfos: file_google_protobuf_struct_proto_enumTypes, + MessageInfos: file_google_protobuf_struct_proto_msgTypes, + }.Build() + File_google_protobuf_struct_proto = out.File + file_google_protobuf_struct_proto_rawDesc = nil + file_google_protobuf_struct_proto_goTypes = nil + file_google_protobuf_struct_proto_depIdxs = nil +} diff --git a/vendor/gopkg.in/natefinch/npipe.v2/.gitignore b/vendor/gopkg.in/natefinch/npipe.v2/.gitignore new file mode 100644 index 0000000000..00268614f0 --- /dev/null +++ b/vendor/gopkg.in/natefinch/npipe.v2/.gitignore @@ -0,0 +1,22 @@ +# 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 diff --git a/vendor/gopkg.in/natefinch/npipe.v2/LICENSE.txt b/vendor/gopkg.in/natefinch/npipe.v2/LICENSE.txt new file mode 100644 index 0000000000..a4a11046cc --- /dev/null +++ b/vendor/gopkg.in/natefinch/npipe.v2/LICENSE.txt @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2013 npipe authors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/vendor/gopkg.in/natefinch/npipe.v2/README.md b/vendor/gopkg.in/natefinch/npipe.v2/README.md new file mode 100644 index 0000000000..420a4d16c7 --- /dev/null +++ b/vendor/gopkg.in/natefinch/npipe.v2/README.md @@ -0,0 +1,308 @@ +npipe [![Build status](https://ci.appveyor.com/api/projects/status/00vuepirsot29qwi)](https://ci.appveyor.com/project/natefinch/npipe) [![GoDoc](https://godoc.org/gopkg.in/natefinch/npipe.v2?status.svg)](https://godoc.org/gopkg.in/natefinch/npipe.v2) +===== +Package npipe provides a pure Go wrapper around Windows named pipes. + +Windows named pipe documentation: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365780 + +Note that the code lives at https://github.com/natefinch/npipe (v2 branch) +but should be imported as gopkg.in/natefinch/npipe.v2 (the package name is +still npipe). + +npipe provides an interface based on stdlib's net package, with Dial, Listen, +and Accept functions, as well as associated implementations of net.Conn and +net.Listener. It supports rpc over the connection. + +### Notes +* Deadlines for reading/writing to the connection are only functional in Windows Vista/Server 2008 and above, due to limitations with the Windows API. + +* The pipes support byte mode only (no support for message mode) + +### Examples +The Dial function connects a client to a named pipe: + + + conn, err := npipe.Dial(`\\.\pipe\mypipename`) + if err != nil { + + } + fmt.Fprintf(conn, "Hi server!\n") + msg, err := bufio.NewReader(conn).ReadString('\n') + ... + +The Listen function creates servers: + + + ln, err := npipe.Listen(`\\.\pipe\mypipename`) + if err != nil { + // handle error + } + for { + conn, err := ln.Accept() + if err != nil { + // handle error + continue + } + go handleConnection(conn) + } + + + + + +## Variables +``` go +var ErrClosed = PipeError{"Pipe has been closed.", false} +``` +ErrClosed is the error returned by PipeListener.Accept when Close is called +on the PipeListener. + + + +## type PipeAddr +``` go +type PipeAddr string +``` +PipeAddr represents the address of a named pipe. + + + + + + + + + + + +### func (PipeAddr) Network +``` go +func (a PipeAddr) Network() string +``` +Network returns the address's network name, "pipe". + + + +### func (PipeAddr) String +``` go +func (a PipeAddr) String() string +``` +String returns the address of the pipe + + + +## type PipeConn +``` go +type PipeConn struct { + // contains filtered or unexported fields +} +``` +PipeConn is the implementation of the net.Conn interface for named pipe connections. + + + + + + + + + +### func Dial +``` go +func Dial(address string) (*PipeConn, error) +``` +Dial connects to a named pipe with the given address. If the specified pipe is not available, +it will wait indefinitely for the pipe to become available. + +The address must be of the form \\.\\pipe\ for local pipes and \\\pipe\ +for remote pipes. + +Dial will return a PipeError if you pass in a badly formatted pipe name. + +Examples: + + + // local pipe + conn, err := Dial(`\\.\pipe\mypipename`) + + // remote pipe + conn, err := Dial(`\\othercomp\pipe\mypipename`) + + +### func DialTimeout +``` go +func DialTimeout(address string, timeout time.Duration) (*PipeConn, error) +``` +DialTimeout acts like Dial, but will time out after the duration of timeout + + + + +### func (\*PipeConn) Close +``` go +func (c *PipeConn) Close() error +``` +Close closes the connection. + + + +### func (\*PipeConn) LocalAddr +``` go +func (c *PipeConn) LocalAddr() net.Addr +``` +LocalAddr returns the local network address. + + + +### func (\*PipeConn) Read +``` go +func (c *PipeConn) Read(b []byte) (int, error) +``` +Read implements the net.Conn Read method. + + + +### func (\*PipeConn) RemoteAddr +``` go +func (c *PipeConn) RemoteAddr() net.Addr +``` +RemoteAddr returns the remote network address. + + + +### func (\*PipeConn) SetDeadline +``` go +func (c *PipeConn) SetDeadline(t time.Time) error +``` +SetDeadline implements the net.Conn SetDeadline method. +Note that timeouts are only supported on Windows Vista/Server 2008 and above + + + +### func (\*PipeConn) SetReadDeadline +``` go +func (c *PipeConn) SetReadDeadline(t time.Time) error +``` +SetReadDeadline implements the net.Conn SetReadDeadline method. +Note that timeouts are only supported on Windows Vista/Server 2008 and above + + + +### func (\*PipeConn) SetWriteDeadline +``` go +func (c *PipeConn) SetWriteDeadline(t time.Time) error +``` +SetWriteDeadline implements the net.Conn SetWriteDeadline method. +Note that timeouts are only supported on Windows Vista/Server 2008 and above + + + +### func (\*PipeConn) Write +``` go +func (c *PipeConn) Write(b []byte) (int, error) +``` +Write implements the net.Conn Write method. + + + +## type PipeError +``` go +type PipeError struct { + // contains filtered or unexported fields +} +``` +PipeError is an error related to a call to a pipe + + + + + + + + + + + +### func (PipeError) Error +``` go +func (e PipeError) Error() string +``` +Error implements the error interface + + + +### func (PipeError) Temporary +``` go +func (e PipeError) Temporary() bool +``` +Temporary implements net.AddrError.Temporary() + + + +### func (PipeError) Timeout +``` go +func (e PipeError) Timeout() bool +``` +Timeout implements net.AddrError.Timeout() + + + +## type PipeListener +``` go +type PipeListener struct { + // contains filtered or unexported fields +} +``` +PipeListener is a named pipe listener. Clients should typically +use variables of type net.Listener instead of assuming named pipe. + + + + + + + + + +### func Listen +``` go +func Listen(address string) (*PipeListener, error) +``` +Listen returns a new PipeListener that will listen on a pipe with the given +address. The address must be of the form \\.\pipe\ + +Listen will return a PipeError for an incorrectly formatted pipe name. + + + + +### func (\*PipeListener) Accept +``` go +func (l *PipeListener) Accept() (net.Conn, error) +``` +Accept implements the Accept method in the net.Listener interface; it +waits for the next call and returns a generic net.Conn. + + + +### func (\*PipeListener) AcceptPipe +``` go +func (l *PipeListener) AcceptPipe() (*PipeConn, error) +``` +AcceptPipe accepts the next incoming call and returns the new connection. + + + +### func (\*PipeListener) Addr +``` go +func (l *PipeListener) Addr() net.Addr +``` +Addr returns the listener's network address, a PipeAddr. + + + +### func (\*PipeListener) Close +``` go +func (l *PipeListener) Close() error +``` +Close stops listening on the address. +Already Accepted connections are not closed. diff --git a/vendor/gopkg.in/natefinch/npipe.v2/doc.go b/vendor/gopkg.in/natefinch/npipe.v2/doc.go new file mode 100644 index 0000000000..3445773b46 --- /dev/null +++ b/vendor/gopkg.in/natefinch/npipe.v2/doc.go @@ -0,0 +1,50 @@ +// Copyright 2013 Nate Finch. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// Package npipe provides a pure Go wrapper around Windows named pipes. +// +// !! Note, this package is Windows-only. There is no code to compile on linux. +// +// Windows named pipe documentation: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365780 +// +// Note that the code lives at https://github.com/natefinch/npipe (v2 branch) +// but should be imported as gopkg.in/natefinch/npipe.v2 (the package name is +// still npipe). +// +// npipe provides an interface based on stdlib's net package, with Dial, Listen, +// and Accept functions, as well as associated implementations of net.Conn and +// net.Listener. It supports rpc over the connection. +// +// Notes +// +// * Deadlines for reading/writing to the connection are only functional in Windows Vista/Server 2008 and above, due to limitations with the Windows API. +// +// * The pipes support byte mode only (no support for message mode) +// +// Examples +// +// The Dial function connects a client to a named pipe: +// conn, err := npipe.Dial(`\\.\pipe\mypipename`) +// if err != nil { +// +// } +// fmt.Fprintf(conn, "Hi server!\n") +// msg, err := bufio.NewReader(conn).ReadString('\n') +// ... +// +// The Listen function creates servers: +// +// ln, err := npipe.Listen(`\\.\pipe\mypipename`) +// if err != nil { +// // handle error +// } +// for { +// conn, err := ln.Accept() +// if err != nil { +// // handle error +// continue +// } +// go handleConnection(conn) +// } +package npipe diff --git a/vendor/gopkg.in/natefinch/npipe.v2/npipe_windows.go b/vendor/gopkg.in/natefinch/npipe.v2/npipe_windows.go new file mode 100644 index 0000000000..5e7cf13ee3 --- /dev/null +++ b/vendor/gopkg.in/natefinch/npipe.v2/npipe_windows.go @@ -0,0 +1,531 @@ +package npipe + +//sys createNamedPipe(name *uint16, openMode uint32, pipeMode uint32, maxInstances uint32, outBufSize uint32, inBufSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW +//sys connectNamedPipe(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) = ConnectNamedPipe +//sys disconnectNamedPipe(handle syscall.Handle) (err error) = DisconnectNamedPipe +//sys waitNamedPipe(name *uint16, timeout uint32) (err error) = WaitNamedPipeW +//sys createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateEventW +//sys getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, transferred *uint32, wait bool) (err error) = GetOverlappedResult +//sys cancelIoEx(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) = CancelIoEx + +import ( + "fmt" + "io" + "net" + "sync" + "syscall" + "time" +) + +const ( + // openMode + pipe_access_duplex = 0x3 + pipe_access_inbound = 0x1 + pipe_access_outbound = 0x2 + + // openMode write flags + file_flag_first_pipe_instance = 0x00080000 + file_flag_write_through = 0x80000000 + file_flag_overlapped = 0x40000000 + + // openMode ACL flags + write_dac = 0x00040000 + write_owner = 0x00080000 + access_system_security = 0x01000000 + + // pipeMode + pipe_type_byte = 0x0 + pipe_type_message = 0x4 + + // pipeMode read mode flags + pipe_readmode_byte = 0x0 + pipe_readmode_message = 0x2 + + // pipeMode wait mode flags + pipe_wait = 0x0 + pipe_nowait = 0x1 + + // pipeMode remote-client mode flags + pipe_accept_remote_clients = 0x0 + pipe_reject_remote_clients = 0x8 + + pipe_unlimited_instances = 255 + + nmpwait_wait_forever = 0xFFFFFFFF + + // the two not-an-errors below occur if a client connects to the pipe between + // the server's CreateNamedPipe and ConnectNamedPipe calls. + error_no_data syscall.Errno = 0xE8 + error_pipe_connected syscall.Errno = 0x217 + error_pipe_busy syscall.Errno = 0xE7 + error_sem_timeout syscall.Errno = 0x79 + + error_bad_pathname syscall.Errno = 0xA1 + error_invalid_name syscall.Errno = 0x7B + + error_io_incomplete syscall.Errno = 0x3e4 +) + +var _ net.Conn = (*PipeConn)(nil) +var _ net.Listener = (*PipeListener)(nil) + +// ErrClosed is the error returned by PipeListener.Accept when Close is called +// on the PipeListener. +var ErrClosed = PipeError{"Pipe has been closed.", false} + +// PipeError is an error related to a call to a pipe +type PipeError struct { + msg string + timeout bool +} + +// Error implements the error interface +func (e PipeError) Error() string { + return e.msg +} + +// Timeout implements net.AddrError.Timeout() +func (e PipeError) Timeout() bool { + return e.timeout +} + +// Temporary implements net.AddrError.Temporary() +func (e PipeError) Temporary() bool { + return false +} + +// Dial connects to a named pipe with the given address. If the specified pipe is not available, +// it will wait indefinitely for the pipe to become available. +// +// The address must be of the form \\.\\pipe\ for local pipes and \\\pipe\ +// for remote pipes. +// +// Dial will return a PipeError if you pass in a badly formatted pipe name. +// +// Examples: +// // local pipe +// conn, err := Dial(`\\.\pipe\mypipename`) +// +// // remote pipe +// conn, err := Dial(`\\othercomp\pipe\mypipename`) +func Dial(address string) (*PipeConn, error) { + for { + conn, err := dial(address, nmpwait_wait_forever) + if err == nil { + return conn, nil + } + if isPipeNotReady(err) { + <-time.After(100 * time.Millisecond) + continue + } + return nil, err + } +} + +// DialTimeout acts like Dial, but will time out after the duration of timeout +func DialTimeout(address string, timeout time.Duration) (*PipeConn, error) { + deadline := time.Now().Add(timeout) + + now := time.Now() + for now.Before(deadline) { + millis := uint32(deadline.Sub(now) / time.Millisecond) + conn, err := dial(address, millis) + if err == nil { + return conn, nil + } + if err == error_sem_timeout { + // This is WaitNamedPipe's timeout error, so we know we're done + return nil, PipeError{fmt.Sprintf( + "Timed out waiting for pipe '%s' to come available", address), true} + } + if isPipeNotReady(err) { + left := deadline.Sub(time.Now()) + retry := 100 * time.Millisecond + if left > retry { + <-time.After(retry) + } else { + <-time.After(left - time.Millisecond) + } + now = time.Now() + continue + } + return nil, err + } + return nil, PipeError{fmt.Sprintf( + "Timed out waiting for pipe '%s' to come available", address), true} +} + +// isPipeNotReady checks the error to see if it indicates the pipe is not ready +func isPipeNotReady(err error) bool { + // Pipe Busy means another client just grabbed the open pipe end, + // and the server hasn't made a new one yet. + // File Not Found means the server hasn't created the pipe yet. + // Neither is a fatal error. + + return err == syscall.ERROR_FILE_NOT_FOUND || err == error_pipe_busy +} + +// newOverlapped creates a structure used to track asynchronous +// I/O requests that have been issued. +func newOverlapped() (*syscall.Overlapped, error) { + event, err := createEvent(nil, true, true, nil) + if err != nil { + return nil, err + } + return &syscall.Overlapped{HEvent: event}, nil +} + +// waitForCompletion waits for an asynchronous I/O request referred to by overlapped to complete. +// This function returns the number of bytes transferred by the operation and an error code if +// applicable (nil otherwise). +func waitForCompletion(handle syscall.Handle, overlapped *syscall.Overlapped) (uint32, error) { + _, err := syscall.WaitForSingleObject(overlapped.HEvent, syscall.INFINITE) + if err != nil { + return 0, err + } + var transferred uint32 + err = getOverlappedResult(handle, overlapped, &transferred, true) + return transferred, err +} + +// dial is a helper to initiate a connection to a named pipe that has been started by a server. +// The timeout is only enforced if the pipe server has already created the pipe, otherwise +// this function will return immediately. +func dial(address string, timeout uint32) (*PipeConn, error) { + name, err := syscall.UTF16PtrFromString(string(address)) + if err != nil { + return nil, err + } + // If at least one instance of the pipe has been created, this function + // will wait timeout milliseconds for it to become available. + // It will return immediately regardless of timeout, if no instances + // of the named pipe have been created yet. + // If this returns with no error, there is a pipe available. + if err := waitNamedPipe(name, timeout); err != nil { + if err == error_bad_pathname { + // badly formatted pipe name + return nil, badAddr(address) + } + return nil, err + } + pathp, err := syscall.UTF16PtrFromString(address) + if err != nil { + return nil, err + } + handle, err := syscall.CreateFile(pathp, syscall.GENERIC_READ|syscall.GENERIC_WRITE, + uint32(syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE), nil, syscall.OPEN_EXISTING, + syscall.FILE_FLAG_OVERLAPPED, 0) + if err != nil { + return nil, err + } + return &PipeConn{handle: handle, addr: PipeAddr(address)}, nil +} + +// Listen returns a new PipeListener that will listen on a pipe with the given +// address. The address must be of the form \\.\pipe\ +// +// Listen will return a PipeError for an incorrectly formatted pipe name. +func Listen(address string) (*PipeListener, error) { + handle, err := createPipe(address, true) + if err == error_invalid_name { + return nil, badAddr(address) + } + if err != nil { + return nil, err + } + + return &PipeListener{ + addr: PipeAddr(address), + handle: handle, + }, nil +} + +// PipeListener is a named pipe listener. Clients should typically +// use variables of type net.Listener instead of assuming named pipe. +type PipeListener struct { + mu sync.Mutex + + addr PipeAddr + handle syscall.Handle + closed bool + + // acceptHandle contains the current handle waiting for + // an incoming connection or nil. + acceptHandle syscall.Handle + // acceptOverlapped is set before waiting on a connection. + // If not waiting, it is nil. + acceptOverlapped *syscall.Overlapped +} + +// Accept implements the Accept method in the net.Listener interface; it +// waits for the next call and returns a generic net.Conn. +func (l *PipeListener) Accept() (net.Conn, error) { + c, err := l.AcceptPipe() + for err == error_no_data { + // Ignore clients that connect and immediately disconnect. + c, err = l.AcceptPipe() + } + if err != nil { + return nil, err + } + return c, nil +} + +// AcceptPipe accepts the next incoming call and returns the new connection. +// It might return an error if a client connected and immediately cancelled +// the connection. +func (l *PipeListener) AcceptPipe() (*PipeConn, error) { + if l == nil { + return nil, syscall.EINVAL + } + + l.mu.Lock() + defer l.mu.Unlock() + + if l.addr == "" || l.closed { + return nil, syscall.EINVAL + } + + // the first time we call accept, the handle will have been created by the Listen + // call. This is to prevent race conditions where the client thinks the server + // isn't listening because it hasn't actually called create yet. After the first time, we'll + // have to create a new handle each time + handle := l.handle + if handle == 0 { + var err error + handle, err = createPipe(string(l.addr), false) + if err != nil { + return nil, err + } + } else { + l.handle = 0 + } + + overlapped, err := newOverlapped() + if err != nil { + return nil, err + } + defer syscall.CloseHandle(overlapped.HEvent) + err = connectNamedPipe(handle, overlapped) + if err == nil || err == error_pipe_connected { + return &PipeConn{handle: handle, addr: l.addr}, nil + } + + if err == error_io_incomplete || err == syscall.ERROR_IO_PENDING { + l.acceptOverlapped = overlapped + l.acceptHandle = handle + // unlock here so close can function correctly while we wait (we'll + // get relocked via the defer below, before the original defer + // unlock happens.) + l.mu.Unlock() + defer func() { + l.mu.Lock() + l.acceptOverlapped = nil + l.acceptHandle = 0 + // unlock is via defer above. + }() + _, err = waitForCompletion(handle, overlapped) + } + if err == syscall.ERROR_OPERATION_ABORTED { + // Return error compatible to net.Listener.Accept() in case the + // listener was closed. + return nil, ErrClosed + } + if err != nil { + return nil, err + } + return &PipeConn{handle: handle, addr: l.addr}, nil +} + +// Close stops listening on the address. +// Already Accepted connections are not closed. +func (l *PipeListener) Close() error { + l.mu.Lock() + defer l.mu.Unlock() + + if l.closed { + return nil + } + l.closed = true + if l.handle != 0 { + err := disconnectNamedPipe(l.handle) + if err != nil { + return err + } + err = syscall.CloseHandle(l.handle) + if err != nil { + return err + } + l.handle = 0 + } + if l.acceptOverlapped != nil && l.acceptHandle != 0 { + // Cancel the pending IO. This call does not block, so it is safe + // to hold onto the mutex above. + if err := cancelIoEx(l.acceptHandle, l.acceptOverlapped); err != nil { + return err + } + err := syscall.CloseHandle(l.acceptOverlapped.HEvent) + if err != nil { + return err + } + l.acceptOverlapped.HEvent = 0 + err = syscall.CloseHandle(l.acceptHandle) + if err != nil { + return err + } + l.acceptHandle = 0 + } + return nil +} + +// Addr returns the listener's network address, a PipeAddr. +func (l *PipeListener) Addr() net.Addr { return l.addr } + +// PipeConn is the implementation of the net.Conn interface for named pipe connections. +type PipeConn struct { + handle syscall.Handle + addr PipeAddr + + // these aren't actually used yet + readDeadline *time.Time + writeDeadline *time.Time +} + +type iodata struct { + n uint32 + err error +} + +// completeRequest looks at iodata to see if a request is pending. If so, it waits for it to either complete or to +// abort due to hitting the specified deadline. Deadline may be set to nil to wait forever. If no request is pending, +// the content of iodata is returned. +func (c *PipeConn) completeRequest(data iodata, deadline *time.Time, overlapped *syscall.Overlapped) (int, error) { + if data.err == error_io_incomplete || data.err == syscall.ERROR_IO_PENDING { + var timer <-chan time.Time + if deadline != nil { + if timeDiff := deadline.Sub(time.Now()); timeDiff > 0 { + timer = time.After(timeDiff) + } + } + done := make(chan iodata) + go func() { + n, err := waitForCompletion(c.handle, overlapped) + done <- iodata{n, err} + }() + select { + case data = <-done: + case <-timer: + syscall.CancelIoEx(c.handle, overlapped) + data = iodata{0, timeout(c.addr.String())} + } + } + // Windows will produce ERROR_BROKEN_PIPE upon closing + // a handle on the other end of a connection. Go RPC + // expects an io.EOF error in this case. + if data.err == syscall.ERROR_BROKEN_PIPE { + data.err = io.EOF + } + return int(data.n), data.err +} + +// Read implements the net.Conn Read method. +func (c *PipeConn) Read(b []byte) (int, error) { + // Use ReadFile() rather than Read() because the latter + // contains a workaround that eats ERROR_BROKEN_PIPE. + overlapped, err := newOverlapped() + if err != nil { + return 0, err + } + defer syscall.CloseHandle(overlapped.HEvent) + var n uint32 + err = syscall.ReadFile(c.handle, b, &n, overlapped) + return c.completeRequest(iodata{n, err}, c.readDeadline, overlapped) +} + +// Write implements the net.Conn Write method. +func (c *PipeConn) Write(b []byte) (int, error) { + overlapped, err := newOverlapped() + if err != nil { + return 0, err + } + defer syscall.CloseHandle(overlapped.HEvent) + var n uint32 + err = syscall.WriteFile(c.handle, b, &n, overlapped) + return c.completeRequest(iodata{n, err}, c.writeDeadline, overlapped) +} + +// Close closes the connection. +func (c *PipeConn) Close() error { + return syscall.CloseHandle(c.handle) +} + +// LocalAddr returns the local network address. +func (c *PipeConn) LocalAddr() net.Addr { + return c.addr +} + +// RemoteAddr returns the remote network address. +func (c *PipeConn) RemoteAddr() net.Addr { + // not sure what to do here, we don't have remote addr.... + return c.addr +} + +// SetDeadline implements the net.Conn SetDeadline method. +// Note that timeouts are only supported on Windows Vista/Server 2008 and above +func (c *PipeConn) SetDeadline(t time.Time) error { + c.SetReadDeadline(t) + c.SetWriteDeadline(t) + return nil +} + +// SetReadDeadline implements the net.Conn SetReadDeadline method. +// Note that timeouts are only supported on Windows Vista/Server 2008 and above +func (c *PipeConn) SetReadDeadline(t time.Time) error { + c.readDeadline = &t + return nil +} + +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +// Note that timeouts are only supported on Windows Vista/Server 2008 and above +func (c *PipeConn) SetWriteDeadline(t time.Time) error { + c.writeDeadline = &t + return nil +} + +// PipeAddr represents the address of a named pipe. +type PipeAddr string + +// Network returns the address's network name, "pipe". +func (a PipeAddr) Network() string { return "pipe" } + +// String returns the address of the pipe +func (a PipeAddr) String() string { + return string(a) +} + +// createPipe is a helper function to make sure we always create pipes +// with the same arguments, since subsequent calls to create pipe need +// to use the same arguments as the first one. If first is set, fail +// if the pipe already exists. +func createPipe(address string, first bool) (syscall.Handle, error) { + n, err := syscall.UTF16PtrFromString(address) + if err != nil { + return 0, err + } + mode := uint32(pipe_access_duplex | syscall.FILE_FLAG_OVERLAPPED) + if first { + mode |= file_flag_first_pipe_instance + } + return createNamedPipe(n, + mode, + pipe_type_byte, + pipe_unlimited_instances, + 512, 512, 0, nil) +} + +func badAddr(addr string) PipeError { + return PipeError{fmt.Sprintf("Invalid pipe address '%s'.", addr), false} +} +func timeout(addr string) PipeError { + return PipeError{fmt.Sprintf("Pipe IO timed out waiting for '%s'", addr), true} +} diff --git a/vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_386.go b/vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_386.go new file mode 100644 index 0000000000..c283e6cf95 --- /dev/null +++ b/vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_386.go @@ -0,0 +1,124 @@ +// +build windows +// go build mksyscall_windows.go && ./mksyscall_windows npipe_windows.go +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +package npipe + +import "unsafe" +import "syscall" + +var ( + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + + procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") + procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") + procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe") + procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") + procCreateEventW = modkernel32.NewProc("CreateEventW") + procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") + procCancelIoEx = modkernel32.NewProc("CancelIoEx") +) + +func createNamedPipe(name *uint16, openMode uint32, pipeMode uint32, maxInstances uint32, outBufSize uint32, inBufSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(openMode), uintptr(pipeMode), uintptr(maxInstances), uintptr(outBufSize), uintptr(inBufSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) + handle = syscall.Handle(r0) + if handle == syscall.InvalidHandle { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func cancelIoEx(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func connectNamedPipe(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func disconnectNamedPipe(handle syscall.Handle) (err error) { + r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(handle), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func waitNamedPipe(name *uint16, timeout uint32) (err error) { + r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { + var _p0 uint32 + if manualReset { + _p0 = 1 + } else { + _p0 = 0 + } + var _p1 uint32 + if initialState { + _p1 = 1 + } else { + _p1 = 0 + } + r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(sa)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) + handle = syscall.Handle(r0) + if handle == syscall.InvalidHandle { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, transferred *uint32, wait bool) (err error) { + var _p0 uint32 + if wait { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transferred)), uintptr(_p0), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} diff --git a/vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_amd64.go b/vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_amd64.go new file mode 100644 index 0000000000..c283e6cf95 --- /dev/null +++ b/vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_amd64.go @@ -0,0 +1,124 @@ +// +build windows +// go build mksyscall_windows.go && ./mksyscall_windows npipe_windows.go +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +package npipe + +import "unsafe" +import "syscall" + +var ( + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + + procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") + procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") + procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe") + procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") + procCreateEventW = modkernel32.NewProc("CreateEventW") + procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") + procCancelIoEx = modkernel32.NewProc("CancelIoEx") +) + +func createNamedPipe(name *uint16, openMode uint32, pipeMode uint32, maxInstances uint32, outBufSize uint32, inBufSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(openMode), uintptr(pipeMode), uintptr(maxInstances), uintptr(outBufSize), uintptr(inBufSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) + handle = syscall.Handle(r0) + if handle == syscall.InvalidHandle { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func cancelIoEx(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func connectNamedPipe(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func disconnectNamedPipe(handle syscall.Handle) (err error) { + r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(handle), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func waitNamedPipe(name *uint16, timeout uint32) (err error) { + r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { + var _p0 uint32 + if manualReset { + _p0 = 1 + } else { + _p0 = 0 + } + var _p1 uint32 + if initialState { + _p1 = 1 + } else { + _p1 = 0 + } + r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(sa)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) + handle = syscall.Handle(r0) + if handle == syscall.InvalidHandle { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, transferred *uint32, wait bool) (err error) { + var _p0 uint32 + if wait { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transferred)), uintptr(_p0), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} diff --git a/vendor/gopkg.in/src-d/go-billy.v4/.gitignore b/vendor/gopkg.in/src-d/go-billy.v4/.gitignore new file mode 100644 index 0000000000..7aeb46699c --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/.gitignore @@ -0,0 +1,4 @@ +/coverage.txt +/vendor +Gopkg.lock +Gopkg.toml diff --git a/vendor/gopkg.in/src-d/go-billy.v4/.travis.yml b/vendor/gopkg.in/src-d/go-billy.v4/.travis.yml new file mode 100644 index 0000000000..a70b470d4a --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/.travis.yml @@ -0,0 +1,17 @@ +language: go + +go: + - 1.9.x + - 1.10.x + +go_import_path: gopkg.in/src-d/go-billy.v4 + +install: + - go get -v -t ./... + +script: + - make test-coverage + - ./.ci/test-building-binaries-for-supported-os.sh + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/gopkg.in/src-d/go-billy.v4/DCO b/vendor/gopkg.in/src-d/go-billy.v4/DCO new file mode 100644 index 0000000000..29c1b92089 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/DCO @@ -0,0 +1,25 @@ + Developer's Certificate of Origin 1.1 + + By making a contribution to this project, I certify that: + + (a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + + (b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + + (c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + + (d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. diff --git a/vendor/gopkg.in/src-d/go-billy.v4/LICENSE b/vendor/gopkg.in/src-d/go-billy.v4/LICENSE new file mode 100644 index 0000000000..9d60756894 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/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 2017 Sourced Technologies S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/gopkg.in/src-d/go-billy.v4/MAINTAINERS b/vendor/gopkg.in/src-d/go-billy.v4/MAINTAINERS new file mode 100644 index 0000000000..8dbba477d8 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/MAINTAINERS @@ -0,0 +1 @@ +Máximo Cuadros (@mcuadros) diff --git a/vendor/gopkg.in/src-d/go-billy.v4/Makefile b/vendor/gopkg.in/src-d/go-billy.v4/Makefile new file mode 100644 index 0000000000..19e743378c --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/Makefile @@ -0,0 +1,25 @@ +# General +WORKDIR = $(PWD) + +# Go parameters +GOCMD = go +GOTEST = $(GOCMD) test -v + +# Coverage +COVERAGE_REPORT = coverage.txt +COVERAGE_PROFILE = profile.out +COVERAGE_MODE = atomic + +test-coverage: + cd $(WORKDIR); \ + echo "" > $(COVERAGE_REPORT); \ + for dir in `find . -name "*.go" | grep -o '.*/' | sort | uniq`; do \ + $(GOTEST) $$dir -coverprofile=$(COVERAGE_PROFILE) -covermode=$(COVERAGE_MODE); \ + if [ $$? != 0 ]; then \ + exit 2; \ + fi; \ + if [ -f $(COVERAGE_PROFILE) ]; then \ + cat $(COVERAGE_PROFILE) >> $(COVERAGE_REPORT); \ + rm $(COVERAGE_PROFILE); \ + fi; \ + done; \ diff --git a/vendor/gopkg.in/src-d/go-billy.v4/README.md b/vendor/gopkg.in/src-d/go-billy.v4/README.md new file mode 100644 index 0000000000..ae4a3f8691 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/README.md @@ -0,0 +1,72 @@ +# go-billy [![GoDoc](https://godoc.org/gopkg.in/src-d/go-billy.v4?status.svg)](https://godoc.org/gopkg.in/src-d/go-billy.v4) [![Build Status](https://travis-ci.com/src-d/go-billy.svg)](https://travis-ci.com/src-d/go-billy) [![Build status](https://ci.appveyor.com/api/projects/status/vx2qn6vlakbi724t?svg=true)](https://ci.appveyor.com/project/mcuadros/go-billy) [![codecov](https://codecov.io/gh/src-d/go-billy/branch/master/graph/badge.svg)](https://codecov.io/gh/src-d/go-billy) + +The missing interface filesystem abstraction for Go. +Billy implements an interface based on the `os` standard library, allowing to develop applications without dependency on the underlying storage. Makes it virtually free to implement mocks and testing over filesystem operations. + +Billy was born as part of [src-d/go-git](https://github.com/src-d/go-git) project. + +## Installation + +```go +go get -u gopkg.in/src-d/go-billy.v4/... +``` + +## Usage + +Billy exposes filesystems using the +[`Filesystem` interface](https://godoc.org/github.com/src-d/go-billy#Filesystem). +Each filesystem implementation gives you a `New` method, whose arguments depend on +the implementation itself, that returns a new `Filesystem`. + +The following example caches in memory all readable files in a directory from any +billy's filesystem implementation. + +```go +func LoadToMemory(origin billy.Filesystem, path string) (*memory.Memory, error) { + memory := memory.New() + + files, err := origin.ReadDir("/") + if err != nil { + return nil, err + } + + for _, file := range files { + if file.IsDir() { + continue + } + + src, err := origin.Open(file.Name()) + if err != nil { + return nil, err + } + + dst, err := memory.Create(file.Name()) + if err != nil { + return nil, err + } + + if _, err = io.Copy(dst, src); err != nil { + return nil, err + } + + if err := dst.Close(); err != nil { + return nil, err + } + + if err := src.Close(); err != nil { + return nil, err + } + } + + return memory, nil +} +``` + +## Why billy? + +The library billy deals with storage systems and Billy is the name of a well-known, IKEA +bookcase. That's it. + +## License + +Apache License Version 2.0, see [LICENSE](LICENSE) diff --git a/vendor/gopkg.in/src-d/go-billy.v4/appveyor.yml b/vendor/gopkg.in/src-d/go-billy.v4/appveyor.yml new file mode 100644 index 0000000000..91c0b40c0e --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/appveyor.yml @@ -0,0 +1,15 @@ +version: "{build}" +platform: x64 + +clone_folder: c:\gopath\src\gopkg.in\src-d\go-billy.v4 + +environment: + GOPATH: c:\gopath + +install: + - set PATH=%GOPATH%\bin;c:\go\bin;%PATH% + - go version + - go get -v -t ./... + +build_script: + - go test -v ./... diff --git a/vendor/gopkg.in/src-d/go-billy.v4/fs.go b/vendor/gopkg.in/src-d/go-billy.v4/fs.go new file mode 100644 index 0000000000..a9efccdeb2 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/fs.go @@ -0,0 +1,202 @@ +package billy + +import ( + "errors" + "io" + "os" + "time" +) + +var ( + ErrReadOnly = errors.New("read-only filesystem") + ErrNotSupported = errors.New("feature not supported") + ErrCrossedBoundary = errors.New("chroot boundary crossed") +) + +// Capability holds the supported features of a billy filesystem. This does +// not mean that the capability has to be supported by the underlying storage. +// For example, a billy filesystem may support WriteCapability but the +// storage be mounted in read only mode. +type Capability uint64 + +const ( + // WriteCapability means that the fs is writable. + WriteCapability Capability = 1 << iota + // ReadCapability means that the fs is readable. + ReadCapability + // ReadAndWriteCapability is the ability to open a file in read and write mode. + ReadAndWriteCapability + // SeekCapability means it is able to move position inside the file. + SeekCapability + // TruncateCapability means that a file can be truncated. + TruncateCapability + // LockCapability is the ability to lock a file. + LockCapability + + // DefaultCapabilities lists all capable features supported by filesystems + // without Capability interface. This list should not be changed until a + // major version is released. + DefaultCapabilities Capability = WriteCapability | ReadCapability | + ReadAndWriteCapability | SeekCapability | TruncateCapability | + LockCapability + + // AllCapabilities lists all capable features. + AllCapabilities Capability = WriteCapability | ReadCapability | + ReadAndWriteCapability | SeekCapability | TruncateCapability | + LockCapability +) + +// Filesystem abstract the operations in a storage-agnostic interface. +// Each method implementation mimics the behavior of the equivalent functions +// at the os package from the standard library. +type Filesystem interface { + Basic + TempFile + Dir + Symlink + Chroot +} + +// Basic abstract the basic operations in a storage-agnostic interface as +// an extension to the Basic interface. +type Basic interface { + // Create creates the named file with mode 0666 (before umask), truncating + // it if it already exists. If successful, methods on the returned File can + // be used for I/O; the associated file descriptor has mode O_RDWR. + Create(filename string) (File, error) + // Open opens the named file for reading. If successful, methods on the + // returned file can be used for reading; the associated file descriptor has + // mode O_RDONLY. + Open(filename string) (File, error) + // OpenFile is the generalized open call; most users will use Open or Create + // instead. It opens the named file with specified flag (O_RDONLY etc.) and + // perm, (0666 etc.) if applicable. If successful, methods on the returned + // File can be used for I/O. + OpenFile(filename string, flag int, perm os.FileMode) (File, error) + // Stat returns a FileInfo describing the named file. + Stat(filename string) (os.FileInfo, error) + // Rename renames (moves) oldpath to newpath. If newpath already exists and + // is not a directory, Rename replaces it. OS-specific restrictions may + // apply when oldpath and newpath are in different directories. + Rename(oldpath, newpath string) error + // Remove removes the named file or directory. + Remove(filename string) error + // Join joins any number of path elements into a single path, adding a + // Separator if necessary. Join calls filepath.Clean on the result; in + // particular, all empty strings are ignored. On Windows, the result is a + // UNC path if and only if the first path element is a UNC path. + Join(elem ...string) string +} + +type TempFile interface { + // TempFile creates a new temporary file in the directory dir with a name + // beginning with prefix, opens the file for reading and writing, and + // returns the resulting *os.File. If dir is the empty string, TempFile + // uses the default directory for temporary files (see os.TempDir). + // Multiple programs calling TempFile simultaneously will not choose the + // same file. The caller can use f.Name() to find the pathname of the file. + // It is the caller's responsibility to remove the file when no longer + // needed. + TempFile(dir, prefix string) (File, error) +} + +// Dir abstract the dir related operations in a storage-agnostic interface as +// an extension to the Basic interface. +type Dir interface { + // ReadDir reads the directory named by dirname and returns a list of + // directory entries sorted by filename. + ReadDir(path string) ([]os.FileInfo, error) + // MkdirAll creates a directory named path, along with any necessary + // parents, and returns nil, or else returns an error. The permission bits + // perm are used for all directories that MkdirAll creates. If path is/ + // already a directory, MkdirAll does nothing and returns nil. + MkdirAll(filename string, perm os.FileMode) error +} + +// Symlink abstract the symlink related operations in a storage-agnostic +// interface as an extension to the Basic interface. +type Symlink interface { + // Lstat returns a FileInfo describing the named file. If the file is a + // symbolic link, the returned FileInfo describes the symbolic link. Lstat + // makes no attempt to follow the link. + Lstat(filename string) (os.FileInfo, error) + // Symlink creates a symbolic-link from link to target. target may be an + // absolute or relative path, and need not refer to an existing node. + // Parent directories of link are created as necessary. + Symlink(target, link string) error + // Readlink returns the target path of link. + Readlink(link string) (string, error) +} + +// Change abstract the FileInfo change related operations in a storage-agnostic +// interface as an extension to the Basic interface +type Change interface { + // Chmod changes the mode of the named file to mode. If the file is a + // symbolic link, it changes the mode of the link's target. + Chmod(name string, mode os.FileMode) error + // Lchown changes the numeric uid and gid of the named file. If the file is + // a symbolic link, it changes the uid and gid of the link itself. + Lchown(name string, uid, gid int) error + // Chown changes the numeric uid and gid of the named file. If the file is a + // symbolic link, it changes the uid and gid of the link's target. + Chown(name string, uid, gid int) error + // Chtimes changes the access and modification times of the named file, + // similar to the Unix utime() or utimes() functions. + // + // The underlying filesystem may truncate or round the values to a less + // precise time unit. + Chtimes(name string, atime time.Time, mtime time.Time) error +} + +// Chroot abstract the chroot related operations in a storage-agnostic interface +// as an extension to the Basic interface. +type Chroot interface { + // Chroot returns a new filesystem from the same type where the new root is + // the given path. Files outside of the designated directory tree cannot be + // accessed. + Chroot(path string) (Filesystem, error) + // Root returns the root path of the filesystem. + Root() string +} + +// File represent a file, being a subset of the os.File +type File interface { + // Name returns the name of the file as presented to Open. + Name() string + io.Writer + io.Reader + io.ReaderAt + io.Seeker + io.Closer + // Lock locks the file like e.g. flock. It protects against access from + // other processes. + Lock() error + // Unlock unlocks the file. + Unlock() error + // Truncate the file. + Truncate(size int64) error +} + +// Capable interface can return the available features of a filesystem. +type Capable interface { + // Capabilities returns the capabilities of a filesystem in bit flags. + Capabilities() Capability +} + +// Capabilities returns the features supported by a filesystem. If the FS +// does not implement Capable interface it returns all features. +func Capabilities(fs Basic) Capability { + capable, ok := fs.(Capable) + if !ok { + return DefaultCapabilities + } + + return capable.Capabilities() +} + +// CapabilityCheck tests the filesystem for the provided capabilities and +// returns true in case it supports all of them. +func CapabilityCheck(fs Basic, capabilities Capability) bool { + fsCaps := Capabilities(fs) + return fsCaps&capabilities == capabilities +} diff --git a/vendor/gopkg.in/src-d/go-billy.v4/go.mod b/vendor/gopkg.in/src-d/go-billy.v4/go.mod new file mode 100644 index 0000000000..e5227de0ae --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/go.mod @@ -0,0 +1,8 @@ +module gopkg.in/src-d/go-billy.v4 + +require ( + github.com/kr/pretty v0.1.0 // indirect + github.com/kr/pty v1.1.8 // indirect + golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 +) diff --git a/vendor/gopkg.in/src-d/go-billy.v4/go.sum b/vendor/gopkg.in/src-d/go-billy.v4/go.sum new file mode 100644 index 0000000000..5e9ed217e9 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/go.sum @@ -0,0 +1,12 @@ +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9 h1:lkiLiLBHGoH3XnqSLUIaBsilGMUjI+Uy2Xu2JLUtTas= +golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/vendor/gopkg.in/src-d/go-billy.v4/helper/chroot/chroot.go b/vendor/gopkg.in/src-d/go-billy.v4/helper/chroot/chroot.go new file mode 100644 index 0000000000..44ddb3db53 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/helper/chroot/chroot.go @@ -0,0 +1,242 @@ +package chroot + +import ( + "os" + "path/filepath" + "strings" + + "gopkg.in/src-d/go-billy.v4" + "gopkg.in/src-d/go-billy.v4/helper/polyfill" +) + +// ChrootHelper is a helper to implement billy.Chroot. +type ChrootHelper struct { + underlying billy.Filesystem + base string +} + +// New creates a new filesystem wrapping up the given 'fs'. +// The created filesystem has its base in the given ChrootHelperectory of the +// underlying filesystem. +func New(fs billy.Basic, base string) billy.Filesystem { + return &ChrootHelper{ + underlying: polyfill.New(fs), + base: base, + } +} + +func (fs *ChrootHelper) underlyingPath(filename string) (string, error) { + if isCrossBoundaries(filename) { + return "", billy.ErrCrossedBoundary + } + + return fs.Join(fs.Root(), filename), nil +} + +func isCrossBoundaries(path string) bool { + path = filepath.ToSlash(path) + path = filepath.Clean(path) + + return strings.HasPrefix(path, ".."+string(filepath.Separator)) +} + +func (fs *ChrootHelper) Create(filename string) (billy.File, error) { + fullpath, err := fs.underlyingPath(filename) + if err != nil { + return nil, err + } + + f, err := fs.underlying.Create(fullpath) + if err != nil { + return nil, err + } + + return newFile(fs, f, filename), nil +} + +func (fs *ChrootHelper) Open(filename string) (billy.File, error) { + fullpath, err := fs.underlyingPath(filename) + if err != nil { + return nil, err + } + + f, err := fs.underlying.Open(fullpath) + if err != nil { + return nil, err + } + + return newFile(fs, f, filename), nil +} + +func (fs *ChrootHelper) OpenFile(filename string, flag int, mode os.FileMode) (billy.File, error) { + fullpath, err := fs.underlyingPath(filename) + if err != nil { + return nil, err + } + + f, err := fs.underlying.OpenFile(fullpath, flag, mode) + if err != nil { + return nil, err + } + + return newFile(fs, f, filename), nil +} + +func (fs *ChrootHelper) Stat(filename string) (os.FileInfo, error) { + fullpath, err := fs.underlyingPath(filename) + if err != nil { + return nil, err + } + + return fs.underlying.Stat(fullpath) +} + +func (fs *ChrootHelper) Rename(from, to string) error { + var err error + from, err = fs.underlyingPath(from) + if err != nil { + return err + } + + to, err = fs.underlyingPath(to) + if err != nil { + return err + } + + return fs.underlying.Rename(from, to) +} + +func (fs *ChrootHelper) Remove(path string) error { + fullpath, err := fs.underlyingPath(path) + if err != nil { + return err + } + + return fs.underlying.Remove(fullpath) +} + +func (fs *ChrootHelper) Join(elem ...string) string { + return fs.underlying.Join(elem...) +} + +func (fs *ChrootHelper) TempFile(dir, prefix string) (billy.File, error) { + fullpath, err := fs.underlyingPath(dir) + if err != nil { + return nil, err + } + + f, err := fs.underlying.(billy.TempFile).TempFile(fullpath, prefix) + if err != nil { + return nil, err + } + + return newFile(fs, f, fs.Join(dir, filepath.Base(f.Name()))), nil +} + +func (fs *ChrootHelper) ReadDir(path string) ([]os.FileInfo, error) { + fullpath, err := fs.underlyingPath(path) + if err != nil { + return nil, err + } + + return fs.underlying.(billy.Dir).ReadDir(fullpath) +} + +func (fs *ChrootHelper) MkdirAll(filename string, perm os.FileMode) error { + fullpath, err := fs.underlyingPath(filename) + if err != nil { + return err + } + + return fs.underlying.(billy.Dir).MkdirAll(fullpath, perm) +} + +func (fs *ChrootHelper) Lstat(filename string) (os.FileInfo, error) { + fullpath, err := fs.underlyingPath(filename) + if err != nil { + return nil, err + } + + return fs.underlying.(billy.Symlink).Lstat(fullpath) +} + +func (fs *ChrootHelper) Symlink(target, link string) error { + target = filepath.FromSlash(target) + + // only rewrite target if it's already absolute + if filepath.IsAbs(target) || strings.HasPrefix(target, string(filepath.Separator)) { + target = fs.Join(fs.Root(), target) + target = filepath.Clean(filepath.FromSlash(target)) + } + + link, err := fs.underlyingPath(link) + if err != nil { + return err + } + + return fs.underlying.(billy.Symlink).Symlink(target, link) +} + +func (fs *ChrootHelper) Readlink(link string) (string, error) { + fullpath, err := fs.underlyingPath(link) + if err != nil { + return "", err + } + + target, err := fs.underlying.(billy.Symlink).Readlink(fullpath) + if err != nil { + return "", err + } + + if !filepath.IsAbs(target) && !strings.HasPrefix(target, string(filepath.Separator)) { + return target, nil + } + + target, err = filepath.Rel(fs.base, target) + if err != nil { + return "", err + } + + return string(os.PathSeparator) + target, nil +} + +func (fs *ChrootHelper) Chroot(path string) (billy.Filesystem, error) { + fullpath, err := fs.underlyingPath(path) + if err != nil { + return nil, err + } + + return New(fs.underlying, fullpath), nil +} + +func (fs *ChrootHelper) Root() string { + return fs.base +} + +func (fs *ChrootHelper) Underlying() billy.Basic { + return fs.underlying +} + +// Capabilities implements the Capable interface. +func (fs *ChrootHelper) Capabilities() billy.Capability { + return billy.Capabilities(fs.underlying) +} + +type file struct { + billy.File + name string +} + +func newFile(fs billy.Filesystem, f billy.File, filename string) billy.File { + filename = fs.Join(fs.Root(), filename) + filename, _ = filepath.Rel(fs.Root(), filename) + + return &file{ + File: f, + name: filename, + } +} + +func (f *file) Name() string { + return f.name +} diff --git a/vendor/gopkg.in/src-d/go-billy.v4/helper/polyfill/polyfill.go b/vendor/gopkg.in/src-d/go-billy.v4/helper/polyfill/polyfill.go new file mode 100644 index 0000000000..f613c255d9 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/helper/polyfill/polyfill.go @@ -0,0 +1,105 @@ +package polyfill + +import ( + "os" + "path/filepath" + + "gopkg.in/src-d/go-billy.v4" +) + +// Polyfill is a helper that implements all missing method from billy.Filesystem. +type Polyfill struct { + billy.Basic + c capabilities +} + +type capabilities struct{ tempfile, dir, symlink, chroot bool } + +// New creates a new filesystem wrapping up 'fs' the intercepts all the calls +// made and errors if fs doesn't implement any of the billy interfaces. +func New(fs billy.Basic) billy.Filesystem { + if original, ok := fs.(billy.Filesystem); ok { + return original + } + + h := &Polyfill{Basic: fs} + + _, h.c.tempfile = h.Basic.(billy.TempFile) + _, h.c.dir = h.Basic.(billy.Dir) + _, h.c.symlink = h.Basic.(billy.Symlink) + _, h.c.chroot = h.Basic.(billy.Chroot) + return h +} + +func (h *Polyfill) TempFile(dir, prefix string) (billy.File, error) { + if !h.c.tempfile { + return nil, billy.ErrNotSupported + } + + return h.Basic.(billy.TempFile).TempFile(dir, prefix) +} + +func (h *Polyfill) ReadDir(path string) ([]os.FileInfo, error) { + if !h.c.dir { + return nil, billy.ErrNotSupported + } + + return h.Basic.(billy.Dir).ReadDir(path) +} + +func (h *Polyfill) MkdirAll(filename string, perm os.FileMode) error { + if !h.c.dir { + return billy.ErrNotSupported + } + + return h.Basic.(billy.Dir).MkdirAll(filename, perm) +} + +func (h *Polyfill) Symlink(target, link string) error { + if !h.c.symlink { + return billy.ErrNotSupported + } + + return h.Basic.(billy.Symlink).Symlink(target, link) +} + +func (h *Polyfill) Readlink(link string) (string, error) { + if !h.c.symlink { + return "", billy.ErrNotSupported + } + + return h.Basic.(billy.Symlink).Readlink(link) +} + +func (h *Polyfill) Lstat(path string) (os.FileInfo, error) { + if !h.c.symlink { + return nil, billy.ErrNotSupported + } + + return h.Basic.(billy.Symlink).Lstat(path) +} + +func (h *Polyfill) Chroot(path string) (billy.Filesystem, error) { + if !h.c.chroot { + return nil, billy.ErrNotSupported + } + + return h.Basic.(billy.Chroot).Chroot(path) +} + +func (h *Polyfill) Root() string { + if !h.c.chroot { + return string(filepath.Separator) + } + + return h.Basic.(billy.Chroot).Root() +} + +func (h *Polyfill) Underlying() billy.Basic { + return h.Basic +} + +// Capabilities implements the Capable interface. +func (h *Polyfill) Capabilities() billy.Capability { + return billy.Capabilities(h.Basic) +} diff --git a/vendor/gopkg.in/src-d/go-billy.v4/osfs/os.go b/vendor/gopkg.in/src-d/go-billy.v4/osfs/os.go new file mode 100644 index 0000000000..ff35a3ba96 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/osfs/os.go @@ -0,0 +1,139 @@ +// Package osfs provides a billy filesystem for the OS. +package osfs // import "gopkg.in/src-d/go-billy.v4/osfs" + +import ( + "io/ioutil" + "os" + "path/filepath" + "sync" + + "gopkg.in/src-d/go-billy.v4" + "gopkg.in/src-d/go-billy.v4/helper/chroot" +) + +const ( + defaultDirectoryMode = 0755 + defaultCreateMode = 0666 +) + +// OS is a filesystem based on the os filesystem. +type OS struct{} + +// New returns a new OS filesystem. +func New(baseDir string) billy.Filesystem { + return chroot.New(&OS{}, baseDir) +} + +func (fs *OS) Create(filename string) (billy.File, error) { + return fs.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, defaultCreateMode) +} + +func (fs *OS) OpenFile(filename string, flag int, perm os.FileMode) (billy.File, error) { + if flag&os.O_CREATE != 0 { + if err := fs.createDir(filename); err != nil { + return nil, err + } + } + + f, err := os.OpenFile(filename, flag, perm) + if err != nil { + return nil, err + } + return &file{File: f}, err +} + +func (fs *OS) createDir(fullpath string) error { + dir := filepath.Dir(fullpath) + if dir != "." { + if err := os.MkdirAll(dir, defaultDirectoryMode); err != nil { + return err + } + } + + return nil +} + +func (fs *OS) ReadDir(path string) ([]os.FileInfo, error) { + l, err := ioutil.ReadDir(path) + if err != nil { + return nil, err + } + + var s = make([]os.FileInfo, len(l)) + for i, f := range l { + s[i] = f + } + + return s, nil +} + +func (fs *OS) Rename(from, to string) error { + if err := fs.createDir(to); err != nil { + return err + } + + return os.Rename(from, to) +} + +func (fs *OS) MkdirAll(path string, perm os.FileMode) error { + return os.MkdirAll(path, defaultDirectoryMode) +} + +func (fs *OS) Open(filename string) (billy.File, error) { + return fs.OpenFile(filename, os.O_RDONLY, 0) +} + +func (fs *OS) Stat(filename string) (os.FileInfo, error) { + return os.Stat(filename) +} + +func (fs *OS) Remove(filename string) error { + return os.Remove(filename) +} + +func (fs *OS) TempFile(dir, prefix string) (billy.File, error) { + if err := fs.createDir(dir + string(os.PathSeparator)); err != nil { + return nil, err + } + + f, err := ioutil.TempFile(dir, prefix) + if err != nil { + return nil, err + } + return &file{File: f}, nil +} + +func (fs *OS) Join(elem ...string) string { + return filepath.Join(elem...) +} + +func (fs *OS) RemoveAll(path string) error { + return os.RemoveAll(filepath.Clean(path)) +} + +func (fs *OS) Lstat(filename string) (os.FileInfo, error) { + return os.Lstat(filepath.Clean(filename)) +} + +func (fs *OS) Symlink(target, link string) error { + if err := fs.createDir(link); err != nil { + return err + } + + return os.Symlink(target, link) +} + +func (fs *OS) Readlink(link string) (string, error) { + return os.Readlink(link) +} + +// Capabilities implements the Capable interface. +func (fs *OS) Capabilities() billy.Capability { + return billy.DefaultCapabilities +} + +// file is a wrapper for an os.File which adds support for file locking. +type file struct { + *os.File + m sync.Mutex +} diff --git a/vendor/gopkg.in/src-d/go-billy.v4/osfs/os_posix.go b/vendor/gopkg.in/src-d/go-billy.v4/osfs/os_posix.go new file mode 100644 index 0000000000..144cde1c18 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/osfs/os_posix.go @@ -0,0 +1,21 @@ +// +build !windows + +package osfs + +import ( + "golang.org/x/sys/unix" +) + +func (f *file) Lock() error { + f.m.Lock() + defer f.m.Unlock() + + return unix.Flock(int(f.File.Fd()), unix.LOCK_EX) +} + +func (f *file) Unlock() error { + f.m.Lock() + defer f.m.Unlock() + + return unix.Flock(int(f.File.Fd()), unix.LOCK_UN) +} diff --git a/vendor/gopkg.in/src-d/go-billy.v4/osfs/os_windows.go b/vendor/gopkg.in/src-d/go-billy.v4/osfs/os_windows.go new file mode 100644 index 0000000000..5eb98829d0 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/osfs/os_windows.go @@ -0,0 +1,57 @@ +// +build windows + +package osfs + +import ( + "os" + "runtime" + "unsafe" + + "golang.org/x/sys/windows" +) + +type fileInfo struct { + os.FileInfo + name string +} + +func (fi *fileInfo) Name() string { + return fi.name +} + +var ( + kernel32DLL = windows.NewLazySystemDLL("kernel32.dll") + lockFileExProc = kernel32DLL.NewProc("LockFileEx") + unlockFileProc = kernel32DLL.NewProc("UnlockFile") +) + +const ( + lockfileExclusiveLock = 0x2 +) + +func (f *file) Lock() error { + f.m.Lock() + defer f.m.Unlock() + + var overlapped windows.Overlapped + // err is always non-nil as per sys/windows semantics. + ret, _, err := lockFileExProc.Call(f.File.Fd(), lockfileExclusiveLock, 0, 0xFFFFFFFF, 0, + uintptr(unsafe.Pointer(&overlapped))) + runtime.KeepAlive(&overlapped) + if ret == 0 { + return err + } + return nil +} + +func (f *file) Unlock() error { + f.m.Lock() + defer f.m.Unlock() + + // err is always non-nil as per sys/windows semantics. + ret, _, err := unlockFileProc.Call(f.File.Fd(), 0, 0, 0xFFFFFFFF, 0) + if ret == 0 { + return err + } + return nil +} diff --git a/vendor/gopkg.in/src-d/go-billy.v4/util/glob.go b/vendor/gopkg.in/src-d/go-billy.v4/util/glob.go new file mode 100644 index 0000000000..fdcb3e5f0a --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/util/glob.go @@ -0,0 +1,111 @@ +package util + +import ( + "path/filepath" + "sort" + "strings" + + "gopkg.in/src-d/go-billy.v4" +) + +// Glob returns the names of all files matching pattern or nil +// if there is no matching file. The syntax of patterns is the same +// as in Match. The pattern may describe hierarchical names such as +// /usr/*/bin/ed (assuming the Separator is '/'). +// +// Glob ignores file system errors such as I/O errors reading directories. +// The only possible returned error is ErrBadPattern, when pattern +// is malformed. +// +// Function originally from https://golang.org/src/path/filepath/match_test.go +func Glob(fs billy.Filesystem, pattern string) (matches []string, err error) { + if !hasMeta(pattern) { + if _, err = fs.Lstat(pattern); err != nil { + return nil, nil + } + return []string{pattern}, nil + } + + dir, file := filepath.Split(pattern) + // Prevent infinite recursion. See issue 15879. + if dir == pattern { + return nil, filepath.ErrBadPattern + } + + var m []string + m, err = Glob(fs, cleanGlobPath(dir)) + if err != nil { + return + } + for _, d := range m { + matches, err = glob(fs, d, file, matches) + if err != nil { + return + } + } + return +} + +// cleanGlobPath prepares path for glob matching. +func cleanGlobPath(path string) string { + switch path { + case "": + return "." + case string(filepath.Separator): + // do nothing to the path + return path + default: + return path[0 : len(path)-1] // chop off trailing separator + } +} + +// glob searches for files matching pattern in the directory dir +// and appends them to matches. If the directory cannot be +// opened, it returns the existing matches. New matches are +// added in lexicographical order. +func glob(fs billy.Filesystem, dir, pattern string, matches []string) (m []string, e error) { + m = matches + fi, err := fs.Stat(dir) + if err != nil { + return + } + + if !fi.IsDir() { + return + } + + names, _ := readdirnames(fs, dir) + sort.Strings(names) + + for _, n := range names { + matched, err := filepath.Match(pattern, n) + if err != nil { + return m, err + } + if matched { + m = append(m, filepath.Join(dir, n)) + } + } + return +} + +// hasMeta reports whether path contains any of the magic characters +// recognized by Match. +func hasMeta(path string) bool { + // TODO(niemeyer): Should other magic characters be added here? + return strings.ContainsAny(path, "*?[") +} + +func readdirnames(fs billy.Filesystem, dir string) ([]string, error) { + files, err := fs.ReadDir(dir) + if err != nil { + return nil, err + } + + var names []string + for _, file := range files { + names = append(names, file.Name()) + } + + return names, nil +} diff --git a/vendor/gopkg.in/src-d/go-billy.v4/util/util.go b/vendor/gopkg.in/src-d/go-billy.v4/util/util.go new file mode 100644 index 0000000000..cf7fb57f75 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-billy.v4/util/util.go @@ -0,0 +1,224 @@ +package util + +import ( + "io" + "os" + "path/filepath" + "strconv" + "sync" + "time" + + "gopkg.in/src-d/go-billy.v4" +) + +// RemoveAll removes path and any children it contains. It removes everything it +// can but returns the first error it encounters. If the path does not exist, +// RemoveAll returns nil (no error). +func RemoveAll(fs billy.Basic, path string) error { + fs, path = getUnderlyingAndPath(fs, path) + + if r, ok := fs.(removerAll); ok { + return r.RemoveAll(path) + } + + return removeAll(fs, path) +} + +type removerAll interface { + RemoveAll(string) error +} + +func removeAll(fs billy.Basic, path string) error { + // This implementation is adapted from os.RemoveAll. + + // Simple case: if Remove works, we're done. + err := fs.Remove(path) + if err == nil || os.IsNotExist(err) { + return nil + } + + // Otherwise, is this a directory we need to recurse into? + dir, serr := fs.Stat(path) + if serr != nil { + if os.IsNotExist(serr) { + return nil + } + + return serr + } + + if !dir.IsDir() { + // Not a directory; return the error from Remove. + return err + } + + dirfs, ok := fs.(billy.Dir) + if !ok { + return billy.ErrNotSupported + } + + // Directory. + fis, err := dirfs.ReadDir(path) + if err != nil { + if os.IsNotExist(err) { + // Race. It was deleted between the Lstat and Open. + // Return nil per RemoveAll's docs. + return nil + } + + return err + } + + // Remove contents & return first error. + err = nil + for _, fi := range fis { + cpath := fs.Join(path, fi.Name()) + err1 := removeAll(fs, cpath) + if err == nil { + err = err1 + } + } + + // Remove directory. + err1 := fs.Remove(path) + if err1 == nil || os.IsNotExist(err1) { + return nil + } + + if err == nil { + err = err1 + } + + return err + +} + +// WriteFile writes data to a file named by filename in the given filesystem. +// If the file does not exist, WriteFile creates it with permissions perm; +// otherwise WriteFile truncates it before writing. +func WriteFile(fs billy.Basic, filename string, data []byte, perm os.FileMode) error { + f, err := fs.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) + if err != nil { + return err + } + + n, err := f.Write(data) + if err == nil && n < len(data) { + err = io.ErrShortWrite + } + + if err1 := f.Close(); err == nil { + err = err1 + } + + return err +} + +// Random number state. +// We generate random temporary file names so that there's a good +// chance the file doesn't exist yet - keeps the number of tries in +// TempFile to a minimum. +var rand uint32 +var randmu sync.Mutex + +func reseed() uint32 { + return uint32(time.Now().UnixNano() + int64(os.Getpid())) +} + +func nextSuffix() string { + randmu.Lock() + r := rand + if r == 0 { + r = reseed() + } + r = r*1664525 + 1013904223 // constants from Numerical Recipes + rand = r + randmu.Unlock() + return strconv.Itoa(int(1e9 + r%1e9))[1:] +} + +// TempFile creates a new temporary file in the directory dir with a name +// beginning with prefix, opens the file for reading and writing, and returns +// the resulting *os.File. If dir is the empty string, TempFile uses the default +// directory for temporary files (see os.TempDir). Multiple programs calling +// TempFile simultaneously will not choose the same file. The caller can use +// f.Name() to find the pathname of the file. It is the caller's responsibility +// to remove the file when no longer needed. +func TempFile(fs billy.Basic, dir, prefix string) (f billy.File, err error) { + // This implementation is based on stdlib ioutil.TempFile. + + if dir == "" { + dir = os.TempDir() + } + + nconflict := 0 + for i := 0; i < 10000; i++ { + name := filepath.Join(dir, prefix+nextSuffix()) + f, err = fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) + if os.IsExist(err) { + if nconflict++; nconflict > 10 { + randmu.Lock() + rand = reseed() + randmu.Unlock() + } + continue + } + break + } + return +} + +// TempDir creates a new temporary directory in the directory dir +// with a name beginning with prefix and returns the path of the +// new directory. If dir is the empty string, TempDir uses the +// default directory for temporary files (see os.TempDir). +// Multiple programs calling TempDir simultaneously +// will not choose the same directory. It is the caller's responsibility +// to remove the directory when no longer needed. +func TempDir(fs billy.Dir, dir, prefix string) (name string, err error) { + // This implementation is based on stdlib ioutil.TempDir + + if dir == "" { + dir = os.TempDir() + } + + nconflict := 0 + for i := 0; i < 10000; i++ { + try := filepath.Join(dir, prefix+nextSuffix()) + err = fs.MkdirAll(try, 0700) + if os.IsExist(err) { + if nconflict++; nconflict > 10 { + randmu.Lock() + rand = reseed() + randmu.Unlock() + } + continue + } + if os.IsNotExist(err) { + if _, err := os.Stat(dir); os.IsNotExist(err) { + return "", err + } + } + if err == nil { + name = try + } + break + } + return +} + +type underlying interface { + Underlying() billy.Basic +} + +func getUnderlyingAndPath(fs billy.Basic, path string) (billy.Basic, string) { + u, ok := fs.(underlying) + if !ok { + return fs, path + } + if ch, ok := fs.(billy.Chroot); ok { + path = fs.Join(ch.Root(), path) + } + + return u.Underlying(), path +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/.gitignore b/vendor/gopkg.in/src-d/go-git.v4/.gitignore new file mode 100644 index 0000000000..038dd9f1ed --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/.gitignore @@ -0,0 +1,4 @@ +coverage.out +*~ +coverage.txt +profile.out diff --git a/vendor/gopkg.in/src-d/go-git.v4/.travis.yml b/vendor/gopkg.in/src-d/go-git.v4/.travis.yml new file mode 100644 index 0000000000..3a65f3e082 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/.travis.yml @@ -0,0 +1,37 @@ +language: go + +go: + - "1.11" + - "1.12" + +go_import_path: gopkg.in/src-d/go-git.v4 + +env: + - GIT_VERSION=master + - GIT_VERSION=v1.9.3 + - GIT_VERSION=v2.11.0 + +cache: + directories: + - $HOME/.git-dist + +before_script: + - export GIT_DIST_PATH=$HOME/.git-dist + - make build-git + +before_install: + - git config --global user.email "travis@example.com" + - git config --global user.name "Travis CI" + +install: + - go get -v -t ./... + +script: + - export GIT_EXEC_PATH=$GIT_DIST_PATH + - export PATH=$GIT_DIST_PATH:$PATH + - git version + - make test-coverage + - go vet ./... + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/gopkg.in/src-d/go-git.v4/CODE_OF_CONDUCT.md b/vendor/gopkg.in/src-d/go-git.v4/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..a689fa3c34 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, race, +religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at conduct@sourced.tech. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + diff --git a/vendor/gopkg.in/src-d/go-git.v4/COMPATIBILITY.md b/vendor/gopkg.in/src-d/go-git.v4/COMPATIBILITY.md new file mode 100644 index 0000000000..4a3da62fcd --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/COMPATIBILITY.md @@ -0,0 +1,111 @@ +Supported Capabilities +====================== + +Here is a non-comprehensive table of git commands and features whose equivalent +is supported by go-git. + +| Feature | Status | Notes | +|---------------------------------------|--------|-------| +| **config** | +| config | ✔ | Reading and modifying per-repository configuration (`.git/config`) is supported. Global configuration (`$HOME/.gitconfig`) is not. | +| **getting and creating repositories** | +| init | ✔ | Plain init and `--bare` are supported. Flags `--template`, `--separate-git-dir` and `--shared` are not. | +| clone | ✔ | Plain clone and equivalents to `--progress`, `--single-branch`, `--depth`, `--origin`, `--recurse-submodules` are supported. Others are not. | +| **basic snapshotting** | +| add | ✔ | Plain add is supported. Any other flag aren't supported | +| status | ✔ | +| commit | ✔ | +| reset | ✔ | +| rm | ✔ | +| mv | ✔ | +| **branching and merging** | +| branch | ✔ | +| checkout | ✔ | Basic usages of checkout are supported. | +| merge | ✖ | +| mergetool | ✖ | +| stash | ✖ | +| tag | ✔ | +| **sharing and updating projects** | +| fetch | ✔ | +| pull | ✔ | Only supports merges where the merge can be resolved as a fast-forward. | +| push | ✔ | +| remote | ✔ | +| submodule | ✔ | +| **inspection and comparison** | +| show | ✔ | +| log | ✔ | +| shortlog | (see log) | +| describe | | +| **patching** | +| apply | ✖ | +| cherry-pick | ✖ | +| diff | ✔ | Patch object with UnifiedDiff output representation | +| rebase | ✖ | +| revert | ✖ | +| **debugging** | +| bisect | ✖ | +| blame | ✔ | +| grep | ✔ | +| **email** || +| am | ✖ | +| apply | ✖ | +| format-patch | ✖ | +| send-email | ✖ | +| request-pull | ✖ | +| **external systems** | +| svn | ✖ | +| fast-import | ✖ | +| **administration** | +| clean | ✔ | +| gc | ✖ | +| fsck | ✖ | +| reflog | ✖ | +| filter-branch | ✖ | +| instaweb | ✖ | +| archive | ✖ | +| bundle | ✖ | +| prune | ✖ | +| repack | ✖ | +| **server admin** | +| daemon | | +| update-server-info | | +| **advanced** | +| notes | ✖ | +| replace | ✖ | +| worktree | ✖ | +| annotate | (see blame) | +| **gpg** | +| git-verify-commit | ✔ | +| git-verify-tag | ✔ | +| **plumbing commands** | +| cat-file | ✔ | +| check-ignore | | +| commit-tree | | +| count-objects | | +| diff-index | | +| for-each-ref | ✔ | +| hash-object | ✔ | +| ls-files | ✔ | +| merge-base | ✔ | Calculates the merge-base only between two commits, and supports `--independent` and `--is-ancestor` modifiers; Does not support `--fork-point` nor `--octopus` modifiers. | +| read-tree | | +| rev-list | ✔ | +| rev-parse | | +| show-ref | ✔ | +| symbolic-ref | ✔ | +| update-index | | +| update-ref | | +| verify-pack | | +| write-tree | | +| **protocols** | +| http(s):// (dumb) | ✖ | +| http(s):// (smart) | ✔ | +| git:// | ✔ | +| ssh:// | ✔ | +| file:// | ✔ | +| custom | ✔ | +| **other features** | +| gitignore | ✔ | +| gitattributes | ✖ | +| index version | | +| packfile version | | +| push-certs | ✖ | diff --git a/vendor/gopkg.in/src-d/go-git.v4/CONTRIBUTING.md b/vendor/gopkg.in/src-d/go-git.v4/CONTRIBUTING.md new file mode 100644 index 0000000000..bdb5f73341 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# Contributing Guidelines + +source{d} go-git project is [Apache 2.0 licensed](LICENSE) and accepts +contributions via GitHub pull requests. This document outlines some of the +conventions on development workflow, commit message formatting, contact points, +and other resources to make it easier to get your contribution accepted. + +## Certificate of Origin + +By contributing to this project you agree to the [Developer Certificate of +Origin (DCO)](DCO). This document was created by the Linux Kernel community and is a +simple statement that you, as a contributor, have the legal right to make the +contribution. + +In order to show your agreement with the DCO you should include at the end of commit message, +the following line: `Signed-off-by: John Doe `, using your real name. + +This can be done easily using the [`-s`](https://github.com/git/git/blob/b2c150d3aa82f6583b9aadfecc5f8fa1c74aca09/Documentation/git-commit.txt#L154-L161) flag on the `git commit`. + +## Support Channels + +The official support channels, for both users and contributors, are: + +- [StackOverflow go-git tag](https://stackoverflow.com/questions/tagged/go-git) for user questions. +- GitHub [Issues](https://github.com/src-d/go-git/issues)* for bug reports and feature requests. +- Slack: #go-git room in the [source{d} Slack](https://join.slack.com/t/sourced-community/shared_invite/enQtMjc4Njk5MzEyNzM2LTFjNzY4NjEwZGEwMzRiNTM4MzRlMzQ4MmIzZjkwZmZlM2NjODUxZmJjNDI1OTcxNDAyMmZlNmFjODZlNTg0YWM) + +*Before opening a new issue or submitting a new pull request, it's helpful to +search the project - it's likely that another user has already reported the +issue you're facing, or it's a known issue that we're already aware of. + + +## How to Contribute + +Pull Requests (PRs) are the main and exclusive way to contribute to the official go-git project. +In order for a PR to be accepted it needs to pass a list of requirements: + +- You should be able to run the same query using `git`. We don't accept features that are not implemented in the official git implementation. +- The expected behavior must match the [official git implementation](https://github.com/git/git). +- The actual behavior must be correctly explained with natural language and providing a minimum working example in Go that reproduces it. +- All PRs must be written in idiomatic Go, formatted according to [gofmt](https://golang.org/cmd/gofmt/), and without any warnings from [go lint](https://github.com/golang/lint) nor [go vet](https://golang.org/cmd/vet/). +- They should in general include tests, and those shall pass. +- If the PR is a bug fix, it has to include a suite of unit tests for the new functionality. +- If the PR is a new feature, it has to come with a suite of unit tests, that tests the new functionality. +- In any case, all the PRs have to pass the personal evaluation of at least one of the [maintainers](MAINTAINERS) of go-git. + +### Format of the commit message + +Every commit message should describe what was changed, under which context and, if applicable, the GitHub issue it relates to: + +``` +plumbing: packp, Skip argument validations for unknown capabilities. Fixes #623 +``` + +The format can be described more formally as follows: + +``` +: , . [Fixes #] +``` diff --git a/vendor/gopkg.in/src-d/go-git.v4/DCO b/vendor/gopkg.in/src-d/go-git.v4/DCO new file mode 100644 index 0000000000..3aca339def --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/DCO @@ -0,0 +1,36 @@ +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. \ No newline at end of file diff --git a/vendor/gopkg.in/src-d/go-git.v4/LICENSE b/vendor/gopkg.in/src-d/go-git.v4/LICENSE new file mode 100644 index 0000000000..8aa3d854cf --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/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 2018 Sourced Technologies, S.L. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/gopkg.in/src-d/go-git.v4/MAINTAINERS b/vendor/gopkg.in/src-d/go-git.v4/MAINTAINERS new file mode 100644 index 0000000000..ff2129c45f --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/MAINTAINERS @@ -0,0 +1,3 @@ +Máximo Cuadros (@mcuadros) +Jeremy Stribling (@strib) +Ori Rawlings (@orirawlings) diff --git a/vendor/gopkg.in/src-d/go-git.v4/Makefile b/vendor/gopkg.in/src-d/go-git.v4/Makefile new file mode 100644 index 0000000000..d576778f4d --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/Makefile @@ -0,0 +1,52 @@ +# General +WORKDIR = $(PWD) + +# Go parameters +GOCMD = go +GOTEST = $(GOCMD) test -v + +# Git config +GIT_VERSION ?= +GIT_DIST_PATH ?= $(PWD)/.git-dist +GIT_REPOSITORY = http://github.com/git/git.git + +# Coverage +COVERAGE_REPORT = coverage.txt +COVERAGE_PROFILE = profile.out +COVERAGE_MODE = atomic + +ifneq ($(origin CI), undefined) + WORKDIR := $(GOPATH)/src/gopkg.in/src-d/go-git.v4 +endif + +build-git: + @if [ -f $(GIT_DIST_PATH)/git ]; then \ + echo "nothing to do, using cache $(GIT_DIST_PATH)"; \ + else \ + git clone $(GIT_REPOSITORY) -b $(GIT_VERSION) --depth 1 --single-branch $(GIT_DIST_PATH); \ + cd $(GIT_DIST_PATH); \ + make configure; \ + ./configure; \ + make all; \ + fi + +test: + @cd $(WORKDIR); \ + $(GOTEST) ./... + +test-coverage: + @cd $(WORKDIR); \ + echo "" > $(COVERAGE_REPORT); \ + for dir in `find . -name "*.go" | grep -o '.*/' | sort | uniq`; do \ + $(GOTEST) $$dir -coverprofile=$(COVERAGE_PROFILE) -covermode=$(COVERAGE_MODE); \ + if [ $$? != 0 ]; then \ + exit 2; \ + fi; \ + if [ -f $(COVERAGE_PROFILE) ]; then \ + cat $(COVERAGE_PROFILE) >> $(COVERAGE_REPORT); \ + rm $(COVERAGE_PROFILE); \ + fi; \ + done; \ + +clean: + rm -rf $(GIT_DIST_PATH) \ No newline at end of file diff --git a/vendor/gopkg.in/src-d/go-git.v4/README.md b/vendor/gopkg.in/src-d/go-git.v4/README.md new file mode 100644 index 0000000000..ed9306c83f --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/README.md @@ -0,0 +1,123 @@ +![go-git logo](https://cdn.rawgit.com/src-d/artwork/02036484/go-git/files/go-git-github-readme-header.png) +[![GoDoc](https://godoc.org/gopkg.in/src-d/go-git.v4?status.svg)](https://godoc.org/github.com/src-d/go-git) [![Build Status](https://travis-ci.org/src-d/go-git.svg)](https://travis-ci.org/src-d/go-git) [![Build status](https://ci.appveyor.com/api/projects/status/nyidskwifo4py6ub?svg=true)](https://ci.appveyor.com/project/mcuadros/go-git) [![codecov.io](https://codecov.io/github/src-d/go-git/coverage.svg)](https://codecov.io/github/src-d/go-git) [![Go Report Card](https://goreportcard.com/badge/github.com/src-d/go-git)](https://goreportcard.com/report/github.com/src-d/go-git) + +*go-git* is a highly extensible git implementation library written in **pure Go**. + +It can be used to manipulate git repositories at low level *(plumbing)* or high level *(porcelain)*, through an idiomatic Go API. It also supports several types of storage, such as in-memory filesystems, or custom implementations thanks to the [`Storer`](https://godoc.org/gopkg.in/src-d/go-git.v4/plumbing/storer) interface. + +It's being actively developed since 2015 and is being used extensively by [source{d}](https://sourced.tech/) and [Keybase](https://keybase.io/blog/encrypted-git-for-everyone), and by many other libraries and tools. + +Comparison with git +------------------- + +*go-git* aims to be fully compatible with [git](https://github.com/git/git), all the *porcelain* operations are implemented to work exactly as *git* does. + +*git* is a humongous project with years of development by thousands of contributors, making it challenging for *go-git* to implement all the features. You can find a comparison of *go-git* vs *git* in the [compatibility documentation](COMPATIBILITY.md). + + +Installation +------------ + +The recommended way to install *go-git* is: + +``` +go get -u gopkg.in/src-d/go-git.v4/... +``` + +> We use [gopkg.in](http://labix.org/gopkg.in) to version the API, this means that when `go get` clones the package, it's the latest tag matching `v4.*` that is cloned and not the master branch. + +Examples +-------- + +> Please note that the `CheckIfError` and `Info` functions used in the examples are from the [examples package](https://github.com/src-d/go-git/blob/master/_examples/common.go#L17) just to be used in the examples. + + +### Basic example + +A basic example that mimics the standard `git clone` command + +```go +// Clone the given repository to the given directory +Info("git clone https://github.com/src-d/go-git") + +_, err := git.PlainClone("/tmp/foo", false, &git.CloneOptions{ + URL: "https://github.com/src-d/go-git", + Progress: os.Stdout, +}) + +CheckIfError(err) +``` + +Outputs: +``` +Counting objects: 4924, done. +Compressing objects: 100% (1333/1333), done. +Total 4924 (delta 530), reused 6 (delta 6), pack-reused 3533 +``` + +### In-memory example + +Cloning a repository into memory and printing the history of HEAD, just like `git log` does + + +```go +// Clones the given repository in memory, creating the remote, the local +// branches and fetching the objects, exactly as: +Info("git clone https://github.com/src-d/go-siva") + +r, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{ + URL: "https://github.com/src-d/go-siva", +}) + +CheckIfError(err) + +// Gets the HEAD history from HEAD, just like this command: +Info("git log") + +// ... retrieves the branch pointed by HEAD +ref, err := r.Head() +CheckIfError(err) + + +// ... retrieves the commit history +cIter, err := r.Log(&git.LogOptions{From: ref.Hash()}) +CheckIfError(err) + +// ... just iterates over the commits, printing it +err = cIter.ForEach(func(c *object.Commit) error { + fmt.Println(c) + return nil +}) +CheckIfError(err) +``` + +Outputs: +``` +commit ded8054fd0c3994453e9c8aacaf48d118d42991e +Author: Santiago M. Mola +Date: Sat Nov 12 21:18:41 2016 +0100 + + index: ReadFrom/WriteTo returns IndexReadError/IndexWriteError. (#9) + +commit df707095626f384ce2dc1a83b30f9a21d69b9dfc +Author: Santiago M. Mola +Date: Fri Nov 11 13:23:22 2016 +0100 + + readwriter: fix bug when writing index. (#10) + + When using ReadWriter on an existing siva file, absolute offset for + index entries was not being calculated correctly. +... +``` + +You can find this [example](_examples/log/main.go) and many others in the [examples](_examples) folder. + +Contribute +---------- + +[Contributions](https://github.com/src-d/go-git/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) are more than welcome, if you are interested please take a look to +our [Contributing Guidelines](CONTRIBUTING.md). + +License +------- +Apache License Version 2.0, see [LICENSE](LICENSE) diff --git a/vendor/gopkg.in/src-d/go-git.v4/appveyor.yml b/vendor/gopkg.in/src-d/go-git.v4/appveyor.yml new file mode 100644 index 0000000000..160616bec9 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/appveyor.yml @@ -0,0 +1,21 @@ +version: "{build}" +platform: x64 + +matrix: + allow_failures: + - platform: x64 + +clone_folder: c:\gopath\src\gopkg.in\src-d\go-git.v4 + +environment: + GOPATH: c:\gopath + +install: + - set PATH=%GOPATH%\bin;c:\go\bin;"C:\Program Files\Git\mingw64\bin";%PATH% + - go version + - go get -v -t ./... + - git config --global user.email "travis@example.com" + - git config --global user.name "Travis CI + +build_script: + - go test -v ./... diff --git a/vendor/gopkg.in/src-d/go-git.v4/blame.go b/vendor/gopkg.in/src-d/go-git.v4/blame.go new file mode 100644 index 0000000000..f6108519ad --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/blame.go @@ -0,0 +1,302 @@ +package git + +import ( + "bytes" + "errors" + "fmt" + "strconv" + "strings" + "time" + "unicode/utf8" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/object" + "gopkg.in/src-d/go-git.v4/utils/diff" +) + +// BlameResult represents the result of a Blame operation. +type BlameResult struct { + // Path is the path of the File that we're blaming. + Path string + // Rev (Revision) is the hash of the specified Commit used to generate this result. + Rev plumbing.Hash + // Lines contains every line with its authorship. + Lines []*Line +} + +// Blame returns a BlameResult with the information about the last author of +// each line from file `path` at commit `c`. +func Blame(c *object.Commit, path string) (*BlameResult, error) { + // The file to blame is identified by the input arguments: + // commit and path. commit is a Commit object obtained from a Repository. Path + // represents a path to a specific file contained into the repository. + // + // Blaming a file is a two step process: + // + // 1. Create a linear history of the commits affecting a file. We use + // revlist.New for that. + // + // 2. Then build a graph with a node for every line in every file in + // the history of the file. + // + // Each node is assigned a commit: Start by the nodes in the first + // commit. Assign that commit as the creator of all its lines. + // + // Then jump to the nodes in the next commit, and calculate the diff + // between the two files. Newly created lines get + // assigned the new commit as its origin. Modified lines also get + // this new commit. Untouched lines retain the old commit. + // + // All this work is done in the assignOrigin function which holds all + // the internal relevant data in a "blame" struct, that is not + // exported. + // + // TODO: ways to improve the efficiency of this function: + // 1. Improve revlist + // 2. Improve how to traverse the history (example a backward traversal will + // be much more efficient) + // + // TODO: ways to improve the function in general: + // 1. Add memoization between revlist and assign. + // 2. It is using much more memory than needed, see the TODOs below. + + b := new(blame) + b.fRev = c + b.path = path + + // get all the file revisions + if err := b.fillRevs(); err != nil { + return nil, err + } + + // calculate the line tracking graph and fill in + // file contents in data. + if err := b.fillGraphAndData(); err != nil { + return nil, err + } + + file, err := b.fRev.File(b.path) + if err != nil { + return nil, err + } + finalLines, err := file.Lines() + if err != nil { + return nil, err + } + + // Each node (line) holds the commit where it was introduced or + // last modified. To achieve that we use the FORWARD algorithm + // described in Zimmermann, et al. "Mining Version Archives for + // Co-changed Lines", in proceedings of the Mining Software + // Repositories workshop, Shanghai, May 22-23, 2006. + lines, err := newLines(finalLines, b.sliceGraph(len(b.graph)-1)) + if err != nil { + return nil, err + } + + return &BlameResult{ + Path: path, + Rev: c.Hash, + Lines: lines, + }, nil +} + +// Line values represent the contents and author of a line in BlamedResult values. +type Line struct { + // Author is the email address of the last author that modified the line. + Author string + // Text is the original text of the line. + Text string + // Date is when the original text of the line was introduced + Date time.Time + // Hash is the commit hash that introduced the original line + Hash plumbing.Hash +} + +func newLine(author, text string, date time.Time, hash plumbing.Hash) *Line { + return &Line{ + Author: author, + Text: text, + Hash: hash, + Date: date, + } +} + +func newLines(contents []string, commits []*object.Commit) ([]*Line, error) { + lcontents := len(contents) + lcommits := len(commits) + + if lcontents != lcommits { + if lcontents == lcommits-1 && contents[lcontents-1] != "\n" { + contents = append(contents, "\n") + } else { + return nil, errors.New("contents and commits have different length") + } + } + + result := make([]*Line, 0, lcontents) + for i := range contents { + result = append(result, newLine( + commits[i].Author.Email, contents[i], + commits[i].Author.When, commits[i].Hash, + )) + } + + return result, nil +} + +// this struct is internally used by the blame function to hold its +// inputs, outputs and state. +type blame struct { + // the path of the file to blame + path string + // the commit of the final revision of the file to blame + fRev *object.Commit + // the chain of revisions affecting the the file to blame + revs []*object.Commit + // the contents of the file across all its revisions + data []string + // the graph of the lines in the file across all the revisions + graph [][]*object.Commit +} + +// calculate the history of a file "path", starting from commit "from", sorted by commit date. +func (b *blame) fillRevs() error { + var err error + + b.revs, err = references(b.fRev, b.path) + return err +} + +// build graph of a file from its revision history +func (b *blame) fillGraphAndData() error { + //TODO: not all commits are needed, only the current rev and the prev + b.graph = make([][]*object.Commit, len(b.revs)) + b.data = make([]string, len(b.revs)) // file contents in all the revisions + // for every revision of the file, starting with the first + // one... + for i, rev := range b.revs { + // get the contents of the file + file, err := rev.File(b.path) + if err != nil { + return nil + } + b.data[i], err = file.Contents() + if err != nil { + return err + } + nLines := countLines(b.data[i]) + // create a node for each line + b.graph[i] = make([]*object.Commit, nLines) + // assign a commit to each node + // if this is the first revision, then the node is assigned to + // this first commit. + if i == 0 { + for j := 0; j < nLines; j++ { + b.graph[i][j] = b.revs[i] + } + } else { + // if this is not the first commit, then assign to the old + // commit or to the new one, depending on what the diff + // says. + b.assignOrigin(i, i-1) + } + } + return nil +} + +// sliceGraph returns a slice of commits (one per line) for a particular +// revision of a file (0=first revision). +func (b *blame) sliceGraph(i int) []*object.Commit { + fVs := b.graph[i] + result := make([]*object.Commit, 0, len(fVs)) + for _, v := range fVs { + c := *v + result = append(result, &c) + } + return result +} + +// Assigns origin to vertexes in current (c) rev from data in its previous (p) +// revision +func (b *blame) assignOrigin(c, p int) { + // assign origin based on diff info + hunks := diff.Do(b.data[p], b.data[c]) + sl := -1 // source line + dl := -1 // destination line + for h := range hunks { + hLines := countLines(hunks[h].Text) + for hl := 0; hl < hLines; hl++ { + switch { + case hunks[h].Type == 0: + sl++ + dl++ + b.graph[c][dl] = b.graph[p][sl] + case hunks[h].Type == 1: + dl++ + b.graph[c][dl] = b.revs[c] + case hunks[h].Type == -1: + sl++ + default: + panic("unreachable") + } + } + } +} + +// GoString prints the results of a Blame using git-blame's style. +func (b *blame) GoString() string { + var buf bytes.Buffer + + file, err := b.fRev.File(b.path) + if err != nil { + panic("PrettyPrint: internal error in repo.Data") + } + contents, err := file.Contents() + if err != nil { + panic("PrettyPrint: internal error in repo.Data") + } + + lines := strings.Split(contents, "\n") + // max line number length + mlnl := len(strconv.Itoa(len(lines))) + // max author length + mal := b.maxAuthorLength() + format := fmt.Sprintf("%%s (%%-%ds %%%dd) %%s\n", + mal, mlnl) + + fVs := b.graph[len(b.graph)-1] + for ln, v := range fVs { + fmt.Fprintf(&buf, format, v.Hash.String()[:8], + prettyPrintAuthor(fVs[ln]), ln+1, lines[ln]) + } + return buf.String() +} + +// utility function to pretty print the author. +func prettyPrintAuthor(c *object.Commit) string { + return fmt.Sprintf("%s %s", c.Author.Name, c.Author.When.Format("2006-01-02")) +} + +// utility function to calculate the number of runes needed +// to print the longest author name in the blame of a file. +func (b *blame) maxAuthorLength() int { + memo := make(map[plumbing.Hash]struct{}, len(b.graph)-1) + fVs := b.graph[len(b.graph)-1] + m := 0 + for ln := range fVs { + if _, ok := memo[fVs[ln].Hash]; ok { + continue + } + memo[fVs[ln].Hash] = struct{}{} + m = max(m, utf8.RuneCountInString(prettyPrintAuthor(fVs[ln]))) + } + return m +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/common.go b/vendor/gopkg.in/src-d/go-git.v4/common.go new file mode 100644 index 0000000000..f837a2654c --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/common.go @@ -0,0 +1,22 @@ +package git + +import "strings" + +const defaultDotGitPath = ".git" + +// countLines returns the number of lines in a string à la git, this is +// The newline character is assumed to be '\n'. The empty string +// contains 0 lines. If the last line of the string doesn't end with a +// newline, it will still be considered a line. +func countLines(s string) int { + if s == "" { + return 0 + } + + nEOL := strings.Count(s, "\n") + if strings.HasSuffix(s, "\n") { + return nEOL + } + + return nEOL + 1 +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/config/branch.go b/vendor/gopkg.in/src-d/go-git.v4/config/branch.go new file mode 100644 index 0000000000..20dde6e030 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/config/branch.go @@ -0,0 +1,90 @@ +package config + +import ( + "errors" + + "gopkg.in/src-d/go-git.v4/plumbing" + format "gopkg.in/src-d/go-git.v4/plumbing/format/config" +) + +var ( + errBranchEmptyName = errors.New("branch config: empty name") + errBranchInvalidMerge = errors.New("branch config: invalid merge") + errBranchInvalidRebase = errors.New("branch config: rebase must be one of 'true' or 'interactive'") +) + +// Branch contains information on the +// local branches and which remote to track +type Branch struct { + // Name of branch + Name string + // Remote name of remote to track + Remote string + // Merge is the local refspec for the branch + Merge plumbing.ReferenceName + // Rebase instead of merge when pulling. Valid values are + // "true" and "interactive". "false" is undocumented and + // typically represented by the non-existence of this field + Rebase string + + raw *format.Subsection +} + +// Validate validates fields of branch +func (b *Branch) Validate() error { + if b.Name == "" { + return errBranchEmptyName + } + + if b.Merge != "" && !b.Merge.IsBranch() { + return errBranchInvalidMerge + } + + if b.Rebase != "" && + b.Rebase != "true" && + b.Rebase != "interactive" && + b.Rebase != "false" { + return errBranchInvalidRebase + } + + return nil +} + +func (b *Branch) marshal() *format.Subsection { + if b.raw == nil { + b.raw = &format.Subsection{} + } + + b.raw.Name = b.Name + + if b.Remote == "" { + b.raw.RemoveOption(remoteSection) + } else { + b.raw.SetOption(remoteSection, b.Remote) + } + + if b.Merge == "" { + b.raw.RemoveOption(mergeKey) + } else { + b.raw.SetOption(mergeKey, string(b.Merge)) + } + + if b.Rebase == "" { + b.raw.RemoveOption(rebaseKey) + } else { + b.raw.SetOption(rebaseKey, b.Rebase) + } + + return b.raw +} + +func (b *Branch) unmarshal(s *format.Subsection) error { + b.raw = s + + b.Name = b.raw.Name + b.Remote = b.raw.Options.Get(remoteSection) + b.Merge = plumbing.ReferenceName(b.raw.Options.Get(mergeKey)) + b.Rebase = b.raw.Options.Get(rebaseKey) + + return b.Validate() +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/config/config.go b/vendor/gopkg.in/src-d/go-git.v4/config/config.go new file mode 100644 index 0000000000..ea614e96dd --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/config/config.go @@ -0,0 +1,407 @@ +// Package config contains the abstraction of multiple config files +package config + +import ( + "bytes" + "errors" + "fmt" + "sort" + "strconv" + + "gopkg.in/src-d/go-git.v4/internal/url" + format "gopkg.in/src-d/go-git.v4/plumbing/format/config" +) + +const ( + // DefaultFetchRefSpec is the default refspec used for fetch. + DefaultFetchRefSpec = "+refs/heads/*:refs/remotes/%s/*" + // DefaultPushRefSpec is the default refspec used for push. + DefaultPushRefSpec = "refs/heads/*:refs/heads/*" +) + +// ConfigStorer generic storage of Config object +type ConfigStorer interface { + Config() (*Config, error) + SetConfig(*Config) error +} + +var ( + ErrInvalid = errors.New("config invalid key in remote or branch") + ErrRemoteConfigNotFound = errors.New("remote config not found") + ErrRemoteConfigEmptyURL = errors.New("remote config: empty URL") + ErrRemoteConfigEmptyName = errors.New("remote config: empty name") +) + +// Config contains the repository configuration +// ftp://www.kernel.org/pub/software/scm/git/docs/git-config.html#FILES +type Config struct { + Core struct { + // IsBare if true this repository is assumed to be bare and has no + // working directory associated with it. + IsBare bool + // Worktree is the path to the root of the working tree. + Worktree string + // CommentChar is the character indicating the start of a + // comment for commands like commit and tag + CommentChar string + } + + Pack struct { + // Window controls the size of the sliding window for delta + // compression. The default is 10. A value of 0 turns off + // delta compression entirely. + Window uint + } + + // Remotes list of repository remotes, the key of the map is the name + // of the remote, should equal to RemoteConfig.Name. + Remotes map[string]*RemoteConfig + // Submodules list of repository submodules, the key of the map is the name + // of the submodule, should equal to Submodule.Name. + Submodules map[string]*Submodule + // Branches list of branches, the key is the branch name and should + // equal Branch.Name + Branches map[string]*Branch + // Raw contains the raw information of a config file. The main goal is + // preserve the parsed information from the original format, to avoid + // dropping unsupported fields. + Raw *format.Config +} + +// NewConfig returns a new empty Config. +func NewConfig() *Config { + config := &Config{ + Remotes: make(map[string]*RemoteConfig), + Submodules: make(map[string]*Submodule), + Branches: make(map[string]*Branch), + Raw: format.New(), + } + + config.Pack.Window = DefaultPackWindow + + return config +} + +// Validate validates the fields and sets the default values. +func (c *Config) Validate() error { + for name, r := range c.Remotes { + if r.Name != name { + return ErrInvalid + } + + if err := r.Validate(); err != nil { + return err + } + } + + for name, b := range c.Branches { + if b.Name != name { + return ErrInvalid + } + + if err := b.Validate(); err != nil { + return err + } + } + + return nil +} + +const ( + remoteSection = "remote" + submoduleSection = "submodule" + branchSection = "branch" + coreSection = "core" + packSection = "pack" + fetchKey = "fetch" + urlKey = "url" + bareKey = "bare" + worktreeKey = "worktree" + commentCharKey = "commentChar" + windowKey = "window" + mergeKey = "merge" + rebaseKey = "rebase" + + // DefaultPackWindow holds the number of previous objects used to + // generate deltas. The value 10 is the same used by git command. + DefaultPackWindow = uint(10) +) + +// Unmarshal parses a git-config file and stores it. +func (c *Config) Unmarshal(b []byte) error { + r := bytes.NewBuffer(b) + d := format.NewDecoder(r) + + c.Raw = format.New() + if err := d.Decode(c.Raw); err != nil { + return err + } + + c.unmarshalCore() + if err := c.unmarshalPack(); err != nil { + return err + } + unmarshalSubmodules(c.Raw, c.Submodules) + + if err := c.unmarshalBranches(); err != nil { + return err + } + + return c.unmarshalRemotes() +} + +func (c *Config) unmarshalCore() { + s := c.Raw.Section(coreSection) + if s.Options.Get(bareKey) == "true" { + c.Core.IsBare = true + } + + c.Core.Worktree = s.Options.Get(worktreeKey) + c.Core.CommentChar = s.Options.Get(commentCharKey) +} + +func (c *Config) unmarshalPack() error { + s := c.Raw.Section(packSection) + window := s.Options.Get(windowKey) + if window == "" { + c.Pack.Window = DefaultPackWindow + } else { + winUint, err := strconv.ParseUint(window, 10, 32) + if err != nil { + return err + } + c.Pack.Window = uint(winUint) + } + return nil +} + +func (c *Config) unmarshalRemotes() error { + s := c.Raw.Section(remoteSection) + for _, sub := range s.Subsections { + r := &RemoteConfig{} + if err := r.unmarshal(sub); err != nil { + return err + } + + c.Remotes[r.Name] = r + } + + return nil +} + +func unmarshalSubmodules(fc *format.Config, submodules map[string]*Submodule) { + s := fc.Section(submoduleSection) + for _, sub := range s.Subsections { + m := &Submodule{} + m.unmarshal(sub) + + if m.Validate() == ErrModuleBadPath { + continue + } + + submodules[m.Name] = m + } +} + +func (c *Config) unmarshalBranches() error { + bs := c.Raw.Section(branchSection) + for _, sub := range bs.Subsections { + b := &Branch{} + + if err := b.unmarshal(sub); err != nil { + return err + } + + c.Branches[b.Name] = b + } + return nil +} + +// Marshal returns Config encoded as a git-config file. +func (c *Config) Marshal() ([]byte, error) { + c.marshalCore() + c.marshalPack() + c.marshalRemotes() + c.marshalSubmodules() + c.marshalBranches() + + buf := bytes.NewBuffer(nil) + if err := format.NewEncoder(buf).Encode(c.Raw); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (c *Config) marshalCore() { + s := c.Raw.Section(coreSection) + s.SetOption(bareKey, fmt.Sprintf("%t", c.Core.IsBare)) + + if c.Core.Worktree != "" { + s.SetOption(worktreeKey, c.Core.Worktree) + } +} + +func (c *Config) marshalPack() { + s := c.Raw.Section(packSection) + if c.Pack.Window != DefaultPackWindow { + s.SetOption(windowKey, fmt.Sprintf("%d", c.Pack.Window)) + } +} + +func (c *Config) marshalRemotes() { + s := c.Raw.Section(remoteSection) + newSubsections := make(format.Subsections, 0, len(c.Remotes)) + added := make(map[string]bool) + for _, subsection := range s.Subsections { + if remote, ok := c.Remotes[subsection.Name]; ok { + newSubsections = append(newSubsections, remote.marshal()) + added[subsection.Name] = true + } + } + + remoteNames := make([]string, 0, len(c.Remotes)) + for name := range c.Remotes { + remoteNames = append(remoteNames, name) + } + + sort.Strings(remoteNames) + + for _, name := range remoteNames { + if !added[name] { + newSubsections = append(newSubsections, c.Remotes[name].marshal()) + } + } + + s.Subsections = newSubsections +} + +func (c *Config) marshalSubmodules() { + s := c.Raw.Section(submoduleSection) + s.Subsections = make(format.Subsections, len(c.Submodules)) + + var i int + for _, r := range c.Submodules { + section := r.marshal() + // the submodule section at config is a subset of the .gitmodule file + // we should remove the non-valid options for the config file. + section.RemoveOption(pathKey) + s.Subsections[i] = section + i++ + } +} + +func (c *Config) marshalBranches() { + s := c.Raw.Section(branchSection) + newSubsections := make(format.Subsections, 0, len(c.Branches)) + added := make(map[string]bool) + for _, subsection := range s.Subsections { + if branch, ok := c.Branches[subsection.Name]; ok { + newSubsections = append(newSubsections, branch.marshal()) + added[subsection.Name] = true + } + } + + branchNames := make([]string, 0, len(c.Branches)) + for name := range c.Branches { + branchNames = append(branchNames, name) + } + + sort.Strings(branchNames) + + for _, name := range branchNames { + if !added[name] { + newSubsections = append(newSubsections, c.Branches[name].marshal()) + } + } + + s.Subsections = newSubsections +} + +// RemoteConfig contains the configuration for a given remote repository. +type RemoteConfig struct { + // Name of the remote + Name string + // URLs the URLs of a remote repository. It must be non-empty. Fetch will + // always use the first URL, while push will use all of them. + URLs []string + // Fetch the default set of "refspec" for fetch operation + Fetch []RefSpec + + // raw representation of the subsection, filled by marshal or unmarshal are + // called + raw *format.Subsection +} + +// Validate validates the fields and sets the default values. +func (c *RemoteConfig) Validate() error { + if c.Name == "" { + return ErrRemoteConfigEmptyName + } + + if len(c.URLs) == 0 { + return ErrRemoteConfigEmptyURL + } + + for _, r := range c.Fetch { + if err := r.Validate(); err != nil { + return err + } + } + + if len(c.Fetch) == 0 { + c.Fetch = []RefSpec{RefSpec(fmt.Sprintf(DefaultFetchRefSpec, c.Name))} + } + + return nil +} + +func (c *RemoteConfig) unmarshal(s *format.Subsection) error { + c.raw = s + + fetch := []RefSpec{} + for _, f := range c.raw.Options.GetAll(fetchKey) { + rs := RefSpec(f) + if err := rs.Validate(); err != nil { + return err + } + + fetch = append(fetch, rs) + } + + c.Name = c.raw.Name + c.URLs = append([]string(nil), c.raw.Options.GetAll(urlKey)...) + c.Fetch = fetch + + return nil +} + +func (c *RemoteConfig) marshal() *format.Subsection { + if c.raw == nil { + c.raw = &format.Subsection{} + } + + c.raw.Name = c.Name + if len(c.URLs) == 0 { + c.raw.RemoveOption(urlKey) + } else { + c.raw.SetOption(urlKey, c.URLs...) + } + + if len(c.Fetch) == 0 { + c.raw.RemoveOption(fetchKey) + } else { + var values []string + for _, rs := range c.Fetch { + values = append(values, rs.String()) + } + + c.raw.SetOption(fetchKey, values...) + } + + return c.raw +} + +func (c *RemoteConfig) IsFirstURLLocal() bool { + return url.IsLocalEndpoint(c.URLs[0]) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/config/modules.go b/vendor/gopkg.in/src-d/go-git.v4/config/modules.go new file mode 100644 index 0000000000..90758d9327 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/config/modules.go @@ -0,0 +1,139 @@ +package config + +import ( + "bytes" + "errors" + "regexp" + + format "gopkg.in/src-d/go-git.v4/plumbing/format/config" +) + +var ( + ErrModuleEmptyURL = errors.New("module config: empty URL") + ErrModuleEmptyPath = errors.New("module config: empty path") + ErrModuleBadPath = errors.New("submodule has an invalid path") +) + +var ( + // Matches module paths with dotdot ".." components. + dotdotPath = regexp.MustCompile(`(^|[/\\])\.\.([/\\]|$)`) +) + +// Modules defines the submodules properties, represents a .gitmodules file +// https://www.kernel.org/pub/software/scm/git/docs/gitmodules.html +type Modules struct { + // Submodules is a map of submodules being the key the name of the submodule. + Submodules map[string]*Submodule + + raw *format.Config +} + +// NewModules returns a new empty Modules +func NewModules() *Modules { + return &Modules{ + Submodules: make(map[string]*Submodule), + raw: format.New(), + } +} + +const ( + pathKey = "path" + branchKey = "branch" +) + +// Unmarshal parses a git-config file and stores it. +func (m *Modules) Unmarshal(b []byte) error { + r := bytes.NewBuffer(b) + d := format.NewDecoder(r) + + m.raw = format.New() + if err := d.Decode(m.raw); err != nil { + return err + } + + unmarshalSubmodules(m.raw, m.Submodules) + return nil +} + +// Marshal returns Modules encoded as a git-config file. +func (m *Modules) Marshal() ([]byte, error) { + s := m.raw.Section(submoduleSection) + s.Subsections = make(format.Subsections, len(m.Submodules)) + + var i int + for _, r := range m.Submodules { + s.Subsections[i] = r.marshal() + i++ + } + + buf := bytes.NewBuffer(nil) + if err := format.NewEncoder(buf).Encode(m.raw); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +// Submodule defines a submodule. +type Submodule struct { + // Name module name + Name string + // Path defines the path, relative to the top-level directory of the Git + // working tree. + Path string + // URL defines a URL from which the submodule repository can be cloned. + URL string + // Branch is a remote branch name for tracking updates in the upstream + // submodule. Optional value. + Branch string + + // raw representation of the subsection, filled by marshal or unmarshal are + // called. + raw *format.Subsection +} + +// Validate validates the fields and sets the default values. +func (m *Submodule) Validate() error { + if m.Path == "" { + return ErrModuleEmptyPath + } + + if m.URL == "" { + return ErrModuleEmptyURL + } + + if dotdotPath.MatchString(m.Path) { + return ErrModuleBadPath + } + + return nil +} + +func (m *Submodule) unmarshal(s *format.Subsection) { + m.raw = s + + m.Name = m.raw.Name + m.Path = m.raw.Option(pathKey) + m.URL = m.raw.Option(urlKey) + m.Branch = m.raw.Option(branchKey) +} + +func (m *Submodule) marshal() *format.Subsection { + if m.raw == nil { + m.raw = &format.Subsection{} + } + + m.raw.Name = m.Name + if m.raw.Name == "" { + m.raw.Name = m.Path + } + + m.raw.SetOption(pathKey, m.Path) + m.raw.SetOption(urlKey, m.URL) + + if m.Branch != "" { + m.raw.SetOption(branchKey, m.Branch) + } + + return m.raw +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/config/refspec.go b/vendor/gopkg.in/src-d/go-git.v4/config/refspec.go new file mode 100644 index 0000000000..14bb40069c --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/config/refspec.go @@ -0,0 +1,150 @@ +package config + +import ( + "errors" + "strings" + + "gopkg.in/src-d/go-git.v4/plumbing" +) + +const ( + refSpecWildcard = "*" + refSpecForce = "+" + refSpecSeparator = ":" +) + +var ( + ErrRefSpecMalformedSeparator = errors.New("malformed refspec, separators are wrong") + ErrRefSpecMalformedWildcard = errors.New("malformed refspec, mismatched number of wildcards") +) + +// RefSpec is a mapping from local branches to remote references. +// The format of the refspec is an optional +, followed by :, where +// is the pattern for references on the remote side and is where +// those references will be written locally. The + tells Git to update the +// reference even if it isn’t a fast-forward. +// eg.: "+refs/heads/*:refs/remotes/origin/*" +// +// https://git-scm.com/book/es/v2/Git-Internals-The-Refspec +type RefSpec string + +// Validate validates the RefSpec +func (s RefSpec) Validate() error { + spec := string(s) + if strings.Count(spec, refSpecSeparator) != 1 { + return ErrRefSpecMalformedSeparator + } + + sep := strings.Index(spec, refSpecSeparator) + if sep == len(spec)-1 { + return ErrRefSpecMalformedSeparator + } + + ws := strings.Count(spec[0:sep], refSpecWildcard) + wd := strings.Count(spec[sep+1:], refSpecWildcard) + if ws == wd && ws < 2 && wd < 2 { + return nil + } + + return ErrRefSpecMalformedWildcard +} + +// IsForceUpdate returns if update is allowed in non fast-forward merges. +func (s RefSpec) IsForceUpdate() bool { + return s[0] == refSpecForce[0] +} + +// IsDelete returns true if the refspec indicates a delete (empty src). +func (s RefSpec) IsDelete() bool { + return s[0] == refSpecSeparator[0] +} + +// Src return the src side. +func (s RefSpec) Src() string { + spec := string(s) + + var start int + if s.IsForceUpdate() { + start = 1 + } else { + start = 0 + } + end := strings.Index(spec, refSpecSeparator) + + return spec[start:end] +} + +// Match match the given plumbing.ReferenceName against the source. +func (s RefSpec) Match(n plumbing.ReferenceName) bool { + if !s.IsWildcard() { + return s.matchExact(n) + } + + return s.matchGlob(n) +} + +// IsWildcard returns true if the RefSpec contains a wildcard. +func (s RefSpec) IsWildcard() bool { + return strings.Contains(string(s), refSpecWildcard) +} + +func (s RefSpec) matchExact(n plumbing.ReferenceName) bool { + return s.Src() == n.String() +} + +func (s RefSpec) matchGlob(n plumbing.ReferenceName) bool { + src := s.Src() + name := n.String() + wildcard := strings.Index(src, refSpecWildcard) + + var prefix, suffix string + prefix = src[0:wildcard] + if len(src) > wildcard+1 { + suffix = src[wildcard+1:] + } + + return len(name) >= len(prefix)+len(suffix) && + strings.HasPrefix(name, prefix) && + strings.HasSuffix(name, suffix) +} + +// Dst returns the destination for the given remote reference. +func (s RefSpec) Dst(n plumbing.ReferenceName) plumbing.ReferenceName { + spec := string(s) + start := strings.Index(spec, refSpecSeparator) + 1 + dst := spec[start:] + src := s.Src() + + if !s.IsWildcard() { + return plumbing.ReferenceName(dst) + } + + name := n.String() + ws := strings.Index(src, refSpecWildcard) + wd := strings.Index(dst, refSpecWildcard) + match := name[ws : len(name)-(len(src)-(ws+1))] + + return plumbing.ReferenceName(dst[0:wd] + match + dst[wd+1:]) +} + +func (s RefSpec) Reverse() RefSpec { + spec := string(s) + separator := strings.Index(spec, refSpecSeparator) + + return RefSpec(spec[separator+1:] + refSpecSeparator + spec[:separator]) +} + +func (s RefSpec) String() string { + return string(s) +} + +// MatchAny returns true if any of the RefSpec match with the given ReferenceName. +func MatchAny(l []RefSpec, n plumbing.ReferenceName) bool { + for _, r := range l { + if r.Match(n) { + return true + } + } + + return false +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/doc.go b/vendor/gopkg.in/src-d/go-git.v4/doc.go new file mode 100644 index 0000000000..60f2261e41 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/doc.go @@ -0,0 +1,10 @@ +// A highly extensible git implementation in pure Go. +// +// go-git aims to reach the completeness of libgit2 or jgit, nowadays covers the +// majority of the plumbing read operations and some of the main write +// operations, but lacks the main porcelain operations such as merges. +// +// It is highly extensible, we have been following the open/close principle in +// its design to facilitate extensions, mainly focusing the efforts on the +// persistence of the objects. +package git // import "gopkg.in/src-d/go-git.v4" diff --git a/vendor/gopkg.in/src-d/go-git.v4/go.mod b/vendor/gopkg.in/src-d/go-git.v4/go.mod new file mode 100644 index 0000000000..6f8b3d2e62 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/go.mod @@ -0,0 +1,29 @@ +module gopkg.in/src-d/go-git.v4 + +require ( + github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 // indirect + github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 // indirect + github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 + github.com/emirpasic/gods v1.12.0 + github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect + github.com/gliderlabs/ssh v0.2.2 + github.com/google/go-cmp v0.3.0 + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 + github.com/jessevdk/go-flags v1.4.0 + github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd + github.com/mitchellh/go-homedir v1.1.0 + github.com/pelletier/go-buffruneio v0.2.0 // indirect + github.com/pkg/errors v0.8.1 // indirect + github.com/sergi/go-diff v1.0.0 + github.com/src-d/gcfg v1.4.0 + github.com/stretchr/objx v0.2.0 // indirect + github.com/xanzy/ssh-agent v0.2.1 + golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 + golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 + golang.org/x/text v0.3.2 + golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a // indirect + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 + gopkg.in/src-d/go-billy.v4 v4.3.2 + gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 + gopkg.in/warnings.v0 v0.1.2 // indirect +) diff --git a/vendor/gopkg.in/src-d/go-git.v4/go.sum b/vendor/gopkg.in/src-d/go-git.v4/go.sum new file mode 100644 index 0000000000..65551c1658 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/go.sum @@ -0,0 +1,92 @@ +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/gliderlabs/ssh v0.1.3 h1:cBU46h1lYQk5f2Z+jZbewFKy+1zzE2aUX/ilcPDAm9M= +github.com/gliderlabs/ssh v0.1.3/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8= +github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA= +github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= +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= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= +github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= +github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd h1:sMHc2rZHuzQmrbVoSpt9HgerkXPyIeCSO6k0zUMGfFk= +golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190420063019-afa5a82059c6 h1:HdqqaWmYAUI7/dmByKKEw+yxDksGSo+9GjkUc9Zp34E= +golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190502183928-7f726cade0ab h1:9RfW3ktsOZxgo9YNbBAjq1FWzc/igwEcUzZz8IXgSbk= +golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9 h1:lkiLiLBHGoH3XnqSLUIaBsilGMUjI+Uy2Xu2JLUtTas= +golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e h1:D5TXcfTk7xF7hvieo4QErS3qqCB4teTffacDWr7CI+0= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek= +gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk= +gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= +gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= +gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg= +gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= diff --git a/vendor/gopkg.in/src-d/go-git.v4/internal/revision/parser.go b/vendor/gopkg.in/src-d/go-git.v4/internal/revision/parser.go new file mode 100644 index 0000000000..d2c509e50d --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/internal/revision/parser.go @@ -0,0 +1,622 @@ +// Package revision extracts git revision from string +// More informations about revision : https://www.kernel.org/pub/software/scm/git/docs/gitrevisions.html +package revision + +import ( + "bytes" + "fmt" + "io" + "regexp" + "strconv" + "time" +) + +// ErrInvalidRevision is emitted if string doesn't match valid revision +type ErrInvalidRevision struct { + s string +} + +func (e *ErrInvalidRevision) Error() string { + return "Revision invalid : " + e.s +} + +// Revisioner represents a revision component. +// A revision is made of multiple revision components +// obtained after parsing a revision string, +// for instance revision "master~" will be converted in +// two revision components Ref and TildePath +type Revisioner interface { +} + +// Ref represents a reference name : HEAD, master +type Ref string + +// TildePath represents ~, ~{n} +type TildePath struct { + Depth int +} + +// CaretPath represents ^, ^{n} +type CaretPath struct { + Depth int +} + +// CaretReg represents ^{/foo bar} +type CaretReg struct { + Regexp *regexp.Regexp + Negate bool +} + +// CaretType represents ^{commit} +type CaretType struct { + ObjectType string +} + +// AtReflog represents @{n} +type AtReflog struct { + Depth int +} + +// AtCheckout represents @{-n} +type AtCheckout struct { + Depth int +} + +// AtUpstream represents @{upstream}, @{u} +type AtUpstream struct { + BranchName string +} + +// AtPush represents @{push} +type AtPush struct { + BranchName string +} + +// AtDate represents @{"2006-01-02T15:04:05Z"} +type AtDate struct { + Date time.Time +} + +// ColonReg represents :/foo bar +type ColonReg struct { + Regexp *regexp.Regexp + Negate bool +} + +// ColonPath represents :./ : +type ColonPath struct { + Path string +} + +// ColonStagePath represents ::/ +type ColonStagePath struct { + Path string + Stage int +} + +// Parser represents a parser +// use to tokenize and transform to revisioner chunks +// a given string +type Parser struct { + s *scanner + currentParsedChar struct { + tok token + lit string + } + unreadLastChar bool +} + +// NewParserFromString returns a new instance of parser from a string. +func NewParserFromString(s string) *Parser { + return NewParser(bytes.NewBufferString(s)) +} + +// NewParser returns a new instance of parser. +func NewParser(r io.Reader) *Parser { + return &Parser{s: newScanner(r)} +} + +// scan returns the next token from the underlying scanner +// or the last scanned token if an unscan was requested +func (p *Parser) scan() (token, string, error) { + if p.unreadLastChar { + p.unreadLastChar = false + return p.currentParsedChar.tok, p.currentParsedChar.lit, nil + } + + tok, lit, err := p.s.scan() + + p.currentParsedChar.tok, p.currentParsedChar.lit = tok, lit + + return tok, lit, err +} + +// unscan pushes the previously read token back onto the buffer. +func (p *Parser) unscan() { p.unreadLastChar = true } + +// Parse explode a revision string into revisioner chunks +func (p *Parser) Parse() ([]Revisioner, error) { + var rev Revisioner + var revs []Revisioner + var tok token + var err error + + for { + tok, _, err = p.scan() + + if err != nil { + return nil, err + } + + switch tok { + case at: + rev, err = p.parseAt() + case tilde: + rev, err = p.parseTilde() + case caret: + rev, err = p.parseCaret() + case colon: + rev, err = p.parseColon() + case eof: + err = p.validateFullRevision(&revs) + + if err != nil { + return []Revisioner{}, err + } + + return revs, nil + default: + p.unscan() + rev, err = p.parseRef() + } + + if err != nil { + return []Revisioner{}, err + } + + revs = append(revs, rev) + } +} + +// validateFullRevision ensures all revisioner chunks make a valid revision +func (p *Parser) validateFullRevision(chunks *[]Revisioner) error { + var hasReference bool + + for i, chunk := range *chunks { + switch chunk.(type) { + case Ref: + if i == 0 { + hasReference = true + } else { + return &ErrInvalidRevision{`reference must be defined once at the beginning`} + } + case AtDate: + if len(*chunks) == 1 || hasReference && len(*chunks) == 2 { + return nil + } + + return &ErrInvalidRevision{`"@" statement is not valid, could be : @{}, @{}`} + case AtReflog: + if len(*chunks) == 1 || hasReference && len(*chunks) == 2 { + return nil + } + + return &ErrInvalidRevision{`"@" statement is not valid, could be : @{}, @{}`} + case AtCheckout: + if len(*chunks) == 1 { + return nil + } + + return &ErrInvalidRevision{`"@" statement is not valid, could be : @{-}`} + case AtUpstream: + if len(*chunks) == 1 || hasReference && len(*chunks) == 2 { + return nil + } + + return &ErrInvalidRevision{`"@" statement is not valid, could be : @{upstream}, @{upstream}, @{u}, @{u}`} + case AtPush: + if len(*chunks) == 1 || hasReference && len(*chunks) == 2 { + return nil + } + + return &ErrInvalidRevision{`"@" statement is not valid, could be : @{push}, @{push}`} + case TildePath, CaretPath, CaretReg: + if !hasReference { + return &ErrInvalidRevision{`"~" or "^" statement must have a reference defined at the beginning`} + } + case ColonReg: + if len(*chunks) == 1 { + return nil + } + + return &ErrInvalidRevision{`":" statement is not valid, could be : :/`} + case ColonPath: + if i == len(*chunks)-1 && hasReference || len(*chunks) == 1 { + return nil + } + + return &ErrInvalidRevision{`":" statement is not valid, could be : :`} + case ColonStagePath: + if len(*chunks) == 1 { + return nil + } + + return &ErrInvalidRevision{`":" statement is not valid, could be : ::`} + } + } + + return nil +} + +// parseAt extract @ statements +func (p *Parser) parseAt() (Revisioner, error) { + var tok, nextTok token + var lit, nextLit string + var err error + + tok, _, err = p.scan() + + if err != nil { + return nil, err + } + + if tok != obrace { + p.unscan() + + return Ref("HEAD"), nil + } + + tok, lit, err = p.scan() + + if err != nil { + return nil, err + } + + nextTok, nextLit, err = p.scan() + + if err != nil { + return nil, err + } + + switch { + case tok == word && (lit == "u" || lit == "upstream") && nextTok == cbrace: + return AtUpstream{}, nil + case tok == word && lit == "push" && nextTok == cbrace: + return AtPush{}, nil + case tok == number && nextTok == cbrace: + n, _ := strconv.Atoi(lit) + + return AtReflog{n}, nil + case tok == minus && nextTok == number: + n, _ := strconv.Atoi(nextLit) + + t, _, err := p.scan() + + if err != nil { + return nil, err + } + + if t != cbrace { + return nil, &ErrInvalidRevision{fmt.Sprintf(`missing "}" in @{-n} structure`)} + } + + return AtCheckout{n}, nil + default: + p.unscan() + + date := lit + + for { + tok, lit, err = p.scan() + + if err != nil { + return nil, err + } + + switch { + case tok == cbrace: + t, err := time.Parse("2006-01-02T15:04:05Z", date) + + if err != nil { + return nil, &ErrInvalidRevision{fmt.Sprintf(`wrong date "%s" must fit ISO-8601 format : 2006-01-02T15:04:05Z`, date)} + } + + return AtDate{t}, nil + default: + date += lit + } + } + } +} + +// parseTilde extract ~ statements +func (p *Parser) parseTilde() (Revisioner, error) { + var tok token + var lit string + var err error + + tok, lit, err = p.scan() + + if err != nil { + return nil, err + } + + switch { + case tok == number: + n, _ := strconv.Atoi(lit) + + return TildePath{n}, nil + default: + p.unscan() + return TildePath{1}, nil + } +} + +// parseCaret extract ^ statements +func (p *Parser) parseCaret() (Revisioner, error) { + var tok token + var lit string + var err error + + tok, lit, err = p.scan() + + if err != nil { + return nil, err + } + + switch { + case tok == obrace: + r, err := p.parseCaretBraces() + + if err != nil { + return nil, err + } + + return r, nil + case tok == number: + n, _ := strconv.Atoi(lit) + + if n > 2 { + return nil, &ErrInvalidRevision{fmt.Sprintf(`"%s" found must be 0, 1 or 2 after "^"`, lit)} + } + + return CaretPath{n}, nil + default: + p.unscan() + return CaretPath{1}, nil + } +} + +// parseCaretBraces extract ^{} statements +func (p *Parser) parseCaretBraces() (Revisioner, error) { + var tok, nextTok token + var lit, _ string + start := true + var re string + var negate bool + var err error + + for { + tok, lit, err = p.scan() + + if err != nil { + return nil, err + } + + nextTok, _, err = p.scan() + + if err != nil { + return nil, err + } + + switch { + case tok == word && nextTok == cbrace && (lit == "commit" || lit == "tree" || lit == "blob" || lit == "tag" || lit == "object"): + return CaretType{lit}, nil + case re == "" && tok == cbrace: + return CaretType{"tag"}, nil + case re == "" && tok == emark && nextTok == emark: + re += lit + case re == "" && tok == emark && nextTok == minus: + negate = true + case re == "" && tok == emark: + return nil, &ErrInvalidRevision{fmt.Sprintf(`revision suffix brace component sequences starting with "/!" others than those defined are reserved`)} + case re == "" && tok == slash: + p.unscan() + case tok != slash && start: + return nil, &ErrInvalidRevision{fmt.Sprintf(`"%s" is not a valid revision suffix brace component`, lit)} + case tok != cbrace: + p.unscan() + re += lit + case tok == cbrace: + p.unscan() + + reg, err := regexp.Compile(re) + + if err != nil { + return CaretReg{}, &ErrInvalidRevision{fmt.Sprintf(`revision suffix brace component, %s`, err.Error())} + } + + return CaretReg{reg, negate}, nil + } + + start = false + } +} + +// parseColon extract : statements +func (p *Parser) parseColon() (Revisioner, error) { + var tok token + var err error + + tok, _, err = p.scan() + + if err != nil { + return nil, err + } + + switch tok { + case slash: + return p.parseColonSlash() + default: + p.unscan() + return p.parseColonDefault() + } +} + +// parseColonSlash extract :/ statements +func (p *Parser) parseColonSlash() (Revisioner, error) { + var tok, nextTok token + var lit string + var re string + var negate bool + var err error + + for { + tok, lit, err = p.scan() + + if err != nil { + return nil, err + } + + nextTok, _, err = p.scan() + + if err != nil { + return nil, err + } + + switch { + case tok == emark && nextTok == emark: + re += lit + case re == "" && tok == emark && nextTok == minus: + negate = true + case re == "" && tok == emark: + return nil, &ErrInvalidRevision{fmt.Sprintf(`revision suffix brace component sequences starting with "/!" others than those defined are reserved`)} + case tok == eof: + p.unscan() + reg, err := regexp.Compile(re) + + if err != nil { + return ColonReg{}, &ErrInvalidRevision{fmt.Sprintf(`revision suffix brace component, %s`, err.Error())} + } + + return ColonReg{reg, negate}, nil + default: + p.unscan() + re += lit + } + } +} + +// parseColonDefault extract : statements +func (p *Parser) parseColonDefault() (Revisioner, error) { + var tok token + var lit string + var path string + var stage int + var err error + var n = -1 + + tok, lit, err = p.scan() + + if err != nil { + return nil, err + } + + nextTok, _, err := p.scan() + + if err != nil { + return nil, err + } + + if tok == number && nextTok == colon { + n, _ = strconv.Atoi(lit) + } + + switch n { + case 0, 1, 2, 3: + stage = n + default: + path += lit + p.unscan() + } + + for { + tok, lit, err = p.scan() + + if err != nil { + return nil, err + } + + switch { + case tok == eof && n == -1: + return ColonPath{path}, nil + case tok == eof: + return ColonStagePath{path, stage}, nil + default: + path += lit + } + } +} + +// parseRef extract reference name +func (p *Parser) parseRef() (Revisioner, error) { + var tok, prevTok token + var lit, buf string + var endOfRef bool + var err error + + for { + tok, lit, err = p.scan() + + if err != nil { + return nil, err + } + + switch tok { + case eof, at, colon, tilde, caret: + endOfRef = true + } + + err := p.checkRefFormat(tok, lit, prevTok, buf, endOfRef) + + if err != nil { + return "", err + } + + if endOfRef { + p.unscan() + return Ref(buf), nil + } + + buf += lit + prevTok = tok + } +} + +// checkRefFormat ensure reference name follow rules defined here : +// https://git-scm.com/docs/git-check-ref-format +func (p *Parser) checkRefFormat(token token, literal string, previousToken token, buffer string, endOfRef bool) error { + switch token { + case aslash, space, control, qmark, asterisk, obracket: + return &ErrInvalidRevision{fmt.Sprintf(`must not contains "%s"`, literal)} + } + + switch { + case (token == dot || token == slash) && buffer == "": + return &ErrInvalidRevision{fmt.Sprintf(`must not start with "%s"`, literal)} + case previousToken == slash && endOfRef: + return &ErrInvalidRevision{`must not end with "/"`} + case previousToken == dot && endOfRef: + return &ErrInvalidRevision{`must not end with "."`} + case token == dot && previousToken == slash: + return &ErrInvalidRevision{`must not contains "/."`} + case previousToken == dot && token == dot: + return &ErrInvalidRevision{`must not contains ".."`} + case previousToken == slash && token == slash: + return &ErrInvalidRevision{`must not contains consecutively "/"`} + case (token == slash || endOfRef) && len(buffer) > 4 && buffer[len(buffer)-5:] == ".lock": + return &ErrInvalidRevision{"cannot end with .lock"} + } + + return nil +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/internal/revision/scanner.go b/vendor/gopkg.in/src-d/go-git.v4/internal/revision/scanner.go new file mode 100644 index 0000000000..fb5f333f7f --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/internal/revision/scanner.go @@ -0,0 +1,117 @@ +package revision + +import ( + "bufio" + "io" + "unicode" +) + +// runeCategoryValidator takes a rune as input and +// validates it belongs to a rune category +type runeCategoryValidator func(r rune) bool + +// tokenizeExpression aggegates a series of runes matching check predicate into a single +// string and provides given tokenType as token type +func tokenizeExpression(ch rune, tokenType token, check runeCategoryValidator, r *bufio.Reader) (token, string, error) { + var data []rune + data = append(data, ch) + + for { + c, _, err := r.ReadRune() + + if c == zeroRune { + break + } + + if err != nil { + return tokenError, "", err + } + + if check(c) { + data = append(data, c) + } else { + err := r.UnreadRune() + + if err != nil { + return tokenError, "", err + } + + return tokenType, string(data), nil + } + } + + return tokenType, string(data), nil +} + +var zeroRune = rune(0) + +// scanner represents a lexical scanner. +type scanner struct { + r *bufio.Reader +} + +// newScanner returns a new instance of scanner. +func newScanner(r io.Reader) *scanner { + return &scanner{r: bufio.NewReader(r)} +} + +// Scan extracts tokens and their strings counterpart +// from the reader +func (s *scanner) scan() (token, string, error) { + ch, _, err := s.r.ReadRune() + + if err != nil && err != io.EOF { + return tokenError, "", err + } + + switch ch { + case zeroRune: + return eof, "", nil + case ':': + return colon, string(ch), nil + case '~': + return tilde, string(ch), nil + case '^': + return caret, string(ch), nil + case '.': + return dot, string(ch), nil + case '/': + return slash, string(ch), nil + case '{': + return obrace, string(ch), nil + case '}': + return cbrace, string(ch), nil + case '-': + return minus, string(ch), nil + case '@': + return at, string(ch), nil + case '\\': + return aslash, string(ch), nil + case '?': + return qmark, string(ch), nil + case '*': + return asterisk, string(ch), nil + case '[': + return obracket, string(ch), nil + case '!': + return emark, string(ch), nil + } + + if unicode.IsSpace(ch) { + return space, string(ch), nil + } + + if unicode.IsControl(ch) { + return control, string(ch), nil + } + + if unicode.IsLetter(ch) { + return tokenizeExpression(ch, word, unicode.IsLetter, s.r) + } + + if unicode.IsNumber(ch) { + return tokenizeExpression(ch, number, unicode.IsNumber, s.r) + } + + return tokenError, string(ch), nil +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/internal/revision/token.go b/vendor/gopkg.in/src-d/go-git.v4/internal/revision/token.go new file mode 100644 index 0000000000..abc4048869 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/internal/revision/token.go @@ -0,0 +1,28 @@ +package revision + +// token represents a entity extracted from string parsing +type token int + +const ( + eof token = iota + + aslash + asterisk + at + caret + cbrace + colon + control + dot + emark + minus + number + obrace + obracket + qmark + slash + space + tilde + tokenError + word +) diff --git a/vendor/gopkg.in/src-d/go-git.v4/internal/url/url.go b/vendor/gopkg.in/src-d/go-git.v4/internal/url/url.go new file mode 100644 index 0000000000..0f0d709d93 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/internal/url/url.go @@ -0,0 +1,37 @@ +package url + +import ( + "regexp" +) + +var ( + isSchemeRegExp = regexp.MustCompile(`^[^:]+://`) + scpLikeUrlRegExp = regexp.MustCompile(`^(?:(?P[^@]+)@)?(?P[^:\s]+):(?:(?P[0-9]{1,5})/)?(?P[^\\].*)$`) +) + +// MatchesScheme returns true if the given string matches a URL-like +// format scheme. +func MatchesScheme(url string) bool { + return isSchemeRegExp.MatchString(url) +} + +// MatchesScpLike returns true if the given string matches an SCP-like +// format scheme. +func MatchesScpLike(url string) bool { + return scpLikeUrlRegExp.MatchString(url) +} + +// FindScpLikeComponents returns the user, host, port and path of the +// given SCP-like URL. +func FindScpLikeComponents(url string) (user, host, port, path string) { + m := scpLikeUrlRegExp.FindStringSubmatch(url) + return m[1], m[2], m[3], m[4] +} + +// IsLocalEndpoint returns true if the given URL string specifies a +// local file endpoint. For example, on a Linux machine, +// `/home/user/src/go-git` would match as a local endpoint, but +// `https://github.com/src-d/go-git` would not. +func IsLocalEndpoint(url string) bool { + return !MatchesScheme(url) && !MatchesScpLike(url) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/object_walker.go b/vendor/gopkg.in/src-d/go-git.v4/object_walker.go new file mode 100644 index 0000000000..f8b19cdb09 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/object_walker.go @@ -0,0 +1,104 @@ +package git + +import ( + "fmt" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/filemode" + "gopkg.in/src-d/go-git.v4/plumbing/object" + "gopkg.in/src-d/go-git.v4/storage" +) + +type objectWalker struct { + Storer storage.Storer + // seen is the set of objects seen in the repo. + // seen map can become huge if walking over large + // repos. Thus using struct{} as the value type. + seen map[plumbing.Hash]struct{} +} + +func newObjectWalker(s storage.Storer) *objectWalker { + return &objectWalker{s, map[plumbing.Hash]struct{}{}} +} + +// walkAllRefs walks all (hash) refererences from the repo. +func (p *objectWalker) walkAllRefs() error { + // Walk over all the references in the repo. + it, err := p.Storer.IterReferences() + if err != nil { + return err + } + defer it.Close() + err = it.ForEach(func(ref *plumbing.Reference) error { + // Exit this iteration early for non-hash references. + if ref.Type() != plumbing.HashReference { + return nil + } + return p.walkObjectTree(ref.Hash()) + }) + return err +} + +func (p *objectWalker) isSeen(hash plumbing.Hash) bool { + _, seen := p.seen[hash] + return seen +} + +func (p *objectWalker) add(hash plumbing.Hash) { + p.seen[hash] = struct{}{} +} + +// walkObjectTree walks over all objects and remembers references +// to them in the objectWalker. This is used instead of the revlist +// walks because memory usage is tight with huge repos. +func (p *objectWalker) walkObjectTree(hash plumbing.Hash) error { + // Check if we have already seen, and mark this object + if p.isSeen(hash) { + return nil + } + p.add(hash) + // Fetch the object. + obj, err := object.GetObject(p.Storer, hash) + if err != nil { + return fmt.Errorf("Getting object %s failed: %v", hash, err) + } + // Walk all children depending on object type. + switch obj := obj.(type) { + case *object.Commit: + err = p.walkObjectTree(obj.TreeHash) + if err != nil { + return err + } + for _, h := range obj.ParentHashes { + err = p.walkObjectTree(h) + if err != nil { + return err + } + } + case *object.Tree: + for i := range obj.Entries { + // Shortcut for blob objects: + // 'or' the lower bits of a mode and check that it + // it matches a filemode.Executable. The type information + // is in the higher bits, but this is the cleanest way + // to handle plain files with different modes. + // Other non-tree objects are somewhat rare, so they + // are not special-cased. + if obj.Entries[i].Mode|0755 == filemode.Executable { + p.add(obj.Entries[i].Hash) + continue + } + // Normal walk for sub-trees (and symlinks etc). + err = p.walkObjectTree(obj.Entries[i].Hash) + if err != nil { + return err + } + } + case *object.Tag: + return p.walkObjectTree(obj.Target) + default: + // Error out on unhandled object types. + return fmt.Errorf("Unknown object %X %s %T\n", obj.ID(), obj.Type(), obj) + } + return nil +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/options.go b/vendor/gopkg.in/src-d/go-git.v4/options.go new file mode 100644 index 0000000000..0f728e7c27 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/options.go @@ -0,0 +1,492 @@ +package git + +import ( + "errors" + "regexp" + "strings" + + "golang.org/x/crypto/openpgp" + "gopkg.in/src-d/go-git.v4/config" + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/object" + "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband" + "gopkg.in/src-d/go-git.v4/plumbing/transport" +) + +// SubmoduleRescursivity defines how depth will affect any submodule recursive +// operation. +type SubmoduleRescursivity uint + +const ( + // DefaultRemoteName name of the default Remote, just like git command. + DefaultRemoteName = "origin" + + // NoRecurseSubmodules disables the recursion for a submodule operation. + NoRecurseSubmodules SubmoduleRescursivity = 0 + // DefaultSubmoduleRecursionDepth allow recursion in a submodule operation. + DefaultSubmoduleRecursionDepth SubmoduleRescursivity = 10 +) + +var ( + ErrMissingURL = errors.New("URL field is required") +) + +// CloneOptions describes how a clone should be performed. +type CloneOptions struct { + // The (possibly remote) repository URL to clone from. + URL string + // Auth credentials, if required, to use with the remote repository. + Auth transport.AuthMethod + // Name of the remote to be added, by default `origin`. + RemoteName string + // Remote branch to clone. + ReferenceName plumbing.ReferenceName + // Fetch only ReferenceName if true. + SingleBranch bool + // No checkout of HEAD after clone if true. + NoCheckout bool + // Limit fetching to the specified number of commits. + Depth int + // RecurseSubmodules after the clone is created, initialize all submodules + // within, using their default settings. This option is ignored if the + // cloned repository does not have a worktree. + RecurseSubmodules SubmoduleRescursivity + // Progress is where the human readable information sent by the server is + // stored, if nil nothing is stored and the capability (if supported) + // no-progress, is sent to the server to avoid send this information. + Progress sideband.Progress + // Tags describe how the tags will be fetched from the remote repository, + // by default is AllTags. + Tags TagMode +} + +// Validate validates the fields and sets the default values. +func (o *CloneOptions) Validate() error { + if o.URL == "" { + return ErrMissingURL + } + + if o.RemoteName == "" { + o.RemoteName = DefaultRemoteName + } + + if o.ReferenceName == "" { + o.ReferenceName = plumbing.HEAD + } + + if o.Tags == InvalidTagMode { + o.Tags = AllTags + } + + return nil +} + +// PullOptions describes how a pull should be performed. +type PullOptions struct { + // Name of the remote to be pulled. If empty, uses the default. + RemoteName string + // Remote branch to clone. If empty, uses HEAD. + ReferenceName plumbing.ReferenceName + // Fetch only ReferenceName if true. + SingleBranch bool + // Limit fetching to the specified number of commits. + Depth int + // Auth credentials, if required, to use with the remote repository. + Auth transport.AuthMethod + // RecurseSubmodules controls if new commits of all populated submodules + // should be fetched too. + RecurseSubmodules SubmoduleRescursivity + // Progress is where the human readable information sent by the server is + // stored, if nil nothing is stored and the capability (if supported) + // no-progress, is sent to the server to avoid send this information. + Progress sideband.Progress + // Force allows the pull to update a local branch even when the remote + // branch does not descend from it. + Force bool +} + +// Validate validates the fields and sets the default values. +func (o *PullOptions) Validate() error { + if o.RemoteName == "" { + o.RemoteName = DefaultRemoteName + } + + if o.ReferenceName == "" { + o.ReferenceName = plumbing.HEAD + } + + return nil +} + +type TagMode int + +const ( + InvalidTagMode TagMode = iota + // TagFollowing any tag that points into the histories being fetched is also + // fetched. TagFollowing requires a server with `include-tag` capability + // in order to fetch the annotated tags objects. + TagFollowing + // AllTags fetch all tags from the remote (i.e., fetch remote tags + // refs/tags/* into local tags with the same name) + AllTags + //NoTags fetch no tags from the remote at all + NoTags +) + +// FetchOptions describes how a fetch should be performed +type FetchOptions struct { + // Name of the remote to fetch from. Defaults to origin. + RemoteName string + RefSpecs []config.RefSpec + // Depth limit fetching to the specified number of commits from the tip of + // each remote branch history. + Depth int + // Auth credentials, if required, to use with the remote repository. + Auth transport.AuthMethod + // Progress is where the human readable information sent by the server is + // stored, if nil nothing is stored and the capability (if supported) + // no-progress, is sent to the server to avoid send this information. + Progress sideband.Progress + // Tags describe how the tags will be fetched from the remote repository, + // by default is TagFollowing. + Tags TagMode + // Force allows the fetch to update a local branch even when the remote + // branch does not descend from it. + Force bool +} + +// Validate validates the fields and sets the default values. +func (o *FetchOptions) Validate() error { + if o.RemoteName == "" { + o.RemoteName = DefaultRemoteName + } + + if o.Tags == InvalidTagMode { + o.Tags = TagFollowing + } + + for _, r := range o.RefSpecs { + if err := r.Validate(); err != nil { + return err + } + } + + return nil +} + +// PushOptions describes how a push should be performed. +type PushOptions struct { + // RemoteName is the name of the remote to be pushed to. + RemoteName string + // RefSpecs specify what destination ref to update with what source + // object. A refspec with empty src can be used to delete a reference. + RefSpecs []config.RefSpec + // Auth credentials, if required, to use with the remote repository. + Auth transport.AuthMethod + // Progress is where the human readable information sent by the server is + // stored, if nil nothing is stored. + Progress sideband.Progress + // Prune specify that remote refs that match given RefSpecs and that do + // not exist locally will be removed. + Prune bool +} + +// Validate validates the fields and sets the default values. +func (o *PushOptions) Validate() error { + if o.RemoteName == "" { + o.RemoteName = DefaultRemoteName + } + + if len(o.RefSpecs) == 0 { + o.RefSpecs = []config.RefSpec{ + config.RefSpec(config.DefaultPushRefSpec), + } + } + + for _, r := range o.RefSpecs { + if err := r.Validate(); err != nil { + return err + } + } + + return nil +} + +// SubmoduleUpdateOptions describes how a submodule update should be performed. +type SubmoduleUpdateOptions struct { + // Init, if true initializes the submodules recorded in the index. + Init bool + // NoFetch tell to the update command to not fetch new objects from the + // remote site. + NoFetch bool + // RecurseSubmodules the update is performed not only in the submodules of + // the current repository but also in any nested submodules inside those + // submodules (and so on). Until the SubmoduleRescursivity is reached. + RecurseSubmodules SubmoduleRescursivity + // Auth credentials, if required, to use with the remote repository. + Auth transport.AuthMethod +} + +var ( + ErrBranchHashExclusive = errors.New("Branch and Hash are mutually exclusive") + ErrCreateRequiresBranch = errors.New("Branch is mandatory when Create is used") +) + +// CheckoutOptions describes how a checkout operation should be performed. +type CheckoutOptions struct { + // Hash is the hash of the commit to be checked out. If used, HEAD will be + // in detached mode. If Create is not used, Branch and Hash are mutually + // exclusive. + Hash plumbing.Hash + // Branch to be checked out, if Branch and Hash are empty is set to `master`. + Branch plumbing.ReferenceName + // Create a new branch named Branch and start it at Hash. + Create bool + // Force, if true when switching branches, proceed even if the index or the + // working tree differs from HEAD. This is used to throw away local changes + Force bool + // Keep, if true when switching branches, local changes (the index or the + // working tree changes) will be kept so that they can be committed to the + // target branch. Force and Keep are mutually exclusive, should not be both + // set to true. + Keep bool +} + +// Validate validates the fields and sets the default values. +func (o *CheckoutOptions) Validate() error { + if !o.Create && !o.Hash.IsZero() && o.Branch != "" { + return ErrBranchHashExclusive + } + + if o.Create && o.Branch == "" { + return ErrCreateRequiresBranch + } + + if o.Branch == "" { + o.Branch = plumbing.Master + } + + return nil +} + +// ResetMode defines the mode of a reset operation. +type ResetMode int8 + +const ( + // MixedReset resets the index but not the working tree (i.e., the changed + // files are preserved but not marked for commit) and reports what has not + // been updated. This is the default action. + MixedReset ResetMode = iota + // HardReset resets the index and working tree. Any changes to tracked files + // in the working tree are discarded. + HardReset + // MergeReset resets the index and updates the files in the working tree + // that are different between Commit and HEAD, but keeps those which are + // different between the index and working tree (i.e. which have changes + // which have not been added). + // + // If a file that is different between Commit and the index has unstaged + // changes, reset is aborted. + MergeReset + // SoftReset does not touch the index file or the working tree at all (but + // resets the head to , just like all modes do). This leaves all + // your changed files "Changes to be committed", as git status would put it. + SoftReset +) + +// ResetOptions describes how a reset operation should be performed. +type ResetOptions struct { + // Commit, if commit is present set the current branch head (HEAD) to it. + Commit plumbing.Hash + // Mode, form resets the current branch head to Commit and possibly updates + // the index (resetting it to the tree of Commit) and the working tree + // depending on Mode. If empty MixedReset is used. + Mode ResetMode +} + +// Validate validates the fields and sets the default values. +func (o *ResetOptions) Validate(r *Repository) error { + if o.Commit == plumbing.ZeroHash { + ref, err := r.Head() + if err != nil { + return err + } + + o.Commit = ref.Hash() + } + + return nil +} + +type LogOrder int8 + +const ( + LogOrderDefault LogOrder = iota + LogOrderDFS + LogOrderDFSPost + LogOrderBSF + LogOrderCommitterTime +) + +// LogOptions describes how a log action should be performed. +type LogOptions struct { + // When the From option is set the log will only contain commits + // reachable from it. If this option is not set, HEAD will be used as + // the default From. + From plumbing.Hash + + // The default traversal algorithm is Depth-first search + // set Order=LogOrderCommitterTime for ordering by committer time (more compatible with `git log`) + // set Order=LogOrderBSF for Breadth-first search + Order LogOrder + + // Show only those commits in which the specified file was inserted/updated. + // It is equivalent to running `git log -- `. + FileName *string + + // Pretend as if all the refs in refs/, along with HEAD, are listed on the command line as . + // It is equivalent to running `git log --all`. + // If set on true, the From option will be ignored. + All bool +} + +var ( + ErrMissingAuthor = errors.New("author field is required") +) + +// CommitOptions describes how a commit operation should be performed. +type CommitOptions struct { + // All automatically stage files that have been modified and deleted, but + // new files you have not told Git about are not affected. + All bool + // Author is the author's signature of the commit. + Author *object.Signature + // Committer is the committer's signature of the commit. If Committer is + // nil the Author signature is used. + Committer *object.Signature + // Parents are the parents commits for the new commit, by default when + // len(Parents) is zero, the hash of HEAD reference is used. + Parents []plumbing.Hash + // SignKey denotes a key to sign the commit with. A nil value here means the + // commit will not be signed. The private key must be present and already + // decrypted. + SignKey *openpgp.Entity +} + +// Validate validates the fields and sets the default values. +func (o *CommitOptions) Validate(r *Repository) error { + if o.Author == nil { + return ErrMissingAuthor + } + + if o.Committer == nil { + o.Committer = o.Author + } + + if len(o.Parents) == 0 { + head, err := r.Head() + if err != nil && err != plumbing.ErrReferenceNotFound { + return err + } + + if head != nil { + o.Parents = []plumbing.Hash{head.Hash()} + } + } + + return nil +} + +var ( + ErrMissingName = errors.New("name field is required") + ErrMissingTagger = errors.New("tagger field is required") + ErrMissingMessage = errors.New("message field is required") +) + +// CreateTagOptions describes how a tag object should be created. +type CreateTagOptions struct { + // Tagger defines the signature of the tag creator. + Tagger *object.Signature + // Message defines the annotation of the tag. It is canonicalized during + // validation into the format expected by git - no leading whitespace and + // ending in a newline. + Message string + // SignKey denotes a key to sign the tag with. A nil value here means the tag + // will not be signed. The private key must be present and already decrypted. + SignKey *openpgp.Entity +} + +// Validate validates the fields and sets the default values. +func (o *CreateTagOptions) Validate(r *Repository, hash plumbing.Hash) error { + if o.Tagger == nil { + return ErrMissingTagger + } + + if o.Message == "" { + return ErrMissingMessage + } + + // Canonicalize the message into the expected message format. + o.Message = strings.TrimSpace(o.Message) + "\n" + + return nil +} + +// ListOptions describes how a remote list should be performed. +type ListOptions struct { + // Auth credentials, if required, to use with the remote repository. + Auth transport.AuthMethod +} + +// CleanOptions describes how a clean should be performed. +type CleanOptions struct { + Dir bool +} + +// GrepOptions describes how a grep should be performed. +type GrepOptions struct { + // Patterns are compiled Regexp objects to be matched. + Patterns []*regexp.Regexp + // InvertMatch selects non-matching lines. + InvertMatch bool + // CommitHash is the hash of the commit from which worktree should be derived. + CommitHash plumbing.Hash + // ReferenceName is the branch or tag name from which worktree should be derived. + ReferenceName plumbing.ReferenceName + // PathSpecs are compiled Regexp objects of pathspec to use in the matching. + PathSpecs []*regexp.Regexp +} + +var ( + ErrHashOrReference = errors.New("ambiguous options, only one of CommitHash or ReferenceName can be passed") +) + +// Validate validates the fields and sets the default values. +func (o *GrepOptions) Validate(w *Worktree) error { + if !o.CommitHash.IsZero() && o.ReferenceName != "" { + return ErrHashOrReference + } + + // If none of CommitHash and ReferenceName are provided, set commit hash of + // the repository's head. + if o.CommitHash.IsZero() && o.ReferenceName == "" { + ref, err := w.r.Head() + if err != nil { + return err + } + o.CommitHash = ref.Hash() + } + + return nil +} + +// PlainOpenOptions describes how opening a plain repository should be +// performed. +type PlainOpenOptions struct { + // DetectDotGit defines whether parent directories should be + // walked until a .git directory or file is found. + DetectDotGit bool +} + +// Validate validates the fields and sets the default values. +func (o *PlainOpenOptions) Validate() error { return nil } diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/cache/buffer_lru.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/cache/buffer_lru.go new file mode 100644 index 0000000000..acaf195203 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/cache/buffer_lru.go @@ -0,0 +1,98 @@ +package cache + +import ( + "container/list" + "sync" +) + +// BufferLRU implements an object cache with an LRU eviction policy and a +// maximum size (measured in object size). +type BufferLRU struct { + MaxSize FileSize + + actualSize FileSize + ll *list.List + cache map[int64]*list.Element + mut sync.Mutex +} + +// NewBufferLRU creates a new BufferLRU with the given maximum size. The maximum +// size will never be exceeded. +func NewBufferLRU(maxSize FileSize) *BufferLRU { + return &BufferLRU{MaxSize: maxSize} +} + +// NewBufferLRUDefault creates a new BufferLRU with the default cache size. +func NewBufferLRUDefault() *BufferLRU { + return &BufferLRU{MaxSize: DefaultMaxSize} +} + +type buffer struct { + Key int64 + Slice []byte +} + +// Put puts a buffer into the cache. If the buffer is already in the cache, it +// will be marked as used. Otherwise, it will be inserted. A buffers might +// be evicted to make room for the new one. +func (c *BufferLRU) Put(key int64, slice []byte) { + c.mut.Lock() + defer c.mut.Unlock() + + if c.cache == nil { + c.actualSize = 0 + c.cache = make(map[int64]*list.Element, 1000) + c.ll = list.New() + } + + bufSize := FileSize(len(slice)) + if ee, ok := c.cache[key]; ok { + oldBuf := ee.Value.(buffer) + // in this case bufSize is a delta: new size - old size + bufSize -= FileSize(len(oldBuf.Slice)) + c.ll.MoveToFront(ee) + ee.Value = buffer{key, slice} + } else { + if bufSize > c.MaxSize { + return + } + ee := c.ll.PushFront(buffer{key, slice}) + c.cache[key] = ee + } + + c.actualSize += bufSize + for c.actualSize > c.MaxSize { + last := c.ll.Back() + lastObj := last.Value.(buffer) + lastSize := FileSize(len(lastObj.Slice)) + + c.ll.Remove(last) + delete(c.cache, lastObj.Key) + c.actualSize -= lastSize + } +} + +// Get returns a buffer by its key. It marks the buffer as used. If the buffer +// is not in the cache, (nil, false) will be returned. +func (c *BufferLRU) Get(key int64) ([]byte, bool) { + c.mut.Lock() + defer c.mut.Unlock() + + ee, ok := c.cache[key] + if !ok { + return nil, false + } + + c.ll.MoveToFront(ee) + return ee.Value.(buffer).Slice, true +} + +// Clear the content of this buffer cache. +func (c *BufferLRU) Clear() { + c.mut.Lock() + defer c.mut.Unlock() + + c.ll = nil + c.cache = nil + c.actualSize = 0 +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/cache/common.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/cache/common.go new file mode 100644 index 0000000000..2b7f36a56f --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/cache/common.go @@ -0,0 +1,39 @@ +package cache + +import "gopkg.in/src-d/go-git.v4/plumbing" + +const ( + Byte FileSize = 1 << (iota * 10) + KiByte + MiByte + GiByte +) + +type FileSize int64 + +const DefaultMaxSize FileSize = 96 * MiByte + +// Object is an interface to a object cache. +type Object interface { + // Put puts the given object into the cache. Whether this object will + // actually be put into the cache or not is implementation specific. + Put(o plumbing.EncodedObject) + // Get gets an object from the cache given its hash. The second return value + // is true if the object was returned, and false otherwise. + Get(k plumbing.Hash) (plumbing.EncodedObject, bool) + // Clear clears every object from the cache. + Clear() +} + +// Buffer is an interface to a buffer cache. +type Buffer interface { + // Put puts a buffer into the cache. If the buffer is already in the cache, + // it will be marked as used. Otherwise, it will be inserted. Buffer might + // be evicted to make room for the new one. + Put(key int64, slice []byte) + // Get returns a buffer by its key. It marks the buffer as used. If the + // buffer is not in the cache, (nil, false) will be returned. + Get(key int64) ([]byte, bool) + // Clear clears every object from the cache. + Clear() +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/cache/object_lru.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/cache/object_lru.go new file mode 100644 index 0000000000..cd3712b7d7 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/cache/object_lru.go @@ -0,0 +1,101 @@ +package cache + +import ( + "container/list" + "sync" + + "gopkg.in/src-d/go-git.v4/plumbing" +) + +// ObjectLRU implements an object cache with an LRU eviction policy and a +// maximum size (measured in object size). +type ObjectLRU struct { + MaxSize FileSize + + actualSize FileSize + ll *list.List + cache map[interface{}]*list.Element + mut sync.Mutex +} + +// NewObjectLRU creates a new ObjectLRU with the given maximum size. The maximum +// size will never be exceeded. +func NewObjectLRU(maxSize FileSize) *ObjectLRU { + return &ObjectLRU{MaxSize: maxSize} +} + +// NewObjectLRUDefault creates a new ObjectLRU with the default cache size. +func NewObjectLRUDefault() *ObjectLRU { + return &ObjectLRU{MaxSize: DefaultMaxSize} +} + +// Put puts an object into the cache. If the object is already in the cache, it +// will be marked as used. Otherwise, it will be inserted. A single object might +// be evicted to make room for the new object. +func (c *ObjectLRU) Put(obj plumbing.EncodedObject) { + c.mut.Lock() + defer c.mut.Unlock() + + if c.cache == nil { + c.actualSize = 0 + c.cache = make(map[interface{}]*list.Element, 1000) + c.ll = list.New() + } + + objSize := FileSize(obj.Size()) + key := obj.Hash() + if ee, ok := c.cache[key]; ok { + oldObj := ee.Value.(plumbing.EncodedObject) + // in this case objSize is a delta: new size - old size + objSize -= FileSize(oldObj.Size()) + c.ll.MoveToFront(ee) + ee.Value = obj + } else { + if objSize > c.MaxSize { + return + } + ee := c.ll.PushFront(obj) + c.cache[key] = ee + } + + c.actualSize += objSize + for c.actualSize > c.MaxSize { + last := c.ll.Back() + if last == nil { + c.actualSize = 0 + break + } + + lastObj := last.Value.(plumbing.EncodedObject) + lastSize := FileSize(lastObj.Size()) + + c.ll.Remove(last) + delete(c.cache, lastObj.Hash()) + c.actualSize -= lastSize + } +} + +// Get returns an object by its hash. It marks the object as used. If the object +// is not in the cache, (nil, false) will be returned. +func (c *ObjectLRU) Get(k plumbing.Hash) (plumbing.EncodedObject, bool) { + c.mut.Lock() + defer c.mut.Unlock() + + ee, ok := c.cache[k] + if !ok { + return nil, false + } + + c.ll.MoveToFront(ee) + return ee.Value.(plumbing.EncodedObject), true +} + +// Clear the content of this object cache. +func (c *ObjectLRU) Clear() { + c.mut.Lock() + defer c.mut.Unlock() + + c.ll = nil + c.cache = nil + c.actualSize = 0 +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/error.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/error.go new file mode 100644 index 0000000000..a3ebed3f6c --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/error.go @@ -0,0 +1,35 @@ +package plumbing + +import "fmt" + +type PermanentError struct { + Err error +} + +func NewPermanentError(err error) *PermanentError { + if err == nil { + return nil + } + + return &PermanentError{Err: err} +} + +func (e *PermanentError) Error() string { + return fmt.Sprintf("permanent client error: %s", e.Err.Error()) +} + +type UnexpectedError struct { + Err error +} + +func NewUnexpectedError(err error) *UnexpectedError { + if err == nil { + return nil + } + + return &UnexpectedError{Err: err} +} + +func (e *UnexpectedError) Error() string { + return fmt.Sprintf("unexpected client error: %s", e.Err.Error()) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/filemode/filemode.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/filemode/filemode.go new file mode 100644 index 0000000000..0994bc4d75 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/filemode/filemode.go @@ -0,0 +1,188 @@ +package filemode + +import ( + "encoding/binary" + "fmt" + "os" + "strconv" +) + +// A FileMode represents the kind of tree entries used by git. It +// resembles regular file systems modes, although FileModes are +// considerably simpler (there are not so many), and there are some, +// like Submodule that has no file system equivalent. +type FileMode uint32 + +const ( + // Empty is used as the FileMode of tree elements when comparing + // trees in the following situations: + // + // - the mode of tree elements before their creation. - the mode of + // tree elements after their deletion. - the mode of unmerged + // elements when checking the index. + // + // Empty has no file system equivalent. As Empty is the zero value + // of FileMode, it is also returned by New and + // NewFromOsNewFromOSFileMode along with an error, when they fail. + Empty FileMode = 0 + // Dir represent a Directory. + Dir FileMode = 0040000 + // Regular represent non-executable files. Please note this is not + // the same as golang regular files, which include executable files. + Regular FileMode = 0100644 + // Deprecated represent non-executable files with the group writable + // bit set. This mode was supported by the first versions of git, + // but it has been deprecatred nowadays. This library uses them + // internally, so you can read old packfiles, but will treat them as + // Regulars when interfacing with the outside world. This is the + // standard git behaviuor. + Deprecated FileMode = 0100664 + // Executable represents executable files. + Executable FileMode = 0100755 + // Symlink represents symbolic links to files. + Symlink FileMode = 0120000 + // Submodule represents git submodules. This mode has no file system + // equivalent. + Submodule FileMode = 0160000 +) + +// New takes the octal string representation of a FileMode and returns +// the FileMode and a nil error. If the string can not be parsed to a +// 32 bit unsigned octal number, it returns Empty and the parsing error. +// +// Example: "40000" means Dir, "100644" means Regular. +// +// Please note this function does not check if the returned FileMode +// is valid in git or if it is malformed. For instance, "1" will +// return the malformed FileMode(1) and a nil error. +func New(s string) (FileMode, error) { + n, err := strconv.ParseUint(s, 8, 32) + if err != nil { + return Empty, err + } + + return FileMode(n), nil +} + +// NewFromOSFileMode returns the FileMode used by git to represent +// the provided file system modes and a nil error on success. If the +// file system mode cannot be mapped to any valid git mode (as with +// sockets or named pipes), it will return Empty and an error. +// +// Note that some git modes cannot be generated from os.FileModes, like +// Deprecated and Submodule; while Empty will be returned, along with an +// error, only when the method fails. +func NewFromOSFileMode(m os.FileMode) (FileMode, error) { + if m.IsRegular() { + if isSetTemporary(m) { + return Empty, fmt.Errorf("no equivalent git mode for %s", m) + } + if isSetCharDevice(m) { + return Empty, fmt.Errorf("no equivalent git mode for %s", m) + } + if isSetUserExecutable(m) { + return Executable, nil + } + return Regular, nil + } + + if m.IsDir() { + return Dir, nil + } + + if isSetSymLink(m) { + return Symlink, nil + } + + return Empty, fmt.Errorf("no equivalent git mode for %s", m) +} + +func isSetCharDevice(m os.FileMode) bool { + return m&os.ModeCharDevice != 0 +} + +func isSetTemporary(m os.FileMode) bool { + return m&os.ModeTemporary != 0 +} + +func isSetUserExecutable(m os.FileMode) bool { + return m&0100 != 0 +} + +func isSetSymLink(m os.FileMode) bool { + return m&os.ModeSymlink != 0 +} + +// Bytes return a slice of 4 bytes with the mode in little endian +// encoding. +func (m FileMode) Bytes() []byte { + ret := make([]byte, 4) + binary.LittleEndian.PutUint32(ret, uint32(m)) + return ret[:] +} + +// IsMalformed returns if the FileMode should not appear in a git packfile, +// this is: Empty and any other mode not mentioned as a constant in this +// package. +func (m FileMode) IsMalformed() bool { + return m != Dir && + m != Regular && + m != Deprecated && + m != Executable && + m != Symlink && + m != Submodule +} + +// String returns the FileMode as a string in the standatd git format, +// this is, an octal number padded with ceros to 7 digits. Malformed +// modes are printed in that same format, for easier debugging. +// +// Example: Regular is "0100644", Empty is "0000000". +func (m FileMode) String() string { + return fmt.Sprintf("%07o", uint32(m)) +} + +// IsRegular returns if the FileMode represents that of a regular file, +// this is, either Regular or Deprecated. Please note that Executable +// are not regular even though in the UNIX tradition, they usually are: +// See the IsFile method. +func (m FileMode) IsRegular() bool { + return m == Regular || + m == Deprecated +} + +// IsFile returns if the FileMode represents that of a file, this is, +// Regular, Deprecated, Excutable or Link. +func (m FileMode) IsFile() bool { + return m == Regular || + m == Deprecated || + m == Executable || + m == Symlink +} + +// ToOSFileMode returns the os.FileMode to be used when creating file +// system elements with the given git mode and a nil error on success. +// +// When the provided mode cannot be mapped to a valid file system mode +// (e.g. Submodule) it returns os.FileMode(0) and an error. +// +// The returned file mode does not take into account the umask. +func (m FileMode) ToOSFileMode() (os.FileMode, error) { + switch m { + case Dir: + return os.ModePerm | os.ModeDir, nil + case Submodule: + return os.ModePerm | os.ModeDir, nil + case Regular: + return os.FileMode(0644), nil + // Deprecated is no longer allowed: treated as a Regular instead + case Deprecated: + return os.FileMode(0644), nil + case Executable: + return os.FileMode(0755), nil + case Symlink: + return os.ModePerm | os.ModeSymlink, nil + } + + return os.FileMode(0), fmt.Errorf("malformed mode (%s)", m) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/common.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/common.go new file mode 100644 index 0000000000..8f98ad1741 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/common.go @@ -0,0 +1,99 @@ +package config + +// New creates a new config instance. +func New() *Config { + return &Config{} +} + +// Config contains all the sections, comments and includes from a config file. +type Config struct { + Comment *Comment + Sections Sections + Includes Includes +} + +// Includes is a list of Includes in a config file. +type Includes []*Include + +// Include is a reference to an included config file. +type Include struct { + Path string + Config *Config +} + +// Comment string without the prefix '#' or ';'. +type Comment string + +const ( + // NoSubsection token is passed to Config.Section and Config.SetSection to + // represent the absence of a section. + NoSubsection = "" +) + +// Section returns a existing section with the given name or creates a new one. +func (c *Config) Section(name string) *Section { + for i := len(c.Sections) - 1; i >= 0; i-- { + s := c.Sections[i] + if s.IsName(name) { + return s + } + } + + s := &Section{Name: name} + c.Sections = append(c.Sections, s) + return s +} + +// AddOption adds an option to a given section and subsection. Use the +// NoSubsection constant for the subsection argument if no subsection is wanted. +func (c *Config) AddOption(section string, subsection string, key string, value string) *Config { + if subsection == "" { + c.Section(section).AddOption(key, value) + } else { + c.Section(section).Subsection(subsection).AddOption(key, value) + } + + return c +} + +// SetOption sets an option to a given section and subsection. Use the +// NoSubsection constant for the subsection argument if no subsection is wanted. +func (c *Config) SetOption(section string, subsection string, key string, value string) *Config { + if subsection == "" { + c.Section(section).SetOption(key, value) + } else { + c.Section(section).Subsection(subsection).SetOption(key, value) + } + + return c +} + +// RemoveSection removes a section from a config file. +func (c *Config) RemoveSection(name string) *Config { + result := Sections{} + for _, s := range c.Sections { + if !s.IsName(name) { + result = append(result, s) + } + } + + c.Sections = result + return c +} + +// RemoveSubsection remove s a subsection from a config file. +func (c *Config) RemoveSubsection(section string, subsection string) *Config { + for _, s := range c.Sections { + if s.IsName(section) { + result := Subsections{} + for _, ss := range s.Subsections { + if !ss.IsName(subsection) { + result = append(result, ss) + } + } + s.Subsections = result + } + } + + return c +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/decoder.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/decoder.go new file mode 100644 index 0000000000..0f02ce1930 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/decoder.go @@ -0,0 +1,37 @@ +package config + +import ( + "io" + + "github.com/src-d/gcfg" +) + +// A Decoder reads and decodes config files from an input stream. +type Decoder struct { + io.Reader +} + +// NewDecoder returns a new decoder that reads from r. +func NewDecoder(r io.Reader) *Decoder { + return &Decoder{r} +} + +// Decode reads the whole config from its input and stores it in the +// value pointed to by config. +func (d *Decoder) Decode(config *Config) error { + cb := func(s string, ss string, k string, v string, bv bool) error { + if ss == "" && k == "" { + config.Section(s) + return nil + } + + if ss != "" && k == "" { + config.Section(s).Subsection(ss) + return nil + } + + config.AddOption(s, ss, k, v) + return nil + } + return gcfg.ReadWithCallback(d, cb) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/doc.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/doc.go new file mode 100644 index 0000000000..3986c83658 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/doc.go @@ -0,0 +1,122 @@ +// Package config implements encoding and decoding of git config files. +// +// Configuration File +// ------------------ +// +// The Git configuration file contains a number of variables that affect +// the Git commands' behavior. The `.git/config` file in each repository +// is used to store the configuration for that repository, and +// `$HOME/.gitconfig` is used to store a per-user configuration as +// fallback values for the `.git/config` file. The file `/etc/gitconfig` +// can be used to store a system-wide default configuration. +// +// The configuration variables are used by both the Git plumbing +// and the porcelains. The variables are divided into sections, wherein +// the fully qualified variable name of the variable itself is the last +// dot-separated segment and the section name is everything before the last +// dot. The variable names are case-insensitive, allow only alphanumeric +// characters and `-`, and must start with an alphabetic character. Some +// variables may appear multiple times; we say then that the variable is +// multivalued. +// +// Syntax +// ~~~~~~ +// +// The syntax is fairly flexible and permissive; whitespaces are mostly +// ignored. The '#' and ';' characters begin comments to the end of line, +// blank lines are ignored. +// +// The file consists of sections and variables. A section begins with +// the name of the section in square brackets and continues until the next +// section begins. Section names are case-insensitive. Only alphanumeric +// characters, `-` and `.` are allowed in section names. Each variable +// must belong to some section, which means that there must be a section +// header before the first setting of a variable. +// +// Sections can be further divided into subsections. To begin a subsection +// put its name in double quotes, separated by space from the section name, +// in the section header, like in the example below: +// +// -------- +// [section "subsection"] +// +// -------- +// +// Subsection names are case sensitive and can contain any characters except +// newline (doublequote `"` and backslash can be included by escaping them +// as `\"` and `\\`, respectively). Section headers cannot span multiple +// lines. Variables may belong directly to a section or to a given subsection. +// You can have `[section]` if you have `[section "subsection"]`, but you +// don't need to. +// +// There is also a deprecated `[section.subsection]` syntax. With this +// syntax, the subsection name is converted to lower-case and is also +// compared case sensitively. These subsection names follow the same +// restrictions as section names. +// +// All the other lines (and the remainder of the line after the section +// header) are recognized as setting variables, in the form +// 'name = value' (or just 'name', which is a short-hand to say that +// the variable is the boolean "true"). +// The variable names are case-insensitive, allow only alphanumeric characters +// and `-`, and must start with an alphabetic character. +// +// A line that defines a value can be continued to the next line by +// ending it with a `\`; the backquote and the end-of-line are +// stripped. Leading whitespaces after 'name =', the remainder of the +// line after the first comment character '#' or ';', and trailing +// whitespaces of the line are discarded unless they are enclosed in +// double quotes. Internal whitespaces within the value are retained +// verbatim. +// +// Inside double quotes, double quote `"` and backslash `\` characters +// must be escaped: use `\"` for `"` and `\\` for `\`. +// +// The following escape sequences (beside `\"` and `\\`) are recognized: +// `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) +// and `\b` for backspace (BS). Other char escape sequences (including octal +// escape sequences) are invalid. +// +// Includes +// ~~~~~~~~ +// +// You can include one config file from another by setting the special +// `include.path` variable to the name of the file to be included. The +// variable takes a pathname as its value, and is subject to tilde +// expansion. +// +// The included file is expanded immediately, as if its contents had been +// found at the location of the include directive. If the value of the +// `include.path` variable is a relative path, the path is considered to be +// relative to the configuration file in which the include directive was +// found. See below for examples. +// +// +// Example +// ~~~~~~~ +// +// # Core variables +// [core] +// ; Don't trust file modes +// filemode = false +// +// # Our diff algorithm +// [diff] +// external = /usr/local/bin/diff-wrapper +// renames = true +// +// [branch "devel"] +// remote = origin +// merge = refs/heads/devel +// +// # Proxy settings +// [core] +// gitProxy="ssh" for "kernel.org" +// gitProxy=default-proxy ; for the rest +// +// [include] +// path = /path/to/foo.inc ; include by absolute path +// path = foo ; expand "foo" relative to the current file +// path = ~/foo ; expand "foo" in your `$HOME` directory +// +package config diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/encoder.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/encoder.go new file mode 100644 index 0000000000..4eac8968ad --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/encoder.go @@ -0,0 +1,77 @@ +package config + +import ( + "fmt" + "io" + "strings" +) + +// An Encoder writes config files to an output stream. +type Encoder struct { + w io.Writer +} + +// NewEncoder returns a new encoder that writes to w. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{w} +} + +// Encode writes the config in git config format to the stream of the encoder. +func (e *Encoder) Encode(cfg *Config) error { + for _, s := range cfg.Sections { + if err := e.encodeSection(s); err != nil { + return err + } + } + + return nil +} + +func (e *Encoder) encodeSection(s *Section) error { + if len(s.Options) > 0 { + if err := e.printf("[%s]\n", s.Name); err != nil { + return err + } + + if err := e.encodeOptions(s.Options); err != nil { + return err + } + } + + for _, ss := range s.Subsections { + if err := e.encodeSubsection(s.Name, ss); err != nil { + return err + } + } + + return nil +} + +func (e *Encoder) encodeSubsection(sectionName string, s *Subsection) error { + //TODO: escape + if err := e.printf("[%s \"%s\"]\n", sectionName, s.Name); err != nil { + return err + } + + return e.encodeOptions(s.Options) +} + +func (e *Encoder) encodeOptions(opts Options) error { + for _, o := range opts { + pattern := "\t%s = %s\n" + if strings.Contains(o.Value, "\\") { + pattern = "\t%s = %q\n" + } + + if err := e.printf(pattern, o.Key, o.Value); err != nil { + return err + } + } + + return nil +} + +func (e *Encoder) printf(msg string, args ...interface{}) error { + _, err := fmt.Fprintf(e.w, msg, args...) + return err +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/option.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/option.go new file mode 100644 index 0000000000..d4775e4f0e --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/option.go @@ -0,0 +1,117 @@ +package config + +import ( + "fmt" + "strings" +) + +// Option defines a key/value entity in a config file. +type Option struct { + // Key preserving original caseness. + // Use IsKey instead to compare key regardless of caseness. + Key string + // Original value as string, could be not normalized. + Value string +} + +type Options []*Option + +// IsKey returns true if the given key matches +// this option's key in a case-insensitive comparison. +func (o *Option) IsKey(key string) bool { + return strings.ToLower(o.Key) == strings.ToLower(key) +} + +func (opts Options) GoString() string { + var strs []string + for _, opt := range opts { + strs = append(strs, fmt.Sprintf("%#v", opt)) + } + + return strings.Join(strs, ", ") +} + +// Get gets the value for the given key if set, +// otherwise it returns the empty string. +// +// Note that there is no difference +// +// This matches git behaviour since git v1.8.1-rc1, +// if there are multiple definitions of a key, the +// last one wins. +// +// See: http://article.gmane.org/gmane.linux.kernel/1407184 +// +// In order to get all possible values for the same key, +// use GetAll. +func (opts Options) Get(key string) string { + for i := len(opts) - 1; i >= 0; i-- { + o := opts[i] + if o.IsKey(key) { + return o.Value + } + } + return "" +} + +// GetAll returns all possible values for the same key. +func (opts Options) GetAll(key string) []string { + result := []string{} + for _, o := range opts { + if o.IsKey(key) { + result = append(result, o.Value) + } + } + return result +} + +func (opts Options) withoutOption(key string) Options { + result := Options{} + for _, o := range opts { + if !o.IsKey(key) { + result = append(result, o) + } + } + return result +} + +func (opts Options) withAddedOption(key string, value string) Options { + return append(opts, &Option{key, value}) +} + +func (opts Options) withSettedOption(key string, values ...string) Options { + var result Options + var added []string + for _, o := range opts { + if !o.IsKey(key) { + result = append(result, o) + continue + } + + if contains(values, o.Value) { + added = append(added, o.Value) + result = append(result, o) + continue + } + } + + for _, value := range values { + if contains(added, value) { + continue + } + + result = result.withAddedOption(key, value) + } + + return result +} + +func contains(haystack []string, needle string) bool { + for _, s := range haystack { + if s == needle { + return true + } + } + + return false +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/section.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/section.go new file mode 100644 index 0000000000..4a17e3b21b --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/config/section.go @@ -0,0 +1,146 @@ +package config + +import ( + "fmt" + "strings" +) + +// Section is the representation of a section inside git configuration files. +// Each Section contains Options that are used by both the Git plumbing +// and the porcelains. +// Sections can be further divided into subsections. To begin a subsection +// put its name in double quotes, separated by space from the section name, +// in the section header, like in the example below: +// +// [section "subsection"] +// +// All the other lines (and the remainder of the line after the section header) +// are recognized as option variables, in the form "name = value" (or just name, +// which is a short-hand to say that the variable is the boolean "true"). +// The variable names are case-insensitive, allow only alphanumeric characters +// and -, and must start with an alphabetic character: +// +// [section "subsection1"] +// option1 = value1 +// option2 +// [section "subsection2"] +// option3 = value2 +// +type Section struct { + Name string + Options Options + Subsections Subsections +} + +type Subsection struct { + Name string + Options Options +} + +type Sections []*Section + +func (s Sections) GoString() string { + var strs []string + for _, ss := range s { + strs = append(strs, fmt.Sprintf("%#v", ss)) + } + + return strings.Join(strs, ", ") +} + +type Subsections []*Subsection + +func (s Subsections) GoString() string { + var strs []string + for _, ss := range s { + strs = append(strs, fmt.Sprintf("%#v", ss)) + } + + return strings.Join(strs, ", ") +} + +// IsName checks if the name provided is equals to the Section name, case insensitive. +func (s *Section) IsName(name string) bool { + return strings.ToLower(s.Name) == strings.ToLower(name) +} + +// Option return the value for the specified key. Empty string is returned if +// key does not exists. +func (s *Section) Option(key string) string { + return s.Options.Get(key) +} + +// AddOption adds a new Option to the Section. The updated Section is returned. +func (s *Section) AddOption(key string, value string) *Section { + s.Options = s.Options.withAddedOption(key, value) + return s +} + +// SetOption adds a new Option to the Section. If the option already exists, is replaced. +// The updated Section is returned. +func (s *Section) SetOption(key string, value string) *Section { + s.Options = s.Options.withSettedOption(key, value) + return s +} + +// Remove an option with the specified key. The updated Section is returned. +func (s *Section) RemoveOption(key string) *Section { + s.Options = s.Options.withoutOption(key) + return s +} + +// Subsection returns a Subsection from the specified Section. If the +// Subsection does not exists, new one is created and added to Section. +func (s *Section) Subsection(name string) *Subsection { + for i := len(s.Subsections) - 1; i >= 0; i-- { + ss := s.Subsections[i] + if ss.IsName(name) { + return ss + } + } + + ss := &Subsection{Name: name} + s.Subsections = append(s.Subsections, ss) + return ss +} + +// HasSubsection checks if the Section has a Subsection with the specified name. +func (s *Section) HasSubsection(name string) bool { + for _, ss := range s.Subsections { + if ss.IsName(name) { + return true + } + } + + return false +} + +// IsName checks if the name of the subsection is exactly the specified name. +func (s *Subsection) IsName(name string) bool { + return s.Name == name +} + +// Option returns an option with the specified key. If the option does not exists, +// empty spring will be returned. +func (s *Subsection) Option(key string) string { + return s.Options.Get(key) +} + +// AddOption adds a new Option to the Subsection. The updated Subsection is returned. +func (s *Subsection) AddOption(key string, value string) *Subsection { + s.Options = s.Options.withAddedOption(key, value) + return s +} + +// SetOption adds a new Option to the Subsection. If the option already exists, is replaced. +// The updated Subsection is returned. +func (s *Subsection) SetOption(key string, value ...string) *Subsection { + s.Options = s.Options.withSettedOption(key, value...) + return s +} + +// RemoveOption removes the option with the specified key. The updated Subsection is returned. +func (s *Subsection) RemoveOption(key string) *Subsection { + s.Options = s.Options.withoutOption(key) + return s +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/diff/patch.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/diff/patch.go new file mode 100644 index 0000000000..7c6cf4aee3 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/diff/patch.go @@ -0,0 +1,58 @@ +package diff + +import ( + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/filemode" +) + +// Operation defines the operation of a diff item. +type Operation int + +const ( + // Equal item represents a equals diff. + Equal Operation = iota + // Add item represents an insert diff. + Add + // Delete item represents a delete diff. + Delete +) + +// Patch represents a collection of steps to transform several files. +type Patch interface { + // FilePatches returns a slice of patches per file. + FilePatches() []FilePatch + // Message returns an optional message that can be at the top of the + // Patch representation. + Message() string +} + +// FilePatch represents the necessary steps to transform one file to another. +type FilePatch interface { + // IsBinary returns true if this patch is representing a binary file. + IsBinary() bool + // Files returns the from and to Files, with all the necessary metadata to + // about them. If the patch creates a new file, "from" will be nil. + // If the patch deletes a file, "to" will be nil. + Files() (from, to File) + // Chunks returns a slice of ordered changes to transform "from" File to + // "to" File. If the file is a binary one, Chunks will be empty. + Chunks() []Chunk +} + +// File contains all the file metadata necessary to print some patch formats. +type File interface { + // Hash returns the File Hash. + Hash() plumbing.Hash + // Mode returns the FileMode. + Mode() filemode.FileMode + // Path returns the complete Path to the file, including the filename. + Path() string +} + +// Chunk represents a portion of a file transformation to another. +type Chunk interface { + // Content contains the portion of the file. + Content() string + // Type contains the Operation to do with this Chunk. + Type() Operation +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/diff/unified_encoder.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/diff/unified_encoder.go new file mode 100644 index 0000000000..169242dc5b --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/diff/unified_encoder.go @@ -0,0 +1,360 @@ +package diff + +import ( + "bytes" + "fmt" + "io" + "strings" + + "gopkg.in/src-d/go-git.v4/plumbing" +) + +const ( + diffInit = "diff --git a/%s b/%s\n" + + chunkStart = "@@ -" + chunkMiddle = " +" + chunkEnd = " @@%s\n" + chunkCount = "%d,%d" + + noFilePath = "/dev/null" + aDir = "a/" + bDir = "b/" + + fPath = "--- %s\n" + tPath = "+++ %s\n" + binary = "Binary files %s and %s differ\n" + + addLine = "+%s\n" + deleteLine = "-%s\n" + equalLine = " %s\n" + + oldMode = "old mode %o\n" + newMode = "new mode %o\n" + deletedFileMode = "deleted file mode %o\n" + newFileMode = "new file mode %o\n" + + renameFrom = "from" + renameTo = "to" + renameFileMode = "rename %s %s\n" + + indexAndMode = "index %s..%s %o\n" + indexNoMode = "index %s..%s\n" + + DefaultContextLines = 3 +) + +// UnifiedEncoder encodes an unified diff into the provided Writer. +// There are some unsupported features: +// - Similarity index for renames +// - Sort hash representation +type UnifiedEncoder struct { + io.Writer + + // ctxLines is the count of unchanged lines that will appear + // surrounding a change. + ctxLines int + + buf bytes.Buffer +} + +func NewUnifiedEncoder(w io.Writer, ctxLines int) *UnifiedEncoder { + return &UnifiedEncoder{ctxLines: ctxLines, Writer: w} +} + +func (e *UnifiedEncoder) Encode(patch Patch) error { + e.printMessage(patch.Message()) + + if err := e.encodeFilePatch(patch.FilePatches()); err != nil { + return err + } + + _, err := e.buf.WriteTo(e) + + return err +} + +func (e *UnifiedEncoder) encodeFilePatch(filePatches []FilePatch) error { + for _, p := range filePatches { + f, t := p.Files() + if err := e.header(f, t, p.IsBinary()); err != nil { + return err + } + + g := newHunksGenerator(p.Chunks(), e.ctxLines) + for _, c := range g.Generate() { + c.WriteTo(&e.buf) + } + } + + return nil +} + +func (e *UnifiedEncoder) printMessage(message string) { + isEmpty := message == "" + hasSuffix := strings.HasSuffix(message, "\n") + if !isEmpty && !hasSuffix { + message += "\n" + } + + e.buf.WriteString(message) +} + +func (e *UnifiedEncoder) header(from, to File, isBinary bool) error { + switch { + case from == nil && to == nil: + return nil + case from != nil && to != nil: + hashEquals := from.Hash() == to.Hash() + + fmt.Fprintf(&e.buf, diffInit, from.Path(), to.Path()) + + if from.Mode() != to.Mode() { + fmt.Fprintf(&e.buf, oldMode+newMode, from.Mode(), to.Mode()) + } + + if from.Path() != to.Path() { + fmt.Fprintf(&e.buf, + renameFileMode+renameFileMode, + renameFrom, from.Path(), renameTo, to.Path()) + } + + if from.Mode() != to.Mode() && !hashEquals { + fmt.Fprintf(&e.buf, indexNoMode, from.Hash(), to.Hash()) + } else if !hashEquals { + fmt.Fprintf(&e.buf, indexAndMode, from.Hash(), to.Hash(), from.Mode()) + } + + if !hashEquals { + e.pathLines(isBinary, aDir+from.Path(), bDir+to.Path()) + } + case from == nil: + fmt.Fprintf(&e.buf, diffInit, to.Path(), to.Path()) + fmt.Fprintf(&e.buf, newFileMode, to.Mode()) + fmt.Fprintf(&e.buf, indexNoMode, plumbing.ZeroHash, to.Hash()) + e.pathLines(isBinary, noFilePath, bDir+to.Path()) + case to == nil: + fmt.Fprintf(&e.buf, diffInit, from.Path(), from.Path()) + fmt.Fprintf(&e.buf, deletedFileMode, from.Mode()) + fmt.Fprintf(&e.buf, indexNoMode, from.Hash(), plumbing.ZeroHash) + e.pathLines(isBinary, aDir+from.Path(), noFilePath) + } + + return nil +} + +func (e *UnifiedEncoder) pathLines(isBinary bool, fromPath, toPath string) { + format := fPath + tPath + if isBinary { + format = binary + } + + fmt.Fprintf(&e.buf, format, fromPath, toPath) +} + +type hunksGenerator struct { + fromLine, toLine int + ctxLines int + chunks []Chunk + current *hunk + hunks []*hunk + beforeContext, afterContext []string +} + +func newHunksGenerator(chunks []Chunk, ctxLines int) *hunksGenerator { + return &hunksGenerator{ + chunks: chunks, + ctxLines: ctxLines, + } +} + +func (c *hunksGenerator) Generate() []*hunk { + for i, chunk := range c.chunks { + ls := splitLines(chunk.Content()) + lsLen := len(ls) + + switch chunk.Type() { + case Equal: + c.fromLine += lsLen + c.toLine += lsLen + c.processEqualsLines(ls, i) + case Delete: + if lsLen != 0 { + c.fromLine++ + } + + c.processHunk(i, chunk.Type()) + c.fromLine += lsLen - 1 + c.current.AddOp(chunk.Type(), ls...) + case Add: + if lsLen != 0 { + c.toLine++ + } + c.processHunk(i, chunk.Type()) + c.toLine += lsLen - 1 + c.current.AddOp(chunk.Type(), ls...) + } + + if i == len(c.chunks)-1 && c.current != nil { + c.hunks = append(c.hunks, c.current) + } + } + + return c.hunks +} + +func (c *hunksGenerator) processHunk(i int, op Operation) { + if c.current != nil { + return + } + + var ctxPrefix string + linesBefore := len(c.beforeContext) + if linesBefore > c.ctxLines { + ctxPrefix = " " + c.beforeContext[linesBefore-c.ctxLines-1] + c.beforeContext = c.beforeContext[linesBefore-c.ctxLines:] + linesBefore = c.ctxLines + } + + c.current = &hunk{ctxPrefix: ctxPrefix} + c.current.AddOp(Equal, c.beforeContext...) + + switch op { + case Delete: + c.current.fromLine, c.current.toLine = + c.addLineNumbers(c.fromLine, c.toLine, linesBefore, i, Add) + case Add: + c.current.toLine, c.current.fromLine = + c.addLineNumbers(c.toLine, c.fromLine, linesBefore, i, Delete) + } + + c.beforeContext = nil +} + +// addLineNumbers obtains the line numbers in a new chunk +func (c *hunksGenerator) addLineNumbers(la, lb int, linesBefore int, i int, op Operation) (cla, clb int) { + cla = la - linesBefore + // we need to search for a reference for the next diff + switch { + case linesBefore != 0 && c.ctxLines != 0: + if lb > c.ctxLines { + clb = lb - c.ctxLines + 1 + } else { + clb = 1 + } + case c.ctxLines == 0: + clb = lb + case i != len(c.chunks)-1: + next := c.chunks[i+1] + if next.Type() == op || next.Type() == Equal { + // this diff will be into this chunk + clb = lb + 1 + } + } + + return +} + +func (c *hunksGenerator) processEqualsLines(ls []string, i int) { + if c.current == nil { + c.beforeContext = append(c.beforeContext, ls...) + return + } + + c.afterContext = append(c.afterContext, ls...) + if len(c.afterContext) <= c.ctxLines*2 && i != len(c.chunks)-1 { + c.current.AddOp(Equal, c.afterContext...) + c.afterContext = nil + } else { + ctxLines := c.ctxLines + if ctxLines > len(c.afterContext) { + ctxLines = len(c.afterContext) + } + c.current.AddOp(Equal, c.afterContext[:ctxLines]...) + c.hunks = append(c.hunks, c.current) + + c.current = nil + c.beforeContext = c.afterContext[ctxLines:] + c.afterContext = nil + } +} + +func splitLines(s string) []string { + out := strings.Split(s, "\n") + if out[len(out)-1] == "" { + out = out[:len(out)-1] + } + + return out +} + +type hunk struct { + fromLine int + toLine int + + fromCount int + toCount int + + ctxPrefix string + ops []*op +} + +func (c *hunk) WriteTo(buf *bytes.Buffer) { + buf.WriteString(chunkStart) + + if c.fromCount == 1 { + fmt.Fprintf(buf, "%d", c.fromLine) + } else { + fmt.Fprintf(buf, chunkCount, c.fromLine, c.fromCount) + } + + buf.WriteString(chunkMiddle) + + if c.toCount == 1 { + fmt.Fprintf(buf, "%d", c.toLine) + } else { + fmt.Fprintf(buf, chunkCount, c.toLine, c.toCount) + } + + fmt.Fprintf(buf, chunkEnd, c.ctxPrefix) + + for _, d := range c.ops { + buf.WriteString(d.String()) + } +} + +func (c *hunk) AddOp(t Operation, s ...string) { + ls := len(s) + switch t { + case Add: + c.toCount += ls + case Delete: + c.fromCount += ls + case Equal: + c.toCount += ls + c.fromCount += ls + } + + for _, l := range s { + c.ops = append(c.ops, &op{l, t}) + } +} + +type op struct { + text string + t Operation +} + +func (o *op) String() string { + var prefix string + switch o.t { + case Add: + prefix = addLine + case Delete: + prefix = deleteLine + case Equal: + prefix = equalLine + } + + return fmt.Sprintf(prefix, o.text) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/gitignore/dir.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/gitignore/dir.go new file mode 100644 index 0000000000..1e88970efd --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/gitignore/dir.go @@ -0,0 +1,136 @@ +package gitignore + +import ( + "bytes" + "io/ioutil" + "os" + "os/user" + "strings" + + "gopkg.in/src-d/go-billy.v4" + "gopkg.in/src-d/go-git.v4/plumbing/format/config" + gioutil "gopkg.in/src-d/go-git.v4/utils/ioutil" +) + +const ( + commentPrefix = "#" + coreSection = "core" + eol = "\n" + excludesfile = "excludesfile" + gitDir = ".git" + gitignoreFile = ".gitignore" + gitconfigFile = ".gitconfig" + systemFile = "/etc/gitconfig" +) + +// readIgnoreFile reads a specific git ignore file. +func readIgnoreFile(fs billy.Filesystem, path []string, ignoreFile string) (ps []Pattern, err error) { + f, err := fs.Open(fs.Join(append(path, ignoreFile)...)) + if err == nil { + defer f.Close() + + if data, err := ioutil.ReadAll(f); err == nil { + for _, s := range strings.Split(string(data), eol) { + if !strings.HasPrefix(s, commentPrefix) && len(strings.TrimSpace(s)) > 0 { + ps = append(ps, ParsePattern(s, path)) + } + } + } + } else if !os.IsNotExist(err) { + return nil, err + } + + return +} + +// ReadPatterns reads gitignore patterns recursively traversing through the directory +// structure. The result is in the ascending order of priority (last higher). +func ReadPatterns(fs billy.Filesystem, path []string) (ps []Pattern, err error) { + ps, _ = readIgnoreFile(fs, path, gitignoreFile) + + var fis []os.FileInfo + fis, err = fs.ReadDir(fs.Join(path...)) + if err != nil { + return + } + + for _, fi := range fis { + if fi.IsDir() && fi.Name() != gitDir { + var subps []Pattern + subps, err = ReadPatterns(fs, append(path, fi.Name())) + if err != nil { + return + } + + if len(subps) > 0 { + ps = append(ps, subps...) + } + } + } + + return +} + +func loadPatterns(fs billy.Filesystem, path string) (ps []Pattern, err error) { + f, err := fs.Open(path) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, err + } + + defer gioutil.CheckClose(f, &err) + + b, err := ioutil.ReadAll(f) + if err != nil { + return + } + + d := config.NewDecoder(bytes.NewBuffer(b)) + + raw := config.New() + if err = d.Decode(raw); err != nil { + return + } + + s := raw.Section(coreSection) + efo := s.Options.Get(excludesfile) + if efo == "" { + return nil, nil + } + + ps, err = readIgnoreFile(fs, nil, efo) + if os.IsNotExist(err) { + return nil, nil + } + + return +} + +// LoadGlobalPatterns loads gitignore patterns from from the gitignore file +// declared in a user's ~/.gitconfig file. If the ~/.gitconfig file does not +// exist the function will return nil. If the core.excludesfile property +// is not declared, the function will return nil. If the file pointed to by +// the core.excludesfile property does not exist, the function will return nil. +// +// The function assumes fs is rooted at the root filesystem. +func LoadGlobalPatterns(fs billy.Filesystem) (ps []Pattern, err error) { + usr, err := user.Current() + if err != nil { + return + } + + return loadPatterns(fs, fs.Join(usr.HomeDir, gitconfigFile)) +} + +// LoadSystemPatterns loads gitignore patterns from from the gitignore file +// declared in a system's /etc/gitconfig file. If the ~/.gitconfig file does +// not exist the function will return nil. If the core.excludesfile property +// is not declared, the function will return nil. If the file pointed to by +// the core.excludesfile property does not exist, the function will return nil. +// +// The function assumes fs is rooted at the root filesystem. +func LoadSystemPatterns(fs billy.Filesystem) (ps []Pattern, err error) { + return loadPatterns(fs, systemFile) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/gitignore/doc.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/gitignore/doc.go new file mode 100644 index 0000000000..eecd4baccb --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/gitignore/doc.go @@ -0,0 +1,70 @@ +// Package gitignore implements matching file system paths to gitignore patterns that +// can be automatically read from a git repository tree in the order of definition +// priorities. It support all pattern formats as specified in the original gitignore +// documentation, copied below: +// +// Pattern format +// ============== +// +// - A blank line matches no files, so it can serve as a separator for readability. +// +// - A line starting with # serves as a comment. Put a backslash ("\") in front of +// the first hash for patterns that begin with a hash. +// +// - Trailing spaces are ignored unless they are quoted with backslash ("\"). +// +// - An optional prefix "!" which negates the pattern; any matching file excluded +// by a previous pattern will become included again. It is not possible to +// re-include a file if a parent directory of that file is excluded. +// Git doesn’t list excluded directories for performance reasons, so +// any patterns on contained files have no effect, no matter where they are +// defined. Put a backslash ("\") in front of the first "!" for patterns +// that begin with a literal "!", for example, "\!important!.txt". +// +// - If the pattern ends with a slash, it is removed for the purpose of the +// following description, but it would only find a match with a directory. +// In other words, foo/ will match a directory foo and paths underneath it, +// but will not match a regular file or a symbolic link foo (this is consistent +// with the way how pathspec works in general in Git). +// +// - If the pattern does not contain a slash /, Git treats it as a shell glob +// pattern and checks for a match against the pathname relative to the location +// of the .gitignore file (relative to the toplevel of the work tree if not +// from a .gitignore file). +// +// - Otherwise, Git treats the pattern as a shell glob suitable for consumption +// by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will +// not match a / in the pathname. For example, "Documentation/*.html" matches +// "Documentation/git.html" but not "Documentation/ppc/ppc.html" or +// "tools/perf/Documentation/perf.html". +// +// - A leading slash matches the beginning of the pathname. For example, +// "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c". +// +// Two consecutive asterisks ("**") in patterns matched against full pathname +// may have special meaning: +// +// - A leading "**" followed by a slash means match in all directories. +// For example, "**/foo" matches file or directory "foo" anywhere, the same as +// pattern "foo". "**/foo/bar" matches file or directory "bar" +// anywhere that is directly under directory "foo". +// +// - A trailing "/**" matches everything inside. For example, "abc/**" matches +// all files inside directory "abc", relative to the location of the +// .gitignore file, with infinite depth. +// +// - A slash followed by two consecutive asterisks then a slash matches +// zero or more directories. For example, "a/**/b" matches "a/b", "a/x/b", +// "a/x/y/b" and so on. +// +// - Other consecutive asterisks are considered invalid. +// +// Copyright and license +// ===================== +// +// Copyright (c) Oleg Sklyar, Silvertern and source{d} +// +// The package code was donated to source{d} to include, modify and develop +// further as a part of the `go-git` project, release it on the license of +// the whole project or delete it from the project. +package gitignore diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/gitignore/matcher.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/gitignore/matcher.go new file mode 100644 index 0000000000..bd1e9e2d4c --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/gitignore/matcher.go @@ -0,0 +1,30 @@ +package gitignore + +// Matcher defines a global multi-pattern matcher for gitignore patterns +type Matcher interface { + // Match matches patterns in the order of priorities. As soon as an inclusion or + // exclusion is found, not further matching is performed. + Match(path []string, isDir bool) bool +} + +// NewMatcher constructs a new global matcher. Patterns must be given in the order of +// increasing priority. That is most generic settings files first, then the content of +// the repo .gitignore, then content of .gitignore down the path or the repo and then +// the content command line arguments. +func NewMatcher(ps []Pattern) Matcher { + return &matcher{ps} +} + +type matcher struct { + patterns []Pattern +} + +func (m *matcher) Match(path []string, isDir bool) bool { + n := len(m.patterns) + for i := n - 1; i >= 0; i-- { + if match := m.patterns[i].Match(path, isDir); match > NoMatch { + return match == Exclude + } + } + return false +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/gitignore/pattern.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/gitignore/pattern.go new file mode 100644 index 0000000000..098cb50212 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/gitignore/pattern.go @@ -0,0 +1,153 @@ +package gitignore + +import ( + "path/filepath" + "strings" +) + +// MatchResult defines outcomes of a match, no match, exclusion or inclusion. +type MatchResult int + +const ( + // NoMatch defines the no match outcome of a match check + NoMatch MatchResult = iota + // Exclude defines an exclusion of a file as a result of a match check + Exclude + // Include defines an explicit inclusion of a file as a result of a match check + Include +) + +const ( + inclusionPrefix = "!" + zeroToManyDirs = "**" + patternDirSep = "/" +) + +// Pattern defines a single gitignore pattern. +type Pattern interface { + // Match matches the given path to the pattern. + Match(path []string, isDir bool) MatchResult +} + +type pattern struct { + domain []string + pattern []string + inclusion bool + dirOnly bool + isGlob bool +} + +// ParsePattern parses a gitignore pattern string into the Pattern structure. +func ParsePattern(p string, domain []string) Pattern { + res := pattern{domain: domain} + + if strings.HasPrefix(p, inclusionPrefix) { + res.inclusion = true + p = p[1:] + } + + if !strings.HasSuffix(p, "\\ ") { + p = strings.TrimRight(p, " ") + } + + if strings.HasSuffix(p, patternDirSep) { + res.dirOnly = true + p = p[:len(p)-1] + } + + if strings.Contains(p, patternDirSep) { + res.isGlob = true + } + + res.pattern = strings.Split(p, patternDirSep) + return &res +} + +func (p *pattern) Match(path []string, isDir bool) MatchResult { + if len(path) <= len(p.domain) { + return NoMatch + } + for i, e := range p.domain { + if path[i] != e { + return NoMatch + } + } + + path = path[len(p.domain):] + if p.isGlob && !p.globMatch(path, isDir) { + return NoMatch + } else if !p.isGlob && !p.simpleNameMatch(path, isDir) { + return NoMatch + } + + if p.inclusion { + return Include + } else { + return Exclude + } +} + +func (p *pattern) simpleNameMatch(path []string, isDir bool) bool { + for i, name := range path { + if match, err := filepath.Match(p.pattern[0], name); err != nil { + return false + } else if !match { + continue + } + if p.dirOnly && !isDir && i == len(path)-1 { + return false + } + return true + } + return false +} + +func (p *pattern) globMatch(path []string, isDir bool) bool { + matched := false + canTraverse := false + for i, pattern := range p.pattern { + if pattern == "" { + canTraverse = false + continue + } + if pattern == zeroToManyDirs { + if i == len(p.pattern)-1 { + break + } + canTraverse = true + continue + } + if strings.Contains(pattern, zeroToManyDirs) { + return false + } + if len(path) == 0 { + return false + } + if canTraverse { + canTraverse = false + for len(path) > 0 { + e := path[0] + path = path[1:] + if match, err := filepath.Match(pattern, e); err != nil { + return false + } else if match { + matched = true + break + } else if len(path) == 0 { + // if nothing left then fail + matched = false + } + } + } else { + if match, err := filepath.Match(pattern, path[0]); err != nil || !match { + return false + } + matched = true + path = path[1:] + } + } + if matched && p.dirOnly && !isDir && len(path) == 0 { + matched = false + } + return matched +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/decoder.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/decoder.go new file mode 100644 index 0000000000..9e9c1769ab --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/decoder.go @@ -0,0 +1,177 @@ +package idxfile + +import ( + "bufio" + "bytes" + "errors" + "io" + + "gopkg.in/src-d/go-git.v4/utils/binary" +) + +var ( + // ErrUnsupportedVersion is returned by Decode when the idx file version + // is not supported. + ErrUnsupportedVersion = errors.New("Unsuported version") + // ErrMalformedIdxFile is returned by Decode when the idx file is corrupted. + ErrMalformedIdxFile = errors.New("Malformed IDX file") +) + +const ( + fanout = 256 + objectIDLength = 20 +) + +// Decoder reads and decodes idx files from an input stream. +type Decoder struct { + *bufio.Reader +} + +// NewDecoder builds a new idx stream decoder, that reads from r. +func NewDecoder(r io.Reader) *Decoder { + return &Decoder{bufio.NewReader(r)} +} + +// Decode reads from the stream and decode the content into the MemoryIndex struct. +func (d *Decoder) Decode(idx *MemoryIndex) error { + if err := validateHeader(d); err != nil { + return err + } + + flow := []func(*MemoryIndex, io.Reader) error{ + readVersion, + readFanout, + readObjectNames, + readCRC32, + readOffsets, + readChecksums, + } + + for _, f := range flow { + if err := f(idx, d); err != nil { + return err + } + } + + return nil +} + +func validateHeader(r io.Reader) error { + var h = make([]byte, 4) + if _, err := io.ReadFull(r, h); err != nil { + return err + } + + if !bytes.Equal(h, idxHeader) { + return ErrMalformedIdxFile + } + + return nil +} + +func readVersion(idx *MemoryIndex, r io.Reader) error { + v, err := binary.ReadUint32(r) + if err != nil { + return err + } + + if v > VersionSupported { + return ErrUnsupportedVersion + } + + idx.Version = v + return nil +} + +func readFanout(idx *MemoryIndex, r io.Reader) error { + for k := 0; k < fanout; k++ { + n, err := binary.ReadUint32(r) + if err != nil { + return err + } + + idx.Fanout[k] = n + idx.FanoutMapping[k] = noMapping + } + + return nil +} + +func readObjectNames(idx *MemoryIndex, r io.Reader) error { + for k := 0; k < fanout; k++ { + var buckets uint32 + if k == 0 { + buckets = idx.Fanout[k] + } else { + buckets = idx.Fanout[k] - idx.Fanout[k-1] + } + + if buckets == 0 { + continue + } + + idx.FanoutMapping[k] = len(idx.Names) + + nameLen := int(buckets * objectIDLength) + bin := make([]byte, nameLen) + if _, err := io.ReadFull(r, bin); err != nil { + return err + } + + idx.Names = append(idx.Names, bin) + idx.Offset32 = append(idx.Offset32, make([]byte, buckets*4)) + idx.CRC32 = append(idx.CRC32, make([]byte, buckets*4)) + } + + return nil +} + +func readCRC32(idx *MemoryIndex, r io.Reader) error { + for k := 0; k < fanout; k++ { + if pos := idx.FanoutMapping[k]; pos != noMapping { + if _, err := io.ReadFull(r, idx.CRC32[pos]); err != nil { + return err + } + } + } + + return nil +} + +func readOffsets(idx *MemoryIndex, r io.Reader) error { + var o64cnt int + for k := 0; k < fanout; k++ { + if pos := idx.FanoutMapping[k]; pos != noMapping { + if _, err := io.ReadFull(r, idx.Offset32[pos]); err != nil { + return err + } + + for p := 0; p < len(idx.Offset32[pos]); p += 4 { + if idx.Offset32[pos][p]&(byte(1)<<7) > 0 { + o64cnt++ + } + } + } + } + + if o64cnt > 0 { + idx.Offset64 = make([]byte, o64cnt*8) + if _, err := io.ReadFull(r, idx.Offset64); err != nil { + return err + } + } + + return nil +} + +func readChecksums(idx *MemoryIndex, r io.Reader) error { + if _, err := io.ReadFull(r, idx.PackfileChecksum[:]); err != nil { + return err + } + + if _, err := io.ReadFull(r, idx.IdxChecksum[:]); err != nil { + return err + } + + return nil +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/doc.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/doc.go new file mode 100644 index 0000000000..1e628ab4a5 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/doc.go @@ -0,0 +1,128 @@ +// Package idxfile implements encoding and decoding of packfile idx files. +// +// == Original (version 1) pack-*.idx files have the following format: +// +// - The header consists of 256 4-byte network byte order +// integers. N-th entry of this table records the number of +// objects in the corresponding pack, the first byte of whose +// object name is less than or equal to N. This is called the +// 'first-level fan-out' table. +// +// - The header is followed by sorted 24-byte entries, one entry +// per object in the pack. Each entry is: +// +// 4-byte network byte order integer, recording where the +// object is stored in the packfile as the offset from the +// beginning. +// +// 20-byte object name. +// +// - The file is concluded with a trailer: +// +// A copy of the 20-byte SHA1 checksum at the end of +// corresponding packfile. +// +// 20-byte SHA1-checksum of all of the above. +// +// Pack Idx file: +// +// -- +--------------------------------+ +// fanout | fanout[0] = 2 (for example) |-. +// table +--------------------------------+ | +// | fanout[1] | | +// +--------------------------------+ | +// | fanout[2] | | +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +// | fanout[255] = total objects |---. +// -- +--------------------------------+ | | +// main | offset | | | +// index | object name 00XXXXXXXXXXXXXXXX | | | +// tab +--------------------------------+ | | +// | offset | | | +// | object name 00XXXXXXXXXXXXXXXX | | | +// +--------------------------------+<+ | +// .-| offset | | +// | | object name 01XXXXXXXXXXXXXXXX | | +// | +--------------------------------+ | +// | | offset | | +// | | object name 01XXXXXXXXXXXXXXXX | | +// | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +// | | offset | | +// | | object name FFXXXXXXXXXXXXXXXX | | +// --| +--------------------------------+<--+ +// trailer | | packfile checksum | +// | +--------------------------------+ +// | | idxfile checksum | +// | +--------------------------------+ +// .---------. +// | +// Pack file entry: <+ +// +// packed object header: +// 1-byte size extension bit (MSB) +// type (next 3 bit) +// size0 (lower 4-bit) +// n-byte sizeN (as long as MSB is set, each 7-bit) +// size0..sizeN form 4+7+7+..+7 bit integer, size0 +// is the least significant part, and sizeN is the +// most significant part. +// packed object data: +// If it is not DELTA, then deflated bytes (the size above +// is the size before compression). +// If it is REF_DELTA, then +// 20-byte base object name SHA1 (the size above is the +// size of the delta data that follows). +// delta data, deflated. +// If it is OFS_DELTA, then +// n-byte offset (see below) interpreted as a negative +// offset from the type-byte of the header of the +// ofs-delta entry (the size above is the size of +// the delta data that follows). +// delta data, deflated. +// +// offset encoding: +// n bytes with MSB set in all but the last one. +// The offset is then the number constructed by +// concatenating the lower 7 bit of each byte, and +// for n >= 2 adding 2^7 + 2^14 + ... + 2^(7*(n-1)) +// to the result. +// +// == Version 2 pack-*.idx files support packs larger than 4 GiB, and +// have some other reorganizations. They have the format: +// +// - A 4-byte magic number '\377tOc' which is an unreasonable +// fanout[0] value. +// +// - A 4-byte version number (= 2) +// +// - A 256-entry fan-out table just like v1. +// +// - A table of sorted 20-byte SHA1 object names. These are +// packed together without offset values to reduce the cache +// footprint of the binary search for a specific object name. +// +// - A table of 4-byte CRC32 values of the packed object data. +// This is new in v2 so compressed data can be copied directly +// from pack to pack during repacking without undetected +// data corruption. +// +// - A table of 4-byte offset values (in network byte order). +// These are usually 31-bit pack file offsets, but large +// offsets are encoded as an index into the next table with +// the msbit set. +// +// - A table of 8-byte offset entries (empty for pack files less +// than 2 GiB). Pack files are organized with heavily used +// objects toward the front, so most object references should +// not need to refer to this table. +// +// - The same trailer as a v1 pack file: +// +// A copy of the 20-byte SHA1 checksum at the end of +// corresponding packfile. +// +// 20-byte SHA1-checksum of all of the above. +// +// Source: +// https://www.kernel.org/pub/software/scm/git/docs/v1.7.5/technical/pack-format.txt +package idxfile diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/encoder.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/encoder.go new file mode 100644 index 0000000000..e479511026 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/encoder.go @@ -0,0 +1,142 @@ +package idxfile + +import ( + "crypto/sha1" + "hash" + "io" + + "gopkg.in/src-d/go-git.v4/utils/binary" +) + +// Encoder writes MemoryIndex structs to an output stream. +type Encoder struct { + io.Writer + hash hash.Hash +} + +// NewEncoder returns a new stream encoder that writes to w. +func NewEncoder(w io.Writer) *Encoder { + h := sha1.New() + mw := io.MultiWriter(w, h) + return &Encoder{mw, h} +} + +// Encode encodes an MemoryIndex to the encoder writer. +func (e *Encoder) Encode(idx *MemoryIndex) (int, error) { + flow := []func(*MemoryIndex) (int, error){ + e.encodeHeader, + e.encodeFanout, + e.encodeHashes, + e.encodeCRC32, + e.encodeOffsets, + e.encodeChecksums, + } + + sz := 0 + for _, f := range flow { + i, err := f(idx) + sz += i + + if err != nil { + return sz, err + } + } + + return sz, nil +} + +func (e *Encoder) encodeHeader(idx *MemoryIndex) (int, error) { + c, err := e.Write(idxHeader) + if err != nil { + return c, err + } + + return c + 4, binary.WriteUint32(e, idx.Version) +} + +func (e *Encoder) encodeFanout(idx *MemoryIndex) (int, error) { + for _, c := range idx.Fanout { + if err := binary.WriteUint32(e, c); err != nil { + return 0, err + } + } + + return fanout * 4, nil +} + +func (e *Encoder) encodeHashes(idx *MemoryIndex) (int, error) { + var size int + for k := 0; k < fanout; k++ { + pos := idx.FanoutMapping[k] + if pos == noMapping { + continue + } + + n, err := e.Write(idx.Names[pos]) + if err != nil { + return size, err + } + size += n + } + return size, nil +} + +func (e *Encoder) encodeCRC32(idx *MemoryIndex) (int, error) { + var size int + for k := 0; k < fanout; k++ { + pos := idx.FanoutMapping[k] + if pos == noMapping { + continue + } + + n, err := e.Write(idx.CRC32[pos]) + if err != nil { + return size, err + } + + size += n + } + + return size, nil +} + +func (e *Encoder) encodeOffsets(idx *MemoryIndex) (int, error) { + var size int + for k := 0; k < fanout; k++ { + pos := idx.FanoutMapping[k] + if pos == noMapping { + continue + } + + n, err := e.Write(idx.Offset32[pos]) + if err != nil { + return size, err + } + + size += n + } + + if len(idx.Offset64) > 0 { + n, err := e.Write(idx.Offset64) + if err != nil { + return size, err + } + + size += n + } + + return size, nil +} + +func (e *Encoder) encodeChecksums(idx *MemoryIndex) (int, error) { + if _, err := e.Write(idx.PackfileChecksum[:]); err != nil { + return 0, err + } + + copy(idx.IdxChecksum[:], e.hash.Sum(nil)[:20]) + if _, err := e.Write(idx.IdxChecksum[:]); err != nil { + return 0, err + } + + return 40, nil +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/idxfile.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/idxfile.go new file mode 100644 index 0000000000..14b58603f7 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/idxfile.go @@ -0,0 +1,346 @@ +package idxfile + +import ( + "bytes" + "io" + "sort" + + encbin "encoding/binary" + + "gopkg.in/src-d/go-git.v4/plumbing" +) + +const ( + // VersionSupported is the only idx version supported. + VersionSupported = 2 + + noMapping = -1 +) + +var ( + idxHeader = []byte{255, 't', 'O', 'c'} +) + +// Index represents an index of a packfile. +type Index interface { + // Contains checks whether the given hash is in the index. + Contains(h plumbing.Hash) (bool, error) + // FindOffset finds the offset in the packfile for the object with + // the given hash. + FindOffset(h plumbing.Hash) (int64, error) + // FindCRC32 finds the CRC32 of the object with the given hash. + FindCRC32(h plumbing.Hash) (uint32, error) + // FindHash finds the hash for the object with the given offset. + FindHash(o int64) (plumbing.Hash, error) + // Count returns the number of entries in the index. + Count() (int64, error) + // Entries returns an iterator to retrieve all index entries. + Entries() (EntryIter, error) + // EntriesByOffset returns an iterator to retrieve all index entries ordered + // by offset. + EntriesByOffset() (EntryIter, error) +} + +// MemoryIndex is the in memory representation of an idx file. +type MemoryIndex struct { + Version uint32 + Fanout [256]uint32 + // FanoutMapping maps the position in the fanout table to the position + // in the Names, Offset32 and CRC32 slices. This improves the memory + // usage by not needing an array with unnecessary empty slots. + FanoutMapping [256]int + Names [][]byte + Offset32 [][]byte + CRC32 [][]byte + Offset64 []byte + PackfileChecksum [20]byte + IdxChecksum [20]byte + + offsetHash map[int64]plumbing.Hash + offsetHashIsFull bool +} + +var _ Index = (*MemoryIndex)(nil) + +// NewMemoryIndex returns an instance of a new MemoryIndex. +func NewMemoryIndex() *MemoryIndex { + return &MemoryIndex{} +} + +func (idx *MemoryIndex) findHashIndex(h plumbing.Hash) (int, bool) { + k := idx.FanoutMapping[h[0]] + if k == noMapping { + return 0, false + } + + if len(idx.Names) <= k { + return 0, false + } + + data := idx.Names[k] + high := uint64(len(idx.Offset32[k])) >> 2 + if high == 0 { + return 0, false + } + + low := uint64(0) + for { + mid := (low + high) >> 1 + offset := mid * objectIDLength + + cmp := bytes.Compare(h[:], data[offset:offset+objectIDLength]) + if cmp < 0 { + high = mid + } else if cmp == 0 { + return int(mid), true + } else { + low = mid + 1 + } + + if low >= high { + break + } + } + + return 0, false +} + +// Contains implements the Index interface. +func (idx *MemoryIndex) Contains(h plumbing.Hash) (bool, error) { + _, ok := idx.findHashIndex(h) + return ok, nil +} + +// FindOffset implements the Index interface. +func (idx *MemoryIndex) FindOffset(h plumbing.Hash) (int64, error) { + if len(idx.FanoutMapping) <= int(h[0]) { + return 0, plumbing.ErrObjectNotFound + } + + k := idx.FanoutMapping[h[0]] + i, ok := idx.findHashIndex(h) + if !ok { + return 0, plumbing.ErrObjectNotFound + } + + offset := idx.getOffset(k, i) + + if !idx.offsetHashIsFull { + // Save the offset for reverse lookup + if idx.offsetHash == nil { + idx.offsetHash = make(map[int64]plumbing.Hash) + } + idx.offsetHash[int64(offset)] = h + } + + return int64(offset), nil +} + +const isO64Mask = uint64(1) << 31 + +func (idx *MemoryIndex) getOffset(firstLevel, secondLevel int) uint64 { + offset := secondLevel << 2 + ofs := encbin.BigEndian.Uint32(idx.Offset32[firstLevel][offset : offset+4]) + + if (uint64(ofs) & isO64Mask) != 0 { + offset := 8 * (uint64(ofs) & ^isO64Mask) + n := encbin.BigEndian.Uint64(idx.Offset64[offset : offset+8]) + return n + } + + return uint64(ofs) +} + +// FindCRC32 implements the Index interface. +func (idx *MemoryIndex) FindCRC32(h plumbing.Hash) (uint32, error) { + k := idx.FanoutMapping[h[0]] + i, ok := idx.findHashIndex(h) + if !ok { + return 0, plumbing.ErrObjectNotFound + } + + return idx.getCRC32(k, i), nil +} + +func (idx *MemoryIndex) getCRC32(firstLevel, secondLevel int) uint32 { + offset := secondLevel << 2 + return encbin.BigEndian.Uint32(idx.CRC32[firstLevel][offset : offset+4]) +} + +// FindHash implements the Index interface. +func (idx *MemoryIndex) FindHash(o int64) (plumbing.Hash, error) { + var hash plumbing.Hash + var ok bool + + if idx.offsetHash != nil { + if hash, ok = idx.offsetHash[o]; ok { + return hash, nil + } + } + + // Lazily generate the reverse offset/hash map if required. + if !idx.offsetHashIsFull || idx.offsetHash == nil { + if err := idx.genOffsetHash(); err != nil { + return plumbing.ZeroHash, err + } + + hash, ok = idx.offsetHash[o] + } + + if !ok { + return plumbing.ZeroHash, plumbing.ErrObjectNotFound + } + + return hash, nil +} + +// genOffsetHash generates the offset/hash mapping for reverse search. +func (idx *MemoryIndex) genOffsetHash() error { + count, err := idx.Count() + if err != nil { + return err + } + + idx.offsetHash = make(map[int64]plumbing.Hash, count) + idx.offsetHashIsFull = true + + var hash plumbing.Hash + i := uint32(0) + for firstLevel, fanoutValue := range idx.Fanout { + mappedFirstLevel := idx.FanoutMapping[firstLevel] + for secondLevel := uint32(0); i < fanoutValue; i++ { + copy(hash[:], idx.Names[mappedFirstLevel][secondLevel*objectIDLength:]) + offset := int64(idx.getOffset(mappedFirstLevel, int(secondLevel))) + idx.offsetHash[offset] = hash + secondLevel++ + } + } + + return nil +} + +// Count implements the Index interface. +func (idx *MemoryIndex) Count() (int64, error) { + return int64(idx.Fanout[fanout-1]), nil +} + +// Entries implements the Index interface. +func (idx *MemoryIndex) Entries() (EntryIter, error) { + return &idxfileEntryIter{idx, 0, 0, 0}, nil +} + +// EntriesByOffset implements the Index interface. +func (idx *MemoryIndex) EntriesByOffset() (EntryIter, error) { + count, err := idx.Count() + if err != nil { + return nil, err + } + + iter := &idxfileEntryOffsetIter{ + entries: make(entriesByOffset, count), + } + + entries, err := idx.Entries() + if err != nil { + return nil, err + } + + for pos := 0; int64(pos) < count; pos++ { + entry, err := entries.Next() + if err != nil { + return nil, err + } + + iter.entries[pos] = entry + } + + sort.Sort(iter.entries) + + return iter, nil +} + +// EntryIter is an iterator that will return the entries in a packfile index. +type EntryIter interface { + // Next returns the next entry in the packfile index. + Next() (*Entry, error) + // Close closes the iterator. + Close() error +} + +type idxfileEntryIter struct { + idx *MemoryIndex + total int + firstLevel, secondLevel int +} + +func (i *idxfileEntryIter) Next() (*Entry, error) { + for { + if i.firstLevel >= fanout { + return nil, io.EOF + } + + if i.total >= int(i.idx.Fanout[i.firstLevel]) { + i.firstLevel++ + i.secondLevel = 0 + continue + } + + mappedFirstLevel := i.idx.FanoutMapping[i.firstLevel] + entry := new(Entry) + copy(entry.Hash[:], i.idx.Names[mappedFirstLevel][i.secondLevel*objectIDLength:]) + entry.Offset = i.idx.getOffset(mappedFirstLevel, i.secondLevel) + entry.CRC32 = i.idx.getCRC32(mappedFirstLevel, i.secondLevel) + + i.secondLevel++ + i.total++ + + return entry, nil + } +} + +func (i *idxfileEntryIter) Close() error { + i.firstLevel = fanout + return nil +} + +// Entry is the in memory representation of an object entry in the idx file. +type Entry struct { + Hash plumbing.Hash + CRC32 uint32 + Offset uint64 +} + +type idxfileEntryOffsetIter struct { + entries entriesByOffset + pos int +} + +func (i *idxfileEntryOffsetIter) Next() (*Entry, error) { + if i.pos >= len(i.entries) { + return nil, io.EOF + } + + entry := i.entries[i.pos] + i.pos++ + + return entry, nil +} + +func (i *idxfileEntryOffsetIter) Close() error { + i.pos = len(i.entries) + 1 + return nil +} + +type entriesByOffset []*Entry + +func (o entriesByOffset) Len() int { + return len(o) +} + +func (o entriesByOffset) Less(i int, j int) bool { + return o[i].Offset < o[j].Offset +} + +func (o entriesByOffset) Swap(i int, j int) { + o[i], o[j] = o[j], o[i] +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/writer.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/writer.go new file mode 100644 index 0000000000..fcc78c56d0 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/idxfile/writer.go @@ -0,0 +1,186 @@ +package idxfile + +import ( + "bytes" + "fmt" + "math" + "sort" + "sync" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/utils/binary" +) + +// objects implements sort.Interface and uses hash as sorting key. +type objects []Entry + +// Writer implements a packfile Observer interface and is used to generate +// indexes. +type Writer struct { + m sync.Mutex + + count uint32 + checksum plumbing.Hash + objects objects + offset64 uint32 + finished bool + index *MemoryIndex + added map[plumbing.Hash]struct{} +} + +// Index returns a previously created MemoryIndex or creates a new one if +// needed. +func (w *Writer) Index() (*MemoryIndex, error) { + w.m.Lock() + defer w.m.Unlock() + + if w.index == nil { + return w.createIndex() + } + + return w.index, nil +} + +// Add appends new object data. +func (w *Writer) Add(h plumbing.Hash, pos uint64, crc uint32) { + w.m.Lock() + defer w.m.Unlock() + + if w.added == nil { + w.added = make(map[plumbing.Hash]struct{}) + } + + if _, ok := w.added[h]; !ok { + w.added[h] = struct{}{} + w.objects = append(w.objects, Entry{h, crc, pos}) + } + +} + +func (w *Writer) Finished() bool { + return w.finished +} + +// OnHeader implements packfile.Observer interface. +func (w *Writer) OnHeader(count uint32) error { + w.count = count + w.objects = make(objects, 0, count) + return nil +} + +// OnInflatedObjectHeader implements packfile.Observer interface. +func (w *Writer) OnInflatedObjectHeader(t plumbing.ObjectType, objSize int64, pos int64) error { + return nil +} + +// OnInflatedObjectContent implements packfile.Observer interface. +func (w *Writer) OnInflatedObjectContent(h plumbing.Hash, pos int64, crc uint32, _ []byte) error { + w.Add(h, uint64(pos), crc) + return nil +} + +// OnFooter implements packfile.Observer interface. +func (w *Writer) OnFooter(h plumbing.Hash) error { + w.checksum = h + w.finished = true + _, err := w.createIndex() + if err != nil { + return err + } + + return nil +} + +// creatIndex returns a filled MemoryIndex with the information filled by +// the observer callbacks. +func (w *Writer) createIndex() (*MemoryIndex, error) { + if !w.finished { + return nil, fmt.Errorf("the index still hasn't finished building") + } + + idx := new(MemoryIndex) + w.index = idx + + sort.Sort(w.objects) + + // unmap all fans by default + for i := range idx.FanoutMapping { + idx.FanoutMapping[i] = noMapping + } + + buf := new(bytes.Buffer) + + last := -1 + bucket := -1 + for i, o := range w.objects { + fan := o.Hash[0] + + // fill the gaps between fans + for j := last + 1; j < int(fan); j++ { + idx.Fanout[j] = uint32(i) + } + + // update the number of objects for this position + idx.Fanout[fan] = uint32(i + 1) + + // we move from one bucket to another, update counters and allocate + // memory + if last != int(fan) { + bucket++ + idx.FanoutMapping[fan] = bucket + last = int(fan) + + idx.Names = append(idx.Names, make([]byte, 0)) + idx.Offset32 = append(idx.Offset32, make([]byte, 0)) + idx.CRC32 = append(idx.CRC32, make([]byte, 0)) + } + + idx.Names[bucket] = append(idx.Names[bucket], o.Hash[:]...) + + offset := o.Offset + if offset > math.MaxInt32 { + offset = w.addOffset64(offset) + } + + buf.Truncate(0) + binary.WriteUint32(buf, uint32(offset)) + idx.Offset32[bucket] = append(idx.Offset32[bucket], buf.Bytes()...) + + buf.Truncate(0) + binary.WriteUint32(buf, o.CRC32) + idx.CRC32[bucket] = append(idx.CRC32[bucket], buf.Bytes()...) + } + + for j := last + 1; j < 256; j++ { + idx.Fanout[j] = uint32(len(w.objects)) + } + + idx.Version = VersionSupported + idx.PackfileChecksum = w.checksum + + return idx, nil +} + +func (w *Writer) addOffset64(pos uint64) uint64 { + buf := new(bytes.Buffer) + binary.WriteUint64(buf, pos) + w.index.Offset64 = append(w.index.Offset64, buf.Bytes()...) + + index := uint64(w.offset64 | (1 << 31)) + w.offset64++ + + return index +} + +func (o objects) Len() int { + return len(o) +} + +func (o objects) Less(i int, j int) bool { + cmp := bytes.Compare(o[i].Hash[:], o[j].Hash[:]) + return cmp < 0 +} + +func (o objects) Swap(i int, j int) { + o[i], o[j] = o[j], o[i] +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/decoder.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/decoder.go new file mode 100644 index 0000000000..98f92fda64 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/decoder.go @@ -0,0 +1,477 @@ +package index + +import ( + "bufio" + "bytes" + "crypto/sha1" + "errors" + "hash" + "io" + "io/ioutil" + "strconv" + "time" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/utils/binary" +) + +var ( + // DecodeVersionSupported is the range of supported index versions + DecodeVersionSupported = struct{ Min, Max uint32 }{Min: 2, Max: 4} + + // ErrMalformedSignature is returned by Decode when the index header file is + // malformed + ErrMalformedSignature = errors.New("malformed index signature file") + // ErrInvalidChecksum is returned by Decode if the SHA1 hash mismatch with + // the read content + ErrInvalidChecksum = errors.New("invalid checksum") + + errUnknownExtension = errors.New("unknown extension") +) + +const ( + entryHeaderLength = 62 + entryExtended = 0x4000 + entryValid = 0x8000 + nameMask = 0xfff + intentToAddMask = 1 << 13 + skipWorkTreeMask = 1 << 14 +) + +// A Decoder reads and decodes index files from an input stream. +type Decoder struct { + r io.Reader + hash hash.Hash + lastEntry *Entry + + extReader *bufio.Reader +} + +// NewDecoder returns a new decoder that reads from r. +func NewDecoder(r io.Reader) *Decoder { + h := sha1.New() + return &Decoder{ + r: io.TeeReader(r, h), + hash: h, + extReader: bufio.NewReader(nil), + } +} + +// Decode reads the whole index object from its input and stores it in the +// value pointed to by idx. +func (d *Decoder) Decode(idx *Index) error { + var err error + idx.Version, err = validateHeader(d.r) + if err != nil { + return err + } + + entryCount, err := binary.ReadUint32(d.r) + if err != nil { + return err + } + + if err := d.readEntries(idx, int(entryCount)); err != nil { + return err + } + + return d.readExtensions(idx) +} + +func (d *Decoder) readEntries(idx *Index, count int) error { + for i := 0; i < count; i++ { + e, err := d.readEntry(idx) + if err != nil { + return err + } + + d.lastEntry = e + idx.Entries = append(idx.Entries, e) + } + + return nil +} + +func (d *Decoder) readEntry(idx *Index) (*Entry, error) { + e := &Entry{} + + var msec, mnsec, sec, nsec uint32 + var flags uint16 + + flow := []interface{}{ + &sec, &nsec, + &msec, &mnsec, + &e.Dev, + &e.Inode, + &e.Mode, + &e.UID, + &e.GID, + &e.Size, + &e.Hash, + &flags, + } + + if err := binary.Read(d.r, flow...); err != nil { + return nil, err + } + + read := entryHeaderLength + + if sec != 0 || nsec != 0 { + e.CreatedAt = time.Unix(int64(sec), int64(nsec)) + } + + if msec != 0 || mnsec != 0 { + e.ModifiedAt = time.Unix(int64(msec), int64(mnsec)) + } + + e.Stage = Stage(flags>>12) & 0x3 + + if flags&entryExtended != 0 { + extended, err := binary.ReadUint16(d.r) + if err != nil { + return nil, err + } + + read += 2 + e.IntentToAdd = extended&intentToAddMask != 0 + e.SkipWorktree = extended&skipWorkTreeMask != 0 + } + + if err := d.readEntryName(idx, e, flags); err != nil { + return nil, err + } + + return e, d.padEntry(idx, e, read) +} + +func (d *Decoder) readEntryName(idx *Index, e *Entry, flags uint16) error { + var name string + var err error + + switch idx.Version { + case 2, 3: + len := flags & nameMask + name, err = d.doReadEntryName(len) + case 4: + name, err = d.doReadEntryNameV4() + default: + return ErrUnsupportedVersion + } + + if err != nil { + return err + } + + e.Name = name + return nil +} + +func (d *Decoder) doReadEntryNameV4() (string, error) { + l, err := binary.ReadVariableWidthInt(d.r) + if err != nil { + return "", err + } + + var base string + if d.lastEntry != nil { + base = d.lastEntry.Name[:len(d.lastEntry.Name)-int(l)] + } + + name, err := binary.ReadUntil(d.r, '\x00') + if err != nil { + return "", err + } + + return base + string(name), nil +} + +func (d *Decoder) doReadEntryName(len uint16) (string, error) { + name := make([]byte, len) + _, err := io.ReadFull(d.r, name[:]) + + return string(name), err +} + +// Index entries are padded out to the next 8 byte alignment +// for historical reasons related to how C Git read the files. +func (d *Decoder) padEntry(idx *Index, e *Entry, read int) error { + if idx.Version == 4 { + return nil + } + + entrySize := read + len(e.Name) + padLen := 8 - entrySize%8 + _, err := io.CopyN(ioutil.Discard, d.r, int64(padLen)) + return err +} + +func (d *Decoder) readExtensions(idx *Index) error { + // TODO: support 'Split index' and 'Untracked cache' extensions, take in + // count that they are not supported by jgit or libgit + + var expected []byte + var err error + + var header [4]byte + for { + expected = d.hash.Sum(nil) + + var n int + if n, err = io.ReadFull(d.r, header[:]); err != nil { + if n == 0 { + err = io.EOF + } + + break + } + + err = d.readExtension(idx, header[:]) + if err != nil { + break + } + } + + if err != errUnknownExtension { + return err + } + + return d.readChecksum(expected, header) +} + +func (d *Decoder) readExtension(idx *Index, header []byte) error { + switch { + case bytes.Equal(header, treeExtSignature): + r, err := d.getExtensionReader() + if err != nil { + return err + } + + idx.Cache = &Tree{} + d := &treeExtensionDecoder{r} + if err := d.Decode(idx.Cache); err != nil { + return err + } + case bytes.Equal(header, resolveUndoExtSignature): + r, err := d.getExtensionReader() + if err != nil { + return err + } + + idx.ResolveUndo = &ResolveUndo{} + d := &resolveUndoDecoder{r} + if err := d.Decode(idx.ResolveUndo); err != nil { + return err + } + case bytes.Equal(header, endOfIndexEntryExtSignature): + r, err := d.getExtensionReader() + if err != nil { + return err + } + + idx.EndOfIndexEntry = &EndOfIndexEntry{} + d := &endOfIndexEntryDecoder{r} + if err := d.Decode(idx.EndOfIndexEntry); err != nil { + return err + } + default: + return errUnknownExtension + } + + return nil +} + +func (d *Decoder) getExtensionReader() (*bufio.Reader, error) { + len, err := binary.ReadUint32(d.r) + if err != nil { + return nil, err + } + + d.extReader.Reset(&io.LimitedReader{R: d.r, N: int64(len)}) + return d.extReader, nil +} + +func (d *Decoder) readChecksum(expected []byte, alreadyRead [4]byte) error { + var h plumbing.Hash + copy(h[:4], alreadyRead[:]) + + if _, err := io.ReadFull(d.r, h[4:]); err != nil { + return err + } + + if !bytes.Equal(h[:], expected) { + return ErrInvalidChecksum + } + + return nil +} + +func validateHeader(r io.Reader) (version uint32, err error) { + var s = make([]byte, 4) + if _, err := io.ReadFull(r, s); err != nil { + return 0, err + } + + if !bytes.Equal(s, indexSignature) { + return 0, ErrMalformedSignature + } + + version, err = binary.ReadUint32(r) + if err != nil { + return 0, err + } + + if version < DecodeVersionSupported.Min || version > DecodeVersionSupported.Max { + return 0, ErrUnsupportedVersion + } + + return +} + +type treeExtensionDecoder struct { + r *bufio.Reader +} + +func (d *treeExtensionDecoder) Decode(t *Tree) error { + for { + e, err := d.readEntry() + if err != nil { + if err == io.EOF { + return nil + } + + return err + } + + if e == nil { + continue + } + + t.Entries = append(t.Entries, *e) + } +} + +func (d *treeExtensionDecoder) readEntry() (*TreeEntry, error) { + e := &TreeEntry{} + + path, err := binary.ReadUntil(d.r, '\x00') + if err != nil { + return nil, err + } + + e.Path = string(path) + + count, err := binary.ReadUntil(d.r, ' ') + if err != nil { + return nil, err + } + + i, err := strconv.Atoi(string(count)) + if err != nil { + return nil, err + } + + // An entry can be in an invalidated state and is represented by having a + // negative number in the entry_count field. + if i == -1 { + return nil, nil + } + + e.Entries = i + trees, err := binary.ReadUntil(d.r, '\n') + if err != nil { + return nil, err + } + + i, err = strconv.Atoi(string(trees)) + if err != nil { + return nil, err + } + + e.Trees = i + _, err = io.ReadFull(d.r, e.Hash[:]) + + return e, nil +} + +type resolveUndoDecoder struct { + r *bufio.Reader +} + +func (d *resolveUndoDecoder) Decode(ru *ResolveUndo) error { + for { + e, err := d.readEntry() + if err != nil { + if err == io.EOF { + return nil + } + + return err + } + + ru.Entries = append(ru.Entries, *e) + } +} + +func (d *resolveUndoDecoder) readEntry() (*ResolveUndoEntry, error) { + e := &ResolveUndoEntry{ + Stages: make(map[Stage]plumbing.Hash), + } + + path, err := binary.ReadUntil(d.r, '\x00') + if err != nil { + return nil, err + } + + e.Path = string(path) + + for i := 0; i < 3; i++ { + if err := d.readStage(e, Stage(i+1)); err != nil { + return nil, err + } + } + + for s := range e.Stages { + var hash plumbing.Hash + if _, err := io.ReadFull(d.r, hash[:]); err != nil { + return nil, err + } + + e.Stages[s] = hash + } + + return e, nil +} + +func (d *resolveUndoDecoder) readStage(e *ResolveUndoEntry, s Stage) error { + ascii, err := binary.ReadUntil(d.r, '\x00') + if err != nil { + return err + } + + stage, err := strconv.ParseInt(string(ascii), 8, 64) + if err != nil { + return err + } + + if stage != 0 { + e.Stages[s] = plumbing.ZeroHash + } + + return nil +} + +type endOfIndexEntryDecoder struct { + r *bufio.Reader +} + +func (d *endOfIndexEntryDecoder) Decode(e *EndOfIndexEntry) error { + var err error + e.Offset, err = binary.ReadUint32(d.r) + if err != nil { + return err + } + + _, err = io.ReadFull(d.r, e.Hash[:]) + return err +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/doc.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/doc.go new file mode 100644 index 0000000000..39ae6ad5f9 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/doc.go @@ -0,0 +1,360 @@ +// Package index implements encoding and decoding of index format files. +// +// Git index format +// ================ +// +// == The Git index file has the following format +// +// All binary numbers are in network byte order. Version 2 is described +// here unless stated otherwise. +// +// - A 12-byte header consisting of +// +// 4-byte signature: +// The signature is { 'D', 'I', 'R', 'C' } (stands for "dircache") +// +// 4-byte version number: +// The current supported versions are 2, 3 and 4. +// +// 32-bit number of index entries. +// +// - A number of sorted index entries (see below). +// +// - Extensions +// +// Extensions are identified by signature. Optional extensions can +// be ignored if Git does not understand them. +// +// Git currently supports cached tree and resolve undo extensions. +// +// 4-byte extension signature. If the first byte is 'A'..'Z' the +// extension is optional and can be ignored. +// +// 32-bit size of the extension +// +// Extension data +// +// - 160-bit SHA-1 over the content of the index file before this +// checksum. +// +// == Index entry +// +// Index entries are sorted in ascending order on the name field, +// interpreted as a string of unsigned bytes (i.e. memcmp() order, no +// localization, no special casing of directory separator '/'). Entries +// with the same name are sorted by their stage field. +// +// 32-bit ctime seconds, the last time a file's metadata changed +// this is stat(2) data +// +// 32-bit ctime nanosecond fractions +// this is stat(2) data +// +// 32-bit mtime seconds, the last time a file's data changed +// this is stat(2) data +// +// 32-bit mtime nanosecond fractions +// this is stat(2) data +// +// 32-bit dev +// this is stat(2) data +// +// 32-bit ino +// this is stat(2) data +// +// 32-bit mode, split into (high to low bits) +// +// 4-bit object type +// valid values in binary are 1000 (regular file), 1010 (symbolic link) +// and 1110 (gitlink) +// +// 3-bit unused +// +// 9-bit unix permission. Only 0755 and 0644 are valid for regular files. +// Symbolic links and gitlinks have value 0 in this field. +// +// 32-bit uid +// this is stat(2) data +// +// 32-bit gid +// this is stat(2) data +// +// 32-bit file size +// This is the on-disk size from stat(2), truncated to 32-bit. +// +// 160-bit SHA-1 for the represented object +// +// A 16-bit 'flags' field split into (high to low bits) +// +// 1-bit assume-valid flag +// +// 1-bit extended flag (must be zero in version 2) +// +// 2-bit stage (during merge) +// +// 12-bit name length if the length is less than 0xFFF; otherwise 0xFFF +// is stored in this field. +// +// (Version 3 or later) A 16-bit field, only applicable if the +// "extended flag" above is 1, split into (high to low bits). +// +// 1-bit reserved for future +// +// 1-bit skip-worktree flag (used by sparse checkout) +// +// 1-bit intent-to-add flag (used by "git add -N") +// +// 13-bit unused, must be zero +// +// Entry path name (variable length) relative to top level directory +// (without leading slash). '/' is used as path separator. The special +// path components ".", ".." and ".git" (without quotes) are disallowed. +// Trailing slash is also disallowed. +// +// The exact encoding is undefined, but the '.' and '/' characters +// are encoded in 7-bit ASCII and the encoding cannot contain a NUL +// byte (iow, this is a UNIX pathname). +// +// (Version 4) In version 4, the entry path name is prefix-compressed +// relative to the path name for the previous entry (the very first +// entry is encoded as if the path name for the previous entry is an +// empty string). At the beginning of an entry, an integer N in the +// variable width encoding (the same encoding as the offset is encoded +// for OFS_DELTA pack entries; see pack-format.txt) is stored, followed +// by a NUL-terminated string S. Removing N bytes from the end of the +// path name for the previous entry, and replacing it with the string S +// yields the path name for this entry. +// +// 1-8 nul bytes as necessary to pad the entry to a multiple of eight bytes +// while keeping the name NUL-terminated. +// +// (Version 4) In version 4, the padding after the pathname does not +// exist. +// +// Interpretation of index entries in split index mode is completely +// different. See below for details. +// +// == Extensions +// +// === Cached tree +// +// Cached tree extension contains pre-computed hashes for trees that can +// be derived from the index. It helps speed up tree object generation +// from index for a new commit. +// +// When a path is updated in index, the path must be invalidated and +// removed from tree cache. +// +// The signature for this extension is { 'T', 'R', 'E', 'E' }. +// +// A series of entries fill the entire extension; each of which +// consists of: +// +// - NUL-terminated path component (relative to its parent directory); +// +// - ASCII decimal number of entries in the index that is covered by the +// tree this entry represents (entry_count); +// +// - A space (ASCII 32); +// +// - ASCII decimal number that represents the number of subtrees this +// tree has; +// +// - A newline (ASCII 10); and +// +// - 160-bit object name for the object that would result from writing +// this span of index as a tree. +// +// An entry can be in an invalidated state and is represented by having +// a negative number in the entry_count field. In this case, there is no +// object name and the next entry starts immediately after the newline. +// When writing an invalid entry, -1 should always be used as entry_count. +// +// The entries are written out in the top-down, depth-first order. The +// first entry represents the root level of the repository, followed by the +// first subtree--let's call this A--of the root level (with its name +// relative to the root level), followed by the first subtree of A (with +// its name relative to A), ... +// +// === Resolve undo +// +// A conflict is represented in the index as a set of higher stage entries. +// When a conflict is resolved (e.g. with "git add path"), these higher +// stage entries will be removed and a stage-0 entry with proper resolution +// is added. +// +// When these higher stage entries are removed, they are saved in the +// resolve undo extension, so that conflicts can be recreated (e.g. with +// "git checkout -m"), in case users want to redo a conflict resolution +// from scratch. +// +// The signature for this extension is { 'R', 'E', 'U', 'C' }. +// +// A series of entries fill the entire extension; each of which +// consists of: +// +// - NUL-terminated pathname the entry describes (relative to the root of +// the repository, i.e. full pathname); +// +// - Three NUL-terminated ASCII octal numbers, entry mode of entries in +// stage 1 to 3 (a missing stage is represented by "0" in this field); +// and +// +// - At most three 160-bit object names of the entry in stages from 1 to 3 +// (nothing is written for a missing stage). +// +// === Split index +// +// In split index mode, the majority of index entries could be stored +// in a separate file. This extension records the changes to be made on +// top of that to produce the final index. +// +// The signature for this extension is { 'l', 'i', 'n', 'k' }. +// +// The extension consists of: +// +// - 160-bit SHA-1 of the shared index file. The shared index file path +// is $GIT_DIR/sharedindex.. If all 160 bits are zero, the +// index does not require a shared index file. +// +// - An ewah-encoded delete bitmap, each bit represents an entry in the +// shared index. If a bit is set, its corresponding entry in the +// shared index will be removed from the final index. Note, because +// a delete operation changes index entry positions, but we do need +// original positions in replace phase, it's best to just mark +// entries for removal, then do a mass deletion after replacement. +// +// - An ewah-encoded replace bitmap, each bit represents an entry in +// the shared index. If a bit is set, its corresponding entry in the +// shared index will be replaced with an entry in this index +// file. All replaced entries are stored in sorted order in this +// index. The first "1" bit in the replace bitmap corresponds to the +// first index entry, the second "1" bit to the second entry and so +// on. Replaced entries may have empty path names to save space. +// +// The remaining index entries after replaced ones will be added to the +// final index. These added entries are also sorted by entry name then +// stage. +// +// == Untracked cache +// +// Untracked cache saves the untracked file list and necessary data to +// verify the cache. The signature for this extension is { 'U', 'N', +// 'T', 'R' }. +// +// The extension starts with +// +// - A sequence of NUL-terminated strings, preceded by the size of the +// sequence in variable width encoding. Each string describes the +// environment where the cache can be used. +// +// - Stat data of $GIT_DIR/info/exclude. See "Index entry" section from +// ctime field until "file size". +// +// - Stat data of plumbing.excludesfile +// +// - 32-bit dir_flags (see struct dir_struct) +// +// - 160-bit SHA-1 of $GIT_DIR/info/exclude. Null SHA-1 means the file +// does not exist. +// +// - 160-bit SHA-1 of plumbing.excludesfile. Null SHA-1 means the file does +// not exist. +// +// - NUL-terminated string of per-dir exclude file name. This usually +// is ".gitignore". +// +// - The number of following directory blocks, variable width +// encoding. If this number is zero, the extension ends here with a +// following NUL. +// +// - A number of directory blocks in depth-first-search order, each +// consists of +// +// - The number of untracked entries, variable width encoding. +// +// - The number of sub-directory blocks, variable width encoding. +// +// - The directory name terminated by NUL. +// +// - A number of untracked file/dir names terminated by NUL. +// +// The remaining data of each directory block is grouped by type: +// +// - An ewah bitmap, the n-th bit marks whether the n-th directory has +// valid untracked cache entries. +// +// - An ewah bitmap, the n-th bit records "check-only" bit of +// read_directory_recursive() for the n-th directory. +// +// - An ewah bitmap, the n-th bit indicates whether SHA-1 and stat data +// is valid for the n-th directory and exists in the next data. +// +// - An array of stat data. The n-th data corresponds with the n-th +// "one" bit in the previous ewah bitmap. +// +// - An array of SHA-1. The n-th SHA-1 corresponds with the n-th "one" bit +// in the previous ewah bitmap. +// +// - One NUL. +// +// == File System Monitor cache +// +// The file system monitor cache tracks files for which the core.fsmonitor +// hook has told us about changes. The signature for this extension is +// { 'F', 'S', 'M', 'N' }. +// +// The extension starts with +// +// - 32-bit version number: the current supported version is 1. +// +// - 64-bit time: the extension data reflects all changes through the given +// time which is stored as the nanoseconds elapsed since midnight, +// January 1, 1970. +// +// - 32-bit bitmap size: the size of the CE_FSMONITOR_VALID bitmap. +// +// - An ewah bitmap, the n-th bit indicates whether the n-th index entry +// is not CE_FSMONITOR_VALID. +// +// == End of Index Entry +// +// The End of Index Entry (EOIE) is used to locate the end of the variable +// length index entries and the beginning of the extensions. Code can take +// advantage of this to quickly locate the index extensions without having +// to parse through all of the index entries. +// +// Because it must be able to be loaded before the variable length cache +// entries and other index extensions, this extension must be written last. +// The signature for this extension is { 'E', 'O', 'I', 'E' }. +// +// The extension consists of: +// +// - 32-bit offset to the end of the index entries +// +// - 160-bit SHA-1 over the extension types and their sizes (but not +// their contents). E.g. if we have "TREE" extension that is N-bytes +// long, "REUC" extension that is M-bytes long, followed by "EOIE", +// then the hash would be: +// +// SHA-1("TREE" + + +// "REUC" + ) +// +// == Index Entry Offset Table +// +// The Index Entry Offset Table (IEOT) is used to help address the CPU +// cost of loading the index by enabling multi-threading the process of +// converting cache entries from the on-disk format to the in-memory format. +// The signature for this extension is { 'I', 'E', 'O', 'T' }. +// +// The extension consists of: +// +// - 32-bit version (currently 1) +// +// - A number of index offset entries each consisting of: +// +// - 32-bit offset from the beginning of the file to the first cache entry +// in this block of entries. +// +// - 32-bit count of cache entries in this blockpackage index +package index diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/encoder.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/encoder.go new file mode 100644 index 0000000000..7111314c93 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/encoder.go @@ -0,0 +1,150 @@ +package index + +import ( + "bytes" + "crypto/sha1" + "errors" + "hash" + "io" + "sort" + "time" + + "gopkg.in/src-d/go-git.v4/utils/binary" +) + +var ( + // EncodeVersionSupported is the range of supported index versions + EncodeVersionSupported uint32 = 2 + + // ErrInvalidTimestamp is returned by Encode if a Index with a Entry with + // negative timestamp values + ErrInvalidTimestamp = errors.New("negative timestamps are not allowed") +) + +// An Encoder writes an Index to an output stream. +type Encoder struct { + w io.Writer + hash hash.Hash +} + +// NewEncoder returns a new encoder that writes to w. +func NewEncoder(w io.Writer) *Encoder { + h := sha1.New() + mw := io.MultiWriter(w, h) + return &Encoder{mw, h} +} + +// Encode writes the Index to the stream of the encoder. +func (e *Encoder) Encode(idx *Index) error { + // TODO: support versions v3 and v4 + // TODO: support extensions + if idx.Version != EncodeVersionSupported { + return ErrUnsupportedVersion + } + + if err := e.encodeHeader(idx); err != nil { + return err + } + + if err := e.encodeEntries(idx); err != nil { + return err + } + + return e.encodeFooter() +} + +func (e *Encoder) encodeHeader(idx *Index) error { + return binary.Write(e.w, + indexSignature, + idx.Version, + uint32(len(idx.Entries)), + ) +} + +func (e *Encoder) encodeEntries(idx *Index) error { + sort.Sort(byName(idx.Entries)) + + for _, entry := range idx.Entries { + if err := e.encodeEntry(entry); err != nil { + return err + } + + wrote := entryHeaderLength + len(entry.Name) + if err := e.padEntry(wrote); err != nil { + return err + } + } + + return nil +} + +func (e *Encoder) encodeEntry(entry *Entry) error { + if entry.IntentToAdd || entry.SkipWorktree { + return ErrUnsupportedVersion + } + + sec, nsec, err := e.timeToUint32(&entry.CreatedAt) + if err != nil { + return err + } + + msec, mnsec, err := e.timeToUint32(&entry.ModifiedAt) + if err != nil { + return err + } + + flags := uint16(entry.Stage&0x3) << 12 + if l := len(entry.Name); l < nameMask { + flags |= uint16(l) + } else { + flags |= nameMask + } + + flow := []interface{}{ + sec, nsec, + msec, mnsec, + entry.Dev, + entry.Inode, + entry.Mode, + entry.UID, + entry.GID, + entry.Size, + entry.Hash[:], + flags, + } + + if err := binary.Write(e.w, flow...); err != nil { + return err + } + + return binary.Write(e.w, []byte(entry.Name)) +} + +func (e *Encoder) timeToUint32(t *time.Time) (uint32, uint32, error) { + if t.IsZero() { + return 0, 0, nil + } + + if t.Unix() < 0 || t.UnixNano() < 0 { + return 0, 0, ErrInvalidTimestamp + } + + return uint32(t.Unix()), uint32(t.Nanosecond()), nil +} + +func (e *Encoder) padEntry(wrote int) error { + padLen := 8 - wrote%8 + + _, err := e.w.Write(bytes.Repeat([]byte{'\x00'}, padLen)) + return err +} + +func (e *Encoder) encodeFooter() error { + return binary.Write(e.w, e.hash.Sum(nil)) +} + +type byName []*Entry + +func (l byName) Len() int { return len(l) } +func (l byName) Swap(i, j int) { l[i], l[j] = l[j], l[i] } +func (l byName) Less(i, j int) bool { return l[i].Name < l[j].Name } diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/index.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/index.go new file mode 100644 index 0000000000..6653c91d2b --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/index.go @@ -0,0 +1,213 @@ +package index + +import ( + "bytes" + "errors" + "fmt" + "path/filepath" + "time" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/filemode" +) + +var ( + // ErrUnsupportedVersion is returned by Decode when the index file version + // is not supported. + ErrUnsupportedVersion = errors.New("unsupported version") + // ErrEntryNotFound is returned by Index.Entry, if an entry is not found. + ErrEntryNotFound = errors.New("entry not found") + + indexSignature = []byte{'D', 'I', 'R', 'C'} + treeExtSignature = []byte{'T', 'R', 'E', 'E'} + resolveUndoExtSignature = []byte{'R', 'E', 'U', 'C'} + endOfIndexEntryExtSignature = []byte{'E', 'O', 'I', 'E'} +) + +// Stage during merge +type Stage int + +const ( + // Merged is the default stage, fully merged + Merged Stage = 1 + // AncestorMode is the base revision + AncestorMode Stage = 1 + // OurMode is the first tree revision, ours + OurMode Stage = 2 + // TheirMode is the second tree revision, theirs + TheirMode Stage = 3 +) + +// Index contains the information about which objects are currently checked out +// in the worktree, having information about the working files. Changes in +// worktree are detected using this Index. The Index is also used during merges +type Index struct { + // Version is index version + Version uint32 + // Entries collection of entries represented by this Index. The order of + // this collection is not guaranteed + Entries []*Entry + // Cache represents the 'Cached tree' extension + Cache *Tree + // ResolveUndo represents the 'Resolve undo' extension + ResolveUndo *ResolveUndo + // EndOfIndexEntry represents the 'End of Index Entry' extension + EndOfIndexEntry *EndOfIndexEntry +} + +// Add creates a new Entry and returns it. The caller should first check that +// another entry with the same path does not exist. +func (i *Index) Add(path string) *Entry { + e := &Entry{ + Name: filepath.ToSlash(path), + } + + i.Entries = append(i.Entries, e) + return e +} + +// Entry returns the entry that match the given path, if any. +func (i *Index) Entry(path string) (*Entry, error) { + path = filepath.ToSlash(path) + for _, e := range i.Entries { + if e.Name == path { + return e, nil + } + } + + return nil, ErrEntryNotFound +} + +// Remove remove the entry that match the give path and returns deleted entry. +func (i *Index) Remove(path string) (*Entry, error) { + path = filepath.ToSlash(path) + for index, e := range i.Entries { + if e.Name == path { + i.Entries = append(i.Entries[:index], i.Entries[index+1:]...) + return e, nil + } + } + + return nil, ErrEntryNotFound +} + +// Glob returns the all entries matching pattern or nil if there is no matching +// entry. The syntax of patterns is the same as in filepath.Glob. +func (i *Index) Glob(pattern string) (matches []*Entry, err error) { + pattern = filepath.ToSlash(pattern) + for _, e := range i.Entries { + m, err := match(pattern, e.Name) + if err != nil { + return nil, err + } + + if m { + matches = append(matches, e) + } + } + + return +} + +// String is equivalent to `git ls-files --stage --debug` +func (i *Index) String() string { + buf := bytes.NewBuffer(nil) + for _, e := range i.Entries { + buf.WriteString(e.String()) + } + + return buf.String() +} + +// Entry represents a single file (or stage of a file) in the cache. An entry +// represents exactly one stage of a file. If a file path is unmerged then +// multiple Entry instances may appear for the same path name. +type Entry struct { + // Hash is the SHA1 of the represented file + Hash plumbing.Hash + // Name is the Entry path name relative to top level directory + Name string + // CreatedAt time when the tracked path was created + CreatedAt time.Time + // ModifiedAt time when the tracked path was changed + ModifiedAt time.Time + // Dev and Inode of the tracked path + Dev, Inode uint32 + // Mode of the path + Mode filemode.FileMode + // UID and GID, userid and group id of the owner + UID, GID uint32 + // Size is the length in bytes for regular files + Size uint32 + // Stage on a merge is defines what stage is representing this entry + // https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging + Stage Stage + // SkipWorktree used in sparse checkouts + // https://git-scm.com/docs/git-read-tree#_sparse_checkout + SkipWorktree bool + // IntentToAdd record only the fact that the path will be added later + // https://git-scm.com/docs/git-add ("git add -N") + IntentToAdd bool +} + +func (e Entry) String() string { + buf := bytes.NewBuffer(nil) + + fmt.Fprintf(buf, "%06o %s %d\t%s\n", e.Mode, e.Hash, e.Stage, e.Name) + fmt.Fprintf(buf, " ctime: %d:%d\n", e.CreatedAt.Unix(), e.CreatedAt.Nanosecond()) + fmt.Fprintf(buf, " mtime: %d:%d\n", e.ModifiedAt.Unix(), e.ModifiedAt.Nanosecond()) + fmt.Fprintf(buf, " dev: %d\tino: %d\n", e.Dev, e.Inode) + fmt.Fprintf(buf, " uid: %d\tgid: %d\n", e.UID, e.GID) + fmt.Fprintf(buf, " size: %d\tflags: %x\n", e.Size, 0) + + return buf.String() +} + +// Tree contains pre-computed hashes for trees that can be derived from the +// index. It helps speed up tree object generation from index for a new commit. +type Tree struct { + Entries []TreeEntry +} + +// TreeEntry entry of a cached Tree +type TreeEntry struct { + // Path component (relative to its parent directory) + Path string + // Entries is the number of entries in the index that is covered by the tree + // this entry represents. + Entries int + // Trees is the number that represents the number of subtrees this tree has + Trees int + // Hash object name for the object that would result from writing this span + // of index as a tree. + Hash plumbing.Hash +} + +// ResolveUndo is used when a conflict is resolved (e.g. with "git add path"), +// these higher stage entries are removed and a stage-0 entry with proper +// resolution is added. When these higher stage entries are removed, they are +// saved in the resolve undo extension. +type ResolveUndo struct { + Entries []ResolveUndoEntry +} + +// ResolveUndoEntry contains the information about a conflict when is resolved +type ResolveUndoEntry struct { + Path string + Stages map[Stage]plumbing.Hash +} + +// EndOfIndexEntry is the End of Index Entry (EOIE) is used to locate the end of +// the variable length index entries and the beginning of the extensions. Code +// can take advantage of this to quickly locate the index extensions without +// having to parse through all of the index entries. +// +// Because it must be able to be loaded before the variable length cache +// entries and other index extensions, this extension must be written last. +type EndOfIndexEntry struct { + // Offset to the end of the index entries + Offset uint32 + // Hash is a SHA-1 over the extension types and their sizes (but not + // their contents). + Hash plumbing.Hash +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/match.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/match.go new file mode 100644 index 0000000000..2891d7d34c --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/index/match.go @@ -0,0 +1,186 @@ +package index + +import ( + "path/filepath" + "runtime" + "unicode/utf8" +) + +// match is filepath.Match with support to match fullpath and not only filenames +// code from: +// https://github.com/golang/go/blob/39852bf4cce6927e01d0136c7843f65a801738cb/src/path/filepath/match.go#L44-L224 +func match(pattern, name string) (matched bool, err error) { +Pattern: + for len(pattern) > 0 { + var star bool + var chunk string + star, chunk, pattern = scanChunk(pattern) + + // Look for match at current position. + t, ok, err := matchChunk(chunk, name) + // if we're the last chunk, make sure we've exhausted the name + // otherwise we'll give a false result even if we could still match + // using the star + if ok && (len(t) == 0 || len(pattern) > 0) { + name = t + continue + } + if err != nil { + return false, err + } + if star { + // Look for match skipping i+1 bytes. + // Cannot skip /. + for i := 0; i < len(name); i++ { + t, ok, err := matchChunk(chunk, name[i+1:]) + if ok { + // if we're the last chunk, make sure we exhausted the name + if len(pattern) == 0 && len(t) > 0 { + continue + } + name = t + continue Pattern + } + if err != nil { + return false, err + } + } + } + return false, nil + } + return len(name) == 0, nil +} + +// scanChunk gets the next segment of pattern, which is a non-star string +// possibly preceded by a star. +func scanChunk(pattern string) (star bool, chunk, rest string) { + for len(pattern) > 0 && pattern[0] == '*' { + pattern = pattern[1:] + star = true + } + inrange := false + var i int +Scan: + for i = 0; i < len(pattern); i++ { + switch pattern[i] { + case '\\': + if runtime.GOOS != "windows" { + // error check handled in matchChunk: bad pattern. + if i+1 < len(pattern) { + i++ + } + } + case '[': + inrange = true + case ']': + inrange = false + case '*': + if !inrange { + break Scan + } + } + } + return star, pattern[0:i], pattern[i:] +} + +// matchChunk checks whether chunk matches the beginning of s. +// If so, it returns the remainder of s (after the match). +// Chunk is all single-character operators: literals, char classes, and ?. +func matchChunk(chunk, s string) (rest string, ok bool, err error) { + for len(chunk) > 0 { + if len(s) == 0 { + return + } + switch chunk[0] { + case '[': + // character class + r, n := utf8.DecodeRuneInString(s) + s = s[n:] + chunk = chunk[1:] + // We can't end right after '[', we're expecting at least + // a closing bracket and possibly a caret. + if len(chunk) == 0 { + err = filepath.ErrBadPattern + return + } + // possibly negated + negated := chunk[0] == '^' + if negated { + chunk = chunk[1:] + } + // parse all ranges + match := false + nrange := 0 + for { + if len(chunk) > 0 && chunk[0] == ']' && nrange > 0 { + chunk = chunk[1:] + break + } + var lo, hi rune + if lo, chunk, err = getEsc(chunk); err != nil { + return + } + hi = lo + if chunk[0] == '-' { + if hi, chunk, err = getEsc(chunk[1:]); err != nil { + return + } + } + if lo <= r && r <= hi { + match = true + } + nrange++ + } + if match == negated { + return + } + + case '?': + _, n := utf8.DecodeRuneInString(s) + s = s[n:] + chunk = chunk[1:] + + case '\\': + if runtime.GOOS != "windows" { + chunk = chunk[1:] + if len(chunk) == 0 { + err = filepath.ErrBadPattern + return + } + } + fallthrough + + default: + if chunk[0] != s[0] { + return + } + s = s[1:] + chunk = chunk[1:] + } + } + return s, true, nil +} + +// getEsc gets a possibly-escaped character from chunk, for a character class. +func getEsc(chunk string) (r rune, nchunk string, err error) { + if len(chunk) == 0 || chunk[0] == '-' || chunk[0] == ']' { + err = filepath.ErrBadPattern + return + } + if chunk[0] == '\\' && runtime.GOOS != "windows" { + chunk = chunk[1:] + if len(chunk) == 0 { + err = filepath.ErrBadPattern + return + } + } + r, n := utf8.DecodeRuneInString(chunk) + if r == utf8.RuneError && n == 1 { + err = filepath.ErrBadPattern + } + nchunk = chunk[n:] + if len(nchunk) == 0 { + err = filepath.ErrBadPattern + } + return +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/objfile/doc.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/objfile/doc.go new file mode 100644 index 0000000000..a7145160ae --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/objfile/doc.go @@ -0,0 +1,2 @@ +// Package objfile implements encoding and decoding of object files. +package objfile diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/objfile/reader.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/objfile/reader.go new file mode 100644 index 0000000000..c4467e4817 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/objfile/reader.go @@ -0,0 +1,114 @@ +package objfile + +import ( + "compress/zlib" + "errors" + "io" + "strconv" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/format/packfile" +) + +var ( + ErrClosed = errors.New("objfile: already closed") + ErrHeader = errors.New("objfile: invalid header") + ErrNegativeSize = errors.New("objfile: negative object size") +) + +// Reader reads and decodes compressed objfile data from a provided io.Reader. +// Reader implements io.ReadCloser. Close should be called when finished with +// the Reader. Close will not close the underlying io.Reader. +type Reader struct { + multi io.Reader + zlib io.ReadCloser + hasher plumbing.Hasher +} + +// NewReader returns a new Reader reading from r. +func NewReader(r io.Reader) (*Reader, error) { + zlib, err := zlib.NewReader(r) + if err != nil { + return nil, packfile.ErrZLib.AddDetails(err.Error()) + } + + return &Reader{ + zlib: zlib, + }, nil +} + +// Header reads the type and the size of object, and prepares the reader for read +func (r *Reader) Header() (t plumbing.ObjectType, size int64, err error) { + var raw []byte + raw, err = r.readUntil(' ') + if err != nil { + return + } + + t, err = plumbing.ParseObjectType(string(raw)) + if err != nil { + return + } + + raw, err = r.readUntil(0) + if err != nil { + return + } + + size, err = strconv.ParseInt(string(raw), 10, 64) + if err != nil { + err = ErrHeader + return + } + + defer r.prepareForRead(t, size) + return +} + +// readSlice reads one byte at a time from r until it encounters delim or an +// error. +func (r *Reader) readUntil(delim byte) ([]byte, error) { + var buf [1]byte + value := make([]byte, 0, 16) + for { + if n, err := r.zlib.Read(buf[:]); err != nil && (err != io.EOF || n == 0) { + if err == io.EOF { + return nil, ErrHeader + } + return nil, err + } + + if buf[0] == delim { + return value, nil + } + + value = append(value, buf[0]) + } +} + +func (r *Reader) prepareForRead(t plumbing.ObjectType, size int64) { + r.hasher = plumbing.NewHasher(t, size) + r.multi = io.TeeReader(r.zlib, r.hasher) +} + +// Read reads len(p) bytes into p from the object data stream. It returns +// the number of bytes read (0 <= n <= len(p)) and any error encountered. Even +// if Read returns n < len(p), it may use all of p as scratch space during the +// call. +// +// If Read encounters the end of the data stream it will return err == io.EOF, +// either in the current call if n > 0 or in a subsequent call. +func (r *Reader) Read(p []byte) (n int, err error) { + return r.multi.Read(p) +} + +// Hash returns the hash of the object data stream that has been read so far. +func (r *Reader) Hash() plumbing.Hash { + return r.hasher.Sum() +} + +// Close releases any resources consumed by the Reader. Calling Close does not +// close the wrapped io.Reader originally passed to NewReader. +func (r *Reader) Close() error { + return r.zlib.Close() +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/objfile/writer.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/objfile/writer.go new file mode 100644 index 0000000000..5555243401 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/objfile/writer.go @@ -0,0 +1,109 @@ +package objfile + +import ( + "compress/zlib" + "errors" + "io" + "strconv" + + "gopkg.in/src-d/go-git.v4/plumbing" +) + +var ( + ErrOverflow = errors.New("objfile: declared data length exceeded (overflow)") +) + +// Writer writes and encodes data in compressed objfile format to a provided +// io.Writer. Close should be called when finished with the Writer. Close will +// not close the underlying io.Writer. +type Writer struct { + raw io.Writer + zlib io.WriteCloser + hasher plumbing.Hasher + multi io.Writer + + closed bool + pending int64 // number of unwritten bytes +} + +// NewWriter returns a new Writer writing to w. +// +// The returned Writer implements io.WriteCloser. Close should be called when +// finished with the Writer. Close will not close the underlying io.Writer. +func NewWriter(w io.Writer) *Writer { + return &Writer{ + raw: w, + zlib: zlib.NewWriter(w), + } +} + +// WriteHeader writes the type and the size and prepares to accept the object's +// contents. If an invalid t is provided, plumbing.ErrInvalidType is returned. If a +// negative size is provided, ErrNegativeSize is returned. +func (w *Writer) WriteHeader(t plumbing.ObjectType, size int64) error { + if !t.Valid() { + return plumbing.ErrInvalidType + } + if size < 0 { + return ErrNegativeSize + } + + b := t.Bytes() + b = append(b, ' ') + b = append(b, []byte(strconv.FormatInt(size, 10))...) + b = append(b, 0) + + defer w.prepareForWrite(t, size) + _, err := w.zlib.Write(b) + + return err +} + +func (w *Writer) prepareForWrite(t plumbing.ObjectType, size int64) { + w.pending = size + + w.hasher = plumbing.NewHasher(t, size) + w.multi = io.MultiWriter(w.zlib, w.hasher) +} + +// Write writes the object's contents. Write returns the error ErrOverflow if +// more than size bytes are written after WriteHeader. +func (w *Writer) Write(p []byte) (n int, err error) { + if w.closed { + return 0, ErrClosed + } + + overwrite := false + if int64(len(p)) > w.pending { + p = p[0:w.pending] + overwrite = true + } + + n, err = w.multi.Write(p) + w.pending -= int64(n) + if err == nil && overwrite { + err = ErrOverflow + return + } + + return +} + +// Hash returns the hash of the object data stream that has been written so far. +// It can be called before or after Close. +func (w *Writer) Hash() plumbing.Hash { + return w.hasher.Sum() // Not yet closed, return hash of data written so far +} + +// Close releases any resources consumed by the Writer. +// +// Calling Close does not close the wrapped io.Writer originally passed to +// NewWriter. +func (w *Writer) Close() error { + if err := w.zlib.Close(); err != nil { + return err + } + + w.closed = true + return nil +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/common.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/common.go new file mode 100644 index 0000000000..f82c1abe55 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/common.go @@ -0,0 +1,78 @@ +package packfile + +import ( + "bytes" + "compress/zlib" + "io" + "sync" + + "gopkg.in/src-d/go-git.v4/plumbing/storer" + "gopkg.in/src-d/go-git.v4/utils/ioutil" +) + +var signature = []byte{'P', 'A', 'C', 'K'} + +const ( + // VersionSupported is the packfile version supported by this package + VersionSupported uint32 = 2 + + firstLengthBits = uint8(4) // the first byte into object header has 4 bits to store the length + lengthBits = uint8(7) // subsequent bytes has 7 bits to store the length + maskFirstLength = 15 // 0000 1111 + maskContinue = 0x80 // 1000 0000 + maskLength = uint8(127) // 0111 1111 + maskType = uint8(112) // 0111 0000 +) + +// UpdateObjectStorage updates the storer with the objects in the given +// packfile. +func UpdateObjectStorage(s storer.Storer, packfile io.Reader) error { + if pw, ok := s.(storer.PackfileWriter); ok { + return WritePackfileToObjectStorage(pw, packfile) + } + + p, err := NewParserWithStorage(NewScanner(packfile), s) + if err != nil { + return err + } + + _, err = p.Parse() + return err +} + +// WritePackfileToObjectStorage writes all the packfile objects into the given +// object storage. +func WritePackfileToObjectStorage( + sw storer.PackfileWriter, + packfile io.Reader, +) (err error) { + w, err := sw.PackfileWriter() + if err != nil { + return err + } + + defer ioutil.CheckClose(w, &err) + + var n int64 + n, err = io.Copy(w, packfile) + if err == nil && n == 0 { + return ErrEmptyPackfile + } + + return err +} + +var bufPool = sync.Pool{ + New: func() interface{} { + return bytes.NewBuffer(nil) + }, +} + +var zlibInitBytes = []byte{0x78, 0x9c, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01} + +var zlibReaderPool = sync.Pool{ + New: func() interface{} { + r, _ := zlib.NewReader(bytes.NewReader(zlibInitBytes)) + return r + }, +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/delta_index.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/delta_index.go new file mode 100644 index 0000000000..07a61120e5 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/delta_index.go @@ -0,0 +1,297 @@ +package packfile + +const blksz = 16 +const maxChainLength = 64 + +// deltaIndex is a modified version of JGit's DeltaIndex adapted to our current +// design. +type deltaIndex struct { + table []int + entries []int + mask int +} + +func (idx *deltaIndex) init(buf []byte) { + scanner := newDeltaIndexScanner(buf, len(buf)) + idx.mask = scanner.mask + idx.table = scanner.table + idx.entries = make([]int, countEntries(scanner)+1) + idx.copyEntries(scanner) +} + +// findMatch returns the offset of src where the block starting at tgtOffset +// is and the length of the match. A length of 0 means there was no match. A +// length of -1 means the src length is lower than the blksz and whatever +// other positive length is the length of the match in bytes. +func (idx *deltaIndex) findMatch(src, tgt []byte, tgtOffset int) (srcOffset, l int) { + if len(tgt) < tgtOffset+s { + return 0, len(tgt) - tgtOffset + } + + if len(src) < blksz { + return 0, -1 + } + + if len(tgt) >= tgtOffset+s && len(src) >= blksz { + h := hashBlock(tgt, tgtOffset) + tIdx := h & idx.mask + eIdx := idx.table[tIdx] + if eIdx != 0 { + srcOffset = idx.entries[eIdx] + } else { + return + } + + l = matchLength(src, tgt, tgtOffset, srcOffset) + } + + return +} + +func matchLength(src, tgt []byte, otgt, osrc int) (l int) { + lensrc := len(src) + lentgt := len(tgt) + for (osrc < lensrc && otgt < lentgt) && src[osrc] == tgt[otgt] { + l++ + osrc++ + otgt++ + } + return +} + +func countEntries(scan *deltaIndexScanner) (cnt int) { + // Figure out exactly how many entries we need. As we do the + // enumeration truncate any delta chains longer than what we + // are willing to scan during encode. This keeps the encode + // logic linear in the size of the input rather than quadratic. + for i := 0; i < len(scan.table); i++ { + h := scan.table[i] + if h == 0 { + continue + } + + size := 0 + for { + size++ + if size == maxChainLength { + scan.next[h] = 0 + break + } + h = scan.next[h] + + if h == 0 { + break + } + } + cnt += size + } + + return +} + +func (idx *deltaIndex) copyEntries(scanner *deltaIndexScanner) { + // Rebuild the entries list from the scanner, positioning all + // blocks in the same hash chain next to each other. We can + // then later discard the next list, along with the scanner. + // + next := 1 + for i := 0; i < len(idx.table); i++ { + h := idx.table[i] + if h == 0 { + continue + } + + idx.table[i] = next + for { + idx.entries[next] = scanner.entries[h] + next++ + h = scanner.next[h] + + if h == 0 { + break + } + } + } +} + +type deltaIndexScanner struct { + table []int + entries []int + next []int + mask int + count int +} + +func newDeltaIndexScanner(buf []byte, size int) *deltaIndexScanner { + size -= size % blksz + worstCaseBlockCnt := size / blksz + if worstCaseBlockCnt < 1 { + return new(deltaIndexScanner) + } + + tableSize := tableSize(worstCaseBlockCnt) + scanner := &deltaIndexScanner{ + table: make([]int, tableSize), + mask: tableSize - 1, + entries: make([]int, worstCaseBlockCnt+1), + next: make([]int, worstCaseBlockCnt+1), + } + + scanner.scan(buf, size) + return scanner +} + +// slightly modified version of JGit's DeltaIndexScanner. We store the offset on the entries +// instead of the entries and the key, so we avoid operations to retrieve the offset later, as +// we don't use the key. +// See: https://github.com/eclipse/jgit/blob/005e5feb4ecd08c4e4d141a38b9e7942accb3212/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaIndexScanner.java +func (s *deltaIndexScanner) scan(buf []byte, end int) { + lastHash := 0 + ptr := end - blksz + + for { + key := hashBlock(buf, ptr) + tIdx := key & s.mask + head := s.table[tIdx] + if head != 0 && lastHash == key { + s.entries[head] = ptr + } else { + s.count++ + eIdx := s.count + s.entries[eIdx] = ptr + s.next[eIdx] = head + s.table[tIdx] = eIdx + } + + lastHash = key + ptr -= blksz + + if 0 > ptr { + break + } + } +} + +func tableSize(worstCaseBlockCnt int) int { + shift := 32 - leadingZeros(uint32(worstCaseBlockCnt)) + sz := 1 << uint(shift-1) + if sz < worstCaseBlockCnt { + sz <<= 1 + } + return sz +} + +// use https://golang.org/pkg/math/bits/#LeadingZeros32 in the future +func leadingZeros(x uint32) (n int) { + if x >= 1<<16 { + x >>= 16 + n = 16 + } + if x >= 1<<8 { + x >>= 8 + n += 8 + } + n += int(len8tab[x]) + return 32 - n +} + +var len8tab = [256]uint8{ + 0x00, 0x01, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, +} + +func hashBlock(raw []byte, ptr int) int { + // The first 4 steps collapse out into a 4 byte big-endian decode, + // with a larger right shift as we combined shift lefts together. + // + hash := ((uint32(raw[ptr]) & 0xff) << 24) | + ((uint32(raw[ptr+1]) & 0xff) << 16) | + ((uint32(raw[ptr+2]) & 0xff) << 8) | + (uint32(raw[ptr+3]) & 0xff) + hash ^= T[hash>>31] + + hash = ((hash << 8) | (uint32(raw[ptr+4]) & 0xff)) ^ T[hash>>23] + hash = ((hash << 8) | (uint32(raw[ptr+5]) & 0xff)) ^ T[hash>>23] + hash = ((hash << 8) | (uint32(raw[ptr+6]) & 0xff)) ^ T[hash>>23] + hash = ((hash << 8) | (uint32(raw[ptr+7]) & 0xff)) ^ T[hash>>23] + + hash = ((hash << 8) | (uint32(raw[ptr+8]) & 0xff)) ^ T[hash>>23] + hash = ((hash << 8) | (uint32(raw[ptr+9]) & 0xff)) ^ T[hash>>23] + hash = ((hash << 8) | (uint32(raw[ptr+10]) & 0xff)) ^ T[hash>>23] + hash = ((hash << 8) | (uint32(raw[ptr+11]) & 0xff)) ^ T[hash>>23] + + hash = ((hash << 8) | (uint32(raw[ptr+12]) & 0xff)) ^ T[hash>>23] + hash = ((hash << 8) | (uint32(raw[ptr+13]) & 0xff)) ^ T[hash>>23] + hash = ((hash << 8) | (uint32(raw[ptr+14]) & 0xff)) ^ T[hash>>23] + hash = ((hash << 8) | (uint32(raw[ptr+15]) & 0xff)) ^ T[hash>>23] + + return int(hash) +} + +var T = []uint32{0x00000000, 0xd4c6b32d, 0x7d4bd577, + 0xa98d665a, 0x2e5119c3, 0xfa97aaee, 0x531accb4, 0x87dc7f99, + 0x5ca23386, 0x886480ab, 0x21e9e6f1, 0xf52f55dc, 0x72f32a45, + 0xa6359968, 0x0fb8ff32, 0xdb7e4c1f, 0x6d82d421, 0xb944670c, + 0x10c90156, 0xc40fb27b, 0x43d3cde2, 0x97157ecf, 0x3e981895, + 0xea5eabb8, 0x3120e7a7, 0xe5e6548a, 0x4c6b32d0, 0x98ad81fd, + 0x1f71fe64, 0xcbb74d49, 0x623a2b13, 0xb6fc983e, 0x0fc31b6f, + 0xdb05a842, 0x7288ce18, 0xa64e7d35, 0x219202ac, 0xf554b181, + 0x5cd9d7db, 0x881f64f6, 0x536128e9, 0x87a79bc4, 0x2e2afd9e, + 0xfaec4eb3, 0x7d30312a, 0xa9f68207, 0x007be45d, 0xd4bd5770, + 0x6241cf4e, 0xb6877c63, 0x1f0a1a39, 0xcbcca914, 0x4c10d68d, + 0x98d665a0, 0x315b03fa, 0xe59db0d7, 0x3ee3fcc8, 0xea254fe5, + 0x43a829bf, 0x976e9a92, 0x10b2e50b, 0xc4745626, 0x6df9307c, + 0xb93f8351, 0x1f8636de, 0xcb4085f3, 0x62cde3a9, 0xb60b5084, + 0x31d72f1d, 0xe5119c30, 0x4c9cfa6a, 0x985a4947, 0x43240558, + 0x97e2b675, 0x3e6fd02f, 0xeaa96302, 0x6d751c9b, 0xb9b3afb6, + 0x103ec9ec, 0xc4f87ac1, 0x7204e2ff, 0xa6c251d2, 0x0f4f3788, + 0xdb8984a5, 0x5c55fb3c, 0x88934811, 0x211e2e4b, 0xf5d89d66, + 0x2ea6d179, 0xfa606254, 0x53ed040e, 0x872bb723, 0x00f7c8ba, + 0xd4317b97, 0x7dbc1dcd, 0xa97aaee0, 0x10452db1, 0xc4839e9c, + 0x6d0ef8c6, 0xb9c84beb, 0x3e143472, 0xead2875f, 0x435fe105, + 0x97995228, 0x4ce71e37, 0x9821ad1a, 0x31accb40, 0xe56a786d, + 0x62b607f4, 0xb670b4d9, 0x1ffdd283, 0xcb3b61ae, 0x7dc7f990, + 0xa9014abd, 0x008c2ce7, 0xd44a9fca, 0x5396e053, 0x8750537e, + 0x2edd3524, 0xfa1b8609, 0x2165ca16, 0xf5a3793b, 0x5c2e1f61, + 0x88e8ac4c, 0x0f34d3d5, 0xdbf260f8, 0x727f06a2, 0xa6b9b58f, + 0x3f0c6dbc, 0xebcade91, 0x4247b8cb, 0x96810be6, 0x115d747f, + 0xc59bc752, 0x6c16a108, 0xb8d01225, 0x63ae5e3a, 0xb768ed17, + 0x1ee58b4d, 0xca233860, 0x4dff47f9, 0x9939f4d4, 0x30b4928e, + 0xe47221a3, 0x528eb99d, 0x86480ab0, 0x2fc56cea, 0xfb03dfc7, + 0x7cdfa05e, 0xa8191373, 0x01947529, 0xd552c604, 0x0e2c8a1b, + 0xdaea3936, 0x73675f6c, 0xa7a1ec41, 0x207d93d8, 0xf4bb20f5, + 0x5d3646af, 0x89f0f582, 0x30cf76d3, 0xe409c5fe, 0x4d84a3a4, + 0x99421089, 0x1e9e6f10, 0xca58dc3d, 0x63d5ba67, 0xb713094a, + 0x6c6d4555, 0xb8abf678, 0x11269022, 0xc5e0230f, 0x423c5c96, + 0x96faefbb, 0x3f7789e1, 0xebb13acc, 0x5d4da2f2, 0x898b11df, + 0x20067785, 0xf4c0c4a8, 0x731cbb31, 0xa7da081c, 0x0e576e46, + 0xda91dd6b, 0x01ef9174, 0xd5292259, 0x7ca44403, 0xa862f72e, + 0x2fbe88b7, 0xfb783b9a, 0x52f55dc0, 0x8633eeed, 0x208a5b62, + 0xf44ce84f, 0x5dc18e15, 0x89073d38, 0x0edb42a1, 0xda1df18c, + 0x739097d6, 0xa75624fb, 0x7c2868e4, 0xa8eedbc9, 0x0163bd93, + 0xd5a50ebe, 0x52797127, 0x86bfc20a, 0x2f32a450, 0xfbf4177d, + 0x4d088f43, 0x99ce3c6e, 0x30435a34, 0xe485e919, 0x63599680, + 0xb79f25ad, 0x1e1243f7, 0xcad4f0da, 0x11aabcc5, 0xc56c0fe8, + 0x6ce169b2, 0xb827da9f, 0x3ffba506, 0xeb3d162b, 0x42b07071, + 0x9676c35c, 0x2f49400d, 0xfb8ff320, 0x5202957a, 0x86c42657, + 0x011859ce, 0xd5deeae3, 0x7c538cb9, 0xa8953f94, 0x73eb738b, + 0xa72dc0a6, 0x0ea0a6fc, 0xda6615d1, 0x5dba6a48, 0x897cd965, + 0x20f1bf3f, 0xf4370c12, 0x42cb942c, 0x960d2701, 0x3f80415b, + 0xeb46f276, 0x6c9a8def, 0xb85c3ec2, 0x11d15898, 0xc517ebb5, + 0x1e69a7aa, 0xcaaf1487, 0x632272dd, 0xb7e4c1f0, 0x3038be69, + 0xe4fe0d44, 0x4d736b1e, 0x99b5d833, +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/delta_selector.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/delta_selector.go new file mode 100644 index 0000000000..6710085538 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/delta_selector.go @@ -0,0 +1,369 @@ +package packfile + +import ( + "sort" + "sync" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/storer" +) + +const ( + // deltas based on deltas, how many steps we can do. + // 50 is the default value used in JGit + maxDepth = int64(50) +) + +// applyDelta is the set of object types that we should apply deltas +var applyDelta = map[plumbing.ObjectType]bool{ + plumbing.BlobObject: true, + plumbing.TreeObject: true, +} + +type deltaSelector struct { + storer storer.EncodedObjectStorer +} + +func newDeltaSelector(s storer.EncodedObjectStorer) *deltaSelector { + return &deltaSelector{s} +} + +// ObjectsToPack creates a list of ObjectToPack from the hashes +// provided, creating deltas if it's suitable, using an specific +// internal logic. `packWindow` specifies the size of the sliding +// window used to compare objects for delta compression; 0 turns off +// delta compression entirely. +func (dw *deltaSelector) ObjectsToPack( + hashes []plumbing.Hash, + packWindow uint, +) ([]*ObjectToPack, error) { + otp, err := dw.objectsToPack(hashes, packWindow) + if err != nil { + return nil, err + } + + if packWindow == 0 { + return otp, nil + } + + dw.sort(otp) + + var objectGroups [][]*ObjectToPack + var prev *ObjectToPack + i := -1 + for _, obj := range otp { + if prev == nil || prev.Type() != obj.Type() { + objectGroups = append(objectGroups, []*ObjectToPack{obj}) + i++ + prev = obj + } else { + objectGroups[i] = append(objectGroups[i], obj) + } + } + + var wg sync.WaitGroup + var once sync.Once + for _, objs := range objectGroups { + objs := objs + wg.Add(1) + go func() { + if walkErr := dw.walk(objs, packWindow); walkErr != nil { + once.Do(func() { + err = walkErr + }) + } + wg.Done() + }() + } + wg.Wait() + + if err != nil { + return nil, err + } + + return otp, nil +} + +func (dw *deltaSelector) objectsToPack( + hashes []plumbing.Hash, + packWindow uint, +) ([]*ObjectToPack, error) { + var objectsToPack []*ObjectToPack + for _, h := range hashes { + var o plumbing.EncodedObject + var err error + if packWindow == 0 { + o, err = dw.encodedObject(h) + } else { + o, err = dw.encodedDeltaObject(h) + } + if err != nil { + return nil, err + } + + otp := newObjectToPack(o) + if _, ok := o.(plumbing.DeltaObject); ok { + otp.CleanOriginal() + } + + objectsToPack = append(objectsToPack, otp) + } + + if packWindow == 0 { + return objectsToPack, nil + } + + if err := dw.fixAndBreakChains(objectsToPack); err != nil { + return nil, err + } + + return objectsToPack, nil +} + +func (dw *deltaSelector) encodedDeltaObject(h plumbing.Hash) (plumbing.EncodedObject, error) { + edos, ok := dw.storer.(storer.DeltaObjectStorer) + if !ok { + return dw.encodedObject(h) + } + + return edos.DeltaObject(plumbing.AnyObject, h) +} + +func (dw *deltaSelector) encodedObject(h plumbing.Hash) (plumbing.EncodedObject, error) { + return dw.storer.EncodedObject(plumbing.AnyObject, h) +} + +func (dw *deltaSelector) fixAndBreakChains(objectsToPack []*ObjectToPack) error { + m := make(map[plumbing.Hash]*ObjectToPack, len(objectsToPack)) + for _, otp := range objectsToPack { + m[otp.Hash()] = otp + } + + for _, otp := range objectsToPack { + if err := dw.fixAndBreakChainsOne(m, otp); err != nil { + return err + } + } + + return nil +} + +func (dw *deltaSelector) fixAndBreakChainsOne(objectsToPack map[plumbing.Hash]*ObjectToPack, otp *ObjectToPack) error { + if !otp.Object.Type().IsDelta() { + return nil + } + + // Initial ObjectToPack instances might have a delta assigned to Object + // but no actual base initially. Once Base is assigned to a delta, it means + // we already fixed it. + if otp.Base != nil { + return nil + } + + do, ok := otp.Object.(plumbing.DeltaObject) + if !ok { + // if this is not a DeltaObject, then we cannot retrieve its base, + // so we have to break the delta chain here. + return dw.undeltify(otp) + } + + base, ok := objectsToPack[do.BaseHash()] + if !ok { + // The base of the delta is not in our list of objects to pack, so + // we break the chain. + return dw.undeltify(otp) + } + + if err := dw.fixAndBreakChainsOne(objectsToPack, base); err != nil { + return err + } + + otp.SetDelta(base, otp.Object) + return nil +} + +func (dw *deltaSelector) restoreOriginal(otp *ObjectToPack) error { + if otp.Original != nil { + return nil + } + + if !otp.Object.Type().IsDelta() { + return nil + } + + obj, err := dw.encodedObject(otp.Hash()) + if err != nil { + return err + } + + otp.SetOriginal(obj) + + return nil +} + +// undeltify undeltifies an *ObjectToPack by retrieving the original object from +// the storer and resetting it. +func (dw *deltaSelector) undeltify(otp *ObjectToPack) error { + if err := dw.restoreOriginal(otp); err != nil { + return err + } + + otp.Object = otp.Original + otp.Depth = 0 + return nil +} + +func (dw *deltaSelector) sort(objectsToPack []*ObjectToPack) { + sort.Sort(byTypeAndSize(objectsToPack)) +} + +func (dw *deltaSelector) walk( + objectsToPack []*ObjectToPack, + packWindow uint, +) error { + indexMap := make(map[plumbing.Hash]*deltaIndex) + for i := 0; i < len(objectsToPack); i++ { + // Clean up the index map and reconstructed delta objects for anything + // outside our pack window, to save memory. + if i > int(packWindow) { + obj := objectsToPack[i-int(packWindow)] + + delete(indexMap, obj.Hash()) + + if obj.IsDelta() { + obj.SaveOriginalMetadata() + obj.CleanOriginal() + } + } + + target := objectsToPack[i] + + // If we already have a delta, we don't try to find a new one for this + // object. This happens when a delta is set to be reused from an existing + // packfile. + if target.IsDelta() { + continue + } + + // We only want to create deltas from specific types. + if !applyDelta[target.Type()] { + continue + } + + for j := i - 1; j >= 0 && i-j < int(packWindow); j-- { + base := objectsToPack[j] + // Objects must use only the same type as their delta base. + // Since objectsToPack is sorted by type and size, once we find + // a different type, we know we won't find more of them. + if base.Type() != target.Type() { + break + } + + if err := dw.tryToDeltify(indexMap, base, target); err != nil { + return err + } + } + } + + return nil +} + +func (dw *deltaSelector) tryToDeltify(indexMap map[plumbing.Hash]*deltaIndex, base, target *ObjectToPack) error { + // Original object might not be present if we're reusing a delta, so we + // ensure it is restored. + if err := dw.restoreOriginal(target); err != nil { + return err + } + + if err := dw.restoreOriginal(base); err != nil { + return err + } + + // If the sizes are radically different, this is a bad pairing. + if target.Size() < base.Size()>>4 { + return nil + } + + msz := dw.deltaSizeLimit( + target.Object.Size(), + base.Depth, + target.Depth, + target.IsDelta(), + ) + + // Nearly impossible to fit useful delta. + if msz <= 8 { + return nil + } + + // If we have to insert a lot to make this work, find another. + if base.Size()-target.Size() > msz { + return nil + } + + if _, ok := indexMap[base.Hash()]; !ok { + indexMap[base.Hash()] = new(deltaIndex) + } + + // Now we can generate the delta using originals + delta, err := getDelta(indexMap[base.Hash()], base.Original, target.Original) + if err != nil { + return err + } + + // if delta better than target + if delta.Size() < msz { + target.SetDelta(base, delta) + } + + return nil +} + +func (dw *deltaSelector) deltaSizeLimit(targetSize int64, baseDepth int, + targetDepth int, targetDelta bool) int64 { + if !targetDelta { + // Any delta should be no more than 50% of the original size + // (for text files deflate of whole form should shrink 50%). + n := targetSize >> 1 + + // Evenly distribute delta size limits over allowed depth. + // If src is non-delta (depth = 0), delta <= 50% of original. + // If src is almost at limit (9/10), delta <= 10% of original. + return n * (maxDepth - int64(baseDepth)) / maxDepth + } + + // With a delta base chosen any new delta must be "better". + // Retain the distribution described above. + d := int64(targetDepth) + n := targetSize + + // If target depth is bigger than maxDepth, this delta is not suitable to be used. + if d >= maxDepth { + return 0 + } + + // If src is whole (depth=0) and base is near limit (depth=9/10) + // any delta using src can be 10x larger and still be better. + // + // If src is near limit (depth=9/10) and base is whole (depth=0) + // a new delta dependent on src must be 1/10th the size. + return n * (maxDepth - int64(baseDepth)) / (maxDepth - d) +} + +type byTypeAndSize []*ObjectToPack + +func (a byTypeAndSize) Len() int { return len(a) } + +func (a byTypeAndSize) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +func (a byTypeAndSize) Less(i, j int) bool { + if a[i].Type() < a[j].Type() { + return false + } + + if a[i].Type() > a[j].Type() { + return true + } + + return a[i].Size() > a[j].Size() +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/diff_delta.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/diff_delta.go new file mode 100644 index 0000000000..43f87a0b1c --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/diff_delta.go @@ -0,0 +1,200 @@ +package packfile + +import ( + "bytes" + + "gopkg.in/src-d/go-git.v4/plumbing" +) + +// See https://github.com/jelmer/dulwich/blob/master/dulwich/pack.py and +// https://github.com/tarruda/node-git-core/blob/master/src/js/delta.js +// for more info + +const ( + // Standard chunk size used to generate fingerprints + s = 16 + + // https://github.com/git/git/blob/f7466e94375b3be27f229c78873f0acf8301c0a5/diff-delta.c#L428 + // Max size of a copy operation (64KB) + maxCopySize = 64 * 1024 +) + +// GetDelta returns an EncodedObject of type OFSDeltaObject. Base and Target object, +// will be loaded into memory to be able to create the delta object. +// To generate target again, you will need the obtained object and "base" one. +// Error will be returned if base or target object cannot be read. +func GetDelta(base, target plumbing.EncodedObject) (plumbing.EncodedObject, error) { + return getDelta(new(deltaIndex), base, target) +} + +func getDelta(index *deltaIndex, base, target plumbing.EncodedObject) (plumbing.EncodedObject, error) { + br, err := base.Reader() + if err != nil { + return nil, err + } + defer br.Close() + tr, err := target.Reader() + if err != nil { + return nil, err + } + defer tr.Close() + + bb := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(bb) + bb.Reset() + + _, err = bb.ReadFrom(br) + if err != nil { + return nil, err + } + + tb := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(tb) + tb.Reset() + + _, err = tb.ReadFrom(tr) + if err != nil { + return nil, err + } + + db := diffDelta(index, bb.Bytes(), tb.Bytes()) + delta := &plumbing.MemoryObject{} + _, err = delta.Write(db) + if err != nil { + return nil, err + } + + delta.SetSize(int64(len(db))) + delta.SetType(plumbing.OFSDeltaObject) + + return delta, nil +} + +// DiffDelta returns the delta that transforms src into tgt. +func DiffDelta(src, tgt []byte) []byte { + return diffDelta(new(deltaIndex), src, tgt) +} + +func diffDelta(index *deltaIndex, src []byte, tgt []byte) []byte { + buf := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(buf) + buf.Reset() + buf.Write(deltaEncodeSize(len(src))) + buf.Write(deltaEncodeSize(len(tgt))) + + if len(index.entries) == 0 { + index.init(src) + } + + ibuf := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(ibuf) + ibuf.Reset() + for i := 0; i < len(tgt); i++ { + offset, l := index.findMatch(src, tgt, i) + + if l == 0 { + // couldn't find a match, just write the current byte and continue + ibuf.WriteByte(tgt[i]) + } else if l < 0 { + // src is less than blksz, copy the rest of the target to avoid + // calls to findMatch + for ; i < len(tgt); i++ { + ibuf.WriteByte(tgt[i]) + } + } else if l < s { + // remaining target is less than blksz, copy what's left of it + // and avoid calls to findMatch + for j := i; j < i+l; j++ { + ibuf.WriteByte(tgt[j]) + } + i += l - 1 + } else { + encodeInsertOperation(ibuf, buf) + + rl := l + aOffset := offset + for rl > 0 { + if rl < maxCopySize { + buf.Write(encodeCopyOperation(aOffset, rl)) + break + } + + buf.Write(encodeCopyOperation(aOffset, maxCopySize)) + rl -= maxCopySize + aOffset += maxCopySize + } + + i += l - 1 + } + } + + encodeInsertOperation(ibuf, buf) + + // buf.Bytes() is only valid until the next modifying operation on the buffer. Copy it. + return append([]byte{}, buf.Bytes()...) +} + +func encodeInsertOperation(ibuf, buf *bytes.Buffer) { + if ibuf.Len() == 0 { + return + } + + b := ibuf.Bytes() + s := ibuf.Len() + o := 0 + for { + if s <= 127 { + break + } + buf.WriteByte(byte(127)) + buf.Write(b[o : o+127]) + s -= 127 + o += 127 + } + buf.WriteByte(byte(s)) + buf.Write(b[o : o+s]) + + ibuf.Reset() +} + +func deltaEncodeSize(size int) []byte { + var ret []byte + c := size & 0x7f + size >>= 7 + for { + if size == 0 { + break + } + + ret = append(ret, byte(c|0x80)) + c = size & 0x7f + size >>= 7 + } + ret = append(ret, byte(c)) + + return ret +} + +func encodeCopyOperation(offset, length int) []byte { + code := 0x80 + var opcodes []byte + + var i uint + for i = 0; i < 4; i++ { + f := 0xff << (i * 8) + if offset&f != 0 { + opcodes = append(opcodes, byte(offset&f>>(i*8))) + code |= 0x01 << i + } + } + + for i = 0; i < 3; i++ { + f := 0xff << (i * 8) + if length&f != 0 { + opcodes = append(opcodes, byte(length&f>>(i*8))) + code |= 0x10 << i + } + } + + return append([]byte{byte(code)}, opcodes...) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/doc.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/doc.go new file mode 100644 index 0000000000..2882a7f378 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/doc.go @@ -0,0 +1,39 @@ +// Package packfile implements encoding and decoding of packfile format. +// +// == pack-*.pack files have the following format: +// +// - A header appears at the beginning and consists of the following: +// +// 4-byte signature: +// The signature is: {'P', 'A', 'C', 'K'} +// +// 4-byte version number (network byte order): +// GIT currently accepts version number 2 or 3 but +// generates version 2 only. +// +// 4-byte number of objects contained in the pack (network byte order) +// +// Observation: we cannot have more than 4G versions ;-) and +// more than 4G objects in a pack. +// +// - The header is followed by number of object entries, each of +// which looks like this: +// +// (undeltified representation) +// n-byte type and length (3-bit type, (n-1)*7+4-bit length) +// compressed data +// +// (deltified representation) +// n-byte type and length (3-bit type, (n-1)*7+4-bit length) +// 20-byte base object name +// compressed delta data +// +// Observation: length of each object is encoded in a variable +// length format and is not constrained to 32-bit or anything. +// +// - The trailer records 20-byte SHA1 checksum of all of the above. +// +// +// Source: +// https://www.kernel.org/pub/software/scm/git/docs/v1.7.5/technical/pack-protocol.txt +package packfile diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/encoder.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/encoder.go new file mode 100644 index 0000000000..b07791875d --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/encoder.go @@ -0,0 +1,219 @@ +package packfile + +import ( + "compress/zlib" + "crypto/sha1" + "fmt" + "io" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/storer" + "gopkg.in/src-d/go-git.v4/utils/binary" +) + +// Encoder gets the data from the storage and write it into the writer in PACK +// format +type Encoder struct { + selector *deltaSelector + w *offsetWriter + zw *zlib.Writer + hasher plumbing.Hasher + + useRefDeltas bool +} + +// NewEncoder creates a new packfile encoder using a specific Writer and +// EncodedObjectStorer. By default deltas used to generate the packfile will be +// OFSDeltaObject. To use Reference deltas, set useRefDeltas to true. +func NewEncoder(w io.Writer, s storer.EncodedObjectStorer, useRefDeltas bool) *Encoder { + h := plumbing.Hasher{ + Hash: sha1.New(), + } + mw := io.MultiWriter(w, h) + ow := newOffsetWriter(mw) + zw := zlib.NewWriter(mw) + return &Encoder{ + selector: newDeltaSelector(s), + w: ow, + zw: zw, + hasher: h, + useRefDeltas: useRefDeltas, + } +} + +// Encode creates a packfile containing all the objects referenced in +// hashes and writes it to the writer in the Encoder. `packWindow` +// specifies the size of the sliding window used to compare objects +// for delta compression; 0 turns off delta compression entirely. +func (e *Encoder) Encode( + hashes []plumbing.Hash, + packWindow uint, +) (plumbing.Hash, error) { + objects, err := e.selector.ObjectsToPack(hashes, packWindow) + if err != nil { + return plumbing.ZeroHash, err + } + + return e.encode(objects) +} + +func (e *Encoder) encode(objects []*ObjectToPack) (plumbing.Hash, error) { + if err := e.head(len(objects)); err != nil { + return plumbing.ZeroHash, err + } + + for _, o := range objects { + if err := e.entry(o); err != nil { + return plumbing.ZeroHash, err + } + } + + return e.footer() +} + +func (e *Encoder) head(numEntries int) error { + return binary.Write( + e.w, + signature, + int32(VersionSupported), + int32(numEntries), + ) +} + +func (e *Encoder) entry(o *ObjectToPack) error { + if o.WantWrite() { + // A cycle exists in this delta chain. This should only occur if a + // selected object representation disappeared during writing + // (for example due to a concurrent repack) and a different base + // was chosen, forcing a cycle. Select something other than a + // delta, and write this object. + e.selector.restoreOriginal(o) + o.BackToOriginal() + } + + if o.IsWritten() { + return nil + } + + o.MarkWantWrite() + + if err := e.writeBaseIfDelta(o); err != nil { + return err + } + + // We need to check if we already write that object due a cyclic delta chain + if o.IsWritten() { + return nil + } + + o.Offset = e.w.Offset() + + if o.IsDelta() { + if err := e.writeDeltaHeader(o); err != nil { + return err + } + } else { + if err := e.entryHead(o.Type(), o.Size()); err != nil { + return err + } + } + + e.zw.Reset(e.w) + or, err := o.Object.Reader() + if err != nil { + return err + } + + _, err = io.Copy(e.zw, or) + if err != nil { + return err + } + + return e.zw.Close() +} + +func (e *Encoder) writeBaseIfDelta(o *ObjectToPack) error { + if o.IsDelta() && !o.Base.IsWritten() { + // We must write base first + return e.entry(o.Base) + } + + return nil +} + +func (e *Encoder) writeDeltaHeader(o *ObjectToPack) error { + // Write offset deltas by default + t := plumbing.OFSDeltaObject + if e.useRefDeltas { + t = plumbing.REFDeltaObject + } + + if err := e.entryHead(t, o.Object.Size()); err != nil { + return err + } + + if e.useRefDeltas { + return e.writeRefDeltaHeader(o.Base.Hash()) + } else { + return e.writeOfsDeltaHeader(o) + } +} + +func (e *Encoder) writeRefDeltaHeader(base plumbing.Hash) error { + return binary.Write(e.w, base) +} + +func (e *Encoder) writeOfsDeltaHeader(o *ObjectToPack) error { + // for OFS_DELTA, offset of the base is interpreted as negative offset + // relative to the type-byte of the header of the ofs-delta entry. + relativeOffset := o.Offset - o.Base.Offset + if relativeOffset <= 0 { + return fmt.Errorf("bad offset for OFS_DELTA entry: %d", relativeOffset) + } + + return binary.WriteVariableWidthInt(e.w, relativeOffset) +} + +func (e *Encoder) entryHead(typeNum plumbing.ObjectType, size int64) error { + t := int64(typeNum) + header := []byte{} + c := (t << firstLengthBits) | (size & maskFirstLength) + size >>= firstLengthBits + for { + if size == 0 { + break + } + header = append(header, byte(c|maskContinue)) + c = size & int64(maskLength) + size >>= lengthBits + } + + header = append(header, byte(c)) + _, err := e.w.Write(header) + + return err +} + +func (e *Encoder) footer() (plumbing.Hash, error) { + h := e.hasher.Sum() + return h, binary.Write(e.w, h) +} + +type offsetWriter struct { + w io.Writer + offset int64 +} + +func newOffsetWriter(w io.Writer) *offsetWriter { + return &offsetWriter{w: w} +} + +func (ow *offsetWriter) Write(p []byte) (n int, err error) { + n, err = ow.w.Write(p) + ow.offset += int64(n) + return n, err +} + +func (ow *offsetWriter) Offset() int64 { + return ow.offset +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/error.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/error.go new file mode 100644 index 0000000000..c0b9163313 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/error.go @@ -0,0 +1,30 @@ +package packfile + +import "fmt" + +// Error specifies errors returned during packfile parsing. +type Error struct { + reason, details string +} + +// NewError returns a new error. +func NewError(reason string) *Error { + return &Error{reason: reason} +} + +// Error returns a text representation of the error. +func (e *Error) Error() string { + if e.details == "" { + return e.reason + } + + return fmt.Sprintf("%s: %s", e.reason, e.details) +} + +// AddDetails adds details to an error, with additional text. +func (e *Error) AddDetails(format string, args ...interface{}) *Error { + return &Error{ + reason: e.reason, + details: fmt.Sprintf(format, args...), + } +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/fsobject.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/fsobject.go new file mode 100644 index 0000000000..a268bce7ed --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/fsobject.go @@ -0,0 +1,116 @@ +package packfile + +import ( + "io" + + billy "gopkg.in/src-d/go-billy.v4" + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/cache" + "gopkg.in/src-d/go-git.v4/plumbing/format/idxfile" +) + +// FSObject is an object from the packfile on the filesystem. +type FSObject struct { + hash plumbing.Hash + h *ObjectHeader + offset int64 + size int64 + typ plumbing.ObjectType + index idxfile.Index + fs billy.Filesystem + path string + cache cache.Object +} + +// NewFSObject creates a new filesystem object. +func NewFSObject( + hash plumbing.Hash, + finalType plumbing.ObjectType, + offset int64, + contentSize int64, + index idxfile.Index, + fs billy.Filesystem, + path string, + cache cache.Object, +) *FSObject { + return &FSObject{ + hash: hash, + offset: offset, + size: contentSize, + typ: finalType, + index: index, + fs: fs, + path: path, + cache: cache, + } +} + +// Reader implements the plumbing.EncodedObject interface. +func (o *FSObject) Reader() (io.ReadCloser, error) { + obj, ok := o.cache.Get(o.hash) + if ok && obj != o { + reader, err := obj.Reader() + if err != nil { + return nil, err + } + + return reader, nil + } + + f, err := o.fs.Open(o.path) + if err != nil { + return nil, err + } + + p := NewPackfileWithCache(o.index, nil, f, o.cache) + r, err := p.getObjectContent(o.offset) + if err != nil { + _ = f.Close() + return nil, err + } + + if err := f.Close(); err != nil { + return nil, err + } + + return r, nil +} + +// SetSize implements the plumbing.EncodedObject interface. This method +// is a noop. +func (o *FSObject) SetSize(int64) {} + +// SetType implements the plumbing.EncodedObject interface. This method is +// a noop. +func (o *FSObject) SetType(plumbing.ObjectType) {} + +// Hash implements the plumbing.EncodedObject interface. +func (o *FSObject) Hash() plumbing.Hash { return o.hash } + +// Size implements the plumbing.EncodedObject interface. +func (o *FSObject) Size() int64 { return o.size } + +// Type implements the plumbing.EncodedObject interface. +func (o *FSObject) Type() plumbing.ObjectType { + return o.typ +} + +// Writer implements the plumbing.EncodedObject interface. This method always +// returns a nil writer. +func (o *FSObject) Writer() (io.WriteCloser, error) { + return nil, nil +} + +type objectReader struct { + io.ReadCloser + f billy.File +} + +func (r *objectReader) Close() error { + if err := r.ReadCloser.Close(); err != nil { + _ = r.f.Close() + return err + } + + return r.f.Close() +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/object_pack.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/object_pack.go new file mode 100644 index 0000000000..dfea5715f0 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/object_pack.go @@ -0,0 +1,164 @@ +package packfile + +import ( + "gopkg.in/src-d/go-git.v4/plumbing" +) + +// ObjectToPack is a representation of an object that is going to be into a +// pack file. +type ObjectToPack struct { + // The main object to pack, it could be any object, including deltas + Object plumbing.EncodedObject + // Base is the object that a delta is based on (it could be also another delta). + // If the main object is not a delta, Base will be null + Base *ObjectToPack + // Original is the object that we can generate applying the delta to + // Base, or the same object as Object in the case of a non-delta + // object. + Original plumbing.EncodedObject + // Depth is the amount of deltas needed to resolve to obtain Original + // (delta based on delta based on ...) + Depth int + + // offset in pack when object has been already written, or 0 if it + // has not been written yet + Offset int64 + + // Information from the original object + resolvedOriginal bool + originalType plumbing.ObjectType + originalSize int64 + originalHash plumbing.Hash +} + +// newObjectToPack creates a correct ObjectToPack based on a non-delta object +func newObjectToPack(o plumbing.EncodedObject) *ObjectToPack { + return &ObjectToPack{ + Object: o, + Original: o, + } +} + +// newDeltaObjectToPack creates a correct ObjectToPack for a delta object, based on +// his base (could be another delta), the delta target (in this case called original), +// and the delta Object itself +func newDeltaObjectToPack(base *ObjectToPack, original, delta plumbing.EncodedObject) *ObjectToPack { + return &ObjectToPack{ + Object: delta, + Base: base, + Original: original, + Depth: base.Depth + 1, + } +} + +// BackToOriginal converts that ObjectToPack to a non-deltified object if it was one +func (o *ObjectToPack) BackToOriginal() { + if o.IsDelta() && o.Original != nil { + o.Object = o.Original + o.Base = nil + o.Depth = 0 + } +} + +// IsWritten returns if that ObjectToPack was +// already written into the packfile or not +func (o *ObjectToPack) IsWritten() bool { + return o.Offset > 1 +} + +// MarkWantWrite marks this ObjectToPack as WantWrite +// to avoid delta chain loops +func (o *ObjectToPack) MarkWantWrite() { + o.Offset = 1 +} + +// WantWrite checks if this ObjectToPack was marked as WantWrite before +func (o *ObjectToPack) WantWrite() bool { + return o.Offset == 1 +} + +// SetOriginal sets both Original and saves size, type and hash. If object +// is nil Original is set but previous resolved values are kept +func (o *ObjectToPack) SetOriginal(obj plumbing.EncodedObject) { + o.Original = obj + o.SaveOriginalMetadata() +} + +// SaveOriginalMetadata saves size, type and hash of Original object +func (o *ObjectToPack) SaveOriginalMetadata() { + if o.Original != nil { + o.originalSize = o.Original.Size() + o.originalType = o.Original.Type() + o.originalHash = o.Original.Hash() + o.resolvedOriginal = true + } +} + +// CleanOriginal sets Original to nil +func (o *ObjectToPack) CleanOriginal() { + o.Original = nil +} + +func (o *ObjectToPack) Type() plumbing.ObjectType { + if o.Original != nil { + return o.Original.Type() + } + + if o.resolvedOriginal { + return o.originalType + } + + if o.Base != nil { + return o.Base.Type() + } + + if o.Object != nil { + return o.Object.Type() + } + + panic("cannot get type") +} + +func (o *ObjectToPack) Hash() plumbing.Hash { + if o.Original != nil { + return o.Original.Hash() + } + + if o.resolvedOriginal { + return o.originalHash + } + + do, ok := o.Object.(plumbing.DeltaObject) + if ok { + return do.ActualHash() + } + + panic("cannot get hash") +} + +func (o *ObjectToPack) Size() int64 { + if o.Original != nil { + return o.Original.Size() + } + + if o.resolvedOriginal { + return o.originalSize + } + + do, ok := o.Object.(plumbing.DeltaObject) + if ok { + return do.ActualSize() + } + + panic("cannot get ObjectToPack size") +} + +func (o *ObjectToPack) IsDelta() bool { + return o.Base != nil +} + +func (o *ObjectToPack) SetDelta(base *ObjectToPack, delta plumbing.EncodedObject) { + o.Object = delta + o.Base = base + o.Depth = base.Depth + 1 +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/packfile.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/packfile.go new file mode 100644 index 0000000000..21a15de0cc --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/packfile.go @@ -0,0 +1,562 @@ +package packfile + +import ( + "bytes" + "io" + "os" + + billy "gopkg.in/src-d/go-billy.v4" + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/cache" + "gopkg.in/src-d/go-git.v4/plumbing/format/idxfile" + "gopkg.in/src-d/go-git.v4/plumbing/storer" +) + +var ( + // ErrInvalidObject is returned by Decode when an invalid object is + // found in the packfile. + ErrInvalidObject = NewError("invalid git object") + // ErrZLib is returned by Decode when there was an error unzipping + // the packfile contents. + ErrZLib = NewError("zlib reading error") +) + +// When reading small objects from packfile it is beneficial to do so at +// once to exploit the buffered I/O. In many cases the objects are so small +// that they were already loaded to memory when the object header was +// loaded from the packfile. Wrapping in FSObject would cause this buffered +// data to be thrown away and then re-read later, with the additional +// seeking causing reloads from disk. Objects smaller than this threshold +// are now always read into memory and stored in cache instead of being +// wrapped in FSObject. +const smallObjectThreshold = 16 * 1024 + +// Packfile allows retrieving information from inside a packfile. +type Packfile struct { + idxfile.Index + fs billy.Filesystem + file billy.File + s *Scanner + deltaBaseCache cache.Object + offsetToType map[int64]plumbing.ObjectType +} + +// NewPackfileWithCache creates a new Packfile with the given object cache. +// If the filesystem is provided, the packfile will return FSObjects, otherwise +// it will return MemoryObjects. +func NewPackfileWithCache( + index idxfile.Index, + fs billy.Filesystem, + file billy.File, + cache cache.Object, +) *Packfile { + s := NewScanner(file) + return &Packfile{ + index, + fs, + file, + s, + cache, + make(map[int64]plumbing.ObjectType), + } +} + +// NewPackfile returns a packfile representation for the given packfile file +// and packfile idx. +// If the filesystem is provided, the packfile will return FSObjects, otherwise +// it will return MemoryObjects. +func NewPackfile(index idxfile.Index, fs billy.Filesystem, file billy.File) *Packfile { + return NewPackfileWithCache(index, fs, file, cache.NewObjectLRUDefault()) +} + +// Get retrieves the encoded object in the packfile with the given hash. +func (p *Packfile) Get(h plumbing.Hash) (plumbing.EncodedObject, error) { + offset, err := p.FindOffset(h) + if err != nil { + return nil, err + } + + return p.objectAtOffset(offset, h) +} + +// GetByOffset retrieves the encoded object from the packfile at the given +// offset. +func (p *Packfile) GetByOffset(o int64) (plumbing.EncodedObject, error) { + hash, err := p.FindHash(o) + if err != nil { + return nil, err + } + + return p.objectAtOffset(o, hash) +} + +// GetSizeByOffset retrieves the size of the encoded object from the +// packfile with the given offset. +func (p *Packfile) GetSizeByOffset(o int64) (size int64, err error) { + if _, err := p.s.SeekFromStart(o); err != nil { + if err == io.EOF || isInvalid(err) { + return 0, plumbing.ErrObjectNotFound + } + + return 0, err + } + + h, err := p.nextObjectHeader() + if err != nil { + return 0, err + } + return p.getObjectSize(h) +} + +func (p *Packfile) objectHeaderAtOffset(offset int64) (*ObjectHeader, error) { + h, err := p.s.SeekObjectHeader(offset) + p.s.pendingObject = nil + return h, err +} + +func (p *Packfile) nextObjectHeader() (*ObjectHeader, error) { + h, err := p.s.NextObjectHeader() + p.s.pendingObject = nil + return h, err +} + +func (p *Packfile) getDeltaObjectSize(buf *bytes.Buffer) int64 { + delta := buf.Bytes() + _, delta = decodeLEB128(delta) // skip src size + sz, _ := decodeLEB128(delta) + return int64(sz) +} + +func (p *Packfile) getObjectSize(h *ObjectHeader) (int64, error) { + switch h.Type { + case plumbing.CommitObject, plumbing.TreeObject, plumbing.BlobObject, plumbing.TagObject: + return h.Length, nil + case plumbing.REFDeltaObject, plumbing.OFSDeltaObject: + buf := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(buf) + buf.Reset() + + if _, _, err := p.s.NextObject(buf); err != nil { + return 0, err + } + + return p.getDeltaObjectSize(buf), nil + default: + return 0, ErrInvalidObject.AddDetails("type %q", h.Type) + } +} + +func (p *Packfile) getObjectType(h *ObjectHeader) (typ plumbing.ObjectType, err error) { + switch h.Type { + case plumbing.CommitObject, plumbing.TreeObject, plumbing.BlobObject, plumbing.TagObject: + return h.Type, nil + case plumbing.REFDeltaObject, plumbing.OFSDeltaObject: + var offset int64 + if h.Type == plumbing.REFDeltaObject { + offset, err = p.FindOffset(h.Reference) + if err != nil { + return + } + } else { + offset = h.OffsetReference + } + + if baseType, ok := p.offsetToType[offset]; ok { + typ = baseType + } else { + h, err = p.objectHeaderAtOffset(offset) + if err != nil { + return + } + + typ, err = p.getObjectType(h) + if err != nil { + return + } + } + default: + err = ErrInvalidObject.AddDetails("type %q", h.Type) + } + + p.offsetToType[h.Offset] = typ + + return +} + +func (p *Packfile) objectAtOffset(offset int64, hash plumbing.Hash) (plumbing.EncodedObject, error) { + if obj, ok := p.cacheGet(hash); ok { + return obj, nil + } + + h, err := p.objectHeaderAtOffset(offset) + if err != nil { + if err == io.EOF || isInvalid(err) { + return nil, plumbing.ErrObjectNotFound + } + return nil, err + } + + return p.getNextObject(h, hash) +} + +func (p *Packfile) getNextObject(h *ObjectHeader, hash plumbing.Hash) (plumbing.EncodedObject, error) { + var err error + + // If we have no filesystem, we will return a MemoryObject instead + // of an FSObject. + if p.fs == nil { + return p.getNextMemoryObject(h) + } + + // If the object is small enough then read it completely into memory now since + // it is already read from disk into buffer anyway. For delta objects we want + // to perform the optimization too, but we have to be careful about applying + // small deltas on big objects. + var size int64 + if h.Length <= smallObjectThreshold { + if h.Type != plumbing.OFSDeltaObject && h.Type != plumbing.REFDeltaObject { + return p.getNextMemoryObject(h) + } + + // For delta objects we read the delta data and apply the small object + // optimization only if the expanded version of the object still meets + // the small object threshold condition. + buf := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(buf) + buf.Reset() + if _, _, err := p.s.NextObject(buf); err != nil { + return nil, err + } + + size = p.getDeltaObjectSize(buf) + if size <= smallObjectThreshold { + var obj = new(plumbing.MemoryObject) + obj.SetSize(size) + if h.Type == plumbing.REFDeltaObject { + err = p.fillREFDeltaObjectContentWithBuffer(obj, h.Reference, buf) + } else { + err = p.fillOFSDeltaObjectContentWithBuffer(obj, h.OffsetReference, buf) + } + return obj, err + } + } else { + size, err = p.getObjectSize(h) + if err != nil { + return nil, err + } + } + + typ, err := p.getObjectType(h) + if err != nil { + return nil, err + } + + p.offsetToType[h.Offset] = typ + + return NewFSObject( + hash, + typ, + h.Offset, + size, + p.Index, + p.fs, + p.file.Name(), + p.deltaBaseCache, + ), nil +} + +func (p *Packfile) getObjectContent(offset int64) (io.ReadCloser, error) { + h, err := p.objectHeaderAtOffset(offset) + if err != nil { + return nil, err + } + + // getObjectContent is called from FSObject, so we have to explicitly + // get memory object here to avoid recursive cycle + obj, err := p.getNextMemoryObject(h) + if err != nil { + return nil, err + } + + return obj.Reader() +} + +func (p *Packfile) getNextMemoryObject(h *ObjectHeader) (plumbing.EncodedObject, error) { + var obj = new(plumbing.MemoryObject) + obj.SetSize(h.Length) + obj.SetType(h.Type) + + var err error + switch h.Type { + case plumbing.CommitObject, plumbing.TreeObject, plumbing.BlobObject, plumbing.TagObject: + err = p.fillRegularObjectContent(obj) + case plumbing.REFDeltaObject: + err = p.fillREFDeltaObjectContent(obj, h.Reference) + case plumbing.OFSDeltaObject: + err = p.fillOFSDeltaObjectContent(obj, h.OffsetReference) + default: + err = ErrInvalidObject.AddDetails("type %q", h.Type) + } + + if err != nil { + return nil, err + } + + p.offsetToType[h.Offset] = obj.Type() + + return obj, nil +} + +func (p *Packfile) fillRegularObjectContent(obj plumbing.EncodedObject) error { + w, err := obj.Writer() + if err != nil { + return err + } + + _, _, err = p.s.NextObject(w) + p.cachePut(obj) + + return err +} + +func (p *Packfile) fillREFDeltaObjectContent(obj plumbing.EncodedObject, ref plumbing.Hash) error { + buf := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(buf) + buf.Reset() + _, _, err := p.s.NextObject(buf) + if err != nil { + return err + } + + return p.fillREFDeltaObjectContentWithBuffer(obj, ref, buf) +} + +func (p *Packfile) fillREFDeltaObjectContentWithBuffer(obj plumbing.EncodedObject, ref plumbing.Hash, buf *bytes.Buffer) error { + var err error + + base, ok := p.cacheGet(ref) + if !ok { + base, err = p.Get(ref) + if err != nil { + return err + } + } + + obj.SetType(base.Type()) + err = ApplyDelta(obj, base, buf.Bytes()) + p.cachePut(obj) + + return err +} + +func (p *Packfile) fillOFSDeltaObjectContent(obj plumbing.EncodedObject, offset int64) error { + buf := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(buf) + buf.Reset() + _, _, err := p.s.NextObject(buf) + if err != nil { + return err + } + + return p.fillOFSDeltaObjectContentWithBuffer(obj, offset, buf) +} + +func (p *Packfile) fillOFSDeltaObjectContentWithBuffer(obj plumbing.EncodedObject, offset int64, buf *bytes.Buffer) error { + hash, err := p.FindHash(offset) + if err != nil { + return err + } + + base, err := p.objectAtOffset(offset, hash) + if err != nil { + return err + } + + obj.SetType(base.Type()) + err = ApplyDelta(obj, base, buf.Bytes()) + p.cachePut(obj) + + return err +} + +func (p *Packfile) cacheGet(h plumbing.Hash) (plumbing.EncodedObject, bool) { + if p.deltaBaseCache == nil { + return nil, false + } + + return p.deltaBaseCache.Get(h) +} + +func (p *Packfile) cachePut(obj plumbing.EncodedObject) { + if p.deltaBaseCache == nil { + return + } + + p.deltaBaseCache.Put(obj) +} + +// GetAll returns an iterator with all encoded objects in the packfile. +// The iterator returned is not thread-safe, it should be used in the same +// thread as the Packfile instance. +func (p *Packfile) GetAll() (storer.EncodedObjectIter, error) { + return p.GetByType(plumbing.AnyObject) +} + +// GetByType returns all the objects of the given type. +func (p *Packfile) GetByType(typ plumbing.ObjectType) (storer.EncodedObjectIter, error) { + switch typ { + case plumbing.AnyObject, + plumbing.BlobObject, + plumbing.TreeObject, + plumbing.CommitObject, + plumbing.TagObject: + entries, err := p.EntriesByOffset() + if err != nil { + return nil, err + } + + return &objectIter{ + // Easiest way to provide an object decoder is just to pass a Packfile + // instance. To not mess with the seeks, it's a new instance with a + // different scanner but the same cache and offset to hash map for + // reusing as much cache as possible. + p: p, + iter: entries, + typ: typ, + }, nil + default: + return nil, plumbing.ErrInvalidType + } +} + +// ID returns the ID of the packfile, which is the checksum at the end of it. +func (p *Packfile) ID() (plumbing.Hash, error) { + prev, err := p.file.Seek(-20, io.SeekEnd) + if err != nil { + return plumbing.ZeroHash, err + } + + var hash plumbing.Hash + if _, err := io.ReadFull(p.file, hash[:]); err != nil { + return plumbing.ZeroHash, err + } + + if _, err := p.file.Seek(prev, io.SeekStart); err != nil { + return plumbing.ZeroHash, err + } + + return hash, nil +} + +// Scanner returns the packfile's Scanner +func (p *Packfile) Scanner() *Scanner { + return p.s +} + +// Close the packfile and its resources. +func (p *Packfile) Close() error { + closer, ok := p.file.(io.Closer) + if !ok { + return nil + } + + return closer.Close() +} + +type objectIter struct { + p *Packfile + typ plumbing.ObjectType + iter idxfile.EntryIter +} + +func (i *objectIter) Next() (plumbing.EncodedObject, error) { + for { + e, err := i.iter.Next() + if err != nil { + return nil, err + } + + if i.typ != plumbing.AnyObject { + if typ, ok := i.p.offsetToType[int64(e.Offset)]; ok { + if typ != i.typ { + continue + } + } else if obj, ok := i.p.cacheGet(e.Hash); ok { + if obj.Type() != i.typ { + i.p.offsetToType[int64(e.Offset)] = obj.Type() + continue + } + return obj, nil + } else { + h, err := i.p.objectHeaderAtOffset(int64(e.Offset)) + if err != nil { + return nil, err + } + + if h.Type == plumbing.REFDeltaObject || h.Type == plumbing.OFSDeltaObject { + typ, err := i.p.getObjectType(h) + if err != nil { + return nil, err + } + if typ != i.typ { + i.p.offsetToType[int64(e.Offset)] = typ + continue + } + // getObjectType will seek in the file so we cannot use getNextObject safely + return i.p.objectAtOffset(int64(e.Offset), e.Hash) + } else { + if h.Type != i.typ { + i.p.offsetToType[int64(e.Offset)] = h.Type + continue + } + return i.p.getNextObject(h, e.Hash) + } + } + } + + obj, err := i.p.objectAtOffset(int64(e.Offset), e.Hash) + if err != nil { + return nil, err + } + + return obj, nil + } +} + +func (i *objectIter) ForEach(f func(plumbing.EncodedObject) error) error { + for { + o, err := i.Next() + if err != nil { + if err == io.EOF { + return nil + } + return err + } + + if err := f(o); err != nil { + return err + } + } +} + +func (i *objectIter) Close() { + i.iter.Close() +} + +// isInvalid checks whether an error is an os.PathError with an os.ErrInvalid +// error inside. It also checks for the windows error, which is different from +// os.ErrInvalid. +func isInvalid(err error) bool { + pe, ok := err.(*os.PathError) + if !ok { + return false + } + + errstr := pe.Err.Error() + return errstr == errInvalidUnix || errstr == errInvalidWindows +} + +// errInvalidWindows is the Windows equivalent to os.ErrInvalid +const errInvalidWindows = "The parameter is incorrect." + +var errInvalidUnix = os.ErrInvalid.Error() diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/parser.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/parser.go new file mode 100644 index 0000000000..71cbba9838 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/parser.go @@ -0,0 +1,483 @@ +package packfile + +import ( + "bytes" + "errors" + "io" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/cache" + "gopkg.in/src-d/go-git.v4/plumbing/storer" +) + +var ( + // ErrReferenceDeltaNotFound is returned when the reference delta is not + // found. + ErrReferenceDeltaNotFound = errors.New("reference delta not found") + + // ErrNotSeekableSource is returned when the source for the parser is not + // seekable and a storage was not provided, so it can't be parsed. + ErrNotSeekableSource = errors.New("parser source is not seekable and storage was not provided") + + // ErrDeltaNotCached is returned when the delta could not be found in cache. + ErrDeltaNotCached = errors.New("delta could not be found in cache") +) + +// Observer interface is implemented by index encoders. +type Observer interface { + // OnHeader is called when a new packfile is opened. + OnHeader(count uint32) error + // OnInflatedObjectHeader is called for each object header read. + OnInflatedObjectHeader(t plumbing.ObjectType, objSize int64, pos int64) error + // OnInflatedObjectContent is called for each decoded object. + OnInflatedObjectContent(h plumbing.Hash, pos int64, crc uint32, content []byte) error + // OnFooter is called when decoding is done. + OnFooter(h plumbing.Hash) error +} + +// Parser decodes a packfile and calls any observer associated to it. Is used +// to generate indexes. +type Parser struct { + storage storer.EncodedObjectStorer + scanner *Scanner + count uint32 + oi []*objectInfo + oiByHash map[plumbing.Hash]*objectInfo + oiByOffset map[int64]*objectInfo + hashOffset map[plumbing.Hash]int64 + checksum plumbing.Hash + + cache *cache.BufferLRU + // delta content by offset, only used if source is not seekable + deltas map[int64][]byte + + ob []Observer +} + +// NewParser creates a new Parser. The Scanner source must be seekable. +// If it's not, NewParserWithStorage should be used instead. +func NewParser(scanner *Scanner, ob ...Observer) (*Parser, error) { + return NewParserWithStorage(scanner, nil, ob...) +} + +// NewParserWithStorage creates a new Parser. The scanner source must either +// be seekable or a storage must be provided. +func NewParserWithStorage( + scanner *Scanner, + storage storer.EncodedObjectStorer, + ob ...Observer, +) (*Parser, error) { + if !scanner.IsSeekable && storage == nil { + return nil, ErrNotSeekableSource + } + + var deltas map[int64][]byte + if !scanner.IsSeekable { + deltas = make(map[int64][]byte) + } + + return &Parser{ + storage: storage, + scanner: scanner, + ob: ob, + count: 0, + cache: cache.NewBufferLRUDefault(), + deltas: deltas, + }, nil +} + +func (p *Parser) forEachObserver(f func(o Observer) error) error { + for _, o := range p.ob { + if err := f(o); err != nil { + return err + } + } + return nil +} + +func (p *Parser) onHeader(count uint32) error { + return p.forEachObserver(func(o Observer) error { + return o.OnHeader(count) + }) +} + +func (p *Parser) onInflatedObjectHeader( + t plumbing.ObjectType, + objSize int64, + pos int64, +) error { + return p.forEachObserver(func(o Observer) error { + return o.OnInflatedObjectHeader(t, objSize, pos) + }) +} + +func (p *Parser) onInflatedObjectContent( + h plumbing.Hash, + pos int64, + crc uint32, + content []byte, +) error { + return p.forEachObserver(func(o Observer) error { + return o.OnInflatedObjectContent(h, pos, crc, content) + }) +} + +func (p *Parser) onFooter(h plumbing.Hash) error { + return p.forEachObserver(func(o Observer) error { + return o.OnFooter(h) + }) +} + +// Parse start decoding phase of the packfile. +func (p *Parser) Parse() (plumbing.Hash, error) { + if err := p.init(); err != nil { + return plumbing.ZeroHash, err + } + + if err := p.indexObjects(); err != nil { + return plumbing.ZeroHash, err + } + + var err error + p.checksum, err = p.scanner.Checksum() + if err != nil && err != io.EOF { + return plumbing.ZeroHash, err + } + + if err := p.resolveDeltas(); err != nil { + return plumbing.ZeroHash, err + } + + if err := p.onFooter(p.checksum); err != nil { + return plumbing.ZeroHash, err + } + + return p.checksum, nil +} + +func (p *Parser) init() error { + _, c, err := p.scanner.Header() + if err != nil { + return err + } + + if err := p.onHeader(c); err != nil { + return err + } + + p.count = c + p.oiByHash = make(map[plumbing.Hash]*objectInfo, p.count) + p.oiByOffset = make(map[int64]*objectInfo, p.count) + p.oi = make([]*objectInfo, p.count) + + return nil +} + +func (p *Parser) indexObjects() error { + buf := new(bytes.Buffer) + + for i := uint32(0); i < p.count; i++ { + buf.Reset() + + oh, err := p.scanner.NextObjectHeader() + if err != nil { + return err + } + + delta := false + var ota *objectInfo + switch t := oh.Type; t { + case plumbing.OFSDeltaObject: + delta = true + + parent, ok := p.oiByOffset[oh.OffsetReference] + if !ok { + return plumbing.ErrObjectNotFound + } + + ota = newDeltaObject(oh.Offset, oh.Length, t, parent) + parent.Children = append(parent.Children, ota) + case plumbing.REFDeltaObject: + delta = true + parent, ok := p.oiByHash[oh.Reference] + if !ok { + // can't find referenced object in this pack file + // this must be a "thin" pack. + parent = &objectInfo{ //Placeholder parent + SHA1: oh.Reference, + ExternalRef: true, // mark as an external reference that must be resolved + Type: plumbing.AnyObject, + DiskType: plumbing.AnyObject, + } + p.oiByHash[oh.Reference] = parent + } + ota = newDeltaObject(oh.Offset, oh.Length, t, parent) + parent.Children = append(parent.Children, ota) + + default: + ota = newBaseObject(oh.Offset, oh.Length, t) + } + + _, crc, err := p.scanner.NextObject(buf) + if err != nil { + return err + } + + ota.Crc32 = crc + ota.Length = oh.Length + + data := buf.Bytes() + if !delta { + sha1, err := getSHA1(ota.Type, data) + if err != nil { + return err + } + + ota.SHA1 = sha1 + p.oiByHash[ota.SHA1] = ota + } + + if p.storage != nil && !delta { + obj := new(plumbing.MemoryObject) + obj.SetSize(oh.Length) + obj.SetType(oh.Type) + if _, err := obj.Write(data); err != nil { + return err + } + + if _, err := p.storage.SetEncodedObject(obj); err != nil { + return err + } + } + + if delta && !p.scanner.IsSeekable { + p.deltas[oh.Offset] = make([]byte, len(data)) + copy(p.deltas[oh.Offset], data) + } + + p.oiByOffset[oh.Offset] = ota + p.oi[i] = ota + } + + return nil +} + +func (p *Parser) resolveDeltas() error { + for _, obj := range p.oi { + content, err := p.get(obj) + if err != nil { + return err + } + + if err := p.onInflatedObjectHeader(obj.Type, obj.Length, obj.Offset); err != nil { + return err + } + + if err := p.onInflatedObjectContent(obj.SHA1, obj.Offset, obj.Crc32, content); err != nil { + return err + } + + if !obj.IsDelta() && len(obj.Children) > 0 { + for _, child := range obj.Children { + if _, err := p.resolveObject(child, content); err != nil { + return err + } + } + + // Remove the delta from the cache. + if obj.DiskType.IsDelta() && !p.scanner.IsSeekable { + delete(p.deltas, obj.Offset) + } + } + } + + return nil +} + +func (p *Parser) get(o *objectInfo) (b []byte, err error) { + var ok bool + if !o.ExternalRef { // skip cache check for placeholder parents + b, ok = p.cache.Get(o.Offset) + } + + // If it's not on the cache and is not a delta we can try to find it in the + // storage, if there's one. External refs must enter here. + if !ok && p.storage != nil && !o.Type.IsDelta() { + e, err := p.storage.EncodedObject(plumbing.AnyObject, o.SHA1) + if err != nil { + return nil, err + } + o.Type = e.Type() + + r, err := e.Reader() + if err != nil { + return nil, err + } + + b = make([]byte, e.Size()) + if _, err = r.Read(b); err != nil { + return nil, err + } + } + + if b != nil { + return b, nil + } + + if o.ExternalRef { + // we were not able to resolve a ref in a thin pack + return nil, ErrReferenceDeltaNotFound + } + + var data []byte + if o.DiskType.IsDelta() { + base, err := p.get(o.Parent) + if err != nil { + return nil, err + } + + data, err = p.resolveObject(o, base) + if err != nil { + return nil, err + } + } else { + data, err = p.readData(o) + if err != nil { + return nil, err + } + } + + if len(o.Children) > 0 { + p.cache.Put(o.Offset, data) + } + + return data, nil +} + +func (p *Parser) resolveObject( + o *objectInfo, + base []byte, +) ([]byte, error) { + if !o.DiskType.IsDelta() { + return nil, nil + } + + data, err := p.readData(o) + if err != nil { + return nil, err + } + + data, err = applyPatchBase(o, data, base) + if err != nil { + return nil, err + } + + if p.storage != nil { + obj := new(plumbing.MemoryObject) + obj.SetSize(o.Size()) + obj.SetType(o.Type) + if _, err := obj.Write(data); err != nil { + return nil, err + } + + if _, err := p.storage.SetEncodedObject(obj); err != nil { + return nil, err + } + } + + return data, nil +} + +func (p *Parser) readData(o *objectInfo) ([]byte, error) { + if !p.scanner.IsSeekable && o.DiskType.IsDelta() { + data, ok := p.deltas[o.Offset] + if !ok { + return nil, ErrDeltaNotCached + } + + return data, nil + } + + if _, err := p.scanner.SeekObjectHeader(o.Offset); err != nil { + return nil, err + } + + buf := new(bytes.Buffer) + if _, _, err := p.scanner.NextObject(buf); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func applyPatchBase(ota *objectInfo, data, base []byte) ([]byte, error) { + patched, err := PatchDelta(base, data) + if err != nil { + return nil, err + } + + if ota.SHA1 == plumbing.ZeroHash { + ota.Type = ota.Parent.Type + sha1, err := getSHA1(ota.Type, patched) + if err != nil { + return nil, err + } + + ota.SHA1 = sha1 + ota.Length = int64(len(patched)) + } + + return patched, nil +} + +func getSHA1(t plumbing.ObjectType, data []byte) (plumbing.Hash, error) { + hasher := plumbing.NewHasher(t, int64(len(data))) + if _, err := hasher.Write(data); err != nil { + return plumbing.ZeroHash, err + } + + return hasher.Sum(), nil +} + +type objectInfo struct { + Offset int64 + Length int64 + Type plumbing.ObjectType + DiskType plumbing.ObjectType + ExternalRef bool // indicates this is an external reference in a thin pack file + + Crc32 uint32 + + Parent *objectInfo + Children []*objectInfo + SHA1 plumbing.Hash +} + +func newBaseObject(offset, length int64, t plumbing.ObjectType) *objectInfo { + return newDeltaObject(offset, length, t, nil) +} + +func newDeltaObject( + offset, length int64, + t plumbing.ObjectType, + parent *objectInfo, +) *objectInfo { + obj := &objectInfo{ + Offset: offset, + Length: length, + Type: t, + DiskType: t, + Crc32: 0, + Parent: parent, + } + + return obj +} + +func (o *objectInfo) IsDelta() bool { + return o.Type.IsDelta() +} + +func (o *objectInfo) Size() int64 { + return o.Length +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/patch_delta.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/patch_delta.go new file mode 100644 index 0000000000..a972f1c424 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/patch_delta.go @@ -0,0 +1,229 @@ +package packfile + +import ( + "errors" + "io/ioutil" + + "gopkg.in/src-d/go-git.v4/plumbing" +) + +// See https://github.com/git/git/blob/49fa3dc76179e04b0833542fa52d0f287a4955ac/delta.h +// https://github.com/git/git/blob/c2c5f6b1e479f2c38e0e01345350620944e3527f/patch-delta.c, +// and https://github.com/tarruda/node-git-core/blob/master/src/js/delta.js +// for details about the delta format. + +const deltaSizeMin = 4 + +// ApplyDelta writes to target the result of applying the modification deltas in delta to base. +func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) error { + r, err := base.Reader() + if err != nil { + return err + } + + w, err := target.Writer() + if err != nil { + return err + } + + src, err := ioutil.ReadAll(r) + if err != nil { + return err + } + + dst, err := PatchDelta(src, delta) + if err != nil { + return err + } + + target.SetSize(int64(len(dst))) + + _, err = w.Write(dst) + return err +} + +var ( + ErrInvalidDelta = errors.New("invalid delta") + ErrDeltaCmd = errors.New("wrong delta command") +) + +// PatchDelta returns the result of applying the modification deltas in delta to src. +// An error will be returned if delta is corrupted (ErrDeltaLen) or an action command +// is not copy from source or copy from delta (ErrDeltaCmd). +func PatchDelta(src, delta []byte) ([]byte, error) { + if len(delta) < deltaSizeMin { + return nil, ErrInvalidDelta + } + + srcSz, delta := decodeLEB128(delta) + if srcSz != uint(len(src)) { + return nil, ErrInvalidDelta + } + + targetSz, delta := decodeLEB128(delta) + remainingTargetSz := targetSz + + var cmd byte + dest := make([]byte, 0, targetSz) + for { + if len(delta) == 0 { + return nil, ErrInvalidDelta + } + + cmd = delta[0] + delta = delta[1:] + if isCopyFromSrc(cmd) { + var offset, sz uint + var err error + offset, delta, err = decodeOffset(cmd, delta) + if err != nil { + return nil, err + } + + sz, delta, err = decodeSize(cmd, delta) + if err != nil { + return nil, err + } + + if invalidSize(sz, targetSz) || + invalidOffsetSize(offset, sz, srcSz) { + break + } + dest = append(dest, src[offset:offset+sz]...) + remainingTargetSz -= sz + } else if isCopyFromDelta(cmd) { + sz := uint(cmd) // cmd is the size itself + if invalidSize(sz, targetSz) { + return nil, ErrInvalidDelta + } + + if uint(len(delta)) < sz { + return nil, ErrInvalidDelta + } + + dest = append(dest, delta[0:sz]...) + remainingTargetSz -= sz + delta = delta[sz:] + } else { + return nil, ErrDeltaCmd + } + + if remainingTargetSz <= 0 { + break + } + } + + return dest, nil +} + +// Decodes a number encoded as an unsigned LEB128 at the start of some +// binary data and returns the decoded number and the rest of the +// stream. +// +// This must be called twice on the delta data buffer, first to get the +// expected source buffer size, and again to get the target buffer size. +func decodeLEB128(input []byte) (uint, []byte) { + var num, sz uint + var b byte + for { + b = input[sz] + num |= (uint(b) & payload) << (sz * 7) // concats 7 bits chunks + sz++ + + if uint(b)&continuation == 0 || sz == uint(len(input)) { + break + } + } + + return num, input[sz:] +} + +const ( + payload = 0x7f // 0111 1111 + continuation = 0x80 // 1000 0000 +) + +func isCopyFromSrc(cmd byte) bool { + return (cmd & 0x80) != 0 +} + +func isCopyFromDelta(cmd byte) bool { + return (cmd&0x80) == 0 && cmd != 0 +} + +func decodeOffset(cmd byte, delta []byte) (uint, []byte, error) { + var offset uint + if (cmd & 0x01) != 0 { + if len(delta) == 0 { + return 0, nil, ErrInvalidDelta + } + offset = uint(delta[0]) + delta = delta[1:] + } + if (cmd & 0x02) != 0 { + if len(delta) == 0 { + return 0, nil, ErrInvalidDelta + } + offset |= uint(delta[0]) << 8 + delta = delta[1:] + } + if (cmd & 0x04) != 0 { + if len(delta) == 0 { + return 0, nil, ErrInvalidDelta + } + offset |= uint(delta[0]) << 16 + delta = delta[1:] + } + if (cmd & 0x08) != 0 { + if len(delta) == 0 { + return 0, nil, ErrInvalidDelta + } + offset |= uint(delta[0]) << 24 + delta = delta[1:] + } + + return offset, delta, nil +} + +func decodeSize(cmd byte, delta []byte) (uint, []byte, error) { + var sz uint + if (cmd & 0x10) != 0 { + if len(delta) == 0 { + return 0, nil, ErrInvalidDelta + } + sz = uint(delta[0]) + delta = delta[1:] + } + if (cmd & 0x20) != 0 { + if len(delta) == 0 { + return 0, nil, ErrInvalidDelta + } + sz |= uint(delta[0]) << 8 + delta = delta[1:] + } + if (cmd & 0x40) != 0 { + if len(delta) == 0 { + return 0, nil, ErrInvalidDelta + } + sz |= uint(delta[0]) << 16 + delta = delta[1:] + } + if sz == 0 { + sz = 0x10000 + } + + return sz, delta, nil +} + +func invalidSize(sz, targetSz uint) bool { + return sz > targetSz +} + +func invalidOffsetSize(offset, sz, srcSz uint) bool { + return sumOverflows(offset, sz) || + offset+sz > srcSz +} + +func sumOverflows(a, b uint) bool { + return a+b < a +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/scanner.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/scanner.go new file mode 100644 index 0000000000..7b44192a9c --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/packfile/scanner.go @@ -0,0 +1,466 @@ +package packfile + +import ( + "bufio" + "bytes" + "compress/zlib" + "fmt" + "hash" + "hash/crc32" + "io" + stdioutil "io/ioutil" + "sync" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/utils/binary" + "gopkg.in/src-d/go-git.v4/utils/ioutil" +) + +var ( + // ErrEmptyPackfile is returned by ReadHeader when no data is found in the packfile + ErrEmptyPackfile = NewError("empty packfile") + // ErrBadSignature is returned by ReadHeader when the signature in the packfile is incorrect. + ErrBadSignature = NewError("malformed pack file signature") + // ErrUnsupportedVersion is returned by ReadHeader when the packfile version is + // different than VersionSupported. + ErrUnsupportedVersion = NewError("unsupported packfile version") + // ErrSeekNotSupported returned if seek is not support + ErrSeekNotSupported = NewError("not seek support") +) + +// ObjectHeader contains the information related to the object, this information +// is collected from the previous bytes to the content of the object. +type ObjectHeader struct { + Type plumbing.ObjectType + Offset int64 + Length int64 + Reference plumbing.Hash + OffsetReference int64 +} + +type Scanner struct { + r *scannerReader + crc hash.Hash32 + + // pendingObject is used to detect if an object has been read, or still + // is waiting to be read + pendingObject *ObjectHeader + version, objects uint32 + + // lsSeekable says if this scanner can do Seek or not, to have a Scanner + // seekable a r implementing io.Seeker is required + IsSeekable bool +} + +// NewScanner returns a new Scanner based on a reader, if the given reader +// implements io.ReadSeeker the Scanner will be also Seekable +func NewScanner(r io.Reader) *Scanner { + _, ok := r.(io.ReadSeeker) + + crc := crc32.NewIEEE() + return &Scanner{ + r: newScannerReader(r, crc), + crc: crc, + IsSeekable: ok, + } +} + +func (s *Scanner) Reset(r io.Reader) { + _, ok := r.(io.ReadSeeker) + + s.r.Reset(r) + s.crc.Reset() + s.IsSeekable = ok + s.pendingObject = nil + s.version = 0 + s.objects = 0 +} + +// Header reads the whole packfile header (signature, version and object count). +// It returns the version and the object count and performs checks on the +// validity of the signature and the version fields. +func (s *Scanner) Header() (version, objects uint32, err error) { + if s.version != 0 { + return s.version, s.objects, nil + } + + sig, err := s.readSignature() + if err != nil { + if err == io.EOF { + err = ErrEmptyPackfile + } + + return + } + + if !s.isValidSignature(sig) { + err = ErrBadSignature + return + } + + version, err = s.readVersion() + s.version = version + if err != nil { + return + } + + if !s.isSupportedVersion(version) { + err = ErrUnsupportedVersion.AddDetails("%d", version) + return + } + + objects, err = s.readCount() + s.objects = objects + return +} + +// readSignature reads an returns the signature field in the packfile. +func (s *Scanner) readSignature() ([]byte, error) { + var sig = make([]byte, 4) + if _, err := io.ReadFull(s.r, sig); err != nil { + return []byte{}, err + } + + return sig, nil +} + +// isValidSignature returns if sig is a valid packfile signature. +func (s *Scanner) isValidSignature(sig []byte) bool { + return bytes.Equal(sig, signature) +} + +// readVersion reads and returns the version field of a packfile. +func (s *Scanner) readVersion() (uint32, error) { + return binary.ReadUint32(s.r) +} + +// isSupportedVersion returns whether version v is supported by the parser. +// The current supported version is VersionSupported, defined above. +func (s *Scanner) isSupportedVersion(v uint32) bool { + return v == VersionSupported +} + +// readCount reads and returns the count of objects field of a packfile. +func (s *Scanner) readCount() (uint32, error) { + return binary.ReadUint32(s.r) +} + +// SeekObjectHeader seeks to specified offset and returns the ObjectHeader +// for the next object in the reader +func (s *Scanner) SeekObjectHeader(offset int64) (*ObjectHeader, error) { + // if seeking we assume that you are not interested in the header + if s.version == 0 { + s.version = VersionSupported + } + + if _, err := s.r.Seek(offset, io.SeekStart); err != nil { + return nil, err + } + + h, err := s.nextObjectHeader() + if err != nil { + return nil, err + } + + h.Offset = offset + return h, nil +} + +// NextObjectHeader returns the ObjectHeader for the next object in the reader +func (s *Scanner) NextObjectHeader() (*ObjectHeader, error) { + if err := s.doPending(); err != nil { + return nil, err + } + + offset, err := s.r.Seek(0, io.SeekCurrent) + if err != nil { + return nil, err + } + + h, err := s.nextObjectHeader() + if err != nil { + return nil, err + } + + h.Offset = offset + return h, nil +} + +// nextObjectHeader returns the ObjectHeader for the next object in the reader +// without the Offset field +func (s *Scanner) nextObjectHeader() (*ObjectHeader, error) { + s.r.Flush() + s.crc.Reset() + + h := &ObjectHeader{} + s.pendingObject = h + + var err error + h.Offset, err = s.r.Seek(0, io.SeekCurrent) + if err != nil { + return nil, err + } + + h.Type, h.Length, err = s.readObjectTypeAndLength() + if err != nil { + return nil, err + } + + switch h.Type { + case plumbing.OFSDeltaObject: + no, err := binary.ReadVariableWidthInt(s.r) + if err != nil { + return nil, err + } + + h.OffsetReference = h.Offset - no + case plumbing.REFDeltaObject: + var err error + h.Reference, err = binary.ReadHash(s.r) + if err != nil { + return nil, err + } + } + + return h, nil +} + +func (s *Scanner) doPending() error { + if s.version == 0 { + var err error + s.version, s.objects, err = s.Header() + if err != nil { + return err + } + } + + return s.discardObjectIfNeeded() +} + +func (s *Scanner) discardObjectIfNeeded() error { + if s.pendingObject == nil { + return nil + } + + h := s.pendingObject + n, _, err := s.NextObject(stdioutil.Discard) + if err != nil { + return err + } + + if n != h.Length { + return fmt.Errorf( + "error discarding object, discarded %d, expected %d", + n, h.Length, + ) + } + + return nil +} + +// ReadObjectTypeAndLength reads and returns the object type and the +// length field from an object entry in a packfile. +func (s *Scanner) readObjectTypeAndLength() (plumbing.ObjectType, int64, error) { + t, c, err := s.readType() + if err != nil { + return t, 0, err + } + + l, err := s.readLength(c) + + return t, l, err +} + +func (s *Scanner) readType() (plumbing.ObjectType, byte, error) { + var c byte + var err error + if c, err = s.r.ReadByte(); err != nil { + return plumbing.ObjectType(0), 0, err + } + + typ := parseType(c) + + return typ, c, nil +} + +func parseType(b byte) plumbing.ObjectType { + return plumbing.ObjectType((b & maskType) >> firstLengthBits) +} + +// the length is codified in the last 4 bits of the first byte and in +// the last 7 bits of subsequent bytes. Last byte has a 0 MSB. +func (s *Scanner) readLength(first byte) (int64, error) { + length := int64(first & maskFirstLength) + + c := first + shift := firstLengthBits + var err error + for c&maskContinue > 0 { + if c, err = s.r.ReadByte(); err != nil { + return 0, err + } + + length += int64(c&maskLength) << shift + shift += lengthBits + } + + return length, nil +} + +// NextObject writes the content of the next object into the reader, returns +// the number of bytes written, the CRC32 of the content and an error, if any +func (s *Scanner) NextObject(w io.Writer) (written int64, crc32 uint32, err error) { + s.pendingObject = nil + written, err = s.copyObject(w) + + s.r.Flush() + crc32 = s.crc.Sum32() + s.crc.Reset() + + return +} + +// ReadRegularObject reads and write a non-deltified object +// from it zlib stream in an object entry in the packfile. +func (s *Scanner) copyObject(w io.Writer) (n int64, err error) { + zr := zlibReaderPool.Get().(io.ReadCloser) + defer zlibReaderPool.Put(zr) + + if err = zr.(zlib.Resetter).Reset(s.r, nil); err != nil { + return 0, fmt.Errorf("zlib reset error: %s", err) + } + + defer ioutil.CheckClose(zr, &err) + buf := byteSlicePool.Get().([]byte) + n, err = io.CopyBuffer(w, zr, buf) + byteSlicePool.Put(buf) + return +} + +var byteSlicePool = sync.Pool{ + New: func() interface{} { + return make([]byte, 32*1024) + }, +} + +// SeekFromStart sets a new offset from start, returns the old position before +// the change. +func (s *Scanner) SeekFromStart(offset int64) (previous int64, err error) { + // if seeking we assume that you are not interested in the header + if s.version == 0 { + s.version = VersionSupported + } + + previous, err = s.r.Seek(0, io.SeekCurrent) + if err != nil { + return -1, err + } + + _, err = s.r.Seek(offset, io.SeekStart) + return previous, err +} + +// Checksum returns the checksum of the packfile +func (s *Scanner) Checksum() (plumbing.Hash, error) { + err := s.discardObjectIfNeeded() + if err != nil { + return plumbing.ZeroHash, err + } + + return binary.ReadHash(s.r) +} + +// Close reads the reader until io.EOF +func (s *Scanner) Close() error { + buf := byteSlicePool.Get().([]byte) + _, err := io.CopyBuffer(stdioutil.Discard, s.r, buf) + byteSlicePool.Put(buf) + return err +} + +// Flush is a no-op (deprecated) +func (s *Scanner) Flush() error { + return nil +} + +// scannerReader has the following characteristics: +// - Provides an io.SeekReader impl for bufio.Reader, when the underlying +// reader supports it. +// - Keeps track of the current read position, for when the underlying reader +// isn't an io.SeekReader, but we still want to know the current offset. +// - Writes to the hash writer what it reads, with the aid of a smaller buffer. +// The buffer helps avoid a performance penality for performing small writes +// to the crc32 hash writer. +type scannerReader struct { + reader io.Reader + crc io.Writer + rbuf *bufio.Reader + wbuf *bufio.Writer + offset int64 +} + +func newScannerReader(r io.Reader, h io.Writer) *scannerReader { + sr := &scannerReader{ + rbuf: bufio.NewReader(nil), + wbuf: bufio.NewWriterSize(nil, 64), + crc: h, + } + sr.Reset(r) + + return sr +} + +func (r *scannerReader) Reset(reader io.Reader) { + r.reader = reader + r.rbuf.Reset(r.reader) + r.wbuf.Reset(r.crc) + + r.offset = 0 + if seeker, ok := r.reader.(io.ReadSeeker); ok { + r.offset, _ = seeker.Seek(0, io.SeekCurrent) + } +} + +func (r *scannerReader) Read(p []byte) (n int, err error) { + n, err = r.rbuf.Read(p) + + r.offset += int64(n) + if _, err := r.wbuf.Write(p[:n]); err != nil { + return n, err + } + return +} + +func (r *scannerReader) ReadByte() (b byte, err error) { + b, err = r.rbuf.ReadByte() + if err == nil { + r.offset++ + return b, r.wbuf.WriteByte(b) + } + return +} + +func (r *scannerReader) Flush() error { + return r.wbuf.Flush() +} + +// Seek seeks to a location. If the underlying reader is not an io.ReadSeeker, +// then only whence=io.SeekCurrent is supported, any other operation fails. +func (r *scannerReader) Seek(offset int64, whence int) (int64, error) { + var err error + + if seeker, ok := r.reader.(io.ReadSeeker); !ok { + if whence != io.SeekCurrent || offset != 0 { + return -1, ErrSeekNotSupported + } + } else { + if whence == io.SeekCurrent && offset == 0 { + return r.offset, nil + } + + r.offset, err = seeker.Seek(offset, whence) + r.rbuf.Reset(r.reader) + } + + return r.offset, err +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/pktline/encoder.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/pktline/encoder.go new file mode 100644 index 0000000000..6d409795b0 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/pktline/encoder.go @@ -0,0 +1,122 @@ +// Package pktline implements reading payloads form pkt-lines and encoding +// pkt-lines from payloads. +package pktline + +import ( + "bytes" + "errors" + "fmt" + "io" +) + +// An Encoder writes pkt-lines to an output stream. +type Encoder struct { + w io.Writer +} + +const ( + // MaxPayloadSize is the maximum payload size of a pkt-line in bytes. + MaxPayloadSize = 65516 + + // For compatibility with canonical Git implementation, accept longer pkt-lines + OversizePayloadMax = 65520 +) + +var ( + // FlushPkt are the contents of a flush-pkt pkt-line. + FlushPkt = []byte{'0', '0', '0', '0'} + // Flush is the payload to use with the Encode method to encode a flush-pkt. + Flush = []byte{} + // FlushString is the payload to use with the EncodeString method to encode a flush-pkt. + FlushString = "" + // ErrPayloadTooLong is returned by the Encode methods when any of the + // provided payloads is bigger than MaxPayloadSize. + ErrPayloadTooLong = errors.New("payload is too long") +) + +// NewEncoder returns a new encoder that writes to w. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + w: w, + } +} + +// Flush encodes a flush-pkt to the output stream. +func (e *Encoder) Flush() error { + _, err := e.w.Write(FlushPkt) + return err +} + +// Encode encodes a pkt-line with the payload specified and write it to +// the output stream. If several payloads are specified, each of them +// will get streamed in their own pkt-lines. +func (e *Encoder) Encode(payloads ...[]byte) error { + for _, p := range payloads { + if err := e.encodeLine(p); err != nil { + return err + } + } + + return nil +} + +func (e *Encoder) encodeLine(p []byte) error { + if len(p) > MaxPayloadSize { + return ErrPayloadTooLong + } + + if bytes.Equal(p, Flush) { + return e.Flush() + } + + n := len(p) + 4 + if _, err := e.w.Write(asciiHex16(n)); err != nil { + return err + } + _, err := e.w.Write(p) + return err +} + +// Returns the hexadecimal ascii representation of the 16 less +// significant bits of n. The length of the returned slice will always +// be 4. Example: if n is 1234 (0x4d2), the return value will be +// []byte{'0', '4', 'd', '2'}. +func asciiHex16(n int) []byte { + var ret [4]byte + ret[0] = byteToASCIIHex(byte(n & 0xf000 >> 12)) + ret[1] = byteToASCIIHex(byte(n & 0x0f00 >> 8)) + ret[2] = byteToASCIIHex(byte(n & 0x00f0 >> 4)) + ret[3] = byteToASCIIHex(byte(n & 0x000f)) + + return ret[:] +} + +// turns a byte into its hexadecimal ascii representation. Example: +// from 11 (0xb) to 'b'. +func byteToASCIIHex(n byte) byte { + if n < 10 { + return '0' + n + } + + return 'a' - 10 + n +} + +// EncodeString works similarly as Encode but payloads are specified as strings. +func (e *Encoder) EncodeString(payloads ...string) error { + for _, p := range payloads { + if err := e.Encode([]byte(p)); err != nil { + return err + } + } + + return nil +} + +// Encodef encodes a single pkt-line with the payload formatted as +// the format specifier. The rest of the arguments will be used in +// the format string. +func (e *Encoder) Encodef(format string, a ...interface{}) error { + return e.EncodeString( + fmt.Sprintf(format, a...), + ) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/pktline/scanner.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/pktline/scanner.go new file mode 100644 index 0000000000..99aab46e88 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/format/pktline/scanner.go @@ -0,0 +1,134 @@ +package pktline + +import ( + "errors" + "io" +) + +const ( + lenSize = 4 +) + +// ErrInvalidPktLen is returned by Err() when an invalid pkt-len is found. +var ErrInvalidPktLen = errors.New("invalid pkt-len found") + +// Scanner provides a convenient interface for reading the payloads of a +// series of pkt-lines. It takes an io.Reader providing the source, +// which then can be tokenized through repeated calls to the Scan +// method. +// +// After each Scan call, the Bytes method will return the payload of the +// corresponding pkt-line on a shared buffer, which will be 65516 bytes +// or smaller. Flush pkt-lines are represented by empty byte slices. +// +// Scanning stops at EOF or the first I/O error. +type Scanner struct { + r io.Reader // The reader provided by the client + err error // Sticky error + payload []byte // Last pkt-payload + len [lenSize]byte // Last pkt-len +} + +// NewScanner returns a new Scanner to read from r. +func NewScanner(r io.Reader) *Scanner { + return &Scanner{ + r: r, + } +} + +// Err returns the first error encountered by the Scanner. +func (s *Scanner) Err() error { + return s.err +} + +// Scan advances the Scanner to the next pkt-line, whose payload will +// then be available through the Bytes method. Scanning stops at EOF +// or the first I/O error. After Scan returns false, the Err method +// will return any error that occurred during scanning, except that if +// it was io.EOF, Err will return nil. +func (s *Scanner) Scan() bool { + var l int + l, s.err = s.readPayloadLen() + if s.err == io.EOF { + s.err = nil + return false + } + if s.err != nil { + return false + } + + if cap(s.payload) < l { + s.payload = make([]byte, 0, l) + } + + if _, s.err = io.ReadFull(s.r, s.payload[:l]); s.err != nil { + return false + } + s.payload = s.payload[:l] + + return true +} + +// Bytes returns the most recent payload generated by a call to Scan. +// The underlying array may point to data that will be overwritten by a +// subsequent call to Scan. It does no allocation. +func (s *Scanner) Bytes() []byte { + return s.payload +} + +// Method readPayloadLen returns the payload length by reading the +// pkt-len and subtracting the pkt-len size. +func (s *Scanner) readPayloadLen() (int, error) { + if _, err := io.ReadFull(s.r, s.len[:]); err != nil { + if err == io.ErrUnexpectedEOF { + return 0, ErrInvalidPktLen + } + + return 0, err + } + + n, err := hexDecode(s.len) + if err != nil { + return 0, err + } + + switch { + case n == 0: + return 0, nil + case n <= lenSize: + return 0, ErrInvalidPktLen + case n > OversizePayloadMax+lenSize: + return 0, ErrInvalidPktLen + default: + return n - lenSize, nil + } +} + +// Turns the hexadecimal representation of a number in a byte slice into +// a number. This function substitute strconv.ParseUint(string(buf), 16, +// 16) and/or hex.Decode, to avoid generating new strings, thus helping the +// GC. +func hexDecode(buf [lenSize]byte) (int, error) { + var ret int + for i := 0; i < lenSize; i++ { + n, err := asciiHexToByte(buf[i]) + if err != nil { + return 0, ErrInvalidPktLen + } + ret = 16*ret + int(n) + } + return ret, nil +} + +// turns the hexadecimal ascii representation of a byte into its +// numerical value. Example: from 'b' to 11 (0xb). +func asciiHexToByte(b byte) (byte, error) { + switch { + case b >= '0' && b <= '9': + return b - '0', nil + case b >= 'a' && b <= 'f': + return b - 'a' + 10, nil + default: + return 0, ErrInvalidPktLen + } +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/hash.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/hash.go new file mode 100644 index 0000000000..8e60877894 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/hash.go @@ -0,0 +1,73 @@ +package plumbing + +import ( + "bytes" + "crypto/sha1" + "encoding/hex" + "hash" + "sort" + "strconv" +) + +// Hash SHA1 hased content +type Hash [20]byte + +// ZeroHash is Hash with value zero +var ZeroHash Hash + +// ComputeHash compute the hash for a given ObjectType and content +func ComputeHash(t ObjectType, content []byte) Hash { + h := NewHasher(t, int64(len(content))) + h.Write(content) + return h.Sum() +} + +// NewHash return a new Hash from a hexadecimal hash representation +func NewHash(s string) Hash { + b, _ := hex.DecodeString(s) + + var h Hash + copy(h[:], b) + + return h +} + +func (h Hash) IsZero() bool { + var empty Hash + return h == empty +} + +func (h Hash) String() string { + return hex.EncodeToString(h[:]) +} + +type Hasher struct { + hash.Hash +} + +func NewHasher(t ObjectType, size int64) Hasher { + h := Hasher{sha1.New()} + h.Write(t.Bytes()) + h.Write([]byte(" ")) + h.Write([]byte(strconv.FormatInt(size, 10))) + h.Write([]byte{0}) + return h +} + +func (h Hasher) Sum() (hash Hash) { + copy(hash[:], h.Hash.Sum(nil)) + return +} + +// HashesSort sorts a slice of Hashes in increasing order. +func HashesSort(a []Hash) { + sort.Sort(HashSlice(a)) +} + +// HashSlice attaches the methods of sort.Interface to []Hash, sorting in +// increasing order. +type HashSlice []Hash + +func (p HashSlice) Len() int { return len(p) } +func (p HashSlice) Less(i, j int) bool { return bytes.Compare(p[i][:], p[j][:]) < 0 } +func (p HashSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/memory.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/memory.go new file mode 100644 index 0000000000..b8e1e1b817 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/memory.go @@ -0,0 +1,61 @@ +package plumbing + +import ( + "bytes" + "io" + "io/ioutil" +) + +// MemoryObject on memory Object implementation +type MemoryObject struct { + t ObjectType + h Hash + cont []byte + sz int64 +} + +// Hash returns the object Hash, the hash is calculated on-the-fly the first +// time it's called, in all subsequent calls the same Hash is returned even +// if the type or the content have changed. The Hash is only generated if the +// size of the content is exactly the object size. +func (o *MemoryObject) Hash() Hash { + if o.h == ZeroHash && int64(len(o.cont)) == o.sz { + o.h = ComputeHash(o.t, o.cont) + } + + return o.h +} + +// Type return the ObjectType +func (o *MemoryObject) Type() ObjectType { return o.t } + +// SetType sets the ObjectType +func (o *MemoryObject) SetType(t ObjectType) { o.t = t } + +// Size return the size of the object +func (o *MemoryObject) Size() int64 { return o.sz } + +// SetSize set the object size, a content of the given size should be written +// afterwards +func (o *MemoryObject) SetSize(s int64) { o.sz = s } + +// Reader returns a ObjectReader used to read the object's content. +func (o *MemoryObject) Reader() (io.ReadCloser, error) { + return ioutil.NopCloser(bytes.NewBuffer(o.cont)), nil +} + +// Writer returns a ObjectWriter used to write the object's content. +func (o *MemoryObject) Writer() (io.WriteCloser, error) { + return o, nil +} + +func (o *MemoryObject) Write(p []byte) (n int, err error) { + o.cont = append(o.cont, p...) + o.sz = int64(len(o.cont)) + + return len(p), nil +} + +// Close releases any resources consumed by the object when it is acting as a +// ObjectWriter. +func (o *MemoryObject) Close() error { return nil } diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object.go new file mode 100644 index 0000000000..2655dee43e --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object.go @@ -0,0 +1,111 @@ +// package plumbing implement the core interfaces and structs used by go-git +package plumbing + +import ( + "errors" + "io" +) + +var ( + ErrObjectNotFound = errors.New("object not found") + // ErrInvalidType is returned when an invalid object type is provided. + ErrInvalidType = errors.New("invalid object type") +) + +// Object is a generic representation of any git object +type EncodedObject interface { + Hash() Hash + Type() ObjectType + SetType(ObjectType) + Size() int64 + SetSize(int64) + Reader() (io.ReadCloser, error) + Writer() (io.WriteCloser, error) +} + +// DeltaObject is an EncodedObject representing a delta. +type DeltaObject interface { + EncodedObject + // BaseHash returns the hash of the object used as base for this delta. + BaseHash() Hash + // ActualHash returns the hash of the object after applying the delta. + ActualHash() Hash + // Size returns the size of the object after applying the delta. + ActualSize() int64 +} + +// ObjectType internal object type +// Integer values from 0 to 7 map to those exposed by git. +// AnyObject is used to represent any from 0 to 7. +type ObjectType int8 + +const ( + InvalidObject ObjectType = 0 + CommitObject ObjectType = 1 + TreeObject ObjectType = 2 + BlobObject ObjectType = 3 + TagObject ObjectType = 4 + // 5 reserved for future expansion + OFSDeltaObject ObjectType = 6 + REFDeltaObject ObjectType = 7 + + AnyObject ObjectType = -127 +) + +func (t ObjectType) String() string { + switch t { + case CommitObject: + return "commit" + case TreeObject: + return "tree" + case BlobObject: + return "blob" + case TagObject: + return "tag" + case OFSDeltaObject: + return "ofs-delta" + case REFDeltaObject: + return "ref-delta" + case AnyObject: + return "any" + default: + return "unknown" + } +} + +func (t ObjectType) Bytes() []byte { + return []byte(t.String()) +} + +// Valid returns true if t is a valid ObjectType. +func (t ObjectType) Valid() bool { + return t >= CommitObject && t <= REFDeltaObject +} + +// IsDelta returns true for any ObjectTyoe that represents a delta (i.e. +// REFDeltaObject or OFSDeltaObject). +func (t ObjectType) IsDelta() bool { + return t == REFDeltaObject || t == OFSDeltaObject +} + +// ParseObjectType parses a string representation of ObjectType. It returns an +// error on parse failure. +func ParseObjectType(value string) (typ ObjectType, err error) { + switch value { + case "commit": + typ = CommitObject + case "tree": + typ = TreeObject + case "blob": + typ = BlobObject + case "tag": + typ = TagObject + case "ofs-delta": + typ = OFSDeltaObject + case "ref-delta": + typ = REFDeltaObject + default: + err = ErrInvalidType + } + return +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/blob.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/blob.go new file mode 100644 index 0000000000..f376baa65a --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/blob.go @@ -0,0 +1,144 @@ +package object + +import ( + "io" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/storer" + "gopkg.in/src-d/go-git.v4/utils/ioutil" +) + +// Blob is used to store arbitrary data - it is generally a file. +type Blob struct { + // Hash of the blob. + Hash plumbing.Hash + // Size of the (uncompressed) blob. + Size int64 + + obj plumbing.EncodedObject +} + +// GetBlob gets a blob from an object storer and decodes it. +func GetBlob(s storer.EncodedObjectStorer, h plumbing.Hash) (*Blob, error) { + o, err := s.EncodedObject(plumbing.BlobObject, h) + if err != nil { + return nil, err + } + + return DecodeBlob(o) +} + +// DecodeObject decodes an encoded object into a *Blob. +func DecodeBlob(o plumbing.EncodedObject) (*Blob, error) { + b := &Blob{} + if err := b.Decode(o); err != nil { + return nil, err + } + + return b, nil +} + +// ID returns the object ID of the blob. The returned value will always match +// the current value of Blob.Hash. +// +// ID is present to fulfill the Object interface. +func (b *Blob) ID() plumbing.Hash { + return b.Hash +} + +// Type returns the type of object. It always returns plumbing.BlobObject. +// +// Type is present to fulfill the Object interface. +func (b *Blob) Type() plumbing.ObjectType { + return plumbing.BlobObject +} + +// Decode transforms a plumbing.EncodedObject into a Blob struct. +func (b *Blob) Decode(o plumbing.EncodedObject) error { + if o.Type() != plumbing.BlobObject { + return ErrUnsupportedObject + } + + b.Hash = o.Hash() + b.Size = o.Size() + b.obj = o + + return nil +} + +// Encode transforms a Blob into a plumbing.EncodedObject. +func (b *Blob) Encode(o plumbing.EncodedObject) (err error) { + o.SetType(plumbing.BlobObject) + + w, err := o.Writer() + if err != nil { + return err + } + + defer ioutil.CheckClose(w, &err) + + r, err := b.Reader() + if err != nil { + return err + } + + defer ioutil.CheckClose(r, &err) + + _, err = io.Copy(w, r) + return err +} + +// Reader returns a reader allow the access to the content of the blob +func (b *Blob) Reader() (io.ReadCloser, error) { + return b.obj.Reader() +} + +// BlobIter provides an iterator for a set of blobs. +type BlobIter struct { + storer.EncodedObjectIter + s storer.EncodedObjectStorer +} + +// NewBlobIter takes a storer.EncodedObjectStorer and a +// storer.EncodedObjectIter and returns a *BlobIter that iterates over all +// blobs contained in the storer.EncodedObjectIter. +// +// Any non-blob object returned by the storer.EncodedObjectIter is skipped. +func NewBlobIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) *BlobIter { + return &BlobIter{iter, s} +} + +// Next moves the iterator to the next blob and returns a pointer to it. If +// there are no more blobs, it returns io.EOF. +func (iter *BlobIter) Next() (*Blob, error) { + for { + obj, err := iter.EncodedObjectIter.Next() + if err != nil { + return nil, err + } + + if obj.Type() != plumbing.BlobObject { + continue + } + + return DecodeBlob(obj) + } +} + +// ForEach call the cb function for each blob contained on this iter until +// an error happens or the end of the iter is reached. If ErrStop is sent +// the iteration is stop but no error is returned. The iterator is closed. +func (iter *BlobIter) ForEach(cb func(*Blob) error) error { + return iter.EncodedObjectIter.ForEach(func(obj plumbing.EncodedObject) error { + if obj.Type() != plumbing.BlobObject { + return nil + } + + b, err := DecodeBlob(obj) + if err != nil { + return err + } + + return cb(b) + }) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/change.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/change.go new file mode 100644 index 0000000000..a1b4c27499 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/change.go @@ -0,0 +1,157 @@ +package object + +import ( + "bytes" + "context" + "fmt" + "strings" + + "gopkg.in/src-d/go-git.v4/utils/merkletrie" +) + +// Change values represent a detected change between two git trees. For +// modifications, From is the original status of the node and To is its +// final status. For insertions, From is the zero value and for +// deletions To is the zero value. +type Change struct { + From ChangeEntry + To ChangeEntry +} + +var empty = ChangeEntry{} + +// Action returns the kind of action represented by the change, an +// insertion, a deletion or a modification. +func (c *Change) Action() (merkletrie.Action, error) { + if c.From == empty && c.To == empty { + return merkletrie.Action(0), + fmt.Errorf("malformed change: empty from and to") + } + if c.From == empty { + return merkletrie.Insert, nil + } + if c.To == empty { + return merkletrie.Delete, nil + } + + return merkletrie.Modify, nil +} + +// Files return the files before and after a change. +// For insertions from will be nil. For deletions to will be nil. +func (c *Change) Files() (from, to *File, err error) { + action, err := c.Action() + if err != nil { + return + } + + if action == merkletrie.Insert || action == merkletrie.Modify { + to, err = c.To.Tree.TreeEntryFile(&c.To.TreeEntry) + if !c.To.TreeEntry.Mode.IsFile() { + return nil, nil, nil + } + + if err != nil { + return + } + } + + if action == merkletrie.Delete || action == merkletrie.Modify { + from, err = c.From.Tree.TreeEntryFile(&c.From.TreeEntry) + if !c.From.TreeEntry.Mode.IsFile() { + return nil, nil, nil + } + + if err != nil { + return + } + } + + return +} + +func (c *Change) String() string { + action, err := c.Action() + if err != nil { + return fmt.Sprintf("malformed change") + } + + return fmt.Sprintf("", action, c.name()) +} + +// Patch returns a Patch with all the file changes in chunks. This +// representation can be used to create several diff outputs. +func (c *Change) Patch() (*Patch, error) { + return c.PatchContext(context.Background()) +} + +// Patch returns a Patch with all the file changes in chunks. This +// representation can be used to create several diff outputs. +// If context expires, an non-nil error will be returned +// Provided context must be non-nil +func (c *Change) PatchContext(ctx context.Context) (*Patch, error) { + return getPatchContext(ctx, "", c) +} + +func (c *Change) name() string { + if c.From != empty { + return c.From.Name + } + + return c.To.Name +} + +// ChangeEntry values represent a node that has suffered a change. +type ChangeEntry struct { + // Full path of the node using "/" as separator. + Name string + // Parent tree of the node that has changed. + Tree *Tree + // The entry of the node. + TreeEntry TreeEntry +} + +// Changes represents a collection of changes between two git trees. +// Implements sort.Interface lexicographically over the path of the +// changed files. +type Changes []*Change + +func (c Changes) Len() int { + return len(c) +} + +func (c Changes) Swap(i, j int) { + c[i], c[j] = c[j], c[i] +} + +func (c Changes) Less(i, j int) bool { + return strings.Compare(c[i].name(), c[j].name()) < 0 +} + +func (c Changes) String() string { + var buffer bytes.Buffer + buffer.WriteString("[") + comma := "" + for _, v := range c { + buffer.WriteString(comma) + buffer.WriteString(v.String()) + comma = ", " + } + buffer.WriteString("]") + + return buffer.String() +} + +// Patch returns a Patch with all the changes in chunks. This +// representation can be used to create several diff outputs. +func (c Changes) Patch() (*Patch, error) { + return c.PatchContext(context.Background()) +} + +// Patch returns a Patch with all the changes in chunks. This +// representation can be used to create several diff outputs. +// If context expires, an non-nil error will be returned +// Provided context must be non-nil +func (c Changes) PatchContext(ctx context.Context) (*Patch, error) { + return getPatchContext(ctx, "", c...) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/change_adaptor.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/change_adaptor.go new file mode 100644 index 0000000000..491c39907a --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/change_adaptor.go @@ -0,0 +1,61 @@ +package object + +import ( + "errors" + "fmt" + + "gopkg.in/src-d/go-git.v4/utils/merkletrie" + "gopkg.in/src-d/go-git.v4/utils/merkletrie/noder" +) + +// The following functions transform changes types form the merkletrie +// package to changes types from this package. + +func newChange(c merkletrie.Change) (*Change, error) { + ret := &Change{} + + var err error + if ret.From, err = newChangeEntry(c.From); err != nil { + return nil, fmt.Errorf("From field: %s", err) + } + + if ret.To, err = newChangeEntry(c.To); err != nil { + return nil, fmt.Errorf("To field: %s", err) + } + + return ret, nil +} + +func newChangeEntry(p noder.Path) (ChangeEntry, error) { + if p == nil { + return empty, nil + } + + asTreeNoder, ok := p.Last().(*treeNoder) + if !ok { + return ChangeEntry{}, errors.New("cannot transform non-TreeNoders") + } + + return ChangeEntry{ + Name: p.String(), + Tree: asTreeNoder.parent, + TreeEntry: TreeEntry{ + Name: asTreeNoder.name, + Mode: asTreeNoder.mode, + Hash: asTreeNoder.hash, + }, + }, nil +} + +func newChanges(src merkletrie.Changes) (Changes, error) { + ret := make(Changes, len(src)) + var err error + for i, e := range src { + ret[i], err = newChange(e) + if err != nil { + return nil, fmt.Errorf("change #%d: %s", i, err) + } + } + + return ret, nil +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit.go new file mode 100644 index 0000000000..6b50934050 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit.go @@ -0,0 +1,430 @@ +package object + +import ( + "bufio" + "bytes" + "context" + "errors" + "fmt" + "io" + "strings" + + "golang.org/x/crypto/openpgp" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/storer" + "gopkg.in/src-d/go-git.v4/utils/ioutil" +) + +const ( + beginpgp string = "-----BEGIN PGP SIGNATURE-----" + endpgp string = "-----END PGP SIGNATURE-----" + headerpgp string = "gpgsig" +) + +// Hash represents the hash of an object +type Hash plumbing.Hash + +// Commit points to a single tree, marking it as what the project looked like +// at a certain point in time. It contains meta-information about that point +// in time, such as a timestamp, the author of the changes since the last +// commit, a pointer to the previous commit(s), etc. +// http://shafiulazam.com/gitbook/1_the_git_object_model.html +type Commit struct { + // Hash of the commit object. + Hash plumbing.Hash + // Author is the original author of the commit. + Author Signature + // Committer is the one performing the commit, might be different from + // Author. + Committer Signature + // PGPSignature is the PGP signature of the commit. + PGPSignature string + // Message is the commit message, contains arbitrary text. + Message string + // TreeHash is the hash of the root tree of the commit. + TreeHash plumbing.Hash + // ParentHashes are the hashes of the parent commits of the commit. + ParentHashes []plumbing.Hash + + s storer.EncodedObjectStorer +} + +// GetCommit gets a commit from an object storer and decodes it. +func GetCommit(s storer.EncodedObjectStorer, h plumbing.Hash) (*Commit, error) { + o, err := s.EncodedObject(plumbing.CommitObject, h) + if err != nil { + return nil, err + } + + return DecodeCommit(s, o) +} + +// DecodeCommit decodes an encoded object into a *Commit and associates it to +// the given object storer. +func DecodeCommit(s storer.EncodedObjectStorer, o plumbing.EncodedObject) (*Commit, error) { + c := &Commit{s: s} + if err := c.Decode(o); err != nil { + return nil, err + } + + return c, nil +} + +// Tree returns the Tree from the commit. +func (c *Commit) Tree() (*Tree, error) { + return GetTree(c.s, c.TreeHash) +} + +// PatchContext returns the Patch between the actual commit and the provided one. +// Error will be return if context expires. Provided context must be non-nil. +func (c *Commit) PatchContext(ctx context.Context, to *Commit) (*Patch, error) { + fromTree, err := c.Tree() + if err != nil { + return nil, err + } + + toTree, err := to.Tree() + if err != nil { + return nil, err + } + + return fromTree.PatchContext(ctx, toTree) +} + +// Patch returns the Patch between the actual commit and the provided one. +func (c *Commit) Patch(to *Commit) (*Patch, error) { + return c.PatchContext(context.Background(), to) +} + +// Parents return a CommitIter to the parent Commits. +func (c *Commit) Parents() CommitIter { + return NewCommitIter(c.s, + storer.NewEncodedObjectLookupIter(c.s, plumbing.CommitObject, c.ParentHashes), + ) +} + +// NumParents returns the number of parents in a commit. +func (c *Commit) NumParents() int { + return len(c.ParentHashes) +} + +var ErrParentNotFound = errors.New("commit parent not found") + +// Parent returns the ith parent of a commit. +func (c *Commit) Parent(i int) (*Commit, error) { + if len(c.ParentHashes) == 0 || i > len(c.ParentHashes)-1 { + return nil, ErrParentNotFound + } + + return GetCommit(c.s, c.ParentHashes[i]) +} + +// File returns the file with the specified "path" in the commit and a +// nil error if the file exists. If the file does not exist, it returns +// a nil file and the ErrFileNotFound error. +func (c *Commit) File(path string) (*File, error) { + tree, err := c.Tree() + if err != nil { + return nil, err + } + + return tree.File(path) +} + +// Files returns a FileIter allowing to iterate over the Tree +func (c *Commit) Files() (*FileIter, error) { + tree, err := c.Tree() + if err != nil { + return nil, err + } + + return tree.Files(), nil +} + +// ID returns the object ID of the commit. The returned value will always match +// the current value of Commit.Hash. +// +// ID is present to fulfill the Object interface. +func (c *Commit) ID() plumbing.Hash { + return c.Hash +} + +// Type returns the type of object. It always returns plumbing.CommitObject. +// +// Type is present to fulfill the Object interface. +func (c *Commit) Type() plumbing.ObjectType { + return plumbing.CommitObject +} + +// Decode transforms a plumbing.EncodedObject into a Commit struct. +func (c *Commit) Decode(o plumbing.EncodedObject) (err error) { + if o.Type() != plumbing.CommitObject { + return ErrUnsupportedObject + } + + c.Hash = o.Hash() + + reader, err := o.Reader() + if err != nil { + return err + } + defer ioutil.CheckClose(reader, &err) + + r := bufPool.Get().(*bufio.Reader) + defer bufPool.Put(r) + r.Reset(reader) + + var message bool + var pgpsig bool + for { + line, err := r.ReadBytes('\n') + if err != nil && err != io.EOF { + return err + } + + if pgpsig { + if len(line) > 0 && line[0] == ' ' { + line = bytes.TrimLeft(line, " ") + c.PGPSignature += string(line) + continue + } else { + pgpsig = false + } + } + + if !message { + line = bytes.TrimSpace(line) + if len(line) == 0 { + message = true + continue + } + + split := bytes.SplitN(line, []byte{' '}, 2) + + var data []byte + if len(split) == 2 { + data = split[1] + } + + switch string(split[0]) { + case "tree": + c.TreeHash = plumbing.NewHash(string(data)) + case "parent": + c.ParentHashes = append(c.ParentHashes, plumbing.NewHash(string(data))) + case "author": + c.Author.Decode(data) + case "committer": + c.Committer.Decode(data) + case headerpgp: + c.PGPSignature += string(data) + "\n" + pgpsig = true + } + } else { + c.Message += string(line) + } + + if err == io.EOF { + return nil + } + } +} + +// Encode transforms a Commit into a plumbing.EncodedObject. +func (b *Commit) Encode(o plumbing.EncodedObject) error { + return b.encode(o, true) +} + +// EncodeWithoutSignature export a Commit into a plumbing.EncodedObject without the signature (correspond to the payload of the PGP signature). +func (b *Commit) EncodeWithoutSignature(o plumbing.EncodedObject) error { + return b.encode(o, false) +} + +func (b *Commit) encode(o plumbing.EncodedObject, includeSig bool) (err error) { + o.SetType(plumbing.CommitObject) + w, err := o.Writer() + if err != nil { + return err + } + + defer ioutil.CheckClose(w, &err) + + if _, err = fmt.Fprintf(w, "tree %s\n", b.TreeHash.String()); err != nil { + return err + } + + for _, parent := range b.ParentHashes { + if _, err = fmt.Fprintf(w, "parent %s\n", parent.String()); err != nil { + return err + } + } + + if _, err = fmt.Fprint(w, "author "); err != nil { + return err + } + + if err = b.Author.Encode(w); err != nil { + return err + } + + if _, err = fmt.Fprint(w, "\ncommitter "); err != nil { + return err + } + + if err = b.Committer.Encode(w); err != nil { + return err + } + + if b.PGPSignature != "" && includeSig { + if _, err = fmt.Fprint(w, "\n"+headerpgp+" "); err != nil { + return err + } + + // Split all the signature lines and re-write with a left padding and + // newline. Use join for this so it's clear that a newline should not be + // added after this section, as it will be added when the message is + // printed. + signature := strings.TrimSuffix(b.PGPSignature, "\n") + lines := strings.Split(signature, "\n") + if _, err = fmt.Fprint(w, strings.Join(lines, "\n ")); err != nil { + return err + } + } + + if _, err = fmt.Fprintf(w, "\n\n%s", b.Message); err != nil { + return err + } + + return err +} + +// Stats returns the stats of a commit. +func (c *Commit) Stats() (FileStats, error) { + return c.StatsContext(context.Background()) +} + +// StatsContext returns the stats of a commit. Error will be return if context +// expires. Provided context must be non-nil. +func (c *Commit) StatsContext(ctx context.Context) (FileStats, error) { + fromTree, err := c.Tree() + if err != nil { + return nil, err + } + + toTree := &Tree{} + if c.NumParents() != 0 { + firstParent, err := c.Parents().Next() + if err != nil { + return nil, err + } + + toTree, err = firstParent.Tree() + if err != nil { + return nil, err + } + } + + patch, err := toTree.PatchContext(ctx, fromTree) + if err != nil { + return nil, err + } + + return getFileStatsFromFilePatches(patch.FilePatches()), nil +} + +func (c *Commit) String() string { + return fmt.Sprintf( + "%s %s\nAuthor: %s\nDate: %s\n\n%s\n", + plumbing.CommitObject, c.Hash, c.Author.String(), + c.Author.When.Format(DateFormat), indent(c.Message), + ) +} + +// Verify performs PGP verification of the commit with a provided armored +// keyring and returns openpgp.Entity associated with verifying key on success. +func (c *Commit) Verify(armoredKeyRing string) (*openpgp.Entity, error) { + keyRingReader := strings.NewReader(armoredKeyRing) + keyring, err := openpgp.ReadArmoredKeyRing(keyRingReader) + if err != nil { + return nil, err + } + + // Extract signature. + signature := strings.NewReader(c.PGPSignature) + + encoded := &plumbing.MemoryObject{} + // Encode commit components, excluding signature and get a reader object. + if err := c.EncodeWithoutSignature(encoded); err != nil { + return nil, err + } + er, err := encoded.Reader() + if err != nil { + return nil, err + } + + return openpgp.CheckArmoredDetachedSignature(keyring, er, signature) +} + +func indent(t string) string { + var output []string + for _, line := range strings.Split(t, "\n") { + if len(line) != 0 { + line = " " + line + } + + output = append(output, line) + } + + return strings.Join(output, "\n") +} + +// CommitIter is a generic closable interface for iterating over commits. +type CommitIter interface { + Next() (*Commit, error) + ForEach(func(*Commit) error) error + Close() +} + +// storerCommitIter provides an iterator from commits in an EncodedObjectStorer. +type storerCommitIter struct { + storer.EncodedObjectIter + s storer.EncodedObjectStorer +} + +// NewCommitIter takes a storer.EncodedObjectStorer and a +// storer.EncodedObjectIter and returns a CommitIter that iterates over all +// commits contained in the storer.EncodedObjectIter. +// +// Any non-commit object returned by the storer.EncodedObjectIter is skipped. +func NewCommitIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) CommitIter { + return &storerCommitIter{iter, s} +} + +// Next moves the iterator to the next commit and returns a pointer to it. If +// there are no more commits, it returns io.EOF. +func (iter *storerCommitIter) Next() (*Commit, error) { + obj, err := iter.EncodedObjectIter.Next() + if err != nil { + return nil, err + } + + return DecodeCommit(iter.s, obj) +} + +// ForEach call the cb function for each commit contained on this iter until +// an error appends or the end of the iter is reached. If ErrStop is sent +// the iteration is stopped but no error is returned. The iterator is closed. +func (iter *storerCommitIter) ForEach(cb func(*Commit) error) error { + return iter.EncodedObjectIter.ForEach(func(obj plumbing.EncodedObject) error { + c, err := DecodeCommit(iter.s, obj) + if err != nil { + return err + } + + return cb(c) + }) +} + +func (iter *storerCommitIter) Close() { + iter.EncodedObjectIter.Close() +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker.go new file mode 100644 index 0000000000..0eff059127 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker.go @@ -0,0 +1,327 @@ +package object + +import ( + "container/list" + "io" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/storer" + "gopkg.in/src-d/go-git.v4/storage" +) + +type commitPreIterator struct { + seenExternal map[plumbing.Hash]bool + seen map[plumbing.Hash]bool + stack []CommitIter + start *Commit +} + +// NewCommitPreorderIter returns a CommitIter that walks the commit history, +// starting at the given commit and visiting its parents in pre-order. +// The given callback will be called for each visited commit. Each commit will +// be visited only once. If the callback returns an error, walking will stop +// and will return the error. Other errors might be returned if the history +// cannot be traversed (e.g. missing objects). Ignore allows to skip some +// commits from being iterated. +func NewCommitPreorderIter( + c *Commit, + seenExternal map[plumbing.Hash]bool, + ignore []plumbing.Hash, +) CommitIter { + seen := make(map[plumbing.Hash]bool) + for _, h := range ignore { + seen[h] = true + } + + return &commitPreIterator{ + seenExternal: seenExternal, + seen: seen, + stack: make([]CommitIter, 0), + start: c, + } +} + +func (w *commitPreIterator) Next() (*Commit, error) { + var c *Commit + for { + if w.start != nil { + c = w.start + w.start = nil + } else { + current := len(w.stack) - 1 + if current < 0 { + return nil, io.EOF + } + + var err error + c, err = w.stack[current].Next() + if err == io.EOF { + w.stack = w.stack[:current] + continue + } + + if err != nil { + return nil, err + } + } + + if w.seen[c.Hash] || w.seenExternal[c.Hash] { + continue + } + + w.seen[c.Hash] = true + + if c.NumParents() > 0 { + w.stack = append(w.stack, filteredParentIter(c, w.seen)) + } + + return c, nil + } +} + +func filteredParentIter(c *Commit, seen map[plumbing.Hash]bool) CommitIter { + var hashes []plumbing.Hash + for _, h := range c.ParentHashes { + if !seen[h] { + hashes = append(hashes, h) + } + } + + return NewCommitIter(c.s, + storer.NewEncodedObjectLookupIter(c.s, plumbing.CommitObject, hashes), + ) +} + +func (w *commitPreIterator) ForEach(cb func(*Commit) error) error { + for { + c, err := w.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + + err = cb(c) + if err == storer.ErrStop { + break + } + if err != nil { + return err + } + } + + return nil +} + +func (w *commitPreIterator) Close() {} + +type commitPostIterator struct { + stack []*Commit + seen map[plumbing.Hash]bool +} + +// NewCommitPostorderIter returns a CommitIter that walks the commit +// history like WalkCommitHistory but in post-order. This means that after +// walking a merge commit, the merged commit will be walked before the base +// it was merged on. This can be useful if you wish to see the history in +// chronological order. Ignore allows to skip some commits from being iterated. +func NewCommitPostorderIter(c *Commit, ignore []plumbing.Hash) CommitIter { + seen := make(map[plumbing.Hash]bool) + for _, h := range ignore { + seen[h] = true + } + + return &commitPostIterator{ + stack: []*Commit{c}, + seen: seen, + } +} + +func (w *commitPostIterator) Next() (*Commit, error) { + for { + if len(w.stack) == 0 { + return nil, io.EOF + } + + c := w.stack[len(w.stack)-1] + w.stack = w.stack[:len(w.stack)-1] + + if w.seen[c.Hash] { + continue + } + + w.seen[c.Hash] = true + + return c, c.Parents().ForEach(func(p *Commit) error { + w.stack = append(w.stack, p) + return nil + }) + } +} + +func (w *commitPostIterator) ForEach(cb func(*Commit) error) error { + for { + c, err := w.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + + err = cb(c) + if err == storer.ErrStop { + break + } + if err != nil { + return err + } + } + + return nil +} + +func (w *commitPostIterator) Close() {} + +// commitAllIterator stands for commit iterator for all refs. +type commitAllIterator struct { + // currCommit points to the current commit. + currCommit *list.Element +} + +// NewCommitAllIter returns a new commit iterator for all refs. +// repoStorer is a repo Storer used to get commits and references. +// commitIterFunc is a commit iterator function, used to iterate through ref commits in chosen order +func NewCommitAllIter(repoStorer storage.Storer, commitIterFunc func(*Commit) CommitIter) (CommitIter, error) { + commitsPath := list.New() + commitsLookup := make(map[plumbing.Hash]*list.Element) + head, err := storer.ResolveReference(repoStorer, plumbing.HEAD) + if err == nil { + err = addReference(repoStorer, commitIterFunc, head, commitsPath, commitsLookup) + } + + if err != nil && err != plumbing.ErrReferenceNotFound { + return nil, err + } + + // add all references along with the HEAD + refIter, err := repoStorer.IterReferences() + if err != nil { + return nil, err + } + defer refIter.Close() + + for { + ref, err := refIter.Next() + if err == io.EOF { + break + } + + if err == plumbing.ErrReferenceNotFound { + continue + } + + if err != nil { + return nil, err + } + + if err = addReference(repoStorer, commitIterFunc, ref, commitsPath, commitsLookup); err != nil { + return nil, err + } + } + + return &commitAllIterator{commitsPath.Front()}, nil +} + +func addReference( + repoStorer storage.Storer, + commitIterFunc func(*Commit) CommitIter, + ref *plumbing.Reference, + commitsPath *list.List, + commitsLookup map[plumbing.Hash]*list.Element) error { + + _, exists := commitsLookup[ref.Hash()] + if exists { + // we already have it - skip the reference. + return nil + } + + refCommit, _ := GetCommit(repoStorer, ref.Hash()) + if refCommit == nil { + // if it's not a commit - skip it. + return nil + } + + var ( + refCommits []*Commit + parent *list.Element + ) + // collect all ref commits to add + commitIter := commitIterFunc(refCommit) + for c, e := commitIter.Next(); e == nil; { + parent, exists = commitsLookup[c.Hash] + if exists { + break + } + refCommits = append(refCommits, c) + c, e = commitIter.Next() + } + commitIter.Close() + + if parent == nil { + // common parent - not found + // add all commits to the path from this ref (maybe it's a HEAD and we don't have anything, yet) + for _, c := range refCommits { + parent = commitsPath.PushBack(c) + commitsLookup[c.Hash] = parent + } + } else { + // add ref's commits to the path in reverse order (from the latest) + for i := len(refCommits) - 1; i >= 0; i-- { + c := refCommits[i] + // insert before found common parent + parent = commitsPath.InsertBefore(c, parent) + commitsLookup[c.Hash] = parent + } + } + + return nil +} + +func (it *commitAllIterator) Next() (*Commit, error) { + if it.currCommit == nil { + return nil, io.EOF + } + + c := it.currCommit.Value.(*Commit) + it.currCommit = it.currCommit.Next() + + return c, nil +} + +func (it *commitAllIterator) ForEach(cb func(*Commit) error) error { + for { + c, err := it.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + + err = cb(c) + if err == storer.ErrStop { + break + } + if err != nil { + return err + } + } + + return nil +} + +func (it *commitAllIterator) Close() { + it.currCommit = nil +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker_bfs.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker_bfs.go new file mode 100644 index 0000000000..dabfe75c27 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker_bfs.go @@ -0,0 +1,100 @@ +package object + +import ( + "io" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/storer" +) + +type bfsCommitIterator struct { + seenExternal map[plumbing.Hash]bool + seen map[plumbing.Hash]bool + queue []*Commit +} + +// NewCommitIterBSF returns a CommitIter that walks the commit history, +// starting at the given commit and visiting its parents in pre-order. +// The given callback will be called for each visited commit. Each commit will +// be visited only once. If the callback returns an error, walking will stop +// and will return the error. Other errors might be returned if the history +// cannot be traversed (e.g. missing objects). Ignore allows to skip some +// commits from being iterated. +func NewCommitIterBSF( + c *Commit, + seenExternal map[plumbing.Hash]bool, + ignore []plumbing.Hash, +) CommitIter { + seen := make(map[plumbing.Hash]bool) + for _, h := range ignore { + seen[h] = true + } + + return &bfsCommitIterator{ + seenExternal: seenExternal, + seen: seen, + queue: []*Commit{c}, + } +} + +func (w *bfsCommitIterator) appendHash(store storer.EncodedObjectStorer, h plumbing.Hash) error { + if w.seen[h] || w.seenExternal[h] { + return nil + } + c, err := GetCommit(store, h) + if err != nil { + return err + } + w.queue = append(w.queue, c) + return nil +} + +func (w *bfsCommitIterator) Next() (*Commit, error) { + var c *Commit + for { + if len(w.queue) == 0 { + return nil, io.EOF + } + c = w.queue[0] + w.queue = w.queue[1:] + + if w.seen[c.Hash] || w.seenExternal[c.Hash] { + continue + } + + w.seen[c.Hash] = true + + for _, h := range c.ParentHashes { + err := w.appendHash(c.s, h) + if err != nil { + return nil, err + } + } + + return c, nil + } +} + +func (w *bfsCommitIterator) ForEach(cb func(*Commit) error) error { + for { + c, err := w.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + + err = cb(c) + if err == storer.ErrStop { + break + } + if err != nil { + return err + } + } + + return nil +} + +func (w *bfsCommitIterator) Close() {} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker_bfs_filtered.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker_bfs_filtered.go new file mode 100644 index 0000000000..b12523d489 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker_bfs_filtered.go @@ -0,0 +1,176 @@ +package object + +import ( + "io" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/storer" +) + +// NewFilterCommitIter returns a CommitIter that walks the commit history, +// starting at the passed commit and visiting its parents in Breadth-first order. +// The commits returned by the CommitIter will validate the passed CommitFilter. +// The history won't be transversed beyond a commit if isLimit is true for it. +// Each commit will be visited only once. +// If the commit history can not be traversed, or the Close() method is called, +// the CommitIter won't return more commits. +// If no isValid is passed, all ancestors of from commit will be valid. +// If no isLimit is limmit, all ancestors of all commits will be visited. +func NewFilterCommitIter( + from *Commit, + isValid *CommitFilter, + isLimit *CommitFilter, +) CommitIter { + var validFilter CommitFilter + if isValid == nil { + validFilter = func(_ *Commit) bool { + return true + } + } else { + validFilter = *isValid + } + + var limitFilter CommitFilter + if isLimit == nil { + limitFilter = func(_ *Commit) bool { + return false + } + } else { + limitFilter = *isLimit + } + + return &filterCommitIter{ + isValid: validFilter, + isLimit: limitFilter, + visited: map[plumbing.Hash]struct{}{}, + queue: []*Commit{from}, + } +} + +// CommitFilter returns a boolean for the passed Commit +type CommitFilter func(*Commit) bool + +// filterCommitIter implments CommitIter +type filterCommitIter struct { + isValid CommitFilter + isLimit CommitFilter + visited map[plumbing.Hash]struct{} + queue []*Commit + lastErr error +} + +// Next returns the next commit of the CommitIter. +// It will return io.EOF if there are no more commits to visit, +// or an error if the history could not be traversed. +func (w *filterCommitIter) Next() (*Commit, error) { + var commit *Commit + var err error + for { + commit, err = w.popNewFromQueue() + if err != nil { + return nil, w.close(err) + } + + w.visited[commit.Hash] = struct{}{} + + if !w.isLimit(commit) { + err = w.addToQueue(commit.s, commit.ParentHashes...) + if err != nil { + return nil, w.close(err) + } + } + + if w.isValid(commit) { + return commit, nil + } + } +} + +// ForEach runs the passed callback over each Commit returned by the CommitIter +// until the callback returns an error or there is no more commits to traverse. +func (w *filterCommitIter) ForEach(cb func(*Commit) error) error { + for { + commit, err := w.Next() + if err == io.EOF { + break + } + + if err != nil { + return err + } + + if err := cb(commit); err == storer.ErrStop { + break + } else if err != nil { + return err + } + } + + return nil +} + +// Error returns the error that caused that the CommitIter is no longer returning commits +func (w *filterCommitIter) Error() error { + return w.lastErr +} + +// Close closes the CommitIter +func (w *filterCommitIter) Close() { + w.visited = map[plumbing.Hash]struct{}{} + w.queue = []*Commit{} + w.isLimit = nil + w.isValid = nil +} + +// close closes the CommitIter with an error +func (w *filterCommitIter) close(err error) error { + w.Close() + w.lastErr = err + return err +} + +// popNewFromQueue returns the first new commit from the internal fifo queue, +// or an io.EOF error if the queue is empty +func (w *filterCommitIter) popNewFromQueue() (*Commit, error) { + var first *Commit + for { + if len(w.queue) == 0 { + if w.lastErr != nil { + return nil, w.lastErr + } + + return nil, io.EOF + } + + first = w.queue[0] + w.queue = w.queue[1:] + if _, ok := w.visited[first.Hash]; ok { + continue + } + + return first, nil + } +} + +// addToQueue adds the passed commits to the internal fifo queue if they weren't seen +// or returns an error if the passed hashes could not be used to get valid commits +func (w *filterCommitIter) addToQueue( + store storer.EncodedObjectStorer, + hashes ...plumbing.Hash, +) error { + for _, hash := range hashes { + if _, ok := w.visited[hash]; ok { + continue + } + + commit, err := GetCommit(store, hash) + if err != nil { + return err + } + + w.queue = append(w.queue, commit) + } + + return nil +} + diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker_ctime.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker_ctime.go new file mode 100644 index 0000000000..019161496f --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker_ctime.go @@ -0,0 +1,103 @@ +package object + +import ( + "io" + + "github.com/emirpasic/gods/trees/binaryheap" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/storer" +) + +type commitIteratorByCTime struct { + seenExternal map[plumbing.Hash]bool + seen map[plumbing.Hash]bool + heap *binaryheap.Heap +} + +// NewCommitIterCTime returns a CommitIter that walks the commit history, +// starting at the given commit and visiting its parents while preserving Committer Time order. +// this appears to be the closest order to `git log` +// The given callback will be called for each visited commit. Each commit will +// be visited only once. If the callback returns an error, walking will stop +// and will return the error. Other errors might be returned if the history +// cannot be traversed (e.g. missing objects). Ignore allows to skip some +// commits from being iterated. +func NewCommitIterCTime( + c *Commit, + seenExternal map[plumbing.Hash]bool, + ignore []plumbing.Hash, +) CommitIter { + seen := make(map[plumbing.Hash]bool) + for _, h := range ignore { + seen[h] = true + } + + heap := binaryheap.NewWith(func(a, b interface{}) int { + if a.(*Commit).Committer.When.Before(b.(*Commit).Committer.When) { + return 1 + } + return -1 + }) + heap.Push(c) + + return &commitIteratorByCTime{ + seenExternal: seenExternal, + seen: seen, + heap: heap, + } +} + +func (w *commitIteratorByCTime) Next() (*Commit, error) { + var c *Commit + for { + cIn, ok := w.heap.Pop() + if !ok { + return nil, io.EOF + } + c = cIn.(*Commit) + + if w.seen[c.Hash] || w.seenExternal[c.Hash] { + continue + } + + w.seen[c.Hash] = true + + for _, h := range c.ParentHashes { + if w.seen[h] || w.seenExternal[h] { + continue + } + pc, err := GetCommit(c.s, h) + if err != nil { + return nil, err + } + w.heap.Push(pc) + } + + return c, nil + } +} + +func (w *commitIteratorByCTime) ForEach(cb func(*Commit) error) error { + for { + c, err := w.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + + err = cb(c) + if err == storer.ErrStop { + break + } + if err != nil { + return err + } + } + + return nil +} + +func (w *commitIteratorByCTime) Close() {} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker_file.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker_file.go new file mode 100644 index 0000000000..6f16e611f1 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/commit_walker_file.go @@ -0,0 +1,145 @@ +package object + +import ( + "io" + + "gopkg.in/src-d/go-git.v4/plumbing" + + "gopkg.in/src-d/go-git.v4/plumbing/storer" +) + +type commitFileIter struct { + fileName string + sourceIter CommitIter + currentCommit *Commit + checkParent bool +} + +// NewCommitFileIterFromIter returns a commit iterator which performs diffTree between +// successive trees returned from the commit iterator from the argument. The purpose of this is +// to find the commits that explain how the files that match the path came to be. +// If checkParent is true then the function double checks if potential parent (next commit in a path) +// is one of the parents in the tree (it's used by `git log --all`). +func NewCommitFileIterFromIter(fileName string, commitIter CommitIter, checkParent bool) CommitIter { + iterator := new(commitFileIter) + iterator.sourceIter = commitIter + iterator.fileName = fileName + iterator.checkParent = checkParent + return iterator +} + +func (c *commitFileIter) Next() (*Commit, error) { + if c.currentCommit == nil { + var err error + c.currentCommit, err = c.sourceIter.Next() + if err != nil { + return nil, err + } + } + commit, commitErr := c.getNextFileCommit() + + // Setting current-commit to nil to prevent unwanted states when errors are raised + if commitErr != nil { + c.currentCommit = nil + } + return commit, commitErr +} + +func (c *commitFileIter) getNextFileCommit() (*Commit, error) { + for { + // Parent-commit can be nil if the current-commit is the initial commit + parentCommit, parentCommitErr := c.sourceIter.Next() + if parentCommitErr != nil { + // If the parent-commit is beyond the initial commit, keep it nil + if parentCommitErr != io.EOF { + return nil, parentCommitErr + } + parentCommit = nil + } + + // Fetch the trees of the current and parent commits + currentTree, currTreeErr := c.currentCommit.Tree() + if currTreeErr != nil { + return nil, currTreeErr + } + + var parentTree *Tree + if parentCommit != nil { + var parentTreeErr error + parentTree, parentTreeErr = parentCommit.Tree() + if parentTreeErr != nil { + return nil, parentTreeErr + } + } + + // Find diff between current and parent trees + changes, diffErr := DiffTree(currentTree, parentTree) + if diffErr != nil { + return nil, diffErr + } + + found := c.hasFileChange(changes, parentCommit) + + // Storing the current-commit in-case a change is found, and + // Updating the current-commit for the next-iteration + prevCommit := c.currentCommit + c.currentCommit = parentCommit + + if found { + return prevCommit, nil + } + + // If not matches found and if parent-commit is beyond the initial commit, then return with EOF + if parentCommit == nil { + return nil, io.EOF + } + } +} + +func (c *commitFileIter) hasFileChange(changes Changes, parent *Commit) bool { + for _, change := range changes { + if change.name() != c.fileName { + continue + } + + // filename matches, now check if source iterator contains all commits (from all refs) + if c.checkParent { + if parent != nil && isParentHash(parent.Hash, c.currentCommit) { + return true + } + continue + } + + return true + } + + return false +} + +func isParentHash(hash plumbing.Hash, commit *Commit) bool { + for _, h := range commit.ParentHashes { + if h == hash { + return true + } + } + return false +} + +func (c *commitFileIter) ForEach(cb func(*Commit) error) error { + for { + commit, nextErr := c.Next() + if nextErr != nil { + return nextErr + } + err := cb(commit) + if err == storer.ErrStop { + return nil + } else if err != nil { + return err + } + } +} + +func (c *commitFileIter) Close() { + c.sourceIter.Close() +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/common.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/common.go new file mode 100644 index 0000000000..3591f5f0a6 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/common.go @@ -0,0 +1,12 @@ +package object + +import ( + "bufio" + "sync" +) + +var bufPool = sync.Pool{ + New: func() interface{} { + return bufio.NewReader(nil) + }, +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/difftree.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/difftree.go new file mode 100644 index 0000000000..a30a29e37f --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/difftree.go @@ -0,0 +1,37 @@ +package object + +import ( + "bytes" + "context" + + "gopkg.in/src-d/go-git.v4/utils/merkletrie" + "gopkg.in/src-d/go-git.v4/utils/merkletrie/noder" +) + +// DiffTree compares the content and mode of the blobs found via two +// tree objects. +func DiffTree(a, b *Tree) (Changes, error) { + return DiffTreeContext(context.Background(), a, b) +} + +// DiffTree compares the content and mode of the blobs found via two +// tree objects. Provided context must be non-nil. +// An error will be return if context expires +func DiffTreeContext(ctx context.Context, a, b *Tree) (Changes, error) { + from := NewTreeRootNode(a) + to := NewTreeRootNode(b) + + hashEqual := func(a, b noder.Hasher) bool { + return bytes.Equal(a.Hash(), b.Hash()) + } + + merkletrieChanges, err := merkletrie.DiffTreeContext(ctx, from, to, hashEqual) + if err != nil { + if err == merkletrie.ErrCanceled { + return nil, ErrCanceled + } + return nil, err + } + + return newChanges(merkletrieChanges) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/file.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/file.go new file mode 100644 index 0000000000..1c5fdbb386 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/file.go @@ -0,0 +1,137 @@ +package object + +import ( + "bytes" + "io" + "strings" + + "gopkg.in/src-d/go-git.v4/plumbing/filemode" + "gopkg.in/src-d/go-git.v4/plumbing/storer" + "gopkg.in/src-d/go-git.v4/utils/binary" + "gopkg.in/src-d/go-git.v4/utils/ioutil" +) + +// File represents git file objects. +type File struct { + // Name is the path of the file. It might be relative to a tree, + // depending of the function that generates it. + Name string + // Mode is the file mode. + Mode filemode.FileMode + // Blob with the contents of the file. + Blob +} + +// NewFile returns a File based on the given blob object +func NewFile(name string, m filemode.FileMode, b *Blob) *File { + return &File{Name: name, Mode: m, Blob: *b} +} + +// Contents returns the contents of a file as a string. +func (f *File) Contents() (content string, err error) { + reader, err := f.Reader() + if err != nil { + return "", err + } + defer ioutil.CheckClose(reader, &err) + + buf := new(bytes.Buffer) + if _, err := buf.ReadFrom(reader); err != nil { + return "", err + } + + return buf.String(), nil +} + +// IsBinary returns if the file is binary or not +func (f *File) IsBinary() (bin bool, err error) { + reader, err := f.Reader() + if err != nil { + return false, err + } + defer ioutil.CheckClose(reader, &err) + + return binary.IsBinary(reader) +} + +// Lines returns a slice of lines from the contents of a file, stripping +// all end of line characters. If the last line is empty (does not end +// in an end of line), it is also stripped. +func (f *File) Lines() ([]string, error) { + content, err := f.Contents() + if err != nil { + return nil, err + } + + splits := strings.Split(content, "\n") + // remove the last line if it is empty + if splits[len(splits)-1] == "" { + return splits[:len(splits)-1], nil + } + + return splits, nil +} + +// FileIter provides an iterator for the files in a tree. +type FileIter struct { + s storer.EncodedObjectStorer + w TreeWalker +} + +// NewFileIter takes a storer.EncodedObjectStorer and a Tree and returns a +// *FileIter that iterates over all files contained in the tree, recursively. +func NewFileIter(s storer.EncodedObjectStorer, t *Tree) *FileIter { + return &FileIter{s: s, w: *NewTreeWalker(t, true, nil)} +} + +// Next moves the iterator to the next file and returns a pointer to it. If +// there are no more files, it returns io.EOF. +func (iter *FileIter) Next() (*File, error) { + for { + name, entry, err := iter.w.Next() + if err != nil { + return nil, err + } + + if entry.Mode == filemode.Dir || entry.Mode == filemode.Submodule { + continue + } + + blob, err := GetBlob(iter.s, entry.Hash) + if err != nil { + return nil, err + } + + return NewFile(name, entry.Mode, blob), nil + } +} + +// ForEach call the cb function for each file contained in this iter until +// an error happens or the end of the iter is reached. If plumbing.ErrStop is sent +// the iteration is stop but no error is returned. The iterator is closed. +func (iter *FileIter) ForEach(cb func(*File) error) error { + defer iter.Close() + + for { + f, err := iter.Next() + if err != nil { + if err == io.EOF { + return nil + } + + return err + } + + if err := cb(f); err != nil { + if err == storer.ErrStop { + return nil + } + + return err + } + } +} + +func (iter *FileIter) Close() { + iter.w.Close() +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/merge_base.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/merge_base.go new file mode 100644 index 0000000000..6f2568dbc8 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/merge_base.go @@ -0,0 +1,210 @@ +package object + +import ( + "fmt" + "sort" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/storer" +) + +// errIsReachable is thrown when first commit is an ancestor of the second +var errIsReachable = fmt.Errorf("first is reachable from second") + +// MergeBase mimics the behavior of `git merge-base actual other`, returning the +// best common ancestor between the actual and the passed one. +// The best common ancestors can not be reached from other common ancestors. +func (c *Commit) MergeBase(other *Commit) ([]*Commit, error) { + // use sortedByCommitDateDesc strategy + sorted := sortByCommitDateDesc(c, other) + newer := sorted[0] + older := sorted[1] + + newerHistory, err := ancestorsIndex(older, newer) + if err == errIsReachable { + return []*Commit{older}, nil + } + + if err != nil { + return nil, err + } + + var res []*Commit + inNewerHistory := isInIndexCommitFilter(newerHistory) + resIter := NewFilterCommitIter(older, &inNewerHistory, &inNewerHistory) + _ = resIter.ForEach(func(commit *Commit) error { + res = append(res, commit) + return nil + }) + + return Independents(res) +} + +// IsAncestor returns true if the actual commit is ancestor of the passed one. +// It returns an error if the history is not transversable +// It mimics the behavior of `git merge --is-ancestor actual other` +func (c *Commit) IsAncestor(other *Commit) (bool, error) { + found := false + iter := NewCommitPreorderIter(other, nil, nil) + err := iter.ForEach(func(comm *Commit) error { + if comm.Hash != c.Hash { + return nil + } + + found = true + return storer.ErrStop + }) + + return found, err +} + +// ancestorsIndex returns a map with the ancestors of the starting commit if the +// excluded one is not one of them. It returns errIsReachable if the excluded commit +// is ancestor of the starting, or another error if the history is not traversable. +func ancestorsIndex(excluded, starting *Commit) (map[plumbing.Hash]struct{}, error) { + if excluded.Hash.String() == starting.Hash.String() { + return nil, errIsReachable + } + + startingHistory := map[plumbing.Hash]struct{}{} + startingIter := NewCommitIterBSF(starting, nil, nil) + err := startingIter.ForEach(func(commit *Commit) error { + if commit.Hash == excluded.Hash { + return errIsReachable + } + + startingHistory[commit.Hash] = struct{}{} + return nil + }) + + if err != nil { + return nil, err + } + + return startingHistory, nil +} + +// Independents returns a subset of the passed commits, that are not reachable the others +// It mimics the behavior of `git merge-base --independent commit...`. +func Independents(commits []*Commit) ([]*Commit, error) { + // use sortedByCommitDateDesc strategy + candidates := sortByCommitDateDesc(commits...) + candidates = removeDuplicated(candidates) + + seen := map[plumbing.Hash]struct{}{} + var isLimit CommitFilter = func(commit *Commit) bool { + _, ok := seen[commit.Hash] + return ok + } + + if len(candidates) < 2 { + return candidates, nil + } + + pos := 0 + for { + from := candidates[pos] + others := remove(candidates, from) + fromHistoryIter := NewFilterCommitIter(from, nil, &isLimit) + err := fromHistoryIter.ForEach(func(fromAncestor *Commit) error { + for _, other := range others { + if fromAncestor.Hash == other.Hash { + candidates = remove(candidates, other) + others = remove(others, other) + } + } + + if len(candidates) == 1 { + return storer.ErrStop + } + + seen[fromAncestor.Hash] = struct{}{} + return nil + }) + + if err != nil { + return nil, err + } + + nextPos := indexOf(candidates, from) + 1 + if nextPos >= len(candidates) { + break + } + + pos = nextPos + } + + return candidates, nil +} + +// sortByCommitDateDesc returns the passed commits, sorted by `committer.When desc` +// +// Following this strategy, it is tried to reduce the time needed when walking +// the history from one commit to reach the others. It is assumed that ancestors +// use to be committed before its descendant; +// That way `Independents(A^, A)` will be processed as being `Independents(A, A^)`; +// so starting by `A` it will be reached `A^` way sooner than walking from `A^` +// to the initial commit, and then from `A` to `A^`. +func sortByCommitDateDesc(commits ...*Commit) []*Commit { + sorted := make([]*Commit, len(commits)) + copy(sorted, commits) + sort.Slice(sorted, func(i, j int) bool { + return sorted[i].Committer.When.After(sorted[j].Committer.When) + }) + + return sorted +} + +// indexOf returns the first position where target was found in the passed commits +func indexOf(commits []*Commit, target *Commit) int { + for i, commit := range commits { + if target.Hash == commit.Hash { + return i + } + } + + return -1 +} + +// remove returns the passed commits excluding the commit toDelete +func remove(commits []*Commit, toDelete *Commit) []*Commit { + res := make([]*Commit, len(commits)) + j := 0 + for _, commit := range commits { + if commit.Hash == toDelete.Hash { + continue + } + + res[j] = commit + j++ + } + + return res[:j] +} + +// removeDuplicated removes duplicated commits from the passed slice of commits +func removeDuplicated(commits []*Commit) []*Commit { + seen := make(map[plumbing.Hash]struct{}, len(commits)) + res := make([]*Commit, len(commits)) + j := 0 + for _, commit := range commits { + if _, ok := seen[commit.Hash]; ok { + continue + } + + seen[commit.Hash] = struct{}{} + res[j] = commit + j++ + } + + return res[:j] +} + +// isInIndexCommitFilter returns a commitFilter that returns true +// if the commit is in the passed index. +func isInIndexCommitFilter(index map[plumbing.Hash]struct{}) CommitFilter { + return func(c *Commit) bool { + _, ok := index[c.Hash] + return ok + } +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/object.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/object.go new file mode 100644 index 0000000000..e960e50c94 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/object.go @@ -0,0 +1,237 @@ +// Package object contains implementations of all Git objects and utility +// functions to work with them. +package object + +import ( + "bytes" + "errors" + "fmt" + "io" + "strconv" + "time" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/storer" +) + +// ErrUnsupportedObject trigger when a non-supported object is being decoded. +var ErrUnsupportedObject = errors.New("unsupported object type") + +// Object is a generic representation of any git object. It is implemented by +// Commit, Tree, Blob, and Tag, and includes the functions that are common to +// them. +// +// Object is returned when an object can be of any type. It is frequently used +// with a type cast to acquire the specific type of object: +// +// func process(obj Object) { +// switch o := obj.(type) { +// case *Commit: +// // o is a Commit +// case *Tree: +// // o is a Tree +// case *Blob: +// // o is a Blob +// case *Tag: +// // o is a Tag +// } +// } +// +// This interface is intentionally different from plumbing.EncodedObject, which +// is a lower level interface used by storage implementations to read and write +// objects in its encoded form. +type Object interface { + ID() plumbing.Hash + Type() plumbing.ObjectType + Decode(plumbing.EncodedObject) error + Encode(plumbing.EncodedObject) error +} + +// GetObject gets an object from an object storer and decodes it. +func GetObject(s storer.EncodedObjectStorer, h plumbing.Hash) (Object, error) { + o, err := s.EncodedObject(plumbing.AnyObject, h) + if err != nil { + return nil, err + } + + return DecodeObject(s, o) +} + +// DecodeObject decodes an encoded object into an Object and associates it to +// the given object storer. +func DecodeObject(s storer.EncodedObjectStorer, o plumbing.EncodedObject) (Object, error) { + switch o.Type() { + case plumbing.CommitObject: + return DecodeCommit(s, o) + case plumbing.TreeObject: + return DecodeTree(s, o) + case plumbing.BlobObject: + return DecodeBlob(o) + case plumbing.TagObject: + return DecodeTag(s, o) + default: + return nil, plumbing.ErrInvalidType + } +} + +// DateFormat is the format being used in the original git implementation +const DateFormat = "Mon Jan 02 15:04:05 2006 -0700" + +// Signature is used to identify who and when created a commit or tag. +type Signature struct { + // Name represents a person name. It is an arbitrary string. + Name string + // Email is an email, but it cannot be assumed to be well-formed. + Email string + // When is the timestamp of the signature. + When time.Time +} + +// Decode decodes a byte slice into a signature +func (s *Signature) Decode(b []byte) { + open := bytes.LastIndexByte(b, '<') + close := bytes.LastIndexByte(b, '>') + if open == -1 || close == -1 { + return + } + + if close < open { + return + } + + s.Name = string(bytes.Trim(b[:open], " ")) + s.Email = string(b[open+1 : close]) + + hasTime := close+2 < len(b) + if hasTime { + s.decodeTimeAndTimeZone(b[close+2:]) + } +} + +// Encode encodes a Signature into a writer. +func (s *Signature) Encode(w io.Writer) error { + if _, err := fmt.Fprintf(w, "%s <%s> ", s.Name, s.Email); err != nil { + return err + } + if err := s.encodeTimeAndTimeZone(w); err != nil { + return err + } + return nil +} + +var timeZoneLength = 5 + +func (s *Signature) decodeTimeAndTimeZone(b []byte) { + space := bytes.IndexByte(b, ' ') + if space == -1 { + space = len(b) + } + + ts, err := strconv.ParseInt(string(b[:space]), 10, 64) + if err != nil { + return + } + + s.When = time.Unix(ts, 0).In(time.UTC) + var tzStart = space + 1 + if tzStart >= len(b) || tzStart+timeZoneLength > len(b) { + return + } + + // Include a dummy year in this time.Parse() call to avoid a bug in Go: + // https://github.com/golang/go/issues/19750 + // + // Parsing the timezone with no other details causes the tl.Location() call + // below to return time.Local instead of the parsed zone in some cases + tl, err := time.Parse("2006 -0700", "1970 "+string(b[tzStart:tzStart+timeZoneLength])) + if err != nil { + return + } + + s.When = s.When.In(tl.Location()) +} + +func (s *Signature) encodeTimeAndTimeZone(w io.Writer) error { + u := s.When.Unix() + if u < 0 { + u = 0 + } + _, err := fmt.Fprintf(w, "%d %s", u, s.When.Format("-0700")) + return err +} + +func (s *Signature) String() string { + return fmt.Sprintf("%s <%s>", s.Name, s.Email) +} + +// ObjectIter provides an iterator for a set of objects. +type ObjectIter struct { + storer.EncodedObjectIter + s storer.EncodedObjectStorer +} + +// NewObjectIter takes a storer.EncodedObjectStorer and a +// storer.EncodedObjectIter and returns an *ObjectIter that iterates over all +// objects contained in the storer.EncodedObjectIter. +func NewObjectIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) *ObjectIter { + return &ObjectIter{iter, s} +} + +// Next moves the iterator to the next object and returns a pointer to it. If +// there are no more objects, it returns io.EOF. +func (iter *ObjectIter) Next() (Object, error) { + for { + obj, err := iter.EncodedObjectIter.Next() + if err != nil { + return nil, err + } + + o, err := iter.toObject(obj) + if err == plumbing.ErrInvalidType { + continue + } + + if err != nil { + return nil, err + } + + return o, nil + } +} + +// ForEach call the cb function for each object contained on this iter until +// an error happens or the end of the iter is reached. If ErrStop is sent +// the iteration is stop but no error is returned. The iterator is closed. +func (iter *ObjectIter) ForEach(cb func(Object) error) error { + return iter.EncodedObjectIter.ForEach(func(obj plumbing.EncodedObject) error { + o, err := iter.toObject(obj) + if err == plumbing.ErrInvalidType { + return nil + } + + if err != nil { + return err + } + + return cb(o) + }) +} + +func (iter *ObjectIter) toObject(obj plumbing.EncodedObject) (Object, error) { + switch obj.Type() { + case plumbing.BlobObject: + blob := &Blob{} + return blob, blob.Decode(obj) + case plumbing.TreeObject: + tree := &Tree{s: iter.s} + return tree, tree.Decode(obj) + case plumbing.CommitObject: + commit := &Commit{} + return commit, commit.Decode(obj) + case plumbing.TagObject: + tag := &Tag{} + return tag, tag.Decode(obj) + default: + return nil, plumbing.ErrInvalidType + } +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/patch.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/patch.go new file mode 100644 index 0000000000..32454ac48d --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/patch.go @@ -0,0 +1,346 @@ +package object + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "math" + "strings" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/filemode" + fdiff "gopkg.in/src-d/go-git.v4/plumbing/format/diff" + "gopkg.in/src-d/go-git.v4/utils/diff" + + dmp "github.com/sergi/go-diff/diffmatchpatch" +) + +var ( + ErrCanceled = errors.New("operation canceled") +) + +func getPatch(message string, changes ...*Change) (*Patch, error) { + ctx := context.Background() + return getPatchContext(ctx, message, changes...) +} + +func getPatchContext(ctx context.Context, message string, changes ...*Change) (*Patch, error) { + var filePatches []fdiff.FilePatch + for _, c := range changes { + select { + case <-ctx.Done(): + return nil, ErrCanceled + default: + } + + fp, err := filePatchWithContext(ctx, c) + if err != nil { + return nil, err + } + + filePatches = append(filePatches, fp) + } + + return &Patch{message, filePatches}, nil +} + +func filePatchWithContext(ctx context.Context, c *Change) (fdiff.FilePatch, error) { + from, to, err := c.Files() + if err != nil { + return nil, err + } + fromContent, fIsBinary, err := fileContent(from) + if err != nil { + return nil, err + } + + toContent, tIsBinary, err := fileContent(to) + if err != nil { + return nil, err + } + + if fIsBinary || tIsBinary { + return &textFilePatch{from: c.From, to: c.To}, nil + } + + diffs := diff.Do(fromContent, toContent) + + var chunks []fdiff.Chunk + for _, d := range diffs { + select { + case <-ctx.Done(): + return nil, ErrCanceled + default: + } + + var op fdiff.Operation + switch d.Type { + case dmp.DiffEqual: + op = fdiff.Equal + case dmp.DiffDelete: + op = fdiff.Delete + case dmp.DiffInsert: + op = fdiff.Add + } + + chunks = append(chunks, &textChunk{d.Text, op}) + } + + return &textFilePatch{ + chunks: chunks, + from: c.From, + to: c.To, + }, nil + +} + +func filePatch(c *Change) (fdiff.FilePatch, error) { + return filePatchWithContext(context.Background(), c) +} + +func fileContent(f *File) (content string, isBinary bool, err error) { + if f == nil { + return + } + + isBinary, err = f.IsBinary() + if err != nil || isBinary { + return + } + + content, err = f.Contents() + + return +} + +// textPatch is an implementation of fdiff.Patch interface +type Patch struct { + message string + filePatches []fdiff.FilePatch +} + +func (t *Patch) FilePatches() []fdiff.FilePatch { + return t.filePatches +} + +func (t *Patch) Message() string { + return t.message +} + +func (p *Patch) Encode(w io.Writer) error { + ue := fdiff.NewUnifiedEncoder(w, fdiff.DefaultContextLines) + + return ue.Encode(p) +} + +func (p *Patch) Stats() FileStats { + return getFileStatsFromFilePatches(p.FilePatches()) +} + +func (p *Patch) String() string { + buf := bytes.NewBuffer(nil) + err := p.Encode(buf) + if err != nil { + return fmt.Sprintf("malformed patch: %s", err.Error()) + } + + return buf.String() +} + +// changeEntryWrapper is an implementation of fdiff.File interface +type changeEntryWrapper struct { + ce ChangeEntry +} + +func (f *changeEntryWrapper) Hash() plumbing.Hash { + if !f.ce.TreeEntry.Mode.IsFile() { + return plumbing.ZeroHash + } + + return f.ce.TreeEntry.Hash +} + +func (f *changeEntryWrapper) Mode() filemode.FileMode { + return f.ce.TreeEntry.Mode +} +func (f *changeEntryWrapper) Path() string { + if !f.ce.TreeEntry.Mode.IsFile() { + return "" + } + + return f.ce.Name +} + +func (f *changeEntryWrapper) Empty() bool { + return !f.ce.TreeEntry.Mode.IsFile() +} + +// textFilePatch is an implementation of fdiff.FilePatch interface +type textFilePatch struct { + chunks []fdiff.Chunk + from, to ChangeEntry +} + +func (tf *textFilePatch) Files() (from fdiff.File, to fdiff.File) { + f := &changeEntryWrapper{tf.from} + t := &changeEntryWrapper{tf.to} + + if !f.Empty() { + from = f + } + + if !t.Empty() { + to = t + } + + return +} + +func (t *textFilePatch) IsBinary() bool { + return len(t.chunks) == 0 +} + +func (t *textFilePatch) Chunks() []fdiff.Chunk { + return t.chunks +} + +// textChunk is an implementation of fdiff.Chunk interface +type textChunk struct { + content string + op fdiff.Operation +} + +func (t *textChunk) Content() string { + return t.content +} + +func (t *textChunk) Type() fdiff.Operation { + return t.op +} + +// FileStat stores the status of changes in content of a file. +type FileStat struct { + Name string + Addition int + Deletion int +} + +func (fs FileStat) String() string { + return printStat([]FileStat{fs}) +} + +// FileStats is a collection of FileStat. +type FileStats []FileStat + +func (fileStats FileStats) String() string { + return printStat(fileStats) +} + +func printStat(fileStats []FileStat) string { + padLength := float64(len(" ")) + newlineLength := float64(len("\n")) + separatorLength := float64(len("|")) + // Soft line length limit. The text length calculation below excludes + // length of the change number. Adding that would take it closer to 80, + // but probably not more than 80, until it's a huge number. + lineLength := 72.0 + + // Get the longest filename and longest total change. + var longestLength float64 + var longestTotalChange float64 + for _, fs := range fileStats { + if int(longestLength) < len(fs.Name) { + longestLength = float64(len(fs.Name)) + } + totalChange := fs.Addition + fs.Deletion + if int(longestTotalChange) < totalChange { + longestTotalChange = float64(totalChange) + } + } + + // Parts of the output: + // |<+++/---> + // example: " main.go | 10 +++++++--- " + + // + leftTextLength := padLength + longestLength + padLength + + // <+++++/-----> + // Excluding number length here. + rightTextLength := padLength + padLength + newlineLength + + totalTextArea := leftTextLength + separatorLength + rightTextLength + heightOfHistogram := lineLength - totalTextArea + + // Scale the histogram. + var scaleFactor float64 + if longestTotalChange > heightOfHistogram { + // Scale down to heightOfHistogram. + scaleFactor = longestTotalChange / heightOfHistogram + } else { + scaleFactor = 1.0 + } + + finalOutput := "" + for _, fs := range fileStats { + addn := float64(fs.Addition) + deln := float64(fs.Deletion) + adds := strings.Repeat("+", int(math.Floor(addn/scaleFactor))) + dels := strings.Repeat("-", int(math.Floor(deln/scaleFactor))) + finalOutput += fmt.Sprintf(" %s | %d %s%s\n", fs.Name, (fs.Addition + fs.Deletion), adds, dels) + } + + return finalOutput +} + +func getFileStatsFromFilePatches(filePatches []fdiff.FilePatch) FileStats { + var fileStats FileStats + + for _, fp := range filePatches { + // ignore empty patches (binary files, submodule refs updates) + if len(fp.Chunks()) == 0 { + continue + } + + cs := FileStat{} + from, to := fp.Files() + if from == nil { + // New File is created. + cs.Name = to.Path() + } else if to == nil { + // File is deleted. + cs.Name = from.Path() + } else if from.Path() != to.Path() { + // File is renamed. Not supported. + // cs.Name = fmt.Sprintf("%s => %s", from.Path(), to.Path()) + } else { + cs.Name = from.Path() + } + + for _, chunk := range fp.Chunks() { + s := chunk.Content() + if len(s) == 0 { + continue + } + + switch chunk.Type() { + case fdiff.Add: + cs.Addition += strings.Count(s, "\n") + if s[len(s)-1] != '\n' { + cs.Addition++ + } + case fdiff.Delete: + cs.Deletion += strings.Count(s, "\n") + if s[len(s)-1] != '\n' { + cs.Deletion++ + } + } + } + + fileStats = append(fileStats, cs) + } + + return fileStats +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/tag.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/tag.go new file mode 100644 index 0000000000..9ee550925d --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/tag.go @@ -0,0 +1,357 @@ +package object + +import ( + "bufio" + "bytes" + "fmt" + "io" + stdioutil "io/ioutil" + "strings" + + "golang.org/x/crypto/openpgp" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/storer" + "gopkg.in/src-d/go-git.v4/utils/ioutil" +) + +// Tag represents an annotated tag object. It points to a single git object of +// any type, but tags typically are applied to commit or blob objects. It +// provides a reference that associates the target with a tag name. It also +// contains meta-information about the tag, including the tagger, tag date and +// message. +// +// Note that this is not used for lightweight tags. +// +// https://git-scm.com/book/en/v2/Git-Internals-Git-References#Tags +type Tag struct { + // Hash of the tag. + Hash plumbing.Hash + // Name of the tag. + Name string + // Tagger is the one who created the tag. + Tagger Signature + // Message is an arbitrary text message. + Message string + // PGPSignature is the PGP signature of the tag. + PGPSignature string + // TargetType is the object type of the target. + TargetType plumbing.ObjectType + // Target is the hash of the target object. + Target plumbing.Hash + + s storer.EncodedObjectStorer +} + +// GetTag gets a tag from an object storer and decodes it. +func GetTag(s storer.EncodedObjectStorer, h plumbing.Hash) (*Tag, error) { + o, err := s.EncodedObject(plumbing.TagObject, h) + if err != nil { + return nil, err + } + + return DecodeTag(s, o) +} + +// DecodeTag decodes an encoded object into a *Commit and associates it to the +// given object storer. +func DecodeTag(s storer.EncodedObjectStorer, o plumbing.EncodedObject) (*Tag, error) { + t := &Tag{s: s} + if err := t.Decode(o); err != nil { + return nil, err + } + + return t, nil +} + +// ID returns the object ID of the tag, not the object that the tag references. +// The returned value will always match the current value of Tag.Hash. +// +// ID is present to fulfill the Object interface. +func (t *Tag) ID() plumbing.Hash { + return t.Hash +} + +// Type returns the type of object. It always returns plumbing.TagObject. +// +// Type is present to fulfill the Object interface. +func (t *Tag) Type() plumbing.ObjectType { + return plumbing.TagObject +} + +// Decode transforms a plumbing.EncodedObject into a Tag struct. +func (t *Tag) Decode(o plumbing.EncodedObject) (err error) { + if o.Type() != plumbing.TagObject { + return ErrUnsupportedObject + } + + t.Hash = o.Hash() + + reader, err := o.Reader() + if err != nil { + return err + } + defer ioutil.CheckClose(reader, &err) + + r := bufPool.Get().(*bufio.Reader) + defer bufPool.Put(r) + r.Reset(reader) + for { + var line []byte + line, err = r.ReadBytes('\n') + if err != nil && err != io.EOF { + return err + } + + line = bytes.TrimSpace(line) + if len(line) == 0 { + break // Start of message + } + + split := bytes.SplitN(line, []byte{' '}, 2) + switch string(split[0]) { + case "object": + t.Target = plumbing.NewHash(string(split[1])) + case "type": + t.TargetType, err = plumbing.ParseObjectType(string(split[1])) + if err != nil { + return err + } + case "tag": + t.Name = string(split[1]) + case "tagger": + t.Tagger.Decode(split[1]) + } + + if err == io.EOF { + return nil + } + } + + data, err := stdioutil.ReadAll(r) + if err != nil { + return err + } + + var pgpsig bool + // Check if data contains PGP signature. + if bytes.Contains(data, []byte(beginpgp)) { + // Split the lines at newline. + messageAndSig := bytes.Split(data, []byte("\n")) + + for _, l := range messageAndSig { + if pgpsig { + if bytes.Contains(l, []byte(endpgp)) { + t.PGPSignature += endpgp + "\n" + break + } else { + t.PGPSignature += string(l) + "\n" + } + continue + } + + // Check if it's the beginning of a PGP signature. + if bytes.Contains(l, []byte(beginpgp)) { + t.PGPSignature += beginpgp + "\n" + pgpsig = true + continue + } + + t.Message += string(l) + "\n" + } + } else { + t.Message = string(data) + } + + return nil +} + +// Encode transforms a Tag into a plumbing.EncodedObject. +func (t *Tag) Encode(o plumbing.EncodedObject) error { + return t.encode(o, true) +} + +// EncodeWithoutSignature export a Tag into a plumbing.EncodedObject without the signature (correspond to the payload of the PGP signature). +func (t *Tag) EncodeWithoutSignature(o plumbing.EncodedObject) error { + return t.encode(o, false) +} + +func (t *Tag) encode(o plumbing.EncodedObject, includeSig bool) (err error) { + o.SetType(plumbing.TagObject) + w, err := o.Writer() + if err != nil { + return err + } + defer ioutil.CheckClose(w, &err) + + if _, err = fmt.Fprintf(w, + "object %s\ntype %s\ntag %s\ntagger ", + t.Target.String(), t.TargetType.Bytes(), t.Name); err != nil { + return err + } + + if err = t.Tagger.Encode(w); err != nil { + return err + } + + if _, err = fmt.Fprint(w, "\n\n"); err != nil { + return err + } + + if _, err = fmt.Fprint(w, t.Message); err != nil { + return err + } + + // Note that this is highly sensitive to what it sent along in the message. + // Message *always* needs to end with a newline, or else the message and the + // signature will be concatenated into a corrupt object. Since this is a + // lower-level method, we assume you know what you are doing and have already + // done the needful on the message in the caller. + if includeSig { + if _, err = fmt.Fprint(w, t.PGPSignature); err != nil { + return err + } + } + + return err +} + +// Commit returns the commit pointed to by the tag. If the tag points to a +// different type of object ErrUnsupportedObject will be returned. +func (t *Tag) Commit() (*Commit, error) { + if t.TargetType != plumbing.CommitObject { + return nil, ErrUnsupportedObject + } + + o, err := t.s.EncodedObject(plumbing.CommitObject, t.Target) + if err != nil { + return nil, err + } + + return DecodeCommit(t.s, o) +} + +// Tree returns the tree pointed to by the tag. If the tag points to a commit +// object the tree of that commit will be returned. If the tag does not point +// to a commit or tree object ErrUnsupportedObject will be returned. +func (t *Tag) Tree() (*Tree, error) { + switch t.TargetType { + case plumbing.CommitObject: + c, err := t.Commit() + if err != nil { + return nil, err + } + + return c.Tree() + case plumbing.TreeObject: + return GetTree(t.s, t.Target) + default: + return nil, ErrUnsupportedObject + } +} + +// Blob returns the blob pointed to by the tag. If the tag points to a +// different type of object ErrUnsupportedObject will be returned. +func (t *Tag) Blob() (*Blob, error) { + if t.TargetType != plumbing.BlobObject { + return nil, ErrUnsupportedObject + } + + return GetBlob(t.s, t.Target) +} + +// Object returns the object pointed to by the tag. +func (t *Tag) Object() (Object, error) { + o, err := t.s.EncodedObject(t.TargetType, t.Target) + if err != nil { + return nil, err + } + + return DecodeObject(t.s, o) +} + +// String returns the meta information contained in the tag as a formatted +// string. +func (t *Tag) String() string { + obj, _ := t.Object() + + return fmt.Sprintf( + "%s %s\nTagger: %s\nDate: %s\n\n%s\n%s", + plumbing.TagObject, t.Name, t.Tagger.String(), t.Tagger.When.Format(DateFormat), + t.Message, objectAsString(obj), + ) +} + +// Verify performs PGP verification of the tag with a provided armored +// keyring and returns openpgp.Entity associated with verifying key on success. +func (t *Tag) Verify(armoredKeyRing string) (*openpgp.Entity, error) { + keyRingReader := strings.NewReader(armoredKeyRing) + keyring, err := openpgp.ReadArmoredKeyRing(keyRingReader) + if err != nil { + return nil, err + } + + // Extract signature. + signature := strings.NewReader(t.PGPSignature) + + encoded := &plumbing.MemoryObject{} + // Encode tag components, excluding signature and get a reader object. + if err := t.EncodeWithoutSignature(encoded); err != nil { + return nil, err + } + er, err := encoded.Reader() + if err != nil { + return nil, err + } + + return openpgp.CheckArmoredDetachedSignature(keyring, er, signature) +} + +// TagIter provides an iterator for a set of tags. +type TagIter struct { + storer.EncodedObjectIter + s storer.EncodedObjectStorer +} + +// NewTagIter takes a storer.EncodedObjectStorer and a +// storer.EncodedObjectIter and returns a *TagIter that iterates over all +// tags contained in the storer.EncodedObjectIter. +// +// Any non-tag object returned by the storer.EncodedObjectIter is skipped. +func NewTagIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) *TagIter { + return &TagIter{iter, s} +} + +// Next moves the iterator to the next tag and returns a pointer to it. If +// there are no more tags, it returns io.EOF. +func (iter *TagIter) Next() (*Tag, error) { + obj, err := iter.EncodedObjectIter.Next() + if err != nil { + return nil, err + } + + return DecodeTag(iter.s, obj) +} + +// ForEach call the cb function for each tag contained on this iter until +// an error happens or the end of the iter is reached. If ErrStop is sent +// the iteration is stop but no error is returned. The iterator is closed. +func (iter *TagIter) ForEach(cb func(*Tag) error) error { + return iter.EncodedObjectIter.ForEach(func(obj plumbing.EncodedObject) error { + t, err := DecodeTag(iter.s, obj) + if err != nil { + return err + } + + return cb(t) + }) +} + +func objectAsString(obj Object) string { + switch o := obj.(type) { + case *Commit: + return o.String() + default: + return "" + } +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/tree.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/tree.go new file mode 100644 index 0000000000..d0b4fff15c --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/tree.go @@ -0,0 +1,520 @@ +package object + +import ( + "bufio" + "context" + "errors" + "fmt" + "io" + "path" + "path/filepath" + "strings" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/filemode" + "gopkg.in/src-d/go-git.v4/plumbing/storer" + "gopkg.in/src-d/go-git.v4/utils/ioutil" +) + +const ( + maxTreeDepth = 1024 + startingStackSize = 8 +) + +// New errors defined by this package. +var ( + ErrMaxTreeDepth = errors.New("maximum tree depth exceeded") + ErrFileNotFound = errors.New("file not found") + ErrDirectoryNotFound = errors.New("directory not found") + ErrEntryNotFound = errors.New("entry not found") +) + +// Tree is basically like a directory - it references a bunch of other trees +// and/or blobs (i.e. files and sub-directories) +type Tree struct { + Entries []TreeEntry + Hash plumbing.Hash + + s storer.EncodedObjectStorer + m map[string]*TreeEntry + t map[string]*Tree // tree path cache +} + +// GetTree gets a tree from an object storer and decodes it. +func GetTree(s storer.EncodedObjectStorer, h plumbing.Hash) (*Tree, error) { + o, err := s.EncodedObject(plumbing.TreeObject, h) + if err != nil { + return nil, err + } + + return DecodeTree(s, o) +} + +// DecodeTree decodes an encoded object into a *Tree and associates it to the +// given object storer. +func DecodeTree(s storer.EncodedObjectStorer, o plumbing.EncodedObject) (*Tree, error) { + t := &Tree{s: s} + if err := t.Decode(o); err != nil { + return nil, err + } + + return t, nil +} + +// TreeEntry represents a file +type TreeEntry struct { + Name string + Mode filemode.FileMode + Hash plumbing.Hash +} + +// File returns the hash of the file identified by the `path` argument. +// The path is interpreted as relative to the tree receiver. +func (t *Tree) File(path string) (*File, error) { + e, err := t.FindEntry(path) + if err != nil { + return nil, ErrFileNotFound + } + + blob, err := GetBlob(t.s, e.Hash) + if err != nil { + if err == plumbing.ErrObjectNotFound { + return nil, ErrFileNotFound + } + return nil, err + } + + return NewFile(path, e.Mode, blob), nil +} + +// Size returns the plaintext size of an object, without reading it +// into memory. +func (t *Tree) Size(path string) (int64, error) { + e, err := t.FindEntry(path) + if err != nil { + return 0, ErrEntryNotFound + } + + return t.s.EncodedObjectSize(e.Hash) +} + +// Tree returns the tree identified by the `path` argument. +// The path is interpreted as relative to the tree receiver. +func (t *Tree) Tree(path string) (*Tree, error) { + e, err := t.FindEntry(path) + if err != nil { + return nil, ErrDirectoryNotFound + } + + tree, err := GetTree(t.s, e.Hash) + if err == plumbing.ErrObjectNotFound { + return nil, ErrDirectoryNotFound + } + + return tree, err +} + +// TreeEntryFile returns the *File for a given *TreeEntry. +func (t *Tree) TreeEntryFile(e *TreeEntry) (*File, error) { + blob, err := GetBlob(t.s, e.Hash) + if err != nil { + return nil, err + } + + return NewFile(e.Name, e.Mode, blob), nil +} + +// FindEntry search a TreeEntry in this tree or any subtree. +func (t *Tree) FindEntry(path string) (*TreeEntry, error) { + if t.t == nil { + t.t = make(map[string]*Tree) + } + + pathParts := strings.Split(path, "/") + startingTree := t + pathCurrent := "" + + // search for the longest path in the tree path cache + for i := len(pathParts) - 1; i > 1; i-- { + path := filepath.Join(pathParts[:i]...) + + tree, ok := t.t[path] + if ok { + startingTree = tree + pathParts = pathParts[i:] + pathCurrent = path + + break + } + } + + var tree *Tree + var err error + for tree = startingTree; len(pathParts) > 1; pathParts = pathParts[1:] { + if tree, err = tree.dir(pathParts[0]); err != nil { + return nil, err + } + + pathCurrent = filepath.Join(pathCurrent, pathParts[0]) + t.t[pathCurrent] = tree + } + + return tree.entry(pathParts[0]) +} + +func (t *Tree) dir(baseName string) (*Tree, error) { + entry, err := t.entry(baseName) + if err != nil { + return nil, ErrDirectoryNotFound + } + + obj, err := t.s.EncodedObject(plumbing.TreeObject, entry.Hash) + if err != nil { + return nil, err + } + + tree := &Tree{s: t.s} + err = tree.Decode(obj) + + return tree, err +} + +func (t *Tree) entry(baseName string) (*TreeEntry, error) { + if t.m == nil { + t.buildMap() + } + + entry, ok := t.m[baseName] + if !ok { + return nil, ErrEntryNotFound + } + + return entry, nil +} + +// Files returns a FileIter allowing to iterate over the Tree +func (t *Tree) Files() *FileIter { + return NewFileIter(t.s, t) +} + +// ID returns the object ID of the tree. The returned value will always match +// the current value of Tree.Hash. +// +// ID is present to fulfill the Object interface. +func (t *Tree) ID() plumbing.Hash { + return t.Hash +} + +// Type returns the type of object. It always returns plumbing.TreeObject. +func (t *Tree) Type() plumbing.ObjectType { + return plumbing.TreeObject +} + +// Decode transform an plumbing.EncodedObject into a Tree struct +func (t *Tree) Decode(o plumbing.EncodedObject) (err error) { + if o.Type() != plumbing.TreeObject { + return ErrUnsupportedObject + } + + t.Hash = o.Hash() + if o.Size() == 0 { + return nil + } + + t.Entries = nil + t.m = nil + + reader, err := o.Reader() + if err != nil { + return err + } + defer ioutil.CheckClose(reader, &err) + + r := bufPool.Get().(*bufio.Reader) + defer bufPool.Put(r) + r.Reset(reader) + for { + str, err := r.ReadString(' ') + if err != nil { + if err == io.EOF { + break + } + + return err + } + str = str[:len(str)-1] // strip last byte (' ') + + mode, err := filemode.New(str) + if err != nil { + return err + } + + name, err := r.ReadString(0) + if err != nil && err != io.EOF { + return err + } + + var hash plumbing.Hash + if _, err = io.ReadFull(r, hash[:]); err != nil { + return err + } + + baseName := name[:len(name)-1] + t.Entries = append(t.Entries, TreeEntry{ + Hash: hash, + Mode: mode, + Name: baseName, + }) + } + + return nil +} + +// Encode transforms a Tree into a plumbing.EncodedObject. +func (t *Tree) Encode(o plumbing.EncodedObject) (err error) { + o.SetType(plumbing.TreeObject) + w, err := o.Writer() + if err != nil { + return err + } + + defer ioutil.CheckClose(w, &err) + for _, entry := range t.Entries { + if _, err = fmt.Fprintf(w, "%o %s", entry.Mode, entry.Name); err != nil { + return err + } + + if _, err = w.Write([]byte{0x00}); err != nil { + return err + } + + if _, err = w.Write(entry.Hash[:]); err != nil { + return err + } + } + + return err +} + +func (t *Tree) buildMap() { + t.m = make(map[string]*TreeEntry) + for i := 0; i < len(t.Entries); i++ { + t.m[t.Entries[i].Name] = &t.Entries[i] + } +} + +// Diff returns a list of changes between this tree and the provided one +func (from *Tree) Diff(to *Tree) (Changes, error) { + return DiffTree(from, to) +} + +// Diff returns a list of changes between this tree and the provided one +// Error will be returned if context expires +// Provided context must be non nil +func (from *Tree) DiffContext(ctx context.Context, to *Tree) (Changes, error) { + return DiffTreeContext(ctx, from, to) +} + +// Patch returns a slice of Patch objects with all the changes between trees +// in chunks. This representation can be used to create several diff outputs. +func (from *Tree) Patch(to *Tree) (*Patch, error) { + return from.PatchContext(context.Background(), to) +} + +// Patch returns a slice of Patch objects with all the changes between trees +// in chunks. This representation can be used to create several diff outputs. +// If context expires, an error will be returned +// Provided context must be non-nil +func (from *Tree) PatchContext(ctx context.Context, to *Tree) (*Patch, error) { + changes, err := DiffTreeContext(ctx, from, to) + if err != nil { + return nil, err + } + + return changes.PatchContext(ctx) +} + +// treeEntryIter facilitates iterating through the TreeEntry objects in a Tree. +type treeEntryIter struct { + t *Tree + pos int +} + +func (iter *treeEntryIter) Next() (TreeEntry, error) { + if iter.pos >= len(iter.t.Entries) { + return TreeEntry{}, io.EOF + } + iter.pos++ + return iter.t.Entries[iter.pos-1], nil +} + +// TreeWalker provides a means of walking through all of the entries in a Tree. +type TreeWalker struct { + stack []*treeEntryIter + base string + recursive bool + seen map[plumbing.Hash]bool + + s storer.EncodedObjectStorer + t *Tree +} + +// NewTreeWalker returns a new TreeWalker for the given tree. +// +// It is the caller's responsibility to call Close() when finished with the +// tree walker. +func NewTreeWalker(t *Tree, recursive bool, seen map[plumbing.Hash]bool) *TreeWalker { + stack := make([]*treeEntryIter, 0, startingStackSize) + stack = append(stack, &treeEntryIter{t, 0}) + + return &TreeWalker{ + stack: stack, + recursive: recursive, + seen: seen, + + s: t.s, + t: t, + } +} + +// Next returns the next object from the tree. Objects are returned in order +// and subtrees are included. After the last object has been returned further +// calls to Next() will return io.EOF. +// +// In the current implementation any objects which cannot be found in the +// underlying repository will be skipped automatically. It is possible that this +// may change in future versions. +func (w *TreeWalker) Next() (name string, entry TreeEntry, err error) { + var obj *Tree + for { + current := len(w.stack) - 1 + if current < 0 { + // Nothing left on the stack so we're finished + err = io.EOF + return + } + + if current > maxTreeDepth { + // We're probably following bad data or some self-referencing tree + err = ErrMaxTreeDepth + return + } + + entry, err = w.stack[current].Next() + if err == io.EOF { + // Finished with the current tree, move back up to the parent + w.stack = w.stack[:current] + w.base, _ = path.Split(w.base) + w.base = strings.TrimSuffix(w.base, "/") + continue + } + + if err != nil { + return + } + + if w.seen[entry.Hash] { + continue + } + + if entry.Mode == filemode.Dir { + obj, err = GetTree(w.s, entry.Hash) + } + + name = simpleJoin(w.base, entry.Name) + + if err != nil { + err = io.EOF + return + } + + break + } + + if !w.recursive { + return + } + + if obj != nil { + w.stack = append(w.stack, &treeEntryIter{obj, 0}) + w.base = simpleJoin(w.base, entry.Name) + } + + return +} + +// Tree returns the tree that the tree walker most recently operated on. +func (w *TreeWalker) Tree() *Tree { + current := len(w.stack) - 1 + if w.stack[current].pos == 0 { + current-- + } + + if current < 0 { + return nil + } + + return w.stack[current].t +} + +// Close releases any resources used by the TreeWalker. +func (w *TreeWalker) Close() { + w.stack = nil +} + +// TreeIter provides an iterator for a set of trees. +type TreeIter struct { + storer.EncodedObjectIter + s storer.EncodedObjectStorer +} + +// NewTreeIter takes a storer.EncodedObjectStorer and a +// storer.EncodedObjectIter and returns a *TreeIter that iterates over all +// tree contained in the storer.EncodedObjectIter. +// +// Any non-tree object returned by the storer.EncodedObjectIter is skipped. +func NewTreeIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) *TreeIter { + return &TreeIter{iter, s} +} + +// Next moves the iterator to the next tree and returns a pointer to it. If +// there are no more trees, it returns io.EOF. +func (iter *TreeIter) Next() (*Tree, error) { + for { + obj, err := iter.EncodedObjectIter.Next() + if err != nil { + return nil, err + } + + if obj.Type() != plumbing.TreeObject { + continue + } + + return DecodeTree(iter.s, obj) + } +} + +// ForEach call the cb function for each tree contained on this iter until +// an error happens or the end of the iter is reached. If ErrStop is sent +// the iteration is stop but no error is returned. The iterator is closed. +func (iter *TreeIter) ForEach(cb func(*Tree) error) error { + return iter.EncodedObjectIter.ForEach(func(obj plumbing.EncodedObject) error { + if obj.Type() != plumbing.TreeObject { + return nil + } + + t, err := DecodeTree(iter.s, obj) + if err != nil { + return err + } + + return cb(t) + }) +} + +func simpleJoin(parent, child string) string { + if len(parent) > 0 { + return parent + "/" + child + } + return child +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/treenoder.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/treenoder.go new file mode 100644 index 0000000000..52f0e61221 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/object/treenoder.go @@ -0,0 +1,136 @@ +package object + +import ( + "io" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/filemode" + "gopkg.in/src-d/go-git.v4/utils/merkletrie/noder" +) + +// A treenoder is a helper type that wraps git trees into merkletrie +// noders. +// +// As a merkletrie noder doesn't understand the concept of modes (e.g. +// file permissions), the treenoder includes the mode of the git tree in +// the hash, so changes in the modes will be detected as modifications +// to the file contents by the merkletrie difftree algorithm. This is +// consistent with how the "git diff-tree" command works. +type treeNoder struct { + parent *Tree // the root node is its own parent + name string // empty string for the root node + mode filemode.FileMode + hash plumbing.Hash + children []noder.Noder // memoized +} + +// NewTreeRootNode returns the root node of a Tree +func NewTreeRootNode(t *Tree) noder.Noder { + if t == nil { + return &treeNoder{} + } + + return &treeNoder{ + parent: t, + name: "", + mode: filemode.Dir, + hash: t.Hash, + } +} + +func (t *treeNoder) isRoot() bool { + return t.name == "" +} + +func (t *treeNoder) String() string { + return "treeNoder <" + t.name + ">" +} + +func (t *treeNoder) Hash() []byte { + if t.mode == filemode.Deprecated { + return append(t.hash[:], filemode.Regular.Bytes()...) + } + return append(t.hash[:], t.mode.Bytes()...) +} + +func (t *treeNoder) Name() string { + return t.name +} + +func (t *treeNoder) IsDir() bool { + return t.mode == filemode.Dir +} + +// Children will return the children of a treenoder as treenoders, +// building them from the children of the wrapped git tree. +func (t *treeNoder) Children() ([]noder.Noder, error) { + if t.mode != filemode.Dir { + return noder.NoChildren, nil + } + + // children are memoized for efficiency + if t.children != nil { + return t.children, nil + } + + // the parent of the returned children will be ourself as a tree if + // we are a not the root treenoder. The root is special as it + // is is own parent. + parent := t.parent + if !t.isRoot() { + var err error + if parent, err = t.parent.Tree(t.name); err != nil { + return nil, err + } + } + + return transformChildren(parent) +} + +// Returns the children of a tree as treenoders. +// Efficiency is key here. +func transformChildren(t *Tree) ([]noder.Noder, error) { + var err error + var e TreeEntry + + // there will be more tree entries than children in the tree, + // due to submodules and empty directories, but I think it is still + // worth it to pre-allocate the whole array now, even if sometimes + // is bigger than needed. + ret := make([]noder.Noder, 0, len(t.Entries)) + + walker := NewTreeWalker(t, false, nil) // don't recurse + // don't defer walker.Close() for efficiency reasons. + for { + _, e, err = walker.Next() + if err == io.EOF { + break + } + if err != nil { + walker.Close() + return nil, err + } + + ret = append(ret, &treeNoder{ + parent: t, + name: e.Name, + mode: e.Mode, + hash: e.Hash, + }) + } + walker.Close() + + return ret, nil +} + +// len(t.tree.Entries) != the number of elements walked by treewalker +// for some reason because of empty directories, submodules, etc, so we +// have to walk here. +func (t *treeNoder) NumChildren() (int, error) { + children, err := t.Children() + if err != nil { + return 0, err + } + + return len(children), nil +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/advrefs.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/advrefs.go new file mode 100644 index 0000000000..487ee19bd8 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/advrefs.go @@ -0,0 +1,203 @@ +package packp + +import ( + "fmt" + "sort" + "strings" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability" + "gopkg.in/src-d/go-git.v4/plumbing/storer" + "gopkg.in/src-d/go-git.v4/storage/memory" +) + +// AdvRefs values represent the information transmitted on an +// advertised-refs message. Values from this type are not zero-value +// safe, use the New function instead. +type AdvRefs struct { + // Prefix stores prefix payloads. + // + // When using this message over (smart) HTTP, you have to add a pktline + // before the whole thing with the following payload: + // + // '# service=$servicename" LF + // + // Moreover, some (all) git HTTP smart servers will send a flush-pkt + // just after the first pkt-line. + // + // To accommodate both situations, the Prefix field allow you to store + // any data you want to send before the actual pktlines. It will also + // be filled up with whatever is found on the line. + Prefix [][]byte + // Head stores the resolved HEAD reference if present. + // This can be present with git-upload-pack, not with git-receive-pack. + Head *plumbing.Hash + // Capabilities are the capabilities. + Capabilities *capability.List + // References are the hash references. + References map[string]plumbing.Hash + // Peeled are the peeled hash references. + Peeled map[string]plumbing.Hash + // Shallows are the shallow object ids. + Shallows []plumbing.Hash +} + +// NewAdvRefs returns a pointer to a new AdvRefs value, ready to be used. +func NewAdvRefs() *AdvRefs { + return &AdvRefs{ + Prefix: [][]byte{}, + Capabilities: capability.NewList(), + References: make(map[string]plumbing.Hash), + Peeled: make(map[string]plumbing.Hash), + Shallows: []plumbing.Hash{}, + } +} + +func (a *AdvRefs) AddReference(r *plumbing.Reference) error { + switch r.Type() { + case plumbing.SymbolicReference: + v := fmt.Sprintf("%s:%s", r.Name().String(), r.Target().String()) + a.Capabilities.Add(capability.SymRef, v) + case plumbing.HashReference: + a.References[r.Name().String()] = r.Hash() + default: + return plumbing.ErrInvalidType + } + + return nil +} + +func (a *AdvRefs) AllReferences() (memory.ReferenceStorage, error) { + s := memory.ReferenceStorage{} + if err := a.addRefs(s); err != nil { + return s, plumbing.NewUnexpectedError(err) + } + + return s, nil +} + +func (a *AdvRefs) addRefs(s storer.ReferenceStorer) error { + for name, hash := range a.References { + ref := plumbing.NewReferenceFromStrings(name, hash.String()) + if err := s.SetReference(ref); err != nil { + return err + } + } + + if a.supportSymrefs() { + return a.addSymbolicRefs(s) + } + + return a.resolveHead(s) +} + +// If the server does not support symrefs capability, +// we need to guess the reference where HEAD is pointing to. +// +// Git versions prior to 1.8.4.3 has an special procedure to get +// the reference where is pointing to HEAD: +// - Check if a reference called master exists. If exists and it +// has the same hash as HEAD hash, we can say that HEAD is pointing to master +// - If master does not exists or does not have the same hash as HEAD, +// order references and check in that order if that reference has the same +// hash than HEAD. If yes, set HEAD pointing to that branch hash +// - If no reference is found, throw an error +func (a *AdvRefs) resolveHead(s storer.ReferenceStorer) error { + if a.Head == nil { + return nil + } + + ref, err := s.Reference(plumbing.Master) + + // check first if HEAD is pointing to master + if err == nil { + ok, err := a.createHeadIfCorrectReference(ref, s) + if err != nil { + return err + } + + if ok { + return nil + } + } + + if err != nil && err != plumbing.ErrReferenceNotFound { + return err + } + + // From here we are trying to guess the branch that HEAD is pointing + refIter, err := s.IterReferences() + if err != nil { + return err + } + + var refNames []string + err = refIter.ForEach(func(r *plumbing.Reference) error { + refNames = append(refNames, string(r.Name())) + return nil + }) + if err != nil { + return err + } + + sort.Strings(refNames) + + var headSet bool + for _, refName := range refNames { + ref, err := s.Reference(plumbing.ReferenceName(refName)) + if err != nil { + return err + } + ok, err := a.createHeadIfCorrectReference(ref, s) + if err != nil { + return err + } + if ok { + headSet = true + break + } + } + + if !headSet { + return plumbing.ErrReferenceNotFound + } + + return nil +} + +func (a *AdvRefs) createHeadIfCorrectReference( + reference *plumbing.Reference, + s storer.ReferenceStorer) (bool, error) { + if reference.Hash() == *a.Head { + headRef := plumbing.NewSymbolicReference(plumbing.HEAD, reference.Name()) + if err := s.SetReference(headRef); err != nil { + return false, err + } + + return true, nil + } + + return false, nil +} + +func (a *AdvRefs) addSymbolicRefs(s storer.ReferenceStorer) error { + for _, symref := range a.Capabilities.Get(capability.SymRef) { + chunks := strings.Split(symref, ":") + if len(chunks) != 2 { + err := fmt.Errorf("bad number of `:` in symref value (%q)", symref) + return plumbing.NewUnexpectedError(err) + } + name := plumbing.ReferenceName(chunks[0]) + target := plumbing.ReferenceName(chunks[1]) + ref := plumbing.NewSymbolicReference(name, target) + if err := s.SetReference(ref); err != nil { + return nil + } + } + + return nil +} + +func (a *AdvRefs) supportSymrefs() bool { + return a.Capabilities.Supports(capability.SymRef) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/advrefs_decode.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/advrefs_decode.go new file mode 100644 index 0000000000..1b4c62c896 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/advrefs_decode.go @@ -0,0 +1,288 @@ +package packp + +import ( + "bytes" + "encoding/hex" + "errors" + "fmt" + "io" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" +) + +// Decode reads the next advertised-refs message form its input and +// stores it in the AdvRefs. +func (a *AdvRefs) Decode(r io.Reader) error { + d := newAdvRefsDecoder(r) + return d.Decode(a) +} + +type advRefsDecoder struct { + s *pktline.Scanner // a pkt-line scanner from the input stream + line []byte // current pkt-line contents, use parser.nextLine() to make it advance + nLine int // current pkt-line number for debugging, begins at 1 + hash plumbing.Hash // last hash read + err error // sticky error, use the parser.error() method to fill this out + data *AdvRefs // parsed data is stored here +} + +var ( + // ErrEmptyAdvRefs is returned by Decode if it gets an empty advertised + // references message. + ErrEmptyAdvRefs = errors.New("empty advertised-ref message") + // ErrEmptyInput is returned by Decode if the input is empty. + ErrEmptyInput = errors.New("empty input") +) + +func newAdvRefsDecoder(r io.Reader) *advRefsDecoder { + return &advRefsDecoder{ + s: pktline.NewScanner(r), + } +} + +func (d *advRefsDecoder) Decode(v *AdvRefs) error { + d.data = v + + for state := decodePrefix; state != nil; { + state = state(d) + } + + return d.err +} + +type decoderStateFn func(*advRefsDecoder) decoderStateFn + +// fills out the parser stiky error +func (d *advRefsDecoder) error(format string, a ...interface{}) { + msg := fmt.Sprintf( + "pkt-line %d: %s", d.nLine, + fmt.Sprintf(format, a...), + ) + + d.err = NewErrUnexpectedData(msg, d.line) +} + +// Reads a new pkt-line from the scanner, makes its payload available as +// p.line and increments p.nLine. A successful invocation returns true, +// otherwise, false is returned and the sticky error is filled out +// accordingly. Trims eols at the end of the payloads. +func (d *advRefsDecoder) nextLine() bool { + d.nLine++ + + if !d.s.Scan() { + if d.err = d.s.Err(); d.err != nil { + return false + } + + if d.nLine == 1 { + d.err = ErrEmptyInput + return false + } + + d.error("EOF") + return false + } + + d.line = d.s.Bytes() + d.line = bytes.TrimSuffix(d.line, eol) + + return true +} + +// The HTTP smart prefix is often followed by a flush-pkt. +func decodePrefix(d *advRefsDecoder) decoderStateFn { + if ok := d.nextLine(); !ok { + return nil + } + + if !isPrefix(d.line) { + return decodeFirstHash + } + + tmp := make([]byte, len(d.line)) + copy(tmp, d.line) + d.data.Prefix = append(d.data.Prefix, tmp) + if ok := d.nextLine(); !ok { + return nil + } + + if !isFlush(d.line) { + return decodeFirstHash + } + + d.data.Prefix = append(d.data.Prefix, pktline.Flush) + if ok := d.nextLine(); !ok { + return nil + } + + return decodeFirstHash +} + +func isPrefix(payload []byte) bool { + return len(payload) > 0 && payload[0] == '#' +} + +// If the first hash is zero, then a no-refs is coming. Otherwise, a +// list-of-refs is coming, and the hash will be followed by the first +// advertised ref. +func decodeFirstHash(p *advRefsDecoder) decoderStateFn { + // If the repository is empty, we receive a flush here (HTTP). + if isFlush(p.line) { + p.err = ErrEmptyAdvRefs + return nil + } + + if len(p.line) < hashSize { + p.error("cannot read hash, pkt-line too short") + return nil + } + + if _, err := hex.Decode(p.hash[:], p.line[:hashSize]); err != nil { + p.error("invalid hash text: %s", err) + return nil + } + + p.line = p.line[hashSize:] + + if p.hash.IsZero() { + return decodeSkipNoRefs + } + + return decodeFirstRef +} + +// Skips SP "capabilities^{}" NUL +func decodeSkipNoRefs(p *advRefsDecoder) decoderStateFn { + if len(p.line) < len(noHeadMark) { + p.error("too short zero-id ref") + return nil + } + + if !bytes.HasPrefix(p.line, noHeadMark) { + p.error("malformed zero-id ref") + return nil + } + + p.line = p.line[len(noHeadMark):] + + return decodeCaps +} + +// decode the refname, expects SP refname NULL +func decodeFirstRef(l *advRefsDecoder) decoderStateFn { + if len(l.line) < 3 { + l.error("line too short after hash") + return nil + } + + if !bytes.HasPrefix(l.line, sp) { + l.error("no space after hash") + return nil + } + l.line = l.line[1:] + + chunks := bytes.SplitN(l.line, null, 2) + if len(chunks) < 2 { + l.error("NULL not found") + return nil + } + ref := chunks[0] + l.line = chunks[1] + + if bytes.Equal(ref, []byte(head)) { + l.data.Head = &l.hash + } else { + l.data.References[string(ref)] = l.hash + } + + return decodeCaps +} + +func decodeCaps(p *advRefsDecoder) decoderStateFn { + if err := p.data.Capabilities.Decode(p.line); err != nil { + p.error("invalid capabilities: %s", err) + return nil + } + + return decodeOtherRefs +} + +// The refs are either tips (obj-id SP refname) or a peeled (obj-id SP refname^{}). +// If there are no refs, then there might be a shallow or flush-ptk. +func decodeOtherRefs(p *advRefsDecoder) decoderStateFn { + if ok := p.nextLine(); !ok { + return nil + } + + if bytes.HasPrefix(p.line, shallow) { + return decodeShallow + } + + if len(p.line) == 0 { + return nil + } + + saveTo := p.data.References + if bytes.HasSuffix(p.line, peeled) { + p.line = bytes.TrimSuffix(p.line, peeled) + saveTo = p.data.Peeled + } + + ref, hash, err := readRef(p.line) + if err != nil { + p.error("%s", err) + return nil + } + saveTo[ref] = hash + + return decodeOtherRefs +} + +// Reads a ref-name +func readRef(data []byte) (string, plumbing.Hash, error) { + chunks := bytes.Split(data, sp) + switch { + case len(chunks) == 1: + return "", plumbing.ZeroHash, fmt.Errorf("malformed ref data: no space was found") + case len(chunks) > 2: + return "", plumbing.ZeroHash, fmt.Errorf("malformed ref data: more than one space found") + default: + return string(chunks[1]), plumbing.NewHash(string(chunks[0])), nil + } +} + +// Keeps reading shallows until a flush-pkt is found +func decodeShallow(p *advRefsDecoder) decoderStateFn { + if !bytes.HasPrefix(p.line, shallow) { + p.error("malformed shallow prefix, found %q... instead", p.line[:len(shallow)]) + return nil + } + p.line = bytes.TrimPrefix(p.line, shallow) + + if len(p.line) != hashSize { + p.error(fmt.Sprintf( + "malformed shallow hash: wrong length, expected 40 bytes, read %d bytes", + len(p.line))) + return nil + } + + text := p.line[:hashSize] + var h plumbing.Hash + if _, err := hex.Decode(h[:], text); err != nil { + p.error("invalid hash text: %s", err) + return nil + } + + p.data.Shallows = append(p.data.Shallows, h) + + if ok := p.nextLine(); !ok { + return nil + } + + if len(p.line) == 0 { + return nil // succesfull parse of the advertised-refs message + } + + return decodeShallow +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/advrefs_encode.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/advrefs_encode.go new file mode 100644 index 0000000000..c23e3feb0f --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/advrefs_encode.go @@ -0,0 +1,176 @@ +package packp + +import ( + "bytes" + "fmt" + "io" + "sort" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" + "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability" +) + +// Encode writes the AdvRefs encoding to a writer. +// +// All the payloads will end with a newline character. Capabilities, +// references and shallows are written in alphabetical order, except for +// peeled references that always follow their corresponding references. +func (a *AdvRefs) Encode(w io.Writer) error { + e := newAdvRefsEncoder(w) + return e.Encode(a) +} + +type advRefsEncoder struct { + data *AdvRefs // data to encode + pe *pktline.Encoder // where to write the encoded data + firstRefName string // reference name to encode in the first pkt-line (HEAD if present) + firstRefHash plumbing.Hash // hash referenced to encode in the first pkt-line (HEAD if present) + sortedRefs []string // hash references to encode ordered by increasing order + err error // sticky error + +} + +func newAdvRefsEncoder(w io.Writer) *advRefsEncoder { + return &advRefsEncoder{ + pe: pktline.NewEncoder(w), + } +} + +func (e *advRefsEncoder) Encode(v *AdvRefs) error { + e.data = v + e.sortRefs() + e.setFirstRef() + + for state := encodePrefix; state != nil; { + state = state(e) + } + + return e.err +} + +func (e *advRefsEncoder) sortRefs() { + if len(e.data.References) > 0 { + refs := make([]string, 0, len(e.data.References)) + for refName := range e.data.References { + refs = append(refs, refName) + } + + sort.Strings(refs) + e.sortedRefs = refs + } +} + +func (e *advRefsEncoder) setFirstRef() { + if e.data.Head != nil { + e.firstRefName = head + e.firstRefHash = *e.data.Head + return + } + + if len(e.sortedRefs) > 0 { + refName := e.sortedRefs[0] + e.firstRefName = refName + e.firstRefHash = e.data.References[refName] + } +} + +type encoderStateFn func(*advRefsEncoder) encoderStateFn + +func encodePrefix(e *advRefsEncoder) encoderStateFn { + for _, p := range e.data.Prefix { + if bytes.Equal(p, pktline.Flush) { + if e.err = e.pe.Flush(); e.err != nil { + return nil + } + continue + } + if e.err = e.pe.Encodef("%s\n", string(p)); e.err != nil { + return nil + } + } + + return encodeFirstLine +} + +// Adds the first pkt-line payload: head hash, head ref and capabilities. +// If HEAD ref is not found, the first reference ordered in increasing order will be used. +// If there aren't HEAD neither refs, the first line will be "PKT-LINE(zero-id SP "capabilities^{}" NUL capability-list)". +// See: https://github.com/git/git/blob/master/Documentation/technical/pack-protocol.txt +// See: https://github.com/git/git/blob/master/Documentation/technical/protocol-common.txt +func encodeFirstLine(e *advRefsEncoder) encoderStateFn { + const formatFirstLine = "%s %s\x00%s\n" + var firstLine string + capabilities := formatCaps(e.data.Capabilities) + + if e.firstRefName == "" { + firstLine = fmt.Sprintf(formatFirstLine, plumbing.ZeroHash.String(), "capabilities^{}", capabilities) + } else { + firstLine = fmt.Sprintf(formatFirstLine, e.firstRefHash.String(), e.firstRefName, capabilities) + + } + + if e.err = e.pe.EncodeString(firstLine); e.err != nil { + return nil + } + + return encodeRefs +} + +func formatCaps(c *capability.List) string { + if c == nil { + return "" + } + + return c.String() +} + +// Adds the (sorted) refs: hash SP refname EOL +// and their peeled refs if any. +func encodeRefs(e *advRefsEncoder) encoderStateFn { + for _, r := range e.sortedRefs { + if r == e.firstRefName { + continue + } + + hash := e.data.References[r] + if e.err = e.pe.Encodef("%s %s\n", hash.String(), r); e.err != nil { + return nil + } + + if hash, ok := e.data.Peeled[r]; ok { + if e.err = e.pe.Encodef("%s %s^{}\n", hash.String(), r); e.err != nil { + return nil + } + } + } + + return encodeShallow +} + +// Adds the (sorted) shallows: "shallow" SP hash EOL +func encodeShallow(e *advRefsEncoder) encoderStateFn { + sorted := sortShallows(e.data.Shallows) + for _, hash := range sorted { + if e.err = e.pe.Encodef("shallow %s\n", hash); e.err != nil { + return nil + } + } + + return encodeFlush +} + +func sortShallows(c []plumbing.Hash) []string { + ret := []string{} + for _, h := range c { + ret = append(ret, h.String()) + } + sort.Strings(ret) + + return ret +} + +func encodeFlush(e *advRefsEncoder) encoderStateFn { + e.err = e.pe.Flush() + return nil +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability/capability.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability/capability.go new file mode 100644 index 0000000000..a129781157 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability/capability.go @@ -0,0 +1,252 @@ +// Package capability defines the server and client capabilities. +package capability + +// Capability describes a server or client capability. +type Capability string + +func (n Capability) String() string { + return string(n) +} + +const ( + // MultiACK capability allows the server to return "ACK obj-id continue" as + // soon as it finds a commit that it can use as a common base, between the + // client's wants and the client's have set. + // + // By sending this early, the server can potentially head off the client + // from walking any further down that particular branch of the client's + // repository history. The client may still need to walk down other + // branches, sending have lines for those, until the server has a + // complete cut across the DAG, or the client has said "done". + // + // Without multi_ack, a client sends have lines in --date-order until + // the server has found a common base. That means the client will send + // have lines that are already known by the server to be common, because + // they overlap in time with another branch that the server hasn't found + // a common base on yet. + // + // For example suppose the client has commits in caps that the server + // doesn't and the server has commits in lower case that the client + // doesn't, as in the following diagram: + // + // +---- u ---------------------- x + // / +----- y + // / / + // a -- b -- c -- d -- E -- F + // \ + // +--- Q -- R -- S + // + // If the client wants x,y and starts out by saying have F,S, the server + // doesn't know what F,S is. Eventually the client says "have d" and + // the server sends "ACK d continue" to let the client know to stop + // walking down that line (so don't send c-b-a), but it's not done yet, + // it needs a base for x. The client keeps going with S-R-Q, until a + // gets reached, at which point the server has a clear base and it all + // ends. + // + // Without multi_ack the client would have sent that c-b-a chain anyway, + // interleaved with S-R-Q. + MultiACK Capability = "multi_ack" + // MultiACKDetailed is an extension of multi_ack that permits client to + // better understand the server's in-memory state. + MultiACKDetailed Capability = "multi_ack_detailed" + // NoDone should only be used with the smart HTTP protocol. If + // multi_ack_detailed and no-done are both present, then the sender is + // free to immediately send a pack following its first "ACK obj-id ready" + // message. + // + // Without no-done in the smart HTTP protocol, the server session would + // end and the client has to make another trip to send "done" before + // the server can send the pack. no-done removes the last round and + // thus slightly reduces latency. + NoDone Capability = "no-done" + // ThinPack is one with deltas which reference base objects not + // contained within the pack (but are known to exist at the receiving + // end). This can reduce the network traffic significantly, but it + // requires the receiving end to know how to "thicken" these packs by + // adding the missing bases to the pack. + // + // The upload-pack server advertises 'thin-pack' when it can generate + // and send a thin pack. A client requests the 'thin-pack' capability + // when it understands how to "thicken" it, notifying the server that + // it can receive such a pack. A client MUST NOT request the + // 'thin-pack' capability if it cannot turn a thin pack into a + // self-contained pack. + // + // Receive-pack, on the other hand, is assumed by default to be able to + // handle thin packs, but can ask the client not to use the feature by + // advertising the 'no-thin' capability. A client MUST NOT send a thin + // pack if the server advertises the 'no-thin' capability. + // + // The reasons for this asymmetry are historical. The receive-pack + // program did not exist until after the invention of thin packs, so + // historically the reference implementation of receive-pack always + // understood thin packs. Adding 'no-thin' later allowed receive-pack + // to disable the feature in a backwards-compatible manner. + ThinPack Capability = "thin-pack" + // Sideband means that server can send, and client understand multiplexed + // progress reports and error info interleaved with the packfile itself. + // + // These two options are mutually exclusive. A modern client always + // favors Sideband64k. + // + // Either mode indicates that the packfile data will be streamed broken + // up into packets of up to either 1000 bytes in the case of 'side_band', + // or 65520 bytes in the case of 'side_band_64k'. Each packet is made up + // of a leading 4-byte pkt-line length of how much data is in the packet, + // followed by a 1-byte stream code, followed by the actual data. + // + // The stream code can be one of: + // + // 1 - pack data + // 2 - progress messages + // 3 - fatal error message just before stream aborts + // + // The "side-band-64k" capability came about as a way for newer clients + // that can handle much larger packets to request packets that are + // actually crammed nearly full, while maintaining backward compatibility + // for the older clients. + // + // Further, with side-band and its up to 1000-byte messages, it's actually + // 999 bytes of payload and 1 byte for the stream code. With side-band-64k, + // same deal, you have up to 65519 bytes of data and 1 byte for the stream + // code. + // + // The client MUST send only maximum of one of "side-band" and "side- + // band-64k". Server MUST diagnose it as an error if client requests + // both. + Sideband Capability = "side-band" + Sideband64k Capability = "side-band-64k" + // OFSDelta server can send, and client understand PACKv2 with delta + // referring to its base by position in pack rather than by an obj-id. That + // is, they can send/read OBJ_OFS_DELTA (aka type 6) in a packfile. + OFSDelta Capability = "ofs-delta" + // Agent the server may optionally send this capability to notify the client + // that the server is running version `X`. The client may optionally return + // its own agent string by responding with an `agent=Y` capability (but it + // MUST NOT do so if the server did not mention the agent capability). The + // `X` and `Y` strings may contain any printable ASCII characters except + // space (i.e., the byte range 32 < x < 127), and are typically of the form + // "package/version" (e.g., "git/1.8.3.1"). The agent strings are purely + // informative for statistics and debugging purposes, and MUST NOT be used + // to programmatically assume the presence or absence of particular features. + Agent Capability = "agent" + // Shallow capability adds "deepen", "shallow" and "unshallow" commands to + // the fetch-pack/upload-pack protocol so clients can request shallow + // clones. + Shallow Capability = "shallow" + // DeepenSince adds "deepen-since" command to fetch-pack/upload-pack + // protocol so the client can request shallow clones that are cut at a + // specific time, instead of depth. Internally it's equivalent of doing + // "rev-list --max-age=" on the server side. "deepen-since" + // cannot be used with "deepen". + DeepenSince Capability = "deepen-since" + // DeepenNot adds "deepen-not" command to fetch-pack/upload-pack + // protocol so the client can request shallow clones that are cut at a + // specific revision, instead of depth. Internally it's equivalent of + // doing "rev-list --not " on the server side. "deepen-not" + // cannot be used with "deepen", but can be used with "deepen-since". + DeepenNot Capability = "deepen-not" + // DeepenRelative if this capability is requested by the client, the + // semantics of "deepen" command is changed. The "depth" argument is the + // depth from the current shallow boundary, instead of the depth from + // remote refs. + DeepenRelative Capability = "deepen-relative" + // NoProgress the client was started with "git clone -q" or something, and + // doesn't want that side band 2. Basically the client just says "I do not + // wish to receive stream 2 on sideband, so do not send it to me, and if + // you did, I will drop it on the floor anyway". However, the sideband + // channel 3 is still used for error responses. + NoProgress Capability = "no-progress" + // IncludeTag capability is about sending annotated tags if we are + // sending objects they point to. If we pack an object to the client, and + // a tag object points exactly at that object, we pack the tag object too. + // In general this allows a client to get all new annotated tags when it + // fetches a branch, in a single network connection. + // + // Clients MAY always send include-tag, hardcoding it into a request when + // the server advertises this capability. The decision for a client to + // request include-tag only has to do with the client's desires for tag + // data, whether or not a server had advertised objects in the + // refs/tags/* namespace. + // + // Servers MUST pack the tags if their referrant is packed and the client + // has requested include-tags. + // + // Clients MUST be prepared for the case where a server has ignored + // include-tag and has not actually sent tags in the pack. In such + // cases the client SHOULD issue a subsequent fetch to acquire the tags + // that include-tag would have otherwise given the client. + // + // The server SHOULD send include-tag, if it supports it, regardless + // of whether or not there are tags available. + IncludeTag Capability = "include-tag" + // ReportStatus the receive-pack process can receive a 'report-status' + // capability, which tells it that the client wants a report of what + // happened after a packfile upload and reference update. If the pushing + // client requests this capability, after unpacking and updating references + // the server will respond with whether the packfile unpacked successfully + // and if each reference was updated successfully. If any of those were not + // successful, it will send back an error message. See pack-protocol.txt + // for example messages. + ReportStatus Capability = "report-status" + // DeleteRefs If the server sends back this capability, it means that + // it is capable of accepting a zero-id value as the target + // value of a reference update. It is not sent back by the client, it + // simply informs the client that it can be sent zero-id values + // to delete references + DeleteRefs Capability = "delete-refs" + // Quiet If the receive-pack server advertises this capability, it is + // capable of silencing human-readable progress output which otherwise may + // be shown when processing the received pack. A send-pack client should + // respond with the 'quiet' capability to suppress server-side progress + // reporting if the local progress reporting is also being suppressed + // (e.g., via `push -q`, or if stderr does not go to a tty). + Quiet Capability = "quiet" + // Atomic If the server sends this capability it is capable of accepting + // atomic pushes. If the pushing client requests this capability, the server + // will update the refs in one atomic transaction. Either all refs are + // updated or none. + Atomic Capability = "atomic" + // PushOptions If the server sends this capability it is able to accept + // push options after the update commands have been sent, but before the + // packfile is streamed. If the pushing client requests this capability, + // the server will pass the options to the pre- and post- receive hooks + // that process this push request. + PushOptions Capability = "push-options" + // AllowTipSHA1InWant if the upload-pack server advertises this capability, + // fetch-pack may send "want" lines with SHA-1s that exist at the server but + // are not advertised by upload-pack. + AllowTipSHA1InWant Capability = "allow-tip-sha1-in-want" + // AllowReachableSHA1InWant if the upload-pack server advertises this + // capability, fetch-pack may send "want" lines with SHA-1s that exist at + // the server but are not advertised by upload-pack. + AllowReachableSHA1InWant Capability = "allow-reachable-sha1-in-want" + // PushCert the receive-pack server that advertises this capability is + // willing to accept a signed push certificate, and asks the to be + // included in the push certificate. A send-pack client MUST NOT + // send a push-cert packet unless the receive-pack server advertises + // this capability. + PushCert Capability = "push-cert" + // SymRef symbolic reference support for better negotiation. + SymRef Capability = "symref" +) + +const DefaultAgent = "go-git/4.x" + +var known = map[Capability]bool{ + MultiACK: true, MultiACKDetailed: true, NoDone: true, ThinPack: true, + Sideband: true, Sideband64k: true, OFSDelta: true, Agent: true, + Shallow: true, DeepenSince: true, DeepenNot: true, DeepenRelative: true, + NoProgress: true, IncludeTag: true, ReportStatus: true, DeleteRefs: true, + Quiet: true, Atomic: true, PushOptions: true, AllowTipSHA1InWant: true, + AllowReachableSHA1InWant: true, PushCert: true, SymRef: true, +} + +var requiresArgument = map[Capability]bool{ + Agent: true, PushCert: true, SymRef: true, +} + +var multipleArgument = map[Capability]bool{ + SymRef: true, +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability/list.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability/list.go new file mode 100644 index 0000000000..26a79b6e73 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability/list.go @@ -0,0 +1,196 @@ +package capability + +import ( + "bytes" + "errors" + "fmt" + "strings" +) + +var ( + // ErrArgumentsRequired is returned if no arguments are giving with a + // capability that requires arguments + ErrArgumentsRequired = errors.New("arguments required") + // ErrArguments is returned if arguments are given with a capabilities that + // not supports arguments + ErrArguments = errors.New("arguments not allowed") + // ErrEmtpyArgument is returned when an empty value is given + ErrEmtpyArgument = errors.New("empty argument") + // ErrMultipleArguments multiple argument given to a capabilities that not + // support it + ErrMultipleArguments = errors.New("multiple arguments not allowed") +) + +// List represents a list of capabilities +type List struct { + m map[Capability]*entry + sort []string +} + +type entry struct { + Name Capability + Values []string +} + +// NewList returns a new List of capabilities +func NewList() *List { + return &List{ + m: make(map[Capability]*entry), + } +} + +// IsEmpty returns true if the List is empty +func (l *List) IsEmpty() bool { + return len(l.sort) == 0 +} + +// Decode decodes list of capabilities from raw into the list +func (l *List) Decode(raw []byte) error { + // git 1.x receive pack used to send a leading space on its + // git-receive-pack capabilities announcement. We just trim space to be + // tolerant to space changes in different versions. + raw = bytes.TrimSpace(raw) + + if len(raw) == 0 { + return nil + } + + for _, data := range bytes.Split(raw, []byte{' '}) { + pair := bytes.SplitN(data, []byte{'='}, 2) + + c := Capability(pair[0]) + if len(pair) == 1 { + if err := l.Add(c); err != nil { + return err + } + + continue + } + + if err := l.Add(c, string(pair[1])); err != nil { + return err + } + } + + return nil +} + +// Get returns the values for a capability +func (l *List) Get(capability Capability) []string { + if _, ok := l.m[capability]; !ok { + return nil + } + + return l.m[capability].Values +} + +// Set sets a capability removing the previous values +func (l *List) Set(capability Capability, values ...string) error { + if _, ok := l.m[capability]; ok { + delete(l.m, capability) + } + + return l.Add(capability, values...) +} + +// Add adds a capability, values are optional +func (l *List) Add(c Capability, values ...string) error { + if err := l.validate(c, values); err != nil { + return err + } + + if !l.Supports(c) { + l.m[c] = &entry{Name: c} + l.sort = append(l.sort, c.String()) + } + + if len(values) == 0 { + return nil + } + + if known[c] && !multipleArgument[c] && len(l.m[c].Values) > 0 { + return ErrMultipleArguments + } + + l.m[c].Values = append(l.m[c].Values, values...) + return nil +} + +func (l *List) validateNoEmptyArgs(values []string) error { + for _, v := range values { + if v == "" { + return ErrEmtpyArgument + } + } + return nil +} + +func (l *List) validate(c Capability, values []string) error { + if !known[c] { + return l.validateNoEmptyArgs(values) + } + if requiresArgument[c] && len(values) == 0 { + return ErrArgumentsRequired + } + + if !requiresArgument[c] && len(values) != 0 { + return ErrArguments + } + + if !multipleArgument[c] && len(values) > 1 { + return ErrMultipleArguments + } + return l.validateNoEmptyArgs(values) +} + +// Supports returns true if capability is present +func (l *List) Supports(capability Capability) bool { + _, ok := l.m[capability] + return ok +} + +// Delete deletes a capability from the List +func (l *List) Delete(capability Capability) { + if !l.Supports(capability) { + return + } + + delete(l.m, capability) + for i, c := range l.sort { + if c != string(capability) { + continue + } + + l.sort = append(l.sort[:i], l.sort[i+1:]...) + return + } +} + +// All returns a slice with all defined capabilities. +func (l *List) All() []Capability { + var cs []Capability + for _, key := range l.sort { + cs = append(cs, Capability(key)) + } + + return cs +} + +// String generates the capabilities strings, the capabilities are sorted in +// insertion order +func (l *List) String() string { + var o []string + for _, key := range l.sort { + cap := l.m[Capability(key)] + if len(cap.Values) == 0 { + o = append(o, key) + continue + } + + for _, value := range cap.Values { + o = append(o, fmt.Sprintf("%s=%s", key, value)) + } + } + + return strings.Join(o, " ") +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/common.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/common.go new file mode 100644 index 0000000000..ab07ac8f74 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/common.go @@ -0,0 +1,70 @@ +package packp + +import ( + "fmt" +) + +type stateFn func() stateFn + +const ( + // common + hashSize = 40 + + // advrefs + head = "HEAD" + noHead = "capabilities^{}" +) + +var ( + // common + sp = []byte(" ") + eol = []byte("\n") + eq = []byte{'='} + + // advertised-refs + null = []byte("\x00") + peeled = []byte("^{}") + noHeadMark = []byte(" capabilities^{}\x00") + + // upload-request + want = []byte("want ") + shallow = []byte("shallow ") + deepen = []byte("deepen") + deepenCommits = []byte("deepen ") + deepenSince = []byte("deepen-since ") + deepenReference = []byte("deepen-not ") + + // shallow-update + unshallow = []byte("unshallow ") + + // server-response + ack = []byte("ACK") + nak = []byte("NAK") + + // updreq + shallowNoSp = []byte("shallow") +) + +func isFlush(payload []byte) bool { + return len(payload) == 0 +} + +// ErrUnexpectedData represents an unexpected data decoding a message +type ErrUnexpectedData struct { + Msg string + Data []byte +} + +// NewErrUnexpectedData returns a new ErrUnexpectedData containing the data and +// the message given +func NewErrUnexpectedData(msg string, data []byte) error { + return &ErrUnexpectedData{Msg: msg, Data: data} +} + +func (err *ErrUnexpectedData) Error() string { + if len(err.Data) == 0 { + return err.Msg + } + + return fmt.Sprintf("%s (%s)", err.Msg, err.Data) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/doc.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/doc.go new file mode 100644 index 0000000000..4950d1d662 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/doc.go @@ -0,0 +1,724 @@ +package packp + +/* + +A nice way to trace the real data transmitted and received by git, use: + +GIT_TRACE_PACKET=true git ls-remote http://github.com/src-d/go-git +GIT_TRACE_PACKET=true git clone http://github.com/src-d/go-git + +Here follows a copy of the current protocol specification at the time of +this writing. + +(Please notice that most http git servers will add a flush-pkt after the +first pkt-line when using HTTP smart.) + + +Documentation Common to Pack and Http Protocols +=============================================== + +ABNF Notation +------------- + +ABNF notation as described by RFC 5234 is used within the protocol documents, +except the following replacement core rules are used: +---- + HEXDIG = DIGIT / "a" / "b" / "c" / "d" / "e" / "f" +---- + +We also define the following common rules: +---- + NUL = %x00 + zero-id = 40*"0" + obj-id = 40*(HEXDIGIT) + + refname = "HEAD" + refname /= "refs/" +---- + +A refname is a hierarchical octet string beginning with "refs/" and +not violating the 'git-check-ref-format' command's validation rules. +More specifically, they: + +. They can include slash `/` for hierarchical (directory) + grouping, but no slash-separated component can begin with a + dot `.`. + +. They must contain at least one `/`. This enforces the presence of a + category like `heads/`, `tags/` etc. but the actual names are not + restricted. + +. They cannot have two consecutive dots `..` anywhere. + +. They cannot have ASCII control characters (i.e. bytes whose + values are lower than \040, or \177 `DEL`), space, tilde `~`, + caret `^`, colon `:`, question-mark `?`, asterisk `*`, + or open bracket `[` anywhere. + +. They cannot end with a slash `/` or a dot `.`. + +. They cannot end with the sequence `.lock`. + +. They cannot contain a sequence `@{`. + +. They cannot contain a `\\`. + + +pkt-line Format +--------------- + +Much (but not all) of the payload is described around pkt-lines. + +A pkt-line is a variable length binary string. The first four bytes +of the line, the pkt-len, indicates the total length of the line, +in hexadecimal. The pkt-len includes the 4 bytes used to contain +the length's hexadecimal representation. + +A pkt-line MAY contain binary data, so implementors MUST ensure +pkt-line parsing/formatting routines are 8-bit clean. + +A non-binary line SHOULD BE terminated by an LF, which if present +MUST be included in the total length. Receivers MUST treat pkt-lines +with non-binary data the same whether or not they contain the trailing +LF (stripping the LF if present, and not complaining when it is +missing). + +The maximum length of a pkt-line's data component is 65516 bytes. +Implementations MUST NOT send pkt-line whose length exceeds 65520 +(65516 bytes of payload + 4 bytes of length data). + +Implementations SHOULD NOT send an empty pkt-line ("0004"). + +A pkt-line with a length field of 0 ("0000"), called a flush-pkt, +is a special case and MUST be handled differently than an empty +pkt-line ("0004"). + +---- + pkt-line = data-pkt / flush-pkt + + data-pkt = pkt-len pkt-payload + pkt-len = 4*(HEXDIG) + pkt-payload = (pkt-len - 4)*(OCTET) + + flush-pkt = "0000" +---- + +Examples (as C-style strings): + +---- + pkt-line actual value + --------------------------------- + "0006a\n" "a\n" + "0005a" "a" + "000bfoobar\n" "foobar\n" + "0004" "" +---- + +Packfile transfer protocols +=========================== + +Git supports transferring data in packfiles over the ssh://, git://, http:// and +file:// transports. There exist two sets of protocols, one for pushing +data from a client to a server and another for fetching data from a +server to a client. The three transports (ssh, git, file) use the same +protocol to transfer data. http is documented in http-protocol.txt. + +The processes invoked in the canonical Git implementation are 'upload-pack' +on the server side and 'fetch-pack' on the client side for fetching data; +then 'receive-pack' on the server and 'send-pack' on the client for pushing +data. The protocol functions to have a server tell a client what is +currently on the server, then for the two to negotiate the smallest amount +of data to send in order to fully update one or the other. + +pkt-line Format +--------------- + +The descriptions below build on the pkt-line format described in +protocol-common.txt. When the grammar indicate `PKT-LINE(...)`, unless +otherwise noted the usual pkt-line LF rules apply: the sender SHOULD +include a LF, but the receiver MUST NOT complain if it is not present. + +Transports +---------- +There are three transports over which the packfile protocol is +initiated. The Git transport is a simple, unauthenticated server that +takes the command (almost always 'upload-pack', though Git +servers can be configured to be globally writable, in which 'receive- +pack' initiation is also allowed) with which the client wishes to +communicate and executes it and connects it to the requesting +process. + +In the SSH transport, the client just runs the 'upload-pack' +or 'receive-pack' process on the server over the SSH protocol and then +communicates with that invoked process over the SSH connection. + +The file:// transport runs the 'upload-pack' or 'receive-pack' +process locally and communicates with it over a pipe. + +Git Transport +------------- + +The Git transport starts off by sending the command and repository +on the wire using the pkt-line format, followed by a NUL byte and a +hostname parameter, terminated by a NUL byte. + + 0032git-upload-pack /project.git\0host=myserver.com\0 + +-- + git-proto-request = request-command SP pathname NUL [ host-parameter NUL ] + request-command = "git-upload-pack" / "git-receive-pack" / + "git-upload-archive" ; case sensitive + pathname = *( %x01-ff ) ; exclude NUL + host-parameter = "host=" hostname [ ":" port ] +-- + +Only host-parameter is allowed in the git-proto-request. Clients +MUST NOT attempt to send additional parameters. It is used for the +git-daemon name based virtual hosting. See --interpolated-path +option to git daemon, with the %H/%CH format characters. + +Basically what the Git client is doing to connect to an 'upload-pack' +process on the server side over the Git protocol is this: + + $ echo -e -n \ + "0039git-upload-pack /schacon/gitbook.git\0host=example.com\0" | + nc -v example.com 9418 + +If the server refuses the request for some reasons, it could abort +gracefully with an error message. + +---- + error-line = PKT-LINE("ERR" SP explanation-text) +---- + + +SSH Transport +------------- + +Initiating the upload-pack or receive-pack processes over SSH is +executing the binary on the server via SSH remote execution. +It is basically equivalent to running this: + + $ ssh git.example.com "git-upload-pack '/project.git'" + +For a server to support Git pushing and pulling for a given user over +SSH, that user needs to be able to execute one or both of those +commands via the SSH shell that they are provided on login. On some +systems, that shell access is limited to only being able to run those +two commands, or even just one of them. + +In an ssh:// format URI, it's absolute in the URI, so the '/' after +the host name (or port number) is sent as an argument, which is then +read by the remote git-upload-pack exactly as is, so it's effectively +an absolute path in the remote filesystem. + + git clone ssh://user@example.com/project.git + | + v + ssh user@example.com "git-upload-pack '/project.git'" + +In a "user@host:path" format URI, its relative to the user's home +directory, because the Git client will run: + + git clone user@example.com:project.git + | + v + ssh user@example.com "git-upload-pack 'project.git'" + +The exception is if a '~' is used, in which case +we execute it without the leading '/'. + + ssh://user@example.com/~alice/project.git, + | + v + ssh user@example.com "git-upload-pack '~alice/project.git'" + +A few things to remember here: + +- The "command name" is spelled with dash (e.g. git-upload-pack), but + this can be overridden by the client; + +- The repository path is always quoted with single quotes. + +Fetching Data From a Server +--------------------------- + +When one Git repository wants to get data that a second repository +has, the first can 'fetch' from the second. This operation determines +what data the server has that the client does not then streams that +data down to the client in packfile format. + + +Reference Discovery +------------------- + +When the client initially connects the server will immediately respond +with a listing of each reference it has (all branches and tags) along +with the object name that each reference currently points to. + + $ echo -e -n "0039git-upload-pack /schacon/gitbook.git\0host=example.com\0" | + nc -v example.com 9418 + 00887217a7c7e582c46cec22a130adf4b9d7d950fba0 HEAD\0multi_ack thin-pack + side-band side-band-64k ofs-delta shallow no-progress include-tag + 00441d3fcd5ced445d1abc402225c0b8a1299641f497 refs/heads/integration + 003f7217a7c7e582c46cec22a130adf4b9d7d950fba0 refs/heads/master + 003cb88d2441cac0977faf98efc80305012112238d9d refs/tags/v0.9 + 003c525128480b96c89e6418b1e40909bf6c5b2d580f refs/tags/v1.0 + 003fe92df48743b7bc7d26bcaabfddde0a1e20cae47c refs/tags/v1.0^{} + 0000 + +The returned response is a pkt-line stream describing each ref and +its current value. The stream MUST be sorted by name according to +the C locale ordering. + +If HEAD is a valid ref, HEAD MUST appear as the first advertised +ref. If HEAD is not a valid ref, HEAD MUST NOT appear in the +advertisement list at all, but other refs may still appear. + +The stream MUST include capability declarations behind a NUL on the +first ref. The peeled value of a ref (that is "ref^{}") MUST be +immediately after the ref itself, if presented. A conforming server +MUST peel the ref if it's an annotated tag. + +---- + advertised-refs = (no-refs / list-of-refs) + *shallow + flush-pkt + + no-refs = PKT-LINE(zero-id SP "capabilities^{}" + NUL capability-list) + + list-of-refs = first-ref *other-ref + first-ref = PKT-LINE(obj-id SP refname + NUL capability-list) + + other-ref = PKT-LINE(other-tip / other-peeled) + other-tip = obj-id SP refname + other-peeled = obj-id SP refname "^{}" + + shallow = PKT-LINE("shallow" SP obj-id) + + capability-list = capability *(SP capability) + capability = 1*(LC_ALPHA / DIGIT / "-" / "_") + LC_ALPHA = %x61-7A +---- + +Server and client MUST use lowercase for obj-id, both MUST treat obj-id +as case-insensitive. + +See protocol-capabilities.txt for a list of allowed server capabilities +and descriptions. + +Packfile Negotiation +-------------------- +After reference and capabilities discovery, the client can decide to +terminate the connection by sending a flush-pkt, telling the server it can +now gracefully terminate, and disconnect, when it does not need any pack +data. This can happen with the ls-remote command, and also can happen when +the client already is up-to-date. + +Otherwise, it enters the negotiation phase, where the client and +server determine what the minimal packfile necessary for transport is, +by telling the server what objects it wants, its shallow objects +(if any), and the maximum commit depth it wants (if any). The client +will also send a list of the capabilities it wants to be in effect, +out of what the server said it could do with the first 'want' line. + +---- + upload-request = want-list + *shallow-line + *1depth-request + flush-pkt + + want-list = first-want + *additional-want + + shallow-line = PKT-LINE("shallow" SP obj-id) + + depth-request = PKT-LINE("deepen" SP depth) / + PKT-LINE("deepen-since" SP timestamp) / + PKT-LINE("deepen-not" SP ref) + + first-want = PKT-LINE("want" SP obj-id SP capability-list) + additional-want = PKT-LINE("want" SP obj-id) + + depth = 1*DIGIT +---- + +Clients MUST send all the obj-ids it wants from the reference +discovery phase as 'want' lines. Clients MUST send at least one +'want' command in the request body. Clients MUST NOT mention an +obj-id in a 'want' command which did not appear in the response +obtained through ref discovery. + +The client MUST write all obj-ids which it only has shallow copies +of (meaning that it does not have the parents of a commit) as +'shallow' lines so that the server is aware of the limitations of +the client's history. + +The client now sends the maximum commit history depth it wants for +this transaction, which is the number of commits it wants from the +tip of the history, if any, as a 'deepen' line. A depth of 0 is the +same as not making a depth request. The client does not want to receive +any commits beyond this depth, nor does it want objects needed only to +complete those commits. Commits whose parents are not received as a +result are defined as shallow and marked as such in the server. This +information is sent back to the client in the next step. + +Once all the 'want's and 'shallow's (and optional 'deepen') are +transferred, clients MUST send a flush-pkt, to tell the server side +that it is done sending the list. + +Otherwise, if the client sent a positive depth request, the server +will determine which commits will and will not be shallow and +send this information to the client. If the client did not request +a positive depth, this step is skipped. + +---- + shallow-update = *shallow-line + *unshallow-line + flush-pkt + + shallow-line = PKT-LINE("shallow" SP obj-id) + + unshallow-line = PKT-LINE("unshallow" SP obj-id) +---- + +If the client has requested a positive depth, the server will compute +the set of commits which are no deeper than the desired depth. The set +of commits start at the client's wants. + +The server writes 'shallow' lines for each +commit whose parents will not be sent as a result. The server writes +an 'unshallow' line for each commit which the client has indicated is +shallow, but is no longer shallow at the currently requested depth +(that is, its parents will now be sent). The server MUST NOT mark +as unshallow anything which the client has not indicated was shallow. + +Now the client will send a list of the obj-ids it has using 'have' +lines, so the server can make a packfile that only contains the objects +that the client needs. In multi_ack mode, the canonical implementation +will send up to 32 of these at a time, then will send a flush-pkt. The +canonical implementation will skip ahead and send the next 32 immediately, +so that there is always a block of 32 "in-flight on the wire" at a time. + +---- + upload-haves = have-list + compute-end + + have-list = *have-line + have-line = PKT-LINE("have" SP obj-id) + compute-end = flush-pkt / PKT-LINE("done") +---- + +If the server reads 'have' lines, it then will respond by ACKing any +of the obj-ids the client said it had that the server also has. The +server will ACK obj-ids differently depending on which ack mode is +chosen by the client. + +In multi_ack mode: + + * the server will respond with 'ACK obj-id continue' for any common + commits. + + * once the server has found an acceptable common base commit and is + ready to make a packfile, it will blindly ACK all 'have' obj-ids + back to the client. + + * the server will then send a 'NAK' and then wait for another response + from the client - either a 'done' or another list of 'have' lines. + +In multi_ack_detailed mode: + + * the server will differentiate the ACKs where it is signaling + that it is ready to send data with 'ACK obj-id ready' lines, and + signals the identified common commits with 'ACK obj-id common' lines. + +Without either multi_ack or multi_ack_detailed: + + * upload-pack sends "ACK obj-id" on the first common object it finds. + After that it says nothing until the client gives it a "done". + + * upload-pack sends "NAK" on a flush-pkt if no common object + has been found yet. If one has been found, and thus an ACK + was already sent, it's silent on the flush-pkt. + +After the client has gotten enough ACK responses that it can determine +that the server has enough information to send an efficient packfile +(in the canonical implementation, this is determined when it has received +enough ACKs that it can color everything left in the --date-order queue +as common with the server, or the --date-order queue is empty), or the +client determines that it wants to give up (in the canonical implementation, +this is determined when the client sends 256 'have' lines without getting +any of them ACKed by the server - meaning there is nothing in common and +the server should just send all of its objects), then the client will send +a 'done' command. The 'done' command signals to the server that the client +is ready to receive its packfile data. + +However, the 256 limit *only* turns on in the canonical client +implementation if we have received at least one "ACK %s continue" +during a prior round. This helps to ensure that at least one common +ancestor is found before we give up entirely. + +Once the 'done' line is read from the client, the server will either +send a final 'ACK obj-id' or it will send a 'NAK'. 'obj-id' is the object +name of the last commit determined to be common. The server only sends +ACK after 'done' if there is at least one common base and multi_ack or +multi_ack_detailed is enabled. The server always sends NAK after 'done' +if there is no common base found. + +Then the server will start sending its packfile data. + +---- + server-response = *ack_multi ack / nak + ack_multi = PKT-LINE("ACK" SP obj-id ack_status) + ack_status = "continue" / "common" / "ready" + ack = PKT-LINE("ACK" SP obj-id) + nak = PKT-LINE("NAK") +---- + +A simple clone may look like this (with no 'have' lines): + +---- + C: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d multi_ack \ + side-band-64k ofs-delta\n + C: 0032want 7d1665144a3a975c05f1f43902ddaf084e784dbe\n + C: 0032want 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a\n + C: 0032want 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01\n + C: 0032want 74730d410fcb6603ace96f1dc55ea6196122532d\n + C: 0000 + C: 0009done\n + + S: 0008NAK\n + S: [PACKFILE] +---- + +An incremental update (fetch) response might look like this: + +---- + C: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d multi_ack \ + side-band-64k ofs-delta\n + C: 0032want 7d1665144a3a975c05f1f43902ddaf084e784dbe\n + C: 0032want 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a\n + C: 0000 + C: 0032have 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01\n + C: [30 more have lines] + C: 0032have 74730d410fcb6603ace96f1dc55ea6196122532d\n + C: 0000 + + S: 003aACK 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01 continue\n + S: 003aACK 74730d410fcb6603ace96f1dc55ea6196122532d continue\n + S: 0008NAK\n + + C: 0009done\n + + S: 0031ACK 74730d410fcb6603ace96f1dc55ea6196122532d\n + S: [PACKFILE] +---- + + +Packfile Data +------------- + +Now that the client and server have finished negotiation about what +the minimal amount of data that needs to be sent to the client is, the server +will construct and send the required data in packfile format. + +See pack-format.txt for what the packfile itself actually looks like. + +If 'side-band' or 'side-band-64k' capabilities have been specified by +the client, the server will send the packfile data multiplexed. + +Each packet starting with the packet-line length of the amount of data +that follows, followed by a single byte specifying the sideband the +following data is coming in on. + +In 'side-band' mode, it will send up to 999 data bytes plus 1 control +code, for a total of up to 1000 bytes in a pkt-line. In 'side-band-64k' +mode it will send up to 65519 data bytes plus 1 control code, for a +total of up to 65520 bytes in a pkt-line. + +The sideband byte will be a '1', '2' or a '3'. Sideband '1' will contain +packfile data, sideband '2' will be used for progress information that the +client will generally print to stderr and sideband '3' is used for error +information. + +If no 'side-band' capability was specified, the server will stream the +entire packfile without multiplexing. + + +Pushing Data To a Server +------------------------ + +Pushing data to a server will invoke the 'receive-pack' process on the +server, which will allow the client to tell it which references it should +update and then send all the data the server will need for those new +references to be complete. Once all the data is received and validated, +the server will then update its references to what the client specified. + +Authentication +-------------- + +The protocol itself contains no authentication mechanisms. That is to be +handled by the transport, such as SSH, before the 'receive-pack' process is +invoked. If 'receive-pack' is configured over the Git transport, those +repositories will be writable by anyone who can access that port (9418) as +that transport is unauthenticated. + +Reference Discovery +------------------- + +The reference discovery phase is done nearly the same way as it is in the +fetching protocol. Each reference obj-id and name on the server is sent +in packet-line format to the client, followed by a flush-pkt. The only +real difference is that the capability listing is different - the only +possible values are 'report-status', 'delete-refs', 'ofs-delta' and +'push-options'. + +Reference Update Request and Packfile Transfer +---------------------------------------------- + +Once the client knows what references the server is at, it can send a +list of reference update requests. For each reference on the server +that it wants to update, it sends a line listing the obj-id currently on +the server, the obj-id the client would like to update it to and the name +of the reference. + +This list is followed by a flush-pkt. Then the push options are transmitted +one per packet followed by another flush-pkt. After that the packfile that +should contain all the objects that the server will need to complete the new +references will be sent. + +---- + update-request = *shallow ( command-list | push-cert ) [packfile] + + shallow = PKT-LINE("shallow" SP obj-id) + + command-list = PKT-LINE(command NUL capability-list) + *PKT-LINE(command) + flush-pkt + + command = create / delete / update + create = zero-id SP new-id SP name + delete = old-id SP zero-id SP name + update = old-id SP new-id SP name + + old-id = obj-id + new-id = obj-id + + push-cert = PKT-LINE("push-cert" NUL capability-list LF) + PKT-LINE("certificate version 0.1" LF) + PKT-LINE("pusher" SP ident LF) + PKT-LINE("pushee" SP url LF) + PKT-LINE("nonce" SP nonce LF) + PKT-LINE(LF) + *PKT-LINE(command LF) + *PKT-LINE(gpg-signature-lines LF) + PKT-LINE("push-cert-end" LF) + + packfile = "PACK" 28*(OCTET) +---- + +If the receiving end does not support delete-refs, the sending end MUST +NOT ask for delete command. + +If the receiving end does not support push-cert, the sending end +MUST NOT send a push-cert command. When a push-cert command is +sent, command-list MUST NOT be sent; the commands recorded in the +push certificate is used instead. + +The packfile MUST NOT be sent if the only command used is 'delete'. + +A packfile MUST be sent if either create or update command is used, +even if the server already has all the necessary objects. In this +case the client MUST send an empty packfile. The only time this +is likely to happen is if the client is creating +a new branch or a tag that points to an existing obj-id. + +The server will receive the packfile, unpack it, then validate each +reference that is being updated that it hasn't changed while the request +was being processed (the obj-id is still the same as the old-id), and +it will run any update hooks to make sure that the update is acceptable. +If all of that is fine, the server will then update the references. + +Push Certificate +---------------- + +A push certificate begins with a set of header lines. After the +header and an empty line, the protocol commands follow, one per +line. Note that the trailing LF in push-cert PKT-LINEs is _not_ +optional; it must be present. + +Currently, the following header fields are defined: + +`pusher` ident:: + Identify the GPG key in "Human Readable Name " + format. + +`pushee` url:: + The repository URL (anonymized, if the URL contains + authentication material) the user who ran `git push` + intended to push into. + +`nonce` nonce:: + The 'nonce' string the receiving repository asked the + pushing user to include in the certificate, to prevent + replay attacks. + +The GPG signature lines are a detached signature for the contents +recorded in the push certificate before the signature block begins. +The detached signature is used to certify that the commands were +given by the pusher, who must be the signer. + +Report Status +------------- + +After receiving the pack data from the sender, the receiver sends a +report if 'report-status' capability is in effect. +It is a short listing of what happened in that update. It will first +list the status of the packfile unpacking as either 'unpack ok' or +'unpack [error]'. Then it will list the status for each of the references +that it tried to update. Each line is either 'ok [refname]' if the +update was successful, or 'ng [refname] [error]' if the update was not. + +---- + report-status = unpack-status + 1*(command-status) + flush-pkt + + unpack-status = PKT-LINE("unpack" SP unpack-result) + unpack-result = "ok" / error-msg + + command-status = command-ok / command-fail + command-ok = PKT-LINE("ok" SP refname) + command-fail = PKT-LINE("ng" SP refname SP error-msg) + + error-msg = 1*(OCTECT) ; where not "ok" +---- + +Updates can be unsuccessful for a number of reasons. The reference can have +changed since the reference discovery phase was originally sent, meaning +someone pushed in the meantime. The reference being pushed could be a +non-fast-forward reference and the update hooks or configuration could be +set to not allow that, etc. Also, some references can be updated while others +can be rejected. + +An example client/server communication might look like this: + +---- + S: 007c74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/local\0report-status delete-refs ofs-delta\n + S: 003e7d1665144a3a975c05f1f43902ddaf084e784dbe refs/heads/debug\n + S: 003f74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/master\n + S: 003f74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/team\n + S: 0000 + + C: 003e7d1665144a3a975c05f1f43902ddaf084e784dbe 74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/debug\n + C: 003e74730d410fcb6603ace96f1dc55ea6196122532d 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a refs/heads/master\n + C: 0000 + C: [PACKDATA] + + S: 000eunpack ok\n + S: 0018ok refs/heads/debug\n + S: 002ang refs/heads/master non-fast-forward\n +---- +*/ diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/report_status.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/report_status.go new file mode 100644 index 0000000000..29c1a4cd86 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/report_status.go @@ -0,0 +1,165 @@ +package packp + +import ( + "bytes" + "fmt" + "io" + "strings" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" +) + +const ( + ok = "ok" +) + +// ReportStatus is a report status message, as used in the git-receive-pack +// process whenever the 'report-status' capability is negotiated. +type ReportStatus struct { + UnpackStatus string + CommandStatuses []*CommandStatus +} + +// NewReportStatus creates a new ReportStatus message. +func NewReportStatus() *ReportStatus { + return &ReportStatus{} +} + +// Error returns the first error if any. +func (s *ReportStatus) Error() error { + if s.UnpackStatus != ok { + return fmt.Errorf("unpack error: %s", s.UnpackStatus) + } + + for _, s := range s.CommandStatuses { + if err := s.Error(); err != nil { + return err + } + } + + return nil +} + +// Encode writes the report status to a writer. +func (s *ReportStatus) Encode(w io.Writer) error { + e := pktline.NewEncoder(w) + if err := e.Encodef("unpack %s\n", s.UnpackStatus); err != nil { + return err + } + + for _, cs := range s.CommandStatuses { + if err := cs.encode(w); err != nil { + return err + } + } + + return e.Flush() +} + +// Decode reads from the given reader and decodes a report-status message. It +// does not read more input than what is needed to fill the report status. +func (s *ReportStatus) Decode(r io.Reader) error { + scan := pktline.NewScanner(r) + if err := s.scanFirstLine(scan); err != nil { + return err + } + + if err := s.decodeReportStatus(scan.Bytes()); err != nil { + return err + } + + flushed := false + for scan.Scan() { + b := scan.Bytes() + if isFlush(b) { + flushed = true + break + } + + if err := s.decodeCommandStatus(b); err != nil { + return err + } + } + + if !flushed { + return fmt.Errorf("missing flush") + } + + return scan.Err() +} + +func (s *ReportStatus) scanFirstLine(scan *pktline.Scanner) error { + if scan.Scan() { + return nil + } + + if scan.Err() != nil { + return scan.Err() + } + + return io.ErrUnexpectedEOF +} + +func (s *ReportStatus) decodeReportStatus(b []byte) error { + if isFlush(b) { + return fmt.Errorf("premature flush") + } + + b = bytes.TrimSuffix(b, eol) + + line := string(b) + fields := strings.SplitN(line, " ", 2) + if len(fields) != 2 || fields[0] != "unpack" { + return fmt.Errorf("malformed unpack status: %s", line) + } + + s.UnpackStatus = fields[1] + return nil +} + +func (s *ReportStatus) decodeCommandStatus(b []byte) error { + b = bytes.TrimSuffix(b, eol) + + line := string(b) + fields := strings.SplitN(line, " ", 3) + status := ok + if len(fields) == 3 && fields[0] == "ng" { + status = fields[2] + } else if len(fields) != 2 || fields[0] != "ok" { + return fmt.Errorf("malformed command status: %s", line) + } + + cs := &CommandStatus{ + ReferenceName: plumbing.ReferenceName(fields[1]), + Status: status, + } + s.CommandStatuses = append(s.CommandStatuses, cs) + return nil +} + +// CommandStatus is the status of a reference in a report status. +// See ReportStatus struct. +type CommandStatus struct { + ReferenceName plumbing.ReferenceName + Status string +} + +// Error returns the error, if any. +func (s *CommandStatus) Error() error { + if s.Status == ok { + return nil + } + + return fmt.Errorf("command error on %s: %s", + s.ReferenceName.String(), s.Status) +} + +func (s *CommandStatus) encode(w io.Writer) error { + e := pktline.NewEncoder(w) + if s.Error() == nil { + return e.Encodef("ok %s\n", s.ReferenceName.String()) + } + + return e.Encodef("ng %s %s\n", s.ReferenceName.String(), s.Status) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/shallowupd.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/shallowupd.go new file mode 100644 index 0000000000..fce4e3be2b --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/shallowupd.go @@ -0,0 +1,92 @@ +package packp + +import ( + "bytes" + "fmt" + "io" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" +) + +const ( + shallowLineLen = 48 + unshallowLineLen = 50 +) + +type ShallowUpdate struct { + Shallows []plumbing.Hash + Unshallows []plumbing.Hash +} + +func (r *ShallowUpdate) Decode(reader io.Reader) error { + s := pktline.NewScanner(reader) + + for s.Scan() { + line := s.Bytes() + line = bytes.TrimSpace(line) + + var err error + switch { + case bytes.HasPrefix(line, shallow): + err = r.decodeShallowLine(line) + case bytes.HasPrefix(line, unshallow): + err = r.decodeUnshallowLine(line) + case bytes.Equal(line, pktline.Flush): + return nil + } + + if err != nil { + return err + } + } + + return s.Err() +} + +func (r *ShallowUpdate) decodeShallowLine(line []byte) error { + hash, err := r.decodeLine(line, shallow, shallowLineLen) + if err != nil { + return err + } + + r.Shallows = append(r.Shallows, hash) + return nil +} + +func (r *ShallowUpdate) decodeUnshallowLine(line []byte) error { + hash, err := r.decodeLine(line, unshallow, unshallowLineLen) + if err != nil { + return err + } + + r.Unshallows = append(r.Unshallows, hash) + return nil +} + +func (r *ShallowUpdate) decodeLine(line, prefix []byte, expLen int) (plumbing.Hash, error) { + if len(line) != expLen { + return plumbing.ZeroHash, fmt.Errorf("malformed %s%q", prefix, line) + } + + raw := string(line[expLen-40 : expLen]) + return plumbing.NewHash(raw), nil +} + +func (r *ShallowUpdate) Encode(w io.Writer) error { + e := pktline.NewEncoder(w) + + for _, h := range r.Shallows { + if err := e.Encodef("%s%s\n", shallow, h.String()); err != nil { + return err + } + } + + for _, h := range r.Unshallows { + if err := e.Encodef("%s%s\n", unshallow, h.String()); err != nil { + return err + } + } + + return e.Flush() +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband/common.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband/common.go new file mode 100644 index 0000000000..de5001281f --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband/common.go @@ -0,0 +1,33 @@ +package sideband + +// Type sideband type "side-band" or "side-band-64k" +type Type int8 + +const ( + // Sideband legacy sideband type up to 1000-byte messages + Sideband Type = iota + // Sideband64k sideband type up to 65519-byte messages + Sideband64k Type = iota + + // MaxPackedSize for Sideband type + MaxPackedSize = 1000 + // MaxPackedSize64k for Sideband64k type + MaxPackedSize64k = 65520 +) + +// Channel sideband channel +type Channel byte + +// WithPayload encode the payload as a message +func (ch Channel) WithPayload(payload []byte) []byte { + return append([]byte{byte(ch)}, payload...) +} + +const ( + // PackData packfile content + PackData Channel = 1 + // ProgressMessage progress messages + ProgressMessage Channel = 2 + // ErrorMessage fatal error message just before stream aborts + ErrorMessage Channel = 3 +) diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband/demux.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband/demux.go new file mode 100644 index 0000000000..352336dc68 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband/demux.go @@ -0,0 +1,148 @@ +package sideband + +import ( + "errors" + "fmt" + "io" + + "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" +) + +// ErrMaxPackedExceeded returned by Read, if the maximum packed size is exceeded +var ErrMaxPackedExceeded = errors.New("max. packed size exceeded") + +// Progress where the progress information is stored +type Progress interface { + io.Writer +} + +// Demuxer demultiplexes the progress reports and error info interleaved with the +// packfile itself. +// +// A sideband has three different channels the main one, called PackData, contains +// the packfile data; the ErrorMessage channel, that contains server errors; and +// the last one, ProgressMessage channel, containing information about the ongoing +// task happening in the server (optional, can be suppressed sending NoProgress +// or Quiet capabilities to the server) +// +// In order to demultiplex the data stream, method `Read` should be called to +// retrieve the PackData channel, the incoming data from the ProgressMessage is +// written at `Progress` (if any), if any message is retrieved from the +// ErrorMessage channel an error is returned and we can assume that the +// connection has been closed. +type Demuxer struct { + t Type + r io.Reader + s *pktline.Scanner + + max int + pending []byte + + // Progress is where the progress messages are stored + Progress Progress +} + +// NewDemuxer returns a new Demuxer for the given t and read from r +func NewDemuxer(t Type, r io.Reader) *Demuxer { + max := MaxPackedSize64k + if t == Sideband { + max = MaxPackedSize + } + + return &Demuxer{ + t: t, + r: r, + max: max, + s: pktline.NewScanner(r), + } +} + +// Read reads up to len(p) bytes from the PackData channel into p, an error can +// be return if an error happens when reading or if a message is sent in the +// ErrorMessage channel. +// +// When a ProgressMessage is read, is not copy to b, instead of this is written +// to the Progress +func (d *Demuxer) Read(b []byte) (n int, err error) { + var read, req int + + req = len(b) + for read < req { + n, err := d.doRead(b[read:req]) + read += n + + if err != nil { + return read, err + } + } + + return read, nil +} + +func (d *Demuxer) doRead(b []byte) (int, error) { + read, err := d.nextPackData() + size := len(read) + wanted := len(b) + + if size > wanted { + d.pending = read[wanted:] + } + + if wanted > size { + wanted = size + } + + size = copy(b, read[:wanted]) + return size, err +} + +func (d *Demuxer) nextPackData() ([]byte, error) { + content := d.getPending() + if len(content) != 0 { + return content, nil + } + + if !d.s.Scan() { + if err := d.s.Err(); err != nil { + return nil, err + } + + return nil, io.EOF + } + + content = d.s.Bytes() + + size := len(content) + if size == 0 { + return nil, nil + } else if size > d.max { + return nil, ErrMaxPackedExceeded + } + + switch Channel(content[0]) { + case PackData: + return content[1:], nil + case ProgressMessage: + if d.Progress != nil { + _, err := d.Progress.Write(content[1:]) + return nil, err + } + case ErrorMessage: + return nil, fmt.Errorf("unexpected error: %s", content[1:]) + default: + return nil, fmt.Errorf("unknown channel %s", content) + } + + return nil, nil +} + +func (d *Demuxer) getPending() (b []byte) { + if len(d.pending) == 0 { + return nil + } + + content := d.pending + d.pending = nil + + return content +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband/doc.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband/doc.go new file mode 100644 index 0000000000..c5d2429529 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband/doc.go @@ -0,0 +1,31 @@ +// Package sideband implements a sideband mutiplex/demultiplexer +package sideband + +// If 'side-band' or 'side-band-64k' capabilities have been specified by +// the client, the server will send the packfile data multiplexed. +// +// Either mode indicates that the packfile data will be streamed broken +// up into packets of up to either 1000 bytes in the case of 'side_band', +// or 65520 bytes in the case of 'side_band_64k'. Each packet is made up +// of a leading 4-byte pkt-line length of how much data is in the packet, +// followed by a 1-byte stream code, followed by the actual data. +// +// The stream code can be one of: +// +// 1 - pack data +// 2 - progress messages +// 3 - fatal error message just before stream aborts +// +// The "side-band-64k" capability came about as a way for newer clients +// that can handle much larger packets to request packets that are +// actually crammed nearly full, while maintaining backward compatibility +// for the older clients. +// +// Further, with side-band and its up to 1000-byte messages, it's actually +// 999 bytes of payload and 1 byte for the stream code. With side-band-64k, +// same deal, you have up to 65519 bytes of data and 1 byte for the stream +// code. +// +// The client MUST send only maximum of one of "side-band" and "side- +// band-64k". Server MUST diagnose it as an error if client requests +// both. diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband/muxer.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband/muxer.go new file mode 100644 index 0000000000..45fecc2cbd --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband/muxer.go @@ -0,0 +1,65 @@ +package sideband + +import ( + "io" + + "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" +) + +// Muxer multiplex the packfile along with the progress messages and the error +// information. The multiplex is perform using pktline format. +type Muxer struct { + max int + e *pktline.Encoder +} + +const chLen = 1 + +// NewMuxer returns a new Muxer for the given t that writes on w. +// +// If t is equal to `Sideband` the max pack size is set to MaxPackedSize, in any +// other value is given, max pack is set to MaxPackedSize64k, that is the +// maximum length of a line in pktline format. +func NewMuxer(t Type, w io.Writer) *Muxer { + max := MaxPackedSize64k + if t == Sideband { + max = MaxPackedSize + } + + return &Muxer{ + max: max - chLen, + e: pktline.NewEncoder(w), + } +} + +// Write writes p in the PackData channel +func (m *Muxer) Write(p []byte) (int, error) { + return m.WriteChannel(PackData, p) +} + +// WriteChannel writes p in the given channel. This method can be used with any +// channel, but is recommend use it only for the ProgressMessage and +// ErrorMessage channels and use Write for the PackData channel +func (m *Muxer) WriteChannel(t Channel, p []byte) (int, error) { + wrote := 0 + size := len(p) + for wrote < size { + n, err := m.doWrite(t, p[wrote:]) + wrote += n + + if err != nil { + return wrote, err + } + } + + return wrote, nil +} + +func (m *Muxer) doWrite(ch Channel, p []byte) (int, error) { + sz := len(p) + if sz > m.max { + sz = m.max + } + + return sz, m.e.Encode(ch.WithPayload(p[:sz])) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/srvresp.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/srvresp.go new file mode 100644 index 0000000000..6a91991839 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/srvresp.go @@ -0,0 +1,127 @@ +package packp + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" +) + +const ackLineLen = 44 + +// ServerResponse object acknowledgement from upload-pack service +type ServerResponse struct { + ACKs []plumbing.Hash +} + +// Decode decodes the response into the struct, isMultiACK should be true, if +// the request was done with multi_ack or multi_ack_detailed capabilities. +func (r *ServerResponse) Decode(reader *bufio.Reader, isMultiACK bool) error { + // TODO: implement support for multi_ack or multi_ack_detailed responses + if isMultiACK { + return errors.New("multi_ack and multi_ack_detailed are not supported") + } + + s := pktline.NewScanner(reader) + + for s.Scan() { + line := s.Bytes() + + if err := r.decodeLine(line); err != nil { + return err + } + + // we need to detect when the end of a response header and the beginning + // of a packfile header happened, some requests to the git daemon + // produces a duplicate ACK header even when multi_ack is not supported. + stop, err := r.stopReading(reader) + if err != nil { + return err + } + + if stop { + break + } + } + + return s.Err() +} + +// stopReading detects when a valid command such as ACK or NAK is found to be +// read in the buffer without moving the read pointer. +func (r *ServerResponse) stopReading(reader *bufio.Reader) (bool, error) { + ahead, err := reader.Peek(7) + if err == io.EOF { + return true, nil + } + + if err != nil { + return false, err + } + + if len(ahead) > 4 && r.isValidCommand(ahead[0:3]) { + return false, nil + } + + if len(ahead) == 7 && r.isValidCommand(ahead[4:]) { + return false, nil + } + + return true, nil +} + +func (r *ServerResponse) isValidCommand(b []byte) bool { + commands := [][]byte{ack, nak} + for _, c := range commands { + if bytes.Equal(b, c) { + return true + } + } + + return false +} + +func (r *ServerResponse) decodeLine(line []byte) error { + if len(line) == 0 { + return fmt.Errorf("unexpected flush") + } + + if bytes.Equal(line[0:3], ack) { + return r.decodeACKLine(line) + } + + if bytes.Equal(line[0:3], nak) { + return nil + } + + return fmt.Errorf("unexpected content %q", string(line)) +} + +func (r *ServerResponse) decodeACKLine(line []byte) error { + if len(line) < ackLineLen { + return fmt.Errorf("malformed ACK %q", line) + } + + sp := bytes.Index(line, []byte(" ")) + h := plumbing.NewHash(string(line[sp+1 : sp+41])) + r.ACKs = append(r.ACKs, h) + return nil +} + +// Encode encodes the ServerResponse into a writer. +func (r *ServerResponse) Encode(w io.Writer) error { + if len(r.ACKs) > 1 { + return errors.New("multi_ack and multi_ack_detailed are not supported") + } + + e := pktline.NewEncoder(w) + if len(r.ACKs) == 0 { + return e.Encodef("%s\n", nak) + } + + return e.Encodef("%s %s\n", ack, r.ACKs[0].String()) +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/ulreq.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/ulreq.go new file mode 100644 index 0000000000..74109d8853 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/ulreq.go @@ -0,0 +1,168 @@ +package packp + +import ( + "fmt" + "time" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability" +) + +// UploadRequest values represent the information transmitted on a +// upload-request message. Values from this type are not zero-value +// safe, use the New function instead. +// This is a low level type, use UploadPackRequest instead. +type UploadRequest struct { + Capabilities *capability.List + Wants []plumbing.Hash + Shallows []plumbing.Hash + Depth Depth +} + +// Depth values stores the desired depth of the requested packfile: see +// DepthCommit, DepthSince and DepthReference. +type Depth interface { + isDepth() + IsZero() bool +} + +// DepthCommits values stores the maximum number of requested commits in +// the packfile. Zero means infinite. A negative value will have +// undefined consequences. +type DepthCommits int + +func (d DepthCommits) isDepth() {} + +func (d DepthCommits) IsZero() bool { + return d == 0 +} + +// DepthSince values requests only commits newer than the specified time. +type DepthSince time.Time + +func (d DepthSince) isDepth() {} + +func (d DepthSince) IsZero() bool { + return time.Time(d).IsZero() +} + +// DepthReference requests only commits not to found in the specified reference. +type DepthReference string + +func (d DepthReference) isDepth() {} + +func (d DepthReference) IsZero() bool { + return string(d) == "" +} + +// NewUploadRequest returns a pointer to a new UploadRequest value, ready to be +// used. It has no capabilities, wants or shallows and an infinite depth. Please +// note that to encode an upload-request it has to have at least one wanted hash. +func NewUploadRequest() *UploadRequest { + return &UploadRequest{ + Capabilities: capability.NewList(), + Wants: []plumbing.Hash{}, + Shallows: []plumbing.Hash{}, + Depth: DepthCommits(0), + } +} + +// NewUploadRequestFromCapabilities returns a pointer to a new UploadRequest +// value, the request capabilities are filled with the most optiomal ones, based +// on the adv value (advertaised capabilities), the UploadRequest generated it +// has no wants or shallows and an infinite depth. +func NewUploadRequestFromCapabilities(adv *capability.List) *UploadRequest { + r := NewUploadRequest() + + if adv.Supports(capability.MultiACKDetailed) { + r.Capabilities.Set(capability.MultiACKDetailed) + } else if adv.Supports(capability.MultiACK) { + r.Capabilities.Set(capability.MultiACK) + } + + if adv.Supports(capability.Sideband64k) { + r.Capabilities.Set(capability.Sideband64k) + } else if adv.Supports(capability.Sideband) { + r.Capabilities.Set(capability.Sideband) + } + + if adv.Supports(capability.ThinPack) { + r.Capabilities.Set(capability.ThinPack) + } + + if adv.Supports(capability.OFSDelta) { + r.Capabilities.Set(capability.OFSDelta) + } + + if adv.Supports(capability.Agent) { + r.Capabilities.Set(capability.Agent, capability.DefaultAgent) + } + + return r +} + +// Validate validates the content of UploadRequest, following the next rules: +// - Wants MUST have at least one reference +// - capability.Shallow MUST be present if Shallows is not empty +// - is a non-zero DepthCommits is given capability.Shallow MUST be present +// - is a DepthSince is given capability.Shallow MUST be present +// - is a DepthReference is given capability.DeepenNot MUST be present +// - MUST contain only maximum of one of capability.Sideband and capability.Sideband64k +// - MUST contain only maximum of one of capability.MultiACK and capability.MultiACKDetailed +func (r *UploadRequest) Validate() error { + if len(r.Wants) == 0 { + return fmt.Errorf("want can't be empty") + } + + if err := r.validateRequiredCapabilities(); err != nil { + return err + } + + if err := r.validateConflictCapabilities(); err != nil { + return err + } + + return nil +} + +func (r *UploadRequest) validateRequiredCapabilities() error { + msg := "missing capability %s" + + if len(r.Shallows) != 0 && !r.Capabilities.Supports(capability.Shallow) { + return fmt.Errorf(msg, capability.Shallow) + } + + switch r.Depth.(type) { + case DepthCommits: + if r.Depth != DepthCommits(0) { + if !r.Capabilities.Supports(capability.Shallow) { + return fmt.Errorf(msg, capability.Shallow) + } + } + case DepthSince: + if !r.Capabilities.Supports(capability.DeepenSince) { + return fmt.Errorf(msg, capability.DeepenSince) + } + case DepthReference: + if !r.Capabilities.Supports(capability.DeepenNot) { + return fmt.Errorf(msg, capability.DeepenNot) + } + } + + return nil +} + +func (r *UploadRequest) validateConflictCapabilities() error { + msg := "capabilities %s and %s are mutually exclusive" + if r.Capabilities.Supports(capability.Sideband) && + r.Capabilities.Supports(capability.Sideband64k) { + return fmt.Errorf(msg, capability.Sideband, capability.Sideband64k) + } + + if r.Capabilities.Supports(capability.MultiACK) && + r.Capabilities.Supports(capability.MultiACKDetailed) { + return fmt.Errorf(msg, capability.MultiACK, capability.MultiACKDetailed) + } + + return nil +} diff --git a/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/ulreq_decode.go b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/ulreq_decode.go new file mode 100644 index 0000000000..bcd642db2a --- /dev/null +++ b/vendor/gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/ulreq_decode.go @@ -0,0 +1,257 @@ +package packp + +import ( + "bytes" + "encoding/hex" + "fmt" + "io" + "strconv" + "time" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" +) + +// Decode reads the next upload-request form its input and +// stores it in the UploadRequest. +func (u *UploadRequest) Decode(r io.Reader) error { + d := newUlReqDecoder(r) + return d.Decode(u) +} + +type ulReqDecoder struct { + s *pktline.Scanner // a pkt-line scanner from the input stream + line []byte // current pkt-line contents, use parser.nextLine() to make it advance + nLine int // current pkt-line number for debugging, begins at 1 + err error // sticky error, use the parser.error() method to fill this out + data *UploadRequest // parsed data is stored here +} + +func newUlReqDecoder(r io.Reader) *ulReqDecoder { + return &ulReqDecoder{ + s: pktline.NewScanner(r), + } +} + +func (d *ulReqDecoder) Decode(v *UploadRequest) error { + d.data = v + + for state := d.decodeFirstWant; state != nil; { + state = state() + } + + return d.err +} + +// fills out the parser stiky error +func (d *ulReqDecoder) error(format string, a ...interface{}) { + msg := fmt.Sprintf( + "pkt-line %d: %s", d.nLine, + fmt.Sprintf(format, a...), + ) + + d.err = NewErrUnexpectedData(msg, d.line) +} + +// Reads a new pkt-line from the scanner, makes its payload available as +// p.line and increments p.nLine. A successful invocation returns true, +// otherwise, false is returned and the sticky error is filled out +// accordingly. Trims eols at the end of the payloads. +func (d *ulReqDecoder) nextLine() bool { + d.nLine++ + + if !d.s.Scan() { + if d.err = d.s.Err(); d.err != nil { + return false + } + + d.error("EOF") + return false + } + + d.line = d.s.Bytes() + d.line = bytes.TrimSuffix(d.line, eol) + + return true +} + +// Expected format: want [ capabilities] +func (d *ulReqDecoder) decodeFirstWant() stateFn { + if ok := d.nextLine(); !ok { + return nil + } + + if !bytes.HasPrefix(d.line, want) { + d.error("missing 'want ' prefix") + return nil + } + d.line = bytes.TrimPrefix(d.line, want) + + hash, ok := d.readHash() + if !ok { + return nil + } + d.data.Wants = append(d.data.Wants, hash) + + return d.decodeCaps +} + +func (d *ulReqDecoder) readHash() (plumbing.Hash, bool) { + if len(d.line) < hashSize { + d.err = fmt.Errorf("malformed hash: %v", d.line) + return plumbing.ZeroHash, false + } + + var hash plumbing.Hash + if _, err := hex.Decode(hash[:], d.line[:hashSize]); err != nil { + d.error("invalid hash text: %s", err) + return plumbing.ZeroHash, false + } + d.line = d.line[hashSize:] + + return hash, true +} + +// Expected format: sp cap1 sp cap2 sp cap3... +func (d *ulReqDecoder) decodeCaps() stateFn { + d.line = bytes.TrimPrefix(d.line, sp) + if err := d.data.Capabilities.Decode(d.line); err != nil { + d.error("invalid capabilities: %s", err) + } + + return d.decodeOtherWants +} + +// Expected format: want +func (d *ulReqDecoder) decodeOtherWants() stateFn { + if ok := d.nextLine(); !ok { + return nil + } + + if bytes.HasPrefix(d.line, shallow) { + return d.decodeShallow + } + + if bytes.HasPrefix(d.line, deepen) { + return d.decodeDeepen + } + + if len(d.line) == 0 { + return nil + } + + if !bytes.HasPrefix(d.line, want) { + d.error("unexpected payload while expecting a want: %q", d.line) + return nil + } + d.line = bytes.TrimPrefix(d.line, want) + + hash, ok := d.readHash() + if !ok { + return nil + } + d.data.Wants = append(d.data.Wants, hash) + + return d.decodeOtherWants +} + +// Expected format: shallow +func (d *ulReqDecoder) decodeShallow() stateFn { + if bytes.HasPrefix(d.line, deepen) { + return d.decodeDeepen + } + + if len(d.line) == 0 { + return nil + } + + if !bytes.HasPrefix(d.line, shallow) { + d.error("unexpected payload while expecting a shallow: %q", d.line) + return nil + } + d.line = bytes.TrimPrefix(d.line, shallow) + + hash, ok := d.readHash() + if !ok { + return nil + } + d.data.Shallows = append(d.data.Shallows, hash) + + if ok := d.nextLine(); !ok { + return nil + } + + return d.decodeShallow +} + +// Expected format: deepen / deepen-since

    #VBW_{5$#Up)b}Ig*}DiqurNa^ z)GK_@Bg2KQ=Pre9_TSHKiKN=4{=cdMQ^wyl}Ep{Dv^sB#Xv|oO-&p)w0Y-+&6;Pl_45Bzm| zJJU&T+3Ew|di_V2<$`9vwPey(S;bif_Azr)3qT+XVgs)bPcQ*^VrXRFk^NwZ+hOg5c>8Fml$q;_!&w^c*3|Q)Ut7LtZeO5@^^|No-C?9tO?_%qtr#j` zh)2N+3xGOfB;|sPE|4)kv_6O-i@n+`Fg(A{bLl$R*T|4084CUzBOL=GL$wT%Au96x z^_pv2KhubV&l$GxhM23#JBOI%x(4*A>?@8A6=(*fu^^67M@#lrD)6aW5K0UKDt(+#EZ1&uJalq82eAv%b>B>e{670 zB17NU!8hOJniYma-MYr1y1bCL+BMo5EHwjLxSg&t&crFbz_Y3ZBbM?g4y^x#S*r0E818`^ z=(DwB*f|O74gKj2Qy>a0_QnEvEREwJ=9^UQ-3NFDpZHWa{p$`#pL2t)GNZRJxnFsv zb4hKvdDPo(rgE_0Av5hh4frVToIr5zX5|URaIYH?ipHOr)p48!Ueb4t)1N9>0*BbJ z!#ZoYI=#V*O4dhC&~zu_0cfQdV?-OarvYb^fThTioEZ#Vd~p8KdsVKbcPY4&g;`E7 z=jCX(I>WP43{|S^<^a~vi_&1+GiCBN7mP-*nTH^2&uVuTKt>HOJ$r>m)jqFPD<zkD+$c3Ngzoj#BXy zSc8xo<*KDoyhgEF-b;gx<^}}!NBXT-b67SVc7P^Kv;ve@R}^;)+`q`gv<@}^Ow0=A zR+<8b2f$Sv!(}XBNX?!m!t9rSy~Z{CFbhS~F|@UD(I^4S#kIS~g#)<7G~fmL4LMN# zby^QYpYD{9^W_GQ|4R=es*GQEp`xV!W9Z!Dnfm`Ye$MV@W@8w}HrKh2DC9CDcN(dr z+gvK$v`QtRvl$z?g``sER+5UY=(f!@saB~}zBN*fZmXZ9Qhxh)|LyTOJNvvp@7L@3 zEX~)Z@N};(Jn!}QGxF~$ie{2m!cAznIW_!V;Q{hyivc`dx@t9@D2+Zv$o@C|LaA9Q{G0k~y67`DLE|LECkGhgYUY8gOt006)9ZR6?VNrQL2&c4j$Fis34(n;C3 z0iZBDooH{byHB7v(}Iv%o@?oXDecg0lOcL|ocJ9TKI_WLH2`?pKAl%wxeIE6cz*6J zZzX^*h-2|#=$y<)B{t7a1c;2xGgtru#~gx;bksoZ3zN64FnF~Dl31>iNc&D!#r4*q zw!}Y%TyQ*jT}BsEdL4yvmEc@Hq&$Eg;5-}g;_+Wj^9$}@8<&*Wd8%bl#`M{4v!E-h z`Py02JQa}n`Vk;B98b*4T`^vlIxHh#)=LshHQ_d~fm zS`)=Ky!1L%@OagRXCWAm5v~+nP zN(Ja>OprGAvCt5*ew&W8I5269(93B|1#8W9)V6O#-up3T>=)lK_2tjI55qAw9(iV2 z&6AsO`yT(;SZsW&E)WiU*Xcj$owXXAH;P-ny>vO+6VpWXjeASe{);KF)oZ^O57w^D zRDH05r+0!znQE`RTw|W>jQQH;)Upoq6ML@tnm+nrd+fld9f6ve^q0`HL)KJO*3J;) zYcn7K(lXxk?R2FGSI@UVPrh^IiGXHI$wK$E?n6eS3ISsAD5QNwR7P&recnC?4E3Ao zv4))E9qM%vey5uI;U@O?pnb7OECye6_7PoU-qN=K)Z%_%PutO9VDC-4hGow@Lnlt& zTr~-+amn}UF^!^4z6PRd%!B?WR$)YC1_ak&LYrZI3r=nt!^3P~5OuR|=4!7|qYRi# zTxcqdh;tW#C2E~LZMpLlJQ?AwNW|}!=2 zY-XBvey}y>6_W3xat|d&e-$6K=6}~ktURxKTL(y#E5}Gro9QUG zvY>iq#eGGJ(|*T>plVBi_Do!&a9ex8syr0Exxgy~fU0gkDVs2+Vd)WvcaxCy1+xQ3 z7`PZ^u(ah#fpaBIkgW6StpU(>+6mpfwsd^D;bd6}R7}#;vmyQi&#k2k2b1BkxnStV z;F!h# zHI}{CfhQrnUu#>deno`MU_z=I?`~u~hXP^%xa1qrXlpt<{$JX{Yw#BfeH$ahSDKcc z9J97t6d1LoYq#%(Pb(H({XnpVy*rAQgcjcvhqi_ZOFD;A#F8W$C9J1`04}v(pV?Im z`GzcP#bHOvdu-6!)VgZ>Qd5j+;b|M4Y=98x>ji(%@&ajjhX6eQDC~j%w$b z!=$Y#^FDXaM%K>s1aP8SVqV2^uH^4(5889z%`Rzi((P?W0tyB;%1uZYZ7v7e4W5gC z;eEK&sj#*Asm0~nS66XYw0r3&p(oDTE7zvY?AWelzx1UA=fdEVy*WP8lE{x`J1}mx zX}cU^c1{48jNSl)Uun_oPV2|fOVU?(ILDpD*1b6u9ps+1)!usZsu}L5s&BVi7X1}l z&V=ra=skHq>s69u_g#Jamhl;RztSs;(qh_;d<;3}56*?0b%TMW=L6+jfK`MOrhj2UZ~ z1#AABcp0I-DD^2IJb39*vnr+I!!E#_@U1|D?($f@8_ztZ6(idC-if-pbZeCwxy!Yu z>B2G4ibmIBw?XtZlacLA*0BaW86Mdow3#BD6LobW6EXi?8V@I;i$MKyF~W>(3lUjz z^iuxU>$o6MkS341N8Do_4W3I15Mf%Xmlz48piOgq)VI%P)?^6`;cp#se&E~l9_~&*|=ifV? zBAh8(>}7;mlV5tU1Lu5Ujir9Ir)r`fY0dj8_Cu+L`-T{F^IAYQ%+H^pT_gbkS7$0Zo$VuucixHcYoIDG8G#& z)Ws>)jA~_VBnX5sAhRowu?bQA`KR?WGz1z_iV#~ zdflMGvCEAsM&a-P47-d?zrCi71xy-QD3J8zM=)R*RvTHSFOm? zWa1#SDp!x9G(}Q+_-JmgR*>cj7~fuMU&*H$q(qeX_#Ckt_?UQh6&@MH6Fu~iYJYl| z9Cd{&+I*Q0v+2;dHvq7|m4?wrVX>YdTnb`S`39cgVikO-y16s;Q zX@rL+D$#B!(u6PGN-KH;Yls?$-W+fH=?P{j@|=0dgOHFtg1#ve(WJ=j%g}oYI<|SC zX$0|Cx`-;id4Pttee3AdB0eO|^%jc`C}G(WbgB{^J^(W=Kp*tXGk)CA_F~~muC{|( zw1X+iZZppoqm!GfJ4_a#d(h{r!TC%8oz;fGSJVBmhi?{I=+wdC8PJgi^qyvo1^`P? zLZJ$@g@(NpisC3lR17SOoG;tlu&ut6x(Y;(94b85voMuxm ztPF@tfdPzai)K1Zq7UB&!e)cqUwOlsd4HtnETy1W24AT_Zv$X(sVJ~b=tHx){?Q;PC$PM&CCdwk!G>aA%nstZ+%X@t663r_Y=}yqcAc(BX zWCj00#^EiyL0<%)B<5R&mG_NU5;8z|DAc^bPoM_OLiIfM<1b6llgW77k%fCl(7brQ zW|}E~0@v{r4!^VuN^05|2`)Gg{Nu-zx)o8L0UReZZEC-mVZi z3^#;U9tvx8?pH$p+YMnAe6k84WkA@}YZ~O1g#y&{3vfIL*9@UEG(#-k_Hkf=9f(nC zNK1HV zir>czwSSv|!m5rlfxbH=z(yfjK>+NVD<(3;$E4`J)u1;)(z@%&#m>Wr;!E8Cfo4!$ zTZeYm7_`}l9Zbkv0};zW6}~F+pNsTXz@0tMj6c=`$*AmsdNce*$|F3PJk0cGrT9XLUCU^J<{^=v=^jY*l3_MwlHuMB8 z08bAnN(~`#X0=wjO7w>VOAR}3PN#S`lQ9bMzA~bZQskxtvY044b|V~Q@~kdKMK z3p>d0TnRLEY&mp1Rl?a-rop(41vWYKK8|vOn6skOiLjiqjvN{S3ka?m6KJW64${y% z)v)b<(1}v3Q>xSRna2uA8oJ^Du!}E1kRcGm$9m>kXs}TyKnfWs%?21o^snsr_ncFw$cwOs% zL}x(olLrws5J^ccQwcHDEB<8ywr$9jDsW$>mJ$?1F!LNdyGI8X5g565KAko3M69JD z@G8(?gU9PK+v^yMHH>40p>D~np>SbD*pr4wMFdvt%gXu!k6Cbk!$5p_qv;mVC*A|3 z2{_frE3Rb$p~62yur<|)r=H6a6~cF7teGqD-W?*CUJU#mx4E98DUg4_7%OJ0=W~-7 zig*Xbh~DP4=1!eWn7r#lTh2AInVWVR`nuV54BB|s+xNG}{hvP}>t$h<%g3x=J29C8yHhdl?u{#Up0mHzvMxJ&0jrDd9o{eqJM8=Wv@oGf z{h6`qM*P1eR^741?e>nTgAJ*L>#k?i_$C<6|EG6dY^Mfn&WzhXkGH(0cU_dh>F;#O zl}BZES-t+9pr6a+j8z*SEH##%h%7XhH1LzMr?b_TmD&GpDzi2 z{Alhhc>BD&>v*)@8|16{qe-WM(^h&R6IG` zW_dwLS$<(i(X92y;_0llddjZjxYx|fG4svbGsKjocz?zN?$+4xnN+TJgJrkdN#{a0 zBSRwU9*+L3V3s|x?z{Fdai!kv)|+yBcKOA98=S=C%Z&Hd-ZztSK0D5xe0HyCeSU?X z<+F=xwlGf|-(bJ%@TZysZ8?xa=Rig;{Mt9BYs$T~y1> zH}0I6*>!eYid;K8xV_v#B!&LhDn^Ti{R^M~+8Re*Ff@H;WHNr|6-qUbKLd_udiTHzTzZ;hbmAUE}Zgj3lJX>Y%3R0A+t- zu^xwS6Ix-wA{X3+M5%BQ8Mc@MYWRE)73B+f;$y|<7O?o+Ug8F&@Juc?_J9RYQ@p0S z!gS}x!+udYN~Ff%Qb*2Ti<7{~nshr-l$w__BJxB)*Ct~5_UuFU!psgdy%DxcE84ki z(eO@(eN55E-k_GJwe7!z6rBny!r^aQ=T=Fqjc^rWA3HUS!VG zN2l@)#g+FN3Cq+UOj7wK9bknYj7nP(U?uwBfYV?q@}M;Dnhz3AgRPao2k98k%Hsc} z9q&8;&ZOKTT%|d9@z}&|{wNAEjEA8$T-bOJN9J2I7lTk5wV9tf=i0_q7491-niA`fzrYjf~;PkzoUYP%Ypq6ubT9ax=f34+3 zDnAKdJS$nHfxp-qV-|Awx-4)=W6%$P3Ri*|sE!M!`s3U8%47REDkD> z02#~{Bj%HLXSX4OFArwiq2Kek&G43IcOb$$M2G;RIHLFVv!Z3vdz^y*#5Y6@Qk2E4 z=ozv@ziRdoPm~?%x0?$i!ZcRz7`ysy1&hkC6w%s&#cMAVT7c-X-G4;f`)@)Y9I5#w znnk4zt$*6qGk>zd>L=|ezYT6AX}=&D7KJVZSk->x?XCMU8}h{Oq*eb^ww+!G_JBaY zP558xG4o0Bc_YZBaLHffwM#%)eHfxpe^mRB5l% zwr%qJy9garwdBT`(ptZ8)u|X-&(eosmG+?2aq3clg7Zi0E;ib+B)1X`@G_JsMSd&M z30Yn#%3)9^2IP2hwJR6Gb=HL!47xRe8h+%DNhw&sw@6ugZ4v+p5?#j_^7dnO%%u5a z6oe0xqx<+q2JiO^d$_Z<<_eRdeA`r4;=lQg`_5mscOd}%MRlJ%v%`?Ee0Ja@HVI-Z z$9K+Yu67?jEnhL$dc>;?`TRCdxB|y6+4^gwE{JHVpfTh8mv_$G(iUK9ZZFflq`vo7 zx$a!_#hrf-Yyj_NNpc54#G>O4#_E;SZ+6qGvY_yjKXnH4_iUNEc3E%o4hUSziC zS=j!izPtB=(#1|hVm`RiT^(gh%DP-ybD7L7_WL~?h$Q|dJO*-pdkk1bZfzfi;NPs+ zAVKwM_j@cU$ky__%T6ewMJtvm_f?H5q?qYOYypa;=q?5Kw0g!JN!pJ`9qD5->R@SUl zEc*hjE`VpI3?wC)5e$Ve3JXL!1a(%+C&v3y`e8mds{-a7->q!Zh*lrbV>_nW*~Ns@ zjSCuonLjPhaS0#_J0JfVE$?sE0($hGoqa0MeP6!HJWaNS}y7(;g#F^?@9 zKDlFwR(ryGYzn7x>-N^#$l#Ozy?JP}xUA2m^Kszpad1a=?YvPq&gmdikuMQr< z=rbqho;3z;{_k#5%!MmOi=Q0#n!O&(0De8$L;W-N_~`?WkMqZGoPTq>@JY_C&)bf; z9qrXgyq0jcZ=;82tt*D@o1pzSK8Zk=z03aPP}pDI=W;++yeZ`Ds`E1&D#ybJ*0cqu z_KeRMmfm`hdg|*C+=tC^%q|SrVE}AqQrL^wm-Dayg})V>@R1; zTv+4gcIbNF60_GHrTY)Aj`=Y9!>Mtt+WIeB=X1+tsnP8(*8c+Q16tTf!?sW2%Ukaz z-TZK=KJg*Vk;jT#SAF}BK`=lGt-CwA55Tn%oiDR%AASGOXSBDy=z8qfYFhsFr(v$; zPT!6P7w)-*vpNfRYhh8-;F@DSD_+kE0)v?s=YHJ>@apH}!8X`DFsBMxT| zg5Y5pA84jxB0qYv4BmUP;aQ}}E%H(%EF<4z$m9M)S@`i`1ciExWQ~plN$C$Yg=bdyzz+=uPO1uC4phr&Vo_*864;V0crnf9s5U^zX!0Y`8) zLkrVOtoJej#E?o)GsG5wANzg(B^DVN*rYmpt+K97hpkDsvCm1rOPhJ5#~hyC5yb4b zLp%nSx8)wEHTTK52sS`Ym=Un0{<}fZXSNp`Y_>!SR zH5X-G2I%xH*Kl`fNbgh_avK~b8-2`3b1$q&b+5~*79hKrB6xf`E{ej{I*nQKzcopR zXhkf8>gqgal|m4K2_#?4l!60e$A0=o0^O+v4k`k6&6RqC8=eAv9+I$*~Rz_>P}o1T&7n_=ZdN6k=K=hy|hF6ahYg3nx;gWZq$1XogDKF@Q8{ zMYCSroiP3_;d?n-ASp~fbw1EwRnGqs*j4J4-O*ZjcVwvW$J3v|{!1atz^{(@00i1fvXGeyf2h zlJ0Do`&PcMm4^GL*0ctR8s`L@`dbV`2NQJHIiB4>bc5`1kdC)Iq_z3!Hve!}OtyyP zWSrS|QJsjz4|h|4szW>l9M`{wJ7|BZ^O!TlXFI0fnsGJ2n1AiM*|TpgSvph9jyBzW zJ-O8X{p$C}aaUE8KiRwesc6yk;i4%rr1v`>K9u=i$gMC8_E2wVP2b@n8X%3in1%A5 z{-nD#E1BZgF`wSR$$C82o`%Fj0b2UZCle1UVGd60tZP+CUAv0o`NSx_JJ~ zqcsD3KUG8Uh!josR1{{aDHiYRL=LZI_+uT(ld{ku_GA4*vkuj9O)3DPZ zv?dXNc>bDSB!qw?O?m4YXBBjN|>*s3|j*r;$eQ%()UXY&D#)! zVJT@Ep4Wu(Rs?Fd^8H5S;4sUlL$LkIgJl8o0tC&hBlmz)AVD|}`l)1EPrDZ@Abp7C zGb(SFQq=crKB|xU4MTuuw;x9qklr5{)Du(Abz1znlz zFANFK%Jsi#WR{)}FqaSQeEFb8Xg?R|tD5ij?|>nTx}WyKi79;lN)WB=b`-9wnd|m! z4e%<@^>39KkpVl^>OW@1@sxZ1mB!xN^86(5cC95eSsB8NWi|(|D-Q5y6l!=W03b`2 zRcvWJ?uX#RLnB(I#Xpp5+htJx&ReascPSGB+08e)JD&fo?95D#SNQGJXkSwx#?I4y8 z3$_N&HpZ|FDf4Dltum}GJH58<1H#RxqP&52o7QCuUn= z)(D2RSVG_KU9*Sns^#TvHBOX3BTobd)9sutn`py?8$`gshe`}9`pCWa62zfmDa?sz z^zP6*$wMMNa*w$8dXdIZjCxgx{4Oqg;PYUewE8_ZB0jbs%1%&7ZO%B8d^x_pRO?`tC@4w=F?KgM3Yk zd|eZz-aKj%Zx6IIkGJ1`|(sWLgDMG$5zGqVN2e%F6jqI zX;ykCtMp4&MBc&azsXvfK6Dm!?ZHgwqnS^{qnX5yTKM_U@+TvOBY9^MvKWk!lBYhZ zl=~$o<4%b5qEEkUE_wOb{bW=pU>)+{uq2=GbU$GW4`O;e;1o?>CooV@cI*4|748%McwJwggfLi!Eer~@&`XF&)w)%Rp3(Z@$RMH4(ez=GZ*^E z`09MmwVH@r!qj+Y(#2rCPAp#g1>=nK=5LG_%FwD}=koKR*!jjtq@7AGB$6ND?*X8> zPaKC)a!?7@{W{!MrGOC+HC$LKw0dp2C+65x)Ti{g*=@yfEj`zB>_}$DtA%IBn;+^a3u8S}XLL1=_~W4!gpYZr=&I!eHs z-JS@pR{O*K(Rt|X_2ow@0Gxc01ittaOs7=}&xkith8*)Qj+Ep+??ke!_3ym5y#pa|XaQCuYtMrH0#pmA+hc~#LyH|p5Y+Ygnb$?tg z62{~C=FMTA#AWYeEP9sOs}l+N&^MVbSqV7T1OYb$P7%#&C0GUffu>vrbxK z;x==q;G+ME^gD9n_yPbj)V$_gm!ASt*>Eel@6DqQVw}(5Lzd?K2L}RtBTYRuoX?sB z!_n@m!+|8O)Dtl9=jKY7$Di-y*D#MeW3~kqw!$fZb7h0|T+ALe<6t}*tpOoQUt}-o z1=G1!s%{Jy@+%YA#2?#UEhvL5fDwVXeCvb{WHb^GTVnt~Jiw^*BQN=a=wpEjP3P{bnAa1MbHYc&WQL zMO!ac1{$}bQicNws${cI@+$_#IoV6^kHP(wqh&?4`x{NXJLG*SWj z4SgsxP1-iZ+yEJA*k;YhH%si$S0GS{LAUY2xHd2&HZg?--b7%V$&C_iHZIr0Do|JQ z(s35Ma9G-;)osUHv0ze0+pKci5kU;gTjME9n&kVF*zk0|KL_TO(Txpa`>+HjZ^6$^ zf(cIp?FOhmCJ$NDaA%q}UrTx~hA%T_ogB_}h(`pcA{rmP^e+p1a#-&0!RG0I6Y!2) zzsgIl!`1yH!$Adxw*o^m*Pk55B&(DBIbS2xt8%Ipw2NC}mM>kjUsL@678yf`l~I-& zmvL4a62aD@Ko~Ns+-Ls9_5kTV!>}Hk{G@4_FAv&lA@DB>EaG=#nLnDAqlpWxq8?uR zONx0Mt!zIM;7^i+?q-%Fxvs4&2IZ=sPp(Z3j6S@>A~nz@=_1GdG^?4tSzGF;hLgEK z;7G6`846;p7i)^fac>rkcH33LthR-p(<*3xyeH&$Bzd&kuV;}zC&Fyzys;L*m|h+h zYZ`qfm_DoJf`Nk8%U5EQailBYwVp*+dX<<0PQ5eCU8dD+s)vXsgkDpvfDRL+?0u|| zP0WMUv58Po{LROXpWrE7Y+V|fM3DPI-F7zv$t3o!RLI{1p#N{k9%?lA6&{xrI z!tGA0m=FA~^QgH3?nwcyg!1K`FcM?WO26wlVd0+PFa`%}P$XNFj=->ljxj;Dv)$Gj zT%}Jgj{q}Zu6+^{v5Mn$=7`I30!ZZc?rr}W)%Dm20xk_)fv@Ryod$qm$SsA>OXq74 z;}Pi~(%@L)vb3)QLby;b0)TZHOLfwB6$1j*E|w`mZnM5CB4t<1D9P%U&`c>UiZrw? zE3Ar#2c>FlNC|5h4m9rQ@z9Pac{WpiJ9NvcfGTyM0}t}D%0)5QJ_^=`bte}M1B;F?NT4)ka59r|+THtDg}a+q7%u@5+R& z8Gblx!fyC5t469%82D{Hy)a@ra8GF3(6h-;p;60=0G)IIv*x?O-6h)4P+STN(VAx8 zULBLt6VnxhY<~4d88tgBE^8a56+!h$fLSZp{@JDh6`h`SMkwSTnXo_)gGQlZ!@se` z!yxwphS2=$%Y`h7pFu41p#wio+Hoi#3*SNnR|R2+1U3PqHQxG1kc9UL8|PngJySU%aod+!L=>HMPV)BX9A3Zhse$Keg%8|9^|Ayzqu~3K_NnekMXqjXpE>5g#ync{=E#@JJ0g4VFVc%PueW`-E$Z7H9hN)6lfVi9Py`!C z27%rUhaplGZEZ3>9H$ds>Dd;*h9l|h=~T5cq}WnbLpM-8DBNT=}$ z#iI?O-~!*hYgQ5H4-NrV7dR#p$)Q+#`npQ5BU^HF(S$9j1ikrlch6ddoFav$TjiDO z=Gh2S6%Y#~v#4;T2EnX{PC%T|8vHXJvVayc+YNZ2|Yzw>7w z@7dBv-r8-{3DFMiP`cZ#vh5(9m%{AW=3I$9} zA}p(7TO!~f``f14oasl&NPI%U2W%IxIvU`OBx2jp>r+17Ju<4N1k-=N=!Y_zX}YKX zLySY^+0(0%uWp)~{9tAuoN^zG{6k0MZ2$b~_bR|;Vy{&0y8AzC*PrQZAsEN0%Qav% z(+K(u8jugesUZQax+bm(gHy^H-j90q>t(YZZ9>4=-Ifh5>;RRQ@YJJ;)9Btyg^_rK zbOY5wPwR{}9>-WxZfU|zDr2+f=_xv)37R@rSSDmq$@A{z5`0*RWC)G5L+X8L`(CD( zEhY!m@&p6*25SWbu8@t_Ixk_e3mVOLtl)K!W)iB&q{zW))v~S0yQBpLzqCH|)mWVz zv_bReAyjX7Ac?wMt^80~3~-AJatN}XTKzwdgq1lej{S4^-XoLuJMCet#TdOZ)=L_N ztd}>&Vpx=c0*pM2UN2OCV+#Rqei)gIitnPqx#+7&u#;QdU>k0EL; zsa+-j`l-9W&r50AtA9F>Ee>_V;BbD%E=Zbwhb9iqak=RYm&cOk304;MtGXjx>Az@<6$!E(8EA6x$uJrk5j1jZ5l96{u(0;fw(a4WL-EDv9 zOTG*Aj*j$!n#_%#{84ED8N$qW1li3H5x_v6-d{zdgQ5bNJ#;lTReJp$DDSmC}NI1u@}Z^4BE-Rw{GC(G3Io8>RcTD1V| zA71YYhH=o4(hc`VWsD~9b+gpn7to`Q{-KLqs!I%hT{a@G&k{*S^Mki1 z+Hdlxl%j9#l9j5ij+;fcQw%)`rJT@)$e_^w7EBQlhCy`Szs$2|9v^+&=_y&avFGp_ zPS=UwPp40NWB{0H*`b12mS-+Z1}C)nfB@YWrRphu5PUD;l1_`pGiwYRL*i2D1Wbp9 zjK)J7q`m_o_*ndAtMY860JB~j&Q9mg6Jp7+|9>hqaCD*4cp8%b^!cRo3Mclcn?-xtEYdOb zd28MdV;j!Kpzv0QwdO}u{))q5593_w9;Mtrl<)DNsgjOYg!s(LpHDm(c0!T6pSwd~ zw&bn(#$lGuEMFJe^WD^YtxONcEXnT4UF=sxc#pe!Fh8oGf=hHl~@ z;Spv2VfXz)eRCexzU?0HIF+v(C&TYmTVUqO>F&?Up6@%1sBj*0*m+D{w68jsU;^+9 z?tEL%9uc6P93P~=- zS094s^R*4vv~pgmz%oK?8anU#*)6#%Lxxi9F7s$`Jgacymr8!VPm|TvW5c^*A4BrW zvktV)z;luin|J1EO*pt)U?Eh9mdpPQS*R#9TO@ z`=MyWhIyxaQfu+J^2ZMkw&55yZvHRH{aN06_tbk`6i9V#4!u39NjXfftbpS-As5}j ztk6ro*2zH3vp+v5g0^&&Mls`R@}ubMHqRmX81-v-@ziBO7>KQ zI(KygfVv|%_rJbE%SvHK{)LP24`<_@c?09o*B-V@ll!>Q_9ig;T*;T#w@Q+We55wIbC+w0L?hP1C;4QU*JNpFHs%ZJsvgkBqFbW3$cF}rX4jA$1 zMjQFdqoe)9C+sF*o8ub@Ji2i>pI4UTfR~{3K(63n!YB;OOC*=`Ng6E@W6U@-AncjW z)V5?oc-=FUd`bE!xEaqLqm8#EgWcks9yYRAN*-{Wtj=@LOCV572$AYkRuk z2fMfLOI=oLz$vA0%0AHGXPxdb@gt{vr5%NssK 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